diff options
90 files changed, 1670 insertions, 480 deletions
diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c index 4c1615a5c6..a86b42e250 100644 --- a/bgpd/bgp_aspath.c +++ b/bgpd/bgp_aspath.c @@ -297,6 +297,8 @@ static struct aspath *aspath_new(enum asnotation_mode asnotation) as = XCALLOC(MTYPE_AS_PATH, sizeof(struct aspath)); as->asnotation = asnotation; + as->count = 0; + return as; } @@ -400,6 +402,11 @@ unsigned int aspath_count_confeds(struct aspath *aspath) unsigned int aspath_count_hops(const struct aspath *aspath) { + return aspath->count; +} + +static unsigned int aspath_count_hops_internal(const struct aspath *aspath) +{ int count = 0; struct assegment *seg = aspath->segments; @@ -708,6 +715,7 @@ struct aspath *aspath_dup(struct aspath *aspath) else new->str[0] = '\0'; + new->count = aspath->count; return new; } @@ -729,6 +737,7 @@ static void *aspath_hash_alloc(void *arg) new->str_len = aspath->str_len; new->json = aspath->json; new->asnotation = aspath->asnotation; + new->count = aspath->count; return new; } @@ -856,6 +865,8 @@ struct aspath *aspath_parse(struct stream *s, size_t length, int use32bit, if (assegments_parse(s, length, &as.segments, use32bit) < 0) return NULL; + as.count = aspath_count_hops_internal(&as); + /* If already same aspath exist then return it. */ find = hash_get(ashash, &as, aspath_hash_alloc); @@ -1032,7 +1043,7 @@ static struct assegment *aspath_aggregate_as_set_add(struct aspath *aspath, asset->as[asset->length - 1] = as; } - + aspath->count = aspath_count_hops_internal(aspath); return asset; } @@ -1113,6 +1124,8 @@ struct aspath *aspath_aggregate(struct aspath *as1, struct aspath *as2) assegment_normalise(aspath->segments); aspath_str_update(aspath, false); + aspath->count = aspath_count_hops_internal(aspath); + return aspath; } @@ -1268,6 +1281,7 @@ struct aspath *aspath_replace_regex_asn(struct aspath *aspath, } aspath_str_update(new, false); + new->count = aspath_count_hops_internal(new); return new; } @@ -1293,6 +1307,8 @@ struct aspath *aspath_replace_specific_asn(struct aspath *aspath, } aspath_str_update(new, false); + new->count = aspath_count_hops_internal(new); + return new; } @@ -1315,6 +1331,8 @@ struct aspath *aspath_replace_all_asn(struct aspath *aspath, as_t our_asn) } aspath_str_update(new, false); + new->count = aspath_count_hops_internal(new); + return new; } @@ -1341,6 +1359,8 @@ struct aspath *aspath_replace_private_asns(struct aspath *aspath, as_t asn, } aspath_str_update(new, false); + new->count = aspath_count_hops_internal(new); + return new; } @@ -1413,6 +1433,7 @@ struct aspath *aspath_remove_private_asns(struct aspath *aspath, as_t peer_asn) if (!aspath->refcnt) aspath_free(aspath); aspath_str_update(new, false); + new->count = aspath_count_hops_internal(new); return new; } @@ -1469,6 +1490,7 @@ static struct aspath *aspath_merge(struct aspath *as1, struct aspath *as2) last->next = as2->segments; as2->segments = new; aspath_str_update(as2, false); + as2->count = aspath_count_hops_internal(as2); return as2; } @@ -1486,6 +1508,7 @@ struct aspath *aspath_prepend(struct aspath *as1, struct aspath *as2) if (as2->segments == NULL) { as2->segments = assegment_dup_all(as1->segments); aspath_str_update(as2, false); + as2->count = aspath_count_hops_internal(as2); return as2; } @@ -1506,6 +1529,7 @@ struct aspath *aspath_prepend(struct aspath *as1, struct aspath *as2) if (!as2->segments) { as2->segments = assegment_dup_all(as1->segments); aspath_str_update(as2, false); + as2->count = aspath_count_hops_internal(as2); return as2; } @@ -1551,6 +1575,7 @@ struct aspath *aspath_prepend(struct aspath *as1, struct aspath *as2) * the inbetween AS_SEQUENCE of seg2 in the process */ aspath_str_update(as2, false); + as2->count = aspath_count_hops_internal(as2); return as2; } else { /* AS_SET merge code is needed at here. */ @@ -1662,6 +1687,7 @@ struct aspath *aspath_filter_exclude(struct aspath *source, lastseg = newseg; } aspath_str_update(newpath, false); + newpath->count = aspath_count_hops_internal(newpath); /* We are happy returning even an empty AS_PATH, because the * administrator * might expect this very behaviour. There's a mean to avoid this, if @@ -1680,6 +1706,7 @@ struct aspath *aspath_filter_exclude_all(struct aspath *source) newpath = aspath_new(source->asnotation); aspath_str_update(newpath, false); + newpath->count = aspath_count_hops_internal(newpath); /* We are happy returning even an empty AS_PATH, because the * administrator * might expect this very behaviour. There's a mean to avoid this, if @@ -1767,6 +1794,7 @@ struct aspath *aspath_filter_exclude_acl(struct aspath *source, aspath_str_update(source, false); + source->count = aspath_count_hops_internal(source); /* We are happy returning even an empty AS_PATH, because the * administrator * might expect this very behaviour. There's a mean to avoid this, if @@ -1805,6 +1833,7 @@ static struct aspath *aspath_add_asns(struct aspath *aspath, as_t asno, } aspath_str_update(aspath, false); + aspath->count = aspath_count_hops_internal(aspath); return aspath; } @@ -1896,6 +1925,7 @@ struct aspath *aspath_reconcile_as4(struct aspath *aspath, if (!hops) { newpath = aspath_dup(as4path); aspath_str_update(newpath, false); + /* dup sets the count properly */ return newpath; } @@ -1957,6 +1987,7 @@ struct aspath *aspath_reconcile_as4(struct aspath *aspath, aspath_free(newpath); mergedpath->segments = assegment_normalise(mergedpath->segments); aspath_str_update(mergedpath, false); + mergedpath->count = aspath_count_hops_internal(mergedpath); if (BGP_DEBUG(as4, AS4)) zlog_debug("[AS4] result of synthesizing is %s", @@ -2027,8 +2058,10 @@ struct aspath *aspath_delete_confed_seq(struct aspath *aspath) seg = next; } - if (removed_confed_segment) + if (removed_confed_segment) { aspath_str_update(aspath, false); + aspath->count = aspath_count_hops_internal(aspath); + } return aspath; } diff --git a/bgpd/bgp_aspath.h b/bgpd/bgp_aspath.h index f7e57fd66d..46202fd34a 100644 --- a/bgpd/bgp_aspath.h +++ b/bgpd/bgp_aspath.h @@ -59,6 +59,7 @@ struct aspath { and AS path regular expression match. */ char *str; unsigned short str_len; + uint32_t count; /* AS notation used by string expression of AS path */ enum asnotation_mode asnotation; diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 596d820f1b..b62319b211 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -480,12 +480,11 @@ static bool bgp_attr_aigp_get_tlv_metric(uint8_t *pnt, int length, return false; } -static void stream_put_bgp_aigp_tlv_metric(struct stream *s, - struct bgp_path_info *bpi) +static void stream_put_bgp_aigp_tlv_metric(struct stream *s, uint64_t aigp) { stream_putc(s, BGP_AIGP_TLV_METRIC); stream_putw(s, BGP_AIGP_TLV_METRIC_LEN); - stream_putq(s, bgp_aigp_metric_total(bpi)); + stream_putq(s, aigp); } static bool bgp_attr_aigp_valid(uint8_t *pnt, int length) @@ -4483,77 +4482,30 @@ static bool bgp_append_local_as(struct peer *peer, afi_t afi, safi_t safi) } static void bgp_packet_ecommunity_attribute(struct stream *s, struct peer *peer, - struct ecommunity *ecomm, - bool transparent, int attribute) + struct ecommunity *ecomm, int attribute) { - if (peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED || - peer->sub_sort == BGP_PEER_EBGP_OAD || transparent) { - if (ecomm->size * ecomm->unit_size > 255) { - stream_putc(s, BGP_ATTR_FLAG_OPTIONAL | - BGP_ATTR_FLAG_TRANS | - BGP_ATTR_FLAG_EXTLEN); - stream_putc(s, attribute); - stream_putw(s, ecomm->size * ecomm->unit_size); - } else { - stream_putc(s, BGP_ATTR_FLAG_OPTIONAL | - BGP_ATTR_FLAG_TRANS); - stream_putc(s, attribute); - stream_putc(s, ecomm->size * ecomm->unit_size); - } - stream_put(s, ecomm->val, ecomm->size * ecomm->unit_size); - } else { - uint8_t *pnt; - int tbit; - int ecom_tr_size = 0; - uint32_t i; - - for (i = 0; i < ecomm->size; i++) { - pnt = ecomm->val + (i * ecomm->unit_size); - tbit = *pnt; - - if (CHECK_FLAG(tbit, ECOMMUNITY_FLAG_NON_TRANSITIVE)) - continue; - - ecom_tr_size++; - } - - if (ecom_tr_size) { - if (ecom_tr_size * ecomm->unit_size > 255) { - stream_putc(s, BGP_ATTR_FLAG_OPTIONAL | - BGP_ATTR_FLAG_TRANS | - BGP_ATTR_FLAG_EXTLEN); - stream_putc(s, attribute); - stream_putw(s, ecom_tr_size * ecomm->unit_size); - } else { - stream_putc(s, BGP_ATTR_FLAG_OPTIONAL | - BGP_ATTR_FLAG_TRANS); - stream_putc(s, attribute); - stream_putc(s, ecom_tr_size * ecomm->unit_size); - } - - for (i = 0; i < ecomm->size; i++) { - pnt = ecomm->val + (i * ecomm->unit_size); - tbit = *pnt; - - if (CHECK_FLAG(tbit, - ECOMMUNITY_FLAG_NON_TRANSITIVE)) - continue; + if (!ecomm || !ecomm->size) + return; - stream_put(s, pnt, ecomm->unit_size); - } - } + if (ecomm->size * ecomm->unit_size > 255) { + stream_putc(s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_EXTLEN); + stream_putc(s, attribute); + stream_putw(s, ecomm->size * ecomm->unit_size); + } else { + stream_putc(s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS); + stream_putc(s, attribute); + stream_putc(s, ecomm->size * ecomm->unit_size); } + + stream_put(s, ecomm->val, ecomm->size * ecomm->unit_size); } /* Make attribute packet. */ -bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, - struct stream *s, struct attr *attr, - struct bpacket_attr_vec_arr *vecarr, - struct prefix *p, afi_t afi, safi_t safi, - struct peer *from, struct prefix_rd *prd, - mpls_label_t *label, uint8_t num_labels, - bool addpath_capable, uint32_t addpath_tx_id, - struct bgp_path_info *bpi) +bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, struct stream *s, + struct attr *attr, struct bpacket_attr_vec_arr *vecarr, + struct prefix *p, afi_t afi, safi_t safi, struct peer *from, + struct prefix_rd *prd, mpls_label_t *label, uint8_t num_labels, + bool addpath_capable, uint32_t addpath_tx_id) { size_t cp; size_t aspath_sizep; @@ -4852,19 +4804,11 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, /* Extended IPv6/Communities attributes. */ if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY)) { - bool transparent = CHECK_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_RSERVER_CLIENT) && - from && - CHECK_FLAG(from->af_flags[afi][safi], - PEER_FLAG_RSERVER_CLIENT); - if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES))) { struct ecommunity *ecomm = bgp_attr_get_ecommunity(attr); - bgp_packet_ecommunity_attribute(s, peer, ecomm, - transparent, - BGP_ATTR_EXT_COMMUNITIES); + bgp_packet_ecommunity_attribute(s, peer, ecomm, BGP_ATTR_EXT_COMMUNITIES); } if (CHECK_FLAG(attr->flag, @@ -4873,7 +4817,6 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, bgp_attr_get_ipv6_ecommunity(attr); bgp_packet_ecommunity_attribute(s, peer, ecomm, - transparent, BGP_ATTR_IPV6_EXT_COMMUNITIES); } } @@ -5032,10 +4975,7 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, } /* AIGP */ - if (bpi && CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_AIGP)) && - (CHECK_FLAG(peer->flags, PEER_FLAG_AIGP) || - peer->sub_sort == BGP_PEER_EBGP_OAD || - peer->sort != BGP_PEER_EBGP)) { + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_AIGP)) && AIGP_TRANSMIT_ALLOWED(peer)) { /* At the moment only AIGP Metric TLV exists for AIGP * attribute. If more comes in, do not forget to update * attr_len variable to include new ones. @@ -5045,7 +4985,7 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, stream_putc(s, BGP_ATTR_FLAG_OPTIONAL); stream_putc(s, BGP_ATTR_AIGP); stream_putc(s, attr_len); - stream_put_bgp_aigp_tlv_metric(s, bpi); + stream_put_bgp_aigp_tlv_metric(s, attr->aigp_metric); } /* Unknown transit attribute. */ @@ -5314,7 +5254,7 @@ void bgp_dump_routes_attr(struct stream *s, struct bgp_path_info *bpi, stream_putc(s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS); stream_putc(s, BGP_ATTR_AIGP); stream_putc(s, attr_len); - stream_put_bgp_aigp_tlv_metric(s, bpi); + stream_put_bgp_aigp_tlv_metric(s, attr->aigp_metric); } /* Return total size of attribute. */ diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index 0a43399be6..c5532f4005 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -387,12 +387,12 @@ extern struct attr *bgp_attr_aggregate_intern( struct community *community, struct ecommunity *ecommunity, struct lcommunity *lcommunity, struct bgp_aggregate *aggregate, uint8_t atomic_aggregate, const struct prefix *p); -extern bgp_size_t bgp_packet_attribute( - struct bgp *bgp, struct peer *peer, struct stream *s, struct attr *attr, - struct bpacket_attr_vec_arr *vecarr, struct prefix *p, afi_t afi, - safi_t safi, struct peer *from, struct prefix_rd *prd, - mpls_label_t *label, uint8_t num_labels, bool addpath_capable, - uint32_t addpath_tx_id, struct bgp_path_info *bpi); +extern bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, struct stream *s, + struct attr *attr, struct bpacket_attr_vec_arr *vecarr, + struct prefix *p, afi_t afi, safi_t safi, struct peer *from, + struct prefix_rd *prd, mpls_label_t *label, + uint8_t num_labels, bool addpath_capable, + uint32_t addpath_tx_id); extern void bgp_dump_routes_attr(struct stream *s, struct bgp_path_info *bpi, const struct prefix *p); extern bool attrhash_cmp(const void *arg1, const void *arg2); @@ -515,7 +515,7 @@ static inline void bgp_attr_set_ecommunity(struct attr *attr, { attr->ecommunity = ecomm; - if (ecomm) + if (ecomm && ecomm->size) SET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)); else UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)); @@ -566,7 +566,7 @@ static inline void bgp_attr_set_ipv6_ecommunity(struct attr *attr, { attr->ipv6_ecommunity = ipv6_ecomm; - if (ipv6_ecomm) + if (ipv6_ecomm && ipv6_ecomm->size) SET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_IPV6_EXT_COMMUNITIES)); else @@ -585,6 +585,10 @@ static inline void bgp_attr_set_transit(struct attr *attr, attr->transit = transit; } +#define AIGP_TRANSMIT_ALLOWED(peer) \ + (CHECK_FLAG((peer)->flags, PEER_FLAG_AIGP) || ((peer)->sub_sort == BGP_PEER_EBGP_OAD) || \ + ((peer)->sort != BGP_PEER_EBGP)) + static inline uint64_t bgp_attr_get_aigp_metric(const struct attr *attr) { return attr->aigp_metric; diff --git a/bgpd/bgp_bmp.c b/bgpd/bgp_bmp.c index 59efb35235..4d9e580a23 100644 --- a/bgpd/bgp_bmp.c +++ b/bgpd/bgp_bmp.c @@ -1016,9 +1016,8 @@ static struct stream *bmp_update(const struct prefix *p, struct prefix_rd *prd, stream_putw(s, 0); /* 5: Encode all the attributes, except MP_REACH_NLRI attr. */ - total_attr_len = - bgp_packet_attribute(NULL, peer, s, attr, &vecarr, NULL, afi, - safi, peer, NULL, NULL, 0, 0, 0, NULL); + total_attr_len = bgp_packet_attribute(NULL, peer, s, attr, &vecarr, NULL, afi, safi, peer, + NULL, NULL, 0, 0, 0); /* space check? */ @@ -1983,6 +1982,8 @@ static void bmp_bgp_peer_vrf(struct bmp_bgp_peer *bbpeer, struct bgp *bgp) bbpeer->open_tx_len = open_len; bbpeer->open_tx = bbpeer->open_rx; + + stream_free(s); } /* update the vrf status of the bmpbgp struct for vrf peer up/down diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index 97c3e5740f..097d3684f6 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -449,6 +449,10 @@ bool bgp_dump_attr(struct attr *attr, char *buf, size_t size) ", extcommunity %s", ecommunity_str(bgp_attr_get_ecommunity(attr))); + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_IPV6_EXT_COMMUNITIES))) + snprintf(buf + strlen(buf), size - strlen(buf), ", ipv6-extcommunity %s", + ecommunity_str(bgp_attr_get_ipv6_ecommunity(attr))); + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE))) snprintf(buf + strlen(buf), size - strlen(buf), ", atomic-aggregate"); diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index 547dcdf7f3..df32d75103 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -1408,9 +1408,12 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) "FS:marking %u", *(pnt + 5)); } else unk_ecom = true; - } else if (type == ECOMMUNITY_ENCODE_AS_NON_TRANS) { + } else if (CHECK_FLAG(type, ECOMMUNITY_FLAG_NON_TRANSITIVE) || + type == ECOMMUNITY_ENCODE_OPAQUE_NON_TRANS) { sub_type = *pnt++; - if (sub_type == ECOMMUNITY_LINK_BANDWIDTH) + if (sub_type == ECOMMUNITY_ORIGIN_VALIDATION_STATE) + ecommunity_origin_validation_state_str(encbuf, sizeof(encbuf), pnt); + else if (sub_type == ECOMMUNITY_LINK_BANDWIDTH) ecommunity_lb_str(encbuf, sizeof(encbuf), pnt, ecom->disable_ieee_floating); else if (sub_type == ECOMMUNITY_EXTENDED_LINK_BANDWIDTH) @@ -1418,20 +1421,13 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) pnt, len); else unk_ecom = true; - } else if (type == ECOMMUNITY_ENCODE_IP_NON_TRANS) { + } else if (CHECK_FLAG(type, ECOMMUNITY_ENCODE_IP_NON_TRANS)) { sub_type = *pnt++; if (sub_type == ECOMMUNITY_NODE_TARGET) ecommunity_node_target_str( encbuf, sizeof(encbuf), pnt, format); else unk_ecom = true; - } else if (type == ECOMMUNITY_ENCODE_OPAQUE_NON_TRANS) { - sub_type = *pnt++; - if (sub_type == ECOMMUNITY_ORIGIN_VALIDATION_STATE) - ecommunity_origin_validation_state_str( - encbuf, sizeof(encbuf), pnt); - else - unk_ecom = true; } else { sub_type = *pnt++; unk_ecom = true; @@ -1587,6 +1583,57 @@ bool ecommunity_strip(struct ecommunity *ecom, uint8_t type, return true; } +static bool ecommunity_non_transitive(uint8_t type) +{ + return (CHECK_FLAG(type, ECOMMUNITY_FLAG_NON_TRANSITIVE) || + CHECK_FLAG(type, ECOMMUNITY_ENCODE_IP_NON_TRANS) || + type == ECOMMUNITY_ENCODE_OPAQUE_NON_TRANS); +} + +/* Delete all non-transitive extended communities */ +bool ecommunity_strip_non_transitive(struct ecommunity *ecom) +{ + uint8_t *p, *q, *new; + uint32_t c, found = 0; + + if (!ecom || !ecom->val) + return false; + + /* Certain extended communities like the Route Target can be present + * multiple times, handle that. + */ + c = 0; + for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) + if (ecommunity_non_transitive(*p)) + found++; + + if (!found) + return false; + + /* Handle the case where everything needs to be stripped. */ + if (found == ecom->size) { + XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val); + ecom->size = 0; + return true; + } + + /* Strip extended communities with non-transitive flag set */ + new = XMALLOC(MTYPE_ECOMMUNITY_VAL, (ecom->size - found) * ecom->unit_size); + q = new; + for (c = 0, p = ecom->val; c < ecom->size; c++, p += ecom->unit_size) { + if (!ecommunity_non_transitive(*p)) { + memcpy(q, p, ecom->unit_size); + q += ecom->unit_size; + } + } + + XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val); + ecom->val = new; + ecom->size -= found; + + return true; +} + /* * Remove specified extended community value from extended community. * Returns 1 if value was present (and hence, removed), 0 otherwise. @@ -1883,9 +1930,7 @@ const uint8_t *ecommunity_linkbw_present(struct ecommunity *ecom, uint64_t *bw) if (len < ecom->unit_size) return NULL; - if ((type == ECOMMUNITY_ENCODE_AS || - type == ECOMMUNITY_ENCODE_AS_NON_TRANS) && - sub_type == ECOMMUNITY_LINK_BANDWIDTH) { + if ((type == ECOMMUNITY_ENCODE_AS) && sub_type == ECOMMUNITY_LINK_BANDWIDTH) { uint32_t bwval; pnt += 2; /* bandwidth is encoded as AS:val */ diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h index 67c16aeb9e..0544cbd316 100644 --- a/bgpd/bgp_ecommunity.h +++ b/bgpd/bgp_ecommunity.h @@ -32,9 +32,7 @@ #define ECOMMUNITY_EXTENDED_COMMUNITY_PART_3 0x82 /* Non-transitive extended community types. */ -#define ECOMMUNITY_ENCODE_AS_NON_TRANS 0x40 #define ECOMMUNITY_ENCODE_IP_NON_TRANS 0x41 -#define ECOMMUNITY_ENCODE_AS4_NON_TRANS 0x42 #define ECOMMUNITY_ENCODE_OPAQUE_NON_TRANS 0x43 /* Low-order octet of the Extended Communities type field. */ @@ -398,6 +396,7 @@ extern struct ecommunity *ecommunity_new(void); extern bool ecommunity_strip(struct ecommunity *ecom, uint8_t type, uint8_t subtype); extern struct ecommunity *ecommunity_new(void); +extern bool ecommunity_strip_non_transitive(struct ecommunity *ecom); extern bool ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval); struct bgp_pbr_entry_action; diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index 567af5bb75..b3518ac696 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -178,6 +178,7 @@ static struct peer *peer_xfer_conn(struct peer *from_peer) EVENT_OFF(going_away->t_delayopen); EVENT_OFF(going_away->t_connect_check_r); EVENT_OFF(going_away->t_connect_check_w); + EVENT_OFF(going_away->t_stop_with_notify); EVENT_OFF(keeper->t_routeadv); EVENT_OFF(keeper->t_connect); EVENT_OFF(keeper->t_delayopen); @@ -1475,6 +1476,8 @@ enum bgp_fsm_state_progress bgp_stop(struct peer_connection *connection) EVENT_OFF(connection->t_connect_check_r); EVENT_OFF(connection->t_connect_check_w); + EVENT_OFF(connection->t_stop_with_notify); + /* Stop all timers. */ EVENT_OFF(connection->t_start); EVENT_OFF(connection->t_connect); @@ -3032,3 +3035,10 @@ void bgp_peer_gr_flags_update(struct peer *peer) } } } + +void bgp_event_stop_with_notify(struct event *event) +{ + struct peer_connection *connection = EVENT_ARG(event); + + bgp_stop_with_notify(connection, BGP_NOTIFY_SEND_HOLD_ERR, 0); +} diff --git a/bgpd/bgp_fsm.h b/bgpd/bgp_fsm.h index 85c488962f..013c60ce23 100644 --- a/bgpd/bgp_fsm.h +++ b/bgpd/bgp_fsm.h @@ -109,6 +109,7 @@ enum bgp_fsm_state_progress { extern void bgp_fsm_nht_update(struct peer_connection *connection, struct peer *peer, bool has_valid_nexthops); extern void bgp_event(struct event *event); +extern void bgp_event_stop_with_notify(struct event *event); extern int bgp_event_update(struct peer_connection *connection, enum bgp_fsm_events event); extern enum bgp_fsm_state_progress bgp_stop(struct peer_connection *connection); diff --git a/bgpd/bgp_label.c b/bgpd/bgp_label.c index af10111600..5db3621738 100644 --- a/bgpd/bgp_label.c +++ b/bgpd/bgp_label.c @@ -38,7 +38,7 @@ static void *bgp_labels_hash_alloc(void *p) struct bgp_labels *new; uint8_t i; - new = XMALLOC(MTYPE_BGP_LABELS, sizeof(struct bgp_labels)); + new = XCALLOC(MTYPE_BGP_LABELS, sizeof(struct bgp_labels)); new->num_labels = labels->num_labels; for (i = 0; i < labels->num_labels; i++) diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 13da55ffb7..df45cf237e 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -1613,8 +1613,8 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */ struct attr static_attr = {0}; struct attr *new_attr = NULL; safi_t safi = SAFI_MPLS_VPN; - mpls_label_t label_val; - mpls_label_t label; + mpls_label_t label_val = { 0 }; + mpls_label_t label = { 0 }; struct bgp_dest *bn; const char *debugmsg; int nexthop_self_flag = 0; diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index 59566ee6d6..9b633b7139 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -652,11 +652,12 @@ static void bgp_process_nexthop_update(struct bgp_nexthop_cache *bnc, * we receive from bgp. This is to allow us * to work with v4 routing over v6 nexthops */ - if (peer && !peer->ifp - && CHECK_FLAG(peer->flags, - PEER_FLAG_CAPABILITY_ENHE) - && nhr->prefix.family == AF_INET6 - && nexthop->type != NEXTHOP_TYPE_BLACKHOLE) { + if (peer && !peer->ifp && + CHECK_FLAG(peer->flags, PEER_FLAG_CAPABILITY_ENHE) && + !CHECK_FLAG(bnc->bgp->flags, + BGP_FLAG_IPV6_NO_AUTO_RA) && + nhr->prefix.family == AF_INET6 && + nexthop->type != NEXTHOP_TYPE_BLACKHOLE) { struct interface *ifp; ifp = if_lookup_by_index(nexthop->ifindex, @@ -1529,6 +1530,10 @@ void bgp_nht_reg_enhe_cap_intfs(struct peer *peer) return; bgp = peer->bgp; + + if (CHECK_FLAG(bgp->flags, BGP_FLAG_IPV6_NO_AUTO_RA)) + return; + if (!sockunion2hostprefix(&peer->connection->su, &p)) { zlog_warn("%s: Unable to convert sockunion to prefix for %s", __func__, peer->host); diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 6b116db107..0523a4b02b 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -122,42 +122,38 @@ static void bgp_packet_add(struct peer_connection *connection, peer->last_sendq_ok = monotime(NULL); stream_fifo_push(connection->obuf, s); + } - delta = monotime(NULL) - peer->last_sendq_ok; + delta = monotime(NULL) - peer->last_sendq_ok; - if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER)) - holdtime = atomic_load_explicit(&peer->holdtime, - memory_order_relaxed); - else - holdtime = peer->bgp->default_holdtime; + if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER)) + holdtime = atomic_load_explicit(&peer->holdtime, memory_order_relaxed); + else + holdtime = peer->bgp->default_holdtime; - sendholdtime = holdtime * 2; + sendholdtime = holdtime * 2; - /* Note that when we're here, we're adding some packet to the - * OutQ. That includes keepalives when there is nothing to - * do, so there's a guarantee we pass by here once in a while. - * - * That implies there is no need to go set up another separate - * timer that ticks down SendHoldTime, as we'll be here sooner - * or later anyway and will see the checks below failing. - */ - if (!holdtime) { - /* no holdtime, do nothing. */ - } else if (delta > sendholdtime) { - flog_err( - EC_BGP_SENDQ_STUCK_PROPER, - "%pBP has not made any SendQ progress for 2 holdtimes (%jds), terminating session", - peer, sendholdtime); - bgp_stop_with_notify(connection, - BGP_NOTIFY_SEND_HOLD_ERR, 0); - } else if (delta > (intmax_t)holdtime && - monotime(NULL) - peer->last_sendq_warn > 5) { - flog_warn( - EC_BGP_SENDQ_STUCK_WARN, - "%pBP has not made any SendQ progress for 1 holdtime (%us), peer overloaded?", - peer, holdtime); - peer->last_sendq_warn = monotime(NULL); - } + /* Note that when we're here, we're adding some packet to the + * OutQ. That includes keepalives when there is nothing to + * do, so there's a guarantee we pass by here once in a while. + * + * That implies there is no need to go set up another separate + * timer that ticks down SendHoldTime, as we'll be here sooner + * or later anyway and will see the checks below failing. + */ + if (!holdtime) { + /* no holdtime, do nothing. */ + } else if (delta > sendholdtime) { + flog_err(EC_BGP_SENDQ_STUCK_PROPER, + "%pBP has not made any SendQ progress for 2 holdtimes (%jds), terminating session", + peer, sendholdtime); + event_add_event(bm->master, bgp_event_stop_with_notify, connection, 0, + &connection->t_stop_with_notify); + } else if (delta > (intmax_t)holdtime && monotime(NULL) - peer->last_sendq_warn > 5) { + flog_warn(EC_BGP_SENDQ_STUCK_WARN, + "%pBP has not made any SendQ progress for 1 holdtime (%us), peer overloaded?", + peer, holdtime); + peer->last_sendq_warn = monotime(NULL); } } diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 551547b7c0..429d1c8ac7 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -1064,7 +1064,32 @@ int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } } - /* Tie-breaker - AIGP (Metric TLV) attribute */ + /* 3. Local route check. We prefer: + * - BGP_ROUTE_STATIC + * - BGP_ROUTE_AGGREGATE + * - BGP_ROUTE_REDISTRIBUTE + */ + new_origin = !(new->sub_type == BGP_ROUTE_NORMAL || new->sub_type == BGP_ROUTE_IMPORTED); + exist_origin = !(exist->sub_type == BGP_ROUTE_NORMAL || + exist->sub_type == BGP_ROUTE_IMPORTED); + + if (new_origin && !exist_origin) { + *reason = bgp_path_selection_local_route; + if (debug) + zlog_debug("%s: %s wins over %s due to preferred BGP_ROUTE type", pfx_buf, + new_buf, exist_buf); + return 1; + } + + if (!new_origin && exist_origin) { + *reason = bgp_path_selection_local_route; + if (debug) + zlog_debug("%s: %s loses to %s due to preferred BGP_ROUTE type", pfx_buf, + new_buf, exist_buf); + return 0; + } + + /* 3.5. Tie-breaker - AIGP (Metric TLV) attribute */ if (CHECK_FLAG(newattr->flag, ATTR_FLAG_BIT(BGP_ATTR_AIGP)) && CHECK_FLAG(existattr->flag, ATTR_FLAG_BIT(BGP_ATTR_AIGP)) && CHECK_FLAG(bgp->flags, BGP_FLAG_COMPARE_AIGP)) { @@ -1094,34 +1119,6 @@ int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } } - /* 3. Local route check. We prefer: - * - BGP_ROUTE_STATIC - * - BGP_ROUTE_AGGREGATE - * - BGP_ROUTE_REDISTRIBUTE - */ - new_origin = !(new->sub_type == BGP_ROUTE_NORMAL || - new->sub_type == BGP_ROUTE_IMPORTED); - exist_origin = !(exist->sub_type == BGP_ROUTE_NORMAL || - exist->sub_type == BGP_ROUTE_IMPORTED); - - if (new_origin && !exist_origin) { - *reason = bgp_path_selection_local_route; - if (debug) - zlog_debug( - "%s: %s wins over %s due to preferred BGP_ROUTE type", - pfx_buf, new_buf, exist_buf); - return 1; - } - - if (!new_origin && exist_origin) { - *reason = bgp_path_selection_local_route; - if (debug) - zlog_debug( - "%s: %s loses to %s due to preferred BGP_ROUTE type", - pfx_buf, new_buf, exist_buf); - return 0; - } - /* Here if these are imported routes then get ultimate pi for * path compare. */ @@ -1133,9 +1130,9 @@ int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, /* 4. AS path length check. */ if (!CHECK_FLAG(bgp->flags, BGP_FLAG_ASPATH_IGNORE)) { int exist_hops = aspath_count_hops(existattr->aspath); - int exist_confeds = aspath_count_confeds(existattr->aspath); if (CHECK_FLAG(bgp->flags, BGP_FLAG_ASPATH_CONFED)) { + int exist_confeds = aspath_count_confeds(existattr->aspath); int aspath_hops; aspath_hops = aspath_count_hops(newattr->aspath); @@ -1779,14 +1776,13 @@ static bool bgp_community_filter(struct peer *peer, struct attr *attr) return true; /* NO_EXPORT check. */ - if (peer->sort == BGP_PEER_EBGP && - community_include(bgp_attr_get_community(attr), - COMMUNITY_NO_EXPORT)) + if (peer->sort == BGP_PEER_EBGP && peer->sub_sort != BGP_PEER_EBGP_OAD && + community_include(bgp_attr_get_community(attr), COMMUNITY_NO_EXPORT)) return true; /* NO_EXPORT_SUBCONFED check. */ - if (peer->sort == BGP_PEER_EBGP - || peer->sort == BGP_PEER_CONFED) + if ((peer->sort == BGP_PEER_EBGP && peer->sub_sort != BGP_PEER_EBGP_OAD) || + peer->sort == BGP_PEER_CONFED) if (community_include(bgp_attr_get_community(attr), COMMUNITY_NO_EXPORT_SUBCONFED)) return true; @@ -2821,6 +2817,65 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi, false)); } + /* + * Adjust AIGP for propagation when the nexthop is set to ourselves, + * e.g., using "set ip nexthop peer-address" or when advertising to + * EBGP. Note in route reflection the nexthop is usually unmodified + * and the AIGP should not be adjusted in that case. + */ + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_AIGP)) && AIGP_TRANSMIT_ALLOWED(peer)) { + if (nh_reset || + CHECK_FLAG(attr->rmap_change_flags, BATTR_RMAP_NEXTHOP_PEER_ADDRESS)) { + uint64_t aigp = bgp_aigp_metric_total(pi); + + bgp_attr_set_aigp_metric(attr, aigp); + } + } + + /* Extended communities can be transitive and non-transitive. + * If the extended community is non-transitive, strip it off, + * unless it's a locally originated route (static, aggregate, + * redistributed, etc.). + */ + if (from->sort == BGP_PEER_EBGP && peer->sort == BGP_PEER_EBGP && + pi->sub_type == BGP_ROUTE_NORMAL) { + struct ecommunity *new_ecomm; + struct ecommunity *old_ecomm; + + old_ecomm = bgp_attr_get_ecommunity(attr); + if (old_ecomm) { + new_ecomm = ecommunity_dup(old_ecomm); + if (ecommunity_strip_non_transitive(new_ecomm)) { + bgp_attr_set_ecommunity(attr, new_ecomm); + if (!old_ecomm->refcnt) + ecommunity_free(&old_ecomm); + if (bgp_debug_update(NULL, p, subgrp->update_group, 0)) + zlog_debug("%pBP: %pFX stripped non-transitive extended communities", + peer, p); + } else { + ecommunity_free(&new_ecomm); + } + } + + /* Extended link-bandwidth communities are encoded as IPv6 + * address-specific extended communities. + */ + old_ecomm = bgp_attr_get_ipv6_ecommunity(attr); + if (old_ecomm) { + new_ecomm = ecommunity_dup(old_ecomm); + if (ecommunity_strip_non_transitive(new_ecomm)) { + bgp_attr_set_ipv6_ecommunity(attr, new_ecomm); + if (!old_ecomm->refcnt) + ecommunity_free(&old_ecomm); + if (bgp_debug_update(NULL, p, subgrp->update_group, 0)) + zlog_debug("%pBP: %pFX stripped non-transitive ipv6 extended communities", + peer, p); + } else { + ecommunity_free(&new_ecomm); + } + } + } + return true; } @@ -4618,10 +4673,12 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, * will not be interned. In which case, it is ok to update the * attr->evpn_overlay, so that, this can be stored in adj_in. */ - if ((afi == AFI_L2VPN) && evpn) - bgp_attr_set_evpn_overlay(attr, evpn); - else - evpn_overlay_free(evpn); + if (evpn) { + if (afi == AFI_L2VPN) + bgp_attr_set_evpn_overlay(attr, evpn); + else + evpn_overlay_free(evpn); + } bgp_adj_in_set(dest, peer, attr, addpath_id, &bgp_labels); } @@ -4797,10 +4854,12 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, * attr->evpn_overlay with evpn directly. Instead memcpy * evpn to new_atr.evpn_overlay before it is interned. */ - if (soft_reconfig && (afi == AFI_L2VPN) && evpn) - bgp_attr_set_evpn_overlay(&new_attr, evpn); - else - evpn_overlay_free(evpn); + if (soft_reconfig && evpn) { + if (afi == AFI_L2VPN) + bgp_attr_set_evpn_overlay(&new_attr, evpn); + else + evpn_overlay_free(evpn); + } /* Apply incoming route-map. * NB: new_attr may now contain newly allocated values from route-map @@ -12270,7 +12329,7 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, mpls_lse_decode(dest->local_label, &label, &ttl, &exp, &bos); - has_valid_label = bgp_is_valid_label(&label); + has_valid_label = bgp_is_valid_label(&dest->local_label); if (safi == SAFI_EVPN) { if (!json) { diff --git a/bgpd/bgp_updgrp_packet.c b/bgpd/bgp_updgrp_packet.c index bed00a6640..3ce136ef87 100644 --- a/bgpd/bgp_updgrp_packet.c +++ b/bgpd/bgp_updgrp_packet.c @@ -738,9 +738,9 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp) /* 5: Encode all the attributes, except MP_REACH_NLRI * attr. */ - total_attr_len = bgp_packet_attribute( - NULL, peer, s, adv->baa->attr, &vecarr, NULL, - afi, safi, from, NULL, NULL, 0, 0, 0, path); + total_attr_len = bgp_packet_attribute(NULL, peer, s, adv->baa->attr, + &vecarr, NULL, afi, safi, from, NULL, + NULL, 0, 0, 0); space_remaining = STREAM_CONCAT_REMAIN(s, snlri, STREAM_SIZE(s)) @@ -1149,12 +1149,9 @@ void subgroup_default_update_packet(struct update_subgroup *subgrp, /* Make place for total attribute length. */ pos = stream_get_endp(s); stream_putw(s, 0); - total_attr_len = - bgp_packet_attribute(NULL, peer, s, attr, &vecarr, &p, afi, - safi, from, NULL, &label, num_labels, - addpath_capable, - BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE, - NULL); + total_attr_len = bgp_packet_attribute(NULL, peer, s, attr, &vecarr, &p, afi, safi, from, + NULL, &label, num_labels, addpath_capable, + BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE); /* Set Total Path Attribute Length. */ stream_putw_at(s, pos, total_attr_len); diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 62b79541bf..ecb45ff697 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -5034,6 +5034,27 @@ DEFUN(no_bgp_fast_convergence, no_bgp_fast_convergence_cmd, return CMD_SUCCESS; } +DEFPY (bgp_ipv6_auto_ra, + bgp_ipv6_auto_ra_cmd, + "[no] bgp ipv6-auto-ra", + NO_STR + BGP_STR + "Allow enabling IPv6 ND RA sending\n") +{ + if (vty->node == CONFIG_NODE) { + struct listnode *node, *nnode; + struct bgp *bgp; + + COND_FLAG(bm->flags, BM_FLAG_IPV6_NO_AUTO_RA, no); + for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) + COND_FLAG(bgp->flags, BGP_FLAG_IPV6_NO_AUTO_RA, no); + } else { + VTY_DECLVAR_CONTEXT(bgp, bgp); + COND_FLAG(bgp->flags, BGP_FLAG_IPV6_NO_AUTO_RA, no); + } + return CMD_SUCCESS; +} + static int peer_conf_interface_get(struct vty *vty, const char *conf_if, int v6only, const char *peer_group_name, @@ -19418,6 +19439,9 @@ int bgp_config_write(struct vty *vty) if (CHECK_FLAG(bm->flags, BM_FLAG_SEND_EXTRA_DATA_TO_ZEBRA)) vty_out(vty, "bgp send-extra-data zebra\n"); + if (CHECK_FLAG(bm->flags, BM_FLAG_IPV6_NO_AUTO_RA)) + vty_out(vty, "no bgp ipv6-auto-ra\n"); + /* DSCP value for outgoing packets in BGP connections */ if (bm->ip_tos != IPTOS_PREC_INTERNETCONTROL) vty_out(vty, "bgp session-dscp %u\n", bm->ip_tos >> 2); @@ -19839,6 +19863,11 @@ int bgp_config_write(struct vty *vty) if (CHECK_FLAG(bgp->flags, BGP_FLAG_SHUTDOWN)) vty_out(vty, " bgp shutdown\n"); + /* Automatic RA enabling by BGP */ + if (!CHECK_FLAG(bm->flags, BM_FLAG_IPV6_NO_AUTO_RA)) + if (CHECK_FLAG(bgp->flags, BGP_FLAG_IPV6_NO_AUTO_RA)) + vty_out(vty, " no bgp ipv6-auto-ra\n"); + if (bgp->allow_martian) vty_out(vty, " bgp allow-martian-nexthop\n"); @@ -20379,6 +20408,12 @@ void bgp_vty_init(void) install_element(BGP_NODE, &bgp_fast_convergence_cmd); install_element(BGP_NODE, &no_bgp_fast_convergence_cmd); + /* global bgp ipv6-auto-ra command */ + install_element(CONFIG_NODE, &bgp_ipv6_auto_ra_cmd); + + /* bgp ipv6-auto-ra command */ + install_element(BGP_NODE, &bgp_ipv6_auto_ra_cmd); + /* global bgp update-delay command */ install_element(CONFIG_NODE, &bgp_global_update_delay_cmd); install_element(CONFIG_NODE, &no_bgp_global_update_delay_cmd); diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index bffa5a0e6b..9053df3192 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -2327,6 +2327,9 @@ void bgp_zebra_initiate_radv(struct bgp *bgp, struct peer *peer) { uint32_t ra_interval = BGP_UNNUM_DEFAULT_RA_INTERVAL; + if (CHECK_FLAG(bgp->flags, BGP_FLAG_IPV6_NO_AUTO_RA)) + return; + /* Don't try to initiate if we're not connected to Zebra */ if (zclient->sock < 0) return; diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index a186243ffc..d87c6d7752 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -1412,6 +1412,8 @@ int bgp_global_gr_init(struct bgp *bgp) bgp->rib_stale_time = bm->rib_stale_time; if (CHECK_FLAG(bm->flags, BM_FLAG_GR_PRESERVE_FWD)) SET_FLAG(bgp->flags, BGP_FLAG_GR_PRESERVE_FWD); + if (CHECK_FLAG(bm->flags, BM_FLAG_IPV6_NO_AUTO_RA)) + SET_FLAG(bgp->flags, BGP_FLAG_IPV6_NO_AUTO_RA); bgp->present_zebra_gr_state = ZEBRA_GR_DISABLE; diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 852efdf19d..eef5bab333 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -172,6 +172,7 @@ struct bgp_master { #define BM_FLAG_GR_PRESERVE_FWD (1 << 5) #define BM_FLAG_GRACEFUL_RESTART (1 << 6) #define BM_FLAG_GR_COMPLETE (1 << 7) +#define BM_FLAG_IPV6_NO_AUTO_RA (1 << 8) #define BM_FLAG_GR_CONFIGURED (BM_FLAG_GR_RESTARTER | BM_FLAG_GR_DISABLED) @@ -551,6 +552,8 @@ struct bgp { #define BGP_FLAG_DYNAMIC_CAPABILITY (1ULL << 37) #define BGP_FLAG_VNI_DOWN (1ULL << 38) #define BGP_FLAG_INSTANCE_HIDDEN (1ULL << 39) +/* Prohibit BGP from enabling IPv6 RA on interfaces */ +#define BGP_FLAG_IPV6_NO_AUTO_RA (1ULL << 40) /* BGP default address-families. * New peers inherit enabled afi/safis from bgp instance. @@ -1223,6 +1226,8 @@ struct peer_connection { struct event *t_process_packet; struct event *t_process_packet_error; + struct event *t_stop_with_notify; + union sockunion su; #define BGP_CONNECTION_SU_UNSPEC(connection) \ (connection->su.sa.sa_family == AF_UNSPEC) diff --git a/doc/developer/topotests.rst b/doc/developer/topotests.rst index 6209749636..1ec516a1e1 100644 --- a/doc/developer/topotests.rst +++ b/doc/developer/topotests.rst @@ -1393,21 +1393,22 @@ or using unified config (specifying which daemons to run is optional): Requirements: - Directory name for a new topotest must not contain hyphen (``-``) characters. - To separate words, use underscores (``_``). For example, ``tests/topotests/bgp_new_example``. + To separate words, use underscores (``_``). For example, ``tests/topotests/bgp_new_example``; - Test code should always be declared inside functions that begin with the ``test_`` prefix. Functions beginning with different prefixes will not be run - by pytest. + by pytest; - Configuration files and long output commands should go into separated files - inside folders named after the equipment. + inside folders named after the equipment; - Tests must be able to run without any interaction. To make sure your test - conforms with this, run it without the :option:`-s` parameter. + conforms with this, run it without the :option:`-s` parameter; - Use `black <https://github.com/psf/black>`_ code formatter before creating - a pull request. This ensures we have a unified code style. + a pull request. This ensures we have a unified code style; - Mark test modules with pytest markers depending on the daemons used during the - tests (see :ref:`topotests-markers`) + tests (see :ref:`topotests-markers`); - Always use IPv4 :rfc:`5737` (``192.0.2.0/24``, ``198.51.100.0/24``, ``203.0.113.0/24``) and IPv6 :rfc:`3849` (``2001:db8::/32``) ranges reserved - for documentation. + for documentation; +- Use unified config (``frr.conf``) for all new [tests](#writing-tests). Tips: diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 6a46128d72..20a9ef84fa 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -160,16 +160,16 @@ bottom until one of the factors can be used. Prefer higher local preference routes to lower. +3. **Local route check** + + Prefer local routes (statics, aggregates, redistributed) to received routes. + If ``bgp bestpath aigp`` is enabled, and both paths that are compared have AIGP attribute, BGP uses AIGP tie-breaking unless both of the paths have the AIGP metric attribute. This means that the AIGP attribute is not evaluated during the best path selection process between two paths when one path does not have the AIGP attribute. -3. **Local route check** - - Prefer local routes (statics, aggregates, redistributed) to received routes. - 4. **AS path length check** Prefer shortest hop-count AS_PATHs. @@ -1290,6 +1290,13 @@ IPv6 Support address family is enabled by default for all new neighbors. +.. clicmd:: bgp ipv6-auto-ra + + By default, bgpd can ask Zebra to enable sending IPv6 router advertisement + messages on interfaces. For example, this happens for unnumbered peers + support or when extended-nexthop capability is used. The ``no`` form of this + command disables such behaviour. + .. _bgp-route-aggregation: Route Aggregation diff --git a/doc/user/evpn.rst b/doc/user/evpn.rst index 7c4d9fe7d9..a72ea56fea 100644 --- a/doc/user/evpn.rst +++ b/doc/user/evpn.rst @@ -56,13 +56,13 @@ FRR learns about the system's Linux network interface configuration from the kernel via Netlink, however it does not manage network interfaces directly. The following sections will include examples of Linux interface configurations that are compatible with FRR's EVPN implementation. While there are multiple -interface managers that can setup a proper kernel config (e.g. ifupdown2), +interface managers that can set up a proper kernel config (e.g. ifupdown2), these examples will use iproute2 to add/configure the interfaces. All of the examples will follow the same basic setup but use different, yet compatible, interface configurations. -In this example we will setup the following: +In this example we will set up the following: * An IP-VRF named vrf1, associated with L3VNI 100 * An IP-VRF named vrf2, associated with L3VNI 200 @@ -78,7 +78,7 @@ In this example we will setup the following: Sample Configuration -------------------- This is a sample FRR configuration that implements the above EVPN environment. -The first snippet will be the config in its entiretly, then each config element +The first snippet will be the config in its entirety, then each config element will be explained individually later in the document. The following snippet will result in a functional EVPN control plane if the @@ -484,7 +484,7 @@ VNI, The interface settings are the same for an L2VNI as they are for an L3VNI. Finally, to limit a traditional bridge's broadcast domain to traffic matching specific VLAN-IDs, ``vlan`` subinterfaces of a host/network port need to be -setup. This example shows the creation of a VLAN subinterface of "eth0" +set up. This example shows the creation of a VLAN subinterface of "eth0" matching VID 10 with the name "eth0.10". By enslaving "eth0.10" to "br10" (instead of "eth0") we ensure that only Ethernet frames ingressing "eth0" tagged with VID 10 will be associated with the "br10" broadcast domain. diff --git a/doc/user/ipv6.rst b/doc/user/ipv6.rst index 4f01061e7b..18aae00bdb 100644 --- a/doc/user/ipv6.rst +++ b/doc/user/ipv6.rst @@ -25,7 +25,8 @@ Router Advertisement .. clicmd:: ipv6 nd suppress-ra Don't send router advertisement messages. The ``no`` form of this command - enables sending RA messages. + enables sending RA messages. Note that while being suppressed, RA messages + might still be enabled by other daemons, such as bgpd or vrrpd. .. clicmd:: ipv6 nd prefix ipv6prefix [valid-lifetime] [preferred-lifetime] [off-link] [no-autoconfig] [router-address] diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst index 06a19a6139..74564c94b9 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -1864,6 +1864,10 @@ Debugging Nexthop and nexthop-group events. +.. clicmd:: debug zebra srv6 + + Segment Routing for IPv6 dataplane debugging. + Scripting ========= diff --git a/isisd/isis_cli.c b/isisd/isis_cli.c index e6cc794bae..93f7bbf753 100644 --- a/isisd/isis_cli.c +++ b/isisd/isis_cli.c @@ -2015,12 +2015,12 @@ void cli_show_isis_prefix_sid_algorithm(struct vty *vty, const char *sid_value_type; const char *sid_value; bool n_flag_clear; - uint32_t algorithm; + uint8_t algorithm; prefix = yang_dnode_get_string(dnode, "prefix"); sid_value_type = yang_dnode_get_string(dnode, "sid-value-type"); sid_value = yang_dnode_get_string(dnode, "sid-value"); - algorithm = yang_dnode_get_uint32(dnode, "algo"); + algorithm = yang_dnode_get_uint8(dnode, "algo"); lh_behavior = yang_dnode_get_string(dnode, "last-hop-behavior"); n_flag_clear = yang_dnode_get_bool(dnode, "n-flag-clear"); diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c index 0f0c900ec2..fb391534e2 100644 --- a/isisd/isis_nb_config.c +++ b/isisd/isis_nb_config.c @@ -2633,14 +2633,14 @@ int isis_instance_segment_routing_algorithm_prefix_sid_create( struct isis_area *area; struct prefix prefix; struct sr_prefix_cfg *pcfg; - uint32_t algorithm; + uint8_t algorithm; if (args->event != NB_EV_APPLY) return NB_OK; area = nb_running_get_entry(args->dnode, NULL, true); yang_dnode_get_prefix(&prefix, args->dnode, "prefix"); - algorithm = yang_dnode_get_uint32(args->dnode, "algo"); + algorithm = yang_dnode_get_uint8(args->dnode, "algo"); pcfg = isis_sr_cfg_prefix_add(area, &prefix, algorithm); pcfg->algorithm = algorithm; diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index 8fc0f144b2..9c32a8447c 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -3258,6 +3258,7 @@ DEFUN(show_isis_route, show_isis_route_cmd, json_object *json = NULL, *json_vrf = NULL; uint8_t algorithm = SR_ALGORITHM_SPF; + ISIS_FIND_VRF_ARGS(argv, argc, idx, vrf_name, all_vrf); if (argv_find(argv, argc, "level-1", &idx)) levels = ISIS_LEVEL1; else if (argv_find(argv, argc, "level-2", &idx)) @@ -3269,7 +3270,6 @@ DEFUN(show_isis_route, show_isis_route_cmd, vty_out(vty, "IS-IS Routing Process not enabled\n"); return CMD_SUCCESS; } - ISIS_FIND_VRF_ARGS(argv, argc, idx, vrf_name, all_vrf); if (argv_find(argv, argc, "prefix-sid", &idx)) prefix_sid = true; @@ -3520,6 +3520,7 @@ DEFUN(show_isis_frr_summary, show_isis_frr_summary_cmd, bool all_vrf = false; int idx = 0; + ISIS_FIND_VRF_ARGS(argv, argc, idx, vrf_name, all_vrf); if (argv_find(argv, argc, "level-1", &idx)) levels = ISIS_LEVEL1; else if (argv_find(argv, argc, "level-2", &idx)) @@ -3531,7 +3532,6 @@ DEFUN(show_isis_frr_summary, show_isis_frr_summary_cmd, vty_out(vty, "IS-IS Routing Process not enabled\n"); return CMD_SUCCESS; } - ISIS_FIND_VRF_ARGS(argv, argc, idx, vrf_name, all_vrf); if (all_vrf) { for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) diff --git a/lib/frrscript.c b/lib/frrscript.c index dbae31b666..06460b014d 100644 --- a/lib/frrscript.c +++ b/lib/frrscript.c @@ -27,6 +27,16 @@ struct frrscript_names_head frrscript_names_hash; void _lua_decode_noop(lua_State *L, ...) {} +void frrscript_names_config_write(struct vty *vty) +{ + struct frrscript_names_entry *lua_script_entry; + + frr_each (frrscript_names, &frrscript_names_hash, lua_script_entry) + if (lua_script_entry->script_name[0] != '\0') + vty_out(vty, "zebra on-rib-process script %s\n", + lua_script_entry->script_name); +} + /* * Wrapper for frrscript_names_add * Use this to register hook calls when a daemon starts up diff --git a/lib/frrscript.h b/lib/frrscript.h index ce313a1b63..75ac53c609 100644 --- a/lib/frrscript.h +++ b/lib/frrscript.h @@ -44,6 +44,8 @@ struct frrscript_names_entry { extern struct frrscript_names_head frrscript_names_hash; +extern void frrscript_names_config_write(struct vty *vty); + int frrscript_names_hash_cmp(const struct frrscript_names_entry *snhe1, const struct frrscript_names_entry *snhe2); uint32_t frrscript_names_hash_key(const struct frrscript_names_entry *snhe); diff --git a/lib/libfrr.c b/lib/libfrr.c index f2247a48e5..d1a9f0b1cb 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -1126,12 +1126,9 @@ static void frr_terminal_close(int isexit) * don't redirect when stdout is set with --log stdout */ for (fd = 2; fd >= 0; fd--) - if (logging_to_stdout && isatty(fd) && - fd == STDOUT_FILENO) { - /* Do nothing. */ - } else { + if (isatty(fd) && + (fd != STDOUT_FILENO || !logging_to_stdout)) dup2(nullfd, fd); - } close(nullfd); } } @@ -1217,12 +1214,9 @@ void frr_run(struct event_loop *master) * stdout */ for (fd = 2; fd >= 0; fd--) - if (logging_to_stdout && isatty(fd) && - fd == STDOUT_FILENO) { - /* Do nothing. */ - } else { + if (isatty(fd) && + (fd != STDOUT_FILENO || !logging_to_stdout)) dup2(nullfd, fd); - } close(nullfd); } @@ -1245,10 +1239,6 @@ void frr_early_fini(void) void frr_fini(void) { - FILE *fp; - char filename[128]; - int have_leftovers = 0; - hook_call(frr_fini); vty_terminate(); @@ -1269,32 +1259,27 @@ void frr_fini(void) event_master_free(master); master = NULL; zlog_tls_buffer_fini(); - zlog_fini(); + + if (0) { + /* this is intentionally disabled. zlog remains running until + * exit(), so even the very last item done during shutdown can + * have its zlog() messages written out. + * + * Yes this causes memory leaks. They are explicitly marked + * with DEFINE_MGROUP_ACTIVEATEXIT, which is only used for + * log target memory allocations, and excluded from leak + * reporting at shutdown. This is strongly preferable over + * just discarding error messages at shutdown. + */ + zlog_fini(); + } + /* frrmod_init -> nothing needed / hooks */ rcu_shutdown(); frrmod_terminate(); - /* also log memstats to stderr when stderr goes to a file*/ - if (debug_memstats_at_exit || !isatty(STDERR_FILENO)) - have_leftovers = log_memstats(stderr, di->name); - - /* in case we decide at runtime that we want exit-memstats for - * a daemon - * (only do this if we actually have something to print though) - */ - if (!debug_memstats_at_exit || !have_leftovers) - return; - - snprintf(filename, sizeof(filename), "/tmp/frr-memstats-%s-%llu-%llu", - di->name, (unsigned long long)getpid(), - (unsigned long long)time(NULL)); - - fp = fopen(filename, "w"); - if (fp) { - log_memstats(fp, di->name); - fclose(fp); - } + log_memstats(di->name, debug_memstats_at_exit); } struct json_object *frr_daemon_state_load(void) @@ -306,7 +306,7 @@ void memory_oom(size_t size, const char *name) "out of memory: failed to allocate %zu bytes for %s object", size, name); zlog_backtrace(LOG_CRIT); - log_memstats(stderr, "log"); + log_memstats(zlog_progname, true); abort(); } diff --git a/lib/memory.c b/lib/memory.c index ac39516edd..9da46bdb89 100644 --- a/lib/memory.c +++ b/lib/memory.c @@ -148,35 +148,108 @@ int qmem_walk(qmem_walk_fn *func, void *arg) } struct exit_dump_args { - FILE *fp; - const char *prefix; + const char *daemon_name; + bool do_log; + bool do_file; + bool do_stderr; int error; + FILE *fp; + struct memgroup *last_mg; }; +static void qmem_exit_fopen(struct exit_dump_args *eda) +{ + char filename[128]; + + if (eda->fp || !eda->do_file || !eda->daemon_name) + return; + + snprintf(filename, sizeof(filename), "/tmp/frr-memstats-%s-%llu-%llu", eda->daemon_name, + (unsigned long long)getpid(), (unsigned long long)time(NULL)); + eda->fp = fopen(filename, "w"); + + if (!eda->fp) { + zlog_err("failed to open memstats dump file %pSQq: %m", filename); + /* don't try opening file over and over again */ + eda->do_file = false; + } +} + static int qmem_exit_walker(void *arg, struct memgroup *mg, struct memtype *mt) { struct exit_dump_args *eda = arg; + const char *prefix = eda->daemon_name ?: "NONE"; + char size[32]; + + if (!mt) + /* iterator calls mg=X, mt=NULL first */ + return 0; - if (!mt) { - fprintf(eda->fp, - "%s: showing active allocations in memory group %s\n", - eda->prefix, mg->name); + if (!mt->n_alloc) + return 0; - } else if (mt->n_alloc) { - char size[32]; - if (!mg->active_at_exit) - eda->error++; + if (mt->size != SIZE_VAR) snprintf(size, sizeof(size), "%10zu", mt->size); - fprintf(eda->fp, "%s: memstats: %-30s: %6zu * %s\n", - eda->prefix, mt->name, mt->n_alloc, - mt->size == SIZE_VAR ? "(variably sized)" : size); + else + snprintf(size, sizeof(size), "(variably sized)"); + + if (mg->active_at_exit) { + /* not an error - this memgroup has allocations remain active + * at exit. Only printed to zlog_debug. + */ + if (!eda->do_log) + return 0; + + if (eda->last_mg != mg) { + zlog_debug("showing active allocations in memory group %s (not an error)", + mg->name); + eda->last_mg = mg; + } + zlog_debug("memstats: %-30s: %6zu * %s", mt->name, mt->n_alloc, size); + return 0; } + + eda->error++; + if (eda->do_file) + qmem_exit_fopen(eda); + + if (eda->last_mg != mg) { + if (eda->do_log) + zlog_warn("showing active allocations in memory group %s", mg->name); + if (eda->do_stderr) + fprintf(stderr, "%s: showing active allocations in memory group %s\n", + prefix, mg->name); + if (eda->fp) + fprintf(eda->fp, "%s: showing active allocations in memory group %s\n", + prefix, mg->name); + eda->last_mg = mg; + } + + if (eda->do_log) + zlog_warn("memstats: %-30s: %6zu * %s", mt->name, mt->n_alloc, size); + if (eda->do_stderr) + fprintf(stderr, "%s: memstats: %-30s: %6zu * %s\n", prefix, mt->name, mt->n_alloc, + size); + if (eda->fp) + fprintf(eda->fp, "%s: memstats: %-30s: %6zu * %s\n", prefix, mt->name, mt->n_alloc, + size); return 0; } -int log_memstats(FILE *fp, const char *prefix) +int log_memstats(const char *daemon_name, bool enabled) { - struct exit_dump_args eda = {.fp = fp, .prefix = prefix, .error = 0}; + struct exit_dump_args eda = { + .daemon_name = daemon_name, + .do_log = enabled, + .do_file = enabled, + .do_stderr = enabled || !isatty(STDERR_FILENO), + .error = 0, + }; + qmem_walk(qmem_exit_walker, &eda); + if (eda.fp) + fclose(eda.fp); + if (eda.error && eda.do_log) + zlog_warn("exiting with %d leaked MTYPEs", eda.error); return eda.error; } diff --git a/lib/memory.h b/lib/memory.h index 8e8c61da04..8658018832 100644 --- a/lib/memory.h +++ b/lib/memory.h @@ -67,6 +67,8 @@ struct memgroup { * but MGROUP_* aren't. */ +/* clang-format off */ + #define DECLARE_MGROUP(name) extern struct memgroup _mg_##name #define _DEFINE_MGROUP(mname, desc, ...) \ struct memgroup _mg_##mname _DATA_SECTION("mgroups") = { \ @@ -75,6 +77,7 @@ struct memgroup { .next = NULL, \ .insert = NULL, \ .ref = NULL, \ + __VA_ARGS__ \ }; \ static void _mginit_##mname(void) __attribute__((_CONSTRUCTOR(1000))); \ static void _mginit_##mname(void) \ @@ -136,6 +139,8 @@ struct memgroup { DEFINE_MTYPE_ATTR(group, name, static, desc) \ /* end */ +/* clang-format on */ + DECLARE_MGROUP(LIB); DECLARE_MTYPE(TMP); DECLARE_MTYPE(TMP_TTABLE); @@ -176,8 +181,7 @@ static inline size_t mtype_stats_alloc(struct memtype *mt) * last value from qmem_walk_fn. */ typedef int qmem_walk_fn(void *arg, struct memgroup *mg, struct memtype *mt); extern int qmem_walk(qmem_walk_fn *func, void *arg); -extern int log_memstats(FILE *fp, const char *); -#define log_memstats_stderr(prefix) log_memstats(stderr, prefix) +extern int log_memstats(const char *daemon_name, bool enabled); extern __attribute__((__noreturn__)) void memory_oom(size_t size, const char *name); diff --git a/lib/sigevent.c b/lib/sigevent.c index 3e69f280da..7c465bfcec 100644 --- a/lib/sigevent.c +++ b/lib/sigevent.c @@ -237,8 +237,18 @@ core_handler(int signo, siginfo_t *siginfo, void *context) zlog_signal(signo, "aborting...", siginfo, pc); - /* dump memory stats on core */ - log_memstats(stderr, "core_handler"); + /* there used to be a log_memstats() call here, to dump MTYPE counters + * on a coredump. This is not possible since log_memstats is not + * AS-Safe, as it calls fopen(), fprintf(), and cousins. This can + * lead to a deadlock depending on where we crashed - very much not a + * good thing if the process just hangs there after a crash. + * + * The alarm(1) above tries to alleviate this, but that's really a + * last resort recovery. Stick with AS-safe calls here. + * + * If the fprintf() calls are removed from log_memstats(), this can be + * added back in, since writing to log with zlog_sigsafe() is AS-safe. + */ /* * This is a buffer flush because FRR is going down diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 22e09a69a0..511884e37a 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -748,6 +748,8 @@ DEFUN (no_ospf_area_range, ospf_area_range_unset(ospf, area, area->ranges, &p); + ospf_area_check_free(ospf, area_id); + return CMD_SUCCESS; } diff --git a/pimd/pim_autorp.c b/pimd/pim_autorp.c index 3fc30347a6..d36b792e39 100644 --- a/pimd/pim_autorp.c +++ b/pimd/pim_autorp.c @@ -241,8 +241,11 @@ static bool pim_autorp_add_rp(struct pim_autorp *autorp, pim_addr rpaddr, { struct pim_autorp_rp *rp; struct pim_autorp_rp *trp = NULL; + int ret; - if (pim_rp_new(autorp->pim, rpaddr, grp, listname, RP_SRC_AUTORP)) { + ret = pim_rp_new(autorp->pim, rpaddr, grp, listname, RP_SRC_AUTORP); + /* There may not be a path to the RP right now, but that doesn't mean it failed to add the RP */ + if (ret != PIM_SUCCESS && ret != PIM_RP_NO_PATH) { zlog_err("%s: Failed to add new RP addr=%pI4, grp=%pFX, grplist=%s", __func__, &rpaddr, &grp, (listname ? listname : "NONE")); diff --git a/pimd/pim_bsm.c b/pimd/pim_bsm.c index 115aec8933..a44e4e08f3 100644 --- a/pimd/pim_bsm.c +++ b/pimd/pim_bsm.c @@ -1650,8 +1650,18 @@ static void pim_cand_bsr_pending_expire(struct event *t) struct bsm_scope *scope = EVENT_ARG(t); assertf(scope->state == BSR_PENDING, "state=%d", scope->state); - assertf(pim_addr_is_any(scope->current_bsr), "current_bsr=%pPA", - &scope->current_bsr); + + if (!pim_addr_is_any(scope->current_bsr)) { + assertf(scope->cand_bsr_prio >= scope->current_bsr_prio, + "cand_bsr %pPA prio %u is less than current_bsr %pPA prio %u", + &scope->bsr_addrsel.run_addr, scope->current_bsr_prio, &scope->current_bsr, + scope->cand_bsr_prio); + + if (scope->cand_bsr_prio == scope->current_bsr_prio) + assertf(pim_addr_cmp(scope->bsr_addrsel.run_addr, scope->current_bsr) > 0, + "cand_bsr %pPA < current_bsr %pPA", &scope->bsr_addrsel.run_addr, + &scope->current_bsr); + } if (PIM_DEBUG_BSM) zlog_debug("Elected BSR, wait expired without preferable BSMs"); diff --git a/pimd/pim_nht.c b/pimd/pim_nht.c index 5a161c4f16..f2dbfa9765 100644 --- a/pimd/pim_nht.c +++ b/pimd/pim_nht.c @@ -342,9 +342,19 @@ bool pim_nht_bsr_rpf_check(struct pim_instance *pim, pim_addr bsr_addr, nbr = pim_neighbor_find(ifp, znh->nexthop_addr, true); if (!nbr) continue; - - return znh->ifindex == src_ifp->ifindex && - (!pim_addr_cmp(znh->nexthop_addr, src_ip)); + /* Are we on the correct interface? */ + if (znh->ifindex == src_ifp->ifindex) { + /* Do we have the correct NH ? */ + if (!pim_addr_cmp(znh->nexthop_addr, src_ip)) + return true; + /* + * check If the packet came from the neighbor, + * and the dst is a secondary address on the connected interface + */ + return (!pim_addr_cmp(nbr->source_addr, src_ip) && + pim_if_connected_to_source(ifp, znh->nexthop_addr)); + } + return false; } return false; } @@ -409,8 +419,19 @@ bool pim_nht_bsr_rpf_check(struct pim_instance *pim, pim_addr bsr_addr, if (!nbr) continue; - return nh->ifindex == src_ifp->ifindex && - (!pim_addr_cmp(nhaddr, src_ip)); + /* Are we on the correct interface? */ + if (nh->ifindex == src_ifp->ifindex) { + /* Do we have the correct NH ? */ + if (!pim_addr_cmp(nhaddr, src_ip)) + return true; + /* + * check If the packet came from the neighbor, + * and the dst is a secondary address on the connected interface + */ + return (!pim_addr_cmp(nbr->source_addr, src_ip) && + pim_if_connected_to_source(ifp, nhaddr)); + } + return false; } return false; } diff --git a/python/xref2vtysh.py b/python/xref2vtysh.py index 0cfb11e721..0e6cdcd6a7 100644 --- a/python/xref2vtysh.py +++ b/python/xref2vtysh.py @@ -450,9 +450,11 @@ class CommandEntry: graph_delete_node(node->cmdgraph, vector_slot(node->cmdgraph->nodes, 0)); vector_free(node->cmdgraph->nodes); node->cmdgraph->nodes = &gvec_{node}; -{'}'} """ ) + for cmdel in sorted(cmdels): + ofd.write(f"\tvector_set(node->cmd_vector, &{cmdel}_vtysh);\n") + ofd.write("}\n") return [node] diff --git a/sharpd/sharp_vty.c b/sharpd/sharp_vty.c index 21c596bf71..c9211a152f 100644 --- a/sharpd/sharp_vty.c +++ b/sharpd/sharp_vty.c @@ -82,7 +82,7 @@ DEFPY(watch_redistribute, watch_redistribute_cmd, } DEFPY(watch_nexthop_v6, watch_nexthop_v6_cmd, - "sharp watch [vrf NAME$vrf_name] <nexthop$n X:X::X:X$nhop|import$import X:X::X:X/M$inhop> [connected$connected]", + "sharp watch [vrf NAME$vrf_name] <nexthop$n X:X::X:X$nhop|import$import X:X::X:X/M$inhop> [connected$connected] [mrib$mrib]", "Sharp routing Protocol\n" "Watch for changes\n" "The vrf we would like to watch if non-default\n" @@ -91,7 +91,8 @@ DEFPY(watch_nexthop_v6, watch_nexthop_v6_cmd, "The v6 nexthop to signal for watching\n" "Watch for import check changes\n" "The v6 prefix to signal for watching\n" - "Should the route be connected\n") + "Should the route be connected\n" + "In the Multicast rib\n") { struct vrf *vrf; struct prefix p; @@ -119,14 +120,13 @@ DEFPY(watch_nexthop_v6, watch_nexthop_v6_cmd, } sharp_nh_tracker_get(&p); - sharp_zebra_nexthop_watch(&p, vrf->vrf_id, type_import, - true, !!connected); + sharp_zebra_nexthop_watch(&p, vrf->vrf_id, type_import, true, !!connected, !!mrib); return CMD_SUCCESS; } DEFPY(watch_nexthop_v4, watch_nexthop_v4_cmd, - "sharp watch [vrf NAME$vrf_name] <nexthop$n A.B.C.D$nhop|import$import A.B.C.D/M$inhop> [connected$connected]", + "sharp watch [vrf NAME$vrf_name] <nexthop$n A.B.C.D$nhop|import$import A.B.C.D/M$inhop> [connected$connected] [mrib$mrib]", "Sharp routing Protocol\n" "Watch for changes\n" "The vrf we would like to watch if non-default\n" @@ -135,7 +135,8 @@ DEFPY(watch_nexthop_v4, watch_nexthop_v4_cmd, "The v4 address to signal for watching\n" "Watch for import check changes\n" "The v4 prefix for import check to watch\n" - "Should the route be connected\n") + "Should the route be connected\n" + "In the Multicast rib\n") { struct vrf *vrf; struct prefix p; @@ -164,8 +165,7 @@ DEFPY(watch_nexthop_v4, watch_nexthop_v4_cmd, } sharp_nh_tracker_get(&p); - sharp_zebra_nexthop_watch(&p, vrf->vrf_id, type_import, - true, !!connected); + sharp_zebra_nexthop_watch(&p, vrf->vrf_id, type_import, true, !!connected, !!mrib); return CMD_SUCCESS; } diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c index 1048436b43..4447b69bf6 100644 --- a/sharpd/sharp_zebra.c +++ b/sharpd/sharp_zebra.c @@ -618,18 +618,19 @@ void nhg_del(uint32_t id) zclient_nhg_send(zclient, ZEBRA_NHG_DEL, &api_nhg); } -void sharp_zebra_nexthop_watch(struct prefix *p, vrf_id_t vrf_id, bool import, - bool watch, bool connected) +void sharp_zebra_nexthop_watch(struct prefix *p, vrf_id_t vrf_id, bool import, bool watch, + bool connected, bool mrib) { - int command; + int command = ZEBRA_NEXTHOP_REGISTER; + safi_t safi = mrib ? SAFI_MULTICAST : SAFI_UNICAST; command = ZEBRA_NEXTHOP_REGISTER; if (!watch) command = ZEBRA_NEXTHOP_UNREGISTER; - if (zclient_send_rnh(zclient, command, p, SAFI_UNICAST, connected, - false, vrf_id) == ZCLIENT_SEND_FAILURE) + if (zclient_send_rnh(zclient, command, p, safi, connected, false, vrf_id) == + ZCLIENT_SEND_FAILURE) zlog_warn("%s: Failure to send nexthop to zebra", __func__); } diff --git a/sharpd/sharp_zebra.h b/sharpd/sharp_zebra.h index 5cbcc14665..7a86897beb 100644 --- a/sharpd/sharp_zebra.h +++ b/sharpd/sharp_zebra.h @@ -18,8 +18,8 @@ extern void vrf_label_add(vrf_id_t vrf_id, afi_t afi, mpls_label_t label); extern void nhg_add(uint32_t id, const struct nexthop_group *nhg, const struct nexthop_group *backup_nhg); extern void nhg_del(uint32_t id); -extern void sharp_zebra_nexthop_watch(struct prefix *p, vrf_id_t vrf_id, - bool import, bool watch, bool connected); +extern void sharp_zebra_nexthop_watch(struct prefix *p, vrf_id_t vrf_id, bool import, bool watch, + bool connected, bool mrib); extern void sharp_install_routes_helper(struct prefix *p, vrf_id_t vrf_id, uint8_t instance, uint32_t nhgid, diff --git a/tests/isisd/test_fuzz_isis_tlv.c b/tests/isisd/test_fuzz_isis_tlv.c index 627ccfee6f..a3acd0786f 100644 --- a/tests/isisd/test_fuzz_isis_tlv.c +++ b/tests/isisd/test_fuzz_isis_tlv.c @@ -22,7 +22,7 @@ static bool atexit_registered; static void show_meminfo_at_exit(void) { - log_memstats(stderr, "isis fuzztest"); + log_memstats(NULL, true); } static int comp_line(const void *p1, const void *p2) diff --git a/tests/isisd/test_isis_spf.c b/tests/isisd/test_isis_spf.c index e5a8f7a513..9c1ce3d193 100644 --- a/tests/isisd/test_isis_spf.c +++ b/tests/isisd/test_isis_spf.c @@ -475,7 +475,7 @@ static void vty_do_exit(int isexit) yang_terminate(); event_master_free(master); - log_memstats(stderr, "test-isis-spf"); + log_memstats(NULL, true); if (!isexit) exit(0); } diff --git a/tests/lib/cli/common_cli.c b/tests/lib/cli/common_cli.c index 342a91cc79..5c23a71258 100644 --- a/tests/lib/cli/common_cli.c +++ b/tests/lib/cli/common_cli.c @@ -43,7 +43,7 @@ static void vty_do_exit(int isexit) yang_terminate(); event_master_free(master); - log_memstats(stderr, "testcli"); + log_memstats(NULL, true); if (!isexit) exit(0); } diff --git a/tests/lib/northbound/test_oper_data.c b/tests/lib/northbound/test_oper_data.c index fdc9e53ca3..3d700d8a19 100644 --- a/tests/lib/northbound/test_oper_data.c +++ b/tests/lib/northbound/test_oper_data.c @@ -427,7 +427,7 @@ static void vty_do_exit(int isexit) yang_terminate(); event_master_free(master); - log_memstats(stderr, "test-nb-oper-data"); + log_memstats(NULL, true); if (!isexit) exit(0); } diff --git a/tests/lib/test_typelist.c b/tests/lib/test_typelist.c index 070a304335..3ce6683ae5 100644 --- a/tests/lib/test_typelist.c +++ b/tests/lib/test_typelist.c @@ -156,6 +156,6 @@ int main(int argc, char **argv) test_ATOMSORT_UNIQ(); test_ATOMSORT_NONUNIQ(); - log_memstats_stderr("test: "); + log_memstats(NULL, true); return 0; } diff --git a/tests/lib/test_zmq.c b/tests/lib/test_zmq.c index 2cd9d47cb4..5cb518d81a 100644 --- a/tests/lib/test_zmq.c +++ b/tests/lib/test_zmq.c @@ -285,7 +285,7 @@ static void run_server(int syncfd) zmq_close(zmqsock); frrzmq_finish(); event_master_free(master); - log_memstats_stderr("test"); + log_memstats(NULL, true); } int main(void) diff --git a/tests/topotests/bgp_aigp_rr/r1/bgpd.conf b/tests/topotests/bgp_aigp_rr/r1/bgpd.conf new file mode 100644 index 0000000000..4099a248f1 --- /dev/null +++ b/tests/topotests/bgp_aigp_rr/r1/bgpd.conf @@ -0,0 +1,32 @@ +router bgp 65001 + no bgp ebgp-requires-policy + no bgp network import-check + bgp route-reflector allow-outbound-policy + neighbor 10.0.0.2 remote-as internal + neighbor 10.0.0.2 update-source lo + neighbor 10.0.0.2 timers 1 3 + neighbor 10.0.0.2 timers connect 1 + neighbor 10.0.0.2 route-reflector-client + neighbor 10.0.0.3 remote-as internal + neighbor 10.0.0.3 update-source lo + neighbor 10.0.0.3 timers 1 3 + neighbor 10.0.0.3 timers connect 1 + neighbor 10.0.0.3 route-reflector-client + neighbor 10.0.0.4 remote-as internal + neighbor 10.0.0.4 update-source lo + neighbor 10.0.0.4 timers 1 3 + neighbor 10.0.0.4 timers connect 1 + neighbor 10.0.0.4 route-reflector-client + address-family ipv4 + network 10.0.1.2/32 route-map set-aigp + neighbor 10.0.0.4 route-map set-nexthop out + exit-address-family +! +route-map set-nexthop permit 10 + set ip next-hop peer-address +exit +! +route-map set-aigp permit 10 + set aigp 50 + set weight 0 +! diff --git a/tests/topotests/bgp_aigp_rr/r1/ospfd.conf b/tests/topotests/bgp_aigp_rr/r1/ospfd.conf new file mode 100644 index 0000000000..b5b43c76be --- /dev/null +++ b/tests/topotests/bgp_aigp_rr/r1/ospfd.conf @@ -0,0 +1,23 @@ +! +interface lo + ip ospf passive +! +interface r1-eth0 + ip ospf dead-interval 4 + ip ospf hello-interval 1 + ip ospf cost 10 +! +interface r1-eth1 + ip ospf dead-interval 4 + ip ospf hello-interval 1 + ip ospf cost 10 +! +interface r1-eth2 + ip ospf dead-interval 4 + ip ospf hello-interval 1 + ip ospf cost 10 +! +router ospf + router-id 10.0.0.1 + network 0.0.0.0/0 area 0 +! diff --git a/tests/topotests/bgp_aigp_rr/r1/zebra.conf b/tests/topotests/bgp_aigp_rr/r1/zebra.conf new file mode 100644 index 0000000000..bf4a3497e8 --- /dev/null +++ b/tests/topotests/bgp_aigp_rr/r1/zebra.conf @@ -0,0 +1,13 @@ +! +interface lo + ip address 10.0.0.1/32 +! +interface r1-eth0 + ip address 192.168.12.1/24 +! +interface r1-eth1 + ip address 192.168.13.1/24 +! +interface r1-eth2 + ip address 192.168.14.1/24 +! diff --git a/tests/topotests/bgp_aigp_rr/r2/bgpd.conf b/tests/topotests/bgp_aigp_rr/r2/bgpd.conf new file mode 100644 index 0000000000..0159dc8e7c --- /dev/null +++ b/tests/topotests/bgp_aigp_rr/r2/bgpd.conf @@ -0,0 +1,22 @@ +router bgp 65001 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 10.0.0.1 remote-as internal + neighbor 10.0.0.1 update-source lo + neighbor 10.0.0.1 timers 1 3 + neighbor 10.0.0.1 timers connect 1 + address-family ipv4 + redistribute connected route-map connected-to-bgp + network 10.0.1.2/32 route-map set-aigp + neighbor 10.0.0.1 next-hop-self + exit-address-family +! +ip prefix-list p22 seq 5 permit 10.0.2.2/32 +! +route-map connected-to-bgp permit 10 + match ip address prefix-list p22 + set aigp 2 +! +route-map set-aigp permit 10 + set aigp 10 +! diff --git a/tests/topotests/bgp_aigp_rr/r2/ospfd.conf b/tests/topotests/bgp_aigp_rr/r2/ospfd.conf new file mode 100644 index 0000000000..dd91101d90 --- /dev/null +++ b/tests/topotests/bgp_aigp_rr/r2/ospfd.conf @@ -0,0 +1,18 @@ +! +interface lo + ip ospf passive +! +interface r2-eth0 + ip ospf dead-interval 4 + ip ospf hello-interval 1 + ip ospf cost 10 +! +interface r2-eth1 + ip ospf dead-interval 4 + ip ospf hello-interval 1 + ip ospf cost 10 +! +router ospf + router-id 10.0.0.2 + network 0.0.0.0/0 area 0 +! diff --git a/tests/topotests/bgp_aigp_rr/r2/zebra.conf b/tests/topotests/bgp_aigp_rr/r2/zebra.conf new file mode 100644 index 0000000000..6e2130f7e8 --- /dev/null +++ b/tests/topotests/bgp_aigp_rr/r2/zebra.conf @@ -0,0 +1,11 @@ +! +interface lo + ip address 10.0.0.2/32 + ip address 10.0.2.2/32 +! +interface r2-eth0 + ip address 192.168.12.2/24 +! +interface r2-eth1 + ip address 192.168.23.2/24 +! diff --git a/tests/topotests/bgp_aigp_rr/r3/bgpd.conf b/tests/topotests/bgp_aigp_rr/r3/bgpd.conf new file mode 100644 index 0000000000..b8a36c5ad0 --- /dev/null +++ b/tests/topotests/bgp_aigp_rr/r3/bgpd.conf @@ -0,0 +1,11 @@ +router bgp 65001 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 10.0.0.1 remote-as internal + neighbor 10.0.0.1 update-source lo + neighbor 10.0.0.1 timers 1 3 + neighbor 10.0.0.1 timers connect 1 + address-family ipv4 + neighbor 10.0.0.1 next-hop-self + exit-address-family +! diff --git a/tests/topotests/bgp_aigp_rr/r3/ospfd.conf b/tests/topotests/bgp_aigp_rr/r3/ospfd.conf new file mode 100644 index 0000000000..48702e4e4c --- /dev/null +++ b/tests/topotests/bgp_aigp_rr/r3/ospfd.conf @@ -0,0 +1,18 @@ +! +interface lo + ip ospf passive +! +interface r3-eth0 + ip ospf dead-interval 4 + ip ospf hello-interval 1 + ip ospf cost 10 +! +interface r3-eth1 + ip ospf dead-interval 4 + ip ospf hello-interval 1 + ip ospf cost 10 +! +router ospf + router-id 10.0.0.3 + network 0.0.0.0/0 area 0 +! diff --git a/tests/topotests/bgp_aigp_rr/r3/zebra.conf b/tests/topotests/bgp_aigp_rr/r3/zebra.conf new file mode 100644 index 0000000000..ea6663724e --- /dev/null +++ b/tests/topotests/bgp_aigp_rr/r3/zebra.conf @@ -0,0 +1,10 @@ +! +interface lo + ip address 10.0.0.3/32 +! +interface r3-eth0 + ip address 192.168.13.3/24 +! +interface r3-eth1 + ip address 192.168.23.3/24 +! diff --git a/tests/topotests/bgp_aigp_rr/r4/bgpd.conf b/tests/topotests/bgp_aigp_rr/r4/bgpd.conf new file mode 100644 index 0000000000..b8a36c5ad0 --- /dev/null +++ b/tests/topotests/bgp_aigp_rr/r4/bgpd.conf @@ -0,0 +1,11 @@ +router bgp 65001 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 10.0.0.1 remote-as internal + neighbor 10.0.0.1 update-source lo + neighbor 10.0.0.1 timers 1 3 + neighbor 10.0.0.1 timers connect 1 + address-family ipv4 + neighbor 10.0.0.1 next-hop-self + exit-address-family +! diff --git a/tests/topotests/bgp_aigp_rr/r4/ospfd.conf b/tests/topotests/bgp_aigp_rr/r4/ospfd.conf new file mode 100644 index 0000000000..1d95b2ac2b --- /dev/null +++ b/tests/topotests/bgp_aigp_rr/r4/ospfd.conf @@ -0,0 +1,13 @@ +! +interface lo + ip ospf passive +! +interface r4-eth0 + ip ospf dead-interval 4 + ip ospf hello-interval 1 + ip ospf cost 10 +! +router ospf + router-id 10.0.0.4 + network 0.0.0.0/0 area 0 +! diff --git a/tests/topotests/bgp_aigp_rr/r4/zebra.conf b/tests/topotests/bgp_aigp_rr/r4/zebra.conf new file mode 100644 index 0000000000..d7a7dece2b --- /dev/null +++ b/tests/topotests/bgp_aigp_rr/r4/zebra.conf @@ -0,0 +1,7 @@ +! +interface lo + ip address 10.0.0.4/32 +! +interface r4-eth0 + ip address 192.168.14.4/24 +! diff --git a/tests/topotests/bgp_aigp_rr/test_bgp_aigp_rr.py b/tests/topotests/bgp_aigp_rr/test_bgp_aigp_rr.py new file mode 100644 index 0000000000..206e229b2e --- /dev/null +++ b/tests/topotests/bgp_aigp_rr/test_bgp_aigp_rr.py @@ -0,0 +1,172 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2024, Palo Alto Networks, Inc. +# Enke Chen <enchen@paloaltonetworks.com> +# + +""" +r1, r2, and r3 are directly connectd to each other. +r4 is only connected to r1 directly. + +r1 is the route reflector. +r1 sets the nexthop to itself when advertising routes to r4. + +r2 sources 10.0.2.2/32 with agigp-metric 2. + +Results: + +r1, r2 and r3 should have aigp-meric 2. +r4 should have aigp-metric 12, i.e., aigp + nexthop-metric. +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 5): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r3"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r4"]) + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for _, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_aigp_rr(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + r3 = tgen.gears["r3"] + r4 = tgen.gears["r4"] + + def _bgp_check_aigp_metric(router, prefix, aigp): + output = json.loads( + router.vtysh_cmd("show bgp ipv4 unicast {} json".format(prefix)) + ) + expected = {"paths": [{"aigpMetric": aigp, "valid": True}]} + return topotest.json_cmp(output, expected) + + def _bgp_check_aigp_bestpath(): + output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast 10.0.1.2/32 json")) + expected = { + "prefix": "10.0.1.2/32", + "paths": [ + { + "aigpMetric": 50, + "valid": True, + "sourced": True, + "local": True, + "bestpath": {"overall": True, "selectionReason": "Local Route"}, + "nexthops": [ + { + "ip": "0.0.0.0", + "hostname": "r1", + "afi": "ipv4", + "metric": 0, + "accessible": True, + "used": True, + } + ], + }, + { + "aigpMetric": 10, + "valid": True, + "nexthops": [ + { + "ip": "10.0.0.2", + "hostname": "r2", + "afi": "ipv4", + "metric": 10, + "accessible": True, + "used": True, + } + ], + }, + ], + } + return topotest.json_cmp(output, expected) + + # r2, 10.0.2.2/32 with aigp-metric 2 + test_func = functools.partial(_bgp_check_aigp_metric, r2, "10.0.2.2/32", 2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "aigp-metric for 10.0.2.2/32 is not 2" + + # r1, 10.0.2.2/32 with aigp-metric 2 + test_func = functools.partial(_bgp_check_aigp_metric, r1, "10.0.2.2/32", 2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "aigp-metric for 10.0.2.2/32 is not 2" + + # r3, 10.0.2.2/32 with aigp-metric 2 + test_func = functools.partial(_bgp_check_aigp_metric, r3, "10.0.2.2/32", 2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "aigp-metric for 10.0.2.2/32 is not 2" + + # r4, 10.0.2.2/32 with aigp-metric 12: aigp + nexthop-metric + test_func = functools.partial(_bgp_check_aigp_metric, r4, "10.0.2.2/32", 12) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "aigp-metric for 10.0.2.2/32 is not 12" + + # r1, check if the local route is favored over AIGP comparison + test_func = functools.partial(_bgp_check_aigp_bestpath) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "Local route is not favored over AIGP in best-path selection" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_extended_link_bandwidth/r1/frr.conf b/tests/topotests/bgp_extended_link_bandwidth/r1/frr.conf index d0c0813e84..18b312c60f 100644 --- a/tests/topotests/bgp_extended_link_bandwidth/r1/frr.conf +++ b/tests/topotests/bgp_extended_link_bandwidth/r1/frr.conf @@ -22,7 +22,7 @@ ip prefix-list p200 seq 5 permit 10.10.10.200/32 ! route-map r2 permit 10 match ip address prefix-list p40 - set extcommunity bandwidth 40000 + set extcommunity bandwidth 40000 non-transitive route-map r2 permit 20 match ip address prefix-list p100 set extcommunity bandwidth 100000 diff --git a/tests/topotests/bgp_extended_link_bandwidth/r2/frr.conf b/tests/topotests/bgp_extended_link_bandwidth/r2/frr.conf index 5cad150aef..af07c7cc5c 100644 --- a/tests/topotests/bgp_extended_link_bandwidth/r2/frr.conf +++ b/tests/topotests/bgp_extended_link_bandwidth/r2/frr.conf @@ -2,9 +2,15 @@ int r2-eth0 ip address 192.168.1.2/24 ! +int r2-eth1 + ip address 192.168.2.2/24 +! router bgp 65000 no bgp ebgp-requires-policy neighbor 192.168.1.1 remote-as internal neighbor 192.168.1.1 timers 1 3 neighbor 192.168.1.1 timers connect 1 + neighbor 192.168.2.1 remote-as external + neighbor 192.168.2.1 timers 1 3 + neighbor 192.168.2.1 timers connect 1 ! diff --git a/tests/topotests/bgp_extended_link_bandwidth/r3/frr.conf b/tests/topotests/bgp_extended_link_bandwidth/r3/frr.conf new file mode 100644 index 0000000000..61b13c8893 --- /dev/null +++ b/tests/topotests/bgp_extended_link_bandwidth/r3/frr.conf @@ -0,0 +1,16 @@ +! +int r3-eth0 + ip address 192.168.2.1/24 +! +int r3-eth1 + ip address 192.168.3.1/24 +! +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 192.168.2.2 remote-as external + neighbor 192.168.2.2 timers 1 3 + neighbor 192.168.2.2 timers connect 1 + neighbor 192.168.3.2 remote-as external + neighbor 192.168.3.2 timers 1 3 + neighbor 192.168.3.2 timers connect 1 +! diff --git a/tests/topotests/bgp_extended_link_bandwidth/r4/frr.conf b/tests/topotests/bgp_extended_link_bandwidth/r4/frr.conf new file mode 100644 index 0000000000..49f0caf7de --- /dev/null +++ b/tests/topotests/bgp_extended_link_bandwidth/r4/frr.conf @@ -0,0 +1,10 @@ +! +int r4-eth0 + ip address 192.168.3.2/24 +! +router bgp 65004 + no bgp ebgp-requires-policy + neighbor 192.168.3.1 remote-as external + neighbor 192.168.3.1 timers 1 3 + neighbor 192.168.3.1 timers connect 1 +! diff --git a/tests/topotests/bgp_extended_link_bandwidth/test_bgp_extended_link_bandwidth.py b/tests/topotests/bgp_extended_link_bandwidth/test_bgp_extended_link_bandwidth.py index e9006b81c9..3e5be0310f 100644 --- a/tests/topotests/bgp_extended_link_bandwidth/test_bgp_extended_link_bandwidth.py +++ b/tests/topotests/bgp_extended_link_bandwidth/test_bgp_extended_link_bandwidth.py @@ -22,7 +22,7 @@ pytestmark = [pytest.mark.bgpd] def setup_module(mod): - topodef = {"s1": ("r1", "r2")} + topodef = {"s1": ("r1", "r2"), "s2": ("r2", "r3"), "s3": ("r3", "r4")} tgen = Topogen(topodef, mod.__name__) tgen.start_topology() @@ -46,9 +46,11 @@ def test_bgp_dynamic_capability_role(): pytest.skip(tgen.errors) r2 = tgen.gears["r2"] + r3 = tgen.gears["r3"] + r4 = tgen.gears["r4"] - def _bgp_converge(): - output = json.loads(r2.vtysh_cmd("show bgp ipv4 unicast json detail")) + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json detail")) expected = { "routes": { "10.10.10.40/32": { @@ -84,9 +86,60 @@ def test_bgp_dynamic_capability_role(): test_func = functools.partial( _bgp_converge, + r2, ) _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) - assert result is None, "Can't see link bandwidths as expected" + assert result is None, "r2 (iBGP) should see link bandwidth extended communities" + + test_func = functools.partial( + _bgp_converge, + r3, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert ( + result is None + ), "r3 (eBGP) should see link bandwidth extended communities (including non-transitive)" + + def _bgp_check_non_transitive_extended_communities(): + output = json.loads(r4.vtysh_cmd("show bgp ipv4 unicast json detail")) + expected = { + "routes": { + "10.10.10.40/32": { + "paths": [ + { + "extendedIpv6Community": None, + } + ] + }, + "10.10.10.100/32": { + "paths": [ + { + "extendedIpv6Community": { + "string": "LB:65000:12500000000 (100.000 Gbps)", + } + } + ] + }, + "10.10.10.200/32": { + "paths": [ + { + "extendedIpv6Community": { + "string": "LB:65000:25000000000 (200.000 Gbps)", + } + } + ] + }, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_non_transitive_extended_communities, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert ( + result is None + ), "r4 (eBGP) should NOT see non-transitive link bandwidth extended communities" if __name__ == "__main__": diff --git a/tests/topotests/bgp_oad/r1/frr.conf b/tests/topotests/bgp_oad/r1/frr.conf index 39045ba648..f00bdfe7d2 100644 --- a/tests/topotests/bgp_oad/r1/frr.conf +++ b/tests/topotests/bgp_oad/r1/frr.conf @@ -4,6 +4,7 @@ int r1-eth0 ! router bgp 65001 no bgp ebgp-requires-policy + no bgp network import-check neighbor 192.168.1.2 remote-as external neighbor 192.168.1.2 timers 1 3 neighbor 192.168.1.2 timers connect 1 @@ -12,10 +13,14 @@ router bgp 65001 neighbor 192.168.1.4 timers 1 3 neighbor 192.168.1.4 timers connect 1 address-family ipv4 unicast + network 10.10.10.1/32 route-map local neighbor 192.168.1.4 route-map r4 in exit-address-family ! route-map r4 permit 10 set local-preference 123 set metric 123 -exit +! +route-map local permit 10 + set community no-export +! diff --git a/tests/topotests/bgp_oad/test_bgp_oad.py b/tests/topotests/bgp_oad/test_bgp_oad.py index 6dd46fbdaa..b2ea7e0f19 100644 --- a/tests/topotests/bgp_oad/test_bgp_oad.py +++ b/tests/topotests/bgp_oad/test_bgp_oad.py @@ -8,6 +8,8 @@ """ Test if local-preference is passed between different EBGP peers when EBGP-OAD is configured. + +Also check if no-export community is passed to the EBGP-OAD peer. """ import os @@ -51,6 +53,9 @@ def test_bgp_oad(): pytest.skip(tgen.errors) r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + r3 = tgen.gears["r3"] + r4 = tgen.gears["r4"] def _bgp_converge(): output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast 10.10.10.10/32 json")) @@ -85,6 +90,37 @@ def test_bgp_oad(): _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) assert result is None, "Can't converge" + def _bgp_check_no_export(router, arg=[{"valid": True}]): + output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json")) + expected = { + "routes": { + "10.10.10.1/32": arg, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_check_no_export, + r2, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "10.10.10.1/32 should be advertised to r2" + + test_func = functools.partial( + _bgp_check_no_export, + r3, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "10.10.10.1/32 should be advertised to r3" + + test_func = functools.partial( + _bgp_check_no_export, + r4, + None, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "10.10.10.1/32 should not be advertised to r4 (not OAD peer)" + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] diff --git a/tests/topotests/lib/bmp_collector/bmp.py b/tests/topotests/lib/bmp_collector/bmp.py index f3c0be49c5..ac8af02844 100644 --- a/tests/topotests/lib/bmp_collector/bmp.py +++ b/tests/topotests/lib/bmp_collector/bmp.py @@ -10,11 +10,13 @@ BMP main module: - XXX: more bmp messages types to dissect - XXX: complete bgp message dissection """ -import datetime import ipaddress import json import os import struct +import sys + +from datetime import datetime from bgp.update import BGPUpdate from bgp.update.rd import RouteDistinguisher @@ -48,6 +50,13 @@ def log2file(logs, log_file): f.write(json.dumps(logs) + "\n") +def timestamp_print(message, file=sys.stderr): + """Helper function to timestamp_print messages with timestamps.""" + + current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + print(f"[{current_time}] {message}", file=file) + + # ------------------------------------------------------------------------------ class BMPCodes: """ @@ -196,14 +205,18 @@ class BMPMsg: data = data[msglen:] if version != BMPCodes.VERSION: - # XXX: log something + timestamp_print( + f"Expected BMP version {BMPCodes.VERSION} but got version {version}." + ) return data msg_cls = cls.lookup_msg_type(msgtype) if msg_cls == cls.UNKNOWN_TYPE: - # XXX: log something + timestamp_print(f"Got unknown message type ") return data + timestamp_print(f"Got message type: {msg_cls}") + msg_cls.MSG_LEN = msglen - cls.MIN_LEN logs = msg_cls.dissect(msg_data) logs["seq"] = SEQ @@ -281,7 +294,7 @@ class BMPPerPeerMessage: "peer_distinguisher": str(RouteDistinguisher(peer_distinguisher)), "peer_asn": peer_asn, "peer_bgp_id": peer_bgp_id, - "timestamp": str(datetime.datetime.fromtimestamp(timestamp)), + "timestamp": str(datetime.fromtimestamp(timestamp)), } ) diff --git a/tests/topotests/lib/bmp_collector/bmpserver b/tests/topotests/lib/bmp_collector/bmpserver index 5257df7530..56d85fc74b 100755 --- a/tests/topotests/lib/bmp_collector/bmpserver +++ b/tests/topotests/lib/bmp_collector/bmpserver @@ -5,43 +5,105 @@ # Authored by Farid Mihoub <farid.mihoub@6wind.com> # import argparse + # XXX: something more reliable should be used "Twisted" a great choice. +import signal import socket import sys +from datetime import datetime + from bmp import BMPMsg BGP_MAX_SIZE = 4096 +# Global variable to track shutdown signal +shutdown = False + + parser = argparse.ArgumentParser() parser.add_argument("-a", "--address", type=str, default="0.0.0.0") parser.add_argument("-p", "--port", type=int, default=1789) parser.add_argument("-l", "--logfile", type=str, default="/var/log/bmp.log") + +def handle_signal(signum, frame): + global shutdown + timestamp_print(f"Received signal {signum}, shutting down.") + shutdown = True + + +def timestamp_print(message, file=sys.stderr): + """Helper function to timestamp_print messages with timestamps.""" + + current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + print(f"[{current_time}] {message}", file=file) + + def main(): + global shutdown + + # Set up signal handling for SIGTERM and SIGINT + signal.signal(signal.SIGTERM, handle_signal) + signal.signal(signal.SIGINT, handle_signal) + args = parser.parse_args() ADDRESS, PORT = args.address, args.port LOG_FILE = args.logfile + timestamp_print(f"Starting bmpserver on {args.address}:{args.port}") + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - s.bind((ADDRESS, PORT)) - s.listen() - connection, _ = s.accept() - try: - while True: - data = connection.recv(BGP_MAX_SIZE) - while len(data) > BMPMsg.MIN_LEN: - data = BMPMsg.dissect(data, log_file=LOG_FILE) + s.bind((ADDRESS, PORT)) + s.listen() + timestamp_print(f"Listening on TCP {args.address}:{args.port}") + + connection, client_address = s.accept() + timestamp_print(f"TCP session opened from {client_address}") + + try: + while not shutdown: # Check for shutdown signal + data = connection.recv(BGP_MAX_SIZE) + if shutdown: + break + + if not data: + # connection closed + break + + timestamp_print( + f"Data received from {client_address}: length {len(data)}" + ) + + while len(data) > BMPMsg.MIN_LEN: + data = BMPMsg.dissect(data, log_file=LOG_FILE) + + timestamp_print( + f"Finished dissecting data from {client_address}" + ) + + except Exception as e: + timestamp_print(f"{e}") + pass + except KeyboardInterrupt: + timestamp_print(f"Got Keyboard Interrupt.") + pass + finally: + timestamp_print(f"TCP session closed with {client_address}") + connection.close() + except socket.error as sock_err: + timestamp_print(f"Socket error: {e}") except Exception as e: - # XXX: do something - pass - except KeyboardInterrupt: - # XXX: do something - pass + timestamp_print(f"{e}") finally: - connection.close() + timestamp_print(f"Server shutting down on {ADDRESS}:{PORT}") + if __name__ == "__main__": - sys.exit(main()) + try: + sys.exit(main()) + except KeyboardInterrupt: + logging.info("BMP server was interrupted and is shutting down.") + sys.exit(0) diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py index 14dd61b077..641295258e 100644 --- a/tests/topotests/lib/topogen.py +++ b/tests/topotests/lib/topogen.py @@ -1273,16 +1273,24 @@ class TopoBMPCollector(TopoHost): return gear def start(self, log_file=None): + log_dir = os.path.join(self.logdir, self.name) + self.run("chmod 777 {}".format(log_dir)) + + log_err = os.path.join(log_dir, "bmpserver.log") + log_arg = "-l {}".format(log_file) if log_file else "" - self.run( - "{}/bmp_collector/bmpserver -a {} -p {} {}&".format( - CWD, self.ip, self.port, log_arg - ), - stdout=None, - ) + + with open(log_err, "w") as err: + self.run( + "{}/bmp_collector/bmpserver -a {} -p {} {}&".format( + CWD, self.ip, self.port, log_arg + ), + stdout=None, + stderr=err, + ) def stop(self): - self.run("pkill -9 -f bmpserver") + self.run("pkill -f bmpserver") return "" diff --git a/tests/topotests/pim_cand_rp_bsr/r1/frr.conf b/tests/topotests/pim_cand_rp_bsr/r1/frr.conf index d0aa3d529f..899e9c0684 100644 --- a/tests/topotests/pim_cand_rp_bsr/r1/frr.conf +++ b/tests/topotests/pim_cand_rp_bsr/r1/frr.conf @@ -5,21 +5,45 @@ log file /tmp/r1-frr.log ! !debug pim packet !debug pim bsm +!debug pimv6 bsm ! -ip route 0.0.0.0/0 10.0.0.4 +! +interface lo + ip address 10.0.0.1/32 + ipv6 address fd00:0:0:0::1/128 + ip pim + ipv6 pim + ipv6 ospf6 area 0 ! interface r1-eth0 ip address 10.0.0.1/24 + ipv6 address fd00:0:0:0::1/64 ip igmp ip pim + ipv6 pim + ipv6 ospf6 area 0 ! interface r1-eth1 ip address 10.0.1.1/24 + ipv6 address fd00:0:0:1::1/64 ip igmp ip pim + ipv6 pim + ipv6 ospf6 area 0 ! router pim bsr candidate-bsr priority 200 source address 10.0.0.1 ! +router pim6 + bsr candidate-bsr priority 200 source address fd00:0:0:0::1 +! +router ospf + ospf router-id 10.0.0.1 + network 10.0.0.0/16 area 0 +! +router ospf6 + ospf6 router-id 10.0.0.1 +! ip forwarding +ipv6 forwarding ! diff --git a/tests/topotests/pim_cand_rp_bsr/r2/frr.conf b/tests/topotests/pim_cand_rp_bsr/r2/frr.conf index 741c839f19..85af461d5e 100644 --- a/tests/topotests/pim_cand_rp_bsr/r2/frr.conf +++ b/tests/topotests/pim_cand_rp_bsr/r2/frr.conf @@ -3,20 +3,46 @@ hostname r2 password zebra log file /tmp/r2-frr.log ! -ip route 0.0.0.0/0 10.0.0.4 +!debug pim packet +!debug pim bsm +!debug pimv6 bsm +! +interface lo + ip address 10.0.0.2/32 + ipv6 address fd00:0:0:0::2/128 + ip pim + ipv6 pim + ipv6 ospf6 area 0 ! interface r2-eth0 ip address 10.0.0.2/24 + ipv6 address fd00:0:0:0::2/64 ip igmp ip pim + ipv6 pim + ipv6 ospf6 area 0 ! interface r2-eth1 ip address 10.0.2.2/24 + ipv6 address fd00:0:0:2::2/64 ip igmp ip pim + ipv6 pim + ipv6 ospf6 area 0 ! router pim - bsr candidate-bsr priority 100 source address 10.0.0.2 + bsr candidate-bsr priority 100 +! +router pim6 + bsr candidate-bsr priority 100 +! +router ospf + ospf router-id 10.0.0.2 + network 10.0.0.0/16 area 0 +! +router ospf6 + ospf6 router-id 10.0.0.2 ! ip forwarding +ipv6 forwarding ! diff --git a/tests/topotests/pim_cand_rp_bsr/r3/frr.conf b/tests/topotests/pim_cand_rp_bsr/r3/frr.conf index bd5c8ce93f..022c44ea58 100644 --- a/tests/topotests/pim_cand_rp_bsr/r3/frr.conf +++ b/tests/topotests/pim_cand_rp_bsr/r3/frr.conf @@ -5,28 +5,48 @@ log file /tmp/r3-frr.log ! !debug pim packet !debug pim bsm +!debug pimv6 bsm ! -ip route 0.0.0.0/0 10.0.3.4 -ip route 10.0.6.0/24 10.0.3.6 ! interface r3-eth0 ip address 10.0.1.3/24 + ipv6 address fd00:0:0:1::3/64 ip igmp ip pim + ipv6 pim + ipv6 ospf6 area 0 ! interface r3-eth1 ip address 10.0.3.3/24 + ipv6 address fd00:0:0:3::3/64 ip igmp ip pim + ipv6 pim + ipv6 ospf6 area 0 ! interface r3-eth2 ip address 10.0.4.3/24 + ipv6 address fd00:0:0:4::3/64 ip igmp ip pim + ipv6 pim + ipv6 ospf6 area 0 ! router pim bsr candidate-rp group 239.0.0.0/16 bsr candidate-rp priority 10 source address 10.0.3.3 ! +router pim6 + bsr candidate-rp group ffbb::/64 + bsr candidate-rp priority 10 source address fd00:0:0:3::3 +! +router ospf + ospf router-id 10.0.1.3 + network 10.0.0.0/16 area 0 +! +router ospf6 + ospf6 router-id 10.0.1.3 +! ip forwarding +ipv6 forwarding ! diff --git a/tests/topotests/pim_cand_rp_bsr/r4/frr.conf b/tests/topotests/pim_cand_rp_bsr/r4/frr.conf index 825b227728..2d0a035f9a 100644 --- a/tests/topotests/pim_cand_rp_bsr/r4/frr.conf +++ b/tests/topotests/pim_cand_rp_bsr/r4/frr.conf @@ -3,35 +3,64 @@ hostname r4 password zebra log file /tmp/r4-frr.log ! -ip route 10.0.1.0/24 10.0.0.1 -ip route 10.0.4.0/24 10.0.3.3 -ip route 10.0.6.0/24 10.0.3.6 +! +interface lo + ip address 10.0.3.4/32 + ipv6 address fd00:0:0:3::4/64 + ip pim + ipv6 pim ! interface r4-eth0 ip address 10.0.2.4/24 + ipv6 address fd00:0:0:2::4/64 ip igmp ip pim + ipv6 pim + ipv6 ospf6 area 0 ! interface r4-eth1 ip address 10.0.3.4/24 + ipv6 address fd00:0:0:3::4/64 ip igmp ip pim + ipv6 pim + ipv6 ospf6 area 0 ! interface r4-eth2 ip address 10.0.5.4/24 + ipv6 address fd00:0:0:5::4/64 ip igmp ip pim + ipv6 pim + ipv6 ospf6 area 0 ! interface r4-eth3 ip address 10.0.0.4/24 + ipv6 address fd00:0:0:0::4/64 ip igmp ip pim + ipv6 pim + ipv6 ospf6 area 0 ! router pim bsr candidate-rp group 239.0.0.0/24 bsr candidate-rp group 239.0.0.0/16 bsr candidate-rp group 239.0.0.0/8 - bsr candidate-rp priority 20 source address 10.0.3.4 + bsr candidate-rp priority 20 +! +router pim6 + bsr candidate-rp group ffbb::/124 + bsr candidate-rp group ffbb::/64 + bsr candidate-rp group ffbb::/108 + bsr candidate-rp priority 20 +! +router ospf + ospf router-id 10.0.2.4 + network 10.0.0.0/16 area 0 +! +router ospf6 + ospf6 router-id 10.0.2.4 ! ip forwarding +ipv6 forwarding ! diff --git a/tests/topotests/pim_cand_rp_bsr/r5/frr.conf b/tests/topotests/pim_cand_rp_bsr/r5/frr.conf index c934717d08..552e51f417 100644 --- a/tests/topotests/pim_cand_rp_bsr/r5/frr.conf +++ b/tests/topotests/pim_cand_rp_bsr/r5/frr.conf @@ -3,15 +3,32 @@ hostname r5 password zebra log file /tmp/r5-frr.log ! -ip route 0.0.0.0/0 10.0.4.3 ! interface r5-eth0 ip address 10.0.4.5/24 + ipv6 address fd00:0:0:4::5/64 ip igmp ip pim + ipv6 pim + ipv6 ospf6 area 0 ! interface r5-eth1 ip address 10.0.6.5/24 + ipv6 address fd00:0:0:6::5/64 + ip igmp + ip pim + ipv6 pim + ipv6 ospf6 area 0 +! +router pim6 +! +router ospf + ospf router-id 10.0.4.5 + network 10.0.0.0/16 area 0 +! +router ospf6 + ospf6 router-id 10.0.4.5 ! ip forwarding +ipv6 forwarding ! diff --git a/tests/topotests/pim_cand_rp_bsr/r6/frr.conf b/tests/topotests/pim_cand_rp_bsr/r6/frr.conf index fd9d1eb5c4..20955a12c7 100644 --- a/tests/topotests/pim_cand_rp_bsr/r6/frr.conf +++ b/tests/topotests/pim_cand_rp_bsr/r6/frr.conf @@ -3,20 +3,40 @@ hostname r6 password zebra log file /tmp/r6-frr.log ! -ip route 0.0.0.0/0 10.0.6.6 ! interface r6-eth0 ip address 10.0.5.6/24 + ipv6 address fd00:0:0:5::6/64 ip igmp ip pim + ipv6 pim + ipv6 ospf6 area 0 ! interface r6-eth1 ip address 10.0.6.6/24 + ipv6 address fd00:0:0:6::6/64 + ip igmp + ip pim + ipv6 pim + ipv6 ospf6 area 0 ! interface r6-eth2 ip address 10.0.3.6/24 + ipv6 address fd00:0:0:3::6/64 ip igmp ip pim + ipv6 pim + ipv6 ospf6 area 0 +! +router pim6 +! +router ospf + ospf router-id 10.0.5.6 + network 10.0.0.0/16 area 0 +! +router ospf6 + ospf6 router-id 10.0.5.6 ! ip forwarding +ipv6 forwarding ! diff --git a/tests/topotests/pim_cand_rp_bsr/test_pim_cand_rp_bsr.py b/tests/topotests/pim_cand_rp_bsr/test_pim_cand_rp_bsr.py index ce7bc9dc56..96a3241a2b 100644 --- a/tests/topotests/pim_cand_rp_bsr/test_pim_cand_rp_bsr.py +++ b/tests/topotests/pim_cand_rp_bsr/test_pim_cand_rp_bsr.py @@ -59,7 +59,12 @@ CWD = os.path.dirname(os.path.realpath(__file__)) sys.path.append(os.path.join(CWD, "../")) # Required to instantiate the topology builder class. -pytestmark = [pytest.mark.pimd] +pytestmark = [ + pytest.mark.pimd, + pytest.mark.pim6d, + pytest.mark.ospfd, + pytest.mark.ospf6d, +] def build_topo(tgen): @@ -102,6 +107,7 @@ def build_topo(tgen): sw1.add_link(tgen.gears["r4"]) sw3.add_link(tgen.gears["r6"]) + def setup_module(mod): logger.info("PIM Candidate RP/BSR:\n {}".format(TOPOLOGY)) @@ -125,6 +131,7 @@ def teardown_module(mod): tgen = get_topogen() tgen.stop_topology() + def test_pim_bsr_election_r1(request): "Test PIM BSR Election" tgen = get_topogen() @@ -137,19 +144,18 @@ def test_pim_bsr_election_r1(request): r2 = tgen.gears["r2"] # r1 should be the BSR winner because it has higher priority expected = { - "bsr":"10.0.0.1", - "priority":200, - "state":"ACCEPT_PREFERRED", + "bsr": "10.0.0.1", + "priority": 200, + "state": "ACCEPT_PREFERRED", } - test_func = partial( - topotest.router_json_cmp, r2, "show ip pim bsr json", expected - ) + test_func = partial(topotest.router_json_cmp, r2, "show ip pim bsr json", expected) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assertmsg = "r2: r1 was not elected, bsr election mismatch" assert result is None, assertmsg + def test_pim_bsr_cand_bsr_r1(request): "Test PIM BSR candidate BSR" tgen = get_topogen() @@ -162,11 +168,7 @@ def test_pim_bsr_cand_bsr_r1(request): r2 = tgen.gears["r2"] # r2 is a candidate bsr with low priority: elected = False - expected = { - "address": "10.0.0.2", - "priority": 100, - "elected": False - } + expected = {"address": "10.0.0.2", "priority": 100, "elected": False} test_func = partial( topotest.router_json_cmp, r2, "show ip pim bsr candidate-bsr json", expected ) @@ -175,6 +177,7 @@ def test_pim_bsr_cand_bsr_r1(request): assertmsg = "r2: candidate bsr mismatch " assert result is None, assertmsg + def test_pim_bsr_cand_rp(request): "Test PIM BSR candidate RP" tgen = get_topogen() @@ -187,10 +190,7 @@ def test_pim_bsr_cand_rp(request): r3 = tgen.gears["r3"] # r3 is a candidate rp - expected = { - "address":"10.0.3.3", - "priority":10 - } + expected = {"address": "10.0.3.3", "priority": 10} test_func = partial( topotest.router_json_cmp, r3, "show ip pim bsr candidate-rp json", expected ) @@ -211,29 +211,95 @@ def test_pim_bsr_rp_info(request): # At this point, all nodes, including r5 should have synced the RP state step("Verify rp-info on r5 from BSR") - result = verify_pim_rp_info(tgen, None, "r5", "239.0.0.0/16", None, "10.0.3.3", - "BSR", False, "ipv4", True, retry_timeout = 90) + result = verify_pim_rp_info( + tgen, + None, + "r5", + "239.0.0.0/16", + None, + "10.0.3.3", + "BSR", + False, + "ipv4", + True, + retry_timeout=90, + ) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) - result = verify_pim_rp_info(tgen, None, "r5", "239.0.0.0/8", None, "10.0.3.4", - "BSR", False, "ipv4", True, retry_timeout = 30) + result = verify_pim_rp_info( + tgen, + None, + "r5", + "239.0.0.0/8", + None, + "10.0.3.4", + "BSR", + False, + "ipv4", + True, + retry_timeout=30, + ) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) - result = verify_pim_rp_info(tgen, None, "r5", "239.0.0.0/24", None, "10.0.3.4", - "BSR", False, "ipv4", True, retry_timeout = 30) + result = verify_pim_rp_info( + tgen, + None, + "r5", + "239.0.0.0/24", + None, + "10.0.3.4", + "BSR", + False, + "ipv4", + True, + retry_timeout=30, + ) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) step("Verify rp-info on the BSR node itself r1") - result = verify_pim_rp_info(tgen, None, "r1", "239.0.0.0/16", None, "10.0.3.3", - "BSR", False, "ipv4", True, retry_timeout = 10) + result = verify_pim_rp_info( + tgen, + None, + "r1", + "239.0.0.0/16", + None, + "10.0.3.3", + "BSR", + False, + "ipv4", + True, + retry_timeout=10, + ) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) - result = verify_pim_rp_info(tgen, None, "r1", "239.0.0.0/8", None, "10.0.3.4", - "BSR", False, "ipv4", True, retry_timeout = 10) + result = verify_pim_rp_info( + tgen, + None, + "r1", + "239.0.0.0/8", + None, + "10.0.3.4", + "BSR", + False, + "ipv4", + True, + retry_timeout=10, + ) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) - result = verify_pim_rp_info(tgen, None, "r1", "239.0.0.0/24", None, "10.0.3.4", - "BSR", False, "ipv4", True, retry_timeout = 10) + result = verify_pim_rp_info( + tgen, + None, + "r1", + "239.0.0.0/24", + None, + "10.0.3.4", + "BSR", + False, + "ipv4", + True, + retry_timeout=10, + ) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) @@ -253,7 +319,8 @@ def test_pim_bsr_election_fallback_r2(request): configure router pim no bsr candidate-bsr priority 200 source address 10.0.0.1 - """) + """ + ) step("Verify r1 is no longer a BSR candidate") expected = {} @@ -269,16 +336,14 @@ def test_pim_bsr_election_fallback_r2(request): r2 = tgen.gears["r2"] # We should fall back to r2 as the BSR expected = { - "bsr":"10.0.0.2", - "priority":100, - "state":"BSR_ELECTED", + "bsr": "10.0.0.2", + "priority": 100, + "state": "BSR_ELECTED", } step("Verify that we fallback to r2 as the new BSR") - test_func = partial( - topotest.router_json_cmp, r2, "show ip pim bsr json", expected - ) + test_func = partial(topotest.router_json_cmp, r2, "show ip pim bsr json", expected) _, result = topotest.run_and_expect(test_func, None, count=180, wait=1) assertmsg = "r2: failed to fallback to r2 as a BSR" @@ -301,12 +366,129 @@ def test_pim_bsr_rp_info_fallback(request): configure router pim no bsr candidate-rp group 239.0.0.0/16 - """) + """ + ) step("Verify falling back to r4 as the new RP for 239.0.0.0/16") - result = verify_pim_rp_info(tgen, None, "r5", "239.0.0.0/16", None, "10.0.3.4", - "BSR", False, "ipv4", True, retry_timeout = 30) + result = verify_pim_rp_info( + tgen, + None, + "r5", + "239.0.0.0/16", + None, + "10.0.3.4", + "BSR", + False, + "ipv4", + True, + retry_timeout=30, + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + +def test_pimv6_bsr_election_r1(request): + "Test PIMv6 BSR Election" + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + r2 = tgen.gears["r2"] + # r1 should be the BSR winner because it has higher priority + expected = { + "bsr": "fd00::1", + "priority": 200, + "state": "ACCEPT_PREFERRED", + } + + test_func = partial( + topotest.router_json_cmp, r2, "show ipv6 pim bsr json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + + assertmsg = "r2: r1 was not elected, IPv6 bsr election mismatch" + assert result is None, assertmsg + + +def test_pimv6_bsr_cand_rp(request): + "Test PIMv6 BSR candidate RP" + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + r3 = tgen.gears["r3"] + + # r3 is a candidate rp + expected = {"address": "fd00:0:0:3::3", "priority": 10} + test_func = partial( + topotest.router_json_cmp, r3, "show ipv6 pim bsr candidate-rp json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + + assertmsg = "r3: bsr candidate rp mismatch" + assert result is None, assertmsg + + +def test_pimv6_bsr_rp_info(request): + "Test IPv6 RP info state" + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + # At this point, all nodes, including r5 should have synced the RP state + step("Verify rp-info on r5 from BSR") + result = verify_pim_rp_info( + tgen, + None, + "r5", + "ffbb::0/64", + None, + "fd00:0:0:3::3", + "BSR", + False, + "ipv6", + True, + retry_timeout=90, + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + result = verify_pim_rp_info( + tgen, + None, + "r5", + "ffbb::0/124", + None, + "fd00:0:0:3::4", + "BSR", + False, + "ipv6", + True, + retry_timeout=30, + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + result = verify_pim_rp_info( + tgen, + None, + "r5", + "ffbb::0/108", + None, + "fd00:0:0:3::4", + "BSR", + False, + "ipv6", + True, + retry_timeout=30, + ) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) diff --git a/yang/frr-isisd.yang b/yang/frr-isisd.yang index 60914b0be9..a3e073f626 100644 --- a/yang/frr-isisd.yang +++ b/yang/frr-isisd.yang @@ -1922,7 +1922,7 @@ module frr-isisd { if set to disable, ISISEXPLICITNULLFlag will override the value of ISISPHPFlag"; leaf algo { - type uint32 { + type uint8 { range "128..255"; } description diff --git a/zebra/debug.c b/zebra/debug.c index cf1701be19..7b6a19fa1d 100644 --- a/zebra/debug.c +++ b/zebra/debug.c @@ -29,6 +29,7 @@ unsigned long zebra_debug_evpn_mh; unsigned long zebra_debug_pbr; unsigned long zebra_debug_neigh; unsigned long zebra_debug_tc; +unsigned long zebra_debug_srv6; DEFINE_HOOK(zebra_debug_show_debugging, (struct vty *vty), (vty)); @@ -121,6 +122,9 @@ DEFUN_NOSH (show_debugging_zebra, if (IS_ZEBRA_DEBUG_PBR) vty_out(vty, " Zebra PBR debugging is on\n"); + if (IS_ZEBRA_DEBUG_SRV6) + vty_out(vty, " Zebra SRv6 is on\n"); + hook_call(zebra_debug_show_debugging, vty); cmd_show_lib_debugs(vty); @@ -372,6 +376,21 @@ DEFUN (debug_zebra_tc, return CMD_SUCCESS; } +DEFPY(debug_zebra_srv6, + debug_zebra_srv6_cmd, + "[no$no] debug zebra srv6", + NO_STR + DEBUG_STR + "Zebra configuration\n" + "Debug zebra SRv6 events\n") +{ + if (no) + UNSET_FLAG(zebra_debug_srv6, ZEBRA_DEBUG_SRV6); + else + SET_FLAG(zebra_debug_srv6, ZEBRA_DEBUG_SRV6); + return CMD_SUCCESS; +} + DEFPY (debug_zebra_mlag, debug_zebra_mlag_cmd, "[no$no] debug zebra mlag", @@ -754,6 +773,11 @@ static int config_write_debug(struct vty *vty) write++; } + if (IS_ZEBRA_DEBUG_SRV6) { + vty_out(vty, "debug zebra srv6\n"); + write++; + } + return write; } @@ -793,6 +817,7 @@ void zebra_debug_init(void) install_element(ENABLE_NODE, &debug_zebra_rib_cmd); install_element(ENABLE_NODE, &debug_zebra_fpm_cmd); install_element(ENABLE_NODE, &debug_zebra_dplane_cmd); + install_element(ENABLE_NODE, &debug_zebra_srv6_cmd); install_element(ENABLE_NODE, &debug_zebra_mlag_cmd); install_element(ENABLE_NODE, &debug_zebra_nexthop_cmd); install_element(ENABLE_NODE, &debug_zebra_pbr_cmd); @@ -845,6 +870,7 @@ void zebra_debug_init(void) install_element(CONFIG_NODE, &no_debug_zebra_fpm_cmd); install_element(CONFIG_NODE, &no_debug_zebra_dplane_cmd); install_element(CONFIG_NODE, &no_debug_zebra_pbr_cmd); + install_element(CONFIG_NODE, &debug_zebra_srv6_cmd); install_element(CONFIG_NODE, &debug_zebra_mlag_cmd); install_element(CONFIG_NODE, &debug_zebra_evpn_mh_cmd); diff --git a/zebra/debug.h b/zebra/debug.h index 075d903c6b..b4e5ee4b5b 100644 --- a/zebra/debug.h +++ b/zebra/debug.h @@ -62,6 +62,8 @@ extern "C" { #define ZEBRA_DEBUG_TC 0x01 +#define ZEBRA_DEBUG_SRV6 0x01 + /* Debug related macro. */ #define IS_ZEBRA_DEBUG_EVENT (zebra_debug_event & ZEBRA_DEBUG_EVENT) @@ -122,6 +124,8 @@ extern "C" { #define IS_ZEBRA_DEBUG_TC (zebra_debug_tc & ZEBRA_DEBUG_TC) +#define IS_ZEBRA_DEBUG_SRV6 (zebra_debug_srv6 & ZEBRA_DEBUG_SRV6) + extern unsigned long zebra_debug_event; extern unsigned long zebra_debug_packet; extern unsigned long zebra_debug_kernel; @@ -139,6 +143,7 @@ extern unsigned long zebra_debug_evpn_mh; extern unsigned long zebra_debug_pbr; extern unsigned long zebra_debug_neigh; extern unsigned long zebra_debug_tc; +extern unsigned long zebra_debug_srv6; extern void zebra_debug_init(void); diff --git a/zebra/dplane_fpm_nl.c b/zebra/dplane_fpm_nl.c index 8a967978cb..e6b4af3674 100644 --- a/zebra/dplane_fpm_nl.c +++ b/zebra/dplane_fpm_nl.c @@ -1712,6 +1712,16 @@ static int fpm_nl_process(struct zebra_dplane_provider *prov) * anyway. */ if (fnc->socket != -1 && fnc->connecting == false) { + enum dplane_op_e op = dplane_ctx_get_op(ctx); + + /* + * Just skip multicast routes and let them flow through + */ + if ((op == DPLANE_OP_ROUTE_DELETE || op == DPLANE_OP_ROUTE_INSTALL || + op == DPLANE_OP_ROUTE_UPDATE) && + dplane_ctx_get_safi(ctx) == SAFI_MULTICAST) + goto skip; + frr_with_mutex (&fnc->ctxqueue_mutex) { dplane_ctx_enqueue_tail(&fnc->ctxqueue, ctx); cur_queue = @@ -1722,7 +1732,7 @@ static int fpm_nl_process(struct zebra_dplane_provider *prov) peak_queue = cur_queue; continue; } - +skip: dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS); dplane_provider_enqueue_out_ctx(prov, ctx); } diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index 3547314f84..0c607dfa67 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -405,10 +405,6 @@ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id, return netlink_route_change(h, ns_id, startup); case RTM_DELROUTE: return netlink_route_change(h, ns_id, startup); - case RTM_NEWLINK: - return netlink_link_change(h, ns_id, startup); - case RTM_DELLINK: - return 0; case RTM_NEWNEIGH: case RTM_DELNEIGH: case RTM_GETNEIGH: @@ -438,6 +434,8 @@ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id, return 0; /* Messages handled in the dplane thread */ + case RTM_NEWLINK: + case RTM_DELLINK: case RTM_NEWADDR: case RTM_DELADDR: case RTM_NEWNETCONF: @@ -934,7 +932,7 @@ static int netlink_recv_msg(struct nlsock *nl, struct msghdr *msg) } while (status == -1 && errno == EINTR); if (status == -1) { - if (errno == EWOULDBLOCK || errno == EAGAIN) + if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EMSGSIZE) return 0; flog_err(EC_ZEBRA_RECVMSG_OVERRUN, "%s recvmsg overrun: %s", nl->name, safe_strerror(errno)); diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index dc679ed495..ab07ef8d21 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -3178,6 +3178,9 @@ netlink_put_route_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx) } else return FRR_NETLINK_ERROR; + if (dplane_ctx_get_safi(ctx) == SAFI_MULTICAST) + return FRR_NETLINK_SUCCESS; + if (RSYSTEM_ROUTE(dplane_ctx_get_type(ctx))) return FRR_NETLINK_SUCCESS; diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index 0bfcd518ca..4444eda94b 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -317,12 +317,12 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx) frr_with_privs(&zserv_privs) { if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_DELETE) { - if (!RSYSTEM_ROUTE(type)) + if (!RSYSTEM_ROUTE(type) && dplane_ctx_get_safi(ctx) != SAFI_MULTICAST) kernel_rtm(RTM_DELETE, dplane_ctx_get_dest(ctx), dplane_ctx_get_ng(ctx), dplane_ctx_get_metric(ctx)); } else if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_INSTALL) { - if (!RSYSTEM_ROUTE(type)) + if (!RSYSTEM_ROUTE(type) && dplane_ctx_get_safi(ctx) != SAFI_MULTICAST) kernel_rtm(RTM_ADD, dplane_ctx_get_dest(ctx), dplane_ctx_get_ng(ctx), dplane_ctx_get_metric(ctx)); @@ -330,12 +330,12 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx) /* Must do delete and add separately - * no update available */ - if (!RSYSTEM_ROUTE(old_type)) + if (!RSYSTEM_ROUTE(old_type) && dplane_ctx_get_safi(ctx) != SAFI_MULTICAST) kernel_rtm(RTM_DELETE, dplane_ctx_get_dest(ctx), dplane_ctx_get_old_ng(ctx), dplane_ctx_get_old_metric(ctx)); - if (!RSYSTEM_ROUTE(type)) + if (!RSYSTEM_ROUTE(type) && dplane_ctx_get_safi(ctx) != SAFI_MULTICAST) kernel_rtm(RTM_ADD, dplane_ctx_get_dest(ctx), dplane_ctx_get_ng(ctx), dplane_ctx_get_metric(ctx)); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 72421dc8ee..aea39b8ecf 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -637,23 +637,12 @@ int zebra_rib_labeled_unicast(struct route_entry *re) void rib_install_kernel(struct route_node *rn, struct route_entry *re, struct route_entry *old) { - struct nexthop *nexthop; struct rib_table_info *info = srcdest_rnode_table_info(rn); struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(re->vrf_id); - const struct prefix *p, *src_p; enum zebra_dplane_result ret; rib_dest_t *dest = rib_dest_from_rnode(rn); - srcdest_rnode_prefixes(rn, &p, &src_p); - - if (info->safi != SAFI_UNICAST) { - for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); - return; - } - - /* * Install the resolved nexthop object first. */ @@ -720,17 +709,8 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re, /* Uninstall the route from kernel. */ void rib_uninstall_kernel(struct route_node *rn, struct route_entry *re) { - struct nexthop *nexthop; - struct rib_table_info *info = srcdest_rnode_table_info(rn); struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(re->vrf_id); - if (info->safi != SAFI_UNICAST) { - UNSET_FLAG(re->status, ROUTE_ENTRY_INSTALLED); - for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); - return; - } - /* * Make sure we update the FPM any time we send new information to * the dataplane. @@ -1268,8 +1248,15 @@ static void rib_process(struct route_node *rn) struct zebra_vrf *zvrf = NULL; struct vrf *vrf; struct route_entry *proto_re_changed = NULL; - vrf_id_t vrf_id = VRF_UNKNOWN; + safi_t safi = SAFI_UNICAST; + + if (IS_ZEBRA_DEBUG_RIB || IS_ZEBRA_DEBUG_RIB_DETAILED) { + struct rib_table_info *info = srcdest_rnode_table_info(rn); + + assert(info); + safi = info->safi; + } assert(rn); @@ -1295,9 +1282,8 @@ static void rib_process(struct route_node *rn) if (IS_ZEBRA_DEBUG_RIB_DETAILED) { struct route_entry *re = re_list_first(&dest->routes); - zlog_debug("%s(%u:%u):%pRN: Processing rn %p", - VRF_LOGNAME(vrf), vrf_id, re->table, rn, - rn); + zlog_debug("%s(%u:%u:%u):%pRN: Processing rn %p", VRF_LOGNAME(vrf), vrf_id, + re->table, safi, rn, rn); } old_fib = dest->selected_fib; @@ -1307,15 +1293,12 @@ static void rib_process(struct route_node *rn) char flags_buf[128]; char status_buf[128]; - zlog_debug( - "%s(%u:%u):%pRN: Examine re %p (%s) status: %sflags: %sdist %d metric %d", - VRF_LOGNAME(vrf), vrf_id, re->table, rn, re, - zebra_route_string(re->type), - _dump_re_status(re, status_buf, - sizeof(status_buf)), - zclient_dump_route_flags(re->flags, flags_buf, - sizeof(flags_buf)), - re->distance, re->metric); + zlog_debug("%s(%u:%u:%u):%pRN: Examine re %p (%s) status: %sflags: %sdist %d metric %d", + VRF_LOGNAME(vrf), vrf_id, re->table, safi, rn, re, + zebra_route_string(re->type), + _dump_re_status(re, status_buf, sizeof(status_buf)), + zclient_dump_route_flags(re->flags, flags_buf, sizeof(flags_buf)), + re->distance, re->metric); } /* Currently selected re. */ @@ -1339,7 +1322,6 @@ static void rib_process(struct route_node *rn) if (CHECK_FLAG(re->status, ROUTE_ENTRY_CHANGED)) { proto_re_changed = re; if (!nexthop_active_update(rn, re, old_fib)) { - const struct prefix *p; struct rib_table_info *info; if (re->type == ZEBRA_ROUTE_TABLE) { @@ -1373,7 +1355,6 @@ static void rib_process(struct route_node *rn) } info = srcdest_rnode_table_info(rn); - srcdest_rnode_prefixes(rn, &p, NULL); zsend_route_notify_owner( rn, re, ZAPI_ROUTE_FAIL_INSTALL, info->afi, info->safi); @@ -1441,11 +1422,10 @@ static void rib_process(struct route_node *rn) : old_fib ? old_fib : new_fib ? new_fib : NULL; - zlog_debug( - "%s(%u:%u):%pRN: After processing: old_selected %p new_selected %p old_fib %p new_fib %p", - VRF_LOGNAME(vrf), vrf_id, entry ? entry->table : 0, rn, - (void *)old_selected, (void *)new_selected, - (void *)old_fib, (void *)new_fib); + zlog_debug("%s(%u:%u:%u):%pRN: After processing: old_selected %p new_selected %p old_fib %p new_fib %p", + VRF_LOGNAME(vrf), vrf_id, entry ? entry->table : 0, safi, rn, + (void *)old_selected, (void *)new_selected, (void *)old_fib, + (void *)new_fib); } /* Buffer ROUTE_ENTRY_CHANGED here, because it will get cleared if diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index 35486a4cd0..640e6551a7 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -1271,7 +1271,7 @@ void show_nexthop_json_helper(json_object *json_nexthop, bool display_vrfid = false; uint8_t rn_family; - if (re == NULL || nexthop->vrf_id != re->vrf_id) + if ((re == NULL || nexthop->vrf_id != re->vrf_id) && nexthop->type != NEXTHOP_TYPE_BLACKHOLE) display_vrfid = true; if (rn) @@ -1292,7 +1292,7 @@ void show_route_nexthop_helper(struct vty *vty, const struct route_node *rn, bool display_vrfid = false; uint8_t rn_family; - if (re == NULL || nexthop->vrf_id != re->vrf_id) + if ((re == NULL || nexthop->vrf_id != re->vrf_id) && nexthop->type != NEXTHOP_TYPE_BLACKHOLE) display_vrfid = true; if (rn) diff --git a/zebra/zebra_srv6.c b/zebra/zebra_srv6.c index 082d4609aa..92015684f4 100644 --- a/zebra/zebra_srv6.c +++ b/zebra/zebra_srv6.c @@ -214,7 +214,7 @@ void zebra_srv6_locator_format_set(struct srv6_locator *locator, locator->sid_format = format; - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: Locator %s format has changed, old=%s new=%s", __func__, locator->name, locator->sid_format ? ((struct srv6_sid_format *) @@ -237,7 +237,7 @@ void zebra_srv6_locator_format_set(struct srv6_locator *locator, zebra_srv6_sid_ctx_free(ctx); } - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: Locator %s format has changed, send SRV6_LOCATOR_DEL notification to zclients", __func__, locator->name); @@ -269,7 +269,7 @@ void zebra_srv6_locator_format_set(struct srv6_locator *locator, block_new->refcnt++; locator->sid_block = block_new; - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: Locator %s format has changed, send SRV6_LOCATOR_ADD notification to zclients", __func__, locator->name); @@ -293,13 +293,13 @@ void zebra_srv6_sid_format_changed_cb(struct srv6_sid_format *format) struct listnode *node, *nnode; struct zebra_srv6_sid_ctx *ctx; - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: SID format %s has changed. Notifying zclients.", __func__, format->name); for (ALL_LIST_ELEMENTS_RO(srv6->locators, node, locator)) { if (locator->sid_format == format) { - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: Locator %s has changed because its format (%s) has been modified. Notifying zclients.", __func__, locator->name, format->name); @@ -801,7 +801,7 @@ static int zebra_srv6_manager_get_locator_chunk(struct srv6_locator **loc, if (!*loc) zlog_err("Unable to assign locator chunk to %s instance %u", zebra_route_string(client->proto), client->instance); - else if (IS_ZEBRA_DEBUG_PACKET) + else if (IS_ZEBRA_DEBUG_SRV6) zlog_info("Assigned locator chunk %s to %s instance %u", (*loc)->name, zebra_route_string(client->proto), client->instance); @@ -835,7 +835,7 @@ static int release_srv6_locator_chunk(uint8_t proto, uint16_t instance, if (!loc) return -1; - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: Releasing srv6-locator on %s", __func__, locator_name); @@ -892,7 +892,7 @@ int release_daemon_srv6_locator_chunks(struct zserv *client) struct srv6_locator *loc; struct srv6_locator_chunk *chunk; - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: Releasing chunks for client proto %s, instance %d, session %u", __func__, zebra_route_string(client->proto), client->instance, client->session_id); @@ -912,7 +912,7 @@ int release_daemon_srv6_locator_chunks(struct zserv *client) } } - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: Released %d srv6-locator chunks", __func__, count); @@ -1159,7 +1159,7 @@ static bool alloc_srv6_sid_func_explicit(struct zebra_srv6_sid_block *block, format = block->sid_format; - if (ZEBRA_DEBUG_PACKET) + if (ZEBRA_DEBUG_SRV6) zlog_debug("%s: trying to allocate explicit SID function %u from block %pFX", __func__, sid_func, &block->prefix); @@ -1305,7 +1305,7 @@ static bool alloc_srv6_sid_func_explicit(struct zebra_srv6_sid_block *block, block->u.uncompressed.num_func_allocated++; } - if (ZEBRA_DEBUG_PACKET) + if (ZEBRA_DEBUG_SRV6) zlog_debug("%s: allocated explicit SID function %u from block %pFX", __func__, sid_func, &block->prefix); @@ -1331,7 +1331,7 @@ static bool alloc_srv6_sid_func_dynamic(struct zebra_srv6_sid_block *block, format = block->sid_format; - if (ZEBRA_DEBUG_PACKET) + if (ZEBRA_DEBUG_SRV6) zlog_debug("%s: trying to allocate dynamic SID function from block %pFX", __func__, &block->prefix); @@ -1465,7 +1465,7 @@ static bool alloc_srv6_sid_func_dynamic(struct zebra_srv6_sid_block *block, block->u.uncompressed.num_func_allocated++; } - if (ZEBRA_DEBUG_PACKET) + if (ZEBRA_DEBUG_SRV6) zlog_debug("%s: allocated dynamic SID function %u from block %pFX", __func__, *sid_func, &block->prefix); @@ -1510,7 +1510,7 @@ static int get_srv6_sid_explicit(struct zebra_srv6_sid **sid, * return the existing SID */ if (sid_same(&s->sid->value, sid_value)) { - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: returning existing SRv6 SID %pI6 ctx %s", __func__, &s->sid->value, srv6_sid_ctx2str(buf, @@ -1569,7 +1569,7 @@ static int get_srv6_sid_explicit(struct zebra_srv6_sid **sid, * deallocate the current SID function before allocating the new one */ if (zctx->sid) { - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: ctx %s already associated with a dynamic SID %pI6, releasing dynamic SID", __func__, srv6_sid_ctx2str(buf, sizeof(buf), @@ -1595,7 +1595,7 @@ static int get_srv6_sid_explicit(struct zebra_srv6_sid **sid, zctx->sid = *sid; listnode_add(srv6->sids, zctx); - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: allocated explicit SRv6 SID %pI6 for context %s", __func__, &(*sid)->value, srv6_sid_ctx2str(buf, sizeof(buf), ctx)); @@ -1648,7 +1648,7 @@ static int get_srv6_sid_dynamic(struct zebra_srv6_sid **sid, } } if (memcmp(&s->ctx, ctx, sizeof(struct srv6_sid_ctx)) == 0) { - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: returning existing SID %s %pI6", __func__, srv6_sid_ctx2str(buf, sizeof(buf), @@ -1695,7 +1695,7 @@ static int get_srv6_sid_dynamic(struct zebra_srv6_sid **sid, zctx->sid = *sid; listnode_add(srv6->sids, zctx); - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: allocated new dynamic SRv6 SID %pI6 for context %s", __func__, &(*sid)->value, srv6_sid_ctx2str(buf, sizeof(buf), ctx)); @@ -1733,7 +1733,7 @@ int get_srv6_sid(struct zebra_srv6_sid **sid, struct srv6_sid_ctx *ctx, (sid_value) ? SRV6_SID_ALLOC_MODE_EXPLICIT : SRV6_SID_ALLOC_MODE_DYNAMIC; - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: received SRv6 SID alloc request: SID ctx %s (%pI6), mode=%s", __func__, srv6_sid_ctx2str(buf, sizeof(buf), ctx), sid_value, srv6_sid_alloc_mode2str(alloc_mode)); @@ -1794,7 +1794,7 @@ static bool release_srv6_sid_func_explicit(struct zebra_srv6_sid_block *block, format = block->sid_format; - if (ZEBRA_DEBUG_PACKET) + if (ZEBRA_DEBUG_SRV6) zlog_debug("%s: trying to release explicit SRv6 SID function %u from block %pFX", __func__, sid_func, &block->prefix); @@ -1918,7 +1918,7 @@ static bool release_srv6_sid_func_explicit(struct zebra_srv6_sid_block *block, zebra_srv6_sid_func_free(sid_func_ptr); } - if (ZEBRA_DEBUG_PACKET) + if (ZEBRA_DEBUG_SRV6) zlog_debug("%s: released explicit SRv6 SID function %u from block %pFX", __func__, sid_func, &block->prefix); @@ -1944,7 +1944,7 @@ static int release_srv6_sid_func_dynamic(struct zebra_srv6_sid_block *block, format = block->sid_format; - if (ZEBRA_DEBUG_PACKET) + if (ZEBRA_DEBUG_SRV6) zlog_debug("%s: trying to release dynamic SRv6 SID function %u from block %pFX", __func__, sid_func, &block->prefix); @@ -2107,7 +2107,7 @@ static int release_srv6_sid_func_dynamic(struct zebra_srv6_sid_block *block, } } - if (ZEBRA_DEBUG_PACKET) + if (ZEBRA_DEBUG_SRV6) zlog_debug("%s: released dynamic SRv6 SID function %u from block %pFX", __func__, sid_func, &block->prefix); @@ -2129,7 +2129,7 @@ int release_srv6_sid(struct zserv *client, struct zebra_srv6_sid_ctx *zctx) if (!zctx || !zctx->sid) return -1; - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: releasing SRv6 SID %pI6 associated with ctx %s (proto=%u, instance=%u)", __func__, &zctx->sid->value, srv6_sid_ctx2str(buf, sizeof(buf), &zctx->ctx), @@ -2145,7 +2145,7 @@ int release_srv6_sid(struct zserv *client, struct zebra_srv6_sid_ctx *zctx) /* Remove the client from the list of clients using the SID */ listnode_delete(zctx->sid->client_list, client); - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: released SRv6 SID %pI6 associated with ctx %s (proto=%u, instance=%u)", __func__, &zctx->sid->value, srv6_sid_ctx2str(buf, sizeof(buf), &zctx->ctx), @@ -2156,7 +2156,7 @@ int release_srv6_sid(struct zserv *client, struct zebra_srv6_sid_ctx *zctx) * and remove it from the SRv6 database. */ if (listcount(zctx->sid->client_list) == 0) { - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: SRv6 SID %pI6 associated with ctx %s is no longer in use, removing it from SRv6 database", __func__, &zctx->sid->value, srv6_sid_ctx2str(buf, sizeof(buf), @@ -2251,7 +2251,7 @@ static int srv6_manager_get_sid_internal(struct zebra_srv6_sid **sid, struct zserv *c; char buf[256]; - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: getting SRv6 SID for ctx %s, sid_value=%pI6, locator_name=%s", __func__, srv6_sid_ctx2str(buf, sizeof(buf), ctx), sid_value ? sid_value : &in6addr_any, locator_name); @@ -2266,7 +2266,7 @@ static int srv6_manager_get_sid_internal(struct zebra_srv6_sid **sid, zsend_srv6_sid_notify(client, ctx, sid_value, 0, 0, NULL, ZAPI_SRV6_SID_FAIL_ALLOC); } else if (ret == 0) { - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: got existing SRv6 SID for ctx %s: sid_value=%pI6 (func=%u) (proto=%u, instance=%u, sessionId=%u), notify client", __func__, srv6_sid_ctx2str(buf, sizeof(buf), ctx), @@ -2281,7 +2281,7 @@ static int srv6_manager_get_sid_internal(struct zebra_srv6_sid **sid, : NULL, ZAPI_SRV6_SID_ALLOCATED); } else { - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: got new SRv6 SID for ctx %s: sid_value=%pI6 (func=%u) (proto=%u, instance=%u, sessionId=%u), notifying all clients", __func__, srv6_sid_ctx2str(buf, sizeof(buf), ctx), @@ -2318,7 +2318,7 @@ int release_daemon_srv6_sids(struct zserv *client) int count = 0; int ret; - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: releasing SRv6 SIDs for client proto %s, instance %d, session %u", __func__, zebra_route_string(client->proto), client->instance, client->session_id); @@ -2333,7 +2333,7 @@ int release_daemon_srv6_sids(struct zserv *client) count++; } - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: released %d SRv6 SIDs", __func__, count); return count; @@ -2356,7 +2356,7 @@ static int srv6_manager_release_sid_internal(struct zserv *client, char buf[256]; const char *locator_name = NULL; - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: releasing SRv6 SID associated with ctx %s", __func__, srv6_sid_ctx2str(buf, sizeof(buf), ctx)); @@ -2370,7 +2370,7 @@ static int srv6_manager_release_sid_internal(struct zserv *client, break; } - if (IS_ZEBRA_DEBUG_PACKET) + if (IS_ZEBRA_DEBUG_SRV6) zlog_debug("%s: no SID associated with ctx %s", __func__, srv6_sid_ctx2str(buf, sizeof(buf), ctx)); diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 3bf20ff42e..309cde9a35 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -370,7 +370,7 @@ static void show_nexthop_detail_helper(struct vty *vty, break; } - if (re->vrf_id != nexthop->vrf_id) { + if (re->vrf_id != nexthop->vrf_id && nexthop->type != NEXTHOP_TYPE_BLACKHOLE) { struct vrf *vrf = vrf_lookup_by_id(nexthop->vrf_id); vty_out(vty, "(vrf %s)", VRF_LOGNAME(vrf)); @@ -3802,6 +3802,10 @@ static int config_write_protocol(struct vty *vty) if (!zebra_nhg_recursive_use_backups()) vty_out(vty, "no zebra nexthop resolve-via-backup\n"); +#ifdef HAVE_SCRIPTING + frrscript_names_config_write(vty); +#endif + if (rnh_get_hide_backups()) vty_out(vty, "ip nht hide-backup-events\n"); |
