summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/user/pim.rst6
-rw-r--r--isisd/isis_route.c8
-rw-r--r--isisd/isis_vty_fabricd.c2
-rw-r--r--isisd/isisd.c8
-rw-r--r--lib/linklist.c4
-rw-r--r--lib/linklist.h4
-rw-r--r--pimd/pim_cmd.c93
-rw-r--r--snapcraft/README.snap_build.md8
-rw-r--r--snapcraft/README.usage.md2
-rw-r--r--snapcraft/defaults/fabricd.conf.default0
-rw-r--r--snapcraft/scripts/Makefile1
-rw-r--r--snapcraft/scripts/bgpd-service2
-rw-r--r--snapcraft/scripts/fabricd-service13
-rw-r--r--snapcraft/snapcraft.yaml.in51
-rw-r--r--zebra/rib.h15
-rw-r--r--zebra/subdir.am2
-rw-r--r--zebra/zebra_dplane.c325
-rw-r--r--zebra/zebra_dplane.h65
-rw-r--r--zebra/zebra_mpls.c208
-rw-r--r--zebra/zebra_mpls.h3
-rw-r--r--zebra/zebra_nhg.c511
-rw-r--r--zebra/zebra_nhg.h29
-rw-r--r--zebra/zebra_rib.c1037
-rw-r--r--zebra/zebra_vty.c26
24 files changed, 1852 insertions, 571 deletions
diff --git a/doc/user/pim.rst b/doc/user/pim.rst
index 414423ae7c..d05127059b 100644
--- a/doc/user/pim.rst
+++ b/doc/user/pim.rst
@@ -322,6 +322,12 @@ cause great confusion.
Display information about installed into the kernel S,G mroutes and in
addition display data about packet flow for the mroutes.
+.. index:: show ip mroute summary
+.. clicmd:: show ip mroute summary
+
+ Display total number of S,G mroutes and number of S,G mroutes installed
+ into the kernel.
+
.. index:: show ip pim assert
.. clicmd:: show ip pim assert
diff --git a/isisd/isis_route.c b/isisd/isis_route.c
index 82005c911e..281eaf11bc 100644
--- a/isisd/isis_route.c
+++ b/isisd/isis_route.c
@@ -543,7 +543,8 @@ void isis_route_verify_merge(struct isis_area *area,
ISIS_ROUTE_FLAG_ZEBRA_SYNCED
);
continue;
- } else {
+ } else if (CHECK_FLAG(rinfo->flag,
+ ISIS_ROUTE_FLAG_ACTIVE)) {
/* Clear the ZEBRA_SYNCED flag on the L1
* route when L2 wins, otherwise L1
* won't get reinstalled when it
@@ -553,6 +554,11 @@ void isis_route_verify_merge(struct isis_area *area,
mrinfo->flag,
ISIS_ROUTE_FLAG_ZEBRA_SYNCED
);
+ } else if (
+ CHECK_FLAG(
+ mrinfo->flag,
+ ISIS_ROUTE_FLAG_ZEBRA_SYNCED)) {
+ continue;
}
}
mrnode->info = rnode->info;
diff --git a/isisd/isis_vty_fabricd.c b/isisd/isis_vty_fabricd.c
index 2476bd2552..431ad9712a 100644
--- a/isisd/isis_vty_fabricd.c
+++ b/isisd/isis_vty_fabricd.c
@@ -168,7 +168,7 @@ DEFUN (show_lsp_flooding,
area->area_tag : "null");
if (lspid) {
- struct isis_lsp *lsp = lsp_for_arg(head, lspid);
+ lsp = lsp_for_arg(head, lspid);
if (lsp)
lsp_print_flooding(vty, lsp);
diff --git a/isisd/isisd.c b/isisd/isisd.c
index 07be68d9ae..bee3b6deb5 100644
--- a/isisd/isisd.c
+++ b/isisd/isisd.c
@@ -272,7 +272,7 @@ int isis_area_destroy(const char *area_tag)
lsp_db_fini(&area->lspdb[1]);
/* invalidate and verify to delete all routes from zebra */
- isis_area_invalidate_routes(area, ISIS_LEVEL1 & ISIS_LEVEL2);
+ isis_area_invalidate_routes(area, area->is_type);
isis_area_verify_routes(area);
spftree_area_del(area);
@@ -738,11 +738,7 @@ DEFUN (clear_isis_neighbor_arg,
*/
void print_debug(struct vty *vty, int flags, int onoff)
{
- char onoffs[4];
- if (onoff)
- strcpy(onoffs, "on");
- else
- strcpy(onoffs, "off");
+ const char *onoffs = onoff ? "on" : "off";
if (flags & DEBUG_ADJ_PACKETS)
vty_out(vty,
diff --git a/lib/linklist.c b/lib/linklist.c
index e8ba9edc12..0d1efdf3aa 100644
--- a/lib/linklist.c
+++ b/lib/linklist.c
@@ -246,7 +246,7 @@ void listnode_move_to_tail(struct list *l, struct listnode *n)
LISTNODE_ATTACH(l, n);
}
-void listnode_delete(struct list *list, void *val)
+void listnode_delete(struct list *list, const void *val)
{
struct listnode *node = listnode_lookup(list, val);
@@ -307,7 +307,7 @@ void list_delete(struct list **list)
*list = NULL;
}
-struct listnode *listnode_lookup(struct list *list, void *data)
+struct listnode *listnode_lookup(struct list *list, const void *data)
{
struct listnode *node;
diff --git a/lib/linklist.h b/lib/linklist.h
index da42aa2688..d23d425d62 100644
--- a/lib/linklist.h
+++ b/lib/linklist.h
@@ -180,7 +180,7 @@ extern void listnode_move_to_tail(struct list *list, struct listnode *node);
* data
* data to insert into list
*/
-extern void listnode_delete(struct list *list, void *data);
+extern void listnode_delete(struct list *list, const void *data);
/*
* Find the listnode corresponding to an element in a list.
@@ -194,7 +194,7 @@ extern void listnode_delete(struct list *list, void *data);
* Returns:
* pointer to listnode storing the given data if found, NULL otherwise
*/
-extern struct listnode *listnode_lookup(struct list *list, void *data);
+extern struct listnode *listnode_lookup(struct list *list, const void *data);
/*
* Retrieve the element at the head of a list.
diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c
index a2357067f9..fe910591a6 100644
--- a/pimd/pim_cmd.c
+++ b/pimd/pim_cmd.c
@@ -5705,6 +5705,97 @@ DEFUN (show_ip_mroute_count_vrf_all,
return CMD_SUCCESS;
}
+static void show_mroute_summary(struct pim_instance *pim, struct vty *vty)
+{
+ struct listnode *node;
+ struct channel_oil *c_oil;
+ struct static_route *s_route;
+ uint32_t starg_sw_mroute_cnt = 0;
+ uint32_t sg_sw_mroute_cnt = 0;
+ uint32_t starg_hw_mroute_cnt = 0;
+ uint32_t sg_hw_mroute_cnt = 0;
+
+ vty_out(vty, "Mroute Type Installed/Total\n");
+
+ for (ALL_LIST_ELEMENTS_RO(pim->channel_oil_list, node, c_oil)) {
+ if (!c_oil->installed) {
+ if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY)
+ starg_sw_mroute_cnt++;
+ else
+ sg_sw_mroute_cnt++;
+ } else {
+ if (c_oil->oil.mfcc_origin.s_addr == INADDR_ANY)
+ starg_hw_mroute_cnt++;
+ else
+ sg_hw_mroute_cnt++;
+ }
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(pim->static_routes, node, s_route)) {
+ if (!s_route->c_oil.installed) {
+ if (s_route->c_oil.oil.mfcc_origin.s_addr == INADDR_ANY)
+ starg_sw_mroute_cnt++;
+ else
+ sg_sw_mroute_cnt++;
+ } else {
+ if (s_route->c_oil.oil.mfcc_origin.s_addr == INADDR_ANY)
+ starg_hw_mroute_cnt++;
+ else
+ sg_hw_mroute_cnt++;
+ }
+ }
+
+ vty_out(vty, "%-20s %d/%d\n", "(*, G)", starg_hw_mroute_cnt,
+ starg_sw_mroute_cnt + starg_hw_mroute_cnt);
+ vty_out(vty, "%-20s %d/%d\n", "(S, G)", sg_hw_mroute_cnt,
+ sg_sw_mroute_cnt + sg_hw_mroute_cnt);
+ vty_out(vty, "------\n");
+ vty_out(vty, "%-20s %d/%d\n", "Total",
+ (starg_hw_mroute_cnt + sg_hw_mroute_cnt),
+ (starg_sw_mroute_cnt +
+ starg_hw_mroute_cnt +
+ sg_sw_mroute_cnt +
+ sg_hw_mroute_cnt));
+}
+
+DEFUN (show_ip_mroute_summary,
+ show_ip_mroute_summary_cmd,
+ "show ip mroute [vrf NAME] summary",
+ SHOW_STR
+ IP_STR
+ MROUTE_STR
+ VRF_CMD_HELP_STR
+ "Summary of all mroutes\n")
+{
+ int idx = 2;
+ struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
+
+ if (!vrf)
+ return CMD_WARNING;
+
+ show_mroute_summary(vrf->info, vty);
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_mroute_summary_vrf_all,
+ show_ip_mroute_summary_vrf_all_cmd,
+ "show ip mroute vrf all summary",
+ SHOW_STR
+ IP_STR
+ MROUTE_STR
+ VRF_CMD_HELP_STR
+ "Summary of all mroutes\n")
+{
+ struct vrf *vrf;
+
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+ vty_out(vty, "VRF: %s\n", vrf->name);
+ show_mroute_summary(vrf->info, vty);
+ }
+
+ return CMD_SUCCESS;
+}
+
DEFUN (show_ip_rib,
show_ip_rib_cmd,
"show ip rib [vrf NAME] A.B.C.D",
@@ -10085,6 +10176,8 @@ void pim_cmd_init(void)
install_element(VIEW_NODE, &show_ip_mroute_vrf_all_cmd);
install_element(VIEW_NODE, &show_ip_mroute_count_cmd);
install_element(VIEW_NODE, &show_ip_mroute_count_vrf_all_cmd);
+ install_element(VIEW_NODE, &show_ip_mroute_summary_cmd);
+ install_element(VIEW_NODE, &show_ip_mroute_summary_vrf_all_cmd);
install_element(VIEW_NODE, &show_ip_rib_cmd);
install_element(VIEW_NODE, &show_ip_ssmpingd_cmd);
install_element(VIEW_NODE, &show_debugging_pim_cmd);
diff --git a/snapcraft/README.snap_build.md b/snapcraft/README.snap_build.md
index 7c42848451..e43f63f2d9 100644
--- a/snapcraft/README.snap_build.md
+++ b/snapcraft/README.snap_build.md
@@ -92,6 +92,14 @@ All the commands are prefixed with frr.
frr.ripngd-debug
frr.ldp-debug
frr.zebra-debug
+ frr.pimd-debug
+ frr.nhrpd-debug
+ frr.babeld-debug
+ frr.eigrpd-debug
+ frr.pbrd-debug
+ frr.staticd-debug
+ frr.bfdd-debug
+ frr.fabricd-debug
vtysh can be accessed as frr.vtysh (Make sure you have /snap/bin in your
path). If access as `vtysh` instead of `frr.vtysh` is needed, you can enable it
diff --git a/snapcraft/README.usage.md b/snapcraft/README.usage.md
index a7b51a5656..6a0864c8c5 100644
--- a/snapcraft/README.usage.md
+++ b/snapcraft/README.usage.md
@@ -66,6 +66,8 @@ depend on them). These are mainly intended to debug the Snap
Starts staticd daemon in foreground
- `frr.bfdd-debug`:
Starts bfdd daemon in foreground
+- `frr.fabricd-debug`:
+ Starts fabricd daemon in foreground
MPLS (LDP)
----------
diff --git a/snapcraft/defaults/fabricd.conf.default b/snapcraft/defaults/fabricd.conf.default
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/snapcraft/defaults/fabricd.conf.default
diff --git a/snapcraft/scripts/Makefile b/snapcraft/scripts/Makefile
index e3a7708f23..7ddb7f0769 100644
--- a/snapcraft/scripts/Makefile
+++ b/snapcraft/scripts/Makefile
@@ -17,6 +17,7 @@ install:
install -D -m 0755 pbrd-service $(DESTDIR)/bin/
install -D -m 0755 staticd-service $(DESTDIR)/bin/
install -D -m 0755 bfdd-service $(DESTDIR)/bin/
+ install -D -m 0755 fabricd-service $(DESTDIR)/bin/
install -D -m 0755 set-options $(DESTDIR)/bin/
install -D -m 0755 show_version $(DESTDIR)/bin/
diff --git a/snapcraft/scripts/bgpd-service b/snapcraft/scripts/bgpd-service
index 6c3a6f5959..64273d9f80 100644
--- a/snapcraft/scripts/bgpd-service
+++ b/snapcraft/scripts/bgpd-service
@@ -10,7 +10,7 @@ fi
if ! [ -e $SNAP_DATA/rpki.conf ]; then
echo "-M rpki" > $SNAP_DATA/rpki.conf
fi
-EXTRA_OPTIONS="`cat $SNAP_DATA/rpki.conf`"
+EXTRA_OPTIONS="`$SNAP/bin/cat $SNAP_DATA/rpki.conf`"
exec $SNAP/sbin/bgpd \
-f $SNAP_DATA/bgpd.conf \
--pid_file $SNAP_DATA/bgpd.pid \
diff --git a/snapcraft/scripts/fabricd-service b/snapcraft/scripts/fabricd-service
new file mode 100644
index 0000000000..586f061ef0
--- /dev/null
+++ b/snapcraft/scripts/fabricd-service
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+set -e -x
+
+if ! [ -e $SNAP_DATA/fabricd.conf ]; then
+ cp $SNAP/etc/frr/fabricd.conf.default $SNAP_DATA/fabricd.conf
+fi
+exec $SNAP/sbin/fabricd \
+ -f $SNAP_DATA/fabricd.conf \
+ --pid_file $SNAP_DATA/fabricd.pid \
+ --socket $SNAP_DATA/zsock \
+ --vty_socket $SNAP_DATA
+
diff --git a/snapcraft/snapcraft.yaml.in b/snapcraft/snapcraft.yaml.in
index b70d6efee2..d8071e8cfe 100644
--- a/snapcraft/snapcraft.yaml.in
+++ b/snapcraft/snapcraft.yaml.in
@@ -4,8 +4,8 @@ summary: FRRouting BGP/OSPFv2/OSPFv3/ISIS/RIP/RIPng/PIM/LDP/EIGRP/BFD routing da
description: BGP/OSPFv2/OSPFv3/ISIS/RIP/RIPng/PIM/LDP/EIGRP/BFD routing daemon
FRRouting (FRR) is free software which manages TCP/IP based routing
protocols. It supports BGP4, BGP4+, OSPFv2, OSPFv3, IS-IS, RIPv1, RIPv2,
- RIPng, PIM, LDP, Babel, EIGRP, PBR (Policy-based routing) and BFD as well as
- the IPv6 versions of these.
+ RIPng, PIM, LDP, Babel, EIGRP, PBR (Policy-based routing), BFD and OpenFabric
+ as well as the IPv6 versions of these.
FRRouting (frr) is a fork of Quagga.
confinement: strict
grade: devel
@@ -127,6 +127,13 @@ apps:
- network
- network-bind
- network-control
+ fabricd:
+ command: bin/fabricd-service
+ daemon: simple
+ plugs:
+ - network
+ - network-bind
+ - network-control
set:
command: bin/set-options
zebra-debug:
@@ -136,7 +143,7 @@ apps:
- network-bind
- network-control
bgpd-debug:
- command: sbin/bgpd -f $SNAP_DATA/bgpd.conf --pid_file $SNAP_DATA/bgpd.pid --socket $SNAP_DATA/zsock --vty_socket $SNAP_DATA --moduledir $SNAP/lib/frr/modules `cat $SNAP_DATA/rpki.conf 2> /dev/null`
+ command: sbin/bgpd -f $SNAP_DATA/bgpd.conf --pid_file $SNAP_DATA/bgpd.pid --socket $SNAP_DATA/zsock --vty_socket $SNAP_DATA --moduledir $SNAP/lib/frr/modules
plugs:
- network
- network-bind
@@ -219,6 +226,12 @@ apps:
- network
- network-bind
- network-control
+ fabricd-debug:
+ command: sbin/fabricd -f $SNAP_DATA/fabricd.conf --pid_file $SNAP_DATA/fabricd.pid --socket $SNAP_DATA/zsock --vty_socket $SNAP_DATA
+ plugs:
+ - network
+ - network-bind
+ - network-control
parts:
rtrlib:
@@ -230,16 +243,33 @@ parts:
stage-packages:
- libssh-4
prime:
- - lib/x86_64-linux-gnu/librtr.so*
+ - lib/librtr.so*
- usr/lib/x86_64-linux-gnu/libssh.so*
source: https://github.com/rtrlib/rtrlib.git
source-type: git
- source-tag: v0.5.0
+ source-tag: v0.6.3
plugin: cmake
configflags:
- -DCMAKE_BUILD_TYPE=Release
+ libyang:
+ build-packages:
+ - cmake
+ - make
+ - gcc
+ - libpcre3-dev
+ stage-packages:
+ - libpcre3
+ source: https://github.com/CESNET/libyang.git
+ source-type: git
+ source-tag: v0.16-r3
+ plugin: cmake
+ configflags:
+ - -DCMAKE_INSTALL_PREFIX:PATH=/usr
+ - -DENABLE_LYD_PRIV=ON
+ - -DENABLE_CACHE=OFF
+ - -DCMAKE_BUILD_TYPE:String="Release"
frr:
- after: [rtrlib]
+ after: [rtrlib,libyang]
build-packages:
- gcc
- autoconf
@@ -272,13 +302,13 @@ parts:
- iproute2
- logrotate
- libcap2
- - libc6
- libtinfo5
- libreadline6
- libjson-c2
- libc-ares2
- libatm1
- libprotobuf-c1
+ - libdb5.3
plugin: autotools
source: ../frr-@PACKAGE_VERSION@.tar.gz
configflags:
@@ -322,7 +352,9 @@ parts:
eigrpd.conf.default: etc/frr/eigrpd.conf.default
pbrd.conf.default: etc/frr/pbrd.conf.default
bfdd.conf.default: etc/frr/bfdd.conf.default
+ fabricd.conf.default: etc/frr/fabricd.conf.default
vtysh.conf.default: etc/frr/vtysh.conf.default
+ staticd.conf.default: etc/frr/staticd.conf.default
frr-scripts:
plugin: make
source: scripts
@@ -344,3 +376,8 @@ parts:
README.snap_build.md: doc/README.snap_build.md
extra_version_info.txt: doc/extra_version_info.txt
+passthrough:
+ layout:
+ /usr/lib/x86_64-linux-gnu/libyang:
+ bind: $SNAP/usr/lib/x86_64-linux-gnu/libyang
+
diff --git a/zebra/rib.h b/zebra/rib.h
index 0353c9bb99..a54e164d98 100644
--- a/zebra/rib.h
+++ b/zebra/rib.h
@@ -87,9 +87,12 @@ struct route_entry {
/* Link list. */
struct re_list_item next;
- /* Nexthop structure */
+ /* Nexthop structure (from RIB) */
struct nexthop_group ng;
+ /* Nexthop group from FIB (optional) */
+ struct nexthop_group fib_ng;
+
/* Tag */
route_tag_t tag;
@@ -529,6 +532,16 @@ static inline void rib_tables_iter_cleanup(rib_tables_iter_t *iter)
DECLARE_HOOK(rib_update, (struct route_node * rn, const char *reason),
(rn, reason))
+/*
+ * Access active nexthop-group, either RIB or FIB version
+ */
+static inline struct nexthop_group *rib_active_nhg(struct route_entry *re)
+{
+ if (re->fib_ng.nexthop)
+ return &(re->fib_ng);
+ else
+ return &(re->ng);
+}
extern void zebra_vty_init(void);
diff --git a/zebra/subdir.am b/zebra/subdir.am
index 1e36d020a3..25040a2717 100644
--- a/zebra/subdir.am
+++ b/zebra/subdir.am
@@ -75,6 +75,7 @@ zebra_zebra_SOURCES = \
zebra/zebra_mpls_null.c \
zebra/zebra_mpls_vty.c \
zebra/zebra_mroute.c \
+ zebra/zebra_nhg.c \
zebra/zebra_ns.c \
zebra/zebra_pbr.c \
zebra/zebra_ptm.c \
@@ -135,6 +136,7 @@ noinst_HEADERS += \
zebra/zebra_memory.h \
zebra/zebra_mpls.h \
zebra/zebra_mroute.h \
+ zebra/zebra_nhg.h \
zebra/zebra_ns.h \
zebra/zebra_pbr.h \
zebra/zebra_ptm.h \
diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c
index 6fc62147c8..1707d3a68b 100644
--- a/zebra/zebra_dplane.c
+++ b/zebra/zebra_dplane.c
@@ -172,6 +172,11 @@ struct zebra_dplane_ctx {
uint32_t zd_seq;
uint32_t zd_old_seq;
+ /* Some updates may be generated by notifications: allow the
+ * plugin to notice and ignore results from its own notifications.
+ */
+ uint32_t zd_notif_provider;
+
/* TODO -- internal/sub-operation status? */
enum zebra_dplane_result zd_remote_status;
enum zebra_dplane_result zd_kernel_status;
@@ -222,6 +227,8 @@ struct zebra_dplane_provider {
/* Flags */
int dp_flags;
+ int (*dp_start)(struct zebra_dplane_provider *prov);
+
int (*dp_fp)(struct zebra_dplane_provider *prov);
int (*dp_fini)(struct zebra_dplane_provider *prov, bool early_p);
@@ -351,7 +358,7 @@ struct thread_master *dplane_get_thread_master(void)
/*
* Allocate a dataplane update context
*/
-static struct zebra_dplane_ctx *dplane_ctx_alloc(void)
+struct zebra_dplane_ctx *dplane_ctx_alloc(void)
{
struct zebra_dplane_ctx *p;
@@ -392,6 +399,7 @@ static void dplane_ctx_free(struct zebra_dplane_ctx **pctx)
case DPLANE_OP_ROUTE_DELETE:
case DPLANE_OP_SYS_ROUTE_ADD:
case DPLANE_OP_SYS_ROUTE_DELETE:
+ case DPLANE_OP_ROUTE_NOTIFY:
/* Free allocated nexthops */
if ((*pctx)->u.rinfo.zd_ng.nexthop) {
@@ -413,6 +421,7 @@ static void dplane_ctx_free(struct zebra_dplane_ctx **pctx)
case DPLANE_OP_LSP_INSTALL:
case DPLANE_OP_LSP_UPDATE:
case DPLANE_OP_LSP_DELETE:
+ case DPLANE_OP_LSP_NOTIFY:
{
zebra_nhlfe_t *nhlfe, *next;
@@ -543,6 +552,12 @@ bool dplane_ctx_is_skip_kernel(const struct zebra_dplane_ctx *ctx)
return CHECK_FLAG(ctx->zd_flags, DPLANE_CTX_FLAG_NO_KERNEL);
}
+void dplane_ctx_set_op(struct zebra_dplane_ctx *ctx, enum dplane_op_e op)
+{
+ DPLANE_CTX_VALID(ctx);
+ ctx->zd_op = op;
+}
+
enum dplane_op_e dplane_ctx_get_op(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
@@ -569,6 +584,9 @@ const char *dplane_op2str(enum dplane_op_e op)
case DPLANE_OP_ROUTE_DELETE:
ret = "ROUTE_DELETE";
break;
+ case DPLANE_OP_ROUTE_NOTIFY:
+ ret = "ROUTE_NOTIFY";
+ break;
case DPLANE_OP_LSP_INSTALL:
ret = "LSP_INSTALL";
@@ -579,6 +597,9 @@ const char *dplane_op2str(enum dplane_op_e op)
case DPLANE_OP_LSP_DELETE:
ret = "LSP_DELETE";
break;
+ case DPLANE_OP_LSP_NOTIFY:
+ ret = "LSP_NOTIFY";
+ break;
case DPLANE_OP_PW_INSTALL:
ret = "PW_INSTALL";
@@ -625,6 +646,14 @@ const char *dplane_res2str(enum zebra_dplane_result res)
return ret;
}
+void dplane_ctx_set_dest(struct zebra_dplane_ctx *ctx,
+ const struct prefix *dest)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ prefix_copy(&(ctx->u.rinfo.zd_dest), dest);
+}
+
const struct prefix *dplane_ctx_get_dest(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
@@ -632,6 +661,16 @@ const struct prefix *dplane_ctx_get_dest(const struct zebra_dplane_ctx *ctx)
return &(ctx->u.rinfo.zd_dest);
}
+void dplane_ctx_set_src(struct zebra_dplane_ctx *ctx, const struct prefix *src)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ if (src)
+ prefix_copy(&(ctx->u.rinfo.zd_src), src);
+ else
+ memset(&(ctx->u.rinfo.zd_src), 0, sizeof(struct prefix));
+}
+
/* Source prefix is a little special - return NULL for "no src prefix" */
const struct prefix *dplane_ctx_get_src(const struct zebra_dplane_ctx *ctx)
{
@@ -666,6 +705,13 @@ uint32_t dplane_ctx_get_old_seq(const struct zebra_dplane_ctx *ctx)
return ctx->zd_old_seq;
}
+void dplane_ctx_set_vrf(struct zebra_dplane_ctx *ctx, vrf_id_t vrf)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->zd_vrf_id = vrf;
+}
+
vrf_id_t dplane_ctx_get_vrf(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
@@ -673,6 +719,35 @@ vrf_id_t dplane_ctx_get_vrf(const struct zebra_dplane_ctx *ctx)
return ctx->zd_vrf_id;
}
+bool dplane_ctx_is_from_notif(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return (ctx->zd_notif_provider != 0);
+}
+
+uint32_t dplane_ctx_get_notif_provider(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->zd_notif_provider;
+}
+
+void dplane_ctx_set_notif_provider(struct zebra_dplane_ctx *ctx,
+ uint32_t id)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->zd_notif_provider = id;
+}
+
+void dplane_ctx_set_type(struct zebra_dplane_ctx *ctx, int type)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.rinfo.zd_type = type;
+}
+
int dplane_ctx_get_type(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
@@ -687,6 +762,13 @@ int dplane_ctx_get_old_type(const struct zebra_dplane_ctx *ctx)
return ctx->u.rinfo.zd_old_type;
}
+void dplane_ctx_set_afi(struct zebra_dplane_ctx *ctx, afi_t afi)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.rinfo.zd_afi = afi;
+}
+
afi_t dplane_ctx_get_afi(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
@@ -694,6 +776,13 @@ afi_t dplane_ctx_get_afi(const struct zebra_dplane_ctx *ctx)
return ctx->u.rinfo.zd_afi;
}
+void dplane_ctx_set_safi(struct zebra_dplane_ctx *ctx, safi_t safi)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.rinfo.zd_safi = safi;
+}
+
safi_t dplane_ctx_get_safi(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
@@ -701,6 +790,13 @@ safi_t dplane_ctx_get_safi(const struct zebra_dplane_ctx *ctx)
return ctx->u.rinfo.zd_safi;
}
+void dplane_ctx_set_table(struct zebra_dplane_ctx *ctx, uint32_t table)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->zd_table_id = table;
+}
+
uint32_t dplane_ctx_get_table(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
@@ -715,6 +811,13 @@ route_tag_t dplane_ctx_get_tag(const struct zebra_dplane_ctx *ctx)
return ctx->u.rinfo.zd_tag;
}
+void dplane_ctx_set_tag(struct zebra_dplane_ctx *ctx, route_tag_t tag)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.rinfo.zd_tag = tag;
+}
+
route_tag_t dplane_ctx_get_old_tag(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
@@ -729,6 +832,13 @@ uint16_t dplane_ctx_get_instance(const struct zebra_dplane_ctx *ctx)
return ctx->u.rinfo.zd_instance;
}
+void dplane_ctx_set_instance(struct zebra_dplane_ctx *ctx, uint16_t instance)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.rinfo.zd_instance = instance;
+}
+
uint16_t dplane_ctx_get_old_instance(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
@@ -771,6 +881,13 @@ uint8_t dplane_ctx_get_distance(const struct zebra_dplane_ctx *ctx)
return ctx->u.rinfo.zd_distance;
}
+void dplane_ctx_set_distance(struct zebra_dplane_ctx *ctx, uint8_t distance)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.rinfo.zd_distance = distance;
+}
+
uint8_t dplane_ctx_get_old_distance(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
@@ -778,6 +895,17 @@ uint8_t dplane_ctx_get_old_distance(const struct zebra_dplane_ctx *ctx)
return ctx->u.rinfo.zd_old_distance;
}
+void dplane_ctx_set_nexthops(struct zebra_dplane_ctx *ctx, struct nexthop *nh)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ if (ctx->u.rinfo.zd_ng.nexthop) {
+ nexthops_free(ctx->u.rinfo.zd_ng.nexthop);
+ ctx->u.rinfo.zd_ng.nexthop = NULL;
+ }
+ copy_nexthops(&(ctx->u.rinfo.zd_ng.nexthop), nh, NULL);
+}
+
const struct nexthop_group *dplane_ctx_get_ng(
const struct zebra_dplane_ctx *ctx)
{
@@ -811,6 +939,13 @@ mpls_label_t dplane_ctx_get_in_label(const struct zebra_dplane_ctx *ctx)
return ctx->u.lsp.ile.in_label;
}
+void dplane_ctx_set_in_label(struct zebra_dplane_ctx *ctx, mpls_label_t label)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.lsp.ile.in_label = label;
+}
+
uint8_t dplane_ctx_get_addr_family(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
@@ -818,6 +953,14 @@ uint8_t dplane_ctx_get_addr_family(const struct zebra_dplane_ctx *ctx)
return ctx->u.lsp.addr_family;
}
+void dplane_ctx_set_addr_family(struct zebra_dplane_ctx *ctx,
+ uint8_t family)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.lsp.addr_family = family;
+}
+
uint32_t dplane_ctx_get_lsp_flags(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
@@ -825,6 +968,14 @@ uint32_t dplane_ctx_get_lsp_flags(const struct zebra_dplane_ctx *ctx)
return ctx->u.lsp.flags;
}
+void dplane_ctx_set_lsp_flags(struct zebra_dplane_ctx *ctx,
+ uint32_t flags)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.lsp.flags = flags;
+}
+
const zebra_nhlfe_t *dplane_ctx_get_nhlfe(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
@@ -832,6 +983,24 @@ const zebra_nhlfe_t *dplane_ctx_get_nhlfe(const struct zebra_dplane_ctx *ctx)
return ctx->u.lsp.nhlfe_list;
}
+zebra_nhlfe_t *dplane_ctx_add_nhlfe(struct zebra_dplane_ctx *ctx,
+ enum lsp_types_t lsp_type,
+ enum nexthop_types_t nh_type,
+ union g_addr *gate,
+ ifindex_t ifindex,
+ mpls_label_t out_label)
+{
+ zebra_nhlfe_t *nhlfe;
+
+ DPLANE_CTX_VALID(ctx);
+
+ nhlfe = zebra_mpls_lsp_add_nhlfe(&(ctx->u.lsp),
+ lsp_type, nh_type, gate,
+ ifindex, out_label);
+
+ return nhlfe;
+}
+
const zebra_nhlfe_t *
dplane_ctx_get_best_nhlfe(const struct zebra_dplane_ctx *ctx)
{
@@ -840,6 +1009,16 @@ dplane_ctx_get_best_nhlfe(const struct zebra_dplane_ctx *ctx)
return ctx->u.lsp.best_nhlfe;
}
+const zebra_nhlfe_t *
+dplane_ctx_set_best_nhlfe(struct zebra_dplane_ctx *ctx,
+ zebra_nhlfe_t *nhlfe)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.lsp.best_nhlfe = nhlfe;
+ return ctx->u.lsp.best_nhlfe;
+}
+
uint32_t dplane_ctx_get_lsp_num_ecmp(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
@@ -1497,6 +1676,59 @@ done:
}
/*
+ * Update from an async notification, to bring other fibs up-to-date.
+ */
+enum zebra_dplane_result
+dplane_route_notif_update(struct route_node *rn,
+ struct route_entry *re,
+ enum dplane_op_e op,
+ struct zebra_dplane_ctx *ctx)
+{
+ enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
+ struct zebra_dplane_ctx *new_ctx = NULL;
+ struct nexthop *nexthop;
+
+ if (rn == NULL || re == NULL)
+ goto done;
+
+ new_ctx = dplane_ctx_alloc();
+ if (new_ctx == NULL)
+ goto done;
+
+ /* Init context with info from zebra data structs */
+ dplane_ctx_route_init(new_ctx, op, rn, re);
+
+ /* For add/update, need to adjust the nexthops so that we match
+ * the notification state, which may not be the route-entry/RIB
+ * state.
+ */
+ if (op == DPLANE_OP_ROUTE_UPDATE ||
+ op == DPLANE_OP_ROUTE_INSTALL) {
+
+ nexthops_free(new_ctx->u.rinfo.zd_ng.nexthop);
+ new_ctx->u.rinfo.zd_ng.nexthop = NULL;
+
+ copy_nexthops(&(new_ctx->u.rinfo.zd_ng.nexthop),
+ (rib_active_nhg(re))->nexthop, NULL);
+
+ for (ALL_NEXTHOPS(new_ctx->u.rinfo.zd_ng, nexthop))
+ UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
+
+ }
+
+ /* Capture info about the source of the notification, in 'ctx' */
+ dplane_ctx_set_notif_provider(new_ctx,
+ dplane_ctx_get_notif_provider(ctx));
+
+ dplane_route_enqueue(new_ctx);
+
+ ret = ZEBRA_DPLANE_REQUEST_QUEUED;
+
+done:
+ return ret;
+}
+
+/*
* Enqueue LSP add for the dataplane.
*/
enum zebra_dplane_result dplane_lsp_add(zebra_lsp_t *lsp)
@@ -1529,6 +1761,50 @@ enum zebra_dplane_result dplane_lsp_delete(zebra_lsp_t *lsp)
return ret;
}
+/* Update or un-install resulting from an async notification */
+enum zebra_dplane_result
+dplane_lsp_notif_update(zebra_lsp_t *lsp,
+ enum dplane_op_e op,
+ struct zebra_dplane_ctx *notif_ctx)
+{
+ enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
+ int ret = EINVAL;
+ struct zebra_dplane_ctx *ctx = NULL;
+
+ /* Obtain context block */
+ ctx = dplane_ctx_alloc();
+ if (ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = dplane_ctx_lsp_init(ctx, op, lsp);
+ if (ret != AOK)
+ goto done;
+
+ /* Capture info about the source of the notification */
+ dplane_ctx_set_notif_provider(
+ ctx,
+ dplane_ctx_get_notif_provider(notif_ctx));
+
+ ret = dplane_route_enqueue(ctx);
+
+done:
+ /* Update counter */
+ atomic_fetch_add_explicit(&zdplane_info.dg_lsps_in, 1,
+ memory_order_relaxed);
+
+ if (ret == AOK)
+ result = ZEBRA_DPLANE_REQUEST_QUEUED;
+ else {
+ atomic_fetch_add_explicit(&zdplane_info.dg_lsp_errors, 1,
+ memory_order_relaxed);
+ if (ctx)
+ dplane_ctx_free(&ctx);
+ }
+ return result;
+}
+
/*
* Enqueue pseudowire install for the dataplane.
*/
@@ -1823,6 +2099,7 @@ int dplane_show_provs_helper(struct vty *vty, bool detailed)
int dplane_provider_register(const char *name,
enum dplane_provider_prio prio,
int flags,
+ int (*start_fp)(struct zebra_dplane_provider *),
int (*fp)(struct zebra_dplane_provider *),
int (*fini_fp)(struct zebra_dplane_provider *,
bool early),
@@ -1853,6 +2130,7 @@ int dplane_provider_register(const char *name,
p->dp_priority = prio;
p->dp_fp = fp;
+ p->dp_start = start_fp;
p->dp_fini = fini_fp;
p->dp_data = data;
@@ -2046,6 +2324,20 @@ int dplane_provider_work_ready(void)
}
/*
+ * Enqueue a context directly to zebra main.
+ */
+void dplane_provider_enqueue_to_zebra(struct zebra_dplane_ctx *ctx)
+{
+ struct dplane_ctx_q temp_list;
+
+ /* Zebra's api takes a list, so we need to use a temporary list */
+ TAILQ_INIT(&temp_list);
+
+ TAILQ_INSERT_TAIL(&temp_list, ctx, zd_q_entries);
+ (zdplane_info.dg_results_cb)(&temp_list);
+}
+
+/*
* Kernel dataplane provider
*/
@@ -2207,9 +2499,11 @@ static int kernel_dplane_process_func(struct zebra_dplane_provider *prov)
res = kernel_dplane_address_update(ctx);
break;
- /* Ignore system 'notifications' - the kernel already knows */
+ /* Ignore 'notifications' - no-op */
case DPLANE_OP_SYS_ROUTE_ADD:
case DPLANE_OP_SYS_ROUTE_DELETE:
+ case DPLANE_OP_ROUTE_NOTIFY:
+ case DPLANE_OP_LSP_NOTIFY:
res = ZEBRA_DPLANE_REQUEST_SUCCESS;
break;
@@ -2320,7 +2614,7 @@ static void dplane_provider_init(void)
ret = dplane_provider_register("Kernel",
DPLANE_PRIO_KERNEL,
- DPLANE_PROV_FLAGS_DEFAULT,
+ DPLANE_PROV_FLAGS_DEFAULT, NULL,
kernel_dplane_process_func,
NULL,
NULL, NULL);
@@ -2333,7 +2627,7 @@ static void dplane_provider_init(void)
/* Optional test provider ... */
ret = dplane_provider_register("Test",
DPLANE_PRIO_PRE_KERNEL,
- DPLANE_PROV_FLAGS_DEFAULT,
+ DPLANE_PROV_FLAGS_DEFAULT, NULL,
test_dplane_process_func,
test_dplane_shutdown_func,
NULL /* data */, NULL);
@@ -2652,7 +2946,6 @@ static int dplane_thread_loop(struct thread *event)
TAILQ_INIT(&error_list);
-
/* Call through to zebra main */
(zdplane_info.dg_results_cb)(&work_list);
@@ -2717,13 +3010,14 @@ static void zebra_dplane_init_internal(void)
*/
void zebra_dplane_start(void)
{
- /* Start dataplane pthread */
-
+ struct zebra_dplane_provider *prov;
struct frr_pthread_attr pattr = {
.start = frr_pthread_attr_default.start,
.stop = frr_pthread_attr_default.stop
};
+ /* Start dataplane pthread */
+
zdplane_info.dg_pthread = frr_pthread_new(&pattr, "Zebra dplane thread",
"Zebra dplane");
@@ -2735,6 +3029,23 @@ void zebra_dplane_start(void)
thread_add_event(zdplane_info.dg_master, dplane_thread_loop, NULL, 0,
&zdplane_info.dg_t_update);
+ /* Call start callbacks for registered providers */
+
+ DPLANE_LOCK();
+ prov = TAILQ_FIRST(&zdplane_info.dg_providers_q);
+ DPLANE_UNLOCK();
+
+ while (prov) {
+
+ if (prov->dp_start)
+ (prov->dp_start)(prov);
+
+ /* Locate next provider */
+ DPLANE_LOCK();
+ prov = TAILQ_NEXT(prov, dp_prov_link);
+ DPLANE_UNLOCK();
+ }
+
frr_pthread_run(zdplane_info.dg_pthread, NULL);
}
diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h
index d45628fdd0..6238026bcf 100644
--- a/zebra/zebra_dplane.h
+++ b/zebra/zebra_dplane.h
@@ -105,11 +105,13 @@ enum dplane_op_e {
DPLANE_OP_ROUTE_INSTALL,
DPLANE_OP_ROUTE_UPDATE,
DPLANE_OP_ROUTE_DELETE,
+ DPLANE_OP_ROUTE_NOTIFY,
/* LSP update */
DPLANE_OP_LSP_INSTALL,
DPLANE_OP_LSP_UPDATE,
DPLANE_OP_LSP_DELETE,
+ DPLANE_OP_LSP_NOTIFY,
/* Pseudowire update */
DPLANE_OP_PW_INSTALL,
@@ -139,6 +141,9 @@ void dplane_enable_sys_route_notifs(void);
*/
TAILQ_HEAD(dplane_ctx_q, zebra_dplane_ctx);
+/* Allocate a context object */
+struct zebra_dplane_ctx *dplane_ctx_alloc(void);
+
/* Return a dataplane results context block after use; the caller's pointer will
* be cleared.
*/
@@ -169,9 +174,12 @@ void dplane_ctx_set_status(struct zebra_dplane_ctx *ctx,
const char *dplane_res2str(enum zebra_dplane_result res);
enum dplane_op_e dplane_ctx_get_op(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_op(struct zebra_dplane_ctx *ctx, enum dplane_op_e op);
const char *dplane_op2str(enum dplane_op_e op);
const struct prefix *dplane_ctx_get_dest(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_dest(struct zebra_dplane_ctx *ctx,
+ const struct prefix *dest);
/* Retrieve last/current provider id */
uint32_t dplane_ctx_get_provider(const struct zebra_dplane_ctx *ctx);
@@ -186,29 +194,44 @@ bool dplane_ctx_is_skip_kernel(const struct zebra_dplane_ctx *ctx);
* to mean "no src prefix"
*/
const struct prefix *dplane_ctx_get_src(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_src(struct zebra_dplane_ctx *ctx, const struct prefix *src);
bool dplane_ctx_is_update(const struct zebra_dplane_ctx *ctx);
uint32_t dplane_ctx_get_seq(const struct zebra_dplane_ctx *ctx);
uint32_t dplane_ctx_get_old_seq(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_vrf(struct zebra_dplane_ctx *ctx, vrf_id_t vrf);
vrf_id_t dplane_ctx_get_vrf(const struct zebra_dplane_ctx *ctx);
+bool dplane_ctx_is_from_notif(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_notif_provider(struct zebra_dplane_ctx *ctx,
+ uint32_t id);
+uint32_t dplane_ctx_get_notif_provider(const struct zebra_dplane_ctx *ctx);
+
/* Accessors for route update information */
+void dplane_ctx_set_type(struct zebra_dplane_ctx *ctx, int type);
int dplane_ctx_get_type(const struct zebra_dplane_ctx *ctx);
int dplane_ctx_get_old_type(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_afi(struct zebra_dplane_ctx *ctx, afi_t afi);
afi_t dplane_ctx_get_afi(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_safi(struct zebra_dplane_ctx *ctx, safi_t safi);
safi_t dplane_ctx_get_safi(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_table(struct zebra_dplane_ctx *ctx, uint32_t table);
uint32_t dplane_ctx_get_table(const struct zebra_dplane_ctx *ctx);
route_tag_t dplane_ctx_get_tag(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_tag(struct zebra_dplane_ctx *ctx, route_tag_t tag);
route_tag_t dplane_ctx_get_old_tag(const struct zebra_dplane_ctx *ctx);
uint16_t dplane_ctx_get_instance(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_instance(struct zebra_dplane_ctx *ctx, uint16_t instance);
uint16_t dplane_ctx_get_old_instance(const struct zebra_dplane_ctx *ctx);
uint32_t dplane_ctx_get_metric(const struct zebra_dplane_ctx *ctx);
uint32_t dplane_ctx_get_old_metric(const struct zebra_dplane_ctx *ctx);
uint32_t dplane_ctx_get_mtu(const struct zebra_dplane_ctx *ctx);
uint32_t dplane_ctx_get_nh_mtu(const struct zebra_dplane_ctx *ctx);
uint8_t dplane_ctx_get_distance(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_distance(struct zebra_dplane_ctx *ctx, uint8_t distance);
uint8_t dplane_ctx_get_old_distance(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_nexthops(struct zebra_dplane_ctx *ctx, struct nexthop *nh);
const struct nexthop_group *dplane_ctx_get_ng(
const struct zebra_dplane_ctx *ctx);
const struct nexthop_group *dplane_ctx_get_old_ng(
@@ -216,11 +239,26 @@ const struct nexthop_group *dplane_ctx_get_old_ng(
/* Accessors for LSP information */
mpls_label_t dplane_ctx_get_in_label(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_in_label(struct zebra_dplane_ctx *ctx,
+ mpls_label_t label);
uint8_t dplane_ctx_get_addr_family(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_addr_family(struct zebra_dplane_ctx *ctx,
+ uint8_t family);
uint32_t dplane_ctx_get_lsp_flags(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_lsp_flags(struct zebra_dplane_ctx *ctx,
+ uint32_t flags);
const zebra_nhlfe_t *dplane_ctx_get_nhlfe(const struct zebra_dplane_ctx *ctx);
+zebra_nhlfe_t *dplane_ctx_add_nhlfe(struct zebra_dplane_ctx *ctx,
+ enum lsp_types_t lsp_type,
+ enum nexthop_types_t nh_type,
+ union g_addr *gate,
+ ifindex_t ifindex,
+ mpls_label_t out_label);
+
const zebra_nhlfe_t *dplane_ctx_get_best_nhlfe(
const struct zebra_dplane_ctx *ctx);
+const zebra_nhlfe_t *dplane_ctx_set_best_nhlfe(struct zebra_dplane_ctx *ctx,
+ zebra_nhlfe_t *nhlfe);
uint32_t dplane_ctx_get_lsp_num_ecmp(const struct zebra_dplane_ctx *ctx);
/* Accessors for pseudowire information */
@@ -282,6 +320,13 @@ enum zebra_dplane_result dplane_sys_route_add(struct route_node *rn,
enum zebra_dplane_result dplane_sys_route_del(struct route_node *rn,
struct route_entry *re);
+/* Update from an async notification, to bring other fibs up-to-date */
+enum zebra_dplane_result dplane_route_notif_update(
+ struct route_node *rn,
+ struct route_entry *re,
+ enum dplane_op_e op,
+ struct zebra_dplane_ctx *ctx);
+
/*
* Enqueue LSP change operations for the dataplane.
*/
@@ -289,6 +334,11 @@ enum zebra_dplane_result dplane_lsp_add(zebra_lsp_t *lsp);
enum zebra_dplane_result dplane_lsp_update(zebra_lsp_t *lsp);
enum zebra_dplane_result dplane_lsp_delete(zebra_lsp_t *lsp);
+/* Update or un-install resulting from an async notification */
+enum zebra_dplane_result dplane_lsp_notif_update(zebra_lsp_t *lsp,
+ enum dplane_op_e op,
+ struct zebra_dplane_ctx *ctx);
+
/*
* Enqueue pseudowire operations for the dataplane.
*/
@@ -321,7 +371,6 @@ uint32_t dplane_get_in_queue_len(void);
int dplane_show_helper(struct vty *vty, bool detailed);
int dplane_show_provs_helper(struct vty *vty, bool detailed);
-
/*
* Dataplane providers: modules that process or consume dataplane events.
*/
@@ -363,7 +412,13 @@ enum dplane_provider_prio {
* then checks the provider's outbound queue for completed work.
*/
-/* Providers offer an entry-point for shutdown and cleanup. This is called
+/*
+ * Providers can offer a 'start' callback; if present, the dataplane will
+ * call it when it is starting - when its pthread and event-scheduling
+ * thread_master are available.
+ */
+
+/* Providers can offer an entry-point for shutdown and cleanup. This is called
* with 'early' during shutdown, to indicate that the dataplane subsystem
* is allowing work to move through the providers and finish.
* When called without 'early', the provider should release
@@ -372,6 +427,7 @@ enum dplane_provider_prio {
int dplane_provider_register(const char *name,
enum dplane_provider_prio prio,
int flags,
+ int (*start_fp)(struct zebra_dplane_provider *),
int (*fp)(struct zebra_dplane_provider *),
int (*fini_fp)(struct zebra_dplane_provider *,
bool early),
@@ -409,10 +465,13 @@ struct zebra_dplane_ctx *dplane_provider_dequeue_in_ctx(
int dplane_provider_dequeue_in_list(struct zebra_dplane_provider *prov,
struct dplane_ctx_q *listp);
-/* Enqueue, maintain associated counter and locking */
+/* Enqueue completed work, maintain associated counter and locking */
void dplane_provider_enqueue_out_ctx(struct zebra_dplane_provider *prov,
struct zebra_dplane_ctx *ctx);
+/* Enqueue a context directly to zebra main. */
+void dplane_provider_enqueue_to_zebra(struct zebra_dplane_ctx *ctx);
+
/*
* Initialize the dataplane modules at zebra startup. This is currently called
* by the rib module. Zebra registers a results callback with the dataplane.
diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c
index 5356a7f498..48366417dd 100644
--- a/zebra/zebra_mpls.c
+++ b/zebra/zebra_mpls.c
@@ -1810,6 +1810,214 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx)
}
/*
+ * Process async dplane notifications.
+ */
+void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx)
+{
+ struct zebra_vrf *zvrf;
+ zebra_ile_t tmp_ile;
+ struct hash *lsp_table;
+ zebra_lsp_t *lsp;
+ zebra_nhlfe_t *nhlfe;
+ const zebra_nhlfe_t *ctx_nhlfe;
+ struct nexthop *nexthop;
+ const struct nexthop *ctx_nexthop;
+ int start_count = 0, end_count = 0; /* Installed counts */
+ bool changed_p = false;
+ bool is_debug = (IS_ZEBRA_DEBUG_DPLANE | IS_ZEBRA_DEBUG_MPLS);
+
+ if (is_debug)
+ zlog_debug("LSP dplane notif, in-label %u",
+ dplane_ctx_get_in_label(ctx));
+
+ /* Look for zebra LSP object */
+ zvrf = vrf_info_lookup(VRF_DEFAULT);
+ if (zvrf == NULL)
+ goto done;
+
+ lsp_table = zvrf->lsp_table;
+
+ tmp_ile.in_label = dplane_ctx_get_in_label(ctx);
+ lsp = hash_lookup(lsp_table, &tmp_ile);
+ if (lsp == NULL) {
+ if (is_debug)
+ zlog_debug("dplane LSP notif: in-label %u not found",
+ dplane_ctx_get_in_label(ctx));
+ goto done;
+ }
+
+ /*
+ * The dataplane/forwarding plane is notifying zebra about the state
+ * of the nexthops associated with this LSP. First, we take a
+ * pre-scan pass to determine whether the LSP has transitioned
+ * from installed -> uninstalled. In that case, we need to have
+ * the existing state of the LSP objects available before making
+ * any changes.
+ */
+ for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) {
+ char buf[NEXTHOP_STRLEN];
+
+ nexthop = nhlfe->nexthop;
+ if (!nexthop)
+ continue;
+
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB))
+ start_count++;
+
+ ctx_nexthop = NULL;
+ for (ctx_nhlfe = dplane_ctx_get_nhlfe(ctx);
+ ctx_nhlfe; ctx_nhlfe = ctx_nhlfe->next) {
+
+ ctx_nexthop = ctx_nhlfe->nexthop;
+ if (!ctx_nexthop)
+ continue;
+
+ if ((ctx_nexthop->type == nexthop->type) &&
+ nexthop_same(ctx_nexthop, nexthop)) {
+ /* Matched */
+ break;
+ }
+ }
+
+ if (is_debug)
+ nexthop2str(nexthop, buf, sizeof(buf));
+
+ if (ctx_nhlfe && ctx_nexthop) {
+ if (is_debug) {
+ const char *tstr = "";
+
+ if (!CHECK_FLAG(ctx_nhlfe->flags,
+ NHLFE_FLAG_INSTALLED))
+ tstr = "not ";
+
+ zlog_debug("LSP dplane notif: matched nh %s (%sinstalled)",
+ buf, tstr);
+ }
+
+ /* Test zebra nhlfe install state */
+ if (CHECK_FLAG(ctx_nhlfe->flags,
+ NHLFE_FLAG_INSTALLED)) {
+
+ if (!CHECK_FLAG(nhlfe->flags,
+ NHLFE_FLAG_INSTALLED))
+ changed_p = true;
+
+ /* Update counter */
+ end_count++;
+ } else {
+
+ if (CHECK_FLAG(nhlfe->flags,
+ NHLFE_FLAG_INSTALLED))
+ changed_p = true;
+ }
+
+ } else {
+ /* Not mentioned in lfib set -> uninstalled */
+ if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED) ||
+ CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE) ||
+ CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) {
+ changed_p = true;
+ }
+
+ if (is_debug)
+ zlog_debug("LSP dplane notif: no match, nh %s",
+ buf);
+ }
+ }
+
+ if (is_debug)
+ zlog_debug("LSP dplane notif: lfib start_count %d, end_count %d%s",
+ start_count, end_count,
+ changed_p ? ", changed" : "");
+
+ /*
+ * Has the LSP become uninstalled?
+ */
+ if (start_count > 0 && end_count == 0) {
+ /* Inform other lfibs */
+ dplane_lsp_notif_update(lsp, DPLANE_OP_LSP_DELETE, ctx);
+ }
+
+ /*
+ * Now we take a second pass and bring the zebra
+ * nexthop state into sync with the forwarding-plane state.
+ */
+ for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) {
+ char buf[NEXTHOP_STRLEN];
+
+ nexthop = nhlfe->nexthop;
+ if (!nexthop)
+ continue;
+
+ ctx_nexthop = NULL;
+ for (ctx_nhlfe = dplane_ctx_get_nhlfe(ctx);
+ ctx_nhlfe; ctx_nhlfe = ctx_nhlfe->next) {
+
+ ctx_nexthop = ctx_nhlfe->nexthop;
+ if (!ctx_nexthop)
+ continue;
+
+ if ((ctx_nexthop->type == nexthop->type) &&
+ nexthop_same(ctx_nexthop, nexthop)) {
+ /* Matched */
+ break;
+ }
+ }
+
+ if (is_debug)
+ nexthop2str(nexthop, buf, sizeof(buf));
+
+ if (ctx_nhlfe && ctx_nexthop) {
+
+ /* Bring zebra nhlfe install state into sync */
+ if (CHECK_FLAG(ctx_nhlfe->flags,
+ NHLFE_FLAG_INSTALLED)) {
+
+ SET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED);
+
+ } else {
+
+ UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED);
+ }
+
+ if (CHECK_FLAG(ctx_nhlfe->nexthop->flags,
+ NEXTHOP_FLAG_FIB)) {
+ SET_FLAG(nhlfe->nexthop->flags,
+ NEXTHOP_FLAG_ACTIVE);
+ SET_FLAG(nhlfe->nexthop->flags,
+ NEXTHOP_FLAG_FIB);
+ } else {
+ UNSET_FLAG(nhlfe->nexthop->flags,
+ NEXTHOP_FLAG_ACTIVE);
+ UNSET_FLAG(nhlfe->nexthop->flags,
+ NEXTHOP_FLAG_FIB);
+ }
+
+ } else {
+ /* Not mentioned in lfib set -> uninstalled */
+
+ UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED);
+ UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
+ UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ }
+ }
+
+ if (end_count > 0) {
+ SET_FLAG(lsp->flags, LSP_FLAG_INSTALLED);
+
+ if (changed_p)
+ dplane_lsp_notif_update(lsp, DPLANE_OP_LSP_UPDATE, ctx);
+
+ } else {
+ UNSET_FLAG(lsp->flags, LSP_FLAG_INSTALLED);
+ clear_nhlfe_installed(lsp);
+ }
+
+done:
+ dplane_ctx_fini(&ctx);
+}
+
+/*
* Install dynamic LSP entry.
*/
int zebra_mpls_lsp_install(struct zebra_vrf *zvrf, struct route_node *rn,
diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h
index 3a131e1aaf..d983221cb5 100644
--- a/zebra/zebra_mpls.h
+++ b/zebra/zebra_mpls.h
@@ -352,6 +352,9 @@ struct zebra_dplane_ctx;
void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx);
+/* Process async dplane notifications. */
+void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx);
+
/*
* Schedule all MPLS label forwarding entries for processing.
* Called upon changes that may affect one or more of them such as
diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c
new file mode 100644
index 0000000000..f2a76d1c52
--- /dev/null
+++ b/zebra/zebra_nhg.c
@@ -0,0 +1,511 @@
+/* Zebra Nexthop Group Code.
+ * Copyright (C) 2019 Cumulus Networks, Inc.
+ * Donald Sharp
+ * Stephen Worley
+ *
+ * This file is part of FRR.
+ *
+ * FRR 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.
+ *
+ * FRR 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 FRR; 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 "lib/nexthop.h"
+#include "lib/routemap.h"
+
+#include "zebra/connected.h"
+#include "zebra/debug.h"
+#include "zebra/zebra_router.h"
+#include "zebra/zebra_nhg.h"
+#include "zebra/zebra_rnh.h"
+#include "zebra/zebra_routemap.h"
+#include "zebra/rt.h"
+
+static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop,
+ struct nexthop *nexthop)
+{
+ struct nexthop *resolved_hop;
+
+ resolved_hop = nexthop_new();
+ SET_FLAG(resolved_hop->flags, NEXTHOP_FLAG_ACTIVE);
+
+ resolved_hop->vrf_id = nexthop->vrf_id;
+ switch (newhop->type) {
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ /* If the resolving route specifies a gateway, use it */
+ resolved_hop->type = newhop->type;
+ resolved_hop->gate.ipv4 = newhop->gate.ipv4;
+
+ if (newhop->ifindex) {
+ resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
+ resolved_hop->ifindex = newhop->ifindex;
+ }
+ break;
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ resolved_hop->type = newhop->type;
+ resolved_hop->gate.ipv6 = newhop->gate.ipv6;
+
+ if (newhop->ifindex) {
+ resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
+ resolved_hop->ifindex = newhop->ifindex;
+ }
+ break;
+ case NEXTHOP_TYPE_IFINDEX:
+ /* If the resolving route is an interface route,
+ * it means the gateway we are looking up is connected
+ * to that interface. (The actual network is _not_ onlink).
+ * Therefore, the resolved route should have the original
+ * gateway as nexthop as it is directly connected.
+ *
+ * On Linux, we have to set the onlink netlink flag because
+ * otherwise, the kernel won't accept the route.
+ */
+ resolved_hop->flags |= NEXTHOP_FLAG_ONLINK;
+ if (afi == AFI_IP) {
+ resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
+ resolved_hop->gate.ipv4 = nexthop->gate.ipv4;
+ } else if (afi == AFI_IP6) {
+ resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
+ resolved_hop->gate.ipv6 = nexthop->gate.ipv6;
+ }
+ resolved_hop->ifindex = newhop->ifindex;
+ break;
+ case NEXTHOP_TYPE_BLACKHOLE:
+ resolved_hop->type = NEXTHOP_TYPE_BLACKHOLE;
+ resolved_hop->bh_type = nexthop->bh_type;
+ break;
+ }
+
+ if (newhop->flags & NEXTHOP_FLAG_ONLINK)
+ resolved_hop->flags |= NEXTHOP_FLAG_ONLINK;
+
+ /* Copy labels of the resolved route */
+ if (newhop->nh_label)
+ nexthop_add_labels(resolved_hop, newhop->nh_label_type,
+ newhop->nh_label->num_labels,
+ &newhop->nh_label->label[0]);
+
+ resolved_hop->rparent = nexthop;
+ nexthop_add(&nexthop->resolved, resolved_hop);
+}
+
+/*
+ * Given a nexthop we need to properly recursively resolve
+ * the route. As such, do a table lookup to find and match
+ * if at all possible. Set the nexthop->ifindex as appropriate
+ */
+static int nexthop_active(afi_t afi, struct route_entry *re,
+ struct nexthop *nexthop, struct route_node *top)
+{
+ struct prefix p;
+ struct route_table *table;
+ struct route_node *rn;
+ struct route_entry *match = NULL;
+ int resolved;
+ struct nexthop *newhop;
+ struct interface *ifp;
+ rib_dest_t *dest;
+
+ if ((nexthop->type == NEXTHOP_TYPE_IPV4)
+ || nexthop->type == NEXTHOP_TYPE_IPV6)
+ nexthop->ifindex = 0;
+
+ UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
+ nexthops_free(nexthop->resolved);
+ nexthop->resolved = NULL;
+ re->nexthop_mtu = 0;
+
+ /*
+ * If the kernel has sent us a route, then
+ * by golly gee whiz it's a good route.
+ */
+ if (re->type == ZEBRA_ROUTE_KERNEL || re->type == ZEBRA_ROUTE_SYSTEM)
+ return 1;
+
+ /*
+ * Check to see if we should trust the passed in information
+ * for UNNUMBERED interfaces as that we won't find the GW
+ * address in the routing table.
+ * This check should suffice to handle IPv4 or IPv6 routes
+ * sourced from EVPN routes which are installed with the
+ * next hop as the remote VTEP IP.
+ */
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) {
+ ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id);
+ if (!ifp) {
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug(
+ "\t%s: Onlink and interface: %u[%u] does not exist",
+ __PRETTY_FUNCTION__, nexthop->ifindex,
+ nexthop->vrf_id);
+ return 0;
+ }
+ if (connected_is_unnumbered(ifp)) {
+ if (if_is_operative(ifp))
+ return 1;
+ else {
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug(
+ "\t%s: Onlink and interface %s is not operative",
+ __PRETTY_FUNCTION__, ifp->name);
+ return 0;
+ }
+ }
+ if (!if_is_operative(ifp)) {
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug(
+ "\t%s: Interface %s is not unnumbered",
+ __PRETTY_FUNCTION__, ifp->name);
+ return 0;
+ }
+ }
+
+ /* Make lookup prefix. */
+ memset(&p, 0, sizeof(struct prefix));
+ switch (afi) {
+ case AFI_IP:
+ p.family = AF_INET;
+ p.prefixlen = IPV4_MAX_PREFIXLEN;
+ p.u.prefix4 = nexthop->gate.ipv4;
+ break;
+ case AFI_IP6:
+ p.family = AF_INET6;
+ p.prefixlen = IPV6_MAX_PREFIXLEN;
+ p.u.prefix6 = nexthop->gate.ipv6;
+ break;
+ default:
+ assert(afi != AFI_IP && afi != AFI_IP6);
+ break;
+ }
+ /* Lookup table. */
+ table = zebra_vrf_table(afi, SAFI_UNICAST, nexthop->vrf_id);
+ if (!table) {
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug("\t%s: Table not found",
+ __PRETTY_FUNCTION__);
+ return 0;
+ }
+
+ rn = route_node_match(table, (struct prefix *)&p);
+ while (rn) {
+ route_unlock_node(rn);
+
+ /* Lookup should halt if we've matched against ourselves ('top',
+ * if specified) - i.e., we cannot have a nexthop NH1 is
+ * resolved by a route NH1. The exception is if the route is a
+ * host route.
+ */
+ if (top && rn == top)
+ if (((afi == AFI_IP) && (rn->p.prefixlen != 32))
+ || ((afi == AFI_IP6) && (rn->p.prefixlen != 128))) {
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug(
+ "\t%s: Matched against ourself and prefix length is not max bit length",
+ __PRETTY_FUNCTION__);
+ return 0;
+ }
+
+ /* Pick up selected route. */
+ /* However, do not resolve over default route unless explicitly
+ * allowed. */
+ if (is_default_prefix(&rn->p)
+ && !rnh_resolve_via_default(p.family)) {
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug(
+ "\t:%s: Resolved against default route",
+ __PRETTY_FUNCTION__);
+ return 0;
+ }
+
+ dest = rib_dest_from_rnode(rn);
+ if (dest && dest->selected_fib
+ && !CHECK_FLAG(dest->selected_fib->status,
+ ROUTE_ENTRY_REMOVED)
+ && dest->selected_fib->type != ZEBRA_ROUTE_TABLE)
+ match = dest->selected_fib;
+
+ /* If there is no selected route or matched route is EGP, go up
+ tree. */
+ if (!match) {
+ do {
+ rn = rn->parent;
+ } while (rn && rn->info == NULL);
+ if (rn)
+ route_lock_node(rn);
+
+ continue;
+ }
+
+ if (match->type == ZEBRA_ROUTE_CONNECT) {
+ /* Directly point connected route. */
+ newhop = match->ng.nexthop;
+ if (newhop) {
+ if (nexthop->type == NEXTHOP_TYPE_IPV4
+ || nexthop->type == NEXTHOP_TYPE_IPV6)
+ nexthop->ifindex = newhop->ifindex;
+ }
+ return 1;
+ } else if (CHECK_FLAG(re->flags, ZEBRA_FLAG_ALLOW_RECURSION)) {
+ resolved = 0;
+ for (ALL_NEXTHOPS(match->ng, newhop)) {
+ if (!CHECK_FLAG(match->status,
+ ROUTE_ENTRY_INSTALLED))
+ continue;
+ if (CHECK_FLAG(newhop->flags,
+ NEXTHOP_FLAG_RECURSIVE))
+ continue;
+
+ SET_FLAG(nexthop->flags,
+ NEXTHOP_FLAG_RECURSIVE);
+ SET_FLAG(re->status,
+ ROUTE_ENTRY_NEXTHOPS_CHANGED);
+ nexthop_set_resolved(afi, newhop, nexthop);
+ resolved = 1;
+ }
+ if (resolved)
+ re->nexthop_mtu = match->mtu;
+ if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug("\t%s: Recursion failed to find",
+ __PRETTY_FUNCTION__);
+ return resolved;
+ } else if (re->type == ZEBRA_ROUTE_STATIC) {
+ resolved = 0;
+ for (ALL_NEXTHOPS(match->ng, newhop)) {
+ if (!CHECK_FLAG(match->status,
+ ROUTE_ENTRY_INSTALLED))
+ continue;
+ if (CHECK_FLAG(newhop->flags,
+ NEXTHOP_FLAG_RECURSIVE))
+ continue;
+
+ SET_FLAG(nexthop->flags,
+ NEXTHOP_FLAG_RECURSIVE);
+ nexthop_set_resolved(afi, newhop, nexthop);
+ resolved = 1;
+ }
+ if (resolved)
+ re->nexthop_mtu = match->mtu;
+
+ if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug(
+ "\t%s: Static route unable to resolve",
+ __PRETTY_FUNCTION__);
+ return resolved;
+ } else {
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED) {
+ zlog_debug(
+ "\t%s: Route Type %s has not turned on recursion",
+ __PRETTY_FUNCTION__,
+ zebra_route_string(re->type));
+ if (re->type == ZEBRA_ROUTE_BGP
+ && !CHECK_FLAG(re->flags, ZEBRA_FLAG_IBGP))
+ zlog_debug(
+ "\tEBGP: see \"disable-ebgp-connected-route-check\" or \"disable-connected-check\"");
+ }
+ return 0;
+ }
+ }
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug("\t%s: Nexthop did not lookup in table",
+ __PRETTY_FUNCTION__);
+ return 0;
+}
+
+/* This function verifies reachability of one given nexthop, which can be
+ * numbered or unnumbered, IPv4 or IPv6. The result is unconditionally stored
+ * in nexthop->flags field. The nexthop->ifindex will be updated
+ * appropriately as well. An existing route map can turn
+ * (otherwise active) nexthop into inactive, but not vice versa.
+ *
+ * The return value is the final value of 'ACTIVE' flag.
+ */
+static unsigned nexthop_active_check(struct route_node *rn,
+ struct route_entry *re,
+ struct nexthop *nexthop)
+{
+ struct interface *ifp;
+ route_map_result_t ret = RMAP_MATCH;
+ int family;
+ char buf[SRCDEST2STR_BUFFER];
+ const struct prefix *p, *src_p;
+ struct zebra_vrf *zvrf;
+
+ srcdest_rnode_prefixes(rn, &p, &src_p);
+
+ if (rn->p.family == AF_INET)
+ family = AFI_IP;
+ else if (rn->p.family == AF_INET6)
+ family = AFI_IP6;
+ else
+ family = 0;
+ switch (nexthop->type) {
+ case NEXTHOP_TYPE_IFINDEX:
+ ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id);
+ if (ifp && if_is_operative(ifp))
+ SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ else
+ UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ break;
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ family = AFI_IP;
+ if (nexthop_active(AFI_IP, re, nexthop, rn))
+ SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ else
+ UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ break;
+ case NEXTHOP_TYPE_IPV6:
+ family = AFI_IP6;
+ if (nexthop_active(AFI_IP6, re, nexthop, rn))
+ SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ else
+ UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ break;
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ /* RFC 5549, v4 prefix with v6 NH */
+ if (rn->p.family != AF_INET)
+ family = AFI_IP6;
+ if (IN6_IS_ADDR_LINKLOCAL(&nexthop->gate.ipv6)) {
+ ifp = if_lookup_by_index(nexthop->ifindex,
+ nexthop->vrf_id);
+ if (ifp && if_is_operative(ifp))
+ SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ else
+ UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ } else {
+ if (nexthop_active(AFI_IP6, re, nexthop, rn))
+ SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ else
+ UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ }
+ break;
+ case NEXTHOP_TYPE_BLACKHOLE:
+ SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ break;
+ default:
+ break;
+ }
+ if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) {
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug("\t%s: Unable to find a active nexthop",
+ __PRETTY_FUNCTION__);
+ return 0;
+ }
+
+ /* XXX: What exactly do those checks do? Do we support
+ * e.g. IPv4 routes with IPv6 nexthops or vice versa?
+ */
+ if (RIB_SYSTEM_ROUTE(re) || (family == AFI_IP && p->family != AF_INET)
+ || (family == AFI_IP6 && p->family != AF_INET6))
+ return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+
+ /* The original code didn't determine the family correctly
+ * e.g. for NEXTHOP_TYPE_IFINDEX. Retrieve the correct afi
+ * from the rib_table_info in those cases.
+ * Possibly it may be better to use only the rib_table_info
+ * in every case.
+ */
+ if (!family) {
+ rib_table_info_t *info;
+
+ info = srcdest_rnode_table_info(rn);
+ family = info->afi;
+ }
+
+ memset(&nexthop->rmap_src.ipv6, 0, sizeof(union g_addr));
+
+ zvrf = zebra_vrf_lookup_by_id(nexthop->vrf_id);
+ if (!zvrf) {
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug("\t%s: zvrf is NULL", __PRETTY_FUNCTION__);
+ return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ }
+
+ /* It'll get set if required inside */
+ ret = zebra_route_map_check(family, re->type, re->instance, p, nexthop,
+ zvrf, re->tag);
+ if (ret == RMAP_DENYMATCH) {
+ if (IS_ZEBRA_DEBUG_RIB) {
+ srcdest_rnode2str(rn, buf, sizeof(buf));
+ zlog_debug(
+ "%u:%s: Filtering out with NH out %s due to route map",
+ re->vrf_id, buf,
+ ifindex2ifname(nexthop->ifindex,
+ nexthop->vrf_id));
+ }
+ UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ }
+ return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+}
+
+/*
+ * Iterate over all nexthops of the given RIB entry and refresh their
+ * ACTIVE flag. re->nexthop_active_num is updated accordingly. If any
+ * nexthop is found to toggle the ACTIVE flag, the whole re structure
+ * is flagged with ROUTE_ENTRY_CHANGED.
+ *
+ * Return value is the new number of active nexthops.
+ */
+int nexthop_active_update(struct route_node *rn, struct route_entry *re)
+{
+ struct nexthop *nexthop;
+ union g_addr prev_src;
+ unsigned int prev_active, new_active;
+ ifindex_t prev_index;
+
+ re->nexthop_active_num = 0;
+ UNSET_FLAG(re->status, ROUTE_ENTRY_CHANGED);
+
+ for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) {
+ /* No protocol daemon provides src and so we're skipping
+ * tracking it */
+ prev_src = nexthop->rmap_src;
+ prev_active = CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ prev_index = nexthop->ifindex;
+ /*
+ * We need to respect the multipath_num here
+ * as that what we should be able to install from
+ * a multipath perpsective should not be a data plane
+ * decision point.
+ */
+ new_active = nexthop_active_check(rn, re, nexthop);
+ if (new_active
+ && re->nexthop_active_num >= zrouter.multipath_num) {
+ UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ new_active = 0;
+ }
+ if (new_active)
+ re->nexthop_active_num++;
+ /* Don't allow src setting on IPv6 addr for now */
+ if (prev_active != new_active || prev_index != nexthop->ifindex
+ || ((nexthop->type >= NEXTHOP_TYPE_IFINDEX
+ && nexthop->type < NEXTHOP_TYPE_IPV6)
+ && prev_src.ipv4.s_addr
+ != nexthop->rmap_src.ipv4.s_addr)
+ || ((nexthop->type >= NEXTHOP_TYPE_IPV6
+ && nexthop->type < NEXTHOP_TYPE_BLACKHOLE)
+ && !(IPV6_ADDR_SAME(&prev_src.ipv6,
+ &nexthop->rmap_src.ipv6)))
+ || CHECK_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED)) {
+ SET_FLAG(re->status, ROUTE_ENTRY_CHANGED);
+ SET_FLAG(re->status, ROUTE_ENTRY_NEXTHOPS_CHANGED);
+ }
+ }
+
+ return re->nexthop_active_num;
+}
+
diff --git a/zebra/zebra_nhg.h b/zebra/zebra_nhg.h
new file mode 100644
index 0000000000..ff2351c759
--- /dev/null
+++ b/zebra/zebra_nhg.h
@@ -0,0 +1,29 @@
+/* Zebra Nexthop Group header.
+ * Copyright (C) 2019 Cumulus Networks, Inc.
+ * Donald Sharp
+ * Stephen Worley
+ *
+ * This file is part of FRR.
+ *
+ * FRR 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.
+ *
+ * FRR 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 FRR; 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_NHG_H__
+#define __ZEBRA_NHG_H__
+
+#include "zebra/rib.h"
+
+extern int nexthop_active_update(struct route_node *rn, struct route_entry *re);
+#endif
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index b31b6a1250..600e820bc4 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -54,6 +54,7 @@
#include "zebra/zebra_vxlan.h"
#include "zebra/zapi_msg.h"
#include "zebra/zebra_dplane.h"
+#include "zebra/zebra_nhg.h"
/*
* Event, list, and mutex for delivery of dataplane results
@@ -336,298 +337,6 @@ struct nexthop *route_entry_nexthop_blackhole_add(struct route_entry *re,
return nexthop;
}
-static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop,
- struct nexthop *nexthop)
-{
- struct nexthop *resolved_hop;
-
- resolved_hop = nexthop_new();
- SET_FLAG(resolved_hop->flags, NEXTHOP_FLAG_ACTIVE);
-
- resolved_hop->vrf_id = nexthop->vrf_id;
- switch (newhop->type) {
- case NEXTHOP_TYPE_IPV4:
- case NEXTHOP_TYPE_IPV4_IFINDEX:
- /* If the resolving route specifies a gateway, use it */
- resolved_hop->type = newhop->type;
- resolved_hop->gate.ipv4 = newhop->gate.ipv4;
-
- if (newhop->ifindex) {
- resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
- resolved_hop->ifindex = newhop->ifindex;
- }
- break;
- case NEXTHOP_TYPE_IPV6:
- case NEXTHOP_TYPE_IPV6_IFINDEX:
- resolved_hop->type = newhop->type;
- resolved_hop->gate.ipv6 = newhop->gate.ipv6;
-
- if (newhop->ifindex) {
- resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
- resolved_hop->ifindex = newhop->ifindex;
- }
- break;
- case NEXTHOP_TYPE_IFINDEX:
- /* If the resolving route is an interface route,
- * it means the gateway we are looking up is connected
- * to that interface. (The actual network is _not_ onlink).
- * Therefore, the resolved route should have the original
- * gateway as nexthop as it is directly connected.
- *
- * On Linux, we have to set the onlink netlink flag because
- * otherwise, the kernel won't accept the route.
- */
- resolved_hop->flags |= NEXTHOP_FLAG_ONLINK;
- if (afi == AFI_IP) {
- resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
- resolved_hop->gate.ipv4 = nexthop->gate.ipv4;
- } else if (afi == AFI_IP6) {
- resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
- resolved_hop->gate.ipv6 = nexthop->gate.ipv6;
- }
- resolved_hop->ifindex = newhop->ifindex;
- break;
- case NEXTHOP_TYPE_BLACKHOLE:
- resolved_hop->type = NEXTHOP_TYPE_BLACKHOLE;
- resolved_hop->bh_type = nexthop->bh_type;
- break;
- }
-
- if (newhop->flags & NEXTHOP_FLAG_ONLINK)
- resolved_hop->flags |= NEXTHOP_FLAG_ONLINK;
-
- /* Copy labels of the resolved route */
- if (newhop->nh_label)
- nexthop_add_labels(resolved_hop, newhop->nh_label_type,
- newhop->nh_label->num_labels,
- &newhop->nh_label->label[0]);
-
- resolved_hop->rparent = nexthop;
- nexthop_add(&nexthop->resolved, resolved_hop);
-}
-
-/*
- * Given a nexthop we need to properly recursively resolve
- * the route. As such, do a table lookup to find and match
- * if at all possible. Set the nexthop->ifindex as appropriate
- */
-static int nexthop_active(afi_t afi, struct route_entry *re,
- struct nexthop *nexthop,
- struct route_node *top)
-{
- struct prefix p;
- struct route_table *table;
- struct route_node *rn;
- struct route_entry *match = NULL;
- int resolved;
- struct nexthop *newhop;
- struct interface *ifp;
- rib_dest_t *dest;
-
- if ((nexthop->type == NEXTHOP_TYPE_IPV4)
- || nexthop->type == NEXTHOP_TYPE_IPV6)
- nexthop->ifindex = 0;
-
- UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
- nexthops_free(nexthop->resolved);
- nexthop->resolved = NULL;
- re->nexthop_mtu = 0;
-
- /*
- * If the kernel has sent us a route, then
- * by golly gee whiz it's a good route.
- */
- if (re->type == ZEBRA_ROUTE_KERNEL ||
- re->type == ZEBRA_ROUTE_SYSTEM)
- return 1;
-
- /*
- * Check to see if we should trust the passed in information
- * for UNNUMBERED interfaces as that we won't find the GW
- * address in the routing table.
- * This check should suffice to handle IPv4 or IPv6 routes
- * sourced from EVPN routes which are installed with the
- * next hop as the remote VTEP IP.
- */
- if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) {
- ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id);
- if (!ifp) {
- if (IS_ZEBRA_DEBUG_RIB_DETAILED)
- zlog_debug(
- "\t%s: Onlink and interface: %u[%u] does not exist",
- __PRETTY_FUNCTION__, nexthop->ifindex,
- nexthop->vrf_id);
- return 0;
- }
- if (connected_is_unnumbered(ifp)) {
- if (if_is_operative(ifp))
- return 1;
- else {
- if (IS_ZEBRA_DEBUG_RIB_DETAILED)
- zlog_debug(
- "\t%s: Onlink and interface %s is not operative",
- __PRETTY_FUNCTION__, ifp->name);
- return 0;
- }
- }
- if (!if_is_operative(ifp)) {
- if (IS_ZEBRA_DEBUG_RIB_DETAILED)
- zlog_debug(
- "\t%s: Interface %s is not unnumbered",
- __PRETTY_FUNCTION__, ifp->name);
- return 0;
- }
- }
-
- /* Make lookup prefix. */
- memset(&p, 0, sizeof(struct prefix));
- switch (afi) {
- case AFI_IP:
- p.family = AF_INET;
- p.prefixlen = IPV4_MAX_PREFIXLEN;
- p.u.prefix4 = nexthop->gate.ipv4;
- break;
- case AFI_IP6:
- p.family = AF_INET6;
- p.prefixlen = IPV6_MAX_PREFIXLEN;
- p.u.prefix6 = nexthop->gate.ipv6;
- break;
- default:
- assert(afi != AFI_IP && afi != AFI_IP6);
- break;
- }
- /* Lookup table. */
- table = zebra_vrf_table(afi, SAFI_UNICAST, nexthop->vrf_id);
- if (!table) {
- if (IS_ZEBRA_DEBUG_RIB_DETAILED)
- zlog_debug("\t%s: Table not found",
- __PRETTY_FUNCTION__);
- return 0;
- }
-
- rn = route_node_match(table, (struct prefix *)&p);
- while (rn) {
- route_unlock_node(rn);
-
- /* Lookup should halt if we've matched against ourselves ('top',
- * if specified) - i.e., we cannot have a nexthop NH1 is
- * resolved by a route NH1. The exception is if the route is a
- * host route.
- */
- if (top && rn == top)
- if (((afi == AFI_IP) && (rn->p.prefixlen != 32))
- || ((afi == AFI_IP6) && (rn->p.prefixlen != 128))) {
- if (IS_ZEBRA_DEBUG_RIB_DETAILED)
- zlog_debug(
- "\t%s: Matched against ourself and prefix length is not max bit length",
- __PRETTY_FUNCTION__);
- return 0;
- }
-
- /* Pick up selected route. */
- /* However, do not resolve over default route unless explicitly
- * allowed. */
- if (is_default_prefix(&rn->p)
- && !rnh_resolve_via_default(p.family)) {
- if (IS_ZEBRA_DEBUG_RIB_DETAILED)
- zlog_debug(
- "\t:%s: Resolved against default route",
- __PRETTY_FUNCTION__);
- return 0;
- }
-
- dest = rib_dest_from_rnode(rn);
- if (dest && dest->selected_fib
- && !CHECK_FLAG(dest->selected_fib->status,
- ROUTE_ENTRY_REMOVED)
- && dest->selected_fib->type != ZEBRA_ROUTE_TABLE)
- match = dest->selected_fib;
-
- /* If there is no selected route or matched route is EGP, go up
- tree. */
- if (!match) {
- do {
- rn = rn->parent;
- } while (rn && rn->info == NULL);
- if (rn)
- route_lock_node(rn);
-
- continue;
- }
-
- if (match->type == ZEBRA_ROUTE_CONNECT) {
- /* Directly point connected route. */
- newhop = match->ng.nexthop;
- if (newhop) {
- if (nexthop->type == NEXTHOP_TYPE_IPV4
- || nexthop->type == NEXTHOP_TYPE_IPV6)
- nexthop->ifindex = newhop->ifindex;
- }
- return 1;
- } else if (CHECK_FLAG(re->flags, ZEBRA_FLAG_ALLOW_RECURSION)) {
- resolved = 0;
- for (ALL_NEXTHOPS(match->ng, newhop)) {
- if (!CHECK_FLAG(match->status,
- ROUTE_ENTRY_INSTALLED))
- continue;
- if (CHECK_FLAG(newhop->flags,
- NEXTHOP_FLAG_RECURSIVE))
- continue;
-
- SET_FLAG(nexthop->flags,
- NEXTHOP_FLAG_RECURSIVE);
- SET_FLAG(re->status,
- ROUTE_ENTRY_NEXTHOPS_CHANGED);
- nexthop_set_resolved(afi, newhop, nexthop);
- resolved = 1;
- }
- if (resolved)
- re->nexthop_mtu = match->mtu;
- if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED)
- zlog_debug("\t%s: Recursion failed to find",
- __PRETTY_FUNCTION__);
- return resolved;
- } else if (re->type == ZEBRA_ROUTE_STATIC) {
- resolved = 0;
- for (ALL_NEXTHOPS(match->ng, newhop)) {
- if (!CHECK_FLAG(match->status,
- ROUTE_ENTRY_INSTALLED))
- continue;
- if (CHECK_FLAG(newhop->flags,
- NEXTHOP_FLAG_RECURSIVE))
- continue;
-
- SET_FLAG(nexthop->flags,
- NEXTHOP_FLAG_RECURSIVE);
- nexthop_set_resolved(afi, newhop, nexthop);
- resolved = 1;
- }
- if (resolved)
- re->nexthop_mtu = match->mtu;
-
- if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED)
- zlog_debug(
- "\t%s: Static route unable to resolve",
- __PRETTY_FUNCTION__);
- return resolved;
- } else {
- if (IS_ZEBRA_DEBUG_RIB_DETAILED) {
- zlog_debug("\t%s: Route Type %s has not turned on recursion",
- __PRETTY_FUNCTION__,
- zebra_route_string(re->type));
- if (re->type == ZEBRA_ROUTE_BGP &&
- !CHECK_FLAG(re->flags, ZEBRA_FLAG_IBGP))
- zlog_debug("\tEBGP: see \"disable-ebgp-connected-route-check\" or \"disable-connected-check\"");
- }
- return 0;
- }
- }
- if (IS_ZEBRA_DEBUG_RIB_DETAILED)
- zlog_debug("\t%s: Nexthop did not lookup in table",
- __PRETTY_FUNCTION__);
- return 0;
-}
-
struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t vrf_id,
union g_addr *addr, struct route_node **rn_out)
{
@@ -798,190 +507,6 @@ struct route_entry *rib_lookup_ipv4(struct prefix_ipv4 *p, vrf_id_t vrf_id)
return NULL;
}
-/* This function verifies reachability of one given nexthop, which can be
- * numbered or unnumbered, IPv4 or IPv6. The result is unconditionally stored
- * in nexthop->flags field. The nexthop->ifindex will be updated
- * appropriately as well. An existing route map can turn
- * (otherwise active) nexthop into inactive, but not vice versa.
- *
- * The return value is the final value of 'ACTIVE' flag.
- */
-static unsigned nexthop_active_check(struct route_node *rn,
- struct route_entry *re,
- struct nexthop *nexthop)
-{
- struct interface *ifp;
- route_map_result_t ret = RMAP_MATCH;
- int family;
- char buf[SRCDEST2STR_BUFFER];
- const struct prefix *p, *src_p;
- struct zebra_vrf *zvrf;
-
- srcdest_rnode_prefixes(rn, &p, &src_p);
-
- if (rn->p.family == AF_INET)
- family = AFI_IP;
- else if (rn->p.family == AF_INET6)
- family = AFI_IP6;
- else
- family = 0;
- switch (nexthop->type) {
- case NEXTHOP_TYPE_IFINDEX:
- ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id);
- if (ifp && if_is_operative(ifp))
- SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- else
- UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- break;
- case NEXTHOP_TYPE_IPV4:
- case NEXTHOP_TYPE_IPV4_IFINDEX:
- family = AFI_IP;
- if (nexthop_active(AFI_IP, re, nexthop, rn))
- SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- else
- UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- break;
- case NEXTHOP_TYPE_IPV6:
- family = AFI_IP6;
- if (nexthop_active(AFI_IP6, re, nexthop, rn))
- SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- else
- UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- break;
- case NEXTHOP_TYPE_IPV6_IFINDEX:
- /* RFC 5549, v4 prefix with v6 NH */
- if (rn->p.family != AF_INET)
- family = AFI_IP6;
- if (IN6_IS_ADDR_LINKLOCAL(&nexthop->gate.ipv6)) {
- ifp = if_lookup_by_index(nexthop->ifindex,
- nexthop->vrf_id);
- if (ifp && if_is_operative(ifp))
- SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- else
- UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- } else {
- if (nexthop_active(AFI_IP6, re, nexthop, rn))
- SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- else
- UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- }
- break;
- case NEXTHOP_TYPE_BLACKHOLE:
- SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- break;
- default:
- break;
- }
- if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) {
- if (IS_ZEBRA_DEBUG_RIB_DETAILED)
- zlog_debug("\t%s: Unable to find a active nexthop",
- __PRETTY_FUNCTION__);
- return 0;
- }
-
- /* XXX: What exactly do those checks do? Do we support
- * e.g. IPv4 routes with IPv6 nexthops or vice versa?
- */
- if (RIB_SYSTEM_ROUTE(re) || (family == AFI_IP && p->family != AF_INET)
- || (family == AFI_IP6 && p->family != AF_INET6))
- return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
-
- /* The original code didn't determine the family correctly
- * e.g. for NEXTHOP_TYPE_IFINDEX. Retrieve the correct afi
- * from the rib_table_info in those cases.
- * Possibly it may be better to use only the rib_table_info
- * in every case.
- */
- if (!family) {
- rib_table_info_t *info;
-
- info = srcdest_rnode_table_info(rn);
- family = info->afi;
- }
-
- memset(&nexthop->rmap_src.ipv6, 0, sizeof(union g_addr));
-
- zvrf = zebra_vrf_lookup_by_id(nexthop->vrf_id);
- if (!zvrf) {
- if (IS_ZEBRA_DEBUG_RIB_DETAILED)
- zlog_debug("\t%s: zvrf is NULL", __PRETTY_FUNCTION__);
- return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- }
-
- /* It'll get set if required inside */
- ret = zebra_route_map_check(family, re->type, re->instance, p,
- nexthop, zvrf, re->tag);
- if (ret == RMAP_DENYMATCH) {
- if (IS_ZEBRA_DEBUG_RIB) {
- srcdest_rnode2str(rn, buf, sizeof(buf));
- zlog_debug(
- "%u:%s: Filtering out with NH out %s due to route map",
- re->vrf_id, buf,
- ifindex2ifname(nexthop->ifindex,
- nexthop->vrf_id));
- }
- UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- }
- return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
-}
-
-/*
- * Iterate over all nexthops of the given RIB entry and refresh their
- * ACTIVE flag. re->nexthop_active_num is updated accordingly. If any
- * nexthop is found to toggle the ACTIVE flag, the whole re structure
- * is flagged with ROUTE_ENTRY_CHANGED.
- *
- * Return value is the new number of active nexthops.
- */
-static int nexthop_active_update(struct route_node *rn, struct route_entry *re)
-{
- struct nexthop *nexthop;
- union g_addr prev_src;
- unsigned int prev_active, new_active;
- ifindex_t prev_index;
-
- re->nexthop_active_num = 0;
- UNSET_FLAG(re->status, ROUTE_ENTRY_CHANGED);
-
- for (nexthop = re->ng.nexthop; nexthop; nexthop = nexthop->next) {
- /* No protocol daemon provides src and so we're skipping
- * tracking it */
- prev_src = nexthop->rmap_src;
- prev_active = CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- prev_index = nexthop->ifindex;
- /*
- * We need to respect the multipath_num here
- * as that what we should be able to install from
- * a multipath perpsective should not be a data plane
- * decision point.
- */
- new_active = nexthop_active_check(rn, re, nexthop);
- if (new_active
- && re->nexthop_active_num >= zrouter.multipath_num) {
- UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- new_active = 0;
- }
- if (new_active)
- re->nexthop_active_num++;
- /* Don't allow src setting on IPv6 addr for now */
- if (prev_active != new_active || prev_index != nexthop->ifindex
- || ((nexthop->type >= NEXTHOP_TYPE_IFINDEX
- && nexthop->type < NEXTHOP_TYPE_IPV6)
- && prev_src.ipv4.s_addr
- != nexthop->rmap_src.ipv4.s_addr)
- || ((nexthop->type >= NEXTHOP_TYPE_IPV6
- && nexthop->type < NEXTHOP_TYPE_BLACKHOLE)
- && !(IPV6_ADDR_SAME(&prev_src.ipv6,
- &nexthop->rmap_src.ipv6)))
- || CHECK_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED)) {
- SET_FLAG(re->status, ROUTE_ENTRY_CHANGED);
- SET_FLAG(re->status, ROUTE_ENTRY_NEXTHOPS_CHANGED);
- }
- }
-
- return re->nexthop_active_num;
-}
-
/*
* Is this RIB labeled-unicast? It must be of type BGP and all paths
* (nexthops) must have a label.
@@ -1062,8 +587,25 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re,
switch (ret) {
case ZEBRA_DPLANE_REQUEST_QUEUED:
SET_FLAG(re->status, ROUTE_ENTRY_QUEUED);
- if (old)
+
+ if (old) {
SET_FLAG(old->status, ROUTE_ENTRY_QUEUED);
+
+ /* Free old FIB nexthop group */
+ if (old->fib_ng.nexthop) {
+ nexthops_free(old->fib_ng.nexthop);
+ old->fib_ng.nexthop = NULL;
+ }
+
+ if (!RIB_SYSTEM_ROUTE(old)) {
+ /* Clear old route's FIB flags */
+ for (ALL_NEXTHOPS(old->ng, nexthop)) {
+ UNSET_FLAG(nexthop->flags,
+ NEXTHOP_FLAG_FIB);
+ }
+ }
+ }
+
if (zvrf)
zvrf->installs_queued++;
break;
@@ -1149,6 +691,12 @@ static void rib_uninstall(struct route_node *rn, struct route_entry *re)
dest->selected_fib = NULL;
+ /* Free FIB nexthop group, if present */
+ if (re->fib_ng.nexthop) {
+ nexthops_free(re->fib_ng.nexthop);
+ re->fib_ng.nexthop = NULL;
+ }
+
for (ALL_NEXTHOPS(re->ng, nexthop))
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
}
@@ -1841,21 +1389,239 @@ static void zebra_rib_fixup_system(struct route_node *rn)
}
/*
- * Route-update results processing after async dataplane update.
+ * Update a route from a dplane context. This consolidates common code
+ * that can be used in processing of results from FIB updates, and in
+ * async notification processing.
+ * The return is 'true' if the installed nexthops changed; 'false' otherwise.
*/
-static void rib_process_result(struct zebra_dplane_ctx *ctx)
+static bool rib_update_re_from_ctx(struct route_entry *re,
+ struct route_node *rn,
+ struct zebra_dplane_ctx *ctx)
+{
+ char dest_str[PREFIX_STRLEN] = "";
+ char nh_str[NEXTHOP_STRLEN];
+ struct nexthop *nexthop, *ctx_nexthop;
+ bool matched;
+ const struct nexthop_group *ctxnhg;
+ bool is_selected = false; /* Is 're' currently the selected re? */
+ bool changed_p = false; /* Change to nexthops? */
+ rib_dest_t *dest;
+
+ /* Note well: only capturing the prefix string if debug is enabled here;
+ * unconditional log messages will have to generate the string.
+ */
+ if (IS_ZEBRA_DEBUG_RIB)
+ prefix2str(&(rn->p), dest_str, sizeof(dest_str));
+
+ dest = rib_dest_from_rnode(rn);
+ if (dest)
+ is_selected = (re == dest->selected_fib);
+
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug("update_from_ctx: %u:%s: %sSELECTED",
+ re->vrf_id, dest_str, (is_selected ? "" : "NOT "));
+
+ /* Update zebra's nexthop FIB flag for each nexthop that was installed.
+ * If the installed set differs from the set requested by the rib/owner,
+ * we use the fib-specific nexthop-group to record the actual FIB
+ * status.
+ */
+
+ /*
+ * First check the fib nexthop-group, if it's present. The comparison
+ * here is quite strict: we require that the fib sets match exactly.
+ */
+ matched = false;
+ do {
+ if (re->fib_ng.nexthop == NULL)
+ break;
+
+ matched = true;
+
+ /* First check the route's fib nexthops */
+ for (ALL_NEXTHOPS(re->fib_ng, nexthop)) {
+
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+ continue;
+
+ ctx_nexthop = NULL;
+ for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx),
+ ctx_nexthop)) {
+ if (nexthop_same(ctx_nexthop, nexthop))
+ break;
+ }
+
+ if (ctx_nexthop == NULL) {
+ /* Nexthop not in the new installed set */
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED) {
+ nexthop2str(nexthop, nh_str,
+ sizeof(nh_str));
+ zlog_debug("update_from_ctx: no match for fib nh %s",
+ nh_str);
+ }
+
+ matched = false;
+ break;
+ }
+ }
+
+ if (!matched)
+ break;
+
+ /* Check the new installed set */
+ ctx_nexthop = NULL;
+ for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), ctx_nexthop)) {
+
+ if (CHECK_FLAG(ctx_nexthop->flags,
+ NEXTHOP_FLAG_RECURSIVE))
+ continue;
+
+ /* Compare with the current group's nexthops */
+ nexthop = NULL;
+ for (ALL_NEXTHOPS(re->fib_ng, nexthop)) {
+ if (nexthop_same(nexthop, ctx_nexthop))
+ break;
+ }
+
+ if (nexthop == NULL) {
+ /* Nexthop not in the old installed set */
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED) {
+ nexthop2str(ctx_nexthop, nh_str,
+ sizeof(nh_str));
+ zlog_debug("update_from_ctx: no fib match for notif nh %s",
+ nh_str);
+ }
+ matched = false;
+ break;
+ }
+ }
+
+ } while (0);
+
+ /* If the new FIB set matches the existing FIB set, we're done. */
+ if (matched) {
+ if (IS_ZEBRA_DEBUG_RIB)
+ zlog_debug("%u:%s update_from_ctx(): existing fib nhg, no change",
+ re->vrf_id, dest_str);
+ goto done;
+
+ } else if (re->fib_ng.nexthop) {
+ /*
+ * Free stale fib list and move on to check the rib nhg.
+ */
+ if (IS_ZEBRA_DEBUG_RIB)
+ zlog_debug("%u:%s update_from_ctx(): replacing fib nhg",
+ re->vrf_id, dest_str);
+ nexthops_free(re->fib_ng.nexthop);
+ re->fib_ng.nexthop = NULL;
+
+ /* Note that the installed nexthops have changed */
+ changed_p = true;
+ } else {
+ if (IS_ZEBRA_DEBUG_RIB)
+ zlog_debug("%u:%s update_from_ctx(): no fib nhg",
+ re->vrf_id, dest_str);
+ }
+
+ /*
+ * Compare with the rib nexthop group. The comparison here is different:
+ * the RIB group may be a superset of the list installed in the FIB. We
+ * walk the RIB group, looking for the 'installable' candidate
+ * nexthops, and then check those against the set
+ * that is actually installed.
+ */
+ matched = true;
+ for (ALL_NEXTHOPS(re->ng, nexthop)) {
+
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+ continue;
+
+ if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
+ continue;
+
+ /* Check for a FIB nexthop corresponding to the RIB nexthop */
+ ctx_nexthop = NULL;
+ for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), ctx_nexthop)) {
+ if (nexthop_same(ctx_nexthop, nexthop))
+ break;
+ }
+
+ /* If the FIB doesn't know about the nexthop,
+ * it's not installed
+ */
+ if (ctx_nexthop == NULL) {
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED) {
+ nexthop2str(nexthop, nh_str, sizeof(nh_str));
+ zlog_debug("update_from_ctx: no notif match for rib nh %s",
+ nh_str);
+ }
+ matched = false;
+
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB))
+ changed_p = true;
+
+ UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
+ break;
+ }
+
+ if (CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_FIB)) {
+ if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB))
+ changed_p = true;
+
+ SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
+ } else {
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB))
+ changed_p = true;
+
+ UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
+ }
+ }
+
+ /* If all nexthops were processed, we're done */
+ if (matched) {
+ if (IS_ZEBRA_DEBUG_RIB)
+ zlog_debug("%u:%s update_from_ctx(): rib nhg matched, changed '%s'",
+ re->vrf_id, dest_str,
+ (changed_p ? "true" : "false"));
+ goto done;
+ }
+
+ /* FIB nexthop set differs from the RIB set:
+ * create a fib-specific nexthop-group
+ */
+ if (IS_ZEBRA_DEBUG_RIB)
+ zlog_debug("%u:%s update_from_ctx(): changed %s, adding new fib nhg",
+ re->vrf_id, dest_str,
+ (changed_p ? "true" : "false"));
+
+ ctxnhg = dplane_ctx_get_ng(ctx);
+
+ if (ctxnhg->nexthop)
+ copy_nexthops(&(re->fib_ng.nexthop), ctxnhg->nexthop, NULL);
+ else {
+ /* Bit of a special case when the fib has _no_ installed
+ * nexthops.
+ */
+ nexthop = nexthop_new();
+ nexthop->type = NEXTHOP_TYPE_IPV4;
+ nexthop_add(&(re->fib_ng.nexthop), nexthop);
+ }
+
+done:
+ return changed_p;
+}
+
+/*
+ * Helper to locate a zebra route-node from a dplane context. This is used
+ * when processing dplane results, e.g. Note well: the route-node is returned
+ * with a ref held - route_unlock_node() must be called eventually.
+ */
+static struct route_node *
+rib_find_rn_from_ctx(const struct zebra_dplane_ctx *ctx)
{
struct route_table *table = NULL;
- struct zebra_vrf *zvrf = NULL;
struct route_node *rn = NULL;
- struct route_entry *re = NULL, *old_re = NULL, *rib;
- bool is_update = false;
- struct nexthop *nexthop, *ctx_nexthop;
- char dest_str[PREFIX_STRLEN] = "";
- enum dplane_op_e op;
- enum zebra_dplane_result status;
const struct prefix *dest_pfx, *src_pfx;
- uint32_t seq;
/* Locate rn and re(s) from ctx */
@@ -1865,7 +1631,7 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx)
dplane_ctx_get_table(ctx));
if (table == NULL) {
if (IS_ZEBRA_DEBUG_DPLANE) {
- zlog_debug("Failed to process dplane results: no table for afi %d, safi %d, vrf %u",
+ zlog_debug("Failed to find route for ctx: no table for afi %d, safi %d, vrf %u",
dplane_ctx_get_afi(ctx),
dplane_ctx_get_safi(ctx),
dplane_ctx_get_vrf(ctx));
@@ -1873,8 +1639,35 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx)
goto done;
}
- zvrf = vrf_info_lookup(dplane_ctx_get_vrf(ctx));
+ dest_pfx = dplane_ctx_get_dest(ctx);
+ src_pfx = dplane_ctx_get_src(ctx);
+
+ rn = srcdest_rnode_get(table, dest_pfx,
+ src_pfx ? (struct prefix_ipv6 *)src_pfx : NULL);
+
+done:
+ return rn;
+}
+
+
+
+/*
+ * Route-update results processing after async dataplane update.
+ */
+static void rib_process_result(struct zebra_dplane_ctx *ctx)
+{
+ struct zebra_vrf *zvrf = NULL;
+ struct route_node *rn = NULL;
+ struct route_entry *re = NULL, *old_re = NULL, *rib;
+ bool is_update = false;
+ char dest_str[PREFIX_STRLEN] = "";
+ enum dplane_op_e op;
+ enum zebra_dplane_result status;
+ const struct prefix *dest_pfx, *src_pfx;
+ uint32_t seq;
+ bool fib_changed = false;
+ zvrf = vrf_info_lookup(dplane_ctx_get_vrf(ctx));
dest_pfx = dplane_ctx_get_dest(ctx);
/* Note well: only capturing the prefix string if debug is enabled here;
@@ -1883,9 +1676,8 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx)
if (IS_ZEBRA_DEBUG_DPLANE)
prefix2str(dest_pfx, dest_str, sizeof(dest_str));
- src_pfx = dplane_ctx_get_src(ctx);
- rn = srcdest_rnode_get(table, dplane_ctx_get_dest(ctx),
- src_pfx ? (struct prefix_ipv6 *)src_pfx : NULL);
+ /* Locate rn and re(s) from ctx */
+ rn = rib_find_rn_from_ctx(ctx);
if (rn == NULL) {
if (IS_ZEBRA_DEBUG_DPLANE) {
zlog_debug("Failed to process dplane results: no route for %u:%s",
@@ -1979,34 +1771,25 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx)
UNSET_FLAG(old_re->status,
ROUTE_ENTRY_INSTALLED);
}
- /* Update zebra nexthop FIB flag for each
- * nexthop that was installed.
- */
- for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx),
- ctx_nexthop)) {
-
- if (!re)
- continue;
- for (ALL_NEXTHOPS(re->ng, nexthop)) {
- if (nexthop_same(ctx_nexthop, nexthop))
- break;
+ /* Update zebra route based on the results in
+ * the context struct.
+ */
+ if (re) {
+ fib_changed =
+ rib_update_re_from_ctx(re, rn, ctx);
+
+ if (!fib_changed) {
+ if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
+ zlog_debug("%u:%s no fib change for re",
+ dplane_ctx_get_vrf(
+ ctx),
+ dest_str);
}
- if (nexthop == NULL)
- continue;
-
- if (CHECK_FLAG(nexthop->flags,
- NEXTHOP_FLAG_RECURSIVE))
- continue;
-
- if (CHECK_FLAG(ctx_nexthop->flags,
- NEXTHOP_FLAG_FIB))
- SET_FLAG(nexthop->flags,
- NEXTHOP_FLAG_FIB);
- else
- UNSET_FLAG(nexthop->flags,
- NEXTHOP_FLAG_FIB);
+ /* Redistribute */
+ redistribute_update(dest_pfx, src_pfx,
+ re, NULL);
}
/*
@@ -2023,19 +1806,6 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx)
if (zvrf)
zvrf->installs++;
- /* Redistribute */
- /*
- * TODO -- still calling the redist api using the
- * route_entries, and there's a corner-case here:
- * if there's no client for the 'new' route, a redist
- * deleting the 'old' route will be sent. But if the
- * 'old' context info was stale, 'old_re' will be
- * NULL here and that delete will not be sent.
- */
- if (re)
- redistribute_update(dest_pfx, src_pfx,
- re, old_re);
-
/* Notify route owner */
zsend_route_notify_owner_ctx(ctx, ZAPI_ROUTE_INSTALLED);
@@ -2110,6 +1880,179 @@ done:
dplane_ctx_fini(&ctx);
}
+/*
+ * Handle notification from async dataplane: the dataplane has detected
+ * some change to a route, and notifies zebra so that the control plane
+ * can reflect that change.
+ */
+static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx)
+{
+ struct route_node *rn = NULL;
+ struct route_entry *re = NULL;
+ struct nexthop *nexthop;
+ char dest_str[PREFIX_STRLEN] = "";
+ const struct prefix *dest_pfx, *src_pfx;
+ rib_dest_t *dest;
+ bool fib_changed = false;
+ bool debug_p = IS_ZEBRA_DEBUG_DPLANE | IS_ZEBRA_DEBUG_RIB;
+ int start_count, end_count;
+ dest_pfx = dplane_ctx_get_dest(ctx);
+
+ /* Note well: only capturing the prefix string if debug is enabled here;
+ * unconditional log messages will have to generate the string.
+ */
+ if (debug_p)
+ prefix2str(dest_pfx, dest_str, sizeof(dest_str));
+
+ /* Locate rn and re(s) from ctx */
+ rn = rib_find_rn_from_ctx(ctx);
+ if (rn == NULL) {
+ if (debug_p) {
+ zlog_debug("Failed to process dplane notification: no routes for %u:%s",
+ dplane_ctx_get_vrf(ctx), dest_str);
+ }
+ goto done;
+ }
+
+ dest = rib_dest_from_rnode(rn);
+ srcdest_rnode_prefixes(rn, &dest_pfx, &src_pfx);
+
+ if (debug_p)
+ zlog_debug("%u:%s Processing dplane notif ctx %p",
+ dplane_ctx_get_vrf(ctx), dest_str, ctx);
+
+ /*
+ * Take a pass through the routes, look for matches with the context
+ * info.
+ */
+ RNODE_FOREACH_RE(rn, re) {
+ if (rib_route_match_ctx(re, ctx, false /*!update*/))
+ break;
+ }
+
+ /* No match? Nothing we can do */
+ if (re == NULL) {
+ if (debug_p)
+ zlog_debug("%u:%s Unable to process dplane notification: no entry for type %s",
+ dplane_ctx_get_vrf(ctx), dest_str,
+ zebra_route_string(
+ dplane_ctx_get_type(ctx)));
+
+ goto done;
+ }
+
+ /* Is this a notification that ... matters? We only really care about
+ * the route that is currently selected for installation.
+ */
+ if (re != dest->selected_fib) {
+ /* TODO -- don't skip processing entirely? We might like to
+ * at least report on the event.
+ */
+ if (debug_p)
+ zlog_debug("%u:%s dplane notif, but type %s not selected_fib",
+ dplane_ctx_get_vrf(ctx), dest_str,
+ zebra_route_string(
+ dplane_ctx_get_type(ctx)));
+ goto done;
+ }
+
+ /* We'll want to determine whether the installation status of the
+ * route has changed: we'll check the status before processing,
+ * and then again if there's been a change.
+ */
+ start_count = 0;
+ for (ALL_NEXTHOPS_PTR(rib_active_nhg(re), nexthop)) {
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB))
+ start_count++;
+ }
+
+ /* Update zebra's nexthop FIB flags based on the context struct's
+ * nexthops.
+ */
+ fib_changed = rib_update_re_from_ctx(re, rn, ctx);
+
+ if (!fib_changed) {
+ if (debug_p)
+ zlog_debug("%u:%s No change from dplane notification",
+ dplane_ctx_get_vrf(ctx), dest_str);
+
+ goto done;
+ }
+
+ /*
+ * Perform follow-up work if the actual status of the prefix
+ * changed.
+ */
+
+ end_count = 0;
+ for (ALL_NEXTHOPS_PTR(rib_active_nhg(re), nexthop)) {
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB))
+ end_count++;
+ }
+
+ /* Various fib transitions: changed nexthops; from installed to
+ * not-installed; or not-installed to installed.
+ */
+ if (start_count > 0 && end_count > 0) {
+
+ /* Changed nexthops - update kernel/others */
+ dplane_route_notif_update(rn, re,
+ DPLANE_OP_ROUTE_UPDATE, ctx);
+
+ } else if (start_count == 0 && end_count > 0) {
+ if (debug_p)
+ zlog_debug("%u:%s installed transition from dplane notification",
+ dplane_ctx_get_vrf(ctx), dest_str);
+
+ /* We expect this to be the selected route, so we want
+ * to tell others about this transistion.
+ */
+ SET_FLAG(re->status, ROUTE_ENTRY_INSTALLED);
+
+ /* Changed nexthops - update kernel/others */
+ dplane_route_notif_update(rn, re, DPLANE_OP_ROUTE_INSTALL, ctx);
+
+ /* Redistribute, lsp, and nht update */
+ redistribute_update(dest_pfx, src_pfx, re, NULL);
+
+ zebra_rib_evaluate_rn_nexthops(
+ rn, zebra_router_get_next_sequence());
+
+ zebra_rib_evaluate_mpls(rn);
+
+ } else if (start_count > 0 && end_count == 0) {
+ if (debug_p)
+ zlog_debug("%u:%s un-installed transition from dplane notification",
+ dplane_ctx_get_vrf(ctx), dest_str);
+
+ /* Transition from _something_ installed to _nothing_
+ * installed.
+ */
+ /* We expect this to be the selected route, so we want
+ * to tell others about this transistion.
+ */
+ UNSET_FLAG(re->status, ROUTE_ENTRY_INSTALLED);
+
+ /* Changed nexthops - update kernel/others */
+ dplane_route_notif_update(rn, re, DPLANE_OP_ROUTE_DELETE, ctx);
+
+ /* Redistribute, lsp, and nht update */
+ redistribute_delete(dest_pfx, src_pfx, re);
+
+ zebra_rib_evaluate_rn_nexthops(
+ rn, zebra_router_get_next_sequence());
+
+ zebra_rib_evaluate_mpls(rn);
+ }
+
+done:
+ if (rn)
+ route_unlock_node(rn);
+
+ /* Return context to dataplane module */
+ dplane_ctx_fini(&ctx);
+}
+
/* Take a list of route_node structs and return 1, if there was a record
* picked from it and processed by rib_process(). Don't process more,
* than one RN record; operate only in the specified sub-queue.
@@ -2133,6 +2076,7 @@ static unsigned int process_subq(struct list *subq, uint8_t qindex)
if (IS_ZEBRA_DEBUG_RIB_DETAILED) {
char buf[SRCDEST2STR_BUFFER];
+
srcdest_rnode2str(rnode, buf, sizeof(buf));
zlog_debug("%u:%s: rn %p dequeued from sub-queue %u",
zvrf ? zvrf_id(zvrf) : 0, buf, rnode, qindex);
@@ -2468,6 +2412,8 @@ void rib_unlink(struct route_node *rn, struct route_entry *re)
dest->selected_fib = NULL;
nexthops_free(re->ng.nexthop);
+ nexthops_free(re->fib_ng.nexthop);
+
XFREE(MTYPE_RE, re);
}
@@ -3331,13 +3277,40 @@ static int rib_process_dplane_results(struct thread *thread)
case DPLANE_OP_ROUTE_INSTALL:
case DPLANE_OP_ROUTE_UPDATE:
case DPLANE_OP_ROUTE_DELETE:
- rib_process_result(ctx);
+ {
+ /* Bit of special case for route updates
+ * that were generated by async notifications:
+ * we don't want to continue processing these
+ * in the rib.
+ */
+ if (dplane_ctx_get_notif_provider(ctx) == 0)
+ rib_process_result(ctx);
+ else
+ dplane_ctx_fini(&ctx);
+ }
+ break;
+
+ case DPLANE_OP_ROUTE_NOTIFY:
+ rib_process_dplane_notify(ctx);
break;
case DPLANE_OP_LSP_INSTALL:
case DPLANE_OP_LSP_UPDATE:
case DPLANE_OP_LSP_DELETE:
- zebra_mpls_lsp_dplane_result(ctx);
+ {
+ /* Bit of special case for LSP updates
+ * that were generated by async notifications:
+ * we don't want to continue processing these.
+ */
+ if (dplane_ctx_get_notif_provider(ctx) == 0)
+ zebra_mpls_lsp_dplane_result(ctx);
+ else
+ dplane_ctx_fini(&ctx);
+ }
+ break;
+
+ case DPLANE_OP_LSP_NOTIFY:
+ zebra_mpls_process_dplane_notify(ctx);
break;
case DPLANE_OP_PW_INSTALL:
diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c
index ece8f40dcf..257fb168d2 100644
--- a/zebra/zebra_vty.c
+++ b/zebra/zebra_vty.c
@@ -382,7 +382,8 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn,
}
static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
- struct route_entry *re, json_object *json)
+ struct route_entry *re, json_object *json,
+ bool is_fib)
{
struct nexthop *nexthop;
int len = 0;
@@ -394,11 +395,20 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
time_t uptime;
struct tm *tm;
rib_dest_t *dest = rib_dest_from_rnode(rn);
+ struct nexthop_group *nhg;
uptime = monotime(NULL);
uptime -= re->uptime;
tm = gmtime(&uptime);
+ /* If showing fib information, use the fib view of the
+ * nexthops.
+ */
+ if (is_fib)
+ nhg = rib_active_nhg(re);
+ else
+ nhg = &(re->ng);
+
if (json) {
json_route = json_object_new_object();
json_nexthops = json_object_new_array();
@@ -455,7 +465,7 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
json_object_string_add(json_route, "uptime", buf);
- for (ALL_NEXTHOPS(re->ng, nexthop)) {
+ for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
json_nexthop = json_object_new_object();
json_object_int_add(json_nexthop, "flags",
@@ -625,8 +635,8 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
}
/* Nexthop information. */
- for (ALL_NEXTHOPS(re->ng, nexthop)) {
- if (nexthop == re->ng.nexthop) {
+ for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
+ if (nexthop == nhg->nexthop) {
/* Prefix information. */
len = vty_out(vty, "%c", zebra_route_char(re->type));
if (re->instance)
@@ -779,7 +789,7 @@ static void vty_show_ip_route_detail_json(struct vty *vty,
*/
if (use_fib && re != dest->selected_fib)
continue;
- vty_show_ip_route(vty, rn, re, json_prefix);
+ vty_show_ip_route(vty, rn, re, json_prefix, use_fib);
}
prefix2str(&rn->p, buf, sizeof(buf));
@@ -865,7 +875,7 @@ static void do_show_route_helper(struct vty *vty, struct zebra_vrf *zvrf,
}
}
- vty_show_ip_route(vty, rn, re, json_prefix);
+ vty_show_ip_route(vty, rn, re, json_prefix, use_fib);
}
if (json_prefix) {
@@ -1552,7 +1562,7 @@ DEFUN (show_ipv6_mroute,
vty_out(vty, SHOW_ROUTE_V6_HEADER);
first = 0;
}
- vty_show_ip_route(vty, rn, re, NULL);
+ vty_show_ip_route(vty, rn, re, NULL, false);
}
return CMD_SUCCESS;
}
@@ -1584,7 +1594,7 @@ DEFUN (show_ipv6_mroute_vrf_all,
vty_out(vty, SHOW_ROUTE_V6_HEADER);
first = 0;
}
- vty_show_ip_route(vty, rn, re, NULL);
+ vty_show_ip_route(vty, rn, re, NULL, false);
}
}
return CMD_SUCCESS;