diff options
115 files changed, 4893 insertions, 330 deletions
diff --git a/.github/workflows/conflicts.yml b/.github/workflows/conflicts.yml new file mode 100644 index 0000000000..18a8c0d15d --- /dev/null +++ b/.github/workflows/conflicts.yml @@ -0,0 +1,21 @@ +name: Add a conflict label is PR needs to rebase + +on: + push: + pull_request_target: + types: [synchronize] + +jobs: + conflicts: + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + steps: + - name: Check if PRs need a rebase (have some conflicts) + uses: eps1lon/actions-label-merge-conflict@releases/2.x + with: + dirtyLabel: "conflicts" + removeOnDirtyLabel: "no_conflicts" + repoToken: "${{ secrets.GITHUB_TOKEN }}" + commentOnDirty: "This pull request has conflicts, please resolve those before we can evaluate the pull request." diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index 4963ea64d0..41ae6ef49c 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -80,14 +80,6 @@ #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 : \ ((attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV4) ? AFI_IP : AFI_IP6)) diff --git a/bgpd/bgp_community.h b/bgpd/bgp_community.h index 616ddb4405..05a5d4486a 100644 --- a/bgpd/bgp_community.h +++ b/bgpd/bgp_community.h @@ -23,6 +23,7 @@ #include "lib/json.h" #include "bgpd/bgp_route.h" +#include "bgpd/bgp_attr.h" /* Communities attribute. */ struct community { @@ -109,4 +110,30 @@ extern void bgp_remove_comm_from_aggregate_hash(struct bgp_aggregate *aggregate, struct community *community); extern void bgp_aggr_community_remove(void *arg); +/* This implies that when propagating routes into a VRF, the ACCEPT_OWN + * community SHOULD NOT be propagated. + */ +static inline void community_strip_accept_own(struct attr *attr) +{ + struct community *old_com = bgp_attr_get_community(attr); + struct community *new_com = NULL; + uint32_t val = COMMUNITY_ACCEPT_OWN; + + if (old_com && community_include(old_com, val)) { + new_com = community_dup(old_com); + val = htonl(val); + community_del_val(new_com, &val); + + if (!old_com->refcnt) + community_free(&old_com); + + if (!new_com->size) { + community_free(&new_com); + bgp_attr_set_community(attr, NULL); + } else { + bgp_attr_set_community(attr, new_com); + } + } +} + #endif /* _QUAGGA_BGP_COMMUNITY_H */ diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index 264dd85fbc..580c18b58d 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -71,6 +71,7 @@ unsigned long conf_bgp_debug_graceful_restart; unsigned long conf_bgp_debug_evpn_mh; unsigned long conf_bgp_debug_bfd; unsigned long conf_bgp_debug_cond_adv; +unsigned long conf_bgp_debug_optimal_route_reflection; unsigned long term_bgp_debug_as4; unsigned long term_bgp_debug_neighbor_events; @@ -92,6 +93,7 @@ unsigned long term_bgp_debug_graceful_restart; unsigned long term_bgp_debug_evpn_mh; unsigned long term_bgp_debug_bfd; unsigned long term_bgp_debug_cond_adv; +unsigned long term_bgp_debug_optimal_route_reflection; struct list *bgp_debug_neighbor_events_peers = NULL; struct list *bgp_debug_keepalive_peers = NULL; @@ -2044,6 +2046,33 @@ DEFPY (debug_bgp_evpn_mh, return CMD_SUCCESS; } +DEFPY (debug_bgp_optimal_route_reflection, + debug_bgp_optimal_route_reflection_cmd, + "[no$no] debug bgp optimal-route-reflection", + NO_STR + DEBUG_STR + BGP_STR + BGP_ORR_DEBUG) +{ + if (vty->node == CONFIG_NODE) { + if (no) + DEBUG_OFF(optimal_route_reflection, ORR); + else + DEBUG_ON(optimal_route_reflection, ORR); + } else { + if (no) { + TERM_DEBUG_OFF(optimal_route_reflection, ORR); + vty_out(vty, + "BGP Optimal Route Reflection debugging is off\n"); + } else { + TERM_DEBUG_ON(optimal_route_reflection, ORR); + vty_out(vty, + "BGP Optimal Route Reflection debugging is on\n"); + } + } + return CMD_SUCCESS; +} + DEFUN (debug_bgp_labelpool, debug_bgp_labelpool_cmd, "debug bgp labelpool", @@ -2182,6 +2211,7 @@ DEFUN (no_debug_bgp, TERM_DEBUG_OFF(evpn_mh, EVPN_MH_RT); TERM_DEBUG_OFF(bfd, BFD_LIB); TERM_DEBUG_OFF(cond_adv, COND_ADV); + TERM_DEBUG_OFF(optimal_route_reflection, ORR); vty_out(vty, "All possible debugging has been turned off\n"); @@ -2278,6 +2308,10 @@ DEFUN_NOSH (show_debugging_bgp, vty_out(vty, " BGP conditional advertisement debugging is on\n"); + if (BGP_DEBUG(optimal_route_reflection, ORR)) + vty_out(vty, + " BGP Optimal Route Reflection debugging is on\n"); + cmd_show_lib_debugs(vty); return CMD_SUCCESS; @@ -2414,6 +2448,11 @@ static int bgp_config_write_debug(struct vty *vty) write++; } + if (CONF_BGP_DEBUG(optimal_route_reflection, ORR)) { + vty_out(vty, "debug bgp optimal-route-reflection\n"); + write++; + } + return write; } @@ -2546,6 +2585,10 @@ void bgp_debug_init(void) /* debug bgp conditional advertisement */ install_element(ENABLE_NODE, &debug_bgp_cond_adv_cmd); install_element(CONFIG_NODE, &debug_bgp_cond_adv_cmd); + + /* debug bgp optimal route reflection */ + install_element(ENABLE_NODE, &debug_bgp_optimal_route_reflection_cmd); + install_element(CONFIG_NODE, &debug_bgp_optimal_route_reflection_cmd); } /* Return true if this prefix is on the per_prefix_list of prefixes to debug diff --git a/bgpd/bgp_debug.h b/bgpd/bgp_debug.h index be5ed0afdc..f7090260ac 100644 --- a/bgpd/bgp_debug.h +++ b/bgpd/bgp_debug.h @@ -81,6 +81,7 @@ extern unsigned long conf_bgp_debug_graceful_restart; extern unsigned long conf_bgp_debug_evpn_mh; extern unsigned long conf_bgp_debug_bfd; extern unsigned long conf_bgp_debug_cond_adv; +extern unsigned long conf_bgp_debug_optimal_route_reflection; extern unsigned long term_bgp_debug_as4; extern unsigned long term_bgp_debug_neighbor_events; @@ -100,6 +101,7 @@ extern unsigned long term_bgp_debug_graceful_restart; extern unsigned long term_bgp_debug_evpn_mh; extern unsigned long term_bgp_debug_bfd; extern unsigned long term_bgp_debug_cond_adv; +extern unsigned long term_bgp_debug_optimal_route_reflection; extern struct list *bgp_debug_neighbor_events_peers; extern struct list *bgp_debug_keepalive_peers; @@ -138,6 +140,7 @@ struct bgp_debug_filter { #define BGP_DEBUG_PBR_ERROR 0x02 #define BGP_DEBUG_EVPN_MH_ES 0x01 #define BGP_DEBUG_EVPN_MH_RT 0x02 +#define BGP_DEBUG_ORR 0x01 #define BGP_DEBUG_PACKET_SEND 0x01 #define BGP_DEBUG_PACKET_SEND_DETAIL 0x02 diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index ddda100774..a85432a33b 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -1330,6 +1330,9 @@ void bgp_fsm_change_status(struct peer *peer, int status) && bgp_update_delay_applicable(peer->bgp)) bgp_update_delay_process_status_change(peer); + /* BGP ORR : Update Active Root */ + bgp_peer_update_orr_active_roots(peer); + if (bgp_debug_neighbor_events(peer)) zlog_debug("%s went from %s to %s", peer->host, lookup_msg(bgp_status_msg, peer->ostatus, NULL), diff --git a/bgpd/bgp_fsm.h b/bgpd/bgp_fsm.h index aaf6c480b2..368c2c5001 100644 --- a/bgpd/bgp_fsm.h +++ b/bgpd/bgp_fsm.h @@ -175,4 +175,6 @@ const char *print_peer_gr_cmd(enum peer_gr_command pr_gr_cmd); const char *print_global_gr_mode(enum global_mode gl_mode); const char *print_global_gr_cmd(enum global_gr_command gl_gr_cmd); int bgp_peer_reg_with_nht(struct peer *peer); + +extern void bgp_peer_update_orr_active_roots(struct peer *peer); #endif /* _QUAGGA_BGP_FSM_H */ diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c index 850657d35e..ced3e1890e 100644 --- a/bgpd/bgp_memory.c +++ b/bgpd/bgp_memory.c @@ -134,8 +134,12 @@ DEFINE_MTYPE(BGPD, BGP_EVPN_VRF_IMPORT_RT, "BGP EVPN VRF Import RT"); DEFINE_MTYPE(BGPD, BGP_SRV6_L3VPN, "BGP prefix-sid srv6 l3vpn servcie"); DEFINE_MTYPE(BGPD, BGP_SRV6_VPN, "BGP prefix-sid srv6 vpn service"); + DEFINE_MTYPE(BGPD, BGP_SRV6_SID, "BGP srv6 segment-id"); DEFINE_MTYPE(BGPD, BGP_SRV6_FUNCTION, "BGP srv6 function"); DEFINE_MTYPE(BGPD, EVPN_REMOTE_IP, "BGP EVPN Remote IP hash entry"); DEFINE_MTYPE(BGPD, BGP_NOTIFICATION, "BGP Notification Message"); +DEFINE_MTYPE(BGPD, BGP_ORR_GROUP, "BGP Optimal Route Reflection Group"); +DEFINE_MTYPE(BGPD, BGP_ORR_GROUP_NAME, + "BGP Optimal Route Reflection Group Name"); diff --git a/bgpd/bgp_memory.h b/bgpd/bgp_memory.h index 510cfa21c9..990c6e1faa 100644 --- a/bgpd/bgp_memory.h +++ b/bgpd/bgp_memory.h @@ -137,5 +137,7 @@ DECLARE_MTYPE(BGP_SRV6_FUNCTION); DECLARE_MTYPE(EVPN_REMOTE_IP); DECLARE_MTYPE(BGP_NOTIFICATION); +DECLARE_MTYPE(BGP_ORR_GROUP); +DECLARE_MTYPE(BGP_ORR_GROUP_NAME); #endif /* _QUAGGA_BGP_MEMORY_H */ diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index d7fd4bc77e..66eef1aa52 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -42,6 +42,7 @@ #include "bgpd/bgp_packet.h" #include "bgpd/bgp_vty.h" #include "bgpd/bgp_vpn.h" +#include "bgpd/bgp_community.h" #include "bgpd/bgp_ecommunity.h" #include "bgpd/bgp_zebra.h" #include "bgpd/bgp_nexthop.h" @@ -524,37 +525,77 @@ static bool sid_exist(struct bgp *bgp, const struct in6_addr *sid) * else: try to allocate as auto-mode */ static uint32_t alloc_new_sid(struct bgp *bgp, uint32_t index, - struct in6_addr *sid_locator, + struct srv6_locator_chunk *sid_locator_chunk, struct in6_addr *sid) { + int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL); struct listnode *node; struct srv6_locator_chunk *chunk; bool alloced = false; int label = 0; uint8_t offset = 0; - uint8_t len = 0; + uint8_t func_len = 0, shift_len = 0; + uint32_t index_max = 0; - if (!bgp || !sid_locator || !sid) + if (!bgp || !sid_locator_chunk || !sid) return false; for (ALL_LIST_ELEMENTS_RO(bgp->srv6_locator_chunks, node, chunk)) { - *sid_locator = chunk->prefix.prefix; + if (chunk->function_bits_length > + BGP_PREFIX_SID_SRV6_MAX_FUNCTION_LENGTH) { + if (debug) + zlog_debug( + "%s: invalid SRv6 Locator chunk (%pFX): Function Length must be less or equal to %d", + __func__, &chunk->prefix, + BGP_PREFIX_SID_SRV6_MAX_FUNCTION_LENGTH); + continue; + } + + index_max = (1 << chunk->function_bits_length) - 1; + + if (index > index_max) { + if (debug) + zlog_debug( + "%s: skipped SRv6 Locator chunk (%pFX): Function Length is too short to support specified index (%u)", + __func__, &chunk->prefix, index); + continue; + } + *sid = chunk->prefix.prefix; + *sid_locator_chunk = *chunk; offset = chunk->block_bits_length + chunk->node_bits_length; - len = chunk->function_bits_length ?: 16; + func_len = chunk->function_bits_length; + shift_len = BGP_PREFIX_SID_SRV6_MAX_FUNCTION_LENGTH - func_len; if (index != 0) { - label = index << 12; - transpose_sid(sid, label, offset, len); + label = index << shift_len; + if (label < MPLS_LABEL_UNRESERVED_MIN) { + if (debug) + zlog_debug( + "%s: skipped to allocate SRv6 SID (%pFX): Label (%u) is too small to use", + __func__, &chunk->prefix, + label); + continue; + } + + transpose_sid(sid, label, offset, func_len); if (sid_exist(bgp, sid)) - return false; + continue; alloced = true; break; } - for (size_t i = 1; i < 255; i++) { - label = i << 12; - transpose_sid(sid, label, offset, len); + for (uint32_t i = 1; i < index_max; i++) { + label = i << shift_len; + if (label < MPLS_LABEL_UNRESERVED_MIN) { + if (debug) + zlog_debug( + "%s: skipped to allocate SRv6 SID (%pFX): Label (%u) is too small to use", + __func__, &chunk->prefix, + label); + continue; + } + transpose_sid(sid, label, offset, func_len); if (sid_exist(bgp, sid)) continue; alloced = true; @@ -573,7 +614,8 @@ 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); char buf[256]; - struct in6_addr *tovpn_sid, *tovpn_sid_locator; + struct srv6_locator_chunk *tovpn_sid_locator; + struct in6_addr *tovpn_sid; uint32_t tovpn_sid_index = 0, tovpn_sid_transpose_label; bool tovpn_sid_auto = false; @@ -607,8 +649,7 @@ void ensure_vrf_tovpn_sid(struct bgp *bgp_vpn, struct bgp *bgp_vrf, afi_t afi) return; } - tovpn_sid_locator = - XCALLOC(MTYPE_BGP_SRV6_SID, sizeof(struct in6_addr)); + tovpn_sid_locator = srv6_locator_chunk_alloc(); tovpn_sid = XCALLOC(MTYPE_BGP_SRV6_SID, sizeof(struct in6_addr)); tovpn_sid_transpose_label = alloc_new_sid(bgp_vpn, tovpn_sid_index, @@ -617,7 +658,7 @@ void ensure_vrf_tovpn_sid(struct bgp *bgp_vpn, struct bgp *bgp_vrf, afi_t afi) 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)); - XFREE(MTYPE_BGP_SRV6_SID, tovpn_sid_locator); + srv6_locator_chunk_free(tovpn_sid_locator); XFREE(MTYPE_BGP_SRV6_SID, tovpn_sid); return; } @@ -636,20 +677,30 @@ void ensure_vrf_tovpn_sid(struct bgp *bgp_vpn, struct bgp *bgp_vrf, afi_t afi) } /* - * This function shifts "label" 4 bits to the right and - * embeds it by length "len", starting at offset "offset" - * as seen from the MSB (Most Significant Bit) of "sid". + * This function embeds upper `len` bits of `label` in `sid`, + * starting at offset `offset` as seen from the MSB of `sid`. * - * e.g. if "label" is 0x1000 and "len" is 16, "label" is - * embedded in "sid" as follows: + * e.g. Given that `label` is 0x12345 and `len` is 16, + * then `label` will be embedded in `sid` as follows: * * <---- len -----> - * label: 0000 0001 0000 0000 0000 - * sid: .... 0000 0001 0000 0000 + * label: 0001 0002 0003 0004 0005 + * sid: .... 0001 0002 0003 0004 * <---- len -----> * ^ * | * offset from MSB + * + * e.g. Given that `label` is 0x12345 and `len` is 8, + * `label` will be embedded in `sid` as follows: + * + * <- len -> + * label: 0001 0002 0003 0004 0005 + * sid: .... 0001 0002 0000 0000 + * <- len -> + * ^ + * | + * offset from MSB */ void transpose_sid(struct in6_addr *sid, uint32_t label, uint8_t offset, uint8_t len) @@ -657,7 +708,7 @@ void transpose_sid(struct in6_addr *sid, uint32_t label, uint8_t offset, for (uint8_t idx = 0; idx < len; idx++) { uint8_t tidx = offset + idx; sid->s6_addr[tidx / 8] &= ~(0x1 << (7 - tidx % 8)); - if (label >> (len + 3 - idx) & 0x1) + if (label >> (19 - idx) & 0x1) sid->s6_addr[tidx / 8] |= 0x1 << (7 - tidx % 8); } } @@ -946,6 +997,9 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn, if (nexthop_self_flag) bgp_path_info_set_flag(bn, bpi, BGP_PATH_ANNC_NH_SELF); + if (CHECK_FLAG(source_bpi->flags, BGP_PATH_ACCEPT_OWN)) + bgp_path_info_set_flag(bn, bpi, BGP_PATH_ACCEPT_OWN); + if (leak_update_nexthop_valid(to_bgp, bn, new_attr, afi, safi, source_bpi, bpi, bgp_orig, p, debug)) @@ -986,6 +1040,9 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn, if (nexthop_self_flag) bgp_path_info_set_flag(bn, new, BGP_PATH_ANNC_NH_SELF); + if (CHECK_FLAG(source_bpi->flags, BGP_PATH_ACCEPT_OWN)) + bgp_path_info_set_flag(bn, new, BGP_PATH_ACCEPT_OWN); + bgp_path_info_extra_get(new); /* @@ -1167,6 +1224,8 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */ XFREE(MTYPE_ECOMMUNITY_STR, s); } + community_strip_accept_own(&static_attr); + /* Nexthop */ /* if policy nexthop not set, use 0 */ if (CHECK_FLAG(from_bgp->vpn_policy[afi].flags, @@ -1252,19 +1311,29 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */ 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; + from_bgp->vpn_policy[afi] + .tovpn_sid_locator->block_bits_length; static_attr.srv6_l3vpn->loc_node_len = - BGP_PREFIX_SID_SRV6_LOCATOR_NODE_LENGTH; + from_bgp->vpn_policy[afi] + .tovpn_sid_locator->node_bits_length; static_attr.srv6_l3vpn->func_len = - BGP_PREFIX_SID_SRV6_FUNCTION_LENGTH; + from_bgp->vpn_policy[afi] + .tovpn_sid_locator->function_bits_length; static_attr.srv6_l3vpn->arg_len = - BGP_PREFIX_SID_SRV6_ARGUMENT_LENGTH; + from_bgp->vpn_policy[afi] + .tovpn_sid_locator->argument_bits_length; static_attr.srv6_l3vpn->transposition_len = - BGP_PREFIX_SID_SRV6_TRANSPOSITION_LENGTH; + from_bgp->vpn_policy[afi] + .tovpn_sid_locator->function_bits_length; static_attr.srv6_l3vpn->transposition_offset = - BGP_PREFIX_SID_SRV6_TRANSPOSITION_OFFSET; + from_bgp->vpn_policy[afi] + .tovpn_sid_locator->block_bits_length + + from_bgp->vpn_policy[afi] + .tovpn_sid_locator->node_bits_length; + ; memcpy(&static_attr.srv6_l3vpn->sid, - from_bgp->vpn_policy[afi].tovpn_sid_locator, + &from_bgp->vpn_policy[afi] + .tovpn_sid_locator->prefix.prefix, sizeof(struct in6_addr)); } @@ -1302,7 +1371,7 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */ * because of loop checking. */ if (new_info) - vpn_leak_to_vrf_update(from_bgp, new_info); + vpn_leak_to_vrf_update(from_bgp, new_info, NULL); } void vpn_leak_from_vrf_withdraw(struct bgp *to_bgp, /* to */ @@ -1458,10 +1527,40 @@ void vpn_leak_from_vrf_update_all(struct bgp *to_bgp, struct bgp *from_bgp, } } -static bool -vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */ - struct bgp *from_bgp, /* from */ - struct bgp_path_info *path_vpn) /* route */ +static struct bgp *bgp_lookup_by_rd(struct bgp_path_info *bpi, + struct prefix_rd *rd, afi_t afi) +{ + struct listnode *node, *nnode; + struct bgp *bgp; + + if (!rd) + return NULL; + + /* If ACCEPT_OWN is not enabled for this path - return. */ + if (!CHECK_FLAG(bpi->flags, BGP_PATH_ACCEPT_OWN)) + return NULL; + + for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) { + if (bgp->inst_type != BGP_INSTANCE_TYPE_VRF) + continue; + + if (!CHECK_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_RD_SET)) + continue; + + /* Check if we have source VRF by RD value */ + if (memcmp(&bgp->vpn_policy[afi].tovpn_rd.val, rd->val, + ECOMMUNITY_SIZE) == 0) + return bgp; + } + + return NULL; +} + +static bool vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */ + struct bgp *from_bgp, /* from */ + struct bgp_path_info *path_vpn, + struct prefix_rd *prd) { const struct prefix *p = bgp_dest_get_prefix(path_vpn->net); afi_t afi = family2afi(p->family); @@ -1498,9 +1597,22 @@ vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */ return false; } + /* A route MUST NOT ever be accepted back into its source VRF, even if + * it carries one or more RTs that match that VRF. + */ + if (prd && memcmp(&prd->val, &to_bgp->vpn_policy[afi].tovpn_rd.val, + ECOMMUNITY_SIZE) == 0) { + if (debug) + zlog_debug( + "%s: skipping import, match RD (%pRD) of src VRF (%s) and the prefix (%pFX)", + __func__, prd, to_bgp->name_pretty, p); + + return false; + } + if (debug) - zlog_debug("%s: updating %pFX to vrf %s", __func__, p, - to_bgp->name_pretty); + zlog_debug("%s: updating RD %pRD, %pFX to vrf %s", __func__, + prd, p, to_bgp->name_pretty); /* shallow copy */ static_attr = *path_vpn->attr; @@ -1525,6 +1637,8 @@ vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */ ecommunity_free(&old_ecom); } + community_strip_accept_own(&static_attr); + /* * Nexthop: stash and clear * @@ -1651,9 +1765,16 @@ vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */ /* * For VRF-2-VRF route-leaking, * the source will be the originating VRF. + * + * If ACCEPT_OWN mechanism is enabled, then we SHOULD(?) + * get the source VRF (BGP) by looking at the RD. */ + struct bgp *src_bgp = bgp_lookup_by_rd(path_vpn, prd, afi); + if (path_vpn->extra && path_vpn->extra->bgp_orig) src_vrf = path_vpn->extra->bgp_orig; + else if (src_bgp) + src_vrf = src_bgp; else src_vrf = from_bgp; @@ -1663,8 +1784,9 @@ vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */ return true; } -bool vpn_leak_to_vrf_update(struct bgp *from_bgp, /* from */ - struct bgp_path_info *path_vpn) /* route */ +bool vpn_leak_to_vrf_update(struct bgp *from_bgp, + struct bgp_path_info *path_vpn, + struct prefix_rd *prd) { struct listnode *mnode, *mnnode; struct bgp *bgp; @@ -1681,7 +1803,7 @@ bool vpn_leak_to_vrf_update(struct bgp *from_bgp, /* from */ if (!path_vpn->extra || path_vpn->extra->bgp_orig != bgp) { /* no loop */ leak_success |= vpn_leak_to_vrf_update_onevrf( - bgp, from_bgp, path_vpn); + bgp, from_bgp, path_vpn, prd); } } return leak_success; @@ -1837,7 +1959,7 @@ void vpn_leak_to_vrf_update_all(struct bgp *to_bgp, struct bgp *vpn_from, continue; vpn_leak_to_vrf_update_onevrf(to_bgp, vpn_from, - bpi); + bpi, NULL); } } } diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h index c5cc7d4294..9af4cdf3a2 100644 --- a/bgpd/bgp_mplsvpn.h +++ b/bgpd/bgp_mplsvpn.h @@ -41,6 +41,8 @@ #define V4_HEADER_OVERLAY \ " Network Next Hop EthTag Overlay Index RouterMac\n" +#define BGP_PREFIX_SID_SRV6_MAX_FUNCTION_LENGTH 20 + extern void bgp_mplsvpn_init(void); extern int bgp_nlri_parse_vpn(struct peer *, struct attr *, struct bgp_nlri *); extern uint32_t decode_label(mpls_label_t *); @@ -70,7 +72,8 @@ extern void vpn_leak_to_vrf_update_all(struct bgp *to_bgp, struct bgp *from_bgp, afi_t afi); extern bool vpn_leak_to_vrf_update(struct bgp *from_bgp, - struct bgp_path_info *path_vpn); + struct bgp_path_info *path_vpn, + struct prefix_rd *prd); extern void vpn_leak_to_vrf_withdraw(struct bgp *from_bgp, struct bgp_path_info *path_vpn); diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index 7274bcdb21..7eeab373a0 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -139,7 +139,8 @@ static int bgp_isvalid_nexthop_for_mpls(struct bgp_nexthop_cache *bnc, */ return (bgp_zebra_num_connects() == 0 || (bnc && (bnc->nexthop_num > 0 && - (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_LABELED_VALID) || + (CHECK_FLAG(path->flags, BGP_PATH_ACCEPT_OWN) || + CHECK_FLAG(bnc->flags, BGP_NEXTHOP_LABELED_VALID) || bnc->bgp->srv6_enabled || bgp_isvalid_nexthop_for_ebgp(bnc, path) || bgp_isvalid_nexthop_for_mplsovergre(bnc, path))))); diff --git a/bgpd/bgp_orr.c b/bgpd/bgp_orr.c new file mode 100644 index 0000000000..7fed6b775e --- /dev/null +++ b/bgpd/bgp_orr.c @@ -0,0 +1,1176 @@ +/* + * BGP Optimal Route Reflection + * Copyright (C) 2021 Samsung R&D Institute India - Bangalore. + * Madhurilatha Kuruganti + * + * 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 "bgpd/bgpd.h" +#include "bgpd/bgp_debug.h" +#include "bgpd/bgp_orr.h" +#include "bgpd/bgp_vty.h" +#include "zclient.h" + +DEFINE_MTYPE_STATIC(BGPD, ORR_IGP_INFO, "ORR IGP Metric info"); + +static inline bool is_orr_primary_root(struct bgp_orr_group *orr_group, + char *host) +{ + return orr_group->primary && strmatch(orr_group->primary->host, host); +} + +static inline bool is_orr_secondary_root(struct bgp_orr_group *orr_group, + char *host) +{ + return orr_group->secondary && + strmatch(orr_group->secondary->host, host); +} + +static inline bool is_orr_tertiary_root(struct bgp_orr_group *orr_group, + char *host) +{ + return orr_group->tertiary && strmatch(orr_group->tertiary->host, host); +} + +static inline bool is_orr_active_root(struct bgp_orr_group *orr_group, + char *host) +{ + return orr_group->active && strmatch(orr_group->active->host, host); +} + +static inline bool is_orr_root_node(struct bgp_orr_group *orr_group, char *host) +{ + return is_orr_primary_root(orr_group, host) || + is_orr_secondary_root(orr_group, host) || + is_orr_tertiary_root(orr_group, host); +} + +static inline bool is_peer_orr_group_member(struct peer *peer, afi_t afi, + safi_t safi, const char *name) +{ + return peer_af_flag_check(peer, afi, safi, PEER_FLAG_ORR_GROUP) && + strmatch(peer->orr_group_name[afi][safi], name); +} + +static inline bool is_peer_reachable(struct peer *peer, afi_t afi, safi_t safi) +{ + return peer && peer->afc_nego[afi][safi] && peer_established(peer); +} + +static inline bool is_peer_active_eligible(struct peer *peer, afi_t afi, + safi_t safi, const char *name) +{ + return is_peer_reachable(peer, afi, safi) && + is_peer_orr_group_member(peer, afi, safi, name); +} + +static void bgp_orr_igp_metric_register(struct bgp_orr_group *orr_group, + bool reg); + +static void +bgp_peer_update_orr_group_active_root(struct peer *peer, afi_t afi, safi_t safi, + struct bgp_orr_group *orr_group); + +static struct bgp_orr_group *bgp_orr_group_new(struct bgp *bgp, afi_t afi, + safi_t safi, const char *name) +{ + int ret; + struct list *orr_group_list = NULL; + struct bgp_orr_group *orr_group = NULL; + + assert(bgp && name); + + if (!bgp->orr_group[afi][safi]) + bgp->orr_group[afi][safi] = list_new(); + + orr_group_list = bgp->orr_group[afi][safi]; + orr_group = XCALLOC(MTYPE_BGP_ORR_GROUP, sizeof(struct bgp_orr_group)); + + listnode_add(orr_group_list, orr_group); + + orr_group->name = XSTRDUP(MTYPE_BGP_ORR_GROUP_NAME, name); + orr_group->afi = afi; + orr_group->safi = safi; + orr_group->primary = orr_group->secondary = orr_group->tertiary = NULL; + orr_group->bgp = bgp; + + /* Initialize ORR Group route table */ + orr_group->route_table = bgp_table_init(bgp, afi, safi); + assert(orr_group->route_table); + + /* + * Register for opaque messages from IGPs when first ORR group is + * configured. + */ + if (!bgp->orr_group_count) { + ret = zclient_register_opaque(zclient, ORR_IGP_METRIC_UPDATE); + if (ret != ZCLIENT_SEND_SUCCESS) + bgp_orr_debug( + "%s: zclient_register_opaque failed with ret = %d", + __func__, ret); + } + + bgp->orr_group_count++; + + return orr_group; +} + +static void bgp_orr_group_free(struct bgp_orr_group *orr_group) +{ + afi_t afi; + safi_t safi; + struct bgp *bgp; + + assert(orr_group && orr_group->bgp && orr_group->name); + + bgp_orr_debug("%s: Deleting ORR group %s", __func__, orr_group->name); + + afi = orr_group->afi; + safi = orr_group->safi; + bgp = orr_group->bgp; + + /* + * Unregister with IGP for metric calculation from specified location + * and delete igp_metric_info calculated for this group + */ + bgp_orr_igp_metric_register(orr_group, false); + + /* Free RR client list associated with this ORR group */ + if (orr_group->rr_client_list) + list_delete(&orr_group->rr_client_list); + + /* Free route table */ + bgp_table_unlock(orr_group->route_table); + orr_group->route_table = NULL; + + /* Unset ORR Group parameters */ + XFREE(MTYPE_BGP_ORR_GROUP_NAME, orr_group->name); + + listnode_delete(bgp->orr_group[afi][safi], orr_group); + XFREE(MTYPE_BGP_ORR_GROUP, orr_group); + + bgp->orr_group_count--; + + if (!bgp->orr_group[afi][safi]->count) + list_delete(&bgp->orr_group[afi][safi]); +} + +struct bgp_orr_group *bgp_orr_group_lookup_by_name(struct bgp *bgp, afi_t afi, + safi_t safi, + const char *name) +{ + struct list *orr_group_list = NULL; + struct bgp_orr_group *group = NULL; + struct listnode *node; + + assert(bgp); + + orr_group_list = bgp->orr_group[afi][safi]; + if (!orr_group_list) + return NULL; + + for (ALL_LIST_ELEMENTS_RO(orr_group_list, node, group)) + if (strmatch(group->name, name)) + return group; + + bgp_orr_debug("%s: For %s, ORR Group '%s' not found.", __func__, + get_afi_safi_str(afi, safi, false), name); + + return NULL; +} + +static char *bgp_orr_group_rrclient_lookup(struct bgp_orr_group *orr_group, + const char *rr_client_host) +{ + char *rrclient = NULL; + struct list *orr_group_rrclient_list = NULL; + struct listnode *node; + + orr_group_rrclient_list = orr_group->rr_client_list; + if (!orr_group_rrclient_list) + return NULL; + + for (ALL_LIST_ELEMENTS_RO(orr_group_rrclient_list, node, rrclient)) + if (strmatch(rrclient, rr_client_host)) + return rrclient; + + bgp_orr_debug( + "%s: For %s, %s not found in ORR Group '%s' RR Client list", + __func__, + get_afi_safi_str(orr_group->afi, orr_group->safi, false), + rr_client_host, orr_group->name); + + return NULL; +} + +static void bgp_orr_group_rrclient_update(struct peer *peer, afi_t afi, + safi_t safi, + const char *orr_group_name, bool add) +{ + char *rr_client = NULL; + struct bgp_orr_group *orr_group = NULL; + struct list *rr_client_list = NULL; + + assert(peer && peer->bgp && orr_group_name); + + /* Get BGP ORR entry for the given address-family */ + orr_group = bgp_orr_group_lookup_by_name(peer->bgp, afi, safi, + orr_group_name); + if (!orr_group) { + bgp_orr_debug("%s: For %s, ORR Group '%s' not found.", __func__, + get_afi_safi_str(afi, safi, false), + orr_group_name); + return; + } + + /* Get BGP ORR client entry for the given RR client */ + rr_client = bgp_orr_group_rrclient_lookup(orr_group, peer->host); + + /* Nothing to do */ + if ((rr_client && add) || (!rr_client && !add)) + return; + + if (add) { + /* Create BGP ORR RR client entry to the ORR Group */ + if (!orr_group->rr_client_list) + orr_group->rr_client_list = list_new(); + rr_client_list = orr_group->rr_client_list; + rr_client = XSTRDUP(MTYPE_BGP_PEER_HOST, peer->host); + + listnode_add(rr_client_list, rr_client); + + bgp_orr_debug( + "%s: For %s, %pBP is added to ORR Group '%s' RR Client list.", + __func__, get_afi_safi_str(afi, safi, false), peer, + orr_group_name); + } else { + /* Delete BGP ORR RR client entry from the ORR Group */ + listnode_delete(orr_group->rr_client_list, rr_client); + XFREE(MTYPE_BGP_PEER_HOST, rr_client); + if (!orr_group->rr_client_list->count) + list_delete(&orr_group->rr_client_list); + + bgp_orr_debug( + "%s: For %s, %pBP is removed from ORR Group '%s' RR Client list.", + __func__, get_afi_safi_str(afi, safi, false), peer, + orr_group_name); + } +} + +/* Create/Update BGP Optimal Route Reflection Group */ +int bgp_afi_safi_orr_group_set(struct bgp *bgp, afi_t afi, safi_t safi, + const char *name, struct peer *primary, + struct peer *secondary, struct peer *tertiary) +{ + bool primary_eligible = false; + bool secondary_eligible = false; + bool tertiary_eligible = false; + struct bgp_orr_group *orr_group = NULL; + + bgp_orr_debug( + "%s: For %s, ORR Group '%s' Primary %pBP Secondary %pBP Tertiary %pBP", + __func__, get_afi_safi_str(afi, safi, false), name, primary, + secondary, tertiary); + + /* Get BGP ORR entry for the given address-family */ + orr_group = bgp_orr_group_lookup_by_name(bgp, afi, safi, name); + if (!orr_group) { + /* Create BGP ORR entry for the given address-family */ + orr_group = bgp_orr_group_new(bgp, afi, safi, name); + } + + /* Compare and update Primary Root Address */ + if (primary) { + if (!orr_group->primary || + !strmatch(orr_group->primary->host, primary->host)) + orr_group->primary = primary; + else + bgp_orr_debug("%s: No change in Primary Root", + __func__); + + /* + * Update Active Root if there is a change and primary is + * reachable. + */ + primary_eligible = + is_peer_active_eligible(primary, afi, safi, name); + if (!orr_group->active) { + orr_group->active = primary; + bgp_orr_igp_metric_register(orr_group, true); + } else if (orr_group->primary && + !strmatch(orr_group->active->host, + orr_group->primary->host)) { + bgp_orr_igp_metric_register(orr_group, false); + orr_group->active = primary; + bgp_orr_igp_metric_register(orr_group, true); + } else + bgp_orr_debug("%s: %s", __func__, + orr_group->primary + ? "No change in Active Root" + : "Primary Root is NULL"); + } else { + if (orr_group->primary) { + if (orr_group->active && + strmatch(orr_group->active->host, + orr_group->primary->host)) { + bgp_orr_igp_metric_register(orr_group, false); + + orr_group->active = NULL; + } + orr_group->primary = NULL; + } + } + + /* Compare and update Secondary Root Address */ + if (secondary) { + if (!orr_group->secondary || + !strmatch(orr_group->secondary->host, secondary->host)) + orr_group->secondary = secondary; + else + bgp_orr_debug("%s: No change in Secondary Root", + __func__); + + /* Update Active Root if Primary is not reachable */ + secondary_eligible = + is_peer_active_eligible(secondary, afi, safi, name); + if (!orr_group->active) { + orr_group->active = secondary; + bgp_orr_igp_metric_register(orr_group, true); + } else if (!primary_eligible && orr_group->secondary && + !strmatch(orr_group->active->host, + orr_group->secondary->host)) { + bgp_orr_igp_metric_register(orr_group, false); + orr_group->active = secondary; + bgp_orr_igp_metric_register(orr_group, true); + } else + bgp_orr_debug( + "%s: %s", __func__, + primary_eligible + ? "Primary is Active Root" + : orr_group->secondary + ? "No change in Active Root" + : "Secondary Root is NULL"); + } else { + if (orr_group->secondary) { + if (orr_group->active && + strmatch(orr_group->active->host, + orr_group->secondary->host)) { + bgp_orr_igp_metric_register(orr_group, false); + + orr_group->active = NULL; + } + orr_group->secondary = NULL; + } + } + + /* Compare and update Tertiary Root Address */ + if (tertiary) { + if (!orr_group->tertiary || + !strmatch(orr_group->tertiary->host, tertiary->host)) + orr_group->tertiary = tertiary; + else + bgp_orr_debug("%s: No change in Tertiay Root", + __func__); + + /* + * Update Active Root if Primary & Secondary are not reachable + */ + tertiary_eligible = + is_peer_active_eligible(tertiary, afi, safi, name); + if (!orr_group->active) { + orr_group->active = tertiary; + bgp_orr_igp_metric_register(orr_group, true); + } else if (!primary_eligible && !secondary_eligible && + orr_group->tertiary && + !strmatch(orr_group->active->host, + orr_group->tertiary->host)) { + bgp_orr_igp_metric_register(orr_group, false); + + orr_group->active = tertiary; + bgp_orr_igp_metric_register(orr_group, true); + } else + bgp_orr_debug( + "%s: %s", __func__, + primary_eligible + ? "Primary is Active Root" + : secondary_eligible + ? "Secondary is Active Root" + : !orr_group->tertiary + ? "Tertiary Root is NULL" + : "No change in Active Root"); + } else { + if (orr_group->tertiary) { + if (orr_group->active && + strmatch(orr_group->active->host, + orr_group->tertiary->host)) { + bgp_orr_igp_metric_register(orr_group, false); + + orr_group->active = NULL; + } + orr_group->tertiary = NULL; + } + } + + if (orr_group->active && !primary_eligible && !secondary_eligible && + !tertiary_eligible) { + bgp_orr_igp_metric_register(orr_group, false); + + orr_group->active = NULL; + } + + bgp_orr_debug("%s: For %s, ORR Group '%s' Active Root is %pBP", + __func__, get_afi_safi_str(afi, safi, false), name, + orr_group->active); + + return CMD_SUCCESS; +} + +/* Delete BGP Optimal Route Reflection Group */ +int bgp_afi_safi_orr_group_unset(struct bgp *bgp, afi_t afi, safi_t safi, + const char *name) +{ + struct bgp_orr_group *orr_group; + + orr_group = bgp_orr_group_lookup_by_name(bgp, afi, safi, name); + if (!orr_group) + return CMD_WARNING; + + /* Check if there are any neighbors configured with this ORR Group */ + if (orr_group->rr_client_list) { + bgp_orr_debug( + "%s: For %s, ORR Group '%s' not removed as '%s' is configured on neighbor(s)", + __func__, + get_afi_safi_str(orr_group->afi, orr_group->safi, + false), + name, name); + return CMD_WARNING; + } + + bgp_orr_group_free(orr_group); + return CMD_SUCCESS; +} + +/* Set optimal route reflection group to the peer */ +static int peer_orr_group_set(struct peer *peer, afi_t afi, safi_t safi, + const char *orr_group_name) +{ + struct bgp_orr_group *orr_group = NULL; + + if (!peer) + return CMD_WARNING; + + /* Get BGP ORR entry for the given address-family */ + orr_group = bgp_orr_group_lookup_by_name(peer->bgp, afi, safi, + orr_group_name); + if (!orr_group) { + /* Create BGP ORR entry for the given address-family */ + orr_group = + bgp_orr_group_new(peer->bgp, afi, safi, orr_group_name); + } + + /* Skip processing if there is no change in ORR Group */ + if (peer_af_flag_check(peer, afi, safi, PEER_FLAG_ORR_GROUP)) { + if (strmatch(peer->orr_group_name[afi][safi], orr_group_name)) { + bgp_orr_debug( + "%s: For %s, ORR Group '%s' is already configured on %pBP", + __func__, get_afi_safi_str(afi, safi, false), + orr_group_name, peer); + return CMD_SUCCESS; + } + /* Remove the peer from ORR Group's peer list */ + bgp_orr_group_rrclient_update(peer, afi, safi, + peer->orr_group_name[afi][safi], + false); + XFREE(MTYPE_BGP_ORR_GROUP_NAME, + peer->orr_group_name[afi][safi]); + } + + peer->orr_group_name[afi][safi] = + XSTRDUP(MTYPE_BGP_ORR_GROUP_NAME, orr_group_name); + SET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_ORR_GROUP); + + /* Add the peer to ORR Group's client list */ + bgp_orr_group_rrclient_update(peer, afi, safi, orr_group_name, true); + + /* Update ORR group active root and register with IGP */ + bgp_peer_update_orr_group_active_root(peer, afi, safi, orr_group); + + return CMD_SUCCESS; +} + +/* Unset optimal route reflection group from the peer*/ +int peer_orr_group_unset(struct peer *peer, afi_t afi, safi_t safi, + const char *orr_group_name) +{ + struct bgp_orr_group *orr_group = NULL; + + assert(peer && peer->bgp && orr_group_name); + + if (!peer_af_flag_check(peer, afi, safi, PEER_FLAG_ORR_GROUP) || + !strmatch(peer->orr_group_name[afi][safi], orr_group_name)) { + bgp_orr_debug( + "%s: For %s, ORR Group '%s' is not configured on %pBP", + __func__, get_afi_safi_str(afi, safi, false), + orr_group_name, peer); + return CMD_ERR_NO_MATCH; + } + + /* Check if this RR Client is one of the root nodes */ + orr_group = bgp_orr_group_lookup_by_name(peer->bgp, afi, safi, + orr_group_name); + + /* Should not be Null when orr-group is enabled on peer */ + assert(orr_group); + + /* Check if the peer is one of the root nodes of the ORR group */ + if (is_orr_root_node(orr_group, peer->host)) + return CMD_WARNING; + + /* Remove the peer from ORR Group's client list */ + bgp_orr_group_rrclient_update(peer, afi, safi, orr_group_name, false); + + /* Update ORR group active root and unregister with IGP */ + bgp_peer_update_orr_group_active_root(peer, afi, safi, orr_group); + + UNSET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_ORR_GROUP); + XFREE(MTYPE_BGP_ORR_GROUP_NAME, peer->orr_group_name[afi][safi]); + + return CMD_SUCCESS; +} + +int bgp_afi_safi_orr_group_set_vty(struct vty *vty, afi_t afi, safi_t safi, + const char *name, const char *primary_str, + const char *secondary_str, + const char *tertiary_str, bool unset) +{ + int ret = CMD_WARNING_CONFIG_FAILED; + struct bgp *bgp; + struct peer *primary = NULL, *secondary = NULL, *tertiary = NULL; + + bgp = bgp_get_default(); + if (!bgp) { + vty_out(vty, "%% No BGP process is configured\n"); + return ret; + } + + if (unset) { + ret = bgp_afi_safi_orr_group_unset(bgp, afi, safi, name); + if (ret != CMD_SUCCESS) + vty_out(vty, + "%% ORR Group %s not removed as '%s' is not found OR configured on neighbor(s)\n", + name, name); + return ret; + } + + primary = peer_and_group_lookup_vty(vty, primary_str); + if (!primary || !peer_af_flag_check(primary, afi, safi, + PEER_FLAG_REFLECTOR_CLIENT)) { + vty_out(vty, + "%% Primary Root is not a Route Reflector Client\n"); + return ret; + } + + if (secondary_str) { + secondary = peer_and_group_lookup_vty(vty, secondary_str); + if (!secondary || + !peer_af_flag_check(secondary, afi, safi, + PEER_FLAG_REFLECTOR_CLIENT)) { + vty_out(vty, + "%% Secondary Root is not a Route Reflector Client\n"); + return ret; + } + } + + if (tertiary_str) { + tertiary = peer_and_group_lookup_vty(vty, tertiary_str); + if (!tertiary || + !peer_af_flag_check(tertiary, afi, safi, + PEER_FLAG_REFLECTOR_CLIENT)) { + vty_out(vty, + "%% Tertiary Root is not a Route Reflector Client\n"); + return ret; + } + } + return bgp_afi_safi_orr_group_set(bgp, afi, safi, name, primary, + secondary, tertiary); +} + +/* Set optimal route reflection group name to the peer. */ +int peer_orr_group_set_vty(struct vty *vty, const char *ip_str, afi_t afi, + safi_t safi, const char *orr_group_name, bool unset) +{ + int ret = CMD_WARNING_CONFIG_FAILED; + struct peer *peer; + + peer = peer_and_group_lookup_vty(vty, ip_str); + if (!peer) + return ret; + + if (!peer_af_flag_check(peer, afi, safi, PEER_FLAG_REFLECTOR_CLIENT)) { + vty_out(vty, "%% Neighbor %s is not a Route Reflector Client\n", + peer->host); + return ret; + } + + if (!unset) { + ret = peer_orr_group_set(peer, afi, safi, orr_group_name); + if (ret != CMD_SUCCESS) + vty_out(vty, "%% ORR Group '%s' is not configured\n", + orr_group_name); + } else { + ret = peer_orr_group_unset(peer, afi, safi, orr_group_name); + if (ret == CMD_ERR_NO_MATCH) + vty_out(vty, + "%% ORR Group '%s' is not configured on %s\n", + orr_group_name, peer->host); + else if (ret == CMD_WARNING) + vty_out(vty, + "%% %s is one of the root nodes of ORR Group '%s'.\n", + peer->host, orr_group_name); + } + return bgp_vty_return(vty, ret); +} + +void bgp_config_write_orr(struct vty *vty, struct bgp *bgp, afi_t afi, + safi_t safi) +{ + struct list *orr_group_list; + struct listnode *node; + struct bgp_orr_group *orr_group; + + orr_group_list = bgp->orr_group[afi][safi]; + if (!orr_group_list) + return; + + for (ALL_LIST_ELEMENTS_RO(orr_group_list, node, orr_group)) { + /* optimal route reflection configuration */ + vty_out(vty, " optimal-route-reflection %s", orr_group->name); + if (orr_group->primary) + vty_out(vty, " %s", orr_group->primary->host); + if (orr_group->secondary) + vty_out(vty, " %s", orr_group->secondary->host); + if (orr_group->tertiary) + vty_out(vty, " %s", orr_group->tertiary->host); + vty_out(vty, "\n"); + } +} + +static void bgp_show_orr_group(struct vty *vty, struct bgp_orr_group *orr_group, + afi_t afi, safi_t safi) +{ + char *rrclient = NULL; + struct listnode *node; + struct bgp_orr_igp_metric *igp_metric = NULL; + struct list *orr_group_rrclient_list = NULL; + struct list *orr_group_igp_metric_info = NULL; + + if (!orr_group) + return; + + vty_out(vty, "\nORR group: %s, %s\n", orr_group->name, + get_afi_safi_str(afi, safi, false)); + vty_out(vty, "Configured root:"); + vty_out(vty, " primary: %pBP,", orr_group->primary); + vty_out(vty, " secondary: %pBP,", orr_group->secondary); + vty_out(vty, " tertiary: %pBP\n", orr_group->tertiary); + vty_out(vty, "Active Root: %pBP\n", orr_group->active); + + orr_group_rrclient_list = orr_group->rr_client_list; + if (!orr_group_rrclient_list) + return; + + vty_out(vty, "\nRR Clients mapped:\n"); + + for (ALL_LIST_ELEMENTS_RO(orr_group_rrclient_list, node, rrclient)) + vty_out(vty, "%s\n", rrclient); + + vty_out(vty, "\nNumber of mapping entries: %d\n\n", + orr_group_rrclient_list->count); + + + orr_group_igp_metric_info = orr_group->igp_metric_info; + if (!orr_group_igp_metric_info) + return; + vty_out(vty, "Prefix\t\t\t\t\t\tCost\n"); + for (ALL_LIST_ELEMENTS_RO(orr_group_igp_metric_info, node, + igp_metric)) { + vty_out(vty, "%pFX\t\t\t\t\t\t%d\n", &igp_metric->prefix, + igp_metric->igp_metric); + } + vty_out(vty, "\nNumber of mapping entries: %d\n\n", + orr_group_igp_metric_info->count); +} + +int bgp_show_orr(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, + const char *orr_group_name, uint8_t show_flags) +{ + struct listnode *node; + struct bgp_orr_group *orr_group = NULL; + struct list *orr_group_list = NULL; + int ret = 0; + + assert(bgp); + + /* Display the matching entries for the given ORR Group */ + if (orr_group_name) { + orr_group = bgp_orr_group_lookup_by_name(bgp, afi, safi, + orr_group_name); + if (!orr_group) { + vty_out(vty, "%% ORR Group %s not found\n", + orr_group_name); + return CMD_WARNING; + } + bgp_show_orr_group(vty, orr_group, afi, safi); + return ret; + } + orr_group_list = bgp->orr_group[afi][safi]; + if (!orr_group_list) + return ret; + + for (ALL_LIST_ELEMENTS_RO(orr_group_list, node, orr_group)) + bgp_show_orr_group(vty, orr_group, afi, safi); + + return ret; +} + +/* Check if the Route Reflector Client belongs to any ORR Group */ +bool peer_orr_rrclient_check(struct peer *peer, afi_t afi, safi_t safi) +{ + char *rrclient = NULL; + struct listnode *node; + struct list *orr_group_list = NULL; + struct list *orr_group_rrclient_list = NULL; + struct bgp_orr_group *orr_group = NULL; + + assert(peer && peer->bgp); + + orr_group_list = peer->bgp->orr_group[afi][safi]; + if (!orr_group_list) + return false; + + for (ALL_LIST_ELEMENTS_RO(orr_group_list, node, orr_group)) { + /* Check if peer configured as primary/secondary/tertiary root + */ + if (is_orr_root_node(orr_group, peer->host)) + return true; + /* + * Check if peer is mapped to any ORR Group in this + * Address Family. + */ + orr_group_rrclient_list = orr_group->rr_client_list; + if (!orr_group_rrclient_list) + continue; + + for (ALL_LIST_ELEMENTS_RO(orr_group_rrclient_list, node, + rrclient)) + if (strmatch(rrclient, peer->host)) + return true; + } + return false; +} + +static void +bgp_peer_update_orr_group_active_root(struct peer *peer, afi_t afi, safi_t safi, + struct bgp_orr_group *orr_group) +{ + assert(peer && orr_group); + + /* Nothing to do if this peer is not one of the root nodes */ + if (!is_orr_root_node(orr_group, peer->host)) + return; + + /* Root is reachable and group member, update Active Root if needed */ + if (is_peer_active_eligible(peer, afi, safi, orr_group->name)) { + /* Nothing to do, if this is the current Active Root */ + if (is_orr_active_root(orr_group, peer->host)) + return; + + /* If Active is null, update this node as Active Root */ + if (!orr_group->active) { + orr_group->active = peer; + bgp_orr_igp_metric_register(orr_group, true); + return; + } + + /* If this is Primary and current Active is not Primary */ + if (is_orr_primary_root(orr_group, peer->host)) { + bgp_orr_igp_metric_register(orr_group, false); + orr_group->active = peer; + bgp_orr_igp_metric_register(orr_group, true); + return; + } + + /* + * If this is Secondary and current Active is not + * Primary/Secondary + */ + if (is_orr_secondary_root(orr_group, peer->host)) { + if (is_orr_active_root(orr_group, + orr_group->primary->host)) + return; + bgp_orr_igp_metric_register(orr_group, false); + orr_group->active = peer; + bgp_orr_igp_metric_register(orr_group, true); + return; + } + return; + } + + /* Non Active Root is unreachable, so nothing to do */ + if (!is_orr_active_root(orr_group, peer->host)) + return; + + if (is_orr_primary_root(orr_group, peer->host)) { + /* If secondary is reachable, update it as Active */ + if (is_peer_active_eligible(orr_group->secondary, afi, safi, + orr_group->name)) { + bgp_orr_igp_metric_register(orr_group, false); + + orr_group->active = orr_group->secondary; + bgp_orr_igp_metric_register(orr_group, true); + return; + } + + /* If tertiary is reachable, update it as Active */ + if (is_peer_active_eligible(orr_group->tertiary, afi, safi, + orr_group->name)) { + bgp_orr_igp_metric_register(orr_group, false); + + orr_group->active = orr_group->tertiary; + bgp_orr_igp_metric_register(orr_group, true); + return; + } + } else { + if (is_orr_secondary_root(orr_group, peer->host)) { + /* If tertiary is reachable, update it as Active */ + if (is_peer_active_eligible(orr_group->tertiary, afi, + safi, orr_group->name)) { + bgp_orr_igp_metric_register(orr_group, false); + + orr_group->active = orr_group->tertiary; + bgp_orr_igp_metric_register(orr_group, true); + return; + } + } + } + + /* Assign Active as null */ + bgp_orr_igp_metric_register(orr_group, false); + orr_group->active = NULL; + + bgp_orr_debug("%s: For %s, ORR Group '%s' has no active root", __func__, + get_afi_safi_str(afi, safi, false), + peer->orr_group_name[afi][safi]); +} + +void bgp_peer_update_orr_active_roots(struct peer *peer) +{ + afi_t afi; + safi_t safi; + struct bgp_orr_group *orr_group; + + assert(peer && peer->bgp); + + FOREACH_AFI_SAFI (afi, safi) { + if (!peer->orr_group_name[afi][safi]) + continue; + + /* Get BGP ORR entry for the given address-family */ + orr_group = bgp_orr_group_lookup_by_name( + peer->bgp, afi, safi, peer->orr_group_name[afi][safi]); + assert(orr_group); + + /* Free ORR related memory. */ + if (peer->status != Deleted) { + bgp_peer_update_orr_group_active_root(peer, afi, safi, + orr_group); + continue; + } + + if (!is_orr_root_node(orr_group, peer->host)) { + peer_orr_group_unset(peer, afi, safi, + peer->orr_group_name[afi][safi]); + continue; + } + + if (is_orr_primary_root(orr_group, peer->host)) { + orr_group->primary = orr_group->secondary; + orr_group->secondary = orr_group->tertiary; + } else if (is_orr_secondary_root(orr_group, peer->host)) + orr_group->secondary = orr_group->tertiary; + orr_group->tertiary = NULL; + + bgp_afi_safi_orr_group_set(peer->bgp, afi, safi, + orr_group->name, orr_group->primary, + orr_group->secondary, + orr_group->tertiary); + peer_orr_group_unset(peer, afi, safi, + peer->orr_group_name[afi][safi]); + } +} + +/* IGP metric calculated from Active Root */ +static int bgp_orr_igp_metric_update(struct orr_igp_metric_info *table) +{ + afi_t afi; + safi_t safi; + bool add = false; + bool root_found = false; + uint32_t instId = 0; + uint32_t numEntries = 0; + uint32_t entry = 0; + uint8_t proto = ZEBRA_ROUTE_MAX; + struct bgp *bgp = NULL; + struct prefix pfx, root = {0}; + + struct list *orr_group_list = NULL; + struct bgp_orr_group *group = NULL; + struct listnode *node, *nnode; + + struct bgp_orr_igp_metric *igp_metric = NULL; + struct list *bgp_orr_igp_metric = NULL; + + bgp = bgp_get_default(); + assert(bgp && table); + + proto = table->proto; + afi = family2afi(table->root.family); + safi = table->safi; + instId = table->instId; + add = table->add; + numEntries = table->num_entries; + prefix_copy(&root, &table->root); + + if ((proto != ZEBRA_ROUTE_OSPF) && (proto != ZEBRA_ROUTE_OSPF6) && + (proto != ZEBRA_ROUTE_ISIS)) { + bgp_orr_debug("%s: Message received from unsupported protocol", + __func__); + return -1; + } + + orr_group_list = bgp->orr_group[afi][safi]; + if (!orr_group_list) { + bgp_orr_debug( + "%s: Address family %s has no ORR Groups configured", + __func__, get_afi_safi_str(afi, safi, false)); + return -1; + } + + if (BGP_DEBUG(optimal_route_reflection, ORR)) { + zlog_debug( + "[BGP-ORR] %s: Received metric update from protocol %s instance %d", + __func__, + proto == ZEBRA_ROUTE_ISIS + ? "ISIS" + : (proto == ZEBRA_ROUTE_OSPF ? "OSPF" + : "OSPF6"), + instId); + zlog_debug("[BGP-ORR] %s: Address family %s", __func__, + get_afi_safi_str(afi, safi, false)); + zlog_debug("[BGP-ORR] %s: Root %pFX", __func__, &root); + zlog_debug("[BGP-ORR] %s: Number of entries to be %s %d", + __func__, add ? "added" : "deleted", numEntries); + zlog_debug("[BGP-ORR] %s: Prefix (Cost) :", __func__); + for (entry = 0; entry < numEntries; entry++) + zlog_debug("[BGP-ORR] %s: %pFX (%d)", __func__, + &table->nexthop[entry].prefix, + table->nexthop[entry].metric); + } + /* + * Update IGP metric info of all ORR Groups having this as active root + */ + for (ALL_LIST_ELEMENTS_RO(orr_group_list, node, group)) { + if (str2prefix(group->active->host, &pfx) == 0) { + bgp_orr_debug("%s: Malformed prefix for %pBP", __func__, + group->active); + continue; + } + /* + * Copy IGP info if root matches with the active root of the + * group + */ + if (prefix_cmp(&pfx, &root) == 0) { + if (add) { + /* Add new routes */ + if (!group->igp_metric_info) + group->igp_metric_info = list_new(); + + bgp_orr_igp_metric = group->igp_metric_info; + if (!bgp_orr_igp_metric) + bgp_orr_igp_metric_register(group, + false); + assert(bgp_orr_igp_metric); + + for (entry = 0; entry < numEntries; entry++) { + igp_metric = XCALLOC( + MTYPE_ORR_IGP_INFO, + sizeof(struct + bgp_orr_igp_metric)); + if (!igp_metric) + bgp_orr_igp_metric_register( + group, false); + + prefix_copy( + &igp_metric->prefix, + &table->nexthop[entry].prefix); + igp_metric->igp_metric = + table->nexthop[entry].metric; + listnode_add(bgp_orr_igp_metric, + igp_metric); + } + } else { + /* Delete old routes */ + for (entry = 0; entry < numEntries; entry++) { + for (ALL_LIST_ELEMENTS( + group->igp_metric_info, + node, nnode, igp_metric)) { + if (prefix_cmp( + &igp_metric->prefix, + &table->nexthop[entry] + .prefix)) + continue; + listnode_delete( + group->igp_metric_info, + igp_metric); + XFREE(MTYPE_ORR_IGP_INFO, + igp_metric); + } + } + } + root_found = true; + break; + } + } + /* Received IGP for root node thats not found in ORR active roots */ + if (!root_found) { + bgp_orr_debug( + "%s: Received IGP SPF information for root %pFX which is not an ORR active root", + __func__, &root); + } + assert(root_found); + return 0; +} + +/* Register with IGP for sending SPF info */ +static void bgp_orr_igp_metric_register(struct bgp_orr_group *orr_group, + bool reg) +{ + int ret; + struct orr_igp_metric_reg msg; + struct prefix p; + char *rr_client = NULL; + + assert(orr_group); + + if (!orr_group->active) + return; + + memset(&msg, 0, sizeof(msg)); + ret = str2prefix(orr_group->active->host, &p); + + /* Malformed prefix */ + assert(ret); + + /* Check if the active root is part of this ORR group */ + rr_client = bgp_orr_group_rrclient_lookup(orr_group, + orr_group->active->host); + if (reg && !rr_client) { + bgp_orr_debug( + "%s: active root %pBP is not part of this ORR group", + __func__, orr_group->active); + return; + } + + msg.reg = reg; + msg.proto = ZEBRA_ROUTE_BGP; + msg.safi = orr_group->safi; + prefix_copy(&msg.prefix, &p); + strlcpy(msg.group_name, orr_group->name, sizeof(msg.group_name)); + + bgp_orr_debug( + "%s: %s with IGP for metric calculation from location %pFX", + __func__, reg ? "Register" : "Unregister", &msg.prefix); + + if (zclient_send_opaque(zclient, ORR_IGP_METRIC_REGISTER, + (uint8_t *)&msg, + sizeof(msg)) == ZCLIENT_SEND_FAILURE) + zlog_warn("[BGP-ORR] %s: Failed to send message to IGP.", + __func__); + + /* Free IGP metric info calculated from previous active location */ + if (!reg && orr_group->igp_metric_info) + list_delete(&orr_group->igp_metric_info); +} + +/* BGP ORR message processing */ +int bgg_orr_message_process(enum bgp_orr_msg_type msg_type, void *msg) +{ + int ret = 0; + + assert(msg && msg_type > BGP_ORR_IMSG_INVALID && + msg_type < BGP_ORR_IMSG_MAX); + switch (msg_type) { + case BGP_ORR_IMSG_GROUP_CREATE: + break; + case BGP_ORR_IMSG_GROUP_DELETE: + break; + case BGP_ORR_IMSG_GROUP_UPDATE: + break; + case BGP_ORR_IMSG_SET_ORR_ON_PEER: + break; + case BGP_ORR_IMSG_UNSET_ORR_ON_PEER: + break; + case BGP_ORR_IMSG_IGP_METRIC_UPDATE: + ret = bgp_orr_igp_metric_update( + (struct orr_igp_metric_info *)msg); + break; + case BGP_ORR_IMSG_SHOW_ORR: + /* bgp_show_orr */ + break; + case BGP_ORR_IMSG_SHOW_ORR_GROUP: + /* bgp_show_orr_group */ + break; + default: + break; + } + + /* Free Memory */ + return ret; +} + +/* + * Cleanup ORR information - invoked at the time of bgpd exit or + * when the BGP instance (default) is being freed. + */ +void bgp_orr_cleanup(struct bgp *bgp) +{ + afi_t afi; + safi_t safi; + struct listnode *node, *nnode; + struct bgp_orr_group *orr_group; + + assert(bgp); + + if (!bgp->orr_group_count) + return; + + FOREACH_AFI_SAFI (afi, safi) { + for (ALL_LIST_ELEMENTS(bgp->orr_group[afi][safi], node, nnode, + orr_group)) + bgp_orr_group_free(orr_group); + } +} diff --git a/bgpd/bgp_orr.h b/bgpd/bgp_orr.h new file mode 100644 index 0000000000..158de30342 --- /dev/null +++ b/bgpd/bgp_orr.h @@ -0,0 +1,102 @@ +/* + * BGP Optimal Route Reflection + * Copyright (C) 2021 Samsung R&D Institute India - Bangalore. + * Madhurilatha Kuruganti + * + * 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 + */ + +#ifndef _FRR_BGP_ORR_H +#define _FRR_BGP_ORR_H +#include <zebra.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* Macro to log debug message */ +#define bgp_orr_debug(...) \ + do { \ + if (BGP_DEBUG(optimal_route_reflection, ORR)) \ + zlog_debug("[BGP-ORR] " __VA_ARGS__); \ + } while (0) + + +/* BGP ORR Message Type */ +enum bgp_orr_msg_type { + BGP_ORR_IMSG_INVALID = 0, + + /* ORR group update */ + BGP_ORR_IMSG_GROUP_CREATE = 1, + BGP_ORR_IMSG_GROUP_DELETE, + BGP_ORR_IMSG_GROUP_UPDATE, + + /* ORR group update on a BGP RR Client */ + BGP_ORR_IMSG_SET_ORR_ON_PEER = 4, + BGP_ORR_IMSG_UNSET_ORR_ON_PEER, + + /* ORR IGP Metric Update from IGP from requested Location */ + BGP_ORR_IMSG_IGP_METRIC_UPDATE = 6, + + /* ORR Group Related Information display */ + BGP_ORR_IMSG_SHOW_ORR = 7, + BGP_ORR_IMSG_SHOW_ORR_GROUP, + + /* Invalid Message Type*/ + BGP_ORR_IMSG_MAX +}; + +extern struct zclient *zclient; + +extern void bgp_config_write_orr(struct vty *vty, struct bgp *bgp, afi_t afi, + safi_t safi); + +extern int bgp_afi_safi_orr_group_set_vty(struct vty *vty, afi_t afi, + safi_t safi, const char *name, + const char *primary_str, + const char *secondary_str, + const char *tertiary_str, bool unset); +extern int peer_orr_group_unset(struct peer *peer, afi_t afi, safi_t safi, + const char *orr_group_name); +extern int peer_orr_group_set_vty(struct vty *vty, const char *ip_str, + afi_t afi, safi_t safi, + const char *orr_group_name, bool unset); +extern bool peer_orr_rrclient_check(struct peer *peer, afi_t afi, safi_t safi); + +extern int bgp_show_orr(struct vty *vty, struct bgp *bgp, afi_t afi, + safi_t safi, const char *orr_group_name, + uint8_t show_flags); + +extern int bgp_afi_safi_orr_group_set(struct bgp *bgp, afi_t afi, safi_t safi, + const char *name, struct peer *primary, + struct peer *secondary, + struct peer *tertiary); +extern int bgp_afi_safi_orr_group_unset(struct bgp *bgp, afi_t afi, safi_t safi, + const char *name); + +extern void bgp_peer_update_orr_active_roots(struct peer *peer); + +extern int bgg_orr_message_process(enum bgp_orr_msg_type msg_type, void *msg); + +extern struct bgp_orr_group *bgp_orr_group_lookup_by_name(struct bgp *bgp, + afi_t afi, + safi_t safi, + const char *name); +extern void bgp_orr_cleanup(struct bgp *bgp); +#ifdef __cplusplus +} +#endif + +#endif /* _FRR_BGP_ORR_H */ diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index a07c3b331e..f6b6cb93db 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -72,6 +72,7 @@ #include "bgpd/bgp_addpath.h" #include "bgpd/bgp_mac.h" #include "bgpd/bgp_network.h" +#include "bgpd/bgp_orr.h" #include "bgpd/bgp_trace.h" #include "bgpd/bgp_rpki.h" @@ -102,10 +103,6 @@ DEFINE_HOOK(bgp_rpki_prefix_status, const struct prefix *prefix), (peer, attr, prefix)); -/* Render dest to prefix_rd based on safi */ -static const struct prefix_rd *bgp_rd_from_dest(const struct bgp_dest *dest, - safi_t safi); - /* Extern from bgp_dump.c */ extern const char *bgp_origin_str[]; extern const char *bgp_origin_long_str[]; @@ -567,6 +564,7 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, enum bgp_path_selection_reason *reason) { const struct prefix *new_p; + struct prefix exist_p; struct attr *newattr, *existattr; enum bgp_peer_sort new_sort; enum bgp_peer_sort exist_sort; @@ -599,6 +597,11 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, bool new_origin, exist_origin; struct bgp_path_info *bpi_ultimate; + struct bgp_orr_group *orr_group = NULL; + struct listnode *node; + struct bgp_orr_igp_metric *igp_metric = NULL; + struct list *orr_group_igp_metric_info = NULL; + *paths_eq = 0; /* 0. Null check. */ @@ -874,6 +877,49 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, return 0; } + /* If a BGP speaker supports ACCEPT_OWN and is configured for the + * extensions defined in this document, the following step is inserted + * after the LOCAL_PREF comparison step in the BGP decision process: + * When comparing a pair of routes for a BGP destination, the + * route with the ACCEPT_OWN community attached is preferred over + * the route that does not have the community. + * This extra step MUST only be invoked during the best path selection + * process of VPN-IP routes. + */ + if (safi == SAFI_MPLS_VPN && + (CHECK_FLAG(new->peer->af_flags[afi][safi], PEER_FLAG_ACCEPT_OWN) || + CHECK_FLAG(exist->peer->af_flags[afi][safi], + PEER_FLAG_ACCEPT_OWN))) { + bool new_accept_own = false; + bool exist_accept_own = false; + uint32_t accept_own = COMMUNITY_ACCEPT_OWN; + + if (newattr->flag & ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES)) + new_accept_own = community_include( + bgp_attr_get_community(newattr), accept_own); + if (existattr->flag & ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES)) + exist_accept_own = community_include( + bgp_attr_get_community(existattr), accept_own); + + if (new_accept_own && !exist_accept_own) { + *reason = bgp_path_selection_accept_own; + if (debug) + zlog_debug( + "%s: %s wins over %s due to accept-own", + pfx_buf, new_buf, exist_buf); + return 1; + } + + if (!new_accept_own && exist_accept_own) { + *reason = bgp_path_selection_accept_own; + if (debug) + zlog_debug( + "%s: %s loses to %s due to accept-own", + pfx_buf, new_buf, exist_buf); + return 0; + } + } + /* 3. Local route check. We prefer: * - BGP_ROUTE_STATIC * - BGP_ROUTE_AGGREGATE @@ -1061,6 +1107,49 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, if (exist->extra) existm = exist->extra->igpmetric; + if (new->peer->orr_group_name[afi][safi]) { + ret = str2prefix(new->peer->host, &exist_p); + orr_group = bgp_orr_group_lookup_by_name( + bgp, afi, safi, new->peer->orr_group_name[afi][safi]); + if (orr_group) { + orr_group_igp_metric_info = orr_group->igp_metric_info; + if (orr_group_igp_metric_info) { + for (ALL_LIST_ELEMENTS_RO( + orr_group_igp_metric_info, node, + igp_metric)) { + if (ret && + prefix_cmp(&exist_p, + &igp_metric->prefix) == + 0) { + newm = igp_metric->igp_metric; + break; + } + } + } + } + } + if (exist->peer->orr_group_name[afi][safi]) { + ret = str2prefix(exist->peer->host, &exist_p); + orr_group = bgp_orr_group_lookup_by_name( + bgp, afi, safi, exist->peer->orr_group_name[afi][safi]); + if (orr_group) { + orr_group_igp_metric_info = orr_group->igp_metric_info; + if (orr_group_igp_metric_info) { + for (ALL_LIST_ELEMENTS_RO( + orr_group_igp_metric_info, node, + igp_metric)) { + if (ret && + prefix_cmp(&exist_p, + &igp_metric->prefix) == + 0) { + existm = igp_metric->igp_metric; + break; + } + } + } + } + } + if (newm < existm) { if (debug && peer_sort_ret < 0) zlog_debug( @@ -3833,6 +3922,60 @@ static void bgp_attr_add_no_export_community(struct attr *attr) bgp_attr_set_community(attr, new); } +static bool bgp_accept_own(struct peer *peer, afi_t afi, safi_t safi, + struct attr *attr, const struct prefix *prefix, + int *sub_type) +{ + struct listnode *node, *nnode; + struct bgp *bgp; + bool accept_own_found = false; + + if (safi != SAFI_MPLS_VPN) + return false; + + /* Processing of the ACCEPT_OWN community is enabled by configuration */ + if (!CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_ACCEPT_OWN)) + return false; + + /* The route in question carries the ACCEPT_OWN community */ + if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES)) { + struct community *comm = bgp_attr_get_community(attr); + + if (community_include(comm, COMMUNITY_ACCEPT_OWN)) + accept_own_found = true; + } + + /* The route in question is targeted to one or more destination VRFs + * on the router (as determined by inspecting the Route Target(s)). + */ + for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) { + if (bgp->inst_type != BGP_INSTANCE_TYPE_VRF) + continue; + + if (accept_own_found && + ecommunity_include( + bgp->vpn_policy[afi] + .rtlist[BGP_VPN_POLICY_DIR_TOVPN], + bgp_attr_get_ecommunity(attr))) { + if (bgp_debug_update(peer, prefix, NULL, 1)) + zlog_debug( + "%pBP prefix %pFX has ORIGINATOR_ID, but it's accepted due to ACCEPT_OWN", + peer, prefix); + + /* Treat this route as imported, because it's leaked + * already from another VRF, and we got an updated + * version from route-reflector with ACCEPT_OWN + * community. + */ + *sub_type = BGP_ROUTE_IMPORTED; + + return true; + } + } + + return false; +} + int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, struct attr *attr, afi_t afi, safi_t safi, int type, int sub_type, struct prefix_rd *prd, mpls_label_t *label, @@ -3946,12 +4089,20 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, } } - /* Route reflector originator ID check. */ + /* Route reflector originator ID check. If ACCEPT_OWN mechanism is + * enabled, then take care of that too. + */ + bool accept_own = false; + if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID) && IPV4_ADDR_SAME(&bgp->router_id, &attr->originator_id)) { - peer->stat_pfx_originator_loop++; - reason = "originator is us;"; - goto filtered; + accept_own = + bgp_accept_own(peer, afi, safi, attr, p, &sub_type); + if (!accept_own) { + peer->stat_pfx_originator_loop++; + reason = "originator is us;"; + goto filtered; + } } /* Route reflector cluster ID check. */ @@ -4449,8 +4600,13 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, bgp_path_info_unset_flag(dest, pi, BGP_PATH_VALID); } - } else + } else { + if (accept_own) + bgp_path_info_set_flag(dest, pi, + BGP_PATH_ACCEPT_OWN); + bgp_path_info_set_flag(dest, pi, BGP_PATH_VALID); + } #ifdef ENABLE_BGP_VNC if (safi == SAFI_MPLS_VPN) { @@ -4500,8 +4656,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, } if ((SAFI_MPLS_VPN == safi) && (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) { - - leak_success = vpn_leak_to_vrf_update(bgp, pi); + leak_success = vpn_leak_to_vrf_update(bgp, pi, prd); } #ifdef ENABLE_BGP_VNC @@ -4609,8 +4764,12 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, } bgp_path_info_unset_flag(dest, new, BGP_PATH_VALID); } - } else + } else { + if (accept_own) + bgp_path_info_set_flag(dest, new, BGP_PATH_ACCEPT_OWN); + bgp_path_info_set_flag(dest, new, BGP_PATH_VALID); + } /* Addpath ID */ new->addpath_rx_id = addpath_id; @@ -4656,7 +4815,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, } if ((SAFI_MPLS_VPN == safi) && (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) { - leak_success = vpn_leak_to_vrf_update(bgp, new); + leak_success = vpn_leak_to_vrf_update(bgp, new, prd); } #ifdef ENABLE_BGP_VNC if (SAFI_MPLS_VPN == safi) { @@ -6377,7 +6536,8 @@ static void bgp_static_update_safi(struct bgp *bgp, const struct prefix *p, if (SAFI_MPLS_VPN == safi && bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) { - vpn_leak_to_vrf_update(bgp, pi); + vpn_leak_to_vrf_update(bgp, pi, + &bgp_static->prd); } #ifdef ENABLE_BGP_VNC rfapiProcessUpdate(pi->peer, NULL, p, &bgp_static->prd, @@ -6417,7 +6577,7 @@ static void bgp_static_update_safi(struct bgp *bgp, const struct prefix *p, if (SAFI_MPLS_VPN == safi && bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) { - vpn_leak_to_vrf_update(bgp, new); + vpn_leak_to_vrf_update(bgp, new, &bgp_static->prd); } #ifdef ENABLE_BGP_VNC rfapiProcessUpdate(new->peer, NULL, p, &bgp_static->prd, new->attr, afi, @@ -8776,6 +8936,8 @@ const char *bgp_path_selection_reason2str(enum bgp_path_selection_reason reason) return "Weight"; case bgp_path_selection_local_pref: return "Local Pref"; + case bgp_path_selection_accept_own: + return "Accept Own"; case bgp_path_selection_local_route: return "Local Route"; case bgp_path_selection_confed_as_path: @@ -11852,8 +12014,8 @@ static void bgp_show_path_info(const struct prefix_rd *pfx_rd, /* * Return rd based on safi */ -static const struct prefix_rd *bgp_rd_from_dest(const struct bgp_dest *dest, - safi_t safi) +const struct prefix_rd *bgp_rd_from_dest(const struct bgp_dest *dest, + safi_t safi) { switch (safi) { case SAFI_MPLS_VPN: @@ -11862,7 +12024,6 @@ static const struct prefix_rd *bgp_rd_from_dest(const struct bgp_dest *dest, return (struct prefix_rd *)(bgp_dest_get_prefix(dest)); default: return NULL; - } } @@ -12406,6 +12567,7 @@ DEFPY(show_ip_bgp, show_ip_bgp_cmd, |alias ALIAS_NAME\ |A.B.C.D/M longer-prefixes\ |X:X::X:X/M longer-prefixes\ + |optimal-route-reflection [WORD$orr_group_name]\ ] [json$uj [detail$detail] | wide$wide]", SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR BGP_AFI_HELP_STR BGP_SAFI_WITH_LABEL_HELP_STR @@ -12454,6 +12616,8 @@ DEFPY(show_ip_bgp, show_ip_bgp_cmd, "Display route and more specific routes\n" "IPv6 prefix\n" "Display route and more specific routes\n" + "Display Optimal Route Reflection RR Clients\n" + "ORR Group name\n" JSON_STR "Display detailed version of JSON output\n" "Increase table width for longer prefixes\n") @@ -12470,6 +12634,7 @@ DEFPY(show_ip_bgp, show_ip_bgp_cmd, uint16_t show_flags = 0; enum rpki_states rpki_target_state = RPKI_NOT_BEING_USED; struct prefix p; + bool orr_group = false; if (uj) { argc--; @@ -12644,12 +12809,18 @@ DEFPY(show_ip_bgp, show_ip_bgp_cmd, output_arg = &p; } + if (argv_find(argv, argc, "optimal-route-reflection", &idx)) + orr_group = true; + if (!all) { /* show bgp: AFI_IP6, show ip bgp: AFI_IP */ if (community) return bgp_show_community(vty, bgp, community, exact_match, afi, safi, show_flags); + else if (orr_group) + return bgp_show_orr(vty, bgp, afi, safi, orr_group_name, + show_flags); else return bgp_show(vty, bgp, afi, safi, sh_type, output_arg, show_flags, @@ -12695,6 +12866,11 @@ DEFPY(show_ip_bgp, show_ip_bgp_cmd, vty, abgp, community, exact_match, afi, safi, show_flags); + else if (orr_group) + bgp_show_orr(vty, bgp, afi, + safi, + orr_group_name, + show_flags); else bgp_show(vty, abgp, afi, safi, sh_type, output_arg, @@ -12734,6 +12910,11 @@ DEFPY(show_ip_bgp, show_ip_bgp_cmd, vty, abgp, community, exact_match, afi, safi, show_flags); + else if (orr_group) + bgp_show_orr(vty, bgp, afi, + safi, + orr_group_name, + show_flags); else bgp_show(vty, abgp, afi, safi, sh_type, output_arg, diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 1107e3e33e..7c6e60bd92 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -289,7 +289,7 @@ struct bgp_path_info { int lock; /* BGP information status. */ - uint16_t flags; + uint32_t flags; #define BGP_PATH_IGP_CHANGED (1 << 0) #define BGP_PATH_DAMPED (1 << 1) #define BGP_PATH_HISTORY (1 << 2) @@ -306,6 +306,7 @@ struct bgp_path_info { #define BGP_PATH_RIB_ATTR_CHG (1 << 13) #define BGP_PATH_ANNC_NH_SELF (1 << 14) #define BGP_PATH_LINK_BW_CHG (1 << 15) +#define BGP_PATH_ACCEPT_OWN (1 << 16) /* BGP route type. This can be static, RIP, OSPF, BGP etc. */ uint8_t type; @@ -868,4 +869,6 @@ extern void subgroup_announce_reset_nhop(uint8_t family, struct attr *attr); const char * bgp_path_selection_reason2str(enum bgp_path_selection_reason reason); extern bool bgp_addpath_encode_rx(struct peer *peer, afi_t afi, safi_t safi); +extern const struct prefix_rd *bgp_rd_from_dest(const struct bgp_dest *dest, + safi_t safi); #endif /* _QUAGGA_BGP_ROUTE_H */ diff --git a/bgpd/bgp_table.h b/bgpd/bgp_table.h index 86cd4f3da1..91d92ebd6d 100644 --- a/bgpd/bgp_table.h +++ b/bgpd/bgp_table.h @@ -63,6 +63,7 @@ enum bgp_path_selection_reason { bgp_path_selection_evpn_lower_ip, bgp_path_selection_weight, bgp_path_selection_local_pref, + bgp_path_selection_accept_own, bgp_path_selection_local_route, bgp_path_selection_confed_as_path, bgp_path_selection_as_path, diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c index 27e3677702..72e70ebf9f 100644 --- a/bgpd/bgp_updgrp_adv.c +++ b/bgpd/bgp_updgrp_adv.c @@ -355,6 +355,11 @@ static int update_group_announce_walkcb(struct update_group *updgrp, void *arg) struct update_subgroup *subgrp; UPDGRP_FOREACH_SUBGRP (updgrp, subgrp) { + /* Avoid supressing duplicate routes later + * when processing in subgroup_announce_table(). + */ + SET_FLAG(subgrp->sflags, SUBGRP_STATUS_FORCE_UPDATES); + subgroup_announce_all(subgrp); } diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 7de222316f..e574ae7805 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -78,6 +78,8 @@ #ifdef ENABLE_BGP_VNC #include "bgpd/rfapi/bgp_rfapi_cfg.h" #endif +#include "bgpd/bgp_orr.h" + FRR_CFG_DEFAULT_BOOL(BGP_IMPORT_CHECK, { @@ -295,7 +297,7 @@ static int bgp_srv6_locator_unset(struct bgp *bgp) { int ret; struct listnode *node, *nnode; - struct srv6_locator_chunk *chunk; + struct srv6_locator_chunk *chunk, *tovpn_sid_locator; struct bgp_srv6_function *func; struct bgp *bgp_vrf; @@ -340,12 +342,20 @@ static int bgp_srv6_locator_unset(struct bgp *bgp) continue; /* refresh vpnv4 tovpn_sid_locator */ - XFREE(MTYPE_BGP_SRV6_SID, - bgp_vrf->vpn_policy[AFI_IP].tovpn_sid_locator); + tovpn_sid_locator = + bgp_vrf->vpn_policy[AFI_IP].tovpn_sid_locator; + if (tovpn_sid_locator) { + srv6_locator_chunk_free(tovpn_sid_locator); + bgp_vrf->vpn_policy[AFI_IP].tovpn_sid_locator = NULL; + } /* refresh vpnv6 tovpn_sid_locator */ - XFREE(MTYPE_BGP_SRV6_SID, - bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid_locator); + tovpn_sid_locator = + bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid_locator; + if (tovpn_sid_locator) { + srv6_locator_chunk_free(tovpn_sid_locator); + bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid_locator = NULL; + } } /* clear locator name */ @@ -937,6 +947,9 @@ int bgp_vty_return(struct vty *vty, enum bgp_create_error_code ret) case BGP_ERR_INVALID_INTERNAL_ROLE: str = "External roles can be set only on eBGP session"; break; + case BGP_ERR_PEER_ORR_CONFIGURED: + str = "Deconfigure optimal-route-reflection on this peer first"; + break; } if (str) { vty_out(vty, "%% %s\n", str); @@ -5803,7 +5816,7 @@ ALIAS_HIDDEN(neighbor_remove_private_as_all, "neighbor <A.B.C.D|X:X::X:X|WORD> remove-private-AS all", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Remove private ASNs in outbound updates\n" - "Apply to all AS numbers") + "Apply to all AS numbers\n") DEFUN (neighbor_remove_private_as_replace_as, neighbor_remove_private_as_replace_as_cmd, @@ -6193,6 +6206,43 @@ ALIAS_HIDDEN(no_neighbor_route_reflector_client, NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Configure a neighbor as Route Reflector client\n") +/* optimal-route-reflection Root Routers configuration */ +DEFPY (optimal_route_reflection, + optimal_route_reflection_cmd, + "[no$no] optimal-route-reflection WORD$orr_group [<A.B.C.D|X:X::X:X>$primary [<A.B.C.D|X:X::X:X>$secondary [<A.B.C.D|X:X::X:X>$tertiary]]]", + NO_STR + "Create ORR group and assign root router(s)\n" + "ORR Group name\n" + "Primary Root address\n" + "Primary Root IPv6 address\n" + "Secondary Root address\n" + "Secondary Root IPv6 address\n" + "Tertiary Root address\n" + "Tertiary Root IPv6 address\n") +{ + if (!no && !primary) { + vty_out(vty, "%% Specify Primary Root address\n"); + return CMD_WARNING_CONFIG_FAILED; + } + return bgp_afi_safi_orr_group_set_vty( + vty, bgp_node_afi(vty), bgp_node_safi(vty), orr_group, + primary_str, secondary_str, tertiary_str, !!no); +} + +/* neighbor optimal-route-reflection group*/ +DEFPY (neighbor_optimal_route_reflection, + neighbor_optimal_route_reflection_cmd, + "[no$no] neighbor <A.B.C.D|X:X::X:X|WORD>$neighbor optimal-route-reflection WORD$orr_group", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Apply ORR group configuration to the neighbor\n" + "ORR group name\n") +{ + return peer_orr_group_set_vty(vty, neighbor, bgp_node_afi(vty), + bgp_node_safi(vty), orr_group, !!no); +} + /* neighbor route-server-client. */ DEFUN (neighbor_route_server_client, neighbor_route_server_client_cmd, @@ -8277,6 +8327,32 @@ ALIAS_HIDDEN( "Only give warning message when limit is exceeded\n" "Force checking all received routes not only accepted\n") +/* "neighbor accept-own" */ +DEFPY (neighbor_accept_own, + neighbor_accept_own_cmd, + "[no$no] neighbor <A.B.C.D|X:X::X:X|WORD>$neighbor accept-own", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Enable handling of self-originated VPN routes containing ACCEPT_OWN community\n") +{ + struct peer *peer; + afi_t afi = bgp_node_afi(vty); + safi_t safi = bgp_node_safi(vty); + int ret; + + peer = peer_and_group_lookup_vty(vty, neighbor); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + if (no) + ret = peer_af_flag_unset(peer, afi, safi, PEER_FLAG_ACCEPT_OWN); + else + ret = peer_af_flag_set(peer, afi, safi, PEER_FLAG_ACCEPT_OWN); + + return bgp_vty_return(vty, ret); +} + /* "neighbor soo" */ DEFPY (neighbor_soo, neighbor_soo_cmd, @@ -8890,7 +8966,7 @@ DEFPY (af_label_vpn_export, DEFPY (af_sid_vpn_export, af_sid_vpn_export_cmd, - "[no] sid vpn export <(1-255)$sid_idx|auto$sid_auto>", + "[no] sid vpn export <(1-1048575)$sid_idx|auto$sid_auto>", NO_STR "sid value for VRF\n" "Between current address-family and vpn\n" @@ -12380,6 +12456,11 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, if (CHECK_FLAG(p->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)) vty_out(vty, " Route-Server Client\n"); + + if (peer_af_flag_check(p, afi, safi, PEER_FLAG_ORR_GROUP)) + vty_out(vty, " ORR group (configured) : %s\n", + p->orr_group_name[afi][safi]); + if (CHECK_FLAG(p->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG)) vty_out(vty, " Inbound soft reconfiguration allowed\n"); @@ -17307,6 +17388,10 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, } } + /* accept-own */ + if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_ACCEPT_OWN)) + vty_out(vty, " neighbor %s accept-own\n", addr); + /* soo */ if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_SOO)) { char *soo_str = ecommunity_ecom2str( @@ -17356,6 +17441,10 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, : ""); } } + + if (peer_af_flag_check(peer, afi, safi, PEER_FLAG_ORR_GROUP)) + vty_out(vty, " neighbor %s optimal-route-reflection %s\n", + addr, peer->orr_group_name[afi][safi]); } static void bgp_vpn_config_write(struct vty *vty, struct bgp *bgp, afi_t afi, @@ -17462,6 +17551,9 @@ static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi, } } + /* Optimal Route Reflection */ + bgp_config_write_orr(vty, bgp, afi, safi); + vty_endframe(vty, " exit-address-family\n"); } @@ -18971,6 +19063,34 @@ void bgp_vty_init(void) install_element(BGP_EVPN_NODE, &neighbor_route_reflector_client_cmd); install_element(BGP_EVPN_NODE, &no_neighbor_route_reflector_client_cmd); + /* "optimal-route-reflection" commands */ + install_element(BGP_IPV4_NODE, &optimal_route_reflection_cmd); + install_element(BGP_IPV4M_NODE, &optimal_route_reflection_cmd); + install_element(BGP_IPV4L_NODE, &optimal_route_reflection_cmd); + install_element(BGP_IPV6_NODE, &optimal_route_reflection_cmd); + install_element(BGP_IPV6M_NODE, &optimal_route_reflection_cmd); + install_element(BGP_IPV6L_NODE, &optimal_route_reflection_cmd); + install_element(BGP_VPNV4_NODE, &optimal_route_reflection_cmd); + install_element(BGP_VPNV6_NODE, &optimal_route_reflection_cmd); + install_element(BGP_FLOWSPECV4_NODE, &optimal_route_reflection_cmd); + install_element(BGP_FLOWSPECV6_NODE, &optimal_route_reflection_cmd); + install_element(BGP_EVPN_NODE, &optimal_route_reflection_cmd); + + /* "neighbor optimal-route-reflection" commands */ + install_element(BGP_IPV4_NODE, &neighbor_optimal_route_reflection_cmd); + install_element(BGP_IPV4M_NODE, &neighbor_optimal_route_reflection_cmd); + install_element(BGP_IPV4L_NODE, &neighbor_optimal_route_reflection_cmd); + install_element(BGP_IPV6_NODE, &neighbor_optimal_route_reflection_cmd); + install_element(BGP_IPV6M_NODE, &neighbor_optimal_route_reflection_cmd); + install_element(BGP_IPV6L_NODE, &neighbor_optimal_route_reflection_cmd); + install_element(BGP_VPNV4_NODE, &neighbor_optimal_route_reflection_cmd); + install_element(BGP_VPNV6_NODE, &neighbor_optimal_route_reflection_cmd); + install_element(BGP_FLOWSPECV4_NODE, + &neighbor_optimal_route_reflection_cmd); + install_element(BGP_FLOWSPECV6_NODE, + &neighbor_optimal_route_reflection_cmd); + install_element(BGP_EVPN_NODE, &neighbor_optimal_route_reflection_cmd); + /* "neighbor route-server" commands.*/ install_element(BGP_NODE, &neighbor_route_server_client_hidden_cmd); install_element(BGP_NODE, &no_neighbor_route_server_client_hidden_cmd); @@ -19481,6 +19601,10 @@ void bgp_vty_init(void) install_element(BGP_EVPN_NODE, &neighbor_allowas_in_cmd); install_element(BGP_EVPN_NODE, &no_neighbor_allowas_in_cmd); + /* neighbor accept-own */ + install_element(BGP_VPNV4_NODE, &neighbor_accept_own_cmd); + install_element(BGP_VPNV6_NODE, &neighbor_accept_own_cmd); + /* "neighbor soo" */ install_element(BGP_IPV4_NODE, &neighbor_soo_cmd); install_element(BGP_IPV4_NODE, &no_neighbor_soo_cmd); diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 57a859c61d..b5c13fddd0 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -67,10 +67,13 @@ #include "bgpd/bgp_trace.h" #include "bgpd/bgp_community.h" #include "bgpd/bgp_lcommunity.h" +#include "bgpd/bgp_orr.h" /* All information about zebra. */ struct zclient *zclient = NULL; +static int bgp_opaque_msg_handler(ZAPI_CALLBACK_ARGS); + /* hook to indicate vrf status change for SNMP */ DEFINE_HOOK(bgp_vrf_status_changed, (struct bgp *bgp, struct interface *ifp), (bgp, ifp)); @@ -3255,10 +3258,10 @@ static int bgp_zebra_process_srv6_locator_delete(ZAPI_CALLBACK_ARGS) struct srv6_locator loc = {}; struct bgp *bgp = bgp_get_default(); struct listnode *node, *nnode; - struct srv6_locator_chunk *chunk; + struct srv6_locator_chunk *chunk, *tovpn_sid_locator; struct bgp_srv6_function *func; struct bgp *bgp_vrf; - struct in6_addr *tovpn_sid, *tovpn_sid_locator; + struct in6_addr *tovpn_sid; struct prefix_ipv6 tmp_prefi; if (zapi_srv6_locator_decode(zclient->ibuf, &loc) < 0) @@ -3327,10 +3330,13 @@ static int bgp_zebra_process_srv6_locator_delete(ZAPI_CALLBACK_ARGS) if (tovpn_sid_locator) { tmp_prefi.family = AF_INET6; tmp_prefi.prefixlen = IPV6_MAX_BITLEN; - tmp_prefi.prefix = *tovpn_sid_locator; + tmp_prefi.prefix = tovpn_sid_locator->prefix.prefix; if (prefix_match((struct prefix *)&loc.prefix, - (struct prefix *)&tmp_prefi)) - XFREE(MTYPE_BGP_SRV6_SID, tovpn_sid_locator); + (struct prefix *)&tmp_prefi)) { + srv6_locator_chunk_free(tovpn_sid_locator); + bgp_vrf->vpn_policy[AFI_IP].tovpn_sid_locator = + NULL; + } } /* refresh vpnv6 tovpn_sid_locator */ @@ -3339,10 +3345,13 @@ static int bgp_zebra_process_srv6_locator_delete(ZAPI_CALLBACK_ARGS) if (tovpn_sid_locator) { tmp_prefi.family = AF_INET6; tmp_prefi.prefixlen = IPV6_MAX_BITLEN; - tmp_prefi.prefix = *tovpn_sid_locator; + tmp_prefi.prefix = tovpn_sid_locator->prefix.prefix; if (prefix_match((struct prefix *)&loc.prefix, - (struct prefix *)&tmp_prefi)) - XFREE(MTYPE_BGP_SRV6_SID, tovpn_sid_locator); + (struct prefix *)&tmp_prefi)) { + srv6_locator_chunk_free(tovpn_sid_locator); + bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid_locator = + NULL; + } } } @@ -3382,6 +3391,7 @@ static zclient_handler *const bgp_handlers[] = { [ZEBRA_SRV6_LOCATOR_DELETE] = bgp_zebra_process_srv6_locator_delete, [ZEBRA_SRV6_MANAGER_GET_LOCATOR_CHUNK] = bgp_zebra_process_srv6_locator_chunk, + [ZEBRA_OPAQUE_MESSAGE] = bgp_opaque_msg_handler, }; static int bgp_if_new_hook(struct interface *ifp) @@ -3836,3 +3846,34 @@ int bgp_zebra_srv6_manager_release_locator_chunk(const char *name) { return srv6_manager_release_locator_chunk(zclient, name); } + +/* + * ORR messages between processes + */ +static int bgp_opaque_msg_handler(ZAPI_CALLBACK_ARGS) +{ + struct stream *s; + struct zapi_opaque_msg info; + struct orr_igp_metric_info table; + int ret = 0; + + s = zclient->ibuf; + + if (zclient_opaque_decode(s, &info) != 0) { + bgp_orr_debug("%s: opaque decode failed", __func__); + return -1; + } + + switch (info.type) { + case ORR_IGP_METRIC_UPDATE: + STREAM_GET(&table, s, sizeof(table)); + ret = bgg_orr_message_process(BGP_ORR_IMSG_IGP_METRIC_UPDATE, + (void *)&table); + break; + default: + break; + } + +stream_failure: + return ret; +} diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 1bcd714407..40e6c90dfc 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -92,6 +92,7 @@ #include "bgpd/bgp_evpn_private.h" #include "bgpd/bgp_evpn_mh.h" #include "bgpd/bgp_mac.h" +#include "bgpd/bgp_orr.h" DEFINE_MTYPE_STATIC(BGPD, PEER_TX_SHUTDOWN_MSG, "Peer shutdown message (TX)"); DEFINE_MTYPE_STATIC(BGPD, BGP_EVPN_INFO, "BGP EVPN instance information"); @@ -1839,6 +1840,8 @@ bool bgp_afi_safi_peer_exists(struct bgp *bgp, afi_t afi, safi_t safi) /* Change peer's AS number. */ void peer_as_change(struct peer *peer, as_t as, int as_specified) { + afi_t afi; + safi_t safi; enum bgp_peer_sort origtype, newtype; /* Stop peer. */ @@ -1877,6 +1880,11 @@ void peer_as_change(struct peer *peer, as_t as, int as_specified) /* reflector-client reset */ if (newtype != BGP_PEER_IBGP) { + + FOREACH_AFI_SAFI (afi, safi) + UNSET_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_ORR_GROUP); + UNSET_FLAG(peer->af_flags[AFI_IP][SAFI_UNICAST], PEER_FLAG_REFLECTOR_CLIENT); UNSET_FLAG(peer->af_flags[AFI_IP][SAFI_MULTICAST], @@ -3850,6 +3858,8 @@ void bgp_free(struct bgp *bgp) ecommunity_free(&bgp->vpn_policy[afi].rtlist[dir]); } + bgp_orr_cleanup(bgp); + XFREE(MTYPE_BGP, bgp->name); XFREE(MTYPE_BGP, bgp->name_pretty); XFREE(MTYPE_BGP, bgp->snmp_stats); @@ -4303,6 +4313,7 @@ static const struct peer_flag_action peer_af_flag_action_list[] = { {PEER_FLAG_WEIGHT, 0, peer_change_reset_in}, {PEER_FLAG_DISABLE_ADDPATH_RX, 0, peer_change_reset}, {PEER_FLAG_SOO, 0, peer_change_reset}, + {PEER_FLAG_ACCEPT_OWN, 0, peer_change_reset}, {0, 0, 0}}; /* Proper action set. */ @@ -4633,6 +4644,11 @@ static int peer_af_flag_modify(struct peer *peer, afi_t afi, safi_t safi, if (flag & PEER_FLAG_REFLECTOR_CLIENT && ptype != BGP_PEER_IBGP) return BGP_ERR_NOT_INTERNAL_PEER; + /* Do not remove reflector client when ORR is configured on this peer */ + if (flag & PEER_FLAG_REFLECTOR_CLIENT && !set && + peer_orr_rrclient_check(peer, afi, safi)) + return BGP_ERR_PEER_ORR_CONFIGURED; + /* Special check for remove-private-AS. */ if (flag & PEER_FLAG_REMOVE_PRIVATE_AS && ptype == BGP_PEER_IBGP) return BGP_ERR_REMOVE_PRIVATE_AS; diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index af46550650..44e225b043 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -47,6 +47,7 @@ #include "bgp_io.h" #include "lib/bfd.h" +#include "lib/orr_msg.h" #define BGP_MAX_HOSTNAME 64 /* Linux max, is larger than most other sys */ #define BGP_PEER_MAX_HASH_SIZE 16384 @@ -197,6 +198,40 @@ struct bgp_redist { struct bgp_rmap rmap; }; +struct bgp_orr_igp_metric { + struct prefix prefix; + uint32_t igp_metric; +}; + +struct bgp_orr_group { + /* Name of this ORR group */ + char *name; + + /* Address Family Identifiers */ + afi_t afi; + safi_t safi; + + /* Pointer to BGP */ + struct bgp *bgp; + + /* Root Routers of the group */ + struct peer *primary; + struct peer *secondary; + struct peer *tertiary; + + /* Active Root Router of the group */ + struct peer *active; + + /* RR clients belong to this group */ + struct list *rr_client_list; + + /* IGP metric data from active root */ + struct list *igp_metric_info; + + /* Route table calculated from active root for this group */ + struct bgp_table *route_table; +}; + enum vpn_policy_direction { BGP_VPN_POLICY_DIR_FROMVPN = 0, BGP_VPN_POLICY_DIR_TOVPN = 1, @@ -239,7 +274,7 @@ struct vpn_policy { */ uint32_t tovpn_sid_index; /* unset => set to 0 */ struct in6_addr *tovpn_sid; - struct in6_addr *tovpn_sid_locator; + struct srv6_locator_chunk *tovpn_sid_locator; uint32_t tovpn_sid_transpose_label; struct in6_addr *tovpn_zebra_vrf_sid_last_sent; }; @@ -779,6 +814,10 @@ struct bgp { bool allow_martian; + /* BGP optimal route reflection group and Root Router configuration */ + uint32_t orr_group_count; + struct list *orr_group[AFI_MAX][SAFI_MAX]; + QOBJ_FIELDS; }; DECLARE_QOBJ_TYPE(bgp); @@ -1422,6 +1461,11 @@ struct peer { #define PEER_FLAG_MAX_PREFIX_FORCE (1ULL << 28) #define PEER_FLAG_DISABLE_ADDPATH_RX (1ULL << 29) #define PEER_FLAG_SOO (1ULL << 30) +#define PEER_FLAG_ORR_GROUP (1ULL << 31) /* Optimal-Route-Reflection */ +#define PEER_FLAG_ACCEPT_OWN (1ULL << 32) + + /* BGP Optimal Route Reflection Group name */ + char *orr_group_name[AFI_MAX][SAFI_MAX]; enum bgp_addpath_strat addpath_type[AFI_MAX][SAFI_MAX]; @@ -2029,7 +2073,10 @@ enum bgp_create_error_code { /*BGP Open Policy ERRORS */ BGP_ERR_INVALID_ROLE_NAME = -35, - BGP_ERR_INVALID_INTERNAL_ROLE = -36 + BGP_ERR_INVALID_INTERNAL_ROLE = -36, + + /* BGP ORR ERRORS */ + BGP_ERR_PEER_ORR_CONFIGURED = -37, }; /* @@ -2081,6 +2128,7 @@ extern struct peer_group *peer_group_lookup_dynamic_neighbor(struct bgp *, extern struct peer *peer_lookup_dynamic_neighbor(struct bgp *, union sockunion *); +extern bool peer_orr_rrclient_check(struct peer *peer, afi_t afi, safi_t safi); /* * Peers are incredibly easy to memory leak * due to the various ways that they are actually used @@ -2331,6 +2379,12 @@ extern void bgp_shutdown_disable(struct bgp *bgp); extern void bgp_close(void); extern void bgp_free(struct bgp *); void bgp_gr_apply_running_config(void); +extern int bgp_afi_safi_orr_group_set(struct bgp *bgp, afi_t afi, safi_t safi, + const char *name, struct peer *primary, + struct peer *secondary, + struct peer *tertiary); +extern int bgp_afi_safi_orr_group_unset(struct bgp *bgp, afi_t afi, safi_t safi, + const char *name); /* BGP GR */ int bgp_global_gr_init(struct bgp *bgp); diff --git a/bgpd/subdir.am b/bgpd/subdir.am index b1eeb937e1..765650313b 100644 --- a/bgpd/subdir.am +++ b/bgpd/subdir.am @@ -103,6 +103,7 @@ bgpd_libbgp_a_SOURCES = \ bgpd/bgp_vty.c \ bgpd/bgp_zebra.c \ bgpd/bgpd.c \ + bgpd/bgp_orr.c \ bgpd/bgp_trace.c \ # end @@ -183,6 +184,7 @@ noinst_HEADERS += \ bgpd/bgp_vty.h \ bgpd/bgp_zebra.h \ bgpd/bgpd.h \ + bgpd/bgp_orr.h \ bgpd/bgp_trace.h \ \ bgpd/rfapi/bgp_rfapi_cfg.h \ diff --git a/configure.ac b/configure.ac index 4e1080045e..4cbdfe0fcc 100644 --- a/configure.ac +++ b/configure.ac @@ -2631,6 +2631,7 @@ AC_DEFINE_UNQUOTED([ZEBRA_SERV_PATH], ["$frr_statedir%s%s/zserv.api"], [zebra ap AC_DEFINE_UNQUOTED([BFDD_CONTROL_SOCKET], ["$frr_statedir%s%s/bfdd.sock"], [bfdd control socket]) AC_DEFINE_UNQUOTED([OSPFD_GR_STATE], ["$frr_statedir%s/ospfd-gr.json"], [ospfd GR state information]) AC_DEFINE_UNQUOTED([OSPF6D_GR_STATE], ["$frr_statedir/ospf6d-gr.json"], [ospf6d GR state information]) +AC_DEFINE_UNQUOTED([ISISD_RESTART], ["$frr_statedir%s/isid-restart.json"], [isisd restart information]) AC_DEFINE_UNQUOTED([OSPF6_AUTH_SEQ_NUM_FILE], ["$frr_statedir/ospf6d-at-seq-no.dat"], [ospf6d AT Sequence number information]) AC_DEFINE_UNQUOTED([DAEMON_VTY_DIR], ["$frr_statedir%s%s"], [daemon vty directory]) AC_DEFINE_UNQUOTED([DAEMON_DB_DIR], ["$frr_statedir"], [daemon database directory]) diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 25d1cbfb6b..8a16c57e6c 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -1670,6 +1670,23 @@ Configuring Peers turning on this command will allow BGP to install v4 routes with v6 nexthops if you do not have v4 configured on interfaces. +.. clicmd:: neighbor <A.B.C.D|X:X::X:X|WORD> accept-own + + Enable handling of self-originated VPN routes containing ``accept-own`` community. + + This feature allows you to handle self-originated VPN routes, which a BGP speaker + receives from a route-reflector. A 'self-originated' route is one that was + originally advertised by the speaker itself. As per :rfc:`4271`, a BGP speaker rejects + advertisements that originated the speaker itself. However, the BGP ACCEPT_OWN + mechanism enables a router to accept the prefixes it has advertised, when reflected + from a route-reflector that modifies certain attributes of the prefix. + + A special community called ``accept-own`` is attached to the prefix by the + route-reflector, which is a signal to the receiving router to bypass the ORIGINATOR_ID + and NEXTHOP/MP_REACH_NLRI check. + + Default: disabled. + .. clicmd:: bgp fast-external-failover This command causes bgp to take down ebgp peers immediately @@ -3478,6 +3495,319 @@ When default route is not present in R2'2 BGP table, 10.139.224.0/20 and 192.0.2 Total number of prefixes 3 Router2# +.. _bgp-optimal-route-reflection: + +BGP Optimal Route Reflection +---------------------------- +BGP Route Reflectors (RRs) are used to improve network scalability by reducing +or eliminating the need for a full-mesh of IBGP sessions. + +When a BGP RR receives multiple paths for the same IP prefix, it typically +selects a single best path to send for all its clients. +If the RR has multiple nearly-equal best paths and the tie-break is determined +by the next-hop cost, the RR advertises the path based on its view of next-hop +costs, which leads to a non-optimal routing. +The advertised route may differ from the path that a client would select +if it had the visibility of the same set of candidate paths and used +its own view of next-hop costs. + +Non-optimal advertisements by the RR can be a problem in hot-potato routing. +Hot-potato routing aims to hand off traffic to the next AS using the closest +possible exit point from the local AS. +In this context, the closest exit point implies minimum IGP cost to +reach the BGP next-hop. + +The BGP Optimal Route Reflection allows the RR to choose and send a different +best path to a different or a set of RR clients. + +A link-state protocol is required. It can be OSPF or IS-IS. +Current implementation of BGP ORR is based on the IGP cost to the BGP next hop, +and not based on some configured policy. + +RR runs Shortest Path First (SPF) calculation with the selected +router as the root of the tree and calculates the cost to every other router. + +This special SPF calculation with another router as the root, is referred to as +a Reverse SPF (rSPF). This can only be done if the RR learns all the BGP paths +from all the BGP border routers. + +There could be as many rSPFs run as there are RR clients. +This will increase the CPU load somewhat on the RR. + +Current implementation allows up to three root nodes for the rSPF calculation. +There is no need to configure each RR client as a root and run rSPF. +Current implementation allows to configure three, the primary, the secondary, +and the tertiary root, per set of RR clients, for redundancy purposes. +For the BGP ORR feature to apply to any RR client, that RR client must be +configured to be part of an ORR policy group. + +The BGP ORR feature is enabled per address family. + +The minimal configuration needed: + +1. ORR needs to be enabled for specific groups of BGP neighbors. +2. For each group of BGP neighbors, at least one root needs to be configured. + Optionally, a secondary and tertiary root can be configured. +3. For OSPF, the root routers(RR clients) need additional configuration + to make BGP ORR work. + i.e. The MPLS TE configuration on the root router needs to have the minimal + configuration for MPLS TE enabled so that OSPF advertises the MPLS TE + router ID in an opaque-area LSA (type 10). + Once the RR has an opaque-area LSA with the MPLS TE router-ID matching the + configured root router address, rSPF can run and BGP on the RR can + advertise the optimal route. + +.. clicmd:: neighbor A.B.C.D optimal-route-reflection NAME + + This command allows the neighbor to be part of the ORR group. + +.. clicmd:: optimal-route-reflection orr-1 A.B.C.D [A.B.C.D] [A.B.C.D] + + This command creates an ORR group with a mandatory primary root + and optional secondary and/or tertiary roots. + When primary is reachable it will be the active root. + when primary goes down, secondary followed by tertiary takes over + the active root's role. + Always rSPF calculation runs active root as the root. + Which means the RR advertises the path based on active root's + view of next-hop costs. + +Sample Configuration +^^^^^^^^^^^^^^^^^^^^ + +Sample configuration on Route Reflector + +.. code-block:: frr + + ! + debug ospf 8 orr + debug bgp optimal-route-reflection + ! + interface enp0s8 + ip address 10.10.68.8/24 + ip ospf 8 area 0 + exit + ! + interface lo + ip address 10.100.1.8/32 + ip ospf 8 area 0 + exit + ! + router bgp 1 + neighbor 10.100.1.1 remote-as 1 + neighbor 10.100.1.1 update-source lo + neighbor 10.100.1.2 remote-as 1 + neighbor 10.100.1.2 update-source lo + neighbor 10.100.1.3 remote-as 1 + neighbor 10.100.1.3 update-source lo + neighbor 10.100.1.4 remote-as 1 + neighbor 10.100.1.4 update-source lo + ! + address-family ipv4 unicast + neighbor 10.100.1.1 route-reflector-client + neighbor 10.100.1.1 optimal-route-reflection orr-1 + neighbor 10.100.1.2 route-reflector-client + neighbor 10.100.1.2 optimal-route-reflection orr-1 + neighbor 10.100.1.3 route-reflector-client + neighbor 10.100.1.3 optimal-route-reflection orr-1 + neighbor 10.100.1.4 route-reflector-client + neighbor 10.100.1.4 optimal-route-reflection orr-1 + optimal-route-reflection orr-1 10.100.1.4 10.100.1.3 10.100.1.1 + exit-address-family + exit + ! + router ospf 8 + ospf router-id 8.8.8.8 + area 0 authentication + capability opaque + exit + ! + end + +Sample configuration on RR clients + +.. code-block:: frr + + interface enp0s8 + ip address 10.10.34.4/24 + ip ospf 4 area 0 + link-params + enable + exit-link-params + exit + ! + interface enp0s9 + ip address 10.10.74.4/24 + ip ospf 4 area 0 + link-params + enable + exit-link-params + exit + ! + interface lo + ip address 10.100.1.4/32 + ip ospf 4 area 0 + exit + ! + router bgp 1 + neighbor 10.100.1.8 remote-as 1 + neighbor 10.100.1.8 update-source lo + ! + address-family ipv4 unicast + neighbor 10.100.1.8 soft-reconfiguration inbound + exit-address-family + exit + ! + router ospf 4 + ospf router-id 4.4.4.4 + area 0 authentication + capability opaque + mpls-te on + mpls-te router-address 10.100.1.4 + mpls-te inter-as area 0.0.0.0 + mpls-te export + exit + ! + end + +Sample Output +^^^^^^^^^^^^^ + +When Optimal Route Reflection is not enabled on RR, it sends 10.100.1.1 as the best path to its clients. + +.. code-block:: frr + + Router-RR# show ip bgp neighbors 10.100.1.4 + + !--- Output suppressed. + + For address family: IPv4 Unicast + Update group 2, subgroup 2 + Packet Queue length 0 + Route-Reflector Client + Community attribute sent to this neighbor(all) + 0 accepted prefixes + + !--- Output suppressed. + + Router-RR# + Router-RR# show ip bgp + BGP table version is 3, local router ID is 10.100.1.8, vrf id 0 + Default local pref 100, local AS 1 + Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, + i internal, r RIB-failure, S Stale, R Removed + Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self + Origin codes: i - IGP, e - EGP, ? - incomplete + RPKI validation codes: V valid, I invalid, N Not found + + Network Next Hop Metric LocPrf Weight Path + * i203.0.113.0/24 10.100.1.2 0 100 0 i + *>i 10.100.1.1 0 100 0 i + *=i 10.100.1.3 0 100 0 i + + Displayed 1 routes and 3 total paths + Router-RR# + + Router-PE4# show ip bgp + BGP table version is 5, local router ID is 10.100.1.4, vrf id 0 + Default local pref 100, local AS 1 + Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, + i internal, r RIB-failure, S Stale, R Removed + Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self + Origin codes: i - IGP, e - EGP, ? - incomplete + RPKI validation codes: V valid, I invalid, N Not found + + Network Next Hop Metric LocPrf Weight Path + *>i203.0.113.0/24 10.100.1.1 0 100 0 i + + Displayed 1 routes and 1 total paths + Router-PE4# + +When Optimal Route Reflection is enabled on RR, it sends 10.100.1.3 as the best path to its clients. + +.. code-block:: frr + + Router-RR# show ip bgp neighbors 10.100.1.4 + + !--- Output suppressed. + + For address family: IPv4 Unicast + Update group 1, subgroup 1 + Packet Queue length 0 + Route-Reflector Client + ORR group (configured) : orr-1 + Community attribute sent to this neighbor(all) + 0 accepted prefixes + + !--- Output suppressed. + + Router-RR# + Router-RR# show ip bgp + BGP table version is 1, local router ID is 10.100.1.8, vrf id 0 + Default local pref 100, local AS 1 + Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, + i internal, r RIB-failure, S Stale, R Removed + Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self + Origin codes: i - IGP, e - EGP, ? - incomplete + RPKI validation codes: V valid, I invalid, N Not found + + Network Next Hop Metric LocPrf Weight Path + * i203.0.113.0/24 10.100.1.2 0 100 0 i + *>i 10.100.1.3 0 100 0 i + * i 10.100.1.1 0 100 0 i + + Displayed 1 routes and 3 total paths + Router-RR# + + Router-RR# show ip bgp optimal-route-reflection + + ORR group: orr-1, IPv4 Unicast + Configured root: primary: 10.100.1.4(Router-PE4), secondary: 10.100.1.3(Router-PE3), tertiary: 10.100.1.1(Router-PE1) + Active Root: 10.100.1.4(Router-PE4) + + RR Clients mapped: + 10.100.1.1 + 10.100.1.2 + 10.100.1.3 + 10.100.1.4 + + Number of mapping entries: 4 + + Prefix Cost + 10.10.34.0/24 100 + 10.10.61.0/24 300 + 10.10.63.0/24 200 + 10.10.67.0/24 200 + 10.10.68.0/24 300 + 10.10.72.0/24 200 + 10.10.74.0/24 100 + 10.100.1.1/32 300 + 10.100.1.2/32 200 + 10.100.1.3/32 100 + 10.100.1.4/32 0 + 10.100.1.6/32 200 + 10.100.1.7/32 100 + 10.100.1.8/32 300 + + Number of mapping entries: 14 + + Router-RR# + + Router-PE4# show ip bgp + BGP table version is 3, local router ID is 10.100.1.4, vrf id 0 + Default local pref 100, local AS 1 + Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, + i internal, r RIB-failure, S Stale, R Removed + Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self + Origin codes: i - IGP, e - EGP, ? - incomplete + RPKI validation codes: V valid, I invalid, N Not found + + Network Next Hop Metric LocPrf Weight Path + *>i203.0.113.0/24 10.100.1.3 0 100 0 i + + Displayed 1 routes and 1 total paths + Router-PE4# + .. _bgp-debugging: Debugging @@ -3543,6 +3873,10 @@ Debugging Enable or disable debugging of communications between *bgpd* and *zebra*. +.. clicmd:: debug bgp optimal-route-reflection + + Enable or disable debugging of BGP Optimal Route Reflection. + Dumping Messages and Routing Tables ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/doc/user/isisd.rst b/doc/user/isisd.rst index 9ccb5ba4b5..2b114ad127 100644 --- a/doc/user/isisd.rst +++ b/doc/user/isisd.rst @@ -83,6 +83,10 @@ writing, *isisd* does not support multiple ISIS processes. Set overload bit to avoid any transit traffic. +.. clicmd:: set-overload-bit on-startup (0-86400) + + Set overload bit on startup for the specified duration, in seconds. Reference: :rfc:`3277` + .. clicmd:: purge-originator Enable or disable :rfc:`6232` purge originator identification. diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst index 3bb018548e..3aa3d47f35 100644 --- a/doc/user/ospfd.rst +++ b/doc/user/ospfd.rst @@ -831,6 +831,12 @@ Showing Information Show the OSPF routing table, as determined by the most recent SPF calculation. +.. clicmd:: show ip ospf (1-65535) route orr [NAME] + +.. clicmd:: show ip ospf [vrf <NAME|all>] route orr [NAME] + + Show the OSPF routing table, calculated from the active root of all ORR groups or specified ORR group. + .. clicmd:: show ip ospf graceful-restart helper [detail] [json] Displays the Grcaeful Restart Helper details including helper @@ -1138,6 +1144,10 @@ Debugging OSPF .. clicmd:: show debugging ospf +.. clicmd:: debug ospf orr + + Enable or disable debugging of BGP Optimal Route Reflection. + Sample Configuration ==================== diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst index 05990e2523..01cf5316a3 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -745,7 +745,7 @@ and this section also helps that case. Create a new locator. If the name of an existing locator is specified, move to specified locator's configuration node to change the settings it. -.. clicmd:: prefix X:X::X:X/M [func-bits 32] +.. clicmd:: prefix X:X::X:X/M [func-bits (0-64)] Set the ipv6 prefix block of the locator. SRv6 locator is defined by RFC8986. The actual routing protocol specifies the locator and allocates a diff --git a/isisd/isis_cli.c b/isisd/isis_cli.c index a673cb8c1e..9db867e2c0 100644 --- a/isisd/isis_cli.c +++ b/isisd/isis_cli.c @@ -404,7 +404,7 @@ DEFPY_YANG(set_overload_bit, set_overload_bit_cmd, "[no] set-overload-bit", "Reset overload bit to accept transit traffic\n" "Set overload bit to avoid any transit traffic\n") { - nb_cli_enqueue_change(vty, "./overload", NB_OP_MODIFY, + nb_cli_enqueue_change(vty, "./overload/enabled", NB_OP_MODIFY, no ? "false" : "true"); return nb_cli_apply_changes(vty, NULL); @@ -419,6 +419,42 @@ void cli_show_isis_overload(struct vty *vty, const struct lyd_node *dnode, } /* + * XPath: /frr-isisd:isis/instance/overload/on-startup + */ +DEFPY_YANG(set_overload_bit_on_startup, set_overload_bit_on_startup_cmd, + "set-overload-bit on-startup (0-86400)$val", + "Set overload bit to avoid any transit traffic\n" + "Set overload bit on startup\n" + "Set overload time in seconds\n") +{ + nb_cli_enqueue_change(vty, "./overload/on-startup", NB_OP_MODIFY, + val_str); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG(no_set_overload_bit_on_startup, no_set_overload_bit_on_startup_cmd, + "no set-overload-bit on-startup [(0-86400)$val]", + NO_STR + "Reset overload bit to accept transit traffic\n" + "Set overload bit on startup\n" + "Set overload time in seconds\n") +{ + nb_cli_enqueue_change(vty, "./overload/on-startup", NB_OP_DESTROY, + NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +void cli_show_isis_overload_on_startup(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults) +{ + vty_out(vty, " set-overload-bit on-startup %s\n", + yang_dnode_get_string(dnode, NULL)); +} + +/* * XPath: /frr-isisd:isis/instance/attach-send */ DEFPY_YANG(attached_bit_send, attached_bit_send_cmd, "[no] attached-bit send", @@ -3107,6 +3143,9 @@ void isis_cli_init(void) install_element(ISIS_NODE, &dynamic_hostname_cmd); install_element(ISIS_NODE, &set_overload_bit_cmd); + install_element(ISIS_NODE, &set_overload_bit_on_startup_cmd); + install_element(ISIS_NODE, &no_set_overload_bit_on_startup_cmd); + install_element(ISIS_NODE, &attached_bit_send_cmd); install_element(ISIS_NODE, &attached_bit_receive_ignore_cmd); diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index 5387f37039..63b4edb1e1 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -68,6 +68,8 @@ static void lsp_l2_refresh_pseudo(struct thread *thread); static void lsp_destroy(struct isis_lsp *lsp); +static bool device_startup; + int lsp_id_cmp(uint8_t *id1, uint8_t *id2) { return memcmp(id1, id2, ISIS_SYS_ID_LEN + 2); @@ -437,6 +439,21 @@ bool isis_level2_adj_up(struct isis_area *area) return false; } +/* + * Unset the overload bit after the timer expires + */ +void set_overload_on_start_timer(struct thread *thread) +{ + struct isis_area *area = THREAD_ARG(thread); + assert(area); + + area->t_overload_on_startup_timer = NULL; + + /* Check if set-overload-bit is not currently configured */ + if (!area->overload_configured) + isis_area_overload_bit_set(area, false); +} + static void isis_reset_attach_bit(struct isis_adjacency *adj) { struct isis_area *area = adj->circuit->area; @@ -1355,6 +1372,7 @@ int lsp_generate(struct isis_area *area, int level) uint32_t seq_num = 0; uint8_t lspid[ISIS_SYS_ID_LEN + 2]; uint16_t rem_lifetime, refresh_time; + uint32_t overload_time; if ((area == NULL) || (area->is_type & level) != level) return ISIS_ERROR; @@ -1363,6 +1381,18 @@ int lsp_generate(struct isis_area *area, int level) memcpy(&lspid, area->isis->sysid, ISIS_SYS_ID_LEN); + /* Check if device should be overloaded on startup */ + if (device_startup) { + overload_time = isis_restart_read_overload_time(area); + if (overload_time > 0) { + isis_area_overload_bit_set(area, true); + thread_add_timer(master, set_overload_on_start_timer, + area, overload_time, + &area->t_overload_on_startup_timer); + } + device_startup = false; + } + /* only builds the lsp if the area shares the level */ oldlsp = lsp_search(&area->lspdb[level - 1], lspid); if (oldlsp) { @@ -2373,6 +2403,7 @@ int isis_lsp_iterate_is_reach(struct isis_lsp *lsp, uint16_t mtid, void lsp_init(void) { + device_startup = true; hook_register(isis_adj_state_change_hook, lsp_handle_adj_state_change); } diff --git a/isisd/isis_lsp.h b/isisd/isis_lsp.h index b13b2a35e6..d7762324d9 100644 --- a/isisd/isis_lsp.h +++ b/isisd/isis_lsp.h @@ -66,6 +66,7 @@ DECLARE_RBTREE_UNIQ(lspdb, struct isis_lsp, dbe, lspdb_compare); void lsp_db_init(struct lspdb_head *head); void lsp_db_fini(struct lspdb_head *head); void lsp_tick(struct thread *thread); +void set_overload_on_start_timer(struct thread *thread); int lsp_generate(struct isis_area *area, int level); #define lsp_regenerate_schedule(area, level, all_pseudo) \ diff --git a/isisd/isis_nb.c b/isisd/isis_nb.c index a2ba33d078..4f4e6dc730 100644 --- a/isisd/isis_nb.c +++ b/isisd/isis_nb.c @@ -81,11 +81,18 @@ const struct frr_yang_module_info frr_isisd_info = { }, }, { - .xpath = "/frr-isisd:isis/instance/overload", + .xpath = "/frr-isisd:isis/instance/overload/enabled", .cbs = { .cli_show = cli_show_isis_overload, - .modify = isis_instance_overload_modify, - }, + .modify = isis_instance_overload_enabled_modify, + } + }, + { + .xpath = "/frr-isisd:isis/instance/overload/on-startup", + .cbs = { + .cli_show = cli_show_isis_overload_on_startup, + .modify = isis_instance_overload_on_startup_modify, + } }, { .xpath = "/frr-isisd:isis/instance/metric-style", diff --git a/isisd/isis_nb.h b/isisd/isis_nb.h index 00ca8be3b0..a9f2eaea95 100644 --- a/isisd/isis_nb.h +++ b/isisd/isis_nb.h @@ -37,7 +37,8 @@ int isis_instance_dynamic_hostname_modify(struct nb_cb_modify_args *args); int isis_instance_attached_send_modify(struct nb_cb_modify_args *args); int isis_instance_attached_receive_modify(struct nb_cb_modify_args *args); int isis_instance_attached_modify(struct nb_cb_modify_args *args); -int isis_instance_overload_modify(struct nb_cb_modify_args *args); +int isis_instance_overload_enabled_modify(struct nb_cb_modify_args *args); +int isis_instance_overload_on_startup_modify(struct nb_cb_modify_args *args); int isis_instance_metric_style_modify(struct nb_cb_modify_args *args); int isis_instance_purge_originator_modify(struct nb_cb_modify_args *args); int isis_instance_lsp_mtu_modify(struct nb_cb_modify_args *args); @@ -442,6 +443,9 @@ void cli_show_isis_attached_receive(struct vty *vty, bool show_defaults); void cli_show_isis_overload(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); +void cli_show_isis_overload_on_startup(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults); void cli_show_isis_metric_style(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); void cli_show_isis_area_pwd(struct vty *vty, const struct lyd_node *dnode, diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c index e0decf48f2..1b7663fcfd 100644 --- a/isisd/isis_nb_config.c +++ b/isisd/isis_nb_config.c @@ -336,9 +336,9 @@ int isis_instance_attached_modify(struct nb_cb_modify_args *args) } /* - * XPath: /frr-isisd:isis/instance/overload + * XPath: /frr-isisd:isis/instance/overload/enabled */ -int isis_instance_overload_modify(struct nb_cb_modify_args *args) +int isis_instance_overload_enabled_modify(struct nb_cb_modify_args *args) { struct isis_area *area; bool overload; @@ -348,12 +348,32 @@ int isis_instance_overload_modify(struct nb_cb_modify_args *args) area = nb_running_get_entry(args->dnode, NULL, true); overload = yang_dnode_get_bool(args->dnode, NULL); + area->overload_configured = overload; + isis_area_overload_bit_set(area, overload); return NB_OK; } /* + * XPath: /frr-isisd:isis/instance/overload/on-startup + */ +int isis_instance_overload_on_startup_modify(struct nb_cb_modify_args *args) +{ + struct isis_area *area; + uint32_t overload_time; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + overload_time = yang_dnode_get_uint32(args->dnode, NULL); + area = nb_running_get_entry(args->dnode, NULL, true); + isis_area_overload_on_startup_set(area, overload_time); + + return NB_OK; +} + +/* * XPath: /frr-isisd:isis/instance/metric-style */ int isis_instance_metric_style_modify(struct nb_cb_modify_args *args) diff --git a/isisd/isisd.c b/isisd/isisd.c index 0ff31df0f8..efea1e5d5e 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -3198,9 +3198,15 @@ void isis_area_overload_bit_set(struct isis_area *area, bool overload_bit) if (new_overload_bit != area->overload_bit) { area->overload_bit = new_overload_bit; - - if (new_overload_bit) + if (new_overload_bit) { area->overload_counter++; + } else { + /* Cancel overload on startup timer if it's running */ + if (area->t_overload_on_startup_timer) { + THREAD_OFF(area->t_overload_on_startup_timer); + area->t_overload_on_startup_timer = NULL; + } + } #ifndef FABRICD hook_call(isis_hook_db_overload, area); @@ -3213,6 +3219,109 @@ void isis_area_overload_bit_set(struct isis_area *area, bool overload_bit) #endif /* ifndef FABRICD */ } +void isis_area_overload_on_startup_set(struct isis_area *area, + uint32_t startup_time) +{ + if (area->overload_on_startup_time != startup_time) { + area->overload_on_startup_time = startup_time; + isis_restart_write_overload_time(area, startup_time); + } +} + +/* + * Returns the path of the file (non-volatile memory) that contains restart + * information. + */ +char *isis_restart_filepath() +{ + static char filepath[MAXPATHLEN]; + snprintf(filepath, sizeof(filepath), ISISD_RESTART, ""); + return filepath; +} + +/* + * Record in non-volatile memory the overload on startup time. + */ +void isis_restart_write_overload_time(struct isis_area *isis_area, + uint32_t overload_time) +{ + char *filepath; + const char *area_name; + json_object *json; + json_object *json_areas; + json_object *json_area; + + filepath = isis_restart_filepath(); + area_name = isis_area->area_tag; + + json = json_object_from_file(filepath); + if (json == NULL) + json = json_object_new_object(); + + json_object_object_get_ex(json, "areas", &json_areas); + if (!json_areas) { + json_areas = json_object_new_object(); + json_object_object_add(json, "areas", json_areas); + } + + json_object_object_get_ex(json_areas, area_name, &json_area); + if (!json_area) { + json_area = json_object_new_object(); + json_object_object_add(json_areas, area_name, json_area); + } + + json_object_int_add(json_area, "overload_time", + isis_area->overload_on_startup_time); + json_object_to_file_ext(filepath, json, JSON_C_TO_STRING_PRETTY); + json_object_free(json); +} + +/* + * Fetch from non-volatile memory the overload on startup time. + */ +uint32_t isis_restart_read_overload_time(struct isis_area *isis_area) +{ + char *filepath; + const char *area_name; + json_object *json; + json_object *json_areas; + json_object *json_area; + json_object *json_overload_time; + uint32_t overload_time = 0; + + filepath = isis_restart_filepath(); + area_name = isis_area->area_tag; + + json = json_object_from_file(filepath); + if (json == NULL) + json = json_object_new_object(); + + json_object_object_get_ex(json, "areas", &json_areas); + if (!json_areas) { + json_areas = json_object_new_object(); + json_object_object_add(json, "areas", json_areas); + } + + json_object_object_get_ex(json_areas, area_name, &json_area); + if (!json_area) { + json_area = json_object_new_object(); + json_object_object_add(json_areas, area_name, json_area); + } + + json_object_object_get_ex(json_area, "overload_time", + &json_overload_time); + if (json_overload_time) { + overload_time = json_object_get_int(json_overload_time); + } + + json_object_object_del(json_areas, area_name); + + json_object_to_file_ext(filepath, json, JSON_C_TO_STRING_PRETTY); + json_object_free(json); + + return overload_time; +} + void isis_area_attached_bit_send_set(struct isis_area *area, bool attached_bit) { diff --git a/isisd/isisd.h b/isisd/isisd.h index 4951e5809b..a9c1d60439 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -142,6 +142,7 @@ struct isis_area { struct flags flags; struct thread *t_tick; /* LSP walker */ struct thread *t_lsp_refresh[ISIS_LEVELS]; + struct thread *t_overload_on_startup_timer; struct timeval last_lsp_refresh_event[ISIS_LEVELS]; struct thread *t_rlfa_rib_update; /* t_lsp_refresh is used in two ways: @@ -180,7 +181,9 @@ struct isis_area { char is_type; /* level-1 level-1-2 or level-2-only */ /* are we overloaded? */ char overload_bit; + bool overload_configured; uint32_t overload_counter; + uint32_t overload_on_startup_time; /* L1/L2 router identifier for inter-area traffic */ char attached_bit_send; char attached_bit_rcv_ignore; @@ -290,6 +293,8 @@ void isis_area_invalidate_routes(struct isis_area *area, int levels); void isis_area_verify_routes(struct isis_area *area); void isis_area_overload_bit_set(struct isis_area *area, bool overload_bit); +void isis_area_overload_on_startup_set(struct isis_area *area, + uint32_t startup_time); void isis_area_attached_bit_send_set(struct isis_area *area, bool attached_bit); void isis_area_attached_bit_receive_set(struct isis_area *area, bool attached_bit); @@ -315,7 +320,10 @@ void show_isis_database_lspdb_json(struct json_object *json, void show_isis_database_lspdb_vty(struct vty *vty, struct isis_area *area, int level, struct lspdb_head *lspdb, const char *argv, int ui_level); - +char *isis_restart_filepath(void); +void isis_restart_write_overload_time(struct isis_area *isis_area, + uint32_t overload_time); +uint32_t isis_restart_read_overload_time(struct isis_area *isis_area); /* YANG paths */ #define ISIS_INSTANCE "/frr-isisd:isis/instance" #define ISIS_SR "/frr-isisd:isis/instance/segment-routing" diff --git a/ldpd/ldp_vty_cmds.c b/ldpd/ldp_vty_cmds.c index 3795cdaf38..3d11d3137a 100644 --- a/ldpd/ldp_vty_cmds.c +++ b/ldpd/ldp_vty_cmds.c @@ -246,7 +246,7 @@ DEFPY (ldp_allow_broken_lsps, "[no] install allow-broken-lsps", NO_STR "install lsps\n" - "if no remote-label install with imp-null") + "if no remote-label install with imp-null\n") { return (ldp_vty_allow_broken_lsp(vty, no)); } diff --git a/lib/command.c b/lib/command.c index 7e171cb309..1fae32a04a 100644 --- a/lib/command.c +++ b/lib/command.c @@ -265,8 +265,7 @@ void install_node(struct cmd_node *node) node->cmdgraph = graph_new(); node->cmd_vector = vector_init(VECTOR_MIN_SIZE); // add start node - struct cmd_token *token = - cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL); + struct cmd_token *token = cmd_token_new(START_TKN, 0, NULL, NULL); graph_new_node(node->cmdgraph, token, (void (*)(void *)) & cmd_token_del); @@ -326,7 +325,7 @@ void _install_element(enum node_type ntype, const struct cmd_element *cmd) if (cnode->graph_built || !defer_cli_tree) { struct graph *graph = graph_new(); struct cmd_token *token = - cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL); + cmd_token_new(START_TKN, 0, NULL, NULL); graph_new_node(graph, token, (void (*)(void *)) & cmd_token_del); @@ -349,8 +348,7 @@ static void cmd_finalize_iter(struct hash_bucket *hb, void *arg) struct cmd_node *cnode = arg; const struct cmd_element *cmd = hb->data; struct graph *graph = graph_new(); - struct cmd_token *token = - cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL); + struct cmd_token *token = cmd_token_new(START_TKN, 0, NULL, NULL); graph_new_node(graph, token, (void (*)(void *)) & cmd_token_del); @@ -405,7 +403,7 @@ void uninstall_element(enum node_type ntype, const struct cmd_element *cmd) if (cnode->graph_built) { struct graph *graph = graph_new(); struct cmd_token *token = - cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL); + cmd_token_new(START_TKN, 0, NULL, NULL); graph_new_node(graph, token, (void (*)(void *)) & cmd_token_del); @@ -991,7 +989,7 @@ static int cmd_execute_command_real(vector vline, enum cmd_filter_type filter, * Perform pending commit (if any) before executing * non-YANG command. */ - if (matched_element->attr != CMD_ATTR_YANG) + if (!(matched_element->attr & CMD_ATTR_YANG)) (void)nb_cli_pending_commit_check(vty); } @@ -1472,8 +1470,7 @@ static void permute(struct graph_node *start, struct vty *vty) for (unsigned int i = 0; i < vector_active(start->to); i++) { struct graph_node *gn = vector_slot(start->to, i); struct cmd_token *tok = gn->data; - if (tok->attr == CMD_ATTR_HIDDEN - || tok->attr == CMD_ATTR_DEPRECATED) + if (tok->attr & CMD_ATTR_HIDDEN) continue; else if (tok->type == END_TKN || gn == start) { vty_out(vty, " "); @@ -1562,9 +1559,8 @@ int cmd_list_cmds(struct vty *vty, int do_permute) const struct cmd_element *element = NULL; for (unsigned int i = 0; i < vector_active(node->cmd_vector); i++) - if ((element = vector_slot(node->cmd_vector, i)) - && element->attr != CMD_ATTR_DEPRECATED - && element->attr != CMD_ATTR_HIDDEN) { + if ((element = vector_slot(node->cmd_vector, i)) && + !(element->attr & CMD_ATTR_HIDDEN)) { vty_out(vty, " "); print_cmd(vty, element->string); } diff --git a/lib/command.h b/lib/command.h index ca49efd262..f4168dedd7 100644 --- a/lib/command.h +++ b/lib/command.h @@ -280,17 +280,18 @@ struct cmd_node { int argc __attribute__((unused)), \ struct cmd_token *argv[] __attribute__((unused))) -#define DEFPY(funcname, cmdname, cmdstr, helpstr) \ - DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \ - funcdecl_##funcname - -#define DEFPY_NOSH(funcname, cmdname, cmdstr, helpstr) \ - DEFPY(funcname, cmdname, cmdstr, helpstr) +/* DEFPY variants */ #define DEFPY_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0) \ funcdecl_##funcname +#define DEFPY(funcname, cmdname, cmdstr, helpstr) \ + DEFPY_ATTR(funcname, cmdname, cmdstr, helpstr, 0) + +#define DEFPY_NOSH(funcname, cmdname, cmdstr, helpstr) \ + DEFPY_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_NOSH) + #define DEFPY_HIDDEN(funcname, cmdname, cmdstr, helpstr) \ DEFPY_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN) @@ -298,18 +299,19 @@ struct cmd_node { DEFPY_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_YANG) #define DEFPY_YANG_NOSH(funcname, cmdname, cmdstr, helpstr) \ - DEFPY_YANG(funcname, cmdname, cmdstr, helpstr) + DEFPY_ATTR(funcname, cmdname, cmdstr, helpstr, \ + CMD_ATTR_YANG | CMD_ATTR_NOSH) -#define DEFUN(funcname, cmdname, cmdstr, helpstr) \ - DEFUN_CMD_FUNC_DECL(funcname) \ - DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) \ - DEFUN_CMD_FUNC_TEXT(funcname) +/* DEFUN variants */ #define DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \ DEFUN_CMD_FUNC_DECL(funcname) \ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0) \ DEFUN_CMD_FUNC_TEXT(funcname) +#define DEFUN(funcname, cmdname, cmdstr, helpstr) \ + DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, 0) + #define DEFUN_HIDDEN(funcname, cmdname, cmdstr, helpstr) \ DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN) @@ -318,73 +320,55 @@ struct cmd_node { /* DEFUN_NOSH for commands that vtysh should ignore */ #define DEFUN_NOSH(funcname, cmdname, cmdstr, helpstr) \ - DEFUN(funcname, cmdname, cmdstr, helpstr) + DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_NOSH) #define DEFUN_YANG_NOSH(funcname, cmdname, cmdstr, helpstr) \ - DEFUN_YANG(funcname, cmdname, cmdstr, helpstr) + DEFUN_ATTR(funcname, cmdname, cmdstr, helpstr, \ + CMD_ATTR_YANG | CMD_ATTR_NOSH) /* DEFSH for vtysh. */ +#define DEFSH_ATTR(daemon, cmdname, cmdstr, helpstr, attr) \ + DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, attr, daemon) + #define DEFSH(daemon, cmdname, cmdstr, helpstr) \ - DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, 0, daemon) + DEFSH_ATTR(daemon, cmdname, cmdstr, helpstr, 0) #define DEFSH_HIDDEN(daemon, cmdname, cmdstr, helpstr) \ - DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, \ - daemon) - -#define DEFSH_YANG(daemon, cmdname, cmdstr, helpstr) \ - DEFUN_CMD_ELEMENT(NULL, cmdname, cmdstr, helpstr, CMD_ATTR_YANG, daemon) + DEFSH_ATTR(daemon, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN) /* DEFUN + DEFSH */ -#define DEFUNSH(daemon, funcname, cmdname, cmdstr, helpstr) \ - DEFUN_CMD_FUNC_DECL(funcname) \ - DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon) \ - DEFUN_CMD_FUNC_TEXT(funcname) - -/* DEFUN + DEFSH with attributes */ #define DEFUNSH_ATTR(daemon, funcname, cmdname, cmdstr, helpstr, attr) \ DEFUN_CMD_FUNC_DECL(funcname) \ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, daemon) \ DEFUN_CMD_FUNC_TEXT(funcname) +#define DEFUNSH(daemon, funcname, cmdname, cmdstr, helpstr) \ + DEFUNSH_ATTR(daemon, funcname, cmdname, cmdstr, helpstr, 0) + #define DEFUNSH_HIDDEN(daemon, funcname, cmdname, cmdstr, helpstr) \ DEFUNSH_ATTR(daemon, funcname, cmdname, cmdstr, helpstr, \ CMD_ATTR_HIDDEN) -#define DEFUNSH_DEPRECATED(daemon, funcname, cmdname, cmdstr, helpstr) \ - DEFUNSH_ATTR(daemon, funcname, cmdname, cmdstr, helpstr, \ - CMD_ATTR_DEPRECATED) - -#define DEFUNSH_YANG(daemon, funcname, cmdname, cmdstr, helpstr) \ - DEFUNSH_ATTR(daemon, funcname, cmdname, cmdstr, helpstr, CMD_ATTR_YANG) - /* ALIAS macro which define existing command's alias. */ -#define ALIAS(funcname, cmdname, cmdstr, helpstr) \ - DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, 0) - #define ALIAS_ATTR(funcname, cmdname, cmdstr, helpstr, attr) \ DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, attr, 0) +#define ALIAS(funcname, cmdname, cmdstr, helpstr) \ + ALIAS_ATTR(funcname, cmdname, cmdstr, helpstr, 0) + #define ALIAS_HIDDEN(funcname, cmdname, cmdstr, helpstr) \ - DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, \ - 0) + ALIAS_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN) +/* note: DEPRECATED implies HIDDEN, and other than that there is currently no + * difference. It's purely for expressing intent in the source code - a + * DEPRECATED command is supposed to go away, a HIDDEN one is likely to stay. + */ #define ALIAS_DEPRECATED(funcname, cmdname, cmdstr, helpstr) \ - DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, \ - CMD_ATTR_DEPRECATED, 0) + ALIAS_ATTR(funcname, cmdname, cmdstr, helpstr, \ + CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN) #define ALIAS_YANG(funcname, cmdname, cmdstr, helpstr) \ - DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_YANG, 0) - -#define ALIAS_SH(daemon, funcname, cmdname, cmdstr, helpstr) \ - DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, 0, daemon) - -#define ALIAS_SH_HIDDEN(daemon, funcname, cmdname, cmdstr, helpstr) \ - DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_HIDDEN, \ - daemon) - -#define ALIAS_SH_DEPRECATED(daemon, funcname, cmdname, cmdstr, helpstr) \ - DEFUN_CMD_ELEMENT(funcname, cmdname, cmdstr, helpstr, \ - CMD_ATTR_DEPRECATED, daemon) + ALIAS_ATTR(funcname, cmdname, cmdstr, helpstr, CMD_ATTR_YANG) #endif /* VTYSH_EXTRACT_PL */ @@ -417,6 +401,7 @@ struct cmd_node { #define BGP_SOFT_IN_STR "Send route-refresh unless using 'soft-reconfiguration inbound'\n" #define BGP_SOFT_OUT_STR "Resend all outbound updates\n" #define BGP_SOFT_RSCLIENT_RIB_STR "Soft reconfig for rsclient RIB\n" +#define BGP_ORR_DEBUG "Enable Optimal Route Reflection Debugging logs\n" #define OSPF_STR "OSPF information\n" #define NEIGHBOR_STR "Specify neighbor router\n" #define DEBUG_STR "Debugging functions\n" diff --git a/lib/command_graph.c b/lib/command_graph.c index 09d802e796..e940685250 100644 --- a/lib/command_graph.c +++ b/lib/command_graph.c @@ -494,9 +494,10 @@ void cmd_graph_node_print_cb(struct graph_node *gn, struct buffer *buf) snprintf(nbuf, sizeof(nbuf), "<b>%s</b>", lookup_msg(tokennames, tok->type, NULL)); buffer_putstr(buf, nbuf); - if (tok->attr == CMD_ATTR_DEPRECATED) + if (tok->attr & CMD_ATTR_DEPRECATED) buffer_putstr(buf, " (d)"); - else if (tok->attr == CMD_ATTR_HIDDEN) + /* DEPRECATED implies HIDDEN, don't print both */ + else if (tok->attr & CMD_ATTR_HIDDEN) buffer_putstr(buf, " (h)"); if (tok->text) { if (tok->type == WORD_TKN) diff --git a/lib/command_graph.h b/lib/command_graph.h index ed4da6aa4c..b8c7a9c72c 100644 --- a/lib/command_graph.h +++ b/lib/command_graph.h @@ -73,10 +73,11 @@ enum cmd_token_type { #define IS_VARYING_TOKEN(x) ((x) >= VARIABLE_TKN && (x) < FORK_TKN) /* Command attributes */ -enum { CMD_ATTR_NORMAL, - CMD_ATTR_DEPRECATED, - CMD_ATTR_HIDDEN, - CMD_ATTR_YANG, +enum { + CMD_ATTR_YANG = (1 << 0), + CMD_ATTR_HIDDEN = (1 << 1), + CMD_ATTR_DEPRECATED = (1 << 2), + CMD_ATTR_NOSH = (1 << 3), }; enum varname_src { diff --git a/lib/command_match.c b/lib/command_match.c index f221e0a02c..ce2dbc9528 100644 --- a/lib/command_match.c +++ b/lib/command_match.c @@ -395,8 +395,7 @@ enum matcher_rv command_complete(struct graph *graph, vector vline, for (ALL_LIST_ELEMENTS_RO(current, node, gstack)) { struct cmd_token *token = gstack[0]->data; - if (token->attr == CMD_ATTR_HIDDEN - || token->attr == CMD_ATTR_DEPRECATED) + if (token->attr & CMD_ATTR_HIDDEN) continue; enum match_type minmatch = min_match_level(token->type); diff --git a/lib/command_py.c b/lib/command_py.c index 6301eec5e8..cce9542e30 100644 --- a/lib/command_py.c +++ b/lib/command_py.c @@ -226,8 +226,8 @@ static PyObject *graph_to_pyobj(struct wrap_graph *wgraph, wrap->type = "???"; } - wrap->deprecated = (tok->attr == CMD_ATTR_DEPRECATED); - wrap->hidden = (tok->attr == CMD_ATTR_HIDDEN); + wrap->deprecated = !!(tok->attr & CMD_ATTR_DEPRECATED); + wrap->hidden = !!(tok->attr & CMD_ATTR_HIDDEN); wrap->text = tok->text; wrap->desc = tok->desc; wrap->varname = tok->varname; @@ -353,6 +353,12 @@ PyMODINIT_FUNC command_py_init(void) if (!pymod) initret(NULL); + if (PyModule_AddIntMacro(pymod, CMD_ATTR_YANG) + || PyModule_AddIntMacro(pymod, CMD_ATTR_HIDDEN) + || PyModule_AddIntMacro(pymod, CMD_ATTR_DEPRECATED) + || PyModule_AddIntMacro(pymod, CMD_ATTR_NOSH)) + initret(NULL); + Py_INCREF(&typeobj_graph_node); PyModule_AddObject(pymod, "GraphNode", (PyObject *)&typeobj_graph_node); Py_INCREF(&typeobj_graph); diff --git a/lib/frrscript.c b/lib/frrscript.c index a19bd0c3db..2e56932613 100644 --- a/lib/frrscript.c +++ b/lib/frrscript.c @@ -184,13 +184,14 @@ static void *codec_alloc(void *arg) return e; } -#if 0 -static void codec_free(struct codec *c) +static void codec_free(void *data) { - XFREE(MTYPE_TMP, c->typename); - XFREE(MTYPE_TMP, c); + struct frrscript_codec *c = data; + char *constworkaroundandihateit = (char *)c->typename; + + XFREE(MTYPE_SCRIPT, constworkaroundandihateit); + XFREE(MTYPE_SCRIPT, c); } -#endif /* Lua function hash utils */ @@ -212,17 +213,18 @@ bool lua_function_hash_cmp(const void *d1, const void *d2) void *lua_function_alloc(void *arg) { struct lua_function_state *tmp = arg; - struct lua_function_state *lfs = XCALLOC(MTYPE_SCRIPT, sizeof(struct lua_function_state)); + lfs->name = tmp->name; lfs->L = tmp->L; return lfs; } -static void lua_function_free(struct hash_bucket *b, void *data) +static void lua_function_free(void *data) { - struct lua_function_state *lfs = (struct lua_function_state *)b->data; + struct lua_function_state *lfs = data; + lua_close(lfs->L); XFREE(MTYPE_SCRIPT, lfs); } @@ -409,7 +411,8 @@ fail: void frrscript_delete(struct frrscript *fs) { - hash_iterate(fs->lua_function_hash, lua_function_free, NULL); + hash_clean(fs->lua_function_hash, lua_function_free); + hash_free(fs->lua_function_hash); XFREE(MTYPE_SCRIPT, fs->name); XFREE(MTYPE_SCRIPT, fs); } @@ -425,4 +428,11 @@ void frrscript_init(const char *sd) frrscript_register_type_codecs(frrscript_codecs_lib); } +void frrscript_fini(void) +{ + hash_clean(codec_hash, codec_free); + hash_free(codec_hash); + + frrscript_names_destroy(); +} #endif /* HAVE_SCRIPTING */ diff --git a/lib/frrscript.h b/lib/frrscript.h index 4db3e6f1b2..7fa01f70d1 100644 --- a/lib/frrscript.h +++ b/lib/frrscript.h @@ -162,6 +162,11 @@ void frrscript_register_type_codecs(struct frrscript_codec *codecs); void frrscript_init(const char *scriptdir); /* + * On shutdown clean up memory associated with the scripting subsystem + */ +void frrscript_fini(void); + +/* * This macro is mapped to every (name, value) in frrscript_call, * so this in turn maps them onto their encoders */ diff --git a/lib/grammar_sandbox.c b/lib/grammar_sandbox.c index f9778c5d4c..8fa47c053b 100644 --- a/lib/grammar_sandbox.c +++ b/lib/grammar_sandbox.c @@ -76,8 +76,7 @@ DEFUN (grammar_test, // parse the command and install it into the command graph struct graph *graph = graph_new(); - struct cmd_token *token = - cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL); + struct cmd_token *token = cmd_token_new(START_TKN, 0, NULL, NULL); graph_new_node(graph, token, (void (*)(void *)) & cmd_token_del); cmd_graph_parse(graph, cmd); diff --git a/lib/libfrr.c b/lib/libfrr.c index f5aecd9f75..aee6981854 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -1219,6 +1219,10 @@ void frr_fini(void) db_close(); #endif log_ref_fini(); + +#ifdef HAVE_SCRIPTING + frrscript_fini(); +#endif frr_pthread_finish(); zprivs_terminate(di->privs); /* signal_init -> nothing needed */ diff --git a/lib/log_vty.c b/lib/log_vty.c index 81280f302f..c9268734c4 100644 --- a/lib/log_vty.c +++ b/lib/log_vty.c @@ -761,8 +761,8 @@ DEFPY (log_immediate_mode, log_immediate_mode_cmd, "[no] log immediate-mode", NO_STR - "Logging control" - "Output immediately, without buffering") + "Logging control\n" + "Output immediately, without buffering\n") { zlog_set_immediate(!no); return CMD_SUCCESS; diff --git a/lib/orr_msg.h b/lib/orr_msg.h new file mode 100644 index 0000000000..b0c4c48df8 --- /dev/null +++ b/lib/orr_msg.h @@ -0,0 +1,94 @@ +/* + * Structures common to BGP, OSPF and ISIS for BGP Optimal Route Reflection + * Copyright (C) 2021 Samsung R&D Institute India - Bangalore. + * Madhurilatha Kuruganti + * + * 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 + */ + +#ifndef _FRR_ORR_MSG_H +#define _FRR_ORR_MSG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* REVISIT: Need to check if we can use zero length array */ +#define ORR_MAX_PREFIX 100 +#define ORR_GROUP_NAME_SIZE 32 + +struct orr_prefix_metric { + struct prefix prefix; + uint32_t metric; +}; + +/* BGP-IGP Register for IGP metric */ +struct orr_igp_metric_reg { + bool reg; + uint8_t proto; + safi_t safi; + struct prefix prefix; + char group_name[ORR_GROUP_NAME_SIZE]; +}; + +/* IGP-BGP message structures */ +struct orr_igp_metric_info { + /* IGP instance data. */ + uint8_t proto; + uint32_t instId; + + safi_t safi; + + /* Add or delete routes */ + bool add; + + /* IGP metric from Active Root. */ + struct prefix root; + uint32_t num_entries; + struct orr_prefix_metric nexthop[ORR_MAX_PREFIX]; +}; + +/* BGP ORR Root node */ +struct orr_root { + afi_t afi; + safi_t safi; + + char group_name[ORR_GROUP_NAME_SIZE]; + + /* MPLS_TE prefix and router ID */ + struct prefix prefix; + struct in_addr router_id; + + /* Advertising OSPF Router ID. */ + struct in_addr adv_router; + + /* BGP-ORR Received LSAs */ + struct ospf_lsa *router_lsa_rcvd; + + /* Routing tables from root node */ + struct route_table *old_table; /* Old routing table. */ + struct route_table *new_table; /* Current routing table. */ + + struct route_table *old_rtrs; /* Old ABR/ASBR RT. */ + struct route_table *new_rtrs; /* New ABR/ASBR RT. */ +}; + +/* Prototypes. */ + +#ifdef __cplusplus +} +#endif + +#endif /* _FRR_ORR_MSG_H */ diff --git a/lib/subdir.am b/lib/subdir.am index d6defd7149..e04e700eb6 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -245,6 +245,7 @@ pkginclude_HEADERS += \ lib/ns.h \ lib/openbsd-queue.h \ lib/openbsd-tree.h \ + lib/orr_msg.h \ lib/plist.h \ lib/prefix.h \ lib/printfrr.h \ diff --git a/lib/zclient.h b/lib/zclient.h index c3ea2a16ff..fb5da9aad2 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -100,6 +100,8 @@ enum zserv_client_capabilities { extern struct sockaddr_storage zclient_addr; extern socklen_t zclient_addr_len; +#define ZAPI_ORR_FLAG_UNICAST 0x01 + /* Zebra message types. */ typedef enum { ZEBRA_INTERFACE_ADD, @@ -1229,6 +1231,10 @@ enum zapi_opaque_registry { LDP_RLFA_UNREGISTER_ALL = 8, /* Announce LDP labels associated to a previously registered RLFA */ LDP_RLFA_LABELS = 9, + /* Register for IGP METRIC with OSPF/ISIS */ + ORR_IGP_METRIC_REGISTER = 10, + /* Send SPF data to BGP */ + ORR_IGP_METRIC_UPDATE = 11 }; /* Send the hello message. diff --git a/ospfd/ospf_dump.c b/ospfd/ospf_dump.c index 9f6adf3226..59f95c5da2 100644 --- a/ospfd/ospf_dump.c +++ b/ospfd/ospf_dump.c @@ -63,6 +63,7 @@ unsigned long conf_debug_ospf_ldp_sync; unsigned long conf_debug_ospf_gr; unsigned long conf_debug_ospf_bfd; unsigned long conf_debug_ospf_client_api; +unsigned long conf_debug_ospf_orr; /* Enable debug option variables -- valid only session. */ unsigned long term_debug_ospf_packet[5] = {0, 0, 0, 0, 0}; @@ -81,6 +82,7 @@ unsigned long term_debug_ospf_ldp_sync; unsigned long term_debug_ospf_gr; unsigned long term_debug_ospf_bfd; unsigned long term_debug_ospf_client_api; +unsigned long term_debug_ospf_orr; const char *ospf_redist_string(unsigned int route_type) { @@ -1601,6 +1603,33 @@ DEFPY (debug_ospf_client_api, return CMD_SUCCESS; } +DEFPY (debug_ospf_orr, + debug_ospf_orr_cmd, + "[no$no] debug ospf [(1-65535)$instance] orr", + NO_STR + DEBUG_STR + OSPF_STR + "Instance ID\n" + "OSPF ORR information\n") +{ + if (instance && instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + if (vty->node == CONFIG_NODE) { + if (no) + DEBUG_OFF(orr, ORR); + else + DEBUG_ON(orr, ORR); + } else { + if (no) + TERM_DEBUG_OFF(orr, ORR); + else + TERM_DEBUG_ON(orr, ORR); + } + + return CMD_SUCCESS; +} + DEFUN (no_debug_ospf, no_debug_ospf_cmd, "no debug ospf", @@ -1643,6 +1672,8 @@ DEFUN (no_debug_ospf, for (i = 0; i < 5; i++) DEBUG_PACKET_OFF(i, flag); + + DEBUG_OFF(orr, ORR); } for (i = 0; i < 5; i++) @@ -1673,6 +1704,7 @@ DEFUN (no_debug_ospf, TERM_DEBUG_OFF(ti_lfa, TI_LFA); TERM_DEBUG_OFF(bfd, BFD_LIB); TERM_DEBUG_OFF(client_api, CLIENT_API); + TERM_DEBUG_OFF(orr, ORR); return CMD_SUCCESS; } @@ -1802,6 +1834,12 @@ static int show_debugging_ospf_common(struct vty *vty) if (IS_DEBUG_OSPF(client_api, CLIENT_API) == OSPF_DEBUG_CLIENT_API) vty_out(vty, " OSPF client-api debugging is on\n"); + /* Show debug status for ORR. */ + if (IS_DEBUG_OSPF(orr, ORR) == OSPF_DEBUG_ORR) + vty_out(vty, " OSPF ORR debugging is on\n"); + + vty_out(vty, "\n"); + return CMD_SUCCESS; } @@ -2014,6 +2052,12 @@ static int config_write_debug(struct vty *vty) write = 1; } + /* debug ospf orr */ + if (IS_CONF_DEBUG_OSPF(orr, ORR) == OSPF_DEBUG_ORR) { + vty_out(vty, "debug ospf%s orr\n", str); + write = 1; + } + return write; } @@ -2035,6 +2079,7 @@ void ospf_debug_init(void) install_element(ENABLE_NODE, &debug_ospf_default_info_cmd); install_element(ENABLE_NODE, &debug_ospf_ldp_sync_cmd); install_element(ENABLE_NODE, &debug_ospf_client_api_cmd); + install_element(ENABLE_NODE, &debug_ospf_orr_cmd); install_element(ENABLE_NODE, &no_debug_ospf_ism_cmd); install_element(ENABLE_NODE, &no_debug_ospf_nsm_cmd); install_element(ENABLE_NODE, &no_debug_ospf_lsa_cmd); @@ -2074,6 +2119,7 @@ void ospf_debug_init(void) install_element(CONFIG_NODE, &debug_ospf_default_info_cmd); install_element(CONFIG_NODE, &debug_ospf_ldp_sync_cmd); install_element(CONFIG_NODE, &debug_ospf_client_api_cmd); + install_element(CONFIG_NODE, &debug_ospf_orr_cmd); install_element(CONFIG_NODE, &no_debug_ospf_nsm_cmd); install_element(CONFIG_NODE, &no_debug_ospf_lsa_cmd); install_element(CONFIG_NODE, &no_debug_ospf_zebra_cmd); diff --git a/ospfd/ospf_dump.h b/ospfd/ospf_dump.h index 251be7c8d1..e9ba8fc798 100644 --- a/ospfd/ospf_dump.h +++ b/ospfd/ospf_dump.h @@ -70,6 +70,8 @@ #define OSPF_DEBUG_CLIENT_API 0x01 +#define OSPF_DEBUG_ORR 0x01 + /* Macro for setting debug option. */ #define CONF_DEBUG_PACKET_ON(a, b) conf_debug_ospf_packet[a] |= (b) #define CONF_DEBUG_PACKET_OFF(a, b) conf_debug_ospf_packet[a] &= ~(b) @@ -129,6 +131,8 @@ #define AREA_NAME(A) ospf_area_name_string ((A)) #define IF_NAME(I) ospf_if_name_string ((I)) +#define IS_DEBUG_OSPF_ORR IS_DEBUG_OSPF(orr, ORR) + /* Extern debug flag. */ extern unsigned long term_debug_ospf_packet[]; extern unsigned long term_debug_ospf_event; @@ -146,6 +150,7 @@ extern unsigned long term_debug_ospf_ldp_sync; extern unsigned long term_debug_ospf_gr; extern unsigned long term_debug_ospf_bfd; extern unsigned long term_debug_ospf_client_api; +extern unsigned long term_debug_ospf_orr; /* Message Strings. */ extern char *ospf_lsa_type_str[]; diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c index 646d318362..a0b14e73ee 100644 --- a/ospfd/ospf_interface.c +++ b/ospfd/ospf_interface.c @@ -461,13 +461,13 @@ struct ospf_interface *ospf_if_lookup_recv_if(struct ospf *ospf, { struct route_node *rn; struct prefix_ipv4 addr; - struct ospf_interface *oi, *match; + struct ospf_interface *oi, *match, *unnumbered_match; addr.family = AF_INET; addr.prefix = src; addr.prefixlen = IPV4_MAX_BITLEN; - match = NULL; + match = unnumbered_match = NULL; for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) { oi = rn->info; @@ -482,7 +482,7 @@ struct ospf_interface *ospf_if_lookup_recv_if(struct ospf *ospf, continue; if (CHECK_FLAG(oi->connected->flags, ZEBRA_IFA_UNNUMBERED)) - match = oi; + unnumbered_match = oi; else if (prefix_match(CONNECTED_PREFIX(oi->connected), (struct prefix *)&addr)) { if ((match == NULL) || (match->address->prefixlen @@ -491,7 +491,10 @@ struct ospf_interface *ospf_if_lookup_recv_if(struct ospf *ospf, } } - return match; + if (match) + return match; + + return unnumbered_match; } void ospf_interface_fifo_flush(struct ospf_interface *oi) diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index 0df0072f6d..c67181cba6 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -52,6 +52,8 @@ #include "ospfd/ospf_zebra.h" #include "ospfd/ospf_abr.h" #include "ospfd/ospf_errors.h" +#include "ospfd/ospf_te.h" +#include "ospfd/ospf_orr.h" static struct ospf_lsa *ospf_handle_summarylsa_lsId_chg(struct ospf *ospf, struct prefix_ipv4 *p, @@ -2640,6 +2642,13 @@ ospf_router_lsa_install(struct ospf *ospf, struct ospf_lsa *new, int rt_recalc) ospf_refresher_register_lsa(ospf, new); } + /* For BGP ORR SPF should be calculated from specified root(s) */ + else if (ospf->orr_spf_request) { + ospf_lsa_unlock(&area->router_lsa_rcvd); + area->router_lsa_rcvd = ospf_lsa_lock(new); + ospf_orr_root_update_rcvd_lsa(area->router_lsa_rcvd); + } + if (rt_recalc) ospf_spf_calculate_schedule(ospf, SPF_FLAG_ROUTER_LSA_INSTALL); return new; @@ -2651,7 +2660,6 @@ static struct ospf_lsa *ospf_network_lsa_install(struct ospf *ospf, struct ospf_lsa *new, int rt_recalc) { - /* RFC 2328 Section 13.2 Router-LSAs and network-LSAs The entire routing table must be recalculated, starting with the shortest path calculations for each area (not just the @@ -3400,6 +3408,82 @@ struct ospf_lsa *ospf_lsa_lookup_by_id(struct ospf_area *area, uint32_t type, return NULL; } +struct ospf_lsa *ospf_lsa_lookup_by_adv_rid(struct ospf_area *area, + uint32_t type, struct in_addr id) +{ + struct ospf_lsa *lsa = NULL; + struct route_node *rn = NULL; + + switch (type) { + case OSPF_ROUTER_LSA: + for (rn = route_top(ROUTER_LSDB(area)); rn; + rn = route_next(rn)) { + lsa = rn->info; + if (lsa) { + if (IPV4_ADDR_SAME(&lsa->data->adv_router, + &id)) { + route_unlock_node(rn); + return lsa; + } + } + } + break; + case OSPF_NETWORK_LSA: + case OSPF_SUMMARY_LSA: + case OSPF_ASBR_SUMMARY_LSA: + case OSPF_AS_EXTERNAL_LSA: + case OSPF_AS_NSSA_LSA: + case OSPF_OPAQUE_LINK_LSA: + case OSPF_OPAQUE_AREA_LSA: + case OSPF_OPAQUE_AS_LSA: + /* Currently not used. */ + break; + default: + break; + } + + return NULL; +} + +struct ospf_lsa *ospf_lsa_lookup_by_mpls_te_rid(struct ospf_area *area, + uint32_t type, + struct in_addr id) +{ + struct ospf_lsa *lsa = NULL; + struct route_node *rn = NULL; + struct lsa_header *lsah = NULL; + uint32_t lsid; + uint8_t opaque_type; + struct tlv_header *tlvh = NULL; + struct te_tlv_router_addr *router_addr = NULL; + + if (type != OSPF_OPAQUE_AREA_LSA) + return NULL; + + for (rn = route_top(OPAQUE_AREA_LSDB(area)); rn; rn = route_next(rn)) { + lsa = rn->info; + if (lsa) { + lsah = lsa->data; + lsid = ntohl(lsah->id.s_addr); + opaque_type = GET_OPAQUE_TYPE(lsid); + if (opaque_type != OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA) + continue; + + tlvh = TLV_HDR_TOP(lsah); + if (!tlvh || + (ntohs(tlvh->type) != TE_TLV_ROUTER_ADDR) || + (ntohs(tlvh->length) != TE_LINK_SUBTLV_DEF_SIZE)) + continue; + router_addr = (struct te_tlv_router_addr *)tlvh; + if (IPV4_ADDR_SAME(&router_addr->value, &id)) { + route_unlock_node(rn); + return lsa; + } + } + } + return NULL; +} + struct ospf_lsa *ospf_lsa_lookup_by_header(struct ospf_area *area, struct lsa_header *lsah) { @@ -3823,8 +3907,9 @@ struct ospf_lsa *ospf_lsa_refresh(struct ospf *ospf, struct ospf_lsa *lsa) struct as_external_lsa *al; struct prefix_ipv4 p; - assert(CHECK_FLAG(lsa->flags, OSPF_LSA_SELF)); - assert(IS_LSA_SELF(lsa)); + if (!CHECK_FLAG(lsa->flags, OSPF_LSA_SELF) && !IS_LSA_SELF(lsa) && + !IS_LSA_ORR(lsa)) + return NULL; assert(lsa->lock > 0); switch (lsa->data->type) { @@ -3894,7 +3979,8 @@ void ospf_refresher_register_lsa(struct ospf *ospf, struct ospf_lsa *lsa) uint16_t index, current_index; assert(lsa->lock > 0); - assert(IS_LSA_SELF(lsa)); + if (!IS_LSA_SELF(lsa) && !IS_LSA_ORR(lsa)) + return; if (lsa->refresh_list < 0) { int delay; @@ -3943,7 +4029,8 @@ void ospf_refresher_register_lsa(struct ospf *ospf, struct ospf_lsa *lsa) void ospf_refresher_unregister_lsa(struct ospf *ospf, struct ospf_lsa *lsa) { assert(lsa->lock > 0); - assert(IS_LSA_SELF(lsa)); + if (!IS_LSA_SELF(lsa) || !IS_LSA_ORR(lsa)) + return; if (lsa->refresh_list >= 0) { struct list *refresh_list = ospf->lsa_refresh_queue.qs[lsa->refresh_list]; diff --git a/ospfd/ospf_lsa.h b/ospfd/ospf_lsa.h index 97c15d1e3c..a2a2393c90 100644 --- a/ospfd/ospf_lsa.h +++ b/ospfd/ospf_lsa.h @@ -74,15 +74,16 @@ struct vertex; /* OSPF LSA. */ struct ospf_lsa { /* LSA origination flag. */ - uint8_t flags; -#define OSPF_LSA_SELF 0x01 -#define OSPF_LSA_SELF_CHECKED 0x02 -#define OSPF_LSA_RECEIVED 0x04 -#define OSPF_LSA_APPROVED 0x08 -#define OSPF_LSA_DISCARD 0x10 -#define OSPF_LSA_LOCAL_XLT 0x20 -#define OSPF_LSA_PREMATURE_AGE 0x40 -#define OSPF_LSA_IN_MAXAGE 0x80 + uint16_t flags; +#define OSPF_LSA_SELF 0x0001 +#define OSPF_LSA_SELF_CHECKED 0x0002 +#define OSPF_LSA_RECEIVED 0x0004 +#define OSPF_LSA_APPROVED 0x0008 +#define OSPF_LSA_DISCARD 0x0010 +#define OSPF_LSA_LOCAL_XLT 0x0020 +#define OSPF_LSA_PREMATURE_AGE 0x0040 +#define OSPF_LSA_IN_MAXAGE 0x0080 +#define OSPF_LSA_ORR 0x0100 /* LSA data. and size */ struct lsa_header *data; @@ -222,6 +223,7 @@ enum lsid_status { LSID_AVAILABLE = 0, LSID_CHANGE, LSID_NOT_AVAILABLE }; #define IS_LSA_MAXAGE(L) (LS_AGE ((L)) == OSPF_LSA_MAXAGE) #define IS_LSA_MAX_SEQ(L) \ ((L)->data->ls_seqnum == htonl(OSPF_MAX_SEQUENCE_NUMBER)) +#define IS_LSA_ORR(L) (CHECK_FLAG ((L)->flags, OSPF_LSA_ORR)) #define OSPF_LSA_UPDATE_DELAY 2 @@ -292,6 +294,12 @@ extern struct ospf_lsa *ospf_lsa_lookup(struct ospf *ospf, struct ospf_area *, struct in_addr); extern struct ospf_lsa *ospf_lsa_lookup_by_id(struct ospf_area *, uint32_t, struct in_addr); +extern struct ospf_lsa *ospf_lsa_lookup_by_adv_rid(struct ospf_area *area, + uint32_t type, + struct in_addr id); +extern struct ospf_lsa *ospf_lsa_lookup_by_mpls_te_rid(struct ospf_area *area, + uint32_t type, + struct in_addr id); extern struct ospf_lsa *ospf_lsa_lookup_by_header(struct ospf_area *, struct lsa_header *); extern int ospf_lsa_more_recent(struct ospf_lsa *, struct ospf_lsa *); diff --git a/ospfd/ospf_lsdb.c b/ospfd/ospf_lsdb.c index f4fb858a5f..3c65ac388d 100644 --- a/ospfd/ospf_lsdb.c +++ b/ospfd/ospf_lsdb.c @@ -30,6 +30,7 @@ #include "ospfd/ospf_asbr.h" #include "ospfd/ospf_lsa.h" #include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_orr.h" struct ospf_lsdb *ospf_lsdb_new(void) { @@ -87,6 +88,10 @@ static void ospf_lsdb_delete_entry(struct ospf_lsdb *lsdb, assert(rn->table == lsdb->type[lsa->data->type].db); + /* Update ORR Root table MPLS-TE Router address's advertise router */ + if (lsa->data->type == OSPF_OPAQUE_AREA_LSA) + ospf_orr_root_table_update(lsa, false); + if (IS_LSA_SELF(lsa)) lsdb->type[lsa->data->type].count_self--; lsdb->type[lsa->data->type].count--; @@ -134,6 +139,10 @@ void ospf_lsdb_add(struct ospf_lsdb *lsdb, struct ospf_lsa *lsa) #endif /* MONITOR_LSDB_CHANGE */ lsdb->type[lsa->data->type].checksum += ntohs(lsa->data->checksum); rn->info = ospf_lsa_lock(lsa); /* lsdb */ + + /* Update ORR Root table MPLS-TE Router address's advertise router */ + if (lsa->data->type == OSPF_OPAQUE_AREA_LSA) + ospf_orr_root_table_update(lsa, true); } void ospf_lsdb_delete(struct ospf_lsdb *lsdb, struct ospf_lsa *lsa) diff --git a/ospfd/ospf_memory.c b/ospfd/ospf_memory.c index 2838443892..5577a291be 100644 --- a/ospfd/ospf_memory.c +++ b/ospfd/ospf_memory.c @@ -60,3 +60,4 @@ DEFINE_MTYPE(OSPFD, OSPF_GR_HELPER, "OSPF Graceful Restart Helper"); DEFINE_MTYPE(OSPFD, OSPF_EXTERNAL_RT_AGGR, "OSPF External Route Summarisation"); DEFINE_MTYPE(OSPFD, OSPF_P_SPACE, "OSPF TI-LFA P-Space"); DEFINE_MTYPE(OSPFD, OSPF_Q_SPACE, "OSPF TI-LFA Q-Space"); +DEFINE_MTYPE(OSPFD, OSPF_ORR_ROOT, "OSPF ORR Root"); diff --git a/ospfd/ospf_memory.h b/ospfd/ospf_memory.h index 9bd0a844af..3d2133b11a 100644 --- a/ospfd/ospf_memory.h +++ b/ospfd/ospf_memory.h @@ -59,5 +59,6 @@ DECLARE_MTYPE(OSPF_GR_HELPER); DECLARE_MTYPE(OSPF_EXTERNAL_RT_AGGR); DECLARE_MTYPE(OSPF_P_SPACE); DECLARE_MTYPE(OSPF_Q_SPACE); +DECLARE_MTYPE(OSPF_ORR_ROOT); #endif /* _QUAGGA_OSPF_MEMORY_H */ diff --git a/ospfd/ospf_orr.c b/ospfd/ospf_orr.c new file mode 100644 index 0000000000..eed948b190 --- /dev/null +++ b/ospfd/ospf_orr.c @@ -0,0 +1,594 @@ +/* + * OSPF BGP-IGP IGP metric update handling routines + * Copyright (C) 2021 Samsung R&D Institute India - Bangalore. + * Madhurilatha Kuruganti + * + * 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 <string.h> + +#include "monotime.h" +#include "memory.h" +#include "thread.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" +#include "command.h" +#include "plist.h" +#include "log.h" +#include "zclient.h" +#include <lib/json.h> +#include "defaults.h" +#include "orr_msg.h" + +#include "ospfd.h" +#include "ospf_asbr.h" +#include "ospf_dump.h" +#include "ospf_lsa.h" +#include "ospf_orr.h" +#include "ospf_route.h" +#include "ospf_spf.h" +#include "ospf_te.h" + +static void ospf_show_orr_root(struct orr_root *root); +static void ospf_show_orr(struct ospf *ospf, afi_t afi, safi_t safi); +static struct orr_root *ospf_orr_root_new(struct ospf *ospf, afi_t afi, + safi_t safi, struct prefix *p, + char *group_name) +{ + struct list *orr_root_list = NULL; + struct orr_root *root = NULL; + + if (!ospf->orr_root[afi][safi]) + ospf->orr_root[afi][safi] = list_new(); + + orr_root_list = ospf->orr_root[afi][safi]; + root = XCALLOC(MTYPE_OSPF_ORR_ROOT, sizeof(struct orr_root)); + + listnode_add(orr_root_list, root); + + root->afi = afi; + root->safi = safi; + prefix_copy(&root->prefix, p); + IPV4_ADDR_COPY(&root->router_id, &p->u.prefix4); + strlcpy(root->group_name, group_name, sizeof(root->group_name)); + root->new_rtrs = NULL; + root->new_table = NULL; + + ospf_orr_debug( + "%s: For %s %s, ORR Group %s, created ORR Root entry %pFX.", + __func__, afi2str(afi), safi2str(safi), root->group_name, p); + + return root; +} + +static struct orr_root *ospf_orr_root_lookup(struct ospf *ospf, afi_t afi, + safi_t safi, struct in_addr *rid) +{ + struct list *orr_root_list = NULL; + struct orr_root *root = NULL; + struct listnode *node; + + orr_root_list = ospf->orr_root[afi][safi]; + if (!orr_root_list) + return NULL; + + for (ALL_LIST_ELEMENTS_RO(orr_root_list, node, root)) + if (IPV4_ADDR_SAME(&root->router_id, rid)) + return root; + + ospf_orr_debug("%s: For %s %s, ORR Root '%pI4' not found.", __func__, + afi2str(afi), safi2str(safi), rid); + + return NULL; +} + +static struct orr_root *ospf_orr_root_lookup_by_adv_rid(struct ospf *ospf, + afi_t afi, safi_t safi, + struct in_addr *rid) +{ + struct list *orr_root_list = NULL; + struct orr_root *root = NULL; + struct listnode *node; + + orr_root_list = ospf->orr_root[afi][safi]; + if (!orr_root_list) + return NULL; + + for (ALL_LIST_ELEMENTS_RO(orr_root_list, node, root)) + if (IPV4_ADDR_SAME(&root->adv_router, rid)) + return root; + + return NULL; +} + +/* + * Lookup each area's LSDB if is there is any opaque area LSA received and + * update the root database with the advertising router. + */ +static struct ospf_lsa * +ospf_orr_lookup_opaque_area_lsa_by_id(struct in_addr rid) +{ + struct ospf_lsa *lsa = NULL; + struct ospf_area *area = NULL; + struct ospf *ospf = NULL; + struct listnode *node = NULL, *nnode = NULL; + + /* if ospf is not enabled ignore */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (!ospf) + return NULL; + + /* Lookup for Opaque area LSA in each area. */ + for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) { + lsa = ospf_lsa_lookup_by_mpls_te_rid(area, OSPF_OPAQUE_AREA_LSA, + rid); + if (!lsa) + continue; + ospf_orr_debug( + "%s: Opaque Area LSA found in area %pI4 for %pI4", + __func__, &area->area_id, &rid); + return lsa; + } + return NULL; +} + +/* + * Lookup each area's LSDB if is there is any opaque area LSA received and + * update the root database with the advertising router. + */ +static struct ospf_lsa *ospf_orr_lookup_router_lsa_by_id(struct in_addr rid) +{ + struct ospf_lsa *lsa = NULL; + struct ospf_area *area = NULL; + struct ospf *ospf = NULL; + struct listnode *node = NULL, *nnode = NULL; + + /* if ospf is not enabled ignore */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (!ospf) + return NULL; + + /* Lookup for Router LSA in each area. */ + for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) { + lsa = ospf_lsa_lookup_by_adv_rid(area, OSPF_ROUTER_LSA, rid); + if (!lsa) + continue; + ospf_orr_debug("%s: Router LSA found in area %pI4 for %pI4", + __func__, &area->area_id, &rid); + return lsa; + } + return NULL; +} + +/* + * BGP-IGP IGP metric msg between BGP and IGP + */ +int ospf_orr_igp_metric_register(struct orr_igp_metric_reg msg) +{ + afi_t afi; + safi_t safi; + struct ospf *ospf = NULL; + struct ospf_lsa *lsa = NULL; + struct orr_root *root = NULL; + + /* if ospf is not enabled ignore */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (!ospf) + return -1; + + if (msg.proto != ZEBRA_ROUTE_BGP) + return -1; + + afi = family2afi(msg.prefix.family); + safi = msg.safi; + + ospf_orr_debug( + "%s: Received IGP metric %s message from BGP for ORR Group %s from location %pFX", + __func__, msg.reg ? "Register" : "Unregister", msg.group_name, + &msg.prefix); + + /* Get ORR Root entry for the given address-family */ + root = ospf_orr_root_lookup(ospf, afi, safi, &msg.prefix.u.prefix4); + + /* Should not hit this condition */ + if ((root && msg.reg) || (!root && !msg.reg)) + return -1; + + /* Create ORR Root entry and calculate SPF from root */ + if (!root) { + root = ospf_orr_root_new(ospf, afi, safi, &msg.prefix, + msg.group_name); + if (!root) { + ospf_orr_debug( + "%s: For %s %s, Failed to create ORR Root entry %pFX.", + __func__, afi2str(afi), safi2str(safi), + &msg.prefix); + return -1; + } + ospf->orr_spf_request++; + + lsa = ospf_orr_lookup_opaque_area_lsa_by_id(root->router_id); + if (!lsa || !lsa->data) + return -1; + + IPV4_ADDR_COPY(&root->adv_router, &lsa->data->adv_router); + + /* Lookup LSDB for Router LSA */ + if (!root->router_lsa_rcvd) { + lsa = ospf_orr_lookup_router_lsa_by_id( + root->adv_router); + if (!lsa || !lsa->data) + return -1; + root->router_lsa_rcvd = lsa; + } + + /* Compute SPF for all root nodes */ + ospf_orr_spf_calculate_schedule(ospf); + } + /* Delete ORR Root entry. SPF calculation not required. */ + else { + listnode_delete(ospf->orr_root[afi][safi], root); + XFREE(MTYPE_OSPF_ORR_ROOT, root); + + /* If last node is deleted in the list */ + if (!ospf->orr_root[afi][safi]->count) + list_delete(&ospf->orr_root[afi][safi]); + + ospf->orr_spf_request--; + } + + if (IS_DEBUG_OSPF_ORR) + ospf_show_orr(ospf, afi, safi); + + return 0; +} + +void ospf_orr_igp_metric_send_update_add(struct orr_root *root, + unsigned short instance) +{ + int ret; + uint8_t count = 0; + struct route_node *rn; + struct ospf_route *or; + struct orr_igp_metric_info msg; + + memset(&msg, 0, sizeof(msg)); + msg.proto = ZEBRA_ROUTE_OSPF; + msg.safi = root->safi; + msg.instId = instance; + msg.add = true; + prefix_copy(&msg.root, &root->prefix); + + /* Update prefix table from ORR Route table */ + for (rn = route_top(root->new_table); rn; rn = route_next(rn)) { + or = rn->info; + if (!or) + continue; + + if (or->type != OSPF_DESTINATION_NETWORK && + or->type != OSPF_DESTINATION_DISCARD) + continue; + + if (ospf_route_match_same(root->old_table, + (struct prefix_ipv4 *)&rn->p, or)) + continue; + + if (count < ORR_MAX_PREFIX) { + prefix_copy(&msg.nexthop[count].prefix, + (struct prefix_ipv4 *)&rn->p); + msg.nexthop[count].metric = or->cost; + count++; + } else { + msg.num_entries = count; + ret = zclient_send_opaque(zclient, + ORR_IGP_METRIC_UPDATE, + (uint8_t *)&msg, sizeof(msg)); + if (ret != ZCLIENT_SEND_SUCCESS) + ospf_orr_debug( + "%s: Failed to send message to BGP.", + __func__); + count = 0; + prefix_copy(&msg.nexthop[count].prefix, + (struct prefix_ipv4 *)&rn->p); + msg.nexthop[count].metric = or->cost; + count++; + } + } + if (count > 0 && count <= ORR_MAX_PREFIX) { + msg.num_entries = count; + ret = zclient_send_opaque(zclient, ORR_IGP_METRIC_UPDATE, + (uint8_t *)&msg, sizeof(msg)); + if (ret != ZCLIENT_SEND_SUCCESS) + ospf_orr_debug("%s: Failed to send message to BGP.", + __func__); + } +} + +void ospf_orr_igp_metric_send_update_delete(struct orr_root *root, + unsigned short instance) +{ + int ret; + uint8_t count = 0; + struct route_node *rn; + struct ospf_route *or; + struct orr_igp_metric_info msg; + + if (!root->old_table) + return; + + memset(&msg, 0, sizeof(msg)); + msg.proto = ZEBRA_ROUTE_OSPF; + msg.instId = instance; + msg.safi = root->safi; + msg.add = false; + prefix_copy(&msg.root, &root->prefix); + + /* Update prefix table from ORR Route table */ + for (rn = route_top(root->old_table); rn; rn = route_next(rn)) { + or = rn->info; + if (!or) + continue; + + if (or->path_type != OSPF_PATH_INTRA_AREA && + or->path_type != OSPF_PATH_INTER_AREA) + continue; + + if (or->type != OSPF_DESTINATION_NETWORK && + or->type != OSPF_DESTINATION_DISCARD) + continue; + + if (ospf_route_exist_new_table(root->new_table, + (struct prefix_ipv4 *)&rn->p)) + continue; + + if (count < ORR_MAX_PREFIX) { + prefix_copy(&msg.nexthop[count].prefix, + (struct prefix_ipv4 *)&rn->p); + msg.nexthop[count].metric = or->cost; + count++; + } else { + msg.num_entries = count; + ret = zclient_send_opaque(zclient, + ORR_IGP_METRIC_UPDATE, + (uint8_t *)&msg, sizeof(msg)); + if (ret != ZCLIENT_SEND_SUCCESS) + ospf_orr_debug( + "%s: Failed to send message to BGP.", + __func__); + count = 0; + prefix_copy(&msg.nexthop[count].prefix, + (struct prefix_ipv4 *)&rn->p); + msg.nexthop[count].metric = or->cost; + count++; + } + } + if (count > 0 && count <= ORR_MAX_PREFIX) { + msg.num_entries = count; + ret = zclient_send_opaque(zclient, ORR_IGP_METRIC_UPDATE, + (uint8_t *)&msg, sizeof(msg)); + if (ret != ZCLIENT_SEND_SUCCESS) + ospf_orr_debug("%s: Failed to send message to BGP.", + __func__); + } +} + +static void ospf_show_orr_root(struct orr_root *root) +{ + if (!root) + return; + + ospf_orr_debug("%s: Address Family: %s %s", __func__, + afi2str(root->afi), safi2str(root->safi)); + ospf_orr_debug("%s: ORR Group: %s", __func__, root->group_name); + ospf_orr_debug("%s: Router-Address: %pI4:", __func__, &root->router_id); + ospf_orr_debug("%s: Advertising Router: %pI4:", __func__, + &root->adv_router); +} + +static void ospf_show_orr(struct ospf *ospf, afi_t afi, safi_t safi) +{ + struct listnode *node = NULL; + struct orr_root *orr_root = NULL; + struct list *orr_root_list = NULL; + + FOREACH_AFI_SAFI (afi, safi) { + orr_root_list = ospf->orr_root[afi][safi]; + if (!orr_root_list) + return; + + for (ALL_LIST_ELEMENTS_RO(orr_root_list, node, orr_root)) + ospf_show_orr_root(orr_root); + } +} + +void ospf_orr_root_table_update(struct ospf_lsa *lsa, bool add) +{ + afi_t afi; + safi_t safi; + struct lsa_header *lsah = lsa->data; + uint32_t lsid = ntohl(lsah->id.s_addr); + uint8_t opaque_type = GET_OPAQUE_TYPE(lsid); + uint32_t opaque_id = GET_OPAQUE_ID(lsid); + struct tlv_header *tlvh = TLV_HDR_TOP(lsah); + struct te_tlv_router_addr *router_addr = NULL; + struct orr_root *root = NULL; + struct ospf *ospf = NULL; + + /* if ospf is not enabled ignore */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (!ospf) + return; + + if (opaque_type != OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA) + return; + + if (!tlvh || (ntohs(tlvh->type) != TE_TLV_ROUTER_ADDR) || + (ntohs(tlvh->length) != TE_LINK_SUBTLV_DEF_SIZE)) + return; + + router_addr = (struct te_tlv_router_addr *)tlvh; + if (IS_DEBUG_OSPF_ORR) { + zlog_debug("[OSPF-ORR] %s: Opaque-area LSA %s LSDB", __func__, + add ? "added to" : "deleted from"); + zlog_debug("[OSPF-ORR] %s: Opaque-Type %u (%s)", __func__, + opaque_type, "Traffic Engineering LSA"); + zlog_debug("[OSPF-ORR] %s: Opaque-ID 0x%x", __func__, + opaque_id); + zlog_debug("[OSPF-ORR] %s: Opaque-Info: %u octets of data%s", + __func__, ntohs(lsah->length) - OSPF_LSA_HEADER_SIZE, + VALID_OPAQUE_INFO_LEN(lsah) ? "" + : "(Invalid length?)"); + zlog_debug("[OSPF-ORR] %s: Router-Address: %pI4", __func__, + &router_addr->value); + zlog_debug("[OSPF-ORR] %s: Advertising Router: %pI4", __func__, + &lsa->data->adv_router); + } + /* + * When Opaque LSA is added or removed from LSDB check if there is any + * change in MPLS-TE Router address and Advertising router address and + * update the table accordingly if there is no change in the mapping + * ignore update + * + * Get ORR Root entry for the given address-family + */ + FOREACH_AFI_SAFI (afi, safi) { + root = ospf_orr_root_lookup(ospf, afi, safi, + &router_addr->value); + if (root) { + IPV4_ADDR_COPY(&root->adv_router, + &lsa->data->adv_router); + if (IS_DEBUG_OSPF_ORR) + ospf_show_orr(ospf, afi, safi); + break; + } + } +} + +void ospf_orr_root_update_rcvd_lsa(struct ospf_lsa *lsa) +{ + afi_t afi; + safi_t safi; + struct orr_root *root = NULL; + + if (!lsa || !lsa->area || !lsa->area->ospf) + return; + + FOREACH_AFI_SAFI (afi, safi) { + root = ospf_orr_root_lookup_by_adv_rid( + lsa->area->ospf, afi, safi, &lsa->data->adv_router); + if (root) { + SET_FLAG(lsa->flags, OSPF_LSA_ORR); + ospf_refresher_register_lsa(lsa->area->ospf, lsa); + root->router_lsa_rcvd = lsa; + } + + ospf_orr_debug("%s: Received LSA[Type%d:%pI4]", __func__, + lsa->data->type, &lsa->data->adv_router); + + /* Compute SPF for all root nodes */ + ospf_orr_spf_calculate_schedule(lsa->area->ospf); + return; + } +} + +/* Do not Install routes to root table. Just update table ponters */ +void ospf_orr_route_install(struct orr_root *root, struct route_table *rt, + unsigned short instance) +{ + /* + * rt contains new routing table, new_table contains an old one. + * updating pointers + */ + if (root->old_table) + ospf_route_table_free(root->old_table); + + root->old_table = root->new_table; + root->new_table = rt; + + /* Send update to BGP to delete old routes. */ + ospf_orr_igp_metric_send_update_delete(root, instance); + + /* REVISIT: Skipping external route table for now */ + + /* Send update to BGP to add new routes. */ + ospf_orr_igp_metric_send_update_add(root, instance); +} + +void ospf_orr_spf_calculate_schedule(struct ospf *ospf) +{ + /* OSPF instance does not exist. */ + if (ospf == NULL) + return; + + /* No roots nodes rgistered for rSPF */ + if (!ospf->orr_spf_request) + return; + + /* ORR SPF calculation timer is already scheduled. */ + if (ospf->t_orr_calc) { + ospf_orr_debug( + "SPF: calculation timer is already scheduled: %p", + (void *)ospf->t_orr_calc); + return; + } + + ospf->t_orr_calc = NULL; + + ospf_orr_debug("%s: SPF: calculation timer scheduled", __func__); + + thread_add_timer(master, ospf_orr_spf_calculate_schedule_worker, ospf, + OSPF_ORR_CALC_INTERVAL, &ospf->t_orr_calc); +} + +void ospf_orr_spf_calculate_area(struct ospf *ospf, struct ospf_area *area, + struct route_table *new_table, + struct route_table *all_rtrs, + struct route_table *new_rtrs, + struct ospf_lsa *lsa_rcvd) +{ + ospf_spf_calculate(area, lsa_rcvd, new_table, all_rtrs, new_rtrs, false, + true); +} + +void ospf_orr_spf_calculate_areas(struct ospf *ospf, + struct route_table *new_table, + struct route_table *all_rtrs, + struct route_table *new_rtrs, + struct ospf_lsa *lsa_rcvd) +{ + struct ospf_area *area; + struct listnode *node, *nnode; + + /* Calculate SPF for each area. */ + for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) { + /* + * Do backbone last, so as to first discover intra-area paths + * for any back-bone virtual-links + */ + if (ospf->backbone && ospf->backbone == area) + continue; + + ospf_orr_spf_calculate_area(ospf, area, new_table, all_rtrs, + new_rtrs, lsa_rcvd); + } + + /* SPF for backbone, if required */ + if (ospf->backbone) + ospf_orr_spf_calculate_area(ospf, ospf->backbone, new_table, + all_rtrs, new_rtrs, lsa_rcvd); +} diff --git a/ospfd/ospf_orr.h b/ospfd/ospf_orr.h new file mode 100644 index 0000000000..d0a6f6e790 --- /dev/null +++ b/ospfd/ospf_orr.h @@ -0,0 +1,58 @@ +/* + * OSPF BGP-IGP IGP metric update handling routines + * Copyright (C) 2021 Samsung R&D Institute India - Bangalore. + * Madhurilatha Kuruganti + * + * 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 + */ + +#ifndef _ZEBRA_OSPF_ORR_H +#define _ZEBRA_OSPF_ORR_H + +#define BGP_OSPF_LSINFINITY 65535 +#define OSPF_ORR_CALC_INTERVAL 1 + +/* Macro to log debug message */ +#define ospf_orr_debug(...) \ + do { \ + if (IS_DEBUG_OSPF_ORR) \ + zlog_debug("[OSPF-ORR] "__VA_ARGS__); \ + } while (0) + +extern struct zclient *zclient; + +extern int ospf_orr_igp_metric_register(struct orr_igp_metric_reg orr_reg); +extern void ospf_orr_igp_metric_send_update_add(struct orr_root *root, + unsigned short instance); +extern void ospf_orr_igp_metric_send_update_delete(struct orr_root *root, + unsigned short instance); +extern void ospf_orr_root_table_update(struct ospf_lsa *lsa, bool add); +extern void ospf_orr_root_update_rcvd_lsa(struct ospf_lsa *lsa); +extern void ospf_orr_route_install(struct orr_root *root, + struct route_table *rt, + unsigned short instance); +extern void ospf_orr_spf_calculate_schedule(struct ospf *ospf); +extern void ospf_orr_spf_calculate_area(struct ospf *ospf, + struct ospf_area *area, + struct route_table *new_table, + struct route_table *all_rtrs, + struct route_table *new_rtrs, + struct ospf_lsa *lsa_rcvd); +extern void ospf_orr_spf_calculate_areas(struct ospf *ospf, + struct route_table *new_table, + struct route_table *all_rtrs, + struct route_table *new_rtrs, + struct ospf_lsa *lsa_rcvd); +#endif /* _ZEBRA_OSPF_ORR_H */ diff --git a/ospfd/ospf_route.c b/ospfd/ospf_route.c index 6360d8ec60..26f593f089 100644 --- a/ospfd/ospf_route.c +++ b/ospfd/ospf_route.c @@ -151,8 +151,8 @@ void ospf_route_table_free(struct route_table *rt) otherwise return 0. Since the ZEBRA-RIB does an implicit withdraw, it is not necessary to send a delete, an add later will act like an implicit delete. */ -static int ospf_route_exist_new_table(struct route_table *rt, - struct prefix_ipv4 *prefix) +int ospf_route_exist_new_table(struct route_table *rt, + struct prefix_ipv4 *prefix) { struct route_node *rn; diff --git a/ospfd/ospf_route.h b/ospfd/ospf_route.h index fa9478fced..e7e2b651c5 100644 --- a/ospfd/ospf_route.h +++ b/ospfd/ospf_route.h @@ -172,5 +172,6 @@ extern void ospf_delete_discard_route(struct ospf *, struct route_table *, struct prefix_ipv4 *); extern int ospf_route_match_same(struct route_table *, struct prefix_ipv4 *, struct ospf_route *); - +extern int ospf_route_exist_new_table(struct route_table *rt, + struct prefix_ipv4 *prefix); #endif /* _ZEBRA_OSPF_ROUTE_H */ diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c index 4edc1de811..74213d7de2 100644 --- a/ospfd/ospf_spf.c +++ b/ospfd/ospf_spf.c @@ -53,6 +53,8 @@ #include "ospfd/ospf_apiserver.h" #endif +#include "ospfd/ospf_orr.h" + /* Variables to ensure a SPF scheduled log message is printed only once */ static unsigned int spf_reason_flags = 0; @@ -1824,6 +1826,36 @@ void ospf_spf_calculate_areas(struct ospf *ospf, struct route_table *new_table, all_rtrs, new_rtrs); } +/* Print Reason for SPF calculation */ +static void ospf_spf_calculation_reason2str(char *rbuf) +{ + rbuf[0] = '\0'; + if (spf_reason_flags) { + if (spf_reason_flags & (1 << SPF_FLAG_ROUTER_LSA_INSTALL)) + strlcat(rbuf, "R, ", sizeof(rbuf)); + if (spf_reason_flags & (1 << SPF_FLAG_NETWORK_LSA_INSTALL)) + strlcat(rbuf, "N, ", sizeof(rbuf)); + if (spf_reason_flags & (1 << SPF_FLAG_SUMMARY_LSA_INSTALL)) + strlcat(rbuf, "S, ", sizeof(rbuf)); + if (spf_reason_flags & (1 << SPF_FLAG_ASBR_SUMMARY_LSA_INSTALL)) + strlcat(rbuf, "AS, ", sizeof(rbuf)); + if (spf_reason_flags & (1 << SPF_FLAG_ABR_STATUS_CHANGE)) + strlcat(rbuf, "ABR, ", sizeof(rbuf)); + if (spf_reason_flags & (1 << SPF_FLAG_ASBR_STATUS_CHANGE)) + strlcat(rbuf, "ASBR, ", sizeof(rbuf)); + if (spf_reason_flags & (1 << SPF_FLAG_MAXAGE)) + strlcat(rbuf, "M, ", sizeof(rbuf)); + if (spf_reason_flags & (1 << SPF_FLAG_ORR_ROOT_CHANGE)) + strlcat(rbuf, "ORR, ", sizeof(rbuf)); + + size_t rbuflen = strlen(rbuf); + if (rbuflen >= 2) + rbuf[rbuflen - 2] = '\0'; /* skip the last ", " */ + else + rbuf[0] = '\0'; + } +} + /* Worker for SPF calculation scheduler. */ static void ospf_spf_calculate_schedule_worker(struct thread *thread) { @@ -1879,6 +1911,8 @@ static void ospf_spf_calculate_schedule_worker(struct thread *thread) ospf_ase_calculate_schedule(ospf); ospf_ase_calculate_timer_add(ospf); + ospf_orr_spf_calculate_schedule(ospf); + if (IS_DEBUG_OSPF_EVENT) zlog_debug( "%s: ospf install new route, vrf %s id %u new_table count %lu", @@ -1901,7 +1935,6 @@ static void ospf_spf_calculate_schedule_worker(struct thread *thread) #ifdef SUPPORT_OSPF_API ospf_apiserver_notify_reachable(ospf->oall_rtrs, ospf->all_rtrs); #endif - /* Free old ABR/ASBR routing table */ if (ospf->old_rtrs) /* ospf_route_delete (ospf->old_rtrs); */ @@ -1926,31 +1959,7 @@ static void ospf_spf_calculate_schedule_worker(struct thread *thread) total_spf_time = monotime_since(&spf_start_time, &ospf->ts_spf_duration); - rbuf[0] = '\0'; - if (spf_reason_flags) { - if (spf_reason_flags & (1 << SPF_FLAG_ROUTER_LSA_INSTALL)) - strlcat(rbuf, "R, ", sizeof(rbuf)); - if (spf_reason_flags & (1 << SPF_FLAG_NETWORK_LSA_INSTALL)) - strlcat(rbuf, "N, ", sizeof(rbuf)); - if (spf_reason_flags & (1 << SPF_FLAG_SUMMARY_LSA_INSTALL)) - strlcat(rbuf, "S, ", sizeof(rbuf)); - if (spf_reason_flags & (1 << SPF_FLAG_ASBR_SUMMARY_LSA_INSTALL)) - strlcat(rbuf, "AS, ", sizeof(rbuf)); - if (spf_reason_flags & (1 << SPF_FLAG_ABR_STATUS_CHANGE)) - strlcat(rbuf, "ABR, ", sizeof(rbuf)); - if (spf_reason_flags & (1 << SPF_FLAG_ASBR_STATUS_CHANGE)) - strlcat(rbuf, "ASBR, ", sizeof(rbuf)); - if (spf_reason_flags & (1 << SPF_FLAG_MAXAGE)) - strlcat(rbuf, "M, ", sizeof(rbuf)); - if (spf_reason_flags & (1 << SPF_FLAG_GR_FINISH)) - strlcat(rbuf, "GR, ", sizeof(rbuf)); - - size_t rbuflen = strlen(rbuf); - if (rbuflen >= 2) - rbuf[rbuflen - 2] = '\0'; /* skip the last ", " */ - else - rbuf[0] = '\0'; - } + ospf_spf_calculation_reason2str(rbuf); if (IS_DEBUG_OSPF_EVENT) { zlog_info("SPF Processing Time(usecs): %ld", total_spf_time); @@ -1967,6 +1976,145 @@ static void ospf_spf_calculate_schedule_worker(struct thread *thread) ospf_clear_spf_reason_flags(); } +/* Worker for ORR SPF calculation scheduler. */ +void ospf_orr_spf_calculate_schedule_worker(struct thread *thread) +{ + afi_t afi; + safi_t safi; + struct ospf *ospf = THREAD_ARG(thread); + struct route_table *new_table, *new_rtrs; + struct route_table *all_rtrs = NULL; + struct timeval start_time, spf_start_time; + unsigned long ia_time, rt_time; + unsigned long abr_time, total_spf_time, spf_time; + struct listnode *rnode; + struct list *orr_root_list; + struct orr_root *root; + char rbuf[32]; /* reason_buf */ + + ospf_orr_debug("%s: SPF: Timer (SPF calculation expire)", __func__); + + ospf->t_orr_calc = NULL; + + /* Execute SPF for each ORR Root node */ + FOREACH_AFI_SAFI (afi, safi) { + orr_root_list = ospf->orr_root[afi][safi]; + if (!orr_root_list) + continue; + for (ALL_LIST_ELEMENTS_RO(orr_root_list, rnode, root)) { + if (!root || !root->router_lsa_rcvd) + continue; + ospf_orr_debug( + "%s: For %s %s, MPLS TE Router address %pI4 advertised by %pI4", + __func__, afi2str(afi), safi2str(safi), + &root->router_id, &root->adv_router); + + ospf_vl_unapprove(ospf); + + /* + * Execute SPF for each area including backbone, see RFC + * 2328 16.1. + */ + monotime(&spf_start_time); + new_table = route_table_init(); /* routing table */ + new_rtrs = + route_table_init(); /* ABR/ASBR routing table */ + + /* + * If we have opaque enabled then track all router + * reachability + */ + if (CHECK_FLAG(ospf->opaque, + OPAQUE_OPERATION_READY_BIT)) + all_rtrs = route_table_init(); + ospf_orr_spf_calculate_areas(ospf, new_table, all_rtrs, + new_rtrs, + root->router_lsa_rcvd); + + spf_time = monotime_since(&spf_start_time, NULL); + + ospf_vl_shut_unapproved(ospf); + + /* Calculate inter-area routes, see RFC 2328 16.2. */ + monotime(&start_time); + ospf_ia_routing(ospf, new_table, new_rtrs); + ia_time = monotime_since(&start_time, NULL); + + /* + * REVISIT : + * Pruning of unreachable networks, routers skipped. + */ + + /* Note: RFC 2328 16.3. is apparently missing. */ + /* Calculate AS external routes, see RFC 2328 16.4. + * There is a dedicated routing table for external + * routes which is not handled here directly + */ + ospf_ase_calculate_schedule(ospf); + ospf_ase_calculate_timer_add(ospf); + + ospf_orr_debug( + "%s: ospf install new route, vrf %s id %u new_table count %lu", + __func__, ospf_vrf_id_to_name(ospf->vrf_id), + ospf->vrf_id, new_table->count); + + /* Update routing table. */ + monotime(&start_time); + ospf_orr_route_install(root, new_table, ospf->instance); + rt_time = monotime_since(&start_time, NULL); + + /* + * REVISIT : + * Freeing up and Updating old all routers routing table + * skipped. + */ + + /* Free old ABR/ASBR routing table */ + if (root->old_rtrs) + /* ospf_route_delete (ospf->old_rtrs); */ + ospf_rtrs_free(root->old_rtrs); + + /* Update ABR/ASBR routing table */ + root->old_rtrs = root->new_rtrs; + root->new_rtrs = new_rtrs; + + /* + * ABRs may require additional changes, see RFC + * 2328 16.7. + */ + monotime(&start_time); + if (IS_OSPF_ABR(ospf)) { + if (ospf->anyNSSA) + ospf_abr_nssa_check_status(ospf); + ospf_abr_task(ospf); + } + abr_time = monotime_since(&start_time, NULL); + + /* Schedule Segment Routing update */ + ospf_sr_update_task(ospf); + + total_spf_time = monotime_since(&spf_start_time, + &ospf->ts_spf_duration); + + ospf_spf_calculation_reason2str(rbuf); + + if (IS_DEBUG_OSPF_ORR) { + zlog_info("SPF Processing Time(usecs): %ld", + total_spf_time); + zlog_info(" SPF Time: %ld", + spf_time); + zlog_info(" InterArea: %ld", ia_time); + zlog_info(" RouteInstall: %ld", rt_time); + if (IS_OSPF_ABR(ospf)) + zlog_info( + " ABR: %ld (%d areas)", + abr_time, ospf->areas->count); + zlog_info("Reason(s) for SPF: %s", rbuf); + } + } /* ALL_LIST_ELEMENTS_RO() */ + } /* FOREACH_AFI_SAFI() */ +} + /* * Add schedule for SPF calculation. To avoid frequenst SPF calc, we set timer * for SPF calc. @@ -2025,6 +2173,7 @@ void ospf_spf_calculate_schedule(struct ospf *ospf, ospf_spf_reason_t reason) zlog_debug("SPF: calculation timer delay = %ld msec", delay); ospf->t_spf_calc = NULL; + thread_add_timer_msec(master, ospf_spf_calculate_schedule_worker, ospf, delay, &ospf->t_spf_calc); } diff --git a/ospfd/ospf_spf.h b/ospfd/ospf_spf.h index 834bfd0bb0..2578051c2c 100644 --- a/ospfd/ospf_spf.h +++ b/ospfd/ospf_spf.h @@ -70,8 +70,10 @@ typedef enum { SPF_FLAG_ASBR_STATUS_CHANGE, SPF_FLAG_CONFIG_CHANGE, SPF_FLAG_GR_FINISH, + SPF_FLAG_ORR_ROOT_CHANGE, } ospf_spf_reason_t; +extern unsigned int ospf_get_spf_reason_flags(void); extern void ospf_spf_calculate_schedule(struct ospf *, ospf_spf_reason_t); extern void ospf_spf_calculate(struct ospf_area *area, struct ospf_lsa *root_lsa, @@ -103,5 +105,6 @@ extern int vertex_parent_cmp(void *aa, void *bb); extern void ospf_spf_print(struct vty *vty, struct vertex *v, int i); extern void ospf_restart_spf(struct ospf *ospf); +extern void ospf_orr_spf_calculate_schedule_worker(struct thread *thread); /* void ospf_spf_calculate_timer_add (); */ #endif /* _QUAGGA_OSPF_SPF_H */ diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index c957c8c014..4f0fa6194a 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -55,6 +55,7 @@ #include "ospfd/ospf_dump.h" #include "ospfd/ospf_bfd.h" #include "ospfd/ospf_ldp_sync.h" +#include "ospfd/ospf_orr.h" FRR_CFG_DEFAULT_BOOL(OSPF_LOG_ADJACENCY_CHANGES, @@ -10982,6 +10983,131 @@ static void show_ip_ospf_route_external(struct vty *vty, struct ospf *ospf, vty_out(vty, "\n"); } +static void show_ip_ospf_route_orr_root(struct vty *vty, struct ospf *ospf, + struct orr_root *root, bool use_vrf) +{ + if (ospf->instance) + vty_out(vty, "\nOSPF Instance: %d\n", ospf->instance); + + ospf_show_vrf_name(ospf, vty, NULL, use_vrf); + + vty_out(vty, "ORR Group: %s\n", root->group_name); + vty_out(vty, "Active Root: %pI4\n\n", &root->router_id); + vty_out(vty, "SPF calculated from %pI4\n\n", &root->router_id); + + if (root->new_table) + show_ip_ospf_route_network(vty, ospf, root->new_table, NULL); + + if (root->new_rtrs) + show_ip_ospf_route_router(vty, ospf, root->new_rtrs, NULL); + + vty_out(vty, "\n"); +} + +static void show_ip_ospf_route_orr_common(struct vty *vty, struct ospf *ospf, + const char *orr_group, bool use_vrf) +{ + afi_t afi; + safi_t safi; + struct orr_root *root = NULL; + struct listnode *node = NULL; + struct list *orr_root_list = NULL; + + if (!ospf->orr_spf_request) + return; + + FOREACH_AFI_SAFI (afi, safi) { + orr_root_list = ospf->orr_root[afi][safi]; + if (!orr_root_list) + continue; + for (ALL_LIST_ELEMENTS_RO(orr_root_list, node, root)) { + if (orr_group) { + if (!strmatch(root->group_name, orr_group)) + continue; + show_ip_ospf_route_orr_root(vty, ospf, root, + use_vrf); + } else + show_ip_ospf_route_orr_root(vty, ospf, root, + use_vrf); + } + } +} + +DEFPY (show_ip_ospf_instance_route_orr, + show_ip_ospf_instance_route_orr_cmd, + "show ip ospf (1-65535)$instance route orr [WORD$orr_group]", + SHOW_STR + IP_STR + OSPF_STR + "Instance ID\n" + "OSPF routing table\n" + "Optimal Route Reflection\n" + "ORR Group name\n") +{ + struct ospf *ospf; + + if (instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + ospf = ospf_lookup_instance(instance); + if (!ospf || !ospf->oi_running) + return CMD_SUCCESS; + + show_ip_ospf_route_orr_common(vty, ospf, orr_group, false); + + return CMD_SUCCESS; +} + +DEFPY (show_ip_ospf_route_orr, + show_ip_ospf_route_orr_cmd, + "show ip ospf [vrf <NAME$vrf_name|all$all_vrf>] route orr [WORD$orr_group]", + SHOW_STR + IP_STR + OSPF_STR + VRF_CMD_HELP_STR + "All VRFs\n" + "OSPF routing table\n" + "Optimal Route Reflection\n" + "ORR Group name\n") +{ + struct ospf *ospf = NULL; + struct listnode *node = NULL; + int ret = CMD_SUCCESS; + int inst = 0; + bool use_vrf = vrf_name || all_vrf; + + if (all_vrf) { + bool ospf_output = false; + + for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { + if (!ospf->oi_running) + continue; + ospf_output = true; + + show_ip_ospf_route_orr_common(vty, ospf, orr_group, + use_vrf); + } + if (!ospf_output) + vty_out(vty, "%% OSPF is not enabled\n"); + return ret; + } + + if (vrf_name) + ospf = ospf_lookup_by_inst_name(inst, vrf_name); + else + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + + if (!ospf || !ospf->oi_running) { + vty_out(vty, "%% OSPF is not enabled in vrf %s\n", + vrf_name ? vrf_name : "default"); + return CMD_SUCCESS; + } + + show_ip_ospf_route_orr_common(vty, ospf, orr_group, use_vrf); + + return ret; +} + static int show_ip_ospf_reachable_routers_common(struct vty *vty, struct ospf *ospf, uint8_t use_vrf) @@ -12694,11 +12820,13 @@ void ospf_vty_show_init(void) install_element(VIEW_NODE, &show_ip_ospf_route_cmd); install_element(VIEW_NODE, &show_ip_ospf_border_routers_cmd); install_element(VIEW_NODE, &show_ip_ospf_reachable_routers_cmd); + install_element(VIEW_NODE, &show_ip_ospf_route_orr_cmd); install_element(VIEW_NODE, &show_ip_ospf_instance_route_cmd); install_element(VIEW_NODE, &show_ip_ospf_instance_border_routers_cmd); install_element(VIEW_NODE, &show_ip_ospf_instance_reachable_routers_cmd); + install_element(VIEW_NODE, &show_ip_ospf_instance_route_orr_cmd); /* "show ip ospf vrfs" commands. */ install_element(VIEW_NODE, &show_ip_ospf_vrfs_cmd); diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index 1754512b5b..4615864244 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -53,6 +53,7 @@ #include "ospfd/ospf_te.h" #include "ospfd/ospf_sr.h" #include "ospfd/ospf_ldp_sync.h" +#include "ospfd/ospf_orr.h" DEFINE_MTYPE_STATIC(OSPFD, OSPF_EXTERNAL, "OSPF External route table"); DEFINE_MTYPE_STATIC(OSPFD, OSPF_REDISTRIBUTE, "OSPF Redistriute"); @@ -2081,6 +2082,7 @@ static void ospf_zebra_connected(struct zclient *zclient) bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT); zclient_send_reg_requests(zclient, VRF_DEFAULT); + zclient_register_opaque(zclient, ORR_IGP_METRIC_REGISTER); } /* @@ -2093,6 +2095,7 @@ static int ospf_opaque_msg_handler(ZAPI_CALLBACK_ARGS) struct ldp_igp_sync_if_state state; struct ldp_igp_sync_announce announce; struct zapi_opaque_reg_info dst; + struct orr_igp_metric_reg orr_reg; int ret = 0; s = zclient->ibuf; @@ -2116,6 +2119,10 @@ static int ospf_opaque_msg_handler(ZAPI_CALLBACK_ARGS) STREAM_GET(&announce, s, sizeof(announce)); ret = ospf_ldp_sync_announce_update(announce); break; + case ORR_IGP_METRIC_REGISTER: + STREAM_GET(&orr_reg, s, sizeof(orr_reg)); + ret = ospf_orr_igp_metric_register(orr_reg); + break; default: break; } diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index e0c36d86fe..3f82d86921 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -795,6 +795,7 @@ static void ospf_finish_final(struct ospf *ospf) THREAD_OFF(ospf->t_write); THREAD_OFF(ospf->t_spf_calc); THREAD_OFF(ospf->t_ase_calc); + THREAD_OFF(ospf->t_orr_calc); THREAD_OFF(ospf->t_maxage); THREAD_OFF(ospf->t_maxage_walker); THREAD_OFF(ospf->t_abr_task); diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h index 3a43010f85..c7735136bc 100644 --- a/ospfd/ospfd.h +++ b/ospfd/ospfd.h @@ -35,6 +35,8 @@ #include "ospf_memory.h" #include "ospf_dump_api.h" +#include "orr_msg.h" + #define OSPF_VERSION 2 /* VTY port number. */ @@ -261,6 +263,7 @@ struct ospf { struct thread *t_distribute_update; /* Distirbute list update timer. */ struct thread *t_spf_calc; /* SPF calculation timer. */ struct thread *t_ase_calc; /* ASE calculation timer. */ + struct thread *t_orr_calc; /* ORR calculation timer. */ struct thread *t_opaque_lsa_self; /* Type-11 Opaque-LSAs origin event. */ struct thread *t_sr_update; /* Segment Routing update timer */ @@ -406,6 +409,10 @@ struct ospf { bool ti_lfa_enabled; enum protection_type ti_lfa_protection_type; + /* BGP ORR Root node list */ + uint32_t orr_spf_request; + struct list *orr_root[AFI_MAX][SAFI_MAX]; + QOBJ_FIELDS; }; DECLARE_QOBJ_TYPE(ospf); @@ -591,6 +598,9 @@ struct ospf_area { uint32_t act_ints; /* Active interfaces. */ uint32_t full_nbrs; /* Fully adjacent neighbors. */ uint32_t full_vls; /* Fully adjacent virtual neighbors. */ + + /* BGP-ORR Received LSAs */ + struct ospf_lsa *router_lsa_rcvd; }; /* OSPF config network structure. */ diff --git a/ospfd/subdir.am b/ospfd/subdir.am index 4f9cbc7b1e..78688fac95 100644 --- a/ospfd/subdir.am +++ b/ospfd/subdir.am @@ -48,6 +48,7 @@ ospfd_libfrrospf_a_SOURCES = \ ospfd/ospf_network.c \ ospfd/ospf_nsm.c \ ospfd/ospf_opaque.c \ + ospfd/ospf_orr.c \ ospfd/ospf_packet.c \ ospfd/ospf_ri.c \ ospfd/ospf_route.c \ @@ -101,6 +102,7 @@ noinst_HEADERS += \ ospfd/ospf_memory.h \ ospfd/ospf_neighbor.h \ ospfd/ospf_network.h \ + ospfd/ospf_orr.h \ ospfd/ospf_packet.h \ ospfd/ospf_ri.h \ ospfd/ospf_gr.h \ diff --git a/pimd/pim6_mld.c b/pimd/pim6_mld.c index 23042ef14e..c34c782969 100644 --- a/pimd/pim6_mld.c +++ b/pimd/pim6_mld.c @@ -2997,9 +2997,9 @@ DEFPY(gm_debug_show, "debug show mld interface IFNAME", DEBUG_STR SHOW_STR - "MLD" + MLD_STR INTERFACE_STR - "interface name") + "interface name\n") { struct interface *ifp; struct pim_interface *pim_ifp; diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 1ac22f38a3..ab0689a156 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -5438,7 +5438,7 @@ DEFPY(no_ip_msdp_mesh_group, IP_STR CFG_MSDP_STR "Delete MSDP mesh-group\n" - "Mesh group name") + "Mesh group name\n") { const char *vrfname; char xpath_value[XPATH_MAXLEN]; diff --git a/python/clippy/__init__.py b/python/clippy/__init__.py index 344a1c91ee..281e2bb3c6 100644 --- a/python/clippy/__init__.py +++ b/python/clippy/__init__.py @@ -17,8 +17,23 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import os, stat + +try: + from enum import IntFlag as _IntFlag +except ImportError: + # python <3.6 + from enum import IntEnum as _IntFlag # type: ignore + import _clippy -from _clippy import parse, Graph, GraphNode +from _clippy import ( + parse, + Graph, + GraphNode, + CMD_ATTR_YANG, + CMD_ATTR_HIDDEN, + CMD_ATTR_DEPRECATED, + CMD_ATTR_NOSH, +) frr_top_src = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) @@ -78,3 +93,10 @@ def wrdiff(filename, buf, reffiles=[]): with open(newname, "w") as out: out.write(buf) os.rename(newname, filename) + + +class CmdAttr(_IntFlag): + YANG = CMD_ATTR_YANG + HIDDEN = CMD_ATTR_HIDDEN + DEPRECATED = CMD_ATTR_DEPRECATED + NOSH = CMD_ATTR_NOSH diff --git a/python/makefile.py b/python/makefile.py index afc993b5b9..bd897b7508 100644 --- a/python/makefile.py +++ b/python/makefile.py @@ -160,6 +160,9 @@ for clippy_file in clippy_scan: # combine daemon .xref files into frr.xref out_lines.append("") +xref_targets = [ + target for target in xref_targets if target not in ["tools/ssd", "vtysh/vtysh"] +] out_lines.append( "xrefs = %s" % (" ".join(["%s.xref" % target for target in xref_targets])) ) diff --git a/python/xrelfo.py b/python/xrelfo.py index 17262da8d9..09455ea9b4 100644 --- a/python/xrelfo.py +++ b/python/xrelfo.py @@ -21,12 +21,21 @@ import os import struct import re import traceback -import json + +json_dump_args = {} + +try: + import ujson as json + + json_dump_args["escape_forward_slashes"] = False +except ImportError: + import json + import argparse from clippy.uidhash import uidhash from clippy.elf import * -from clippy import frr_top_src +from clippy import frr_top_src, CmdAttr from tiabwarfo import FieldApplicator try: @@ -196,8 +205,6 @@ Xref.containers[XREFT_LOGMSG] = XrefLogmsg class CmdElement(ELFDissectStruct, XrelfoJson): struct = 'cmd_element' - cmd_attrs = { 0: None, 1: 'deprecated', 2: 'hidden'} - def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -207,10 +214,14 @@ class CmdElement(ELFDissectStruct, XrelfoJson): jsobj.update({ 'string': self.string, 'doc': self.doc, - 'attr': self.cmd_attrs.get(self.attr, self.attr), }) - if jsobj['attr'] is None: - del jsobj['attr'] + if self.attr: + jsobj['attr'] = attr = self.attr + for attrname in CmdAttr.__members__: + val = CmdAttr[attrname] + if attr & val: + jsobj.setdefault('attrs', []).append(attrname.lower()) + attr &= ~val jsobj['defun'] = dict([(i, getattr(self.xref, i)) for i in ['file', 'line', 'func']]) @@ -416,12 +427,12 @@ def _main(args): if args.output: with open(args.output + '.tmp', 'w') as fd: - json.dump(out, fd, indent=2, sort_keys=True) + json.dump(out, fd, indent=2, sort_keys=True, **json_dump_args) os.rename(args.output + '.tmp', args.output) if args.out_by_file: with open(args.out_by_file + '.tmp', 'w') as fd: - json.dump(outbyfile, fd, indent=2, sort_keys=True) + json.dump(outbyfile, fd, indent=2, sort_keys=True, **json_dump_args) os.rename(args.out_by_file + '.tmp', args.out_by_file) if __name__ == '__main__': diff --git a/ripd/ripd.c b/ripd/ripd.c index 8a321d9a91..8e02f1a6c1 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -3551,10 +3551,18 @@ static int rip_vrf_new(struct vrf *vrf) static int rip_vrf_delete(struct vrf *vrf) { + struct rip *rip; + if (IS_RIP_DEBUG_EVENT) zlog_debug("%s: VRF deleted: %s(%u)", __func__, vrf->name, vrf->vrf_id); + rip = rip_lookup_by_vrf_name(vrf->name); + if (!rip) + return 0; + + rip_clean(rip); + return 0; } diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c index 1e7a13d7dc..755debd0a4 100644 --- a/ripngd/ripngd.c +++ b/ripngd/ripngd.c @@ -2581,10 +2581,17 @@ static int ripng_vrf_new(struct vrf *vrf) static int ripng_vrf_delete(struct vrf *vrf) { + struct ripng *ripng; + if (IS_RIPNG_DEBUG_EVENT) zlog_debug("%s: VRF deleted: %s(%u)", __func__, vrf->name, vrf->vrf_id); + ripng = ripng_lookup_by_vrf_name(vrf->name); + if (!ripng) + return 0; + + ripng_clean(ripng); return 0; } diff --git a/snapcraft/snapcraft.yaml.in b/snapcraft/snapcraft.yaml.in index 9729be7b92..f634b59c75 100644 --- a/snapcraft/snapcraft.yaml.in +++ b/snapcraft/snapcraft.yaml.in @@ -272,7 +272,7 @@ parts: - zlib1g prime: - lib/librtr.so* - - usr/lib/x86_64-linux-gnu/libssh.so* + - usr/lib/$SNAPCRAFT_ARCH_TRIPLET/libssh.so* source: https://github.com/rtrlib/rtrlib.git source-type: git source-tag: v0.8.0 diff --git a/staticd/static_main.c b/staticd/static_main.c index 7badd50049..79686158cf 100644 --- a/staticd/static_main.c +++ b/staticd/static_main.c @@ -79,6 +79,7 @@ static void sigint(void) static_vrf_terminate(); + static_zebra_stop(); frr_fini(); exit(0); diff --git a/tests/topotests/bgp_accept_own/__init__.py b/tests/topotests/bgp_accept_own/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_accept_own/__init__.py diff --git a/tests/topotests/bgp_accept_own/ce1/bgpd.conf b/tests/topotests/bgp_accept_own/ce1/bgpd.conf new file mode 100644 index 0000000000..fa53a42919 --- /dev/null +++ b/tests/topotests/bgp_accept_own/ce1/bgpd.conf @@ -0,0 +1,12 @@ +! +debug bgp updates +! +router bgp 65010 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers 1 3 + neighbor 192.168.1.2 timers connect 1 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_accept_own/ce1/zebra.conf b/tests/topotests/bgp_accept_own/ce1/zebra.conf new file mode 100644 index 0000000000..7863ae1650 --- /dev/null +++ b/tests/topotests/bgp_accept_own/ce1/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.1/32 +! +interface ce1-eth0 + ip address 192.168.1.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_accept_own/ce2/bgpd.conf b/tests/topotests/bgp_accept_own/ce2/bgpd.conf new file mode 100644 index 0000000000..cdf8898c90 --- /dev/null +++ b/tests/topotests/bgp_accept_own/ce2/bgpd.conf @@ -0,0 +1,12 @@ +! +debug bgp updates +! +router bgp 65020 + no bgp ebgp-requires-policy + neighbor 192.168.2.2 remote-as external + neighbor 192.168.2.2 timers 1 3 + neighbor 192.168.2.2 timers connect 1 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_accept_own/ce2/zebra.conf b/tests/topotests/bgp_accept_own/ce2/zebra.conf new file mode 100644 index 0000000000..829967e361 --- /dev/null +++ b/tests/topotests/bgp_accept_own/ce2/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.2/32 +! +interface ce2-eth0 + ip address 192.168.2.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_accept_own/pe1/bgpd.conf b/tests/topotests/bgp_accept_own/pe1/bgpd.conf new file mode 100644 index 0000000000..8631293730 --- /dev/null +++ b/tests/topotests/bgp_accept_own/pe1/bgpd.conf @@ -0,0 +1,49 @@ +! +debug bgp updates +debug bgp vpn leak-from-vrf +debug bgp vpn leak-to-vrf +debug bgp nht +! +router bgp 65001 + bgp router-id 10.10.10.10 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + neighbor 10.10.10.101 remote-as internal + neighbor 10.10.10.101 update-source 10.10.10.10 + neighbor 10.10.10.101 timers 1 3 + neighbor 10.10.10.101 timers connect 1 + address-family ipv4 vpn + neighbor 10.10.10.101 activate + neighbor 10.10.10.101 attribute-unchanged + exit-address-family +! +router bgp 65001 vrf Customer + bgp router-id 192.168.1.2 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 1 3 + neighbor 192.168.1.1 timers connect 1 + address-family ipv4 unicast + label vpn export 10 + rd vpn export 192.168.1.2:2 + rt vpn import 192.168.1.2:2 + rt vpn export 192.168.1.2:2 + export vpn + import vpn + exit-address-family +! +router bgp 65001 vrf Service + bgp router-id 192.168.2.2 + no bgp ebgp-requires-policy + neighbor 192.168.2.1 remote-as external + neighbor 192.168.2.1 timers 1 3 + neighbor 192.168.2.1 timers connect 1 + address-family ipv4 unicast + label vpn export 20 + rd vpn export 192.168.2.2:2 + rt vpn import 192.168.2.2:2 + rt vpn export 192.168.2.2:2 + export vpn + import vpn + exit-address-family +! diff --git a/tests/topotests/bgp_accept_own/pe1/ldpd.conf b/tests/topotests/bgp_accept_own/pe1/ldpd.conf new file mode 100644 index 0000000000..7c1ea33a31 --- /dev/null +++ b/tests/topotests/bgp_accept_own/pe1/ldpd.conf @@ -0,0 +1,12 @@ +mpls ldp + router-id 10.10.10.10 + ! + address-family ipv4 + discovery transport-address 10.10.10.10 + ! + interface pe1-eth2 + exit + ! + exit-address-family + ! +exit diff --git a/tests/topotests/bgp_accept_own/pe1/ospfd.conf b/tests/topotests/bgp_accept_own/pe1/ospfd.conf new file mode 100644 index 0000000000..1a5e1a06bc --- /dev/null +++ b/tests/topotests/bgp_accept_own/pe1/ospfd.conf @@ -0,0 +1,7 @@ +interface pe1-eth2 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +! +router ospf + router-id 10.10.10.10 + network 0.0.0.0/0 area 0 diff --git a/tests/topotests/bgp_accept_own/pe1/zebra.conf b/tests/topotests/bgp_accept_own/pe1/zebra.conf new file mode 100644 index 0000000000..71476d2aef --- /dev/null +++ b/tests/topotests/bgp_accept_own/pe1/zebra.conf @@ -0,0 +1,15 @@ +! +interface lo + ip address 10.10.10.10/32 +! +interface pe1-eth0 vrf Customer + ip address 192.168.1.2/24 +! +interface pe1-eth1 vrf Service + ip address 192.168.2.2/24 +! +interface pe1-eth2 + ip address 10.0.1.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_accept_own/rr1/bgpd.conf b/tests/topotests/bgp_accept_own/rr1/bgpd.conf new file mode 100644 index 0000000000..4f0a6ab0f1 --- /dev/null +++ b/tests/topotests/bgp_accept_own/rr1/bgpd.conf @@ -0,0 +1,25 @@ +! +debug bgp updates +! +router bgp 65001 + bgp router-id 10.10.10.101 + bgp route-reflector allow-outbound-policy + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + neighbor 10.10.10.10 remote-as internal + neighbor 10.10.10.10 update-source 10.10.10.101 + neighbor 10.10.10.10 timers 1 3 + neighbor 10.10.10.10 timers connect 1 + address-family ipv4 vpn + neighbor 10.10.10.10 activate + neighbor 10.10.10.10 route-reflector-client + neighbor 10.10.10.10 route-map pe1 out + exit-address-family +! +route-map pe1 permit 10 + set extcommunity rt 192.168.1.2:2 192.168.2.2:2 + set community 65001:111 accept-own additive + set ip next-hop unchanged +route-map pe1 permit 20 +exit +! diff --git a/tests/topotests/bgp_accept_own/rr1/ldpd.conf b/tests/topotests/bgp_accept_own/rr1/ldpd.conf new file mode 100644 index 0000000000..0369901862 --- /dev/null +++ b/tests/topotests/bgp_accept_own/rr1/ldpd.conf @@ -0,0 +1,12 @@ +mpls ldp + router-id 10.10.10.101 + ! + address-family ipv4 + discovery transport-address 10.10.10.101 + ! + interface rr1-eth0 + exit + ! + exit-address-family + ! +exit diff --git a/tests/topotests/bgp_accept_own/rr1/ospfd.conf b/tests/topotests/bgp_accept_own/rr1/ospfd.conf new file mode 100644 index 0000000000..b598246ef0 --- /dev/null +++ b/tests/topotests/bgp_accept_own/rr1/ospfd.conf @@ -0,0 +1,7 @@ +interface rr1-eth0 + ip ospf dead-interval 4 + ip ospf hello-interval 1 +! +router ospf + router-id 10.10.10.101 + network 0.0.0.0/0 area 0 diff --git a/tests/topotests/bgp_accept_own/rr1/zebra.conf b/tests/topotests/bgp_accept_own/rr1/zebra.conf new file mode 100644 index 0000000000..aa3f633bf5 --- /dev/null +++ b/tests/topotests/bgp_accept_own/rr1/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 10.10.10.101/32 +! +interface rr1-eth0 + ip address 10.0.1.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_accept_own/test_bgp_accept_own.py b/tests/topotests/bgp_accept_own/test_bgp_accept_own.py new file mode 100644 index 0000000000..161530b483 --- /dev/null +++ b/tests/topotests/bgp_accept_own/test_bgp_accept_own.py @@ -0,0 +1,198 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2022 by +# Donatas Abraitis <donatas@opensourcerouting.org> +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" + +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + tgen.add_router("ce1") + tgen.add_router("ce2") + tgen.add_router("pe1") + tgen.add_router("rr1") + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["ce1"]) + switch.add_link(tgen.gears["pe1"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["ce2"]) + switch.add_link(tgen.gears["pe1"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["pe1"]) + switch.add_link(tgen.gears["rr1"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + pe1 = tgen.gears["pe1"] + rr1 = tgen.gears["rr1"] + + pe1.run("ip link add Customer type vrf table 1001") + pe1.run("ip link set up dev Customer") + pe1.run("ip link set pe1-eth0 master Customer") + pe1.run("ip link add Service type vrf table 1002") + pe1.run("ip link set up dev Service") + pe1.run("ip link set pe1-eth1 master Service") + pe1.run("sysctl -w net.mpls.conf.pe1-eth2.input=1") + rr1.run("sysctl -w net.mpls.conf.rr1-eth0.input=1") + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_accept_own(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + pe1 = tgen.gears["pe1"] + ce2 = tgen.gears["ce2"] + + step("Check if routes are not installed in PE1 from RR1 (due to ORIGINATOR_ID)") + + def _bgp_check_received_routes_due_originator_id(): + output = json.loads(pe1.vtysh_cmd("show bgp ipv4 vpn summary json")) + expected = {"peers": {"10.10.10.101": {"pfxRcd": 0, "pfxSnt": 4}}} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_received_routes_due_originator_id) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "Failed, received routes from RR1 regardless ORIGINATOR_ID" + + step("Enable ACCEPT_OWN for RR1") + + pe1.vtysh_cmd( + """ + configure terminal + router bgp 65001 + address-family ipv4 vpn + neighbor 10.10.10.101 accept-own + """ + ) + + step("Check if we received routes due to ACCEPT_OWN from RR1") + + def _bgp_check_received_routes_with_modified_rts(): + output = json.loads(pe1.vtysh_cmd("show bgp ipv4 vpn summary json")) + expected = {"peers": {"10.10.10.101": {"pfxRcd": 4, "pfxSnt": 4}}} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_received_routes_with_modified_rts) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert ( + result is None + ), "Failed, didn't receive routes from RR1 with ACCEPT_OWN enabled" + + step( + "Check if 172.16.255.1/32 is imported into vrf Service due to modified RT list at RR1" + ) + + def _bgp_check_received_routes_with_changed_rts(): + output = json.loads( + pe1.vtysh_cmd("show bgp vrf Service ipv4 unicast 172.16.255.1/32 json") + ) + expected = { + "paths": [ + { + "community": { + "string": "65001:111" + }, + "extendedCommunity": { + "string": "RT:192.168.1.2:2 RT:192.168.2.2:2" + }, + } + ] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_received_routes_with_changed_rts) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert ( + result is None + ), "Failed, routes are not imported from RR1 with modified RT list" + + step("Check if 172.16.255.1/32 is announced to CE2") + + def _bgp_check_received_routes_from_pe(): + output = json.loads(ce2.vtysh_cmd("show ip route 172.16.255.1/32 json")) + expected = { + "172.16.255.1/32": [ + { + "protocol": "bgp", + "installed": True, + "nexthops": [{"ip": "192.168.2.2"}], + } + ] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_received_routes_from_pe) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert ( + result is None + ), "Failed, didn't receive 172.16.255.1/32 from PE1" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_max_med_on_startup/__init__.py b/tests/topotests/bgp_max_med_on_startup/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_max_med_on_startup/__init__.py diff --git a/tests/topotests/bgp_max_med_on_startup/r1/bgpd.conf b/tests/topotests/bgp_max_med_on_startup/r1/bgpd.conf new file mode 100644 index 0000000000..41bf96344a --- /dev/null +++ b/tests/topotests/bgp_max_med_on_startup/r1/bgpd.conf @@ -0,0 +1,11 @@ +! +router bgp 65001 + bgp max-med on-startup 5 777 + no bgp ebgp-requires-policy + neighbor 192.168.255.2 remote-as 65001 + neighbor 192.168.255.2 timers 3 10 + address-family ipv4 unicast + redistribute connected + exit-address-family + ! +! diff --git a/tests/topotests/bgp_max_med_on_startup/r1/zebra.conf b/tests/topotests/bgp_max_med_on_startup/r1/zebra.conf new file mode 100644 index 0000000000..7c2ed09b81 --- /dev/null +++ b/tests/topotests/bgp_max_med_on_startup/r1/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 172.16.255.254/32 +! +interface r1-eth0 + ip address 192.168.255.1/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_max_med_on_startup/r2/bgpd.conf b/tests/topotests/bgp_max_med_on_startup/r2/bgpd.conf new file mode 100644 index 0000000000..187713d089 --- /dev/null +++ b/tests/topotests/bgp_max_med_on_startup/r2/bgpd.conf @@ -0,0 +1,7 @@ +! +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.255.1 remote-as 65001 + neighbor 192.168.255.1 timers 3 10 + ! +! diff --git a/tests/topotests/bgp_max_med_on_startup/r2/zebra.conf b/tests/topotests/bgp_max_med_on_startup/r2/zebra.conf new file mode 100644 index 0000000000..fd45c48d6d --- /dev/null +++ b/tests/topotests/bgp_max_med_on_startup/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/30 +! +ip forwarding +! diff --git a/tests/topotests/bgp_max_med_on_startup/test_bgp_max_med_on_startup.py b/tests/topotests/bgp_max_med_on_startup/test_bgp_max_med_on_startup.py new file mode 100644 index 0000000000..a83d43310b --- /dev/null +++ b/tests/topotests/bgp_max_med_on_startup/test_bgp_max_med_on_startup.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python + +# +# test_bgp_max_med_on_startup.py +# +# Copyright (c) 2022 Rubicon Communications, LLC. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +Test whether `bgp max-med on-startup (5-86400) [(0-4294967295)]` is working +correctly. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 3): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_max_med_on_startup(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router1 = tgen.gears["r1"] + router2 = tgen.gears["r2"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = {"192.168.255.1": {"bgpState": "Established"}} + return topotest.json_cmp(output, expected) + + def _bgp_has_routes(router, metric): + output = json.loads( + router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 routes json") + ) + expected = {"routes": {"172.16.255.254/32": [{"metric": metric}]}} + return topotest.json_cmp(output, expected) + + # Check session is established + test_func = functools.partial(_bgp_converge, router2) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "Failed bgp convergence on r2" + + # Check metric has value of max-med + test_func = functools.partial(_bgp_has_routes, router2, 777) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + assert result is None, "r2 does not receive routes with metric 777" + + # Check that when the max-med timer expires, metric is updated + test_func = functools.partial(_bgp_has_routes, router2, 0) + success, result = topotest.run_and_expect(test_func, None, count=16, wait=0.5) + assert result is None, "r2 does not receive routes with metric 0" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf index 8defa0125a..8ccf7a2a34 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf @@ -27,7 +27,7 @@ segment-routing srv6 locators locator loc1 - prefix 2001:db8:1:1::/64 + prefix 2001:db8:1:1::/64 func-bits 8 ! ! ! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf index 51d9c92235..839454d8a8 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf @@ -27,7 +27,7 @@ segment-routing srv6 locators locator loc1 - prefix 2001:db8:2:2::/64 + prefix 2001:db8:2:2::/64 func-bits 8 ! ! ! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/zebra.conf index a43cec20ef..a9319a6aed 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/zebra.conf @@ -28,7 +28,7 @@ segment-routing srv6 locators locator loc1 - prefix 2001:db8:1:1::/64 + prefix 2001:db8:1:1::/64 func-bits 8 ! ! ! diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/zebra.conf index 71ddedf6ff..9e5fa0ac07 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/zebra.conf @@ -27,7 +27,7 @@ segment-routing srv6 locators locator loc1 - prefix 2001:db8:2:2::/64 + prefix 2001:db8:2:2::/64 func-bits 8 ! ! ! diff --git a/tests/topotests/isis_topo1/test_isis_topo1.py b/tests/topotests/isis_topo1/test_isis_topo1.py index 014722387f..8b6c3772b8 100644 --- a/tests/topotests/isis_topo1/test_isis_topo1.py +++ b/tests/topotests/isis_topo1/test_isis_topo1.py @@ -25,7 +25,7 @@ """ test_isis_topo1.py: Test ISIS topology. """ - +import datetime import functools import json import os @@ -38,6 +38,11 @@ sys.path.append(os.path.join(CWD, "../")) # pylint: disable=C0413 from lib import topotest +from lib.common_config import ( + retry, + stop_router, + start_router, +) from lib.topogen import Topogen, TopoRouter, get_topogen from lib.topolog import logger @@ -248,10 +253,12 @@ def test_isis_summary_json(): for rname, router in tgen.routers().items(): logger.info("Checking router %s", rname) json_output = tgen.gears[rname].vtysh_cmd("show isis summary json", isjson=True) - assertmsg = "Test isis summary json failed in '{}' data '{}'".format(rname, json_output) - assert json_output['vrf'] == "default", assertmsg - assert json_output['areas'][0]['area'] == "1", assertmsg - assert json_output['areas'][0]['levels'][0]['id'] != '3', assertmsg + assertmsg = "Test isis summary json failed in '{}' data '{}'".format( + rname, json_output + ) + assert json_output["vrf"] == "default", assertmsg + assert json_output["areas"][0]["area"] == "1", assertmsg + assert json_output["areas"][0]["levels"][0]["id"] != "3", assertmsg def test_isis_interface_json(): @@ -265,15 +272,29 @@ def test_isis_interface_json(): logger.info("Checking 'show isis interface json'") for rname, router in tgen.routers().items(): logger.info("Checking router %s", rname) - json_output = tgen.gears[rname].vtysh_cmd("show isis interface json", isjson=True) - assertmsg = "Test isis interface json failed in '{}' data '{}'".format(rname, json_output) - assert json_output['areas'][0]['circuits'][0]['interface']['name'] == rname+"-eth0", assertmsg + json_output = tgen.gears[rname].vtysh_cmd( + "show isis interface json", isjson=True + ) + assertmsg = "Test isis interface json failed in '{}' data '{}'".format( + rname, json_output + ) + assert ( + json_output["areas"][0]["circuits"][0]["interface"]["name"] + == rname + "-eth0" + ), assertmsg for rname, router in tgen.routers().items(): logger.info("Checking router %s", rname) - json_output = tgen.gears[rname].vtysh_cmd("show isis interface detail json", isjson=True) - assertmsg = "Test isis interface json failed in '{}' data '{}'".format(rname, json_output) - assert json_output['areas'][0]['circuits'][0]['interface']['name'] == rname+"-eth0", assertmsg + json_output = tgen.gears[rname].vtysh_cmd( + "show isis interface detail json", isjson=True + ) + assertmsg = "Test isis interface json failed in '{}' data '{}'".format( + rname, json_output + ) + assert ( + json_output["areas"][0]["circuits"][0]["interface"]["name"] + == rname + "-eth0" + ), assertmsg def test_isis_neighbor_json(): @@ -284,19 +305,32 @@ def test_isis_neighbor_json(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - #tgen.mininet_cli() + # tgen.mininet_cli() logger.info("Checking 'show isis neighbor json'") for rname, router in tgen.routers().items(): logger.info("Checking router %s", rname) - json_output = tgen.gears[rname].vtysh_cmd("show isis neighbor json", isjson=True) - assertmsg = "Test isis neighbor json failed in '{}' data '{}'".format(rname, json_output) - assert json_output['areas'][0]['circuits'][0]['interface'] == rname+"-eth0", assertmsg + json_output = tgen.gears[rname].vtysh_cmd( + "show isis neighbor json", isjson=True + ) + assertmsg = "Test isis neighbor json failed in '{}' data '{}'".format( + rname, json_output + ) + assert ( + json_output["areas"][0]["circuits"][0]["interface"] == rname + "-eth0" + ), assertmsg for rname, router in tgen.routers().items(): logger.info("Checking router %s", rname) - json_output = tgen.gears[rname].vtysh_cmd("show isis neighbor detail json", isjson=True) - assertmsg = "Test isis neighbor json failed in '{}' data '{}'".format(rname, json_output) - assert json_output['areas'][0]['circuits'][0]['interface']['name'] == rname+"-eth0", assertmsg + json_output = tgen.gears[rname].vtysh_cmd( + "show isis neighbor detail json", isjson=True + ) + assertmsg = "Test isis neighbor json failed in '{}' data '{}'".format( + rname, json_output + ) + assert ( + json_output["areas"][0]["circuits"][0]["interface"]["name"] + == rname + "-eth0" + ), assertmsg def test_isis_database_json(): @@ -307,21 +341,246 @@ def test_isis_database_json(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - #tgen.mininet_cli() + # tgen.mininet_cli() logger.info("Checking 'show isis database json'") for rname, router in tgen.routers().items(): logger.info("Checking router %s", rname) - json_output = tgen.gears[rname].vtysh_cmd("show isis database json", isjson=True) - assertmsg = "Test isis database json failed in '{}' data '{}'".format(rname, json_output) - assert json_output['areas'][0]['area']['name'] == "1", assertmsg - assert json_output['areas'][0]['levels'][0]['id'] != '3', assertmsg + json_output = tgen.gears[rname].vtysh_cmd( + "show isis database json", isjson=True + ) + assertmsg = "Test isis database json failed in '{}' data '{}'".format( + rname, json_output + ) + assert json_output["areas"][0]["area"]["name"] == "1", assertmsg + assert json_output["areas"][0]["levels"][0]["id"] != "3", assertmsg for rname, router in tgen.routers().items(): logger.info("Checking router %s", rname) - json_output = tgen.gears[rname].vtysh_cmd("show isis database detail json", isjson=True) - assertmsg = "Test isis database json failed in '{}' data '{}'".format(rname, json_output) - assert json_output['areas'][0]['area']['name'] == "1", assertmsg - assert json_output['areas'][0]['levels'][0]['id'] != '3', assertmsg + json_output = tgen.gears[rname].vtysh_cmd( + "show isis database detail json", isjson=True + ) + assertmsg = "Test isis database json failed in '{}' data '{}'".format( + rname, json_output + ) + assert json_output["areas"][0]["area"]["name"] == "1", assertmsg + assert json_output["areas"][0]["levels"][0]["id"] != "3", assertmsg + + +def test_isis_overload_on_startup(): + "Check that overload on startup behaves as expected" + + tgen = get_topogen() + net = get_topogen().net + overload_time = 120 + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Testing overload on startup behavior") + + # Configure set-overload-bit on-startup on r3 + r3 = tgen.gears["r3"] + r3.vtysh_cmd( + f""" + configure + router isis 1 + set-overload-bit on-startup {overload_time} + """ + ) + # Restart r3 + logger.info("Stop router") + stop_router(tgen, "r3") + logger.info("Start router") + + tstamp_before_start_router = datetime.datetime.now() + start_router(tgen, "r3") + tstamp_after_start_router = datetime.datetime.now() + startup_router_time = ( + tstamp_after_start_router - tstamp_before_start_router + ).total_seconds() + + # Check that the overload bit is set in r3's LSP + check_lsp_overload_bit("r3", "r3.00-00", "0/0/1") + check_lsp_overload_bit("r1", "r3.00-00", "0/0/1") + + # Attempt to unset overload bit while timer is still running + r3.vtysh_cmd( + """ + configure + router isis 1 + no set-overload-bit on-startup + no set-overload-bit + """ + ) + + # Check overload bit is still set + check_lsp_overload_bit("r1", "r3.00-00", "0/0/1") + + # Check that overload bit is unset after timer completes + check_lsp_overload_bit("r3", "r3.00-00", "0/0/0") + tstamp_after_bit_unset = datetime.datetime.now() + check_lsp_overload_bit("r1", "r3.00-00", "0/0/0") + + # Collect time overloaded + time_overloaded = ( + tstamp_after_bit_unset - tstamp_after_start_router + ).total_seconds() + logger.info(f"Time Overloaded: {time_overloaded}") + + # Use time it took to startup router as lower bound + logger.info( + f"Assert that overload time falls in range: {overload_time - startup_router_time} < {time_overloaded} <= {overload_time}" + ) + result = overload_time - startup_router_time < time_overloaded <= overload_time + assert result + + +def test_isis_overload_on_startup_cancel_timer(): + "Check that overload on startup timer is cancelled when overload bit is set/unset" + + tgen = get_topogen() + net = get_topogen().net + overload_time = 90 + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info( + "Testing overload on startup behavior with set overload bit: cancel timer" + ) + + # Configure set-overload-bit on-startup on r3 + r3 = tgen.gears["r3"] + r3.vtysh_cmd( + f""" + configure + router isis 1 + set-overload-bit on-startup {overload_time} + set-overload-bit + """ + ) + # Restart r3 + logger.info("Stop router") + stop_router(tgen, "r3") + logger.info("Start router") + start_router(tgen, "r3") + + # Check that the overload bit is set in r3's LSP + check_lsp_overload_bit("r3", "r3.00-00", "0/0/1") + + # Check that overload timer is running + check_overload_timer("r3", True) + + # Unset overload bit while timer is running + r3.vtysh_cmd( + """ + configure + router isis 1 + no set-overload-bit + """ + ) + + # Check that overload timer is cancelled + check_overload_timer("r3", False) + + # Check overload bit is unset + check_lsp_overload_bit("r3", "r3.00-00", "0/0/0") + + +def test_isis_overload_on_startup_override_timer(): + "Check that overload bit remains set after overload timer expires if overload bit is configured" + + tgen = get_topogen() + net = get_topogen().net + overload_time = 60 + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info( + "Testing overload on startup behavior with set overload bit: override timer" + ) + + # Configure set-overload-bit on-startup on r3 + r3 = tgen.gears["r3"] + r3.vtysh_cmd( + f""" + configure + router isis 1 + set-overload-bit on-startup {overload_time} + set-overload-bit + """ + ) + # Restart r3 + logger.info("Stop router") + stop_router(tgen, "r3") + logger.info("Start router") + start_router(tgen, "r3") + + # Check that the overload bit is set in r3's LSP + check_lsp_overload_bit("r3", "r3.00-00", "0/0/1") + + # Check that overload timer is running + check_overload_timer("r3", True) + + # Check that overload timer expired + check_overload_timer("r3", False) + + # Check overload bit is still set + check_lsp_overload_bit("r3", "r3.00-00", "0/0/1") + + +@retry(retry_timeout=200) +def _check_lsp_overload_bit(router, overloaded_router_lsp, att_p_ol_expected): + "Verfiy overload bit in router's LSP" + + tgen = get_topogen() + router = tgen.gears[router] + logger.info(f"check_overload_bit {router}") + isis_database_output = router.vtysh_cmd( + "show isis database {} json".format(overloaded_router_lsp) + ) + + database_json = json.loads(isis_database_output) + att_p_ol = database_json["areas"][0]["levels"][1]["att-p-ol"] + if att_p_ol == att_p_ol_expected: + return True + return "{} peer with expected att_p_ol {} got {} ".format( + router.name, att_p_ol_expected, att_p_ol + ) + + +def check_lsp_overload_bit(router, overloaded_router_lsp, att_p_ol_expected): + "Verfiy overload bit in router's LSP" + + assertmsg = _check_lsp_overload_bit( + router, overloaded_router_lsp, att_p_ol_expected + ) + assert assertmsg is True, assertmsg + + +@retry(retry_timeout=200) +def _check_overload_timer(router, timer_expected): + "Verfiy overload bit in router's LSP" + + tgen = get_topogen() + router = tgen.gears[router] + thread_output = router.vtysh_cmd("show thread timers") + + timer_running = "set_overload_on_start_timer" in thread_output + if timer_running == timer_expected: + return True + return "Expected timer running status: {}".format(timer_expected) + + +def check_overload_timer(router, timer_expected): + "Verfiy overload bit in router's LSP" + + assertmsg = _check_overload_timer(router, timer_expected) + assert assertmsg is True, assertmsg def test_memory_leak(): diff --git a/tools/frr.in b/tools/frr.in index 27b2c0ab84..f0c665fdee 100755 --- a/tools/frr.in +++ b/tools/frr.in @@ -53,13 +53,6 @@ vtyfile() echo "$V_PATH/$1.vty" } -chownfrr() -{ - test -n "$FRR_USER" && chown "$FRR_USER" "$1" - test -n "$FRR_GROUP" && chgrp "$FRR_GROUP" "$1" - test -n "$FRR_CONFIG_MODE" && chmod "$FRR_CONFIG_MODE" "$1" -} - # Check if daemon is started by using the pidfile. started() { @@ -103,12 +96,10 @@ check_daemon() # check for config file if [ -n "$2" ]; then if [ ! -r "$C_PATH/$1-$2.conf" ]; then - touch "$C_PATH/$1-$2.conf" - chownfrr "$C_PATH/$1-$2.conf" + install -g "$FRR_GROUP" -o "$FRR_USER" -m "$FRR_CONFIG_MODE" /dev/null "$C_PATH/$1-$2.conf" fi elif [ ! -r "$C_PATH/$1.conf" ]; then - touch "$C_PATH/$1.conf" - chownfrr "$C_PATH/$1.conf" + install -g "$FRR_GROUP" -o "$FRR_USER" -m "$FRR_CONFIG_MODE" /dev/null "$C_PATH/$1.conf" fi fi return 0 @@ -533,9 +524,8 @@ convert_daemon_prios if [ ! -d $V_PATH ]; then echo "Creating $V_PATH" - mkdir -p $V_PATH - chownfrr $V_PATH - chmod 755 /$V_PATH + install -g "$FRR_GROUP" -o "$FRR_USER" -m "$FRR_CONFIG_MODE" -d /proc "$V_PATH" + chmod gu+x "${V_PATH}" fi if [ -n "$3" ] && [ "$3" != "all" ]; then diff --git a/tools/frrcommon.sh.in b/tools/frrcommon.sh.in index b589ced965..469b9c5d8c 100755 --- a/tools/frrcommon.sh.in +++ b/tools/frrcommon.sh.in @@ -62,15 +62,6 @@ debug() { printf '\n' >&2 } -chownfrr() { - [ -n "$FRR_USER" ] && chown "$FRR_USER" "$1" - [ -n "$FRR_GROUP" ] && chgrp "$FRR_GROUP" "$1" - [ -n "$FRR_CONFIG_MODE" ] && chmod "$FRR_CONFIG_MODE" "$1" - if [ -d "$1" ]; then - chmod gu+x "$1" - fi -} - vtysh_b () { [ "$1" = "watchfrr" ] && return 0 if [ ! -r "$C_PATH/frr.conf" ]; then @@ -152,8 +143,7 @@ daemon_prep() { cfg="$C_PATH/$daemon${inst:+-$inst}.conf" if [ ! -r "$cfg" ]; then - touch "$cfg" - chownfrr "$cfg" + install -g "$FRR_GROUP" -o "$FRR_USER" -m "$FRR_CONFIG_MODE" /dev/null "$cfg" fi return 0 } @@ -171,8 +161,8 @@ daemon_start() { [ "$MAX_FDS" != "" ] && ulimit -n "$MAX_FDS" > /dev/null 2> /dev/null daemon_prep "$daemon" "$inst" || return 1 if test ! -d "$V_PATH"; then - mkdir -p "$V_PATH" - chownfrr "$V_PATH" + install -g "$FRR_GROUP" -o "$FRR_USER" -m "$FRR_CONFIG_MODE" -d /proc "$V_PATH" + chmod gu+x "${V_PATH}" fi eval wrap="\$${daemon}_wrap" diff --git a/tools/permutations.c b/tools/permutations.c index b280cc15b1..a0b041f2f2 100644 --- a/tools/permutations.c +++ b/tools/permutations.c @@ -80,8 +80,7 @@ void permute(struct graph_node *start) for (unsigned int i = 0; i < vector_active(start->to); i++) { struct graph_node *gn = vector_slot(start->to, i); struct cmd_token *tok = gn->data; - if (tok->attr == CMD_ATTR_HIDDEN - || tok->attr == CMD_ATTR_DEPRECATED) + if (tok->attr & CMD_ATTR_HIDDEN) continue; else if (tok->type == END_TKN || gn == start) { fprintf(stdout, " "); diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index d2f25f2d40..aea7d9abc7 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -121,7 +121,7 @@ DEFPY_YANG(vrrp_priority, VRRP_STR VRRP_VRID_STR VRRP_PRIORITY_STR - "Priority value") + "Priority value\n") { nb_cli_enqueue_change(vty, "./priority", NB_OP_MODIFY, priority_str); @@ -138,7 +138,7 @@ DEFPY_YANG(no_vrrp_priority, VRRP_STR VRRP_VRID_STR VRRP_PRIORITY_STR - "Priority value") + "Priority value\n") { nb_cli_enqueue_change(vty, "./priority", NB_OP_MODIFY, NULL); @@ -162,7 +162,7 @@ DEFPY_YANG(vrrp_advertisement_interval, vrrp_advertisement_interval_cmd, "vrrp (1-255)$vrid advertisement-interval (10-40950)", VRRP_STR VRRP_VRID_STR VRRP_ADVINT_STR - "Advertisement interval in milliseconds; must be multiple of 10") + "Advertisement interval in milliseconds; must be multiple of 10\n") { char val[20]; @@ -183,7 +183,7 @@ DEFPY_YANG(no_vrrp_advertisement_interval, no_vrrp_advertisement_interval_cmd, "no vrrp (1-255)$vrid advertisement-interval [(10-40950)]", NO_STR VRRP_STR VRRP_VRID_STR VRRP_ADVINT_STR - "Advertisement interval in milliseconds; must be multiple of 10") + "Advertisement interval in milliseconds; must be multiple of 10\n") { nb_cli_enqueue_change(vty, "./advertisement-interval", NB_OP_MODIFY, NULL); diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index f39ecf0709..48274d7170 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -2951,7 +2951,7 @@ DEFUN_HIDDEN (show_config_running, show_config_running_cmd, "show configuration running\ [<json|xml> [translate WORD]]\ - [with-defaults]" DAEMONS_LIST, + [with-defaults] " DAEMONS_LIST, SHOW_STR "Configuration information\n" "Running configuration\n" @@ -2972,7 +2972,7 @@ DEFUN (show_yang_operational_data, format <json|xml>\ |translate WORD\ |with-config\ - }]" DAEMONS_LIST, + }] " DAEMONS_LIST, SHOW_STR "YANG information\n" "Show YANG operational data\n" diff --git a/yang/frr-isisd.yang b/yang/frr-isisd.yang index 0812c86fac..380fce3859 100644 --- a/yang/frr-isisd.yang +++ b/yang/frr-isisd.yang @@ -1066,11 +1066,25 @@ module frr-isisd { "If true, identify as L1/L2 router for inter-area traffic."; } - leaf overload { - type boolean; - default "false"; + container overload { description - "If true, avoid any transit traffic."; + "Overload bit configuration."; + leaf enabled { + type boolean; + default "false"; + description + "If true, avoid any transit traffic."; + } + + leaf on-startup { + type uint32 { + range "0..86400"; + } + units "seconds"; + default "0"; + description + "The duration the overload bit should be set on startup."; + } } leaf metric-style { diff --git a/zebra/main.c b/zebra/main.c index 46cf2eea7d..3de97943fd 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -229,6 +229,7 @@ void zebra_finalize(struct thread *dummy) zebra_router_terminate(); + ns_terminate(); frr_fini(); exit(0); } diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c index 043ea0f248..064c91b729 100644 --- a/zebra/zebra_evpn_mh.c +++ b/zebra/zebra_evpn_mh.c @@ -4028,4 +4028,6 @@ void zebra_evpn_mh_terminate(void) hash_free(zmh_info->nhg_table); hash_free(zmh_info->nh_ip_table); bf_free(zmh_info->nh_id_bitmap); + + XFREE(MTYPE_ZMH_INFO, zrouter.mh_info); } diff --git a/zebra/zebra_fpm.c b/zebra/zebra_fpm.c index 1b2753377b..c74c692ee1 100644 --- a/zebra/zebra_fpm.c +++ b/zebra/zebra_fpm.c @@ -1841,12 +1841,15 @@ DEFUN (clear_zebra_fpm_stats, /* * update fpm connection information */ -DEFUN ( fpm_remote_ip, +DEFUN (fpm_remote_ip, fpm_remote_ip_cmd, - "fpm connection ip A.B.C.D port (1-65535)", - "fpm connection remote ip and port\n" - "Remote fpm server ip A.B.C.D\n" - "Enter ip ") + "fpm connection ip A.B.C.D port (1-65535)", + "Forwarding Path Manager\n" + "Configure FPM connection\n" + "Connect to IPv4 address\n" + "Connect to IPv4 address\n" + "TCP port number\n" + "TCP port number\n") { in_addr_t fpm_server; @@ -1867,13 +1870,16 @@ DEFUN ( fpm_remote_ip, return CMD_SUCCESS; } -DEFUN ( no_fpm_remote_ip, +DEFUN (no_fpm_remote_ip, no_fpm_remote_ip_cmd, - "no fpm connection ip A.B.C.D port (1-65535)", - "fpm connection remote ip and port\n" - "Connection\n" - "Remote fpm server ip A.B.C.D\n" - "Enter ip ") + "no fpm connection ip A.B.C.D port (1-65535)", + NO_STR + "Forwarding Path Manager\n" + "Remove configured FPM connection\n" + "Connect to IPv4 address\n" + "Connect to IPv4 address\n" + "TCP port number\n" + "TCP port number\n") { if (zfpm_g->fpm_server != inet_addr(argv[4]->arg) || zfpm_g->fpm_port != atoi(argv[6]->arg)) diff --git a/zebra/zebra_srv6_vty.c b/zebra/zebra_srv6_vty.c index 62ce17326c..daac4cade0 100644 --- a/zebra/zebra_srv6_vty.c +++ b/zebra/zebra_srv6_vty.c @@ -271,7 +271,7 @@ DEFUN (no_srv6_locator, DEFPY (locator_prefix, locator_prefix_cmd, - "prefix X:X::X:X/M$prefix [func-bits (16-64)$func_bit_len]", + "prefix X:X::X:X/M$prefix [func-bits (0-64)$func_bit_len]", "Configure SRv6 locator prefix\n" "Specify SRv6 locator prefix\n" "Configure SRv6 locator function length in bits\n" @@ -281,7 +281,14 @@ DEFPY (locator_prefix, struct srv6_locator_chunk *chunk = NULL; struct listnode *node = NULL; + if (prefix->prefixlen != 64) { + vty_out(vty, + "%% Invalid argument: Unsupported locator format\n"); + return CMD_WARNING_CONFIG_FAILED; + } + locator->prefix = *prefix; + func_bit_len = func_bit_len ?: ZEBRA_SRV6_FUNCTION_LENGTH; /* * TODO(slankdev): please support variable node-bit-length. @@ -298,8 +305,8 @@ DEFPY (locator_prefix, * user should use a pattern of zeros as a filler. * (3) The Node Id portion (LSBs) cannot exceed 24 bits. */ - locator->block_bits_length = prefix->prefixlen - 24; - locator->node_bits_length = 24; + locator->block_bits_length = ZEBRA_SRV6_LOCATOR_BLOCK_LENGTH; + locator->node_bits_length = ZEBRA_SRV6_LOCATOR_NODE_LENGTH; locator->function_bits_length = func_bit_len; locator->argument_bits_length = 0; diff --git a/zebra/zebra_srv6_vty.h b/zebra/zebra_srv6_vty.h index 42d6aefa9a..2f8b5048d5 100644 --- a/zebra/zebra_srv6_vty.h +++ b/zebra/zebra_srv6_vty.h @@ -20,6 +20,10 @@ #ifndef _ZEBRA_SRV6_VTY_H #define _ZEBRA_SRV6_VTY_H +#define ZEBRA_SRV6_LOCATOR_BLOCK_LENGTH 40 +#define ZEBRA_SRV6_LOCATOR_NODE_LENGTH 24 +#define ZEBRA_SRV6_FUNCTION_LENGTH 16 + extern void zebra_srv6_vty_init(void); #endif /* _ZEBRA_SRV6_VTY_H */ |
