]> git.proxmox.com Git - mirror_frr.git/blobdiff - isisd/isis_tlvs.c
Merge pull request #5625 from qlyoung/fix-zapi-ipset-name-nullterm
[mirror_frr.git] / isisd / isis_tlvs.c
index ae149a04284cd0cb6b8440fa13d49c82c5d17fd1..df6280e5c3b8896b6cee47177c50ff68c29dd46a 100644 (file)
@@ -3,6 +3,8 @@
  *
  * 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"
@@ -83,7 +88,7 @@ struct pack_order_entry {
                .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),
@@ -96,19 +101,694 @@ static struct pack_order_entry pack_order[] = {
        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;
@@ -125,20 +805,22 @@ static void format_item_prefix_sid(uint16_t mtid, struct isis_item *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)
@@ -184,15 +866,17 @@ static int unpack_item_prefix_sid(uint16_t mtid, uint8_t len, struct stream *s,
        }
 
        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. "
@@ -203,6 +887,7 @@ static int unpack_item_prefix_sid(uint16_t mtid, uint8_t len, struct stream *s,
 
        if (sid.flags & ISIS_PREFIX_SID_VALUE) {
                sid.value = stream_get3(s);
+               sid.value &= MPLS_LABEL_VALUE_MASK;
        } else {
                sid.value = stream_getl(s);
        }
@@ -274,7 +959,7 @@ static int unpack_subtlv_ipv6_source_prefix(enum isis_tlv_context context,
 
        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;
        }
@@ -303,7 +988,7 @@ static int unpack_subtlv_ipv6_source_prefix(enum isis_tlv_context context,
        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);
@@ -318,7 +1003,7 @@ static void free_items(enum isis_tlv_context context, enum isis_tlv_type type,
 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__)
@@ -701,11 +1386,8 @@ static struct isis_item *copy_item_extended_reach(struct isis_item *i)
        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;
 }
@@ -722,28 +1404,37 @@ static void format_item_extended_reach(uint16_t mtid, struct isis_item *i,
                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;
 }
 
@@ -778,9 +1469,6 @@ static int unpack_item_extended_reach(uint16_t mtid, uint8_t len,
        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
@@ -793,20 +1481,14 @@ static int unpack_item_extended_reach(uint16_t mtid, uint8_t len,
                  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, NULL)) {
+               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:
@@ -1255,6 +1937,7 @@ static struct isis_item *copy_item_extended_ip_reach(struct isis_item *i)
        rv->metric = r->metric;
        rv->down = r->down;
        rv->prefix = r->prefix;
+       rv->subtlvs = copy_subtlvs(r->subtlvs);
 
        return (struct isis_item *)rv;
 }
@@ -1346,7 +2029,7 @@ static int unpack_item_extended_ip_reach(uint16_t mtid, uint8_t len,
        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;
        }
@@ -1478,7 +2161,7 @@ static int unpack_tlv_dynamic_hostname(enum isis_tlv_context context,
        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] = '?';
                }
@@ -1832,7 +2515,7 @@ static int unpack_item_ipv6_reach(uint16_t mtid, uint8_t len, struct stream *s,
        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;
        }
@@ -1846,6 +2529,7 @@ static int unpack_item_ipv6_reach(uint16_t mtid, uint8_t len, struct stream *s,
        }
        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,
@@ -1896,6 +2580,222 @@ out:
        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)
 {
@@ -2195,7 +3095,7 @@ static void free_items(enum isis_tlv_context context, enum isis_tlv_type type,
 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];
 
@@ -2207,7 +3107,8 @@ static int pack_item(enum isis_tlv_context context, enum isis_tlv_type 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;
@@ -2226,7 +3127,7 @@ static void add_item_to_fragment(struct isis_item *i, struct pack_order_entry *p
 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)
 {
@@ -2316,6 +3217,39 @@ static void append_item(struct isis_item_list *dest, struct isis_item *item)
        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);
@@ -2468,7 +3402,7 @@ static void format_mt_items(enum isis_tlv_context context,
 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)
 {
@@ -2594,6 +3528,8 @@ struct isis_tlvs *isis_copy_tlvs(struct isis_tlvs *tlvs)
 
        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;
@@ -2629,6 +3565,7 @@ static void format_tlvs(struct isis_tlvs *tlvs, struct sbuf *buf, int indent)
 
        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);
@@ -2715,6 +3652,7 @@ void isis_free_tlvs(struct isis_tlvs *tlvs)
        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);
@@ -2770,8 +3708,16 @@ static void update_auth_hmac_md5(struct isis_auth *auth, struct stream *s,
                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);
 
@@ -2789,7 +3735,7 @@ static void update_auth(struct isis_tlvs *tlvs, struct stream *s, bool is_lsp)
        }
 }
 
-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),
@@ -2887,6 +3833,14 @@ static int pack_tlvs(struct isis_tlvs *tlvs, struct stream *stream,
                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;
@@ -3121,11 +4075,12 @@ 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(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,
@@ -3134,21 +4089,22 @@ static const struct tlv_ops *tlv_table[ISIS_CONTEXT_MAX][ISIS_TLV_MAX] = {
                [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] = {
@@ -3271,13 +4227,18 @@ void isis_tlvs_add_ipv6_addresses(struct isis_tlvs *tlvs,
 {
        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++;
        }
 }
 
@@ -3305,8 +4266,16 @@ static bool auth_validator_hmac_md5(struct isis_passwd *passwd,
                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);
@@ -3552,7 +4521,7 @@ void isis_tlvs_add_csnp_entries(struct isis_tlvs *tlvs, uint8_t *start_id,
        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;
@@ -3570,6 +4539,18 @@ void isis_tlvs_set_dynamic_hostname(struct isis_tlvs *tlvs,
                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)
 {
@@ -3591,6 +4572,38 @@ void isis_tlvs_add_oldstyle_ip_reach(struct isis_tlvs *tlvs,
        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)
 {
@@ -3645,17 +4658,14 @@ void isis_tlvs_add_oldstyle_reach(struct isis_tlvs *tlvs, uint8_t *id,
 
 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)