2 * IS-IS TLV Serializer/Deserializer
4 * Copyright (C) 2015,2017 Christian Franke
6 * This file is part of FRR.
8 * FRR is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2, or (at your option) any
13 * FRR is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with FRR; see the file COPYING. If not, write to the Free
20 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
30 #include "isisd/isisd.h"
31 #include "isisd/isis_memory.h"
32 #include "isisd/isis_tlvs.h"
33 #include "isisd/isis_common.h"
34 #include "isisd/isis_mt.h"
35 #include "isisd/isis_misc.h"
36 #include "isisd/isis_adjacency.h"
37 #include "isisd/isis_circuit.h"
38 #include "isisd/isis_pdu.h"
39 #include "isisd/isis_lsp.h"
40 #include "isisd/isis_te.h"
42 DEFINE_MTYPE_STATIC(ISISD
, ISIS_TLV
, "ISIS TLVs")
43 DEFINE_MTYPE_STATIC(ISISD
, ISIS_SUBTLV
, "ISIS Sub-TLVs")
44 DEFINE_MTYPE_STATIC(ISISD
, ISIS_MT_ITEM_LIST
, "ISIS MT Item Lists")
46 typedef int (*unpack_tlv_func
)(enum isis_tlv_context context
, uint8_t tlv_type
,
47 uint8_t tlv_len
, struct stream
*s
,
48 struct sbuf
*log
, void *dest
, int indent
);
49 typedef int (*pack_item_func
)(struct isis_item
*item
, struct stream
*s
);
50 typedef void (*free_item_func
)(struct isis_item
*i
);
51 typedef int (*unpack_item_func
)(uint16_t mtid
, uint8_t len
, struct stream
*s
,
52 struct sbuf
*log
, void *dest
, int indent
);
53 typedef void (*format_item_func
)(uint16_t mtid
, struct isis_item
*i
,
54 struct sbuf
*buf
, int indent
);
55 typedef struct isis_item
*(*copy_item_func
)(struct isis_item
*i
);
59 unpack_tlv_func unpack
;
61 pack_item_func pack_item
;
62 free_item_func free_item
;
63 unpack_item_func unpack_item
;
64 format_item_func format_item
;
65 copy_item_func copy_item
;
73 struct pack_order_entry
{
74 enum isis_tlv_context context
;
75 enum isis_tlv_type type
;
76 enum how_to_pack how_to_pack
;
79 #define PACK_ENTRY(t, h, w) \
81 .context = ISIS_CONTEXT_LSP, .type = ISIS_TLV_##t, \
83 .what_to_pack = offsetof(struct isis_tlvs, w), \
86 static struct pack_order_entry pack_order
[] = {
87 PACK_ENTRY(OLDSTYLE_REACH
, ISIS_ITEMS
, oldstyle_reach
),
88 PACK_ENTRY(LAN_NEIGHBORS
, ISIS_ITEMS
, lan_neighbor
),
89 PACK_ENTRY(LSP_ENTRY
, ISIS_ITEMS
, lsp_entries
),
90 PACK_ENTRY(EXTENDED_REACH
, ISIS_ITEMS
, extended_reach
),
91 PACK_ENTRY(MT_REACH
, ISIS_MT_ITEMS
, mt_reach
),
92 PACK_ENTRY(OLDSTYLE_IP_REACH
, ISIS_ITEMS
, oldstyle_ip_reach
),
93 PACK_ENTRY(OLDSTYLE_IP_REACH_EXT
, ISIS_ITEMS
, oldstyle_ip_reach_ext
),
94 PACK_ENTRY(IPV4_ADDRESS
, ISIS_ITEMS
, ipv4_address
),
95 PACK_ENTRY(IPV6_ADDRESS
, ISIS_ITEMS
, ipv6_address
),
96 PACK_ENTRY(EXTENDED_IP_REACH
, ISIS_ITEMS
, extended_ip_reach
),
97 PACK_ENTRY(MT_IP_REACH
, ISIS_MT_ITEMS
, mt_ip_reach
),
98 PACK_ENTRY(IPV6_REACH
, ISIS_ITEMS
, ipv6_reach
),
99 PACK_ENTRY(MT_IPV6_REACH
, ISIS_MT_ITEMS
, mt_ipv6_reach
)};
101 /* This is a forward definition. The table is actually initialized
102 * in at the bottom. */
103 static const struct tlv_ops
*tlv_table
[ISIS_CONTEXT_MAX
][ISIS_TLV_MAX
];
105 /* End of _ops forward definition. */
108 static void append_item(struct isis_item_list
*dest
, struct isis_item
*item
);
110 /* Functions for Sub-TVL ??? IPv6 Source Prefix */
112 static struct prefix_ipv6
*copy_subtlv_ipv6_source_prefix(struct prefix_ipv6
*p
)
117 struct prefix_ipv6
*rv
= XCALLOC(MTYPE_ISIS_SUBTLV
, sizeof(*rv
));
118 rv
->family
= p
->family
;
119 rv
->prefixlen
= p
->prefixlen
;
120 memcpy(&rv
->prefix
, &p
->prefix
, sizeof(rv
->prefix
));
124 static void format_subtlv_ipv6_source_prefix(struct prefix_ipv6
*p
,
125 struct sbuf
*buf
, int indent
)
130 char prefixbuf
[PREFIX2STR_BUFFER
];
131 sbuf_push(buf
, indent
, "IPv6 Source Prefix: %s\n",
132 prefix2str(p
, prefixbuf
, sizeof(prefixbuf
)));
135 static int pack_subtlv_ipv6_source_prefix(struct prefix_ipv6
*p
,
141 if (STREAM_WRITEABLE(s
) < 3 + (unsigned)PSIZE(p
->prefixlen
))
144 stream_putc(s
, ISIS_SUBTLV_IPV6_SOURCE_PREFIX
);
145 stream_putc(s
, 1 + PSIZE(p
->prefixlen
));
146 stream_putc(s
, p
->prefixlen
);
147 stream_put(s
, &p
->prefix
, PSIZE(p
->prefixlen
));
151 static int unpack_subtlv_ipv6_source_prefix(enum isis_tlv_context context
,
152 uint8_t tlv_type
, uint8_t tlv_len
,
153 struct stream
*s
, struct sbuf
*log
,
154 void *dest
, int indent
)
156 struct isis_subtlvs
*subtlvs
= dest
;
157 struct prefix_ipv6 p
= {
161 sbuf_push(log
, indent
, "Unpacking IPv6 Source Prefix Sub-TLV...\n");
164 sbuf_push(log
, indent
,
165 "Not enough data left. (expected 1 or more bytes, got %" PRIu8
")\n",
170 p
.prefixlen
= stream_getc(s
);
171 if (p
.prefixlen
> 128) {
172 sbuf_push(log
, indent
, "Prefixlen %u is inplausible for IPv6\n",
177 if (tlv_len
!= 1 + PSIZE(p
.prefixlen
)) {
180 "TLV size differs from expected size for the prefixlen. "
181 "(expected %u but got %" PRIu8
")\n",
182 1 + PSIZE(p
.prefixlen
), tlv_len
);
186 stream_get(&p
.prefix
, s
, PSIZE(p
.prefixlen
));
188 if (subtlvs
->source_prefix
) {
191 "WARNING: source prefix Sub-TLV present multiple times.\n");
192 /* Ignore all but first occurrence of the source prefix Sub-TLV
197 subtlvs
->source_prefix
= XCALLOC(MTYPE_ISIS_SUBTLV
, sizeof(p
));
198 memcpy(subtlvs
->source_prefix
, &p
, sizeof(p
));
202 /* Functions related to subtlvs */
204 static struct isis_subtlvs
*isis_alloc_subtlvs(void)
206 struct isis_subtlvs
*result
;
208 result
= XCALLOC(MTYPE_ISIS_SUBTLV
, sizeof(*result
));
213 static struct isis_subtlvs
*copy_subtlvs(struct isis_subtlvs
*subtlvs
)
218 struct isis_subtlvs
*rv
= XCALLOC(MTYPE_ISIS_SUBTLV
, sizeof(*rv
));
221 copy_subtlv_ipv6_source_prefix(subtlvs
->source_prefix
);
225 static void format_subtlvs(struct isis_subtlvs
*subtlvs
, struct sbuf
*buf
,
228 format_subtlv_ipv6_source_prefix(subtlvs
->source_prefix
, buf
, indent
);
231 static void isis_free_subtlvs(struct isis_subtlvs
*subtlvs
)
236 XFREE(MTYPE_ISIS_SUBTLV
, subtlvs
->source_prefix
);
238 XFREE(MTYPE_ISIS_SUBTLV
, subtlvs
);
241 static int pack_subtlvs(struct isis_subtlvs
*subtlvs
, struct stream
*s
)
244 size_t subtlv_len_pos
= stream_get_endp(s
);
246 if (STREAM_WRITEABLE(s
) < 1)
249 stream_putc(s
, 0); /* Put 0 as subtlvs length, filled in later */
251 rv
= pack_subtlv_ipv6_source_prefix(subtlvs
->source_prefix
, s
);
255 size_t subtlv_len
= stream_get_endp(s
) - subtlv_len_pos
- 1;
256 if (subtlv_len
> 255)
259 stream_putc_at(s
, subtlv_len_pos
, subtlv_len
);
263 static int unpack_tlvs(enum isis_tlv_context context
, size_t avail_len
,
264 struct stream
*stream
, struct sbuf
*log
, void *dest
,
267 /* Functions related to TLVs 1 Area Addresses */
269 static struct isis_item
*copy_item_area_address(struct isis_item
*i
)
271 struct isis_area_address
*addr
= (struct isis_area_address
*)i
;
272 struct isis_area_address
*rv
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*rv
));
275 memcpy(rv
->addr
, addr
->addr
, addr
->len
);
276 return (struct isis_item
*)rv
;
279 static void format_item_area_address(uint16_t mtid
, struct isis_item
*i
,
280 struct sbuf
*buf
, int indent
)
282 struct isis_area_address
*addr
= (struct isis_area_address
*)i
;
284 sbuf_push(buf
, indent
, "Area Address: %s\n",
285 isonet_print(addr
->addr
, addr
->len
));
288 static void free_item_area_address(struct isis_item
*i
)
290 XFREE(MTYPE_ISIS_TLV
, i
);
293 static int pack_item_area_address(struct isis_item
*i
, struct stream
*s
)
295 struct isis_area_address
*addr
= (struct isis_area_address
*)i
;
297 if (STREAM_WRITEABLE(s
) < (unsigned)1 + addr
->len
)
299 stream_putc(s
, addr
->len
);
300 stream_put(s
, addr
->addr
, addr
->len
);
304 static int unpack_item_area_address(uint16_t mtid
, uint8_t len
,
305 struct stream
*s
, struct sbuf
*log
,
306 void *dest
, int indent
)
308 struct isis_tlvs
*tlvs
= dest
;
309 struct isis_area_address
*rv
= NULL
;
311 sbuf_push(log
, indent
, "Unpack area address...\n");
315 "Not enough data left. (Expected 1 byte of address length, got %" PRIu8
321 rv
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*rv
));
322 rv
->len
= stream_getc(s
);
324 if (len
< 1 + rv
->len
) {
325 sbuf_push(log
, indent
, "Not enough data left. (Expected %" PRIu8
326 " bytes of address, got %" PRIu8
")\n",
331 if (rv
->len
< 1 || rv
->len
> 20) {
332 sbuf_push(log
, indent
,
333 "Implausible area address length %" PRIu8
"\n",
338 stream_get(rv
->addr
, s
, rv
->len
);
340 format_item_area_address(ISIS_MT_IPV4_UNICAST
, (struct isis_item
*)rv
,
342 append_item(&tlvs
->area_addresses
, (struct isis_item
*)rv
);
345 XFREE(MTYPE_ISIS_TLV
, rv
);
349 /* Functions related to TLV 2 (Old-Style) IS Reach */
350 static struct isis_item
*copy_item_oldstyle_reach(struct isis_item
*i
)
352 struct isis_oldstyle_reach
*r
= (struct isis_oldstyle_reach
*)i
;
353 struct isis_oldstyle_reach
*rv
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*rv
));
355 memcpy(rv
->id
, r
->id
, 7);
356 rv
->metric
= r
->metric
;
357 return (struct isis_item
*)rv
;
360 static void format_item_oldstyle_reach(uint16_t mtid
, struct isis_item
*i
,
361 struct sbuf
*buf
, int indent
)
363 struct isis_oldstyle_reach
*r
= (struct isis_oldstyle_reach
*)i
;
365 sbuf_push(buf
, indent
, "IS Reachability: %s (Metric: %" PRIu8
")\n",
366 isis_format_id(r
->id
, 7), r
->metric
);
369 static void free_item_oldstyle_reach(struct isis_item
*i
)
371 XFREE(MTYPE_ISIS_TLV
, i
);
374 static int pack_item_oldstyle_reach(struct isis_item
*i
, struct stream
*s
)
376 struct isis_oldstyle_reach
*r
= (struct isis_oldstyle_reach
*)i
;
378 if (STREAM_WRITEABLE(s
) < 11)
381 stream_putc(s
, r
->metric
);
382 stream_putc(s
, 0x80); /* delay metric - unsupported */
383 stream_putc(s
, 0x80); /* expense metric - unsupported */
384 stream_putc(s
, 0x80); /* error metric - unsupported */
385 stream_put(s
, r
->id
, 7);
390 static int unpack_item_oldstyle_reach(uint16_t mtid
, uint8_t len
,
391 struct stream
*s
, struct sbuf
*log
,
392 void *dest
, int indent
)
394 struct isis_tlvs
*tlvs
= dest
;
396 sbuf_push(log
, indent
, "Unpack oldstyle reach...\n");
400 "Not enough data left.(Expected 11 bytes of reach information, got %" PRIu8
406 struct isis_oldstyle_reach
*rv
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*rv
));
407 rv
->metric
= stream_getc(s
);
408 if ((rv
->metric
& 0x3f) != rv
->metric
) {
409 sbuf_push(log
, indent
, "Metric has unplausible format\n");
412 stream_forward_getp(s
, 3); /* Skip other metrics */
413 stream_get(rv
->id
, s
, 7);
415 format_item_oldstyle_reach(mtid
, (struct isis_item
*)rv
, log
,
417 append_item(&tlvs
->oldstyle_reach
, (struct isis_item
*)rv
);
421 /* Functions related to TLV 6 LAN Neighbors */
422 static struct isis_item
*copy_item_lan_neighbor(struct isis_item
*i
)
424 struct isis_lan_neighbor
*n
= (struct isis_lan_neighbor
*)i
;
425 struct isis_lan_neighbor
*rv
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*rv
));
427 memcpy(rv
->mac
, n
->mac
, 6);
428 return (struct isis_item
*)rv
;
431 static void format_item_lan_neighbor(uint16_t mtid
, struct isis_item
*i
,
432 struct sbuf
*buf
, int indent
)
434 struct isis_lan_neighbor
*n
= (struct isis_lan_neighbor
*)i
;
436 sbuf_push(buf
, indent
, "LAN Neighbor: %s\n", isis_format_id(n
->mac
, 6));
439 static void free_item_lan_neighbor(struct isis_item
*i
)
441 XFREE(MTYPE_ISIS_TLV
, i
);
444 static int pack_item_lan_neighbor(struct isis_item
*i
, struct stream
*s
)
446 struct isis_lan_neighbor
*n
= (struct isis_lan_neighbor
*)i
;
448 if (STREAM_WRITEABLE(s
) < 6)
451 stream_put(s
, n
->mac
, 6);
456 static int unpack_item_lan_neighbor(uint16_t mtid
, uint8_t len
,
457 struct stream
*s
, struct sbuf
*log
,
458 void *dest
, int indent
)
460 struct isis_tlvs
*tlvs
= dest
;
462 sbuf_push(log
, indent
, "Unpack LAN neighbor...\n");
466 "Not enough data left.(Expected 6 bytes of mac, got %" PRIu8
472 struct isis_lan_neighbor
*rv
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*rv
));
473 stream_get(rv
->mac
, s
, 6);
475 format_item_lan_neighbor(mtid
, (struct isis_item
*)rv
, log
, indent
+ 2);
476 append_item(&tlvs
->lan_neighbor
, (struct isis_item
*)rv
);
480 /* Functions related to TLV 9 LSP Entry */
481 static struct isis_item
*copy_item_lsp_entry(struct isis_item
*i
)
483 struct isis_lsp_entry
*e
= (struct isis_lsp_entry
*)i
;
484 struct isis_lsp_entry
*rv
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*rv
));
486 rv
->rem_lifetime
= e
->rem_lifetime
;
487 memcpy(rv
->id
, e
->id
, sizeof(rv
->id
));
488 rv
->seqno
= e
->seqno
;
489 rv
->checksum
= e
->checksum
;
491 return (struct isis_item
*)rv
;
494 static void format_item_lsp_entry(uint16_t mtid
, struct isis_item
*i
,
495 struct sbuf
*buf
, int indent
)
497 struct isis_lsp_entry
*e
= (struct isis_lsp_entry
*)i
;
499 sbuf_push(buf
, indent
,
500 "LSP Entry: %s, seq 0x%08" PRIx32
", cksum 0x%04" PRIx16
501 ", lifetime %" PRIu16
"s\n",
502 isis_format_id(e
->id
, 8), e
->seqno
, e
->checksum
,
506 static void free_item_lsp_entry(struct isis_item
*i
)
508 XFREE(MTYPE_ISIS_TLV
, i
);
511 static int pack_item_lsp_entry(struct isis_item
*i
, struct stream
*s
)
513 struct isis_lsp_entry
*e
= (struct isis_lsp_entry
*)i
;
515 if (STREAM_WRITEABLE(s
) < 16)
518 stream_putw(s
, e
->rem_lifetime
);
519 stream_put(s
, e
->id
, 8);
520 stream_putl(s
, e
->seqno
);
521 stream_putw(s
, e
->checksum
);
526 static int unpack_item_lsp_entry(uint16_t mtid
, uint8_t len
, struct stream
*s
,
527 struct sbuf
*log
, void *dest
, int indent
)
529 struct isis_tlvs
*tlvs
= dest
;
531 sbuf_push(log
, indent
, "Unpack LSP entry...\n");
535 "Not enough data left. (Expected 16 bytes of LSP info, got %" PRIu8
,
540 struct isis_lsp_entry
*rv
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*rv
));
541 rv
->rem_lifetime
= stream_getw(s
);
542 stream_get(rv
->id
, s
, 8);
543 rv
->seqno
= stream_getl(s
);
544 rv
->checksum
= stream_getw(s
);
546 format_item_lsp_entry(mtid
, (struct isis_item
*)rv
, log
, indent
+ 2);
547 append_item(&tlvs
->lsp_entries
, (struct isis_item
*)rv
);
551 /* Functions related to TLVs 22/222 Extended Reach/MT Reach */
553 static struct isis_item
*copy_item_extended_reach(struct isis_item
*i
)
555 struct isis_extended_reach
*r
= (struct isis_extended_reach
*)i
;
556 struct isis_extended_reach
*rv
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*rv
));
558 memcpy(rv
->id
, r
->id
, 7);
559 rv
->metric
= r
->metric
;
561 if (r
->subtlvs
&& r
->subtlv_len
) {
562 rv
->subtlvs
= XCALLOC(MTYPE_ISIS_TLV
, r
->subtlv_len
);
563 memcpy(rv
->subtlvs
, r
->subtlvs
, r
->subtlv_len
);
564 rv
->subtlv_len
= r
->subtlv_len
;
567 return (struct isis_item
*)rv
;
570 static void format_item_extended_reach(uint16_t mtid
, struct isis_item
*i
,
571 struct sbuf
*buf
, int indent
)
573 struct isis_extended_reach
*r
= (struct isis_extended_reach
*)i
;
575 sbuf_push(buf
, indent
, "%s Reachability: %s (Metric: %u)",
576 (mtid
== ISIS_MT_IPV4_UNICAST
) ? "Extended" : "MT",
577 isis_format_id(r
->id
, 7), r
->metric
);
578 if (mtid
!= ISIS_MT_IPV4_UNICAST
)
579 sbuf_push(buf
, 0, " %s", isis_mtid2str(mtid
));
580 sbuf_push(buf
, 0, "\n");
582 if (r
->subtlv_len
&& r
->subtlvs
)
583 mpls_te_print_detail(buf
, indent
+ 2, r
->subtlvs
,
587 static void free_item_extended_reach(struct isis_item
*i
)
589 struct isis_extended_reach
*item
= (struct isis_extended_reach
*)i
;
590 XFREE(MTYPE_ISIS_TLV
, item
->subtlvs
);
591 XFREE(MTYPE_ISIS_TLV
, item
);
594 static int pack_item_extended_reach(struct isis_item
*i
, struct stream
*s
)
596 struct isis_extended_reach
*r
= (struct isis_extended_reach
*)i
;
598 if (STREAM_WRITEABLE(s
) < 11 + (unsigned)r
->subtlv_len
)
600 stream_put(s
, r
->id
, sizeof(r
->id
));
601 stream_put3(s
, r
->metric
);
602 stream_putc(s
, r
->subtlv_len
);
603 stream_put(s
, r
->subtlvs
, r
->subtlv_len
);
607 static int unpack_item_extended_reach(uint16_t mtid
, uint8_t len
,
608 struct stream
*s
, struct sbuf
*log
,
609 void *dest
, int indent
)
611 struct isis_tlvs
*tlvs
= dest
;
612 struct isis_extended_reach
*rv
= NULL
;
614 struct isis_item_list
*items
;
616 if (mtid
== ISIS_MT_IPV4_UNICAST
) {
617 items
= &tlvs
->extended_reach
;
619 items
= isis_get_mt_items(&tlvs
->mt_reach
, mtid
);
622 sbuf_push(log
, indent
, "Unpacking %s reachability...\n",
623 (mtid
== ISIS_MT_IPV4_UNICAST
) ? "extended" : "mt");
626 sbuf_push(log
, indent
,
627 "Not enough data left. (expected 11 or more bytes, got %"
633 rv
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*rv
));
634 stream_get(rv
->id
, s
, 7);
635 rv
->metric
= stream_get3(s
);
636 subtlv_len
= stream_getc(s
);
638 format_item_extended_reach(mtid
, (struct isis_item
*)rv
, log
,
641 if ((size_t)len
< ((size_t)11) + subtlv_len
) {
642 sbuf_push(log
, indent
,
643 "Not enough data left for subtlv size %" PRIu8
644 ", there are only %" PRIu8
" bytes left.\n",
645 subtlv_len
, len
- 11);
649 sbuf_push(log
, indent
, "Storing %" PRIu8
" bytes of subtlvs\n",
653 size_t subtlv_start
= stream_get_getp(s
);
655 if (unpack_tlvs(ISIS_CONTEXT_SUBTLV_NE_REACH
, subtlv_len
, s
,
656 log
, NULL
, indent
+ 4)) {
660 stream_set_getp(s
, subtlv_start
);
662 rv
->subtlvs
= XCALLOC(MTYPE_ISIS_TLV
, subtlv_len
);
663 stream_get(rv
->subtlvs
, s
, subtlv_len
);
664 rv
->subtlv_len
= subtlv_len
;
667 append_item(items
, (struct isis_item
*)rv
);
671 free_item_extended_reach((struct isis_item
*)rv
);
676 /* Functions related to TLV 128 (Old-Style) IP Reach */
677 static struct isis_item
*copy_item_oldstyle_ip_reach(struct isis_item
*i
)
679 struct isis_oldstyle_ip_reach
*r
= (struct isis_oldstyle_ip_reach
*)i
;
680 struct isis_oldstyle_ip_reach
*rv
=
681 XCALLOC(MTYPE_ISIS_TLV
, sizeof(*rv
));
683 rv
->metric
= r
->metric
;
684 rv
->prefix
= r
->prefix
;
685 return (struct isis_item
*)rv
;
688 static void format_item_oldstyle_ip_reach(uint16_t mtid
, struct isis_item
*i
,
689 struct sbuf
*buf
, int indent
)
691 struct isis_oldstyle_ip_reach
*r
= (struct isis_oldstyle_ip_reach
*)i
;
692 char prefixbuf
[PREFIX2STR_BUFFER
];
694 sbuf_push(buf
, indent
, "IP Reachability: %s (Metric: %" PRIu8
")\n",
695 prefix2str(&r
->prefix
, prefixbuf
, sizeof(prefixbuf
)),
699 static void free_item_oldstyle_ip_reach(struct isis_item
*i
)
701 XFREE(MTYPE_ISIS_TLV
, i
);
704 static int pack_item_oldstyle_ip_reach(struct isis_item
*i
, struct stream
*s
)
706 struct isis_oldstyle_ip_reach
*r
= (struct isis_oldstyle_ip_reach
*)i
;
708 if (STREAM_WRITEABLE(s
) < 12)
711 stream_putc(s
, r
->metric
);
712 stream_putc(s
, 0x80); /* delay metric - unsupported */
713 stream_putc(s
, 0x80); /* expense metric - unsupported */
714 stream_putc(s
, 0x80); /* error metric - unsupported */
715 stream_put(s
, &r
->prefix
.prefix
, 4);
718 masklen2ip(r
->prefix
.prefixlen
, &mask
);
719 stream_put(s
, &mask
, sizeof(mask
));
724 static int unpack_item_oldstyle_ip_reach(uint16_t mtid
, uint8_t len
,
725 struct stream
*s
, struct sbuf
*log
,
726 void *dest
, int indent
)
728 sbuf_push(log
, indent
, "Unpack oldstyle ip reach...\n");
732 "Not enough data left.(Expected 12 bytes of reach information, got %" PRIu8
738 struct isis_oldstyle_ip_reach
*rv
=
739 XCALLOC(MTYPE_ISIS_TLV
, sizeof(*rv
));
740 rv
->metric
= stream_getc(s
);
741 if ((rv
->metric
& 0x7f) != rv
->metric
) {
742 sbuf_push(log
, indent
, "Metric has unplausible format\n");
745 stream_forward_getp(s
, 3); /* Skip other metrics */
746 rv
->prefix
.family
= AF_INET
;
747 stream_get(&rv
->prefix
.prefix
, s
, 4);
750 stream_get(&mask
, s
, 4);
751 rv
->prefix
.prefixlen
= ip_masklen(mask
);
753 format_item_oldstyle_ip_reach(mtid
, (struct isis_item
*)rv
, log
,
755 append_item(dest
, (struct isis_item
*)rv
);
760 /* Functions related to TLV 129 protocols supported */
762 static void copy_tlv_protocols_supported(struct isis_protocols_supported
*src
,
763 struct isis_protocols_supported
*dest
)
765 if (!src
->protocols
|| !src
->count
)
767 dest
->count
= src
->count
;
768 dest
->protocols
= XCALLOC(MTYPE_ISIS_TLV
, src
->count
);
769 memcpy(dest
->protocols
, src
->protocols
, src
->count
);
772 static void format_tlv_protocols_supported(struct isis_protocols_supported
*p
,
773 struct sbuf
*buf
, int indent
)
775 if (!p
|| !p
->count
|| !p
->protocols
)
778 sbuf_push(buf
, indent
, "Protocols Supported: ");
779 for (uint8_t i
= 0; i
< p
->count
; i
++) {
780 sbuf_push(buf
, 0, "%s%s", nlpid2str(p
->protocols
[i
]),
781 (i
+ 1 < p
->count
) ? ", " : "");
783 sbuf_push(buf
, 0, "\n");
786 static void free_tlv_protocols_supported(struct isis_protocols_supported
*p
)
788 XFREE(MTYPE_ISIS_TLV
, p
->protocols
);
791 static int pack_tlv_protocols_supported(struct isis_protocols_supported
*p
,
794 if (!p
|| !p
->count
|| !p
->protocols
)
797 if (STREAM_WRITEABLE(s
) < (unsigned)(p
->count
+ 2))
800 stream_putc(s
, ISIS_TLV_PROTOCOLS_SUPPORTED
);
801 stream_putc(s
, p
->count
);
802 stream_put(s
, p
->protocols
, p
->count
);
806 static int unpack_tlv_protocols_supported(enum isis_tlv_context context
,
807 uint8_t tlv_type
, uint8_t tlv_len
,
808 struct stream
*s
, struct sbuf
*log
,
809 void *dest
, int indent
)
811 struct isis_tlvs
*tlvs
= dest
;
813 sbuf_push(log
, indent
, "Unpacking Protocols Supported TLV...\n");
815 sbuf_push(log
, indent
, "WARNING: No protocols included\n");
818 if (tlvs
->protocols_supported
.protocols
) {
821 "WARNING: protocols supported TLV present multiple times.\n");
822 stream_forward_getp(s
, tlv_len
);
826 tlvs
->protocols_supported
.count
= tlv_len
;
827 tlvs
->protocols_supported
.protocols
= XCALLOC(MTYPE_ISIS_TLV
, tlv_len
);
828 stream_get(tlvs
->protocols_supported
.protocols
, s
, tlv_len
);
830 format_tlv_protocols_supported(&tlvs
->protocols_supported
, log
,
835 /* Functions related to TLV 132 IPv4 Interface addresses */
836 static struct isis_item
*copy_item_ipv4_address(struct isis_item
*i
)
838 struct isis_ipv4_address
*a
= (struct isis_ipv4_address
*)i
;
839 struct isis_ipv4_address
*rv
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*rv
));
842 return (struct isis_item
*)rv
;
845 static void format_item_ipv4_address(uint16_t mtid
, struct isis_item
*i
,
846 struct sbuf
*buf
, int indent
)
848 struct isis_ipv4_address
*a
= (struct isis_ipv4_address
*)i
;
849 char addrbuf
[INET_ADDRSTRLEN
];
851 inet_ntop(AF_INET
, &a
->addr
, addrbuf
, sizeof(addrbuf
));
852 sbuf_push(buf
, indent
, "IPv4 Interface Address: %s\n", addrbuf
);
855 static void free_item_ipv4_address(struct isis_item
*i
)
857 XFREE(MTYPE_ISIS_TLV
, i
);
860 static int pack_item_ipv4_address(struct isis_item
*i
, struct stream
*s
)
862 struct isis_ipv4_address
*a
= (struct isis_ipv4_address
*)i
;
864 if (STREAM_WRITEABLE(s
) < 4)
867 stream_put(s
, &a
->addr
, 4);
872 static int unpack_item_ipv4_address(uint16_t mtid
, uint8_t len
,
873 struct stream
*s
, struct sbuf
*log
,
874 void *dest
, int indent
)
876 struct isis_tlvs
*tlvs
= dest
;
878 sbuf_push(log
, indent
, "Unpack IPv4 Interface address...\n");
882 "Not enough data left.(Expected 4 bytes of IPv4 address, got %" PRIu8
888 struct isis_ipv4_address
*rv
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*rv
));
889 stream_get(&rv
->addr
, s
, 4);
891 format_item_ipv4_address(mtid
, (struct isis_item
*)rv
, log
, indent
+ 2);
892 append_item(&tlvs
->ipv4_address
, (struct isis_item
*)rv
);
897 /* Functions related to TLV 232 IPv6 Interface addresses */
898 static struct isis_item
*copy_item_ipv6_address(struct isis_item
*i
)
900 struct isis_ipv6_address
*a
= (struct isis_ipv6_address
*)i
;
901 struct isis_ipv6_address
*rv
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*rv
));
904 return (struct isis_item
*)rv
;
907 static void format_item_ipv6_address(uint16_t mtid
, struct isis_item
*i
,
908 struct sbuf
*buf
, int indent
)
910 struct isis_ipv6_address
*a
= (struct isis_ipv6_address
*)i
;
911 char addrbuf
[INET6_ADDRSTRLEN
];
913 inet_ntop(AF_INET6
, &a
->addr
, addrbuf
, sizeof(addrbuf
));
914 sbuf_push(buf
, indent
, "IPv6 Interface Address: %s\n", addrbuf
);
917 static void free_item_ipv6_address(struct isis_item
*i
)
919 XFREE(MTYPE_ISIS_TLV
, i
);
922 static int pack_item_ipv6_address(struct isis_item
*i
, struct stream
*s
)
924 struct isis_ipv6_address
*a
= (struct isis_ipv6_address
*)i
;
926 if (STREAM_WRITEABLE(s
) < 16)
929 stream_put(s
, &a
->addr
, 16);
934 static int unpack_item_ipv6_address(uint16_t mtid
, uint8_t len
,
935 struct stream
*s
, struct sbuf
*log
,
936 void *dest
, int indent
)
938 struct isis_tlvs
*tlvs
= dest
;
940 sbuf_push(log
, indent
, "Unpack IPv6 Interface address...\n");
944 "Not enough data left.(Expected 16 bytes of IPv6 address, got %" PRIu8
950 struct isis_ipv6_address
*rv
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*rv
));
951 stream_get(&rv
->addr
, s
, 16);
953 format_item_ipv6_address(mtid
, (struct isis_item
*)rv
, log
, indent
+ 2);
954 append_item(&tlvs
->ipv6_address
, (struct isis_item
*)rv
);
959 /* Functions related to TLV 229 MT Router information */
960 static struct isis_item
*copy_item_mt_router_info(struct isis_item
*i
)
962 struct isis_mt_router_info
*info
= (struct isis_mt_router_info
*)i
;
963 struct isis_mt_router_info
*rv
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*rv
));
965 rv
->overload
= info
->overload
;
966 rv
->attached
= info
->attached
;
967 rv
->mtid
= info
->mtid
;
968 return (struct isis_item
*)rv
;
971 static void format_item_mt_router_info(uint16_t mtid
, struct isis_item
*i
,
972 struct sbuf
*buf
, int indent
)
974 struct isis_mt_router_info
*info
= (struct isis_mt_router_info
*)i
;
976 sbuf_push(buf
, indent
, "MT Router Info: %s%s%s\n",
977 isis_mtid2str(info
->mtid
),
978 info
->overload
? " Overload" : "",
979 info
->attached
? " Attached" : "");
982 static void free_item_mt_router_info(struct isis_item
*i
)
984 XFREE(MTYPE_ISIS_TLV
, i
);
987 static int pack_item_mt_router_info(struct isis_item
*i
, struct stream
*s
)
989 struct isis_mt_router_info
*info
= (struct isis_mt_router_info
*)i
;
991 if (STREAM_WRITEABLE(s
) < 2)
994 uint16_t entry
= info
->mtid
;
997 entry
|= ISIS_MT_OL_MASK
;
999 entry
|= ISIS_MT_AT_MASK
;
1001 stream_putw(s
, entry
);
1006 static int unpack_item_mt_router_info(uint16_t mtid
, uint8_t len
,
1007 struct stream
*s
, struct sbuf
*log
,
1008 void *dest
, int indent
)
1010 struct isis_tlvs
*tlvs
= dest
;
1012 sbuf_push(log
, indent
, "Unpack MT Router info...\n");
1016 "Not enough data left.(Expected 2 bytes of MT info, got %" PRIu8
1022 struct isis_mt_router_info
*rv
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*rv
));
1024 uint16_t entry
= stream_getw(s
);
1025 rv
->overload
= entry
& ISIS_MT_OL_MASK
;
1026 rv
->attached
= entry
& ISIS_MT_AT_MASK
;
1027 rv
->mtid
= entry
& ISIS_MT_MASK
;
1029 format_item_mt_router_info(mtid
, (struct isis_item
*)rv
, log
,
1031 append_item(&tlvs
->mt_router_info
, (struct isis_item
*)rv
);
1035 /* Functions related to TLV 134 TE Router ID */
1037 static struct in_addr
*copy_tlv_te_router_id(const struct in_addr
*id
)
1042 struct in_addr
*rv
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*rv
));
1043 memcpy(rv
, id
, sizeof(*rv
));
1047 static void format_tlv_te_router_id(const struct in_addr
*id
, struct sbuf
*buf
,
1053 char addrbuf
[INET_ADDRSTRLEN
];
1054 inet_ntop(AF_INET
, id
, addrbuf
, sizeof(addrbuf
));
1055 sbuf_push(buf
, indent
, "TE Router ID: %s\n", addrbuf
);
1058 static void free_tlv_te_router_id(struct in_addr
*id
)
1060 XFREE(MTYPE_ISIS_TLV
, id
);
1063 static int pack_tlv_te_router_id(const struct in_addr
*id
, struct stream
*s
)
1068 if (STREAM_WRITEABLE(s
) < (unsigned)(2 + sizeof(*id
)))
1071 stream_putc(s
, ISIS_TLV_TE_ROUTER_ID
);
1073 stream_put(s
, id
, 4);
1077 static int unpack_tlv_te_router_id(enum isis_tlv_context context
,
1078 uint8_t tlv_type
, uint8_t tlv_len
,
1079 struct stream
*s
, struct sbuf
*log
,
1080 void *dest
, int indent
)
1082 struct isis_tlvs
*tlvs
= dest
;
1084 sbuf_push(log
, indent
, "Unpacking TE Router ID TLV...\n");
1086 sbuf_push(log
, indent
, "WARNING: Length invalid\n");
1090 if (tlvs
->te_router_id
) {
1091 sbuf_push(log
, indent
,
1092 "WARNING: TE Router ID present multiple times.\n");
1093 stream_forward_getp(s
, tlv_len
);
1097 tlvs
->te_router_id
= XCALLOC(MTYPE_ISIS_TLV
, 4);
1098 stream_get(tlvs
->te_router_id
, s
, 4);
1099 format_tlv_te_router_id(tlvs
->te_router_id
, log
, indent
+ 2);
1104 /* Functions related to TLVs 135/235 extended IP reach/MT IP Reach */
1106 static struct isis_item
*copy_item_extended_ip_reach(struct isis_item
*i
)
1108 struct isis_extended_ip_reach
*r
= (struct isis_extended_ip_reach
*)i
;
1109 struct isis_extended_ip_reach
*rv
=
1110 XCALLOC(MTYPE_ISIS_TLV
, sizeof(*rv
));
1112 rv
->metric
= r
->metric
;
1114 rv
->prefix
= r
->prefix
;
1116 return (struct isis_item
*)rv
;
1119 static void format_item_extended_ip_reach(uint16_t mtid
, struct isis_item
*i
,
1120 struct sbuf
*buf
, int indent
)
1122 struct isis_extended_ip_reach
*r
= (struct isis_extended_ip_reach
*)i
;
1123 char prefixbuf
[PREFIX2STR_BUFFER
];
1125 sbuf_push(buf
, indent
, "%s IP Reachability: %s (Metric: %u)%s",
1126 (mtid
== ISIS_MT_IPV4_UNICAST
) ? "Extended" : "MT",
1127 prefix2str(&r
->prefix
, prefixbuf
, sizeof(prefixbuf
)), r
->metric
,
1128 r
->down
? " Down" : "");
1129 if (mtid
!= ISIS_MT_IPV4_UNICAST
)
1130 sbuf_push(buf
, 0, " %s", isis_mtid2str(mtid
));
1131 sbuf_push(buf
, 0, "\n");
1134 static void free_item_extended_ip_reach(struct isis_item
*i
)
1136 struct isis_extended_ip_reach
*item
=
1137 (struct isis_extended_ip_reach
*)i
;
1138 XFREE(MTYPE_ISIS_TLV
, item
);
1141 static int pack_item_extended_ip_reach(struct isis_item
*i
, struct stream
*s
)
1143 struct isis_extended_ip_reach
*r
= (struct isis_extended_ip_reach
*)i
;
1146 if (STREAM_WRITEABLE(s
) < 5)
1148 stream_putl(s
, r
->metric
);
1150 control
= r
->down
? ISIS_EXTENDED_IP_REACH_DOWN
: 0;
1151 control
|= r
->prefix
.prefixlen
;
1152 stream_putc(s
, control
);
1154 if (STREAM_WRITEABLE(s
) < (unsigned)PSIZE(r
->prefix
.prefixlen
))
1156 stream_put(s
, &r
->prefix
.prefix
.s_addr
, PSIZE(r
->prefix
.prefixlen
));
1160 static int unpack_item_extended_ip_reach(uint16_t mtid
, uint8_t len
,
1161 struct stream
*s
, struct sbuf
*log
,
1162 void *dest
, int indent
)
1164 struct isis_tlvs
*tlvs
= dest
;
1165 struct isis_extended_ip_reach
*rv
= NULL
;
1167 uint8_t control
, subtlv_len
;
1168 struct isis_item_list
*items
;
1170 if (mtid
== ISIS_MT_IPV4_UNICAST
) {
1171 items
= &tlvs
->extended_ip_reach
;
1173 items
= isis_get_mt_items(&tlvs
->mt_ip_reach
, mtid
);
1176 sbuf_push(log
, indent
, "Unpacking %s IPv4 reachability...\n",
1177 (mtid
== ISIS_MT_IPV4_UNICAST
) ? "extended" : "mt");
1180 if (len
< consume
) {
1181 sbuf_push(log
, indent
,
1182 "Not enough data left. (expected 5 or more bytes, got %" PRIu8
")\n",
1187 rv
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*rv
));
1189 rv
->metric
= stream_getl(s
);
1190 control
= stream_getc(s
);
1191 rv
->down
= (control
& ISIS_EXTENDED_IP_REACH_DOWN
);
1192 rv
->prefix
.family
= AF_INET
;
1193 rv
->prefix
.prefixlen
= control
& 0x3f;
1194 if (rv
->prefix
.prefixlen
> 32) {
1195 sbuf_push(log
, indent
, "Prefixlen %u is inplausible for IPv4\n",
1196 rv
->prefix
.prefixlen
);
1200 consume
+= PSIZE(rv
->prefix
.prefixlen
);
1201 if (len
< consume
) {
1202 sbuf_push(log
, indent
,
1203 "Expected %u bytes of prefix, but only %u bytes available.\n",
1204 PSIZE(rv
->prefix
.prefixlen
), len
- 5);
1207 stream_get(&rv
->prefix
.prefix
.s_addr
, s
, PSIZE(rv
->prefix
.prefixlen
));
1208 in_addr_t orig_prefix
= rv
->prefix
.prefix
.s_addr
;
1209 apply_mask_ipv4(&rv
->prefix
);
1210 if (orig_prefix
!= rv
->prefix
.prefix
.s_addr
)
1211 sbuf_push(log
, indent
+ 2,
1212 "WARNING: Prefix had hostbits set.\n");
1213 format_item_extended_ip_reach(mtid
, (struct isis_item
*)rv
, log
,
1216 if (control
& ISIS_EXTENDED_IP_REACH_SUBTLV
) {
1218 if (len
< consume
) {
1219 sbuf_push(log
, indent
,
1220 "Expected 1 byte of subtlv len, but no more data present.\n");
1223 subtlv_len
= stream_getc(s
);
1226 sbuf_push(log
, indent
+ 2,
1227 " WARNING: subtlv bit is set, but there are no subtlvs.\n");
1229 consume
+= subtlv_len
;
1230 if (len
< consume
) {
1231 sbuf_push(log
, indent
,
1233 " bytes of subtlvs, but only %u bytes available.\n",
1235 len
- 6 - PSIZE(rv
->prefix
.prefixlen
));
1238 sbuf_push(log
, indent
, "Skipping %" PRIu8
" bytes of subvls",
1240 stream_forward_getp(s
, subtlv_len
);
1243 append_item(items
, (struct isis_item
*)rv
);
1247 free_item_extended_ip_reach((struct isis_item
*)rv
);
1251 /* Functions related to TLV 137 Dynamic Hostname */
1253 static char *copy_tlv_dynamic_hostname(const char *hostname
)
1258 return XSTRDUP(MTYPE_ISIS_TLV
, hostname
);
1261 static void format_tlv_dynamic_hostname(const char *hostname
, struct sbuf
*buf
,
1267 sbuf_push(buf
, indent
, "Hostname: %s\n", hostname
);
1270 static void free_tlv_dynamic_hostname(char *hostname
)
1272 XFREE(MTYPE_ISIS_TLV
, hostname
);
1275 static int pack_tlv_dynamic_hostname(const char *hostname
, struct stream
*s
)
1280 uint8_t name_len
= strlen(hostname
);
1282 if (STREAM_WRITEABLE(s
) < (unsigned)(2 + name_len
))
1285 stream_putc(s
, ISIS_TLV_DYNAMIC_HOSTNAME
);
1286 stream_putc(s
, name_len
);
1287 stream_put(s
, hostname
, name_len
);
1291 static int unpack_tlv_dynamic_hostname(enum isis_tlv_context context
,
1292 uint8_t tlv_type
, uint8_t tlv_len
,
1293 struct stream
*s
, struct sbuf
*log
,
1294 void *dest
, int indent
)
1296 struct isis_tlvs
*tlvs
= dest
;
1298 sbuf_push(log
, indent
, "Unpacking Dynamic Hostname TLV...\n");
1300 sbuf_push(log
, indent
, "WARNING: No hostname included\n");
1304 if (tlvs
->hostname
) {
1305 sbuf_push(log
, indent
,
1306 "WARNING: Hostname present multiple times.\n");
1307 stream_forward_getp(s
, tlv_len
);
1311 tlvs
->hostname
= XCALLOC(MTYPE_ISIS_TLV
, tlv_len
+ 1);
1312 stream_get(tlvs
->hostname
, s
, tlv_len
);
1313 tlvs
->hostname
[tlv_len
] = '\0';
1316 for (uint8_t i
= 0; i
< tlv_len
; i
++) {
1317 if ((unsigned char)tlvs
->hostname
[i
] > 127
1318 || !isprint((int)tlvs
->hostname
[i
])) {
1320 tlvs
->hostname
[i
] = '?';
1326 "WARNING: Hostname contained non-printable/non-ascii characters.\n");
1332 /* Functions related to TLV 240 P2P Three-Way Adjacency */
1334 const char *isis_threeway_state_name(enum isis_threeway_state state
)
1337 case ISIS_THREEWAY_DOWN
:
1339 case ISIS_THREEWAY_INITIALIZING
:
1340 return "Initializing";
1341 case ISIS_THREEWAY_UP
:
1348 static struct isis_threeway_adj
*copy_tlv_threeway_adj(
1349 const struct isis_threeway_adj
*threeway_adj
)
1354 struct isis_threeway_adj
*rv
= XMALLOC(MTYPE_ISIS_TLV
, sizeof(*rv
));
1355 memcpy(rv
, threeway_adj
, sizeof(*rv
));
1360 static void format_tlv_threeway_adj(const struct isis_threeway_adj
*threeway_adj
,
1361 struct sbuf
*buf
, int indent
)
1366 sbuf_push(buf
, indent
, "P2P Three-Way Adjacency:\n");
1367 sbuf_push(buf
, indent
, " State: %s (%d)\n",
1368 isis_threeway_state_name(threeway_adj
->state
),
1369 threeway_adj
->state
);
1370 sbuf_push(buf
, indent
, " Extended Local Circuit ID: %" PRIu32
"\n",
1371 threeway_adj
->local_circuit_id
);
1372 if (!threeway_adj
->neighbor_set
)
1375 sbuf_push(buf
, indent
, " Neighbor System ID: %s\n",
1376 isis_format_id(threeway_adj
->neighbor_id
, 6));
1377 sbuf_push(buf
, indent
, " Neighbor Extended Circuit ID: %" PRIu32
"\n",
1378 threeway_adj
->neighbor_circuit_id
);
1381 static void free_tlv_threeway_adj(struct isis_threeway_adj
*threeway_adj
)
1383 XFREE(MTYPE_ISIS_TLV
, threeway_adj
);
1386 static int pack_tlv_threeway_adj(const struct isis_threeway_adj
*threeway_adj
,
1392 uint8_t tlv_len
= (threeway_adj
->neighbor_set
) ? 15 : 5;
1394 if (STREAM_WRITEABLE(s
) < (unsigned)(2 + tlv_len
))
1397 stream_putc(s
, ISIS_TLV_THREE_WAY_ADJ
);
1398 stream_putc(s
, tlv_len
);
1399 stream_putc(s
, threeway_adj
->state
);
1400 stream_putl(s
, threeway_adj
->local_circuit_id
);
1402 if (threeway_adj
->neighbor_set
) {
1403 stream_put(s
, threeway_adj
->neighbor_id
, 6);
1404 stream_putl(s
, threeway_adj
->neighbor_circuit_id
);
1410 static int unpack_tlv_threeway_adj(enum isis_tlv_context context
,
1411 uint8_t tlv_type
, uint8_t tlv_len
,
1412 struct stream
*s
, struct sbuf
*log
,
1413 void *dest
, int indent
)
1415 struct isis_tlvs
*tlvs
= dest
;
1417 sbuf_push(log
, indent
, "Unpacking P2P Three-Way Adjacency TLV...\n");
1418 if (tlv_len
!= 5 && tlv_len
!= 15) {
1419 sbuf_push(log
, indent
, "WARNING: Unexepected TLV size\n");
1420 stream_forward_getp(s
, tlv_len
);
1424 if (tlvs
->threeway_adj
) {
1425 sbuf_push(log
, indent
,
1426 "WARNING: P2P Three-Way Adjacency TLV present multiple times.\n");
1427 stream_forward_getp(s
, tlv_len
);
1431 tlvs
->threeway_adj
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*tlvs
->threeway_adj
));
1433 tlvs
->threeway_adj
->state
= stream_getc(s
);
1434 tlvs
->threeway_adj
->local_circuit_id
= stream_getl(s
);
1436 if (tlv_len
== 15) {
1437 tlvs
->threeway_adj
->neighbor_set
= true;
1438 stream_get(tlvs
->threeway_adj
->neighbor_id
, s
, 6);
1439 tlvs
->threeway_adj
->neighbor_circuit_id
= stream_getl(s
);
1445 /* Functions related to TLVs 236/237 IPv6/MT-IPv6 reach */
1447 static struct isis_item
*copy_item_ipv6_reach(struct isis_item
*i
)
1449 struct isis_ipv6_reach
*r
= (struct isis_ipv6_reach
*)i
;
1450 struct isis_ipv6_reach
*rv
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*rv
));
1451 rv
->metric
= r
->metric
;
1453 rv
->external
= r
->external
;
1454 rv
->prefix
= r
->prefix
;
1455 rv
->subtlvs
= copy_subtlvs(r
->subtlvs
);
1457 return (struct isis_item
*)rv
;
1460 static void format_item_ipv6_reach(uint16_t mtid
, struct isis_item
*i
,
1461 struct sbuf
*buf
, int indent
)
1463 struct isis_ipv6_reach
*r
= (struct isis_ipv6_reach
*)i
;
1464 char prefixbuf
[PREFIX2STR_BUFFER
];
1466 sbuf_push(buf
, indent
, "%sIPv6 Reachability: %s (Metric: %u)%s%s",
1467 (mtid
== ISIS_MT_IPV4_UNICAST
) ? "" : "MT ",
1468 prefix2str(&r
->prefix
, prefixbuf
, sizeof(prefixbuf
)),
1470 r
->down
? " Down" : "",
1471 r
->external
? " External" : "");
1472 if (mtid
!= ISIS_MT_IPV4_UNICAST
)
1473 sbuf_push(buf
, 0, " %s", isis_mtid2str(mtid
));
1474 sbuf_push(buf
, 0, "\n");
1477 sbuf_push(buf
, indent
, " Subtlvs:\n");
1478 format_subtlvs(r
->subtlvs
, buf
, indent
+ 4);
1482 static void free_item_ipv6_reach(struct isis_item
*i
)
1484 struct isis_ipv6_reach
*item
= (struct isis_ipv6_reach
*)i
;
1486 isis_free_subtlvs(item
->subtlvs
);
1487 XFREE(MTYPE_ISIS_TLV
, item
);
1490 static int pack_item_ipv6_reach(struct isis_item
*i
, struct stream
*s
)
1492 struct isis_ipv6_reach
*r
= (struct isis_ipv6_reach
*)i
;
1495 if (STREAM_WRITEABLE(s
) < 6)
1497 stream_putl(s
, r
->metric
);
1499 control
= r
->down
? ISIS_IPV6_REACH_DOWN
: 0;
1500 control
|= r
->external
? ISIS_IPV6_REACH_EXTERNAL
: 0;
1501 control
|= r
->subtlvs
? ISIS_IPV6_REACH_SUBTLV
: 0;
1503 stream_putc(s
, control
);
1504 stream_putc(s
, r
->prefix
.prefixlen
);
1506 if (STREAM_WRITEABLE(s
) < (unsigned)PSIZE(r
->prefix
.prefixlen
))
1508 stream_put(s
, &r
->prefix
.prefix
.s6_addr
, PSIZE(r
->prefix
.prefixlen
));
1511 return pack_subtlvs(r
->subtlvs
, s
);
1516 static int unpack_item_ipv6_reach(uint16_t mtid
, uint8_t len
, struct stream
*s
,
1517 struct sbuf
*log
, void *dest
, int indent
)
1519 struct isis_tlvs
*tlvs
= dest
;
1520 struct isis_ipv6_reach
*rv
= NULL
;
1522 uint8_t control
, subtlv_len
;
1523 struct isis_item_list
*items
;
1525 if (mtid
== ISIS_MT_IPV4_UNICAST
) {
1526 items
= &tlvs
->ipv6_reach
;
1528 items
= isis_get_mt_items(&tlvs
->mt_ipv6_reach
, mtid
);
1531 sbuf_push(log
, indent
, "Unpacking %sIPv6 reachability...\n",
1532 (mtid
== ISIS_MT_IPV4_UNICAST
) ? "" : "mt ");
1534 if (len
< consume
) {
1535 sbuf_push(log
, indent
,
1536 "Not enough data left. (expected 6 or more bytes, got %"
1542 rv
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*rv
));
1544 rv
->metric
= stream_getl(s
);
1545 control
= stream_getc(s
);
1546 rv
->down
= (control
& ISIS_IPV6_REACH_DOWN
);
1547 rv
->external
= (control
& ISIS_IPV6_REACH_EXTERNAL
);
1549 rv
->prefix
.family
= AF_INET6
;
1550 rv
->prefix
.prefixlen
= stream_getc(s
);
1551 if (rv
->prefix
.prefixlen
> 128) {
1552 sbuf_push(log
, indent
, "Prefixlen %u is inplausible for IPv6\n",
1553 rv
->prefix
.prefixlen
);
1557 consume
+= PSIZE(rv
->prefix
.prefixlen
);
1558 if (len
< consume
) {
1559 sbuf_push(log
, indent
,
1560 "Expected %u bytes of prefix, but only %u bytes available.\n",
1561 PSIZE(rv
->prefix
.prefixlen
), len
- 6);
1564 stream_get(&rv
->prefix
.prefix
.s6_addr
, s
, PSIZE(rv
->prefix
.prefixlen
));
1565 struct in6_addr orig_prefix
= rv
->prefix
.prefix
;
1566 apply_mask_ipv6(&rv
->prefix
);
1567 if (memcmp(&orig_prefix
, &rv
->prefix
.prefix
, sizeof(orig_prefix
)))
1568 sbuf_push(log
, indent
+ 2,
1569 "WARNING: Prefix had hostbits set.\n");
1570 format_item_ipv6_reach(mtid
, (struct isis_item
*)rv
, log
, indent
+ 2);
1572 if (control
& ISIS_IPV6_REACH_SUBTLV
) {
1574 if (len
< consume
) {
1575 sbuf_push(log
, indent
,
1576 "Expected 1 byte of subtlv len, but no more data persent.\n");
1579 subtlv_len
= stream_getc(s
);
1582 sbuf_push(log
, indent
+ 2,
1583 " WARNING: subtlv bit set, but there are no subtlvs.\n");
1585 consume
+= subtlv_len
;
1586 if (len
< consume
) {
1587 sbuf_push(log
, indent
,
1589 " bytes of subtlvs, but only %u bytes available.\n",
1591 len
- 6 - PSIZE(rv
->prefix
.prefixlen
));
1595 rv
->subtlvs
= isis_alloc_subtlvs();
1596 if (unpack_tlvs(ISIS_CONTEXT_SUBTLV_IPV6_REACH
, subtlv_len
, s
,
1597 log
, rv
->subtlvs
, indent
+ 4)) {
1602 append_item(items
, (struct isis_item
*)rv
);
1606 free_item_ipv6_reach((struct isis_item
*)rv
);
1610 /* Functions related to TLV 10 Authentication */
1611 static struct isis_item
*copy_item_auth(struct isis_item
*i
)
1613 struct isis_auth
*auth
= (struct isis_auth
*)i
;
1614 struct isis_auth
*rv
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*rv
));
1616 rv
->type
= auth
->type
;
1617 rv
->length
= auth
->length
;
1618 memcpy(rv
->value
, auth
->value
, sizeof(rv
->value
));
1619 return (struct isis_item
*)rv
;
1622 static void format_item_auth(uint16_t mtid
, struct isis_item
*i
,
1623 struct sbuf
*buf
, int indent
)
1625 struct isis_auth
*auth
= (struct isis_auth
*)i
;
1628 sbuf_push(buf
, indent
, "Authentication:\n");
1629 switch (auth
->type
) {
1630 case ISIS_PASSWD_TYPE_CLEARTXT
:
1631 zlog_sanitize(obuf
, sizeof(obuf
), auth
->value
, auth
->length
);
1632 sbuf_push(buf
, indent
, " Password: %s\n", obuf
);
1634 case ISIS_PASSWD_TYPE_HMAC_MD5
:
1635 for (unsigned int i
= 0; i
< 16; i
++) {
1636 snprintf(obuf
+ 2 * i
, sizeof(obuf
) - 2 * i
,
1637 "%02" PRIx8
, auth
->value
[i
]);
1639 sbuf_push(buf
, indent
, " HMAC-MD5: %s\n", obuf
);
1642 sbuf_push(buf
, indent
, " Unknown (%" PRIu8
")\n", auth
->type
);
1647 static void free_item_auth(struct isis_item
*i
)
1649 XFREE(MTYPE_ISIS_TLV
, i
);
1652 static int pack_item_auth(struct isis_item
*i
, struct stream
*s
)
1654 struct isis_auth
*auth
= (struct isis_auth
*)i
;
1656 if (STREAM_WRITEABLE(s
) < 1)
1658 stream_putc(s
, auth
->type
);
1660 switch (auth
->type
) {
1661 case ISIS_PASSWD_TYPE_CLEARTXT
:
1662 if (STREAM_WRITEABLE(s
) < auth
->length
)
1664 stream_put(s
, auth
->passwd
, auth
->length
);
1666 case ISIS_PASSWD_TYPE_HMAC_MD5
:
1667 if (STREAM_WRITEABLE(s
) < 16)
1669 auth
->offset
= stream_get_endp(s
);
1670 stream_put(s
, NULL
, 16);
1679 static int unpack_item_auth(uint16_t mtid
, uint8_t len
, struct stream
*s
,
1680 struct sbuf
*log
, void *dest
, int indent
)
1682 struct isis_tlvs
*tlvs
= dest
;
1684 sbuf_push(log
, indent
, "Unpack Auth TLV...\n");
1688 "Not enough data left.(Expected 1 bytes of auth type, got %" PRIu8
1694 struct isis_auth
*rv
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*rv
));
1696 rv
->type
= stream_getc(s
);
1697 rv
->length
= len
- 1;
1699 if (rv
->type
== ISIS_PASSWD_TYPE_HMAC_MD5
&& rv
->length
!= 16) {
1702 "Unexpected auth length for HMAC-MD5 (expected 16, got %" PRIu8
1705 XFREE(MTYPE_ISIS_TLV
, rv
);
1709 rv
->offset
= stream_get_getp(s
);
1710 stream_get(rv
->value
, s
, rv
->length
);
1711 format_item_auth(mtid
, (struct isis_item
*)rv
, log
, indent
+ 2);
1712 append_item(&tlvs
->isis_auth
, (struct isis_item
*)rv
);
1716 /* Functions relating to item TLVs */
1718 static void init_item_list(struct isis_item_list
*items
)
1721 items
->tail
= &items
->head
;
1725 static struct isis_item
*copy_item(enum isis_tlv_context context
,
1726 enum isis_tlv_type type
,
1727 struct isis_item
*item
)
1729 const struct tlv_ops
*ops
= tlv_table
[context
][type
];
1731 if (ops
&& ops
->copy_item
)
1732 return ops
->copy_item(item
);
1734 assert(!"Unknown item tlv type!");
1738 static void copy_items(enum isis_tlv_context context
, enum isis_tlv_type type
,
1739 struct isis_item_list
*src
, struct isis_item_list
*dest
)
1741 struct isis_item
*item
;
1743 init_item_list(dest
);
1745 for (item
= src
->head
; item
; item
= item
->next
) {
1746 append_item(dest
, copy_item(context
, type
, item
));
1750 static void format_item(uint16_t mtid
, enum isis_tlv_context context
,
1751 enum isis_tlv_type type
, struct isis_item
*i
,
1752 struct sbuf
*buf
, int indent
)
1754 const struct tlv_ops
*ops
= tlv_table
[context
][type
];
1756 if (ops
&& ops
->format_item
) {
1757 ops
->format_item(mtid
, i
, buf
, indent
);
1761 assert(!"Unknown item tlv type!");
1764 static void format_items_(uint16_t mtid
, enum isis_tlv_context context
,
1765 enum isis_tlv_type type
, struct isis_item_list
*items
,
1766 struct sbuf
*buf
, int indent
)
1768 struct isis_item
*i
;
1770 for (i
= items
->head
; i
; i
= i
->next
)
1771 format_item(mtid
, context
, type
, i
, buf
, indent
);
1773 #define format_items(...) format_items_(ISIS_MT_IPV4_UNICAST, __VA_ARGS__)
1775 static void free_item(enum isis_tlv_context tlv_context
,
1776 enum isis_tlv_type tlv_type
, struct isis_item
*item
)
1778 const struct tlv_ops
*ops
= tlv_table
[tlv_context
][tlv_type
];
1780 if (ops
&& ops
->free_item
) {
1781 ops
->free_item(item
);
1785 assert(!"Unknown item tlv type!");
1788 static void free_items(enum isis_tlv_context context
, enum isis_tlv_type type
,
1789 struct isis_item_list
*items
)
1791 struct isis_item
*item
, *next_item
;
1793 for (item
= items
->head
; item
; item
= next_item
) {
1794 next_item
= item
->next
;
1795 free_item(context
, type
, item
);
1799 static int pack_item(enum isis_tlv_context context
, enum isis_tlv_type type
,
1800 struct isis_item
*i
, struct stream
*s
,
1801 struct isis_tlvs
**fragment_tlvs
,
1802 struct pack_order_entry
*pe
, uint16_t mtid
)
1804 const struct tlv_ops
*ops
= tlv_table
[context
][type
];
1806 if (ops
&& ops
->pack_item
) {
1807 return ops
->pack_item(i
, s
);
1810 assert(!"Unknown item tlv type!");
1814 static void add_item_to_fragment(struct isis_item
*i
, struct pack_order_entry
*pe
,
1815 struct isis_tlvs
*fragment_tlvs
, uint16_t mtid
)
1817 struct isis_item_list
*l
;
1819 if (pe
->how_to_pack
== ISIS_ITEMS
) {
1820 l
= (struct isis_item_list
*)(((char *)fragment_tlvs
) + pe
->what_to_pack
);
1822 struct isis_mt_item_list
*m
;
1823 m
= (struct isis_mt_item_list
*)(((char *)fragment_tlvs
) + pe
->what_to_pack
);
1824 l
= isis_get_mt_items(m
, mtid
);
1827 append_item(l
, copy_item(pe
->context
, pe
->type
, i
));
1830 static int pack_items_(uint16_t mtid
, enum isis_tlv_context context
,
1831 enum isis_tlv_type type
, struct isis_item_list
*items
,
1832 struct stream
*s
, struct isis_tlvs
**fragment_tlvs
,
1833 struct pack_order_entry
*pe
,
1834 struct isis_tlvs
*(*new_fragment
)(struct list
*l
),
1835 struct list
*new_fragment_arg
)
1837 size_t len_pos
, last_len
, len
;
1838 struct isis_item
*item
= NULL
;
1845 if (STREAM_WRITEABLE(s
) < 2)
1848 stream_putc(s
, type
);
1849 len_pos
= stream_get_endp(s
);
1850 stream_putc(s
, 0); /* Put 0 as length for now */
1852 if (context
== ISIS_CONTEXT_LSP
&& IS_COMPAT_MT_TLV(type
)
1853 && mtid
!= ISIS_MT_IPV4_UNICAST
) {
1854 if (STREAM_WRITEABLE(s
) < 2)
1856 stream_putw(s
, mtid
);
1859 if (context
== ISIS_CONTEXT_LSP
&& type
== ISIS_TLV_OLDSTYLE_REACH
) {
1860 if (STREAM_WRITEABLE(s
) < 1)
1862 stream_putc(s
, 0); /* Virtual flag is set to 0 */
1866 for (item
= item
? item
: items
->head
; item
; item
= item
->next
) {
1867 rv
= pack_item(context
, type
, item
, s
, fragment_tlvs
, pe
, mtid
);
1871 len
= stream_get_endp(s
) - len_pos
- 1;
1873 /* Multiple auths don't go into one TLV, so always break */
1874 if (context
== ISIS_CONTEXT_LSP
&& type
== ISIS_TLV_AUTH
) {
1880 if (!last_len
) /* strange, not a single item fit */
1882 /* drop last tlv, otherwise, its too long */
1883 stream_set_endp(s
, len_pos
+ 1 + last_len
);
1889 add_item_to_fragment(item
, pe
, *fragment_tlvs
, mtid
);
1894 stream_putc_at(s
, len_pos
, len
);
1903 *fragment_tlvs
= new_fragment(new_fragment_arg
);
1906 #define pack_items(...) pack_items_(ISIS_MT_IPV4_UNICAST, __VA_ARGS__)
1908 static void append_item(struct isis_item_list
*dest
, struct isis_item
*item
)
1911 dest
->tail
= &(*dest
->tail
)->next
;
1915 static int unpack_item(uint16_t mtid
, enum isis_tlv_context context
,
1916 uint8_t tlv_type
, uint8_t len
, struct stream
*s
,
1917 struct sbuf
*log
, void *dest
, int indent
)
1919 const struct tlv_ops
*ops
= tlv_table
[context
][tlv_type
];
1921 if (ops
&& ops
->unpack_item
)
1922 return ops
->unpack_item(mtid
, len
, s
, log
, dest
, indent
);
1924 assert(!"Unknown item tlv type!");
1925 sbuf_push(log
, indent
, "Unknown item tlv type!\n");
1929 static int unpack_tlv_with_items(enum isis_tlv_context context
,
1930 uint8_t tlv_type
, uint8_t tlv_len
,
1931 struct stream
*s
, struct sbuf
*log
, void *dest
,
1939 tlv_start
= stream_get_getp(s
);
1942 if (context
== ISIS_CONTEXT_LSP
&& IS_COMPAT_MT_TLV(tlv_type
)) {
1944 sbuf_push(log
, indent
,
1945 "TLV is too short to contain MTID\n");
1948 mtid
= stream_getw(s
) & ISIS_MT_MASK
;
1950 sbuf_push(log
, indent
, "Unpacking as MT %s item TLV...\n",
1951 isis_mtid2str(mtid
));
1953 sbuf_push(log
, indent
, "Unpacking as item TLV...\n");
1954 mtid
= ISIS_MT_IPV4_UNICAST
;
1957 if (context
== ISIS_CONTEXT_LSP
1958 && tlv_type
== ISIS_TLV_OLDSTYLE_REACH
) {
1959 if (tlv_len
- tlv_pos
< 1) {
1960 sbuf_push(log
, indent
,
1961 "TLV is too short for old style reach\n");
1964 stream_forward_getp(s
, 1);
1968 if (context
== ISIS_CONTEXT_LSP
1969 && tlv_type
== ISIS_TLV_OLDSTYLE_IP_REACH
) {
1970 struct isis_tlvs
*tlvs
= dest
;
1971 dest
= &tlvs
->oldstyle_ip_reach
;
1972 } else if (context
== ISIS_CONTEXT_LSP
1973 && tlv_type
== ISIS_TLV_OLDSTYLE_IP_REACH_EXT
) {
1974 struct isis_tlvs
*tlvs
= dest
;
1975 dest
= &tlvs
->oldstyle_ip_reach_ext
;
1978 if (context
== ISIS_CONTEXT_LSP
1979 && tlv_type
== ISIS_TLV_MT_ROUTER_INFO
) {
1980 struct isis_tlvs
*tlvs
= dest
;
1981 tlvs
->mt_router_info_empty
= (tlv_pos
>= (size_t)tlv_len
);
1984 while (tlv_pos
< (size_t)tlv_len
) {
1985 rv
= unpack_item(mtid
, context
, tlv_type
, tlv_len
- tlv_pos
, s
,
1986 log
, dest
, indent
+ 2);
1990 tlv_pos
= stream_get_getp(s
) - tlv_start
;
1996 /* Functions to manipulate mt_item_lists */
1998 static int isis_mt_item_list_cmp(const struct isis_item_list
*a
,
1999 const struct isis_item_list
*b
)
2001 if (a
->mtid
< b
->mtid
)
2003 if (a
->mtid
> b
->mtid
)
2008 RB_PROTOTYPE(isis_mt_item_list
, isis_item_list
, mt_tree
, isis_mt_item_list_cmp
);
2009 RB_GENERATE(isis_mt_item_list
, isis_item_list
, mt_tree
, isis_mt_item_list_cmp
);
2011 struct isis_item_list
*isis_get_mt_items(struct isis_mt_item_list
*m
,
2014 struct isis_item_list
*rv
;
2016 rv
= isis_lookup_mt_items(m
, mtid
);
2018 rv
= XCALLOC(MTYPE_ISIS_MT_ITEM_LIST
, sizeof(*rv
));
2021 RB_INSERT(isis_mt_item_list
, m
, rv
);
2027 struct isis_item_list
*isis_lookup_mt_items(struct isis_mt_item_list
*m
,
2030 struct isis_item_list key
= {.mtid
= mtid
};
2032 return RB_FIND(isis_mt_item_list
, m
, &key
);
2035 static void free_mt_items(enum isis_tlv_context context
,
2036 enum isis_tlv_type type
, struct isis_mt_item_list
*m
)
2038 struct isis_item_list
*n
, *nnext
;
2040 RB_FOREACH_SAFE (n
, isis_mt_item_list
, m
, nnext
) {
2041 free_items(context
, type
, n
);
2042 RB_REMOVE(isis_mt_item_list
, m
, n
);
2043 XFREE(MTYPE_ISIS_MT_ITEM_LIST
, n
);
2047 static void format_mt_items(enum isis_tlv_context context
,
2048 enum isis_tlv_type type
,
2049 struct isis_mt_item_list
*m
, struct sbuf
*buf
,
2052 struct isis_item_list
*n
;
2054 RB_FOREACH (n
, isis_mt_item_list
, m
) {
2055 format_items_(n
->mtid
, context
, type
, n
, buf
, indent
);
2059 static int pack_mt_items(enum isis_tlv_context context
, enum isis_tlv_type type
,
2060 struct isis_mt_item_list
*m
, struct stream
*s
,
2061 struct isis_tlvs
**fragment_tlvs
,
2062 struct pack_order_entry
*pe
,
2063 struct isis_tlvs
*(*new_fragment
)(struct list
*l
),
2064 struct list
*new_fragment_arg
)
2066 struct isis_item_list
*n
;
2068 RB_FOREACH (n
, isis_mt_item_list
, m
) {
2071 rv
= pack_items_(n
->mtid
, context
, type
, n
, s
, fragment_tlvs
,
2072 pe
, new_fragment
, new_fragment_arg
);
2080 static void copy_mt_items(enum isis_tlv_context context
,
2081 enum isis_tlv_type type
,
2082 struct isis_mt_item_list
*src
,
2083 struct isis_mt_item_list
*dest
)
2085 struct isis_item_list
*n
;
2087 RB_INIT(isis_mt_item_list
, dest
);
2089 RB_FOREACH (n
, isis_mt_item_list
, src
) {
2090 copy_items(context
, type
, n
, isis_get_mt_items(dest
, n
->mtid
));
2094 /* Functions related to tlvs in general */
2096 struct isis_tlvs
*isis_alloc_tlvs(void)
2098 struct isis_tlvs
*result
;
2100 result
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*result
));
2102 init_item_list(&result
->isis_auth
);
2103 init_item_list(&result
->area_addresses
);
2104 init_item_list(&result
->mt_router_info
);
2105 init_item_list(&result
->oldstyle_reach
);
2106 init_item_list(&result
->lan_neighbor
);
2107 init_item_list(&result
->lsp_entries
);
2108 init_item_list(&result
->extended_reach
);
2109 RB_INIT(isis_mt_item_list
, &result
->mt_reach
);
2110 init_item_list(&result
->oldstyle_ip_reach
);
2111 init_item_list(&result
->oldstyle_ip_reach_ext
);
2112 init_item_list(&result
->ipv4_address
);
2113 init_item_list(&result
->ipv6_address
);
2114 init_item_list(&result
->extended_ip_reach
);
2115 RB_INIT(isis_mt_item_list
, &result
->mt_ip_reach
);
2116 init_item_list(&result
->ipv6_reach
);
2117 RB_INIT(isis_mt_item_list
, &result
->mt_ipv6_reach
);
2122 struct isis_tlvs
*isis_copy_tlvs(struct isis_tlvs
*tlvs
)
2124 struct isis_tlvs
*rv
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*rv
));
2126 copy_items(ISIS_CONTEXT_LSP
, ISIS_TLV_AUTH
, &tlvs
->isis_auth
,
2129 copy_items(ISIS_CONTEXT_LSP
, ISIS_TLV_AREA_ADDRESSES
,
2130 &tlvs
->area_addresses
, &rv
->area_addresses
);
2132 copy_items(ISIS_CONTEXT_LSP
, ISIS_TLV_MT_ROUTER_INFO
,
2133 &tlvs
->mt_router_info
, &rv
->mt_router_info
);
2135 tlvs
->mt_router_info_empty
= rv
->mt_router_info_empty
;
2137 copy_items(ISIS_CONTEXT_LSP
, ISIS_TLV_OLDSTYLE_REACH
,
2138 &tlvs
->oldstyle_reach
, &rv
->oldstyle_reach
);
2140 copy_items(ISIS_CONTEXT_LSP
, ISIS_TLV_LAN_NEIGHBORS
,
2141 &tlvs
->lan_neighbor
, &rv
->lan_neighbor
);
2143 copy_items(ISIS_CONTEXT_LSP
, ISIS_TLV_LSP_ENTRY
, &tlvs
->lsp_entries
,
2146 copy_items(ISIS_CONTEXT_LSP
, ISIS_TLV_EXTENDED_REACH
,
2147 &tlvs
->extended_reach
, &rv
->extended_reach
);
2149 copy_mt_items(ISIS_CONTEXT_LSP
, ISIS_TLV_MT_REACH
, &tlvs
->mt_reach
,
2152 copy_items(ISIS_CONTEXT_LSP
, ISIS_TLV_OLDSTYLE_IP_REACH
,
2153 &tlvs
->oldstyle_ip_reach
, &rv
->oldstyle_ip_reach
);
2155 copy_tlv_protocols_supported(&tlvs
->protocols_supported
,
2156 &rv
->protocols_supported
);
2158 copy_items(ISIS_CONTEXT_LSP
, ISIS_TLV_OLDSTYLE_IP_REACH_EXT
,
2159 &tlvs
->oldstyle_ip_reach_ext
, &rv
->oldstyle_ip_reach_ext
);
2161 copy_items(ISIS_CONTEXT_LSP
, ISIS_TLV_IPV4_ADDRESS
, &tlvs
->ipv4_address
,
2164 copy_items(ISIS_CONTEXT_LSP
, ISIS_TLV_IPV6_ADDRESS
, &tlvs
->ipv6_address
,
2167 rv
->te_router_id
= copy_tlv_te_router_id(tlvs
->te_router_id
);
2169 copy_items(ISIS_CONTEXT_LSP
, ISIS_TLV_EXTENDED_IP_REACH
,
2170 &tlvs
->extended_ip_reach
, &rv
->extended_ip_reach
);
2172 copy_mt_items(ISIS_CONTEXT_LSP
, ISIS_TLV_MT_IP_REACH
,
2173 &tlvs
->mt_ip_reach
, &rv
->mt_ip_reach
);
2175 rv
->hostname
= copy_tlv_dynamic_hostname(tlvs
->hostname
);
2177 copy_items(ISIS_CONTEXT_LSP
, ISIS_TLV_IPV6_REACH
, &tlvs
->ipv6_reach
,
2180 copy_mt_items(ISIS_CONTEXT_LSP
, ISIS_TLV_MT_IPV6_REACH
,
2181 &tlvs
->mt_ipv6_reach
, &rv
->mt_ipv6_reach
);
2183 rv
->threeway_adj
= copy_tlv_threeway_adj(tlvs
->threeway_adj
);
2188 static void format_tlvs(struct isis_tlvs
*tlvs
, struct sbuf
*buf
, int indent
)
2190 format_tlv_protocols_supported(&tlvs
->protocols_supported
, buf
, indent
);
2192 format_items(ISIS_CONTEXT_LSP
, ISIS_TLV_AUTH
, &tlvs
->isis_auth
, buf
,
2195 format_items(ISIS_CONTEXT_LSP
, ISIS_TLV_AREA_ADDRESSES
,
2196 &tlvs
->area_addresses
, buf
, indent
);
2198 if (tlvs
->mt_router_info_empty
) {
2199 sbuf_push(buf
, indent
, "MT Router Info: None\n");
2201 format_items(ISIS_CONTEXT_LSP
, ISIS_TLV_MT_ROUTER_INFO
,
2202 &tlvs
->mt_router_info
, buf
, indent
);
2205 format_items(ISIS_CONTEXT_LSP
, ISIS_TLV_OLDSTYLE_REACH
,
2206 &tlvs
->oldstyle_reach
, buf
, indent
);
2208 format_items(ISIS_CONTEXT_LSP
, ISIS_TLV_LAN_NEIGHBORS
,
2209 &tlvs
->lan_neighbor
, buf
, indent
);
2211 format_items(ISIS_CONTEXT_LSP
, ISIS_TLV_LSP_ENTRY
, &tlvs
->lsp_entries
,
2214 format_tlv_dynamic_hostname(tlvs
->hostname
, buf
, indent
);
2215 format_tlv_te_router_id(tlvs
->te_router_id
, buf
, indent
);
2217 format_items(ISIS_CONTEXT_LSP
, ISIS_TLV_EXTENDED_REACH
,
2218 &tlvs
->extended_reach
, buf
, indent
);
2220 format_mt_items(ISIS_CONTEXT_LSP
, ISIS_TLV_MT_REACH
, &tlvs
->mt_reach
,
2223 format_items(ISIS_CONTEXT_LSP
, ISIS_TLV_OLDSTYLE_IP_REACH
,
2224 &tlvs
->oldstyle_ip_reach
, buf
, indent
);
2226 format_items(ISIS_CONTEXT_LSP
, ISIS_TLV_OLDSTYLE_IP_REACH_EXT
,
2227 &tlvs
->oldstyle_ip_reach_ext
, buf
, indent
);
2229 format_items(ISIS_CONTEXT_LSP
, ISIS_TLV_IPV4_ADDRESS
,
2230 &tlvs
->ipv4_address
, buf
, indent
);
2232 format_items(ISIS_CONTEXT_LSP
, ISIS_TLV_IPV6_ADDRESS
,
2233 &tlvs
->ipv6_address
, buf
, indent
);
2235 format_items(ISIS_CONTEXT_LSP
, ISIS_TLV_EXTENDED_IP_REACH
,
2236 &tlvs
->extended_ip_reach
, buf
, indent
);
2238 format_mt_items(ISIS_CONTEXT_LSP
, ISIS_TLV_MT_IP_REACH
,
2239 &tlvs
->mt_ip_reach
, buf
, indent
);
2241 format_items(ISIS_CONTEXT_LSP
, ISIS_TLV_IPV6_REACH
, &tlvs
->ipv6_reach
,
2244 format_mt_items(ISIS_CONTEXT_LSP
, ISIS_TLV_MT_IPV6_REACH
,
2245 &tlvs
->mt_ipv6_reach
, buf
, indent
);
2247 format_tlv_threeway_adj(tlvs
->threeway_adj
, buf
, indent
);
2250 const char *isis_format_tlvs(struct isis_tlvs
*tlvs
)
2252 static struct sbuf buf
;
2254 if (!sbuf_buf(&buf
))
2255 sbuf_init(&buf
, NULL
, 0);
2258 format_tlvs(tlvs
, &buf
, 0);
2259 return sbuf_buf(&buf
);
2262 void isis_free_tlvs(struct isis_tlvs
*tlvs
)
2267 free_items(ISIS_CONTEXT_LSP
, ISIS_TLV_AUTH
, &tlvs
->isis_auth
);
2268 free_items(ISIS_CONTEXT_LSP
, ISIS_TLV_AREA_ADDRESSES
,
2269 &tlvs
->area_addresses
);
2270 free_items(ISIS_CONTEXT_LSP
, ISIS_TLV_MT_ROUTER_INFO
,
2271 &tlvs
->mt_router_info
);
2272 free_items(ISIS_CONTEXT_LSP
, ISIS_TLV_OLDSTYLE_REACH
,
2273 &tlvs
->oldstyle_reach
);
2274 free_items(ISIS_CONTEXT_LSP
, ISIS_TLV_LAN_NEIGHBORS
,
2275 &tlvs
->lan_neighbor
);
2276 free_items(ISIS_CONTEXT_LSP
, ISIS_TLV_LSP_ENTRY
, &tlvs
->lsp_entries
);
2277 free_items(ISIS_CONTEXT_LSP
, ISIS_TLV_EXTENDED_REACH
,
2278 &tlvs
->extended_reach
);
2279 free_mt_items(ISIS_CONTEXT_LSP
, ISIS_TLV_MT_REACH
, &tlvs
->mt_reach
);
2280 free_items(ISIS_CONTEXT_LSP
, ISIS_TLV_OLDSTYLE_IP_REACH
,
2281 &tlvs
->oldstyle_ip_reach
);
2282 free_tlv_protocols_supported(&tlvs
->protocols_supported
);
2283 free_items(ISIS_CONTEXT_LSP
, ISIS_TLV_OLDSTYLE_IP_REACH_EXT
,
2284 &tlvs
->oldstyle_ip_reach_ext
);
2285 free_items(ISIS_CONTEXT_LSP
, ISIS_TLV_IPV4_ADDRESS
,
2286 &tlvs
->ipv4_address
);
2287 free_items(ISIS_CONTEXT_LSP
, ISIS_TLV_IPV6_ADDRESS
,
2288 &tlvs
->ipv6_address
);
2289 free_tlv_te_router_id(tlvs
->te_router_id
);
2290 free_items(ISIS_CONTEXT_LSP
, ISIS_TLV_EXTENDED_IP_REACH
,
2291 &tlvs
->extended_ip_reach
);
2292 free_mt_items(ISIS_CONTEXT_LSP
, ISIS_TLV_MT_IP_REACH
,
2293 &tlvs
->mt_ip_reach
);
2294 free_tlv_dynamic_hostname(tlvs
->hostname
);
2295 free_items(ISIS_CONTEXT_LSP
, ISIS_TLV_IPV6_REACH
, &tlvs
->ipv6_reach
);
2296 free_mt_items(ISIS_CONTEXT_LSP
, ISIS_TLV_MT_IPV6_REACH
,
2297 &tlvs
->mt_ipv6_reach
);
2298 free_tlv_threeway_adj(tlvs
->threeway_adj
);
2300 XFREE(MTYPE_ISIS_TLV
, tlvs
);
2303 static void add_padding(struct stream
*s
)
2305 while (STREAM_WRITEABLE(s
)) {
2306 if (STREAM_WRITEABLE(s
) == 1)
2308 uint32_t padding_len
= STREAM_WRITEABLE(s
) - 2;
2310 if (padding_len
> 255) {
2311 if (padding_len
== 256)
2317 stream_putc(s
, ISIS_TLV_PADDING
);
2318 stream_putc(s
, padding_len
);
2319 stream_put(s
, NULL
, padding_len
);
2323 #define LSP_REM_LIFETIME_OFF 10
2324 #define LSP_CHECKSUM_OFF 24
2325 static void safe_auth_md5(struct stream
*s
, uint16_t *checksum
,
2326 uint16_t *rem_lifetime
)
2328 memcpy(rem_lifetime
, STREAM_DATA(s
) + LSP_REM_LIFETIME_OFF
,
2329 sizeof(*rem_lifetime
));
2330 memset(STREAM_DATA(s
) + LSP_REM_LIFETIME_OFF
, 0, sizeof(*rem_lifetime
));
2331 memcpy(checksum
, STREAM_DATA(s
) + LSP_CHECKSUM_OFF
, sizeof(*checksum
));
2332 memset(STREAM_DATA(s
) + LSP_CHECKSUM_OFF
, 0, sizeof(*checksum
));
2335 static void restore_auth_md5(struct stream
*s
, uint16_t checksum
,
2336 uint16_t rem_lifetime
)
2338 memcpy(STREAM_DATA(s
) + LSP_REM_LIFETIME_OFF
, &rem_lifetime
,
2339 sizeof(rem_lifetime
));
2340 memcpy(STREAM_DATA(s
) + LSP_CHECKSUM_OFF
, &checksum
, sizeof(checksum
));
2343 static void update_auth_hmac_md5(struct isis_auth
*auth
, struct stream
*s
,
2347 uint16_t checksum
, rem_lifetime
;
2350 safe_auth_md5(s
, &checksum
, &rem_lifetime
);
2352 memset(STREAM_DATA(s
) + auth
->offset
, 0, 16);
2353 hmac_md5(STREAM_DATA(s
), stream_get_endp(s
), auth
->passwd
,
2354 auth
->plength
, digest
);
2355 memcpy(auth
->value
, digest
, 16);
2356 memcpy(STREAM_DATA(s
) + auth
->offset
, digest
, 16);
2359 restore_auth_md5(s
, checksum
, rem_lifetime
);
2362 static void update_auth(struct isis_tlvs
*tlvs
, struct stream
*s
, bool is_lsp
)
2364 struct isis_auth
*auth_head
= (struct isis_auth
*)tlvs
->isis_auth
.head
;
2366 for (struct isis_auth
*auth
= auth_head
; auth
; auth
= auth
->next
) {
2367 if (auth
->type
== ISIS_PASSWD_TYPE_HMAC_MD5
)
2368 update_auth_hmac_md5(auth
, s
, is_lsp
);
2372 static int handle_pack_entry(struct pack_order_entry
*pe
,
2373 struct isis_tlvs
*tlvs
, struct stream
*stream
,
2374 struct isis_tlvs
**fragment_tlvs
,
2375 struct isis_tlvs
*(*new_fragment
)(struct list
*l
),
2376 struct list
*new_fragment_arg
)
2380 if (pe
->how_to_pack
== ISIS_ITEMS
) {
2381 struct isis_item_list
*l
;
2382 l
= (struct isis_item_list
*)(((char *)tlvs
)
2383 + pe
->what_to_pack
);
2384 rv
= pack_items(pe
->context
, pe
->type
, l
, stream
, fragment_tlvs
,
2385 pe
, new_fragment
, new_fragment_arg
);
2387 struct isis_mt_item_list
*l
;
2388 l
= (struct isis_mt_item_list
*)(((char *)tlvs
)
2389 + pe
->what_to_pack
);
2390 rv
= pack_mt_items(pe
->context
, pe
->type
, l
, stream
,
2391 fragment_tlvs
, pe
, new_fragment
,
2398 static int pack_tlvs(struct isis_tlvs
*tlvs
, struct stream
*stream
,
2399 struct isis_tlvs
*fragment_tlvs
,
2400 struct isis_tlvs
*(*new_fragment
)(struct list
*l
),
2401 struct list
*new_fragment_arg
)
2405 /* When fragmenting, don't add auth as it's already accounted for in the
2406 * size we are given. */
2407 if (!fragment_tlvs
) {
2408 rv
= pack_items(ISIS_CONTEXT_LSP
, ISIS_TLV_AUTH
,
2409 &tlvs
->isis_auth
, stream
, NULL
, NULL
, NULL
,
2415 rv
= pack_tlv_protocols_supported(&tlvs
->protocols_supported
, stream
);
2418 if (fragment_tlvs
) {
2419 copy_tlv_protocols_supported(
2420 &tlvs
->protocols_supported
,
2421 &fragment_tlvs
->protocols_supported
);
2424 rv
= pack_items(ISIS_CONTEXT_LSP
, ISIS_TLV_AREA_ADDRESSES
,
2425 &tlvs
->area_addresses
, stream
, NULL
, NULL
, NULL
, NULL
);
2428 if (fragment_tlvs
) {
2429 copy_items(ISIS_CONTEXT_LSP
, ISIS_TLV_AREA_ADDRESSES
,
2430 &tlvs
->area_addresses
,
2431 &fragment_tlvs
->area_addresses
);
2435 if (tlvs
->mt_router_info_empty
) {
2436 if (STREAM_WRITEABLE(stream
) < 2)
2438 stream_putc(stream
, ISIS_TLV_MT_ROUTER_INFO
);
2439 stream_putc(stream
, 0);
2441 fragment_tlvs
->mt_router_info_empty
= true;
2443 rv
= pack_items(ISIS_CONTEXT_LSP
, ISIS_TLV_MT_ROUTER_INFO
,
2444 &tlvs
->mt_router_info
, stream
, NULL
, NULL
, NULL
,
2448 if (fragment_tlvs
) {
2449 copy_items(ISIS_CONTEXT_LSP
, ISIS_TLV_MT_ROUTER_INFO
,
2450 &tlvs
->mt_router_info
,
2451 &fragment_tlvs
->mt_router_info
);
2455 rv
= pack_tlv_dynamic_hostname(tlvs
->hostname
, stream
);
2459 fragment_tlvs
->hostname
=
2460 copy_tlv_dynamic_hostname(tlvs
->hostname
);
2462 rv
= pack_tlv_te_router_id(tlvs
->te_router_id
, stream
);
2465 if (fragment_tlvs
) {
2466 fragment_tlvs
->te_router_id
=
2467 copy_tlv_te_router_id(tlvs
->te_router_id
);
2470 rv
= pack_tlv_threeway_adj(tlvs
->threeway_adj
, stream
);
2473 if (fragment_tlvs
) {
2474 fragment_tlvs
->threeway_adj
=
2475 copy_tlv_threeway_adj(tlvs
->threeway_adj
);
2478 for (size_t pack_idx
= 0; pack_idx
< array_size(pack_order
);
2480 rv
= handle_pack_entry(&pack_order
[pack_idx
], tlvs
, stream
,
2481 fragment_tlvs
? &fragment_tlvs
: NULL
,
2482 new_fragment
, new_fragment_arg
);
2491 int isis_pack_tlvs(struct isis_tlvs
*tlvs
, struct stream
*stream
,
2492 size_t len_pointer
, bool pad
, bool is_lsp
)
2496 rv
= pack_tlvs(tlvs
, stream
, NULL
, NULL
, NULL
);
2501 add_padding(stream
);
2503 if (len_pointer
!= (size_t)-1) {
2504 stream_putw_at(stream
, len_pointer
, stream_get_endp(stream
));
2507 update_auth(tlvs
, stream
, is_lsp
);
2512 static struct isis_tlvs
*new_fragment(struct list
*l
)
2514 struct isis_tlvs
*rv
= isis_alloc_tlvs();
2516 listnode_add(l
, rv
);
2520 struct list
*isis_fragment_tlvs(struct isis_tlvs
*tlvs
, size_t size
)
2522 struct stream
*dummy_stream
= stream_new(size
);
2523 struct list
*rv
= list_new();
2524 struct isis_tlvs
*fragment_tlvs
= new_fragment(rv
);
2526 if (pack_tlvs(tlvs
, dummy_stream
, fragment_tlvs
, new_fragment
, rv
)) {
2527 struct listnode
*node
;
2528 for (ALL_LIST_ELEMENTS_RO(rv
, node
, fragment_tlvs
))
2529 isis_free_tlvs(fragment_tlvs
);
2530 list_delete_and_null(&rv
);
2533 stream_free(dummy_stream
);
2537 static int unpack_tlv_unknown(enum isis_tlv_context context
, uint8_t tlv_type
,
2538 uint8_t tlv_len
, struct stream
*s
,
2539 struct sbuf
*log
, int indent
)
2541 stream_forward_getp(s
, tlv_len
);
2542 sbuf_push(log
, indent
,
2543 "Skipping unknown TLV %" PRIu8
" (%" PRIu8
" bytes)\n",
2548 static int unpack_tlv(enum isis_tlv_context context
, size_t avail_len
,
2549 struct stream
*stream
, struct sbuf
*log
, void *dest
,
2552 uint8_t tlv_type
, tlv_len
;
2553 const struct tlv_ops
*ops
;
2555 sbuf_push(log
, indent
, "Unpacking TLV...\n");
2557 if (avail_len
< 2) {
2560 "Available data %zu too short to contain a TLV header.\n",
2565 tlv_type
= stream_getc(stream
);
2566 tlv_len
= stream_getc(stream
);
2568 sbuf_push(log
, indent
+ 2,
2569 "Found TLV of type %" PRIu8
" and len %" PRIu8
".\n",
2572 if (avail_len
< ((size_t)tlv_len
) + 2) {
2573 sbuf_push(log
, indent
+ 2,
2574 "Available data %zu too short for claimed TLV len %" PRIu8
".\n",
2575 avail_len
- 2, tlv_len
);
2579 ops
= tlv_table
[context
][tlv_type
];
2580 if (ops
&& ops
->unpack
) {
2581 return ops
->unpack(context
, tlv_type
, tlv_len
, stream
, log
,
2585 return unpack_tlv_unknown(context
, tlv_type
, tlv_len
, stream
, log
,
2589 static int unpack_tlvs(enum isis_tlv_context context
, size_t avail_len
,
2590 struct stream
*stream
, struct sbuf
*log
, void *dest
,
2594 size_t tlv_start
, tlv_pos
;
2596 tlv_start
= stream_get_getp(stream
);
2599 sbuf_push(log
, indent
, "Unpacking %zu bytes of %s...\n", avail_len
,
2600 (context
== ISIS_CONTEXT_LSP
) ? "TLVs" : "sub-TLVs");
2602 while (tlv_pos
< avail_len
) {
2603 rv
= unpack_tlv(context
, avail_len
- tlv_pos
, stream
, log
, dest
,
2608 tlv_pos
= stream_get_getp(stream
) - tlv_start
;
2614 int isis_unpack_tlvs(size_t avail_len
, struct stream
*stream
,
2615 struct isis_tlvs
**dest
, const char **log
)
2617 static struct sbuf logbuf
;
2620 struct isis_tlvs
*result
;
2622 if (!sbuf_buf(&logbuf
))
2623 sbuf_init(&logbuf
, NULL
, 0);
2625 sbuf_reset(&logbuf
);
2626 if (avail_len
> STREAM_READABLE(stream
)) {
2627 sbuf_push(&logbuf
, indent
,
2628 "Stream doesn't contain sufficient data. "
2629 "Claimed %zu, available %zu\n",
2630 avail_len
, STREAM_READABLE(stream
));
2634 result
= isis_alloc_tlvs();
2635 rv
= unpack_tlvs(ISIS_CONTEXT_LSP
, avail_len
, stream
, &logbuf
, result
,
2638 *log
= sbuf_buf(&logbuf
);
2644 #define TLV_OPS(_name_, _desc_) \
2645 static const struct tlv_ops tlv_##_name_##_ops = { \
2646 .name = _desc_, .unpack = unpack_tlv_##_name_, \
2649 #define ITEM_TLV_OPS(_name_, _desc_) \
2650 static const struct tlv_ops tlv_##_name_##_ops = { \
2652 .unpack = unpack_tlv_with_items, \
2654 .pack_item = pack_item_##_name_, \
2655 .free_item = free_item_##_name_, \
2656 .unpack_item = unpack_item_##_name_, \
2657 .format_item = format_item_##_name_, \
2658 .copy_item = copy_item_##_name_}
2660 #define SUBTLV_OPS(_name_, _desc_) \
2661 static const struct tlv_ops subtlv_##_name_##_ops = { \
2662 .name = _desc_, .unpack = unpack_subtlv_##_name_, \
2665 ITEM_TLV_OPS(area_address
, "TLV 1 Area Addresses");
2666 ITEM_TLV_OPS(oldstyle_reach
, "TLV 2 IS Reachability");
2667 ITEM_TLV_OPS(lan_neighbor
, "TLV 6 LAN Neighbors");
2668 ITEM_TLV_OPS(lsp_entry
, "TLV 9 LSP Entries");
2669 ITEM_TLV_OPS(auth
, "TLV 10 IS-IS Auth");
2670 ITEM_TLV_OPS(extended_reach
, "TLV 22 Extended Reachability");
2671 ITEM_TLV_OPS(oldstyle_ip_reach
, "TLV 128/130 IP Reachability");
2672 TLV_OPS(protocols_supported
, "TLV 129 Protocols Supported");
2673 ITEM_TLV_OPS(ipv4_address
, "TLV 132 IPv4 Interface Address");
2674 TLV_OPS(te_router_id
, "TLV 134 TE Router ID");
2675 ITEM_TLV_OPS(extended_ip_reach
, "TLV 135 Extended IP Reachability");
2676 TLV_OPS(dynamic_hostname
, "TLV 137 Dynamic Hostname");
2677 ITEM_TLV_OPS(mt_router_info
, "TLV 229 MT Router Information");
2678 TLV_OPS(threeway_adj
, "TLV 240 P2P Three-Way Adjacency");
2679 ITEM_TLV_OPS(ipv6_address
, "TLV 232 IPv6 Interface Address");
2680 ITEM_TLV_OPS(ipv6_reach
, "TLV 236 IPv6 Reachability");
2682 SUBTLV_OPS(ipv6_source_prefix
, "Sub-TLV 22 IPv6 Source Prefix");
2684 static const struct tlv_ops
*tlv_table
[ISIS_CONTEXT_MAX
][ISIS_TLV_MAX
] = {
2685 [ISIS_CONTEXT_LSP
] = {
2686 [ISIS_TLV_AREA_ADDRESSES
] = &tlv_area_address_ops
,
2687 [ISIS_TLV_OLDSTYLE_REACH
] = &tlv_oldstyle_reach_ops
,
2688 [ISIS_TLV_LAN_NEIGHBORS
] = &tlv_lan_neighbor_ops
,
2689 [ISIS_TLV_LSP_ENTRY
] = &tlv_lsp_entry_ops
,
2690 [ISIS_TLV_AUTH
] = &tlv_auth_ops
,
2691 [ISIS_TLV_EXTENDED_REACH
] = &tlv_extended_reach_ops
,
2692 [ISIS_TLV_MT_REACH
] = &tlv_extended_reach_ops
,
2693 [ISIS_TLV_OLDSTYLE_IP_REACH
] = &tlv_oldstyle_ip_reach_ops
,
2694 [ISIS_TLV_PROTOCOLS_SUPPORTED
] = &tlv_protocols_supported_ops
,
2695 [ISIS_TLV_OLDSTYLE_IP_REACH_EXT
] = &tlv_oldstyle_ip_reach_ops
,
2696 [ISIS_TLV_IPV4_ADDRESS
] = &tlv_ipv4_address_ops
,
2697 [ISIS_TLV_TE_ROUTER_ID
] = &tlv_te_router_id_ops
,
2698 [ISIS_TLV_EXTENDED_IP_REACH
] = &tlv_extended_ip_reach_ops
,
2699 [ISIS_TLV_MT_IP_REACH
] = &tlv_extended_ip_reach_ops
,
2700 [ISIS_TLV_DYNAMIC_HOSTNAME
] = &tlv_dynamic_hostname_ops
,
2701 [ISIS_TLV_MT_ROUTER_INFO
] = &tlv_mt_router_info_ops
,
2702 [ISIS_TLV_THREE_WAY_ADJ
] = &tlv_threeway_adj_ops
,
2703 [ISIS_TLV_IPV6_ADDRESS
] = &tlv_ipv6_address_ops
,
2704 [ISIS_TLV_IPV6_REACH
] = &tlv_ipv6_reach_ops
,
2705 [ISIS_TLV_MT_IPV6_REACH
] = &tlv_ipv6_reach_ops
,
2707 [ISIS_CONTEXT_SUBTLV_NE_REACH
] = {},
2708 [ISIS_CONTEXT_SUBTLV_IP_REACH
] = {},
2709 [ISIS_CONTEXT_SUBTLV_IPV6_REACH
] = {
2710 [ISIS_SUBTLV_IPV6_SOURCE_PREFIX
] = &subtlv_ipv6_source_prefix_ops
,
2714 /* Accessor functions */
2716 void isis_tlvs_add_auth(struct isis_tlvs
*tlvs
, struct isis_passwd
*passwd
)
2718 free_items(ISIS_CONTEXT_LSP
, ISIS_TLV_AUTH
, &tlvs
->isis_auth
);
2719 init_item_list(&tlvs
->isis_auth
);
2721 if (passwd
->type
== ISIS_PASSWD_TYPE_UNUSED
)
2724 struct isis_auth
*auth
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*auth
));
2726 auth
->type
= passwd
->type
;
2728 auth
->plength
= passwd
->len
;
2729 memcpy(auth
->passwd
, passwd
->passwd
,
2730 MIN(sizeof(auth
->passwd
), sizeof(passwd
->passwd
)));
2732 if (auth
->type
== ISIS_PASSWD_TYPE_CLEARTXT
) {
2733 auth
->length
= passwd
->len
;
2734 memcpy(auth
->value
, passwd
->passwd
,
2735 MIN(sizeof(auth
->value
), sizeof(passwd
->passwd
)));
2738 append_item(&tlvs
->isis_auth
, (struct isis_item
*)auth
);
2741 void isis_tlvs_add_area_addresses(struct isis_tlvs
*tlvs
,
2742 struct list
*addresses
)
2744 struct listnode
*node
;
2745 struct area_addr
*area_addr
;
2747 for (ALL_LIST_ELEMENTS_RO(addresses
, node
, area_addr
)) {
2748 struct isis_area_address
*a
=
2749 XCALLOC(MTYPE_ISIS_TLV
, sizeof(*a
));
2751 a
->len
= area_addr
->addr_len
;
2752 memcpy(a
->addr
, area_addr
->area_addr
, 20);
2753 append_item(&tlvs
->area_addresses
, (struct isis_item
*)a
);
2757 void isis_tlvs_add_lan_neighbors(struct isis_tlvs
*tlvs
, struct list
*neighbors
)
2759 struct listnode
*node
;
2762 for (ALL_LIST_ELEMENTS_RO(neighbors
, node
, snpa
)) {
2763 struct isis_lan_neighbor
*n
=
2764 XCALLOC(MTYPE_ISIS_TLV
, sizeof(*n
));
2766 memcpy(n
->mac
, snpa
, 6);
2767 append_item(&tlvs
->lan_neighbor
, (struct isis_item
*)n
);
2771 void isis_tlvs_set_protocols_supported(struct isis_tlvs
*tlvs
,
2772 struct nlpids
*nlpids
)
2774 tlvs
->protocols_supported
.count
= nlpids
->count
;
2775 if (tlvs
->protocols_supported
.protocols
)
2776 XFREE(MTYPE_ISIS_TLV
, tlvs
->protocols_supported
.protocols
);
2777 if (nlpids
->count
) {
2778 tlvs
->protocols_supported
.protocols
=
2779 XCALLOC(MTYPE_ISIS_TLV
, nlpids
->count
);
2780 memcpy(tlvs
->protocols_supported
.protocols
, nlpids
->nlpids
,
2783 tlvs
->protocols_supported
.protocols
= NULL
;
2787 void isis_tlvs_add_mt_router_info(struct isis_tlvs
*tlvs
, uint16_t mtid
,
2788 bool overload
, bool attached
)
2790 struct isis_mt_router_info
*i
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*i
));
2792 i
->overload
= overload
;
2793 i
->attached
= attached
;
2795 append_item(&tlvs
->mt_router_info
, (struct isis_item
*)i
);
2798 void isis_tlvs_add_ipv4_address(struct isis_tlvs
*tlvs
, struct in_addr
*addr
)
2800 struct isis_ipv4_address
*a
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*a
));
2802 append_item(&tlvs
->ipv4_address
, (struct isis_item
*)a
);
2806 void isis_tlvs_add_ipv4_addresses(struct isis_tlvs
*tlvs
,
2807 struct list
*addresses
)
2809 struct listnode
*node
;
2810 struct prefix_ipv4
*ip_addr
;
2811 unsigned int addr_count
= 0;
2813 for (ALL_LIST_ELEMENTS_RO(addresses
, node
, ip_addr
)) {
2814 isis_tlvs_add_ipv4_address(tlvs
, &ip_addr
->prefix
);
2816 if (addr_count
>= 63)
2821 void isis_tlvs_add_ipv6_addresses(struct isis_tlvs
*tlvs
,
2822 struct list
*addresses
)
2824 struct listnode
*node
;
2825 struct prefix_ipv6
*ip_addr
;
2827 for (ALL_LIST_ELEMENTS_RO(addresses
, node
, ip_addr
)) {
2828 struct isis_ipv6_address
*a
=
2829 XCALLOC(MTYPE_ISIS_TLV
, sizeof(*a
));
2831 a
->addr
= ip_addr
->prefix
;
2832 append_item(&tlvs
->ipv6_address
, (struct isis_item
*)a
);
2836 typedef bool (*auth_validator_func
)(struct isis_passwd
*passwd
,
2837 struct stream
*stream
,
2838 struct isis_auth
*auth
, bool is_lsp
);
2840 static bool auth_validator_cleartxt(struct isis_passwd
*passwd
,
2841 struct stream
*stream
,
2842 struct isis_auth
*auth
, bool is_lsp
)
2844 return (auth
->length
== passwd
->len
2845 && !memcmp(auth
->value
, passwd
->passwd
, passwd
->len
));
2848 static bool auth_validator_hmac_md5(struct isis_passwd
*passwd
,
2849 struct stream
*stream
,
2850 struct isis_auth
*auth
, bool is_lsp
)
2854 uint16_t rem_lifetime
;
2857 safe_auth_md5(stream
, &checksum
, &rem_lifetime
);
2859 memset(STREAM_DATA(stream
) + auth
->offset
, 0, 16);
2860 hmac_md5(STREAM_DATA(stream
), stream_get_endp(stream
), passwd
->passwd
,
2861 passwd
->len
, digest
);
2862 memcpy(STREAM_DATA(stream
) + auth
->offset
, auth
->value
, 16);
2864 bool rv
= !memcmp(digest
, auth
->value
, 16);
2867 restore_auth_md5(stream
, checksum
, rem_lifetime
);
2872 static const auth_validator_func auth_validators
[] = {
2873 [ISIS_PASSWD_TYPE_CLEARTXT
] = auth_validator_cleartxt
,
2874 [ISIS_PASSWD_TYPE_HMAC_MD5
] = auth_validator_hmac_md5
,
2877 bool isis_tlvs_auth_is_valid(struct isis_tlvs
*tlvs
, struct isis_passwd
*passwd
,
2878 struct stream
*stream
, bool is_lsp
)
2880 /* If no auth is set, always pass authentication */
2884 /* If we don't known how to validate the auth, return invalid */
2885 if (passwd
->type
>= array_size(auth_validators
)
2886 || !auth_validators
[passwd
->type
])
2889 struct isis_auth
*auth_head
= (struct isis_auth
*)tlvs
->isis_auth
.head
;
2890 struct isis_auth
*auth
;
2891 for (auth
= auth_head
; auth
; auth
= auth
->next
) {
2892 if (auth
->type
== passwd
->type
)
2896 /* If matching auth TLV could not be found, return invalid */
2900 /* Perform validation and return result */
2901 return auth_validators
[passwd
->type
](passwd
, stream
, auth
, is_lsp
);
2904 bool isis_tlvs_area_addresses_match(struct isis_tlvs
*tlvs
,
2905 struct list
*addresses
)
2907 struct isis_area_address
*addr_head
;
2909 addr_head
= (struct isis_area_address
*)tlvs
->area_addresses
.head
;
2910 for (struct isis_area_address
*addr
= addr_head
; addr
;
2911 addr
= addr
->next
) {
2912 struct listnode
*node
;
2913 struct area_addr
*a
;
2915 for (ALL_LIST_ELEMENTS_RO(addresses
, node
, a
)) {
2916 if (a
->addr_len
== addr
->len
2917 && !memcmp(a
->area_addr
, addr
->addr
, addr
->len
))
2925 static void tlvs_area_addresses_to_adj(struct isis_tlvs
*tlvs
,
2926 struct isis_adjacency
*adj
,
2929 if (adj
->area_address_count
!= tlvs
->area_addresses
.count
) {
2931 adj
->area_address_count
= tlvs
->area_addresses
.count
;
2932 adj
->area_addresses
= XREALLOC(
2933 MTYPE_ISIS_ADJACENCY_INFO
, adj
->area_addresses
,
2934 adj
->area_address_count
* sizeof(*adj
->area_addresses
));
2937 struct isis_area_address
*addr
= NULL
;
2938 for (unsigned int i
= 0; i
< tlvs
->area_addresses
.count
; i
++) {
2940 addr
= (struct isis_area_address
*)
2941 tlvs
->area_addresses
.head
;
2945 if (adj
->area_addresses
[i
].addr_len
== addr
->len
2946 && !memcmp(adj
->area_addresses
[i
].area_addr
, addr
->addr
,
2952 adj
->area_addresses
[i
].addr_len
= addr
->len
;
2953 memcpy(adj
->area_addresses
[i
].area_addr
, addr
->addr
, addr
->len
);
2957 static void tlvs_protocols_supported_to_adj(struct isis_tlvs
*tlvs
,
2958 struct isis_adjacency
*adj
,
2961 bool ipv4_supported
= false, ipv6_supported
= false;
2963 for (uint8_t i
= 0; i
< tlvs
->protocols_supported
.count
; i
++) {
2964 if (tlvs
->protocols_supported
.protocols
[i
] == NLPID_IP
)
2965 ipv4_supported
= true;
2966 if (tlvs
->protocols_supported
.protocols
[i
] == NLPID_IPV6
)
2967 ipv6_supported
= true;
2970 struct nlpids reduced
= {};
2972 if (ipv4_supported
&& ipv6_supported
) {
2974 reduced
.nlpids
[0] = NLPID_IP
;
2975 reduced
.nlpids
[1] = NLPID_IPV6
;
2976 } else if (ipv4_supported
) {
2978 reduced
.nlpids
[0] = NLPID_IP
;
2979 } else if (ipv6_supported
) {
2981 reduced
.nlpids
[1] = NLPID_IPV6
;
2986 if (adj
->nlpids
.count
== reduced
.count
2987 && !memcmp(adj
->nlpids
.nlpids
, reduced
.nlpids
, reduced
.count
))
2991 adj
->nlpids
.count
= reduced
.count
;
2992 memcpy(adj
->nlpids
.nlpids
, reduced
.nlpids
, reduced
.count
);
2995 static void tlvs_ipv4_addresses_to_adj(struct isis_tlvs
*tlvs
,
2996 struct isis_adjacency
*adj
,
2999 if (adj
->ipv4_address_count
!= tlvs
->ipv4_address
.count
) {
3001 adj
->ipv4_address_count
= tlvs
->ipv4_address
.count
;
3002 adj
->ipv4_addresses
= XREALLOC(
3003 MTYPE_ISIS_ADJACENCY_INFO
, adj
->ipv4_addresses
,
3004 adj
->ipv4_address_count
* sizeof(*adj
->ipv4_addresses
));
3007 struct isis_ipv4_address
*addr
= NULL
;
3008 for (unsigned int i
= 0; i
< tlvs
->ipv4_address
.count
; i
++) {
3010 addr
= (struct isis_ipv4_address
*)
3011 tlvs
->ipv4_address
.head
;
3015 if (!memcmp(&adj
->ipv4_addresses
[i
], &addr
->addr
,
3016 sizeof(addr
->addr
)))
3020 adj
->ipv4_addresses
[i
] = addr
->addr
;
3024 static void tlvs_ipv6_addresses_to_adj(struct isis_tlvs
*tlvs
,
3025 struct isis_adjacency
*adj
,
3028 if (adj
->ipv6_address_count
!= tlvs
->ipv6_address
.count
) {
3030 adj
->ipv6_address_count
= tlvs
->ipv6_address
.count
;
3031 adj
->ipv6_addresses
= XREALLOC(
3032 MTYPE_ISIS_ADJACENCY_INFO
, adj
->ipv6_addresses
,
3033 adj
->ipv6_address_count
* sizeof(*adj
->ipv6_addresses
));
3036 struct isis_ipv6_address
*addr
= NULL
;
3037 for (unsigned int i
= 0; i
< tlvs
->ipv6_address
.count
; i
++) {
3039 addr
= (struct isis_ipv6_address
*)
3040 tlvs
->ipv6_address
.head
;
3044 if (!memcmp(&adj
->ipv6_addresses
[i
], &addr
->addr
,
3045 sizeof(addr
->addr
)))
3049 adj
->ipv6_addresses
[i
] = addr
->addr
;
3053 void isis_tlvs_to_adj(struct isis_tlvs
*tlvs
, struct isis_adjacency
*adj
,
3058 tlvs_area_addresses_to_adj(tlvs
, adj
, changed
);
3059 tlvs_protocols_supported_to_adj(tlvs
, adj
, changed
);
3060 tlvs_ipv4_addresses_to_adj(tlvs
, adj
, changed
);
3061 tlvs_ipv6_addresses_to_adj(tlvs
, adj
, changed
);
3064 bool isis_tlvs_own_snpa_found(struct isis_tlvs
*tlvs
, uint8_t *snpa
)
3066 struct isis_lan_neighbor
*ne_head
;
3068 ne_head
= (struct isis_lan_neighbor
*)tlvs
->lan_neighbor
.head
;
3069 for (struct isis_lan_neighbor
*ne
= ne_head
; ne
; ne
= ne
->next
) {
3070 if (!memcmp(ne
->mac
, snpa
, ETH_ALEN
))
3077 void isis_tlvs_add_lsp_entry(struct isis_tlvs
*tlvs
, struct isis_lsp
*lsp
)
3079 struct isis_lsp_entry
*entry
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*entry
));
3081 entry
->rem_lifetime
= lsp
->hdr
.rem_lifetime
;
3082 memcpy(entry
->id
, lsp
->hdr
.lsp_id
, ISIS_SYS_ID_LEN
+ 2);
3083 entry
->checksum
= lsp
->hdr
.checksum
;
3084 entry
->seqno
= lsp
->hdr
.seqno
;
3087 append_item(&tlvs
->lsp_entries
, (struct isis_item
*)entry
);
3090 void isis_tlvs_add_csnp_entries(struct isis_tlvs
*tlvs
, uint8_t *start_id
,
3091 uint8_t *stop_id
, uint16_t num_lsps
,
3092 dict_t
*lspdb
, struct isis_lsp
**last_lsp
)
3094 dnode_t
*first
= dict_lower_bound(lspdb
, start_id
);
3098 dnode_t
*last
= dict_upper_bound(lspdb
, stop_id
);
3099 dnode_t
*curr
= first
;
3101 isis_tlvs_add_lsp_entry(tlvs
, first
->dict_data
);
3102 *last_lsp
= first
->dict_data
;
3105 curr
= dict_next(lspdb
, curr
);
3107 isis_tlvs_add_lsp_entry(tlvs
, curr
->dict_data
);
3108 *last_lsp
= curr
->dict_data
;
3110 if (curr
== last
|| tlvs
->lsp_entries
.count
== num_lsps
)
3115 void isis_tlvs_set_dynamic_hostname(struct isis_tlvs
*tlvs
,
3116 const char *hostname
)
3118 XFREE(MTYPE_ISIS_TLV
, tlvs
->hostname
);
3120 tlvs
->hostname
= XSTRDUP(MTYPE_ISIS_TLV
, hostname
);
3123 void isis_tlvs_set_te_router_id(struct isis_tlvs
*tlvs
,
3124 const struct in_addr
*id
)
3126 XFREE(MTYPE_ISIS_TLV
, tlvs
->te_router_id
);
3129 tlvs
->te_router_id
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*id
));
3130 memcpy(tlvs
->te_router_id
, id
, sizeof(*id
));
3133 void isis_tlvs_add_oldstyle_ip_reach(struct isis_tlvs
*tlvs
,
3134 struct prefix_ipv4
*dest
, uint8_t metric
)
3136 struct isis_oldstyle_ip_reach
*r
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*r
));
3139 memcpy(&r
->prefix
, dest
, sizeof(*dest
));
3140 apply_mask_ipv4(&r
->prefix
);
3141 append_item(&tlvs
->oldstyle_ip_reach
, (struct isis_item
*)r
);
3144 void isis_tlvs_add_extended_ip_reach(struct isis_tlvs
*tlvs
,
3145 struct prefix_ipv4
*dest
, uint32_t metric
)
3147 struct isis_extended_ip_reach
*r
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*r
));
3150 memcpy(&r
->prefix
, dest
, sizeof(*dest
));
3151 apply_mask_ipv4(&r
->prefix
);
3152 append_item(&tlvs
->extended_ip_reach
, (struct isis_item
*)r
);
3155 void isis_tlvs_add_ipv6_reach(struct isis_tlvs
*tlvs
, uint16_t mtid
,
3156 struct prefix_ipv6
*dest
, uint32_t metric
)
3158 struct isis_ipv6_reach
*r
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*r
));
3161 memcpy(&r
->prefix
, dest
, sizeof(*dest
));
3162 apply_mask_ipv6(&r
->prefix
);
3164 struct isis_item_list
*l
;
3165 l
= (mtid
== ISIS_MT_IPV4_UNICAST
)
3167 : isis_get_mt_items(&tlvs
->mt_ipv6_reach
, mtid
);
3168 append_item(l
, (struct isis_item
*)r
);
3171 void isis_tlvs_add_oldstyle_reach(struct isis_tlvs
*tlvs
, uint8_t *id
,
3174 struct isis_oldstyle_reach
*r
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*r
));
3177 memcpy(r
->id
, id
, sizeof(r
->id
));
3178 append_item(&tlvs
->oldstyle_reach
, (struct isis_item
*)r
);
3181 void isis_tlvs_add_extended_reach(struct isis_tlvs
*tlvs
, uint16_t mtid
,
3182 uint8_t *id
, uint32_t metric
,
3183 uint8_t *subtlvs
, uint8_t subtlv_len
)
3185 struct isis_extended_reach
*r
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*r
));
3187 memcpy(r
->id
, id
, sizeof(r
->id
));
3189 if (subtlvs
&& subtlv_len
) {
3190 r
->subtlvs
= XCALLOC(MTYPE_ISIS_TLV
, subtlv_len
);
3191 memcpy(r
->subtlvs
, subtlvs
, subtlv_len
);
3192 r
->subtlv_len
= subtlv_len
;
3195 struct isis_item_list
*l
;
3196 if (mtid
== ISIS_MT_IPV4_UNICAST
)
3197 l
= &tlvs
->extended_reach
;
3199 l
= isis_get_mt_items(&tlvs
->mt_reach
, mtid
);
3200 append_item(l
, (struct isis_item
*)r
);
3203 void isis_tlvs_add_threeway_adj(struct isis_tlvs
*tlvs
,
3204 enum isis_threeway_state state
,
3205 uint32_t local_circuit_id
,
3206 const uint8_t *neighbor_id
,
3207 uint32_t neighbor_circuit_id
)
3209 assert(!tlvs
->threeway_adj
);
3211 tlvs
->threeway_adj
= XCALLOC(MTYPE_ISIS_TLV
, sizeof(*tlvs
->threeway_adj
));
3212 tlvs
->threeway_adj
->state
= state
;
3213 tlvs
->threeway_adj
->local_circuit_id
= local_circuit_id
;
3216 tlvs
->threeway_adj
->neighbor_set
= true;
3217 memcpy(tlvs
->threeway_adj
->neighbor_id
, neighbor_id
, 6);
3218 tlvs
->threeway_adj
->neighbor_circuit_id
= neighbor_circuit_id
;
3222 struct isis_mt_router_info
*
3223 isis_tlvs_lookup_mt_router_info(struct isis_tlvs
*tlvs
, uint16_t mtid
)
3225 if (tlvs
->mt_router_info_empty
)
3228 struct isis_mt_router_info
*rv
;
3229 for (rv
= (struct isis_mt_router_info
*)tlvs
->mt_router_info
.head
; rv
;
3231 if (rv
->mtid
== mtid
)