diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index 264dd85fbc66..580c18b58de0 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -71,6 +71,7 @@ unsigned long conf_bgp_debug_graceful_restart; unsigned long conf_bgp_debug_evpn_mh; unsigned long conf_bgp_debug_bfd; unsigned long conf_bgp_debug_cond_adv; +unsigned long conf_bgp_debug_optimal_route_reflection; unsigned long term_bgp_debug_as4; unsigned long term_bgp_debug_neighbor_events; @@ -92,6 +93,7 @@ unsigned long term_bgp_debug_graceful_restart; unsigned long term_bgp_debug_evpn_mh; unsigned long term_bgp_debug_bfd; unsigned long term_bgp_debug_cond_adv; +unsigned long term_bgp_debug_optimal_route_reflection; struct list *bgp_debug_neighbor_events_peers = NULL; struct list *bgp_debug_keepalive_peers = NULL; @@ -2044,6 +2046,33 @@ DEFPY (debug_bgp_evpn_mh, return CMD_SUCCESS; } +DEFPY (debug_bgp_optimal_route_reflection, + debug_bgp_optimal_route_reflection_cmd, + "[no$no] debug bgp optimal-route-reflection", + NO_STR + DEBUG_STR + BGP_STR + BGP_ORR_DEBUG) +{ + if (vty->node == CONFIG_NODE) { + if (no) + DEBUG_OFF(optimal_route_reflection, ORR); + else + DEBUG_ON(optimal_route_reflection, ORR); + } else { + if (no) { + TERM_DEBUG_OFF(optimal_route_reflection, ORR); + vty_out(vty, + "BGP Optimal Route Reflection debugging is off\n"); + } else { + TERM_DEBUG_ON(optimal_route_reflection, ORR); + vty_out(vty, + "BGP Optimal Route Reflection debugging is on\n"); + } + } + return CMD_SUCCESS; +} + DEFUN (debug_bgp_labelpool, debug_bgp_labelpool_cmd, "debug bgp labelpool", @@ -2182,6 +2211,7 @@ DEFUN (no_debug_bgp, TERM_DEBUG_OFF(evpn_mh, EVPN_MH_RT); TERM_DEBUG_OFF(bfd, BFD_LIB); TERM_DEBUG_OFF(cond_adv, COND_ADV); + TERM_DEBUG_OFF(optimal_route_reflection, ORR); vty_out(vty, "All possible debugging has been turned off\n"); @@ -2278,6 +2308,10 @@ DEFUN_NOSH (show_debugging_bgp, vty_out(vty, " BGP conditional advertisement debugging is on\n"); + if (BGP_DEBUG(optimal_route_reflection, ORR)) + vty_out(vty, + " BGP Optimal Route Reflection debugging is on\n"); + cmd_show_lib_debugs(vty); return CMD_SUCCESS; @@ -2414,6 +2448,11 @@ static int bgp_config_write_debug(struct vty *vty) write++; } + if (CONF_BGP_DEBUG(optimal_route_reflection, ORR)) { + vty_out(vty, "debug bgp optimal-route-reflection\n"); + write++; + } + return write; } @@ -2546,6 +2585,10 @@ void bgp_debug_init(void) /* debug bgp conditional advertisement */ install_element(ENABLE_NODE, &debug_bgp_cond_adv_cmd); install_element(CONFIG_NODE, &debug_bgp_cond_adv_cmd); + + /* debug bgp optimal route reflection */ + install_element(ENABLE_NODE, &debug_bgp_optimal_route_reflection_cmd); + install_element(CONFIG_NODE, &debug_bgp_optimal_route_reflection_cmd); } /* Return true if this prefix is on the per_prefix_list of prefixes to debug diff --git a/bgpd/bgp_debug.h b/bgpd/bgp_debug.h index be5ed0afdc8d..f7090260ac39 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 ddda10077456..a85432a33ba8 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 aaf6c480b240..368c2c500162 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 850657d35ed0..ced3e1890e0d 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 510cfa21c921..990c6e1faaec 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 000000000000..7fed6b775ead --- /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 +#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 000000000000..158de3034213 --- /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 + +#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 a07c3b331e25..0b411d0ec1c4 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 7de222316fee..0182d125c781 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 [$primary [$secondary [$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 $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", + addr, 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 57a859c61da9..c3973efa918b 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 1bcd71440798..188402d0b4f9 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 af46550650fb..9822d5c0bd42 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 b1eeb937e180..765650313bfd 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/doc/user/bgp.rst b/doc/user/bgp.rst index 25d1cbfb6b97..cd7a0adff93b 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -3478,6 +3478,319 @@ When default route is not present in R2'2 BGP table, 10.139.224.0/20 and 192.0.2 Total number of prefixes 3 Router2# +.. _bgp-optimal-route-reflection: + +BGP Optimal Route Reflection +---------------------------- +BGP Route Reflectors (RRs) are used to improve network scalability by reducing +or eliminating the need for a full-mesh of IBGP sessions. + +When a BGP RR receives multiple paths for the same IP prefix, it typically +selects a single best path to send for all its clients. +If the RR has multiple nearly-equal best paths and the tie-break is determined +by the next-hop cost, the RR advertises the path based on its view of next-hop +costs, which leads to a non-optimal routing. +The advertised route may differ from the path that a client would select +if it had the visibility of the same set of candidate paths and used +its own view of next-hop costs. + +Non-optimal advertisements by the RR can be a problem in hot-potato routing. +Hot-potato routing aims to hand off traffic to the next AS using the closest +possible exit point from the local AS. +In this context, the closest exit point implies minimum IGP cost to +reach the BGP next-hop. + +The BGP Optimal Route Reflection allows the RR to choose and send a different +best path to a different or a set of RR clients. + +A link-state protocol is required. It can be OSPF or IS-IS. +Current implementation of BGP ORR is based on the IGP cost to the BGP next hop, +and not based on some configured policy. + +RR runs Shortest Path First (SPF) calculation with the selected +router as the root of the tree and calculates the cost to every other router. + +This special SPF calculation with another router as the root, is referred to as +a Reverse SPF (rSPF). This can only be done if the RR learns all the BGP paths +from all the BGP border routers. + +There could be as many rSPFs run as there are RR clients. +This will increase the CPU load somewhat on the RR. + +Current implementation allows up to three root nodes for the rSPF calculation. +There is no need to configure each RR client as a root and run rSPF. +Current implementation allows to configure three, the primary, the secondary, +and the tertiary root, per set of RR clients, for redundancy purposes. +For the BGP ORR feature to apply to any RR client, that RR client must be +configured to be part of an ORR policy group. + +The BGP ORR feature is enabled per address family. + +The minimal configuration needed: + +1. ORR needs to be enabled for specific groups of BGP neighbors. +2. For each group of BGP neighbors, at least one root needs to be configured. + Optionally, a secondary and tertiary root can be configured. +3. For OSPF, the root routers(RR clients) need additional configuration + to make BGP ORR work. + i.e. The MPLS TE configuration on the root router needs to have the minimal + configuration for MPLS TE enabled so that OSPF advertises the MPLS TE + router ID in an opaque-area LSA (type 10). + Once the RR has an opaque-area LSA with the MPLS TE router-ID matching the + configured root router address, rSPF can run and BGP on the RR can + advertise the optimal route. + +.. clicmd:: neighbor A.B.C.D optimal-route-reflection NAME + + This command allows the neighbor to be part of the ORR group. + +.. clicmd:: optimal-route-reflection orr-1 A.B.C.D [A.B.C.D] [A.B.C.D] + + This command creates an ORR group with a mandatory primary root + and optional secondary and/or tertiary roots. + When primary is reachable it will be the active root. + when primary goes down, secondary followed by tertiary takes over + the active root's role. + Always rSPF calculation runs active root as the root. + Which means the RR advertises the path based on active root's + view of next-hop costs. + +Sample Configuration +^^^^^^^^^^^^^^^^^^^^ + +Sample configuration on Route Reflector + +.. code-block:: frr + + ! + debug ospf 8 orr + debug bgp optimal-route-reflection + ! + interface enp0s8 + ip address 10.10.68.8/24 + ip ospf 8 area 0 + exit + ! + interface lo + ip address 10.100.1.8/32 + ip ospf 8 area 0 + exit + ! + router bgp 1 + neighbor 10.100.1.1 remote-as 1 + neighbor 10.100.1.1 update-source lo + neighbor 10.100.1.2 remote-as 1 + neighbor 10.100.1.2 update-source lo + neighbor 10.100.1.3 remote-as 1 + neighbor 10.100.1.3 update-source lo + neighbor 10.100.1.4 remote-as 1 + neighbor 10.100.1.4 update-source lo + ! + address-family ipv4 unicast + neighbor 10.100.1.1 route-reflector-client + neighbor 10.100.1.1 optimal-route-reflection orr-1 + neighbor 10.100.1.2 route-reflector-client + neighbor 10.100.1.2 optimal-route-reflection orr-1 + neighbor 10.100.1.3 route-reflector-client + neighbor 10.100.1.3 optimal-route-reflection orr-1 + neighbor 10.100.1.4 route-reflector-client + neighbor 10.100.1.4 optimal-route-reflection orr-1 + optimal-route-reflection orr-1 10.100.1.4 10.100.1.3 10.100.1.1 + exit-address-family + exit + ! + router ospf 8 + ospf router-id 8.8.8.8 + area 0 authentication + capability opaque + exit + ! + end + +Sample configuration on RR clients + +.. code-block:: frr + + interface enp0s8 + ip address 10.10.34.4/24 + ip ospf 4 area 0 + link-params + enable + exit-link-params + exit + ! + interface enp0s9 + ip address 10.10.74.4/24 + ip ospf 4 area 0 + link-params + enable + exit-link-params + exit + ! + interface lo + ip address 10.100.1.4/32 + ip ospf 4 area 0 + exit + ! + router bgp 1 + neighbor 10.100.1.8 remote-as 1 + neighbor 10.100.1.8 update-source lo + ! + address-family ipv4 unicast + neighbor 10.100.1.8 soft-reconfiguration inbound + exit-address-family + exit + ! + router ospf 4 + ospf router-id 4.4.4.4 + area 0 authentication + capability opaque + mpls-te on + mpls-te router-address 10.100.1.4 + mpls-te inter-as area 0.0.0.0 + mpls-te export + exit + ! + end + +Sample Output +^^^^^^^^^^^^^ + +When Optimal Route Reflection is not enabled on RR, it sends 10.100.1.1 as the best path to its clients. + +.. code-block:: frr + + Router-RR# show ip bgp neighbors 10.100.1.4 + + !--- Output suppressed. + + For address family: IPv4 Unicast + Update group 2, subgroup 2 + Packet Queue length 0 + Route-Reflector Client + Community attribute sent to this neighbor(all) + 0 accepted prefixes + + !--- Output suppressed. + + Router-RR# + Router-RR# show ip bgp + BGP table version is 3, local router ID is 10.100.1.8, vrf id 0 + Default local pref 100, local AS 1 + Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, + i internal, r RIB-failure, S Stale, R Removed + Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self + Origin codes: i - IGP, e - EGP, ? - incomplete + RPKI validation codes: V valid, I invalid, N Not found + + Network Next Hop Metric LocPrf Weight Path + * i203.0.113.0/24 10.100.1.2 0 100 0 i + *>i 10.100.1.1 0 100 0 i + *=i 10.100.1.3 0 100 0 i + + Displayed 1 routes and 3 total paths + Router-RR# + + Router-PE4# show ip bgp + BGP table version is 5, local router ID is 10.100.1.4, vrf id 0 + Default local pref 100, local AS 1 + Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, + i internal, r RIB-failure, S Stale, R Removed + Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self + Origin codes: i - IGP, e - EGP, ? - incomplete + RPKI validation codes: V valid, I invalid, N Not found + + Network Next Hop Metric LocPrf Weight Path + *>i203.0.113.0/24 10.100.1.1 0 100 0 i + + Displayed 1 routes and 1 total paths + Router-PE4# + +When Optimal Route Reflection is enabled on RR, it sends 10.100.1.3 as the best path to its clients. + +.. code-block:: frr + + Router-RR# show ip bgp neighbors 10.100.1.4 + + !--- Output suppressed. + + For address family: IPv4 Unicast + Update group 1, subgroup 1 + Packet Queue length 0 + Route-Reflector Client + ORR group (configured) : orr-1 + Community attribute sent to this neighbor(all) + 0 accepted prefixes + + !--- Output suppressed. + + Router-RR# + Router-RR# show ip bgp + BGP table version is 1, local router ID is 10.100.1.8, vrf id 0 + Default local pref 100, local AS 1 + Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, + i internal, r RIB-failure, S Stale, R Removed + Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self + Origin codes: i - IGP, e - EGP, ? - incomplete + RPKI validation codes: V valid, I invalid, N Not found + + Network Next Hop Metric LocPrf Weight Path + * i203.0.113.0/24 10.100.1.2 0 100 0 i + *>i 10.100.1.3 0 100 0 i + * i 10.100.1.1 0 100 0 i + + Displayed 1 routes and 3 total paths + Router-RR# + + Router-RR# show ip bgp optimal-route-reflection + + ORR group: orr-1, IPv4 Unicast + Configured root: primary: 10.100.1.4(Router-PE4), secondary: 10.100.1.3(Router-PE3), tertiary: 10.100.1.1(Router-PE1) + Active Root: 10.100.1.4(Router-PE4) + + RR Clients mapped: + 10.100.1.1 + 10.100.1.2 + 10.100.1.3 + 10.100.1.4 + + Number of mapping entries: 4 + + Prefix Cost + 10.10.34.0/24 100 + 10.10.61.0/24 300 + 10.10.63.0/24 200 + 10.10.67.0/24 200 + 10.10.68.0/24 300 + 10.10.72.0/24 200 + 10.10.74.0/24 100 + 10.100.1.1/32 300 + 10.100.1.2/32 200 + 10.100.1.3/32 100 + 10.100.1.4/32 0 + 10.100.1.6/32 200 + 10.100.1.7/32 100 + 10.100.1.8/32 300 + + Number of mapping entries: 14 + + Router-RR# + + Router-PE4# show ip bgp + BGP table version is 3, local router ID is 10.100.1.4, vrf id 0 + Default local pref 100, local AS 1 + Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, + i internal, r RIB-failure, S Stale, R Removed + Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self + Origin codes: i - IGP, e - EGP, ? - incomplete + RPKI validation codes: V valid, I invalid, N Not found + + Network Next Hop Metric LocPrf Weight Path + *>i203.0.113.0/24 10.100.1.3 0 100 0 i + + Displayed 1 routes and 1 total paths + Router-PE4# + .. _bgp-debugging: Debugging @@ -3543,6 +3856,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/ospfd.rst b/doc/user/ospfd.rst index 3bb018548ea0..3aa3d47f3516 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 ] route orr [NAME] + + Show the OSPF routing table, calculated from the active root of all ORR groups or specified ORR group. + .. clicmd:: show ip ospf graceful-restart helper [detail] [json] Displays the Grcaeful Restart Helper details including helper @@ -1138,6 +1144,10 @@ Debugging OSPF .. clicmd:: show debugging ospf +.. clicmd:: debug ospf orr + + Enable or disable debugging of BGP Optimal Route Reflection. + Sample Configuration ==================== diff --git a/lib/command.h b/lib/command.h index ca49efd262a7..42b4406212e2 100644 --- a/lib/command.h +++ b/lib/command.h @@ -417,6 +417,7 @@ struct cmd_node { #define BGP_SOFT_IN_STR "Send route-refresh unless using 'soft-reconfiguration inbound'\n" #define BGP_SOFT_OUT_STR "Resend all outbound updates\n" #define BGP_SOFT_RSCLIENT_RIB_STR "Soft reconfig for rsclient RIB\n" +#define BGP_ORR_DEBUG "Enable Optimal Route Reflection Debugging logs\n" #define OSPF_STR "OSPF information\n" #define NEIGHBOR_STR "Specify neighbor router\n" #define DEBUG_STR "Debugging functions\n" diff --git a/lib/orr_msg.h b/lib/orr_msg.h new file mode 100644 index 000000000000..b0c4c48df872 --- /dev/null +++ b/lib/orr_msg.h @@ -0,0 +1,94 @@ +/* + * Structures common to BGP, OSPF and ISIS for BGP Optimal Route Reflection + * Copyright (C) 2021 Samsung R&D Institute India - Bangalore. + * Madhurilatha Kuruganti + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _FRR_ORR_MSG_H +#define _FRR_ORR_MSG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* REVISIT: Need to check if we can use zero length array */ +#define ORR_MAX_PREFIX 100 +#define ORR_GROUP_NAME_SIZE 32 + +struct orr_prefix_metric { + struct prefix prefix; + uint32_t metric; +}; + +/* BGP-IGP Register for IGP metric */ +struct orr_igp_metric_reg { + bool reg; + uint8_t proto; + safi_t safi; + struct prefix prefix; + char group_name[ORR_GROUP_NAME_SIZE]; +}; + +/* IGP-BGP message structures */ +struct orr_igp_metric_info { + /* IGP instance data. */ + uint8_t proto; + uint32_t instId; + + safi_t safi; + + /* Add or delete routes */ + bool add; + + /* IGP metric from Active Root. */ + struct prefix root; + uint32_t num_entries; + struct orr_prefix_metric nexthop[ORR_MAX_PREFIX]; +}; + +/* BGP ORR Root node */ +struct orr_root { + afi_t afi; + safi_t safi; + + char group_name[ORR_GROUP_NAME_SIZE]; + + /* MPLS_TE prefix and router ID */ + struct prefix prefix; + struct in_addr router_id; + + /* Advertising OSPF Router ID. */ + struct in_addr adv_router; + + /* BGP-ORR Received LSAs */ + struct ospf_lsa *router_lsa_rcvd; + + /* Routing tables from root node */ + struct route_table *old_table; /* Old routing table. */ + struct route_table *new_table; /* Current routing table. */ + + struct route_table *old_rtrs; /* Old ABR/ASBR RT. */ + struct route_table *new_rtrs; /* New ABR/ASBR RT. */ +}; + +/* Prototypes. */ + +#ifdef __cplusplus +} +#endif + +#endif /* _FRR_ORR_MSG_H */ diff --git a/lib/subdir.am b/lib/subdir.am index d6defd7149fd..e04e700eb692 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -245,6 +245,7 @@ pkginclude_HEADERS += \ lib/ns.h \ lib/openbsd-queue.h \ lib/openbsd-tree.h \ + lib/orr_msg.h \ lib/plist.h \ lib/prefix.h \ lib/printfrr.h \ diff --git a/lib/zclient.h b/lib/zclient.h index c3ea2a16fffa..fb5da9aad2a9 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -100,6 +100,8 @@ enum zserv_client_capabilities { extern struct sockaddr_storage zclient_addr; extern socklen_t zclient_addr_len; +#define ZAPI_ORR_FLAG_UNICAST 0x01 + /* Zebra message types. */ typedef enum { ZEBRA_INTERFACE_ADD, @@ -1229,6 +1231,10 @@ enum zapi_opaque_registry { LDP_RLFA_UNREGISTER_ALL = 8, /* Announce LDP labels associated to a previously registered RLFA */ LDP_RLFA_LABELS = 9, + /* Register for IGP METRIC with OSPF/ISIS */ + ORR_IGP_METRIC_REGISTER = 10, + /* Send SPF data to BGP */ + ORR_IGP_METRIC_UPDATE = 11 }; /* Send the hello message. diff --git a/ospfd/ospf_dump.c b/ospfd/ospf_dump.c index 9f6adf322685..59f95c5da268 100644 --- a/ospfd/ospf_dump.c +++ b/ospfd/ospf_dump.c @@ -63,6 +63,7 @@ unsigned long conf_debug_ospf_ldp_sync; unsigned long conf_debug_ospf_gr; unsigned long conf_debug_ospf_bfd; unsigned long conf_debug_ospf_client_api; +unsigned long conf_debug_ospf_orr; /* Enable debug option variables -- valid only session. */ unsigned long term_debug_ospf_packet[5] = {0, 0, 0, 0, 0}; @@ -81,6 +82,7 @@ unsigned long term_debug_ospf_ldp_sync; unsigned long term_debug_ospf_gr; unsigned long term_debug_ospf_bfd; unsigned long term_debug_ospf_client_api; +unsigned long term_debug_ospf_orr; const char *ospf_redist_string(unsigned int route_type) { @@ -1601,6 +1603,33 @@ DEFPY (debug_ospf_client_api, return CMD_SUCCESS; } +DEFPY (debug_ospf_orr, + debug_ospf_orr_cmd, + "[no$no] debug ospf [(1-65535)$instance] orr", + NO_STR + DEBUG_STR + OSPF_STR + "Instance ID\n" + "OSPF ORR information\n") +{ + if (instance && instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + if (vty->node == CONFIG_NODE) { + if (no) + DEBUG_OFF(orr, ORR); + else + DEBUG_ON(orr, ORR); + } else { + if (no) + TERM_DEBUG_OFF(orr, ORR); + else + TERM_DEBUG_ON(orr, ORR); + } + + return CMD_SUCCESS; +} + DEFUN (no_debug_ospf, no_debug_ospf_cmd, "no debug ospf", @@ -1643,6 +1672,8 @@ DEFUN (no_debug_ospf, for (i = 0; i < 5; i++) DEBUG_PACKET_OFF(i, flag); + + DEBUG_OFF(orr, ORR); } for (i = 0; i < 5; i++) @@ -1673,6 +1704,7 @@ DEFUN (no_debug_ospf, TERM_DEBUG_OFF(ti_lfa, TI_LFA); TERM_DEBUG_OFF(bfd, BFD_LIB); TERM_DEBUG_OFF(client_api, CLIENT_API); + TERM_DEBUG_OFF(orr, ORR); return CMD_SUCCESS; } @@ -1802,6 +1834,12 @@ static int show_debugging_ospf_common(struct vty *vty) if (IS_DEBUG_OSPF(client_api, CLIENT_API) == OSPF_DEBUG_CLIENT_API) vty_out(vty, " OSPF client-api debugging is on\n"); + /* Show debug status for ORR. */ + if (IS_DEBUG_OSPF(orr, ORR) == OSPF_DEBUG_ORR) + vty_out(vty, " OSPF ORR debugging is on\n"); + + vty_out(vty, "\n"); + return CMD_SUCCESS; } @@ -2014,6 +2052,12 @@ static int config_write_debug(struct vty *vty) write = 1; } + /* debug ospf orr */ + if (IS_CONF_DEBUG_OSPF(orr, ORR) == OSPF_DEBUG_ORR) { + vty_out(vty, "debug ospf%s orr\n", str); + write = 1; + } + return write; } @@ -2035,6 +2079,7 @@ void ospf_debug_init(void) install_element(ENABLE_NODE, &debug_ospf_default_info_cmd); install_element(ENABLE_NODE, &debug_ospf_ldp_sync_cmd); install_element(ENABLE_NODE, &debug_ospf_client_api_cmd); + install_element(ENABLE_NODE, &debug_ospf_orr_cmd); install_element(ENABLE_NODE, &no_debug_ospf_ism_cmd); install_element(ENABLE_NODE, &no_debug_ospf_nsm_cmd); install_element(ENABLE_NODE, &no_debug_ospf_lsa_cmd); @@ -2074,6 +2119,7 @@ void ospf_debug_init(void) install_element(CONFIG_NODE, &debug_ospf_default_info_cmd); install_element(CONFIG_NODE, &debug_ospf_ldp_sync_cmd); install_element(CONFIG_NODE, &debug_ospf_client_api_cmd); + install_element(CONFIG_NODE, &debug_ospf_orr_cmd); install_element(CONFIG_NODE, &no_debug_ospf_nsm_cmd); install_element(CONFIG_NODE, &no_debug_ospf_lsa_cmd); install_element(CONFIG_NODE, &no_debug_ospf_zebra_cmd); diff --git a/ospfd/ospf_dump.h b/ospfd/ospf_dump.h index 251be7c8d177..e9ba8fc79811 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_lsa.c b/ospfd/ospf_lsa.c index 0df0072f6d7c..c67181cba607 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 97c15d1e3c99..a2a2393c90d5 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 f4fb858a5f4b..3c65ac388d32 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 28384438923f..5577a291be77 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 9bd0a844af81..3d2133b11abd 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 000000000000..eed948b19022 --- /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 +#include + +#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 +#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 000000000000..d0a6f6e790ce --- /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 6360d8ec601d..26f593f08971 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 fa9478fcedb2..e7e2b651c56a 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 4edc1de8119e..74213d7de283 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 834bfd0bb02e..2578051c2c04 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 c957c8c014e0..4f0fa6194acf 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 ] 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 1754512b5b15..461586424417 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 e0c36d86fea0..3f82d8692147 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 3a43010f85f6..c7735136bce6 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 4f9cbc7b1ee6..78688fac95be 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 \