frr/isisd/isis_tlvs.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

7476 lines
207 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* IS-IS TLV Serializer/Deserializer
*
* Copyright (C) 2015,2017 Christian Franke
*
* Copyright (C) 2019 Olivier Dugeon - Orange Labs (for TE and SR)
*/
#include <zebra.h>
#include <json-c/json_object.h>
#ifdef CRYPTO_INTERNAL
#include "md5.h"
#endif
#include "memory.h"
#include "stream.h"
#include "sbuf.h"
#include "network.h"
#include "isisd/isisd.h"
#include "isisd/isis_tlvs.h"
#include "isisd/isis_common.h"
#include "isisd/isis_mt.h"
#include "isisd/isis_misc.h"
#include "isisd/isis_adjacency.h"
#include "isisd/isis_circuit.h"
#include "isisd/isis_pdu.h"
#include "isisd/isis_lsp.h"
#include "isisd/isis_te.h"
#include "isisd/isis_sr.h"
#include "isisd/isis_flex_algo.h"
#define TLV_SIZE_MISMATCH(log, indent, target) \
sbuf_push(log, indent, \
"TLV size does not match expected size for " target "!\n")
DEFINE_MTYPE_STATIC(ISISD, ISIS_TLV, "ISIS TLVs");
DEFINE_MTYPE(ISISD, ISIS_SUBTLV, "ISIS Sub-TLVs");
DEFINE_MTYPE_STATIC(ISISD, ISIS_SUBSUBTLV, "ISIS Sub-Sub-TLVs");
DEFINE_MTYPE_STATIC(ISISD, ISIS_MT_ITEM_LIST, "ISIS MT Item Lists");
typedef int (*unpack_tlv_func)(enum isis_tlv_context context, uint8_t tlv_type,
uint8_t tlv_len, struct stream *s,
struct sbuf *log, void *dest, int indent);
typedef int (*pack_item_func)(struct isis_item *item, struct stream *s,
size_t *min_length);
typedef void (*free_item_func)(struct isis_item *i);
typedef int (*unpack_item_func)(uint16_t mtid, uint8_t len, struct stream *s,
struct sbuf *log, void *dest, int indent);
typedef void (*format_item_func)(uint16_t mtid, struct isis_item *i,
struct sbuf *buf, struct json_object *json,
int indent);
typedef struct isis_item *(*copy_item_func)(struct isis_item *i);
struct tlv_ops {
const char *name;
unpack_tlv_func unpack;
pack_item_func pack_item;
free_item_func free_item;
unpack_item_func unpack_item;
format_item_func format_item;
copy_item_func copy_item;
};
enum how_to_pack {
ISIS_ITEMS,
ISIS_MT_ITEMS,
};
struct pack_order_entry {
enum isis_tlv_context context;
enum isis_tlv_type type;
enum how_to_pack how_to_pack;
size_t what_to_pack;
};
#define PACK_ENTRY(t, h, w) \
{ \
.context = ISIS_CONTEXT_LSP, .type = ISIS_TLV_##t, \
.how_to_pack = (h), \
.what_to_pack = offsetof(struct isis_tlvs, w), \
}
static const struct pack_order_entry pack_order[] = {
PACK_ENTRY(OLDSTYLE_REACH, ISIS_ITEMS, oldstyle_reach),
PACK_ENTRY(LAN_NEIGHBORS, ISIS_ITEMS, lan_neighbor),
PACK_ENTRY(LSP_ENTRY, ISIS_ITEMS, lsp_entries),
PACK_ENTRY(EXTENDED_REACH, ISIS_ITEMS, extended_reach),
PACK_ENTRY(MT_REACH, ISIS_MT_ITEMS, mt_reach),
PACK_ENTRY(OLDSTYLE_IP_REACH, ISIS_ITEMS, oldstyle_ip_reach),
PACK_ENTRY(OLDSTYLE_IP_REACH_EXT, ISIS_ITEMS, oldstyle_ip_reach_ext),
PACK_ENTRY(IPV4_ADDRESS, ISIS_ITEMS, ipv4_address),
PACK_ENTRY(IPV6_ADDRESS, ISIS_ITEMS, ipv6_address),
PACK_ENTRY(GLOBAL_IPV6_ADDRESS, ISIS_ITEMS, global_ipv6_address),
PACK_ENTRY(EXTENDED_IP_REACH, ISIS_ITEMS, extended_ip_reach),
PACK_ENTRY(MT_IP_REACH, ISIS_MT_ITEMS, mt_ip_reach),
PACK_ENTRY(IPV6_REACH, ISIS_ITEMS, ipv6_reach),
PACK_ENTRY(MT_IPV6_REACH, ISIS_MT_ITEMS, mt_ipv6_reach),
PACK_ENTRY(SRV6_LOCATOR, ISIS_MT_ITEMS, srv6_locator)
};
/* This is a forward definition. The table is actually initialized
* in at the bottom. */
static const struct tlv_ops *const tlv_table[ISIS_CONTEXT_MAX][ISIS_TLV_MAX];
/* End of _ops forward definition. */
/* Prototypes */
static void append_item(struct isis_item_list *dest, struct isis_item *item);
static void init_item_list(struct isis_item_list *items);
/* For tests/isisd, TLV text requires ipv4-unicast instead of standard */
static const char *isis_mtid2str_fake(uint16_t mtid)
{
if (mtid == ISIS_MT_STANDARD)
return "ipv4-unicast";
return isis_mtid2str(mtid);
}
/* Functions for Extended IS Reachability SubTLVs a.k.a Traffic Engineering */
struct isis_ext_subtlvs *isis_alloc_ext_subtlvs(void)
{
struct isis_ext_subtlvs *ext;
ext = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(struct isis_ext_subtlvs));
init_item_list(&ext->adj_sid);
init_item_list(&ext->lan_sid);
ext->aslas = list_new();
admin_group_init(&ext->ext_admin_group);
return ext;
}
void isis_del_ext_subtlvs(struct isis_ext_subtlvs *ext)
{
struct isis_item *item, *next_item;
struct listnode *node, *nnode;
struct isis_asla_subtlvs *asla;
if (!ext)
return;
/* First, free Adj SID and LAN Adj SID list if needed */
for (item = ext->adj_sid.head; item; item = next_item) {
next_item = item->next;
XFREE(MTYPE_ISIS_SUBTLV, item);
}
for (item = ext->lan_sid.head; item; item = next_item) {
next_item = item->next;
XFREE(MTYPE_ISIS_SUBTLV, item);
}
for (ALL_LIST_ELEMENTS(ext->aslas, node, nnode, asla))
isis_tlvs_del_asla_flex_algo(ext, asla);
list_delete(&ext->aslas);
admin_group_term(&ext->ext_admin_group);
XFREE(MTYPE_ISIS_SUBTLV, ext);
}
/*
* mtid parameter is used to determine if Adjacency is related to IPv4 or IPv6
* Multi-Topology. Special 4096 value i.e. first R flag set is used to indicate
* that MT is disabled i.e. IS-IS is working with a Single Topology.
*/
static struct isis_ext_subtlvs *
copy_item_ext_subtlvs(struct isis_ext_subtlvs *exts, uint16_t mtid)
{
struct isis_ext_subtlvs *rv = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*rv));
struct isis_adj_sid *adj;
struct isis_lan_adj_sid *lan;
struct listnode *node, *nnode;
struct isis_asla_subtlvs *new_asla, *asla;
/* Copy the Extended IS main part */
memcpy(rv, exts, sizeof(struct isis_ext_subtlvs));
/* Disable IPv4 / IPv6 advertisement in function of MTID */
if (mtid == ISIS_MT_IPV4_UNICAST) {
UNSET_SUBTLV(rv, EXT_LOCAL_ADDR6);
UNSET_SUBTLV(rv, EXT_NEIGH_ADDR6);
}
if (mtid == ISIS_MT_IPV6_UNICAST) {
UNSET_SUBTLV(rv, EXT_LOCAL_ADDR);
UNSET_SUBTLV(rv, EXT_NEIGH_ADDR);
}
/* Prepare (LAN)-Adjacency Segment Routing ID*/
init_item_list(&rv->adj_sid);
init_item_list(&rv->lan_sid);
UNSET_SUBTLV(rv, EXT_ADJ_SID);
UNSET_SUBTLV(rv, EXT_LAN_ADJ_SID);
/* Copy Adj SID list for IPv4 & IPv6 in function of MT ID */
for (adj = (struct isis_adj_sid *)exts->adj_sid.head; adj != NULL;
adj = adj->next) {
if ((mtid != ISIS_MT_DISABLE)
&& (((mtid == ISIS_MT_IPV4_UNICAST)
&& (adj->family != AF_INET))
|| ((mtid == ISIS_MT_IPV6_UNICAST)
&& (adj->family != AF_INET6))))
continue;
struct isis_adj_sid *new;
new = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(struct isis_adj_sid));
new->family = adj->family;
new->flags = adj->flags;
new->weight = adj->weight;
new->sid = adj->sid;
append_item(&rv->adj_sid, (struct isis_item *)new);
SET_SUBTLV(rv, EXT_ADJ_SID);
}
/* Same for LAN Adj SID */
for (lan = (struct isis_lan_adj_sid *)exts->lan_sid.head; lan != NULL;
lan = lan->next) {
if ((mtid != ISIS_MT_DISABLE)
&& (((mtid == ISIS_MT_IPV4_UNICAST)
&& (lan->family != AF_INET))
|| ((mtid == ISIS_MT_IPV6_UNICAST)
&& (lan->family != AF_INET6))))
continue;
struct isis_lan_adj_sid *new;
new = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(struct isis_lan_adj_sid));
new->family = lan->family;
new->flags = lan->flags;
new->weight = lan->weight;
memcpy(new->neighbor_id, lan->neighbor_id, 6);
new->sid = lan->sid;
append_item(&rv->lan_sid, (struct isis_item *)new);
SET_SUBTLV(rv, EXT_LAN_ADJ_SID);
}
rv->aslas = list_new();
for (ALL_LIST_ELEMENTS(exts->aslas, node, nnode, asla)) {
new_asla = XCALLOC(MTYPE_ISIS_SUBTLV,
sizeof(struct isis_asla_subtlvs));
memcpy(new_asla, asla, sizeof(struct isis_asla_subtlvs));
new_asla->ext_admin_group.bitmap.data = NULL;
admin_group_copy(&new_asla->ext_admin_group,
&asla->ext_admin_group);
listnode_add(rv->aslas, new_asla);
}
rv->ext_admin_group.bitmap.data = NULL;
admin_group_copy(&rv->ext_admin_group, &exts->ext_admin_group);
return rv;
}
static void format_item_asla_subtlvs(struct isis_asla_subtlvs *asla,
struct sbuf *buf, int indent)
{
char admin_group_buf[ADMIN_GROUP_PRINT_MAX_SIZE];
sbuf_push(buf, indent, "Application Specific Link Attributes:\n");
sbuf_push(buf, indent + 2,
"L flag: %u, SA-Length: %u, UDA-Length: %u\n", asla->legacy,
asla->standard_apps_length, asla->user_def_apps_length);
sbuf_push(buf, indent + 2, "Standard Applications: 0x%02x",
asla->standard_apps);
if (asla->standard_apps) {
uint8_t bit = asla->standard_apps;
if (bit & ISIS_SABM_FLAG_R)
sbuf_push(buf, 0, " RSVP-TE");
if (bit & ISIS_SABM_FLAG_S)
sbuf_push(buf, 0, " SR-Policy");
if (bit & ISIS_SABM_FLAG_L)
sbuf_push(buf, 0, " Loop-Free-Alternate");
if (bit & ISIS_SABM_FLAG_X)
sbuf_push(buf, 0, " Flex-Algo");
}
sbuf_push(buf, 0, "\n");
sbuf_push(buf, indent + 2, "User Defined Applications: 0x%02x\n",
asla->user_def_apps);
if (IS_SUBTLV(asla, EXT_ADM_GRP)) {
sbuf_push(buf, indent + 2, "Admin Group: 0x%08x\n",
asla->admin_group);
sbuf_push(buf, indent + 4, "Bit positions: %s\n",
admin_group_standard_print(
admin_group_buf,
indent + 2 + strlen("Admin Group: "),
asla->admin_group));
}
if (IS_SUBTLV(asla, EXT_EXTEND_ADM_GRP) &&
admin_group_nb_words(&asla->ext_admin_group) != 0) {
sbuf_push(buf, indent + 2, "Ext Admin Group: %s\n",
admin_group_string(
admin_group_buf, ADMIN_GROUP_PRINT_MAX_SIZE,
indent + 2 + strlen("Ext Admin Group: "),
&asla->ext_admin_group));
admin_group_print(admin_group_buf,
indent + 2 + strlen("Ext Admin Group: "),
&asla->ext_admin_group);
if (admin_group_buf[0] != '\0' &&
(buf->pos + strlen(admin_group_buf) +
SBUF_DEFAULT_SIZE / 2) < buf->size)
sbuf_push(buf, indent + 4, "Bit positions: %s\n",
admin_group_buf);
}
if (IS_SUBTLV(asla, EXT_MAX_BW))
sbuf_push(buf, indent + 2,
"Maximum Bandwidth: %g (Bytes/sec)\n", asla->max_bw);
if (IS_SUBTLV(asla, EXT_MAX_RSV_BW))
sbuf_push(buf, indent + 2,
"Maximum Reservable Bandwidth: %g (Bytes/sec)\n",
asla->max_rsv_bw);
if (IS_SUBTLV(asla, EXT_UNRSV_BW)) {
sbuf_push(buf, indent + 2, "Unreserved Bandwidth:\n");
for (int j = 0; j < MAX_CLASS_TYPE; j += 2) {
sbuf_push(
buf, indent + 2,
"[%d]: %g (Bytes/sec),\t[%d]: %g (Bytes/sec)\n",
j, asla->unrsv_bw[j], j + 1,
asla->unrsv_bw[j + 1]);
}
}
if (IS_SUBTLV(asla, EXT_TE_METRIC))
sbuf_push(buf, indent + 2, "Traffic Engineering Metric: %u\n",
asla->te_metric);
/* Extended metrics */
if (IS_SUBTLV(asla, EXT_DELAY))
sbuf_push(buf, indent + 2,
"%s Average Link Delay: %u (micro-sec)\n",
IS_ANORMAL(asla->delay) ? "Anomalous" : "Normal",
asla->delay);
if (IS_SUBTLV(asla, EXT_MM_DELAY)) {
sbuf_push(buf, indent + 2,
"%s Min/Max Link Delay: %u / %u (micro-sec)\n",
IS_ANORMAL(asla->min_delay) ? "Anomalous" : "Normal",
asla->min_delay & TE_EXT_MASK,
asla->max_delay & TE_EXT_MASK);
}
if (IS_SUBTLV(asla, EXT_DELAY_VAR)) {
sbuf_push(buf, indent + 2, "Delay Variation: %u (micro-sec)\n",
asla->delay_var & TE_EXT_MASK);
}
if (IS_SUBTLV(asla, EXT_PKT_LOSS))
sbuf_push(buf, indent + 2, "%s Link Packet Loss: %g (%%)\n",
IS_ANORMAL(asla->pkt_loss) ? "Anomalous" : "Normal",
(float)((asla->pkt_loss & TE_EXT_MASK) *
LOSS_PRECISION));
if (IS_SUBTLV(asla, EXT_RES_BW))
sbuf_push(buf, indent + 2,
"Unidir. Residual Bandwidth: %g (Bytes/sec)\n",
asla->res_bw);
if (IS_SUBTLV(asla, EXT_AVA_BW))
sbuf_push(buf, indent + 2,
"Unidir. Available Bandwidth: %g (Bytes/sec)\n",
asla->ava_bw);
if (IS_SUBTLV(asla, EXT_USE_BW))
sbuf_push(buf, indent + 2,
"Unidir. Utilized Bandwidth: %g (Bytes/sec)\n",
asla->use_bw);
}
/* mtid parameter is used to manage multi-topology i.e. IPv4 / IPv6 */
static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts,
struct sbuf *buf, struct json_object *json,
int indent, uint16_t mtid)
{
char admin_group_buf[ADMIN_GROUP_PRINT_MAX_SIZE];
char aux_buf[255];
char cnt_buf[255];
struct isis_asla_subtlvs *asla;
struct listnode *node;
/* Standard metrics */
if (IS_SUBTLV(exts, EXT_ADM_GRP)) {
if (json) {
snprintfrr(aux_buf, sizeof(aux_buf), "0x%x",
exts->adm_group);
json_object_string_add(json, "adm-group", aux_buf);
} else {
sbuf_push(buf, indent, "Admin Group: 0x%08x\n",
exts->adm_group);
sbuf_push(buf, indent + 2, "Bit positions: %s\n",
admin_group_standard_print(
admin_group_buf,
indent + strlen("Admin Group: "),
exts->adm_group));
}
}
if (IS_SUBTLV(exts, EXT_EXTEND_ADM_GRP) &&
admin_group_nb_words(&exts->ext_admin_group) != 0) {
if (!json) {
/* TODO json after fix show database detail json */
sbuf_push(buf, indent, "Ext Admin Group: %s\n",
admin_group_string(
admin_group_buf,
ADMIN_GROUP_PRINT_MAX_SIZE,
indent + strlen("Ext Admin Group: "),
&exts->ext_admin_group));
admin_group_print(admin_group_buf,
indent + strlen("Ext Admin Group: "),
&exts->ext_admin_group);
if (admin_group_buf[0] != '\0' &&
(buf->pos + strlen(admin_group_buf) +
SBUF_DEFAULT_SIZE / 2) < buf->size)
sbuf_push(buf, indent + 2,
"Bit positions: %s\n",
admin_group_buf);
}
}
if (IS_SUBTLV(exts, EXT_LLRI)) {
if (json) {
json_object_int_add(json, "link-local-id",
exts->local_llri);
json_object_int_add(json, "link-remote-id",
exts->remote_llri);
} else {
sbuf_push(buf, indent, "Link Local ID: %u\n",
exts->local_llri);
sbuf_push(buf, indent, "Link Remote ID: %u\n",
exts->remote_llri);
}
}
if (IS_SUBTLV(exts, EXT_LOCAL_ADDR)) {
if (json) {
inet_ntop(AF_INET, &exts->local_addr, aux_buf,
sizeof(aux_buf));
json_object_string_add(json, "local-iface-ip", aux_buf);
} else
sbuf_push(buf, indent,
"Local Interface IP Address(es): %pI4\n",
&exts->local_addr);
}
if (IS_SUBTLV(exts, EXT_NEIGH_ADDR)) {
if (json) {
inet_ntop(AF_INET, &exts->neigh_addr, aux_buf,
sizeof(aux_buf));
json_object_string_add(json, "remote-iface-ip",
aux_buf);
} else
sbuf_push(buf, indent,
"Remote Interface IP Address(es): %pI4\n",
&exts->neigh_addr);
}
if (IS_SUBTLV(exts, EXT_LOCAL_ADDR6)) {
if (json) {
inet_ntop(AF_INET6, &exts->local_addr6, aux_buf,
sizeof(aux_buf));
json_object_string_add(json, "local-iface-ipv6",
aux_buf);
} else
sbuf_push(buf, indent,
"Local Interface IPv6 Address(es): %pI6\n",
&exts->local_addr6);
}
if (IS_SUBTLV(exts, EXT_NEIGH_ADDR6)) {
if (json) {
inet_ntop(AF_INET6, &exts->neigh_addr6, aux_buf,
sizeof(aux_buf));
json_object_string_add(json, "remote-iface-ipv6",
aux_buf);
} else
sbuf_push(buf, indent,
"Remote Interface IPv6 Address(es): %pI6\n",
&exts->neigh_addr6);
}
if (IS_SUBTLV(exts, EXT_MAX_BW)) {
if (json) {
snprintfrr(aux_buf, sizeof(aux_buf), "%g",
exts->max_bw);
json_object_string_add(json, "max-bandwith-bytes-sec",
aux_buf);
} else
sbuf_push(buf, indent,
"Maximum Bandwidth: %g (Bytes/sec)\n",
exts->max_bw);
}
if (IS_SUBTLV(exts, EXT_MAX_RSV_BW)) {
if (json) {
snprintfrr(aux_buf, sizeof(aux_buf), "%g",
exts->max_rsv_bw);
json_object_string_add(
json, "max-res-bandwith-bytes-sec", aux_buf);
} else
sbuf_push(
buf, indent,
"Maximum Reservable Bandwidth: %g (Bytes/sec)\n",
exts->max_rsv_bw);
}
if (IS_SUBTLV(exts, EXT_UNRSV_BW)) {
if (json) {
struct json_object *unrsv_json;
unrsv_json = json_object_new_object();
json_object_object_add(json, "unrsv-bandwith-bytes-sec",
unrsv_json);
for (int j = 0; j < MAX_CLASS_TYPE; j += 1) {
snprintfrr(cnt_buf, sizeof(cnt_buf), "%d", j);
snprintfrr(aux_buf, sizeof(aux_buf), "%g",
exts->unrsv_bw[j]);
json_object_string_add(unrsv_json, cnt_buf,
aux_buf);
}
} else {
sbuf_push(buf, indent, "Unreserved Bandwidth:\n");
for (int j = 0; j < MAX_CLASS_TYPE; j += 2) {
sbuf_push(
buf, indent + 2,
"[%d]: %g (Bytes/sec),\t[%d]: %g (Bytes/sec)\n",
j, exts->unrsv_bw[j], j + 1,
exts->unrsv_bw[j + 1]);
}
}
}
if (IS_SUBTLV(exts, EXT_TE_METRIC)) {
if (json) {
json_object_int_add(json, "te-metric", exts->te_metric);
} else
sbuf_push(buf, indent,
"Traffic Engineering Metric: %u\n",
exts->te_metric);
}
if (IS_SUBTLV(exts, EXT_RMT_AS)) {
if (json) {
json_object_int_add(json, "inter-as-te-remote-as",
exts->remote_as);
} else
sbuf_push(buf, indent,
"Inter-AS TE Remote AS number: %u\n",
exts->remote_as);
}
if (IS_SUBTLV(exts, EXT_RMT_IP)) {
if (json) {
inet_ntop(AF_INET6, &exts->remote_ip, aux_buf,
sizeof(aux_buf));
json_object_string_add(
json, "inter-as-te-remote-asbr-ip", aux_buf);
} else
sbuf_push(buf, indent,
"Inter-AS TE Remote ASBR IP address: %pI4\n",
&exts->remote_ip);
}
/* Extended metrics */
if (IS_SUBTLV(exts, EXT_DELAY)) {
if (json) {
struct json_object *avg_json;
avg_json = json_object_new_object();
json_object_object_add(json, "avg-delay", avg_json);
json_object_string_add(avg_json, "delay",
IS_ANORMAL(exts->delay)
? "Anomalous"
: "Normal");
json_object_int_add(avg_json, "micro-sec", exts->delay);
} else
sbuf_push(buf, indent,
"%s Average Link Delay: %u (micro-sec)\n",
IS_ANORMAL(exts->delay) ? "Anomalous"
: "Normal",
exts->delay & TE_EXT_MASK);
}
if (IS_SUBTLV(exts, EXT_MM_DELAY)) {
if (json) {
struct json_object *avg_json;
avg_json = json_object_new_object();
json_object_object_add(json, "max-min-delay", avg_json);
json_object_string_add(avg_json, "delay",
IS_ANORMAL(exts->min_delay)
? "Anomalous"
: "Normal");
snprintfrr(aux_buf, sizeof(aux_buf), "%u / %u",
exts->min_delay & TE_EXT_MASK,
exts->max_delay & TE_EXT_MASK);
json_object_string_add(avg_json, "micro-sec", aux_buf);
} else
sbuf_push(
buf, indent,
"%s Min/Max Link Delay: %u / %u (micro-sec)\n",
IS_ANORMAL(exts->min_delay) ? "Anomalous"
: "Normal",
exts->min_delay & TE_EXT_MASK,
exts->max_delay & TE_EXT_MASK);
}
if (IS_SUBTLV(exts, EXT_DELAY_VAR)) {
if (json) {
json_object_int_add(json, "delay-variation-micro-sec",
exts->delay_var & TE_EXT_MASK);
} else
sbuf_push(buf, indent,
"Delay Variation: %u (micro-sec)\n",
exts->delay_var & TE_EXT_MASK);
}
if (IS_SUBTLV(exts, EXT_PKT_LOSS)) {
if (json) {
snprintfrr(aux_buf, sizeof(aux_buf), "%g",
(float)((exts->pkt_loss & TE_EXT_MASK) *
LOSS_PRECISION));
struct json_object *link_json;
link_json = json_object_new_object();
json_object_object_add(json, "link-packet-loss",
link_json);
json_object_string_add(link_json, "loss",
IS_ANORMAL(exts->pkt_loss)
? "Anomalous"
: "Normal");
json_object_string_add(link_json, "percentaje",
aux_buf);
} else
sbuf_push(buf, indent, "%s Link Packet Loss: %g (%%)\n",
IS_ANORMAL(exts->pkt_loss) ? "Anomalous"
: "Normal",
(float)((exts->pkt_loss & TE_EXT_MASK) *
LOSS_PRECISION));
}
if (IS_SUBTLV(exts, EXT_RES_BW)) {
if (json) {
snprintfrr(aux_buf, sizeof(aux_buf), "%g",
(exts->res_bw));
json_object_string_add(json,
"unidir-residual-band-bytes-sec",
aux_buf);
} else
sbuf_push(
buf, indent,
"Unidir. Residual Bandwidth: %g (Bytes/sec)\n",
exts->res_bw);
}
if (IS_SUBTLV(exts, EXT_AVA_BW)) {
if (json) {
snprintfrr(aux_buf, sizeof(aux_buf), "%g",
(exts->ava_bw));
json_object_string_add(
json, "unidir-available-band-bytes-sec",
aux_buf);
} else
sbuf_push(
buf, indent,
"Unidir. Available Bandwidth: %g (Bytes/sec)\n",
exts->ava_bw);
}
if (IS_SUBTLV(exts, EXT_USE_BW)) {
if (json) {
snprintfrr(aux_buf, sizeof(aux_buf), "%g",
(exts->use_bw));
json_object_string_add(json,
"unidir-utilized-band-bytes-sec",
aux_buf);
} else
sbuf_push(
buf, indent,
"Unidir. Utilized Bandwidth: %g (Bytes/sec)\n",
exts->use_bw);
}
/* Segment Routing Adjacency as per RFC8667 section #2.2.1 */
if (IS_SUBTLV(exts, EXT_ADJ_SID)) {
struct isis_adj_sid *adj;
if (json) {
struct json_object *arr_adj_json, *flags_json;
arr_adj_json = json_object_new_array();
json_object_object_add(json, "adj-sid", arr_adj_json);
for (adj = (struct isis_adj_sid *)exts->adj_sid.head;
adj; adj = adj->next) {
snprintfrr(cnt_buf, sizeof(cnt_buf), "%d",
adj->sid);
flags_json = json_object_new_object();
json_object_int_add(flags_json, "sid",
adj->sid);
json_object_int_add(flags_json, "weight",
adj->weight);
json_object_string_add(
flags_json, "flag-f",
adj->flags & EXT_SUBTLV_LINK_ADJ_SID_FFLG
? "1"
: "0");
json_object_string_add(
flags_json, "flag-b",
adj->flags & EXT_SUBTLV_LINK_ADJ_SID_BFLG
? "1"
: "0");
json_object_string_add(
flags_json, "flag-v",
adj->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG
? "1"
: "0");
json_object_string_add(
flags_json, "flag-l",
adj->flags & EXT_SUBTLV_LINK_ADJ_SID_LFLG
? "1"
: "0");
json_object_string_add(
flags_json, "flag-s",
adj->flags & EXT_SUBTLV_LINK_ADJ_SID_SFLG
? "1"
: "0");
json_object_string_add(
flags_json, "flag-p",
adj->flags & EXT_SUBTLV_LINK_ADJ_SID_PFLG
? "1"
: "0");
json_object_array_add(arr_adj_json, flags_json);
}
} else
for (adj = (struct isis_adj_sid *)exts->adj_sid.head;
adj; adj = adj->next) {
sbuf_push(
buf, indent,
"Adjacency-SID: %u, Weight: %hhu, Flags: F:%c B:%c, V:%c, L:%c, S:%c, P:%c\n",
adj->sid, adj->weight,
adj->flags & EXT_SUBTLV_LINK_ADJ_SID_FFLG
? '1'
: '0',
adj->flags & EXT_SUBTLV_LINK_ADJ_SID_BFLG
? '1'
: '0',
adj->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG
? '1'
: '0',
adj->flags & EXT_SUBTLV_LINK_ADJ_SID_LFLG
? '1'
: '0',
adj->flags & EXT_SUBTLV_LINK_ADJ_SID_SFLG
? '1'
: '0',
adj->flags & EXT_SUBTLV_LINK_ADJ_SID_PFLG
? '1'
: '0');
}
}
/* Segment Routing LAN-Adjacency as per RFC8667 section #2.2.2 */
if (IS_SUBTLV(exts, EXT_LAN_ADJ_SID)) {
struct isis_lan_adj_sid *lan;
if (json) {
struct json_object *arr_adj_json, *flags_json;
arr_adj_json = json_object_new_array();
json_object_object_add(json, "lan-adj-sid",
arr_adj_json);
for (lan = (struct isis_lan_adj_sid *)
exts->adj_sid.head;
lan; lan = lan->next) {
if (((mtid == ISIS_MT_IPV4_UNICAST) &&
(lan->family != AF_INET)) ||
((mtid == ISIS_MT_IPV6_UNICAST) &&
(lan->family != AF_INET6)))
continue;
snprintfrr(cnt_buf, sizeof(cnt_buf), "%d",
lan->sid);
flags_json = json_object_new_object();
json_object_int_add(flags_json, "sid",
lan->sid);
json_object_int_add(flags_json, "weight",
lan->weight);
json_object_string_add(
flags_json, "flag-f",
lan->flags & EXT_SUBTLV_LINK_ADJ_SID_FFLG
? "1"
: "0");
json_object_string_add(
flags_json, "flag-b",
lan->flags & EXT_SUBTLV_LINK_ADJ_SID_BFLG
? "1"
: "0");
json_object_string_add(
flags_json, "flag-v",
lan->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG
? "1"
: "0");
json_object_string_add(
flags_json, "flag-l",
lan->flags & EXT_SUBTLV_LINK_ADJ_SID_LFLG
? "1"
: "0");
json_object_string_add(
flags_json, "flag-s",
lan->flags & EXT_SUBTLV_LINK_ADJ_SID_SFLG
? "1"
: "0");
json_object_string_add(
flags_json, "flag-p",
lan->flags & EXT_SUBTLV_LINK_ADJ_SID_PFLG
? "1"
: "0");
json_object_array_add(arr_adj_json, flags_json);
}
} else
for (lan = (struct isis_lan_adj_sid *)
exts->lan_sid.head;
lan; lan = lan->next) {
if (((mtid == ISIS_MT_IPV4_UNICAST) &&
(lan->family != AF_INET)) ||
((mtid == ISIS_MT_IPV6_UNICAST) &&
(lan->family != AF_INET6)))
continue;
sbuf_push(
buf, indent,
"Lan-Adjacency-SID: %u, Weight: %hhu, Flags: F:%c B:%c, V:%c, L:%c, S:%c, P:%c\n"
" Neighbor-ID: %pSY\n",
lan->sid, lan->weight,
lan->flags & EXT_SUBTLV_LINK_ADJ_SID_FFLG
? '1'
: '0',
lan->flags & EXT_SUBTLV_LINK_ADJ_SID_BFLG
? '1'
: '0',
lan->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG
? '1'
: '0',
lan->flags & EXT_SUBTLV_LINK_ADJ_SID_LFLG
? '1'
: '0',
lan->flags & EXT_SUBTLV_LINK_ADJ_SID_SFLG
? '1'
: '0',
lan->flags & EXT_SUBTLV_LINK_ADJ_SID_PFLG
? '1'
: '0',
lan->neighbor_id);
}
}
for (ALL_LIST_ELEMENTS_RO(exts->aslas, node, asla))
format_item_asla_subtlvs(asla, buf, indent);
}
static void free_item_ext_subtlvs(struct isis_ext_subtlvs *exts)
{
isis_del_ext_subtlvs(exts);
}
static int pack_item_ext_subtlv_asla(struct isis_asla_subtlvs *asla,
struct stream *s, size_t *min_len)
{
size_t subtlv_len;
size_t subtlv_len_pos;
/* Sub TLV header */
stream_putc(s, ISIS_SUBTLV_ASLA);
subtlv_len_pos = stream_get_endp(s);
stream_putc(s, 0); /* length will be filled later */
/* SABM Flag/Length */
if (asla->legacy)
stream_putc(s, ASLA_LEGACY_FLAG | asla->standard_apps_length);
else
stream_putc(s, asla->standard_apps_length);
stream_putc(s, asla->user_def_apps_length); /* UDABM Flag/Length */
stream_putc(s, asla->standard_apps);
stream_putc(s, asla->user_def_apps);
/* Administrative Group */
if (IS_SUBTLV(asla, EXT_ADM_GRP)) {
stream_putc(s, ISIS_SUBTLV_ADMIN_GRP);
stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
stream_putl(s, asla->admin_group);
}
/* Extended Administrative Group */
if (IS_SUBTLV(asla, EXT_EXTEND_ADM_GRP) &&
admin_group_nb_words(&asla->ext_admin_group) != 0) {
size_t ag_length;
size_t ag_length_pos;
struct admin_group *ag;
stream_putc(s, ISIS_SUBTLV_EXT_ADMIN_GRP);
ag_length_pos = stream_get_endp(s);
stream_putc(s, 0); /* length will be filled later*/
ag = &asla->ext_admin_group;
for (size_t i = 0; i < admin_group_nb_words(ag); i++)
stream_putl(s, ag->bitmap.data[i]);
ag_length = stream_get_endp(s) - ag_length_pos - 1;
stream_putc_at(s, ag_length_pos, ag_length);
}
if (IS_SUBTLV(asla, EXT_MAX_BW)) {
stream_putc(s, ISIS_SUBTLV_MAX_BW);
stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
stream_putf(s, asla->max_bw);
}
if (IS_SUBTLV(asla, EXT_MAX_RSV_BW)) {
stream_putc(s, ISIS_SUBTLV_MAX_RSV_BW);
stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
stream_putf(s, asla->max_rsv_bw);
}
if (IS_SUBTLV(asla, EXT_UNRSV_BW)) {
stream_putc(s, ISIS_SUBTLV_UNRSV_BW);
stream_putc(s, ISIS_SUBTLV_UNRSV_BW_SIZE);
for (int j = 0; j < MAX_CLASS_TYPE; j++)
stream_putf(s, asla->unrsv_bw[j]);
}
if (IS_SUBTLV(asla, EXT_TE_METRIC)) {
stream_putc(s, ISIS_SUBTLV_TE_METRIC);
stream_putc(s, ISIS_SUBTLV_TE_METRIC_SIZE);
stream_put3(s, asla->te_metric);
}
if (IS_SUBTLV(asla, EXT_DELAY)) {
stream_putc(s, ISIS_SUBTLV_AV_DELAY);
stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
stream_putl(s, asla->delay);
}
if (IS_SUBTLV(asla, EXT_MM_DELAY)) {
stream_putc(s, ISIS_SUBTLV_MM_DELAY);
stream_putc(s, ISIS_SUBTLV_MM_DELAY_SIZE);
stream_putl(s, asla->min_delay);
stream_putl(s, asla->max_delay);
}
if (IS_SUBTLV(asla, EXT_DELAY_VAR)) {
stream_putc(s, ISIS_SUBTLV_DELAY_VAR);
stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
stream_putl(s, asla->delay_var);
}
if (IS_SUBTLV(asla, EXT_PKT_LOSS)) {
stream_putc(s, ISIS_SUBTLV_PKT_LOSS);
stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
stream_putl(s, asla->pkt_loss);
}
if (IS_SUBTLV(asla, EXT_RES_BW)) {
stream_putc(s, ISIS_SUBTLV_RES_BW);
stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
stream_putf(s, asla->res_bw);
}
if (IS_SUBTLV(asla, EXT_AVA_BW)) {
stream_putc(s, ISIS_SUBTLV_AVA_BW);
stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
stream_putf(s, asla->ava_bw);
}
if (IS_SUBTLV(asla, EXT_USE_BW)) {
stream_putc(s, ISIS_SUBTLV_USE_BW);
stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
stream_putf(s, asla->use_bw);
}
subtlv_len = stream_get_endp(s) - subtlv_len_pos - 1;
stream_putc_at(s, subtlv_len_pos, subtlv_len);
return 0;
}
static int pack_item_ext_subtlvs(struct isis_ext_subtlvs *exts,
struct stream *s, size_t *min_len)
{
struct isis_asla_subtlvs *asla;
struct listnode *node;
uint8_t size;
int ret;
if (STREAM_WRITEABLE(s) < ISIS_SUBTLV_MAX_SIZE) {
*min_len = ISIS_SUBTLV_MAX_SIZE;
return 1;
}
if (IS_SUBTLV(exts, EXT_ADM_GRP)) {
stream_putc(s, ISIS_SUBTLV_ADMIN_GRP);
stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
stream_putl(s, exts->adm_group);
}
if (IS_SUBTLV(exts, EXT_EXTEND_ADM_GRP) &&
admin_group_nb_words(&exts->ext_admin_group) != 0) {
/* Extended Administrative Group */
size_t ag_length;
size_t ag_length_pos;
struct admin_group *ag;
stream_putc(s, ISIS_SUBTLV_EXT_ADMIN_GRP);
ag_length_pos = stream_get_endp(s);
stream_putc(s, 0); /* length will be filled later*/
ag = &exts->ext_admin_group;
for (size_t i = 0; i < admin_group_nb_words(ag); i++)
stream_putl(s, ag->bitmap.data[i]);
ag_length = stream_get_endp(s) - ag_length_pos - 1;
stream_putc_at(s, ag_length_pos, ag_length);
}
if (IS_SUBTLV(exts, EXT_LLRI)) {
stream_putc(s, ISIS_SUBTLV_LLRI);
stream_putc(s, ISIS_SUBTLV_LLRI_SIZE);
stream_putl(s, exts->local_llri);
stream_putl(s, exts->remote_llri);
}
if (IS_SUBTLV(exts, EXT_LOCAL_ADDR)) {
stream_putc(s, ISIS_SUBTLV_LOCAL_IPADDR);
stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
stream_put(s, &exts->local_addr.s_addr, 4);
}
if (IS_SUBTLV(exts, EXT_NEIGH_ADDR)) {
stream_putc(s, ISIS_SUBTLV_RMT_IPADDR);
stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
stream_put(s, &exts->neigh_addr.s_addr, 4);
}
if (IS_SUBTLV(exts, EXT_LOCAL_ADDR6)) {
stream_putc(s, ISIS_SUBTLV_LOCAL_IPADDR6);
stream_putc(s, ISIS_SUBTLV_IPV6_ADDR_SIZE);
stream_put(s, &exts->local_addr6, 16);
}
if (IS_SUBTLV(exts, EXT_NEIGH_ADDR6)) {
stream_putc(s, ISIS_SUBTLV_RMT_IPADDR6);
stream_putc(s, ISIS_SUBTLV_IPV6_ADDR_SIZE);
stream_put(s, &exts->neigh_addr6, 16);
}
if (IS_SUBTLV(exts, EXT_MAX_BW)) {
stream_putc(s, ISIS_SUBTLV_MAX_BW);
stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
stream_putf(s, exts->max_bw);
}
if (IS_SUBTLV(exts, EXT_MAX_RSV_BW)) {
stream_putc(s, ISIS_SUBTLV_MAX_RSV_BW);
stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
stream_putf(s, exts->max_rsv_bw);
}
if (IS_SUBTLV(exts, EXT_UNRSV_BW)) {
stream_putc(s, ISIS_SUBTLV_UNRSV_BW);
stream_putc(s, ISIS_SUBTLV_UNRSV_BW_SIZE);
for (int j = 0; j < MAX_CLASS_TYPE; j++)
stream_putf(s, exts->unrsv_bw[j]);
}
if (IS_SUBTLV(exts, EXT_TE_METRIC)) {
stream_putc(s, ISIS_SUBTLV_TE_METRIC);
stream_putc(s, ISIS_SUBTLV_TE_METRIC_SIZE);
stream_put3(s, exts->te_metric);
}
if (IS_SUBTLV(exts, EXT_RMT_AS)) {
stream_putc(s, ISIS_SUBTLV_RAS);
stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
stream_putl(s, exts->remote_as);
}
if (IS_SUBTLV(exts, EXT_RMT_IP)) {
stream_putc(s, ISIS_SUBTLV_RIP);
stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
stream_put(s, &exts->remote_ip.s_addr, 4);
}
if (IS_SUBTLV(exts, EXT_DELAY)) {
stream_putc(s, ISIS_SUBTLV_AV_DELAY);
stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
stream_putl(s, exts->delay);
}
if (IS_SUBTLV(exts, EXT_MM_DELAY)) {
stream_putc(s, ISIS_SUBTLV_MM_DELAY);
stream_putc(s, ISIS_SUBTLV_MM_DELAY_SIZE);
stream_putl(s, exts->min_delay);
stream_putl(s, exts->max_delay);
}
if (IS_SUBTLV(exts, EXT_DELAY_VAR)) {
stream_putc(s, ISIS_SUBTLV_DELAY_VAR);
stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
stream_putl(s, exts->delay_var);
}
if (IS_SUBTLV(exts, EXT_PKT_LOSS)) {
stream_putc(s, ISIS_SUBTLV_PKT_LOSS);
stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
stream_putl(s, exts->pkt_loss);
}
if (IS_SUBTLV(exts, EXT_RES_BW)) {
stream_putc(s, ISIS_SUBTLV_RES_BW);
stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
stream_putf(s, exts->res_bw);
}
if (IS_SUBTLV(exts, EXT_AVA_BW)) {
stream_putc(s, ISIS_SUBTLV_AVA_BW);
stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
stream_putf(s, exts->ava_bw);
}
if (IS_SUBTLV(exts, EXT_USE_BW)) {
stream_putc(s, ISIS_SUBTLV_USE_BW);
stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
stream_putf(s, exts->use_bw);
}
/* Segment Routing Adjacency as per RFC8667 section #2.2.1 */
if (IS_SUBTLV(exts, EXT_ADJ_SID)) {
struct isis_adj_sid *adj;
for (adj = (struct isis_adj_sid *)exts->adj_sid.head; adj;
adj = adj->next) {
stream_putc(s, ISIS_SUBTLV_ADJ_SID);
size = ISIS_SUBTLV_ADJ_SID_SIZE;
if (!(adj->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG))
size++;
stream_putc(s, size);
stream_putc(s, adj->flags);
stream_putc(s, adj->weight);
if (adj->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG)
stream_put3(s, adj->sid);
else
stream_putl(s, adj->sid);
}
}
/* Segment Routing LAN-Adjacency as per RFC8667 section #2.2.2 */
if (IS_SUBTLV(exts, EXT_LAN_ADJ_SID)) {
struct isis_lan_adj_sid *lan;
for (lan = (struct isis_lan_adj_sid *)exts->lan_sid.head; lan;
lan = lan->next) {
stream_putc(s, ISIS_SUBTLV_LAN_ADJ_SID);
size = ISIS_SUBTLV_LAN_ADJ_SID_SIZE;
if (!(lan->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG))
size++;
stream_putc(s, size);
stream_putc(s, lan->flags);
stream_putc(s, lan->weight);
stream_put(s, lan->neighbor_id, 6);
if (lan->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG)
stream_put3(s, lan->sid);
else
stream_putl(s, lan->sid);
}
}
for (ALL_LIST_ELEMENTS_RO(exts->aslas, node, asla)) {
ret = pack_item_ext_subtlv_asla(asla, s, min_len);
if (ret < 0)
return ret;
}
return 0;
}
static int unpack_item_ext_subtlv_asla(uint16_t mtid, uint8_t subtlv_len,
struct stream *s, struct sbuf *log,
int indent,
struct isis_ext_subtlvs *exts)
{
/* Standard App Identifier Bit Flags/Length */
uint8_t sabm_flag_len;
/* User-defined App Identifier Bit Flags/Length */
uint8_t uabm_flag_len;
uint8_t sabm[ASLA_APP_IDENTIFIER_BIT_LENGTH] = {0};
uint8_t uabm[ASLA_APP_IDENTIFIER_BIT_LENGTH] = {0};
uint8_t readable = subtlv_len;
uint8_t subsubtlv_type;
uint8_t subsubtlv_len;
size_t nb_groups;
struct isis_asla_subtlvs *asla;
if (subtlv_len < ISIS_SUBSUBTLV_HDR_SIZE) {
TLV_SIZE_MISMATCH(log, indent, "ASLA");
return -1;
}
asla = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*asla));
admin_group_init(&asla->ext_admin_group);
sabm_flag_len = stream_getc(s);
uabm_flag_len = stream_getc(s);
asla->legacy = CHECK_FLAG(sabm_flag_len, ASLA_LEGACY_FLAG);
asla->standard_apps_length = ASLA_APPS_LENGTH_MASK & sabm_flag_len;
asla->user_def_apps_length = ASLA_APPS_LENGTH_MASK & uabm_flag_len;
readable -= ISIS_SUBSUBTLV_HDR_SIZE;
if (readable <
asla->standard_apps_length + asla->user_def_apps_length) {
TLV_SIZE_MISMATCH(log, indent, "ASLA");
return -1;
}
for (int i = 0; i < asla->standard_apps_length; i++)
sabm[i] = stream_getc(s);
for (int i = 0; i < asla->user_def_apps_length; i++)
uabm[i] = stream_getc(s);
readable -= (asla->standard_apps_length + asla->user_def_apps_length);
asla->standard_apps = sabm[0];
asla->user_def_apps = uabm[0];
while (readable > 0) {
if (readable < ISIS_SUBSUBTLV_HDR_SIZE) {
TLV_SIZE_MISMATCH(log, indent, "ASLA Sub TLV");
return -1;
}
subsubtlv_type = stream_getc(s);
subsubtlv_len = stream_getc(s);
readable -= ISIS_SUBSUBTLV_HDR_SIZE;
switch (subsubtlv_type) {
case ISIS_SUBTLV_ADMIN_GRP:
if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) {
TLV_SIZE_MISMATCH(log, indent,
"ASLA Adm Group");
stream_forward_getp(s, subsubtlv_len);
} else {
asla->admin_group = stream_getl(s);
SET_SUBTLV(asla, EXT_ADM_GRP);
}
break;
case ISIS_SUBTLV_EXT_ADMIN_GRP:
nb_groups = subsubtlv_len / sizeof(uint32_t);
for (size_t i = 0; i < nb_groups; i++) {
uint32_t val = stream_getl(s);
admin_group_bulk_set(&asla->ext_admin_group,
val, i);
}
SET_SUBTLV(asla, EXT_EXTEND_ADM_GRP);
break;
case ISIS_SUBTLV_MAX_BW:
if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) {
TLV_SIZE_MISMATCH(log, indent,
"Maximum Bandwidth");
stream_forward_getp(s, subsubtlv_len);
} else {
asla->max_bw = stream_getf(s);
SET_SUBTLV(asla, EXT_MAX_BW);
}
break;
case ISIS_SUBTLV_MAX_RSV_BW:
if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) {
TLV_SIZE_MISMATCH(
log, indent,
"Maximum Reservable Bandwidth");
stream_forward_getp(s, subsubtlv_len);
} else {
asla->max_rsv_bw = stream_getf(s);
SET_SUBTLV(asla, EXT_MAX_RSV_BW);
}
break;
case ISIS_SUBTLV_UNRSV_BW:
if (subsubtlv_len != ISIS_SUBTLV_UNRSV_BW_SIZE) {
TLV_SIZE_MISMATCH(log, indent,
"Unreserved Bandwidth");
stream_forward_getp(s, subsubtlv_len);
} else {
for (int i = 0; i < MAX_CLASS_TYPE; i++)
asla->unrsv_bw[i] = stream_getf(s);
SET_SUBTLV(asla, EXT_UNRSV_BW);
}
break;
case ISIS_SUBTLV_TE_METRIC:
if (subsubtlv_len != ISIS_SUBTLV_TE_METRIC_SIZE) {
TLV_SIZE_MISMATCH(log, indent,
"Traffic Engineering Metric");
stream_forward_getp(s, subsubtlv_len);
} else {
asla->te_metric = stream_get3(s);
SET_SUBTLV(asla, EXT_TE_METRIC);
}
break;
/* Extended Metrics as defined in RFC 7810 */
case ISIS_SUBTLV_AV_DELAY:
if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) {
TLV_SIZE_MISMATCH(log, indent,
"Average Link Delay");
stream_forward_getp(s, subsubtlv_len);
} else {
asla->delay = stream_getl(s);
SET_SUBTLV(asla, EXT_DELAY);
}
break;
case ISIS_SUBTLV_MM_DELAY:
if (subsubtlv_len != ISIS_SUBTLV_MM_DELAY_SIZE) {
TLV_SIZE_MISMATCH(log, indent,
"Min/Max Link Delay");
stream_forward_getp(s, subsubtlv_len);
} else {
asla->min_delay = stream_getl(s);
asla->max_delay = stream_getl(s);
SET_SUBTLV(asla, EXT_MM_DELAY);
}
break;
case ISIS_SUBTLV_DELAY_VAR:
if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) {
TLV_SIZE_MISMATCH(log, indent,
"Delay Variation");
stream_forward_getp(s, subsubtlv_len);
} else {
asla->delay_var = stream_getl(s);
SET_SUBTLV(asla, EXT_DELAY_VAR);
}
break;
case ISIS_SUBTLV_PKT_LOSS:
if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) {
TLV_SIZE_MISMATCH(log, indent,
"Link Packet Loss");
stream_forward_getp(s, subsubtlv_len);
} else {
asla->pkt_loss = stream_getl(s);
SET_SUBTLV(asla, EXT_PKT_LOSS);
}
break;
case ISIS_SUBTLV_RES_BW:
if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) {
TLV_SIZE_MISMATCH(
log, indent,
"Unidirectional Residual Bandwidth");
stream_forward_getp(s, subsubtlv_len);
} else {
asla->res_bw = stream_getf(s);
SET_SUBTLV(asla, EXT_RES_BW);
}
break;
case ISIS_SUBTLV_AVA_BW:
if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) {
TLV_SIZE_MISMATCH(
log, indent,
"Unidirectional Available Bandwidth");
stream_forward_getp(s, subsubtlv_len);
} else {
asla->ava_bw = stream_getf(s);
SET_SUBTLV(asla, EXT_AVA_BW);
}
break;
case ISIS_SUBTLV_USE_BW:
if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) {
TLV_SIZE_MISMATCH(
log, indent,
"Unidirectional Utilized Bandwidth");
stream_forward_getp(s, subsubtlv_len);
} else {
asla->use_bw = stream_getf(s);
SET_SUBTLV(asla, EXT_USE_BW);
}
break;
default:
zlog_debug("unknown (t,l)=(%u,%u)", subsubtlv_type,
subsubtlv_len);
stream_forward_getp(s, subsubtlv_len);
break;
}
readable -= subsubtlv_len;
}
listnode_add(exts->aslas, asla);
return 0;
}
static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s,
struct sbuf *log, void *dest, int indent)
{
uint8_t sum = 0;
uint8_t subtlv_type;
uint8_t subtlv_len;
size_t nb_groups;
uint32_t val;
struct isis_extended_reach *rv = dest;
struct isis_ext_subtlvs *exts = isis_alloc_ext_subtlvs();
rv->subtlvs = exts;
/*
* Parse subTLVs until reach subTLV length
* Check that it remains at least 2 bytes: subTLV Type & Length
*/
while (len > sum + 2) {
/* Read SubTLV Type and Length */
subtlv_type = stream_getc(s);
subtlv_len = stream_getc(s);
if (subtlv_len > len - sum - ISIS_SUBTLV_HDR_SIZE) {
sbuf_push(
log, indent,
"TLV %hhu: Available data %u is less than TLV size %u !\n",
subtlv_type, len - sum - ISIS_SUBTLV_HDR_SIZE,
subtlv_len);
return 1;
}
switch (subtlv_type) {
/* Standard Metric as defined in RFC5305 */
case ISIS_SUBTLV_ADMIN_GRP:
if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
TLV_SIZE_MISMATCH(log, indent,
"Administrative Group");
stream_forward_getp(s, subtlv_len);
} else {
exts->adm_group = stream_getl(s);
SET_SUBTLV(exts, EXT_ADM_GRP);
}
break;
case ISIS_SUBTLV_EXT_ADMIN_GRP:
nb_groups = subtlv_len / sizeof(uint32_t);
for (size_t i = 0; i < nb_groups; i++) {
val = stream_getl(s);
admin_group_bulk_set(&exts->ext_admin_group,
val, i);
}
SET_SUBTLV(exts, EXT_EXTEND_ADM_GRP);
break;
case ISIS_SUBTLV_LLRI:
if (subtlv_len != ISIS_SUBTLV_LLRI_SIZE) {
TLV_SIZE_MISMATCH(log, indent, "Link ID");
stream_forward_getp(s, subtlv_len);
} else {
exts->local_llri = stream_getl(s);
exts->remote_llri = stream_getl(s);
SET_SUBTLV(exts, EXT_LLRI);
}
break;
case ISIS_SUBTLV_LOCAL_IPADDR:
if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
TLV_SIZE_MISMATCH(log, indent,
"Local IP address");
stream_forward_getp(s, subtlv_len);
} else {
stream_get(&exts->local_addr.s_addr, s, 4);
SET_SUBTLV(exts, EXT_LOCAL_ADDR);
}
break;
case ISIS_SUBTLV_RMT_IPADDR:
if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
TLV_SIZE_MISMATCH(log, indent,
"Remote IP address");
stream_forward_getp(s, subtlv_len);
} else {
stream_get(&exts->neigh_addr.s_addr, s, 4);
SET_SUBTLV(exts, EXT_NEIGH_ADDR);
}
break;
case ISIS_SUBTLV_LOCAL_IPADDR6:
if (subtlv_len != ISIS_SUBTLV_IPV6_ADDR_SIZE) {
TLV_SIZE_MISMATCH(log, indent,
"Local IPv6 address");
stream_forward_getp(s, subtlv_len);
} else {
stream_get(&exts->local_addr6, s, 16);
SET_SUBTLV(exts, EXT_LOCAL_ADDR6);
}
break;
case ISIS_SUBTLV_RMT_IPADDR6:
if (subtlv_len != ISIS_SUBTLV_IPV6_ADDR_SIZE) {
TLV_SIZE_MISMATCH(log, indent,
"Remote IPv6 address");
stream_forward_getp(s, subtlv_len);
} else {
stream_get(&exts->neigh_addr6, s, 16);
SET_SUBTLV(exts, EXT_NEIGH_ADDR6);
}
break;
case ISIS_SUBTLV_MAX_BW:
if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
TLV_SIZE_MISMATCH(log, indent,
"Maximum Bandwidth");
stream_forward_getp(s, subtlv_len);
} else {
exts->max_bw = stream_getf(s);
SET_SUBTLV(exts, EXT_MAX_BW);
}
break;
case ISIS_SUBTLV_MAX_RSV_BW:
if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
TLV_SIZE_MISMATCH(
log, indent,
"Maximum Reservable Bandwidth");
stream_forward_getp(s, subtlv_len);
} else {
exts->max_rsv_bw = stream_getf(s);
SET_SUBTLV(exts, EXT_MAX_RSV_BW);
}
break;
case ISIS_SUBTLV_UNRSV_BW:
if (subtlv_len != ISIS_SUBTLV_UNRSV_BW_SIZE) {
TLV_SIZE_MISMATCH(log, indent,
"Unreserved Bandwidth");
stream_forward_getp(s, subtlv_len);
} else {
for (int i = 0; i < MAX_CLASS_TYPE; i++)
exts->unrsv_bw[i] = stream_getf(s);
SET_SUBTLV(exts, EXT_UNRSV_BW);
}
break;
case ISIS_SUBTLV_TE_METRIC:
if (subtlv_len != ISIS_SUBTLV_TE_METRIC_SIZE) {
TLV_SIZE_MISMATCH(log, indent,
"Traffic Engineering Metric");
stream_forward_getp(s, subtlv_len);
} else {
exts->te_metric = stream_get3(s);
SET_SUBTLV(exts, EXT_TE_METRIC);
}
break;
case ISIS_SUBTLV_RAS:
if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
TLV_SIZE_MISMATCH(log, indent,
"Remote AS number");
stream_forward_getp(s, subtlv_len);
} else {
exts->remote_as = stream_getl(s);
SET_SUBTLV(exts, EXT_RMT_AS);
}
break;
case ISIS_SUBTLV_RIP:
if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
TLV_SIZE_MISMATCH(log, indent,
"Remote ASBR IP Address");
stream_forward_getp(s, subtlv_len);
} else {
stream_get(&exts->remote_ip.s_addr, s, 4);
SET_SUBTLV(exts, EXT_RMT_IP);
}
break;
/* Extended Metrics as defined in RFC 7810 */
case ISIS_SUBTLV_AV_DELAY:
if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
TLV_SIZE_MISMATCH(log, indent,
"Average Link Delay");
stream_forward_getp(s, subtlv_len);
} else {
exts->delay = stream_getl(s);
SET_SUBTLV(exts, EXT_DELAY);
}
break;
case ISIS_SUBTLV_MM_DELAY:
if (subtlv_len != ISIS_SUBTLV_MM_DELAY_SIZE) {
TLV_SIZE_MISMATCH(log, indent,
"Min/Max Link Delay");
stream_forward_getp(s, subtlv_len);
} else {
exts->min_delay = stream_getl(s);
exts->max_delay = stream_getl(s);
SET_SUBTLV(exts, EXT_MM_DELAY);
}
break;
case ISIS_SUBTLV_DELAY_VAR:
if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
TLV_SIZE_MISMATCH(log, indent,
"Delay Variation");
stream_forward_getp(s, subtlv_len);
} else {
exts->delay_var = stream_getl(s);
SET_SUBTLV(exts, EXT_DELAY_VAR);
}
break;
case ISIS_SUBTLV_PKT_LOSS:
if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
TLV_SIZE_MISMATCH(log, indent,
"Link Packet Loss");
stream_forward_getp(s, subtlv_len);
} else {
exts->pkt_loss = stream_getl(s);
SET_SUBTLV(exts, EXT_PKT_LOSS);
}
break;
case ISIS_SUBTLV_RES_BW:
if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
TLV_SIZE_MISMATCH(
log, indent,
"Unidirectional Residual Bandwidth");
stream_forward_getp(s, subtlv_len);
} else {
exts->res_bw = stream_getf(s);
SET_SUBTLV(exts, EXT_RES_BW);
}
break;
case ISIS_SUBTLV_AVA_BW:
if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
TLV_SIZE_MISMATCH(
log, indent,
"Unidirectional Available Bandwidth");
stream_forward_getp(s, subtlv_len);
} else {
exts->ava_bw = stream_getf(s);
SET_SUBTLV(exts, EXT_AVA_BW);
}
break;
case ISIS_SUBTLV_USE_BW:
if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
TLV_SIZE_MISMATCH(
log, indent,
"Unidirectional Utilized Bandwidth");
stream_forward_getp(s, subtlv_len);
} else {
exts->use_bw = stream_getf(s);
SET_SUBTLV(exts, EXT_USE_BW);
}
break;
/* Segment Routing Adjacency as per RFC8667 section #2.2.1 */
case ISIS_SUBTLV_ADJ_SID:
if (subtlv_len != ISIS_SUBTLV_ADJ_SID_SIZE
&& subtlv_len != ISIS_SUBTLV_ADJ_SID_SIZE + 1) {
TLV_SIZE_MISMATCH(log, indent, "Adjacency SID");
stream_forward_getp(s, subtlv_len);
} else {
struct isis_adj_sid *adj;
adj = XCALLOC(MTYPE_ISIS_SUBTLV,
sizeof(struct isis_adj_sid));
adj->flags = stream_getc(s);
adj->weight = stream_getc(s);
if (adj->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG
&& subtlv_len != ISIS_SUBTLV_ADJ_SID_SIZE) {
TLV_SIZE_MISMATCH(log, indent,
"Adjacency SID");
stream_forward_getp(s, subtlv_len - 2);
XFREE(MTYPE_ISIS_SUBTLV, adj);
break;
}
if (!(adj->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG)
&& subtlv_len
!= ISIS_SUBTLV_ADJ_SID_SIZE
+ 1) {
TLV_SIZE_MISMATCH(log, indent,
"Adjacency SID");
stream_forward_getp(s, subtlv_len - 2);
XFREE(MTYPE_ISIS_SUBTLV, adj);
break;
}
if (adj->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG) {
adj->sid = stream_get3(s);
adj->sid &= MPLS_LABEL_VALUE_MASK;
} else {
adj->sid = stream_getl(s);
}
if (mtid == ISIS_MT_IPV4_UNICAST)
adj->family = AF_INET;
if (mtid == ISIS_MT_IPV6_UNICAST)
adj->family = AF_INET6;
append_item(&exts->adj_sid,
(struct isis_item *)adj);
SET_SUBTLV(exts, EXT_ADJ_SID);
}
break;
/* Segment Routing LAN-Adjacency as per RFC8667 section 2.2.2 */
case ISIS_SUBTLV_LAN_ADJ_SID:
if (subtlv_len != ISIS_SUBTLV_LAN_ADJ_SID_SIZE
&& subtlv_len != ISIS_SUBTLV_LAN_ADJ_SID_SIZE + 1) {
TLV_SIZE_MISMATCH(log, indent,
"LAN-Adjacency SID");
stream_forward_getp(s, subtlv_len);
} else {
struct isis_lan_adj_sid *lan;
lan = XCALLOC(MTYPE_ISIS_SUBTLV,
sizeof(struct isis_lan_adj_sid));
lan->flags = stream_getc(s);
lan->weight = stream_getc(s);
stream_get(&(lan->neighbor_id), s,
ISIS_SYS_ID_LEN);
if (lan->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG
&& subtlv_len
!= ISIS_SUBTLV_LAN_ADJ_SID_SIZE) {
TLV_SIZE_MISMATCH(log, indent,
"LAN-Adjacency SID");
stream_forward_getp(
s, subtlv_len - 2
- ISIS_SYS_ID_LEN);
XFREE(MTYPE_ISIS_SUBTLV, lan);
break;
}
if (!(lan->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG)
&& subtlv_len
!= ISIS_SUBTLV_LAN_ADJ_SID_SIZE
+ 1) {
TLV_SIZE_MISMATCH(log, indent,
"LAN-Adjacency SID");
stream_forward_getp(
s, subtlv_len - 2
- ISIS_SYS_ID_LEN);
XFREE(MTYPE_ISIS_SUBTLV, lan);
break;
}
if (lan->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG) {
lan->sid = stream_get3(s);
lan->sid &= MPLS_LABEL_VALUE_MASK;
} else {
lan->sid = stream_getl(s);
}
if (mtid == ISIS_MT_IPV4_UNICAST)
lan->family = AF_INET;
if (mtid == ISIS_MT_IPV6_UNICAST)
lan->family = AF_INET6;
append_item(&exts->lan_sid,
(struct isis_item *)lan);
SET_SUBTLV(exts, EXT_LAN_ADJ_SID);
}
break;
case ISIS_SUBTLV_ASLA:
if (unpack_item_ext_subtlv_asla(mtid, subtlv_len, s,
log, indent,
exts) < 0) {
sbuf_push(log, indent, "TLV parse error");
}
break;
default:
/* Skip unknown TLV */
stream_forward_getp(s, subtlv_len);
break;
}
sum += subtlv_len + ISIS_SUBTLV_HDR_SIZE;
}
return 0;
}
/* Functions for Sub-TLV 3 SR Prefix-SID as per RFC8667 section 2.1 */
static struct isis_item *copy_item_prefix_sid(struct isis_item *i)
{
struct isis_prefix_sid *sid = (struct isis_prefix_sid *)i;
struct isis_prefix_sid *rv = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*rv));
rv->flags = sid->flags;
rv->algorithm = sid->algorithm;
rv->value = sid->value;
return (struct isis_item *)rv;
}
static void format_item_prefix_sid(uint16_t mtid, struct isis_item *i,
struct sbuf *buf, struct json_object *json,
int indent)
{
struct isis_prefix_sid *sid = (struct isis_prefix_sid *)i;
if (json) {
struct json_object *sr_json;
sr_json = json_object_new_object();
json_object_object_add(json, "sr", sr_json);
if (sid->flags & ISIS_PREFIX_SID_VALUE) {
json_object_int_add(sr_json, "label", sid->value);
} else {
json_object_int_add(sr_json, "index", sid->value);
}
json_object_int_add(sr_json, "alg", sid->algorithm);
json_object_string_add(
sr_json, "readvertised",
((sid->flags & ISIS_PREFIX_SID_READVERTISED) ? "yes"
: ""));
json_object_string_add(
sr_json, "node",
((sid->flags & ISIS_PREFIX_SID_NODE) ? "yes" : ""));
json_object_string_add(sr_json, "php",
((sid->flags & ISIS_PREFIX_SID_NO_PHP)
? "no-php"
: "php"));
json_object_string_add(
sr_json, "explicit-null",
((sid->flags & ISIS_PREFIX_SID_EXPLICIT_NULL) ? "yes"
: ""));
json_object_string_add(
sr_json, "value",
((sid->flags & ISIS_PREFIX_SID_VALUE) ? "yes" : ""));
json_object_string_add(
sr_json, "local",
((sid->flags & ISIS_PREFIX_SID_LOCAL) ? "yes" : ""));
} else {
sbuf_push(buf, indent, "SR Prefix-SID ");
if (sid->flags & ISIS_PREFIX_SID_VALUE) {
sbuf_push(buf, 0, "Label: %u, ", sid->value);
} else {
sbuf_push(buf, 0, "Index: %u, ", sid->value);
}
sbuf_push(buf, 0, "Algorithm: %hhu, ", sid->algorithm);
sbuf_push(buf, 0, "Flags:%s%s%s%s%s%s\n",
sid->flags & ISIS_PREFIX_SID_READVERTISED
? " READVERTISED"
: "",
sid->flags & ISIS_PREFIX_SID_NODE ? " NODE" : "",
sid->flags & ISIS_PREFIX_SID_NO_PHP ? " NO-PHP"
: " PHP",
sid->flags & ISIS_PREFIX_SID_EXPLICIT_NULL
? " EXPLICIT-NULL"
: "",
sid->flags & ISIS_PREFIX_SID_VALUE ? " VALUE" : "",
sid->flags & ISIS_PREFIX_SID_LOCAL ? " LOCAL" : "");
}
}
static void free_item_prefix_sid(struct isis_item *i)
{
XFREE(MTYPE_ISIS_SUBTLV, i);
}
static int pack_item_prefix_sid(struct isis_item *i, struct stream *s,
size_t *min_len)
{
struct isis_prefix_sid *sid = (struct isis_prefix_sid *)i;
uint8_t size = (sid->flags & ISIS_PREFIX_SID_VALUE) ? 5 : 6;
if (STREAM_WRITEABLE(s) < size) {
*min_len = size;
return 1;
}
stream_putc(s, sid->flags);
stream_putc(s, sid->algorithm);
if (sid->flags & ISIS_PREFIX_SID_VALUE) {
stream_put3(s, sid->value);
} else {
stream_putl(s, sid->value);
}
return 0;
}
static int unpack_item_prefix_sid(uint16_t mtid, uint8_t len, struct stream *s,
struct sbuf *log, void *dest, int indent)
{
struct isis_subtlvs *subtlvs = dest;
struct isis_prefix_sid sid = {
};
sbuf_push(log, indent, "Unpacking SR Prefix-SID...\n");
if (len < 5) {
sbuf_push(log, indent,
"Not enough data left. (expected 5 or more bytes, got %hhu)\n",
len);
return 1;
}
sid.flags = stream_getc(s);
if (!!(sid.flags & ISIS_PREFIX_SID_VALUE)
!= !!(sid.flags & ISIS_PREFIX_SID_LOCAL)) {
sbuf_push(log, indent, "Flags implausible: Local Flag needs to match Value Flag\n");
return 1;
}
sid.algorithm = stream_getc(s);
uint8_t expected_size = (sid.flags & ISIS_PREFIX_SID_VALUE)
? ISIS_SUBTLV_PREFIX_SID_SIZE
: ISIS_SUBTLV_PREFIX_SID_SIZE + 1;
if (len != expected_size) {
sbuf_push(log, indent,
"TLV size differs from expected size. (expected %u but got %hhu)\n",
expected_size, len);
return 1;
}
if (sid.flags & ISIS_PREFIX_SID_VALUE) {
sid.value = stream_get3(s);
if (!IS_MPLS_UNRESERVED_LABEL(sid.value)) {
sbuf_push(log, indent, "Invalid absolute SID %u\n",
sid.value);
return 1;
}
} else {
sid.value = stream_getl(s);
}
format_item_prefix_sid(mtid, (struct isis_item *)&sid, log, NULL, indent + 2);
append_item(&subtlvs->prefix_sids, copy_item_prefix_sid((struct isis_item *)&sid));
return 0;
}
/* Functions for Sub-TVL ??? IPv6 Source Prefix */
static struct prefix_ipv6 *copy_subtlv_ipv6_source_prefix(struct prefix_ipv6 *p)
{
if (!p)
return NULL;
struct prefix_ipv6 *rv = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*rv));
rv->family = p->family;
rv->prefixlen = p->prefixlen;
memcpy(&rv->prefix, &p->prefix, sizeof(rv->prefix));
return rv;
}
static void format_subtlv_ipv6_source_prefix(struct prefix_ipv6 *p,
struct sbuf *buf,
struct json_object *json,
int indent)
{
if (!p)
return;
char prefixbuf[PREFIX2STR_BUFFER];
if (json) {
prefix2str(p, prefixbuf, sizeof(prefixbuf));
json_object_string_add(json, "ipv6-src-prefix", prefixbuf);
} else {
sbuf_push(buf, indent, "IPv6 Source Prefix: %s\n",
prefix2str(p, prefixbuf, sizeof(prefixbuf)));
}
}
static int pack_subtlv_ipv6_source_prefix(struct prefix_ipv6 *p,
struct stream *s)
{
if (!p)
return 0;
if (STREAM_WRITEABLE(s) < 3 + (unsigned)PSIZE(p->prefixlen))
return 1;
stream_putc(s, ISIS_SUBTLV_IPV6_SOURCE_PREFIX);
stream_putc(s, 1 + PSIZE(p->prefixlen));
stream_putc(s, p->prefixlen);
stream_put(s, &p->prefix, PSIZE(p->prefixlen));
return 0;
}
static int unpack_subtlv_ipv6_source_prefix(enum isis_tlv_context context,
uint8_t tlv_type, uint8_t tlv_len,
struct stream *s, struct sbuf *log,
void *dest, int indent)
{
struct isis_subtlvs *subtlvs = dest;
struct prefix_ipv6 p = {
.family = AF_INET6,
};
sbuf_push(log, indent, "Unpacking IPv6 Source Prefix Sub-TLV...\n");
if (tlv_len < 1) {
sbuf_push(log, indent,
"Not enough data left. (expected 1 or more bytes, got %hhu)\n",
tlv_len);
return 1;
}
p.prefixlen = stream_getc(s);
if (p.prefixlen > IPV6_MAX_BITLEN) {
sbuf_push(log, indent, "Prefixlen %u is implausible for IPv6\n",
p.prefixlen);
return 1;
}
if (tlv_len != 1 + PSIZE(p.prefixlen)) {
sbuf_push(
log, indent,
"TLV size differs from expected size for the prefixlen. (expected %u but got %hhu)\n",
1 + PSIZE(p.prefixlen), tlv_len);
return 1;
}
stream_get(&p.prefix, s, PSIZE(p.prefixlen));
if (subtlvs->source_prefix) {
sbuf_push(
log, indent,
"WARNING: source prefix Sub-TLV present multiple times.\n");
/* Ignore all but first occurrence of the source prefix Sub-TLV
*/
return 0;
}
subtlvs->source_prefix = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(p));
memcpy(subtlvs->source_prefix, &p, sizeof(p));
return 0;
}
static struct isis_item *copy_item(enum isis_tlv_context context,
enum isis_tlv_type type,
struct isis_item *item);
static void copy_items(enum isis_tlv_context context, enum isis_tlv_type type,
struct isis_item_list *src, struct isis_item_list *dest);
static void format_items_(uint16_t mtid, enum isis_tlv_context context,
enum isis_tlv_type type, struct isis_item_list *items,
struct sbuf *buf, struct json_object *json,
int indent);
#define format_items(...) format_items_(ISIS_MT_IPV4_UNICAST, __VA_ARGS__)
static void free_items(enum isis_tlv_context context, enum isis_tlv_type type,
struct isis_item_list *items);
static int pack_items_(uint16_t mtid, enum isis_tlv_context context,
enum isis_tlv_type type, struct isis_item_list *items,
struct stream *s, struct isis_tlvs **fragment_tlvs,
const struct pack_order_entry *pe,
struct isis_tlvs *(*new_fragment)(struct list *l),
struct list *new_fragment_arg);
#define pack_items(...) pack_items_(ISIS_MT_IPV4_UNICAST, __VA_ARGS__)
/* Functions related to subtlvs */
static struct isis_subtlvs *isis_alloc_subtlvs(enum isis_tlv_context context)
{
struct isis_subtlvs *result;
result = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*result));
result->context = context;
init_item_list(&result->prefix_sids);
init_item_list(&result->srv6_end_sids);
return result;
}
static struct isis_subtlvs *copy_subtlvs(struct isis_subtlvs *subtlvs)
{
if (!subtlvs)
return NULL;
struct isis_subtlvs *rv = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*rv));
rv->context = subtlvs->context;
copy_items(subtlvs->context, ISIS_SUBTLV_PREFIX_SID,
&subtlvs->prefix_sids, &rv->prefix_sids);
rv->source_prefix =
copy_subtlv_ipv6_source_prefix(subtlvs->source_prefix);
return rv;
}
static void format_subtlvs(struct isis_subtlvs *subtlvs, struct sbuf *buf,
struct json_object *json, int indent)
{
format_items(subtlvs->context, ISIS_SUBTLV_PREFIX_SID,
&subtlvs->prefix_sids, buf, json, indent);
format_subtlv_ipv6_source_prefix(subtlvs->source_prefix, buf, json, indent);
}
static void isis_free_subtlvs(struct isis_subtlvs *subtlvs)
{
if (!subtlvs)
return;
free_items(subtlvs->context, ISIS_SUBTLV_PREFIX_SID,
&subtlvs->prefix_sids);
XFREE(MTYPE_ISIS_SUBTLV, subtlvs->source_prefix);
XFREE(MTYPE_ISIS_SUBTLV, subtlvs);
}
static int pack_subtlvs(struct isis_subtlvs *subtlvs, struct stream *s)
{
int rv;
size_t subtlv_len_pos = stream_get_endp(s);
if (STREAM_WRITEABLE(s) < 1)
return 1;
stream_putc(s, 0); /* Put 0 as subtlvs length, filled in later */
rv = pack_items(subtlvs->context, ISIS_SUBTLV_PREFIX_SID,
&subtlvs->prefix_sids, s, NULL, NULL, NULL, NULL);
if (rv)
return rv;
rv = pack_subtlv_ipv6_source_prefix(subtlvs->source_prefix, s);
if (rv)
return rv;
size_t subtlv_len = stream_get_endp(s) - subtlv_len_pos - 1;
if (subtlv_len > 255)
return 1;
stream_putc_at(s, subtlv_len_pos, subtlv_len);
return 0;
}
static int unpack_tlvs(enum isis_tlv_context context, size_t avail_len,
struct stream *stream, struct sbuf *log, void *dest,
int indent, bool *unpacked_known_tlvs);
/* Functions for Sub-TLV 5 SRv6 End SID as per RFC 9352 section #7.2 */
static struct isis_item *copy_item_srv6_end_sid(struct isis_item *i)
{
struct isis_srv6_end_sid_subtlv *sid =
(struct isis_srv6_end_sid_subtlv *)i;
struct isis_srv6_end_sid_subtlv *rv =
XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*rv));
rv->behavior = sid->behavior;
rv->sid = sid->sid;
return (struct isis_item *)rv;
}
static void format_item_srv6_end_sid(uint16_t mtid, struct isis_item *i,
struct sbuf *buf, struct json_object *json,
int indent)
{
struct isis_srv6_end_sid_subtlv *sid =
(struct isis_srv6_end_sid_subtlv *)i;
if (json) {
struct json_object *sid_json;
sid_json = json_object_new_object();
json_object_object_add(json, "srv6-end-sid", sid_json);
json_object_string_add(sid_json, "endpoint-behavior",
seg6local_action2str(sid->behavior));
json_object_string_addf(sid_json, "sid-value", "%pI6",
&sid->sid);
} else {
sbuf_push(buf, indent, "SRv6 End SID ");
sbuf_push(buf, 0, "Endpoint Behavior: %s, ",
seg6local_action2str(sid->behavior));
sbuf_push(buf, 0, "SID value: %pI6\n", &sid->sid);
}
}
static void free_item_srv6_end_sid(struct isis_item *i)
{
XFREE(MTYPE_ISIS_SUBTLV, i);
}
static int pack_item_srv6_end_sid(struct isis_item *i, struct stream *s,
size_t *min_len)
{
struct isis_srv6_end_sid_subtlv *sid =
(struct isis_srv6_end_sid_subtlv *)i;
if (STREAM_WRITEABLE(s) < 19) {
*min_len = 19;
return 1;
}
stream_putc(s, sid->flags);
stream_putw(s, sid->behavior);
stream_put(s, &sid->sid, IPV6_MAX_BYTELEN);
/* Put 0 as Sub-Sub-TLV length, because we don't support any Sub-Sub-TLV
* at this time */
stream_putc(s, 0);
return 0;
}
static int unpack_item_srv6_end_sid(uint16_t mtid, uint8_t len,
struct stream *s, struct sbuf *log,
void *dest, int indent)
{
struct isis_subtlvs *subtlvs = dest;
struct isis_srv6_end_sid_subtlv *sid;
size_t consume;
uint8_t subsubtlv_len;
sbuf_push(log, indent, "Unpacking SRv6 End SID...\n");
consume = 19;
if (len < consume) {
sbuf_push(
log, indent,
"Not enough data left. (expected 19 or more bytes, got %hhu)\n",
len);
return 1;
}
sid = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*sid));
sid->flags = stream_getc(s);
sid->behavior = stream_getw(s);
stream_get(&sid->sid, s, IPV6_MAX_BYTELEN);
format_item_srv6_end_sid(mtid, (struct isis_item *)sid, log, NULL,
indent + 2);
/* Process Sub-Sub-TLVs */
consume += 1;
if (len < consume) {
sbuf_push(
log, indent,
"Expected 1 byte of Sub-Sub-TLV len, but no more data persent.\n");
goto out;
}
subsubtlv_len = stream_getc(s);
consume += subsubtlv_len;
if (len < consume) {
sbuf_push(log, indent,
"Expected %hhu bytes of Sub-Sub-TLVs, but only %u bytes available.\n",
subsubtlv_len, len - ((uint8_t)consume - subsubtlv_len));
goto out;
}
append_item(&subtlvs->srv6_end_sids, (struct isis_item *)sid);
return 0;
out:
if (sid)
free_item_srv6_end_sid((struct isis_item *)sid);
return 1;
}
/* Functions related to TLVs 1 Area Addresses */
static struct isis_item *copy_item_area_address(struct isis_item *i)
{
struct isis_area_address *addr = (struct isis_area_address *)i;
struct isis_area_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
rv->len = addr->len;
memcpy(rv->addr, addr->addr, addr->len);
return (struct isis_item *)rv;
}
static void format_item_area_address(uint16_t mtid, struct isis_item *i,
struct sbuf *buf, struct json_object *json,
int indent)
{
struct isis_area_address *addr = (struct isis_area_address *)i;
struct iso_address iso_addr;
memcpy(iso_addr.area_addr, addr->addr, ISO_ADDR_SIZE);
iso_addr.addr_len = addr->len;
if (json)
json_object_string_addf(json, "area-addr", "%pIS", &iso_addr);
else
sbuf_push(buf, indent, "Area Address: %pIS\n", &iso_addr);
}
static void free_item_area_address(struct isis_item *i)
{
XFREE(MTYPE_ISIS_TLV, i);
}
static int pack_item_area_address(struct isis_item *i, struct stream *s,
size_t *min_len)
{
struct isis_area_address *addr = (struct isis_area_address *)i;
if (STREAM_WRITEABLE(s) < (unsigned)1 + addr->len) {
*min_len = (unsigned)1 + addr->len;
return 1;
}
stream_putc(s, addr->len);
stream_put(s, addr->addr, addr->len);
return 0;
}
static int unpack_item_area_address(uint16_t mtid, uint8_t len,
struct stream *s, struct sbuf *log,
void *dest, int indent)
{
struct isis_tlvs *tlvs = dest;
struct isis_area_address *rv = NULL;
sbuf_push(log, indent, "Unpack area address...\n");
if (len < 1) {
sbuf_push(
log, indent,
"Not enough data left. (Expected 1 byte of address length, got %hhu)\n",
len);
goto out;
}
rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
rv->len = stream_getc(s);
if (len < 1 + rv->len) {
sbuf_push(log, indent, "Not enough data left. (Expected %hhu bytes of address, got %u)\n",
rv->len, len - 1);
goto out;
}
if (rv->len < 1 || rv->len > 20) {
sbuf_push(log, indent,
"Implausible area address length %hhu\n",
rv->len);
goto out;
}
stream_get(rv->addr, s, rv->len);
format_item_area_address(ISIS_MT_IPV4_UNICAST, (struct isis_item *)rv,
log, NULL, indent + 2);
append_item(&tlvs->area_addresses, (struct isis_item *)rv);
return 0;
out:
XFREE(MTYPE_ISIS_TLV, rv);
return 1;
}
/* Functions related to TLV 2 (Old-Style) IS Reach */
static struct isis_item *copy_item_oldstyle_reach(struct isis_item *i)
{
struct isis_oldstyle_reach *r = (struct isis_oldstyle_reach *)i;
struct isis_oldstyle_reach *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
memcpy(rv->id, r->id, 7);
rv->metric = r->metric;
return (struct isis_item *)rv;
}
static void format_item_oldstyle_reach(uint16_t mtid, struct isis_item *i,
struct sbuf *buf,
struct json_object *json, int indent)
{
struct isis_oldstyle_reach *r = (struct isis_oldstyle_reach *)i;
char sys_id[ISO_SYSID_STRLEN];
snprintfrr(sys_id, ISO_SYSID_STRLEN, "%pPN", r->id);
if (json) {
struct json_object *old_json;
old_json = json_object_new_object();
json_object_object_add(json, "old-reach-style", old_json);
json_object_string_add(old_json, "is-reach", sys_id);
json_object_int_add(old_json, "metric", r->metric);
} else
sbuf_push(buf, indent, "IS Reachability: %s (Metric: %hhu)\n",
sys_id, r->metric);
}
static void free_item_oldstyle_reach(struct isis_item *i)
{
XFREE(MTYPE_ISIS_TLV, i);
}
static int pack_item_oldstyle_reach(struct isis_item *i, struct stream *s,
size_t *min_len)
{
struct isis_oldstyle_reach *r = (struct isis_oldstyle_reach *)i;
if (STREAM_WRITEABLE(s) < 11) {
*min_len = 11;
return 1;
}
stream_putc(s, r->metric);
stream_putc(s, 0x80); /* delay metric - unsupported */
stream_putc(s, 0x80); /* expense metric - unsupported */
stream_putc(s, 0x80); /* error metric - unsupported */
stream_put(s, r->id, 7);
return 0;
}
static int unpack_item_oldstyle_reach(uint16_t mtid, uint8_t len,
struct stream *s, struct sbuf *log,
void *dest, int indent)
{
struct isis_tlvs *tlvs = dest;
sbuf_push(log, indent, "Unpack oldstyle reach...\n");
if (len < 11) {
sbuf_push(
log, indent,
"Not enough data left.(Expected 11 bytes of reach information, got %hhu)\n",
len);
return 1;
}
struct isis_oldstyle_reach *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
rv->metric = stream_getc(s);
if ((rv->metric & 0x3f) != rv->metric) {
sbuf_push(log, indent, "Metric has unplausible format\n");
rv->metric &= 0x3f;
}
stream_forward_getp(s, 3); /* Skip other metrics */
stream_get(rv->id, s, 7);
format_item_oldstyle_reach(mtid, (struct isis_item *)rv, log, NULL,
indent + 2);
append_item(&tlvs->oldstyle_reach, (struct isis_item *)rv);
return 0;
}
/* Functions related to TLV 6 LAN Neighbors */
static struct isis_item *copy_item_lan_neighbor(struct isis_item *i)
{
struct isis_lan_neighbor *n = (struct isis_lan_neighbor *)i;
struct isis_lan_neighbor *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
memcpy(rv->mac, n->mac, 6);
return (struct isis_item *)rv;
}
static void format_item_lan_neighbor(uint16_t mtid, struct isis_item *i,
struct sbuf *buf, struct json_object *json,
int indent)
{
struct isis_lan_neighbor *n = (struct isis_lan_neighbor *)i;
char sys_id[ISO_SYSID_STRLEN];
snprintfrr(sys_id, ISO_SYSID_STRLEN, "%pSY", n->mac);
if (json)
json_object_string_add(json, "lan-neighbor", sys_id);
else
sbuf_push(buf, indent, "LAN Neighbor: %s\n", sys_id);
}
static void free_item_lan_neighbor(struct isis_item *i)
{
XFREE(MTYPE_ISIS_TLV, i);
}
static int pack_item_lan_neighbor(struct isis_item *i, struct stream *s,
size_t *min_len)
{
struct isis_lan_neighbor *n = (struct isis_lan_neighbor *)i;
if (STREAM_WRITEABLE(s) < 6) {
*min_len = 6;
return 1;
}
stream_put(s, n->mac, 6);
return 0;
}
static int unpack_item_lan_neighbor(uint16_t mtid, uint8_t len,
struct stream *s, struct sbuf *log,
void *dest, int indent)
{
struct isis_tlvs *tlvs = dest;
sbuf_push(log, indent, "Unpack LAN neighbor...\n");
if (len < 6) {
sbuf_push(
log, indent,
"Not enough data left.(Expected 6 bytes of mac, got %hhu)\n",
len);
return 1;
}
struct isis_lan_neighbor *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
stream_get(rv->mac, s, 6);
format_item_lan_neighbor(mtid, (struct isis_item *)rv, log, NULL, indent + 2);
append_item(&tlvs->lan_neighbor, (struct isis_item *)rv);
return 0;
}
/* Functions related to TLV 9 LSP Entry */
static struct isis_item *copy_item_lsp_entry(struct isis_item *i)
{
struct isis_lsp_entry *e = (struct isis_lsp_entry *)i;
struct isis_lsp_entry *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
rv->rem_lifetime = e->rem_lifetime;
memcpy(rv->id, e->id, sizeof(rv->id));
rv->seqno = e->seqno;
rv->checksum = e->checksum;
return (struct isis_item *)rv;
}
static void format_item_lsp_entry(uint16_t mtid, struct isis_item *i,
struct sbuf *buf, struct json_object *json,
int indent)
{
struct isis_lsp_entry *e = (struct isis_lsp_entry *)i;
char sys_id[ISO_SYSID_STRLEN];
snprintfrr(sys_id, ISO_SYSID_STRLEN, "%pLS", e->id);
if (json) {
char buf[255];
struct json_object *lsp_json;
lsp_json = json_object_new_object();
json_object_object_add(json, "lsp-entry", lsp_json);
json_object_string_add(lsp_json, "id", sys_id);
snprintfrr(buf,sizeof(buf),"0x%08x",e->seqno);
json_object_string_add(lsp_json, "seq", buf);
snprintfrr(buf,sizeof(buf),"0x%04hx",e->checksum);
json_object_string_add(lsp_json, "chksum", buf);
json_object_int_add(lsp_json, "lifetime", e->checksum);
} else
sbuf_push(
buf, indent,
"LSP Entry: %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus\n",
sys_id, e->seqno, e->checksum, e->rem_lifetime);
}
static void free_item_lsp_entry(struct isis_item *i)
{
XFREE(MTYPE_ISIS_TLV, i);
}
static int pack_item_lsp_entry(struct isis_item *i, struct stream *s,
size_t *min_len)
{
struct isis_lsp_entry *e = (struct isis_lsp_entry *)i;
if (STREAM_WRITEABLE(s) < 16) {
*min_len = 16;
return 1;
}
stream_putw(s, e->rem_lifetime);
stream_put(s, e->id, 8);
stream_putl(s, e->seqno);
stream_putw(s, e->checksum);
return 0;
}
static int unpack_item_lsp_entry(uint16_t mtid, uint8_t len, struct stream *s,
struct sbuf *log, void *dest, int indent)
{
struct isis_tlvs *tlvs = dest;
sbuf_push(log, indent, "Unpack LSP entry...\n");
if (len < 16) {
sbuf_push(
log, indent,
"Not enough data left. (Expected 16 bytes of LSP info, got %hhu",
len);
return 1;
}
struct isis_lsp_entry *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
rv->rem_lifetime = stream_getw(s);
stream_get(rv->id, s, 8);
rv->seqno = stream_getl(s);
rv->checksum = stream_getw(s);
format_item_lsp_entry(mtid, (struct isis_item *)rv, log, NULL, indent + 2);
append_item(&tlvs->lsp_entries, (struct isis_item *)rv);
return 0;
}
/* Functions related to TLVs 22/222 Extended Reach/MT Reach */
static struct isis_item *copy_item_extended_reach(struct isis_item *i)
{
struct isis_extended_reach *r = (struct isis_extended_reach *)i;
struct isis_extended_reach *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
memcpy(rv->id, r->id, 7);
rv->metric = r->metric;
if (r->subtlvs)
rv->subtlvs = copy_item_ext_subtlvs(r->subtlvs, -1);
return (struct isis_item *)rv;
}
static void format_item_extended_reach(uint16_t mtid, struct isis_item *i,
struct sbuf *buf,
struct json_object *json, int indent)
{
struct isis_extended_reach *r = (struct isis_extended_reach *)i;
char sys_id[ISO_SYSID_STRLEN];
snprintfrr(sys_id, ISO_SYSID_STRLEN, "%pPN", r->id);
if (json) {
struct json_object *reach_json;
reach_json = json_object_new_object();
json_object_object_add(json, "ext-reach", reach_json);
json_object_string_add(
reach_json, "mt-id",
(mtid == ISIS_MT_IPV4_UNICAST) ? "Extended" : "MT");
json_object_string_add(reach_json, "id", sys_id);
json_object_int_add(reach_json, "metric", r->metric);
if (mtid != ISIS_MT_IPV4_UNICAST)
json_object_string_add(reach_json, "mt-name",
isis_mtid2str(mtid));
if (r->subtlvs)
format_item_ext_subtlvs(r->subtlvs, NULL, json,
indent + 2, mtid);
} else {
sbuf_push(buf, indent, "%s Reachability: %s (Metric: %u)",
(mtid == ISIS_MT_IPV4_UNICAST) ? "Extended" : "MT",
sys_id, r->metric);
if (mtid != ISIS_MT_IPV4_UNICAST)
sbuf_push(buf, 0, " %s", isis_mtid2str(mtid));
sbuf_push(buf, 0, "\n");
if (r->subtlvs)
format_item_ext_subtlvs(r->subtlvs, buf, NULL,
indent + 2, mtid);
}
}
static void free_item_extended_reach(struct isis_item *i)
{
struct isis_extended_reach *item = (struct isis_extended_reach *)i;
if (item->subtlvs != NULL)
free_item_ext_subtlvs(item->subtlvs);
XFREE(MTYPE_ISIS_TLV, item);
}
static int pack_item_extended_reach(struct isis_item *i, struct stream *s,
size_t *min_len)
{
struct isis_extended_reach *r = (struct isis_extended_reach *)i;
size_t len;
size_t len_pos;
if (STREAM_WRITEABLE(s) < 11 + ISIS_SUBTLV_MAX_SIZE) {
*min_len = 11 + ISIS_SUBTLV_MAX_SIZE;
return 1;
}
stream_put(s, r->id, sizeof(r->id));
stream_put3(s, r->metric);
len_pos = stream_get_endp(s);
/* Real length will be adjust after adding subTLVs */
stream_putc(s, 11);
if (r->subtlvs)
pack_item_ext_subtlvs(r->subtlvs, s, min_len);
/* Adjust length */
len = stream_get_endp(s) - len_pos - 1;
stream_putc_at(s, len_pos, len);
return 0;
}
static int unpack_item_extended_reach(uint16_t mtid, uint8_t len,
struct stream *s, struct sbuf *log,
void *dest, int indent)
{
struct isis_tlvs *tlvs = dest;
struct isis_extended_reach *rv = NULL;
uint8_t subtlv_len;
struct isis_item_list *items;
if (mtid == ISIS_MT_IPV4_UNICAST) {
items = &tlvs->extended_reach;
} else {
items = isis_get_mt_items(&tlvs->mt_reach, mtid);
}
sbuf_push(log, indent, "Unpacking %s reachability...\n",
(mtid == ISIS_MT_IPV4_UNICAST) ? "extended" : "mt");
if (len < 11) {
sbuf_push(log, indent,
"Not enough data left. (expected 11 or more bytes, got %hhu)\n",
len);
goto out;
}
rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
stream_get(rv->id, s, 7);
rv->metric = stream_get3(s);
subtlv_len = stream_getc(s);
if ((size_t)len < ((size_t)11) + subtlv_len) {
sbuf_push(log, indent,
"Not enough data left for subtlv size %hhu, there are only %u bytes left.\n",
subtlv_len, len - 11);
goto out;
}
sbuf_push(log, indent, "Storing %hhu bytes of subtlvs\n",
subtlv_len);
if (subtlv_len) {
if (unpack_item_ext_subtlvs(mtid, subtlv_len, s, log, rv,
indent + 4)) {
goto out;
}
}
format_item_extended_reach(mtid, (struct isis_item *)rv, log, NULL,
indent + 2);
append_item(items, (struct isis_item *)rv);
return 0;
out:
if (rv)
free_item_extended_reach((struct isis_item *)rv);
return 1;
}
/* Functions related to TLV 128 (Old-Style) IP Reach */
static struct isis_item *copy_item_oldstyle_ip_reach(struct isis_item *i)
{
struct isis_oldstyle_ip_reach *r = (struct isis_oldstyle_ip_reach *)i;
struct isis_oldstyle_ip_reach *rv =
XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
rv->metric = r->metric;
rv->prefix = r->prefix;
return (struct isis_item *)rv;
}
static void format_item_oldstyle_ip_reach(uint16_t mtid, struct isis_item *i,
struct sbuf *buf,
struct json_object *json, int indent)
{
struct isis_oldstyle_ip_reach *r = (struct isis_oldstyle_ip_reach *)i;
char prefixbuf[PREFIX2STR_BUFFER];
if (json) {
struct json_object *old_json;
old_json = json_object_new_object();
json_object_object_add(json, "old-ip-reach-style", old_json);
json_object_string_add(old_json, "prefix",
prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf)));
json_object_int_add(old_json, "metric", r->metric);
} else
sbuf_push(buf, indent, "IP Reachability: %s (Metric: %hhu)\n",
prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf)),
r->metric);
}
static void free_item_oldstyle_ip_reach(struct isis_item *i)
{
XFREE(MTYPE_ISIS_TLV, i);
}
static int pack_item_oldstyle_ip_reach(struct isis_item *i, struct stream *s,
size_t *min_len)
{
struct isis_oldstyle_ip_reach *r = (struct isis_oldstyle_ip_reach *)i;
if (STREAM_WRITEABLE(s) < 12) {
*min_len = 12;
return 1;
}
stream_putc(s, r->metric);
stream_putc(s, 0x80); /* delay metric - unsupported */
stream_putc(s, 0x80); /* expense metric - unsupported */
stream_putc(s, 0x80); /* error metric - unsupported */
stream_put(s, &r->prefix.prefix, 4);
struct in_addr mask;
masklen2ip(r->prefix.prefixlen, &mask);
stream_put(s, &mask, sizeof(mask));
return 0;
}
static int unpack_item_oldstyle_ip_reach(uint16_t mtid, uint8_t len,
struct stream *s, struct sbuf *log,
void *dest, int indent)
{
sbuf_push(log, indent, "Unpack oldstyle ip reach...\n");
if (len < 12) {
sbuf_push(
log, indent,
"Not enough data left.(Expected 12 bytes of reach information, got %hhu)\n",
len);
return 1;
}
struct isis_oldstyle_ip_reach *rv =
XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
rv->metric = stream_getc(s);
if ((rv->metric & 0x7f) != rv->metric) {
sbuf_push(log, indent, "Metric has unplausible format\n");
rv->metric &= 0x7f;
}
stream_forward_getp(s, 3); /* Skip other metrics */
rv->prefix.family = AF_INET;
stream_get(&rv->prefix.prefix, s, 4);
struct in_addr mask;
stream_get(&mask, s, 4);
rv->prefix.prefixlen = ip_masklen(mask);
format_item_oldstyle_ip_reach(mtid, (struct isis_item *)rv, log, NULL,
indent + 2);
append_item(dest, (struct isis_item *)rv);
return 0;
}
/* Functions related to TLV 129 protocols supported */
static void copy_tlv_protocols_supported(struct isis_protocols_supported *src,
struct isis_protocols_supported *dest)
{
if (!src->protocols || !src->count)
return;
dest->count = src->count;
dest->protocols = XCALLOC(MTYPE_ISIS_TLV, src->count);
memcpy(dest->protocols, src->protocols, src->count);
}
static void format_tlv_protocols_supported(struct isis_protocols_supported *p,
struct sbuf *buf,
struct json_object *json, int indent)
{
if (!p || !p->count || !p->protocols)
return;
if (json) {
struct json_object *protocol_json;
char buf[255];
protocol_json = json_object_new_object();
json_object_object_add(json, "protocols-supported",
protocol_json);
for (uint8_t i = 0; i < p->count; i++) {
snprintfrr(buf, sizeof(buf), "%d", i);
json_object_string_add(protocol_json, buf,
nlpid2str(p->protocols[i]));
}
} else {
sbuf_push(buf, indent, "Protocols Supported: ");
for (uint8_t i = 0; i < p->count; i++) {
sbuf_push(buf, 0, "%s%s", nlpid2str(p->protocols[i]),
(i + 1 < p->count) ? ", " : "");
}
sbuf_push(buf, 0, "\n");
}
}
static void free_tlv_protocols_supported(struct isis_protocols_supported *p)
{
XFREE(MTYPE_ISIS_TLV, p->protocols);
}
static int pack_tlv_protocols_supported(struct isis_protocols_supported *p,
struct stream *s)
{
if (!p || !p->count || !p->protocols)
return 0;
if (STREAM_WRITEABLE(s) < (unsigned)(p->count + 2))
return 1;
stream_putc(s, ISIS_TLV_PROTOCOLS_SUPPORTED);
stream_putc(s, p->count);
stream_put(s, p->protocols, p->count);
return 0;
}
static int unpack_tlv_protocols_supported(enum isis_tlv_context context,
uint8_t tlv_type, uint8_t tlv_len,
struct stream *s, struct sbuf *log,
void *dest, int indent)
{
struct isis_tlvs *tlvs = dest;
sbuf_push(log, indent, "Unpacking Protocols Supported TLV...\n");
if (!tlv_len) {
sbuf_push(log, indent, "WARNING: No protocols included\n");
return 0;
}
if (tlvs->protocols_supported.protocols) {
sbuf_push(
log, indent,
"WARNING: protocols supported TLV present multiple times.\n");
stream_forward_getp(s, tlv_len);
return 0;
}
tlvs->protocols_supported.count = tlv_len;
tlvs->protocols_supported.protocols = XCALLOC(MTYPE_ISIS_TLV, tlv_len);
stream_get(tlvs->protocols_supported.protocols, s, tlv_len);
format_tlv_protocols_supported(&tlvs->protocols_supported, log, NULL,
indent + 2);
return 0;
}
/* Functions related to TLV 132 IPv4 Interface addresses */
static struct isis_item *copy_item_ipv4_address(struct isis_item *i)
{
struct isis_ipv4_address *a = (struct isis_ipv4_address *)i;
struct isis_ipv4_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
rv->addr = a->addr;
return (struct isis_item *)rv;
}
static void format_item_ipv4_address(uint16_t mtid, struct isis_item *i,
struct sbuf *buf, struct json_object *json,
int indent)
{
struct isis_ipv4_address *a = (struct isis_ipv4_address *)i;
char addrbuf[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &a->addr, addrbuf, sizeof(addrbuf));
if (json) {
json_object_string_add(json, "ipv4", addrbuf);
} else {
sbuf_push(buf, indent, "IPv4 Interface Address: %s\n", addrbuf);
}
}
static void free_item_ipv4_address(struct isis_item *i)
{
XFREE(MTYPE_ISIS_TLV, i);
}
static int pack_item_ipv4_address(struct isis_item *i, struct stream *s,
size_t *min_len)
{
struct isis_ipv4_address *a = (struct isis_ipv4_address *)i;
if (STREAM_WRITEABLE(s) < 4) {
*min_len = 4;
return 1;
}
stream_put(s, &a->addr, 4);
return 0;
}
static int unpack_item_ipv4_address(uint16_t mtid, uint8_t len,
struct stream *s, struct sbuf *log,
void *dest, int indent)
{
struct isis_tlvs *tlvs = dest;
sbuf_push(log, indent, "Unpack IPv4 Interface address...\n");
if (len < 4) {
sbuf_push(
log, indent,
"Not enough data left.(Expected 4 bytes of IPv4 address, got %hhu)\n",
len);
return 1;
}
struct isis_ipv4_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
stream_get(&rv->addr, s, 4);
format_item_ipv4_address(mtid, (struct isis_item *)rv, log, NULL, indent + 2);
append_item(&tlvs->ipv4_address, (struct isis_item *)rv);
return 0;
}
/* Functions related to TLV 232 IPv6 Interface addresses */
static struct isis_item *copy_item_ipv6_address(struct isis_item *i)
{
struct isis_ipv6_address *a = (struct isis_ipv6_address *)i;
struct isis_ipv6_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
rv->addr = a->addr;
return (struct isis_item *)rv;
}
static void format_item_ipv6_address(uint16_t mtid, struct isis_item *i,
struct sbuf *buf, struct json_object *json,
int indent)
{
struct isis_ipv6_address *a = (struct isis_ipv6_address *)i;
char addrbuf[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &a->addr, addrbuf, sizeof(addrbuf));
if (json)
json_object_string_add(json, "ipv6", addrbuf);
else
sbuf_push(buf, indent, "IPv6 Interface Address: %s\n", addrbuf);
}
static void free_item_ipv6_address(struct isis_item *i)
{
XFREE(MTYPE_ISIS_TLV, i);
}
static int pack_item_ipv6_address(struct isis_item *i, struct stream *s,
size_t *min_len)
{
struct isis_ipv6_address *a = (struct isis_ipv6_address *)i;
if (STREAM_WRITEABLE(s) < IPV6_MAX_BYTELEN) {
*min_len = IPV6_MAX_BYTELEN;
return 1;
}
stream_put(s, &a->addr, IPV6_MAX_BYTELEN);
return 0;
}
static int unpack_item_ipv6_address(uint16_t mtid, uint8_t len,
struct stream *s, struct sbuf *log,
void *dest, int indent)
{
struct isis_tlvs *tlvs = dest;
sbuf_push(log, indent, "Unpack IPv6 Interface address...\n");
if (len < 16) {
sbuf_push(
log, indent,
"Not enough data left.(Expected 16 bytes of IPv6 address, got %hhu)\n",
len);
return 1;
}
struct isis_ipv6_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
stream_get(&rv->addr, s, IPV6_MAX_BYTELEN);
format_item_ipv6_address(mtid, (struct isis_item *)rv, log, NULL, indent + 2);
append_item(&tlvs->ipv6_address, (struct isis_item *)rv);
return 0;
}
/* Functions related to TLV 233 Global IPv6 Interface addresses */
static struct isis_item *copy_item_global_ipv6_address(struct isis_item *i)
{
struct isis_ipv6_address *a = (struct isis_ipv6_address *)i;
struct isis_ipv6_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
rv->addr = a->addr;
return (struct isis_item *)rv;
}
static void format_item_global_ipv6_address(uint16_t mtid, struct isis_item *i,
struct sbuf *buf,
struct json_object *json,
int indent)
{
struct isis_ipv6_address *a = (struct isis_ipv6_address *)i;
char addrbuf[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &a->addr, addrbuf, sizeof(addrbuf));
if (json)
json_object_string_add(json, "global-ipv6", addrbuf);
else
sbuf_push(buf, indent, "Global IPv6 Interface Address: %s\n",
addrbuf);
}
static void free_item_global_ipv6_address(struct isis_item *i)
{
XFREE(MTYPE_ISIS_TLV, i);
}
static int pack_item_global_ipv6_address(struct isis_item *i, struct stream *s,
size_t *min_len)
{
struct isis_ipv6_address *a = (struct isis_ipv6_address *)i;
if (STREAM_WRITEABLE(s) < IPV6_MAX_BYTELEN) {
*min_len = IPV6_MAX_BYTELEN;
return 1;
}
stream_put(s, &a->addr, IPV6_MAX_BYTELEN);
return 0;
}
static int unpack_item_global_ipv6_address(uint16_t mtid, uint8_t len,
struct stream *s, struct sbuf *log,
void *dest, int indent)
{
struct isis_tlvs *tlvs = dest;
sbuf_push(log, indent, "Unpack Global IPv6 Interface address...\n");
if (len < IPV6_MAX_BYTELEN) {
sbuf_push(
log, indent,
"Not enough data left.(Expected 16 bytes of IPv6 address, got %hhu)\n",
len);
return 1;
}
struct isis_ipv6_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
stream_get(&rv->addr, s, IPV6_MAX_BYTELEN);
format_item_global_ipv6_address(mtid, (struct isis_item *)rv, log, NULL,
indent + 2);
append_item(&tlvs->global_ipv6_address, (struct isis_item *)rv);
return 0;
}
/* Functions related to TLV 229 MT Router information */
static struct isis_item *copy_item_mt_router_info(struct isis_item *i)
{
struct isis_mt_router_info *info = (struct isis_mt_router_info *)i;
struct isis_mt_router_info *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
rv->overload = info->overload;
rv->attached = info->attached;
rv->mtid = info->mtid;
return (struct isis_item *)rv;
}
static void format_item_mt_router_info(uint16_t mtid, struct isis_item *i,
struct sbuf *buf,
struct json_object *json, int indent)
{
struct isis_mt_router_info *info = (struct isis_mt_router_info *)i;
if (json) {
struct json_object *mt_json;
mt_json = json_object_new_object();
json_object_object_add(json, "mt", mt_json);
json_object_int_add(mt_json, "mtid", info->mtid);
json_object_string_add(mt_json, "overload", info->overload?"true":"false");
json_object_string_add(mt_json, "attached", info->attached?"true":"false");
} else
sbuf_push(buf, indent, "MT Router Info: %s%s%s\n",
isis_mtid2str_fake(info->mtid),
info->overload ? " Overload" : "",
info->attached ? " Attached" : "");
}
static void free_item_mt_router_info(struct isis_item *i)
{
XFREE(MTYPE_ISIS_TLV, i);
}
static int pack_item_mt_router_info(struct isis_item *i, struct stream *s,
size_t *min_len)
{
struct isis_mt_router_info *info = (struct isis_mt_router_info *)i;
if (STREAM_WRITEABLE(s) < 2) {
*min_len = 2;
return 1;
}
uint16_t entry = info->mtid;
if (info->overload)
entry |= ISIS_MT_OL_MASK;
if (info->attached)
entry |= ISIS_MT_AT_MASK;
stream_putw(s, entry);
return 0;
}
static int unpack_item_mt_router_info(uint16_t mtid, uint8_t len,
struct stream *s, struct sbuf *log,
void *dest, int indent)
{
struct isis_tlvs *tlvs = dest;
sbuf_push(log, indent, "Unpack MT Router info...\n");
if (len < 2) {
sbuf_push(
log, indent,
"Not enough data left.(Expected 2 bytes of MT info, got %hhu)\n",
len);
return 1;
}
struct isis_mt_router_info *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
uint16_t entry = stream_getw(s);
rv->overload = entry & ISIS_MT_OL_MASK;
rv->attached = entry & ISIS_MT_AT_MASK;
rv->mtid = entry & ISIS_MT_MASK;
format_item_mt_router_info(mtid, (struct isis_item *)rv, log, NULL,
indent + 2);
append_item(&tlvs->mt_router_info, (struct isis_item *)rv);
return 0;
}
/* Functions related to TLV 134 TE Router ID */
static struct in_addr *copy_tlv_te_router_id(const struct in_addr *id)
{
if (!id)
return NULL;
struct in_addr *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
memcpy(rv, id, sizeof(*rv));
return rv;
}
static void format_tlv_te_router_id(const struct in_addr *id, struct sbuf *buf,
struct json_object *json, int indent)
{
if (!id)
return;
char addrbuf[INET_ADDRSTRLEN];
inet_ntop(AF_INET, id, addrbuf, sizeof(addrbuf));
if (json)
json_object_string_add(json, "te-router-id", addrbuf);
else
sbuf_push(buf, indent, "TE Router ID: %s\n", addrbuf);
}
static void free_tlv_te_router_id(struct in_addr *id)
{
XFREE(MTYPE_ISIS_TLV, id);
}
static int pack_tlv_te_router_id(const struct in_addr *id, struct stream *s)
{
if (!id)
return 0;
if (STREAM_WRITEABLE(s) < (unsigned)(2 + sizeof(*id)))
return 1;
stream_putc(s, ISIS_TLV_TE_ROUTER_ID);
stream_putc(s, 4);
stream_put(s, id, 4);
return 0;
}
static int unpack_tlv_te_router_id(enum isis_tlv_context context,
uint8_t tlv_type, uint8_t tlv_len,
struct stream *s, struct sbuf *log,
void *dest, int indent)
{
struct isis_tlvs *tlvs = dest;
sbuf_push(log, indent, "Unpacking TE Router ID TLV...\n");
if (tlv_len != 4) {
sbuf_push(log, indent, "WARNING: Length invalid\n");
return 1;
}
if (tlvs->te_router_id) {
sbuf_push(log, indent,
"WARNING: TE Router ID present multiple times.\n");
stream_forward_getp(s, tlv_len);
return 0;
}
tlvs->te_router_id = XCALLOC(MTYPE_ISIS_TLV, 4);
stream_get(tlvs->te_router_id, s, 4);
format_tlv_te_router_id(tlvs->te_router_id, log, NULL, indent + 2);
return 0;
}
/* Functions related to TLVs 135/235 extended IP reach/MT IP Reach */
static struct isis_item *copy_item_extended_ip_reach(struct isis_item *i)
{
struct isis_extended_ip_reach *r = (struct isis_extended_ip_reach *)i;
struct isis_extended_ip_reach *rv =
XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
rv->metric = r->metric;
rv->down = r->down;
rv->prefix = r->prefix;
rv->subtlvs = copy_subtlvs(r->subtlvs);
return (struct isis_item *)rv;
}
static void format_item_extended_ip_reach(uint16_t mtid, struct isis_item *i,
struct sbuf *buf,
struct json_object *json, int indent)
{
struct isis_extended_ip_reach *r = (struct isis_extended_ip_reach *)i;
char prefixbuf[PREFIX2STR_BUFFER];
if (json) {
struct json_object *ext_json;
ext_json = json_object_new_object();
json_object_object_add(json, "ext-ip-reach", ext_json);
json_object_string_add(
json, "mt-id",
(mtid == ISIS_MT_IPV4_UNICAST) ? "Extended" : "MT");
json_object_string_add(
json, "ip-reach",
prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf)));
json_object_int_add(json, "ip-reach-metric", r->metric);
json_object_string_add(json, "down", r->down ? "yes" : "");
if (mtid != ISIS_MT_IPV4_UNICAST)
json_object_string_add(json, "mt-name",
isis_mtid2str(mtid));
if (r->subtlvs) {
struct json_object *subtlv_json;
subtlv_json = json_object_new_object();
json_object_object_add(json, "subtlvs", subtlv_json);
format_subtlvs(r->subtlvs, NULL, subtlv_json, 0);
}
} else {
sbuf_push(buf, indent, "%s IP Reachability: %s (Metric: %u)%s",
(mtid == ISIS_MT_IPV4_UNICAST) ? "Extended" : "MT",
prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf)),
r->metric, r->down ? " Down" : "");
if (mtid != ISIS_MT_IPV4_UNICAST)
sbuf_push(buf, 0, " %s", isis_mtid2str(mtid));
sbuf_push(buf, 0, "\n");
if (r->subtlvs) {
sbuf_push(buf, indent, " Subtlvs:\n");
format_subtlvs(r->subtlvs, buf, NULL, indent + 4);
}
}
}
static void free_item_extended_ip_reach(struct isis_item *i)
{
struct isis_extended_ip_reach *item =
(struct isis_extended_ip_reach *)i;
isis_free_subtlvs(item->subtlvs);
XFREE(MTYPE_ISIS_TLV, item);
}
static int pack_item_extended_ip_reach(struct isis_item *i, struct stream *s,
size_t *min_len)
{
struct isis_extended_ip_reach *r = (struct isis_extended_ip_reach *)i;
uint8_t control;
if (STREAM_WRITEABLE(s) < 5) {
*min_len = 5;
return 1;
}
stream_putl(s, r->metric);
control = r->down ? ISIS_EXTENDED_IP_REACH_DOWN : 0;
control |= r->prefix.prefixlen;
control |= r->subtlvs ? ISIS_EXTENDED_IP_REACH_SUBTLV : 0;
stream_putc(s, control);
if (STREAM_WRITEABLE(s) < (unsigned)PSIZE(r->prefix.prefixlen)) {
*min_len = 5 + (unsigned)PSIZE(r->prefix.prefixlen);
return 1;
}
stream_put(s, &r->prefix.prefix.s_addr, PSIZE(r->prefix.prefixlen));
if (r->subtlvs)
return pack_subtlvs(r->subtlvs, s);
return 0;
}
static int unpack_item_extended_ip_reach(uint16_t mtid, uint8_t len,
struct stream *s, struct sbuf *log,
void *dest, int indent)
{
struct isis_tlvs *tlvs = dest;
struct isis_extended_ip_reach *rv = NULL;
size_t consume;
uint8_t control, subtlv_len;
struct isis_item_list *items;
if (mtid == ISIS_MT_IPV4_UNICAST) {
items = &tlvs->extended_ip_reach;
} else {
items = isis_get_mt_items(&tlvs->mt_ip_reach, mtid);
}
sbuf_push(log, indent, "Unpacking %s IPv4 reachability...\n",
(mtid == ISIS_MT_IPV4_UNICAST) ? "extended" : "mt");
consume = 5;
if (len < consume) {
sbuf_push(log, indent,
"Not enough data left. (expected 5 or more bytes, got %hhu)\n",
len);
goto out;
}
rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
rv->metric = stream_getl(s);
control = stream_getc(s);
rv->down = (control & ISIS_EXTENDED_IP_REACH_DOWN);
rv->prefix.family = AF_INET;
rv->prefix.prefixlen = control & 0x3f;
if (rv->prefix.prefixlen > IPV4_MAX_BITLEN) {
sbuf_push(log, indent, "Prefixlen %u is implausible for IPv4\n",
rv->prefix.prefixlen);
goto out;
}
consume += PSIZE(rv->prefix.prefixlen);
if (len < consume) {
sbuf_push(log, indent,
"Expected %u bytes of prefix, but only %u bytes available.\n",
PSIZE(rv->prefix.prefixlen), len - 5);
goto out;
}
stream_get(&rv->prefix.prefix.s_addr, s, PSIZE(rv->prefix.prefixlen));
in_addr_t orig_prefix = rv->prefix.prefix.s_addr;
apply_mask_ipv4(&rv->prefix);
if (orig_prefix != rv->prefix.prefix.s_addr)
sbuf_push(log, indent + 2,
"WARNING: Prefix had hostbits set.\n");
format_item_extended_ip_reach(mtid, (struct isis_item *)rv, log, NULL,
indent + 2);
if (control & ISIS_EXTENDED_IP_REACH_SUBTLV) {
consume += 1;
if (len < consume) {
sbuf_push(log, indent,
"Expected 1 byte of subtlv len, but no more data present.\n");
goto out;
}
subtlv_len = stream_getc(s);
if (!subtlv_len) {
sbuf_push(log, indent + 2,
" WARNING: subtlv bit is set, but there are no subtlvs.\n");
}
consume += subtlv_len;
if (len < consume) {
sbuf_push(log, indent,
"Expected %hhu bytes of subtlvs, but only %u bytes available.\n",
subtlv_len,
len - 6 - PSIZE(rv->prefix.prefixlen));
goto out;
}
rv->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IP_REACH);
bool unpacked_known_tlvs = false;
if (unpack_tlvs(ISIS_CONTEXT_SUBTLV_IP_REACH, subtlv_len, s,
log, rv->subtlvs, indent + 4, &unpacked_known_tlvs)) {
goto out;
}
if (!unpacked_known_tlvs) {
isis_free_subtlvs(rv->subtlvs);
rv->subtlvs = NULL;
}
}
append_item(items, (struct isis_item *)rv);
return 0;
out:
if (rv)
free_item_extended_ip_reach((struct isis_item *)rv);
return 1;
}
/* Functions related to TLV 137 Dynamic Hostname */
static char *copy_tlv_dynamic_hostname(const char *hostname)
{
if (!hostname)
return NULL;
return XSTRDUP(MTYPE_ISIS_TLV, hostname);
}
static void format_tlv_dynamic_hostname(const char *hostname, struct sbuf *buf,
struct json_object *json, int indent)
{
if (!hostname)
return;
if (json)
json_object_string_add(json, "hostname", hostname);
else
sbuf_push(buf, indent, "Hostname: %s\n", hostname);
}
static void free_tlv_dynamic_hostname(char *hostname)
{
XFREE(MTYPE_ISIS_TLV, hostname);
}
static int pack_tlv_dynamic_hostname(const char *hostname, struct stream *s)
{
if (!hostname)
return 0;
uint8_t name_len = strlen(hostname);
if (STREAM_WRITEABLE(s) < (unsigned)(2 + name_len))
return 1;
stream_putc(s, ISIS_TLV_DYNAMIC_HOSTNAME);
stream_putc(s, name_len);
stream_put(s, hostname, name_len);
return 0;
}
static int unpack_tlv_dynamic_hostname(enum isis_tlv_context context,
uint8_t tlv_type, uint8_t tlv_len,
struct stream *s, struct sbuf *log,
void *dest, int indent)
{
struct isis_tlvs *tlvs = dest;
sbuf_push(log, indent, "Unpacking Dynamic Hostname TLV...\n");
if (!tlv_len) {
sbuf_push(log, indent, "WARNING: No hostname included\n");
return 0;
}
if (tlvs->hostname) {
sbuf_push(log, indent,
"WARNING: Hostname present multiple times.\n");
stream_forward_getp(s, tlv_len);
return 0;
}
tlvs->hostname = XCALLOC(MTYPE_ISIS_TLV, tlv_len + 1);
stream_get(tlvs->hostname, s, tlv_len);
tlvs->hostname[tlv_len] = '\0';
bool sane = true;
for (uint8_t i = 0; i < tlv_len; i++) {
if ((unsigned char)tlvs->hostname[i] > 127
|| !isprint((unsigned char)tlvs->hostname[i])) {
sane = false;
tlvs->hostname[i] = '?';
}
}
if (!sane) {
sbuf_push(
log, indent,
"WARNING: Hostname contained non-printable/non-ascii characters.\n");
}
return 0;
}
/* Functions related to TLV 140 IPv6 TE Router ID */
static struct in6_addr *copy_tlv_te_router_id_ipv6(const struct in6_addr *id)
{
if (!id)
return NULL;
struct in6_addr *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
memcpy(rv, id, sizeof(*rv));
return rv;
}
static void format_tlv_te_router_id_ipv6(const struct in6_addr *id,
struct sbuf *buf,
struct json_object *json, int indent)
{
if (!id)
return;
char addrbuf[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, id, addrbuf, sizeof(addrbuf));
if (json)
json_object_string_add(json, "ipv6-te-router-id", addrbuf);
else
sbuf_push(buf, indent, "IPv6 TE Router ID: %s\n", addrbuf);
}
static void free_tlv_te_router_id_ipv6(struct in6_addr *id)
{
XFREE(MTYPE_ISIS_TLV, id);
}
static int pack_tlv_te_router_id_ipv6(const struct in6_addr *id,
struct stream *s)
{
if (!id)
return 0;
if (STREAM_WRITEABLE(s) < (unsigned)(2 + sizeof(*id)))
return 1;
stream_putc(s, ISIS_TLV_TE_ROUTER_ID_IPV6);
stream_putc(s, IPV6_MAX_BYTELEN);
stream_put(s, id, IPV6_MAX_BYTELEN);
return 0;
}
static int unpack_tlv_te_router_id_ipv6(enum isis_tlv_context context,
uint8_t tlv_type, uint8_t tlv_len,
struct stream *s, struct sbuf *log,
void *dest, int indent)
{
struct isis_tlvs *tlvs = dest;
sbuf_push(log, indent, "Unpacking IPv6 TE Router ID TLV...\n");
if (tlv_len != IPV6_MAX_BYTELEN) {
sbuf_push(log, indent, "WARNING: Length invalid\n");
return 1;
}
if (tlvs->te_router_id_ipv6) {
sbuf_push(
log, indent,
"WARNING: IPv6 TE Router ID present multiple times.\n");
stream_forward_getp(s, tlv_len);
return 0;
}
tlvs->te_router_id_ipv6 = XCALLOC(MTYPE_ISIS_TLV, IPV6_MAX_BYTELEN);
stream_get(tlvs->te_router_id_ipv6, s, IPV6_MAX_BYTELEN);
format_tlv_te_router_id_ipv6(tlvs->te_router_id_ipv6, log, NULL, indent + 2);
return 0;
}
/* Functions related to TLV 150 Spine-Leaf-Extension */
static struct isis_spine_leaf *copy_tlv_spine_leaf(
const struct isis_spine_leaf *spine_leaf)
{
if (!spine_leaf)
return NULL;
struct isis_spine_leaf *rv = XMALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
memcpy(rv, spine_leaf, sizeof(*rv));
return rv;
}
static void format_tlv_spine_leaf(const struct isis_spine_leaf *spine_leaf,
struct sbuf *buf, struct json_object *json,
int indent)
{
if (!spine_leaf)
return;
char aux_buf[255];
if (json) {
struct json_object *spine_json;
spine_json = json_object_new_object();
json_object_object_add(json, "spine-leaf-extension",
spine_json);
if (spine_leaf->has_tier) {
snprintfrr(aux_buf, sizeof(aux_buf), "%hhu",
spine_leaf->tier);
json_object_string_add(
spine_json, "tier",
(spine_leaf->tier == ISIS_TIER_UNDEFINED)
? "undefined"
: aux_buf);
}
json_object_string_add(spine_json, "flag-leaf",
spine_leaf->is_leaf ? "yes" : "");
json_object_string_add(spine_json, "flag-spine",
spine_leaf->is_spine ? "yes" : "");
json_object_string_add(spine_json, "flag-backup",
spine_leaf->is_backup ? "yes" : "");
} else {
sbuf_push(buf, indent, "Spine-Leaf-Extension:\n");
if (spine_leaf->has_tier) {
if (spine_leaf->tier == ISIS_TIER_UNDEFINED) {
sbuf_push(buf, indent, " Tier: undefined\n");
} else {
sbuf_push(buf, indent, " Tier: %hhu\n",
spine_leaf->tier);
}
}
sbuf_push(buf, indent, " Flags:%s%s%s\n",
spine_leaf->is_leaf ? " LEAF" : "",
spine_leaf->is_spine ? " SPINE" : "",
spine_leaf->is_backup ? " BACKUP" : "");
}
}
static void free_tlv_spine_leaf(struct isis_spine_leaf *spine_leaf)
{
XFREE(MTYPE_ISIS_TLV, spine_leaf);
}
#define ISIS_SPINE_LEAF_FLAG_TIER 0x08
#define ISIS_SPINE_LEAF_FLAG_BACKUP 0x04
#define ISIS_SPINE_LEAF_FLAG_SPINE 0x02
#define ISIS_SPINE_LEAF_FLAG_LEAF 0x01
static int pack_tlv_spine_leaf(const struct isis_spine_leaf *spine_leaf,
struct stream *s)
{
if (!spine_leaf)
return 0;
uint8_t tlv_len = 2;
if (STREAM_WRITEABLE(s) < (unsigned)(2 + tlv_len))
return 1;
stream_putc(s, ISIS_TLV_SPINE_LEAF_EXT);
stream_putc(s, tlv_len);
uint16_t spine_leaf_flags = 0;
if (spine_leaf->has_tier) {
spine_leaf_flags |= ISIS_SPINE_LEAF_FLAG_TIER;
spine_leaf_flags |= spine_leaf->tier << 12;
}
if (spine_leaf->is_leaf)
spine_leaf_flags |= ISIS_SPINE_LEAF_FLAG_LEAF;
if (spine_leaf->is_spine)
spine_leaf_flags |= ISIS_SPINE_LEAF_FLAG_SPINE;
if (spine_leaf->is_backup)
spine_leaf_flags |= ISIS_SPINE_LEAF_FLAG_BACKUP;
stream_putw(s, spine_leaf_flags);
return 0;
}
static int unpack_tlv_spine_leaf(enum isis_tlv_context context,
uint8_t tlv_type, uint8_t tlv_len,
struct stream *s, struct sbuf *log,
void *dest, int indent)
{
struct isis_tlvs *tlvs = dest;
sbuf_push(log, indent, "Unpacking Spine Leaf Extension TLV...\n");
if (tlv_len < 2) {
sbuf_push(log, indent, "WARNING: Unexpected TLV size\n");
stream_forward_getp(s, tlv_len);
return 0;
}
if (tlvs->spine_leaf) {
sbuf_push(log, indent,
"WARNING: Spine Leaf Extension TLV present multiple times.\n");
stream_forward_getp(s, tlv_len);
return 0;
}
tlvs->spine_leaf = XCALLOC(MTYPE_ISIS_TLV, sizeof(*tlvs->spine_leaf));
uint16_t spine_leaf_flags = stream_getw(s);
if (spine_leaf_flags & ISIS_SPINE_LEAF_FLAG_TIER) {
tlvs->spine_leaf->has_tier = true;
tlvs->spine_leaf->tier = spine_leaf_flags >> 12;
}
tlvs->spine_leaf->is_leaf = spine_leaf_flags & ISIS_SPINE_LEAF_FLAG_LEAF;
tlvs->spine_leaf->is_spine = spine_leaf_flags & ISIS_SPINE_LEAF_FLAG_SPINE;
tlvs->spine_leaf->is_backup = spine_leaf_flags & ISIS_SPINE_LEAF_FLAG_BACKUP;
stream_forward_getp(s, tlv_len - 2);
return 0;
}
/* Functions related to TLV 240 P2P Three-Way Adjacency */
const char *isis_threeway_state_name(enum isis_threeway_state state)
{
switch (state) {
case ISIS_THREEWAY_DOWN:
return "Down";
case ISIS_THREEWAY_INITIALIZING:
return "Initializing";
case ISIS_THREEWAY_UP:
return "Up";
default:
return "Invalid!";
}
}
static struct isis_threeway_adj *copy_tlv_threeway_adj(
const struct isis_threeway_adj *threeway_adj)
{
if (!threeway_adj)
return NULL;
struct isis_threeway_adj *rv = XMALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
memcpy(rv, threeway_adj, sizeof(*rv));
return rv;
}
static void
format_tlv_threeway_adj(const struct isis_threeway_adj *threeway_adj,
struct sbuf *buf, struct json_object *json, int indent)
{
char sys_id[ISO_SYSID_STRLEN];
if (!threeway_adj)
return;
snprintfrr(sys_id, ISO_SYSID_STRLEN, "%pSY", threeway_adj->neighbor_id);
if (json) {
struct json_object *three_json;
three_json = json_object_new_object();
json_object_object_add(json, "p2p-three-way-adj", three_json);
json_object_string_add(
three_json, "state-name",
isis_threeway_state_name(threeway_adj->state));
json_object_int_add(three_json, "state", threeway_adj->state);
json_object_int_add(three_json, "ext-local-circuit-id",
threeway_adj->local_circuit_id);
if (!threeway_adj->neighbor_set)
return;
json_object_string_add(three_json, "neigh-system-id", sys_id);
json_object_int_add(three_json, "neigh-ext-circuit-id",
threeway_adj->neighbor_circuit_id);
} else {
sbuf_push(buf, indent, "P2P Three-Way Adjacency:\n");
sbuf_push(buf, indent, " State: %s (%d)\n",
isis_threeway_state_name(threeway_adj->state),
threeway_adj->state);
sbuf_push(buf, indent, " Extended Local Circuit ID: %u\n",
threeway_adj->local_circuit_id);
if (!threeway_adj->neighbor_set)
return;
sbuf_push(buf, indent, " Neighbor System ID: %s\n", sys_id);
sbuf_push(buf, indent, " Neighbor Extended Circuit ID: %u\n",
threeway_adj->neighbor_circuit_id);
}
}
static void free_tlv_threeway_adj(struct isis_threeway_adj *threeway_adj)
{
XFREE(MTYPE_ISIS_TLV, threeway_adj);
}
static int pack_tlv_threeway_adj(const struct isis_threeway_adj *threeway_adj,
struct stream *s)
{
if (!threeway_adj)
return 0;
uint8_t tlv_len = (threeway_adj->neighbor_set) ? 15 : 5;
if (STREAM_WRITEABLE(s) < (unsigned)(2 + tlv_len))
return 1;
stream_putc(s, ISIS_TLV_THREE_WAY_ADJ);
stream_putc(s, tlv_len);
stream_putc(s, threeway_adj->state);
stream_putl(s, threeway_adj->local_circuit_id);
if (threeway_adj->neighbor_set) {
stream_put(s, threeway_adj->neighbor_id, 6);
stream_putl(s, threeway_adj->neighbor_circuit_id);
}
return 0;
}
static int unpack_tlv_threeway_adj(enum isis_tlv_context context,
uint8_t tlv_type, uint8_t tlv_len,
struct stream *s, struct sbuf *log,
void *dest, int indent)
{
struct isis_tlvs *tlvs = dest;
sbuf_push(log, indent, "Unpacking P2P Three-Way Adjacency TLV...\n");
if (tlv_len != 5 && tlv_len != 15) {
sbuf_push(log, indent, "WARNING: Unexpected TLV size\n");
stream_forward_getp(s, tlv_len);
return 0;
}
if (tlvs->threeway_adj) {
sbuf_push(log, indent,
"WARNING: P2P Three-Way Adjacency TLV present multiple times.\n");
stream_forward_getp(s, tlv_len);
return 0;
}
tlvs->threeway_adj = XCALLOC(MTYPE_ISIS_TLV, sizeof(*tlvs->threeway_adj));
tlvs->threeway_adj->state = stream_getc(s);
tlvs->threeway_adj->local_circuit_id = stream_getl(s);
if (tlv_len == 15) {
tlvs->threeway_adj->neighbor_set = true;
stream_get(tlvs->threeway_adj->neighbor_id, s, 6);
tlvs->threeway_adj->neighbor_circuit_id = stream_getl(s);
}
return 0;
}
/* Functions related to TLVs 236/237 IPv6/MT-IPv6 reach */
static struct isis_item *copy_item_ipv6_reach(struct isis_item *i)
{
struct isis_ipv6_reach *r = (struct isis_ipv6_reach *)i;
struct isis_ipv6_reach *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
rv->metric = r->metric;
rv->down = r->down;
rv->external = r->external;
rv->prefix = r->prefix;
rv->subtlvs = copy_subtlvs(r->subtlvs);
return (struct isis_item *)rv;
}
static void format_item_ipv6_reach(uint16_t mtid, struct isis_item *i,
struct sbuf *buf, struct json_object *json,
int indent)
{
struct isis_ipv6_reach *r = (struct isis_ipv6_reach *)i;
char prefixbuf[PREFIX2STR_BUFFER];
if (json) {
struct json_object *reach_json;
reach_json = json_object_new_object();
json_object_object_add(json, "ipv6-reach", reach_json);
json_object_string_add(reach_json, "mt-id",
(mtid == ISIS_MT_IPV4_UNICAST) ? ""
: "mt");
json_object_string_add(
reach_json, "prefix",
prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf)));
json_object_int_add(reach_json, "metric", r->metric);
json_object_string_add(reach_json, "down",
r->down ? "yes" : "");
json_object_string_add(reach_json, "external",
r->external ? "yes" : "");
if (mtid != ISIS_MT_IPV4_UNICAST)
json_object_string_add(reach_json, "mt-name",
isis_mtid2str(mtid));
if (r->subtlvs) {
struct json_object *subtlvs_json;
subtlvs_json = json_object_new_object();
json_object_object_add(json, "subtlvs", subtlvs_json);
format_subtlvs(r->subtlvs, NULL, subtlvs_json, 0);
}
} else {
sbuf_push(buf, indent,
"%sIPv6 Reachability: %s (Metric: %u)%s%s",
(mtid == ISIS_MT_IPV4_UNICAST) ? "" : "MT ",
prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf)),
r->metric, r->down ? " Down" : "",
r->external ? " External" : "");
if (mtid != ISIS_MT_IPV4_UNICAST)
sbuf_push(buf, 0, " %s", isis_mtid2str(mtid));
sbuf_push(buf, 0, "\n");
if (r->subtlvs) {
sbuf_push(buf, indent, " Subtlvs:\n");
format_subtlvs(r->subtlvs, buf, NULL, indent + 4);
}
}
}
static void free_item_ipv6_reach(struct isis_item *i)
{
struct isis_ipv6_reach *item = (struct isis_ipv6_reach *)i;
isis_free_subtlvs(item->subtlvs);
XFREE(MTYPE_ISIS_TLV, item);
}
static int pack_item_ipv6_reach(struct isis_item *i, struct stream *s,
size_t *min_len)
{
struct isis_ipv6_reach *r = (struct isis_ipv6_reach *)i;
uint8_t control;
if (STREAM_WRITEABLE(s) < 6 + (unsigned)PSIZE(r->prefix.prefixlen)) {
*min_len = 6 + (unsigned)PSIZE(r->prefix.prefixlen);
return 1;
}
stream_putl(s, r->metric);
control = r->down ? ISIS_IPV6_REACH_DOWN : 0;
control |= r->external ? ISIS_IPV6_REACH_EXTERNAL : 0;
control |= r->subtlvs ? ISIS_IPV6_REACH_SUBTLV : 0;
stream_putc(s, control);
stream_putc(s, r->prefix.prefixlen);
stream_put(s, &r->prefix.prefix.s6_addr, PSIZE(r->prefix.prefixlen));
if (r->subtlvs)
return pack_subtlvs(r->subtlvs, s);
return 0;
}
static int unpack_item_ipv6_reach(uint16_t mtid, uint8_t len, struct stream *s,
struct sbuf *log, void *dest, int indent)
{
struct isis_tlvs *tlvs = dest;
struct isis_ipv6_reach *rv = NULL;
size_t consume;
uint8_t control, subtlv_len;
struct isis_item_list *items;
if (mtid == ISIS_MT_IPV4_UNICAST) {
items = &tlvs->ipv6_reach;
} else {
items = isis_get_mt_items(&tlvs->mt_ipv6_reach, mtid);
}
sbuf_push(log, indent, "Unpacking %sIPv6 reachability...\n",
(mtid == ISIS_MT_IPV4_UNICAST) ? "" : "mt ");
consume = 6;
if (len < consume) {
sbuf_push(log, indent,
"Not enough data left. (expected 6 or more bytes, got %hhu)\n",
len);
goto out;
}
rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
rv->metric = stream_getl(s);
control = stream_getc(s);
rv->down = (control & ISIS_IPV6_REACH_DOWN);
rv->external = (control & ISIS_IPV6_REACH_EXTERNAL);
rv->prefix.family = AF_INET6;
rv->prefix.prefixlen = stream_getc(s);
if (rv->prefix.prefixlen > IPV6_MAX_BITLEN) {
sbuf_push(log, indent, "Prefixlen %u is implausible for IPv6\n",
rv->prefix.prefixlen);
goto out;
}
consume += PSIZE(rv->prefix.prefixlen);
if (len < consume) {
sbuf_push(log, indent,
"Expected %u bytes of prefix, but only %u bytes available.\n",
PSIZE(rv->prefix.prefixlen), len - 6);
goto out;
}
stream_get(&rv->prefix.prefix.s6_addr, s, PSIZE(rv->prefix.prefixlen));
struct in6_addr orig_prefix = rv->prefix.prefix;
apply_mask_ipv6(&rv->prefix);
if (memcmp(&orig_prefix, &rv->prefix.prefix, sizeof(orig_prefix)))
sbuf_push(log, indent + 2,
"WARNING: Prefix had hostbits set.\n");
format_item_ipv6_reach(mtid, (struct isis_item *)rv, log, NULL, indent + 2);
if (control & ISIS_IPV6_REACH_SUBTLV) {
consume += 1;
if (len < consume) {
sbuf_push(log, indent,
"Expected 1 byte of subtlv len, but no more data persent.\n");
goto out;
}
subtlv_len = stream_getc(s);
if (!subtlv_len) {
sbuf_push(log, indent + 2,
" WARNING: subtlv bit set, but there are no subtlvs.\n");
}
consume += subtlv_len;
if (len < consume) {
sbuf_push(log, indent,
"Expected %hhu bytes of subtlvs, but only %u bytes available.\n",
subtlv_len,
len - 6 - PSIZE(rv->prefix.prefixlen));
goto out;
}
rv->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IPV6_REACH);
bool unpacked_known_tlvs = false;
if (unpack_tlvs(ISIS_CONTEXT_SUBTLV_IPV6_REACH, subtlv_len, s,
log, rv->subtlvs, indent + 4, &unpacked_known_tlvs)) {
goto out;
}
if (!unpacked_known_tlvs) {
isis_free_subtlvs(rv->subtlvs);
rv->subtlvs = NULL;
}
}
append_item(items, (struct isis_item *)rv);
return 0;
out:
if (rv)
free_item_ipv6_reach((struct isis_item *)rv);
return 1;
}
/* Functions related to TLV 242 Router Capability as per RFC7981 */
static struct isis_router_cap *copy_tlv_router_cap(
const struct isis_router_cap *router_cap)
{
struct isis_router_cap *rv;
if (!router_cap)
return NULL;
rv = XMALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
memcpy(rv, router_cap, sizeof(*rv));
#ifndef FABRICD
for (int i = 0; i < SR_ALGORITHM_COUNT; i++) {
struct isis_router_cap_fad *sc_fad;
struct isis_router_cap_fad *rv_fad;
sc_fad = router_cap->fads[i];
if (!sc_fad)
continue;
rv_fad = XMALLOC(MTYPE_ISIS_TLV,
sizeof(struct isis_router_cap_fad));
*rv_fad = *sc_fad;
rv_fad->fad.admin_group_exclude_any.bitmap.data = NULL;
rv_fad->fad.admin_group_include_any.bitmap.data = NULL;
rv_fad->fad.admin_group_include_all.bitmap.data = NULL;
assert(bf_is_inited(
sc_fad->fad.admin_group_exclude_any.bitmap));
assert(bf_is_inited(
sc_fad->fad.admin_group_include_any.bitmap));
assert(bf_is_inited(
sc_fad->fad.admin_group_include_all.bitmap));
admin_group_copy(&rv_fad->fad.admin_group_exclude_any,
&sc_fad->fad.admin_group_exclude_any);
admin_group_copy(&rv_fad->fad.admin_group_include_any,
&sc_fad->fad.admin_group_include_any);
admin_group_copy(&rv_fad->fad.admin_group_include_all,
&sc_fad->fad.admin_group_include_all);
rv->fads[i] = rv_fad;
}
#endif /* ifndef FABRICD */
return rv;
}
static void format_tlv_router_cap_json(const struct isis_router_cap *router_cap,
struct json_object *json)
{
char addrbuf[INET_ADDRSTRLEN];
if (!router_cap)
return;
/* Router ID and Flags */
struct json_object *cap_json;
cap_json = json_object_new_object();
json_object_object_add(json, "router-capability", cap_json);
inet_ntop(AF_INET, &router_cap->router_id, addrbuf, sizeof(addrbuf));
json_object_string_add(cap_json, "id", addrbuf);
json_object_string_add(
cap_json, "flag-d",
router_cap->flags & ISIS_ROUTER_CAP_FLAG_D ? "1" : "0");
json_object_string_add(
cap_json, "flag-s",
router_cap->flags & ISIS_ROUTER_CAP_FLAG_S ? "1" : "0");
/* Segment Routing Global Block as per RFC8667 section #3.1 */
if (router_cap->srgb.range_size != 0) {
struct json_object *gb_json;
gb_json = json_object_new_object();
json_object_object_add(json, "segment-routing-gb", gb_json);
json_object_string_add(gb_json, "ipv4",
IS_SR_IPV4(&router_cap->srgb) ? "1"
: "0");
json_object_string_add(gb_json, "ipv6",
IS_SR_IPV6(&router_cap->srgb) ? "1"
: "0");
json_object_int_add(gb_json, "global-block-base",
router_cap->srgb.lower_bound);
json_object_int_add(gb_json, "global-block-range",
router_cap->srgb.range_size);
}
/* Segment Routing Local Block as per RFC8667 section #3.3 */
if (router_cap->srlb.range_size != 0) {
struct json_object *lb_json;
lb_json = json_object_new_object();
json_object_object_add(json, "segment-routing-lb", lb_json);
json_object_int_add(lb_json, "global-block-base",
router_cap->srlb.lower_bound);
json_object_int_add(lb_json, "global-block-range",
router_cap->srlb.range_size);
}
/* Segment Routing Algorithms as per RFC8667 section #3.2 */
if (router_cap->algo[0] != SR_ALGORITHM_UNSET) {
char buf[255];
struct json_object *alg_json;
alg_json = json_object_new_object();
json_object_object_add(json, "segment-routing-algorithm",
alg_json);
for (int i = 0; i < SR_ALGORITHM_COUNT; i++)
if (router_cap->algo[i] != SR_ALGORITHM_UNSET) {
snprintfrr(buf, sizeof(buf), "%d", i);
json_object_string_add(alg_json, buf,
router_cap->algo[i] == 0
? "SPF"
: "Strict SPF");
}
}
/* Segment Routing Node MSD as per RFC8491 section #2 */
if (router_cap->msd != 0)
json_object_int_add(json, "msd", router_cap->msd);
}
static void format_tlv_router_cap(const struct isis_router_cap *router_cap,
struct sbuf *buf, int indent)
{
char addrbuf[INET_ADDRSTRLEN];
if (!router_cap)
return;
/* Router ID and Flags */
inet_ntop(AF_INET, &router_cap->router_id, addrbuf, sizeof(addrbuf));
sbuf_push(buf, indent, "Router Capability:");
sbuf_push(buf, indent, " %s , D:%c, S:%c\n", addrbuf,
router_cap->flags & ISIS_ROUTER_CAP_FLAG_D ? '1' : '0',
router_cap->flags & ISIS_ROUTER_CAP_FLAG_S ? '1' : '0');
/* Segment Routing Global Block as per RFC8667 section #3.1 */
if (router_cap->srgb.range_size != 0)
sbuf_push(
buf, indent,
" Segment Routing: I:%s V:%s, Global Block Base: %u Range: %u\n",
IS_SR_IPV4(&router_cap->srgb) ? "1" : "0",
IS_SR_IPV6(&router_cap->srgb) ? "1" : "0",
router_cap->srgb.lower_bound,
router_cap->srgb.range_size);
/* Segment Routing Local Block as per RFC8667 section #3.3 */
if (router_cap->srlb.range_size != 0)
sbuf_push(buf, indent, " SR Local Block Base: %u Range: %u\n",
router_cap->srlb.lower_bound,
router_cap->srlb.range_size);
/* Segment Routing Algorithms as per RFC8667 section #3.2 */
if (router_cap->algo[0] != SR_ALGORITHM_UNSET) {
sbuf_push(buf, indent, " SR Algorithm:\n");
for (int i = 0; i < SR_ALGORITHM_COUNT; i++)
if (router_cap->algo[i] != SR_ALGORITHM_UNSET)
sbuf_push(buf, indent, " %u: %s\n", i,
sr_algorithm_string(
router_cap->algo[i]));
}
/* Segment Routing Node MSD as per RFC8491 section #2 */
if (router_cap->msd != 0)
sbuf_push(buf, indent, " Node Maximum SID Depth: %u\n",
router_cap->msd);
#ifndef FABRICD
/* Flex-Algo */
for (int i = 0; i < SR_ALGORITHM_COUNT; i++) {
char admin_group_buf[ADMIN_GROUP_PRINT_MAX_SIZE];
int indent2;
struct admin_group *admin_group;
struct isis_router_cap_fad *fad;
fad = router_cap->fads[i];
if (!fad)
continue;
sbuf_push(buf, indent, " Flex-Algo Definition: %d\n",
fad->fad.algorithm);
sbuf_push(buf, indent, " Metric-Type: %d\n",
fad->fad.metric_type);
sbuf_push(buf, indent, " Calc-Type: %d\n",
fad->fad.calc_type);
sbuf_push(buf, indent, " Priority: %d\n", fad->fad.priority);
indent2 = indent + strlen(" Exclude-Any: ");
admin_group = &fad->fad.admin_group_exclude_any;
sbuf_push(buf, indent, " Exclude-Any: ");
sbuf_push(buf, 0, "%s\n",
admin_group_string(admin_group_buf,
ADMIN_GROUP_PRINT_MAX_SIZE,
indent2, admin_group));
indent2 = indent + strlen(" Include-Any: ");
admin_group = &fad->fad.admin_group_include_any;
sbuf_push(buf, indent, " Include-Any: ");
sbuf_push(buf, 0, "%s\n",
admin_group_string(admin_group_buf,
ADMIN_GROUP_PRINT_MAX_SIZE,
indent2, admin_group));
indent2 = indent + strlen(" Include-All: ");
admin_group = &fad->fad.admin_group_include_all;
sbuf_push(buf, indent, " Include-All: ");
sbuf_push(buf, 0, "%s\n",
admin_group_string(admin_group_buf,
ADMIN_GROUP_PRINT_MAX_SIZE,
indent2, admin_group));
sbuf_push(buf, indent, " M-Flag: %c\n",
CHECK_FLAG(fad->fad.flags, FAD_FLAG_M) ? '1' : '0');
if (fad->fad.flags != 0 && fad->fad.flags != FAD_FLAG_M)
sbuf_push(buf, indent, " Flags: 0x%x\n",
fad->fad.flags);
if (fad->fad.exclude_srlg)
sbuf_push(buf, indent, " Exclude SRLG: Enabled\n");
if (fad->fad.unsupported_subtlv)
sbuf_push(buf, indent,
" Got an unsupported sub-TLV: Yes\n");
}
#endif /* ifndef FABRICD */
/* SRv6 Flags as per RFC 9352 section #2 */
if (router_cap->srv6_cap.is_srv6_capable)
sbuf_push(buf, indent, " SRv6: O:%s\n",
SUPPORTS_SRV6_OAM(&router_cap->srv6_cap) ? "1" : "0");
}
static void free_tlv_router_cap(struct isis_router_cap *router_cap)
{
if (!router_cap)
return;
#ifndef FABRICD
for (int i = 0; i < SR_ALGORITHM_COUNT; i++) {
struct isis_router_cap_fad *fad;
fad = router_cap->fads[i];
if (!fad)
continue;
admin_group_term(&fad->fad.admin_group_exclude_any);
admin_group_term(&fad->fad.admin_group_include_any);
admin_group_term(&fad->fad.admin_group_include_all);
XFREE(MTYPE_ISIS_TLV, fad);
}
#endif /* ifndef FABRICD */
XFREE(MTYPE_ISIS_TLV, router_cap);
}
#ifndef FABRICD
static size_t
isis_router_cap_fad_sub_tlv_len(const struct isis_router_cap_fad *fad)
{
size_t sz = ISIS_SUBTLV_FAD_MIN_SIZE;
uint32_t admin_group_length;
admin_group_length =
admin_group_nb_words(&fad->fad.admin_group_exclude_any);
if (admin_group_length)
sz += sizeof(uint32_t) * admin_group_length + 2;
admin_group_length =
admin_group_nb_words(&fad->fad.admin_group_include_any);
if (admin_group_length)
sz += sizeof(uint32_t) * admin_group_length + 2;
admin_group_length =
admin_group_nb_words(&fad->fad.admin_group_include_all);
if (admin_group_length)
sz += sizeof(uint32_t) * admin_group_length + 2;
if (fad->fad.flags != 0)
sz += ISIS_SUBTLV_FAD_SUBSUBTLV_FLAGS_SIZE + 2;
/* TODO: add exclude SRLG sub-sub-TLV length when supported */
return sz;
}
#endif /* ifndef FABRICD */
static size_t isis_router_cap_tlv_size(const struct isis_router_cap *router_cap)
{
size_t sz = 2 + ISIS_ROUTER_CAP_SIZE;
#ifndef FABRICD
size_t fad_sz;
#endif /* ifndef FABRICD */
int nb_algo, nb_msd;
if ((router_cap->srgb.range_size != 0) &&
(router_cap->srgb.lower_bound != 0)) {
sz += 2 + ISIS_SUBTLV_SID_LABEL_RANGE_SIZE;
sz += 2 + ISIS_SUBTLV_SID_LABEL_SIZE;
nb_algo = isis_tlvs_sr_algo_count(router_cap);
if (nb_algo != 0)
sz += 2 + nb_algo;
if ((router_cap->srlb.range_size != 0) &&
(router_cap->srlb.lower_bound != 0)) {
sz += 2 + ISIS_SUBTLV_SID_LABEL_RANGE_SIZE;
sz += 2 + ISIS_SUBTLV_SID_LABEL_SIZE;
}
if (router_cap->msd != 0)
sz += 2 + ISIS_SUBTLV_NODE_MSD_SIZE;
}
#ifndef FABRICD
for (int i = 0; i < SR_ALGORITHM_COUNT; i++) {
if (!router_cap->fads[i])
continue;
fad_sz = 2 +
isis_router_cap_fad_sub_tlv_len(router_cap->fads[i]);
if (((sz + fad_sz) % 256) < (sz % 256))
sz += 2 + ISIS_ROUTER_CAP_SIZE + fad_sz;
else
sz += fad_sz;
}
#endif /* ifndef FABRICD */
if (router_cap->srv6_cap.is_srv6_capable) {
sz += ISIS_SUBTLV_TYPE_FIELD_SIZE +
ISIS_SUBTLV_LENGTH_FIELD_SIZE +
ISIS_SUBTLV_SRV6_CAPABILITIES_SIZE;
nb_algo = isis_tlvs_sr_algo_count(router_cap);
if (nb_algo != 0)
sz += ISIS_SUBTLV_TYPE_FIELD_SIZE +
ISIS_SUBTLV_LENGTH_FIELD_SIZE + nb_algo;
nb_msd = router_cap->srv6_msd.max_seg_left_msd +
router_cap->srv6_msd.max_end_pop_msd +
router_cap->srv6_msd.max_h_encaps_msd +
router_cap->srv6_msd.max_end_d_msd;
if (nb_msd != 0)
sz += ISIS_SUBTLV_TYPE_FIELD_SIZE +
ISIS_SUBTLV_LENGTH_FIELD_SIZE +
(ISIS_SUBTLV_NODE_MSD_TYPE_SIZE +
ISIS_SUBTLV_NODE_MSD_VALUE_SIZE) *
nb_msd;
}
return sz;
}
static int pack_tlv_router_cap(const struct isis_router_cap *router_cap,
struct stream *s)
{
size_t tlv_len, len_pos;
uint8_t nb_algo;
size_t subtlv_len, subtlv_len_pos;
bool sr_algo_subtlv_present = false;
if (!router_cap)
return 0;
if (STREAM_WRITEABLE(s) < isis_router_cap_tlv_size(router_cap))
return 1;
/* Add Router Capability TLV 242 with Router ID and Flags */
stream_putc(s, ISIS_TLV_ROUTER_CAPABILITY);
len_pos = stream_get_endp(s);
stream_putc(s, 0); /* Real length will be adjusted later */
stream_put_ipv4(s, router_cap->router_id.s_addr);
stream_putc(s, router_cap->flags);
/* Add SRGB if set as per RFC8667 section #3.1 */
if ((router_cap->srgb.range_size != 0)
&& (router_cap->srgb.lower_bound != 0)) {
stream_putc(s, ISIS_SUBTLV_SID_LABEL_RANGE);
stream_putc(s, ISIS_SUBTLV_SID_LABEL_RANGE_SIZE);
stream_putc(s, router_cap->srgb.flags);
stream_put3(s, router_cap->srgb.range_size);
stream_putc(s, ISIS_SUBTLV_SID_LABEL);
stream_putc(s, ISIS_SUBTLV_SID_LABEL_SIZE);
stream_put3(s, router_cap->srgb.lower_bound);
/* Then SR Algorithm if set as per RFC8667 section #3.2 */
nb_algo = isis_tlvs_sr_algo_count(router_cap);
if (nb_algo > 0) {
stream_putc(s, ISIS_SUBTLV_ALGORITHM);
stream_putc(s, nb_algo);
for (int i = 0; i < SR_ALGORITHM_COUNT; i++)
if (router_cap->algo[i] != SR_ALGORITHM_UNSET)
stream_putc(s, router_cap->algo[i]);
sr_algo_subtlv_present = true;
}
/* Local Block if defined as per RFC8667 section #3.3 */
if ((router_cap->srlb.range_size != 0)
&& (router_cap->srlb.lower_bound != 0)) {
stream_putc(s, ISIS_SUBTLV_SRLB);
stream_putc(s, ISIS_SUBTLV_SID_LABEL_RANGE_SIZE);
/* No Flags are defined for SRLB */
stream_putc(s, 0);
stream_put3(s, router_cap->srlb.range_size);
stream_putc(s, ISIS_SUBTLV_SID_LABEL);
stream_putc(s, ISIS_SUBTLV_SID_LABEL_SIZE);
stream_put3(s, router_cap->srlb.lower_bound);
}
/* And finish with MSD if set as per RFC8491 section #2 */
if (router_cap->msd != 0) {
stream_putc(s, ISIS_SUBTLV_NODE_MSD);
stream_putc(s, ISIS_SUBTLV_NODE_MSD_SIZE);
stream_putc(s, MSD_TYPE_BASE_MPLS_IMPOSITION);
stream_putc(s, router_cap->msd);
}
}
#ifndef FABRICD
/* Flex Algo Definitions */
for (int i = 0; i < SR_ALGORITHM_COUNT; i++) {
struct isis_router_cap_fad *fad;
size_t subtlv_len;
struct admin_group *ag;
uint32_t admin_group_length;
fad = router_cap->fads[i];
if (!fad)
continue;
subtlv_len = isis_router_cap_fad_sub_tlv_len(fad);
if ((stream_get_endp(s) - len_pos - 1) > 250) {
/* Adjust TLV length which depends on subTLVs presence
*/
tlv_len = stream_get_endp(s) - len_pos - 1;
stream_putc_at(s, len_pos, tlv_len);
/* Add Router Capability TLV 242 with Router ID and
* Flags
*/
stream_putc(s, ISIS_TLV_ROUTER_CAPABILITY);
/* Real length will be adjusted later */
len_pos = stream_get_endp(s);
stream_putc(s, 0);
stream_put_ipv4(s, router_cap->router_id.s_addr);
stream_putc(s, router_cap->flags);
}
stream_putc(s, ISIS_SUBTLV_FAD);
stream_putc(s, subtlv_len); /* length will be filled later */
stream_putc(s, fad->fad.algorithm);
stream_putc(s, fad->fad.metric_type);
stream_putc(s, fad->fad.calc_type);
stream_putc(s, fad->fad.priority);
ag = &fad->fad.admin_group_exclude_any;
admin_group_length = admin_group_nb_words(ag);
if (admin_group_length) {
stream_putc(s, ISIS_SUBTLV_FAD_SUBSUBTLV_EXCAG);
stream_putc(s, sizeof(uint32_t) * admin_group_length);
for (size_t i = 0; i < admin_group_length; i++)
stream_putl(s, admin_group_get_offset(ag, i));
}
ag = &fad->fad.admin_group_include_any;
admin_group_length = admin_group_nb_words(ag);
if (admin_group_length) {
stream_putc(s, ISIS_SUBTLV_FAD_SUBSUBTLV_INCANYAG);
stream_putc(s, sizeof(uint32_t) * admin_group_length);
for (size_t i = 0; i < admin_group_length; i++)
stream_putl(s, admin_group_get_offset(ag, i));
}
ag = &fad->fad.admin_group_include_all;
admin_group_length = admin_group_nb_words(ag);
if (admin_group_length) {
stream_putc(s, ISIS_SUBTLV_FAD_SUBSUBTLV_INCALLAG);
stream_putc(s, sizeof(uint32_t) * admin_group_length);
for (size_t i = 0; i < admin_group_length; i++)
stream_putl(s, admin_group_get_offset(ag, i));
}
if (fad->fad.flags != 0) {
stream_putc(s, ISIS_SUBTLV_FAD_SUBSUBTLV_FLAGS);
stream_putc(s, ISIS_SUBTLV_FAD_SUBSUBTLV_FLAGS_SIZE);
stream_putc(s, fad->fad.flags);
}
}
#endif /* ifndef FABRICD */
/* Add SRv6 capabilities if set as per RFC 9352 section #2 */
if (router_cap->srv6_cap.is_srv6_capable) {
stream_putc(s, ISIS_SUBTLV_SRV6_CAPABILITIES);
stream_putc(s, ISIS_SUBTLV_SRV6_CAPABILITIES_SIZE);
stream_putw(s, router_cap->srv6_cap.flags);
/*
* Then add SR Algorithm if set and if we haven't already
* added it when we processed SR-MPLS related Sub-TLVs as
* per RFC 9352 section #3
*/
if (!sr_algo_subtlv_present) {
nb_algo = isis_tlvs_sr_algo_count(router_cap);
if (nb_algo > 0) {
stream_putc(s, ISIS_SUBTLV_ALGORITHM);
stream_putc(s, nb_algo);
for (int i = 0; i < SR_ALGORITHM_COUNT; i++)
if (router_cap->algo[i] !=
SR_ALGORITHM_UNSET)
stream_putc(s,
router_cap->algo[i]);
}
}
/* And finish with MSDs if set as per RFC 9352 section #4 */
if (router_cap->srv6_msd.max_seg_left_msd +
router_cap->srv6_msd.max_end_pop_msd +
router_cap->srv6_msd.max_h_encaps_msd +
router_cap->srv6_msd.max_end_d_msd !=
0) {
stream_putc(s, ISIS_SUBTLV_NODE_MSD);
subtlv_len_pos = stream_get_endp(s);
/* Put 0 as Sub-TLV length for now, real length will be
* adjusted later */
stream_putc(s, 0);
/* RFC 9352 section #4.1 */
if (router_cap->srv6_msd.max_seg_left_msd != 0) {
stream_putc(s, ISIS_SUBTLV_SRV6_MAX_SL_MSD);
stream_putc(
s,
router_cap->srv6_msd.max_seg_left_msd);
}
/* RFC 9352 section #4.2 */
if (router_cap->srv6_msd.max_end_pop_msd != 0) {
stream_putc(s,
ISIS_SUBTLV_SRV6_MAX_END_POP_MSD);
stream_putc(
s,
router_cap->srv6_msd.max_end_pop_msd);
}
/* RFC 9352 section #4.3 */
if (router_cap->srv6_msd.max_h_encaps_msd != 0) {
stream_putc(s,
ISIS_SUBTLV_SRV6_MAX_H_ENCAPS_MSD);
stream_putc(
s,
router_cap->srv6_msd.max_h_encaps_msd);
}
/* RFC 9352 section #4.4 */
if (router_cap->srv6_msd.max_end_d_msd != 0) {
stream_putc(s, ISIS_SUBTLV_SRV6_MAX_END_D_MSD);
stream_putc(s,
router_cap->srv6_msd.max_end_d_msd);
}
/* Adjust Node MSD Sub-TLV length which depends on MSDs
* presence */
subtlv_len = stream_get_endp(s) - subtlv_len_pos - 1;
stream_putc_at(s, subtlv_len_pos, subtlv_len);
}
}
/* Adjust TLV length which depends on subTLVs presence */
tlv_len = stream_get_endp(s) - len_pos - 1;
stream_putc_at(s, len_pos, tlv_len);
return 0;
}
static int unpack_tlv_router_cap(enum isis_tlv_context context,
uint8_t tlv_type, uint8_t tlv_len,
struct stream *s, struct sbuf *log, void *dest,
int indent)
{
struct isis_tlvs *tlvs = dest;
struct isis_router_cap *rcap;
uint8_t type;
uint8_t length;
uint8_t subtlv_len;
uint8_t size;
int num_msd;
sbuf_push(log, indent, "Unpacking Router Capability TLV...\n");
if (tlv_len < ISIS_ROUTER_CAP_SIZE) {
sbuf_push(log, indent, "WARNING: Unexpected TLV size\n");
stream_forward_getp(s, tlv_len);
return 0;
}
if (tlvs->router_cap)
/* Multiple Router Capability found */
rcap = tlvs->router_cap;
else {
/* Allocate router cap structure and initialize SR Algorithms */
rcap = XCALLOC(MTYPE_ISIS_TLV, sizeof(struct isis_router_cap));
for (int i = 0; i < SR_ALGORITHM_COUNT; i++)
rcap->algo[i] = SR_ALGORITHM_UNSET;
}
/* Get Router ID and Flags */
rcap->router_id.s_addr = stream_get_ipv4(s);
rcap->flags = stream_getc(s);
/* Parse remaining part of the TLV if present */
subtlv_len = tlv_len - ISIS_ROUTER_CAP_SIZE;
while (subtlv_len > 2) {
#ifndef FABRICD
struct isis_router_cap_fad *fad;
uint8_t subsubtlvs_len;
#endif /* ifndef FABRICD */
uint8_t msd_type;
type = stream_getc(s);
length = stream_getc(s);
if (length > STREAM_READABLE(s) || length > subtlv_len - 2) {
sbuf_push(
log, indent,
"WARNING: Router Capability subTLV length too large compared to expected size\n");
stream_forward_getp(s, STREAM_READABLE(s));
XFREE(MTYPE_ISIS_TLV, rcap);
return 0;
}
switch (type) {
case ISIS_SUBTLV_SID_LABEL_RANGE:
/* Check that SRGB is correctly formated */
if (length < SUBTLV_RANGE_LABEL_SIZE
|| length > SUBTLV_RANGE_INDEX_SIZE) {
stream_forward_getp(s, length);
break;
}
/* Only one SRGB is supported. Skip subsequent one */
if (rcap->srgb.range_size != 0) {
stream_forward_getp(s, length);
break;
}
rcap->srgb.flags = stream_getc(s);
rcap->srgb.range_size = stream_get3(s);
/* Skip Type and get Length of SID Label */
stream_getc(s);
size = stream_getc(s);
if (size == ISIS_SUBTLV_SID_LABEL_SIZE
&& length != SUBTLV_RANGE_LABEL_SIZE) {
stream_forward_getp(s, length - 6);
break;
}
if (size == ISIS_SUBTLV_SID_INDEX_SIZE
&& length != SUBTLV_RANGE_INDEX_SIZE) {
stream_forward_getp(s, length - 6);
break;
}
if (size == ISIS_SUBTLV_SID_LABEL_SIZE) {
rcap->srgb.lower_bound = stream_get3(s);
} else if (size == ISIS_SUBTLV_SID_INDEX_SIZE) {
rcap->srgb.lower_bound = stream_getl(s);
} else {
stream_forward_getp(s, length - 6);
break;
}
/* SRGB sanity checks. */
if (rcap->srgb.range_size == 0
|| (rcap->srgb.lower_bound <= MPLS_LABEL_RESERVED_MAX)
|| ((rcap->srgb.lower_bound + rcap->srgb.range_size - 1)
> MPLS_LABEL_UNRESERVED_MAX)) {
sbuf_push(log, indent, "Invalid label range. Reset SRGB\n");
rcap->srgb.lower_bound = 0;
rcap->srgb.range_size = 0;
}
/* Only one range is supported. Skip subsequent one */
size = length - (size + SUBTLV_SR_BLOCK_SIZE);
if (size > 0)
stream_forward_getp(s, size);
break;
case ISIS_SUBTLV_ALGORITHM:
if (length == 0)
break;
for (int i = 0; i < SR_ALGORITHM_COUNT; i++)
rcap->algo[i] = SR_ALGORITHM_UNSET;
for (int i = 0; i < length; i++) {
uint8_t algo;
algo = stream_getc(s);
rcap->algo[algo] = algo;
}
break;
case ISIS_SUBTLV_SRLB:
/* Check that SRLB is correctly formated */
if (length < SUBTLV_RANGE_LABEL_SIZE
|| length > SUBTLV_RANGE_INDEX_SIZE) {
stream_forward_getp(s, length);
break;
}
/* RFC 8667 section #3.3: Only one SRLB is authorized */
if (rcap->srlb.range_size != 0) {
stream_forward_getp(s, length);
break;
}
/* Ignore Flags which are not defined */
stream_getc(s);
rcap->srlb.range_size = stream_get3(s);
/* Skip Type and get Length of SID Label */
stream_getc(s);
size = stream_getc(s);
if (size == ISIS_SUBTLV_SID_LABEL_SIZE
&& length != SUBTLV_RANGE_LABEL_SIZE) {
stream_forward_getp(s, length - 6);
break;
}
if (size == ISIS_SUBTLV_SID_INDEX_SIZE
&& length != SUBTLV_RANGE_INDEX_SIZE) {
stream_forward_getp(s, length - 6);
break;
}
if (size == ISIS_SUBTLV_SID_LABEL_SIZE) {
rcap->srlb.lower_bound = stream_get3(s);
} else if (size == ISIS_SUBTLV_SID_INDEX_SIZE) {
rcap->srlb.lower_bound = stream_getl(s);
} else {
stream_forward_getp(s, length - 6);
break;
}
/* SRLB sanity checks. */
if (rcap->srlb.range_size == 0
|| (rcap->srlb.lower_bound <= MPLS_LABEL_RESERVED_MAX)
|| ((rcap->srlb.lower_bound + rcap->srlb.range_size - 1)
> MPLS_LABEL_UNRESERVED_MAX)) {
sbuf_push(log, indent, "Invalid label range. Reset SRLB\n");
rcap->srlb.lower_bound = 0;
rcap->srlb.range_size = 0;
}
/* Only one range is supported. Skip subsequent one */
size = length - (size + SUBTLV_SR_BLOCK_SIZE);
if (size > 0)
stream_forward_getp(s, size);
break;
case ISIS_SUBTLV_NODE_MSD:
sbuf_push(log, indent,
"Unpacking Node MSD sub-TLV...\n");
/* Check that MSD is correctly formated */
if (length % 2) {
sbuf_push(
log, indent,
"WARNING: Unexpected MSD sub-TLV length\n");
stream_forward_getp(s, length);
break;
}
/* Get the number of MSDs carried in the value field of
* the Node MSD sub-TLV. The value field consists of one
* or more pairs of a 1-octet MSD-Type and 1-octet
* MSD-Value */
num_msd = length / 2;
/* Unpack MSDs */
for (int i = 0; i < num_msd; i++) {
msd_type = stream_getc(s);
switch (msd_type) {
case MSD_TYPE_BASE_MPLS_IMPOSITION:
/* BMI-MSD type as per RFC 8491 */
rcap->msd = stream_getc(s);
break;
case ISIS_SUBTLV_SRV6_MAX_SL_MSD:
/* SRv6 Maximum Segments Left MSD Type
* as per RFC 9352 section #4.1 */
rcap->srv6_msd.max_seg_left_msd =
stream_getc(s);
break;
case ISIS_SUBTLV_SRV6_MAX_END_POP_MSD:
/* SRv6 Maximum End Pop MSD Type as per
* RFC 9352 section #4.2 */
rcap->srv6_msd.max_end_pop_msd =
stream_getc(s);
break;
case ISIS_SUBTLV_SRV6_MAX_H_ENCAPS_MSD:
/* SRv6 Maximum H.Encaps MSD Type as per
* RFC 9352 section #4.3 */
rcap->srv6_msd.max_h_encaps_msd =
stream_getc(s);
break;
case ISIS_SUBTLV_SRV6_MAX_END_D_MSD:
/* SRv6 Maximum End D MSD Type as per
* RFC 9352 section #4.4 */
rcap->srv6_msd.max_end_d_msd =
stream_getc(s);
break;
default:
/* Unknown MSD, let's skip it */
sbuf_push(
log, indent,
"WARNING: Skipping unknown MSD Type %hhu (1 byte)\n",
msd_type);
stream_forward_getp(s, 1);
}
}
break;
#ifndef FABRICD
case ISIS_SUBTLV_FAD:
fad = XCALLOC(MTYPE_ISIS_TLV,
sizeof(struct isis_router_cap_fad));
fad->fad.algorithm = stream_getc(s);
fad->fad.metric_type = stream_getc(s);
fad->fad.calc_type = stream_getc(s);
fad->fad.priority = stream_getc(s);
rcap->fads[fad->fad.algorithm] = fad;
admin_group_init(&fad->fad.admin_group_exclude_any);
admin_group_init(&fad->fad.admin_group_include_any);
admin_group_init(&fad->fad.admin_group_include_all);
subsubtlvs_len = length - 4;
while (subsubtlvs_len > 2) {
struct admin_group *ag;
uint8_t subsubtlv_type;
uint8_t subsubtlv_len;
uint32_t v;
int n_ag, i;
subsubtlv_type = stream_getc(s);
subsubtlv_len = stream_getc(s);
switch (subsubtlv_type) {
case ISIS_SUBTLV_FAD_SUBSUBTLV_EXCAG:
ag = &fad->fad.admin_group_exclude_any;
n_ag = subsubtlv_len / sizeof(uint32_t);
for (i = 0; i < n_ag; i++) {
v = stream_getl(s);
admin_group_bulk_set(ag, v, i);
}
break;
case ISIS_SUBTLV_FAD_SUBSUBTLV_INCANYAG:
ag = &fad->fad.admin_group_include_any;
n_ag = subsubtlv_len / sizeof(uint32_t);
for (i = 0; i < n_ag; i++) {
v = stream_getl(s);
admin_group_bulk_set(ag, v, i);
}
break;
case ISIS_SUBTLV_FAD_SUBSUBTLV_INCALLAG:
ag = &fad->fad.admin_group_include_all;
n_ag = subsubtlv_len / sizeof(uint32_t);
for (i = 0; i < n_ag; i++) {
v = stream_getl(s);
admin_group_bulk_set(ag, v, i);
}
break;
case ISIS_SUBTLV_FAD_SUBSUBTLV_FLAGS:
if (subsubtlv_len == 0)
break;
fad->fad.flags = stream_getc(s);
for (i = subsubtlv_len - 1; i > 0; --i)
stream_getc(s);
break;
case ISIS_SUBTLV_FAD_SUBSUBTLV_ESRLG:
fad->fad.exclude_srlg = true;
stream_forward_getp(s, subsubtlv_len);
break;
default:
sbuf_push(
log, indent,
"Received an unsupported Flex-Algo sub-TLV type %u\n",
subsubtlv_type);
fad->fad.unsupported_subtlv = true;
stream_forward_getp(s, subsubtlv_len);
break;
}
subsubtlvs_len -= 2 + subsubtlv_len;
}
break;
#endif /* ifndef FABRICD */
case ISIS_SUBTLV_SRV6_CAPABILITIES:
sbuf_push(log, indent,
"Unpacking SRv6 Capabilities sub-TLV...\n");
/* Check that SRv6 capabilities sub-TLV is correctly
* formated */
if (length < ISIS_SUBTLV_SRV6_CAPABILITIES_SIZE) {
sbuf_push(
log, indent,
"WARNING: Unexpected SRv6 Capabilities sub-TLV size (expected %d or more bytes, got %hhu)\n",
ISIS_SUBTLV_SRV6_CAPABILITIES_SIZE,
length);
stream_forward_getp(s, length);
break;
}
/* Only one SRv6 capabilities is supported. Skip
* subsequent one */
if (rcap->srv6_cap.is_srv6_capable) {
sbuf_push(
log, indent,
"WARNING: SRv6 Capabilities sub-TLV present multiple times, ignoring.\n");
stream_forward_getp(s, length);
break;
}
rcap->srv6_cap.is_srv6_capable = true;
rcap->srv6_cap.flags = stream_getw(s);
/* The SRv6 Capabilities Sub-TLV may contain optional
* Sub-Sub-TLVs, as per RFC 9352 section #2.
* Skip any Sub-Sub-TLV contained in the SRv6
* Capabilities Sub-TLV that is not currently supported
* by IS-IS.
*/
if (length > ISIS_SUBTLV_SRV6_CAPABILITIES_SIZE)
sbuf_push(
log, indent,
"Skipping unknown sub-TLV (%hhu bytes)\n",
length);
stream_forward_getp(
s, length - ISIS_SUBTLV_SRV6_CAPABILITIES_SIZE);
break;
default:
stream_forward_getp(s, length);
break;
}
subtlv_len = subtlv_len - length - 2;
}
tlvs->router_cap = rcap;
return 0;
}
/* Functions related to TLV 10 Authentication */
static struct isis_item *copy_item_auth(struct isis_item *i)
{
struct isis_auth *auth = (struct isis_auth *)i;
struct isis_auth *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
rv->type = auth->type;
rv->length = auth->length;
memcpy(rv->value, auth->value, sizeof(rv->value));
return (struct isis_item *)rv;
}
static void format_item_auth(uint16_t mtid, struct isis_item *i,
struct sbuf *buf, struct json_object *json,
int indent)
{
struct isis_auth *auth = (struct isis_auth *)i;
char obuf[768];
if (json)
json_object_string_add(json, "test-auth", "ok");
else
sbuf_push(buf, indent, "Authentication:\n");
switch (auth->type) {
case ISIS_PASSWD_TYPE_CLEARTXT:
zlog_sanitize(obuf, sizeof(obuf), auth->value, auth->length);
if (json)
json_object_string_add(json, "auth-pass", obuf);
else
sbuf_push(buf, indent, " Password: %s\n", obuf);
break;
case ISIS_PASSWD_TYPE_HMAC_MD5:
for (unsigned int j = 0; j < 16; j++) {
snprintf(obuf + 2 * j, sizeof(obuf) - 2 * j, "%02hhx",
auth->value[j]);
}
if (json)
json_object_string_add(json, "auth-hmac-md5", obuf);
else
sbuf_push(buf, indent, " HMAC-MD5: %s\n", obuf);
break;
default:
if (json)
json_object_int_add(json, "auth-unknown", auth->type);
else
sbuf_push(buf, indent, " Unknown (%hhu)\n",
auth->type);
break;
}
}
static void free_item_auth(struct isis_item *i)
{
XFREE(MTYPE_ISIS_TLV, i);
}
static int pack_item_auth(struct isis_item *i, struct stream *s,
size_t *min_len)
{
struct isis_auth *auth = (struct isis_auth *)i;
if (STREAM_WRITEABLE(s) < 1) {
*min_len = 1;
return 1;
}
stream_putc(s, auth->type);
switch (auth->type) {
case ISIS_PASSWD_TYPE_CLEARTXT:
if (STREAM_WRITEABLE(s) < auth->length) {
*min_len = 1 + auth->length;
return 1;
}
stream_put(s, auth->passwd, auth->length);
break;
case ISIS_PASSWD_TYPE_HMAC_MD5:
if (STREAM_WRITEABLE(s) < 16) {
*min_len = 1 + 16;
return 1;
}
auth->offset = stream_get_endp(s);
stream_put(s, NULL, 16);
break;
default:
return 1;
}
return 0;
}
static int unpack_item_auth(uint16_t mtid, uint8_t len, struct stream *s,
struct sbuf *log, void *dest, int indent)
{
struct isis_tlvs *tlvs = dest;
sbuf_push(log, indent, "Unpack Auth TLV...\n");
if (len < 1) {
sbuf_push(
log, indent,
"Not enough data left.(Expected 1 bytes of auth type, got %hhu)\n",
len);
return 1;
}
struct isis_auth *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
rv->type = stream_getc(s);
rv->length = len - 1;
if (rv->type == ISIS_PASSWD_TYPE_HMAC_MD5 && rv->length != 16) {
sbuf_push(
log, indent,
"Unexpected auth length for HMAC-MD5 (expected 16, got %hhu)\n",
rv->length);
XFREE(MTYPE_ISIS_TLV, rv);
return 1;
}
rv->offset = stream_get_getp(s);
stream_get(rv->value, s, rv->length);
format_item_auth(mtid, (struct isis_item *)rv, log, NULL, indent + 2);
append_item(&tlvs->isis_auth, (struct isis_item *)rv);
return 0;
}
/* Functions related to TLV 13 Purge Originator */
static struct isis_purge_originator *copy_tlv_purge_originator(
struct isis_purge_originator *poi)
{
if (!poi)
return NULL;
struct isis_purge_originator *rv;
rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
rv->sender_set = poi->sender_set;
memcpy(rv->generator, poi->generator, sizeof(rv->generator));
if (poi->sender_set)
memcpy(rv->sender, poi->sender, sizeof(rv->sender));
return rv;
}
static void format_tlv_purge_originator(struct isis_purge_originator *poi,
struct sbuf *buf,
struct json_object *json, int indent)
{
char sen_id[ISO_SYSID_STRLEN];
char gen_id[ISO_SYSID_STRLEN];
if (!poi)
return;
snprintfrr(gen_id, ISO_SYSID_STRLEN, "%pSY", poi->generator);
if (poi->sender_set)
snprintfrr(sen_id, ISO_SYSID_STRLEN, "%pSY", poi->sender);
if (json) {
struct json_object *purge_json;
purge_json = json_object_new_object();
json_object_object_add(json, "purge_originator", purge_json);
json_object_string_add(purge_json, "id", gen_id);
if (poi->sender_set)
json_object_string_add(purge_json, "rec-from", sen_id);
} else {
sbuf_push(buf, indent, "Purge Originator Identification:\n");
sbuf_push(buf, indent, " Generator: %s\n", gen_id);
if (poi->sender_set)
sbuf_push(buf, indent, " Received-From: %s\n", sen_id);
}
}
static void free_tlv_purge_originator(struct isis_purge_originator *poi)
{
XFREE(MTYPE_ISIS_TLV, poi);
}
static int pack_tlv_purge_originator(struct isis_purge_originator *poi,
struct stream *s)
{
if (!poi)
return 0;
uint8_t data_len = 1 + sizeof(poi->generator);
if (poi->sender_set)
data_len += sizeof(poi->sender);
if (STREAM_WRITEABLE(s) < (unsigned)(2 + data_len))
return 1;
stream_putc(s, ISIS_TLV_PURGE_ORIGINATOR);
stream_putc(s, data_len);
stream_putc(s, poi->sender_set ? 2 : 1);
stream_put(s, poi->generator, sizeof(poi->generator));
if (poi->sender_set)
stream_put(s, poi->sender, sizeof(poi->sender));
return 0;
}
static int unpack_tlv_purge_originator(enum isis_tlv_context context,
uint8_t tlv_type, uint8_t tlv_len,
struct stream *s, struct sbuf *log,
void *dest, int indent)
{
struct isis_tlvs *tlvs = dest;
struct isis_purge_originator poi = {};
sbuf_push(log, indent, "Unpacking Purge Originator Identification TLV...\n");
if (tlv_len < 7) {
sbuf_push(log, indent, "Not enough data left. (Expected at least 7 bytes, got %hhu)\n", tlv_len);
return 1;
}
uint8_t number_of_ids = stream_getc(s);
if (number_of_ids == 1) {
poi.sender_set = false;
} else if (number_of_ids == 2) {
poi.sender_set = true;
} else {
sbuf_push(log, indent, "Got invalid value for number of system IDs: %hhu)\n", number_of_ids);
return 1;
}
if (tlv_len != 1 + 6 * number_of_ids) {
sbuf_push(log, indent, "Incorrect tlv len for number of IDs.\n");
return 1;
}
stream_get(poi.generator, s, sizeof(poi.generator));
if (poi.sender_set)
stream_get(poi.sender, s, sizeof(poi.sender));
if (tlvs->purge_originator) {
sbuf_push(log, indent,
"WARNING: Purge originator present multiple times, ignoring.\n");
return 0;
}
tlvs->purge_originator = copy_tlv_purge_originator(&poi);
return 0;
}
/* Functions relating to item TLVs */
static void init_item_list(struct isis_item_list *items)
{
items->head = NULL;
items->tail = &items->head;
items->count = 0;
}
static struct isis_item *copy_item(enum isis_tlv_context context,
enum isis_tlv_type type,
struct isis_item *item)
{
const struct tlv_ops *ops = tlv_table[context][type];
if (ops && ops->copy_item)
return ops->copy_item(item);
assert(!"Unknown item tlv type!");
return NULL;
}
static void copy_items(enum isis_tlv_context context, enum isis_tlv_type type,
struct isis_item_list *src, struct isis_item_list *dest)
{
struct isis_item *item;
init_item_list(dest);
for (item = src->head; item; item = item->next) {
append_item(dest, copy_item(context, type, item));
}
}
static void format_item(uint16_t mtid, enum isis_tlv_context context,
enum isis_tlv_type type, struct isis_item *i,
struct sbuf *buf, struct json_object *json, int indent)
{
const struct tlv_ops *ops = tlv_table[context][type];
if (ops && ops->format_item) {
ops->format_item(mtid, i, buf, json, indent);
return;
}
assert(!"Unknown item tlv type!");
}
static void format_items_(uint16_t mtid, enum isis_tlv_context context,
enum isis_tlv_type type, struct isis_item_list *items,
struct sbuf *buf, struct json_object *json,
int indent)
{
struct isis_item *i;
for (i = items->head; i; i = i->next)
format_item(mtid, context, type, i, buf, json, indent);
}
static void free_item(enum isis_tlv_context tlv_context,
enum isis_tlv_type tlv_type, struct isis_item *item)
{
const struct tlv_ops *ops = tlv_table[tlv_context][tlv_type];
if (ops && ops->free_item) {
ops->free_item(item);
return;
}
assert(!"Unknown item tlv type!");
}
static void free_items(enum isis_tlv_context context, enum isis_tlv_type type,
struct isis_item_list *items)
{
struct isis_item *item, *next_item;
for (item = items->head; item; item = next_item) {
next_item = item->next;
free_item(context, type, item);
}
}
static int pack_item(enum isis_tlv_context context, enum isis_tlv_type type,
struct isis_item *i, struct stream *s, size_t *min_len,
struct isis_tlvs **fragment_tlvs,
const struct pack_order_entry *pe, uint16_t mtid)
{
const struct tlv_ops *ops = tlv_table[context][type];
if (ops && ops->pack_item) {
return ops->pack_item(i, s, min_len);
}
assert(!"Unknown item tlv type!");
return 1;
}
static void add_item_to_fragment(struct isis_item *i,
const struct pack_order_entry *pe,
struct isis_tlvs *fragment_tlvs, uint16_t mtid)
{
struct isis_item_list *l;
if (pe->how_to_pack == ISIS_ITEMS) {
l = (struct isis_item_list *)(((char *)fragment_tlvs) + pe->what_to_pack);
} else {
struct isis_mt_item_list *m;
m = (struct isis_mt_item_list *)(((char *)fragment_tlvs) + pe->what_to_pack);
l = isis_get_mt_items(m, mtid);
}
append_item(l, copy_item(pe->context, pe->type, i));
}
static int pack_items_(uint16_t mtid, enum isis_tlv_context context,
enum isis_tlv_type type, struct isis_item_list *items,
struct stream *s, struct isis_tlvs **fragment_tlvs,
const struct pack_order_entry *pe,
struct isis_tlvs *(*new_fragment)(struct list *l),
struct list *new_fragment_arg)
{
size_t len_pos, last_len, len;
struct isis_item *item = NULL;
int rv;
size_t min_len = 0;
if (!items->head)
return 0;
top:
if (STREAM_WRITEABLE(s) < 2)
goto too_long;
stream_putc(s, type);
len_pos = stream_get_endp(s);
stream_putc(s, 0); /* Put 0 as length for now */
if (context == ISIS_CONTEXT_LSP && IS_COMPAT_MT_TLV(type)
&& mtid != ISIS_MT_IPV4_UNICAST) {
if (STREAM_WRITEABLE(s) < 2)
goto too_long;
stream_putw(s, mtid);
}
/* The SRv6 Locator TLV (RFC 9352 section #7.1) starts with the MTID
* field */
if (context == ISIS_CONTEXT_LSP && type == ISIS_TLV_SRV6_LOCATOR) {
if (STREAM_WRITEABLE(s) < 2)
goto too_long;
stream_putw(s, mtid);
}
if (context == ISIS_CONTEXT_LSP && type == ISIS_TLV_OLDSTYLE_REACH) {
if (STREAM_WRITEABLE(s) < 1)
goto too_long;
stream_putc(s, 0); /* Virtual flag is set to 0 */
}
last_len = len = 0;
for (item = item ? item : items->head; item; item = item->next) {
rv = pack_item(context, type, item, s, &min_len, fragment_tlvs,
pe, mtid);
if (rv)
goto too_long;
len = stream_get_endp(s) - len_pos - 1;
/* Multiple auths don't go into one TLV, so always break */
if (context == ISIS_CONTEXT_LSP && type == ISIS_TLV_AUTH) {
item = item->next;
break;
}
/* Multiple prefix-sids don't go into one TLV, so always break */
if (type == ISIS_SUBTLV_PREFIX_SID
&& (context == ISIS_CONTEXT_SUBTLV_IP_REACH
|| context == ISIS_CONTEXT_SUBTLV_IPV6_REACH)) {
item = item->next;
break;
}
if (len > 255) {
if (!last_len) /* strange, not a single item fit */
return 1;
/* drop last tlv, otherwise, its too long */
stream_set_endp(s, len_pos + 1 + last_len);
len = last_len;
break;
}
if (fragment_tlvs)
add_item_to_fragment(item, pe, *fragment_tlvs, mtid);
last_len = len;
}
stream_putc_at(s, len_pos, len);
if (item)
goto top;
return 0;
too_long:
if (!fragment_tlvs)
return 1;
stream_reset(s);
if (STREAM_WRITEABLE(s) < min_len)
return 1;
*fragment_tlvs = new_fragment(new_fragment_arg);
goto top;
}
#define pack_items(...) pack_items_(ISIS_MT_IPV4_UNICAST, __VA_ARGS__)
static void append_item(struct isis_item_list *dest, struct isis_item *item)
{
*dest->tail = item;
dest->tail = &(*dest->tail)->next;
dest->count++;
}
static void delete_item(struct isis_item_list *dest, struct isis_item *del)
{
struct isis_item *item, *prev = NULL, *next;
/* Sanity Check */
if ((dest == NULL) || (del == NULL))
return;
/*
* TODO: delete is tricky because "dest" is a singly linked list.
* We need to switch a doubly linked list.
*/
for (item = dest->head; item; item = next) {
if (item->next == del) {
prev = item;
break;
}
next = item->next;
}
if (prev)
prev->next = del->next;
if (dest->head == del)
dest->head = del->next;
if ((struct isis_item *)dest->tail == del) {
*dest->tail = prev;
if (prev)
dest->tail = &(*dest->tail)->next;
else
dest->tail = &dest->head;
}
dest->count--;
}
static struct isis_item *last_item(struct isis_item_list *list)
{
return container_of(list->tail, struct isis_item, next);
}
static int unpack_item(uint16_t mtid, enum isis_tlv_context context,
uint8_t tlv_type, uint8_t len, struct stream *s,
struct sbuf *log, void *dest, int indent)
{
const struct tlv_ops *ops = tlv_table[context][tlv_type];
if (ops && ops->unpack_item)
return ops->unpack_item(mtid, len, s, log, dest, indent);
assert(!"Unknown item tlv type!");
sbuf_push(log, indent, "Unknown item tlv type!\n");
return 1;
}
static int unpack_tlv_with_items(enum isis_tlv_context context,
uint8_t tlv_type, uint8_t tlv_len,
struct stream *s, struct sbuf *log, void *dest,
int indent)
{
size_t tlv_start;
size_t tlv_pos;
int rv;
uint16_t mtid;
tlv_start = stream_get_getp(s);
tlv_pos = 0;
if (context == ISIS_CONTEXT_LSP &&
(IS_COMPAT_MT_TLV(tlv_type) || tlv_type == ISIS_TLV_SRV6_LOCATOR)) {
if (tlv_len < 2) {
sbuf_push(log, indent,
"TLV is too short to contain MTID\n");
return 1;
}
mtid = stream_getw(s) & ISIS_MT_MASK;
tlv_pos += 2;
sbuf_push(log, indent, "Unpacking as MT %s item TLV...\n",
isis_mtid2str_fake(mtid));
} else {
sbuf_push(log, indent, "Unpacking as item TLV...\n");
mtid = ISIS_MT_IPV4_UNICAST;
}
if (context == ISIS_CONTEXT_LSP
&& tlv_type == ISIS_TLV_OLDSTYLE_REACH) {
if (tlv_len - tlv_pos < 1) {
sbuf_push(log, indent,
"TLV is too short for old style reach\n");
return 1;
}
stream_forward_getp(s, 1);
tlv_pos += 1;
}
if (context == ISIS_CONTEXT_LSP
&& tlv_type == ISIS_TLV_OLDSTYLE_IP_REACH) {
struct isis_tlvs *tlvs = dest;
dest = &tlvs->oldstyle_ip_reach;
} else if (context == ISIS_CONTEXT_LSP
&& tlv_type == ISIS_TLV_OLDSTYLE_IP_REACH_EXT) {
struct isis_tlvs *tlvs = dest;
dest = &tlvs->oldstyle_ip_reach_ext;
}
if (context == ISIS_CONTEXT_LSP
&& tlv_type == ISIS_TLV_MT_ROUTER_INFO) {
struct isis_tlvs *tlvs = dest;
tlvs->mt_router_info_empty = (tlv_pos >= (size_t)tlv_len);
}
while (tlv_pos < (size_t)tlv_len) {
rv = unpack_item(mtid, context, tlv_type, tlv_len - tlv_pos, s,
log, dest, indent + 2);
if (rv)
return rv;
tlv_pos = stream_get_getp(s) - tlv_start;
}
return 0;
}
/* Functions to manipulate mt_item_lists */
static int isis_mt_item_list_cmp(const struct isis_item_list *a,
const struct isis_item_list *b)
{
if (a->mtid < b->mtid)
return -1;
if (a->mtid > b->mtid)
return 1;
return 0;
}
RB_PROTOTYPE(isis_mt_item_list, isis_item_list, mt_tree, isis_mt_item_list_cmp);
RB_GENERATE(isis_mt_item_list, isis_item_list, mt_tree, isis_mt_item_list_cmp);
struct isis_item_list *isis_get_mt_items(struct isis_mt_item_list *m,
uint16_t mtid)
{
struct isis_item_list *rv;
rv = isis_lookup_mt_items(m, mtid);
if (!rv) {
rv = XCALLOC(MTYPE_ISIS_MT_ITEM_LIST, sizeof(*rv));
init_item_list(rv);
rv->mtid = mtid;
RB_INSERT(isis_mt_item_list, m, rv);
}
return rv;
}
struct isis_item_list *isis_lookup_mt_items(struct isis_mt_item_list *m,
uint16_t mtid)
{
struct isis_item_list key = {.mtid = mtid};
return RB_FIND(isis_mt_item_list, m, &key);
}
static void free_mt_items(enum isis_tlv_context context,
enum isis_tlv_type type, struct isis_mt_item_list *m)
{
struct isis_item_list *n, *nnext;
RB_FOREACH_SAFE (n, isis_mt_item_list, m, nnext) {
free_items(context, type, n);
RB_REMOVE(isis_mt_item_list, m, n);
XFREE(MTYPE_ISIS_MT_ITEM_LIST, n);
}
}
static void format_mt_items(enum isis_tlv_context context,
enum isis_tlv_type type,
struct isis_mt_item_list *m, struct sbuf *buf,
struct json_object *json, int indent)
{
struct isis_item_list *n;
RB_FOREACH (n, isis_mt_item_list, m) {
format_items_(n->mtid, context, type, n, buf, json, indent);
}
}
static int pack_mt_items(enum isis_tlv_context context, enum isis_tlv_type type,
struct isis_mt_item_list *m, struct stream *s,
struct isis_tlvs **fragment_tlvs,
const struct pack_order_entry *pe,
struct isis_tlvs *(*new_fragment)(struct list *l),
struct list *new_fragment_arg)
{
struct isis_item_list *n;
RB_FOREACH (n, isis_mt_item_list, m) {
int rv;
rv = pack_items_(n->mtid, context, type, n, s, fragment_tlvs,
pe, new_fragment, new_fragment_arg);
if (rv)
return rv;
}
return 0;
}
static void copy_mt_items(enum isis_tlv_context context,
enum isis_tlv_type type,
struct isis_mt_item_list *src,
struct isis_mt_item_list *dest)
{
struct isis_item_list *n;
RB_INIT(isis_mt_item_list, dest);
RB_FOREACH (n, isis_mt_item_list, src) {
copy_items(context, type, n, isis_get_mt_items(dest, n->mtid));
}
}
/* Functions related to TLV 27 SRv6 Locator as per RFC 9352 section #7.1*/
static struct isis_item *copy_item_srv6_locator(struct isis_item *i)
{
struct isis_srv6_locator_tlv *loc = (struct isis_srv6_locator_tlv *)i;
struct isis_srv6_locator_tlv *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
rv->metric = loc->metric;
rv->flags = loc->flags;
rv->algorithm = loc->algorithm;
rv->prefix = loc->prefix;
rv->subtlvs = copy_subtlvs(loc->subtlvs);
return (struct isis_item *)rv;
}
static void format_item_srv6_locator(uint16_t mtid, struct isis_item *i,
struct sbuf *buf, struct json_object *json,
int indent)
{
struct isis_srv6_locator_tlv *loc = (struct isis_srv6_locator_tlv *)i;
if (json) {
struct json_object *loc_json;
loc_json = json_object_new_object();
json_object_object_add(json, "srv6-locator", loc_json);
json_object_int_add(loc_json, "mt-id", mtid);
json_object_string_addf(loc_json, "prefix", "%pFX",
&loc->prefix);
json_object_int_add(loc_json, "metric", loc->metric);
json_object_string_add(
loc_json, "d-flag",
CHECK_FLAG(loc->flags, ISIS_TLV_SRV6_LOCATOR_FLAG_D)
? "yes"
: "");
json_object_int_add(loc_json, "algorithm", loc->algorithm);
json_object_string_add(loc_json, "mt-name",
isis_mtid2str(mtid));
if (loc->subtlvs) {
struct json_object *subtlvs_json;
subtlvs_json = json_object_new_object();
json_object_object_add(loc_json, "subtlvs",
subtlvs_json);
format_subtlvs(loc->subtlvs, NULL, subtlvs_json, 0);
}
} else {
sbuf_push(buf, indent, "SRv6 Locator: %pFX (Metric: %u)%s",
&loc->prefix, loc->metric,
CHECK_FLAG(loc->flags, ISIS_TLV_SRV6_LOCATOR_FLAG_D)
? " D-flag"
: "");
sbuf_push(buf, 0, " %s\n", isis_mtid2str(mtid));
if (loc->subtlvs) {
sbuf_push(buf, indent, " Sub-TLVs:\n");
format_subtlvs(loc->subtlvs, buf, NULL, indent + 4);
}
}
}
static void free_item_srv6_locator(struct isis_item *i)
{
struct isis_srv6_locator_tlv *item = (struct isis_srv6_locator_tlv *)i;
isis_free_subtlvs(item->subtlvs);
XFREE(MTYPE_ISIS_TLV, item);
}
static int pack_item_srv6_locator(struct isis_item *i, struct stream *s,
size_t *min_len)
{
struct isis_srv6_locator_tlv *loc = (struct isis_srv6_locator_tlv *)i;
if (STREAM_WRITEABLE(s) < 7 + (unsigned)PSIZE(loc->prefix.prefixlen)) {
*min_len = 7 + (unsigned)PSIZE(loc->prefix.prefixlen);
return 1;
}
stream_putl(s, loc->metric);
stream_putc(s, loc->flags);
stream_putc(s, loc->algorithm);
/* Locator size */
stream_putc(s, loc->prefix.prefixlen);
/* Locator prefix */
stream_put(s, &loc->prefix.prefix.s6_addr,
PSIZE(loc->prefix.prefixlen));
if (loc->subtlvs) {
/* Pack Sub-TLVs */
if (pack_subtlvs(loc->subtlvs, s))
return 1;
} else {
/* No Sub-TLVs */
if (STREAM_WRITEABLE(s) < 1) {
*min_len = 8 + (unsigned)PSIZE(loc->prefix.prefixlen);
return 1;
}
/* Put 0 as Sub-TLV length, because we have no Sub-TLVs */
stream_putc(s, 0);
}
return 0;
}
static int unpack_item_srv6_locator(uint16_t mtid, uint8_t len,
struct stream *s, struct sbuf *log,
void *dest, int indent)
{
struct isis_tlvs *tlvs = dest;
struct isis_srv6_locator_tlv *rv = NULL;
size_t consume;
uint8_t subtlv_len;
struct isis_item_list *items;
items = isis_get_mt_items(&tlvs->srv6_locator, mtid);
sbuf_push(log, indent, "Unpacking SRv6 Locator...\n");
consume = 7;
if (len < consume) {
sbuf_push(
log, indent,
"Not enough data left. (expected 7 or more bytes, got %hhu)\n",
len);
goto out;
}
rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
rv->metric = stream_getl(s);
rv->flags = stream_getc(s);
rv->algorithm = stream_getc(s);
rv->prefix.family = AF_INET6;
rv->prefix.prefixlen = stream_getc(s);
if (rv->prefix.prefixlen > IPV6_MAX_BITLEN) {
sbuf_push(log, indent, "Loc Size %u is implausible for SRv6\n",
rv->prefix.prefixlen);
goto out;
}
consume += PSIZE(rv->prefix.prefixlen);
if (len < consume) {
sbuf_push(
log, indent,
"Expected %u bytes of prefix, but only %u bytes available.\n",
PSIZE(rv->prefix.prefixlen), len - 7);
goto out;
}
stream_get(&rv->prefix.prefix.s6_addr, s, PSIZE(rv->prefix.prefixlen));
struct in6_addr orig_locator = rv->prefix.prefix;
apply_mask_ipv6(&rv->prefix);
if (memcmp(&orig_locator, &rv->prefix.prefix, sizeof(orig_locator)))
sbuf_push(log, indent + 2,
"WARNING: SRv6 Locator had hostbits set.\n");
format_item_srv6_locator(mtid, (struct isis_item *)rv, log, NULL,
indent + 2);
consume += 1;
if (len < consume) {
sbuf_push(
log, indent,
"Expected 1 byte of subtlv len, but no more data persent.\n");
goto out;
}
subtlv_len = stream_getc(s);
if (subtlv_len) {
consume += subtlv_len;
if (len < consume) {
sbuf_push(
log, indent,
"Expected %hhu bytes of subtlvs, but only %u bytes available.\n",
subtlv_len,
len - 7 - PSIZE(rv->prefix.prefixlen));
goto out;
}
rv->subtlvs =
isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_SRV6_LOCATOR);
bool unpacked_known_tlvs = false;
if (unpack_tlvs(ISIS_CONTEXT_SUBTLV_SRV6_LOCATOR, subtlv_len, s,
log, rv->subtlvs, indent + 4,
&unpacked_known_tlvs)) {
goto out;
}
if (!unpacked_known_tlvs) {
isis_free_subtlvs(rv->subtlvs);
rv->subtlvs = NULL;
}
}
append_item(items, (struct isis_item *)rv);
return 0;
out:
if (rv)
free_item_srv6_locator((struct isis_item *)rv);
return 1;
}
/* Functions related to tlvs in general */
struct isis_tlvs *isis_alloc_tlvs(void)
{
struct isis_tlvs *result;
result = XCALLOC(MTYPE_ISIS_TLV, sizeof(*result));
init_item_list(&result->isis_auth);
init_item_list(&result->area_addresses);
init_item_list(&result->mt_router_info);
init_item_list(&result->oldstyle_reach);
init_item_list(&result->lan_neighbor);
init_item_list(&result->lsp_entries);
init_item_list(&result->extended_reach);
RB_INIT(isis_mt_item_list, &result->mt_reach);
init_item_list(&result->oldstyle_ip_reach);
init_item_list(&result->oldstyle_ip_reach_ext);
init_item_list(&result->ipv4_address);
init_item_list(&result->ipv6_address);
init_item_list(&result->global_ipv6_address);
init_item_list(&result->extended_ip_reach);
RB_INIT(isis_mt_item_list, &result->mt_ip_reach);
init_item_list(&result->ipv6_reach);
RB_INIT(isis_mt_item_list, &result->mt_ipv6_reach);
RB_INIT(isis_mt_item_list, &result->srv6_locator);
return result;
}
struct isis_tlvs *isis_copy_tlvs(struct isis_tlvs *tlvs)
{
struct isis_tlvs *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth,
&rv->isis_auth);
rv->purge_originator =
copy_tlv_purge_originator(tlvs->purge_originator);
copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES,
&tlvs->area_addresses, &rv->area_addresses);
copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO,
&tlvs->mt_router_info, &rv->mt_router_info);
rv->mt_router_info_empty = tlvs->mt_router_info_empty;
copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_REACH,
&tlvs->oldstyle_reach, &rv->oldstyle_reach);
copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_LAN_NEIGHBORS,
&tlvs->lan_neighbor, &rv->lan_neighbor);
copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_LSP_ENTRY, &tlvs->lsp_entries,
&rv->lsp_entries);
copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_REACH,
&tlvs->extended_reach, &rv->extended_reach);
copy_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_REACH, &tlvs->mt_reach,
&rv->mt_reach);
copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH,
&tlvs->oldstyle_ip_reach, &rv->oldstyle_ip_reach);
copy_tlv_protocols_supported(&tlvs->protocols_supported,
&rv->protocols_supported);
copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH_EXT,
&tlvs->oldstyle_ip_reach_ext, &rv->oldstyle_ip_reach_ext);
copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV4_ADDRESS, &tlvs->ipv4_address,
&rv->ipv4_address);
copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_ADDRESS, &tlvs->ipv6_address,
&rv->ipv6_address);
copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_GLOBAL_IPV6_ADDRESS,
&tlvs->global_ipv6_address, &rv->global_ipv6_address);
rv->te_router_id = copy_tlv_te_router_id(tlvs->te_router_id);
rv->te_router_id_ipv6 =
copy_tlv_te_router_id_ipv6(tlvs->te_router_id_ipv6);
copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_IP_REACH,
&tlvs->extended_ip_reach, &rv->extended_ip_reach);
copy_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IP_REACH,
&tlvs->mt_ip_reach, &rv->mt_ip_reach);
rv->hostname = copy_tlv_dynamic_hostname(tlvs->hostname);
copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_REACH, &tlvs->ipv6_reach,
&rv->ipv6_reach);
copy_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IPV6_REACH,
&tlvs->mt_ipv6_reach, &rv->mt_ipv6_reach);
rv->threeway_adj = copy_tlv_threeway_adj(tlvs->threeway_adj);
rv->router_cap = copy_tlv_router_cap(tlvs->router_cap);
rv->spine_leaf = copy_tlv_spine_leaf(tlvs->spine_leaf);
copy_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_SRV6_LOCATOR,
&tlvs->srv6_locator, &rv->srv6_locator);
return rv;
}
static void format_tlvs(struct isis_tlvs *tlvs, struct sbuf *buf, struct json_object *json, int indent)
{
format_tlv_protocols_supported(&tlvs->protocols_supported, buf, json,
indent);
format_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth, buf,
json, indent);
format_tlv_purge_originator(tlvs->purge_originator, buf, json, indent);
format_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES,
&tlvs->area_addresses, buf, json, indent);
if (tlvs->mt_router_info_empty) {
if (json)
json_object_string_add(json, "mt-router-info", "none");
else
sbuf_push(buf, indent, "MT Router Info: None\n");
} else {
format_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO,
&tlvs->mt_router_info, buf, json, indent);
}
format_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_REACH,
&tlvs->oldstyle_reach, buf, json, indent);
format_items(ISIS_CONTEXT_LSP, ISIS_TLV_LAN_NEIGHBORS,
&tlvs->lan_neighbor, buf, json, indent);
format_items(ISIS_CONTEXT_LSP, ISIS_TLV_LSP_ENTRY, &tlvs->lsp_entries,
buf, json, indent);
format_tlv_dynamic_hostname(tlvs->hostname, buf, json, indent);
format_tlv_te_router_id(tlvs->te_router_id, buf, json, indent);
format_tlv_te_router_id_ipv6(tlvs->te_router_id_ipv6, buf, json,
indent);
if (json)
format_tlv_router_cap_json(tlvs->router_cap, json);
else
format_tlv_router_cap(tlvs->router_cap, buf, indent);
format_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_REACH,
&tlvs->extended_reach, buf, json, indent);
format_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_REACH, &tlvs->mt_reach,
buf, json, indent);
format_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH,
&tlvs->oldstyle_ip_reach, buf, json, indent);
format_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH_EXT,
&tlvs->oldstyle_ip_reach_ext, buf, json, indent);
format_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV4_ADDRESS,
&tlvs->ipv4_address, buf, json, indent);
format_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_ADDRESS,
&tlvs->ipv6_address, buf, json, indent);
format_items(ISIS_CONTEXT_LSP, ISIS_TLV_GLOBAL_IPV6_ADDRESS,
&tlvs->global_ipv6_address, buf, json, indent);
format_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_IP_REACH,
&tlvs->extended_ip_reach, buf, json, indent);
format_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IP_REACH,
&tlvs->mt_ip_reach, buf, json, indent);
format_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_REACH, &tlvs->ipv6_reach,
buf, json, indent);
format_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IPV6_REACH,
&tlvs->mt_ipv6_reach, buf, json, indent);
format_tlv_threeway_adj(tlvs->threeway_adj, buf, json, indent);
format_tlv_spine_leaf(tlvs->spine_leaf, buf, json, indent);
format_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_SRV6_LOCATOR,
&tlvs->srv6_locator, buf, json, indent);
}
const char *isis_format_tlvs(struct isis_tlvs *tlvs, struct json_object *json)
{
if (json) {
format_tlvs(tlvs, NULL, json, 0);
return NULL;
} else {
static struct sbuf buf;
if (!sbuf_buf(&buf))
sbuf_init(&buf, NULL, 0);
sbuf_reset(&buf);
format_tlvs(tlvs, &buf, NULL, 0);
return sbuf_buf(&buf);
}
}
void isis_free_tlvs(struct isis_tlvs *tlvs)
{
if (!tlvs)
return;
free_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth);
free_tlv_purge_originator(tlvs->purge_originator);
free_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES,
&tlvs->area_addresses);
free_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO,
&tlvs->mt_router_info);
free_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_REACH,
&tlvs->oldstyle_reach);
free_items(ISIS_CONTEXT_LSP, ISIS_TLV_LAN_NEIGHBORS,
&tlvs->lan_neighbor);
free_items(ISIS_CONTEXT_LSP, ISIS_TLV_LSP_ENTRY, &tlvs->lsp_entries);
free_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_REACH,
&tlvs->extended_reach);
free_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_REACH, &tlvs->mt_reach);
free_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH,
&tlvs->oldstyle_ip_reach);
free_tlv_protocols_supported(&tlvs->protocols_supported);
free_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH_EXT,
&tlvs->oldstyle_ip_reach_ext);
free_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV4_ADDRESS,
&tlvs->ipv4_address);
free_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_ADDRESS,
&tlvs->ipv6_address);
free_items(ISIS_CONTEXT_LSP, ISIS_TLV_GLOBAL_IPV6_ADDRESS,
&tlvs->global_ipv6_address);
free_tlv_te_router_id(tlvs->te_router_id);
free_tlv_te_router_id_ipv6(tlvs->te_router_id_ipv6);
free_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_IP_REACH,
&tlvs->extended_ip_reach);
free_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IP_REACH,
&tlvs->mt_ip_reach);
free_tlv_dynamic_hostname(tlvs->hostname);
free_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_REACH, &tlvs->ipv6_reach);
free_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IPV6_REACH,
&tlvs->mt_ipv6_reach);
free_tlv_threeway_adj(tlvs->threeway_adj);
free_tlv_router_cap(tlvs->router_cap);
free_tlv_spine_leaf(tlvs->spine_leaf);
free_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_SRV6_LOCATOR,
&tlvs->srv6_locator);
XFREE(MTYPE_ISIS_TLV, tlvs);
}
static void add_padding(struct stream *s)
{
while (STREAM_WRITEABLE(s)) {
if (STREAM_WRITEABLE(s) == 1)
break;
uint32_t padding_len = STREAM_WRITEABLE(s) - 2;
if (padding_len > 255) {
if (padding_len == 256)
padding_len = 254;
else
padding_len = 255;
}
stream_putc(s, ISIS_TLV_PADDING);
stream_putc(s, padding_len);
stream_put(s, NULL, padding_len);
}
}
#define LSP_REM_LIFETIME_OFF 10
#define LSP_CHECKSUM_OFF 24
static void safe_auth_md5(struct stream *s, uint16_t *checksum,
uint16_t *rem_lifetime)
{
memcpy(rem_lifetime, STREAM_DATA(s) + LSP_REM_LIFETIME_OFF,
sizeof(*rem_lifetime));
memset(STREAM_DATA(s) + LSP_REM_LIFETIME_OFF, 0, sizeof(*rem_lifetime));
memcpy(checksum, STREAM_DATA(s) + LSP_CHECKSUM_OFF, sizeof(*checksum));
memset(STREAM_DATA(s) + LSP_CHECKSUM_OFF, 0, sizeof(*checksum));
}
static void restore_auth_md5(struct stream *s, uint16_t checksum,
uint16_t rem_lifetime)
{
memcpy(STREAM_DATA(s) + LSP_REM_LIFETIME_OFF, &rem_lifetime,
sizeof(rem_lifetime));
memcpy(STREAM_DATA(s) + LSP_CHECKSUM_OFF, &checksum, sizeof(checksum));
}
static void update_auth_hmac_md5(struct isis_auth *auth, struct stream *s,
bool is_lsp)
{
uint8_t digest[16];
uint16_t checksum, rem_lifetime;
if (is_lsp)
safe_auth_md5(s, &checksum, &rem_lifetime);
memset(STREAM_DATA(s) + auth->offset, 0, 16);
#ifdef CRYPTO_OPENSSL
uint8_t *result = (uint8_t *)HMAC(EVP_md5(), auth->passwd,
auth->plength, STREAM_DATA(s),
stream_get_endp(s), NULL, NULL);
memcpy(digest, result, 16);
#elif CRYPTO_INTERNAL
hmac_md5(STREAM_DATA(s), stream_get_endp(s), auth->passwd,
auth->plength, digest);
#endif
memcpy(auth->value, digest, 16);
memcpy(STREAM_DATA(s) + auth->offset, digest, 16);
if (is_lsp)
restore_auth_md5(s, checksum, rem_lifetime);
}
static void update_auth(struct isis_tlvs *tlvs, struct stream *s, bool is_lsp)
{
struct isis_auth *auth_head = (struct isis_auth *)tlvs->isis_auth.head;
for (struct isis_auth *auth = auth_head; auth; auth = auth->next) {
if (auth->type == ISIS_PASSWD_TYPE_HMAC_MD5)
update_auth_hmac_md5(auth, s, is_lsp);
}
}
static int handle_pack_entry(const struct pack_order_entry *pe,
struct isis_tlvs *tlvs, struct stream *stream,
struct isis_tlvs **fragment_tlvs,
struct isis_tlvs *(*new_fragment)(struct list *l),
struct list *new_fragment_arg)
{
int rv;
if (pe->how_to_pack == ISIS_ITEMS) {
struct isis_item_list *l;
l = (struct isis_item_list *)(((char *)tlvs)
+ pe->what_to_pack);
rv = pack_items(pe->context, pe->type, l, stream, fragment_tlvs,
pe, new_fragment, new_fragment_arg);
} else {
struct isis_mt_item_list *l;
l = (struct isis_mt_item_list *)(((char *)tlvs)
+ pe->what_to_pack);
rv = pack_mt_items(pe->context, pe->type, l, stream,
fragment_tlvs, pe, new_fragment,
new_fragment_arg);
}
return rv;
}
static int pack_tlvs(struct isis_tlvs *tlvs, struct stream *stream,
struct isis_tlvs *fragment_tlvs,
struct isis_tlvs *(*new_fragment)(struct list *l),
struct list *new_fragment_arg)
{
int rv;
/* When fragmenting, don't add auth as it's already accounted for in the
* size we are given. */
if (!fragment_tlvs) {
rv = pack_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH,
&tlvs->isis_auth, stream, NULL, NULL, NULL,
NULL);
if (rv)
return rv;
}
rv = pack_tlv_purge_originator(tlvs->purge_originator, stream);
if (rv)
return rv;
if (fragment_tlvs) {
fragment_tlvs->purge_originator =
copy_tlv_purge_originator(tlvs->purge_originator);
}
rv = pack_tlv_protocols_supported(&tlvs->protocols_supported, stream);
if (rv)
return rv;
if (fragment_tlvs) {
copy_tlv_protocols_supported(
&tlvs->protocols_supported,
&fragment_tlvs->protocols_supported);
}
rv = pack_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES,
&tlvs->area_addresses, stream, NULL, NULL, NULL, NULL);
if (rv)
return rv;
if (fragment_tlvs) {
copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES,
&tlvs->area_addresses,
&fragment_tlvs->area_addresses);
}
if (tlvs->mt_router_info_empty) {
if (STREAM_WRITEABLE(stream) < 2)
return 1;
stream_putc(stream, ISIS_TLV_MT_ROUTER_INFO);
stream_putc(stream, 0);
if (fragment_tlvs)
fragment_tlvs->mt_router_info_empty = true;
} else {
rv = pack_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO,
&tlvs->mt_router_info, stream, NULL, NULL, NULL,
NULL);
if (rv)
return rv;
if (fragment_tlvs) {
copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO,
&tlvs->mt_router_info,
&fragment_tlvs->mt_router_info);
}
}
rv = pack_tlv_dynamic_hostname(tlvs->hostname, stream);
if (rv)
return rv;
if (fragment_tlvs)
fragment_tlvs->hostname =
copy_tlv_dynamic_hostname(tlvs->hostname);
rv = pack_tlv_router_cap(tlvs->router_cap, stream);
if (rv)
return rv;
if (fragment_tlvs) {
fragment_tlvs->router_cap =
copy_tlv_router_cap(tlvs->router_cap);
}
rv = pack_tlv_te_router_id(tlvs->te_router_id, stream);
if (rv)
return rv;
if (fragment_tlvs) {
fragment_tlvs->te_router_id =
copy_tlv_te_router_id(tlvs->te_router_id);
}
rv = pack_tlv_te_router_id_ipv6(tlvs->te_router_id_ipv6, stream);
if (rv)
return rv;
if (fragment_tlvs) {
fragment_tlvs->te_router_id_ipv6 =
copy_tlv_te_router_id_ipv6(tlvs->te_router_id_ipv6);
}
rv = pack_tlv_threeway_adj(tlvs->threeway_adj, stream);
if (rv)
return rv;
if (fragment_tlvs) {
fragment_tlvs->threeway_adj =
copy_tlv_threeway_adj(tlvs->threeway_adj);
}
rv = pack_tlv_spine_leaf(tlvs->spine_leaf, stream);
if (rv)
return rv;
if (fragment_tlvs) {
fragment_tlvs->spine_leaf =
copy_tlv_spine_leaf(tlvs->spine_leaf);
}
for (size_t pack_idx = 0; pack_idx < array_size(pack_order);
pack_idx++) {
rv = handle_pack_entry(&pack_order[pack_idx], tlvs, stream,
fragment_tlvs ? &fragment_tlvs : NULL,
new_fragment, new_fragment_arg);
if (rv)
return rv;
}
return 0;
}
int isis_pack_tlvs(struct isis_tlvs *tlvs, struct stream *stream,
size_t len_pointer, bool pad, bool is_lsp)
{
int rv;
rv = pack_tlvs(tlvs, stream, NULL, NULL, NULL);
if (rv)
return rv;
if (pad)
add_padding(stream);
if (len_pointer != (size_t)-1) {
stream_putw_at(stream, len_pointer, stream_get_endp(stream));
}
update_auth(tlvs, stream, is_lsp);
return 0;
}
static struct isis_tlvs *new_fragment(struct list *l)
{
struct isis_tlvs *rv = isis_alloc_tlvs();
listnode_add(l, rv);
return rv;
}
struct list *isis_fragment_tlvs(struct isis_tlvs *tlvs, size_t size)
{
struct stream *dummy_stream = stream_new(size);
struct list *rv = list_new();
struct isis_tlvs *fragment_tlvs = new_fragment(rv);
if (pack_tlvs(tlvs, dummy_stream, fragment_tlvs, new_fragment, rv)) {
struct listnode *node;
for (ALL_LIST_ELEMENTS_RO(rv, node, fragment_tlvs))
isis_free_tlvs(fragment_tlvs);
list_delete(&rv);
}
stream_free(dummy_stream);
return rv;
}
static int unpack_tlv_unknown(enum isis_tlv_context context, uint8_t tlv_type,
uint8_t tlv_len, struct stream *s,
struct sbuf *log, int indent)
{
stream_forward_getp(s, tlv_len);
sbuf_push(log, indent,
"Skipping unknown TLV %hhu (%hhu bytes)\n",
tlv_type, tlv_len);
return 0;
}
static int unpack_tlv(enum isis_tlv_context context, size_t avail_len,
struct stream *stream, struct sbuf *log, void *dest,
int indent, bool *unpacked_known_tlvs)
{
uint8_t tlv_type, tlv_len;
const struct tlv_ops *ops;
sbuf_push(log, indent, "Unpacking TLV...\n");
if (avail_len < 2) {
sbuf_push(
log, indent + 2,
"Available data %zu too short to contain a TLV header.\n",
avail_len);
return 1;
}
tlv_type = stream_getc(stream);
tlv_len = stream_getc(stream);
sbuf_push(log, indent + 2,
"Found TLV of type %hhu and len %hhu.\n",
tlv_type, tlv_len);
if (avail_len < ((size_t)tlv_len) + 2) {
sbuf_push(log, indent + 2,
"Available data %zu too short for claimed TLV len %hhu.\n",
avail_len - 2, tlv_len);
return 1;
}
ops = tlv_table[context][tlv_type];
if (ops && ops->unpack) {
if (unpacked_known_tlvs)
*unpacked_known_tlvs = true;
return ops->unpack(context, tlv_type, tlv_len, stream, log,
dest, indent + 2);
}
return unpack_tlv_unknown(context, tlv_type, tlv_len, stream, log,
indent + 2);
}
static int unpack_tlvs(enum isis_tlv_context context, size_t avail_len,
struct stream *stream, struct sbuf *log, void *dest,
int indent, bool *unpacked_known_tlvs)
{
int rv;
size_t tlv_start, tlv_pos;
tlv_start = stream_get_getp(stream);
tlv_pos = 0;
sbuf_push(log, indent, "Unpacking %zu bytes of %s...\n", avail_len,
(context == ISIS_CONTEXT_LSP) ? "TLVs" : "sub-TLVs");
while (tlv_pos < avail_len) {
rv = unpack_tlv(context, avail_len - tlv_pos, stream, log, dest,
indent + 2, unpacked_known_tlvs);
if (rv)
return rv;
tlv_pos = stream_get_getp(stream) - tlv_start;
}
return 0;
}
int isis_unpack_tlvs(size_t avail_len, struct stream *stream,
struct isis_tlvs **dest, const char **log)
{
static struct sbuf logbuf;
int indent = 0;
int rv;
struct isis_tlvs *result;
if (!sbuf_buf(&logbuf))
sbuf_init(&logbuf, NULL, 0);
sbuf_reset(&logbuf);
if (avail_len > STREAM_READABLE(stream)) {
sbuf_push(&logbuf, indent,
"Stream doesn't contain sufficient data. Claimed %zu, available %zu\n",
avail_len, STREAM_READABLE(stream));
return 1;
}
result = isis_alloc_tlvs();
rv = unpack_tlvs(ISIS_CONTEXT_LSP, avail_len, stream, &logbuf, result,
indent, NULL);
*log = sbuf_buf(&logbuf);
*dest = result;
return rv;
}
#define TLV_OPS(_name_, _desc_) \
static const struct tlv_ops tlv_##_name_##_ops = { \
.name = _desc_, .unpack = unpack_tlv_##_name_, \
}
#define ITEM_TLV_OPS(_name_, _desc_) \
static const struct tlv_ops tlv_##_name_##_ops = { \
.name = _desc_, \
.unpack = unpack_tlv_with_items, \
\
.pack_item = pack_item_##_name_, \
.free_item = free_item_##_name_, \
.unpack_item = unpack_item_##_name_, \
.format_item = format_item_##_name_, \
.copy_item = copy_item_##_name_}
#define SUBTLV_OPS(_name_, _desc_) \
static const struct tlv_ops subtlv_##_name_##_ops = { \
.name = _desc_, .unpack = unpack_subtlv_##_name_, \
}
#define ITEM_SUBTLV_OPS(_name_, _desc_) \
ITEM_TLV_OPS(_name_, _desc_)
ITEM_TLV_OPS(area_address, "TLV 1 Area Addresses");
ITEM_TLV_OPS(oldstyle_reach, "TLV 2 IS Reachability");
ITEM_TLV_OPS(lan_neighbor, "TLV 6 LAN Neighbors");
ITEM_TLV_OPS(lsp_entry, "TLV 9 LSP Entries");
ITEM_TLV_OPS(auth, "TLV 10 IS-IS Auth");
TLV_OPS(purge_originator, "TLV 13 Purge Originator Identification");
ITEM_TLV_OPS(extended_reach, "TLV 22 Extended Reachability");
ITEM_TLV_OPS(oldstyle_ip_reach, "TLV 128/130 IP Reachability");
TLV_OPS(protocols_supported, "TLV 129 Protocols Supported");
ITEM_TLV_OPS(ipv4_address, "TLV 132 IPv4 Interface Address");
TLV_OPS(te_router_id, "TLV 134 TE Router ID");
ITEM_TLV_OPS(extended_ip_reach, "TLV 135 Extended IP Reachability");
TLV_OPS(dynamic_hostname, "TLV 137 Dynamic Hostname");
TLV_OPS(te_router_id_ipv6, "TLV 140 IPv6 TE Router ID");
TLV_OPS(spine_leaf, "TLV 150 Spine Leaf Extensions");
ITEM_TLV_OPS(mt_router_info, "TLV 229 MT Router Information");
TLV_OPS(threeway_adj, "TLV 240 P2P Three-Way Adjacency");
ITEM_TLV_OPS(ipv6_address, "TLV 232 IPv6 Interface Address");
ITEM_TLV_OPS(global_ipv6_address, "TLV 233 Global IPv6 Interface Address");
ITEM_TLV_OPS(ipv6_reach, "TLV 236 IPv6 Reachability");
TLV_OPS(router_cap, "TLV 242 Router Capability");
ITEM_SUBTLV_OPS(prefix_sid, "Sub-TLV 3 SR Prefix-SID");
SUBTLV_OPS(ipv6_source_prefix, "Sub-TLV 22 IPv6 Source Prefix");
ITEM_TLV_OPS(srv6_locator, "TLV 27 SRv6 Locator");
static const struct tlv_ops *const tlv_table[ISIS_CONTEXT_MAX][ISIS_TLV_MAX] = {
[ISIS_CONTEXT_LSP] = {
[ISIS_TLV_AREA_ADDRESSES] = &tlv_area_address_ops,
[ISIS_TLV_OLDSTYLE_REACH] = &tlv_oldstyle_reach_ops,
[ISIS_TLV_LAN_NEIGHBORS] = &tlv_lan_neighbor_ops,
[ISIS_TLV_LSP_ENTRY] = &tlv_lsp_entry_ops,
[ISIS_TLV_AUTH] = &tlv_auth_ops,
[ISIS_TLV_PURGE_ORIGINATOR] = &tlv_purge_originator_ops,
[ISIS_TLV_EXTENDED_REACH] = &tlv_extended_reach_ops,
[ISIS_TLV_OLDSTYLE_IP_REACH] = &tlv_oldstyle_ip_reach_ops,
[ISIS_TLV_PROTOCOLS_SUPPORTED] = &tlv_protocols_supported_ops,
[ISIS_TLV_OLDSTYLE_IP_REACH_EXT] = &tlv_oldstyle_ip_reach_ops,
[ISIS_TLV_IPV4_ADDRESS] = &tlv_ipv4_address_ops,
[ISIS_TLV_TE_ROUTER_ID] = &tlv_te_router_id_ops,
[ISIS_TLV_TE_ROUTER_ID_IPV6] = &tlv_te_router_id_ipv6_ops,
[ISIS_TLV_EXTENDED_IP_REACH] = &tlv_extended_ip_reach_ops,
[ISIS_TLV_DYNAMIC_HOSTNAME] = &tlv_dynamic_hostname_ops,
[ISIS_TLV_SPINE_LEAF_EXT] = &tlv_spine_leaf_ops,
[ISIS_TLV_MT_REACH] = &tlv_extended_reach_ops,
[ISIS_TLV_MT_ROUTER_INFO] = &tlv_mt_router_info_ops,
[ISIS_TLV_IPV6_ADDRESS] = &tlv_ipv6_address_ops,
[ISIS_TLV_GLOBAL_IPV6_ADDRESS] = &tlv_global_ipv6_address_ops,
[ISIS_TLV_MT_IP_REACH] = &tlv_extended_ip_reach_ops,
[ISIS_TLV_IPV6_REACH] = &tlv_ipv6_reach_ops,
[ISIS_TLV_MT_IPV6_REACH] = &tlv_ipv6_reach_ops,
[ISIS_TLV_THREE_WAY_ADJ] = &tlv_threeway_adj_ops,
[ISIS_TLV_ROUTER_CAPABILITY] = &tlv_router_cap_ops,
[ISIS_TLV_SRV6_LOCATOR] = &tlv_srv6_locator_ops,
},
[ISIS_CONTEXT_SUBTLV_NE_REACH] = {},
[ISIS_CONTEXT_SUBTLV_IP_REACH] = {
[ISIS_SUBTLV_PREFIX_SID] = &tlv_prefix_sid_ops,
},
[ISIS_CONTEXT_SUBTLV_IPV6_REACH] = {
[ISIS_SUBTLV_PREFIX_SID] = &tlv_prefix_sid_ops,
[ISIS_SUBTLV_IPV6_SOURCE_PREFIX] = &subtlv_ipv6_source_prefix_ops,
}
};
/* Accessor functions */
void isis_tlvs_add_auth(struct isis_tlvs *tlvs, struct isis_passwd *passwd)
{
free_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth);
init_item_list(&tlvs->isis_auth);
if (passwd->type == ISIS_PASSWD_TYPE_UNUSED)
return;
struct isis_auth *auth = XCALLOC(MTYPE_ISIS_TLV, sizeof(*auth));
auth->type = passwd->type;
auth->plength = passwd->len;
memcpy(auth->passwd, passwd->passwd,
MIN(sizeof(auth->passwd), sizeof(passwd->passwd)));
if (auth->type == ISIS_PASSWD_TYPE_CLEARTXT) {
auth->length = passwd->len;
memcpy(auth->value, passwd->passwd,
MIN(sizeof(auth->value), sizeof(passwd->passwd)));
}
append_item(&tlvs->isis_auth, (struct isis_item *)auth);
}
void isis_tlvs_add_area_addresses(struct isis_tlvs *tlvs,
struct list *addresses)
{
struct listnode *node;
struct iso_address *area_addr;
for (ALL_LIST_ELEMENTS_RO(addresses, node, area_addr)) {
struct isis_area_address *a =
XCALLOC(MTYPE_ISIS_TLV, sizeof(*a));
a->len = area_addr->addr_len;
memcpy(a->addr, area_addr->area_addr, ISO_ADDR_SIZE);
append_item(&tlvs->area_addresses, (struct isis_item *)a);
}
}
void isis_tlvs_add_lan_neighbors(struct isis_tlvs *tlvs, struct list *neighbors)
{
struct listnode *node;
uint8_t *snpa;
for (ALL_LIST_ELEMENTS_RO(neighbors, node, snpa)) {
struct isis_lan_neighbor *n =
XCALLOC(MTYPE_ISIS_TLV, sizeof(*n));
memcpy(n->mac, snpa, 6);
append_item(&tlvs->lan_neighbor, (struct isis_item *)n);
}
}
void isis_tlvs_set_protocols_supported(struct isis_tlvs *tlvs,
struct nlpids *nlpids)
{
tlvs->protocols_supported.count = nlpids->count;
XFREE(MTYPE_ISIS_TLV, tlvs->protocols_supported.protocols);
if (nlpids->count) {
tlvs->protocols_supported.protocols =
XCALLOC(MTYPE_ISIS_TLV, nlpids->count);
memcpy(tlvs->protocols_supported.protocols, nlpids->nlpids,
nlpids->count);
} else {
tlvs->protocols_supported.protocols = NULL;
}
}
void isis_tlvs_add_mt_router_info(struct isis_tlvs *tlvs, uint16_t mtid,
bool overload, bool attached)
{
struct isis_mt_router_info *i = XCALLOC(MTYPE_ISIS_TLV, sizeof(*i));
i->overload = overload;
i->attached = attached;
i->mtid = mtid;
append_item(&tlvs->mt_router_info, (struct isis_item *)i);
}
void isis_tlvs_add_ipv4_address(struct isis_tlvs *tlvs, struct in_addr *addr)
{
struct isis_ipv4_address *a = XCALLOC(MTYPE_ISIS_TLV, sizeof(*a));
a->addr = *addr;
append_item(&tlvs->ipv4_address, (struct isis_item *)a);
}
void isis_tlvs_add_ipv4_addresses(struct isis_tlvs *tlvs,
struct list *addresses)
{
struct listnode *node;
struct prefix_ipv4 *ip_addr;
unsigned int addr_count = 0;
for (ALL_LIST_ELEMENTS_RO(addresses, node, ip_addr)) {
isis_tlvs_add_ipv4_address(tlvs, &ip_addr->prefix);
addr_count++;
if (addr_count >= 63)
break;
}
}
void isis_tlvs_add_ipv6_addresses(struct isis_tlvs *tlvs,
struct list *addresses)
{
struct listnode *node;
struct prefix_ipv6 *ip_addr;
unsigned int addr_count = 0;
for (ALL_LIST_ELEMENTS_RO(addresses, node, ip_addr)) {
if (addr_count >= 15)
break;
struct isis_ipv6_address *a =
XCALLOC(MTYPE_ISIS_TLV, sizeof(*a));
a->addr = ip_addr->prefix;
append_item(&tlvs->ipv6_address, (struct isis_item *)a);
addr_count++;
}
}
void isis_tlvs_add_global_ipv6_addresses(struct isis_tlvs *tlvs,
struct list *addresses)
{
struct listnode *node;
struct prefix_ipv6 *ip_addr;
unsigned int addr_count = 0;
for (ALL_LIST_ELEMENTS_RO(addresses, node, ip_addr)) {
if (addr_count >= 15)
break;
struct isis_ipv6_address *a =
XCALLOC(MTYPE_ISIS_TLV, sizeof(*a));
a->addr = ip_addr->prefix;
append_item(&tlvs->global_ipv6_address, (struct isis_item *)a);
addr_count++;
}
}
typedef bool (*auth_validator_func)(struct isis_passwd *passwd,
struct stream *stream,
struct isis_auth *auth, bool is_lsp);
static bool auth_validator_cleartxt(struct isis_passwd *passwd,
struct stream *stream,
struct isis_auth *auth, bool is_lsp)
{
return (auth->length == passwd->len
&& !memcmp(auth->value, passwd->passwd, passwd->len));
}
static bool auth_validator_hmac_md5(struct isis_passwd *passwd,
struct stream *stream,
struct isis_auth *auth, bool is_lsp)
{
uint8_t digest[16];
uint16_t checksum;
uint16_t rem_lifetime;
if (is_lsp)
safe_auth_md5(stream, &checksum, &rem_lifetime);
memset(STREAM_DATA(stream) + auth->offset, 0, 16);
#ifdef CRYPTO_OPENSSL
uint8_t *result = (uint8_t *)HMAC(EVP_md5(), passwd->passwd,
passwd->len, STREAM_DATA(stream),
stream_get_endp(stream), NULL, NULL);
memcpy(digest, result, 16);
#elif CRYPTO_INTERNAL
hmac_md5(STREAM_DATA(stream), stream_get_endp(stream), passwd->passwd,
passwd->len, digest);
#endif
memcpy(STREAM_DATA(stream) + auth->offset, auth->value, 16);
bool rv = !memcmp(digest, auth->value, 16);
if (is_lsp)
restore_auth_md5(stream, checksum, rem_lifetime);
return rv;
}
static const auth_validator_func auth_validators[] = {
[ISIS_PASSWD_TYPE_CLEARTXT] = auth_validator_cleartxt,
[ISIS_PASSWD_TYPE_HMAC_MD5] = auth_validator_hmac_md5,
};
int isis_tlvs_auth_is_valid(struct isis_tlvs *tlvs, struct isis_passwd *passwd,
struct stream *stream, bool is_lsp)
{
/* If no auth is set, always pass authentication */
if (!passwd->type)
return ISIS_AUTH_OK;
/* If we don't known how to validate the auth, return invalid */
if (passwd->type >= array_size(auth_validators)
|| !auth_validators[passwd->type])
return ISIS_AUTH_NO_VALIDATOR;
struct isis_auth *auth_head = (struct isis_auth *)tlvs->isis_auth.head;
struct isis_auth *auth;
for (auth = auth_head; auth; auth = auth->next) {
if (auth->type == passwd->type)
break;
}
/* If matching auth TLV could not be found, return invalid */
if (!auth)
return ISIS_AUTH_TYPE_FAILURE;
/* Perform validation and return result */
if (auth_validators[passwd->type](passwd, stream, auth, is_lsp))
return ISIS_AUTH_OK;
else
return ISIS_AUTH_FAILURE;
}
bool isis_tlvs_area_addresses_match(struct isis_tlvs *tlvs,
struct list *addresses)
{
struct isis_area_address *addr_head;
addr_head = (struct isis_area_address *)tlvs->area_addresses.head;
for (struct isis_area_address *addr = addr_head; addr;
addr = addr->next) {
struct listnode *node;
struct iso_address *a;
for (ALL_LIST_ELEMENTS_RO(addresses, node, a)) {
if (a->addr_len == addr->len
&& !memcmp(a->area_addr, addr->addr, addr->len))
return true;
}
}
return false;
}
static void tlvs_area_addresses_to_adj(struct isis_tlvs *tlvs,
struct isis_adjacency *adj,
bool *changed)
{
if (adj->area_address_count != tlvs->area_addresses.count) {
isisd: Fix usage of uninited memory valgrind is showing a usage of uninited memory: ==935465== Conditional jump or move depends on uninitialised value(s) ==935465== at 0x159E17: tlvs_area_addresses_to_adj (isis_tlvs.c:4430) ==935465== by 0x15A4BD: isis_tlvs_to_adj (isis_tlvs.c:4568) ==935465== by 0x1377F0: process_p2p_hello (isis_pdu.c:203) ==935465== by 0x1391FD: process_hello (isis_pdu.c:781) ==935465== by 0x13BDBE: isis_handle_pdu (isis_pdu.c:1700) ==935465== by 0x13BECD: isis_receive (isis_pdu.c:1744) ==935465== by 0x49210FF: thread_call (thread.c:1585) ==935465== by 0x48CFACB: frr_run (libfrr.c:1099) ==935465== by 0x1218C9: main (isis_main.c:272) ==935465== ==935465== Conditional jump or move depends on uninitialised value(s) ==935465== at 0x483EEC5: bcmp (vg_replace_strmem.c:1111) ==935465== by 0x15A290: tlvs_ipv4_addresses_to_adj (isis_tlvs.c:4512) ==935465== by 0x15A4EB: isis_tlvs_to_adj (isis_tlvs.c:4570) ==935465== by 0x1377F0: process_p2p_hello (isis_pdu.c:203) ==935465== by 0x1391FD: process_hello (isis_pdu.c:781) ==935465== by 0x13BDBE: isis_handle_pdu (isis_pdu.c:1700) ==935465== by 0x13BECD: isis_receive (isis_pdu.c:1744) ==935465== by 0x49210FF: thread_call (thread.c:1585) ==935465== by 0x48CFACB: frr_run (libfrr.c:1099) ==935465== by 0x1218C9: main (isis_main.c:272) Effectively we are reallocing memory to hold data. realloc does not set the new memory to anything. So whatever happens to be in the memory is what is there. after the realloc happens we are iterating over the memory just realloced and doing memcmp's to values in it causing these use of uninitialized memory. Signed-off-by: Donald Sharp <sharpd@nvidia.com>
2020-10-27 14:59:10 +01:00
uint32_t oc = adj->area_address_count;
*changed = true;
adj->area_address_count = tlvs->area_addresses.count;
adj->area_addresses = XREALLOC(
MTYPE_ISIS_ADJACENCY_INFO, adj->area_addresses,
adj->area_address_count * sizeof(*adj->area_addresses));
isisd: Fix usage of uninited memory valgrind is showing a usage of uninited memory: ==935465== Conditional jump or move depends on uninitialised value(s) ==935465== at 0x159E17: tlvs_area_addresses_to_adj (isis_tlvs.c:4430) ==935465== by 0x15A4BD: isis_tlvs_to_adj (isis_tlvs.c:4568) ==935465== by 0x1377F0: process_p2p_hello (isis_pdu.c:203) ==935465== by 0x1391FD: process_hello (isis_pdu.c:781) ==935465== by 0x13BDBE: isis_handle_pdu (isis_pdu.c:1700) ==935465== by 0x13BECD: isis_receive (isis_pdu.c:1744) ==935465== by 0x49210FF: thread_call (thread.c:1585) ==935465== by 0x48CFACB: frr_run (libfrr.c:1099) ==935465== by 0x1218C9: main (isis_main.c:272) ==935465== ==935465== Conditional jump or move depends on uninitialised value(s) ==935465== at 0x483EEC5: bcmp (vg_replace_strmem.c:1111) ==935465== by 0x15A290: tlvs_ipv4_addresses_to_adj (isis_tlvs.c:4512) ==935465== by 0x15A4EB: isis_tlvs_to_adj (isis_tlvs.c:4570) ==935465== by 0x1377F0: process_p2p_hello (isis_pdu.c:203) ==935465== by 0x1391FD: process_hello (isis_pdu.c:781) ==935465== by 0x13BDBE: isis_handle_pdu (isis_pdu.c:1700) ==935465== by 0x13BECD: isis_receive (isis_pdu.c:1744) ==935465== by 0x49210FF: thread_call (thread.c:1585) ==935465== by 0x48CFACB: frr_run (libfrr.c:1099) ==935465== by 0x1218C9: main (isis_main.c:272) Effectively we are reallocing memory to hold data. realloc does not set the new memory to anything. So whatever happens to be in the memory is what is there. after the realloc happens we are iterating over the memory just realloced and doing memcmp's to values in it causing these use of uninitialized memory. Signed-off-by: Donald Sharp <sharpd@nvidia.com>
2020-10-27 14:59:10 +01:00
for (; oc < adj->area_address_count; oc++) {
adj->area_addresses[oc].addr_len = 0;
memset(&adj->area_addresses[oc].area_addr, 0,
sizeof(adj->area_addresses[oc].area_addr));
}
}
struct isis_area_address *addr = NULL;
for (unsigned int i = 0; i < tlvs->area_addresses.count; i++) {
if (!addr)
addr = (struct isis_area_address *)
tlvs->area_addresses.head;
else
addr = addr->next;
if (adj->area_addresses[i].addr_len == addr->len
&& !memcmp(adj->area_addresses[i].area_addr, addr->addr,
addr->len)) {
continue;
}
*changed = true;
adj->area_addresses[i].addr_len = addr->len;
memcpy(adj->area_addresses[i].area_addr, addr->addr, addr->len);
}
}
static void tlvs_protocols_supported_to_adj(struct isis_tlvs *tlvs,
struct isis_adjacency *adj,
bool *changed)
{
bool ipv4_supported = false, ipv6_supported = false;
for (uint8_t i = 0; i < tlvs->protocols_supported.count; i++) {
if (tlvs->protocols_supported.protocols[i] == NLPID_IP)
ipv4_supported = true;
if (tlvs->protocols_supported.protocols[i] == NLPID_IPV6)
ipv6_supported = true;
}
struct nlpids reduced = {};
if (ipv4_supported && ipv6_supported) {
reduced.count = 2;
reduced.nlpids[0] = NLPID_IP;
reduced.nlpids[1] = NLPID_IPV6;
} else if (ipv4_supported) {
reduced.count = 1;
reduced.nlpids[0] = NLPID_IP;
} else if (ipv6_supported) {
reduced.count = 1;
reduced.nlpids[0] = NLPID_IPV6;
} else {
reduced.count = 0;
}
if (adj->nlpids.count == reduced.count
&& !memcmp(adj->nlpids.nlpids, reduced.nlpids, reduced.count))
return;
*changed = true;
adj->nlpids.count = reduced.count;
memcpy(adj->nlpids.nlpids, reduced.nlpids, reduced.count);
}
DEFINE_HOOK(isis_adj_ip_enabled_hook,
(struct isis_adjacency * adj, int family, bool global),
(adj, family, global));
DEFINE_HOOK(isis_adj_ip_disabled_hook,
(struct isis_adjacency * adj, int family, bool global),
(adj, family, global));
static void tlvs_ipv4_addresses_to_adj(struct isis_tlvs *tlvs,
struct isis_adjacency *adj,
bool *changed)
{
bool ipv4_enabled = false;
if (adj->ipv4_address_count == 0 && tlvs->ipv4_address.count > 0)
ipv4_enabled = true;
else if (adj->ipv4_address_count > 0 && tlvs->ipv4_address.count == 0)
hook_call(isis_adj_ip_disabled_hook, adj, AF_INET, false);
if (adj->ipv4_address_count != tlvs->ipv4_address.count) {
isisd: Fix usage of uninited memory valgrind is showing a usage of uninited memory: ==935465== Conditional jump or move depends on uninitialised value(s) ==935465== at 0x159E17: tlvs_area_addresses_to_adj (isis_tlvs.c:4430) ==935465== by 0x15A4BD: isis_tlvs_to_adj (isis_tlvs.c:4568) ==935465== by 0x1377F0: process_p2p_hello (isis_pdu.c:203) ==935465== by 0x1391FD: process_hello (isis_pdu.c:781) ==935465== by 0x13BDBE: isis_handle_pdu (isis_pdu.c:1700) ==935465== by 0x13BECD: isis_receive (isis_pdu.c:1744) ==935465== by 0x49210FF: thread_call (thread.c:1585) ==935465== by 0x48CFACB: frr_run (libfrr.c:1099) ==935465== by 0x1218C9: main (isis_main.c:272) ==935465== ==935465== Conditional jump or move depends on uninitialised value(s) ==935465== at 0x483EEC5: bcmp (vg_replace_strmem.c:1111) ==935465== by 0x15A290: tlvs_ipv4_addresses_to_adj (isis_tlvs.c:4512) ==935465== by 0x15A4EB: isis_tlvs_to_adj (isis_tlvs.c:4570) ==935465== by 0x1377F0: process_p2p_hello (isis_pdu.c:203) ==935465== by 0x1391FD: process_hello (isis_pdu.c:781) ==935465== by 0x13BDBE: isis_handle_pdu (isis_pdu.c:1700) ==935465== by 0x13BECD: isis_receive (isis_pdu.c:1744) ==935465== by 0x49210FF: thread_call (thread.c:1585) ==935465== by 0x48CFACB: frr_run (libfrr.c:1099) ==935465== by 0x1218C9: main (isis_main.c:272) Effectively we are reallocing memory to hold data. realloc does not set the new memory to anything. So whatever happens to be in the memory is what is there. after the realloc happens we are iterating over the memory just realloced and doing memcmp's to values in it causing these use of uninitialized memory. Signed-off-by: Donald Sharp <sharpd@nvidia.com>
2020-10-27 14:59:10 +01:00
uint32_t oc = adj->ipv4_address_count;
*changed = true;
adj->ipv4_address_count = tlvs->ipv4_address.count;
adj->ipv4_addresses = XREALLOC(
MTYPE_ISIS_ADJACENCY_INFO, adj->ipv4_addresses,
adj->ipv4_address_count * sizeof(*adj->ipv4_addresses));
isisd: Fix usage of uninited memory valgrind is showing a usage of uninited memory: ==935465== Conditional jump or move depends on uninitialised value(s) ==935465== at 0x159E17: tlvs_area_addresses_to_adj (isis_tlvs.c:4430) ==935465== by 0x15A4BD: isis_tlvs_to_adj (isis_tlvs.c:4568) ==935465== by 0x1377F0: process_p2p_hello (isis_pdu.c:203) ==935465== by 0x1391FD: process_hello (isis_pdu.c:781) ==935465== by 0x13BDBE: isis_handle_pdu (isis_pdu.c:1700) ==935465== by 0x13BECD: isis_receive (isis_pdu.c:1744) ==935465== by 0x49210FF: thread_call (thread.c:1585) ==935465== by 0x48CFACB: frr_run (libfrr.c:1099) ==935465== by 0x1218C9: main (isis_main.c:272) ==935465== ==935465== Conditional jump or move depends on uninitialised value(s) ==935465== at 0x483EEC5: bcmp (vg_replace_strmem.c:1111) ==935465== by 0x15A290: tlvs_ipv4_addresses_to_adj (isis_tlvs.c:4512) ==935465== by 0x15A4EB: isis_tlvs_to_adj (isis_tlvs.c:4570) ==935465== by 0x1377F0: process_p2p_hello (isis_pdu.c:203) ==935465== by 0x1391FD: process_hello (isis_pdu.c:781) ==935465== by 0x13BDBE: isis_handle_pdu (isis_pdu.c:1700) ==935465== by 0x13BECD: isis_receive (isis_pdu.c:1744) ==935465== by 0x49210FF: thread_call (thread.c:1585) ==935465== by 0x48CFACB: frr_run (libfrr.c:1099) ==935465== by 0x1218C9: main (isis_main.c:272) Effectively we are reallocing memory to hold data. realloc does not set the new memory to anything. So whatever happens to be in the memory is what is there. after the realloc happens we are iterating over the memory just realloced and doing memcmp's to values in it causing these use of uninitialized memory. Signed-off-by: Donald Sharp <sharpd@nvidia.com>
2020-10-27 14:59:10 +01:00
for (; oc < adj->ipv4_address_count; oc++) {
memset(&adj->ipv4_addresses[oc], 0,
sizeof(adj->ipv4_addresses[oc]));
}
}
struct isis_ipv4_address *addr = NULL;
for (unsigned int i = 0; i < tlvs->ipv4_address.count; i++) {
if (!addr)
addr = (struct isis_ipv4_address *)
tlvs->ipv4_address.head;
else
addr = addr->next;
if (!memcmp(&adj->ipv4_addresses[i], &addr->addr,
sizeof(addr->addr)))
continue;
*changed = true;
adj->ipv4_addresses[i] = addr->addr;
}
if (ipv4_enabled)
hook_call(isis_adj_ip_enabled_hook, adj, AF_INET, false);
}
static void tlvs_ipv6_addresses_to_adj(struct isis_tlvs *tlvs,
struct isis_adjacency *adj,
bool *changed)
{
bool ipv6_enabled = false;
if (adj->ll_ipv6_count == 0 && tlvs->ipv6_address.count > 0)
ipv6_enabled = true;
else if (adj->ll_ipv6_count > 0 && tlvs->ipv6_address.count == 0)
hook_call(isis_adj_ip_disabled_hook, adj, AF_INET6, false);
if (adj->ll_ipv6_count != tlvs->ipv6_address.count) {
uint32_t oc = adj->ll_ipv6_count;
isisd: Fix usage of uninited memory valgrind is showing a usage of uninited memory: ==935465== Conditional jump or move depends on uninitialised value(s) ==935465== at 0x159E17: tlvs_area_addresses_to_adj (isis_tlvs.c:4430) ==935465== by 0x15A4BD: isis_tlvs_to_adj (isis_tlvs.c:4568) ==935465== by 0x1377F0: process_p2p_hello (isis_pdu.c:203) ==935465== by 0x1391FD: process_hello (isis_pdu.c:781) ==935465== by 0x13BDBE: isis_handle_pdu (isis_pdu.c:1700) ==935465== by 0x13BECD: isis_receive (isis_pdu.c:1744) ==935465== by 0x49210FF: thread_call (thread.c:1585) ==935465== by 0x48CFACB: frr_run (libfrr.c:1099) ==935465== by 0x1218C9: main (isis_main.c:272) ==935465== ==935465== Conditional jump or move depends on uninitialised value(s) ==935465== at 0x483EEC5: bcmp (vg_replace_strmem.c:1111) ==935465== by 0x15A290: tlvs_ipv4_addresses_to_adj (isis_tlvs.c:4512) ==935465== by 0x15A4EB: isis_tlvs_to_adj (isis_tlvs.c:4570) ==935465== by 0x1377F0: process_p2p_hello (isis_pdu.c:203) ==935465== by 0x1391FD: process_hello (isis_pdu.c:781) ==935465== by 0x13BDBE: isis_handle_pdu (isis_pdu.c:1700) ==935465== by 0x13BECD: isis_receive (isis_pdu.c:1744) ==935465== by 0x49210FF: thread_call (thread.c:1585) ==935465== by 0x48CFACB: frr_run (libfrr.c:1099) ==935465== by 0x1218C9: main (isis_main.c:272) Effectively we are reallocing memory to hold data. realloc does not set the new memory to anything. So whatever happens to be in the memory is what is there. after the realloc happens we are iterating over the memory just realloced and doing memcmp's to values in it causing these use of uninitialized memory. Signed-off-by: Donald Sharp <sharpd@nvidia.com>
2020-10-27 14:59:10 +01:00
*changed = true;
adj->ll_ipv6_count = tlvs->ipv6_address.count;
adj->ll_ipv6_addrs = XREALLOC(
MTYPE_ISIS_ADJACENCY_INFO, adj->ll_ipv6_addrs,
adj->ll_ipv6_count * sizeof(*adj->ll_ipv6_addrs));
for (; oc < adj->ll_ipv6_count; oc++) {
memset(&adj->ll_ipv6_addrs[oc], 0,
sizeof(adj->ll_ipv6_addrs[oc]));
isisd: Fix usage of uninited memory valgrind is showing a usage of uninited memory: ==935465== Conditional jump or move depends on uninitialised value(s) ==935465== at 0x159E17: tlvs_area_addresses_to_adj (isis_tlvs.c:4430) ==935465== by 0x15A4BD: isis_tlvs_to_adj (isis_tlvs.c:4568) ==935465== by 0x1377F0: process_p2p_hello (isis_pdu.c:203) ==935465== by 0x1391FD: process_hello (isis_pdu.c:781) ==935465== by 0x13BDBE: isis_handle_pdu (isis_pdu.c:1700) ==935465== by 0x13BECD: isis_receive (isis_pdu.c:1744) ==935465== by 0x49210FF: thread_call (thread.c:1585) ==935465== by 0x48CFACB: frr_run (libfrr.c:1099) ==935465== by 0x1218C9: main (isis_main.c:272) ==935465== ==935465== Conditional jump or move depends on uninitialised value(s) ==935465== at 0x483EEC5: bcmp (vg_replace_strmem.c:1111) ==935465== by 0x15A290: tlvs_ipv4_addresses_to_adj (isis_tlvs.c:4512) ==935465== by 0x15A4EB: isis_tlvs_to_adj (isis_tlvs.c:4570) ==935465== by 0x1377F0: process_p2p_hello (isis_pdu.c:203) ==935465== by 0x1391FD: process_hello (isis_pdu.c:781) ==935465== by 0x13BDBE: isis_handle_pdu (isis_pdu.c:1700) ==935465== by 0x13BECD: isis_receive (isis_pdu.c:1744) ==935465== by 0x49210FF: thread_call (thread.c:1585) ==935465== by 0x48CFACB: frr_run (libfrr.c:1099) ==935465== by 0x1218C9: main (isis_main.c:272) Effectively we are reallocing memory to hold data. realloc does not set the new memory to anything. So whatever happens to be in the memory is what is there. after the realloc happens we are iterating over the memory just realloced and doing memcmp's to values in it causing these use of uninitialized memory. Signed-off-by: Donald Sharp <sharpd@nvidia.com>
2020-10-27 14:59:10 +01:00
}
}
struct isis_ipv6_address *addr = NULL;
for (unsigned int i = 0; i < tlvs->ipv6_address.count; i++) {
if (!addr)
addr = (struct isis_ipv6_address *)
tlvs->ipv6_address.head;
else
addr = addr->next;
if (!memcmp(&adj->ll_ipv6_addrs[i], &addr->addr,
sizeof(addr->addr)))
continue;
*changed = true;
adj->ll_ipv6_addrs[i] = addr->addr;
}
if (ipv6_enabled)
hook_call(isis_adj_ip_enabled_hook, adj, AF_INET6, false);
}
static void tlvs_global_ipv6_addresses_to_adj(struct isis_tlvs *tlvs,
struct isis_adjacency *adj,
bool *changed)
{
bool global_ipv6_enabled = false;
if (adj->global_ipv6_count == 0 && tlvs->global_ipv6_address.count > 0)
global_ipv6_enabled = true;
else if (adj->global_ipv6_count > 0
&& tlvs->global_ipv6_address.count == 0)
hook_call(isis_adj_ip_disabled_hook, adj, AF_INET6, true);
if (adj->global_ipv6_count != tlvs->global_ipv6_address.count) {
uint32_t oc = adj->global_ipv6_count;
*changed = true;
adj->global_ipv6_count = tlvs->global_ipv6_address.count;
adj->global_ipv6_addrs = XREALLOC(
MTYPE_ISIS_ADJACENCY_INFO, adj->global_ipv6_addrs,
adj->global_ipv6_count
* sizeof(*adj->global_ipv6_addrs));
for (; oc < adj->global_ipv6_count; oc++) {
memset(&adj->global_ipv6_addrs[oc], 0,
sizeof(adj->global_ipv6_addrs[oc]));
}
}
struct isis_ipv6_address *addr = NULL;
for (unsigned int i = 0; i < tlvs->global_ipv6_address.count; i++) {
if (!addr)
addr = (struct isis_ipv6_address *)
tlvs->global_ipv6_address.head;
else
addr = addr->next;
if (!memcmp(&adj->global_ipv6_addrs[i], &addr->addr,
sizeof(addr->addr)))
continue;
*changed = true;
adj->global_ipv6_addrs[i] = addr->addr;
}
if (global_ipv6_enabled)
hook_call(isis_adj_ip_enabled_hook, adj, AF_INET6, true);
}
void isis_tlvs_to_adj(struct isis_tlvs *tlvs, struct isis_adjacency *adj,
bool *changed)
{
*changed = false;
tlvs_area_addresses_to_adj(tlvs, adj, changed);
tlvs_protocols_supported_to_adj(tlvs, adj, changed);
tlvs_ipv4_addresses_to_adj(tlvs, adj, changed);
tlvs_ipv6_addresses_to_adj(tlvs, adj, changed);
tlvs_global_ipv6_addresses_to_adj(tlvs, adj, changed);
}
bool isis_tlvs_own_snpa_found(struct isis_tlvs *tlvs, uint8_t *snpa)
{
struct isis_lan_neighbor *ne_head;
ne_head = (struct isis_lan_neighbor *)tlvs->lan_neighbor.head;
for (struct isis_lan_neighbor *ne = ne_head; ne; ne = ne->next) {
if (!memcmp(ne->mac, snpa, ETH_ALEN))
return true;
}
return false;
}
void isis_tlvs_add_lsp_entry(struct isis_tlvs *tlvs, struct isis_lsp *lsp)
{
struct isis_lsp_entry *entry = XCALLOC(MTYPE_ISIS_TLV, sizeof(*entry));
entry->rem_lifetime = lsp->hdr.rem_lifetime;
memcpy(entry->id, lsp->hdr.lsp_id, ISIS_SYS_ID_LEN + 2);
entry->checksum = lsp->hdr.checksum;
entry->seqno = lsp->hdr.seqno;
entry->lsp = lsp;
append_item(&tlvs->lsp_entries, (struct isis_item *)entry);
}
void isis_tlvs_add_csnp_entries(struct isis_tlvs *tlvs, uint8_t *start_id,
uint8_t *stop_id, uint16_t num_lsps,
struct lspdb_head *head,
struct isis_lsp **last_lsp)
{
struct isis_lsp searchfor;
struct isis_lsp *first, *lsp;
memcpy(&searchfor.hdr.lsp_id, start_id, sizeof(searchfor.hdr.lsp_id));
first = lspdb_find_gteq(head, &searchfor);
if (!first)
return;
frr_each_from (lspdb, head, lsp, first) {
if (memcmp(lsp->hdr.lsp_id, stop_id, sizeof(lsp->hdr.lsp_id))
> 0 || tlvs->lsp_entries.count == num_lsps)
break;
isis_tlvs_add_lsp_entry(tlvs, lsp);
*last_lsp = lsp;
}
}
void isis_tlvs_set_dynamic_hostname(struct isis_tlvs *tlvs,
const char *hostname)
{
XFREE(MTYPE_ISIS_TLV, tlvs->hostname);
if (hostname)
tlvs->hostname = XSTRDUP(MTYPE_ISIS_TLV, hostname);
}
/* Init Router Capability TLV parameters */
struct isis_router_cap *isis_tlvs_init_router_capability(struct isis_tlvs *tlvs)
{
tlvs->router_cap = XCALLOC(MTYPE_ISIS_TLV, sizeof(*tlvs->router_cap));
/* init SR algo list content to the default value */
for (int i = 0; i < SR_ALGORITHM_COUNT; i++)
tlvs->router_cap->algo[i] = SR_ALGORITHM_UNSET;
return tlvs->router_cap;
}
#ifndef FABRICD
void isis_tlvs_set_router_capability_fad(struct isis_tlvs *tlvs,
struct flex_algo *fa, int algorithm,
uint8_t *sysid)
{
struct isis_router_cap_fad *rcap_fad;
assert(tlvs->router_cap);
rcap_fad = tlvs->router_cap->fads[algorithm];
if (!rcap_fad)
rcap_fad = XCALLOC(MTYPE_ISIS_TLV,
sizeof(struct isis_router_cap_fad));
memset(rcap_fad->sysid, 0, ISIS_SYS_ID_LEN + 2);
memcpy(rcap_fad->sysid, sysid, ISIS_SYS_ID_LEN);
memcpy(&rcap_fad->fad, fa, sizeof(struct flex_algo));
rcap_fad->fad.admin_group_exclude_any.bitmap.data = NULL;
rcap_fad->fad.admin_group_include_any.bitmap.data = NULL;
rcap_fad->fad.admin_group_include_all.bitmap.data = NULL;
admin_group_copy(&rcap_fad->fad.admin_group_exclude_any,
&fa->admin_group_exclude_any);
admin_group_copy(&rcap_fad->fad.admin_group_include_any,
&fa->admin_group_include_any);
admin_group_copy(&rcap_fad->fad.admin_group_include_all,
&fa->admin_group_include_all);
tlvs->router_cap->fads[algorithm] = rcap_fad;
}
#endif /* ifndef FABRICD */
int isis_tlvs_sr_algo_count(const struct isis_router_cap *cap)
{
int count = 0;
for (int i = 0; i < SR_ALGORITHM_COUNT; i++)
if (cap->algo[i] != SR_ALGORITHM_UNSET)
count++;
return count;
}
void isis_tlvs_set_te_router_id(struct isis_tlvs *tlvs,
const struct in_addr *id)
{
XFREE(MTYPE_ISIS_TLV, tlvs->te_router_id);
if (!id)
return;
tlvs->te_router_id = XCALLOC(MTYPE_ISIS_TLV, sizeof(*id));
memcpy(tlvs->te_router_id, id, sizeof(*id));
}
void isis_tlvs_set_te_router_id_ipv6(struct isis_tlvs *tlvs,
const struct in6_addr *id)
{
XFREE(MTYPE_ISIS_TLV, tlvs->te_router_id_ipv6);
if (!id)
return;
tlvs->te_router_id_ipv6 = XCALLOC(MTYPE_ISIS_TLV, sizeof(*id));
memcpy(tlvs->te_router_id_ipv6, id, sizeof(*id));
}
void isis_tlvs_add_oldstyle_ip_reach(struct isis_tlvs *tlvs,
struct prefix_ipv4 *dest, uint8_t metric)
{
struct isis_oldstyle_ip_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
r->metric = metric;
memcpy(&r->prefix, dest, sizeof(*dest));
apply_mask_ipv4(&r->prefix);
append_item(&tlvs->oldstyle_ip_reach, (struct isis_item *)r);
}
/* Add IS-IS SR Adjacency-SID subTLVs */
void isis_tlvs_add_adj_sid(struct isis_ext_subtlvs *exts,
struct isis_adj_sid *adj)
{
append_item(&exts->adj_sid, (struct isis_item *)adj);
SET_SUBTLV(exts, EXT_ADJ_SID);
}
/* Delete IS-IS SR Adjacency-SID subTLVs */
void isis_tlvs_del_adj_sid(struct isis_ext_subtlvs *exts,
struct isis_adj_sid *adj)
{
delete_item(&exts->adj_sid, (struct isis_item *)adj);
XFREE(MTYPE_ISIS_SUBTLV, adj);
if (exts->adj_sid.count == 0)
UNSET_SUBTLV(exts, EXT_ADJ_SID);
}
/* Add IS-IS SR LAN-Adjacency-SID subTLVs */
void isis_tlvs_add_lan_adj_sid(struct isis_ext_subtlvs *exts,
struct isis_lan_adj_sid *lan)
{
append_item(&exts->lan_sid, (struct isis_item *)lan);
SET_SUBTLV(exts, EXT_LAN_ADJ_SID);
}
/* Delete IS-IS SR LAN-Adjacency-SID subTLVs */
void isis_tlvs_del_lan_adj_sid(struct isis_ext_subtlvs *exts,
struct isis_lan_adj_sid *lan)
{
delete_item(&exts->lan_sid, (struct isis_item *)lan);
XFREE(MTYPE_ISIS_SUBTLV, lan);
if (exts->lan_sid.count == 0)
UNSET_SUBTLV(exts, EXT_LAN_ADJ_SID);
}
void isis_tlvs_del_asla_flex_algo(struct isis_ext_subtlvs *ext,
struct isis_asla_subtlvs *asla)
{
admin_group_term(&asla->ext_admin_group);
listnode_delete(ext->aslas, asla);
XFREE(MTYPE_ISIS_SUBTLV, asla);
}
struct isis_asla_subtlvs *
isis_tlvs_find_alloc_asla(struct isis_ext_subtlvs *ext, uint8_t standard_apps)
{
struct isis_asla_subtlvs *asla;
struct listnode *node;
if (!list_isempty(ext->aslas)) {
for (ALL_LIST_ELEMENTS_RO(ext->aslas, node, asla)) {
if (CHECK_FLAG(asla->standard_apps, standard_apps))
return asla;
}
}
asla = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(struct isis_asla_subtlvs));
admin_group_init(&asla->ext_admin_group);
SET_FLAG(asla->standard_apps, standard_apps);
SET_FLAG(asla->user_def_apps, standard_apps);
asla->standard_apps_length = ASLA_APP_IDENTIFIER_BIT_LENGTH;
asla->user_def_apps_length = ASLA_APP_IDENTIFIER_BIT_LENGTH;
listnode_add(ext->aslas, asla);
return asla;
}
void isis_tlvs_free_asla(struct isis_ext_subtlvs *ext, uint8_t standard_apps)
{
struct isis_asla_subtlvs *asla;
struct listnode *node;
if (!ext)
return;
for (ALL_LIST_ELEMENTS_RO(ext->aslas, node, asla)) {
if (!CHECK_FLAG(asla->standard_apps, standard_apps))
continue;
isis_tlvs_del_asla_flex_algo(ext, asla);
break;
}
}
void isis_tlvs_add_extended_ip_reach(struct isis_tlvs *tlvs,
struct prefix_ipv4 *dest, uint32_t metric,
bool external,
struct sr_prefix_cfg **pcfgs)
{
struct isis_extended_ip_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
r->metric = metric;
memcpy(&r->prefix, dest, sizeof(*dest));
apply_mask_ipv4(&r->prefix);
if (pcfgs) {
r->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IP_REACH);
for (int i = 0; i < SR_ALGORITHM_COUNT; i++) {
struct isis_prefix_sid *psid;
struct sr_prefix_cfg *pcfg = pcfgs[i];
if (!pcfg)
continue;
psid = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*psid));
isis_sr_prefix_cfg2subtlv(pcfg, external, psid);
append_item(&r->subtlvs->prefix_sids,
(struct isis_item *)psid);
}
}
append_item(&tlvs->extended_ip_reach, (struct isis_item *)r);
}
void isis_tlvs_add_ipv6_reach(struct isis_tlvs *tlvs, uint16_t mtid,
struct prefix_ipv6 *dest, uint32_t metric,
bool external, struct sr_prefix_cfg **pcfgs)
{
struct isis_ipv6_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
r->metric = metric;
memcpy(&r->prefix, dest, sizeof(*dest));
apply_mask_ipv6(&r->prefix);
if (pcfgs) {
r->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IP_REACH);
for (int i = 0; i < SR_ALGORITHM_COUNT; i++) {
struct isis_prefix_sid *psid;
struct sr_prefix_cfg *pcfg = pcfgs[i];
if (!pcfg)
continue;
psid = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*psid));
isis_sr_prefix_cfg2subtlv(pcfg, external, psid);
append_item(&r->subtlvs->prefix_sids,
(struct isis_item *)psid);
}
}
struct isis_item_list *l;
l = (mtid == ISIS_MT_IPV4_UNICAST)
? &tlvs->ipv6_reach
: isis_get_mt_items(&tlvs->mt_ipv6_reach, mtid);
append_item(l, (struct isis_item *)r);
}
void isis_tlvs_add_ipv6_dstsrc_reach(struct isis_tlvs *tlvs, uint16_t mtid,
struct prefix_ipv6 *dest,
struct prefix_ipv6 *src,
uint32_t metric)
{
isis_tlvs_add_ipv6_reach(tlvs, mtid, dest, metric, false, NULL);
struct isis_item_list *l = isis_get_mt_items(&tlvs->mt_ipv6_reach,
mtid);
struct isis_ipv6_reach *r = (struct isis_ipv6_reach*)last_item(l);
r->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IPV6_REACH);
r->subtlvs->source_prefix = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*src));
memcpy(r->subtlvs->source_prefix, src, sizeof(*src));
}
void isis_tlvs_add_oldstyle_reach(struct isis_tlvs *tlvs, uint8_t *id,
uint8_t metric)
{
struct isis_oldstyle_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
r->metric = metric;
memcpy(r->id, id, sizeof(r->id));
append_item(&tlvs->oldstyle_reach, (struct isis_item *)r);
}
void isis_tlvs_add_extended_reach(struct isis_tlvs *tlvs, uint16_t mtid,
uint8_t *id, uint32_t metric,
struct isis_ext_subtlvs *exts)
{
struct isis_extended_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
memcpy(r->id, id, sizeof(r->id));
r->metric = metric;
if (exts)
r->subtlvs = copy_item_ext_subtlvs(exts, mtid);
struct isis_item_list *l;
if ((mtid == ISIS_MT_IPV4_UNICAST) || (mtid == ISIS_MT_DISABLE))
l = &tlvs->extended_reach;
else
l = isis_get_mt_items(&tlvs->mt_reach, mtid);
append_item(l, (struct isis_item *)r);
}
void isis_tlvs_add_threeway_adj(struct isis_tlvs *tlvs,
enum isis_threeway_state state,
uint32_t local_circuit_id,
const uint8_t *neighbor_id,
uint32_t neighbor_circuit_id)
{
assert(!tlvs->threeway_adj);
tlvs->threeway_adj = XCALLOC(MTYPE_ISIS_TLV, sizeof(*tlvs->threeway_adj));
tlvs->threeway_adj->state = state;
tlvs->threeway_adj->local_circuit_id = local_circuit_id;
if (neighbor_id) {
tlvs->threeway_adj->neighbor_set = true;
memcpy(tlvs->threeway_adj->neighbor_id, neighbor_id, 6);
tlvs->threeway_adj->neighbor_circuit_id = neighbor_circuit_id;
}
}
void isis_tlvs_add_spine_leaf(struct isis_tlvs *tlvs, uint8_t tier,
bool has_tier, bool is_leaf, bool is_spine,
bool is_backup)
{
assert(!tlvs->spine_leaf);
tlvs->spine_leaf = XCALLOC(MTYPE_ISIS_TLV, sizeof(*tlvs->spine_leaf));
if (has_tier) {
tlvs->spine_leaf->tier = tier;
}
tlvs->spine_leaf->has_tier = has_tier;
tlvs->spine_leaf->is_leaf = is_leaf;
tlvs->spine_leaf->is_spine = is_spine;
tlvs->spine_leaf->is_backup = is_backup;
}
struct isis_mt_router_info *
isis_tlvs_lookup_mt_router_info(struct isis_tlvs *tlvs, uint16_t mtid)
{
if (!tlvs || tlvs->mt_router_info_empty)
return NULL;
struct isis_mt_router_info *rv;
for (rv = (struct isis_mt_router_info *)tlvs->mt_router_info.head; rv;
rv = rv->next) {
if (rv->mtid == mtid)
return rv;
}
return NULL;
}
void isis_tlvs_set_purge_originator(struct isis_tlvs *tlvs,
const uint8_t *generator,
const uint8_t *sender)
{
assert(!tlvs->purge_originator);
tlvs->purge_originator = XCALLOC(MTYPE_ISIS_TLV,
sizeof(*tlvs->purge_originator));
memcpy(tlvs->purge_originator->generator, generator,
sizeof(tlvs->purge_originator->generator));
if (sender) {
tlvs->purge_originator->sender_set = true;
memcpy(tlvs->purge_originator->sender, sender,
sizeof(tlvs->purge_originator->sender));
}
}