diff options
| -rw-r--r-- | .clang-format | 1 | ||||
| -rw-r--r-- | bgpd/bgp_nht.c | 17 | ||||
| -rw-r--r-- | bgpd/bgp_route.c | 241 | ||||
| -rw-r--r-- | bgpd/bgp_route.h | 1 | ||||
| -rw-r--r-- | bgpd/bgp_updgrp_packet.c | 12 | ||||
| -rw-r--r-- | doc/user/bgp.rst | 31 | ||||
| -rw-r--r-- | lib/ipaddr.h | 7 | ||||
| -rw-r--r-- | nhrpd/nhrp_route.c | 4 | ||||
| -rw-r--r-- | ospfd/ospf_zebra.c | 2 | ||||
| -rwxr-xr-x | tests/topotests/route-scale/test_route_scale.py | 156 | ||||
| -rw-r--r-- | yang/frr-route-map.yang | 144 | ||||
| -rw-r--r-- | zebra/dplane_fpm_nl.c | 166 | ||||
| -rw-r--r-- | zebra/rt_netlink.c | 19 | ||||
| -rw-r--r-- | zebra/zebra_fpm_netlink.c | 12 | ||||
| -rw-r--r-- | zebra/zebra_nhg.c | 13 |
15 files changed, 498 insertions, 328 deletions
diff --git a/.clang-format b/.clang-format index 654577d936..47d681e0e2 100644 --- a/.clang-format +++ b/.clang-format @@ -66,5 +66,6 @@ ForEachMacros: - SUBGRP_FOREACH_ADJ_SAFE - AF_FOREACH - FOREACH_AFI_SAFI + - FOREACH_SAFI # ospfd - LSDB_LOOP diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index aefcaeff3a..a74b5f91ac 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -146,6 +146,11 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, afi = BGP_ATTR_NEXTHOP_AFI_IP6(pi->attr) ? AFI_IP6 : AFI_IP; + /* Validation for the ipv4 mapped ipv6 nexthop. */ + if (IS_MAPPED_IPV6(&pi->attr->mp_nexthop_global)) { + afi = AFI_IP; + } + /* This will return true if the global IPv6 NH is a link local * addr */ if (make_prefix(afi, pi, &p) < 0) @@ -533,6 +538,7 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p) : 0; struct bgp_dest *net = pi->net; const struct prefix *p_orig = bgp_dest_get_prefix(net); + struct in_addr ipv4; if (p_orig->family == AF_FLOWSPEC) { if (!pi->peer) @@ -548,8 +554,15 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p) p->u.prefix4 = p_orig->u.prefix4; p->prefixlen = p_orig->prefixlen; } else { - p->u.prefix4 = pi->attr->nexthop; - p->prefixlen = IPV4_MAX_BITLEN; + if (IS_MAPPED_IPV6(&pi->attr->mp_nexthop_global)) { + ipv4_mapped_ipv6_to_ipv4( + &pi->attr->mp_nexthop_global, &ipv4); + p->u.prefix4 = ipv4; + p->prefixlen = IPV4_MAX_BITLEN; + } else { + p->u.prefix4 = pi->attr->nexthop; + p->prefixlen = IPV4_MAX_BITLEN; + } } break; case AFI_IP6: diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index e8d5b431d6..6b2a5f55b7 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -11651,6 +11651,7 @@ enum bgp_pcounts { PCOUNT_VALID, PCOUNT_ALL, PCOUNT_COUNTED, + PCOUNT_BPATH_SELECTED, PCOUNT_PFCNT, /* the figure we display to users */ PCOUNT_MAX, }; @@ -11664,6 +11665,7 @@ static const char *const pcount_strs[] = { [PCOUNT_VALID] = "Valid", [PCOUNT_ALL] = "All RIB", [PCOUNT_COUNTED] = "PfxCt counted", + [PCOUNT_BPATH_SELECTED] = "PfxCt Best Selected", [PCOUNT_PFCNT] = "Useable", [PCOUNT_MAX] = NULL, }; @@ -11704,6 +11706,8 @@ static void bgp_peer_count_proc(struct bgp_dest *rn, struct peer_pcounts *pc) pc->count[PCOUNT_VALID]++; if (!CHECK_FLAG(pi->flags, BGP_PATH_UNUSEABLE)) pc->count[PCOUNT_PFCNT]++; + if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) + pc->count[PCOUNT_BPATH_SELECTED]++; if (CHECK_FLAG(pi->flags, BGP_PATH_COUNTED)) { pc->count[PCOUNT_COUNTED]++; @@ -11967,6 +11971,52 @@ DEFUN (show_bgp_l2vpn_evpn_route_prefix, use_json(argc, argv)); } +static void show_adj_route_header(struct vty *vty, struct bgp *bgp, + struct bgp_table *table, int *header1, + int *header2, json_object *json, + json_object *json_scode, + json_object *json_ocode, bool wide) +{ + uint64_t version = table ? table->version : 0; + + if (*header1) { + if (json) { + json_object_int_add(json, "bgpTableVersion", version); + json_object_string_add(json, "bgpLocalRouterId", + inet_ntoa(bgp->router_id)); + json_object_int_add(json, "defaultLocPrf", + bgp->default_local_pref); + json_object_int_add(json, "localAS", bgp->as); + json_object_object_add(json, "bgpStatusCodes", + json_scode); + json_object_object_add(json, "bgpOriginCodes", + json_ocode); + } else { + vty_out(vty, + "BGP table version is %" PRIu64 ", local router ID is %s, vrf id ", + version, inet_ntoa(bgp->router_id)); + if (bgp->vrf_id == VRF_UNKNOWN) + vty_out(vty, "%s", VRFID_NONE_STR); + else + vty_out(vty, "%u", bgp->vrf_id); + vty_out(vty, "\n"); + vty_out(vty, "Default local pref %u, ", + bgp->default_local_pref); + vty_out(vty, "local AS %u\n", bgp->as); + vty_out(vty, BGP_SHOW_SCODE_HEADER); + vty_out(vty, BGP_SHOW_NCODE_HEADER); + vty_out(vty, BGP_SHOW_OCODE_HEADER); + } + *header1 = 0; + } + if (*header2) { + if (!json) + vty_out(vty, (wide ? BGP_SHOW_HEADER_WIDE + : BGP_SHOW_HEADER)); + *header2 = 0; + } +} + static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi, safi_t safi, enum bgp_show_adj_route_type type, const char *rmap_name, bool use_json, @@ -12077,60 +12127,9 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi, if (ain->peer != peer) continue; - if (header1) { - if (use_json) { - json_object_int_add( - json, "bgpTableVersion", - 0); - json_object_string_add( - json, - "bgpLocalRouterId", - inet_ntoa( - bgp->router_id)); - json_object_int_add(json, - "defaultLocPrf", - bgp->default_local_pref); - json_object_int_add(json, - "localAS", bgp->as); - json_object_object_add( - json, "bgpStatusCodes", - json_scode); - json_object_object_add( - json, "bgpOriginCodes", - json_ocode); - } else { - vty_out(vty, - "BGP table version is 0, local router ID is %s, vrf id ", - inet_ntoa( - bgp->router_id)); - if (bgp->vrf_id == VRF_UNKNOWN) - vty_out(vty, "%s", - VRFID_NONE_STR); - else - vty_out(vty, "%u", - bgp->vrf_id); - vty_out(vty, "\n"); - vty_out(vty, - "Default local pref %u, ", - bgp->default_local_pref); - vty_out(vty, "local AS %u\n", - bgp->as); - vty_out(vty, - BGP_SHOW_SCODE_HEADER); - vty_out(vty, - BGP_SHOW_NCODE_HEADER); - vty_out(vty, - BGP_SHOW_OCODE_HEADER); - } - header1 = 0; - } - if (header2) { - if (!use_json) - vty_out(vty, - (wide ? BGP_SHOW_HEADER_WIDE - : BGP_SHOW_HEADER)); - header2 = 0; - } + show_adj_route_header( + vty, bgp, table, &header1, &header2, + json, json_scode, json_ocode, wide); attr = *ain->attr; route_filtered = false; @@ -12171,71 +12170,10 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi, if (paf->peer != peer || !adj->attr) continue; - if (header1) { - if (use_json) { - json_object_int_add( - json, - "bgpTableVersion", - table->version); - json_object_string_add( - json, - "bgpLocalRouterId", - inet_ntoa( - bgp->router_id)); - json_object_int_add( - json, "defaultLocPrf", - bgp->default_local_pref - ); - json_object_int_add( - json, "localAS", - bgp->as); - json_object_object_add( - json, - "bgpStatusCodes", - json_scode); - json_object_object_add( - json, - "bgpOriginCodes", - json_ocode); - } else { - vty_out(vty, - "BGP table version is %" PRIu64", local router ID is %s, vrf id ", - table->version, - inet_ntoa( - bgp->router_id)); - if (bgp->vrf_id == - VRF_UNKNOWN) - vty_out(vty, - "%s", - VRFID_NONE_STR); - else - vty_out(vty, - "%u", - bgp->vrf_id); - vty_out(vty, "\n"); - vty_out(vty, - "Default local pref %u, ", - bgp->default_local_pref - ); - vty_out(vty, - "local AS %u\n", - bgp->as); - vty_out(vty, - BGP_SHOW_SCODE_HEADER); - vty_out(vty, - BGP_SHOW_NCODE_HEADER); - vty_out(vty, - BGP_SHOW_OCODE_HEADER); - } - header1 = 0; - } - if (header2) { - if (!use_json) - vty_out(vty, - (wide ? BGP_SHOW_HEADER_WIDE - : BGP_SHOW_HEADER)); - header2 = 0; - } + show_adj_route_header( + vty, bgp, table, &header1, + &header2, json, json_scode, + json_ocode, wide); const struct prefix *rn_p = bgp_dest_get_prefix(dest); @@ -12257,6 +12195,27 @@ static void show_adj_route(struct vty *vty, struct peer *peer, afi_t afi, bgp_attr_undup(&attr, adj->attr); } + } else if (type == bgp_show_adj_route_bestpath) { + struct bgp_path_info *pi; + + show_adj_route_header(vty, bgp, table, &header1, + &header2, json, json_scode, + json_ocode, wide); + + for (pi = bgp_dest_get_bgp_path_info(dest); pi; + pi = pi->next) { + if (pi->peer != peer) + continue; + + if (!CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) + continue; + + route_vty_out_tmp(vty, + bgp_dest_get_prefix(dest), + pi->attr, safi, use_json, + json_ar, wide); + output_count++; + } } } @@ -12331,6 +12290,48 @@ static int peer_adj_routes(struct vty *vty, struct peer *peer, afi_t afi, return CMD_SUCCESS; } +DEFPY (show_ip_bgp_instance_neighbor_bestpath_route, + show_ip_bgp_instance_neighbor_bestpath_route_cmd, + "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] neighbors <A.B.C.D|X:X::X:X|WORD> bestpath-routes [json$uj | wide$wide]", + SHOW_STR + IP_STR + BGP_STR + BGP_INSTANCE_HELP_STR + BGP_AFI_HELP_STR + BGP_SAFI_WITH_LABEL_HELP_STR + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Neighbor on BGP configured interface\n" + "Display the routes selected by best path\n" + JSON_STR + "Increase table width for longer prefixes\n") +{ + afi_t afi = AFI_IP6; + safi_t safi = SAFI_UNICAST; + char *rmap_name = NULL; + char *peerstr = NULL; + struct bgp *bgp = NULL; + struct peer *peer; + enum bgp_show_adj_route_type type = bgp_show_adj_route_bestpath; + int idx = 0; + + bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi, + &bgp, uj); + + if (!idx) + return CMD_WARNING; + + argv_find(argv, argc, "neighbors", &idx); + peerstr = argv[++idx]->arg; + + peer = peer_lookup_in_view(vty, bgp, peerstr, uj); + if (!peer) + return CMD_WARNING; + + return peer_adj_routes(vty, peer, afi, safi, type, rmap_name, uj, wide); +} + DEFPY (show_ip_bgp_instance_neighbor_advertised_route, show_ip_bgp_instance_neighbor_advertised_route_cmd, "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] neighbors <A.B.C.D|X:X::X:X|WORD> <advertised-routes|received-routes|filtered-routes> [route-map WORD] [json$uj | wide$wide]", @@ -13540,6 +13541,8 @@ void bgp_route_init(void) install_element(VIEW_NODE, &show_ip_bgp_instance_neighbor_advertised_route_cmd); + install_element(VIEW_NODE, + &show_ip_bgp_instance_neighbor_bestpath_route_cmd); install_element(VIEW_NODE, &show_ip_bgp_neighbor_routes_cmd); install_element(VIEW_NODE, &show_ip_bgp_neighbor_received_prefix_filter_cmd); diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 0767d2912f..32c65c8fac 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -62,6 +62,7 @@ enum bgp_show_adj_route_type { bgp_show_adj_route_advertised, bgp_show_adj_route_received, bgp_show_adj_route_filtered, + bgp_show_adj_route_bestpath, }; diff --git a/bgpd/bgp_updgrp_packet.c b/bgpd/bgp_updgrp_packet.c index 4de5ec3b04..5df9e3f23f 100644 --- a/bgpd/bgp_updgrp_packet.c +++ b/bgpd/bgp_updgrp_packet.c @@ -574,6 +574,18 @@ struct stream *bpacket_reformat_for_peer(struct bpacket *pkt, gnh_modified = 1; } + if (IN6_IS_ADDR_UNSPECIFIED(mod_v6nhg)) { + if (peer->nexthop.v4.s_addr) { + ipv4_to_ipv4_mapped_ipv6(mod_v6nhg, + peer->nexthop.v4); + } + } + + if (IS_MAPPED_IPV6(&peer->nexthop.v6_global)) { + mod_v6nhg = &peer->nexthop.v6_global; + gnh_modified = 1; + } + if (nhlen == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL || nhlen == BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL) { stream_get_from(&v6nhlocal, s, offset_nhlocal, diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index d776b32a88..795ca00ecf 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -1393,6 +1393,15 @@ Configuring Peers peer in question. This number is between 0 and 600 seconds, with the default advertisement interval being 0. +Displaying Information about Peers +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. index:: show bgp <afi> <safi> neighbors WORD bestpath-routes [json] [wide] +.. clicmd:: show bgp <afi> <safi> neighbors WORD bestpath-routes [json] [wide] + + For the given neighbor, WORD, that is specified list the routes selected + by BGP as having the best path. + .. _bgp-peer-filtering: Peer Filtering @@ -2612,17 +2621,17 @@ daemon project, while :clicmd:`show bgp` command is the new format. The choice has been done to keep old format with IPv4 routing table, while new format displays IPv6 routing table. -.. index:: show ip bgp -.. clicmd:: show ip bgp +.. index:: show ip bgp [wide] +.. clicmd:: show ip bgp [wide] -.. index:: show ip bgp A.B.C.D -.. clicmd:: show ip bgp A.B.C.D +.. index:: show ip bgp A.B.C.D [wide] +.. clicmd:: show ip bgp A.B.C.D [wide] -.. index:: show bgp -.. clicmd:: show bgp +.. index:: show bgp [wide] +.. clicmd:: show bgp [wide] -.. index:: show bgp X:X::X:X -.. clicmd:: show bgp X:X::X:X +.. index:: show bgp X:X::X:X [wide] +.. clicmd:: show bgp X:X::X:X [wide] These commands display BGP routes. When no route is specified, the default is to display all BGP routes. @@ -2638,6 +2647,12 @@ displays IPv6 routing table. Total number of prefixes 1 + If _wide_ option is specified, then the prefix table's width is increased + to fully display the prefix and the nexthop. + + This is especially handy dealing with IPv6 prefixes and + if :clicmd:`[no] bgp default show-nexthop-hostname` is enabled. + Some other commands provide additional options for filtering the output. .. index:: show [ip] bgp regexp LINE diff --git a/lib/ipaddr.h b/lib/ipaddr.h index a96c9788bc..f2b75c1306 100644 --- a/lib/ipaddr.h +++ b/lib/ipaddr.h @@ -89,6 +89,13 @@ static inline char *ipaddr2str(const struct ipaddr *ip, char *buf, int size) return buf; } +#define IS_MAPPED_IPV6(A) \ + ((A)->s6_addr32[0] == 0x00000000 \ + ? ((A)->s6_addr32[1] == 0x00000000 \ + ? (ntohl((A)->s6_addr32[2]) == 0xFFFF ? 1 : 0) \ + : 0) \ + : 0) + /* * Convert IPv4 address to IPv4-mapped IPv6 address which is of the * form ::FFFF:<IPv4 address> (RFC 4291). This IPv6 address can then diff --git a/nhrpd/nhrp_route.c b/nhrpd/nhrp_route.c index e4270f09a5..0c5513b892 100644 --- a/nhrpd/nhrp_route.c +++ b/nhrpd/nhrp_route.c @@ -198,6 +198,10 @@ int nhrp_route_read(ZAPI_CALLBACK_ARGS) if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX)) return 0; + /* ignore our routes */ + if (api.type == ZEBRA_ROUTE_NHRP) + return 0; + sockunion_family(&nexthop_addr) = AF_UNSPEC; if (CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP)) { api_nh = &api.nexthops[0]; diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index e04eb539d8..84bdb9ec5b 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -276,7 +276,7 @@ void ospf_zebra_add(struct ospf *ospf, struct prefix_ipv4 *p, count++; if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) { - char buf[2][INET_ADDRSTRLEN]; + char buf[2][PREFIX2STR_BUFFER]; struct interface *ifp; ifp = if_lookup_by_index(path->ifindex, ospf->vrf_id); diff --git a/tests/topotests/route-scale/test_route_scale.py b/tests/topotests/route-scale/test_route_scale.py index 9ba0c7e50e..508d1746b3 100755 --- a/tests/topotests/route-scale/test_route_scale.py +++ b/tests/topotests/route-scale/test_route_scale.py @@ -112,99 +112,103 @@ def test_converge_protocols(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) -def test_route_install(): - "Test route install for a variety of ecmp" +def run_one_setup(r1, s): + "Run one ecmp config" - tgen = get_topogen() - # Don't run this test if we have any failure. - if tgen.routers_have_failure(): - pytest.skip(tgen.errors) + # Extract params + expected_installed = s['expect_in'] + expected_removed = s['expect_rem'] - installed_file = "{}/r1/installed.routes.json".format(CWD) - expected_installed = json.loads(open(installed_file).read()) + count = s['count'] + wait = s['wait'] - removed_file = "{}/r1/no.routes.json".format(CWD) - expected_removed = json.loads(open(removed_file).read()) + logger.info("Testing 1 million routes X {} ecmp".format(s['ecmp'])) - r1 = tgen.gears["r1"] + r1.vtysh_cmd("sharp install route 1.0.0.0 \ + nexthop-group {} 1000000".format(s['nhg']), + isjson=False) - r1.vtysh_cmd("sharp install route 1.0.0.0 nexthop-group one 1000000", isjson=False) test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_installed) - _, result = topotest.run_and_expect(test_func, None, count=40, wait=5) - output = r1.vtysh_cmd("sharp data route", isjson=False) - logger.info("1 million routes X 1 ecmp installed") - logger.info(output) - r1.vtysh_cmd("sharp remove route 1.0.0.0 1000000", isjson=False) - test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_removed) - _, result = topotest.run_and_expect(test_func, None, count=40, wait=5) - output = r1.vtysh_cmd("sharp data route", isjson=False) - logger.info("1 million routes x 1 ecmp removed") - logger.info(output) + success, result = topotest.run_and_expect(test_func, None, count, wait) + assert success, "Route scale test install failed:\n{}".format(result) - r1.vtysh_cmd("sharp install route 1.0.0.0 nexthop-group two 1000000", isjson=False) - test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_installed) - _, result = topotest.run_and_expect(test_func, None, count=40, wait=5) output = r1.vtysh_cmd("sharp data route", isjson=False) - logger.info("1 million routes X 2 ecmp installed") + logger.info("1 million routes X {} ecmp installed".format(s['ecmp'])) logger.info(output) r1.vtysh_cmd("sharp remove route 1.0.0.0 1000000", isjson=False) test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_removed) - _, result = topotest.run_and_expect(test_func, None, count=40, wait=5) - output = r1.vtysh_cmd("sharp data route", isjson=False) - logger.info("1 million routes x 2 ecmp removed") - logger.info(output) + success, result = topotest.run_and_expect(test_func, None, count, wait) + assert success, "Route scale test remove failed:\n{}".format(result) - r1.vtysh_cmd("sharp install route 1.0.0.0 nexthop-group four 1000000", isjson=False) - test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_installed) - _, result = topotest.run_and_expect(test_func, None, count=40, wait=5) output = r1.vtysh_cmd("sharp data route", isjson=False) - logger.info("1 million routes X 4 ecmp installed") - logger.info(output) - r1.vtysh_cmd("sharp remove route 1.0.0.0 1000000", isjson=False) - test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_removed) - _, result = topotest.run_and_expect(test_func, None, count=40, wait=5) - output = r1.vtysh_cmd("sharp data route", isjson=False) - logger.info("1 million routes x 4 ecmp removed") + logger.info("1 million routes x {} ecmp removed".format( + s['ecmp'])) logger.info(output) - r1.vtysh_cmd("sharp install route 1.0.0.0 nexthop-group eight 1000000", isjson=False) - test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_installed) - _, result = topotest.run_and_expect(test_func, None, count=40, wait=5) - output = r1.vtysh_cmd("sharp data route", isjson=False) - logger.info("1 million routes X 8 ecmp installed") - logger.info(output) - r1.vtysh_cmd("sharp remove route 1.0.0.0 1000000", isjson=False) - test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_removed) - _, result = topotest.run_and_expect(test_func, None, count=40, wait=5) - output = r1.vtysh_cmd("sharp data route", isjson=False) - logger.info("1 million routes x 8 ecmp removed") - logger.info(output) - r1.vtysh_cmd("sharp install route 1.0.0.0 nexthop-group sixteen 1000000", isjson=False) - test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_installed) - _, result = topotest.run_and_expect(test_func, None, count=40, wait=5) - output = r1.vtysh_cmd("sharp data route", isjson=False) - logger.info("1 million routes X 16 ecmp installed") - logger.info(output) - r1.vtysh_cmd("sharp remove route 1.0.0.0 1000000", isjson=False) - test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_removed) - _, result = topotest.run_and_expect(test_func, None, count=40, wait=5) - output = r1.vtysh_cmd("sharp data route", isjson=False) - logger.info("1 million routes x 16 ecmp removed") - logger.info(output) +def test_route_install(): + "Test route install for a variety of ecmp" - r1.vtysh_cmd("sharp install route 1.0.0.0 nexthop-group thirtytwo 1000000", isjson=False) - test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_installed) - _, result = topotest.run_and_expect(test_func, None, count=40, wait=5) - output = r1.vtysh_cmd("sharp data route", isjson=False) - logger.info("1 million routes X 32 ecmp installed") - logger.info(output) - r1.vtysh_cmd("sharp remove route 1.0.0.0 1000000", isjson=False) - test_func = partial(topotest.router_json_cmp, r1, "show ip route summary json", expected_removed) - _, result = topotest.run_and_expect(test_func, None, count=40, wait=5) - output = r1.vtysh_cmd("sharp data route", isjson=False) - logger.info("1 million routes x 32 ecmp removed") - logger.info(output) + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + installed_file = "{}/r1/installed.routes.json".format(CWD) + expected_installed = json.loads(open(installed_file).read()) + + removed_file = "{}/r1/no.routes.json".format(CWD) + expected_removed = json.loads(open(removed_file).read()) + + # dict keys of params: ecmp number, corresponding nhg name, timeout, + # number of times to wait + scale_keys = ['ecmp', 'nhg', 'wait', 'count', 'expect_in', 'expect_rem'] + + # Table of defaults, used for timeout values and 'expected' objects + scale_defaults = dict(zip(scale_keys, [None, None, 7, 30, + expected_installed, + expected_removed])) + + # List of params for each step in the test; note extra time given + # for the highest ecmp steps. Executing 'show' at scale can be costly + # so we widen the interval there too. + scale_steps = [ + [1, 'one'], [2, 'two'], [4, 'four'], + [8, 'eight'], [16, 'sixteen', 10, 40], [32, 'thirtytwo', 10, 40] + ] + + # Build up a list of dicts with params for each step of the test; + # use defaults where the step doesn't supply a value + scale_setups = [] + for s in scale_steps: + d = dict(zip(scale_keys, s)) + for k in scale_keys: + if k not in d: + d[k] = scale_defaults[k] + + scale_setups.append(d) + + # Avoid top ecmp case for runs with < 4G memory + p = os.popen('free') + l = p.readlines()[1].split() + mem = int(l[1]) + if mem < 4000000: + logger.info('Limited memory available: {}, skipping x32 testcase'.format(mem)) + scale_setups = scale_setups[0:-1] + + # Run each step using the dicts we've built + for s in scale_setups: + run_one_setup(r1, s) + +# Mem leak testcase +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:] diff --git a/yang/frr-route-map.yang b/yang/frr-route-map.yang index 734b984d7d..b895cd12a4 100644 --- a/yang/frr-route-map.yang +++ b/yang/frr-route-map.yang @@ -6,14 +6,17 @@ module frr-route-map { import ietf-inet-types { prefix inet; } + import frr-filter { prefix filter; } + import frr-interface { prefix frr-interface; } - organization "FRRouting"; + organization + "FRRouting"; contact "FRR Users List: <mailto:frog@lists.frrouting.org> FRR Development List: <mailto:dev@lists.frrouting.org>"; @@ -46,22 +49,34 @@ module frr-route-map { OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; revision 2019-07-01 { - description "Initial revision"; + description + "Initial revision"; } /* * Types. */ typedef route-map-sequence { - description "Route map valid sequence numbers"; type uint16 { range "1..65535"; } + description + "Route map valid sequence numbers."; } typedef route-map-name { - description "Route map name format"; type string; + description + "Route map name format."; + } + + typedef route-map-ref { + type leafref { + path "/frr-route-map:lib/frr-route-map:route-map/frr-route-map:name"; + require-instance false; + } + description + "Reference to a route-map."; } /* @@ -69,34 +84,33 @@ module frr-route-map { */ container lib { list route-map { - description "Route map instance"; - key "name"; - + description + "Route map instance."; leaf name { - description "Route map instance name"; type route-map-name; + description + "Route map instance name."; } list entry { - description "Route map entry"; - key "sequence"; - + description + "Route map entry."; leaf sequence { description - "Route map instance priority (low number means higher priority)"; + "Route map instance priority (low number means higher priority)."; type route-map-sequence; } leaf description { - description "Route map description"; + description "Route map description."; type string; } leaf action { description - "Route map actions: permit (executes action), deny (quits evaluation)"; + "Route map actions: permit (executes action), deny (quits evaluation)."; mandatory true; type enumeration { enum permit { @@ -120,25 +134,25 @@ module frr-route-map { description "Call another route map before calling `exit-policy`. If the called route map returns deny then this route map will also - return deny"; + return deny."; type route-map-name; } leaf exit-policy { - description "What do to after route map successful match, set and call"; + description "What do to after route map successful match, set and call."; type enumeration { enum permit-or-deny { - description "End route map evaluation and return"; + description "End route map evaluation and return."; value 0; } enum next { description - "Proceed evaluating next route map entry per sequence"; + "Proceed evaluating next route map entry per sequence."; value 1; } enum goto { description - "Go to route map entry with the provided sequence number"; + "Go to route map entry with the provided sequence number."; value 2; } } @@ -148,82 +162,81 @@ module frr-route-map { leaf goto-value { when "../exit-policy = 'goto'"; description - "Sequence number to jump (when using `goto` exit policy)"; + "Sequence number to jump (when using `goto` exit policy)."; mandatory true; type route-map-sequence; } list match-condition { - description "Route map match conditions"; - key "condition"; - + description + "Route map match conditions."; leaf condition { - description "Match condition"; + description "Match condition."; type enumeration { enum interface { - description "Match interface"; + description "Match interface."; value 0; } enum ipv4-address-list { - description "Match an IPv4 access-list"; + description "Match an IPv4 access-list."; value 1; } enum ipv4-prefix-list { - description "Match an IPv4 prefix-list"; + description "Match an IPv4 prefix-list."; value 2; } enum ipv4-next-hop-list { - description "Match an IPv4 next-hop"; + description "Match an IPv4 next-hop."; value 3; } enum ipv4-next-hop-prefix-list { - description "Match an IPv4 next-hop prefix list"; + description "Match an IPv4 next-hop prefix list."; value 4; } enum ipv4-next-hop-type { - description "Match an IPv4 next-hop type"; + description "Match an IPv4 next-hop type."; value 5; } enum ipv6-address-list { - description "Match an IPv6 access-list"; + description "Match an IPv6 access-list."; value 6; } enum ipv6-prefix-list { - description "Match an IPv6 prefix-list"; + description "Match an IPv6 prefix-list."; value 7; } enum ipv6-next-hop-type { - description "Match an IPv6 next-hop type"; + description "Match an IPv6 next-hop type."; value 8; } enum metric { - description "Match a route metric"; + description "Match a route metric."; value 9; } enum tag { - description "Match a route tag"; + description "Match a route tag."; value 10; } /* zebra specific conditions. */ enum ipv4-prefix-length { - description "Match IPv4 prefix length"; + description "Match IPv4 prefix length."; value 100; } enum ipv6-prefix-length { - description "Match IPv6 prefix length"; + description "Match IPv6 prefix length."; value 101; } enum ipv4-next-hop-prefix-length { - description "Match next-hop prefix length"; + description "Match next-hop prefix length."; value 102; } enum source-protocol { - description "Match source protocol"; + description "Match source protocol."; value 103; } enum source-instance { - description "Match source protocol instance"; + description "Match source protocol instance."; value 104; } } @@ -231,7 +244,7 @@ module frr-route-map { choice condition-value { description - "Value to match (interpretation depends on condition type)"; + "Value to match (interpretation depends on condition type)."; mandatory true; case interface { when "./condition = 'interface'"; @@ -239,6 +252,7 @@ module frr-route-map { type string; } } + case list-name { when "./condition = 'ipv4-address-list' or ./condition = 'ipv4-prefix-list' or @@ -250,6 +264,7 @@ module frr-route-map { type filter:access-list-name; } } + case ipv4-next-hop-type { when "./condition = 'ipv4-next-hop-type'"; leaf ipv4-next-hop-type { @@ -260,6 +275,7 @@ module frr-route-map { } } } + case ipv6-next-hop-type { when "./condition = 'ipv6-next-hop-type'"; leaf ipv6-next-hop-type { @@ -270,6 +286,7 @@ module frr-route-map { } } } + case metric { when "./condition = 'metric'"; leaf metric { @@ -278,6 +295,7 @@ module frr-route-map { } } } + case tag { when "./condition = 'tag'"; leaf tag { @@ -290,32 +308,32 @@ module frr-route-map { } list set-action { - description "Route map set actions"; + description "Route map set actions."; key "action"; leaf action { - description "Action to do when the route map matches"; + description "Action to do when the route map matches."; type enumeration { enum ipv4-next-hop { - description "Set IPv4 address of the next hop"; + description "Set IPv4 address of the next hop."; value 0; } enum ipv6-next-hop { - description "Set IPv6 address of the next hop"; + description "Set IPv6 address of the next hop."; value 1; } enum metric { - description "Set prefix/route metric"; + description "Set prefix/route metric."; value 2; } enum tag { - description "Set tag"; + description "Set tag."; value 3; } /* zebra specific conditions. */ enum source { - description "Set source address for route"; + description "Set source address for route."; value 100; } } @@ -323,69 +341,77 @@ module frr-route-map { choice action-value { description - "Value to set (interpretation depends on action-type)"; + "Value to set (interpretation depends on action-type)."; case ipv4-address { when "./action = 'ipv4-next-hop'"; leaf ipv4-address { - description "IPv4 address"; + description "IPv4 address."; type inet:ipv4-address; } } + case ipv6-address { when "./action = 'ipv6-next-hop'"; leaf ipv6-address { - description "IPv6 address"; + description "IPv6 address."; type inet:ipv6-address; } } + case metric { when "./action = 'metric'"; choice metric-value { - description "Metric to set or use"; + description "Metric to set or use."; case value { leaf value { - description "Use the following metric value"; + description "Use the following metric value."; type uint32 { range "0..4294967295"; } } } + case add-metric { leaf add-metric { - description "Add unit to metric"; + description "Add unit to metric."; type boolean; } } + case subtract-metric { leaf subtract-metric { - description "Subtract unit from metric"; + description "Subtract unit from metric."; type boolean; } } + case use-round-trip-time { leaf use-round-trip-time { - description "Use the round trip time as metric"; + description "Use the round trip time as metric."; type boolean; } } + case add-round-trip-time { leaf add-round-trip-time { - description "Add round trip time to metric"; + description "Add round trip time to metric."; type boolean; } } + case subtract-round-trip-time { leaf subtract-round-trip-time { - description "Subtract round trip time to metric"; + description "Subtract round trip time to metric."; type boolean; } } } } + case tag { when "./action = 'tag'"; leaf tag { - description "Tag value"; + description "Tag value."; type uint32 { range "0..4294967295"; } diff --git a/zebra/dplane_fpm_nl.c b/zebra/dplane_fpm_nl.c index ef208bdc83..c81d451693 100644 --- a/zebra/dplane_fpm_nl.c +++ b/zebra/dplane_fpm_nl.c @@ -72,6 +72,7 @@ struct fpm_nl_ctx { int socket; bool disabled; bool connecting; + bool nhg_complete; bool rib_complete; bool rmac_complete; bool use_nhg; @@ -149,8 +150,25 @@ enum fpm_nl_events { FNE_RESET_COUNTERS, /* Toggle next hop group feature. */ FNE_TOGGLE_NHG, + /* Reconnect request by our own code to avoid races. */ + FNE_INTERNAL_RECONNECT, + + /* Next hop groups walk finished. */ + FNE_NHG_FINISHED, + /* RIB walk finished. */ + FNE_RIB_FINISHED, + /* RMAC walk finished. */ + FNE_RMAC_FINISHED, }; +#define FPM_RECONNECT(fnc) \ + thread_add_event((fnc)->fthread->master, fpm_process_event, (fnc), \ + FNE_INTERNAL_RECONNECT, &(fnc)->t_event) + +#define WALK_FINISH(fnc, ev) \ + thread_add_event((fnc)->fthread->master, fpm_process_event, (fnc), \ + (ev), NULL) + /* * Prototypes. */ @@ -428,7 +446,18 @@ 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. */ + /* Cancel all zebra threads first. */ + thread_cancel_async(zrouter.master, &fnc->t_nhgreset, NULL); + thread_cancel_async(zrouter.master, &fnc->t_nhgwalk, NULL); + thread_cancel_async(zrouter.master, &fnc->t_ribreset, NULL); + thread_cancel_async(zrouter.master, &fnc->t_ribwalk, NULL); + thread_cancel_async(zrouter.master, &fnc->t_rmacreset, NULL); + thread_cancel_async(zrouter.master, &fnc->t_rmacwalk, NULL); + + /* + * Grab the lock to empty the streams (data plane might try to + * enqueue updates while we are closing). + */ frr_mutex_lock_autounlock(&fnc->obuf_mutex); /* Avoid calling close on `-1`. */ @@ -442,13 +471,6 @@ static void fpm_reconnect(struct fpm_nl_ctx *fnc) THREAD_OFF(fnc->t_read); THREAD_OFF(fnc->t_write); - thread_cancel_async(zrouter.master, &fnc->t_nhgreset, NULL); - thread_cancel_async(zrouter.master, &fnc->t_nhgwalk, NULL); - thread_cancel_async(zrouter.master, &fnc->t_ribreset, NULL); - thread_cancel_async(zrouter.master, &fnc->t_ribwalk, NULL); - thread_cancel_async(zrouter.master, &fnc->t_rmacreset, NULL); - thread_cancel_async(zrouter.master, &fnc->t_rmacwalk, NULL); - /* FPM is disabled, don't attempt to connect. */ if (fnc->disabled) return; @@ -465,6 +487,13 @@ static int fpm_read(struct thread *t) /* Let's ignore the input at the moment. */ rv = stream_read_try(fnc->ibuf, fnc->socket, STREAM_WRITEABLE(fnc->ibuf)); + /* We've got an interruption. */ + if (rv == -2) { + /* Schedule next read. */ + thread_add_read(fnc->fthread->master, fpm_read, fnc, + fnc->socket, &fnc->t_read); + return 0; + } if (rv == 0) { atomic_fetch_add_explicit(&fnc->counters.connection_closes, 1, memory_order_relaxed); @@ -472,19 +501,15 @@ static int fpm_read(struct thread *t) if (IS_ZEBRA_DEBUG_FPM) zlog_debug("%s: connection closed", __func__); - fpm_reconnect(fnc); + 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); + FPM_RECONNECT(fnc); return 0; } stream_reset(fnc->ibuf); @@ -525,33 +550,15 @@ static int fpm_write(struct thread *t) &fnc->counters.connection_errors, 1, memory_order_relaxed); - fpm_reconnect(fnc); + FPM_RECONNECT(fnc); return 0; } fnc->connecting = false; - /* - * Walk the route tables to send old information before starting - * to send updated information. - * - * NOTE 1: - * RIB table walk is called after the next group table walk - * ends. - * - * NOTE 2: - * Don't attempt to go through next hop group table if we were - * explictly told to not use it. - */ - if (fnc->use_nhg) - thread_add_timer(zrouter.master, fpm_nhg_send, fnc, 0, - &fnc->t_nhgwalk); - else - 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); + /* Permit receiving messages now. */ + thread_add_read(fnc->fthread->master, fpm_read, fnc, + fnc->socket, &fnc->t_read); } frr_mutex_lock_autounlock(&fnc->obuf_mutex); @@ -589,8 +596,9 @@ static int fpm_write(struct thread *t) memory_order_relaxed); zlog_warn("%s: connection failure: %s", __func__, strerror(errno)); - fpm_reconnect(fnc); - break; + + FPM_RECONNECT(fnc); + return 0; } /* Account all bytes sent. */ @@ -661,18 +669,19 @@ static int fpm_connect(struct thread *t) fnc->connecting = (errno == EINPROGRESS); fnc->socket = sock; - thread_add_read(fnc->fthread->master, fpm_read, fnc, sock, - &fnc->t_read); + if (!fnc->connecting) + 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_nhg_reset, fnc, 0, - &fnc->t_nhgreset); - 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); + if (fnc->use_nhg) + thread_add_timer(zrouter.master, fpm_nhg_reset, fnc, 0, + &fnc->t_nhgreset); + else + thread_add_timer(zrouter.master, fpm_rib_reset, fnc, 0, + &fnc->t_ribreset); return 0; } @@ -904,10 +913,11 @@ static int fpm_nhg_send(struct thread *t) dplane_ctx_fini(&fna.ctx); /* We are done sending next hops, lets install the routes now. */ - if (fna.complete) - thread_add_timer(zrouter.master, fpm_rib_send, fnc, 0, - &fnc->t_ribwalk); - else /* Otherwise reschedule next hop group again. */ + if (fna.complete) { + WALK_FINISH(fnc, FNE_NHG_FINISHED); + thread_add_timer(zrouter.master, fpm_rib_reset, fnc, 0, + &fnc->t_ribreset); + } else /* Otherwise reschedule next hop group again. */ thread_add_timer(zrouter.master, fpm_nhg_send, fnc, 0, &fnc->t_nhgwalk); @@ -963,7 +973,11 @@ static int fpm_rib_send(struct thread *t) dplane_ctx_fini(&ctx); /* All RIB routes sent! */ - fnc->rib_complete = true; + WALK_FINISH(fnc, FNE_RIB_FINISHED); + + /* Schedule next event: RMAC reset. */ + thread_add_event(zrouter.master, fpm_rmac_reset, fnc, 0, + &fnc->t_rmacreset); return 0; } @@ -975,6 +989,7 @@ struct fpm_rmac_arg { struct zebra_dplane_ctx *ctx; struct fpm_nl_ctx *fnc; zebra_l3vni_t *zl3vni; + bool complete; }; static void fpm_enqueue_rmac_table(struct hash_bucket *backet, void *arg) @@ -988,7 +1003,7 @@ static void fpm_enqueue_rmac_table(struct hash_bucket *backet, void *arg) bool sticky; /* Entry already sent. */ - if (CHECK_FLAG(zrmac->flags, ZEBRA_MAC_FPM_SENT)) + if (CHECK_FLAG(zrmac->flags, ZEBRA_MAC_FPM_SENT) || !fra->complete) return; sticky = !!CHECK_FLAG(zrmac->flags, @@ -1004,6 +1019,7 @@ static void fpm_enqueue_rmac_table(struct hash_bucket *backet, void *arg) if (fpm_nl_enqueue(fra->fnc, fra->ctx) == -1) { thread_add_timer(zrouter.master, fpm_rmac_send, fra->fnc, 1, &fra->fnc->t_rmacwalk); + fra->complete = false; } } @@ -1022,9 +1038,14 @@ static int fpm_rmac_send(struct thread *t) fra.fnc = THREAD_ARG(t); fra.ctx = dplane_ctx_alloc(); + fra.complete = true; hash_iterate(zrouter.l3vni_table, fpm_enqueue_l3vni_table, &fra); dplane_ctx_fini(&fra.ctx); + /* RMAC walk completed. */ + if (fra.complete) + WALK_FINISH(fra.fnc, FNE_RMAC_FINISHED); + return 0; } @@ -1041,7 +1062,14 @@ static void fpm_nhg_reset_cb(struct hash_bucket *bucket, void *arg) static int fpm_nhg_reset(struct thread *t) { + struct fpm_nl_ctx *fnc = THREAD_ARG(t); + + fnc->nhg_complete = false; hash_iterate(zrouter.nhgs_id, fpm_nhg_reset_cb, NULL); + + /* Schedule next step: send next hop groups. */ + thread_add_event(zrouter.master, fpm_nhg_send, fnc, 0, &fnc->t_nhgwalk); + return 0; } @@ -1070,6 +1098,9 @@ static int fpm_rib_reset(struct thread *t) } } + /* Schedule next step: send RIB routes. */ + thread_add_event(zrouter.master, fpm_rib_send, fnc, 0, &fnc->t_ribwalk); + return 0; } @@ -1092,8 +1123,15 @@ static void fpm_unset_l3vni_table(struct hash_bucket *backet, void *arg) static int fpm_rmac_reset(struct thread *t) { + struct fpm_nl_ctx *fnc = THREAD_ARG(t); + + fnc->rmac_complete = false; hash_iterate(zrouter.l3vni_table, fpm_unset_l3vni_table, NULL); + /* Schedule next event: send RMAC entries. */ + thread_add_event(zrouter.master, fpm_rmac_send, fnc, 0, + &fnc->t_rmacwalk); + return 0; } @@ -1174,6 +1212,30 @@ static int fpm_process_event(struct thread *t) fpm_reconnect(fnc); break; + case FNE_INTERNAL_RECONNECT: + fpm_reconnect(fnc); + break; + + case FNE_NHG_FINISHED: + if (IS_ZEBRA_DEBUG_FPM) + zlog_debug("%s: next hop groups walk finished", + __func__); + + fnc->nhg_complete = true; + break; + case FNE_RIB_FINISHED: + if (IS_ZEBRA_DEBUG_FPM) + zlog_debug("%s: RIB walk finished", __func__); + + fnc->rib_complete = true; + break; + case FNE_RMAC_FINISHED: + if (IS_ZEBRA_DEBUG_FPM) + zlog_debug("%s: RMAC walk finished", __func__); + + fnc->rmac_complete = true; + break; + default: if (IS_ZEBRA_DEBUG_FPM) zlog_debug("%s: unhandled event %d", __func__, event); diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 07e8e37b82..8d38b6defe 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1051,14 +1051,17 @@ static bool _netlink_route_add_gateway_info(uint8_t route_family, bytelen + 2)) return false; } else { - if (gw_family == AF_INET) { - if (!nl_attr_put(nlmsg, req_size, RTA_GATEWAY, - &nexthop->gate.ipv4, bytelen)) - return false; - } else { - if (!nl_attr_put(nlmsg, req_size, RTA_GATEWAY, - &nexthop->gate.ipv6, bytelen)) - return false; + if (!(nexthop->rparent + && IS_MAPPED_IPV6(&nexthop->rparent->gate.ipv6))) { + if (gw_family == AF_INET) { + if (!nl_attr_put(nlmsg, req_size, RTA_GATEWAY, + &nexthop->gate.ipv4, bytelen)) + return false; + } else { + if (!nl_attr_put(nlmsg, req_size, RTA_GATEWAY, + &nexthop->gate.ipv6, bytelen)) + return false; + } } } diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c index a18885ddb7..5831093b50 100644 --- a/zebra/zebra_fpm_netlink.c +++ b/zebra/zebra_fpm_netlink.c @@ -364,6 +364,7 @@ static int netlink_route_info_encode(struct netlink_route_info *ri, struct rtattr *nest, *inner_nest; struct rtnexthop *rtnh; struct vxlan_encap_info_t *vxlan; + struct in6_addr ipv6; struct { struct nlmsghdr n; @@ -423,8 +424,15 @@ static int netlink_route_info_encode(struct netlink_route_info *ri, nhi = &ri->nhs[0]; if (nhi->gateway) { - nl_attr_put(&req->n, in_buf_len, RTA_GATEWAY, - nhi->gateway, bytelen); + if (nhi->type == NEXTHOP_TYPE_IPV4_IFINDEX + && ri->af == AF_INET6) { + ipv4_to_ipv4_mapped_ipv6(&ipv6, + nhi->gateway->ipv4); + nl_attr_put(&req->n, in_buf_len, RTA_GATEWAY, + &ipv6, bytelen); + } else + nl_attr_put(&req->n, in_buf_len, RTA_GATEWAY, + nhi->gateway, bytelen); } if (nhi->if_index) { diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index c058090844..43bf745896 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -1775,6 +1775,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re, struct interface *ifp; rib_dest_t *dest; struct zebra_vrf *zvrf; + struct in_addr ipv4; if ((nexthop->type == NEXTHOP_TYPE_IPV4) || nexthop->type == NEXTHOP_TYPE_IPV6) @@ -1835,13 +1836,23 @@ static int nexthop_active(afi_t afi, struct route_entry *re, return 0; } + /* Validation for ipv4 mapped ipv6 nexthop. */ + if (IS_MAPPED_IPV6(&nexthop->gate.ipv6)) { + afi = AFI_IP; + } + /* Make lookup prefix. */ memset(&p, 0, sizeof(struct prefix)); switch (afi) { case AFI_IP: p.family = AF_INET; p.prefixlen = IPV4_MAX_PREFIXLEN; - p.u.prefix4 = nexthop->gate.ipv4; + if (IS_MAPPED_IPV6(&nexthop->gate.ipv6)) { + ipv4_mapped_ipv6_to_ipv4(&nexthop->gate.ipv6, &ipv4); + p.u.prefix4 = ipv4; + } else { + p.u.prefix4 = nexthop->gate.ipv4; + } break; case AFI_IP6: p.family = AF_INET6; |
