From 8b531b110756bf8627b5716f433190a0748b5e76 Mon Sep 17 00:00:00 2001 From: Louis Scalbert Date: Wed, 11 Jan 2023 13:24:47 +0100 Subject: [PATCH] bgpd: store and send bgp link-state attributes 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 Signed-off-by: Olivier Dugeon --- bgpd/bgp_attr.c | 153 ++++- bgpd/bgp_attr.h | 9 + bgpd/bgp_linkstate_tlv.c | 1162 ++++++++++++++++++++++++++++++++++++++ bgpd/bgp_linkstate_tlv.h | 3 + bgpd/bgp_route.c | 27 +- bgpd/bgpd.h | 1 + 6 files changed, 1350 insertions(+), 5 deletions(-) diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 411522464164..cc7afbe74f64 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -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 @@ static enum bgp_attr_parse_ret bgp_attr_aigp(struct bgp_attr_parser_args *args) 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. */ diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index 961e5f122470..e637b0efbfd3 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -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; diff --git a/bgpd/bgp_linkstate_tlv.c b/bgpd/bgp_linkstate_tlv.c index 5c324382f927..5538f7a761d3 100644 --- a/bgpd/bgp_linkstate_tlv.c +++ b/bgpd/bgp_linkstate_tlv.c @@ -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); + } +} diff --git a/bgpd/bgp_linkstate_tlv.h b/bgpd/bgp_linkstate_tlv.h index dc7a43b6e1fe..ad3b2570d671 100644 --- a/bgpd/bgp_linkstate_tlv.h +++ b/bgpd/bgp_linkstate_tlv.h @@ -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 */ diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 5b9a36121e70..30d99af0f8d4 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -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) { diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 92f27c0120e6..f1783bde95cd 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -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