diff options
Diffstat (limited to 'zebra')
-rw-r--r-- | zebra/Makefile.am | 6 | ||||
-rw-r--r-- | zebra/debug.c | 30 | ||||
-rw-r--r-- | zebra/debug.h | 3 | ||||
-rw-r--r-- | zebra/rib.h | 62 | ||||
-rw-r--r-- | zebra/rt_netlink.c | 1 | ||||
-rw-r--r-- | zebra/zebra_fpm_netlink.c | 1 | ||||
-rw-r--r-- | zebra/zebra_rib.c | 50 | ||||
-rw-r--r-- | zebra/zebra_rnh.c | 603 | ||||
-rw-r--r-- | zebra/zebra_rnh.h | 48 | ||||
-rw-r--r-- | zebra/zebra_rnh_null.c | 10 | ||||
-rw-r--r-- | zebra/zebra_routemap.c | 1 | ||||
-rw-r--r-- | zebra/zebra_vty.c | 28 | ||||
-rw-r--r-- | zebra/zserv.c | 81 | ||||
-rw-r--r-- | zebra/zserv.h | 6 |
14 files changed, 839 insertions, 91 deletions
diff --git a/zebra/Makefile.am b/zebra/Makefile.am index 96f7bef30..9aa135236 100644 --- a/zebra/Makefile.am +++ b/zebra/Makefile.am @@ -34,16 +34,16 @@ zebra_SOURCES = \ 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 \ - $(othersrc) + $(othersrc) zebra_rnh.c 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 + rt_netlink.h zebra_fpm.h zebra_fpm_private.h zebra_rnh.h zebra_LDADD = $(otherobj) ../lib/libzebra.la $(LIBCAP) $(LIB_IPV6) diff --git a/zebra/debug.c b/zebra/debug.c index c3b00e0fa..10aba2d55 100644 --- a/zebra/debug.c +++ b/zebra/debug.c @@ -30,6 +30,7 @@ unsigned long zebra_debug_packet; 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 @@ DEFUN (show_debugging_zebra, 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 @@ DEFUN (debug_zebra_events, 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", @@ -196,6 +210,18 @@ DEFUN (no_debug_zebra_events, 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", @@ -352,6 +378,7 @@ zebra_debug_init (void) 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); @@ -360,6 +387,7 @@ zebra_debug_init (void) 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); @@ -367,6 +395,7 @@ zebra_debug_init (void) 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); @@ -375,6 +404,7 @@ zebra_debug_init (void) 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 d9231a22b..0fb4dd9fe 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/rib.h b/zebra/rib.h index ad726c6cc..833b892cb 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -26,18 +26,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. */ @@ -224,50 +216,6 @@ struct static_ipv6 }; #endif /* HAVE_IPV6 */ -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; - unsigned int 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. * @@ -334,6 +282,9 @@ struct vrf /* Static route configuration. */ struct route_table *stable[AFI_MAX][SAFI_MAX]; + + /* Recursive Nexthop table */ + struct route_table *rnh_table[AFI_MAX]; }; /* @@ -373,7 +324,6 @@ typedef struct rib_tables_iter_t_ rib_tables_iter_state_t state; } rib_tables_iter_t; -extern const char *nexthop_type_to_str (enum nexthop_types_t nh_type); extern struct nexthop *nexthop_ifindex_add (struct rib *, unsigned int); extern struct nexthop *nexthop_ifname_add (struct rib *, char *); extern struct nexthop *nexthop_blackhole_add (struct rib *); @@ -383,6 +333,10 @@ extern struct nexthop *nexthop_ipv4_ifindex_add (struct rib *, struct in_addr *, struct in_addr *, unsigned int); +extern void nexthop_free (struct nexthop *nexthop); +extern void nexthops_free (struct nexthop *nexthop); +extern void 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 *); diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index c242480f0..11f6f0ab2 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -36,6 +36,7 @@ #include "rib.h" #include "thread.h" #include "privs.h" +#include "nexthop.h" #include "zebra/zserv.h" #include "zebra/rt.h" diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c index b5f2b7607..1de75be4b 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 02116b3d2..5cd81e3de 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -34,6 +34,7 @@ #include "workqueue.h" #include "prefix.h" #include "routemap.h" +#include "nexthop.h" #include "zebra/rib.h" #include "zebra/rt.h" @@ -41,6 +42,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; @@ -118,6 +120,8 @@ vrf_alloc (const char *name) vrf->stable[AFI_IP][SAFI_MULTICAST] = route_table_init (); vrf->stable[AFI_IP6][SAFI_MULTICAST] = route_table_init (); + vrf->rnh_table[AFI_IP] = route_table_init(); + vrf->rnh_table[AFI_IP6] = route_table_init(); return vrf; } @@ -177,31 +181,6 @@ vrf_static_table (afi_t afi, safi_t safi, u_int32_t id) return vrf->stable[afi][safi]; } -/* - * 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) @@ -218,7 +197,7 @@ _nexthop_add (struct nexthop **target, struct nexthop *nexthop) } /* Add nexthop to the end of a rib node's nexthop list */ -static void +void nexthop_add (struct rib *rib, struct nexthop *nexthop) { _nexthop_add(&rib->nexthop, nexthop); @@ -238,10 +217,8 @@ nexthop_delete (struct rib *rib, struct nexthop *nexthop) rib->nexthop_num--; } -static void nexthops_free(struct nexthop *nexthop); - /* Free nexthop. */ -static void +void nexthop_free (struct nexthop *nexthop) { if (nexthop->ifname) @@ -252,7 +229,7 @@ nexthop_free (struct nexthop *nexthop) } /* Frees a list of nexthops */ -static void +void nexthops_free (struct nexthop *nexthop) { struct nexthop *nh, *next; @@ -1475,6 +1452,18 @@ process_subq (struct list * subq, u_char qindex) 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. @@ -1635,6 +1624,7 @@ rib_queue_init (struct zebra_t *zebra) /* 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; diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c new file mode 100644 index 000000000..117a2659c --- /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 vrf *vrf; \ + struct route_table *t = NULL; \ + vrf = vrf_lookup(v); \ + if (vrf) \ + t = vrf->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); +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, u_int32_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, u_int32_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) +{ + 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); + } +} + +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 (int 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 = 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); + } + } + return 1; +} + +int +zebra_dispatch_rnh_table (int 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); + } + return 1; +} + +void +zebra_print_rnh_table (int 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 (int 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 +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)); + + 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) + 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) +{ + 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); + + 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 000000000..212bab602 --- /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, u_int32_t vrfid); +extern struct rnh *zebra_lookup_rnh(struct prefix *p, u_int32_t vrfid); +extern void zebra_delete_rnh(struct rnh *rnh); +extern void zebra_add_rnh_client(struct rnh *rnh, struct zserv *client); +extern void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client); +extern int zebra_evaluate_rnh_table(int vrfid, int family); +extern int zebra_dispatch_rnh_table(int vrfid, int family, struct zserv *cl); +extern void zebra_print_rnh_table(int vrfid, int family, struct vty *vty); +extern char *rnh_str(struct rnh *rnh, char *buf, int size); +extern int zebra_cleanup_rnh_client(int 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 000000000..68b58ce3e --- /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 (int vrfid, int family) +{ return 0; } + +void zebra_print_rnh_table (int vrfid, int family, struct vty *vty) +{} diff --git a/zebra/zebra_routemap.c b/zebra/zebra_routemap.c index b0dca088b..7b4472488 100644 --- a/zebra/zebra_routemap.c +++ b/zebra/zebra_routemap.c @@ -28,6 +28,7 @@ #include "command.h" #include "filter.h" #include "plist.h" +#include "nexthop.h" #include "zebra/zserv.h" diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 254354c72..72095706e 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -27,8 +27,10 @@ #include "command.h" #include "table.h" #include "rib.h" +#include "nexthop.h" #include "zebra/zserv.h" +#include "zebra/zebra_rnh.h" /* General fucntion for static route. */ static int @@ -811,6 +813,28 @@ DEFUN (show_ip_route, return CMD_SUCCESS; } +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", @@ -2210,6 +2234,8 @@ zebra_vty_init (void) 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); @@ -2218,6 +2244,8 @@ zebra_vty_init (void) install_element (VIEW_NODE, &show_ip_route_summary_cmd); install_element (VIEW_NODE, &show_ip_route_summary_prefix_cmd); install_element (ENABLE_NODE, &show_ip_route_cmd); + install_element (ENABLE_NODE, &show_ip_nht_cmd); + install_element (ENABLE_NODE, &show_ipv6_nht_cmd); install_element (ENABLE_NODE, &show_ip_route_addr_cmd); install_element (ENABLE_NODE, &show_ip_route_prefix_cmd); install_element (ENABLE_NODE, &show_ip_route_prefix_longer_cmd); diff --git a/zebra/zserv.c b/zebra/zserv.c index b245b7f7b..ecb3bd05d 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -36,12 +36,14 @@ #include "privs.h" #include "network.h" #include "buffer.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 }; @@ -101,7 +103,7 @@ zserv_flush_data(struct thread *thread) return 0; } -static int +int zebra_server_send_message(struct zserv *client) { if (client->t_suicide) @@ -130,7 +132,7 @@ zebra_server_send_message(struct zserv *client) return 0; } -static void +void zserv_create_header (struct stream *s, uint16_t cmd) { /* length placeholder, caller can update */ @@ -599,6 +601,65 @@ zsend_ipv4_nexthop_lookup (struct zserv *client, struct in_addr addr) return zebra_server_send_message(client); } +/* Nexthop register */ +static int +zserv_nexthop_register (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_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); + } + 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) { @@ -1171,6 +1232,7 @@ zread_hello (struct zserv *client) client->sock); route_type_oaths[proto] = client->sock; + client->proto = proto; } } @@ -1196,6 +1258,9 @@ zebra_score_rib (int client_sock) 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) { @@ -1408,6 +1473,12 @@ zebra_client_read (struct thread *thread) case ZEBRA_HELLO: zread_hello (client); break; + case ZEBRA_NEXTHOP_REGISTER: + zserv_nexthop_register(client, sock, length); + break; + case ZEBRA_NEXTHOP_UNREGISTER: + zserv_nexthop_unregister(client, sock, length); + break; default: zlog_info ("Zebra received unknown command %d", command); break; @@ -1681,8 +1752,10 @@ DEFUN (show_zebra_client, 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 5e8bccac3..92b8ba3a6 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -66,6 +66,9 @@ struct zserv /* Router-id information. */ u_char ridinfo; + + /* client's protocol */ + u_char proto; }; /* Zebra instance */ @@ -110,4 +113,7 @@ extern int zsend_router_id_update(struct zserv *, struct prefix *); extern pid_t pid; +extern void zserv_create_header(struct stream *s, uint16_t cmd); +extern int zebra_server_send_message(struct zserv *client); + #endif /* _ZEBRA_ZEBRA_H */ |