diff options
author | Madhuri Kuruganti <maduri111@gmail.com> | 2022-09-04 14:06:33 +0200 |
---|---|---|
committer | Madhuri Kuruganti <maduri111@gmail.com> | 2022-10-12 10:13:55 +0200 |
commit | 70cd87ca02125616e9f61f97ab504248c935bd83 (patch) | |
tree | 0e919f109aef193f7fbb0653a2da413ca2cda9cc /bgpd | |
parent | doc: Add documentation for BGP ORR support (diff) | |
download | frr-70cd87ca02125616e9f61f97ab504248c935bd83.tar.xz frr-70cd87ca02125616e9f61f97ab504248c935bd83.zip |
bgpd: optimal router reflection cli and fsm changes
Signed-off-by: Madhuri Kuruganti <maduri111@gmail.com>
Diffstat (limited to 'bgpd')
-rw-r--r-- | bgpd/bgp_debug.c | 43 | ||||
-rw-r--r-- | bgpd/bgp_debug.h | 3 | ||||
-rw-r--r-- | bgpd/bgp_fsm.c | 3 | ||||
-rw-r--r-- | bgpd/bgp_fsm.h | 2 | ||||
-rw-r--r-- | bgpd/bgp_memory.c | 4 | ||||
-rw-r--r-- | bgpd/bgp_memory.h | 2 | ||||
-rw-r--r-- | bgpd/bgp_orr.c | 1147 | ||||
-rw-r--r-- | bgpd/bgp_orr.h | 98 | ||||
-rw-r--r-- | bgpd/bgp_route.c | 70 | ||||
-rw-r--r-- | bgpd/bgp_vty.c | 82 | ||||
-rw-r--r-- | bgpd/bgp_zebra.c | 35 | ||||
-rw-r--r-- | bgpd/bgpd.c | 15 | ||||
-rw-r--r-- | bgpd/bgpd.h | 55 | ||||
-rw-r--r-- | bgpd/subdir.am | 2 |
14 files changed, 1560 insertions, 1 deletions
diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index 264dd85fb..987b4508f 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -71,6 +71,7 @@ unsigned long conf_bgp_debug_graceful_restart; unsigned long conf_bgp_debug_evpn_mh; unsigned long conf_bgp_debug_bfd; unsigned long conf_bgp_debug_cond_adv; +unsigned long conf_bgp_debug_optimal_route_reflection; unsigned long term_bgp_debug_as4; unsigned long term_bgp_debug_neighbor_events; @@ -92,6 +93,7 @@ unsigned long term_bgp_debug_graceful_restart; unsigned long term_bgp_debug_evpn_mh; unsigned long term_bgp_debug_bfd; unsigned long term_bgp_debug_cond_adv; +unsigned long term_bgp_debug_optimal_route_reflection; struct list *bgp_debug_neighbor_events_peers = NULL; struct list *bgp_debug_keepalive_peers = NULL; @@ -2044,6 +2046,33 @@ DEFPY (debug_bgp_evpn_mh, return CMD_SUCCESS; } +DEFPY (debug_bgp_optimal_route_reflection, + debug_bgp_optimal_route_reflection_cmd, + "[no$no] debug bgp optimal-route-reflection", + NO_STR + DEBUG_STR + BGP_STR + BGP_ORR_DEBUG) +{ + if (vty->node == CONFIG_NODE) { + if (no) + DEBUG_OFF(optimal_route_reflection, ORR); + else + DEBUG_ON(optimal_route_reflection, ORR); + } else { + if (no) { + TERM_DEBUG_OFF(optimal_route_reflection, ORR); + vty_out(vty, + "BGP Optimal Route Reflection debugging is off\n"); + } else { + TERM_DEBUG_ON(optimal_route_reflection, ORR); + vty_out(vty, + "BGP Optimal Route Reflection debugging is on\n"); + } + } + return CMD_SUCCESS; +} + DEFUN (debug_bgp_labelpool, debug_bgp_labelpool_cmd, "debug bgp labelpool", @@ -2182,6 +2211,7 @@ DEFUN (no_debug_bgp, TERM_DEBUG_OFF(evpn_mh, EVPN_MH_RT); TERM_DEBUG_OFF(bfd, BFD_LIB); TERM_DEBUG_OFF(cond_adv, COND_ADV); + TERM_DEBUG_OFF(optimal_route_reflection, ORR); vty_out(vty, "All possible debugging has been turned off\n"); @@ -2278,6 +2308,10 @@ DEFUN_NOSH (show_debugging_bgp, vty_out(vty, " BGP conditional advertisement debugging is on\n"); + if (BGP_DEBUG(optimal_route_reflection, ORR)) + vty_out(vty, + " BGP Optimal Route Reflection debugging is on\n"); + cmd_show_lib_debugs(vty); return CMD_SUCCESS; @@ -2411,6 +2445,11 @@ static int bgp_config_write_debug(struct vty *vty) if (CONF_BGP_DEBUG(cond_adv, COND_ADV)) { vty_out(vty, "debug bgp conditional-advertisement\n"); + write++ + } + + if (CONF_BGP_DEBUG(optimal_route_reflection, ORR)) { + vty_out(vty, "debug bgp optimal-route-reflection\n"); write++; } @@ -2546,6 +2585,10 @@ void bgp_debug_init(void) /* debug bgp conditional advertisement */ install_element(ENABLE_NODE, &debug_bgp_cond_adv_cmd); install_element(CONFIG_NODE, &debug_bgp_cond_adv_cmd); + + /* debug bgp optimal route reflection */ + install_element(ENABLE_NODE, &debug_bgp_optimal_route_reflection_cmd); + install_element(CONFIG_NODE, &debug_bgp_optimal_route_reflection_cmd); } /* Return true if this prefix is on the per_prefix_list of prefixes to debug diff --git a/bgpd/bgp_debug.h b/bgpd/bgp_debug.h index 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_orr.c b/bgpd/bgp_orr.c new file mode 100644 index 000000000..b11bca4a0 --- /dev/null +++ b/bgpd/bgp_orr.c @@ -0,0 +1,1147 @@ +/* + * 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"); + +extern struct zclient *zclient; + +static inline bool is_orr_primary_root(struct bgp_orr_group *orr_group, + char *host) +{ + return orr_group->primary && !strcmp(orr_group->primary->host, host); +} + +static inline bool is_orr_secondary_root(struct bgp_orr_group *orr_group, + char *host) +{ + return orr_group->secondary && + !strcmp(orr_group->secondary->host, host); +} + +static inline bool is_orr_tertiary_root(struct bgp_orr_group *orr_group, + char *host) +{ + return orr_group->tertiary && !strcmp(orr_group->tertiary->host, host); +} + +static inline bool is_orr_active_root(struct bgp_orr_group *orr_group, + char *host) +{ + return orr_group->active && !strcmp(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_orr_primary_reachable(struct bgp_orr_group *orr_group) +{ + return orr_group->primary && + orr_group->primary->afc_nego[orr_group->afi][orr_group->safi] && + peer_established(orr_group->primary); +} + +static inline bool is_orr_secondary_reachable(struct bgp_orr_group *orr_group) +{ + return orr_group->secondary && + orr_group->secondary + ->afc_nego[orr_group->afi][orr_group->safi] && + peer_established(orr_group->secondary); +} + +static inline bool is_orr_tertiary_reachable(struct bgp_orr_group *orr_group) +{ + return orr_group->tertiary && + orr_group->tertiary->afc_nego[orr_group->afi][orr_group->safi] && + peer_established(orr_group->tertiary); +} + +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) && + !strcmp(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, afi) && + 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) + zlog_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); + memset(orr_group, 0, sizeof(struct bgp_orr_group)); + + 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 (strcmp(group->name, name) == 0) + 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 (strcmp(rrclient, rr_client_host) == 0) + 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 || + strcmp(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 && + strcmp(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: %pBP", __func__, + orr_group->primary + ? "No change in Active Root" + : "Primary Root is NULL"); + } else { + if (orr_group->primary) { + if (orr_group->active && + !strcmp(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 || + strcmp(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 && + strcmp(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: %pBP", __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 && + !strcmp(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 || + strcmp(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 && + strcmp(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 && + !strcmp(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 (!strcmp(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*/ +static 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) || + strcmp(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); + + UNSET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_ORR_GROUP); + XFREE(MTYPE_BGP_ORR_GROUP_NAME, peer->orr_group_name[afi][safi]); + + /* 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); + + 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 set) +{ + 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 (!set) { + 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 set) +{ + 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 (set) { + 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 ((orr_group->primary && + !strcmp(peer->host, orr_group->primary->host)) || + (orr_group->secondary && + !strcmp(peer->host, orr_group->secondary->host)) || + (orr_group->tertiary && + !strcmp(peer->host, orr_group->tertiary->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 (!strcmp(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); + bgp_peer_update_orr_group_active_root(peer, afi, safi, + orr_group); + } +} + +/* 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 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; + + 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; + 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 %d", __func__, + 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 (!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); + } + 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); + + 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..29a6fddf0 --- /dev/null +++ b/bgpd/bgp_orr.h @@ -0,0 +1,98 @@ +/* + * 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 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 set); +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 set); +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 a07c3b331..0b411d0ec 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" @@ -567,6 +568,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 +601,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. */ @@ -1061,6 +1068,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( @@ -12406,6 +12456,7 @@ DEFPY(show_ip_bgp, show_ip_bgp_cmd, |alias ALIAS_NAME\ |A.B.C.D/M longer-prefixes\ |X:X::X:X/M longer-prefixes\ + |optimal-route-reflection [WORD$orr_group_name]\ ] [json$uj [detail$detail] | wide$wide]", SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR BGP_AFI_HELP_STR BGP_SAFI_WITH_LABEL_HELP_STR @@ -12454,6 +12505,8 @@ DEFPY(show_ip_bgp, show_ip_bgp_cmd, "Display route and more specific routes\n" "IPv6 prefix\n" "Display route and more specific routes\n" + "Display Optimal Route Reflection RR Clients\n" + "ORR Group name\n" JSON_STR "Display detailed version of JSON output\n" "Increase table width for longer prefixes\n") @@ -12470,6 +12523,7 @@ DEFPY(show_ip_bgp, show_ip_bgp_cmd, uint16_t show_flags = 0; enum rpki_states rpki_target_state = RPKI_NOT_BEING_USED; struct prefix p; + bool orr_group = false; if (uj) { argc--; @@ -12644,12 +12698,18 @@ DEFPY(show_ip_bgp, show_ip_bgp_cmd, output_arg = &p; } + if (argv_find(argv, argc, "optimal-route-reflection", &idx)) + orr_group = true; + if (!all) { /* show bgp: AFI_IP6, show ip bgp: AFI_IP */ if (community) return bgp_show_community(vty, bgp, community, exact_match, afi, safi, show_flags); + else if (orr_group) + return bgp_show_orr(vty, bgp, afi, safi, orr_group_name, + show_flags); else return bgp_show(vty, bgp, afi, safi, sh_type, output_arg, show_flags, @@ -12695,6 +12755,11 @@ DEFPY(show_ip_bgp, show_ip_bgp_cmd, vty, abgp, community, exact_match, afi, safi, show_flags); + else if (orr_group) + bgp_show_orr(vty, bgp, afi, + safi, + orr_group_name, + show_flags); else bgp_show(vty, abgp, afi, safi, sh_type, output_arg, @@ -12734,6 +12799,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_vty.c b/bgpd/bgp_vty.c index 7de222316..42d2ffa8d 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, { @@ -937,6 +939,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); @@ -6193,6 +6198,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, @@ -12380,6 +12422,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"); @@ -17356,6 +17403,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", + peer->host, peer->orr_group_name[afi][safi]); } static void bgp_vpn_config_write(struct vty *vty, struct bgp *bgp, afi_t afi, @@ -17462,6 +17513,9 @@ static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi, } } + /* Optimal Route Reflection */ + bgp_config_write_orr(vty, bgp, afi, safi); + vty_endframe(vty, " exit-address-family\n"); } @@ -18971,6 +19025,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); diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 57a859c61..c3973efa9 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)); @@ -3382,6 +3385,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 +3840,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 1bcd71440..188402d0b 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -92,6 +92,7 @@ #include "bgpd/bgp_evpn_private.h" #include "bgpd/bgp_evpn_mh.h" #include "bgpd/bgp_mac.h" +#include "bgpd/bgp_orr.h" DEFINE_MTYPE_STATIC(BGPD, PEER_TX_SHUTDOWN_MSG, "Peer shutdown message (TX)"); DEFINE_MTYPE_STATIC(BGPD, BGP_EVPN_INFO, "BGP EVPN instance information"); @@ -1839,6 +1840,8 @@ bool bgp_afi_safi_peer_exists(struct bgp *bgp, afi_t afi, safi_t safi) /* Change peer's AS number. */ void peer_as_change(struct peer *peer, as_t as, int as_specified) { + afi_t afi; + safi_t safi; enum bgp_peer_sort origtype, newtype; /* Stop peer. */ @@ -1877,6 +1880,11 @@ void peer_as_change(struct peer *peer, as_t as, int as_specified) /* reflector-client reset */ if (newtype != BGP_PEER_IBGP) { + + FOREACH_AFI_SAFI (afi, safi) + UNSET_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_ORR_GROUP); + UNSET_FLAG(peer->af_flags[AFI_IP][SAFI_UNICAST], PEER_FLAG_REFLECTOR_CLIENT); UNSET_FLAG(peer->af_flags[AFI_IP][SAFI_MULTICAST], @@ -3850,6 +3858,8 @@ void bgp_free(struct bgp *bgp) ecommunity_free(&bgp->vpn_policy[afi].rtlist[dir]); } + bgp_orr_cleanup(bgp); + XFREE(MTYPE_BGP, bgp->name); XFREE(MTYPE_BGP, bgp->name_pretty); XFREE(MTYPE_BGP, bgp->snmp_stats); @@ -4633,6 +4643,11 @@ static int peer_af_flag_modify(struct peer *peer, afi_t afi, safi_t safi, if (flag & PEER_FLAG_REFLECTOR_CLIENT && ptype != BGP_PEER_IBGP) return BGP_ERR_NOT_INTERNAL_PEER; + /* Do not remove reflector client when ORR is configured on this peer */ + if (flag & PEER_FLAG_REFLECTOR_CLIENT && !set && + peer_orr_rrclient_check(peer, afi, safi)) + return BGP_ERR_PEER_ORR_CONFIGURED; + /* Special check for remove-private-AS. */ if (flag & PEER_FLAG_REMOVE_PRIVATE_AS && ptype == BGP_PEER_IBGP) return BGP_ERR_REMOVE_PRIVATE_AS; diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index af4655065..9822d5c0b 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -47,6 +47,7 @@ #include "bgp_io.h" #include "lib/bfd.h" +#include "lib/orr_msg.h" #define BGP_MAX_HOSTNAME 64 /* Linux max, is larger than most other sys */ #define BGP_PEER_MAX_HASH_SIZE 16384 @@ -197,6 +198,40 @@ struct bgp_redist { struct bgp_rmap rmap; }; +struct bgp_orr_igp_metric { + struct prefix prefix; + uint32_t igp_metric; +}; + +struct bgp_orr_group { + /* Name of this ORR group */ + char *name; + + /* Address Family Identifiers */ + afi_t afi; + safi_t safi; + + /* Pointer to BGP */ + struct bgp *bgp; + + /* Root Routers of the group */ + struct peer *primary; + struct peer *secondary; + struct peer *tertiary; + + /* Active Root Router of the group */ + struct peer *active; + + /* RR clients belong to this group */ + struct list *rr_client_list; + + /* IGP metric data from active root */ + struct list *igp_metric_info; + + /* Route table calculated from active root for this group */ + struct bgp_table *route_table; +}; + enum vpn_policy_direction { BGP_VPN_POLICY_DIR_FROMVPN = 0, BGP_VPN_POLICY_DIR_TOVPN = 1, @@ -779,6 +814,10 @@ struct bgp { bool allow_martian; + /* BGP optimal route reflection group and Root Router configuration */ + uint32_t orr_group_count; + struct list *orr_group[AFI_MAX][SAFI_MAX]; + QOBJ_FIELDS; }; DECLARE_QOBJ_TYPE(bgp); @@ -1422,6 +1461,10 @@ 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 */ + + /* BGP Optimal Route Reflection Group name */ + char *orr_group_name[AFI_MAX][SAFI_MAX]; enum bgp_addpath_strat addpath_type[AFI_MAX][SAFI_MAX]; @@ -2029,7 +2072,10 @@ enum bgp_create_error_code { /*BGP Open Policy ERRORS */ BGP_ERR_INVALID_ROLE_NAME = -35, - BGP_ERR_INVALID_INTERNAL_ROLE = -36 + BGP_ERR_INVALID_INTERNAL_ROLE = -36, + + /* BGP ORR ERRORS */ + BGP_ERR_PEER_ORR_CONFIGURED = -37, }; /* @@ -2081,6 +2127,7 @@ extern struct peer_group *peer_group_lookup_dynamic_neighbor(struct bgp *, extern struct peer *peer_lookup_dynamic_neighbor(struct bgp *, union sockunion *); +extern bool peer_orr_rrclient_check(struct peer *peer, afi_t afi, safi_t safi); /* * Peers are incredibly easy to memory leak * due to the various ways that they are actually used @@ -2331,6 +2378,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 \ |