summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_attr.c247
-rw-r--r--bgpd/bgp_attr.h23
-rw-r--r--bgpd/bgp_mplsvpn.c101
-rw-r--r--bgpd/bgp_mplsvpn.h2
-rw-r--r--bgpd/bgp_route.c72
-rw-r--r--bgpd/bgp_route.h10
-rw-r--r--bgpd/bgp_zebra.c9
-rw-r--r--bgpd/bgpd.h1
-rw-r--r--bgpd/rfapi/rfapi_vty.c12
-rw-r--r--doc/user/ospf6d.rst41
-rw-r--r--lib/prefix.h4
-rw-r--r--lib/skiplist.c43
-rw-r--r--lib/skiplist.h3
-rw-r--r--ospf6d/ospf6_abr.c158
-rw-r--r--ospf6d/ospf6_abr.h3
-rw-r--r--ospf6d/ospf6_area.c69
-rw-r--r--ospf6d/ospf6_area.h4
-rw-r--r--ospf6d/ospf6_intra.c4
-rw-r--r--ospf6d/ospf6d.c18
-rw-r--r--tests/lib/test_skiplist.c135
-rw-r--r--tests/subdir.am5
-rw-r--r--tests/topotests/bgp_prefix_sid2/peer1/exabgp.cfg4
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf10_rib.json5
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf20_rib.json10
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf10_rib.json10
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf20_rib.json5
-rw-r--r--tests/topotests/ospf6_topo2/test_ospf6_topo2.py69
-rw-r--r--vtysh/vtysh_config.c1
-rw-r--r--zebra/connected.c21
-rw-r--r--zebra/connected.h20
-rw-r--r--zebra/if_netlink.c247
-rw-r--r--zebra/if_netlink.h8
-rw-r--r--zebra/interface.c103
-rw-r--r--zebra/interface.h1
-rw-r--r--zebra/kernel_netlink.c145
-rw-r--r--zebra/kernel_socket.c24
-rw-r--r--zebra/rt.h5
-rw-r--r--zebra/zebra_dplane.c253
-rw-r--r--zebra/zebra_dplane.h26
-rw-r--r--zebra/zebra_fpm_netlink.c2
-rw-r--r--zebra/zebra_l2.c38
-rw-r--r--zebra/zebra_l2.h7
-rw-r--r--zebra/zebra_nhg.c2
-rw-r--r--zebra/zebra_ns.c3
-rw-r--r--zebra/zebra_ns.h7
-rw-r--r--zebra/zebra_rib.c5
-rw-r--r--zebra/zebra_vxlan.c93
-rw-r--r--zebra/zebra_vxlan.h1
48 files changed, 1691 insertions, 388 deletions
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index eeb0ac5c6a..7de7a6628f 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -530,6 +530,12 @@ static uint32_t srv6_l3vpn_hash_key_make(const void *p)
key = jhash(&l3vpn->sid, 16, key);
key = jhash_1word(l3vpn->sid_flags, key);
key = jhash_1word(l3vpn->endpoint_behavior, key);
+ key = jhash_1word(l3vpn->loc_block_len, key);
+ key = jhash_1word(l3vpn->loc_node_len, key);
+ key = jhash_1word(l3vpn->func_len, key);
+ key = jhash_1word(l3vpn->arg_len, key);
+ key = jhash_1word(l3vpn->transposition_len, key);
+ key = jhash_1word(l3vpn->transposition_offset, key);
return key;
}
@@ -540,7 +546,13 @@ static bool srv6_l3vpn_hash_cmp(const void *p1, const void *p2)
return sid_same(&l3vpn1->sid, &l3vpn2->sid)
&& l3vpn1->sid_flags == l3vpn2->sid_flags
- && l3vpn1->endpoint_behavior == l3vpn2->endpoint_behavior;
+ && l3vpn1->endpoint_behavior == l3vpn2->endpoint_behavior
+ && l3vpn1->loc_block_len == l3vpn2->loc_block_len
+ && l3vpn1->loc_node_len == l3vpn2->loc_node_len
+ && l3vpn1->func_len == l3vpn2->func_len
+ && l3vpn1->arg_len == l3vpn2->arg_len
+ && l3vpn1->transposition_len == l3vpn2->transposition_len
+ && l3vpn1->transposition_offset == l3vpn2->transposition_offset;
}
static bool srv6_l3vpn_same(const struct bgp_attr_srv6_l3vpn *h1,
@@ -2533,6 +2545,172 @@ static int bgp_attr_encap(uint8_t type, struct peer *peer, /* IN */
return 0;
}
+
+/* SRv6 Service Data Sub-Sub-TLV attribute
+ * draft-ietf-bess-srv6-services-07
+ */
+static bgp_attr_parse_ret_t
+bgp_attr_srv6_service_data(struct bgp_attr_parser_args *args)
+{
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ uint8_t type, loc_block_len, loc_node_len, func_len, arg_len,
+ transposition_len, transposition_offset;
+ uint16_t length;
+ size_t headersz = sizeof(type) + sizeof(length);
+
+ if (STREAM_READABLE(peer->curr) < headersz) {
+ flog_err(
+ EC_BGP_ATTR_LEN,
+ "Malformed SRv6 Service Data Sub-Sub-TLV attribute - insufficent data (need %zu for attribute header, have %zu remaining in UPDATE)",
+ headersz, STREAM_READABLE(peer->curr));
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
+ type = stream_getc(peer->curr);
+ length = stream_getw(peer->curr);
+
+ if (STREAM_READABLE(peer->curr) < length) {
+ flog_err(
+ EC_BGP_ATTR_LEN,
+ "Malformed SRv6 Service Data Sub-Sub-TLV attribute - insufficent data (need %hu for attribute data, have %zu remaining in UPDATE)",
+ length, STREAM_READABLE(peer->curr));
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
+ if (type == BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_STRUCTURE) {
+ loc_block_len = stream_getc(peer->curr);
+ loc_node_len = stream_getc(peer->curr);
+ func_len = stream_getc(peer->curr);
+ arg_len = stream_getc(peer->curr);
+ transposition_len = stream_getc(peer->curr);
+ transposition_offset = stream_getc(peer->curr);
+
+ /* Log SRv6 Service Data Sub-Sub-TLV */
+ if (BGP_DEBUG(vpn, VPN_LEAK_LABEL)) {
+ zlog_debug(
+ "%s: srv6-l3-srv-data loc-block-len=%u, loc-node-len=%u func-len=%u, arg-len=%u, transposition-len=%u, transposition-offset=%u",
+ __func__, loc_block_len, loc_node_len, func_len,
+ arg_len, transposition_len,
+ transposition_offset);
+ }
+
+ attr->srv6_l3vpn->loc_block_len = loc_block_len;
+ attr->srv6_l3vpn->loc_node_len = loc_node_len;
+ attr->srv6_l3vpn->func_len = func_len;
+ attr->srv6_l3vpn->arg_len = arg_len;
+ attr->srv6_l3vpn->transposition_len = transposition_len;
+ attr->srv6_l3vpn->transposition_offset = transposition_offset;
+ }
+
+ else {
+ if (bgp_debug_update(peer, NULL, NULL, 1))
+ zlog_debug(
+ "%s attr SRv6 Service Data Sub-Sub-TLV sub-sub-type=%u is not supported, skipped",
+ peer->host, type);
+
+ stream_forward_getp(peer->curr, length);
+ }
+
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* SRv6 Service Sub-TLV attribute
+ * draft-ietf-bess-srv6-services-07
+ */
+static bgp_attr_parse_ret_t
+bgp_attr_srv6_service(struct bgp_attr_parser_args *args)
+{
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ struct in6_addr ipv6_sid;
+ uint8_t type, sid_flags;
+ uint16_t length, endpoint_behavior;
+ size_t headersz = sizeof(type) + sizeof(length);
+ bgp_attr_parse_ret_t err;
+ char buf[BUFSIZ];
+
+ if (STREAM_READABLE(peer->curr) < headersz) {
+ flog_err(
+ EC_BGP_ATTR_LEN,
+ "Malformed SRv6 Service Sub-TLV attribute - insufficent data (need %zu for attribute header, have %zu remaining in UPDATE)",
+ headersz, STREAM_READABLE(peer->curr));
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
+ type = stream_getc(peer->curr);
+ length = stream_getw(peer->curr);
+
+ if (STREAM_READABLE(peer->curr) < length) {
+ flog_err(
+ EC_BGP_ATTR_LEN,
+ "Malformed SRv6 Service Sub-TLV attribute - insufficent data (need %hu for attribute data, have %zu remaining in UPDATE)",
+ length, STREAM_READABLE(peer->curr));
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
+ if (type == BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_INFO) {
+ stream_getc(peer->curr);
+ stream_get(&ipv6_sid, peer->curr, sizeof(ipv6_sid));
+ sid_flags = stream_getc(peer->curr);
+ endpoint_behavior = stream_getw(peer->curr);
+ stream_getc(peer->curr);
+
+ /* Log SRv6 Service Sub-TLV */
+ if (BGP_DEBUG(vpn, VPN_LEAK_LABEL)) {
+ inet_ntop(AF_INET6, &ipv6_sid, buf, sizeof(buf));
+ zlog_debug(
+ "%s: srv6-l3-srv sid %s, sid-flags 0x%02x, end-behaviour 0x%04x",
+ __func__, buf, sid_flags, endpoint_behavior);
+ }
+
+ /* Configure from Info */
+ if (attr->srv6_l3vpn) {
+ flog_err(EC_BGP_ATTRIBUTE_REPEATED,
+ "Prefix SID SRv6 L3VPN field repeated");
+ return bgp_attr_malformed(
+ args, BGP_NOTIFY_UPDATE_MAL_ATTR, args->total);
+ }
+ attr->srv6_l3vpn = XCALLOC(MTYPE_BGP_SRV6_L3VPN,
+ sizeof(struct bgp_attr_srv6_l3vpn));
+ sid_copy(&attr->srv6_l3vpn->sid, &ipv6_sid);
+ attr->srv6_l3vpn->sid_flags = sid_flags;
+ attr->srv6_l3vpn->endpoint_behavior = endpoint_behavior;
+ attr->srv6_l3vpn->loc_block_len = 0;
+ attr->srv6_l3vpn->loc_node_len = 0;
+ attr->srv6_l3vpn->func_len = 0;
+ attr->srv6_l3vpn->arg_len = 0;
+ attr->srv6_l3vpn->transposition_len = 0;
+ attr->srv6_l3vpn->transposition_offset = 0;
+
+ // Sub-Sub-TLV found
+ if (length > BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_INFO_LENGTH) {
+ err = bgp_attr_srv6_service_data(args);
+
+ if (err != BGP_ATTR_PARSE_PROCEED)
+ return err;
+ }
+
+ attr->srv6_l3vpn = srv6_l3vpn_intern(attr->srv6_l3vpn);
+ }
+
+ /* Placeholder code for unsupported type */
+ else {
+ if (bgp_debug_update(peer, NULL, NULL, 1))
+ zlog_debug(
+ "%s attr SRv6 Service Sub-TLV sub-type=%u is not supported, skipped",
+ peer->host, type);
+
+ stream_forward_getp(peer->curr, length);
+ }
+
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
/*
* Read an individual SID value returning how much data we have read
* Returns 0 if there was an error that needs to be passed up the stack
@@ -2548,7 +2726,6 @@ static bgp_attr_parse_ret_t bgp_attr_psid_sub(uint8_t type, uint16_t length,
uint32_t srgb_range;
int srgb_count;
uint8_t sid_type, sid_flags;
- uint16_t endpoint_behavior;
char buf[BUFSIZ];
if (type == BGP_PREFIX_SID_LABEL_INDEX) {
@@ -2703,45 +2880,20 @@ static bgp_attr_parse_ret_t bgp_attr_psid_sub(uint8_t type, uint16_t length,
/* Placeholder code for the SRv6 L3 Service type */
else if (type == BGP_PREFIX_SID_SRV6_L3_SERVICE) {
- if (STREAM_READABLE(peer->curr) < length
- || length != BGP_PREFIX_SID_SRV6_L3_SERVICE_LENGTH) {
- flog_err(EC_BGP_ATTR_LEN,
- "Prefix SID SRv6 L3-Service length is %hu instead of %u",
- length, BGP_PREFIX_SID_SRV6_L3_SERVICE_LENGTH);
+ if (STREAM_READABLE(peer->curr) < length) {
+ flog_err(
+ EC_BGP_ATTR_LEN,
+ "Prefix SID SRv6 L3-Service length is %hu, but only %zu bytes remain",
+ length, STREAM_READABLE(peer->curr));
return bgp_attr_malformed(args,
BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
args->total);
}
- /* Parse L3-SERVICE Sub-TLV */
- stream_getc(peer->curr); /* reserved */
- stream_get(&ipv6_sid, peer->curr,
- sizeof(ipv6_sid)); /* sid_value */
- sid_flags = stream_getc(peer->curr); /* sid_flags */
- endpoint_behavior = stream_getw(peer->curr); /* endpoint */
- stream_getc(peer->curr); /* reserved */
-
- /* Log L3-SERVICE Sub-TLV */
- if (BGP_DEBUG(vpn, VPN_LEAK_LABEL)) {
- inet_ntop(AF_INET6, &ipv6_sid, buf, sizeof(buf));
- zlog_debug(
- "%s: srv6-l3-srv sid %s, sid-flags 0x%02x, end-behaviour 0x%04x",
- __func__, buf, sid_flags, endpoint_behavior);
- }
+ /* ignore reserved */
+ stream_getc(peer->curr);
- /* Configure from Info */
- if (attr->srv6_l3vpn) {
- flog_err(EC_BGP_ATTRIBUTE_REPEATED,
- "Prefix SID SRv6 L3VPN field repeated");
- return bgp_attr_malformed(
- args, BGP_NOTIFY_UPDATE_MAL_ATTR, args->total);
- }
- attr->srv6_l3vpn = XCALLOC(MTYPE_BGP_SRV6_L3VPN,
- sizeof(struct bgp_attr_srv6_l3vpn));
- attr->srv6_l3vpn->sid_flags = sid_flags;
- attr->srv6_l3vpn->endpoint_behavior = endpoint_behavior;
- sid_copy(&attr->srv6_l3vpn->sid, &ipv6_sid);
- attr->srv6_l3vpn = srv6_l3vpn_intern(attr->srv6_l3vpn);
+ return bgp_attr_srv6_service(args);
}
/* Placeholder code for Unsupported TLV */
@@ -4123,18 +4275,39 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer,
/* SRv6 Service Information Attribute. */
if ((afi == AFI_IP || afi == AFI_IP6) && safi == SAFI_MPLS_VPN) {
if (attr->srv6_l3vpn) {
+ uint8_t subtlv_len =
+ BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_STRUCTURE_LENGTH
+ + BGP_ATTR_MIN_LEN
+ + BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_INFO_LENGTH;
+ uint8_t tlv_len = subtlv_len + BGP_ATTR_MIN_LEN + 1;
+ uint8_t attr_len = tlv_len + BGP_ATTR_MIN_LEN;
stream_putc(s, BGP_ATTR_FLAG_OPTIONAL
| BGP_ATTR_FLAG_TRANS);
stream_putc(s, BGP_ATTR_PREFIX_SID);
- stream_putc(s, 24); /* tlv len */
+ stream_putc(s, attr_len);
stream_putc(s, BGP_PREFIX_SID_SRV6_L3_SERVICE);
- stream_putw(s, 21); /* sub-tlv len */
+ stream_putw(s, tlv_len);
+ stream_putc(s, 0); /* reserved */
+ stream_putc(s, BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_INFO);
+ stream_putw(s, subtlv_len);
stream_putc(s, 0); /* reserved */
stream_put(s, &attr->srv6_l3vpn->sid,
sizeof(attr->srv6_l3vpn->sid)); /* sid */
stream_putc(s, 0); /* sid_flags */
stream_putw(s, 0xffff); /* endpoint */
stream_putc(s, 0); /* reserved */
+ stream_putc(
+ s,
+ BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_STRUCTURE);
+ stream_putw(
+ s,
+ BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_STRUCTURE_LENGTH);
+ stream_putc(s, attr->srv6_l3vpn->loc_block_len);
+ stream_putc(s, attr->srv6_l3vpn->loc_node_len);
+ stream_putc(s, attr->srv6_l3vpn->func_len);
+ stream_putc(s, attr->srv6_l3vpn->arg_len);
+ stream_putc(s, attr->srv6_l3vpn->transposition_len);
+ stream_putc(s, attr->srv6_l3vpn->transposition_offset);
} else if (attr->srv6_vpn) {
stream_putc(s, BGP_ATTR_FLAG_OPTIONAL
| BGP_ATTR_FLAG_TRANS);
diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h
index 6c49cf509f..3573c2ae03 100644
--- a/bgpd/bgp_attr.h
+++ b/bgpd/bgp_attr.h
@@ -71,7 +71,22 @@
#define BGP_PREFIX_SID_IPV6_LENGTH 19
#define BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH 6
#define BGP_PREFIX_SID_VPN_SID_LENGTH 19
-#define BGP_PREFIX_SID_SRV6_L3_SERVICE_LENGTH 21
+
+/* SRv6 Service Sub-TLV types */
+#define BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_INFO 1
+#define BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_INFO_LENGTH 21
+
+/* SRv6 Service Data Sub-Sub-TLV types */
+#define BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_STRUCTURE 1
+#define BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_STRUCTURE_LENGTH 6
+
+/* SRv6 SID Structure default values */
+#define BGP_PREFIX_SID_SRV6_LOCATOR_BLOCK_LENGTH 40
+#define BGP_PREFIX_SID_SRV6_LOCATOR_NODE_LENGTH 24
+#define BGP_PREFIX_SID_SRV6_FUNCTION_LENGTH 16
+#define BGP_PREFIX_SID_SRV6_ARGUMENT_LENGTH 0
+#define BGP_PREFIX_SID_SRV6_TRANSPOSITION_LENGTH 16
+#define BGP_PREFIX_SID_SRV6_TRANSPOSITION_OFFSET 64
#define BGP_ATTR_NH_AFI(afi, attr) \
((afi != AFI_L2VPN) ? afi : \
@@ -136,6 +151,12 @@ struct bgp_attr_srv6_l3vpn {
uint8_t sid_flags;
uint16_t endpoint_behavior;
struct in6_addr sid;
+ uint8_t loc_block_len;
+ uint8_t loc_node_len;
+ uint8_t func_len;
+ uint8_t arg_len;
+ uint8_t transposition_len;
+ uint8_t transposition_offset;
};
/* BGP core attribute structure. */
diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c
index b0bab02fb8..659029b04c 100644
--- a/bgpd/bgp_mplsvpn.c
+++ b/bgpd/bgp_mplsvpn.c
@@ -522,13 +522,14 @@ static bool sid_exist(struct bgp *bgp, const struct in6_addr *sid)
* if index != 0: try to allocate as index-mode
* else: try to allocate as auto-mode
*/
-static bool alloc_new_sid(struct bgp *bgp, uint32_t index,
- struct in6_addr *sid)
+static uint32_t alloc_new_sid(struct bgp *bgp, uint32_t index,
+ struct in6_addr *sid)
{
struct listnode *node;
struct prefix_ipv6 *chunk;
struct in6_addr sid_buf;
bool alloced = false;
+ int label;
if (!bgp || !sid)
return false;
@@ -536,7 +537,8 @@ static bool alloc_new_sid(struct bgp *bgp, uint32_t index,
for (ALL_LIST_ELEMENTS_RO(bgp->srv6_locator_chunks, node, chunk)) {
sid_buf = chunk->prefix;
if (index != 0) {
- sid_buf.s6_addr[15] = index;
+ label = index << 12;
+ transpose_sid(&sid_buf, label, 64, 16);
if (sid_exist(bgp, &sid_buf))
return false;
alloced = true;
@@ -544,9 +546,8 @@ static bool alloc_new_sid(struct bgp *bgp, uint32_t index,
}
for (size_t i = 1; i < 255; i++) {
- sid_buf.s6_addr[15] = (i & 0xff00) >> 8;
- sid_buf.s6_addr[14] = (i & 0x00ff);
-
+ label = i << 12;
+ transpose_sid(&sid_buf, label, 64, 16);
if (sid_exist(bgp, &sid_buf))
continue;
alloced = true;
@@ -555,20 +556,19 @@ static bool alloc_new_sid(struct bgp *bgp, uint32_t index,
}
if (!alloced)
- return false;
+ return 0;
sid_register(bgp, &sid_buf, bgp->srv6_locator_name);
*sid = sid_buf;
- return true;
+ return label;
}
void ensure_vrf_tovpn_sid(struct bgp *bgp_vpn, struct bgp *bgp_vrf, afi_t afi)
{
int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF);
- bool alloced = false;
char buf[256];
struct in6_addr *sid;
- uint32_t tovpn_sid_index = 0;
+ uint32_t tovpn_sid_index = 0, tovpn_sid_transpose_label;
bool tovpn_sid_auto = false;
if (debug)
@@ -602,8 +602,9 @@ void ensure_vrf_tovpn_sid(struct bgp *bgp_vpn, struct bgp *bgp_vrf, afi_t afi)
}
sid = XCALLOC(MTYPE_BGP_SRV6_SID, sizeof(struct in6_addr));
- alloced = alloc_new_sid(bgp_vpn, tovpn_sid_index, sid);
- if (!alloced) {
+ tovpn_sid_transpose_label =
+ alloc_new_sid(bgp_vpn, tovpn_sid_index, sid);
+ if (tovpn_sid_transpose_label == 0) {
zlog_debug("%s: not allocated new sid for vrf %s: afi %s",
__func__, bgp_vrf->name_pretty, afi2str(afi));
return;
@@ -615,9 +616,22 @@ void ensure_vrf_tovpn_sid(struct bgp *bgp_vpn, struct bgp *bgp_vrf, afi_t afi)
__func__, buf, bgp_vrf->name_pretty,
afi2str(afi));
}
+ bgp_vrf->vpn_policy[afi].tovpn_sid_transpose_label =
+ tovpn_sid_transpose_label;
bgp_vrf->vpn_policy[afi].tovpn_sid = sid;
}
+void transpose_sid(struct in6_addr *sid, uint32_t label, uint8_t offset,
+ uint8_t len)
+{
+ for (uint8_t idx = 0; idx < len; idx++) {
+ uint8_t tidx = offset + idx;
+ sid->s6_addr[tidx / 8] &= ~(0x1 << (7 - tidx % 8));
+ if (label >> (19 - idx) & 0x1)
+ sid->s6_addr[tidx / 8] |= 0x1 << (7 - tidx % 8);
+ }
+}
+
static bool ecom_intersect(struct ecommunity *e1, struct ecommunity *e2)
{
uint32_t i, j;
@@ -710,7 +724,7 @@ static void setsids(struct bgp_path_info *bpi,
extra = bgp_path_info_extra_get(bpi);
for (i = 0; i < num_sids; i++)
- memcpy(&extra->sid[i], &sid[i], sizeof(struct in6_addr));
+ memcpy(&extra->sid[i].sid, &sid[i], sizeof(struct in6_addr));
extra->num_sids = num_sids;
}
@@ -738,6 +752,7 @@ leak_update(struct bgp *bgp, /* destination bgp instance */
struct bgp_path_info *bpi;
struct bgp_path_info *bpi_ultimate;
struct bgp_path_info *new;
+ struct bgp_path_info_extra *extra;
uint32_t num_sids = 0;
if (new_attr->srv6_l3vpn || new_attr->srv6_vpn)
@@ -824,10 +839,31 @@ leak_update(struct bgp *bgp, /* destination bgp instance */
* rewrite sid
*/
if (num_sids) {
- if (new_attr->srv6_l3vpn)
+ if (new_attr->srv6_l3vpn) {
setsids(bpi, &new_attr->srv6_l3vpn->sid,
num_sids);
- else if (new_attr->srv6_vpn)
+
+ extra = bgp_path_info_extra_get(bpi);
+
+ extra->sid[0].loc_block_len =
+ new_attr->srv6_l3vpn->loc_block_len;
+ extra->sid[0].loc_node_len =
+ new_attr->srv6_l3vpn->loc_node_len;
+ extra->sid[0].func_len =
+ new_attr->srv6_l3vpn->func_len;
+ extra->sid[0].arg_len =
+ new_attr->srv6_l3vpn->arg_len;
+
+ if (new_attr->srv6_l3vpn->transposition_len
+ != 0)
+ transpose_sid(
+ &extra->sid[0].sid,
+ decode_label(label),
+ new_attr->srv6_l3vpn
+ ->transposition_offset,
+ new_attr->srv6_l3vpn
+ ->transposition_len);
+ } else if (new_attr->srv6_vpn)
setsids(bpi, &new_attr->srv6_vpn->sid,
num_sids);
} else
@@ -910,9 +946,26 @@ leak_update(struct bgp *bgp, /* destination bgp instance */
* rewrite sid
*/
if (num_sids) {
- if (new_attr->srv6_l3vpn)
+ if (new_attr->srv6_l3vpn) {
setsids(new, &new_attr->srv6_l3vpn->sid, num_sids);
- else if (new_attr->srv6_vpn)
+
+ extra = bgp_path_info_extra_get(new);
+
+ extra->sid[0].loc_block_len =
+ new_attr->srv6_l3vpn->loc_block_len;
+ extra->sid[0].loc_node_len =
+ new_attr->srv6_l3vpn->loc_node_len;
+ extra->sid[0].func_len = new_attr->srv6_l3vpn->func_len;
+ extra->sid[0].arg_len = new_attr->srv6_l3vpn->arg_len;
+
+ if (new_attr->srv6_l3vpn->transposition_len != 0)
+ transpose_sid(&extra->sid[0].sid,
+ decode_label(label),
+ new_attr->srv6_l3vpn
+ ->transposition_offset,
+ new_attr->srv6_l3vpn
+ ->transposition_len);
+ } else if (new_attr->srv6_vpn)
setsids(new, &new_attr->srv6_vpn->sid, num_sids);
} else
unsetsids(new);
@@ -1186,10 +1239,24 @@ void vpn_leak_from_vrf_update(struct bgp *bgp_vpn, /* to */
/* Set SID for SRv6 VPN */
if (bgp_vrf->vpn_policy[afi].tovpn_sid) {
+ encode_label(bgp_vrf->vpn_policy[afi].tovpn_sid_transpose_label,
+ &label);
static_attr.srv6_l3vpn = XCALLOC(MTYPE_BGP_SRV6_L3VPN,
sizeof(struct bgp_attr_srv6_l3vpn));
static_attr.srv6_l3vpn->sid_flags = 0x00;
static_attr.srv6_l3vpn->endpoint_behavior = 0xffff;
+ static_attr.srv6_l3vpn->loc_block_len =
+ BGP_PREFIX_SID_SRV6_LOCATOR_BLOCK_LENGTH;
+ static_attr.srv6_l3vpn->loc_node_len =
+ BGP_PREFIX_SID_SRV6_LOCATOR_NODE_LENGTH;
+ static_attr.srv6_l3vpn->func_len =
+ BGP_PREFIX_SID_SRV6_FUNCTION_LENGTH;
+ static_attr.srv6_l3vpn->arg_len =
+ BGP_PREFIX_SID_SRV6_ARGUMENT_LENGTH;
+ static_attr.srv6_l3vpn->transposition_len =
+ BGP_PREFIX_SID_SRV6_TRANSPOSITION_LENGTH;
+ static_attr.srv6_l3vpn->transposition_offset =
+ BGP_PREFIX_SID_SRV6_TRANSPOSITION_OFFSET;
memcpy(&static_attr.srv6_l3vpn->sid,
bgp_vrf->vpn_policy[afi].tovpn_sid,
sizeof(static_attr.srv6_l3vpn->sid));
diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h
index b0e0f74f77..b0d586223f 100644
--- a/bgpd/bgp_mplsvpn.h
+++ b/bgpd/bgp_mplsvpn.h
@@ -81,6 +81,8 @@ extern void vpn_leak_zebra_vrf_sid_update(struct bgp *bgp, afi_t afi);
extern void vpn_leak_zebra_vrf_sid_withdraw(struct bgp *bgp, afi_t afi);
extern int vpn_leak_label_callback(mpls_label_t label, void *lblid, bool alloc);
extern void ensure_vrf_tovpn_sid(struct bgp *vpn, struct bgp *vrf, afi_t afi);
+extern void transpose_sid(struct in6_addr *sid, uint32_t label, uint8_t offset,
+ uint8_t size);
extern void vrf_import_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp,
afi_t afi, safi_t safi);
void vrf_unimport_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp,
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 5a16fecc26..44629b3d7c 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -4042,15 +4042,48 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
/* Update SRv6 SID */
if (attr->srv6_l3vpn) {
extra = bgp_path_info_extra_get(pi);
- if (sid_diff(&extra->sid[0], &attr->srv6_l3vpn->sid)) {
- sid_copy(&extra->sid[0],
+ if (sid_diff(&extra->sid[0].sid,
+ &attr->srv6_l3vpn->sid)) {
+ sid_copy(&extra->sid[0].sid,
&attr->srv6_l3vpn->sid);
extra->num_sids = 1;
+
+ extra->sid[0].loc_block_len = 0;
+ extra->sid[0].loc_node_len = 0;
+ extra->sid[0].func_len = 0;
+ extra->sid[0].arg_len = 0;
+
+ if (attr->srv6_l3vpn->loc_block_len != 0) {
+ extra->sid[0].loc_block_len =
+ attr->srv6_l3vpn->loc_block_len;
+ extra->sid[0].loc_node_len =
+ attr->srv6_l3vpn->loc_node_len;
+ extra->sid[0].func_len =
+ attr->srv6_l3vpn->func_len;
+ extra->sid[0].arg_len =
+ attr->srv6_l3vpn->arg_len;
+ }
+
+ /*
+ * draft-ietf-bess-srv6-services-07
+ * The part of SRv6 SID may be encoded as MPLS
+ * Label for the efficient packing.
+ */
+ if (attr->srv6_l3vpn->transposition_len != 0)
+ transpose_sid(
+ &extra->sid[0].sid,
+ decode_label(label),
+ attr->srv6_l3vpn
+ ->transposition_offset,
+ attr->srv6_l3vpn
+ ->transposition_len);
}
} else if (attr->srv6_vpn) {
extra = bgp_path_info_extra_get(pi);
- if (sid_diff(&extra->sid[0], &attr->srv6_vpn->sid)) {
- sid_copy(&extra->sid[0], &attr->srv6_vpn->sid);
+ if (sid_diff(&extra->sid[0].sid,
+ &attr->srv6_vpn->sid)) {
+ sid_copy(&extra->sid[0].sid,
+ &attr->srv6_vpn->sid);
extra->num_sids = 1;
}
}
@@ -4231,10 +4264,28 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
if (safi == SAFI_MPLS_VPN) {
extra = bgp_path_info_extra_get(new);
if (attr->srv6_l3vpn) {
- sid_copy(&extra->sid[0], &attr->srv6_l3vpn->sid);
+ sid_copy(&extra->sid[0].sid, &attr->srv6_l3vpn->sid);
extra->num_sids = 1;
+
+ extra->sid[0].loc_block_len =
+ attr->srv6_l3vpn->loc_block_len;
+ extra->sid[0].loc_node_len =
+ attr->srv6_l3vpn->loc_node_len;
+ extra->sid[0].func_len = attr->srv6_l3vpn->func_len;
+ extra->sid[0].arg_len = attr->srv6_l3vpn->arg_len;
+
+ /*
+ * draft-ietf-bess-srv6-services-07
+ * The part of SRv6 SID may be encoded as MPLS Label for
+ * the efficient packing.
+ */
+ if (attr->srv6_l3vpn->transposition_len != 0)
+ transpose_sid(
+ &extra->sid[0].sid, decode_label(label),
+ attr->srv6_l3vpn->transposition_offset,
+ attr->srv6_l3vpn->transposition_len);
} else if (attr->srv6_vpn) {
- sid_copy(&extra->sid[0], &attr->srv6_vpn->sid);
+ sid_copy(&extra->sid[0].sid, &attr->srv6_vpn->sid);
extra->num_sids = 1;
}
}
@@ -10450,7 +10501,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn,
/* Remote SID */
if (path->extra && path->extra->num_sids > 0 && safi != SAFI_EVPN) {
- inet_ntop(AF_INET6, &path->extra->sid, buf, sizeof(buf));
+ inet_ntop(AF_INET6, &path->extra->sid[0].sid, buf, sizeof(buf));
if (json_paths)
json_object_string_add(json_path, "remoteSid", buf);
else
@@ -11121,6 +11172,10 @@ static int bgp_show(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,
return CMD_WARNING;
}
+ /* Labeled-unicast routes live in the unicast table. */
+ if (safi == SAFI_LABELED_UNICAST)
+ safi = SAFI_UNICAST;
+
table = bgp->rib[afi][safi];
/* use MPLS and ENCAP specific shows until they are merged */
if (safi == SAFI_MPLS_VPN) {
@@ -11133,9 +11188,6 @@ static int bgp_show(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,
output_arg, use_json,
1, NULL, NULL);
}
- /* labeled-unicast routes live in the unicast table */
- else if (safi == SAFI_LABELED_UNICAST)
- safi = SAFI_UNICAST;
return bgp_show_table(vty, bgp, safi, table, type, output_arg, NULL, 1,
NULL, NULL, &json_header_depth, show_flags,
diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h
index d052a3f408..7609f7196d 100644
--- a/bgpd/bgp_route.h
+++ b/bgpd/bgp_route.h
@@ -145,6 +145,14 @@ struct bgp_path_mh_info {
struct bgp_path_evpn_nh_info *nh_info;
};
+struct bgp_sid_info {
+ struct in6_addr sid;
+ uint8_t loc_block_len;
+ uint8_t loc_node_len;
+ uint8_t func_len;
+ uint8_t arg_len;
+};
+
/* Ancillary information to struct bgp_path_info,
* used for uncommonly used data (aggregation, MPLS, etc.)
* and lazily allocated to save memory.
@@ -168,7 +176,7 @@ struct bgp_path_info_extra {
#define BGP_EVPN_MACIP_TYPE_SVI_IP (1 << 0)
/* SRv6 SID(s) for SRv6-VPN */
- struct in6_addr sid[BGP_MAX_SIDS];
+ struct bgp_sid_info sid[BGP_MAX_SIDS];
uint32_t num_sids;
#ifdef ENABLE_BGP_VNC
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
index b367b393d9..dc48629e1d 100644
--- a/bgpd/bgp_zebra.c
+++ b/bgpd/bgp_zebra.c
@@ -1219,7 +1219,6 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p,
struct zapi_nexthop *api_nh;
int nh_family;
unsigned int valid_nh_count = 0;
- int has_valid_label = 0;
bool allow_recursion = false;
int has_valid_sid = 0;
uint8_t distance;
@@ -1434,7 +1433,6 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p,
if (mpinfo->extra
&& bgp_is_valid_label(&mpinfo->extra->label[0])
&& !CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE)) {
- has_valid_label = 1;
label = label_pton(&mpinfo->extra->label[0]);
SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL);
@@ -1451,11 +1449,10 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p,
api_nh->weight = nh_weight;
- if (mpinfo->extra
- && !sid_zero(&mpinfo->extra->sid[0])
+ if (mpinfo->extra && !sid_zero(&mpinfo->extra->sid[0].sid)
&& !CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE)) {
has_valid_sid = 1;
- memcpy(&api_nh->seg6_segs, &mpinfo->extra->sid[0],
+ memcpy(&api_nh->seg6_segs, &mpinfo->extra->sid[0].sid,
sizeof(api_nh->seg6_segs));
}
@@ -1561,7 +1558,7 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p,
label_buf[0] = '\0';
eth_buf[0] = '\0';
segs_buf[0] = '\0';
- if (has_valid_label
+ if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL)
&& !CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE))
snprintf(label_buf, sizeof(label_buf),
"label %u", api_nh->labels[0]);
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 13b530a613..5e1eacbb9e 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -241,6 +241,7 @@ struct vpn_policy {
*/
uint32_t tovpn_sid_index; /* unset => set to 0 */
struct in6_addr *tovpn_sid;
+ uint32_t tovpn_sid_transpose_label;
struct in6_addr *tovpn_zebra_vrf_sid_last_sent;
};
diff --git a/bgpd/rfapi/rfapi_vty.c b/bgpd/rfapi/rfapi_vty.c
index 45ef7230b5..6762c2b4a2 100644
--- a/bgpd/rfapi/rfapi_vty.c
+++ b/bgpd/rfapi/rfapi_vty.c
@@ -435,8 +435,16 @@ void rfapi_vty_out_vncinfo(struct vty *vty, const struct prefix *p,
char buf[BUFSIZ];
vty_out(vty, " sid=%s",
- inet_ntop(AF_INET6, &bpi->extra->sid[0], buf,
- sizeof(buf)));
+ inet_ntop(AF_INET6, &bpi->extra->sid[0].sid,
+ buf, sizeof(buf)));
+
+ if (bpi->extra->sid[0].loc_block_len != 0) {
+ vty_out(vty, " sid_structure=[%d,%d,%d,%d]",
+ bpi->extra->sid[0].loc_block_len,
+ bpi->extra->sid[0].loc_node_len,
+ bpi->extra->sid[0].func_len,
+ bpi->extra->sid[0].arg_len);
+ }
}
}
diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst
index ede2144107..f0b0638eeb 100644
--- a/doc/user/ospf6d.rst
+++ b/doc/user/ospf6d.rst
@@ -198,6 +198,47 @@ OSPF6 area
advertisement of summaries into the area. In that case, a single Type-3 LSA
containing a default route is originated into the NSSA.
+.. clicmd:: area A.B.C.D export-list NAME
+
+.. clicmd:: area (0-4294967295) export-list NAME
+
+ Filter Type-3 summary-LSAs announced to other areas originated from intra-
+ area paths from specified area.
+
+ .. code-block:: frr
+
+ router ospf6
+ area 0.0.0.10 export-list foo
+ !
+ ipv6 access-list foo permit 2001:db8:1000::/64
+ ipv6 access-list foo deny any
+
+ With example above any intra-area paths from area 0.0.0.10 and from range
+ 2001:db8::/32 (for example 2001:db8:1::/64 and 2001:db8:2::/64) are announced
+ into other areas as Type-3 summary-LSA's, but any others (for example
+ 2001:200::/48) aren't.
+
+ This command is only relevant if the router is an ABR for the specified
+ area.
+
+.. clicmd:: area A.B.C.D import-list NAME
+
+.. clicmd:: area (0-4294967295) import-list NAME
+
+ Same as export-list, but it applies to paths announced into specified area
+ as Type-3 summary-LSAs.
+
+.. clicmd:: area A.B.C.D filter-list prefix NAME in
+
+.. clicmd:: area A.B.C.D filter-list prefix NAME out
+
+.. clicmd:: area (0-4294967295) filter-list prefix NAME in
+
+.. clicmd:: area (0-4294967295) filter-list prefix NAME out
+
+ Filtering Type-3 summary-LSAs to/from area using prefix lists. This command
+ makes sense in ABR only.
+
.. _ospf6-interface:
OSPF6 interface
diff --git a/lib/prefix.h b/lib/prefix.h
index 944c94f57f..c92f5cec5a 100644
--- a/lib/prefix.h
+++ b/lib/prefix.h
@@ -512,7 +512,7 @@ extern char *esi_to_str(const esi_t *esi, char *buf, int size);
extern char *evpn_es_df_alg2str(uint8_t df_alg, char *buf, int buf_len);
extern void prefix_evpn_hexdump(const struct prefix_evpn *p);
-static inline int ipv6_martian(struct in6_addr *addr)
+static inline int ipv6_martian(const struct in6_addr *addr)
{
struct in6_addr localhost_addr;
@@ -527,7 +527,7 @@ static inline int ipv6_martian(struct in6_addr *addr)
extern int macstr2prefix_evpn(const char *str, struct prefix_evpn *p);
/* NOTE: This routine expects the address argument in network byte order. */
-static inline int ipv4_martian(struct in_addr *addr)
+static inline int ipv4_martian(const struct in_addr *addr)
{
in_addr_t ip = ntohl(addr->s_addr);
diff --git a/lib/skiplist.c b/lib/skiplist.c
index fc42857418..c5219f7381 100644
--- a/lib/skiplist.c
+++ b/lib/skiplist.c
@@ -65,17 +65,25 @@
DEFINE_MTYPE_STATIC(LIB, SKIP_LIST, "Skip List");
DEFINE_MTYPE_STATIC(LIB, SKIP_LIST_NODE, "Skip Node");
+DEFINE_MTYPE_STATIC(LIB, SKIP_LIST_STATS, "Skiplist Counters");
#define BitsInRandom 31
#define MaxNumberOfLevels 16
#define MaxLevel (MaxNumberOfLevels-1)
-#define newNodeOfLevel(l) XCALLOC(MTYPE_SKIP_LIST_NODE, sizeof(struct skiplistnode)+(l)*sizeof(struct skiplistnode *))
+#define newNodeOfLevel(l) \
+ XCALLOC(MTYPE_SKIP_LIST_NODE, \
+ sizeof(struct skiplistnode) \
+ + (l) * sizeof(struct skiplistnode *))
+
+/* XXX must match type of (struct skiplist).level_stats */
+#define newStatsOfLevel(l) \
+ XCALLOC(MTYPE_SKIP_LIST_STATS, ((l) + 1) * sizeof(int))
static int randomsLeft;
static int randomBits;
-#if 1
+#ifdef SKIPLIST_DEBUG
#define CHECKLAST(sl) \
do { \
if ((sl)->header->forward[0] && !(sl)->last) \
@@ -138,7 +146,7 @@ struct skiplist *skiplist_new(int flags,
new->level = 0;
new->count = 0;
new->header = newNodeOfLevel(MaxNumberOfLevels);
- new->stats = newNodeOfLevel(MaxNumberOfLevels);
+ new->level_stats = newStatsOfLevel(MaxNumberOfLevels);
new->flags = flags;
if (cmp)
@@ -166,7 +174,7 @@ void skiplist_free(struct skiplist *l)
p = q;
} while (p);
- XFREE(MTYPE_SKIP_LIST_NODE, l->stats);
+ XFREE(MTYPE_SKIP_LIST_STATS, l->level_stats);
XFREE(MTYPE_SKIP_LIST, l);
}
@@ -180,11 +188,13 @@ int skiplist_insert(register struct skiplist *l, register void *key,
CHECKLAST(l);
+#ifdef SKIPLIST_DEBUG
/* DEBUG */
if (!key) {
flog_err(EC_LIB_DEVELOPMENT, "%s: key is 0, value is %p",
__func__, value);
}
+#endif
p = l->header;
k = l->level;
@@ -214,10 +224,10 @@ int skiplist_insert(register struct skiplist *l, register void *key,
q->flags = SKIPLIST_NODE_FLAG_INSERTED; /* debug */
#endif
- ++(l->stats->forward[k]);
+ ++(l->level_stats[k]);
#ifdef SKIPLIST_DEBUG
- zlog_debug("%s: incremented stats @%p:%d, now %ld", __func__, l, k,
- l->stats->forward[k] - (struct skiplistnode *)NULL);
+ zlog_debug("%s: incremented level_stats @%p:%d, now %d", __func__, l, k,
+ l->level_stats[k]);
#endif
do {
@@ -298,12 +308,10 @@ int skiplist_delete(register struct skiplist *l, register void *key,
k++) {
p->forward[k] = q->forward[k];
}
- --(l->stats->forward[k - 1]);
+ --(l->level_stats[k - 1]);
#ifdef SKIPLIST_DEBUG
- zlog_debug("%s: decremented stats @%p:%d, now %ld",
- __func__, l, k - 1,
- l->stats->forward[k - 1]
- - (struct skiplistnode *)NULL);
+ zlog_debug("%s: decremented level_stats @%p:%d, now %d",
+ __func__, l, k - 1, l->level_stats[k - 1]);
#endif
if (l->del)
(*l->del)(q->value);
@@ -559,11 +567,10 @@ int skiplist_delete_first(register struct skiplist *l)
l->last = NULL;
}
- --(l->stats->forward[nodelevel]);
+ --(l->level_stats[nodelevel]);
#ifdef SKIPLIST_DEBUG
- zlog_debug("%s: decremented stats @%p:%d, now %ld", __func__, l,
- nodelevel,
- l->stats->forward[nodelevel] - (struct skiplistnode *)NULL);
+ zlog_debug("%s: decremented level_stats @%p:%d, now %d", __func__, l,
+ nodelevel, l->level_stats[nodelevel]);
#endif
if (l->del)
@@ -587,9 +594,7 @@ void skiplist_debug(struct vty *vty, struct skiplist *l)
vty_out(vty, "Skiplist %p has max level %d\n", l, l->level);
for (i = l->level; i >= 0; --i)
- vty_out(vty, " @%d: %ld\n", i,
- (long)((l->stats->forward[i])
- - (struct skiplistnode *)NULL));
+ vty_out(vty, " @%d: %d\n", i, l->level_stats[i]);
}
static void *scramble(int i)
diff --git a/lib/skiplist.h b/lib/skiplist.h
index a106a455d6..00950e13bb 100644
--- a/lib/skiplist.h
+++ b/lib/skiplist.h
@@ -60,7 +60,7 @@ struct skiplist {
int level; /* max lvl (1 + current # of levels in list) */
unsigned int count;
struct skiplistnode *header;
- struct skiplistnode *stats;
+ int *level_stats;
struct skiplistnode
*last; /* last real list item (NULL if empty list) */
@@ -123,6 +123,7 @@ extern int skiplist_empty(register struct skiplist *l); /* in */
extern unsigned int skiplist_count(register struct skiplist *l); /* in */
+struct vty;
extern void skiplist_debug(struct vty *vty, struct skiplist *l);
extern void skiplist_test(struct vty *vty);
diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c
index fe1845907f..57165201bd 100644
--- a/ospf6d/ospf6_abr.c
+++ b/ospf6d/ospf6_abr.c
@@ -231,6 +231,69 @@ int ospf6_abr_originate_summary_to_area(struct ospf6_route *route,
return 0;
}
+ if (route->type == OSPF6_DEST_TYPE_NETWORK) {
+ bool filter = false;
+
+ route_area =
+ ospf6_area_lookup(route->path.area_id, area->ospf6);
+ assert(route_area);
+
+ /* Check export-list */
+ if (EXPORT_LIST(route_area)
+ && access_list_apply(EXPORT_LIST(route_area),
+ &route->prefix)
+ == FILTER_DENY) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug(
+ "%s: prefix %pFX was denied by export-list",
+ __func__, &route->prefix);
+ filter = true;
+ }
+
+ /* Check output prefix-list */
+ if (PREFIX_LIST_OUT(route_area)
+ && prefix_list_apply(PREFIX_LIST_OUT(route_area),
+ &route->prefix)
+ != PREFIX_PERMIT) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug(
+ "%s: prefix %pFX was denied by prefix-list out",
+ __func__, &route->prefix);
+ filter = true;
+ }
+
+ /* Check import-list */
+ if (IMPORT_LIST(area)
+ && access_list_apply(IMPORT_LIST(area), &route->prefix)
+ == FILTER_DENY) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug(
+ "%s: prefix %pFX was denied by import-list",
+ __func__, &route->prefix);
+ filter = true;
+ }
+
+ /* Check input prefix-list */
+ if (PREFIX_LIST_IN(area)
+ && prefix_list_apply(PREFIX_LIST_IN(area), &route->prefix)
+ != PREFIX_PERMIT) {
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug(
+ "%s: prefix %pFX was denied by prefix-list in",
+ __func__, &route->prefix);
+ filter = true;
+ }
+
+ if (filter) {
+ if (summary) {
+ ospf6_route_remove(summary, summary_table);
+ if (old)
+ ospf6_lsa_purge(old);
+ }
+ return 0;
+ }
+ }
+
/* do not generate if the nexthops belongs to the target area */
if (ospf6_abr_nexthops_belong_to_area(route, area)) {
if (IS_OSPF6_DEBUG_ABR)
@@ -430,39 +493,6 @@ int ospf6_abr_originate_summary_to_area(struct ospf6_route *route,
}
}
- /* Check export list */
- if (EXPORT_NAME(area)) {
- if (EXPORT_LIST(area) == NULL)
- EXPORT_LIST(area) =
- access_list_lookup(AFI_IP6, EXPORT_NAME(area));
-
- if (EXPORT_LIST(area))
- if (access_list_apply(EXPORT_LIST(area), &route->prefix)
- == FILTER_DENY) {
- if (is_debug)
- zlog_debug(
- "prefix %pFX was denied by export list",
- &route->prefix);
- ospf6_abr_delete_route(route, summary,
- summary_table, old);
- return 0;
- }
- }
-
- /* Check filter-list */
- if (PREFIX_LIST_OUT(area))
- if (prefix_list_apply(PREFIX_LIST_OUT(area), &route->prefix)
- != PREFIX_PERMIT) {
- if (is_debug)
- zlog_debug(
- "prefix %pFX was denied by filter-list out",
- &route->prefix);
- ospf6_abr_delete_route(route, summary, summary_table,
- old);
-
- return 0;
- }
-
/* the route is going to be originated. store it in area's summary_table
*/
if (summary == NULL) {
@@ -1134,39 +1164,6 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa)
return;
}
- /* Check import list */
- if (IMPORT_NAME(oa)) {
- if (IMPORT_LIST(oa) == NULL)
- IMPORT_LIST(oa) =
- access_list_lookup(AFI_IP6, IMPORT_NAME(oa));
-
- if (IMPORT_LIST(oa))
- if (access_list_apply(IMPORT_LIST(oa), &prefix)
- == FILTER_DENY) {
- if (is_debug)
- zlog_debug(
- "Prefix %pFX was denied by import-list",
- &prefix);
- if (old)
- ospf6_route_remove(old, table);
- return;
- }
- }
-
- /* Check input prefix-list */
- if (PREFIX_LIST_IN(oa)) {
- if (prefix_list_apply(PREFIX_LIST_IN(oa), &prefix)
- != PREFIX_PERMIT) {
- if (is_debug)
- zlog_debug(
- "Prefix %pFX was denied by prefix-list in",
- &prefix);
- if (old)
- ospf6_route_remove(old, table);
- return;
- }
- }
-
/* (5),(6): the path preference is handled by the sorting
in the routing table. Always install the path by substituting
old route (if any). */
@@ -1355,35 +1352,6 @@ void ospf6_abr_examin_brouter(uint32_t router_id, struct ospf6_route *route,
ospf6_abr_examin_summary(lsa, oa);
}
-void ospf6_abr_reimport(struct ospf6_area *oa)
-{
- struct ospf6_lsa *lsa;
- uint16_t type;
-
- type = htons(OSPF6_LSTYPE_INTER_ROUTER);
- for (ALL_LSDB_TYPED(oa->lsdb, type, lsa))
- ospf6_abr_examin_summary(lsa, oa);
-
- type = htons(OSPF6_LSTYPE_INTER_PREFIX);
- for (ALL_LSDB_TYPED(oa->lsdb, type, lsa))
- ospf6_abr_examin_summary(lsa, oa);
-}
-
-/* export filter removed so determine if we should reoriginate summary LSAs */
-void ospf6_abr_reexport(struct ospf6_area *oa)
-{
- struct ospf6_route *route;
-
- /* if not a ABR return success */
- if (!ospf6_check_and_set_router_abr(oa->ospf6))
- return;
-
- /* Redo summaries if required */
- for (route = ospf6_route_head(oa->ospf6->route_table); route;
- route = ospf6_route_next(route))
- ospf6_abr_originate_summary_to_area(route, oa);
-}
-
void ospf6_abr_prefix_resummarize(struct ospf6 *o)
{
struct ospf6_route *route;
diff --git a/ospf6d/ospf6_abr.h b/ospf6d/ospf6_abr.h
index a5f0f124b9..08521ecb0f 100644
--- a/ospf6d/ospf6_abr.h
+++ b/ospf6d/ospf6_abr.h
@@ -73,8 +73,6 @@ extern void ospf6_abr_defaults_to_stub(struct ospf6 *ospf6);
extern void ospf6_abr_examin_brouter(uint32_t router_id,
struct ospf6_route *route,
struct ospf6 *ospf6);
-extern void ospf6_abr_reimport(struct ospf6_area *oa);
-extern void ospf6_abr_reexport(struct ospf6_area *oa);
extern void ospf6_abr_range_reset_cost(struct ospf6 *ospf6);
extern void ospf6_abr_prefix_resummarize(struct ospf6 *ospf6);
@@ -88,7 +86,6 @@ extern void ospf6_abr_old_path_update(struct ospf6_route *old_route,
struct ospf6_route *route,
struct ospf6_route_table *table);
extern void ospf6_abr_init(void);
-extern void ospf6_abr_reexport(struct ospf6_area *oa);
extern void ospf6_abr_range_update(struct ospf6_route *range,
struct ospf6 *ospf6);
extern void ospf6_abr_remove_unapproved_summaries(struct ospf6 *ospf6);
diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c
index 7423a1a9e9..2f5328c662 100644
--- a/ospf6d/ospf6_area.c
+++ b/ospf6d/ospf6_area.c
@@ -696,17 +696,17 @@ DEFUN (area_filter_list,
XFREE(MTYPE_OSPF6_PLISTNAME, PREFIX_NAME_IN(area));
PREFIX_NAME_IN(area) =
XSTRDUP(MTYPE_OSPF6_PLISTNAME, plistname);
- ospf6_abr_reimport(area);
} else {
PREFIX_LIST_OUT(area) = plist;
XFREE(MTYPE_OSPF6_PLISTNAME, PREFIX_NAME_OUT(area));
PREFIX_NAME_OUT(area) =
XSTRDUP(MTYPE_OSPF6_PLISTNAME, plistname);
-
- /* Redo summaries if required */
- ospf6_abr_reexport(area);
}
+ /* Redo summaries if required */
+ if (ospf6_check_and_set_router_abr(area->ospf6))
+ ospf6_schedule_abr_task(ospf6);
+
return CMD_SUCCESS;
}
@@ -739,7 +739,6 @@ DEFUN (no_area_filter_list,
PREFIX_LIST_IN(area) = NULL;
XFREE(MTYPE_OSPF6_PLISTNAME, PREFIX_NAME_IN(area));
- ospf6_abr_reimport(area);
} else {
if (PREFIX_NAME_OUT(area))
if (!strmatch(PREFIX_NAME_OUT(area), plistname))
@@ -747,9 +746,12 @@ DEFUN (no_area_filter_list,
XFREE(MTYPE_OSPF6_PLISTNAME, PREFIX_NAME_OUT(area));
PREFIX_LIST_OUT(area) = NULL;
- ospf6_abr_reexport(area);
}
+ /* Redo summaries if required */
+ if (ospf6_check_and_set_router_abr(area->ospf6))
+ ospf6_schedule_abr_task(ospf6);
+
return CMD_SUCCESS;
}
@@ -760,19 +762,30 @@ void ospf6_filter_update(struct access_list *access)
struct ospf6 *ospf6;
for (ALL_LIST_ELEMENTS(om6->ospf6, node, nnode, ospf6)) {
+ bool update = false;
+
for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, n, oa)) {
if (IMPORT_NAME(oa)
- && strcmp(IMPORT_NAME(oa), access->name) == 0)
- ospf6_abr_reimport(oa);
+ && strcmp(IMPORT_NAME(oa), access->name) == 0) {
+ IMPORT_LIST(oa) = access_list_lookup(
+ AFI_IP6, IMPORT_NAME(oa));
+ update = true;
+ }
if (EXPORT_NAME(oa)
- && strcmp(EXPORT_NAME(oa), access->name) == 0)
- ospf6_abr_reexport(oa);
+ && strcmp(EXPORT_NAME(oa), access->name) == 0) {
+ EXPORT_LIST(oa) = access_list_lookup(
+ AFI_IP6, EXPORT_NAME(oa));
+ update = true;
+ }
}
+
+ if (update && ospf6_check_and_set_router_abr(ospf6))
+ ospf6_schedule_abr_task(ospf6);
}
}
-void ospf6_area_plist_update(struct prefix_list *plist, int add)
+void ospf6_plist_update(struct prefix_list *plist)
{
struct listnode *node, *nnode;
struct ospf6_area *oa;
@@ -780,19 +793,29 @@ void ospf6_area_plist_update(struct prefix_list *plist, int add)
const char *name = prefix_list_name(plist);
struct ospf6 *ospf6 = NULL;
+ if (prefix_list_afi(plist) != AFI_IP6)
+ return;
+
for (ALL_LIST_ELEMENTS(om6->ospf6, node, nnode, ospf6)) {
+ bool update = false;
+
for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, n, oa)) {
if (PREFIX_NAME_IN(oa)
&& !strcmp(PREFIX_NAME_IN(oa), name)) {
- PREFIX_LIST_IN(oa) = add ? plist : NULL;
- ospf6_abr_reexport(oa);
+ PREFIX_LIST_IN(oa) = prefix_list_lookup(
+ AFI_IP6, PREFIX_NAME_IN(oa));
+ update = true;
}
if (PREFIX_NAME_OUT(oa)
&& !strcmp(PREFIX_NAME_OUT(oa), name)) {
- PREFIX_LIST_OUT(oa) = add ? plist : NULL;
- ospf6_abr_reexport(oa);
+ PREFIX_LIST_OUT(oa) = prefix_list_lookup(
+ AFI_IP6, PREFIX_NAME_OUT(oa));
+ update = true;
}
}
+
+ if (update && ospf6_check_and_set_router_abr(ospf6))
+ ospf6_schedule_abr_task(ospf6);
}
}
@@ -822,7 +845,8 @@ DEFUN (area_import_list,
free(IMPORT_NAME(area));
IMPORT_NAME(area) = strdup(argv[idx_name]->arg);
- ospf6_abr_reimport(area);
+ if (ospf6_check_and_set_router_abr(area->ospf6))
+ ospf6_schedule_abr_task(ospf6);
return CMD_SUCCESS;
}
@@ -844,13 +868,14 @@ DEFUN (no_area_import_list,
OSPF6_CMD_AREA_GET(argv[idx_ipv4]->arg, area, ospf6);
- IMPORT_LIST(area) = 0;
+ IMPORT_LIST(area) = NULL;
if (IMPORT_NAME(area))
free(IMPORT_NAME(area));
IMPORT_NAME(area) = NULL;
- ospf6_abr_reimport(area);
+ if (ospf6_check_and_set_router_abr(area->ospf6))
+ ospf6_schedule_abr_task(ospf6);
return CMD_SUCCESS;
}
@@ -883,7 +908,8 @@ DEFUN (area_export_list,
EXPORT_NAME(area) = strdup(argv[idx_name]->arg);
/* Redo summaries if required */
- ospf6_abr_reexport(area);
+ if (ospf6_check_and_set_router_abr(area->ospf6))
+ ospf6_schedule_abr_task(ospf6);
return CMD_SUCCESS;
}
@@ -905,13 +931,14 @@ DEFUN (no_area_export_list,
OSPF6_CMD_AREA_GET(argv[idx_ipv4]->arg, area, ospf6);
- EXPORT_LIST(area) = 0;
+ EXPORT_LIST(area) = NULL;
if (EXPORT_NAME(area))
free(EXPORT_NAME(area));
EXPORT_NAME(area) = NULL;
- ospf6_abr_reexport(area);
+ if (ospf6_check_and_set_router_abr(area->ospf6))
+ ospf6_schedule_abr_task(ospf6);
return CMD_SUCCESS;
}
diff --git a/ospf6d/ospf6_area.h b/ospf6d/ospf6_area.h
index aed9589bfb..43ac60b261 100644
--- a/ospf6d/ospf6_area.h
+++ b/ospf6d/ospf6_area.h
@@ -53,7 +53,7 @@ struct ospf6_area {
int no_summary;
/* Brouter traversal protection */
- int intra_brouter_calc;
+ bool intra_brouter_calc;
/* OSPF interface list */
struct list *if_list;
@@ -163,7 +163,7 @@ extern void ospf6_area_disable(struct ospf6_area *oa);
extern void ospf6_area_show(struct vty *vty, struct ospf6_area *oa,
json_object *json_areas, bool use_json);
-extern void ospf6_area_plist_update(struct prefix_list *plist, int add);
+extern void ospf6_plist_update(struct prefix_list *plist);
extern void ospf6_filter_update(struct access_list *access);
extern void ospf6_area_config_write(struct vty *vty, struct ospf6 *ospf6);
extern void ospf6_area_init(void);
diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c
index b31def2d74..277af4b1c5 100644
--- a/ospf6d/ospf6_intra.c
+++ b/ospf6d/ospf6_intra.c
@@ -2353,7 +2353,7 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa)
* the table. For an example, ospf6_abr_examin_summary,
* removes brouters which are marked for remove.
*/
- oa->intra_brouter_calc = 1;
+ oa->intra_brouter_calc = true;
ospf6_route_remove(brouter, oa->ospf6->brouter_table);
brouter = NULL;
} else if (CHECK_FLAG(brouter->flag, OSPF6_ROUTE_ADD)
@@ -2386,7 +2386,7 @@ void ospf6_intra_brouter_calculation(struct ospf6_area *oa)
UNSET_FLAG(brouter->flag, OSPF6_ROUTE_CHANGE);
}
/* Reset for nbrouter */
- oa->intra_brouter_calc = 0;
+ oa->intra_brouter_calc = false;
}
if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(oa->area_id) ||
diff --git a/ospf6d/ospf6d.c b/ospf6d/ospf6d.c
index 2c8c9b9d45..5e6dcde991 100644
--- a/ospf6d/ospf6d.c
+++ b/ospf6d/ospf6d.c
@@ -1355,20 +1355,6 @@ DEFUN(show_ipv6_ospf6_linkstate_detail, show_ipv6_ospf6_linkstate_detail_cmd,
return CMD_SUCCESS;
}
-static void ospf6_plist_add(struct prefix_list *plist)
-{
- if (prefix_list_afi(plist) != AFI_IP6)
- return;
- ospf6_area_plist_update(plist, 1);
-}
-
-static void ospf6_plist_del(struct prefix_list *plist)
-{
- if (prefix_list_afi(plist) != AFI_IP6)
- return;
- ospf6_area_plist_update(plist, 0);
-}
-
/* Install ospf related commands. */
void ospf6_init(struct thread_master *master)
{
@@ -1387,8 +1373,8 @@ void ospf6_init(struct thread_master *master)
ospf6_gr_helper_config_init();
/* initialize hooks for modifying filter rules */
- prefix_list_add_hook(ospf6_plist_add);
- prefix_list_delete_hook(ospf6_plist_del);
+ prefix_list_add_hook(ospf6_plist_update);
+ prefix_list_delete_hook(ospf6_plist_update);
access_list_add_hook(ospf6_filter_update);
access_list_delete_hook(ospf6_filter_update);
diff --git a/tests/lib/test_skiplist.c b/tests/lib/test_skiplist.c
new file mode 100644
index 0000000000..2f9ca5eaea
--- /dev/null
+++ b/tests/lib/test_skiplist.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2021, LabN Consulting, L.L.C
+ *
+ * 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 <zebra.h>
+#include <skiplist.h>
+
+static void sl_debug(struct skiplist *l)
+{
+ int i;
+
+ if (!l)
+ return;
+
+ printf("Skiplist %p has max level %d\n", l, l->level);
+ for (i = l->level; i >= 0; --i)
+ printf(" @%d: %d\n", i, l->level_stats[i]);
+}
+
+static void *scramble(int i)
+{
+ uintptr_t result;
+
+ result = (uintptr_t)(i & 0xff) << 24;
+ result |= (uintptr_t)i >> 8;
+
+ return (void *)result;
+}
+#define sampleSize 65536
+static int sl_test(void)
+{
+ struct skiplist *l;
+ register int i, k;
+ void *keys[sampleSize];
+ void *v = NULL;
+ int errors = 0;
+
+ l = skiplist_new(SKIPLIST_FLAG_ALLOW_DUPLICATES, NULL, NULL);
+
+ printf("%s: skiplist_new returned %p\n", __func__, l);
+
+ for (i = 0; i < 4; i++) {
+
+ for (k = 0; k < sampleSize; k++) {
+ if (!(k % 10000))
+ printf("%s: (%d:%d)\n", __func__, i, k);
+ /* keys[k] = (void *)random(); */
+ keys[k] = scramble(k);
+ if (skiplist_insert(l, keys[k], keys[k])) {
+ ++errors;
+ printf("error in insert #%d,#%d\n", i, k);
+ }
+ }
+
+ printf("%s: inserts done\n", __func__);
+ sl_debug(l);
+
+ for (k = 0; k < sampleSize; k++) {
+
+ if (!(k % 10000))
+ printf("[%d:%d]\n", i, k);
+ /* keys[k] = (void *)random(); */
+ if (skiplist_search(l, keys[k], &v)) {
+ ++errors;
+ printf("error in search #%d,#%d\n", i, k);
+ }
+
+ if (v != keys[k]) {
+ ++errors;
+ printf("search returned wrong value\n");
+ }
+ }
+ printf("%s: searches done\n", __func__);
+
+
+ for (k = 0; k < sampleSize; k++) {
+
+ if (!(k % 10000))
+ printf("<%d:%d>\n", i, k);
+ /* keys[k] = (void *)random(); */
+ if (skiplist_delete(l, keys[k], keys[k])) {
+ ++errors;
+ printf("error in delete\n");
+ }
+ keys[k] = scramble(k ^ 0xf0f0f0f0);
+ if (skiplist_insert(l, keys[k], keys[k])) {
+ ++errors;
+ printf("error in insert #%d,#%d\n", i, k);
+ }
+ }
+
+ printf("%s: del+inserts done\n", __func__);
+ sl_debug(l);
+
+ for (k = 0; k < sampleSize; k++) {
+
+ if (!(k % 10000))
+ printf("{%d:%d}\n", i, k);
+ /* keys[k] = (void *)random(); */
+ if (skiplist_delete_first(l)) {
+ ++errors;
+ printf("error in delete_first\n");
+ }
+ }
+ }
+
+ sl_debug(l);
+
+ skiplist_free(l);
+
+ return errors;
+}
+
+int main(int argc, char **argv)
+{
+ int errors = sl_test();
+
+ if (errors)
+ return 1;
+ return 0;
+}
diff --git a/tests/subdir.am b/tests/subdir.am
index 1edfda9bc2..f21e12ecbb 100644
--- a/tests/subdir.am
+++ b/tests/subdir.am
@@ -98,6 +98,7 @@ check_PROGRAMS = \
tests/lib/test_segv \
tests/lib/test_seqlock \
tests/lib/test_sig \
+ tests/lib/test_skiplist \
tests/lib/test_stream \
tests/lib/test_table \
tests/lib/test_timer_correctness \
@@ -366,6 +367,10 @@ tests_lib_test_sig_CFLAGS = $(TESTS_CFLAGS)
tests_lib_test_sig_CPPFLAGS = $(TESTS_CPPFLAGS)
tests_lib_test_sig_LDADD = $(ALL_TESTS_LDADD)
tests_lib_test_sig_SOURCES = tests/lib/test_sig.c
+tests_lib_test_skiplist_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_skiplist_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_skiplist_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_skiplist_SOURCES = tests/lib/test_skiplist.c
tests_lib_test_srcdest_table_CFLAGS = $(TESTS_CFLAGS)
tests_lib_test_srcdest_table_CPPFLAGS = $(TESTS_CPPFLAGS)
tests_lib_test_srcdest_table_LDADD = $(ALL_TESTS_LDADD)
diff --git a/tests/topotests/bgp_prefix_sid2/peer1/exabgp.cfg b/tests/topotests/bgp_prefix_sid2/peer1/exabgp.cfg
index ad1b15a26c..3819179570 100644
--- a/tests/topotests/bgp_prefix_sid2/peer1/exabgp.cfg
+++ b/tests/topotests/bgp_prefix_sid2/peer1/exabgp.cfg
@@ -15,14 +15,14 @@ group controller {
next-hop 2001::2;
extended-community [ target:2:10 ];
label 3;
- attribute [0x28 0xc0 0x0500150020010db800010001000000000000000100ffff00 ];
+ attribute [0x28 0xc0 0x050019000100150020010db800010001000000000000000100ffff00 ];
}
route 2001:2::/64 {
rd 2:10;
next-hop 2001::2;
extended-community [ target:2:10 ];
label 3;
- attribute [0x28 0xc0 0x0500150020010db800010001000000000000000100ffff00 ];
+ attribute [0x28 0xc0 0x050019000100150020010db800010001000000000000000100ffff00 ];
}
}
}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf10_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf10_rib.json
index fa05972a35..141c1cb957 100644
--- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf10_rib.json
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf10_rib.json
@@ -48,12 +48,9 @@
"interfaceName": "eth0",
"vrf": "default",
"active": true,
- "labels": [
- 3
- ],
"weight": 1,
"seg6": {
- "segs": "2001:db8:2:2::100"
+ "segs": "2001:db8:2:2:100::"
}
}
],
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf20_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf20_rib.json
index 0155557242..e20998061f 100644
--- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf20_rib.json
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf20_rib.json
@@ -22,12 +22,9 @@
"interfaceName": "eth0",
"vrf": "default",
"active": true,
- "labels": [
- 3
- ],
"weight": 1,
"seg6": {
- "segs": "2001:db8:2:2::200"
+ "segs": "2001:db8:2:2:200::"
}
}
],
@@ -83,12 +80,9 @@
"interfaceName": "eth0",
"vrf": "default",
"active": true,
- "labels": [
- 3
- ],
"weight": 1,
"seg6": {
- "segs": "2001:db8:2:2::200"
+ "segs": "2001:db8:2:2:200::"
}
}
],
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf10_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf10_rib.json
index 887eb24386..7f8a930d00 100644
--- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf10_rib.json
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf10_rib.json
@@ -22,12 +22,9 @@
"interfaceName": "eth0",
"vrf": "default",
"active": true,
- "labels": [
- 3
- ],
"weight": 1,
"seg6": {
- "segs": "2001:db8:1:1::100"
+ "segs": "2001:db8:1:1:100::"
}
}
],
@@ -83,12 +80,9 @@
"interfaceName": "eth0",
"vrf": "default",
"active": true,
- "labels": [
- 3
- ],
"weight": 1,
"seg6": {
- "segs": "2001:db8:1:1::100"
+ "segs": "2001:db8:1:1:100::"
}
}
],
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf20_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf20_rib.json
index c118518423..104bdc30d2 100644
--- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf20_rib.json
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf20_rib.json
@@ -48,12 +48,9 @@
"interfaceName": "eth0",
"vrf": "default",
"active": true,
- "labels": [
- 3
- ],
"weight": 1,
"seg6": {
- "segs": "2001:db8:1:1::200"
+ "segs": "2001:db8:1:1:200::"
}
}
],
diff --git a/tests/topotests/ospf6_topo2/test_ospf6_topo2.py b/tests/topotests/ospf6_topo2/test_ospf6_topo2.py
index bea3aeaaf6..acaa3b8a9b 100644
--- a/tests/topotests/ospf6_topo2/test_ospf6_topo2.py
+++ b/tests/topotests/ospf6_topo2/test_ospf6_topo2.py
@@ -431,6 +431,75 @@ def test_nssa_no_summary():
assert result is None, assertmsg
+def test_area_filters():
+ """
+ Test ABR import/export filters.
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ #
+ # Configure import/export filters on r2 (ABR for area 1).
+ #
+ config = """
+ configure terminal
+ ipv6 access-list ACL_IMPORT seq 5 permit 2001:db8:2::/64
+ ipv6 access-list ACL_IMPORT seq 10 deny any
+ ipv6 access-list ACL_EXPORT seq 10 deny any
+ router ospf6
+ area 1 import-list ACL_IMPORT
+ area 1 export-list ACL_EXPORT
+ """
+ tgen.gears["r2"].vtysh_cmd(config)
+
+ logger.info("Expecting inter-area routes to be removed on r1")
+ for route in ["::/0", "2001:db8:3::/64"]:
+ test_func = partial(dont_expect_route, "r1", route, type="inter-area")
+ _, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
+ assertmsg = "{}'s {} inter-area route still exists".format("r1", route)
+ assert result is None, assertmsg
+
+ logger.info("Expecting inter-area routes to be removed on r3")
+ for route in ["2001:db8:1::/64"]:
+ test_func = partial(dont_expect_route, "r3", route, type="inter-area")
+ _, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
+ assertmsg = "{}'s {} inter-area route still exists".format("r3", route)
+ assert result is None, assertmsg
+
+ #
+ # Update the ACLs used by the import/export filters.
+ #
+ config = """
+ configure terminal
+ ipv6 access-list ACL_IMPORT seq 6 permit 2001:db8:3::/64
+ ipv6 access-list ACL_EXPORT seq 5 permit 2001:db8:1::/64
+ """
+ tgen.gears["r2"].vtysh_cmd(config)
+
+ logger.info("Expecting 2001:db8:3::/64 to be re-added on r1")
+ routes = {"2001:db8:3::/64": {}}
+ expect_ospfv3_routes("r1", routes, wait=30, type="inter-area")
+ logger.info("Expecting 2001:db8:1::/64 to be re-added on r3")
+ routes = {"2001:db8:1::/64": {}}
+ expect_ospfv3_routes("r3", routes, wait=30, type="inter-area")
+
+ #
+ # Unconfigure r2's ABR import/export filters.
+ #
+ config = """
+ configure terminal
+ router ospf6
+ no area 1 import-list ACL_IMPORT
+ no area 1 export-list ACL_EXPORT
+ """
+ tgen.gears["r2"].vtysh_cmd(config)
+
+ logger.info("Expecting ::/0 to be re-added on r1")
+ routes = {"::/0": {}}
+ expect_ospfv3_routes("r1", routes, wait=30, type="inter-area")
+
+
def teardown_module(_mod):
"Teardown the pytest environment"
tgen = get_topogen()
diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c
index 2e1d7c5bad..7d66319669 100644
--- a/vtysh/vtysh_config.c
+++ b/vtysh/vtysh_config.c
@@ -286,7 +286,6 @@ void vtysh_config_parse_line(void *arg, const char *line)
} else if (config->index == RMAP_NODE
|| config->index == INTERFACE_NODE
|| config->index == VTY_NODE
- || config->index == VRF_NODE
|| config->index == NH_GROUP_NODE)
config_add_line_uniq(config->line, line);
else
diff --git a/zebra/connected.c b/zebra/connected.c
index e1dd0dbdff..0511b35185 100644
--- a/zebra/connected.c
+++ b/zebra/connected.c
@@ -307,9 +307,10 @@ void connected_up(struct interface *ifp, struct connected *ifc)
}
/* Add connected IPv4 route to the interface. */
-void connected_add_ipv4(struct interface *ifp, int flags, struct in_addr *addr,
- uint16_t prefixlen, struct in_addr *dest,
- const char *label, uint32_t metric)
+void connected_add_ipv4(struct interface *ifp, int flags,
+ const struct in_addr *addr, uint16_t prefixlen,
+ const struct in_addr *dest, const char *label,
+ uint32_t metric)
{
struct prefix_ipv4 *p;
struct connected *ifc;
@@ -502,8 +503,8 @@ static void connected_delete_helper(struct connected *ifc, struct prefix *p)
/* Delete connected IPv4 route to the interface. */
void connected_delete_ipv4(struct interface *ifp, int flags,
- struct in_addr *addr, uint16_t prefixlen,
- struct in_addr *dest)
+ const struct in_addr *addr, uint16_t prefixlen,
+ const struct in_addr *dest)
{
struct prefix p, d;
struct connected *ifc;
@@ -527,8 +528,9 @@ void connected_delete_ipv4(struct interface *ifp, int flags,
}
/* Add connected IPv6 route to the interface. */
-void connected_add_ipv6(struct interface *ifp, int flags, struct in6_addr *addr,
- struct in6_addr *dest, uint16_t prefixlen,
+void connected_add_ipv6(struct interface *ifp, int flags,
+ const struct in6_addr *addr,
+ const struct in6_addr *dest, uint16_t prefixlen,
const char *label, uint32_t metric)
{
struct prefix_ipv6 *p;
@@ -589,8 +591,9 @@ void connected_add_ipv6(struct interface *ifp, int flags, struct in6_addr *addr,
connected_update(ifp, ifc);
}
-void connected_delete_ipv6(struct interface *ifp, struct in6_addr *address,
- struct in6_addr *dest, uint16_t prefixlen)
+void connected_delete_ipv6(struct interface *ifp,
+ const struct in6_addr *address,
+ const struct in6_addr *dest, uint16_t prefixlen)
{
struct prefix p, d;
struct connected *ifc;
diff --git a/zebra/connected.h b/zebra/connected.h
index 14f6cb2db0..3ed9f6d5b9 100644
--- a/zebra/connected.h
+++ b/zebra/connected.h
@@ -39,13 +39,14 @@ extern struct connected *connected_check_ptp(struct interface *ifp,
union prefixconstptr d);
extern void connected_add_ipv4(struct interface *ifp, int flags,
- struct in_addr *addr, uint16_t prefixlen,
- struct in_addr *dest, const char *label,
+ const struct in_addr *addr, uint16_t prefixlen,
+ const struct in_addr *dest, const char *label,
uint32_t metric);
extern void connected_delete_ipv4(struct interface *ifp, int flags,
- struct in_addr *addr, uint16_t prefixlen,
- struct in_addr *dest);
+ const struct in_addr *addr,
+ uint16_t prefixlen,
+ const struct in_addr *dest);
extern void connected_delete_ipv4_unnumbered(struct connected *ifc);
@@ -53,12 +54,13 @@ extern void connected_up(struct interface *ifp, struct connected *ifc);
extern void connected_down(struct interface *ifp, struct connected *ifc);
extern void connected_add_ipv6(struct interface *ifp, int flags,
- struct in6_addr *address, struct in6_addr *dest,
- uint16_t prefixlen, const char *label,
- uint32_t metric);
+ const struct in6_addr *address,
+ const struct in6_addr *dest, uint16_t prefixlen,
+ const char *label, uint32_t metric);
extern void connected_delete_ipv6(struct interface *ifp,
- struct in6_addr *address,
- struct in6_addr *dest, uint16_t prefixlen);
+ const struct in6_addr *address,
+ const struct in6_addr *dest,
+ uint16_t prefixlen);
extern int connected_is_unnumbered(struct interface *);
diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c
index 8b3b788b72..acd6697b65 100644
--- a/zebra/if_netlink.c
+++ b/zebra/if_netlink.c
@@ -1021,7 +1021,8 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup)
if (IS_ZEBRA_IF_BOND(ifp))
zebra_l2if_update_bond(ifp, true);
if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp))
- zebra_l2if_update_bridge_slave(ifp, bridge_ifindex, ns_id);
+ zebra_l2if_update_bridge_slave(ifp, bridge_ifindex, ns_id,
+ ZEBRA_BRIDGE_NO_ACTION);
else if (IS_ZEBRA_IF_BOND_SLAVE(ifp))
zebra_l2if_update_bond_slave(ifp, bond_ifindex, !!bypass);
@@ -1443,7 +1444,6 @@ int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, int startup)
NULL, ifa->ifa_prefixlen);
}
-
/*
* Linux kernel does not send route delete on interface down/addr del
* so we have to re-process routes it owns (i.e. kernel routes)
@@ -1454,6 +1454,215 @@ int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, int startup)
return 0;
}
+/*
+ * Parse and validate an incoming interface address change message,
+ * generating a dplane context object.
+ * This runs in the dplane pthread; the context is enqueued to the
+ * main pthread for processing.
+ */
+int netlink_interface_addr_dplane(struct nlmsghdr *h, ns_id_t ns_id,
+ int startup /*ignored*/)
+{
+ int len;
+ struct ifaddrmsg *ifa;
+ struct rtattr *tb[IFA_MAX + 1];
+ void *addr;
+ void *broad;
+ char *label = NULL;
+ uint32_t metric = METRIC_MAX;
+ uint32_t kernel_flags = 0;
+ struct zebra_dplane_ctx *ctx;
+ struct prefix p;
+
+ ifa = NLMSG_DATA(h);
+
+ /* Validate message types */
+ if (h->nlmsg_type != RTM_NEWADDR && h->nlmsg_type != RTM_DELADDR)
+ return 0;
+
+ if (ifa->ifa_family != AF_INET && ifa->ifa_family != AF_INET6) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: %s: Invalid address family: %u",
+ __func__, nl_msg_type_to_str(h->nlmsg_type),
+ ifa->ifa_family);
+ return 0;
+ }
+
+ len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+ if (len < 0) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: %s: netlink msg bad size: %d %zu",
+ __func__, nl_msg_type_to_str(h->nlmsg_type),
+ h->nlmsg_len,
+ (size_t)NLMSG_LENGTH(
+ sizeof(struct ifaddrmsg)));
+ return -1;
+ }
+
+ netlink_parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), len);
+
+ /* Flags passed through */
+ if (tb[IFA_FLAGS])
+ kernel_flags = *(int *)RTA_DATA(tb[IFA_FLAGS]);
+ else
+ kernel_flags = ifa->ifa_flags;
+
+ if (IS_ZEBRA_DEBUG_KERNEL) { /* remove this line to see initial ifcfg */
+ char buf[PREFIX_STRLEN];
+
+ zlog_debug("%s: %s nsid %u ifindex %u flags 0x%x:", __func__,
+ nl_msg_type_to_str(h->nlmsg_type), ns_id,
+ ifa->ifa_index, kernel_flags);
+ if (tb[IFA_LOCAL])
+ zlog_debug(" IFA_LOCAL %s/%d",
+ inet_ntop(ifa->ifa_family,
+ RTA_DATA(tb[IFA_LOCAL]), buf,
+ sizeof(buf)),
+ ifa->ifa_prefixlen);
+ if (tb[IFA_ADDRESS])
+ zlog_debug(" IFA_ADDRESS %s/%d",
+ inet_ntop(ifa->ifa_family,
+ RTA_DATA(tb[IFA_ADDRESS]), buf,
+ sizeof(buf)),
+ ifa->ifa_prefixlen);
+ if (tb[IFA_BROADCAST])
+ zlog_debug(" IFA_BROADCAST %s/%d",
+ inet_ntop(ifa->ifa_family,
+ RTA_DATA(tb[IFA_BROADCAST]), buf,
+ sizeof(buf)),
+ ifa->ifa_prefixlen);
+ if (tb[IFA_LABEL])
+ zlog_debug(" IFA_LABEL %s",
+ (const char *)RTA_DATA(tb[IFA_LABEL]));
+
+ if (tb[IFA_CACHEINFO]) {
+ struct ifa_cacheinfo *ci = RTA_DATA(tb[IFA_CACHEINFO]);
+
+ zlog_debug(" IFA_CACHEINFO pref %d, valid %d",
+ ci->ifa_prefered, ci->ifa_valid);
+ }
+ }
+
+ /* Validate prefix length */
+
+ if (ifa->ifa_family == AF_INET
+ && ifa->ifa_prefixlen > IPV4_MAX_BITLEN) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: %s: Invalid prefix length: %u",
+ __func__, nl_msg_type_to_str(h->nlmsg_type),
+ ifa->ifa_prefixlen);
+ return -1;
+ }
+
+ if (ifa->ifa_family == AF_INET6) {
+ if (ifa->ifa_prefixlen > IPV6_MAX_BITLEN) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: %s: Invalid prefix length: %u",
+ __func__,
+ nl_msg_type_to_str(h->nlmsg_type),
+ ifa->ifa_prefixlen);
+ return -1;
+ }
+
+ /* Only consider valid addresses; we'll not get a kernel
+ * notification till IPv6 DAD has completed, but at init
+ * time, FRR does query for and will receive all addresses.
+ */
+ if (h->nlmsg_type == RTM_NEWADDR
+ && (kernel_flags & (IFA_F_DADFAILED | IFA_F_TENTATIVE))) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: %s: Invalid/tentative addr",
+ __func__,
+ nl_msg_type_to_str(h->nlmsg_type));
+ return 0;
+ }
+ }
+
+ /* logic copied from iproute2/ip/ipaddress.c:print_addrinfo() */
+ if (tb[IFA_LOCAL] == NULL)
+ tb[IFA_LOCAL] = tb[IFA_ADDRESS];
+ if (tb[IFA_ADDRESS] == NULL)
+ tb[IFA_ADDRESS] = tb[IFA_LOCAL];
+
+ /* local interface address */
+ addr = (tb[IFA_LOCAL] ? RTA_DATA(tb[IFA_LOCAL]) : NULL);
+
+ /* addr is primary key, SOL if we don't have one */
+ if (addr == NULL) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: %s: No local interface address",
+ __func__, nl_msg_type_to_str(h->nlmsg_type));
+ return -1;
+ }
+
+ /* Allocate a context object, now that validation is done. */
+ ctx = dplane_ctx_alloc();
+ if (h->nlmsg_type == RTM_NEWADDR)
+ dplane_ctx_set_op(ctx, DPLANE_OP_INTF_ADDR_ADD);
+ else
+ dplane_ctx_set_op(ctx, DPLANE_OP_INTF_ADDR_DEL);
+
+ dplane_ctx_set_ifindex(ctx, ifa->ifa_index);
+ dplane_ctx_set_ns_id(ctx, ns_id);
+
+ /* Convert addr to prefix */
+ memset(&p, 0, sizeof(p));
+ p.family = ifa->ifa_family;
+ p.prefixlen = ifa->ifa_prefixlen;
+ if (p.family == AF_INET)
+ p.u.prefix4 = *(struct in_addr *)addr;
+ else
+ p.u.prefix6 = *(struct in6_addr *)addr;
+
+ dplane_ctx_set_intf_addr(ctx, &p);
+
+ /* is there a peer address? */
+ if (tb[IFA_ADDRESS]
+ && memcmp(RTA_DATA(tb[IFA_ADDRESS]), RTA_DATA(tb[IFA_LOCAL]),
+ RTA_PAYLOAD(tb[IFA_ADDRESS]))) {
+ broad = RTA_DATA(tb[IFA_ADDRESS]);
+ dplane_ctx_intf_set_connected(ctx);
+ } else if (tb[IFA_BROADCAST]) {
+ /* seeking a broadcast address */
+ broad = RTA_DATA(tb[IFA_BROADCAST]);
+ dplane_ctx_intf_set_broadcast(ctx);
+ } else
+ broad = NULL;
+
+ if (broad) {
+ /* Convert addr to prefix */
+ memset(&p, 0, sizeof(p));
+ p.family = ifa->ifa_family;
+ p.prefixlen = ifa->ifa_prefixlen;
+ if (p.family == AF_INET)
+ p.u.prefix4 = *(struct in_addr *)broad;
+ else
+ p.u.prefix6 = *(struct in6_addr *)broad;
+
+ dplane_ctx_set_intf_dest(ctx, &p);
+ }
+
+ /* Flags. */
+ if (kernel_flags & IFA_F_SECONDARY)
+ dplane_ctx_intf_set_secondary(ctx);
+
+ /* Label */
+ if (tb[IFA_LABEL]) {
+ label = (char *)RTA_DATA(tb[IFA_LABEL]);
+ dplane_ctx_set_intf_label(ctx, label);
+ }
+
+ if (tb[IFA_RT_PRIORITY])
+ metric = *(uint32_t *)RTA_DATA(tb[IFA_RT_PRIORITY]);
+
+ dplane_ctx_set_intf_metric(ctx, metric);
+
+ /* Enqueue ctx for main pthread to process */
+ dplane_provider_enqueue_to_zebra(ctx);
+
+ return 0;
+}
+
int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
{
int len;
@@ -1644,9 +1853,9 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
ifp, linkinfo[IFLA_INFO_DATA],
1, link_nsid);
if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp))
- zebra_l2if_update_bridge_slave(ifp,
- bridge_ifindex,
- ns_id);
+ zebra_l2if_update_bridge_slave(
+ ifp, bridge_ifindex, ns_id,
+ ZEBRA_BRIDGE_NO_ACTION);
else if (IS_ZEBRA_IF_BOND_SLAVE(ifp))
zebra_l2if_update_bond_slave(ifp, bond_ifindex,
!!bypass);
@@ -1670,6 +1879,7 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
if_handle_vrf_change(ifp, vrf_id);
} else {
bool was_bridge_slave, was_bond_slave;
+ uint8_t chgflags = ZEBRA_BRIDGE_NO_ACTION;
/* Interface update. */
if (IS_ZEBRA_DEBUG_KERNEL)
@@ -1711,6 +1921,8 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
if_down(ifp);
rib_update(RIB_UPDATE_KERNEL);
} else if (if_is_operative(ifp)) {
+ bool mac_updated = false;
+
/* Must notify client daemons of new
* interface status. */
if (IS_ZEBRA_DEBUG_KERNEL)
@@ -1721,9 +1933,11 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
/* Update EVPN VNI when SVI MAC change
*/
- if (IS_ZEBRA_IF_VLAN(ifp) &&
- memcmp(old_hw_addr, ifp->hw_addr,
- INTERFACE_HWADDR_MAX)) {
+ if (memcmp(old_hw_addr, ifp->hw_addr,
+ INTERFACE_HWADDR_MAX))
+ mac_updated = true;
+ if (IS_ZEBRA_IF_VLAN(ifp)
+ && mac_updated) {
struct interface *link_if;
link_if =
@@ -1733,6 +1947,13 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
if (link_if)
zebra_vxlan_svi_up(ifp,
link_if);
+ } else if (mac_updated
+ && IS_ZEBRA_IF_BRIDGE(ifp)) {
+ zlog_debug(
+ "Intf %s(%u) bridge changed MAC address",
+ name, ifp->ifindex);
+ chgflags =
+ ZEBRA_BRIDGE_MASTER_MAC_CHANGE;
}
}
} else {
@@ -1743,6 +1964,9 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
"Intf %s(%u) has come UP",
name, ifp->ifindex);
if_up(ifp);
+ if (IS_ZEBRA_IF_BRIDGE(ifp))
+ chgflags =
+ ZEBRA_BRIDGE_MASTER_UP;
} else {
if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug(
@@ -1758,12 +1982,13 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
netlink_interface_update_l2info(
ifp, linkinfo[IFLA_INFO_DATA],
0, link_nsid);
+ if (IS_ZEBRA_IF_BRIDGE(ifp))
+ zebra_l2if_update_bridge(ifp, chgflags);
if (IS_ZEBRA_IF_BOND(ifp))
zebra_l2if_update_bond(ifp, true);
if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp) || was_bridge_slave)
- zebra_l2if_update_bridge_slave(ifp,
- bridge_ifindex,
- ns_id);
+ zebra_l2if_update_bridge_slave(
+ ifp, bridge_ifindex, ns_id, chgflags);
else if (IS_ZEBRA_IF_BOND_SLAVE(ifp) || was_bond_slave)
zebra_l2if_update_bond_slave(ifp, bond_ifindex,
!!bypass);
diff --git a/zebra/if_netlink.h b/zebra/if_netlink.h
index 4f09b10b75..a1ce7af8c7 100644
--- a/zebra/if_netlink.h
+++ b/zebra/if_netlink.h
@@ -29,6 +29,14 @@ extern "C" {
extern int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id,
int startup);
+
+/*
+ * Parse an incoming interface address change message, generate a dplane
+ * context object for processing.
+ */
+int netlink_interface_addr_dplane(struct nlmsghdr *h, ns_id_t ns_id,
+ int startup);
+
extern int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup);
extern int interface_lookup_netlink(struct zebra_ns *zns);
diff --git a/zebra/interface.c b/zebra/interface.c
index 328ef3fa41..a68d00d55c 100644
--- a/zebra/interface.c
+++ b/zebra/interface.c
@@ -1205,6 +1205,109 @@ void zebra_if_set_protodown(struct interface *ifp, bool down)
#endif
}
+/*
+ * Handle an interface addr event based on info in a dplane context object.
+ * This runs in the main pthread, using the info in the context object to
+ * modify an interface.
+ */
+void zebra_if_addr_update_ctx(struct zebra_dplane_ctx *ctx)
+{
+ struct interface *ifp;
+ uint8_t flags = 0;
+ const char *label = NULL;
+ ns_id_t ns_id;
+ struct zebra_ns *zns;
+ uint32_t metric = METRIC_MAX;
+ ifindex_t ifindex;
+ const struct prefix *addr, *dest = NULL;
+ enum dplane_op_e op;
+
+ op = dplane_ctx_get_op(ctx);
+ ns_id = dplane_ctx_get_ns_id(ctx);
+
+ zns = zebra_ns_lookup(ns_id);
+ if (zns == NULL) {
+ /* No ns - deleted maybe? */
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: can't find zns id %u", __func__, ns_id);
+ goto done;
+ }
+
+ ifindex = dplane_ctx_get_ifindex(ctx);
+
+ ifp = if_lookup_by_index_per_ns(zns, ifindex);
+ if (ifp == NULL) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: can't find ifp at nsid %u index %d",
+ __func__, ns_id, ifindex);
+ goto done;
+ }
+
+ addr = dplane_ctx_get_intf_addr(ctx);
+
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: %s: ifindex %u, addr %pFX", __func__,
+ dplane_op2str(op), ifindex, addr);
+
+ /* Is there a peer or broadcast address? */
+ dest = dplane_ctx_get_intf_dest(ctx);
+ if (dest->prefixlen == 0)
+ dest = NULL;
+
+ if (dplane_ctx_intf_is_connected(ctx))
+ SET_FLAG(flags, ZEBRA_IFA_PEER);
+
+ /* Flags. */
+ if (dplane_ctx_intf_is_secondary(ctx))
+ SET_FLAG(flags, ZEBRA_IFA_SECONDARY);
+
+ /* Label? */
+ if (dplane_ctx_intf_has_label(ctx))
+ label = dplane_ctx_get_intf_label(ctx);
+
+ if (label && strcmp(ifp->name, label) == 0)
+ label = NULL;
+
+ metric = dplane_ctx_get_intf_metric(ctx);
+
+ /* Register interface address to the interface. */
+ if (addr->family == AF_INET) {
+ if (op == DPLANE_OP_INTF_ADDR_ADD)
+ connected_add_ipv4(
+ ifp, flags, &addr->u.prefix4, addr->prefixlen,
+ dest ? &dest->u.prefix4 : NULL, label, metric);
+ else if (CHECK_FLAG(flags, ZEBRA_IFA_PEER)) {
+ /* Delete with a peer address */
+ connected_delete_ipv4(ifp, flags, &addr->u.prefix4,
+ addr->prefixlen,
+ &dest->u.prefix4);
+ } else
+ connected_delete_ipv4(ifp, flags, &addr->u.prefix4,
+ addr->prefixlen, NULL);
+ }
+
+ if (addr->family == AF_INET6) {
+ if (op == DPLANE_OP_INTF_ADDR_ADD) {
+ connected_add_ipv6(ifp, flags, &addr->u.prefix6,
+ dest ? &dest->u.prefix6 : NULL,
+ addr->prefixlen, label, metric);
+ } else
+ connected_delete_ipv6(ifp, &addr->u.prefix6, NULL,
+ addr->prefixlen);
+ }
+
+ /*
+ * Linux kernel does not send route delete on interface down/addr del
+ * so we have to re-process routes it owns (i.e. kernel routes)
+ */
+ if (op != DPLANE_OP_INTF_ADDR_ADD)
+ rib_update(RIB_UPDATE_KERNEL);
+
+done:
+ /* We're responsible for the ctx object */
+ dplane_ctx_fini(&ctx);
+}
+
/* Dump if address information to vty. */
static void connected_dump_vty(struct vty *vty, json_object *json,
struct connected *connected)
diff --git a/zebra/interface.h b/zebra/interface.h
index d86bc68ef0..23e22bdda8 100644
--- a/zebra/interface.h
+++ b/zebra/interface.h
@@ -513,6 +513,7 @@ extern void zebra_l2_map_slave_to_bond(struct zebra_if *zif, vrf_id_t vrf);
extern void zebra_l2_unmap_slave_from_bond(struct zebra_if *zif);
extern const char *zebra_protodown_rc_str(enum protodown_reasons protodown_rc,
char *pd_buf, uint32_t pd_buf_len);
+void zebra_if_addr_update_ctx(struct zebra_dplane_ctx *ctx);
#ifdef HAVE_PROC_NET_DEV
extern void ifstat_update_proc(void);
diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c
index effec24c1f..602bdc1dc5 100644
--- a/zebra/kernel_netlink.c
+++ b/zebra/kernel_netlink.c
@@ -324,6 +324,10 @@ static int netlink_socket(struct nlsock *nl, unsigned long groups,
return ret;
}
+/*
+ * Dispatch an incoming netlink message; used by the zebra main pthread's
+ * netlink event reader.
+ */
static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id,
int startup)
{
@@ -345,10 +349,6 @@ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id,
return netlink_link_change(h, ns_id, startup);
case RTM_DELLINK:
return netlink_link_change(h, ns_id, startup);
- case RTM_NEWADDR:
- return netlink_interface_addr(h, ns_id, startup);
- case RTM_DELADDR:
- return netlink_interface_addr(h, ns_id, startup);
case RTM_NEWNEIGH:
case RTM_DELNEIGH:
case RTM_GETNEIGH:
@@ -361,6 +361,12 @@ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id,
return netlink_nexthop_change(h, ns_id, startup);
case RTM_DELNEXTHOP:
return netlink_nexthop_change(h, ns_id, startup);
+
+ /* Messages handled in the dplane thread */
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ return 0;
+
default:
/*
* If we have received this message then
@@ -378,6 +384,32 @@ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id,
return 0;
}
+/*
+ * Dispatch an incoming netlink message; used by the dataplane pthread's
+ * netlink event reader code.
+ */
+static int dplane_netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id,
+ int startup)
+{
+ /*
+ * Dispatch the incoming messages that the dplane pthread handles
+ */
+ switch (h->nlmsg_type) {
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ return netlink_interface_addr_dplane(h, ns_id, startup);
+
+ /* TODO */
+ case RTM_NEWLINK:
+ case RTM_DELLINK:
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static int kernel_read(struct thread *thread)
{
struct zebra_ns *zns = (struct zebra_ns *)THREAD_ARG(thread);
@@ -388,7 +420,7 @@ static int kernel_read(struct thread *thread)
netlink_parse_info(netlink_information_fetch, &zns->netlink, &dp_info,
5, 0);
- zns->t_netlink = NULL;
+
thread_add_read(zrouter.master, kernel_read, zns, zns->netlink.sock,
&zns->t_netlink);
@@ -396,6 +428,17 @@ static int kernel_read(struct thread *thread)
}
/*
+ * Called by the dplane pthread to read incoming OS messages and dispatch them.
+ */
+int kernel_dplane_read(struct zebra_dplane_info *info)
+{
+ netlink_parse_info(dplane_netlink_information_fetch, &info->nls, info,
+ 5, 0);
+
+ return 0;
+}
+
+/*
* Filter out messages from self that occur on listener socket,
* caused by our actions on the command socket(s)
*
@@ -408,7 +451,7 @@ static int kernel_read(struct thread *thread)
* so that we only had to write one way to handle incoming
* address add/delete changes.
*/
-static void netlink_install_filter(int sock, __u32 pid, __u32 dplane_pid)
+static void netlink_install_filter(int sock, uint32_t pid, uint32_t dplane_pid)
{
/*
* BPF_JUMP instructions and where you jump to are based upon
@@ -476,8 +519,8 @@ static void netlink_install_filter(int sock, __u32 pid, __u32 dplane_pid)
safe_strerror(errno));
}
-void netlink_parse_rtattr_flags(struct rtattr **tb, int max,
- struct rtattr *rta, int len, unsigned short flags)
+void netlink_parse_rtattr_flags(struct rtattr **tb, int max, struct rtattr *rta,
+ int len, unsigned short flags)
{
unsigned short type;
@@ -799,8 +842,7 @@ static int netlink_recv_msg(const struct nlsock *nl, struct msghdr msg,
* ignored, -1 otherwise.
*/
static int netlink_parse_error(const struct nlsock *nl, struct nlmsghdr *h,
- const struct zebra_dplane_info *zns,
- bool startup)
+ bool is_cmd, bool startup)
{
struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(h);
int errnum = err->error;
@@ -833,7 +875,7 @@ static int netlink_parse_error(const struct nlsock *nl, struct nlmsghdr *h,
}
/* Deal with errors that occur because of races in link handling. */
- if (zns->is_cmd
+ if (is_cmd
&& ((msg_type == RTM_DELROUTE
&& (-errnum == ENODEV || -errnum == ESRCH))
|| (msg_type == RTM_NEWROUTE
@@ -852,7 +894,7 @@ static int netlink_parse_error(const struct nlsock *nl, struct nlmsghdr *h,
* do not log these as an error.
*/
if (msg_type == RTM_DELNEIGH
- || (zns->is_cmd && msg_type == RTM_NEWROUTE
+ || (is_cmd && msg_type == RTM_NEWROUTE
&& (-errnum == ESRCH || -errnum == ENETUNREACH))) {
/*
* This is known to happen in some situations, don't log as
@@ -924,8 +966,9 @@ int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int),
/* Error handling. */
if (h->nlmsg_type == NLMSG_ERROR) {
- int err = netlink_parse_error(nl, h, zns,
- startup);
+ int err = netlink_parse_error(
+ nl, h, zns->is_cmd, startup);
+
if (err == 1) {
if (!(h->nlmsg_flags & NLM_F_MULTI))
return 0;
@@ -937,8 +980,8 @@ int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int),
/* OK we got netlink message. */
if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug(
- "netlink_parse_info: %s type %s(%u), len=%d, seq=%u, pid=%u",
- nl->name,
+ "%s: %s type %s(%u), len=%d, seq=%u, pid=%u",
+ __func__, nl->name,
nl_msg_type_to_str(h->nlmsg_type),
h->nlmsg_type, h->nlmsg_len,
h->nlmsg_seq, h->nlmsg_pid);
@@ -1140,7 +1183,8 @@ static int nl_batch_read_resp(struct nl_batch *bth)
}
if (h->nlmsg_type == NLMSG_ERROR) {
- int err = netlink_parse_error(nl, h, bth->zns, 0);
+ int err = netlink_parse_error(nl, h, bth->zns->is_cmd,
+ false);
if (err == -1)
dplane_ctx_set_status(
@@ -1359,6 +1403,8 @@ static enum netlink_msg_status nl_put_msg(struct nl_batch *bth,
case DPLANE_OP_GRE_SET:
return netlink_put_gre_set_msg(bth, ctx);
+ case DPLANE_OP_INTF_ADDR_ADD:
+ case DPLANE_OP_INTF_ADDR_DEL:
case DPLANE_OP_NONE:
return FRR_NETLINK_ERROR;
}
@@ -1455,12 +1501,25 @@ void kernel_init(struct zebra_ns *zns)
exit(-1);
}
- snprintf(zns->netlink_dplane.name, sizeof(zns->netlink_dplane.name),
- "netlink-dp (NS %u)", zns->ns_id);
- zns->netlink_dplane.sock = -1;
- if (netlink_socket(&zns->netlink_dplane, 0, zns->ns_id) < 0) {
+ /* Outbound socket for dplane programming of the host OS. */
+ snprintf(zns->netlink_dplane_out.name,
+ sizeof(zns->netlink_dplane_out.name), "netlink-dp (NS %u)",
+ zns->ns_id);
+ zns->netlink_dplane_out.sock = -1;
+ if (netlink_socket(&zns->netlink_dplane_out, 0, zns->ns_id) < 0) {
zlog_err("Failure to create %s socket",
- zns->netlink_dplane.name);
+ zns->netlink_dplane_out.name);
+ exit(-1);
+ }
+
+ /* Inbound socket for OS events coming to the dplane. */
+ snprintf(zns->netlink_dplane_in.name,
+ sizeof(zns->netlink_dplane_in.name), "netlink-dp-in (NS %u)",
+ zns->ns_id);
+ zns->netlink_dplane_in.sock = -1;
+ if (netlink_socket(&zns->netlink_dplane_in, groups, zns->ns_id) < 0) {
+ zlog_err("Failure to create %s socket",
+ zns->netlink_dplane_in.name);
exit(-1);
}
@@ -1483,8 +1542,8 @@ void kernel_init(struct zebra_ns *zns)
errno, safe_strerror(errno));
one = 1;
- ret = setsockopt(zns->netlink_dplane.sock, SOL_NETLINK, NETLINK_EXT_ACK,
- &one, sizeof(one));
+ ret = setsockopt(zns->netlink_dplane_out.sock, SOL_NETLINK,
+ NETLINK_EXT_ACK, &one, sizeof(one));
if (ret < 0)
zlog_notice("Registration for extended dp ACK failed : %d %s",
@@ -1496,8 +1555,8 @@ void kernel_init(struct zebra_ns *zns)
* setsockopt fails, ignore the error.
*/
one = 1;
- ret = setsockopt(zns->netlink_dplane.sock, SOL_NETLINK, NETLINK_CAP_ACK,
- &one, sizeof(one));
+ ret = setsockopt(zns->netlink_dplane_out.sock, SOL_NETLINK,
+ NETLINK_CAP_ACK, &one, sizeof(one));
if (ret < 0)
zlog_notice(
"Registration for reduced ACK packet size failed, probably running an early kernel");
@@ -1512,20 +1571,33 @@ void kernel_init(struct zebra_ns *zns)
zlog_err("Can't set %s socket error: %s(%d)",
zns->netlink_cmd.name, safe_strerror(errno), errno);
- if (fcntl(zns->netlink_dplane.sock, F_SETFL, O_NONBLOCK) < 0)
+ if (fcntl(zns->netlink_dplane_out.sock, F_SETFL, O_NONBLOCK) < 0)
zlog_err("Can't set %s socket error: %s(%d)",
- zns->netlink_dplane.name, safe_strerror(errno), errno);
+ zns->netlink_dplane_out.name, safe_strerror(errno),
+ errno);
+
+ if (fcntl(zns->netlink_dplane_in.sock, F_SETFL, O_NONBLOCK) < 0)
+ zlog_err("Can't set %s socket error: %s(%d)",
+ zns->netlink_dplane_in.name, safe_strerror(errno),
+ errno);
/* Set receive buffer size if it's set from command line */
if (nl_rcvbufsize) {
netlink_recvbuf(&zns->netlink, nl_rcvbufsize);
netlink_recvbuf(&zns->netlink_cmd, nl_rcvbufsize);
- netlink_recvbuf(&zns->netlink_dplane, nl_rcvbufsize);
+ netlink_recvbuf(&zns->netlink_dplane_out, nl_rcvbufsize);
+ netlink_recvbuf(&zns->netlink_dplane_in, nl_rcvbufsize);
}
- netlink_install_filter(zns->netlink.sock,
+ /* Set filter for inbound sockets, to exclude events we've generated
+ * ourselves.
+ */
+ netlink_install_filter(zns->netlink.sock, zns->netlink_cmd.snl.nl_pid,
+ zns->netlink_dplane_out.snl.nl_pid);
+
+ netlink_install_filter(zns->netlink_dplane_in.sock,
zns->netlink_cmd.snl.nl_pid,
- zns->netlink_dplane.snl.nl_pid);
+ zns->netlink_dplane_out.snl.nl_pid);
zns->t_netlink = NULL;
@@ -1549,13 +1621,18 @@ void kernel_terminate(struct zebra_ns *zns, bool complete)
zns->netlink_cmd.sock = -1;
}
+ if (zns->netlink_dplane_in.sock >= 0) {
+ close(zns->netlink_dplane_in.sock);
+ zns->netlink_dplane_in.sock = -1;
+ }
+
/* During zebra shutdown, we need to leave the dataplane socket
* around until all work is done.
*/
if (complete) {
- if (zns->netlink_dplane.sock >= 0) {
- close(zns->netlink_dplane.sock);
- zns->netlink_dplane.sock = -1;
+ if (zns->netlink_dplane_out.sock >= 0) {
+ close(zns->netlink_dplane_out.sock);
+ zns->netlink_dplane_out.sock = -1;
}
}
}
diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c
index 5c060ac6f8..d9c69ceb6d 100644
--- a/zebra/kernel_socket.c
+++ b/zebra/kernel_socket.c
@@ -529,7 +529,7 @@ int ifm_read(struct if_msghdr *ifm)
/* paranoia: sanity check structure */
if (ifm->ifm_msglen < sizeof(struct if_msghdr)) {
flog_err(EC_ZEBRA_NETLINK_LENGTH_ERROR,
- "ifm_read: ifm->ifm_msglen %d too short\n",
+ "ifm_read: ifm->ifm_msglen %d too short",
ifm->ifm_msglen);
return -1;
}
@@ -807,23 +807,17 @@ static void ifam_read_mesg(struct ifa_msghdr *ifm, union sockunion *addr,
switch (sockunion_family(addr)) {
case AF_INET:
case AF_INET6: {
- char buf[4][INET6_ADDRSTRLEN];
int masklen =
(sockunion_family(addr) == AF_INET)
? ip_masklen(mask->sin.sin_addr)
: ip6_masklen(mask->sin6.sin6_addr);
zlog_debug(
- "%s: ifindex %d, ifname %s, ifam_addrs {%s}, ifam_flags 0x%x, addr %s/%d broad %s dst %s gateway %s",
+ "%s: ifindex %d, ifname %s, ifam_addrs {%s}, ifam_flags 0x%x, addr %pSU/%d broad %pSU dst %pSU gateway %pSU",
__func__, ifm->ifam_index,
(ifnlen ? ifname : "(nil)"),
rtatostr(ifm->ifam_addrs, fbuf, sizeof(fbuf)),
- ifm->ifam_flags,
- sockunion2str(addr, buf[0], sizeof(buf[0])),
- masklen,
- sockunion2str(brd, buf[1], sizeof(buf[1])),
- sockunion2str(&dst, buf[2], sizeof(buf[2])),
- sockunion2str(&gateway, buf[2],
- sizeof(buf[2])));
+ ifm->ifam_flags, addr, masklen, brd, &dst,
+ &gateway);
} break;
default:
zlog_debug("%s: ifindex %d, ifname %s, ifam_addrs {%s}",
@@ -951,7 +945,7 @@ static int rtm_read_mesg(struct rt_msghdr *rtm, union sockunion *dest,
/* rt_msghdr version check. */
if (rtm->rtm_version != RTM_VERSION)
flog_warn(EC_ZEBRA_RTM_VERSION_MISMATCH,
- "Routing message version different %d should be %d.This may cause problem\n",
+ "Routing message version different %d should be %d.This may cause problem",
rtm->rtm_version, RTM_VERSION);
/* Be sure structure is cleared */
@@ -1463,6 +1457,14 @@ void kernel_terminate(struct zebra_ns *zns, bool complete)
return;
}
+/*
+ * Called by the dplane pthread to read incoming OS messages and dispatch them.
+ */
+int kernel_dplane_read(struct zebra_dplane_info *info)
+{
+ return 0;
+}
+
void kernel_update_multi(struct dplane_ctx_q *ctx_list)
{
struct zebra_dplane_ctx *ctx;
diff --git a/zebra/rt.h b/zebra/rt.h
index 929a44ade7..90148d2c0d 100644
--- a/zebra/rt.h
+++ b/zebra/rt.h
@@ -110,6 +110,11 @@ extern int kernel_del_mac_nhg(uint32_t nhg_id);
*/
extern void kernel_update_multi(struct dplane_ctx_q *ctx_list);
+/*
+ * Called by the dplane pthread to read incoming OS messages and dispatch them.
+ */
+int kernel_dplane_read(struct zebra_dplane_info *info);
+
#ifdef __cplusplus
}
#endif
diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c
index 39f865fbfc..ab06ea6438 100644
--- a/zebra/zebra_dplane.c
+++ b/zebra/zebra_dplane.c
@@ -37,11 +37,12 @@
#include "zebra/zebra_pbr.h"
#include "printfrr.h"
-/* Memory type for context blocks */
+/* Memory types */
DEFINE_MTYPE_STATIC(ZEBRA, DP_CTX, "Zebra DPlane Ctx");
DEFINE_MTYPE_STATIC(ZEBRA, DP_INTF, "Zebra DPlane Intf");
DEFINE_MTYPE_STATIC(ZEBRA, DP_PROV, "Zebra DPlane Provider");
DEFINE_MTYPE_STATIC(ZEBRA, DP_NETFILTER, "Zebra Netfilter Internal Object");
+DEFINE_MTYPE_STATIC(ZEBRA, DP_NS, "DPlane NSes");
#ifndef AOK
# define AOK 0
@@ -402,6 +403,19 @@ struct zebra_dplane_provider {
TAILQ_ENTRY(zebra_dplane_provider) dp_prov_link;
};
+/* Declare types for list of zns info objects */
+PREDECL_DLIST(zns_info_list);
+
+struct dplane_zns_info {
+ struct zebra_dplane_info info;
+
+ /* Read event */
+ struct thread *t_read;
+
+ /* List linkage */
+ struct zns_info_list_item link;
+};
+
/*
* Globals
*/
@@ -424,6 +438,9 @@ static struct zebra_dplane_globals {
/* Ordered list of providers */
TAILQ_HEAD(zdg_prov_q, zebra_dplane_provider) dg_providers_q;
+ /* List of info about each zns */
+ struct zns_info_list_head dg_zns_list;
+
/* Counter used to assign internal ids to providers */
uint32_t dg_provider_id;
@@ -498,6 +515,9 @@ static struct zebra_dplane_globals {
} zdplane_info;
+/* Instantiate zns list type */
+DECLARE_DLIST(zns_info_list, struct dplane_zns_info, link);
+
/*
* Lock and unlock for interactions with the zebra 'core' pthread
*/
@@ -690,6 +710,8 @@ static void dplane_ctx_free_internal(struct zebra_dplane_ctx *ctx)
case DPLANE_OP_ADDR_INSTALL:
case DPLANE_OP_ADDR_UNINSTALL:
+ case DPLANE_OP_INTF_ADDR_ADD:
+ case DPLANE_OP_INTF_ADDR_DEL:
/* Maybe free label string, if allocated */
if (ctx->u.intf.label != NULL &&
ctx->u.intf.label != ctx->u.intf.label_buf) {
@@ -1011,6 +1033,12 @@ const char *dplane_op2str(enum dplane_op_e op)
case DPLANE_OP_GRE_SET:
ret = "GRE_SET";
break;
+
+ case DPLANE_OP_INTF_ADDR_ADD:
+ return "INTF_ADDR_ADD";
+
+ case DPLANE_OP_INTF_ADDR_DEL:
+ return "INTF_ADDR_DEL";
}
return ret;
@@ -1108,6 +1136,21 @@ vrf_id_t dplane_ctx_get_vrf(const struct zebra_dplane_ctx *ctx)
return ctx->zd_vrf_id;
}
+/* In some paths we have only a namespace id */
+void dplane_ctx_set_ns_id(struct zebra_dplane_ctx *ctx, ns_id_t nsid)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->zd_ns_info.ns_id = nsid;
+}
+
+ns_id_t dplane_ctx_get_ns_id(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->zd_ns_info.ns_id;
+}
+
bool dplane_ctx_is_from_notif(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
@@ -1154,6 +1197,13 @@ ifindex_t dplane_ctx_get_ifindex(const struct zebra_dplane_ctx *ctx)
return ctx->zd_ifindex;
}
+void dplane_ctx_set_ifindex(struct zebra_dplane_ctx *ctx, ifindex_t ifindex)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->zd_ifindex = ifindex;
+}
+
void dplane_ctx_set_type(struct zebra_dplane_ctx *ctx, int type)
{
DPLANE_CTX_VALID(ctx);
@@ -1669,6 +1719,13 @@ uint32_t dplane_ctx_get_intf_metric(const struct zebra_dplane_ctx *ctx)
return ctx->u.intf.metric;
}
+void dplane_ctx_set_intf_metric(struct zebra_dplane_ctx *ctx, uint32_t metric)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.intf.metric = metric;
+}
+
/* Is interface addr p2p? */
bool dplane_ctx_intf_is_connected(const struct zebra_dplane_ctx *ctx)
{
@@ -1691,6 +1748,27 @@ bool dplane_ctx_intf_is_broadcast(const struct zebra_dplane_ctx *ctx)
return (ctx->u.intf.flags & DPLANE_INTF_BROADCAST);
}
+void dplane_ctx_intf_set_connected(struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.intf.flags |= DPLANE_INTF_CONNECTED;
+}
+
+void dplane_ctx_intf_set_secondary(struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.intf.flags |= DPLANE_INTF_SECONDARY;
+}
+
+void dplane_ctx_intf_set_broadcast(struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.intf.flags |= DPLANE_INTF_BROADCAST;
+}
+
const struct prefix *dplane_ctx_get_intf_addr(
const struct zebra_dplane_ctx *ctx)
{
@@ -1699,6 +1777,14 @@ const struct prefix *dplane_ctx_get_intf_addr(
return &(ctx->u.intf.prefix);
}
+void dplane_ctx_set_intf_addr(struct zebra_dplane_ctx *ctx,
+ const struct prefix *p)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ prefix_copy(&(ctx->u.intf.prefix), p);
+}
+
bool dplane_ctx_intf_has_dest(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
@@ -1711,10 +1797,15 @@ const struct prefix *dplane_ctx_get_intf_dest(
{
DPLANE_CTX_VALID(ctx);
- if (ctx->u.intf.flags & DPLANE_INTF_HAS_DEST)
- return &(ctx->u.intf.dest_prefix);
- else
- return NULL;
+ return &(ctx->u.intf.dest_prefix);
+}
+
+void dplane_ctx_set_intf_dest(struct zebra_dplane_ctx *ctx,
+ const struct prefix *p)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ prefix_copy(&(ctx->u.intf.dest_prefix), p);
}
bool dplane_ctx_intf_has_label(const struct zebra_dplane_ctx *ctx)
@@ -1731,6 +1822,35 @@ const char *dplane_ctx_get_intf_label(const struct zebra_dplane_ctx *ctx)
return ctx->u.intf.label;
}
+void dplane_ctx_set_intf_label(struct zebra_dplane_ctx *ctx, const char *label)
+{
+ size_t len;
+
+ DPLANE_CTX_VALID(ctx);
+
+ if (ctx->u.intf.label && ctx->u.intf.label != ctx->u.intf.label_buf)
+ free(ctx->u.intf.label);
+
+ ctx->u.intf.label = NULL;
+
+ if (label) {
+ ctx->u.intf.flags |= DPLANE_INTF_HAS_LABEL;
+
+ /* Use embedded buffer if it's adequate; else allocate. */
+ len = strlen(label);
+
+ if (len < sizeof(ctx->u.intf.label_buf)) {
+ strlcpy(ctx->u.intf.label_buf, label,
+ sizeof(ctx->u.intf.label_buf));
+ ctx->u.intf.label = ctx->u.intf.label_buf;
+ } else {
+ ctx->u.intf.label = strdup(label);
+ }
+ } else {
+ ctx->u.intf.flags &= ~DPLANE_INTF_HAS_LABEL;
+ }
+}
+
/* Accessors for MAC information */
vlanid_t dplane_ctx_mac_get_vlan(const struct zebra_dplane_ctx *ctx)
{
@@ -2177,9 +2297,9 @@ static int dplane_ctx_ns_init(struct zebra_dplane_ctx *ctx,
* two messages in some 'update' cases.
*/
if (is_update)
- zns->netlink_dplane.seq += 2;
+ zns->netlink_dplane_out.seq += 2;
else
- zns->netlink_dplane.seq++;
+ zns->netlink_dplane_out.seq++;
#endif /* HAVE_NETLINK */
return AOK;
@@ -4709,10 +4829,92 @@ static void dplane_info_from_zns(struct zebra_dplane_info *ns_info,
#if defined(HAVE_NETLINK)
ns_info->is_cmd = true;
- ns_info->nls = zns->netlink_dplane;
+ ns_info->nls = zns->netlink_dplane_out;
#endif /* NETLINK */
}
+#ifdef HAVE_NETLINK
+/*
+ * Callback when an OS (netlink) incoming event read is ready. This runs
+ * in the dplane pthread.
+ */
+static int dplane_incoming_read(struct thread *event)
+{
+ struct dplane_zns_info *zi = THREAD_ARG(event);
+
+ kernel_dplane_read(&zi->info);
+
+ /* Re-start read task */
+ thread_add_read(zdplane_info.dg_master, dplane_incoming_read, zi,
+ zi->info.nls.sock, &zi->t_read);
+
+ return 0;
+}
+#endif /* HAVE_NETLINK */
+
+/*
+ * Notify dplane when namespaces are enabled and disabled. The dplane
+ * needs to start and stop reading incoming events from the zns. In the
+ * common case where vrfs are _not_ namespaces, there will only be one
+ * of these.
+ *
+ * This is called in the main pthread.
+ */
+void zebra_dplane_ns_enable(struct zebra_ns *zns, bool enabled)
+{
+ struct dplane_zns_info *zi;
+
+ if (IS_ZEBRA_DEBUG_DPLANE)
+ zlog_debug("%s: %s for nsid %u", __func__,
+ (enabled ? "ENABLED" : "DISABLED"), zns->ns_id);
+
+ /* Search for an existing zns info entry */
+ frr_each (zns_info_list, &zdplane_info.dg_zns_list, zi) {
+ if (zi->info.ns_id == zns->ns_id)
+ break;
+ }
+
+ if (enabled) {
+ /* Create a new entry if necessary; start reading. */
+ if (zi == NULL) {
+ zi = XCALLOC(MTYPE_DP_NS, sizeof(*zi));
+
+ zi->info.ns_id = zns->ns_id;
+
+ zns_info_list_add_tail(&zdplane_info.dg_zns_list, zi);
+
+ if (IS_ZEBRA_DEBUG_DPLANE)
+ zlog_debug("%s: nsid %u, new zi %p", __func__,
+ zns->ns_id, zi);
+ }
+
+ /* Make sure we're up-to-date with the zns object */
+#if defined(HAVE_NETLINK)
+ zi->info.is_cmd = false;
+ zi->info.nls = zns->netlink_dplane_in;
+
+ /* Start read task for the dplane pthread. */
+ if (zdplane_info.dg_master)
+ thread_add_read(zdplane_info.dg_master,
+ dplane_incoming_read, zi,
+ zi->info.nls.sock, &zi->t_read);
+#endif
+ } else if (zi) {
+ if (IS_ZEBRA_DEBUG_DPLANE)
+ zlog_debug("%s: nsid %u, deleting zi %p", __func__,
+ zns->ns_id, zi);
+
+ /* Stop reading, free memory */
+ zns_info_list_del(&zdplane_info.dg_zns_list, zi);
+
+ if (zdplane_info.dg_master)
+ thread_cancel_async(zdplane_info.dg_master, &zi->t_read,
+ NULL);
+
+ XFREE(MTYPE_DP_NS, zi);
+ }
+}
+
/*
* Provider api to signal that work/events are available
* for the dataplane pthread.
@@ -4878,6 +5080,14 @@ static void kernel_dplane_log_detail(struct zebra_dplane_ctx *ctx)
dplane_ctx_get_ifname(ctx),
ctx->u.gre.link_ifindex);
break;
+
+ case DPLANE_OP_INTF_ADDR_ADD:
+ case DPLANE_OP_INTF_ADDR_DEL:
+ zlog_debug("Dplane incoming op %s, intf %s, addr %pFX",
+ dplane_op2str(dplane_ctx_get_op(ctx)),
+ dplane_ctx_get_ifname(ctx),
+ dplane_ctx_get_intf_addr(ctx));
+ break;
}
}
@@ -5020,6 +5230,11 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx)
case DPLANE_OP_BR_PORT_UPDATE:
break;
+ /* TODO -- error counters for incoming events? */
+ case DPLANE_OP_INTF_ADDR_ADD:
+ case DPLANE_OP_INTF_ADDR_DEL:
+ break;
+
case DPLANE_OP_NONE:
if (res != ZEBRA_DPLANE_REQUEST_SUCCESS)
atomic_fetch_add_explicit(&zdplane_info.dg_other_errors,
@@ -5355,9 +5570,21 @@ done:
*/
static int dplane_check_shutdown_status(struct thread *event)
{
+ struct dplane_zns_info *zi;
+
if (IS_ZEBRA_DEBUG_DPLANE)
zlog_debug("Zebra dataplane shutdown status check called");
+ /* Remove any zns info entries as we stop the dplane pthread. */
+ frr_each_safe (zns_info_list, &zdplane_info.dg_zns_list, zi) {
+ zns_info_list_del(&zdplane_info.dg_zns_list, zi);
+
+ if (zdplane_info.dg_master)
+ thread_cancel(&zi->t_read);
+
+ XFREE(MTYPE_DP_NS, zi);
+ }
+
if (dplane_work_pending()) {
/* Reschedule dplane check on a short timer */
thread_add_timer_msec(zdplane_info.dg_master,
@@ -5652,6 +5879,7 @@ static void zebra_dplane_init_internal(void)
TAILQ_INIT(&zdplane_info.dg_update_ctx_q);
TAILQ_INIT(&zdplane_info.dg_providers_q);
+ zns_info_list_init(&zdplane_info.dg_zns_list);
zdplane_info.dg_updates_per_cycle = DPLANE_DEFAULT_NEW_WORK;
@@ -5667,6 +5895,7 @@ static void zebra_dplane_init_internal(void)
*/
void zebra_dplane_start(void)
{
+ struct dplane_zns_info *zi;
struct zebra_dplane_provider *prov;
struct frr_pthread_attr pattr = {
.start = frr_pthread_attr_default.start,
@@ -5686,6 +5915,14 @@ void zebra_dplane_start(void)
thread_add_event(zdplane_info.dg_master, dplane_thread_loop, NULL, 0,
&zdplane_info.dg_t_update);
+ /* Enqueue reads if necessary */
+ frr_each (zns_info_list, &zdplane_info.dg_zns_list, zi) {
+#if defined(HAVE_NETLINK)
+ thread_add_read(zdplane_info.dg_master, dplane_incoming_read,
+ zi, zi->info.nls.sock, &zi->t_read);
+#endif
+ }
+
/* Call start callbacks for registered providers */
DPLANE_LOCK();
diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h
index 3f3ff4de0f..a23de61c80 100644
--- a/zebra/zebra_dplane.h
+++ b/zebra/zebra_dplane.h
@@ -64,6 +64,12 @@ zebra_dplane_info_from_zns(struct zebra_dplane_info *zns_info,
}
/*
+ * Notify dplane when namespaces are enabled and disabled. The dplane
+ * needs to start and stop reading incoming events from the ns.
+ */
+void zebra_dplane_ns_enable(struct zebra_ns *zns, bool enabled);
+
+/*
* Result codes used when returning status back to the main zebra context.
*/
@@ -98,7 +104,7 @@ enum zebra_dplane_result {
*/
/*
- * Enqueue a route install or update for the dataplane.
+ * Operations that the dataplane can process.
*/
enum dplane_op_e {
DPLANE_OP_NONE = 0,
@@ -172,6 +178,10 @@ enum dplane_op_e {
DPLANE_OP_NEIGH_TABLE_UPDATE,
DPLANE_OP_GRE_SET,
+
+ /* Incoming interface address events */
+ DPLANE_OP_INTF_ADDR_ADD,
+ DPLANE_OP_INTF_ADDR_DEL,
};
/*
@@ -284,6 +294,7 @@ void dplane_ctx_set_dest(struct zebra_dplane_ctx *ctx,
const char *dplane_ctx_get_ifname(const struct zebra_dplane_ctx *ctx);
void dplane_ctx_set_ifname(struct zebra_dplane_ctx *ctx, const char *ifname);
ifindex_t dplane_ctx_get_ifindex(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_ifindex(struct zebra_dplane_ctx *ctx, ifindex_t ifindex);
/* Retrieve last/current provider id */
uint32_t dplane_ctx_get_provider(const struct zebra_dplane_ctx *ctx);
@@ -306,6 +317,10 @@ uint32_t dplane_ctx_get_old_seq(const struct zebra_dplane_ctx *ctx);
void dplane_ctx_set_vrf(struct zebra_dplane_ctx *ctx, vrf_id_t vrf);
vrf_id_t dplane_ctx_get_vrf(const struct zebra_dplane_ctx *ctx);
+/* In some paths we have only a namespace id */
+void dplane_ctx_set_ns_id(struct zebra_dplane_ctx *ctx, ns_id_t nsid);
+ns_id_t dplane_ctx_get_ns_id(const struct zebra_dplane_ctx *ctx);
+
bool dplane_ctx_is_from_notif(const struct zebra_dplane_ctx *ctx);
void dplane_ctx_set_notif_provider(struct zebra_dplane_ctx *ctx,
uint32_t id);
@@ -441,17 +456,26 @@ dplane_ctx_get_pw_backup_nhg(const struct zebra_dplane_ctx *ctx);
/* Accessors for interface information */
uint32_t dplane_ctx_get_intf_metric(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_intf_metric(struct zebra_dplane_ctx *ctx, uint32_t metric);
/* Is interface addr p2p? */
bool dplane_ctx_intf_is_connected(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_intf_set_connected(struct zebra_dplane_ctx *ctx);
bool dplane_ctx_intf_is_secondary(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_intf_set_secondary(struct zebra_dplane_ctx *ctx);
bool dplane_ctx_intf_is_broadcast(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_intf_set_broadcast(struct zebra_dplane_ctx *ctx);
const struct prefix *dplane_ctx_get_intf_addr(
const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_intf_addr(struct zebra_dplane_ctx *ctx,
+ const struct prefix *p);
bool dplane_ctx_intf_has_dest(const struct zebra_dplane_ctx *ctx);
const struct prefix *dplane_ctx_get_intf_dest(
const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_intf_dest(struct zebra_dplane_ctx *ctx,
+ const struct prefix *p);
bool dplane_ctx_intf_has_label(const struct zebra_dplane_ctx *ctx);
const char *dplane_ctx_get_intf_label(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_intf_label(struct zebra_dplane_ctx *ctx, const char *label);
/* Accessors for MAC information */
vlanid_t dplane_ctx_mac_get_vlan(const struct zebra_dplane_ctx *ctx);
diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c
index 68fb044353..168e36ac9b 100644
--- a/zebra/zebra_fpm_netlink.c
+++ b/zebra/zebra_fpm_netlink.c
@@ -281,7 +281,7 @@ static int netlink_route_info_fill(struct netlink_route_info *ri, int cmd,
ri->af = rib_dest_af(dest);
if (zvrf && zvrf->zns)
- ri->nlmsg_pid = zvrf->zns->netlink_dplane.snl.nl_pid;
+ ri->nlmsg_pid = zvrf->zns->netlink_dplane_out.snl.nl_pid;
ri->nlmsg_type = cmd;
ri->rtm_table = table_info->table_id;
diff --git a/zebra/zebra_l2.c b/zebra/zebra_l2.c
index 71fac556e1..5a02149611 100644
--- a/zebra/zebra_l2.c
+++ b/zebra/zebra_l2.c
@@ -50,7 +50,8 @@
/* static function declarations */
/* Private functions */
-static void map_slaves_to_bridge(struct interface *br_if, int link)
+static void map_slaves_to_bridge(struct interface *br_if, int link,
+ bool update_slave, uint8_t chgflags)
{
struct vrf *vrf;
struct interface *ifp;
@@ -79,9 +80,17 @@ static void map_slaves_to_bridge(struct interface *br_if, int link)
br_slave = &zif->brslave_info;
if (link) {
- if (br_slave->bridge_ifindex == br_if->ifindex &&
- br_slave->ns_id == zns->ns_id)
+ if (br_slave->bridge_ifindex == br_if->ifindex
+ && br_slave->ns_id == zns->ns_id) {
br_slave->br_if = br_if;
+ if (update_slave) {
+ zebra_l2if_update_bridge_slave(
+ ifp,
+ br_slave->bridge_ifindex,
+ br_slave->ns_id,
+ chgflags);
+ }
+ }
} else {
if (br_slave->br_if == br_if)
br_slave->br_if = NULL;
@@ -261,7 +270,7 @@ void zebra_l2_bridge_add_update(struct interface *ifp,
memcpy(&zif->l2info.br, bridge_info, sizeof(*bridge_info));
/* Link all slaves to this bridge */
- map_slaves_to_bridge(ifp, 1);
+ map_slaves_to_bridge(ifp, 1, false, ZEBRA_BRIDGE_NO_ACTION);
}
/*
@@ -270,7 +279,14 @@ void zebra_l2_bridge_add_update(struct interface *ifp,
void zebra_l2_bridge_del(struct interface *ifp)
{
/* Unlink all slaves to this bridge */
- map_slaves_to_bridge(ifp, 0);
+ map_slaves_to_bridge(ifp, 0, false, ZEBRA_BRIDGE_NO_ACTION);
+}
+
+void zebra_l2if_update_bridge(struct interface *ifp, uint8_t chgflags)
+{
+ if (!chgflags)
+ return;
+ map_slaves_to_bridge(ifp, 1, true, chgflags);
}
/*
@@ -398,8 +414,8 @@ void zebra_l2_vxlanif_del(struct interface *ifp)
* from a bridge before it can be mapped to another bridge.
*/
void zebra_l2if_update_bridge_slave(struct interface *ifp,
- ifindex_t bridge_ifindex,
- ns_id_t ns_id)
+ ifindex_t bridge_ifindex, ns_id_t ns_id,
+ uint8_t chgflags)
{
struct zebra_if *zif;
ifindex_t old_bridge_ifindex;
@@ -413,6 +429,14 @@ void zebra_l2if_update_bridge_slave(struct interface *ifp,
if (!zvrf)
return;
+ if (zif->zif_type == ZEBRA_IF_VXLAN
+ && chgflags != ZEBRA_BRIDGE_NO_ACTION) {
+ if (ZEBRA_BRIDGE_MASTER_MAC_CHANGE)
+ zebra_vxlan_if_update(ifp,
+ ZEBRA_VXLIF_MASTER_MAC_CHANGE);
+ if (ZEBRA_BRIDGE_MASTER_UP)
+ zebra_vxlan_if_update(ifp, ZEBRA_VXLIF_MASTER_CHANGE);
+ }
old_bridge_ifindex = zif->brslave_info.bridge_ifindex;
old_ns_id = zif->brslave_info.ns_id;
if (old_bridge_ifindex == bridge_ifindex &&
diff --git a/zebra/zebra_l2.h b/zebra/zebra_l2.h
index 6572f344c4..98744f3c1f 100644
--- a/zebra/zebra_l2.h
+++ b/zebra/zebra_l2.h
@@ -33,6 +33,10 @@
extern "C" {
#endif
+#define ZEBRA_BRIDGE_NO_ACTION (0)
+#define ZEBRA_BRIDGE_MASTER_MAC_CHANGE (1 << 1)
+#define ZEBRA_BRIDGE_MASTER_UP (1 << 2)
+
/* zebra L2 interface information - bridge slave (linkage to bridge) */
struct zebra_l2info_brslave {
ifindex_t bridge_ifindex; /* Bridge Master */
@@ -121,7 +125,7 @@ extern void zebra_l2_greif_del(struct interface *ifp);
extern void zebra_l2_vxlanif_del(struct interface *ifp);
extern void zebra_l2if_update_bridge_slave(struct interface *ifp,
ifindex_t bridge_ifindex,
- ns_id_t ns_id);
+ ns_id_t ns_id, uint8_t chgflags);
extern void zebra_l2if_update_bond_slave(struct interface *ifp,
ifindex_t bond_ifindex, bool bypass);
@@ -130,6 +134,7 @@ extern void zebra_vlan_bitmap_compute(struct interface *ifp,
extern void zebra_vlan_mbr_re_eval(struct interface *ifp,
bitfield_t vlan_bitmap);
extern void zebra_l2if_update_bond(struct interface *ifp, bool add);
+extern void zebra_l2if_update_bridge(struct interface *ifp, uint8_t chgflags);
#ifdef __cplusplus
}
diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c
index aed4f8ca8d..aa015992d5 100644
--- a/zebra/zebra_nhg.c
+++ b/zebra/zebra_nhg.c
@@ -2987,6 +2987,8 @@ void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx)
case DPLANE_OP_IPSET_ENTRY_DELETE:
case DPLANE_OP_NEIGH_TABLE_UPDATE:
case DPLANE_OP_GRE_SET:
+ case DPLANE_OP_INTF_ADDR_ADD:
+ case DPLANE_OP_INTF_ADDR_DEL:
break;
}
diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c
index 27b8a3ea47..8ae677fb22 100644
--- a/zebra/zebra_ns.c
+++ b/zebra/zebra_ns.c
@@ -123,6 +123,7 @@ int zebra_ns_enable(ns_id_t ns_id, void **info)
zns->ns_id = ns_id;
kernel_init(zns);
+ zebra_dplane_ns_enable(zns, true);
interface_list(zns);
route_read(zns);
kernel_read_pbr_rules(zns);
@@ -140,6 +141,8 @@ static int zebra_ns_disable_internal(struct zebra_ns *zns, bool complete)
{
route_table_finish(zns->if_table);
+ zebra_dplane_ns_enable(zns, false /*Disable*/);
+
kernel_terminate(zns, complete);
table_manager_disable(zns->ns_id);
diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h
index f7d1f40782..8237de7dde 100644
--- a/zebra/zebra_ns.h
+++ b/zebra/zebra_ns.h
@@ -52,7 +52,12 @@ struct zebra_ns {
#ifdef HAVE_NETLINK
struct nlsock netlink; /* kernel messages */
struct nlsock netlink_cmd; /* command channel */
- struct nlsock netlink_dplane; /* dataplane channel */
+
+ /* dplane system's channels: one for outgoing programming,
+ * for the FIB e.g., and one for incoming events from the OS.
+ */
+ struct nlsock netlink_dplane_out;
+ struct nlsock netlink_dplane_in;
struct thread *t_netlink;
#endif
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index 1fb4e5e6fc..24c51e485f 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -4209,6 +4209,11 @@ static int rib_process_dplane_results(struct thread *thread)
zebra_pbr_dplane_result(ctx);
break;
+ case DPLANE_OP_INTF_ADDR_ADD:
+ case DPLANE_OP_INTF_ADDR_DEL:
+ zebra_if_addr_update_ctx(ctx);
+ break;
+
/* Some op codes not handled here */
case DPLANE_OP_ADDR_INSTALL:
case DPLANE_OP_ADDR_UNINSTALL:
diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c
index aede4098b4..c13c867d2a 100644
--- a/zebra/zebra_vxlan.c
+++ b/zebra/zebra_vxlan.c
@@ -1798,6 +1798,51 @@ struct zebra_l3vni *zl3vni_from_vrf(vrf_id_t vrf_id)
return zl3vni_lookup(zvrf->l3vni);
}
+static int zl3vni_from_svi_ns(struct ns *ns, void *_in_param, void **_p_zl3vni)
+{
+ int found = 0;
+ struct zebra_ns *zns = ns->info;
+ struct zebra_l3vni **p_zl3vni = (struct zebra_l3vni **)_p_zl3vni;
+ struct zebra_from_svi_param *in_param =
+ (struct zebra_from_svi_param *)_in_param;
+ struct route_node *rn = NULL;
+ struct interface *tmp_if = NULL;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
+
+ if (!in_param)
+ return NS_WALK_STOP;
+
+ /* loop through all vxlan-interface */
+ for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) {
+ tmp_if = (struct interface *)rn->info;
+ if (!tmp_if)
+ continue;
+ zif = tmp_if->info;
+ if (!zif || zif->zif_type != ZEBRA_IF_VXLAN)
+ continue;
+ if (!if_is_operative(tmp_if))
+ continue;
+ vxl = &zif->l2info.vxl;
+
+ if (zif->brslave_info.br_if != in_param->br_if)
+ continue;
+
+ if (!in_param->bridge_vlan_aware
+ || vxl->access_vlan == in_param->vid) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ return NS_WALK_CONTINUE;
+
+ if (p_zl3vni)
+ *p_zl3vni = zl3vni_lookup(vxl->vni);
+ return NS_WALK_STOP;
+}
+
/*
* Map SVI and associated bridge to a VNI. This is invoked upon getting
* neighbor notifications, to see if they are of interest.
@@ -1805,16 +1850,11 @@ struct zebra_l3vni *zl3vni_from_vrf(vrf_id_t vrf_id)
static struct zebra_l3vni *zl3vni_from_svi(struct interface *ifp,
struct interface *br_if)
{
- int found = 0;
- vlanid_t vid = 0;
- uint8_t bridge_vlan_aware = 0;
struct zebra_l3vni *zl3vni = NULL;
- struct zebra_ns *zns = NULL;
- struct route_node *rn = NULL;
struct zebra_if *zif = NULL;
- struct interface *tmp_if = NULL;
struct zebra_l2info_bridge *br = NULL;
- struct zebra_l2info_vxlan *vxl = NULL;
+ struct zebra_from_svi_param in_param = {};
+ struct zebra_l3vni **p_zl3vni;
if (!br_if)
return NULL;
@@ -1822,13 +1862,14 @@ static struct zebra_l3vni *zl3vni_from_svi(struct interface *ifp,
/* Make sure the linked interface is a bridge. */
if (!IS_ZEBRA_IF_BRIDGE(br_if))
return NULL;
+ in_param.br_if = br_if;
/* Determine if bridge is VLAN-aware or not */
zif = br_if->info;
assert(zif);
br = &zif->l2info.br;
- bridge_vlan_aware = br->vlan_aware;
- if (bridge_vlan_aware) {
+ in_param.bridge_vlan_aware = br->vlan_aware;
+ if (in_param.bridge_vlan_aware) {
struct zebra_l2info_vlan *vl;
if (!IS_ZEBRA_IF_VLAN(ifp))
@@ -1837,36 +1878,15 @@ static struct zebra_l3vni *zl3vni_from_svi(struct interface *ifp,
zif = ifp->info;
assert(zif);
vl = &zif->l2info.vl;
- vid = vl->vid;
+ in_param.vid = vl->vid;
}
/* See if this interface (or interface plus VLAN Id) maps to a VxLAN */
/* TODO: Optimize with a hash. */
- zns = zebra_ns_lookup(NS_DEFAULT);
- for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) {
- tmp_if = (struct interface *)rn->info;
- if (!tmp_if)
- continue;
- zif = tmp_if->info;
- if (!zif || zif->zif_type != ZEBRA_IF_VXLAN)
- continue;
- if (!if_is_operative(tmp_if))
- continue;
- vxl = &zif->l2info.vxl;
-
- if (zif->brslave_info.br_if != br_if)
- continue;
- if (!bridge_vlan_aware || vxl->access_vlan == vid) {
- found = 1;
- break;
- }
- }
+ p_zl3vni = &zl3vni;
- if (!found)
- return NULL;
-
- zl3vni = zl3vni_lookup(vxl->vni);
+ ns_walk_func(zl3vni_from_svi_ns, (void *)&in_param, (void **)p_zl3vni);
return zl3vni;
}
@@ -5023,6 +5043,13 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags)
return 0;
}
+ if ((chgflags & ZEBRA_VXLIF_MASTER_MAC_CHANGE)
+ && if_is_operative(ifp) && is_l3vni_oper_up(zl3vni)) {
+ zebra_vxlan_process_l3vni_oper_down(zl3vni);
+ zebra_vxlan_process_l3vni_oper_up(zl3vni);
+ return 0;
+ }
+
/* access-vlan change - process oper down, associate with new
* svi_if and then process oper up again
*/
diff --git a/zebra/zebra_vxlan.h b/zebra/zebra_vxlan.h
index 915e987b6b..464a8e5fc4 100644
--- a/zebra/zebra_vxlan.h
+++ b/zebra/zebra_vxlan.h
@@ -65,6 +65,7 @@ is_vxlan_flooding_head_end(void)
#define ZEBRA_VXLIF_MASTER_CHANGE (1 << 1)
#define ZEBRA_VXLIF_VLAN_CHANGE (1 << 2)
#define ZEBRA_VXLIF_MCAST_GRP_CHANGE (1 << 3)
+#define ZEBRA_VXLIF_MASTER_MAC_CHANGE (1 << 4)
#define VNI_STR_LEN 32