]> git.proxmox.com Git - mirror_frr.git/blobdiff - bgpd/bgp_attr.c
Merge pull request #12432 from mjstapp/use_real_mtypes
[mirror_frr.git] / bgpd / bgp_attr.c
index 2f246e61d85cdd9be028dbe07b0aee253764eead..ace7e7975331c28da2e97a608fee4c3a237fa27d 100644 (file)
@@ -76,12 +76,14 @@ static const struct message attr_str[] = {
        {BGP_ATTR_AS_PATHLIMIT, "AS_PATHLIMIT"},
        {BGP_ATTR_PMSI_TUNNEL, "PMSI_TUNNEL_ATTRIBUTE"},
        {BGP_ATTR_ENCAP, "ENCAP"},
+       {BGP_ATTR_OTC, "OTC"},
 #ifdef ENABLE_BGP_VNC_ATTR
        {BGP_ATTR_VNC, "VNC"},
 #endif
        {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[] = {
@@ -447,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;
@@ -700,6 +806,8 @@ unsigned int attrhash_key_make(const void *p)
        MIX(attr->rmap_table_id);
        MIX(attr->nh_type);
        MIX(attr->bh_type);
+       MIX(attr->otc);
+       MIX(bgp_attr_get_aigp_metric(attr));
 
        return key;
 }
@@ -734,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)
@@ -762,7 +872,8 @@ bool attrhash_cmp(const void *p1, const void *p2)
                    && srv6_vpn_same(attr1->srv6_vpn, attr2->srv6_vpn)
                    && attr1->srte_color == attr2->srte_color
                    && attr1->nh_type == attr2->nh_type
-                   && attr1->bh_type == attr2->bh_type)
+                   && attr1->bh_type == attr2->bh_type
+                   && attr1->otc == attr2->otc)
                        return true;
        }
 
@@ -793,20 +904,16 @@ static void attrhash_finish(void)
 static void attr_show_all_iterator(struct hash_bucket *bucket, struct vty *vty)
 {
        struct attr *attr = bucket->data;
-       char sid_str[BUFSIZ];
 
        vty_out(vty, "attr[%ld] nexthop %pI4\n", attr->refcnt, &attr->nexthop);
 
-       sid_str[0] = '\0';
-       if (attr->srv6_l3vpn)
-               inet_ntop(AF_INET6, &attr->srv6_l3vpn->sid, sid_str, BUFSIZ);
-       else if (attr->srv6_vpn)
-               inet_ntop(AF_INET6, &attr->srv6_vpn->sid, sid_str, BUFSIZ);
-
        vty_out(vty,
-               "\tflags: %" PRIu64" distance: %u med: %u local_pref: %u origin: %u weight: %u label: %u sid: %s\n",
+               "\tflags: %" PRIu64
+               " distance: %u med: %u local_pref: %u origin: %u weight: %u label: %u sid: %pI6\n",
                attr->flag, attr->distance, attr->med, attr->local_pref,
-               attr->origin, attr->weight, attr->label, sid_str);
+               attr->origin, attr->weight, attr->label,
+               attr->srv6_l3vpn ? &attr->srv6_l3vpn->sid
+                                : &attr->srv6_vpn->sid);
 }
 
 void attr_show_all(struct vty *vty)
@@ -847,7 +954,7 @@ struct attr *bgp_attr_intern(struct attr *attr)
        struct lcommunity *lcomm = NULL;
        struct community *comm = NULL;
 
-       /* Intern referenced strucutre. */
+       /* Intern referenced structure. */
        if (attr->aspath) {
                if (!attr->aspath->refcnt)
                        attr->aspath = aspath_intern(attr->aspath);
@@ -951,7 +1058,8 @@ struct attr *bgp_attr_intern(struct attr *attr)
 }
 
 /* Make network statement's attribute. */
-struct attr *bgp_attr_default_set(struct attr *attr, uint8_t origin)
+struct attr *bgp_attr_default_set(struct attr *attr, struct bgp *bgp,
+                                 uint8_t origin)
 {
        memset(attr, 0, sizeof(struct attr));
 
@@ -965,6 +1073,7 @@ struct attr *bgp_attr_default_set(struct attr *attr, uint8_t origin)
        attr->label = MPLS_INVALID_LABEL;
        attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP);
        attr->mp_nexthop_len = IPV6_MAX_BYTELEN;
+       attr->local_pref = bgp->default_local_pref;
 
        return attr;
 }
@@ -978,14 +1087,18 @@ struct attr *bgp_attr_aggregate_intern(
 {
        struct attr attr;
        struct attr *new;
-       int ret;
+       route_map_result_t ret;
 
-       memset(&attr, 0, sizeof(struct attr));
+       memset(&attr, 0, sizeof(attr));
 
        /* Origin attribute. */
        attr.origin = origin;
        attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_ORIGIN);
 
+       /* MED */
+       attr.med = 0;
+       attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC);
+
        /* AS path attribute. */
        if (aspath)
                attr.aspath = aspath_intern(aspath);
@@ -1008,18 +1121,13 @@ struct attr *bgp_attr_aggregate_intern(
                }
 
                bgp_attr_set_community(&attr, community);
-               attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES);
        }
 
-       if (ecommunity) {
+       if (ecommunity)
                bgp_attr_set_ecommunity(&attr, ecommunity);
-               attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES);
-       }
 
-       if (lcommunity) {
+       if (lcommunity)
                bgp_attr_set_lcommunity(&attr, lcommunity);
-               attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES);
-       }
 
        if (bgp_in_graceful_shutdown(bgp))
                bgp_attr_add_gshut_community(&attr);
@@ -1036,15 +1144,13 @@ struct attr *bgp_attr_aggregate_intern(
        else
                attr.aggregator_as = bgp->as;
        attr.aggregator_addr = bgp->router_id;
-       attr.label_index = BGP_INVALID_LABEL_INDEX;
-       attr.label = MPLS_INVALID_LABEL;
 
        /* Apply route-map */
        if (aggregate->rmap.name) {
                struct attr attr_tmp = attr;
                struct bgp_path_info rmap_path;
 
-               memset(&rmap_path, 0, sizeof(struct bgp_path_info));
+               memset(&rmap_path, 0, sizeof(rmap_path));
                rmap_path.peer = bgp->peer_self;
                rmap_path.attr = &attr_tmp;
 
@@ -1096,22 +1202,18 @@ void bgp_attr_unintern_sub(struct attr *attr)
 
        comm = bgp_attr_get_community(attr);
        community_unintern(&comm);
-       UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES));
        bgp_attr_set_community(attr, NULL);
 
        ecomm = bgp_attr_get_ecommunity(attr);
        ecommunity_unintern(&ecomm);
-       UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES));
        bgp_attr_set_ecommunity(attr, NULL);
 
        ipv6_ecomm = bgp_attr_get_ipv6_ecommunity(attr);
        ecommunity_unintern(&ipv6_ecomm);
-       UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_IPV6_EXT_COMMUNITIES));
        bgp_attr_set_ipv6_ecommunity(attr, NULL);
 
        lcomm = bgp_attr_get_lcommunity(attr);
        lcommunity_unintern(&lcomm);
-       UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES));
        bgp_attr_set_lcommunity(attr, NULL);
 
        cluster = bgp_attr_get_cluster(attr);
@@ -1243,7 +1345,7 @@ void bgp_attr_flush(struct attr *attr)
  * are partial/optional and hence where the error likely was not
  * introduced by the sending neighbour.
  */
-static bgp_attr_parse_ret_t
+static enum bgp_attr_parse_ret
 bgp_attr_malformed(struct bgp_attr_parser_args *args, uint8_t subcode,
                   bgp_size_t length)
 {
@@ -1303,6 +1405,7 @@ bgp_attr_malformed(struct bgp_attr_parser_args *args, uint8_t subcode,
        case BGP_ATTR_LARGE_COMMUNITIES:
        case BGP_ATTR_ORIGINATOR_ID:
        case BGP_ATTR_CLUSTER_LIST:
+       case BGP_ATTR_OTC:
                return BGP_ATTR_PARSE_WITHDRAW;
        case BGP_ATTR_MP_REACH_NLRI:
        case BGP_ATTR_MP_UNREACH_NLRI:
@@ -1384,9 +1487,11 @@ const uint8_t attr_flags_values[] = {
        [BGP_ATTR_PMSI_TUNNEL] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
        [BGP_ATTR_LARGE_COMMUNITIES] =
                BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
+       [BGP_ATTR_OTC] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
        [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;
 
@@ -1450,7 +1555,8 @@ static bool bgp_attr_flag_invalid(struct bgp_attr_parser_args *args)
 }
 
 /* Get origin attribute of the update message. */
-static bgp_attr_parse_ret_t bgp_attr_origin(struct bgp_attr_parser_args *args)
+static enum bgp_attr_parse_ret
+bgp_attr_origin(struct bgp_attr_parser_args *args)
 {
        struct peer *const peer = args->peer;
        struct attr *const attr = args->attr;
@@ -1514,14 +1620,27 @@ static int bgp_attr_aspath(struct bgp_attr_parser_args *args)
                                          0);
        }
 
+       /* Conformant BGP speakers SHOULD NOT send BGP
+        * UPDATE messages containing AS_SET or AS_CONFED_SET.  Upon receipt of
+        * such messages, conformant BGP speakers SHOULD use the "Treat-as-
+        * withdraw" error handling behavior as per [RFC7606].
+        */
+       if (peer->bgp->reject_as_sets && aspath_check_as_sets(attr->aspath)) {
+               flog_err(EC_BGP_ATTR_MAL_AS_PATH,
+                        "AS_SET and AS_CONFED_SET are deprecated from %pBP",
+                        peer);
+               return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_MAL_AS_PATH,
+                                         0);
+       }
+
        /* Set aspath attribute flag. */
        attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AS_PATH);
 
        return BGP_ATTR_PARSE_PROCEED;
 }
 
-static bgp_attr_parse_ret_t bgp_attr_aspath_check(struct peer *const peer,
-                                                 struct attr *const attr)
+static enum bgp_attr_parse_ret bgp_attr_aspath_check(struct peer *const peer,
+                                                    struct attr *const attr)
 {
        /* These checks were part of bgp_attr_aspath, but with
         * as4 we should to check aspath things when
@@ -1594,6 +1713,19 @@ static int bgp_attr_as4_path(struct bgp_attr_parser_args *args,
                                          0);
        }
 
+       /* Conformant BGP speakers SHOULD NOT send BGP
+        * UPDATE messages containing AS_SET or AS_CONFED_SET.  Upon receipt of
+        * such messages, conformant BGP speakers SHOULD use the "Treat-as-
+        * withdraw" error handling behavior as per [RFC7606].
+        */
+       if (peer->bgp->reject_as_sets && aspath_check_as_sets(attr->aspath)) {
+               flog_err(EC_BGP_ATTR_MAL_AS_PATH,
+                        "AS_SET and AS_CONFED_SET are deprecated from %pBP",
+                        peer);
+               return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_MAL_AS_PATH,
+                                         0);
+       }
+
        /* Set aspath attribute flag. */
        attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AS4_PATH);
 
@@ -1603,22 +1735,16 @@ static int bgp_attr_as4_path(struct bgp_attr_parser_args *args,
 /*
  * Check that the nexthop attribute is valid.
  */
-bgp_attr_parse_ret_t
-bgp_attr_nexthop_valid(struct peer *peer, struct attr *attr)
+enum bgp_attr_parse_ret bgp_attr_nexthop_valid(struct peer *peer,
+                                              struct attr *attr)
 {
-       in_addr_t nexthop_h;
+       struct bgp *bgp = peer->bgp;
 
-       nexthop_h = ntohl(attr->nexthop.s_addr);
-       if ((IPV4_NET0(nexthop_h) || IPV4_NET127(nexthop_h)
-            || IPV4_CLASS_DE(nexthop_h))
-           && !BGP_DEBUG(allow_martians, ALLOW_MARTIANS)) {
+       if (ipv4_martian(&attr->nexthop) && !bgp->allow_martian) {
                uint8_t data[7]; /* type(2) + length(1) + nhop(4) */
-               char buf[INET_ADDRSTRLEN];
 
-               inet_ntop(AF_INET, &attr->nexthop.s_addr, buf,
-                         INET_ADDRSTRLEN);
-               flog_err(EC_BGP_ATTR_MARTIAN_NH, "Martian nexthop %s",
-                        buf);
+               flog_err(EC_BGP_ATTR_MARTIAN_NH, "Martian nexthop %pI4",
+                        &attr->nexthop);
                data[0] = BGP_ATTR_FLAG_TRANS;
                data[1] = BGP_ATTR_NEXT_HOP;
                data[2] = BGP_ATTR_NHLEN_IPV4;
@@ -1633,7 +1759,8 @@ bgp_attr_nexthop_valid(struct peer *peer, struct attr *attr)
 }
 
 /* Nexthop attribute. */
-static bgp_attr_parse_ret_t bgp_attr_nexthop(struct bgp_attr_parser_args *args)
+static enum bgp_attr_parse_ret
+bgp_attr_nexthop(struct bgp_attr_parser_args *args)
 {
        struct peer *const peer = args->peer;
        struct attr *const attr = args->attr;
@@ -1655,7 +1782,7 @@ static bgp_attr_parse_ret_t bgp_attr_nexthop(struct bgp_attr_parser_args *args)
 }
 
 /* MED atrribute. */
-static bgp_attr_parse_ret_t bgp_attr_med(struct bgp_attr_parser_args *args)
+static enum bgp_attr_parse_ret bgp_attr_med(struct bgp_attr_parser_args *args)
 {
        struct peer *const peer = args->peer;
        struct attr *const attr = args->attr;
@@ -1678,7 +1805,7 @@ static bgp_attr_parse_ret_t bgp_attr_med(struct bgp_attr_parser_args *args)
 }
 
 /* Local preference attribute. */
-static bgp_attr_parse_ret_t
+static enum bgp_attr_parse_ret
 bgp_attr_local_pref(struct bgp_attr_parser_args *args)
 {
        struct peer *const peer = args->peer;
@@ -1790,7 +1917,7 @@ static int bgp_attr_aggregator(struct bgp_attr_parser_args *args)
 }
 
 /* New Aggregator attribute */
-static bgp_attr_parse_ret_t
+static enum bgp_attr_parse_ret
 bgp_attr_as4_aggregator(struct bgp_attr_parser_args *args,
                        as_t *as4_aggregator_as,
                        struct in_addr *as4_aggregator_addr)
@@ -1834,7 +1961,7 @@ bgp_attr_as4_aggregator(struct bgp_attr_parser_args *args,
 
 /* Munge Aggregator and New-Aggregator, AS_PATH and NEW_AS_PATH.
  */
-static bgp_attr_parse_ret_t
+static enum bgp_attr_parse_ret
 bgp_attr_munge_as4_attrs(struct peer *const peer, struct attr *const attr,
                         struct aspath *as4_path, as_t as4_aggregator,
                         struct in_addr *as4_aggregator_addr)
@@ -1936,7 +2063,7 @@ bgp_attr_munge_as4_attrs(struct peer *const peer, struct attr *const attr,
 }
 
 /* Community attribute. */
-static bgp_attr_parse_ret_t
+static enum bgp_attr_parse_ret
 bgp_attr_community(struct bgp_attr_parser_args *args)
 {
        struct peer *const peer = args->peer;
@@ -1963,13 +2090,11 @@ bgp_attr_community(struct bgp_attr_parser_args *args)
                return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
                                          args->total);
 
-       attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES);
-
        return BGP_ATTR_PARSE_PROCEED;
 }
 
 /* Originator ID attribute. */
-static bgp_attr_parse_ret_t
+static enum bgp_attr_parse_ret
 bgp_attr_originator_id(struct bgp_attr_parser_args *args)
 {
        struct peer *const peer = args->peer;
@@ -1997,7 +2122,7 @@ bgp_attr_originator_id(struct bgp_attr_parser_args *args)
 }
 
 /* Cluster list attribute. */
-static bgp_attr_parse_ret_t
+static enum bgp_attr_parse_ret
 bgp_attr_cluster_list(struct bgp_attr_parser_args *args)
 {
        struct peer *const peer = args->peer;
@@ -2268,7 +2393,7 @@ int bgp_mp_unreach_parse(struct bgp_attr_parser_args *args,
 }
 
 /* Large Community attribute. */
-static bgp_attr_parse_ret_t
+static enum bgp_attr_parse_ret
 bgp_attr_large_community(struct bgp_attr_parser_args *args)
 {
        struct peer *const peer = args->peer;
@@ -2294,13 +2419,11 @@ bgp_attr_large_community(struct bgp_attr_parser_args *args)
                return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
                                          args->total);
 
-       attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES);
-
        return BGP_ATTR_PARSE_PROCEED;
 }
 
 /* Extended Community attribute. */
-static bgp_attr_parse_ret_t
+static enum bgp_attr_parse_ret
 bgp_attr_ext_communities(struct bgp_attr_parser_args *args)
 {
        struct peer *const peer = args->peer;
@@ -2332,8 +2455,6 @@ bgp_attr_ext_communities(struct bgp_attr_parser_args *args)
                return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
                                          args->total);
 
-       attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES);
-
        /* Extract DF election preference and  mobility sequence number */
        attr->df_pref = bgp_attr_df_pref_from_ec(attr, &attr->df_alg);
 
@@ -2376,7 +2497,7 @@ bgp_attr_ext_communities(struct bgp_attr_parser_args *args)
 }
 
 /* IPv6 Extended Community attribute. */
-static bgp_attr_parse_ret_t
+static enum bgp_attr_parse_ret
 bgp_attr_ipv6_ext_communities(struct bgp_attr_parser_args *args)
 {
        struct peer *const peer = args->peer;
@@ -2403,8 +2524,6 @@ bgp_attr_ipv6_ext_communities(struct bgp_attr_parser_args *args)
                return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
                                          args->total);
 
-       attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_IPV6_EXT_COMMUNITIES);
-
        return BGP_ATTR_PARSE_PROCEED;
 }
 
@@ -2541,7 +2660,7 @@ static int bgp_attr_encap(uint8_t type, struct peer *peer, /* IN */
 /* SRv6 Service Data Sub-Sub-TLV attribute
  * draft-ietf-bess-srv6-services-07
  */
-static bgp_attr_parse_ret_t
+static enum bgp_attr_parse_ret
 bgp_attr_srv6_service_data(struct bgp_attr_parser_args *args)
 {
        struct peer *const peer = args->peer;
@@ -2583,6 +2702,18 @@ bgp_attr_srv6_service_data(struct bgp_attr_parser_args *args)
        }
 
        if (type == BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_STRUCTURE) {
+               if (STREAM_READABLE(peer->curr) <
+                   BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_STRUCTURE_LENGTH) {
+                       flog_err(
+                               EC_BGP_ATTR_LEN,
+                               "Malformed SRv6 Service Data Sub-Sub-TLV attribute - insufficient data (need %u, have %zu remaining in UPDATE)",
+                               BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_STRUCTURE_LENGTH,
+                               STREAM_READABLE(peer->curr));
+                       return bgp_attr_malformed(
+                               args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+                               args->total);
+               }
+
                loc_block_len = stream_getc(peer->curr);
                loc_node_len = stream_getc(peer->curr);
                func_len = stream_getc(peer->curr);
@@ -2622,7 +2753,7 @@ bgp_attr_srv6_service_data(struct bgp_attr_parser_args *args)
 /* SRv6 Service Sub-TLV attribute
  * draft-ietf-bess-srv6-services-07
  */
-static bgp_attr_parse_ret_t
+static enum bgp_attr_parse_ret
 bgp_attr_srv6_service(struct bgp_attr_parser_args *args)
 {
        struct peer *const peer = args->peer;
@@ -2631,8 +2762,7 @@ bgp_attr_srv6_service(struct bgp_attr_parser_args *args)
        uint8_t type, sid_flags;
        uint16_t length, endpoint_behavior;
        size_t headersz = sizeof(type) + sizeof(length);
-       bgp_attr_parse_ret_t err;
-       char buf[BUFSIZ];
+       enum bgp_attr_parse_ret err;
 
        if (STREAM_READABLE(peer->curr) < headersz) {
                flog_err(
@@ -2656,6 +2786,17 @@ bgp_attr_srv6_service(struct bgp_attr_parser_args *args)
        }
 
        if (type == BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_INFO) {
+               if (STREAM_READABLE(peer->curr) <
+                   BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_INFO_LENGTH) {
+                       flog_err(
+                               EC_BGP_ATTR_LEN,
+                               "Malformed SRv6 Service Sub-TLV attribute - insufficent data (need %d for attribute data, have %zu remaining in UPDATE)",
+                               BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_INFO_LENGTH,
+                               STREAM_READABLE(peer->curr));
+                       return bgp_attr_malformed(
+                               args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+                               args->total);
+               }
                stream_getc(peer->curr);
                stream_get(&ipv6_sid, peer->curr, sizeof(ipv6_sid));
                sid_flags = stream_getc(peer->curr);
@@ -2663,12 +2804,11 @@ bgp_attr_srv6_service(struct bgp_attr_parser_args *args)
                stream_getc(peer->curr);
 
                /* Log SRv6 Service Sub-TLV */
-               if (BGP_DEBUG(vpn, VPN_LEAK_LABEL)) {
-                       inet_ntop(AF_INET6, &ipv6_sid, buf, sizeof(buf));
+               if (BGP_DEBUG(vpn, VPN_LEAK_LABEL))
                        zlog_debug(
-                               "%s: srv6-l3-srv sid %s, sid-flags 0x%02x, end-behaviour 0x%04x",
-                               __func__, buf, sid_flags, endpoint_behavior);
-               }
+                               "%s: srv6-l3-srv sid %pI6, sid-flags 0x%02x, end-behaviour 0x%04x",
+                               __func__, &ipv6_sid, sid_flags,
+                               endpoint_behavior);
 
                /* Configure from Info */
                if (attr->srv6_l3vpn) {
@@ -2717,8 +2857,9 @@ bgp_attr_srv6_service(struct bgp_attr_parser_args *args)
  * Read an individual SID value returning how much data we have read
  * Returns 0 if there was an error that needs to be passed up the stack
  */
-static bgp_attr_parse_ret_t bgp_attr_psid_sub(uint8_t type, uint16_t length,
-                                             struct bgp_attr_parser_args *args)
+static enum bgp_attr_parse_ret
+bgp_attr_psid_sub(uint8_t type, uint16_t length,
+                 struct bgp_attr_parser_args *args)
 {
        struct peer *const peer = args->peer;
        struct attr *const attr = args->attr;
@@ -2728,7 +2869,6 @@ static bgp_attr_parse_ret_t bgp_attr_psid_sub(uint8_t type, uint16_t length,
        uint32_t srgb_range;
        int srgb_count;
        uint8_t sid_type, sid_flags;
-       char buf[BUFSIZ];
 
        if (type == BGP_PREFIX_SID_LABEL_INDEX) {
                if (STREAM_READABLE(peer->curr) < length
@@ -2859,12 +2999,10 @@ static bgp_attr_parse_ret_t bgp_attr_psid_sub(uint8_t type, uint16_t length,
                           sizeof(ipv6_sid)); /* sid_value */
 
                /* Log VPN-SID Sub-TLV */
-               if (BGP_DEBUG(vpn, VPN_LEAK_LABEL)) {
-                       inet_ntop(AF_INET6, &ipv6_sid, buf, sizeof(buf));
+               if (BGP_DEBUG(vpn, VPN_LEAK_LABEL))
                        zlog_debug(
-                               "%s: vpn-sid: sid %s, sid-type 0x%02x sid-flags 0x%02x",
-                               __func__, buf, sid_type, sid_flags);
-               }
+                               "%s: vpn-sid: sid %pI6, sid-type 0x%02x sid-flags 0x%02x",
+                               __func__, &ipv6_sid, sid_type, sid_flags);
 
                /* Configure from Info */
                if (attr->srv6_vpn) {
@@ -2925,11 +3063,11 @@ static bgp_attr_parse_ret_t bgp_attr_psid_sub(uint8_t type, uint16_t length,
 /* Prefix SID attribute
  * draft-ietf-idr-bgp-prefix-sid-05
  */
-bgp_attr_parse_ret_t bgp_attr_prefix_sid(struct bgp_attr_parser_args *args)
+enum bgp_attr_parse_ret bgp_attr_prefix_sid(struct bgp_attr_parser_args *args)
 {
        struct peer *const peer = args->peer;
        struct attr *const attr = args->attr;
-       bgp_attr_parse_ret_t ret;
+       enum bgp_attr_parse_ret ret;
 
        attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID);
 
@@ -2988,7 +3126,7 @@ bgp_attr_parse_ret_t bgp_attr_prefix_sid(struct bgp_attr_parser_args *args)
 /* PMSI tunnel attribute (RFC 6514)
  * Basic validation checks done here.
  */
-static bgp_attr_parse_ret_t
+static enum bgp_attr_parse_ret
 bgp_attr_pmsi_tunnel(struct bgp_attr_parser_args *args)
 {
        struct peer *const peer = args->peer;
@@ -3035,8 +3173,75 @@ 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)
+{
+       struct peer *const peer = args->peer;
+       struct attr *const attr = args->attr;
+       const bgp_size_t length = args->length;
+
+       /* Length check. */
+       if (length != 4) {
+               flog_err(EC_BGP_ATTR_LEN, "OTC attribute length isn't 4 [%u]",
+                        length);
+               return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+                                         args->total);
+       }
+
+       attr->otc = stream_getl(peer->curr);
+       if (!attr->otc) {
+               flog_err(EC_BGP_ATTR_MAL_AS_PATH, "OTC attribute value is 0");
+               return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_MAL_AS_PATH,
+                                         args->total);
+       }
+
+       attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_OTC);
+
+       return BGP_ATTR_PARSE_PROCEED;
+}
+
 /* BGP unknown attribute treatment. */
-static bgp_attr_parse_ret_t bgp_attr_unknown(struct bgp_attr_parser_args *args)
+static enum bgp_attr_parse_ret
+bgp_attr_unknown(struct bgp_attr_parser_args *args)
 {
        bgp_size_t total = args->total;
        struct transit *transit;
@@ -3141,11 +3346,12 @@ static int bgp_attr_check(struct peer *peer, struct attr *attr)
 
 /* Read attribute of update packet.  This function is called from
    bgp_update_receive() in bgp_packet.c.  */
-bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr,
-                                   bgp_size_t size, struct bgp_nlri *mp_update,
-                                   struct bgp_nlri *mp_withdraw)
+enum bgp_attr_parse_ret bgp_attr_parse(struct peer *peer, struct attr *attr,
+                                      bgp_size_t size,
+                                      struct bgp_nlri *mp_update,
+                                      struct bgp_nlri *mp_withdraw)
 {
-       bgp_attr_parse_ret_t ret;
+       enum bgp_attr_parse_ret ret;
        uint8_t flag = 0;
        uint8_t type = 0;
        bgp_size_t length;
@@ -3381,6 +3587,12 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr,
                case BGP_ATTR_IPV6_EXT_COMMUNITIES:
                        ret = bgp_attr_ipv6_ext_communities(&attr_args);
                        break;
+               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;
@@ -3604,8 +3816,8 @@ size_t bgp_packet_mpattr_start(struct stream *s, struct peer *peer, afi_t afi,
                               struct attr *attr)
 {
        size_t sizep;
-       iana_afi_t pkt_afi;
-       iana_safi_t pkt_safi;
+       iana_afi_t pkt_afi = IANA_AFI_IPV4;
+       iana_safi_t pkt_safi = IANA_SAFI_UNICAST;
        afi_t nh_afi;
 
        /* Set extended bit always to encode the attribute length as 2 bytes */
@@ -3686,13 +3898,6 @@ size_t bgp_packet_mpattr_start(struct stream *s, struct peer *peer, afi_t afi,
                } break;
                case SAFI_MPLS_VPN: {
                        if (attr->mp_nexthop_len
-                           == BGP_ATTR_NHLEN_IPV6_GLOBAL) {
-                               stream_putc(s, 24);
-                               stream_putl(s, 0); /* RD = 0, per RFC */
-                               stream_putl(s, 0);
-                               stream_put(s, &attr->mp_nexthop_global,
-                                          IPV6_MAX_BYTELEN);
-                       } else if (attr->mp_nexthop_len
                                   == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) {
                                stream_putc(s, 48);
                                stream_putl(s, 0); /* RD = 0, per RFC */
@@ -3703,6 +3908,12 @@ size_t bgp_packet_mpattr_start(struct stream *s, struct peer *peer, afi_t afi,
                                stream_putl(s, 0);
                                stream_put(s, &attr->mp_nexthop_local,
                                           IPV6_MAX_BYTELEN);
+                       } else {
+                               stream_putc(s, 24);
+                               stream_putl(s, 0); /* RD = 0, per RFC */
+                               stream_putl(s, 0);
+                               stream_put(s, &attr->mp_nexthop_global,
+                                          IPV6_MAX_BYTELEN);
                        }
                } break;
                case SAFI_ENCAP:
@@ -3901,7 +4112,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;
@@ -4185,9 +4397,14 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer,
        if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY)
            && (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES))) {
                struct ecommunity *ecomm = bgp_attr_get_ecommunity(attr);
-
-               if (peer->sort == BGP_PEER_IBGP
-                   || peer->sort == BGP_PEER_CONFED) {
+               bool transparent = CHECK_FLAG(peer->af_flags[afi][safi],
+                                             PEER_FLAG_RSERVER_CLIENT) &&
+                                  from &&
+                                  CHECK_FLAG(from->af_flags[afi][safi],
+                                             PEER_FLAG_RSERVER_CLIENT);
+
+               if (peer->sort == BGP_PEER_IBGP ||
+                   peer->sort == BGP_PEER_CONFED || transparent) {
                        if (ecomm->size * 8 > 255) {
                                stream_putc(s,
                                            BGP_ATTR_FLAG_OPTIONAL
@@ -4300,7 +4517,9 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer,
                        stream_put(s, &attr->srv6_l3vpn->sid,
                                   sizeof(attr->srv6_l3vpn->sid)); /* sid */
                        stream_putc(s, 0);      /* sid_flags */
-                       stream_putw(s, 0xffff); /* endpoint */
+                       stream_putw(s,
+                                   attr->srv6_l3vpn
+                                           ->endpoint_behavior); /* endpoint */
                        stream_putc(s, 0);      /* reserved */
                        stream_putc(
                                s,
@@ -4394,6 +4613,30 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer,
                // Unicast tunnel endpoint IP address
        }
 
+       /* OTC */
+       if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_OTC)) {
+               stream_putc(s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS);
+               stream_putc(s, BGP_ATTR_OTC);
+               stream_putc(s, 4);
+               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);
 
@@ -4407,8 +4650,8 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer,
 size_t bgp_packet_mpunreach_start(struct stream *s, afi_t afi, safi_t safi)
 {
        unsigned long attrlen_pnt;
-       iana_afi_t pkt_afi;
-       iana_safi_t pkt_safi;
+       iana_afi_t pkt_afi = IANA_AFI_IPV4;
+       iana_safi_t pkt_safi = IANA_SAFI_UNICAST;
 
        /* Set extended bit always to encode the attribute length as 2 bytes */
        stream_putc(s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_EXTLEN);
@@ -4433,7 +4676,7 @@ void bgp_packet_mpunreach_prefix(struct stream *s, const struct prefix *p,
                                 bool addpath_capable, uint32_t addpath_tx_id,
                                 struct attr *attr)
 {
-       uint8_t wlabel[3] = {0x80, 0x00, 0x00};
+       uint8_t wlabel[4] = {0x80, 0x00, 0x00};
 
        if (safi == SAFI_LABELED_UNICAST) {
                label = (mpls_label_t *)wlabel;
@@ -4477,7 +4720,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;
@@ -4486,6 +4729,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);
@@ -4641,6 +4885,28 @@ void bgp_dump_routes_attr(struct stream *s, struct attr *attr,
                }
        }
 
+       /* OTC */
+       if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_OTC)) {
+               stream_putc(s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS);
+               stream_putc(s, BGP_ATTR_OTC);
+               stream_putc(s, 4);
+               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);