summaryrefslogtreecommitdiff
path: root/bgpd/bgp_route.c
diff options
context:
space:
mode:
Diffstat (limited to 'bgpd/bgp_route.c')
-rw-r--r--bgpd/bgp_route.c194
1 files changed, 169 insertions, 25 deletions
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index ca6d138eaa..e55cd7b949 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -110,6 +110,36 @@ bgp_afi_node_get (struct bgp_table *table, afi_t afi, safi_t safi, struct prefix
return rn;
}
+struct bgp_node *
+bgp_afi_node_lookup (struct bgp_table *table, afi_t afi, safi_t safi, struct prefix *p,
+ struct prefix_rd *prd)
+{
+ struct bgp_node *rn;
+ struct bgp_node *prn = NULL;
+
+ if (!table)
+ return NULL;
+
+ if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP) || (safi == SAFI_EVPN))
+ {
+ prn = bgp_node_lookup (table, (struct prefix *) prd);
+ if (!prn)
+ return NULL;
+
+ if (prn->info == NULL)
+ {
+ bgp_unlock_node (prn);
+ return NULL;
+ }
+
+ table = prn->info;
+ }
+
+ rn = bgp_node_lookup (table, p);
+
+ return rn;
+}
+
/* Allocate bgp_info_extra */
static struct bgp_info_extra *
bgp_info_extra_new (void)
@@ -224,7 +254,7 @@ bgp_info_add (struct bgp_node *rn, struct bgp_info *ri)
/* Do the actual removal of info from RIB, for use by bgp_process
completion callback *only* */
-static void
+void
bgp_info_reap (struct bgp_node *rn, struct bgp_info *ri)
{
if (ri->next)
@@ -359,7 +389,7 @@ bgp_info_path_with_addpath_rx_str (struct bgp_info *ri, char *buf)
static int
bgp_info_cmp (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist,
int *paths_eq, struct bgp_maxpaths_cfg *mpath_cfg, int debug,
- const char *pfx_buf)
+ char *pfx_buf, afi_t afi, safi_t safi)
{
struct attr *newattr, *existattr;
struct attr_extra *newattre, *existattre;
@@ -381,6 +411,8 @@ bgp_info_cmp (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist,
int ret;
char new_buf[PATH_ADDPATH_STR_BUFFER];
char exist_buf[PATH_ADDPATH_STR_BUFFER];
+ u_int32_t new_mm_seq;
+ u_int32_t exist_mm_seq;
*paths_eq = 0;
@@ -414,6 +446,61 @@ bgp_info_cmp (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist,
newattre = newattr->extra;
existattre = existattr->extra;
+ /* For EVPN routes, we cannot just go by local vs remote, we have to
+ * look at the MAC mobility sequence number, if present.
+ */
+ if (safi == SAFI_EVPN)
+ {
+ /* This is an error condition described in RFC 7432 Section 15.2. The RFC
+ * states that in this scenario "the PE MUST alert the operator" but it
+ * does not state what other action to take. In order to provide some
+ * consistency in this scenario we are going to prefer the path with the
+ * sticky flag.
+ */
+ if (newattre->sticky != existattre->sticky)
+ {
+ if (!debug)
+ {
+ prefix2str (&new->net->p, pfx_buf, sizeof (*pfx_buf) * PREFIX2STR_BUFFER);
+ bgp_info_path_with_addpath_rx_str (new, new_buf);
+ bgp_info_path_with_addpath_rx_str (exist, exist_buf);
+ }
+
+ if (newattre->sticky && !existattre->sticky)
+ {
+ zlog_warn("%s: %s wins over %s due to sticky MAC flag",
+ pfx_buf, new_buf, exist_buf);
+ return 1;
+ }
+
+ if (!newattre->sticky && existattre->sticky)
+ {
+ zlog_warn("%s: %s loses to %s due to sticky MAC flag",
+ pfx_buf, new_buf, exist_buf);
+ return 0;
+ }
+ }
+
+ new_mm_seq = mac_mobility_seqnum (newattr);
+ exist_mm_seq = mac_mobility_seqnum (existattr);
+
+ if (new_mm_seq > exist_mm_seq)
+ {
+ if (debug)
+ zlog_debug("%s: %s wins over %s due to MM seq %u > %u",
+ pfx_buf, new_buf, exist_buf, new_mm_seq, exist_mm_seq);
+ return 1;
+ }
+
+ if (new_mm_seq < exist_mm_seq)
+ {
+ if (debug)
+ zlog_debug("%s: %s loses to %s due to MM seq %u < %u",
+ pfx_buf, new_buf, exist_buf, new_mm_seq, exist_mm_seq);
+ return 0;
+ }
+ }
+
/* 1. Weight check. */
new_weight = exist_weight = 0;
@@ -891,11 +978,12 @@ bgp_info_cmp (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist,
* This version is compatible with */
int
bgp_info_cmp_compatible (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist,
- afi_t afi, safi_t safi)
+ char *pfx_buf, afi_t afi, safi_t safi)
{
int paths_eq;
int ret;
- ret = bgp_info_cmp (bgp, new, exist, &paths_eq, NULL, 0, __func__);
+ ret = bgp_info_cmp (bgp, new, exist, &paths_eq, NULL, 0,
+ pfx_buf, afi, safi);
if (paths_eq)
ret = 0;
@@ -1599,16 +1687,11 @@ subgroup_announce_check (struct bgp_node *rn, struct bgp_info *ri,
return 1;
}
-struct bgp_info_pair
-{
- struct bgp_info *old;
- struct bgp_info *new;
-};
-
-static void
+void
bgp_best_selection (struct bgp *bgp, struct bgp_node *rn,
struct bgp_maxpaths_cfg *mpath_cfg,
- struct bgp_info_pair *result)
+ struct bgp_info_pair *result,
+ afi_t afi, safi_t safi)
{
struct bgp_info *new_select;
struct bgp_info *old_select;
@@ -1668,7 +1751,7 @@ bgp_best_selection (struct bgp *bgp, struct bgp_node *rn,
ri2->attr->aspath))
{
if (bgp_info_cmp (bgp, ri2, new_select, &paths_eq,
- mpath_cfg, debug, pfx_buf))
+ mpath_cfg, debug, pfx_buf, afi, safi))
{
bgp_info_unset_flag (rn, new_select, BGP_INFO_DMED_SELECTED);
new_select = ri2;
@@ -1725,7 +1808,8 @@ bgp_best_selection (struct bgp *bgp, struct bgp_node *rn,
bgp_info_unset_flag (rn, ri, BGP_INFO_DMED_CHECK);
- if (bgp_info_cmp (bgp, ri, new_select, &paths_eq, mpath_cfg, debug, pfx_buf))
+ if (bgp_info_cmp (bgp, ri, new_select, &paths_eq, mpath_cfg,
+ debug, pfx_buf, afi, safi))
{
new_select = ri;
}
@@ -1779,7 +1863,8 @@ bgp_best_selection (struct bgp *bgp, struct bgp_node *rn,
continue;
}
- bgp_info_cmp (bgp, ri, new_select, &paths_eq, mpath_cfg, debug, pfx_buf);
+ bgp_info_cmp (bgp, ri, new_select, &paths_eq, mpath_cfg,
+ debug, pfx_buf, afi, safi);
if (paths_eq)
{
@@ -1855,7 +1940,7 @@ subgroup_process_announce_selected (struct update_subgroup *subgrp,
* Clear IGP changed flag and attribute changed flag for a route (all paths).
* This is called at the end of route processing.
*/
-static void
+void
bgp_zebra_clear_route_change_flags (struct bgp_node *rn)
{
struct bgp_info *ri;
@@ -1874,7 +1959,7 @@ bgp_zebra_clear_route_change_flags (struct bgp_node *rn)
* if the route selection returns the same best route as earlier - to
* determine if we need to update zebra or not.
*/
-static int
+int
bgp_zebra_has_route_changed (struct bgp_node *rn, struct bgp_info *selected)
{
struct bgp_info *mpinfo;
@@ -1943,7 +2028,8 @@ bgp_process_main (struct work_queue *wq, void *data)
}
/* Best path selection. */
- bgp_best_selection (bgp, rn, &bgp->maxpaths[afi][safi], &old_and_new);
+ bgp_best_selection (bgp, rn, &bgp->maxpaths[afi][safi],
+ &old_and_new, afi, safi);
old_select = old_and_new.old;
new_select = old_and_new.new;
@@ -2343,12 +2429,17 @@ bgp_rib_withdraw (struct bgp_node *rn, struct bgp_info *ri, struct peer *peer,
}
}
#endif
+
+ /* If this is an EVPN route, process for un-import. */
+ if (safi == SAFI_EVPN)
+ bgp_evpn_unimport_route (peer->bgp, afi, safi, &rn->p, ri);
+
bgp_rib_remove (rn, ri, peer, afi, safi);
}
-static struct bgp_info *
-info_make (int type, int sub_type, u_short instance, struct peer *peer, struct attr *attr,
- struct bgp_node *rn)
+struct bgp_info *
+info_make (int type, int sub_type, u_short instance, struct peer *peer,
+ struct attr *attr, struct bgp_node *rn)
{
struct bgp_info *new;
@@ -2435,7 +2526,8 @@ bgp_update_martian_nexthop (struct bgp *bgp, afi_t afi, safi_t safi, struct attr
int ret = 0;
/* Only validated for unicast and multicast currently. */
- if (safi != SAFI_UNICAST && safi != SAFI_MULTICAST)
+ /* Also valid for EVPN where the nexthop is an IP address. */
+ if (safi != SAFI_UNICAST && safi != SAFI_MULTICAST && safi != SAFI_EVPN)
return 0;
/* If NEXT_HOP is present, validate it. */
@@ -2504,6 +2596,7 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id,
#if ENABLE_BGP_VNC
int vnc_implicit_withdraw = 0;
#endif
+ int same_attr=0;
memset (&new_attr, 0, sizeof(struct attr));
memset (&new_extra, 0, sizeof(struct attr_extra));
@@ -2613,6 +2706,7 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id,
if (ri)
{
ri->uptime = bgp_clock ();
+ same_attr = attrhash_cmp (ri->attr, attr_new);
/* Same attribute comes in. */
if (!CHECK_FLAG (ri->flags, BGP_INFO_REMOVED)
@@ -2734,7 +2828,32 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id,
}
}
#endif
-
+
+ /* Special handling for EVPN update of an existing route. If the
+ * extended community attribute has changed, we need to un-import
+ * the route using its existing extended community. It will be
+ * subsequently processed for import with the new extended community.
+ */
+ if (safi == SAFI_EVPN && !same_attr)
+ {
+ if ((ri->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES)) &&
+ (attr_new->flag & ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES)))
+ {
+ int cmp;
+
+ cmp = ecommunity_cmp (ri->attr->extra->ecommunity,
+ attr_new->extra->ecommunity);
+ if (!cmp)
+ {
+ if (bgp_debug_update(peer, p, NULL, 1))
+ zlog_debug ("Change in EXT-COMM, existing %s new %s",
+ ecommunity_str (ri->attr->extra->ecommunity),
+ ecommunity_str (attr_new->extra->ecommunity));
+ bgp_evpn_unimport_route (bgp, afi, safi, p, ri);
+ }
+ }
+ }
+
/* Update to new attribute. */
bgp_attr_unintern (&ri->attr);
ri->attr = attr_new;
@@ -2833,6 +2952,16 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id,
}
#endif
+ /* If this is an EVPN route and some attribute has changed, process
+ * route for import. If the extended community has changed, we would
+ * have done the un-import earlier and the import would result in the
+ * route getting injected into appropriate L2 VNIs. If it is just
+ * some other attribute change, the import will result in updating
+ * the attributes for the route in the VNI(s).
+ */
+ if (safi == SAFI_EVPN && !same_attr)
+ bgp_evpn_import_route (bgp, afi, safi, p, ri);
+
/* Process change. */
bgp_aggregate_increment (bgp, p, ri, afi, safi);
@@ -2953,6 +3082,10 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id,
if (bgp_maximum_prefix_overflow (peer, afi, safi, 0))
return -1;
+ /* If this is an EVPN route, process for import. */
+ if (safi == SAFI_EVPN)
+ bgp_evpn_import_route (bgp, afi, safi, p, new);
+
/* Process change. */
bgp_process (bgp, rn, afi, safi);
@@ -2991,7 +3124,13 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id,
}
if (ri)
- bgp_rib_remove (rn, ri, peer, afi, safi);
+ {
+ /* If this is an EVPN route, un-import it as it is now filtered. */
+ if (safi == SAFI_EVPN)
+ bgp_evpn_unimport_route (bgp, afi, safi, p, ri);
+
+ bgp_rib_remove (rn, ri, peer, afi, safi);
+ }
bgp_unlock_node (rn);
@@ -3271,7 +3410,12 @@ bgp_clear_route_node (struct work_queue *wq, void *data)
&& ! CHECK_FLAG (ri->flags, BGP_INFO_UNUSEABLE))
bgp_info_set_flag (rn, ri, BGP_INFO_STALE);
else
- bgp_rib_remove (rn, ri, peer, afi, safi);
+ {
+ /* If this is an EVPN route, process for un-import. */
+ if (safi == SAFI_EVPN)
+ bgp_evpn_unimport_route (peer->bgp, afi, safi, &rn->p, ri);
+ bgp_rib_remove (rn, ri, peer, afi, safi);
+ }
}
return WQ_SUCCESS;
}