summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_route.c456
-rw-r--r--bgpd/bgp_vty.h2
-rw-r--r--debian/frr.install1
-rw-r--r--doc/user/bgp.rst10
-rw-r--r--doc/user/pim.rst5
-rw-r--r--lib/defaults.c7
-rw-r--r--lib/json.c5
-rw-r--r--lib/json.h3
-rw-r--r--lib/nexthop.h11
-rw-r--r--ospfd/ospf_packet.c44
-rw-r--r--pbrd/pbr_map.c35
-rw-r--r--pbrd/pbr_map.h10
-rw-r--r--pbrd/pbr_nht.c28
-rw-r--r--pbrd/pbr_nht.h3
-rw-r--r--pbrd/pbr_vty.c281
-rw-r--r--pbrd/pbr_zebra.c18
-rw-r--r--pbrd/pbr_zebra.h3
-rw-r--r--pimd/pim_cmd.c24
-rw-r--r--pimd/pim_msdp.c4
-rw-r--r--redhat/frr.spec.in1
-rw-r--r--tests/topotests/bgp-evpn-vxlan_topo1/P1/bgpd.conf1
-rw-r--r--tests/topotests/bgp-evpn-vxlan_topo1/P1/ospfd.conf4
-rw-r--r--tests/topotests/bgp-evpn-vxlan_topo1/P1/zebra.conf7
-rw-r--r--tests/topotests/bgp-evpn-vxlan_topo1/PE1/bgpd.conf10
-rw-r--r--tests/topotests/bgp-evpn-vxlan_topo1/PE1/evpn.vni.json16
-rw-r--r--tests/topotests/bgp-evpn-vxlan_topo1/PE1/ospfd.conf4
-rw-r--r--tests/topotests/bgp-evpn-vxlan_topo1/PE1/zebra.conf10
-rw-r--r--tests/topotests/bgp-evpn-vxlan_topo1/PE2/bgpd.conf11
-rw-r--r--tests/topotests/bgp-evpn-vxlan_topo1/PE2/evpn.vni.json15
-rw-r--r--tests/topotests/bgp-evpn-vxlan_topo1/PE2/ospfd.conf4
-rw-r--r--tests/topotests/bgp-evpn-vxlan_topo1/PE2/zebra.conf8
-rw-r--r--tests/topotests/bgp-evpn-vxlan_topo1/__init__.py0
-rw-r--r--tests/topotests/bgp-evpn-vxlan_topo1/host1/bgpd.conf1
-rw-r--r--tests/topotests/bgp-evpn-vxlan_topo1/host1/ospfd.conf1
-rw-r--r--tests/topotests/bgp-evpn-vxlan_topo1/host1/zebra.conf3
-rw-r--r--tests/topotests/bgp-evpn-vxlan_topo1/host2/bgpd.conf1
-rw-r--r--tests/topotests/bgp-evpn-vxlan_topo1/host2/ospfd.conf1
-rw-r--r--tests/topotests/bgp-evpn-vxlan_topo1/host2/zebra.conf3
-rwxr-xr-xtests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py289
-rw-r--r--zebra/dplane_fpm_nl.c1127
-rw-r--r--zebra/rt_netlink.c426
-rw-r--r--zebra/rt_netlink.h6
-rw-r--r--zebra/rule_netlink.c135
-rw-r--r--zebra/rule_socket.c8
-rw-r--r--zebra/subdir.am10
-rw-r--r--zebra/zebra_dplane.c204
-rw-r--r--zebra/zebra_dplane.h24
-rw-r--r--zebra/zebra_fpm.c61
-rw-r--r--zebra/zebra_nhg.c31
-rw-r--r--zebra/zebra_ns.c1
-rw-r--r--zebra/zebra_pbr.c51
-rw-r--r--zebra/zebra_pbr.h7
-rw-r--r--zebra/zebra_vxlan_private.h1
53 files changed, 2762 insertions, 670 deletions
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 1d8be6496d..d5f903f6e2 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -10545,45 +10545,177 @@ DEFUN (show_ip_bgp_large_community,
}
static int bgp_table_stats(struct vty *vty, struct bgp *bgp, afi_t afi,
- safi_t safi);
+ safi_t safi, struct json_object *json);
+DEFUN(show_ip_bgp_statistics_all, show_ip_bgp_statistics_all_cmd,
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] statistics-all [json]",
+ SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR
+ "Display number of prefixes for all afi/safi\n" JSON_STR)
+{
+ bool uj = use_json(argc, argv);
+ struct bgp *bgp = NULL;
+ safi_t safi;
+ afi_t afi;
+ int idx = 0;
+ struct json_object *json_all = NULL;
+ struct json_object *json_afi_safi = NULL;
+
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, false);
+ if (!bgp)
+ return CMD_WARNING;
+
+ if (uj)
+ json_all = json_object_new_object();
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ /*
+ * So limit output to those afi/safi pairs that
+ * actually have something interesting in them
+ */
+ if (strmatch(get_afi_safi_str(afi, safi, true),
+ "Unknown")) {
+ continue;
+ }
+ if (uj) {
+ json_afi_safi = json_object_new_array();
+ json_object_object_add(
+ json_all,
+ get_afi_safi_str(afi, safi, true),
+ json_afi_safi);
+ } else {
+ json_afi_safi = NULL;
+ }
+
+ bgp_table_stats(vty, bgp, afi, safi, json_afi_safi);
+ }
+
+ if (uj) {
+ vty_out(vty, "%s",
+ json_object_to_json_string_ext(
+ json_all, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json_all);
+ }
+
+ return CMD_SUCCESS;
+}
+
/* BGP route print out function without JSON */
-DEFUN (show_ip_bgp,
- show_ip_bgp_cmd,
- "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]]\
+DEFUN (show_ip_bgp_l2vpn_evpn_statistics,
+ show_ip_bgp_l2vpn_evpn_statistics_cmd,
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] l2vpn evpn statistics [json]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_INSTANCE_HELP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "BGP RIB advertisement statistics\n"
+ JSON_STR)
+{
+ afi_t afi;
+ safi_t safi;
+ struct bgp *bgp = NULL;
+ int idx = 0, ret;
+ bool uj = use_json(argc, argv);
+ struct json_object *json_afi_safi = NULL, *json = NULL;
+
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, false);
+ if (!idx)
+ return CMD_WARNING;
+
+ if (uj)
+ json_afi_safi = json_object_new_array();
+ else
+ json_afi_safi = NULL;
+
+ ret = bgp_table_stats(vty, bgp, afi, safi, json_afi_safi);
+
+ if (uj) {
+ json = json_object_new_object();
+ json_object_object_add(json, get_afi_safi_str(afi, safi, true),
+ json_afi_safi);
+ vty_out(vty, "%s", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+ return ret;
+}
+
+/* BGP route print out function without JSON */
+DEFUN(show_ip_bgp_afi_safi_statistics, show_ip_bgp_afi_safi_statistics_cmd,
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] [" BGP_AFI_CMD_STR
+ " [" BGP_SAFI_WITH_LABEL_CMD_STR
+ "]]\
+ statistics [json]",
+ SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR BGP_AFI_HELP_STR
+ BGP_SAFI_WITH_LABEL_HELP_STR
+ "BGP RIB advertisement statistics\n" JSON_STR)
+{
+ afi_t afi;
+ safi_t safi;
+ struct bgp *bgp = NULL;
+ int idx = 0, ret;
+ bool uj = use_json(argc, argv);
+ struct json_object *json_afi_safi = NULL, *json = NULL;
+
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, false);
+ if (!idx)
+ return CMD_WARNING;
+
+ if (uj)
+ json_afi_safi = json_object_new_array();
+ else
+ json_afi_safi = NULL;
+
+ ret = bgp_table_stats(vty, bgp, afi, safi, json_afi_safi);
+
+ if (uj) {
+ json = json_object_new_object();
+ json_object_object_add(json, get_afi_safi_str(afi, safi, true),
+ json_afi_safi);
+ vty_out(vty, "%s",
+ json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+ return ret;
+}
+
+/* BGP route print out function without JSON */
+DEFUN(show_ip_bgp, show_ip_bgp_cmd,
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] [" BGP_AFI_CMD_STR
+ " [" BGP_SAFI_WITH_LABEL_CMD_STR
+ "]]\
<dampening <parameters>\
|route-map WORD\
|prefix-list WORD\
|filter-list WORD\
- |statistics\
|community-list <(1-500)|WORD> [exact-match]\
|A.B.C.D/M longer-prefixes\
|X:X::X:X/M longer-prefixes\
- >",
- SHOW_STR
- IP_STR
- BGP_STR
- BGP_INSTANCE_HELP_STR
- BGP_AFI_HELP_STR
- BGP_SAFI_WITH_LABEL_HELP_STR
- "Display detailed information about dampening\n"
- "Display detail of configured dampening parameters\n"
- "Display routes matching the route-map\n"
- "A route-map to match on\n"
- "Display routes conforming to the prefix-list\n"
- "Prefix-list name\n"
- "Display routes conforming to the filter-list\n"
- "Regular expression access list name\n"
- "BGP RIB advertisement statistics\n"
- "Display routes matching the community-list\n"
- "community-list number\n"
- "community-list name\n"
- "Exact match of the communities\n"
- "IPv4 prefix\n"
- "Display route and more specific routes\n"
- "IPv6 prefix\n"
- "Display route and more specific routes\n")
+ >",
+ SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR BGP_AFI_HELP_STR
+ BGP_SAFI_WITH_LABEL_HELP_STR
+ "Display detailed information about dampening\n"
+ "Display detail of configured dampening parameters\n"
+ "Display routes matching the route-map\n"
+ "A route-map to match on\n"
+ "Display routes conforming to the prefix-list\n"
+ "Prefix-list name\n"
+ "Display routes conforming to the filter-list\n"
+ "Regular expression access list name\n"
+ "Display routes matching the community-list\n"
+ "community-list number\n"
+ "community-list name\n"
+ "Exact match of the communities\n"
+ "IPv4 prefix\n"
+ "Display route and more specific routes\n"
+ "IPv6 prefix\n"
+ "Display route and more specific routes\n")
{
afi_t afi = AFI_IP6;
safi_t safi = SAFI_UNICAST;
@@ -10609,9 +10741,6 @@ DEFUN (show_ip_bgp,
return bgp_show_filter_list(vty, bgp, argv[idx + 1]->arg, afi,
safi, bgp_show_type_filter_list);
- if (argv_find(argv, argc, "statistics", &idx))
- return bgp_table_stats(vty, bgp, afi, safi);
-
if (argv_find(argv, argc, "route-map", &idx))
return bgp_show_route_map(vty, bgp, argv[idx + 1]->arg, afi,
safi, bgp_show_type_route_map);
@@ -11021,22 +11150,33 @@ enum bgp_stats {
BGP_STATS_MAX,
};
-static const char *const table_stats_strs[] = {
- [BGP_STATS_PREFIXES] = "Total Prefixes",
- [BGP_STATS_TOTPLEN] = "Average prefix length",
- [BGP_STATS_RIB] = "Total Advertisements",
- [BGP_STATS_UNAGGREGATEABLE] = "Unaggregateable prefixes",
- [BGP_STATS_MAX_AGGREGATEABLE] =
- "Maximum aggregateable prefixes",
- [BGP_STATS_AGGREGATES] = "BGP Aggregate advertisements",
- [BGP_STATS_SPACE] = "Address space advertised",
- [BGP_STATS_ASPATH_COUNT] = "Advertisements with paths",
- [BGP_STATS_ASPATH_MAXHOPS] = "Longest AS-Path (hops)",
- [BGP_STATS_ASPATH_MAXSIZE] = "Largest AS-Path (bytes)",
- [BGP_STATS_ASPATH_TOTHOPS] = "Average AS-Path length (hops)",
- [BGP_STATS_ASPATH_TOTSIZE] = "Average AS-Path size (bytes)",
- [BGP_STATS_ASN_HIGHEST] = "Highest public ASN",
- [BGP_STATS_MAX] = NULL,
+#define TABLE_STATS_IDX_VTY 0
+#define TABLE_STATS_IDX_JSON 1
+
+static const char *table_stats_strs[][2] = {
+ [BGP_STATS_PREFIXES] = {"Total Prefixes", "totalPrefixes"},
+ [BGP_STATS_TOTPLEN] = {"Average prefix length", "averagePrefixLength"},
+ [BGP_STATS_RIB] = {"Total Advertisements", "totalAdvertisements"},
+ [BGP_STATS_UNAGGREGATEABLE] = {"Unaggregateable prefixes",
+ "unaggregateablePrefixes"},
+ [BGP_STATS_MAX_AGGREGATEABLE] = {"Maximum aggregateable prefixes",
+ "maximumAggregateablePrefixes"},
+ [BGP_STATS_AGGREGATES] = {"BGP Aggregate advertisements",
+ "bgpAggregateAdvertisements"},
+ [BGP_STATS_SPACE] = {"Address space advertised",
+ "addressSpaceAdvertised"},
+ [BGP_STATS_ASPATH_COUNT] = {"Advertisements with paths",
+ "advertisementsWithPaths"},
+ [BGP_STATS_ASPATH_MAXHOPS] = {"Longest AS-Path (hops)",
+ "longestAsPath"},
+ [BGP_STATS_ASPATH_MAXSIZE] = {"Largest AS-Path (bytes)",
+ "largestAsPath"},
+ [BGP_STATS_ASPATH_TOTHOPS] = {"Average AS-Path length (hops)",
+ "averageAsPathLengthHops"},
+ [BGP_STATS_ASPATH_TOTSIZE] = {"Average AS-Path size (bytes)",
+ "averageAsPathSizeBytes"},
+ [BGP_STATS_ASN_HIGHEST] = {"Highest public ASN", "highestPublicAsn"},
+ [BGP_STATS_MAX] = {NULL, NULL}
};
struct bgp_table_stats {
@@ -11161,7 +11301,9 @@ static int bgp_table_stats_walker(struct thread *t)
ts->counts[BGP_STATS_MAXBITLEN] = space;
for (rn = top; rn; rn = bgp_route_next(rn)) {
- if (ts->table->safi == SAFI_MPLS_VPN) {
+ if (ts->table->safi == SAFI_MPLS_VPN
+ || ts->table->safi == SAFI_ENCAP
+ || ts->table->safi == SAFI_EVPN) {
struct bgp_table *table;
table = bgp_node_get_bgp_table_info(rn);
@@ -11181,18 +11323,36 @@ static int bgp_table_stats_walker(struct thread *t)
}
static int bgp_table_stats(struct vty *vty, struct bgp *bgp, afi_t afi,
- safi_t safi)
+ safi_t safi, struct json_object *json_array)
{
struct bgp_table_stats ts;
unsigned int i;
+ int ret = CMD_SUCCESS;
+ char temp_buf[20];
+ struct json_object *json = NULL;
+
+ if (json_array)
+ json = json_object_new_object();
if (!bgp->rib[afi][safi]) {
- vty_out(vty, "%% No RIB exist's for the AFI(%d)/SAFI(%d)\n",
- afi, safi);
- return CMD_WARNING;
+ char warning_msg[50];
+
+ snprintf(warning_msg, sizeof(warning_msg),
+ "%% No RIB exist's for the AFI(%d)/SAFI(%d)", afi,
+ safi);
+
+ if (!json)
+ vty_out(vty, "%s\n", warning_msg);
+ else
+ json_object_string_add(json, "warning", warning_msg);
+
+ ret = CMD_WARNING;
+ goto end_table_stats;
}
- vty_out(vty, "BGP %s RIB statistics\n", get_afi_safi_str(afi, safi, false));
+ if (!json)
+ vty_out(vty, "BGP %s RIB statistics\n",
+ get_afi_safi_str(afi, safi, false));
/* labeled-unicast routes live in the unicast table */
if (safi == SAFI_LABELED_UNICAST)
@@ -11203,7 +11363,8 @@ static int bgp_table_stats(struct vty *vty, struct bgp *bgp, afi_t afi,
thread_execute(bm->master, bgp_table_stats_walker, &ts, 0);
for (i = 0; i < BGP_STATS_MAX; i++) {
- if (!table_stats_strs[i])
+ if ((!json && !table_stats_strs[i][TABLE_STATS_IDX_VTY])
+ || (json && !table_stats_strs[i][TABLE_STATS_IDX_JSON]))
continue;
switch (i) {
@@ -11218,54 +11379,166 @@ static int bgp_table_stats(struct vty *vty, struct bgp *bgp, afi_t afi,
#endif
case BGP_STATS_ASPATH_TOTHOPS:
case BGP_STATS_ASPATH_TOTSIZE:
- vty_out(vty, "%-30s: ", table_stats_strs[i]);
- vty_out(vty, "%12.2f",
- ts.counts[i]
- ? (float)ts.counts[i]
- / (float)ts.counts
+ if (!json) {
+ snprintf(
+ temp_buf, sizeof(temp_buf), "%12.2f",
+ ts.counts[i]
+ ? (float)ts.counts[i]
+ / (float)ts.counts
+ [BGP_STATS_ASPATH_COUNT]
+ : 0);
+ vty_out(vty, "%-30s: %s",
+ table_stats_strs[i]
+ [TABLE_STATS_IDX_VTY],
+ temp_buf);
+ } else {
+ json_object_double_add(
+ json,
+ table_stats_strs[i]
+ [TABLE_STATS_IDX_JSON],
+ ts.counts[i]
+ ? (double)ts.counts[i]
+ / (double)ts.counts
[BGP_STATS_ASPATH_COUNT]
- : 0);
+ : 0);
+ }
break;
case BGP_STATS_TOTPLEN:
- vty_out(vty, "%-30s: ", table_stats_strs[i]);
- vty_out(vty, "%12.2f",
- ts.counts[i]
- ? (float)ts.counts[i]
- / (float)ts.counts
+ if (!json) {
+ snprintf(
+ temp_buf, sizeof(temp_buf), "%12.2f",
+ ts.counts[i]
+ ? (float)ts.counts[i]
+ / (float)ts.counts
+ [BGP_STATS_PREFIXES]
+ : 0);
+ vty_out(vty, "%-30s: %s",
+ table_stats_strs[i]
+ [TABLE_STATS_IDX_VTY],
+ temp_buf);
+ } else {
+ json_object_double_add(
+ json,
+ table_stats_strs[i]
+ [TABLE_STATS_IDX_JSON],
+ ts.counts[i]
+ ? (double)ts.counts[i]
+ / (double)ts.counts
[BGP_STATS_PREFIXES]
- : 0);
+ : 0);
+ }
break;
case BGP_STATS_SPACE:
- vty_out(vty, "%-30s: ", table_stats_strs[i]);
- vty_out(vty, "%12g\n", ts.total_space);
-
+ if (!json) {
+ snprintf(temp_buf, sizeof(temp_buf), "%12g",
+ ts.total_space);
+ vty_out(vty, "%-30s: %s\n",
+ table_stats_strs[i]
+ [TABLE_STATS_IDX_VTY],
+ temp_buf);
+ } else {
+ json_object_double_add(
+ json,
+ table_stats_strs[i]
+ [TABLE_STATS_IDX_JSON],
+ (double)ts.total_space);
+ }
if (afi == AFI_IP6) {
- vty_out(vty, "%30s: ", "/32 equivalent ");
- vty_out(vty, "%12g\n",
- ts.total_space * pow(2.0, -128 + 32));
- vty_out(vty, "%30s: ", "/48 equivalent ");
- vty_out(vty, "%12g\n",
- ts.total_space * pow(2.0, -128 + 48));
+ if (!json) {
+ snprintf(temp_buf, sizeof(temp_buf),
+ "%12g",
+ ts.total_space
+ * pow(2.0, -128 + 32));
+ vty_out(vty, "%30s: %s\n",
+ "/32 equivalent %s\n",
+ temp_buf);
+ } else {
+ json_object_double_add(
+ json, "/32equivalent",
+ (double)(ts.total_space
+ * pow(2.0,
+ -128 + 32)));
+ }
+ if (!json) {
+ snprintf(temp_buf, sizeof(temp_buf),
+ "%12g",
+ ts.total_space
+ * pow(2.0, -128 + 48));
+ vty_out(vty, "%30s: %s\n",
+ "/48 equivalent %s\n",
+ temp_buf);
+ } else {
+ json_object_double_add(
+ json, "/48equivalent",
+ (double)(ts.total_space
+ * pow(2.0,
+ -128 + 48)));
+ }
} else {
- vty_out(vty, "%30s: ", "% announced ");
- vty_out(vty, "%12.2f\n",
- ts.total_space * 100. * pow(2.0, -32));
- vty_out(vty, "%30s: ", "/8 equivalent ");
- vty_out(vty, "%12.2f\n",
- ts.total_space * pow(2.0, -32 + 8));
- vty_out(vty, "%30s: ", "/24 equivalent ");
- vty_out(vty, "%12.2f\n",
- ts.total_space * pow(2.0, -32 + 24));
+ if (!json) {
+ snprintf(temp_buf, sizeof(temp_buf),
+ "%12.2f",
+ ts.total_space * 100.
+ * pow(2.0, -32));
+ vty_out(vty, "%30s: %s\n",
+ "% announced ", temp_buf);
+ } else {
+ json_object_double_add(
+ json, "%announced",
+ (double)(ts.total_space * 100.
+ * pow(2.0, -32)));
+ }
+ if (!json) {
+ snprintf(temp_buf, sizeof(temp_buf),
+ "%12.2f",
+ ts.total_space
+ * pow(2.0, -32 + 8));
+ vty_out(vty, "%30s: %s\n",
+ "/8 equivalent ", temp_buf);
+ } else {
+ json_object_double_add(
+ json, "/8equivalent",
+ (double)(ts.total_space
+ * pow(2.0, -32 + 8)));
+ }
+ if (!json) {
+ snprintf(temp_buf, sizeof(temp_buf),
+ "%12.2f",
+ ts.total_space
+ * pow(2.0, -32 + 24));
+ vty_out(vty, "%30s: %s\n",
+ "/24 equivalent ", temp_buf);
+ } else {
+ json_object_double_add(
+ json, "/24equivalent",
+ (double)(ts.total_space
+ * pow(2.0, -32 + 24)));
+ }
}
break;
default:
- vty_out(vty, "%-30s: ", table_stats_strs[i]);
- vty_out(vty, "%12llu", ts.counts[i]);
+ if (!json) {
+ snprintf(temp_buf, sizeof(temp_buf), "%12llu",
+ ts.counts[i]);
+ vty_out(vty, "%-30s: %s",
+ table_stats_strs[i]
+ [TABLE_STATS_IDX_VTY],
+ temp_buf);
+ } else {
+ json_object_int_add(
+ json,
+ table_stats_strs[i]
+ [TABLE_STATS_IDX_JSON],
+ ts.counts[i]);
+ }
}
-
- vty_out(vty, "\n");
+ if (!json)
+ vty_out(vty, "\n");
}
- return CMD_SUCCESS;
+end_table_stats:
+ if (json)
+ json_object_array_add(json_array, json);
+ return ret;
}
enum bgp_pcounts {
@@ -13147,9 +13420,12 @@ void bgp_route_init(void)
/* IPv4 labeled-unicast configuration. */
install_element(VIEW_NODE, &show_ip_bgp_instance_all_cmd);
install_element(VIEW_NODE, &show_ip_bgp_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_afi_safi_statistics_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_statistics_cmd);
install_element(VIEW_NODE, &show_ip_bgp_json_cmd);
install_element(VIEW_NODE, &show_ip_bgp_route_cmd);
install_element(VIEW_NODE, &show_ip_bgp_regexp_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_statistics_all_cmd);
install_element(VIEW_NODE,
&show_ip_bgp_instance_neighbor_advertised_route_cmd);
diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h
index fa7b96d87b..d6ca198d09 100644
--- a/bgpd/bgp_vty.h
+++ b/bgpd/bgp_vty.h
@@ -175,6 +175,8 @@ extern int bgp_vty_find_and_parse_afi_safi_bgp(struct vty *vty,
int argc, int *idx, afi_t *afi,
safi_t *safi, struct bgp **bgp,
bool use_json);
+int bgp_vty_find_and_parse_bgp(struct vty *vty, struct cmd_token **argv,
+ int argc, struct bgp **bgp, bool use_json);
extern int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi,
safi_t safi, bool show_failed, bool use_json);
diff --git a/debian/frr.install b/debian/frr.install
index 5917c0da84..e2485fe8b8 100644
--- a/debian/frr.install
+++ b/debian/frr.install
@@ -9,6 +9,7 @@ usr/lib/frr/*d
usr/lib/frr/watchfrr
usr/lib/frr/zebra
usr/lib/*/frr/modules/zebra_cumulus_mlag.so
+usr/lib/*/frr/modules/dplane_fpm_nl.so
usr/lib/*/frr/modules/zebra_irdp.so
usr/lib/*/frr/modules/zebra_fpm.so
usr/lib/*/frr/modules/bgpd_bmp.so
diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst
index 91ba37991b..d84bffc3c6 100644
--- a/doc/user/bgp.rst
+++ b/doc/user/bgp.rst
@@ -2677,6 +2677,16 @@ structure is extended with :clicmd:`show bgp [afi] [safi]`.
Display flap statistics of routes of the selected afi and safi selected.
+.. index:: show bgp [afi] [safi] statistics
+.. clicmd:: show bgp [afi] [safi] statistics
+
+ Display statistics of routes of the selected afi and safi.
+
+.. index:: show bgp statistics-all
+.. clicmd:: show bgp statistics-all
+
+ Display statistics of routes of all the afi and safi.
+
.. _bgp-display-routes-by-community:
Displaying Routes by Community Attribute
diff --git a/doc/user/pim.rst b/doc/user/pim.rst
index f480c6bdc4..f0ec2d26ff 100644
--- a/doc/user/pim.rst
+++ b/doc/user/pim.rst
@@ -166,6 +166,11 @@ Certain signals have special meanings to *pimd*.
urib-only
Lookup in the Unicast Rib only.
+.. index:: no ip msdp mesh-group [WORD]
+.. clicmd:: no ip msdp mesh-group [WORD]
+
+ Delete multicast source discovery protocol mesh-group
+
.. index:: ip igmp generate-query-once [version (2-3)]
.. clicmd:: ip igmp generate-query-once [version (2-3)]
diff --git a/lib/defaults.c b/lib/defaults.c
index 71ccc73cc6..7466aad5b1 100644
--- a/lib/defaults.c
+++ b/lib/defaults.c
@@ -100,10 +100,10 @@ static bool frr_match_version(const char *name, const char *vspec,
const char *version, bool check)
{
int cmp;
- static struct spec {
+ static const struct spec {
const char *str;
- bool dir, eq;
- } *s, specs[] = {
+ int dir, eq;
+ } specs[] = {
{"<=", -1, 1},
{">=", 1, 1},
{"==", 0, 1},
@@ -112,6 +112,7 @@ static bool frr_match_version(const char *name, const char *vspec,
{"=", 0, 1},
{NULL, 0, 0},
};
+ const struct spec *s;
if (!vspec)
/* NULL = all versions */
diff --git a/lib/json.c b/lib/json.c
index 991240639a..6bea3982e3 100644
--- a/lib/json.c
+++ b/lib/json.c
@@ -50,6 +50,11 @@ void json_object_int_add(struct json_object *obj, const char *key, int64_t i)
json_object_object_add(obj, key, json_object_new_int64(i));
}
+void json_object_double_add(struct json_object *obj, const char *key, double i)
+{
+ json_object_object_add(obj, key, json_object_new_double(i));
+}
+
void json_object_boolean_false_add(struct json_object *obj, const char *key)
{
json_object_object_add(obj, key, json_object_new_boolean(0));
diff --git a/lib/json.h b/lib/json.h
index c8866c524a..afe0b175da 100644
--- a/lib/json.h
+++ b/lib/json.h
@@ -48,6 +48,9 @@ extern void json_object_int_add(struct json_object *obj, const char *key,
int64_t i);
void json_object_boolean_add(struct json_object *obj, const char *key,
bool val);
+
+extern void json_object_double_add(struct json_object *obj, const char *key,
+ double i);
extern void json_object_boolean_false_add(struct json_object *obj,
const char *key);
extern void json_object_boolean_true_add(struct json_object *obj,
diff --git a/lib/nexthop.h b/lib/nexthop.h
index c4e88dd844..9b71262589 100644
--- a/lib/nexthop.h
+++ b/lib/nexthop.h
@@ -25,6 +25,7 @@
#include "prefix.h"
#include "mpls.h"
+#include "vxlan.h"
#ifdef __cplusplus
extern "C" {
@@ -60,6 +61,10 @@ enum blackhole_type {
? (type) \
: ((type) | 1)
+enum nh_encap_type {
+ NET_VXLAN = 100, /* value copied from FPM_NH_ENCAP_VXLAN. */
+};
+
/* Nexthop structure. */
struct nexthop {
struct nexthop *next;
@@ -123,6 +128,12 @@ struct nexthop {
* only meaningful if the HAS_BACKUP flag is set.
*/
uint8_t backup_idx;
+
+ /* Encapsulation information. */
+ enum nh_encap_type nh_encap_type;
+ union {
+ vni_t vni;
+ } nh_encap;
};
/* Backup index value is limited */
diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c
index aa50aeacbc..a39d19cc5a 100644
--- a/ospfd/ospf_packet.c
+++ b/ospfd/ospf_packet.c
@@ -2038,10 +2038,10 @@ static void ospf_ls_upd(struct ospf *ospf, struct ip *iph,
SET_FLAG(lsa->flags, OSPF_LSA_SELF);
- ospf_opaque_self_originated_lsa_received(nbr,
- lsa);
ospf_ls_ack_send(nbr, lsa);
+ ospf_opaque_self_originated_lsa_received(nbr,
+ lsa);
continue;
}
}
@@ -2604,7 +2604,7 @@ static unsigned ospf_router_lsa_links_examin(struct router_lsa_link *link,
{
unsigned counted_links = 0, thislinklen;
- while (linkbytes) {
+ while (linkbytes >= OSPF_ROUTER_LSA_LINK_SIZE) {
thislinklen =
OSPF_ROUTER_LSA_LINK_SIZE + 4 * link->m[0].tos_count;
if (thislinklen > linkbytes) {
@@ -2642,26 +2642,32 @@ static unsigned ospf_lsa_examin(struct lsa_header *lsah, const uint16_t lsalen,
return MSG_NG;
}
switch (lsah->type) {
- case OSPF_ROUTER_LSA:
- /* RFC2328 A.4.2, LSA header + 4 bytes followed by N>=1
- * (12+)-byte link blocks */
- if (headeronly) {
- ret = (lsalen - OSPF_LSA_HEADER_SIZE
- - OSPF_ROUTER_LSA_MIN_SIZE)
- % 4
- ? MSG_NG
- : MSG_OK;
- break;
- }
+ case OSPF_ROUTER_LSA: {
+ /*
+ * RFC2328 A.4.2, LSA header + 4 bytes followed by N>=0
+ * (12+)-byte link blocks
+ */
+ size_t linkbytes_len = lsalen - OSPF_LSA_HEADER_SIZE
+ - OSPF_ROUTER_LSA_MIN_SIZE;
+
+ /*
+ * LSA link blocks are variable length but always multiples of
+ * 4; basic sanity check
+ */
+ if (linkbytes_len % 4 != 0)
+ return MSG_NG;
+
+ if (headeronly)
+ return MSG_OK;
+
rlsa = (struct router_lsa *)lsah;
+
ret = ospf_router_lsa_links_examin(
(struct router_lsa_link *)rlsa->link,
- lsalen - OSPF_LSA_HEADER_SIZE - 4, /* skip: basic
- header, "flags",
- 0, "# links" */
- ntohs(rlsa->links) /* 16 bits */
- );
+ linkbytes_len,
+ ntohs(rlsa->links));
break;
+ }
case OSPF_AS_EXTERNAL_LSA:
/* RFC2328 A.4.5, LSA header + 4 bytes followed by N>=1 12-bytes long
* blocks */
diff --git a/pbrd/pbr_map.c b/pbrd/pbr_map.c
index e45e629649..7928b8e2e7 100644
--- a/pbrd/pbr_map.c
+++ b/pbrd/pbr_map.c
@@ -145,7 +145,7 @@ static bool pbr_map_interface_is_valid(const struct pbr_map_interface *pmi)
}
static void pbr_map_pbrms_update_common(struct pbr_map_sequence *pbrms,
- bool install)
+ bool install, bool changed)
{
struct pbr_map *pbrm;
struct listnode *node;
@@ -161,19 +161,19 @@ static void pbr_map_pbrms_update_common(struct pbr_map_sequence *pbrms,
if (install && !pbr_map_interface_is_valid(pmi))
continue;
- pbr_send_pbr_map(pbrms, pmi, install);
+ pbr_send_pbr_map(pbrms, pmi, install, changed);
}
}
}
-static void pbr_map_pbrms_install(struct pbr_map_sequence *pbrms)
+static void pbr_map_pbrms_install(struct pbr_map_sequence *pbrms, bool changed)
{
- pbr_map_pbrms_update_common(pbrms, true);
+ pbr_map_pbrms_update_common(pbrms, true, changed);
}
static void pbr_map_pbrms_uninstall(struct pbr_map_sequence *pbrms)
{
- pbr_map_pbrms_update_common(pbrms, false);
+ pbr_map_pbrms_update_common(pbrms, false, false);
}
static const char *const pbr_map_reason_str[] = {
@@ -292,7 +292,7 @@ void pbr_map_policy_interface_update(const struct interface *ifp, bool state_up)
for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms))
for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi))
if (pmi->ifp == ifp && pbr_map_interface_is_valid(pmi))
- pbr_send_pbr_map(pbrms, pmi, state_up);
+ pbr_send_pbr_map(pbrms, pmi, state_up, false);
}
static void pbrms_vrf_update(struct pbr_map_sequence *pbrms,
@@ -306,7 +306,7 @@ static void pbrms_vrf_update(struct pbr_map_sequence *pbrms,
DEBUGD(&pbr_dbg_map, "\tSeq %u uses vrf %s (%u), updating map",
pbrms->seqno, vrf_name, pbr_vrf_id(pbr_vrf));
- pbr_map_check(pbrms);
+ pbr_map_check(pbrms, false);
}
}
@@ -360,7 +360,7 @@ extern void pbr_map_delete(struct pbr_map_sequence *pbrms)
pbrm = pbrms->parent;
for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi))
- pbr_send_pbr_map(pbrms, pmi, false);
+ pbr_send_pbr_map(pbrms, pmi, false, false);
if (pbrms->nhg)
pbr_nht_delete_individual_nexthop(pbrms);
@@ -384,7 +384,7 @@ static void pbr_map_delete_common(struct pbr_map_sequence *pbrms)
pbrm->valid = false;
pbrms->nhs_installed = false;
pbrms->reason |= PBR_MAP_INVALID_NO_NEXTHOPS;
- pbrms->nhgrp_name = NULL;
+ XFREE(MTYPE_TMP, pbrms->nhgrp_name);
}
void pbr_map_delete_nexthops(struct pbr_map_sequence *pbrms)
@@ -619,7 +619,7 @@ void pbr_map_schedule_policy_from_nhg(const char *nh_group)
&& (strcmp(nh_group, pbrms->nhgrp_name) == 0)) {
pbrms->nhs_installed = true;
- pbr_map_check(pbrms);
+ pbr_map_check(pbrms, false);
}
if (pbrms->nhg
@@ -627,7 +627,7 @@ void pbr_map_schedule_policy_from_nhg(const char *nh_group)
== 0)) {
pbrms->nhs_installed = true;
- pbr_map_check(pbrms);
+ pbr_map_check(pbrms, false);
}
}
}
@@ -656,7 +656,8 @@ void pbr_map_policy_install(const char *name)
pbrms->seqno);
for (ALL_LIST_ELEMENTS_RO(pbrm->incoming, inode, pmi))
if (pbr_map_interface_is_valid(pmi))
- pbr_send_pbr_map(pbrms, pmi, true);
+ pbr_send_pbr_map(pbrms, pmi, true,
+ false);
}
}
}
@@ -668,7 +669,7 @@ void pbr_map_policy_delete(struct pbr_map *pbrm, struct pbr_map_interface *pmi)
for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms))
- pbr_send_pbr_map(pbrms, pmi, false);
+ pbr_send_pbr_map(pbrms, pmi, false, false);
pmi->delete = true;
}
@@ -710,13 +711,13 @@ void pbr_map_check_nh_group_change(const char *nh_group)
pbrm->incoming, inode,
pmi))
pbr_send_pbr_map(pbrms, pmi,
- false);
+ false, false);
}
}
}
}
-void pbr_map_check(struct pbr_map_sequence *pbrms)
+void pbr_map_check(struct pbr_map_sequence *pbrms, bool changed)
{
struct pbr_map *pbrm;
bool install;
@@ -741,7 +742,7 @@ void pbr_map_check(struct pbr_map_sequence *pbrms)
}
if (install)
- pbr_map_pbrms_install(pbrms);
+ pbr_map_pbrms_install(pbrms, changed);
else
pbr_map_pbrms_uninstall(pbrms);
}
@@ -755,7 +756,7 @@ void pbr_map_install(struct pbr_map *pbrm)
return;
for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms))
- pbr_map_pbrms_install(pbrms);
+ pbr_map_pbrms_install(pbrms, false);
}
void pbr_map_init(void)
diff --git a/pbrd/pbr_map.h b/pbrd/pbr_map.h
index 8bd22cbf2a..41f1703954 100644
--- a/pbrd/pbr_map.h
+++ b/pbrd/pbr_map.h
@@ -182,7 +182,15 @@ extern void pbr_map_init(void);
extern bool pbr_map_check_valid(const char *name);
-extern void pbr_map_check(struct pbr_map_sequence *pbrms);
+/**
+ * Re-check the pbr map for validity.
+ *
+ * Install if valid, remove if not.
+ *
+ * If changed is set, the config on the on the map has changed somewhere
+ * and the rules need to be replaced if valid.
+ */
+extern void pbr_map_check(struct pbr_map_sequence *pbrms, bool changed);
extern void pbr_map_check_nh_group_change(const char *nh_group);
extern void pbr_map_reason_string(unsigned int reason, char *buf, int size);
diff --git a/pbrd/pbr_nht.c b/pbrd/pbr_nht.c
index ecd375333c..2f3591ac8d 100644
--- a/pbrd/pbr_nht.c
+++ b/pbrd/pbr_nht.c
@@ -510,12 +510,26 @@ char *pbr_nht_nexthop_make_name(char *name, size_t l,
return buffer;
}
-void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms)
+void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms,
+ const struct nexthop *nhop)
{
struct pbr_nexthop_group_cache *pnhgc;
struct pbr_nexthop_group_cache find;
struct pbr_nexthop_cache *pnhc;
struct pbr_nexthop_cache lookup;
+ struct nexthop *nh;
+ char buf[PBR_NHC_NAMELEN];
+
+ pbrms->nhg = nexthop_group_new();
+ pbrms->internal_nhg_name = XSTRDUP(
+ MTYPE_TMP,
+ pbr_nht_nexthop_make_name(pbrms->parent->name, PBR_NHC_NAMELEN,
+ pbrms->seqno, buf));
+
+ nh = nexthop_new();
+ memcpy(nh, nhop, sizeof(*nh));
+
+ nexthop_group_add_sorted(pbrms->nhg, nh);
memset(&find, 0, sizeof(find));
pbr_nht_nexthop_make_name(pbrms->parent->name, PBR_NHC_NAMELEN,
@@ -539,7 +553,7 @@ void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms)
pbr_nht_install_nexthop_group(pnhgc, *pbrms->nhg);
}
-void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence *pbrms)
+static void pbr_nht_release_individual_nexthop(struct pbr_map_sequence *pbrms)
{
struct pbr_nexthop_group_cache *pnhgc;
struct pbr_nexthop_group_cache find;
@@ -548,8 +562,6 @@ void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence *pbrms)
struct nexthop *nh;
enum nexthop_types_t nh_type = 0;
- pbr_map_delete_nexthops(pbrms);
-
memset(&find, 0, sizeof(find));
snprintf(find.name, sizeof(find.name), "%s", pbrms->internal_nhg_name);
pnhgc = hash_lookup(pbr_nhg_hash, &find);
@@ -564,11 +576,19 @@ void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence *pbrms)
pbr_nht_uninstall_nexthop_group(pnhgc, *pbrms->nhg, nh_type);
hash_release(pbr_nhg_hash, pnhgc);
+ pbr_nhgc_delete(pnhgc);
nexthop_group_delete(&pbrms->nhg);
XFREE(MTYPE_TMP, pbrms->internal_nhg_name);
}
+void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence *pbrms)
+{
+ pbr_map_delete_nexthops(pbrms);
+
+ pbr_nht_release_individual_nexthop(pbrms);
+}
+
struct pbr_nexthop_group_cache *pbr_nht_add_group(const char *name)
{
struct nexthop *nhop;
diff --git a/pbrd/pbr_nht.h b/pbrd/pbr_nht.h
index 4ef41cede7..2533942547 100644
--- a/pbrd/pbr_nht.h
+++ b/pbrd/pbr_nht.h
@@ -88,7 +88,8 @@ extern struct pbr_nexthop_group_cache *pbr_nht_add_group(const char *name);
extern void pbr_nht_change_group(const char *name);
extern void pbr_nht_delete_group(const char *name);
-extern void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms);
+extern void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms,
+ const struct nexthop *nhop);
extern void pbr_nht_delete_individual_nexthop(struct pbr_map_sequence *pbrms);
/*
* Given the tableid of the installed default
diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c
index dfc8bec1bc..4a3a9ca382 100644
--- a/pbrd/pbr_vty.c
+++ b/pbrd/pbr_vty.c
@@ -142,18 +142,14 @@ DEFPY(pbr_map_match_src, pbr_map_match_src_cmd,
if (pbrms->src) {
if (prefix_same(pbrms->src, prefix))
return CMD_SUCCESS;
+ } else
+ pbrms->src = prefix_new();
- vty_out(vty,
- "A `match src-ip XX` command already exists, please remove that first\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- pbrms->src = prefix_new();
prefix_copy(pbrms->src, prefix);
} else
prefix_free(&pbrms->src);
- pbr_map_check(pbrms);
+ pbr_map_check(pbrms, true);
return CMD_SUCCESS;
}
@@ -174,18 +170,14 @@ DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd,
if (pbrms->dst) {
if (prefix_same(pbrms->dst, prefix))
return CMD_SUCCESS;
+ } else
+ pbrms->dst = prefix_new();
- vty_out(vty,
- "A `match dst-ip XX` command already exists, please remove that first\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- pbrms->dst = prefix_new();
prefix_copy(pbrms->dst, prefix);
} else
prefix_free(&pbrms->dst);
- pbr_map_check(pbrms);
+ pbr_map_check(pbrms, true);
return CMD_SUCCESS;
}
@@ -205,30 +197,52 @@ DEFPY(pbr_map_match_mark, pbr_map_match_mark_cmd,
#endif
if (!no) {
- if (pbrms->mark) {
+ if (pbrms->mark)
if (pbrms->mark == (uint32_t)mark)
return CMD_SUCCESS;
- vty_out(vty,
- "A `match mark XX` command already exists, please remove that first\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
pbrms->mark = (uint32_t)mark;
} else
pbrms->mark = 0;
- pbr_map_check(pbrms);
+ pbr_map_check(pbrms, true);
return CMD_SUCCESS;
}
-#define SET_VRF_EXISTS_STR \
- "A `set vrf XX` command already exists, please remove that first\n"
+static void pbrms_clear_set_vrf_config(struct pbr_map_sequence *pbrms)
+{
+ if (pbrms->vrf_lookup || pbrms->vrf_unchanged) {
+ pbr_map_delete_vrf(pbrms);
+ pbrms->vrf_name[0] = '\0';
+ pbrms->vrf_lookup = false;
+ pbrms->vrf_unchanged = false;
+ }
+}
+
+static void pbrms_clear_set_nhg_config(struct pbr_map_sequence *pbrms)
+{
+ if (pbrms->nhgrp_name)
+ pbr_map_delete_nexthops(pbrms);
+}
+
+static void pbrms_clear_set_nexthop_config(struct pbr_map_sequence *pbrms)
+{
+ if (pbrms->nhg)
+ pbr_nht_delete_individual_nexthop(pbrms);
+}
+
+static void pbrms_clear_set_config(struct pbr_map_sequence *pbrms)
+{
+ pbrms_clear_set_vrf_config(pbrms);
+ pbrms_clear_set_nhg_config(pbrms);
+ pbrms_clear_set_nexthop_config(pbrms);
+
+ pbrms->nhs_installed = false;
+}
DEFPY(pbr_map_nexthop_group, pbr_map_nexthop_group_cmd,
- "[no] set nexthop-group NHGNAME$name",
- NO_STR
+ "set nexthop-group NHGNAME$name",
"Set for the PBR-MAP\n"
"nexthop-group to use\n"
"The name of the nexthop-group\n")
@@ -236,17 +250,6 @@ DEFPY(pbr_map_nexthop_group, pbr_map_nexthop_group_cmd,
struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
struct nexthop_group_cmd *nhgc;
- if (pbrms->nhg) {
- vty_out(vty,
- "A `set nexthop XX` command already exists, please remove that first\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- if (pbrms->vrf_lookup || pbrms->vrf_unchanged) {
- vty_out(vty, SET_VRF_EXISTS_STR);
- return CMD_WARNING_CONFIG_FAILED;
- }
-
nhgc = nhgc_find(name);
if (!nhgc) {
vty_out(vty, "Specified nexthop-group %s does not exist\n",
@@ -255,40 +258,39 @@ DEFPY(pbr_map_nexthop_group, pbr_map_nexthop_group_cmd,
"PBR-MAP will not be applied until it is created\n");
}
- if (no) {
- if (pbrms->nhgrp_name && strcmp(name, pbrms->nhgrp_name) == 0)
- pbr_map_delete_nexthops(pbrms);
- else {
- vty_out(vty,
- "Nexthop Group specified: %s does not exist to remove\n",
- name);
- return CMD_WARNING_CONFIG_FAILED;
- }
- } else {
- if (pbrms->nhgrp_name) {
- if (strcmp(name, pbrms->nhgrp_name) != 0) {
- vty_out(vty,
- "Please delete current nexthop group before modifying current one\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
+ if (pbrms->nhgrp_name && strcmp(name, pbrms->nhgrp_name) == 0)
+ return CMD_SUCCESS;
- return CMD_SUCCESS;
- }
- pbrms->nhgrp_name = XSTRDUP(MTYPE_TMP, name);
- pbr_map_check(pbrms);
- }
+ /* This is new/replacement config */
+ pbrms_clear_set_config(pbrms);
+
+ pbrms->nhgrp_name = XSTRDUP(MTYPE_TMP, name);
+ pbr_map_check(pbrms, true);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(no_pbr_map_nexthop_group, no_pbr_map_nexthop_group_cmd,
+ "no set nexthop-group [NHGNAME$name]",
+ NO_STR
+ "Set for the PBR-MAP\n"
+ "nexthop-group to use\n"
+ "The name of the nexthop-group\n")
+{
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+
+ pbrms_clear_set_config(pbrms);
return CMD_SUCCESS;
}
DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd,
- "[no] set nexthop\
+ "set nexthop\
<\
<A.B.C.D|X:X::X:X>$addr [INTERFACE$intf]\
|INTERFACE$intf\
>\
[nexthop-vrf NAME$vrf_name]",
- NO_STR
"Set for the PBR-MAP\n"
"Specify one of the nexthops in this map\n"
"v4 Address\n"
@@ -301,18 +303,7 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd,
struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
struct vrf *vrf;
struct nexthop nhop;
- struct nexthop *nh;
-
- if (pbrms->nhgrp_name) {
- vty_out(vty,
- "Please unconfigure the nexthop group before adding an individual nexthop\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- if (pbrms->vrf_lookup || pbrms->vrf_unchanged) {
- vty_out(vty, SET_VRF_EXISTS_STR);
- return CMD_WARNING_CONFIG_FAILED;
- }
+ struct nexthop *nh = NULL;
if (vrf_name)
vrf = vrf_lookup_by_name(vrf_name);
@@ -362,45 +353,18 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd,
if (pbrms->nhg)
nh = nexthop_exists(pbrms->nhg, &nhop);
- else {
- char buf[PBR_NHC_NAMELEN];
-
- if (no) {
- vty_out(vty, "No nexthops to delete\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- pbrms->nhg = nexthop_group_new();
- pbrms->internal_nhg_name =
- XSTRDUP(MTYPE_TMP,
- pbr_nht_nexthop_make_name(pbrms->parent->name,
- PBR_NHC_NAMELEN,
- pbrms->seqno,
- buf));
- nh = NULL;
- }
- if (no) {
- if (nh)
- pbr_nht_delete_individual_nexthop(pbrms);
- } else if (!nh) {
+ if (nh) /* Same config re-entered */
+ goto done;
- if (pbrms->nhg->nexthop) {
- vty_out(vty,
- "If you would like more than one nexthop please use nexthop-groups\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
+ /* This is new/replacement config */
+ pbrms_clear_set_config(pbrms);
- /* must be adding new nexthop since !no and !nexthop_exists */
- nh = nexthop_new();
+ pbr_nht_add_individual_nexthop(pbrms, &nhop);
- memcpy(nh, &nhop, sizeof(nhop));
- _nexthop_add(&pbrms->nhg->nexthop, nh);
-
- pbr_nht_add_individual_nexthop(pbrms);
- pbr_map_check(pbrms);
- }
+ pbr_map_check(pbrms, true);
+done:
if (nhop.type == NEXTHOP_TYPE_IFINDEX
|| (nhop.type == NEXTHOP_TYPE_IPV6_IFINDEX
&& IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6))) {
@@ -414,84 +378,80 @@ DEFPY(pbr_map_nexthop, pbr_map_nexthop_cmd,
return CMD_SUCCESS;
}
-DEFPY(pbr_map_vrf, pbr_map_vrf_cmd,
- "[no] set vrf <NAME$vrf_name|unchanged>",
+DEFPY(no_pbr_map_nexthop, no_pbr_map_nexthop_cmd,
+ "no set nexthop\
+ [<\
+ <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf]\
+ |INTERFACE$intf\
+ >\
+ [nexthop-vrf NAME$vrf_name]]",
NO_STR
"Set for the PBR-MAP\n"
- "Specify the VRF for this map\n"
- "The VRF Name\n"
- "Use the interface's VRF for lookup\n")
+ "Specify one of the nexthops in this map\n"
+ "v4 Address\n"
+ "v6 Address\n"
+ "Interface to use\n"
+ "Interface to use\n"
+ "If the nexthop is in a different vrf tell us\n"
+ "The nexthop-vrf Name\n")
{
struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
- if (no) {
- pbr_map_delete_vrf(pbrms);
+ pbrms_clear_set_config(pbrms);
- /* Reset all data */
- pbrms->nhs_installed = false;
- pbrms->vrf_name[0] = '\0';
- pbrms->vrf_lookup = false;
- pbrms->vrf_unchanged = false;
-
- return CMD_SUCCESS;
- }
+ return CMD_SUCCESS;
+}
- if (pbrms->nhgrp_name || pbrms->nhg) {
- vty_out(vty,
- "A `set nexthop/nexthop-group XX` command already exits, please remove that first\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
+DEFPY(pbr_map_vrf, pbr_map_vrf_cmd,
+ "set vrf <NAME$vrf_name|unchanged>",
+ "Set for the PBR-MAP\n"
+ "Specify the VRF for this map\n"
+ "The VRF Name\n"
+ "Use the interface's VRF for lookup\n")
+{
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
/*
- * Determine if a set vrf * command already exists.
- *
- * If its equivalent, just return success.
- *
- * Else, return failure, we don't allow atomic swaps yet.
+ * If an equivalent set vrf * exists, just return success.
*/
- if (vrf_name && pbrms->vrf_lookup) {
- /* New vrf specified and one already exists */
-
- /* Is this vrf different from one already configured? */
- if (strncmp(pbrms->vrf_name, vrf_name, sizeof(pbrms->vrf_name))
- != 0)
- goto vrf_exists;
-
+ if (vrf_name && pbrms->vrf_lookup
+ && strncmp(pbrms->vrf_name, vrf_name, sizeof(pbrms->vrf_name)) == 0)
return CMD_SUCCESS;
-
- } else if (!vrf_name && pbrms->vrf_unchanged) {
- /* Unchanged specified and unchanged already exists */
+ else if (!vrf_name && pbrms->vrf_unchanged) /* Unchanged already set */
return CMD_SUCCESS;
- } else if (vrf_name && pbrms->vrf_unchanged) {
- /* New vrf specified and unchanged is already set */
- goto vrf_exists;
-
- } else if (!vrf_name && pbrms->vrf_lookup) {
- /* Unchanged specified and vrf to lookup already exists */
- goto vrf_exists;
+ if (vrf_name && !pbr_vrf_lookup_by_name(vrf_name)) {
+ vty_out(vty, "Specified: %s is non-existent\n", vrf_name);
+ return CMD_WARNING_CONFIG_FAILED;
}
- /* Create new lookup VRF or Unchanged */
- if (vrf_name) {
- if (!pbr_vrf_lookup_by_name(vrf_name)) {
- vty_out(vty, "Specified: %s is non-existent\n",
- vrf_name);
- return CMD_WARNING_CONFIG_FAILED;
- }
+ /* This is new/replacement config */
+ pbrms_clear_set_config(pbrms);
+ if (vrf_name) {
pbrms->vrf_lookup = true;
strlcpy(pbrms->vrf_name, vrf_name, sizeof(pbrms->vrf_name));
} else
pbrms->vrf_unchanged = true;
- pbr_map_check(pbrms);
+ pbr_map_check(pbrms, true);
return CMD_SUCCESS;
+}
-vrf_exists:
- vty_out(vty, SET_VRF_EXISTS_STR);
- return CMD_WARNING_CONFIG_FAILED;
+DEFPY(no_pbr_map_vrf, no_pbr_map_vrf_cmd,
+ "no set vrf [<NAME$vrf_name|unchanged>]",
+ NO_STR
+ "Set for the PBR-MAP\n"
+ "Specify the VRF for this map\n"
+ "The VRF Name\n"
+ "Use the interface's VRF for lookup\n")
+{
+ struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+
+ pbrms_clear_set_config(pbrms);
+
+ return CMD_SUCCESS;
}
DEFPY (pbr_policy,
@@ -897,8 +857,11 @@ void pbr_vty_init(void)
install_element(PBRMAP_NODE, &pbr_map_match_dst_cmd);
install_element(PBRMAP_NODE, &pbr_map_match_mark_cmd);
install_element(PBRMAP_NODE, &pbr_map_nexthop_group_cmd);
+ install_element(PBRMAP_NODE, &no_pbr_map_nexthop_group_cmd);
install_element(PBRMAP_NODE, &pbr_map_nexthop_cmd);
+ install_element(PBRMAP_NODE, &no_pbr_map_nexthop_cmd);
install_element(PBRMAP_NODE, &pbr_map_vrf_cmd);
+ install_element(PBRMAP_NODE, &no_pbr_map_vrf_cmd);
install_element(VIEW_NODE, &show_pbr_cmd);
install_element(VIEW_NODE, &show_pbr_map_cmd);
install_element(VIEW_NODE, &show_pbr_interface_cmd);
diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c
index c2d93a405e..de2a99e269 100644
--- a/pbrd/pbr_zebra.c
+++ b/pbrd/pbr_zebra.c
@@ -548,7 +548,7 @@ static void pbr_encode_pbr_map_sequence(struct stream *s,
}
void pbr_send_pbr_map(struct pbr_map_sequence *pbrms,
- struct pbr_map_interface *pmi, bool install)
+ struct pbr_map_interface *pmi, bool install, bool changed)
{
struct pbr_map *pbrm = pbrms->parent;
struct stream *s;
@@ -560,11 +560,13 @@ void pbr_send_pbr_map(struct pbr_map_sequence *pbrms,
pbrm->name, install, is_installed);
/*
- * If we are installed and asked to do so again
- * just return. If we are not installed and asked
- * and asked to delete just return;
+ * If we are installed and asked to do so again and the config
+ * has not changed, just return.
+ *
+ * If we are not installed and asked
+ * to delete just return.
*/
- if (install && is_installed)
+ if (install && is_installed && !changed)
return;
if (!install && !is_installed)
@@ -582,9 +584,9 @@ void pbr_send_pbr_map(struct pbr_map_sequence *pbrms,
*/
stream_putl(s, 1);
- DEBUGD(&pbr_dbg_zebra, "%s: \t%s %s %d %s %u", __func__,
- install ? "Installing" : "Deleting", pbrm->name, install,
- pmi->ifp->name, pmi->delete);
+ DEBUGD(&pbr_dbg_zebra, "%s: \t%s %s seq %u %d %s %u", __func__,
+ install ? "Installing" : "Deleting", pbrm->name, pbrms->seqno,
+ install, pmi->ifp->name, pmi->delete);
pbr_encode_pbr_map_sequence(s, pbrms, pmi->ifp);
diff --git a/pbrd/pbr_zebra.h b/pbrd/pbr_zebra.h
index d5d938021a..cc42e21abe 100644
--- a/pbrd/pbr_zebra.h
+++ b/pbrd/pbr_zebra.h
@@ -36,7 +36,8 @@ extern void route_delete(struct pbr_nexthop_group_cache *pnhgc,
extern void pbr_send_rnh(struct nexthop *nhop, bool reg);
extern void pbr_send_pbr_map(struct pbr_map_sequence *pbrms,
- struct pbr_map_interface *pmi, bool install);
+ struct pbr_map_interface *pmi, bool install,
+ bool changed);
extern struct pbr_interface *pbr_if_new(struct interface *ifp);
diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c
index e94d15effd..d6c500cdb0 100644
--- a/pimd/pim_cmd.c
+++ b/pimd/pim_cmd.c
@@ -9712,11 +9712,25 @@ DEFUN (no_ip_msdp_mesh_group_source,
"mesh group local address\n")
{
PIM_DECLVAR_CONTEXT(vrf, pim);
- if (argc == 7)
- return ip_no_msdp_mesh_group_cmd_worker(pim, vty, argv[6]->arg);
+
+ return ip_no_msdp_mesh_group_source_cmd_worker(pim, vty, argv[4]->arg);
+}
+
+DEFUN (no_ip_msdp_mesh_group,
+ no_ip_msdp_mesh_group_cmd,
+ "no ip msdp mesh-group [WORD]",
+ NO_STR
+ IP_STR
+ CFG_MSDP_STR
+ "Delete MSDP mesh-group\n"
+ "mesh group name")
+{
+ PIM_DECLVAR_CONTEXT(vrf, pim);
+
+ if (argc == 5)
+ return ip_no_msdp_mesh_group_cmd_worker(pim, vty, argv[4]->arg);
else
- return ip_no_msdp_mesh_group_source_cmd_worker(pim, vty,
- argv[4]->arg);
+ return ip_no_msdp_mesh_group_cmd_worker(pim, vty, NULL);
}
static void print_empty_json_obj(struct vty *vty)
@@ -11104,6 +11118,8 @@ void pim_cmd_init(void)
install_element(VRF_NODE, &ip_msdp_mesh_group_source_cmd);
install_element(CONFIG_NODE, &no_ip_msdp_mesh_group_source_cmd);
install_element(VRF_NODE, &no_ip_msdp_mesh_group_source_cmd);
+ install_element(CONFIG_NODE, &no_ip_msdp_mesh_group_cmd);
+ install_element(VRF_NODE, &no_ip_msdp_mesh_group_cmd);
install_element(VIEW_NODE, &show_ip_msdp_peer_detail_cmd);
install_element(VIEW_NODE, &show_ip_msdp_peer_detail_vrf_all_cmd);
install_element(VIEW_NODE, &show_ip_msdp_sa_detail_cmd);
diff --git a/pimd/pim_msdp.c b/pimd/pim_msdp.c
index 63d34e859c..52c989e644 100644
--- a/pimd/pim_msdp.c
+++ b/pimd/pim_msdp.c
@@ -1286,7 +1286,9 @@ enum pim_msdp_err pim_msdp_mg_del(struct pim_instance *pim,
struct pim_msdp_mg *mg = pim->msdp.mg;
struct pim_msdp_mg_mbr *mbr;
- if (!mg || strcmp(mg->mesh_group_name, mesh_group_name)) {
+ if (!mg
+ || (mesh_group_name
+ && strcmp(mg->mesh_group_name, mesh_group_name))) {
return PIM_MSDP_ERR_NO_MG;
}
diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in
index db465f2b00..929214a142 100644
--- a/redhat/frr.spec.in
+++ b/redhat/frr.spec.in
@@ -675,6 +675,7 @@ fi
%{_libdir}/frr/modules/bgpd_rpki.so
%endif
%{_libdir}/frr/modules/zebra_cumulus_mlag.so
+%{_libdir}/frr/modules/dplane_fpm_nl.so
%{_libdir}/frr/modules/zebra_irdp.so
%{_libdir}/frr/modules/bgpd_bmp.so
%{_bindir}/*
diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/P1/bgpd.conf b/tests/topotests/bgp-evpn-vxlan_topo1/P1/bgpd.conf
new file mode 100644
index 0000000000..cdf4cb4feb
--- /dev/null
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/P1/bgpd.conf
@@ -0,0 +1 @@
+!
diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/P1/ospfd.conf b/tests/topotests/bgp-evpn-vxlan_topo1/P1/ospfd.conf
new file mode 100644
index 0000000000..772675ddff
--- /dev/null
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/P1/ospfd.conf
@@ -0,0 +1,4 @@
+!
+router ospf
+ network 10.20.0.0/16 area 0
+ network 10.20.20.20/32 area 0
diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/P1/zebra.conf b/tests/topotests/bgp-evpn-vxlan_topo1/P1/zebra.conf
new file mode 100644
index 0000000000..95b5da8402
--- /dev/null
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/P1/zebra.conf
@@ -0,0 +1,7 @@
+!
+interface lo
+ ip address 10.20.20.20/32
+interface P1-eth0
+ ip address 10.20.1.2/24
+interface P1-eth1
+ ip address 10.20.2.2/24
diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE1/bgpd.conf b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/bgpd.conf
new file mode 100644
index 0000000000..720b83e485
--- /dev/null
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/bgpd.conf
@@ -0,0 +1,10 @@
+router bgp 65000
+ bgp router-id 10.10.10.10
+ no bgp default ipv4-unicast
+ neighbor 10.30.30.30 remote-as 65000
+ neighbor 10.30.30.30 ebgp-multihop 2
+ neighbor 10.30.30.30 update-source lo
+ neighbor 10.30.30.30 capability extended-nexthop
+ address-family l2vpn evpn
+ neighbor 10.30.30.30 activate
+ advertise-all-vni
diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE1/evpn.vni.json b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/evpn.vni.json
new file mode 100644
index 0000000000..d9f2182aa0
--- /dev/null
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/evpn.vni.json
@@ -0,0 +1,16 @@
+{
+ "vni":101,
+ "type":"L2",
+ "vrf":"default",
+ "vxlanInterface":"vxlan101",
+ "ifindex":5,
+ "vtepIp":"10.10.10.10",
+ "mcastGroup":"0.0.0.0",
+ "advertiseGatewayMacip":"No",
+ "numMacs":5,
+ "numArpNd":2,
+ "numRemoteVteps":[
+ "10.30.30.30"
+ ]
+}
+
diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE1/ospfd.conf b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/ospfd.conf
new file mode 100644
index 0000000000..31c7fc4188
--- /dev/null
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/ospfd.conf
@@ -0,0 +1,4 @@
+!
+router ospf
+ network 10.20.0.0/16 area 0
+ network 10.10.10.10/32 area 0
diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE1/zebra.conf b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/zebra.conf
new file mode 100644
index 0000000000..938ec7bca9
--- /dev/null
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/zebra.conf
@@ -0,0 +1,10 @@
+!
+log file zebra.log
+!
+interface lo
+ ip address 10.10.10.10/32
+interface PE1-eth0
+ ip address 10.10.1.1/24
+interface PE1-eth1
+ ip address 10.20.1.1/24
+!
diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE2/bgpd.conf b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/bgpd.conf
new file mode 100644
index 0000000000..7739795367
--- /dev/null
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/bgpd.conf
@@ -0,0 +1,11 @@
+router bgp 65000
+ bgp router-id 10.30.30.30
+ no bgp default ipv4-unicast
+ neighbor 10.10.10.10 remote-as 65000
+ neighbor 10.10.10.10 ebgp-multihop 2
+ neighbor 10.10.10.10 update-source lo
+ neighbor 10.10.10.10 capability extended-nexthop
+ !
+ address-family l2vpn evpn
+ neighbor 10.10.10.10 activate
+ advertise-all-vni
diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE2/evpn.vni.json b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/evpn.vni.json
new file mode 100644
index 0000000000..13255ab4f2
--- /dev/null
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/evpn.vni.json
@@ -0,0 +1,15 @@
+{
+ "vni":101,
+ "type":"L2",
+ "vrf":"default",
+ "vxlanInterface":"vxlan101",
+ "ifindex":5,
+ "vtepIp":"10.30.30.30",
+ "mcastGroup":"0.0.0.0",
+ "advertiseGatewayMacip":"No",
+ "numMacs":5,
+ "numArpNd":2,
+ "numRemoteVteps":[
+ "10.10.10.10"
+ ]
+}
diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE2/ospfd.conf b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/ospfd.conf
new file mode 100644
index 0000000000..c1a8308db5
--- /dev/null
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/ospfd.conf
@@ -0,0 +1,4 @@
+!
+router ospf
+ network 10.20.0.0/16 area 0
+ network 10.30.30.30/32 area 0
diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE2/zebra.conf b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/zebra.conf
new file mode 100644
index 0000000000..07b83f6395
--- /dev/null
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/zebra.conf
@@ -0,0 +1,8 @@
+!
+interface lo
+ ip address 10.30.30.30/32
+interface PE2-eth0
+ ip address 10.20.2.3/24
+interface PE2-eth1
+ ip address 10.10.1.3/24
+!
diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/__init__.py b/tests/topotests/bgp-evpn-vxlan_topo1/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/__init__.py
diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/host1/bgpd.conf b/tests/topotests/bgp-evpn-vxlan_topo1/host1/bgpd.conf
new file mode 100644
index 0000000000..cdf4cb4feb
--- /dev/null
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/host1/bgpd.conf
@@ -0,0 +1 @@
+!
diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/host1/ospfd.conf b/tests/topotests/bgp-evpn-vxlan_topo1/host1/ospfd.conf
new file mode 100644
index 0000000000..cdf4cb4feb
--- /dev/null
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/host1/ospfd.conf
@@ -0,0 +1 @@
+!
diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/host1/zebra.conf b/tests/topotests/bgp-evpn-vxlan_topo1/host1/zebra.conf
new file mode 100644
index 0000000000..91fae9eeba
--- /dev/null
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/host1/zebra.conf
@@ -0,0 +1,3 @@
+!
+int host1-eth0
+ ip address 10.10.1.55/24
diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/host2/bgpd.conf b/tests/topotests/bgp-evpn-vxlan_topo1/host2/bgpd.conf
new file mode 100644
index 0000000000..cdf4cb4feb
--- /dev/null
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/host2/bgpd.conf
@@ -0,0 +1 @@
+!
diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/host2/ospfd.conf b/tests/topotests/bgp-evpn-vxlan_topo1/host2/ospfd.conf
new file mode 100644
index 0000000000..cdf4cb4feb
--- /dev/null
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/host2/ospfd.conf
@@ -0,0 +1 @@
+!
diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/host2/zebra.conf b/tests/topotests/bgp-evpn-vxlan_topo1/host2/zebra.conf
new file mode 100644
index 0000000000..df9adeb3b5
--- /dev/null
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/host2/zebra.conf
@@ -0,0 +1,3 @@
+!
+interface host2-eth0
+ ip address 10.10.1.56/24
diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py b/tests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py
new file mode 100755
index 0000000000..ad72540185
--- /dev/null
+++ b/tests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py
@@ -0,0 +1,289 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_evpn_vxlan.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+# 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_bgp_evpn_vxlan.py: Test VXLAN EVPN MAC a route signalling over BGP.
+"""
+
+import os
+import sys
+import json
+from functools import partial
+import pytest
+
+# 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
+
+# Required to instantiate the topology builder class.
+from mininet.topo import Topo
+
+
+class TemplateTopo(Topo):
+ "Test topology builder"
+
+ def build(self, *_args, **_opts):
+ "Build function"
+ tgen = get_topogen(self)
+
+ # This function only purpose is to define allocation and relationship
+ # between routers, switches and hosts.
+ #
+ #
+ # Create routers
+ tgen.add_router("P1")
+ tgen.add_router("PE1")
+ tgen.add_router("PE2")
+ tgen.add_router("host1")
+ tgen.add_router("host2")
+
+ # Host1-PE1
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["host1"])
+ switch.add_link(tgen.gears["PE1"])
+
+ # PE1-P1
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["PE1"])
+ switch.add_link(tgen.gears["P1"])
+
+ # P1-PE2
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["P1"])
+ switch.add_link(tgen.gears["PE2"])
+
+ # PE2-host2
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["PE2"])
+ switch.add_link(tgen.gears["host2"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(TemplateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+ tgen.start_topology()
+
+ pe1 = tgen.gears["PE1"]
+ pe2 = tgen.gears["PE2"]
+ p1 = tgen.gears["P1"]
+
+ # set up PE bridges with the EVPN member interfaces facing the CE hosts
+ pe1.run("ip link add name br101 type bridge stp_state 0")
+ pe1.run("ip link set dev br101 up")
+ pe1.run(
+ "ip link add vxlan101 type vxlan id 101 dstport 4789 local 10.10.10.10 nolearning"
+ )
+ pe1.run("ip link set dev vxlan101 master br101")
+ pe1.run("ip link set up dev vxlan101")
+ pe1.run("ip link set dev PE1-eth0 master br101")
+
+ pe2.run("ip link add name br101 type bridge stp_state 0")
+ pe2.run("ip link set dev br101 up")
+ pe2.run(
+ "ip link add vxlan101 type vxlan id 101 dstport 4789 local 10.30.30.30 nolearning"
+ )
+ pe2.run("ip link set dev vxlan101 master br101")
+ pe2.run("ip link set up dev vxlan101")
+ pe2.run("ip link set dev PE2-eth1 master br101")
+ p1.run("sysctl -w net.ipv4.ip_forward=1")
+
+ # This is a sample of configuration loading.
+ router_list = tgen.routers()
+
+ # For all registred routers, load the zebra configuration file
+ 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_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ # After loading the configurations, this function loads configured daemons.
+ 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_pe1_converge_evpn():
+ "Wait for protocol convergence"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ pe1 = tgen.gears["PE1"]
+ json_file = "{}/{}/evpn.vni.json".format(CWD, pe1.name)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp, pe1, "show evpn vni 101 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=125, wait=1)
+ assertmsg = '"{}" JSON output mismatches'.format(pe1.name)
+ assert result is None, assertmsg
+ # tgen.mininet_cli()
+
+
+def test_pe2_converge_evpn():
+ "Wait for protocol convergence"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ pe2 = tgen.gears["PE2"]
+ json_file = "{}/{}/evpn.vni.json".format(CWD, pe2.name)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp, pe2, "show evpn vni 101 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=125, wait=1)
+ assertmsg = '"{}" JSON output mismatches'.format(pe2.name)
+ assert result is None, assertmsg
+ # tgen.mininet_cli()
+
+
+def mac_learn_test(host, local):
+ "check the host MAC gets learned by the VNI"
+
+ host_output = host.vtysh_cmd("show interface {}-eth0".format(host.name))
+ int_lines = host_output.splitlines()
+ line_items = int_lines[7].split(": ")
+ mac = line_items[1]
+ mac_output = local.vtysh_cmd("show evpn mac vni 101 mac {} json".format(mac))
+ mac_output_json = json.loads(mac_output)
+ assertmsg = "Local MAC output does not match interface mac {}".format(mac)
+ assert mac_output_json[mac]["type"] == "local"
+
+
+def mac_test_local_remote(local, remote):
+ "test MAC transfer between local and remote"
+
+ local_output = local.vtysh_cmd("show evpn mac vni all json")
+ remote_output = remote.vtysh_cmd("show evpn mac vni all json")
+ local_output_vni = local.vtysh_cmd("show evpn vni detail json")
+ local_output_json = json.loads(local_output)
+ remote_output_json = json.loads(remote_output)
+ local_output_vni_json = json.loads(local_output_vni)
+
+ for vni in local_output_json:
+ mac_list = local_output_json[vni]["macs"]
+ for mac in mac_list:
+ if mac_list[mac]["type"] == "local" and mac_list[mac]["intf"] != "br101":
+ assertmsg = "JSON output mismatches local: {} remote: {}".format(
+ local_output_vni_json[0]["vtepIp"],
+ remote_output_json[vni]["macs"][mac]["remoteVtep"],
+ )
+ assert (
+ remote_output_json[vni]["macs"][mac]["remoteVtep"]
+ == local_output_vni_json[0]["vtepIp"]
+ ), assertmsg
+
+
+def test_learning_pe1():
+ "test MAC learning on PE1"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ host1 = tgen.gears["host1"]
+ pe1 = tgen.gears["PE1"]
+ mac_learn_test(host1, pe1)
+
+
+def test_learning_pe2():
+ "test MAC learning on PE2"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ host2 = tgen.gears["host2"]
+ pe2 = tgen.gears["PE2"]
+ mac_learn_test(host2, pe2)
+
+
+def test_local_remote_mac_pe1():
+ " Test MAC transfer PE1 local and PE2 remote"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ pe1 = tgen.gears["PE1"]
+ pe2 = tgen.gears["PE2"]
+ mac_test_local_remote(pe1, pe2)
+
+
+def test_local_remote_mac_pe2():
+ " Test MAC transfer PE2 local and PE1 remote"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ pe1 = tgen.gears["PE1"]
+ pe2 = tgen.gears["PE2"]
+ mac_test_local_remote(pe2, pe1)
+
+ # Memory leak test template
+
+
+def test_memory_leak():
+ "Run the memory leak test and report results."
+ tgen = get_topogen()
+ if not tgen.is_memleak_enabled():
+ pytest.skip("Memory leak test/report is disabled")
+
+ tgen.report_memory_leaks()
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/zebra/dplane_fpm_nl.c b/zebra/dplane_fpm_nl.c
new file mode 100644
index 0000000000..a697a306bf
--- /dev/null
+++ b/zebra/dplane_fpm_nl.c
@@ -0,0 +1,1127 @@
+/*
+ * Zebra dataplane plugin for Forwarding Plane Manager (FPM) using netlink.
+ *
+ * Copyright (C) 2019 Network Device Education Foundation, Inc. ("NetDEF")
+ * Rafael Zalamena
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 <arpa/inet.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <errno.h>
+#include <string.h>
+
+#include "config.h" /* Include this explicitly */
+#include "lib/zebra.h"
+#include "lib/json.h"
+#include "lib/libfrr.h"
+#include "lib/frratomic.h"
+#include "lib/command.h"
+#include "lib/memory.h"
+#include "lib/network.h"
+#include "lib/ns.h"
+#include "lib/frr_pthread.h"
+#include "zebra/debug.h"
+#include "zebra/interface.h"
+#include "zebra/zebra_dplane.h"
+#include "zebra/zebra_router.h"
+#include "zebra/zebra_vxlan_private.h"
+#include "zebra/kernel_netlink.h"
+#include "zebra/rt_netlink.h"
+#include "zebra/debug.h"
+
+#define SOUTHBOUND_DEFAULT_ADDR INADDR_LOOPBACK
+#define SOUTHBOUND_DEFAULT_PORT 2620
+
+/**
+ * FPM header:
+ * {
+ * version: 1 byte (always 1),
+ * type: 1 byte (1 for netlink, 2 protobuf),
+ * len: 2 bytes (network order),
+ * }
+ *
+ * This header is used with any format to tell the users how many bytes to
+ * expect.
+ */
+#define FPM_HEADER_SIZE 4
+
+static const char *prov_name = "dplane_fpm_nl";
+
+struct fpm_nl_ctx {
+ /* data plane connection. */
+ int socket;
+ bool disabled;
+ bool connecting;
+ bool rib_complete;
+ bool rmac_complete;
+ struct sockaddr_storage addr;
+
+ /* data plane buffers. */
+ struct stream *ibuf;
+ struct stream *obuf;
+ pthread_mutex_t obuf_mutex;
+
+ /*
+ * data plane context queue:
+ * When a FPM server connection becomes a bottleneck, we must keep the
+ * data plane contexts until we get a chance to process them.
+ */
+ struct dplane_ctx_q ctxqueue;
+ pthread_mutex_t ctxqueue_mutex;
+
+ /* data plane events. */
+ struct zebra_dplane_provider *prov;
+ struct frr_pthread *fthread;
+ struct thread *t_connect;
+ struct thread *t_read;
+ struct thread *t_write;
+ struct thread *t_event;
+ struct thread *t_dequeue;
+
+ /* zebra events. */
+ struct thread *t_ribreset;
+ struct thread *t_ribwalk;
+ struct thread *t_rmacreset;
+ struct thread *t_rmacwalk;
+
+ /* Statistic counters. */
+ struct {
+ /* Amount of bytes read into ibuf. */
+ _Atomic uint32_t bytes_read;
+ /* Amount of bytes written from obuf. */
+ _Atomic uint32_t bytes_sent;
+ /* Output buffer current usage. */
+ _Atomic uint32_t obuf_bytes;
+ /* Output buffer peak usage. */
+ _Atomic uint32_t obuf_peak;
+
+ /* Amount of connection closes. */
+ _Atomic uint32_t connection_closes;
+ /* Amount of connection errors. */
+ _Atomic uint32_t connection_errors;
+
+ /* Amount of user configurations: FNE_RECONNECT. */
+ _Atomic uint32_t user_configures;
+ /* Amount of user disable requests: FNE_DISABLE. */
+ _Atomic uint32_t user_disables;
+
+ /* Amount of data plane context processed. */
+ _Atomic uint32_t dplane_contexts;
+ /* Amount of data plane contexts enqueued. */
+ _Atomic uint32_t ctxqueue_len;
+ /* Peak amount of data plane contexts enqueued. */
+ _Atomic uint32_t ctxqueue_len_peak;
+
+ /* Amount of buffer full events. */
+ _Atomic uint32_t buffer_full;
+ } counters;
+} *gfnc;
+
+enum fpm_nl_events {
+ /* Ask for FPM to reconnect the external server. */
+ FNE_RECONNECT,
+ /* Disable FPM. */
+ FNE_DISABLE,
+ /* Reset counters. */
+ FNE_RESET_COUNTERS,
+};
+
+/*
+ * Prototypes.
+ */
+static int fpm_process_event(struct thread *t);
+static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx);
+static int fpm_rib_send(struct thread *t);
+static int fpm_rib_reset(struct thread *t);
+static int fpm_rmac_send(struct thread *t);
+static int fpm_rmac_reset(struct thread *t);
+
+/*
+ * Helper functions.
+ */
+
+/**
+ * Reorganizes the data on the buffer so it can fit more data.
+ *
+ * @param s stream pointer.
+ */
+static void stream_pulldown(struct stream *s)
+{
+ size_t rlen = STREAM_READABLE(s);
+
+ /* No more data, so just move the pointers. */
+ if (rlen == 0) {
+ stream_reset(s);
+ return;
+ }
+
+ /* Move the available data to the beginning. */
+ memmove(s->data, &s->data[s->getp], rlen);
+ s->getp = 0;
+ s->endp = rlen;
+}
+
+/*
+ * CLI.
+ */
+#define FPM_STR "Forwarding Plane Manager configuration\n"
+
+DEFUN(fpm_set_address, fpm_set_address_cmd,
+ "fpm address <A.B.C.D|X:X::X:X> [port (1-65535)]",
+ FPM_STR
+ "FPM remote listening server address\n"
+ "Remote IPv4 FPM server\n"
+ "Remote IPv6 FPM server\n"
+ "FPM remote listening server port\n"
+ "Remote FPM server port\n")
+{
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ uint16_t port = 0;
+ uint8_t naddr[INET6_BUFSIZ];
+
+ if (argc == 5)
+ port = strtol(argv[4]->arg, NULL, 10);
+
+ /* Handle IPv4 addresses. */
+ if (inet_pton(AF_INET, argv[2]->arg, naddr) == 1) {
+ sin = (struct sockaddr_in *)&gfnc->addr;
+
+ memset(sin, 0, sizeof(*sin));
+ sin->sin_family = AF_INET;
+ sin->sin_port =
+ port ? htons(port) : htons(SOUTHBOUND_DEFAULT_PORT);
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ sin->sin_len = sizeof(*sin);
+#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
+ memcpy(&sin->sin_addr, naddr, sizeof(sin->sin_addr));
+
+ goto ask_reconnect;
+ }
+
+ /* Handle IPv6 addresses. */
+ if (inet_pton(AF_INET6, argv[2]->arg, naddr) != 1) {
+ vty_out(vty, "%% Invalid address: %s\n", argv[2]->arg);
+ return CMD_WARNING;
+ }
+
+ sin6 = (struct sockaddr_in6 *)&gfnc->addr;
+ memset(sin6, 0, sizeof(*sin6));
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = port ? htons(port) : htons(SOUTHBOUND_DEFAULT_PORT);
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ sin6->sin6_len = sizeof(*sin6);
+#endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */
+ memcpy(&sin6->sin6_addr, naddr, sizeof(sin6->sin6_addr));
+
+ask_reconnect:
+ thread_add_event(gfnc->fthread->master, fpm_process_event, gfnc,
+ FNE_RECONNECT, &gfnc->t_event);
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_fpm_set_address, no_fpm_set_address_cmd,
+ "no fpm address [<A.B.C.D|X:X::X:X> [port <1-65535>]]",
+ NO_STR
+ FPM_STR
+ "FPM remote listening server address\n"
+ "Remote IPv4 FPM server\n"
+ "Remote IPv6 FPM server\n"
+ "FPM remote listening server port\n"
+ "Remote FPM server port\n")
+{
+ thread_add_event(gfnc->fthread->master, fpm_process_event, gfnc,
+ FNE_DISABLE, &gfnc->t_event);
+ return CMD_SUCCESS;
+}
+
+DEFUN(fpm_reset_counters, fpm_reset_counters_cmd,
+ "clear fpm counters",
+ CLEAR_STR
+ FPM_STR
+ "FPM statistic counters\n")
+{
+ thread_add_event(gfnc->fthread->master, fpm_process_event, gfnc,
+ FNE_RESET_COUNTERS, &gfnc->t_event);
+ return CMD_SUCCESS;
+}
+
+DEFUN(fpm_show_counters, fpm_show_counters_cmd,
+ "show fpm counters",
+ SHOW_STR
+ FPM_STR
+ "FPM statistic counters\n")
+{
+ vty_out(vty, "%30s\n%30s\n", "FPM counters", "============");
+
+#define SHOW_COUNTER(label, counter) \
+ vty_out(vty, "%28s: %u\n", (label), (counter))
+
+ SHOW_COUNTER("Input bytes", gfnc->counters.bytes_read);
+ SHOW_COUNTER("Output bytes", gfnc->counters.bytes_sent);
+ SHOW_COUNTER("Output buffer current size", gfnc->counters.obuf_bytes);
+ SHOW_COUNTER("Output buffer peak size", gfnc->counters.obuf_peak);
+ SHOW_COUNTER("Connection closes", gfnc->counters.connection_closes);
+ SHOW_COUNTER("Connection errors", gfnc->counters.connection_errors);
+ SHOW_COUNTER("Data plane items processed",
+ gfnc->counters.dplane_contexts);
+ SHOW_COUNTER("Data plane items enqueued",
+ gfnc->counters.ctxqueue_len);
+ SHOW_COUNTER("Data plane items queue peak",
+ gfnc->counters.ctxqueue_len_peak);
+ SHOW_COUNTER("Buffer full hits", gfnc->counters.buffer_full);
+ SHOW_COUNTER("User FPM configurations", gfnc->counters.user_configures);
+ SHOW_COUNTER("User FPM disable requests", gfnc->counters.user_disables);
+
+#undef SHOW_COUNTER
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(fpm_show_counters_json, fpm_show_counters_json_cmd,
+ "show fpm counters json",
+ SHOW_STR
+ FPM_STR
+ "FPM statistic counters\n"
+ JSON_STR)
+{
+ struct json_object *jo;
+
+ jo = json_object_new_object();
+ json_object_int_add(jo, "bytes-read", gfnc->counters.bytes_read);
+ json_object_int_add(jo, "bytes-sent", gfnc->counters.bytes_sent);
+ json_object_int_add(jo, "obuf-bytes", gfnc->counters.obuf_bytes);
+ json_object_int_add(jo, "obuf-bytes-peak", gfnc->counters.obuf_peak);
+ json_object_int_add(jo, "connection-closes",
+ gfnc->counters.connection_closes);
+ json_object_int_add(jo, "connection-errors",
+ gfnc->counters.connection_errors);
+ json_object_int_add(jo, "data-plane-contexts",
+ gfnc->counters.dplane_contexts);
+ json_object_int_add(jo, "data-plane-contexts-queue",
+ gfnc->counters.ctxqueue_len);
+ json_object_int_add(jo, "data-plane-contexts-queue-peak",
+ gfnc->counters.ctxqueue_len_peak);
+ json_object_int_add(jo, "buffer-full-hits", gfnc->counters.buffer_full);
+ json_object_int_add(jo, "user-configures",
+ gfnc->counters.user_configures);
+ json_object_int_add(jo, "user-disables", gfnc->counters.user_disables);
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(jo, 0));
+ json_object_free(jo);
+
+ return CMD_SUCCESS;
+}
+
+static int fpm_write_config(struct vty *vty)
+{
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ int written = 0;
+ char addrstr[INET6_ADDRSTRLEN];
+
+ if (gfnc->disabled)
+ return written;
+
+ switch (gfnc->addr.ss_family) {
+ case AF_INET:
+ written = 1;
+ sin = (struct sockaddr_in *)&gfnc->addr;
+ inet_ntop(AF_INET, &sin->sin_addr, addrstr, sizeof(addrstr));
+ vty_out(vty, "fpm address %s", addrstr);
+ if (sin->sin_port != htons(SOUTHBOUND_DEFAULT_PORT))
+ vty_out(vty, " port %d", ntohs(sin->sin_port));
+
+ vty_out(vty, "\n");
+ break;
+ case AF_INET6:
+ written = 1;
+ sin6 = (struct sockaddr_in6 *)&gfnc->addr;
+ inet_ntop(AF_INET, &sin6->sin6_addr, addrstr, sizeof(addrstr));
+ vty_out(vty, "fpm address %s", addrstr);
+ if (sin6->sin6_port != htons(SOUTHBOUND_DEFAULT_PORT))
+ vty_out(vty, " port %d", ntohs(sin6->sin6_port));
+
+ vty_out(vty, "\n");
+ break;
+
+ default:
+ break;
+ }
+
+ return written;
+}
+
+struct cmd_node fpm_node = {
+ .node = VTY_NODE,
+ .prompt = "",
+ .vtysh = 1,
+};
+
+/*
+ * FPM functions.
+ */
+static int fpm_connect(struct thread *t);
+
+static void fpm_reconnect(struct fpm_nl_ctx *fnc)
+{
+ /* Grab the lock to empty the stream and stop the zebra thread. */
+ frr_mutex_lock_autounlock(&fnc->obuf_mutex);
+
+ /* Avoid calling close on `-1`. */
+ if (fnc->socket != -1) {
+ close(fnc->socket);
+ fnc->socket = -1;
+ }
+
+ stream_reset(fnc->ibuf);
+ stream_reset(fnc->obuf);
+ THREAD_OFF(fnc->t_read);
+ THREAD_OFF(fnc->t_write);
+
+ if (fnc->t_ribreset)
+ thread_cancel_async(zrouter.master, &fnc->t_ribreset, NULL);
+ if (fnc->t_ribwalk)
+ thread_cancel_async(zrouter.master, &fnc->t_ribwalk, NULL);
+ if (fnc->t_rmacreset)
+ thread_cancel_async(zrouter.master, &fnc->t_rmacreset, NULL);
+ if (fnc->t_rmacwalk)
+ thread_cancel_async(zrouter.master, &fnc->t_rmacwalk, NULL);
+
+ /* FPM is disabled, don't attempt to connect. */
+ if (fnc->disabled)
+ return;
+
+ thread_add_timer(fnc->fthread->master, fpm_connect, fnc, 3,
+ &fnc->t_connect);
+}
+
+static int fpm_read(struct thread *t)
+{
+ struct fpm_nl_ctx *fnc = THREAD_ARG(t);
+ ssize_t rv;
+
+ /* Let's ignore the input at the moment. */
+ rv = stream_read_try(fnc->ibuf, fnc->socket,
+ STREAM_WRITEABLE(fnc->ibuf));
+ if (rv == 0) {
+ atomic_fetch_add_explicit(&fnc->counters.connection_closes, 1,
+ memory_order_relaxed);
+
+ if (IS_ZEBRA_DEBUG_FPM)
+ zlog_debug("%s: connection closed", __func__);
+
+ fpm_reconnect(fnc);
+ return 0;
+ }
+ if (rv == -1) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK
+ || errno == EINTR)
+ return 0;
+
+ atomic_fetch_add_explicit(&fnc->counters.connection_errors, 1,
+ memory_order_relaxed);
+ zlog_warn("%s: connection failure: %s", __func__,
+ strerror(errno));
+ fpm_reconnect(fnc);
+ return 0;
+ }
+ stream_reset(fnc->ibuf);
+
+ /* Account all bytes read. */
+ atomic_fetch_add_explicit(&fnc->counters.bytes_read, rv,
+ memory_order_relaxed);
+
+ thread_add_read(fnc->fthread->master, fpm_read, fnc, fnc->socket,
+ &fnc->t_read);
+
+ return 0;
+}
+
+static int fpm_write(struct thread *t)
+{
+ struct fpm_nl_ctx *fnc = THREAD_ARG(t);
+ socklen_t statuslen;
+ ssize_t bwritten;
+ int rv, status;
+ size_t btotal;
+
+ if (fnc->connecting == true) {
+ status = 0;
+ statuslen = sizeof(status);
+
+ rv = getsockopt(fnc->socket, SOL_SOCKET, SO_ERROR, &status,
+ &statuslen);
+ if (rv == -1 || status != 0) {
+ if (rv != -1)
+ zlog_warn("%s: connection failed: %s", __func__,
+ strerror(status));
+ else
+ zlog_warn("%s: SO_ERROR failed: %s", __func__,
+ strerror(status));
+
+ atomic_fetch_add_explicit(
+ &fnc->counters.connection_errors, 1,
+ memory_order_relaxed);
+
+ fpm_reconnect(fnc);
+ return 0;
+ }
+
+ fnc->connecting = false;
+
+ /* Ask zebra main thread to start walking the RIB table. */
+ thread_add_timer(zrouter.master, fpm_rib_send, fnc, 0,
+ &fnc->t_ribwalk);
+ thread_add_timer(zrouter.master, fpm_rmac_send, fnc, 0,
+ &fnc->t_rmacwalk);
+ }
+
+ frr_mutex_lock_autounlock(&fnc->obuf_mutex);
+
+ while (true) {
+ /* Stream is empty: reset pointers and return. */
+ if (STREAM_READABLE(fnc->obuf) == 0) {
+ stream_reset(fnc->obuf);
+ break;
+ }
+
+ /* Try to write all at once. */
+ btotal = stream_get_endp(fnc->obuf) -
+ stream_get_getp(fnc->obuf);
+ bwritten = write(fnc->socket, stream_pnt(fnc->obuf), btotal);
+ if (bwritten == 0) {
+ atomic_fetch_add_explicit(
+ &fnc->counters.connection_closes, 1,
+ memory_order_relaxed);
+
+ if (IS_ZEBRA_DEBUG_FPM)
+ zlog_debug("%s: connection closed", __func__);
+ break;
+ }
+ if (bwritten == -1) {
+ /* Attempt to continue if blocked by a signal. */
+ if (errno == EINTR)
+ continue;
+ /* Receiver is probably slow, lets give it some time. */
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ break;
+
+ atomic_fetch_add_explicit(
+ &fnc->counters.connection_errors, 1,
+ memory_order_relaxed);
+ zlog_warn("%s: connection failure: %s", __func__,
+ strerror(errno));
+ fpm_reconnect(fnc);
+ break;
+ }
+
+ /* Account all bytes sent. */
+ atomic_fetch_add_explicit(&fnc->counters.bytes_sent, bwritten,
+ memory_order_relaxed);
+
+ /* Account number of bytes free. */
+ atomic_fetch_sub_explicit(&fnc->counters.obuf_bytes, bwritten,
+ memory_order_relaxed);
+
+ stream_forward_getp(fnc->obuf, (size_t)bwritten);
+ }
+
+ /* Stream is not empty yet, we must schedule more writes. */
+ if (STREAM_READABLE(fnc->obuf)) {
+ stream_pulldown(fnc->obuf);
+ thread_add_write(fnc->fthread->master, fpm_write, fnc,
+ fnc->socket, &fnc->t_write);
+ return 0;
+ }
+
+ return 0;
+}
+
+static int fpm_connect(struct thread *t)
+{
+ struct fpm_nl_ctx *fnc = THREAD_ARG(t);
+ struct sockaddr_in *sin = (struct sockaddr_in *)&fnc->addr;
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&fnc->addr;
+ socklen_t slen;
+ int rv, sock;
+ char addrstr[INET6_ADDRSTRLEN];
+
+ sock = socket(fnc->addr.ss_family, SOCK_STREAM, 0);
+ if (sock == -1) {
+ zlog_err("%s: fpm socket failed: %s", __func__,
+ strerror(errno));
+ thread_add_timer(fnc->fthread->master, fpm_connect, fnc, 3,
+ &fnc->t_connect);
+ return 0;
+ }
+
+ set_nonblocking(sock);
+
+ if (fnc->addr.ss_family == AF_INET) {
+ inet_ntop(AF_INET, &sin->sin_addr, addrstr, sizeof(addrstr));
+ slen = sizeof(*sin);
+ } else {
+ inet_ntop(AF_INET6, &sin6->sin6_addr, addrstr, sizeof(addrstr));
+ slen = sizeof(*sin6);
+ }
+
+ if (IS_ZEBRA_DEBUG_FPM)
+ zlog_debug("%s: attempting to connect to %s:%d", __func__,
+ addrstr, ntohs(sin->sin_port));
+
+ rv = connect(sock, (struct sockaddr *)&fnc->addr, slen);
+ if (rv == -1 && errno != EINPROGRESS) {
+ atomic_fetch_add_explicit(&fnc->counters.connection_errors, 1,
+ memory_order_relaxed);
+ close(sock);
+ zlog_warn("%s: fpm connection failed: %s", __func__,
+ strerror(errno));
+ thread_add_timer(fnc->fthread->master, fpm_connect, fnc, 3,
+ &fnc->t_connect);
+ return 0;
+ }
+
+ fnc->connecting = (errno == EINPROGRESS);
+ fnc->socket = sock;
+ thread_add_read(fnc->fthread->master, fpm_read, fnc, sock,
+ &fnc->t_read);
+ thread_add_write(fnc->fthread->master, fpm_write, fnc, sock,
+ &fnc->t_write);
+
+ /* Mark all routes as unsent. */
+ thread_add_timer(zrouter.master, fpm_rib_reset, fnc, 0,
+ &fnc->t_ribreset);
+ thread_add_timer(zrouter.master, fpm_rmac_reset, fnc, 0,
+ &fnc->t_rmacreset);
+
+ return 0;
+}
+
+/**
+ * Encode data plane operation context into netlink and enqueue it in the FPM
+ * output buffer.
+ *
+ * @param fnc the netlink FPM context.
+ * @param ctx the data plane operation context data.
+ * @return 0 on success or -1 on not enough space.
+ */
+static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx)
+{
+ uint8_t nl_buf[NL_PKT_BUF_SIZE];
+ size_t nl_buf_len;
+ ssize_t rv;
+ uint64_t obytes, obytes_peak;
+
+ nl_buf_len = 0;
+
+ frr_mutex_lock_autounlock(&fnc->obuf_mutex);
+
+ switch (dplane_ctx_get_op(ctx)) {
+ case DPLANE_OP_ROUTE_UPDATE:
+ case DPLANE_OP_ROUTE_DELETE:
+ rv = netlink_route_multipath(RTM_DELROUTE, ctx, nl_buf,
+ sizeof(nl_buf), true);
+ if (rv <= 0) {
+ zlog_err("%s: netlink_route_multipath failed",
+ __func__);
+ return 0;
+ }
+
+ nl_buf_len = (size_t)rv;
+
+ /* UPDATE operations need a INSTALL, otherwise just quit. */
+ if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_DELETE)
+ break;
+
+ /* FALL THROUGH */
+ case DPLANE_OP_ROUTE_INSTALL:
+ rv = netlink_route_multipath(RTM_NEWROUTE, ctx,
+ &nl_buf[nl_buf_len],
+ sizeof(nl_buf) - nl_buf_len, true);
+ if (rv <= 0) {
+ zlog_err("%s: netlink_route_multipath failed",
+ __func__);
+ return 0;
+ }
+
+ nl_buf_len += (size_t)rv;
+ break;
+
+ case DPLANE_OP_MAC_INSTALL:
+ case DPLANE_OP_MAC_DELETE:
+ rv = netlink_macfdb_update_ctx(ctx, nl_buf, sizeof(nl_buf));
+ if (rv <= 0) {
+ zlog_err("%s: netlink_macfdb_update_ctx failed",
+ __func__);
+ return 0;
+ }
+
+ nl_buf_len = (size_t)rv;
+ break;
+
+ case DPLANE_OP_NH_INSTALL:
+ case DPLANE_OP_NH_UPDATE:
+ case DPLANE_OP_NH_DELETE:
+ case DPLANE_OP_LSP_INSTALL:
+ case DPLANE_OP_LSP_UPDATE:
+ case DPLANE_OP_LSP_DELETE:
+ case DPLANE_OP_PW_INSTALL:
+ case DPLANE_OP_PW_UNINSTALL:
+ case DPLANE_OP_ADDR_INSTALL:
+ case DPLANE_OP_ADDR_UNINSTALL:
+ 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:
+ case DPLANE_OP_SYS_ROUTE_ADD:
+ case DPLANE_OP_SYS_ROUTE_DELETE:
+ case DPLANE_OP_ROUTE_NOTIFY:
+ case DPLANE_OP_LSP_NOTIFY:
+ case DPLANE_OP_NONE:
+ break;
+
+ default:
+ if (IS_ZEBRA_DEBUG_FPM)
+ zlog_debug("%s: unhandled data plane message (%d) %s",
+ __func__, dplane_ctx_get_op(ctx),
+ dplane_op2str(dplane_ctx_get_op(ctx)));
+ break;
+ }
+
+ /* Skip empty enqueues. */
+ if (nl_buf_len == 0)
+ return 0;
+
+ /* We must know if someday a message goes beyond 65KiB. */
+ assert((nl_buf_len + FPM_HEADER_SIZE) <= UINT16_MAX);
+
+ /* Check if we have enough buffer space. */
+ if (STREAM_WRITEABLE(fnc->obuf) < (nl_buf_len + FPM_HEADER_SIZE)) {
+ atomic_fetch_add_explicit(&fnc->counters.buffer_full, 1,
+ memory_order_relaxed);
+
+ if (IS_ZEBRA_DEBUG_FPM)
+ zlog_debug(
+ "%s: buffer full: wants to write %zu but has %zu",
+ __func__, nl_buf_len + FPM_HEADER_SIZE,
+ STREAM_WRITEABLE(fnc->obuf));
+
+ return -1;
+ }
+
+ /*
+ * Fill in the FPM header information.
+ *
+ * See FPM_HEADER_SIZE definition for more information.
+ */
+ stream_putc(fnc->obuf, 1);
+ stream_putc(fnc->obuf, 1);
+ stream_putw(fnc->obuf, nl_buf_len + FPM_HEADER_SIZE);
+
+ /* Write current data. */
+ stream_write(fnc->obuf, nl_buf, (size_t)nl_buf_len);
+
+ /* Account number of bytes waiting to be written. */
+ atomic_fetch_add_explicit(&fnc->counters.obuf_bytes,
+ nl_buf_len + FPM_HEADER_SIZE,
+ memory_order_relaxed);
+ obytes = atomic_load_explicit(&fnc->counters.obuf_bytes,
+ memory_order_relaxed);
+ obytes_peak = atomic_load_explicit(&fnc->counters.obuf_peak,
+ memory_order_relaxed);
+ if (obytes_peak < obytes)
+ atomic_store_explicit(&fnc->counters.obuf_peak, obytes,
+ memory_order_relaxed);
+
+ /* Tell the thread to start writing. */
+ thread_add_write(fnc->fthread->master, fpm_write, fnc, fnc->socket,
+ &fnc->t_write);
+
+ return 0;
+}
+
+/**
+ * Send all RIB installed routes to the connected data plane.
+ */
+static int fpm_rib_send(struct thread *t)
+{
+ struct fpm_nl_ctx *fnc = THREAD_ARG(t);
+ rib_dest_t *dest;
+ struct route_node *rn;
+ struct route_table *rt;
+ struct zebra_dplane_ctx *ctx;
+ rib_tables_iter_t rt_iter;
+
+ /* Allocate temporary context for all transactions. */
+ ctx = dplane_ctx_alloc();
+
+ rt_iter.state = RIB_TABLES_ITER_S_INIT;
+ while ((rt = rib_tables_iter_next(&rt_iter))) {
+ for (rn = route_top(rt); rn; rn = srcdest_route_next(rn)) {
+ dest = rib_dest_from_rnode(rn);
+ /* Skip bad route entries. */
+ if (dest == NULL || dest->selected_fib == NULL)
+ continue;
+
+ /* Check for already sent routes. */
+ if (CHECK_FLAG(dest->flags, RIB_DEST_UPDATE_FPM))
+ continue;
+
+ /* Enqueue route install. */
+ dplane_ctx_reset(ctx);
+ dplane_ctx_route_init(ctx, DPLANE_OP_ROUTE_INSTALL, rn,
+ dest->selected_fib);
+ if (fpm_nl_enqueue(fnc, ctx) == -1) {
+ /* Free the temporary allocated context. */
+ dplane_ctx_fini(&ctx);
+
+ thread_add_timer(zrouter.master, fpm_rib_send,
+ fnc, 1, &fnc->t_ribwalk);
+ return 0;
+ }
+
+ /* Mark as sent. */
+ SET_FLAG(dest->flags, RIB_DEST_UPDATE_FPM);
+ }
+ }
+
+ /* Free the temporary allocated context. */
+ dplane_ctx_fini(&ctx);
+
+ /* All RIB routes sent! */
+ fnc->rib_complete = true;
+
+ return 0;
+}
+
+/*
+ * The next three functions will handle RMAC enqueue.
+ */
+struct fpm_rmac_arg {
+ struct zebra_dplane_ctx *ctx;
+ struct fpm_nl_ctx *fnc;
+ zebra_l3vni_t *zl3vni;
+};
+
+static void fpm_enqueue_rmac_table(struct hash_bucket *backet, void *arg)
+{
+ struct fpm_rmac_arg *fra = arg;
+ zebra_mac_t *zrmac = backet->data;
+ struct zebra_if *zif = fra->zl3vni->vxlan_if->info;
+ const struct zebra_l2info_vxlan *vxl = &zif->l2info.vxl;
+ struct zebra_if *br_zif;
+ vlanid_t vid;
+ bool sticky;
+
+ /* Entry already sent. */
+ if (CHECK_FLAG(zrmac->flags, ZEBRA_MAC_FPM_SENT))
+ return;
+
+ sticky = !!CHECK_FLAG(zrmac->flags,
+ (ZEBRA_MAC_STICKY | ZEBRA_MAC_REMOTE_DEF_GW));
+ br_zif = (struct zebra_if *)(zif->brslave_info.br_if->info);
+ vid = IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif) ? vxl->access_vlan : 0;
+
+ dplane_ctx_reset(fra->ctx);
+ dplane_ctx_set_op(fra->ctx, DPLANE_OP_MAC_INSTALL);
+ dplane_mac_init(fra->ctx, fra->zl3vni->vxlan_if,
+ zif->brslave_info.br_if, vid,
+ &zrmac->macaddr, zrmac->fwd_info.r_vtep_ip, sticky);
+ if (fpm_nl_enqueue(fra->fnc, fra->ctx) == -1) {
+ thread_add_timer(zrouter.master, fpm_rmac_send,
+ fra->fnc, 1, &fra->fnc->t_rmacwalk);
+ }
+}
+
+static void fpm_enqueue_l3vni_table(struct hash_bucket *backet, void *arg)
+{
+ struct fpm_rmac_arg *fra = arg;
+ zebra_l3vni_t *zl3vni = backet->data;
+
+ fra->zl3vni = zl3vni;
+ hash_iterate(zl3vni->rmac_table, fpm_enqueue_rmac_table, zl3vni);
+}
+
+static int fpm_rmac_send(struct thread *t)
+{
+ struct fpm_rmac_arg fra;
+
+ fra.fnc = THREAD_ARG(t);
+ fra.ctx = dplane_ctx_alloc();
+ hash_iterate(zrouter.l3vni_table, fpm_enqueue_l3vni_table, &fra);
+ dplane_ctx_fini(&fra.ctx);
+
+ return 0;
+}
+
+/**
+ * Resets the RIB FPM flags so we send all routes again.
+ */
+static int fpm_rib_reset(struct thread *t)
+{
+ struct fpm_nl_ctx *fnc = THREAD_ARG(t);
+ rib_dest_t *dest;
+ struct route_node *rn;
+ struct route_table *rt;
+ rib_tables_iter_t rt_iter;
+
+ fnc->rib_complete = false;
+
+ rt_iter.state = RIB_TABLES_ITER_S_INIT;
+ while ((rt = rib_tables_iter_next(&rt_iter))) {
+ for (rn = route_top(rt); rn; rn = srcdest_route_next(rn)) {
+ dest = rib_dest_from_rnode(rn);
+ /* Skip bad route entries. */
+ if (dest == NULL)
+ continue;
+
+ UNSET_FLAG(dest->flags, RIB_DEST_UPDATE_FPM);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * The next three function will handle RMAC table reset.
+ */
+static void fpm_unset_rmac_table(struct hash_bucket *backet, void *arg)
+{
+ zebra_mac_t *zrmac = backet->data;
+
+ UNSET_FLAG(zrmac->flags, ZEBRA_MAC_FPM_SENT);
+}
+
+static void fpm_unset_l3vni_table(struct hash_bucket *backet, void *arg)
+{
+ zebra_l3vni_t *zl3vni = backet->data;
+
+ hash_iterate(zl3vni->rmac_table, fpm_unset_rmac_table, zl3vni);
+}
+
+static int fpm_rmac_reset(struct thread *t)
+{
+ hash_iterate(zrouter.l3vni_table, fpm_unset_l3vni_table, NULL);
+
+ return 0;
+}
+
+static int fpm_process_queue(struct thread *t)
+{
+ struct fpm_nl_ctx *fnc = THREAD_ARG(t);
+ struct zebra_dplane_ctx *ctx;
+
+ frr_mutex_lock_autounlock(&fnc->ctxqueue_mutex);
+
+ while (true) {
+ /* No space available yet. */
+ if (STREAM_WRITEABLE(fnc->obuf) < NL_PKT_BUF_SIZE)
+ break;
+
+ /* Dequeue next item or quit processing. */
+ ctx = dplane_ctx_dequeue(&fnc->ctxqueue);
+ if (ctx == NULL)
+ break;
+
+ fpm_nl_enqueue(fnc, ctx);
+
+ /* Account the processed entries. */
+ atomic_fetch_add_explicit(&fnc->counters.dplane_contexts, 1,
+ memory_order_relaxed);
+ atomic_fetch_sub_explicit(&fnc->counters.ctxqueue_len, 1,
+ memory_order_relaxed);
+
+ dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS);
+ dplane_provider_enqueue_out_ctx(fnc->prov, ctx);
+ }
+
+ /* Check for more items in the queue. */
+ if (atomic_load_explicit(&fnc->counters.ctxqueue_len,
+ memory_order_relaxed)
+ > 0)
+ thread_add_timer(fnc->fthread->master, fpm_process_queue,
+ fnc, 0, &fnc->t_dequeue);
+
+ return 0;
+}
+
+/**
+ * Handles external (e.g. CLI, data plane or others) events.
+ */
+static int fpm_process_event(struct thread *t)
+{
+ struct fpm_nl_ctx *fnc = THREAD_ARG(t);
+ int event = THREAD_VAL(t);
+
+ switch (event) {
+ case FNE_DISABLE:
+ zlog_info("%s: manual FPM disable event", __func__);
+ fnc->disabled = true;
+ atomic_fetch_add_explicit(&fnc->counters.user_disables, 1,
+ memory_order_relaxed);
+
+ /* Call reconnect to disable timers and clean up context. */
+ fpm_reconnect(fnc);
+ break;
+
+ case FNE_RECONNECT:
+ zlog_info("%s: manual FPM reconnect event", __func__);
+ fnc->disabled = false;
+ atomic_fetch_add_explicit(&fnc->counters.user_configures, 1,
+ memory_order_relaxed);
+ fpm_reconnect(fnc);
+ break;
+
+ case FNE_RESET_COUNTERS:
+ zlog_info("%s: manual FPM counters reset event", __func__);
+ memset(&fnc->counters, 0, sizeof(fnc->counters));
+ break;
+
+ default:
+ if (IS_ZEBRA_DEBUG_FPM)
+ zlog_debug("%s: unhandled event %d", __func__, event);
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * Data plane functions.
+ */
+static int fpm_nl_start(struct zebra_dplane_provider *prov)
+{
+ struct fpm_nl_ctx *fnc;
+
+ fnc = dplane_provider_get_data(prov);
+ fnc->fthread = frr_pthread_new(NULL, prov_name, prov_name);
+ assert(frr_pthread_run(fnc->fthread, NULL) == 0);
+ fnc->ibuf = stream_new(NL_PKT_BUF_SIZE);
+ fnc->obuf = stream_new(NL_PKT_BUF_SIZE * 128);
+ pthread_mutex_init(&fnc->obuf_mutex, NULL);
+ fnc->socket = -1;
+ fnc->disabled = true;
+ fnc->prov = prov;
+ TAILQ_INIT(&fnc->ctxqueue);
+ pthread_mutex_init(&fnc->ctxqueue_mutex, NULL);
+
+ return 0;
+}
+
+static int fpm_nl_finish(struct zebra_dplane_provider *prov, bool early)
+{
+ struct fpm_nl_ctx *fnc;
+
+ fnc = dplane_provider_get_data(prov);
+ stream_free(fnc->ibuf);
+ stream_free(fnc->obuf);
+ close(fnc->socket);
+
+ return 0;
+}
+
+static int fpm_nl_process(struct zebra_dplane_provider *prov)
+{
+ struct zebra_dplane_ctx *ctx;
+ struct fpm_nl_ctx *fnc;
+ int counter, limit;
+ uint64_t cur_queue, peak_queue;
+
+ fnc = dplane_provider_get_data(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;
+
+ /*
+ * Skip all notifications if not connected, we'll walk the RIB
+ * anyway.
+ */
+ if (fnc->socket != -1 && fnc->connecting == false) {
+ frr_mutex_lock_autounlock(&fnc->ctxqueue_mutex);
+ dplane_ctx_enqueue_tail(&fnc->ctxqueue, ctx);
+
+ /* Account the number of contexts. */
+ atomic_fetch_add_explicit(&fnc->counters.ctxqueue_len,
+ 1, memory_order_relaxed);
+ cur_queue = atomic_load_explicit(
+ &fnc->counters.ctxqueue_len,
+ memory_order_relaxed);
+ peak_queue = atomic_load_explicit(
+ &fnc->counters.ctxqueue_len_peak,
+ memory_order_relaxed);
+ if (peak_queue < cur_queue)
+ atomic_store_explicit(
+ &fnc->counters.ctxqueue_len_peak,
+ peak_queue, memory_order_relaxed);
+ continue;
+ }
+
+ dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS);
+ dplane_provider_enqueue_out_ctx(prov, ctx);
+ }
+
+ if (atomic_load_explicit(&fnc->counters.ctxqueue_len,
+ memory_order_relaxed)
+ > 0)
+ thread_add_timer(fnc->fthread->master, fpm_process_queue,
+ fnc, 0, &fnc->t_dequeue);
+
+ return 0;
+}
+
+static int fpm_nl_new(struct thread_master *tm)
+{
+ struct zebra_dplane_provider *prov = NULL;
+ int rv;
+
+ gfnc = calloc(1, sizeof(*gfnc));
+ rv = dplane_provider_register(prov_name, DPLANE_PRIO_POSTPROCESS,
+ DPLANE_PROV_FLAG_THREADED, fpm_nl_start,
+ fpm_nl_process, fpm_nl_finish, gfnc,
+ &prov);
+
+ if (IS_ZEBRA_DEBUG_DPLANE)
+ zlog_debug("%s register status: %d", prov_name, rv);
+
+ install_node(&fpm_node, fpm_write_config);
+ install_element(ENABLE_NODE, &fpm_show_counters_cmd);
+ install_element(ENABLE_NODE, &fpm_show_counters_json_cmd);
+ install_element(ENABLE_NODE, &fpm_reset_counters_cmd);
+ install_element(CONFIG_NODE, &fpm_set_address_cmd);
+ install_element(CONFIG_NODE, &no_fpm_set_address_cmd);
+
+ return 0;
+}
+
+static int fpm_nl_init(void)
+{
+ hook_register(frr_late_init, fpm_nl_new);
+ return 0;
+}
+
+FRR_MODULE_SETUP(
+ .name = "dplane_fpm_nl",
+ .version = "0.0.1",
+ .description = "Data plane plugin for FPM using netlink.",
+ .init = fpm_nl_init,
+ )
diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c
index b6224b3da9..c4af082e72 100644
--- a/zebra/rt_netlink.c
+++ b/zebra/rt_netlink.c
@@ -1123,6 +1123,7 @@ static void _netlink_route_build_singlepath(const struct prefix *p,
char label_buf[256];
int num_labels = 0;
struct vrf *vrf;
+ char addrstr[INET6_ADDRSTRLEN];
assert(nexthop);
@@ -1179,11 +1180,10 @@ static void _netlink_route_build_singlepath(const struct prefix *p,
&nexthop->src.ipv4, bytelen);
if (IS_ZEBRA_DEBUG_KERNEL)
- zlog_debug(
- " 5549: _netlink_route_build_singlepath() (%s): %pFX nexthop via %s %s if %u vrf %s(%u)",
- routedesc, p, ipv4_ll_buf, label_buf,
- nexthop->ifindex, VRF_LOGNAME(vrf),
- nexthop->vrf_id);
+ zlog_debug("%s: 5549 (%s): %pFX nexthop via %s %s if %u vrf %s(%u)",
+ __func__, routedesc, p, ipv4_ll_buf,
+ label_buf, nexthop->ifindex,
+ VRF_LOGNAME(vrf), nexthop->vrf_id);
return;
}
@@ -1204,12 +1204,14 @@ static void _netlink_route_build_singlepath(const struct prefix *p,
&nexthop->src.ipv4, bytelen);
}
- if (IS_ZEBRA_DEBUG_KERNEL)
- zlog_debug(
- "netlink_route_multipath() (%s): %pFX nexthop via %s %s if %u vrf %s(%u)",
- routedesc, p, inet_ntoa(nexthop->gate.ipv4),
- label_buf, nexthop->ifindex, VRF_LOGNAME(vrf),
- nexthop->vrf_id);
+ if (IS_ZEBRA_DEBUG_KERNEL) {
+ inet_ntop(AF_INET, &nexthop->gate.ipv4, addrstr,
+ sizeof(addrstr));
+ zlog_debug("%s: (%s): %pFX nexthop via %s %s if %u vrf %s(%u)",
+ __func__, routedesc, p, addrstr, label_buf,
+ nexthop->ifindex, VRF_LOGNAME(vrf),
+ nexthop->vrf_id);
+ }
}
if (nexthop->type == NEXTHOP_TYPE_IPV6
@@ -1227,12 +1229,14 @@ static void _netlink_route_build_singlepath(const struct prefix *p,
&nexthop->src.ipv6, bytelen);
}
- if (IS_ZEBRA_DEBUG_KERNEL)
- zlog_debug(
- "netlink_route_multipath() (%s): %pFX nexthop via %s %s if %u vrf %s(%u)",
- routedesc, p, inet6_ntoa(nexthop->gate.ipv6),
- label_buf, nexthop->ifindex, VRF_LOGNAME(vrf),
- nexthop->vrf_id);
+ if (IS_ZEBRA_DEBUG_KERNEL) {
+ inet_ntop(AF_INET6, &nexthop->gate.ipv6, addrstr,
+ sizeof(addrstr));
+ zlog_debug("%s: (%s): %pFX nexthop via %s %s if %u vrf %s(%u)",
+ __func__, routedesc, p, addrstr, label_buf,
+ nexthop->ifindex, VRF_LOGNAME(vrf),
+ nexthop->vrf_id);
+ }
}
/*
@@ -1254,10 +1258,9 @@ static void _netlink_route_build_singlepath(const struct prefix *p,
}
if (IS_ZEBRA_DEBUG_KERNEL)
- zlog_debug(
- "netlink_route_multipath() (%s): %pFX nexthop via if %u vrf %s(%u)",
- routedesc, p, nexthop->ifindex,
- VRF_LOGNAME(vrf), nexthop->vrf_id);
+ zlog_debug("%s: (%s): %pFX nexthop via if %u vrf %s(%u)",
+ __func__, routedesc, p, nexthop->ifindex,
+ VRF_LOGNAME(vrf), nexthop->vrf_id);
}
}
@@ -1356,8 +1359,8 @@ _netlink_route_build_multipath(const struct prefix *p, const char *routedesc,
if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug(
- " 5549: netlink_route_build_multipath() (%s): %pFX nexthop via %s %s if %u vrf %s(%u)",
- routedesc, p, ipv4_ll_buf, label_buf,
+ "%s: 5549 (%s): %pFX nexthop via %s %s if %u vrf %s(%u)",
+ __func__, routedesc, p, ipv4_ll_buf, label_buf,
nexthop->ifindex, VRF_LOGNAME(vrf),
nexthop->vrf_id);
return;
@@ -1374,11 +1377,10 @@ _netlink_route_build_multipath(const struct prefix *p, const char *routedesc,
*src = &nexthop->src;
if (IS_ZEBRA_DEBUG_KERNEL)
- zlog_debug(
- "netlink_route_multipath() (%s): %pFX nexthop via %s %s if %u vrf %s(%u)",
- routedesc, p, inet_ntoa(nexthop->gate.ipv4),
- label_buf, nexthop->ifindex, VRF_LOGNAME(vrf),
- nexthop->vrf_id);
+ zlog_debug("%s: (%s): %pFX nexthop via %pI4 %s if %u vrf %s(%u)",
+ __func__, routedesc, p, &nexthop->gate.ipv4,
+ label_buf, nexthop->ifindex,
+ VRF_LOGNAME(vrf), nexthop->vrf_id);
}
if (nexthop->type == NEXTHOP_TYPE_IPV6
|| nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) {
@@ -1392,11 +1394,10 @@ _netlink_route_build_multipath(const struct prefix *p, const char *routedesc,
*src = &nexthop->src;
if (IS_ZEBRA_DEBUG_KERNEL)
- zlog_debug(
- "netlink_route_multipath() (%s): %pFX nexthop via %s %s if %u vrf %s(%u)",
- routedesc, p, inet6_ntoa(nexthop->gate.ipv6),
- label_buf, nexthop->ifindex, VRF_LOGNAME(vrf),
- nexthop->vrf_id);
+ zlog_debug("%s: (%s): %pFX nexthop via %pI6 %s if %u vrf %s(%u)",
+ __func__, routedesc, p, &nexthop->gate.ipv6,
+ label_buf, nexthop->ifindex,
+ VRF_LOGNAME(vrf), nexthop->vrf_id);
}
/*
@@ -1415,10 +1416,9 @@ _netlink_route_build_multipath(const struct prefix *p, const char *routedesc,
*src = &nexthop->src;
if (IS_ZEBRA_DEBUG_KERNEL)
- zlog_debug(
- "netlink_route_multipath() (%s): %pFX nexthop via if %u vrf %s(%u)",
- routedesc, p, nexthop->ifindex,
- VRF_LOGNAME(vrf), nexthop->vrf_id);
+ zlog_debug("%s: (%s): %pFX nexthop via if %u vrf %s(%u)",
+ __func__, routedesc, p, nexthop->ifindex,
+ VRF_LOGNAME(vrf), nexthop->vrf_id);
}
if (nexthop->weight)
@@ -1457,37 +1457,6 @@ _netlink_mpls_build_multipath(const struct prefix *p, const char *routedesc,
rta, rtnh, rtmsg, src);
}
-
-/* Log debug information for netlink_route_multipath
- * if debug logging is enabled.
- *
- * @param cmd: Netlink command which is to be processed
- * @param p: Prefix for which the change is due
- * @param family: Address family which the change concerns
- * @param zvrf: The vrf we are in
- * @param tableid: The table we are working on
- */
-static void _netlink_route_debug(int cmd, const struct prefix *p,
- int family, vrf_id_t vrfid,
- uint32_t tableid)
-{
- if (IS_ZEBRA_DEBUG_KERNEL) {
- char buf[PREFIX_STRLEN];
- zlog_debug(
- "netlink_route_multipath(): %s %s vrf %s(%u) table_id: %u",
- nl_msg_type_to_str(cmd),
- prefix2str(p, buf, sizeof(buf)), vrf_id_to_name(vrfid),
- vrfid, tableid);
- }
-}
-
-static void _netlink_nexthop_debug(int cmd, uint32_t id)
-{
- if (IS_ZEBRA_DEBUG_KERNEL)
- zlog_debug("netlink_nexthop(): %s, id=%u",
- nl_msg_type_to_str(cmd), id);
-}
-
static void _netlink_mpls_debug(int cmd, uint32_t label, const char *routedesc)
{
if (IS_ZEBRA_DEBUG_KERNEL)
@@ -1552,15 +1521,32 @@ static bool nexthop_set_src(const struct nexthop *nexthop, int family,
return false;
}
+static void netlink_route_nexthop_encap(struct nlmsghdr *n, size_t nlen,
+ struct nexthop *nh)
+{
+ struct rtattr *nest;
+
+ switch (nh->nh_encap_type) {
+ case NET_VXLAN:
+ addattr_l(n, nlen, RTA_ENCAP_TYPE, &nh->nh_encap_type,
+ sizeof(uint16_t));
+
+ nest = addattr_nest(n, nlen, RTA_ENCAP);
+ addattr32(n, nlen, 0 /* VXLAN_VNI */, nh->nh_encap.vni);
+ addattr_nest_end(n, nest);
+ break;
+ }
+}
+
/*
* Routing table change via netlink interface, using a dataplane context object
*/
-static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx)
+ssize_t netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx,
+ uint8_t *data, size_t datalen, bool fpm)
{
int bytelen;
struct nexthop *nexthop = NULL;
unsigned int nexthop_num;
- int family;
const char *routedesc;
bool setsrc = false;
union g_addr src;
@@ -1570,38 +1556,36 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx)
struct {
struct nlmsghdr n;
struct rtmsg r;
- char buf[NL_PKT_BUF_SIZE];
- } req;
+ char buf[];
+ } *req = (void *)data;
p = dplane_ctx_get_dest(ctx);
src_p = dplane_ctx_get_src(ctx);
- family = PREFIX_FAMILY(p);
+ memset(req, 0, sizeof(*req));
- memset(&req, 0, sizeof(req) - NL_PKT_BUF_SIZE);
-
- bytelen = (family == AF_INET ? 4 : 16);
+ bytelen = (p->family == AF_INET ? 4 : 16);
- req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
- req.n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST;
+ req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+ req->n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST;
if ((cmd == RTM_NEWROUTE) &&
((p->family == AF_INET) || v6_rr_semantics))
- req.n.nlmsg_flags |= NLM_F_REPLACE;
+ req->n.nlmsg_flags |= NLM_F_REPLACE;
- req.n.nlmsg_type = cmd;
+ req->n.nlmsg_type = cmd;
- req.n.nlmsg_pid = dplane_ctx_get_ns(ctx)->nls.snl.nl_pid;
+ req->n.nlmsg_pid = dplane_ctx_get_ns(ctx)->nls.snl.nl_pid;
- req.r.rtm_family = family;
- req.r.rtm_dst_len = p->prefixlen;
- req.r.rtm_src_len = src_p ? src_p->prefixlen : 0;
- req.r.rtm_scope = RT_SCOPE_UNIVERSE;
+ req->r.rtm_family = p->family;
+ req->r.rtm_dst_len = p->prefixlen;
+ req->r.rtm_src_len = src_p ? src_p->prefixlen : 0;
+ req->r.rtm_scope = RT_SCOPE_UNIVERSE;
if (cmd == RTM_DELROUTE)
- req.r.rtm_protocol = zebra2proto(dplane_ctx_get_old_type(ctx));
+ req->r.rtm_protocol = zebra2proto(dplane_ctx_get_old_type(ctx));
else
- req.r.rtm_protocol = zebra2proto(dplane_ctx_get_type(ctx));
+ req->r.rtm_protocol = zebra2proto(dplane_ctx_get_type(ctx));
/*
* blackhole routes are not RTN_UNICAST, they are
@@ -1612,12 +1596,11 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx)
* the RTM_DELROUTE case
*/
if (cmd != RTM_DELROUTE)
- req.r.rtm_type = RTN_UNICAST;
+ req->r.rtm_type = RTN_UNICAST;
- addattr_l(&req.n, sizeof(req), RTA_DST, &p->u.prefix, bytelen);
+ addattr_l(&req->n, datalen, RTA_DST, &p->u.prefix, bytelen);
if (src_p)
- addattr_l(&req.n, sizeof(req), RTA_SRC, &src_p->u.prefix,
- bytelen);
+ addattr_l(&req->n, datalen, RTA_SRC, &src_p->u.prefix, bytelen);
/* Metric. */
/* Hardcode the metric for all routes coming from zebra. Metric isn't
@@ -1626,7 +1609,7 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx)
* path(s)
* by the routing protocol and for communicating with protocol peers.
*/
- addattr32(&req.n, sizeof(req), RTA_PRIORITY, NL_DEFAULT_ROUTE_METRIC);
+ addattr32(&req->n, datalen, RTA_PRIORITY, NL_DEFAULT_ROUTE_METRIC);
#if defined(SUPPORT_REALMS)
{
@@ -1638,19 +1621,23 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx)
tag = dplane_ctx_get_tag(ctx);
if (tag > 0 && tag <= 255)
- addattr32(&req.n, sizeof(req), RTA_FLOW, tag);
+ addattr32(&req->n, datalen, RTA_FLOW, tag);
}
#endif
/* Table corresponding to this route. */
table_id = dplane_ctx_get_table(ctx);
if (table_id < 256)
- req.r.rtm_table = table_id;
+ req->r.rtm_table = table_id;
else {
- req.r.rtm_table = RT_TABLE_UNSPEC;
- addattr32(&req.n, sizeof(req), RTA_TABLE, table_id);
+ req->r.rtm_table = RT_TABLE_UNSPEC;
+ addattr32(&req->n, datalen, RTA_TABLE, table_id);
}
- _netlink_route_debug(cmd, p, family, dplane_ctx_get_vrf(ctx), table_id);
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug(
+ "%s: %s %pFX vrf %u(%u)", __func__,
+ nl_msg_type_to_str(cmd), p, dplane_ctx_get_vrf(ctx),
+ table_id);
/*
* If we are not updating the route and we have received
@@ -1659,7 +1646,7 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx)
* it.
*/
if (cmd == RTM_DELROUTE)
- goto skip;
+ return req->n.nlmsg_len;
if (dplane_ctx_get_mtu(ctx) || dplane_ctx_get_nh_mtu(ctx)) {
char buf[NL_PKT_BUF_SIZE];
@@ -1673,7 +1660,7 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx)
rta->rta_len = RTA_LENGTH(0);
rta_addattr_l(rta, NL_PKT_BUF_SIZE,
RTAX_MTU, &mtu, sizeof(mtu));
- addattr_l(&req.n, NL_PKT_BUF_SIZE, RTA_METRICS, RTA_DATA(rta),
+ addattr_l(&req->n, datalen, RTA_METRICS, RTA_DATA(rta),
RTA_PAYLOAD(rta));
}
@@ -1683,7 +1670,8 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx)
zlog_debug(
"netlink_route_multipath(): %pFX nhg_id is %u",
p, dplane_ctx_get_nhe_id(ctx));
- addattr32(&req.n, sizeof(req), RTA_NH_ID,
+
+ addattr32(&req->n, datalen, RTA_NH_ID,
dplane_ctx_get_nhe_id(ctx));
/* Have to determine src still */
@@ -1691,18 +1679,19 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx)
if (setsrc)
break;
- setsrc = nexthop_set_src(nexthop, family, &src);
+ setsrc = nexthop_set_src(nexthop, p->family, &src);
}
if (setsrc) {
- if (family == AF_INET)
- addattr_l(&req.n, sizeof(req), RTA_PREFSRC,
+ if (p->family == AF_INET)
+ addattr_l(&req->n, datalen, RTA_PREFSRC,
&src.ipv4, bytelen);
- else if (family == AF_INET6)
- addattr_l(&req.n, sizeof(req), RTA_PREFSRC,
+ else if (p->family == AF_INET6)
+ addattr_l(&req->n, datalen, RTA_PREFSRC,
&src.ipv6, bytelen);
}
- goto skip;
+
+ return req->n.nlmsg_len;
}
/* Count overall nexthops so we can decide whether to use singlepath
@@ -1712,7 +1701,7 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx)
for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) {
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
continue;
- if (cmd == RTM_NEWROUTE && !NEXTHOP_IS_ACTIVE(nexthop->flags))
+ if (!NEXTHOP_IS_ACTIVE(nexthop->flags))
continue;
nexthop_num++;
@@ -1732,16 +1721,16 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx)
if (nexthop->type == NEXTHOP_TYPE_BLACKHOLE) {
switch (nexthop->bh_type) {
case BLACKHOLE_ADMINPROHIB:
- req.r.rtm_type = RTN_PROHIBIT;
+ req->r.rtm_type = RTN_PROHIBIT;
break;
case BLACKHOLE_REJECT:
- req.r.rtm_type = RTN_UNREACHABLE;
+ req->r.rtm_type = RTN_UNREACHABLE;
break;
default:
- req.r.rtm_type = RTN_BLACKHOLE;
+ req->r.rtm_type = RTN_BLACKHOLE;
break;
}
- goto skip;
+ return req->n.nlmsg_len;
}
if (CHECK_FLAG(nexthop->flags,
NEXTHOP_FLAG_RECURSIVE)) {
@@ -1749,30 +1738,38 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx)
if (setsrc)
continue;
- setsrc = nexthop_set_src(nexthop, family, &src);
-
+ setsrc = nexthop_set_src(nexthop, p->family,
+ &src);
continue;
}
- if ((cmd == RTM_NEWROUTE
- && NEXTHOP_IS_ACTIVE(nexthop->flags))) {
+ if (NEXTHOP_IS_ACTIVE(nexthop->flags)) {
routedesc = nexthop->rparent
? "recursive, single-path"
: "single-path";
_netlink_route_build_singlepath(
- p, routedesc, bytelen, nexthop, &req.n,
- &req.r, sizeof(req), cmd);
+ p, routedesc, bytelen, nexthop, &req->n,
+ &req->r, datalen, cmd);
nexthop_num++;
break;
}
+
+ /*
+ * Add encapsulation information when installing via
+ * FPM.
+ */
+ if (fpm)
+ netlink_route_nexthop_encap(&req->n, datalen,
+ nexthop);
}
+
if (setsrc) {
- if (family == AF_INET)
- addattr_l(&req.n, sizeof(req), RTA_PREFSRC,
+ if (p->family == AF_INET)
+ addattr_l(&req->n, datalen, RTA_PREFSRC,
&src.ipv4, bytelen);
- else if (family == AF_INET6)
- addattr_l(&req.n, sizeof(req), RTA_PREFSRC,
+ else if (p->family == AF_INET6)
+ addattr_l(&req->n, datalen, RTA_PREFSRC,
&src.ipv6, bytelen);
}
} else { /* Multipath case */
@@ -1793,13 +1790,12 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx)
if (setsrc)
continue;
- setsrc = nexthop_set_src(nexthop, family, &src);
-
+ setsrc = nexthop_set_src(nexthop, p->family,
+ &src);
continue;
}
- if ((cmd == RTM_NEWROUTE
- && NEXTHOP_IS_ACTIVE(nexthop->flags))) {
+ if (NEXTHOP_IS_ACTIVE(nexthop->flags)) {
routedesc = nexthop->rparent
? "recursive, multipath"
: "multipath";
@@ -1807,47 +1803,51 @@ static int netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx)
_netlink_route_build_multipath(
p, routedesc, bytelen, nexthop, rta,
- rtnh, &req.r, &src1);
+ rtnh, &req->r, &src1);
rtnh = RTNH_NEXT(rtnh);
if (!setsrc && src1) {
- if (family == AF_INET)
+ if (p->family == AF_INET)
src.ipv4 = src1->ipv4;
- else if (family == AF_INET6)
+ else if (p->family == AF_INET6)
src.ipv6 = src1->ipv6;
setsrc = 1;
}
}
+
+ /*
+ * Add encapsulation information when installing via
+ * FPM.
+ */
+ if (fpm)
+ netlink_route_nexthop_encap(&req->n, datalen,
+ nexthop);
}
+
if (setsrc) {
- if (family == AF_INET)
- addattr_l(&req.n, sizeof(req), RTA_PREFSRC,
+ if (p->family == AF_INET)
+ addattr_l(&req->n, datalen, RTA_PREFSRC,
&src.ipv4, bytelen);
- else if (family == AF_INET6)
- addattr_l(&req.n, sizeof(req), RTA_PREFSRC,
+ else if (p->family == AF_INET6)
+ addattr_l(&req->n, datalen, RTA_PREFSRC,
&src.ipv6, bytelen);
if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug("Setting source");
}
if (rta->rta_len > RTA_LENGTH(0))
- addattr_l(&req.n, NL_PKT_BUF_SIZE, RTA_MULTIPATH,
+ addattr_l(&req->n, datalen, RTA_MULTIPATH,
RTA_DATA(rta), RTA_PAYLOAD(rta));
}
/* If there is no useful nexthop then return. */
if (nexthop_num == 0) {
if (IS_ZEBRA_DEBUG_KERNEL)
- zlog_debug(
- "netlink_route_multipath(): No useful nexthop.");
- return 0;
+ zlog_debug("%s: No useful nexthop.", __func__);
}
-skip:
- /* Talk to netlink socket. */
- return netlink_talk_info(netlink_talk_filter, &req.n,
- dplane_ctx_get_ns(ctx), 0);
+ return req->n.nlmsg_len;
}
int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *in)
@@ -2106,7 +2106,9 @@ nexthop_done:
return -1;
}
- _netlink_nexthop_debug(cmd, id);
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: %s, id=%u", __func__, nl_msg_type_to_str(cmd),
+ id);
return netlink_talk_info(netlink_talk_filter, &req.n,
dplane_ctx_get_ns(ctx), 0);
@@ -2152,6 +2154,7 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx)
int cmd, ret;
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;
@@ -2172,9 +2175,14 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx)
* the kernel the old non-system route
*/
if (RSYSTEM_ROUTE(dplane_ctx_get_type(ctx)) &&
- !RSYSTEM_ROUTE(dplane_ctx_get_old_type(ctx)))
- (void)netlink_route_multipath(RTM_DELROUTE,
- ctx);
+ !RSYSTEM_ROUTE(dplane_ctx_get_old_type(ctx))) {
+ netlink_route_multipath(RTM_DELROUTE, ctx,
+ nl_pkt, sizeof(nl_pkt),
+ false);
+ netlink_talk_info(netlink_talk_filter,
+ (struct nlmsghdr *)nl_pkt,
+ dplane_ctx_get_ns(ctx), 0);
+ }
} else {
/*
* So v6 route replace semantics are not in
@@ -2188,9 +2196,14 @@ 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)))
- (void)netlink_route_multipath(RTM_DELROUTE,
- ctx);
+ if (!RSYSTEM_ROUTE(dplane_ctx_get_old_type(ctx))) {
+ netlink_route_multipath(RTM_DELROUTE, ctx,
+ nl_pkt, sizeof(nl_pkt),
+ false);
+ netlink_talk_info(netlink_talk_filter,
+ (struct nlmsghdr *)nl_pkt,
+ dplane_ctx_get_ns(ctx), 0);
+ }
cmd = RTM_NEWROUTE;
}
@@ -2198,9 +2211,13 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx)
return ZEBRA_DPLANE_REQUEST_FAILURE;
}
- if (!RSYSTEM_ROUTE(dplane_ctx_get_type(ctx)))
- ret = netlink_route_multipath(cmd, ctx);
- else
+ if (!RSYSTEM_ROUTE(dplane_ctx_get_type(ctx))) {
+ netlink_route_multipath(cmd, ctx, nl_pkt, sizeof(nl_pkt),
+ false);
+ ret = netlink_talk_info(netlink_talk_filter,
+ (struct nlmsghdr *)nl_pkt,
+ dplane_ctx_get_ns(ctx), 0);
+ } else
ret = 0;
if ((cmd == RTM_NEWROUTE) && (ret == 0)) {
/* Update installed nexthops to signal which have been
@@ -2535,62 +2552,63 @@ int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla,
* @type: RTN_* route type
* @flags: NTF_* flags
* @state: NUD_* states
+ * @data: data buffer pointer
+ * @datalen: total amount of data buffer space
*
* Return: Result status
*/
-static int netlink_update_neigh_ctx_internal(const struct zebra_dplane_ctx *ctx,
- int cmd, const struct ethaddr *mac,
- const struct ipaddr *ip,
- bool replace_obj, uint8_t family,
- uint8_t type, uint8_t flags,
- uint16_t state)
+static ssize_t
+netlink_update_neigh_ctx_internal(const struct zebra_dplane_ctx *ctx,
+ int cmd, const struct ethaddr *mac,
+ const struct ipaddr *ip, bool replace_obj,
+ uint8_t family, uint8_t type, uint8_t flags,
+ uint16_t state, void *data, size_t datalen)
{
uint8_t protocol = RTPROT_ZEBRA;
struct {
struct nlmsghdr n;
struct ndmsg ndm;
- char buf[256];
- } req;
+ char buf[];
+ } *req = data;
int ipa_len;
enum dplane_op_e op;
- memset(&req, 0, sizeof(req));
+ memset(req, 0, datalen);
op = dplane_ctx_get_op(ctx);
- req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
- req.n.nlmsg_flags = NLM_F_REQUEST;
+ req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
+ req->n.nlmsg_flags = NLM_F_REQUEST;
if (cmd == RTM_NEWNEIGH)
- req.n.nlmsg_flags |=
+ req->n.nlmsg_flags |=
NLM_F_CREATE
| (replace_obj ? NLM_F_REPLACE : NLM_F_APPEND);
- req.n.nlmsg_type = cmd;
- req.ndm.ndm_family = family;
- req.ndm.ndm_type = type;
- req.ndm.ndm_state = state;
- req.ndm.ndm_flags = flags;
- req.ndm.ndm_ifindex = dplane_ctx_get_ifindex(ctx);
-
- addattr_l(&req.n, sizeof(req),
+ req->n.nlmsg_type = cmd;
+ req->ndm.ndm_family = family;
+ req->ndm.ndm_type = type;
+ req->ndm.ndm_state = state;
+ req->ndm.ndm_flags = flags;
+ req->ndm.ndm_ifindex = dplane_ctx_get_ifindex(ctx);
+
+ addattr_l(&req->n, sizeof(req),
NDA_PROTOCOL, &protocol, sizeof(protocol));
if (mac)
- addattr_l(&req.n, sizeof(req), NDA_LLADDR, mac, 6);
+ addattr_l(&req->n, datalen, NDA_LLADDR, mac, 6);
ipa_len = IS_IPADDR_V4(ip) ? IPV4_MAX_BYTELEN : IPV6_MAX_BYTELEN;
- addattr_l(&req.n, sizeof(req), NDA_DST, &ip->ip.addr, ipa_len);
+ addattr_l(&req->n, datalen, NDA_DST, &ip->ip.addr, ipa_len);
if (op == DPLANE_OP_MAC_INSTALL || op == DPLANE_OP_MAC_DELETE) {
vlanid_t vid = dplane_ctx_mac_get_vlan(ctx);
if (vid > 0)
- addattr16(&req.n, sizeof(req), NDA_VLAN, vid);
+ addattr16(&req->n, datalen, NDA_VLAN, vid);
- addattr32(&req.n, sizeof(req), NDA_MASTER,
+ addattr32(&req->n, datalen, NDA_MASTER,
dplane_ctx_mac_get_br_ifindex(ctx));
}
- return netlink_talk_info(netlink_talk_filter, &req.n,
- dplane_ctx_get_ns(ctx), 0);
+ return NLMSG_ALIGN(req->n.nlmsg_len);
}
/*
@@ -2601,10 +2619,16 @@ static int netlink_vxlan_flood_update_ctx(const struct zebra_dplane_ctx *ctx,
int cmd)
{
struct ethaddr dst_mac = {.octet = {0}};
+ uint8_t nl_pkt[NL_PKT_BUF_SIZE];
- return netlink_update_neigh_ctx_internal(
+ netlink_update_neigh_ctx_internal(
ctx, cmd, &dst_mac, dplane_ctx_neigh_get_ipaddr(ctx), false,
- PF_BRIDGE, 0, NTF_SELF, (NUD_NOARP | NUD_PERMANENT));
+ PF_BRIDGE, 0, NTF_SELF, (NUD_NOARP | NUD_PERMANENT), nl_pkt,
+ sizeof(nl_pkt));
+
+ return netlink_talk_info(netlink_talk_filter,
+ (struct nlmsghdr *)nl_pkt,
+ dplane_ctx_get_ns(ctx), 0);
}
#ifndef NDA_RTA
@@ -2930,12 +2954,20 @@ int netlink_macfdb_read_specific_mac(struct zebra_ns *zns,
/*
* Netlink-specific handler for MAC updates using dataplane context object.
*/
-static int netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, int cmd)
+ssize_t
+netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, uint8_t *data,
+ size_t datalen)
{
struct ipaddr vtep_ip;
vlanid_t vid;
+ ssize_t total;
+ int cmd;
uint8_t flags;
uint16_t state;
+ uint8_t nl_pkt[NL_PKT_BUF_SIZE];
+
+ cmd = dplane_ctx_get_op(ctx) == DPLANE_OP_MAC_INSTALL
+ ? RTM_NEWNEIGH : RTM_DELNEIGH;
flags = (NTF_SELF | NTF_MASTER);
state = NUD_REACHABLE;
@@ -2970,10 +3002,12 @@ static int netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, int cmd)
ipaddr2str(&vtep_ip, ipbuf, sizeof(ipbuf)));
}
- return netlink_update_neigh_ctx_internal(
- ctx, cmd, dplane_ctx_mac_get_addr(ctx),
- dplane_ctx_neigh_get_ipaddr(ctx), true, AF_BRIDGE, 0, flags,
- state);
+ total = netlink_update_neigh_ctx_internal(
+ ctx, cmd, dplane_ctx_mac_get_addr(ctx),
+ dplane_ctx_neigh_get_ipaddr(ctx), true, AF_BRIDGE, 0,
+ flags, state, nl_pkt, sizeof(nl_pkt));
+
+ return total;
}
/*
@@ -3362,6 +3396,7 @@ static int netlink_neigh_update_ctx(const struct zebra_dplane_ctx *ctx,
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);
@@ -3386,8 +3421,12 @@ static int netlink_neigh_update_ctx(const struct zebra_dplane_ctx *ctx,
flags, state);
}
- return netlink_update_neigh_ctx_internal(
- ctx, cmd, mac, ip, true, family, RTN_UNICAST, flags, state);
+ netlink_update_neigh_ctx_internal(
+ ctx, cmd, mac, ip, true, family, RTN_UNICAST, flags,
+ state, nl_pkt, sizeof(nl_pkt));
+
+ return netlink_talk_info(netlink_talk_filter, (struct nlmsghdr *)nl_pkt,
+ dplane_ctx_get_ns(ctx), 0);
}
/*
@@ -3395,13 +3434,18 @@ static int netlink_neigh_update_ctx(const struct zebra_dplane_ctx *ctx,
*/
enum zebra_dplane_result kernel_mac_update_ctx(struct zebra_dplane_ctx *ctx)
{
- int cmd = dplane_ctx_get_op(ctx) == DPLANE_OP_MAC_INSTALL
- ? RTM_NEWNEIGH
- : RTM_DELNEIGH;
- int ret = netlink_macfdb_update_ctx(ctx, cmd);
+ uint8_t nl_pkt[NL_PKT_BUF_SIZE];
+ ssize_t rv;
- return (ret == 0 ? ZEBRA_DPLANE_REQUEST_SUCCESS
- : ZEBRA_DPLANE_REQUEST_FAILURE);
+ 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)
diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h
index 2b4b145149..d6a993e78a 100644
--- a/zebra/rt_netlink.h
+++ b/zebra/rt_netlink.h
@@ -66,6 +66,12 @@ void rt_netlink_init(void);
/* MPLS label forwarding table change, using dataplane context information. */
extern int netlink_mpls_multipath(int cmd, struct zebra_dplane_ctx *ctx);
+extern ssize_t netlink_route_multipath(int cmd, struct zebra_dplane_ctx *ctx,
+ uint8_t *data, size_t datalen,
+ bool fpm);
+extern ssize_t netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx,
+ uint8_t *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);
diff --git a/zebra/rule_netlink.c b/zebra/rule_netlink.c
index c9699c7d95..07e1902f65 100644
--- a/zebra/rule_netlink.c
+++ b/zebra/rule_netlink.c
@@ -174,11 +174,40 @@ enum zebra_dplane_result kernel_del_pbr_rule(struct zebra_pbr_rule *rule)
}
/*
+ * Update specified rule for a specific interface.
+ */
+enum zebra_dplane_result kernel_update_pbr_rule(struct zebra_pbr_rule *old_rule,
+ struct zebra_pbr_rule *new_rule)
+{
+ int ret = 0;
+
+ /* Add the new, updated one */
+ ret = netlink_rule_update(RTM_NEWRULE, new_rule);
+
+ /**
+ * Delete the old one.
+ *
+ * Don't care about this result right?
+ */
+ netlink_rule_update(RTM_DELRULE, old_rule);
+
+ kernel_pbr_rule_add_del_status(new_rule,
+ (!ret) ? ZEBRA_DPLANE_INSTALL_SUCCESS
+ : ZEBRA_DPLANE_INSTALL_FAILURE);
+
+ return ZEBRA_DPLANE_REQUEST_SUCCESS;
+}
+
+/*
* Handle netlink notification informing a rule add or delete.
* Handling of an ADD is TBD.
* DELs are notified up, if other attributes indicate it may be a
* notification of interest. The expectation is that if this corresponds
* to a PBR rule added by FRR, it will be readded.
+ *
+ * If startup and we see a rule we created, delete it as its leftover
+ * from a previous instance and should have been removed on shutdown.
+ *
*/
int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
{
@@ -190,15 +219,12 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
struct zebra_pbr_rule rule = {};
char buf1[PREFIX_STRLEN];
char buf2[PREFIX_STRLEN];
+ uint8_t proto = 0;
/* Basic validation followed by extracting attributes. */
if (h->nlmsg_type != RTM_NEWRULE && h->nlmsg_type != RTM_DELRULE)
return 0;
- /* TBD */
- if (h->nlmsg_type == RTM_NEWRULE)
- return 0;
-
len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct fib_rule_hdr));
if (len < 0) {
zlog_err(
@@ -222,19 +248,6 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
memset(tb, 0, sizeof(tb));
netlink_parse_rtattr(tb, FRA_MAX, RTM_RTA(frh), len);
- /* TBD: We don't care about rules not specifying an IIF. */
- if (tb[FRA_IFNAME] == NULL)
- return 0;
-
- ifname = (char *)RTA_DATA(tb[FRA_IFNAME]);
- zns = zebra_ns_lookup(ns_id);
-
- /* If we don't know the interface, we don't care. */
- if (!if_lookup_by_name_per_ns(zns, ifname))
- return 0;
-
- strlcpy(rule.ifname, ifname, sizeof(rule.ifname));
-
if (tb[FRA_PRIORITY])
rule.rule.priority = *(uint32_t *)RTA_DATA(tb[FRA_PRIORITY]);
@@ -246,6 +259,7 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
memcpy(&rule.rule.filter.src_ip.u.prefix6,
RTA_DATA(tb[FRA_SRC]), 16);
rule.rule.filter.src_ip.prefixlen = frh->src_len;
+ rule.rule.filter.src_ip.family = frh->family;
rule.rule.filter.filter_bm |= PBR_FILTER_SRC_IP;
}
@@ -257,6 +271,7 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
memcpy(&rule.rule.filter.dst_ip.u.prefix6,
RTA_DATA(tb[FRA_DST]), 16);
rule.rule.filter.dst_ip.prefixlen = frh->dst_len;
+ rule.rule.filter.dst_ip.family = frh->family;
rule.rule.filter.filter_bm |= PBR_FILTER_DST_IP;
}
@@ -265,6 +280,49 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
else
rule.rule.action.table = frh->table;
+ /* TBD: We don't care about rules not specifying an IIF. */
+ if (tb[FRA_IFNAME] == NULL)
+ return 0;
+
+ if (tb[FRA_PROTOCOL])
+ proto = *(uint8_t *)RTA_DATA(tb[FRA_PROTOCOL]);
+
+ ifname = (char *)RTA_DATA(tb[FRA_IFNAME]);
+ strlcpy(rule.ifname, ifname, sizeof(rule.ifname));
+
+ if (h->nlmsg_type == RTM_NEWRULE) {
+ /*
+ * If we see a rule at startup we created, delete it now.
+ * It should have been flushed on a previous shutdown.
+ */
+ if (startup && proto == RTPROT_ZEBRA) {
+ int ret;
+
+ ret = netlink_rule_update(RTM_DELRULE, &rule);
+
+ zlog_debug(
+ "%s: %s leftover rule: family %s IF %s(%u) Pref %u Src %s Dst %s Table %u",
+ __func__,
+ ((ret == 0) ? "Removed" : "Failed to remove"),
+ nl_family_to_str(frh->family), rule.ifname,
+ rule.rule.ifindex, rule.rule.priority,
+ prefix2str(&rule.rule.filter.src_ip, buf1,
+ sizeof(buf1)),
+ prefix2str(&rule.rule.filter.dst_ip, buf2,
+ sizeof(buf2)),
+ rule.rule.action.table);
+ }
+
+ /* TBD */
+ return 0;
+ }
+
+ zns = zebra_ns_lookup(ns_id);
+
+ /* If we don't know the interface, we don't care. */
+ if (!if_lookup_by_name_per_ns(zns, ifname))
+ return 0;
+
if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug(
"Rx %s family %s IF %s(%u) Pref %u Src %s Dst %s Table %u",
@@ -281,12 +339,51 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
}
/*
+ * Request rules from the kernel
+ */
+static int netlink_request_rules(struct zebra_ns *zns, int family, int type)
+{
+ struct {
+ struct nlmsghdr n;
+ struct fib_rule_hdr frh;
+ char buf[NL_PKT_BUF_SIZE];
+ } req;
+
+ memset(&req, 0, sizeof(req));
+ req.n.nlmsg_type = type;
+ req.n.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct fib_rule_hdr));
+ req.frh.family = family;
+
+ return netlink_request(&zns->netlink_cmd, &req.n);
+}
+
+/*
* Get to know existing PBR rules in the kernel - typically called at startup.
- * TBD.
*/
int netlink_rules_read(struct zebra_ns *zns)
{
- return 0;
+ int ret;
+ struct zebra_dplane_info dp_info;
+
+ zebra_dplane_info_from_zns(&dp_info, zns, true);
+
+ ret = netlink_request_rules(zns, AF_INET, RTM_GETRULE);
+ if (ret < 0)
+ return ret;
+
+ ret = netlink_parse_info(netlink_rule_change, &zns->netlink_cmd,
+ &dp_info, 0, 1);
+ if (ret < 0)
+ return ret;
+
+ ret = netlink_request_rules(zns, AF_INET6, RTM_GETRULE);
+ if (ret < 0)
+ return ret;
+
+ ret = netlink_parse_info(netlink_rule_change, &zns->netlink_cmd,
+ &dp_info, 0, 1);
+ return ret;
}
#endif /* HAVE_NETLINK */
diff --git a/zebra/rule_socket.c b/zebra/rule_socket.c
index e2c650b4ad..219fa7de6f 100644
--- a/zebra/rule_socket.c
+++ b/zebra/rule_socket.c
@@ -57,4 +57,12 @@ enum zebra_dplane_result kernel_del_pbr_rule(struct zebra_pbr_rule *rule)
return ZEBRA_DPLANE_REQUEST_FAILURE;
}
+enum zebra_dplane_result kernel_update_pbr_rule(struct zebra_pbr_rule *old_rule,
+ struct zebra_pbr_rule *new_rule)
+{
+ flog_err(EC_LIB_UNAVAILABLE, "%s not Implemented for this platform",
+ __PRETTY_FUNCTION__);
+ return ZEBRA_DPLANE_REQUEST_FAILURE;
+}
+
#endif
diff --git a/zebra/subdir.am b/zebra/subdir.am
index f281afce94..71094cb52c 100644
--- a/zebra/subdir.am
+++ b/zebra/subdir.am
@@ -199,3 +199,13 @@ nodist_zebra_zebra_SOURCES = \
zebra_zebra_cumulus_mlag_la_SOURCES = zebra/zebra_mlag_private.c
zebra_zebra_cumulus_mlag_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+
+if LINUX
+module_LTLIBRARIES += zebra/dplane_fpm_nl.la
+
+zebra_dplane_fpm_nl_la_SOURCES = zebra/dplane_fpm_nl.c
+zebra_dplane_fpm_nl_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+zebra_dplane_fpm_nl_la_LIBADD =
+
+vtysh_scan += $(top_srcdir)/zebra/dplane_fpm_nl.c
+endif
diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c
index a2365ee76b..76447c260b 100644
--- a/zebra/zebra_dplane.c
+++ b/zebra/zebra_dplane.c
@@ -32,6 +32,7 @@
#include "zebra/zebra_memory.h"
#include "zebra/zebra_router.h"
#include "zebra/zebra_dplane.h"
+#include "zebra/zebra_vxlan_private.h"
#include "zebra/rt.h"
#include "zebra/debug.h"
@@ -178,7 +179,6 @@ struct dplane_mac_info {
struct ethaddr mac;
struct in_addr vtep_ip;
bool is_sticky;
-
};
/*
@@ -401,7 +401,7 @@ static enum zebra_dplane_result pw_update_internal(struct zebra_pw *pw,
static enum zebra_dplane_result intf_addr_update_internal(
const struct interface *ifp, const struct connected *ifc,
enum dplane_op_e op);
-static enum zebra_dplane_result mac_update_internal(
+static enum zebra_dplane_result mac_update_common(
enum dplane_op_e op, const struct interface *ifp,
const struct interface *br_ifp,
vlanid_t vid, const struct ethaddr *mac,
@@ -445,23 +445,15 @@ void dplane_enable_sys_route_notifs(void)
}
/*
- * Free a dataplane results context.
+ * Clean up dependent/internal allocations inside a context object
*/
-static void dplane_ctx_free(struct zebra_dplane_ctx **pctx)
+static void dplane_ctx_free_internal(struct zebra_dplane_ctx *ctx)
{
- if (pctx == NULL)
- return;
-
- DPLANE_CTX_VALID(*pctx);
-
- /* TODO -- just freeing memory, but would like to maintain
- * a pool
- */
-
- /* Some internal allocations may need to be freed, depending on
+ /*
+ * Some internal allocations may need to be freed, depending on
* the type of info captured in the ctx.
*/
- switch ((*pctx)->zd_op) {
+ switch (ctx->zd_op) {
case DPLANE_OP_ROUTE_INSTALL:
case DPLANE_OP_ROUTE_UPDATE:
case DPLANE_OP_ROUTE_DELETE:
@@ -470,33 +462,33 @@ static void dplane_ctx_free(struct zebra_dplane_ctx **pctx)
case DPLANE_OP_ROUTE_NOTIFY:
/* Free allocated nexthops */
- if ((*pctx)->u.rinfo.zd_ng.nexthop) {
+ if (ctx->u.rinfo.zd_ng.nexthop) {
/* This deals with recursive nexthops too */
- nexthops_free((*pctx)->u.rinfo.zd_ng.nexthop);
+ nexthops_free(ctx->u.rinfo.zd_ng.nexthop);
- (*pctx)->u.rinfo.zd_ng.nexthop = NULL;
+ ctx->u.rinfo.zd_ng.nexthop = NULL;
}
/* Free backup info also (if present) */
- if ((*pctx)->u.rinfo.backup_ng.nexthop) {
+ if (ctx->u.rinfo.backup_ng.nexthop) {
/* This deals with recursive nexthops too */
- nexthops_free((*pctx)->u.rinfo.backup_ng.nexthop);
+ nexthops_free(ctx->u.rinfo.backup_ng.nexthop);
- (*pctx)->u.rinfo.backup_ng.nexthop = NULL;
+ ctx->u.rinfo.backup_ng.nexthop = NULL;
}
- if ((*pctx)->u.rinfo.zd_old_ng.nexthop) {
+ if (ctx->u.rinfo.zd_old_ng.nexthop) {
/* This deals with recursive nexthops too */
- nexthops_free((*pctx)->u.rinfo.zd_old_ng.nexthop);
+ nexthops_free(ctx->u.rinfo.zd_old_ng.nexthop);
- (*pctx)->u.rinfo.zd_old_ng.nexthop = NULL;
+ ctx->u.rinfo.zd_old_ng.nexthop = NULL;
}
- if ((*pctx)->u.rinfo.old_backup_ng.nexthop) {
+ if (ctx->u.rinfo.old_backup_ng.nexthop) {
/* This deals with recursive nexthops too */
- nexthops_free((*pctx)->u.rinfo.old_backup_ng.nexthop);
+ nexthops_free(ctx->u.rinfo.old_backup_ng.nexthop);
- (*pctx)->u.rinfo.old_backup_ng.nexthop = NULL;
+ ctx->u.rinfo.old_backup_ng.nexthop = NULL;
}
break;
@@ -504,11 +496,11 @@ static void dplane_ctx_free(struct zebra_dplane_ctx **pctx)
case DPLANE_OP_NH_INSTALL:
case DPLANE_OP_NH_UPDATE:
case DPLANE_OP_NH_DELETE: {
- if ((*pctx)->u.rinfo.nhe.ng.nexthop) {
+ if (ctx->u.rinfo.nhe.ng.nexthop) {
/* This deals with recursive nexthops too */
- nexthops_free((*pctx)->u.rinfo.nhe.ng.nexthop);
+ nexthops_free(ctx->u.rinfo.nhe.ng.nexthop);
- (*pctx)->u.rinfo.nhe.ng.nexthop = NULL;
+ ctx->u.rinfo.nhe.ng.nexthop = NULL;
}
break;
}
@@ -521,7 +513,7 @@ static void dplane_ctx_free(struct zebra_dplane_ctx **pctx)
zebra_nhlfe_t *nhlfe, *next;
/* Free allocated NHLFEs */
- for (nhlfe = (*pctx)->u.lsp.nhlfe_list; nhlfe; nhlfe = next) {
+ for (nhlfe = ctx->u.lsp.nhlfe_list; nhlfe; nhlfe = next) {
next = nhlfe->next;
zebra_mpls_nhlfe_del(nhlfe);
@@ -530,8 +522,8 @@ static void dplane_ctx_free(struct zebra_dplane_ctx **pctx)
/* Clear pointers in lsp struct, in case we're cacheing
* free context structs.
*/
- (*pctx)->u.lsp.nhlfe_list = NULL;
- (*pctx)->u.lsp.best_nhlfe = NULL;
+ ctx->u.lsp.nhlfe_list = NULL;
+ ctx->u.lsp.best_nhlfe = NULL;
break;
}
@@ -539,21 +531,21 @@ static void dplane_ctx_free(struct zebra_dplane_ctx **pctx)
case DPLANE_OP_PW_INSTALL:
case DPLANE_OP_PW_UNINSTALL:
/* Free allocated nexthops */
- if ((*pctx)->u.pw.nhg.nexthop) {
+ if (ctx->u.pw.nhg.nexthop) {
/* This deals with recursive nexthops too */
- nexthops_free((*pctx)->u.pw.nhg.nexthop);
+ nexthops_free(ctx->u.pw.nhg.nexthop);
- (*pctx)->u.pw.nhg.nexthop = NULL;
+ ctx->u.pw.nhg.nexthop = NULL;
}
break;
case DPLANE_OP_ADDR_INSTALL:
case DPLANE_OP_ADDR_UNINSTALL:
/* Maybe free label string, if allocated */
- if ((*pctx)->u.intf.label != NULL &&
- (*pctx)->u.intf.label != (*pctx)->u.intf.label_buf) {
- free((*pctx)->u.intf.label);
- (*pctx)->u.intf.label = NULL;
+ if (ctx->u.intf.label != NULL &&
+ ctx->u.intf.label != ctx->u.intf.label_buf) {
+ free(ctx->u.intf.label);
+ ctx->u.intf.label = NULL;
}
break;
@@ -567,11 +559,41 @@ static void dplane_ctx_free(struct zebra_dplane_ctx **pctx)
case DPLANE_OP_NONE:
break;
}
+}
+
+/*
+ * Free a dataplane results context.
+ */
+static void dplane_ctx_free(struct zebra_dplane_ctx **pctx)
+{
+ if (pctx == NULL)
+ return;
+
+ DPLANE_CTX_VALID(*pctx);
+
+ /* TODO -- just freeing memory, but would like to maintain
+ * a pool
+ */
+
+ /* Some internal allocations may need to be freed, depending on
+ * the type of info captured in the ctx.
+ */
+ dplane_ctx_free_internal(*pctx);
XFREE(MTYPE_DP_CTX, *pctx);
}
/*
+ * Reset an allocated context object for re-use. All internal allocations are
+ * freed and the context is memset.
+ */
+void dplane_ctx_reset(struct zebra_dplane_ctx *ctx)
+{
+ dplane_ctx_free_internal(ctx);
+ memset(ctx, 0, sizeof(*ctx));
+}
+
+/*
* Return a context block to the dplane module after processing
*/
void dplane_ctx_fini(struct zebra_dplane_ctx **pctx)
@@ -1503,10 +1525,8 @@ static int dplane_ctx_ns_init(struct zebra_dplane_ctx *ctx,
/*
* Initialize a context block for a route update from zebra data structs.
*/
-static int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx,
- enum dplane_op_e op,
- struct route_node *rn,
- struct route_entry *re)
+int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
+ struct route_node *rn, struct route_entry *re)
{
int ret = EINVAL;
const struct route_table *table = NULL;
@@ -1515,6 +1535,7 @@ static int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx,
struct zebra_ns *zns;
struct zebra_vrf *zvrf;
struct nexthop *nexthop;
+ zebra_l3vni_t *zl3vni;
if (!ctx || !rn || !re)
goto done;
@@ -1564,10 +1585,24 @@ static int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx,
re->nhe->backup_info->nhe->nhg.nexthop, NULL);
}
- /* Ensure that the dplane nexthops' flags are clear. */
- for (ALL_NEXTHOPS(ctx->u.rinfo.zd_ng, nexthop))
+ /*
+ * Ensure that the dplane nexthops' flags are clear and copy
+ * encapsulation information.
+ */
+ for (ALL_NEXTHOPS(ctx->u.rinfo.zd_ng, nexthop)) {
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
+ /* Check for available encapsulations. */
+ if (!CHECK_FLAG(re->flags, ZEBRA_FLAG_EVPN_ROUTE))
+ continue;
+
+ zl3vni = zl3vni_from_vrf(nexthop->vrf_id);
+ if (zl3vni && is_l3vni_oper_up(zl3vni)) {
+ nexthop->nh_encap_type = NET_VXLAN;
+ nexthop->nh_encap.vni = zl3vni->vni;
+ }
+ }
+
/* Don't need some info when capturing a system notification */
if (op == DPLANE_OP_SYS_ROUTE_ADD ||
op == DPLANE_OP_SYS_ROUTE_DELETE) {
@@ -1581,7 +1616,7 @@ static int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx,
dplane_ctx_ns_init(ctx, zns, (op == DPLANE_OP_ROUTE_UPDATE));
#ifdef HAVE_NETLINK
- if (re->nhe) {
+ {
struct nhg_hash_entry *nhe = zebra_nhg_resolve(re->nhe);
ctx->u.rinfo.nhe.id = nhe->id;
@@ -2470,8 +2505,8 @@ enum zebra_dplane_result dplane_mac_add(const struct interface *ifp,
enum zebra_dplane_result result;
/* Use common helper api */
- result = mac_update_internal(DPLANE_OP_MAC_INSTALL, ifp, bridge_ifp,
- vid, mac, vtep_ip, sticky);
+ result = mac_update_common(DPLANE_OP_MAC_INSTALL, ifp, bridge_ifp,
+ vid, mac, vtep_ip, sticky);
return result;
}
@@ -2487,41 +2522,25 @@ enum zebra_dplane_result dplane_mac_del(const struct interface *ifp,
enum zebra_dplane_result result;
/* Use common helper api */
- result = mac_update_internal(DPLANE_OP_MAC_DELETE, ifp, bridge_ifp,
- vid, mac, vtep_ip, false);
+ result = mac_update_common(DPLANE_OP_MAC_DELETE, ifp, bridge_ifp,
+ vid, mac, vtep_ip, false);
return result;
}
/*
- * Common helper api for MAC address/vxlan updates
+ * Public api to init an empty context - either newly-allocated or
+ * reset/cleared - for a MAC update.
*/
-static enum zebra_dplane_result
-mac_update_internal(enum dplane_op_e op,
- const struct interface *ifp,
- const struct interface *br_ifp,
- vlanid_t vid,
- const struct ethaddr *mac,
- struct in_addr vtep_ip,
- bool sticky)
+void dplane_mac_init(struct zebra_dplane_ctx *ctx,
+ const struct interface *ifp,
+ const struct interface *br_ifp,
+ vlanid_t vid,
+ const struct ethaddr *mac,
+ struct in_addr vtep_ip,
+ bool sticky)
{
- enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
- int ret;
- struct zebra_dplane_ctx *ctx = NULL;
struct zebra_ns *zns;
- if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
- char buf1[ETHER_ADDR_STRLEN], buf2[PREFIX_STRLEN];
-
- zlog_debug("init mac ctx %s: mac %s, ifp %s, vtep %s",
- dplane_op2str(op),
- prefix_mac2str(mac, buf1, sizeof(buf1)),
- ifp->name,
- inet_ntop(AF_INET, &vtep_ip, buf2, sizeof(buf2)));
- }
-
- ctx = dplane_ctx_alloc();
-
- ctx->zd_op = op;
ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS;
ctx->zd_vrf_id = ifp->vrf_id;
@@ -2539,6 +2558,39 @@ mac_update_internal(enum dplane_op_e op,
ctx->u.macinfo.mac = *mac;
ctx->u.macinfo.vid = vid;
ctx->u.macinfo.is_sticky = sticky;
+}
+
+/*
+ * Common helper api for MAC address/vxlan updates
+ */
+static enum zebra_dplane_result
+mac_update_common(enum dplane_op_e op,
+ const struct interface *ifp,
+ const struct interface *br_ifp,
+ vlanid_t vid,
+ const struct ethaddr *mac,
+ struct in_addr vtep_ip,
+ bool sticky)
+{
+ enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
+ int ret;
+ struct zebra_dplane_ctx *ctx = NULL;
+
+ if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
+ char buf1[ETHER_ADDR_STRLEN], buf2[PREFIX_STRLEN];
+
+ zlog_debug("init mac ctx %s: mac %s, ifp %s, vtep %s",
+ dplane_op2str(op),
+ prefix_mac2str(mac, buf1, sizeof(buf1)),
+ ifp->name,
+ inet_ntop(AF_INET, &vtep_ip, buf2, sizeof(buf2)));
+ }
+
+ ctx = dplane_ctx_alloc();
+ ctx->zd_op = op;
+
+ /* Common init for the ctx */
+ dplane_mac_init(ctx, ifp, br_ifp, vid, mac, vtep_ip, sticky);
/* Enqueue for processing on the dplane pthread */
ret = dplane_update_enqueue(ctx);
diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h
index 9ce4df197c..f01ca2e84c 100644
--- a/zebra/zebra_dplane.h
+++ b/zebra/zebra_dplane.h
@@ -180,6 +180,12 @@ TAILQ_HEAD(dplane_ctx_q, zebra_dplane_ctx);
/* Allocate a context object */
struct zebra_dplane_ctx *dplane_ctx_alloc(void);
+/*
+ * Reset an allocated context object for re-use. All internal allocations are
+ * freed.
+ */
+void dplane_ctx_reset(struct zebra_dplane_ctx *ctx);
+
/* Return a dataplane results context block after use; the caller's pointer will
* be cleared.
*/
@@ -438,6 +444,12 @@ enum zebra_dplane_result dplane_intf_addr_unset(const struct interface *ifp,
/*
* Enqueue evpn mac operations for the dataplane.
*/
+extern struct zebra_dplane_ctx *mac_update_internal(
+ enum dplane_op_e op, const struct interface *ifp,
+ const struct interface *br_ifp,
+ vlanid_t vid, const struct ethaddr *mac,
+ struct in_addr vtep_ip, bool sticky);
+
enum zebra_dplane_result dplane_mac_add(const struct interface *ifp,
const struct interface *bridge_ifp,
vlanid_t vid,
@@ -451,6 +463,15 @@ enum zebra_dplane_result dplane_mac_del(const struct interface *ifp,
const struct ethaddr *mac,
struct in_addr vtep_ip);
+/* Helper api to init an empty or new context for a MAC update */
+void dplane_mac_init(struct zebra_dplane_ctx *ctx,
+ const struct interface *ifp,
+ const struct interface *br_ifp,
+ vlanid_t vid,
+ const struct ethaddr *mac,
+ struct in_addr vtep_ip,
+ bool sticky);
+
/*
* Enqueue evpn neighbor updates for the dataplane.
*/
@@ -474,6 +495,9 @@ enum zebra_dplane_result dplane_vtep_delete(const struct interface *ifp,
const struct in_addr *ip,
vni_t vni);
+/* Encode route information into data plane context. */
+int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
+ struct route_node *rn, struct route_entry *re);
/* Retrieve the limit on the number of pending, unprocessed updates. */
uint32_t dplane_get_in_queue_limit(void);
diff --git a/zebra/zebra_fpm.c b/zebra/zebra_fpm.c
index 0190ee2b8d..10cdf49d12 100644
--- a/zebra/zebra_fpm.c
+++ b/zebra/zebra_fpm.c
@@ -1470,8 +1470,6 @@ static int zfpm_trigger_update(struct route_node *rn, const char *reason)
/*
* Generate Key for FPM MAC info hash entry
- * Key is generated using MAC address and VNI id which should be sufficient
- * to provide uniqueness
*/
static unsigned int zfpm_mac_info_hash_keymake(const void *p)
{
@@ -1494,8 +1492,6 @@ static bool zfpm_mac_info_cmp(const void *p1, const void *p2)
if (memcmp(fpm_mac1->macaddr.octet, fpm_mac2->macaddr.octet, ETH_ALEN)
!= 0)
return false;
- if (fpm_mac1->r_vtep_ip.s_addr != fpm_mac2->r_vtep_ip.s_addr)
- return false;
if (fpm_mac1->vni != fpm_mac2->vni)
return false;
@@ -1521,7 +1517,6 @@ static void *zfpm_mac_info_alloc(void *p)
fpm_mac = XCALLOC(MTYPE_FPM_MAC_INFO, sizeof(struct fpm_mac_info_t));
memcpy(&fpm_mac->macaddr, &key->macaddr, ETH_ALEN);
- memcpy(&fpm_mac->r_vtep_ip, &key->r_vtep_ip, sizeof(struct in_addr));
fpm_mac->vni = key->vni;
return (void *)fpm_mac;
@@ -1552,6 +1547,7 @@ static int zfpm_trigger_rmac_update(zebra_mac_t *rmac, zebra_l3vni_t *zl3vni,
char buf[ETHER_ADDR_STRLEN];
struct fpm_mac_info_t *fpm_mac, key;
struct interface *vxlan_if, *svi_if;
+ bool mac_found = false;
/*
* Ignore if the connection is down. We will update the FPM about
@@ -1572,56 +1568,34 @@ static int zfpm_trigger_rmac_update(zebra_mac_t *rmac, zebra_l3vni_t *zl3vni,
memset(&key, 0, sizeof(struct fpm_mac_info_t));
memcpy(&key.macaddr, &rmac->macaddr, ETH_ALEN);
- key.r_vtep_ip.s_addr = rmac->fwd_info.r_vtep_ip.s_addr;
key.vni = zl3vni->vni;
/* Check if this MAC is already present in the queue. */
fpm_mac = zfpm_mac_info_lookup(&key);
if (fpm_mac) {
- if (!!CHECK_FLAG(fpm_mac->fpm_flags, ZEBRA_MAC_DELETE_FPM)
- == delete) {
- /*
- * MAC is already present in the queue
- * with the same op as this one. Do nothing
- */
- zfpm_g->stats.redundant_triggers++;
- return 0;
- }
+ mac_found = true;
/*
- * A new op for an already existing fpm_mac_info_t node.
- * Update the existing node for the new op.
+ * If the enqueued op is "add" and current op is "delete",
+ * this is a noop. So, Unset ZEBRA_MAC_UPDATE_FPM flag.
+ * While processing FPM queue, we will silently delete this
+ * MAC entry without sending any update for this MAC.
*/
- if (!delete) {
- /*
- * New op is "add". Previous op is "delete".
- * Update the fpm_mac_info_t for the new add.
- */
- fpm_mac->zebra_flags = rmac->flags;
-
- fpm_mac->vxlan_if = vxlan_if ? vxlan_if->ifindex : 0;
- fpm_mac->svi_if = svi_if ? svi_if->ifindex : 0;
-
- UNSET_FLAG(fpm_mac->fpm_flags, ZEBRA_MAC_DELETE_FPM);
- SET_FLAG(fpm_mac->fpm_flags, ZEBRA_MAC_UPDATE_FPM);
- } else {
- /*
- * New op is "delete". Previous op is "add".
- * Thus, no-op. Unset ZEBRA_MAC_UPDATE_FPM flag.
- */
+ if (!CHECK_FLAG(fpm_mac->fpm_flags, ZEBRA_MAC_DELETE_FPM) &&
+ delete == 1) {
SET_FLAG(fpm_mac->fpm_flags, ZEBRA_MAC_DELETE_FPM);
UNSET_FLAG(fpm_mac->fpm_flags, ZEBRA_MAC_UPDATE_FPM);
+ return 0;
}
-
- return 0;
+ } else {
+ fpm_mac = hash_get(zfpm_g->fpm_mac_info_table, &key,
+ zfpm_mac_info_alloc);
+ if (!fpm_mac)
+ return 0;
}
- fpm_mac = hash_get(zfpm_g->fpm_mac_info_table, &key,
- zfpm_mac_info_alloc);
- if (!fpm_mac)
- return 0;
-
+ fpm_mac->r_vtep_ip.s_addr = rmac->fwd_info.r_vtep_ip.s_addr;
fpm_mac->zebra_flags = rmac->flags;
fpm_mac->vxlan_if = vxlan_if ? vxlan_if->ifindex : 0;
fpm_mac->svi_if = svi_if ? svi_if->ifindex : 0;
@@ -1629,8 +1603,11 @@ static int zfpm_trigger_rmac_update(zebra_mac_t *rmac, zebra_l3vni_t *zl3vni,
SET_FLAG(fpm_mac->fpm_flags, ZEBRA_MAC_UPDATE_FPM);
if (delete)
SET_FLAG(fpm_mac->fpm_flags, ZEBRA_MAC_DELETE_FPM);
+ else
+ UNSET_FLAG(fpm_mac->fpm_flags, ZEBRA_MAC_DELETE_FPM);
- TAILQ_INSERT_TAIL(&zfpm_g->mac_q, fpm_mac, fpm_mac_q_entries);
+ if (!mac_found)
+ TAILQ_INSERT_TAIL(&zfpm_g->mac_q, fpm_mac, fpm_mac_q_entries);
zfpm_g->stats.updates_triggered++;
diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c
index fceddcb745..de044c0ea0 100644
--- a/zebra/zebra_nhg.c
+++ b/zebra/zebra_nhg.c
@@ -459,11 +459,7 @@ uint32_t zebra_nhg_id_key(const void *arg)
static bool nhg_compare_nexthops(const struct nexthop *nh1,
const struct nexthop *nh2)
{
- if (nh1 && !nh2)
- return false;
-
- if (!nh1 && nh2)
- return false;
+ assert(nh1 != NULL && nh2 != NULL);
/*
* We have to check the active flag of each individual one,
@@ -518,13 +514,17 @@ bool zebra_nhg_hash_equal(const void *arg1, const void *arg2)
/* Nexthops should be in-order, so we simply compare them in-place */
for (nexthop1 = nhe1->nhg.nexthop, nexthop2 = nhe2->nhg.nexthop;
- nexthop1 || nexthop2;
+ nexthop1 && nexthop2;
nexthop1 = nexthop1->next, nexthop2 = nexthop2->next) {
if (!nhg_compare_nexthops(nexthop1, nexthop2))
return false;
}
+ /* Check for unequal list lengths */
+ if (nexthop1 || nexthop2)
+ return false;
+
/* If there's no backup info, comparison is done. */
if ((nhe1->backup_info == NULL) && (nhe2->backup_info == NULL))
return true;
@@ -550,7 +550,7 @@ bool zebra_nhg_hash_equal(const void *arg1, const void *arg2)
/* Have to compare the backup nexthops */
for (nexthop1 = nhe1->backup_info->nhe->nhg.nexthop,
nexthop2 = nhe2->backup_info->nhe->nhg.nexthop;
- nexthop1 || nexthop2;
+ nexthop1 && nexthop2;
nexthop1 = nexthop1->next, nexthop2 = nexthop2->next) {
if (!nhg_compare_nexthops(nexthop1, nexthop2))
@@ -1475,9 +1475,7 @@ zebra_nhg_rib_find_nhe(struct nhg_hash_entry *rt_nhe, afi_t rt_afi)
}
if (IS_ZEBRA_DEBUG_NHG_DETAIL)
- zlog_debug("%s: rt_nhe %p (%u)",
- __func__, rt_nhe,
- rt_nhe ? rt_nhe->id : 0);
+ zlog_debug("%s: rt_nhe %p (%u)", __func__, rt_nhe, rt_nhe->id);
zebra_nhe_find(&nhe, rt_nhe, NULL, rt_afi);
@@ -1568,14 +1566,10 @@ void zebra_nhg_free(struct nhg_hash_entry *nhe)
/* Group or singleton? */
if (nhe->nhg.nexthop && nhe->nhg.nexthop->next)
zlog_debug("%s: nhe %p (%u), refcnt %d",
- __func__, nhe,
- (nhe ? nhe->id : 0),
- (nhe ? nhe->refcnt : 0));
+ __func__, nhe, nhe->id, nhe->refcnt);
else
zlog_debug("%s: nhe %p (%u), refcnt %d, NH %pNHv",
- __func__, nhe,
- (nhe ? nhe->id : 0),
- (nhe ? nhe->refcnt : 0),
+ __func__, nhe, nhe->id, nhe->refcnt,
nhe->nhg.nexthop);
}
@@ -1942,13 +1936,14 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
nexthop_set_resolved(afi, newhop, nexthop);
resolved = 1;
}
+
if (resolved)
re->nexthop_mtu = match->mtu;
-
- if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED)
+ else if (IS_ZEBRA_DEBUG_RIB_DETAILED)
zlog_debug(
" %s: Recursion failed to find",
__func__);
+
return resolved;
} else if (re->type == ZEBRA_ROUTE_STATIC) {
resolved = 0;
diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c
index 3287176ef5..4e51437337 100644
--- a/zebra/zebra_ns.c
+++ b/zebra/zebra_ns.c
@@ -126,6 +126,7 @@ int zebra_ns_enable(ns_id_t ns_id, void **info)
kernel_init(zns);
interface_list(zns);
route_read(zns);
+ kernel_read_pbr_rules(zns);
/* Initiate Table Manager per ZNS */
table_manager_enable(ns_id);
diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c
index 6728567e6e..c049aa14f6 100644
--- a/zebra/zebra_pbr.c
+++ b/zebra/zebra_pbr.c
@@ -431,32 +431,51 @@ static void *pbr_rule_alloc_intern(void *arg)
return new;
}
+static int pbr_rule_release(struct zebra_pbr_rule *rule)
+{
+ struct zebra_pbr_rule *lookup;
+
+ lookup = hash_lookup(zrouter.rules_hash, rule);
+
+ if (!lookup)
+ return -ENOENT;
+
+ hash_release(zrouter.rules_hash, lookup);
+ XFREE(MTYPE_TMP, lookup);
+
+ return 0;
+}
+
void zebra_pbr_add_rule(struct zebra_pbr_rule *rule)
{
- struct zebra_pbr_rule *unique =
- pbr_rule_lookup_unique(rule);
+ struct zebra_pbr_rule *found;
- (void)hash_get(zrouter.rules_hash, rule, pbr_rule_alloc_intern);
- (void)kernel_add_pbr_rule(rule);
- /*
- * Rule Replace semantics, if we have an old, install the
- * new rule, look above, and then delete the old
+ /**
+ * Check if we already have it (this checks via a unique ID, walking
+ * over the hash table, not via a hash operation).
*/
- if (unique)
- zebra_pbr_del_rule(unique);
+ found = pbr_rule_lookup_unique(rule);
+
+ (void)hash_get(zrouter.rules_hash, rule, pbr_rule_alloc_intern);
+
+ /* If found, this is an update */
+ if (found) {
+ (void)kernel_update_pbr_rule(found, rule);
+
+ if (pbr_rule_release(found))
+ zlog_debug(
+ "%s: Rule being updated we know nothing about",
+ __PRETTY_FUNCTION__);
+
+ } else
+ (void)kernel_add_pbr_rule(rule);
}
void zebra_pbr_del_rule(struct zebra_pbr_rule *rule)
{
- struct zebra_pbr_rule *lookup;
-
- lookup = hash_lookup(zrouter.rules_hash, rule);
(void)kernel_del_pbr_rule(rule);
- if (lookup) {
- hash_release(zrouter.rules_hash, lookup);
- XFREE(MTYPE_TMP, lookup);
- } else
+ if (pbr_rule_release(rule))
zlog_debug("%s: Rule being deleted we know nothing about",
__func__);
}
diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h
index b7fbc9b7d5..83797b9521 100644
--- a/zebra/zebra_pbr.h
+++ b/zebra/zebra_pbr.h
@@ -183,6 +183,13 @@ extern enum zebra_dplane_result kernel_add_pbr_rule(struct zebra_pbr_rule *rule)
extern enum zebra_dplane_result kernel_del_pbr_rule(struct zebra_pbr_rule *rule);
/*
+ * Update specified rule for a specific interface.
+ */
+extern enum zebra_dplane_result
+kernel_update_pbr_rule(struct zebra_pbr_rule *old_rule,
+ struct zebra_pbr_rule *new_rule);
+
+/*
* 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_vxlan_private.h b/zebra/zebra_vxlan_private.h
index 100bb0e093..0a46fb2075 100644
--- a/zebra/zebra_vxlan_private.h
+++ b/zebra/zebra_vxlan_private.h
@@ -301,6 +301,7 @@ struct zebra_mac_t_ {
/* remote VTEP advertised MAC as default GW */
#define ZEBRA_MAC_REMOTE_DEF_GW 0x40
#define ZEBRA_MAC_DUPLICATE 0x80
+#define ZEBRA_MAC_FPM_SENT 0x100 /* whether or not this entry was sent. */
/* back pointer to zvni */
zebra_vni_t *zvni;