From: Donatas Abraitis Date: Wed, 12 Oct 2022 18:06:47 +0000 (+0300) Subject: bgpd: Implement Accumulated IGP Metric Attribute for BGP X-Git-Tag: base_8.5~283^2 X-Git-Url: https://git.puffer.fish/?a=commitdiff_plain;h=97a52c82a569f4a2ba792fbd734f5e635a057e6f;p=mirror%2Ffrr.git bgpd: Implement Accumulated IGP Metric Attribute for BGP https://www.rfc-editor.org/rfc/rfc7311.html Signed-off-by: Donatas Abraitis --- diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index b7d0958bac..e9050c5aec 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -83,6 +83,7 @@ static const struct message attr_str[] = { {BGP_ATTR_LARGE_COMMUNITIES, "LARGE_COMMUNITY"}, {BGP_ATTR_PREFIX_SID, "PREFIX_SID"}, {BGP_ATTR_IPV6_EXT_COMMUNITIES, "IPV6_EXT_COMMUNITIES"}, + {BGP_ATTR_AIGP, "AIGP"}, {0}}; static const struct message attr_flag_str[] = { @@ -448,6 +449,110 @@ static void transit_unintern(struct transit **transit) } } +static bool bgp_attr_aigp_get_tlv_metric(uint8_t *pnt, int length, + uint64_t *aigp) +{ + uint8_t *data = pnt; + uint8_t tlv_type; + uint16_t tlv_length; + + while (length) { + tlv_type = *data; + ptr_get_be16(data + 1, &tlv_length); + (void)data; + + /* The value field of the AIGP TLV is always 8 octets + * long and its value is interpreted as an unsigned 64-bit + * integer. + */ + if (tlv_type == BGP_AIGP_TLV_METRIC) { + (void)ptr_get_be64(data + 3, aigp); + + /* If an AIGP attribute is received and its first AIGP + * TLV contains the maximum value 0xffffffffffffffff, + * the attribute SHOULD be considered to be malformed + * and SHOULD be discarded as specified in this section. + */ + if (*aigp == BGP_AIGP_TLV_METRIC_MAX) { + zlog_err("Bad AIGP TLV (%s) length: %llu", + BGP_AIGP_TLV_METRIC_DESC, + BGP_AIGP_TLV_METRIC_MAX); + return false; + } + + return true; + } + + data += tlv_length; + length -= tlv_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) +{ + stream_putc(s, BGP_AIGP_TLV_METRIC); + stream_putw(s, BGP_AIGP_TLV_METRIC_LEN); + stream_putq(s, bgp_aigp_metric_total(bpi)); +} + +static bool bgp_attr_aigp_valid(uint8_t *pnt, int length) +{ + uint8_t *data = pnt; + uint8_t tlv_type; + uint16_t tlv_length; + + if (length < 3) { + zlog_err("Bad AIGP attribute length (MUST be minimum 3): %u", + length); + return false; + } + + while (length) { + tlv_type = *data; + ptr_get_be16(data + 1, &tlv_length); + (void)data; + + if (length < tlv_length) { + zlog_err( + "Bad AIGP attribute length: %u, but TLV length: %u", + length, tlv_length); + return false; + } + + if (tlv_length < 3) { + zlog_err("Bad AIGP TLV length (MUST be minimum 3): %u", + tlv_length); + return false; + } + + /* AIGP TLV, Length: 11 */ + if (tlv_type == BGP_AIGP_TLV_METRIC && + tlv_length != BGP_AIGP_TLV_METRIC_LEN) { + zlog_err("Bad AIGP TLV (%s) length: %u", + BGP_AIGP_TLV_METRIC_DESC, tlv_length); + return false; + } + + data += tlv_length; + length -= tlv_length; + } + + return true; +} + static void *srv6_l3vpn_hash_alloc(void *p) { return p; @@ -702,6 +807,7 @@ unsigned int attrhash_key_make(const void *p) MIX(attr->nh_type); MIX(attr->bh_type); MIX(attr->otc); + MIX(bgp_attr_get_aigp_metric(attr)); return key; } @@ -736,6 +842,8 @@ bool attrhash_cmp(const void *p1, const void *p2) == bgp_attr_get_cluster(attr2) && bgp_attr_get_transit(attr1) == bgp_attr_get_transit(attr2) + && bgp_attr_get_aigp_metric(attr1) + == bgp_attr_get_aigp_metric(attr2) && attr1->rmap_table_id == attr2->rmap_table_id && (attr1->encap_tunneltype == attr2->encap_tunneltype) && encap_same(attr1->encap_subtlvs, attr2->encap_subtlvs) @@ -1387,6 +1495,7 @@ const uint8_t attr_flags_values[] = { [BGP_ATTR_PREFIX_SID] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS, [BGP_ATTR_IPV6_EXT_COMMUNITIES] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS, + [BGP_ATTR_AIGP] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS, }; static const size_t attr_flags_values_max = array_size(attr_flags_values) - 1; @@ -3053,6 +3162,45 @@ bgp_attr_pmsi_tunnel(struct bgp_attr_parser_args *args) return BGP_ATTR_PARSE_PROCEED; } +/* AIGP attribute (rfc7311) */ +static enum bgp_attr_parse_ret bgp_attr_aigp(struct bgp_attr_parser_args *args) +{ + struct peer *const peer = args->peer; + struct attr *const attr = args->attr; + const bgp_size_t length = args->length; + uint8_t *s = stream_pnt(peer->curr); + uint64_t aigp = 0; + + /* If an AIGP attribute is received on a BGP session for which + * AIGP_SESSION is disabled, the attribute MUST be treated exactly + * as if it were an unrecognized non-transitive attribute. + * That is, it "MUST be quietly ignored and not passed along to + * other BGP peers". + * For Internal BGP (IBGP) sessions, and for External BGP (EBGP) + * sessions between members of the same BGP Confederation, + * the default value of AIGP_SESSION SHOULD be "enabled". + */ + if (peer->sort == BGP_PEER_EBGP && + !CHECK_FLAG(peer->flags, PEER_FLAG_AIGP)) { + zlog_warn( + "%pBP received AIGP attribute, but eBGP peer do not support it", + peer); + goto aigp_ignore; + } + + if (!bgp_attr_aigp_valid(s, length)) + goto aigp_ignore; + + /* Extract AIGP Metric TLV */ + if (bgp_attr_aigp_get_tlv_metric(s, length, &aigp)) + bgp_attr_set_aigp_metric(attr, aigp); + +aigp_ignore: + stream_forward_getp(peer->curr, length); + + return BGP_ATTR_PARSE_PROCEED; +} + /* OTC attribute. */ static enum bgp_attr_parse_ret bgp_attr_otc(struct bgp_attr_parser_args *args) { @@ -3431,6 +3579,9 @@ enum bgp_attr_parse_ret bgp_attr_parse(struct peer *peer, struct attr *attr, case BGP_ATTR_OTC: ret = bgp_attr_otc(&attr_args); break; + case BGP_ATTR_AIGP: + ret = bgp_attr_aigp(&attr_args); + break; default: ret = bgp_attr_unknown(&attr_args); break; @@ -3951,7 +4102,8 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, struct prefix *p, afi_t afi, safi_t safi, struct peer *from, struct prefix_rd *prd, mpls_label_t *label, uint32_t num_labels, - bool addpath_capable, uint32_t addpath_tx_id) + bool addpath_capable, uint32_t addpath_tx_id, + struct bgp_path_info *bpi) { size_t cp; size_t aspath_sizep; @@ -4457,6 +4609,22 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, stream_putl(s, attr->otc); } + /* AIGP */ + if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_AIGP) && + (CHECK_FLAG(peer->flags, PEER_FLAG_AIGP) || + peer->sort != BGP_PEER_EBGP)) { + /* 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. + */ + uint8_t attr_len = BGP_AIGP_TLV_METRIC_LEN; + + 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); + } + /* Unknown transit attribute. */ struct transit *transit = bgp_attr_get_transit(attr); @@ -4540,7 +4708,7 @@ void bgp_attr_finish(void) } /* Make attribute packet. */ -void bgp_dump_routes_attr(struct stream *s, struct attr *attr, +void bgp_dump_routes_attr(struct stream *s, struct bgp_path_info *bpi, const struct prefix *prefix) { unsigned long cp; @@ -4549,6 +4717,7 @@ void bgp_dump_routes_attr(struct stream *s, struct attr *attr, struct aspath *aspath; bool addpath_capable = false; uint32_t addpath_tx_id = 0; + struct attr *attr = bpi->attr; /* Remember current pointer. */ cp = stream_get_endp(s); @@ -4712,6 +4881,20 @@ void bgp_dump_routes_attr(struct stream *s, struct attr *attr, stream_putl(s, attr->otc); } + /* AIGP */ + if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_AIGP)) { + /* 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. + */ + uint8_t attr_len = BGP_AIGP_TLV_METRIC_LEN; + + 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); + } + /* Return total size of attribute. */ len = stream_get_endp(s) - cp - 2; stream_putw_at(s, cp, len); diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index 41ae6ef49c..bc82d0c6ed 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -329,6 +329,9 @@ struct attr { /* OTC value if set */ uint32_t otc; + + /* AIGP Metric */ + uint64_t aigp_metric; }; /* rmap_change_flags definition */ @@ -396,15 +399,13 @@ 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, uint32_t num_labels, - bool addpath_capable, - uint32_t addpath_tx_id); -extern void bgp_dump_routes_attr(struct stream *s, struct attr *attr, +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, uint32_t num_labels, bool addpath_capable, + uint32_t addpath_tx_id, struct bgp_path_info *bpi); +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); extern unsigned int attrhash_key_make(const void *p); @@ -585,6 +586,19 @@ static inline void bgp_attr_set_transit(struct attr *attr, attr->transit = transit; } +static inline uint64_t bgp_attr_get_aigp_metric(const struct attr *attr) +{ + return attr->aigp_metric; +} + +static inline void bgp_attr_set_aigp_metric(struct attr *attr, uint64_t aigp) +{ + attr->aigp_metric = aigp; + + if (aigp) + attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AIGP); +} + static inline struct cluster_list *bgp_attr_get_cluster(const struct attr *attr) { return attr->cluster1; diff --git a/bgpd/bgp_bmp.c b/bgpd/bgp_bmp.c index bcab4099c0..2e5c772679 100644 --- a/bgpd/bgp_bmp.c +++ b/bgpd/bgp_bmp.c @@ -853,8 +853,9 @@ 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); + total_attr_len = + bgp_packet_attribute(NULL, peer, s, attr, &vecarr, NULL, afi, + safi, peer, NULL, NULL, 0, 0, 0, NULL); /* space check? */ diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index 580c18b58d..a629695a3b 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -412,6 +412,11 @@ bool bgp_dump_attr(struct attr *attr, char *buf, size_t size) snprintf(buf + strlen(buf), size - strlen(buf), ", localpref %u", attr->local_pref); + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_AIGP))) + snprintf(buf + strlen(buf), size - strlen(buf), + ", aigp-metric %" PRIu64, + (unsigned long long)bgp_attr_get_aigp_metric(attr)); + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC))) snprintf(buf + strlen(buf), size - strlen(buf), ", metric %u", attr->med); diff --git a/bgpd/bgp_dump.c b/bgpd/bgp_dump.c index 11e84f00b4..254996edad 100644 --- a/bgpd/bgp_dump.c +++ b/bgpd/bgp_dump.c @@ -376,7 +376,7 @@ bgp_dump_route_node_record(int afi, struct bgp_dest *dest, /* Dump attribute. */ /* Skip prefix & AFI/SAFI for MP_NLRI */ - bgp_dump_routes_attr(obuf, path->attr, p); + bgp_dump_routes_attr(obuf, path, p); cur_endp = stream_get_endp(obuf); if (cur_endp > BGP_STANDARD_MESSAGE_MAX_PACKET_SIZE diff --git a/bgpd/bgp_errors.c b/bgpd/bgp_errors.c index 9f87f8ce7a..8bc2dbca7a 100644 --- a/bgpd/bgp_errors.c +++ b/bgpd/bgp_errors.c @@ -235,6 +235,12 @@ static struct log_ref ferr_bgp_err[] = { .description = "BGP update has invalid length for PMSI tunnel", .suggestion = "Determine the source of the update and determine why the PMSI tunnel attribute length has been set incorrectly" }, + { + .code = EC_BGP_ATTR_AIGP, + .title = "BGP AIGP attribute is incorrect", + .description = "BGP AIGP attribute is incorrect", + .suggestion = "Determine the source of the attribute and determine why the AIGP attribute has been set incorrectly" + }, { .code = EC_BGP_PEER_GROUP, .title = "BGP peergroup operated on in error", diff --git a/bgpd/bgp_errors.h b/bgpd/bgp_errors.h index 0c0917c49e..4986c314a1 100644 --- a/bgpd/bgp_errors.h +++ b/bgpd/bgp_errors.h @@ -34,6 +34,7 @@ enum bgp_log_refs { EC_BGP_ATTR_PMSI_TYPE, EC_BGP_ATTR_PMSI_LEN, EC_BGP_ATTR_NH_SEND_LEN, + EC_BGP_ATTR_AIGP, EC_BGP_PEER_GROUP, EC_BGP_PEER_DELETE, EC_BGP_TABLE_CHUNK, diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 38332bd6fd..d71e2f79a3 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -920,6 +920,36 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, } } + /* 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); + + if (new_aigp < exist_aigp) { + *reason = bgp_path_selection_aigp; + if (debug) + zlog_debug( + "%s: %s wins over %s due to AIGP %" PRIu64 + " < %" PRIu64, + pfx_buf, new_buf, exist_buf, new_aigp, + exist_aigp); + return 1; + } + + if (new_aigp > exist_aigp) { + *reason = bgp_path_selection_aigp; + if (debug) + zlog_debug( + "%s: %s loses to %s due to AIGP %" PRIu64 + " > %" PRIu64, + pfx_buf, new_buf, exist_buf, new_aigp, + exist_aigp); + return 0; + } + } + /* 3. Local route check. We prefer: * - BGP_ROUTE_STATIC * - BGP_ROUTE_AGGREGATE @@ -6139,6 +6169,9 @@ void bgp_static_update(struct bgp *bgp, const struct prefix *p, 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); @@ -8684,6 +8717,9 @@ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p, 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); @@ -8693,8 +8729,10 @@ 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) { @@ -8944,6 +8982,8 @@ const char *bgp_path_selection_reason2str(enum bgp_path_selection_reason reason) return "Accept Own"; case bgp_path_selection_local_route: return "Local Route"; + case bgp_path_selection_aigp: + return "AIGP"; case bgp_path_selection_confed_as_path: return "Confederation based AS Path"; case bgp_path_selection_as_path: @@ -10678,6 +10718,15 @@ 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 (json_paths) + json_object_int_add(json_path, "aigpMetric", + bgp_attr_get_aigp_metric(attr)); + else + vty_out(vty, ", aigp-metric %" PRIu64, + bgp_attr_get_aigp_metric(attr)); + } + if (attr->weight != 0) { if (json_paths) json_object_int_add(json_path, "weight", attr->weight); diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 233fd55ef0..637f462e8d 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -3043,6 +3043,46 @@ static const struct route_map_rule_cmd route_set_atomic_aggregate_cmd = { route_set_atomic_aggregate_free, }; +/* AIGP TLV Metric */ +static enum route_map_cmd_result_t +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; + + bgp_attr_set_aigp_metric(path->attr, path->nexthop->metric); + } else { + aigp = atoi(aigp_metric); + bgp_attr_set_aigp_metric(path->attr, aigp); + } + + path->attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AIGP); + + return RMAP_OKAY; +} + +static void *route_set_aigp_metric_compile(const char *arg) +{ + return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); +} + +static void route_set_aigp_metric_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +static const struct route_map_rule_cmd route_set_aigp_metric_cmd = { + "aigp-metric", + route_set_aigp_metric, + route_set_aigp_metric_compile, + route_set_aigp_metric_free, +}; + /* `set aggregator as AS A.B.C.D' */ struct aggregator { as_t as; @@ -6354,6 +6394,42 @@ DEFUN_YANG (no_set_atomic_aggregate, return nb_cli_apply_changes(vty, NULL); } +DEFPY_YANG (set_aigp_metric, + set_aigp_metric_cmd, + "set aigp-metric $aigp_metric", + SET_STR + "BGP AIGP attribute (AIGP Metric TLV)\n" + "AIGP Metric value from IGP protocol\n" + "Manual AIGP Metric value\n") +{ + const char *xpath = + "./set-action[action='frr-bgp-route-map:aigp-metric']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:aigp-metric", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, aigp_metric); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG (no_set_aigp_metric, + no_set_aigp_metric_cmd, + "no set aigp-metric []", + NO_STR + SET_STR + "BGP AIGP attribute (AIGP Metric TLV)\n" + "AIGP Metric value from IGP protocol\n" + "Manual AIGP Metric value\n") +{ + const char *xpath = + "./set-action[action='frr-bgp-route-map:aigp-metric']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + DEFUN_YANG (set_aggregator_as, set_aggregator_as_cmd, "set aggregator as (1-4294967295) A.B.C.D", @@ -7013,6 +7089,7 @@ void bgp_route_map_init(void) route_map_install_set(&route_set_aspath_replace_cmd); route_map_install_set(&route_set_origin_cmd); route_map_install_set(&route_set_atomic_aggregate_cmd); + route_map_install_set(&route_set_aigp_metric_cmd); route_map_install_set(&route_set_aggregator_as_cmd); route_map_install_set(&route_set_community_cmd); route_map_install_set(&route_set_community_delete_cmd); @@ -7095,6 +7172,8 @@ void bgp_route_map_init(void) install_element(RMAP_NODE, &no_set_origin_cmd); install_element(RMAP_NODE, &set_atomic_aggregate_cmd); install_element(RMAP_NODE, &no_set_atomic_aggregate_cmd); + install_element(RMAP_NODE, &set_aigp_metric_cmd); + install_element(RMAP_NODE, &no_set_aigp_metric_cmd); install_element(RMAP_NODE, &set_aggregator_as_cmd); install_element(RMAP_NODE, &no_set_aggregator_as_cmd); install_element(RMAP_NODE, &set_community_cmd); diff --git a/bgpd/bgp_routemap_nb.c b/bgpd/bgp_routemap_nb.c index c47c37dc66..35e4703492 100644 --- a/bgpd/bgp_routemap_nb.c +++ b/bgpd/bgp_routemap_nb.c @@ -282,6 +282,13 @@ const struct frr_yang_module_info frr_bgp_route_map_info = { .destroy = lib_route_map_entry_set_action_rmap_set_action_atomic_aggregate_destroy, } }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:aigp-metric", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_aigp_metric_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_aigp_metric_destroy, + } + }, { .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:prepend-as-path", .cbs = { diff --git a/bgpd/bgp_routemap_nb.h b/bgpd/bgp_routemap_nb.h index 163e3b55cc..9332044158 100644 --- a/bgpd/bgp_routemap_nb.h +++ b/bgpd/bgp_routemap_nb.h @@ -106,6 +106,10 @@ int lib_route_map_entry_set_action_rmap_set_action_table_modify(struct nb_cb_mod int lib_route_map_entry_set_action_rmap_set_action_table_destroy(struct nb_cb_destroy_args *args); int lib_route_map_entry_set_action_rmap_set_action_atomic_aggregate_create(struct nb_cb_create_args *args); int lib_route_map_entry_set_action_rmap_set_action_atomic_aggregate_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_aigp_metric_modify( + struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_aigp_metric_destroy( + struct nb_cb_destroy_args *args); int lib_route_map_entry_set_action_rmap_set_action_prepend_as_path_modify(struct nb_cb_modify_args *args); int lib_route_map_entry_set_action_rmap_set_action_prepend_as_path_destroy(struct nb_cb_destroy_args *args); int lib_route_map_entry_set_action_rmap_set_action_last_as_modify(struct nb_cb_modify_args *args); diff --git a/bgpd/bgp_routemap_nb_config.c b/bgpd/bgp_routemap_nb_config.c index b18cf9d4dd..216bcc0b07 100644 --- a/bgpd/bgp_routemap_nb_config.c +++ b/bgpd/bgp_routemap_nb_config.c @@ -2088,6 +2088,58 @@ lib_route_map_entry_set_action_rmap_set_action_atomic_aggregate_destroy( return NB_OK; } +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:aigp-metric + */ +int lib_route_map_entry_set_action_rmap_set_action_aigp_metric_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *aigp; + int rv; + + 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); + aigp = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "aigp-metric"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, rhc->rhc_rule, aigp, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int lib_route_map_entry_set_action_rmap_set_action_aigp_metric_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_set_destroy(args); + } + + return NB_OK; +} + /* * XPath: * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:prepend-as-path diff --git a/bgpd/bgp_table.h b/bgpd/bgp_table.h index 91d92ebd6d..121afc481f 100644 --- a/bgpd/bgp_table.h +++ b/bgpd/bgp_table.h @@ -65,6 +65,7 @@ enum bgp_path_selection_reason { bgp_path_selection_local_pref, bgp_path_selection_accept_own, bgp_path_selection_local_route, + bgp_path_selection_aigp, bgp_path_selection_confed_as_path, bgp_path_selection_as_path, bgp_path_selection_origin, diff --git a/bgpd/bgp_updgrp.c b/bgpd/bgp_updgrp.c index 0219535b0d..9d550fd19b 100644 --- a/bgpd/bgp_updgrp.c +++ b/bgpd/bgp_updgrp.c @@ -436,6 +436,11 @@ static unsigned int updgrp_hash_key_make(const void *p) */ key = jhash_1word(peer->local_role, key); + /* Neighbors configured with the AIGP attribute are put in a separate + * update group from other neighbors. + */ + key = jhash_1word((peer->flags & PEER_FLAG_AIGP), key); + if (peer->soo[afi][safi]) { char *soo_str = ecommunity_str(peer->soo[afi][safi]); diff --git a/bgpd/bgp_updgrp_packet.c b/bgpd/bgp_updgrp_packet.c index 88a81f255d..dd073c9c49 100644 --- a/bgpd/bgp_updgrp_packet.c +++ b/bgpd/bgp_updgrp_packet.c @@ -752,7 +752,7 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp) * attr. */ total_attr_len = bgp_packet_attribute( NULL, peer, s, adv->baa->attr, &vecarr, NULL, - afi, safi, from, NULL, NULL, 0, 0, 0); + afi, safi, from, NULL, NULL, 0, 0, 0, path); space_remaining = STREAM_CONCAT_REMAIN(s, snlri, STREAM_SIZE(s)) @@ -1125,7 +1125,8 @@ void subgroup_default_update_packet(struct update_subgroup *subgrp, stream_putw(s, 0); total_attr_len = bgp_packet_attribute( NULL, peer, s, attr, &vecarr, &p, afi, safi, from, NULL, NULL, - 0, addpath_capable, BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE); + 0, addpath_capable, BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE, + NULL); /* 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 ec0fcd8f11..d01d508d6e 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -3575,6 +3575,26 @@ DEFUN (no_bgp_fast_external_failover, return CMD_SUCCESS; } +DEFPY (bgp_bestpath_aigp, + bgp_bestpath_aigp_cmd, + "[no$no] bgp bestpath aigp", + NO_STR + BGP_STR + "Change the default bestpath selection\n" + "Evaluate the AIGP attribute during the best path selection process\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + + if (no) + UNSET_FLAG(bgp->flags, BGP_FLAG_COMPARE_AIGP); + else + SET_FLAG(bgp->flags, BGP_FLAG_COMPARE_AIGP); + + bgp_recalculate_all_bestpaths(bgp); + + return CMD_SUCCESS; +} + /* "bgp bestpath compare-routerid" configuration. */ DEFUN (bgp_bestpath_compare_router_id, bgp_bestpath_compare_router_id_cmd, @@ -6555,6 +6575,26 @@ DEFUN (no_neighbor_ebgp_multihop, return peer_ebgp_multihop_unset_vty(vty, argv[idx_peer]->arg); } +DEFPY (neighbor_aigp, + neighbor_aigp_cmd, + "[no$no] neighbor $neighbor aigp", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Enable send and receive of the AIGP attribute per neighbor\n") +{ + struct peer *peer; + + peer = peer_and_group_lookup_vty(vty, neighbor); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + if (no) + return peer_flag_unset_vty(vty, neighbor, PEER_FLAG_AIGP); + else + return peer_flag_set_vty(vty, neighbor, PEER_FLAG_AIGP); +} + static uint8_t get_role_by_name(const char *role_str) { if (strncmp(role_str, "peer", 2) == 0) @@ -17162,6 +17202,10 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp, } } + /* aigp */ + if (peergroup_flag_check(peer, PEER_FLAG_AIGP)) + vty_out(vty, " neighbor %s aigp\n", addr); + /* role */ if (peergroup_flag_check(peer, PEER_FLAG_ROLE) && peer->local_role != ROLE_UNDEFINED) @@ -17996,6 +18040,8 @@ int bgp_config_write(struct vty *vty) } if (CHECK_FLAG(bgp->flags, BGP_FLAG_COMPARE_ROUTER_ID)) vty_out(vty, " bgp bestpath compare-routerid\n"); + if (CHECK_FLAG(bgp->flags, BGP_FLAG_COMPARE_AIGP)) + vty_out(vty, " bgp bestpath aigp\n"); if (CHECK_FLAG(bgp->flags, BGP_FLAG_MED_CONFED) || CHECK_FLAG(bgp->flags, BGP_FLAG_MED_MISSING_AS_WORST)) { vty_out(vty, " bgp bestpath med"); @@ -18570,6 +18616,9 @@ void bgp_vty_init(void) install_element(BGP_NODE, &neighbor_role_strict_cmd); install_element(BGP_NODE, &no_neighbor_role_cmd); + /* "neighbor aigp" commands. */ + install_element(BGP_NODE, &neighbor_aigp_cmd); + /* bgp disable-ebgp-connected-nh-check */ install_element(BGP_NODE, &bgp_disable_connected_route_check_cmd); install_element(BGP_NODE, &no_bgp_disable_connected_route_check_cmd); @@ -18704,6 +18753,9 @@ void bgp_vty_init(void) install_element(BGP_NODE, &bgp_fast_external_failover_cmd); install_element(BGP_NODE, &no_bgp_fast_external_failover_cmd); + /* "bgp bestpath aigp" commands */ + install_element(BGP_NODE, &bgp_bestpath_aigp_cmd); + /* "bgp bestpath compare-routerid" commands */ install_element(BGP_NODE, &bgp_bestpath_compare_router_id_cmd); install_element(BGP_NODE, &no_bgp_bestpath_compare_router_id_cmd); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 005d6b3092..5199edb97e 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -4281,6 +4281,7 @@ static const struct peer_flag_action peer_flag_action_list[] = { {PEER_FLAG_ROLE_STRICT_MODE, 0, peer_change_reset}, {PEER_FLAG_ROLE, 0, peer_change_reset}, {PEER_FLAG_PORT, 0, peer_change_reset}, + {PEER_FLAG_AIGP, 0, peer_change_none}, {0, 0, 0}}; static const struct peer_flag_action peer_af_flag_action_list[] = { diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index c844f1530f..b400e5210c 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -494,44 +494,46 @@ struct bgp { /* BGP flags. */ uint64_t flags; -#define BGP_FLAG_ALWAYS_COMPARE_MED (1 << 0) -#define BGP_FLAG_DETERMINISTIC_MED (1 << 1) -#define BGP_FLAG_MED_MISSING_AS_WORST (1 << 2) -#define BGP_FLAG_MED_CONFED (1 << 3) -#define BGP_FLAG_NO_CLIENT_TO_CLIENT (1 << 4) -#define BGP_FLAG_COMPARE_ROUTER_ID (1 << 5) -#define BGP_FLAG_ASPATH_IGNORE (1 << 6) -#define BGP_FLAG_IMPORT_CHECK (1 << 7) -#define BGP_FLAG_NO_FAST_EXT_FAILOVER (1 << 8) -#define BGP_FLAG_LOG_NEIGHBOR_CHANGES (1 << 9) +#define BGP_FLAG_ALWAYS_COMPARE_MED (1ULL << 0) +#define BGP_FLAG_DETERMINISTIC_MED (1ULL << 1) +#define BGP_FLAG_MED_MISSING_AS_WORST (1ULL << 2) +#define BGP_FLAG_MED_CONFED (1ULL << 3) +#define BGP_FLAG_NO_CLIENT_TO_CLIENT (1ULL << 4) +#define BGP_FLAG_COMPARE_ROUTER_ID (1ULL << 5) +#define BGP_FLAG_ASPATH_IGNORE (1ULL << 6) +#define BGP_FLAG_IMPORT_CHECK (1ULL << 7) +#define BGP_FLAG_NO_FAST_EXT_FAILOVER (1ULL << 8) +#define BGP_FLAG_LOG_NEIGHBOR_CHANGES (1ULL << 9) /* This flag is set when we have full BGP Graceful-Restart mode enable */ -#define BGP_FLAG_GRACEFUL_RESTART (1 << 10) - -#define BGP_FLAG_ASPATH_CONFED (1 << 11) -#define BGP_FLAG_ASPATH_MULTIPATH_RELAX (1 << 12) -#define BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY (1 << 13) -#define BGP_FLAG_DISABLE_NH_CONNECTED_CHK (1 << 14) -#define BGP_FLAG_MULTIPATH_RELAX_AS_SET (1 << 15) -#define BGP_FLAG_FORCE_STATIC_PROCESS (1 << 16) -#define BGP_FLAG_SHOW_HOSTNAME (1 << 17) -#define BGP_FLAG_GR_PRESERVE_FWD (1 << 18) -#define BGP_FLAG_GRACEFUL_SHUTDOWN (1 << 19) -#define BGP_FLAG_DELETE_IN_PROGRESS (1 << 20) -#define BGP_FLAG_SELECT_DEFER_DISABLE (1 << 21) -#define BGP_FLAG_GR_DISABLE_EOR (1 << 22) -#define BGP_FLAG_EBGP_REQUIRES_POLICY (1 << 23) -#define BGP_FLAG_SHOW_NEXTHOP_HOSTNAME (1 << 24) +#define BGP_FLAG_GRACEFUL_RESTART (1ULL << 10) + +#define BGP_FLAG_ASPATH_CONFED (1ULL << 11) +#define BGP_FLAG_ASPATH_MULTIPATH_RELAX (1ULL << 12) +#define BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY (1ULL << 13) +#define BGP_FLAG_DISABLE_NH_CONNECTED_CHK (1ULL << 14) +#define BGP_FLAG_MULTIPATH_RELAX_AS_SET (1ULL << 15) +#define BGP_FLAG_FORCE_STATIC_PROCESS (1ULL << 16) +#define BGP_FLAG_SHOW_HOSTNAME (1ULL << 17) +#define BGP_FLAG_GR_PRESERVE_FWD (1ULL << 18) +#define BGP_FLAG_GRACEFUL_SHUTDOWN (1ULL << 19) +#define BGP_FLAG_DELETE_IN_PROGRESS (1ULL << 20) +#define BGP_FLAG_SELECT_DEFER_DISABLE (1ULL << 21) +#define BGP_FLAG_GR_DISABLE_EOR (1ULL << 22) +#define BGP_FLAG_EBGP_REQUIRES_POLICY (1ULL << 23) +#define BGP_FLAG_SHOW_NEXTHOP_HOSTNAME (1ULL << 24) /* This flag is set if the instance is in administrative shutdown */ -#define BGP_FLAG_SHUTDOWN (1 << 25) -#define BGP_FLAG_SUPPRESS_FIB_PENDING (1 << 26) -#define BGP_FLAG_SUPPRESS_DUPLICATES (1 << 27) -#define BGP_FLAG_PEERTYPE_MULTIPATH_RELAX (1 << 29) +#define BGP_FLAG_SHUTDOWN (1ULL << 25) +#define BGP_FLAG_SUPPRESS_FIB_PENDING (1ULL << 26) +#define BGP_FLAG_SUPPRESS_DUPLICATES (1ULL << 27) +#define BGP_FLAG_PEERTYPE_MULTIPATH_RELAX (1ULL << 29) /* Indicate Graceful Restart support for BGP NOTIFICATION messages */ -#define BGP_FLAG_GRACEFUL_NOTIFICATION (1 << 30) +#define BGP_FLAG_GRACEFUL_NOTIFICATION (1ULL << 30) /* Send Hard Reset CEASE Notification for 'Administrative Reset' */ -#define BGP_FLAG_HARD_ADMIN_RESET (1 << 31) +#define BGP_FLAG_HARD_ADMIN_RESET (1ULL << 31) +/* Evaluate the AIGP attribute during the best path selection process */ +#define BGP_FLAG_COMPARE_AIGP (1ULL << 32) /* BGP default address-families. * New peers inherit enabled afi/safis from bgp instance. @@ -1405,6 +1407,7 @@ struct peer { /* `local-role` configured */ #define PEER_FLAG_ROLE (1ULL << 32) #define PEER_FLAG_PORT (1ULL << 33) +#define PEER_FLAG_AIGP (1ULL << 34) /* *GR-Disabled mode means unset PEER_FLAG_GRACEFUL_RESTART @@ -1898,6 +1901,7 @@ struct bgp_nlri { #define BGP_ATTR_PMSI_TUNNEL 22 #define BGP_ATTR_ENCAP 23 #define BGP_ATTR_IPV6_EXT_COMMUNITIES 25 +#define BGP_ATTR_AIGP 26 #define BGP_ATTR_LARGE_COMMUNITIES 32 #define BGP_ATTR_OTC 35 #define BGP_ATTR_PREFIX_SID 40 @@ -2026,6 +2030,13 @@ struct bgp_nlri { #define BGP_DYNAMIC_NEIGHBORS_LIMIT_MIN 1 #define BGP_DYNAMIC_NEIGHBORS_LIMIT_MAX 65535 +/* BGP AIGP */ +#define BGP_AIGP_TLV_RESERVED 0 /* AIGP Reserved */ +#define BGP_AIGP_TLV_METRIC 1 /* AIGP Metric */ +#define BGP_AIGP_TLV_METRIC_LEN 11 +#define BGP_AIGP_TLV_METRIC_MAX 0xffffffffffffffffULL +#define BGP_AIGP_TLV_METRIC_DESC "Accumulated IGP Metric" + /* Flag for peer_clear_soft(). */ enum bgp_clear_type { BGP_CLEAR_SOFT_NONE, diff --git a/lib/routemap.h b/lib/routemap.h index c2e9de6cfb..9c78e15735 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -348,6 +348,7 @@ DECLARE_QOBJ_TYPE(route_map); (strmatch(A, "frr-bgp-route-map:set-origin")) #define IS_SET_ATOMIC_AGGREGATE(A) \ (strmatch(A, "frr-bgp-route-map:atomic-aggregate")) +#define IS_SET_AIGP_METRIC(A) (strmatch(A, "frr-bgp-route-map:aigp-metric")) #define IS_SET_ORIGINATOR_ID(A) \ (strmatch(A, "frr-bgp-route-map:originator-id")) #define IS_SET_COMM_LIST_DEL(A) \ diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c index 6be5d15ec4..e3da29bd71 100644 --- a/lib/routemap_cli.c +++ b/lib/routemap_cli.c @@ -1089,6 +1089,11 @@ void route_map_action_show(struct vty *vty, const struct lyd_node *dnode, "./rmap-set-action/frr-bgp-route-map:origin")); } else if (IS_SET_ATOMIC_AGGREGATE(action)) { vty_out(vty, " set atomic-aggregate\n"); + } else if (IS_SET_AIGP_METRIC(action)) { + vty_out(vty, " set aigp-metric %s\n", + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-bgp-route-map:aigp-metric")); } else if (IS_SET_ORIGINATOR_ID(action)) { vty_out(vty, " set originator-id %s\n", yang_dnode_get_string( diff --git a/tests/topotests/bgp_aigp/__init__.py b/tests/topotests/bgp_aigp/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bgp_aigp/r1/bgpd.conf b/tests/topotests/bgp_aigp/r1/bgpd.conf new file mode 100644 index 0000000000..74a0215bc4 --- /dev/null +++ b/tests/topotests/bgp_aigp/r1/bgpd.conf @@ -0,0 +1,12 @@ +router bgp 65001 + no bgp ebgp-requires-policy + no bgp network import-check + 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.3 remote-as internal + neighbor 10.0.0.3 timers 1 3 + neighbor 10.0.0.3 timers connect 1 + neighbor 10.0.0.3 update-source lo +! diff --git a/tests/topotests/bgp_aigp/r1/ospfd.conf b/tests/topotests/bgp_aigp/r1/ospfd.conf new file mode 100644 index 0000000000..38aa11d036 --- /dev/null +++ b/tests/topotests/bgp_aigp/r1/ospfd.conf @@ -0,0 +1,17 @@ +! +interface lo + ip ospf cost 10 +! +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 30 +! +router ospf + router-id 10.0.0.1 + network 0.0.0.0/0 area 0 diff --git a/tests/topotests/bgp_aigp/r1/zebra.conf b/tests/topotests/bgp_aigp/r1/zebra.conf new file mode 100644 index 0000000000..0ed22d37be --- /dev/null +++ b/tests/topotests/bgp_aigp/r1/zebra.conf @@ -0,0 +1,10 @@ +! +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 +! diff --git a/tests/topotests/bgp_aigp/r2/bgpd.conf b/tests/topotests/bgp_aigp/r2/bgpd.conf new file mode 100644 index 0000000000..ae72f215c0 --- /dev/null +++ b/tests/topotests/bgp_aigp/r2/bgpd.conf @@ -0,0 +1,14 @@ +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 timers 1 3 + neighbor 10.0.0.1 timers connect 1 + neighbor 192.168.24.4 remote-as external + neighbor 192.168.24.4 timers 1 3 + neighbor 192.168.24.4 timers connect 1 + neighbor 192.168.24.4 aigp + address-family ipv4 + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_aigp/r2/ospfd.conf b/tests/topotests/bgp_aigp/r2/ospfd.conf new file mode 100644 index 0000000000..ed31941f65 --- /dev/null +++ b/tests/topotests/bgp_aigp/r2/ospfd.conf @@ -0,0 +1,13 @@ +! +interface lo + ip ospf cost 10 +! +interface r2-eth0 + ip ospf dead-interval 4 + ip ospf hello-interval 1 + ip ospf cost 10 +! +router ospf + router-id 10.0.0.2 + network 192.168.12.0/24 area 0 + network 10.0.0.2/32 area 0 diff --git a/tests/topotests/bgp_aigp/r2/zebra.conf b/tests/topotests/bgp_aigp/r2/zebra.conf new file mode 100644 index 0000000000..6d6a557caa --- /dev/null +++ b/tests/topotests/bgp_aigp/r2/zebra.conf @@ -0,0 +1,10 @@ +! +interface lo + ip address 10.0.0.2/32 +! +interface r2-eth0 + ip address 192.168.12.2/24 +! +interface r2-eth1 + ip address 192.168.24.2/24 +! diff --git a/tests/topotests/bgp_aigp/r3/bgpd.conf b/tests/topotests/bgp_aigp/r3/bgpd.conf new file mode 100644 index 0000000000..7572e268c5 --- /dev/null +++ b/tests/topotests/bgp_aigp/r3/bgpd.conf @@ -0,0 +1,14 @@ +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 timers 1 3 + neighbor 10.0.0.1 timers connect 1 + neighbor 192.168.35.5 remote-as external + neighbor 192.168.35.5 timers 1 3 + neighbor 192.168.35.5 timers connect 1 + neighbor 192.168.35.5 aigp + address-family ipv4 + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_aigp/r3/ospfd.conf b/tests/topotests/bgp_aigp/r3/ospfd.conf new file mode 100644 index 0000000000..f971ad6f89 --- /dev/null +++ b/tests/topotests/bgp_aigp/r3/ospfd.conf @@ -0,0 +1,13 @@ +! +interface lo + ip ospf cost 10 +! +interface r3-eth0 + ip ospf dead-interval 4 + ip ospf hello-interval 1 + ip ospf cost 30 +! +router ospf + router-id 10.0.0.3 + network 192.168.13.0/24 area 0 + network 10.0.0.3/32 area 0 diff --git a/tests/topotests/bgp_aigp/r3/zebra.conf b/tests/topotests/bgp_aigp/r3/zebra.conf new file mode 100644 index 0000000000..82c87d5504 --- /dev/null +++ b/tests/topotests/bgp_aigp/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.35.3/24 +! diff --git a/tests/topotests/bgp_aigp/r4/bgpd.conf b/tests/topotests/bgp_aigp/r4/bgpd.conf new file mode 100644 index 0000000000..d2b96b7b0a --- /dev/null +++ b/tests/topotests/bgp_aigp/r4/bgpd.conf @@ -0,0 +1,16 @@ +router bgp 65002 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.24.2 remote-as external + neighbor 192.168.24.2 timers 1 3 + neighbor 192.168.24.2 timers connect 1 + neighbor 192.168.24.2 aigp + neighbor 10.0.0.6 remote-as internal + neighbor 10.0.0.6 timers 1 3 + neighbor 10.0.0.6 timers connect 1 + neighbor 10.0.0.6 update-source lo + address-family ipv4 + redistribute connected + redistribute ospf + exit-address-family +! diff --git a/tests/topotests/bgp_aigp/r4/ospfd.conf b/tests/topotests/bgp_aigp/r4/ospfd.conf new file mode 100644 index 0000000000..c9e6796f6e --- /dev/null +++ b/tests/topotests/bgp_aigp/r4/ospfd.conf @@ -0,0 +1,13 @@ +! +interface lo + ip ospf cost 10 +! +interface r4-eth1 + ip ospf dead-interval 4 + ip ospf hello-interval 1 + ip ospf cost 20 +! +router ospf + router-id 10.0.0.4 + network 192.168.46.0/24 area 0 + network 10.0.0.4/32 area 0 diff --git a/tests/topotests/bgp_aigp/r4/zebra.conf b/tests/topotests/bgp_aigp/r4/zebra.conf new file mode 100644 index 0000000000..5f544e855c --- /dev/null +++ b/tests/topotests/bgp_aigp/r4/zebra.conf @@ -0,0 +1,10 @@ +! +interface lo + ip address 10.0.0.4/32 +! +interface r4-eth0 + ip address 192.168.24.4/24 +! +interface r4-eth1 + ip address 192.168.46.4/24 +! diff --git a/tests/topotests/bgp_aigp/r5/bgpd.conf b/tests/topotests/bgp_aigp/r5/bgpd.conf new file mode 100644 index 0000000000..944872289d --- /dev/null +++ b/tests/topotests/bgp_aigp/r5/bgpd.conf @@ -0,0 +1,16 @@ +router bgp 65002 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.35.3 remote-as external + neighbor 192.168.35.3 timers 1 3 + neighbor 192.168.35.3 timers connect 1 + neighbor 192.168.35.3 aigp + neighbor 10.0.0.6 remote-as internal + neighbor 10.0.0.6 timers 1 3 + neighbor 10.0.0.6 timers connect 1 + neighbor 10.0.0.6 update-source lo + address-family ipv4 + redistribute connected + redistribute ospf + exit-address-family +! diff --git a/tests/topotests/bgp_aigp/r5/ospfd.conf b/tests/topotests/bgp_aigp/r5/ospfd.conf new file mode 100644 index 0000000000..b853c74102 --- /dev/null +++ b/tests/topotests/bgp_aigp/r5/ospfd.conf @@ -0,0 +1,13 @@ +! +interface lo + ip ospf cost 10 +! +interface r5-eth1 + ip ospf dead-interval 4 + ip ospf hello-interval 1 + ip ospf cost 10 +! +router ospf + router-id 10.0.0.5 + network 192.168.56.0/24 area 0 + network 10.0.0.5/32 area 0 diff --git a/tests/topotests/bgp_aigp/r5/zebra.conf b/tests/topotests/bgp_aigp/r5/zebra.conf new file mode 100644 index 0000000000..69b8bf2c3e --- /dev/null +++ b/tests/topotests/bgp_aigp/r5/zebra.conf @@ -0,0 +1,10 @@ +! +interface lo + ip address 10.0.0.5/32 +! +interface r5-eth0 + ip address 192.168.35.5/24 +! +interface r5-eth1 + ip address 192.168.56.5/24 +! diff --git a/tests/topotests/bgp_aigp/r6/bgpd.conf b/tests/topotests/bgp_aigp/r6/bgpd.conf new file mode 100644 index 0000000000..15d9437a8e --- /dev/null +++ b/tests/topotests/bgp_aigp/r6/bgpd.conf @@ -0,0 +1,20 @@ +router bgp 65002 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 10.0.0.4 remote-as internal + neighbor 10.0.0.4 timers 1 3 + neighbor 10.0.0.4 timers connect 1 + neighbor 10.0.0.4 route-reflector-client + neighbor 10.0.0.4 update-source lo + neighbor 10.0.0.5 remote-as internal + neighbor 10.0.0.5 timers 1 3 + neighbor 10.0.0.5 timers connect 1 + neighbor 10.0.0.5 route-reflector-client + neighbor 10.0.0.5 update-source lo + neighbor 192.168.67.7 remote-as internal + neighbor 192.168.67.7 timers 1 3 + neighbor 192.168.67.7 timers connect 1 + address-family ipv4 + redistribute ospf + exit-address-family +! diff --git a/tests/topotests/bgp_aigp/r6/ospfd.conf b/tests/topotests/bgp_aigp/r6/ospfd.conf new file mode 100644 index 0000000000..46b2933178 --- /dev/null +++ b/tests/topotests/bgp_aigp/r6/ospfd.conf @@ -0,0 +1,17 @@ +! +interface lo + ip ospf cost 10 +! +interface r6-eth0 + ip ospf dead-interval 4 + ip ospf hello-interval 1 + ip ospf cost 20 +! +interface r6-eth1 + ip ospf dead-interval 4 + ip ospf hello-interval 1 + ip ospf cost 10 +! +router ospf + router-id 10.0.0.6 + network 0.0.0.0/0 area 0 diff --git a/tests/topotests/bgp_aigp/r6/zebra.conf b/tests/topotests/bgp_aigp/r6/zebra.conf new file mode 100644 index 0000000000..f8ca5f8b82 --- /dev/null +++ b/tests/topotests/bgp_aigp/r6/zebra.conf @@ -0,0 +1,13 @@ +! +interface lo + ip address 10.0.0.6/32 +! +interface r6-eth0 + ip address 192.168.46.6/24 +! +interface r6-eth1 + ip address 192.168.56.6/24 +! +interface r6-eth2 + ip address 192.168.67.6/24 +! diff --git a/tests/topotests/bgp_aigp/r7/bgpd.conf b/tests/topotests/bgp_aigp/r7/bgpd.conf new file mode 100644 index 0000000000..00d85cfadd --- /dev/null +++ b/tests/topotests/bgp_aigp/r7/bgpd.conf @@ -0,0 +1,22 @@ +router bgp 65002 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.67.6 remote-as internal + neighbor 192.168.67.6 timers 1 3 + neighbor 192.168.67.6 timers connect 1 + address-family ipv4 + redistribute connected route-map rmap metric 71 + 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 rmap permit 10 + match ip address prefix-list p71 + set aigp igp-metric +! +route-map rmap permit 20 + match ip address prefix-list p72 + set aigp 72 +exit +! diff --git a/tests/topotests/bgp_aigp/r7/zebra.conf b/tests/topotests/bgp_aigp/r7/zebra.conf new file mode 100644 index 0000000000..4c05df84ff --- /dev/null +++ b/tests/topotests/bgp_aigp/r7/zebra.conf @@ -0,0 +1,9 @@ +! +interface lo + ip address 10.0.0.7/32 + ip address 10.0.0.71/32 + ip address 10.0.0.72/32 +! +interface r7-eth0 + ip address 192.168.67.7/24 +! diff --git a/tests/topotests/bgp_aigp/test_bgp_aigp.py b/tests/topotests/bgp_aigp/test_bgp_aigp.py new file mode 100644 index 0000000000..9fa80c6da3 --- /dev/null +++ b/tests/topotests/bgp_aigp/test_bgp_aigp.py @@ -0,0 +1,224 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2022 by +# Donatas Abraitis +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +r7 sets aigp-metric for 10.0.0.71/32 to 71, and 72 for 10.0.0.72/32. + +r6 receives those routes with aigp-metric TLV. + +r2 and r3 receives those routes with aigp-metric TLV increased by 20, +and 30 appropriately. + +r1 receives routes with aigp-metric TLV 91,101 and 92,102 appropriately. +""" + +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 +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 8): + 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["r2"]) + switch.add_link(tgen.gears["r4"]) + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r5"]) + + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["r4"]) + switch.add_link(tgen.gears["r6"]) + + switch = tgen.add_switch("s6") + switch.add_link(tgen.gears["r5"]) + switch.add_link(tgen.gears["r6"]) + + switch = tgen.add_switch("s7") + switch.add_link(tgen.gears["r6"]) + switch.add_link(tgen.gears["r7"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (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(): + 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"] + r5 = tgen.gears["r5"] + + def _bgp_converge(): + output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast 10.0.0.71/32 json")) + expected = { + "paths": [ + { + "aigpMetric": 101, + "valid": True, + "bestpath": {"selectionReason": "Router ID"}, + "nexthops": [{"hostname": "r2", "accessible": True}], + }, + { + "aigpMetric": 91, + "valid": True, + "nexthops": [{"hostname": "r3", "accessible": True}], + }, + ] + } + return topotest.json_cmp(output, expected) + + 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_metric_bestpath(): + output = json.loads( + r1.vtysh_cmd( + "show bgp ipv4 unicast 10.0.0.64/28 longer-prefixes json detail" + ) + ) + expected = { + "routes": { + "10.0.0.71/32": [ + { + "aigpMetric": 101, + "valid": True, + }, + { + "aigpMetric": 91, + "valid": True, + "bestpath": {"selectionReason": "AIGP"}, + "nexthops": [{"hostname": "r3", "accessible": True}], + }, + ], + "10.0.0.72/32": [ + { + "aigpMetric": 102, + "valid": True, + }, + { + "aigpMetric": 92, + "valid": True, + "bestpath": {"selectionReason": "AIGP"}, + "nexthops": [{"hostname": "r3", "accessible": True}], + }, + ], + } + } + return topotest.json_cmp(output, expected) + + # Initial converge, AIGP is not involved in best-path selection process + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "can't converge initially" + + # Enable `bgp bestpath aigp` + r1.vtysh_cmd( + """ + configure terminal + router bgp + bgp bestpath aigp + """ + ) + + # 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=0.5) + assert result is None, "aigp-metric for 10.0.0.71/32 is not 71" + + # r5, 10.0.0.72/32 with aigp-metric 72 + test_func = functools.partial(_bgp_check_aigp_metric, r5, "10.0.0.72/32", 72) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + 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) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "aigp-metric for 10.0.0.71/32 is not 101" + + # 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) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "aigp-metric for 10.0.0.72/32 is not 92" + + # r1, check if AIGP is considered in best-path selection (lowest wins) + test_func = functools.partial(_bgp_check_aigp_metric_bestpath) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "AIGP attribute is not considered in best-path selection" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/yang/frr-bgp-route-map.yang b/yang/frr-bgp-route-map.yang index 3f3d829211..ad2a142fef 100644 --- a/yang/frr-bgp-route-map.yang +++ b/yang/frr-bgp-route-map.yang @@ -276,6 +276,12 @@ module frr-bgp-route-map { "Set BGP atomic-aggregate attribute"; } + identity aigp-metric { + base frr-route-map:rmap-set-type; + description + "Set BGP AIGP attribute (AIGP TLV Metric)"; + } + identity as-path-prepend { base frr-route-map:rmap-set-type; description @@ -800,6 +806,15 @@ module frr-bgp-route-map { } } + case aigp-metric { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'frr-bgp-route-map:aigp-metric')"; + leaf aigp-metric { + type string; + description + "Set BGP AIGP attribute (AIGP Metric TLV)"; + } + } + case as-path-prepend { when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'frr-bgp-route-map:as-path-prepend')"; choice as-path-prepend {