*
* Copyright (C) 2015,2017 Christian Franke
*
+ * Copyright (C) 2019 Olivier Dugeon - Orange Labs (for TE and SR)
+ *
* This file is part of FRR.
*
* FRR is free software; you can redistribute it and/or modify it
*/
#include <zebra.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_memory.h"
.what_to_pack = offsetof(struct isis_tlvs, w), \
}
-static struct pack_order_entry pack_order[] = {
+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_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(MT_IPV6_REACH, ISIS_MT_ITEMS, mt_ipv6_reach)
+};
/* This is a forward definition. The table is actually initialized
* in at the bottom. */
-static const struct tlv_ops *tlv_table[ISIS_CONTEXT_MAX][ISIS_TLV_MAX];
+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);
-/* Functions for Sub-TLV 3 SR Prefix-SID */
+/* 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);
+
+ return ext;
+}
+
+/*
+ * mtid parameter is used to determine if Adjacency is related to IPv4 or IPv6.
+ * A negative value could be used to skip copy of Adjacency SID.
+ */
+static struct isis_ext_subtlvs *
+copy_item_ext_subtlvs(struct isis_ext_subtlvs *exts, int16_t mtid)
+{
+ struct isis_ext_subtlvs *rv = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*rv));
+ struct isis_adj_sid *adj;
+ struct isis_lan_adj_sid *lan;
+
+ memcpy(rv, exts, sizeof(struct isis_ext_subtlvs));
+ 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 and LAN Adj SID list for IPv4 if needed */
+ for (adj = (struct isis_adj_sid *)exts->adj_sid.head; adj != NULL;
+ adj = adj->next) {
+ if ((mtid != -1)
+ && (((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);
+ }
+
+ for (lan = (struct isis_lan_adj_sid *)exts->lan_sid.head; lan != NULL;
+ lan = lan->next) {
+ if ((mtid != -1)
+ && (((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);
+ }
+
+ return rv;
+}
+
+/* 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, int indent,
+ uint16_t mtid)
+{
+
+ char ibuf[PREFIX2STR_BUFFER];
+
+ /* Standard metrics */
+ if (IS_SUBTLV(exts, EXT_ADM_GRP))
+ sbuf_push(buf, indent, "Administrative Group: 0x%" PRIx32 "\n",
+ exts->adm_group);
+ if (IS_SUBTLV(exts, EXT_LLRI)) {
+ sbuf_push(buf, indent, "Link Local ID: %" PRIu32 "\n",
+ exts->local_llri);
+ sbuf_push(buf, indent, "Link Remote ID: %" PRIu32 "\n",
+ exts->remote_llri);
+ }
+ if (IS_SUBTLV(exts, EXT_LOCAL_ADDR))
+ sbuf_push(buf, indent, "Local Interface IP Address(es): %s\n",
+ inet_ntoa(exts->local_addr));
+ if (IS_SUBTLV(exts, EXT_NEIGH_ADDR))
+ sbuf_push(buf, indent, "Remote Interface IP Address(es): %s\n",
+ inet_ntoa(exts->neigh_addr));
+ if (IS_SUBTLV(exts, EXT_LOCAL_ADDR6))
+ sbuf_push(buf, indent, "Local Interface IPv6 Address(es): %s\n",
+ inet_ntop(AF_INET6, &exts->local_addr6, ibuf,
+ PREFIX2STR_BUFFER));
+ if (IS_SUBTLV(exts, EXT_NEIGH_ADDR6))
+ sbuf_push(buf, indent, "Remote Interface IPv6 Address(es): %s\n",
+ inet_ntop(AF_INET6, &exts->local_addr6, ibuf,
+ PREFIX2STR_BUFFER));
+ if (IS_SUBTLV(exts, EXT_MAX_BW))
+ sbuf_push(buf, indent, "Maximum Bandwidth: %g (Bytes/sec)\n",
+ exts->max_bw);
+ if (IS_SUBTLV(exts, EXT_MAX_RSV_BW))
+ sbuf_push(buf, indent,
+ "Maximum Reservable Bandwidth: %g (Bytes/sec)\n",
+ exts->max_rsv_bw);
+ if (IS_SUBTLV(exts, EXT_UNRSV_BW)) {
+ 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))
+ sbuf_push(buf, indent, "Traffic Engineering Metric: %u\n",
+ exts->te_metric);
+ if (IS_SUBTLV(exts, EXT_RMT_AS))
+ sbuf_push(buf, indent,
+ "Inter-AS TE Remote AS number: %" PRIu32 "\n",
+ exts->remote_as);
+ if (IS_SUBTLV(exts, EXT_RMT_IP))
+ sbuf_push(buf, indent,
+ "Inter-AS TE Remote ASBR IP address: %s\n",
+ inet_ntoa(exts->remote_ip));
+ /* Extended metrics */
+ if (IS_SUBTLV(exts, EXT_DELAY))
+ sbuf_push(buf, indent,
+ "%s Average Link Delay: %" PRIu32 " (micro-sec)\n",
+ IS_ANORMAL(exts->delay) ? "Anomalous" : "Normal",
+ exts->delay);
+ if (IS_SUBTLV(exts, EXT_MM_DELAY)) {
+ sbuf_push(buf, indent, "%s Min/Max Link Delay: %" PRIu32 " / %"
+ PRIu32 " (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)) {
+ sbuf_push(buf, indent,
+ "Delay Variation: %" PRIu32 " (micro-sec)\n",
+ exts->delay_var & TE_EXT_MASK);
+ }
+ if (IS_SUBTLV(exts, EXT_PKT_LOSS))
+ 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))
+ sbuf_push(buf, indent,
+ "Unidir. Residual Bandwidth: %g (Bytes/sec)\n",
+ exts->res_bw);
+ if (IS_SUBTLV(exts, EXT_AVA_BW))
+ sbuf_push(buf, indent,
+ "Unidir. Available Bandwidth: %g (Bytes/sec)\n",
+ exts->ava_bw);
+ if (IS_SUBTLV(exts, EXT_USE_BW))
+ sbuf_push(buf, indent,
+ "Unidir. Utilized Bandwidth: %g (Bytes/sec)\n",
+ exts->use_bw);
+ /* Segment Routing Adjacency */
+ 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) {
+ if (((mtid == ISIS_MT_IPV4_UNICAST)
+ && (adj->family != AF_INET))
+ || ((mtid == ISIS_MT_IPV6_UNICAST)
+ && (adj->family != AF_INET6)))
+ continue;
+ sbuf_push(
+ buf, indent,
+ "Adjacency-SID: %" PRIu32 ", Weight: %" PRIu8
+ ", 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');
+ }
+ }
+ 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) {
+ 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: %" PRIu32
+ ", Weight: %" PRIu8
+ ", Flags: F:%c B:%c, V:%c, L:%c, S:%c, P:%c\n"
+ " Neighbor-ID: %s\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',
+ isis_format_id(lan->neighbor_id, 6));
+ }
+ }
+}
+static void free_item_ext_subtlvs(struct isis_ext_subtlvs *exts)
+{
+ struct isis_item *item, *next_item;
+
+ /* First, free Adj SID and LAN Adj SID list if needed */
+ for (item = exts->adj_sid.head; item; item = next_item) {
+ next_item = item->next;
+ XFREE(MTYPE_ISIS_SUBTLV, item);
+ }
+ for (item = exts->lan_sid.head; item; item = next_item) {
+ next_item = item->next;
+ XFREE(MTYPE_ISIS_SUBTLV, item);
+ }
+ XFREE(MTYPE_ISIS_SUBTLV, exts);
+}
+
+static int pack_item_ext_subtlvs(struct isis_ext_subtlvs *exts,
+ struct stream *s)
+{
+ uint8_t size;
+
+ if (STREAM_WRITEABLE(s) < 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_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);
+ }
+ 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);
+
+ }
+ }
+ 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);
+ }
+ }
+
+ 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;
+
+ 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) {
+ sbuf_push(log, indent, "TLV %" PRIu8 ": Available data %" PRIu8 " is less than TLV size %u !\n",
+ subtlv_type, len - sum, 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) {
+ sbuf_push(log, indent,
+ "TLV size does not match expected size for Administrative Group!\n");
+ } else {
+ exts->adm_group = stream_getl(s);
+ SET_SUBTLV(exts, EXT_ADM_GRP);
+ }
+ break;
+ case ISIS_SUBTLV_LLRI:
+ if (subtlv_len != ISIS_SUBTLV_LLRI_SIZE) {
+ sbuf_push(log, indent,
+ "TLV size does not match expected size for Link ID!\n");
+ } 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) {
+ sbuf_push(log, indent,
+ "TLV size does not match expected size for Local IP address!\n");
+ } 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) {
+ sbuf_push(log, indent,
+ "TLV size does not match expected size for Remote IP address!\n");
+ } 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) {
+ sbuf_push(log, indent,
+ "TLV size does not match expected size for Local IPv6 address!\n");
+ } 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) {
+ sbuf_push(log, indent,
+ "TLV size does not match expected size for Remote IPv6 address!\n");
+ } 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) {
+ sbuf_push(log, indent,
+ "TLV size does not match expected size for Maximum Bandwidth!\n");
+ } 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) {
+ sbuf_push(log, indent,
+ "TLV size does not match expected size for Maximum Reservable Bandwidth!\n");
+ } 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) {
+ sbuf_push(log, indent,
+ "TLV size does not match expected size for Unreserved Bandwidth!\n");
+ } 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) {
+ sbuf_push(log, indent,
+ "TLV size does not match expected size for Traffic Engineering Metric!\n");
+ } 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) {
+ sbuf_push(log, indent,
+ "TLV size does not match expected size for Remote AS number!\n");
+ } 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) {
+ sbuf_push(log, indent,
+ "TLV size does not match expected size for Remote ASBR IP Address!\n");
+ } 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) {
+ sbuf_push(log, indent,
+ "TLV size does not match expected size for Average Link Delay!\n");
+ } else {
+ exts->delay = stream_getl(s);
+ SET_SUBTLV(exts, EXT_DELAY);
+ }
+ break;
+ case ISIS_SUBTLV_MM_DELAY:
+ if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
+ sbuf_push(log, indent,
+ "TLV size does not match expected size for Min/Max Link Delay!\n");
+ } 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) {
+ sbuf_push(log, indent,
+ "TLV size does not match expected size for Delay Variation!\n");
+ } 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) {
+ sbuf_push(log, indent,
+ "TLV size does not match expected size for Link Packet Loss!\n");
+ } 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) {
+ sbuf_push(log, indent,
+ "TLV size does not match expected size for Unidirectional Residual Bandwidth!\n");
+ } 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) {
+ sbuf_push(log, indent,
+ "TLV size does not match expected size for Unidirectional Available Bandwidth!\n");
+ } 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) {
+ sbuf_push(log, indent,
+ "TLV size does not match expected size for Unidirectional Utilized Bandwidth!\n");
+ } else {
+ exts->use_bw = stream_getf(s);
+ SET_SUBTLV(exts, EXT_USE_BW);
+ }
+ break;
+ /* Segment Routing Adjacency */
+ case ISIS_SUBTLV_ADJ_SID:
+ if (subtlv_len != ISIS_SUBTLV_ADJ_SID_SIZE
+ && subtlv_len != ISIS_SUBTLV_ADJ_SID_SIZE + 1) {
+ sbuf_push(log, indent,
+ "TLV size does not match expected size for Adjacency SID!\n");
+ } 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) {
+ 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;
+ case ISIS_SUBTLV_LAN_ADJ_SID:
+ if (subtlv_len != ISIS_SUBTLV_LAN_ADJ_SID_SIZE
+ && subtlv_len != ISIS_SUBTLV_LAN_ADJ_SID_SIZE + 1) {
+ sbuf_push(log, indent,
+ "TLV size does not match expected size for LAN-Adjacency SID!\n");
+ } 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) {
+ 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;
+ 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 */
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 *sid = (struct isis_prefix_sid *)i;
- sbuf_push(buf, indent, "SR Prefix-SID:\n");
- sbuf_push(buf, indent, " 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" : "",
- sid->flags & ISIS_PREFIX_SID_EXPLICIT_NULL ? " EXPLICIT-NULL" : "",
- sid->flags & ISIS_PREFIX_SID_VALUE ? " VALUE" : "",
- sid->flags & ISIS_PREFIX_SID_LOCAL ? " LOCAL" : "");
- sbuf_push(buf, indent, " Algorithm: %" PRIu8 "\n", sid->algorithm);
+ sbuf_push(buf, indent, "SR Prefix-SID ");
if (sid->flags & ISIS_PREFIX_SID_VALUE) {
- sbuf_push(buf, indent, "Label: %" PRIu32 "\n", sid->value);
+ sbuf_push(buf, 0, "Label: %" PRIu32 ", ", sid->value);
} else {
- sbuf_push(buf, indent, "Index: %" PRIu32 "\n", sid->value);
+ sbuf_push(buf, 0, "Index: %" PRIu32 ", ", sid->value);
}
+ sbuf_push(buf, 0, "Algorithm: %" PRIu8 ", ", 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)
}
sid.flags = stream_getc(s);
- if ((sid.flags & ISIS_PREFIX_SID_VALUE)
- != (sid.flags & ISIS_PREFIX_SID_LOCAL)) {
- sbuf_push(log, indent, "Flags inplausible: Local Flag needs to match Value Flag\n");
- return 0;
+ 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) ? 5 : 6;
+ 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. "
if (sid.flags & ISIS_PREFIX_SID_VALUE) {
sid.value = stream_get3(s);
+ sid.value &= MPLS_LABEL_VALUE_MASK;
} else {
sid.value = stream_getl(s);
}
p.prefixlen = stream_getc(s);
if (p.prefixlen > 128) {
- sbuf_push(log, indent, "Prefixlen %u is inplausible for IPv6\n",
+ sbuf_push(log, indent, "Prefixlen %u is implausible for IPv6\n",
p.prefixlen);
return 1;
}
memcpy(subtlvs->source_prefix, &p, sizeof(p));
return 0;
}
-static void init_item_list(struct isis_item_list *items);
+
static struct isis_item *copy_item(enum isis_tlv_context context,
enum isis_tlv_type type,
struct isis_item *item);
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,
- struct pack_order_entry *pe,
+ 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__)
static int unpack_tlvs(enum isis_tlv_context context, size_t avail_len,
struct stream *stream, struct sbuf *log, void *dest,
- int indent);
+ int indent, bool *unpacked_known_tlvs);
/* Functions related to TLVs 1 Area Addresses */
memcpy(rv->id, r->id, 7);
rv->metric = r->metric;
- if (r->subtlvs && r->subtlv_len) {
- rv->subtlvs = XCALLOC(MTYPE_ISIS_TLV, r->subtlv_len);
- memcpy(rv->subtlvs, r->subtlvs, r->subtlv_len);
- rv->subtlv_len = r->subtlv_len;
- }
+ if (r->subtlvs)
+ rv->subtlvs = copy_item_ext_subtlvs(r->subtlvs, -1);
return (struct isis_item *)rv;
}
sbuf_push(buf, 0, " %s", isis_mtid2str(mtid));
sbuf_push(buf, 0, "\n");
- if (r->subtlv_len && r->subtlvs)
- mpls_te_print_detail(buf, indent + 2, r->subtlvs,
- r->subtlv_len);
+ if (r->subtlvs)
+ format_item_ext_subtlvs(r->subtlvs, buf, indent + 2, mtid);
}
static void free_item_extended_reach(struct isis_item *i)
{
struct isis_extended_reach *item = (struct isis_extended_reach *)i;
- XFREE(MTYPE_ISIS_TLV, item->subtlvs);
+ 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)
{
struct isis_extended_reach *r = (struct isis_extended_reach *)i;
+ size_t len;
+ size_t len_pos;
- if (STREAM_WRITEABLE(s) < 11 + (unsigned)r->subtlv_len)
+ if (STREAM_WRITEABLE(s) < 11 + ISIS_SUBTLV_MAX_SIZE)
return 1;
+
stream_put(s, r->id, sizeof(r->id));
stream_put3(s, r->metric);
- stream_putc(s, r->subtlv_len);
- stream_put(s, r->subtlvs, r->subtlv_len);
+ 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);
+ /* Adjust length */
+ len = stream_get_endp(s) - len_pos - 1;
+ stream_putc_at(s, len_pos, len);
return 0;
}
rv->metric = stream_get3(s);
subtlv_len = stream_getc(s);
- format_item_extended_reach(mtid, (struct isis_item *)rv, log,
- indent + 2);
-
if ((size_t)len < ((size_t)11) + subtlv_len) {
sbuf_push(log, indent,
"Not enough data left for subtlv size %" PRIu8
subtlv_len);
if (subtlv_len) {
- size_t subtlv_start = stream_get_getp(s);
-
- if (unpack_tlvs(ISIS_CONTEXT_SUBTLV_NE_REACH, subtlv_len, s,
- log, NULL, indent + 4)) {
+ if (unpack_item_ext_subtlvs(mtid, subtlv_len, s, log, rv,
+ indent + 4)) {
goto out;
}
-
- stream_set_getp(s, subtlv_start);
-
- rv->subtlvs = XCALLOC(MTYPE_ISIS_TLV, subtlv_len);
- stream_get(rv->subtlvs, s, subtlv_len);
- rv->subtlv_len = subtlv_len;
}
+ format_item_extended_reach(mtid, (struct isis_item *)rv, log,
+ indent + 2);
append_item(items, (struct isis_item *)rv);
return 0;
out:
rv->metric = r->metric;
rv->down = r->down;
rv->prefix = r->prefix;
+ rv->subtlvs = copy_subtlvs(r->subtlvs);
return (struct isis_item *)rv;
}
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, indent + 4);
+ }
}
static void free_item_extended_ip_reach(struct isis_item *i)
rv->prefix.family = AF_INET;
rv->prefix.prefixlen = control & 0x3f;
if (rv->prefix.prefixlen > 32) {
- sbuf_push(log, indent, "Prefixlen %u is inplausible for IPv4\n",
+ sbuf_push(log, indent, "Prefixlen %u is implausible for IPv4\n",
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)) {
+ 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);
bool sane = true;
for (uint8_t i = 0; i < tlv_len; i++) {
if ((unsigned char)tlvs->hostname[i] > 127
- || !isprint((int)tlvs->hostname[i])) {
+ || !isprint((unsigned char)tlvs->hostname[i])) {
sane = false;
tlvs->hostname[i] = '?';
}
rv->prefix.family = AF_INET6;
rv->prefix.prefixlen = stream_getc(s);
if (rv->prefix.prefixlen > 128) {
- sbuf_push(log, indent, "Prefixlen %u is inplausible for IPv6\n",
+ sbuf_push(log, indent, "Prefixlen %u is implausible for IPv6\n",
rv->prefix.prefixlen);
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,
}
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)) {
+ 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 1;
}
+/* Functions related to TLV 242 Router Capability */
+static struct isis_router_cap *copy_tlv_router_cap(
+ const struct isis_router_cap *router_cap)
+{
+ struct isis_router_cap *rv = XMALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
+
+ if (!router_cap)
+ return NULL;
+
+ memcpy(rv, router_cap, sizeof(*rv));
+
+ return rv;
+}
+
+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');
+
+ /* SR Global Block */
+ if (router_cap->srgb.range_size != 0)
+ sbuf_push(buf, indent,
+ " Segment Routing: I:%s V:%s, SRGB Base: %d Range: %d\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);
+
+ /* SR Algorithms */
+ if (router_cap->algo[0] != SR_ALGORITHM_UNSET) {
+ sbuf_push(buf, indent, " Algorithm: %s",
+ router_cap->algo[0] == 0 ? "0: SPF"
+ : "0: Strict SPF");
+ for (int i = 0; i < SR_ALGORITHM_COUNT; i++)
+ if (router_cap->algo[i] != SR_ALGORITHM_UNSET)
+ sbuf_push(buf, indent, " %s",
+ router_cap->algo[1] == 0
+ ? "0: SPF"
+ : "0: Strict SPF");
+ sbuf_push(buf, indent, "\n");
+ }
+
+ /* SR Node MSSD */
+ if (router_cap->msd != 0)
+ sbuf_push(buf, indent, " Node MSD: %d\n", router_cap->msd);
+}
+
+static void free_tlv_router_cap(struct isis_router_cap *router_cap)
+{
+ XFREE(MTYPE_ISIS_TLV, router_cap);
+}
+
+static int pack_tlv_router_cap(const struct isis_router_cap *router_cap,
+ struct stream *s)
+{
+ size_t tlv_len = ISIS_ROUTER_CAP_SIZE;
+ size_t len_pos;
+ uint8_t nb_algo;
+
+ if (!router_cap)
+ return 0;
+
+ /* Compute Maximum TLV size */
+ tlv_len += ISIS_SUBTLV_SID_LABEL_RANGE_SIZE
+ + ISIS_SUBTLV_HDR_SIZE
+ + ISIS_SUBTLV_ALGORITHM_SIZE
+ + ISIS_SUBTLV_NODE_MSD_SIZE;
+
+ if (STREAM_WRITEABLE(s) < (unsigned int)(2 + tlv_len))
+ return 1;
+
+ /* 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, tlv_len);
+ stream_put_ipv4(s, router_cap->router_id.s_addr);
+ stream_putc(s, router_cap->flags);
+
+ /* Add SRGB if set */
+ 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 */
+ for (nb_algo = 0; nb_algo < SR_ALGORITHM_COUNT; nb_algo++)
+ if (router_cap->algo[nb_algo] == SR_ALGORITHM_UNSET)
+ break;
+ if (nb_algo > 0) {
+ stream_putc(s, ISIS_SUBTLV_ALGORITHM);
+ stream_putc(s, nb_algo);
+ for (int i = 0; i < nb_algo; i++)
+ stream_putc(s, router_cap->algo[i]);
+ }
+ /* And finish with MSD if set */
+ 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);
+ }
+ }
+
+ /* 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;
+ uint8_t type;
+ uint8_t length;
+ uint8_t subtlv_len;
+ uint8_t sid_len;
+
+ 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) {
+ sbuf_push(log, indent,
+ "WARNING: Router Capability TLV present multiple times.\n");
+ stream_forward_getp(s, tlv_len);
+ return 0;
+ }
+
+ /* Allocate router cap structure and initialize SR Algorithms */
+ tlvs->router_cap = XCALLOC(MTYPE_ISIS_TLV, sizeof(*tlvs->router_cap));
+ for (int i = 0; i < SR_ALGORITHM_COUNT; i++)
+ tlvs->router_cap->algo[i] = SR_ALGORITHM_UNSET;
+
+ /* Get Router ID and Flags */
+ tlvs->router_cap->router_id.s_addr = stream_get_ipv4(s);
+ tlvs->router_cap->flags = stream_getc(s);
+
+ /* Parse remaining part of the TLV if present */
+ subtlv_len = tlv_len - ISIS_ROUTER_CAP_SIZE;
+ while (subtlv_len > 2) {
+ struct isis_router_cap *rc = tlvs->router_cap;
+ uint8_t msd_type;
+
+ type = stream_getc(s);
+ length = stream_getc(s);
+ switch (type) {
+ case ISIS_SUBTLV_SID_LABEL_RANGE:
+ rc->srgb.flags = stream_getc(s);
+ rc->srgb.range_size = stream_get3(s);
+ /* Skip Type and get Length of SID Label */
+ stream_getc(s);
+ sid_len = stream_getc(s);
+ if (sid_len == ISIS_SUBTLV_SID_LABEL_SIZE)
+ rc->srgb.lower_bound = stream_get3(s);
+ else
+ rc->srgb.lower_bound = stream_getl(s);
+
+ /* SRGB sanity checks. */
+ if (rc->srgb.range_size == 0
+ || (rc->srgb.lower_bound <= MPLS_LABEL_RESERVED_MAX)
+ || ((rc->srgb.lower_bound + rc->srgb.range_size - 1)
+ > MPLS_LABEL_UNRESERVED_MAX)) {
+ sbuf_push(log, indent, "Invalid label range. Reset SRGB\n");
+ rc->srgb.lower_bound = 0;
+ rc->srgb.range_size = 0;
+ }
+ break;
+ case ISIS_SUBTLV_ALGORITHM:
+ /* Only 2 algorithms are supported: SPF & Strict SPF */
+ stream_get(&rc->algo, s,
+ length > SR_ALGORITHM_COUNT
+ ? SR_ALGORITHM_COUNT
+ : length);
+ if (length > SR_ALGORITHM_COUNT)
+ stream_forward_getp(
+ s, length - SR_ALGORITHM_COUNT);
+ break;
+ case ISIS_SUBTLV_NODE_MSD:
+ msd_type = stream_getc(s);
+ rc->msd = stream_getc(s);
+ /* Only BMI-MSD type has been defined in RFC 8491 */
+ if (msd_type != MSD_TYPE_BASE_MPLS_IMPOSITION)
+ rc->msd = 0;
+ break;
+ default:
+ stream_forward_getp(s, length);
+ break;
+ }
+ subtlv_len = subtlv_len - length - 2;
+ }
+ return 0;
+}
+
/* Functions related to TLV 10 Authentication */
static struct isis_item *copy_item_auth(struct isis_item *i)
{
static int pack_item(enum isis_tlv_context context, enum isis_tlv_type type,
struct isis_item *i, struct stream *s,
struct isis_tlvs **fragment_tlvs,
- struct pack_order_entry *pe, uint16_t mtid)
+ const struct pack_order_entry *pe, uint16_t mtid)
{
const struct tlv_ops *ops = tlv_table[context][type];
return 1;
}
-static void add_item_to_fragment(struct isis_item *i, struct pack_order_entry *pe,
+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;
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,
- struct pack_order_entry *pe,
+ const struct pack_order_entry *pe,
struct isis_tlvs *(*new_fragment)(struct list *l),
struct list *new_fragment_arg)
{
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 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,
- struct pack_order_entry *pe,
+ const struct pack_order_entry *pe,
struct isis_tlvs *(*new_fragment)(struct list *l),
struct list *new_fragment_arg)
{
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);
return rv;
format_tlv_dynamic_hostname(tlvs->hostname, buf, indent);
format_tlv_te_router_id(tlvs->te_router_id, buf, indent);
+ format_tlv_router_cap(tlvs->router_cap, buf, indent);
format_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_REACH,
&tlvs->extended_reach, buf, indent);
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);
XFREE(MTYPE_ISIS_TLV, tlvs);
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);
}
}
-static int handle_pack_entry(struct pack_order_entry *pe,
+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),
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;
static int unpack_tlv(enum isis_tlv_context context, size_t avail_len,
struct stream *stream, struct sbuf *log, void *dest,
- int indent)
+ int indent, bool *unpacked_known_tlvs)
{
uint8_t tlv_type, tlv_len;
const struct tlv_ops *ops;
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);
}
static int unpack_tlvs(enum isis_tlv_context context, size_t avail_len,
struct stream *stream, struct sbuf *log, void *dest,
- int indent)
+ int indent, bool *unpacked_known_tlvs)
{
int rv;
size_t tlv_start, tlv_pos;
while (tlv_pos < avail_len) {
rv = unpack_tlv(context, avail_len - tlv_pos, stream, log, dest,
- indent + 2);
+ indent + 2, unpacked_known_tlvs);
if (rv)
return rv;
result = isis_alloc_tlvs();
rv = unpack_tlvs(ISIS_CONTEXT_LSP, avail_len, stream, &logbuf, result,
- indent);
+ indent, NULL);
*log = sbuf_buf(&logbuf);
*dest = result;
TLV_OPS(threeway_adj, "TLV 240 P2P Three-Way Adjacency");
ITEM_TLV_OPS(ipv6_address, "TLV 232 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");
-static const struct tlv_ops *tlv_table[ISIS_CONTEXT_MAX][ISIS_TLV_MAX] = {
+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_AUTH] = &tlv_auth_ops,
[ISIS_TLV_PURGE_ORIGINATOR] = &tlv_purge_originator_ops,
[ISIS_TLV_EXTENDED_REACH] = &tlv_extended_reach_ops,
- [ISIS_TLV_MT_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_EXTENDED_IP_REACH] = &tlv_extended_ip_reach_ops,
- [ISIS_TLV_MT_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_THREE_WAY_ADJ] = &tlv_threeway_adj_ops,
[ISIS_TLV_IPV6_ADDRESS] = &tlv_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_CONTEXT_SUBTLV_NE_REACH] = {},
[ISIS_CONTEXT_SUBTLV_IP_REACH] = {
{
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++;
}
}
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 (!first)
return;
- for_each_from (lspdb, head, lsp, first) {
+ 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;
tlvs->hostname = XSTRDUP(MTYPE_ISIS_TLV, hostname);
}
+/* Set Router Capability TLV parameters */
+void isis_tlvs_set_router_capability(struct isis_tlvs *tlvs,
+ const struct isis_router_cap *cap)
+{
+ XFREE(MTYPE_ISIS_TLV, tlvs->router_cap);
+ if (!cap)
+ return;
+
+ tlvs->router_cap = XCALLOC(MTYPE_ISIS_TLV, sizeof(*tlvs->router_cap));
+ *tlvs->router_cap = *cap;
+}
+
void isis_tlvs_set_te_router_id(struct isis_tlvs *tlvs,
const struct in_addr *id)
{
append_item(&tlvs->oldstyle_ip_reach, (struct isis_item *)r);
}
+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);
+}
+
+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);
+}
+
+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);
+}
+
+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_add_extended_ip_reach(struct isis_tlvs *tlvs,
struct prefix_ipv4 *dest, uint32_t metric)
{
void isis_tlvs_add_extended_reach(struct isis_tlvs *tlvs, uint16_t mtid,
uint8_t *id, uint32_t metric,
- uint8_t *subtlvs, uint8_t subtlv_len)
+ 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 (subtlvs && subtlv_len) {
- r->subtlvs = XCALLOC(MTYPE_ISIS_TLV, subtlv_len);
- memcpy(r->subtlvs, subtlvs, subtlv_len);
- r->subtlv_len = subtlv_len;
- }
+ if (exts)
+ r->subtlvs = copy_item_ext_subtlvs(exts, mtid);
struct isis_item_list *l;
if (mtid == ISIS_MT_IPV4_UNICAST)