diff --git a/bfdd/bfdd_cli.c b/bfdd/bfdd_cli.c index 2e213a2237ea..6527ec5f41a3 100644 --- a/bfdd/bfdd_cli.c +++ b/bfdd/bfdd_cli.c @@ -338,7 +338,7 @@ void bfd_cli_show_minimum_ttl(struct vty *vty, const struct lyd_node *dnode, DEFPY_YANG( bfd_peer_mult, bfd_peer_mult_cmd, - "[no] detect-multiplier ![(2-255)$multiplier]", + "[no] detect-multiplier ![(1-255)$multiplier]", NO_STR "Configure peer detection multiplier\n" "Configure peer detection multiplier value\n") @@ -357,7 +357,7 @@ void bfd_cli_show_mult(struct vty *vty, const struct lyd_node *dnode, DEFPY_YANG( bfd_peer_rx, bfd_peer_rx_cmd, - "[no] receive-interval ![(10-60000)$interval]", + "[no] receive-interval ![(10-4294967)$interval]", NO_STR "Configure peer receive interval\n" "Configure peer receive interval value in milliseconds\n") @@ -381,7 +381,7 @@ void bfd_cli_show_rx(struct vty *vty, const struct lyd_node *dnode, DEFPY_YANG( bfd_peer_tx, bfd_peer_tx_cmd, - "[no] transmit-interval ![(10-60000)$interval]", + "[no] transmit-interval ![(10-4294967)$interval]", NO_STR "Configure peer transmit interval\n" "Configure peer transmit interval value in milliseconds\n") @@ -439,7 +439,7 @@ void bfd_cli_show_echo(struct vty *vty, const struct lyd_node *dnode, DEFPY_YANG( bfd_peer_echo_interval, bfd_peer_echo_interval_cmd, - "[no] echo-interval ![(10-60000)$interval]", + "[no] echo-interval ![(10-4294967)$interval]", NO_STR "Configure peer echo intervals\n" "Configure peer echo rx/tx intervals value in milliseconds\n") @@ -462,7 +462,7 @@ DEFPY_YANG( DEFPY_YANG( bfd_peer_echo_transmit_interval, bfd_peer_echo_transmit_interval_cmd, - "[no] echo transmit-interval ![(10-60000)$interval]", + "[no] echo transmit-interval ![(10-4294967)$interval]", NO_STR "Configure peer echo intervals\n" "Configure desired transmit interval\n" @@ -492,7 +492,7 @@ void bfd_cli_show_desired_echo_transmission_interval( DEFPY_YANG( bfd_peer_echo_receive_interval, bfd_peer_echo_receive_interval_cmd, - "[no] echo receive-interval ![]", + "[no] echo receive-interval ![]", NO_STR "Configure peer echo intervals\n" "Configure required receive interval\n" @@ -577,19 +577,19 @@ void bfd_cli_show_profile(struct vty *vty, const struct lyd_node *dnode, } ALIAS_YANG(bfd_peer_mult, bfd_profile_mult_cmd, - "[no] detect-multiplier ![(2-255)$multiplier]", + "[no] detect-multiplier ![(1-255)$multiplier]", NO_STR "Configure peer detection multiplier\n" "Configure peer detection multiplier value\n") ALIAS_YANG(bfd_peer_tx, bfd_profile_tx_cmd, - "[no] transmit-interval ![(10-60000)$interval]", + "[no] transmit-interval ![(10-4294967)$interval]", NO_STR "Configure peer transmit interval\n" "Configure peer transmit interval value in milliseconds\n") ALIAS_YANG(bfd_peer_rx, bfd_profile_rx_cmd, - "[no] receive-interval ![(10-60000)$interval]", + "[no] receive-interval ![(10-4294967)$interval]", NO_STR "Configure peer receive interval\n" "Configure peer receive interval value in milliseconds\n") @@ -621,14 +621,14 @@ ALIAS_YANG(bfd_peer_echo, bfd_profile_echo_cmd, "Configure echo mode\n") ALIAS_YANG(bfd_peer_echo_interval, bfd_profile_echo_interval_cmd, - "[no] echo-interval ![(10-60000)$interval]", + "[no] echo-interval ![(10-4294967)$interval]", NO_STR "Configure peer echo interval\n" "Configure peer echo interval value in milliseconds\n") ALIAS_YANG( bfd_peer_echo_transmit_interval, bfd_profile_echo_transmit_interval_cmd, - "[no] echo transmit-interval ![(10-60000)$interval]", + "[no] echo transmit-interval ![(10-4294967)$interval]", NO_STR "Configure peer echo intervals\n" "Configure desired transmit interval\n" @@ -636,7 +636,7 @@ ALIAS_YANG( ALIAS_YANG( bfd_peer_echo_receive_interval, bfd_profile_echo_receive_interval_cmd, - "[no] echo receive-interval ![]", + "[no] echo receive-interval ![]", NO_STR "Configure peer echo intervals\n" "Configure required receive interval\n" diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c index 4c1615a5c625..a86b42e25015 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; } @@ -399,6 +401,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 f7e57fd66dda..46202fd34afc 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 7cdf98cba706..b62319b211e4 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -480,22 +480,11 @@ static bool bgp_attr_aigp_get_tlv_metric(uint8_t *pnt, int length, return false; } -static uint64_t bgp_aigp_metric_total(struct bgp_path_info *bpi) -{ - uint64_t aigp = bgp_attr_get_aigp_metric(bpi->attr); - - if (bpi->nexthop) - return aigp + bpi->nexthop->metric; - else - return aigp; -} - -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) @@ -1187,8 +1176,7 @@ struct attr *bgp_attr_aggregate_intern( SET_FLAG(attr.flag, ATTR_FLAG_BIT(BGP_ATTR_ORIGIN)); /* MED */ - attr.med = 0; - SET_FLAG(attr.flag, ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)); + bgp_attr_set_med(&attr, 0); /* AS path attribute. */ if (aspath) @@ -1943,9 +1931,7 @@ static enum bgp_attr_parse_ret bgp_attr_med(struct bgp_attr_parser_args *args) args->total); } - attr->med = stream_getl(peer->curr); - - SET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)); + bgp_attr_set_med(attr, stream_getl(peer->curr)); return BGP_ATTR_PARSE_PROCEED; } @@ -4496,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; @@ -4865,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, @@ -4886,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); } } @@ -5045,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. @@ -5058,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. */ @@ -5327,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 a8ca8c1fa638..c5532f400597 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; @@ -593,9 +597,23 @@ static inline uint64_t bgp_attr_get_aigp_metric(const struct attr *attr) static inline void bgp_attr_set_aigp_metric(struct attr *attr, uint64_t aigp) { attr->aigp_metric = aigp; + SET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_AIGP)); +} + +static inline uint64_t bgp_aigp_metric_total(struct bgp_path_info *bpi) +{ + uint64_t aigp = bgp_attr_get_aigp_metric(bpi->attr); - if (aigp) - SET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_AIGP)); + if (bpi->nexthop) + return aigp + bpi->nexthop->metric; + else + return aigp; +} + +static inline void bgp_attr_set_med(struct attr *attr, uint32_t med) +{ + attr->med = med; + SET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)); } static inline struct cluster_list *bgp_attr_get_cluster(const struct attr *attr) diff --git a/bgpd/bgp_bmp.c b/bgpd/bgp_bmp.c index 9d99c2c7fda8..4d9e580a234b 100644 --- a/bgpd/bgp_bmp.c +++ b/bgpd/bgp_bmp.c @@ -39,6 +39,8 @@ #include "bgpd/bgp_trace.h" #include "bgpd/bgp_network.h" #include "bgpd/bgp_label.h" +#include "bgpd/bgp_open.h" +#include "bgpd/bgp_aspath.h" static void bmp_close(struct bmp *bmp); static struct bmp_bgp *bmp_bgp_find(struct bgp *bgp); @@ -246,6 +248,33 @@ static void bmp_free(struct bmp *bmp) #define BMP_PEER_TYPE_LOCAL_INSTANCE 2 #define BMP_PEER_TYPE_LOC_RIB_INSTANCE 3 +/* determine the peer type for per-peer headers from a vrf_id + * for non loc-rib peer type only + */ +static inline int bmp_get_peer_type_vrf(vrf_id_t vrf_id) +{ + switch (vrf_id) { + case VRF_DEFAULT: + return BMP_PEER_TYPE_GLOBAL_INSTANCE; + case VRF_UNKNOWN: + /* when the VRF is unknown consider it a local instance */ + return BMP_PEER_TYPE_LOCAL_INSTANCE; + default: + return BMP_PEER_TYPE_RD_INSTANCE; + } +} + +/* determine the peer type for per-peer headers from a struct peer + * provide a bgp->peer_self for loc-rib + */ +static inline int bmp_get_peer_type(struct peer *peer) +{ + if (peer->bgp->peer_self == peer) + return BMP_PEER_TYPE_LOC_RIB_INSTANCE; + + return bmp_get_peer_type_vrf(peer->bgp->vrf_id); +} + static inline int bmp_get_peer_distinguisher(struct bmp *bmp, afi_t afi, uint8_t peer_type, uint64_t *result_ref) @@ -370,17 +399,18 @@ static void bmp_put_info_tlv(struct stream *s, uint16_t type, stream_put(s, string, len); } -static void __attribute__((unused)) -bmp_put_vrftablename_info_tlv(struct stream *s, struct bmp *bmp) +/* put the vrf table name of the bgp instance bmp is bound to in a tlv on the + * stream + */ +static void bmp_put_vrftablename_info_tlv(struct stream *s, struct bgp *bgp) { + const char *vrftablename = "global"; #define BMP_INFO_TYPE_VRFTABLENAME 3 - const char *vrftablename = "global"; - if (bmp->targets->bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT) { - struct vrf *vrf = vrf_lookup_by_id(bmp->targets->bgp->vrf_id); - vrftablename = vrf ? vrf->name : NULL; - } + if (bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT) + vrftablename = bgp->name; + if (vrftablename != NULL) bmp_put_info_tlv(s, BMP_INFO_TYPE_VRFTABLENAME, vrftablename); } @@ -428,6 +458,10 @@ static void bmp_notify_put(struct stream *s, struct bgp_notify *nfy) + sizeof(marker)); } +/* send peer up/down for peer based on down boolean value + * returns the message to send or NULL if the peer_distinguisher is not + * available + */ static struct stream *bmp_peerstate(struct peer *peer, bool down) { struct stream *s; @@ -438,11 +472,14 @@ static struct stream *bmp_peerstate(struct peer *peer, bool down) uptime.tv_usec = 0; monotime_to_realtime(&uptime, &uptime_real); + uint8_t peer_type = bmp_get_peer_type(peer); + bool is_locrib = peer_type == BMP_PEER_TYPE_LOC_RIB_INSTANCE; + #define BGP_BMP_MAX_PACKET_SIZE 1024 #define BMP_PEERUP_INFO_TYPE_STRING 0 s = stream_new(BGP_MAX_PACKET_SIZE); - if (peer_established(peer->connection) && !down) { + if ((peer_established(peer->connection) || is_locrib) && !down) { struct bmp_bgp_peer *bbpeer; bmp_common_hdr(s, BMP_VERSION_3, @@ -452,7 +489,9 @@ static struct stream *bmp_peerstate(struct peer *peer, bool down) &uptime_real); /* Local Address (16 bytes) */ - if (peer->su_local->sa.sa_family == AF_INET6) + if (is_locrib) + stream_put(s, 0, 16); + else if (peer->su_local->sa.sa_family == AF_INET6) stream_put(s, &peer->su_local->sin6.sin6_addr, 16); else if (peer->su_local->sa.sa_family == AF_INET) { stream_putl(s, 0); @@ -462,15 +501,21 @@ static struct stream *bmp_peerstate(struct peer *peer, bool down) } /* Local Port, Remote Port */ - if (peer->su_local->sa.sa_family == AF_INET6) + if (!peer->su_local || is_locrib) + stream_putw(s, 0); + else if (peer->su_local->sa.sa_family == AF_INET6) stream_putw(s, htons(peer->su_local->sin6.sin6_port)); else if (peer->su_local->sa.sa_family == AF_INET) stream_putw(s, htons(peer->su_local->sin.sin_port)); - if (peer->su_remote->sa.sa_family == AF_INET6) + + if (!peer->su_remote || is_locrib) + stream_putw(s, 0); + else if (peer->su_remote->sa.sa_family == AF_INET6) stream_putw(s, htons(peer->su_remote->sin6.sin6_port)); else if (peer->su_remote->sa.sa_family == AF_INET) stream_putw(s, htons(peer->su_remote->sin.sin_port)); + /* TODO craft message with fields & capabilities for loc-rib */ static const uint8_t dummy_open[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, @@ -510,32 +555,40 @@ static struct stream *bmp_peerstate(struct peer *peer, bool down) type_pos = stream_get_endp(s); stream_putc(s, 0); /* placeholder for down reason */ - switch (peer->last_reset) { - case PEER_DOWN_NOTIFY_RECEIVED: - type = BMP_PEERDOWN_REMOTE_NOTIFY; - bmp_notify_put(s, &peer->notify); - break; - case PEER_DOWN_CLOSE_SESSION: - type = BMP_PEERDOWN_REMOTE_CLOSE; - break; - case PEER_DOWN_WAITING_NHT: - type = BMP_PEERDOWN_LOCAL_FSM; - stream_putw(s, BGP_FSM_TcpConnectionFails); - break; - /* - * TODO: Map remaining PEER_DOWN_* reasons to RFC event codes. - * TODO: Implement BMP_PEERDOWN_LOCAL_NOTIFY. - * - * See RFC7854 ss. 4.9 - */ - default: - type = BMP_PEERDOWN_LOCAL_FSM; - stream_putw(s, BMP_PEER_DOWN_NO_RELEVANT_EVENT_CODE); - break; + if (is_locrib) { + type = BMP_PEERDOWN_LOCAL_TLV; + } else { + switch (peer->last_reset) { + case PEER_DOWN_NOTIFY_RECEIVED: + type = BMP_PEERDOWN_REMOTE_NOTIFY; + bmp_notify_put(s, &peer->notify); + break; + case PEER_DOWN_CLOSE_SESSION: + type = BMP_PEERDOWN_REMOTE_CLOSE; + break; + case PEER_DOWN_WAITING_NHT: + type = BMP_PEERDOWN_LOCAL_FSM; + stream_putw(s, BGP_FSM_TcpConnectionFails); + break; + /* + * TODO: Map remaining PEER_DOWN_* reasons to RFC event + * codes. + * TODO: Implement BMP_PEERDOWN_LOCAL_NOTIFY. + * + * See RFC7854 ss. 4.9 + */ + default: + type = BMP_PEERDOWN_LOCAL_FSM; + stream_putw(s, BMP_PEER_DOWN_NO_RELEVANT_EVENT_CODE); + break; + } } stream_putc_at(s, type_pos, type); } + if (is_locrib) + bmp_put_vrftablename_info_tlv(s, peer->bgp); + len = stream_get_endp(s); stream_putl_at(s, BMP_LENGTH_POS, len); /* message length is set. */ return s; @@ -558,6 +611,25 @@ static int bmp_send_peerup(struct bmp *bmp) return 0; } +static int bmp_send_peerup_vrf(struct bmp *bmp) +{ + struct bmp_bgp *bmpbgp = bmp->targets->bmpbgp; + struct stream *s; + + /* send unconditionally because state may has been set before the + * session was up. and in this case the peer up has not been sent. + */ + bmp_bgp_update_vrf_status(bmpbgp, vrf_state_unknown); + + s = bmp_peerstate(bmpbgp->bgp->peer_self, bmpbgp->vrf_state == vrf_state_down); + + pullwr_write_stream(bmp->pullwr, s); + stream_free(s); + + return 0; +} + +/* send a stream to all bmp sessions configured in a bgp instance */ /* XXX: kludge - filling the pullwr's buffer */ static void bmp_send_all(struct bmp_bgp *bmpbgp, struct stream *s) { @@ -570,6 +642,16 @@ static void bmp_send_all(struct bmp_bgp *bmpbgp, struct stream *s) stream_free(s); } +static void bmp_send_all_safe(struct bmp_bgp *bmpbgp, struct stream *s) +{ + if (!bmpbgp) { + stream_free(s); + return; + } + + bmp_send_all(bmpbgp, s); +} + /* * Route Mirroring */ @@ -816,7 +898,7 @@ static int bmp_peer_status_changed(struct peer *peer) } } - bmp_send_all(bmpbgp, bmp_peerstate(peer, false)); + bmp_send_all_safe(bmpbgp, bmp_peerstate(peer, false)); return 0; } @@ -838,7 +920,7 @@ static int bmp_peer_backward(struct peer *peer) bbpeer->open_rx_len = 0; } - bmp_send_all(bmpbgp, bmp_peerstate(peer, true)); + bmp_send_all_safe(bmpbgp, bmp_peerstate(peer, true)); return 0; } @@ -934,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? */ @@ -1476,6 +1557,7 @@ static void bmp_wrfill(struct bmp *bmp, struct pullwr *pullwr) { switch(bmp->state) { case BMP_PeerUp: + bmp_send_peerup_vrf(bmp); bmp_send_peerup(bmp); bmp->state = BMP_Run; break; @@ -1839,6 +1921,7 @@ static struct bmp_bgp *bmp_bgp_get(struct bgp *bgp) bmpbgp = XCALLOC(MTYPE_BMP, sizeof(*bmpbgp)); bmpbgp->bgp = bgp; + bmpbgp->vrf_state = vrf_state_unknown; bmpbgp->mirror_qsizelimit = ~0UL; bmp_mirrorq_init(&bmpbgp->mirrorq); bmp_bgph_add(&bmp_bgph, bmpbgp); @@ -1873,6 +1956,81 @@ static int bmp_bgp_del(struct bgp *bgp) return 0; } +static void bmp_bgp_peer_vrf(struct bmp_bgp_peer *bbpeer, struct bgp *bgp) +{ + struct peer *peer = bgp->peer_self; + uint16_t send_holdtime; + as_t local_as; + + if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER)) + send_holdtime = peer->holdtime; + else + send_holdtime = peer->bgp->default_holdtime; + + /* local-as Change */ + if (peer->change_local_as) + local_as = peer->change_local_as; + else + local_as = peer->local_as; + + struct stream *s = bgp_open_make(peer, send_holdtime, local_as); + size_t open_len = stream_get_endp(s); + + bbpeer->open_rx_len = open_len; + bbpeer->open_rx = XMALLOC(MTYPE_BMP_OPEN, open_len); + memcpy(bbpeer->open_rx, s->data, open_len); + + 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 + * + * if force is unknown, use zebra vrf state + * + * returns true if state has changed + */ +bool bmp_bgp_update_vrf_status(struct bmp_bgp *bmpbgp, enum bmp_vrf_state force) +{ + enum bmp_vrf_state old_state; + struct bmp_bgp_peer *bbpeer; + struct peer *peer; + struct vrf *vrf; + struct bgp *bgp; + bool changed; + + if (!bmpbgp || !bmpbgp->bgp) + return false; + + bgp = bmpbgp->bgp; + old_state = bmpbgp->vrf_state; + + vrf = bgp_vrf_lookup_by_instance_type(bgp); + bmpbgp->vrf_state = force != vrf_state_unknown ? force + : vrf_is_enabled(vrf) ? vrf_state_up + : vrf_state_down; + + changed = old_state != bmpbgp->vrf_state; + if (changed) { + peer = bmpbgp->bgp->peer_self; + if (bmpbgp->vrf_state == vrf_state_up) { + bbpeer = bmp_bgp_peer_get(peer); + bmp_bgp_peer_vrf(bbpeer, bgp); + } else { + bbpeer = bmp_bgp_peer_find(peer->qobj_node.nid); + if (bbpeer) { + XFREE(MTYPE_BMP_OPEN, bbpeer->open_rx); + bmp_peerh_del(&bmp_peerh, bbpeer); + XFREE(MTYPE_BMP_PEER, bbpeer); + } + } + } + + return changed; +} + static struct bmp_bgp_peer *bmp_bgp_peer_find(uint64_t peerid) { struct bmp_bgp_peer dummy = { .peerid = peerid }; @@ -2953,6 +3111,44 @@ static int bgp_bmp_early_fini(void) return 0; } +/* called when a bgp instance goes up/down, implying that the underlying VRF + * has been created or deleted in zebra + */ +static int bmp_vrf_state_changed(struct bgp *bgp) +{ + struct bmp_bgp *bmpbgp = bmp_bgp_find(bgp); + + if (!bmp_bgp_update_vrf_status(bmpbgp, vrf_state_unknown)) + return 1; + + bmp_send_all_safe(bmpbgp, + bmp_peerstate(bgp->peer_self, bmpbgp->vrf_state == vrf_state_down)); + + return 0; +} + +/* called when an interface goes up/down in a vrf, this may signal that the + * VRF changed state and is how bgp_snmp detects vrf state changes + */ +static int bmp_vrf_itf_state_changed(struct bgp *bgp, struct interface *itf) +{ + struct bmp_bgp *bmpbgp; + enum bmp_vrf_state new_state; + + /* if the update is not about the vrf device double-check + * the zebra status of the vrf + */ + if (!itf || !if_is_vrf(itf)) + return bmp_vrf_state_changed(bgp); + + bmpbgp = bmp_bgp_find(bgp); + new_state = if_is_up(itf) ? vrf_state_up : vrf_state_down; + if (bmp_bgp_update_vrf_status(bmpbgp, new_state)) + bmp_send_all(bmpbgp, bmp_peerstate(bgp->peer_self, new_state == vrf_state_down)); + + return 0; +} + static int bgp_bmp_module_init(void) { hook_register(bgp_packet_dump, bmp_mirror_packet); @@ -2965,6 +3161,8 @@ static int bgp_bmp_module_init(void) hook_register(frr_late_init, bgp_bmp_init); hook_register(bgp_route_update, bmp_route_update); hook_register(frr_early_fini, bgp_bmp_early_fini); + hook_register(bgp_instance_state, bmp_vrf_state_changed); + hook_register(bgp_vrf_status_changed, bmp_vrf_itf_state_changed); return 0; } diff --git a/bgpd/bgp_bmp.h b/bgpd/bgp_bmp.h index 33247c402504..d45a4278f69e 100644 --- a/bgpd/bgp_bmp.h +++ b/bgpd/bgp_bmp.h @@ -268,10 +268,19 @@ PREDECL_HASH(bmp_bgph); #define BMP_PEER_DOWN_NO_RELEVANT_EVENT_CODE 0x00 +enum bmp_vrf_state { + vrf_state_down = -1, + vrf_state_unknown = 0, + vrf_state_up = 1, +}; + struct bmp_bgp { struct bmp_bgph_item bbi; struct bgp *bgp; + + enum bmp_vrf_state vrf_state; + struct bmp_targets_head targets; struct bmp_mirrorq_head mirrorq; @@ -280,12 +289,17 @@ struct bmp_bgp { size_t mirror_qsizelimit; }; +extern bool bmp_bgp_update_vrf_status(struct bmp_bgp *bmpbgp, enum bmp_vrf_state force); + enum { - BMP_PEERDOWN_LOCAL_NOTIFY = 1, - BMP_PEERDOWN_LOCAL_FSM = 2, - BMP_PEERDOWN_REMOTE_NOTIFY = 3, - BMP_PEERDOWN_REMOTE_CLOSE = 4, - BMP_PEERDOWN_ENDMONITOR = 5, + /* RFC7854 - 10.8 */ + BMP_PEERDOWN_LOCAL_NOTIFY = 1, + BMP_PEERDOWN_LOCAL_FSM = 2, + BMP_PEERDOWN_REMOTE_NOTIFY = 3, + BMP_PEERDOWN_REMOTE_CLOSE = 4, + BMP_PEERDOWN_ENDMONITOR = 5, + /* RFC9069 - 8.4 */ + BMP_PEERDOWN_LOCAL_TLV = 6, }; enum { diff --git a/bgpd/bgp_conditional_adv.c b/bgpd/bgp_conditional_adv.c index 2d96b444b6c5..a99d0201e7e0 100644 --- a/bgpd/bgp_conditional_adv.c +++ b/bgpd/bgp_conditional_adv.c @@ -30,8 +30,8 @@ bgp_check_rmap_prefixes_in_bgp_table(struct bgp_table *table, dummy_attr = *pi->attr; /* Fill temp path_info */ - prep_for_rmap_apply(&path, &path_extra, dest, pi, - pi->peer, &dummy_attr); + prep_for_rmap_apply(&path, &path_extra, dest, pi, pi->peer, NULL, + &dummy_attr); RESET_FLAG(dummy_attr.rmap_change_flags); @@ -99,8 +99,8 @@ static void bgp_conditional_adv_routes(struct peer *peer, afi_t afi, advmap_attr = *pi->attr; /* Fill temp path_info */ - prep_for_rmap_apply(&path, &path_extra, dest, pi, - pi->peer, &advmap_attr); + prep_for_rmap_apply(&path, &path_extra, dest, pi, pi->peer, NULL, + &advmap_attr); RESET_FLAG(advmap_attr.rmap_change_flags); diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index 97c3e5740f6e..097d3684f67c 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 547dcdf7f32a..df32d7510316 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 67c16aeb9e82..0544cbd316e0 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_evpn.c b/bgpd/bgp_evpn.c index fb7d2f47fb79..ad29828adecb 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -3477,9 +3477,8 @@ uninstall_evpn_route_entry_in_vni_mac(struct bgp *bgp, struct bgpevpn *vpn, * Uninstall route entry from the VRF routing table and send message * to zebra, if appropriate. */ -static int uninstall_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, - const struct prefix_evpn *evp, - struct bgp_path_info *parent_pi) +int uninstall_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, const struct prefix_evpn *evp, + struct bgp_path_info *parent_pi) { struct bgp_dest *dest; struct bgp_path_info *pi; @@ -3846,10 +3845,8 @@ static int bgp_evpn_route_rmac_self_check(struct bgp *bgp_vrf, } /* don't import hosts that are locally attached */ -static inline bool -bgp_evpn_skip_vrf_import_of_local_es(struct bgp *bgp_vrf, - const struct prefix_evpn *evp, - struct bgp_path_info *pi, int install) +bool bgp_evpn_skip_vrf_import_of_local_es(struct bgp *bgp_vrf, const struct prefix_evpn *evp, + struct bgp_path_info *pi, int install) { esi_t *esi; @@ -5395,9 +5392,8 @@ void bgp_evpn_advertise_type5_routes(struct bgp *bgp_vrf, afi_t afi, tmp_attr = *pi->attr; /* Fill temp path_info */ - prep_for_rmap_apply(&tmp_pi, &tmp_pie, - dest, pi, pi->peer, - &tmp_attr); + prep_for_rmap_apply(&tmp_pi, &tmp_pie, dest, pi, pi->peer, + NULL, &tmp_attr); RESET_FLAG(tmp_attr.rmap_change_flags); diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h index dc82bcfba9e9..10eff1dcfb2f 100644 --- a/bgpd/bgp_evpn.h +++ b/bgpd/bgp_evpn.h @@ -195,4 +195,8 @@ extern enum zclient_send_status evpn_zebra_uninstall(struct bgp *bgp, struct bgpevpn *vpn, const struct prefix_evpn *p, struct bgp_path_info *pi, bool is_sync); +bool bgp_evpn_skip_vrf_import_of_local_es(struct bgp *bgp_vrf, const struct prefix_evpn *evp, + struct bgp_path_info *pi, int install); +int uninstall_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, const struct prefix_evpn *evp, + struct bgp_path_info *parent_pi); #endif /* _QUAGGA_BGP_EVPN_H */ diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c index 1fb65945572a..ad3625242ee1 100644 --- a/bgpd/bgp_evpn_mh.c +++ b/bgpd/bgp_evpn_mh.c @@ -3807,6 +3807,7 @@ int bgp_evpn_local_es_evi_add(struct bgp *bgp, esi_t *esi, vni_t vni) bgp_evpn_ead_evi_route_update(bgp, es, vpn, &p); } + bgp_evpn_local_es_evi_unistall_local_routes_in_vrfs(es, es_evi); /* update EAD-ES */ if (bgp_evpn_local_es_is_active(es)) bgp_evpn_ead_es_route_update(bgp, es); @@ -5058,3 +5059,38 @@ void bgp_evpn_switch_ead_evi_rx(void) } } } + +void bgp_evpn_local_es_evi_unistall_local_routes_in_vrfs(struct bgp_evpn_es *es, + struct bgp_evpn_es_evi *es_evi) +{ + struct listnode *node; + struct bgp_path_es_info *es_info; + struct bgp_path_info *pi; + const struct prefix_evpn *evp; + struct bgp_evpn_es_vrf *es_vrf = es_evi->es_vrf; + + if (!es_vrf) + return; + + for (ALL_LIST_ELEMENTS_RO(es->macip_global_path_list, node, es_info)) { + pi = es_info->pi; + + if (!bgp_evpn_is_macip_path(pi)) + continue; + + evp = (const struct prefix_evpn *)bgp_dest_get_prefix(pi->net); + + if (!(CHECK_FLAG(pi->flags, BGP_PATH_VALID) && pi->type == ZEBRA_ROUTE_BGP && + pi->sub_type == BGP_ROUTE_NORMAL)) + continue; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) + zlog_debug("route %pFX is matched on local esi %s, uninstall from %s route table", + evp, es->esi_str, es_vrf->bgp_vrf->name_pretty); + + if (!bgp_evpn_skip_vrf_import_of_local_es(es_vrf->bgp_vrf, evp, pi, 0)) + continue; + + uninstall_evpn_route_entry_in_vrf(es_vrf->bgp_vrf, evp, pi); + } +} diff --git a/bgpd/bgp_evpn_mh.h b/bgpd/bgp_evpn_mh.h index 5d393c37a20d..149bf384b1d8 100644 --- a/bgpd/bgp_evpn_mh.h +++ b/bgpd/bgp_evpn_mh.h @@ -459,5 +459,7 @@ extern void bgp_evpn_path_nh_add(struct bgp *bgp_vrf, struct bgp_path_info *pi); extern void bgp_evpn_path_nh_del(struct bgp *bgp_vrf, struct bgp_path_info *pi); extern void bgp_evpn_mh_config_ead_export_rt(struct bgp *bgp, struct ecommunity *ecom, bool del); +extern void bgp_evpn_local_es_evi_unistall_local_routes_in_vrfs(struct bgp_evpn_es *es, + struct bgp_evpn_es_evi *es_evi); #endif /* _FRR_BGP_EVPN_MH_H */ diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index cdd9b7ae4d51..b3518ac69657 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); @@ -2776,99 +2779,72 @@ int bgp_gr_update_all(struct bgp *bgp, enum global_gr_command global_gr_cmd) const char *print_peer_gr_mode(enum peer_mode pr_mode) { - const char *peer_gr_mode = NULL; - switch (pr_mode) { case PEER_HELPER: - peer_gr_mode = "PEER_HELPER"; - break; + return "PEER_HELPER"; case PEER_GR: - peer_gr_mode = "PEER_GR"; - break; + return "PEER_GR"; case PEER_DISABLE: - peer_gr_mode = "PEER_DISABLE"; - break; + return "PEER_DISABLE"; case PEER_INVALID: - peer_gr_mode = "PEER_INVALID"; - break; + return "PEER_INVALID"; case PEER_GLOBAL_INHERIT: - peer_gr_mode = "PEER_GLOBAL_INHERIT"; - break; + return "PEER_GLOBAL_INHERIT"; } - return peer_gr_mode; + return NULL; } const char *print_peer_gr_cmd(enum peer_gr_command pr_gr_cmd) { - const char *peer_gr_cmd = NULL; - switch (pr_gr_cmd) { case PEER_GR_CMD: - peer_gr_cmd = "PEER_GR_CMD"; - break; + return "PEER_GR_CMD"; case NO_PEER_GR_CMD: - peer_gr_cmd = "NO_PEER_GR_CMD"; - break; + return "NO_PEER_GR_CMD"; case PEER_DISABLE_CMD: - peer_gr_cmd = "PEER_DISABLE_GR_CMD"; - break; + return "PEER_DISABLE_GR_CMD"; case NO_PEER_DISABLE_CMD: - peer_gr_cmd = "NO_PEER_DISABLE_GR_CMD"; - break; + return "NO_PEER_DISABLE_GR_CMD"; case PEER_HELPER_CMD: - peer_gr_cmd = "PEER_HELPER_CMD"; - break; + return "PEER_HELPER_CMD"; case NO_PEER_HELPER_CMD: - peer_gr_cmd = "NO_PEER_HELPER_CMD"; - break; + return "NO_PEER_HELPER_CMD"; } - return peer_gr_cmd; + return NULL; } const char *print_global_gr_mode(enum global_mode gl_mode) { - const char *global_gr_mode = "???"; - switch (gl_mode) { case GLOBAL_HELPER: - global_gr_mode = "GLOBAL_HELPER"; - break; + return "GLOBAL_HELPER"; case GLOBAL_GR: - global_gr_mode = "GLOBAL_GR"; - break; + return "GLOBAL_GR"; case GLOBAL_DISABLE: - global_gr_mode = "GLOBAL_DISABLE"; - break; + return "GLOBAL_DISABLE"; case GLOBAL_INVALID: - global_gr_mode = "GLOBAL_INVALID"; - break; + return "GLOBAL_INVALID"; } - return global_gr_mode; + return "???"; } const char *print_global_gr_cmd(enum global_gr_command gl_gr_cmd) { - const char *global_gr_cmd = NULL; - switch (gl_gr_cmd) { case GLOBAL_GR_CMD: - global_gr_cmd = "GLOBAL_GR_CMD"; - break; + return "GLOBAL_GR_CMD"; case NO_GLOBAL_GR_CMD: - global_gr_cmd = "NO_GLOBAL_GR_CMD"; - break; + return "NO_GLOBAL_GR_CMD"; case GLOBAL_DISABLE_CMD: - global_gr_cmd = "GLOBAL_DISABLE_CMD"; - break; + return "GLOBAL_DISABLE_CMD"; case NO_GLOBAL_DISABLE_CMD: - global_gr_cmd = "NO_GLOBAL_DISABLE_CMD"; - break; + return "NO_GLOBAL_DISABLE_CMD"; } - return global_gr_cmd; + return NULL; } enum global_mode bgp_global_gr_mode_get(struct bgp *bgp) @@ -3059,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 85c488962fc9..013c60ce23ae 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_io.c b/bgpd/bgp_io.c index b07e69ac31bb..9e9251c85459 100644 --- a/bgpd/bgp_io.c +++ b/bgpd/bgp_io.c @@ -512,7 +512,13 @@ static uint16_t bgp_read(struct peer_connection *connection, int *code_p) readsize = MIN(ibuf_work_space, sizeof(ibuf_scratch)); +#ifdef __clang_analyzer__ + /* clang-SA doesn't want you to call read() while holding a mutex */ + (void)readsize; + nbytes = 0; +#else nbytes = read(connection->fd, ibuf_scratch, readsize); +#endif /* EAGAIN or EWOULDBLOCK; come back later */ if (nbytes < 0 && ERRNO_IO_RETRY(errno)) { diff --git a/bgpd/bgp_label.c b/bgpd/bgp_label.c index af10111600cc..5db362173878 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 d67a99b68f6f..3642df501901 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -1623,8 +1623,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_nexthop.c b/bgpd/bgp_nexthop.c index 98eb9565bf20..401549c4e859 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -528,14 +528,12 @@ bool bgp_nexthop_self(struct bgp *bgp, afi_t afi, uint8_t type, tmp_addr.p.prefixlen = p->prefixlen; } else { /* Here we need to find out which nexthop to be used*/ - if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP)) { + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP))) { tmp_addr.p.u.prefix4 = attr->nexthop; tmp_addr.p.prefixlen = IPV4_MAX_BITLEN; - } else if ((attr->mp_nexthop_len) - && ((attr->mp_nexthop_len - == BGP_ATTR_NHLEN_IPV4) - || (attr->mp_nexthop_len - == BGP_ATTR_NHLEN_VPNV4))) { + } else if ((attr->mp_nexthop_len) && + ((attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV4) || + (attr->mp_nexthop_len == BGP_ATTR_NHLEN_VPNV4))) { tmp_addr.p.u.prefix4 = attr->mp_nexthop_global_in; tmp_addr.p.prefixlen = IPV4_MAX_BITLEN; @@ -564,11 +562,11 @@ bool bgp_nexthop_self(struct bgp *bgp, afi_t afi, uint8_t type, memset(&tmp_tip, 0, sizeof(tmp_tip)); tmp_tip.addr = attr->nexthop; - if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP)) { + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP))) { tmp_tip.addr = attr->nexthop; } else if ((attr->mp_nexthop_len) && - ((attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV4) - || (attr->mp_nexthop_len == BGP_ATTR_NHLEN_VPNV4))) { + ((attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV4) || + (attr->mp_nexthop_len == BGP_ATTR_NHLEN_VPNV4))) { tmp_tip.addr = attr->mp_nexthop_global_in; } @@ -1379,16 +1377,9 @@ char *bgp_nexthop_dump_bnc_change_flags(struct bgp_nexthop_cache *bnc, return buf; } - snprintfrr(buf, len, "%s%s%s", - CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_CHANGED) - ? "Changed " - : "", - CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_METRIC_CHANGED) - ? "Metric " - : "", - CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_CONNECTED_CHANGED) - ? "Connected " - : ""); + snprintfrr(buf, len, "%s%s", + CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_CHANGED) ? "Changed " : "", + CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_METRIC_CHANGED) ? "Metric " : ""); return buf; } diff --git a/bgpd/bgp_nexthop.h b/bgpd/bgp_nexthop.h index 5014eb8f3455..6a4a02dcc824 100644 --- a/bgpd/bgp_nexthop.h +++ b/bgpd/bgp_nexthop.h @@ -45,11 +45,10 @@ struct bgp_nexthop_cache { */ bool is_evpn_gwip_nexthop; - uint16_t change_flags; + uint8_t change_flags; #define BGP_NEXTHOP_CHANGED (1 << 0) #define BGP_NEXTHOP_METRIC_CHANGED (1 << 1) -#define BGP_NEXTHOP_CONNECTED_CHANGED (1 << 2) -#define BGP_NEXTHOP_MACIP_CHANGED (1 << 3) +#define BGP_NEXTHOP_MACIP_CHANGED (1 << 2) struct nexthop *nexthop; time_t last_update; diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 62be7ffbf733..0523a4b02b4e 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); } } @@ -641,31 +637,11 @@ void bgp_keepalive_send(struct peer *peer) bgp_writes_on(peer->connection); } -/* - * Creates a BGP Open packet and appends it to the peer's output queue. - * Sets capabilities as necessary. - */ -void bgp_open_send(struct peer_connection *connection) +struct stream *bgp_open_make(struct peer *peer, uint16_t send_holdtime, as_t local_as) { - struct stream *s; - uint16_t send_holdtime; - as_t local_as; - struct peer *peer = connection->peer; + struct stream *s = stream_new(BGP_STANDARD_MESSAGE_MAX_PACKET_SIZE); bool ext_opt_params = false; - if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER)) - send_holdtime = peer->holdtime; - else - send_holdtime = peer->bgp->default_holdtime; - - /* local-as Change */ - if (peer->change_local_as) - local_as = peer->change_local_as; - else - local_as = peer->local_as; - - s = stream_new(BGP_STANDARD_MESSAGE_MAX_PACKET_SIZE); - /* Make open packet. */ bgp_packet_set_marker(s, BGP_MSG_OPEN); @@ -704,6 +680,33 @@ void bgp_open_send(struct peer_connection *connection) ext_opt_params ? " (Extended)" : "", BGP_VERSION_4, local_as, send_holdtime, &peer->local_id); + return s; +} + +/* + * Creates a BGP Open packet and appends it to the peer's output queue. + * Sets capabilities as necessary. + */ +void bgp_open_send(struct peer_connection *connection) +{ + struct stream *s; + uint16_t send_holdtime; + as_t local_as; + struct peer *peer = connection->peer; + + if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER)) + send_holdtime = peer->holdtime; + else + send_holdtime = peer->bgp->default_holdtime; + + /* local-as Change */ + if (peer->change_local_as) + local_as = peer->change_local_as; + else + local_as = peer->local_as; + + s = bgp_open_make(peer, send_holdtime, local_as); + /* Dump packet if debug option is set. */ /* bgp_packet_dump (s); */ hook_call(bgp_packet_send, peer, BGP_MSG_OPEN, stream_get_endp(s), s); @@ -2935,35 +2938,31 @@ static int bgp_route_refresh_receive(struct peer_connection *connection, if (bgp_debug_neighbor_events(peer)) { char buf[INET6_BUFSIZ]; - zlog_debug( - "%pBP rcvd %s %s seq %u %s/%d ge %d le %d%s", - peer, - (common & ORF_COMMON_PART_REMOVE - ? "Remove" - : "Add"), - (common & ORF_COMMON_PART_DENY - ? "deny" - : "permit"), - orfp.seq, - inet_ntop( - orfp.p.family, - &orfp.p.u.prefix, - buf, - INET6_BUFSIZ), - orfp.p.prefixlen, - orfp.ge, orfp.le, - ok ? "" : " MALFORMED"); + zlog_debug("%pBP rcvd %s %s seq %u %s/%d ge %d le %d%s", + peer, + (CHECK_FLAG(common, ORF_COMMON_PART_REMOVE) + ? "Remove" + : "Add"), + (CHECK_FLAG(common, ORF_COMMON_PART_DENY) + ? "deny" + : "permit"), + orfp.seq, + inet_ntop(orfp.p.family, &orfp.p.u.prefix, + buf, INET6_BUFSIZ), + orfp.p.prefixlen, orfp.ge, orfp.le, + ok ? "" : " MALFORMED"); } if (ok) - ret = prefix_bgp_orf_set( - name, afi, &orfp, - (common & ORF_COMMON_PART_DENY - ? 0 - : 1), - (common & ORF_COMMON_PART_REMOVE - ? 0 - : 1)); + ret = prefix_bgp_orf_set(name, afi, &orfp, + (CHECK_FLAG(common, + ORF_COMMON_PART_DENY) + ? 0 + : 1), + (CHECK_FLAG(common, + ORF_COMMON_PART_REMOVE) + ? 0 + : 1)); if (!ok || (ok && ret != CMD_SUCCESS)) { zlog_info( @@ -3190,17 +3189,11 @@ static void bgp_dynamic_capability_addpath(uint8_t *pnt, int action, if (bgp_debug_neighbor_events(peer)) zlog_debug("%s OPEN has %s capability for afi/safi: %s/%s%s%s", - peer->host, - lookup_msg(capcode_str, hdr->code, - NULL), - iana_afi2str(pkt_afi), - iana_safi2str(pkt_safi), - (bac.flags & BGP_ADDPATH_RX) - ? ", receive" - : "", - (bac.flags & BGP_ADDPATH_TX) - ? ", transmit" - : ""); + peer->host, lookup_msg(capcode_str, hdr->code, NULL), + iana_afi2str(pkt_afi), iana_safi2str(pkt_safi), + CHECK_FLAG(bac.flags, BGP_ADDPATH_RX) ? ", receive" : "", + CHECK_FLAG(bac.flags, BGP_ADDPATH_TX) ? ", transmit" + : ""); if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) { diff --git a/bgpd/bgp_packet.h b/bgpd/bgp_packet.h index b67acf205593..c266b17266ec 100644 --- a/bgpd/bgp_packet.h +++ b/bgpd/bgp_packet.h @@ -43,6 +43,7 @@ DECLARE_HOOK(bgp_packet_send, /* Packet send and receive function prototypes. */ extern void bgp_keepalive_send(struct peer *peer); +extern struct stream *bgp_open_make(struct peer *peer, uint16_t send_holdtime, as_t local_as); extern void bgp_open_send(struct peer_connection *connection); extern void bgp_notify_send(struct peer_connection *connection, uint8_t code, uint8_t sub_code); diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 475b709a0766..d1b3919d72d6 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -648,7 +648,7 @@ static bool use_bgp_med_value(struct attr *attr, struct bgp *bgp) missing-as-worst" is specified, treat it as the worst value. */ static uint32_t bgp_med_value(struct attr *attr, struct bgp *bgp) { - if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)) + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC))) return attr->med; else { if (CHECK_FLAG(bgp->flags, BGP_FLAG_MED_MISSING_AS_WORST)) @@ -996,9 +996,9 @@ int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, /* 2. Local preference check. */ new_pref = exist_pref = bgp->default_local_pref; - if (newattr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) + if (CHECK_FLAG(newattr->flag, ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) new_pref = newattr->local_pref; - if (existattr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) + if (CHECK_FLAG(existattr->flag, ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) exist_pref = existattr->local_pref; if (new_pref > exist_pref) { @@ -1038,10 +1038,10 @@ int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, bool exist_accept_own = false; uint32_t accept_own = COMMUNITY_ACCEPT_OWN; - if (newattr->flag & ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES)) + if (CHECK_FLAG(newattr->flag, ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES))) new_accept_own = community_include( bgp_attr_get_community(newattr), accept_own); - if (existattr->flag & ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES)) + if (CHECK_FLAG(existattr->flag, ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES))) exist_accept_own = community_include( bgp_attr_get_community(existattr), accept_own); @@ -1064,12 +1064,37 @@ 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)) { - uint64_t new_aigp = bgp_attr_get_aigp_metric(newattr); - uint64_t exist_aigp = bgp_attr_get_aigp_metric(existattr); + uint64_t new_aigp = bgp_aigp_metric_total(new); + uint64_t exist_aigp = bgp_aigp_metric_total(exist); if (new_aigp < exist_aigp) { *reason = bgp_path_selection_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); @@ -1500,11 +1497,11 @@ int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, * be 0 and would always win over the other path. If originator id is * used for the comparison, it will decide which path is better. */ - if (newattr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)) + if (CHECK_FLAG(newattr->flag, ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID))) new_id.s_addr = newattr->originator_id.s_addr; else new_id.s_addr = peer_new->remote_id.s_addr; - if (existattr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)) + if (CHECK_FLAG(existattr->flag, ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID))) exist_id.s_addr = existattr->originator_id.s_addr; else exist_id.s_addr = peer_exist->remote_id.s_addr; @@ -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; @@ -1801,7 +1797,7 @@ static bool bgp_cluster_filter(struct peer *peer, struct attr *attr) struct cluster_list *cluster = bgp_attr_get_cluster(attr); if (cluster) { - if (peer->bgp->config & BGP_CONFIG_CLUSTER_ID) + if (CHECK_FLAG(peer->bgp->config, BGP_CONFIG_CLUSTER_ID)) cluster_id = peer->bgp->cluster_id; else cluster_id = peer->bgp->router_id; @@ -1814,7 +1810,7 @@ static bool bgp_cluster_filter(struct peer *peer, struct attr *attr) static bool bgp_otc_filter(struct peer *peer, struct attr *attr) { - if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_OTC)) { + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_OTC))) { if (peer->local_role == ROLE_PROVIDER || peer->local_role == ROLE_RS_SERVER) return true; @@ -1825,7 +1821,7 @@ static bool bgp_otc_filter(struct peer *peer, struct attr *attr) if (peer->local_role == ROLE_CUSTOMER || peer->local_role == ROLE_PEER || peer->local_role == ROLE_RS_CLIENT) { - attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_OTC); + SET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_OTC)); attr->otc = peer->as; } return false; @@ -1833,7 +1829,7 @@ static bool bgp_otc_filter(struct peer *peer, struct attr *attr) static bool bgp_otc_egress(struct peer *peer, struct attr *attr) { - if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_OTC)) { + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_OTC))) { if (peer->local_role == ROLE_CUSTOMER || peer->local_role == ROLE_RS_CLIENT || peer->local_role == ROLE_PEER) @@ -1843,7 +1839,7 @@ static bool bgp_otc_egress(struct peer *peer, struct attr *attr) if (peer->local_role == ROLE_PROVIDER || peer->local_role == ROLE_PEER || peer->local_role == ROLE_RS_SERVER) { - attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_OTC); + SET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_OTC)); attr->otc = peer->bgp->as; } return false; @@ -2100,7 +2096,7 @@ void bgp_attr_add_gshut_community(struct attr *attr) /* When we add the graceful-shutdown community we must also * lower the local-preference */ - attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF); + SET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)); attr->local_pref = BGP_GSHUT_LOCAL_PREF; } @@ -2148,13 +2144,14 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi, struct attr *piattr; route_map_result_t ret; int transparent; - int reflect; + int ibgp_to_ibgp; afi_t afi; safi_t safi; int samepeer_safe = 0; /* for synthetic mplsvpns routes */ bool nh_reset = false; uint64_t cum_bw; mpls_label_t label; + bool global_and_ll = false; if (DISABLE_BGP_ANNOUNCE) return false; @@ -2303,8 +2300,8 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi, /* If the attribute has originator-id and it is same as remote peer's id. */ - if (onlypeer && piattr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID) - && (IPV4_ADDR_SAME(&onlypeer->remote_id, &piattr->originator_id))) { + if (onlypeer && (CHECK_FLAG(piattr->flag, ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID))) && + (IPV4_ADDR_SAME(&onlypeer->remote_id, &piattr->originator_id))) { if (bgp_debug_update(NULL, p, subgrp->update_group, 0)) zlog_debug( "%pBP [Update:SEND] %pFX originator-id is same as remote router-id", @@ -2356,14 +2353,14 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi, } } - /* Route-Reflect check. */ + /* iBGP to iBGP check. */ if (from->sort == BGP_PEER_IBGP && peer->sort == BGP_PEER_IBGP) - reflect = 1; + ibgp_to_ibgp = 1; else - reflect = 0; + ibgp_to_ibgp = 0; /* IBGP reflection check. */ - if (reflect && !samepeer_safe) { + if (ibgp_to_ibgp && !samepeer_safe) { /* A route from a Client peer. */ if (CHECK_FLAG(from->af_flags[afi][safi], PEER_FLAG_REFLECTOR_CLIENT)) { @@ -2401,16 +2398,15 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi, RESET_FLAG(attr->rmap_change_flags); /* If local-preference is not set. */ - if ((peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED) - && (!(attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)))) { - attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF); + if ((peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED) && + (!CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)))) { + SET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)); attr->local_pref = bgp->default_local_pref; } /* If originator-id is not set and the route is to be reflected, set the originator id */ - if (reflect - && (!(attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)))) { + if (ibgp_to_ibgp && (!CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)))) { IPV4_ADDR_COPY(&(attr->originator_id), &(from->remote_id)); SET_FLAG(attr->flag, BGP_ATTR_ORIGINATOR_ID); } @@ -2418,12 +2414,11 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi, /* Remove MED if its an EBGP peer - will get overwritten by route-maps */ if (peer->sort == BGP_PEER_EBGP && peer->sub_sort != BGP_PEER_EBGP_OAD && - attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)) { + CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC))) { if (from != bgp->peer_self && !transparent && !CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MED_UNCHANGED)) - attr->flag &= - ~(ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)); + UNSET_FLAG(attr->flag, (ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC))); } /* Since the nexthop attribute can vary per peer, it is not explicitly @@ -2443,7 +2438,7 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi, * announced to an EBGP peer (and they have the same attributes barring * their nexthop). */ - if (reflect) + if (ibgp_to_ibgp) SET_FLAG(attr->rmap_change_flags, BATTR_REFLECTED); #define NEXTHOP_IS_V6 \ @@ -2465,33 +2460,38 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi, * we do not announce LL address as `::`. */ if (NEXTHOP_IS_V6) { - attr->mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL; - if ((CHECK_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED) && - IN6_IS_ADDR_LINKLOCAL(&attr->mp_nexthop_local)) || - (!reflect && !transparent && - IN6_IS_ADDR_LINKLOCAL(&peer->nexthop.v6_local) && - peer->shared_network && - ((from == bgp->peer_self && peer->sort == BGP_PEER_EBGP) || - (from == bgp->peer_self && peer->sort != BGP_PEER_EBGP) || - (from != bgp->peer_self && - IN6_IS_ADDR_LINKLOCAL(&attr->mp_nexthop_local) && - peer->sort == BGP_PEER_EBGP)))) { + if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED)) { + /* nexthop local unchanged: only include the link-local nexthop if it + * was already present. + */ + if (IN6_IS_ADDR_LINKLOCAL(&attr->mp_nexthop_local)) + global_and_ll = true; + } else if (!ibgp_to_ibgp && !transparent && + !CHECK_FLAG(from->af_flags[afi][safi], PEER_FLAG_REFLECTOR_CLIENT) && + IN6_IS_ADDR_LINKLOCAL(&peer->nexthop.v6_local) && peer->shared_network && + (from == bgp->peer_self || peer->sort == BGP_PEER_EBGP)) + global_and_ll = true; + + if (global_and_ll) { if (safi == SAFI_MPLS_VPN) attr->mp_nexthop_len = BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL; else attr->mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL; - } + } else + attr->mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL; /* Clear off link-local nexthop in source, whenever it is not * needed to * ensure more prefixes share the same attribute for * announcement. */ - if (!(CHECK_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED))) + if (!(CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED)) || + !IPV6_ADDR_SAME(&peer->nexthop.v6_global, &from->nexthop.v6_global)) + /* Reset if "nexthop-local unchanged" is not set or originating and destination peer + * does not share the same subnet. + */ memset(&attr->mp_nexthop_local, 0, IPV6_MAX_BYTELEN); } @@ -2507,8 +2507,8 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi, struct attr dummy_attr = *attr; /* Fill temp path_info */ - prep_for_rmap_apply(&rmap_path, &dummy_rmap_path_extra, dest, - pi, peer, &dummy_attr); + prep_for_rmap_apply(&rmap_path, &dummy_rmap_path_extra, dest, pi, peer, NULL, + &dummy_attr); struct route_map *amap = route_map_lookup_by_name(filter->advmap.aname); @@ -2532,9 +2532,13 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi, struct bgp_path_info_extra dummy_rmap_path_extra = {0}; struct attr dummy_attr = {0}; - /* Fill temp path_info */ - prep_for_rmap_apply(&rmap_path, &dummy_rmap_path_extra, dest, - pi, peer, attr); + /* Fill temp path_info. + * Inject the peer structure of the source peer (from). + * This is useful for e.g. `match peer ...` in outgoing + * direction. + */ + prep_for_rmap_apply(&rmap_path, &dummy_rmap_path_extra, dest, pi, peer, from, attr); + /* * The route reflector is not allowed to modify the attributes * of the reflected IBGP routes unless explicitly allowed. @@ -2610,8 +2614,7 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi, * one. If they match, do not announce, to prevent routing * loops. */ - if ((attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)) && - peer->soo[afi][safi]) { + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)) && peer->soo[afi][safi]) { struct ecommunity *ecomm_soo = peer->soo[afi][safi]; struct ecommunity *ecomm = bgp_attr_get_ecommunity(attr); @@ -2638,7 +2641,7 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi, if (peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED || peer->sub_sort == BGP_PEER_EBGP_OAD) { - attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF); + SET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)); attr->local_pref = BGP_GSHUT_LOCAL_PREF; } else { bgp_attr_add_gshut_community(attr); @@ -2689,9 +2692,8 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi, PEER_FLAG_NEXTHOP_SELF) || CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_FORCE_NEXTHOP_SELF)) { - if (!reflect - || CHECK_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_FORCE_NEXTHOP_SELF)) { + if (!ibgp_to_ibgp || + CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_FORCE_NEXTHOP_SELF)) { subgroup_announce_reset_nhop( (peer_cap_enhe(peer, afi, safi) ? AF_INET6 @@ -2815,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; } @@ -3422,9 +3483,8 @@ static void bgp_process_evpn_route_injection(struct bgp *bgp, afi_t afi, dummy_attr = *new_select->attr; /* Fill temp path_info */ - prep_for_rmap_apply(&rmap_path, &rmap_path_extra, dest, - new_select, new_select->peer, - &dummy_attr); + prep_for_rmap_apply(&rmap_path, &rmap_path_extra, dest, new_select, + new_select->peer, NULL, &dummy_attr); RESET_FLAG(dummy_attr.rmap_change_flags); @@ -3722,11 +3782,10 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest, /* If there is a change of interest to peers, reannounce the * route. */ - if (CHECK_FLAG(old_select->flags, BGP_PATH_ATTR_CHANGED) - || CHECK_FLAG(old_select->flags, BGP_PATH_LINK_BW_CHG) - || CHECK_FLAG(dest->flags, BGP_NODE_LABEL_CHANGED)) { + if (CHECK_FLAG(old_select->flags, BGP_PATH_ATTR_CHANGED) || + CHECK_FLAG(dest->flags, BGP_NODE_LABEL_CHANGED) || + bgp_zebra_has_route_changed(old_select)) { group_announce_route(bgp, afi, safi, dest, new_select); - /* unicast routes must also be annouced to * labeled-unicast update-groups */ if (safi == SAFI_UNICAST) @@ -4413,7 +4472,7 @@ bool bgp_update_martian_nexthop(struct bgp *bgp, afi_t afi, safi_t safi, return false; /* If NEXT_HOP is present, validate it. */ - if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP)) { + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP))) { if (attr->nexthop.s_addr == INADDR_ANY || !ipv4_unicast_valid(&attr->nexthop) || bgp_nexthop_self(bgp, afi, type, stype, attr, dest)) @@ -4513,7 +4572,7 @@ static bool bgp_accept_own(struct peer *peer, afi_t afi, safi_t safi, return false; /* The route in question carries the ACCEPT_OWN community */ - if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES)) { + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES))) { struct community *comm = bgp_attr_get_community(attr); if (community_include(comm, COMMUNITY_ACCEPT_OWN)) @@ -4614,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); } @@ -4700,8 +4761,8 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, */ bool accept_own = false; - if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID) - && IPV4_ADDR_SAME(&bgp->router_id, &attr->originator_id)) { + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)) && + IPV4_ADDR_SAME(&bgp->router_id, &attr->originator_id)) { accept_own = bgp_accept_own(peer, afi, safi, attr, p, &sub_type); if (!accept_own) { @@ -4738,7 +4799,7 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, /* If the route has Node Target Extended Communities, check * if it's allowed to be installed locally. */ - if ((attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES))) { + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES))) { struct ecommunity *ecomm = bgp_attr_get_ecommunity(attr); if (ecommunity_lookup(ecomm, ECOMMUNITY_ENCODE_IP, @@ -4793,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 @@ -4840,7 +4903,7 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, if (bgp_attr_get_community(&new_attr) && community_include(bgp_attr_get_community(&new_attr), COMMUNITY_GSHUT)) { - new_attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF); + SET_FLAG(new_attr.flag, ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)); new_attr.local_pref = BGP_GSHUT_LOCAL_PREF; /* If graceful-shutdown is configured globally or @@ -5061,10 +5124,8 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, */ if (((safi == SAFI_EVPN) || (safi == SAFI_MPLS_VPN)) && !same_attr) { - if ((pi->attr->flag - & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)) - && (attr_new->flag - & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES))) { + if (CHECK_FLAG(pi->attr->flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)) && + CHECK_FLAG(attr_new->flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES))) { int cmp; cmp = ecommunity_cmp( @@ -6742,22 +6803,19 @@ void bgp_static_update(struct bgp *bgp, const struct prefix *p, bgp_attr_default_set(&attr, bgp, BGP_ORIGIN_IGP); attr.nexthop = bgp_static->igpnexthop; - attr.med = bgp_static->igpmetric; - attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC); + + bgp_attr_set_med(&attr, bgp_static->igpmetric); if (afi == AFI_IP) attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; - if (bgp_static->igpmetric) - bgp_attr_set_aigp_metric(&attr, bgp_static->igpmetric); - if (bgp_static->atomic) - attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE); + SET_FLAG(attr.flag, ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE)); /* Store label index, if required. */ if (bgp_static->label_index != BGP_INVALID_LABEL_INDEX) { attr.label_index = bgp_static->label_index; - attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID); + SET_FLAG(attr.flag, ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID)); } if (safi == SAFI_EVPN || safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP) { @@ -8050,8 +8108,7 @@ bool bgp_aggregate_route(struct bgp *bgp, const struct prefix *p, afi_t afi, if (BGP_PATH_HOLDDOWN(pi)) continue; - if (pi->attr->flag - & ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE)) + if (CHECK_FLAG(pi->attr->flag, ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE))) atomic_aggregate = 1; if (pi->sub_type == BGP_ROUTE_AGGREGATE) @@ -9010,14 +9067,10 @@ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p, else UNSET_FLAG(attr.nh_flags, BGP_ATTR_NH_IF_OPERSTATE); - attr.med = metric; + bgp_attr_set_med(&attr, metric); attr.distance = distance; - attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC); attr.tag = tag; - if (metric) - bgp_attr_set_aigp_metric(&attr, metric); - afi = family2afi(p->family); red = bgp_redist_lookup(bgp, afi, type, instance); @@ -9027,10 +9080,8 @@ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p, /* Copy attribute for modification. */ attr_new = attr; - if (red->redist_metric_flag) { + if (red->redist_metric_flag) attr_new.med = red->redist_metric; - bgp_attr_set_aigp_metric(&attr_new, red->redist_metric); - } /* Apply route-map. */ if (red->rmap.name) { @@ -9796,7 +9847,7 @@ void route_vty_out(struct vty *vty, const struct prefix *p, } /* Local Pref */ - if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) if (json_paths) json_object_int_add(json_path, "locPrf", attr->local_pref); @@ -9837,7 +9888,7 @@ void route_vty_out(struct vty *vty, const struct prefix *p, esi_buf, sizeof(esi_buf))); } if (safi == SAFI_EVPN && - attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)) { + CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES))) { json_ext_community = json_object_new_object(); json_object_string_add( json_ext_community, "string", @@ -9891,8 +9942,7 @@ void route_vty_out(struct vty *vty, const struct prefix *p, vty_out(vty, "\n"); } - if (attr->flag & - ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)) { + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES))) { vty_out(vty, "%*s", 20, " "); vty_out(vty, "%s\n", bgp_attr_get_ecommunity(attr)->str); @@ -9974,7 +10024,7 @@ void route_vty_out_tmp(struct vty *vty, struct bgp *bgp, struct bgp_dest *dest, json_object_int_add(json_net, "metric", value); } - if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) json_object_int_add(json_net, "locPrf", attr->local_pref); @@ -10025,7 +10075,7 @@ void route_vty_out_tmp(struct vty *vty, struct bgp *bgp, struct bgp_dest *dest, else vty_out(vty, " "); - if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) vty_out(vty, "%7u", attr->local_pref); else vty_out(vty, " "); @@ -10937,7 +10987,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, &path->peer->connection->su); } - if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)) + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID))) vty_out(vty, " (%pI4)", &attr->originator_id); else vty_out(vty, " (%pI4)", &path->peer->remote_id); @@ -11060,7 +11110,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, vty_out(vty, ", metric %u", value); } - if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) { + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) { if (json_paths) json_object_int_add(json_path, "locPrf", attr->local_pref); @@ -11068,7 +11118,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, vty_out(vty, ", localpref %u", attr->local_pref); } - if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_AIGP)) { + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_AIGP))) { if (json_paths) json_object_int_add(json_path, "aigpMetric", bgp_attr_get_aigp_metric(attr)); @@ -11168,7 +11218,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, } } - if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE)) { + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE))) { if (json_paths) json_object_boolean_true_add(json_path, "atomicAggregate"); @@ -11176,7 +11226,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, vty_out(vty, ", atomic-aggregate"); } - if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_OTC)) { + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_OTC))) { if (json_paths) json_object_int_add(json_path, "otc", attr->otc); else @@ -11240,7 +11290,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, vty_out(vty, "\n"); /* Line 4 display Community */ - if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES)) { + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES))) { if (json_paths) { if (!bgp_attr_get_community(attr)->json) community_str(bgp_attr_get_community(attr), @@ -11256,7 +11306,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, } /* Line 5 display Extended-community */ - if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)) { + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES))) { if (json_paths) { json_ext_community = json_object_new_object(); json_object_string_add( @@ -11270,7 +11320,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, } } - if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_IPV6_EXT_COMMUNITIES)) { + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_IPV6_EXT_COMMUNITIES))) { if (json_paths) { json_ext_ipv6_community = json_object_new_object(); json_object_string_add(json_ext_ipv6_community, "string", @@ -11286,7 +11336,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, } /* Line 6 display Large community */ - if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES)) { + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES))) { if (json_paths) { if (!bgp_attr_get_lcommunity(attr)->json) lcommunity_str(bgp_attr_get_lcommunity(attr), @@ -11302,11 +11352,11 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, } /* Line 7 display Originator, Cluster-id */ - if ((attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)) - || (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST))) { + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)) || + CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST))) { char buf[BUFSIZ] = {0}; - if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)) { + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID))) { if (json_paths) json_object_string_addf(json_path, "originatorId", "%pI4", @@ -11316,7 +11366,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, &attr->originator_id); } - if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST)) { + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST))) { struct cluster_list *cluster = bgp_attr_get_cluster(attr); int i; @@ -11485,7 +11535,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, vty_out(vty, " Last update: %s", ctime_r(&tbuf, timebuf)); /* Line 10 display PMSI tunnel attribute, if present */ - if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_PMSI_TUNNEL)) { + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_PMSI_TUNNEL))) { const char *str = lookup_msg(bgp_pmsi_tnltype_str, bgp_attr_get_pmsi_tnl_type(attr), PMSI_TNLTYPE_STR_DEFAULT); @@ -11782,8 +11832,8 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t sa dummy_attr = *pi->attr; - prep_for_rmap_apply(&path, &extra, dest, pi, - pi->peer, &dummy_attr); + prep_for_rmap_apply(&path, &extra, dest, pi, pi->peer, NULL, + &dummy_attr); ret = route_map_apply(rmap, dest_p, &path); bgp_attr_flush(&dummy_attr); diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index d71bfd3ebc62..1df0ffd300e1 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -284,6 +284,9 @@ struct bgp_path_info { /* Peer structure. */ struct peer *peer; + /* From peer structure */ + struct peer *from; + /* Attribute structure. */ struct attr *attr; @@ -619,13 +622,13 @@ static inline bool is_pi_family_matching(struct bgp_path_info *pi, } static inline void prep_for_rmap_apply(struct bgp_path_info *dst_pi, - struct bgp_path_info_extra *dst_pie, - struct bgp_dest *dest, - struct bgp_path_info *src_pi, - struct peer *peer, struct attr *attr) + struct bgp_path_info_extra *dst_pie, struct bgp_dest *dest, + struct bgp_path_info *src_pi, struct peer *peer, + struct peer *from, struct attr *attr) { memset(dst_pi, 0, sizeof(struct bgp_path_info)); dst_pi->peer = peer; + dst_pi->from = from; dst_pi->attr = attr; dst_pi->net = dest; dst_pi->flags = src_pi->flags; diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 583b9e7980f5..8666831c7f59 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -125,6 +125,10 @@ o Local extensions #define RMAP_VALUE_ADD 1 #define RMAP_VALUE_SUB 2 +#define RMAP_VALUE_TYPE_RTT 1 +#define RMAP_VALUE_TYPE_IGP 2 +#define RMAP_VALUE_TYPE_AIGP 3 + struct rmap_value { uint8_t action; uint8_t variable; @@ -140,14 +144,21 @@ static int route_value_match(struct rmap_value *rv, uint32_t value) } static uint32_t route_value_adjust(struct rmap_value *rv, uint32_t current, - struct peer *peer) + struct bgp_path_info *bpi) { uint32_t value; + struct peer *peer = bpi->peer; switch (rv->variable) { - case 1: + case RMAP_VALUE_TYPE_RTT: value = peer->rtt; break; + case RMAP_VALUE_TYPE_IGP: + value = bpi->extra ? bpi->extra->igpmetric : 0; + break; + case RMAP_VALUE_TYPE_AIGP: + value = MIN(bpi->attr->aigp_metric, UINT32_MAX); + break; default: value = rv->value; break; @@ -187,11 +198,14 @@ static void *route_value_compile(const char *arg) larg = strtoul(arg, &endptr, 10); if (*arg == 0 || *endptr != 0 || errno || larg > UINT32_MAX) return NULL; + } else if (strmatch(arg, "rtt")) { + var = RMAP_VALUE_TYPE_RTT; + } else if (strmatch(arg, "igp")) { + var = RMAP_VALUE_TYPE_IGP; + } else if (strmatch(arg, "aigp")) { + var = RMAP_VALUE_TYPE_AIGP; } else { - if (strcmp(arg, "rtt") == 0) - var = 1; - else - return NULL; + return NULL; } rv = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct rmap_value)); @@ -333,6 +347,66 @@ static const struct route_map_rule_cmd route_match_peer_cmd = { route_match_peer_free }; +static enum route_map_cmd_result_t route_match_src_peer(void *rule, const struct prefix *prefix, + void *object) +{ + struct bgp_match_peer_compiled *pc; + union sockunion *su; + struct peer_group *group; + struct peer *peer; + struct listnode *node, *nnode; + struct bgp_path_info *bpi; + + pc = rule; + su = &pc->su; + bpi = object; + peer = bpi->from; + + /* Fallback to destination (current) peer. This is mostly + * happens if `match src-peer ...` is used at incoming direction. + */ + if (!peer) + peer = bpi->peer; + + if (!peer) + return RMAP_NOMATCH; + + if (pc->interface) { + if (!peer->conf_if && !peer->group) + return RMAP_NOMATCH; + + if (peer->conf_if && strcmp(peer->conf_if, pc->interface) == 0) + return RMAP_MATCH; + + if (peer->group && strcmp(peer->group->name, pc->interface) == 0) + return RMAP_MATCH; + + return RMAP_NOMATCH; + } + + if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { + if (sockunion_same(su, &peer->connection->su)) + return RMAP_MATCH; + + return RMAP_NOMATCH; + } + + group = peer->group; + for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { + if (sockunion_same(su, &peer->connection->su)) + return RMAP_MATCH; + } + + return RMAP_NOMATCH; +} + +static const struct route_map_rule_cmd route_match_src_peer_cmd = { + "src-peer", + route_match_src_peer, + route_match_peer_compile, + route_match_peer_free +}; + #ifdef HAVE_SCRIPTING enum frrlua_rm_status { @@ -406,7 +480,7 @@ route_match_script(void *rule, const struct prefix *prefix, void *object) path->attr->med = newattr.med; - if (path->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) + if (CHECK_FLAG(path->attr->flag, ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) locpref = path->attr->local_pref; if (locpref != newattr.local_pref) { SET_FLAG(path->attr->flag, @@ -639,15 +713,15 @@ route_match_prefix_list_flowspec(afi_t afi, struct prefix_list *plist, afi); if (ret < 0) return RMAP_NOMATCH; - if (api.match_bitmask & PREFIX_DST_PRESENT || - api.match_bitmask_iprule & PREFIX_DST_PRESENT) { + if (CHECK_FLAG(api.match_bitmask, PREFIX_DST_PRESENT) || + CHECK_FLAG(api.match_bitmask_iprule, PREFIX_DST_PRESENT)) { if (family2afi((&api.dst_prefix)->family) != afi) return RMAP_NOMATCH; return prefix_list_apply(plist, &api.dst_prefix) == PREFIX_DENY ? RMAP_NOMATCH : RMAP_MATCH; - } else if (api.match_bitmask & PREFIX_SRC_PRESENT || - api.match_bitmask_iprule & PREFIX_SRC_PRESENT) { + } else if (CHECK_FLAG(api.match_bitmask, PREFIX_SRC_PRESENT) || + CHECK_FLAG(api.match_bitmask_iprule, PREFIX_SRC_PRESENT)) { if (family2afi((&api.src_prefix)->family) != afi) return RMAP_NOMATCH; return (prefix_list_apply(plist, &api.src_prefix) == PREFIX_DENY @@ -1997,7 +2071,7 @@ route_set_ip_nexthop(void *rule, const struct prefix *prefix, void *object) sockunion_family(peer->su_remote) == AF_INET) { path->attr->nexthop.s_addr = sockunion2ip(peer->su_remote); - path->attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); + SET_FLAG(path->attr->flag, ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP)); } else if (CHECK_FLAG(peer->rmap_type, PEER_RMAP_TYPE_OUT)) { /* The next hop value will be set as part of * packet rewrite. Set the flags here to indicate @@ -2010,7 +2084,7 @@ route_set_ip_nexthop(void *rule, const struct prefix *prefix, void *object) } } else { /* Set next hop value. */ - path->attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); + SET_FLAG(path->attr->flag, ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP)); path->attr->nexthop = *rins->address; SET_FLAG(path->attr->rmap_change_flags, BATTR_RMAP_IPV4_NHOP_CHANGED); @@ -2144,8 +2218,8 @@ route_set_local_pref(void *rule, const struct prefix *prefix, void *object) if (path->attr->local_pref) locpref = path->attr->local_pref; - path->attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF); - path->attr->local_pref = route_value_adjust(rv, locpref, path->peer); + SET_FLAG(path->attr->flag, ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)); + path->attr->local_pref = route_value_adjust(rv, locpref, path); return RMAP_OKAY; } @@ -2172,7 +2246,7 @@ route_set_weight(void *rule, const struct prefix *prefix, void *object) path = object; /* Set weight value. */ - path->attr->weight = route_value_adjust(rv, 0, path->peer); + path->attr->weight = route_value_adjust(rv, 0, path); return RMAP_OKAY; } @@ -2219,11 +2293,10 @@ route_set_metric(void *rule, const struct prefix *prefix, void *object) rv = rule; path = object; - if (path->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)) + if (CHECK_FLAG(path->attr->flag, ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC))) med = path->attr->med; - path->attr->med = route_value_adjust(rv, med, path->peer); - path->attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC); + bgp_attr_set_med(path->attr, route_value_adjust(rv, med, path)); return RMAP_OKAY; } @@ -3420,7 +3493,7 @@ route_set_atomic_aggregate(void *rule, const struct prefix *pfx, void *object) struct bgp_path_info *path; path = object; - path->attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE); + SET_FLAG(path->attr->flag, ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE)); return RMAP_OKAY; } @@ -3451,19 +3524,15 @@ route_set_aigp_metric(void *rule, const struct prefix *pfx, void *object) { const char *aigp_metric = rule; struct bgp_path_info *path = object; - uint32_t aigp = 0; - - if (strmatch(aigp_metric, "igp-metric")) { - if (!path->nexthop) - return RMAP_NOMATCH; + uint32_t aigp; - bgp_attr_set_aigp_metric(path->attr, path->nexthop->metric); - } else { + /* Note: the metric is stored as MED for a locally redistributed. */ + if (strmatch(aigp_metric, "igp-metric")) + aigp = path->nexthop ? path->nexthop->metric : path->attr->med; + else aigp = atoi(aigp_metric); - bgp_attr_set_aigp_metric(path->attr, aigp); - } - path->attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AIGP); + bgp_attr_set_aigp_metric(path->attr, aigp); return RMAP_OKAY; } @@ -3502,7 +3571,7 @@ route_set_aggregator_as(void *rule, const struct prefix *prefix, void *object) path->attr->aggregator_as = aggregator->as; path->attr->aggregator_addr = aggregator->address; - path->attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AGGREGATOR); + SET_FLAG(path->attr->flag, ATTR_FLAG_BIT(BGP_ATTR_AGGREGATOR)); return RMAP_OKAY; } @@ -3582,7 +3651,7 @@ route_set_label_index(void *rule, const struct prefix *prefix, void *object) label_index = rv->value; if (label_index) { path->attr->label_index = label_index; - path->attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID); + SET_FLAG(path->attr->flag, ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID)); } return RMAP_OKAY; @@ -4249,7 +4318,7 @@ route_set_originator_id(void *rule, const struct prefix *prefix, void *object) address = rule; path = object; - path->attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID); + SET_FLAG(path->attr->flag, ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)); path->attr->originator_id = *address; return RMAP_OKAY; @@ -5274,6 +5343,52 @@ DEFUN_YANG (no_match_peer, return nb_cli_apply_changes(vty, NULL); } +DEFPY_YANG (match_src_peer, + match_src_peer_cmd, + "match src-peer ", + MATCH_STR + "Match source peer address\n" + "IP address of peer\n" + "IPv6 address of peer\n" + "Interface name of peer or peer group name\n") +{ + const char *xpath = "./match-condition[condition='frr-bgp-route-map:src-peer']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:src-peer-ipv4-address", xpath); + nb_cli_enqueue_change(vty, xpath_value, addrv4_str ? NB_OP_MODIFY : NB_OP_DESTROY, + addrv4_str); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:src-peer-ipv6-address", xpath); + nb_cli_enqueue_change(vty, xpath_value, addrv6_str ? NB_OP_MODIFY : NB_OP_DESTROY, + addrv6_str); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:src-peer-interface", xpath); + nb_cli_enqueue_change(vty, xpath_value, intf ? NB_OP_MODIFY : NB_OP_DESTROY, intf); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFUN_YANG (no_match_src_peer, + no_match_src_peer_cmd, + "no match src-peer []", + NO_STR + MATCH_STR + "Match peer address\n" + "IP address of peer\n" + "IPv6 address of peer\n" + "Interface name of peer\n") +{ + const char *xpath = "./match-condition[condition='frr-bgp-route-map:src-peer']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + #ifdef HAVE_SCRIPTING DEFUN_YANG (match_script, match_script_cmd, @@ -7140,7 +7255,7 @@ DEFUN_YANG (no_set_atomic_aggregate, DEFPY_YANG (set_aigp_metric, set_aigp_metric_cmd, - "set aigp-metric $aigp_metric", + "set aigp-metric $aigp_metric", SET_STR "BGP AIGP attribute (AIGP Metric TLV)\n" "AIGP Metric value from IGP protocol\n" @@ -7160,7 +7275,7 @@ DEFPY_YANG (set_aigp_metric, DEFPY_YANG (no_set_aigp_metric, no_set_aigp_metric_cmd, - "no set aigp-metric []", + "no set aigp-metric []", NO_STR SET_STR "BGP AIGP attribute (AIGP Metric TLV)\n" @@ -7232,43 +7347,6 @@ DEFUN_YANG (no_set_aggregator_as, return nb_cli_apply_changes(vty, NULL); } -DEFUN_YANG (match_ipv6_next_hop, - match_ipv6_next_hop_cmd, - "match ipv6 next-hop ACCESSLIST6_NAME", - MATCH_STR - IPV6_STR - "Match IPv6 next-hop address of route\n" - "IPv6 access-list name\n") -{ - const char *xpath = - "./match-condition[condition='frr-route-map:ipv6-next-hop-list']"; - char xpath_value[XPATH_MAXLEN]; - - nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - snprintf(xpath_value, sizeof(xpath_value), - "%s/rmap-match-condition/list-name", xpath); - nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, - argv[argc - 1]->arg); - - return nb_cli_apply_changes(vty, NULL); -} - -DEFUN_YANG (no_match_ipv6_next_hop, - no_match_ipv6_next_hop_cmd, - "no match ipv6 next-hop [ACCESSLIST6_NAME]", - NO_STR - MATCH_STR - IPV6_STR - "Match IPv6 next-hop address of route\n" - "IPv6 access-list name\n") -{ - const char *xpath = - "./match-condition[condition='frr-route-map:ipv6-next-hop-list']"; - - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - return nb_cli_apply_changes(vty, NULL); -} - DEFUN_YANG (match_ipv6_next_hop_address, match_ipv6_next_hop_address_cmd, "match ipv6 next-hop address X:X::X:X", @@ -7326,45 +7404,6 @@ ALIAS_HIDDEN (no_match_ipv6_next_hop_address, "Match IPv6 next-hop address of route\n" "IPv6 address of next hop\n") -DEFUN_YANG (match_ipv6_next_hop_prefix_list, - match_ipv6_next_hop_prefix_list_cmd, - "match ipv6 next-hop prefix-list PREFIXLIST_NAME", - MATCH_STR - IPV6_STR - "Match IPv6 next-hop address of route\n" - "Match entries by prefix-list\n" - "IPv6 prefix-list name\n") -{ - const char *xpath = - "./match-condition[condition='frr-route-map:ipv6-next-hop-prefix-list']"; - char xpath_value[XPATH_MAXLEN]; - - nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - snprintf(xpath_value, sizeof(xpath_value), - "%s/rmap-match-condition/list-name", xpath); - nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, - argv[argc - 1]->arg); - - return nb_cli_apply_changes(vty, NULL); -} - -DEFUN_YANG (no_match_ipv6_next_hop_prefix_list, - no_match_ipv6_next_hop_prefix_list_cmd, - "no match ipv6 next-hop prefix-list [PREFIXLIST_NAME]", - NO_STR - MATCH_STR - IPV6_STR - "Match IPv6 next-hop address of route\n" - "Match entries by prefix-list\n" - "IPv6 prefix-list name\n") -{ - const char *xpath = - "./match-condition[condition='frr-route-map:ipv6-next-hop-prefix-list']"; - - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - return nb_cli_apply_changes(vty, NULL); -} - DEFPY_YANG (match_ipv4_next_hop, match_ipv4_next_hop_cmd, "match ip next-hop address A.B.C.D", @@ -7841,6 +7880,7 @@ void bgp_route_map_init(void) route_map_no_set_tag_hook(generic_set_delete); route_map_install_match(&route_match_peer_cmd); + route_map_install_match(&route_match_src_peer_cmd); route_map_install_match(&route_match_alias_cmd); route_map_install_match(&route_match_local_pref_cmd); #ifdef HAVE_SCRIPTING @@ -7908,6 +7948,8 @@ void bgp_route_map_init(void) install_element(RMAP_NODE, &match_peer_cmd); install_element(RMAP_NODE, &match_peer_local_cmd); + install_element(RMAP_NODE, &match_src_peer_cmd); + install_element(RMAP_NODE, &no_match_src_peer_cmd); install_element(RMAP_NODE, &no_match_peer_cmd); install_element(RMAP_NODE, &match_ip_route_source_cmd); install_element(RMAP_NODE, &no_match_ip_route_source_cmd); @@ -8037,12 +8079,8 @@ void bgp_route_map_init(void) route_map_install_set(&route_set_ipv6_nexthop_peer_cmd); route_map_install_match(&route_match_rpki_extcommunity_cmd); - install_element(RMAP_NODE, &match_ipv6_next_hop_cmd); install_element(RMAP_NODE, &match_ipv6_next_hop_address_cmd); - install_element(RMAP_NODE, &match_ipv6_next_hop_prefix_list_cmd); - install_element(RMAP_NODE, &no_match_ipv6_next_hop_cmd); install_element(RMAP_NODE, &no_match_ipv6_next_hop_address_cmd); - install_element(RMAP_NODE, &no_match_ipv6_next_hop_prefix_list_cmd); install_element(RMAP_NODE, &match_ipv6_next_hop_old_cmd); install_element(RMAP_NODE, &no_match_ipv6_next_hop_old_cmd); install_element(RMAP_NODE, &match_ipv4_next_hop_cmd); diff --git a/bgpd/bgp_routemap_nb.c b/bgpd/bgp_routemap_nb.c index 096502aaa904..d8fdb4fbc43b 100644 --- a/bgpd/bgp_routemap_nb.c +++ b/bgpd/bgp_routemap_nb.c @@ -109,6 +109,27 @@ const struct frr_yang_module_info frr_bgp_route_map_info = { .destroy = lib_route_map_entry_match_condition_rmap_match_condition_peer_local_destroy, } }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:src-peer-ipv4-address", + .cbs = { + .modify = lib_route_map_entry_match_condition_rmap_match_condition_src_peer_ipv4_address_modify, + .destroy = lib_route_map_entry_match_condition_rmap_match_condition_src_peer_ipv4_address_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:src-peer-interface", + .cbs = { + .modify = lib_route_map_entry_match_condition_rmap_match_condition_src_peer_interface_modify, + .destroy = lib_route_map_entry_match_condition_rmap_match_condition_src_peer_interface_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:src-peer-ipv6-address", + .cbs = { + .modify = lib_route_map_entry_match_condition_rmap_match_condition_src_peer_ipv6_address_modify, + .destroy = lib_route_map_entry_match_condition_rmap_match_condition_src_peer_ipv6_address_destroy, + } + }, { .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:list-name", .cbs = { diff --git a/bgpd/bgp_routemap_nb.h b/bgpd/bgp_routemap_nb.h index d7f0cea30ea0..f59686f3864b 100644 --- a/bgpd/bgp_routemap_nb.h +++ b/bgpd/bgp_routemap_nb.h @@ -46,6 +46,18 @@ int lib_route_map_entry_match_condition_rmap_match_condition_peer_ipv6_address_m int lib_route_map_entry_match_condition_rmap_match_condition_peer_ipv6_address_destroy(struct nb_cb_destroy_args *args); int lib_route_map_entry_match_condition_rmap_match_condition_peer_local_modify(struct nb_cb_modify_args *args); int lib_route_map_entry_match_condition_rmap_match_condition_peer_local_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_src_peer_ipv4_address_modify( + struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_src_peer_ipv4_address_destroy( + struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_src_peer_interface_modify( + struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_src_peer_interface_destroy( + struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_src_peer_ipv6_address_modify( + struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_src_peer_ipv6_address_destroy( + struct nb_cb_destroy_args *args); int lib_route_map_entry_match_condition_rmap_match_condition_access_list_num_modify(struct nb_cb_modify_args *args); int lib_route_map_entry_match_condition_rmap_match_condition_access_list_num_destroy(struct nb_cb_destroy_args *args); int lib_route_map_entry_match_condition_rmap_match_condition_access_list_num_extended_modify(struct nb_cb_modify_args *args); diff --git a/bgpd/bgp_routemap_nb_config.c b/bgpd/bgp_routemap_nb_config.c index 15c32eaa2856..0dca196ed637 100644 --- a/bgpd/bgp_routemap_nb_config.c +++ b/bgpd/bgp_routemap_nb_config.c @@ -808,6 +808,162 @@ lib_route_map_entry_match_condition_rmap_match_condition_peer_local_destroy( return NB_OK; } +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:src-peer-ipv4-address + */ +int lib_route_map_entry_match_condition_rmap_match_condition_src_peer_ipv4_address_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *peer; + enum rmap_compile_rets ret; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + peer = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = bgp_route_match_delete; + rhc->rhc_rule = "src-peer"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + ret = bgp_route_match_add(rhc->rhc_rmi, "src-peer", peer, RMAP_EVENT_MATCH_ADDED, + args->errmsg, args->errmsg_len); + + if (ret != RMAP_COMPILE_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int lib_route_map_entry_match_condition_rmap_match_condition_src_peer_ipv4_address_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:src-peer-interface + */ +int lib_route_map_entry_match_condition_rmap_match_condition_src_peer_interface_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *peer; + enum rmap_compile_rets ret; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + peer = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = bgp_route_match_delete; + rhc->rhc_rule = "src-peer"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + ret = bgp_route_match_add(rhc->rhc_rmi, "src-peer", peer, RMAP_EVENT_MATCH_ADDED, + args->errmsg, args->errmsg_len); + + if (ret != RMAP_COMPILE_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int lib_route_map_entry_match_condition_rmap_match_condition_src_peer_interface_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:src-peer-ipv6-address + */ +int lib_route_map_entry_match_condition_rmap_match_condition_src_peer_ipv6_address_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *peer; + enum rmap_compile_rets ret; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + peer = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = bgp_route_match_delete; + rhc->rhc_rule = "src-peer"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + ret = bgp_route_match_add(rhc->rhc_rmi, "src-peer", peer, RMAP_EVENT_MATCH_ADDED, + args->errmsg, args->errmsg_len); + + if (ret != RMAP_COMPILE_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int lib_route_map_entry_match_condition_rmap_match_condition_src_peer_ipv6_address_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; +} + /* * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:list-name */ diff --git a/bgpd/bgp_updgrp.c b/bgpd/bgp_updgrp.c index 90c43b938ffd..ef036067078f 100644 --- a/bgpd/bgp_updgrp.c +++ b/bgpd/bgp_updgrp.c @@ -455,6 +455,10 @@ static unsigned int updgrp_hash_key_make(const void *p) key = jhash_1word(jhash(soo_str, strlen(soo_str), SEED1), key); } + if (afi == AFI_IP6 && + (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED))) + key = jhash(&peer->nexthop.v6_global, IPV6_MAX_BYTELEN, key); + /* * ANY NEW ITEMS THAT ARE ADDED TO THE key, ENSURE DEBUG * STATEMENT STAYS UP TO DATE @@ -521,6 +525,12 @@ static unsigned int updgrp_hash_key_make(const void *p) peer->soo[afi][safi] ? ecommunity_str(peer->soo[afi][safi]) : "(NONE)"); + zlog_debug("%pBP Update Group Hash: IPv6 nexthop-local unchanged: %d IPv6 global %pI6", + peer, + afi == AFI_IP6 && (CHECK_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED)), + &peer->nexthop.v6_global); + zlog_debug("%pBP Update Group Hash key: %u", peer, key); } return key; @@ -655,6 +665,12 @@ static bool updgrp_hash_cmp(const void *p1, const void *p2) !sockunion_same(&pe1->connection->su, &pe2->connection->su)) return false; + if (afi == AFI_IP6 && + (CHECK_FLAG(flags1, PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED) || + CHECK_FLAG(flags2, PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED)) && + !IPV6_ADDR_SAME(&pe1->nexthop.v6_global, &pe2->nexthop.v6_global)) + return false; + return true; } diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c index 250378af6845..1a66df59fc20 100644 --- a/bgpd/bgp_updgrp_adv.c +++ b/bgpd/bgp_updgrp_adv.c @@ -906,8 +906,8 @@ void subgroup_default_originate(struct update_subgroup *subgrp, bool withdraw) assert(attr.aspath); aspath = attr.aspath; - attr.med = 0; - attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC); + + bgp_attr_set_med(&attr, 0); if ((afi == AFI_IP6) || peer_cap_enhe(peer, afi, safi)) { /* IPv6 global nexthop must be included. */ diff --git a/bgpd/bgp_updgrp_packet.c b/bgpd/bgp_updgrp_packet.c index bed00a66402f..3ce136ef873a 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 e7be2a33d2b6..62b79541bf21 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -161,9 +161,8 @@ static struct peer_group *listen_range_exists(struct bgp *bgp, static void bgp_show_global_graceful_restart_mode_vty(struct vty *vty, struct bgp *bgp); -static int bgp_show_neighbor_graceful_restart_afi_all(struct vty *vty, - enum show_type type, - const char *ip_str, +static int bgp_show_neighbor_graceful_restart_afi_all(struct vty *vty, struct bgp *bgp, + enum show_type type, const char *ip_str, afi_t afi, bool use_json); static enum node_type bgp_node_type(afi_t afi, safi_t safi) @@ -4494,16 +4493,6 @@ DEFUN (bgp_network_import_check, return CMD_SUCCESS; } -#if CONFDATE > 20241013 -CPP_NOTICE("Drop `bgp network import-check exact` command") -#endif -ALIAS_HIDDEN(bgp_network_import_check, bgp_network_import_check_exact_cmd, - "bgp network import-check exact", - BGP_STR - "BGP network command\n" - "Check BGP network route exists in IGP\n" - "Match route precisely\n") - DEFUN (no_bgp_network_import_check, no_bgp_network_import_check_cmd, "no bgp network import-check", @@ -16144,21 +16133,13 @@ static int bgp_show_neighbor(struct vty *vty, struct bgp *bgp, return CMD_SUCCESS; } -static void bgp_show_neighbor_graceful_restart_vty(struct vty *vty, - enum show_type type, - const char *ip_str, +static void bgp_show_neighbor_graceful_restart_vty(struct vty *vty, struct bgp *bgp, + enum show_type type, const char *ip_str, afi_t afi, json_object *json) { - int ret; - struct bgp *bgp; union sockunion su; - bgp = bgp_get_default(); - - if (!bgp) - return; - if (!json) bgp_show_global_graceful_restart_mode_vty(vty, bgp); @@ -16313,48 +16294,41 @@ static int bgp_show_neighbor_vty(struct vty *vty, const char *name, return CMD_SUCCESS; } - - /* "show [ip] bgp neighbors graceful-restart" commands. */ -DEFUN (show_ip_bgp_neighbors_graceful_restart, - show_ip_bgp_neighbors_graceful_restart_cmd, - "show bgp [] neighbors [] graceful-restart [json]", - SHOW_STR - BGP_STR - IP_STR - IPV6_STR - NEIGHBOR_STR - "Neighbor to display information about\n" - "Neighbor to display information about\n" - "Neighbor on BGP configured interface\n" - GR_SHOW +DEFPY (show_ip_bgp_neighbors_graceful_restart, + show_ip_bgp_neighbors_graceful_restart_cmd, + "show bgp []$afi [ VIEWVRFNAME$vrf] neighbors [$neigh] graceful-restart [json]$json", + SHOW_STR + BGP_STR + IP_STR + IPV6_STR + BGP_INSTANCE_HELP_STR + NEIGHBOR_STR + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Neighbor on BGP configured interface\n" + GR_SHOW JSON_STR) { - char *sh_arg = NULL; - enum show_type sh_type; - int idx = 0; - afi_t afi = AFI_MAX; - bool uj = use_json(argc, argv); - - if (!argv_find_and_parse_afi(argv, argc, &idx, &afi)) - afi = AFI_MAX; + enum show_type sh_type = show_all; + afi_t afiz = AFI_IP; + bool uj = !!json; + struct bgp *bgp; - idx++; + if (afi) + afiz = bgp_vty_afi_from_str(afi); - if (argv_find(argv, argc, "A.B.C.D", &idx) - || argv_find(argv, argc, "X:X::X:X", &idx) - || argv_find(argv, argc, "WORD", &idx)) { + if (neigh) sh_type = show_peer; - sh_arg = argv[idx]->arg; - } else - sh_type = show_all; - if (!argv_find(argv, argc, "graceful-restart", &idx)) - return CMD_SUCCESS; + bgp = vrf ? bgp_lookup_by_name(vrf) : bgp_get_default(); + if (!bgp) { + vty_out(vty, "No such bgp instance %s", vrf ? vrf : ""); + return CMD_WARNING; + } - return bgp_show_neighbor_graceful_restart_afi_all(vty, sh_type, sh_arg, - afi, uj); + return bgp_show_neighbor_graceful_restart_afi_all(vty, bgp, sh_type, neigh, afiz, uj); } /* "show [ip] bgp neighbors" commands. */ @@ -16528,9 +16502,8 @@ static void bgp_show_global_graceful_restart_mode_vty(struct vty *vty, vty_out(vty, "\n"); } -static int bgp_show_neighbor_graceful_restart_afi_all(struct vty *vty, - enum show_type type, - const char *ip_str, +static int bgp_show_neighbor_graceful_restart_afi_all(struct vty *vty, struct bgp *bgp, + enum show_type type, const char *ip_str, afi_t afi, bool use_json) { json_object *json = NULL; @@ -16542,14 +16515,11 @@ static int bgp_show_neighbor_graceful_restart_afi_all(struct vty *vty, afi = AFI_IP; while ((afi != AFI_L2VPN) && (afi < AFI_MAX)) { - - bgp_show_neighbor_graceful_restart_vty( - vty, type, ip_str, afi, json); + bgp_show_neighbor_graceful_restart_vty(vty, bgp, type, ip_str, afi, json); afi++; } } else if (afi != AFI_MAX) { - bgp_show_neighbor_graceful_restart_vty(vty, type, ip_str, afi, - json); + bgp_show_neighbor_graceful_restart_vty(vty, bgp, type, ip_str, afi, json); } else { if (json) json_object_free(json); @@ -20678,7 +20648,6 @@ void bgp_vty_init(void) /* "bgp network import-check" commands. */ install_element(BGP_NODE, &bgp_network_import_check_cmd); - install_element(BGP_NODE, &bgp_network_import_check_exact_cmd); install_element(BGP_NODE, &no_bgp_network_import_check_cmd); /* "bgp default local-preference" commands. */ diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 80b1ae39d4d1..a186243ffcfc 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -85,6 +85,7 @@ DEFINE_QOBJ_TYPE(bgp_master); DEFINE_QOBJ_TYPE(bgp); DEFINE_QOBJ_TYPE(peer); DEFINE_HOOK(bgp_inst_delete, (struct bgp *bgp), (bgp)); +DEFINE_HOOK(bgp_instance_state, (struct bgp *bgp), (bgp)); /* BGP process wide configuration. */ static struct bgp_master bgp_master; @@ -3929,6 +3930,9 @@ void bgp_instance_up(struct bgp *bgp) struct peer *peer; struct listnode *node, *next; + /* notify BMP of instance state changed */ + hook_call(bgp_instance_state, bgp); + bgp_set_redist_vrf_bitmaps(bgp, true); /* Register with zebra. */ @@ -3957,6 +3961,9 @@ void bgp_instance_down(struct bgp *bgp) /* Cleanup evpn instance state */ bgp_evpn_instance_down(bgp); + /* notify BMP of instance state changed */ + hook_call(bgp_instance_state, bgp); + /* Stop timers. */ if (bgp->t_rmap_def_originate_eval) EVENT_OFF(bgp->t_rmap_def_originate_eval); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 3c3655f0a5ad..5ffed544a510 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -880,6 +880,7 @@ DECLARE_HOOK(bgp_snmp_traps_config_write, (struct vty *vty), (vty)); DECLARE_HOOK(bgp_config_end, (struct bgp *bgp), (bgp)); DECLARE_HOOK(bgp_hook_vrf_update, (struct vrf *vrf, bool enabled), (vrf, enabled)); +DECLARE_HOOK(bgp_instance_state, (struct bgp *bgp), (bgp)); /* Thread callback information */ struct afi_safi_info { @@ -1222,6 +1223,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/bgpd/rfapi/rfapi.c b/bgpd/rfapi/rfapi.c index 23e3eb482308..61d154f1b436 100644 --- a/bgpd/rfapi/rfapi.c +++ b/bgpd/rfapi/rfapi.c @@ -667,10 +667,8 @@ void add_vnc_route(struct rfapi_descriptor *rfd, /* cookie, VPN UN addr, peer */ attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF); } - if (med) { - attr.med = *med; - attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC); - } + if (med) + bgp_attr_set_med(&attr, *med); /* override default weight assigned by bgp_attr_default_set() */ attr.weight = rfd->peer ? rfd->peer->weight[afi][safi] : 0; @@ -863,10 +861,8 @@ void add_vnc_route(struct rfapi_descriptor *rfd, /* cookie, VPN UN addr, peer */ red = bgp_redist_lookup(bgp, afi, type, 0); - if (red && red->redist_metric_flag) { - attr.med = red->redist_metric; - attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC); - } + if (red && red->redist_metric_flag) + bgp_attr_set_med(&attr, red->redist_metric); bn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, prd); diff --git a/bgpd/rfapi/vnc_export_bgp.c b/bgpd/rfapi/vnc_export_bgp.c index 4de2306609c9..c9a058ea44c0 100644 --- a/bgpd/rfapi/vnc_export_bgp.c +++ b/bgpd/rfapi/vnc_export_bgp.c @@ -96,15 +96,16 @@ static void encap_attr_export_ce(struct attr *new, struct attr *orig, * neighbor NEIGHBOR attribute-unchanged med */ if (!CHECK_FLAG(new->flag, BGP_ATTR_MULTI_EXIT_DISC)) { + uint32_t med = 255; + if (CHECK_FLAG(new->flag, BGP_ATTR_LOCAL_PREF)) { if (new->local_pref > 255) - new->med = 0; + med = 0; else - new->med = 255 - new->local_pref; - } else { - new->med = 255; /* shouldn't happen */ + med = 255 - new->local_pref; } - new->flag |= ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC); + + bgp_attr_set_med(new, med); } /* @@ -642,15 +643,16 @@ encap_attr_export(struct attr *new, struct attr *orig, * neighbor NEIGHBOR attribute-unchanged med */ if (!CHECK_FLAG(new->flag, BGP_ATTR_MULTI_EXIT_DISC)) { + uint32_t med = 255; + if (CHECK_FLAG(new->flag, BGP_ATTR_LOCAL_PREF)) { if (new->local_pref > 255) - new->med = 0; + med = 0; else - new->med = 255 - new->local_pref; - } else { - new->med = 255; /* shouldn't happen */ + med = 255 - new->local_pref; } - new->flag |= ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC); + + bgp_attr_set_med(new, med); } /* diff --git a/debian/changelog b/debian/changelog index 96c66db89bf6..9917b5bcb5fe 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,8 +1,14 @@ -frr (10.1~dev-1) UNRELEASED; urgency=medium +frr (10.3~dev-1) UNRELEASED; urgency=medium - * FRR Dev 10.1 + * FRR 10.3 Development - -- Jafar Al-Gharaibeh Tue, 26 Mar 2024 02:00:00 -0600 + -- Jafar Al-Gharaibeh Thu, 10 Oct 2024 02:00:00 -0600 + +frr (10.1-0) unstable; urgency=medium + + * New upstream release FRR 10.1 + + -- Jafar Al-Gharaibeh Fri, 26 Jul 2024 02:00:00 -0600 frr (10.0-0) unstable; urgency=medium diff --git a/doc/developer/topotests.rst b/doc/developer/topotests.rst index 620974963687..1ec516a1e153 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 `_ 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/bfd.rst b/doc/user/bfd.rst index 4c142cfbbb06..5f698a1cd092 100644 --- a/doc/user/bfd.rst +++ b/doc/user/bfd.rst @@ -139,7 +139,7 @@ Peer / Profile Configuration BFD peers and profiles share the same BFD session configuration commands. -.. clicmd:: detect-multiplier (2-255) +.. clicmd:: detect-multiplier (1-255) Configures the detection multiplier to determine packet loss. The remote transmission interval will be multiplied by this value to @@ -151,23 +151,23 @@ BFD peers and profiles share the same BFD session configuration commands. detect failures only after 900 milliseconds without receiving packets. -.. clicmd:: receive-interval (10-60000) +.. clicmd:: receive-interval (10-4294967) Configures the minimum interval that this system is capable of receiving control packets. The default value is 300 milliseconds. -.. clicmd:: transmit-interval (10-60000) +.. clicmd:: transmit-interval (10-4294967) The minimum transmission interval (less jitter) that this system wants to use to send BFD control packets. Defaults to 300ms. -.. clicmd:: echo receive-interval +.. clicmd:: echo receive-interval Configures the minimum interval that this system is capable of receiving echo packets. Disabled means that this system doesn't want to receive echo packets. The default value is 50 milliseconds. -.. clicmd:: echo transmit-interval (10-60000) +.. clicmd:: echo transmit-interval (10-4294967) The minimum transmission interval (less jitter) that this system wants to use to send BFD echo packets. Defaults to 50ms. diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 4632c70d53c4..f2005c18dd63 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. @@ -1169,6 +1169,14 @@ BGP GR Peer Mode Commands at the peer level. +BGP GR Show Commands +^^^^^^^^^^^^^^^^^^^^ + +.. clicmd:: show bgp [] [ VRF] neighbors [] graceful-restart [json] + + This command will display information about the neighbors graceful-restart status + + Long-lived Graceful Restart --------------------------- @@ -1842,7 +1850,8 @@ Configuring Peers .. clicmd:: neighbor as-override - Override AS number of the originating router with the local AS number. + Override any AS number in the AS path that matches the neighbor's AS number + with the local AS number. Usually this configuration is used in PEs (Provider Edge) to replace the incoming customer AS number so the connected CE (Customer Edge) diff --git a/doc/user/routemap.rst b/doc/user/routemap.rst index 1d2f4e352f38..02d674dff04a 100644 --- a/doc/user/routemap.rst +++ b/doc/user/routemap.rst @@ -190,6 +190,12 @@ Route Map Match Command do the exact matching of the communities, while ``any`` - can match any community specified in COMMUNITY_LIST. +.. clicmd:: match src-peer [IPV4_ADDR|IPV6_ADDR|INTERFACE_NAME|PEER_GROUP_NAME] + + This is a BGP specific match command. Matches the source peer if the neighbor + was specified in this manner. Useful to announce the routes that was originated + by the source peer. + .. clicmd:: match peer IPV4_ADDR This is a BGP specific match command. Matches the peer ip address @@ -305,22 +311,27 @@ Route Map Set Command Set the route's weight. -.. clicmd:: set metric <[+|-](1-4294967295)|rtt|+rtt|-rtt> +.. clicmd:: set metric <[+|-](1-4294967295)|rtt|+rtt|-rtt|igp|aigp> Set the route metric. When used with BGP, set the BGP attribute MED to a specific value. Use `+`/`-` to add or subtract the specified value to/from the existing/MED. Use `rtt` to set the MED to the round trip time or `+rtt`/`-rtt` to add/subtract the round trip time to/from the MED. + If ``igp`` is specified, then the actual value from the IGP protocol is used. + + If ``aigp`` is specified, then the actual value from the AIGP metric is used + (encoded as MED instead of AIGP attribute). + .. clicmd:: set min-metric <(0-4294967295)> - Set the minimum meric for the route. + Set the minimum metric for the route. .. clicmd:: set max-metric <(0-4294967295)> - Set the maximum meric for the route. + Set the maximum metric for the route. -.. clicmd:: set aigp-metric +.. clicmd:: set aigp-metric Set the BGP attribute AIGP to a specific value. If ``igp-metric`` is specified, then the value is taken from the IGP protocol, otherwise an arbitrary value. diff --git a/doc/user/vrrp.rst b/doc/user/vrrp.rst index d99fc23ef5be..cad850e7acbd 100644 --- a/doc/user/vrrp.rst +++ b/doc/user/vrrp.rst @@ -519,6 +519,7 @@ Check: - Do you have unusual ``sysctls`` enabled that could affect the operation of multicast traffic? - Are you running in ESXi? See below. +- Are you running in a linux VM with a bridged network? See below. My master router is not forwarding traffic @@ -552,6 +553,24 @@ feature instead, explained `here Issue reference: https://github.com/FRRouting/frr/issues/5386 +My router is running in a linux VM with a bridged host network and VRRP has issues +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Issues can arise with VRRP (especially IPv6) when you have a VM running on top +of a linux host, where your physical network is in a bridge, and the VM +has an interface attached to the bridge. By default, the linux bridge will +snoop multicast traffic, and you will likely see sporadic VRRP advertisements failing +to be received. IPv6 traffic was be particularly affected. + +This was observed on a VM running on proxmox, and the solution was to disable +multicast snooping on the bridge: + +.. code-block:: console + + echo 0 > /sys/devices/virtual/net/vmbr0/bridge/multicast_snooping + +Issue reference: https://github.com/FRRouting/frr/issues/5386 + My router cannot interoperate with branded routers / L3 switches ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/fpm/fpm.h b/fpm/fpm.h index 70c0df57157e..9003c643b00c 100644 --- a/fpm/fpm.h +++ b/fpm/fpm.h @@ -65,7 +65,7 @@ /* * Largest message that can be sent to or received from the FPM. */ -#define FPM_MAX_MSG_LEN 4096 +#define FPM_MAX_MSG_LEN MAX(MULTIPATH_NUM * 32, 4096) #ifdef __SUNPRO_C #pragma pack(1) diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index d588af314c11..28b987df3823 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -119,6 +119,10 @@ static void lsp_destroy(struct isis_lsp *lsp) lsp_clear_data(lsp); if (!LSP_FRAGMENT(lsp->hdr.lsp_id)) { + /* Only non-pseudo nodes and non-fragment LSPs can delete nodes. */ + if (!LSP_PSEUDO_ID(lsp->hdr.lsp_id)) + isis_dynhn_remove(lsp->area->isis, lsp->hdr.lsp_id); + if (lsp->lspu.frags) { lsp_remove_frags(&lsp->area->lspdb[lsp->level - 1], lsp->lspu.frags); @@ -2209,10 +2213,6 @@ void lsp_tick(struct event *thread) &area->lspdb[level], next); - if (!LSP_PSEUDO_ID(lsp->hdr.lsp_id)) - isis_dynhn_remove(area->isis, - lsp->hdr.lsp_id); - lspdb_del(&area->lspdb[level], lsp); lsp_destroy(lsp); lsp = NULL; diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index 8fc0f144b203..9c32a8447c97 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/isisd/isis_vty_fabricd.c b/isisd/isis_vty_fabricd.c index 0d25f6610934..85ef2c40a10a 100644 --- a/isisd/isis_vty_fabricd.c +++ b/isisd/isis_vty_fabricd.c @@ -218,17 +218,9 @@ DEFUN (ip_router_isis, if (!area) isis_area_create(area_tag, VRF_DEFAULT_NAME); - if (!circuit) { + if (!circuit) circuit = isis_circuit_new(ifp, area_tag); - if (circuit->state != C_STATE_CONF - && circuit->state != C_STATE_UP) { - vty_out(vty, - "Couldn't bring up interface, please check log.\n"); - return CMD_WARNING_CONFIG_FAILED; - } - } - bool ip = circuit->ip_router, ipv6 = circuit->ipv6_router; if (af[2] != '\0') ipv6 = true; diff --git a/ldpd/ldpe.c b/ldpd/ldpe.c index 6e844c0aa18d..0a3a03bc38de 100644 --- a/ldpd/ldpe.c +++ b/ldpd/ldpe.c @@ -459,6 +459,8 @@ static void ldpe_dispatch_main(struct event *thread) tnbr_update_all(AF_UNSPEC); break; case IMSG_RECONF_CONF: + if (nconf) + ldp_clear_config(nconf); if ((nconf = malloc(sizeof(struct ldpd_conf))) == NULL) fatal(NULL); memcpy(nconf, imsg.data, sizeof(struct ldpd_conf)); diff --git a/lib/command.h b/lib/command.h index f369a35243f6..c60751789f66 100644 --- a/lib/command.h +++ b/lib/command.h @@ -84,6 +84,7 @@ enum node_type { CONFIG_NODE, /* Config node. Default mode of config file. */ PREFIX_NODE, /* ip prefix-list node. */ PREFIX_IPV6_NODE, /* ipv6 prefix-list node. */ + RMAP_NODE, /* Route map node. */ LIB_DEBUG_NODE, /* frrlib debug node. */ DEBUG_NODE, /* Debug node. */ VRF_DEBUG_NODE, /* Vrf Debug node. */ @@ -136,7 +137,6 @@ enum node_type { AS_LIST_NODE, /* AS list node. */ COMMUNITY_LIST_NODE, /* Community list node. */ COMMUNITY_ALIAS_NODE, /* Community alias node. */ - RMAP_NODE, /* Route map node. */ PBRMAP_NODE, /* PBR map node. */ SMUX_NODE, /* SNMP configuration node. */ DUMP_NODE, /* Packet dump node. */ diff --git a/lib/defun_lex.l b/lib/defun_lex.l index 3104e48063b5..9528e448521a 100644 --- a/lib/defun_lex.l +++ b/lib/defun_lex.l @@ -157,6 +157,9 @@ SPECIAL [(),] %% +#else +extern int def_yylex(void); +extern int def_yylex_destroy(void); #endif /* __clang_analyzer__ */ static int yylex_clr(char **retbuf) diff --git a/lib/event.c b/lib/event.c index d925d0d5f0fc..c573923f5191 100644 --- a/lib/event.c +++ b/lib/event.c @@ -979,7 +979,7 @@ void _event_add_read_write(const struct xref_eventsched *xref, * if we already have a pollfd for our file descriptor, find and * use it */ - for (nfds_t i = 0; i < m->handler.pfdcount; i++) + for (nfds_t i = 0; i < m->handler.pfdcount; i++) { if (m->handler.pfds[i].fd == fd) { queuepos = i; @@ -993,6 +993,15 @@ void _event_add_read_write(const struct xref_eventsched *xref, #endif break; } + /* + * We are setting the fd = -1 for the + * case when a read/write event is going + * away. if we find a -1 we can stuff it + * into that spot, so note it + */ + if (m->handler.pfds[i].fd == -1 && queuepos == m->handler.pfdcount) + queuepos = i; + } /* make sure we have room for this fd + pipe poker fd */ assert(queuepos + 1 < m->handler.pfdsize); @@ -1269,6 +1278,14 @@ static void cancel_arg_helper(struct event_loop *master, for (i = 0; i < master->handler.pfdcount;) { pfd = master->handler.pfds + i; + /* + * Skip this spot, nothing here to see + */ + if (pfd->fd == -1) { + i++; + continue; + } + if (pfd->events & POLLIN) t = master->read[pfd->fd]; else @@ -1590,6 +1607,12 @@ static int thread_process_io_helper(struct event_loop *m, struct event *thread, * we should. */ m->handler.pfds[pos].events &= ~(state); + /* + * ppoll man page says that a fd of -1 causes the particular + * array item to be skipped. So let's skip it + */ + if (m->handler.pfds[pos].events == 0) + m->handler.pfds[pos].fd = -1; if (!thread) { if ((actual_state & (POLLHUP|POLLIN)) != POLLHUP) diff --git a/lib/libfrr.c b/lib/libfrr.c index f2247a48e555..d1a9f0b1cb69 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) diff --git a/lib/log.c b/lib/log.c index 04b789b5da5d..2b049cebe41e 100644 --- a/lib/log.c +++ b/lib/log.c @@ -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 ac39516edd75..9da46bdb8995 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 8e8c61da04d0..865801883295 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/netns_linux.c b/lib/netns_linux.c index 8fa4bc6fe0f7..82162abfd4ba 100644 --- a/lib/netns_linux.c +++ b/lib/netns_linux.c @@ -258,7 +258,7 @@ static void ns_disable_internal(struct ns *ns) if (ns_master.ns_disable_hook) (*ns_master.ns_disable_hook)(ns); - if (have_netns()) + if (have_netns() && ns->fd >= 0) close(ns->fd); ns->fd = -1; diff --git a/lib/ntop.c b/lib/ntop.c index 89b4d5ecdcab..edf03cd07680 100644 --- a/lib/ntop.c +++ b/lib/ntop.c @@ -116,7 +116,18 @@ const char *frr_inet_ntop(int af, const void * restrict src, best = i - curlen; bestlen = curlen; } - /* do we want ::ffff:A.B.C.D? */ + if (best == 0 && bestlen == 5 && b[10] == 0xff && b[11] == 0xff) { + /* ::ffff:A.B.C.D */ + *o++ = ':'; + *o++ = ':'; + *o++ = 'f'; + *o++ = 'f'; + *o++ = 'f'; + *o++ = 'f'; + *o++ = ':'; + b += 12; + goto inet4; + } if (best == 0 && bestlen == 6) { *o++ = ':'; *o++ = ':'; diff --git a/lib/ptm_lib.c b/lib/ptm_lib.c index ac800be0a5ef..737c60775f64 100644 --- a/lib/ptm_lib.c +++ b/lib/ptm_lib.c @@ -308,22 +308,18 @@ static int _ptm_lib_read_ptm_socket(int fd, char *buf, int len) while (bytes_read != len) { rc = recv(fd, (void *)(buf + bytes_read), (len - bytes_read), MSG_DONTWAIT); - if (rc <= 0) { - if (errno && (errno != EAGAIN) - && (errno != EWOULDBLOCK)) { - ERRLOG("fatal recv error(%s), closing connection, rc %d\n", - strerror(errno), rc); - return (rc); - } else { - if (retries++ < 2) { - usleep(10000); - continue; - } - DLOG("max retries - recv error(%d - %s) bytes read %d (%d)\n", - errno, strerror(errno), bytes_read, len); - return (bytes_read); + if (rc < 0 && (errno != EAGAIN) && (errno != EWOULDBLOCK)) { + ERRLOG("fatal recv error(%s), closing connection, rc %d\n", strerror(errno), + rc); + return (rc); + } else if (rc <= 0) { + if (retries++ < 2) { + usleep(10000); + continue; } - break; + DLOG("max retries - recv error(%d - %s) bytes read %d (%d)\n", errno, + strerror(errno), bytes_read, len); + return (bytes_read); } else { bytes_read += rc; } diff --git a/lib/routemap.h b/lib/routemap.h index dfb84ced5bae..ef9b3cb160c7 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -278,6 +278,7 @@ DECLARE_QOBJ_TYPE(route_map); #define IS_MATCH_SRC_VRF(C) \ (strmatch(C, "frr-bgp-route-map:source-vrf")) #define IS_MATCH_PEER(C) (strmatch(C, "frr-bgp-route-map:peer")) +#define IS_MATCH_SRC_PEER(C) (strmatch(C, "frr-bgp-route-map:src-peer")) #define IS_MATCH_AS_LIST(C) \ (strmatch(C, "frr-bgp-route-map:as-path-list")) #define IS_MATCH_MAC_LIST(C) \ diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c index f22d5880807c..69b942064b5d 100644 --- a/lib/routemap_cli.c +++ b/lib/routemap_cli.c @@ -417,6 +417,74 @@ DEFPY_YANG( return nb_cli_apply_changes(vty, NULL); } +DEFUN_YANG (match_ipv6_next_hop, + match_ipv6_next_hop_cmd, + "match ipv6 next-hop ACCESSLIST6_NAME", + MATCH_STR + IPV6_STR + "Match IPv6 next-hop address of route\n" + "IPv6 access-list name\n") +{ + const char *xpath = "./match-condition[condition='frr-route-map:ipv6-next-hop-list']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), "%s/rmap-match-condition/list-name", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[argc - 1]->arg); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFUN_YANG (no_match_ipv6_next_hop, + no_match_ipv6_next_hop_cmd, + "no match ipv6 next-hop [ACCESSLIST6_NAME]", + NO_STR + MATCH_STR + IPV6_STR + "Match IPv6 next-hop address of route\n" + "IPv6 access-list name\n") +{ + const char *xpath = "./match-condition[condition='frr-route-map:ipv6-next-hop-list']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +DEFUN_YANG (match_ipv6_next_hop_prefix_list, + match_ipv6_next_hop_prefix_list_cmd, + "match ipv6 next-hop prefix-list PREFIXLIST_NAME", + MATCH_STR + IPV6_STR + "Match IPv6 next-hop address of route\n" + "Match entries by prefix-list\n" + "IPv6 prefix-list name\n") +{ + const char *xpath = "./match-condition[condition='frr-route-map:ipv6-next-hop-prefix-list']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), "%s/rmap-match-condition/list-name", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[argc - 1]->arg); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFUN_YANG (no_match_ipv6_next_hop_prefix_list, + no_match_ipv6_next_hop_prefix_list_cmd, + "no match ipv6 next-hop prefix-list [PREFIXLIST_NAME]", + NO_STR + MATCH_STR + IPV6_STR + "Match IPv6 next-hop address of route\n" + "Match entries by prefix-list\n" + "IPv6 prefix-list name\n") +{ + const char *xpath = "./match-condition[condition='frr-route-map:ipv6-next-hop-prefix-list']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + DEFPY_YANG( match_ipv6_next_hop_type, match_ipv6_next_hop_type_cmd, "match ipv6 next-hop type $type", @@ -688,6 +756,18 @@ void route_map_condition_show(struct vty *vty, const struct lyd_node *dnode, acl = "local"; vty_out(vty, " match peer %s\n", acl); + } else if (IS_MATCH_SRC_PEER(condition)) { + acl = NULL; + ln = yang_dnode_get(dnode, + "./rmap-match-condition/frr-bgp-route-map:src-peer-ipv4-address"); + if (!ln) + ln = yang_dnode_get(dnode, + "./rmap-match-condition/frr-bgp-route-map:src-peer-ipv6-address"); + if (!ln) + ln = yang_dnode_get(dnode, + "./rmap-match-condition/frr-bgp-route-map:src-peer-interface"); + acl = yang_dnode_get_string(ln, NULL); + vty_out(vty, " match src-peer %s\n", acl); } else if (IS_MATCH_AS_LIST(condition)) { vty_out(vty, " match as-path %s\n", yang_dnode_get_string( @@ -854,13 +934,15 @@ DEFPY_YANG( DEFPY_YANG( set_metric, set_metric_cmd, - "set metric <(-4294967295-4294967295)$metric|rtt$rtt|+rtt$artt|-rtt$srtt>", + "set metric <(-4294967295-4294967295)$metric|rtt$rtt|+rtt$artt|-rtt$srtt|igp$igp|aigp$aigp>", SET_STR "Metric value for destination routing protocol\n" "Metric value (use +/- for additions or subtractions)\n" "Assign round trip time\n" "Add round trip time\n" - "Subtract round trip time\n") + "Subtract round trip time\n" + "Metric value from IGP protocol\n" + "Metric value from AIGP (Accumulated IGP)\n") { const char *xpath = "./set-action[action='frr-route-map:set-metric']"; char xpath_value[XPATH_MAXLEN]; @@ -871,6 +953,12 @@ DEFPY_YANG( snprintf(xpath_value, sizeof(xpath_value), "%s/rmap-set-action/use-round-trip-time", xpath); snprintf(value, sizeof(value), "true"); + } else if (igp) { + snprintf(xpath_value, sizeof(xpath_value), "%s/rmap-set-action/use-igp", xpath); + snprintf(value, sizeof(value), "true"); + } else if (aigp) { + snprintf(xpath_value, sizeof(xpath_value), "%s/rmap-set-action/use-aigp", xpath); + snprintf(value, sizeof(value), "true"); } else if (artt) { snprintf(xpath_value, sizeof(xpath_value), "%s/rmap-set-action/add-round-trip-time", xpath); @@ -1080,23 +1168,19 @@ void route_map_action_show(struct vty *vty, const struct lyd_node *dnode, if (yang_dnode_get(dnode, "./rmap-set-action/use-round-trip-time")) { vty_out(vty, " set metric rtt\n"); - } else if (yang_dnode_get( - dnode, - "./rmap-set-action/add-round-trip-time")) { + } else if (yang_dnode_get(dnode, "./rmap-set-action/use-igp")) { + vty_out(vty, " set metric igp\n"); + } else if (yang_dnode_get(dnode, "./rmap-set-action/use-aigp")) { + vty_out(vty, " set metric aigp\n"); + } else if (yang_dnode_get(dnode, "./rmap-set-action/add-round-trip-time")) { vty_out(vty, " set metric +rtt\n"); - } else if ( - yang_dnode_get( - dnode, - "./rmap-set-action/subtract-round-trip-time")) { + } else if (yang_dnode_get(dnode, "./rmap-set-action/subtract-round-trip-time")) { vty_out(vty, " set metric -rtt\n"); - } else if (yang_dnode_get(dnode, - "./rmap-set-action/add-metric")) { + } else if (yang_dnode_get(dnode, "./rmap-set-action/add-metric")) { vty_out(vty, " set metric +%s\n", yang_dnode_get_string( dnode, "./rmap-set-action/add-metric")); - } else if (yang_dnode_get( - dnode, - "./rmap-set-action/subtract-metric")) { + } else if (yang_dnode_get(dnode, "./rmap-set-action/subtract-metric")) { vty_out(vty, " set metric -%s\n", yang_dnode_get_string( dnode, @@ -1665,6 +1749,11 @@ void route_map_cli_init(void) install_element(RMAP_NODE, &match_ipv6_next_hop_type_cmd); install_element(RMAP_NODE, &no_match_ipv6_next_hop_type_cmd); + install_element(RMAP_NODE, &match_ipv6_next_hop_cmd); + install_element(RMAP_NODE, &match_ipv6_next_hop_prefix_list_cmd); + install_element(RMAP_NODE, &no_match_ipv6_next_hop_cmd); + install_element(RMAP_NODE, &no_match_ipv6_next_hop_prefix_list_cmd); + install_element(RMAP_NODE, &match_metric_cmd); install_element(RMAP_NODE, &no_match_metric_cmd); diff --git a/lib/routemap_northbound.c b/lib/routemap_northbound.c index 1bba4dad47a6..0ee055e65304 100644 --- a/lib/routemap_northbound.c +++ b/lib/routemap_northbound.c @@ -1213,6 +1213,34 @@ static int lib_route_map_entry_set_action_use_round_trip_time_destroy( return lib_route_map_entry_set_action_value_destroy(args); } +/* + * XPath: /frr-route-map:lib/route-map/entry/set-action/use-igp + */ +static int lib_route_map_entry_set_action_use_igp_modify(struct nb_cb_modify_args *args) +{ + return set_action_modify(args->event, args->dnode, args->resource, "igp", args->errmsg, + args->errmsg_len); +} + +static int lib_route_map_entry_set_action_use_igp_destroy(struct nb_cb_destroy_args *args) +{ + return lib_route_map_entry_set_action_value_destroy(args); +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/set-action/use-aigp + */ +static int lib_route_map_entry_set_action_use_aigp_modify(struct nb_cb_modify_args *args) +{ + return set_action_modify(args->event, args->dnode, args->resource, "aigp", args->errmsg, + args->errmsg_len); +} + +static int lib_route_map_entry_set_action_use_aigp_destroy(struct nb_cb_destroy_args *args) +{ + return lib_route_map_entry_set_action_value_destroy(args); +} + /* * XPath: /frr-route-map:lib/route-map/entry/set-action/add-round-trip-time */ @@ -1516,6 +1544,20 @@ const struct frr_yang_module_info frr_route_map_info = { .destroy = lib_route_map_entry_set_action_use_round_trip_time_destroy, } }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/use-igp", + .cbs = { + .modify = lib_route_map_entry_set_action_use_igp_modify, + .destroy = lib_route_map_entry_set_action_use_igp_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/use-aigp", + .cbs = { + .modify = lib_route_map_entry_set_action_use_aigp_modify, + .destroy = lib_route_map_entry_set_action_use_aigp_destroy, + } + }, { .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/add-round-trip-time", .cbs = { diff --git a/lib/sigevent.c b/lib/sigevent.c index 3e69f280da2e..7c465bfcec09 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/lib/zclient.c b/lib/zclient.c index 0e832f0d8fe5..557d9c3eb9b4 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -2374,7 +2374,7 @@ static bool zapi_nexthop_update_decode(struct stream *s, struct prefix *match, STREAM_GETW(s, nhr->instance); STREAM_GETC(s, nhr->distance); STREAM_GETL(s, nhr->metric); - STREAM_GETC(s, nhr->nexthop_num); + STREAM_GETW(s, nhr->nexthop_num); for (i = 0; i < nhr->nexthop_num; i++) { if (zapi_nexthop_decode(s, &(nhr->nexthops[i]), 0, 0) != 0) diff --git a/lib/zclient.h b/lib/zclient.h index 91c0c9ed6d3d..6da9558aa560 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -251,21 +251,16 @@ enum zebra_error_types { static inline const char *zebra_error_type2str(enum zebra_error_types type) { - const char *ret = "UNKNOWN"; - switch (type) { case ZEBRA_UNKNOWN_ERROR: - ret = "ZEBRA_UNKNOWN_ERROR"; - break; + return "ZEBRA_UNKNOWN_ERROR"; case ZEBRA_NO_VRF: - ret = "ZEBRA_NO_VRF"; - break; + return "ZEBRA_NO_VRF"; case ZEBRA_INVALID_MSG_TYPE: - ret = "ZEBRA_INVALID_MSG_TYPE"; - break; + return "ZEBRA_INVALID_MSG_TYPE"; } - return ret; + return "UNKNOWN"; } struct redist_proto { @@ -780,69 +775,51 @@ enum zclient_send_status { static inline const char * zapi_nhg_notify_owner2str(enum zapi_nhg_notify_owner note) { - const char *ret = "UNKNOWN"; - switch (note) { case ZAPI_NHG_FAIL_INSTALL: - ret = "ZAPI_NHG_FAIL_INSTALL"; - break; + return "ZAPI_NHG_FAIL_INSTALL"; case ZAPI_NHG_INSTALLED: - ret = "ZAPI_NHG_INSTALLED"; - break; + return "ZAPI_NHG_INSTALLED"; case ZAPI_NHG_REMOVE_FAIL: - ret = "ZAPI_NHG_REMOVE_FAIL"; - break; + return "ZAPI_NHG_REMOVE_FAIL"; case ZAPI_NHG_REMOVED: - ret = "ZAPI_NHG_REMOVED"; - break; + return "ZAPI_NHG_REMOVED"; } - return ret; + return "UNKNOWN"; } static inline const char * zapi_rule_notify_owner2str(enum zapi_rule_notify_owner note) { - const char *ret = "UNKNOWN"; - switch (note) { case ZAPI_RULE_FAIL_INSTALL: - ret = "ZAPI_RULE_FAIL_INSTALL"; - break; + return "ZAPI_RULE_FAIL_INSTALL"; case ZAPI_RULE_INSTALLED: - ret = "ZAPI_RULE_INSTALLED"; - break; + return "ZAPI_RULE_INSTALLED"; case ZAPI_RULE_FAIL_REMOVE: - ret = "ZAPI_RULE_FAIL_REMOVE"; - break; + return "ZAPI_RULE_FAIL_REMOVE"; case ZAPI_RULE_REMOVED: - ret = "ZAPI_RULE_REMOVED"; - break; + return "ZAPI_RULE_REMOVED"; } - return ret; + return "UNKNOWN"; } static inline const char *zapi_srv6_sid_notify2str(enum zapi_srv6_sid_notify note) { - const char *ret = "UNKNOWN"; - switch (note) { case ZAPI_SRV6_SID_FAIL_ALLOC: - ret = "ZAPI_SRV6_SID_FAIL_ALLOC"; - break; + return "ZAPI_SRV6_SID_FAIL_ALLOC"; case ZAPI_SRV6_SID_ALLOCATED: - ret = "ZAPI_SRV6_SID_ALLOCATED"; - break; + return "ZAPI_SRV6_SID_ALLOCATED"; case ZAPI_SRV6_SID_FAIL_RELEASE: - ret = "ZAPI_SRV6_SID_FAIL_RELEASE"; - break; + return "ZAPI_SRV6_SID_FAIL_RELEASE"; case ZAPI_SRV6_SID_RELEASED: - ret = "ZAPI_SRV6_SID_RELEASED"; - break; + return "ZAPI_SRV6_SID_RELEASED"; } - return ret; + return "UNKNOWN"; } /* Zebra MAC types */ diff --git a/lib/zlog_5424_cli.c b/lib/zlog_5424_cli.c index 3003df542f58..f97c42646371 100644 --- a/lib/zlog_5424_cli.c +++ b/lib/zlog_5424_cli.c @@ -674,6 +674,7 @@ static int log_5424_config_write(struct vty *vty) vty_out(vty, "log extended %s\n", cfg->name); + (void)fmt_str; /* clang-SA */ switch (cfg->cfg.fmt) { case ZLOG_FMT_5424: fmt_str = " format rfc5424"; diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index f125fa93b1e4..a8b8de6d5ba0 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -2613,8 +2613,7 @@ void ospf_external_lsa_refresh_default(struct ospf *ospf) } } -void ospf_external_lsa_refresh_type(struct ospf *ospf, uint8_t type, - unsigned short instance, int force) +void ospf_external_lsa_refresh_type(struct ospf *ospf, uint8_t type, uint8_t instance, int force) { struct route_node *rn; struct external_info *ei; @@ -3170,9 +3169,9 @@ int ospf_check_nbr_status(struct ospf *ospf) } -void ospf_maxage_lsa_remover(struct event *thread) +void ospf_maxage_lsa_remover(struct event *event) { - struct ospf *ospf = EVENT_ARG(thread); + struct ospf *ospf = EVENT_ARG(event); struct ospf_lsa *lsa, *old; struct route_node *rn; int reschedule = 0; @@ -3202,7 +3201,7 @@ void ospf_maxage_lsa_remover(struct event *thread) } /* TODO: maybe convert this function to a work-queue */ - if (event_should_yield(thread)) { + if (event_should_yield(event)) { OSPF_TIMER_ON(ospf->t_maxage, ospf_maxage_lsa_remover, 0); route_unlock_node( @@ -3418,9 +3417,9 @@ static int ospf_lsa_maxage_walker_remover(struct ospf *ospf, } /* Periodical check of MaxAge LSA. */ -void ospf_lsa_maxage_walker(struct event *thread) +void ospf_lsa_maxage_walker(struct event *event) { - struct ospf *ospf = EVENT_ARG(thread); + struct ospf *ospf = EVENT_ARG(event); struct route_node *rn; struct ospf_lsa *lsa; struct ospf_area *area; @@ -4157,11 +4156,11 @@ void ospf_refresher_unregister_lsa(struct ospf *ospf, struct ospf_lsa *lsa) } } -void ospf_lsa_refresh_walker(struct event *t) +void ospf_lsa_refresh_walker(struct event *e) { struct list *refresh_list; struct listnode *node, *nnode; - struct ospf *ospf = EVENT_ARG(t); + struct ospf *ospf = EVENT_ARG(e); struct ospf_lsa *lsa; int i; struct list *lsa_to_refresh = list_new(); diff --git a/ospfd/ospf_lsa.h b/ospfd/ospf_lsa.h index d5ca0694ccb2..c751f081fb92 100644 --- a/ospfd/ospf_lsa.h +++ b/ospfd/ospf_lsa.h @@ -225,122 +225,111 @@ enum lsid_status { LSID_AVAILABLE = 0, LSID_CHANGE, LSID_NOT_AVAILABLE }; /* Prototypes. */ /* XXX: Eek, time functions, similar are in lib/thread.c */ extern struct timeval int2tv(int); -extern struct timeval msec2tv(int); +extern struct timeval msec2tv(int a); -extern int get_age(struct ospf_lsa *); -extern uint16_t ospf_lsa_checksum(struct lsa_header *); -extern int ospf_lsa_checksum_valid(struct lsa_header *); -extern int ospf_lsa_refresh_delay(struct ospf_lsa *); +extern int get_age(struct ospf_lsa *lsa); +extern uint16_t ospf_lsa_checksum(struct lsa_header *lsah); +extern int ospf_lsa_checksum_valid(struct lsa_header *lsah); +extern int ospf_lsa_refresh_delay(struct ospf_lsa *lsa); -extern const char *dump_lsa_key(struct ospf_lsa *); -extern uint32_t lsa_seqnum_increment(struct ospf_lsa *); -extern void lsa_header_set(struct stream *, uint8_t, uint8_t, struct in_addr, - struct in_addr); -extern struct ospf_neighbor *ospf_nbr_lookup_ptop(struct ospf_interface *); -extern int ospf_check_nbr_status(struct ospf *); +extern const char *dump_lsa_key(struct ospf_lsa *lsa); +extern uint32_t lsa_seqnum_increment(struct ospf_lsa *lsa); +extern void lsa_header_set(struct stream *s, uint8_t options, uint8_t type, struct in_addr id, + struct in_addr router_id); +extern struct ospf_neighbor *ospf_nbr_lookup_ptop(struct ospf_interface *oi); +extern int ospf_check_nbr_status(struct ospf *ospf); /* Prototype for LSA primitive. */ extern struct ospf_lsa *ospf_lsa_new(void); extern struct ospf_lsa *ospf_lsa_new_and_data(size_t size); -extern struct ospf_lsa *ospf_lsa_dup(struct ospf_lsa *); -extern void ospf_lsa_free(struct ospf_lsa *); -extern struct ospf_lsa *ospf_lsa_lock(struct ospf_lsa *); -extern void ospf_lsa_unlock(struct ospf_lsa **); -extern void ospf_lsa_discard(struct ospf_lsa *); -extern int ospf_lsa_flush_schedule(struct ospf *, struct ospf_lsa *); -extern struct lsa_header *ospf_lsa_data_new(size_t); -extern struct lsa_header *ospf_lsa_data_dup(struct lsa_header *); -extern void ospf_lsa_data_free(struct lsa_header *); +extern struct ospf_lsa *ospf_lsa_dup(struct ospf_lsa *lsa); +extern void ospf_lsa_free(struct ospf_lsa *lsa); +extern struct ospf_lsa *ospf_lsa_lock(struct ospf_lsa *lsa); +extern void ospf_lsa_unlock(struct ospf_lsa **lsa); +extern void ospf_lsa_discard(struct ospf_lsa *lsa); +extern int ospf_lsa_flush_schedule(struct ospf *ospf, struct ospf_lsa *lsa); +extern struct lsa_header *ospf_lsa_data_new(size_t size); +extern struct lsa_header *ospf_lsa_data_dup(struct lsa_header *lsah); +extern void ospf_lsa_data_free(struct lsa_header *lsah); /* Prototype for various LSAs */ extern void ospf_router_lsa_body_set(struct stream **s, struct ospf_area *area); extern uint8_t router_lsa_flags(struct ospf_area *area); -extern int ospf_router_lsa_update(struct ospf *); -extern int ospf_router_lsa_update_area(struct ospf_area *); +extern int ospf_router_lsa_update(struct ospf *ospf); +extern int ospf_router_lsa_update_area(struct ospf_area *area); -extern void ospf_network_lsa_update(struct ospf_interface *); +extern void ospf_network_lsa_update(struct ospf_interface *oi); -extern struct ospf_lsa * -ospf_summary_lsa_originate(struct prefix_ipv4 *, uint32_t, struct ospf_area *); -extern struct ospf_lsa *ospf_summary_asbr_lsa_originate(struct prefix_ipv4 *, - uint32_t, - struct ospf_area *); +extern struct ospf_lsa *ospf_summary_lsa_originate(struct prefix_ipv4 *p, uint32_t metric, + struct ospf_area *area); +extern struct ospf_lsa *ospf_summary_asbr_lsa_originate(struct prefix_ipv4 *p, uint32_t metric, + struct ospf_area *area); -extern struct ospf_lsa *ospf_lsa_install(struct ospf *, struct ospf_interface *, - struct ospf_lsa *); +extern struct ospf_lsa *ospf_lsa_install(struct ospf *ospf, struct ospf_interface *oi, + struct ospf_lsa *lsa); extern void ospf_nssa_lsa_flush(struct ospf *ospf, struct prefix_ipv4 *p); -extern void ospf_external_lsa_flush(struct ospf *, uint8_t, - struct prefix_ipv4 *, +extern void ospf_external_lsa_flush(struct ospf *ospf, uint8_t type, struct prefix_ipv4 *p, ifindex_t /* , struct in_addr nexthop */); -extern struct in_addr ospf_get_ip_from_ifp(struct ospf_interface *); +extern struct in_addr ospf_get_ip_from_ifp(struct ospf_interface *oi); -extern struct ospf_lsa *ospf_external_lsa_originate(struct ospf *, - struct external_info *); +extern struct ospf_lsa *ospf_external_lsa_originate(struct ospf *ospf, struct external_info *ei); extern struct ospf_lsa *ospf_nssa_lsa_originate(struct ospf_area *area, struct external_info *ei); extern struct ospf_lsa *ospf_nssa_lsa_refresh(struct ospf_area *area, struct ospf_lsa *lsa, struct external_info *ei); extern void ospf_external_lsa_rid_change(struct ospf *ospf); -extern struct ospf_lsa *ospf_lsa_lookup(struct ospf *ospf, struct ospf_area *, - uint32_t, struct in_addr, - struct in_addr); -extern struct ospf_lsa *ospf_lsa_lookup_by_id(struct ospf_area *, uint32_t, - struct in_addr); -extern struct ospf_lsa *ospf_lsa_lookup_by_header(struct ospf_area *, - struct lsa_header *); -extern int ospf_lsa_more_recent(struct ospf_lsa *, struct ospf_lsa *); -extern int ospf_lsa_different(struct ospf_lsa *, struct ospf_lsa *, - bool ignore_rcvd_flag); -extern void ospf_flush_self_originated_lsas_now(struct ospf *); - -extern int ospf_lsa_is_self_originated(struct ospf *, struct ospf_lsa *); - -extern struct ospf_lsa *ospf_lsa_lookup_by_prefix(struct ospf_lsdb *, uint8_t, - struct prefix_ipv4 *, - struct in_addr); - -extern void ospf_lsa_maxage(struct ospf *, struct ospf_lsa *); -extern uint32_t get_metric(uint8_t *); - -extern void ospf_lsa_maxage_walker(struct event *thread); -extern struct ospf_lsa *ospf_lsa_refresh(struct ospf *, struct ospf_lsa *); - -extern void ospf_external_lsa_refresh_default(struct ospf *); - -extern void ospf_external_lsa_refresh_type(struct ospf *, uint8_t, - unsigned short, int); -extern struct ospf_lsa *ospf_external_lsa_refresh(struct ospf *, - struct ospf_lsa *, - struct external_info *, int, - bool aggr); +extern struct ospf_lsa *ospf_lsa_lookup(struct ospf *ospf, struct ospf_area *area, uint32_t type, + struct in_addr id, struct in_addr adv_router); +extern struct ospf_lsa *ospf_lsa_lookup_by_id(struct ospf_area *area, uint32_t type, + struct in_addr id); +extern struct ospf_lsa *ospf_lsa_lookup_by_header(struct ospf_area *area, struct lsa_header *lsah); +extern int ospf_lsa_more_recent(struct ospf_lsa *l1, struct ospf_lsa *l2); +extern int ospf_lsa_different(struct ospf_lsa *l1, struct ospf_lsa *l2, bool ignore_rcvd_flag); +extern void ospf_flush_self_originated_lsas_now(struct ospf *ospf); + +extern int ospf_lsa_is_self_originated(struct ospf *ospf, struct ospf_lsa *lsa); + +extern struct ospf_lsa *ospf_lsa_lookup_by_prefix(struct ospf_lsdb *lsdb, uint8_t type, + struct prefix_ipv4 *p, struct in_addr router_id); + +extern void ospf_lsa_maxage(struct ospf *ospf, struct ospf_lsa *lsa); +extern uint32_t get_metric(uint8_t *metric); + +extern void ospf_lsa_maxage_walker(struct event *event); +extern struct ospf_lsa *ospf_lsa_refresh(struct ospf *ospf, struct ospf_lsa *lsa); + +extern void ospf_external_lsa_refresh_default(struct ospf *ospf); + +extern void ospf_external_lsa_refresh_type(struct ospf *ospf, uint8_t type, uint8_t instance, + int force); +extern struct ospf_lsa *ospf_external_lsa_refresh(struct ospf *ospf, struct ospf_lsa *lsa, + struct external_info *ei, int force, bool aggr); extern enum lsid_status ospf_lsa_unique_id(struct ospf *ospf, struct ospf_lsdb *lsdb, uint8_t type, struct prefix_ipv4 *p, struct in_addr *addr); -extern void ospf_schedule_lsa_flood_area(struct ospf_area *, struct ospf_lsa *); -extern void ospf_schedule_lsa_flush_area(struct ospf_area *, struct ospf_lsa *); +extern void ospf_schedule_lsa_flood_area(struct ospf_area *area, struct ospf_lsa *lsa); +extern void ospf_schedule_lsa_flush_area(struct ospf_area *area, struct ospf_lsa *lsa); -extern void ospf_refresher_register_lsa(struct ospf *, struct ospf_lsa *); -extern void ospf_refresher_unregister_lsa(struct ospf *, struct ospf_lsa *); -extern void ospf_lsa_refresh_walker(struct event *thread); +extern void ospf_refresher_register_lsa(struct ospf *ospf, struct ospf_lsa *lsa); +extern void ospf_refresher_unregister_lsa(struct ospf *ospf, struct ospf_lsa *lsa); +extern void ospf_lsa_refresh_walker(struct event *event); -extern void ospf_lsa_maxage_delete(struct ospf *, struct ospf_lsa *); +extern void ospf_lsa_maxage_delete(struct ospf *ospf, struct ospf_lsa *lsa); -extern void ospf_discard_from_db(struct ospf *, struct ospf_lsdb *, - struct ospf_lsa *); +extern void ospf_discard_from_db(struct ospf *ospf, struct ospf_lsdb *lsdb, struct ospf_lsa *lsa); -extern int metric_type(struct ospf *, uint8_t, unsigned short); -extern int metric_value(struct ospf *, uint8_t, unsigned short); +extern int metric_type(struct ospf *ospf, uint8_t src, unsigned short instance); +extern int metric_value(struct ospf *ospf, uint8_t src, unsigned short instance); extern char link_info_set(struct stream **s, struct in_addr id, struct in_addr data, uint8_t type, uint8_t tos, uint16_t cost); -extern struct in_addr ospf_get_nssa_ip(struct ospf_area *); -extern int ospf_translated_nssa_compare(struct ospf_lsa *, struct ospf_lsa *); +extern struct in_addr ospf_get_nssa_ip(struct ospf_area *area); extern struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf, struct ospf_lsa *type7, struct ospf_lsa *type5); @@ -351,7 +340,7 @@ extern void ospf_check_and_gen_init_seq_lsa(struct ospf_interface *oi, struct ospf_lsa *lsa); extern void ospf_flush_lsa_from_area(struct ospf *ospf, struct in_addr area_id, int type); -extern void ospf_maxage_lsa_remover(struct event *thread); +extern void ospf_maxage_lsa_remover(struct event *event); extern bool ospf_check_dna_lsa(const struct ospf_lsa *lsa); extern void ospf_refresh_area_self_lsas(struct ospf_area *area); diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index b7261da261ed..3be3de24cf38 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -718,7 +718,7 @@ DEFUN (ospf_area_range_not_advertise, DEFUN (no_ospf_area_range, no_ospf_area_range_cmd, - "no area range A.B.C.D/M []", + "no area range A.B.C.D/M []", NO_STR "OSPF area parameters\n" "OSPF area ID in IP address format\n" @@ -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; } @@ -781,6 +783,8 @@ DEFUN (no_ospf_area_range_substitute, ospf_area_range_substitute_unset(ospf, area, &p); + ospf_area_check_free(ospf, area_id); + return CMD_SUCCESS; } @@ -1353,12 +1357,13 @@ DEFUN (ospf_area_shortcut, DEFUN (no_ospf_area_shortcut, no_ospf_area_shortcut_cmd, - "no area shortcut ", + "no area shortcut ", NO_STR "OSPF area parameters\n" "OSPF area ID in IP address format\n" "OSPF area ID as a decimal value\n" "Deconfigure the area's shortcutting mode\n" + "Deconfigure default shortcutting through the area\n" "Deconfigure enabled shortcutting through the area\n" "Deconfigure disabled shortcutting through the area\n") { diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index 7638e979a207..9d7870d2f7ea 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -1730,6 +1730,8 @@ int ospf_area_nssa_unset(struct ospf *ospf, struct in_addr area_id) area->no_summary = 0; area->suppress_fa = 0; area->NSSATranslatorRole = OSPF_NSSA_ROLE_CANDIDATE; + if (area->NSSATranslatorState == OSPF_NSSA_TRANSLATE_ENABLED) + ospf_asbr_status_update(ospf, --ospf->redistribute); area->NSSATranslatorState = OSPF_NSSA_TRANSLATE_DISABLED; area->NSSATranslatorStabilityInterval = OSPF_NSSA_TRANS_STABLE_DEFAULT; ospf_area_type_set(area, OSPF_AREA_DEFAULT); diff --git a/pimd/pim6_mld.c b/pimd/pim6_mld.c index a871837701a4..59cd9aea5f53 100644 --- a/pimd/pim6_mld.c +++ b/pimd/pim6_mld.c @@ -468,6 +468,8 @@ static void gm_sg_update(struct gm_sg *sg, bool has_expired) static void gm_packet_free(struct gm_packet_state *pkt) { + assert(pkt->iface); + gm_packet_expires_del(pkt->iface->expires, pkt); gm_packets_del(pkt->subscriber->packets, pkt); gm_subscriber_drop(&pkt->subscriber); diff --git a/pimd/pim_autorp.c b/pimd/pim_autorp.c index 35347a2790c8..d36b792e394d 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")); @@ -308,7 +311,7 @@ static bool pim_autorp_discovery(struct pim_autorp *autorp, uint8_t rpcnt, struct autorp_pkt_grp *grp; size_t offset = 0; pim_addr rp_addr; - struct prefix grppfix; + struct prefix grppfix = {}; char plname[32]; struct prefix_list *pl; struct prefix_list_entry *ple; diff --git a/pimd/pim_bsr_rpdb.c b/pimd/pim_bsr_rpdb.c index 3ec9f99cd198..6e93b65f4b99 100644 --- a/pimd/pim_bsr_rpdb.c +++ b/pimd/pim_bsr_rpdb.c @@ -502,6 +502,9 @@ int pim_crp_process(struct interface *ifp, pim_sgaddr *src_dst, uint8_t *buf, buf += sizeof(*crp_hdr); remain -= sizeof(*crp_hdr); + /* ignore trailing data */ + (void)buf; + size_t ngroups = crp_hdr->prefix_cnt; if (remain < ngroups * sizeof(struct pim_encoded_group_ipv4)) { diff --git a/pimd/pim_zlookup.c b/pimd/pim_zlookup.c index c19119fa4788..5d344f1f66ba 100644 --- a/pimd/pim_zlookup.c +++ b/pimd/pim_zlookup.c @@ -193,7 +193,7 @@ static int zclient_read_nexthop(struct pim_instance *pim, distance = stream_getc(s); metric = stream_getl(s); - nexthop_num = stream_getc(s); + nexthop_num = stream_getw(s); if (nexthop_num < 1 || nexthop_num > router->multipath) { if (PIM_DEBUG_PIM_NHT_DETAIL) diff --git a/redhat/frr.logrotate b/redhat/frr.logrotate index 31061e3ae0fe..1224a332b46f 100644 --- a/redhat/frr.logrotate +++ b/redhat/frr.logrotate @@ -78,6 +78,14 @@ endscript } +/var/log/frr/mgmtd.log { + notifempty + missingok + postrotate + /bin/kill -USR1 `cat /var/run/frr/mgmtd.pid 2> /dev/null` 2> /dev/null || true + endscript +} + /var/log/frr/nhrpd.log { notifempty missingok diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in index d6775e6e9cae..b65ebf966c9f 100644 --- a/redhat/frr.spec.in +++ b/redhat/frr.spec.in @@ -834,7 +834,23 @@ sed -i 's/ -M rpki//' %{_sysconfdir}/frr/daemons %changelog -* Mon Mar 25 2024 Jafar Al-Gharaibeh - %{version} +* Thu Oct 10 2024 Jafar Al-Gharaibeh - %{version} + +* Thu Oct 10 2024 Jafar Al-Gharaibeh - 10.3-dev +- FRR 10.3 Development + +* Fri Jul 26 2024 Jafar Al-Gharaibeh - 10.1 +- Breaking changes +- Enable BGP dynamic capability by default for datacenter profile +- Split BGP `rpki cache` command into separate per SSH/TCP +- Add deprecation cycle for OSPF `router-info X [A.B.C.D]` command +- Major highlights: +- BGP dampening per-neighbor support +- BMP send-experimental stats +- Implement extended link-bandwidth for BGP +- Paths Limit for Multiple Paths in BGP +- New command for OSPFv2 `ip ospf neighbor-filter NAME [A.B.C.D]` +- Implement non-broadcast support for point-to-multipoint networks * Mon Mar 25 2024 Jafar Al-Gharaibeh - 10.0 - Major highlights: diff --git a/staticd/static_zebra.c b/staticd/static_zebra.c index 420ed7903bbc..d76befc1318f 100644 --- a/staticd/static_zebra.c +++ b/staticd/static_zebra.c @@ -43,7 +43,7 @@ struct static_nht_data { vrf_id_t nh_vrf_id; uint32_t refcount; - uint8_t nh_num; + uint16_t nh_num; bool registered; }; diff --git a/tests/isisd/test_fuzz_isis_tlv.c b/tests/isisd/test_fuzz_isis_tlv.c index 627ccfee6fa3..a3acd0786f61 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_fuzz_isis_tlv_tests.h.gz b/tests/isisd/test_fuzz_isis_tlv_tests.h.gz index 05e9f723a162..902b671b32c7 100644 Binary files a/tests/isisd/test_fuzz_isis_tlv_tests.h.gz and b/tests/isisd/test_fuzz_isis_tlv_tests.h.gz differ diff --git a/tests/isisd/test_isis_spf.c b/tests/isisd/test_isis_spf.c index e5a8f7a51392..9c1ce3d193b5 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 342a91cc79a7..5c23a712582d 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 fdc9e53ca3ea..3d700d8a19b1 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 070a30433573..3ce6683ae584 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 2cd9d47cb4d4..5cb518d81a87 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/bfd_isis_topo1/rt1/step1/show_ipv6_route.ref b/tests/topotests/bfd_isis_topo1/rt1/step1/show_ipv6_route.ref index 68d3fe2c44e0..a760f1c24936 100644 --- a/tests/topotests/bfd_isis_topo1/rt1/step1/show_ipv6_route.ref +++ b/tests/topotests/bfd_isis_topo1/rt1/step1/show_ipv6_route.ref @@ -1,7 +1,7 @@ { - "::ffff:202:202\/128":[ + "::ffff:2.2.2.2\/128":[ { - "prefix":"::ffff:202:202\/128", + "prefix":"::ffff:2.2.2.2\/128", "protocol":"isis", "selected":true, "destSelected":true, @@ -16,9 +16,9 @@ ] } ], - "::ffff:303:303\/128":[ + "::ffff:3.3.3.3\/128":[ { - "prefix":"::ffff:303:303\/128", + "prefix":"::ffff:3.3.3.3\/128", "protocol":"isis", "selected":true, "destSelected":true, @@ -33,9 +33,9 @@ ] } ], - "::ffff:404:404\/128":[ + "::ffff:4.4.4.4\/128":[ { - "prefix":"::ffff:404:404\/128", + "prefix":"::ffff:4.4.4.4\/128", "protocol":"isis", "selected":true, "destSelected":true, @@ -50,9 +50,9 @@ ] } ], - "::ffff:505:505\/128":[ + "::ffff:5.5.5.5\/128":[ { - "prefix":"::ffff:505:505\/128", + "prefix":"::ffff:5.5.5.5\/128", "protocol":"isis", "selected":true, "destSelected":true, diff --git a/tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_healthy.ref b/tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_healthy.ref index 68d3fe2c44e0..a760f1c24936 100644 --- a/tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_healthy.ref +++ b/tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_healthy.ref @@ -1,7 +1,7 @@ { - "::ffff:202:202\/128":[ + "::ffff:2.2.2.2\/128":[ { - "prefix":"::ffff:202:202\/128", + "prefix":"::ffff:2.2.2.2\/128", "protocol":"isis", "selected":true, "destSelected":true, @@ -16,9 +16,9 @@ ] } ], - "::ffff:303:303\/128":[ + "::ffff:3.3.3.3\/128":[ { - "prefix":"::ffff:303:303\/128", + "prefix":"::ffff:3.3.3.3\/128", "protocol":"isis", "selected":true, "destSelected":true, @@ -33,9 +33,9 @@ ] } ], - "::ffff:404:404\/128":[ + "::ffff:4.4.4.4\/128":[ { - "prefix":"::ffff:404:404\/128", + "prefix":"::ffff:4.4.4.4\/128", "protocol":"isis", "selected":true, "destSelected":true, @@ -50,9 +50,9 @@ ] } ], - "::ffff:505:505\/128":[ + "::ffff:5.5.5.5\/128":[ { - "prefix":"::ffff:505:505\/128", + "prefix":"::ffff:5.5.5.5\/128", "protocol":"isis", "selected":true, "destSelected":true, diff --git a/tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_rt2_down.ref b/tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_rt2_down.ref index 200053c3e830..116876545753 100644 --- a/tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_rt2_down.ref +++ b/tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_rt2_down.ref @@ -1,7 +1,7 @@ { - "::ffff:202:202\/128":[ + "::ffff:2.2.2.2\/128":[ { - "prefix":"::ffff:202:202\/128", + "prefix":"::ffff:2.2.2.2\/128", "protocol":"isis", "selected":true, "destSelected":true, @@ -16,9 +16,9 @@ ] } ], - "::ffff:303:303\/128":[ + "::ffff:3.3.3.3\/128":[ { - "prefix":"::ffff:303:303\/128", + "prefix":"::ffff:3.3.3.3\/128", "protocol":"isis", "selected":true, "destSelected":true, @@ -33,9 +33,9 @@ ] } ], - "::ffff:404:404\/128":[ + "::ffff:4.4.4.4\/128":[ { - "prefix":"::ffff:404:404\/128", + "prefix":"::ffff:4.4.4.4\/128", "protocol":"isis", "selected":true, "destSelected":true, @@ -50,9 +50,9 @@ ] } ], - "::ffff:505:505\/128":[ + "::ffff:5.5.5.5\/128":[ { - "prefix":"::ffff:505:505\/128", + "prefix":"::ffff:5.5.5.5\/128", "protocol":"isis", "selected":true, "destSelected":true, diff --git a/tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_rt3_down.ref b/tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_rt3_down.ref index 4297f163b568..3e8505e79baa 100644 --- a/tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_rt3_down.ref +++ b/tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_rt3_down.ref @@ -1,7 +1,7 @@ { - "::ffff:202:202\/128":[ + "::ffff:2.2.2.2\/128":[ { - "prefix":"::ffff:202:202\/128", + "prefix":"::ffff:2.2.2.2\/128", "protocol":"isis", "selected":true, "destSelected":true, @@ -16,9 +16,9 @@ ] } ], - "::ffff:303:303\/128":[ + "::ffff:3.3.3.3\/128":[ { - "prefix":"::ffff:303:303\/128", + "prefix":"::ffff:3.3.3.3\/128", "protocol":"isis", "selected":true, "destSelected":true, @@ -33,9 +33,9 @@ ] } ], - "::ffff:404:404\/128":[ + "::ffff:4.4.4.4\/128":[ { - "prefix":"::ffff:404:404\/128", + "prefix":"::ffff:4.4.4.4\/128", "protocol":"isis", "selected":true, "destSelected":true, @@ -50,9 +50,9 @@ ] } ], - "::ffff:505:505\/128":[ + "::ffff:5.5.5.5\/128":[ { - "prefix":"::ffff:505:505\/128", + "prefix":"::ffff:5.5.5.5\/128", "protocol":"isis", "selected":true, "destSelected":true, diff --git a/tests/topotests/bfd_isis_topo1/rt1/zebra.conf b/tests/topotests/bfd_isis_topo1/rt1/zebra.conf index 7e6f7881b4d5..bf2428e62562 100644 --- a/tests/topotests/bfd_isis_topo1/rt1/zebra.conf +++ b/tests/topotests/bfd_isis_topo1/rt1/zebra.conf @@ -10,7 +10,7 @@ hostname rt1 ! interface lo ip address 1.1.1.1/32 - ipv6 address ::ffff:0101:0101/128 + ipv6 address ::ffff:1.1.1.1/128 ! interface eth-rt2 ip address 10.0.1.1/24 diff --git a/tests/topotests/bfd_isis_topo1/rt2/zebra.conf b/tests/topotests/bfd_isis_topo1/rt2/zebra.conf index 5788e31f123a..1670c205064e 100644 --- a/tests/topotests/bfd_isis_topo1/rt2/zebra.conf +++ b/tests/topotests/bfd_isis_topo1/rt2/zebra.conf @@ -7,7 +7,7 @@ hostname rt2 ! interface lo ip address 2.2.2.2/32 - ipv6 address ::ffff:0202:0202/128 + ipv6 address ::ffff:2.2.2.2/128 ! interface eth-rt1 ip address 10.0.1.2/24 diff --git a/tests/topotests/bfd_isis_topo1/rt3/zebra.conf b/tests/topotests/bfd_isis_topo1/rt3/zebra.conf index 78eac2e15a60..d59085560f3e 100644 --- a/tests/topotests/bfd_isis_topo1/rt3/zebra.conf +++ b/tests/topotests/bfd_isis_topo1/rt3/zebra.conf @@ -7,7 +7,7 @@ hostname rt3 ! interface lo ip address 3.3.3.3/32 - ipv6 address ::ffff:0303:0303/128 + ipv6 address ::ffff:3.3.3.3/128 ! interface eth-rt1 ip address 10.0.2.2/24 diff --git a/tests/topotests/bfd_isis_topo1/rt4/zebra.conf b/tests/topotests/bfd_isis_topo1/rt4/zebra.conf index a6cb573ed878..66121662ddec 100644 --- a/tests/topotests/bfd_isis_topo1/rt4/zebra.conf +++ b/tests/topotests/bfd_isis_topo1/rt4/zebra.conf @@ -7,7 +7,7 @@ hostname rt4 ! interface lo ip address 4.4.4.4/32 - ipv6 address ::ffff:0404:0404/128 + ipv6 address ::ffff:4.4.4.4/128 ! interface eth-rt3 ip address 10.0.4.2/24 diff --git a/tests/topotests/bfd_isis_topo1/rt5/zebra.conf b/tests/topotests/bfd_isis_topo1/rt5/zebra.conf index 33473c91a3d1..d3331b5b6ca9 100644 --- a/tests/topotests/bfd_isis_topo1/rt5/zebra.conf +++ b/tests/topotests/bfd_isis_topo1/rt5/zebra.conf @@ -7,7 +7,7 @@ hostname rt5 ! interface lo ip address 5.5.5.5/32 - ipv6 address ::ffff:0505:0505/128 + ipv6 address ::ffff:5.5.5.5/128 ! interface eth-rt2 ip address 10.0.3.2/24 diff --git a/tests/topotests/bfd_ospf_topo1/rt1/step1/show_ipv6_route.ref b/tests/topotests/bfd_ospf_topo1/rt1/step1/show_ipv6_route.ref index 6465efb8b532..0526d8a6af5d 100644 --- a/tests/topotests/bfd_ospf_topo1/rt1/step1/show_ipv6_route.ref +++ b/tests/topotests/bfd_ospf_topo1/rt1/step1/show_ipv6_route.ref @@ -1,7 +1,7 @@ { - "::ffff:202:202\/128":[ + "::ffff:2.2.2.2\/128":[ { - "prefix":"::ffff:202:202\/128", + "prefix":"::ffff:2.2.2.2\/128", "protocol":"ospf6", "selected":true, "destSelected":true, @@ -16,9 +16,9 @@ ] } ], - "::ffff:303:303\/128":[ + "::ffff:3.3.3.3\/128":[ { - "prefix":"::ffff:303:303\/128", + "prefix":"::ffff:3.3.3.3\/128", "protocol":"ospf6", "selected":true, "destSelected":true, @@ -33,9 +33,9 @@ ] } ], - "::ffff:404:404\/128":[ + "::ffff:4.4.4.4\/128":[ { - "prefix":"::ffff:404:404\/128", + "prefix":"::ffff:4.4.4.4\/128", "protocol":"ospf6", "selected":true, "destSelected":true, @@ -50,9 +50,9 @@ ] } ], - "::ffff:505:505\/128":[ + "::ffff:5.5.5.5\/128":[ { - "prefix":"::ffff:505:505\/128", + "prefix":"::ffff:5.5.5.5\/128", "protocol":"ospf6", "selected":true, "destSelected":true, diff --git a/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_healthy.ref b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_healthy.ref index 6465efb8b532..0526d8a6af5d 100644 --- a/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_healthy.ref +++ b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_healthy.ref @@ -1,7 +1,7 @@ { - "::ffff:202:202\/128":[ + "::ffff:2.2.2.2\/128":[ { - "prefix":"::ffff:202:202\/128", + "prefix":"::ffff:2.2.2.2\/128", "protocol":"ospf6", "selected":true, "destSelected":true, @@ -16,9 +16,9 @@ ] } ], - "::ffff:303:303\/128":[ + "::ffff:3.3.3.3\/128":[ { - "prefix":"::ffff:303:303\/128", + "prefix":"::ffff:3.3.3.3\/128", "protocol":"ospf6", "selected":true, "destSelected":true, @@ -33,9 +33,9 @@ ] } ], - "::ffff:404:404\/128":[ + "::ffff:4.4.4.4\/128":[ { - "prefix":"::ffff:404:404\/128", + "prefix":"::ffff:4.4.4.4\/128", "protocol":"ospf6", "selected":true, "destSelected":true, @@ -50,9 +50,9 @@ ] } ], - "::ffff:505:505\/128":[ + "::ffff:5.5.5.5\/128":[ { - "prefix":"::ffff:505:505\/128", + "prefix":"::ffff:5.5.5.5\/128", "protocol":"ospf6", "selected":true, "destSelected":true, diff --git a/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_rt2_down.ref b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_rt2_down.ref index cfb1ef1bb6c1..8a8a0cd77d99 100644 --- a/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_rt2_down.ref +++ b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_rt2_down.ref @@ -1,7 +1,7 @@ { - "::ffff:202:202\/128":[ + "::ffff:2.2.2.2\/128":[ { - "prefix":"::ffff:202:202\/128", + "prefix":"::ffff:2.2.2.2\/128", "protocol":"ospf6", "selected":true, "destSelected":true, @@ -16,9 +16,9 @@ ] } ], - "::ffff:303:303\/128":[ + "::ffff:3.3.3.3\/128":[ { - "prefix":"::ffff:303:303\/128", + "prefix":"::ffff:3.3.3.3\/128", "protocol":"ospf6", "selected":true, "destSelected":true, @@ -33,9 +33,9 @@ ] } ], - "::ffff:404:404\/128":[ + "::ffff:4.4.4.4\/128":[ { - "prefix":"::ffff:404:404\/128", + "prefix":"::ffff:4.4.4.4\/128", "protocol":"ospf6", "selected":true, "destSelected":true, @@ -50,9 +50,9 @@ ] } ], - "::ffff:505:505\/128":[ + "::ffff:5.5.5.5\/128":[ { - "prefix":"::ffff:505:505\/128", + "prefix":"::ffff:5.5.5.5\/128", "protocol":"ospf6", "selected":true, "destSelected":true, diff --git a/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_rt3_down.ref b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_rt3_down.ref index 58b44da5c21b..e4f056f7b5b6 100644 --- a/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_rt3_down.ref +++ b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_rt3_down.ref @@ -1,7 +1,7 @@ { - "::ffff:202:202\/128":[ + "::ffff:2.2.2.2\/128":[ { - "prefix":"::ffff:202:202\/128", + "prefix":"::ffff:2.2.2.2\/128", "protocol":"ospf6", "selected":true, "destSelected":true, @@ -16,9 +16,9 @@ ] } ], - "::ffff:303:303\/128":[ + "::ffff:3.3.3.3\/128":[ { - "prefix":"::ffff:303:303\/128", + "prefix":"::ffff:3.3.3.3\/128", "protocol":"ospf6", "selected":true, "destSelected":true, @@ -33,9 +33,9 @@ ] } ], - "::ffff:404:404\/128":[ + "::ffff:4.4.4.4\/128":[ { - "prefix":"::ffff:404:404\/128", + "prefix":"::ffff:4.4.4.4\/128", "protocol":"ospf6", "selected":true, "destSelected":true, @@ -50,9 +50,9 @@ ] } ], - "::ffff:505:505\/128":[ + "::ffff:5.5.5.5\/128":[ { - "prefix":"::ffff:505:505\/128", + "prefix":"::ffff:5.5.5.5\/128", "protocol":"ospf6", "selected":true, "destSelected":true, diff --git a/tests/topotests/bfd_ospf_topo1/rt1/zebra.conf b/tests/topotests/bfd_ospf_topo1/rt1/zebra.conf index 7e6f7881b4d5..bf2428e62562 100644 --- a/tests/topotests/bfd_ospf_topo1/rt1/zebra.conf +++ b/tests/topotests/bfd_ospf_topo1/rt1/zebra.conf @@ -10,7 +10,7 @@ hostname rt1 ! interface lo ip address 1.1.1.1/32 - ipv6 address ::ffff:0101:0101/128 + ipv6 address ::ffff:1.1.1.1/128 ! interface eth-rt2 ip address 10.0.1.1/24 diff --git a/tests/topotests/bfd_ospf_topo1/rt2/zebra.conf b/tests/topotests/bfd_ospf_topo1/rt2/zebra.conf index 5788e31f123a..1670c205064e 100644 --- a/tests/topotests/bfd_ospf_topo1/rt2/zebra.conf +++ b/tests/topotests/bfd_ospf_topo1/rt2/zebra.conf @@ -7,7 +7,7 @@ hostname rt2 ! interface lo ip address 2.2.2.2/32 - ipv6 address ::ffff:0202:0202/128 + ipv6 address ::ffff:2.2.2.2/128 ! interface eth-rt1 ip address 10.0.1.2/24 diff --git a/tests/topotests/bfd_ospf_topo1/rt3/zebra.conf b/tests/topotests/bfd_ospf_topo1/rt3/zebra.conf index 78eac2e15a60..d59085560f3e 100644 --- a/tests/topotests/bfd_ospf_topo1/rt3/zebra.conf +++ b/tests/topotests/bfd_ospf_topo1/rt3/zebra.conf @@ -7,7 +7,7 @@ hostname rt3 ! interface lo ip address 3.3.3.3/32 - ipv6 address ::ffff:0303:0303/128 + ipv6 address ::ffff:3.3.3.3/128 ! interface eth-rt1 ip address 10.0.2.2/24 diff --git a/tests/topotests/bfd_ospf_topo1/rt4/zebra.conf b/tests/topotests/bfd_ospf_topo1/rt4/zebra.conf index a6cb573ed878..66121662ddec 100644 --- a/tests/topotests/bfd_ospf_topo1/rt4/zebra.conf +++ b/tests/topotests/bfd_ospf_topo1/rt4/zebra.conf @@ -7,7 +7,7 @@ hostname rt4 ! interface lo ip address 4.4.4.4/32 - ipv6 address ::ffff:0404:0404/128 + ipv6 address ::ffff:4.4.4.4/128 ! interface eth-rt3 ip address 10.0.4.2/24 diff --git a/tests/topotests/bfd_ospf_topo1/rt5/zebra.conf b/tests/topotests/bfd_ospf_topo1/rt5/zebra.conf index 33473c91a3d1..d3331b5b6ca9 100644 --- a/tests/topotests/bfd_ospf_topo1/rt5/zebra.conf +++ b/tests/topotests/bfd_ospf_topo1/rt5/zebra.conf @@ -7,7 +7,7 @@ hostname rt5 ! interface lo ip address 5.5.5.5/32 - ipv6 address ::ffff:0505:0505/128 + ipv6 address ::ffff:5.5.5.5/128 ! interface eth-rt2 ip address 10.0.3.2/24 diff --git a/tests/topotests/bgp_aigp/r1/bgpd.conf b/tests/topotests/bgp_aigp/r1/bgpd.conf index 74a0215bc476..15621999c59f 100644 --- a/tests/topotests/bgp_aigp/r1/bgpd.conf +++ b/tests/topotests/bgp_aigp/r1/bgpd.conf @@ -9,4 +9,20 @@ router bgp 65001 neighbor 10.0.0.3 timers 1 3 neighbor 10.0.0.3 timers connect 1 neighbor 10.0.0.3 update-source lo + neighbor 192.168.18.8 remote-as external + neighbor 192.168.18.8 timers 1 3 + neighbor 192.168.18.8 timers connect 1 + address-family ipv4 + neighbor 192.168.18.8 route-map r8 out + exit-address-family ! +ip prefix-list p71 seq 5 permit 10.0.0.71/32 +ip prefix-list p72 seq 5 permit 10.0.0.72/32 +! +route-map r8 permit 10 + match ip address prefix-list p71 + set metric igp +route-map r8 permit 20 + match ip address prefix-list p72 + set metric aigp +exit diff --git a/tests/topotests/bgp_aigp/r1/ospfd.conf b/tests/topotests/bgp_aigp/r1/ospfd.conf index 38aa11d0362a..098bf57b0365 100644 --- a/tests/topotests/bgp_aigp/r1/ospfd.conf +++ b/tests/topotests/bgp_aigp/r1/ospfd.conf @@ -1,6 +1,6 @@ ! interface lo - ip ospf cost 10 + ip ospf passive ! interface r1-eth0 ip ospf dead-interval 4 diff --git a/tests/topotests/bgp_aigp/r1/zebra.conf b/tests/topotests/bgp_aigp/r1/zebra.conf index 0ed22d37be8b..7ac4bb5de9a3 100644 --- a/tests/topotests/bgp_aigp/r1/zebra.conf +++ b/tests/topotests/bgp_aigp/r1/zebra.conf @@ -8,3 +8,6 @@ interface r1-eth0 interface r1-eth1 ip address 192.168.13.1/24 ! +interface r1-eth2 + ip address 192.168.18.1/24 +! diff --git a/tests/topotests/bgp_aigp/r2/bgpd.conf b/tests/topotests/bgp_aigp/r2/bgpd.conf index 4db468753642..4539016f916b 100644 --- a/tests/topotests/bgp_aigp/r2/bgpd.conf +++ b/tests/topotests/bgp_aigp/r2/bgpd.conf @@ -1,6 +1,7 @@ router bgp 65001 no bgp ebgp-requires-policy no bgp network import-check + bgp route-reflector allow-outbound-policy neighbor 10.0.0.1 remote-as internal neighbor 10.0.0.1 timers 1 3 neighbor 10.0.0.1 timers connect 1 @@ -11,5 +12,6 @@ router bgp 65001 neighbor 192.168.24.4 aigp address-family ipv4 redistribute connected + neighbor 10.0.0.1 next-hop-self force exit-address-family ! diff --git a/tests/topotests/bgp_aigp/r2/ospfd.conf b/tests/topotests/bgp_aigp/r2/ospfd.conf index ed31941f65c0..106a46251d2e 100644 --- a/tests/topotests/bgp_aigp/r2/ospfd.conf +++ b/tests/topotests/bgp_aigp/r2/ospfd.conf @@ -1,6 +1,6 @@ ! interface lo - ip ospf cost 10 + ip ospf passive ! interface r2-eth0 ip ospf dead-interval 4 diff --git a/tests/topotests/bgp_aigp/r3/bgpd.conf b/tests/topotests/bgp_aigp/r3/bgpd.conf index 5ab712eaba65..bdaa5cf55d1e 100644 --- a/tests/topotests/bgp_aigp/r3/bgpd.conf +++ b/tests/topotests/bgp_aigp/r3/bgpd.conf @@ -1,6 +1,7 @@ router bgp 65001 no bgp ebgp-requires-policy no bgp network import-check + bgp route-reflector allow-outbound-policy neighbor 10.0.0.1 remote-as internal neighbor 10.0.0.1 timers 1 3 neighbor 10.0.0.1 timers connect 1 @@ -11,5 +12,6 @@ router bgp 65001 neighbor 192.168.35.5 aigp address-family ipv4 redistribute connected + neighbor 10.0.0.1 next-hop-self force exit-address-family ! diff --git a/tests/topotests/bgp_aigp/r3/ospfd.conf b/tests/topotests/bgp_aigp/r3/ospfd.conf index f971ad6f8975..9ede3a1fab64 100644 --- a/tests/topotests/bgp_aigp/r3/ospfd.conf +++ b/tests/topotests/bgp_aigp/r3/ospfd.conf @@ -1,6 +1,6 @@ ! interface lo - ip ospf cost 10 + ip ospf passive ! interface r3-eth0 ip ospf dead-interval 4 diff --git a/tests/topotests/bgp_aigp/r4/bgpd.conf b/tests/topotests/bgp_aigp/r4/bgpd.conf index aa88bac91385..2cdf84a1b38b 100644 --- a/tests/topotests/bgp_aigp/r4/bgpd.conf +++ b/tests/topotests/bgp_aigp/r4/bgpd.conf @@ -1,6 +1,7 @@ router bgp 65001 no bgp ebgp-requires-policy no bgp network import-check + bgp route-reflector allow-outbound-policy neighbor 192.168.24.2 remote-as internal neighbor 192.168.24.2 timers 1 3 neighbor 192.168.24.2 timers connect 1 @@ -11,7 +12,25 @@ router bgp 65001 neighbor 10.0.0.6 timers connect 1 neighbor 10.0.0.6 update-source lo address-family ipv4 - redistribute connected - redistribute ospf + redistribute connected route-map connected-to-bgp + redistribute ospf route-map ospf-to-bgp + neighbor 192.168.24.2 route-map set-nexthop out exit-address-family ! +ip prefix-list p66 seq 5 permit 10.0.6.6/32 +! +route-map ospf-to-bgp permit 10 + match ip address prefix-list p66 + set aigp igp-metric +! +! Two OSPF domains should be isolated - otherwise the connected routes +! on r4 would be advertised to r3 (via r4 -> r6 -> r5 -> r3), and can +! mess up bgp bestpath calculation (igp metrics for the BGP nexthops). +! +route-map connected-to-bgp permit 10 + set community no-advertise +! +route-map set-nexthop permit 10 + set ip next-hop peer-address +exit +! diff --git a/tests/topotests/bgp_aigp/r4/ospfd.conf b/tests/topotests/bgp_aigp/r4/ospfd.conf index c9e6796f6e74..237b5e18abe0 100644 --- a/tests/topotests/bgp_aigp/r4/ospfd.conf +++ b/tests/topotests/bgp_aigp/r4/ospfd.conf @@ -1,6 +1,6 @@ ! interface lo - ip ospf cost 10 + ip ospf passive ! interface r4-eth1 ip ospf dead-interval 4 diff --git a/tests/topotests/bgp_aigp/r5/bgpd.conf b/tests/topotests/bgp_aigp/r5/bgpd.conf index 4fde262053ce..3d1f5e85722b 100644 --- a/tests/topotests/bgp_aigp/r5/bgpd.conf +++ b/tests/topotests/bgp_aigp/r5/bgpd.conf @@ -1,6 +1,7 @@ router bgp 65001 no bgp ebgp-requires-policy no bgp network import-check + bgp route-reflector allow-outbound-policy neighbor 192.168.35.3 remote-as internal neighbor 192.168.35.3 timers 1 3 neighbor 192.168.35.3 timers connect 1 @@ -11,7 +12,18 @@ router bgp 65001 neighbor 10.0.0.6 timers connect 1 neighbor 10.0.0.6 update-source lo address-family ipv4 - redistribute connected - redistribute ospf + redistribute connected route-map connected-to-bgp + neighbor 192.168.35.3 route-map set-nexthop out exit-address-family ! +! Two OSPF domains should be isolated - otherwise the connected routes +! on r5 would be advertised to r2 (via r5 -> r6 -> r4 -> r2), and can +! mess up bgp bestpath calculation (igp metrics for the BGP nexthops). +! +route-map connected-to-bgp permit 10 + set community no-advertise +! +route-map set-nexthop permit 10 + set ip next-hop peer-address +exit +! diff --git a/tests/topotests/bgp_aigp/r5/ospfd.conf b/tests/topotests/bgp_aigp/r5/ospfd.conf index b853c74102a2..65a213df1721 100644 --- a/tests/topotests/bgp_aigp/r5/ospfd.conf +++ b/tests/topotests/bgp_aigp/r5/ospfd.conf @@ -1,6 +1,6 @@ ! interface lo - ip ospf cost 10 + ip ospf passive ! interface r5-eth1 ip ospf dead-interval 4 diff --git a/tests/topotests/bgp_aigp/r6/bgpd.conf b/tests/topotests/bgp_aigp/r6/bgpd.conf index 2faae7720c90..2d5f7a89baf4 100644 --- a/tests/topotests/bgp_aigp/r6/bgpd.conf +++ b/tests/topotests/bgp_aigp/r6/bgpd.conf @@ -1,6 +1,7 @@ router bgp 65001 no bgp ebgp-requires-policy no bgp network import-check + bgp route-reflector allow-outbound-policy neighbor 10.0.0.4 remote-as internal neighbor 10.0.0.4 timers 1 3 neighbor 10.0.0.4 timers connect 1 @@ -15,6 +16,11 @@ router bgp 65001 neighbor 192.168.67.7 timers 1 3 neighbor 192.168.67.7 timers connect 1 address-family ipv4 - redistribute ospf + neighbor 10.0.0.4 route-map set-nexthop out + neighbor 10.0.0.5 route-map set-nexthop out exit-address-family ! +route-map set-nexthop permit 10 + set ip next-hop peer-address +exit +! diff --git a/tests/topotests/bgp_aigp/r6/ospfd.conf b/tests/topotests/bgp_aigp/r6/ospfd.conf index 46b293317866..89cbefa89522 100644 --- a/tests/topotests/bgp_aigp/r6/ospfd.conf +++ b/tests/topotests/bgp_aigp/r6/ospfd.conf @@ -1,6 +1,6 @@ ! interface lo - ip ospf cost 10 + ip ospf passive ! interface r6-eth0 ip ospf dead-interval 4 diff --git a/tests/topotests/bgp_aigp/r6/zebra.conf b/tests/topotests/bgp_aigp/r6/zebra.conf index f8ca5f8b825f..b6456cacc5ac 100644 --- a/tests/topotests/bgp_aigp/r6/zebra.conf +++ b/tests/topotests/bgp_aigp/r6/zebra.conf @@ -1,6 +1,7 @@ ! interface lo ip address 10.0.0.6/32 + ip address 10.0.6.6/32 ! interface r6-eth0 ip address 192.168.46.6/24 diff --git a/tests/topotests/bgp_aigp/r8/bgpd.conf b/tests/topotests/bgp_aigp/r8/bgpd.conf new file mode 100644 index 000000000000..c50c3ce91a58 --- /dev/null +++ b/tests/topotests/bgp_aigp/r8/bgpd.conf @@ -0,0 +1,4 @@ +router bgp 65008 + no bgp ebgp-requires-policy + neighbor 192.168.18.1 remote-as external +! diff --git a/tests/topotests/bgp_aigp/r8/zebra.conf b/tests/topotests/bgp_aigp/r8/zebra.conf new file mode 100644 index 000000000000..f7545270b2c7 --- /dev/null +++ b/tests/topotests/bgp_aigp/r8/zebra.conf @@ -0,0 +1,4 @@ +! +interface r8-eth0 + ip address 192.168.18.8/24 +! diff --git a/tests/topotests/bgp_aigp/test_bgp_aigp.py b/tests/topotests/bgp_aigp/test_bgp_aigp.py index b81c5432972d..f937f851a62f 100644 --- a/tests/topotests/bgp_aigp/test_bgp_aigp.py +++ b/tests/topotests/bgp_aigp/test_bgp_aigp.py @@ -12,9 +12,13 @@ r6 receives those routes with aigp-metric TLV. r2 and r3 receives those routes with aigp-metric TLV increased by 20, -and 30 appropriately. +and 10 appropriately. -r1 receives routes with aigp-metric TLV 111,131 and 112,132 appropriately. +r1 receives routes with aigp-metric TLV 81, 91 and 82, 92 respectively. + +r1 advertises MED from IGP protocol (set metric igp) to r8. + +r1 advertises MED from AIGP (set metric aigp) to r8. """ import os @@ -34,7 +38,7 @@ def build_topo(tgen): - for routern in range(1, 8): + for routern in range(1, 9): tgen.add_router("r{}".format(routern)) switch = tgen.add_switch("s1") @@ -65,6 +69,10 @@ def build_topo(tgen): switch.add_link(tgen.gears["r6"]) switch.add_link(tgen.gears["r7"]) + switch = tgen.add_switch("s8") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r8"]) + def setup_module(mod): tgen = Topogen(build_topo, mod.__name__) @@ -102,21 +110,36 @@ def test_bgp_aigp(): r3 = tgen.gears["r3"] r4 = tgen.gears["r4"] r5 = tgen.gears["r5"] + r8 = tgen.gears["r8"] def _bgp_converge(): output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast 10.0.0.71/32 json")) expected = { "paths": [ { - "aigpMetric": 111, + "aigpMetric": 81, "valid": True, - "nexthops": [{"hostname": "r3", "accessible": True}], + "nexthops": [ + { + "ip": "10.0.0.3", + "hostname": "r3", + "metric": 30, + "accessible": True, + } + ], }, { - "aigpMetric": 131, + "aigpMetric": 91, "valid": True, - "bestpath": {"selectionReason": "Neighbor IP"}, - "nexthops": [{"hostname": "r2", "accessible": True}], + "bestpath": {"selectionReason": "IGP Metric"}, + "nexthops": [ + { + "ip": "10.0.0.2", + "hostname": "r2", + "metric": 10, + "accessible": True, + } + ], }, ] } @@ -129,6 +152,18 @@ def _bgp_check_aigp_metric(router, prefix, aigp): expected = {"paths": [{"aigpMetric": aigp, "valid": True}]} return topotest.json_cmp(output, expected) + def _bgp_check_received_med(): + output = json.loads( + r8.vtysh_cmd("show bgp ipv4 unicast 10.0.0.64/28 longer-prefixes json") + ) + expected = { + "routes": { + "10.0.0.71/32": [{"valid": True, "metric": 10}], + "10.0.0.72/32": [{"valid": True, "metric": 92}], + } + } + return topotest.json_cmp(output, expected) + def _bgp_check_aigp_metric_bestpath(): output = json.loads( r1.vtysh_cmd( @@ -140,30 +175,58 @@ def _bgp_check_aigp_metric_bestpath(): "10.0.0.71/32": { "paths": [ { - "aigpMetric": 111, - "bestpath": {"selectionReason": "AIGP"}, + "aigpMetric": 81, "valid": True, - "nexthops": [{"hostname": "r3", "accessible": True}], + "nexthops": [ + { + "ip": "10.0.0.3", + "hostname": "r3", + "metric": 30, + "accessible": True, + } + ], }, { - "aigpMetric": 131, + "aigpMetric": 91, "valid": True, - "nexthops": [{"hostname": "r2", "accessible": True}], + "bestpath": {"selectionReason": "AIGP"}, + "nexthops": [ + { + "ip": "10.0.0.2", + "hostname": "r2", + "metric": 10, + "accessible": True, + } + ], }, ], }, "10.0.0.72/32": { "paths": [ { - "aigpMetric": 112, - "bestpath": {"selectionReason": "AIGP"}, + "aigpMetric": 82, "valid": True, - "nexthops": [{"hostname": "r3", "accessible": True}], + "nexthops": [ + { + "ip": "10.0.0.3", + "hostname": "r3", + "metric": 30, + "accessible": True, + } + ], }, { - "aigpMetric": 132, + "aigpMetric": 92, "valid": True, - "nexthops": [{"hostname": "r2", "accessible": True}], + "bestpath": {"selectionReason": "AIGP"}, + "nexthops": [ + { + "ip": "10.0.0.2", + "hostname": "r2", + "metric": 10, + "accessible": True, + } + ], }, ], }, @@ -185,6 +248,11 @@ def _bgp_check_aigp_metric_bestpath(): """ ) + # r4, 10.0.6.6/32 with aigp-metric 20 + test_func = functools.partial(_bgp_check_aigp_metric, r4, "10.0.6.6/32", 20) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "aigp-metric for 10.0.6.6/32 is not 20" + # r4, 10.0.0.71/32 with aigp-metric 71 test_func = functools.partial(_bgp_check_aigp_metric, r4, "10.0.0.71/32", 71) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) @@ -195,21 +263,28 @@ def _bgp_check_aigp_metric_bestpath(): _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assert result is None, "aigp-metric for 10.0.0.72/32 is not 72" - # r2, 10.0.0.71/32 with aigp-metric 101 (71 + 30) - test_func = functools.partial(_bgp_check_aigp_metric, r2, "10.0.0.71/32", 101) + # r2, 10.0.0.71/32 with aigp-metric 101 (71 + 20) + test_func = functools.partial(_bgp_check_aigp_metric, r2, "10.0.0.71/32", 91) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) - assert result is None, "aigp-metric for 10.0.0.71/32 is not 101" + assert result is None, "aigp-metric for 10.0.0.71/32 is not 91" - # r3, 10.0.0.72/32 with aigp-metric 92 (72 + 20) - test_func = functools.partial(_bgp_check_aigp_metric, r3, "10.0.0.72/32", 92) + # r3, 10.0.0.72/32 with aigp-metric 92 (72 + 10) + test_func = functools.partial(_bgp_check_aigp_metric, r3, "10.0.0.72/32", 82) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) - assert result is None, "aigp-metric for 10.0.0.72/32 is not 92" + assert result is None, "aigp-metric for 10.0.0.72/32 is not 82" - # r1, check if AIGP is considered in best-path selection (lowest wins) + # r1, check if AIGP is considered in best-path selection (lowest wins: aigp + nexthop-metric) test_func = functools.partial(_bgp_check_aigp_metric_bestpath) _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assert result is None, "AIGP attribute is not considered in best-path selection" + # r8, check if MED is set derived from `set metric igp`, and `set metric aigp` + test_func = functools.partial(_bgp_check_received_med) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert ( + result is None + ), "MED attribute values are not derived from `set metric [a]igp`" + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] 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 000000000000..4099a248f128 --- /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 000000000000..b5b43c76bed4 --- /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 000000000000..bf4a3497e836 --- /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 000000000000..0159dc8e7c33 --- /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 000000000000..dd91101d9050 --- /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 000000000000..6e2130f7e829 --- /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 000000000000..b8a36c5ad05a --- /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 000000000000..48702e4e4c65 --- /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 000000000000..ea6663724e9b --- /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 000000000000..b8a36c5ad05a --- /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 000000000000..1d95b2ac2bd6 --- /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 000000000000..d7a7dece2bca --- /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 000000000000..206e229b2ee6 --- /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 +# + +""" +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_as_override/test_bgp_as_override.py b/tests/topotests/bgp_as_override/test_bgp_as_override.py index dbbdf2c88f8e..ab22b4936f7a 100644 --- a/tests/topotests/bgp_as_override/test_bgp_as_override.py +++ b/tests/topotests/bgp_as_override/test_bgp_as_override.py @@ -27,7 +27,7 @@ def build_topo(tgen): - for routern in range(1, 7): + for routern in range(1, 5): tgen.add_router("r{}".format(routern)) switch = tgen.add_switch("s1") diff --git a/tests/topotests/bgp_bmp/test_bgp_bmp.py b/tests/topotests/bgp_bmp/test_bgp_bmp.py index 80e291b2bdb6..1cf6a0e7f826 100644 --- a/tests/topotests/bgp_bmp/test_bgp_bmp.py +++ b/tests/topotests/bgp_bmp/test_bgp_bmp.py @@ -160,6 +160,34 @@ def check_for_prefixes(expected_prefixes, bmp_log_type, policy, labels=None): return True +def check_for_peer_message(expected_peers, bmp_log_type): + """ + Check for the presence of a peer up message for the peer + """ + global SEQ + # we care only about the new messages + messages = [ + m for m in sorted(get_bmp_messages(), key=lambda d: d["seq"]) if m["seq"] > SEQ + ] + + # get the list of pairs (prefix, policy, seq) for the given message type + peers = [ + m["peer_ip"] + for m in messages + if "peer_ip" in m.keys() and m["bmp_log_type"] == bmp_log_type + ] + + # check for prefixes + for ep in expected_peers: + if ep not in peers: + msg = "The peer {} is not present in the {} log messages." + logger.debug(msg.format(ep, bmp_log_type)) + return False + + SEQ = messages[-1]["seq"] + return True + + def set_bmp_policy(tgen, node, asn, target, safi, policy, vrf=None): """ Configure the bmp policy. @@ -234,15 +262,11 @@ def vpn_prefixes(policy): prefixes = ["172.31.10.1/32", "2001::2222/128"] - if policy == PRE_POLICY: - # labels are not yet supported in adj-RIB-in. Do not test for the moment - labels = None - else: - # "label vpn export" value in r2/bgpd.conf - labels = { - "172.31.10.1/32": 102, - "2001::2222/128": 105, - } + # "label vpn export" value in r2/bgpd.conf + labels = { + "172.31.10.1/32": 102, + "2001::2222/128": 105, + } # add prefixes configure_prefixes(tgen, "r2", 65502, "unicast", prefixes, vrf="vrf1") @@ -280,6 +304,20 @@ def check_for_log_file(): assert success, "The BMP server is not logging" +def test_peer_up(): + """ + Checking for BMP peers up messages + """ + + peers = ["192.168.0.2", "192:168::2"] + + logger.info("checking for BMP peers up messages") + + test_func = partial(check_for_peer_message, peers, "peer up") + success, _ = topotest.run_and_expect(test_func, True, wait=0.5) + assert success, "Checking the updated prefixes has been failed !." + + def test_bmp_bgp_unicast(): """ Add/withdraw bgp unicast prefixes and check the bmp logs. @@ -302,6 +340,23 @@ def test_bmp_bgp_vpn(): vpn_prefixes(LOC_RIB) +def test_peer_down(): + """ + Checking for BMP peers down messages + """ + tgen = get_topogen() + + tgen.gears["r2"].vtysh_cmd("clear bgp *") + + peers = ["192.168.0.2", "192:168::2"] + + logger.info("checking for BMP peers down messages") + + test_func = partial(check_for_peer_message, peers, "peer down") + success, _ = topotest.run_and_expect(test_func, True, wait=0.5) + assert success, "Checking the updated prefixes has been failed !." + + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_bmp_vrf/__init__.py b/tests/topotests/bgp_bmp_vrf/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/bgp_bmp_vrf/r1/bgpd.conf b/tests/topotests/bgp_bmp_vrf/r1/bgpd.conf new file mode 100644 index 000000000000..994cdbf68ea7 --- /dev/null +++ b/tests/topotests/bgp_bmp_vrf/r1/bgpd.conf @@ -0,0 +1,23 @@ +router bgp 65501 vrf vrf1 + bgp router-id 192.168.0.1 + bgp log-neighbor-changes + no bgp ebgp-requires-policy + neighbor 192.168.0.2 remote-as 65502 + neighbor 192:168::2 remote-as 65502 +! + bmp targets bmp1 + bmp connect 192.0.2.10 port 1789 min-retry 100 max-retry 10000 + exit +! + + address-family ipv4 unicast + neighbor 192.168.0.2 activate + neighbor 192.168.0.2 soft-reconfiguration inbound + no neighbor 192:168::2 activate + exit-address-family +! + address-family ipv6 unicast + neighbor 192:168::2 activate + neighbor 192:168::2 soft-reconfiguration inbound + exit-address-family +! diff --git a/tests/topotests/bgp_bmp_vrf/r1/zebra.conf b/tests/topotests/bgp_bmp_vrf/r1/zebra.conf new file mode 100644 index 000000000000..0b523c9e18d8 --- /dev/null +++ b/tests/topotests/bgp_bmp_vrf/r1/zebra.conf @@ -0,0 +1,7 @@ +interface r1-eth0 + ip address 192.0.2.1/24 +! +interface r1-eth1 + ip address 192.168.0.1/24 + ipv6 address 192:168::1/64 +! diff --git a/tests/topotests/bgp_bmp_vrf/r2/bgpd.conf b/tests/topotests/bgp_bmp_vrf/r2/bgpd.conf new file mode 100644 index 000000000000..7c8255a17563 --- /dev/null +++ b/tests/topotests/bgp_bmp_vrf/r2/bgpd.conf @@ -0,0 +1,19 @@ +router bgp 65502 + bgp router-id 192.168.0.2 + bgp log-neighbor-changes + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.0.1 remote-as 65501 + neighbor 192:168::1 remote-as 65501 +! + address-family ipv4 unicast + neighbor 192.168.0.1 activate + no neighbor 192:168::1 activate + redistribute connected + exit-address-family +! + address-family ipv6 unicast + neighbor 192:168::1 activate + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_bmp_vrf/r2/zebra.conf b/tests/topotests/bgp_bmp_vrf/r2/zebra.conf new file mode 100644 index 000000000000..9d82bfe2df5c --- /dev/null +++ b/tests/topotests/bgp_bmp_vrf/r2/zebra.conf @@ -0,0 +1,8 @@ +interface r2-eth0 + ip address 192.168.0.2/24 + ipv6 address 192:168::2/64 +! +interface r2-eth1 + ip address 172.31.0.2/24 + ipv6 address 172:31::2/64 +! diff --git a/tests/topotests/bgp_bmp_vrf/test_bgp_bmp_vrf.py b/tests/topotests/bgp_bmp_vrf/test_bgp_bmp_vrf.py new file mode 100644 index 000000000000..b683920d2e70 --- /dev/null +++ b/tests/topotests/bgp_bmp_vrf/test_bgp_bmp_vrf.py @@ -0,0 +1,326 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright 2023 6WIND S.A. +# Authored by Farid Mihoub +# + +""" +test_bgp_bmp.py: Test BGP BMP functionalities + + +------+ +------+ +------+ + | | | | | | + | BMP1 |------------| R1 |---------------| R2 | + | | | | | | + +------+ +------+ +------+ + +Setup two routers R1 and R2 with one link configured with IPv4 and +IPv6 addresses. +Configure BGP in R1 and R2 to exchange prefixes from +the latter to the first router. +Setup a link between R1 and the BMP server, activate the BMP feature in R1 +and ensure the monitored BGP sessions logs are well present on the BMP server. +""" + +from functools import partial +from ipaddress import ip_network +import json +import os +import platform +import pytest +import sys + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join("../")) +sys.path.append(os.path.join("../lib/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.bgp import verify_bgp_convergence_from_running_config +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +pytestmark = [pytest.mark.bgpd] + +# remember the last sequence number of the logging messages +SEQ = 0 + +PRE_POLICY = "pre-policy" +POST_POLICY = "post-policy" +LOC_RIB = "loc-rib" + + +def build_topo(tgen): + tgen.add_router("r1") + tgen.add_router("r2") + tgen.add_bmp_server("bmp1", ip="192.0.2.10", defaultRoute="via 192.0.2.1") + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["bmp1"]) + + tgen.add_link(tgen.gears["r1"], tgen.gears["r2"], "r1-eth1", "r2-eth0") + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + tgen.net["r1"].cmd( + """ +ip link add vrf1 type vrf table 10 +ip link set vrf1 up +ip link set r1-eth1 master vrf1 +""" + ) + + for rname, router in tgen.routers().items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, + os.path.join(CWD, "{}/bgpd.conf".format(rname)), + "-M bmp", + ) + + tgen.start_router() + + logger.info("starting BMP servers") + for bmp_name, server in tgen.get_bmp_servers().items(): + server.start(log_file=os.path.join(tgen.logdir, bmp_name, "bmp.log")) + + +def teardown_module(_mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_convergence(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + result = verify_bgp_convergence_from_running_config(tgen, dut="r1") + assert result is True, "BGP is not converging" + + +def get_bmp_messages(): + """ + Read the BMP logging messages. + """ + messages = [] + tgen = get_topogen() + text_output = tgen.gears["bmp1"].run( + "cat {}".format(os.path.join(tgen.logdir, "bmp1", "bmp.log")) + ) + + for m in text_output.splitlines(): + # some output in the bash can break the message decoding + try: + messages.append(json.loads(m)) + except Exception as e: + logger.warning(str(e) + " message: {}".format(str(m))) + continue + + if not messages: + logger.error("Bad BMP log format, check your BMP server") + + return messages + + +def check_for_prefixes(expected_prefixes, bmp_log_type, policy, labels=None): + """ + Check for the presence of the given prefixes in the BMP server logs with + the given message type and the set policy. + """ + global SEQ + # we care only about the new messages + messages = [ + m for m in sorted(get_bmp_messages(), key=lambda d: d["seq"]) if m["seq"] > SEQ + ] + + # get the list of pairs (prefix, policy, seq) for the given message type + prefixes = [ + m["ip_prefix"] + for m in messages + if "ip_prefix" in m.keys() + and "bmp_log_type" in m.keys() + and m["bmp_log_type"] == bmp_log_type + and m["policy"] == policy + and ( + labels is None + or ( + m["ip_prefix"] in labels.keys() and m["label"] == labels[m["ip_prefix"]] + ) + ) + ] + + # check for prefixes + for ep in expected_prefixes: + if ep not in prefixes: + msg = "The prefix {} is not present in the {} log messages." + logger.debug(msg.format(ep, bmp_log_type)) + return False + + SEQ = messages[-1]["seq"] + return True + + +def check_for_peer_message(expected_peers, bmp_log_type): + """ + Check for the presence of a peer up message for the peer + """ + global SEQ + # we care only about the new messages + messages = [ + m for m in sorted(get_bmp_messages(), key=lambda d: d["seq"]) if m["seq"] > SEQ + ] + + # get the list of pairs (prefix, policy, seq) for the given message type + peers = [ + m["peer_ip"] + for m in messages + if "peer_ip" in m.keys() and m["bmp_log_type"] == bmp_log_type + ] + + # check for prefixes + for ep in expected_peers: + if ep not in peers: + msg = "The peer {} is not present in the {} log messages." + logger.debug(msg.format(ep, bmp_log_type)) + return False + + SEQ = messages[-1]["seq"] + return True + + +def set_bmp_policy(tgen, node, asn, target, safi, policy, vrf=None): + """ + Configure the bmp policy. + """ + vrf = " vrf {}".format(vrf) if vrf else "" + cmd = [ + "con t\n", + "router bgp {}{}\n".format(asn, vrf), + "bmp targets {}\n".format(target), + "bmp monitor ipv4 {} {}\n".format(safi, policy), + "bmp monitor ipv6 {} {}\n".format(safi, policy), + "end\n", + ] + tgen.gears[node].vtysh_cmd("".join(cmd)) + + +def configure_prefixes(tgen, node, asn, safi, prefixes, vrf=None, update=True): + """ + Configure the bgp prefixes. + """ + withdraw = "no " if not update else "" + vrf = " vrf {}".format(vrf) if vrf else "" + for p in prefixes: + ip = ip_network(p) + cmd = [ + "conf t\n", + "router bgp {}{}\n".format(asn, vrf), + "address-family ipv{} {}\n".format(ip.version, safi), + "{}network {}\n".format(withdraw, ip), + "exit-address-family\n", + ] + logger.debug("setting prefix: ipv{} {} {}".format(ip.version, safi, ip)) + tgen.gears[node].vtysh_cmd("".join(cmd)) + + +def unicast_prefixes(policy): + """ + Setup the BMP monitor policy, Add and withdraw ipv4/v6 prefixes. + Check if the previous actions are logged in the BMP server with the right + message type and the right policy. + """ + tgen = get_topogen() + set_bmp_policy(tgen, "r1", 65501, "bmp1", "unicast", policy, vrf="vrf1") + + prefixes = ["172.31.0.15/32", "2111::1111/128"] + # add prefixes + configure_prefixes(tgen, "r2", 65502, "unicast", prefixes) + + logger.info("checking for updated prefixes") + # check + test_func = partial(check_for_prefixes, prefixes, "update", policy) + success, _ = topotest.run_and_expect(test_func, True, wait=0.5) + assert success, "Checking the updated prefixes has been failed !." + + # withdraw prefixes + configure_prefixes(tgen, "r2", 65502, "unicast", prefixes, update=False) + logger.info("checking for withdrawed prefxies") + # check + test_func = partial(check_for_prefixes, prefixes, "withdraw", policy) + success, _ = topotest.run_and_expect(test_func, True, wait=0.5) + assert success, "Checking the withdrawed prefixes has been failed !." + + +def test_bmp_server_logging(): + """ + Assert the logging of the bmp server. + """ + + def check_for_log_file(): + tgen = get_topogen() + output = tgen.gears["bmp1"].run( + "ls {}".format(os.path.join(tgen.logdir, "bmp1")) + ) + if "bmp.log" not in output: + return False + return True + + success, _ = topotest.run_and_expect(check_for_log_file, True, wait=0.5) + assert success, "The BMP server is not logging" + + +def test_peer_up(): + """ + Checking for BMP peers up messages + """ + + peers = ["192.168.0.2", "192:168::2"] + + logger.info("checking for BMP peers up messages") + + test_func = partial(check_for_peer_message, peers, "peer up") + success, _ = topotest.run_and_expect(test_func, True, wait=0.5) + assert success, "Checking the updated prefixes has been failed !." + + +def test_bmp_bgp_unicast(): + """ + Add/withdraw bgp unicast prefixes and check the bmp logs. + """ + logger.info("*** Unicast prefixes pre-policy logging ***") + unicast_prefixes(PRE_POLICY) + logger.info("*** Unicast prefixes post-policy logging ***") + unicast_prefixes(POST_POLICY) + logger.info("*** Unicast prefixes loc-rib logging ***") + unicast_prefixes(LOC_RIB) + + +def test_peer_down(): + """ + Checking for BMP peers down messages + """ + tgen = get_topogen() + + tgen.gears["r2"].vtysh_cmd("clear bgp *") + + peers = ["192.168.0.2", "192:168::2"] + + logger.info("checking for BMP peers down messages") + + test_func = partial(check_for_peer_message, peers, "peer down") + success, _ = topotest.run_and_expect(test_func, True, wait=0.5) + assert success, "Checking the updated prefixes has been failed !." + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py index 2d027081cb55..9dfb7fc4d90d 100644 --- a/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py +++ b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py @@ -210,7 +210,7 @@ def test_protocols_convergence(): "vrfName": "r1-vrf-101", "nexthops": [ { - "ip": "::ffff:c0a8:6429", + "ip": "::ffff:192.168.100.41", } ], } @@ -227,8 +227,8 @@ def test_protocols_convergence(): "192.168.100.41": { "nexthopIp": "192.168.100.41", }, - "::ffff:c0a8:6429": { - "nexthopIp": "::ffff:c0a8:6429", + "::ffff:192.168.100.41": { + "nexthopIp": "::ffff:192.168.100.41", }, } } diff --git a/tests/topotests/bgp_extended_link_bandwidth/r1/frr.conf b/tests/topotests/bgp_extended_link_bandwidth/r1/frr.conf index d0c0813e842d..18b312c60f14 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 5cad150aef0b..af07c7cc5c25 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 000000000000..61b13c8893c3 --- /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 000000000000..49f0caf7de19 --- /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 e9006b81c930..3e5be0310fe3 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 @@ 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 _bgp_converge(): 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_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py index 803b51c04327..8382fea18886 100644 --- a/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py +++ b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py @@ -670,7 +670,7 @@ def bgp_prefix_received_v4_mapped_v6_nh(router): { "nexthops": [ { - "ip": "::ffff:a00:501", + "ip": "::ffff:10.0.5.1", "hostname": "r1", "afi": "ipv6", "scope": "global", @@ -754,7 +754,7 @@ def bgp_prefix_received_v4_mapped_v6_nh(router): assert ( result is None ), "Testcase {} : Failed \n Error: Nexthop for prefix 11.0.20.1 \ - is not ::ffff:a00:501".format( + is not ::ffff:10.0.5.1".format( tc_name ) diff --git a/tests/topotests/bgp_match_peer/r1/frr.conf b/tests/topotests/bgp_match_peer/r1/frr.conf index f30da3b896a2..02fb8ae518e2 100644 --- a/tests/topotests/bgp_match_peer/r1/frr.conf +++ b/tests/topotests/bgp_match_peer/r1/frr.conf @@ -17,6 +17,7 @@ router bgp 65001 neighbor 192.168.1.2 route-map all in neighbor r3 route-map all in neighbor r4 route-map all in + neighbor r4 route-map r4 out exit-address-family ! route-map all permit 5 @@ -24,7 +25,7 @@ route-map all permit 5 set metric 1 ! route-map all permit 10 - match peer 192.168.1.2 + match src-peer 192.168.1.2 set metric 2 ! route-map all permit 15 @@ -35,3 +36,6 @@ route-map all permit 20 match peer r4 set metric 4 ! +route-map r4 permit 10 + match src-peer r3 +! diff --git a/tests/topotests/bgp_match_peer/test_bgp_match_peer.py b/tests/topotests/bgp_match_peer/test_bgp_match_peer.py index 4eb7473df0af..180dbcd08aef 100644 --- a/tests/topotests/bgp_match_peer/test_bgp_match_peer.py +++ b/tests/topotests/bgp_match_peer/test_bgp_match_peer.py @@ -82,6 +82,29 @@ def _bgp_converge(): _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) assert result is None, "Can't converge" + def _bgp_show_advertised_routes(): + output = json.loads( + r1.vtysh_cmd("show bgp ipv4 unicast neighbors r4 advertised-routes json") + ) + expected = { + "advertisedRoutes": { + "10.0.0.3/32": { + "network": "10.0.0.3/32", + "nextHop": "192.168.1.3", + "path": "65003", + } + }, + "totalPrefixCounter": 1, + } + + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_show_advertised_routes, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Can't filter by source peer" + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] diff --git a/tests/topotests/bgp_nexthop_ipv6/exabgp.env b/tests/topotests/bgp_nexthop_ipv6/exabgp.env new file mode 100644 index 000000000000..28e642360a39 --- /dev/null +++ b/tests/topotests/bgp_nexthop_ipv6/exabgp.env @@ -0,0 +1,53 @@ +[exabgp.api] +encoder = text +highres = false +respawn = false +socket = '' + +[exabgp.bgp] +openwait = 60 + +[exabgp.cache] +attributes = true +nexthops = true + +[exabgp.daemon] +daemonize = true +pid = '/var/run/exabgp/exabgp.pid' +user = 'exabgp' +##daemonize = false + +[exabgp.log] +all = false +configuration = true +daemon = true +destination = '/var/log/exabgp.log' +enable = true +level = INFO +message = false +network = true +packets = false +parser = false +processes = true +reactor = true +rib = false +routes = false +short = false +timers = false + +[exabgp.pdb] +enable = false + +[exabgp.profile] +enable = false +file = '' + +[exabgp.reactor] +speed = 1.0 + +[exabgp.tcp] +acl = false +bind = '' +delay = 0 +once = false +port = 179 diff --git a/tests/topotests/bgp_nexthop_ipv6/r1/bgpd.conf b/tests/topotests/bgp_nexthop_ipv6/r1/bgpd.conf new file mode 100644 index 000000000000..7efa1b79fa5d --- /dev/null +++ b/tests/topotests/bgp_nexthop_ipv6/r1/bgpd.conf @@ -0,0 +1,22 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor fd00:0:2::9 remote-as internal + neighbor fd00:0:2::9 timers 3 10 + address-family ipv4 unicast + redistribute connected route-map RMAP4 + ! + address-family ipv6 unicast + redistribute connected route-map RMAP6 + neighbor fd00:0:2::9 activate + +ip prefix-list RANGE4 seq 10 permit 172.16.0.0/16 le 24 +ip prefix-list RANGE4 seq 20 permit 10.0.0.0/8 ge 32 + +ipv6 prefix-list RANGE6 seq 10 permit fd00:100::0/64 +ipv6 prefix-list RANGE6 seq 20 permit 2001:db8::0/64 ge 128 + +route-map RMAP4 permit 10 + match ip address prefix-list RANGE4 +! +route-map RMAP6 permit 10 + match ipv6 address prefix-list RANGE6 diff --git a/tests/topotests/bgp_nexthop_ipv6/r1/show_bgp_ipv6_step1.json b/tests/topotests/bgp_nexthop_ipv6/r1/show_bgp_ipv6_step1.json new file mode 100644 index 000000000000..f468ae1b3e37 --- /dev/null +++ b/tests/topotests/bgp_nexthop_ipv6/r1/show_bgp_ipv6_step1.json @@ -0,0 +1,210 @@ +{ + "routerId": "10.1.1.1", + "localAS": 65000, + "routes": { + "2001:db8::1/128": [ + { + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::2/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::2", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r2:eth-sw", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8::3/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::3", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::4/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::4", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r4:eth-sw", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8::5/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::5", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-sw", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8::6/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:4::6", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-sw", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:100::/64": [ + { + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:200::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::2", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r2:eth-sw", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:300::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::3", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:400::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::4", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r4:eth-sw", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:500::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::5", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-sw", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:600::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:4::6", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-sw", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_nexthop_ipv6/r1/show_bgp_ipv6_step2.json b/tests/topotests/bgp_nexthop_ipv6/r1/show_bgp_ipv6_step2.json new file mode 100644 index 000000000000..9923edb348da --- /dev/null +++ b/tests/topotests/bgp_nexthop_ipv6/r1/show_bgp_ipv6_step2.json @@ -0,0 +1,162 @@ +{ + "routerId": "10.1.1.1", + "localAS": 65000, + "routes": { + "2001:db8::1/128": [ + { + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::2/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::2", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::3/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::3", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::4/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::4", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::5/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::5", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::6/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:4::6", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:100::/64": [ + { + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:200::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::2", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:300::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::3", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:400::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::4", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:500::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::5", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:600::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:4::6", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_nexthop_ipv6/r1/zebra.conf b/tests/topotests/bgp_nexthop_ipv6/r1/zebra.conf new file mode 100644 index 000000000000..d06a3e194ba3 --- /dev/null +++ b/tests/topotests/bgp_nexthop_ipv6/r1/zebra.conf @@ -0,0 +1,14 @@ +ip forwarding +ipv6 forwarding + +int eth-dummy + ip addr 172.16.1.1/24 + ip addr fd00:100::1/64 + +int eth-sw + ip addr 192.168.2.1/24 + ipv6 address fd00:0:2::1/64 + +int lo + ip addr 10.1.1.1/32 + ipv6 address 2001:db8::1/128 diff --git a/tests/topotests/bgp_nexthop_ipv6/r2/bgpd.conf b/tests/topotests/bgp_nexthop_ipv6/r2/bgpd.conf new file mode 100644 index 000000000000..4d4ae44e284b --- /dev/null +++ b/tests/topotests/bgp_nexthop_ipv6/r2/bgpd.conf @@ -0,0 +1,22 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor fd00:0:2::9 remote-as internal + neighbor fd00:0:2::9 timers 3 10 + address-family ipv4 unicast + redistribute connected route-map RMAP4 + ! + address-family ipv6 unicast + redistribute connected route-map RMAP6 + neighbor fd00:0:2::9 activate + +ip prefix-list RANGE4 seq 10 permit 172.16.0.0/16 le 24 +ip prefix-list RANGE4 seq 20 permit 10.0.0.0/8 ge 32 + +ipv6 prefix-list RANGE6 seq 10 permit fd00:200::0/64 +ipv6 prefix-list RANGE6 seq 20 permit 2001:db8::0/64 ge 128 + +route-map RMAP4 permit 10 + match ip address prefix-list RANGE4 +! +route-map RMAP6 permit 10 + match ipv6 address prefix-list RANGE6 diff --git a/tests/topotests/bgp_nexthop_ipv6/r2/show_bgp_ipv6_step1.json b/tests/topotests/bgp_nexthop_ipv6/r2/show_bgp_ipv6_step1.json new file mode 100644 index 000000000000..824db383a9ef --- /dev/null +++ b/tests/topotests/bgp_nexthop_ipv6/r2/show_bgp_ipv6_step1.json @@ -0,0 +1,210 @@ +{ + "routerId": "10.2.2.2", + "localAS": 65000, + "routes": { + "2001:db8::1/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::1", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r1:eth-sw", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8::2/128": [ + { + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::3/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::3", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::4/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::4", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r4:eth-sw", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8::5/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::5", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-sw", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8::6/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:4::6", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-sw", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:100::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::1", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r1:eth-sw", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:200::/64": [ + { + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:300::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::3", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:400::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::4", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r4:eth-sw", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:500::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::5", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-sw", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:600::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:4::6", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-sw", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_nexthop_ipv6/r2/show_bgp_ipv6_step2.json b/tests/topotests/bgp_nexthop_ipv6/r2/show_bgp_ipv6_step2.json new file mode 100644 index 000000000000..bb2efa16d9e8 --- /dev/null +++ b/tests/topotests/bgp_nexthop_ipv6/r2/show_bgp_ipv6_step2.json @@ -0,0 +1,162 @@ +{ + "routerId": "10.2.2.2", + "localAS": 65000, + "routes": { + "2001:db8::1/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::1", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::2/128": [ + { + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::3/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::3", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::4/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::4", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::5/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::5", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::6/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:4::6", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:100::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::1", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:200::/64": [ + { + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:300::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::3", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:400::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::4", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:500::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::5", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:600::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:4::6", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_nexthop_ipv6/r2/zebra.conf b/tests/topotests/bgp_nexthop_ipv6/r2/zebra.conf new file mode 100644 index 000000000000..de8746df98f8 --- /dev/null +++ b/tests/topotests/bgp_nexthop_ipv6/r2/zebra.conf @@ -0,0 +1,14 @@ +ip forwarding +ipv6 forwarding + +int eth-dummy + ip addr 172.16.2.2/24 + ip addr fd00:200::1/64 + +int eth-sw + ip addr 192.168.2.2/24 + ipv6 address fd00:0:2::2/64 + +int lo + ip addr 10.2.2.2/32 + ipv6 address 2001:db8::2/128 \ No newline at end of file diff --git a/tests/topotests/bgp_nexthop_ipv6/r3/exabgp.cfg b/tests/topotests/bgp_nexthop_ipv6/r3/exabgp.cfg new file mode 100644 index 000000000000..373169bcbab4 --- /dev/null +++ b/tests/topotests/bgp_nexthop_ipv6/r3/exabgp.cfg @@ -0,0 +1,16 @@ +neighbor fd00:0:2::9 { + router-id 10.3.3.3; + local-address fd00:0:2::3; + local-as 65000; + peer-as 65000; + + family { + ipv6 unicast; + } + + static { + route fd00:300::0/64 next-hop fd00:0:2::3; + route 2001:db8::3/128 next-hop fd00:0:2::3; + } + hold-time 10; +} diff --git a/tests/topotests/bgp_nexthop_ipv6/r3/show_bgp_ipv6_step1.json b/tests/topotests/bgp_nexthop_ipv6/r3/show_bgp_ipv6_step1.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/bgp_nexthop_ipv6/r3/show_bgp_ipv6_step2.json b/tests/topotests/bgp_nexthop_ipv6/r3/show_bgp_ipv6_step2.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/bgp_nexthop_ipv6/r4/bgpd.conf b/tests/topotests/bgp_nexthop_ipv6/r4/bgpd.conf new file mode 100644 index 000000000000..b14c9bace4a6 --- /dev/null +++ b/tests/topotests/bgp_nexthop_ipv6/r4/bgpd.conf @@ -0,0 +1,22 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor fd00:0:2::9 remote-as external + neighbor fd00:0:2::9 timers 3 10 + address-family ipv4 unicast + redistribute connected route-map RMAP4 + ! + address-family ipv6 unicast + redistribute connected route-map RMAP6 + neighbor fd00:0:2::9 activate + +ip prefix-list RANGE4 seq 10 permit 172.16.0.0/16 le 24 +ip prefix-list RANGE4 seq 20 permit 10.0.0.0/8 ge 32 + +ipv6 prefix-list RANGE6 seq 10 permit fd00:400::0/64 +ipv6 prefix-list RANGE6 seq 20 permit 2001:db8::0/64 ge 128 + +route-map RMAP4 permit 10 + match ip address prefix-list RANGE4 +! +route-map RMAP6 permit 10 + match ipv6 address prefix-list RANGE6 diff --git a/tests/topotests/bgp_nexthop_ipv6/r4/show_bgp_ipv6_step1.json b/tests/topotests/bgp_nexthop_ipv6/r4/show_bgp_ipv6_step1.json new file mode 100644 index 000000000000..dd8603f4e321 --- /dev/null +++ b/tests/topotests/bgp_nexthop_ipv6/r4/show_bgp_ipv6_step1.json @@ -0,0 +1,210 @@ +{ + "routerId": "10.4.4.4", + "localAS": 65001, + "routes": { + "2001:db8::1/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::1", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r1:eth-sw", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8::2/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::2", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r2:eth-sw", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8::3/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::3", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::4/128": [ + { + "nexthops": [ + { + "ip": "::", + "hostname": "r4", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::5/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::9", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-sw", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8::6/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::9", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-sw", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:100::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::1", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r1:eth-sw", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:200::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::2", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r2:eth-sw", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:300::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::3", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:400::/64": [ + { + "nexthops": [ + { + "ip": "::", + "hostname": "r4", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:500::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::9", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-sw", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:600::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::9", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-sw", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_nexthop_ipv6/r4/show_bgp_ipv6_step2.json b/tests/topotests/bgp_nexthop_ipv6/r4/show_bgp_ipv6_step2.json new file mode 100644 index 000000000000..5506f07f29c6 --- /dev/null +++ b/tests/topotests/bgp_nexthop_ipv6/r4/show_bgp_ipv6_step2.json @@ -0,0 +1,186 @@ +{ + "routerId": "10.4.4.4", + "localAS": 65001, + "routes": { + "2001:db8::1/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::1", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::2/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::2", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::3/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::3", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::4/128": [ + { + "nexthops": [ + { + "ip": "::", + "hostname": "r4", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::5/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::9", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-sw", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8::6/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::9", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-sw", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:100::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::1", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:200::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::2", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:300::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::3", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:400::/64": [ + { + "nexthops": [ + { + "ip": "::", + "hostname": "r4", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:500::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::9", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-sw", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:600::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::9", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-sw", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_nexthop_ipv6/r4/zebra.conf b/tests/topotests/bgp_nexthop_ipv6/r4/zebra.conf new file mode 100644 index 000000000000..edc39684e1f5 --- /dev/null +++ b/tests/topotests/bgp_nexthop_ipv6/r4/zebra.conf @@ -0,0 +1,14 @@ +ip forwarding +ipv6 forwarding + +int eth-dummy + ip addr 172.16.4.4/24 + ip addr fd00:400::4/64 + +int eth-sw + ip addr 192.168.2.4/24 + ipv6 address fd00:0:2::4/64 + +int lo + ip addr 10.4.4.4/32 + ipv6 address 2001:db8::4/128 \ No newline at end of file diff --git a/tests/topotests/bgp_nexthop_ipv6/r5/bgpd.conf b/tests/topotests/bgp_nexthop_ipv6/r5/bgpd.conf new file mode 100644 index 000000000000..becea2bbe648 --- /dev/null +++ b/tests/topotests/bgp_nexthop_ipv6/r5/bgpd.conf @@ -0,0 +1,22 @@ +router bgp 65002 + no bgp ebgp-requires-policy + neighbor fd00:0:3::9 remote-as external + neighbor fd00:0:3::9 timers 3 10 + address-family ipv4 unicast + redistribute connected route-map RMAP4 + ! + address-family ipv6 unicast + redistribute connected route-map RMAP6 + neighbor fd00:0:3::9 activate + +ip prefix-list RANGE4 seq 10 permit 172.16.0.0/16 le 24 +ip prefix-list RANGE4 seq 20 permit 10.0.0.0/8 ge 32 + +ipv6 prefix-list RANGE6 seq 10 permit fd00:500::0/64 +ipv6 prefix-list RANGE6 seq 20 permit 2001:db8::0/64 ge 128 + +route-map RMAP4 permit 10 + match ip address prefix-list RANGE4 +! +route-map RMAP6 permit 10 + match ipv6 address prefix-list RANGE6 diff --git a/tests/topotests/bgp_nexthop_ipv6/r5/show_bgp_ipv6_step1.json b/tests/topotests/bgp_nexthop_ipv6/r5/show_bgp_ipv6_step1.json new file mode 100644 index 000000000000..88e3efb61757 --- /dev/null +++ b/tests/topotests/bgp_nexthop_ipv6/r5/show_bgp_ipv6_step1.json @@ -0,0 +1,210 @@ +{ + "routerId": "10.5.5.5", + "localAS": 65002, + "routes": { + "2001:db8::1/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::9", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-r5", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8::2/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::9", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-r5", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8::3/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::9", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::4/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::9", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-r5", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8::5/128": [ + { + "nexthops": [ + { + "ip": "::", + "hostname": "r5", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::6/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::9", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-r5", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:100::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::9", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-r5", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:200::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::9", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-r5", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:300::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::9", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:400::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::9", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-r5", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:500::/64": [ + { + "nexthops": [ + { + "ip": "::", + "hostname": "r5", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:600::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::9", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-r5", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_nexthop_ipv6/r5/show_bgp_ipv6_step2.json b/tests/topotests/bgp_nexthop_ipv6/r5/show_bgp_ipv6_step2.json new file mode 100644 index 000000000000..afcf7c3ffc5a --- /dev/null +++ b/tests/topotests/bgp_nexthop_ipv6/r5/show_bgp_ipv6_step2.json @@ -0,0 +1,186 @@ +{ + "routerId": "10.5.5.5", + "localAS": 65002, + "routes": { + "2001:db8::1/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::9", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::2/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::9", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::3/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::9", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::4/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::9", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-r5", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8::5/128": [ + { + "nexthops": [ + { + "ip": "::", + "hostname": "r5", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::6/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::9", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-r5", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:100::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::9", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:200::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::9", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:300::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::9", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:400::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::9", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-r5", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:500::/64": [ + { + "nexthops": [ + { + "ip": "::", + "hostname": "r5", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:600::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::9", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-r5", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_nexthop_ipv6/r5/zebra.conf b/tests/topotests/bgp_nexthop_ipv6/r5/zebra.conf new file mode 100644 index 000000000000..d278889ed908 --- /dev/null +++ b/tests/topotests/bgp_nexthop_ipv6/r5/zebra.conf @@ -0,0 +1,14 @@ +ip forwarding +ipv6 forwarding + +int eth-dummy + ip addr 172.16.5.5/24 + ip addr fd00:500::5/64 + +int eth-rr + ip addr 192.168.3.5/24 + ipv6 address fd00:0:3::5/64 + +int lo + ip addr 10.5.5.5/32 + ipv6 address 2001:db8::5/128 diff --git a/tests/topotests/bgp_nexthop_ipv6/r6/bgpd.conf b/tests/topotests/bgp_nexthop_ipv6/r6/bgpd.conf new file mode 100644 index 000000000000..801736ab988e --- /dev/null +++ b/tests/topotests/bgp_nexthop_ipv6/r6/bgpd.conf @@ -0,0 +1,22 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor fd00:0:4::9 remote-as internal + neighbor fd00:0:4::9 timers 3 10 + address-family ipv4 unicast + redistribute connected route-map RMAP4 + ! + address-family ipv6 unicast + redistribute connected route-map RMAP6 + neighbor fd00:0:4::9 activate + +ip prefix-list RANGE4 seq 10 permit 172.16.0.0/16 le 24 +ip prefix-list RANGE4 seq 20 permit 10.0.0.0/8 ge 32 + +ipv6 prefix-list RANGE6 seq 10 permit fd00:600::0/64 +ipv6 prefix-list RANGE6 seq 20 permit 2001:db8::0/64 ge 128 + +route-map RMAP4 permit 10 + match ip address prefix-list RANGE4 +! +route-map RMAP6 permit 10 + match ipv6 address prefix-list RANGE6 diff --git a/tests/topotests/bgp_nexthop_ipv6/r6/show_bgp_ipv6_step1.json b/tests/topotests/bgp_nexthop_ipv6/r6/show_bgp_ipv6_step1.json new file mode 100644 index 000000000000..1407eca359f1 --- /dev/null +++ b/tests/topotests/bgp_nexthop_ipv6/r6/show_bgp_ipv6_step1.json @@ -0,0 +1,210 @@ +{ + "routerId": "10.6.6.6", + "localAS": 65000, + "routes": { + "2001:db8::1/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::1", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-r6", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8::2/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::2", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-r6", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8::3/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::3", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::4/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::4", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-r6", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8::5/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::5", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-r6", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8::6/128": [ + { + "nexthops": [ + { + "ip": "::", + "hostname": "r6", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:100::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::1", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-r6", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:200::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::2", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-r6", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:300::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::3", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:400::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::4", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-r6", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:500::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::5", + "hostname": "rr", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:rr:eth-r6", + "hostname": "rr", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:600::/64": [ + { + "nexthops": [ + { + "ip": "::", + "hostname": "r6", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_nexthop_ipv6/r6/show_bgp_ipv6_step2.json b/tests/topotests/bgp_nexthop_ipv6/r6/show_bgp_ipv6_step2.json new file mode 100644 index 000000000000..cd48dd4697ce --- /dev/null +++ b/tests/topotests/bgp_nexthop_ipv6/r6/show_bgp_ipv6_step2.json @@ -0,0 +1,162 @@ +{ + "routerId": "10.6.6.6", + "localAS": 65000, + "routes": { + "2001:db8::1/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::1", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::2/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::2", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::3/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::3", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::4/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::4", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::5/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::5", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::6/128": [ + { + "nexthops": [ + { + "ip": "::", + "hostname": "r6", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:100::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::1", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:200::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::2", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:300::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::3", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:400::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::4", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:500::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::5", + "hostname": "rr", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:600::/64": [ + { + "nexthops": [ + { + "ip": "::", + "hostname": "r6", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_nexthop_ipv6/r6/zebra.conf b/tests/topotests/bgp_nexthop_ipv6/r6/zebra.conf new file mode 100644 index 000000000000..84bbb95a833a --- /dev/null +++ b/tests/topotests/bgp_nexthop_ipv6/r6/zebra.conf @@ -0,0 +1,14 @@ +ip forwarding +ipv6 forwarding + +int eth-dummy + ip addr 172.16.6.6/24 + ip addr fd00:600::6/64 + +int eth-rr + ip addr 192.168.4.6/24 + ipv6 address fd00:0:4::6/64 + +int lo + ip addr 10.6.6.6/32 + ipv6 address 2001:db8::6/128 diff --git a/tests/topotests/bgp_nexthop_ipv6/rr/bgpd.conf b/tests/topotests/bgp_nexthop_ipv6/rr/bgpd.conf new file mode 100644 index 000000000000..705ae78b8e1e --- /dev/null +++ b/tests/topotests/bgp_nexthop_ipv6/rr/bgpd.conf @@ -0,0 +1,35 @@ +router bgp 65000 + no bgp ebgp-requires-policy + neighbor fd00:0:2::1 remote-as internal + neighbor fd00:0:2::1 timers 3 10 + neighbor fd00:0:2::2 remote-as internal + neighbor fd00:0:2::2 timers 3 10 + neighbor fd00:0:2::3 remote-as internal + neighbor fd00:0:2::3 timers 3 10 + neighbor fd00:0:2::4 remote-as external + neighbor fd00:0:2::4 timers 3 10 + neighbor fd00:0:3::5 remote-as external + neighbor fd00:0:3::5 timers 3 10 + neighbor fd00:0:4::6 remote-as internal + neighbor fd00:0:4::6 timers 3 10 + address-family ipv4 unicast + neighbor fd00:0:2::1 route-reflector-client + neighbor fd00:0:2::2 route-reflector-client + neighbor fd00:0:2::3 route-reflector-client + neighbor fd00:0:4::6 route-reflector-client + address-family ipv6 unicast + neighbor fd00:0:2::1 route-reflector-client + neighbor fd00:0:2::1 nexthop-local unchanged + neighbor fd00:0:2::1 activate + neighbor fd00:0:2::2 route-reflector-client + neighbor fd00:0:2::2 nexthop-local unchanged + neighbor fd00:0:2::2 activate + neighbor fd00:0:2::3 route-reflector-client + neighbor fd00:0:2::3 nexthop-local unchanged + neighbor fd00:0:2::3 activate + neighbor fd00:0:2::4 nexthop-local unchanged + neighbor fd00:0:2::4 activate + neighbor fd00:0:3::5 nexthop-local unchanged + neighbor fd00:0:3::5 activate + neighbor fd00:0:4::6 nexthop-local unchanged + neighbor fd00:0:4::6 activate diff --git a/tests/topotests/bgp_nexthop_ipv6/rr/show_bgp_ipv6_step1.json b/tests/topotests/bgp_nexthop_ipv6/rr/show_bgp_ipv6_step1.json new file mode 100644 index 000000000000..c8b8e1e89356 --- /dev/null +++ b/tests/topotests/bgp_nexthop_ipv6/rr/show_bgp_ipv6_step1.json @@ -0,0 +1,220 @@ +{ + "routerId": "10.9.9.9", + "localAS": 65000, + "routes": { + "2001:db8::1/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::1", + "hostname": "r1", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r1:eth-sw", + "hostname": "r1", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8::2/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::2", + "hostname": "r2", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r2:eth-sw", + "hostname": "r2", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8::3/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::3", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::4/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::4", + "hostname": "r4", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r4:eth-sw", + "hostname": "r4", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8::5/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::5", + "hostname": "r5", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r5:eth-rr", + "hostname": "r5", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8::6/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:4::6", + "hostname": "r6", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r6:eth-rr", + "hostname": "r6", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:100::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::1", + "hostname": "r1", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r1:eth-sw", + "hostname": "r1", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:200::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::2", + "hostname": "r2", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r2:eth-sw", + "hostname": "r2", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:300::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::3", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:400::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::4", + "hostname": "r4", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r4:eth-sw", + "hostname": "r4", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:500::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::5", + "hostname": "r5", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r5:eth-rr", + "hostname": "r5", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:600::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:4::6", + "hostname": "r6", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r6:eth-rr", + "hostname": "r6", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_nexthop_ipv6/rr/show_bgp_ipv6_step2.json b/tests/topotests/bgp_nexthop_ipv6/rr/show_bgp_ipv6_step2.json new file mode 100644 index 000000000000..c8b8e1e89356 --- /dev/null +++ b/tests/topotests/bgp_nexthop_ipv6/rr/show_bgp_ipv6_step2.json @@ -0,0 +1,220 @@ +{ + "routerId": "10.9.9.9", + "localAS": 65000, + "routes": { + "2001:db8::1/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::1", + "hostname": "r1", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r1:eth-sw", + "hostname": "r1", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8::2/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::2", + "hostname": "r2", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r2:eth-sw", + "hostname": "r2", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8::3/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::3", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8::4/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::4", + "hostname": "r4", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r4:eth-sw", + "hostname": "r4", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8::5/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::5", + "hostname": "r5", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r5:eth-rr", + "hostname": "r5", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8::6/128": [ + { + "nexthops": [ + { + "ip": "fd00:0:4::6", + "hostname": "r6", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r6:eth-rr", + "hostname": "r6", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:100::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::1", + "hostname": "r1", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r1:eth-sw", + "hostname": "r1", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:200::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::2", + "hostname": "r2", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r2:eth-sw", + "hostname": "r2", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:300::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::3", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:400::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:2::4", + "hostname": "r4", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r4:eth-sw", + "hostname": "r4", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:500::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:3::5", + "hostname": "r5", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r5:eth-rr", + "hostname": "r5", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "fd00:600::/64": [ + { + "nexthops": [ + { + "ip": "fd00:0:4::6", + "hostname": "r6", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r6:eth-rr", + "hostname": "r6", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_nexthop_ipv6/rr/show_bgp_ipv6_summary.json b/tests/topotests/bgp_nexthop_ipv6/rr/show_bgp_ipv6_summary.json new file mode 100644 index 000000000000..ceac91215dad --- /dev/null +++ b/tests/topotests/bgp_nexthop_ipv6/rr/show_bgp_ipv6_summary.json @@ -0,0 +1,6 @@ +{ + "ipv6Unicast": { + "failedPeers": 0, + "totalPeers": 6 + } +} diff --git a/tests/topotests/bgp_nexthop_ipv6/rr/zebra.conf b/tests/topotests/bgp_nexthop_ipv6/rr/zebra.conf new file mode 100644 index 000000000000..21d44f3a15fa --- /dev/null +++ b/tests/topotests/bgp_nexthop_ipv6/rr/zebra.conf @@ -0,0 +1,18 @@ +ip forwarding +ipv6 forwarding + +int eth-sw + ip addr 192.168.2.9/24 + ipv6 address fd00:0:2::9/64 + +int eth-r5 + ip addr 192.168.3.9/24 + ipv6 address fd00:0:3::9/64 + +int eth-r6 + ip addr 192.168.4.9/24 + ipv6 address fd00:0:4::9/64 + +int lo + ip addr 10.9.9.9/32 + ipv6 address 2001:db8::9/128 diff --git a/tests/topotests/bgp_nexthop_ipv6/test_bgp_nexthop_ipv6_topo1.py b/tests/topotests/bgp_nexthop_ipv6/test_bgp_nexthop_ipv6_topo1.py new file mode 100644 index 000000000000..e478139eb1e6 --- /dev/null +++ b/tests/topotests/bgp_nexthop_ipv6/test_bgp_nexthop_ipv6_topo1.py @@ -0,0 +1,361 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_nexthop_ipv6_topo1.py +# +# Copyright (c) 2024 by +# Cumulus Networks, Inc. +# 6WIND S.A. +# + +""" +Ensure that BGP ipv6 nexthops are correct +""" + +import os +import sys +import pytest +from functools import partial +import json + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + """ + All peers are FRR BGP peers except r3 that is a exabgp peer. + rr is a route-reflector for AS 65000 iBGP peers. + Exabgp does not send any IPv6 Link-Local nexthop + + iBGP peers | eBGP peers + | + AS 65000 | + | + +---+ | + | r6| | + +---+ | + | | + fd00:0:3::0/64 | + | | AS 65002 + +---+ | +---+ + |rr |----fd00:0:4::0/64---| r5| + +---+ |_________+---+ + | | +---+ + fd00:0:2::0/64----------------| r4| + / | \ | +---+ + +---+ +---+ +---+ | AS 65001 + | r1| | r2| |r3 | | + +---+ +---+ +---+ + """ + + def connect_routers(tgen, left, right): + for rname in [left, right]: + if rname not in tgen.routers().keys(): + tgen.add_router(rname) + + switch = tgen.add_switch("s-{}-{}".format(left, right)) + switch.add_link(tgen.gears[left], nodeif="eth-{}".format(right)) + switch.add_link(tgen.gears[right], nodeif="eth-{}".format(left)) + + def connect_switchs(tgen, rname, switch): + if rname not in tgen.routers().keys(): + tgen.add_router(rname) + + switch.add_link(tgen.gears[rname], nodeif="eth-{}".format(switch.name)) + + def connect_dummy(tgen, rname, switch): + if rname not in tgen.routers().keys(): + tgen.add_router(rname) + + switch.add_link(tgen.gears[rname], nodeif="eth-dummy") + + # sw_du switch is for a dummy interface (for local network) + for i in range(1, 7): + if i == 3: + # r3 is an exabgp peer + continue + sw_du = tgen.add_switch("sw%s" % i) + connect_dummy(tgen, "r%s" % i, sw_du) + + # sw switch is for interconnecting peers on the same subnet + sw = tgen.add_switch("sw") + connect_switchs(tgen, "rr", sw) + connect_switchs(tgen, "r1", sw) + connect_switchs(tgen, "r2", sw) + connect_switchs(tgen, "r4", sw) + + # directly connected without switch routers + connect_routers(tgen, "rr", "r5") + connect_routers(tgen, "rr", "r6") + + ## Add iBGP ExaBGP neighbor + peer_ip = "fd00:0:2::3" ## peer + peer_route = "via fd00:0:2::9" ## router + r3 = tgen.add_exabgp_peer("r3", ip=peer_ip, defaultRoute=peer_route) + sw.add_link(r3) + + +##################################################### +## +## Tests starting +## +##################################################### + + +def setup_module(module): + "Setup topology" + tgen = Topogen(build_topo, module.__name__) + tgen.start_topology() + + # This is a sample of configuration loading. + router_list = tgen.routers() + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + # Start r3 exabgp peer + r3 = tgen.gears["r3"] + r3.start(os.path.join(CWD, "r3"), os.path.join(CWD, "exabgp.env")) + + +def get_link_local(rname, ifname, cache): + ip = cache.get(rname, {}).get(ifname) + if ip: + return ip + + tgen = get_topogen() + out = tgen.gears[rname].vtysh_cmd("show interface %s json" % ifname, isjson=True) + for address in out[ifname]["ipAddresses"]: + if not address["address"].startswith("fe80::"): + continue + ip = address["address"].split("/")[0] + cache.setdefault(rname, {})[ifname] = ip + return ip + + +def replace_link_local(expected, cache): + for prefix, prefix_info in expected.get("routes", {}).items(): + for nexthop in prefix_info[0].get("nexthops", []): + ip = nexthop.get("ip", "") + if not ip.startswith("link-local:"): + continue + rname = ip.split(":")[1] + ifname = ip.split(":")[2] + ip = get_link_local(rname, ifname, cache) + nexthop["ip"] = ip + + +def check_rr_sub_group(expected): + tgen = get_topogen() + + rr = tgen.gears["rr"] + + output = json.loads(rr.vtysh_cmd("show bgp update-groups json")) + actual = [ + subgroup["peers"] + for entry in output.get("default", {}).values() + for subgroup in entry["subGroup"] + ] + + return topotest.json_cmp(actual, expected) + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def test_converge_protocols(): + "Wait for protocol convergence" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + rr = tgen.gears["rr"] + ref_file = "{}/{}/show_bgp_ipv6_summary.json".format(CWD, rr.name) + expected = json.loads(open(ref_file).read()) + + test_func = partial( + topotest.router_json_cmp, + rr, + "show bgp ipv6 summary json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = "{}: BGP convergence".format(rr.name) + assert res is None, assertmsg + + +def test_bgp_ipv6_table_step1(): + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global link_local_cache + link_local_cache = {} + router_list = tgen.routers().values() + for router in router_list: + # router.cmd("vtysh -c 'sh bgp ipv6 json' >/tmp/show_bgp_ipv6_%s.json" % router.name) + ref_file = "{}/{}/show_bgp_ipv6_step1.json".format(CWD, router.name) + expected = json.loads(open(ref_file).read()) + replace_link_local(expected, link_local_cache) + + test_func = partial( + topotest.router_json_cmp, + router, + "show bgp ipv6 unicast json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = "{}: BGP IPv6 Nexthop failure".format(router.name) + assert res is None, assertmsg + + # check rr sub-groups + expected = [ + ["fd00:0:2::1", "fd00:0:2::2"], + ["fd00:0:2::3"], + ["fd00:0:2::4"], + ["fd00:0:3::5"], + ["fd00:0:4::6"], + ] + + test_func = partial(check_rr_sub_group, expected) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Peer group split failed" + + +def test_bgp_ipv6_table_step2(): + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + rr = tgen.gears["rr"] + rr.vtysh_cmd( + """ +configure terminal +router bgp 65000 + address-family ipv6 unicast + no neighbor fd00:0:2::1 nexthop-local unchanged + no neighbor fd00:0:2::2 nexthop-local unchanged + no neighbor fd00:0:2::3 nexthop-local unchanged + no neighbor fd00:0:2::4 nexthop-local unchanged + no neighbor fd00:0:3::5 nexthop-local unchanged + no neighbor fd00:0:4::6 nexthop-local unchanged +""" + ) + + router_list = tgen.routers().values() + for router in router_list: + # router.cmd("vtysh -c 'sh bgp ipv6 json' >/tmp/show_bgp_ipv6_%s.json" % router.name) + ref_file = "{}/{}/show_bgp_ipv6_step2.json".format(CWD, router.name) + expected = json.loads(open(ref_file).read()) + replace_link_local(expected, link_local_cache) + + test_func = partial( + topotest.router_json_cmp, + router, + "show bgp ipv6 unicast json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = "{}: BGP IPv6 Nexthop failure".format(router.name) + assert res is None, assertmsg + + # check rr sub-groups + expected = [ + ["fd00:0:2::1", "fd00:0:2::2"], + ["fd00:0:2::3"], + ["fd00:0:3::5", "fd00:0:2::4"], + ["fd00:0:4::6"], + ] + + test_func = partial(check_rr_sub_group, expected) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Peer group split failed" + + +def test_bgp_ipv6_table_step3(): + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + rr = tgen.gears["rr"] + rr.vtysh_cmd( + """ +configure terminal +router bgp 65000 + address-family ipv6 unicast + neighbor fd00:0:2::1 nexthop-local unchanged + neighbor fd00:0:2::2 nexthop-local unchanged + neighbor fd00:0:2::3 nexthop-local unchanged + neighbor fd00:0:2::4 nexthop-local unchanged + neighbor fd00:0:3::5 nexthop-local unchanged + neighbor fd00:0:4::6 nexthop-local unchanged +""" + ) + + router_list = tgen.routers().values() + for router in router_list: + # router.cmd("vtysh -c 'sh bgp ipv6 json' >/tmp/show_bgp_ipv6_%s.json" % router.name) + ref_file = "{}/{}/show_bgp_ipv6_step1.json".format(CWD, router.name) + expected = json.loads(open(ref_file).read()) + replace_link_local(expected, link_local_cache) + + test_func = partial( + topotest.router_json_cmp, + router, + "show bgp ipv6 unicast json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = "{}: BGP IPv6 Nexthop failure".format(router.name) + assert res is None, assertmsg + + # check rr sub-groups + expected = [ + ["fd00:0:2::1", "fd00:0:2::2"], + ["fd00:0:2::3"], + ["fd00:0:2::4"], + ["fd00:0:3::5"], + ["fd00:0:4::6"], + ] + + test_func = partial(check_rr_sub_group, expected) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Peer group split failed" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_oad/r1/frr.conf b/tests/topotests/bgp_oad/r1/frr.conf index 39045ba648eb..f00bdfe7d2fb 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 6dd46fbdaadc..b2ea7e0f1987 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 _bgp_converge(): _, 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/bgp_route_server_client/exabgp.env b/tests/topotests/bgp_route_server_client/exabgp.env new file mode 100644 index 000000000000..28e642360a39 --- /dev/null +++ b/tests/topotests/bgp_route_server_client/exabgp.env @@ -0,0 +1,53 @@ +[exabgp.api] +encoder = text +highres = false +respawn = false +socket = '' + +[exabgp.bgp] +openwait = 60 + +[exabgp.cache] +attributes = true +nexthops = true + +[exabgp.daemon] +daemonize = true +pid = '/var/run/exabgp/exabgp.pid' +user = 'exabgp' +##daemonize = false + +[exabgp.log] +all = false +configuration = true +daemon = true +destination = '/var/log/exabgp.log' +enable = true +level = INFO +message = false +network = true +packets = false +parser = false +processes = true +reactor = true +rib = false +routes = false +short = false +timers = false + +[exabgp.pdb] +enable = false + +[exabgp.profile] +enable = false +file = '' + +[exabgp.reactor] +speed = 1.0 + +[exabgp.tcp] +acl = false +bind = '' +delay = 0 +once = false +port = 179 diff --git a/tests/topotests/bgp_route_server_client/r1/show_bgp_ipv6_step1.json b/tests/topotests/bgp_route_server_client/r1/show_bgp_ipv6_step1.json new file mode 100644 index 000000000000..387d7b3374b7 --- /dev/null +++ b/tests/topotests/bgp_route_server_client/r1/show_bgp_ipv6_step1.json @@ -0,0 +1,95 @@ +{ + "routerId": "10.10.10.1", + "localAS": 65001, + "routes": { + "2001:db8:1::/64": [ + { + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + }, + { + "nexthops": [ + { + "ip": "2001:db8:1::4", + "hostname": "r2", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:3::/64": [ + { + "nexthops": [ + { + "ip": "2001:db8:3::2", + "hostname": "r2", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:f::1/128": [ + { + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:f::3/128": [ + { + "nexthops": [ + { + "ip": "2001:db8:3::2", + "hostname": "r2", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:f::4/128": [ + { + "nexthops": [ + { + "ip": "2001:db8:1::3", + "hostname": "r2", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:f::5/128": [ + { + "nexthops": [ + { + "ip": "2001:db8:1::4", + "hostname": "r2", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_route_server_client/r1/show_bgp_ipv6_step2.json b/tests/topotests/bgp_route_server_client/r1/show_bgp_ipv6_step2.json new file mode 100644 index 000000000000..f9e68b897723 --- /dev/null +++ b/tests/topotests/bgp_route_server_client/r1/show_bgp_ipv6_step2.json @@ -0,0 +1,113 @@ +{ + "routerId": "10.10.10.1", + "localAS": 65001, + "routes": { + "2001:db8:1::/64": [ + { + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + }, + { + "nexthops": [ + { + "ip": "2001:db8:1::4", + "hostname": "r2", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:3::/64": [ + { + "nexthops": [ + { + "ip": "2001:db8:3::2", + "hostname": "r2", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r2:r2-eth0", + "hostname": "r2", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8:f::1/128": [ + { + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:f::3/128": [ + { + "nexthops": [ + { + "ip": "2001:db8:3::2", + "hostname": "r2", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r2:r2-eth0", + "hostname": "r2", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8:f::4/128": [ + { + "nexthops": [ + { + "ip": "2001:db8:1::3", + "hostname": "r2", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r4:r4-eth0", + "hostname": "r2", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8:f::5/128": [ + { + "nexthops": [ + { + "ip": "2001:db8:1::4", + "hostname": "r2", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_route_server_client/r2/bgpd.conf b/tests/topotests/bgp_route_server_client/r2/bgpd.conf index 3b0a24b8ba5b..19607660f98f 100644 --- a/tests/topotests/bgp_route_server_client/r2/bgpd.conf +++ b/tests/topotests/bgp_route_server_client/r2/bgpd.conf @@ -4,14 +4,24 @@ router bgp 65000 view RS neighbor 2001:db8:1::2 remote-as external neighbor 2001:db8:1::2 timers 3 10 neighbor 2001:db8:1::2 timers connect 5 + neighbor 2001:db8:1::3 remote-as external + neighbor 2001:db8:1::3 timers 3 10 + neighbor 2001:db8:1::3 timers connect 5 + neighbor 2001:db8:1::4 remote-as external + neighbor 2001:db8:1::4 timers 3 10 + neighbor 2001:db8:1::4 timers connect 5 neighbor 2001:db8:3::2 remote-as external neighbor 2001:db8:3::2 timers 3 10 neighbor 2001:db8:3::2 timers connect 5 address-family ipv6 unicast redistribute connected neighbor 2001:db8:1::2 activate + neighbor 2001:db8:1::3 activate + neighbor 2001:db8:1::4 activate neighbor 2001:db8:3::2 activate neighbor 2001:db8:1::2 route-server-client + neighbor 2001:db8:1::3 route-server-client + neighbor 2001:db8:1::4 route-server-client neighbor 2001:db8:3::2 route-server-client exit-address-family ! diff --git a/tests/topotests/bgp_route_server_client/r2/show_bgp_ipv6_step1.json b/tests/topotests/bgp_route_server_client/r2/show_bgp_ipv6_step1.json new file mode 100644 index 000000000000..c2f31f8c32ed --- /dev/null +++ b/tests/topotests/bgp_route_server_client/r2/show_bgp_ipv6_step1.json @@ -0,0 +1,208 @@ +{ + "routerId": "10.10.10.2", + "localAS": 65000, + "routes": { + "2001:db8:1::/64": [ + { + "nexthops": [ + { + "ip": "2001:db8:1::4", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + }, + { + "nexthops": [ + { + "ip": "2001:db8:1::2", + "hostname": "r1", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r1:r1-eth0", + "hostname": "r1", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + }, + { + "nexthops": [ + { + "ip": "2001:db8:1::3", + "hostname": "r4", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r4:r4-eth0", + "hostname": "r4", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8:3::/64": [ + { + "nexthops": [ + { + "ip": "2001:db8:3::2", + "hostname": "r3", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r3:r3-eth0", + "hostname": "r3", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8:f::1/128": [ + { + "nexthops": [ + { + "ip": "2001:db8:1::2", + "hostname": "r1", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r1:r1-eth0", + "hostname": "r1", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + }, + { + "nexthops": [ + { + "ip": "2001:db8:1::3", + "hostname": "r4", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r4:r4-eth0", + "hostname": "r4", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8:f::3/128": [ + { + "nexthops": [ + { + "ip": "2001:db8:3::2", + "hostname": "r3", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r3:r3-eth0", + "hostname": "r3", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8:f::4/128": [ + { + "nexthops": [ + { + "ip": "2001:db8:1::3", + "hostname": "r4", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r4:r4-eth0", + "hostname": "r4", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + }, + { + "nexthops": [ + { + "ip": "2001:db8:1::2", + "hostname": "r1", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r1:r1-eth0", + "hostname": "r1", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8:f::5/128": [ + { + "nexthops": [ + { + "ip": "2001:db8:1::4", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + }, + { + "nexthops": [ + { + "ip": "2001:db8:1::2", + "hostname": "r1", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r1:r1-eth0", + "hostname": "r1", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + }, + { + "nexthops": [ + { + "ip": "2001:db8:1::3", + "hostname": "r4", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r4:r4-eth0", + "hostname": "r4", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_route_server_client/r2/show_bgp_ipv6_step2.json b/tests/topotests/bgp_route_server_client/r2/show_bgp_ipv6_step2.json new file mode 100644 index 000000000000..c2f31f8c32ed --- /dev/null +++ b/tests/topotests/bgp_route_server_client/r2/show_bgp_ipv6_step2.json @@ -0,0 +1,208 @@ +{ + "routerId": "10.10.10.2", + "localAS": 65000, + "routes": { + "2001:db8:1::/64": [ + { + "nexthops": [ + { + "ip": "2001:db8:1::4", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + }, + { + "nexthops": [ + { + "ip": "2001:db8:1::2", + "hostname": "r1", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r1:r1-eth0", + "hostname": "r1", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + }, + { + "nexthops": [ + { + "ip": "2001:db8:1::3", + "hostname": "r4", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r4:r4-eth0", + "hostname": "r4", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8:3::/64": [ + { + "nexthops": [ + { + "ip": "2001:db8:3::2", + "hostname": "r3", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r3:r3-eth0", + "hostname": "r3", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8:f::1/128": [ + { + "nexthops": [ + { + "ip": "2001:db8:1::2", + "hostname": "r1", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r1:r1-eth0", + "hostname": "r1", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + }, + { + "nexthops": [ + { + "ip": "2001:db8:1::3", + "hostname": "r4", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r4:r4-eth0", + "hostname": "r4", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8:f::3/128": [ + { + "nexthops": [ + { + "ip": "2001:db8:3::2", + "hostname": "r3", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r3:r3-eth0", + "hostname": "r3", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8:f::4/128": [ + { + "nexthops": [ + { + "ip": "2001:db8:1::3", + "hostname": "r4", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r4:r4-eth0", + "hostname": "r4", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + }, + { + "nexthops": [ + { + "ip": "2001:db8:1::2", + "hostname": "r1", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r1:r1-eth0", + "hostname": "r1", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8:f::5/128": [ + { + "nexthops": [ + { + "ip": "2001:db8:1::4", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + }, + { + "nexthops": [ + { + "ip": "2001:db8:1::2", + "hostname": "r1", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r1:r1-eth0", + "hostname": "r1", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + }, + { + "nexthops": [ + { + "ip": "2001:db8:1::3", + "hostname": "r4", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r4:r4-eth0", + "hostname": "r4", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_route_server_client/r2/show_bgp_ipv6_summary.json b/tests/topotests/bgp_route_server_client/r2/show_bgp_ipv6_summary.json new file mode 100644 index 000000000000..8a42a11c47a9 --- /dev/null +++ b/tests/topotests/bgp_route_server_client/r2/show_bgp_ipv6_summary.json @@ -0,0 +1,6 @@ +{ + "ipv6Unicast": { + "failedPeers": 0, + "totalPeers": 4 + } +} diff --git a/tests/topotests/bgp_route_server_client/r3/bgpd.conf b/tests/topotests/bgp_route_server_client/r3/bgpd.conf index 60a5ffc559a0..f7daba87face 100644 --- a/tests/topotests/bgp_route_server_client/r3/bgpd.conf +++ b/tests/topotests/bgp_route_server_client/r3/bgpd.conf @@ -2,6 +2,7 @@ router bgp 65003 bgp router-id 10.10.10.3 no bgp ebgp-requires-policy + no bgp enforce-first-as neighbor 2001:db8:3::1 remote-as external neighbor 2001:db8:3::1 timers 3 10 neighbor 2001:db8:3::1 timers connect 5 diff --git a/tests/topotests/bgp_route_server_client/r3/show_bgp_ipv6_step1.json b/tests/topotests/bgp_route_server_client/r3/show_bgp_ipv6_step1.json new file mode 100644 index 000000000000..bf8d74801dc3 --- /dev/null +++ b/tests/topotests/bgp_route_server_client/r3/show_bgp_ipv6_step1.json @@ -0,0 +1,84 @@ +{ + "routerId": "10.10.10.3", + "localAS": 65003, + "routes": { + "2001:db8:1::/64": [ + { + "nexthops": [ + { + "ip": "2001:db8:1::4", + "hostname": "r2", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:3::/64": [ + { + "nexthops": [ + { + "ip": "::", + "hostname": "r3", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:f::1/128": [ + { + "nexthops": [ + { + "ip": "2001:db8:1::2", + "hostname": "r2", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:f::3/128": [ + { + "nexthops": [ + { + "ip": "::", + "hostname": "r3", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:f::4/128": [ + { + "nexthops": [ + { + "ip": "2001:db8:1::3", + "hostname": "r2", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:f::5/128": [ + { + "nexthops": [ + { + "ip": "2001:db8:1::4", + "hostname": "r2", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_route_server_client/r3/show_bgp_ipv6_step2.json b/tests/topotests/bgp_route_server_client/r3/show_bgp_ipv6_step2.json new file mode 100644 index 000000000000..31c1eb781611 --- /dev/null +++ b/tests/topotests/bgp_route_server_client/r3/show_bgp_ipv6_step2.json @@ -0,0 +1,96 @@ +{ + "routerId": "10.10.10.3", + "localAS": 65003, + "routes": { + "2001:db8:1::/64": [ + { + "nexthops": [ + { + "ip": "2001:db8:1::4", + "hostname": "r2", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:3::/64": [ + { + "nexthops": [ + { + "ip": "::", + "hostname": "r3", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:f::1/128": [ + { + "nexthops": [ + { + "ip": "2001:db8:1::2", + "hostname": "r2", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r2:r2-eth1", + "hostname": "r2", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8:f::3/128": [ + { + "nexthops": [ + { + "ip": "::", + "hostname": "r3", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:f::4/128": [ + { + "nexthops": [ + { + "ip": "2001:db8:1::3", + "hostname": "r2", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r2:r2-eth1", + "hostname": "r2", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8:f::5/128": [ + { + "nexthops": [ + { + "ip": "2001:db8:1::4", + "hostname": "r2", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_route_server_client/r4/bgpd.conf b/tests/topotests/bgp_route_server_client/r4/bgpd.conf new file mode 100644 index 000000000000..c907d7284e22 --- /dev/null +++ b/tests/topotests/bgp_route_server_client/r4/bgpd.conf @@ -0,0 +1,13 @@ +! +router bgp 65004 + bgp router-id 10.10.10.4 + no bgp ebgp-requires-policy + no bgp enforce-first-as + neighbor 2001:db8:1::1 remote-as external + neighbor 2001:db8:1::1 timers 3 10 + neighbor 2001:db8:1::1 timers connect 5 + address-family ipv6 unicast + redistribute connected + neighbor 2001:db8:1::1 activate + exit-address-family +! diff --git a/tests/topotests/bgp_route_server_client/r4/show_bgp_ipv6_step1.json b/tests/topotests/bgp_route_server_client/r4/show_bgp_ipv6_step1.json new file mode 100644 index 000000000000..5c090d9cf947 --- /dev/null +++ b/tests/topotests/bgp_route_server_client/r4/show_bgp_ipv6_step1.json @@ -0,0 +1,95 @@ +{ + "routerId": "10.10.10.4", + "localAS": 65004, + "routes": { + "2001:db8:1::/64": [ + { + "nexthops": [ + { + "ip": "::", + "hostname": "r4", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + }, + { + "nexthops": [ + { + "ip": "2001:db8:1::4", + "hostname": "r2", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:3::/64": [ + { + "nexthops": [ + { + "ip": "2001:db8:3::2", + "hostname": "r2", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:f::1/128": [ + { + "nexthops": [ + { + "ip": "2001:db8:1::2", + "hostname": "r2", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:f::3/128": [ + { + "nexthops": [ + { + "ip": "2001:db8:3::2", + "hostname": "r2", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:f::4/128": [ + { + "nexthops": [ + { + "ip": "::", + "hostname": "r4", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:f::5/128": [ + { + "nexthops": [ + { + "ip": "2001:db8:1::4", + "hostname": "r2", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_route_server_client/r4/show_bgp_ipv6_step2.json b/tests/topotests/bgp_route_server_client/r4/show_bgp_ipv6_step2.json new file mode 100644 index 000000000000..01db18e926d4 --- /dev/null +++ b/tests/topotests/bgp_route_server_client/r4/show_bgp_ipv6_step2.json @@ -0,0 +1,113 @@ +{ + "routerId": "10.10.10.4", + "localAS": 65004, + "routes": { + "2001:db8:1::/64": [ + { + "nexthops": [ + { + "ip": "::", + "hostname": "r4", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + }, + { + "nexthops": [ + { + "ip": "2001:db8:1::4", + "hostname": "r2", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:3::/64": [ + { + "nexthops": [ + { + "ip": "2001:db8:3::2", + "hostname": "r2", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r2:r2-eth0", + "hostname": "r2", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8:f::1/128": [ + { + "nexthops": [ + { + "ip": "2001:db8:1::2", + "hostname": "r2", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r1:r1-eth0", + "hostname": "r2", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8:f::3/128": [ + { + "nexthops": [ + { + "ip": "2001:db8:3::2", + "hostname": "r2", + "afi": "ipv6", + "scope": "global" + }, + { + "ip": "link-local:r2:r2-eth0", + "hostname": "r2", + "afi": "ipv6", + "scope": "link-local", + "used": true + } + ] + } + ], + "2001:db8:f::4/128": [ + { + "nexthops": [ + { + "ip": "::", + "hostname": "r4", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "2001:db8:f::5/128": [ + { + "nexthops": [ + { + "ip": "2001:db8:1::4", + "hostname": "r2", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_route_server_client/r4/zebra.conf b/tests/topotests/bgp_route_server_client/r4/zebra.conf new file mode 100644 index 000000000000..849884045d98 --- /dev/null +++ b/tests/topotests/bgp_route_server_client/r4/zebra.conf @@ -0,0 +1,7 @@ +! +int lo + ipv6 address 2001:db8:f::4/128 +! +int r4-eth0 + ipv6 address 2001:db8:1::3/64 +! diff --git a/tests/topotests/bgp_route_server_client/r5/exabgp.cfg b/tests/topotests/bgp_route_server_client/r5/exabgp.cfg new file mode 100644 index 000000000000..b151f16caaeb --- /dev/null +++ b/tests/topotests/bgp_route_server_client/r5/exabgp.cfg @@ -0,0 +1,16 @@ +neighbor 2001:db8:1::1{ + router-id 10.10.10.5; + local-address 2001:db8:1::4; + local-as 65005; + peer-as 65000; + + family { + ipv6 unicast; + } + + static { + route 2001:db8:1::0/64 next-hop 2001:db8:1::4; + route 2001:db8:f::5/128 next-hop 2001:db8:1::4; + } + hold-time 10; +} diff --git a/tests/topotests/bgp_route_server_client/test_bgp_route_server_client.py b/tests/topotests/bgp_route_server_client/test_bgp_route_server_client.py index 29d9842d59dd..a6334918dfcb 100644 --- a/tests/topotests/bgp_route_server_client/test_bgp_route_server_client.py +++ b/tests/topotests/bgp_route_server_client/test_bgp_route_server_client.py @@ -13,6 +13,7 @@ import sys import json import pytest +from functools import partial import functools pytestmark = [pytest.mark.bgpd] @@ -26,16 +27,49 @@ def build_topo(tgen): - for routern in range(1, 4): + """ + All peers are FRR BGP peers except r5 that is a exabgp peer. + Exabgp does not send any IPv6 Link-Local nexthop + + r2 is a route-server view RS AS 65000 + Other routers rX has AS 6500X + + +---+ + | r3| + +---+ + | + 2001:db8:3::0/64 + | + eth1 + +---+ + |r2 | + +---+ + eth0 + | + 2001:db8:1::0/64 + / | \ + +---+ +---+ +---+ + | r1| | r4| |r5 | + +---+ +---+ +---+ + """ + + 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"]) + sw1 = tgen.add_switch("s1") + sw1.add_link(tgen.gears["r1"]) + sw1.add_link(tgen.gears["r2"]) + sw1.add_link(tgen.gears["r4"]) + + sw2 = tgen.add_switch("s2") + sw2.add_link(tgen.gears["r2"]) + sw2.add_link(tgen.gears["r3"]) - switch = tgen.add_switch("s2") - switch.add_link(tgen.gears["r2"]) - switch.add_link(tgen.gears["r3"]) + ## Add iBGP ExaBGP neighbor + peer_ip = "2001:db8:1::4" ## peer + peer_route = "via 2001:db8:1::1" ## router + r5 = tgen.add_exabgp_peer("r5", ip=peer_ip, defaultRoute=peer_route) + sw1.add_link(r5) def setup_module(mod): @@ -54,73 +88,225 @@ def setup_module(mod): tgen.start_router() + # Start r5 exabgp peer + r5 = tgen.gears["r5"] + r5.start(os.path.join(CWD, "r5"), os.path.join(CWD, "exabgp.env")) + def teardown_module(mod): tgen = get_topogen() tgen.stop_topology() -def test_bgp_route_server_client(): +def get_link_local(rname, ifname, cache): + ip = cache.get(rname, {}).get(ifname) + if ip: + return ip + tgen = get_topogen() + out = tgen.gears[rname].vtysh_cmd("show interface %s json" % ifname, isjson=True) + for address in out[ifname]["ipAddresses"]: + if not address["address"].startswith("fe80::"): + continue + ip = address["address"].split("/")[0] + cache.setdefault(rname, {})[ifname] = ip + return ip + + +def replace_link_local(expected, cache): + for prefix, prefix_infos in expected.get("routes", {}).items(): + for prefix_info in prefix_infos: + for nexthop in prefix_info.get("nexthops", []): + ip = nexthop.get("ip", "") + if not ip.startswith("link-local:"): + continue + rname = ip.split(":")[1] + ifname = ip.split(":")[2] + ip = get_link_local(rname, ifname, cache) + nexthop["ip"] = ip + + +def check_r2_sub_group(expected): + tgen = get_topogen() + + r2 = tgen.gears["r2"] + + output = json.loads(r2.vtysh_cmd("show bgp view RS update-groups json")) + actual = [ + subgroup["peers"] + for entry in output.get("RS", {}).values() + for subgroup in entry["subGroup"] + ] + + return topotest.json_cmp(actual, expected) + +def test_converge_protocols(): + "Wait for protocol convergence" + + tgen = get_topogen() + # Don't run this test if we have any failure. if tgen.routers_have_failure(): pytest.skip(tgen.errors) - r1 = tgen.gears["r1"] r2 = tgen.gears["r2"] + ref_file = "{}/{}/show_bgp_ipv6_summary.json".format(CWD, r2.name) + expected = json.loads(open(ref_file).read()) + + test_func = partial( + topotest.router_json_cmp, + r2, + "show bgp view RS ipv6 summary json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = "{}: BGP convergence failed".format(r2.name) + assert res is None, assertmsg + + +def test_bgp_route_server_client_step1(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global link_local_cache + link_local_cache = {} + router_list = tgen.routers().values() + for router in router_list: + if router.name == "r2": + # route-server + cmd = "show bgp view RS ipv6 unicast json" + else: + cmd = "show bgp ipv6 unicast json" + + # router.cmd("vtysh -c 'sh bgp ipv6 json' >/tmp/show_bgp_ipv6_%s.json" % router.name) + ref_file = "{}/{}/show_bgp_ipv6_step1.json".format(CWD, router.name) + expected = json.loads(open(ref_file).read()) + replace_link_local(expected, link_local_cache) + + test_func = partial( + topotest.router_json_cmp, + router, + cmd, + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = "{}: BGP IPv6 table failure".format(router.name) + assert res is None, assertmsg - def _bgp_converge(router): - output = json.loads(router.vtysh_cmd("show bgp ipv6 unicast summary json")) - expected = {"peers": {"2001:db8:1::1": {"state": "Established", "pfxRcd": 2}}} - return topotest.json_cmp(output, expected) + # check r2 sub-groups + expected = [["2001:db8:1::4"], ["2001:db8:1::3", "2001:db8:1::2", "2001:db8:3::2"]] - test_func = functools.partial(_bgp_converge, r1) + test_func = functools.partial(check_r2_sub_group, expected) _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) - assert result is None, "Cannot see BGP sessions to be up" + assert result is None, "Peer group split failed" - def _bgp_prefix_received(router): - output = json.loads(router.vtysh_cmd("show bgp 2001:db8:f::3/128 json")) - expected = { - "prefix": "2001:db8:f::3/128", - "paths": [{"nexthops": [{"ip": "2001:db8:3::2"}]}], - } - return topotest.json_cmp(output, expected) - test_func = functools.partial(_bgp_prefix_received, r1) +def test_bgp_route_server_client_step2(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r2 = tgen.gears["r2"] + r2.vtysh_cmd( + """ +configure terminal +router bgp 65000 view RS + address-family ipv6 unicast + neighbor 2001:db8:1::2 nexthop-local unchanged + neighbor 2001:db8:1::3 nexthop-local unchanged + neighbor 2001:db8:1::4 nexthop-local unchanged + neighbor 2001:db8:3::2 nexthop-local unchanged +""" + ) + + router_list = tgen.routers().values() + for router in router_list: + if router.name == "r2": + # route-server + cmd = "show bgp view RS ipv6 unicast json" + else: + cmd = "show bgp ipv6 unicast json" + + # router.cmd("vtysh -c 'sh bgp ipv6 json' >/tmp/show_bgp_ipv6_%s.json" % router.name) + ref_file = "{}/{}/show_bgp_ipv6_step2.json".format(CWD, router.name) + expected = json.loads(open(ref_file).read()) + replace_link_local(expected, link_local_cache) + + test_func = partial( + topotest.router_json_cmp, + router, + cmd, + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = "{}: BGP IPv6 table failure".format(router.name) + assert res is None, assertmsg + + # check r2 sub-groups + expected = [ + ["2001:db8:1::4"], + ["2001:db8:1::3", "2001:db8:1::2"], + ["2001:db8:3::2"], + ] + + test_func = functools.partial(check_r2_sub_group, expected) _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) - assert result is None, "Cannot see BGP GUA next hop from r3 in r1" - - def _bgp_single_next_hop(router): - output = json.loads(router.vtysh_cmd("show bgp 2001:db8:f::3/128 json")) - return len(output["paths"][0]["nexthops"]) - - assert ( - _bgp_single_next_hop(r1) == 1 - ), "Not ONLY one Next Hop received for 2001:db8:f::3/128" - - def _bgp_gua_lla_next_hop(router): - output = json.loads(router.vtysh_cmd("show bgp view RS 2001:db8:f::3/128 json")) - expected = { - "prefix": "2001:db8:f::3/128", - "paths": [ - { - "nexthops": [ - { - "ip": "2001:db8:3::2", - "hostname": "r3", - "afi": "ipv6", - "scope": "global", - }, - {"hostname": "r3", "afi": "ipv6", "scope": "link-local"}, - ] - } - ], - } - return topotest.json_cmp(output, expected) - - test_func = functools.partial(_bgp_gua_lla_next_hop, r2) + assert result is None, "Peer group split failed" + + +def test_bgp_route_server_client_step3(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r2 = tgen.gears["r2"] + r2.vtysh_cmd( + """ +configure terminal +router bgp 65000 view RS + address-family ipv6 unicast + no neighbor 2001:db8:1::2 nexthop-local unchanged + no neighbor 2001:db8:1::3 nexthop-local unchanged + no neighbor 2001:db8:1::4 nexthop-local unchanged + no neighbor 2001:db8:3::2 nexthop-local unchanged +""" + ) + + global link_local_cache + link_local_cache = {} + router_list = tgen.routers().values() + for router in router_list: + if router.name == "r2": + # route-server + cmd = "show bgp view RS ipv6 unicast json" + else: + cmd = "show bgp ipv6 unicast json" + + # router.cmd("vtysh -c 'sh bgp ipv6 json' >/tmp/show_bgp_ipv6_%s.json" % router.name) + ref_file = "{}/{}/show_bgp_ipv6_step1.json".format(CWD, router.name) + expected = json.loads(open(ref_file).read()) + replace_link_local(expected, link_local_cache) + + test_func = partial( + topotest.router_json_cmp, + router, + cmd, + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = "{}: BGP IPv6 table failure".format(router.name) + assert res is None, assertmsg + + # check r2 sub-groups + expected = [["2001:db8:1::4"], ["2001:db8:1::3", "2001:db8:1::2", "2001:db8:3::2"]] + + test_func = functools.partial(check_r2_sub_group, expected) _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) - assert result is None, "Cannot see BGP LLA next hop from r3 in r2" + assert result is None, "Peer group split failed" if __name__ == "__main__": diff --git a/tests/topotests/bgp_set_metric_igp/__init__.py b/tests/topotests/bgp_set_metric_igp/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/bgp_set_metric_igp/r1/frr.conf b/tests/topotests/bgp_set_metric_igp/r1/frr.conf new file mode 100644 index 000000000000..018ea0269eda --- /dev/null +++ b/tests/topotests/bgp_set_metric_igp/r1/frr.conf @@ -0,0 +1,13 @@ +! +int r1-eth0 + ip address 10.0.0.1/24 +! +int r1-eth1 + ip address 10.0.1.1/24 +! +router bgp 65001 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 10.0.0.2 remote-as external + neighbor 10.0.1.2 remote-as external +! diff --git a/tests/topotests/bgp_set_metric_igp/r2/frr.conf b/tests/topotests/bgp_set_metric_igp/r2/frr.conf new file mode 100644 index 000000000000..bce06c47f065 --- /dev/null +++ b/tests/topotests/bgp_set_metric_igp/r2/frr.conf @@ -0,0 +1,36 @@ +! +int r2-eth0 + ip address 10.0.0.2/24 + ip router isis n2 + isis circuit-type level-2-only + isis fast-reroute lfa level-2 + isis network point-to-point + isis hello-interval 1 + isis hello-multiplier 10 +! +int r2-eth1 + ip address 10.0.2.1/24 + ip router isis n2 + isis circuit-type level-2-only + isis fast-reroute lfa level-2 + isis network point-to-point + isis hello-interval 1 + isis hello-multiplier 10 +! +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 10.0.0.1 remote-as external + neighbor 10.0.2.2 remote-as internal + address-family ipv4 unicast + neighbor 10.0.0.1 route-map igp out + exit-address-family +! +router isis n2 + is-type level-2-only + net 49.0001.0000.0000.0002.00 + lsp-mtu 1440 +exit +! +route-map igp permit 10 + set metric igp +exit diff --git a/tests/topotests/bgp_set_metric_igp/r3/frr.conf b/tests/topotests/bgp_set_metric_igp/r3/frr.conf new file mode 100644 index 000000000000..312f97d08a4a --- /dev/null +++ b/tests/topotests/bgp_set_metric_igp/r3/frr.conf @@ -0,0 +1,38 @@ +! +int r3-eth0 + ip address 10.0.1.2/24 + ip router isis n3 + isis circuit-type level-2-only + isis fast-reroute lfa level-2 + isis network point-to-point + isis hello-interval 1 + isis hello-multiplier 10 +! +int r3-eth1 + ip address 10.0.3.1/24 + ip router isis n3 + isis circuit-type level-2-only + isis fast-reroute lfa level-2 + isis metric level-1 10 + isis metric level-2 100 + isis network point-to-point + isis hello-interval 1 + isis hello-multiplier 10 +! +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 10.0.1.1 remote-as external + neighbor 10.0.3.2 remote-as internal + address-family ipv4 unicast + neighbor 10.0.1.1 route-map igp out + exit-address-family +! +router isis n3 + is-type level-2-only + net 49.0001.0000.0000.0003.00 + lsp-mtu 1440 +exit +! +route-map igp permit 10 + set metric igp +exit diff --git a/tests/topotests/bgp_set_metric_igp/r4/frr.conf b/tests/topotests/bgp_set_metric_igp/r4/frr.conf new file mode 100644 index 000000000000..04165b80b244 --- /dev/null +++ b/tests/topotests/bgp_set_metric_igp/r4/frr.conf @@ -0,0 +1,41 @@ +! +int r4-eth0 + ip address 10.0.2.2/24 + ip router isis n4 + isis circuit-type level-2-only + isis fast-reroute lfa level-2 + isis network point-to-point + isis hello-interval 1 + isis hello-multiplier 10 +! +int r4-eth1 + ip address 10.0.3.2/24 + ip router isis n4 + isis circuit-type level-2-only + isis fast-reroute lfa level-2 + isis metric level-1 10 + isis metric level-2 100 + isis network point-to-point + isis hello-interval 1 + isis hello-multiplier 10 +! +int r4-eth2 + ip address 10.0.4.1/24 + ip router isis n4 + isis circuit-type level-2-only + isis fast-reroute lfa level-2 + isis network point-to-point + isis hello-interval 1 + isis hello-multiplier 10 +! +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 10.0.2.1 remote-as internal + neighbor 10.0.3.1 remote-as internal + neighbor 10.0.4.2 remote-as external +! +router isis n4 + is-type level-2-only + net 49.0001.0000.0000.0004.00 + lsp-mtu 1440 +exit diff --git a/tests/topotests/bgp_set_metric_igp/r5/frr.conf b/tests/topotests/bgp_set_metric_igp/r5/frr.conf new file mode 100644 index 000000000000..af23677b72b5 --- /dev/null +++ b/tests/topotests/bgp_set_metric_igp/r5/frr.conf @@ -0,0 +1,14 @@ +! +int r5-eth0 + ip address 10.0.4.2/24 +! +router bgp 65005 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 10.0.4.1 remote-as external + neighbor 10.0.4.1 timers 1 3 + neighbor 10.0.4.1 timers connect 1 + address-family ipv4 unicast + network 10.5.5.5/32 + exit-address-family +! diff --git a/tests/topotests/bgp_set_metric_igp/test_bgp_set_metric_igp.py b/tests/topotests/bgp_set_metric_igp/test_bgp_set_metric_igp.py new file mode 100644 index 000000000000..8fbe817689d1 --- /dev/null +++ b/tests/topotests/bgp_set_metric_igp/test_bgp_set_metric_igp.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2024 by +# Donatas Abraitis +# + +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, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def setup_module(mod): + topodef = { + "s1": ("r1", "r2"), + "s2": ("r1", "r3"), + "s3": ("r2", "r4"), + "s4": ("r3", "r4"), + "s5": ("r4", "r5"), + } + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for _, (rname, router) in enumerate(router_list.items(), 1): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_set_metric_igp(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + def _bgp_converge(): + output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast json")) + expected = { + "routes": { + "10.5.5.5/32": [ + { + "valid": True, + "bestpath": True, + "selectionReason": "MED", + "metric": 20, + "nexthops": [ + { + "ip": "10.0.0.2", + "hostname": "r2", + } + ], + }, + { + "valid": True, + "bestpath": None, + "metric": 110, + "nexthops": [ + { + "ip": "10.0.1.2", + "hostname": "r3", + } + ], + }, + ] + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_converge, + ) + _, result = topotest.run_and_expect(test_func, None, count=120, wait=5) + assert result is None, "10.5.5.5/32 best path is not via r2 (MED == 20)" + + r2.vtysh_cmd( + """ +configure terminal +interface r2-eth1 + isis metric level-2 6000 +""" + ) + + def _bgp_converge_after_isis_metric_changes(): + output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast json")) + expected = { + "routes": { + "10.5.5.5/32": [ + { + "valid": True, + "bestpath": None, + "metric": 6010, + "nexthops": [ + { + "ip": "10.0.0.2", + "hostname": "r2", + } + ], + }, + { + "valid": True, + "bestpath": True, + "selectionReason": "MED", + "metric": 110, + "nexthops": [ + { + "ip": "10.0.1.2", + "hostname": "r3", + } + ], + }, + ] + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_converge_after_isis_metric_changes, + ) + _, result = topotest.run_and_expect(test_func, None, count=120, wait=5) + assert result is None, "10.5.5.5/32 best path is not via r3 (MED == 110)" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/lib/bmp_collector/bmp.py b/tests/topotests/lib/bmp_collector/bmp.py index 237decdd5ecb..ac8af0284499 100644 --- a/tests/topotests/lib/bmp_collector/bmp.py +++ b/tests/topotests/lib/bmp_collector/bmp.py @@ -10,11 +10,13 @@ - 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 @@ def dissect(cls, data, log_file=None): 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 @@ def dissect(cls, data): "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)), } ) @@ -316,7 +329,8 @@ class BMPStatisticsReport: # ------------------------------------------------------------------------------ -class BMPPeerDownNotification: +@BMPMsg.register_msg_type(BMPCodes.BMP_MSG_TYPE_PEER_DOWN_NOTIFICATION) +class BMPPeerDownNotification(BMPPerPeerMessage): """ 0 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -326,7 +340,20 @@ class BMPPeerDownNotification: +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ """ - pass + @classmethod + def dissect(cls, data): + data, peer_msg = super().dissect(data) + + msg = { + **peer_msg, + **{ + "bmp_log_type": "peer down", + }, + } + + # XXX: dissect the bgp open message + + return msg # ------------------------------------------------------------------------------ @@ -360,6 +387,7 @@ def dissect(cls, data): msg = { **peer_msg, **{ + "bmp_log_type": "peer up", "local_ip": bin2str_ipaddress(local_addr, peer_msg.get("ipv6")), "local_port": int(local_port), "remote_port": int(remote_port), diff --git a/tests/topotests/lib/bmp_collector/bmpserver b/tests/topotests/lib/bmp_collector/bmpserver index 5257df7530b6..56d85fc74b30 100755 --- a/tests/topotests/lib/bmp_collector/bmpserver +++ b/tests/topotests/lib/bmp_collector/bmpserver @@ -5,43 +5,105 @@ # Authored by Farid Mihoub # 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/common_check.py b/tests/topotests/lib/common_check.py index 19f02dbadc9f..b04b9de44e89 100644 --- a/tests/topotests/lib/common_check.py +++ b/tests/topotests/lib/common_check.py @@ -58,7 +58,7 @@ def iproute2_check_path_selection(router, ipaddr_str, expected, vrf_name=None): else: cmdstr = f"ip -json route show {ipaddr_str}" try: - output = json.loads(cmdstr) + output = json.loads(router.cmd(cmdstr)) except: output = [] diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py index 14dd61b077eb..641295258e33 100644 --- a/tests/topotests/lib/topogen.py +++ b/tests/topotests/lib/topogen.py @@ -1273,16 +1273,24 @@ def __str__(self): 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/lib/topotest.py b/tests/topotests/lib/topotest.py index d15fefc0390e..bd989583553d 100644 --- a/tests/topotests/lib/topotest.py +++ b/tests/topotests/lib/topotest.py @@ -1244,8 +1244,8 @@ def _sysctl_assure(commander, variable, value): def sysctl_atleast(commander, variable, min_value, raises=False): try: if commander is None: - logger = logging.getLogger("topotest") - commander = micronet.Commander("sysctl", logger=logger) + topotest_logger = logging.getLogger("topotest") + commander = micronet.Commander("sysctl", logger=topotest_logger) return _sysctl_atleast(commander, variable, min_value) except subprocess.CalledProcessError as error: @@ -1262,8 +1262,8 @@ def sysctl_atleast(commander, variable, min_value, raises=False): def sysctl_assure(commander, variable, value, raises=False): try: if commander is None: - logger = logging.getLogger("topotest") - commander = micronet.Commander("sysctl", logger=logger) + topotest_logger = logging.getLogger("topotest") + commander = micronet.Commander("sysctl", logger=topotest_logger) return _sysctl_assure(commander, variable, value) except subprocess.CalledProcessError as error: logger.warning( diff --git a/tools/etc/logrotate.d/frr b/tools/etc/logrotate.d/frr index 735af6539b26..2da554350bc6 100644 --- a/tools/etc/logrotate.d/frr +++ b/tools/etc/logrotate.d/frr @@ -16,7 +16,7 @@ # between file and syslog, rsyslogd might still have file # open, as well as the daemons, so always signal the daemons. # It's safe, a NOP if (only) syslog is being used. - for i in babeld bgpd eigrpd isisd ldpd nhrpd ospf6d ospfd sharpd \ + for i in babeld bgpd eigrpd isisd ldpd mgmtd nhrpd ospf6d ospfd sharpd \ pimd pim6d ripd ripngd zebra pathd pbrd staticd bfdd fabricd vrrpd; do if [ -e /var/run/frr/$i.pid ] ; then pids="$pids $(cat /var/run/frr/$i.pid)" diff --git a/tools/etc/rsyslog.d/45-frr.conf b/tools/etc/rsyslog.d/45-frr.conf index 75b20d76bc10..ef37d66d820e 100644 --- a/tools/etc/rsyslog.d/45-frr.conf +++ b/tools/etc/rsyslog.d/45-frr.conf @@ -11,6 +11,7 @@ if $programname == 'babeld' or $programname == 'isisd' or $programname == 'fabricd' or $programname == 'ldpd' or + $programname == 'mgmtd' or $programname == 'nhrpd' or $programname == 'ospf6d' or $programname == 'ospfd' or @@ -33,6 +34,7 @@ if $programname == 'babeld' or $programname == 'isisd' or $programname == 'fabricd' or $programname == 'ldpd' or + $programname == 'mgmtd' or $programname == 'nhrpd' or $programname == 'ospf6d' or $programname == 'ospfd' or diff --git a/tools/gcc-plugins/frr-format.c b/tools/gcc-plugins/frr-format.c index 963741e4798f..d9e38f62cae4 100644 --- a/tools/gcc-plugins/frr-format.c +++ b/tools/gcc-plugins/frr-format.c @@ -2685,7 +2685,8 @@ tree type_normalize (tree type, tree *cousin, tree target = NULL) { while (1) { - if (TREE_CODE (type) == FUNCTION_TYPE || TREE_CODE (type) == POINTER_TYPE) + if (TREE_CODE (type) == FUNCTION_TYPE || TREE_CODE (type) == POINTER_TYPE + || TREE_CODE (type) == ARRAY_TYPE) return type; if (target) /* Strip off any "const" etc. */ diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 2d80feef6c97..5a54c60c6b11 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -280,9 +280,6 @@ static int vtysh_client_run(struct vtysh_client *vclient, const char *line, nread = vtysh_client_receive( vclient, bufvalid, buf + bufsz - bufvalid - 1, pass_fd); - if (nread < 0 && (errno == EINTR || errno == EAGAIN)) - continue; - if (nread <= 0) { if (vty->of) vty_out(vty, @@ -698,7 +695,7 @@ static char *trim(char *s) int vtysh_mark_file(const char *filename) { struct vty *vty; - FILE *confp = NULL; + FILE *confp = NULL, *closefp = NULL; int ret; vector vline; int tried = 0; @@ -711,7 +708,7 @@ int vtysh_mark_file(const char *filename) if (strncmp("-", filename, 1) == 0) confp = stdin; else - confp = fopen(filename, "r"); + confp = closefp = fopen(filename, "r"); if (confp == NULL) { fprintf(stderr, "%% Can't open config file %s due to '%s'.\n", @@ -851,9 +848,8 @@ int vtysh_mark_file(const char *filename) vty_close(vty); XFREE(MTYPE_VTYSH_CMD, vty_buf_copy); - if (confp != stdin) - fclose(confp); - + if (closefp) + fclose(closefp); return 0; } diff --git a/vtysh/vtysh_main.c b/vtysh/vtysh_main.c index 64198132cc64..297d87ec419d 100644 --- a/vtysh/vtysh_main.c +++ b/vtysh/vtysh_main.c @@ -350,6 +350,7 @@ int main(int argc, char **argv, char **env) char pathspace[MAXPATHLEN] = ""; const char *histfile = NULL; const char *histfile_env = getenv("VTYSH_HISTFILE"); + const char *logpath = getenv("VTYSH_LOG"); /* SUID: drop down to calling user & go back up when needed */ elevuid = geteuid(); @@ -643,9 +644,7 @@ int main(int argc, char **argv, char **env) } } - if (getenv("VTYSH_LOG")) { - const char *logpath = getenv("VTYSH_LOG"); - + if (logpath != NULL) { logfile = fopen(logpath, "a"); if (!logfile) { fprintf(stderr, "Failed to open logfile (%s): %s\n", diff --git a/yang/frr-bfdd.yang b/yang/frr-bfdd.yang index 02ed9214599f..c5c824f7927f 100644 --- a/yang/frr-bfdd.yang +++ b/yang/frr-bfdd.yang @@ -65,7 +65,7 @@ module frr-bfdd { typedef multiplier { description "Detection multiplier"; type uint8 { - range "2..255"; + range "1..255"; } } @@ -169,7 +169,7 @@ module frr-bfdd { leaf desired-transmission-interval { type uint32 { - range "10000..60000000"; + range "10000..max"; } units microseconds; default 300000; @@ -178,7 +178,7 @@ module frr-bfdd { leaf required-receive-interval { type uint32 { - range "10000..60000000"; + range "10000..max"; } units microseconds; default 300000; @@ -210,7 +210,7 @@ module frr-bfdd { leaf desired-echo-transmission-interval { type uint32 { - range "10000..60000000"; + range "10000..max"; } units microseconds; default 50000; @@ -219,7 +219,7 @@ module frr-bfdd { leaf required-echo-receive-interval { type uint32 { - range "0 | 10000..60000000"; + range "0 | 10000..max"; } units microseconds; default 50000; diff --git a/yang/frr-bgp-route-map.yang b/yang/frr-bgp-route-map.yang index 44058ab04ece..4ed80d7d0700 100644 --- a/yang/frr-bgp-route-map.yang +++ b/yang/frr-bgp-route-map.yang @@ -94,6 +94,12 @@ module frr-bgp-route-map { "Match peer address"; } + identity src-peer { + base frr-route-map:rmap-match-type; + description + "Match source peer address"; + } + identity mac-address-list { base frr-route-map:rmap-match-type; description @@ -688,6 +694,37 @@ identity set-extcommunity-color { } } + case src-peer { + when "derived-from-or-self(../frr-route-map:condition, 'frr-bgp-route-map:src-peer')"; + choice peer { + description + "Value of the peer"; + case src-peer-ipv4-address { + description + "IP address of peer"; + leaf src-peer-ipv4-address { + type inet:ipv4-address; + } + } + + case src-peer-interface { + description + "Interface name of peer"; + leaf src-peer-interface { + type string; + } + } + + case src-peer-ipv6-address { + description + "IPv6 address of peer"; + leaf src-peer-ipv6-address { + type inet:ipv6-address; + } + } + } + } + case access-list-name { when "derived-from-or-self(../frr-route-map:condition, 'frr-bgp-route-map:mac-address-list') or " + "derived-from-or-self(../frr-route-map:condition, 'frr-bgp-route-map:as-path-list') or " diff --git a/yang/frr-route-map.yang b/yang/frr-route-map.yang index c875a6ec7f05..244c353a6357 100644 --- a/yang/frr-route-map.yang +++ b/yang/frr-route-map.yang @@ -355,6 +355,22 @@ module frr-route-map { "Subtract round trip time to metric"; } } + + case use-igp { + leaf use-igp { + type boolean; + description + "Use metric from IGP procotol"; + } + } + + case use-aigp { + leaf use-aigp { + type boolean; + description + "Use metric from AIGP (Accumulated IGP)"; + } + } } } diff --git a/zebra/connected.c b/zebra/connected.c index 974a1c17a292..ce4f6919d546 100644 --- a/zebra/connected.c +++ b/zebra/connected.c @@ -176,6 +176,43 @@ static void connected_update(struct interface *ifp, struct connected *ifc) connected_announce(ifp, ifc); } +/* + * This function goes through and handles the deletion of a kernel route that happened + * to be the exact same as the connected route, so that the connected route wins. + * This can happen during processing if we happen to receive events in a slightly + * unexpected order. This is similiar to code in the other direction where if we + * have a kernel route don't install it if it perfectly matches a connected route. + */ +static void connected_remove_kernel_for_connected(afi_t afi, safi_t safi, struct zebra_vrf *zvrf, + struct prefix *p, struct nexthop *nh) +{ + struct route_node *rn; + struct route_entry *re; + rib_dest_t *dest; + struct route_table *table = zebra_vrf_table(afi, SAFI_UNICAST, zvrf->vrf->vrf_id); + + if (!table) + return; + + rn = route_node_match(table, p); + if (!rn) + return; + + if (!prefix_same(&rn->p, p)) + return; + + dest = rib_dest_from_rnode(rn); + if (!dest || !dest->selected_fib) + return; + + re = dest->selected_fib; + if (re->type != ZEBRA_ROUTE_KERNEL) + return; + + rib_delete(afi, SAFI_UNICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_KERNEL, 0, 0, p, NULL, nh, 0, + zvrf->table_id, 0, 0, false); +} + /* Called from if_up(). */ void connected_up(struct interface *ifp, struct connected *ifc) { @@ -284,10 +321,13 @@ void connected_up(struct interface *ifp, struct connected *ifc) } if (!CHECK_FLAG(ifc->flags, ZEBRA_IFA_NOPREFIXROUTE)) { + connected_remove_kernel_for_connected(afi, SAFI_UNICAST, zvrf, &p, &nh); + rib_add(afi, SAFI_UNICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT, 0, flags, &p, NULL, &nh, 0, zvrf->table_id, metric, 0, 0, 0, false); + connected_remove_kernel_for_connected(afi, SAFI_MULTICAST, zvrf, &p, &nh); rib_add(afi, SAFI_MULTICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT, 0, flags, &p, NULL, &nh, 0, zvrf->table_id, metric, 0, 0, 0, false); diff --git a/zebra/dpdk/zebra_dplane_dpdk.c b/zebra/dpdk/zebra_dplane_dpdk.c index 411155167f64..ae1a3743ce58 100644 --- a/zebra/dpdk/zebra_dplane_dpdk.c +++ b/zebra/dpdk/zebra_dplane_dpdk.c @@ -400,6 +400,7 @@ static void zd_dpdk_rule_update(struct zebra_dplane_ctx *ctx) case DPLANE_OP_INTF_INSTALL: case DPLANE_OP_INTF_UPDATE: case DPLANE_OP_INTF_DELETE: + case DPLANE_OP_VLAN_INSTALL, break; } } @@ -459,6 +460,7 @@ static void zd_dpdk_process_update(struct zebra_dplane_ctx *ctx) case DPLANE_OP_INTF_INSTALL: case DPLANE_OP_INTF_UPDATE: case DPLANE_OP_INTF_DELETE: + case DPLANE_OP_VLAN_INSTALL, atomic_fetch_add_explicit(&dpdk_stat->ignored_updates, 1, memory_order_relaxed); diff --git a/zebra/dplane_fpm_nl.c b/zebra/dplane_fpm_nl.c index d594fc2c8640..8a967978cb31 100644 --- a/zebra/dplane_fpm_nl.c +++ b/zebra/dplane_fpm_nl.c @@ -1058,6 +1058,7 @@ static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx) case DPLANE_OP_SRV6_ENCAP_SRCADDR_SET: case DPLANE_OP_NONE: case DPLANE_OP_STARTUP_STAGE: + case DPLANE_OP_VLAN_INSTALL: break; } @@ -1512,8 +1513,12 @@ static void fpm_process_queue(struct event *t) /* Re-schedule if we ran out of buffer space */ if (no_bufs) { - event_add_event(fnc->fthread->master, fpm_process_queue, fnc, 0, - &fnc->t_dequeue); + if (processed_contexts) + event_add_event(fnc->fthread->master, fpm_process_queue, fnc, 0, + &fnc->t_dequeue); + else + event_add_timer_msec(fnc->fthread->master, fpm_process_queue, fnc, 10, + &fnc->t_dequeue); event_add_timer(fnc->fthread->master, fpm_process_wedged, fnc, DPLANE_FPM_NL_WEDGIE_TIME, &fnc->t_wedged); } else @@ -1525,7 +1530,7 @@ static void fpm_process_queue(struct event *t) * until the dataplane thread gets scheduled for new, * unrelated work. */ - if (dplane_provider_out_ctx_queue_len(fnc->prov) > 0) + if (processed_contexts) dplane_provider_work_ready(); } diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index 8beae125d2da..62b665682ff6 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -1610,68 +1610,18 @@ int netlink_tunneldump_read(struct zebra_ns *zns) return 0; } -static const char *port_state2str(uint8_t state) +static uint8_t netlink_get_dplane_vlan_state(uint8_t state) { - switch (state) { - case BR_STATE_DISABLED: - return "DISABLED"; - case BR_STATE_LISTENING: - return "LISTENING"; - case BR_STATE_LEARNING: - return "LEARNING"; - case BR_STATE_FORWARDING: - return "FORWARDING"; - case BR_STATE_BLOCKING: - return "BLOCKING"; - } - - return "UNKNOWN"; -} - -static void vxlan_vni_state_change(struct zebra_if *zif, uint16_t id, - uint8_t state) -{ - struct zebra_vxlan_vni *vnip; - - vnip = zebra_vxlan_if_vlanid_vni_find(zif, id); - - if (!vnip) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "Cannot find VNI for VID (%u) IF %s for vlan state update", - id, zif->ifp->name); - - return; - } - - switch (state) { - case BR_STATE_FORWARDING: - zebra_vxlan_if_vni_up(zif->ifp, vnip); - break; - case BR_STATE_BLOCKING: - zebra_vxlan_if_vni_down(zif->ifp, vnip); - break; - case BR_STATE_DISABLED: - case BR_STATE_LISTENING: - case BR_STATE_LEARNING: - default: - /* Not used for anything at the moment */ - break; - } -} - -static void vlan_id_range_state_change(struct interface *ifp, uint16_t id_start, - uint16_t id_end, uint8_t state) -{ - struct zebra_if *zif; - - zif = (struct zebra_if *)ifp->info; - - if (!zif) - return; - - for (uint16_t i = id_start; i <= id_end; i++) - vxlan_vni_state_change(zif, i, state); + if (state == BR_STATE_LISTENING) + return ZEBRA_DPLANE_BR_STATE_LISTENING; + else if (state == BR_STATE_LEARNING) + return ZEBRA_DPLANE_BR_STATE_LEARNING; + else if (state == BR_STATE_FORWARDING) + return ZEBRA_DPLANE_BR_STATE_FORWARDING; + else if (state == BR_STATE_BLOCKING) + return ZEBRA_DPLANE_BR_STATE_BLOCKING; + + return ZEBRA_DPLANE_BR_STATE_DISABLED; } /** @@ -1686,7 +1636,6 @@ static void vlan_id_range_state_change(struct interface *ifp, uint16_t id_start, int netlink_vlan_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) { int len, rem; - struct interface *ifp; struct br_vlan_msg *bvm; struct bridge_vlan_info *vinfo; struct rtattr *vtb[BRIDGE_VLANDB_ENTRY_MAX + 1] = {}; @@ -1694,6 +1643,9 @@ int netlink_vlan_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) uint8_t state; uint32_t vrange; int type; + uint32_t count = 0; + struct zebra_dplane_ctx *ctx = NULL; + struct zebra_vxlan_vlan_array *vlan_array = NULL; /* We only care about state changes for now */ if (!(h->nlmsg_type == RTM_NEWVLAN)) @@ -1713,25 +1665,10 @@ int netlink_vlan_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) if (bvm->family != AF_BRIDGE) return 0; - ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), bvm->ifindex); - if (!ifp) { - zlog_debug("Cannot find bridge-vlan IF (%u) for vlan update", - bvm->ifindex); - return 0; - } - - if (!IS_ZEBRA_IF_VXLAN(ifp)) { - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("Ignoring non-vxlan IF (%s) for vlan update", - ifp->name); - - return 0; - } - - if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("%s %s IF %s NS %u", - nl_msg_type_to_str(h->nlmsg_type), - nl_family_to_str(bvm->family), ifp->name, ns_id); + ctx = dplane_ctx_alloc(); + dplane_ctx_set_ns_id(ctx, ns_id); + dplane_ctx_set_op(ctx, DPLANE_OP_VLAN_INSTALL); + dplane_ctx_set_vlan_ifindex(ctx, bvm->ifindex); /* Loop over "ALL" BRIDGE_VLANDB_ENTRY */ rem = len; @@ -1762,26 +1699,39 @@ int netlink_vlan_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) if (!vtb[BRIDGE_VLANDB_ENTRY_STATE]) continue; + count++; + vlan_array = + XREALLOC(MTYPE_VLAN_CHANGE_ARR, vlan_array, + sizeof(struct zebra_vxlan_vlan_array) + + count * sizeof(struct zebra_vxlan_vlan)); + + memset(&vlan_array->vlans[count - 1], 0, + sizeof(struct zebra_vxlan_vlan)); + state = *(uint8_t *)RTA_DATA(vtb[BRIDGE_VLANDB_ENTRY_STATE]); if (vtb[BRIDGE_VLANDB_ENTRY_RANGE]) vrange = *(uint32_t *)RTA_DATA( vtb[BRIDGE_VLANDB_ENTRY_RANGE]); - if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_VXLAN) { - if (vrange) - zlog_debug("VLANDB_ENTRY: VID (%u-%u) state=%s", - vinfo->vid, vrange, - port_state2str(state)); - else - zlog_debug("VLANDB_ENTRY: VID (%u) state=%s", - vinfo->vid, port_state2str(state)); - } - - vlan_id_range_state_change( - ifp, vinfo->vid, (vrange ? vrange : vinfo->vid), state); + vlan_array->vlans[count - 1].state = + netlink_get_dplane_vlan_state(state); + vlan_array->vlans[count - 1].vid = vinfo->vid; + vlan_array->vlans[count - 1].vrange = vrange; } + if (count) { + vlan_array->count = count; + dplane_ctx_set_vxlan_vlan_array(ctx, vlan_array); + if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("RTM_NEWVLAN for ifindex %u NS %u, enqueuing for zebra main", + bvm->ifindex, ns_id); + + dplane_provider_enqueue_to_zebra(ctx); + } else + dplane_ctx_fini(&ctx); + + return 0; } diff --git a/zebra/interface.c b/zebra/interface.c index d146004781a5..f1f1b17209a7 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -1999,10 +1999,9 @@ static void zebra_if_dplane_ifp_handling(struct zebra_dplane_ctx *ctx) !CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) { /* Add interface notification from kernel */ if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - "RTM_NEWLINK ADD for %s(%u) vrf_id %u type %d sl_type %d master %u", - name, ifindex, vrf_id, zif_type, - zif_slave_type, master_ifindex); + zlog_debug("RTM_NEWLINK ADD for %s(%u) vrf_id %u type %d sl_type %d master %u flags 0x%llx", + name, ifindex, vrf_id, zif_type, zif_slave_type, + master_ifindex, (unsigned long long)flags); if (ifp == NULL) { /* unknown interface */ @@ -2087,10 +2086,9 @@ static void zebra_if_dplane_ifp_handling(struct zebra_dplane_ctx *ctx) /* Interface update. */ if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - "RTM_NEWLINK update for %s(%u) sl_type %d master %u", - name, ifp->ifindex, zif_slave_type, - master_ifindex); + zlog_debug("RTM_NEWLINK update for %s(%u) sl_type %d master %u flags 0x%llx", + name, ifp->ifindex, zif_slave_type, master_ifindex, + (unsigned long long)flags); set_ifindex(ifp, ifindex, zns); ifp->mtu6 = ifp->mtu = mtu; diff --git a/zebra/ipforward_proc.c b/zebra/ipforward_proc.c index 08fbfede42af..0eae221e1e11 100644 --- a/zebra/ipforward_proc.c +++ b/zebra/ipforward_proc.c @@ -17,10 +17,15 @@ extern struct zebra_privs_t zserv_privs; static const char proc_net_snmp[] = "/proc/net/snmp"; -static void dropline(FILE *fp) +static bool dropline(FILE *fp) { - while (getc(fp) != '\n') - ; + int ch; + + do { + ch = getc(fp); + } while (ch != EOF && ch != '\n'); + + return ch != EOF; } int ipforward(void) @@ -36,7 +41,10 @@ int ipforward(void) return -1; /* We don't care about the first line. */ - dropline(fp); + if (!dropline(fp)) { + fclose(fp); + return 0; + } /* Get ip_statistics.IpForwarding : 1 => ip forwarding enabled diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index 84aabc425456..0c607dfa67cb 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: @@ -430,10 +426,6 @@ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id, case RTM_NEWTFILTER: case RTM_DELTFILTER: return netlink_tfilter_change(h, ns_id, startup); - case RTM_NEWVLAN: - return netlink_vlan_change(h, ns_id, startup); - case RTM_DELVLAN: - return netlink_vlan_change(h, ns_id, startup); /* Messages we may receive, but ignore */ case RTM_NEWCHAIN: @@ -442,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: @@ -449,6 +443,8 @@ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id, case RTM_NEWTUNNEL: case RTM_DELTUNNEL: case RTM_GETTUNNEL: + case RTM_NEWVLAN: + case RTM_DELVLAN: return 0; default: /* @@ -492,6 +488,10 @@ static int dplane_netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id, case RTM_DELLINK: return netlink_link_change(h, ns_id, startup); + case RTM_NEWVLAN: + case RTM_DELVLAN: + return netlink_vlan_change(h, ns_id, startup); + default: break; } @@ -932,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)); @@ -1621,6 +1621,7 @@ static enum netlink_msg_status nl_put_msg(struct nl_batch *bth, case DPLANE_OP_IPSET_ENTRY_ADD: case DPLANE_OP_IPSET_ENTRY_DELETE: case DPLANE_OP_STARTUP_STAGE: + case DPLANE_OP_VLAN_INSTALL: return FRR_NETLINK_ERROR; case DPLANE_OP_GRE_SET: @@ -1862,8 +1863,8 @@ void kernel_init(struct zebra_ns *zns) * setsockopt multicast group subscriptions that don't fit in nl_groups */ grp = RTNLGRP_BRVLAN; - ret = setsockopt(zns->netlink.sock, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, - &grp, sizeof(grp)); + ret = setsockopt(zns->netlink_dplane_in.sock, SOL_NETLINK, + NETLINK_ADD_MEMBERSHIP, &grp, sizeof(grp)); if (ret < 0) zlog_notice( diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index 5cfbe7a896dd..4789cb62f257 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -1627,6 +1627,7 @@ void kernel_update_multi(struct dplane_ctx_list_head *ctx_list) case DPLANE_OP_INTF_ADDR_DEL: case DPLANE_OP_STARTUP_STAGE: case DPLANE_OP_SRV6_ENCAP_SRCADDR_SET: + case DPLANE_OP_VLAN_INSTALL: zlog_err("Unhandled dplane data for %s", dplane_op2str(dplane_ctx_get_op(ctx))); res = ZEBRA_DPLANE_REQUEST_FAILURE; diff --git a/zebra/main.c b/zebra/main.c index 687da70cabce..dd6249eff5f1 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -238,7 +238,7 @@ void zebra_finalize(struct event *dummy) zebra_ns_notify_close(); /* Final shutdown of ns resources */ - ns_walk_func(zebra_ns_final_shutdown, NULL, NULL); + ns_walk_func(zebra_ns_kernel_shutdown, NULL, NULL); zebra_rib_terminate(); zebra_router_terminate(); @@ -251,6 +251,8 @@ void zebra_finalize(struct event *dummy) label_manager_terminate(); + ns_walk_func(zebra_ns_final_shutdown, NULL, NULL); + ns_terminate(); frr_fini(); exit(0); diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 7dae75baccd9..10acee9be435 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -647,7 +647,7 @@ static int zsend_nexthop_lookup_mrib(struct zserv *client, struct ipaddr *addr, { struct stream *s; unsigned long nump; - uint8_t num; + uint16_t num; struct nexthop *nexthop; /* Get output stream. */ @@ -667,7 +667,7 @@ static int zsend_nexthop_lookup_mrib(struct zserv *client, struct ipaddr *addr, /* remember position for nexthop_num */ nump = stream_get_endp(s); /* reserve room for nexthop_num */ - stream_putc(s, 0); + stream_putw(s, 0); nhg = rib_get_fib_nhg(re); for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { if (rnh_nexthop_valid(re, nexthop)) @@ -675,11 +675,11 @@ static int zsend_nexthop_lookup_mrib(struct zserv *client, struct ipaddr *addr, } /* store nexthop_num */ - stream_putc_at(s, nump, num); + stream_putw_at(s, nump, num); } else { stream_putc(s, 0); /* distance */ stream_putl(s, 0); /* metric */ - stream_putc(s, 0); /* nexthop_num */ + stream_putw(s, 0); /* nexthop_num */ } stream_putw_at(s, 0, stream_get_endp(s)); diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 00e990e856fd..88c1a049382b 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -35,6 +35,8 @@ DEFINE_MTYPE_STATIC(ZEBRA, DP_PROV, "Zebra DPlane Provider"); DEFINE_MTYPE_STATIC(ZEBRA, DP_NETFILTER, "Zebra Netfilter Internal Object"); DEFINE_MTYPE_STATIC(ZEBRA, DP_NS, "DPlane NSes"); +DEFINE_MTYPE(ZEBRA, VLAN_CHANGE_ARR, "Vlan Change Array"); + #ifndef AOK # define AOK 0 #endif @@ -370,6 +372,14 @@ struct dplane_srv6_encap_ctx { struct in6_addr srcaddr; }; +/* + * VLAN info for the dataplane + */ +struct dplane_vlan_info { + ifindex_t ifindex; + struct zebra_vxlan_vlan_array *vlan_array; +}; + /* * The context block used to exchange info about route updates across * the boundary between the zebra main context (and pthread) and the @@ -416,6 +426,7 @@ struct zebra_dplane_ctx { struct dplane_pw_info pw; struct dplane_br_port_info br_port; struct dplane_intf_info intf; + struct dplane_vlan_info vlan_info; struct dplane_mac_info macinfo; struct dplane_neigh_info neigh; struct dplane_rule_info rule; @@ -885,6 +896,11 @@ static void dplane_ctx_free_internal(struct zebra_dplane_ctx *ctx) case DPLANE_OP_STARTUP_STAGE: case DPLANE_OP_SRV6_ENCAP_SRCADDR_SET: break; + case DPLANE_OP_VLAN_INSTALL: + if (ctx->u.vlan_info.vlan_array) + XFREE(MTYPE_VLAN_CHANGE_ARR, + ctx->u.vlan_info.vlan_array); + break; } } @@ -1030,144 +1046,102 @@ enum dplane_op_e dplane_ctx_get_op(const struct zebra_dplane_ctx *ctx) const char *dplane_op2str(enum dplane_op_e op) { - const char *ret = "UNKNOWN"; - switch (op) { case DPLANE_OP_NONE: - ret = "NONE"; - break; + return "NONE"; /* Route update */ case DPLANE_OP_ROUTE_INSTALL: - ret = "ROUTE_INSTALL"; - break; + return "ROUTE_INSTALL"; case DPLANE_OP_ROUTE_UPDATE: - ret = "ROUTE_UPDATE"; - break; + return "ROUTE_UPDATE"; case DPLANE_OP_ROUTE_DELETE: - ret = "ROUTE_DELETE"; - break; + return "ROUTE_DELETE"; case DPLANE_OP_ROUTE_NOTIFY: - ret = "ROUTE_NOTIFY"; - break; + return "ROUTE_NOTIFY"; /* Nexthop update */ case DPLANE_OP_NH_INSTALL: - ret = "NH_INSTALL"; - break; + return "NH_INSTALL"; case DPLANE_OP_NH_UPDATE: - ret = "NH_UPDATE"; - break; + return "NH_UPDATE"; case DPLANE_OP_NH_DELETE: - ret = "NH_DELETE"; - break; + return "NH_DELETE"; case DPLANE_OP_LSP_INSTALL: - ret = "LSP_INSTALL"; - break; + return "LSP_INSTALL"; case DPLANE_OP_LSP_UPDATE: - ret = "LSP_UPDATE"; - break; + return "LSP_UPDATE"; case DPLANE_OP_LSP_DELETE: - ret = "LSP_DELETE"; - break; + return "LSP_DELETE"; case DPLANE_OP_LSP_NOTIFY: - ret = "LSP_NOTIFY"; - break; + return "LSP_NOTIFY"; case DPLANE_OP_PW_INSTALL: - ret = "PW_INSTALL"; - break; + return "PW_INSTALL"; case DPLANE_OP_PW_UNINSTALL: - ret = "PW_UNINSTALL"; - break; + return "PW_UNINSTALL"; case DPLANE_OP_SYS_ROUTE_ADD: - ret = "SYS_ROUTE_ADD"; - break; + return "SYS_ROUTE_ADD"; case DPLANE_OP_SYS_ROUTE_DELETE: - ret = "SYS_ROUTE_DEL"; - break; + return "SYS_ROUTE_DEL"; case DPLANE_OP_BR_PORT_UPDATE: - ret = "BR_PORT_UPDATE"; - break; + return "BR_PORT_UPDATE"; case DPLANE_OP_ADDR_INSTALL: - ret = "ADDR_INSTALL"; - break; + return "ADDR_INSTALL"; case DPLANE_OP_ADDR_UNINSTALL: - ret = "ADDR_UNINSTALL"; - break; + return "ADDR_UNINSTALL"; case DPLANE_OP_MAC_INSTALL: - ret = "MAC_INSTALL"; - break; + return "MAC_INSTALL"; case DPLANE_OP_MAC_DELETE: - ret = "MAC_DELETE"; - break; + return "MAC_DELETE"; case DPLANE_OP_NEIGH_INSTALL: - ret = "NEIGH_INSTALL"; - break; + return "NEIGH_INSTALL"; case DPLANE_OP_NEIGH_UPDATE: - ret = "NEIGH_UPDATE"; - break; + return "NEIGH_UPDATE"; case DPLANE_OP_NEIGH_DELETE: - ret = "NEIGH_DELETE"; - break; + return "NEIGH_DELETE"; case DPLANE_OP_VTEP_ADD: - ret = "VTEP_ADD"; - break; + return "VTEP_ADD"; case DPLANE_OP_VTEP_DELETE: - ret = "VTEP_DELETE"; - break; + return "VTEP_DELETE"; case DPLANE_OP_RULE_ADD: - ret = "RULE_ADD"; - break; + return "RULE_ADD"; case DPLANE_OP_RULE_DELETE: - ret = "RULE_DELETE"; - break; + return "RULE_DELETE"; case DPLANE_OP_RULE_UPDATE: - ret = "RULE_UPDATE"; - break; + return "RULE_UPDATE"; case DPLANE_OP_NEIGH_DISCOVER: - ret = "NEIGH_DISCOVER"; - break; + return "NEIGH_DISCOVER"; case DPLANE_OP_IPTABLE_ADD: - ret = "IPTABLE_ADD"; - break; + return "IPTABLE_ADD"; case DPLANE_OP_IPTABLE_DELETE: - ret = "IPTABLE_DELETE"; - break; + return "IPTABLE_DELETE"; case DPLANE_OP_IPSET_ADD: - ret = "IPSET_ADD"; - break; + return "IPSET_ADD"; case DPLANE_OP_IPSET_DELETE: - ret = "IPSET_DELETE"; - break; + return "IPSET_DELETE"; case DPLANE_OP_IPSET_ENTRY_ADD: - ret = "IPSET_ENTRY_ADD"; - break; + return "IPSET_ENTRY_ADD"; case DPLANE_OP_IPSET_ENTRY_DELETE: - ret = "IPSET_ENTRY_DELETE"; - break; + return "IPSET_ENTRY_DELETE"; case DPLANE_OP_NEIGH_IP_INSTALL: - ret = "NEIGH_IP_INSTALL"; - break; + return "NEIGH_IP_INSTALL"; case DPLANE_OP_NEIGH_IP_DELETE: - ret = "NEIGH_IP_DELETE"; - break; + return "NEIGH_IP_DELETE"; case DPLANE_OP_NEIGH_TABLE_UPDATE: - ret = "NEIGH_TABLE_UPDATE"; - break; + return "NEIGH_TABLE_UPDATE"; case DPLANE_OP_GRE_SET: - ret = "GRE_SET"; - break; + return "GRE_SET"; case DPLANE_OP_INTF_ADDR_ADD: return "INTF_ADDR_ADD"; @@ -1179,68 +1153,53 @@ const char *dplane_op2str(enum dplane_op_e op) return "INTF_NETCONFIG"; case DPLANE_OP_INTF_INSTALL: - ret = "INTF_INSTALL"; - break; + return "INTF_INSTALL"; case DPLANE_OP_INTF_UPDATE: - ret = "INTF_UPDATE"; - break; + return "INTF_UPDATE"; case DPLANE_OP_INTF_DELETE: - ret = "INTF_DELETE"; - break; + return "INTF_DELETE"; case DPLANE_OP_TC_QDISC_INSTALL: - ret = "TC_QDISC_INSTALL"; - break; + return "TC_QDISC_INSTALL"; case DPLANE_OP_TC_QDISC_UNINSTALL: - ret = "TC_QDISC_UNINSTALL"; - break; + return "TC_QDISC_UNINSTALL"; case DPLANE_OP_TC_CLASS_ADD: - ret = "TC_CLASS_ADD"; - break; + return "TC_CLASS_ADD"; case DPLANE_OP_TC_CLASS_DELETE: - ret = "TC_CLASS_DELETE"; - break; + return "TC_CLASS_DELETE"; case DPLANE_OP_TC_CLASS_UPDATE: - ret = "TC_CLASS_UPDATE"; - break; + return "TC_CLASS_UPDATE"; case DPLANE_OP_TC_FILTER_ADD: - ret = "TC_FILTER_ADD"; - break; + return "TC_FILTER_ADD"; case DPLANE_OP_TC_FILTER_DELETE: - ret = "TC_FILTER_DELETE"; - break; + return "TC_FILTER_DELETE"; case DPLANE_OP_TC_FILTER_UPDATE: - ret = "TC__FILTER_UPDATE"; - break; + return "TC__FILTER_UPDATE"; case DPLANE_OP_STARTUP_STAGE: - ret = "STARTUP_STAGE"; - break; + return "STARTUP_STAGE"; case DPLANE_OP_SRV6_ENCAP_SRCADDR_SET: - ret = "SRV6_ENCAP_SRCADDR_SET"; - break; + return "SRV6_ENCAP_SRCADDR_SET"; + + case DPLANE_OP_VLAN_INSTALL: + return "NEW_VLAN"; } - return ret; + return "UNKNOWN"; } const char *dplane_res2str(enum zebra_dplane_result res) { - const char *ret = ""; - switch (res) { case ZEBRA_DPLANE_REQUEST_FAILURE: - ret = "FAILURE"; - break; + return "FAILURE"; case ZEBRA_DPLANE_REQUEST_QUEUED: - ret = "QUEUED"; - break; + return "QUEUED"; case ZEBRA_DPLANE_REQUEST_SUCCESS: - ret = "SUCCESS"; - break; + return "SUCCESS"; } - return ret; + return ""; } void dplane_ctx_set_dest(struct zebra_dplane_ctx *ctx, @@ -3321,6 +3280,35 @@ uint32_t dplane_get_in_queue_len(void) memory_order_seq_cst); } +void dplane_ctx_set_vlan_ifindex(struct zebra_dplane_ctx *ctx, ifindex_t ifindex) +{ + DPLANE_CTX_VALID(ctx); + ctx->u.vlan_info.ifindex = ifindex; +} + +ifindex_t dplane_ctx_get_vlan_ifindex(struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.vlan_info.ifindex; +} + +void dplane_ctx_set_vxlan_vlan_array(struct zebra_dplane_ctx *ctx, + struct zebra_vxlan_vlan_array *vlan_array) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.vlan_info.vlan_array = vlan_array; +} + +const struct zebra_vxlan_vlan_array * +dplane_ctx_get_vxlan_vlan_array(struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.vlan_info.vlan_array; +} + /* * Internal helper that copies information from a zebra ns object; this is * called in the zebra main pthread context as part of dplane ctx init. @@ -6720,6 +6708,12 @@ static void kernel_dplane_log_detail(struct zebra_dplane_ctx *ctx) dplane_op2str(dplane_ctx_get_op(ctx)), &ctx->u.srv6_encap.srcaddr); break; + + case DPLANE_OP_VLAN_INSTALL: + zlog_debug("Dplane %s on idx %u", + dplane_op2str(dplane_ctx_get_op(ctx)), + dplane_ctx_get_vlan_ifindex(ctx)); + break; } } @@ -6888,6 +6882,7 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx) case DPLANE_OP_INTF_ADDR_ADD: case DPLANE_OP_INTF_ADDR_DEL: case DPLANE_OP_INTF_NETCONFIG: + case DPLANE_OP_VLAN_INSTALL: break; case DPLANE_OP_SRV6_ENCAP_SRCADDR_SET: diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index a3318bf5e995..285b00c9b7ea 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -24,6 +24,8 @@ extern "C" { #endif +DECLARE_MTYPE(VLAN_CHANGE_ARR); + /* Retrieve the dataplane API version number; see libfrr.h to decode major, * minor, sub version values. * Plugins should pay attention to the major version number, at least, to @@ -204,6 +206,9 @@ enum dplane_op_e { DPLANE_OP_TC_FILTER_DELETE, DPLANE_OP_TC_FILTER_UPDATE, + /* VLAN update */ + DPLANE_OP_VLAN_INSTALL, + /* Startup Control */ DPLANE_OP_STARTUP_STAGE, @@ -211,6 +216,13 @@ enum dplane_op_e { DPLANE_OP_SRV6_ENCAP_SRCADDR_SET, }; +/* Operational status of Bridge Ports */ +#define ZEBRA_DPLANE_BR_STATE_DISABLED 0x01 +#define ZEBRA_DPLANE_BR_STATE_LISTENING 0x02 +#define ZEBRA_DPLANE_BR_STATE_LEARNING 0x04 +#define ZEBRA_DPLANE_BR_STATE_FORWARDING 0x08 +#define ZEBRA_DPLANE_BR_STATE_BLOCKING 0x10 + /* * The vxlan/evpn neighbor management code needs some values to use * when programming neighbor changes. Offer some platform-neutral values @@ -1078,6 +1090,26 @@ void dplane_set_in_queue_limit(uint32_t limit, bool set); /* Retrieve the current queue depth of incoming, unprocessed updates */ uint32_t dplane_get_in_queue_len(void); +void dplane_ctx_set_vlan_ifindex(struct zebra_dplane_ctx *ctx, + ifindex_t ifindex); +ifindex_t dplane_ctx_get_vlan_ifindex(struct zebra_dplane_ctx *ctx); +struct zebra_vxlan_vlan_array; + +/* + * In netlink_vlan_change(), the memory allocated for vlan_array is freed + * in two cases + * 1) Inline free in netlink_vlan_change() when there are no new + * vlans to process i.e. nothing is enqueued to main thread. + * 2) Dplane-ctx takes over the vlan memory which gets freed in + * rib_process_dplane_results() after handling the vlan install + * + * Note: MTYPE of interest for this purpose is MTYPE_VLAN_CHANGE_ARR + */ +void dplane_ctx_set_vxlan_vlan_array(struct zebra_dplane_ctx *ctx, + struct zebra_vxlan_vlan_array *vlan_array); +const struct zebra_vxlan_vlan_array * +dplane_ctx_get_vxlan_vlan_array(struct zebra_dplane_ctx *ctx); + /* * Vty/cli apis */ diff --git a/zebra/zebra_l2.h b/zebra/zebra_l2.h index 588917f4c036..ad5f5eeee008 100644 --- a/zebra/zebra_l2.h +++ b/zebra/zebra_l2.h @@ -146,6 +146,17 @@ union zebra_l2if_info { struct zebra_l2info_gre gre; }; +struct zebra_vxlan_vlan { + uint8_t state; + uint32_t vrange; + vlanid_t vid; +}; + +struct zebra_vxlan_vlan_array { + uint16_t count; + struct zebra_vxlan_vlan vlans[0]; +}; + /* NOTE: These macros are to be invoked only in the "correct" context. * IOW, the macro VNI_FROM_ZEBRA_IF() will assume the interface is * of type ZEBRA_IF_VXLAN. diff --git a/zebra/zebra_nb_state.c b/zebra/zebra_nb_state.c index 63ac7877d0f9..6ed11f75f173 100644 --- a/zebra/zebra_nb_state.c +++ b/zebra/zebra_nb_state.c @@ -900,6 +900,7 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_bh_type_get_elem( if (nexthop->type != NEXTHOP_TYPE_BLACKHOLE) return NULL; + (void)type_str; /* clang-SA */ switch (nexthop->bh_type) { case BLACKHOLE_NULL: type_str = "null"; diff --git a/zebra/zebra_netns_id.c b/zebra/zebra_netns_id.c index 4cee3b89f198..3da79e249e66 100644 --- a/zebra/zebra_netns_id.c +++ b/zebra/zebra_netns_id.c @@ -159,6 +159,7 @@ ns_id_t zebra_ns_id_get(const char *netnspath, int fd_param) int fd = -1, sock, ret; unsigned int seq; ns_id_t return_nsid = NS_UNKNOWN; + int nl_errno; /* netns path check */ if (!netnspath && fd_param == -1) @@ -231,32 +232,31 @@ ns_id_t zebra_ns_id_get(const char *netnspath, int fd_param) ret = -1; if (err->error < 0) - errno = -err->error; + nl_errno = -err->error; else - errno = err->error; - if (errno == 0) { + nl_errno = err->error; + if (nl_errno == 0) { /* request NEWNSID was successfull * return EEXIST error to get GETNSID */ - errno = EEXIST; + nl_errno = EEXIST; } } else { /* other errors ignored * attempt to get nsid */ ret = -1; - errno = EEXIST; + nl_errno = EEXIST; } } - if (errno != EEXIST && ret != 0) { - flog_err(EC_LIB_SOCKET, - "netlink( %u) recvfrom() error 2 when reading: %s", fd, - safe_strerror(errno)); + if (ret != 0 && nl_errno != EEXIST) { + flog_err(EC_LIB_SOCKET, "netlink( %u) recvfrom() error 2 when reading: %s", fd, + safe_strerror(nl_errno)); close(sock); if (netnspath) close(fd); - if (errno == ENOTSUP) { + if (nl_errno == ENOTSUP) { zlog_debug("NEWNSID locally generated"); return zebra_ns_id_get_fallback(netnspath); } diff --git a/zebra/zebra_netns_notify.c b/zebra/zebra_netns_notify.c index 3ac4fdd73716..52b94e040566 100644 --- a/zebra/zebra_netns_notify.c +++ b/zebra/zebra_netns_notify.c @@ -378,19 +378,28 @@ void zebra_ns_notify_parse(void) { struct dirent *dent; DIR *srcdir = opendir(NS_RUN_DIR); + int srcdirfd; if (srcdir == NULL) { flog_err_sys(EC_LIB_SYSTEM_CALL, "NS parsing init: failed to parse %s", NS_RUN_DIR); return; } + + srcdirfd = dirfd(srcdir); + if (srcdirfd < 0) { + closedir(srcdir); + flog_err_sys(EC_LIB_SYSTEM_CALL, "NS parsing init: failed to parse %s", NS_RUN_DIR); + return; + } + while ((dent = readdir(srcdir)) != NULL) { struct stat st; if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) continue; - if (fstatat(dirfd(srcdir), dent->d_name, &st, 0) < 0) { + if (fstatat(srcdirfd, dent->d_name, &st, 0) < 0) { flog_err_sys( EC_LIB_SYSTEM_CALL, "NS parsing init: failed to parse entry %s", diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 803d8f0034c0..ffd749fcf17e 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -175,6 +175,22 @@ int zebra_ns_early_shutdown(struct ns *ns, return NS_WALK_CONTINUE; } +/* During zebra shutdown, do kernel cleanup + * netlink sockets, .. + */ +int zebra_ns_kernel_shutdown(struct ns *ns, void *param_in __attribute__((unused)), + void **param_out __attribute__((unused))) +{ + struct zebra_ns *zns = ns->info; + + if (zns == NULL) + return NS_WALK_CONTINUE; + + kernel_terminate(zns, true); + + return NS_WALK_CONTINUE; +} + /* During zebra shutdown, do final cleanup * after all dataplane work is complete. */ @@ -185,9 +201,7 @@ int zebra_ns_final_shutdown(struct ns *ns, struct zebra_ns *zns = ns->info; if (zns == NULL) - return 0; - - kernel_terminate(zns, true); + return NS_WALK_CONTINUE; zebra_ns_delete(ns); diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h index 8d988c3f82d0..d5fd5869bca4 100644 --- a/zebra/zebra_ns.h +++ b/zebra/zebra_ns.h @@ -70,6 +70,8 @@ int zebra_ns_early_shutdown(struct ns *ns, int zebra_ns_final_shutdown(struct ns *ns, void *param_in __attribute__((unused)), void **param_out __attribute__((unused))); +int zebra_ns_kernel_shutdown(struct ns *ns, void *param_in __attribute__((unused)), + void **param_out __attribute__((unused))); void zebra_ns_startup_continue(struct zebra_dplane_ctx *ctx); diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c index c8ffaf0bbe36..6adc0b1b4aa5 100644 --- a/zebra/zebra_pw.c +++ b/zebra/zebra_pw.c @@ -205,9 +205,11 @@ void zebra_pw_handle_dplane_results(struct zebra_dplane_ctx *ctx) vrf = zebra_vrf_lookup_by_id(dplane_ctx_get_vrf(ctx)); pw = zebra_pw_find(vrf, dplane_ctx_get_ifname(ctx)); + if (!pw) + return; + if (dplane_ctx_get_status(ctx) != ZEBRA_DPLANE_REQUEST_SUCCESS) { - if (pw) - zebra_pw_install_failure(pw, dplane_ctx_get_pw_status(ctx)); + zebra_pw_install_failure(pw, dplane_ctx_get_pw_status(ctx)); } else { if (op == DPLANE_OP_PW_INSTALL && pw->status != PW_FORWARDING) zebra_pw_update_status(pw, PW_FORWARDING); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 8ebc193fba99..4c1401124bc2 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -640,20 +640,16 @@ void rib_install_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); - 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. */ @@ -795,13 +791,77 @@ void zebra_rib_evaluate_rn_nexthops(struct route_node *rn, uint32_t seq, struct rnh *rnh; /* - * We are storing the rnh's associated withb - * the tracked nexthop as a list of the rn's. + * We are storing the rnh's associated with + * the tracked nexthop as a list of the rnh's + * on the rn that we have matched to. As an + * example if you have these rnh's: + * rnh 1.1.1.1 + * rnh 1.1.1.2 + * rnh 1.1.3.4 + * rnh 4.5.6.7 + * Now imagine that you have in the tree these + * prefix's: + * 1.1.1.1/32 + * 1.1.1.0/24 + * 1.1.0.0/16 + * 0.0.0.0/0 + * + * The 1.1.1.1 rnh would be stored on 1.1.1.1/32 + * The 1.1.1.2 rnh would be stored on 1.1.1.0/24 + * The 1.1.3.4 rnh would be stored on the 1.1.0.0/16 + * and finally the 4.5.6.7 would be stored on the 0.0.0.0/0 + * prefix. + * * Unresolved rnh's are placed at the top * of the tree list.( 0.0.0.0/0 for v4 and 0::0/0 for v6 ) * As such for each rn we need to walk up the tree * and see if any rnh's need to see if they * would match a more specific route + * + * Now if a 1.1.1.2/32 prefix was added to the tree + * this function would start at this new node and + * see that the 1.1.1.2/32 node has no rnh's and + * there is nothing to do on this node currently, + * so the function would walk the parent pointers, until the + * 1.1.1.0/24 node is hit with the 1.1.1.2 rnh. This function + * would then call zebra_evaluate_rnh() which would then + * do a LPM and match on the 1.1.1.2/32 node. This function + * would then pull the 1.1.1.2 rnh off the 1.1.1.0/24 node + * and place it on the 1.1.1.1/32 node and notify the upper + * level protocols interested about the change( as necessary ). + * At this point in time a sequence number is added to note + * that the rnh has been moved. + * The function would also continue to walk up the tree + * looking at the list of rnh's and moving them around + * as necessary. Since in this example nothing else + * would change no further actions are made. + * + * Another case to consider is a node being deleted + * suppose the 1.1.1.2/32 route is being deleted. + * This function would start at the 1.1.1.1/32 node, + * perform a LPM and settle on the 1.1.1.0/24 node + * as where it belongs. The code would update appropriate + * interested parties and additionally also mark the sequence + * number and walk up the tree. Eventually it would get to + * the 1.1.1.0/24 node and since the seqno matches we would + * know that it is not necessary to reconsider this node + * as it was already moved to this spot. + * + * This all works because each node's parent pointer points + * to a node that has a prefix that contains this node. Eventually + * the parent traversal will hit the 0.0.0.0/0 node and we know + * we are done. We know this is pretty efficient because when + * a more specific is added as we walk the tree we can + * find the rnh's that matched to a less specific very easily + * and move them to a more specific node. Also vice-versa as a + * more specific node is removed. + * + * Long term the rnh code might be improved some as the rnh's + * are stored as a list. This might be transformed to a better + * data structure. This has not proven to be necessary yet as + * that we have not seen any particular case where a rn is + * storing more than a couple rnh's. If we find a case + * where this matters something might need to be done. */ while (rn) { if (IS_ZEBRA_DEBUG_NHT_DETAILED) @@ -1275,7 +1335,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) { @@ -1309,7 +1368,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); @@ -5020,6 +5078,9 @@ static void rib_process_dplane_results(struct event *thread) zebra_ns_startup_continue(ctx); break; + case DPLANE_OP_VLAN_INSTALL: + zebra_vlan_dplane_result(ctx); + break; } /* Dispatch by op code */ dplane_ctx_fini(&ctx); diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index 89317be74d37..640e6551a774 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -220,10 +220,9 @@ void zebra_free_rnh(struct rnh *rnh) if (rern) { rib_dest_t *dest; - route_unlock_node(rern); - dest = rib_dest_from_rnode(rern); rnh_list_del(&dest->nht, rnh); + route_unlock_node(rern); } } free_state(rnh->vrf_id, rnh->state, rnh->node); @@ -1141,7 +1140,7 @@ int zebra_send_rnh_update(struct rnh *rnh, struct zserv *client, struct stream *s = NULL; struct route_entry *re; unsigned long nump; - uint8_t num; + uint16_t num; struct nexthop *nh; struct route_node *rn; int ret; @@ -1212,7 +1211,7 @@ int zebra_send_rnh_update(struct rnh *rnh, struct zserv *client, stream_putl(s, re->metric); num = 0; nump = stream_get_endp(s); - stream_putc(s, 0); + stream_putw(s, 0); nhg = rib_get_fib_nhg(re); for (ALL_NEXTHOPS_PTR(nhg, nh)) @@ -1240,13 +1239,13 @@ int zebra_send_rnh_update(struct rnh *rnh, struct zserv *client, } } - stream_putc_at(s, nump, num); + stream_putw_at(s, nump, num); } else { stream_putc(s, 0); // type stream_putw(s, 0); // instance stream_putc(s, 0); // distance stream_putl(s, 0); // metric - stream_putc(s, 0); // nexthops + stream_putw(s, 0); // nexthops } stream_putw_at(s, 0, stream_get_endp(s)); @@ -1272,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) @@ -1293,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_script.c b/zebra/zebra_script.c index 6c34d12c6409..a0d5e2054cca 100644 --- a/zebra/zebra_script.c +++ b/zebra/zebra_script.c @@ -418,6 +418,7 @@ void lua_pushzebra_dplane_ctx(lua_State *L, const struct zebra_dplane_ctx *ctx) case DPLANE_OP_SRV6_ENCAP_SRCADDR_SET: case DPLANE_OP_NONE: case DPLANE_OP_STARTUP_STAGE: + case DPLANE_OP_VLAN_INSTALL: break; } /* Dispatch by op code */ } diff --git a/zebra/zebra_srte.c b/zebra/zebra_srte.c index c0b83382c457..bb8d4b3b4077 100644 --- a/zebra/zebra_srte.c +++ b/zebra/zebra_srte.c @@ -145,7 +145,7 @@ static int zebra_sr_policy_notify_update_client(struct zebra_sr_policy *policy, stream_putc(s, nhlfe->distance); stream_putl(s, 0); /* metric - not available */ nump = stream_get_endp(s); - stream_putc(s, 0); + stream_putw(s, 0); } zapi_nexthop_from_nexthop(&znh, nhlfe->nexthop); @@ -155,7 +155,7 @@ static int zebra_sr_policy_notify_update_client(struct zebra_sr_policy *policy, num++; } - stream_putc_at(s, nump, num); + stream_putw_at(s, nump, num); stream_putw_at(s, 0, stream_get_endp(s)); client->nh_last_upd_time = monotime(NULL); diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 3bf20ff42e7c..f3292fd85ae8 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)); diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index f1ae42e32043..0f72259951b0 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -6246,3 +6246,114 @@ extern void zebra_evpn_init(void) { hook_register(zserv_client_close, zebra_evpn_cfg_clean_up); } + +static const char *port_state2str(uint8_t state) +{ + switch (state) { + case ZEBRA_DPLANE_BR_STATE_DISABLED: + return "DISABLED"; + case ZEBRA_DPLANE_BR_STATE_LISTENING: + return "LISTENING"; + case ZEBRA_DPLANE_BR_STATE_LEARNING: + return "LEARNING"; + case ZEBRA_DPLANE_BR_STATE_FORWARDING: + return "FORWARDING"; + case ZEBRA_DPLANE_BR_STATE_BLOCKING: + return "BLOCKING"; + } + + return "UNKNOWN"; +} + +static void vxlan_vni_state_change(struct zebra_if *zif, uint16_t id, + uint8_t state) +{ + struct zebra_vxlan_vni *vnip; + + vnip = zebra_vxlan_if_vlanid_vni_find(zif, id); + + if (!vnip) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Cannot find VNI for VID (%u) IF %s for vlan state update", + id, zif->ifp->name); + + return; + } + + switch (state) { + case ZEBRA_DPLANE_BR_STATE_FORWARDING: + zebra_vxlan_if_vni_up(zif->ifp, vnip); + break; + case ZEBRA_DPLANE_BR_STATE_BLOCKING: + zebra_vxlan_if_vni_down(zif->ifp, vnip); + break; + case ZEBRA_DPLANE_BR_STATE_DISABLED: + case ZEBRA_DPLANE_BR_STATE_LISTENING: + case ZEBRA_DPLANE_BR_STATE_LEARNING: + default: + /* Not used for anything at the moment */ + break; + } +} + +static void vlan_id_range_state_change(struct interface *ifp, uint16_t id_start, + uint16_t id_end, uint8_t state) +{ + struct zebra_if *zif; + + zif = (struct zebra_if *)ifp->info; + + if (!zif) + return; + + for (uint16_t i = id_start; i <= id_end; i++) + vxlan_vni_state_change(zif, i, state); +} + +void zebra_vlan_dplane_result(struct zebra_dplane_ctx *ctx) +{ + int i; + struct interface *ifp = NULL; + ns_id_t ns_id = dplane_ctx_get_ns_id(ctx); + enum dplane_op_e op = dplane_ctx_get_op(ctx); + const struct zebra_vxlan_vlan_array *vlan_array = + dplane_ctx_get_vxlan_vlan_array(ctx); + ifindex_t ifindex = dplane_ctx_get_vlan_ifindex(ctx); + + ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), ifindex); + if (!ifp) { + zlog_debug("Cannot find bridge-vlan IF (%u) for vlan update", + ifindex); + return; + } + + if (!IS_ZEBRA_IF_VXLAN(ifp)) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("Ignoring non-vxlan IF (%s) for vlan update", + ifp->name); + + return; + } + + if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Dequeuing in zebra main..%s IF %s ifindex %u NS %u", + dplane_op2str(op), ifp->name, ifindex, ns_id); + + for (i = 0; i < vlan_array->count; i++) { + vlanid_t vid = vlan_array->vlans[i].vid; + uint8_t state = vlan_array->vlans[i].state; + uint32_t vrange = vlan_array->vlans[i].vrange; + + if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_VXLAN) { + if (vrange) + zlog_debug("VLANDB_ENTRY: VID (%u-%u) state=%s", + vid, vrange, port_state2str(state)); + else + zlog_debug("VLANDB_ENTRY: VID (%u) state=%s", + vid, port_state2str(state)); + } + + vlan_id_range_state_change(ifp, vid, (vrange ? vrange : vid), + state); + } +} diff --git a/zebra/zebra_vxlan.h b/zebra/zebra_vxlan.h index eb02de6f7b40..ef4c39c060fe 100644 --- a/zebra/zebra_vxlan.h +++ b/zebra/zebra_vxlan.h @@ -221,7 +221,7 @@ extern int zebra_vxlan_dp_network_mac_del(struct interface *ifp, extern void zebra_vxlan_set_accept_bgp_seq(bool set); extern bool zebra_vxlan_get_accept_bgp_seq(void); - +extern void zebra_vlan_dplane_result(struct zebra_dplane_ctx *ctx); #ifdef __cplusplus } #endif