summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.clang-format1
-rw-r--r--bgpd/bgp_nht.c17
-rw-r--r--bgpd/bgp_route.c241
-rw-r--r--bgpd/bgp_route.h1
-rw-r--r--bgpd/bgp_updgrp_packet.c12
-rw-r--r--doc/user/bgp.rst31
-rw-r--r--lib/ipaddr.h7
-rw-r--r--nhrpd/nhrp_route.c4
-rw-r--r--ospfd/ospf_zebra.c2
-rwxr-xr-xtests/topotests/route-scale/test_route_scale.py156
-rw-r--r--yang/frr-route-map.yang144
-rw-r--r--zebra/dplane_fpm_nl.c166
-rw-r--r--zebra/rt_netlink.c19
-rw-r--r--zebra/zebra_fpm_netlink.c12
-rw-r--r--zebra/zebra_nhg.c13
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;