summaryrefslogtreecommitdiffstats
path: root/zebra
diff options
context:
space:
mode:
Diffstat (limited to 'zebra')
-rw-r--r--zebra/Makefile.am6
-rw-r--r--zebra/debug.c30
-rw-r--r--zebra/debug.h3
-rw-r--r--zebra/rib.h62
-rw-r--r--zebra/rt_netlink.c1
-rw-r--r--zebra/zebra_fpm_netlink.c1
-rw-r--r--zebra/zebra_rib.c50
-rw-r--r--zebra/zebra_rnh.c603
-rw-r--r--zebra/zebra_rnh.h48
-rw-r--r--zebra/zebra_rnh_null.c10
-rw-r--r--zebra/zebra_routemap.c1
-rw-r--r--zebra/zebra_vty.c28
-rw-r--r--zebra/zserv.c81
-rw-r--r--zebra/zserv.h6
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 */