]> git.puffer.fish Git - mirror/frr.git/commitdiff
bgpd: Implement Accumulated IGP Metric Attribute for BGP 12114/head
authorDonatas Abraitis <donatas@opensourcerouting.org>
Wed, 12 Oct 2022 18:06:47 +0000 (21:06 +0300)
committerDonatas Abraitis <donatas@opensourcerouting.org>
Wed, 26 Oct 2022 08:26:57 +0000 (11:26 +0300)
https://www.rfc-editor.org/rfc/rfc7311.html

Signed-off-by: Donatas Abraitis <donatas@opensourcerouting.org>
43 files changed:
bgpd/bgp_attr.c
bgpd/bgp_attr.h
bgpd/bgp_bmp.c
bgpd/bgp_debug.c
bgpd/bgp_dump.c
bgpd/bgp_errors.c
bgpd/bgp_errors.h
bgpd/bgp_route.c
bgpd/bgp_routemap.c
bgpd/bgp_routemap_nb.c
bgpd/bgp_routemap_nb.h
bgpd/bgp_routemap_nb_config.c
bgpd/bgp_table.h
bgpd/bgp_updgrp.c
bgpd/bgp_updgrp_packet.c
bgpd/bgp_vty.c
bgpd/bgpd.c
bgpd/bgpd.h
lib/routemap.h
lib/routemap_cli.c
tests/topotests/bgp_aigp/__init__.py [new file with mode: 0644]
tests/topotests/bgp_aigp/r1/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_aigp/r1/ospfd.conf [new file with mode: 0644]
tests/topotests/bgp_aigp/r1/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_aigp/r2/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_aigp/r2/ospfd.conf [new file with mode: 0644]
tests/topotests/bgp_aigp/r2/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_aigp/r3/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_aigp/r3/ospfd.conf [new file with mode: 0644]
tests/topotests/bgp_aigp/r3/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_aigp/r4/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_aigp/r4/ospfd.conf [new file with mode: 0644]
tests/topotests/bgp_aigp/r4/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_aigp/r5/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_aigp/r5/ospfd.conf [new file with mode: 0644]
tests/topotests/bgp_aigp/r5/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_aigp/r6/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_aigp/r6/ospfd.conf [new file with mode: 0644]
tests/topotests/bgp_aigp/r6/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_aigp/r7/bgpd.conf [new file with mode: 0644]
tests/topotests/bgp_aigp/r7/zebra.conf [new file with mode: 0644]
tests/topotests/bgp_aigp/test_bgp_aigp.py [new file with mode: 0644]
yang/frr-bgp-route-map.yang

index b7d0958bac02b3e10140061ef95a3618f67f74d9..e9050c5aecd5facf52465b50331e63f9fb66c06c 100644 (file)
@@ -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);
index 41ae6ef49c8bc09091474f46ce4b46f0d6022faa..bc82d0c6ed6976b19aa716a1eb925d8ab1c585e7 100644 (file)
@@ -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;
index bcab4099c049442ab8d338ed423e8da9f2d41da9..2e5c772679b3b9b64b4d3379061a76b5144f80af 100644 (file)
@@ -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? */
 
index 580c18b58de0b6cf5e13c21bc9de56e6442d9e9a..a629695a3b7c0bf0ef02118aacdc2d7d346539b0 100644 (file)
@@ -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);
index 11e84f00b4d04c36954f6f52594d5618d90b97fd..254996edadd63e1ce424009026965a73222f8fd0 100644 (file)
@@ -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
index 9f87f8ce7a6fcf382e9621c628f421269978e250..8bc2dbca7a2273edf11f8868d0ff926c4d56f7be 100644 (file)
@@ -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",
index 0c0917c49e9606e7827923cf3ff9a92fff240115..4986c314a182498a652c90f57cd569eaae73a363 100644 (file)
@@ -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,
index 38332bd6fd7c4cd9b3ea3bd9310c3989b7d45ffe..d71e2f79a3b55676dab3f11f830d7bbca2a7ca63 100644 (file)
@@ -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);
index 233fd55ef0c134496ddcf67e2a52aa9daf9d0b72..637f462e8df4e7c8267760606097b6bc30445b86 100644 (file)
@@ -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 <igp-metric|(1-4294967295)>$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 [<igp-metric|(1-4294967295)>]",
+           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);
index c47c37dc667bc3fc87df1c4cfcb91a10d42e061d..35e470349275dff7ae122e253bb36cca759e79b8 100644 (file)
@@ -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 = {
index 163e3b55cc0de19a1e32ffd39407a8217afcace8..93320441585954a2eeaf6caa879f497d3c541a3a 100644 (file)
@@ -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);
index b18cf9d4ddfe00537728062dd32b54326c683084..216bcc0b07de15d917a2cc8bc7b1d31b75ce5701 100644 (file)
@@ -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
index 91d92ebd6d4acfffad7cb51e0f0f89208883debd..121afc481fe5cbd4272f24892a87a99b643d0ba7 100644 (file)
@@ -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,
index 0219535b0d57b567959148d08b27ff5e691c6399..9d550fd19b826e555d3eaed8dcfcd1637e923ae1 100644 (file)
@@ -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]);
 
index 88a81f255de86d0afbf982f1f606a8ebc6e9a7cb..dd073c9c490a13603949e4aeb29dfe097f6f7c22 100644 (file)
@@ -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);
index ec0fcd8f11985709fa57412e3f8c8a34de3bd8be..d01d508d6ecfdee237ee6f932be3053ae14d50da 100644 (file)
@@ -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 <A.B.C.D|X:X::X:X|WORD>$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);
index 005d6b309217784f01d619ce1c45ac9f3afb8a12..5199edb97e3402b03099b5886c69c1be172a8eba 100644 (file)
@@ -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[] = {
index c844f1530f5c9ffa8786d63823feb3b8b8d1ba6e..b400e5210c4bd1064b29c33689ddbe6a82d24ca6 100644 (file)
@@ -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,
index c2e9de6cfbb2bf106b07bfdcb5f8c7d461e8d027..9c78e1573577e562adc697f9d48c03afe2f9d93f 100644 (file)
@@ -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)                                                \
index 6be5d15ec4f80aef36d935ca1567fc4b6057213d..e3da29bd71950444434b2c97575ea8c6654bf8e5 100644 (file)
@@ -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 (file)
index 0000000..e69de29
diff --git a/tests/topotests/bgp_aigp/r1/bgpd.conf b/tests/topotests/bgp_aigp/r1/bgpd.conf
new file mode 100644 (file)
index 0000000..74a0215
--- /dev/null
@@ -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 (file)
index 0000000..38aa11d
--- /dev/null
@@ -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 (file)
index 0000000..0ed22d3
--- /dev/null
@@ -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 (file)
index 0000000..ae72f21
--- /dev/null
@@ -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 (file)
index 0000000..ed31941
--- /dev/null
@@ -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 (file)
index 0000000..6d6a557
--- /dev/null
@@ -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 (file)
index 0000000..7572e26
--- /dev/null
@@ -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 (file)
index 0000000..f971ad6
--- /dev/null
@@ -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 (file)
index 0000000..82c87d5
--- /dev/null
@@ -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 (file)
index 0000000..d2b96b7
--- /dev/null
@@ -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 (file)
index 0000000..c9e6796
--- /dev/null
@@ -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 (file)
index 0000000..5f544e8
--- /dev/null
@@ -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 (file)
index 0000000..9448722
--- /dev/null
@@ -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 (file)
index 0000000..b853c74
--- /dev/null
@@ -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 (file)
index 0000000..69b8bf2
--- /dev/null
@@ -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 (file)
index 0000000..15d9437
--- /dev/null
@@ -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 (file)
index 0000000..46b2933
--- /dev/null
@@ -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 (file)
index 0000000..f8ca5f8
--- /dev/null
@@ -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 (file)
index 0000000..00d85cf
--- /dev/null
@@ -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 (file)
index 0000000..4c05df8
--- /dev/null
@@ -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 (file)
index 0000000..9fa80c6
--- /dev/null
@@ -0,0 +1,224 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+# 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))
index 3f3d8292117c5ae00bdc513f9b6023b93291e783..ad2a142fef6d180310a924304f13c5a8b0c946b2 100644 (file)
@@ -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 {