bgpd, doc, lib, zebra: nexthop-tracking in zebra
0. Introduction
This is the design specification for next hop tracking feature in
Quagga.
1. Background
Recursive routes are of the form:
p/m --> n
[Ex: 1.1.0.0/16 --> 2.2.2.2]
where 'n' itself is resolved through another route as follows:
p2/m --> h, interface
[Ex: 2.2.2.0/24 --> 3.3.3.3, eth0]
Usually, BGP routes are recursive in nature and BGP nexthops get
resolved through an IGP route. IGP usually adds its routes pointing to
an interface (these are called non-recursive routes).
When BGP receives a recursive route from a peer, it needs to validate
the nexthop. The path is marked valid or invalid based on the
reachability status of the nexthop. Nexthop validation is also
important for BGP decision process as the metric to reach the nexthop
is a parameter to best path selection process.
As it goes with routing, this is a dynamic process. Route to the
nexthop can change. The nexthop can become unreachable or
reachable. In the current BGP implementation, the nexthop validation
is done periodically in the scanner run. The default scanner run
interval is one minute. Every minute, the scanner task walks the
entire BGP table. It checks the validity of each nexthop with Zebra
(the routing table manager) through a request and response message
exchange between BGP and Zebra process. BGP process is blocked for
that duration. The mechanism has two major drawbacks:
(1) The scanner task runs to completion. That can potentially starve
the other tasks for long periods of time, based on the BGP table
size and number of nexthops.
(2) Convergence around routing changes that affect the nexthops can be
long (around a minute with the default intervals). The interval
can be shortened to achieve faster reaction time, but it makes the
first problem worse, with the scanner task consuming most of the
CPU resources.
"Next hop tracking" feature makes this process event-driven. It
eliminates periodic nexthop validation and introduces an asynchronous
communication path between BGP and Zebra for route change notifications
that can then be acted upon.
2. Goal
Stating the obvious, the main goal is to remove the two limitations we
discussed in the previous section. The goals, in a constructive tone,
are the following:
- fairness: the scanner run should not consume an unjustly high amount
of CPU time. This should give an overall good performance and
response time to other events (route changes, session events,
IO/user interface).
- convergence: BGP must react to nexthop changes instantly and provide
sub-second convergence. This may involve diverting the routes from
one nexthop to another.
3. Overview of the changes
The changes are in both BGP and Zebra modules. The short summary is
the following:
- Zebra implements a registration mechanism by which clients can
register for next hop notification. Consequently, it maintains a
separate table, per (VRF, AF) pair, of next hops and interested
client-list per next hop.
- When the main routing table changes in Zebra, it evaluates the next
hop table: for each next hop, it checks if the route table
modifications have changed its state. If so, it notifies the
interested clients.
- BGP is one such client. It registers the next hops corresponding to
all of its received routes/paths. It also threads the paths against
each nexthop structure.
- When BGP receives a next hop notification from Zebra, it walks the
corresponding path list. It makes them valid or invalid depending
on the next hop notification. It then re-computes best path for the
corresponding destination. This may result in re-announcing those
destinations to peers.
4. Design
4.1. Modules
The core design introduces an "nht" (next hop tracking) module in BGP
and "rnh" (recursive nexthop) module in Zebra. The "nht" module
provides the following APIs:
bgp_find_or_add_nexthop() : find or add a nexthop in BGP nexthop table
bgp_find_nexthop() : find a nexthop in BGP nexthop table
bgp_parse_nexthop_update() : parse a nexthop update message coming
from zebra
The "rnh" module provides the following APIs:
zebra_add_rnh() : add a recursive nexthop
zebra_delete_rnh() : delete a recursive nexthop
zebra_lookup_rnh() : lookup a recursive nexthop
zebra_add_rnh_client() : register a client for nexthop notifications
against a recursive nexthop
zebra_remove_rnh_client(): remove the client registration for a
recursive nexthop
zebra_evaluate_rnh_table(): (re)evaluate the recursive nexthop table
(most probably because the main routing
table has changed).
zebra_cleanup_rnh_client(): Cleanup a client from the "rnh" module
data structures (most probably because the
client is going away).
4.2. Control flow
The next hop registration control flow is the following:
<==== BGP Process ====>|<==== Zebra Process ====>
|
receive module nht module | zserv module rnh module
----------------------------------------------------------------------
| | |
bgp_update_ | | |
main() | bgp_find_or_add_ | |
| nexthop() | |
| | |
| | zserv_nexthop_ |
| | register() |
| | | zebra_add_rnh()
| | |
The next hop notification control flow is the following:
<==== Zebra Process ====>|<==== BGP Process ====>
|
rib module rnh module | zebra module nht module
----------------------------------------------------------------------
| | |
meta_queue_ | | |
process() | zebra_evaluate_ | |
| rnh_table() | |
| | |
| | bgp_read_nexthop_ |
| | update() |
| | | bgp_parse_
| | | nexthop_update()
| | |
4.3. zclient message format
ZEBRA_NEXTHOP_REGISTER and ZEBRA_NEXTHOP_UNREGISTER messages are
encoded in the following way:
/*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | AF | prefix len |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* . Nexthop prefix .
* . .
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* . .
* . .
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | AF | prefix len |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* . Nexthop prefix .
* . .
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
ZEBRA_NEXTHOP_UPDATE message is encoded as follows:
/*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | AF | prefix len |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* . Nexthop prefix getting resolved .
* . .
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | metric |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | #nexthops |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | nexthop type |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* . resolving Nexthop details .
* . .
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* . .
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | nexthop type |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* . resolving Nexthop details .
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
4.4. BGP data structure
Legend:
/\ struct bgp_node: a BGP destination/route/prefix
\/
[ ] struct bgp_info: a BGP path (e.g. route received from a peer)
_
(_) struct bgp_nexthop_cache: a BGP nexthop
/\ NULL
\/--+ ^
| :
+--[ ]--[ ]--[ ]--> NULL
/\ :
\/--+ :
| :
+--[ ]--[ ]--> NULL
:
_ :
(_).............
4.5. Zebra data structure
rnh table:
O
/ \
O O
/ \
O O
struct rnh
{
u_char flags;
struct rib *state;
struct list *client_list;
struct route_node *node;
};
5. User interface changes
quagga# show ip nht
3.3.3.3
resolved via kernel
via 11.0.0.6, swp1
Client list: bgp(fd 12)
11.0.0.10
resolved via connected
is directly connected, swp2
Client list: bgp(fd 12)
11.0.0.18
resolved via connected
is directly connected, swp4
Client list: bgp(fd 12)
11.11.11.11
resolved via kernel
via 10.0.1.2, eth0
Client list: bgp(fd 12)
quagga# show ip bgp nexthop
Current BGP nexthop cache:
3.3.3.3 valid [IGP metric 0], #paths 3
Last update: Wed Oct 16 04:43:49 2013
11.0.0.10 valid [IGP metric 1], #paths 1
Last update: Wed Oct 16 04:43:51 2013
11.0.0.18 valid [IGP metric 1], #paths 2
Last update: Wed Oct 16 04:43:47 2013
11.11.11.11 valid [IGP metric 0], #paths 1
Last update: Wed Oct 16 04:43:47 2013
quagga# show ipv6 nht
quagga# show ip bgp nexthop detail
quagga# debug bgp nht
quagga# debug zebra nht
6. Sample test cases
r2----r3
/ \ /
r1----r4
- Verify that a change in IGP cost triggers NHT
+ shutdown the r1-r4 and r2-r4 links
+ no shut the r1-r4 and r2-r4 links and wait for OSPF to come back
up
+ We should be back to the original nexthop via r4 now
- Verify that a NH becoming unreachable triggers NHT
+ Shutdown all links to r4
- Verify that a NH becoming reachable triggers NHT
+ no shut all links to r4
7. Future work
- route-policy for next hop validation (e.g. ignore default route)
- damping for rapid next hop changes
- prioritized handling of nexthop changes ((un)reachability vs. metric
changes)
- handling recursion loop, e.g.
11.11.11.11/32 -> 12.12.12.12
12.12.12.12/32 -> 11.11.11.11
11.0.0.0/8 -> <interface>
- better statistics
Addresses upstream comments.
"show ip bgp nexthop detail" couldn't display multiple NHs due to a bug.
Fix that.
Fix reference counts for the nexthop cache entries
Signed-off-by: Pradosh Mohapatra <pmohapat@cumulusnetworks.com>
Signed-off-by: Daniel Walton <dwalton@cumulusnetworks.com>
Signed-off-by: Dinesh Dutt <ddutt@cumulusnetworks.com>
Signed-off-by: Donald Sharp <sharpd@cumulusnetworks.com>
Signed-off-by: Vivek Venkatraman <vivek@cumulusnetworks.com>
Fix reference counts for the nexthop cache entries.
Signed-off-by: Vivek Venkatraman <vivek@cumulusnetworks.com>
Edited-by: Paul Jakma <paul.jakma@hpe.com>
- Fix nexthop_ipv6_add defs in rib.h not having been modified with rib_ prefix.
- Remove rib_lookup_and_pushup, appears not to be used except for
!HAVE_NETLINK && HAVE_STRUCT_IFALIASREQ case of ioctl.c::if_set_prefix,
so it's not being used at all on platform with most testing of RIB.
diff --git a/zebra/Makefile.am b/zebra/Makefile.am
index abd3797..b23f9f1 100644
--- a/zebra/Makefile.am
+++ b/zebra/Makefile.am
@@ -40,17 +40,18 @@
zserv.c main.c interface.c connected.c zebra_rib.c zebra_routemap.c \
redistribute.c debug.c rtadv.c zebra_snmp.c zebra_vty.c \
irdp_main.c irdp_interface.c irdp_packet.c router-id.c zebra_fpm.c \
+ zebra_rnh.c \
$(othersrc) $(protobuf_srcs) $(dev_srcs)
testzebra_SOURCES = test_main.c zebra_rib.c interface.c connected.c debug.c \
zebra_vty.c \
- kernel_null.c redistribute_null.c ioctl_null.c misc_null.c
+ kernel_null.c redistribute_null.c ioctl_null.c misc_null.c zebra_rnh_null.c
noinst_HEADERS = \
connected.h ioctl.h rib.h rt.h zserv.h redistribute.h debug.h rtadv.h \
interface.h ipforward.h irdp.h router-id.h kernel_socket.h \
rt_netlink.h zebra_fpm.h zebra_fpm_private.h \
- ioctl_solaris.h
+ ioctl_solaris.h zebra_rnh.h
zebra_LDADD = $(otherobj) ../lib/libzebra.la $(LIBCAP) $(Q_FPM_PB_CLIENT_LDOPTS)
diff --git a/zebra/debug.c b/zebra/debug.c
index 537c476..fbeef52 100644
--- a/zebra/debug.c
+++ b/zebra/debug.c
@@ -30,6 +30,7 @@
unsigned long zebra_debug_kernel;
unsigned long zebra_debug_rib;
unsigned long zebra_debug_fpm;
+unsigned long zebra_debug_nht;
DEFUN (show_debugging_zebra,
show_debugging_zebra_cmd,
@@ -74,6 +75,8 @@
if (IS_ZEBRA_DEBUG_FPM)
vty_out (vty, " Zebra FPM debugging is on%s", VTY_NEWLINE);
+ if (IS_ZEBRA_DEBUG_NHT)
+ vty_out (vty, " Zebra next-hop tracking debugging is on%s", VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -89,6 +92,17 @@
return CMD_WARNING;
}
+DEFUN (debug_zebra_nht,
+ debug_zebra_nht_cmd,
+ "debug zebra nht",
+ DEBUG_STR
+ "Zebra configuration\n"
+ "Debug option set for zebra next hop tracking\n")
+{
+ zebra_debug_nht = ZEBRA_DEBUG_NHT;
+ return CMD_WARNING;
+}
+
DEFUN (debug_zebra_packet,
debug_zebra_packet_cmd,
"debug zebra packet",
@@ -197,6 +211,18 @@
return CMD_SUCCESS;
}
+DEFUN (no_debug_zebra_nht,
+ no_debug_zebra_nht_cmd,
+ "no debug zebra nht",
+ NO_STR
+ DEBUG_STR
+ "Zebra configuration\n"
+ "Debug option set for zebra next hop tracking\n")
+{
+ zebra_debug_nht = 0;
+ return CMD_SUCCESS;
+}
+
DEFUN (no_debug_zebra_packet,
no_debug_zebra_packet_cmd,
"no debug zebra packet",
@@ -353,6 +379,7 @@
install_element (ENABLE_NODE, &show_debugging_zebra_cmd);
install_element (ENABLE_NODE, &debug_zebra_events_cmd);
+ install_element (ENABLE_NODE, &debug_zebra_nht_cmd);
install_element (ENABLE_NODE, &debug_zebra_packet_cmd);
install_element (ENABLE_NODE, &debug_zebra_packet_direct_cmd);
install_element (ENABLE_NODE, &debug_zebra_packet_detail_cmd);
@@ -361,6 +388,7 @@
install_element (ENABLE_NODE, &debug_zebra_rib_q_cmd);
install_element (ENABLE_NODE, &debug_zebra_fpm_cmd);
install_element (ENABLE_NODE, &no_debug_zebra_events_cmd);
+ install_element (ENABLE_NODE, &no_debug_zebra_nht_cmd);
install_element (ENABLE_NODE, &no_debug_zebra_packet_cmd);
install_element (ENABLE_NODE, &no_debug_zebra_kernel_cmd);
install_element (ENABLE_NODE, &no_debug_zebra_rib_cmd);
@@ -368,6 +396,7 @@
install_element (ENABLE_NODE, &no_debug_zebra_fpm_cmd);
install_element (CONFIG_NODE, &debug_zebra_events_cmd);
+ install_element (CONFIG_NODE, &debug_zebra_nht_cmd);
install_element (CONFIG_NODE, &debug_zebra_packet_cmd);
install_element (CONFIG_NODE, &debug_zebra_packet_direct_cmd);
install_element (CONFIG_NODE, &debug_zebra_packet_detail_cmd);
@@ -376,6 +405,7 @@
install_element (CONFIG_NODE, &debug_zebra_rib_q_cmd);
install_element (CONFIG_NODE, &debug_zebra_fpm_cmd);
install_element (CONFIG_NODE, &no_debug_zebra_events_cmd);
+ install_element (CONFIG_NODE, &no_debug_zebra_nht_cmd);
install_element (CONFIG_NODE, &no_debug_zebra_packet_cmd);
install_element (CONFIG_NODE, &no_debug_zebra_kernel_cmd);
install_element (CONFIG_NODE, &no_debug_zebra_rib_cmd);
diff --git a/zebra/debug.h b/zebra/debug.h
index d9231a2..0fb4dd9 100644
--- a/zebra/debug.h
+++ b/zebra/debug.h
@@ -37,6 +37,7 @@
#define ZEBRA_DEBUG_RIB_Q 0x02
#define ZEBRA_DEBUG_FPM 0x01
+#define ZEBRA_DEBUG_NHT 0x01
/* Debug related macro. */
#define IS_ZEBRA_DEBUG_EVENT (zebra_debug_event & ZEBRA_DEBUG_EVENT)
@@ -52,12 +53,14 @@
#define IS_ZEBRA_DEBUG_RIB_Q (zebra_debug_rib & ZEBRA_DEBUG_RIB_Q)
#define IS_ZEBRA_DEBUG_FPM (zebra_debug_fpm & ZEBRA_DEBUG_FPM)
+#define IS_ZEBRA_DEBUG_NHT (zebra_debug_nht & ZEBRA_DEBUG_NHT)
extern unsigned long zebra_debug_event;
extern unsigned long zebra_debug_packet;
extern unsigned long zebra_debug_kernel;
extern unsigned long zebra_debug_rib;
extern unsigned long zebra_debug_fpm;
+extern unsigned long zebra_debug_nht;
extern void zebra_debug_init (void);
diff --git a/zebra/ioctl.c b/zebra/ioctl.c
index f7a7ff4..e1ee429 100644
--- a/zebra/ioctl.c
+++ b/zebra/ioctl.c
@@ -196,7 +196,6 @@
struct prefix_ipv4 *p;
p = (struct prefix_ipv4 *) ifc->address;
- rib_lookup_and_pushup (p);
memset (&addreq, 0, sizeof addreq);
strncpy ((char *)&addreq.ifra_name, ifp->name, sizeof addreq.ifra_name);
diff --git a/zebra/rib.h b/zebra/rib.h
index 677e395..9773cba 100644
--- a/zebra/rib.h
+++ b/zebra/rib.h
@@ -27,18 +27,10 @@
#include "prefix.h"
#include "table.h"
#include "queue.h"
+#include "nexthop.h"
#define DISTANCE_INFINITY 255
-/* Routing information base. */
-
-union g_addr {
- struct in_addr ipv4;
-#ifdef HAVE_IPV6
- struct in6_addr ipv6;
-#endif /* HAVE_IPV6 */
-};
-
struct rib
{
/* Link list. */
@@ -207,50 +199,6 @@
*/
};
-enum nexthop_types_t
-{
- NEXTHOP_TYPE_IFINDEX = 1, /* Directly connected. */
- NEXTHOP_TYPE_IFNAME, /* Interface route. */
- NEXTHOP_TYPE_IPV4, /* IPv4 nexthop. */
- NEXTHOP_TYPE_IPV4_IFINDEX, /* IPv4 nexthop with ifindex. */
- NEXTHOP_TYPE_IPV4_IFNAME, /* IPv4 nexthop with ifname. */
- NEXTHOP_TYPE_IPV6, /* IPv6 nexthop. */
- NEXTHOP_TYPE_IPV6_IFINDEX, /* IPv6 nexthop with ifindex. */
- NEXTHOP_TYPE_IPV6_IFNAME, /* IPv6 nexthop with ifname. */
- NEXTHOP_TYPE_BLACKHOLE, /* Null0 nexthop. */
-};
-
-/* Nexthop structure. */
-struct nexthop
-{
- struct nexthop *next;
- struct nexthop *prev;
-
- /* Interface index. */
- char *ifname;
- ifindex_t ifindex;
-
- enum nexthop_types_t type;
-
- u_char flags;
-#define NEXTHOP_FLAG_ACTIVE (1 << 0) /* This nexthop is alive. */
-#define NEXTHOP_FLAG_FIB (1 << 1) /* FIB nexthop. */
-#define NEXTHOP_FLAG_RECURSIVE (1 << 2) /* Recursive nexthop. */
-#define NEXTHOP_FLAG_ONLINK (1 << 3) /* Nexthop should be installed onlink. */
-
- /* Nexthop address */
- union g_addr gate;
- union g_addr src;
-
- /* Nexthops obtained by recursive resolution.
- *
- * If the nexthop struct needs to be resolved recursively,
- * NEXTHOP_FLAG_RECURSIVE will be set in flags and the nexthops
- * obtained by recursive resolution will be added to `resolved'.
- * Only one level of recursive resolution is currently supported. */
- struct nexthop *resolved;
-};
-
/* The following for loop allows to iterate over the nexthop
* structure of routes.
*
@@ -370,6 +318,9 @@
#if defined (HAVE_RTADV)
struct rtadv rtadv;
#endif /* HAVE_RTADV */
+
+ /* Recursive Nexthop table */
+ struct route_table *rnh_table[AFI_MAX];
};
/*
@@ -425,18 +376,20 @@
extern enum multicast_mode multicast_mode_ipv4_get (void);
extern const char *nexthop_type_to_str (enum nexthop_types_t nh_type);
-extern struct nexthop *nexthop_ifindex_add (struct rib *, ifindex_t);
-extern struct nexthop *nexthop_ifname_add (struct rib *, char *);
-extern struct nexthop *nexthop_blackhole_add (struct rib *);
-extern struct nexthop *nexthop_ipv4_add (struct rib *, struct in_addr *,
- struct in_addr *);
-extern struct nexthop *nexthop_ipv4_ifindex_add (struct rib *,
- struct in_addr *,
- struct in_addr *,
- ifindex_t);
+extern struct nexthop *rib_nexthop_ifindex_add (struct rib *, ifindex_t);
+extern struct nexthop *rib_nexthop_ifname_add (struct rib *, char *);
+extern struct nexthop *rib_nexthop_blackhole_add (struct rib *);
+extern struct nexthop *rib_nexthop_ipv4_add (struct rib *, struct in_addr *,
+ struct in_addr *);
+extern struct nexthop *rib_nexthop_ipv4_ifindex_add (struct rib *,
+ struct in_addr *,
+ struct in_addr *,
+ ifindex_t);
+
+extern void rib_nexthop_add (struct rib *rib, struct nexthop *nexthop);
+
extern int nexthop_has_fib_child(struct nexthop *);
extern void rib_lookup_and_dump (struct prefix_ipv4 *);
-extern void rib_lookup_and_pushup (struct prefix_ipv4 *);
#define rib_dump(prefix ,rib) _rib_dump(__func__, prefix, rib)
extern void _rib_dump (const char *,
union prefix46constptr, const struct rib *);
@@ -448,11 +401,12 @@
#define ZEBRA_RIB_FOUND_CONNECTED 2
#define ZEBRA_RIB_NOTFOUND 3
-extern struct nexthop *nexthop_ipv6_add (struct rib *, struct in6_addr *);
-extern struct nexthop *nexthop_ipv6_ifindex_add (struct rib *,
- struct in6_addr *,
- ifindex_t);
+extern struct nexthop *rib_nexthop_ipv6_add (struct rib *, struct in6_addr *);
+extern struct nexthop *rib_nexthop_ipv6_ifindex_add (struct rib *,
+ struct in6_addr *,
+ ifindex_t);
+extern struct zebra_vrf *zebra_vrf_lookup (vrf_id_t vrf_id);
extern struct zebra_vrf *zebra_vrf_alloc (vrf_id_t);
extern struct route_table *zebra_vrf_table (afi_t, safi_t, vrf_id_t);
extern struct route_table *zebra_vrf_static_table (afi_t, safi_t, vrf_id_t);
diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c
index 42cb9d4..105e559 100644
--- a/zebra/rt_netlink.c
+++ b/zebra/rt_netlink.c
@@ -38,6 +38,7 @@
#include "thread.h"
#include "privs.h"
#include "vrf.h"
+#include "nexthop.h"
#include "zebra/zserv.h"
#include "zebra/rt.h"
@@ -851,12 +852,12 @@
if (gate)
{
if (index)
- nexthop_ipv4_ifindex_add (rib, gate, src, index);
+ rib_nexthop_ipv4_ifindex_add (rib, gate, src, index);
else
- nexthop_ipv4_add (rib, gate, src);
+ rib_nexthop_ipv4_add (rib, gate, src);
}
else
- nexthop_ifindex_add (rib, index);
+ rib_nexthop_ifindex_add (rib, index);
len -= NLMSG_ALIGN(rtnh->rtnh_len);
rtnh = RTNH_NEXT(rtnh);
@@ -1065,12 +1066,12 @@
if (gate)
{
if (index)
- nexthop_ipv4_ifindex_add (rib, gate, src, index);
+ rib_nexthop_ipv4_ifindex_add (rib, gate, src, index);
else
- nexthop_ipv4_add (rib, gate, src);
+ rib_nexthop_ipv4_add (rib, gate, src);
}
else
- nexthop_ifindex_add (rib, index);
+ rib_nexthop_ifindex_add (rib, index);
len -= NLMSG_ALIGN(rtnh->rtnh_len);
rtnh = RTNH_NEXT(rtnh);
diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c
index 96e897b..59e861b 100644
--- a/zebra/zebra_fpm_netlink.c
+++ b/zebra/zebra_fpm_netlink.c
@@ -29,6 +29,7 @@
#include "rib.h"
#include "rt_netlink.h"
+#include "nexthop.h"
#include "zebra_fpm_private.h"
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index eec2976..0aa516c 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -35,6 +35,7 @@
#include "prefix.h"
#include "routemap.h"
#include "vrf.h"
+#include "nexthop.h"
#include "zebra/rib.h"
#include "zebra/rt.h"
@@ -42,6 +43,7 @@
#include "zebra/redistribute.h"
#include "zebra/debug.h"
#include "zebra/zebra_fpm.h"
+#include "zebra/zebra_rnh.h"
/* Default rtm_table for all clients */
extern struct zebra_t zebrad;
@@ -110,57 +112,17 @@
#define rnode_info(node, ...) \
_rnode_zlog(__func__, node, LOG_INFO, __VA_ARGS__)
-/*
- * nexthop_type_to_str
- */
-const char *
-nexthop_type_to_str (enum nexthop_types_t nh_type)
-{
- static const char *desc[] = {
- "none",
- "Directly connected",
- "Interface route",
- "IPv4 nexthop",
- "IPv4 nexthop with ifindex",
- "IPv4 nexthop with ifname",
- "IPv6 nexthop",
- "IPv6 nexthop with ifindex",
- "IPv6 nexthop with ifname",
- "Null0 nexthop",
- };
-
- if (nh_type >= ZEBRA_NUM_OF (desc))
- return "<Invalid nh type>";
-
- return desc[nh_type];
-}
-
-/* Add nexthop to the end of a nexthop list. */
-static void
-_nexthop_add (struct nexthop **target, struct nexthop *nexthop)
-{
- struct nexthop *last;
-
- for (last = *target; last && last->next; last = last->next)
- ;
- if (last)
- last->next = nexthop;
- else
- *target = nexthop;
- nexthop->prev = last;
-}
-
/* Add nexthop to the end of a rib node's nexthop list */
-static void
-nexthop_add (struct rib *rib, struct nexthop *nexthop)
+void
+rib_nexthop_add (struct rib *rib, struct nexthop *nexthop)
{
- _nexthop_add(&rib->nexthop, nexthop);
+ nexthop_add(&rib->nexthop, nexthop);
rib->nexthop_num++;
}
/* Delete specified nexthop from the list. */
static void
-nexthop_delete (struct rib *rib, struct nexthop *nexthop)
+rib_nexthop_delete (struct rib *rib, struct nexthop *nexthop)
{
if (nexthop->next)
nexthop->next->prev = nexthop->prev;
@@ -171,150 +133,124 @@
rib->nexthop_num--;
}
-static void nexthops_free(struct nexthop *nexthop);
-
-/* Free nexthop. */
-static void
-nexthop_free (struct nexthop *nexthop)
-{
- if (nexthop->ifname)
- XFREE (0, nexthop->ifname);
- if (nexthop->resolved)
- nexthops_free(nexthop->resolved);
- XFREE (MTYPE_NEXTHOP, nexthop);
-}
-
-/* Frees a list of nexthops */
-static void
-nexthops_free (struct nexthop *nexthop)
-{
- struct nexthop *nh, *next;
-
- for (nh = nexthop; nh; nh = next)
- {
- next = nh->next;
- nexthop_free (nh);
- }
-}
-
struct nexthop *
-nexthop_ifindex_add (struct rib *rib, ifindex_t ifindex)
+rib_nexthop_ifindex_add (struct rib *rib, ifindex_t ifindex)
{
struct nexthop *nexthop;
- nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
+ nexthop = nexthop_new ();
nexthop->type = NEXTHOP_TYPE_IFINDEX;
nexthop->ifindex = ifindex;
- nexthop_add (rib, nexthop);
+ rib_nexthop_add (rib, nexthop);
return nexthop;
}
struct nexthop *
-nexthop_ifname_add (struct rib *rib, char *ifname)
+rib_nexthop_ifname_add (struct rib *rib, char *ifname)
{
struct nexthop *nexthop;
- nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
+ nexthop = nexthop_new ();
nexthop->type = NEXTHOP_TYPE_IFNAME;
nexthop->ifname = XSTRDUP (MTYPE_TMP, ifname);
- nexthop_add (rib, nexthop);
+ rib_nexthop_add (rib, nexthop);
return nexthop;
}
struct nexthop *
-nexthop_ipv4_add (struct rib *rib, struct in_addr *ipv4, struct in_addr *src)
+rib_nexthop_ipv4_add (struct rib *rib, struct in_addr *ipv4, struct in_addr *src)
{
struct nexthop *nexthop;
- nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
+ nexthop = nexthop_new ();
nexthop->type = NEXTHOP_TYPE_IPV4;
nexthop->gate.ipv4 = *ipv4;
if (src)
nexthop->src.ipv4 = *src;
- nexthop_add (rib, nexthop);
+ rib_nexthop_add (rib, nexthop);
return nexthop;
}
struct nexthop *
-nexthop_ipv4_ifindex_add (struct rib *rib, struct in_addr *ipv4,
- struct in_addr *src, ifindex_t ifindex)
+rib_nexthop_ipv4_ifindex_add (struct rib *rib, struct in_addr *ipv4,
+ struct in_addr *src, ifindex_t ifindex)
{
struct nexthop *nexthop;
- nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
+ nexthop = nexthop_new ();
nexthop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
nexthop->gate.ipv4 = *ipv4;
if (src)
nexthop->src.ipv4 = *src;
nexthop->ifindex = ifindex;
- nexthop_add (rib, nexthop);
+ rib_nexthop_add (rib, nexthop);
return nexthop;
}
struct nexthop *
-nexthop_ipv6_add (struct rib *rib, struct in6_addr *ipv6)
+rib_nexthop_ipv6_add (struct rib *rib, struct in6_addr *ipv6)
{
struct nexthop *nexthop;
- nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
+ nexthop = nexthop_new ();
nexthop->type = NEXTHOP_TYPE_IPV6;
nexthop->gate.ipv6 = *ipv6;
- nexthop_add (rib, nexthop);
+ rib_nexthop_add (rib, nexthop);
return nexthop;
}
static struct nexthop *
-nexthop_ipv6_ifname_add (struct rib *rib, struct in6_addr *ipv6,
- char *ifname)
+rib_nexthop_ipv6_ifname_add (struct rib *rib, struct in6_addr *ipv6,
+ char *ifname)
{
struct nexthop *nexthop;
- nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
+ nexthop = nexthop_new ();
nexthop->type = NEXTHOP_TYPE_IPV6_IFNAME;
nexthop->gate.ipv6 = *ipv6;
nexthop->ifname = XSTRDUP (MTYPE_TMP, ifname);
- nexthop_add (rib, nexthop);
+ rib_nexthop_add (rib, nexthop);
return nexthop;
}
struct nexthop *
-nexthop_ipv6_ifindex_add (struct rib *rib, struct in6_addr *ipv6,
- ifindex_t ifindex)
+rib_nexthop_ipv6_ifindex_add (struct rib *rib, struct in6_addr *ipv6,
+ ifindex_t ifindex)
{
struct nexthop *nexthop;
- nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
+ nexthop = nexthop_new ();
nexthop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
nexthop->gate.ipv6 = *ipv6;
nexthop->ifindex = ifindex;
- nexthop_add (rib, nexthop);
+ rib_nexthop_add (rib, nexthop);
return nexthop;
}
struct nexthop *
-nexthop_blackhole_add (struct rib *rib)
+rib_nexthop_blackhole_add (struct rib *rib)
{
struct nexthop *nexthop;
- nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
+ nexthop = nexthop_new ();
nexthop->type = NEXTHOP_TYPE_BLACKHOLE;
SET_FLAG (rib->flags, ZEBRA_FLAG_BLACKHOLE);
- nexthop_add (rib, nexthop);
+ rib_nexthop_add (rib, nexthop);
return nexthop;
}
@@ -455,7 +391,7 @@
resolved_hop->ifindex = newhop->ifindex;
}
- _nexthop_add(&nexthop->resolved, resolved_hop);
+ nexthop_add(&nexthop->resolved, resolved_hop);
}
resolved = 1;
}
@@ -592,7 +528,7 @@
resolved_hop->ifindex = newhop->ifindex;
}
- _nexthop_add(&nexthop->resolved, resolved_hop);
+ nexthop_add(&nexthop->resolved, resolved_hop);
}
resolved = 1;
}
@@ -1440,6 +1376,18 @@
return 1;
}
+/*
+ * All meta queues have been processed. Trigger next-hop evaluation.
+ */
+static void
+meta_queue_process_complete (struct work_queue *dummy)
+{
+ zebra_evaluate_rnh_table(0, AF_INET);
+#ifdef HAVE_IPV6
+ zebra_evaluate_rnh_table(0, AF_INET6);
+#endif /* HAVE_IPV6 */
+}
+
/* Dispatch the meta queue by picking, processing and unlocking the next RN from
* a non-empty sub-queue with lowest priority. wq is equal to zebra->ribq and data
* is pointed to the meta queue structure.
@@ -1592,6 +1540,7 @@
/* fill in the work queue spec */
zebra->ribq->spec.workfunc = &meta_queue_process;
zebra->ribq->spec.errorfunc = NULL;
+ zebra->ribq->spec.completion_func = &meta_queue_process_complete;
/* XXX: TODO: These should be runtime configurable via vty */
zebra->ribq->spec.max_retries = 3;
zebra->ribq->spec.hold = rib_process_hold_time;
@@ -1815,12 +1764,12 @@
if (gate)
{
if (ifindex)
- nexthop_ipv4_ifindex_add (rib, gate, src, ifindex);
+ rib_nexthop_ipv4_ifindex_add (rib, gate, src, ifindex);
else
- nexthop_ipv4_add (rib, gate, src);
+ rib_nexthop_ipv4_add (rib, gate, src);
}
else
- nexthop_ifindex_add (rib, ifindex);
+ rib_nexthop_ifindex_add (rib, ifindex);
/* If this route is kernel route, set FIB flag to the route. */
if (type == ZEBRA_ROUTE_KERNEL || type == ZEBRA_ROUTE_CONNECT)
@@ -1954,58 +1903,6 @@
}
}
-/* Check if requested address assignment will fail due to another
- * route being installed by zebra in FIB already. Take necessary
- * actions, if needed: remove such a route from FIB and deSELECT
- * corresponding RIB entry. Then put affected RN into RIBQ head.
- */
-void rib_lookup_and_pushup (struct prefix_ipv4 * p)
-{
- struct route_table *table;
- struct route_node *rn;
- struct rib *rib;
- unsigned changed = 0;
-
- if (NULL == (table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, VRF_DEFAULT)))
- {
- zlog_err ("%s: zebra_vrf_table() returned NULL", __func__);
- return;
- }
-
- /* No matches would be the simplest case. */
- if (NULL == (rn = route_node_lookup (table, (struct prefix *) p)))
- return;
-
- /* Unlock node. */
- route_unlock_node (rn);
-
- /* Check all RIB entries. In case any changes have to be done, requeue
- * the RN into RIBQ head. If the routing message about the new connected
- * route (generated by the IP address we are going to assign very soon)
- * comes before the RIBQ is processed, the new RIB entry will join
- * RIBQ record already on head. This is necessary for proper revalidation
- * of the rest of the RIB.
- */
- RNODE_FOREACH_RIB (rn, rib)
- {
- if (CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB) &&
- ! RIB_SYSTEM_ROUTE (rib))
- {
- changed = 1;
- if (IS_ZEBRA_DEBUG_RIB)
- {
- char buf[PREFIX_STRLEN];
- zlog_debug ("%s: freeing way for connected prefix %s", __func__,
- prefix2str(&rn->p, buf, sizeof(buf)));
- rib_dump (&rn->p, rib);
- }
- rib_uninstall (rn, rib);
- }
- }
- if (changed)
- rib_queue_add (&zebrad, rn);
-}
-
int
rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib, safi_t safi)
{
@@ -2251,22 +2148,22 @@
switch (si->type)
{
case STATIC_IPV4_GATEWAY:
- nexthop_ipv4_add (rib, &si->addr.ipv4, NULL);
+ rib_nexthop_ipv4_add (rib, &si->addr.ipv4, NULL);
break;
case STATIC_IPV4_IFNAME:
- nexthop_ifname_add (rib, si->ifname);
+ rib_nexthop_ifname_add (rib, si->ifname);
break;
case STATIC_IPV4_BLACKHOLE:
- nexthop_blackhole_add (rib);
+ rib_nexthop_blackhole_add (rib);
break;
case STATIC_IPV6_GATEWAY:
- nexthop_ipv6_add (rib, &si->addr.ipv6);
+ rib_nexthop_ipv6_add (rib, &si->addr.ipv6);
break;
case STATIC_IPV6_IFNAME:
- nexthop_ifname_add (rib, si->ifname);
+ rib_nexthop_ifname_add (rib, si->ifname);
break;
case STATIC_IPV6_GATEWAY_IFNAME:
- nexthop_ipv6_ifname_add (rib, &si->addr.ipv6, si->ifname);
+ rib_nexthop_ipv6_ifname_add (rib, &si->addr.ipv6, si->ifname);
break;
}
rib_queue_add (&zebrad, rn);
@@ -2286,22 +2183,22 @@
switch (si->type)
{
case STATIC_IPV4_GATEWAY:
- nexthop_ipv4_add (rib, &si->addr.ipv4, NULL);
+ rib_nexthop_ipv4_add (rib, &si->addr.ipv4, NULL);
break;
case STATIC_IPV4_IFNAME:
- nexthop_ifname_add (rib, si->ifname);
+ rib_nexthop_ifname_add (rib, si->ifname);
break;
case STATIC_IPV4_BLACKHOLE:
- nexthop_blackhole_add (rib);
+ rib_nexthop_blackhole_add (rib);
break;
case STATIC_IPV6_GATEWAY:
- nexthop_ipv6_add (rib, &si->addr.ipv6);
+ rib_nexthop_ipv6_add (rib, &si->addr.ipv6);
break;
case STATIC_IPV6_IFNAME:
- nexthop_ifname_add (rib, si->ifname);
+ rib_nexthop_ifname_add (rib, si->ifname);
break;
case STATIC_IPV6_GATEWAY_IFNAME:
- nexthop_ipv6_ifname_add (rib, &si->addr.ipv6, si->ifname);
+ rib_nexthop_ipv6_ifname_add (rib, &si->addr.ipv6, si->ifname);
break;
}
@@ -2396,7 +2293,7 @@
{
if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
rib_uninstall (rn, rib);
- nexthop_delete (rib, nexthop);
+ rib_nexthop_delete (rib, nexthop);
nexthop_free (nexthop);
rib_queue_add (&zebrad, rn);
}
@@ -2632,12 +2529,12 @@
if (gate)
{
if (ifindex)
- nexthop_ipv6_ifindex_add (rib, gate, ifindex);
+ rib_nexthop_ipv6_ifindex_add (rib, gate, ifindex);
else
- nexthop_ipv6_add (rib, gate);
+ rib_nexthop_ipv6_add (rib, gate);
}
else
- nexthop_ifindex_add (rib, ifindex);
+ rib_nexthop_ifindex_add (rib, ifindex);
/* If this route is kernel route, set FIB flag to the route. */
if (type == ZEBRA_ROUTE_KERNEL || type == ZEBRA_ROUTE_CONNECT)
@@ -2878,7 +2775,6 @@
return 0;
}
-
/* Add static route into static route configuration. */
int
static_add_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate,
@@ -3321,6 +3217,13 @@
return table;
}
+/* Lookup VRF by identifier. */
+struct zebra_vrf *
+zebra_vrf_lookup (vrf_id_t vrf_id)
+{
+ return vrf_info_lookup (vrf_id);
+}
+
/*
* Create a routing table for the specific AFI/SAFI in the given VRF.
*/
@@ -3363,6 +3266,9 @@
zvrf->stable[AFI_IP][SAFI_MULTICAST] = route_table_init ();
zvrf->stable[AFI_IP6][SAFI_MULTICAST] = route_table_init ();
+ zvrf->rnh_table[AFI_IP] = route_table_init();
+ zvrf->rnh_table[AFI_IP6] = route_table_init();
+
/* Set VRF ID */
zvrf->vrf_id = vrf_id;
diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c
new file mode 100644
index 0000000..3e02812
--- /dev/null
+++ b/zebra/zebra_rnh.c
@@ -0,0 +1,603 @@
+/* Zebra next hop tracking code
+ * Copyright (C) 2013 Cumulus Networks, Inc.
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "table.h"
+#include "memory.h"
+#include "str.h"
+#include "command.h"
+#include "if.h"
+#include "log.h"
+#include "sockunion.h"
+#include "linklist.h"
+#include "thread.h"
+#include "workqueue.h"
+#include "prefix.h"
+#include "routemap.h"
+#include "stream.h"
+#include "nexthop.h"
+
+#include "zebra/rib.h"
+#include "zebra/rt.h"
+#include "zebra/zserv.h"
+#include "zebra/redistribute.h"
+#include "zebra/debug.h"
+#include "zebra/zebra_rnh.h"
+
+#define lookup_rnh_table(v, f) \
+({ \
+ struct zebra_vrf *zvrf; \
+ struct route_table *t = NULL; \
+ zvrf = zebra_vrf_lookup(v); \
+ if (zvrf) \
+ t = zvrf->rnh_table[family2afi(f)]; \
+ t; \
+})
+
+static void free_state(struct rib *rib);
+static void copy_state(struct rnh *rnh, struct rib *rib);
+static int compare_state(struct rib *r1, struct rib *r2);
+static int send_client(struct rnh *rnh, struct zserv *client, vrf_id_t vrf_id);
+static void print_rnh(struct route_node *rn, struct vty *vty);
+
+char *
+rnh_str (struct rnh *rnh, char *buf, int size)
+{
+ prefix2str(&(rnh->node->p), buf, size);
+ return buf;
+}
+
+struct rnh *
+zebra_add_rnh (struct prefix *p, vrf_id_t vrfid)
+{
+ struct route_table *table;
+ struct route_node *rn;
+ struct rnh *rnh = NULL;
+
+ if (IS_ZEBRA_DEBUG_NHT)
+ {
+ char buf[INET6_ADDRSTRLEN];
+ prefix2str(p, buf, INET6_ADDRSTRLEN);
+ zlog_debug("add rnh %s in vrf %d", buf, vrfid);
+ }
+ table = lookup_rnh_table(vrfid, PREFIX_FAMILY(p));
+ if (!table)
+ {
+ zlog_debug("add_rnh: rnh table not found\n");
+ return NULL;
+ }
+
+ /* Make it sure prefixlen is applied to the prefix. */
+ apply_mask (p);
+
+ /* Lookup (or add) route node.*/
+ rn = route_node_get (table, p);
+
+ if (!rn->info)
+ {
+ rnh = XCALLOC(MTYPE_RNH, sizeof(struct rnh));
+ rnh->client_list = list_new();
+ route_lock_node (rn);
+ rn->info = rnh;
+ rnh->node = rn;
+ }
+
+ route_unlock_node (rn);
+ return (rn->info);
+}
+
+struct rnh *
+zebra_lookup_rnh (struct prefix *p, vrf_id_t vrfid)
+{
+ struct route_table *table;
+ struct route_node *rn;
+
+ table = lookup_rnh_table(vrfid, PREFIX_FAMILY(p));
+ if (!table)
+ return NULL;
+
+ /* Make it sure prefixlen is applied to the prefix. */
+ apply_mask (p);
+
+ /* Lookup route node.*/
+ rn = route_node_lookup (table, p);
+ if (!rn)
+ return NULL;
+
+ route_unlock_node (rn);
+ return (rn->info);
+}
+
+void
+zebra_delete_rnh (struct rnh *rnh)
+{
+ struct route_node *rn;
+
+ if (!rnh || !(rn = rnh->node))
+ return;
+
+ if (IS_ZEBRA_DEBUG_NHT)
+ {
+ char buf[INET6_ADDRSTRLEN];
+ zlog_debug("delete rnh %s", rnh_str(rnh, buf, INET6_ADDRSTRLEN));
+ }
+
+ list_free(rnh->client_list);
+ free_state(rnh->state);
+ XFREE(MTYPE_RNH, rn->info);
+ rn->info = NULL;
+ route_unlock_node (rn);
+ return;
+}
+
+void
+zebra_add_rnh_client (struct rnh *rnh, struct zserv *client, vrf_id_t vrf_id)
+{
+ if (IS_ZEBRA_DEBUG_NHT)
+ {
+ char buf[INET6_ADDRSTRLEN];
+ zlog_debug("client %s registers rnh %s",
+ zebra_route_string(client->proto),
+ rnh_str(rnh, buf, INET6_ADDRSTRLEN));
+ }
+ if (!listnode_lookup(rnh->client_list, client))
+ {
+ listnode_add(rnh->client_list, client);
+ send_client(rnh, client, vrf_id);
+ }
+}
+
+void
+zebra_remove_rnh_client (struct rnh *rnh, struct zserv *client)
+{
+ if (IS_ZEBRA_DEBUG_NHT)
+ {
+ char buf[INET6_ADDRSTRLEN];
+ zlog_debug("client %s unregisters rnh %s",
+ zebra_route_string(client->proto),
+ rnh_str(rnh, buf, INET6_ADDRSTRLEN));
+ }
+ listnode_delete(rnh->client_list, client);
+ if (list_isempty(rnh->client_list))
+ zebra_delete_rnh(rnh);
+}
+
+int
+zebra_evaluate_rnh_table (vrf_id_t vrfid, int family)
+{
+ struct route_table *ptable;
+ struct route_table *ntable;
+ struct route_node *prn;
+ struct route_node *nrn;
+ struct rnh *rnh;
+ struct zserv *client;
+ struct listnode *node;
+ struct rib *rib;
+
+ ntable = lookup_rnh_table(vrfid, family);
+ if (!ntable)
+ {
+ zlog_debug("evaluate_rnh_table: rnh table not found\n");
+ return -1;
+ }
+
+ ptable = zebra_vrf_table(family2afi(family), SAFI_UNICAST, vrfid);
+ if (!ptable)
+ {
+ zlog_debug("evaluate_rnh_table: prefix table not found\n");
+ return -1;
+ }
+
+ for (nrn = route_top (ntable); nrn; nrn = route_next (nrn))
+ {
+ if (!nrn->info)
+ continue;
+
+ prn = route_node_match(ptable, &nrn->p);
+ if (!prn)
+ rib = NULL;
+ else
+ {
+ RNODE_FOREACH_RIB(prn, rib)
+ {
+ if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
+ continue;
+ if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED))
+ break;
+ }
+ }
+
+ rnh = nrn->info;
+ if (compare_state(rib, rnh->state))
+ {
+ if (IS_ZEBRA_DEBUG_NHT)
+ {
+ char bufn[INET6_ADDRSTRLEN];
+ char bufp[INET6_ADDRSTRLEN];
+ prefix2str(&nrn->p, bufn, INET6_ADDRSTRLEN);
+ if (prn)
+ prefix2str(&prn->p, bufp, INET6_ADDRSTRLEN);
+ else
+ strcpy(bufp, "null");
+ zlog_debug("rnh %s resolved through route %s - sending "
+ "nexthop %s event to clients", bufn, bufp,
+ rib ? "reachable" : "unreachable");
+ }
+ copy_state(rnh, rib);
+ for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client))
+ send_client(rnh, client, vrfid);
+ }
+ }
+ return 1;
+}
+
+int
+zebra_dispatch_rnh_table (vrf_id_t vrfid, int family, struct zserv *client)
+{
+ struct route_table *ntable;
+ struct route_node *nrn;
+ struct rnh *rnh;
+
+ ntable = lookup_rnh_table(vrfid, family);
+ if (!ntable)
+ {
+ zlog_debug("dispatch_rnh_table: rnh table not found\n");
+ return -1;
+ }
+
+ for (nrn = route_top (ntable); nrn; nrn = route_next (nrn))
+ {
+ if (!nrn->info)
+ continue;
+
+ rnh = nrn->info;
+ if (IS_ZEBRA_DEBUG_NHT)
+ {
+ char bufn[INET6_ADDRSTRLEN];
+ prefix2str(&nrn->p, bufn, INET6_ADDRSTRLEN);
+ zlog_debug("rnh %s - sending nexthop %s event to client %s", bufn,
+ rnh->state ? "reachable" : "unreachable",
+ zebra_route_string(client->proto));
+ }
+ send_client(rnh, client, vrfid);
+ }
+ return 1;
+}
+
+void
+zebra_print_rnh_table (vrf_id_t vrfid, int af, struct vty *vty)
+{
+ struct route_table *table;
+ struct route_node *rn;
+
+ table = lookup_rnh_table(vrfid, af);
+ if (!table)
+ {
+ zlog_debug("print_rnhs: rnh table not found\n");
+ return;
+ }
+
+ for (rn = route_top(table); rn; rn = route_next(rn))
+ if (rn->info)
+ print_rnh(rn, vty);
+}
+
+int
+zebra_cleanup_rnh_client (vrf_id_t vrfid, int family, struct zserv *client)
+{
+ struct route_table *ntable;
+ struct route_node *nrn;
+ struct rnh *rnh;
+
+ ntable = lookup_rnh_table(vrfid, family);
+ if (!ntable)
+ {
+ zlog_debug("cleanup_rnh_client: rnh table not found\n");
+ return -1;
+ }
+
+ for (nrn = route_top (ntable); nrn; nrn = route_next (nrn))
+ {
+ if (!nrn->info)
+ continue;
+
+ rnh = nrn->info;
+ if (IS_ZEBRA_DEBUG_NHT)
+ {
+ char bufn[INET6_ADDRSTRLEN];
+ prefix2str(&nrn->p, bufn, INET6_ADDRSTRLEN);
+ zlog_debug("rnh %s - cleaning state for client %s", bufn,
+ zebra_route_string(client->proto));
+ }
+ zebra_remove_rnh_client(rnh, client);
+ }
+ return 1;
+}
+
+/**
+ * free_state - free up the rib structure associated with the rnh.
+ */
+static void
+free_state (struct rib *rib)
+{
+ struct nexthop *nexthop, *next;
+
+ if (!rib)
+ return;
+
+ /* free RIB and nexthops */
+ for (nexthop = rib->nexthop; nexthop; nexthop = next)
+ {
+ next = nexthop->next;
+ nexthop_free (nexthop);
+ }
+ XFREE (MTYPE_RIB, rib);
+}
+
+/**
+ * copy_nexthop - copy a nexthop to the rib structure.
+ */
+static void
+rib_copy_nexthop (struct rib *state, struct nexthop *nh)
+{
+ struct nexthop *nexthop;
+
+ nexthop = nexthop_new();
+ nexthop->flags = nh->flags;
+ nexthop->type = nh->type;
+ nexthop->ifindex = nh->ifindex;
+ if (nh->ifname)
+ nexthop->ifname = XSTRDUP(0, nh->ifname);
+ memcpy(&(nexthop->gate), &(nh->gate), sizeof(union g_addr));
+ memcpy(&(nexthop->src), &(nh->src), sizeof(union g_addr));
+
+ rib_nexthop_add(state, nexthop);
+}
+
+static void
+copy_state (struct rnh *rnh, struct rib *rib)
+{
+ struct rib *state;
+ struct nexthop *nh;
+
+ if (rnh->state)
+ {
+ free_state(rnh->state);
+ rnh->state = NULL;
+ }
+
+ if (!rib)
+ return;
+
+ state = XCALLOC (MTYPE_RIB, sizeof (struct rib));
+ state->type = rib->type;
+ state->metric = rib->metric;
+
+ for (nh = rib->nexthop; nh; nh = nh->next)
+ rib_copy_nexthop(state, nh);
+ rnh->state = state;
+}
+
+static int
+compare_state (struct rib *r1, struct rib *r2)
+{
+ struct nexthop *nh1;
+ struct nexthop *nh2;
+ u_char found_nh = 0;
+
+ if (!r1 && !r2)
+ return 0;
+
+ if ((!r1 && r2) || (r1 && !r2))
+ return 1;
+
+ if (r1->metric != r2->metric)
+ return 1;
+
+ if (r1->nexthop_num != r2->nexthop_num)
+ return 1;
+
+ /* We need to verify that the nexthops for r1 match the nexthops for r2.
+ * Since it is possible for a rib entry to have the same nexthop multiple
+ * times (Example: [a,a]) we need to keep track of which r2 nexthops we have
+ * already used as a match against a r1 nexthop. We track this
+ * via NEXTHOP_FLAG_MATCHED. Clear this flag for all r2 nexthops when you
+ * are finished.
+ *
+ * TRUE: r1 [a,b], r2 [a,b]
+ * TRUE: r1 [a,b], r2 [b,a]
+ * FALSE: r1 [a,b], r2 [a,c]
+ * FALSE: r1 [a,a], r2 [a,b]
+ */
+ for (nh1 = r1->nexthop; nh1; nh1 = nh1->next)
+ {
+ found_nh = 0;
+ for (nh2 = r2->nexthop; nh2; nh2 = nh2->next)
+ {
+ if (CHECK_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED))
+ continue;
+
+ if (nexthop_same_no_recurse(nh1, nh2))
+ {
+ SET_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED);
+ found_nh = 1;
+ break;
+ }
+ }
+
+ if (!found_nh)
+ {
+ for (nh2 = r2->nexthop; nh2; nh2 = nh2->next)
+ if (CHECK_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED))
+ UNSET_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED);
+ return 1;
+ }
+ }
+
+ for (nh2 = r2->nexthop; nh2; nh2 = nh2->next)
+ if (CHECK_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED))
+ UNSET_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED);
+
+ return 0;
+}
+
+static int
+send_client (struct rnh *rnh, struct zserv *client, vrf_id_t vrf_id)
+{
+ struct stream *s;
+ struct rib *rib;
+ unsigned long nump;
+ u_char num;
+ struct nexthop *nexthop;
+ struct route_node *rn;
+
+ rn = rnh->node;
+ rib = rnh->state;
+
+ /* Get output stream. */
+ s = client->obuf;
+ stream_reset (s);
+
+ zserv_create_header (s, ZEBRA_NEXTHOP_UPDATE, vrf_id);
+
+ stream_putw(s, rn->p.family);
+ stream_put_prefix (s, &rn->p);
+
+ if (rib)
+ {
+ stream_putl (s, rib->metric);
+ num = 0;
+ nump = stream_get_endp(s);
+ stream_putc (s, 0);
+ for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
+ if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
+ {
+ stream_putc (s, nexthop->type);
+ switch (nexthop->type)
+ {
+ case ZEBRA_NEXTHOP_IPV4:
+ stream_put_in_addr (s, &nexthop->gate.ipv4);
+ break;
+ case ZEBRA_NEXTHOP_IFINDEX:
+ case ZEBRA_NEXTHOP_IFNAME:
+ stream_putl (s, nexthop->ifindex);
+ break;
+ case ZEBRA_NEXTHOP_IPV4_IFINDEX:
+ case ZEBRA_NEXTHOP_IPV4_IFNAME:
+ stream_put_in_addr (s, &nexthop->gate.ipv4);
+ stream_putl (s, nexthop->ifindex);
+ break;
+#ifdef HAVE_IPV6
+ case ZEBRA_NEXTHOP_IPV6:
+ stream_put (s, &nexthop->gate.ipv6, 16);
+ break;
+ case ZEBRA_NEXTHOP_IPV6_IFINDEX:
+ case ZEBRA_NEXTHOP_IPV6_IFNAME:
+ stream_put (s, &nexthop->gate.ipv6, 16);
+ stream_putl (s, nexthop->ifindex);
+ break;
+#endif /* HAVE_IPV6 */
+ default:
+ /* do nothing */
+ break;
+ }
+ num++;
+ }
+ stream_putc_at (s, nump, num);
+ }
+ else
+ {
+ stream_putl (s, 0);
+ stream_putc (s, 0);
+ }
+ stream_putw_at (s, 0, stream_get_endp (s));
+ return zebra_server_send_message(client);
+}
+
+static void
+print_nh (struct nexthop *nexthop, struct vty *vty)
+{
+ char buf[BUFSIZ];
+
+ switch (nexthop->type)
+ {
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ vty_out (vty, " via %s", inet_ntoa (nexthop->gate.ipv4));
+ if (nexthop->ifindex)
+ vty_out (vty, ", %s", ifindex2ifname (nexthop->ifindex));
+ break;
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ case NEXTHOP_TYPE_IPV6_IFNAME:
+ vty_out (vty, " %s",
+ inet_ntop (AF_INET6, &nexthop->gate.ipv6, buf, BUFSIZ));
+ if (nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME)
+ vty_out (vty, ", %s", nexthop->ifname);
+ else if (nexthop->ifindex)
+ vty_out (vty, ", via %s", ifindex2ifname (nexthop->ifindex));
+ break;
+ case NEXTHOP_TYPE_IFINDEX:
+ vty_out (vty, " is directly connected, %s",
+ ifindex2ifname (nexthop->ifindex));
+ break;
+ case NEXTHOP_TYPE_IFNAME:
+ vty_out (vty, " is directly connected, %s", nexthop->ifname);
+ break;
+ case NEXTHOP_TYPE_BLACKHOLE:
+ vty_out (vty, " is directly connected, Null0");
+ break;
+ default:
+ break;
+ }
+ vty_out(vty, "%s", VTY_NEWLINE);
+}
+
+static void
+print_rnh (struct route_node *rn, struct vty *vty)
+{
+ struct rnh *rnh;
+ struct nexthop *nexthop;
+ struct listnode *node;
+ struct zserv *client;
+ char buf[BUFSIZ];
+
+ rnh = rn->info;
+ vty_out(vty, "%s%s", inet_ntop(rn->p.family, &rn->p.u.prefix, buf, BUFSIZ),
+ VTY_NEWLINE);
+ if (rnh->state)
+ {
+ vty_out(vty, " resolved via %s%s",
+ zebra_route_string(rnh->state->type), VTY_NEWLINE);
+ for (nexthop = rnh->state->nexthop; nexthop; nexthop = nexthop->next)
+ print_nh(nexthop, vty);
+ }
+ else
+ vty_out(vty, " unresolved%s", VTY_NEWLINE);
+
+ vty_out(vty, " Client list:");
+ for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client))
+ vty_out(vty, " %s(fd %d)", zebra_route_string(client->proto),
+ client->sock);
+ vty_out(vty, "%s", VTY_NEWLINE);
+}
diff --git a/zebra/zebra_rnh.h b/zebra/zebra_rnh.h
new file mode 100644
index 0000000..97dbba7
--- /dev/null
+++ b/zebra/zebra_rnh.h
@@ -0,0 +1,48 @@
+/*
+ * Zebra next hop tracking header
+ * Copyright (C) 2013 Cumulus Networks, Inc.
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_RNH_H
+#define _ZEBRA_RNH_H
+
+#include "prefix.h"
+#include "vty.h"
+
+/* Nexthop structure. */
+struct rnh
+{
+ u_char flags;
+ struct rib *state;
+ struct list *client_list;
+ struct route_node *node;
+};
+
+extern struct rnh *zebra_add_rnh(struct prefix *p, vrf_id_t vrfid);
+extern struct rnh *zebra_lookup_rnh(struct prefix *p, vrf_id_t vrfid);
+extern void zebra_delete_rnh(struct rnh *rnh);
+extern void zebra_add_rnh_client(struct rnh *rnh, struct zserv *client, vrf_id_t vrf_id_t);
+extern void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client);
+extern int zebra_evaluate_rnh_table(vrf_id_t vrfid, int family);
+extern int zebra_dispatch_rnh_table(vrf_id_t vrfid, int family, struct zserv *cl);
+extern void zebra_print_rnh_table(vrf_id_t vrfid, int family, struct vty *vty);
+extern char *rnh_str(struct rnh *rnh, char *buf, int size);
+extern int zebra_cleanup_rnh_client(vrf_id_t vrf, int family, struct zserv *client);
+#endif /*_ZEBRA_RNH_H */
diff --git a/zebra/zebra_rnh_null.c b/zebra/zebra_rnh_null.c
new file mode 100644
index 0000000..664667d
--- /dev/null
+++ b/zebra/zebra_rnh_null.c
@@ -0,0 +1,10 @@
+#include <zebra.h>
+#include "zebra/rib.h"
+#include "zebra/zserv.h"
+#include "zebra/zebra_rnh.h"
+
+int zebra_evaluate_rnh_table (vrf_id_t vrfid, int family)
+{ return 0; }
+
+void zebra_print_rnh_table (vrf_id_t vrfid, int family, struct vty *vty)
+{}
diff --git a/zebra/zebra_routemap.c b/zebra/zebra_routemap.c
index 49feccd..bf7e335 100644
--- a/zebra/zebra_routemap.c
+++ b/zebra/zebra_routemap.c
@@ -29,6 +29,7 @@
#include "filter.h"
#include "plist.h"
#include "vrf.h"
+#include "nexthop.h"
#include "zebra/zserv.h"
diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c
index 23660d7..e3ef963 100644
--- a/zebra/zebra_vty.c
+++ b/zebra/zebra_vty.c
@@ -28,8 +28,10 @@
#include "table.h"
#include "rib.h"
#include "vrf.h"
+#include "nexthop.h"
#include "zebra/zserv.h"
+#include "zebra/zebra_rnh.h"
static int do_show_ip_route(struct vty *vty, safi_t safi, vrf_id_t vrf_id);
static void vty_show_ip_route_detail (struct vty *vty, struct route_node *rn,
@@ -1610,6 +1612,28 @@
"IP routing table\n"
VRF_CMD_HELP_STR)
+DEFUN (show_ip_nht,
+ show_ip_nht_cmd,
+ "show ip nht",
+ SHOW_STR
+ IP_STR
+ "IP nexthop tracking table\n")
+{
+ zebra_print_rnh_table(0, AF_INET, vty);
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ipv6_nht,
+ show_ipv6_nht_cmd,
+ "show ipv6 nht",
+ SHOW_STR
+ IP_STR
+ "IPv6 nexthop tracking table\n")
+{
+ zebra_print_rnh_table(0, AF_INET6, vty);
+ return CMD_SUCCESS;
+}
+
DEFUN (show_ip_route_prefix_longer,
show_ip_route_prefix_longer_cmd,
"show ip route A.B.C.D/M longer-prefixes",
@@ -3880,6 +3904,8 @@
install_element (CONFIG_NODE, &no_ip_route_mask_flags_distance2_cmd);
install_element (VIEW_NODE, &show_ip_route_cmd);
+ install_element (VIEW_NODE, &show_ip_nht_cmd);
+ install_element (VIEW_NODE, &show_ipv6_nht_cmd);
install_element (VIEW_NODE, &show_ip_route_addr_cmd);
install_element (VIEW_NODE, &show_ip_route_prefix_cmd);
install_element (VIEW_NODE, &show_ip_route_prefix_longer_cmd);
diff --git a/zebra/zserv.c b/zebra/zserv.c
index 86f141b..6e65aa1 100644
--- a/zebra/zserv.c
+++ b/zebra/zserv.c
@@ -37,12 +37,14 @@
#include "network.h"
#include "buffer.h"
#include "vrf.h"
+#include "nexthop.h"
#include "zebra/zserv.h"
#include "zebra/router-id.h"
#include "zebra/redistribute.h"
#include "zebra/debug.h"
#include "zebra/ipforward.h"
+#include "zebra/zebra_rnh.h"
/* Event list of zebra. */
enum event { ZEBRA_SERV, ZEBRA_READ, ZEBRA_WRITE };
@@ -102,7 +104,7 @@
return 0;
}
-static int
+int
zebra_server_send_message(struct zserv *client)
{
if (client->t_suicide)
@@ -131,7 +133,7 @@
return 0;
}
-static void
+void
zserv_create_header (struct stream *s, uint16_t cmd, vrf_id_t vrf_id)
{
/* length placeholder, caller can update */
@@ -717,6 +719,65 @@
return zebra_server_send_message(client);
}
+/* Nexthop register */
+static int
+zserv_nexthop_register (struct zserv *client, int sock, u_short length, vrf_id_t vrf_id)
+{
+ struct rnh *rnh;
+ struct stream *s;
+ struct prefix p;
+ u_short l = 0;
+
+ if (IS_ZEBRA_DEBUG_NHT)
+ zlog_debug("nexthop_register msg from client %s: length=%d\n",
+ zebra_route_string(client->proto), length);
+
+ s = client->ibuf;
+
+ while (l < length)
+ {
+ p.family = stream_getw(s);
+ p.prefixlen = stream_getc(s);
+ l += 3;
+ stream_get(&p.u.prefix, s, PSIZE(p.prefixlen));
+ l += PSIZE(p.prefixlen);
+ rnh = zebra_add_rnh(&p, 0);
+ zebra_add_rnh_client(rnh, client, vrf_id);
+ }
+ zebra_evaluate_rnh_table(0, AF_INET);
+ zebra_evaluate_rnh_table(0, AF_INET6);
+ return 0;
+}
+
+/* Nexthop register */
+static int
+zserv_nexthop_unregister (struct zserv *client, int sock, u_short length)
+{
+ struct rnh *rnh;
+ struct stream *s;
+ struct prefix p;
+ u_short l = 0;
+
+ if (IS_ZEBRA_DEBUG_NHT)
+ zlog_debug("nexthop_unregister msg from client %s: length=%d\n",
+ zebra_route_string(client->proto), length);
+
+ s = client->ibuf;
+
+ while (l < length)
+ {
+ p.family = stream_getw(s);
+ p.prefixlen = stream_getc(s);
+ l += 3;
+ stream_get(&p.u.prefix, s, PSIZE(p.prefixlen));
+ l += PSIZE(p.prefixlen);
+ rnh = zebra_lookup_rnh(&p, 0);
+ if (rnh)
+ zebra_remove_rnh_client(rnh, client);
+ }
+ return 0;
+}
+
static int
zsend_ipv4_import_lookup (struct zserv *client, struct prefix_ipv4 *p,
vrf_id_t vrf_id)
@@ -907,7 +968,7 @@
{
case ZEBRA_NEXTHOP_IFINDEX:
ifindex = stream_getl (s);
- nexthop_ifindex_add (rib, ifindex);
+ rib_nexthop_ifindex_add (rib, ifindex);
break;
case ZEBRA_NEXTHOP_IFNAME:
ifname_len = stream_getc (s);
@@ -915,18 +976,18 @@
break;
case ZEBRA_NEXTHOP_IPV4:
nexthop.s_addr = stream_get_ipv4 (s);
- nexthop_ipv4_add (rib, &nexthop, NULL);
+ rib_nexthop_ipv4_add (rib, &nexthop, NULL);
break;
case ZEBRA_NEXTHOP_IPV4_IFINDEX:
nexthop.s_addr = stream_get_ipv4 (s);
ifindex = stream_getl (s);
- nexthop_ipv4_ifindex_add (rib, &nexthop, NULL, ifindex);
+ rib_nexthop_ipv4_ifindex_add (rib, &nexthop, NULL, ifindex);
break;
case ZEBRA_NEXTHOP_IPV6:
stream_forward_getp (s, IPV6_MAX_BYTELEN);
break;
case ZEBRA_NEXTHOP_BLACKHOLE:
- nexthop_blackhole_add (rib);
+ rib_nexthop_blackhole_add (rib);
break;
}
}
@@ -1150,14 +1211,14 @@
if ((i < nh_count) && !IN6_IS_ADDR_UNSPECIFIED (&nexthops[i]))
{
if ((i < if_count) && ifindices[i])
- nexthop_ipv6_ifindex_add (rib, &nexthops[i], ifindices[i]);
+ rib_nexthop_ipv6_ifindex_add (rib, &nexthops[i], ifindices[i]);
else
- nexthop_ipv6_add (rib, &nexthops[i]);
+ rib_nexthop_ipv6_add (rib, &nexthops[i]);
}
else
{
if ((i < if_count) && ifindices[i])
- nexthop_ifindex_add (rib, ifindices[i]);
+ rib_nexthop_ifindex_add (rib, ifindices[i]);
}
}
}
@@ -1306,6 +1367,7 @@
client->sock);
route_type_oaths[proto] = client->sock;
+ client->proto = proto;
}
}
@@ -1346,6 +1408,9 @@
static void
zebra_client_close (struct zserv *client)
{
+ zebra_cleanup_rnh_client(0, AF_INET, client);
+ zebra_cleanup_rnh_client(0, AF_INET6, client);
+
/* Close file descriptor. */
if (client->sock)
{
@@ -1573,6 +1638,11 @@
break;
case ZEBRA_VRF_UNREGISTER:
zread_vrf_unregister (client, length, vrf_id);
+ case ZEBRA_NEXTHOP_REGISTER:
+ zserv_nexthop_register(client, sock, length, vrf_id);
+ break;
+ case ZEBRA_NEXTHOP_UNREGISTER:
+ zserv_nexthop_unregister(client, sock, length);
break;
default:
zlog_info ("Zebra received unknown command %d", command);
@@ -1848,8 +1918,10 @@
struct zserv *client;
for (ALL_LIST_ELEMENTS_RO (zebrad.client_list, node, client))
- vty_out (vty, "Client fd %d%s", client->sock, VTY_NEWLINE);
-
+ vty_out (vty, "Client %s fd %d%s",
+ zebra_route_string(client->proto), client->sock,
+ VTY_NEWLINE);
+
return CMD_SUCCESS;
}
diff --git a/zebra/zserv.h b/zebra/zserv.h
index 9d65998..7a2b4a1 100644
--- a/zebra/zserv.h
+++ b/zebra/zserv.h
@@ -67,6 +67,9 @@
/* Router-id information. */
vrf_bitmap_t ridinfo;
+
+ /* client's protocol */
+ u_char proto;
};
/* Zebra instance */
@@ -112,4 +115,7 @@
extern pid_t pid;
+extern void zserv_create_header(struct stream *s, uint16_t cmd, vrf_id_t);
+extern int zebra_server_send_message(struct zserv *client);
+
#endif /* _ZEBRA_ZEBRA_H */