]> git.puffer.fish Git - matthieu/frr.git/commitdiff
bgpd: store and send bgp link-state attributes
authorLouis Scalbert <louis.scalbert@6wind.com>
Wed, 11 Jan 2023 12:24:47 +0000 (13:24 +0100)
committerLouis Scalbert <louis.scalbert@6wind.com>
Mon, 18 Sep 2023 13:07:32 +0000 (15:07 +0200)
Add the ability to store a raw copy of the incoming BGP Link-State
attributes and to redistribute them as is to other routes.

New types of data BGP_ATTR_LS and BGP_ATTR_LS_DATA are defined.

Signed-off-by: Louis Scalbert <louis.scalbert@6wind.com>
Signed-off-by: Olivier Dugeon <olivier.dugeon@orange.com>
bgpd/bgp_attr.c
bgpd/bgp_attr.h
bgpd/bgp_linkstate_tlv.c
bgpd/bgp_linkstate_tlv.h
bgpd/bgp_route.c
bgpd/bgpd.h

index 411522464164408a45ca90ad64573fe2ef6ca4e6..cc7afbe74f646cf7a2bf3d4dafb767c72f30805c 100644 (file)
@@ -43,6 +43,9 @@
 #include "bgp_linkstate_tlv.h"
 #include "bgp_mac.h"
 
+DEFINE_MTYPE_STATIC(BGPD, BGP_ATTR_LS, "BGP Attribute Link-State");
+DEFINE_MTYPE_STATIC(BGPD, BGP_ATTR_LS_DATA, "BGP Attribute Link-State Data");
+
 /* Attribute strings for logging. */
 static const struct message attr_str[] = {
        {BGP_ATTR_ORIGIN, "ORIGIN"},
@@ -66,6 +69,7 @@ static const struct message attr_str[] = {
 #ifdef ENABLE_BGP_VNC_ATTR
        {BGP_ATTR_VNC, "VNC"},
 #endif
+       {BGP_ATTR_LINK_STATE, "LINK_STATE"},
        {BGP_ATTR_LARGE_COMMUNITIES, "LARGE_COMMUNITY"},
        {BGP_ATTR_PREFIX_SID, "PREFIX_SID"},
        {BGP_ATTR_IPV6_EXT_COMMUNITIES, "IPV6_EXT_COMMUNITIES"},
@@ -200,6 +204,8 @@ static struct hash *vnc_hash = NULL;
 static struct hash *srv6_l3vpn_hash;
 static struct hash *srv6_vpn_hash;
 
+static struct hash *link_state_hash;
+
 struct bgp_attr_encap_subtlv *encap_tlv_dup(struct bgp_attr_encap_subtlv *orig)
 {
        struct bgp_attr_encap_subtlv *new;
@@ -717,6 +723,99 @@ static void srv6_finish(void)
        hash_clean_and_free(&srv6_vpn_hash, (void (*)(void *))srv6_vpn_free);
 }
 
+static void *link_state_hash_alloc(void *p)
+{
+       return p;
+}
+
+static void link_state_free(struct bgp_attr_ls *link_state)
+{
+       XFREE(MTYPE_BGP_ATTR_LS_DATA, link_state->data);
+       XFREE(MTYPE_BGP_ATTR_LS, link_state);
+}
+
+static struct bgp_attr_ls *link_state_intern(struct bgp_attr_ls *link_state)
+{
+       struct bgp_attr_ls *find;
+
+       find = hash_get(link_state_hash, link_state, link_state_hash_alloc);
+       if (find != link_state)
+               link_state_free(link_state);
+       find->refcnt++;
+       return find;
+}
+
+static void link_state_unintern(struct bgp_attr_ls **link_statep)
+{
+       struct bgp_attr_ls *link_state = *link_statep;
+
+       if (!*link_statep)
+               return;
+
+       if (link_state->refcnt)
+               link_state->refcnt--;
+
+       if (link_state->refcnt == 0) {
+               hash_release(link_state_hash, link_state);
+               link_state_free(link_state);
+               *link_statep = NULL;
+       }
+}
+
+static uint32_t link_state_hash_key_make(const void *p)
+{
+       const struct bgp_attr_ls *link_state = p;
+       uint32_t key = 0;
+
+       key = jhash_1word(link_state->length, key);
+       key = jhash(link_state->data, link_state->length, key);
+
+       return key;
+}
+
+static bool link_state_hash_cmp(const void *p1, const void *p2)
+{
+       const struct bgp_attr_ls *link_state1 = p1;
+       const struct bgp_attr_ls *link_state2 = p2;
+
+       if (!link_state1 && link_state2)
+               return false;
+       if (!link_state1 && link_state2)
+               return false;
+       if (!link_state1 && !link_state2)
+               return true;
+
+       if (link_state1->length != link_state2->length)
+               return false;
+
+       return !memcmp(link_state1->data, link_state2->data,
+                      link_state1->length);
+}
+
+static bool link_state_same(const struct bgp_attr_ls *h1,
+                           const struct bgp_attr_ls *h2)
+{
+       if (h1 == h2)
+               return true;
+       else if (h1 == NULL || h2 == NULL)
+               return false;
+       else
+               return link_state_hash_cmp((const void *)h1, (const void *)h2);
+}
+
+static void link_state_init(void)
+{
+       link_state_hash =
+               hash_create(link_state_hash_key_make, link_state_hash_cmp,
+                           "BGP Link-State Attributes TLVs");
+}
+
+static void link_state_finish(void)
+{
+       hash_clean_and_free(&link_state_hash,
+                           (void (*)(void *))link_state_free);
+}
+
 static unsigned int transit_hash_key_make(const void *p)
 {
        const struct transit *transit = p;
@@ -806,6 +905,8 @@ unsigned int attrhash_key_make(const void *p)
        MIX(attr->bh_type);
        MIX(attr->otc);
        MIX(bgp_attr_get_aigp_metric(attr));
+       if (attr->link_state)
+               MIX(link_state_hash_key_make(attr->link_state));
 
        return key;
 }
@@ -871,7 +972,8 @@ bool attrhash_cmp(const void *p1, const void *p2)
                    attr1->srte_color == attr2->srte_color &&
                    attr1->nh_type == attr2->nh_type &&
                    attr1->bh_type == attr2->bh_type &&
-                   attr1->otc == attr2->otc)
+                   attr1->otc == attr2->otc &&
+                   link_state_same(attr1->link_state, attr2->link_state))
                        return true;
        }
 
@@ -1031,6 +1133,12 @@ struct attr *bgp_attr_intern(struct attr *attr)
                else
                        attr->srv6_vpn->refcnt++;
        }
+       if (attr->link_state) {
+               if (!attr->link_state->refcnt)
+                       attr->link_state = link_state_intern(attr->link_state);
+               else
+                       attr->link_state->refcnt++;
+       }
 #ifdef ENABLE_BGP_VNC
        struct bgp_attr_encap_subtlv *vnc_subtlvs =
                bgp_attr_get_vnc_subtlvs(attr);
@@ -1249,6 +1357,8 @@ void bgp_attr_unintern_sub(struct attr *attr)
 
        srv6_l3vpn_unintern(&attr->srv6_l3vpn);
        srv6_vpn_unintern(&attr->srv6_vpn);
+
+       link_state_unintern(&attr->link_state);
 }
 
 /* Free bgp attribute and aspath. */
@@ -1412,6 +1522,7 @@ bgp_attr_malformed(struct bgp_attr_parser_args *args, uint8_t subcode,
        case BGP_ATTR_ENCAP:
        case BGP_ATTR_OTC:
                return BGP_ATTR_PARSE_WITHDRAW;
+       case BGP_ATTR_LINK_STATE:
        case BGP_ATTR_MP_REACH_NLRI:
        case BGP_ATTR_MP_UNREACH_NLRI:
                bgp_notify_send_with_data(peer->connection,
@@ -1498,6 +1609,7 @@ const uint8_t attr_flags_values[] = {
        [BGP_ATTR_IPV6_EXT_COMMUNITIES] =
                BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
        [BGP_ATTR_AIGP] = BGP_ATTR_FLAG_OPTIONAL,
+       [BGP_ATTR_LINK_STATE] = BGP_ATTR_FLAG_OPTIONAL,
 };
 static const size_t attr_flags_values_max = array_size(attr_flags_values) - 1;
 
@@ -3291,6 +3403,32 @@ aigp_ignore:
        return bgp_attr_ignore(peer, args->type);
 }
 
+/* Link-State (rfc7752) */
+static enum bgp_attr_parse_ret
+bgp_attr_linkstate(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;
+       struct stream *s = peer->curr;
+       struct bgp_attr_ls *bgp_attr_ls;
+       void *bgp_attr_ls_data;
+
+       if (STREAM_READABLE(s) == 0)
+               return BGP_ATTR_PARSE_PROCEED;
+
+       attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_LINK_STATE);
+
+       bgp_attr_ls = XCALLOC(MTYPE_BGP_ATTR_LS, sizeof(struct bgp_attr_ls));
+       bgp_attr_ls->length = length;
+       bgp_attr_ls_data = XCALLOC(MTYPE_BGP_ATTR_LS_DATA, length);
+       bgp_attr_ls->data = bgp_attr_ls_data;
+       stream_get(bgp_attr_ls_data, s, length);
+       attr->link_state = link_state_intern(bgp_attr_ls);
+
+       return BGP_ATTR_PARSE_PROCEED;
+}
+
 /* OTC attribute. */
 static enum bgp_attr_parse_ret bgp_attr_otc(struct bgp_attr_parser_args *args)
 {
@@ -3748,6 +3886,9 @@ enum bgp_attr_parse_ret bgp_attr_parse(struct peer *peer, struct attr *attr,
                case BGP_ATTR_AIGP:
                        ret = bgp_attr_aigp(&attr_args);
                        break;
+               case BGP_ATTR_LINK_STATE:
+                       ret = bgp_attr_linkstate(&attr_args);
+                       break;
                default:
                        ret = bgp_attr_unknown(&attr_args);
                        break;
@@ -4840,6 +4981,14 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer,
 #endif
        }
 
+       /* BGP Link-State */
+       if (attr && attr->link_state) {
+               stream_putc(s, BGP_ATTR_FLAG_OPTIONAL);
+               stream_putc(s, BGP_ATTR_LINK_STATE);
+               stream_putc(s, attr->link_state->length);
+               stream_put(s, attr->link_state->data, attr->link_state->length);
+       }
+
        /* PMSI Tunnel */
        if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_PMSI_TUNNEL)) {
                stream_putc(s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS);
@@ -4944,6 +5093,7 @@ void bgp_attr_init(void)
        transit_init();
        encap_init();
        srv6_init();
+       link_state_init();
 }
 
 void bgp_attr_finish(void)
@@ -4957,6 +5107,7 @@ void bgp_attr_finish(void)
        transit_finish();
        encap_finish();
        srv6_finish();
+       link_state_finish();
 }
 
 /* Make attribute packet. */
index 961e5f1224706117977bab0d7a02b5b420d6cd9b..e637b0efbfd358109a5da2abce2d430bf43daf49 100644 (file)
@@ -136,6 +136,13 @@ struct bgp_attr_srv6_l3vpn {
        uint8_t transposition_offset;
 };
 
+struct bgp_attr_ls {
+       unsigned long refcnt;
+
+       uint8_t length;
+       void *data;
+};
+
 /* BGP core attribute structure. */
 struct attr {
        /* AS Path structure */
@@ -159,6 +166,8 @@ struct attr {
        /* Path origin attribute */
        uint8_t origin;
 
+       struct bgp_attr_ls *link_state; /*  BGP Link State attribute  */
+
        /* PMSI tunnel type (RFC 6514). */
        enum pta_type pmsi_tnl_type;
 
index 5c324382f92706ad25d734016616067b6dc8d664..5538f7a761d30fab1fc83f963d83be10f72ddc94 100644 (file)
@@ -48,6 +48,105 @@ struct bgp_linkstate_tlv_info bgp_linkstate_tlv_infos[BGP_LS_TLV_MAX] = {
        [BGP_LS_TLV_IGP_ROUTER_ID] = {"IGP Router-ID", 4, 8, UNDEF_MULTPL},
        /* NRLI & BGP-LS Attributes */
        [BGP_LS_TLV_MULTI_TOPOLOGY_ID] = {"Multi-Topology ID", 2, MAX_SZ, 2},
+       /* BGP-LS Attributes */
+       [BGP_LS_TLV_NODE_MSD] = {"Node MSD", 2, MAX_SZ, 2},
+       [BGP_LS_TLV_LINK_MSD] = {"Link MSD", 2, MAX_SZ, 2},
+       [BGP_LS_TLV_BGP_ROUTER_ID] = {"BGP Router-ID", 4, 4, UNDEF_MULTPL},
+       [BGP_LS_TLV_BGP_CONFEDERATION_MEMBER] = {"BGP Confederation Member", 4, 4, UNDEF_MULTPL},
+       [BGP_LS_TLV_NODE_FLAG_BITS] = {"Node Flag Bits", 1, 1, UNDEF_MULTPL},
+       [BGP_LS_TLV_OPAQUE_NODE_ATTRIBUTE] = {"Opaque Node Attribute", 1, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_NODE_NAME] = {"Node Name", 1, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_IS_IS_AREA_IDENTIFIER] = {"IS-IS Area Identifier", 1, 13, UNDEF_MULTPL},
+       [BGP_LS_TLV_IPV4_ROUTER_ID_OF_LOCAL_NODE] =     {"IPv4 Router-ID of Local Node", 4, 4, UNDEF_MULTPL},
+       [BGP_LS_TLV_IPV6_ROUTER_ID_OF_LOCAL_NODE] = {"IPv6 Router-ID of Local Node", 16, 16, UNDEF_MULTPL},
+       [BGP_LS_TLV_IPV4_ROUTER_ID_OF_REMOTE_NODE] = {"IPv4 Router-ID of Remote Node", 4, 4, UNDEF_MULTPL},
+       [BGP_LS_TLV_IPV6_ROUTER_ID_OF_REMOTE_NODE] = {"IPv6 Router-ID of Remote Node", 16, 16, UNDEF_MULTPL},
+       [BGP_LS_TLV_S_BFD_DISCRIMINATORS] = {"S-BFD Discriminators", 4, MAX_SZ, 4},
+       [BGP_LS_TLV_SR_CAPABILITIES] = {"SR Capabilities", 12, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_SR_ALGORITHM] = {"SR Algorithm", 1, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_SR_LOCAL_BLOCK] = {"SR Local Block", 12, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_SRMS_PREFERENCE] = {"SRMS Preference", 1, 1, UNDEF_MULTPL},
+       [BGP_LS_TLV_FLEXIBLE_ALGORITHM_DEFINITION] = {"Flexible Algorithm Definition", 4, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_FLEXIBLE_ALGORITHM_EXCLUDE_ANY_AFFINITY] = {"Flexible Algorithm Exclude Any Affinity", 4, MAX_SZ, 4},
+       [BGP_LS_TLV_FLEXIBLE_ALGORITHM_INCLUDE_ANY_AFFINITY] = {"Flexible Algorithm Include Any Affinity", 4, MAX_SZ, 4},
+       [BGP_LS_TLV_FLEXIBLE_ALGORITHM_INCLUDE_ALL_AFFINITY] = {"Flexible Algorithm Include All Affinity", 4, MAX_SZ, 4},
+       [BGP_LS_TLV_FLEXIBLE_ALGORITHM_DEFINITION_FLAGS] = {"Flexible Algorithm Definition Flags", 4, MAX_SZ, 4},
+       [BGP_LS_TLV_FLEXIBLE_ALGORITHM_PREFIX_METRIC] = {"Flexible Algorithm Prefix Metric", 8, 8, UNDEF_MULTPL},
+       [BGP_LS_TLV_FLEXIBLE_ALGORITHM_EXCLUDE_SRLG] = {"Flexible Algorithm Exclude SRLG", 4, MAX_SZ, 4},
+       [BGP_LS_TLV_ADMINISTRATIVE_GROUP] = {"Administrative group", 4, 4, UNDEF_MULTPL},
+       [BGP_LS_TLV_MAXIMUM_LINK_BANDWIDTH] = {"Maximum link bandwidth", 4, 4, UNDEF_MULTPL},
+       [BGP_LS_TLV_MAX_RESERVABLE_LINK_BANDWIDTH] = {"Max. reservable link bandwidth", 4, 4, UNDEF_MULTPL},
+       [BGP_LS_TLV_UNRESERVED_BANDWIDTH] = {"Unreserved bandwidth", 32, 32, UNDEF_MULTPL},
+       [BGP_LS_TLV_TE_DEFAULT_METRIC] = {"TE Default Metric", 3, 4, UNDEF_MULTPL},
+       [BGP_LS_TLV_LINK_PROTECTION_TYPE] = {"Link Protection Type", 2, 2, UNDEF_MULTPL},
+       [BGP_LS_TLV_MPLS_PROTOCOL_MASK] = {"MPLS Protocol Mask", 1, 1, UNDEF_MULTPL},
+       [BGP_LS_TLV_IGP_METRIC] = {"IGP Metric", 1, 3, UNDEF_MULTPL},
+       [BGP_LS_TLV_SHARED_RISK_LINK_GROUP] = {"Shared Risk Link Group", 4, MAX_SZ, 4},
+       [BGP_LS_TLV_OPAQUE_LINK_ATTRIBUTE] = {"Opaque Link Attribute", 1, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_LINK_NAME] = {"Link Name", 1, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_ADJACENCY_SID] = {"Adjacency SID", 7, 8, UNDEF_MULTPL},
+       [BGP_LS_TLV_LAN_ADJACENCY_SID] = {"LAN Adjacency SID", 11, 14, UNDEF_MULTPL},
+       [BGP_LS_TLV_PEERNODE_SID] = {"PeerNode SID", 7, 8, UNDEF_MULTPL},
+       [BGP_LS_TLV_PEERADJ_SID] = {"PeerAdj SID", 7, 8, UNDEF_MULTPL},
+       [BGP_LS_TLV_PEERSET_SID] = {"PeerSet SID", 7, 8, UNDEF_MULTPL},
+       [BGP_LS_TLV_RTM_CAPABILITY] = {"RTM Capability", 1, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_UNIDIRECTIONAL_LINK_DELAY] = {"Unidirectional Link Delay", 4, 4, UNDEF_MULTPL},
+       [BGP_LS_TLV_MIN_MAX_UNIDIRECTIONAL_LINK_DELAY] = {"Min/Max Unidirectional Link Delay", 8, 8, UNDEF_MULTPL},
+       [BGP_LS_TLV_UNIDIRECTIONAL_DELAY_VARIATION] = {"Unidirectional Delay Variation", 4, 4, UNDEF_MULTPL},
+       [BGP_LS_TLV_UNIDIRECTIONAL_LINK_LOSS] = {"Unidirectional Link Loss", 4, 4, UNDEF_MULTPL},
+       [BGP_LS_TLV_UNIDIRECTIONAL_RESIDUAL_BANDWIDTH] = {"Unidirectional Residual Bandwidth", 4, 4, UNDEF_MULTPL},
+       [BGP_LS_TLV_UNIDIRECTIONAL_AVAILABLE_BANDWIDTH] = {"Unidirectional Available Bandwidth", 4, 4, UNDEF_MULTPL},
+       [BGP_LS_TLV_UNIDIRECTIONAL_UTILIZED_BANDWIDTH] = {"Unidirectional Utilized Bandwidth", 4, 4, UNDEF_MULTPL},
+       [BGP_LS_TLV_GRACEFUL_LINK_SHUTDOWN_TLV] = {"Graceful-Link-Shutdown TLV", 0, 0, UNDEF_MULTPL},
+       [BGP_LS_TLV_APPLICATION_SPECIFIC_LINK_ATTRIBUTES] = {"Application-Specific Link Attributes", 11, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_IGP_FLAGS] = {"IGP Flags", 1, 1, UNDEF_MULTPL},
+       [BGP_LS_TLV_IGP_ROUTE_TAG] = {"IGP Route Tag", 4, MAX_SZ, 4},
+       [BGP_LS_TLV_IGP_EXTENDED_ROUTE_TAG] = {"IGP Extended Route Tag", 8, MAX_SZ, 8},
+       [BGP_LS_TLV_PREFIX_METRIC] = {"Prefix Metric", 3, 4, UNDEF_MULTPL},
+       [BGP_LS_TLV_OSPF_FORWARDING_ADDRESS] = {"OSPF Forwarding Address", 4, 4, UNDEF_MULTPL},
+       [BGP_LS_TLV_OPAQUE_PREFIX_ATTRIBUTE] = {"Opaque Prefix Attribute", 1, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_PREFIX_SID] = {"Prefix-SID", 7, 8, UNDEF_MULTPL},
+       [BGP_LS_TLV_RANGE] = {"Range", 11, 12, UNDEF_MULTPL},
+       [BGP_LS_TLV_SID_LABEL] = {"SID/Label", 3, 4, UNDEF_MULTPL},
+       [BGP_LS_TLV_PREFIX_ATTRIBUTES_FLAGS] = {"Prefix Attributes Flags", 1, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_SOURCE_ROUTER_IDENTIFIER] = {"Source Router Identifier", 4, 16, UNDEF_MULTPL},
+       [BGP_LS_TLV_L2_BUNDLE_MEMBER_ATTRIBUTES] = {"L2 Bundle Member Attributes", 4, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_EXTENDED_ADMINISTRATIVE_GROUP] = {"Extended Administrative Group", 4, MAX_SZ, 4},
+       [BGP_LS_TLV_SOURCE_OSPF_ROUTER_ID] = {"Source OSPF Router-ID", 4, 4, UNDEF_MULTPL},
+       /* display not yet supported */
+       [BGP_LS_TLV_SRV6_SID_INFORMATION_TLV] = {"SRv6 SID Information TLV", UNDEF_MIN_SZ, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_TUNNEL_ID_TLV] = {"Tunnel ID TLV", UNDEF_MIN_SZ, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_LSP_ID_TLV] = {"LSP ID TLV", UNDEF_MIN_SZ, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_IPV4_6_TUNNEL_HEAD_END_ADDRESS_TLV] = {"IPv4/6 Tunnel Head-end address TLV", UNDEF_MIN_SZ, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_IPV4_6_TUNNEL_TAIL_END_ADDRESS_TLV] = {"IPv4/6 Tunnel Tail-end address TLV", UNDEF_MIN_SZ, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_SR_POLICY_CP_DESCRIPTOR_TLV] = {"SR Policy CP Descriptor TLV", UNDEF_MIN_SZ, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_MPLS_LOCAL_CROSS_CONNECT_TLV] = {"MPLS Local Cross Connect TLV", UNDEF_MIN_SZ, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_MPLS_CROSS_CONNECT_INTERFACE_TLV] = {"MPLS Cross Connect Interface TLV", UNDEF_MIN_SZ, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_MPLS_CROSS_CONNECT_FEC_TLV] = {"MPLS Cross Connect FEC TLV", UNDEF_MIN_SZ, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_SRV6_CAPABILITIES_TLV] = {"SRv6 Capabilities TLV", UNDEF_MIN_SZ, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_FLEXIBLE_ALGORITHM_UNSUPPORTED] = {"Flexible Algorithm Unsupported", UNDEF_MIN_SZ, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_SRV6_END_X_SID_TLV] = {"SRv6 End.X SID TLV", UNDEF_MIN_SZ, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_IS_IS_SRV6_LAN_END_X_SID_TLV] = {"IS-IS SRv6 LAN End.X SID TLV", UNDEF_MIN_SZ, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_OSPFV3_SRV6_LAN_END_X_SID_TLV] = {"OSPFv3 SRv6 LAN End.X SID TLV", UNDEF_MIN_SZ, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_IS_IS_FLOOD_REFLECTION] = {"IS-IS Flood Reflection", UNDEF_MIN_SZ, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_SRV6_LOCATOR_TLV] = {"SRv6 Locator TLV", UNDEF_MIN_SZ, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_MPLS_TE_POLICY_STATE_TLV] = {"MPLS-TE Policy State TLV", UNDEF_MIN_SZ, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_SR_BSID_TLV] = {"SR BSID TLV", UNDEF_MIN_SZ, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_SR_CP_STATE_TLV] = {"SR CP State TLV", UNDEF_MIN_SZ, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_SR_CP_NAME_TLV] = {"SR CP Name TLV", UNDEF_MIN_SZ, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_SR_CP_CONSTRAINTS_TLV] = {"SR CP Constraints TLV", UNDEF_MIN_SZ, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_SR_SEGMENT_LIST_TLV] = {"SR Segment List TLV", UNDEF_MIN_SZ, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_SR_SEGMENT_SUB_TLV] = {"SR Segment sub-TLV", UNDEF_MIN_SZ, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_SR_SEGMENT_LIST_METRIC_SUB_TLV] = {"SR Segment List Metric sub-TLV", UNDEF_MIN_SZ, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_SR_AFFINITY_CONSTRAINT_SUB_TLV] = {"SR Affinity Constraint sub-TLV", UNDEF_MIN_SZ, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_SR_SRLG_CONSTRAINT_SUB_TLV] = {"SR SRLG Constraint sub-TLV", UNDEF_MIN_SZ, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_SR_BANDWIDTH_CONSTRAINT_SUB_TLV] = {"SR Bandwidth Constraint sub-TLV", UNDEF_MIN_SZ, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_SR_DISJOINT_GROUP_CONSTRAINT_SUB_TLV] = {"SR Disjoint Group Constraint sub-TLV", UNDEF_MIN_SZ, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_SRV6_BSID_TLV] = {"SRv6 BSID TLV", UNDEF_MIN_SZ, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_SR_POLICY_NAME_TLV] = {"SR Policy Name TLV", UNDEF_MIN_SZ, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_SRV6_ENDPOINT_FUNCTION_TLV] = {"SRv6 Endpoint Function TLV", UNDEF_MIN_SZ, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_SRV6_BGP_PEER_NODE_SID_TLV] = {"SRv6 BGP Peer Node SID TLV", UNDEF_MIN_SZ, MAX_SZ, UNDEF_MULTPL},
+       [BGP_LS_TLV_SRV6_SID_STRUCTURE_TLV] = {"SRv6 SID Structure TLV", UNDEF_MIN_SZ, MAX_SZ, UNDEF_MULTPL},
 };
 /* clang-format on */
 
@@ -87,6 +186,19 @@ static uint16_t pnt_decode16(uint8_t **pnt)
        return data;
 }
 
+static uint32_t pnt_decode24(uint8_t **pnt)
+{
+       uint8_t tmp1;
+       uint16_t tmp2;
+
+       memcpy(&tmp1, *pnt, sizeof(uint8_t));
+       memcpy(&tmp2, *pnt + sizeof(uint8_t), sizeof(uint16_t));
+
+       *pnt += 3;
+
+       return (tmp1 << 16) | ntohs(tmp2);
+}
+
 static uint32_t pnt_decode32(uint8_t **pnt)
 {
        uint32_t data;
@@ -586,3 +698,1053 @@ void bgp_linkstate_nlri_prefix_json(json_object *json, uint16_t nlri_type,
                        return;
        }
 }
+
+
+static uint8_t *bgp_linkstate_tlv_binary_string(char *buf, size_t buf_sz,
+                                               uint8_t *pnt, uint16_t length)
+{
+       uint8_t tmp;
+       int i, j;
+
+       for (i = 0; i < length; i++) {
+               if (i == 0)
+                       snprintf(buf, buf_sz, "0b");
+               else
+                       snprintf(buf + strlen(buf), buf_sz - strlen(buf), " ");
+               tmp = pnt_decode8(&pnt);
+               for (j = 7; j >= 0; j--)
+                       snprintf(buf + strlen(buf), buf_sz - strlen(buf), "%d",
+                                (tmp >> j) & 1);
+       }
+
+       return pnt;
+}
+
+/* dump bits. Their meaning is not decoded */
+static uint8_t *bgp_linkstate_tlv_binary_display(struct vty *vty, uint8_t *pnt,
+                                                uint16_t length,
+                                                json_object *json)
+{
+       char buf[290];
+       uint8_t tmp;
+       int i, j;
+
+       if (json) {
+               pnt = bgp_linkstate_tlv_binary_string(buf, sizeof(buf), pnt,
+                                                     length);
+               json_object_string_add(json, "data", buf);
+               return pnt;
+       }
+
+       for (i = 0; i < length; i++) {
+               if (i == 0)
+                       vty_out(vty, "0b");
+               else
+                       vty_out(vty, " ");
+               tmp = pnt_decode8(&pnt);
+               for (j = 7; j >= 0; j--)
+                       vty_out(vty, "%d", (tmp >> j) & 1);
+       }
+       vty_out(vty, "\n");
+
+       return pnt;
+}
+
+static void bgp_linkstate_tlv_hexa_display(struct vty *vty, uint8_t *pnt,
+                                          uint16_t length, json_object *json)
+{
+       uint8_t *lim = pnt + length;
+       char buf[290];
+       int i;
+
+       if (json) {
+               snprintf(buf, sizeof(buf), "0x");
+               for (; pnt < lim; pnt++)
+                       snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+                                "%02x", *pnt);
+               json_object_string_add(json, "data", buf);
+
+               return;
+       }
+
+       vty_out(vty, "0x");
+       for (i = 0; pnt < lim; pnt++, i++) {
+               if (i != 0 && i % 8 == 0)
+                       vty_out(vty, " ");
+               vty_out(vty, "%02x", *pnt);
+       }
+       vty_out(vty, "\n");
+}
+
+static void bgp_linkstate_tlv_integer_list_display(struct vty *vty,
+                                                  uint8_t *pnt,
+                                                  uint16_t length,
+                                                  uint8_t integer_sz,
+                                                  json_object *json)
+{
+       json_object *json_array = NULL;
+       int i;
+
+       if (json) {
+               json_array = json_object_new_array();
+               json_object_object_add(json, "data", json_array);
+       }
+
+       for (i = 0; i < (length / integer_sz); i++) {
+               switch (integer_sz) {
+               case 1:
+                       if (json) {
+                               json_object_array_add(
+                                       json_array,
+                                       json_object_new_int(pnt_decode8(&pnt)));
+                               break;
+                       }
+                       vty_out(vty, "%s%u", i == 0 ? "" : ", ",
+                               pnt_decode8(&pnt));
+                       break;
+               case 2:
+                       if (json) {
+                               json_object_array_add(
+                                       json_array,
+                                       json_object_new_int(
+                                               pnt_decode16(&pnt)));
+                               break;
+                       }
+                       vty_out(vty, "%s%u", i == 0 ? "" : ", ",
+                               pnt_decode16(&pnt));
+                       break;
+               case 4:
+                       if (json) {
+                               json_object_array_add(
+                                       json_array,
+                                       json_object_new_int(
+                                               pnt_decode32(&pnt)));
+                               break;
+                       }
+                       vty_out(vty, "%s%u", i == 0 ? "" : ", ",
+                               pnt_decode32(&pnt));
+                       break;
+               case 8:
+                       if (json) {
+                               json_object_array_add(
+                                       json_array,
+                                       json_object_new_int64(
+                                               pnt_decode64(&pnt)));
+                               break;
+                       }
+                       vty_out(vty, "%s%" PRIu64, i == 0 ? "" : ", ",
+                               pnt_decode64(&pnt));
+                       break;
+               }
+       }
+       vty_out(vty, "\n");
+}
+
+static void bgp_linkstate_tlv_integer_display(struct vty *vty, uint8_t *pnt,
+                                             uint16_t length,
+                                             json_object *json)
+{
+       switch (length) {
+       case 1:
+               if (json) {
+                       json_object_int_add(json, "data", pnt_decode8(&pnt));
+                       break;
+               }
+               vty_out(vty, "%u\n", pnt_decode8(&pnt));
+               break;
+       case 2:
+               if (json) {
+                       json_object_int_add(json, "data", pnt_decode16(&pnt));
+                       break;
+               }
+               vty_out(vty, "%u\n", pnt_decode16(&pnt));
+               break;
+       case 3:
+               if (json) {
+                       json_object_int_add(json, "data", pnt_decode24(&pnt));
+                       break;
+               }
+               vty_out(vty, "%u\n", pnt_decode24(&pnt));
+               break;
+       case 4:
+               if (json) {
+                       json_object_int_add(json, "data", pnt_decode32(&pnt));
+                       break;
+               }
+               vty_out(vty, "%u\n", pnt_decode32(&pnt));
+               break;
+       case 8:
+               if (json) {
+                       json_object_int_add(json, "data", pnt_decode64(&pnt));
+                       break;
+               }
+               vty_out(vty, "%" PRIu64 "\n", pnt_decode64(&pnt));
+               break;
+       }
+}
+
+static void bgp_linkstate_tlv_ipv4_6_address_display(struct vty *vty,
+                                                    uint8_t *pnt,
+                                                    uint16_t length,
+                                                    json_object *json)
+{
+       if (length == IPV4_MAX_BYTELEN) {
+               if (json) {
+                       json_object_string_addf(json, "data", "%pI4",
+                                               (in_addr_t *)pnt);
+                       return;
+               }
+               vty_out(vty, "%pI4\n", (in_addr_t *)pnt);
+       } else if (length == IPV6_MAX_BYTELEN) {
+               if (json) {
+                       json_object_string_addf(json, "data", "%pI6",
+                                               (struct in6_addr *)pnt);
+                       return;
+               }
+               vty_out(vty, "%pI6\n", (struct in6_addr *)pnt);
+       } else
+               bgp_linkstate_tlv_hexa_display(vty, pnt, length, json);
+}
+
+static void bgp_linkstate_tlv_name_display(struct vty *vty, uint8_t *pnt,
+                                          uint16_t length, json_object *json)
+{
+       char buf[length + 1];
+       int i;
+
+       buf[0] = '\0';
+       for (i = 0; i < length; i++)
+               snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%c",
+                        pnt_decode8(&pnt));
+
+       if (json)
+               json_object_string_add(json, "data", buf);
+       else
+               vty_out(vty, "%s\n", buf);
+}
+
+static void bgp_linkstate_tlv_msd_display(struct vty *vty, uint8_t *pnt,
+                                         uint16_t length, int indent,
+                                         json_object *json)
+{
+       json_object *json_array = NULL;
+       json_object *json_data = NULL;
+       int i;
+
+       if (json) {
+               json_array = json_object_new_array();
+               json_object_object_add(json, "data", json_array);
+       }
+
+       for (i = 0; i < (length / 2); i++) {
+               if (json) {
+                       json_data = json_object_new_object();
+                       json_object_array_add(json_array, json_data);
+                       json_object_int_add(json_data, "type",
+                                           pnt_decode8(&pnt));
+                       json_object_int_add(json_data, "value",
+                                           pnt_decode8(&pnt));
+                       continue;
+               }
+               vty_out(vty, "\n%*sType: %u Value: %u", indent, "",
+                       pnt_decode8(&pnt), pnt_decode8(&pnt));
+       }
+
+       if (!json)
+               vty_out(vty, "\n");
+}
+
+static void bgp_linkstate_tlv_bandwidth_display(struct vty *vty, uint8_t *pnt,
+                                               uint16_t length,
+                                               json_object *json)
+{
+       union {
+               float r;
+               uint32_t d;
+       } float_uint32;
+
+       float_uint32.d = pnt_decode32(&pnt);
+
+       if (json) {
+               json_object_double_add(json, "data", float_uint32.r);
+               json_object_string_add(json, "dataUnit", "bps");
+               return;
+       }
+       vty_out(vty, "%g Mbps\n", float_uint32.r / 1000 / 1000 * 8);
+}
+
+static void bgp_linkstate_tlv_unreserved_bandwidth_display(struct vty *vty,
+                                                          uint8_t *pnt,
+                                                          uint16_t length,
+                                                          int indent,
+                                                          json_object *json)
+{
+       json_object *json_data = NULL;
+       union {
+               float r;
+               uint32_t d;
+       } float_uint32;
+       char buf[3];
+       int i;
+
+       if (json) {
+               json_data = json_object_new_object();
+               json_object_object_add(json, "data", json_data);
+               for (i = 0; i < MAX_CLASS_TYPE; i++) {
+                       float_uint32.d = pnt_decode32(&pnt);
+                       snprintf(buf, sizeof(buf), "%d", i);
+                       json_object_double_add(json_data, buf, float_uint32.r);
+               }
+               json_object_string_add(json, "dataUnit", "bps");
+               return;
+       }
+
+       for (i = 0; i < MAX_CLASS_TYPE; i += 2) {
+               float_uint32.d = pnt_decode32(&pnt);
+               vty_out(vty, "\n%*s[%d]: %g Mbps  ", indent, "", i,
+                       float_uint32.r / 1000 / 1000 * 8);
+               float_uint32.d = pnt_decode32(&pnt);
+               vty_out(vty, "[%d]: %g Mbps", i + 1,
+                       float_uint32.r / 1000 / 1000 * 8);
+       }
+       vty_out(vty, "\n");
+}
+
+static void bgp_linkstate_tlv_sid_display(struct vty *vty, uint8_t *pnt,
+                                         uint16_t length, uint16_t type,
+                                         int indent, json_object *json)
+{
+       json_object *json_data = NULL;
+       char buf[11];
+       uint32_t sid;
+
+       if (json) {
+               json_data = json_object_new_object();
+               json_object_object_add(json, "data", json_data);
+       }
+
+       if (json) {
+               pnt = bgp_linkstate_tlv_binary_string(buf, sizeof(buf), pnt, 1);
+               json_object_string_add(json_data, "flags", buf);
+       } else {
+               vty_out(vty, "\n%*sFlags: ", indent, "");
+               pnt = bgp_linkstate_tlv_binary_display(vty, pnt, 1, json);
+       }
+
+       if (type == BGP_LS_TLV_PREFIX_SID) {
+               if (json)
+                       json_object_int_add(json_data, "algorithm",
+                                           pnt_decode8(&pnt));
+               else
+                       vty_out(vty, "%*sAlgorithm: %u\n", indent, "",
+                               pnt_decode8(&pnt));
+       } else {
+               if (json)
+                       json_object_int_add(json_data, "weight",
+                                           pnt_decode8(&pnt));
+               else
+                       vty_out(vty, "%*sWeight: %u\n", indent, "",
+                               pnt_decode8(&pnt));
+       }
+
+       pnt += 2; /* ignore reserved 2 bytes */
+
+       if (type == BGP_LS_TLV_LAN_ADJACENCY_SID) {
+               vty_out(vty, "%*sNeighbor ID:", indent, "");
+               if (length == 11 || length == 12) {
+                       /* OSPF Router-ID */
+                       if (json)
+                               json_object_string_addf(json_data, "neighborId",
+                                                       "%pI4\n",
+                                                       (in_addr_t *)pnt);
+                       else
+                               vty_out(vty, "%pI4\n", (in_addr_t *)pnt);
+                       pnt += 4;
+               } else {
+                       /* IS-IS System-ID */
+                       if (json)
+                               json_object_string_addf(json_data, "neighborId",
+                                                       "%pSY\n",
+                                                       (uint8_t *)pnt);
+                       else
+                               vty_out(vty, "%pSY\n", (uint8_t *)pnt);
+                       pnt += 6;
+               }
+       }
+
+       if (length == 7 || length == 11 || length == 13)
+               sid = pnt_decode24(&pnt);
+       else
+               sid = pnt_decode32(&pnt);
+
+       if (json)
+               json_object_int_add(json_data, "sid", sid);
+       else
+               vty_out(vty, "%*sSID: %u\n", indent, "", sid);
+}
+
+static void bgp_linkstate_tlv_range_display(struct vty *vty, uint8_t *pnt,
+                                           uint16_t length, int indent,
+                                           json_object *json)
+{
+       json_object *json_data = NULL;
+       char buf[11];
+
+       if (json) {
+               json_data = json_object_new_object();
+               json_object_object_add(json, "data", json_data);
+               pnt = bgp_linkstate_tlv_binary_string(buf, sizeof(buf), pnt, 1);
+               json_object_string_add(json_data, "flags", buf);
+       } else {
+               vty_out(vty, "\n%*sFlags: ", indent, "");
+               pnt = bgp_linkstate_tlv_binary_display(vty, pnt, 1, json);
+       }
+       pnt++; /* ignore reserved byte */
+       if (json)
+               json_object_int_add(json_data, "rangeSize", pnt_decode16(&pnt));
+       else
+               vty_out(vty, "%*sRange Size: %u\n", indent, "",
+                       pnt_decode16(&pnt));
+
+       /* RFC9085 2.3.5 is unclear. Just display a hexa dump */
+       bgp_linkstate_tlv_hexa_display(vty, pnt, length - 4, json_data);
+}
+
+static void bgp_linkstate_tlv_delay_display(struct vty *vty, uint8_t *pnt,
+                                           uint16_t length, uint16_t type,
+                                           json_object *json)
+{
+       json_object *json_data = NULL;
+       uint32_t tmp32;
+       bool anomalous;
+
+       if (json) {
+               json_data = json_object_new_object();
+               json_object_object_add(json, "data", json_data);
+       }
+
+       tmp32 = pnt_decode32(&pnt);
+       anomalous = !!(tmp32 & TE_EXT_ANORMAL);
+
+       if (json)
+               json_object_boolean_add(json_data, "anomalous", anomalous);
+       else if (anomalous)
+               vty_out(vty, "Anomalous ");
+
+       if (json)
+               json_object_string_add(json, "dataUnit", "microseconds");
+
+       if (type == BGP_LS_TLV_UNIDIRECTIONAL_LINK_DELAY ||
+           type == BGP_LS_TLV_UNIDIRECTIONAL_DELAY_VARIATION) {
+               if (json)
+                       json_object_int_add(json_data, "delay",
+                                           tmp32 & TE_EXT_MASK);
+               else
+                       vty_out(vty, "%u microseconds\n", tmp32 & TE_EXT_MASK);
+       } else if (type == BGP_LS_TLV_MIN_MAX_UNIDIRECTIONAL_LINK_DELAY) {
+               if (json) {
+                       json_object_int_add(json_data, "minDelay",
+                                           tmp32 & TE_EXT_MASK);
+                       json_object_int_add(json_data, "maxDelay",
+                                           pnt_decode32(&pnt) & TE_EXT_MASK);
+               } else {
+                       vty_out(vty, "%u", tmp32 & TE_EXT_MASK);
+                       vty_out(vty, "/%u microseconds\n",
+                               pnt_decode32(&pnt) & TE_EXT_MASK);
+               }
+       }
+}
+
+static void bgp_linkstate_tlv_unidirectional_link_loss_display(
+       struct vty *vty, uint8_t *pnt, uint16_t length, json_object *json)
+{
+       json_object *json_data = NULL;
+       uint32_t tmp32;
+       float value;
+       bool anomalous;
+
+       if (json) {
+               json_data = json_object_new_object();
+               json_object_object_add(json, "data", json_data);
+       }
+
+       tmp32 = pnt_decode32(&pnt);
+
+       anomalous = !!(tmp32 & TE_EXT_ANORMAL);
+       value = ((float)(tmp32 & TE_EXT_MASK)) * LOSS_PRECISION;
+
+       if (json) {
+               json_object_boolean_add(json_data, "anomalous", anomalous);
+               json_object_double_add(json_data, "lossPercent", value);
+               return;
+       }
+
+       if (anomalous)
+               vty_out(vty, "Anomalous ");
+       vty_out(vty, "%g%%\n", value);
+}
+
+static void bgp_linkstate_tlv_asla_display(struct vty *vty, uint8_t *pnt,
+                                          uint16_t length, int indent,
+                                          json_object *json)
+{
+       json_object *json_data = NULL;
+       char buf[290];
+       uint8_t sabm_len, udabm_len;
+       struct bgp_attr_ls attr_ls;
+       uint8_t *orig_pnt = pnt;
+
+       sabm_len = pnt_decode8(&pnt);
+       udabm_len = pnt_decode8(&pnt);
+       pnt += 2; /* ignore reserved 2 bytes */
+
+       if (json) {
+               json_data = json_object_new_object();
+               json_object_object_add(json, "data", json_data);
+               pnt = bgp_linkstate_tlv_binary_string(buf, sizeof(buf), pnt,
+                                                     sabm_len);
+               json_object_string_add(json_data, "sabmFlags", buf);
+               pnt = bgp_linkstate_tlv_binary_string(buf, sizeof(buf), pnt,
+                                                     udabm_len);
+               json_object_string_add(json_data, "udabmFlags", buf);
+       } else {
+               vty_out(vty, "\n%*sSABM Flags : ", indent, "");
+               pnt = bgp_linkstate_tlv_binary_display(vty, pnt, sabm_len,
+                                                      json);
+               vty_out(vty, "%*sUDABM Flags: ", indent, "");
+               pnt = bgp_linkstate_tlv_binary_display(vty, pnt, udabm_len,
+                                                      json);
+       }
+
+       attr_ls.length = length - (pnt - orig_pnt);
+       attr_ls.data = pnt;
+
+       bgp_linkstate_tlv_attribute_display(vty, &attr_ls, indent, json_data);
+}
+
+static void bgp_linkstate_tlv_sr_range_display(struct vty *vty, uint8_t *pnt,
+                                              uint16_t length, int indent,
+                                              json_object *json)
+{
+       json_object *json_data = NULL;
+       struct bgp_attr_ls attr_ls;
+       uint8_t *orig_pnt = pnt;
+       char buf[11];
+
+       if (json) {
+               json_data = json_object_new_object();
+               json_object_object_add(json, "data", json_data);
+               pnt = bgp_linkstate_tlv_binary_string(buf, sizeof(buf), pnt, 1);
+               json_object_string_add(json_data, "flags", buf);
+       } else {
+               vty_out(vty, "\n%*sFlags: ", indent, "");
+               pnt = bgp_linkstate_tlv_binary_display(vty, pnt, 1, json);
+       }
+       pnt++; /* ignore reserved byte */
+       if (json)
+               json_object_int_add(json_data, "range", pnt_decode24(&pnt));
+       else
+               vty_out(vty, "%*sRange: %u\n", indent, "", pnt_decode24(&pnt));
+
+       attr_ls.length = length - (pnt - orig_pnt);
+       attr_ls.data = pnt;
+
+       bgp_linkstate_tlv_attribute_display(vty, &attr_ls, indent, json_data);
+}
+
+static void bgp_linkstate_tlv_sid_label_display(struct vty *vty, uint8_t *pnt,
+                                               uint16_t length,
+                                               json_object *json)
+{
+       json_object *json_data = NULL;
+
+       /* RFC9085
+        * If the length is set to 3, then the 20 rightmost bits
+        * represent a label (the total TLV size is 7), and the 4
+        * leftmost bits are set to 0. If the length is set to 4, then
+        * the value represents a 32-bit SID (the total TLV size is 8).
+        */
+       if (json) {
+               json_data = json_object_new_object();
+               json_object_object_add(json, "data", json_data);
+       }
+
+       if (length == 3) {
+               if (json)
+                       json_object_int_add(json_data, "fromLabel",
+                                           pnt_decode24(&pnt) & 0x0FFFFF);
+               else
+                       vty_out(vty, "From Label: %u\n",
+                               pnt_decode24(&pnt) & 0x0FFFFF);
+       } else {
+               if (json)
+                       json_object_int_add(json_data, "fromIndex",
+                                           pnt_decode32(&pnt));
+               else
+                       vty_out(vty, "From Index: %u\n", pnt_decode32(&pnt));
+       }
+}
+
+static void bgp_linkstate_tlv_flexible_algorithm_definition_display(
+       struct vty *vty, uint8_t *pnt, uint16_t length, int indent,
+       json_object *json)
+{
+       json_object *json_data = NULL;
+       struct bgp_attr_ls attr_ls;
+       uint8_t *orig_pnt = pnt;
+
+       if (json) {
+               json_data = json_object_new_object();
+               json_object_object_add(json, "data", json_data);
+               json_object_int_add(json_data, "flexAlgo", pnt_decode8(&pnt));
+               json_object_int_add(json_data, "metricType", pnt_decode8(&pnt));
+               json_object_int_add(json_data, "calcType", pnt_decode8(&pnt));
+               json_object_int_add(json_data, "priority", pnt_decode8(&pnt));
+       } else {
+               vty_out(vty, "\n%*sFlex-Algo: %u\n", indent, "",
+                       pnt_decode8(&pnt));
+               vty_out(vty, "%*sMetric-Type: %u\n", indent, "",
+                       pnt_decode8(&pnt));
+               vty_out(vty, "%*sCalc-Type: %u\n", indent, "",
+                       pnt_decode8(&pnt));
+               vty_out(vty, "%*sPriority: %u\n", indent, "",
+                       pnt_decode8(&pnt));
+       }
+
+       attr_ls.length = length - (pnt - orig_pnt);
+       attr_ls.data = pnt;
+
+       bgp_linkstate_tlv_attribute_display(vty, &attr_ls, indent, json_data);
+}
+
+static void bgp_linkstate_tlv_flexible_algorithm_prefix_metric_display(
+       struct vty *vty, uint8_t *pnt, uint16_t length, int indent,
+       json_object *json)
+{
+       json_object *json_data = NULL;
+       char buf[11];
+
+       if (json) {
+               json_data = json_object_new_object();
+               json_object_object_add(json, "data", json_data);
+               json_object_int_add(json_data, "flexAlgo", pnt_decode8(&pnt));
+               pnt = bgp_linkstate_tlv_binary_string(buf, sizeof(buf), pnt, 1);
+               json_object_string_add(json_data, "flags", buf);
+               pnt += 2; /* ignore reserved 2 bytes */
+               json_object_int_add(json_data, "metric", pnt_decode32(&pnt));
+               return;
+       }
+
+       vty_out(vty, "\n%*sFlex-Algo: %u\n", indent, "", pnt_decode8(&pnt));
+       vty_out(vty, "%*sFlags: ", indent, "");
+       pnt = bgp_linkstate_tlv_binary_display(vty, pnt, 1, json);
+       pnt += 2; /* ignore reserved 2 bytes */
+       vty_out(vty, "%*sMetric: %u\n", indent, "", pnt_decode32(&pnt));
+}
+
+static void bgp_linkstate_tlv_opaque_display(struct vty *vty, uint8_t *pnt,
+                                            uint16_t length, int indent,
+                                            json_object *json)
+{
+       uint16_t sub_type = 0, sub_length = 0;
+       json_object *json_data = NULL;
+       json_object *json_tlv = NULL;
+       uint8_t *lim = pnt + length;
+       bool ospf_tlv_header;
+       char tlv_type[6];
+       int i;
+
+
+       if (json) {
+               json_data = json_object_new_object();
+               json_object_object_add(json, "data", json_data);
+       }
+
+       /* Opaque TLV carries original IGP TLVs
+        * IS-IS TLV header is 1 byte each for the TLV type and length.
+        * OSPF TLV header is 2 bytes each for the TLV type and length
+        * but the TLV type values are far from exceeding 255.
+        * Assume TLV header format is the OSPF one if first value is 0x00.
+        */
+       ospf_tlv_header = (*pnt == 0);
+
+       for (; pnt < lim; pnt += sub_length) {
+               if (ospf_tlv_header) {
+                       sub_type = pnt_decode16(&pnt);
+                       sub_length = pnt_decode16(&pnt);
+               } else {
+                       sub_type = pnt_decode8(&pnt);
+                       sub_length = pnt_decode8(&pnt);
+               }
+
+               if (json) {
+                       snprintf(tlv_type, sizeof(tlv_type), "%u", sub_type);
+                       json_tlv = json_object_new_object();
+                       json_object_object_add(json_data, tlv_type, json_tlv);
+
+                       json_object_int_add(json_tlv, "type", sub_type);
+                       json_object_int_add(json_tlv, "length", sub_length);
+
+                       if (pnt + sub_length > lim) {
+                               json_object_string_addf(
+                                       json_tlv, "error",
+                                       "too high length received: %u",
+                                       sub_length);
+                               break;
+                       }
+
+                       bgp_linkstate_tlv_hexa_display(vty, pnt, sub_length,
+                                                      json_tlv);
+                       continue;
+               }
+
+               vty_out(vty, "\n%*sTLV type %u: 0x", indent, "", sub_type);
+
+               if (pnt + sub_length > lim) {
+                       vty_out(vty, "Bad length received: %u\n", sub_length);
+                       break;
+               }
+
+               for (i = 0; i < sub_length; i++) {
+                       if (i != 0 && i % 8 == 0)
+                               vty_out(vty, " ");
+                       vty_out(vty, "%02x", *pnt);
+               }
+       }
+       if (!json)
+               vty_out(vty, "\n");
+}
+
+static void bgp_linkstate_tlv_rtm_capability_display(struct vty *vty,
+                                                    uint8_t *pnt,
+                                                    uint16_t length,
+                                                    json_object *json)
+{
+       json_object *json_data = NULL;
+       json_object *json_array = NULL;
+       uint8_t *lim = pnt + length;
+       uint8_t tmp8;
+       char buf[11];
+       int i;
+
+       if (json) {
+               json_data = json_object_new_object();
+               json_object_object_add(json, "data", json_data);
+
+               tmp8 = pnt_decode8(&pnt);
+               snprintf(buf, sizeof(buf), "0b");
+               for (i = 7; i >= 5; i--)
+                       snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
+                                "%d", (tmp8 >> i) & 1);
+               json_object_string_add(json_data, "flags", buf);
+
+               if (length > 8) {
+                       json_array = json_object_new_array();
+                       json_object_object_add(json, "values", json_array);
+                       for (i = 0; pnt < lim; pnt++, i++) {
+                               if (i % 8 == 0) {
+                                       if (i != 0)
+                                               json_object_array_add(
+                                                       json_array,
+                                                       json_object_new_string(
+                                                               buf));
+                                       snprintf(buf, sizeof(buf), "0x");
+                               }
+                               if (i == 0)
+                                       snprintf(buf + strlen(buf),
+                                                sizeof(buf) - strlen(buf),
+                                                "%02x", tmp8 & 0x1F);
+                               else
+                                       snprintf(buf + strlen(buf),
+                                                sizeof(buf) - strlen(buf),
+                                                "%02x", *pnt);
+                       }
+                       if (strlen(buf) > 2) /* do not only contain 0x */
+                               json_object_array_add(
+                                       json_array,
+                                       json_object_new_string(buf));
+               } else {
+                       snprintf(buf, sizeof(buf), "0x");
+                       for (i = 0; pnt < lim; pnt++, i++) {
+                               if (i == 0)
+                                       snprintf(buf + strlen(buf),
+                                                sizeof(buf) - strlen(buf),
+                                                "%02x", tmp8 & 0x1F);
+                               else
+                                       snprintf(buf + strlen(buf),
+                                                sizeof(buf) - strlen(buf),
+                                                "%02x", *pnt);
+                       }
+                       json_object_string_add(json_data, "values", buf);
+               }
+               return;
+       }
+
+       tmp8 = pnt_decode8(&pnt);
+
+       vty_out(vty, "Flags: 0b");
+       for (i = 7; i >= 5; i--)
+               vty_out(vty, "%d", (tmp8 >> i) & 1);
+       vty_out(vty, " Values: 0x%02x", tmp8 & 0x1F);
+       for (; pnt < lim; pnt++)
+               vty_out(vty, "%02x", *pnt);
+       vty_out(vty, "\n");
+}
+
+static void bgp_linkstate_tlv_l2_member_attributes_display(struct vty *vty,
+                                                          uint8_t *pnt,
+                                                          uint16_t length,
+                                                          int indent,
+                                                          json_object *json)
+{
+       json_object *json_data = NULL;
+       struct bgp_attr_ls attr_ls;
+       uint8_t *orig_pnt = pnt;
+
+       if (json) {
+               json_data = json_object_new_object();
+               json_object_object_add(json, "data", json_data);
+               json_object_string_addf(json_data, "descriptor", "0x%02x",
+                                       pnt_decode32(&pnt));
+       } else
+               vty_out(vty, "Descriptor: 0x%02x\n", pnt_decode32(&pnt));
+
+       attr_ls.length = length - (pnt - orig_pnt);
+       attr_ls.data = pnt;
+
+       if (attr_ls.length == 0)
+               /* No Sub-TLV */
+               return;
+
+       bgp_linkstate_tlv_attribute_display(vty, &attr_ls, indent, json_data);
+}
+
+static void bgp_linkstate_tlv_isis_area_indentifier_display(struct vty *vty,
+                                                           uint8_t *pnt,
+                                                           uint16_t length,
+                                                           json_object *json)
+{
+       struct iso_address addr;
+
+       addr.addr_len = length;
+       memcpy(addr.area_addr, pnt, length);
+
+       if (json)
+               json_object_string_addf(json, "data", "%pIS", &addr);
+       else
+               vty_out(vty, "%pIS\n", &addr);
+}
+
+static void
+bgp_linkstate_tlv_attribute_value_display(struct vty *vty, uint8_t *pnt,
+                                         uint16_t type, uint16_t length,
+                                         int indent, json_object *json)
+{
+       if (!bgp_ls_tlv_check_size(type, length)) {
+               bgp_linkstate_tlv_hexa_display(vty, pnt, length, json);
+               return;
+       }
+
+       switch (type) {
+       case BGP_LS_TLV_SRMS_PREFERENCE:
+       case BGP_LS_TLV_IGP_METRIC:
+       case BGP_LS_TLV_PREFIX_METRIC:
+       case BGP_LS_TLV_TE_DEFAULT_METRIC:
+               bgp_linkstate_tlv_integer_display(vty, pnt, length, json);
+               break;
+       case BGP_LS_TLV_SR_ALGORITHM:
+               bgp_linkstate_tlv_integer_list_display(vty, pnt, length, 1,
+                                                      json);
+               break;
+       case BGP_LS_TLV_MULTI_TOPOLOGY_ID:
+               bgp_linkstate_tlv_integer_list_display(vty, pnt, length, 2,
+                                                      json);
+               break;
+       case BGP_LS_TLV_IGP_ROUTE_TAG:
+       case BGP_LS_TLV_SHARED_RISK_LINK_GROUP:
+       case BGP_LS_TLV_S_BFD_DISCRIMINATORS:
+       case BGP_LS_TLV_FLEXIBLE_ALGORITHM_EXCLUDE_SRLG:
+               bgp_linkstate_tlv_integer_list_display(vty, pnt, length, 4,
+                                                      json);
+               break;
+       case BGP_LS_TLV_IGP_EXTENDED_ROUTE_TAG:
+               bgp_linkstate_tlv_integer_list_display(vty, pnt, length, 8,
+                                                      json);
+               break;
+       case BGP_LS_TLV_IPV4_ROUTER_ID_OF_LOCAL_NODE:
+       case BGP_LS_TLV_IPV6_ROUTER_ID_OF_LOCAL_NODE:
+       case BGP_LS_TLV_IPV4_ROUTER_ID_OF_REMOTE_NODE:
+       case BGP_LS_TLV_IPV6_ROUTER_ID_OF_REMOTE_NODE:
+       case BGP_LS_TLV_OSPF_FORWARDING_ADDRESS:
+       case BGP_LS_TLV_SOURCE_OSPF_ROUTER_ID:
+       case BGP_LS_TLV_SOURCE_ROUTER_IDENTIFIER:
+               bgp_linkstate_tlv_ipv4_6_address_display(vty, pnt, length,
+                                                        json);
+               break;
+       case BGP_LS_TLV_NODE_NAME:
+       case BGP_LS_TLV_LINK_NAME:
+               bgp_linkstate_tlv_name_display(vty, pnt, length, json);
+               break;
+       case BGP_LS_TLV_NODE_FLAG_BITS:
+       case BGP_LS_TLV_IGP_FLAGS:
+       case BGP_LS_TLV_PREFIX_ATTRIBUTES_FLAGS:
+       case BGP_LS_TLV_MPLS_PROTOCOL_MASK:
+       case BGP_LS_TLV_LINK_PROTECTION_TYPE:
+       case BGP_LS_TLV_FLEXIBLE_ALGORITHM_DEFINITION_FLAGS:
+               bgp_linkstate_tlv_binary_display(vty, pnt, length, json);
+               break;
+       case BGP_LS_TLV_ADMINISTRATIVE_GROUP:
+       case BGP_LS_TLV_EXTENDED_ADMINISTRATIVE_GROUP:
+       case BGP_LS_TLV_FLEXIBLE_ALGORITHM_EXCLUDE_ANY_AFFINITY:
+       case BGP_LS_TLV_FLEXIBLE_ALGORITHM_INCLUDE_ANY_AFFINITY:
+       case BGP_LS_TLV_FLEXIBLE_ALGORITHM_INCLUDE_ALL_AFFINITY:
+               bgp_linkstate_tlv_hexa_display(vty, pnt, length, json);
+               break;
+       case BGP_LS_TLV_OPAQUE_NODE_ATTRIBUTE:
+       case BGP_LS_TLV_OPAQUE_LINK_ATTRIBUTE:
+       case BGP_LS_TLV_OPAQUE_PREFIX_ATTRIBUTE:
+               bgp_linkstate_tlv_opaque_display(vty, pnt, length, indent + 2,
+                                                json);
+               break;
+       case BGP_LS_TLV_NODE_MSD:
+       case BGP_LS_TLV_LINK_MSD:
+               bgp_linkstate_tlv_msd_display(vty, pnt, length, indent + 2,
+                                             json);
+               break;
+       case BGP_LS_TLV_MAXIMUM_LINK_BANDWIDTH:
+       case BGP_LS_TLV_MAX_RESERVABLE_LINK_BANDWIDTH:
+       case BGP_LS_TLV_UNIDIRECTIONAL_RESIDUAL_BANDWIDTH:
+       case BGP_LS_TLV_UNIDIRECTIONAL_AVAILABLE_BANDWIDTH:
+       case BGP_LS_TLV_UNIDIRECTIONAL_UTILIZED_BANDWIDTH:
+               bgp_linkstate_tlv_bandwidth_display(vty, pnt, length, json);
+               break;
+       case BGP_LS_TLV_UNRESERVED_BANDWIDTH:
+               bgp_linkstate_tlv_unreserved_bandwidth_display(
+                       vty, pnt, length, indent + 2, json);
+               break;
+       case BGP_LS_TLV_IS_IS_AREA_IDENTIFIER:
+               bgp_linkstate_tlv_isis_area_indentifier_display(vty, pnt,
+                                                               length, json);
+               break;
+       case BGP_LS_TLV_PREFIX_SID:
+       case BGP_LS_TLV_ADJACENCY_SID:
+       case BGP_LS_TLV_LAN_ADJACENCY_SID:
+       case BGP_LS_TLV_PEERNODE_SID:
+       case BGP_LS_TLV_PEERADJ_SID:
+       case BGP_LS_TLV_PEERSET_SID:
+               bgp_linkstate_tlv_sid_display(vty, pnt, length, type,
+                                             indent + 2, json);
+               break;
+       case BGP_LS_TLV_RANGE:
+               bgp_linkstate_tlv_range_display(vty, pnt, length, indent + 2,
+                                               json);
+               break;
+       case BGP_LS_TLV_UNIDIRECTIONAL_LINK_DELAY:
+       case BGP_LS_TLV_MIN_MAX_UNIDIRECTIONAL_LINK_DELAY:
+       case BGP_LS_TLV_UNIDIRECTIONAL_DELAY_VARIATION:
+               bgp_linkstate_tlv_delay_display(vty, pnt, length, type, json);
+               break;
+       case BGP_LS_TLV_UNIDIRECTIONAL_LINK_LOSS:
+               bgp_linkstate_tlv_unidirectional_link_loss_display(
+                       vty, pnt, length, json);
+               break;
+       case BGP_LS_TLV_APPLICATION_SPECIFIC_LINK_ATTRIBUTES:
+               bgp_linkstate_tlv_asla_display(vty, pnt, length, indent + 2,
+                                              json);
+               break;
+       case BGP_LS_TLV_SR_CAPABILITIES:
+       case BGP_LS_TLV_SR_LOCAL_BLOCK:
+               bgp_linkstate_tlv_sr_range_display(vty, pnt, length, indent + 2,
+                                                  json);
+               break;
+       case BGP_LS_TLV_SID_LABEL:
+               bgp_linkstate_tlv_sid_label_display(vty, pnt, length, json);
+               break;
+       case BGP_LS_TLV_FLEXIBLE_ALGORITHM_DEFINITION:
+               bgp_linkstate_tlv_flexible_algorithm_definition_display(
+                       vty, pnt, length, indent + 2, json);
+               break;
+       case BGP_LS_TLV_FLEXIBLE_ALGORITHM_PREFIX_METRIC:
+               bgp_linkstate_tlv_flexible_algorithm_prefix_metric_display(
+                       vty, pnt, length, indent + 2, json);
+               break;
+       case BGP_LS_TLV_GRACEFUL_LINK_SHUTDOWN_TLV:
+               if (!json)
+                       vty_out(vty, "Enabled\n"); /* TLV must have no data */
+               break;
+       case BGP_LS_TLV_L2_BUNDLE_MEMBER_ATTRIBUTES:
+               bgp_linkstate_tlv_l2_member_attributes_display(
+                       vty, pnt, length, indent + 2, json);
+               break;
+       case BGP_LS_TLV_RTM_CAPABILITY:
+               bgp_linkstate_tlv_rtm_capability_display(vty, pnt, length,
+                                                        json);
+               break;
+       default:
+               bgp_linkstate_tlv_hexa_display(vty, pnt, length, json);
+       }
+}
+
+void bgp_linkstate_tlv_attribute_display(struct vty *vty,
+                                        struct bgp_attr_ls *attr_ls,
+                                        int indent, json_object *json)
+{
+       uint8_t *pnt = attr_ls->data;
+       uint8_t *lim = pnt + attr_ls->length;
+       uint16_t length = 0;
+       uint16_t type = 0;
+       char tlv_type[6];
+       json_object *json_tlv = NULL;
+
+       for (; pnt < lim; pnt += length) {
+               type = pnt_decode16(&pnt);
+               length = pnt_decode16(&pnt);
+
+               if (json) {
+                       snprintf(tlv_type, sizeof(tlv_type), "%u", type);
+
+                       json_tlv = json_object_new_object();
+                       json_object_object_add(json, tlv_type, json_tlv);
+
+                       if (type < BGP_LS_TLV_MAX &&
+                           bgp_linkstate_tlv_infos[type].descr != NULL)
+                               json_object_string_add(
+                                       json_tlv, "description",
+                                       bgp_linkstate_tlv_infos[type].descr);
+
+                       json_object_int_add(json_tlv, "type", type);
+                       json_object_int_add(json_tlv, "length", length);
+
+                       if (pnt + length > lim) {
+                               json_object_string_addf(
+                                       json_tlv, "error",
+                                       "too high length received: %u", length);
+                               break;
+                       }
+                       if (type < BGP_LS_TLV_MAX &&
+                           bgp_linkstate_tlv_infos[type].descr != NULL &&
+                           !bgp_ls_tlv_check_size(type, length))
+                               json_object_string_addf(
+                                       json_tlv, "error",
+                                       "unexpected length received: %u",
+                                       length);
+               } else {
+                       if (type < BGP_LS_TLV_MAX &&
+                           bgp_linkstate_tlv_infos[type].descr != NULL)
+                               vty_out(vty, "%*s%s: ", indent, "",
+                                       bgp_linkstate_tlv_infos[type].descr);
+                       else
+                               vty_out(vty, "%*sTLV type %u: ", indent, "",
+                                       type);
+
+                       if (pnt + length > lim) {
+                               vty_out(vty, "Bad length received: %u\n",
+                                       length);
+                               break;
+                       }
+               }
+
+               bgp_linkstate_tlv_attribute_value_display(
+                       vty, pnt, type, length, indent, json_tlv);
+       }
+}
index dc7a43b6e1fec47caa7933ca5e1d81ec6c834e65..ad3b2570d671ea28854479a3bbded9e0ca91f6ad 100644 (file)
@@ -219,5 +219,8 @@ extern char *bgp_linkstate_nlri_prefix_display(char *buf, size_t size,
 extern void bgp_linkstate_nlri_prefix_json(json_object *json,
                                           uint16_t nlri_type, uintptr_t prefix,
                                           uint16_t len);
+extern void bgp_linkstate_tlv_attribute_display(struct vty *vty,
+                                               struct bgp_attr_ls *attr_ls,
+                                               int indent, json_object *json);
 
 #endif /* BGP_LINKSTATE_TLV_H */
index 5b9a36121e702a3ce4b1e67e8de588be3f82c32f..30d99af0f8d4a3d3d4fc3284761f8885fa04eab1 100644 (file)
@@ -10112,6 +10112,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn,
        json_object *json_peer = NULL;
        json_object *json_string = NULL;
        json_object *json_adv_to = NULL;
+       json_object *json_bgp_ls_attr = NULL;
        int first = 0;
        struct listnode *node, *nnode;
        struct peer *peer;
@@ -11045,10 +11046,28 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn,
                                "      Time until Long-lived stale route deleted: %lu\n",
                                llgr_remaining);
        }
-       if (safi == SAFI_LINKSTATE && json_paths)
-               bgp_linkstate_nlri_prefix_json(
-                       json_path, bn->rn->p.u.prefix_linkstate.nlri_type,
-                       bn->rn->p.u.prefix_linkstate.ptr, bn->rn->p.prefixlen);
+
+       if (safi == SAFI_LINKSTATE) {
+               /* BGP Link-State NLRI */
+               if (json_paths)
+                       bgp_linkstate_nlri_prefix_json(
+                               json_path, bn->rn->p.u.prefix_linkstate.nlri_type,
+                               bn->rn->p.u.prefix_linkstate.ptr, bn->rn->p.prefixlen);
+
+               /* BGP Link-State Attributes */
+               if (attr->link_state) {
+                       if (json_paths) {
+                               json_bgp_ls_attr = json_object_new_object();
+                               json_object_object_add(json_path,
+                                                      "linkStateAttributes",
+                                                      json_bgp_ls_attr);
+                       } else {
+                               vty_out(vty, "  BGP-LS attributes:\n");
+                       }
+                       bgp_linkstate_tlv_attribute_display(
+                               vty, attr->link_state, 4, json_bgp_ls_attr);
+               }
+       }
 
        /* Output some debug about internal state of the dest flags */
        if (json_paths) {
index 92f27c0120e636d891de50335c748542fc2cc04a..f1783bde95cd7b5232f00200a9ad7dd4900548f6 100644 (file)
@@ -1926,6 +1926,7 @@ struct bgp_nlri {
 #define BGP_ATTR_ENCAP                          23
 #define BGP_ATTR_IPV6_EXT_COMMUNITIES           25
 #define BGP_ATTR_AIGP                           26
+#define BGP_ATTR_LINK_STATE                     29
 #define BGP_ATTR_LARGE_COMMUNITIES              32
 #define BGP_ATTR_OTC                            35
 #define BGP_ATTR_PREFIX_SID                     40