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