summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_attr.h1
-rw-r--r--bgpd/bgp_ecommunity.c41
-rw-r--r--bgpd/bgp_ecommunity.h2
-rw-r--r--bgpd/bgp_route.c32
-rw-r--r--bgpd/bgp_routemap.c4
5 files changed, 76 insertions, 4 deletions
diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h
index 5fec4be29c..a6964c4653 100644
--- a/bgpd/bgp_attr.h
+++ b/bgpd/bgp_attr.h
@@ -263,6 +263,7 @@ struct attr {
#define BATTR_RMAP_IPV6_GLOBAL_NHOP_CHANGED (1 << 4)
#define BATTR_RMAP_IPV6_LL_NHOP_CHANGED (1 << 5)
#define BATTR_RMAP_IPV6_PREFER_GLOBAL_CHANGED (1 << 6)
+#define BATTR_RMAP_LINK_BW_SET (1 << 7)
/* Router Reflector related structure. */
struct cluster_list {
diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c
index 0fbbc885b9..1ab59e62cb 100644
--- a/bgpd/bgp_ecommunity.c
+++ b/bgpd/bgp_ecommunity.c
@@ -1268,3 +1268,44 @@ const uint8_t *ecommunity_linkbw_present(struct ecommunity *ecom, uint32_t *bw)
return NULL;
}
+
+
+struct ecommunity *ecommunity_replace_linkbw(as_t as,
+ struct ecommunity *ecom,
+ uint64_t cum_bw)
+{
+ struct ecommunity *new;
+ struct ecommunity_val lb_eval;
+ const uint8_t *eval;
+ uint8_t type;
+ uint32_t cur_bw;
+
+ /* Nothing to replace if link-bandwidth doesn't exist or
+ * is non-transitive - just return existing extcommunity.
+ */
+ new = ecom;
+ if (!ecom || !ecom->size)
+ return new;
+
+ eval = ecommunity_linkbw_present(ecom, &cur_bw);
+ if (!eval)
+ return new;
+
+ type = *eval;
+ if (type & ECOMMUNITY_FLAG_NON_TRANSITIVE)
+ return new;
+
+ /* Transitive link-bandwidth exists, replace with the passed
+ * (cumulative) bandwidth value. We need to create a new
+ * extcommunity for this - refer to AS-Path replace function
+ * for reference.
+ */
+ if (cum_bw > 0xFFFFFFFF)
+ cum_bw = 0xFFFFFFFF;
+ encode_lb_extcomm(as > BGP_AS_MAX ? BGP_AS_TRANS : as, cum_bw,
+ false, &lb_eval);
+ new = ecommunity_dup(ecom);
+ ecommunity_add_val(new, &lb_eval, true, true);
+
+ return new;
+}
diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h
index 094c677ff9..7deae8e746 100644
--- a/bgpd/bgp_ecommunity.h
+++ b/bgpd/bgp_ecommunity.h
@@ -238,6 +238,8 @@ extern void bgp_remove_ecomm_from_aggregate_hash(
extern void bgp_aggr_ecommunity_remove(void *arg);
extern const uint8_t *ecommunity_linkbw_present(struct ecommunity *ecom,
uint32_t *bw);
+extern struct ecommunity *ecommunity_replace_linkbw(as_t as,
+ struct ecommunity *ecom, uint64_t cum_bw);
static inline void ecommunity_strip_rts(struct ecommunity *ecom)
{
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index e853351928..8bb4bb075b 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -1560,6 +1560,8 @@ bool subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi,
afi_t afi;
safi_t safi;
int samepeer_safe = 0; /* for synthetic mplsvpns routes */
+ bool nh_reset = false;
+ uint64_t cum_bw;
if (DISABLE_BGP_ANNOUNCE)
return false;
@@ -1983,12 +1985,14 @@ bool subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi,
PEER_FLAG_FORCE_NEXTHOP_SELF)) {
if (!reflect
|| CHECK_FLAG(peer->af_flags[afi][safi],
- PEER_FLAG_FORCE_NEXTHOP_SELF))
+ PEER_FLAG_FORCE_NEXTHOP_SELF)) {
subgroup_announce_reset_nhop(
(peer_cap_enhe(peer, afi, safi)
? AF_INET6
: p->family),
attr);
+ nh_reset = true;
+ }
} else if (peer->sort == BGP_PEER_EBGP) {
/* Can also reset the nexthop if announcing to EBGP, but
* only if
@@ -1999,22 +2003,26 @@ bool subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi,
if ((p->family == AF_INET) &&
(!bgp_subgrp_multiaccess_check_v4(
piattr->nexthop,
- subgrp, from)))
+ subgrp, from))) {
subgroup_announce_reset_nhop(
(peer_cap_enhe(peer, afi, safi)
? AF_INET6
: p->family),
attr);
+ nh_reset = true;
+ }
if ((p->family == AF_INET6) &&
(!bgp_subgrp_multiaccess_check_v6(
piattr->mp_nexthop_global,
- subgrp, from)))
+ subgrp, from))) {
subgroup_announce_reset_nhop(
(peer_cap_enhe(peer, afi, safi)
? AF_INET6
: p->family),
attr);
+ nh_reset = true;
+ }
@@ -2032,6 +2040,7 @@ bool subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi,
"%s: BGP_PATH_ANNC_NH_SELF, family=%s",
__func__, family2str(family));
subgroup_announce_reset_nhop(family, attr);
+ nh_reset = true;
}
}
@@ -2044,10 +2053,25 @@ bool subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi,
* the same interface.
*/
if (p->family == AF_INET6 || peer_cap_enhe(peer, afi, safi)) {
- if (IN6_IS_ADDR_LINKLOCAL(&attr->mp_nexthop_global))
+ if (IN6_IS_ADDR_LINKLOCAL(&attr->mp_nexthop_global)) {
subgroup_announce_reset_nhop(AF_INET6, attr);
+ nh_reset = true;
+ }
}
+ /*
+ * When the next hop is set to ourselves, if all multipaths have
+ * link-bandwidth announce the cumulative bandwidth as that makes
+ * the most sense. However, don't modify if the link-bandwidth has
+ * been explicitly set by user policy.
+ */
+ if (nh_reset &&
+ bgp_path_info_mpath_chkwtd(pi) &&
+ (cum_bw = bgp_path_info_mpath_cumbw(pi)) != 0 &&
+ !CHECK_FLAG(attr->rmap_change_flags, BATTR_RMAP_LINK_BW_SET))
+ attr->ecommunity = ecommunity_replace_linkbw(
+ bgp->as, attr->ecommunity, cum_bw);
+
return true;
}
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
index 7552ff2a66..7f23f6beca 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -2609,6 +2609,10 @@ route_set_ecommunity_lb(void *rule, const struct prefix *prefix,
path->attr->ecommunity = new_ecom;
path->attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES);
+ /* Mark that route-map has set link bandwidth; used in attribute
+ * setting decisions.
+ */
+ SET_FLAG(path->attr->rmap_change_flags, BATTR_RMAP_LINK_BW_SET);
return RMAP_OKAY;
}