summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_attr.c169
-rw-r--r--bgpd/bgp_attr.h9
-rw-r--r--bgpd/bgp_btoa.c3
-rw-r--r--bgpd/bgp_debug.c2
-rw-r--r--bgpd/bgp_debug.h3
-rw-r--r--bgpd/bgp_errors.c6
-rw-r--r--bgpd/bgp_errors.h1
-rw-r--r--bgpd/bgp_linkstate.c21
-rw-r--r--bgpd/bgp_linkstate.h10
-rw-r--r--bgpd/bgp_linkstate_tlv.c1750
-rw-r--r--bgpd/bgp_linkstate_tlv.h226
-rw-r--r--bgpd/bgp_linkstate_vty.c53
-rw-r--r--bgpd/bgp_linkstate_vty.h11
-rw-r--r--bgpd/bgp_open.c30
-rw-r--r--bgpd/bgp_packet.c7
-rw-r--r--bgpd/bgp_route.c135
-rw-r--r--bgpd/bgp_table.c5
-rw-r--r--bgpd/bgp_vty.c191
-rw-r--r--bgpd/bgp_vty.h8
-rw-r--r--bgpd/bgp_zebra.c4
-rw-r--r--bgpd/bgpd.c16
-rw-r--r--bgpd/bgpd.h35
-rw-r--r--bgpd/rfapi/rfapi_import.c8
-rw-r--r--bgpd/rfapi/rfapi_monitor.c4
-rw-r--r--bgpd/subdir.am7
-rw-r--r--doc/user/bgp-linkstate.rst146
-rw-r--r--doc/user/bgp.rst16
-rw-r--r--doc/user/conf.py1
-rw-r--r--lib/command.h1
-rw-r--r--lib/iana_afi.h15
-rw-r--r--lib/prefix.c172
-rw-r--r--lib/prefix.h39
-rw-r--r--lib/table.c16
-rw-r--r--lib/zebra.h9
-rw-r--r--pbrd/pbr_zebra.c10
-rw-r--r--staticd/static_vty.c1
-rw-r--r--tests/bgpd/test_peer_attr.c2
-rw-r--r--tests/topotests/bgp_linkstate_topo1/__init__.py0
-rw-r--r--tests/topotests/bgp_linkstate_topo1/r1/bgp_injector.cfg215
-rwxr-xr-xtests/topotests/bgp_linkstate_topo1/r1/bgp_injector.py596
-rw-r--r--tests/topotests/bgp_linkstate_topo1/r1/staticd.conf1
-rw-r--r--tests/topotests/bgp_linkstate_topo1/r1/zebra.conf7
-rw-r--r--tests/topotests/bgp_linkstate_topo1/r2/bgpd.conf20
-rw-r--r--tests/topotests/bgp_linkstate_topo1/r2/linkstate.json189
-rw-r--r--tests/topotests/bgp_linkstate_topo1/r2/linkstate_detail.json325
-rw-r--r--tests/topotests/bgp_linkstate_topo1/r2/staticd.conf2
-rw-r--r--tests/topotests/bgp_linkstate_topo1/r2/zebra.conf11
-rw-r--r--tests/topotests/bgp_linkstate_topo1/r3/bgpd.conf14
l---------tests/topotests/bgp_linkstate_topo1/r3/linkstate.json1
l---------tests/topotests/bgp_linkstate_topo1/r3/linkstate_detail.json1
-rw-r--r--tests/topotests/bgp_linkstate_topo1/r3/staticd.conf1
-rw-r--r--tests/topotests/bgp_linkstate_topo1/r3/zebra.conf7
-rw-r--r--tests/topotests/bgp_linkstate_topo1/test_bgp_linkstate_topo1.py143
-rw-r--r--vtysh/vtysh.c25
-rw-r--r--yang/frr-bgp-common-multiprotocol.yang10
-rw-r--r--yang/frr-bgp.yang31
-rw-r--r--yang/frr-routing.yang7
-rw-r--r--zebra/connected.c2
-rw-r--r--zebra/router-id.c2
-rw-r--r--zebra/zapi_msg.c1
-rw-r--r--zebra/zebra_nhg.c2
61 files changed, 4692 insertions, 63 deletions
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index 7916233444..cc7afbe74f 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -40,8 +40,12 @@
#endif
#include "bgp_evpn.h"
#include "bgp_flowspec_private.h"
+#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"},
@@ -65,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"},
@@ -199,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;
@@ -716,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;
@@ -805,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;
}
@@ -870,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;
}
@@ -1030,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);
@@ -1248,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. */
@@ -1411,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,
@@ -1497,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;
@@ -3290,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)
{
@@ -3747,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;
@@ -4003,6 +4145,8 @@ size_t bgp_packet_mpattr_start(struct stream *s, struct peer *peer, afi_t afi,
switch (nh_afi) {
case AFI_IP:
switch (safi) {
+ case SAFI_LINKSTATE:
+ case SAFI_LINKSTATE_VPN:
case SAFI_UNICAST:
case SAFI_MULTICAST:
case SAFI_LABELED_UNICAST:
@@ -4036,6 +4180,8 @@ size_t bgp_packet_mpattr_start(struct stream *s, struct peer *peer, afi_t afi,
break;
case AFI_IP6:
switch (safi) {
+ case SAFI_LINKSTATE:
+ case SAFI_LINKSTATE_VPN:
case SAFI_UNICAST:
case SAFI_MULTICAST:
case SAFI_LABELED_UNICAST:
@@ -4086,8 +4232,9 @@ size_t bgp_packet_mpattr_start(struct stream *s, struct peer *peer, afi_t afi,
break;
}
break;
+ case AFI_LINKSTATE:
case AFI_L2VPN:
- if (safi != SAFI_FLOWSPEC)
+ if (nh_afi == AFI_L2VPN && safi != SAFI_FLOWSPEC)
flog_err(
EC_BGP_ATTR_NH_SEND_LEN,
"Bad nexthop when sending to %s, AFI %u SAFI %u nhlen %d",
@@ -4138,6 +4285,12 @@ void bgp_packet_mpattr_prefix(struct stream *s, afi_t afi, safi_t safi,
stream_put_labeled_prefix(s, p, label, addpath_capable,
addpath_tx_id);
break;
+ case SAFI_LINKSTATE:
+ bgp_nlri_encode_linkstate(s, p);
+ break;
+ case SAFI_LINKSTATE_VPN:
+ /* not yet supported */
+ break;
case SAFI_FLOWSPEC:
stream_putc(s, p->u.prefix_flowspec.prefixlen);
stream_put(s, (const void *)p->u.prefix_flowspec.ptr,
@@ -4164,6 +4317,8 @@ size_t bgp_packet_mpattr_prefix_size(afi_t afi, safi_t safi,
case SAFI_MAX:
assert(!"Attempting to figure size for a SAFI_UNSPEC/SAFI_MAX this is a DEV ESCAPE");
break;
+ case SAFI_LINKSTATE:
+ case SAFI_LINKSTATE_VPN:
case SAFI_UNICAST:
case SAFI_MULTICAST:
break;
@@ -4826,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);
@@ -4930,6 +5093,7 @@ void bgp_attr_init(void)
transit_init();
encap_init();
srv6_init();
+ link_state_init();
}
void bgp_attr_finish(void)
@@ -4943,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 961e5f1224..e637b0efbf 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_btoa.c b/bgpd/bgp_btoa.c
index 8e27d9769f..c4e866c42e 100644
--- a/bgpd/bgp_btoa.c
+++ b/bgpd/bgp_btoa.c
@@ -177,6 +177,9 @@ int main(int argc, char **argv)
case AFI_IP6:
printf("/AFI_IP6\n");
break;
+ case AFI_LINKSTATE:
+ printf("/AFI_LINKSTATE\n");
+ break;
default:
printf("/UNKNOWN %d", subtype);
break;
diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c
index 782245e512..0ff8bdcbbe 100644
--- a/bgpd/bgp_debug.c
+++ b/bgpd/bgp_debug.c
@@ -51,6 +51,7 @@ unsigned long conf_bgp_debug_nht;
unsigned long conf_bgp_debug_update_groups;
unsigned long conf_bgp_debug_vpn;
unsigned long conf_bgp_debug_flowspec;
+unsigned long conf_bgp_debug_linkstate;
unsigned long conf_bgp_debug_labelpool;
unsigned long conf_bgp_debug_pbr;
unsigned long conf_bgp_debug_graceful_restart;
@@ -72,6 +73,7 @@ unsigned long term_bgp_debug_nht;
unsigned long term_bgp_debug_update_groups;
unsigned long term_bgp_debug_vpn;
unsigned long term_bgp_debug_flowspec;
+unsigned long term_bgp_debug_linkstate;
unsigned long term_bgp_debug_labelpool;
unsigned long term_bgp_debug_pbr;
unsigned long term_bgp_debug_graceful_restart;
diff --git a/bgpd/bgp_debug.h b/bgpd/bgp_debug.h
index 118325e0a3..38beebf872 100644
--- a/bgpd/bgp_debug.h
+++ b/bgpd/bgp_debug.h
@@ -60,6 +60,7 @@ extern unsigned long conf_bgp_debug_nht;
extern unsigned long conf_bgp_debug_update_groups;
extern unsigned long conf_bgp_debug_vpn;
extern unsigned long conf_bgp_debug_flowspec;
+extern unsigned long conf_bgp_debug_linkstate;
extern unsigned long conf_bgp_debug_labelpool;
extern unsigned long conf_bgp_debug_pbr;
extern unsigned long conf_bgp_debug_graceful_restart;
@@ -79,6 +80,7 @@ extern unsigned long term_bgp_debug_nht;
extern unsigned long term_bgp_debug_update_groups;
extern unsigned long term_bgp_debug_vpn;
extern unsigned long term_bgp_debug_flowspec;
+extern unsigned long term_bgp_debug_linkstate;
extern unsigned long term_bgp_debug_labelpool;
extern unsigned long term_bgp_debug_pbr;
extern unsigned long term_bgp_debug_graceful_restart;
@@ -118,6 +120,7 @@ struct bgp_debug_filter {
#define BGP_DEBUG_VPN_LEAK_RMAP_EVENT 0x04
#define BGP_DEBUG_VPN_LEAK_LABEL 0x08
#define BGP_DEBUG_FLOWSPEC 0x01
+#define BGP_DEBUG_LINKSTATE 0x01
#define BGP_DEBUG_LABELPOOL 0x01
#define BGP_DEBUG_PBR 0x01
#define BGP_DEBUG_PBR_ERROR 0x02
diff --git a/bgpd/bgp_errors.c b/bgpd/bgp_errors.c
index cfcefed996..2f9f16f800 100644
--- a/bgpd/bgp_errors.c
+++ b/bgpd/bgp_errors.c
@@ -450,6 +450,12 @@ static struct log_ref ferr_bgp_err[] = {
.suggestion = "Gather log files from the router and open an issue, Restart FRR"
},
{
+ .code = EC_BGP_LINKSTATE_PACKET,
+ .title = "BGP Link-State packet processing error",
+ .description = "The BGP Link-State subsystem has detected a error in the send or receive of a packet",
+ .suggestion = "Gather log files from both sides of the peering relationship and open an issue"
+ },
+ {
.code = EC_BGP_DOPPELGANGER_CONFIG,
.title = "BGP has detected a configuration overwrite during peer collision resolution",
.description = "As part of BGP startup, the peer and ourselves can start connections to each other at the same time. During this process BGP received additional configuration, but it was only applied to one of the two nascent connections. Depending on the result of collision detection and resolution this configuration might be lost. To remedy this, after performing collision detection and resolution the peer session has been reset in order to apply the new configuration.",
diff --git a/bgpd/bgp_errors.h b/bgpd/bgp_errors.h
index 4567f87835..bc6b5a4a3d 100644
--- a/bgpd/bgp_errors.h
+++ b/bgpd/bgp_errors.h
@@ -59,6 +59,7 @@ enum bgp_log_refs {
EC_BGP_EVPN_INSTANCE_MISMATCH,
EC_BGP_FLOWSPEC_PACKET,
EC_BGP_FLOWSPEC_INSTALLATION,
+ EC_BGP_LINKSTATE_PACKET,
EC_BGP_ASPATH_FEWER_HOPS,
EC_BGP_DEFUNCT_SNPA_LEN,
EC_BGP_MISSING_ATTRIBUTE,
diff --git a/bgpd/bgp_linkstate.c b/bgpd/bgp_linkstate.c
new file mode 100644
index 0000000000..f76c68ca5d
--- /dev/null
+++ b/bgpd/bgp_linkstate.c
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* BGP Link-State
+ * Copyright 2023 6WIND S.A.
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "lib_errors.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_errors.h"
+#include "bgpd/bgp_linkstate.h"
+#include "bgpd/bgp_linkstate_tlv.h"
+
+void bgp_linkstate_init(void)
+{
+ prefix_set_linkstate_display_hook(bgp_linkstate_nlri_prefix_display);
+}
diff --git a/bgpd/bgp_linkstate.h b/bgpd/bgp_linkstate.h
new file mode 100644
index 0000000000..c8d4d23f16
--- /dev/null
+++ b/bgpd/bgp_linkstate.h
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* BGP Link-State header
+ * Copyright 2023 6WIND S.A.
+ */
+
+#ifndef _FRR_BGP_LINKSTATE_H
+#define _FRR_BGP_LINKSTATE_H
+
+void bgp_linkstate_init(void);
+#endif /* _FRR_BGP_LINKSTATE_H */
diff --git a/bgpd/bgp_linkstate_tlv.c b/bgpd/bgp_linkstate_tlv.c
new file mode 100644
index 0000000000..5538f7a761
--- /dev/null
+++ b/bgpd/bgp_linkstate_tlv.c
@@ -0,0 +1,1750 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* BGP Link-State TLV Serializer/Deserializer
+ * Copyright 2023 6WIND S.A.
+ */
+
+#include <zebra.h>
+
+#include "iso.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_errors.h"
+#include "bgpd/bgp_linkstate_tlv.h"
+
+
+static bool bgp_linkstate_nlri_value_display(char *buf, size_t size,
+ uint8_t *pnt, uint16_t nlri_type,
+ uint16_t type, uint16_t length,
+ bool first, json_object *json);
+
+struct bgp_linkstate_tlv_info {
+ const char *descr;
+ uint8_t min_size;
+ uint16_t max_size;
+ uint8_t multiple;
+};
+
+#define UNDEF_MIN_SZ 0xFF
+#define MAX_SZ 0xFFFF
+#define UNDEF_MULTPL 1
+
+/* clang-format off */
+struct bgp_linkstate_tlv_info bgp_linkstate_tlv_infos[BGP_LS_TLV_MAX] = {
+ /* NLRI TLV */
+ [BGP_LS_TLV_LOCAL_NODE_DESCRIPTORS] = {"Local Node Descriptors", 1, MAX_SZ, UNDEF_MULTPL},
+ [BGP_LS_TLV_REMOTE_NODE_DESCRIPTORS] = {"Remote Node Descriptors", 1, MAX_SZ, UNDEF_MULTPL},
+ [BGP_LS_TLV_LINK_LOCAL_REMOTE_IDENTIFIERS] = {"Link Local/Remote Identifiers", 2, 2, UNDEF_MULTPL},
+ [BGP_LS_TLV_IPV4_INTERFACE_ADDRESS] = {"IPv4 interface address", 4, 4, UNDEF_MULTPL},
+ [BGP_LS_TLV_IPV4_NEIGHBOR_ADDRESS] = {"IPv4 neighbor address", 4, 4, UNDEF_MULTPL},
+ [BGP_LS_TLV_IPV6_INTERFACE_ADDRESS] = {"IPv6 interface address", 16, 16, UNDEF_MULTPL},
+ [BGP_LS_TLV_IPV6_NEIGHBOR_ADDRESS] = {"IPv6 neighbor address", 16, 16, UNDEF_MULTPL},
+ [BGP_LS_TLV_OSPF_ROUTE_TYPE] = {"OSPF Route Type", 1, 1, UNDEF_MULTPL},
+ [BGP_LS_TLV_IP_REACHABILITY_INFORMATION] = {"IP Reachability Information", 2, 17, UNDEF_MULTPL},
+ [BGP_LS_TLV_AUTONOMOUS_SYSTEM] = {"Autonomous System", 4, 4, UNDEF_MULTPL},
+ [BGP_LS_TLV_BGP_LS_IDENTIFIER] = {"BGP-LS Identifier", 4, 4, UNDEF_MULTPL},
+ [BGP_LS_TLV_OSPF_AREA_ID] = {"OSPF Area-ID", 4, 4, UNDEF_MULTPL},
+ [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 */
+
+/* Return the TLV length is valid for the TLV type */
+static bool bgp_ls_tlv_check_size(enum bgp_linkstate_tlv type, size_t length)
+{
+ if (type > BGP_LS_TLV_MAX ||
+ bgp_linkstate_tlv_infos[type].descr == NULL)
+ /* TLV type is not defined. Cannot check size */
+ return false;
+
+ if (bgp_linkstate_tlv_infos[type].min_size > length)
+ return false;
+ if (bgp_linkstate_tlv_infos[type].max_size < length)
+ return false;
+ if (length % bgp_linkstate_tlv_infos[type].multiple != 0)
+ return false;
+
+ return true;
+}
+
+static uint8_t pnt_decode8(uint8_t **pnt)
+{
+ uint8_t data;
+
+ data = **pnt;
+ *pnt += 1;
+ return data;
+}
+
+static uint16_t pnt_decode16(uint8_t **pnt)
+{
+ uint16_t data;
+
+ *pnt = ptr_get_be16(*pnt, &data);
+
+ 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;
+
+ *pnt = (uint8_t *)ptr_get_be32(*pnt, &data);
+
+ return data;
+}
+
+static uint64_t pnt_decode64(uint8_t **pnt)
+{
+ uint64_t data;
+
+ *pnt = (uint8_t *)ptr_get_be64(*pnt, &data);
+
+ return data;
+}
+
+static const char *bgp_ls_print_nlri_proto(enum bgp_ls_nlri_proto proto)
+{
+ switch (proto) {
+ case BGP_LS_NLRI_PROTO_ID_IS_IS_LEVEL_1:
+ return "ISIS-L1";
+ case BGP_LS_NLRI_PROTO_ID_IS_IS_LEVEL_2:
+ return "ISIS-L2";
+ case BGP_LS_NLRI_PROTO_ID_OSPF:
+ return "OSPFv2";
+ case BGP_LS_NLRI_PROTO_ID_DIRECT:
+ return "Direct";
+ case BGP_LS_NLRI_PROTO_ID_STATIC:
+ return "Static";
+ case BGP_LS_NLRI_PROTO_ID_OSPFv3:
+ return "OSPFv3";
+ case BGP_LS_NLRI_PROTO_ID_UNKNOWN:
+ return "Unknown";
+ }
+ return "Unknown";
+}
+
+int bgp_nlri_parse_linkstate(struct peer *peer, struct attr *attr,
+ struct bgp_nlri *packet, int withdraw)
+{
+ uint8_t *pnt;
+ uint8_t *lim;
+ afi_t afi;
+ safi_t safi;
+ uint16_t length = 0;
+ struct prefix p;
+
+ /* Start processing the NLRI - there may be multiple in the MP_REACH */
+ pnt = packet->nlri;
+ lim = pnt + packet->length;
+ afi = packet->afi;
+ safi = packet->safi;
+
+ for (; pnt < lim; pnt += length) {
+ /* Clear prefix structure. */
+ memset(&p, 0, sizeof(p));
+
+ /* All linkstate NLRI begin with NRLI type and length. */
+ if (pnt + 4 > lim)
+ return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW;
+
+ p.u.prefix_linkstate.nlri_type = pnt_decode16(&pnt);
+ length = pnt_decode16(&pnt);
+ /* When packet overflow occur return immediately. */
+ if (pnt + length > lim) {
+ flog_err(
+ EC_BGP_LINKSTATE_PACKET,
+ "Link-State NLRI length inconsistent (size %u seen)",
+ length);
+ return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW;
+ }
+ p.family = AF_LINKSTATE;
+
+ p.u.prefix_linkstate.ptr = (uintptr_t)pnt;
+ p.prefixlen = length;
+
+ if (BGP_DEBUG(linkstate, LINKSTATE)) {
+ zlog_debug("LS Rx %s %s %pFX",
+ withdraw ? "Withdraw" : "Update",
+ afi2str(afi), &p);
+ }
+
+ /* Process the route. */
+ if (withdraw)
+ bgp_withdraw(peer, &p, 0, afi, safi, ZEBRA_ROUTE_BGP,
+ BGP_ROUTE_NORMAL, NULL, NULL, 0, NULL);
+ else
+ bgp_update(peer, &p, 0, attr, afi, safi,
+ ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL,
+ NULL, 0, 0, NULL);
+ }
+ return BGP_NLRI_PARSE_OK;
+}
+
+/*
+ * Encode Link-State prefix in Update (MP_REACH)
+ */
+void bgp_nlri_encode_linkstate(struct stream *s, const struct prefix *p)
+{
+ /* NLRI type */
+ stream_putw(s, p->u.prefix_linkstate.nlri_type);
+
+ /* Size */
+ stream_putw(s, p->prefixlen);
+
+ stream_put(s, (const void *)p->u.prefix_linkstate.ptr, p->prefixlen);
+}
+
+static size_t bgp_linkstate_nlri_hexa_display(char *buf, size_t size,
+ uint8_t *pnt, uint16_t type,
+ uint16_t length, bool first,
+ json_object *json)
+{
+ json_object *json_array = NULL;
+ uint8_t *lim = pnt + length;
+ char json_buf[19];
+ int i;
+
+ if (json) {
+ snprintf(json_buf, sizeof(json_buf), "%u", type);
+ json_array = json_object_new_array();
+ json_object_object_add(json, json_buf, 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(
+ json_buf));
+ snprintf(json_buf, sizeof(buf), "0x");
+ }
+ snprintf(json_buf + strlen(json_buf),
+ sizeof(json_buf) - strlen(json_buf), "%02x",
+ *pnt);
+ }
+ if (strlen(json_buf) > 2) /* do not only contain 0x */
+ json_object_array_add(json_array,
+ json_object_new_string(json_buf));
+
+ return size;
+ }
+
+ snprintf(buf, size, "%s%u:", first ? "" : " ", type);
+ size -= strlen(buf);
+ buf += strlen(buf);
+
+ snprintf(buf, size, "0x");
+ size -= strlen(buf);
+ buf += strlen(buf);
+
+ for (i = 0; pnt < lim; pnt++, i++) {
+ snprintf(buf, size, "%02x", *pnt);
+ size -= strlen(buf);
+ buf += strlen(buf);
+ }
+
+ return size;
+}
+
+static void bgp_linkstate_nlri_mtid_display(char *buf, size_t size,
+ uint8_t *pnt, uint16_t type,
+ uint16_t length, bool first,
+ json_object *json)
+{
+ json_object *json_array = NULL;
+
+ if (json) {
+ json_array = json_object_new_array();
+ json_object_object_add(json, "mtID", json_array);
+ for (int i = 0; i < (length / 2); i++) {
+ json_object_array_add(
+ json_array,
+ json_object_new_int(pnt_decode16(&pnt)));
+ }
+ return;
+ }
+
+ for (int i = 0; i < (length / 2); i++) {
+ if (i == 0)
+ snprintf(buf, size, "%sMT:%hu", first ? "" : " ",
+ pnt_decode16(&pnt));
+ else
+ snprintf(buf, size, ",%hu", pnt_decode16(&pnt));
+ size -= strlen(buf);
+ buf += strlen(buf);
+ }
+}
+
+static bool bgp_linkstate_nlri_node_descriptor_display(
+ char *buf, size_t size, uint8_t *pnt, uint16_t nlri_type, uint16_t type,
+ uint16_t length, bool first, json_object *json)
+{
+ json_object *json_node = NULL;
+ bool sub_first = true;
+ uint8_t *lim = pnt + length;
+ uint16_t sub_type, sub_length;
+
+ if (json) {
+ json_node = json_object_new_object();
+ if (type == BGP_LS_TLV_LOCAL_NODE_DESCRIPTORS)
+ json_object_object_add(json, "localNode", json_node);
+ else
+ json_object_object_add(json, "remoteNode", json_node);
+ } else {
+ if (type == BGP_LS_TLV_LOCAL_NODE_DESCRIPTORS)
+ snprintf(buf, size, "%sLocal {", first ? "" : " ");
+ else
+ snprintf(buf, size, "%sRemote {", first ? "" : " ");
+ size -= strlen(buf);
+ buf += strlen(buf);
+ }
+
+ for (; pnt < lim; pnt += sub_length) {
+ sub_type = pnt_decode16(&pnt);
+ sub_length = pnt_decode16(&pnt);
+
+ if (pnt + sub_length > lim)
+ /* bad length */
+ return false;
+
+ bgp_linkstate_nlri_value_display(buf, size, pnt, nlri_type,
+ sub_type, sub_length,
+ sub_first, json_node);
+
+ if (!json) {
+ size -= strlen(buf);
+ buf += strlen(buf);
+ sub_first = false;
+ }
+ }
+
+ if (!json)
+ snprintf(buf, size, "}");
+
+ return true;
+}
+
+static bool bgp_linkstate_nlri_value_display(char *buf, size_t size,
+ uint8_t *pnt, uint16_t nlri_type,
+ uint16_t type, uint16_t length,
+ bool first, json_object *json)
+{
+ struct in_addr ipv4 = {0};
+ struct in6_addr ipv6 = {0};
+ uint8_t mask_length;
+
+ if (!bgp_ls_tlv_check_size(type, length) && !json) {
+ bgp_linkstate_nlri_hexa_display(buf, size, pnt, type, length,
+ first, json);
+ return true;
+ }
+
+ switch (type) {
+ case BGP_LS_TLV_LOCAL_NODE_DESCRIPTORS:
+ case BGP_LS_TLV_REMOTE_NODE_DESCRIPTORS:
+ return bgp_linkstate_nlri_node_descriptor_display(
+ buf, size, pnt, nlri_type, type, length, first, json);
+ case BGP_LS_TLV_AUTONOMOUS_SYSTEM:
+ if (json)
+ json_object_int_add(json, "as", pnt_decode32(&pnt));
+ else
+ snprintf(buf, size, "%sAS:%u", first ? "" : " ",
+ pnt_decode32(&pnt));
+ break;
+ case BGP_LS_TLV_BGP_LS_IDENTIFIER:
+ if (json)
+ json_object_int_add(json, "identifier",
+ pnt_decode32(&pnt));
+ else
+ snprintf(buf, size, "%sID:%u", first ? "" : " ",
+ pnt_decode32(&pnt));
+ break;
+ case BGP_LS_TLV_OSPF_AREA_ID:
+ if (json)
+ json_object_int_add(json, "area", pnt_decode32(&pnt));
+ else
+ snprintf(buf, size, "%sArea:%u", first ? "" : " ",
+ pnt_decode32(&pnt));
+ break;
+ case BGP_LS_TLV_IGP_ROUTER_ID:
+ switch (length) {
+ case BGP_LS_TLV_IGP_ROUTER_ID_ISIS_NON_PSEUDOWIRE_SIZE:
+ if (json)
+ json_object_string_addf(json, "routerID",
+ "%pSY", pnt);
+ else
+ snprintfrr(buf, size, "%sRtr:%pSY",
+ first ? "" : " ", pnt);
+ break;
+ case BGP_LS_TLV_IGP_ROUTER_ID_ISIS_PSEUDOWIRE_SIZE:
+ if (json)
+ json_object_string_addf(json, "routerID",
+ "%pPN", pnt);
+ else
+ snprintfrr(buf, size, "%sRtr:%pPN",
+ first ? "" : " ", pnt);
+ break;
+ case BGP_LS_TLV_IGP_ROUTER_ID_OSPF_NON_PSEUDOWIRE_SIZE:
+ if (json)
+ json_object_string_addf(json, "routerID",
+ "%pI4",
+ (in_addr_t *)pnt);
+ else
+ snprintfrr(buf, size, "%sRtr:%pI4",
+ first ? "" : " ", (in_addr_t *)pnt);
+ break;
+ case BGP_LS_TLV_IGP_ROUTER_ID_OSPF_PSEUDOWIRE_SIZE:
+ if (json)
+ json_object_string_addf(json, "routerID",
+ "%pI4:%pI4",
+ (in_addr_t *)pnt,
+ ((in_addr_t *)pnt + 1));
+ else
+ snprintfrr(buf, size, "%sRtr:%pI4:%pI4",
+ first ? "" : " ", (in_addr_t *)pnt,
+ ((in_addr_t *)pnt + 1));
+ break;
+ default:
+ bgp_linkstate_nlri_hexa_display(buf, size, pnt, type,
+ length, first, json);
+ }
+ break;
+ case BGP_LS_TLV_LINK_LOCAL_REMOTE_IDENTIFIERS:
+ if (json)
+ json_object_int_add(json, "localRemoteID",
+ pnt_decode16(&pnt));
+ else
+ snprintf(buf, size, "%sLocal/remote:%hu",
+ first ? "" : " ", pnt_decode16(&pnt));
+ break;
+ case BGP_LS_TLV_IPV4_INTERFACE_ADDRESS:
+ if (json)
+ json_object_string_addf(json, "interfaceIPv4", "%pI4",
+ (in_addr_t *)pnt);
+ else
+ snprintfrr(buf, size, "%sIPv4:%pI4", first ? "" : " ",
+ (in_addr_t *)pnt);
+ break;
+ case BGP_LS_TLV_IPV4_NEIGHBOR_ADDRESS:
+ if (json)
+ json_object_string_addf(json, "neighborIPv4", "%pI4",
+ (in_addr_t *)pnt);
+ else
+ snprintfrr(buf, size, "%sNeigh-IPv4:%pI4",
+ first ? "" : " ", (in_addr_t *)pnt);
+ break;
+ case BGP_LS_TLV_IPV6_INTERFACE_ADDRESS:
+ if (json)
+ json_object_string_addf(json, "interfaceIPv6", "%pI6",
+ (struct in6_addr *)pnt);
+ else
+ snprintfrr(buf, size, "%sIPv6:%pI6", first ? "" : " ",
+ (struct in6_addr *)pnt);
+ break;
+ case BGP_LS_TLV_IPV6_NEIGHBOR_ADDRESS:
+ if (json)
+ json_object_string_addf(json, "neighborIPv6", "%pI6",
+ (struct in6_addr *)pnt);
+ else
+ snprintfrr(buf, size, "%sNeigh-IPv6:%pI6",
+ first ? "" : " ", (struct in6_addr *)pnt);
+ break;
+ case BGP_LS_TLV_MULTI_TOPOLOGY_ID:
+ bgp_linkstate_nlri_mtid_display(buf, size, pnt, type, length,
+ first, json);
+ break;
+ case BGP_LS_TLV_OSPF_ROUTE_TYPE:
+ if (json)
+ json_object_int_add(json, "ospfRouteType",
+ pnt_decode8(&pnt));
+ else
+ snprintf(buf, size, "%sOSPF-Route-Type:%u",
+ first ? "" : " ", pnt_decode8(&pnt));
+ break;
+ case BGP_LS_TLV_IP_REACHABILITY_INFORMATION:
+ mask_length = pnt_decode8(&pnt);
+ if (nlri_type == BGP_LINKSTATE_PREFIX4) {
+ memcpy(&ipv4.s_addr, pnt, length - sizeof(mask_length));
+ if (json)
+ json_object_string_addf(json, "ipReachability",
+ "%pI4/%u", &ipv4,
+ mask_length);
+ else
+ snprintfrr(buf, size, "%sIPv4:%pI4/%u",
+ first ? "" : " ", &ipv4,
+ mask_length);
+ } else if (nlri_type == BGP_LINKSTATE_PREFIX6) {
+ memcpy(&ipv6, pnt, length - sizeof(mask_length));
+ if (json)
+ json_object_string_addf(json, "ipReachability",
+ "%pI6/%u", &ipv6,
+ mask_length);
+ else
+ snprintfrr(buf, size, "%sIPv6:%pI6/%u",
+ first ? "" : " ", &ipv6,
+ mask_length);
+ } else
+ bgp_linkstate_nlri_hexa_display(buf, size, pnt, type,
+ length, first, json);
+
+ break;
+ default:
+ bgp_linkstate_nlri_hexa_display(buf, size, pnt, type, length,
+ first, json);
+ }
+
+ return true;
+}
+
+char *bgp_linkstate_nlri_prefix_display(char *buf, size_t size,
+ uint16_t nlri_type, uintptr_t ptr,
+ uint16_t len)
+{
+ uint8_t *pnt = (uint8_t *)ptr;
+ uint8_t *lim = pnt + len;
+ uint16_t type, length;
+ char *cbuf = buf, *cbuf2;
+ uint8_t proto;
+ bool ret;
+ bool first = true;
+
+ proto = pnt_decode8(&pnt);
+
+ snprintfrr(buf, size, "%s %s ID:0x%" PRIx64 " {",
+ bgp_linkstate_nlri_type_2str(nlri_type),
+ bgp_ls_print_nlri_proto(proto), pnt_decode64(&pnt));
+ size -= strlen(buf);
+ buf += strlen(buf);
+
+ cbuf2 = buf;
+
+ for (; pnt < lim; pnt += length) {
+ type = pnt_decode16(&pnt);
+ length = pnt_decode16(&pnt);
+
+ if (pnt + length > lim) {
+ /* bad length */
+ snprintf(cbuf2, size, "Bad format}");
+ return cbuf;
+ }
+
+ ret = bgp_linkstate_nlri_value_display(
+ buf, size, pnt, nlri_type, type, length, first, NULL);
+
+ if (!ret) {
+ /* bad length */
+ snprintf(cbuf2, size, "Bad format}");
+ return cbuf;
+ }
+
+ size -= strlen(buf);
+ buf += strlen(buf);
+ first = false;
+ }
+
+ snprintf(buf, size, "}");
+
+ return cbuf;
+}
+
+void bgp_linkstate_nlri_prefix_json(json_object *json, uint16_t nlri_type,
+ uintptr_t ptr, uint16_t len)
+{
+ json_object *json_nlri = json_object_new_object();
+ uint8_t *pnt = (uint8_t *)ptr;
+ uint8_t *lim = pnt + len;
+ uint16_t type, length;
+ uint8_t proto;
+ bool ret;
+
+ proto = pnt_decode8(&pnt);
+
+ json_object_object_add(json, "linkStateNLRI", json_nlri);
+ json_object_string_add(json_nlri, "nlriType",
+ bgp_linkstate_nlri_type_2str(nlri_type));
+ json_object_string_add(json_nlri, "protocol",
+ bgp_ls_print_nlri_proto(proto));
+ json_object_string_addf(json_nlri, "identifier", "0x%" PRIx64,
+ pnt_decode64(&pnt));
+
+ for (; pnt < lim; pnt += length) {
+ type = pnt_decode16(&pnt);
+ length = pnt_decode16(&pnt);
+
+ if (pnt + length > lim)
+ /* bad length */
+ return;
+
+ ret = bgp_linkstate_nlri_value_display(NULL, 0, pnt, nlri_type,
+ type, length, false,
+ json_nlri);
+
+ if (!ret)
+ /* bad length */
+ 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
new file mode 100644
index 0000000000..ad3b2570d6
--- /dev/null
+++ b/bgpd/bgp_linkstate_tlv.h
@@ -0,0 +1,226 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* BGP Link-State TLV Serializer/Deserializer header
+ * Copyright 2023 6WIND S.A.
+ */
+
+#ifndef BGP_LINKSTATE_TLV_H
+#define BGP_LINKSTATE_TLV_H
+
+/* RFC7752 Link-State NLRI Protocol-ID values
+ * +-------------+----------------------------------+
+ * | Protocol-ID | NLRI information source protocol |
+ * +-------------+----------------------------------+
+ * | 1 | IS-IS Level 1 |
+ * | 2 | IS-IS Level 2 |
+ * | 3 | OSPFv2 |
+ * | 4 | Direct |
+ * | 5 | Static configuration |
+ * | 6 | OSPFv3 |
+ * +-------------+----------------------------------+
+ */
+
+enum bgp_ls_nlri_proto {
+ BGP_LS_NLRI_PROTO_ID_UNKNOWN = 0,
+ BGP_LS_NLRI_PROTO_ID_IS_IS_LEVEL_1 = 1,
+ BGP_LS_NLRI_PROTO_ID_IS_IS_LEVEL_2 = 2,
+ BGP_LS_NLRI_PROTO_ID_OSPF = 3,
+ BGP_LS_NLRI_PROTO_ID_DIRECT = 4,
+ BGP_LS_NLRI_PROTO_ID_STATIC = 5,
+ BGP_LS_NLRI_PROTO_ID_OSPFv3 = 6,
+};
+
+/*
+ * List of BGP Link-State TLVs extracted from
+ * https://www.iana.org/assignments/bgp-ls-parameters/bgp-ls-parameters.xhtml#node-descriptor-link-descriptor-prefix-descriptor-attribute-tlv
+ *
+ * Retrieved on 2023-01-03
+ *
+ * The following bash command was used to convert the list:
+ * sed -e 's| (.\+)||g' tmp \
+ * | awk -F'\t' '($1 ~ /^[0-9]+$/) {gsub(/(\/|-| |\.)/,"_",$2); printf
+ * "\tBGP_LS_TLV_"toupper($2)" = "$1", \/\* "$4" \*\/\n"}' \
+ * | grep -v UNASSIGNED \
+ * | sed -e 's/\[//g;s/\]//g'
+ *
+ */
+
+enum bgp_linkstate_tlv {
+ BGP_LS_TLV_LOCAL_NODE_DESCRIPTORS = 256, /* RFC7752, Section 3.2.1.2 */
+ BGP_LS_TLV_REMOTE_NODE_DESCRIPTORS = 257, /* RFC7752, Section 3.2.1.3 */
+ BGP_LS_TLV_LINK_LOCAL_REMOTE_IDENTIFIERS =
+ 258, /* RFC5307, Section 1.1 */
+ BGP_LS_TLV_IPV4_INTERFACE_ADDRESS = 259, /* RFC5305, Section 3.2 */
+ BGP_LS_TLV_IPV4_NEIGHBOR_ADDRESS = 260, /* RFC5305, Section 3.3 */
+ BGP_LS_TLV_IPV6_INTERFACE_ADDRESS = 261, /* RFC6119, Section 4.2 */
+ BGP_LS_TLV_IPV6_NEIGHBOR_ADDRESS = 262, /* RFC6119, Section 4.3 */
+ BGP_LS_TLV_MULTI_TOPOLOGY_ID = 263, /* RFC7752, Section 3.2.1.5 */
+ BGP_LS_TLV_OSPF_ROUTE_TYPE = 264, /* RFC7752, Section 3.2.3 */
+ BGP_LS_TLV_IP_REACHABILITY_INFORMATION =
+ 265, /* RFC7752, Section 3.2.3 */
+ BGP_LS_TLV_NODE_MSD = 266, /* RFC8814 */
+ BGP_LS_TLV_LINK_MSD = 267, /* RFC8814 */
+ BGP_LS_TLV_AUTONOMOUS_SYSTEM = 512, /* RFC7752, Section 3.2.1.4 */
+ BGP_LS_TLV_BGP_LS_IDENTIFIER = 513, /* RFC7752, Section 3.2.1.4 */
+ BGP_LS_TLV_OSPF_AREA_ID = 514, /* RFC7752, Section 3.2.1.4 */
+ BGP_LS_TLV_IGP_ROUTER_ID = 515, /* RFC7752, Section 3.2.1.4 */
+ BGP_LS_TLV_BGP_ROUTER_ID = 516, /* RFC9086 */
+ BGP_LS_TLV_BGP_CONFEDERATION_MEMBER = 517, /* RFC9086 */
+ BGP_LS_TLV_SRV6_SID_INFORMATION_TLV =
+ 518, /* draft-ietf-idr-bgpls-srv6-ext-08 */
+ BGP_LS_TLV_TUNNEL_ID_TLV =
+ 550, /* draft-ietf-idr-te-lsp-distribution-17 */
+ BGP_LS_TLV_LSP_ID_TLV = 551, /* draft-ietf-idr-te-lsp-distribution-17 */
+ BGP_LS_TLV_IPV4_6_TUNNEL_HEAD_END_ADDRESS_TLV =
+ 552, /* draft-ietf-idr-te-lsp-distribution-17 */
+ BGP_LS_TLV_IPV4_6_TUNNEL_TAIL_END_ADDRESS_TLV =
+ 553, /* draft-ietf-idr-te-lsp-distribution-17 */
+ BGP_LS_TLV_SR_POLICY_CP_DESCRIPTOR_TLV =
+ 554, /* draft-ietf-idr-te-lsp-distribution-17 */
+ BGP_LS_TLV_MPLS_LOCAL_CROSS_CONNECT_TLV =
+ 555, /* draft-ietf-idr-te-lsp-distribution-17 */
+ BGP_LS_TLV_MPLS_CROSS_CONNECT_INTERFACE_TLV =
+ 556, /* draft-ietf-idr-te-lsp-distribution-17 */
+ BGP_LS_TLV_MPLS_CROSS_CONNECT_FEC_TLV =
+ 557, /* draft-ietf-idr-te-lsp-distribution-17 */
+ BGP_LS_TLV_NODE_FLAG_BITS = 1024, /* RFC7752, Section 3.3.1.1 */
+ BGP_LS_TLV_OPAQUE_NODE_ATTRIBUTE = 1025, /* RFC7752, Section 3.3.1.5 */
+ BGP_LS_TLV_NODE_NAME = 1026, /* RFC7752, Section 3.3.1.3 */
+ BGP_LS_TLV_IS_IS_AREA_IDENTIFIER = 1027, /* RFC7752, Section 3.3.1.2 */
+ BGP_LS_TLV_IPV4_ROUTER_ID_OF_LOCAL_NODE =
+ 1028, /* RFC5305, Section 4.3 */
+ BGP_LS_TLV_IPV6_ROUTER_ID_OF_LOCAL_NODE =
+ 1029, /* RFC6119, Section 4.1 */
+ BGP_LS_TLV_IPV4_ROUTER_ID_OF_REMOTE_NODE =
+ 1030, /* RFC5305, Section 4.3 */
+ BGP_LS_TLV_IPV6_ROUTER_ID_OF_REMOTE_NODE =
+ 1031, /* RFC6119, Section 4.1 */
+ BGP_LS_TLV_S_BFD_DISCRIMINATORS = 1032, /* RFC9247 */
+ BGP_LS_TLV_UNASSIGNED = 1033, /* */
+ BGP_LS_TLV_SR_CAPABILITIES = 1034, /* RFC9085, Section 2.1.2 */
+ BGP_LS_TLV_SR_ALGORITHM = 1035, /* RFC9085, Section 2.1.3 */
+ BGP_LS_TLV_SR_LOCAL_BLOCK = 1036, /* RFC9085, Section 2.1.4 */
+ BGP_LS_TLV_SRMS_PREFERENCE = 1037, /* RFC9085, Section 2.1.5 */
+ BGP_LS_TLV_SRV6_CAPABILITIES_TLV =
+ 1038, /* draft-ietf-idr-bgpls-srv6-ext-08 */
+ BGP_LS_TLV_FLEXIBLE_ALGORITHM_DEFINITION = 1039, /* RFC9351 */
+ BGP_LS_TLV_FLEXIBLE_ALGORITHM_EXCLUDE_ANY_AFFINITY = 1040, /* RFC9351 */
+ BGP_LS_TLV_FLEXIBLE_ALGORITHM_INCLUDE_ANY_AFFINITY = 1041, /* RFC9351 */
+ BGP_LS_TLV_FLEXIBLE_ALGORITHM_INCLUDE_ALL_AFFINITY = 1042, /* RFC9351 */
+ BGP_LS_TLV_FLEXIBLE_ALGORITHM_DEFINITION_FLAGS = 1043, /* RFC9351 */
+ BGP_LS_TLV_FLEXIBLE_ALGORITHM_PREFIX_METRIC = 1044, /* RFC9351 */
+ BGP_LS_TLV_FLEXIBLE_ALGORITHM_EXCLUDE_SRLG = 1045, /* RFC9351 */
+ BGP_LS_TLV_FLEXIBLE_ALGORITHM_UNSUPPORTED = 1046, /* RFC9351 */
+ BGP_LS_TLV_ADMINISTRATIVE_GROUP = 1088, /* RFC5305, Section 3.1 */
+ BGP_LS_TLV_MAXIMUM_LINK_BANDWIDTH = 1089, /* RFC5305, Section 3.4 */
+ BGP_LS_TLV_MAX_RESERVABLE_LINK_BANDWIDTH =
+ 1090, /* RFC5305, Section 3.5 */
+ BGP_LS_TLV_UNRESERVED_BANDWIDTH = 1091, /* RFC5305, Section 3.6 */
+ BGP_LS_TLV_TE_DEFAULT_METRIC = 1092, /* RFC7752, Section 3.3.2.3 */
+ BGP_LS_TLV_LINK_PROTECTION_TYPE = 1093, /* RFC5307, Section 1.2 */
+ BGP_LS_TLV_MPLS_PROTOCOL_MASK = 1094, /* RFC7752, Section 3.3.2.2 */
+ BGP_LS_TLV_IGP_METRIC = 1095, /* RFC7752, Section 3.3.2.4 */
+ BGP_LS_TLV_SHARED_RISK_LINK_GROUP = 1096, /* RFC7752, Section 3.3.2.5 */
+ BGP_LS_TLV_OPAQUE_LINK_ATTRIBUTE = 1097, /* RFC7752, Section 3.3.2.6 */
+ BGP_LS_TLV_LINK_NAME = 1098, /* RFC7752, Section 3.3.2.7 */
+ BGP_LS_TLV_ADJACENCY_SID = 1099, /* RFC9085, Section 2.2.1 */
+ BGP_LS_TLV_LAN_ADJACENCY_SID = 1100, /* RFC9085, Section 2.2.2 */
+ BGP_LS_TLV_PEERNODE_SID = 1101, /* RFC9086 */
+ BGP_LS_TLV_PEERADJ_SID = 1102, /* RFC9086 */
+ BGP_LS_TLV_PEERSET_SID = 1103, /* RFC9086 */
+ BGP_LS_TLV_RTM_CAPABILITY = 1105, /* RFC8169 */
+ BGP_LS_TLV_SRV6_END_X_SID_TLV =
+ 1106, /* draft-ietf-idr-bgpls-srv6-ext-08 */
+ BGP_LS_TLV_IS_IS_SRV6_LAN_END_X_SID_TLV =
+ 1107, /* draft-ietf-idr-bgpls-srv6-ext-08 */
+ BGP_LS_TLV_OSPFV3_SRV6_LAN_END_X_SID_TLV =
+ 1108, /* draft-ietf-idr-bgpls-srv6-ext-08 */
+ BGP_LS_TLV_UNIDIRECTIONAL_LINK_DELAY = 1114, /* RFC8571 */
+ BGP_LS_TLV_MIN_MAX_UNIDIRECTIONAL_LINK_DELAY = 1115, /* RFC8571 */
+ BGP_LS_TLV_UNIDIRECTIONAL_DELAY_VARIATION = 1116, /* RFC8571 */
+ BGP_LS_TLV_UNIDIRECTIONAL_LINK_LOSS = 1117, /* RFC8571 */
+ BGP_LS_TLV_UNIDIRECTIONAL_RESIDUAL_BANDWIDTH = 1118, /* RFC8571 */
+ BGP_LS_TLV_UNIDIRECTIONAL_AVAILABLE_BANDWIDTH = 1119, /* RFC8571 */
+ BGP_LS_TLV_UNIDIRECTIONAL_UTILIZED_BANDWIDTH = 1120, /* RFC8571 */
+ BGP_LS_TLV_GRACEFUL_LINK_SHUTDOWN_TLV = 1121, /* RFC8379 */
+ BGP_LS_TLV_APPLICATION_SPECIFIC_LINK_ATTRIBUTES = 1122, /* RFC9294 */
+ BGP_LS_TLV_IGP_FLAGS = 1152, /* RFC7752, Section 3.3.3.1 */
+ BGP_LS_TLV_IGP_ROUTE_TAG = 1153, /* RFC5130 */
+ BGP_LS_TLV_IGP_EXTENDED_ROUTE_TAG = 1154, /* RFC5130 */
+ BGP_LS_TLV_PREFIX_METRIC = 1155, /* RFC5305 */
+ BGP_LS_TLV_OSPF_FORWARDING_ADDRESS = 1156, /* RFC2328 */
+ BGP_LS_TLV_OPAQUE_PREFIX_ATTRIBUTE =
+ 1157, /* RFC7752, Section 3.3.3.6 */
+ BGP_LS_TLV_PREFIX_SID = 1158, /* RFC9085, Section 2.3.1 */
+ BGP_LS_TLV_RANGE = 1159, /* RFC9085, Section 2.3.5 */
+ BGP_LS_TLV_IS_IS_FLOOD_REFLECTION =
+ 1160, /* draft-ietf-idr-bgp-ls-isis-flood-reflection-02 */
+ BGP_LS_TLV_SID_LABEL = 1161, /* RFC9085, Section 2.1.1 */
+ BGP_LS_TLV_SRV6_LOCATOR_TLV =
+ 1162, /* draft-ietf-idr-bgpls-srv6-ext-08 */
+ BGP_LS_TLV_PREFIX_ATTRIBUTES_FLAGS = 1170, /* RFC9085, Section 2.3.2 */
+ BGP_LS_TLV_SOURCE_ROUTER_IDENTIFIER = 1171, /* RFC9085, Section 2.3.3 */
+ BGP_LS_TLV_L2_BUNDLE_MEMBER_ATTRIBUTES =
+ 1172, /* RFC9085, Section 2.2.3 */
+ BGP_LS_TLV_EXTENDED_ADMINISTRATIVE_GROUP = 1173, /* RFC9104 */
+ BGP_LS_TLV_SOURCE_OSPF_ROUTER_ID = 1174, /* RFC9085, Section 2.3.4 */
+ BGP_LS_TLV_MPLS_TE_POLICY_STATE_TLV =
+ 1200, /* draft-ietf-idr-te-lsp-distribution-17 */
+ BGP_LS_TLV_SR_BSID_TLV =
+ 1201, /* draft-ietf-idr-te-lsp-distribution-17 */
+ BGP_LS_TLV_SR_CP_STATE_TLV =
+ 1202, /* draft-ietf-idr-te-lsp-distribution-17 */
+ BGP_LS_TLV_SR_CP_NAME_TLV =
+ 1203, /* draft-ietf-idr-te-lsp-distribution-17 */
+ BGP_LS_TLV_SR_CP_CONSTRAINTS_TLV =
+ 1204, /* draft-ietf-idr-te-lsp-distribution-17 */
+ BGP_LS_TLV_SR_SEGMENT_LIST_TLV =
+ 1205, /* draft-ietf-idr-te-lsp-distribution-17 */
+ BGP_LS_TLV_SR_SEGMENT_SUB_TLV =
+ 1206, /* draft-ietf-idr-te-lsp-distribution-17 */
+ BGP_LS_TLV_SR_SEGMENT_LIST_METRIC_SUB_TLV =
+ 1207, /* draft-ietf-idr-te-lsp-distribution-17 */
+ BGP_LS_TLV_SR_AFFINITY_CONSTRAINT_SUB_TLV =
+ 1208, /* draft-ietf-idr-te-lsp-distribution-17 */
+ BGP_LS_TLV_SR_SRLG_CONSTRAINT_SUB_TLV =
+ 1209, /* draft-ietf-idr-te-lsp-distribution-17 */
+ BGP_LS_TLV_SR_BANDWIDTH_CONSTRAINT_SUB_TLV =
+ 1210, /* draft-ietf-idr-te-lsp-distribution-17 */
+ BGP_LS_TLV_SR_DISJOINT_GROUP_CONSTRAINT_SUB_TLV =
+ 1211, /* draft-ietf-idr-te-lsp-distribution-17 */
+ BGP_LS_TLV_SRV6_BSID_TLV =
+ 1212, /* draft-ietf-idr-te-lsp-distribution-17 */
+ BGP_LS_TLV_SR_POLICY_NAME_TLV =
+ 1213, /* draft-ietf-idr-te-lsp-distribution-17 */
+ BGP_LS_TLV_SRV6_ENDPOINT_FUNCTION_TLV =
+ 1250, /* draft-ietf-idr-bgpls-srv6-ext-08 */
+ BGP_LS_TLV_SRV6_BGP_PEER_NODE_SID_TLV =
+ 1251, /* draft-ietf-idr-bgpls-srv6-ext-08 */
+ BGP_LS_TLV_SRV6_SID_STRUCTURE_TLV =
+ 1252, /* draft-ietf-idr-bgpls-srv6-ext-08 */
+ BGP_LS_TLV_MAX = 1253, /* max TLV value for table size*/
+};
+
+/* RFC7752 #3.2.1.4 IGP router-ID */
+enum bgp_ls_nlri_node_descr_ig_router_id_size {
+ BGP_LS_TLV_IGP_ROUTER_ID_ISIS_NON_PSEUDOWIRE_SIZE = 6,
+ BGP_LS_TLV_IGP_ROUTER_ID_ISIS_PSEUDOWIRE_SIZE = 7,
+ BGP_LS_TLV_IGP_ROUTER_ID_OSPF_NON_PSEUDOWIRE_SIZE = 4,
+ BGP_LS_TLV_IGP_ROUTER_ID_OSPF_PSEUDOWIRE_SIZE = 8,
+};
+
+extern int bgp_nlri_parse_linkstate(struct peer *peer, struct attr *attr,
+ struct bgp_nlri *packet, int withdraw);
+
+extern void bgp_nlri_encode_linkstate(struct stream *s, const struct prefix *p);
+
+extern char *bgp_linkstate_nlri_prefix_display(char *buf, size_t size,
+ uint16_t nlri_type,
+ uintptr_t prefix, uint16_t len);
+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_linkstate_vty.c b/bgpd/bgp_linkstate_vty.c
new file mode 100644
index 0000000000..3962da9c69
--- /dev/null
+++ b/bgpd/bgp_linkstate_vty.c
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* BGP Link-State VTY
+ * Copyright 2023 6WIND S.A.
+ */
+
+#include <zebra.h>
+#include "command.h"
+#include "prefix.h"
+#include "lib/json.h"
+#include "lib/printfrr.h"
+#include "stream.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_linkstate_vty.h"
+#include "bgpd/bgp_linkstate.h"
+#include "bgpd/bgp_zebra.h"
+#include "bgpd/bgp_vty.h"
+#include "bgpd/bgp_debug.h"
+
+#include "bgpd/bgp_linkstate_vty_clippy.c"
+
+
+DEFPY (debug_bgp_linkstate,
+ debug_bgp_linkstate_cmd,
+ "[no] debug bgp linkstate",
+ NO_STR
+ DEBUG_STR
+ BGP_STR
+ "BGP allow linkstate debugging entries\n")
+{
+ if (vty->node == CONFIG_NODE) {
+ if (no)
+ DEBUG_OFF(linkstate, LINKSTATE);
+ else
+ DEBUG_ON(linkstate, LINKSTATE);
+ } else {
+ if (no)
+ TERM_DEBUG_OFF(linkstate, LINKSTATE);
+ else
+ TERM_DEBUG_ON(linkstate, LINKSTATE);
+ vty_out(vty, "BGP linkstate debugging is %s\n",
+ no ? "off" : "on");
+ }
+
+ return CMD_SUCCESS;
+}
+
+
+void bgp_linkstate_vty_init(void)
+{
+ install_element(ENABLE_NODE, &debug_bgp_linkstate_cmd);
+ install_element(CONFIG_NODE, &debug_bgp_linkstate_cmd);
+}
diff --git a/bgpd/bgp_linkstate_vty.h b/bgpd/bgp_linkstate_vty.h
new file mode 100644
index 0000000000..5ba96b58b4
--- /dev/null
+++ b/bgpd/bgp_linkstate_vty.h
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* BGP Link-State VTY header
+ * Copyright 2023 6WIND S.A.
+ */
+
+#ifndef _FRR_BGP_LINKSTATE_VTY_H
+#define _FRR_BGP_LINKSTATE_VTY_H
+
+void bgp_linkstate_vty_init(void);
+
+#endif /* _FRR_BGP_LINKSTATE_VTY_H */
diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c
index 6ee5b5dc5c..8a202b480a 100644
--- a/bgpd/bgp_open.c
+++ b/bgpd/bgp_open.c
@@ -147,6 +147,12 @@ void bgp_capability_vty_out(struct vty *vty, struct peer *peer, bool use_json,
"capabilityErrorMultiProtocolAfi",
"L2VPN");
break;
+ case AFI_LINKSTATE:
+ json_object_string_add(
+ json_cap,
+ "capabilityErrorMultiProtocolAfi",
+ "Link State");
+ break;
case AFI_UNSPEC:
case AFI_MAX:
json_object_int_add(
@@ -198,6 +204,18 @@ void bgp_capability_vty_out(struct vty *vty, struct peer *peer, bool use_json,
"capabilityErrorMultiProtocolSafi",
"flowspec");
break;
+ case SAFI_LINKSTATE:
+ json_object_string_add(
+ json_cap,
+ "capabilityErrorMultiProtocolSafi",
+ "Link State");
+ break;
+ case SAFI_LINKSTATE_VPN:
+ json_object_string_add(
+ json_cap,
+ "capabilityErrorMultiProtocolSafi",
+ "Link State VPN");
+ break;
case SAFI_UNSPEC:
case SAFI_MAX:
json_object_int_add(
@@ -219,6 +237,9 @@ void bgp_capability_vty_out(struct vty *vty, struct peer *peer, bool use_json,
case AFI_L2VPN:
vty_out(vty, "AFI L2VPN, ");
break;
+ case AFI_LINKSTATE:
+ vty_out(vty, "AFI Link State, ");
+ break;
case AFI_UNSPEC:
case AFI_MAX:
vty_out(vty, "AFI Unknown %d, ",
@@ -247,6 +268,12 @@ void bgp_capability_vty_out(struct vty *vty, struct peer *peer, bool use_json,
case SAFI_EVPN:
vty_out(vty, "SAFI EVPN");
break;
+ case SAFI_LINKSTATE:
+ vty_out(vty, "SAFI LINK STATE");
+ break;
+ case SAFI_LINKSTATE_VPN:
+ vty_out(vty, "SAFI LINK STATE VPN");
+ break;
case SAFI_UNSPEC:
case SAFI_MAX:
vty_out(vty, "SAFI Unknown %d ",
@@ -1414,7 +1441,8 @@ int bgp_open_option_parse(struct peer *peer, uint16_t length,
&& !peer->afc_nego[AFI_IP6][SAFI_MPLS_VPN]
&& !peer->afc_nego[AFI_IP6][SAFI_ENCAP]
&& !peer->afc_nego[AFI_IP6][SAFI_FLOWSPEC]
- && !peer->afc_nego[AFI_L2VPN][SAFI_EVPN]) {
+ && !peer->afc_nego[AFI_L2VPN][SAFI_EVPN]
+ && !peer->afc_nego[AFI_LINKSTATE][SAFI_LINKSTATE]) {
flog_err(EC_BGP_PKT_OPEN,
"%s [Error] Configured AFI/SAFIs do not overlap with received MP capabilities",
peer->host);
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index abbc298e47..d473245934 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -48,6 +48,7 @@
#include "bgpd/bgp_io.h"
#include "bgpd/bgp_keepalives.h"
#include "bgpd/bgp_flowspec.h"
+#include "bgpd/bgp_linkstate_tlv.h"
#include "bgpd/bgp_trace.h"
DEFINE_HOOK(bgp_packet_dump,
@@ -349,7 +350,11 @@ int bgp_nlri_parse(struct peer *peer, struct attr *attr,
return bgp_nlri_parse_evpn(peer, attr, packet, mp_withdraw);
case SAFI_FLOWSPEC:
return bgp_nlri_parse_flowspec(peer, attr, packet, mp_withdraw);
+ case SAFI_LINKSTATE:
+ return bgp_nlri_parse_linkstate(peer, attr, packet,
+ mp_withdraw);
}
+
return BGP_NLRI_PARSE_ERROR;
}
@@ -1896,6 +1901,8 @@ static int bgp_open_receive(struct peer_connection *connection,
peer->afc[AFI_L2VPN][SAFI_EVPN];
peer->afc_nego[AFI_IP6][SAFI_FLOWSPEC] =
peer->afc[AFI_IP6][SAFI_FLOWSPEC];
+ peer->afc_nego[AFI_LINKSTATE][SAFI_LINKSTATE] =
+ peer->afc[AFI_LINKSTATE][SAFI_LINKSTATE];
}
/* Verify valid local address present based on negotiated
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 3a850a486d..71b43c6756 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -73,6 +73,7 @@
#include "bgpd/bgp_flowspec.h"
#include "bgpd/bgp_flowspec_util.h"
#include "bgpd/bgp_pbr.h"
+#include "bgpd/bgp_linkstate_tlv.h"
#include "bgpd/bgp_route_clippy.c"
@@ -1581,7 +1582,7 @@ static enum filter_type bgp_input_filter(struct peer *peer,
done:
if (frrtrace_enabled(frr_bgp, input_filter)) {
- char pfxprint[PREFIX2STR_BUFFER];
+ char pfxprint[PREFIX_STRLEN_EXTENDED];
prefix2str(p, pfxprint, sizeof(pfxprint));
frrtrace(5, frr_bgp, input_filter, peer, pfxprint, afi, safi,
@@ -1638,7 +1639,7 @@ static enum filter_type bgp_output_filter(struct peer *peer,
}
if (frrtrace_enabled(frr_bgp, output_filter)) {
- char pfxprint[PREFIX2STR_BUFFER];
+ char pfxprint[PREFIX_STRLEN_EXTENDED];
prefix2str(p, pfxprint, sizeof(pfxprint));
frrtrace(5, frr_bgp, output_filter, peer, pfxprint, afi, safi,
@@ -2712,7 +2713,7 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_dest *dest,
int paths_eq, do_mpath;
bool debug;
struct list mp_list;
- char pfx_buf[PREFIX2STR_BUFFER] = {};
+ char pfx_buf[PREFIX_STRLEN_EXTENDED] = {};
char path_buf[PATH_ADDPATH_STR_BUFFER];
bgp_mp_list_init(&mp_list);
@@ -4167,7 +4168,7 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
int allowas_in = 0;
if (frrtrace_enabled(frr_bgp, process_update)) {
- char pfxprint[PREFIX2STR_BUFFER];
+ char pfxprint[PREFIX_STRLEN_EXTENDED];
prefix2str(p, pfxprint, sizeof(pfxprint));
frrtrace(6, frr_bgp, process_update, peer, pfxprint, addpath_id,
@@ -4729,8 +4730,8 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
(safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST ||
(safi == SAFI_MPLS_VPN &&
pi->sub_type != BGP_ROUTE_IMPORTED))) ||
- (safi == SAFI_EVPN &&
- bgp_evpn_is_prefix_nht_supported(p))) {
+ (safi == SAFI_EVPN && bgp_evpn_is_prefix_nht_supported(p)) ||
+ afi == AFI_LINKSTATE) {
if (safi != SAFI_EVPN && peer->sort == BGP_PEER_EBGP
&& peer->ttl == BGP_DEFAULT_TTL
&& !CHECK_FLAG(peer->flags,
@@ -4877,9 +4878,9 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
/* Nexthop reachability check. */
if (((afi == AFI_IP || afi == AFI_IP6) &&
(safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST ||
- (safi == SAFI_MPLS_VPN &&
- new->sub_type != BGP_ROUTE_IMPORTED))) ||
- (safi == SAFI_EVPN && bgp_evpn_is_prefix_nht_supported(p))) {
+ (safi == SAFI_MPLS_VPN && new->sub_type != BGP_ROUTE_IMPORTED))) ||
+ (safi == SAFI_EVPN && bgp_evpn_is_prefix_nht_supported(p)) ||
+ afi == AFI_LINKSTATE) {
if (safi != SAFI_EVPN && peer->sort == BGP_PEER_EBGP
&& peer->ttl == BGP_DEFAULT_TTL
&& !CHECK_FLAG(peer->flags,
@@ -8772,7 +8773,7 @@ static void route_vty_out_route(struct bgp_dest *dest, const struct prefix *p,
struct vty *vty, json_object *json, bool wide)
{
int len = 0;
- char buf[INET6_ADDRSTRLEN];
+ char buf[PREFIX_STRLEN_EXTENDED];
if (p->family == AF_INET) {
if (!json) {
@@ -8798,6 +8799,14 @@ static void route_vty_out_route(struct bgp_dest *dest, const struct prefix *p,
json ?
NLRI_STRING_FORMAT_JSON_SIMPLE :
NLRI_STRING_FORMAT_MIN, json);
+ } else if (p->family == AF_LINKSTATE) {
+ if (json) {
+ json_object_int_add(json, "version", dest->version);
+ bgp_linkstate_nlri_prefix_json(
+ json, p->u.prefix_linkstate.nlri_type,
+ p->u.prefix_linkstate.ptr, p->prefixlen);
+ } else
+ len = vty_out(vty, "%pFX", p);
} else {
if (!json)
len = vty_out(vty, "%pFX", p);
@@ -10103,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;
@@ -11037,6 +11047,28 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn,
llgr_remaining);
}
+ 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) {
if (CHECK_FLAG(bn->flags, BGP_NODE_PROCESS_SCHEDULED))
@@ -11419,7 +11451,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t sa
vty_out(vty, ASN_FORMAT(bgp->asnotation),
&bgp->as);
vty_out(vty, "\n");
- if (!detail_routes) {
+ if (!detail_routes && safi != SAFI_LINKSTATE) {
vty_out(vty, BGP_SHOW_SCODE_HEADER);
vty_out(vty, BGP_SHOW_NCODE_HEADER);
vty_out(vty, BGP_SHOW_OCODE_HEADER);
@@ -12052,6 +12084,8 @@ const struct prefix_rd *bgp_rd_from_dest(const struct bgp_dest *dest,
case SAFI_UNICAST:
case SAFI_MULTICAST:
case SAFI_LABELED_UNICAST:
+ case SAFI_LINKSTATE:
+ case SAFI_LINKSTATE_VPN:
case SAFI_FLOWSPEC:
case SAFI_MAX:
return NULL;
@@ -12968,6 +13002,62 @@ DEFPY(show_ip_bgp, show_ip_bgp_cmd,
return CMD_SUCCESS;
}
+/* BGP route print out function */
+DEFPY (show_ip_bgp_link_state, show_ip_bgp_link_state_cmd,
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] link-state link-state\
+ [all$all]\
+ [version (1-4294967295)\
+ |detail-routes$detail_routes\
+ ] [json$uj [detail$detail_json] | wide$wide]",
+ SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR
+ BGP_AF_STR
+ BGP_AF_MODIFIER_STR
+ "Display the entries for all address families\n"
+ "Display prefixes with matching version numbers\n"
+ "Version number and above\n"
+ "Display detailed version of all routes\n"
+ JSON_STR
+ "Display detailed version of JSON output\n"
+ "Increase table width for longer prefixes\n")
+{
+ afi_t afi = AFI_LINKSTATE;
+ safi_t safi = SAFI_LINKSTATE;
+ enum bgp_show_type sh_type = bgp_show_type_normal;
+ void *output_arg = NULL;
+ struct bgp *bgp = NULL;
+ int idx = 0;
+ uint16_t show_flags = 0;
+ enum rpki_states rpki_target_state = RPKI_NOT_BEING_USED;
+
+ if (uj) {
+ argc--;
+ SET_FLAG(show_flags, BGP_SHOW_OPT_JSON);
+ }
+
+ if (detail_json)
+ SET_FLAG(show_flags, BGP_SHOW_OPT_JSON_DETAIL);
+
+ if (detail_routes)
+ SET_FLAG(show_flags, BGP_SHOW_OPT_ROUTES_DETAIL);
+
+ if (wide)
+ SET_FLAG(show_flags, BGP_SHOW_OPT_WIDE);
+
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, uj);
+ if (!idx)
+ return CMD_WARNING;
+
+ /* Display prefixes with matching version numbers */
+ if (argv_find(argv, argc, "version", &idx)) {
+ sh_type = bgp_show_type_prefix_version;
+ output_arg = argv[idx + 1]->arg;
+ }
+
+ return bgp_show(vty, bgp, afi, safi, sh_type, output_arg, show_flags,
+ rpki_target_state);
+}
+
DEFUN (show_ip_bgp_route,
show_ip_bgp_route_cmd,
"show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]]<A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M> [<bestpath|multipath>] [rpki <valid|invalid|notfound>] [json]",
@@ -13048,13 +13138,13 @@ DEFUN (show_ip_bgp_route,
DEFUN (show_ip_bgp_regexp,
show_ip_bgp_regexp_cmd,
- "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] regexp REGEX [json]",
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_WITH_LS_CMD_STR" ["BGP_SAFI_WITH_LABEL_LS_CMD_STR"]] regexp REGEX [json]",
SHOW_STR
IP_STR
BGP_STR
BGP_INSTANCE_HELP_STR
- BGP_AFI_HELP_STR
- BGP_SAFI_WITH_LABEL_HELP_STR
+ BGP_AFI_WITH_LS_HELP_STR
+ BGP_SAFI_WITH_LABEL_LS_HELP_STR
"Display routes matching the AS path regular expression\n"
"A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n"
JSON_STR)
@@ -13301,6 +13391,8 @@ static void bgp_table_stats_walker(struct event *t)
case AFI_L2VPN:
space = EVPN_ROUTE_PREFIXLEN;
break;
+ case AFI_LINKSTATE:
+ /* TODO */
case AFI_UNSPEC:
case AFI_MAX:
return;
@@ -13557,6 +13649,8 @@ static int bgp_table_stats_single(struct vty *vty, struct bgp *bgp, afi_t afi,
case AFI_L2VPN:
bitlen = EVPN_ROUTE_PREFIXLEN;
break;
+ case AFI_LINKSTATE:
+ /* TODO */
case AFI_UNSPEC:
case AFI_MAX:
break;
@@ -14591,13 +14685,13 @@ DEFPY (show_ip_bgp_instance_neighbor_bestpath_route,
DEFPY(show_ip_bgp_instance_neighbor_advertised_route,
show_ip_bgp_instance_neighbor_advertised_route_cmd,
- "show [ip] bgp [<view|vrf> VIEWVRFNAME] [" BGP_AFI_CMD_STR " [" BGP_SAFI_WITH_LABEL_CMD_STR "]] [all$all] neighbors <A.B.C.D|X:X::X:X|WORD> <advertised-routes|received-routes|filtered-routes> [route-map RMAP_NAME$route_map] [<A.B.C.D/M|X:X::X:X/M>$prefix | detail$detail] [json$uj | wide$wide]",
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] [" BGP_AFI_WITH_LS_CMD_STR " [" BGP_SAFI_WITH_LABEL_LS_CMD_STR "]] [all$all] neighbors <A.B.C.D|X:X::X:X|WORD> <advertised-routes|received-routes|filtered-routes> [route-map RMAP_NAME$route_map] [<A.B.C.D/M|X:X::X:X/M>$prefix | detail$detail] [json$uj | wide$wide]",
SHOW_STR
IP_STR
BGP_STR
BGP_INSTANCE_HELP_STR
- BGP_AFI_HELP_STR
- BGP_SAFI_WITH_LABEL_HELP_STR
+ BGP_AFI_WITH_LS_HELP_STR
+ BGP_SAFI_WITH_LABEL_LS_HELP_STR
"Display the entries for all address families\n"
"Detailed information on TCP and BGP neighbor connections\n"
"Neighbor to display information about\n"
@@ -14651,6 +14745,12 @@ DEFPY(show_ip_bgp_instance_neighbor_advertised_route,
if (!idx)
return CMD_WARNING;
+ if (afi == AFI_LINKSTATE && prefix_str) {
+ vty_out(vty,
+ "The prefix option cannot be selected with AFI Link-State\n");
+ return CMD_WARNING;
+ }
+
/* neighbors <A.B.C.D|X:X::X:X|WORD> */
argv_find(argv, argc, "neighbors", &idx);
peerstr = argv[++idx]->arg;
@@ -15873,6 +15973,7 @@ void bgp_route_init(void)
install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_statistics_cmd);
install_element(VIEW_NODE, &show_ip_bgp_dampening_params_cmd);
install_element(VIEW_NODE, &show_ip_bgp_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_link_state_cmd);
install_element(VIEW_NODE, &show_ip_bgp_route_cmd);
install_element(VIEW_NODE, &show_ip_bgp_regexp_cmd);
install_element(VIEW_NODE, &show_ip_bgp_statistics_all_cmd);
diff --git a/bgpd/bgp_table.c b/bgpd/bgp_table.c
index 8465ada996..e01bf39113 100644
--- a/bgpd/bgp_table.c
+++ b/bgpd/bgp_table.c
@@ -63,7 +63,7 @@ struct bgp_dest *bgp_dest_lock_node(struct bgp_dest *dest)
const char *bgp_dest_get_prefix_str(struct bgp_dest *dest)
{
const struct prefix *p = NULL;
- static char str[PREFIX_STRLEN] = {0};
+ static char str[PREFIX_STRLEN_EXTENDED] = {0};
p = bgp_dest_get_prefix(dest);
if (p)
@@ -117,6 +117,9 @@ static void bgp_node_destroy(route_table_delegate_t *delegate,
node->info = NULL;
}
+ if (family2afi(node->p.family) == AFI_LINKSTATE)
+ prefix_linkstate_ptr_free(&node->p);
+
XFREE(MTYPE_ROUTE_NODE, node);
}
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index bbe74ef5a7..08c14df1fe 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -168,6 +168,8 @@ static enum node_type bgp_node_type(afi_t afi, safi_t safi)
return BGP_VPNV4_NODE;
case SAFI_FLOWSPEC:
return BGP_FLOWSPECV4_NODE;
+ case SAFI_LINKSTATE:
+ case SAFI_LINKSTATE_VPN:
case SAFI_UNSPEC:
case SAFI_ENCAP:
case SAFI_EVPN:
@@ -188,6 +190,8 @@ static enum node_type bgp_node_type(afi_t afi, safi_t safi)
return BGP_VPNV6_NODE;
case SAFI_FLOWSPEC:
return BGP_FLOWSPECV6_NODE;
+ case SAFI_LINKSTATE:
+ case SAFI_LINKSTATE_VPN:
case SAFI_UNSPEC:
case SAFI_ENCAP:
case SAFI_EVPN:
@@ -198,6 +202,23 @@ static enum node_type bgp_node_type(afi_t afi, safi_t safi)
break;
case AFI_L2VPN:
return BGP_EVPN_NODE;
+ case AFI_LINKSTATE:
+ switch (safi) {
+ case SAFI_LINKSTATE:
+ return BGP_LS_NODE;
+ case SAFI_LINKSTATE_VPN: /* Not yet supported */
+ case SAFI_UNICAST:
+ case SAFI_MULTICAST:
+ case SAFI_LABELED_UNICAST:
+ case SAFI_MPLS_VPN:
+ case SAFI_FLOWSPEC:
+ case SAFI_UNSPEC:
+ case SAFI_ENCAP:
+ case SAFI_EVPN:
+ case SAFI_MAX:
+ return BGP_IPV4_NODE;
+ }
+ break;
case AFI_UNSPEC:
case AFI_MAX:
// We should never be here but to clarify the switch statement..
@@ -239,6 +260,11 @@ static const char *get_afi_safi_vty_str(afi_t afi, safi_t safi)
} else if (afi == AFI_L2VPN) {
if (safi == SAFI_EVPN)
return "L2VPN EVPN";
+ } else if (afi == AFI_LINKSTATE) {
+ if (safi == SAFI_LINKSTATE)
+ return "Link State";
+ if (safi == SAFI_LINKSTATE_VPN)
+ return "Link State VPN";
}
return "Unknown";
@@ -281,6 +307,11 @@ static const char *get_afi_safi_json_str(afi_t afi, safi_t safi)
} else if (afi == AFI_L2VPN) {
if (safi == SAFI_EVPN)
return "l2VpnEvpn";
+ } else if (afi == AFI_LINKSTATE) {
+ if (safi == SAFI_LINKSTATE)
+ return "linkState";
+ if (safi == SAFI_LINKSTATE_VPN)
+ return "linkStateVPN";
}
return "Unknown";
@@ -371,6 +402,9 @@ afi_t bgp_node_afi(struct vty *vty)
case BGP_EVPN_NODE:
afi = AFI_L2VPN;
break;
+ case BGP_LS_NODE:
+ afi = AFI_LINKSTATE;
+ break;
default:
afi = AFI_IP;
break;
@@ -403,6 +437,9 @@ safi_t bgp_node_safi(struct vty *vty)
case BGP_FLOWSPECV6_NODE:
safi = SAFI_FLOWSPEC;
break;
+ case BGP_LS_NODE:
+ safi = SAFI_LINKSTATE;
+ break;
default:
safi = SAFI_UNICAST;
break;
@@ -428,6 +465,8 @@ afi_t bgp_vty_afi_from_str(const char *afi_str)
afi = AFI_IP6;
else if (strmatch(afi_str, "l2vpn"))
afi = AFI_L2VPN;
+ else if (strmatch(afi_str, "link-state"))
+ afi = AFI_LINKSTATE;
return afi;
}
@@ -447,6 +486,10 @@ int argv_find_and_parse_afi(struct cmd_token **argv, int argc, int *index,
ret = 1;
if (afi)
*afi = AFI_L2VPN;
+ } else if (argv_find(argv, argc, "link-state", index)) {
+ ret = 1;
+ if (afi)
+ *afi = AFI_LINKSTATE;
}
return ret;
}
@@ -467,6 +510,10 @@ safi_t bgp_vty_safi_from_str(const char *safi_str)
safi = SAFI_LABELED_UNICAST;
else if (strmatch(safi_str, "flowspec"))
safi = SAFI_FLOWSPEC;
+ else if (strmatch(safi_str, "link-state"))
+ safi = SAFI_LINKSTATE;
+ else if (strmatch(safi_str, "link-state-vpn"))
+ safi = SAFI_LINKSTATE_VPN;
return safi;
}
@@ -498,6 +545,10 @@ int argv_find_and_parse_safi(struct cmd_token **argv, int argc, int *index,
ret = 1;
if (safi)
*safi = SAFI_FLOWSPEC;
+ } else if (argv_find(argv, argc, "link-state", index)) {
+ ret = 1;
+ if (safi)
+ *safi = SAFI_LINKSTATE;
}
return ret;
}
@@ -534,6 +585,8 @@ static const char *get_bgp_default_af_flag(afi_t afi, safi_t safi)
case SAFI_FLOWSPEC:
return "ipv4-flowspec";
case SAFI_UNSPEC:
+ case SAFI_LINKSTATE:
+ case SAFI_LINKSTATE_VPN:
case SAFI_EVPN:
case SAFI_MAX:
return "unknown-afi/safi";
@@ -553,6 +606,8 @@ static const char *get_bgp_default_af_flag(afi_t afi, safi_t safi)
return "ipv6-labeled-unicast";
case SAFI_FLOWSPEC:
return "ipv6-flowspec";
+ case SAFI_LINKSTATE:
+ case SAFI_LINKSTATE_VPN:
case SAFI_UNSPEC:
case SAFI_EVPN:
case SAFI_MAX:
@@ -568,9 +623,30 @@ static const char *get_bgp_default_af_flag(afi_t afi, safi_t safi)
case SAFI_MPLS_VPN:
case SAFI_ENCAP:
case SAFI_LABELED_UNICAST:
+ case SAFI_LINKSTATE:
+ case SAFI_LINKSTATE_VPN:
+ case SAFI_FLOWSPEC:
+ case SAFI_UNSPEC:
+ case SAFI_MAX:
+ return "unknown-afi/safi";
+ }
+ break;
+ case AFI_LINKSTATE:
+ switch (safi) {
+ case SAFI_EVPN:
+ case SAFI_UNICAST:
+ case SAFI_MULTICAST:
+ case SAFI_MPLS_VPN:
+ case SAFI_ENCAP:
+ case SAFI_LABELED_UNICAST:
case SAFI_FLOWSPEC:
case SAFI_UNSPEC:
case SAFI_MAX:
+ case SAFI_LINKSTATE:
+ return "link-state";
+ case SAFI_LINKSTATE_VPN:
+ return "link-state-vpn";
+ default:
return "unknown-afi/safi";
}
break;
@@ -4136,6 +4212,7 @@ DEFPY(bgp_default_afi_safi, bgp_default_afi_safi_cmd,
"ipv6-vpn|"
"ipv6-labeled-unicast|"
"ipv6-flowspec|"
+ "link-state|"
"l2vpn-evpn>$afi_safi",
NO_STR
BGP_STR
@@ -4150,6 +4227,7 @@ DEFPY(bgp_default_afi_safi, bgp_default_afi_safi_cmd,
"Activate ipv6-vpn for a peer by default\n"
"Activate ipv6-labeled-unicast for a peer by default\n"
"Activate ipv6-flowspec for a peer by default\n"
+ "Activate link-state for a peer by default\n"
"Activate l2vpn-evpn for a peer by default\n")
{
VTY_DECLVAR_CONTEXT(bgp, bgp);
@@ -4159,9 +4237,14 @@ DEFPY(bgp_default_afi_safi, bgp_default_afi_safi_cmd,
strlcpy(afi_safi_str, afi_safi, sizeof(afi_safi_str));
char *afi_str = strtok_r(afi_safi_str, "-", &afi_safi_str_tok);
char *safi_str = strtok_r(NULL, "-", &afi_safi_str_tok);
- afi_t afi = bgp_vty_afi_from_str(afi_str);
+ afi_t afi;
safi_t safi;
+ if (strmatch(afi_safi, "link-state"))
+ afi = bgp_vty_afi_from_str("link-state");
+ else
+ afi = bgp_vty_afi_from_str(afi_str);
+
/*
* Impossible situation but making coverity happy
*/
@@ -4169,6 +4252,8 @@ DEFPY(bgp_default_afi_safi, bgp_default_afi_safi_cmd,
if (strmatch(safi_str, "labeled"))
safi = bgp_vty_safi_from_str("labeled-unicast");
+ else if (strmatch(afi_safi, "link-state"))
+ safi = bgp_vty_safi_from_str("link-state");
else
safi = bgp_vty_safi_from_str(safi_str);
@@ -10278,6 +10363,15 @@ DEFUN_NOSH (address_family_evpn,
return CMD_SUCCESS;
}
+DEFUN_NOSH(address_family_linkstate, address_family_linkstate_cmd,
+ "address-family link-state link-state",
+ "Enter Address Family command mode\n" BGP_AF_STR BGP_AF_MODIFIER_STR)
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ vty->node = BGP_LS_NODE;
+ return CMD_SUCCESS;
+}
+
DEFUN_NOSH (bgp_segment_routing_srv6,
bgp_segment_routing_srv6_cmd,
"segment-routing srv6",
@@ -10418,7 +10512,8 @@ DEFUN_NOSH (exit_address_family,
|| vty->node == BGP_IPV6L_NODE || vty->node == BGP_VPNV6_NODE
|| vty->node == BGP_EVPN_NODE
|| vty->node == BGP_FLOWSPECV4_NODE
- || vty->node == BGP_FLOWSPECV6_NODE)
+ || vty->node == BGP_FLOWSPECV6_NODE
+ || vty->node == BGP_LS_NODE)
vty->node = BGP_NODE;
return CMD_SUCCESS;
}
@@ -12292,11 +12387,11 @@ int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi,
/* `show [ip] bgp summary' commands. */
DEFPY(show_ip_bgp_summary, show_ip_bgp_summary_cmd,
- "show [ip] bgp [<view|vrf> VIEWVRFNAME] [" BGP_AFI_CMD_STR
- " [" BGP_SAFI_WITH_LABEL_CMD_STR
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] [" BGP_AFI_WITH_LS_CMD_STR
+ " [" BGP_SAFI_WITH_LABEL_LS_CMD_STR
"]] [all$all] summary [established|failed] [<neighbor <A.B.C.D|X:X::X:X|WORD>|remote-as <ASNUM|internal|external>>] [terse] [wide] [json$uj]",
- SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR BGP_AFI_HELP_STR
- BGP_SAFI_WITH_LABEL_HELP_STR
+ SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR BGP_AFI_WITH_LS_HELP_STR
+ BGP_SAFI_WITH_LABEL_LS_HELP_STR
"Display the entries for all address families\n"
"Summary of BGP neighbor status\n"
"Show only sessions in Established state\n"
@@ -18365,6 +18460,11 @@ static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi,
} else if (afi == AFI_L2VPN) {
if (safi == SAFI_EVPN)
vty_frame(vty, "l2vpn evpn");
+ } else if (afi == AFI_LINKSTATE) {
+ if (safi == SAFI_LINKSTATE)
+ vty_frame(vty, "link-state link-state");
+ else if (safi == SAFI_LINKSTATE_VPN)
+ vty_frame(vty, "link-state link-state-vpn");
}
vty_frame(vty, "\n");
@@ -18908,6 +19008,11 @@ int bgp_config_write(struct vty *vty)
/* EVPN configuration. */
bgp_config_write_family(vty, bgp, AFI_L2VPN, SAFI_EVPN);
+ bgp_config_write_family(vty, bgp, AFI_LINKSTATE,
+ SAFI_LINKSTATE);
+ bgp_config_write_family(vty, bgp, AFI_LINKSTATE,
+ SAFI_LINKSTATE_VPN);
+
hook_call(bgp_inst_config_write, bgp, vty);
#ifdef ENABLE_BGP_VNC
@@ -19032,6 +19137,13 @@ static struct cmd_node bgp_srv6_node = {
.prompt = "%s(config-router-srv6)# ",
};
+static struct cmd_node bgp_ls_node = {
+ .name = "bgp link-state",
+ .node = BGP_LS_NODE,
+ .parent_node = BGP_NODE,
+ .prompt = "%s(config-router-af-ls)# ",
+};
+
static void community_list_vty(void);
static void bgp_ac_peergroup(vector comps, struct cmd_token *token)
@@ -19346,6 +19458,7 @@ void bgp_vty_init(void)
install_node(&bgp_flowspecv4_node);
install_node(&bgp_flowspecv6_node);
install_node(&bgp_srv6_node);
+ install_node(&bgp_ls_node);
/* Install default VTY commands to new nodes. */
install_default(BGP_NODE);
@@ -19362,6 +19475,7 @@ void bgp_vty_init(void)
install_default(BGP_EVPN_NODE);
install_default(BGP_EVPN_VNI_NODE);
install_default(BGP_SRV6_NODE);
+ install_default(BGP_LS_NODE);
/* "global bgp inq-limit command */
install_element(CONFIG_NODE, &bgp_inq_limit_cmd);
@@ -19493,6 +19607,13 @@ void bgp_vty_init(void)
install_element(BGP_IPV6L_NODE, &bgp_maxpaths_ibgp_cluster_cmd);
install_element(BGP_IPV6L_NODE, &no_bgp_maxpaths_ibgp_cmd);
+ install_element(BGP_LS_NODE, &bgp_maxpaths_cmd);
+ install_element(BGP_LS_NODE, &no_bgp_maxpaths_cmd);
+ install_element(BGP_LS_NODE, &bgp_maxpaths_ibgp_cmd);
+ install_element(BGP_LS_NODE, &no_bgp_maxpaths_ibgp_cmd);
+ install_element(BGP_LS_NODE, &bgp_maxpaths_ibgp_cluster_cmd);
+
+
/* "timers bgp" commands. */
install_element(BGP_NODE, &bgp_timers_cmd);
install_element(BGP_NODE, &no_bgp_timers_cmd);
@@ -19715,6 +19836,7 @@ void bgp_vty_init(void)
install_element(BGP_FLOWSPECV4_NODE, &neighbor_activate_cmd);
install_element(BGP_FLOWSPECV6_NODE, &neighbor_activate_cmd);
install_element(BGP_EVPN_NODE, &neighbor_activate_cmd);
+ install_element(BGP_LS_NODE, &neighbor_activate_cmd);
/* "no neighbor activate" commands. */
install_element(BGP_NODE, &no_neighbor_activate_hidden_cmd);
@@ -19729,6 +19851,7 @@ void bgp_vty_init(void)
install_element(BGP_FLOWSPECV4_NODE, &no_neighbor_activate_cmd);
install_element(BGP_FLOWSPECV6_NODE, &no_neighbor_activate_cmd);
install_element(BGP_EVPN_NODE, &no_neighbor_activate_cmd);
+ install_element(BGP_LS_NODE, &no_neighbor_activate_cmd);
/* "neighbor peer-group" set commands. */
install_element(BGP_NODE, &neighbor_set_peer_group_cmd);
@@ -19743,6 +19866,8 @@ void bgp_vty_init(void)
&neighbor_set_peer_group_hidden_cmd);
install_element(BGP_FLOWSPECV6_NODE,
&neighbor_set_peer_group_hidden_cmd);
+ install_element(BGP_LS_NODE, &neighbor_set_peer_group_hidden_cmd);
+
/* "no neighbor peer-group unset" commands. */
install_element(BGP_NODE, &no_neighbor_set_peer_group_cmd);
@@ -19757,6 +19882,7 @@ void bgp_vty_init(void)
&no_neighbor_set_peer_group_hidden_cmd);
install_element(BGP_FLOWSPECV6_NODE,
&no_neighbor_set_peer_group_hidden_cmd);
+ install_element(BGP_LS_NODE, &no_neighbor_set_peer_group_hidden_cmd);
/* "neighbor softreconfiguration inbound" commands.*/
install_element(BGP_NODE, &neighbor_soft_reconfiguration_hidden_cmd);
@@ -19787,6 +19913,8 @@ void bgp_vty_init(void)
&no_neighbor_soft_reconfiguration_cmd);
install_element(BGP_EVPN_NODE, &neighbor_soft_reconfiguration_cmd);
install_element(BGP_EVPN_NODE, &no_neighbor_soft_reconfiguration_cmd);
+ install_element(BGP_LS_NODE, &neighbor_soft_reconfiguration_cmd);
+ install_element(BGP_LS_NODE, &no_neighbor_soft_reconfiguration_cmd);
/* "neighbor attribute-unchanged" commands. */
install_element(BGP_NODE, &neighbor_attr_unchanged_hidden_cmd);
@@ -19807,9 +19935,10 @@ void bgp_vty_init(void)
install_element(BGP_VPNV4_NODE, &no_neighbor_attr_unchanged_cmd);
install_element(BGP_VPNV6_NODE, &neighbor_attr_unchanged_cmd);
install_element(BGP_VPNV6_NODE, &no_neighbor_attr_unchanged_cmd);
-
install_element(BGP_EVPN_NODE, &neighbor_attr_unchanged_cmd);
install_element(BGP_EVPN_NODE, &no_neighbor_attr_unchanged_cmd);
+ install_element(BGP_LS_NODE, &neighbor_attr_unchanged_cmd);
+ install_element(BGP_LS_NODE, &no_neighbor_attr_unchanged_cmd);
install_element(BGP_FLOWSPECV4_NODE, &neighbor_attr_unchanged_cmd);
install_element(BGP_FLOWSPECV4_NODE, &no_neighbor_attr_unchanged_cmd);
@@ -19842,6 +19971,8 @@ void bgp_vty_init(void)
install_element(BGP_VPNV6_NODE, &no_neighbor_nexthop_self_cmd);
install_element(BGP_EVPN_NODE, &neighbor_nexthop_self_cmd);
install_element(BGP_EVPN_NODE, &no_neighbor_nexthop_self_cmd);
+ install_element(BGP_LS_NODE, &neighbor_nexthop_self_cmd);
+ install_element(BGP_LS_NODE, &no_neighbor_nexthop_self_cmd);
/* "neighbor next-hop-self force" commands. */
install_element(BGP_NODE, &neighbor_nexthop_self_force_hidden_cmd);
@@ -19890,6 +20021,8 @@ void bgp_vty_init(void)
&no_neighbor_nexthop_self_all_hidden_cmd);
install_element(BGP_EVPN_NODE, &neighbor_nexthop_self_force_cmd);
install_element(BGP_EVPN_NODE, &no_neighbor_nexthop_self_force_cmd);
+ install_element(BGP_LS_NODE, &neighbor_nexthop_self_force_cmd);
+ install_element(BGP_LS_NODE, &no_neighbor_nexthop_self_force_cmd);
/* "neighbor as-override" commands. */
install_element(BGP_NODE, &neighbor_as_override_hidden_cmd);
@@ -20022,6 +20155,18 @@ void bgp_vty_init(void)
&neighbor_remove_private_as_all_replace_as_cmd);
install_element(BGP_VPNV6_NODE,
&no_neighbor_remove_private_as_all_replace_as_cmd);
+ install_element(BGP_LS_NODE, &neighbor_remove_private_as_cmd);
+ install_element(BGP_LS_NODE, &no_neighbor_remove_private_as_cmd);
+ install_element(BGP_LS_NODE, &neighbor_remove_private_as_all_cmd);
+ install_element(BGP_LS_NODE, &no_neighbor_remove_private_as_all_cmd);
+ install_element(BGP_LS_NODE,
+ &neighbor_remove_private_as_replace_as_cmd);
+ install_element(BGP_LS_NODE,
+ &no_neighbor_remove_private_as_replace_as_cmd);
+ install_element(BGP_LS_NODE,
+ &neighbor_remove_private_as_all_replace_as_cmd);
+ install_element(BGP_LS_NODE,
+ &no_neighbor_remove_private_as_all_replace_as_cmd);
/* "neighbor send-community" commands.*/
install_element(BGP_NODE, &neighbor_send_community_hidden_cmd);
@@ -20060,6 +20205,10 @@ void bgp_vty_init(void)
install_element(BGP_VPNV6_NODE, &neighbor_send_community_type_cmd);
install_element(BGP_VPNV6_NODE, &no_neighbor_send_community_cmd);
install_element(BGP_VPNV6_NODE, &no_neighbor_send_community_type_cmd);
+ install_element(BGP_LS_NODE, &neighbor_send_community_cmd);
+ install_element(BGP_LS_NODE, &neighbor_send_community_type_cmd);
+ install_element(BGP_LS_NODE, &no_neighbor_send_community_cmd);
+ install_element(BGP_LS_NODE, &no_neighbor_send_community_type_cmd);
/* "neighbor route-reflector" commands.*/
install_element(BGP_NODE, &neighbor_route_reflector_client_hidden_cmd);
@@ -20097,6 +20246,8 @@ void bgp_vty_init(void)
&no_neighbor_route_reflector_client_cmd);
install_element(BGP_EVPN_NODE, &neighbor_route_reflector_client_cmd);
install_element(BGP_EVPN_NODE, &no_neighbor_route_reflector_client_cmd);
+ install_element(BGP_LS_NODE, &neighbor_route_reflector_client_cmd);
+ install_element(BGP_LS_NODE, &no_neighbor_route_reflector_client_cmd);
/* "neighbor route-server" commands.*/
install_element(BGP_NODE, &neighbor_route_server_client_hidden_cmd);
@@ -20125,6 +20276,8 @@ void bgp_vty_init(void)
install_element(BGP_FLOWSPECV6_NODE, &neighbor_route_server_client_cmd);
install_element(BGP_FLOWSPECV6_NODE,
&no_neighbor_route_server_client_cmd);
+ install_element(BGP_LS_NODE, &neighbor_route_server_client_cmd);
+ install_element(BGP_LS_NODE, &no_neighbor_route_server_client_cmd);
/* "neighbor disable-addpath-rx" commands. */
install_element(BGP_IPV4_NODE, &neighbor_disable_addpath_rx_cmd);
@@ -20286,6 +20439,8 @@ void bgp_vty_init(void)
install_element(BGP_IPV6M_NODE, &no_neighbor_capability_orf_prefix_cmd);
install_element(BGP_IPV6L_NODE, &neighbor_capability_orf_prefix_cmd);
install_element(BGP_IPV6L_NODE, &no_neighbor_capability_orf_prefix_cmd);
+ install_element(BGP_LS_NODE, &neighbor_capability_orf_prefix_cmd);
+ install_element(BGP_LS_NODE, &no_neighbor_capability_orf_prefix_cmd);
/* "neighbor capability dynamic" commands.*/
install_element(BGP_NODE, &neighbor_capability_dynamic_cmd);
@@ -20422,6 +20577,8 @@ void bgp_vty_init(void)
install_element(BGP_VPNV4_NODE, &no_neighbor_distribute_list_cmd);
install_element(BGP_VPNV6_NODE, &neighbor_distribute_list_cmd);
install_element(BGP_VPNV6_NODE, &no_neighbor_distribute_list_cmd);
+ install_element(BGP_LS_NODE, &neighbor_distribute_list_cmd);
+ install_element(BGP_LS_NODE, &no_neighbor_distribute_list_cmd);
/* "neighbor prefix-list" commands. */
install_element(BGP_NODE, &neighbor_prefix_list_hidden_cmd);
@@ -20446,6 +20603,7 @@ void bgp_vty_init(void)
install_element(BGP_FLOWSPECV4_NODE, &no_neighbor_prefix_list_cmd);
install_element(BGP_FLOWSPECV6_NODE, &neighbor_prefix_list_cmd);
install_element(BGP_FLOWSPECV6_NODE, &no_neighbor_prefix_list_cmd);
+ install_element(BGP_LS_NODE, &neighbor_prefix_list_cmd);
/* "neighbor filter-list" commands. */
install_element(BGP_NODE, &neighbor_filter_list_hidden_cmd);
@@ -20470,6 +20628,8 @@ void bgp_vty_init(void)
install_element(BGP_FLOWSPECV4_NODE, &no_neighbor_filter_list_cmd);
install_element(BGP_FLOWSPECV6_NODE, &neighbor_filter_list_cmd);
install_element(BGP_FLOWSPECV6_NODE, &no_neighbor_filter_list_cmd);
+ install_element(BGP_LS_NODE, &neighbor_filter_list_cmd);
+ install_element(BGP_LS_NODE, &no_neighbor_filter_list_cmd);
/* "neighbor route-map" commands. */
install_element(BGP_NODE, &neighbor_route_map_hidden_cmd);
@@ -20496,6 +20656,7 @@ void bgp_vty_init(void)
install_element(BGP_FLOWSPECV6_NODE, &no_neighbor_route_map_cmd);
install_element(BGP_EVPN_NODE, &neighbor_route_map_cmd);
install_element(BGP_EVPN_NODE, &no_neighbor_route_map_cmd);
+ install_element(BGP_LS_NODE, &neighbor_route_map_cmd);
/* "neighbor unsuppress-map" commands. */
install_element(BGP_NODE, &neighbor_unsuppress_map_hidden_cmd);
@@ -20516,6 +20677,8 @@ void bgp_vty_init(void)
install_element(BGP_VPNV4_NODE, &no_neighbor_unsuppress_map_cmd);
install_element(BGP_VPNV6_NODE, &neighbor_unsuppress_map_cmd);
install_element(BGP_VPNV6_NODE, &no_neighbor_unsuppress_map_cmd);
+ install_element(BGP_LS_NODE, &neighbor_unsuppress_map_cmd);
+ install_element(BGP_LS_NODE, &no_neighbor_unsuppress_map_cmd);
/* "neighbor advertise-map" commands. */
install_element(BGP_NODE, &bgp_condadv_period_cmd);
@@ -20635,6 +20798,15 @@ void bgp_vty_init(void)
install_element(BGP_VPNV6_NODE,
&neighbor_maximum_prefix_threshold_restart_cmd);
install_element(BGP_VPNV6_NODE, &no_neighbor_maximum_prefix_cmd);
+ install_element(BGP_LS_NODE, &neighbor_maximum_prefix_cmd);
+ install_element(BGP_LS_NODE, &neighbor_maximum_prefix_threshold_cmd);
+ install_element(BGP_LS_NODE, &neighbor_maximum_prefix_warning_cmd);
+ install_element(BGP_LS_NODE,
+ &neighbor_maximum_prefix_threshold_warning_cmd);
+ install_element(BGP_LS_NODE, &neighbor_maximum_prefix_restart_cmd);
+ install_element(BGP_LS_NODE,
+ &neighbor_maximum_prefix_threshold_restart_cmd);
+ install_element(BGP_LS_NODE, &no_neighbor_maximum_prefix_cmd);
/* "neighbor allowas-in" */
install_element(BGP_NODE, &neighbor_allowas_in_hidden_cmd);
@@ -20657,6 +20829,8 @@ void bgp_vty_init(void)
install_element(BGP_VPNV6_NODE, &no_neighbor_allowas_in_cmd);
install_element(BGP_EVPN_NODE, &neighbor_allowas_in_cmd);
install_element(BGP_EVPN_NODE, &no_neighbor_allowas_in_cmd);
+ install_element(BGP_LS_NODE, &neighbor_allowas_in_cmd);
+ install_element(BGP_LS_NODE, &no_neighbor_allowas_in_cmd);
/* neighbor accept-own */
install_element(BGP_VPNV4_NODE, &neighbor_accept_own_cmd);
@@ -20692,6 +20866,8 @@ void bgp_vty_init(void)
install_element(BGP_NODE, &address_family_evpn_cmd);
+ install_element(BGP_NODE, &address_family_linkstate_cmd);
+
/* "exit-address-family" command. */
install_element(BGP_IPV4_NODE, &exit_address_family_cmd);
install_element(BGP_IPV4M_NODE, &exit_address_family_cmd);
@@ -20704,6 +20880,7 @@ void bgp_vty_init(void)
install_element(BGP_FLOWSPECV4_NODE, &exit_address_family_cmd);
install_element(BGP_FLOWSPECV6_NODE, &exit_address_family_cmd);
install_element(BGP_EVPN_NODE, &exit_address_family_cmd);
+ install_element(BGP_LS_NODE, &exit_address_family_cmd);
/* BGP retain all route-target */
install_element(BGP_VPNV4_NODE, &bgp_retain_route_target_cmd);
diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h
index a105b6de3f..955752f85a 100644
--- a/bgpd/bgp_vty.h
+++ b/bgpd/bgp_vty.h
@@ -17,6 +17,8 @@ struct bgp;
#define BGP_AF_MODIFIER_STR "Address Family modifier\n"
#define BGP_AFI_CMD_STR "<ipv4|ipv6>"
#define BGP_AFI_HELP_STR BGP_AF_STR BGP_AF_STR
+#define BGP_AFI_WITH_LS_CMD_STR "<ipv4|ipv6|link-state>"
+#define BGP_AFI_WITH_LS_HELP_STR BGP_AF_STR BGP_AF_STR BGP_AF_STR
#define BGP_SAFI_CMD_STR "<unicast|multicast|vpn>"
#define BGP_SAFI_HELP_STR \
BGP_AF_MODIFIER_STR BGP_AF_MODIFIER_STR BGP_AF_MODIFIER_STR
@@ -28,6 +30,12 @@ struct bgp;
BGP_AF_MODIFIER_STR BGP_AF_MODIFIER_STR BGP_AF_MODIFIER_STR \
BGP_AF_MODIFIER_STR BGP_AF_MODIFIER_STR
+#define BGP_SAFI_WITH_LABEL_LS_CMD_STR \
+ "<unicast|multicast|vpn|labeled-unicast|flowspec|link-state>"
+#define BGP_SAFI_WITH_LABEL_LS_HELP_STR \
+ BGP_AF_MODIFIER_STR BGP_AF_MODIFIER_STR BGP_AF_MODIFIER_STR \
+ BGP_AF_MODIFIER_STR BGP_AF_MODIFIER_STR BGP_AF_MODIFIER_STR
+
#define BGP_SELF_ORIG_CMD_STR "self-originate"
#define BGP_SELF_ORIG_HELP_STR "Display only self-originated routes\n"
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
index 9e02f1b550..741542f9ba 100644
--- a/bgpd/bgp_zebra.c
+++ b/bgpd/bgp_zebra.c
@@ -1322,6 +1322,10 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p,
uint32_t bos = 0;
uint32_t exp = 0;
+ if (afi == AFI_LINKSTATE)
+ /* nothing to install */
+ return;
+
/*
* BGP is installing this route and bgp has been configured
* to suppress announcements until the route has been installed
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 585863954c..dbbc1fa822 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -71,6 +71,8 @@
#include "bgpd/bgp_io.h"
#include "bgpd/bgp_ecommunity.h"
#include "bgpd/bgp_flowspec.h"
+#include "bgpd/bgp_linkstate.h"
+#include "bgpd/bgp_linkstate_vty.h"
#include "bgpd/bgp_labelpool.h"
#include "bgpd/bgp_pbr.h"
#include "bgpd/bgp_addpath.h"
@@ -2040,6 +2042,10 @@ void peer_as_change(struct peer *peer, as_t as, int as_specified,
PEER_FLAG_REFLECTOR_CLIENT);
UNSET_FLAG(peer->af_flags[AFI_L2VPN][SAFI_EVPN],
PEER_FLAG_REFLECTOR_CLIENT);
+ UNSET_FLAG(peer->af_flags[AFI_LINKSTATE][SAFI_LINKSTATE],
+ PEER_FLAG_REFLECTOR_CLIENT);
+ UNSET_FLAG(peer->af_flags[AFI_LINKSTATE][SAFI_LINKSTATE_VPN],
+ PEER_FLAG_REFLECTOR_CLIENT);
}
}
@@ -4384,7 +4390,9 @@ bool peer_active(struct peer *peer)
|| peer->afc[AFI_IP6][SAFI_MPLS_VPN]
|| peer->afc[AFI_IP6][SAFI_ENCAP]
|| peer->afc[AFI_IP6][SAFI_FLOWSPEC]
- || peer->afc[AFI_L2VPN][SAFI_EVPN])
+ || peer->afc[AFI_L2VPN][SAFI_EVPN]
+ || peer->afc[AFI_LINKSTATE][SAFI_LINKSTATE]
+ || peer->afc[AFI_LINKSTATE][SAFI_LINKSTATE_VPN])
return true;
return false;
}
@@ -4404,7 +4412,9 @@ bool peer_active_nego(struct peer *peer)
|| peer->afc_nego[AFI_IP6][SAFI_MPLS_VPN]
|| peer->afc_nego[AFI_IP6][SAFI_ENCAP]
|| peer->afc_nego[AFI_IP6][SAFI_FLOWSPEC]
- || peer->afc_nego[AFI_L2VPN][SAFI_EVPN])
+ || peer->afc_nego[AFI_L2VPN][SAFI_EVPN]
+ || peer->afc_nego[AFI_LINKSTATE][SAFI_LINKSTATE]
+ || peer->afc_nego[AFI_LINKSTATE][SAFI_LINKSTATE_VPN])
return true;
return false;
}
@@ -8384,6 +8394,8 @@ void bgp_init(unsigned short instance)
#endif
bgp_ethernetvpn_init();
bgp_flowspec_vty_init();
+ bgp_linkstate_init();
+ bgp_linkstate_vty_init();
/* Access list initialize. */
access_list_init();
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index ca1411a3b1..8f78e33f65 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -81,6 +81,8 @@ enum bgp_af_index {
BGP_AF_IPV6_LBL_UNICAST,
BGP_AF_IPV4_FLOWSPEC,
BGP_AF_IPV6_FLOWSPEC,
+ BGP_AF_LINKSTATE,
+ BGP_AF_LINKSTATE_VPN,
BGP_AF_MAX
};
@@ -1924,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
@@ -2504,6 +2507,8 @@ static inline int afindex(afi_t afi, safi_t safi)
return BGP_AF_IPV4_ENCAP;
case SAFI_FLOWSPEC:
return BGP_AF_IPV4_FLOWSPEC;
+ case SAFI_LINKSTATE:
+ case SAFI_LINKSTATE_VPN:
case SAFI_EVPN:
case SAFI_UNSPEC:
case SAFI_MAX:
@@ -2524,6 +2529,8 @@ static inline int afindex(afi_t afi, safi_t safi)
return BGP_AF_IPV6_ENCAP;
case SAFI_FLOWSPEC:
return BGP_AF_IPV6_FLOWSPEC;
+ case SAFI_LINKSTATE:
+ case SAFI_LINKSTATE_VPN:
case SAFI_EVPN:
case SAFI_UNSPEC:
case SAFI_MAX:
@@ -2541,6 +2548,26 @@ static inline int afindex(afi_t afi, safi_t safi)
case SAFI_ENCAP:
case SAFI_FLOWSPEC:
case SAFI_UNSPEC:
+ case SAFI_LINKSTATE:
+ case SAFI_LINKSTATE_VPN:
+ case SAFI_MAX:
+ return BGP_AF_MAX;
+ }
+ break;
+ case AFI_LINKSTATE:
+ switch (safi) {
+ case SAFI_LINKSTATE:
+ return BGP_AF_LINKSTATE;
+ case SAFI_LINKSTATE_VPN:
+ return BGP_AF_LINKSTATE_VPN;
+ case SAFI_EVPN:
+ case SAFI_UNICAST:
+ case SAFI_MULTICAST:
+ case SAFI_LABELED_UNICAST:
+ case SAFI_MPLS_VPN:
+ case SAFI_ENCAP:
+ case SAFI_FLOWSPEC:
+ case SAFI_UNSPEC:
case SAFI_MAX:
return BGP_AF_MAX;
}
@@ -2570,7 +2597,9 @@ static inline int peer_afi_active_nego(const struct peer *peer, afi_t afi)
|| peer->afc_nego[afi][SAFI_MPLS_VPN]
|| peer->afc_nego[afi][SAFI_ENCAP]
|| peer->afc_nego[afi][SAFI_FLOWSPEC]
- || peer->afc_nego[afi][SAFI_EVPN])
+ || peer->afc_nego[afi][SAFI_EVPN]
+ || peer->afc_nego[afi][SAFI_LINKSTATE]
+ || peer->afc_nego[afi][SAFI_LINKSTATE_VPN])
return 1;
return 0;
}
@@ -2590,7 +2619,9 @@ static inline int peer_group_af_configured(struct peer_group *group)
|| peer->afc[AFI_IP6][SAFI_MPLS_VPN]
|| peer->afc[AFI_IP6][SAFI_ENCAP]
|| peer->afc[AFI_IP6][SAFI_FLOWSPEC]
- || peer->afc[AFI_L2VPN][SAFI_EVPN])
+ || peer->afc[AFI_L2VPN][SAFI_EVPN]
+ || peer->afc[AFI_LINKSTATE][SAFI_LINKSTATE]
+ || peer->afc[AFI_LINKSTATE][SAFI_LINKSTATE_VPN])
return 1;
return 0;
}
diff --git a/bgpd/rfapi/rfapi_import.c b/bgpd/rfapi/rfapi_import.c
index a93e186f8d..126ba225cb 100644
--- a/bgpd/rfapi/rfapi_import.c
+++ b/bgpd/rfapi/rfapi_import.c
@@ -248,6 +248,8 @@ void rfapiCheckRefcount(struct agg_node *rn, safi_t safi, int lockoffset)
case SAFI_EVPN:
case SAFI_LABELED_UNICAST:
case SAFI_FLOWSPEC:
+ case SAFI_LINKSTATE:
+ case SAFI_LINKSTATE_VPN:
case SAFI_MAX:
assert(!"Passed in safi should be impossible");
}
@@ -2972,6 +2974,7 @@ static void rfapiBgpInfoFilteredImportEncap(
case AFI_UNSPEC:
case AFI_L2VPN:
+ case AFI_LINKSTATE:
case AFI_MAX:
flog_err(EC_LIB_DEVELOPMENT, "%s: bad afi %d", __func__, afi);
return;
@@ -3420,6 +3423,7 @@ void rfapiBgpInfoFilteredImportVPN(
rt = import_table->imported_vpn[afi];
break;
+ case AFI_LINKSTATE:
case AFI_UNSPEC:
case AFI_MAX:
flog_err(EC_LIB_DEVELOPMENT, "%s: bad afi %d", __func__, afi);
@@ -3819,6 +3823,8 @@ rfapiBgpInfoFilteredImportFunction(safi_t safi)
case SAFI_EVPN:
case SAFI_LABELED_UNICAST:
case SAFI_FLOWSPEC:
+ case SAFI_LINKSTATE:
+ case SAFI_LINKSTATE_VPN:
case SAFI_MAX:
/* not expected */
flog_err(EC_LIB_DEVELOPMENT, "%s: bad safi %d", __func__, safi);
@@ -4063,6 +4069,8 @@ static void rfapiProcessPeerDownRt(struct peer *peer,
case SAFI_EVPN:
case SAFI_LABELED_UNICAST:
case SAFI_FLOWSPEC:
+ case SAFI_LINKSTATE:
+ case SAFI_LINKSTATE_VPN:
case SAFI_MAX:
/* Suppress uninitialized variable warning */
rt = NULL;
diff --git a/bgpd/rfapi/rfapi_monitor.c b/bgpd/rfapi/rfapi_monitor.c
index 3fe957ba4d..766b5d5360 100644
--- a/bgpd/rfapi/rfapi_monitor.c
+++ b/bgpd/rfapi/rfapi_monitor.c
@@ -237,6 +237,8 @@ void rfapiMonitorExtraFlush(safi_t safi, struct agg_node *rn)
case SAFI_EVPN:
case SAFI_LABELED_UNICAST:
case SAFI_FLOWSPEC:
+ case SAFI_LINKSTATE:
+ case SAFI_LINKSTATE_VPN:
case SAFI_MAX:
assert(0);
}
@@ -305,6 +307,8 @@ void rfapiMonitorExtraPrune(safi_t safi, struct agg_node *rn)
case SAFI_EVPN:
case SAFI_LABELED_UNICAST:
case SAFI_FLOWSPEC:
+ case SAFI_LINKSTATE:
+ case SAFI_LINKSTATE_VPN:
case SAFI_MAX:
assert(0);
}
diff --git a/bgpd/subdir.am b/bgpd/subdir.am
index c2dd207a49..8b6b5ae743 100644
--- a/bgpd/subdir.am
+++ b/bgpd/subdir.am
@@ -51,6 +51,9 @@ bgpd_libbgp_a_SOURCES = \
bgpd/bgp_label.c \
bgpd/bgp_labelpool.c \
bgpd/bgp_lcommunity.c \
+ bgpd/bgp_linkstate.c \
+ bgpd/bgp_linkstate_tlv.c \
+ bgpd/bgp_linkstate_vty.c \
bgpd/bgp_mac.c \
bgpd/bgp_memory.c \
bgpd/bgp_mpath.c \
@@ -133,6 +136,9 @@ noinst_HEADERS += \
bgpd/bgp_label.h \
bgpd/bgp_labelpool.h \
bgpd/bgp_lcommunity.h \
+ bgpd/bgp_linkstate.h \
+ bgpd/bgp_linkstate_tlv.h \
+ bgpd/bgp_linkstate_vty.h \
bgpd/bgp_mac.h \
bgpd/bgp_memory.h \
bgpd/bgp_mpath.h \
@@ -209,6 +215,7 @@ clippy_scan += \
bgpd/bgp_debug.c \
bgpd/bgp_evpn_vty.c \
bgpd/bgp_labelpool.c \
+ bgpd/bgp_linkstate_vty.c \
bgpd/bgp_route.c \
bgpd/bgp_routemap.c \
bgpd/bgp_rpki.c \
diff --git a/doc/user/bgp-linkstate.rst b/doc/user/bgp-linkstate.rst
new file mode 100644
index 0000000000..59e37b19f7
--- /dev/null
+++ b/doc/user/bgp-linkstate.rst
@@ -0,0 +1,146 @@
+.. _bgp-link-state:
+
+BGP Link-State
+==============
+
+Overview
+--------
+
+BGP Link-State (BGP-LS) is an extension of the BGP protocol designed to
+redistribute information from an IGP database to a remote controller (most
+likely a Path Computation Element - PCE). BGP-LS does nothing more than
+transport IGP information. Therefore, it cannot be used to replace a Link State
+routing protocol like OSPF and IS-IS.
+
+Historically, the only way to get a network controller to collect an IGP
+database was to have it participate in the IGP itself as if it were a standard
+router. Since the controllers were usually located far from the IGP area,
+tunnels such as GRE were used to connect the controllers to the IGP area. This
+method was so impractical that an alternative solution was imagined: using the
+already deployed inter-domain BGP protocol to redistribute the various IGP
+databases.
+
+BGP Link-State as defined in `RFC7752
+<https://www.rfc-editor.org/rfc/rfc7752.html>`_ uses the AFI 16388 and SAFI 71.
+The BGP Link-State pseudo-prefixes distributed by the `NLRI (Network Layer
+Reachability Information)` uniquely define the following
+IGP information:
+
+- Nodes
+- Link
+- IPv4 Prefix
+- IPv6 Prefix
+
+They are called descriptors. In addition, a new type of BGP Attributes called
+"BGP-LS attributes" carries the other information related to a descriptor.
+
+NLRI and attribute information for BGP-LS is organized using the TLV format
+already used by IS-IS LSPs and OSPF opaque LSAs. The `list of TLV code points
+<https://www.iana.org/assignments/bgp-ls-parameters/bgp-ls-parameters.xhtml#node-descriptor-link-descriptor-prefix-descriptor-attribute-tlv>`_
+is maintained by IANA.
+
+Current implementation
+----------------------
+
+The current version can participate in BGP Link-State AFI / SAFI with
+third-party routers and forward the BGP Link-State descriptors and attributes to
+other routers. However, it can not generate BGP Link-State data from OSPF and
+IS-IS.
+
+IANA maintains a `registry of BGP-LS NRLI descriptor types
+<https://www.iana.org/assignments/bgp-ls-parameters/bgp-ls-parameters.xhtml#nlri-types>`_.
+Only the following RFC7752 NRLI types are supported by the current version:
+
+- Nodes
+- Link
+- IPv4 Prefix
+- IPv6 Prefix
+
+The BGP-LS attribute TLVs for these NLRI types are transmitted as is to other
+routers which means that all the current and future version are already
+supported.
+
+Show commands
+-------------
+
+The following configuration enables the negotiation of the link-state AFI / SAFI
+with the 192.0.2.2 eBGP peer.
+
+.. code-block:: frr
+
+ router bgp 65003
+ neighbor 192.0.2.2 remote-as 65002
+ neighbor 192.0.2.2 update-source 192.0.2.3
+ !
+ address-family link-state link-state
+ neighbor 192.0.2.2 activate
+ neighbor 192.0.2.2 route-map PERMIT-ALL in
+ neighbor 192.0.2.2 route-map PERMIT-ALL out
+ exit-address-family
+ exit
+ !
+ route-map PERMIT-ALL permit 1
+
+The BGP-LS table can be displayed.
+
+.. code-block:: frr
+
+ frr# show bgp link-state link-state
+ BGP table version is 8, local router ID is 192.0.2.3, vrf id 0
+ Default local pref 100, local AS 65003
+ Network Next Hop Metric LocPrf Weight Path
+ *> Node OSPFv2 ID:0x20 Local{AS:65001 ID:0 Area:0 Rtr:10.10.10.10:1.1.1.1}/48
+ 0 65002 65001 i
+ *> IPv4-Prefix OSPFv2 ID:0x20 Local{AS:65001 ID:0 Area:0 Rtr:10.10.10.10:1.1.1.1} Prefix{IPv4:89.10.11.0/24}/64
+ 0 65002 65001 i
+ *> IPv6-Prefix ISIS-L2 ID:0x20 Local{AS:65001 ID:0 Rtr:0000.0000.1003.00} Prefix{IPv6:12:12::12:12/128 MT:2}/74
+ 0 65002 65001 i
+ *> IPv6-Prefix OSPFv3 ID:0x20 Local{AS:65001 ID:0 Area:0 Rtr:10.10.10.10} Prefix{OSPF-Route-Type:1 IPv6:12:12::12:12/128 MT:2}/74
+ 0 65002 65001 i
+ *> Node OSPFv2 ID:0x20 Local{AS:65001 ID:0 Area:0 Rtr:10.10.10.10}/48
+ 0 65002 65001 i
+ *> Node ISIS-L1 ID:0x20 Local{AS:65001 ID:0 Rtr:0000.0000.1003.00}/48
+ 0 65002 65001 i
+ *> Link ISIS-L1 ID:0x20 Local{AS:65001 ID:0 Rtr:0000.0000.1001} Remote{AS:65001 ID:0 Rtr:0000.0000.1000} Link{IPv4:10.1.0.1 Neigh-IPv4:10.1.0.2 IPv6:2001::1 Neigh-IPv6:2001::2 MT:0,2}/132
+ 0 65002 65001 i
+
+The "detail-routes" allows displaying the BGP-LS attributes as well.
+
+.. code-block:: frr
+
+ frr# show bgp link-state link-state detail-routes
+ (...)
+ BGP routing table entry for Link ISIS-L1 ID:0x20 Local{AS:65001 ID:0 Rtr:0000.0000.1001} Remote{AS:65001 ID:0 Rtr:0000.0000.1000} Link{IPv4:10.1.0.1 Neigh-IPv4:10.1.0.2 IPv6:2001::1 Neigh-IPv6:2001::2 MT:0,2}/116, version 1
+ Paths: (1 available, best #1)
+ Advertised to non peer-group peers:
+ 192.0.2.1 192.0.2.3
+ 65001
+ :: from 192.0.2.1 (192.0.2.1)
+ Origin IGP, valid, external, best (First path received)
+ Last update: Mon Apr 17 15:45:42 2023
+ BGP-LS attributes:
+ IPv4 Router-ID of Local Node: 1.1.1.1
+ IPv4 Router-ID of Remote Node: 10.10.10.10
+ Maximum link bandwidth: 1410.07 Mbps
+ Max. reservable link bandwidth: 1410.07 Mbps
+ Unreserved bandwidth:
+ [0]: 1410.07 Mbps [1]: 1410.07 Mbps
+ [2]: 1410.07 Mbps [3]: 1410.07 Mbps
+ [4]: 1410.07 Mbps [5]: 1410.07 Mbps
+ [6]: 1410.07 Mbps [7]: 1410.07 Mbps
+ TE Default Metric: 100
+ IGP Metric: 10
+ Adjacency SID:
+ Flags: 0b00110000
+ Weight: 0
+ SID: 15000
+ Unidirectional Link Delay: 8500 microseconds
+ Min/Max Unidirectional Link Delay: 8000/9000 microseconds
+ Application-Specific Link Attributes:
+ SABM Flags : 0b00010000 00000000 00000000 00000000
+ UDABM Flags: 0b00000000 00000000 00000000 00000000
+ Administrative group: 0x00000001
+ TE Default Metric: 100
+ Min/Max Unidirectional Link Delay: 8000/9000 microseconds
+ Extended Administrative Group: 0x00000001
+
diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst
index ba6c160560..1431cbdd28 100644
--- a/doc/user/bgp.rst
+++ b/doc/user/bgp.rst
@@ -818,6 +818,10 @@ from eBGP peers, :ref:`bgp-route-selection`.
MED as an intra-AS metric to steer equal-length AS_PATH routes to, e.g.,
desired exit points.
+.. [#med-transitivity-rant] For some set of objects to have an order, there *must* be some binary ordering relation that is defined for *every* combination of those objects, and that relation *must* be transitive. I.e.:, if the relation operator is <, and if a < b and b < c then that relation must carry over and it *must* be that a < c for the objects to have an order. The ordering relation may allow for equality, i.e. a < b and b < a may both be true and imply that a and b are equal in the order and not distinguished by it, in which case the set has a partial order. Otherwise, if there is an order, all the objects have a distinct place in the order and the set has a total order)
+.. [bgp-route-osci-cond] McPherson, D. and Gill, V. and Walton, D., "Border Gateway Protocol (BGP) Persistent Route Oscillation Condition", IETF RFC3345
+.. [stable-flexible-ibgp] Flavel, A. and M. Roughan, "Stable and flexible iBGP", ACM SIGCOMM 2009
+.. [ibgp-correctness] Griffin, T. and G. Wilfong, "On the correctness of IBGP configuration", ACM SIGCOMM 2002
.. _bgp-graceful-restart:
@@ -1814,6 +1818,13 @@ Configuring Peers
and is not displayed.
The `bgp default l2vpn-evpn` form of the command is displayed.
+.. clicmd:: bgp default link-state
+
+ This command allows the user to specify that the link-state link-state
+ address family is turned on by default or not. This command defaults to off
+ and is not displayed.
+ The `bgp default link-state` form of the command is displayed.
+
.. clicmd:: bgp default show-hostname
This command shows the hostname of the peer in certain BGP commands
@@ -5213,10 +5224,7 @@ Show command json output:
.. include:: flowspec.rst
-.. [#med-transitivity-rant] For some set of objects to have an order, there *must* be some binary ordering relation that is defined for *every* combination of those objects, and that relation *must* be transitive. I.e.:, if the relation operator is <, and if a < b and b < c then that relation must carry over and it *must* be that a < c for the objects to have an order. The ordering relation may allow for equality, i.e. a < b and b < a may both be true and imply that a and b are equal in the order and not distinguished by it, in which case the set has a partial order. Otherwise, if there is an order, all the objects have a distinct place in the order and the set has a total order)
-.. [bgp-route-osci-cond] McPherson, D. and Gill, V. and Walton, D., "Border Gateway Protocol (BGP) Persistent Route Oscillation Condition", IETF RFC3345
-.. [stable-flexible-ibgp] Flavel, A. and M. Roughan, "Stable and flexible iBGP", ACM SIGCOMM 2009
-.. [ibgp-correctness] Griffin, T. and G. Wilfong, "On the correctness of IBGP configuration", ACM SIGCOMM 2002
+.. include:: bgp-linkstate.rst
.. _bgp-fast-convergence:
diff --git a/doc/user/conf.py b/doc/user/conf.py
index 728f9c9364..40accc1bd2 100644
--- a/doc/user/conf.py
+++ b/doc/user/conf.py
@@ -130,6 +130,7 @@ exclude_patterns = [
"rpki.rst",
"routeserver.rst",
"ospf_fundamentals.rst",
+ "bgp-linkstate.rst",
"flowspec.rst",
"snmptrap.rst",
"wecmp_linkbw.rst",
diff --git a/lib/command.h b/lib/command.h
index 718d34b007..36640c493f 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -174,6 +174,7 @@ enum node_type {
BMP_NODE, /* BMP config under router bgp */
ISIS_SRV6_NODE, /* ISIS SRv6 node */
ISIS_SRV6_NODE_MSD_NODE, /* ISIS SRv6 Node MSDs node */
+ BGP_LS_NODE, /* BGP-LS configuration node */
NODE_TYPE_MAX, /* maximum */
};
/* clang-format on */
diff --git a/lib/iana_afi.h b/lib/iana_afi.h
index b9c19cc3d5..9b4d33fa4d 100644
--- a/lib/iana_afi.h
+++ b/lib/iana_afi.h
@@ -26,6 +26,7 @@ typedef enum {
IANA_AFI_IPV4 = 1,
IANA_AFI_IPV6 = 2,
IANA_AFI_L2VPN = 25,
+ IANA_AFI_LINKSTATE = 16388, /* BGP-LS RFC 7752 */
} iana_afi_t;
typedef enum {
@@ -35,6 +36,8 @@ typedef enum {
IANA_SAFI_LABELED_UNICAST = 4,
IANA_SAFI_ENCAP = 7,
IANA_SAFI_EVPN = 70,
+ IANA_SAFI_LINKSTATE = 71, /* BGP-LS RFC 7752 */
+ IANA_SAFI_LINKSTATE_VPN = 72, /* BGP-LS RFC 7752 */
IANA_SAFI_MPLS_VPN = 128,
IANA_SAFI_FLOWSPEC = 133
} iana_safi_t;
@@ -48,6 +51,8 @@ static inline afi_t afi_iana2int(iana_afi_t afi)
return AFI_IP6;
case IANA_AFI_L2VPN:
return AFI_L2VPN;
+ case IANA_AFI_LINKSTATE:
+ return AFI_LINKSTATE;
case IANA_AFI_RESERVED:
return AFI_MAX;
}
@@ -64,6 +69,8 @@ static inline iana_afi_t afi_int2iana(afi_t afi)
return IANA_AFI_IPV6;
case AFI_L2VPN:
return IANA_AFI_L2VPN;
+ case AFI_LINKSTATE:
+ return IANA_AFI_LINKSTATE;
case AFI_UNSPEC:
case AFI_MAX:
return IANA_AFI_RESERVED;
@@ -94,6 +101,10 @@ static inline safi_t safi_iana2int(iana_safi_t safi)
return SAFI_LABELED_UNICAST;
case IANA_SAFI_FLOWSPEC:
return SAFI_FLOWSPEC;
+ case IANA_SAFI_LINKSTATE:
+ return SAFI_LINKSTATE;
+ case IANA_SAFI_LINKSTATE_VPN:
+ return SAFI_LINKSTATE_VPN;
case IANA_SAFI_RESERVED:
return SAFI_MAX;
}
@@ -118,6 +129,10 @@ static inline iana_safi_t safi_int2iana(safi_t safi)
return IANA_SAFI_LABELED_UNICAST;
case SAFI_FLOWSPEC:
return IANA_SAFI_FLOWSPEC;
+ case SAFI_LINKSTATE:
+ return IANA_SAFI_LINKSTATE;
+ case SAFI_LINKSTATE_VPN:
+ return IANA_SAFI_LINKSTATE_VPN;
case SAFI_UNSPEC:
case SAFI_MAX:
return IANA_SAFI_RESERVED;
diff --git a/lib/prefix.c b/lib/prefix.c
index f342c4c1db..cde8677cf0 100644
--- a/lib/prefix.c
+++ b/lib/prefix.c
@@ -20,6 +20,7 @@
DEFINE_MTYPE_STATIC(LIB, PREFIX, "Prefix");
DEFINE_MTYPE_STATIC(LIB, PREFIX_FLOWSPEC, "Prefix Flowspec");
+DEFINE_MTYPE_STATIC(LIB, PREFIX_LINKSTATE, "Prefix Link-State");
/* Maskbit. */
static const uint8_t maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0,
@@ -32,6 +33,18 @@ static const uint8_t maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0,
#define MASKBIT(offset) ((0xff << (PNBBY - (offset))) & 0xff)
+char *(*prefix_linkstate_display_hook)(char *buf, size_t size,
+ uint16_t nlri_type, uintptr_t ptr,
+ uint16_t len) = NULL;
+
+void prefix_set_linkstate_display_hook(char *(*func)(char *buf, size_t size,
+ uint16_t nlri_type,
+ uintptr_t ptr,
+ uint16_t len))
+{
+ prefix_linkstate_display_hook = func;
+}
+
int is_zero_mac(const struct ethaddr *mac)
{
int i = 0;
@@ -81,6 +94,8 @@ int str2family(const char *string)
return AF_ETHERNET;
else if (!strcmp("evpn", string))
return AF_EVPN;
+ else if (!strcmp("link-state", string))
+ return AF_LINKSTATE;
return -1;
}
@@ -95,6 +110,8 @@ const char *family2str(int family)
return "Ethernet";
case AF_EVPN:
return "Evpn";
+ case AF_LINKSTATE:
+ return "Link-State";
}
return "?";
}
@@ -109,6 +126,8 @@ int afi2family(afi_t afi)
else if (afi == AFI_L2VPN)
return AF_ETHERNET;
/* NOTE: EVPN code should NOT use this interface. */
+ else if (afi == AFI_LINKSTATE)
+ return AF_LINKSTATE;
return 0;
}
@@ -120,6 +139,8 @@ afi_t family2afi(int family)
return AFI_IP6;
else if (family == AF_ETHERNET || family == AF_EVPN)
return AFI_L2VPN;
+ else if (family == AF_LINKSTATE)
+ return AFI_LINKSTATE;
return 0;
}
@@ -132,6 +153,8 @@ const char *afi2str_lower(afi_t afi)
return "ipv6";
case AFI_L2VPN:
return "l2vpn";
+ case AFI_LINKSTATE:
+ return "link-state";
case AFI_MAX:
case AFI_UNSPEC:
return "bad-value";
@@ -149,6 +172,8 @@ const char *afi2str(afi_t afi)
return "IPv6";
case AFI_L2VPN:
return "l2vpn";
+ case AFI_LINKSTATE:
+ return "link-state";
case AFI_MAX:
case AFI_UNSPEC:
return "bad-value";
@@ -174,6 +199,10 @@ const char *safi2str(safi_t safi)
return "labeled-unicast";
case SAFI_FLOWSPEC:
return "flowspec";
+ case SAFI_LINKSTATE:
+ return "link-state";
+ case SAFI_LINKSTATE_VPN:
+ return "link-state-vpn";
case SAFI_UNSPEC:
case SAFI_MAX:
return "unknown";
@@ -215,6 +244,21 @@ int prefix_match(union prefixconstptr unet, union prefixconstptr upfx)
if (np[offset] != pp[offset])
return 0;
return 1;
+ } else if (n->family == AF_LINKSTATE) {
+ if (n->u.prefix_linkstate.nlri_type !=
+ p->u.prefix_linkstate.nlri_type)
+ return 0;
+
+ /* Set both prefix's head pointer. */
+ np = (const uint8_t *)&n->u.prefix_linkstate.ptr;
+ pp = (const uint8_t *)&p->u.prefix_linkstate.ptr;
+
+ offset = n->prefixlen; /* length is checked above */
+
+ while (offset--)
+ if (np[offset] != pp[offset])
+ return 0;
+ return 1;
}
/* Set both prefix's head pointer. */
@@ -316,6 +360,8 @@ void prefix_copy(union prefixptr udest, union prefixconstptr usrc)
{
struct prefix *dest = udest.p;
const struct prefix *src = usrc.p;
+ void *temp;
+ int len;
dest->family = src->family;
dest->prefixlen = src->prefixlen;
@@ -334,9 +380,6 @@ void prefix_copy(union prefixptr udest, union prefixconstptr usrc)
dest->u.lp.id = src->u.lp.id;
dest->u.lp.adv_router = src->u.lp.adv_router;
} else if (src->family == AF_FLOWSPEC) {
- void *temp;
- int len;
-
len = src->u.prefix_flowspec.prefixlen;
dest->u.prefix_flowspec.prefixlen =
src->u.prefix_flowspec.prefixlen;
@@ -347,6 +390,14 @@ void prefix_copy(union prefixptr udest, union prefixconstptr usrc)
dest->u.prefix_flowspec.ptr = (uintptr_t)temp;
memcpy((void *)dest->u.prefix_flowspec.ptr,
(void *)src->u.prefix_flowspec.ptr, len);
+ } else if (src->family == AF_LINKSTATE) {
+ len = src->prefixlen;
+ dest->u.prefix_linkstate.nlri_type =
+ src->u.prefix_linkstate.nlri_type;
+ temp = XCALLOC(MTYPE_PREFIX_LINKSTATE, len);
+ dest->u.prefix_linkstate.ptr = (uintptr_t)temp;
+ memcpy((void *)dest->u.prefix_linkstate.ptr,
+ (void *)src->u.prefix_linkstate.ptr, len);
} else {
flog_err(EC_LIB_DEVELOPMENT,
"prefix_copy(): Unknown address family %d",
@@ -436,6 +487,14 @@ int prefix_same(union prefixconstptr up1, union prefixconstptr up2)
p2->u.prefix_flowspec.prefixlen))
return 1;
}
+ if (p1->family == AF_LINKSTATE) {
+ if (p1->u.prefix_linkstate.nlri_type !=
+ p2->u.prefix_linkstate.nlri_type)
+ return 0;
+ if (!memcmp(&p1->u.prefix_linkstate.ptr,
+ &p2->u.prefix_linkstate.ptr, p2->prefixlen))
+ return 1;
+ }
}
return 0;
}
@@ -483,6 +542,22 @@ int prefix_cmp(union prefixconstptr up1, union prefixconstptr up2)
if (pp1[offset] != pp2[offset])
return numcmp(pp1[offset], pp2[offset]);
return 0;
+ } else if (p1->family == AF_LINKSTATE) {
+ pp1 = (const uint8_t *)p1->u.prefix_linkstate.ptr;
+ pp2 = (const uint8_t *)p2->u.prefix_linkstate.ptr;
+
+ if (p1->u.prefix_linkstate.nlri_type !=
+ p2->u.prefix_linkstate.nlri_type)
+ return 1;
+
+ if (p1->prefixlen != p2->prefixlen)
+ return numcmp(p1->prefixlen, p2->prefixlen);
+
+ offset = p1->prefixlen;
+ while (offset--)
+ if (pp1[offset] != pp2[offset])
+ return numcmp(pp1[offset], pp2[offset]);
+ return 0;
}
pp1 = p1->u.val;
pp2 = p2->u.val;
@@ -1072,10 +1147,26 @@ static const char *prefixevpn2str(const struct prefix_evpn *p, char *str,
return str;
}
+const char *bgp_linkstate_nlri_type_2str(uint16_t nlri_type)
+{
+ switch (nlri_type) {
+ case BGP_LINKSTATE_NODE:
+ return "Node";
+ case BGP_LINKSTATE_LINK:
+ return "Link";
+ case BGP_LINKSTATE_PREFIX4:
+ return "IPv4-Prefix";
+ case BGP_LINKSTATE_PREFIX6:
+ return "IPv6-Prefix";
+ }
+
+ return "Unknown";
+}
+
const char *prefix2str(union prefixconstptr pu, char *str, int size)
{
const struct prefix *p = pu.p;
- char buf[PREFIX2STR_BUFFER];
+ char buf[PREFIX_STRLEN_EXTENDED];
int byte, tmp, a, b;
bool z = false;
size_t l;
@@ -1116,6 +1207,22 @@ const char *prefix2str(union prefixconstptr pu, char *str, int size)
strlcpy(str, "FS prefix", size);
break;
+ case AF_LINKSTATE:
+ if (prefix_linkstate_display_hook)
+ snprintf(str, size, "%s/%d",
+ prefix_linkstate_display_hook(
+ buf, sizeof(buf),
+ p->u.prefix_linkstate.nlri_type,
+ p->u.prefix_linkstate.ptr,
+ p->prefixlen),
+ p->prefixlen);
+ else
+ snprintf(str, size, "%s/%d",
+ bgp_linkstate_nlri_type_2str(
+ p->u.prefix_linkstate.nlri_type),
+ p->prefixlen);
+ break;
+
default:
strlcpy(str, "UNK prefix", size);
break;
@@ -1127,7 +1234,7 @@ const char *prefix2str(union prefixconstptr pu, char *str, int size)
static ssize_t prefixhost2str(struct fbuf *fbuf, union prefixconstptr pu)
{
const struct prefix *p = pu.p;
- char buf[PREFIX2STR_BUFFER];
+ char buf[PREFIX_STRLEN_EXTENDED];
switch (p->family) {
case AF_INET:
@@ -1139,6 +1246,17 @@ static ssize_t prefixhost2str(struct fbuf *fbuf, union prefixconstptr pu)
prefix_mac2str(&p->u.prefix_eth, buf, sizeof(buf));
return bputs(fbuf, buf);
+ case AF_LINKSTATE:
+ if (prefix_linkstate_display_hook)
+ prefix_linkstate_display_hook(
+ buf, sizeof(buf),
+ p->u.prefix_linkstate.nlri_type,
+ p->u.prefix_linkstate.ptr, p->prefixlen);
+ else
+ snprintf(buf, sizeof(buf), "%s",
+ bgp_linkstate_nlri_type_2str(
+ p->u.prefix_linkstate.nlri_type));
+ return bputs(fbuf, buf);
default:
return bprintfrr(fbuf, "{prefix.af=%dPF}", p->family);
}
@@ -1173,6 +1291,20 @@ const char *prefix_sg2str(const struct prefix_sg *sg, char *sg_str)
return sg_str;
}
+
+void prefix_linkstate_ptr_free(struct prefix *p)
+{
+ void *temp;
+
+ if (!p || p->family != AF_LINKSTATE || !p->u.prefix_linkstate.ptr)
+ return;
+
+ temp = (void *)p->u.prefix_linkstate.ptr;
+ XFREE(MTYPE_PREFIX_LINKSTATE, temp);
+ p->u.prefix_linkstate.ptr = (uintptr_t)NULL;
+}
+
+
struct prefix *prefix_new(void)
{
struct prefix *p;
@@ -1322,17 +1454,16 @@ char *prefix_mac2str(const struct ethaddr *mac, char *buf, int size)
unsigned prefix_hash_key(const void *pp)
{
struct prefix copy;
+ uint32_t len;
+ void *temp;
- if (((struct prefix *)pp)->family == AF_FLOWSPEC) {
- uint32_t len;
- void *temp;
+ /* make sure *all* unused bits are zero, particularly including
+ * alignment /
+ * padding and unused prefix bytes. */
+ memset(&copy, 0, sizeof(copy));
+ prefix_copy(&copy, (struct prefix *)pp);
- /* make sure *all* unused bits are zero,
- * particularly including alignment /
- * padding and unused prefix bytes.
- */
- memset(&copy, 0, sizeof(copy));
- prefix_copy(&copy, (struct prefix *)pp);
+ if (((struct prefix *)pp)->family == AF_FLOWSPEC) {
len = jhash((void *)copy.u.prefix_flowspec.ptr,
copy.u.prefix_flowspec.prefixlen,
0x55aa5a5a);
@@ -1340,12 +1471,13 @@ unsigned prefix_hash_key(const void *pp)
XFREE(MTYPE_PREFIX_FLOWSPEC, temp);
copy.u.prefix_flowspec.ptr = (uintptr_t)NULL;
return len;
+ } else if (((struct prefix *)pp)->family == AF_LINKSTATE) {
+ len = jhash((void *)copy.u.prefix_linkstate.ptr, copy.prefixlen,
+ 0x55aa5a5a);
+ prefix_linkstate_ptr_free(&copy);
+ return len;
}
- /* make sure *all* unused bits are zero, particularly including
- * alignment /
- * padding and unused prefix bytes. */
- memset(&copy, 0, sizeof(copy));
- prefix_copy(&copy, (struct prefix *)pp);
+
return jhash(&copy,
offsetof(struct prefix, u.prefix) + PSIZE(copy.prefixlen),
0x55aa5a5a);
@@ -1620,7 +1752,7 @@ static ssize_t printfrr_pfx(struct fbuf *buf, struct printfrr_eargs *ea,
if (host_only)
return prefixhost2str(buf, (struct prefix *)ptr);
else {
- char cbuf[PREFIX_STRLEN];
+ char cbuf[PREFIX_STRLEN_EXTENDED];
prefix2str(ptr, cbuf, sizeof(cbuf));
return bputs(buf, cbuf);
diff --git a/lib/prefix.h b/lib/prefix.h
index fc6e32dd54..f1aff43689 100644
--- a/lib/prefix.h
+++ b/lib/prefix.h
@@ -125,6 +125,15 @@ struct evpn_addr {
#define prefix_addr u._prefix_addr
};
+/* BGP Link-State NRLI types*/
+enum bgp_linkstate_nlri_type {
+ /* RFC7752 Table 1 */
+ BGP_LINKSTATE_NODE = 1,
+ BGP_LINKSTATE_LINK = 2,
+ BGP_LINKSTATE_PREFIX4 = 3, /* IPv4 Topology Prefix */
+ BGP_LINKSTATE_PREFIX6 = 4, /* IPv6 Topology Prefix */
+};
+
/*
* A struct prefix contains an address family, a prefix length, and an
* address. This can represent either a 'network prefix' as defined
@@ -158,12 +167,21 @@ struct evpn_addr {
#define AF_FLOWSPEC (AF_MAX + 2)
#endif
+#if !defined(AF_LINKSTATE)
+#define AF_LINKSTATE (AF_MAX + 3)
+#endif
+
struct flowspec_prefix {
uint8_t family;
uint16_t prefixlen; /* length in bytes */
uintptr_t ptr;
};
+struct linkstate_prefix {
+ uint16_t nlri_type;
+ uintptr_t ptr;
+};
+
/* FRR generic prefix structure. */
struct prefix {
uint8_t family;
@@ -182,6 +200,7 @@ struct prefix {
uintptr_t ptr;
struct evpn_addr prefix_evpn; /* AF_EVPN */
struct flowspec_prefix prefix_flowspec; /* AF_FLOWSPEC */
+ struct linkstate_prefix prefix_linkstate; /* AF_LINKSTATE */
} u __attribute__((aligned(8)));
};
@@ -279,6 +298,14 @@ struct prefix_fs {
struct flowspec_prefix prefix __attribute__((aligned(8)));
};
+
+/* Prefix for a BGP-LS entry */
+struct prefix_bgpls {
+ uint8_t family;
+ uint16_t prefixlen;
+ struct linkstate_prefix prefix __attribute__((aligned(8)));
+};
+
struct prefix_sg {
uint8_t family;
uint16_t prefixlen;
@@ -320,6 +347,11 @@ union prefixconstptr {
/* Maximum string length of the result of prefix2str */
#define PREFIX_STRLEN 80
+/* Maximum string length of the result of prefix2str for
+ * long string prefixes (eg. BGP Link-State)
+ */
+#define PREFIX_STRLEN_EXTENDED 512
+
/*
* Longest possible length of a (S,G) string is 34 bytes
* 123.123.123.123 = 15 * 2
@@ -376,6 +408,10 @@ static inline void ipv4_addr_copy(struct in_addr *dst,
#define s6_addr32 __u6_addr.__u6_addr32
#endif /*s6_addr32*/
+extern void prefix_set_linkstate_display_hook(
+ char *(*func)(char *buf, size_t size, uint16_t nlri_type, uintptr_t ptr,
+ uint16_t len));
+
/* Prototypes. */
extern int str2family(const char *string);
extern int afi2family(afi_t afi);
@@ -401,6 +437,8 @@ static inline afi_t prefix_afi(union prefixconstptr pu)
*/
extern unsigned int prefix_bit(const uint8_t *prefix, const uint16_t bit_index);
+extern void prefix_linkstate_ptr_free(struct prefix *p);
+
extern struct prefix *prefix_new(void);
extern void prefix_free(struct prefix **p);
/*
@@ -418,6 +456,7 @@ extern void prefix_mcast_inet4_dump(const char *onfail, struct in_addr addr,
extern const char *prefix_sg2str(const struct prefix_sg *sg, char *str);
extern const char *prefix2str(union prefixconstptr upfx, char *buffer,
int size);
+extern const char *bgp_linkstate_nlri_type_2str(uint16_t nlri_type);
extern int evpn_type5_prefix_match(const struct prefix *evpn_pfx,
const struct prefix *match_pfx);
extern int prefix_match(union prefixconstptr unet, union prefixconstptr upfx);
diff --git a/lib/table.c b/lib/table.c
index 3bf93894ec..dbfc3f8b91 100644
--- a/lib/table.c
+++ b/lib/table.c
@@ -142,7 +142,7 @@ static void route_common(const struct prefix *n, const struct prefix *p,
const uint8_t *pp;
uint8_t *newp;
- if (n->family == AF_FLOWSPEC)
+ if (n->family == AF_FLOWSPEC || n->family == AF_LINKSTATE)
return prefix_copy(new, p);
np = (const uint8_t *)&n->u.prefix;
pp = (const uint8_t *)&p->u.prefix;
@@ -281,15 +281,22 @@ struct route_node *route_node_get(struct route_table *table,
const uint8_t *prefix = &p->u.prefix;
node = rn_hash_node_find(&table->hash, &search);
- if (node && node->info)
+ if (node && node->info) {
+ if (family2afi(p->family) == AFI_LINKSTATE)
+ prefix_linkstate_ptr_free(p);
+
return route_lock_node(node);
+ }
match = NULL;
node = table->top;
while (node && node->p.prefixlen <= prefixlen
&& prefix_match(&node->p, p)) {
- if (node->p.prefixlen == prefixlen)
+ if (node->p.prefixlen == prefixlen) {
+ if (family2afi(p->family) == AFI_LINKSTATE)
+ prefix_linkstate_ptr_free(p);
return route_lock_node(node);
+ }
match = node;
node = node->link[prefix_bit(prefix, node->p.prefixlen)];
@@ -324,6 +331,9 @@ struct route_node *route_node_get(struct route_table *table,
table->count++;
route_lock_node(new);
+ if (family2afi(p->family) == AFI_LINKSTATE)
+ prefix_linkstate_ptr_free(p);
+
return new;
}
diff --git a/lib/zebra.h b/lib/zebra.h
index ecc87f58f1..cd0b72834c 100644
--- a/lib/zebra.h
+++ b/lib/zebra.h
@@ -326,13 +326,14 @@ struct in_pktinfo {
#define INADDR_LOOPBACK 0x7f000001 /* Internet address 127.0.0.1. */
#endif
-/* Address family numbers from RFC1700. */
+/* Address family numbers. */
typedef enum {
AFI_UNSPEC = 0,
AFI_IP = 1,
AFI_IP6 = 2,
AFI_L2VPN = 3,
- AFI_MAX = 4
+ AFI_LINKSTATE = 4, /* BGP-LS RFC 7752 */
+ AFI_MAX = 5,
} afi_t;
#define IS_VALID_AFI(a) ((a) > AFI_UNSPEC && (a) < AFI_MAX)
@@ -347,7 +348,9 @@ typedef enum {
SAFI_EVPN = 5,
SAFI_LABELED_UNICAST = 6,
SAFI_FLOWSPEC = 7,
- SAFI_MAX = 8
+ SAFI_LINKSTATE = 8, /* BGP-LS RFC 7752 */
+ SAFI_LINKSTATE_VPN = 9, /* BGP-LS RFC 7752 */
+ SAFI_MAX = 10,
} safi_t;
#define FOREACH_AFI_SAFI(afi, safi) \
diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c
index ee17a193f4..e8a49b3176 100644
--- a/pbrd/pbr_zebra.c
+++ b/pbrd/pbr_zebra.c
@@ -335,6 +335,11 @@ void route_add(struct pbr_nexthop_group_cache *pnhgc, struct nexthop_group nhg,
"%s: Asked to install unsupported route type: L2VPN",
__func__);
break;
+ case AFI_LINKSTATE:
+ DEBUGD(&pbr_dbg_zebra,
+ "%s: Asked to install unsupported route type: Link-State",
+ __func__);
+ break;
case AFI_UNSPEC:
DEBUGD(&pbr_dbg_zebra,
"%s: Asked to install unspecified route type", __func__);
@@ -380,6 +385,11 @@ void route_delete(struct pbr_nexthop_group_cache *pnhgc, afi_t afi)
"%s: Asked to delete unsupported route type: L2VPN",
__func__);
break;
+ case AFI_LINKSTATE:
+ DEBUGD(&pbr_dbg_zebra,
+ "%s: Asked to delete unsupported route type: Link-State",
+ __func__);
+ break;
case AFI_UNSPEC:
DEBUGD(&pbr_dbg_zebra,
"%s: Asked to delete unspecified route type", __func__);
diff --git a/staticd/static_vty.c b/staticd/static_vty.c
index b07878f063..4afc250493 100644
--- a/staticd/static_vty.c
+++ b/staticd/static_vty.c
@@ -131,6 +131,7 @@ static int static_route_nb_run(struct vty *vty, struct static_route_args *args)
assert(!!str2prefix(args->source, &src));
break;
case AFI_L2VPN:
+ case AFI_LINKSTATE:
case AFI_UNSPEC:
case AFI_MAX:
break;
diff --git a/tests/bgpd/test_peer_attr.c b/tests/bgpd/test_peer_attr.c
index bc6eba9069..761b23bf97 100644
--- a/tests/bgpd/test_peer_attr.c
+++ b/tests/bgpd/test_peer_attr.c
@@ -660,6 +660,8 @@ static const char *str_from_afi(afi_t afi)
return "ipv6";
case AFI_L2VPN:
return "l2vpn";
+ case AFI_LINKSTATE:
+ return "link-state";
case AFI_MAX:
case AFI_UNSPEC:
return "bad-value";
diff --git a/tests/topotests/bgp_linkstate_topo1/__init__.py b/tests/topotests/bgp_linkstate_topo1/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp_linkstate_topo1/__init__.py
diff --git a/tests/topotests/bgp_linkstate_topo1/r1/bgp_injector.cfg b/tests/topotests/bgp_linkstate_topo1/r1/bgp_injector.cfg
new file mode 100644
index 0000000000..3e4ff6cde3
--- /dev/null
+++ b/tests/topotests/bgp_linkstate_topo1/r1/bgp_injector.cfg
@@ -0,0 +1,215 @@
+// Check content with
+// cat bgp_injector.cfg | sed -e 's|//.*||g' | jq .
+{
+"my_as": 65001,
+"hold_time": 30,
+"bgp_identifier": "192.0.2.1",
+"local_address": "192.0.2.1",
+"peer_address": "192.0.2.2",
+"mss": 4000,
+"port": 179,
+"path_attributes":
+{
+ "as-path": "65001",
+ "next-hop": "192.0.2.1",
+ "origin": 0
+},
+"link_states":
+[
+ {
+ "nlri":
+ {
+ "proto": "01", // IS-IS L1
+ "id": "0000000000000020",
+ "type": "0002", // Link-NLRI
+ "256": { // Local Link-Node Descriptor TLV
+ "512": "0000fde9", // AS 65001
+ "513": "00000000", // BGP-LS ID
+ "515": "000000001001" // router-id: 0000.0000.1001
+ },
+ "257": { // Remote Link-Node Descriptor TLV
+ "512": "0000fde9", // AS 65001
+ "513": "00000000", // BGP-LS ID
+ "515": "000000001000" // router-id: 0000.0000.1000
+ },
+ "259": "0a010001", // IPv4 interface address TLV
+ "260": "0a010002", // IPv4 Neighbor address TLV
+ "261": "20010000000000000000000000000001", // IPv6 interface address TLV
+ "262": "20010000000000000000000000000002", // IPv6 Neighbor address TLV
+ "263": "00000002" // MT-ID
+ },
+ "attr":
+ {
+ "1028": "01010101", //IPv4 Router-ID of Local Node TLV
+ "1030": "0a0a0a0a", //IPv4 Router-ID of Remote Node TLV
+ "1089": "4d2817c8", // Maximum link bandwidth TLV 1410.07 Mbps
+ "1090": "4d2817c8", // Maximum reservable link bandwidth TLV 1410.07 Mbps
+ "1091": "4d2817c84d2817c84d2817c84d2817c84d2817c84d2817c84d2817c84d2817c8", // Unreserved bandwidth TLV
+ "1092": "00000064", // TE Default Metric TLV
+ "1095": "00000a", // Metric TLV
+ // Adjacency SID TLV
+ // Flags: 0x30, Value Flag (V), Local Flag (L)
+ // Weight: 0
+ // .... 0000 0011 1010 1001 1000 = SID/Label: 15000
+ "1099": "30000000003a98",
+ //Unidirectional Link Delay TLV
+ // TE Metric Flags: 0x00
+ // Delay: 8500
+ "1114": "00002134",
+ //Min/Max Unidirectional Link Delay TLV
+ // TE Metric Flags: 0x00
+ // Min Delay: 8000
+ // Reserved: 0x00
+ // Max Delay: 9000
+ "1115": "00001f4000002328",
+ "1122": { //Application-Specific Link Attributes TLV
+ // Type: 1122
+ // Length: 48
+ // SABM Length: 4
+ // UDABM Length: 4
+ // Reserved: 0x0000
+ // Standard Application Identifier Bit Mask: 0x10000000, Flexible Algorithm (X)
+ // User-Defined Application Identifier Bit Mask: 00 00 00 00
+ "0": "040400001000000000000000", // 0 means encode data directly
+ "1088": "00000001", // Administrative group (color) TLV
+ "1092": "00000064", // TE Default Metric TLV
+ "1115": "00001f4000002328", // Min/Max Unidirectional Link Delay TLV
+ "1173": "00000001"// Extended Administrative Group TLV
+ }
+ }
+ },
+ {
+ "nlri":
+ {
+ "proto": "01", // IS-IS L1
+ "id": "0000000000000020",
+ "type": "0001", // Node-NLRI
+ "256": { // Local Link-Node Descriptor TLV
+ "512": "0000fde9", // AS 65001
+ "513": "00000000", // BGP-LS ID
+ "515": "00000000100300" // router-id: 0000.0000.1003.00
+ }
+ },
+ "attr":
+ {
+ "0": "0107000400000002010a00020108040200027233040300034910000404000403030303040a000cc000000fa004890003004e20040b0003008082040c000c00000003e804890003003a98"
+ }
+ },
+ {
+ "nlri":
+ {
+ "proto": "03", // OSPFv2
+ "id": "0000000000000020",
+ "type": "0001", // Node-NLRI
+ "256": { // Local Link-Node Descriptor TLV
+ "512": "0000fde9", // AS 65001
+ "513": "00000000", // BGP-LS ID
+ "514": "00000000", // Area 0
+ "515": "0a0a0a0a" // router-id: 10.10.10.10
+ }
+ },
+ "attr": // Attributes to test display
+ {
+ "1152": "80", // IGP flags
+ "1097": "000b00020123000500020123", //opaque Link TLV
+ "1105": "d123ed", // RTM capabilities
+ "1172": "dc12fceb04400004000f0001", // L2 bundle member attributes
+ "1117": "00FFFFFF", //Packet Loss 50.331642%
+ "1039": "820000800411000400000001041500040000000a0413000480000000", //flex-algo definition
+ "1044": "82800000000003e8", // flex-algo prefix metric
+ "1121": "", // graceful link shutdown
+ "1171": "00120012000000000000000000120012", // source router IPv6
+ "1871": "00120012000000000000000000" // Unknown TLV
+ }
+ },
+ {
+ "nlri":
+ {
+ "proto": "03", // OSPFv2
+ "id": "0000000000000020",
+ "type": "0001", // Node-NLRI
+ "256": { // Local Link-Node Descriptor TLV
+ "512": "0000fde9", // AS 65001
+ "513": "00000000", // BGP-LS ID
+ "514": "00000000", // Area 0
+ "515": "0a0a0a0a01010101" // router-id: 10.10.10.10:1.1.1.1
+ }
+ }
+ },
+ {
+ "nlri":
+ {
+ "proto": "03", // OSPFv2
+ "id": "0000000000000020",
+ "type": "0003", // IPv4-topo-prefix-NLRI
+ "256": { // Local Link-Node Descriptor TLV
+ "512": "0000fde9", // AS 65001
+ "513": "00000000", // BGP-LS ID
+ "514": "00000000", // Area 0
+ "515": "0a0a0a0a01010101" // router-id: 10.10.10.10:1.1.1.1
+ },
+ "265": "18590a0b" // IP Reachability Information TLV (89.10.11.0/24)
+ }
+ },
+ {
+ "nlri":
+ {
+ "proto": "02", // IS-IS L2
+ "id": "0000000000000020",
+ "type": "0004", // IPv6-topo-prefix-NLRI
+ "256": { // Local Link-Node Descriptor TLV
+ "512": "0000fde9", // AS 65001
+ "513": "00000000", // BGP-LS ID
+ "515": "00000000100300" // router-id: 0000.0000.1003.00
+ },
+ "263": "0002", // MT-ID
+ // IP Reachability Information TLV (12:12::12:12/128)
+ "265": "8000120012000000000000000000120012"
+ }
+ },
+ {
+ "nlri":
+ {
+ "proto": "06", // OSPFv3
+ "id": "0000000000000020",
+ "type": "0004", // IPv6-topo-prefix-NLRI
+ "256": { // Local Link-Node Descriptor TLV
+ "512": "0000fde9", // AS 65001
+ "513": "00000000", // BGP-LS ID
+ "514": "00000000", // Area 0
+ "515": "0a0a0a0a" // router-id: 10.10.10.10
+ },
+ "263": "0002", // MT-ID
+ "264": "01", // OSPF: route-type Intra-Area (0x1)
+ // IP Reachability Information TLV (12:12::12:12/128)
+ "265": "8000120012000000000000000000120012"
+ }
+ },
+ {
+ "nlri":
+ {
+ "proto": "06", // OSPFv3
+ "id": "ffffffffffffffff",
+ "type": "0002", // Link-NLRI
+ "256": { // Local Link-Node Descriptor TLV
+ "512": "ffffffff", // AS
+ "513": "ffffffff", // BGP-LS ID
+ "514": "ffffffff", // OSPF area ID
+ "515": "0a0a0a0b02020202" // router-id: 10.10.10.11:2.2.2.2
+ },
+ "257": { // Remote Link-Node Descriptor TLV
+ "512": "ffffffff", // AS
+ "513": "ffffffff", // BGP-LS ID
+ "514": "ffffffff", // OSPF area ID
+ "515": "0a0a0a0a01010101" // router-id: 10.10.10.10:1.1.1.1
+ },
+ "259": "0a010001", // IPv4 interface address TLV
+ "260": "0a010002", // IPv4 Neighbor address TLV
+ "261": "20010000000000000000000000000001", // IPv6 interface address TLV
+ "262": "20010000000000000000000000000002", // IPv6 Neighbor address TLV
+ "263": "00000002", // MT-ID
+ "424": "200100000000000001" // unknown TLV
+ }
+ }
+]
+}
diff --git a/tests/topotests/bgp_linkstate_topo1/r1/bgp_injector.py b/tests/topotests/bgp_linkstate_topo1/r1/bgp_injector.py
new file mode 100755
index 0000000000..314b8fe44c
--- /dev/null
+++ b/tests/topotests/bgp_linkstate_topo1/r1/bgp_injector.py
@@ -0,0 +1,596 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: MIT
+
+#
+# Copyright 2018 Jorge Borreicho
+# Copyright 2023 6WIND S.A.
+
+"""
+ BGP prefix injection tool
+"""
+
+import socket
+import sys
+import time
+from datetime import datetime
+import struct
+import threading
+import json
+import os
+import re
+import signal
+import errno
+
+
+AFI_IPV4 = 1
+SAFI_UNICAST = 1
+
+AFI_LINKSTATE = 16388
+SAFI_LINKSTATE = 71
+
+saved_pid = False
+global pid_file
+
+class Unbuffered(object):
+ def __init__(self, stream):
+ self.stream = stream
+ def write(self, data):
+ self.stream.write(data)
+ self.stream.flush()
+ def writelines(self, datas):
+ self.stream.writelines(datas)
+ self.stream.flush()
+ def __getattr__(self, attr):
+ return getattr(self.stream, attr)
+
+def keepalive_thread(conn, interval):
+
+ # infinite loop so that function do not terminate and thread do not end.
+ while True:
+ time.sleep(interval)
+ keepalive_bgp(conn)
+
+
+def receive_thread(conn):
+
+ # infinite loop so that function do not terminate and thread do not end.
+ while True:
+
+ # Receiving from client
+ r = conn.recv(1500)
+ while True:
+ start_ptr = (
+ r.find(
+ b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+ )
+ + 16
+ )
+ end_ptr = (
+ r[16:].find(
+ b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+ )
+ + 16
+ )
+ if (
+ start_ptr >= end_ptr
+ ): # a single message was sent in the BGP packet OR it is the last message of the BGP packet
+ decode_bgp(r[start_ptr:])
+ break
+ else: # more messages left to decode
+ decode_bgp(r[start_ptr:end_ptr])
+ r = r[end_ptr:]
+
+
+def decode_bgp(msg):
+ if len(msg) < 3:
+ return
+ msg_length, msg_type = struct.unpack("!HB", msg[0:3])
+ if msg_type == 4:
+ # print(timestamp + " - " + "Received KEEPALIVE") #uncomment to debug
+ pass
+ elif msg_type == 2:
+ timestamp = str(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
+ print(timestamp + " - " + "Received UPDATE")
+ elif msg_type == 1:
+ version, remote_as, holdtime, i1, i2, i3, i4, opt_length = struct.unpack(
+ "!BHHBBBBB", msg[3:13]
+ )
+ timestamp = str(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
+ print(timestamp + " - " + "Received OPEN")
+ print()
+ print(
+ "--> Version:"
+ + str(version)
+ + ", Remote AS: "
+ + str(remote_as)
+ + ", Hold Time:"
+ + str(holdtime)
+ + ", Remote ID: "
+ + str(i1)
+ + "."
+ + str(i2)
+ + "."
+ + str(i3)
+ + "."
+ + str(i4)
+ )
+ print()
+ elif msg_type == 3:
+ timestamp = str(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
+ print(timestamp + " - " + "Received NOTIFICATION")
+
+
+def multiprotocol_capability(afi, safi):
+ hexstream = bytes.fromhex("02060104")
+ hexstream += struct.pack("!H", afi)
+ hexstream += struct.pack("!B", 0)
+ hexstream += struct.pack("!B", safi)
+
+ return hexstream
+
+
+def open_bgp(conn, config):
+
+ # Build the BGP Message
+ bgp_version = b"\x04"
+ bgp_as = struct.pack("!H", config["my_as"])
+ bgp_hold_time = struct.pack("!H", config["hold_time"])
+
+ octet = config["bgp_identifier"].split(".")
+ bgp_identifier = struct.pack(
+ "!BBBB", int(octet[0]), int(octet[1]), int(octet[2]), int(octet[3])
+ )
+
+ bgp_opt = b""
+ bgp_opt += multiprotocol_capability(AFI_IPV4, SAFI_UNICAST)
+ bgp_opt += multiprotocol_capability(AFI_LINKSTATE, SAFI_LINKSTATE)
+
+ bgp_opt_lenght = struct.pack("!B", len(bgp_opt))
+
+ bgp_message = (
+ bgp_version + bgp_as + bgp_hold_time + bgp_identifier + bgp_opt_lenght + bgp_opt
+ )
+
+ # Build the BGP Header
+ total_length = len(bgp_message) + 16 + 2 + 1
+ bgp_marker = b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+ bgp_length = struct.pack("!H", total_length)
+ bgp_type = b"\x01"
+ bgp_header = bgp_marker + bgp_length + bgp_type
+
+ bgp_packet = bgp_header + bgp_message
+
+ conn.send(bgp_packet)
+ return 0
+
+
+def keepalive_bgp(conn):
+
+ # Build the BGP Header
+ total_length = 16 + 2 + 1
+ bgp_marker = b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+ bgp_length = struct.pack("!H", total_length)
+ bgp_type = b"\x04"
+ bgp_header = bgp_marker + bgp_length + bgp_type
+
+ bgp_packet = bgp_header
+
+ conn.send(bgp_packet)
+ return 0
+
+
+def encode_ipv4_prefix(address, netmask):
+
+ octet = address.split(".")
+ length = struct.pack("!B", int(netmask))
+
+ if int(netmask) <= 8:
+ prefix = struct.pack("!B", int(octet[0]))
+ elif int(netmask) <= 16:
+ prefix = struct.pack("!BB", int(octet[0]), int(octet[1]))
+ elif int(netmask) <= 24:
+ prefix = struct.pack("!BBB", int(octet[0]), int(octet[1]), int(octet[2]))
+ else:
+ prefix = struct.pack(
+ "!BBBB", int(octet[0]), int(octet[1]), int(octet[2]), int(octet[3])
+ )
+
+ return length + prefix
+
+
+def encode_path_attribute_mp_reach_nrli(afi, safi, data, config):
+ hexstream = b""
+ hexstream += b"\x90" # flags optional, extended
+ hexstream += struct.pack("!B", 14) # type code MP_REACH_NLRI
+
+ hexstream2 = b""
+ hexstream2 += struct.pack("!H", afi)
+ hexstream2 += struct.pack("!B", safi)
+ hexstream2 += struct.pack("!B", 4) # nexthop length
+ hexstream2 += socket.inet_aton(config["local_address"]) # nexthop IPv4
+ hexstream2 += b"\x00" # SNPA
+ hexstream2 += data
+
+ hexstream += struct.pack("!H", len(hexstream2)) # length
+ hexstream += hexstream2
+
+ return hexstream
+
+
+def encode_path_attribute_linkstate(data):
+ hexstream = b""
+ hexstream += b"\x80" # flags optional
+ hexstream += struct.pack("!B", 29) # type code BGP-LS
+ hexstream += struct.pack("!B", len(data)) # length
+ hexstream += data
+
+ return hexstream
+
+
+def encode_path_attribute(type, value):
+
+ path_attributes = {
+ "origin": [b"\x40", 1],
+ "as-path": [b"\x40", 2],
+ "next-hop": [b"\x40", 3],
+ "med": [b"\x80", 4],
+ "local_pref": [b"\x40", 5],
+ "communities": [b"\xc0", 8],
+ }
+
+ attribute_flag = path_attributes[type][0]
+ attribute_type_code = struct.pack("!B", int(path_attributes[type][1]))
+
+ if type == "origin":
+ attribute_value = struct.pack("!B", value)
+ elif type == "as-path":
+ as_number_list = value.split(" ")
+ attribute_value = struct.pack("!BB", 2, len(as_number_list))
+ for as_number in as_number_list:
+ attribute_value += struct.pack("!H", int(as_number))
+ elif type == "next-hop":
+ octet = value.split(".")
+ attribute_value = struct.pack(
+ "!BBBB", int(octet[0]), int(octet[1]), int(octet[2]), int(octet[3])
+ )
+ elif type == "med":
+ attribute_value = struct.pack("!I", value)
+ elif type == "local_pref":
+ attribute_value = struct.pack("!I", value)
+ elif type == "communities":
+ communities_list = value.split(" ")
+ attribute_value = b""
+ for community in communities_list:
+ aux = community.split(":")
+ attribute_value += struct.pack("!HH", int(aux[0]), int(aux[1]))
+
+ attribute_length = struct.pack("!B", len(attribute_value))
+
+ return attribute_flag + attribute_type_code + attribute_length + attribute_value
+
+
+def encode_tlvs(tlvs):
+ stream = b""
+ for key, tlv_data in tlvs.items():
+ if isinstance(key, str) and key.isdigit():
+ tlv_type = int(key)
+ else:
+ # key is not a TLV
+ continue
+ if isinstance(tlv_data, str):
+ if tlv_type != 0:
+ # TLV type 0 is fake TLV
+ stream += struct.pack("!H", tlv_type)
+ stream += struct.pack("!H", len(bytes.fromhex(tlv_data)))
+ stream += bytes.fromhex(tlv_data)
+ elif isinstance(tlv_data, dict):
+ # TLV contains sub-TLV
+ stream += struct.pack("!H", tlv_type)
+
+ stream_subtlv = encode_tlvs(tlv_data)
+ stream += struct.pack("!H", len(stream_subtlv))
+ stream += stream_subtlv
+ else:
+ # invalid input
+ assert 0
+
+ return stream
+
+
+def encode_linkstate_nrli_tlv(nlri):
+ stream = b""
+ stream += bytes.fromhex(nlri["type"])
+
+ stream2 = b""
+ stream2 += bytes.fromhex(nlri["proto"])
+ stream2 += bytes.fromhex(nlri["id"])
+ stream2 += encode_tlvs(nlri)
+
+ stream += struct.pack("!H", len(stream2))
+ stream += stream2
+
+ return stream
+
+
+def update_bgp(conn, link_state, config):
+
+ # Build the BGP Message
+
+ # Expired Routes
+ # 1 - Withdrawn Routes
+
+ bgp_withdrawn_routes = b""
+ max_length_reached = False
+
+ bgp_withdrawn_routes_length = struct.pack("!H", len(bgp_withdrawn_routes))
+ bgp_withdrawn_routes = bgp_withdrawn_routes_length + bgp_withdrawn_routes
+
+ # New Routes
+ # 2 - Path Attributes
+
+ path_attributes = config["path_attributes"]
+ bgp_mss = config["mss"]
+
+ bgp_total_path_attributes = b""
+
+ # encode link-state MP_REACH NLRI
+ data = encode_linkstate_nrli_tlv(link_state["nlri"])
+ bgp_total_path_attributes += encode_path_attribute_mp_reach_nrli(
+ AFI_LINKSTATE, SAFI_LINKSTATE, data, config
+ )
+
+ # encode classic attributes
+ for key in path_attributes.keys():
+ bgp_total_path_attributes += encode_path_attribute(key, path_attributes[key])
+
+ # encode link-state attributes
+ if "attr" in link_state:
+ data = encode_tlvs(link_state["attr"])
+ else:
+ data = b""
+ bgp_total_path_attributes += encode_path_attribute_linkstate(data)
+
+ bgp_total_path_attributes_length = struct.pack("!H", len(bgp_total_path_attributes))
+ bgp_total_path_attributes = (
+ bgp_total_path_attributes_length + bgp_total_path_attributes
+ )
+
+ # 3- Network Layer Reachability Information (NLRI)
+
+ bgp_new_routes = b""
+
+ bgp_message = bgp_withdrawn_routes + bgp_total_path_attributes + bgp_new_routes
+
+ # Build the BGP Header
+ total_length = len(bgp_message) + 16 + 2 + 1
+ bgp_marker = b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+ bgp_length = struct.pack("!H", total_length)
+ bgp_type = b"\x02"
+ bgp_header = bgp_marker + bgp_length + bgp_type
+
+ bgp_packet = bgp_header + bgp_message
+
+ conn.send(bgp_packet)
+
+ timestamp = str(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
+ print(timestamp + " - " + "Sent UPDATE")
+
+ return 0
+
+
+def str2ip(ip_str):
+ s_octet = ip_str.split(".")
+ ip_addr = struct.pack(
+ "!BBBB", int(s_octet[0]), int(s_octet[1]), int(s_octet[2]), int(s_octet[3])
+ )
+ return ip_addr
+
+
+def check_pid(pid):
+ if pid < 0: # user input error
+ return False
+ if pid == 0: # all processes
+ return False
+ try:
+ os.kill(pid, 0)
+ return True
+ except OSError as err:
+ if err.errno == errno.EPERM: # a process we were denied access to
+ return True
+ if err.errno == errno.ESRCH: # No such process
+ return False
+ # should never happen
+ return False
+
+
+def savepid():
+ ownid = os.getpid()
+
+ flags = os.O_CREAT | os.O_EXCL | os.O_WRONLY
+ mode = ((os.R_OK | os.W_OK) << 6) | (os.R_OK << 3) | os.R_OK
+
+ try:
+ fd = os.open(pid_file, flags, mode)
+ except OSError:
+ try:
+ pid = open(pid_file, "r").readline().strip()
+ if check_pid(int(pid)):
+ sys.stderr.write(
+ "PIDfile already exists and program still running %s\n" % pid_file
+ )
+ return False
+ else:
+ # If pid is not running, reopen file without O_EXCL
+ fd = os.open(pid_file, flags ^ os.O_EXCL, mode)
+ except (OSError, IOError, ValueError):
+ sys.stderr.write(
+ "issue accessing PID file %s (most likely permission or ownership)\n"
+ % pid_file
+ )
+ return False
+
+ try:
+ f = os.fdopen(fd, "w")
+ line = "%d\n" % ownid
+ f.write(line)
+ f.close()
+ saved_pid = True
+ except IOError:
+ sys.stderr.write("Can not create PIDfile %s\n" % pid_file)
+ return False
+ print("Created PIDfile %s with value %d\n" % (pid_file, ownid))
+ return True
+
+
+def removepid():
+ if not saved_pid:
+ return
+ try:
+ os.remove(pid_file)
+ except OSError as exc:
+ if exc.errno == errno.ENOENT:
+ pass
+ else:
+ sys.stderr.write("Can not remove PIDfile %s\n" % pid_file)
+ return
+ sys.stderr.write("Removed PIDfile %s\n" % pid_file)
+
+
+def daemonize():
+ try:
+ pid = os.fork()
+ if pid > 0:
+ # Exit first parent
+ sys.exit(0)
+ except OSError as e:
+ print("Fork #1 failed: %d (%s)" % (e.errno, e.strerror))
+ sys.exit(1)
+
+ # Decouple from parent environment
+ os.chdir("/")
+ os.setsid()
+ os.umask(0)
+
+ # Do second fork
+ try:
+ pid = os.fork()
+ if pid > 0:
+ # Exit from second parent
+ sys.exit(0)
+ except OSError as e:
+ print("Fork #2 failed: %d (%s)" % (e.errno, e.strerror))
+ sys.exit(1)
+
+ # Redirect standard file descriptors
+ sys.stdout.flush()
+ sys.stderr.flush()
+ si = open(os.devnull, "r")
+ so = open(os.devnull, "a+")
+ se = open(os.devnull, "a+")
+
+ os.dup2(si.fileno(), sys.stdin.fileno())
+ os.dup2(so.fileno(), sys.stdout.fileno())
+ os.dup2(se.fileno(), sys.stderr.fileno())
+
+
+def term(signal, frame):
+ timestamp = str(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
+ print(timestamp + " - " + "^C received, shutting down.\n")
+ bgp_socket.close()
+ removepid()
+ exit()
+
+
+if __name__ == "__main__":
+ if len(sys.argv) > 1:
+ # daemonize and log to file
+ daemonize()
+ pid_file = os.path.join(sys.argv[1], "bgp_injector.pid")
+ savepid()
+ # deal with daemon termination
+ signal.signal(signal.SIGTERM, term)
+ signal.signal(signal.SIGINT, term) # CTRL + C
+
+ log_dir = os.path.join(sys.argv[1], "bgp_injector.log")
+ f = open(log_dir, 'w')
+ sys.stdout = Unbuffered(f)
+ sys.stderr = Unbuffered(f)
+
+ timestamp = str(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
+ print(timestamp + " - " + "Starting BGP injector ")
+
+ CONFIG_FILENAME = os.path.join(sys.path[0], "bgp_injector.cfg")
+
+ timestamp = str(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
+ print(timestamp + " - " + "Reading config file " + CONFIG_FILENAME)
+
+ input_file = open(CONFIG_FILENAME, "r")
+
+ input = input_file.read()
+ # cleanup comments that are not supported by JSON
+ json_input = re.sub(r"//.*\n", "", input, flags=re.MULTILINE)
+
+ config = json.loads(json_input)
+
+ bgp_peer = config["peer_address"]
+ bgp_local = config["local_address"]
+ bgp_mss = config["mss"]
+ bgp_port = config["port"]
+ rib = dict()
+ timestamp = str(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
+ print(timestamp + " - " + "Starting BGP... (peer: " + str(bgp_peer) + ")")
+
+ retry = 30
+ while retry:
+ retry -= 1
+ try:
+ bgp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ bgp_socket.bind((bgp_local, 0))
+ bgp_socket.connect((bgp_peer, bgp_port))
+ open_bgp(bgp_socket, config)
+ break
+ except TimeoutError:
+ if retry == 0:
+ timestamp = str(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
+ print(timestamp + " - " + "Error: timeout connecting to the peer.")
+ exit()
+ time.sleep(1)
+ except OSError as e:
+ if retry == 0:
+ timestamp = str(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
+ print(timestamp + " - " + "Error: cannot connect to the peer: " + str(e))
+ exit()
+ time.sleep(1)
+
+ receive_worker = threading.Thread(
+ target=receive_thread, args=(bgp_socket,)
+ ) # wait from BGP msg from peer and process them
+ receive_worker.setDaemon(True)
+ receive_worker.start()
+
+ keepalive_worker = threading.Thread(
+ target=keepalive_thread,
+ args=(
+ bgp_socket,
+ (config["hold_time"]) / 3,
+ ),
+ ) # send keep alives every 10s by default
+ keepalive_worker.setDaemon(True)
+ keepalive_worker.start()
+
+ # send a first keepalive packet before sending the initial UPDATE packet
+ keepalive_bgp(bgp_socket)
+
+ timestamp = str(datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
+ print(timestamp + " - " + "BGP is up.")
+
+ time.sleep(3)
+ for link_state in config["link_states"]:
+ update_bgp(
+ bgp_socket,
+ link_state,
+ config,
+ )
+
+ while True:
+ time.sleep(60)
diff --git a/tests/topotests/bgp_linkstate_topo1/r1/staticd.conf b/tests/topotests/bgp_linkstate_topo1/r1/staticd.conf
new file mode 100644
index 0000000000..7f2f057bfe
--- /dev/null
+++ b/tests/topotests/bgp_linkstate_topo1/r1/staticd.conf
@@ -0,0 +1 @@
+ip route 192.0.2.2/32 192.168.1.2
diff --git a/tests/topotests/bgp_linkstate_topo1/r1/zebra.conf b/tests/topotests/bgp_linkstate_topo1/r1/zebra.conf
new file mode 100644
index 0000000000..3307c123f8
--- /dev/null
+++ b/tests/topotests/bgp_linkstate_topo1/r1/zebra.conf
@@ -0,0 +1,7 @@
+!
+interface lo
+ ip address 192.0.2.1/32
+!
+interface r1-eth0
+ ip address 192.168.1.1/24
+! \ No newline at end of file
diff --git a/tests/topotests/bgp_linkstate_topo1/r2/bgpd.conf b/tests/topotests/bgp_linkstate_topo1/r2/bgpd.conf
new file mode 100644
index 0000000000..26ffee9c1b
--- /dev/null
+++ b/tests/topotests/bgp_linkstate_topo1/r2/bgpd.conf
@@ -0,0 +1,20 @@
+router bgp 65002
+ no bgp ebgp-requires-policy
+ neighbor 192.0.2.1 remote-as 65001
+ neighbor 192.0.2.1 timers connect 1
+ neighbor 192.0.2.1 ebgp-multihop 3
+ neighbor 192.0.2.1 update-source 192.0.2.2
+ neighbor 192.0.2.3 remote-as 65003
+ neighbor 192.0.2.3 timers 1 3
+ neighbor 192.0.2.3 timers connect 1
+ neighbor 192.0.2.3 ebgp-multihop 3
+ neighbor 192.0.2.3 update-source 192.0.2.2
+ address-family ipv4 unicast
+ no neighbor 192.0.2.1 activate
+ no neighbor 192.0.2.3 activate
+ exit-address-family
+ address-family link-state link-state
+ neighbor 192.0.2.1 activate
+ neighbor 192.0.2.3 activate
+ exit-address-family
+! \ No newline at end of file
diff --git a/tests/topotests/bgp_linkstate_topo1/r2/linkstate.json b/tests/topotests/bgp_linkstate_topo1/r2/linkstate.json
new file mode 100644
index 0000000000..1e653c5531
--- /dev/null
+++ b/tests/topotests/bgp_linkstate_topo1/r2/linkstate.json
@@ -0,0 +1,189 @@
+{
+ "routes": {
+ "Link OSPFv3 ID:0xffffffffffffffff {Local {AS:4294967295 ID:4294967295 Area:4294967295 Rtr:10.10.10.11:2.2.2.2} Remote {AS:4294967295 ID:4294967295 Area:4294967295 Rtr:10.10.10.10:1.1.1.1} IPv4:10.1.0.1 Neigh-IPv4:10.1.0.2 IPv6:2001::1 Neigh-IPv6:2001::2 MT:0,2 424:0x200100000000000001}/XX": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "linkStateNLRI": {
+ "nlriType": "Link",
+ "protocol": "OSPFv3",
+ "identifier": "0xffffffffffffffff",
+ "localNode": {
+ "as": 4294967295,
+ "identifier": 4294967295,
+ "area": 4294967295,
+ "routerID": "10.10.10.11:2.2.2.2"
+ },
+ "remoteNode": {
+ "as": 4294967295,
+ "identifier": 4294967295,
+ "area": 4294967295,
+ "routerID": "10.10.10.10:1.1.1.1"
+ },
+ "interfaceIPv4": "10.1.0.1",
+ "neighborIPv4": "10.1.0.2",
+ "interfaceIPv6": "2001::1",
+ "neighborIPv6": "2001::2",
+ "mtID": [0, 2],
+ "424": ["0x2001000000000000", "0x01"]
+ },
+ "weight": 0,
+ "origin": "IGP"
+ }
+ ],
+ "IPv6-Prefix OSPFv3 ID:0x20 {Local {AS:65001 ID:0 Area:0 Rtr:10.10.10.10} MT:2 OSPF-Route-Type:1 IPv6:12:12::12:12/128}/XX": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "linkStateNLRI": {
+ "nlriType": "IPv6-Prefix",
+ "protocol": "OSPFv3",
+ "identifier": "0x20",
+ "localNode": {
+ "as": 65001,
+ "identifier": 0,
+ "area": 0,
+ "routerID": "10.10.10.10"
+ },
+ "ospfRouteType": 1,
+ "ipReachability": "12:12::12:12/128",
+ "mtID": [2]
+ },
+ "weight": 0,
+ "origin": "IGP"
+ }
+ ],
+ "IPv6-Prefix ISIS-L2 ID:0x20 {Local {AS:65001 ID:0 Rtr:0000.0000.1003.00} MT:2 IPv6:12:12::12:12/128}/XX": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "linkStateNLRI": {
+ "nlriType": "IPv6-Prefix",
+ "protocol": "ISIS-L2",
+ "identifier": "0x20",
+ "localNode": {
+ "as": 65001,
+ "identifier": 0,
+ "routerID": "0000.0000.1003.00"
+ },
+ "ipReachability": "12:12::12:12/128",
+ "mtID": [2]
+ },
+ "weight": 0,
+ "origin": "IGP"
+ }
+ ],
+ "IPv4-Prefix OSPFv2 ID:0x20 {Local {AS:65001 ID:0 Area:0 Rtr:10.10.10.10:1.1.1.1} IPv4:89.10.11.0/24}/XX": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "linkStateNLRI": {
+ "nlriType": "IPv4-Prefix",
+ "protocol": "OSPFv2",
+ "identifier": "0x20",
+ "localNode": {
+ "as": 65001,
+ "identifier": 0,
+ "area": 0,
+ "routerID": "10.10.10.10:1.1.1.1"
+ },
+ "ipReachability": "89.10.11.0/24"
+ },
+ "weight": 0,
+ "origin": "IGP"
+ }
+ ],
+ "Node OSPFv2 ID:0x20 {Local {AS:65001 ID:0 Area:0 Rtr:10.10.10.10:1.1.1.1}}/XX": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "linkStateNLRI": {
+ "nlriType": "Node",
+ "protocol": "OSPFv2",
+ "identifier": "0x20",
+ "localNode": {
+ "as": 65001,
+ "identifier": 0,
+ "area": 0,
+ "routerID": "10.10.10.10:1.1.1.1"
+ }
+ },
+ "weight": 0,
+ "origin": "IGP"
+ }
+ ],
+ "Node OSPFv2 ID:0x20 {Local {AS:65001 ID:0 Area:0 Rtr:10.10.10.10}}/XX": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "linkStateNLRI": {
+ "nlriType": "Node",
+ "protocol": "OSPFv2",
+ "identifier": "0x20",
+ "localNode": {
+ "as": 65001,
+ "identifier": 0,
+ "area": 0,
+ "routerID": "10.10.10.10"
+ }
+ },
+ "weight": 0,
+ "origin": "IGP"
+ }
+ ],
+ "Node ISIS-L1 ID:0x20 {Local {AS:65001 ID:0 Rtr:0000.0000.1003.00}}/XX": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "linkStateNLRI": {
+ "nlriType": "Node",
+ "protocol": "ISIS-L1",
+ "identifier": "0x20",
+ "localNode": {
+ "as": 65001,
+ "identifier": 0,
+ "routerID": "0000.0000.1003.00"
+ }
+ },
+ "weight": 0,
+ "origin": "IGP"
+ }
+ ],
+ "Link ISIS-L1 ID:0x20 {Local {AS:65001 ID:0 Rtr:0000.0000.1001} Remote {AS:65001 ID:0 Rtr:0000.0000.1000} IPv4:10.1.0.1 Neigh-IPv4:10.1.0.2 IPv6:2001::1 Neigh-IPv6:2001::2 MT:0,2}/XX": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "linkStateNLRI": {
+ "nlriType": "Link",
+ "protocol": "ISIS-L1",
+ "identifier": "0x20",
+ "localNode": {
+ "as": 65001,
+ "identifier": 0,
+ "routerID": "0000.0000.1001"
+ },
+ "remoteNode": {
+ "as": 65001,
+ "identifier": 0,
+ "routerID": "0000.0000.1000"
+ },
+ "interfaceIPv4": "10.1.0.1",
+ "neighborIPv4": "10.1.0.2",
+ "interfaceIPv6": "2001::1",
+ "neighborIPv6": "2001::2",
+ "mtID": [0, 2]
+ },
+ "weight": 0,
+ "origin": "IGP"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bgp_linkstate_topo1/r2/linkstate_detail.json b/tests/topotests/bgp_linkstate_topo1/r2/linkstate_detail.json
new file mode 100644
index 0000000000..06498f3bb9
--- /dev/null
+++ b/tests/topotests/bgp_linkstate_topo1/r2/linkstate_detail.json
@@ -0,0 +1,325 @@
+{
+ "Node OSPFv2 ID:0x20 {Local {AS:65001 ID:0 Area:0 Rtr:10.10.10.10}}/XX": {
+ "1152": {
+ "description": "IGP Flags",
+ "type": 1152,
+ "length": 1,
+ "data": "0b10000000"
+ },
+ "1097": {
+ "description": "Opaque Link Attribute",
+ "type": 1097,
+ "length": 12,
+ "data": {
+ "11": {
+ "type": 11,
+ "length": 2,
+ "data": "0x0123"
+ },
+ "5": {
+ "type": 5,
+ "length": 2,
+ "data": "0x0123"
+ }
+ }
+ },
+ "1105": {
+ "description": "RTM Capability",
+ "type": 1105,
+ "length": 3,
+ "data": {
+ "flags": "0b110",
+ "values": "0x11ed"
+ }
+ },
+ "1172": {
+ "description": "L2 Bundle Member Attributes",
+ "type": 1172,
+ "length": 12,
+ "data": {
+ "descriptor": "0xdc12fceb",
+ "1088": {
+ "description": "Administrative group",
+ "type": 1088,
+ "length": 4,
+ "data": "0x000f0001"
+ }
+ }
+ },
+ "1117": {
+ "description": "Unidirectional Link Loss",
+ "type": 1117,
+ "length": 4,
+ "data": {
+ "anomalous": false,
+ "lossPercent": 50.33164596557617
+ }
+ },
+ "1039": {
+ "description": "Flexible Algorithm Definition",
+ "type": 1039,
+ "length": 28,
+ "data": {
+ "flexAlgo": 130,
+ "metricType": 0,
+ "calcType": 0,
+ "priority": 128,
+ "1041": {
+ "description": "Flexible Algorithm Include Any Affinity",
+ "type": 1041,
+ "length": 4,
+ "data": "0x00000001"
+ },
+ "1045": {
+ "description": "Flexible Algorithm Exclude SRLG",
+ "type": 1045,
+ "length": 4,
+ "data": [
+ 10
+ ]
+ },
+ "1043": {
+ "description": "Flexible Algorithm Definition Flags",
+ "type": 1043,
+ "length": 4,
+ "data": "0b10000000 00000000 00000000 00000000"
+ }
+ }
+ },
+ "1044": {
+ "description": "Flexible Algorithm Prefix Metric",
+ "type": 1044,
+ "length": 8,
+ "data": {
+ "flexAlgo": 130,
+ "flags": "0b10000000",
+ "metric": 1000
+ }
+ },
+ "1121": {
+ "description": "Graceful-Link-Shutdown TLV",
+ "type": 1121,
+ "length": 0
+ },
+ "1171": {
+ "description": "Source Router Identifier",
+ "type": 1171,
+ "length": 16,
+ "data": "12:12::12:12"
+ },
+ "1871": {
+ "type": 1871,
+ "length": 13,
+ "data": "0x00120012000000000000000000"
+ }
+ },
+ "Node ISIS-L1 ID:0x20 {Local {AS:65001 ID:0 Rtr:0000.0000.1003.00}}/XX": {
+ "263": {
+ "description": "Multi-Topology ID",
+ "type": 263,
+ "length": 4,
+ "data": [
+ 0,
+ 2
+ ]
+ },
+ "266": {
+ "description": "Node MSD",
+ "type": 266,
+ "length": 2,
+ "data": [
+ {
+ "type": 1,
+ "value": 8
+ }
+ ]
+ },
+ "1026": {
+ "description": "Node Name",
+ "type": 1026,
+ "length": 2,
+ "data": "r3"
+ },
+ "1027": {
+ "description": "IS-IS Area Identifier",
+ "type": 1027,
+ "length": 3,
+ "data": "49.1000"
+ },
+ "1028": {
+ "description": "IPv4 Router-ID of Local Node",
+ "type": 1028,
+ "length": 4,
+ "data": "3.3.3.3"
+ },
+ "1034": {
+ "description": "SR Capabilities",
+ "type": 1034,
+ "length": 12,
+ "data": {
+ "flags": "0b11000000",
+ "range": 4000,
+ "1161": {
+ "description": "SID/Label",
+ "type": 1161,
+ "length": 3,
+ "data": {
+ "fromLabel": 20000
+ }
+ }
+ }
+ },
+ "1035": {
+ "description": "SR Algorithm",
+ "type": 1035,
+ "length": 3,
+ "data": [
+ 0,
+ 128,
+ 130
+ ]
+ },
+ "1036": {
+ "description": "SR Local Block",
+ "type": 1036,
+ "length": 12,
+ "data": {
+ "flags": "0b00000000",
+ "range": 1000,
+ "1161": {
+ "description": "SID/Label",
+ "type": 1161,
+ "length": 3,
+ "data": {
+ "fromLabel": 15000
+ }
+ }
+ }
+ }
+ },
+ "Link ISIS-L1 ID:0x20 {Local {AS:65001 ID:0 Rtr:0000.0000.1001} Remote {AS:65001 ID:0 Rtr:0000.0000.1000} IPv4:10.1.0.1 Neigh-IPv4:10.1.0.2 IPv6:2001::1 Neigh-IPv6:2001::2 MT:0,2}/XX": {
+ "1028": {
+ "description": "IPv4 Router-ID of Local Node",
+ "type": 1028,
+ "length": 4,
+ "data": "1.1.1.1"
+ },
+ "1030": {
+ "description": "IPv4 Router-ID of Remote Node",
+ "type": 1030,
+ "length": 4,
+ "data": "10.10.10.10"
+ },
+ "1089": {
+ "description": "Maximum link bandwidth",
+ "type": 1089,
+ "length": 4,
+ "data": 176258176,
+ "dataUnit": "bps"
+ },
+ "1090": {
+ "description": "Max. reservable link bandwidth",
+ "type": 1090,
+ "length": 4,
+ "data": 176258176,
+ "dataUnit": "bps"
+ },
+ "1091": {
+ "description": "Unreserved bandwidth",
+ "type": 1091,
+ "length": 32,
+ "dataUnit": "bps",
+ "data": {
+ "0": 176258176,
+ "1": 176258176,
+ "2": 176258176,
+ "3": 176258176,
+ "4": 176258176,
+ "5": 176258176,
+ "6": 176258176,
+ "7": 176258176
+ }
+ },
+ "1092": {
+ "description": "TE Default Metric",
+ "type": 1092,
+ "length": 4,
+ "data": 100
+ },
+ "1095": {
+ "description": "IGP Metric",
+ "type": 1095,
+ "length": 3,
+ "data": 10
+ },
+ "1099": {
+ "description": "Adjacency SID",
+ "type": 1099,
+ "length": 7,
+ "data": {
+ "flags": "0b00110000",
+ "weight": 0,
+ "sid": 15000
+ }
+ },
+ "1114": {
+ "description": "Unidirectional Link Delay",
+ "type": 1114,
+ "length": 4,
+ "data": {
+ "anomalous": false,
+ "delay": 8500
+ },
+ "dataUnit": "microseconds"
+ },
+ "1115": {
+ "description": "Min/Max Unidirectional Link Delay",
+ "type": 1115,
+ "length": 8,
+ "data": {
+ "anomalous": false,
+ "minDelay": 8000,
+ "maxDelay": 9000
+ },
+ "dataUnit": "microseconds"
+ },
+ "1122": {
+ "description": "Application-Specific Link Attributes",
+ "type": 1122,
+ "length": 48,
+ "data": {
+ "sabmFlags": "0b00010000 00000000 00000000 00000000",
+ "udabmFlags": "0b00000000 00000000 00000000 00000000",
+ "1088": {
+ "description": "Administrative group",
+ "type": 1088,
+ "length": 4,
+ "data": "0x00000001"
+ },
+ "1092": {
+ "description": "TE Default Metric",
+ "type": 1092,
+ "length": 4,
+ "data": 100
+ },
+ "1115": {
+ "description": "Min/Max Unidirectional Link Delay",
+ "type": 1115,
+ "length": 8,
+ "data": {
+ "anomalous": false,
+ "minDelay": 8000,
+ "maxDelay": 9000
+ },
+ "dataUnit": "microseconds"
+ },
+ "1173": {
+ "description": "Extended Administrative Group",
+ "type": 1173,
+ "length": 4,
+ "data": "0x00000001"
+ }
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_linkstate_topo1/r2/staticd.conf b/tests/topotests/bgp_linkstate_topo1/r2/staticd.conf
new file mode 100644
index 0000000000..4ce3f7b9b1
--- /dev/null
+++ b/tests/topotests/bgp_linkstate_topo1/r2/staticd.conf
@@ -0,0 +1,2 @@
+ip route 192.0.2.1/32 192.168.1.1
+ip route 192.0.2.3/32 192.168.2.3 \ No newline at end of file
diff --git a/tests/topotests/bgp_linkstate_topo1/r2/zebra.conf b/tests/topotests/bgp_linkstate_topo1/r2/zebra.conf
new file mode 100644
index 0000000000..586dc0acad
--- /dev/null
+++ b/tests/topotests/bgp_linkstate_topo1/r2/zebra.conf
@@ -0,0 +1,11 @@
+!
+int lo
+ ip address 192.0.2.2/32
+!
+interface r2-eth0
+ ip address 192.168.1.2/24
+!
+interface r2-eth1
+ ip address 192.168.2.2/24
+!
+
diff --git a/tests/topotests/bgp_linkstate_topo1/r3/bgpd.conf b/tests/topotests/bgp_linkstate_topo1/r3/bgpd.conf
new file mode 100644
index 0000000000..88693a3750
--- /dev/null
+++ b/tests/topotests/bgp_linkstate_topo1/r3/bgpd.conf
@@ -0,0 +1,14 @@
+router bgp 65003
+ no bgp ebgp-requires-policy
+ neighbor 192.0.2.2 remote-as 65002
+ neighbor 192.0.2.2 timers 1 3
+ neighbor 192.0.2.2 timers connect 1
+ neighbor 192.0.2.2 ebgp-multihop 3
+ neighbor 192.0.2.2 update-source 192.0.2.3
+ address-family ipv4 unicast
+ no neighbor 192.0.2.2 activate
+ exit-address-family
+ address-family link-state link-state
+ neighbor 192.0.2.2 activate
+ exit-address-family
+! \ No newline at end of file
diff --git a/tests/topotests/bgp_linkstate_topo1/r3/linkstate.json b/tests/topotests/bgp_linkstate_topo1/r3/linkstate.json
new file mode 120000
index 0000000000..588691dcf7
--- /dev/null
+++ b/tests/topotests/bgp_linkstate_topo1/r3/linkstate.json
@@ -0,0 +1 @@
+../r2/linkstate.json \ No newline at end of file
diff --git a/tests/topotests/bgp_linkstate_topo1/r3/linkstate_detail.json b/tests/topotests/bgp_linkstate_topo1/r3/linkstate_detail.json
new file mode 120000
index 0000000000..ec06e56c66
--- /dev/null
+++ b/tests/topotests/bgp_linkstate_topo1/r3/linkstate_detail.json
@@ -0,0 +1 @@
+../r2/linkstate_detail.json \ No newline at end of file
diff --git a/tests/topotests/bgp_linkstate_topo1/r3/staticd.conf b/tests/topotests/bgp_linkstate_topo1/r3/staticd.conf
new file mode 100644
index 0000000000..e8a3198ba7
--- /dev/null
+++ b/tests/topotests/bgp_linkstate_topo1/r3/staticd.conf
@@ -0,0 +1 @@
+ip route 192.0.2.2/32 192.168.2.2 \ No newline at end of file
diff --git a/tests/topotests/bgp_linkstate_topo1/r3/zebra.conf b/tests/topotests/bgp_linkstate_topo1/r3/zebra.conf
new file mode 100644
index 0000000000..532aede2e8
--- /dev/null
+++ b/tests/topotests/bgp_linkstate_topo1/r3/zebra.conf
@@ -0,0 +1,7 @@
+!
+int lo
+ ip address 192.0.2.3/32
+!
+interface r3-eth0
+ ip address 192.168.2.3/24
+!
diff --git a/tests/topotests/bgp_linkstate_topo1/test_bgp_linkstate_topo1.py b/tests/topotests/bgp_linkstate_topo1/test_bgp_linkstate_topo1.py
new file mode 100644
index 0000000000..5593b45fba
--- /dev/null
+++ b/tests/topotests/bgp_linkstate_topo1/test_bgp_linkstate_topo1.py
@@ -0,0 +1,143 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright 2023 6WIND S.A.
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.common_config import step
+from lib.topolog import logger
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 4):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_STATIC, os.path.join(CWD, "{}/staticd.conf".format(rname))
+ )
+ if rname == "r1":
+ # use bgp_injector.py to inject BGP prefixes
+ continue
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+ r1_path = os.path.join(CWD, "r1")
+ log_dir = os.path.join(tgen.logdir, "r1")
+ tgen.gears["r1"].cmd("chmod u+x {}/bgp_injector.py".format(r1_path))
+ tgen.gears["r1"].run("{}/bgp_injector.py {}".format(r1_path, log_dir))
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+
+ log_dir = os.path.join(tgen.logdir, "r1")
+ pid_file = os.path.join(log_dir, "bgp_injector.pid")
+
+ logger.info("r1: sending SIGTERM to bgp_injector")
+ tgen.gears["r1"].cmd("kill $(cat {})".format(pid_file))
+ tgen.stop_topology()
+
+
+def test_show_bgp_link_state():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _remove_prefixlen(tmp_json):
+ new_json = {
+ prefix.split("}/")[0] + "}/XX": data
+ for prefix, data in tmp_json["routes"].items()
+ }
+
+ return new_json
+
+ def _show_bgp_link_state_json(rname, tmp_expected):
+ tmp_output = json.loads(
+ tgen.gears[rname].vtysh_cmd("show bgp link-state link-state json")
+ )
+ # prefix length is the size of prefix in memory
+ # which differs on 32 and 64 bits.
+ # do not compare the prefix length
+ output = _remove_prefixlen(tmp_output)
+ expected = _remove_prefixlen(tmp_expected)
+
+ return topotest.json_cmp(output, expected)
+
+ step("Check BGP Link-State tables")
+ for rname in ["r2", "r3"]:
+ expected = open(os.path.join(CWD, "{}/linkstate.json".format(rname))).read()
+ expected_json = json.loads(expected)
+ test_func = functools.partial(_show_bgp_link_state_json, rname, expected_json)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to see BGP prefixes on {}".format(rname)
+
+
+def test_show_bgp_link_state_detail():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _show_bgp_link_state_json(rname, expected):
+ output = json.loads(
+ tgen.gears[rname].vtysh_cmd("show bgp link-state link-state detail-routes json")
+ )
+ json_output = {
+ prefix.split("/")[0] + "/XX": item["linkStateAttributes"]
+ for prefix, data in output["routes"].items()
+ for item in data
+ if "linkStateAttributes" in item
+ }
+
+ return topotest.json_cmp(json_output, expected)
+
+ step("Check BGP Link-State Attributes tables")
+ for rname in ["r2", "r3"]:
+ expected = open(
+ os.path.join(CWD, "{}/linkstate_detail.json".format(rname))
+ ).read()
+ expected_json = json.loads(expected)
+ test_func = functools.partial(_show_bgp_link_state_json, rname, expected_json)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to display BGP-LS Attributes on {}".format(rname)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
index 113a15c172..050807ccd8 100644
--- a/vtysh/vtysh.c
+++ b/vtysh/vtysh.c
@@ -1447,6 +1447,13 @@ static struct cmd_node bgp_ipv6l_node = {
.no_xpath = true,
};
+static struct cmd_node bgp_ls_node = {
+ .name = "bgp link-state",
+ .node = BGP_LS_NODE,
+ .parent_node = BGP_NODE,
+ .prompt = "%s(config-router-af-ls)# ",
+};
+
#ifdef ENABLE_BGP_VNC
static struct cmd_node bgp_vnc_defaults_node = {
.name = "bgp vnc defaults",
@@ -1758,6 +1765,14 @@ DEFUNSH(VTYSH_BGPD, address_family_flowspecv6, address_family_flowspecv6_cmd,
return CMD_SUCCESS;
}
+DEFUNSH(VTYSH_BGPD, address_family_linkstate, address_family_linkstate_cmd,
+ "address-family link-state link-state",
+ "Enter Address Family command mode\n" BGP_AF_STR BGP_AF_MODIFIER_STR)
+{
+ vty->node = BGP_LS_NODE;
+ return CMD_SUCCESS;
+}
+
DEFUNSH(VTYSH_BGPD, address_family_ipv4_multicast,
address_family_ipv4_multicast_cmd, "address-family ipv4 multicast",
"Enter Address Family command mode\n"
@@ -2420,7 +2435,8 @@ DEFUNSH(VTYSH_BGPD, exit_address_family, exit_address_family_cmd,
|| vty->node == BGP_IPV6L_NODE || vty->node == BGP_IPV6M_NODE
|| vty->node == BGP_EVPN_NODE
|| vty->node == BGP_FLOWSPECV4_NODE
- || vty->node == BGP_FLOWSPECV6_NODE)
+ || vty->node == BGP_FLOWSPECV6_NODE
+ || vty->node == BGP_LS_NODE)
vty->node = BGP_NODE;
return CMD_SUCCESS;
}
@@ -4671,6 +4687,13 @@ void vtysh_init_vty(void)
install_element(BGP_EVPN_VNI_NODE, &vtysh_end_all_cmd);
install_element(BGP_EVPN_VNI_NODE, &exit_vni_cmd);
+ install_node(&bgp_ls_node);
+ install_element(BGP_NODE, &address_family_linkstate_cmd);
+ install_element(BGP_LS_NODE, &vtysh_exit_bgpd_cmd);
+ install_element(BGP_LS_NODE, &vtysh_quit_bgpd_cmd);
+ install_element(BGP_LS_NODE, &vtysh_end_all_cmd);
+ install_element(BGP_LS_NODE, &exit_address_family_cmd);
+
install_node(&rpki_node);
install_element(CONFIG_NODE, &rpki_cmd);
install_element(RPKI_NODE, &rpki_exit_cmd);
diff --git a/yang/frr-bgp-common-multiprotocol.yang b/yang/frr-bgp-common-multiprotocol.yang
index c22bdf9964..f7f4ff5075 100644
--- a/yang/frr-bgp-common-multiprotocol.yang
+++ b/yang/frr-bgp-common-multiprotocol.yang
@@ -204,5 +204,15 @@ submodule frr-bgp-common-multiprotocol {
description
"IPv6 flowspec configuration options.";
}
+
+ container linkstate-linkstate {
+ when "derived-from-or-self(../afi-safi-name, 'frr-rt:linkstate-linkstate')" {
+ description
+ "Include this container for Link-State specific
+ configuration.";
+ }
+ description
+ "Link-State configuration options.";
+ }
}
}
diff --git a/yang/frr-bgp.yang b/yang/frr-bgp.yang
index b34fd43e78..557b9ef81b 100644
--- a/yang/frr-bgp.yang
+++ b/yang/frr-bgp.yang
@@ -819,6 +819,17 @@ module frr-bgp {
uses structure-neighbor-group-filter-config;
}
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/linkstate-linkstate" {
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+
+ uses structure-neighbor-group-filter-config;
+ }
+
augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/ipv4-unicast" {
uses structure-neighbor-group-add-paths;
@@ -1090,6 +1101,16 @@ module frr-bgp {
uses structure-neighbor-group-filter-config;
}
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/linkstate-linkstate" {
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+
+ uses structure-neighbor-group-filter-config;
+ }
+
augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/ipv4-unicast" {
uses structure-neighbor-group-add-paths;
@@ -1366,4 +1387,14 @@ module frr-bgp {
uses structure-neighbor-group-filter-config;
}
+
+ augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/linkstate-linkstate" {
+ uses structure-neighbor-route-reflector;
+
+ uses structure-neighbor-route-server;
+
+ uses structure-neighbor-group-soft-reconfiguration;
+
+ uses structure-neighbor-group-filter-config;
+ }
}
diff --git a/yang/frr-routing.yang b/yang/frr-routing.yang
index b304c241a4..f6e1f8e74a 100644
--- a/yang/frr-routing.yang
+++ b/yang/frr-routing.yang
@@ -172,6 +172,13 @@ module frr-routing {
}
+ identity linkstate-linkstate {
+ base afi-safi-type;
+ description
+ "This identity represents the link-state address family.";
+ }
+
+
identity control-plane-protocol {
description
"Base identity from which control-plane protocol identities are
diff --git a/zebra/connected.c b/zebra/connected.c
index ee0823f56f..46f2a83a3c 100644
--- a/zebra/connected.c
+++ b/zebra/connected.c
@@ -234,6 +234,7 @@ void connected_up(struct interface *ifp, struct connected *ifc)
break;
case AFI_UNSPEC:
case AFI_L2VPN:
+ case AFI_LINKSTATE:
case AFI_MAX:
flog_warn(EC_ZEBRA_CONNECTED_AFI_UNKNOWN,
"Received unknown AFI: %s", afi2str(afi));
@@ -424,6 +425,7 @@ void connected_down(struct interface *ifp, struct connected *ifc)
break;
case AFI_UNSPEC:
case AFI_L2VPN:
+ case AFI_LINKSTATE:
case AFI_MAX:
zlog_warn("Unknown AFI: %s", afi2str(afi));
break;
diff --git a/zebra/router-id.c b/zebra/router-id.c
index ef87d924fe..45edf45336 100644
--- a/zebra/router-id.c
+++ b/zebra/router-id.c
@@ -102,6 +102,7 @@ int router_id_get(afi_t afi, struct prefix *p, struct zebra_vrf *zvrf)
return 0;
case AFI_UNSPEC:
case AFI_L2VPN:
+ case AFI_LINKSTATE:
case AFI_MAX:
return -1;
}
@@ -126,6 +127,7 @@ static int router_id_set(afi_t afi, struct prefix *p, struct zebra_vrf *zvrf)
break;
case AFI_UNSPEC:
case AFI_L2VPN:
+ case AFI_LINKSTATE:
case AFI_MAX:
return -1;
}
diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c
index 8a480cfa1c..86e4f2570c 100644
--- a/zebra/zapi_msg.c
+++ b/zebra/zapi_msg.c
@@ -565,6 +565,7 @@ int zsend_redistribute_route(int cmd, struct zserv *client,
client->redist_v6_del_cnt++;
break;
case AFI_L2VPN:
+ case AFI_LINKSTATE:
case AFI_MAX:
case AFI_UNSPEC:
break;
diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c
index 9588f65fc6..d1a84491aa 100644
--- a/zebra/zebra_nhg.c
+++ b/zebra/zebra_nhg.c
@@ -2288,6 +2288,7 @@ static int nexthop_active(struct nexthop *nexthop, struct nhg_hash_entry *nhe,
break;
case AFI_UNSPEC:
case AFI_L2VPN:
+ case AFI_LINKSTATE:
case AFI_MAX:
flog_err(EC_LIB_DEVELOPMENT,
"%s: unknown address-family: %u", __func__,
@@ -2331,6 +2332,7 @@ static int nexthop_active(struct nexthop *nexthop, struct nhg_hash_entry *nhe,
break;
case AFI_UNSPEC:
case AFI_L2VPN:
+ case AFI_LINKSTATE:
case AFI_MAX:
assert(afi != AFI_IP && afi != AFI_IP6);
break;