]> git.proxmox.com Git - mirror_frr.git/blobdiff - bgpd/bgp_attr.c
*: conform with COMMUNITY.md formatting rules, via 'make indent'
[mirror_frr.git] / bgpd / bgp_attr.c
index 83ffcc5558c9b0064bde1df758848c87e6f135bf..84b5de91fd16e4f836c3aeca5ce2efc3ab50bfe5 100644 (file)
@@ -74,6 +74,7 @@ static const struct message attr_str[] = {
        {BGP_ATTR_AS4_PATH, "AS4_PATH"},
        {BGP_ATTR_AS4_AGGREGATOR, "AS4_AGGREGATOR"},
        {BGP_ATTR_AS_PATHLIMIT, "AS_PATHLIMIT"},
+       {BGP_ATTR_PMSI_TUNNEL, "PMSI_TUNNEL_ATTRIBUTE"},
        {BGP_ATTR_ENCAP, "ENCAP"},
 #if ENABLE_BGP_VNC
        {BGP_ATTR_VNC, "VNC"},
@@ -82,15 +83,14 @@ static const struct message attr_str[] = {
        {BGP_ATTR_PREFIX_SID, "PREFIX_SID"},
        {0}};
 
-static const struct message attr_flag_str[] =
-       {
-               {BGP_ATTR_FLAG_OPTIONAL, "Optional"},
-               {BGP_ATTR_FLAG_TRANS, "Transitive"},
-               {BGP_ATTR_FLAG_PARTIAL, "Partial"},
-               /* bgp_attr_flags_diagnose() relies on this bit being last in
-                  this list */
-               {BGP_ATTR_FLAG_EXTLEN, "Extended Length"},
-               {0}};
+static const struct message attr_flag_str[] = {
+       {BGP_ATTR_FLAG_OPTIONAL, "Optional"},
+       {BGP_ATTR_FLAG_TRANS, "Transitive"},
+       {BGP_ATTR_FLAG_PARTIAL, "Partial"},
+       /* bgp_attr_flags_diagnose() relies on this bit being last in
+          this list */
+       {BGP_ATTR_FLAG_EXTLEN, "Extended Length"},
+       {0}};
 
 static struct hash *cluster_hash;
 
@@ -184,8 +184,7 @@ void cluster_unintern(struct cluster_list *cluster)
 
 static void cluster_init(void)
 {
-       cluster_hash = hash_create(cluster_hash_key_make,
-                                  cluster_hash_cmp,
+       cluster_hash = hash_create(cluster_hash_key_make, cluster_hash_cmp,
                                   "BGP Cluster");
 }
 
@@ -362,12 +361,10 @@ static int encap_hash_cmp(const void *p1, const void *p2)
 
 static void encap_init(void)
 {
-       encap_hash = hash_create(encap_hash_key_make,
-                                encap_hash_cmp,
+       encap_hash = hash_create(encap_hash_key_make, encap_hash_cmp,
                                 "BGP Encap Hash");
 #if ENABLE_BGP_VNC
-       vnc_hash = hash_create(encap_hash_key_make,
-                              encap_hash_cmp,
+       vnc_hash = hash_create(encap_hash_key_make, encap_hash_cmp,
                               "BGP VNC Hash");
 #endif
 }
@@ -453,8 +450,7 @@ static int transit_hash_cmp(const void *p1, const void *p2)
 
 static void transit_init(void)
 {
-       transit_hash = hash_create(transit_hash_key_make,
-                                  transit_hash_cmp,
+       transit_hash = hash_create(transit_hash_key_make, transit_hash_cmp,
                                   "BGP Transit Hash");
 }
 
@@ -492,19 +488,14 @@ unsigned int attrhash_key_make(void *p)
        const struct attr *attr = (struct attr *)p;
        uint32_t key = 0;
 #define MIX(val)       key = jhash_1word(val, key)
+#define MIX3(a, b, c)  key = jhash_3words((a), (b), (c), key)
 
-       MIX(attr->origin);
-       MIX(attr->nexthop.s_addr);
-       MIX(attr->med);
-       MIX(attr->local_pref);
-       MIX(attr->aggregator_as);
-       MIX(attr->aggregator_addr.s_addr);
-       MIX(attr->weight);
-       MIX(attr->mp_nexthop_global_in.s_addr);
-       MIX(attr->originator_id.s_addr);
-       MIX(attr->tag);
-       MIX(attr->label);
-       MIX(attr->label_index);
+       MIX3(attr->origin, attr->nexthop.s_addr, attr->med);
+       MIX3(attr->local_pref, attr->aggregator_as,
+            attr->aggregator_addr.s_addr);
+       MIX3(attr->weight, attr->mp_nexthop_global_in.s_addr,
+            attr->originator_id.s_addr);
+       MIX3(attr->tag, attr->label, attr->label_index);
 
        if (attr->aspath)
                MIX(aspath_key_make(attr->aspath));
@@ -550,12 +541,6 @@ int attrhash_cmp(const void *p1, const void *p2)
                    && attr1->tag == attr2->tag
                    && attr1->label_index == attr2->label_index
                    && attr1->mp_nexthop_len == attr2->mp_nexthop_len
-                   && IPV6_ADDR_SAME(&attr1->mp_nexthop_global,
-                                     &attr2->mp_nexthop_global)
-                   && IPV6_ADDR_SAME(&attr1->mp_nexthop_local,
-                                     &attr2->mp_nexthop_local)
-                   && IPV4_ADDR_SAME(&attr1->mp_nexthop_global_in,
-                                     &attr2->mp_nexthop_global_in)
                    && attr1->ecommunity == attr2->ecommunity
                    && attr1->lcommunity == attr2->lcommunity
                    && attr1->cluster == attr2->cluster
@@ -565,6 +550,12 @@ int attrhash_cmp(const void *p1, const void *p2)
 #if ENABLE_BGP_VNC
                    && encap_same(attr1->vnc_subtlvs, attr2->vnc_subtlvs)
 #endif
+                   && IPV6_ADDR_SAME(&attr1->mp_nexthop_global,
+                                     &attr2->mp_nexthop_global)
+                   && IPV6_ADDR_SAME(&attr1->mp_nexthop_local,
+                                     &attr2->mp_nexthop_local)
+                   && IPV4_ADDR_SAME(&attr1->mp_nexthop_global_in,
+                                     &attr2->mp_nexthop_global_in)
                    && IPV4_ADDR_SAME(&attr1->originator_id,
                                      &attr2->originator_id)
                    && overlay_index_same(attr1, attr2))
@@ -576,9 +567,8 @@ int attrhash_cmp(const void *p1, const void *p2)
 
 static void attrhash_init(void)
 {
-       attrhash = hash_create(attrhash_key_make,
-                              attrhash_cmp,
-                              "BGP Attributes");
+       attrhash =
+               hash_create(attrhash_key_make, attrhash_cmp, "BGP Attributes");
 }
 
 /*
@@ -752,8 +742,8 @@ struct attr *bgp_attr_aggregate_intern(struct bgp *bgp, u_char origin,
                /* If we are not shutting down ourselves and we are
                 * aggregating a route that contains the GSHUT community we
                 * need to remove that community when creating the aggregate */
-               if (!bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN) &&
-                   community_include(community, gshut)) {
+               if (!bgp_flag_check(bgp, BGP_FLAG_GRACEFUL_SHUTDOWN)
+                   && community_include(community, gshut)) {
                        community_del_val(community, &gshut);
                }
 
@@ -822,6 +812,31 @@ void bgp_attr_unintern_sub(struct attr *attr)
 #endif
 }
 
+/*
+ * We have some show commands that let you experimentally
+ * apply a route-map.  When we apply the route-map
+ * we are reseting values but not saving them for
+ * posterity via intern'ing( because route-maps don't
+ * do that) but at this point in time we need
+ * to compare the new attr to the old and if the
+ * routemap has changed it we need to, as Snoop Dog says,
+ * Drop it like it's hot
+ */
+void bgp_attr_undup(struct attr *new, struct attr *old)
+{
+       if (new->aspath != old->aspath)
+               aspath_free(new->aspath);
+
+       if (new->community != old->community)
+               community_free(new->community);
+
+       if (new->ecommunity != old->ecommunity)
+               ecommunity_free(&new->ecommunity);
+
+       if (new->lcommunity != old->lcommunity)
+               lcommunity_free(&new->lcommunity);
+}
+
 /* Free bgp attribute and aspath. */
 void bgp_attr_unintern(struct attr **pattr)
 {
@@ -1014,6 +1029,8 @@ const u_int8_t attr_flags_values[] = {
                        BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
                [BGP_ATTR_AS4_AGGREGATOR] =
                        BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
+               [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_PREFIX_SID] =
@@ -1130,7 +1147,7 @@ static int bgp_attr_aspath(struct bgp_attr_parser_args *args)
         * peer with AS4 => will get 4Byte ASnums
         * otherwise, will get 16 Bit
         */
-       attr->aspath = aspath_parse(peer->ibuf, length,
+       attr->aspath = aspath_parse(peer->curr, length,
                                    CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV));
 
        /* In case of IBGP, length will be zero. */
@@ -1204,7 +1221,7 @@ static int bgp_attr_as4_path(struct bgp_attr_parser_args *args,
        struct attr *const attr = args->attr;
        const bgp_size_t length = args->length;
 
-       *as4_path = aspath_parse(peer->ibuf, length, 1);
+       *as4_path = aspath_parse(peer->curr, length, 1);
 
        /* In case of IBGP, length will be zero. */
        if (!*as4_path) {
@@ -1245,7 +1262,7 @@ static bgp_attr_parse_ret_t bgp_attr_nexthop(struct bgp_attr_parser_args *args)
           logged locally (this is implemented somewhere else). The UPDATE
           message
           gets ignored in any of these cases. */
-       nexthop_n = stream_get_ipv4(peer->ibuf);
+       nexthop_n = stream_get_ipv4(peer->curr);
        nexthop_h = ntohl(nexthop_n);
        if ((IPV4_NET0(nexthop_h) || IPV4_NET127(nexthop_h)
             || IPV4_CLASS_DE(nexthop_h))
@@ -1281,7 +1298,7 @@ static bgp_attr_parse_ret_t bgp_attr_med(struct bgp_attr_parser_args *args)
                                          args->total);
        }
 
-       attr->med = stream_getl(peer->ibuf);
+       attr->med = stream_getl(peer->curr);
 
        attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC);
 
@@ -1307,11 +1324,11 @@ bgp_attr_local_pref(struct bgp_attr_parser_args *args)
           external peer, then this attribute MUST be ignored by the
           receiving speaker. */
        if (peer->sort == BGP_PEER_EBGP) {
-               stream_forward_getp(peer->ibuf, length);
+               stream_forward_getp(peer->curr, length);
                return BGP_ATTR_PARSE_PROCEED;
        }
 
-       attr->local_pref = stream_getl(peer->ibuf);
+       attr->local_pref = stream_getl(peer->curr);
 
        /* Set the local-pref flag. */
        attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF);
@@ -1360,10 +1377,10 @@ static int bgp_attr_aggregator(struct bgp_attr_parser_args *args)
        }
 
        if (CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV))
-               attr->aggregator_as = stream_getl(peer->ibuf);
+               attr->aggregator_as = stream_getl(peer->curr);
        else
-               attr->aggregator_as = stream_getw(peer->ibuf);
-       attr->aggregator_addr.s_addr = stream_get_ipv4(peer->ibuf);
+               attr->aggregator_as = stream_getw(peer->curr);
+       attr->aggregator_addr.s_addr = stream_get_ipv4(peer->curr);
 
        /* Set atomic aggregate flag. */
        attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AGGREGATOR);
@@ -1387,8 +1404,8 @@ bgp_attr_as4_aggregator(struct bgp_attr_parser_args *args,
                                          0);
        }
 
-       *as4_aggregator_as = stream_getl(peer->ibuf);
-       as4_aggregator_addr->s_addr = stream_get_ipv4(peer->ibuf);
+       *as4_aggregator_as = stream_getl(peer->curr);
+       as4_aggregator_addr->s_addr = stream_get_ipv4(peer->curr);
 
        attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AS4_AGGREGATOR);
 
@@ -1514,10 +1531,10 @@ bgp_attr_community(struct bgp_attr_parser_args *args)
        }
 
        attr->community =
-               community_parse((u_int32_t *)stream_pnt(peer->ibuf), length);
+               community_parse((u_int32_t *)stream_pnt(peer->curr), length);
 
        /* XXX: fix community_parse to use stream API and remove this */
-       stream_forward_getp(peer->ibuf, length);
+       stream_forward_getp(peer->curr, length);
 
        if (!attr->community)
                return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
@@ -1544,7 +1561,7 @@ bgp_attr_originator_id(struct bgp_attr_parser_args *args)
                                          args->total);
        }
 
-       attr->originator_id.s_addr = stream_get_ipv4(peer->ibuf);
+       attr->originator_id.s_addr = stream_get_ipv4(peer->curr);
 
        attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID);
 
@@ -1568,10 +1585,10 @@ bgp_attr_cluster_list(struct bgp_attr_parser_args *args)
        }
 
        attr->cluster =
-               cluster_parse((struct in_addr *)stream_pnt(peer->ibuf), length);
+               cluster_parse((struct in_addr *)stream_pnt(peer->curr), length);
 
        /* XXX: Fix cluster_parse to use stream API and then remove this */
-       stream_forward_getp(peer->ibuf, length);
+       stream_forward_getp(peer->curr, length);
 
        attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST);
 
@@ -1638,10 +1655,14 @@ int bgp_mp_reach_parse(struct bgp_attr_parser_args *args,
        case BGP_ATTR_NHLEN_VPNV4:
                stream_getl(s); /* RD high */
                stream_getl(s); /* RD low */
-               /*
-                * NOTE: intentional fall through
-                * - for consistency in rx processing
-                */
+                               /*
+                                * NOTE: intentional fall through
+                                * - for consistency in rx processing
+                                *
+                                * The following comment is to signal GCC this intention
+                                * and supress the warning
+                                */
+       /* FALLTHRU */
        case BGP_ATTR_NHLEN_IPV4:
                stream_get(&attr->mp_nexthop_global_in, s, IPV4_MAX_BYTELEN);
                /* Probably needed for RFC 2283 */
@@ -1711,11 +1732,20 @@ int bgp_mp_reach_parse(struct bgp_attr_parser_args *args,
 
        /* must have nrli_len, what is left of the attribute */
        nlri_len = LEN_LEFT;
-       if ((!nlri_len) || (nlri_len > STREAM_READABLE(s))) {
+       if (nlri_len > STREAM_READABLE(s)) {
                zlog_info("%s: (%s) Failed to read NLRI", __func__, peer->host);
                return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
        }
 
+       if (!nlri_len) {
+               zlog_info("%s: (%s) No Reachability, Treating as a EOR marker",
+                         __func__, peer->host);
+
+               mp_update->afi = afi;
+               mp_update->safi = safi;
+               return BGP_ATTR_PARSE_EOR;
+       }
+
        mp_update->afi = afi;
        mp_update->safi = safi;
        mp_update->nlri = stream_pnt(s);
@@ -1743,7 +1773,7 @@ int bgp_mp_unreach_parse(struct bgp_attr_parser_args *args,
        struct attr *const attr = args->attr;
        const bgp_size_t length = args->length;
 
-       s = peer->ibuf;
+       s = peer->curr;
 
 #define BGP_MP_UNREACH_MIN_SIZE 3
        if ((length > STREAM_READABLE(s)) || (length < BGP_MP_UNREACH_MIN_SIZE))
@@ -1797,9 +1827,9 @@ bgp_attr_large_community(struct bgp_attr_parser_args *args)
        }
 
        attr->lcommunity =
-               lcommunity_parse((u_int8_t *)stream_pnt(peer->ibuf), length);
+               lcommunity_parse((u_int8_t *)stream_pnt(peer->curr), length);
        /* XXX: fix ecommunity_parse to use stream API */
-       stream_forward_getp(peer->ibuf, length);
+       stream_forward_getp(peer->curr, length);
 
        if (!attr->lcommunity)
                return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
@@ -1826,9 +1856,9 @@ bgp_attr_ext_communities(struct bgp_attr_parser_args *args)
        }
 
        attr->ecommunity =
-               ecommunity_parse((u_int8_t *)stream_pnt(peer->ibuf), length);
+               ecommunity_parse((u_int8_t *)stream_pnt(peer->curr), length);
        /* XXX: fix ecommunity_parse to use stream API */
-       stream_forward_getp(peer->ibuf, length);
+       stream_forward_getp(peer->curr, length);
 
        if (!attr->ecommunity)
                return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
@@ -1840,6 +1870,12 @@ bgp_attr_ext_communities(struct bgp_attr_parser_args *args)
        attr->mm_seqnum = bgp_attr_mac_mobility_seqnum(attr, &sticky);
        attr->sticky = sticky;
 
+       /* Check if this is a Gateway MAC-IP advertisement */
+       attr->default_gw = bgp_attr_default_gw(attr);
+
+       /* Extract the Rmac, if any */
+       bgp_attr_rmac(attr, &attr->rmac);
+
        return BGP_ATTR_PARSE_PROCEED;
 }
 
@@ -1918,11 +1954,10 @@ static int bgp_attr_encap(uint8_t type, struct peer *peer, /* IN */
                /* alloc and copy sub-tlv */
                /* TBD make sure these are freed when attributes are released */
                tlv = XCALLOC(MTYPE_ENCAP_TLV,
-                             sizeof(struct bgp_attr_encap_subtlv)
-                                     + sublength);
+                             sizeof(struct bgp_attr_encap_subtlv) + sublength);
                tlv->type = subtype;
                tlv->length = sublength;
-               stream_get(tlv->value, peer->ibuf, sublength);
+               stream_get(tlv->value, peer->curr, sublength);
                length -= sublength;
 
                /* attach tlv to encap chain */
@@ -1990,8 +2025,8 @@ bgp_attr_prefix_sid(struct bgp_attr_parser_args *args,
 
        attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID);
 
-       type = stream_getc(peer->ibuf);
-       length = stream_getw(peer->ibuf);
+       type = stream_getc(peer->curr);
+       length = stream_getw(peer->curr);
 
        if (type == BGP_PREFIX_SID_LABEL_INDEX) {
                if (length != BGP_PREFIX_SID_LABEL_INDEX_LENGTH) {
@@ -2004,11 +2039,11 @@ bgp_attr_prefix_sid(struct bgp_attr_parser_args *args,
                }
 
                /* Ignore flags and reserved */
-               stream_getc(peer->ibuf);
-               stream_getw(peer->ibuf);
+               stream_getc(peer->curr);
+               stream_getw(peer->curr);
 
                /* Fetch the label index and see if it is valid. */
-               label_index = stream_getl(peer->ibuf);
+               label_index = stream_getl(peer->curr);
                if (label_index == BGP_INVALID_LABEL_INDEX)
                        return bgp_attr_malformed(
                                args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
@@ -2039,16 +2074,16 @@ bgp_attr_prefix_sid(struct bgp_attr_parser_args *args,
                }
 
                /* Ignore reserved */
-               stream_getc(peer->ibuf);
-               stream_getw(peer->ibuf);
+               stream_getc(peer->curr);
+               stream_getw(peer->curr);
 
-               stream_get(&ipv6_sid, peer->ibuf, 16);
+               stream_get(&ipv6_sid, peer->curr, 16);
        }
 
        /* Placeholder code for the Originator SRGB type */
        else if (type == BGP_PREFIX_SID_ORIGINATOR_SRGB) {
                /* Ignore flags */
-               stream_getw(peer->ibuf);
+               stream_getw(peer->curr);
 
                length -= 2;
 
@@ -2064,8 +2099,8 @@ bgp_attr_prefix_sid(struct bgp_attr_parser_args *args,
                srgb_count = length / BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH;
 
                for (int i = 0; i < srgb_count; i++) {
-                       stream_get(&srgb_base, peer->ibuf, 3);
-                       stream_get(&srgb_range, peer->ibuf, 3);
+                       stream_get(&srgb_base, peer->curr, 3);
+                       stream_get(&srgb_range, peer->curr, 3);
                }
        }
 
@@ -2090,7 +2125,7 @@ static bgp_attr_parse_ret_t bgp_attr_unknown(struct bgp_attr_parser_args *args)
                        peer->host, type, length);
 
        /* Forward read pointer of input stream. */
-       stream_forward_getp(peer->ibuf, length);
+       stream_forward_getp(peer->curr, length);
 
        /* If any of the mandatory well-known attributes are not recognized,
           then the Error Subcode is set to Unrecognized Well-known
@@ -2212,7 +2247,7 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr,
                                "%s: error BGP attribute length %lu is smaller than min len",
                                peer->host,
                                (unsigned long)(endp
-                                               - STREAM_PNT(BGP_INPUT(peer))));
+                                               - stream_pnt(BGP_INPUT(peer))));
 
                        bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR,
                                        BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
@@ -2234,7 +2269,7 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr,
                                "%s: Extended length set, but just %lu bytes of attr header",
                                peer->host,
                                (unsigned long)(endp
-                                               - STREAM_PNT(BGP_INPUT(peer))));
+                                               - stream_pnt(BGP_INPUT(peer))));
 
                        bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR,
                                        BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
@@ -2274,10 +2309,46 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr,
                                "%s: BGP type %d length %d is too large, attribute total length is %d.  attr_endp is %p.  endp is %p",
                                peer->host, type, length, size, attr_endp,
                                endp);
+                       /*
+                        * RFC 4271 6.3
+                        * If any recognized attribute has an Attribute
+                        * Length that conflicts with the expected length
+                        * (based on the attribute type code), then the
+                        * Error Subcode MUST be set to Attribute Length
+                        * Error.  The Data field MUST contain the erroneous
+                        * attribute (type, length, and value).
+                        * ----------
+                        * We do not currently have a good way to determine the
+                        * length of the attribute independent of the length
+                        * received in the message. Instead we send the
+                        * minimum between the amount of data we have and the
+                        * amount specified by the attribute length field.
+                        *
+                        * Instead of directly passing in the packet buffer and
+                        * offset we use the stream_get* functions to read into
+                        * a stack buffer, since they perform bounds checking
+                        * and we are working with untrusted data.
+                        */
+                       unsigned char ndata[BGP_MAX_PACKET_SIZE];
+                       memset(ndata, 0x00, sizeof(ndata));
+                       size_t lfl =
+                               CHECK_FLAG(flag, BGP_ATTR_FLAG_EXTLEN) ? 2 : 1;
+                       /* Rewind to end of flag field */
+                       stream_forward_getp(BGP_INPUT(peer), -(1 + lfl));
+                       /* Type */
+                       stream_get(&ndata[0], BGP_INPUT(peer), 1);
+                       /* Length */
+                       stream_get(&ndata[1], BGP_INPUT(peer), lfl);
+                       /* Value */
+                       size_t atl = attr_endp - startp;
+                       size_t ndl = MIN(atl, STREAM_READABLE(BGP_INPUT(peer)));
+                       stream_get(&ndata[lfl + 1], BGP_INPUT(peer), ndl);
+
                        bgp_notify_send_with_data(
                                peer, BGP_NOTIFY_UPDATE_ERR,
-                               BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, startp,
-                               attr_endp - startp);
+                               BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, ndata,
+                               ndl + lfl + 1);
+
                        return BGP_ATTR_PARSE_ERROR;
                }
 
@@ -2380,6 +2451,12 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr,
                        ret = BGP_ATTR_PARSE_ERROR;
                }
 
+               if (ret == BGP_ATTR_PARSE_EOR) {
+                       if (as4_path)
+                               aspath_unintern(&as4_path);
+                       return ret;
+               }
+
                /* If hard error occured immediately return to the caller. */
                if (ret == BGP_ATTR_PARSE_ERROR) {
                        zlog_warn("%s: Attribute %s, parse error", peer->host,
@@ -2617,8 +2694,9 @@ size_t bgp_packet_mpattr_start(struct stream *s, struct peer *peer, afi_t afi,
 
 void bgp_packet_mpattr_prefix(struct stream *s, afi_t afi, safi_t safi,
                              struct prefix *p, struct prefix_rd *prd,
-                             mpls_label_t *label, int addpath_encode,
-                             u_int32_t addpath_tx_id, struct attr *attr)
+                             mpls_label_t *label, u_int32_t num_labels,
+                             int addpath_encode, u_int32_t addpath_tx_id,
+                             struct attr *attr)
 {
        if (safi == SAFI_MPLS_VPN) {
                if (addpath_encode)
@@ -2630,8 +2708,8 @@ void bgp_packet_mpattr_prefix(struct stream *s, afi_t afi, safi_t safi,
                stream_put(s, &p->u.prefix, PSIZE(p->prefixlen));
        } else if (afi == AFI_L2VPN && safi == SAFI_EVPN) {
                /* EVPN prefix - contents depend on type */
-               bgp_evpn_encode_prefix(s, p, prd, label, attr, addpath_encode,
-                                      addpath_tx_id);
+               bgp_evpn_encode_prefix(s, p, prd, label, num_labels, attr,
+                                      addpath_encode, addpath_tx_id);
        } else if (safi == SAFI_LABELED_UNICAST) {
                /* Prefix write with label. */
                stream_put_labeled_prefix(s, p, label);
@@ -2714,8 +2792,9 @@ static void bgp_packet_mpattr_tea(struct bgp *bgp, struct peer *peer,
 
        if (attrlenfield > 0xff) {
                /* 2-octet length field */
-               stream_putc(s, BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL
-                                      | BGP_ATTR_FLAG_EXTLEN);
+               stream_putc(s,
+                           BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL
+                                   | BGP_ATTR_FLAG_EXTLEN);
                stream_putc(s, attrtype);
                stream_putw(s, attrlenfield & 0xffff);
        } else {
@@ -2759,8 +2838,8 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer,
                                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, int addpath_encode,
-                               u_int32_t addpath_tx_id)
+                               mpls_label_t *label, u_int32_t num_labels,
+                               int addpath_encode, u_int32_t addpath_tx_id)
 {
        size_t cp;
        size_t aspath_sizep;
@@ -2783,7 +2862,8 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer,
                mpattrlen_pos = bgp_packet_mpattr_start(s, peer, afi, safi,
                                                        vecarr, attr);
                bgp_packet_mpattr_prefix(s, afi, safi, p, prd, label,
-                                        addpath_encode, addpath_tx_id, attr);
+                                        num_labels, addpath_encode,
+                                        addpath_tx_id, attr);
                bgp_packet_mpattr_end(s, mpattrlen_pos);
        }
 
@@ -2953,14 +3033,15 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer,
        if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY)
            && (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES))) {
                if (attr->community->size * 4 > 255) {
-                       stream_putc(s, BGP_ATTR_FLAG_OPTIONAL
-                                              | BGP_ATTR_FLAG_TRANS
-                                              | BGP_ATTR_FLAG_EXTLEN);
+                       stream_putc(s,
+                                   BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS
+                                           | BGP_ATTR_FLAG_EXTLEN);
                        stream_putc(s, BGP_ATTR_COMMUNITIES);
                        stream_putw(s, attr->community->size * 4);
                } else {
-                       stream_putc(s, BGP_ATTR_FLAG_OPTIONAL
-                                              | BGP_ATTR_FLAG_TRANS);
+                       stream_putc(s,
+                                   BGP_ATTR_FLAG_OPTIONAL
+                                           | BGP_ATTR_FLAG_TRANS);
                        stream_putc(s, BGP_ATTR_COMMUNITIES);
                        stream_putc(s, attr->community->size * 4);
                }
@@ -2974,14 +3055,15 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer,
                       PEER_FLAG_SEND_LARGE_COMMUNITY)
            && (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES))) {
                if (lcom_length(attr->lcommunity) > 255) {
-                       stream_putc(s, BGP_ATTR_FLAG_OPTIONAL
-                                              | BGP_ATTR_FLAG_TRANS
-                                              | BGP_ATTR_FLAG_EXTLEN);
+                       stream_putc(s,
+                                   BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS
+                                           | BGP_ATTR_FLAG_EXTLEN);
                        stream_putc(s, BGP_ATTR_LARGE_COMMUNITIES);
                        stream_putw(s, lcom_length(attr->lcommunity));
                } else {
-                       stream_putc(s, BGP_ATTR_FLAG_OPTIONAL
-                                              | BGP_ATTR_FLAG_TRANS);
+                       stream_putc(s,
+                                   BGP_ATTR_FLAG_OPTIONAL
+                                           | BGP_ATTR_FLAG_TRANS);
                        stream_putc(s, BGP_ATTR_LARGE_COMMUNITIES);
                        stream_putc(s, lcom_length(attr->lcommunity));
                }
@@ -3033,14 +3115,16 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer,
                if (peer->sort == BGP_PEER_IBGP
                    || peer->sort == BGP_PEER_CONFED) {
                        if (attr->ecommunity->size * 8 > 255) {
-                               stream_putc(s, BGP_ATTR_FLAG_OPTIONAL
-                                                      | BGP_ATTR_FLAG_TRANS
-                                                      | BGP_ATTR_FLAG_EXTLEN);
+                               stream_putc(s,
+                                           BGP_ATTR_FLAG_OPTIONAL
+                                                   | BGP_ATTR_FLAG_TRANS
+                                                   | BGP_ATTR_FLAG_EXTLEN);
                                stream_putc(s, BGP_ATTR_EXT_COMMUNITIES);
                                stream_putw(s, attr->ecommunity->size * 8);
                        } else {
-                               stream_putc(s, BGP_ATTR_FLAG_OPTIONAL
-                                                      | BGP_ATTR_FLAG_TRANS);
+                               stream_putc(s,
+                                           BGP_ATTR_FLAG_OPTIONAL
+                                                   | BGP_ATTR_FLAG_TRANS);
                                stream_putc(s, BGP_ATTR_EXT_COMMUNITIES);
                                stream_putc(s, attr->ecommunity->size * 8);
                        }
@@ -3106,8 +3190,9 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer,
                        label_index = attr->label_index;
 
                        if (label_index != BGP_INVALID_LABEL_INDEX) {
-                               stream_putc(s, BGP_ATTR_FLAG_OPTIONAL
-                                                      | BGP_ATTR_FLAG_TRANS);
+                               stream_putc(s,
+                                           BGP_ATTR_FLAG_OPTIONAL
+                                                   | BGP_ATTR_FLAG_TRANS);
                                stream_putc(s, BGP_ATTR_PREFIX_SID);
                                stream_putc(s, 10);
                                stream_putc(s, BGP_PREFIX_SID_LABEL_INDEX);
@@ -3135,8 +3220,9 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer,
                 */
                aspath = aspath_delete_confed_seq(aspath);
 
-               stream_putc(s, BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL
-                                      | BGP_ATTR_FLAG_EXTLEN);
+               stream_putc(s,
+                           BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL
+                                   | BGP_ATTR_FLAG_EXTLEN);
                stream_putc(s, BGP_ATTR_AS4_PATH);
                aspath_sizep = stream_get_endp(s);
                stream_putw(s, 0);
@@ -3171,6 +3257,19 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer,
 #endif
        }
 
+       /* PMSI Tunnel */
+       if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_PMSI_TUNNEL)) {
+               stream_putc(s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS);
+               stream_putc(s, BGP_ATTR_PMSI_TUNNEL);
+               stream_putc(s, 9); // Length
+               stream_putc(s, 0); // Flags
+               stream_putc(s, 6); // Tunnel type: Ingress Replication (6)
+               stream_put(s, &(attr->label),
+                          BGP_LABEL_BYTES); // MPLS Label / VXLAN VNI
+               stream_put_ipv4(s, attr->nexthop.s_addr); // Unicast tunnel
+                                                         // endpoint IP address
+       }
+
        /* Unknown transit attribute. */
        if (attr->transit)
                stream_put(s, attr->transit->val, attr->transit->length);
@@ -3203,15 +3302,18 @@ size_t bgp_packet_mpunreach_start(struct stream *s, afi_t afi, safi_t safi)
 
 void bgp_packet_mpunreach_prefix(struct stream *s, struct prefix *p, afi_t afi,
                                 safi_t safi, struct prefix_rd *prd,
-                                mpls_label_t *label, int addpath_encode,
-                                u_int32_t addpath_tx_id, struct attr *attr)
+                                mpls_label_t *label, u_int32_t num_labels,
+                                int addpath_encode, u_int32_t addpath_tx_id,
+                                struct attr *attr)
 {
        u_char wlabel[3] = {0x80, 0x00, 0x00};
 
-       if (safi == SAFI_LABELED_UNICAST)
+       if (safi == SAFI_LABELED_UNICAST) {
                label = (mpls_label_t *)wlabel;
+               num_labels = 1;
+       }
 
-       return bgp_packet_mpattr_prefix(s, afi, safi, p, prd, label,
+       return bgp_packet_mpattr_prefix(s, afi, safi, p, prd, label, num_labels,
                                        addpath_encode, addpath_tx_id, attr);
 }
 
@@ -3321,14 +3423,15 @@ void bgp_dump_routes_attr(struct stream *s, struct attr *attr,
        /* Community attribute. */
        if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES)) {
                if (attr->community->size * 4 > 255) {
-                       stream_putc(s, BGP_ATTR_FLAG_OPTIONAL
-                                              | BGP_ATTR_FLAG_TRANS
-                                              | BGP_ATTR_FLAG_EXTLEN);
+                       stream_putc(s,
+                                   BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS
+                                           | BGP_ATTR_FLAG_EXTLEN);
                        stream_putc(s, BGP_ATTR_COMMUNITIES);
                        stream_putw(s, attr->community->size * 4);
                } else {
-                       stream_putc(s, BGP_ATTR_FLAG_OPTIONAL
-                                              | BGP_ATTR_FLAG_TRANS);
+                       stream_putc(s,
+                                   BGP_ATTR_FLAG_OPTIONAL
+                                           | BGP_ATTR_FLAG_TRANS);
                        stream_putc(s, BGP_ATTR_COMMUNITIES);
                        stream_putc(s, attr->community->size * 4);
                }
@@ -3338,19 +3441,21 @@ void bgp_dump_routes_attr(struct stream *s, struct attr *attr,
        /* Large Community attribute. */
        if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES)) {
                if (lcom_length(attr->lcommunity) > 255) {
-                       stream_putc(s, BGP_ATTR_FLAG_OPTIONAL
-                                              | BGP_ATTR_FLAG_TRANS
-                                              | BGP_ATTR_FLAG_EXTLEN);
+                       stream_putc(s,
+                                   BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS
+                                           | BGP_ATTR_FLAG_EXTLEN);
                        stream_putc(s, BGP_ATTR_LARGE_COMMUNITIES);
                        stream_putw(s, lcom_length(attr->lcommunity));
                } else {
-                       stream_putc(s, BGP_ATTR_FLAG_OPTIONAL
-                                              | BGP_ATTR_FLAG_TRANS);
+                       stream_putc(s,
+                                   BGP_ATTR_FLAG_OPTIONAL
+                                           | BGP_ATTR_FLAG_TRANS);
                        stream_putc(s, BGP_ATTR_LARGE_COMMUNITIES);
                        stream_putc(s, lcom_length(attr->lcommunity));
                }
 
-               stream_put(s, attr->lcommunity->val, lcom_length(attr->lcommunity));
+               stream_put(s, attr->lcommunity->val,
+                          lcom_length(attr->lcommunity));
        }
 
        /* Add a MP_NLRI attribute to dump the IPv6 next hop */
@@ -3389,8 +3494,9 @@ void bgp_dump_routes_attr(struct stream *s, struct attr *attr,
        /* Prefix SID */
        if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID)) {
                if (attr->label_index != BGP_INVALID_LABEL_INDEX) {
-                       stream_putc(s, BGP_ATTR_FLAG_OPTIONAL
-                                              | BGP_ATTR_FLAG_TRANS);
+                       stream_putc(s,
+                                   BGP_ATTR_FLAG_OPTIONAL
+                                           | BGP_ATTR_FLAG_TRANS);
                        stream_putc(s, BGP_ATTR_PREFIX_SID);
                        stream_putc(s, 10);
                        stream_putc(s, BGP_PREFIX_SID_LABEL_INDEX);