diff options
160 files changed, 6116 insertions, 1047 deletions
diff --git a/.github/workflows/conflicts.yml b/.github/workflows/conflicts.yml new file mode 100644 index 000000000..18a8c0d15 --- /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/babeld/babel_zebra.c b/babeld/babel_zebra.c index d0da93e50..daaa870a6 100644 --- a/babeld/babel_zebra.c +++ b/babeld/babel_zebra.c @@ -225,6 +225,8 @@ DEFUN_NOSH (show_debugging_babel, debug_babel_config_write(vty); + cmd_show_lib_debugs(vty); + return CMD_SUCCESS; } diff --git a/bfdd/bfdd_vty.c b/bfdd/bfdd_vty.c index 21429f06c..4a2c5bf66 100644 --- a/bfdd/bfdd_vty.c +++ b/bfdd/bfdd_vty.c @@ -973,6 +973,8 @@ DEFUN_NOSH(show_debugging_bfd, if (bglobal.debug_network) vty_out(vty, " Network layer debugging is on.\n"); + cmd_show_lib_debugs(vty); + return CMD_SUCCESS; } diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index 4963ea64d..41ae6ef49 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 616ddb440..05a5d4486 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 df7262be6..580c18b58 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,12 @@ 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; } @@ -2412,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; } @@ -2544,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 be5ed0afd..f7090260a 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 ddda10077..a85432a33 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 aaf6c480b..368c2c500 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 850657d35..ced3e1890 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 510cfa21c..990c6e1fa 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 d7fd4bc77..66eef1aa5 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 c5cc7d429..9af4cdf3a 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_network.c b/bgpd/bgp_network.c index 9ecc2ae4e..9582ec01e 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -207,6 +207,25 @@ int bgp_md5_set(struct peer *peer) return bgp_md5_set_password(peer, peer->password); } +static void bgp_update_setsockopt_tcp_keepalive(struct bgp *bgp, int fd) +{ + if (!bgp) + return; + if (bgp->tcp_keepalive_idle != 0) { + int ret; + + ret = setsockopt_tcp_keepalive(fd, bgp->tcp_keepalive_idle, + bgp->tcp_keepalive_intvl, + bgp->tcp_keepalive_probes); + if (ret < 0) + zlog_err( + "Can't set TCP keepalive on socket %d, idle %u intvl %u probes %u", + fd, bgp->tcp_keepalive_idle, + bgp->tcp_keepalive_intvl, + bgp->tcp_keepalive_probes); + } +} + int bgp_md5_unset(struct peer *peer) { /* Unset the password from listen socket. */ @@ -415,6 +434,9 @@ static void bgp_accept(struct thread *thread) bgp_socket_set_buffer_size(bgp_sock); + /* Set TCP keepalive when TCP keepalive is enabled */ + bgp_update_setsockopt_tcp_keepalive(bgp, bgp_sock); + /* Check remote IP address */ peer1 = peer_lookup(bgp, &su); @@ -718,12 +740,16 @@ int bgp_connect(struct peer *peer) bgp_socket_set_buffer_size(peer->fd); + /* Set TCP keepalive when TCP keepalive is enabled */ + bgp_update_setsockopt_tcp_keepalive(peer->bgp, peer->fd); + if (bgp_set_socket_ttl(peer, peer->fd) < 0) { peer->last_reset = PEER_DOWN_SOCKET_ERROR; if (bgp_debug_neighbor_events(peer)) zlog_debug("%s: Failure to set socket ttl for connection to %s, error received: %s(%d)", __func__, peer->host, safe_strerror(errno), errno); + return -1; } diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index 59b31072b..075350cd2 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -865,7 +865,6 @@ static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp, } tbuf = time(NULL) - (monotime(NULL) - bnc->last_update); vty_out(vty, " Last update: %s", ctime(&tbuf)); - vty_out(vty, "\n"); /* show paths dependent on nexthop, if needed. */ if (specific) @@ -912,6 +911,7 @@ static int show_ip_bgp_nexthop_table(struct vty *vty, const char *name, struct prefix nhop; struct bgp_nexthop_cache_head (*tree)[AFI_MAX]; struct bgp_nexthop_cache *bnc; + bool found = false; if (!str2prefix(nhopip_str, &nhop)) { vty_out(vty, "nexthop address is malformed\n"); @@ -919,12 +919,16 @@ static int show_ip_bgp_nexthop_table(struct vty *vty, const char *name, } tree = import_table ? &bgp->import_check_table : &bgp->nexthop_cache_table; - bnc = bnc_find(&(*tree)[family2afi(nhop.family)], &nhop, 0, 0); - if (!bnc) { - vty_out(vty, "specified nexthop does not have entry\n"); - return CMD_SUCCESS; + frr_each (bgp_nexthop_cache, &(*tree)[family2afi(nhop.family)], + bnc) { + if (prefix_cmp(&bnc->prefix, &nhop)) + continue; + bgp_show_nexthop(vty, bgp, bnc, true); + found = true; } - bgp_show_nexthop(vty, bgp, bnc, true); + if (!found) + vty_out(vty, "nexthop %s does not have entry\n", + nhopip_str); } else bgp_show_nexthops(vty, bgp, import_table); diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index 7274bcdb2..7eeab373a 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 000000000..7fed6b775 --- /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 000000000..158de3034 --- /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 7d4110029..f6b6cb93d 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, @@ -3854,8 +3997,6 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, int do_loop_check = 1; int has_valid_label = 0; afi_t nh_afi; - uint8_t pi_type = 0; - uint8_t pi_sub_type = 0; bool force_evpn_import = false; safi_t orig_safi = safi; bool leak_success = true; @@ -3948,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. */ @@ -4059,15 +4208,10 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, bgp_attr_add_gshut_community(&new_attr); } - if (pi) { - pi_type = pi->type; - pi_sub_type = pi->sub_type; - } - /* next hop check. */ - if (!CHECK_FLAG(peer->flags, PEER_FLAG_IS_RFAPI_HD) - && bgp_update_martian_nexthop(bgp, afi, safi, pi_type, pi_sub_type, - &new_attr, dest)) { + if (!CHECK_FLAG(peer->flags, PEER_FLAG_IS_RFAPI_HD) && + bgp_update_martian_nexthop(bgp, afi, safi, type, sub_type, + &new_attr, dest)) { peer->stat_pfx_nh_invalid++; reason = "martian or self next-hop;"; bgp_attr_flush(&new_attr); @@ -4456,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) { @@ -4507,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 @@ -4616,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; @@ -4663,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) { @@ -6384,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, @@ -6424,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, @@ -8783,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: @@ -11859,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: @@ -11869,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; - } } @@ -12203,8 +12357,10 @@ DEFUN (show_ip_bgp_large_community, return CMD_WARNING; if (argv_find(argv, argc, "AA:BB:CC", &idx)) { - if (argv_find(argv, argc, "exact-match", &idx)) + if (argv_find(argv, argc, "exact-match", &idx)) { + argc--; exact_match = 1; + } return bgp_show_lcommunity(vty, bgp, argc, argv, exact_match, afi, safi, uj); } else @@ -12411,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 @@ -12459,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") @@ -12475,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--; @@ -12649,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, @@ -12700,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, @@ -12739,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 ddef4ca1b..22d28ecd0 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; @@ -852,4 +853,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_rpki.c b/bgpd/bgp_rpki.c index b90c09c68..cb7afd896 100644 --- a/bgpd/bgp_rpki.c +++ b/bgpd/bgp_rpki.c @@ -592,7 +592,7 @@ static int bgp_rpki_module_init(void) hook_register(bgp_rpki_prefix_status, rpki_validate_prefix); hook_register(frr_late_init, bgp_rpki_init); - hook_register(frr_early_fini, &bgp_rpki_fini); + hook_register(frr_early_fini, bgp_rpki_fini); return 0; } @@ -1688,7 +1688,7 @@ DEFUN_YANG (no_match_rpki, const char *xpath = "./match-condition[condition='frr-bgp-route-map:rpki']"; - nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); return nb_cli_apply_changes(vty, NULL); } diff --git a/bgpd/bgp_table.h b/bgpd/bgp_table.h index 86cd4f3da..91d92ebd6 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 27e367770..72e70ebf9 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 9a4e4b7af..e574ae780 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,10 +297,9 @@ 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; - struct in6_addr *tovpn_sid; /* release chunk notification via ZAPI */ ret = bgp_zebra_srv6_manager_release_locator_chunk( @@ -324,16 +325,12 @@ static int bgp_srv6_locator_unset(struct bgp *bgp) continue; /* refresh vpnv4 tovpn_sid */ - tovpn_sid = bgp_vrf->vpn_policy[AFI_IP].tovpn_sid; - if (tovpn_sid) - XFREE(MTYPE_BGP_SRV6_SID, - bgp_vrf->vpn_policy[AFI_IP].tovpn_sid); + XFREE(MTYPE_BGP_SRV6_SID, + bgp_vrf->vpn_policy[AFI_IP].tovpn_sid); /* refresh vpnv6 tovpn_sid */ - tovpn_sid = bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid; - if (tovpn_sid) - XFREE(MTYPE_BGP_SRV6_SID, - bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid); + XFREE(MTYPE_BGP_SRV6_SID, + bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid); } /* update vpn bgp processes */ @@ -345,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 */ @@ -873,9 +878,6 @@ int bgp_vty_return(struct vty *vty, enum bgp_create_error_code ret) case BGP_ERR_REMOVE_PRIVATE_AS: str = "remove-private-AS cannot be configured for IBGP peers"; break; - case BGP_ERR_LOCAL_AS_ALLOWED_ONLY_FOR_EBGP: - str = "Local-AS allowed only for EBGP peers"; - break; case BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS: str = "Cannot have local-as same as BGP AS number"; break; @@ -936,9 +938,6 @@ int bgp_vty_return(struct vty *vty, enum bgp_create_error_code ret) case BGP_ERR_AF_UNCONFIGURED: str = "AFI/SAFI specified is not currently configured."; break; - case BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS_REMOTE_AS: - str = "AS specified for local as is the same as the remote as and this is not allowed."; - break; case BGP_ERR_INVALID_AS: str = "Confederation AS specified is the same AS as our AS."; break; @@ -948,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); @@ -2354,6 +2356,15 @@ void bgp_config_write_coalesce_time(struct vty *vty, struct bgp *bgp) vty_out(vty, " coalesce-time %u\n", bgp->coalesce_time); } +/* BGP TCP keepalive */ +static void bgp_config_tcp_keepalive(struct vty *vty, struct bgp *bgp) +{ + if (bgp->tcp_keepalive_idle) { + vty_out(vty, " bgp tcp-keepalive %u %u %u\n", + bgp->tcp_keepalive_idle, bgp->tcp_keepalive_intvl, + bgp->tcp_keepalive_probes); + } +} DEFUN (bgp_coalesce_time, bgp_coalesce_time_cmd, @@ -2577,6 +2588,38 @@ DEFUN(no_bgp_minimum_holdtime, no_bgp_minimum_holdtime_cmd, return CMD_SUCCESS; } +DEFPY(bgp_tcp_keepalive, bgp_tcp_keepalive_cmd, + "bgp tcp-keepalive (1-65535)$idle (1-65535)$intvl (1-30)$probes", + BGP_STR + "TCP keepalive parameters\n" + "TCP keepalive idle time (seconds)\n" + "TCP keepalive interval (seconds)\n" + "TCP keepalive maximum probes\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + + bgp_tcp_keepalive_set(bgp, (uint16_t)idle, (uint16_t)intvl, + (uint16_t)probes); + + return CMD_SUCCESS; +} + +DEFPY(no_bgp_tcp_keepalive, no_bgp_tcp_keepalive_cmd, + "no bgp tcp-keepalive [(1-65535) (1-65535) (1-30)]", + NO_STR + BGP_STR + "TCP keepalive parameters\n" + "TCP keepalive idle time (seconds)\n" + "TCP keepalive interval (seconds)\n" + "TCP keepalive maximum probes\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + + bgp_tcp_keepalive_unset(bgp); + + return CMD_SUCCESS; +} + DEFUN (bgp_client_to_client_reflection, bgp_client_to_client_reflection_cmd, "bgp client-to-client reflection", @@ -6163,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, @@ -8247,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, @@ -8860,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" @@ -9637,11 +9743,7 @@ DEFPY (show_bgp_srv6, struct listnode *node; struct srv6_locator_chunk *chunk; struct bgp_srv6_function *func; - struct in6_addr *tovpn4_sid; - struct in6_addr *tovpn6_sid; char buf[256]; - char buf_tovpn4_sid[256]; - char buf_tovpn6_sid[256]; bgp = bgp_get_default(); if (!bgp) @@ -9664,19 +9766,10 @@ DEFPY (show_bgp_srv6, vty_out(vty, "- name: %s\n", bgp->name ? bgp->name : "default"); - tovpn4_sid = bgp->vpn_policy[AFI_IP].tovpn_sid; - tovpn6_sid = bgp->vpn_policy[AFI_IP6].tovpn_sid; - if (tovpn4_sid) - inet_ntop(AF_INET6, tovpn4_sid, buf_tovpn4_sid, - sizeof(buf_tovpn4_sid)); - if (tovpn6_sid) - inet_ntop(AF_INET6, tovpn6_sid, buf_tovpn6_sid, - sizeof(buf_tovpn6_sid)); - - vty_out(vty, " vpn_policy[AFI_IP].tovpn_sid: %s\n", - tovpn4_sid ? buf_tovpn4_sid : "none"); - vty_out(vty, " vpn_policy[AFI_IP6].tovpn_sid: %s\n", - tovpn6_sid ? buf_tovpn6_sid : "none"); + vty_out(vty, " vpn_policy[AFI_IP].tovpn_sid: %pI6\n", + bgp->vpn_policy[AFI_IP].tovpn_sid); + vty_out(vty, " vpn_policy[AFI_IP6].tovpn_sid: %pI6\n", + bgp->vpn_policy[AFI_IP6].tovpn_sid); } return CMD_SUCCESS; @@ -12363,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"); @@ -17290,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( @@ -17339,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, @@ -17445,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"); } @@ -17717,6 +17826,9 @@ int bgp_config_write(struct vty *vty) vty_out(vty, " bgp graceful-restart preserve-fw-state\n"); + /* BGP TCP keepalive */ + bgp_config_tcp_keepalive(vty, bgp); + /* Stale timer for RIB */ if (bgp->rib_stale_time != BGP_DEFAULT_RIB_STALE_TIME) vty_out(vty, @@ -18951,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); @@ -19461,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); @@ -19596,6 +19740,10 @@ void bgp_vty_init(void) install_element(BGP_NODE, &neighbor_ttl_security_cmd); install_element(BGP_NODE, &no_neighbor_ttl_security_cmd); + /* "bgp tcp-keepalive" commands */ + install_element(BGP_NODE, &bgp_tcp_keepalive_cmd); + install_element(BGP_NODE, &no_bgp_tcp_keepalive_cmd); + /* "show [ip] bgp memory" commands. */ install_element(VIEW_NODE, &show_bgp_memory_cmd); diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 57a859c61..b5c13fddd 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 b2427d730..40e6c90df 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"); @@ -551,6 +552,21 @@ void bgp_timers_unset(struct bgp *bgp) bgp->default_delayopen = BGP_DEFAULT_DELAYOPEN; } +void bgp_tcp_keepalive_set(struct bgp *bgp, uint16_t keepalive_idle, + uint16_t keepalive_intvl, uint16_t keepalive_probes) +{ + bgp->tcp_keepalive_idle = keepalive_idle; + bgp->tcp_keepalive_intvl = keepalive_intvl; + bgp->tcp_keepalive_probes = keepalive_probes; +} + +void bgp_tcp_keepalive_unset(struct bgp *bgp) +{ + bgp->tcp_keepalive_idle = 0; + bgp->tcp_keepalive_intvl = 0; + bgp->tcp_keepalive_probes = 0; +} + /* BGP confederation configuration. */ void bgp_confederation_id_set(struct bgp *bgp, as_t as) { @@ -997,9 +1013,15 @@ void peer_af_flag_inherit(struct peer *peer, afi_t afi, safi_t safi, static inline enum bgp_peer_sort peer_calc_sort(struct peer *peer) { struct bgp *bgp; + as_t local_as; bgp = peer->bgp; + if (peer->change_local_as) + local_as = peer->change_local_as; + else + local_as = peer->local_as; + /* Peer-group */ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { if (peer->as_type == AS_INTERNAL) @@ -1010,8 +1032,8 @@ static inline enum bgp_peer_sort peer_calc_sort(struct peer *peer) else if (peer->as_type == AS_SPECIFIED && peer->as) { assert(bgp); - return (bgp->as == peer->as ? BGP_PEER_IBGP - : BGP_PEER_EBGP); + return (local_as == peer->as ? BGP_PEER_IBGP + : BGP_PEER_EBGP); } else { @@ -1028,17 +1050,17 @@ static inline enum bgp_peer_sort peer_calc_sort(struct peer *peer) /* Normal peer */ if (bgp && CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION)) { - if (peer->local_as == 0) + if (local_as == 0) return BGP_PEER_INTERNAL; - if (peer->local_as == peer->as) { + if (local_as == peer->as) { if (bgp->as == bgp->confed_id) { - if (peer->local_as == bgp->as) + if (local_as == bgp->as) return BGP_PEER_IBGP; else return BGP_PEER_EBGP; } else { - if (peer->local_as == bgp->confed_id) + if (local_as == bgp->confed_id) return BGP_PEER_EBGP; else return BGP_PEER_IBGP; @@ -1056,8 +1078,7 @@ static inline enum bgp_peer_sort peer_calc_sort(struct peer *peer) && (peer->group->conf->as_type != AS_UNSPECIFIED)) { if (peer->group->conf->as_type == AS_SPECIFIED) { - if (peer->local_as - == peer->group->conf->as) + if (local_as == peer->group->conf->as) return BGP_PEER_IBGP; else return BGP_PEER_EBGP; @@ -1073,9 +1094,8 @@ static inline enum bgp_peer_sort peer_calc_sort(struct peer *peer) return (peer->as_type == AS_INTERNAL ? BGP_PEER_IBGP : BGP_PEER_EBGP); - return (peer->local_as == 0 - ? BGP_PEER_INTERNAL - : peer->local_as == peer->as ? BGP_PEER_IBGP + return (local_as == 0 ? BGP_PEER_INTERNAL + : local_as == peer->as ? BGP_PEER_IBGP : BGP_PEER_EBGP); } } @@ -1820,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. */ @@ -1858,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], @@ -1885,14 +1912,6 @@ void peer_as_change(struct peer *peer, as_t as, int as_specified) UNSET_FLAG(peer->af_flags[AFI_L2VPN][SAFI_EVPN], PEER_FLAG_REFLECTOR_CLIENT); } - - /* local-as reset */ - if (newtype != BGP_PEER_EBGP) { - peer->change_local_as = 0; - peer_flag_unset(peer, PEER_FLAG_LOCAL_AS); - peer_flag_unset(peer, PEER_FLAG_LOCAL_AS_NO_PREPEND); - peer_flag_unset(peer, PEER_FLAG_LOCAL_AS_REPLACE_AS); - } } /* If peer does not exist, create new one. If peer already exists, @@ -2760,6 +2779,21 @@ void peer_notify_unconfig(struct peer *peer) BGP_NOTIFY_CEASE_PEER_UNCONFIG); } +static void peer_notify_shutdown(struct peer *peer) +{ + if (BGP_PEER_GRACEFUL_RESTART_CAPABLE(peer)) { + if (bgp_debug_neighbor_events(peer)) + zlog_debug( + "%pBP configured Graceful-Restart, skipping shutdown notification", + peer); + return; + } + + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN); +} + void peer_group_notify_unconfig(struct peer_group *group) { struct peer *peer, *other; @@ -3018,17 +3052,6 @@ int peer_group_bind(struct bgp *bgp, union sockunion *su, struct peer *peer, /* ebgp-multihop reset */ if (gtype == BGP_PEER_IBGP) group->conf->ttl = MAXTTL; - - /* local-as reset */ - if (gtype != BGP_PEER_EBGP) { - group->conf->change_local_as = 0; - peer_flag_unset(group->conf, - PEER_FLAG_LOCAL_AS); - peer_flag_unset(group->conf, - PEER_FLAG_LOCAL_AS_NO_PREPEND); - peer_flag_unset(group->conf, - PEER_FLAG_LOCAL_AS_REPLACE_AS); - } } SET_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE); @@ -3182,6 +3205,7 @@ static struct bgp *bgp_create(as_t *as, const char *name, bgp->default_local_pref = BGP_DEFAULT_LOCAL_PREF; bgp->default_subgroup_pkt_queue_max = BGP_DEFAULT_SUBGROUP_PKT_QUEUE_MAX; + bgp_tcp_keepalive_unset(bgp); bgp_timers_unset(bgp); bgp->default_min_holdtime = 0; bgp->restart_time = BGP_DEFAULT_RESTART_TIME; @@ -3676,11 +3700,8 @@ int bgp_delete(struct bgp *bgp) } /* Inform peers we're going down. */ - for (ALL_LIST_ELEMENTS(bgp->peer, node, next, peer)) { - if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) - bgp_notify_send(peer, BGP_NOTIFY_CEASE, - BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN); - } + for (ALL_LIST_ELEMENTS(bgp->peer, node, next, peer)) + peer_notify_shutdown(peer); /* Delete static routes (networks). */ bgp_static_delete(bgp); @@ -3837,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); @@ -4290,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. */ @@ -4620,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; @@ -6109,17 +6138,10 @@ int peer_local_as_set(struct peer *peer, as_t as, bool no_prepend, struct bgp *bgp = peer->bgp; struct peer *member; struct listnode *node, *nnode; - enum bgp_peer_sort ptype = peer_sort(peer); - - if (ptype != BGP_PEER_EBGP && ptype != BGP_PEER_INTERNAL) - return BGP_ERR_LOCAL_AS_ALLOWED_ONLY_FOR_EBGP; if (bgp->as == as) return BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS; - if (peer->as == as) - return BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS_REMOTE_AS; - /* Save previous flag states. */ old_no_prepend = !!CHECK_FLAG(peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND); @@ -6135,6 +6157,7 @@ int peer_local_as_set(struct peer *peer, as_t as, bool no_prepend, && old_replace_as == replace_as) return 0; peer->change_local_as = as; + (void)peer_sort(peer); /* Check if handling a regular peer. */ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { @@ -8088,11 +8111,18 @@ void bgp_terminate(void) /* reverse bgp_master_init */ for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) { bgp_close_vrf_socket(bgp); - for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) - if (peer_established(peer) || peer->status == OpenSent - || peer->status == OpenConfirm) + for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + if (BGP_PEER_GRACEFUL_RESTART_CAPABLE(peer)) { + if (bgp_debug_neighbor_events(peer)) + zlog_debug( + "%pBP configured Graceful-Restart, skipping unconfig notification", + peer); + continue; + } + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) bgp_notify_send(peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_PEER_UNCONFIG); + } } if (bm->listen_sockets) diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 2c6618e40..44e225b04 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 @@ -132,6 +133,7 @@ struct bgp_master { /* Various BGP global configuration. */ uint8_t options; + #define BGP_OPT_NO_FIB (1 << 0) #define BGP_OPT_NO_LISTEN (1 << 1) #define BGP_OPT_NO_ZEBRA (1 << 2) @@ -196,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, @@ -238,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; }; @@ -768,12 +804,20 @@ struct bgp { char srv6_locator_name[SRV6_LOCNAME_SIZE]; struct list *srv6_locator_chunks; struct list *srv6_functions; + /* TCP keepalive parameters for BGP connection */ + uint16_t tcp_keepalive_idle; + uint16_t tcp_keepalive_intvl; + uint16_t tcp_keepalive_probes; struct timeval ebgprequirespolicywarning; #define FIFTEENMINUTE2USEC (int64_t)15 * 60 * 1000000 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); @@ -1417,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]; @@ -2004,13 +2053,11 @@ enum bgp_create_error_code { BGP_ERR_AF_UNCONFIGURED = -15, BGP_ERR_SOFT_RECONFIG_UNCONFIGURED = -16, BGP_ERR_INSTANCE_MISMATCH = -17, - BGP_ERR_LOCAL_AS_ALLOWED_ONLY_FOR_EBGP = -18, BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS = -19, BGP_ERR_TCPSIG_FAILED = -20, BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK = -21, BGP_ERR_NO_IBGP_WITH_TTLHACK = -22, BGP_ERR_NO_INTERFACE_CONFIG = -23, - BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS_REMOTE_AS = -24, BGP_ERR_AS_OVERRIDE = -25, BGP_ERR_INVALID_DYNAMIC_NEIGHBORS_LIMIT = -26, BGP_ERR_DYNAMIC_NEIGHBORS_RANGE_EXISTS = -27, @@ -2026,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, }; /* @@ -2078,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 @@ -2215,6 +2266,9 @@ extern int peer_default_originate_set(struct peer *peer, afi_t afi, safi_t safi, const char *rmap, struct route_map *route_map); extern int peer_default_originate_unset(struct peer *, afi_t, safi_t); +extern void bgp_tcp_keepalive_set(struct bgp *bgp, uint16_t idle, + uint16_t interval, uint16_t probes); +extern void bgp_tcp_keepalive_unset(struct bgp *bgp); extern void peer_port_set(struct peer *, uint16_t); extern void peer_port_unset(struct peer *); @@ -2325,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 b1eeb937e..765650313 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 4e1080045..4cbdfe0fc 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 f136ea4ae..8a16c57e6 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 @@ -1790,6 +1807,13 @@ Configuring Peers with lower holdtime less than configured minimum holdtime. When this command is not set, minimum holdtime does not work. +.. clicmd:: bgp tcp-keepalive (1-65535) (1-65535) (1-30) + + This command allows user to configure TCP keepalive with new BGP peers. + Each parameter respectively stands for TCP keepalive idle timer (seconds), + interval (seconds), and maximum probes. By default, TCP keepalive is + disabled. + Displaying Information about Peers ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -3471,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 @@ -3536,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 9ccb5ba4b..2b114ad12 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 26810bd88..3aa3d47f3 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 @@ -1060,78 +1066,87 @@ TI-LFA requires a proper Segment Routing configuration. Debugging OSPF ============== -.. clicmd:: debug ospf bfd +.. clicmd:: debug ospf [(1-65535)] bfd Enable or disable debugging for BFD events. This will show BFD integration library messages and OSPF BFD integration messages that are mostly state transitions and validation problems. -.. clicmd:: debug ospf client-api +.. clicmd:: debug ospf [(1-65535)] client-api Show debug information for the OSPF opaque data client API. -.. clicmd:: debug ospf packet (hello|dd|ls-request|ls-update|ls-ack|all) (send|recv) [detail] +.. clicmd:: debug ospf [(1-65535)] default-information + Show debug information of default information - Dump Packet for debugging +.. clicmd:: debug ospf [(1-65535)] packet (hello|dd|ls-request|ls-update|ls-ack|all) (send|recv) [detail] -.. clicmd:: debug ospf ism -.. clicmd:: debug ospf ism (status|events|timers) + Dump Packet for debugging +.. clicmd:: debug ospf [(1-65535)] ism [status|events|timers] - Show debug information of Interface State Machine -.. clicmd:: debug ospf nsm + Show debug information of Interface State Machine -.. clicmd:: debug ospf nsm (status|events|timers) +.. clicmd:: debug ospf [(1-65535)] nsm [status|events|timers] Show debug information of Network State Machine -.. clicmd:: debug ospf event +.. clicmd:: debug ospf [(1-65535)] event Show debug information of OSPF event -.. clicmd:: debug ospf nssa +.. clicmd:: debug ospf [(1-65535)] nssa Show debug information about Not So Stub Area -.. clicmd:: debug ospf lsa +.. clicmd:: debug ospf [(1-65535)] ldp-sync + + Show debug information about LDP-Sync -.. clicmd:: debug ospf lsa (generate|flooding|refresh) +.. clicmd:: debug ospf [(1-65535)] lsa [aggregate|flooding|generate|install|refresh] Show debug detail of Link State messages -.. clicmd:: debug ospf te +.. clicmd:: debug ospf [(1-65535)] sr + + Show debug information about Segment Routing + +.. clicmd:: debug ospf [(1-65535)] te Show debug information about Traffic Engineering LSA -.. clicmd:: debug ospf zebra +.. clicmd:: debug ospf [(1-65535)] ti-lfa + + Show debug information about SR TI-LFA -.. clicmd:: debug ospf zebra (interface|redistribute) +.. clicmd:: debug ospf [(1-65535)] zebra [interface|redistribute] Show debug information of ZEBRA API -.. clicmd:: debug ospf graceful-restart helper +.. clicmd:: debug ospf [(1-65535)] graceful-restart Enable/disable debug information for OSPF Graceful Restart Helper .. clicmd:: show debugging ospf -.. clicmd:: debug ospf lsa aggregate - Debug commnd to enable/disable external route summarisation specific debugs. +.. 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 05990e252..01cf5316a 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/eigrpd/eigrp_dump.c b/eigrpd/eigrp_dump.c index e1ad51a9d..b0d34f55e 100644 --- a/eigrpd/eigrp_dump.c +++ b/eigrpd/eigrp_dump.c @@ -322,6 +322,7 @@ DEFUN_NOSH (show_debugging_eigrp, } } + cmd_show_lib_debugs(vty); return CMD_SUCCESS; } diff --git a/isisd/isis_cli.c b/isisd/isis_cli.c index a673cb8c1..9db867e2c 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 5387f3703..63b4edb1e 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 b13b2a35e..d7762324d 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 a2ba33d07..4f4e6dc73 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 00ca8be3b..a9f2eaea9 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 e0decf48f..1b7663fcf 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 54e6be597..efea1e5d5 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -1700,6 +1700,8 @@ DEFUN_NOSH (show_debugging, if (IS_DEBUG_LFA) print_debug(vty, DEBUG_LFA, 1); + cmd_show_lib_debugs(vty); + return CMD_SUCCESS; } @@ -3196,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); @@ -3211,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 4951e5809..a9c1d6043 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 f1fc74406..3d11d3137 100644 --- a/ldpd/ldp_vty_cmds.c +++ b/ldpd/ldp_vty_cmds.c @@ -774,7 +774,11 @@ DEFPY_NOSH (ldp_show_debugging_mpls_ldp, "MPLS information\n" "Label Distribution Protocol\n") { - return (ldp_vty_show_debugging(vty)); + ldp_vty_show_debugging(vty); + + cmd_show_lib_debugs(vty); + + return CMD_SUCCESS; } static void diff --git a/lib/command.c b/lib/command.c index e555404c1..1fae32a04 100644 --- a/lib/command.c +++ b/lib/command.c @@ -48,6 +48,7 @@ #include "lib_errors.h" #include "northbound_cli.h" #include "network.h" +#include "routemap.h" #include "frrscript.h" @@ -2442,6 +2443,11 @@ const char *host_config_get(void) return host.config; } +void cmd_show_lib_debugs(struct vty *vty) +{ + route_map_show_debug(vty); +} + void install_default(enum node_type node) { _install_element(node, &config_exit_cmd); diff --git a/lib/command.h b/lib/command.h index cde10a0a3..f4168dedd 100644 --- a/lib/command.h +++ b/lib/command.h @@ -401,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" @@ -633,6 +634,12 @@ extern char *cmd_variable_comp2str(vector comps, unsigned short cols); extern void command_setup_early_logging(const char *dest, const char *level); +/* + * Allow a mechanism for `debug XXX` commands that live + * under the lib directory to output their debug status + */ +extern void cmd_show_lib_debugs(struct vty *vty); + #ifdef __cplusplus } #endif diff --git a/lib/frrscript.c b/lib/frrscript.c index a19bd0c3d..2e5693261 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 4db3e6f1b..7fa01f70d 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/libfrr.c b/lib/libfrr.c index f5aecd9f7..aee698185 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/orr_msg.h b/lib/orr_msg.h new file mode 100644 index 000000000..b0c4c48df --- /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/routemap.c b/lib/routemap.c index 3cc010c14..3a9279999 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -3174,6 +3174,12 @@ static struct cmd_node rmap_debug_node = { .config_write = rmap_config_write_debug, }; +void route_map_show_debug(struct vty *vty) +{ + if (rmap_debug) + vty_out(vty, "debug route-map\n"); +} + /* Configuration write function. */ static int rmap_config_write_debug(struct vty *vty) { diff --git a/lib/routemap.h b/lib/routemap.h index a36592585..c2e9de6cf 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -1015,6 +1015,8 @@ extern void route_map_optimization_disabled_show(struct vty *vty, bool show_defaults); extern void route_map_cli_init(void); +extern void route_map_show_debug(struct vty *vty); + #ifdef __cplusplus } #endif diff --git a/lib/sockopt.c b/lib/sockopt.c index 7a2b8a1c8..de11a9eab 100644 --- a/lib/sockopt.c +++ b/lib/sockopt.c @@ -693,3 +693,52 @@ int sockopt_tcp_mss_get(int sock) return tcp_maxseg; } + +int setsockopt_tcp_keepalive(int sock, uint16_t keepalive_idle, + uint16_t keepalive_intvl, + uint16_t keepalive_probes) +{ + int val = 1; + + if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) < 0) { + flog_err_sys(EC_LIB_SYSTEM_CALL, + "%s failed: setsockopt SO_KEEPALIVE (%d): %s", + __func__, sock, safe_strerror(errno)); + return -1; + } + +#if defined __OpenBSD__ + return 0; +#else + /* Send first probe after keepalive_idle seconds */ + val = keepalive_idle; + if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < + 0) { + flog_err_sys(EC_LIB_SYSTEM_CALL, + "%s failed: setsockopt TCP_KEEPIDLE (%d): %s", + __func__, sock, safe_strerror(errno)); + return -1; + } + + /* Set interval between two probes */ + val = keepalive_intvl; + if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < + 0) { + flog_err_sys(EC_LIB_SYSTEM_CALL, + "%s failed: setsockopt TCP_KEEPINTVL (%d): %s", + __func__, sock, safe_strerror(errno)); + return -1; + } + + /* Set maximum probes */ + val = keepalive_probes; + if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) { + flog_err_sys(EC_LIB_SYSTEM_CALL, + "%s failed: setsockopt TCP_KEEPCNT (%d): %s", + __func__, sock, safe_strerror(errno)); + return -1; + } + + return 0; +#endif +} diff --git a/lib/sockopt.h b/lib/sockopt.h index 6c80841e3..694edf763 100644 --- a/lib/sockopt.h +++ b/lib/sockopt.h @@ -153,6 +153,28 @@ extern int sockopt_tcp_mss_set(int sock, int tcp_maxseg); * Socket to get max segement size. */ extern int sockopt_tcp_mss_get(int sock); + +/* + * Configure TCP keepalive for a given socket + * + * sock + * Socket to enable keepalive option on. + * + * keepalive_idle + * number of seconds a connection needs to be idle + * before sending out keep-alive proves + * + * keepalive_intvl + * number of seconds between TCP keep-alive probes + * + * keepalive_probes + * max number of probers to send before giving up + * and killing tcp connection + */ +extern int setsockopt_tcp_keepalive(int sock, uint16_t keepalive_idle, + uint16_t keepalive_intvl, + uint16_t keepalive_probes); + #ifdef __cplusplus } #endif diff --git a/lib/subdir.am b/lib/subdir.am index d6defd714..e04e700eb 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/typesafe.h b/lib/typesafe.h index 50c410ad2..8aeabb34e 100644 --- a/lib/typesafe.h +++ b/lib/typesafe.h @@ -850,9 +850,12 @@ macro_inline type *prefix ## _add(struct prefix##_head *h, type *item) \ struct thash_item **np = &h->hh.entries[hbits]; \ while (*np && (*np)->hashval < hval) \ np = &(*np)->next; \ - if (*np && cmpfn(container_of(*np, type, field.hi), item) == 0) { \ - h->hh.count--; \ - return container_of(*np, type, field.hi); \ + while (*np && (*np)->hashval == hval) { \ + if (cmpfn(container_of(*np, type, field.hi), item) == 0) { \ + h->hh.count--; \ + return container_of(*np, type, field.hi); \ + } \ + np = &(*np)->next; \ } \ item->field.hi.next = *np; \ *np = &item->field.hi; \ diff --git a/lib/zclient.h b/lib/zclient.h index c3ea2a16f..fb5da9aad 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/nhrpd/nhrp_vty.c b/nhrpd/nhrp_vty.c index 3a8baa234..53ba9eb12 100644 --- a/nhrpd/nhrp_vty.c +++ b/nhrpd/nhrp_vty.c @@ -126,6 +126,8 @@ DEFUN_NOSH(show_debugging_nhrp, show_debugging_nhrp_cmd, debug_flags_desc[i].str); } + cmd_show_lib_debugs(vty); + return CMD_SUCCESS; } diff --git a/ospf6d/ospf6d.c b/ospf6d/ospf6d.c index a16f4f73e..fe742b912 100644 --- a/ospf6d/ospf6d.c +++ b/ospf6d/ospf6d.c @@ -115,6 +115,8 @@ DEFUN_NOSH (show_debugging_ospf6, config_write_ospf6_debug(vty); + cmd_show_lib_debugs(vty); + return CMD_SUCCESS; } diff --git a/ospfd/ospf_dump.c b/ospfd/ospf_dump.c index 258a93fb1..59f95c5da 100644 --- a/ospfd/ospf_dump.c +++ b/ospfd/ospf_dump.c @@ -54,15 +54,16 @@ unsigned long conf_debug_ospf_nsm = 0; unsigned long conf_debug_ospf_lsa = 0; unsigned long conf_debug_ospf_zebra = 0; unsigned long conf_debug_ospf_nssa = 0; -unsigned long conf_debug_ospf_te = 0; +unsigned long conf_debug_ospf_te; unsigned long conf_debug_ospf_ext = 0; -unsigned long conf_debug_ospf_sr = 0; -unsigned long conf_debug_ospf_ti_lfa = 0; -unsigned long conf_debug_ospf_defaultinfo = 0; -unsigned long conf_debug_ospf_ldp_sync = 0; -unsigned long conf_debug_ospf_gr = 0; +unsigned long conf_debug_ospf_sr; +unsigned long conf_debug_ospf_ti_lfa; +unsigned long conf_debug_ospf_defaultinfo; +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}; @@ -72,15 +73,16 @@ unsigned long term_debug_ospf_nsm = 0; unsigned long term_debug_ospf_lsa = 0; unsigned long term_debug_ospf_zebra = 0; unsigned long term_debug_ospf_nssa = 0; -unsigned long term_debug_ospf_te = 0; +unsigned long term_debug_ospf_te; unsigned long term_debug_ospf_ext = 0; -unsigned long term_debug_ospf_sr = 0; -unsigned long term_debug_ospf_ti_lfa = 0; +unsigned long term_debug_ospf_sr; +unsigned long term_debug_ospf_ti_lfa; unsigned long term_debug_ospf_defaultinfo; unsigned long term_debug_ospf_ldp_sync; -unsigned long term_debug_ospf_gr = 0; +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) { @@ -628,84 +630,9 @@ void ospf_packet_dump(struct stream *s) stream_set_getp(s, gp); } -DEFUN (debug_ospf_packet, +DEFPY (debug_ospf_packet, debug_ospf_packet_cmd, - "debug ospf [(1-65535)] packet <hello|dd|ls-request|ls-update|ls-ack|all> [<send [detail]|recv [detail]|detail>]", - DEBUG_STR - OSPF_STR - "Instance ID\n" - "OSPF packets\n" - "OSPF Hello\n" - "OSPF Database Description\n" - "OSPF Link State Request\n" - "OSPF Link State Update\n" - "OSPF Link State Acknowledgment\n" - "OSPF all packets\n" - "Packet sent\n" - "Detail Information\n" - "Packet received\n" - "Detail Information\n" - "Detail Information\n") -{ - int inst = (argv[2]->type == RANGE_TKN) ? 1 : 0; - int detail = strmatch(argv[argc - 1]->text, "detail"); - int send = strmatch(argv[argc - (1 + detail)]->text, "send"); - int recv = strmatch(argv[argc - (1 + detail)]->text, "recv"); - char *packet = argv[3 + inst]->text; - - if (inst) // user passed instance ID - { - if (inst != ospf_instance) - return CMD_NOT_MY_INSTANCE; - } - - int type = 0; - int flag = 0; - int i; - - /* Check packet type. */ - if (strmatch(packet, "hello")) - type = OSPF_DEBUG_HELLO; - else if (strmatch(packet, "dd")) - type = OSPF_DEBUG_DB_DESC; - else if (strmatch(packet, "ls-request")) - type = OSPF_DEBUG_LS_REQ; - else if (strmatch(packet, "ls-update")) - type = OSPF_DEBUG_LS_UPD; - else if (strmatch(packet, "ls-ack")) - type = OSPF_DEBUG_LS_ACK; - else if (strmatch(packet, "all")) - type = OSPF_DEBUG_ALL; - - /* Cases: - * (none) = send + recv - * detail = send + recv + detail - * recv = recv - * send = send - * recv detail = recv + detail - * send detail = send + detail - */ - if (!send && !recv) - send = recv = 1; - - flag |= (send) ? OSPF_DEBUG_SEND : 0; - flag |= (recv) ? OSPF_DEBUG_RECV : 0; - flag |= (detail) ? OSPF_DEBUG_DETAIL : 0; - - for (i = 0; i < 5; i++) - if (type & (0x01 << i)) { - if (vty->node == CONFIG_NODE) - DEBUG_PACKET_ON(i, flag); - else - TERM_DEBUG_PACKET_ON(i, flag); - } - - return CMD_SUCCESS; -} - -DEFUN (no_debug_ospf_packet, - no_debug_ospf_packet_cmd, - "no debug ospf [(1-65535)] packet <hello|dd|ls-request|ls-update|ls-ack|all> [<send [detail]|recv [detail]|detail>]", + "[no$no] debug ospf [(1-65535)$inst] packet <hello|dd|ls-request|ls-update|ls-ack|all>$packet [<send$send [detail$detail]|recv$recv [detail$detail]|detail$detail>]", NO_STR DEBUG_STR OSPF_STR @@ -723,22 +650,13 @@ DEFUN (no_debug_ospf_packet, "Detail Information\n" "Detail Information\n") { - int inst = (argv[3]->type == RANGE_TKN) ? 1 : 0; - int detail = strmatch(argv[argc - 1]->text, "detail"); - int send = strmatch(argv[argc - (1 + detail)]->text, "send"); - int recv = strmatch(argv[argc - (1 + detail)]->text, "recv"); - char *packet = argv[4 + inst]->text; - - if (inst) // user passed instance ID - { - if (inst != ospf_instance) - return CMD_NOT_MY_INSTANCE; - } - int type = 0; int flag = 0; int i; + if (inst && inst != ospf_instance) + return CMD_NOT_MY_INSTANCE; + /* Check packet type. */ if (strmatch(packet, "hello")) type = OSPF_DEBUG_HELLO; @@ -761,8 +679,10 @@ DEFUN (no_debug_ospf_packet, * recv detail = recv + detail * send detail = send + detail */ - if (!send && !recv) - send = recv = 1; + if (!send && !recv) { + flag |= OSPF_DEBUG_SEND; + flag |= OSPF_DEBUG_RECV; + } flag |= (send) ? OSPF_DEBUG_SEND : 0; flag |= (recv) ? OSPF_DEBUG_RECV : 0; @@ -770,10 +690,17 @@ DEFUN (no_debug_ospf_packet, for (i = 0; i < 5; i++) if (type & (0x01 << i)) { - if (vty->node == CONFIG_NODE) - DEBUG_PACKET_OFF(i, flag); - else - TERM_DEBUG_PACKET_OFF(i, flag); + if (vty->node == CONFIG_NODE) { + if (no) + DEBUG_PACKET_OFF(i, flag); + else + DEBUG_PACKET_ON(i, flag); + } else { + if (no) + TERM_DEBUG_PACKET_OFF(i, flag); + else + TERM_DEBUG_PACKET_ON(i, flag); + } } #ifdef DEBUG @@ -1457,194 +1384,248 @@ DEFUN (no_debug_ospf_instance_nssa, return CMD_SUCCESS; } -DEFUN (debug_ospf_te, +DEFPY (debug_ospf_te, debug_ospf_te_cmd, - "debug ospf te", - DEBUG_STR - OSPF_STR - "OSPF-TE information\n") -{ - if (vty->node == CONFIG_NODE) - CONF_DEBUG_ON(te, TE); - TERM_DEBUG_ON(te, TE); - return CMD_SUCCESS; -} - -DEFUN (no_debug_ospf_te, - no_debug_ospf_te_cmd, - "no debug ospf te", + "[no$no] debug ospf [(1-65535)$instance] te", NO_STR DEBUG_STR OSPF_STR + "Instance ID\n" "OSPF-TE information\n") { - if (vty->node == CONFIG_NODE) - CONF_DEBUG_OFF(te, TE); - TERM_DEBUG_OFF(te, TE); + if (instance && instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + if (vty->node == CONFIG_NODE) { + if (no) + DEBUG_OFF(te, TE); + else + DEBUG_ON(te, TE); + } else { + if (no) + TERM_DEBUG_OFF(te, TE); + else + TERM_DEBUG_ON(te, TE); + } + return CMD_SUCCESS; } -DEFUN (debug_ospf_sr, +DEFPY (debug_ospf_sr, debug_ospf_sr_cmd, - "debug ospf sr", + "[no$no] debug ospf [(1-65535)$instance] sr", + NO_STR DEBUG_STR OSPF_STR + "Instance ID\n" "OSPF-SR information\n") { - if (vty->node == CONFIG_NODE) - CONF_DEBUG_ON(sr, SR); - TERM_DEBUG_ON(sr, SR); + if (instance && instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + if (vty->node == CONFIG_NODE) { + if (no) + DEBUG_OFF(sr, SR); + else + DEBUG_ON(sr, SR); + } else { + if (no) + TERM_DEBUG_OFF(sr, SR); + else + TERM_DEBUG_ON(sr, SR); + } + return CMD_SUCCESS; } -DEFUN (no_debug_ospf_sr, - no_debug_ospf_sr_cmd, - "no debug ospf sr", +DEFPY (debug_ospf_ti_lfa, + debug_ospf_ti_lfa_cmd, + "[no$no] debug ospf [(1-65535)$instance] ti-lfa", NO_STR DEBUG_STR OSPF_STR - "OSPF-SR information\n") + "Instance ID\n" + "OSPF-SR TI-LFA information\n") { - if (vty->node == CONFIG_NODE) - CONF_DEBUG_OFF(sr, SR); - TERM_DEBUG_OFF(sr, SR); - return CMD_SUCCESS; -} + if (instance && instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; -DEFUN(debug_ospf_ti_lfa, debug_ospf_ti_lfa_cmd, "debug ospf ti-lfa", - DEBUG_STR OSPF_STR "OSPF-SR TI-LFA information\n") -{ - if (vty->node == CONFIG_NODE) - CONF_DEBUG_ON(ti_lfa, TI_LFA); - TERM_DEBUG_ON(ti_lfa, TI_LFA); - return CMD_SUCCESS; -} + if (vty->node == CONFIG_NODE) { + if (no) + DEBUG_OFF(ti_lfa, TI_LFA); + else + DEBUG_ON(ti_lfa, TI_LFA); + } else { + if (no) + TERM_DEBUG_OFF(ti_lfa, TI_LFA); + else + TERM_DEBUG_ON(ti_lfa, TI_LFA); + } -DEFUN(no_debug_ospf_ti_lfa, no_debug_ospf_ti_lfa_cmd, "no debug ospf ti-lfa", - NO_STR DEBUG_STR OSPF_STR "OSPF-SR TI-LFA information\n") -{ - if (vty->node == CONFIG_NODE) - CONF_DEBUG_OFF(ti_lfa, TI_LFA); - TERM_DEBUG_OFF(ti_lfa, TI_LFA); return CMD_SUCCESS; } -DEFUN (debug_ospf_default_info, +DEFPY (debug_ospf_default_info, debug_ospf_default_info_cmd, - "debug ospf default-information", + "[no$no] debug ospf [(1-65535)$instance] default-information", + NO_STR DEBUG_STR OSPF_STR + "Instance ID\n" "OSPF default information\n") { - if (vty->node == CONFIG_NODE) - CONF_DEBUG_ON(defaultinfo, DEFAULTINFO); - TERM_DEBUG_ON(defaultinfo, DEFAULTINFO); + if (instance && instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + if (vty->node == CONFIG_NODE) { + if (no) + DEBUG_OFF(defaultinfo, DEFAULTINFO); + else + DEBUG_ON(defaultinfo, DEFAULTINFO); + } else { + if (no) + TERM_DEBUG_OFF(defaultinfo, DEFAULTINFO); + else + TERM_DEBUG_ON(defaultinfo, DEFAULTINFO); + } + return CMD_SUCCESS; } -DEFUN (no_debug_ospf_default_info, - no_debug_ospf_default_info_cmd, - "no debug ospf default-information", +DEFPY (debug_ospf_ldp_sync, + debug_ospf_ldp_sync_cmd, + "[no$no] debug ospf [(1-65535)$instance] ldp-sync", NO_STR DEBUG_STR OSPF_STR - "OSPF default information\n") -{ - if (vty->node == CONFIG_NODE) - CONF_DEBUG_OFF(defaultinfo, DEFAULTINFO); - TERM_DEBUG_OFF(defaultinfo, DEFAULTINFO); - return CMD_SUCCESS; -} - -DEFUN(debug_ospf_ldp_sync, - debug_ospf_ldp_sync_cmd, - "debug ospf ldp-sync", - DEBUG_STR OSPF_STR - "OSPF LDP-Sync information\n") + "Instance ID\n" + "OSPF LDP-Sync information\n") { - if (vty->node == CONFIG_NODE) - CONF_DEBUG_ON(ldp_sync, LDP_SYNC); - TERM_DEBUG_ON(ldp_sync, LDP_SYNC); - return CMD_SUCCESS; -} + if (instance && instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; -DEFUN(no_debug_ospf_ldp_sync, - no_debug_ospf_ldp_sync_cmd, - "no debug ospf ldp-sync", - NO_STR - DEBUG_STR - OSPF_STR - "OSPF LDP-Sync information\n") -{ - if (vty->node == CONFIG_NODE) - CONF_DEBUG_OFF(ldp_sync, LDP_SYNC); - TERM_DEBUG_OFF(ldp_sync, LDP_SYNC); + if (vty->node == CONFIG_NODE) { + if (no) + DEBUG_OFF(ldp_sync, LDP_SYNC); + else + DEBUG_ON(ldp_sync, LDP_SYNC); + } else { + if (no) + TERM_DEBUG_OFF(ldp_sync, LDP_SYNC); + else + TERM_DEBUG_ON(ldp_sync, LDP_SYNC); + } return CMD_SUCCESS; } -DEFPY(debug_ospf_gr, debug_ospf_gr_cmd, "[no$no] debug ospf graceful-restart", - NO_STR DEBUG_STR OSPF_STR "OSPF Graceful Restart\n") +DEFPY (debug_ospf_gr, + debug_ospf_gr_cmd, + "[no$no] debug ospf [(1-65535)$instance] graceful-restart", + NO_STR + DEBUG_STR + OSPF_STR + "Instance ID\n" + "OSPF Graceful Restart\n") { - if (vty->node == CONFIG_NODE) - CONF_DEBUG_ON(gr, GR); + if (instance && instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; - if (!no) - TERM_DEBUG_ON(gr, GR); - else + if (vty->node == CONFIG_NODE) { + if (no) + CONF_DEBUG_OFF(gr, GR); + else + CONF_DEBUG_ON(gr, GR); + } + + if (no) TERM_DEBUG_OFF(gr, GR); + else + TERM_DEBUG_ON(gr, GR); return CMD_SUCCESS; } -DEFPY(debug_ospf_bfd, debug_ospf_bfd_cmd, - "[no] debug ospf bfd", - NO_STR - DEBUG_STR - OSPF_STR - "Bidirection Forwarding Detection\n") +DEFPY (debug_ospf_bfd, + debug_ospf_bfd_cmd, + "[no] debug ospf [(1-65535)$instance] bfd", + NO_STR + DEBUG_STR + OSPF_STR + "Instance ID\n" + "Bidirection Forwarding Detection\n") { + if (instance && instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + if (vty->node == CONFIG_NODE) { if (no) { bfd_protocol_integration_set_debug(false); - CONF_DEBUG_OFF(bfd, BFD_LIB); + DEBUG_OFF(bfd, BFD_LIB); } else { bfd_protocol_integration_set_debug(true); - CONF_DEBUG_ON(bfd, BFD_LIB); + DEBUG_ON(bfd, BFD_LIB); } + } else { + if (no) + TERM_DEBUG_OFF(bfd, BFD_LIB); + else + TERM_DEBUG_ON(bfd, BFD_LIB); } - if (no) - TERM_DEBUG_OFF(bfd, BFD_LIB); - else - TERM_DEBUG_ON(bfd, BFD_LIB); - return CMD_SUCCESS; } -DEFUN(debug_ospf_client_api, - debug_ospf_client_api_cmd, - "debug ospf client-api", - DEBUG_STR OSPF_STR - "OSPF client API information\n") +DEFPY (debug_ospf_client_api, + debug_ospf_client_api_cmd, + "[no$no] debug ospf [(1-65535)$instance] client-api", + NO_STR + DEBUG_STR + OSPF_STR + "Instance ID\n" + "OSPF client API information\n") { - if (vty->node == CONFIG_NODE) - CONF_DEBUG_ON(client_api, CLIENT_API); - TERM_DEBUG_ON(client_api, CLIENT_API); + if (instance && instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + if (vty->node == CONFIG_NODE) { + if (no) + DEBUG_OFF(client_api, CLIENT_API); + else + DEBUG_ON(client_api, CLIENT_API); + } else { + if (no) + TERM_DEBUG_OFF(client_api, CLIENT_API); + else + TERM_DEBUG_ON(client_api, CLIENT_API); + } + return CMD_SUCCESS; } -DEFUN(no_debug_ospf_client_api, - no_debug_ospf_client_api_cmd, - "no debug ospf client-api", - NO_STR - DEBUG_STR - OSPF_STR - "OSPF client API information\n") +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 (vty->node == CONFIG_NODE) - CONF_DEBUG_OFF(client_api, CLIENT_API); - TERM_DEBUG_OFF(client_api, CLIENT_API); + 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; } @@ -1691,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++) @@ -1721,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; } @@ -1816,7 +1800,7 @@ static int show_debugging_ospf_common(struct vty *vty) } if (IS_DEBUG_OSPF(defaultinfo, DEFAULTINFO) == OSPF_DEBUG_DEFAULTINFO) - vty_out(vty, "OSPF default information is on\n"); + vty_out(vty, " OSPF default information is on\n"); /* Show debug status for NSSA. */ if (IS_DEBUG_OSPF(nssa, NSSA) == OSPF_DEBUG_NSSA) @@ -1850,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; } @@ -1860,7 +1850,11 @@ DEFUN_NOSH (show_debugging_ospf, DEBUG_STR OSPF_STR) { - return show_debugging_ospf_common(vty); + show_debugging_ospf_common(vty); + + cmd_show_lib_debugs(vty); + + return CMD_SUCCESS; } DEFUN_NOSH (show_debugging_ospf_instance, @@ -1878,7 +1872,11 @@ DEFUN_NOSH (show_debugging_ospf_instance, if (instance != ospf_instance) return CMD_NOT_MY_INSTANCE; - return show_debugging_ospf_common(vty); + show_debugging_ospf_common(vty); + + cmd_show_lib_debugs(vty); + + return CMD_SUCCESS; } static int config_write_debug(struct vty *vty); @@ -1978,7 +1976,7 @@ static int config_write_debug(struct vty *vty) & (OSPF_DEBUG_SEND_RECV | OSPF_DEBUG_DETAIL); if (r == (OSPF_DEBUG_SEND_RECV | OSPF_DEBUG_DETAIL)) { vty_out(vty, "debug ospf%s packet all detail\n", str); - return 1; + write = 1; } /* debug ospf packet all. */ @@ -1991,7 +1989,7 @@ static int config_write_debug(struct vty *vty) if (conf_debug_ospf_packet[i] & OSPF_DEBUG_DETAIL) vty_out(vty, "debug ospf%s packet %s detail\n", str, type_str[i]); - return 1; + write = 1; } /* debug ospf packet (hello|dd|ls-request|ls-update|ls-ack) @@ -2047,6 +2045,19 @@ static int config_write_debug(struct vty *vty) write = 1; } + /* debug ospf default-information */ + if (IS_CONF_DEBUG_OSPF(defaultinfo, DEFAULTINFO) == + OSPF_DEBUG_DEFAULTINFO) { + vty_out(vty, "debug ospf%s default-information\n", str); + 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; } @@ -2068,24 +2079,18 @@ 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); install_element(ENABLE_NODE, &no_debug_ospf_zebra_cmd); install_element(ENABLE_NODE, &no_debug_ospf_event_cmd); install_element(ENABLE_NODE, &no_debug_ospf_nssa_cmd); - install_element(ENABLE_NODE, &no_debug_ospf_te_cmd); - install_element(ENABLE_NODE, &no_debug_ospf_sr_cmd); - install_element(ENABLE_NODE, &no_debug_ospf_ti_lfa_cmd); - install_element(ENABLE_NODE, &no_debug_ospf_default_info_cmd); - install_element(ENABLE_NODE, &no_debug_ospf_ldp_sync_cmd); - install_element(ENABLE_NODE, &no_debug_ospf_client_api_cmd); install_element(ENABLE_NODE, &debug_ospf_gr_cmd); install_element(ENABLE_NODE, &debug_ospf_bfd_cmd); install_element(ENABLE_NODE, &show_debugging_ospf_instance_cmd); install_element(ENABLE_NODE, &debug_ospf_packet_cmd); - install_element(ENABLE_NODE, &no_debug_ospf_packet_cmd); install_element(ENABLE_NODE, &debug_ospf_instance_nsm_cmd); install_element(ENABLE_NODE, &debug_ospf_instance_lsa_cmd); @@ -2100,7 +2105,6 @@ void ospf_debug_init(void) install_element(ENABLE_NODE, &no_debug_ospf_cmd); install_element(CONFIG_NODE, &debug_ospf_packet_cmd); - install_element(CONFIG_NODE, &no_debug_ospf_packet_cmd); install_element(CONFIG_NODE, &debug_ospf_ism_cmd); install_element(CONFIG_NODE, &no_debug_ospf_ism_cmd); @@ -2115,17 +2119,12 @@ 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); install_element(CONFIG_NODE, &no_debug_ospf_event_cmd); install_element(CONFIG_NODE, &no_debug_ospf_nssa_cmd); - install_element(CONFIG_NODE, &no_debug_ospf_te_cmd); - install_element(CONFIG_NODE, &no_debug_ospf_sr_cmd); - install_element(CONFIG_NODE, &no_debug_ospf_ti_lfa_cmd); - install_element(CONFIG_NODE, &no_debug_ospf_default_info_cmd); - install_element(CONFIG_NODE, &no_debug_ospf_ldp_sync_cmd); - install_element(CONFIG_NODE, &no_debug_ospf_client_api_cmd); install_element(CONFIG_NODE, &debug_ospf_gr_cmd); install_element(CONFIG_NODE, &debug_ospf_bfd_cmd); diff --git a/ospfd/ospf_dump.h b/ospfd/ospf_dump.h index 251be7c8d..e9ba8fc79 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 646d31836..a0b14e73e 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 0df0072f6..c67181cba 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 97c15d1e3..a2a2393c9 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 f4fb858a5..3c65ac388 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 283844389..5577a291b 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 9bd0a844a..3d2133b11 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 000000000..eed948b19 --- /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 000000000..d0a6f6e79 --- /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 6360d8ec6..26f593f08 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 fa9478fce..e7e2b651c 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 4edc1de81..74213d7de 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 834bfd0bb..2578051c2 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 c957c8c01..4f0fa6194 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 1754512b5..461586424 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 e0c36d86f..3f82d8692 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 3a43010f8..c7735136b 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 4f9cbc7b1..78688fac9 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/pathd/path_cli.c b/pathd/path_cli.c index 4775aa36f..13e52ac86 100644 --- a/pathd/path_cli.c +++ b/pathd/path_cli.c @@ -1092,6 +1092,8 @@ DEFPY_NOSH(show_debugging_pathd, show_debugging_pathd_cmd, "State of each debugging option\n" "pathd module debugging\n") { + + cmd_show_lib_debugs(vty); /* nothing to do here */ return CMD_SUCCESS; } diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index a2b3431b9..6f53adb33 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -1243,6 +1243,8 @@ DEFUN_NOSH(show_debugging_pbr, pbr_debug_config_write_helper(vty, false); + cmd_show_lib_debugs(vty); + return CMD_SUCCESS; } diff --git a/pimd/pim6_cmd.c b/pimd/pim6_cmd.c index 49248798b..f6b370cee 100644 --- a/pimd/pim6_cmd.c +++ b/pimd/pim6_cmd.c @@ -1558,6 +1558,8 @@ DEFUN_NOSH (show_debugging_pimv6, pim_debug_config_write(vty); + cmd_show_lib_debugs(vty); + return CMD_SUCCESS; } diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index f3830eaf1..ab0689a15 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -963,7 +963,7 @@ static void pim_show_bsm_db(struct pim_instance *pim, struct vty *vty, bool uj) bsm_rpinfo = (struct bsmmsg_rpinfo *)buf; /* unaligned, again */ - memcpy(&rp_addr, &bsm_rpinfo->rpaddr, + memcpy(&rp_addr, &bsm_rpinfo->rpaddr.addr, sizeof(rp_addr)); buf += sizeof(struct bsmmsg_rpinfo); @@ -4906,6 +4906,7 @@ DEFUN_NOSH (show_debugging_pim, pim_debug_config_write(vty); + cmd_show_lib_debugs(vty); return CMD_SUCCESS; } diff --git a/ripd/rip_debug.c b/ripd/rip_debug.c index 871ee8e87..ded62812a 100644 --- a/ripd/rip_debug.c +++ b/ripd/rip_debug.c @@ -55,6 +55,8 @@ DEFUN_NOSH (show_debugging_rip, if (IS_RIP_DEBUG_ZEBRA) vty_out(vty, " RIP zebra debugging is on\n"); + cmd_show_lib_debugs(vty); + return CMD_SUCCESS; } diff --git a/ripd/ripd.c b/ripd/ripd.c index 8a321d9a9..8e02f1a6c 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/ripng_debug.c b/ripngd/ripng_debug.c index 539c01b3e..d36327cb7 100644 --- a/ripngd/ripng_debug.c +++ b/ripngd/ripng_debug.c @@ -56,6 +56,8 @@ DEFUN_NOSH (show_debugging_ripng, if (IS_RIPNG_DEBUG_ZEBRA) vty_out(vty, " RIPng zebra debugging is on\n"); + cmd_show_lib_debugs(vty); + return CMD_SUCCESS; } diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c index 1e7a13d7d..755debd0a 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/sharpd/sharp_vty.c b/sharpd/sharp_vty.c index 3853df7cb..9b900fccd 100644 --- a/sharpd/sharp_vty.c +++ b/sharpd/sharp_vty.c @@ -615,6 +615,8 @@ DEFUN_NOSH (show_debugging_sharpd, { vty_out(vty, "Sharp debugging status:\n"); + cmd_show_lib_debugs(vty); + return CMD_SUCCESS; } diff --git a/snapcraft/snapcraft.yaml.in b/snapcraft/snapcraft.yaml.in index 9729be7b9..f634b59c7 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 7badd5004..79686158c 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/staticd/static_vty.c b/staticd/static_vty.c index c0ace0e25..c0638f4bc 100644 --- a/staticd/static_vty.c +++ b/staticd/static_vty.c @@ -1301,6 +1301,8 @@ DEFUN_NOSH (show_debugging_static, static_debug_status_write(vty); + cmd_show_lib_debugs(vty); + return CMD_SUCCESS; } diff --git a/tests/lib/test_typelist.h b/tests/lib/test_typelist.h index e3579c67a..feb42909a 100644 --- a/tests/lib/test_typelist.h +++ b/tests/lib/test_typelist.h @@ -74,7 +74,7 @@ static uint32_t list_hash(const struct item *a) { #ifdef SHITTY_HASH /* crappy hash to get some hash collisions */ - return a->val ^ (a->val << 29) ^ 0x55AA0000U; + return (a->val & 0xFF) ^ (a->val << 29) ^ 0x55AA0000U; #else return jhash_1word(a->val, 0xdeadbeef); #endif diff --git a/tests/topotests/bgp_accept_own/__init__.py b/tests/topotests/bgp_accept_own/__init__.py new file mode 100644 index 000000000..e69de29bb --- /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 000000000..fa53a4291 --- /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 000000000..7863ae165 --- /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 000000000..cdf8898c9 --- /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 000000000..829967e36 --- /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 000000000..863129373 --- /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 000000000..7c1ea33a3 --- /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 000000000..1a5e1a06b --- /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 000000000..71476d2ae --- /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 000000000..4f0a6ab0f --- /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 000000000..036990186 --- /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 000000000..b598246ef --- /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 000000000..aa3f633bf --- /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 000000000..161530b48 --- /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_basic_functionality_topo1/test_bgp_basic_functionality.py b/tests/topotests/bgp_basic_functionality_topo1/test_bgp_basic_functionality.py index b18e32f6b..42b9b8adb 100644 --- a/tests/topotests/bgp_basic_functionality_topo1/test_bgp_basic_functionality.py +++ b/tests/topotests/bgp_basic_functionality_topo1/test_bgp_basic_functionality.py @@ -121,7 +121,7 @@ def setup_module(mod): # Required linux kernel version for this suite to run. result = required_linux_kernel_version("4.15") if result is not True: - pytest.skip("Kernel requirements are not met") + pytest.skip("Kernel requirements are not met, kernel version should be >=4.15") testsuite_run_time = time.asctime(time.localtime(time.time())) logger.info("Testsuite start time: {}".format(testsuite_run_time)) @@ -1148,9 +1148,9 @@ def test_bgp_with_loopback_with_same_subnet_p1(request): tgen, addr_type, dut, input_dict_r1, expected=False ) # pylint: disable=E1123 assert result is not True, ( - "Testcase {} : Failed \n".format(tc_name) - + "Expected behavior: routes should not present in fib \n" - + "Error: {}".format(result) + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) step("Verify Ipv4 and Ipv6 network installed in r3 RIB but not in FIB") @@ -1169,9 +1169,9 @@ def test_bgp_with_loopback_with_same_subnet_p1(request): tgen, addr_type, dut, input_dict_r1, expected=False ) # pylint: disable=E1123 assert result is not True, ( - "Testcase {} : Failed \n".format(tc_name) - + "Expected behavior: routes should not present in fib \n" - + "Error: {}".format(result) + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) write_test_footer(tc_name) diff --git a/tests/topotests/bgp_communities_topo1/test_bgp_communities.py b/tests/topotests/bgp_communities_topo1/test_bgp_communities.py index e4c25ff5c..1c1a5cdfe 100644 --- a/tests/topotests/bgp_communities_topo1/test_bgp_communities.py +++ b/tests/topotests/bgp_communities_topo1/test_bgp_communities.py @@ -83,7 +83,7 @@ def setup_module(mod): # Required linux kernel version for this suite to run. result = required_linux_kernel_version("4.15") if result is not True: - pytest.skip("Kernel requirements are not met") + pytest.skip("Kernel requirements are not met, kernel version should be >= 4.15") testsuite_run_time = time.asctime(time.localtime(time.time())) logger.info("Testsuite start time: {}".format(testsuite_run_time)) @@ -317,17 +317,18 @@ def test_bgp_no_advertise_community_p0(request): ) result = verify_bgp_rib(tgen, addr_type, dut, input_dict, expected=False) - assert result is not True, "Testcase {} : Failed \n ".format( - tc_name - ) + " Routes still present in R3 router. Error: {}".format(result) + assert result is not True, ( + "Testcase {} : Failed \n Expected: " + "Routes still present in {} router. Found: {}".format(tc_name, dut, result) + ) result = verify_rib( tgen, addr_type, dut, input_dict, protocol=protocol, expected=False ) assert ( result is not True - ), "Testcase {} : Failed \n Routes still present in R3 router. Error: {}".format( - tc_name, result + ), "Testcase {} : Failed \n Expected: Routes still present in {} router. Found: {}".format( + tc_name, dut, result ) step("Remove and Add no advertise community") diff --git a/tests/topotests/bgp_communities_topo1/test_bgp_communities_topo2.py b/tests/topotests/bgp_communities_topo1/test_bgp_communities_topo2.py index f6ee9ea79..a93061740 100644 --- a/tests/topotests/bgp_communities_topo1/test_bgp_communities_topo2.py +++ b/tests/topotests/bgp_communities_topo1/test_bgp_communities_topo2.py @@ -87,7 +87,7 @@ def setup_module(mod): # Required linux kernel version for this suite to run. result = required_linux_kernel_version("4.14") if result is not True: - pytest.skip("Kernel requirements are not met") + pytest.skip("Kernel requirements are not met, kernel version should be >= 4.14") testsuite_run_time = time.asctime(time.localtime(time.time())) logger.info("Testsuite start time: {}".format(testsuite_run_time)) @@ -305,8 +305,10 @@ def test_bgp_no_export_local_as_and_internet_communities_p0(request): ], expected=False, ) - assert result is not True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes are still present in rib of r3 \n " + "Found: {}".format(tc_name, result) ) step("Remove route-map from redistribute static on R1") diff --git a/tests/topotests/bgp_ecmp_topo2/test_ebgp_ecmp_topo2.py b/tests/topotests/bgp_ecmp_topo2/test_ebgp_ecmp_topo2.py index 2784e956f..8fd0120dd 100644 --- a/tests/topotests/bgp_ecmp_topo2/test_ebgp_ecmp_topo2.py +++ b/tests/topotests/bgp_ecmp_topo2/test_ebgp_ecmp_topo2.py @@ -90,7 +90,7 @@ def setup_module(mod): # Required linux kernel version for this suite to run. result = required_linux_kernel_version("4.15") if result is not True: - pytest.skip("Kernel requirements are not met") + pytest.skip("Kernel requirements are not met, kernel version should be >=4.15") testsuite_run_time = time.asctime(time.localtime(time.time())) logger.info("Testsuite start time: {}".format(testsuite_run_time)) @@ -436,7 +436,9 @@ def test_ecmp_remove_redistribute_static(request): ) assert ( result is not True - ), "Testcase {} : Failed \n Routes still" " present in RIB".format(tc_name) + ), "Testcase {} : Failed \n Expected: Routes still present in {} RIB. Found: {}".format( + tc_name, dut, result + ) logger.info("Enable redistribute static") input_dict_2 = { @@ -621,7 +623,9 @@ def test_ecmp_remove_static_route(request): ) assert ( result is not True - ), "Testcase {} : Failed \n Routes still" " present in RIB".format(tc_name) + ), "Testcase {} : Failed \n Expected: Routes still present in {} RIB. Found: {}".format( + tc_name, dut, result + ) for addr_type in ADDR_TYPES: # Enable static routes @@ -727,7 +731,9 @@ def test_ecmp_remove_nw_advertise(request): ) assert ( result is not True - ), "Testcase {} : Failed \n Routes still" " present in RIB".format(tc_name) + ), "Testcase {} : Failed \n Expected: Routes still present in {} RIB. Found: {}".format( + tc_name, dut, result + ) static_or_nw(tgen, topo, tc_name, "advertise_nw", "r2") for addr_type in ADDR_TYPES: diff --git a/tests/topotests/bgp_ecmp_topo2/test_ibgp_ecmp_topo2.py b/tests/topotests/bgp_ecmp_topo2/test_ibgp_ecmp_topo2.py index 704e8fdf0..185a08683 100644 --- a/tests/topotests/bgp_ecmp_topo2/test_ibgp_ecmp_topo2.py +++ b/tests/topotests/bgp_ecmp_topo2/test_ibgp_ecmp_topo2.py @@ -90,7 +90,7 @@ def setup_module(mod): # Required linux kernel version for this suite to run. result = required_linux_kernel_version("4.15") if result is not True: - pytest.skip("Kernel requirements are not met") + pytest.skip("Kernel requirements are not met, kernel version should be >=4.15") testsuite_run_time = time.asctime(time.localtime(time.time())) logger.info("Testsuite start time: {}".format(testsuite_run_time)) @@ -437,7 +437,9 @@ def test_ecmp_remove_redistribute_static(request): ) assert ( result is not True - ), "Testcase {} : Failed \n Routes still" " present in RIB".format(tc_name) + ), "Testcase {} : Failed \n Expected: Routes still present in {} RIB. Found: {}".format( + tc_name, dut, result + ) logger.info("Enable redistribute static") input_dict_2 = { @@ -622,7 +624,9 @@ def test_ecmp_remove_static_route(request): ) assert ( result is not True - ), "Testcase {} : Failed \n Routes still" " present in RIB".format(tc_name) + ), "Testcase {} : Failed \n Expected: Routes still present in {} RIB. Found: {}".format( + tc_name, dut, result + ) for addr_type in ADDR_TYPES: # Enable static routes @@ -730,7 +734,9 @@ def test_ecmp_remove_nw_advertise(request): ) assert ( result is not True - ), "Testcase {} : Failed \n Routes still" " present in RIB".format(tc_name) + ), "Testcase {} : Failed \n Expected: Routes still present in {} RIB. Found: {}".format( + tc_name, dut, result + ) static_or_nw(tgen, topo, tc_name, "advertise_nw", "r2") for addr_type in ADDR_TYPES: diff --git a/tests/topotests/bgp_ecmp_topo3/test_ibgp_ecmp_topo3.py b/tests/topotests/bgp_ecmp_topo3/test_ibgp_ecmp_topo3.py index 2a51dc83e..9b6480c0d 100644 --- a/tests/topotests/bgp_ecmp_topo3/test_ibgp_ecmp_topo3.py +++ b/tests/topotests/bgp_ecmp_topo3/test_ibgp_ecmp_topo3.py @@ -240,17 +240,18 @@ def test_ecmp_fast_convergence(request, test_type, tgen, topo): logger.info("Ensure BGP has processed the cli") r2 = tgen.gears["r2"] output = r2.vtysh_cmd("show run") - verify = re.search(r"fast-convergence", output ) - assert verify is not None, ( - "r2 does not have the fast convergence command yet") + verify = re.search(r"fast-convergence", output) + assert verify is not None, "r2 does not have the fast convergence command yet" logger.info("Shutdown one link b/w r2 and r3") shutdown_bringup_interface(tgen, "r2", intf1, False) logger.info("Verify bgp neighbors goes down immediately") result = verify_bgp_convergence(tgen, topo, dut="r2", expected=False) - assert result is not True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: BGP should not be converged for {} \n " + "Found: {}".format(tc_name, "r2", result) ) logger.info("Shutdown second link b/w r2 and r3") @@ -258,8 +259,10 @@ def test_ecmp_fast_convergence(request, test_type, tgen, topo): logger.info("Verify bgp neighbors goes down immediately") result = verify_bgp_convergence(tgen, topo, dut="r2", expected=False) - assert result is not True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: BGP should not be converged for {} \n " + "Found: {}".format(tc_name, "r2", result) ) write_test_footer(tc_name) diff --git a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-1.py b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-1.py index f15532550..deca4bcfd 100644 --- a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-1.py +++ b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-1.py @@ -160,7 +160,7 @@ def setup_module(mod): # Required linux kernel version for this suite to run. result = required_linux_kernel_version("4.16") if result is not True: - pytest.skip("Kernel requirements are not met") + pytest.skip("Kernel requirements are not met, kernel version should be >=4.16") testsuite_run_time = time.asctime(time.localtime(time.time())) logger.info("Testsuite start time: {}".format(testsuite_run_time)) @@ -1033,11 +1033,9 @@ def test_BGP_GR_TC_4_p0(request): ) assert result is not True, ( "Testcase {} : Failed \n " - "r1: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - ) + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info(" Expected behavior: {}".format(result)) # Verifying RIB routes result = verify_rib( @@ -1045,9 +1043,9 @@ def test_BGP_GR_TC_4_p0(request): ) assert result is not True, ( "Testcase {} : Failed \n " - "r1: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info(" Expected behavior: {}".format(result)) logger.info("[Phase 5] : R2 is about to come up now ") start_router_daemons(tgen, "r2", ["bgpd"]) @@ -1506,10 +1504,10 @@ def test_BGP_GR_TC_6_1_2_p1(request): result = verify_r_bit( tgen, topo, addr_type, input_dict, dut="r2", peer="r1", expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed \n " "r2: R-bit is set to True\n Error: {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: R-bit should not be set to True in r2\n" + "Found: {}".format(tc_name, result) ) logger.info("Restart BGPd on R2 ") @@ -1528,10 +1526,10 @@ def test_BGP_GR_TC_6_1_2_p1(request): result = verify_r_bit( tgen, topo, addr_type, input_dict, dut="r2", peer="r1", expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed \n " "r2: R-bit is set to True\n Error: {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: R-bit should not be set to True in r2\n" + "Found: {}".format(tc_name, result) ) write_test_footer(tc_name) diff --git a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-2.py b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-2.py index dda3bd4b3..a92fde2d0 100644 --- a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-2.py +++ b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-2.py @@ -160,7 +160,7 @@ def setup_module(mod): # Required linux kernel version for this suite to run. result = required_linux_kernel_version("4.16") if result is not True: - pytest.skip("Kernel requirements are not met") + pytest.skip("Kernel requirements are not met, kernel version should be >=4.16") testsuite_run_time = time.asctime(time.localtime(time.time())) logger.info("Testsuite start time: {}".format(testsuite_run_time)) diff --git a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-3.py b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-3.py index e4b533b29..b0166033d 100644 --- a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-3.py +++ b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-3.py @@ -160,7 +160,7 @@ def setup_module(mod): # Required linux kernel version for this suite to run. result = required_linux_kernel_version("4.16") if result is not True: - pytest.skip("Kernel requirements are not met") + pytest.skip("Kernel requirements are not met, kernel version should be >=4.16") testsuite_run_time = time.asctime(time.localtime(time.time())) logger.info("Testsuite start time: {}".format(testsuite_run_time)) @@ -829,11 +829,9 @@ def test_BGP_GR_TC_20_p1(request): ) assert result is not True, ( "Testcase {} : Failed \n " - "r1: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - ) + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info(" Expected behavior: {}".format(result)) # Verifying RIB routes result = verify_rib( @@ -841,9 +839,9 @@ def test_BGP_GR_TC_20_p1(request): ) assert result is not True, ( "Testcase {} : Failed \n " - "r1: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info(" Expected behavior: {}".format(result)) logger.info("[Phase 5] : R2 is about to come up now ") @@ -1117,7 +1115,8 @@ def test_BGP_GR_TC_31_1_p1(request): ) assert result is not True, ( "Testcase {} : Failed \n " - "r1: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) logger.info("[Phase 4] : R1 is about to come up now ") @@ -1599,11 +1598,9 @@ def test_BGP_GR_TC_9_p1(request): ) assert result is not True, ( "Testcase {} : Failed \n " - "r1: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - ) + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info(" Expected behavior: {}".format(result)) # Verifying RIB routes protocol = "bgp" @@ -1612,9 +1609,9 @@ def test_BGP_GR_TC_9_p1(request): ) assert result is not True, ( "Testcase {} : Failed \n " - "r1: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info(" Expected behavior: {}".format(result)) logger.info("[Phase 5] : R2 is about to come up now ") start_router_daemons(tgen, "r2", ["bgpd"]) @@ -1643,10 +1640,10 @@ def test_BGP_GR_TC_9_p1(request): result = verify_f_bit( tgen, topo, addr_type, input_dict, dut="r1", peer="r2", expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed \n " "r1: F-bit is set to True\n Error: {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: F-bit should not be set to True in r1\n" + "Found: {}".format(tc_name, result) ) write_test_footer(tc_name) @@ -1781,11 +1778,9 @@ def test_BGP_GR_TC_17_p1(request): ) assert result is not True, ( "Testcase {} : Failed \n " - "r1: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - ) + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info(" Expected behavior: {}".format(result)) # Verifying RIB routes protocol = "bgp" @@ -1794,9 +1789,9 @@ def test_BGP_GR_TC_17_p1(request): ) assert result is not True, ( "Testcase {} : Failed \n " - "r1: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info(" Expected behavior: {}".format(result)) logger.info("[Phase 5] : R2 is about to come up now ") start_router_daemons(tgen, "r2", ["bgpd"]) @@ -1817,10 +1812,10 @@ def test_BGP_GR_TC_17_p1(request): result = verify_r_bit( tgen, topo, addr_type, input_dict, dut="r1", peer="r2", expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed \n " "r1: R-bit is set to True\n Error: {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: R-bit should not be set to True in r1\n" + "Found: {}".format(tc_name, result) ) # Verifying BGP RIB routes @@ -2026,10 +2021,10 @@ def test_BGP_GR_TC_43_p1(request): result = verify_rib( tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} :Failed \n Routes are still present \n Error {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) dut = "r2" @@ -2043,18 +2038,18 @@ def test_BGP_GR_TC_43_p1(request): ) assert result is not True, ( "Testcase {} : Failed \n " - "r2: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - ) + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) ) + protocol = "bgp" result = verify_rib( tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} :Failed \n Routes are still present \n Error {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) step( @@ -2353,17 +2348,17 @@ def test_BGP_GR_TC_44_p1(request): ) assert result is not True, ( "Testcase {} : Failed \n " - "r1: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - ) + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) ) + result = verify_rib( tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} :Failed \n Routes are still present \n Error {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) step("Bring up BGPd on R2 and remove GR related config from R1 in global level") diff --git a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-4.py b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-4.py index 835ef41de..f408ff485 100644 --- a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-4.py +++ b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-4.py @@ -160,7 +160,7 @@ def setup_module(mod): # Required linux kernel version for this suite to run. result = required_linux_kernel_version("4.16") if result is not True: - pytest.skip("Kernel requirements are not met") + pytest.skip("Kernel requirements are not met, kernel version should be >=4.16") testsuite_run_time = time.asctime(time.localtime(time.time())) logger.info("Testsuite start time: {}".format(testsuite_run_time)) @@ -1157,7 +1157,8 @@ def test_BGP_GR_TC_48_p1(request): ) assert result is not True, ( "Testcase {} : Failed \n " - "r1: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) dut = "r2" @@ -1171,16 +1172,17 @@ def test_BGP_GR_TC_48_p1(request): ) assert result is not True, ( "Testcase {} : Failed \n " - "r2: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - ) + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) ) + result = verify_rib( tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False ) assert result is not True, ( "Testcase {} : Failed \n " - "r2: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) step("Bring up BGP on R1 and remove Peer-level GR config from R1") @@ -1542,16 +1544,17 @@ def BGP_GR_TC_52_p1(request): ) assert result is not True, ( "Testcase {} : Failed \n " - "r1: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - ) + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) ) + result = verify_rib( tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False ) assert result is not True, ( "Testcase {} : Failed \n " - "r1: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) step("Bring up BGP on R2 and remove Peer-level GR config from R1") diff --git a/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-1.py b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-1.py index 3afe38857..293f3042e 100644 --- a/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-1.py +++ b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-1.py @@ -155,7 +155,7 @@ def setup_module(mod): # Required linux kernel version for this suite to run. result = required_linux_kernel_version("4.16") if result is not True: - pytest.skip("Kernel requirements are not met") + pytest.skip("Kernel requirements are not met, kernel version should be >=4.16") global ADDR_TYPES @@ -528,10 +528,10 @@ def test_BGP_GR_TC_3_p0(request): result = verify_eor( tgen, topo, addr_type, input_dict, dut="r2", peer="r1", expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed \n " "r2: EOR is set to True\n Error: {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: EOR should not be set to True in r2\n" + "Found: {}".format(tc_name, result) ) logger.info( @@ -675,10 +675,10 @@ def test_BGP_GR_TC_11_p0(request): result = verify_eor( tgen, topo, addr_type, input_dict, dut="r1", peer="r3", expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed \n " "r1: EOR is set to True\n Error: {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: EOR should not be set to True in r1\n" + "Found: {}".format(tc_name, result) ) logger.info( @@ -706,10 +706,10 @@ def test_BGP_GR_TC_11_p0(request): result = verify_eor( tgen, topo, addr_type, input_dict, dut="r3", peer="r1", expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed \n " "r3: EOR is set to True\n Error: {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: EOR should not be set to True in r3\n" + "Found: {}".format(tc_name, result) ) write_test_footer(tc_name) @@ -1474,38 +1474,33 @@ def test_BGP_GR_18_p1(request): result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1, expected=False) assert result is not True, ( "Testcase {} : Failed \n " - "r6: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - ) + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info(" Expected behavior: {}".format(result)) # Verifying RIB routes before shutting down BGPd daemon result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False) assert result is not True, ( "Testcase {} : Failed \n " - "r6: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info(" Expected behavior: {}".format(result)) # Verifying BGP RIB routes dut = "r2" - result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1, expected=False) assert result is not True, ( "Testcase {} : Failed \n " - "r2: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - ) + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info(" Expected behavior: {}".format(result)) # Verifying RIB routes before shutting down BGPd daemon result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False) assert result is not True, ( "Testcase {} : Failed \n " - "r6: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info(" Expected behavior: {}".format(result)) write_test_footer(tc_name) diff --git a/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-2.py b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-2.py index 535f272ef..fe885372d 100644 --- a/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-2.py +++ b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-2.py @@ -155,7 +155,7 @@ def setup_module(mod): # Required linux kernel version for this suite to run. result = required_linux_kernel_version("4.16") if result is not True: - pytest.skip("Kernel requirements are not met") + pytest.skip("Kernel requirements are not met, kernel version should be >=4.16") global ADDR_TYPES @@ -726,19 +726,17 @@ def test_BGP_GR_chaos_29_p1(request): result = verify_bgp_rib(tgen, addr_type, dut, input_dict, expected=False) assert result is not True, ( "Testcase {} : Failed \n " - "r3: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - ) + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info(" Expected behavior: {}".format(result)) # Verifying RIB routes before shutting down BGPd daemon result = verify_rib(tgen, addr_type, dut, input_dict, expected=False) assert result is not True, ( "Testcase {} : Failed \n " - "r3: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info(" Expected behavior: {}".format(result)) logger.info("[Step 4] : Start BGPd daemon on R1..") @@ -981,11 +979,9 @@ def test_BGP_GR_chaos_33_p1(request): ) assert result is not True, ( "Testcase {} : Failed \n " - "r3: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - ) + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info(" Expected behavior: {}".format(result)) if addr_type == "ipv6": if "link_local" in PREFERRED_NEXT_HOP: @@ -998,11 +994,9 @@ def test_BGP_GR_chaos_33_p1(request): ) assert result is not True, ( "Testcase {} : Failed \n " - "r3: routes are still present in ZEBRA\n Error: {}".format( - tc_name, result - ) + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info(" Expected behavior: {}".format(result)) logger.info("[Step 4] : Start BGPd daemon on R1 and R4..") @@ -1182,31 +1176,28 @@ def test_BGP_GR_chaos_34_2_p1(request): result = verify_f_bit( tgen, topo, addr_type, input_dict, "r3", "r1", expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed \n " "r3: F-bit is set to True\n Error: {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: F-bit should not be set to True in r3\n" + "Found: {}".format(tc_name, result) ) - logger.info(" Expected behavior: {}".format(result)) # Verifying BGP RIB routes after starting BGPd daemon input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1, expected=False) assert result is not True, ( "Testcase {} : Failed \n " - "r3: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - ) + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info(" Expected behavior: {}".format(result)) # Verifying RIB routes result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False) assert result is not True, ( "Testcase {} : Failed \n " - "r3: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info(" Expected behavior: {}".format(result)) write_test_footer(tc_name) diff --git a/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-3.py b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-3.py index e60552ed1..4ff62a978 100644 --- a/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-3.py +++ b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-3.py @@ -155,7 +155,7 @@ def setup_module(mod): # Required linux kernel version for this suite to run. result = required_linux_kernel_version("4.16") if result is not True: - pytest.skip("Kernel requirements are not met") + pytest.skip("Kernel requirements are not met, kernel version should be >=4.16") global ADDR_TYPES @@ -380,12 +380,11 @@ def test_BGP_GR_chaos_34_1_p1(request): result = verify_f_bit( tgen, topo, addr_type, input_dict_2, "r3", "r1", expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed \n " "r3: F-bit is set to True\n Error: {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: F-bit should not be set to True in r3\n" + "Found: {}".format(tc_name, result) ) - logger.info(" Expected behavior: {}".format(result)) logger.info("[Step 3] : Kill BGPd daemon on R1..") @@ -402,19 +401,17 @@ def test_BGP_GR_chaos_34_1_p1(request): result = verify_bgp_rib(tgen, addr_type, dut, input_dict, expected=False) assert result is not True, ( "Testcase {} : Failed \n " - "r3: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - ) + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info(" Expected behavior: {}".format(result)) # Verifying RIB routes result = verify_rib(tgen, addr_type, dut, input_dict, expected=False) assert result is not True, ( "Testcase {} : Failed \n " - "r3: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info(" Expected behavior: {}".format(result)) # Start BGPd daemon on R1 start_router_daemons(tgen, "r1", ["bgpd"]) @@ -587,31 +584,28 @@ def test_BGP_GR_chaos_32_p1(request): result = verify_eor( tgen, topo, addr_type, input_dict_3, dut="r5", peer="r1", expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed \n " "r5: EOR is set to TRUE\n Error: {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: EOR should not be set to True in r5\n" + "Found: {}".format(tc_name, result) ) - logger.info(" Expected behavior: {}".format(result)) # Verifying BGP RIB routes after starting BGPd daemon input_dict_1 = {key: topo["routers"][key] for key in ["r5"]} result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1, expected=False) assert result is not True, ( "Testcase {} : Failed \n " - "r3: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - ) + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info(" Expected behavior: {}".format(result)) # Verifying RIB routes result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False) assert result is not True, ( "Testcase {} : Failed \n " - "r3: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info(" Expected behavior: {}".format(result)) write_test_footer(tc_name) @@ -716,12 +710,11 @@ def test_BGP_GR_chaos_37_p1(request): result = verify_eor( tgen, topo, addr_type, input_dict, dut="r3", peer="r1", expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed \n " "r3: EOR is set to True\n Error: {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: EOR should not be set to True in r3\n" + "Found: {}".format(tc_name, result) ) - logger.info(" Expected behavior: {}".format(result)) # Verifying BGP RIB routes after starting BGPd daemon dut = "r1" @@ -783,10 +776,10 @@ def test_BGP_GR_chaos_37_p1(request): result = verify_eor( tgen, topo, addr_type, input_dict_3, dut="r1", peer="r3", expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed \n " "r1: EOR is set to True\n Error: {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: EOR should not be set to True in r1\n" + "Found: {}".format(tc_name, result) ) write_test_footer(tc_name) @@ -941,19 +934,17 @@ def test_BGP_GR_chaos_30_p1(request): result = verify_bgp_rib(tgen, addr_type, dut, input_dict, expected=False) assert result is not True, ( "Testcase {} : Failed \n " - "r1: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - ) + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info(" Expected behavior: {}".format(result)) # Verifying RIB routes before shutting down BGPd daemon result = verify_rib(tgen, addr_type, dut, input_dict, expected=False) assert result is not True, ( "Testcase {} : Failed \n " - "r1: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info(" Expected behavior: {}".format(result)) write_test_footer(tc_name) @@ -1356,7 +1347,8 @@ def BGP_GR_TC_7_p1(request): result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False) assert result is not True, ( "Testcase {} : Failed \n " - "r1: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) write_test_footer(tc_name) diff --git a/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-4.py b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-4.py index 1df77ebeb..fd1ef097c 100644 --- a/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-4.py +++ b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-4.py @@ -157,7 +157,7 @@ def setup_module(mod): # Required linux kernel version for this suite to run. result = required_linux_kernel_version("4.16") if result is not True: - pytest.skip("Kernel requirements are not met") + pytest.skip("Kernel requirements are not met, kernel version should be >=4.16") global ADDR_TYPES @@ -420,10 +420,10 @@ def test_BGP_GR_TC_23_p1(request): result = verify_eor( tgen, topo, addr_type, input_dict, dut="r1", peer="r2", expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed \n " "r1: EOR is set to True\n Error: {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: EOR should not be set to True in r2\n" + "Found: {}".format(tc_name, result) ) # Verifying BGP RIB routes received from router R1 @@ -547,19 +547,17 @@ def test_BGP_GR_20_p1(request): result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1, expected=False) assert result is not True, ( "Testcase {} : Failed \n " - "r3: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - ) + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info(" Expected behavior: {}".format(result)) # Verifying RIB routes before shutting down BGPd daemon result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False) assert result is not True, ( "Testcase {} : Failed \n " - "r3: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info(" Expected behavior: {}".format(result)) # Start BGPd daemon on R1 start_router_daemons(tgen, "r1", ["bgpd"]) diff --git a/tests/topotests/bgp_gr_functionality_topo3/bgp_gr_functionality_topo3.py b/tests/topotests/bgp_gr_functionality_topo3/bgp_gr_functionality_topo3.py index 6bf8b9630..76522206a 100644 --- a/tests/topotests/bgp_gr_functionality_topo3/bgp_gr_functionality_topo3.py +++ b/tests/topotests/bgp_gr_functionality_topo3/bgp_gr_functionality_topo3.py @@ -56,15 +56,20 @@ from lib.bgp import ( verify_graceful_restart_timers, verify_bgp_convergence_from_running_config, ) + # Import common_config to use commomnly used APIs -from lib.common_config import (create_common_configuration, - InvalidCLIError, retry, - generate_ips, FRRCFG_FILE, - find_interface_with_greater_ip, - check_address_types, - validate_ip_address, - run_frr_cmd, - get_frr_ipv6_linklocal) +from lib.common_config import ( + create_common_configuration, + InvalidCLIError, + retry, + generate_ips, + FRRCFG_FILE, + find_interface_with_greater_ip, + check_address_types, + validate_ip_address, + run_frr_cmd, + get_frr_ipv6_linklocal, +) from lib.common_config import ( write_test_header, @@ -108,8 +113,7 @@ NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} PREFERRED_NEXT_HOP = "link_local" -def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, - dut, peer): +def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut, peer): """ result = configure_gr_followed_by_clear(tgen, topo, dut) assert result is True, \ @@ -118,8 +122,7 @@ def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, """ result = create_router_bgp(tgen, topo, input_dict) - assert result is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, result) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) for addr_type in ADDR_TYPES: clear_bgp(tgen, addr_type, dut) @@ -131,6 +134,7 @@ def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, return True + def verify_stale_routes_list(tgen, addr_type, dut, input_dict): """ This API is use verify Stale routes on refering the network with next hop value @@ -175,8 +179,8 @@ def verify_stale_routes_list(tgen, addr_type, dut, input_dict): command = "show bgp" # Static routes sleep(2) - logger.info('Checking router {} BGP RIB:'.format(dut)) - if 'static_routes' in input_dict[routerInput]: + logger.info("Checking router {} BGP RIB:".format(dut)) + if "static_routes" in input_dict[routerInput]: static_routes = input_dict[routerInput]["static_routes"] for static_route in static_routes: found_routes = [] @@ -185,30 +189,27 @@ def verify_stale_routes_list(tgen, addr_type, dut, input_dict): nh_found = False vrf = static_route.setdefault("vrf", None) community = static_route.setdefault("community", None) - largeCommunity = \ - static_route.setdefault("largeCommunity", None) + largeCommunity = static_route.setdefault("largeCommunity", None) if vrf: - cmd = "{} vrf {} {}".\ - format(command, vrf, addr_type) + cmd = "{} vrf {} {}".format(command, vrf, addr_type) if community: - cmd = "{} community {}".\ - format(cmd, community) + cmd = "{} community {}".format(cmd, community) if largeCommunity: - cmd = "{} large-community {}".\ - format(cmd, largeCommunity) + cmd = "{} large-community {}".format(cmd, largeCommunity) else: - cmd = "{} {}".\ - format(command, addr_type) + cmd = "{} {}".format(command, addr_type) cmd = "{} json".format(cmd) rib_routes_json = run_frr_cmd(rnode, cmd, isjson=True) # Verifying output dictionary rib_routes_json is not empty if bool(rib_routes_json) == False: - errormsg = "[DUT: {}]: No route found in rib of router". \ - format(router) + errormsg = "[DUT: {}]: No route found in rib of router".format( + router + ) return errormsg elif "warning" in rib_routes_json: - errormsg = "[DUT: {}]: {}". \ - format(router, rib_routes_json["warning"]) + errormsg = "[DUT: {}]: {}".format( + router, rib_routes_json["warning"] + ) return errormsg network = static_route["network"] if "no_of_ip" in static_route: @@ -227,15 +228,18 @@ def verify_stale_routes_list(tgen, addr_type, dut, input_dict): st_found = True found_routes.append(st_rt) - for mnh in range(0, len(rib_routes_json[ - 'routes'][st_rt])): - found_hops.append([rib_r[ - "ip"] for rib_r in rib_routes_json[ - 'routes'][st_rt][ - mnh]["nexthops"]]) + for mnh in range(0, len(rib_routes_json["routes"][st_rt])): + found_hops.append( + [ + rib_r["ip"] + for rib_r in rib_routes_json["routes"][st_rt][ + mnh + ]["nexthops"] + ] + ) return found_hops else: - return 'error msg - no hops found' + return "error msg - no hops found" def setup_module(mod): @@ -248,7 +252,7 @@ def setup_module(mod): # Required linux kernel version for this suite to run. result = required_linux_kernel_version("4.16") if result is not True: - pytest.skip("Kernel requirements are not met") + pytest.skip("Kernel requirements are not met, kernel version should be >=4.16") global ADDR_TYPES @@ -302,6 +306,8 @@ def teardown_module(mod): "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) ) logger.info("=" * 40) + + ################################################################################ # # TEST CASES @@ -318,175 +324,160 @@ def test_bgp_gr_stale_routes(request): step("Creating 5 static Routes in Router R3 with NULL0 as Next hop") for addr_type in ADDR_TYPES: - input_dict_1 = { - "r3": { - "static_routes": [{ - "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], - "next_hop": NEXT_HOP_IP[addr_type] - }, - { - "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], - "next_hop": NEXT_HOP_IP[addr_type] - }, - { - "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], - "next_hop": NEXT_HOP_IP[addr_type] - }, - { - "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], - "next_hop": NEXT_HOP_IP[addr_type] - }, - { - "network": [NETWORK5_1[addr_type]] + [NETWORK5_2[addr_type]], - "next_hop": NEXT_HOP_IP[addr_type] - }] - } + input_dict_1 = { + "r3": { + "static_routes": [ + { + "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + { + "network": [NETWORK5_1[addr_type]] + [NETWORK5_2[addr_type]], + "next_hop": NEXT_HOP_IP[addr_type], + }, + ] } - result = create_static_routes(tgen, input_dict_1) - assert result is True, 'Testcase {} : Failed \n Error: {}'.format( - tc_name, result) + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) step("verifying Created Route at R3 in VRF default") for addr_type in ADDR_TYPES: - dut = 'r3' - input_dict_1= {'r3': topo['routers']['r3']} + dut = "r3" + input_dict_1 = {"r3": topo["routers"]["r3"]} result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) - assert result is True, \ - "Testcase {} :Failed \n Error {}". \ - format(tc_name, result) - #done + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + # done step("verifying Created Route at R2 in VRF default") for addr_type in ADDR_TYPES: - dut = 'r2' - input_dict_1= {'r2': topo['routers']['r2']} + dut = "r2" + input_dict_1 = {"r2": topo["routers"]["r2"]} result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) - assert result is True, \ - "Testcase {} :Failed \n Error {}". \ - format(tc_name, result) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) step("importing vrf RED on R2 under Address Family") for addr_type in ADDR_TYPES: - input_import_vrf={ + input_import_vrf = { "r2": { "bgp": [ { "local_as": 200, "vrf": "RED", - "address_family": {addr_type: {"unicast": {"import": {"vrf": "default"}}}}, + "address_family": { + addr_type: {"unicast": {"import": {"vrf": "default"}}} + }, } ] } } - result = create_router_bgp(tgen, topo,input_import_vrf) - assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) - #done + result = create_router_bgp(tgen, topo, input_import_vrf) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + # done step("verifying static Routes at R2 in VRF RED") for addr_type in ADDR_TYPES: - dut = 'r2' - input_dict_1= {'r2': topo['routers']['r2']} + dut = "r2" + input_dict_1 = {"r2": topo["routers"]["r2"]} result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) - assert result is True, \ - "Testcase {} :Failed \n Error {}". \ - format(tc_name, result) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) step("verifying static Routes at R1 in VRF RED") for addr_type in ADDR_TYPES: - dut = 'r1' - input_dict_1= {'r1': topo['routers']['r1']} + dut = "r1" + input_dict_1 = {"r1": topo["routers"]["r1"]} result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) - assert result is True, \ - "Testcase {} :Failed \n Error {}". \ - format(tc_name, result) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) step("Configuring Graceful restart at R2 and R3 ") input_dict = { "r2": { - "bgp": { "local_as": "200", "graceful-restart": { "graceful-restart": True, - } + }, } }, "r3": { - "bgp": { - "local_as": "300", - "graceful-restart": { - "graceful-restart": True - } - } - } + "bgp": {"local_as": "300", "graceful-restart": {"graceful-restart": True}} + }, } - configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name,dut='r2', peer='r3') - - + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r2", peer="r3") step("verify Graceful restart at R2") for addr_type in ADDR_TYPES: - result = verify_graceful_restart(tgen, topo, addr_type, input_dict, - dut='r2', peer='r3') - assert result is True, \ - "Testcase {} :Failed \n Error {}". \ - format(tc_name, result) + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r2", peer="r3" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) step("verify Graceful restart at R3") for addr_type in ADDR_TYPES: - result = verify_graceful_restart(tgen, topo, addr_type, input_dict, - dut='r3', peer='r2') - assert result is True, \ - "Testcase {} :Failed \n Error {}". \ - format(tc_name, result) + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r3", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) step("Configuring Graceful-restart-disable at R3") input_dict = { "r2": { - "bgp": { "local_as": "200", "graceful-restart": { "graceful-restart": False, - } + }, } }, "r3": { - "bgp": { - "local_as": "300", - "graceful-restart": { - "graceful-restart": False - } - } - } + "bgp": {"local_as": "300", "graceful-restart": {"graceful-restart": False}} + }, } - configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name,dut='r3', peer='r2') + configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r3", peer="r2") step("Verify Graceful-restart-disable at R3") for addr_type in ADDR_TYPES: - result = verify_graceful_restart(tgen, topo, addr_type, input_dict, - dut='r3', peer='r2') - assert result is True, \ - "Testcase {} :Failed \n Error {}". \ - format(tc_name, result) + result = verify_graceful_restart( + tgen, topo, addr_type, input_dict, dut="r3", peer="r2" + ) + assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) for iteration in range(5): step("graceful-restart-disable:True at R3") input_dict = { - "r3": { - "bgp": { - "graceful-restart": { - "graceful-restart-disable": True, + "r3": { + "bgp": { + "graceful-restart": { + "graceful-restart-disable": True, + } } } - } } - configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, - dut='r3', peer='r2') + configure_gr_followed_by_clear( + tgen, topo, input_dict, tc_name, dut="r3", peer="r2" + ) step("Verifying Routes at R2 on enabling GRD") - dut = 'r2' + dut = "r2" for addr_type in ADDR_TYPES: - input_dict_1= {'r2': topo['routers']['r2']} + input_dict_1 = {"r2": topo["routers"]["r2"]} result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) - assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + assert result is True, "Testcase {} :Failed \n Error {}".format( + tc_name, result + ) step("Verify stale Routes in Router R2 enabling GRD") for addr_type in ADDR_TYPES: @@ -495,39 +486,45 @@ def test_bgp_gr_stale_routes(request): verify_nh_for_static_rtes = { "r3": { "static_routes": [ - { "network": [NETWORK1_1[addr_type]], "no_of_ip": 2, - "vrf": "RED" + "vrf": "RED", } ] } } bgp_rib_next_hops = verify_stale_routes_list( - tgen, addr_type, dut, verify_nh_for_static_rtes) - assert (len(bgp_rib_next_hops)== 1) is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, bgp_rib_next_hops,expected=True) + tgen, addr_type, dut, verify_nh_for_static_rtes + ) + assert ( + len(bgp_rib_next_hops) == 1 + ) is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, bgp_rib_next_hops, expected=True + ) step("graceful-restart-disable:False at R3") input_dict = { - "r3": { - "bgp": { - "graceful-restart": { - "graceful-restart-disable": False, + "r3": { + "bgp": { + "graceful-restart": { + "graceful-restart-disable": False, + } } } - } } - configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, - dut='r3', peer='r2') + configure_gr_followed_by_clear( + tgen, topo, input_dict, tc_name, dut="r3", peer="r2" + ) step("Verifying Routes at R2 on disabling GRD") - dut = 'r2' + dut = "r2" for addr_type in ADDR_TYPES: - input_dict_1= {'r2': topo['routers']['r2']} + input_dict_1 = {"r2": topo["routers"]["r2"]} result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1) - assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + assert result is True, "Testcase {} :Failed \n Error {}".format( + tc_name, result + ) step("Verify stale Routes in Router R2 on disabling GRD") for addr_type in ADDR_TYPES: @@ -536,19 +533,22 @@ def test_bgp_gr_stale_routes(request): verify_nh_for_static_rtes = { "r3": { "static_routes": [ - { "network": [NETWORK1_1[addr_type]], "no_of_ip": 2, - "vrf": "RED" + "vrf": "RED", } ] } } - bgp_rib_next_hops = verify_stale_routes_list(tgen, addr_type, dut, verify_nh_for_static_rtes) - - stale_route_status=len(bgp_rib_next_hops)== 1 - assert stale_route_status is True, "Testcase {} : Failed \n Error: {}".format( - tc_name, stale_route_status,expected=True) + bgp_rib_next_hops = verify_stale_routes_list( + tgen, addr_type, dut, verify_nh_for_static_rtes + ) + + stale_route_status = len(bgp_rib_next_hops) == 1 + assert ( + stale_route_status is True + ), "Testcase {} : Failed \n Error: {}".format( + tc_name, stale_route_status, expected=True + ) write_test_footer(tc_name) - diff --git a/tests/topotests/bgp_gr_restart_retain_routes/__init__.py b/tests/topotests/bgp_gr_restart_retain_routes/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/topotests/bgp_gr_restart_retain_routes/__init__.py diff --git a/tests/topotests/bgp_gr_restart_retain_routes/r1/bgpd.conf b/tests/topotests/bgp_gr_restart_retain_routes/r1/bgpd.conf new file mode 100644 index 000000000..50d158387 --- /dev/null +++ b/tests/topotests/bgp_gr_restart_retain_routes/r1/bgpd.conf @@ -0,0 +1,11 @@ +router bgp 65001 + no bgp ebgp-requires-policy + bgp graceful-restart + bgp graceful-restart preserve-fw-state + neighbor 192.168.255.2 remote-as external + neighbor 192.168.255.2 timers 1 3 + neighbor 192.168.255.2 timers connect 1 + address-family ipv4 + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_gr_restart_retain_routes/r1/zebra.conf b/tests/topotests/bgp_gr_restart_retain_routes/r1/zebra.conf new file mode 100644 index 000000000..e65bfb2c3 --- /dev/null +++ b/tests/topotests/bgp_gr_restart_retain_routes/r1/zebra.conf @@ -0,0 +1,7 @@ +! +interface lo + ip address 172.16.255.1/32 +! +interface r1-eth0 + ip address 192.168.255.1/24 +! diff --git a/tests/topotests/bgp_gr_restart_retain_routes/r2/bgpd.conf b/tests/topotests/bgp_gr_restart_retain_routes/r2/bgpd.conf new file mode 100644 index 000000000..97418ca9a --- /dev/null +++ b/tests/topotests/bgp_gr_restart_retain_routes/r2/bgpd.conf @@ -0,0 +1,8 @@ +router bgp 65002 + no bgp ebgp-requires-policy + bgp graceful-restart + bgp graceful-restart preserve-fw-state + neighbor 192.168.255.1 remote-as external + neighbor 192.168.255.1 timers 1 3 + neighbor 192.168.255.1 timers connect 1 +! diff --git a/tests/topotests/bgp_gr_restart_retain_routes/r2/zebra.conf b/tests/topotests/bgp_gr_restart_retain_routes/r2/zebra.conf new file mode 100644 index 000000000..758d797ad --- /dev/null +++ b/tests/topotests/bgp_gr_restart_retain_routes/r2/zebra.conf @@ -0,0 +1,5 @@ +no zebra nexthop kernel enable +! +interface r2-eth0 + ip address 192.168.255.2/24 +! diff --git a/tests/topotests/bgp_gr_restart_retain_routes/test_bgp_gr_restart_retain_routes.py b/tests/topotests/bgp_gr_restart_retain_routes/test_bgp_gr_restart_retain_routes.py new file mode 100644 index 000000000..52ce0d5b5 --- /dev/null +++ b/tests/topotests/bgp_gr_restart_retain_routes/test_bgp_gr_restart_retain_routes.py @@ -0,0 +1,131 @@ +#!/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. +# + +""" +Test if routes are retained during BGP restarts. +""" + +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, stop_router + +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_gr_restart_retain_routes(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + def _bgp_converge(): + output = json.loads(r2.vtysh_cmd("show bgp ipv4 neighbors 192.168.255.1 json")) + expected = { + "192.168.255.1": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}}, + } + } + return topotest.json_cmp(output, expected) + + def _bgp_check_bgp_retained_routes(): + output = json.loads(r2.vtysh_cmd("show bgp ipv4 unicast 172.16.255.1/32 json")) + expected = {"paths": [{"stale": True}]} + return topotest.json_cmp(output, expected) + + def _bgp_check_kernel_retained_routes(): + output = ( + r2.cmd("ip route show 172.16.255.1/32 proto bgp dev r2-eth0") + .replace("\n", "") + .rstrip() + ) + expected = "172.16.255.1 via 192.168.255.1 metric 20" + diff = topotest.get_textdiff( + output, expected, "Actual IP Routing Table", "Expected IP RoutingTable" + ) + if diff: + return False + return True + + step("Initial BGP converge") + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see BGP convergence on R2" + + step("Restart R1") + stop_router(tgen, "r1") + + step("Check if routes (BGP) are retained at R2") + test_func = functools.partial(_bgp_check_bgp_retained_routes) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see BGP retained routes on R2" + + step("Check if routes (Kernel) are retained at R2") + assert _bgp_check_kernel_retained_routes() == True + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_gshut_topo1/test_ebgp_gshut_topo1.py b/tests/topotests/bgp_gshut_topo1/test_ebgp_gshut_topo1.py index 58133c44a..684d2fcb5 100644 --- a/tests/topotests/bgp_gshut_topo1/test_ebgp_gshut_topo1.py +++ b/tests/topotests/bgp_gshut_topo1/test_ebgp_gshut_topo1.py @@ -92,7 +92,7 @@ def setup_module(mod): # Required linux kernel version for this suite to run. result = required_linux_kernel_version("4.16") if result is not True: - pytest.skip("Kernel requirements are not met") + pytest.skip("Kernel requirements are not met, kernel version should be >=4.16") testsuite_run_time = time.asctime(time.localtime(time.time())) logger.info("Testsuite start time: {}".format(testsuite_run_time)) diff --git a/tests/topotests/bgp_gshut_topo1/test_ibgp_gshut_topo1.py b/tests/topotests/bgp_gshut_topo1/test_ibgp_gshut_topo1.py index a79ce0e3a..16572a796 100644 --- a/tests/topotests/bgp_gshut_topo1/test_ibgp_gshut_topo1.py +++ b/tests/topotests/bgp_gshut_topo1/test_ibgp_gshut_topo1.py @@ -87,7 +87,7 @@ def setup_module(mod): # Required linux kernel version for this suite to run. result = required_linux_kernel_version("4.16") if result is not True: - pytest.skip("Kernel requirements are not met") + pytest.skip("Kernel requirements are not met, kernel version should be >=4.16") testsuite_run_time = time.asctime(time.localtime(time.time())) logger.info("Testsuite start time: {}".format(testsuite_run_time)) diff --git a/tests/topotests/bgp_local_as/__init__.py b/tests/topotests/bgp_local_as/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/topotests/bgp_local_as/__init__.py diff --git a/tests/topotests/bgp_local_as/r1/bgpd.conf b/tests/topotests/bgp_local_as/r1/bgpd.conf new file mode 100644 index 000000000..fa147d867 --- /dev/null +++ b/tests/topotests/bgp_local_as/r1/bgpd.conf @@ -0,0 +1,14 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as 65002 + neighbor 192.168.1.2 local-as 65002 + neighbor 192.168.1.2 timers 1 3 + neighbor 192.168.1.2 timers connect 1 + neighbor PG peer-group + neighbor PG remote-as 65003 + neighbor PG local-as 65003 + neighbor 192.168.2.2 peer-group PG + address-family ipv4 + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_local_as/r1/zebra.conf b/tests/topotests/bgp_local_as/r1/zebra.conf new file mode 100644 index 000000000..5b32faeb4 --- /dev/null +++ b/tests/topotests/bgp_local_as/r1/zebra.conf @@ -0,0 +1,10 @@ +! +interface lo + ip address 172.16.255.1/32 +! +interface r1-eth0 + ip address 192.168.1.1/24 +! +interface r1-eth1 + ip address 192.168.2.1/24 +! diff --git a/tests/topotests/bgp_local_as/r2/bgpd.conf b/tests/topotests/bgp_local_as/r2/bgpd.conf new file mode 100644 index 000000000..9c25bdfdd --- /dev/null +++ b/tests/topotests/bgp_local_as/r2/bgpd.conf @@ -0,0 +1,6 @@ +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as internal + neighbor 192.168.1.1 timers 1 3 + neighbor 192.168.1.1 timers connect 1 +! diff --git a/tests/topotests/bgp_local_as/r2/zebra.conf b/tests/topotests/bgp_local_as/r2/zebra.conf new file mode 100644 index 000000000..0c9565666 --- /dev/null +++ b/tests/topotests/bgp_local_as/r2/zebra.conf @@ -0,0 +1,4 @@ +! +interface r2-eth0 + ip address 192.168.1.2/24 +! diff --git a/tests/topotests/bgp_local_as/r3/bgpd.conf b/tests/topotests/bgp_local_as/r3/bgpd.conf new file mode 100644 index 000000000..54ccd90f3 --- /dev/null +++ b/tests/topotests/bgp_local_as/r3/bgpd.conf @@ -0,0 +1,6 @@ +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 192.168.2.1 remote-as internal + neighbor 192.168.2.1 timers 1 3 + neighbor 192.168.2.1 timers connect 1 +! diff --git a/tests/topotests/bgp_local_as/r3/zebra.conf b/tests/topotests/bgp_local_as/r3/zebra.conf new file mode 100644 index 000000000..d28deddfc --- /dev/null +++ b/tests/topotests/bgp_local_as/r3/zebra.conf @@ -0,0 +1,4 @@ +! +interface r3-eth0 + ip address 192.168.2.2/24 +! diff --git a/tests/topotests/bgp_local_as/test_bgp_local_as.py b/tests/topotests/bgp_local_as/test_bgp_local_as.py new file mode 100644 index 000000000..525d44220 --- /dev/null +++ b/tests/topotests/bgp_local_as/test_bgp_local_as.py @@ -0,0 +1,134 @@ +#!/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): + for routern in range(1, 4): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r3"]) + + +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_local_as_same_remote_as(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_check_local_as_same_remote_as(): + output = json.loads( + tgen.gears["r2"].vtysh_cmd("show bgp ipv4 unicast 172.16.255.1/32 json") + ) + expected = { + "paths": [ + { + "valid": True, + "aspath": {"string": "Local"}, + "nexthops": [{"ip": "192.168.1.1", "hostname": "r1"}], + } + ] + } + return topotest.json_cmp(output, expected) + + step("Check if iBGP works when local-as == remote-as") + test_func = functools.partial(_bgp_check_local_as_same_remote_as) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see BGP prefixes on R2" + + +def test_bgp_peer_group_local_as_same_remote_as(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_check_local_as_same_remote_as(): + output = json.loads( + tgen.gears["r3"].vtysh_cmd("show bgp ipv4 unicast 172.16.255.1/32 json") + ) + expected = { + "paths": [ + { + "valid": True, + "aspath": {"string": "Local"}, + "nexthops": [{"ip": "192.168.2.1", "hostname": "r1"}], + } + ] + } + return topotest.json_cmp(output, expected) + + step("Initial BGP converge") + test_func = functools.partial(_bgp_check_local_as_same_remote_as) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see BGP prefixes on R3" + + +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 000000000..e69de29bb --- /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 000000000..41bf96344 --- /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 000000000..7c2ed09b8 --- /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 000000000..187713d08 --- /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 000000000..fd45c48d6 --- /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 000000000..a83d43310 --- /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_prefix_list_topo1/test_prefix_lists.py b/tests/topotests/bgp_prefix_list_topo1/test_prefix_lists.py index 2dc95cee2..5131a89ce 100644 --- a/tests/topotests/bgp_prefix_list_topo1/test_prefix_lists.py +++ b/tests/topotests/bgp_prefix_list_topo1/test_prefix_lists.py @@ -346,9 +346,12 @@ def test_ip_prefix_lists_out_permit(request): result = verify_rib( tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: Routes still" " present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) + ) + write_test_footer(tc_name) @@ -444,9 +447,11 @@ def test_ip_prefix_lists_in_deny_and_permit_any(request): result = verify_rib( tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: Routes still" " present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) + ) write_test_footer(tc_name) @@ -644,9 +649,12 @@ def test_ip_prefix_lists_out_deny_and_permit_any(request): result = verify_rib( tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: Routes still" " present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) + ) + write_test_footer(tc_name) @@ -778,9 +786,11 @@ def test_modify_prefix_lists_in_permit_to_deny(request): result = verify_rib( tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: Routes still" " present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) + ) write_test_footer(tc_name) @@ -882,9 +892,11 @@ def test_modify_prefix_lists_in_deny_to_permit(request): result = verify_rib( tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: Routes still" " present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) + ) # Modify ip prefix list input_dict_1 = { @@ -1051,9 +1063,11 @@ def test_modify_prefix_lists_out_permit_to_deny(request): result = verify_rib( tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: Routes still" " present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) + ) write_test_footer(tc_name) @@ -1157,9 +1171,11 @@ def test_modify_prefix_lists_out_deny_to_permit(request): result = verify_rib( tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: Routes still" " present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) + ) # Modify ip prefix list input_dict_1 = { @@ -1324,9 +1340,11 @@ def test_ip_prefix_lists_implicit_deny(request): result = verify_rib( tgen, "ipv4", dut, input_dict_1, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: Routes still" " present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) + ) write_test_footer(tc_name) diff --git a/tests/topotests/bgp_route_map/test_route_map_topo1.py b/tests/topotests/bgp_route_map/test_route_map_topo1.py index 655a3dc89..77bddbe33 100644 --- a/tests/topotests/bgp_route_map/test_route_map_topo1.py +++ b/tests/topotests/bgp_route_map/test_route_map_topo1.py @@ -444,12 +444,11 @@ def test_route_map_inbound_outbound_same_neighbor_p0(request): result = verify_rib( tgen, adt, dut, input_dict_2, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed \nroutes are not present in rib \n Error: {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} BGP RIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info("Expected behaviour: {}".format(result)) # Verifying RIB routes dut = "r4" @@ -467,12 +466,11 @@ def test_route_map_inbound_outbound_same_neighbor_p0(request): result = verify_rib( tgen, adt, dut, input_dict, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed \nroutes are not present in rib \n Error: {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info("Expected behaviour: {}".format(result)) write_test_footer(tc_name) @@ -666,12 +664,11 @@ def test_route_map_with_action_values_combination_of_prefix_action_p0( result = verify_rib( tgen, adt, dut, input_dict_2, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed \nRoutes are still present \n Error: {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info("Expected behaviour: {}".format(result)) else: result = verify_rib(tgen, adt, dut, input_dict_2, protocol=protocol) assert result is True, "Testcase {} : Failed \n Error: {}".format( diff --git a/tests/topotests/bgp_route_map/test_route_map_topo2.py b/tests/topotests/bgp_route_map/test_route_map_topo2.py index 4da7eeb2f..589482d6a 100644 --- a/tests/topotests/bgp_route_map/test_route_map_topo2.py +++ b/tests/topotests/bgp_route_map/test_route_map_topo2.py @@ -1022,12 +1022,11 @@ def test_modify_prefix_list_referenced_by_rmap_p0(): result = verify_rib( tgen, addr_type, dut, input_dict, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed \nroutes are not present \n Error: {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info("Expected behaviour: {}".format(result)) # Verifying RIB routes dut = "r4" @@ -1036,10 +1035,10 @@ def test_modify_prefix_list_referenced_by_rmap_p0(): result = verify_rib( tgen, addr_type, dut, input_dict, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed \nExpected behaviour: routes are not present \n Error: {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) write_test_footer(tc_name) @@ -1293,12 +1292,11 @@ def test_remove_prefix_list_referenced_by_rmap_p0(): result = verify_rib( tgen, addr_type, dut, input_dict, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed \nroutes are not present \n Error: {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info("Expected behaviour: {}".format(result)) # Verifying RIB routes dut = "r4" @@ -1307,12 +1305,11 @@ def test_remove_prefix_list_referenced_by_rmap_p0(): result = verify_rib( tgen, addr_type, dut, input_dict, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed \nroutes are not present \n Error: {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info("Expected behaviour: {}".format(result)) write_test_footer(tc_name) @@ -2139,12 +2136,11 @@ def test_add_remove_rmap_to_specific_neighbor_p0(): result = verify_rib( tgen, addr_type, dut, input_dict, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed \n Error Routes are still present: {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info("Expected behaviour: {}".format(result)) # Remove applied rmap from neighbor input_dict_4 = { @@ -2553,12 +2549,11 @@ def test_rmap_without_match_and_set_clause_p0(): result = verify_rib( tgen, addr_type, dut, input_dict, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed \nroutes are not present \n Error: {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info("Expected behaviour: {}".format(result)) write_test_footer(tc_name) # Uncomment next line for debugging @@ -2801,12 +2796,11 @@ def test_set_localpref_weight_to_ebgp_and_med_to_ibgp_peers_p0(): input_dict_3_addr_type[addr_type], expected=False, ) - assert ( - result is not True - ), "Testcase {} : Failed \nAttributes are not set \n Error: {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: BGP attributes should not be set in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info("Expected behaviour: {}".format(result)) # Verifying RIB routes dut = "r5" @@ -2835,12 +2829,11 @@ def test_set_localpref_weight_to_ebgp_and_med_to_ibgp_peers_p0(): input_dict_3_addr_type[addr_type], expected=False, ) - assert ( - result is not True - ), "Testcase {} : Failed \nAttributes are not set \n Error: {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: BGP attributes should not be set in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info("Expected behaviour: {}".format(result)) write_test_footer(tc_name) @@ -3644,12 +3637,11 @@ def test_create_rmap_match_prefix_list_to_deny_in_and_outbound_prefixes_p0(): result = verify_rib( tgen, addr_type, dut, input_dict, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed \nroutes are not present \n Error: {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info("Expected behaviour: {}".format(result)) # Verifying RIB routes dut = "r4" @@ -3658,12 +3650,11 @@ def test_create_rmap_match_prefix_list_to_deny_in_and_outbound_prefixes_p0(): result = verify_rib( tgen, addr_type, dut, input_dict, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed \nroutes are not present \n Error: {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info("Expected behaviour: {}".format(result)) write_test_footer(tc_name) @@ -3963,12 +3954,11 @@ def test_create_rmap_to_match_tag_deny_outbound_prefixes_p0(): result = verify_rib( tgen, addr_type, dut, input_dict, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed \nroutes are denied \n Error: {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n " + "Expected: Routes should not be present in {} FIB \n " + "Found: {}".format(tc_name, dut, result) ) - logger.info("Expected behaviour: {}".format(result)) write_test_footer(tc_name) 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 8defa0125..8ccf7a2a3 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 51d9c9223..839454d8a 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 a43cec20e..a9319a6ae 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 71ddedf6f..9e5fa0ac0 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 014722387..8b6c3772b 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-reload.py b/tools/frr-reload.py index 32f9a7884..7c5d91d4d 100755 --- a/tools/frr-reload.py +++ b/tools/frr-reload.py @@ -890,7 +890,7 @@ def bgp_remove_neighbor_cfg(lines_to_del, del_nbr_dict): ): if ctx_keys[0] in del_nbr_dict: for nbr in del_nbr_dict[ctx_keys[0]]: - re_nbr_pg = re.search('neighbor (\S+) .*peer-group (\S+)', line) + re_nbr_pg = re.search("neighbor (\S+) .*peer-group (\S+)", line) nb_exp = "neighbor %s .*" % nbr if not re_nbr_pg: re_nb = re.search(nb_exp, line) @@ -997,7 +997,7 @@ def delete_move_lines(lines_to_add, lines_to_del): # 'no neighbor peer [interface] peer-group <>' is in lines_to_del # copy the neighbor and look for all config removal lines associated # to neighbor and delete them from the lines_to_del - re_nbr_pg = re.search('neighbor (\S+) .*peer-group (\S+)', line) + re_nbr_pg = re.search("neighbor (\S+) .*peer-group (\S+)", line) if re_nbr_pg: if ctx_keys[0] not in del_nbr_dict: del_nbr_dict[ctx_keys[0]] = list() @@ -1376,6 +1376,37 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del): lines_to_add.append((add_cmd, None)) lines_to_del_to_del.append((ctx_keys, None)) + # bgp community-list, large-community-list, extcommunity-list can be + # specified without a seq number. However, the running config always + # adds `seq X` (sequence number). So, ignore such lines as well. + # Examples: + # bgp community-list standard clist seq 5 permit 222:213 + # bgp large-community-list standard llist seq 5 permit 65001:65001:1 + # bgp extcommunity-list standard elist seq 5 permit soo 123:123 + re_bgp_lists = re.search( + "^(bgp )(community-list|large-community-list|extcommunity-list)(\s+\S+\s+)(\S+\s+)(seq \d+\s+)(permit|deny)(.*)$", + ctx_keys[0], + ) + if re_bgp_lists: + found = False + tmpline = ( + re_bgp_lists.group(1) + + re_bgp_lists.group(2) + + re_bgp_lists.group(3) + + re_bgp_lists.group(4) + + re_bgp_lists.group(6) + + re_bgp_lists.group(7) + ) + for ctx in lines_to_add: + if ctx[0][0] == tmpline: + lines_to_del_to_del.append((ctx_keys, None)) + lines_to_add_to_del.append(((tmpline,), None)) + found = True + if found is False: + add_cmd = ("no " + ctx_keys[0],) + lines_to_add.append((add_cmd, None)) + lines_to_del_to_del.append((ctx_keys, None)) + if ( len(ctx_keys) == 3 and ctx_keys[0].startswith("router bgp") diff --git a/tools/frr.in b/tools/frr.in index 27b2c0ab8..f0c665fde 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 b589ced96..469b9c5d8 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/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 35b81dfd8..aea7d9abc 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -710,6 +710,8 @@ DEFUN_NOSH (show_debugging_vrrp, vrrp_debug_status_write(vty); + cmd_show_lib_debugs(vty); + return CMD_SUCCESS; } diff --git a/watchfrr/watchfrr_vty.c b/watchfrr/watchfrr_vty.c index 1492ee37b..e5cc43754 100644 --- a/watchfrr/watchfrr_vty.c +++ b/watchfrr/watchfrr_vty.c @@ -125,6 +125,8 @@ DEFUN_NOSH (show_debugging_watchfrr, DEBUG_STR WATCHFRR_STR) { + cmd_show_lib_debugs(vty); + return CMD_SUCCESS; } diff --git a/yang/frr-isisd.yang b/yang/frr-isisd.yang index 0812c86fa..380fce385 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/debug.c b/zebra/debug.c index 69aaed33a..25102145d 100644 --- a/zebra/debug.c +++ b/zebra/debug.c @@ -137,6 +137,9 @@ DEFUN_NOSH (show_debugging_zebra, vty_out(vty, " Zebra PBR debugging is on\n"); hook_call(zebra_debug_show_debugging, vty); + + cmd_show_lib_debugs(vty); + return CMD_SUCCESS; } diff --git a/zebra/main.c b/zebra/main.c index 46cf2eea7..3de97943f 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 043ea0f24..064c91b72 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_srv6_vty.c b/zebra/zebra_srv6_vty.c index 62ce17326..daac4cade 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 42d6aefa9..2f8b5048d 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 */ |