diff options
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; |
