summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_route.c51
-rw-r--r--bgpd/bgp_vty.c130
-rw-r--r--bgpd/bgpd.c22
-rw-r--r--bgpd/bgpd.h5
-rw-r--r--doc/developer/grpc.rst249
-rw-r--r--doc/developer/index.rst1
-rw-r--r--doc/developer/subdir.am1
-rw-r--r--doc/developer/zebra.rst70
-rw-r--r--doc/figures/fig_dmvpn_topologies.pngbin0 -> 41860 bytes
-rw-r--r--doc/user/bgp.rst14
-rw-r--r--doc/user/grpc.rst66
-rw-r--r--doc/user/index.rst1
-rw-r--r--doc/user/nhrpd.rst203
-rw-r--r--doc/user/sharp.rst3
-rw-r--r--doc/user/subdir.am1
-rw-r--r--lib/command.h2
-rw-r--r--lib/ipaddr.h39
-rw-r--r--lib/log.c3
-rw-r--r--lib/mpls.h1
-rw-r--r--lib/nexthop.c6
-rw-r--r--lib/nexthop.h4
-rw-r--r--lib/northbound.c25
-rw-r--r--lib/northbound.h8
-rw-r--r--lib/privs.h1
-rw-r--r--lib/route_types.txt1
-rw-r--r--lib/routemap.c62
-rw-r--r--lib/routemap.h16
-rw-r--r--lib/srte.h56
-rw-r--r--lib/stream.c58
-rw-r--r--lib/stream.h8
-rw-r--r--lib/subdir.am1
-rw-r--r--lib/thread.c77
-rw-r--r--lib/yang_wrappers.c23
-rw-r--r--lib/yang_wrappers.h2
-rw-r--r--lib/zclient.c137
-rw-r--r--lib/zclient.h43
-rw-r--r--ospfd/ospf_sr.c35
-rw-r--r--pimd/pim_iface.c2
-rw-r--r--sharpd/sharp_vty.c4
-rwxr-xr-xtests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py42
-rwxr-xr-xtests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py12
-rw-r--r--tests/topotests/lib/common_config.py2
-rw-r--r--tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json18
-rw-r--r--tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json27
-rw-r--r--tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json15
-rw-r--r--tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json18
-rw-r--r--tests/topotests/zebra_netlink/__init__.py0
-rw-r--r--tests/topotests/zebra_netlink/r1/sharpd.conf0
-rw-r--r--tests/topotests/zebra_netlink/r1/v4_route.json2802
-rw-r--r--tests/topotests/zebra_netlink/r1/zebra.conf2
-rwxr-xr-xtests/topotests/zebra_netlink/test_zebra_netlink.py131
-rw-r--r--tools/gen_northbound_callbacks.c2
-rw-r--r--vrrpd/vrrp_northbound.c25
-rw-r--r--zebra/if_netlink.c55
-rw-r--r--zebra/if_netlink.h4
-rw-r--r--zebra/kernel_netlink.c405
-rw-r--r--zebra/kernel_netlink.h46
-rw-r--r--zebra/kernel_socket.c94
-rw-r--r--zebra/label_manager.c2
-rw-r--r--zebra/main.c6
-rw-r--r--zebra/rt.h12
-rw-r--r--zebra/rt_netlink.c235
-rw-r--r--zebra/rt_netlink.h20
-rw-r--r--zebra/rt_socket.c14
-rw-r--r--zebra/rule_netlink.c85
-rw-r--r--zebra/rule_netlink.h3
-rw-r--r--zebra/subdir.am2
-rw-r--r--zebra/zapi_msg.c117
-rw-r--r--zebra/zapi_msg.h4
-rw-r--r--zebra/zebra_dplane.c291
-rw-r--r--zebra/zebra_dplane.h1
-rw-r--r--zebra/zebra_mpls.c54
-rw-r--r--zebra/zebra_mpls.h13
-rw-r--r--zebra/zebra_mpls_netlink.c34
-rw-r--r--zebra/zebra_nb_config.c41
-rw-r--r--zebra/zebra_nhg.c83
-rw-r--r--zebra/zebra_pbr.h7
-rw-r--r--zebra/zebra_rib.c9
-rw-r--r--zebra/zebra_rnh.c32
-rw-r--r--zebra/zebra_rnh.h3
-rw-r--r--zebra/zebra_srte.c378
-rw-r--r--zebra/zebra_srte.h74
-rw-r--r--zebra/zebra_vty.c58
-rw-r--r--zebra/zebra_vxlan.c3
84 files changed, 6025 insertions, 687 deletions
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 80ffa18424..3a627b4486 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -3056,16 +3056,43 @@ static int bgp_maximum_prefix_restart_timer(struct thread *thread)
return 0;
}
+static uint32_t bgp_filtered_routes_count(struct peer *peer, afi_t afi,
+ safi_t safi)
+{
+ uint32_t count = 0;
+ struct bgp_dest *dest;
+ struct bgp_adj_in *ain;
+ struct bgp_table *table = peer->bgp->rib[afi][safi];
+
+ for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
+ for (ain = dest->adj_in; ain; ain = ain->next) {
+ const struct prefix *rn_p = bgp_dest_get_prefix(dest);
+ struct attr attr = {};
+
+ if (bgp_input_filter(peer, rn_p, &attr, afi, safi)
+ == FILTER_DENY)
+ count++;
+ }
+ }
+
+ return count;
+}
+
bool bgp_maximum_prefix_overflow(struct peer *peer, afi_t afi, safi_t safi,
int always)
{
iana_afi_t pkt_afi;
iana_safi_t pkt_safi;
+ uint32_t pcount = (CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_FORCE))
+ ? bgp_filtered_routes_count(peer, afi, safi)
+ + peer->pcount[afi][safi]
+ : peer->pcount[afi][safi];
if (!CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX))
return false;
- if (peer->pcount[afi][safi] > peer->pmax[afi][safi]) {
+ if (pcount > peer->pmax[afi][safi]) {
if (CHECK_FLAG(peer->af_sflags[afi][safi],
PEER_STATUS_PREFIX_LIMIT)
&& !always)
@@ -3073,8 +3100,8 @@ bool bgp_maximum_prefix_overflow(struct peer *peer, afi_t afi, safi_t safi,
zlog_info(
"%%MAXPFXEXCEED: No. of %s prefix received from %s %u exceed, limit %u",
- get_afi_safi_str(afi, safi, false), peer->host,
- peer->pcount[afi][safi], peer->pmax[afi][safi]);
+ get_afi_safi_str(afi, safi, false), peer->host, pcount,
+ peer->pmax[afi][safi]);
SET_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_PREFIX_LIMIT);
if (CHECK_FLAG(peer->af_flags[afi][safi],
@@ -3125,8 +3152,7 @@ bool bgp_maximum_prefix_overflow(struct peer *peer, afi_t afi, safi_t safi,
UNSET_FLAG(peer->af_sflags[afi][safi],
PEER_STATUS_PREFIX_LIMIT);
- if (peer->pcount[afi][safi]
- > (peer->pmax[afi][safi] * peer->pmax_threshold[afi][safi] / 100)) {
+ if (pcount > (pcount * peer->pmax_threshold[afi][safi] / 100)) {
if (CHECK_FLAG(peer->af_sflags[afi][safi],
PEER_STATUS_PREFIX_THRESHOLD)
&& !always)
@@ -3134,8 +3160,8 @@ bool bgp_maximum_prefix_overflow(struct peer *peer, afi_t afi, safi_t safi,
zlog_info(
"%%MAXPFX: No. of %s prefix received from %s reaches %u, max %u",
- get_afi_safi_str(afi, safi, false), peer->host,
- peer->pcount[afi][safi], peer->pmax[afi][safi]);
+ get_afi_safi_str(afi, safi, false), peer->host, pcount,
+ peer->pmax[afi][safi]);
SET_FLAG(peer->af_sflags[afi][safi],
PEER_STATUS_PREFIX_THRESHOLD);
} else
@@ -3566,6 +3592,12 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
attr_new = bgp_attr_intern(&new_attr);
+ /* If maximum prefix count is configured and current prefix
+ * count exeed it.
+ */
+ if (bgp_maximum_prefix_overflow(peer, afi, safi, 0))
+ return -1;
+
/* If the update is implicit withdraw. */
if (pi) {
pi->uptime = bgp_clock();
@@ -4036,11 +4068,6 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
}
#endif
- /* If maximum prefix count is configured and current prefix
- count exeed it. */
- if (bgp_maximum_prefix_overflow(peer, afi, safi, 0))
- return -1;
-
/* If this is an EVPN route, process for import. */
if (safi == SAFI_EVPN && CHECK_FLAG(new->flags, BGP_PATH_VALID))
bgp_evpn_import_route(bgp, afi, safi, p, new);
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 0268b7ec9d..b2a795b97d 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -6704,7 +6704,8 @@ static int peer_maximum_prefix_set_vty(struct vty *vty, const char *ip_str,
afi_t afi, safi_t safi,
const char *num_str,
const char *threshold_str, int warning,
- const char *restart_str)
+ const char *restart_str,
+ const char *force_str)
{
int ret;
struct peer *peer;
@@ -6728,7 +6729,7 @@ static int peer_maximum_prefix_set_vty(struct vty *vty, const char *ip_str,
restart = 0;
ret = peer_maximum_prefix_set(peer, afi, safi, max, threshold, warning,
- restart);
+ restart, force_str ? true : false);
return bgp_vty_return(vty, ret);
}
@@ -6799,172 +6800,220 @@ DEFUN(no_neighbor_maximum_prefix_out,
return CMD_SUCCESS;
}
-/* Maximum number of prefix configuration. prefix count is different
- for each peer configuration. So this configuration can be set for
+/* Maximum number of prefix configuration. Prefix count is different
+ for each peer configuration. So this configuration can be set for
each peer configuration. */
DEFUN (neighbor_maximum_prefix,
neighbor_maximum_prefix_cmd,
- "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295)",
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) [force]",
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
"Maximum number of prefix accept from this peer\n"
- "maximum no. of prefix limit\n")
+ "maximum no. of prefix limit\n"
+ "Force checking all received routes not only accepted\n")
{
int idx_peer = 1;
int idx_number = 3;
+ int idx_force = 0;
+ char *force = NULL;
+
+ if (argv_find(argv, argc, "force", &idx_force))
+ force = argv[idx_force]->arg;
+
return peer_maximum_prefix_set_vty(
vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty),
- argv[idx_number]->arg, NULL, 0, NULL);
+ argv[idx_number]->arg, NULL, 0, NULL, force);
}
ALIAS_HIDDEN(neighbor_maximum_prefix, neighbor_maximum_prefix_hidden_cmd,
- "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295)",
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) [force]",
NEIGHBOR_STR NEIGHBOR_ADDR_STR2
"Maximum number of prefix accept from this peer\n"
- "maximum no. of prefix limit\n")
+ "maximum no. of prefix limit\n"
+ "Force checking all received routes not only accepted\n")
DEFUN (neighbor_maximum_prefix_threshold,
neighbor_maximum_prefix_threshold_cmd,
- "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100)",
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) [force]",
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
"Maximum number of prefix accept from this peer\n"
"maximum no. of prefix limit\n"
- "Threshold value (%) at which to generate a warning msg\n")
+ "Threshold value (%) at which to generate a warning msg\n"
+ "Force checking all received routes not only accepted\n")
{
int idx_peer = 1;
int idx_number = 3;
int idx_number_2 = 4;
+ int idx_force = 0;
+ char *force = NULL;
+
+ if (argv_find(argv, argc, "force", &idx_force))
+ force = argv[idx_force]->arg;
+
return peer_maximum_prefix_set_vty(
vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty),
- argv[idx_number]->arg, argv[idx_number_2]->arg, 0, NULL);
+ argv[idx_number]->arg, argv[idx_number_2]->arg, 0, NULL, force);
}
ALIAS_HIDDEN(
neighbor_maximum_prefix_threshold,
neighbor_maximum_prefix_threshold_hidden_cmd,
- "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100)",
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) [force]",
NEIGHBOR_STR NEIGHBOR_ADDR_STR2
"Maximum number of prefix accept from this peer\n"
"maximum no. of prefix limit\n"
- "Threshold value (%) at which to generate a warning msg\n")
+ "Threshold value (%) at which to generate a warning msg\n"
+ "Force checking all received routes not only accepted\n")
DEFUN (neighbor_maximum_prefix_warning,
neighbor_maximum_prefix_warning_cmd,
- "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) warning-only",
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) warning-only [force]",
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
"Maximum number of prefix accept from this peer\n"
"maximum no. of prefix limit\n"
- "Only give warning message when limit is exceeded\n")
+ "Only give warning message when limit is exceeded\n"
+ "Force checking all received routes not only accepted\n")
{
int idx_peer = 1;
int idx_number = 3;
+ int idx_force = 0;
+ char *force = NULL;
+
+ if (argv_find(argv, argc, "force", &idx_force))
+ force = argv[idx_force]->arg;
+
return peer_maximum_prefix_set_vty(
vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty),
- argv[idx_number]->arg, NULL, 1, NULL);
+ argv[idx_number]->arg, NULL, 1, NULL, force);
}
ALIAS_HIDDEN(
neighbor_maximum_prefix_warning,
neighbor_maximum_prefix_warning_hidden_cmd,
- "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) warning-only",
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) warning-only [force]",
NEIGHBOR_STR NEIGHBOR_ADDR_STR2
"Maximum number of prefix accept from this peer\n"
"maximum no. of prefix limit\n"
- "Only give warning message when limit is exceeded\n")
+ "Only give warning message when limit is exceeded\n"
+ "Force checking all received routes not only accepted\n")
DEFUN (neighbor_maximum_prefix_threshold_warning,
neighbor_maximum_prefix_threshold_warning_cmd,
- "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) warning-only",
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) warning-only [force]",
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
"Maximum number of prefix accept from this peer\n"
"maximum no. of prefix limit\n"
"Threshold value (%) at which to generate a warning msg\n"
- "Only give warning message when limit is exceeded\n")
+ "Only give warning message when limit is exceeded\n"
+ "Force checking all received routes not only accepted\n")
{
int idx_peer = 1;
int idx_number = 3;
int idx_number_2 = 4;
+ int idx_force = 0;
+ char *force = NULL;
+
+ if (argv_find(argv, argc, "force", &idx_force))
+ force = argv[idx_force]->arg;
+
return peer_maximum_prefix_set_vty(
vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty),
- argv[idx_number]->arg, argv[idx_number_2]->arg, 1, NULL);
+ argv[idx_number]->arg, argv[idx_number_2]->arg, 1, NULL, force);
}
ALIAS_HIDDEN(
neighbor_maximum_prefix_threshold_warning,
neighbor_maximum_prefix_threshold_warning_hidden_cmd,
- "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) warning-only",
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) warning-only [force]",
NEIGHBOR_STR NEIGHBOR_ADDR_STR2
"Maximum number of prefix accept from this peer\n"
"maximum no. of prefix limit\n"
"Threshold value (%) at which to generate a warning msg\n"
- "Only give warning message when limit is exceeded\n")
+ "Only give warning message when limit is exceeded\n"
+ "Force checking all received routes not only accepted\n")
DEFUN (neighbor_maximum_prefix_restart,
neighbor_maximum_prefix_restart_cmd,
- "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) restart (1-65535)",
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) restart (1-65535) [force]",
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
"Maximum number of prefix accept from this peer\n"
"maximum no. of prefix limit\n"
"Restart bgp connection after limit is exceeded\n"
- "Restart interval in minutes\n")
+ "Restart interval in minutes\n"
+ "Force checking all received routes not only accepted\n")
{
int idx_peer = 1;
int idx_number = 3;
int idx_number_2 = 5;
+ int idx_force = 0;
+ char *force = NULL;
+
+ if (argv_find(argv, argc, "force", &idx_force))
+ force = argv[idx_force]->arg;
+
return peer_maximum_prefix_set_vty(
vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty),
- argv[idx_number]->arg, NULL, 0, argv[idx_number_2]->arg);
+ argv[idx_number]->arg, NULL, 0, argv[idx_number_2]->arg, force);
}
ALIAS_HIDDEN(
neighbor_maximum_prefix_restart,
neighbor_maximum_prefix_restart_hidden_cmd,
- "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) restart (1-65535)",
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) restart (1-65535) [force]",
NEIGHBOR_STR NEIGHBOR_ADDR_STR2
"Maximum number of prefix accept from this peer\n"
"maximum no. of prefix limit\n"
"Restart bgp connection after limit is exceeded\n"
- "Restart interval in minutes\n")
+ "Restart interval in minutes\n"
+ "Force checking all received routes not only accepted\n")
DEFUN (neighbor_maximum_prefix_threshold_restart,
neighbor_maximum_prefix_threshold_restart_cmd,
- "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) restart (1-65535)",
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) restart (1-65535) [force]",
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
"Maximum number of prefixes to accept from this peer\n"
"maximum no. of prefix limit\n"
"Threshold value (%) at which to generate a warning msg\n"
"Restart bgp connection after limit is exceeded\n"
- "Restart interval in minutes\n")
+ "Restart interval in minutes\n"
+ "Force checking all received routes not only accepted\n")
{
int idx_peer = 1;
int idx_number = 3;
int idx_number_2 = 4;
int idx_number_3 = 6;
+ int idx_force = 0;
+ char *force = NULL;
+
+ if (argv_find(argv, argc, "force", &idx_force))
+ force = argv[idx_force]->arg;
+
return peer_maximum_prefix_set_vty(
vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty),
argv[idx_number]->arg, argv[idx_number_2]->arg, 0,
- argv[idx_number_3]->arg);
+ argv[idx_number_3]->arg, force);
}
ALIAS_HIDDEN(
neighbor_maximum_prefix_threshold_restart,
neighbor_maximum_prefix_threshold_restart_hidden_cmd,
- "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) restart (1-65535)",
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) restart (1-65535) [force]",
NEIGHBOR_STR NEIGHBOR_ADDR_STR2
"Maximum number of prefixes to accept from this peer\n"
"maximum no. of prefix limit\n"
"Threshold value (%) at which to generate a warning msg\n"
"Restart bgp connection after limit is exceeded\n"
- "Restart interval in minutes\n")
+ "Restart interval in minutes\n"
+ "Force checking all received routes not only accepted\n")
DEFUN (no_neighbor_maximum_prefix,
no_neighbor_maximum_prefix_cmd,
- "no neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix [(1-4294967295) [(1-100)] [restart (1-65535)] [warning-only]]",
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix [(1-4294967295) [(1-100)] [restart (1-65535)] [warning-only] [force]]",
NO_STR
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
@@ -6973,7 +7022,8 @@ DEFUN (no_neighbor_maximum_prefix,
"Threshold value (%) at which to generate a warning msg\n"
"Restart bgp connection after limit is exceeded\n"
"Restart interval in minutes\n"
- "Only give warning message when limit is exceeded\n")
+ "Only give warning message when limit is exceeded\n"
+ "Force checking all received routes not only accepted\n")
{
int idx_peer = 2;
return peer_maximum_prefix_unset_vty(vty, argv[idx_peer]->arg,
@@ -6983,14 +7033,15 @@ DEFUN (no_neighbor_maximum_prefix,
ALIAS_HIDDEN(
no_neighbor_maximum_prefix, no_neighbor_maximum_prefix_hidden_cmd,
- "no neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix [(1-4294967295) [(1-100)] [restart (1-65535)] [warning-only]]",
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix [(1-4294967295) [(1-100)] [restart (1-65535)] [warning-only] [force]]",
NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
"Maximum number of prefixes to accept from this peer\n"
"maximum no. of prefix limit\n"
"Threshold value (%) at which to generate a warning msg\n"
"Restart bgp connection after limit is exceeded\n"
"Restart interval in minutes\n"
- "Only give warning message when limit is exceeded\n")
+ "Only give warning message when limit is exceeded\n"
+ "Force checking all received routes not only accepted\n")
/* "neighbor allowas-in" */
@@ -15054,6 +15105,9 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp,
if (peer->pmax_restart[afi][safi])
vty_out(vty, " restart %u",
peer->pmax_restart[afi][safi]);
+ if (peer_af_flag_check(peer, afi, safi,
+ PEER_FLAG_MAX_PREFIX_FORCE))
+ vty_out(vty, " force");
vty_out(vty, "\n");
}
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index c9e6fd2ac0..66f7fa1d00 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -3902,6 +3902,7 @@ static const struct peer_flag_action peer_af_flag_action_list[] = {
{PEER_FLAG_ORF_PREFIX_RM, 1, peer_change_reset},
{PEER_FLAG_MAX_PREFIX, 0, peer_change_none},
{PEER_FLAG_MAX_PREFIX_WARNING, 0, peer_change_none},
+ {PEER_FLAG_MAX_PREFIX_FORCE, 0, peer_change_none},
{PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED, 0, peer_change_reset_out},
{PEER_FLAG_FORCE_NEXTHOP_SELF, 1, peer_change_reset_out},
{PEER_FLAG_REMOVE_PRIVATE_AS_ALL, 1, peer_change_reset_out},
@@ -6451,13 +6452,19 @@ int peer_unsuppress_map_unset(struct peer *peer, afi_t afi, safi_t safi)
int peer_maximum_prefix_set(struct peer *peer, afi_t afi, safi_t safi,
uint32_t max, uint8_t threshold, int warning,
- uint16_t restart)
+ uint16_t restart, bool force)
{
struct peer *member;
struct listnode *node, *nnode;
/* Set flags and configuration on peer. */
peer_af_flag_set(peer, afi, safi, PEER_FLAG_MAX_PREFIX);
+
+ if (force)
+ peer_af_flag_set(peer, afi, safi, PEER_FLAG_MAX_PREFIX_FORCE);
+ else
+ peer_af_flag_unset(peer, afi, safi, PEER_FLAG_MAX_PREFIX_FORCE);
+
if (warning)
peer_af_flag_set(peer, afi, safi, PEER_FLAG_MAX_PREFIX_WARNING);
else
@@ -6492,6 +6499,14 @@ int peer_maximum_prefix_set(struct peer *peer, afi_t afi, safi_t safi,
member->pmax[afi][safi] = max;
member->pmax_threshold[afi][safi] = threshold;
member->pmax_restart[afi][safi] = restart;
+
+ if (force)
+ SET_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_FORCE);
+ else
+ UNSET_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_FORCE);
+
if (warning)
SET_FLAG(member->af_flags[afi][safi],
PEER_FLAG_MAX_PREFIX_WARNING);
@@ -6513,6 +6528,8 @@ int peer_maximum_prefix_unset(struct peer *peer, afi_t afi, safi_t safi)
if (peer_group_active(peer)) {
peer_af_flag_inherit(peer, afi, safi, PEER_FLAG_MAX_PREFIX);
peer_af_flag_inherit(peer, afi, safi,
+ PEER_FLAG_MAX_PREFIX_FORCE);
+ peer_af_flag_inherit(peer, afi, safi,
PEER_FLAG_MAX_PREFIX_WARNING);
PEER_ATTR_INHERIT(peer, peer->group, pmax[afi][safi]);
PEER_ATTR_INHERIT(peer, peer->group, pmax_threshold[afi][safi]);
@@ -6523,6 +6540,7 @@ int peer_maximum_prefix_unset(struct peer *peer, afi_t afi, safi_t safi)
/* Remove flags and configuration from peer. */
peer_af_flag_unset(peer, afi, safi, PEER_FLAG_MAX_PREFIX);
+ peer_af_flag_unset(peer, afi, safi, PEER_FLAG_MAX_PREFIX_FORCE);
peer_af_flag_unset(peer, afi, safi, PEER_FLAG_MAX_PREFIX_WARNING);
peer->pmax[afi][safi] = 0;
peer->pmax_threshold[afi][safi] = 0;
@@ -6547,6 +6565,8 @@ int peer_maximum_prefix_unset(struct peer *peer, afi_t afi, safi_t safi)
UNSET_FLAG(member->af_flags[afi][safi],
PEER_FLAG_MAX_PREFIX);
UNSET_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_FORCE);
+ UNSET_FLAG(member->af_flags[afi][safi],
PEER_FLAG_MAX_PREFIX_WARNING);
member->pmax[afi][safi] = 0;
member->pmax_threshold[afi][safi] = 0;
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 966de87830..e84b0e43b9 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -1177,6 +1177,7 @@ struct peer {
#define PEER_FLAG_ALLOWAS_IN_ORIGIN (1 << 25) /* allowas-in origin */
#define PEER_FLAG_SEND_LARGE_COMMUNITY (1 << 26) /* Send large Communities */
#define PEER_FLAG_MAX_PREFIX_OUT (1 << 27) /* outgoing maximum prefix */
+#define PEER_FLAG_MAX_PREFIX_FORCE (1 << 28) /* maximum-prefix <num> force */
enum bgp_addpath_strat addpath_type[AFI_MAX][SAFI_MAX];
@@ -1348,7 +1349,7 @@ struct peer {
/* Track if we printed the attribute in debugs */
int rcvd_attr_printed;
- /* Prefix count. */
+ /* Accepted prefix count */
uint32_t pcount[AFI_MAX][SAFI_MAX];
/* Max prefix count. */
@@ -1919,7 +1920,7 @@ extern int peer_password_unset(struct peer *);
extern int peer_unsuppress_map_unset(struct peer *, afi_t, safi_t);
extern int peer_maximum_prefix_set(struct peer *, afi_t, safi_t, uint32_t,
- uint8_t, int, uint16_t);
+ uint8_t, int, uint16_t, bool force);
extern int peer_maximum_prefix_unset(struct peer *, afi_t, safi_t);
extern int peer_clear(struct peer *, struct listnode **);
diff --git a/doc/developer/grpc.rst b/doc/developer/grpc.rst
new file mode 100644
index 0000000000..8029a08b73
--- /dev/null
+++ b/doc/developer/grpc.rst
@@ -0,0 +1,249 @@
+.. _grpc-dev:
+
+***************
+Northbound gRPC
+***************
+
+.. _grpc-languages-bindings:
+
+Programming Language Bindings
+=============================
+
+The gRPC supported programming language bindings can be found here:
+https://grpc.io/docs/languages/
+
+After picking a programming language that supports gRPC bindings, the
+next step is to generate the FRR northbound bindings. To generate the
+northbound bindings you'll need the programming language binding
+generator tools and those are language specific.
+
+Next sections will use Ruby as an example for writing scripts to use
+the northbound.
+
+
+.. _grpc-ruby-generate:
+
+Generating Ruby FRR Bindings
+----------------------------
+
+Generating FRR northbound bindings for Ruby example:
+
+::
+
+ # Install the required gems:
+ # - grpc: the gem that will talk with FRR's gRPC plugin.
+ # - grpc-tools: the gem that provides the code generator.
+ gem install grpc
+ gem install grpc-tools
+
+ # Create your project/scripts directory:
+ mkdir /tmp/frr-ruby
+
+ # Go to FRR's grpc directory:
+ cd grpc
+
+ # Generate the ruby bindings:
+ grpc_tools_ruby_protoc \
+ --ruby_out=/tmp/frr-ruby \
+ --grpc_out=/tmp/frr-ruby \
+ frr-northbound.proto
+
+
+.. _grpc-ruby-if-sample:
+
+Using Ruby To Get Interfaces State
+----------------------------------
+
+Here is a sample script to print all interfaces FRR discovered:
+
+::
+
+ require 'frr-northbound_services_pb'
+
+ # Create the connection with FRR's gRPC:
+ stub = Frr::Northbound::Stub.new('localhost:50051', :this_channel_is_insecure)
+
+ # Create a new state request to get interface state:
+ request = Frr::GetRequest.new
+ request.type = :STATE
+ request.path.push('/frr-interface:lib')
+
+ # Ask FRR.
+ response = stub.get(request)
+
+ # Print the response.
+ response.each do |result|
+ result.data.data.each_line do |line|
+ puts line
+ end
+ end
+
+
+.. note::
+
+ The generated files will assume that they are in the search path (e.g.
+ inside gem) so you'll need to either edit it to use ``require_relative`` or
+ tell Ruby where to look for them. For simplicity we'll use ``-I .`` to tell
+ it is in the current directory.
+
+
+The previous script will output something like this:
+
+::
+
+ $ cd /tmp/frr-ruby
+ # Add `-I.` so ruby finds the FRR generated file locally.
+ $ ruby -I. interface.rb
+ {
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth0",
+ "vrf": "default",
+ "state": {
+ "if-index": 2,
+ "mtu": 1500,
+ "mtu6": 1500,
+ "speed": 1000,
+ "metric": 0,
+ "phy-address": "11:22:33:44:55:66"
+ },
+ "frr-zebra:zebra": {
+ "state": {
+ "up-count": 0,
+ "down-count": 0
+ }
+ }
+ },
+ {
+ "name": "lo",
+ "vrf": "default",
+ "state": {
+ "if-index": 1,
+ "mtu": 0,
+ "mtu6": 65536,
+ "speed": 0,
+ "metric": 0,
+ "phy-address": "00:00:00:00:00:00"
+ },
+ "frr-zebra:zebra": {
+ "state": {
+ "up-count": 0,
+ "down-count": 0
+ }
+ }
+ }
+ ]
+ }
+ }
+
+
+.. _grpc-ruby-bfd-profile-sample:
+
+Using Ruby To Create BFD Profiles
+---------------------------------
+
+In this example you'll learn how to edit configuration using JSON
+and programmatic (XPath) format.
+
+::
+
+ require 'frr-northbound_services_pb'
+
+ # Create the connection with FRR's gRPC:
+ stub = Frr::Northbound::Stub.new('localhost:50051', :this_channel_is_insecure)
+
+ # Create a new candidate configuration change.
+ new_candidate = stub.create_candidate(Frr::CreateCandidateRequest.new)
+
+ # Use JSON to configure.
+ request = Frr::LoadToCandidateRequest.new
+ request.candidate_id = new_candidate.candidate_id
+ request.type = :MERGE
+ request.config = Frr::DataTree.new
+ request.config.encoding = :JSON
+ request.config.data = <<-EOJ
+ {
+ "frr-bfdd:bfdd": {
+ "bfd": {
+ "profile": [
+ {
+ "name": "test-prof",
+ "detection-multiplier": 4,
+ "required-receive-interval": 800000
+ }
+ ]
+ }
+ }
+ }
+ EOJ
+
+ # Load configuration to candidate.
+ stub.load_to_candidate(request)
+
+ # Commit candidate.
+ stub.commit(
+ Frr::CommitRequest.new(
+ candidate_id: new_candidate.candidate_id,
+ phase: :ALL,
+ comment: 'create test-prof'
+ )
+ )
+
+ #
+ # Now lets delete the previous profile and create a new one.
+ #
+
+ # Create a new candidate configuration change.
+ new_candidate = stub.create_candidate(Frr::CreateCandidateRequest.new)
+
+ # Edit the configuration candidate.
+ request = Frr::EditCandidateRequest.new
+ request.candidate_id = new_candidate.candidate_id
+
+ # Delete previously created profile.
+ request.delete.push(
+ Frr::PathValue.new(
+ path: "/frr-bfdd:bfdd/bfd/profile[name='test-prof']",
+ )
+ )
+
+ # Add new profile with two configurations.
+ request.update.push(
+ Frr::PathValue.new(
+ path: "/frr-bfdd:bfdd/bfd/profile[name='test-prof-2']/detection-multiplier",
+ value: 5.to_s
+ )
+ )
+ request.update.push(
+ Frr::PathValue.new(
+ path: "/frr-bfdd:bfdd/bfd/profile[name='test-prof-2']/desired-transmission-interval",
+ value: 900_000.to_s
+ )
+ )
+
+ # Modify the candidate.
+ stub.edit_candidate(request)
+
+ # Commit the candidate configuration.
+ stub.commit(
+ Frr::CommitRequest.new(
+ candidate_id: new_candidate.candidate_id,
+ phase: :ALL,
+ comment: 'replace test-prof with test-prof-2'
+ )
+ )
+
+
+And here is the new FRR configuration:
+
+::
+
+ $ sudo vtysh -c 'show running-config'
+ ...
+ bfd
+ profile test-prof-2
+ detect-multiplier 5
+ transmit-interval 900
+ !
+ !
diff --git a/doc/developer/index.rst b/doc/developer/index.rst
index 26b590c876..1f803b3772 100644
--- a/doc/developer/index.rst
+++ b/doc/developer/index.rst
@@ -12,6 +12,7 @@ FRRouting Developer's Guide
testing
bgpd
fpm
+ grpc
ospf
zebra
vtysh
diff --git a/doc/developer/subdir.am b/doc/developer/subdir.am
index 6dab244a03..03b4b5a3e2 100644
--- a/doc/developer/subdir.am
+++ b/doc/developer/subdir.am
@@ -28,6 +28,7 @@ dev_RSTFILES = \
doc/developer/cli.rst \
doc/developer/conf.py \
doc/developer/frr-release-procedure.rst \
+ doc/developer/grpc.rst \
doc/developer/hooks.rst \
doc/developer/include-compile.rst \
doc/developer/index.rst \
diff --git a/doc/developer/zebra.rst b/doc/developer/zebra.rst
index 6a73803d01..edd8b9ae29 100644
--- a/doc/developer/zebra.rst
+++ b/doc/developer/zebra.rst
@@ -382,3 +382,73 @@ Zebra Protocol Commands
+------------------------------------+-------+
| ZEBRA_OPAQUE_UNREGISTER | 109 |
+------------------------------------+-------+
+
+Dataplane batching
+=========================
+
+Dataplane batching is an optimization feature that reduces the processing
+time involved in the user space to kernel space transition for every message we
+want to send.
+
+Design
+-----------
+
+With our dataplane abstraction, we create a queue of dataplane context objects
+for the messages we want to send to the kernel. In a separate pthread, we
+loop over this queue and send the context objects to the appropriate
+dataplane. A batching enhancement tightly integrates with the dataplane
+context objects so they are able to be batch sent to dataplanes that support
+it.
+
+There is one main change in the dataplane code. It does not call
+kernel-dependent functions one-by-one, but instead it hands a list of work down
+to the kernel level for processing.
+
+Netlink
+^^^^^^^
+
+At the moment, this is the only dataplane that allows for batch sending
+messages to it.
+
+When messages must be sent to the kernel, they are consecutively added
+to the batch represented by the `struct nl_batch`. Context objects are firstly
+encoded to their binary representation. All the encoding functions use the same
+interface: take a context object, a buffer and a size of the buffer as an
+argument. It is important that they should handle a situation in which a message
+wouldn't fit in the buffer and return a proper error. To achieve a zero-copy
+(in the user space only) messages are encoded to the same buffer which will
+be passed to the kernel. Hence, we can theoretically hit the boundary of the
+buffer.
+
+Messages stored in the batch are sent if one of the conditions occurs:
+
+- When an encoding function returns the buffer overflow error. The context
+ object that caused this error is re-added to the new, empty batch.
+
+- When the size of the batch hits certain limit.
+
+- When the namespace of a currently being processed context object is
+ different from all the previous ones. They have to be sent through
+ distinct sockets, so the messages cannot share the same buffer.
+
+- After the last message from the list is processed.
+
+As mentioned earlier, there is a special threshold which is smaller than
+the size of the underlying buffer. It prevents the overflow error and thus
+eliminates the case, in which a message is encoded twice.
+
+The buffer used in the batching is global, since allocating that big amount of
+memory every time wouldn't be most effective. However, its size can be changed
+dynamically, using hidden vtysh command:
+``zebra kernel netlink batch-tx-buf (1-1048576) (1-1048576)``. This feature is
+only used in tests and shouldn't be utilized in any other place.
+
+For every failed message in the batch, the kernel responds with an error
+message. Error messages are kept in the same order as they were sent, so parsing the
+response is straightforward. We use the two pointer technique to match
+requests with responses and then set appropriate status of dataplane context
+objects. There is also a global receive buffer and it is assumed that whatever
+the kernel sends it will fit in this buffer. The payload of netlink error messages
+consists of a error code and the original netlink message of the request, so
+the batch response won't be bigger than the batch request increased by
+some space for the headers.
diff --git a/doc/figures/fig_dmvpn_topologies.png b/doc/figures/fig_dmvpn_topologies.png
new file mode 100644
index 0000000000..a0dcc3e67d
--- /dev/null
+++ b/doc/figures/fig_dmvpn_topologies.png
Binary files differ
diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst
index db0776adba..99dfee60eb 100644
--- a/doc/user/bgp.rst
+++ b/doc/user/bgp.rst
@@ -1222,6 +1222,11 @@ Configuring Peers
.. index:: [no] neighbor PEER ebgp-multihop
.. clicmd:: [no] neighbor PEER ebgp-multihop
+ Specifying ``ebgp-multihop`` allows sessions with eBGP neighbors to
+ establish when they are multiple hops away. When the neighbor is not
+ directly connected and this knob is not enabled, the session will not
+ establish.
+
.. index:: [no] neighbor PEER description ...
.. clicmd:: [no] neighbor PEER description ...
@@ -1306,8 +1311,8 @@ Configuring Peers
This command specifies a default `weight` value for the neighbor's routes.
-.. index:: [no] neighbor PEER maximum-prefix NUMBER
-.. clicmd:: [no] neighbor PEER maximum-prefix NUMBER
+.. index:: [no] neighbor PEER maximum-prefix NUMBER [force]
+.. clicmd:: [no] neighbor PEER maximum-prefix NUMBER [force]
Sets a maximum number of prefixes we can receive from a given peer. If this
number is exceeded, the BGP session will be destroyed.
@@ -1319,6 +1324,11 @@ Configuring Peers
granular and offers much smarter matching criterion than number of received
prefixes, making it more suited to implementing policy.
+ If _force_ is set, then ALL prefixes are counted for maximum instead of
+ accepted only. This is useful for cases where an inbound filter is applied,
+ but you want maximum-prefix to act on ALL (including filtered) prefixes. This
+ option requires `soft-reconfiguration inbound` to be enabled for the peer.
+
.. index:: [no] neighbor PEER maximum-prefix-out NUMBER
.. clicmd:: [no] neighbor PEER maximum-prefix-out NUMBER
diff --git a/doc/user/grpc.rst b/doc/user/grpc.rst
new file mode 100644
index 0000000000..d6767fc866
--- /dev/null
+++ b/doc/user/grpc.rst
@@ -0,0 +1,66 @@
+.. _grpc:
+
+***************
+Northbound gRPC
+***************
+
+.. program:: configure
+
+*gRPC* provides a combined front end to all FRR daemons using the YANG
+northbound. It is currently disabled by default due its experimental
+stage, but it can be enabled with :option:`--enable-grpc` option in the
+configure script.
+
+
+.. _grpc-features:
+
+Northbound gRPC Features
+========================
+
+* Get/set configuration using JSON/XML/XPath encondings.
+* Execute YANG RPC calls.
+* Lock/unlock configuration.
+* Create/edit/load/update/commit candidate configuration.
+* List/get transactions.
+
+
+.. note::
+
+ There is currently no support for YANG notifications.
+
+
+.. note::
+
+ You can find more information on how to code programs to interact
+ with FRR by reading the gRPC Programming Language Bindings section
+ in the `developer's documentation
+ <http://docs.frrouting.org/projects/dev-guide/en/latest/grpc.html>`_.
+
+
+.. _grpc-config:
+
+Daemon gRPC Configuration
+=========================
+
+The *gRPC* module accepts the following run time option:
+
+- ``port``: the port to listen to (defaults to ``50051``).
+
+
+.. note::
+
+ At the moment only localhost connections with no SSL/TLS are
+ supported.
+
+
+To configure FRR daemons to listen to gRPC you need to append the
+following parameter to the daemon's command line: ``-M grpc``
+(optionally ``-M grpc:PORT`` to specify listening port).
+
+To do that in production you need to edit the ``/etc/frr/daemons`` file
+so the daemons get started with the command line argument. Example:
+
+::
+
+ # other daemons...
+ bfdd_options=" --daemon -A 127.0.0.1 -M grpc"
diff --git a/doc/user/index.rst b/doc/user/index.rst
index 7bb86e563b..8ac997f8dd 100644
--- a/doc/user/index.rst
+++ b/doc/user/index.rst
@@ -23,6 +23,7 @@ Basics
basic
vtysh
+ grpc
filter
routemap
ipv6
diff --git a/doc/user/nhrpd.rst b/doc/user/nhrpd.rst
index 8d3bea7c94..9caeb0eedb 100644
--- a/doc/user/nhrpd.rst
+++ b/doc/user/nhrpd.rst
@@ -227,5 +227,206 @@ Show NHRP
Configuration Example
=====================
-FIXME
+.. figure:: ../figures/fig_dmvpn_topologies.png
+ :alt: image
+
+ image
+
+IPSec configurration example
+----------------------------
+
+This changes required on all nodes as HUB and Spokes.
+
+ipsec.conf file
+
+.. code-block:: shell
+
+ config setup
+ conn dmvpn
+ authby=secret
+ auto=add
+ keyexchange=ikev2
+ ike=aes256-aes256-sha256-modp2048
+ esp=aes256-aes256-sha256-modp2048
+ dpdaction=clear
+ dpddelay=300s
+ left=%any
+ leftid=%any
+ right=%any
+ rightid=%any
+ leftprotoport=gre
+ rightprotoport=gre
+ type=transport
+ keyingtries=%forever
+
+ipsec.secrets file
+
+.. code-block:: shell
+
+ %any : PSK "some_s3cret!"
+
+
+HUB configuration example
+-------------------------
+
+Creating gre interface
+
+.. code-block:: console
+
+ ip tunnel add gre1 mode gre key 42 ttl 64
+ ip addr add 10.0.0.254/32 dev gre1
+ ip link set gre1 up
+
+Adding iptables rules to provide possibility shortcut tunnels and connect spokes directly
+
+.. code-block:: shell
+
+ iptables -A FORWARD -i gre1 -o gre1 \\
+ -m hashlimit --hashlimit-upto 4/minute --hashlimit-burst 1 \\
+ --hashlimit-mode srcip,dstip --hashlimit-srcmask 24 --hashlimit-dstmask 24 \\
+ --hashlimit-name loglimit-0 -j NFLOG --nflog-group 1 --nflog-range 128
+
+FRR config on HUB
+
+.. code-block:: frr
+
+ nhrp nflog-group 1
+ !
+ interface gre1
+ description DMVPN Tunnel Interface
+ ip address 10.0.0.254/32
+ ip nhrp network-id 1
+ ip nhrp redirect
+ ip nhrp registration no-unique
+ ip nhrp shortcut
+ tunnel protection vici profile dmvpn
+ tunnel source eth0
+ !
+ router bgp 65000
+ bgp router-id 10.0.0.254
+ no bgp ebgp-requires-policy
+ neighbor SPOKES peer-group
+ neighbor SPOKES disable-connected-check
+ neighbor 10.0.0.1 remote-as 65001
+ neighbor 10.0.0.1 peer-group SPOKES
+ neighbor 10.0.0.2 remote-as 65002
+ neighbor 10.0.0.2 peer-group SPOKES
+ neighbor 10.0.0.3 remote-as 65003
+ neighbor 10.0.0.3 peer-group SPOKES
+ !
+ address-family ipv4 unicast
+ network 172.16.0.0/24
+ redistribute nhrp
+ exit-address-family
+
+Spoke1 configuration
+--------------------
+
+Creating gre interface
+
+.. code-block:: console
+
+ ip tunnel add gre1 mode gre key 42 ttl 64
+ ip addr add 10.0.0.1/32 dev gre1
+ ip link set gre1 up
+
+
+FRR config on Spoke1
+
+.. code-block:: frr
+
+ interface gre1
+ description DMVPN Tunnel Interface
+ ip address 10.0.0.1/32
+ ip nhrp network-id 1
+ ip nhrp nhs dynamic nbma 198.51.100.1
+ ip nhrp redirect
+ ip nhrp registration no-unique
+ ip nhrp shortcut
+ no link-detect
+ tunnel protection vici profile dmvpn
+ tunnel source eth0
+ !
+ router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 10.0.0.254 remote-as 65000
+ neighbor 10.0.0.254 disable-connected-check
+ !
+ address-family ipv4 unicast
+ network 172.16.1.0/24
+ exit-address-family
+
+
+Spoke2 configuration
+--------------------
+
+Creating gre interface
+
+.. code-block:: console
+
+ ip tunnel add gre1 mode gre key 42 ttl 64
+ ip addr add 10.0.0.1/32 dev gre1
+ ip link set gre1 up
+
+FRR config on Spoke2
+
+.. code-block:: frr
+
+ interface gre1
+ description DMVPN Tunnel Interface
+ ip address 10.0.0.2/32
+ ip nhrp network-id 1
+ ip nhrp nhs dynamic nbma 198.51.100.1
+ ip nhrp redirect
+ ip nhrp registration no-unique
+ ip nhrp shortcut
+ no link-detect
+ tunnel protection vici profile dmvpn
+ tunnel source eth0
+ !
+ router bgp 65002
+ no bgp ebgp-requires-policy
+ neighbor 10.0.0.254 remote-as 65000
+ neighbor 10.0.0.254 disable-connected-check
+ !
+ address-family ipv4 unicast
+ network 172.16.2.0/24
+ exit-address-family
+
+
+Spoke3 configuration
+--------------------
+
+Creating gre interface
+
+.. code-block:: console
+
+ ip tunnel add gre1 mode gre key 42 ttl 64
+ ip addr add 10.0.0.3/32 dev gre1
+ ip link set gre1 up
+
+FRR config on Spoke3
+
+.. code-block:: frr
+
+ interface gre1
+ description DMVPN Tunnel Interface
+ ip address 10.0.0.3/32
+ ip nhrp network-id 1
+ ip nhrp nhs dynamic nbma 198.51.100.1
+ ip nhrp redirect
+ ip nhrp registration no-unique
+ ip nhrp shortcut
+ no link-detect
+ tunnel protection vici profile dmvpn
+ tunnel source eth0
+ !
+ router bgp 65003
+ no bgp ebgp-requires-policy
+ neighbor 10.0.0.254 remote-as 65000
+ neighbor 10.0.0.254 disable-connected-check
+ !
+ address-family ipv4 unicast
+ network 172.16.3.0/24
+ exit-address-family
diff --git a/doc/user/sharp.rst b/doc/user/sharp.rst
index 76bdc48dc0..5a27be22b9 100644
--- a/doc/user/sharp.rst
+++ b/doc/user/sharp.rst
@@ -78,7 +78,8 @@ keyword. At present, no sharp commands will be preserved in the config.
The nexthop or import choice chooses the type of nexthop we are asking
zebra to watch for us. This choice affects zebra's decision on what
matches. Connected tells zebra whether or not that we want the route
- matched against to be a static or connected route. The no form of
+ matched against to be a static or connected route for the nexthop keyword,
+ for the import keyword connected means exact match. The no form of
the command obviously turns this watching off.
.. index:: sharp data nexthop
diff --git a/doc/user/subdir.am b/doc/user/subdir.am
index eb7b7ced52..dd7a193e34 100644
--- a/doc/user/subdir.am
+++ b/doc/user/subdir.am
@@ -15,6 +15,7 @@ user_RSTFILES = \
doc/user/filter.rst \
doc/user/frr-reload.rst \
doc/user/glossary.rst \
+ doc/user/grpc.rst \
doc/user/index.rst \
doc/user/installation.rst \
doc/user/ipv6.rst \
diff --git a/lib/command.h b/lib/command.h
index a7a2eaf868..e20bfe3318 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -367,6 +367,8 @@ struct cmd_node {
#define SHOW_STR "Show running system information\n"
#define IP_STR "IP information\n"
#define IPV6_STR "IPv6 information\n"
+#define SRTE_STR "SR-TE information\n"
+#define SRTE_COLOR_STR "SR-TE Color information\n"
#define NO_STR "Negate a command or set its defaults\n"
#define REDIST_STR "Redistribute information from another routing protocol\n"
#define CLEAR_STR "Reset functions\n"
diff --git a/lib/ipaddr.h b/lib/ipaddr.h
index f2b75c1306..730c7ce130 100644
--- a/lib/ipaddr.h
+++ b/lib/ipaddr.h
@@ -25,6 +25,8 @@
#include <zebra.h>
+#include "lib/log.h"
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -59,6 +61,18 @@ struct ipaddr {
#define IPADDRSZ(p) \
(IS_IPADDR_V4((p)) ? sizeof(struct in_addr) : sizeof(struct in6_addr))
+static inline int ipaddr_family(const struct ipaddr *ip)
+{
+ switch (ip->ipa_type) {
+ case IPADDR_V4:
+ return AF_INET;
+ case IPADDR_V6:
+ return AF_INET6;
+ default:
+ return AF_UNSPEC;
+ }
+}
+
static inline int str2ipaddr(const char *str, struct ipaddr *ip)
{
int ret;
@@ -131,6 +145,31 @@ static inline bool ipaddr_isset(struct ipaddr *ip)
return (0 != memcmp(&a, ip, sizeof(struct ipaddr)));
}
+/*
+ * generic ordering comparison between IP addresses
+ */
+static inline int ipaddr_cmp(const struct ipaddr *a, const struct ipaddr *b)
+{
+ uint32_t va, vb;
+ va = a->ipa_type;
+ vb = b->ipa_type;
+ if (va != vb)
+ return (va < vb) ? -1 : 1;
+ switch (a->ipa_type) {
+ case IPADDR_V4:
+ va = ntohl(a->ipaddr_v4.s_addr);
+ vb = ntohl(b->ipaddr_v4.s_addr);
+ if (va != vb)
+ return (va < vb) ? -1 : 1;
+ return 0;
+ case IPADDR_V6:
+ return memcmp((void *)&a->ipaddr_v6, (void *)&b->ipaddr_v6,
+ sizeof(a->ipaddr_v6));
+ default:
+ return 0;
+ }
+}
+
#ifdef _FRR_ATTRIBUTE_PRINTFRR
#pragma FRR printfrr_ext "%pIA" (struct ipaddr *)
#endif
diff --git a/lib/log.c b/lib/log.c
index bbce4eb793..202d6d858f 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -384,6 +384,9 @@ static const struct zebra_desc_table command_types[] = {
DESC_ENTRY(ZEBRA_MPLS_LABELS_ADD),
DESC_ENTRY(ZEBRA_MPLS_LABELS_DELETE),
DESC_ENTRY(ZEBRA_MPLS_LABELS_REPLACE),
+ DESC_ENTRY(ZEBRA_SR_POLICY_SET),
+ DESC_ENTRY(ZEBRA_SR_POLICY_DELETE),
+ DESC_ENTRY(ZEBRA_SR_POLICY_NOTIFY_STATUS),
DESC_ENTRY(ZEBRA_IPMR_ROUTE_STATS),
DESC_ENTRY(ZEBRA_LABEL_MANAGER_CONNECT),
DESC_ENTRY(ZEBRA_LABEL_MANAGER_CONNECT_ASYNC),
diff --git a/lib/mpls.h b/lib/mpls.h
index 126dbf753d..8922a36664 100644
--- a/lib/mpls.h
+++ b/lib/mpls.h
@@ -129,6 +129,7 @@ enum lsp_types_t {
ZEBRA_LSP_OSPF_SR = 4,/* OSPF Segment Routing LSP. */
ZEBRA_LSP_ISIS_SR = 5,/* IS-IS Segment Routing LSP. */
ZEBRA_LSP_SHARP = 6, /* Identifier for test protocol */
+ ZEBRA_LSP_SRTE = 7, /* SR-TE LSP */
};
/* Functions for basic label operations. */
diff --git a/lib/nexthop.c b/lib/nexthop.c
index 28d96a539c..0ea72d03e1 100644
--- a/lib/nexthop.c
+++ b/lib/nexthop.c
@@ -152,6 +152,11 @@ static int _nexthop_cmp_no_labels(const struct nexthop *next1,
break;
}
+ if (next1->srte_color < next2->srte_color)
+ return -1;
+ if (next1->srte_color > next2->srte_color)
+ return 1;
+
ret = _nexthop_source_cmp(next1, next2);
if (ret != 0)
goto done;
@@ -643,6 +648,7 @@ void nexthop_copy_no_recurse(struct nexthop *copy,
if (copy->backup_num > 0)
memcpy(copy->backup_idx, nexthop->backup_idx, copy->backup_num);
+ copy->srte_color = nexthop->srte_color;
memcpy(&copy->gate, &nexthop->gate, sizeof(nexthop->gate));
memcpy(&copy->src, &nexthop->src, sizeof(nexthop->src));
memcpy(&copy->rmap_src, &nexthop->rmap_src, sizeof(nexthop->rmap_src));
diff --git a/lib/nexthop.h b/lib/nexthop.h
index ed40cc7eed..cadcea1f41 100644
--- a/lib/nexthop.h
+++ b/lib/nexthop.h
@@ -98,6 +98,7 @@ struct nexthop {
*/
#define NEXTHOP_FLAG_RNH_FILTERED (1 << 5) /* rmap filtered, used by rnh */
#define NEXTHOP_FLAG_HAS_BACKUP (1 << 6) /* Backup nexthop index is set */
+#define NEXTHOP_FLAG_SRTE (1 << 7) /* SR-TE color used for BGP traffic */
#define NEXTHOP_IS_ACTIVE(flags) \
(CHECK_FLAG(flags, NEXTHOP_FLAG_ACTIVE) \
@@ -141,6 +142,9 @@ struct nexthop {
union {
vni_t vni;
} nh_encap;
+
+ /* SR-TE color used for matching SR-TE policies */
+ uint32_t srte_color;
};
/* Utility to append one nexthop to another. */
diff --git a/lib/northbound.c b/lib/northbound.c
index 48b8499bfc..11007e4309 100644
--- a/lib/northbound.c
+++ b/lib/northbound.c
@@ -2046,18 +2046,21 @@ void *nb_running_unset_entry(const struct lyd_node *dnode)
return entry;
}
-void *nb_running_get_entry(const struct lyd_node *dnode, const char *xpath,
- bool abort_if_not_found)
+static void *nb_running_get_entry_worker(const struct lyd_node *dnode,
+ const char *xpath,
+ bool abort_if_not_found,
+ bool rec_search)
{
const struct lyd_node *orig_dnode = dnode;
char xpath_buf[XPATH_MAXLEN];
+ bool rec_flag = true;
assert(dnode || xpath);
if (!dnode)
dnode = yang_dnode_get(running_config->dnode, xpath);
- while (dnode) {
+ while (rec_flag && dnode) {
struct nb_config_entry *config, s;
yang_dnode_get_path(dnode, s.xpath, sizeof(s.xpath));
@@ -2065,6 +2068,8 @@ void *nb_running_get_entry(const struct lyd_node *dnode, const char *xpath,
if (config)
return config->entry;
+ rec_flag = rec_search;
+
dnode = dnode->parent;
}
@@ -2078,6 +2083,20 @@ void *nb_running_get_entry(const struct lyd_node *dnode, const char *xpath,
abort();
}
+void *nb_running_get_entry(const struct lyd_node *dnode, const char *xpath,
+ bool abort_if_not_found)
+{
+ return nb_running_get_entry_worker(dnode, xpath, abort_if_not_found,
+ true);
+}
+
+void *nb_running_get_entry_non_rec(const struct lyd_node *dnode,
+ const char *xpath, bool abort_if_not_found)
+{
+ return nb_running_get_entry_worker(dnode, xpath, abort_if_not_found,
+ false);
+}
+
/* Logging functions. */
const char *nb_event_name(enum nb_event event)
{
diff --git a/lib/northbound.h b/lib/northbound.h
index bd57013f59..d5028ea7d2 100644
--- a/lib/northbound.h
+++ b/lib/northbound.h
@@ -1165,6 +1165,14 @@ extern void *nb_running_get_entry(const struct lyd_node *dnode,
const char *xpath, bool abort_if_not_found);
/*
+ * Same as 'nb_running_get_entry', but doesn't search within parent nodes
+ * recursively if an user point is not found.
+ */
+extern void *nb_running_get_entry_non_rec(const struct lyd_node *dnode,
+ const char *xpath,
+ bool abort_if_not_found);
+
+/*
* Return a human-readable string representing a northbound event.
*
* event
diff --git a/lib/privs.h b/lib/privs.h
index db5707d675..18ba8e8888 100644
--- a/lib/privs.h
+++ b/lib/privs.h
@@ -24,6 +24,7 @@
#define _ZEBRA_PRIVS_H
#include <pthread.h>
+#include <stdint.h>
#include "lib/queue.h"
#ifdef __cplusplus
diff --git a/lib/route_types.txt b/lib/route_types.txt
index 71d0a46449..b549c11cfc 100644
--- a/lib/route_types.txt
+++ b/lib/route_types.txt
@@ -85,6 +85,7 @@ ZEBRA_ROUTE_BFD, bfd, bfdd, '-', 0, 0, 0, "BFD"
ZEBRA_ROUTE_OPENFABRIC, openfabric, fabricd, 'f', 1, 1, 1, "OpenFabric"
ZEBRA_ROUTE_VRRP, vrrp, vrrpd, '-', 0, 0, 0, "VRRP"
ZEBRA_ROUTE_NHG, nhg, none, '-', 0, 0, 0, "Nexthop Group"
+ZEBRA_ROUTE_SRTE, srte, none, '-', 0, 0, 0, "SR-TE"
ZEBRA_ROUTE_ALL, wildcard, none, '-', 0, 0, 0, "-"
diff --git a/lib/routemap.c b/lib/routemap.c
index df9a6a33ea..fb70860024 100644
--- a/lib/routemap.c
+++ b/lib/routemap.c
@@ -263,6 +263,24 @@ void route_map_no_match_tag_hook(int (*func)(
rmap_match_set_hook.no_match_tag = func;
}
+/* set sr-te color */
+void route_map_set_srte_color_hook(int (*func)(struct vty *vty,
+ struct route_map_index *index,
+ const char *command,
+ const char *arg))
+{
+ rmap_match_set_hook.set_srte_color = func;
+}
+
+/* no set sr-te color */
+void route_map_no_set_srte_color_hook(int (*func)(struct vty *vty,
+ struct route_map_index *index,
+ const char *command,
+ const char *arg))
+{
+ rmap_match_set_hook.no_set_srte_color = func;
+}
+
/* set ip nexthop */
void route_map_set_ip_nexthop_hook(int (*func)(struct vty *vty,
struct route_map_index *index,
@@ -2613,6 +2631,47 @@ static unsigned int route_map_dep_data_hash_make_key(const void *p)
return string_hash_make(dep_data->rname);
}
+DEFUN (set_srte_color,
+ set_srte_color_cmd,
+ "set sr-te color [(1-4294967295)]",
+ SET_STR
+ SRTE_STR
+ SRTE_COLOR_STR
+ "Color of the SR-TE Policies to match with\n")
+{
+ VTY_DECLVAR_CONTEXT(route_map_index, index);
+ int idx = 0;
+ char *arg = argv_find(argv, argc, "(1-4294967295)", &idx)
+ ? argv[idx]->arg
+ : NULL;
+
+ if (rmap_match_set_hook.set_srte_color)
+ return rmap_match_set_hook.set_srte_color(vty, index,
+ "sr-te color", arg);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_set_srte_color,
+ no_set_srte_color_cmd,
+ "no set sr-te color [(1-4294967295)]",
+ NO_STR
+ SET_STR
+ SRTE_STR
+ SRTE_COLOR_STR
+ "Color of the SR-TE Policies to match with\n")
+{
+ VTY_DECLVAR_CONTEXT(route_map_index, index);
+ int idx = 0;
+ char *arg = argv_find(argv, argc, "(1-4294967295)", &idx)
+ ? argv[idx]->arg
+ : NULL;
+
+ if (rmap_match_set_hook.no_set_srte_color)
+ return rmap_match_set_hook.no_set_srte_color(
+ vty, index, "sr-te color", arg);
+ return CMD_SUCCESS;
+}
+
static void *route_map_dep_hash_alloc(void *p)
{
char *dep_name = (char *)p;
@@ -3237,5 +3296,8 @@ void route_map_init(void)
install_element(RMAP_NODE, &routemap_optimization_cmd);
install_element(RMAP_NODE, &no_routemap_optimization_cmd);
+ install_element(RMAP_NODE, &set_srte_color_cmd);
+ install_element(RMAP_NODE, &no_set_srte_color_cmd);
+
install_element(ENABLE_NODE, &show_route_map_pfx_tbl_cmd);
}
diff --git a/lib/routemap.h b/lib/routemap.h
index 62195b8349..64da4b87ef 100644
--- a/lib/routemap.h
+++ b/lib/routemap.h
@@ -424,6 +424,14 @@ extern void route_map_match_tag_hook(int (*func)(
extern void route_map_no_match_tag_hook(int (*func)(
struct vty *vty, struct route_map_index *index, const char *command,
const char *arg, route_map_event_t type));
+/* set sr-te color */
+extern void route_map_set_srte_color_hook(
+ int (*func)(struct vty *vty, struct route_map_index *index,
+ const char *command, const char *arg));
+/* no set sr-te color */
+extern void route_map_no_set_srte_color_hook(
+ int (*func)(struct vty *vty, struct route_map_index *index,
+ const char *command, const char *arg));
/* set ip nexthop */
extern void route_map_set_ip_nexthop_hook(
int (*func)(struct vty *vty, struct route_map_index *index,
@@ -606,6 +614,14 @@ struct route_map_match_set_hooks {
const char *command, const char *arg,
route_map_event_t type);
+ /* set sr-te color */
+ int (*set_srte_color)(struct vty *vty, struct route_map_index *index,
+ const char *command, const char *arg);
+
+ /* no set sr-te color */
+ int (*no_set_srte_color)(struct vty *vty, struct route_map_index *index,
+ const char *command, const char *arg);
+
/* set ip nexthop */
int (*set_ip_nexthop)(struct vty *vty, struct route_map_index *index,
const char *command, const char *arg);
diff --git a/lib/srte.h b/lib/srte.h
new file mode 100644
index 0000000000..d468c1cac9
--- /dev/null
+++ b/lib/srte.h
@@ -0,0 +1,56 @@
+/*
+ * SR-TE definitions
+ * Copyright 2020 NetDef Inc.
+ * Sascha Kattelmann
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _FRR_SRTE_H
+#define _FRR_SRTE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SRTE_POLICY_NAME_MAX_LENGTH 64
+
+enum zebra_sr_policy_status {
+ ZEBRA_SR_POLICY_UP = 0,
+ ZEBRA_SR_POLICY_DOWN,
+};
+
+static inline int sr_policy_compare(const struct ipaddr *a_endpoint,
+ const struct ipaddr *b_endpoint,
+ uint32_t a_color, uint32_t b_color)
+{
+ int ret;
+
+ ret = ipaddr_cmp(a_endpoint, b_endpoint);
+ if (ret < 0)
+ return -1;
+ if (ret > 0)
+ return 1;
+
+ return a_color - b_color;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FRR_SRTE_H */
diff --git a/lib/stream.c b/lib/stream.c
index d3afebbf13..768114e69b 100644
--- a/lib/stream.c
+++ b/lib/stream.c
@@ -586,6 +586,43 @@ uint32_t stream_get_ipv4(struct stream *s)
return l;
}
+bool stream_get_ipaddr(struct stream *s, struct ipaddr *ip)
+{
+ uint16_t ipa_len;
+
+ STREAM_VERIFY_SANE(s);
+
+ /* Get address type. */
+ if (STREAM_READABLE(s) < sizeof(uint16_t)) {
+ STREAM_BOUND_WARN2(s, "get ipaddr");
+ return false;
+ }
+ ip->ipa_type = stream_getw(s);
+
+ /* Get address value. */
+ switch (ip->ipa_type) {
+ case IPADDR_V4:
+ ipa_len = IPV4_MAX_BYTELEN;
+ break;
+ case IPADDR_V6:
+ ipa_len = IPV6_MAX_BYTELEN;
+ break;
+ default:
+ flog_err(EC_LIB_DEVELOPMENT,
+ "%s: unknown ip address-family: %u", __func__,
+ ip->ipa_type);
+ return false;
+ }
+ if (STREAM_READABLE(s) < ipa_len) {
+ STREAM_BOUND_WARN2(s, "get ipaddr");
+ return false;
+ }
+ memcpy(&ip->ip, s->data + s->getp, ipa_len);
+ s->getp += ipa_len;
+
+ return true;
+}
+
float stream_getf(struct stream *s)
{
union {
@@ -852,6 +889,27 @@ int stream_put_in_addr(struct stream *s, const struct in_addr *addr)
return sizeof(uint32_t);
}
+bool stream_put_ipaddr(struct stream *s, struct ipaddr *ip)
+{
+ stream_putw(s, ip->ipa_type);
+
+ switch (ip->ipa_type) {
+ case IPADDR_V4:
+ stream_put_in_addr(s, &ip->ipaddr_v4);
+ break;
+ case IPADDR_V6:
+ stream_write(s, (uint8_t *)&ip->ipaddr_v6, 16);
+ break;
+ default:
+ flog_err(EC_LIB_DEVELOPMENT,
+ "%s: unknown ip address-family: %u", __func__,
+ ip->ipa_type);
+ return false;
+ }
+
+ return true;
+}
+
/* Put in_addr at location in the stream. */
int stream_put_in_addr_at(struct stream *s, size_t putp,
const struct in_addr *addr)
diff --git a/lib/stream.h b/lib/stream.h
index 245f35db51..1250b6944d 100644
--- a/lib/stream.h
+++ b/lib/stream.h
@@ -189,6 +189,7 @@ extern int stream_putq(struct stream *, uint64_t);
extern int stream_putq_at(struct stream *, size_t, uint64_t);
extern int stream_put_ipv4(struct stream *, uint32_t);
extern int stream_put_in_addr(struct stream *s, const struct in_addr *addr);
+extern bool stream_put_ipaddr(struct stream *s, struct ipaddr *ip);
extern int stream_put_in_addr_at(struct stream *s, size_t putp,
const struct in_addr *addr);
extern int stream_put_in6_addr_at(struct stream *s, size_t putp,
@@ -219,6 +220,7 @@ extern uint64_t stream_getq(struct stream *);
extern uint64_t stream_getq_from(struct stream *, size_t);
bool stream_getq2(struct stream *s, uint64_t *q);
extern uint32_t stream_get_ipv4(struct stream *);
+extern bool stream_get_ipaddr(struct stream *s, struct ipaddr *ip);
/* IEEE-754 floats */
extern float stream_getf(struct stream *);
@@ -439,6 +441,12 @@ static inline const uint8_t *ptr_get_be32(const uint8_t *ptr, uint32_t *out)
(P) = _pval; \
} while (0)
+#define STREAM_GET_IPADDR(S, P) \
+ do { \
+ if (!stream_get_ipaddr((S), (P))) \
+ goto stream_failure; \
+ } while (0)
+
#define STREAM_GET(P, STR, SIZE) \
do { \
if (!stream_get2((P), (STR), (SIZE))) \
diff --git a/lib/subdir.am b/lib/subdir.am
index 34ad30f968..1feaa56d13 100644
--- a/lib/subdir.am
+++ b/lib/subdir.am
@@ -242,6 +242,7 @@ pkginclude_HEADERS += \
lib/sockunion.h \
lib/spf_backoff.h \
lib/srcdest_table.h \
+ lib/srte.h \
lib/stream.h \
lib/systemd.h \
lib/table.h \
diff --git a/lib/thread.c b/lib/thread.c
index 1df4eee25c..19e4827283 100644
--- a/lib/thread.c
+++ b/lib/thread.c
@@ -726,17 +726,16 @@ static void thread_free(struct thread_master *master, struct thread *thread)
static int fd_poll(struct thread_master *m, struct pollfd *pfds, nfds_t pfdsize,
nfds_t count, const struct timeval *timer_wait)
{
- /* If timer_wait is null here, that means poll() should block
- * indefinitely,
- * unless the thread_master has overridden it by setting
+ /*
+ * If timer_wait is null here, that means poll() should block
+ * indefinitely, unless the thread_master has overridden it by setting
* ->selectpoll_timeout.
+ *
* If the value is positive, it specifies the maximum number of
- * milliseconds
- * to wait. If the timeout is -1, it specifies that we should never wait
- * and
- * always return immediately even if no event is detected. If the value
- * is
- * zero, the behavior is default. */
+ * milliseconds to wait. If the timeout is -1, it specifies that
+ * we should never wait and always return immediately even if no
+ * event is detected. If the value is zero, the behavior is default.
+ */
int timeout = -1;
/* number of file descriptors with events */
@@ -860,7 +859,7 @@ funcname_thread_add_timer_timeval(struct thread_master *m,
frr_with_mutex(&m->mtx) {
if (t_ptr && *t_ptr)
- // thread is already scheduled; don't reschedule
+ /* thread is already scheduled; don't reschedule */
return NULL;
thread = thread_get(m, type, func, arg, debugargpass);
@@ -940,7 +939,7 @@ struct thread *funcname_thread_add_event(struct thread_master *m,
frr_with_mutex(&m->mtx) {
if (t_ptr && *t_ptr)
- // thread is already scheduled; don't reschedule
+ /* thread is already scheduled; don't reschedule */
break;
thread = thread_get(m, THREAD_EVENT, func, arg, debugargpass);
@@ -1047,11 +1046,12 @@ static void do_thread_cancel(struct thread_master *master)
struct cancel_req *cr;
struct listnode *ln;
for (ALL_LIST_ELEMENTS_RO(master->cancel_req, ln, cr)) {
- /* If this is an event object cancellation, linear search
- * through event
- * list deleting any events which have the specified argument.
- * We also
- * need to check every thread in the ready queue. */
+ /*
+ * If this is an event object cancellation, linear search
+ * through event list deleting any events which have the
+ * specified argument. We also need to check every thread
+ * in the ready queue.
+ */
if (cr->eventobj) {
struct thread *t;
@@ -1075,11 +1075,12 @@ static void do_thread_cancel(struct thread_master *master)
continue;
}
- /* The pointer varies depending on whether the cancellation
- * request was
- * made asynchronously or not. If it was, we need to check
- * whether the
- * thread even exists anymore before cancelling it. */
+ /*
+ * The pointer varies depending on whether the cancellation
+ * request was made asynchronously or not. If it was, we
+ * need to check whether the thread even exists anymore
+ * before cancelling it.
+ */
thread = (cr->thread) ? cr->thread : *cr->threadref;
if (!thread)
@@ -1304,18 +1305,21 @@ static void thread_process_io(struct thread_master *m, unsigned int num)
ready++;
- /* Unless someone has called thread_cancel from another pthread,
- * the only
- * thing that could have changed in m->handler.pfds while we
- * were
- * asleep is the .events field in a given pollfd. Barring
- * thread_cancel()
- * that value should be a superset of the values we have in our
- * copy, so
- * there's no need to update it. Similarily, barring deletion,
- * the fd
- * should still be a valid index into the master's pfds. */
- if (pfds[i].revents & (POLLIN | POLLHUP)) {
+ /*
+ * Unless someone has called thread_cancel from another
+ * pthread, the only thing that could have changed in
+ * m->handler.pfds while we were asleep is the .events
+ * field in a given pollfd. Barring thread_cancel() that
+ * value should be a superset of the values we have in our
+ * copy, so there's no need to update it. Similarily,
+ * barring deletion, the fd should still be a valid index
+ * into the master's pfds.
+ *
+ * We are including POLLERR here to do a READ event
+ * this is because the read should fail and the
+ * read function should handle it appropriately
+ */
+ if (pfds[i].revents & (POLLIN | POLLHUP | POLLERR)) {
thread_process_io_helper(m, m->read[pfds[i].fd], POLLIN,
pfds[i].revents, i);
}
@@ -1427,11 +1431,10 @@ struct thread *thread_fetch(struct thread_master *m, struct thread *fetch)
*
* - If there are events pending, set the poll() timeout to zero
* - If there are no events pending, but there are timers
- * pending, set the
- * timeout to the smallest remaining time on any timer
+ * pending, set the timeout to the smallest remaining time on
+ * any timer.
* - If there are neither timers nor events pending, but there
- * are file
- * descriptors pending, block indefinitely in poll()
+ * are file descriptors pending, block indefinitely in poll()
* - If nothing is pending, it's time for the application to die
*
* In every case except the last, we need to hit poll() at least
diff --git a/lib/yang_wrappers.c b/lib/yang_wrappers.c
index 4f743096ee..4c658c1bfb 100644
--- a/lib/yang_wrappers.c
+++ b/lib/yang_wrappers.c
@@ -792,6 +792,29 @@ struct yang_data *yang_data_new_empty(const char *xpath)
return yang_data_new(xpath, NULL);
}
+bool yang_dnode_get_empty(const struct lyd_node *dnode, const char *xpath_fmt,
+ ...)
+{
+ va_list ap;
+ char xpath[XPATH_MAXLEN];
+ const struct lyd_node_leaf_list *dleaf;
+
+ assert(dnode);
+
+ va_start(ap, xpath_fmt);
+ vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
+ va_end(ap);
+
+ dnode = yang_dnode_get(dnode, xpath);
+ if (dnode) {
+ dleaf = (const struct lyd_node_leaf_list *)dnode;
+ if (dleaf->value_type == LY_TYPE_EMPTY)
+ return true;
+ }
+
+ return false;
+}
+
/*
* Derived type: IP prefix.
*/
diff --git a/lib/yang_wrappers.h b/lib/yang_wrappers.h
index 335ff319d5..d781dfb1e4 100644
--- a/lib/yang_wrappers.h
+++ b/lib/yang_wrappers.h
@@ -120,6 +120,8 @@ extern void yang_get_default_string_buf(char *buf, size_t size,
/* empty */
extern struct yang_data *yang_data_new_empty(const char *xpath);
+extern bool yang_dnode_get_empty(const struct lyd_node *dnode,
+ const char *xpath_fmt, ...);
/* ip prefix */
extern void yang_str2prefix(const char *value, union prefixptr prefix);
diff --git a/lib/zclient.c b/lib/zclient.c
index 250d697bb4..6449fe15b9 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -39,6 +39,7 @@
#include "pbr.h"
#include "nexthop_group.h"
#include "lib_errors.h"
+#include "srte.h"
DEFINE_MTYPE_STATIC(LIB, ZCLIENT, "Zclient")
DEFINE_MTYPE_STATIC(LIB, REDIST_INST, "Redistribution instance IDs")
@@ -900,7 +901,7 @@ static void zapi_nexthop_group_sort(struct zapi_nexthop *nh_grp,
* Encode a single zapi nexthop
*/
int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh,
- uint32_t api_flags)
+ uint32_t api_flags, uint32_t api_message)
{
int i, ret = 0;
int nh_flags = api_nh->flags;
@@ -964,6 +965,10 @@ int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh,
stream_put(s, &(api_nh->rmac),
sizeof(struct ethaddr));
+ /* Color for Segment Routing TE. */
+ if (CHECK_FLAG(api_message, ZAPI_MESSAGE_SRTE))
+ stream_putl(s, api_nh->srte_color);
+
/* Index of backup nexthop */
if (CHECK_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) {
/* Validate backup count */
@@ -1000,7 +1005,7 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api)
stream_putw(s, api->instance);
stream_putl(s, api->flags);
- stream_putc(s, api->message);
+ stream_putl(s, api->message);
if (api->safi < SAFI_UNICAST || api->safi >= SAFI_MAX) {
flog_err(EC_LIB_ZAPI_ENCODE,
@@ -1061,7 +1066,9 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api)
return -1;
}
- if (zapi_nexthop_encode(s, api_nh, api->flags) != 0)
+ if (zapi_nexthop_encode(s, api_nh, api->flags,
+ api->message)
+ != 0)
return -1;
}
}
@@ -1105,7 +1112,9 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api)
return -1;
}
- if (zapi_nexthop_encode(s, api_nh, api->flags) != 0)
+ if (zapi_nexthop_encode(s, api_nh, api->flags,
+ api->message)
+ != 0)
return -1;
}
}
@@ -1132,7 +1141,7 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api)
* Decode a single zapi nexthop object
*/
static int zapi_nexthop_decode(struct stream *s, struct zapi_nexthop *api_nh,
- uint32_t api_flags)
+ uint32_t api_flags, uint32_t api_message)
{
int i, ret = -1;
@@ -1185,6 +1194,10 @@ static int zapi_nexthop_decode(struct stream *s, struct zapi_nexthop *api_nh,
STREAM_GET(&(api_nh->rmac), s,
sizeof(struct ethaddr));
+ /* Color for Segment Routing TE. */
+ if (CHECK_FLAG(api_message, ZAPI_MESSAGE_SRTE))
+ STREAM_GETL(s, api_nh->srte_color);
+
/* Backup nexthop index */
if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) {
STREAM_GETC(s, api_nh->backup_num);
@@ -1222,7 +1235,7 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api)
STREAM_GETW(s, api->instance);
STREAM_GETL(s, api->flags);
- STREAM_GETC(s, api->message);
+ STREAM_GETL(s, api->message);
STREAM_GETC(s, api->safi);
if (api->safi < SAFI_UNICAST || api->safi >= SAFI_MAX) {
flog_err(EC_LIB_ZAPI_ENCODE,
@@ -1297,7 +1310,9 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api)
for (i = 0; i < api->nexthop_num; i++) {
api_nh = &api->nexthops[i];
- if (zapi_nexthop_decode(s, api_nh, api->flags) != 0)
+ if (zapi_nexthop_decode(s, api_nh, api->flags,
+ api->message)
+ != 0)
return -1;
}
}
@@ -1315,7 +1330,9 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api)
for (i = 0; i < api->backup_nexthop_num; i++) {
api_nh = &api->backup_nexthops[i];
- if (zapi_nexthop_decode(s, api_nh, api->flags) != 0)
+ if (zapi_nexthop_decode(s, api_nh, api->flags,
+ api->message)
+ != 0)
return -1;
}
}
@@ -1502,6 +1519,7 @@ struct nexthop *nexthop_from_zapi_nexthop(const struct zapi_nexthop *znh)
n->vrf_id = znh->vrf_id;
n->ifindex = znh->ifindex;
n->gate = znh->gate;
+ n->srte_color = znh->srte_color;
/*
* This function currently handles labels
@@ -1619,6 +1637,7 @@ bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr)
memset(nhr, 0, sizeof(*nhr));
+ STREAM_GETL(s, nhr->message);
STREAM_GETW(s, nhr->prefix.family);
STREAM_GETC(s, nhr->prefix.prefixlen);
switch (nhr->prefix.family) {
@@ -1631,6 +1650,8 @@ bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr)
default:
break;
}
+ if (CHECK_FLAG(nhr->message, ZAPI_MESSAGE_SRTE))
+ STREAM_GETL(s, nhr->srte_color);
STREAM_GETC(s, nhr->type);
STREAM_GETW(s, nhr->instance);
@@ -1639,7 +1660,7 @@ bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr)
STREAM_GETC(s, nhr->nexthop_num);
for (i = 0; i < nhr->nexthop_num; i++) {
- if (zapi_nexthop_decode(s, &(nhr->nexthops[i]), 0) != 0)
+ if (zapi_nexthop_decode(s, &(nhr->nexthops[i]), 0, 0) != 0)
return -1;
}
@@ -2835,6 +2856,92 @@ int tm_release_table_chunk(struct zclient *zclient, uint32_t start,
return zclient_send_message(zclient);
}
+int zebra_send_sr_policy(struct zclient *zclient, int cmd,
+ struct zapi_sr_policy *zp)
+{
+ if (zapi_sr_policy_encode(zclient->obuf, cmd, zp) < 0)
+ return -1;
+ return zclient_send_message(zclient);
+}
+
+int zapi_sr_policy_encode(struct stream *s, int cmd, struct zapi_sr_policy *zp)
+{
+ struct zapi_srte_tunnel *zt = &zp->segment_list;
+
+ stream_reset(s);
+
+ zclient_create_header(s, cmd, VRF_DEFAULT);
+ stream_putl(s, zp->color);
+ stream_put_ipaddr(s, &zp->endpoint);
+ stream_write(s, &zp->name, SRTE_POLICY_NAME_MAX_LENGTH);
+
+ stream_putc(s, zt->type);
+ stream_putl(s, zt->local_label);
+
+ if (zt->label_num > MPLS_MAX_LABELS) {
+ flog_err(EC_LIB_ZAPI_ENCODE,
+ "%s: label %u: can't encode %u labels (maximum is %u)",
+ __func__, zt->local_label, zt->label_num,
+ MPLS_MAX_LABELS);
+ return -1;
+ }
+ stream_putw(s, zt->label_num);
+
+ for (int i = 0; i < zt->label_num; i++)
+ stream_putl(s, zt->labels[i]);
+
+ /* Put length at the first point of the stream. */
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ return 0;
+}
+
+int zapi_sr_policy_decode(struct stream *s, struct zapi_sr_policy *zp)
+{
+ memset(zp, 0, sizeof(*zp));
+
+ struct zapi_srte_tunnel *zt = &zp->segment_list;
+
+ STREAM_GETL(s, zp->color);
+ STREAM_GET_IPADDR(s, &zp->endpoint);
+ STREAM_GET(&zp->name, s, SRTE_POLICY_NAME_MAX_LENGTH);
+
+ /* segment list of active candidate path */
+ STREAM_GETC(s, zt->type);
+ STREAM_GETL(s, zt->local_label);
+ STREAM_GETW(s, zt->label_num);
+ if (zt->label_num > MPLS_MAX_LABELS) {
+ flog_err(EC_LIB_ZAPI_ENCODE,
+ "%s: label %u: can't decode %u labels (maximum is %u)",
+ __func__, zt->local_label, zt->label_num,
+ MPLS_MAX_LABELS);
+ return -1;
+ }
+ for (int i = 0; i < zt->label_num; i++)
+ STREAM_GETL(s, zt->labels[i]);
+
+ return 0;
+
+stream_failure:
+ return -1;
+}
+
+int zapi_sr_policy_notify_status_decode(struct stream *s,
+ struct zapi_sr_policy *zp)
+{
+ memset(zp, 0, sizeof(*zp));
+
+ STREAM_GETL(s, zp->color);
+ STREAM_GET_IPADDR(s, &zp->endpoint);
+ STREAM_GET(&zp->name, s, SRTE_POLICY_NAME_MAX_LENGTH);
+ STREAM_GETL(s, zp->status);
+
+ return 0;
+
+stream_failure:
+ return -1;
+}
+
int zebra_send_mpls_labels(struct zclient *zclient, int cmd,
struct zapi_labels *zl)
{
@@ -2874,7 +2981,7 @@ int zapi_labels_encode(struct stream *s, int cmd, struct zapi_labels *zl)
for (int i = 0; i < zl->nexthop_num; i++) {
znh = &zl->nexthops[i];
- if (zapi_nexthop_encode(s, znh, 0) < 0)
+ if (zapi_nexthop_encode(s, znh, 0, 0) < 0)
return -1;
}
@@ -2893,7 +3000,7 @@ int zapi_labels_encode(struct stream *s, int cmd, struct zapi_labels *zl)
for (int i = 0; i < zl->backup_nexthop_num; i++) {
znh = &zl->backup_nexthops[i];
- if (zapi_nexthop_encode(s, znh, 0) < 0)
+ if (zapi_nexthop_encode(s, znh, 0, 0) < 0)
return -1;
}
@@ -2969,7 +3076,7 @@ int zapi_labels_decode(struct stream *s, struct zapi_labels *zl)
for (int i = 0; i < zl->nexthop_num; i++) {
znh = &zl->nexthops[i];
- if (zapi_nexthop_decode(s, znh, 0) < 0)
+ if (zapi_nexthop_decode(s, znh, 0, 0) < 0)
return -1;
}
@@ -2990,7 +3097,7 @@ int zapi_labels_decode(struct stream *s, struct zapi_labels *zl)
for (int i = 0; i < zl->backup_nexthop_num; i++) {
znh = &zl->backup_nexthops[i];
- if (zapi_nexthop_decode(s, znh, 0) < 0)
+ if (zapi_nexthop_decode(s, znh, 0, 0) < 0)
return -1;
}
}
@@ -3657,6 +3764,10 @@ static int zclient_read(struct thread *thread)
(*zclient->opaque_unregister_handler)(command, zclient,
length, vrf_id);
break;
+ case ZEBRA_SR_POLICY_NOTIFY_STATUS:
+ if (zclient->sr_policy_notify_status)
+ (*zclient->sr_policy_notify_status)(command, zclient,
+ length, vrf_id);
default:
break;
}
diff --git a/lib/zclient.h b/lib/zclient.h
index f1730f15b2..bcdae85823 100644
--- a/lib/zclient.h
+++ b/lib/zclient.h
@@ -37,6 +37,7 @@
#include "pw.h"
#include "mlag.h"
+#include "srte.h"
#ifdef __cplusplus
extern "C" {
@@ -89,6 +90,8 @@ enum zserv_client_capabilities {
/* Macro to check if there GR enabled. */
#define ZEBRA_CLIENT_GR_ENABLED(X) (X == ZEBRA_CLIENT_GR_CAPABILITIES)
+#define ZEBRA_SR_POLICY_NAME_MAX_LENGTH 100
+
extern struct sockaddr_storage zclient_addr;
extern socklen_t zclient_addr_len;
@@ -143,6 +146,9 @@ typedef enum {
ZEBRA_MPLS_LABELS_ADD,
ZEBRA_MPLS_LABELS_DELETE,
ZEBRA_MPLS_LABELS_REPLACE,
+ ZEBRA_SR_POLICY_SET,
+ ZEBRA_SR_POLICY_DELETE,
+ ZEBRA_SR_POLICY_NOTIFY_STATUS,
ZEBRA_IPMR_ROUTE_STATS,
ZEBRA_LABEL_MANAGER_CONNECT,
ZEBRA_LABEL_MANAGER_CONNECT_ASYNC,
@@ -351,6 +357,7 @@ struct zclient {
int (*opaque_msg_handler)(ZAPI_CALLBACK_ARGS);
int (*opaque_register_handler)(ZAPI_CALLBACK_ARGS);
int (*opaque_unregister_handler)(ZAPI_CALLBACK_ARGS);
+ int (*sr_policy_notify_status)(ZAPI_CALLBACK_ARGS);
};
/* Zebra API message flag. */
@@ -368,7 +375,8 @@ struct zclient {
* the table being used is not in the VRF. You must pass the
* default vrf, else this will be ignored.
*/
-#define ZAPI_MESSAGE_TABLEID 0x80
+#define ZAPI_MESSAGE_TABLEID 0x0080
+#define ZAPI_MESSAGE_SRTE 0x0100
#define ZSERV_VERSION 6
/* Zserv protocol message header */
@@ -403,6 +411,9 @@ struct zapi_nexthop {
/* Backup nexthops, for IP-FRR, TI-LFA, etc */
uint8_t backup_num;
uint8_t backup_idx[NEXTHOP_MAX_BACKUPS];
+
+ /* SR-TE color. */
+ uint32_t srte_color;
};
/*
@@ -465,7 +476,7 @@ struct zapi_route {
#define ZEBRA_FLAG_RR_USE_DISTANCE 0x40
/* The older XXX_MESSAGE flags live here */
- uint8_t message;
+ uint32_t message;
/*
* This is an enum but we are going to treat it as a uint8_t
@@ -494,6 +505,9 @@ struct zapi_route {
vrf_id_t vrf_id;
uint32_t tableid;
+
+ /* SR-TE color (used for nexthop updates only). */
+ uint32_t srte_color;
};
struct zapi_labels {
@@ -516,6 +530,21 @@ struct zapi_labels {
struct zapi_nexthop backup_nexthops[MULTIPATH_NUM];
};
+struct zapi_srte_tunnel {
+ enum lsp_types_t type;
+ mpls_label_t local_label;
+ uint8_t label_num;
+ mpls_label_t labels[MPLS_MAX_LABELS];
+};
+
+struct zapi_sr_policy {
+ uint32_t color;
+ struct ipaddr endpoint;
+ char name[SRTE_POLICY_NAME_MAX_LENGTH];
+ struct zapi_srte_tunnel segment_list;
+ int status;
+};
+
struct zapi_pw {
char ifname[IF_NAMESIZE];
ifindex_t ifindex;
@@ -778,6 +807,14 @@ extern int tm_get_table_chunk(struct zclient *zclient, uint32_t chunk_size,
extern int tm_release_table_chunk(struct zclient *zclient, uint32_t start,
uint32_t end);
+extern int zebra_send_sr_policy(struct zclient *zclient, int cmd,
+ struct zapi_sr_policy *zp);
+extern int zapi_sr_policy_encode(struct stream *s, int cmd,
+ struct zapi_sr_policy *zp);
+extern int zapi_sr_policy_decode(struct stream *s, struct zapi_sr_policy *zp);
+extern int zapi_sr_policy_notify_status_decode(struct stream *s,
+ struct zapi_sr_policy *zp);
+
extern int zebra_send_mpls_labels(struct zclient *zclient, int cmd,
struct zapi_labels *zl);
extern int zapi_labels_encode(struct stream *s, int cmd,
@@ -794,7 +831,7 @@ extern int zclient_send_rnh(struct zclient *zclient, int command,
const struct prefix *p, bool exact_match,
vrf_id_t vrf_id);
int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh,
- uint32_t api_flags);
+ uint32_t api_flags, uint32_t api_message);
extern int zapi_route_encode(uint8_t, struct stream *, struct zapi_route *);
extern int zapi_route_decode(struct stream *, struct zapi_route *);
bool zapi_route_notify_decode(struct stream *s, struct prefix *p,
diff --git a/ospfd/ospf_sr.c b/ospfd/ospf_sr.c
index 8110bc2d02..f2330d6bdd 100644
--- a/ospfd/ospf_sr.c
+++ b/ospfd/ospf_sr.c
@@ -1048,32 +1048,25 @@ void ospf_sr_ri_lsa_update(struct ospf_lsa *lsa)
}
/* update LSA ID */
srn->instance = ntohl(lsah->id.s_addr);
- /* Copy SRGB */
- srn->srgb.range_size = srgb.range_size;
- srn->srgb.lower_bound = srgb.lower_bound;
- /* Set Algorithm */
- if (algo != NULL) {
- int i;
- for (i = 0; i < ntohs(algo->header.length); i++)
- srn->algo[i] = algo->value[0];
- for (; i < ALGORITHM_COUNT; i++)
- srn->algo[i] = SR_ALGORITHM_UNSET;
- } else {
- srn->algo[0] = SR_ALGORITHM_SPF;
- }
- /* set MSD */
- srn->msd = msd;
- return;
}
- /* Check if SRGB has changed */
- if ((srn->srgb.range_size == srgb.range_size)
- && (srn->srgb.lower_bound == srgb.lower_bound))
- return;
+ /* Set Algorithm */
+ if (algo != NULL) {
+ int i;
+ for (i = 0; i < ntohs(algo->header.length); i++)
+ srn->algo[i] = algo->value[0];
+ for (; i < ALGORITHM_COUNT; i++)
+ srn->algo[i] = SR_ALGORITHM_UNSET;
+ } else {
+ srn->algo[0] = SR_ALGORITHM_SPF;
+ }
- /* Update SRGB ... */
+ srn->msd = msd;
+
+ /* Copy SRGB */
srn->srgb.range_size = srgb.range_size;
srn->srgb.lower_bound = srgb.lower_bound;
+
/* ... and NHLFE if it is a neighbor SR node */
if (srn->neighbor == OspfSR.self)
hash_iterate(OspfSR.neighbors, update_out_nhlfe, srn);
diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c
index b79fb689dc..88bcc48f80 100644
--- a/pimd/pim_iface.c
+++ b/pimd/pim_iface.c
@@ -341,7 +341,7 @@ pim_sec_addr_find(struct pim_interface *pim_ifp, struct prefix *addr)
struct listnode *node;
for (ALL_LIST_ELEMENTS_RO(pim_ifp->sec_addr_list, node, sec_addr)) {
- if (prefix_cmp(&sec_addr->addr, addr)) {
+ if (prefix_cmp(&sec_addr->addr, addr) == 0) {
return sec_addr;
}
}
diff --git a/sharpd/sharp_vty.c b/sharpd/sharp_vty.c
index 6a120c8eff..049b8475e4 100644
--- a/sharpd/sharp_vty.c
+++ b/sharpd/sharp_vty.c
@@ -131,8 +131,8 @@ DEFPY(sharp_nht_data_dump,
sharp_nht_data_dump_cmd,
"sharp data nexthop",
"Sharp routing Protocol\n"
- "Nexthop information\n"
- "Data Dump\n")
+ "Data about what is going on\n"
+ "Nexthop information\n")
{
sharp_nh_tracker_dump(vty);
diff --git a/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py
index 3cdec760f7..8892d13eff 100755
--- a/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py
+++ b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py
@@ -250,8 +250,8 @@ def prerequisite_config_for_test_suite(tgen):
}
result = configure_vxlan(tgen, vxlan_input)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
+ assert result is True, "Testcase {} on {} :Failed \n Error: {}".format(
+ tc_name, dut, result
)
step("Configure bridge interface")
@@ -268,8 +268,8 @@ def prerequisite_config_for_test_suite(tgen):
}
}
result = configure_brctl(tgen, topo, brctl_input)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
+ assert result is True, "Testcase {} on {} :Failed \n Error: {}".format(
+ tc_name, dut, result
)
step("Configure default routes")
@@ -406,8 +406,8 @@ def test_RD_verification_manual_and_auto_p0(request):
result = verify_attributes_for_evpn_routes(
tgen, topo, dut, input_routes, rd="auto", rd_peer="e1"
)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
+ assert result is True, "Testcase {} on {} :Failed \n Error: {}".format(
+ tc_name, dut, result
)
step(
@@ -437,8 +437,8 @@ def test_RD_verification_manual_and_auto_p0(request):
result = verify_attributes_for_evpn_routes(
tgen, topo, dut, input_routes, rd="50.50.50.50:50"
)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
+ assert result is True, "Testcase {} on {} :Failed \n Error: {}".format(
+ tc_name, dut, result
)
step(
@@ -472,8 +472,8 @@ def test_RD_verification_manual_and_auto_p0(request):
result = verify_attributes_for_evpn_routes(
tgen, topo, dut, input_routes, rd="100.100.100.100:100"
)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
+ assert result is True, "Testcase {} on {} :Failed \n Error: {}".format(
+ tc_name, dut, result
)
step(
@@ -672,8 +672,8 @@ def test_RT_verification_manual_p0(request):
result = verify_attributes_for_evpn_routes(
tgen, topo, dut, input_routes, rt="100:100"
)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
+ assert result is True, "Testcase {} on {} :Failed \n Error: {}".format(
+ tc_name, dut, result
)
step(
@@ -711,8 +711,8 @@ def test_RT_verification_manual_p0(request):
result = verify_attributes_for_evpn_routes(
tgen, topo, dut, input_routes, rt=["100:100", "500:500"]
)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
+ assert result is True, "Testcase {} on {} :Failed \n Error: {}".format(
+ tc_name, dut, result
)
step(
@@ -834,8 +834,8 @@ def test_RT_verification_manual_p0(request):
result = verify_attributes_for_evpn_routes(
tgen, topo, dut, input_routes, rt=["100:100", "500:500"]
)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
+ assert result is True, "Testcase {} on {} :Failed \n Error: {}".format(
+ tc_name, dut, result
)
step("Delete RT export value 100:100 for vrf RED on Edge-1")
@@ -1277,14 +1277,14 @@ def test_evpn_routes_from_VNFs_p1(request):
for dut in ["d1", "d2"]:
input_routes = {key: topo["routers"][key] for key in ["r1"]}
result = verify_evpn_routes(tgen, topo, dut, input_routes)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
+ assert result is True, "Testcase {} on {} :Failed \n Error: {}".format(
+ tc_name, dut, result
)
input_routes = {key: topo["routers"][key] for key in ["r2"]}
result = verify_evpn_routes(tgen, topo, dut, input_routes)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
- tc_name, result
+ assert result is True, "Testcase {} on {} :Failed \n Error: {}".format(
+ tc_name, dut, result
)
step(
@@ -1861,7 +1861,7 @@ def test_bgp_attributes_for_evpn_address_family_p1(request, attribute):
temp[dut]["bgp"].append({"local_as": as_num, "delete": True})
result = create_router_bgp(tgen, topo, input_dict_2)
- assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ assert result is True, "Testcase {} on d1 :Failed \n Error: {}".format(
tc_name, result
)
diff --git a/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py b/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py
index 600d640a70..a1662dc411 100755
--- a/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py
+++ b/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py
@@ -154,7 +154,7 @@ def teardown_module(mod):
tgen.stop_topology()
-def router_compare_json_output(rname, command, reference):
+def router_compare_json_output(rname, command, reference, count=80, wait=1):
"Compare router JSON output"
logger.info('Comparing router "%s" "%s" output', rname, command)
@@ -163,9 +163,9 @@ def router_compare_json_output(rname, command, reference):
filename = "{}/{}/{}".format(CWD, rname, reference)
expected = json.loads(open(filename).read())
- # Run test function until we get an result. Wait at most 80 seconds.
+ # Run test function until we get an result.
test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected)
- _, diff = topotest.run_and_expect(test_func, None, count=160, wait=0.5)
+ _, diff = topotest.run_and_expect(test_func, None, count, wait)
assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
assert diff is None, assertmsg
@@ -279,10 +279,12 @@ def test_ldp_pseudowires_after_link_down():
tgen.gears["r1"].peer_link_enable("r1-eth1", False)
topotest.sleep(5, "Waiting for the network to reconverge")
- # check if the pseudowire is still up (using an alternate path for nexthop resolution)
+ # check if the pseudowire is still up (using an alternate path
+ # for nexthop resolution). Give some extra wait time.
for rname in ["r1", "r2", "r3"]:
router_compare_json_output(
- rname, "show l2vpn atom vc json", "show_l2vpn_vc.ref"
+ rname, "show l2vpn atom vc json", "show_l2vpn_vc.ref",
+ count=160, wait=1
)
diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py
index 156a5f7ea4..e4d72ea2d7 100644
--- a/tests/topotests/lib/common_config.py
+++ b/tests/topotests/lib/common_config.py
@@ -2281,7 +2281,7 @@ def configure_vxlan(tgen, input_dict):
cmd = "{} local {}".format(cmd, local_addr)
if learning == "no":
- cmd = "{} {} learning".format(cmd, learning)
+ cmd = "{} nolearning".format(cmd)
elif learning == "yes":
cmd = "{} learning".format(cmd)
diff --git a/tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json b/tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json
index 5ae2399e5c..dd42e326ce 100644
--- a/tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json
+++ b/tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json
@@ -6,6 +6,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true
}
@@ -18,6 +21,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.1.2"
@@ -38,6 +44,9 @@
{
"type":"SR (OSPF)",
"outLabel":8300,
+ "outLabelStack":[
+ 8300
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.1.2"
@@ -58,6 +67,9 @@
{
"type":"SR (OSPF)",
"outLabel":8400,
+ "outLabelStack":[
+ 8400
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.1.2"
@@ -104,6 +116,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.1.2"
@@ -117,6 +132,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.1.2"
diff --git a/tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json b/tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json
index aedcc5b8f8..f3462e239e 100644
--- a/tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json
+++ b/tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json
@@ -6,6 +6,9 @@
{
"type":"SR (OSPF)",
"outLabel":20100,
+ "outLabelStack":[
+ 20100
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.1.1"
@@ -26,6 +29,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.3.1"
@@ -39,6 +45,9 @@
{
"type":"SR (OSPF)",
"outLabel":10400,
+ "outLabelStack":[
+ 10400
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.4.1"
@@ -52,6 +61,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.4.1"
@@ -65,6 +77,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.4.1"
@@ -78,6 +93,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.0.1"
@@ -91,6 +109,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.0.1"
@@ -104,6 +125,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.1.1"
@@ -117,6 +141,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.1.1"
diff --git a/tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json b/tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json
index 71e8366137..3d036801d5 100644
--- a/tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json
+++ b/tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json
@@ -6,6 +6,9 @@
{
"type":"SR (OSPF)",
"outLabel":8100,
+ "outLabelStack":[
+ 8100
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.3.2"
@@ -19,6 +22,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.3.2"
@@ -32,6 +38,9 @@
{
"type":"SR (OSPF)",
"outLabel":8400,
+ "outLabelStack":[
+ 8400
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.3.2"
@@ -45,6 +54,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.3.2"
@@ -58,6 +70,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.3.2"
diff --git a/tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json b/tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json
index b5767e1d7d..86ad8721f8 100644
--- a/tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json
+++ b/tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json
@@ -6,6 +6,9 @@
{
"type":"SR (OSPF)",
"outLabel":8100,
+ "outLabelStack":[
+ 8100
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.4.2"
@@ -19,6 +22,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.4.2"
@@ -32,6 +38,9 @@
{
"type":"SR (OSPF)",
"outLabel":8300,
+ "outLabelStack":[
+ 8300
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.4.2"
@@ -45,6 +54,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true
}
@@ -57,6 +69,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.4.2"
@@ -70,6 +85,9 @@
{
"type":"SR (OSPF)",
"outLabel":3,
+ "outLabelStack":[
+ 3
+ ],
"distance":150,
"installed":true,
"nexthop":"10.0.4.2"
diff --git a/tests/topotests/zebra_netlink/__init__.py b/tests/topotests/zebra_netlink/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/zebra_netlink/__init__.py
diff --git a/tests/topotests/zebra_netlink/r1/sharpd.conf b/tests/topotests/zebra_netlink/r1/sharpd.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/zebra_netlink/r1/sharpd.conf
diff --git a/tests/topotests/zebra_netlink/r1/v4_route.json b/tests/topotests/zebra_netlink/r1/v4_route.json
new file mode 100644
index 0000000000..61e9bb240b
--- /dev/null
+++ b/tests/topotests/zebra_netlink/r1/v4_route.json
@@ -0,0 +1,2802 @@
+{
+ "2.1.3.7\/32":[
+ {
+ "prefix":"2.1.3.7\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.8\/32":[
+ {
+ "prefix":"2.1.3.8\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.9\/32":[
+ {
+ "prefix":"2.1.3.9\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.10\/32":[
+ {
+ "prefix":"2.1.3.10\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.11\/32":[
+ {
+ "prefix":"2.1.3.11\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.12\/32":[
+ {
+ "prefix":"2.1.3.12\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.13\/32":[
+ {
+ "prefix":"2.1.3.13\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.14\/32":[
+ {
+ "prefix":"2.1.3.14\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.15\/32":[
+ {
+ "prefix":"2.1.3.15\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.16\/32":[
+ {
+ "prefix":"2.1.3.16\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.17\/32":[
+ {
+ "prefix":"2.1.3.17\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.18\/32":[
+ {
+ "prefix":"2.1.3.18\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.19\/32":[
+ {
+ "prefix":"2.1.3.19\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.20\/32":[
+ {
+ "prefix":"2.1.3.20\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.21\/32":[
+ {
+ "prefix":"2.1.3.21\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.22\/32":[
+ {
+ "prefix":"2.1.3.22\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.23\/32":[
+ {
+ "prefix":"2.1.3.23\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.24\/32":[
+ {
+ "prefix":"2.1.3.24\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.25\/32":[
+ {
+ "prefix":"2.1.3.25\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.26\/32":[
+ {
+ "prefix":"2.1.3.26\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.27\/32":[
+ {
+ "prefix":"2.1.3.27\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.28\/32":[
+ {
+ "prefix":"2.1.3.28\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.29\/32":[
+ {
+ "prefix":"2.1.3.29\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.30\/32":[
+ {
+ "prefix":"2.1.3.30\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.31\/32":[
+ {
+ "prefix":"2.1.3.31\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.32\/32":[
+ {
+ "prefix":"2.1.3.32\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.33\/32":[
+ {
+ "prefix":"2.1.3.33\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.34\/32":[
+ {
+ "prefix":"2.1.3.34\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.35\/32":[
+ {
+ "prefix":"2.1.3.35\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.36\/32":[
+ {
+ "prefix":"2.1.3.36\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.37\/32":[
+ {
+ "prefix":"2.1.3.37\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.38\/32":[
+ {
+ "prefix":"2.1.3.38\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.39\/32":[
+ {
+ "prefix":"2.1.3.39\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.40\/32":[
+ {
+ "prefix":"2.1.3.40\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.41\/32":[
+ {
+ "prefix":"2.1.3.41\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.42\/32":[
+ {
+ "prefix":"2.1.3.42\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.43\/32":[
+ {
+ "prefix":"2.1.3.43\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.44\/32":[
+ {
+ "prefix":"2.1.3.44\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.45\/32":[
+ {
+ "prefix":"2.1.3.45\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.46\/32":[
+ {
+ "prefix":"2.1.3.46\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.47\/32":[
+ {
+ "prefix":"2.1.3.47\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.48\/32":[
+ {
+ "prefix":"2.1.3.48\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.49\/32":[
+ {
+ "prefix":"2.1.3.49\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.50\/32":[
+ {
+ "prefix":"2.1.3.50\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.51\/32":[
+ {
+ "prefix":"2.1.3.51\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.52\/32":[
+ {
+ "prefix":"2.1.3.52\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.53\/32":[
+ {
+ "prefix":"2.1.3.53\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.54\/32":[
+ {
+ "prefix":"2.1.3.54\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.55\/32":[
+ {
+ "prefix":"2.1.3.55\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.56\/32":[
+ {
+ "prefix":"2.1.3.56\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.57\/32":[
+ {
+ "prefix":"2.1.3.57\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.58\/32":[
+ {
+ "prefix":"2.1.3.58\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.59\/32":[
+ {
+ "prefix":"2.1.3.59\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.60\/32":[
+ {
+ "prefix":"2.1.3.60\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.61\/32":[
+ {
+ "prefix":"2.1.3.61\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.62\/32":[
+ {
+ "prefix":"2.1.3.62\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.63\/32":[
+ {
+ "prefix":"2.1.3.63\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.64\/32":[
+ {
+ "prefix":"2.1.3.64\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.65\/32":[
+ {
+ "prefix":"2.1.3.65\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.66\/32":[
+ {
+ "prefix":"2.1.3.66\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.67\/32":[
+ {
+ "prefix":"2.1.3.67\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.68\/32":[
+ {
+ "prefix":"2.1.3.68\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.69\/32":[
+ {
+ "prefix":"2.1.3.69\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.70\/32":[
+ {
+ "prefix":"2.1.3.70\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.71\/32":[
+ {
+ "prefix":"2.1.3.71\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.72\/32":[
+ {
+ "prefix":"2.1.3.72\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.73\/32":[
+ {
+ "prefix":"2.1.3.73\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.74\/32":[
+ {
+ "prefix":"2.1.3.74\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.75\/32":[
+ {
+ "prefix":"2.1.3.75\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.76\/32":[
+ {
+ "prefix":"2.1.3.76\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.77\/32":[
+ {
+ "prefix":"2.1.3.77\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.78\/32":[
+ {
+ "prefix":"2.1.3.78\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.79\/32":[
+ {
+ "prefix":"2.1.3.79\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.80\/32":[
+ {
+ "prefix":"2.1.3.80\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.81\/32":[
+ {
+ "prefix":"2.1.3.81\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.82\/32":[
+ {
+ "prefix":"2.1.3.82\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.83\/32":[
+ {
+ "prefix":"2.1.3.83\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.84\/32":[
+ {
+ "prefix":"2.1.3.84\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.85\/32":[
+ {
+ "prefix":"2.1.3.85\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.86\/32":[
+ {
+ "prefix":"2.1.3.86\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.87\/32":[
+ {
+ "prefix":"2.1.3.87\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.88\/32":[
+ {
+ "prefix":"2.1.3.88\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.89\/32":[
+ {
+ "prefix":"2.1.3.89\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.90\/32":[
+ {
+ "prefix":"2.1.3.90\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.91\/32":[
+ {
+ "prefix":"2.1.3.91\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.92\/32":[
+ {
+ "prefix":"2.1.3.92\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.93\/32":[
+ {
+ "prefix":"2.1.3.93\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.94\/32":[
+ {
+ "prefix":"2.1.3.94\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.95\/32":[
+ {
+ "prefix":"2.1.3.95\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.96\/32":[
+ {
+ "prefix":"2.1.3.96\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.97\/32":[
+ {
+ "prefix":"2.1.3.97\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.98\/32":[
+ {
+ "prefix":"2.1.3.98\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.99\/32":[
+ {
+ "prefix":"2.1.3.99\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.100\/32":[
+ {
+ "prefix":"2.1.3.100\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.101\/32":[
+ {
+ "prefix":"2.1.3.101\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.102\/32":[
+ {
+ "prefix":"2.1.3.102\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.103\/32":[
+ {
+ "prefix":"2.1.3.103\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.104\/32":[
+ {
+ "prefix":"2.1.3.104\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.105\/32":[
+ {
+ "prefix":"2.1.3.105\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "2.1.3.106\/32":[
+ {
+ "prefix":"2.1.3.106\/32",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":9,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":2,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/zebra_netlink/r1/zebra.conf b/tests/topotests/zebra_netlink/r1/zebra.conf
new file mode 100644
index 0000000000..786be19ad4
--- /dev/null
+++ b/tests/topotests/zebra_netlink/r1/zebra.conf
@@ -0,0 +1,2 @@
+int r1-eth0
+ ip address 192.168.1.1/24 \ No newline at end of file
diff --git a/tests/topotests/zebra_netlink/test_zebra_netlink.py b/tests/topotests/zebra_netlink/test_zebra_netlink.py
new file mode 100755
index 0000000000..7b692c75ab
--- /dev/null
+++ b/tests/topotests/zebra_netlink/test_zebra_netlink.py
@@ -0,0 +1,131 @@
+#!/usr/bin/env python
+
+#
+# test_zebra_netlink.py
+#
+# Copyright (c) 2020 by
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_zebra_netlink.py: Test some basic interactions with kernel using Netlink
+
+"""
+
+import os
+import re
+import sys
+import pytest
+import json
+import platform
+from functools import partial
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+from lib.common_config import shutdown_bringup_interface
+
+# Required to instantiate the topology builder class.
+from mininet.topo import Topo
+
+#####################################################
+##
+## Network Topology Definition
+##
+#####################################################
+
+
+class ZebraTopo(Topo):
+ "Test topology builder"
+
+ def build(self, *_args, **_opts):
+ "Build function"
+ tgen = get_topogen(self)
+
+ tgen.add_router("r1")
+
+ # Create a empty network for router 1
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+
+
+#####################################################
+##
+## Tests starting
+##
+#####################################################
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(ZebraTopo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+ for rname, router in router_list.iteritems():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+
+ router.load_config(
+ TopoRouter.RD_SHARP, os.path.join(CWD, "{}/sharpd.conf".format(rname))
+ )
+
+ # Initialize all routers.
+ tgen.start_router()
+
+
+def teardown_module(_mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ # This function tears down the whole topology.
+ tgen.stop_topology()
+
+
+def test_zebra_netlink_batching():
+ "Test the situation where dataplane fills netlink send buffer entirely."
+ logger.info(
+ "Test the situation where dataplane fills netlink send buffer entirely."
+ )
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ ptyest.skip("skipped because of preview test failure")
+ r1 = tgen.gears["r1"]
+
+ # Reduce the size of the buffer to hit the limit.
+ r1.vtysh_cmd("conf t\nzebra kernel netlink batch-tx-buf 256 256")
+
+ r1.vtysh_cmd("sharp install routes 2.1.3.7 nexthop 192.168.1.1 100")
+ json_file = "{}/r1/v4_route.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(topotest.router_json_cmp, r1, "show ip route json", expected,)
+ _, result = topotest.run_and_expect(test_func, None, count=2, wait=0.5)
+ assertmsg = '"r1" JSON output mismatches'
+ assert result is None, assertmsg
+
+ r1.vtysh_cmd("sharp remove routes 2.1.3.7 100")
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tools/gen_northbound_callbacks.c b/tools/gen_northbound_callbacks.c
index 8dccbac3ae..eaab932228 100644
--- a/tools/gen_northbound_callbacks.c
+++ b/tools/gen_northbound_callbacks.c
@@ -194,7 +194,7 @@ static void generate_callback(const struct nb_callback_info *ncinfo,
case NB_OP_MODIFY:
case NB_OP_DESTROY:
case NB_OP_MOVE:
- printf("\tswitch (event) {\n"
+ printf("\tswitch (args->event) {\n"
"\tcase NB_EV_VALIDATE:\n"
"\tcase NB_EV_PREPARE:\n"
"\tcase NB_EV_ABORT:\n"
diff --git a/vrrpd/vrrp_northbound.c b/vrrpd/vrrp_northbound.c
index e9cd714a95..f814963fe5 100644
--- a/vrrpd/vrrp_northbound.c
+++ b/vrrpd/vrrp_northbound.c
@@ -40,12 +40,31 @@ static int lib_interface_vrrp_vrrp_group_create(struct nb_cb_create_args *args)
uint8_t version = 3;
struct vrrp_vrouter *vr;
- if (args->event != NB_EV_APPLY)
+ vrid = yang_dnode_get_uint8(args->dnode, "./virtual-router-id");
+ version = yang_dnode_get_enum(args->dnode, "./version");
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ ifp = nb_running_get_entry(args->dnode, NULL, false);
+ if (ifp) {
+ vr = vrrp_lookup(ifp, vrid);
+ if (vr && vr->autoconf) {
+ snprintf(
+ args->errmsg, args->errmsg_len,
+ "Virtual Router with ID %d already exists on interface '%s'; created by VRRP autoconfiguration",
+ vrid, ifp->name);
+ return NB_ERR_VALIDATION;
+ }
+ }
+ return NB_OK;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
return NB_OK;
+ case NB_EV_APPLY:
+ break;
+ }
ifp = nb_running_get_entry(args->dnode, NULL, true);
- vrid = yang_dnode_get_uint8(args->dnode, "./virtual-router-id");
- version = yang_dnode_get_enum(args->dnode, "./version");
vr = vrrp_vrouter_create(ifp, vrid, version);
nb_running_set_entry(args->dnode, vr);
diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c
index 81f77d4f9b..5cd3e69299 100644
--- a/zebra/if_netlink.c
+++ b/zebra/if_netlink.c
@@ -987,7 +987,8 @@ int kernel_interface_set_master(struct interface *master,
}
/* Interface address modification. */
-static int netlink_address_ctx(const struct zebra_dplane_ctx *ctx)
+static ssize_t netlink_address_msg_encoder(struct zebra_dplane_ctx *ctx,
+ void *buf, size_t buflen)
{
int bytelen;
const struct prefix *p;
@@ -997,64 +998,72 @@ static int netlink_address_ctx(const struct zebra_dplane_ctx *ctx)
struct {
struct nlmsghdr n;
struct ifaddrmsg ifa;
- char buf[NL_PKT_BUF_SIZE];
- } req;
+ char buf[0];
+ } *req = buf;
+
+ if (buflen < sizeof(*req))
+ return 0;
p = dplane_ctx_get_intf_addr(ctx);
- memset(&req, 0, sizeof(req) - NL_PKT_BUF_SIZE);
+ memset(req, 0, sizeof(*req));
bytelen = (p->family == AF_INET ? 4 : 16);
- req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
- req.n.nlmsg_flags = NLM_F_REQUEST;
+ req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+ req->n.nlmsg_flags = NLM_F_REQUEST;
if (dplane_ctx_get_op(ctx) == DPLANE_OP_ADDR_INSTALL)
cmd = RTM_NEWADDR;
else
cmd = RTM_DELADDR;
- req.n.nlmsg_type = cmd;
- req.ifa.ifa_family = p->family;
+ req->n.nlmsg_type = cmd;
+ req->ifa.ifa_family = p->family;
- req.ifa.ifa_index = dplane_ctx_get_ifindex(ctx);
+ req->ifa.ifa_index = dplane_ctx_get_ifindex(ctx);
- nl_attr_put(&req.n, sizeof(req), IFA_LOCAL, &p->u.prefix, bytelen);
+ if (!nl_attr_put(&req->n, buflen, IFA_LOCAL, &p->u.prefix, bytelen))
+ return 0;
if (p->family == AF_INET) {
if (dplane_ctx_intf_is_connected(ctx)) {
p = dplane_ctx_get_intf_dest(ctx);
- nl_attr_put(&req.n, sizeof(req), IFA_ADDRESS,
- &p->u.prefix, bytelen);
+ if (!nl_attr_put(&req->n, buflen, IFA_ADDRESS,
+ &p->u.prefix, bytelen))
+ return 0;
} else if (cmd == RTM_NEWADDR) {
struct in_addr broad = {
.s_addr = ipv4_broadcast_addr(p->u.prefix4.s_addr,
p->prefixlen)
};
- nl_attr_put(&req.n, sizeof(req), IFA_BROADCAST, &broad,
- bytelen);
+ if (!nl_attr_put(&req->n, buflen, IFA_BROADCAST, &broad,
+ bytelen))
+ return 0;
}
}
/* p is now either address or destination/bcast addr */
- req.ifa.ifa_prefixlen = p->prefixlen;
+ req->ifa.ifa_prefixlen = p->prefixlen;
if (dplane_ctx_intf_is_secondary(ctx))
- SET_FLAG(req.ifa.ifa_flags, IFA_F_SECONDARY);
+ SET_FLAG(req->ifa.ifa_flags, IFA_F_SECONDARY);
if (dplane_ctx_intf_has_label(ctx)) {
label = dplane_ctx_get_intf_label(ctx);
- nl_attr_put(&req.n, sizeof(req), IFA_LABEL, label,
- strlen(label) + 1);
+ if (!nl_attr_put(&req->n, buflen, IFA_LABEL, label,
+ strlen(label) + 1))
+ return 0;
}
- return netlink_talk_info(netlink_talk_filter, &req.n,
- dplane_ctx_get_ns(ctx), 0);
+ return NLMSG_ALIGN(req->n.nlmsg_len);
}
-enum zebra_dplane_result kernel_address_update_ctx(struct zebra_dplane_ctx *ctx)
+enum netlink_msg_status
+netlink_put_address_update_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx)
{
- return (netlink_address_ctx(ctx) == 0 ?
- ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE);
+ return netlink_batch_add_msg(bth, ctx, netlink_address_msg_encoder,
+ false);
}
int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, int startup)
diff --git a/zebra/if_netlink.h b/zebra/if_netlink.h
index 29fd2aca35..0bbba81ca6 100644
--- a/zebra/if_netlink.h
+++ b/zebra/if_netlink.h
@@ -32,6 +32,10 @@ extern int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id,
extern int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup);
extern int interface_lookup_netlink(struct zebra_ns *zns);
+extern enum netlink_msg_status
+netlink_put_address_update_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx);
+
/*
* Set protodown status of interface.
*
diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c
index a4d22c12a4..b04076b945 100644
--- a/zebra/kernel_netlink.c
+++ b/zebra/kernel_netlink.c
@@ -84,6 +84,27 @@
#define RTPROT_MROUTED 17
#endif
+#define NL_DEFAULT_BATCH_BUFSIZE (16 * NL_PKT_BUF_SIZE)
+
+/*
+ * We limit the batch's size to a number smaller than the length of the
+ * underlying buffer since the last message that wouldn't fit the batch would go
+ * over the upper boundary and then it would have to be encoded again into a new
+ * buffer. If the difference between the limit and the length of the buffer is
+ * big enough (bigger than the biggest Netlink message) then this situation
+ * won't occur.
+ */
+#define NL_DEFAULT_BATCH_SEND_THRESHOLD (15 * NL_PKT_BUF_SIZE)
+
+/*
+ * For every request sent to the kernel that has failed we get an error message,
+ * which contains a standard netlink message header and the payload consisting
+ * of an error code and the original netlink mesage. So the receiving buffer
+ * must be at least as big as the transmitting buffer increased by some space
+ * for headers.
+ */
+#define NL_BATCH_RX_BUFSIZE (NL_DEFAULT_BATCH_BUFSIZE + NL_PKT_BUF_SIZE)
+
static const struct message nlmsg_str[] = {{RTM_NEWROUTE, "RTM_NEWROUTE"},
{RTM_DELROUTE, "RTM_DELROUTE"},
{RTM_GETROUTE, "RTM_GETROUTE"},
@@ -151,6 +172,62 @@ extern uint32_t nl_rcvbufsize;
extern struct zebra_privs_t zserv_privs;
+DEFINE_MTYPE_STATIC(ZEBRA, NL_BUF, "Zebra Netlink buffers")
+
+size_t nl_batch_tx_bufsize;
+char *nl_batch_tx_buf;
+
+char nl_batch_rx_buf[NL_BATCH_RX_BUFSIZE];
+
+_Atomic uint32_t nl_batch_bufsize = NL_DEFAULT_BATCH_BUFSIZE;
+_Atomic uint32_t nl_batch_send_threshold = NL_DEFAULT_BATCH_SEND_THRESHOLD;
+
+struct nl_batch {
+ void *buf;
+ size_t bufsiz;
+ size_t limit;
+
+ void *buf_head;
+ size_t curlen;
+ size_t msgcnt;
+
+ const struct zebra_dplane_info *zns;
+
+ struct dplane_ctx_q ctx_list;
+
+ /*
+ * Pointer to the queue of completed contexts outbound back
+ * towards the dataplane module.
+ */
+ struct dplane_ctx_q *ctx_out_q;
+};
+
+int netlink_config_write_helper(struct vty *vty)
+{
+ uint32_t size =
+ atomic_load_explicit(&nl_batch_bufsize, memory_order_relaxed);
+ uint32_t threshold = atomic_load_explicit(&nl_batch_send_threshold,
+ memory_order_relaxed);
+
+ if (size != NL_DEFAULT_BATCH_BUFSIZE
+ || threshold != NL_DEFAULT_BATCH_SEND_THRESHOLD)
+ vty_out(vty, "zebra kernel netlink batch-tx-buf %u %u\n", size,
+ threshold);
+
+ return 0;
+}
+
+void netlink_set_batch_buffer_size(uint32_t size, uint32_t threshold, bool set)
+{
+ if (!set) {
+ size = NL_DEFAULT_BATCH_BUFSIZE;
+ threshold = NL_DEFAULT_BATCH_SEND_THRESHOLD;
+ }
+
+ atomic_store_explicit(&nl_batch_bufsize, size, memory_order_relaxed);
+ atomic_store_explicit(&nl_batch_send_threshold, threshold,
+ memory_order_relaxed);
+}
int netlink_talk_filter(struct nlmsghdr *h, ns_id_t ns_id, int startup)
{
@@ -1008,9 +1085,10 @@ int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int),
* startup -> Are we reading in under startup conditions
* This is passed through eventually to filter.
*/
-int netlink_talk_info(int (*filter)(struct nlmsghdr *, ns_id_t, int startup),
- struct nlmsghdr *n,
- const struct zebra_dplane_info *dp_info, int startup)
+static int
+netlink_talk_info(int (*filter)(struct nlmsghdr *, ns_id_t, int startup),
+ struct nlmsghdr *n, const struct zebra_dplane_info *dp_info,
+ int startup)
{
const struct nlsock *nl;
@@ -1080,6 +1158,327 @@ int netlink_request(struct nlsock *nl, void *req)
return 0;
}
+static int nl_batch_read_resp(struct nl_batch *bth)
+{
+ struct nlmsghdr *h;
+ struct sockaddr_nl snl;
+ struct msghdr msg;
+ int status, seq;
+ const struct nlsock *nl;
+ struct zebra_dplane_ctx *ctx;
+ bool ignore_msg;
+
+ nl = &(bth->zns->nls);
+
+ msg.msg_name = (void *)&snl;
+ msg.msg_namelen = sizeof(snl);
+
+ status = netlink_recv_msg(nl, msg, nl_batch_rx_buf,
+ sizeof(nl_batch_rx_buf));
+ if (status == -1 || status == 0)
+ return status;
+
+ for (h = (struct nlmsghdr *)nl_batch_rx_buf;
+ (status >= 0 && NLMSG_OK(h, (unsigned int)status));
+ h = NLMSG_NEXT(h, status)) {
+ ignore_msg = false;
+ seq = h->nlmsg_seq;
+ /*
+ * Find the corresponding context object. Received responses are
+ * in the same order as requests we sent, so we can simply
+ * iterate over the context list and match responses with
+ * requests at same time.
+ */
+ while (true) {
+ ctx = dplane_ctx_dequeue(&(bth->ctx_list));
+ if (ctx == NULL)
+ break;
+
+ dplane_ctx_enqueue_tail(bth->ctx_out_q, ctx);
+
+ /* We have found corresponding context object. */
+ if (dplane_ctx_get_ns(ctx)->nls.seq == seq)
+ break;
+
+ /*
+ * 'update' context objects take two consecutive
+ * sequence numbers.
+ */
+ if (dplane_ctx_is_update(ctx)
+ && dplane_ctx_get_ns(ctx)->nls.seq + 1 == seq) {
+ /*
+ * This is the situation where we get a response
+ * to a message that should be ignored.
+ */
+ ignore_msg = true;
+ break;
+ }
+ }
+
+ if (ignore_msg)
+ continue;
+
+ /*
+ * We received a message with the sequence number that isn't
+ * associated with any dplane context object.
+ */
+ if (ctx == NULL) {
+ zlog_debug(
+ "%s: skipping unassociated response, seq number %d NS %u",
+ __func__, h->nlmsg_seq, bth->zns->ns_id);
+ continue;
+ }
+
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ int err = netlink_parse_error(nl, h, bth->zns, 0);
+
+ if (err == -1)
+ dplane_ctx_set_status(
+ ctx, ZEBRA_DPLANE_REQUEST_FAILURE);
+
+ zlog_debug("%s: netlink error message seq=%d ",
+ __func__, h->nlmsg_seq);
+ continue;
+ }
+
+ /*
+ * If we get here then we did not receive neither the ack nor
+ * the error and instead received some other message in an
+ * unexpected way.
+ */
+ zlog_debug("%s: ignoring message type 0x%04x(%s) NS %u",
+ __func__, h->nlmsg_type,
+ nl_msg_type_to_str(h->nlmsg_type), bth->zns->ns_id);
+ }
+
+ return 0;
+}
+
+static void nl_batch_reset(struct nl_batch *bth)
+{
+ bth->buf_head = bth->buf;
+ bth->curlen = 0;
+ bth->msgcnt = 0;
+ bth->zns = NULL;
+
+ TAILQ_INIT(&(bth->ctx_list));
+}
+
+static void nl_batch_init(struct nl_batch *bth, struct dplane_ctx_q *ctx_out_q)
+{
+ /*
+ * If the size of the buffer has changed, free and then allocate a new
+ * one.
+ */
+ size_t bufsize =
+ atomic_load_explicit(&nl_batch_bufsize, memory_order_relaxed);
+ if (bufsize != nl_batch_tx_bufsize) {
+ if (nl_batch_tx_buf)
+ XFREE(MTYPE_NL_BUF, nl_batch_tx_buf);
+
+ nl_batch_tx_buf = XCALLOC(MTYPE_NL_BUF, bufsize);
+ nl_batch_tx_bufsize = bufsize;
+ }
+
+ bth->buf = nl_batch_tx_buf;
+ bth->bufsiz = bufsize;
+ bth->limit = atomic_load_explicit(&nl_batch_send_threshold,
+ memory_order_relaxed);
+
+ bth->ctx_out_q = ctx_out_q;
+
+ nl_batch_reset(bth);
+}
+
+static void nl_batch_send(struct nl_batch *bth)
+{
+ struct zebra_dplane_ctx *ctx;
+ bool err = false;
+
+ if (bth->curlen != 0 && bth->zns != NULL) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: %s, batch size=%zu, msg cnt=%zu",
+ __func__, bth->zns->nls.name, bth->curlen,
+ bth->msgcnt);
+
+ if (netlink_send_msg(&(bth->zns->nls), bth->buf, bth->curlen)
+ == -1)
+ err = true;
+
+ if (!err) {
+ if (nl_batch_read_resp(bth) == -1)
+ err = true;
+ }
+ }
+
+ /* Move remaining contexts to the outbound queue. */
+ while (true) {
+ ctx = dplane_ctx_dequeue(&(bth->ctx_list));
+ if (ctx == NULL)
+ break;
+
+ if (err)
+ dplane_ctx_set_status(ctx,
+ ZEBRA_DPLANE_REQUEST_FAILURE);
+
+ dplane_ctx_enqueue_tail(bth->ctx_out_q, ctx);
+ }
+
+ nl_batch_reset(bth);
+}
+
+enum netlink_msg_status netlink_batch_add_msg(
+ struct nl_batch *bth, struct zebra_dplane_ctx *ctx,
+ ssize_t (*msg_encoder)(struct zebra_dplane_ctx *, void *, size_t),
+ bool ignore_res)
+{
+ int seq;
+ ssize_t size;
+ struct nlmsghdr *msgh;
+
+ size = (*msg_encoder)(ctx, bth->buf_head, bth->bufsiz - bth->curlen);
+
+ /*
+ * If there was an error while encoding the message (other than buffer
+ * overflow) then return an error.
+ */
+ if (size < 0)
+ return FRR_NETLINK_ERROR;
+
+ /*
+ * If the message doesn't fit entirely in the buffer then send the batch
+ * and retry.
+ */
+ if (size == 0) {
+ nl_batch_send(bth);
+ size = (*msg_encoder)(ctx, bth->buf_head,
+ bth->bufsiz - bth->curlen);
+ /*
+ * If the message doesn't fit in the empty buffer then just
+ * return an error.
+ */
+ if (size <= 0)
+ return FRR_NETLINK_ERROR;
+ }
+
+ seq = dplane_ctx_get_ns(ctx)->nls.seq;
+ if (ignore_res)
+ seq++;
+
+ msgh = (struct nlmsghdr *)bth->buf_head;
+ msgh->nlmsg_seq = seq;
+ msgh->nlmsg_pid = dplane_ctx_get_ns(ctx)->nls.snl.nl_pid;
+
+ bth->zns = dplane_ctx_get_ns(ctx);
+ bth->buf_head = ((char *)bth->buf_head) + size;
+ bth->curlen += size;
+ bth->msgcnt++;
+
+ return FRR_NETLINK_QUEUED;
+}
+
+static enum netlink_msg_status nl_put_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx)
+{
+ if (dplane_ctx_is_skip_kernel(ctx))
+ return FRR_NETLINK_SUCCESS;
+
+ switch (dplane_ctx_get_op(ctx)) {
+
+ case DPLANE_OP_ROUTE_INSTALL:
+ case DPLANE_OP_ROUTE_UPDATE:
+ case DPLANE_OP_ROUTE_DELETE:
+ return netlink_put_route_update_msg(bth, ctx);
+
+ case DPLANE_OP_NH_INSTALL:
+ case DPLANE_OP_NH_UPDATE:
+ case DPLANE_OP_NH_DELETE:
+ return netlink_put_nexthop_update_msg(bth, ctx);
+
+ case DPLANE_OP_LSP_INSTALL:
+ case DPLANE_OP_LSP_UPDATE:
+ case DPLANE_OP_LSP_DELETE:
+ return netlink_put_lsp_update_msg(bth, ctx);
+
+ case DPLANE_OP_PW_INSTALL:
+ case DPLANE_OP_PW_UNINSTALL:
+ return netlink_put_pw_update_msg(bth, ctx);
+
+ case DPLANE_OP_ADDR_INSTALL:
+ case DPLANE_OP_ADDR_UNINSTALL:
+ return netlink_put_address_update_msg(bth, ctx);
+
+ case DPLANE_OP_MAC_INSTALL:
+ case DPLANE_OP_MAC_DELETE:
+ return netlink_put_mac_update_msg(bth, ctx);
+
+ case DPLANE_OP_NEIGH_INSTALL:
+ case DPLANE_OP_NEIGH_UPDATE:
+ case DPLANE_OP_NEIGH_DELETE:
+ case DPLANE_OP_VTEP_ADD:
+ case DPLANE_OP_VTEP_DELETE:
+ return netlink_put_neigh_update_msg(bth, ctx);
+
+ case DPLANE_OP_RULE_ADD:
+ case DPLANE_OP_RULE_DELETE:
+ case DPLANE_OP_RULE_UPDATE:
+ return netlink_put_rule_update_msg(bth, ctx);
+
+ case DPLANE_OP_SYS_ROUTE_ADD:
+ case DPLANE_OP_SYS_ROUTE_DELETE:
+ case DPLANE_OP_ROUTE_NOTIFY:
+ case DPLANE_OP_LSP_NOTIFY:
+ return FRR_NETLINK_SUCCESS;
+
+ case DPLANE_OP_NONE:
+ return FRR_NETLINK_ERROR;
+ }
+
+ return FRR_NETLINK_ERROR;
+}
+
+void kernel_update_multi(struct dplane_ctx_q *ctx_list)
+{
+ struct nl_batch batch;
+ struct zebra_dplane_ctx *ctx;
+ struct dplane_ctx_q handled_list;
+ enum netlink_msg_status res;
+
+ TAILQ_INIT(&handled_list);
+ nl_batch_init(&batch, &handled_list);
+
+ while (true) {
+ ctx = dplane_ctx_dequeue(ctx_list);
+ if (ctx == NULL)
+ break;
+
+ if (batch.zns != NULL
+ && batch.zns->ns_id != dplane_ctx_get_ns(ctx)->ns_id)
+ nl_batch_send(&batch);
+
+ /*
+ * Assume all messages will succeed and then mark only the ones
+ * that failed.
+ */
+ dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS);
+
+ res = nl_put_msg(&batch, ctx);
+
+ dplane_ctx_enqueue_tail(&(batch.ctx_list), ctx);
+ if (res == FRR_NETLINK_ERROR)
+ dplane_ctx_set_status(ctx,
+ ZEBRA_DPLANE_REQUEST_FAILURE);
+
+ if (batch.curlen > batch.limit)
+ nl_batch_send(&batch);
+ }
+
+ nl_batch_send(&batch);
+
+ TAILQ_INIT(ctx_list);
+ dplane_ctx_list_append(ctx_list, &handled_list);
+}
+
/* Exported interface function. This function simply calls
netlink_socket (). */
void kernel_init(struct zebra_ns *zns)
diff --git a/zebra/kernel_netlink.h b/zebra/kernel_netlink.h
index bd8159faf3..c02e16480b 100644
--- a/zebra/kernel_netlink.h
+++ b/zebra/kernel_netlink.h
@@ -98,13 +98,49 @@ extern int netlink_talk_filter(struct nlmsghdr *h, ns_id_t ns, int startup);
extern int netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup),
struct nlmsghdr *n, struct nlsock *nl,
struct zebra_ns *zns, int startup);
-/* Version with 'info' struct only */
-int netlink_talk_info(int (*filter)(struct nlmsghdr *, ns_id_t, int startup),
- struct nlmsghdr *n,
- const struct zebra_dplane_info *dp_info, int startup);
-
extern int netlink_request(struct nlsock *nl, void *req);
+enum netlink_msg_status {
+ FRR_NETLINK_SUCCESS,
+ FRR_NETLINK_ERROR,
+ FRR_NETLINK_QUEUED,
+};
+
+struct nl_batch;
+
+/*
+ * netlink_batch_add_msg - add message to the netlink batch using dplane
+ * context object.
+ *
+ * @ctx: Dataplane context
+ * @msg_encoder: A function that encodes dplane context object into
+ * netlink message. Should take dplane context object,
+ * pointer to a buffer and buffer's length as parameters
+ * and should return -1 on error, 0 on buffer overflow or
+ * size of the encoded message.
+ * @ignore_res: Whether the result of this message should be ignored.
+ * This should be used in some 'update' cases where we
+ * need to send two messages for one context object.
+ *
+ * Return: Status of the message.
+ */
+extern enum netlink_msg_status netlink_batch_add_msg(
+ struct nl_batch *bth, struct zebra_dplane_ctx *ctx,
+ ssize_t (*msg_encoder)(struct zebra_dplane_ctx *, void *, size_t),
+ bool ignore_res);
+
+/*
+ * Vty/cli apis
+ */
+extern int netlink_config_write_helper(struct vty *vty);
+
+/*
+ * Configure size of the batch buffer and sending threshold. If 'unset', reset
+ * to default value.
+ */
+extern void netlink_set_batch_buffer_size(uint32_t size, uint32_t threshold,
+ bool set);
+
#endif /* HAVE_NETLINK */
#ifdef __cplusplus
diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c
index 873d221149..9e56e2988d 100644
--- a/zebra/kernel_socket.c
+++ b/zebra/kernel_socket.c
@@ -1464,4 +1464,98 @@ void kernel_terminate(struct zebra_ns *zns, bool complete)
return;
}
+void kernel_update_multi(struct dplane_ctx_q *ctx_list)
+{
+ struct zebra_dplane_ctx *ctx;
+ struct dplane_ctx_q handled_list;
+ enum zebra_dplane_result res;
+
+ TAILQ_INIT(&handled_list);
+
+ while (true) {
+ ctx = dplane_ctx_dequeue(ctx_list);
+ if (ctx == NULL)
+ break;
+
+ /*
+ * A previous provider plugin may have asked to skip the
+ * kernel update.
+ */
+ if (dplane_ctx_is_skip_kernel(ctx)) {
+ res = ZEBRA_DPLANE_REQUEST_SUCCESS;
+ goto skip_one;
+ }
+
+ switch (dplane_ctx_get_op(ctx)) {
+
+ case DPLANE_OP_ROUTE_INSTALL:
+ case DPLANE_OP_ROUTE_UPDATE:
+ case DPLANE_OP_ROUTE_DELETE:
+ res = kernel_route_update(ctx);
+ break;
+
+ case DPLANE_OP_NH_INSTALL:
+ case DPLANE_OP_NH_UPDATE:
+ case DPLANE_OP_NH_DELETE:
+ res = kernel_nexthop_update(ctx);
+ break;
+
+ case DPLANE_OP_LSP_INSTALL:
+ case DPLANE_OP_LSP_UPDATE:
+ case DPLANE_OP_LSP_DELETE:
+ res = kernel_lsp_update(ctx);
+ break;
+
+ case DPLANE_OP_PW_INSTALL:
+ case DPLANE_OP_PW_UNINSTALL:
+ res = kernel_pw_update(ctx);
+ break;
+
+ case DPLANE_OP_ADDR_INSTALL:
+ case DPLANE_OP_ADDR_UNINSTALL:
+ res = kernel_address_update_ctx(ctx);
+ break;
+
+ case DPLANE_OP_MAC_INSTALL:
+ case DPLANE_OP_MAC_DELETE:
+ res = kernel_mac_update_ctx(ctx);
+ break;
+
+ case DPLANE_OP_NEIGH_INSTALL:
+ case DPLANE_OP_NEIGH_UPDATE:
+ case DPLANE_OP_NEIGH_DELETE:
+ case DPLANE_OP_VTEP_ADD:
+ case DPLANE_OP_VTEP_DELETE:
+ res = kernel_neigh_update_ctx(ctx);
+ break;
+
+ case DPLANE_OP_RULE_ADD:
+ case DPLANE_OP_RULE_DELETE:
+ case DPLANE_OP_RULE_UPDATE:
+ res = kernel_pbr_rule_update(ctx);
+ break;
+
+ /* 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;
+
+ default:
+ res = ZEBRA_DPLANE_REQUEST_FAILURE;
+ break;
+ }
+
+ skip_one:
+ dplane_ctx_set_status(ctx, res);
+
+ dplane_ctx_enqueue_tail(&handled_list, ctx);
+ }
+
+ TAILQ_INIT(ctx_list);
+ dplane_ctx_list_append(ctx_list, &handled_list);
+}
+
#endif /* !HAVE_NETLINK */
diff --git a/zebra/label_manager.c b/zebra/label_manager.c
index 93736e672a..d312a661f3 100644
--- a/zebra/label_manager.c
+++ b/zebra/label_manager.c
@@ -240,7 +240,7 @@ assign_specific_label_chunk(uint8_t proto, unsigned short instance,
if (lmc->proto != NO_PROTO)
return NULL;
- if (end < lmc->end) {
+ if (end <= lmc->end) {
last_node = node;
break;
}
diff --git a/zebra/main.c b/zebra/main.c
index 92e94c2a2a..64746f7166 100644
--- a/zebra/main.c
+++ b/zebra/main.c
@@ -57,6 +57,7 @@
#include "zebra/zebra_routemap.h"
#include "zebra/zebra_nb.h"
#include "zebra/zebra_opaque.h"
+#include "zebra/zebra_srte.h"
#if defined(HANDLE_NETLINK_FUZZING)
#include "zebra/kernel_netlink.h"
@@ -438,9 +439,10 @@ int main(int argc, char **argv)
zebra_pw_vty_init();
zebra_pbr_init();
zebra_opaque_init();
+ zebra_srte_init();
-/* For debug purpose. */
-/* SET_FLAG (zebra_debug_event, ZEBRA_DEBUG_EVENT); */
+ /* For debug purpose. */
+ /* SET_FLAG (zebra_debug_event, ZEBRA_DEBUG_EVENT); */
/* Process the configuration file. Among other configuration
* directives we can meet those installing static routes. Such
diff --git a/zebra/rt.h b/zebra/rt.h
index 143e16b3ea..48f1df2868 100644
--- a/zebra/rt.h
+++ b/zebra/rt.h
@@ -40,7 +40,7 @@ extern "C" {
#define RSYSTEM_ROUTE(type) \
((RKERNEL_ROUTE(type)) || (type) == ZEBRA_ROUTE_CONNECT)
-
+#ifndef HAVE_NETLINK
/*
* Update or delete a route, nexthop, LSP, pseudowire, or vxlan MAC from the
* kernel, using info from a dataplane context.
@@ -63,6 +63,11 @@ enum zebra_dplane_result kernel_mac_update_ctx(struct zebra_dplane_ctx *ctx);
enum zebra_dplane_result kernel_neigh_update_ctx(struct zebra_dplane_ctx *ctx);
+extern enum zebra_dplane_result
+kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx);
+
+#endif /* !HAVE_NETLINK */
+
extern int kernel_neigh_update(int cmd, int ifindex, uint32_t addr, char *lla,
int llalen, ns_id_t ns_id);
extern int kernel_interface_set_master(struct interface *master,
@@ -97,6 +102,11 @@ extern int kernel_upd_mac_nhg(uint32_t nhg_id, uint32_t nh_cnt,
struct nh_grp *nh_ids);
extern int kernel_del_mac_nhg(uint32_t nhg_id);
+/*
+ * Message batching interface.
+ */
+extern void kernel_update_multi(struct dplane_ctx_q *ctx_list);
+
#ifdef __cplusplus
}
#endif
diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c
index 4daef42d7a..190af3f59d 100644
--- a/zebra/rt_netlink.c
+++ b/zebra/rt_netlink.c
@@ -179,7 +179,8 @@ static inline bool is_selfroute(int proto)
|| (proto == RTPROT_NHRP) || (proto == RTPROT_EIGRP)
|| (proto == RTPROT_LDP) || (proto == RTPROT_BABEL)
|| (proto == RTPROT_RIP) || (proto == RTPROT_SHARP)
- || (proto == RTPROT_PBR) || (proto == RTPROT_OPENFABRIC)) {
+ || (proto == RTPROT_PBR) || (proto == RTPROT_OPENFABRIC)
+ || (proto == RTPROT_SRTE)) {
return true;
}
@@ -229,6 +230,9 @@ static inline int zebra2proto(int proto)
case ZEBRA_ROUTE_OPENFABRIC:
proto = RTPROT_OPENFABRIC;
break;
+ case ZEBRA_ROUTE_SRTE:
+ proto = RTPROT_SRTE;
+ break;
case ZEBRA_ROUTE_TABLE:
case ZEBRA_ROUTE_NHG:
proto = RTPROT_ZEBRA;
@@ -294,6 +298,9 @@ static inline int proto2zebra(int proto, int family, bool is_nexthop)
case RTPROT_OPENFABRIC:
proto = ZEBRA_ROUTE_OPENFABRIC;
break;
+ case RTPROT_SRTE:
+ proto = ZEBRA_ROUTE_SRTE;
+ break;
case RTPROT_ZEBRA:
if (is_nexthop) {
proto = ZEBRA_ROUTE_NHG;
@@ -2223,19 +2230,11 @@ nexthop_done:
return NLMSG_ALIGN(req->n.nlmsg_len);
}
-/**
- * kernel_nexthop_update() - Update/delete a nexthop from the kernel
- *
- * @ctx: Dataplane context
- *
- * Return: Dataplane result flag
- */
-enum zebra_dplane_result kernel_nexthop_update(struct zebra_dplane_ctx *ctx)
+static ssize_t netlink_nexthop_msg_encoder(struct zebra_dplane_ctx *ctx,
+ void *buf, size_t buflen)
{
enum dplane_op_e op;
int cmd = 0;
- int ret = 0;
- char buf[NL_PKT_BUF_SIZE];
op = dplane_ctx_get_op(ctx);
if (op == DPLANE_OP_NH_INSTALL || op == DPLANE_OP_NH_UPDATE)
@@ -2246,33 +2245,43 @@ enum zebra_dplane_result kernel_nexthop_update(struct zebra_dplane_ctx *ctx)
flog_err(EC_ZEBRA_NHG_FIB_UPDATE,
"Context received for kernel nexthop update with incorrect OP code (%u)",
op);
- return ZEBRA_DPLANE_REQUEST_FAILURE;
+ return -1;
}
+ return netlink_nexthop_msg_encode(cmd, ctx, buf, buflen);
+}
+
+enum netlink_msg_status
+netlink_put_nexthop_update_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx)
+{
/* Nothing to do if the kernel doesn't support nexthop objects */
if (!kernel_nexthops_supported())
- return ZEBRA_DPLANE_REQUEST_SUCCESS;
+ return FRR_NETLINK_SUCCESS;
- if (netlink_nexthop_msg_encode(cmd, ctx, buf, sizeof(buf)) > 0)
- ret = netlink_talk_info(netlink_talk_filter, (void *)&buf,
- dplane_ctx_get_ns(ctx), 0);
- else
- ret = 0;
+ return netlink_batch_add_msg(bth, ctx, netlink_nexthop_msg_encoder,
+ false);
+}
- return (ret == 0 ? ZEBRA_DPLANE_REQUEST_SUCCESS
- : ZEBRA_DPLANE_REQUEST_FAILURE);
+static ssize_t netlink_newroute_msg_encoder(struct zebra_dplane_ctx *ctx,
+ void *buf, size_t buflen)
+{
+ return netlink_route_multipath_msg_encode(RTM_NEWROUTE, ctx, buf,
+ buflen, false, false);
}
-/*
- * Update or delete a prefix from the kernel,
- * using info from a dataplane context.
- */
-enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx)
+static ssize_t netlink_delroute_msg_encoder(struct zebra_dplane_ctx *ctx,
+ void *buf, size_t buflen)
+{
+ return netlink_route_multipath_msg_encode(RTM_DELROUTE, ctx, buf,
+ buflen, false, false);
+}
+
+enum netlink_msg_status
+netlink_put_route_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx)
{
- int cmd, ret;
+ int cmd;
const struct prefix *p = dplane_ctx_get_dest(ctx);
- struct nexthop *nexthop;
- uint8_t nl_pkt[NL_PKT_BUF_SIZE];
if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_DELETE) {
cmd = RTM_DELROUTE;
@@ -2282,7 +2291,6 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx)
if (p->family == AF_INET || v6_rr_semantics) {
/* Single 'replace' operation */
- cmd = RTM_NEWROUTE;
/*
* With route replace semantics in place
@@ -2292,17 +2300,11 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx)
* route should cause us to withdraw from
* the kernel the old non-system route
*/
- if (RSYSTEM_ROUTE(dplane_ctx_get_type(ctx)) &&
- !RSYSTEM_ROUTE(dplane_ctx_get_old_type(ctx))) {
- if (netlink_route_multipath_msg_encode(
- RTM_DELROUTE, ctx, nl_pkt,
- sizeof(nl_pkt), false, false)
- > 0)
- netlink_talk_info(
- netlink_talk_filter,
- (struct nlmsghdr *)nl_pkt,
- dplane_ctx_get_ns(ctx), 0);
- }
+ if (RSYSTEM_ROUTE(dplane_ctx_get_type(ctx))
+ && !RSYSTEM_ROUTE(dplane_ctx_get_old_type(ctx)))
+ netlink_batch_add_msg(
+ bth, ctx, netlink_delroute_msg_encoder,
+ true);
} else {
/*
* So v6 route replace semantics are not in
@@ -2316,51 +2318,24 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx)
* of the route delete. If that happens yeah we're
* screwed.
*/
- if (!RSYSTEM_ROUTE(dplane_ctx_get_old_type(ctx))) {
- if (netlink_route_multipath_msg_encode(
- RTM_DELROUTE, ctx, nl_pkt,
- sizeof(nl_pkt), false, false)
- > 0)
- netlink_talk_info(
- netlink_talk_filter,
- (struct nlmsghdr *)nl_pkt,
- dplane_ctx_get_ns(ctx), 0);
- }
- cmd = RTM_NEWROUTE;
+ if (!RSYSTEM_ROUTE(dplane_ctx_get_old_type(ctx)))
+ netlink_batch_add_msg(
+ bth, ctx, netlink_delroute_msg_encoder,
+ true);
}
- } else {
- return ZEBRA_DPLANE_REQUEST_FAILURE;
- }
-
- if (!RSYSTEM_ROUTE(dplane_ctx_get_type(ctx))) {
- if (netlink_route_multipath_msg_encode(
- cmd, ctx, nl_pkt, sizeof(nl_pkt), false, false)
- > 0)
- ret = netlink_talk_info(netlink_talk_filter,
- (struct nlmsghdr *)nl_pkt,
- dplane_ctx_get_ns(ctx), 0);
- else
- ret = -1;
-
+ cmd = RTM_NEWROUTE;
} else
- ret = 0;
- if ((cmd == RTM_NEWROUTE) && (ret == 0)) {
- /* Update installed nexthops to signal which have been
- * installed.
- */
- for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) {
- if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
- continue;
+ return FRR_NETLINK_ERROR;
- if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) {
- SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
- }
- }
- }
+ if (RSYSTEM_ROUTE(dplane_ctx_get_type(ctx)))
+ return FRR_NETLINK_SUCCESS;
- return (ret == 0 ?
- ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE);
+ return netlink_batch_add_msg(bth, ctx,
+ cmd == RTM_NEWROUTE
+ ? netlink_newroute_msg_encoder
+ : netlink_delroute_msg_encoder,
+ false);
}
/**
@@ -2776,23 +2751,16 @@ static ssize_t netlink_neigh_update_msg_encode(
* Add remote VTEP to the flood list for this VxLAN interface (VNI). This
* is done by adding an FDB entry with a MAC of 00:00:00:00:00:00.
*/
-static int netlink_vxlan_flood_update_ctx(const struct zebra_dplane_ctx *ctx,
- int cmd)
+static ssize_t
+netlink_vxlan_flood_update_ctx(const struct zebra_dplane_ctx *ctx, int cmd,
+ void *buf, size_t buflen)
{
struct ethaddr dst_mac = {.octet = {0}};
- uint8_t nl_pkt[NL_PKT_BUF_SIZE];
-
- if (netlink_neigh_update_msg_encode(
- ctx, cmd, &dst_mac, dplane_ctx_neigh_get_ipaddr(ctx), false,
- PF_BRIDGE, 0, NTF_SELF, (NUD_NOARP | NUD_PERMANENT),
- 0 /*nhg*/, false /*nfy*/, 0 /*nfy_flags*/,
- nl_pkt, sizeof(nl_pkt))
- <= 0)
- return -1;
- return netlink_talk_info(netlink_talk_filter,
- (struct nlmsghdr *)nl_pkt,
- dplane_ctx_get_ns(ctx), 0);
+ return netlink_neigh_update_msg_encode(
+ ctx, cmd, &dst_mac, dplane_ctx_neigh_get_ipaddr(ctx), false,
+ PF_BRIDGE, 0, NTF_SELF, (NUD_NOARP | NUD_PERMANENT), 0 /*nhg*/,
+ false /*nfy*/, 0 /*nfy_flags*/, buf, buflen);
}
#ifndef NDA_RTA
@@ -3141,9 +3109,8 @@ int netlink_macfdb_read_specific_mac(struct zebra_ns *zns,
/*
* Netlink-specific handler for MAC updates using dataplane context object.
*/
-ssize_t
-netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, uint8_t *data,
- size_t datalen)
+ssize_t netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, void *data,
+ size_t datalen)
{
struct ipaddr vtep_ip;
vlanid_t vid;
@@ -3619,15 +3586,14 @@ int netlink_neigh_change(struct nlmsghdr *h, ns_id_t ns_id)
/*
* Utility neighbor-update function, using info from dplane context.
*/
-static int netlink_neigh_update_ctx(const struct zebra_dplane_ctx *ctx,
- int cmd)
+static ssize_t netlink_neigh_update_ctx(const struct zebra_dplane_ctx *ctx,
+ int cmd, void *buf, size_t buflen)
{
const struct ipaddr *ip;
const struct ethaddr *mac;
uint8_t flags;
uint16_t state;
uint8_t family;
- uint8_t nl_pkt[NL_PKT_BUF_SIZE];
ip = dplane_ctx_neigh_get_ipaddr(ctx);
mac = dplane_ctx_neigh_get_mac(ctx);
@@ -3652,60 +3618,55 @@ static int netlink_neigh_update_ctx(const struct zebra_dplane_ctx *ctx,
flags, state);
}
- if (netlink_neigh_update_msg_encode(ctx, cmd, mac, ip, true, family,
- RTN_UNICAST, flags, state,
- 0 /*nhg*/, false /*nfy*/, 0 /*nfy_flags*/,
- nl_pkt, sizeof(nl_pkt))
- <= 0)
- return -1;
-
- return netlink_talk_info(netlink_talk_filter, (struct nlmsghdr *)nl_pkt,
- dplane_ctx_get_ns(ctx), 0);
+ return netlink_neigh_update_msg_encode(
+ ctx, cmd, mac, ip, true, family, RTN_UNICAST, flags, state,
+ 0 /*nhg*/, false /*nfy*/, 0 /*nfy_flags*/, buf, buflen);
}
-/*
- * Update MAC, using dataplane context object.
- */
-enum zebra_dplane_result kernel_mac_update_ctx(struct zebra_dplane_ctx *ctx)
+static ssize_t netlink_neigh_msg_encoder(struct zebra_dplane_ctx *ctx,
+ void *buf, size_t buflen)
{
- uint8_t nl_pkt[NL_PKT_BUF_SIZE];
- ssize_t rv;
-
- rv = netlink_macfdb_update_ctx(ctx, nl_pkt, sizeof(nl_pkt));
- if (rv <= 0)
- return ZEBRA_DPLANE_REQUEST_FAILURE;
-
- rv = netlink_talk_info(netlink_talk_filter, (struct nlmsghdr *)nl_pkt,
- dplane_ctx_get_ns(ctx), 0);
-
- return rv == 0 ?
- ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE;
-}
-
-enum zebra_dplane_result kernel_neigh_update_ctx(struct zebra_dplane_ctx *ctx)
-{
- int ret = -1;
+ ssize_t ret;
switch (dplane_ctx_get_op(ctx)) {
case DPLANE_OP_NEIGH_INSTALL:
case DPLANE_OP_NEIGH_UPDATE:
- ret = netlink_neigh_update_ctx(ctx, RTM_NEWNEIGH);
+ ret = netlink_neigh_update_ctx(ctx, RTM_NEWNEIGH, buf, buflen);
break;
case DPLANE_OP_NEIGH_DELETE:
- ret = netlink_neigh_update_ctx(ctx, RTM_DELNEIGH);
+ ret = netlink_neigh_update_ctx(ctx, RTM_DELNEIGH, buf, buflen);
break;
case DPLANE_OP_VTEP_ADD:
- ret = netlink_vxlan_flood_update_ctx(ctx, RTM_NEWNEIGH);
+ ret = netlink_vxlan_flood_update_ctx(ctx, RTM_NEWNEIGH, buf,
+ buflen);
break;
case DPLANE_OP_VTEP_DELETE:
- ret = netlink_vxlan_flood_update_ctx(ctx, RTM_DELNEIGH);
+ ret = netlink_vxlan_flood_update_ctx(ctx, RTM_DELNEIGH, buf,
+ buflen);
break;
default:
- break;
+ ret = -1;
}
- return (ret == 0 ?
- ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE);
+ return ret;
+}
+
+/*
+ * Update MAC, using dataplane context object.
+ */
+
+enum netlink_msg_status netlink_put_mac_update_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx)
+{
+ return netlink_batch_add_msg(bth, ctx, netlink_macfdb_update_ctx,
+ false);
+}
+
+enum netlink_msg_status
+netlink_put_neigh_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx)
+{
+ return netlink_batch_add_msg(bth, ctx, netlink_neigh_msg_encoder,
+ false);
}
/*
diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h
index 429ff0bf85..e1bb844785 100644
--- a/zebra/rt_netlink.h
+++ b/zebra/rt_netlink.h
@@ -60,6 +60,7 @@ extern "C" {
#define RTPROT_PBR 195
#define RTPROT_ZSTATIC 196
#define RTPROT_OPENFABRIC 197
+#define RTPROT_SRTE 198
void rt_netlink_init(void);
@@ -73,7 +74,7 @@ extern ssize_t netlink_route_multipath_msg_encode(int cmd,
uint8_t *data, size_t datalen,
bool fpm, bool force_nhg);
extern ssize_t netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx,
- uint8_t *data, size_t datalen);
+ void *data, size_t datalen);
extern int netlink_route_change(struct nlmsghdr *h, ns_id_t ns_id, int startup);
extern int netlink_route_read(struct zebra_ns *zns);
@@ -100,6 +101,23 @@ extern int netlink_neigh_read_specific_ip(struct ipaddr *ip,
struct interface *vlan_if);
extern vrf_id_t vrf_lookup_by_table(uint32_t table_id, ns_id_t ns_id);
+struct nl_batch;
+extern enum netlink_msg_status
+netlink_put_route_update_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx);
+extern enum netlink_msg_status
+netlink_put_nexthop_update_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx);
+extern enum netlink_msg_status
+netlink_put_mac_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx);
+extern enum netlink_msg_status
+netlink_put_neigh_update_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx);
+extern enum netlink_msg_status
+netlink_put_lsp_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx);
+extern enum netlink_msg_status
+netlink_put_pw_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx);
+
#ifdef __cplusplus
}
#endif
diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c
index 0271dc7f41..a2e15cbd2b 100644
--- a/zebra/rt_socket.c
+++ b/zebra/rt_socket.c
@@ -358,20 +358,6 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx)
}
} /* Elevated privs */
- if (RSYSTEM_ROUTE(type)
- && dplane_ctx_get_op(ctx) != DPLANE_OP_ROUTE_DELETE) {
- struct nexthop *nexthop;
-
- for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) {
- if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
- continue;
-
- if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) {
- SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
- }
- }
- }
-
return res;
}
diff --git a/zebra/rule_netlink.c b/zebra/rule_netlink.c
index 648e9eabe1..3a3baab4ca 100644
--- a/zebra/rule_netlink.c
+++ b/zebra/rule_netlink.c
@@ -78,6 +78,8 @@ netlink_rule_msg_encode(int cmd, const struct zebra_dplane_ctx *ctx,
char buf1[PREFIX_STRLEN];
char buf2[PREFIX_STRLEN];
+ if (buflen < sizeof(*req))
+ return 0;
memset(req, 0, sizeof(*req));
family = PREFIX_FAMILY(src_ip);
bytelen = (family == AF_INET ? 4 : 16);
@@ -148,53 +150,55 @@ netlink_rule_msg_encode(int cmd, const struct zebra_dplane_ctx *ctx,
return NLMSG_ALIGN(req->n.nlmsg_len);
}
-/* Install or uninstall specified rule for a specific interface.
- * Form netlink message and ship it.
- */
-static int netlink_rule_update_internal(
- int cmd, const struct zebra_dplane_ctx *ctx, uint32_t filter_bm,
- uint32_t priority, uint32_t table, const struct prefix *src_ip,
- const struct prefix *dst_ip, uint32_t fwmark, uint8_t dsfield)
+static ssize_t netlink_rule_msg_encoder(struct zebra_dplane_ctx *ctx, void *buf,
+ size_t buflen)
{
- char buf[NL_PKT_BUF_SIZE];
+ int cmd = RTM_NEWRULE;
+
+ if (dplane_ctx_get_op(ctx) == DPLANE_OP_RULE_DELETE)
+ cmd = RTM_DELRULE;
- netlink_rule_msg_encode(cmd, ctx, filter_bm, priority, table, src_ip,
- dst_ip, fwmark, dsfield, buf, sizeof(buf));
- return netlink_talk_info(netlink_talk_filter, (void *)&buf,
- dplane_ctx_get_ns(ctx), 0);
+ return netlink_rule_msg_encode(
+ cmd, ctx, dplane_ctx_rule_get_filter_bm(ctx),
+ dplane_ctx_rule_get_priority(ctx),
+ dplane_ctx_rule_get_table(ctx), dplane_ctx_rule_get_src_ip(ctx),
+ dplane_ctx_rule_get_dst_ip(ctx),
+ dplane_ctx_rule_get_fwmark(ctx),
+ dplane_ctx_rule_get_dsfield(ctx), buf, buflen);
+}
+
+static ssize_t netlink_oldrule_msg_encoder(struct zebra_dplane_ctx *ctx,
+ void *buf, size_t buflen)
+{
+ return netlink_rule_msg_encode(
+ RTM_DELRULE, ctx, dplane_ctx_rule_get_old_filter_bm(ctx),
+ dplane_ctx_rule_get_old_priority(ctx),
+ dplane_ctx_rule_get_old_table(ctx),
+ dplane_ctx_rule_get_old_src_ip(ctx),
+ dplane_ctx_rule_get_old_dst_ip(ctx),
+ dplane_ctx_rule_get_old_fwmark(ctx),
+ dplane_ctx_rule_get_old_dsfield(ctx), buf, buflen);
}
+
/* Public functions */
-/*
- * Add, update or delete a rule from the
- * kernel, using info from a dataplane context.
- */
-enum zebra_dplane_result kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx)
+enum netlink_msg_status
+netlink_put_rule_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx)
{
enum dplane_op_e op;
- int cmd;
- int ret;
+ enum netlink_msg_status ret;
op = dplane_ctx_get_op(ctx);
- if (op == DPLANE_OP_RULE_ADD || op == DPLANE_OP_RULE_UPDATE)
- cmd = RTM_NEWRULE;
- else if (op == DPLANE_OP_RULE_DELETE)
- cmd = RTM_DELRULE;
- else {
+ if (!(op == DPLANE_OP_RULE_ADD || op == DPLANE_OP_RULE_UPDATE
+ || op == DPLANE_OP_RULE_DELETE)) {
flog_err(
EC_ZEBRA_PBR_RULE_UPDATE,
"Context received for kernel rule update with incorrect OP code (%u)",
op);
- return ZEBRA_DPLANE_REQUEST_FAILURE;
+ return FRR_NETLINK_ERROR;
}
- ret = netlink_rule_update_internal(
- cmd, ctx, dplane_ctx_rule_get_filter_bm(ctx),
- dplane_ctx_rule_get_priority(ctx),
- dplane_ctx_rule_get_table(ctx), dplane_ctx_rule_get_src_ip(ctx),
- dplane_ctx_rule_get_dst_ip(ctx),
- dplane_ctx_rule_get_fwmark(ctx),
- dplane_ctx_rule_get_dsfield(ctx));
+ ret = netlink_batch_add_msg(bth, ctx, netlink_rule_msg_encoder, false);
/**
* Delete the old one.
@@ -202,19 +206,10 @@ enum zebra_dplane_result kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx)
* Don't care about this result right?
*/
if (op == DPLANE_OP_RULE_UPDATE)
- netlink_rule_update_internal(
- RTM_DELRULE, ctx,
- dplane_ctx_rule_get_old_filter_bm(ctx),
- dplane_ctx_rule_get_old_priority(ctx),
- dplane_ctx_rule_get_old_table(ctx),
- dplane_ctx_rule_get_old_src_ip(ctx),
- dplane_ctx_rule_get_old_dst_ip(ctx),
- dplane_ctx_rule_get_old_fwmark(ctx),
- dplane_ctx_rule_get_old_dsfield(ctx));
-
-
- return (ret == 0 ? ZEBRA_DPLANE_REQUEST_SUCCESS
- : ZEBRA_DPLANE_REQUEST_FAILURE);
+ netlink_batch_add_msg(bth, ctx, netlink_oldrule_msg_encoder,
+ true);
+
+ return ret;
}
/*
diff --git a/zebra/rule_netlink.h b/zebra/rule_netlink.h
index 8c4741dc06..cf4d978e78 100644
--- a/zebra/rule_netlink.h
+++ b/zebra/rule_netlink.h
@@ -40,6 +40,9 @@ extern int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup);
*/
extern int netlink_rules_read(struct zebra_ns *zns);
+extern enum netlink_msg_status
+netlink_put_rule_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx);
+
#ifdef __cplusplus
}
#endif
diff --git a/zebra/subdir.am b/zebra/subdir.am
index c552ca513e..1cea1a9f40 100644
--- a/zebra/subdir.am
+++ b/zebra/subdir.am
@@ -106,6 +106,7 @@ zebra_zebra_SOURCES = \
zebra/zebra_router.c \
zebra/zebra_rnh.c \
zebra/zebra_routemap.c \
+ zebra/zebra_srte.c \
zebra/zebra_vrf.c \
zebra/zebra_vty.c \
zebra/zebra_vxlan.c \
@@ -167,6 +168,7 @@ noinst_HEADERS += \
zebra/zebra_rnh.h \
zebra/zebra_routemap.h \
zebra/zebra_router.h \
+ zebra/zebra_srte.h \
zebra/zebra_vrf.h \
zebra/zebra_vxlan.h \
zebra/zebra_vxlan_private.h \
diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c
index 48aa566136..17114f820c 100644
--- a/zebra/zapi_msg.c
+++ b/zebra/zapi_msg.c
@@ -60,6 +60,7 @@
#include "zebra/zebra_mlag.h"
#include "zebra/connected.h"
#include "zebra/zebra_opaque.h"
+#include "zebra/zebra_srte.h"
/* Encoding helpers -------------------------------------------------------- */
@@ -1077,7 +1078,8 @@ static void zread_rnh_register(ZAPI_HANDLER_ARGS)
s = msg;
- client->nh_reg_time = monotime(NULL);
+ if (!client->nh_reg_time)
+ client->nh_reg_time = monotime(NULL);
while (l < hdr->length) {
STREAM_GETC(s, flags);
@@ -1651,6 +1653,11 @@ static void zread_route_add(ZAPI_HANDLER_ARGS)
return;
}
+ if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRTE)) {
+ SET_FLAG(nexthop->flags, NEXTHOP_FLAG_SRTE);
+ nexthop->srte_color = api_nh->srte_color;
+ }
+
/* MPLS labels for BGP-LU or Segment Routing */
if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL)
&& api_nh->type != NEXTHOP_TYPE_IFINDEX
@@ -1730,6 +1737,11 @@ static void zread_route_add(ZAPI_HANDLER_ARGS)
nexthop->backup_num = 0;
}
+ if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRTE)) {
+ SET_FLAG(nexthop->flags, NEXTHOP_FLAG_SRTE);
+ nexthop->srte_color = api_nh->srte_color;
+ }
+
/* MPLS labels for BGP-LU or Segment Routing */
if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL)
&& api_nh->type != NEXTHOP_TYPE_IFINDEX
@@ -2209,6 +2221,107 @@ static void zread_mpls_labels_replace(ZAPI_HANDLER_ARGS)
mpls_zapi_labels_process(true, zvrf, &zl);
}
+static void zread_sr_policy_set(ZAPI_HANDLER_ARGS)
+{
+ struct stream *s;
+ struct zapi_sr_policy zp;
+ struct zapi_srte_tunnel *zt;
+ struct zebra_sr_policy *policy;
+
+ /* Get input stream. */
+ s = msg;
+ if (zapi_sr_policy_decode(s, &zp) < 0) {
+ if (IS_ZEBRA_DEBUG_RECV)
+ zlog_debug("%s: Unable to decode zapi_sr_policy sent",
+ __PRETTY_FUNCTION__);
+ return;
+ }
+ zt = &zp.segment_list;
+ if (zt->label_num < 1) {
+ if (IS_ZEBRA_DEBUG_RECV)
+ zlog_debug(
+ "%s: SR-TE tunnel must contain at least one label",
+ __PRETTY_FUNCTION__);
+ return;
+ }
+
+ if (!mpls_enabled)
+ return;
+
+ policy = zebra_sr_policy_find(zp.color, &zp.endpoint);
+ if (!policy)
+ policy = zebra_sr_policy_add(zp.color, &zp.endpoint, zp.name);
+ /* TODO: per-VRF list of SR-TE policies. */
+ policy->zvrf = zvrf;
+
+ zebra_sr_policy_validate(policy, &zp.segment_list);
+}
+
+static void zread_sr_policy_delete(ZAPI_HANDLER_ARGS)
+{
+ struct stream *s;
+ struct zapi_sr_policy zp;
+ struct zebra_sr_policy *policy;
+
+ /* Get input stream. */
+ s = msg;
+ if (zapi_sr_policy_decode(s, &zp) < 0) {
+ if (IS_ZEBRA_DEBUG_RECV)
+ zlog_debug("%s: Unable to decode zapi_sr_policy sent",
+ __PRETTY_FUNCTION__);
+ return;
+ }
+
+ if (!mpls_enabled)
+ return;
+
+ policy = zebra_sr_policy_find(zp.color, &zp.endpoint);
+ if (!policy) {
+ if (IS_ZEBRA_DEBUG_RECV)
+ zlog_debug("%s: Unable to find SR-TE policy",
+ __PRETTY_FUNCTION__);
+ return;
+ }
+
+ zebra_sr_policy_del(policy);
+}
+
+int zsend_sr_policy_notify_status(uint32_t color, struct ipaddr *endpoint,
+ char *name, int status)
+{
+ struct zserv *client;
+ struct stream *s;
+
+ client = zserv_find_client(ZEBRA_ROUTE_SRTE, 0);
+ if (!client) {
+ if (IS_ZEBRA_DEBUG_PACKET)
+ zlog_debug(
+ "Not notifying pathd about policy %s"
+ " status change to %d",
+ name, status);
+ return 0;
+ }
+
+ if (IS_ZEBRA_DEBUG_PACKET)
+ zlog_debug(
+ "Notifying pathd about policy %s status change"
+ " to %d",
+ name, status);
+
+ s = stream_new(ZEBRA_MAX_PACKET_SIZ);
+ stream_reset(s);
+
+ zclient_create_header(s, ZEBRA_SR_POLICY_NOTIFY_STATUS, VRF_DEFAULT);
+ stream_putl(s, color);
+ stream_put_ipaddr(s, endpoint);
+ stream_write(s, name, SRTE_POLICY_NAME_MAX_LENGTH);
+ stream_putl(s, status);
+
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ return zserv_send_message(client, s);
+}
+
/* Send response to a table manager connect request to client */
static void zread_table_manager_connect(struct zserv *client,
struct stream *msg, vrf_id_t vrf_id)
@@ -2908,6 +3021,8 @@ void (*const zserv_handlers[])(ZAPI_HANDLER_ARGS) = {
[ZEBRA_BFD_CLIENT_REGISTER] = zebra_ptm_bfd_client_register,
[ZEBRA_INTERFACE_ENABLE_RADV] = zebra_interface_radv_enable,
[ZEBRA_INTERFACE_DISABLE_RADV] = zebra_interface_radv_disable,
+ [ZEBRA_SR_POLICY_SET] = zread_sr_policy_set,
+ [ZEBRA_SR_POLICY_DELETE] = zread_sr_policy_delete,
[ZEBRA_MPLS_LABELS_ADD] = zread_mpls_labels_add,
[ZEBRA_MPLS_LABELS_DELETE] = zread_mpls_labels_delete,
[ZEBRA_MPLS_LABELS_REPLACE] = zread_mpls_labels_replace,
diff --git a/zebra/zapi_msg.h b/zebra/zapi_msg.h
index 13beb6a273..e7c755c2bf 100644
--- a/zebra/zapi_msg.h
+++ b/zebra/zapi_msg.h
@@ -100,7 +100,9 @@ extern int zsend_assign_label_chunk_response(struct zserv *client,
extern int zsend_label_manager_connect_response(struct zserv *client,
vrf_id_t vrf_id,
unsigned short result);
-
+extern int zsend_sr_policy_notify_status(uint32_t color,
+ struct ipaddr *endpoint, char *name,
+ int status);
#ifdef __cplusplus
}
diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c
index 5dcf76db15..d34e1433ce 100644
--- a/zebra/zebra_dplane.c
+++ b/zebra/zebra_dplane.c
@@ -858,8 +858,6 @@ const char *dplane_res2str(enum zebra_dplane_result res)
case ZEBRA_DPLANE_REQUEST_SUCCESS:
ret = "SUCCESS";
break;
- case ZEBRA_DPLANE_REQUEST_PENDING:
- ret = "PENDING";
}
return ret;
@@ -1979,6 +1977,7 @@ int dplane_ctx_nexthop_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
* it probably won't require two messages
*/
dplane_ctx_ns_init(ctx, zns, (op == DPLANE_OP_NH_UPDATE));
+ ctx->zd_is_update = (op == DPLANE_OP_NH_UPDATE);
ret = AOK;
@@ -2001,6 +2000,7 @@ int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
/* Capture namespace info */
dplane_ctx_ns_init(ctx, zebra_ns_lookup(NS_DEFAULT),
(op == DPLANE_OP_LSP_UPDATE));
+ ctx->zd_is_update = (op == DPLANE_OP_LSP_UPDATE);
memset(&ctx->u.lsp, 0, sizeof(ctx->u.lsp));
@@ -2222,6 +2222,7 @@ static int dplane_ctx_rule_init(struct zebra_dplane_ctx *ctx,
dplane_ctx_ns_init(ctx, zebra_ns_lookup(NS_DEFAULT),
op == DPLANE_OP_RULE_UPDATE);
+ ctx->zd_is_update = (op == DPLANE_OP_RULE_UPDATE);
ctx->zd_vrf_id = new_rule->vrf_id;
memcpy(ctx->zd_ifname, new_rule->ifname, sizeof(new_rule->ifname));
@@ -3717,149 +3718,99 @@ void dplane_provider_enqueue_to_zebra(struct zebra_dplane_ctx *ctx)
* Kernel dataplane provider
*/
-/*
- * Handler for kernel LSP updates
- */
-static enum zebra_dplane_result
-kernel_dplane_lsp_update(struct zebra_dplane_ctx *ctx)
+static void kernel_dplane_log_detail(struct zebra_dplane_ctx *ctx)
{
- return kernel_lsp_update(ctx);
-}
+ char buf[PREFIX_STRLEN];
-/*
- * Handler for kernel pseudowire updates
- */
-static enum zebra_dplane_result
-kernel_dplane_pw_update(struct zebra_dplane_ctx *ctx)
-{
- if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
- zlog_debug("Dplane pw %s: op %s af %d loc: %u rem: %u",
- dplane_ctx_get_ifname(ctx),
- dplane_op2str(ctx->zd_op),
- dplane_ctx_get_pw_af(ctx),
- dplane_ctx_get_pw_local_label(ctx),
- dplane_ctx_get_pw_remote_label(ctx));
-
- return kernel_pw_update(ctx);
-}
-
-/*
- * Handler for kernel route updates
- */
-static enum zebra_dplane_result
-kernel_dplane_route_update(struct zebra_dplane_ctx *ctx)
-{
- if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
- char dest_str[PREFIX_STRLEN];
+ switch (dplane_ctx_get_op(ctx)) {
- prefix2str(dplane_ctx_get_dest(ctx),
- dest_str, sizeof(dest_str));
+ case DPLANE_OP_ROUTE_INSTALL:
+ case DPLANE_OP_ROUTE_UPDATE:
+ case DPLANE_OP_ROUTE_DELETE:
+ prefix2str(dplane_ctx_get_dest(ctx), buf, sizeof(buf));
zlog_debug("%u:%s Dplane route update ctx %p op %s",
- dplane_ctx_get_vrf(ctx), dest_str,
- ctx, dplane_op2str(dplane_ctx_get_op(ctx)));
- }
-
- return kernel_route_update(ctx);
-}
-
-/*
- * Handler for kernel-facing interface address updates
- */
-static enum zebra_dplane_result
-kernel_dplane_address_update(struct zebra_dplane_ctx *ctx)
-{
- if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
- char dest_str[PREFIX_STRLEN];
-
- prefix2str(dplane_ctx_get_intf_addr(ctx), dest_str,
- sizeof(dest_str));
-
- zlog_debug("Dplane intf %s, idx %u, addr %s",
- dplane_op2str(dplane_ctx_get_op(ctx)),
- dplane_ctx_get_ifindex(ctx), dest_str);
- }
-
- return kernel_address_update_ctx(ctx);
-}
+ dplane_ctx_get_vrf(ctx), buf, ctx,
+ dplane_op2str(dplane_ctx_get_op(ctx)));
+ break;
-/**
- * kernel_dplane_nexthop_update() - Handler for kernel nexthop updates
- *
- * @ctx: Dataplane context
- *
- * Return: Dataplane result flag
- */
-static enum zebra_dplane_result
-kernel_dplane_nexthop_update(struct zebra_dplane_ctx *ctx)
-{
- if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
+ case DPLANE_OP_NH_INSTALL:
+ case DPLANE_OP_NH_UPDATE:
+ case DPLANE_OP_NH_DELETE:
zlog_debug("ID (%u) Dplane nexthop update ctx %p op %s",
dplane_ctx_get_nhe_id(ctx), ctx,
dplane_op2str(dplane_ctx_get_op(ctx)));
- }
+ break;
- return kernel_nexthop_update(ctx);
-}
+ case DPLANE_OP_LSP_INSTALL:
+ case DPLANE_OP_LSP_UPDATE:
+ case DPLANE_OP_LSP_DELETE:
+ break;
-/*
- * Handler for kernel-facing EVPN MAC address updates
- */
-static enum zebra_dplane_result
-kernel_dplane_mac_update(struct zebra_dplane_ctx *ctx)
-{
- if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
- char buf[ETHER_ADDR_STRLEN];
+ case DPLANE_OP_PW_INSTALL:
+ case DPLANE_OP_PW_UNINSTALL:
+ zlog_debug("Dplane pw %s: op %s af %d loc: %u rem: %u",
+ dplane_ctx_get_ifname(ctx),
+ dplane_op2str(ctx->zd_op), dplane_ctx_get_pw_af(ctx),
+ dplane_ctx_get_pw_local_label(ctx),
+ dplane_ctx_get_pw_remote_label(ctx));
+ break;
+
+ case DPLANE_OP_ADDR_INSTALL:
+ case DPLANE_OP_ADDR_UNINSTALL:
+ prefix2str(dplane_ctx_get_intf_addr(ctx), buf, sizeof(buf));
+
+ zlog_debug("Dplane intf %s, idx %u, addr %s",
+ dplane_op2str(dplane_ctx_get_op(ctx)),
+ dplane_ctx_get_ifindex(ctx), buf);
+ break;
+ case DPLANE_OP_MAC_INSTALL:
+ case DPLANE_OP_MAC_DELETE:
prefix_mac2str(dplane_ctx_mac_get_addr(ctx), buf,
sizeof(buf));
zlog_debug("Dplane %s, mac %s, ifindex %u",
dplane_op2str(dplane_ctx_get_op(ctx)),
buf, dplane_ctx_get_ifindex(ctx));
- }
-
- return kernel_mac_update_ctx(ctx);
-}
-
-/*
- * Handler for kernel-facing EVPN neighbor updates
- */
-static enum zebra_dplane_result
-kernel_dplane_neigh_update(struct zebra_dplane_ctx *ctx)
-{
- if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
- char buf[PREFIX_STRLEN];
+ break;
+ case DPLANE_OP_NEIGH_INSTALL:
+ case DPLANE_OP_NEIGH_UPDATE:
+ case DPLANE_OP_NEIGH_DELETE:
+ case DPLANE_OP_VTEP_ADD:
+ case DPLANE_OP_VTEP_DELETE:
ipaddr2str(dplane_ctx_neigh_get_ipaddr(ctx), buf,
sizeof(buf));
zlog_debug("Dplane %s, ip %s, ifindex %u",
dplane_op2str(dplane_ctx_get_op(ctx)),
buf, dplane_ctx_get_ifindex(ctx));
- }
-
- return kernel_neigh_update_ctx(ctx);
-}
+ break;
-/*
- * Handler for kernel PBR rule updates
- */
-static enum zebra_dplane_result
-kernel_dplane_rule_update(struct zebra_dplane_ctx *ctx)
-{
- if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
+ case DPLANE_OP_RULE_ADD:
+ case DPLANE_OP_RULE_DELETE:
+ case DPLANE_OP_RULE_UPDATE:
zlog_debug("Dplane rule update op %s, if %s(%u), ctx %p",
dplane_op2str(dplane_ctx_get_op(ctx)),
dplane_ctx_get_ifname(ctx),
dplane_ctx_get_ifindex(ctx), ctx);
+ break;
+
+ case DPLANE_OP_SYS_ROUTE_ADD:
+ case DPLANE_OP_SYS_ROUTE_DELETE:
+ case DPLANE_OP_ROUTE_NOTIFY:
+ case DPLANE_OP_LSP_NOTIFY:
- return kernel_pbr_rule_update(ctx);
+ case DPLANE_OP_NONE:
+ break;
+ }
}
-static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx,
- enum zebra_dplane_result res)
+static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx)
{
+ enum zebra_dplane_result res = dplane_ctx_get_status(ctx);
+
switch (dplane_ctx_get_op(ctx)) {
case DPLANE_OP_ROUTE_INSTALL:
@@ -3868,6 +3819,27 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx,
if (res != ZEBRA_DPLANE_REQUEST_SUCCESS)
atomic_fetch_add_explicit(&zdplane_info.dg_route_errors,
1, memory_order_relaxed);
+
+ if ((dplane_ctx_get_op(ctx) != DPLANE_OP_ROUTE_DELETE)
+ && (res == ZEBRA_DPLANE_REQUEST_SUCCESS)) {
+ struct nexthop *nexthop;
+
+ /* Update installed nexthops to signal which have been
+ * installed.
+ */
+ for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx),
+ nexthop)) {
+ if (CHECK_FLAG(nexthop->flags,
+ NEXTHOP_FLAG_RECURSIVE))
+ continue;
+
+ if (CHECK_FLAG(nexthop->flags,
+ NEXTHOP_FLAG_ACTIVE)) {
+ SET_FLAG(nexthop->flags,
+ NEXTHOP_FLAG_FIB);
+ }
+ }
+ }
break;
case DPLANE_OP_NH_INSTALL:
@@ -3932,11 +3904,14 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx,
case DPLANE_OP_SYS_ROUTE_DELETE:
case DPLANE_OP_ROUTE_NOTIFY:
case DPLANE_OP_LSP_NOTIFY:
+ break;
+
case DPLANE_OP_NONE:
+ if (res != ZEBRA_DPLANE_REQUEST_SUCCESS)
+ atomic_fetch_add_explicit(&zdplane_info.dg_other_errors,
+ 1, memory_order_relaxed);
break;
}
-
- dplane_ctx_set_status(ctx, res);
}
/*
@@ -3944,7 +3919,6 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx,
*/
static int kernel_dplane_process_func(struct zebra_dplane_provider *prov)
{
- enum zebra_dplane_result res;
struct zebra_dplane_ctx *ctx, *tctx;
struct dplane_ctx_q work_list;
int counter, limit;
@@ -3958,97 +3932,21 @@ static int kernel_dplane_process_func(struct zebra_dplane_provider *prov)
dplane_provider_get_name(prov));
for (counter = 0; counter < limit; counter++) {
-
ctx = dplane_provider_dequeue_in_ctx(prov);
if (ctx == NULL)
break;
- /* A previous provider plugin may have asked to skip the
- * kernel update.
- */
- if (dplane_ctx_is_skip_kernel(ctx)) {
- res = ZEBRA_DPLANE_REQUEST_SUCCESS;
- goto skip_one;
- }
-
- /* Dispatch to appropriate kernel-facing apis */
- switch (dplane_ctx_get_op(ctx)) {
-
- case DPLANE_OP_ROUTE_INSTALL:
- case DPLANE_OP_ROUTE_UPDATE:
- case DPLANE_OP_ROUTE_DELETE:
- res = kernel_dplane_route_update(ctx);
- break;
-
- case DPLANE_OP_NH_INSTALL:
- case DPLANE_OP_NH_UPDATE:
- case DPLANE_OP_NH_DELETE:
- res = kernel_dplane_nexthop_update(ctx);
- break;
-
- case DPLANE_OP_LSP_INSTALL:
- case DPLANE_OP_LSP_UPDATE:
- case DPLANE_OP_LSP_DELETE:
- res = kernel_dplane_lsp_update(ctx);
- break;
-
- case DPLANE_OP_PW_INSTALL:
- case DPLANE_OP_PW_UNINSTALL:
- res = kernel_dplane_pw_update(ctx);
- break;
-
- case DPLANE_OP_ADDR_INSTALL:
- case DPLANE_OP_ADDR_UNINSTALL:
- res = kernel_dplane_address_update(ctx);
- break;
-
- case DPLANE_OP_MAC_INSTALL:
- case DPLANE_OP_MAC_DELETE:
- res = kernel_dplane_mac_update(ctx);
- break;
-
- case DPLANE_OP_NEIGH_INSTALL:
- case DPLANE_OP_NEIGH_UPDATE:
- case DPLANE_OP_NEIGH_DELETE:
- case DPLANE_OP_VTEP_ADD:
- case DPLANE_OP_VTEP_DELETE:
- res = kernel_dplane_neigh_update(ctx);
- break;
-
- case DPLANE_OP_RULE_ADD:
- case DPLANE_OP_RULE_DELETE:
- case DPLANE_OP_RULE_UPDATE:
- res = kernel_dplane_rule_update(ctx);
- break;
-
- /* 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;
-
- default:
- atomic_fetch_add_explicit(
- &zdplane_info.dg_other_errors, 1,
- memory_order_relaxed);
-
- res = ZEBRA_DPLANE_REQUEST_FAILURE;
- break;
- }
-
- skip_one:
- /* If the request isn't pending, we can handle the result right
- * away.
- */
- if (res != ZEBRA_DPLANE_REQUEST_PENDING)
- kernel_dplane_handle_result(ctx, res);
+ if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
+ kernel_dplane_log_detail(ctx);
TAILQ_INSERT_TAIL(&work_list, ctx, zd_q_entries);
}
+ kernel_update_multi(&work_list);
+
TAILQ_FOREACH_SAFE (ctx, &work_list, zd_q_entries, tctx) {
+ kernel_dplane_handle_result(ctx);
+
TAILQ_REMOVE(&work_list, ctx, zd_q_entries);
dplane_provider_enqueue_out_ctx(prov, ctx);
}
@@ -4093,7 +3991,6 @@ static int test_dplane_process_func(struct zebra_dplane_provider *prov)
limit = dplane_provider_get_work_limit(prov);
for (counter = 0; counter < limit; counter++) {
-
ctx = dplane_provider_dequeue_in_ctx(prov);
if (ctx == NULL)
break;
diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h
index 32032ed77d..3b5eda2486 100644
--- a/zebra/zebra_dplane.h
+++ b/zebra/zebra_dplane.h
@@ -90,7 +90,6 @@ enum zebra_dplane_result {
ZEBRA_DPLANE_REQUEST_QUEUED,
ZEBRA_DPLANE_REQUEST_SUCCESS,
ZEBRA_DPLANE_REQUEST_FAILURE,
- ZEBRA_DPLANE_REQUEST_PENDING,
};
/*
diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c
index 1ac7bc7a2a..03b8c8de1f 100644
--- a/zebra/zebra_mpls.c
+++ b/zebra/zebra_mpls.c
@@ -47,6 +47,7 @@
#include "zebra/zebra_memory.h"
#include "zebra/zebra_vrf.h"
#include "zebra/zebra_mpls.h"
+#include "zebra/zebra_srte.h"
#include "zebra/zebra_errors.h"
DEFINE_MTYPE_STATIC(ZEBRA, LSP, "MPLS LSP object")
@@ -955,9 +956,6 @@ static wq_item_status lsp_process(struct work_queue *wq, void *data)
case ZEBRA_DPLANE_REQUEST_SUCCESS:
zvrf->lsp_installs++;
break;
- /* Should never happen */
- case ZEBRA_DPLANE_REQUEST_PENDING:
- break;
}
}
} else {
@@ -983,10 +981,6 @@ static wq_item_status lsp_process(struct work_queue *wq, void *data)
case ZEBRA_DPLANE_REQUEST_SUCCESS:
zvrf->lsp_removals++;
break;
-
- /* Should never happen */
- case ZEBRA_DPLANE_REQUEST_PENDING:
- break;
}
} else if (CHECK_FLAG(lsp->flags, LSP_FLAG_CHANGED)) {
zebra_nhlfe_t *nhlfe;
@@ -1030,10 +1024,6 @@ static wq_item_status lsp_process(struct work_queue *wq, void *data)
case ZEBRA_DPLANE_REQUEST_SUCCESS:
zvrf->lsp_installs++;
break;
-
- /* Should never happen */
- case ZEBRA_DPLANE_REQUEST_PENDING:
- break;
}
}
}
@@ -1505,6 +1495,7 @@ static json_object *nhlfe_json(zebra_nhlfe_t *nhlfe)
char buf[BUFSIZ];
json_object *json_nhlfe = NULL;
json_object *json_backups = NULL;
+ json_object *json_label_stack;
struct nexthop *nexthop = nhlfe->nexthop;
int i;
@@ -1512,6 +1503,14 @@ static json_object *nhlfe_json(zebra_nhlfe_t *nhlfe)
json_object_string_add(json_nhlfe, "type", nhlfe_type2str(nhlfe->type));
json_object_int_add(json_nhlfe, "outLabel",
nexthop->nh_label->label[0]);
+
+ json_label_stack = json_object_new_array();
+ json_object_object_add(json_nhlfe, "outLabelStack", json_label_stack);
+ for (i = 0; i < nexthop->nh_label->num_labels; i++)
+ json_object_array_add(
+ json_label_stack,
+ json_object_new_int(nexthop->nh_label->label[i]));
+
json_object_int_add(json_nhlfe, "distance", nhlfe->distance);
if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED))
@@ -1916,14 +1915,13 @@ static int mpls_processq_init(void)
}
-/* Public functions */
-
/*
* Process LSP update results from zebra dataplane.
*/
void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx)
{
struct zebra_vrf *zvrf;
+ mpls_label_t label;
zebra_ile_t tmp_ile;
struct hash *lsp_table;
zebra_lsp_t *lsp;
@@ -1931,6 +1929,7 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx)
struct nexthop *nexthop;
enum dplane_op_e op;
enum zebra_dplane_result status;
+ enum zebra_sr_policy_update_label_mode update_mode;
op = dplane_ctx_get_op(ctx);
status = dplane_ctx_get_status(ctx);
@@ -1941,6 +1940,8 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx)
dplane_ctx_get_in_label(ctx),
dplane_res2str(status));
+ label = dplane_ctx_get_in_label(ctx);
+
switch (op) {
case DPLANE_OP_LSP_INSTALL:
case DPLANE_OP_LSP_UPDATE:
@@ -1951,7 +1952,7 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx)
lsp_table = zvrf->lsp_table;
- tmp_ile.in_label = dplane_ctx_get_in_label(ctx);
+ tmp_ile.in_label = label;
lsp = hash_lookup(lsp_table, &tmp_ile);
if (lsp == NULL) {
if (IS_ZEBRA_DEBUG_DPLANE)
@@ -1985,13 +1986,21 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx)
}
}
+ update_mode = (op == DPLANE_OP_LSP_INSTALL)
+ ? ZEBRA_SR_POLICY_LABEL_CREATED
+ : ZEBRA_SR_POLICY_LABEL_UPDATED;
+ zebra_sr_policy_label_update(label, update_mode);
break;
case DPLANE_OP_LSP_DELETE:
- if (status != ZEBRA_DPLANE_REQUEST_SUCCESS)
+ if (status != ZEBRA_DPLANE_REQUEST_SUCCESS) {
flog_warn(EC_ZEBRA_LSP_DELETE_FAILURE,
"LSP Deletion Failure: in-label %u",
dplane_ctx_get_in_label(ctx));
+ break;
+ }
+ zebra_sr_policy_label_update(label,
+ ZEBRA_SR_POLICY_LABEL_REMOVED);
break;
default:
@@ -3419,6 +3428,21 @@ static int lsp_backup_znh_install(zebra_lsp_t *lsp, enum lsp_types_t type,
return 0;
}
+zebra_lsp_t *mpls_lsp_find(struct zebra_vrf *zvrf, mpls_label_t in_label)
+{
+ struct hash *lsp_table;
+ zebra_ile_t tmp_ile;
+
+ /* Lookup table. */
+ lsp_table = zvrf->lsp_table;
+ if (!lsp_table)
+ return NULL;
+
+ /* If entry is not present, exit. */
+ tmp_ile.in_label = in_label;
+ return hash_lookup(lsp_table, &tmp_ile);
+}
+
/*
* Uninstall a particular NHLFE in the forwarding table. If this is
* the only NHLFE, the entire LSP forwarding entry has to be deleted.
diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h
index 07a8efeb8b..c0e58c44e3 100644
--- a/zebra/zebra_mpls.h
+++ b/zebra/zebra_mpls.h
@@ -33,6 +33,7 @@
#include "mpls.h"
#include "zebra/zserv.h"
#include "zebra/zebra_vrf.h"
+#include "hook.h"
#ifdef __cplusplus
extern "C" {
@@ -326,6 +327,11 @@ int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type,
const union g_addr *gate, ifindex_t ifindex);
/*
+ * Lookup LSP by its input label.
+ */
+zebra_lsp_t *mpls_lsp_find(struct zebra_vrf *zvrf, mpls_label_t in_label);
+
+/*
* Uninstall a particular NHLFE in the forwarding table. If this is
* the only NHLFE, the entire LSP forwarding entry has to be deleted.
*/
@@ -461,6 +467,7 @@ static inline uint8_t lsp_distance(enum lsp_types_t type)
case ZEBRA_LSP_SHARP:
case ZEBRA_LSP_OSPF_SR:
case ZEBRA_LSP_ISIS_SR:
+ case ZEBRA_LSP_SRTE:
return 150;
}
@@ -492,6 +499,8 @@ static inline enum lsp_types_t lsp_type_from_re_type(int re_type)
return ZEBRA_LSP_ISIS_SR;
case ZEBRA_ROUTE_SHARP:
return ZEBRA_LSP_SHARP;
+ case ZEBRA_ROUTE_SRTE:
+ return ZEBRA_LSP_SRTE;
default:
return ZEBRA_LSP_NONE;
}
@@ -517,6 +526,8 @@ static inline int re_type_from_lsp_type(enum lsp_types_t lsp_type)
return ZEBRA_ROUTE_KERNEL;
case ZEBRA_LSP_SHARP:
return ZEBRA_ROUTE_SHARP;
+ case ZEBRA_LSP_SRTE:
+ return ZEBRA_ROUTE_SRTE;
}
/*
@@ -544,6 +555,8 @@ static inline const char *nhlfe_type2str(enum lsp_types_t lsp_type)
return "SR (IS-IS)";
case ZEBRA_LSP_SHARP:
return "SHARP";
+ case ZEBRA_LSP_SRTE:
+ return "SR-TE";
case ZEBRA_LSP_NONE:
return "Unknown";
}
diff --git a/zebra/zebra_mpls_netlink.c b/zebra/zebra_mpls_netlink.c
index c95a021639..3b2279c66c 100644
--- a/zebra/zebra_mpls_netlink.c
+++ b/zebra/zebra_mpls_netlink.c
@@ -28,13 +28,9 @@
#include "zebra/zebra_mpls.h"
#include "zebra/kernel_netlink.h"
-/*
- * LSP forwarding update using dataplane context information.
- */
-enum zebra_dplane_result kernel_lsp_update(struct zebra_dplane_ctx *ctx)
+static ssize_t netlink_lsp_msg_encoder(struct zebra_dplane_ctx *ctx, void *buf,
+ size_t buflen)
{
- uint8_t nl_pkt[NL_PKT_BUF_SIZE];
- ssize_t ret = -1;
int cmd;
/* Call to netlink layer based on type of update */
@@ -48,26 +44,21 @@ enum zebra_dplane_result kernel_lsp_update(struct zebra_dplane_ctx *ctx)
if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_MPLS)
zlog_debug("LSP in-label %u: update fails, no best NHLFE",
dplane_ctx_get_in_label(ctx));
- goto done;
+ return -1;
}
cmd = RTM_NEWROUTE;
} else
/* Invalid op? */
- goto done;
-
- ret = netlink_mpls_multipath_msg_encode(cmd, ctx, nl_pkt,
- sizeof(nl_pkt));
- if (ret <= 0)
- return ZEBRA_DPLANE_REQUEST_FAILURE;
-
- ret = netlink_talk_info(netlink_talk_filter, (struct nlmsghdr *)nl_pkt,
- dplane_ctx_get_ns(ctx), 0);
+ return -1;
-done:
+ return netlink_mpls_multipath_msg_encode(cmd, ctx, buf, buflen);
+}
- return (ret == 0 ?
- ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE);
+enum netlink_msg_status netlink_put_lsp_update_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx)
+{
+ return netlink_batch_add_msg(bth, ctx, netlink_lsp_msg_encoder, false);
}
/*
@@ -75,9 +66,10 @@ done:
* but note that the default has been to report 'success' for pw updates
* on unsupported platforms.
*/
-enum zebra_dplane_result kernel_pw_update(struct zebra_dplane_ctx *ctx)
+enum netlink_msg_status netlink_put_pw_update_msg(struct nl_batch *bth,
+ struct zebra_dplane_ctx *ctx)
{
- return ZEBRA_DPLANE_REQUEST_SUCCESS;
+ return FRR_NETLINK_SUCCESS;
}
int mpls_kernel_init(void)
diff --git a/zebra/zebra_nb_config.c b/zebra/zebra_nb_config.c
index e4501273bd..b4ed910b4d 100644
--- a/zebra/zebra_nb_config.c
+++ b/zebra/zebra_nb_config.c
@@ -1204,12 +1204,14 @@ int lib_vrf_zebra_l3vni_id_modify(struct nb_cb_modify_args *args)
switch (args->event) {
case NB_EV_PREPARE:
case NB_EV_ABORT:
- break;
+ return NB_OK;
case NB_EV_VALIDATE:
zvrf_evpn = zebra_vrf_get_evpn();
- if (!zvrf_evpn)
+ if (!zvrf_evpn) {
+ snprintf(args->errmsg, args->errmsg_len,
+ "evpn vrf is not present.");
return NB_ERR_VALIDATION;
-
+ }
vni = yang_dnode_get_uint32(args->dnode, NULL);
/* Get vrf info from parent node, reject configuration
* if zebra vrf already mapped to different vni id.
@@ -1217,10 +1219,16 @@ int lib_vrf_zebra_l3vni_id_modify(struct nb_cb_modify_args *args)
pn_dnode = yang_dnode_get_parent(args->dnode, "vrf");
if (pn_dnode) {
vrfname = yang_dnode_get_string(pn_dnode, "./name");
- vrf = vrf_lookup_by_name(vrfname);
- zvrf = zebra_vrf_lookup_by_id(vrf->vrf_id);
+ zvrf = zebra_vrf_lookup_by_name(vrfname);
+ if (!zvrf) {
+ snprintf(args->errmsg, args->errmsg_len,
+ "zebra vrf info not found for vrf:%s.",
+ vrfname);
+ return NB_ERR_VALIDATION;
+ }
if (zvrf->l3vni && zvrf->l3vni != vni) {
- zlog_debug(
+ snprintf(
+ args->errmsg, args->errmsg_len,
"vni %u cannot be configured as vni %u is already configured under the vrf",
vni, zvrf->l3vni);
return NB_ERR_VALIDATION;
@@ -1230,10 +1238,8 @@ int lib_vrf_zebra_l3vni_id_modify(struct nb_cb_modify_args *args)
/* Check if this VNI is already present in the system */
zl3vni = zl3vni_lookup(vni);
if (zl3vni) {
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
- "VNI %u is already configured as L3-VNI",
- vni);
+ snprintf(args->errmsg, args->errmsg_len,
+ "VNI %u is already configured as L3-VNI", vni);
return NB_ERR_VALIDATION;
}
@@ -1241,7 +1247,7 @@ int lib_vrf_zebra_l3vni_id_modify(struct nb_cb_modify_args *args)
case NB_EV_APPLY:
vrf = nb_running_get_entry(args->dnode, NULL, true);
- zvrf = zebra_vrf_lookup_by_id(vrf->vrf_id);
+ zvrf = zebra_vrf_lookup_by_name(vrf->name);
vni = yang_dnode_get_uint32(args->dnode, NULL);
/* Note: This covers lib_vrf_zebra_prefix_only_modify() config
* along with l3vni config
@@ -1252,7 +1258,8 @@ int lib_vrf_zebra_l3vni_id_modify(struct nb_cb_modify_args *args)
pfx_only ? 1 : 0, 1)
!= 0) {
if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
+ snprintf(
+ args->errmsg, args->errmsg_len,
"vrf vni %u mapping failed with error: %s",
vni, err);
return NB_ERR;
@@ -1279,17 +1286,21 @@ int lib_vrf_zebra_l3vni_id_destroy(struct nb_cb_destroy_args *args)
case NB_EV_PREPARE:
case NB_EV_ABORT:
case NB_EV_VALIDATE:
- break;
+ return NB_OK;
case NB_EV_APPLY:
vrf = nb_running_get_entry(args->dnode, NULL, true);
- zvrf = zebra_vrf_lookup_by_id(vrf->vrf_id);
+ zvrf = zebra_vrf_lookup_by_name(vrf->name);
vni = yang_dnode_get_uint32(args->dnode, NULL);
if (!zl3vni_lookup(vni))
return NB_OK;
- if (zvrf->l3vni != vni)
+ if (zvrf->l3vni != vni) {
+ snprintf(args->errmsg, args->errmsg_len,
+ "vrf %s has different vni %u mapped",
+ vrf->name, zvrf->l3vni);
return NB_ERR;
+ }
if (is_l3vni_for_prefix_routes_only(zvrf->l3vni))
filter = 1;
diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c
index 43bf745896..1c5b843539 100644
--- a/zebra/zebra_nhg.c
+++ b/zebra/zebra_nhg.c
@@ -28,6 +28,7 @@
#include "lib/mpls.h"
#include "lib/jhash.h"
#include "lib/debug.h"
+#include "lib/lib_errors.h"
#include "zebra/connected.h"
#include "zebra/debug.h"
@@ -36,6 +37,7 @@
#include "zebra/zebra_rnh.h"
#include "zebra/zebra_routemap.h"
#include "zebra/zebra_memory.h"
+#include "zebra/zebra_srte.h"
#include "zebra/zserv.h"
#include "zebra/rt.h"
#include "zebra_errors.h"
@@ -1620,7 +1622,8 @@ void zebra_nhg_increment_ref(struct nhg_hash_entry *nhe)
}
static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop,
- struct nexthop *nexthop)
+ struct nexthop *nexthop,
+ struct zebra_sr_policy *policy)
{
struct nexthop *resolved_hop;
uint8_t num_labels = 0;
@@ -1684,7 +1687,21 @@ static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop,
resolved_hop->flags |= NEXTHOP_FLAG_ONLINK;
/* Copy labels of the resolved route and the parent resolving to it */
- if (newhop->nh_label) {
+ if (policy) {
+ int i = 0;
+
+ /*
+ * Don't push the first SID if the corresponding action in the
+ * LFIB is POP.
+ */
+ if (!newhop->nh_label || !newhop->nh_label->num_labels
+ || newhop->nh_label->label[0] == MPLS_LABEL_IMPLICIT_NULL)
+ i = 1;
+
+ for (; i < policy->segment_list.label_num; i++)
+ labels[num_labels++] = policy->segment_list.labels[i];
+ label_type = policy->segment_list.type;
+ } else if (newhop->nh_label) {
for (i = 0; i < newhop->nh_label->num_labels; i++) {
/* Be a bit picky about overrunning the local array */
if (num_labels >= MPLS_MAX_LABELS) {
@@ -1771,11 +1788,13 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
struct route_node *rn;
struct route_entry *match = NULL;
int resolved;
+ zebra_nhlfe_t *nhlfe;
struct nexthop *newhop;
struct interface *ifp;
rib_dest_t *dest;
struct zebra_vrf *zvrf;
- struct in_addr ipv4;
+ struct in_addr local_ipv4;
+ struct in_addr *ipv4;
if ((nexthop->type == NEXTHOP_TYPE_IPV4)
|| nexthop->type == NEXTHOP_TYPE_IPV6)
@@ -1839,6 +1858,51 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
/* Validation for ipv4 mapped ipv6 nexthop. */
if (IS_MAPPED_IPV6(&nexthop->gate.ipv6)) {
afi = AFI_IP;
+ ipv4 = &local_ipv4;
+ ipv4_mapped_ipv6_to_ipv4(&nexthop->gate.ipv6, ipv4);
+ } else {
+ ipv4 = &nexthop->gate.ipv4;
+ }
+
+ if (nexthop->srte_color) {
+ struct ipaddr endpoint = {0};
+ struct zebra_sr_policy *policy;
+
+ switch (afi) {
+ case AFI_IP:
+ endpoint.ipa_type = IPADDR_V4;
+ endpoint.ipaddr_v4 = *ipv4;
+ break;
+ case AFI_IP6:
+ endpoint.ipa_type = IPADDR_V6;
+ endpoint.ipaddr_v6 = nexthop->gate.ipv6;
+ break;
+ default:
+ flog_err(EC_LIB_DEVELOPMENT,
+ "%s: unknown address-family: %u", __func__,
+ afi);
+ exit(1);
+ }
+
+ policy = zebra_sr_policy_find(nexthop->srte_color, &endpoint);
+ if (policy && policy->status == ZEBRA_SR_POLICY_UP) {
+ resolved = 0;
+ frr_each_safe (nhlfe_list, &policy->lsp->nhlfe_list,
+ nhlfe) {
+ if (!CHECK_FLAG(nhlfe->flags,
+ NHLFE_FLAG_SELECTED)
+ || CHECK_FLAG(nhlfe->flags,
+ NHLFE_FLAG_DELETED))
+ continue;
+ SET_FLAG(nexthop->flags,
+ NEXTHOP_FLAG_RECURSIVE);
+ nexthop_set_resolved(afi, nhlfe->nexthop,
+ nexthop, policy);
+ resolved = 1;
+ }
+ if (resolved)
+ return 1;
+ }
}
/* Make lookup prefix. */
@@ -1847,12 +1911,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
case AFI_IP:
p.family = AF_INET;
p.prefixlen = IPV4_MAX_PREFIXLEN;
- if (IS_MAPPED_IPV6(&nexthop->gate.ipv6)) {
- ipv4_mapped_ipv6_to_ipv4(&nexthop->gate.ipv6, &ipv4);
- p.u.prefix4 = ipv4;
- } else {
- p.u.prefix4 = nexthop->gate.ipv4;
- }
+ p.u.prefix4 = *ipv4;
break;
case AFI_IP6:
p.family = AF_INET6;
@@ -1978,7 +2037,8 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
SET_FLAG(nexthop->flags,
NEXTHOP_FLAG_RECURSIVE);
- nexthop_set_resolved(afi, newhop, nexthop);
+ nexthop_set_resolved(afi, newhop, nexthop,
+ NULL);
resolved = 1;
}
@@ -2001,7 +2061,8 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
SET_FLAG(nexthop->flags,
NEXTHOP_FLAG_RECURSIVE);
- nexthop_set_resolved(afi, newhop, nexthop);
+ nexthop_set_resolved(afi, newhop, nexthop,
+ NULL);
resolved = 1;
}
done_with_match:
diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h
index dd2d7a190d..888d2fcfa0 100644
--- a/zebra/zebra_pbr.h
+++ b/zebra/zebra_pbr.h
@@ -172,13 +172,6 @@ void zebra_pbr_add_iptable(struct zebra_pbr_iptable *iptable);
void zebra_pbr_del_iptable(struct zebra_pbr_iptable *iptable);
/*
- * Add, update or delete a rule from the
- * kernel, using info from a dataplane context.
- */
-extern enum zebra_dplane_result
-kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx);
-
-/*
* Get to know existing PBR rules in the kernel - typically called at startup.
*/
extern void kernel_read_pbr_rules(struct zebra_ns *zns);
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index ee9d5d76e0..55dccc4a12 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -106,7 +106,8 @@ static const struct {
[ZEBRA_ROUTE_PBR] = {ZEBRA_ROUTE_PBR, 200, 5},
[ZEBRA_ROUTE_BFD] = {ZEBRA_ROUTE_BFD, 255, 5},
[ZEBRA_ROUTE_OPENFABRIC] = {ZEBRA_ROUTE_OPENFABRIC, 115, 3},
- [ZEBRA_ROUTE_VRRP] = {ZEBRA_ROUTE_VRRP, 255, 5}
+ [ZEBRA_ROUTE_VRRP] = {ZEBRA_ROUTE_VRRP, 255, 5},
+ [ZEBRA_ROUTE_SRTE] = {ZEBRA_ROUTE_SRTE, 255, 5},
/* Any new route type added to zebra, should be mirrored here */
/* no entry/default: 150 */
@@ -496,9 +497,6 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re,
if (zvrf)
zvrf->installs++;
break;
- /* Should never happen */
- case ZEBRA_DPLANE_REQUEST_PENDING:
- break;
}
return;
@@ -543,9 +541,6 @@ void rib_uninstall_kernel(struct route_node *rn, struct route_entry *re)
if (zvrf)
zvrf->removals++;
break;
- /* Should never happen */
- case ZEBRA_DPLANE_REQUEST_PENDING:
- break;
}
return;
diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c
index b151e90a92..cdc00f6026 100644
--- a/zebra/zebra_rnh.c
+++ b/zebra/zebra_rnh.c
@@ -46,6 +46,7 @@
#include "zebra/debug.h"
#include "zebra/zebra_rnh.h"
#include "zebra/zebra_routemap.h"
+#include "zebra/zebra_srte.h"
#include "zebra/interface.h"
#include "zebra/zebra_memory.h"
#include "zebra/zebra_errors.h"
@@ -57,8 +58,6 @@ static void free_state(vrf_id_t vrf_id, struct route_entry *re,
static void copy_state(struct rnh *rnh, const struct route_entry *re,
struct route_node *rn);
static int compare_state(struct route_entry *r1, struct route_entry *r2);
-static int send_client(struct rnh *rnh, struct zserv *client,
- enum rnh_type type, vrf_id_t vrf_id);
static void print_rnh(struct route_node *rn, struct vty *vty);
static int zebra_client_cleanup_rnh(struct zserv *client);
@@ -305,7 +304,7 @@ void zebra_add_rnh_client(struct rnh *rnh, struct zserv *client,
* We always need to respond with known information,
* currently multiple daemons expect this behavior
*/
- send_client(rnh, client, type, vrf_id);
+ zebra_send_rnh_update(rnh, client, type, vrf_id, 0);
}
void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client,
@@ -531,8 +530,9 @@ static void zebra_rnh_eval_import_check_entry(struct zebra_vrf *zvrf, afi_t afi,
}
/* state changed, notify clients */
for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) {
- send_client(rnh, client,
- RNH_IMPORT_CHECK_TYPE, zvrf->vrf->vrf_id);
+ zebra_send_rnh_update(rnh, client,
+ RNH_IMPORT_CHECK_TYPE,
+ zvrf->vrf->vrf_id, 0);
}
}
}
@@ -594,7 +594,8 @@ static void zebra_rnh_notify_protocol_clients(struct zebra_vrf *zvrf, afi_t afi,
zebra_route_string(client->proto));
}
- send_client(rnh, client, RNH_NEXTHOP_TYPE, zvrf->vrf->vrf_id);
+ zebra_send_rnh_update(rnh, client, RNH_NEXTHOP_TYPE,
+ zvrf->vrf->vrf_id, 0);
}
if (re)
@@ -1107,8 +1108,9 @@ static int compare_state(struct route_entry *r1, struct route_entry *r2)
return 0;
}
-static int send_client(struct rnh *rnh, struct zserv *client,
- enum rnh_type type, vrf_id_t vrf_id)
+int zebra_send_rnh_update(struct rnh *rnh, struct zserv *client,
+ enum rnh_type type, vrf_id_t vrf_id,
+ uint32_t srte_color)
{
struct stream *s = NULL;
struct route_entry *re;
@@ -1117,6 +1119,7 @@ static int send_client(struct rnh *rnh, struct zserv *client,
struct nexthop *nh;
struct route_node *rn;
int ret;
+ uint32_t message = 0;
int cmd = (type == RNH_IMPORT_CHECK_TYPE) ? ZEBRA_IMPORT_CHECK_UPDATE
: ZEBRA_NEXTHOP_UPDATE;
@@ -1128,6 +1131,11 @@ static int send_client(struct rnh *rnh, struct zserv *client,
zclient_create_header(s, cmd, vrf_id);
+ /* Message flags. */
+ if (srte_color)
+ SET_FLAG(message, ZAPI_MESSAGE_SRTE);
+ stream_putl(s, message);
+
stream_putw(s, rn->p.family);
switch (rn->p.family) {
case AF_INET:
@@ -1144,6 +1152,9 @@ static int send_client(struct rnh *rnh, struct zserv *client,
__func__, rn->p.family);
goto failure;
}
+ if (srte_color)
+ stream_putl(s, srte_color);
+
if (re) {
struct zapi_nexthop znh;
struct nexthop_group *nhg;
@@ -1160,7 +1171,7 @@ static int send_client(struct rnh *rnh, struct zserv *client,
for (ALL_NEXTHOPS_PTR(nhg, nh))
if (rnh_nexthop_valid(re, nh)) {
zapi_nexthop_from_nexthop(&znh, nh);
- ret = zapi_nexthop_encode(s, &znh, 0/*flags*/);
+ ret = zapi_nexthop_encode(s, &znh, 0, message);
if (ret < 0)
goto failure;
@@ -1173,7 +1184,8 @@ static int send_client(struct rnh *rnh, struct zserv *client,
if (rnh_nexthop_valid(re, nh)) {
zapi_nexthop_from_nexthop(&znh, nh);
ret = zapi_nexthop_encode(
- s, &znh, 0 /* flags */);
+ s, &znh, 0 /* flags */,
+ 0 /* message */);
if (ret < 0)
goto failure;
diff --git a/zebra/zebra_rnh.h b/zebra/zebra_rnh.h
index e744504920..ba12b1738f 100644
--- a/zebra/zebra_rnh.h
+++ b/zebra/zebra_rnh.h
@@ -50,6 +50,9 @@ extern struct rnh *zebra_lookup_rnh(struct prefix *p, vrf_id_t vrfid,
extern void zebra_free_rnh(struct rnh *rnh);
extern void zebra_add_rnh_client(struct rnh *rnh, struct zserv *client,
enum rnh_type type, vrf_id_t vrfid);
+extern int zebra_send_rnh_update(struct rnh *rnh, struct zserv *client,
+ enum rnh_type type, vrf_id_t vrf_id,
+ uint32_t srte_color);
extern void zebra_register_rnh_pseudowire(vrf_id_t, struct zebra_pw *, bool *);
extern void zebra_deregister_rnh_pseudowire(vrf_id_t, struct zebra_pw *);
extern void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client,
diff --git a/zebra/zebra_srte.c b/zebra/zebra_srte.c
new file mode 100644
index 0000000000..d6043534e3
--- /dev/null
+++ b/zebra/zebra_srte.c
@@ -0,0 +1,378 @@
+/* Zebra SR-TE code
+ * Copyright (C) 2020 NetDEF, Inc.
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "lib/zclient.h"
+#include "lib/lib_errors.h"
+
+#include "zebra/zebra_srte.h"
+#include "zebra/zebra_memory.h"
+#include "zebra/zebra_mpls.h"
+#include "zebra/zebra_rnh.h"
+#include "zebra/zapi_msg.h"
+
+DEFINE_MTYPE_STATIC(ZEBRA, ZEBRA_SR_POLICY, "SR Policy")
+
+static void zebra_sr_policy_deactivate(struct zebra_sr_policy *policy);
+
+/* Generate rb-tree of SR Policy instances. */
+static inline int
+zebra_sr_policy_instance_compare(const struct zebra_sr_policy *a,
+ const struct zebra_sr_policy *b)
+{
+ return sr_policy_compare(&a->endpoint, &b->endpoint, a->color,
+ b->color);
+}
+RB_GENERATE(zebra_sr_policy_instance_head, zebra_sr_policy, entry,
+ zebra_sr_policy_instance_compare)
+
+struct zebra_sr_policy_instance_head zebra_sr_policy_instances =
+ RB_INITIALIZER(&zebra_sr_policy_instances);
+
+struct zebra_sr_policy *zebra_sr_policy_add(uint32_t color,
+ struct ipaddr *endpoint, char *name)
+{
+ struct zebra_sr_policy *policy;
+
+ policy = XCALLOC(MTYPE_ZEBRA_SR_POLICY, sizeof(*policy));
+ policy->color = color;
+ policy->endpoint = *endpoint;
+ strlcpy(policy->name, name, sizeof(policy->name));
+ policy->status = ZEBRA_SR_POLICY_DOWN;
+ RB_INSERT(zebra_sr_policy_instance_head, &zebra_sr_policy_instances,
+ policy);
+
+ return policy;
+}
+
+void zebra_sr_policy_del(struct zebra_sr_policy *policy)
+{
+ if (policy->status == ZEBRA_SR_POLICY_UP)
+ zebra_sr_policy_deactivate(policy);
+ RB_REMOVE(zebra_sr_policy_instance_head, &zebra_sr_policy_instances,
+ policy);
+ XFREE(MTYPE_ZEBRA_SR_POLICY, policy);
+}
+
+struct zebra_sr_policy *zebra_sr_policy_find(uint32_t color,
+ struct ipaddr *endpoint)
+{
+ struct zebra_sr_policy policy = {};
+
+ policy.color = color;
+ policy.endpoint = *endpoint;
+ return RB_FIND(zebra_sr_policy_instance_head,
+ &zebra_sr_policy_instances, &policy);
+}
+
+struct zebra_sr_policy *zebra_sr_policy_find_by_name(char *name)
+{
+ struct zebra_sr_policy *policy;
+
+ // TODO: create index for policy names
+ RB_FOREACH (policy, zebra_sr_policy_instance_head,
+ &zebra_sr_policy_instances) {
+ if (strcmp(policy->name, name) == 0)
+ return policy;
+ }
+
+ return NULL;
+}
+
+static int zebra_sr_policy_notify_update_client(struct zebra_sr_policy *policy,
+ struct zserv *client)
+{
+ const zebra_nhlfe_t *nhlfe;
+ struct stream *s;
+ uint32_t message = 0;
+ unsigned long nump = 0;
+ uint8_t num;
+ struct zapi_nexthop znh;
+ int ret;
+
+ /* Get output stream. */
+ s = stream_new(ZEBRA_MAX_PACKET_SIZ);
+
+ zclient_create_header(s, ZEBRA_NEXTHOP_UPDATE, zvrf_id(policy->zvrf));
+
+ /* Message flags. */
+ SET_FLAG(message, ZAPI_MESSAGE_SRTE);
+ stream_putl(s, message);
+
+ switch (policy->endpoint.ipa_type) {
+ case IPADDR_V4:
+ stream_putw(s, AF_INET);
+ stream_putc(s, IPV4_MAX_BITLEN);
+ stream_put_in_addr(s, &policy->endpoint.ipaddr_v4);
+ break;
+ case IPADDR_V6:
+ stream_putw(s, AF_INET6);
+ stream_putc(s, IPV6_MAX_BITLEN);
+ stream_put(s, &policy->endpoint.ipaddr_v6, IPV6_MAX_BYTELEN);
+ break;
+ default:
+ flog_warn(EC_LIB_DEVELOPMENT,
+ "%s: unknown policy endpoint address family: %u",
+ __func__, policy->endpoint.ipa_type);
+ exit(1);
+ }
+ stream_putl(s, policy->color);
+
+ num = 0;
+ frr_each (nhlfe_list_const, &policy->lsp->nhlfe_list, nhlfe) {
+ if (!CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED)
+ || CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED))
+ continue;
+
+ if (num == 0) {
+ stream_putc(s, re_type_from_lsp_type(nhlfe->type));
+ stream_putw(s, 0); /* instance - not available */
+ stream_putc(s, nhlfe->distance);
+ stream_putl(s, 0); /* metric - not available */
+ nump = stream_get_endp(s);
+ stream_putc(s, 0);
+ }
+
+ zapi_nexthop_from_nexthop(&znh, nhlfe->nexthop);
+ ret = zapi_nexthop_encode(s, &znh, 0, message);
+ if (ret < 0)
+ goto failure;
+
+ num++;
+ }
+ stream_putc_at(s, nump, num);
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ client->nh_last_upd_time = monotime(NULL);
+ client->last_write_cmd = ZEBRA_NEXTHOP_UPDATE;
+ return zserv_send_message(client, s);
+
+failure:
+
+ stream_free(s);
+ return -1;
+}
+
+static void zebra_sr_policy_notify_update(struct zebra_sr_policy *policy)
+{
+ struct rnh *rnh;
+ struct prefix p = {};
+ struct zebra_vrf *zvrf;
+ struct listnode *node;
+ struct zserv *client;
+
+ zvrf = policy->zvrf;
+ switch (policy->endpoint.ipa_type) {
+ case IPADDR_V4:
+ p.family = AF_INET;
+ p.prefixlen = IPV4_MAX_BITLEN;
+ p.u.prefix4 = policy->endpoint.ipaddr_v4;
+ break;
+ case IPADDR_V6:
+ p.family = AF_INET6;
+ p.prefixlen = IPV6_MAX_BITLEN;
+ p.u.prefix6 = policy->endpoint.ipaddr_v6;
+ break;
+ default:
+ flog_warn(EC_LIB_DEVELOPMENT,
+ "%s: unknown policy endpoint address family: %u",
+ __func__, policy->endpoint.ipa_type);
+ exit(1);
+ }
+
+ rnh = zebra_lookup_rnh(&p, zvrf_id(zvrf), RNH_NEXTHOP_TYPE);
+ if (!rnh)
+ return;
+
+ for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) {
+ if (policy->status == ZEBRA_SR_POLICY_UP)
+ zebra_sr_policy_notify_update_client(policy, client);
+ else
+ /* Fallback to the IGP shortest path. */
+ zebra_send_rnh_update(rnh, client, RNH_NEXTHOP_TYPE,
+ zvrf_id(zvrf), policy->color);
+ }
+}
+
+static void zebra_sr_policy_activate(struct zebra_sr_policy *policy,
+ zebra_lsp_t *lsp)
+{
+ policy->status = ZEBRA_SR_POLICY_UP;
+ policy->lsp = lsp;
+ (void)zebra_sr_policy_bsid_install(policy);
+ zsend_sr_policy_notify_status(policy->color, &policy->endpoint,
+ policy->name, ZEBRA_SR_POLICY_UP);
+ zebra_sr_policy_notify_update(policy);
+}
+
+static void zebra_sr_policy_update(struct zebra_sr_policy *policy,
+ zebra_lsp_t *lsp,
+ struct zapi_srte_tunnel *old_tunnel)
+{
+ bool bsid_changed;
+ bool segment_list_changed;
+
+ policy->lsp = lsp;
+
+ bsid_changed =
+ policy->segment_list.local_label != old_tunnel->local_label;
+ segment_list_changed =
+ policy->segment_list.label_num != old_tunnel->label_num
+ || memcmp(policy->segment_list.labels, old_tunnel->labels,
+ sizeof(mpls_label_t)
+ * policy->segment_list.label_num);
+
+ /* Re-install label stack if necessary. */
+ if (bsid_changed || segment_list_changed) {
+ zebra_sr_policy_bsid_uninstall(policy, old_tunnel->local_label);
+ (void)zebra_sr_policy_bsid_install(policy);
+ }
+
+ zsend_sr_policy_notify_status(policy->color, &policy->endpoint,
+ policy->name, ZEBRA_SR_POLICY_UP);
+
+ /* Handle segment-list update. */
+ if (segment_list_changed)
+ zebra_sr_policy_notify_update(policy);
+}
+
+static void zebra_sr_policy_deactivate(struct zebra_sr_policy *policy)
+{
+ policy->status = ZEBRA_SR_POLICY_DOWN;
+ policy->lsp = NULL;
+ zebra_sr_policy_bsid_uninstall(policy,
+ policy->segment_list.local_label);
+ zsend_sr_policy_notify_status(policy->color, &policy->endpoint,
+ policy->name, ZEBRA_SR_POLICY_DOWN);
+ zebra_sr_policy_notify_update(policy);
+}
+
+int zebra_sr_policy_validate(struct zebra_sr_policy *policy,
+ struct zapi_srte_tunnel *new_tunnel)
+{
+ struct zapi_srte_tunnel old_tunnel = policy->segment_list;
+ zebra_lsp_t *lsp;
+
+ if (new_tunnel)
+ policy->segment_list = *new_tunnel;
+
+ /* Try to resolve the Binding-SID nexthops. */
+ lsp = mpls_lsp_find(policy->zvrf, policy->segment_list.labels[0]);
+ if (!lsp || !lsp->best_nhlfe
+ || lsp->addr_family != ipaddr_family(&policy->endpoint)) {
+ if (policy->status == ZEBRA_SR_POLICY_UP)
+ zebra_sr_policy_deactivate(policy);
+ return -1;
+ }
+
+ /* First label was resolved successfully. */
+ if (policy->status == ZEBRA_SR_POLICY_DOWN)
+ zebra_sr_policy_activate(policy, lsp);
+ else
+ zebra_sr_policy_update(policy, lsp, &old_tunnel);
+
+ return 0;
+}
+
+int zebra_sr_policy_bsid_install(struct zebra_sr_policy *policy)
+{
+ struct zapi_srte_tunnel *zt = &policy->segment_list;
+ zebra_nhlfe_t *nhlfe;
+
+ if (zt->local_label == MPLS_LABEL_NONE)
+ return 0;
+
+ frr_each_safe (nhlfe_list, &policy->lsp->nhlfe_list, nhlfe) {
+ uint8_t num_out_labels;
+ mpls_label_t *out_labels;
+ mpls_label_t null_label = MPLS_LABEL_IMPLICIT_NULL;
+
+ if (!CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED)
+ || CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED))
+ continue;
+
+ /*
+ * Don't push the first SID if the corresponding action in the
+ * LFIB is POP.
+ */
+ if (!nhlfe->nexthop->nh_label
+ || !nhlfe->nexthop->nh_label->num_labels
+ || nhlfe->nexthop->nh_label->label[0]
+ == MPLS_LABEL_IMPLICIT_NULL) {
+ if (zt->label_num > 1) {
+ num_out_labels = zt->label_num - 1;
+ out_labels = &zt->labels[1];
+ } else {
+ num_out_labels = 1;
+ out_labels = &null_label;
+ }
+ } else {
+ num_out_labels = zt->label_num;
+ out_labels = zt->labels;
+ }
+
+ if (mpls_lsp_install(
+ policy->zvrf, zt->type, zt->local_label,
+ num_out_labels, out_labels, nhlfe->nexthop->type,
+ &nhlfe->nexthop->gate, nhlfe->nexthop->ifindex)
+ < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+void zebra_sr_policy_bsid_uninstall(struct zebra_sr_policy *policy,
+ mpls_label_t old_bsid)
+{
+ struct zapi_srte_tunnel *zt = &policy->segment_list;
+
+ mpls_lsp_uninstall_all_vrf(policy->zvrf, zt->type, old_bsid);
+}
+
+int zebra_sr_policy_label_update(mpls_label_t label,
+ enum zebra_sr_policy_update_label_mode mode)
+{
+ struct zebra_sr_policy *policy;
+
+ RB_FOREACH (policy, zebra_sr_policy_instance_head,
+ &zebra_sr_policy_instances) {
+ mpls_label_t next_hop_label;
+
+ next_hop_label = policy->segment_list.labels[0];
+ if (next_hop_label != label)
+ continue;
+
+ switch (mode) {
+ case ZEBRA_SR_POLICY_LABEL_CREATED:
+ case ZEBRA_SR_POLICY_LABEL_UPDATED:
+ case ZEBRA_SR_POLICY_LABEL_REMOVED:
+ zebra_sr_policy_validate(policy, NULL);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+void zebra_srte_init(void)
+{
+}
diff --git a/zebra/zebra_srte.h b/zebra/zebra_srte.h
new file mode 100644
index 0000000000..e5239b7b7b
--- /dev/null
+++ b/zebra/zebra_srte.h
@@ -0,0 +1,74 @@
+/* Zebra's client header.
+ * Copyright (C) 2020 Netdef, Inc.
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _ZEBRA_SRTE_H
+#define _ZEBRA_SRTE_H
+
+#include "zebra/zebra_mpls.h"
+
+#include "lib/zclient.h"
+#include "lib/srte.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum zebra_sr_policy_update_label_mode {
+ ZEBRA_SR_POLICY_LABEL_CREATED = 1,
+ ZEBRA_SR_POLICY_LABEL_UPDATED = 2,
+ ZEBRA_SR_POLICY_LABEL_REMOVED = 3,
+};
+
+struct zebra_sr_policy {
+ RB_ENTRY(zebra_sr_policy) entry;
+ uint32_t color;
+ struct ipaddr endpoint;
+ char name[SRTE_POLICY_NAME_MAX_LENGTH];
+ enum zebra_sr_policy_status status;
+ struct zapi_srte_tunnel segment_list;
+ zebra_lsp_t *lsp;
+ struct zebra_vrf *zvrf;
+};
+RB_HEAD(zebra_sr_policy_instance_head, zebra_sr_policy);
+RB_PROTOTYPE(zebra_sr_policy_instance_head, zebra_sr_policy, entry,
+ zebra_sr_policy_instance_compare)
+
+extern struct zebra_sr_policy_instance_head zebra_sr_policy_instances;
+
+struct zebra_sr_policy *
+zebra_sr_policy_add(uint32_t color, struct ipaddr *endpoint, char *name);
+void zebra_sr_policy_del(struct zebra_sr_policy *policy);
+struct zebra_sr_policy *zebra_sr_policy_find(uint32_t color,
+ struct ipaddr *endpoint);
+struct zebra_sr_policy *zebra_sr_policy_find_by_name(char *name);
+int zebra_sr_policy_validate(struct zebra_sr_policy *policy,
+ struct zapi_srte_tunnel *new_tunnel);
+int zebra_sr_policy_bsid_install(struct zebra_sr_policy *policy);
+void zebra_sr_policy_bsid_uninstall(struct zebra_sr_policy *policy,
+ mpls_label_t old_bsid);
+void zebra_srte_init(void);
+int zebra_sr_policy_label_update(mpls_label_t label,
+ enum zebra_sr_policy_update_label_mode mode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ZEBRA_SRTE_H */
diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c
index 2ea04eee2e..b6d0b26125 100644
--- a/zebra/zebra_vty.c
+++ b/zebra/zebra_vty.c
@@ -57,6 +57,7 @@
#include "zebra/interface.h"
#include "northbound_cli.h"
#include "zebra/zebra_nb.h"
+#include "zebra/kernel_netlink.h"
extern int allow_delete;
@@ -562,6 +563,10 @@ static void show_route_nexthop_helper(struct vty *vty,
if (inet_ntop(AF_INET, &nexthop->src.ipv4, buf,
sizeof(buf)))
vty_out(vty, ", src %s", buf);
+ /* SR-TE information */
+ if (nexthop->srte_color)
+ vty_out(vty, ", SR-TE color %u",
+ nexthop->srte_color);
}
break;
case NEXTHOP_TYPE_IPV6:
@@ -781,6 +786,9 @@ static void show_nexthop_json_helper(json_object *json_nexthop,
json_object_int_add(json_nexthop, "weight",
nexthop->weight);
+ if (nexthop->srte_color)
+ json_object_int_add(json_nexthop, "srteColor",
+ nexthop->srte_color);
}
static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
@@ -2434,7 +2442,7 @@ DEFUN (no_vrf_vni_mapping,
int filter = 0;
ZEBRA_DECLVAR_CONTEXT(vrf, zvrf);
- vni_t vni = strtoul(argv[1]->arg, NULL, 10);
+ vni_t vni = strtoul(argv[2]->arg, NULL, 10);
assert(vrf);
assert(zvrf);
@@ -3390,6 +3398,11 @@ static int config_write_protocol(struct vty *vty)
if (!zebra_nhg_kernel_nexthops_enabled())
vty_out(vty, "no zebra nexthop kernel enable\n");
+#ifdef HAVE_NETLINK
+ /* Include netlink info */
+ netlink_config_write_helper(vty);
+#endif /* HAVE_NETLINK */
+
return 1;
}
@@ -3712,6 +3725,44 @@ DEFUN_HIDDEN (show_frr,
return CMD_SUCCESS;
}
+#ifdef HAVE_NETLINK
+DEFUN_HIDDEN(zebra_kernel_netlink_batch_tx_buf,
+ zebra_kernel_netlink_batch_tx_buf_cmd,
+ "zebra kernel netlink batch-tx-buf (1-1048576) (1-1048576)",
+ ZEBRA_STR
+ "Zebra kernel interface\n"
+ "Set Netlink parameters\n"
+ "Set batch buffer size and send threshold\n"
+ "Size of the buffer\n"
+ "Send threshold\n")
+{
+ uint32_t bufsize = 0, threshold = 0;
+
+ bufsize = strtoul(argv[4]->arg, NULL, 10);
+ threshold = strtoul(argv[5]->arg, NULL, 10);
+
+ netlink_set_batch_buffer_size(bufsize, threshold, true);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_HIDDEN(no_zebra_kernel_netlink_batch_tx_buf,
+ no_zebra_kernel_netlink_batch_tx_buf_cmd,
+ "no zebra kernel netlink batch-tx-buf [(0-1048576)] [(0-1048576)]",
+ NO_STR ZEBRA_STR
+ "Zebra kernel interface\n"
+ "Set Netlink parameters\n"
+ "Set batch buffer size and send threshold\n"
+ "Size of the buffer\n"
+ "Send threshold\n")
+{
+ netlink_set_batch_buffer_size(0, 0, false);
+
+ return CMD_SUCCESS;
+}
+
+#endif /* HAVE_NETLINK */
+
/* IP node for static routes. */
static int zebra_ip_config(struct vty *vty);
static struct cmd_node ip_node = {
@@ -3849,5 +3900,10 @@ void zebra_vty_init(void)
install_element(CONFIG_NODE, &zebra_dplane_queue_limit_cmd);
install_element(CONFIG_NODE, &no_zebra_dplane_queue_limit_cmd);
+#ifdef HAVE_NETLINK
+ install_element(CONFIG_NODE, &zebra_kernel_netlink_batch_tx_buf_cmd);
+ install_element(CONFIG_NODE, &no_zebra_kernel_netlink_batch_tx_buf_cmd);
+#endif /* HAVE_NETLINK */
+
install_element(VIEW_NODE, &zebra_show_routing_tables_summary_cmd);
}
diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c
index ff09b48dcf..ee167fa5a3 100644
--- a/zebra/zebra_vxlan.c
+++ b/zebra/zebra_vxlan.c
@@ -9811,6 +9811,9 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp,
bool inform_dataplane = false;
bool new_static = false;
+ if (ifp == NULL)
+ return -1;
+
/* We are interested in MACs only on ports or (port, VLAN) that
* map to a VNI.
*/