]> git.proxmox.com Git - mirror_frr.git/blame - isisd/isis_tlvs.c
*: silence '-Wmaybe-uninitialized' warnings on NetBSD
[mirror_frr.git] / isisd / isis_tlvs.c
CommitLineData
7ef5fefc
CF
1/*
2 * IS-IS TLV Serializer/Deserializer
3 *
4 * Copyright (C) 2015,2017 Christian Franke
5 *
6 * This file is part of FRR.
7 *
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
11 * later version.
12 *
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.
17 *
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
21 * 02111-1307, USA.
22 */
23#include <zebra.h>
24
25#include "md5.h"
26#include "memory.h"
27#include "stream.h"
28#include "sbuf.h"
29
30#include "isisd/isisd.h"
31#include "isisd/isis_memory.h"
841791b6 32#include "isisd/isis_tlvs.h"
7ef5fefc
CF
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"
41
841791b6 42DEFINE_MTYPE_STATIC(ISISD, ISIS_TLV, "ISIS TLVs")
7ef5fefc
CF
43DEFINE_MTYPE_STATIC(ISISD, ISIS_SUBTLV, "ISIS Sub-TLVs")
44DEFINE_MTYPE_STATIC(ISISD, ISIS_MT_ITEM_LIST, "ISIS MT Item Lists")
45
46typedef 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);
49typedef int (*pack_item_func)(struct isis_item *item, struct stream *s);
50typedef void (*free_item_func)(struct isis_item *i);
51typedef int (*unpack_item_func)(uint16_t mtid, uint8_t len, struct stream *s,
52 struct sbuf *log, void *dest, int indent);
53typedef void (*format_item_func)(uint16_t mtid, struct isis_item *i,
54 struct sbuf *buf, int indent);
55typedef struct isis_item *(*copy_item_func)(struct isis_item *i);
56
57struct tlv_ops {
58 const char *name;
59 unpack_tlv_func unpack;
60
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;
66};
67
68enum how_to_pack {
69 ISIS_ITEMS,
70 ISIS_MT_ITEMS,
71};
72
73struct pack_order_entry {
74 enum isis_tlv_context context;
75 enum isis_tlv_type type;
76 enum how_to_pack how_to_pack;
77 size_t what_to_pack;
78};
79#define PACK_ENTRY(t, h, w) \
80 { \
81 .context = ISIS_CONTEXT_LSP, .type = ISIS_TLV_##t, \
82 .how_to_pack = (h), \
83 .what_to_pack = offsetof(struct isis_tlvs, w), \
84 }
85
86static 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)};
100
101/* This is a forward definition. The table is actually initialized
102 * in at the bottom. */
103static const struct tlv_ops *tlv_table[ISIS_CONTEXT_MAX][ISIS_TLV_MAX];
104
105/* End of _ops forward definition. */
106
107/* Prototypes */
108static void append_item(struct isis_item_list *dest, struct isis_item *item);
109
110/* Functions for Sub-TVL ??? IPv6 Source Prefix */
111
112static struct prefix_ipv6 *copy_subtlv_ipv6_source_prefix(struct prefix_ipv6 *p)
113{
114 if (!p)
115 return NULL;
116
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));
121 return rv;
122}
123
124static void format_subtlv_ipv6_source_prefix(struct prefix_ipv6 *p,
125 struct sbuf *buf, int indent)
126{
127 if (!p)
128 return;
129
130 char prefixbuf[PREFIX2STR_BUFFER];
131 sbuf_push(buf, indent, "IPv6 Source Prefix: %s\n",
132 prefix2str(p, prefixbuf, sizeof(prefixbuf)));
133}
134
135static int pack_subtlv_ipv6_source_prefix(struct prefix_ipv6 *p,
136 struct stream *s)
137{
138 if (!p)
139 return 0;
140
141 if (STREAM_WRITEABLE(s) < 3 + (unsigned)PSIZE(p->prefixlen))
142 return 1;
143
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));
148 return 0;
149}
150
151static 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)
155{
156 struct isis_subtlvs *subtlvs = dest;
157 struct prefix_ipv6 p = {
158 .family = AF_INET6,
159 };
160
161 sbuf_push(log, indent, "Unpacking IPv6 Source Prefix Sub-TLV...\n");
162
163 if (tlv_len < 1) {
164 sbuf_push(log, indent,
165 "Not enough data left. (expected 1 or more bytes, got %" PRIu8 ")\n",
166 tlv_len);
167 return 1;
168 }
169
170 p.prefixlen = stream_getc(s);
171 if (p.prefixlen > 128) {
172 sbuf_push(log, indent, "Prefixlen %u is inplausible for IPv6\n",
173 p.prefixlen);
174 return 1;
175 }
176
177 if (tlv_len != 1 + PSIZE(p.prefixlen)) {
178 sbuf_push(
179 log, indent,
180 "TLV size differs from expected size for the prefixlen. "
181 "(expected %u but got %" PRIu8 ")\n",
182 1 + PSIZE(p.prefixlen), tlv_len);
183 return 1;
184 }
185
186 stream_get(&p.prefix, s, PSIZE(p.prefixlen));
187
188 if (subtlvs->source_prefix) {
189 sbuf_push(
190 log, indent,
191 "WARNING: source prefix Sub-TLV present multiple times.\n");
192 /* Ignore all but first occurrence of the source prefix Sub-TLV
193 */
194 return 0;
195 }
196
197 subtlvs->source_prefix = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(p));
198 memcpy(subtlvs->source_prefix, &p, sizeof(p));
199 return 0;
200}
201
202/* Functions related to subtlvs */
203
204static struct isis_subtlvs *isis_alloc_subtlvs(void)
205{
206 struct isis_subtlvs *result;
207
208 result = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*result));
209
210 return result;
211}
212
213static struct isis_subtlvs *copy_subtlvs(struct isis_subtlvs *subtlvs)
214{
215 if (!subtlvs)
216 return NULL;
217
218 struct isis_subtlvs *rv = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*rv));
219
220 rv->source_prefix =
221 copy_subtlv_ipv6_source_prefix(subtlvs->source_prefix);
222 return rv;
223}
224
225static void format_subtlvs(struct isis_subtlvs *subtlvs, struct sbuf *buf,
226 int indent)
227{
228 format_subtlv_ipv6_source_prefix(subtlvs->source_prefix, buf, indent);
229}
230
231static void isis_free_subtlvs(struct isis_subtlvs *subtlvs)
232{
233 if (!subtlvs)
234 return;
235
236 XFREE(MTYPE_ISIS_SUBTLV, subtlvs->source_prefix);
237
238 XFREE(MTYPE_ISIS_SUBTLV, subtlvs);
239}
240
241static int pack_subtlvs(struct isis_subtlvs *subtlvs, struct stream *s)
242{
243 int rv;
244 size_t subtlv_len_pos = stream_get_endp(s);
245
246 if (STREAM_WRITEABLE(s) < 1)
247 return 1;
248
249 stream_putc(s, 0); /* Put 0 as subtlvs length, filled in later */
250
251 rv = pack_subtlv_ipv6_source_prefix(subtlvs->source_prefix, s);
252 if (rv)
253 return rv;
254
255 size_t subtlv_len = stream_get_endp(s) - subtlv_len_pos - 1;
256 if (subtlv_len > 255)
257 return 1;
258
259 stream_putc_at(s, subtlv_len_pos, subtlv_len);
260 return 0;
261}
262
263static int unpack_tlvs(enum isis_tlv_context context, size_t avail_len,
264 struct stream *stream, struct sbuf *log, void *dest,
265 int indent);
266
267/* Functions related to TLVs 1 Area Addresses */
268
269static struct isis_item *copy_item_area_address(struct isis_item *i)
270{
271 struct isis_area_address *addr = (struct isis_area_address *)i;
841791b6 272 struct isis_area_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
273
274 rv->len = addr->len;
275 memcpy(rv->addr, addr->addr, addr->len);
276 return (struct isis_item *)rv;
277}
278
279static void format_item_area_address(uint16_t mtid, struct isis_item *i,
280 struct sbuf *buf, int indent)
281{
282 struct isis_area_address *addr = (struct isis_area_address *)i;
283
284 sbuf_push(buf, indent, "Area Address: %s\n",
285 isonet_print(addr->addr, addr->len));
286}
287
288static void free_item_area_address(struct isis_item *i)
289{
841791b6 290 XFREE(MTYPE_ISIS_TLV, i);
7ef5fefc
CF
291}
292
293static int pack_item_area_address(struct isis_item *i, struct stream *s)
294{
295 struct isis_area_address *addr = (struct isis_area_address *)i;
296
297 if (STREAM_WRITEABLE(s) < (unsigned)1 + addr->len)
298 return 1;
299 stream_putc(s, addr->len);
300 stream_put(s, addr->addr, addr->len);
301 return 0;
302}
303
304static int unpack_item_area_address(uint16_t mtid, uint8_t len,
305 struct stream *s, struct sbuf *log,
306 void *dest, int indent)
307{
308 struct isis_tlvs *tlvs = dest;
309 struct isis_area_address *rv = NULL;
310
311 sbuf_push(log, indent, "Unpack area address...\n");
312 if (len < 1) {
313 sbuf_push(
314 log, indent,
315 "Not enough data left. (Expected 1 byte of address length, got %" PRIu8
316 ")\n",
317 len);
318 goto out;
319 }
320
841791b6 321 rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
322 rv->len = stream_getc(s);
323
324 if (len < 1 + rv->len) {
325 sbuf_push(log, indent, "Not enough data left. (Expected %" PRIu8
326 " bytes of address, got %" PRIu8 ")\n",
327 rv->len, len - 1);
328 goto out;
329 }
330
331 if (rv->len < 1 || rv->len > 20) {
332 sbuf_push(log, indent,
333 "Implausible area address length %" PRIu8 "\n",
334 rv->len);
335 goto out;
336 }
337
338 stream_get(rv->addr, s, rv->len);
339
340 format_item_area_address(ISIS_MT_IPV4_UNICAST, (struct isis_item *)rv,
341 log, indent + 2);
342 append_item(&tlvs->area_addresses, (struct isis_item *)rv);
343 return 0;
344out:
841791b6 345 XFREE(MTYPE_ISIS_TLV, rv);
7ef5fefc
CF
346 return 1;
347}
348
349/* Functions related to TLV 2 (Old-Style) IS Reach */
350static struct isis_item *copy_item_oldstyle_reach(struct isis_item *i)
351{
352 struct isis_oldstyle_reach *r = (struct isis_oldstyle_reach *)i;
841791b6 353 struct isis_oldstyle_reach *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
354
355 memcpy(rv->id, r->id, 7);
356 rv->metric = r->metric;
357 return (struct isis_item *)rv;
358}
359
360static void format_item_oldstyle_reach(uint16_t mtid, struct isis_item *i,
361 struct sbuf *buf, int indent)
362{
363 struct isis_oldstyle_reach *r = (struct isis_oldstyle_reach *)i;
364
365 sbuf_push(buf, indent, "IS Reachability: %s (Metric: %" PRIu8 ")\n",
366 isis_format_id(r->id, 7), r->metric);
367}
368
369static void free_item_oldstyle_reach(struct isis_item *i)
370{
841791b6 371 XFREE(MTYPE_ISIS_TLV, i);
7ef5fefc
CF
372}
373
374static int pack_item_oldstyle_reach(struct isis_item *i, struct stream *s)
375{
376 struct isis_oldstyle_reach *r = (struct isis_oldstyle_reach *)i;
377
378 if (STREAM_WRITEABLE(s) < 11)
379 return 1;
380
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);
386
387 return 0;
388}
389
390static int unpack_item_oldstyle_reach(uint16_t mtid, uint8_t len,
391 struct stream *s, struct sbuf *log,
392 void *dest, int indent)
393{
394 struct isis_tlvs *tlvs = dest;
395
396 sbuf_push(log, indent, "Unpack oldstyle reach...\n");
397 if (len < 11) {
398 sbuf_push(
399 log, indent,
400 "Not enough data left.(Expected 11 bytes of reach information, got %" PRIu8
401 ")\n",
402 len);
403 return 1;
404 }
405
841791b6 406 struct isis_oldstyle_reach *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
407 rv->metric = stream_getc(s);
408 if ((rv->metric & 0x3f) != rv->metric) {
409 sbuf_push(log, indent, "Metric has unplausible format\n");
410 rv->metric &= 0x3f;
411 }
412 stream_forward_getp(s, 3); /* Skip other metrics */
413 stream_get(rv->id, s, 7);
414
415 format_item_oldstyle_reach(mtid, (struct isis_item *)rv, log,
416 indent + 2);
417 append_item(&tlvs->oldstyle_reach, (struct isis_item *)rv);
418 return 0;
419}
420
421/* Functions related to TLV 6 LAN Neighbors */
422static struct isis_item *copy_item_lan_neighbor(struct isis_item *i)
423{
424 struct isis_lan_neighbor *n = (struct isis_lan_neighbor *)i;
841791b6 425 struct isis_lan_neighbor *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
426
427 memcpy(rv->mac, n->mac, 6);
428 return (struct isis_item *)rv;
429}
430
431static void format_item_lan_neighbor(uint16_t mtid, struct isis_item *i,
432 struct sbuf *buf, int indent)
433{
434 struct isis_lan_neighbor *n = (struct isis_lan_neighbor *)i;
435
436 sbuf_push(buf, indent, "LAN Neighbor: %s\n", isis_format_id(n->mac, 6));
437}
438
439static void free_item_lan_neighbor(struct isis_item *i)
440{
841791b6 441 XFREE(MTYPE_ISIS_TLV, i);
7ef5fefc
CF
442}
443
444static int pack_item_lan_neighbor(struct isis_item *i, struct stream *s)
445{
446 struct isis_lan_neighbor *n = (struct isis_lan_neighbor *)i;
447
448 if (STREAM_WRITEABLE(s) < 6)
449 return 1;
450
451 stream_put(s, n->mac, 6);
452
453 return 0;
454}
455
456static int unpack_item_lan_neighbor(uint16_t mtid, uint8_t len,
457 struct stream *s, struct sbuf *log,
458 void *dest, int indent)
459{
460 struct isis_tlvs *tlvs = dest;
461
462 sbuf_push(log, indent, "Unpack LAN neighbor...\n");
463 if (len < 6) {
464 sbuf_push(
465 log, indent,
466 "Not enough data left.(Expected 6 bytes of mac, got %" PRIu8
467 ")\n",
468 len);
469 return 1;
470 }
471
841791b6 472 struct isis_lan_neighbor *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
473 stream_get(rv->mac, s, 6);
474
475 format_item_lan_neighbor(mtid, (struct isis_item *)rv, log, indent + 2);
476 append_item(&tlvs->lan_neighbor, (struct isis_item *)rv);
477 return 0;
478}
479
480/* Functions related to TLV 9 LSP Entry */
481static struct isis_item *copy_item_lsp_entry(struct isis_item *i)
482{
483 struct isis_lsp_entry *e = (struct isis_lsp_entry *)i;
841791b6 484 struct isis_lsp_entry *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
485
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;
490
491 return (struct isis_item *)rv;
492}
493
494static void format_item_lsp_entry(uint16_t mtid, struct isis_item *i,
495 struct sbuf *buf, int indent)
496{
497 struct isis_lsp_entry *e = (struct isis_lsp_entry *)i;
498
499 sbuf_push(buf, indent, "LSP Entry: %s, seq 0x%08" PRIx32
500 ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16 "s\n",
501 isis_format_id(e->id, 8), e->seqno, e->checksum,
502 e->rem_lifetime);
503}
504
505static void free_item_lsp_entry(struct isis_item *i)
506{
841791b6 507 XFREE(MTYPE_ISIS_TLV, i);
7ef5fefc
CF
508}
509
510static int pack_item_lsp_entry(struct isis_item *i, struct stream *s)
511{
512 struct isis_lsp_entry *e = (struct isis_lsp_entry *)i;
513
514 if (STREAM_WRITEABLE(s) < 16)
515 return 1;
516
517 stream_putw(s, e->rem_lifetime);
518 stream_put(s, e->id, 8);
519 stream_putl(s, e->seqno);
520 stream_putw(s, e->checksum);
521
522 return 0;
523}
524
525static int unpack_item_lsp_entry(uint16_t mtid, uint8_t len, struct stream *s,
526 struct sbuf *log, void *dest, int indent)
527{
528 struct isis_tlvs *tlvs = dest;
529
530 sbuf_push(log, indent, "Unpack LSP entry...\n");
531 if (len < 16) {
532 sbuf_push(
533 log, indent,
534 "Not enough data left. (Expected 16 bytes of LSP info, got %" PRIu8,
535 len);
536 return 1;
537 }
538
841791b6 539 struct isis_lsp_entry *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
540 rv->rem_lifetime = stream_getw(s);
541 stream_get(rv->id, s, 8);
542 rv->seqno = stream_getl(s);
543 rv->checksum = stream_getw(s);
544
545 format_item_lsp_entry(mtid, (struct isis_item *)rv, log, indent + 2);
546 append_item(&tlvs->lsp_entries, (struct isis_item *)rv);
547 return 0;
548}
549
550/* Functions related to TLVs 22/222 Extended Reach/MT Reach */
551
552static struct isis_item *copy_item_extended_reach(struct isis_item *i)
553{
554 struct isis_extended_reach *r = (struct isis_extended_reach *)i;
841791b6 555 struct isis_extended_reach *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
556
557 memcpy(rv->id, r->id, 7);
558 rv->metric = r->metric;
559
560 if (r->subtlvs && r->subtlv_len) {
841791b6 561 rv->subtlvs = XCALLOC(MTYPE_ISIS_TLV, r->subtlv_len);
7ef5fefc
CF
562 memcpy(rv->subtlvs, r->subtlvs, r->subtlv_len);
563 rv->subtlv_len = r->subtlv_len;
564 }
565
566 return (struct isis_item *)rv;
567}
568
569static void format_item_extended_reach(uint16_t mtid, struct isis_item *i,
570 struct sbuf *buf, int indent)
571{
572 struct isis_extended_reach *r = (struct isis_extended_reach *)i;
573
574 sbuf_push(buf, indent, "%s Reachability: %s (Metric: %u)",
575 (mtid == ISIS_MT_IPV4_UNICAST) ? "Extended" : "MT",
576 isis_format_id(r->id, 7), r->metric);
577 if (mtid != ISIS_MT_IPV4_UNICAST)
578 sbuf_push(buf, 0, " %s", isis_mtid2str(mtid));
579 sbuf_push(buf, 0, "\n");
580
581 if (r->subtlv_len && r->subtlvs)
582 mpls_te_print_detail(buf, indent + 2, r->subtlvs, r->subtlv_len);
583}
584
585static void free_item_extended_reach(struct isis_item *i)
586{
587 struct isis_extended_reach *item = (struct isis_extended_reach *)i;
841791b6
CF
588 XFREE(MTYPE_ISIS_TLV, item->subtlvs);
589 XFREE(MTYPE_ISIS_TLV, item);
7ef5fefc
CF
590}
591
592static int pack_item_extended_reach(struct isis_item *i, struct stream *s)
593{
594 struct isis_extended_reach *r = (struct isis_extended_reach *)i;
595
596 if (STREAM_WRITEABLE(s) < 11 + (unsigned)r->subtlv_len)
597 return 1;
598 stream_put(s, r->id, sizeof(r->id));
599 stream_put3(s, r->metric);
600 stream_putc(s, r->subtlv_len);
601 stream_put(s, r->subtlvs, r->subtlv_len);
602 return 0;
603}
604
605static int unpack_item_extended_reach(uint16_t mtid, uint8_t len,
606 struct stream *s, struct sbuf *log,
607 void *dest, int indent)
608{
609 struct isis_tlvs *tlvs = dest;
610 struct isis_extended_reach *rv = NULL;
611 uint8_t subtlv_len;
612 struct isis_item_list *items;
613
614 if (mtid == ISIS_MT_IPV4_UNICAST) {
615 items = &tlvs->extended_reach;
616 } else {
617 items = isis_get_mt_items(&tlvs->mt_reach, mtid);
618 }
619
620 sbuf_push(log, indent, "Unpacking %s reachability...\n",
621 (mtid == ISIS_MT_IPV4_UNICAST) ? "extended" : "mt");
622
623 if (len < 11) {
624 sbuf_push(log, indent,
625 "Not enough data left. (expected 11 or more bytes, got %"
626 PRIu8 ")\n",
627 len);
628 goto out;
629 }
630
841791b6 631 rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
632 stream_get(rv->id, s, 7);
633 rv->metric = stream_get3(s);
634 subtlv_len = stream_getc(s);
635
636 format_item_extended_reach(mtid, (struct isis_item *)rv, log,
637 indent + 2);
638
639 if ((size_t)len < ((size_t)11) + subtlv_len) {
640 sbuf_push(log, indent,
641 "Not enough data left for subtlv size %" PRIu8
642 ", there are only %" PRIu8 " bytes left.\n",
643 subtlv_len, len - 11);
644 goto out;
645 }
646
647 sbuf_push(log, indent, "Storing %" PRIu8 " bytes of subtlvs\n",
648 subtlv_len);
649
650 if (subtlv_len) {
651 size_t subtlv_start = stream_get_getp(s);
652
653 if (unpack_tlvs(ISIS_CONTEXT_SUBTLV_NE_REACH, subtlv_len, s,
654 log, NULL, indent + 4)) {
655 goto out;
656 }
657
658 stream_set_getp(s, subtlv_start);
659
841791b6 660 rv->subtlvs = XCALLOC(MTYPE_ISIS_TLV, subtlv_len);
7ef5fefc
CF
661 stream_get(rv->subtlvs, s, subtlv_len);
662 rv->subtlv_len = subtlv_len;
663 }
664
665 append_item(items, (struct isis_item *)rv);
666 return 0;
667out:
668 if (rv)
669 free_item_extended_reach((struct isis_item *)rv);
670
671 return 1;
672}
673
674/* Functions related to TLV 128 (Old-Style) IP Reach */
675static struct isis_item *copy_item_oldstyle_ip_reach(struct isis_item *i)
676{
677 struct isis_oldstyle_ip_reach *r = (struct isis_oldstyle_ip_reach *)i;
678 struct isis_oldstyle_ip_reach *rv =
841791b6 679 XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
680
681 rv->metric = r->metric;
682 rv->prefix = r->prefix;
683 return (struct isis_item *)rv;
684}
685
686static void format_item_oldstyle_ip_reach(uint16_t mtid, struct isis_item *i,
687 struct sbuf *buf, int indent)
688{
689 struct isis_oldstyle_ip_reach *r = (struct isis_oldstyle_ip_reach *)i;
690 char prefixbuf[PREFIX2STR_BUFFER];
691
692 sbuf_push(buf, indent, "IP Reachability: %s (Metric: %" PRIu8 ")\n",
693 prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf)), r->metric);
694}
695
696static void free_item_oldstyle_ip_reach(struct isis_item *i)
697{
841791b6 698 XFREE(MTYPE_ISIS_TLV, i);
7ef5fefc
CF
699}
700
701static int pack_item_oldstyle_ip_reach(struct isis_item *i, struct stream *s)
702{
703 struct isis_oldstyle_ip_reach *r = (struct isis_oldstyle_ip_reach *)i;
704
705 if (STREAM_WRITEABLE(s) < 12)
706 return 1;
707
708 stream_putc(s, r->metric);
709 stream_putc(s, 0x80); /* delay metric - unsupported */
710 stream_putc(s, 0x80); /* expense metric - unsupported */
711 stream_putc(s, 0x80); /* error metric - unsupported */
712 stream_put(s, &r->prefix.prefix, 4);
713
714 struct in_addr mask;
715 masklen2ip(r->prefix.prefixlen, &mask);
716 stream_put(s, &mask, sizeof(mask));
717
718 return 0;
719}
720
721static int unpack_item_oldstyle_ip_reach(uint16_t mtid, uint8_t len,
722 struct stream *s, struct sbuf *log,
723 void *dest, int indent)
724{
725 sbuf_push(log, indent, "Unpack oldstyle ip reach...\n");
726 if (len < 12) {
727 sbuf_push(
728 log, indent,
729 "Not enough data left.(Expected 12 bytes of reach information, got %" PRIu8
730 ")\n",
731 len);
732 return 1;
733 }
734
735 struct isis_oldstyle_ip_reach *rv =
841791b6 736 XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
737 rv->metric = stream_getc(s);
738 if ((rv->metric & 0x7f) != rv->metric) {
739 sbuf_push(log, indent, "Metric has unplausible format\n");
740 rv->metric &= 0x7f;
741 }
742 stream_forward_getp(s, 3); /* Skip other metrics */
743 rv->prefix.family = AF_INET;
744 stream_get(&rv->prefix.prefix, s, 4);
745
746 struct in_addr mask;
747 stream_get(&mask, s, 4);
748 rv->prefix.prefixlen = ip_masklen(mask);
749
750 format_item_oldstyle_ip_reach(mtid, (struct isis_item *)rv, log,
751 indent + 2);
752 append_item(dest, (struct isis_item *)rv);
753 return 0;
754}
755
756
757/* Functions related to TLV 129 protocols supported */
758
759static void copy_tlv_protocols_supported(struct isis_protocols_supported *src,
760 struct isis_protocols_supported *dest)
761{
762 if (!src->protocols || !src->count)
763 return;
764 dest->count = src->count;
841791b6 765 dest->protocols = XCALLOC(MTYPE_ISIS_TLV, src->count);
7ef5fefc
CF
766 memcpy(dest->protocols, src->protocols, src->count);
767}
768
769static void format_tlv_protocols_supported(struct isis_protocols_supported *p,
770 struct sbuf *buf, int indent)
771{
772 if (!p || !p->count || !p->protocols)
773 return;
774
775 sbuf_push(buf, indent, "Protocols Supported: ");
776 for (uint8_t i = 0; i < p->count; i++) {
777 sbuf_push(buf, 0, "%s%s", nlpid2str(p->protocols[i]),
778 (i + 1 < p->count) ? ", " : "");
779 }
780 sbuf_push(buf, 0, "\n");
781}
782
783static void free_tlv_protocols_supported(struct isis_protocols_supported *p)
784{
841791b6 785 XFREE(MTYPE_ISIS_TLV, p->protocols);
7ef5fefc
CF
786}
787
788static int pack_tlv_protocols_supported(struct isis_protocols_supported *p,
789 struct stream *s)
790{
791 if (!p || !p->count || !p->protocols)
792 return 0;
793
794 if (STREAM_WRITEABLE(s) < (unsigned)(p->count + 2))
795 return 1;
796
797 stream_putc(s, ISIS_TLV_PROTOCOLS_SUPPORTED);
798 stream_putc(s, p->count);
799 stream_put(s, p->protocols, p->count);
800 return 0;
801}
802
803static int unpack_tlv_protocols_supported(enum isis_tlv_context context,
804 uint8_t tlv_type, uint8_t tlv_len,
805 struct stream *s, struct sbuf *log,
806 void *dest, int indent)
807{
808 struct isis_tlvs *tlvs = dest;
809
810 sbuf_push(log, indent, "Unpacking Protocols Supported TLV...\n");
811 if (!tlv_len) {
812 sbuf_push(log, indent, "WARNING: No protocols included\n");
813 return 0;
814 }
815 if (tlvs->protocols_supported.protocols) {
816 sbuf_push(
817 log, indent,
818 "WARNING: protocols supported TLV present multiple times.\n");
819 stream_forward_getp(s, tlv_len);
820 return 0;
821 }
822
823 tlvs->protocols_supported.count = tlv_len;
841791b6 824 tlvs->protocols_supported.protocols = XCALLOC(MTYPE_ISIS_TLV, tlv_len);
7ef5fefc
CF
825 stream_get(tlvs->protocols_supported.protocols, s, tlv_len);
826
827 format_tlv_protocols_supported(&tlvs->protocols_supported, log,
828 indent + 2);
829 return 0;
830}
831
832/* Functions related to TLV 132 IPv4 Interface addresses */
833static struct isis_item *copy_item_ipv4_address(struct isis_item *i)
834{
835 struct isis_ipv4_address *a = (struct isis_ipv4_address *)i;
841791b6 836 struct isis_ipv4_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
837
838 rv->addr = a->addr;
839 return (struct isis_item *)rv;
840}
841
842static void format_item_ipv4_address(uint16_t mtid, struct isis_item *i,
843 struct sbuf *buf, int indent)
844{
845 struct isis_ipv4_address *a = (struct isis_ipv4_address *)i;
846 char addrbuf[INET_ADDRSTRLEN];
847
848 inet_ntop(AF_INET, &a->addr, addrbuf, sizeof(addrbuf));
849 sbuf_push(buf, indent, "IPv4 Interface Address: %s\n", addrbuf);
850}
851
852static void free_item_ipv4_address(struct isis_item *i)
853{
841791b6 854 XFREE(MTYPE_ISIS_TLV, i);
7ef5fefc
CF
855}
856
857static int pack_item_ipv4_address(struct isis_item *i, struct stream *s)
858{
859 struct isis_ipv4_address *a = (struct isis_ipv4_address *)i;
860
861 if (STREAM_WRITEABLE(s) < 4)
862 return 1;
863
864 stream_put(s, &a->addr, 4);
865
866 return 0;
867}
868
869static int unpack_item_ipv4_address(uint16_t mtid, uint8_t len,
870 struct stream *s, struct sbuf *log,
871 void *dest, int indent)
872{
873 struct isis_tlvs *tlvs = dest;
874
875 sbuf_push(log, indent, "Unpack IPv4 Interface address...\n");
876 if (len < 4) {
877 sbuf_push(
878 log, indent,
879 "Not enough data left.(Expected 4 bytes of IPv4 address, got %" PRIu8
880 ")\n",
881 len);
882 return 1;
883 }
884
841791b6 885 struct isis_ipv4_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
886 stream_get(&rv->addr, s, 4);
887
888 format_item_ipv4_address(mtid, (struct isis_item *)rv, log, indent + 2);
889 append_item(&tlvs->ipv4_address, (struct isis_item *)rv);
890 return 0;
891}
892
893
894/* Functions related to TLV 232 IPv6 Interface addresses */
895static struct isis_item *copy_item_ipv6_address(struct isis_item *i)
896{
897 struct isis_ipv6_address *a = (struct isis_ipv6_address *)i;
841791b6 898 struct isis_ipv6_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
899
900 rv->addr = a->addr;
901 return (struct isis_item *)rv;
902}
903
904static void format_item_ipv6_address(uint16_t mtid, struct isis_item *i,
905 struct sbuf *buf, int indent)
906{
907 struct isis_ipv6_address *a = (struct isis_ipv6_address *)i;
908 char addrbuf[INET6_ADDRSTRLEN];
909
910 inet_ntop(AF_INET6, &a->addr, addrbuf, sizeof(addrbuf));
911 sbuf_push(buf, indent, "IPv6 Interface Address: %s\n", addrbuf);
912}
913
914static void free_item_ipv6_address(struct isis_item *i)
915{
841791b6 916 XFREE(MTYPE_ISIS_TLV, i);
7ef5fefc
CF
917}
918
919static int pack_item_ipv6_address(struct isis_item *i, struct stream *s)
920{
921 struct isis_ipv6_address *a = (struct isis_ipv6_address *)i;
922
923 if (STREAM_WRITEABLE(s) < 16)
924 return 1;
925
926 stream_put(s, &a->addr, 16);
927
928 return 0;
929}
930
931static int unpack_item_ipv6_address(uint16_t mtid, uint8_t len,
932 struct stream *s, struct sbuf *log,
933 void *dest, int indent)
934{
935 struct isis_tlvs *tlvs = dest;
936
937 sbuf_push(log, indent, "Unpack IPv6 Interface address...\n");
938 if (len < 16) {
939 sbuf_push(
940 log, indent,
941 "Not enough data left.(Expected 16 bytes of IPv6 address, got %" PRIu8
942 ")\n",
943 len);
944 return 1;
945 }
946
841791b6 947 struct isis_ipv6_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
948 stream_get(&rv->addr, s, 16);
949
950 format_item_ipv6_address(mtid, (struct isis_item *)rv, log, indent + 2);
951 append_item(&tlvs->ipv6_address, (struct isis_item *)rv);
952 return 0;
953}
954
955
956/* Functions related to TLV 229 MT Router information */
957static struct isis_item *copy_item_mt_router_info(struct isis_item *i)
958{
959 struct isis_mt_router_info *info = (struct isis_mt_router_info *)i;
841791b6 960 struct isis_mt_router_info *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
961
962 rv->overload = info->overload;
963 rv->attached = info->attached;
964 rv->mtid = info->mtid;
965 return (struct isis_item *)rv;
966}
967
968static void format_item_mt_router_info(uint16_t mtid, struct isis_item *i,
969 struct sbuf *buf, int indent)
970{
971 struct isis_mt_router_info *info = (struct isis_mt_router_info *)i;
972
973 sbuf_push(buf, indent, "MT Router Info: %s%s%s\n",
974 isis_mtid2str(info->mtid),
975 info->overload ? " Overload" : "",
976 info->attached ? " Attached" : "");
977}
978
979static void free_item_mt_router_info(struct isis_item *i)
980{
841791b6 981 XFREE(MTYPE_ISIS_TLV, i);
7ef5fefc
CF
982}
983
984static int pack_item_mt_router_info(struct isis_item *i, struct stream *s)
985{
986 struct isis_mt_router_info *info = (struct isis_mt_router_info *)i;
987
988 if (STREAM_WRITEABLE(s) < 2)
989 return 1;
990
991 uint16_t entry = info->mtid;
992
993 if (info->overload)
994 entry |= ISIS_MT_OL_MASK;
995 if (info->attached)
996 entry |= ISIS_MT_AT_MASK;
997
998 stream_putw(s, entry);
999
1000 return 0;
1001}
1002
1003static int unpack_item_mt_router_info(uint16_t mtid, uint8_t len,
1004 struct stream *s, struct sbuf *log,
1005 void *dest, int indent)
1006{
1007 struct isis_tlvs *tlvs = dest;
1008
1009 sbuf_push(log, indent, "Unpack MT Router info...\n");
1010 if (len < 2) {
1011 sbuf_push(
1012 log, indent,
1013 "Not enough data left.(Expected 2 bytes of MT info, got %" PRIu8
1014 ")\n",
1015 len);
1016 return 1;
1017 }
1018
841791b6 1019 struct isis_mt_router_info *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1020
1021 uint16_t entry = stream_getw(s);
1022 rv->overload = entry & ISIS_MT_OL_MASK;
1023 rv->attached = entry & ISIS_MT_AT_MASK;
1024 rv->mtid = entry & ISIS_MT_MASK;
1025
1026 format_item_mt_router_info(mtid, (struct isis_item *)rv, log,
1027 indent + 2);
1028 append_item(&tlvs->mt_router_info, (struct isis_item *)rv);
1029 return 0;
1030}
1031
1032/* Functions related to TLV 134 TE Router ID */
1033
1034static struct in_addr *copy_tlv_te_router_id(const struct in_addr *id)
1035{
1036 if (!id)
1037 return NULL;
1038
841791b6 1039 struct in_addr *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1040 memcpy(rv, id, sizeof(*rv));
1041 return rv;
1042}
1043
1044static void format_tlv_te_router_id(const struct in_addr *id, struct sbuf *buf,
1045 int indent)
1046{
1047 if (!id)
1048 return;
1049
1050 char addrbuf[INET_ADDRSTRLEN];
1051 inet_ntop(AF_INET, id, addrbuf, sizeof(addrbuf));
1052 sbuf_push(buf, indent, "TE Router ID: %s\n", addrbuf);
1053}
1054
1055static void free_tlv_te_router_id(struct in_addr *id)
1056{
841791b6 1057 XFREE(MTYPE_ISIS_TLV, id);
7ef5fefc
CF
1058}
1059
1060static int pack_tlv_te_router_id(const struct in_addr *id, struct stream *s)
1061{
1062 if (!id)
1063 return 0;
1064
1065 if (STREAM_WRITEABLE(s) < (unsigned)(2 + sizeof(*id)))
1066 return 1;
1067
1068 stream_putc(s, ISIS_TLV_TE_ROUTER_ID);
1069 stream_putc(s, 4);
1070 stream_put(s, id, 4);
1071 return 0;
1072}
1073
1074static int unpack_tlv_te_router_id(enum isis_tlv_context context,
1075 uint8_t tlv_type, uint8_t tlv_len,
1076 struct stream *s, struct sbuf *log,
1077 void *dest, int indent)
1078{
1079 struct isis_tlvs *tlvs = dest;
1080
1081 sbuf_push(log, indent, "Unpacking TE Router ID TLV...\n");
1082 if (tlv_len != 4) {
1083 sbuf_push(log, indent, "WARNING: Length invalid\n");
1084 return 1;
1085 }
1086
1087 if (tlvs->te_router_id) {
1088 sbuf_push(log, indent,
1089 "WARNING: TE Router ID present multiple times.\n");
1090 stream_forward_getp(s, tlv_len);
1091 return 0;
1092 }
1093
841791b6 1094 tlvs->te_router_id = XCALLOC(MTYPE_ISIS_TLV, 4);
7ef5fefc
CF
1095 stream_get(tlvs->te_router_id, s, 4);
1096 format_tlv_te_router_id(tlvs->te_router_id, log, indent + 2);
1097 return 0;
1098}
1099
1100
1101/* Functions related to TLVs 135/235 extended IP reach/MT IP Reach */
1102
1103static struct isis_item *copy_item_extended_ip_reach(struct isis_item *i)
1104{
1105 struct isis_extended_ip_reach *r = (struct isis_extended_ip_reach *)i;
1106 struct isis_extended_ip_reach *rv =
841791b6 1107 XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1108
1109 rv->metric = r->metric;
1110 rv->down = r->down;
1111 rv->prefix = r->prefix;
1112
1113 return (struct isis_item *)rv;
1114}
1115
1116static void format_item_extended_ip_reach(uint16_t mtid, struct isis_item *i,
1117 struct sbuf *buf, int indent)
1118{
1119 struct isis_extended_ip_reach *r = (struct isis_extended_ip_reach *)i;
1120 char prefixbuf[PREFIX2STR_BUFFER];
1121
1122 sbuf_push(buf, indent, "%s IP Reachability: %s (Metric: %u)%s",
1123 (mtid == ISIS_MT_IPV4_UNICAST) ? "Extended" : "MT",
1124 prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf)), r->metric,
1125 r->down ? " Down" : "");
1126 if (mtid != ISIS_MT_IPV4_UNICAST)
1127 sbuf_push(buf, 0, " %s", isis_mtid2str(mtid));
1128 sbuf_push(buf, 0, "\n");
1129}
1130
1131static void free_item_extended_ip_reach(struct isis_item *i)
1132{
1133 struct isis_extended_ip_reach *item =
1134 (struct isis_extended_ip_reach *)i;
841791b6 1135 XFREE(MTYPE_ISIS_TLV, item);
7ef5fefc
CF
1136}
1137
1138static int pack_item_extended_ip_reach(struct isis_item *i, struct stream *s)
1139{
1140 struct isis_extended_ip_reach *r = (struct isis_extended_ip_reach *)i;
1141 uint8_t control;
1142
1143 if (STREAM_WRITEABLE(s) < 5)
1144 return 1;
1145 stream_putl(s, r->metric);
1146
1147 control = r->down ? ISIS_EXTENDED_IP_REACH_DOWN : 0;
1148 control |= r->prefix.prefixlen;
1149 stream_putc(s, control);
1150
1151 if (STREAM_WRITEABLE(s) < (unsigned)PSIZE(r->prefix.prefixlen))
1152 return 1;
1153 stream_put(s, &r->prefix.prefix.s_addr, PSIZE(r->prefix.prefixlen));
1154 return 0;
1155}
1156
1157static int unpack_item_extended_ip_reach(uint16_t mtid, uint8_t len,
1158 struct stream *s, struct sbuf *log,
1159 void *dest, int indent)
1160{
1161 struct isis_tlvs *tlvs = dest;
1162 struct isis_extended_ip_reach *rv = NULL;
1163 size_t consume;
1164 uint8_t control, subtlv_len;
1165 struct isis_item_list *items;
1166
1167 if (mtid == ISIS_MT_IPV4_UNICAST) {
1168 items = &tlvs->extended_ip_reach;
1169 } else {
1170 items = isis_get_mt_items(&tlvs->mt_ip_reach, mtid);
1171 }
1172
1173 sbuf_push(log, indent, "Unpacking %s IPv4 reachability...\n",
1174 (mtid == ISIS_MT_IPV4_UNICAST) ? "extended" : "mt");
1175
1176 consume = 5;
1177 if (len < consume) {
1178 sbuf_push(log, indent,
1179 "Not enough data left. (expected 5 or more bytes, got %" PRIu8 ")\n",
1180 len);
1181 goto out;
1182 }
1183
841791b6 1184 rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1185
1186 rv->metric = stream_getl(s);
1187 control = stream_getc(s);
1188 rv->down = (control & ISIS_EXTENDED_IP_REACH_DOWN);
1189 rv->prefix.family = AF_INET;
1190 rv->prefix.prefixlen = control & 0x3f;
1191 if (rv->prefix.prefixlen > 32) {
1192 sbuf_push(log, indent, "Prefixlen %u is inplausible for IPv4\n",
1193 rv->prefix.prefixlen);
1194 goto out;
1195 }
1196
1197 consume += PSIZE(rv->prefix.prefixlen);
1198 if (len < consume) {
1199 sbuf_push(log, indent,
1200 "Expected %u bytes of prefix, but only %u bytes available.\n",
1201 PSIZE(rv->prefix.prefixlen), len - 5);
1202 goto out;
1203 }
1204 stream_get(&rv->prefix.prefix.s_addr, s, PSIZE(rv->prefix.prefixlen));
1205 in_addr_t orig_prefix = rv->prefix.prefix.s_addr;
1206 apply_mask_ipv4(&rv->prefix);
1207 if (orig_prefix != rv->prefix.prefix.s_addr)
1208 sbuf_push(log, indent + 2,
1209 "WARNING: Prefix had hostbits set.\n");
1210 format_item_extended_ip_reach(mtid, (struct isis_item *)rv, log,
1211 indent + 2);
1212
1213 if (control & ISIS_EXTENDED_IP_REACH_SUBTLV) {
1214 consume += 1;
1215 if (len < consume) {
1216 sbuf_push(log, indent,
1217 "Expected 1 byte of subtlv len, but no more data present.\n");
1218 goto out;
1219 }
1220 subtlv_len = stream_getc(s);
1221
1222 if (!subtlv_len) {
1223 sbuf_push(log, indent + 2,
1224 " WARNING: subtlv bit is set, but there are no subtlvs.\n");
1225 }
1226 consume += subtlv_len;
1227 if (len < consume) {
1228 sbuf_push(log, indent,
1229 "Expected %" PRIu8
1230 " bytes of subtlvs, but only %u bytes available.\n",
1231 subtlv_len,
1232 len - 6 - PSIZE(rv->prefix.prefixlen));
1233 goto out;
1234 }
1235 sbuf_push(log, indent, "Skipping %" PRIu8 " bytes of subvls",
1236 subtlv_len);
1237 stream_forward_getp(s, subtlv_len);
1238 }
1239
1240 append_item(items, (struct isis_item *)rv);
1241 return 0;
1242out:
1243 if (rv)
1244 free_item_extended_ip_reach((struct isis_item *)rv);
1245 return 1;
1246}
1247
1248/* Functions related to TLV 137 Dynamic Hostname */
1249
1250static char *copy_tlv_dynamic_hostname(const char *hostname)
1251{
1252 if (!hostname)
1253 return NULL;
1254
841791b6 1255 return XSTRDUP(MTYPE_ISIS_TLV, hostname);
7ef5fefc
CF
1256}
1257
1258static void format_tlv_dynamic_hostname(const char *hostname, struct sbuf *buf,
1259 int indent)
1260{
1261 if (!hostname)
1262 return;
1263
1264 sbuf_push(buf, indent, "Hostname: %s\n", hostname);
1265}
1266
1267static void free_tlv_dynamic_hostname(char *hostname)
1268{
841791b6 1269 XFREE(MTYPE_ISIS_TLV, hostname);
7ef5fefc
CF
1270}
1271
1272static int pack_tlv_dynamic_hostname(const char *hostname, struct stream *s)
1273{
1274 if (!hostname)
1275 return 0;
1276
1277 uint8_t name_len = strlen(hostname);
1278
1279 if (STREAM_WRITEABLE(s) < (unsigned)(2 + name_len))
1280 return 1;
1281
1282 stream_putc(s, ISIS_TLV_DYNAMIC_HOSTNAME);
1283 stream_putc(s, name_len);
1284 stream_put(s, hostname, name_len);
1285 return 0;
1286}
1287
1288static int unpack_tlv_dynamic_hostname(enum isis_tlv_context context,
1289 uint8_t tlv_type, uint8_t tlv_len,
1290 struct stream *s, struct sbuf *log,
1291 void *dest, int indent)
1292{
1293 struct isis_tlvs *tlvs = dest;
1294
1295 sbuf_push(log, indent, "Unpacking Dynamic Hostname TLV...\n");
1296 if (!tlv_len) {
1297 sbuf_push(log, indent, "WARNING: No hostname included\n");
1298 return 0;
1299 }
1300
1301 if (tlvs->hostname) {
1302 sbuf_push(log, indent,
1303 "WARNING: Hostname present multiple times.\n");
1304 stream_forward_getp(s, tlv_len);
1305 return 0;
1306 }
1307
841791b6 1308 tlvs->hostname = XCALLOC(MTYPE_ISIS_TLV, tlv_len + 1);
7ef5fefc
CF
1309 stream_get(tlvs->hostname, s, tlv_len);
1310 tlvs->hostname[tlv_len] = '\0';
1311
1312 bool sane = true;
1313 for (uint8_t i = 0; i < tlv_len; i++) {
1314 if ((unsigned char)tlvs->hostname[i] > 127
1315 || !isprint(tlvs->hostname[i])) {
1316 sane = false;
1317 tlvs->hostname[i] = '?';
1318 }
1319 }
1320 if (!sane) {
1321 sbuf_push(
1322 log, indent,
1323 "WARNING: Hostname contained non-printable/non-ascii characters.\n");
1324 }
1325
1326 return 0;
1327}
1328
1329/* Functions related to TLVs 236/237 IPv6/MT-IPv6 reach */
1330
1331static struct isis_item *copy_item_ipv6_reach(struct isis_item *i)
1332{
1333 struct isis_ipv6_reach *r = (struct isis_ipv6_reach *)i;
841791b6 1334 struct isis_ipv6_reach *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1335 rv->metric = r->metric;
1336 rv->down = r->down;
1337 rv->external = r->external;
1338 rv->prefix = r->prefix;
1339 rv->subtlvs = copy_subtlvs(r->subtlvs);
1340
1341 return (struct isis_item *)rv;
1342}
1343
1344static void format_item_ipv6_reach(uint16_t mtid, struct isis_item *i,
1345 struct sbuf *buf, int indent)
1346{
1347 struct isis_ipv6_reach *r = (struct isis_ipv6_reach *)i;
1348 char prefixbuf[PREFIX2STR_BUFFER];
1349
1350 sbuf_push(buf, indent, "%sIPv6 Reachability: %s (Metric: %u)%s%s",
1351 (mtid == ISIS_MT_IPV4_UNICAST) ? "" : "MT ",
1352 prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf)),
1353 r->metric,
1354 r->down ? " Down" : "",
1355 r->external ? " External" : "");
1356 if (mtid != ISIS_MT_IPV4_UNICAST)
1357 sbuf_push(buf, 0, " %s", isis_mtid2str(mtid));
1358 sbuf_push(buf, 0, "\n");
1359
1360 if (r->subtlvs) {
1361 sbuf_push(buf, indent, " Subtlvs:\n");
1362 format_subtlvs(r->subtlvs, buf, indent + 4);
1363 }
1364}
1365
1366static void free_item_ipv6_reach(struct isis_item *i)
1367{
1368 struct isis_ipv6_reach *item = (struct isis_ipv6_reach *)i;
1369
1370 isis_free_subtlvs(item->subtlvs);
841791b6 1371 XFREE(MTYPE_ISIS_TLV, item);
7ef5fefc
CF
1372}
1373
1374static int pack_item_ipv6_reach(struct isis_item *i, struct stream *s)
1375{
1376 struct isis_ipv6_reach *r = (struct isis_ipv6_reach *)i;
1377 uint8_t control;
1378
1379 if (STREAM_WRITEABLE(s) < 6)
1380 return 1;
1381 stream_putl(s, r->metric);
1382
1383 control = r->down ? ISIS_IPV6_REACH_DOWN : 0;
1384 control |= r->external ? ISIS_IPV6_REACH_EXTERNAL : 0;
1385 control |= r->subtlvs ? ISIS_IPV6_REACH_SUBTLV : 0;
1386
1387 stream_putc(s, control);
1388 stream_putc(s, r->prefix.prefixlen);
1389
1390 if (STREAM_WRITEABLE(s) < (unsigned)PSIZE(r->prefix.prefixlen))
1391 return 1;
1392 stream_put(s, &r->prefix.prefix.s6_addr, PSIZE(r->prefix.prefixlen));
1393
1394 if (r->subtlvs)
1395 return pack_subtlvs(r->subtlvs, s);
1396
1397 return 0;
1398}
1399
1400static int unpack_item_ipv6_reach(uint16_t mtid, uint8_t len, struct stream *s,
1401 struct sbuf *log, void *dest, int indent)
1402{
1403 struct isis_tlvs *tlvs = dest;
1404 struct isis_ipv6_reach *rv = NULL;
1405 size_t consume;
1406 uint8_t control, subtlv_len;
1407 struct isis_item_list *items;
1408
1409 if (mtid == ISIS_MT_IPV4_UNICAST) {
1410 items = &tlvs->ipv6_reach;
1411 } else {
1412 items = isis_get_mt_items(&tlvs->mt_ipv6_reach, mtid);
1413 }
1414
1415 sbuf_push(log, indent, "Unpacking %sIPv6 reachability...\n",
1416 (mtid == ISIS_MT_IPV4_UNICAST) ? "" : "mt ");
1417 consume = 6;
1418 if (len < consume) {
1419 sbuf_push(log, indent,
1420 "Not enough data left. (expected 6 or more bytes, got %"
1421 PRIu8 ")\n",
1422 len);
1423 goto out;
1424 }
1425
841791b6 1426 rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1427
1428 rv->metric = stream_getl(s);
1429 control = stream_getc(s);
1430 rv->down = (control & ISIS_IPV6_REACH_DOWN);
1431 rv->external = (control & ISIS_IPV6_REACH_EXTERNAL);
1432
1433 rv->prefix.family = AF_INET6;
1434 rv->prefix.prefixlen = stream_getc(s);
1435 if (rv->prefix.prefixlen > 128) {
1436 sbuf_push(log, indent, "Prefixlen %u is inplausible for IPv6\n",
1437 rv->prefix.prefixlen);
1438 goto out;
1439 }
1440
1441 consume += PSIZE(rv->prefix.prefixlen);
1442 if (len < consume) {
1443 sbuf_push(log, indent,
1444 "Expected %u bytes of prefix, but only %u bytes available.\n",
1445 PSIZE(rv->prefix.prefixlen), len - 6);
1446 goto out;
1447 }
1448 stream_get(&rv->prefix.prefix.s6_addr, s, PSIZE(rv->prefix.prefixlen));
1449 struct in6_addr orig_prefix = rv->prefix.prefix;
1450 apply_mask_ipv6(&rv->prefix);
1451 if (memcmp(&orig_prefix, &rv->prefix.prefix, sizeof(orig_prefix)))
1452 sbuf_push(log, indent + 2,
1453 "WARNING: Prefix had hostbits set.\n");
1454 format_item_ipv6_reach(mtid, (struct isis_item *)rv, log, indent + 2);
1455
1456 if (control & ISIS_IPV6_REACH_SUBTLV) {
1457 consume += 1;
1458 if (len < consume) {
1459 sbuf_push(log, indent,
1460 "Expected 1 byte of subtlv len, but no more data persent.\n");
1461 goto out;
1462 }
1463 subtlv_len = stream_getc(s);
1464
1465 if (!subtlv_len) {
1466 sbuf_push(log, indent + 2,
1467 " WARNING: subtlv bit set, but there are no subtlvs.\n");
1468 }
1469 consume += subtlv_len;
1470 if (len < consume) {
1471 sbuf_push(log, indent,
1472 "Expected %" PRIu8
1473 " bytes of subtlvs, but only %u bytes available.\n",
1474 subtlv_len,
1475 len - 6 - PSIZE(rv->prefix.prefixlen));
1476 goto out;
1477 }
1478
1479 rv->subtlvs = isis_alloc_subtlvs();
1480 if (unpack_tlvs(ISIS_CONTEXT_SUBTLV_IPV6_REACH, subtlv_len, s,
1481 log, rv->subtlvs, indent + 4)) {
1482 goto out;
1483 }
1484 }
1485
1486 append_item(items, (struct isis_item *)rv);
1487 return 0;
1488out:
1489 if (rv)
1490 free_item_ipv6_reach((struct isis_item *)rv);
1491 return 1;
1492}
1493
1494/* Functions related to TLV 10 Authentication */
1495static struct isis_item *copy_item_auth(struct isis_item *i)
1496{
1497 struct isis_auth *auth = (struct isis_auth *)i;
841791b6 1498 struct isis_auth *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1499
1500 rv->type = auth->type;
1501 rv->length = auth->length;
1502 memcpy(rv->value, auth->value, sizeof(rv->value));
1503 return (struct isis_item *)rv;
1504}
1505
1506static void format_item_auth(uint16_t mtid, struct isis_item *i,
1507 struct sbuf *buf, int indent)
1508{
1509 struct isis_auth *auth = (struct isis_auth *)i;
1510 char obuf[768];
1511
1512 sbuf_push(buf, indent, "Authentication:\n");
1513 switch (auth->type) {
1514 case ISIS_PASSWD_TYPE_CLEARTXT:
1515 zlog_sanitize(obuf, sizeof(obuf), auth->value, auth->length);
1516 sbuf_push(buf, indent, " Password: %s\n", obuf);
1517 break;
1518 case ISIS_PASSWD_TYPE_HMAC_MD5:
1519 for (unsigned int i = 0; i < 16; i++) {
1520 snprintf(obuf + 2 * i, sizeof(obuf) - 2 * i,
1521 "%02" PRIx8, auth->value[i]);
1522 }
1523 sbuf_push(buf, indent, " HMAC-MD5: %s\n", obuf);
1524 break;
1525 default:
1526 sbuf_push(buf, indent, " Unknown (%" PRIu8 ")\n", auth->type);
1527 break;
1528 };
1529}
1530
1531static void free_item_auth(struct isis_item *i)
1532{
841791b6 1533 XFREE(MTYPE_ISIS_TLV, i);
7ef5fefc
CF
1534}
1535
1536static int pack_item_auth(struct isis_item *i, struct stream *s)
1537{
1538 struct isis_auth *auth = (struct isis_auth *)i;
1539
1540 if (STREAM_WRITEABLE(s) < 1)
1541 return 1;
1542 stream_putc(s, auth->type);
1543
1544 switch (auth->type) {
1545 case ISIS_PASSWD_TYPE_CLEARTXT:
1546 if (STREAM_WRITEABLE(s) < auth->length)
1547 return 1;
1548 stream_put(s, auth->passwd, auth->length);
1549 break;
1550 case ISIS_PASSWD_TYPE_HMAC_MD5:
1551 if (STREAM_WRITEABLE(s) < 16)
1552 return 1;
1553 auth->offset = stream_get_endp(s);
1554 stream_put(s, NULL, 16);
1555 break;
1556 default:
1557 return 1;
1558 }
1559
1560 return 0;
1561}
1562
1563static int unpack_item_auth(uint16_t mtid, uint8_t len, struct stream *s,
1564 struct sbuf *log, void *dest, int indent)
1565{
1566 struct isis_tlvs *tlvs = dest;
1567
1568 sbuf_push(log, indent, "Unpack Auth TLV...\n");
1569 if (len < 1) {
1570 sbuf_push(
1571 log, indent,
1572 "Not enough data left.(Expected 1 bytes of auth type, got %" PRIu8
1573 ")\n",
1574 len);
1575 return 1;
1576 }
1577
841791b6 1578 struct isis_auth *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1579
1580 rv->type = stream_getc(s);
1581 rv->length = len - 1;
1582
1583 if (rv->type == ISIS_PASSWD_TYPE_HMAC_MD5 && rv->length != 16) {
1584 sbuf_push(
1585 log, indent,
1586 "Unexpected auth length for HMAC-MD5 (expected 16, got %" PRIu8
1587 ")\n",
1588 rv->length);
841791b6 1589 XFREE(MTYPE_ISIS_TLV, rv);
7ef5fefc
CF
1590 return 1;
1591 }
1592
1593 rv->offset = stream_get_getp(s);
1594 stream_get(rv->value, s, rv->length);
1595 format_item_auth(mtid, (struct isis_item *)rv, log, indent + 2);
1596 append_item(&tlvs->isis_auth, (struct isis_item *)rv);
1597 return 0;
1598}
1599
1600/* Functions relating to item TLVs */
1601
1602static void init_item_list(struct isis_item_list *items)
1603{
1604 items->head = NULL;
1605 items->tail = &items->head;
1606 items->count = 0;
1607}
1608
1609static struct isis_item *copy_item(enum isis_tlv_context context,
1610 enum isis_tlv_type type,
1611 struct isis_item *item)
1612{
1613 const struct tlv_ops *ops = tlv_table[context][type];
1614
1615 if (ops && ops->copy_item)
1616 return ops->copy_item(item);
1617
1618 assert(!"Unknown item tlv type!");
1619 return NULL;
1620}
1621
1622static void copy_items(enum isis_tlv_context context, enum isis_tlv_type type,
1623 struct isis_item_list *src, struct isis_item_list *dest)
1624{
1625 struct isis_item *item;
1626
1627 init_item_list(dest);
1628
1629 for (item = src->head; item; item = item->next) {
1630 append_item(dest, copy_item(context, type, item));
1631 }
1632}
1633
1634static void format_item(uint16_t mtid, enum isis_tlv_context context,
1635 enum isis_tlv_type type, struct isis_item *i,
1636 struct sbuf *buf, int indent)
1637{
1638 const struct tlv_ops *ops = tlv_table[context][type];
1639
1640 if (ops && ops->format_item) {
1641 ops->format_item(mtid, i, buf, indent);
1642 return;
1643 }
1644
1645 assert(!"Unknown item tlv type!");
1646}
1647
1648static void format_items_(uint16_t mtid, enum isis_tlv_context context,
1649 enum isis_tlv_type type, struct isis_item_list *items,
1650 struct sbuf *buf, int indent)
1651{
1652 struct isis_item *i;
1653
1654 for (i = items->head; i; i = i->next)
1655 format_item(mtid, context, type, i, buf, indent);
1656}
1657#define format_items(...) format_items_(ISIS_MT_IPV4_UNICAST, __VA_ARGS__)
1658
1659static void free_item(enum isis_tlv_context tlv_context,
1660 enum isis_tlv_type tlv_type, struct isis_item *item)
1661{
1662 const struct tlv_ops *ops = tlv_table[tlv_context][tlv_type];
1663
1664 if (ops && ops->free_item) {
1665 ops->free_item(item);
1666 return;
1667 }
1668
1669 assert(!"Unknown item tlv type!");
1670}
1671
1672static void free_items(enum isis_tlv_context context, enum isis_tlv_type type,
1673 struct isis_item_list *items)
1674{
1675 struct isis_item *item, *next_item;
1676
1677 for (item = items->head; item; item = next_item) {
1678 next_item = item->next;
1679 free_item(context, type, item);
1680 }
1681}
1682
1683static int pack_item(enum isis_tlv_context context, enum isis_tlv_type type,
1684 struct isis_item *i, struct stream *s,
1685 struct isis_tlvs **fragment_tlvs,
1686 struct pack_order_entry *pe, uint16_t mtid)
1687{
1688 const struct tlv_ops *ops = tlv_table[context][type];
1689
1690 if (ops && ops->pack_item) {
1691 return ops->pack_item(i, s);
1692 }
1693
1694 assert(!"Unknown item tlv type!");
1695 return 1;
1696}
1697
1698static void add_item_to_fragment(struct isis_item *i, struct pack_order_entry *pe,
1699 struct isis_tlvs *fragment_tlvs, uint16_t mtid)
1700{
1701 struct isis_item_list *l;
1702
1703 if (pe->how_to_pack == ISIS_ITEMS) {
1704 l = (struct isis_item_list *)(((char *)fragment_tlvs) + pe->what_to_pack);
1705 } else {
1706 struct isis_mt_item_list *m;
1707 m = (struct isis_mt_item_list *)(((char *)fragment_tlvs) + pe->what_to_pack);
1708 l = isis_get_mt_items(m, mtid);
1709 }
1710
1711 append_item(l, copy_item(pe->context, pe->type, i));
1712}
1713
1714static int pack_items_(uint16_t mtid, enum isis_tlv_context context,
1715 enum isis_tlv_type type, struct isis_item_list *items,
1716 struct stream *s, struct isis_tlvs **fragment_tlvs,
1717 struct pack_order_entry *pe,
1718 struct isis_tlvs *(*new_fragment)(struct list *l),
1719 struct list *new_fragment_arg)
1720{
1721 size_t len_pos, last_len, len;
1722 struct isis_item *item = NULL;
1723 int rv;
1724
1725 if (!items->head)
1726 return 0;
1727
1728top:
1729 if (STREAM_WRITEABLE(s) < 2)
1730 goto too_long;
1731
1732 stream_putc(s, type);
1733 len_pos = stream_get_endp(s);
1734 stream_putc(s, 0); /* Put 0 as length for now */
1735
1736 if (context == ISIS_CONTEXT_LSP && IS_COMPAT_MT_TLV(type)
1737 && mtid != ISIS_MT_IPV4_UNICAST) {
1738 if (STREAM_WRITEABLE(s) < 2)
1739 goto too_long;
1740 stream_putw(s, mtid);
1741 }
1742
1743 if (context == ISIS_CONTEXT_LSP && type == ISIS_TLV_OLDSTYLE_REACH) {
1744 if (STREAM_WRITEABLE(s) < 1)
1745 goto too_long;
1746 stream_putc(s, 0); /* Virtual flag is set to 0 */
1747 }
1748
1749 last_len = len = 0;
1750 for (item = item ? item : items->head; item; item = item->next) {
1751 rv = pack_item(context, type, item, s, fragment_tlvs, pe, mtid);
1752 if (rv)
1753 goto too_long;
1754
1755 len = stream_get_endp(s) - len_pos - 1;
1756
1757 /* Multiple auths don't go into one TLV, so always break */
1758 if (context == ISIS_CONTEXT_LSP && type == ISIS_TLV_AUTH) {
1759 item = item->next;
1760 break;
1761 }
1762
1763 if (len > 255) {
1764 if (!last_len) /* strange, not a single item fit */
1765 return 1;
1766 /* drop last tlv, otherwise, its too long */
1767 stream_set_endp(s, len_pos + 1 + last_len);
1768 len = last_len;
1769 break;
1770 }
1771
1772 if (fragment_tlvs)
1773 add_item_to_fragment(item, pe, *fragment_tlvs, mtid);
1774
1775 last_len = len;
1776 }
1777
1778 stream_putc_at(s, len_pos, len);
1779 if (item)
1780 goto top;
1781
1782 return 0;
1783too_long:
1784 if (!fragment_tlvs)
1785 return 1;
1786 stream_reset(s);
1787 *fragment_tlvs = new_fragment(new_fragment_arg);
1788 goto top;
1789}
1790#define pack_items(...) pack_items_(ISIS_MT_IPV4_UNICAST, __VA_ARGS__)
1791
1792static void append_item(struct isis_item_list *dest, struct isis_item *item)
1793{
1794 *dest->tail = item;
1795 dest->tail = &(*dest->tail)->next;
1796 dest->count++;
1797}
1798
1799static int unpack_item(uint16_t mtid, enum isis_tlv_context context,
1800 uint8_t tlv_type, uint8_t len, struct stream *s,
1801 struct sbuf *log, void *dest, int indent)
1802{
1803 const struct tlv_ops *ops = tlv_table[context][tlv_type];
1804
1805 if (ops && ops->unpack_item)
1806 return ops->unpack_item(mtid, len, s, log, dest, indent);
1807
1808 assert(!"Unknown item tlv type!");
1809 sbuf_push(log, indent, "Unknown item tlv type!\n");
1810 return 1;
1811}
1812
1813static int unpack_tlv_with_items(enum isis_tlv_context context,
1814 uint8_t tlv_type, uint8_t tlv_len,
1815 struct stream *s, struct sbuf *log, void *dest,
1816 int indent)
1817{
1818 size_t tlv_start;
1819 size_t tlv_pos;
1820 int rv;
1821 uint16_t mtid;
1822
1823 tlv_start = stream_get_getp(s);
1824 tlv_pos = 0;
1825
1826 if (context == ISIS_CONTEXT_LSP && IS_COMPAT_MT_TLV(tlv_type)) {
1827 if (tlv_len < 2) {
1828 sbuf_push(log, indent,
1829 "TLV is too short to contain MTID\n");
1830 return 1;
1831 }
1832 mtid = stream_getw(s) & ISIS_MT_MASK;
1833 tlv_pos += 2;
1834 sbuf_push(log, indent, "Unpacking as MT %s item TLV...\n",
1835 isis_mtid2str(mtid));
1836 } else {
1837 sbuf_push(log, indent, "Unpacking as item TLV...\n");
1838 mtid = ISIS_MT_IPV4_UNICAST;
1839 }
1840
1841 if (context == ISIS_CONTEXT_LSP
1842 && tlv_type == ISIS_TLV_OLDSTYLE_REACH) {
1843 if (tlv_len - tlv_pos < 1) {
1844 sbuf_push(log, indent,
1845 "TLV is too short for old style reach\n");
1846 return 1;
1847 }
1848 stream_forward_getp(s, 1);
1849 tlv_pos += 1;
1850 }
1851
1852 if (context == ISIS_CONTEXT_LSP
1853 && tlv_type == ISIS_TLV_OLDSTYLE_IP_REACH) {
1854 struct isis_tlvs *tlvs = dest;
1855 dest = &tlvs->oldstyle_ip_reach;
1856 } else if (context == ISIS_CONTEXT_LSP
1857 && tlv_type == ISIS_TLV_OLDSTYLE_IP_REACH_EXT) {
1858 struct isis_tlvs *tlvs = dest;
1859 dest = &tlvs->oldstyle_ip_reach_ext;
1860 }
1861
1862 if (context == ISIS_CONTEXT_LSP
1863 && tlv_type == ISIS_TLV_MT_ROUTER_INFO) {
1864 struct isis_tlvs *tlvs = dest;
1865 tlvs->mt_router_info_empty = (tlv_pos >= (size_t)tlv_len);
1866 }
1867
1868 while (tlv_pos < (size_t)tlv_len) {
1869 rv = unpack_item(mtid, context, tlv_type, tlv_len - tlv_pos, s,
1870 log, dest, indent + 2);
1871 if (rv)
1872 return rv;
1873
1874 tlv_pos = stream_get_getp(s) - tlv_start;
1875 }
1876
1877 return 0;
1878}
1879
1880/* Functions to manipulate mt_item_lists */
1881
1882static int isis_mt_item_list_cmp(const struct isis_item_list *a,
1883 const struct isis_item_list *b)
1884{
1885 if (a->mtid < b->mtid)
1886 return -1;
1887 if (a->mtid > b->mtid)
1888 return 1;
1889 return 0;
1890}
1891
1892RB_PROTOTYPE(isis_mt_item_list, isis_item_list, mt_tree, isis_mt_item_list_cmp);
1893RB_GENERATE(isis_mt_item_list, isis_item_list, mt_tree, isis_mt_item_list_cmp);
1894
1895struct isis_item_list *isis_get_mt_items(struct isis_mt_item_list *m,
1896 uint16_t mtid)
1897{
1898 struct isis_item_list *rv;
1899
1900 rv = isis_lookup_mt_items(m, mtid);
1901 if (!rv) {
1902 rv = XCALLOC(MTYPE_ISIS_MT_ITEM_LIST, sizeof(*rv));
1903 init_item_list(rv);
1904 rv->mtid = mtid;
1905 RB_INSERT(isis_mt_item_list, m, rv);
1906 }
1907
1908 return rv;
1909}
1910
1911struct isis_item_list *isis_lookup_mt_items(struct isis_mt_item_list *m,
1912 uint16_t mtid)
1913{
1914 struct isis_item_list key = {.mtid = mtid};
1915
1916 return RB_FIND(isis_mt_item_list, m, &key);
1917}
1918
1919static void free_mt_items(enum isis_tlv_context context,
1920 enum isis_tlv_type type, struct isis_mt_item_list *m)
1921{
1922 struct isis_item_list *n, *nnext;
1923
a2addae8 1924 RB_FOREACH_SAFE (n, isis_mt_item_list, m, nnext) {
7ef5fefc
CF
1925 free_items(context, type, n);
1926 RB_REMOVE(isis_mt_item_list, m, n);
1927 XFREE(MTYPE_ISIS_MT_ITEM_LIST, n);
1928 }
1929}
1930
1931static void format_mt_items(enum isis_tlv_context context,
1932 enum isis_tlv_type type,
1933 struct isis_mt_item_list *m, struct sbuf *buf,
1934 int indent)
1935{
1936 struct isis_item_list *n;
1937
a2addae8 1938 RB_FOREACH (n, isis_mt_item_list, m) {
7ef5fefc
CF
1939 format_items_(n->mtid, context, type, n, buf, indent);
1940 }
1941}
1942
1943static int pack_mt_items(enum isis_tlv_context context, enum isis_tlv_type type,
1944 struct isis_mt_item_list *m, struct stream *s,
1945 struct isis_tlvs **fragment_tlvs,
1946 struct pack_order_entry *pe,
1947 struct isis_tlvs *(*new_fragment)(struct list *l),
1948 struct list *new_fragment_arg)
1949{
1950 struct isis_item_list *n;
1951
a2addae8 1952 RB_FOREACH (n, isis_mt_item_list, m) {
7ef5fefc
CF
1953 int rv;
1954
1955 rv = pack_items_(n->mtid, context, type, n, s, fragment_tlvs,
1956 pe, new_fragment, new_fragment_arg);
1957 if (rv)
1958 return rv;
1959 }
1960
1961 return 0;
1962}
1963
1964static void copy_mt_items(enum isis_tlv_context context,
1965 enum isis_tlv_type type,
1966 struct isis_mt_item_list *src,
1967 struct isis_mt_item_list *dest)
1968{
1969 struct isis_item_list *n;
1970
1971 RB_INIT(isis_mt_item_list, dest);
1972
a2addae8 1973 RB_FOREACH (n, isis_mt_item_list, src) {
7ef5fefc
CF
1974 copy_items(context, type, n, isis_get_mt_items(dest, n->mtid));
1975 }
1976}
1977
1978/* Functions related to tlvs in general */
1979
1980struct isis_tlvs *isis_alloc_tlvs(void)
1981{
1982 struct isis_tlvs *result;
1983
841791b6 1984 result = XCALLOC(MTYPE_ISIS_TLV, sizeof(*result));
7ef5fefc
CF
1985
1986 init_item_list(&result->isis_auth);
1987 init_item_list(&result->area_addresses);
1988 init_item_list(&result->mt_router_info);
1989 init_item_list(&result->oldstyle_reach);
1990 init_item_list(&result->lan_neighbor);
1991 init_item_list(&result->lsp_entries);
1992 init_item_list(&result->extended_reach);
1993 RB_INIT(isis_mt_item_list, &result->mt_reach);
1994 init_item_list(&result->oldstyle_ip_reach);
1995 init_item_list(&result->oldstyle_ip_reach_ext);
1996 init_item_list(&result->ipv4_address);
1997 init_item_list(&result->ipv6_address);
1998 init_item_list(&result->extended_ip_reach);
1999 RB_INIT(isis_mt_item_list, &result->mt_ip_reach);
2000 init_item_list(&result->ipv6_reach);
2001 RB_INIT(isis_mt_item_list, &result->mt_ipv6_reach);
2002
2003 return result;
2004}
2005
2006struct isis_tlvs *isis_copy_tlvs(struct isis_tlvs *tlvs)
2007{
841791b6 2008 struct isis_tlvs *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
2009
2010 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth,
2011 &rv->isis_auth);
2012
2013 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES,
2014 &tlvs->area_addresses, &rv->area_addresses);
2015
2016 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO,
2017 &tlvs->mt_router_info, &rv->mt_router_info);
2018
2019 tlvs->mt_router_info_empty = rv->mt_router_info_empty;
2020
2021 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_REACH,
2022 &tlvs->oldstyle_reach, &rv->oldstyle_reach);
2023
2024 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_LAN_NEIGHBORS,
2025 &tlvs->lan_neighbor, &rv->lan_neighbor);
2026
2027 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_LSP_ENTRY, &tlvs->lsp_entries,
2028 &rv->lsp_entries);
2029
2030 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_REACH,
2031 &tlvs->extended_reach, &rv->extended_reach);
2032
2033 copy_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_REACH, &tlvs->mt_reach,
2034 &rv->mt_reach);
2035
2036 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH,
2037 &tlvs->oldstyle_ip_reach, &rv->oldstyle_ip_reach);
2038
2039 copy_tlv_protocols_supported(&tlvs->protocols_supported,
2040 &rv->protocols_supported);
2041
2042 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH_EXT,
2043 &tlvs->oldstyle_ip_reach_ext, &rv->oldstyle_ip_reach_ext);
2044
2045 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV4_ADDRESS, &tlvs->ipv4_address,
2046 &rv->ipv4_address);
2047
2048 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_ADDRESS, &tlvs->ipv6_address,
2049 &rv->ipv6_address);
2050
2051 rv->te_router_id = copy_tlv_te_router_id(tlvs->te_router_id);
2052
2053 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_IP_REACH,
2054 &tlvs->extended_ip_reach, &rv->extended_ip_reach);
2055
2056 copy_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IP_REACH,
2057 &tlvs->mt_ip_reach, &rv->mt_ip_reach);
2058
2059 rv->hostname = copy_tlv_dynamic_hostname(tlvs->hostname);
2060
2061 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_REACH, &tlvs->ipv6_reach,
2062 &rv->ipv6_reach);
2063
2064 copy_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IPV6_REACH,
2065 &tlvs->mt_ipv6_reach, &rv->mt_ipv6_reach);
2066
2067 return rv;
2068}
2069
2070static void format_tlvs(struct isis_tlvs *tlvs, struct sbuf *buf, int indent)
2071{
2072 format_tlv_protocols_supported(&tlvs->protocols_supported, buf, indent);
2073
2074 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth, buf,
2075 indent);
2076
2077 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES,
2078 &tlvs->area_addresses, buf, indent);
2079
2080 if (tlvs->mt_router_info_empty) {
2081 sbuf_push(buf, indent, "MT Router Info: None\n");
2082 } else {
2083 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO,
2084 &tlvs->mt_router_info, buf, indent);
2085 }
2086
2087 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_REACH,
2088 &tlvs->oldstyle_reach, buf, indent);
2089
2090 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_LAN_NEIGHBORS,
2091 &tlvs->lan_neighbor, buf, indent);
2092
2093 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_LSP_ENTRY, &tlvs->lsp_entries,
2094 buf, indent);
2095
2096 format_tlv_dynamic_hostname(tlvs->hostname, buf, indent);
2097 format_tlv_te_router_id(tlvs->te_router_id, buf, indent);
2098
2099 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_REACH,
2100 &tlvs->extended_reach, buf, indent);
2101
2102 format_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_REACH, &tlvs->mt_reach,
2103 buf, indent);
2104
2105 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH,
2106 &tlvs->oldstyle_ip_reach, buf, indent);
2107
2108 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH_EXT,
2109 &tlvs->oldstyle_ip_reach_ext, buf, indent);
2110
2111 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV4_ADDRESS,
2112 &tlvs->ipv4_address, buf, indent);
2113
2114 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_ADDRESS,
2115 &tlvs->ipv6_address, buf, indent);
2116
2117 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_IP_REACH,
2118 &tlvs->extended_ip_reach, buf, indent);
2119
2120 format_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IP_REACH,
2121 &tlvs->mt_ip_reach, buf, indent);
2122
2123 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_REACH, &tlvs->ipv6_reach,
2124 buf, indent);
2125
2126 format_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IPV6_REACH,
2127 &tlvs->mt_ipv6_reach, buf, indent);
2128}
2129
2130const char *isis_format_tlvs(struct isis_tlvs *tlvs)
2131{
2132 static struct sbuf buf;
2133
2134 if (!sbuf_buf(&buf))
2135 sbuf_init(&buf, NULL, 0);
2136
2137 sbuf_reset(&buf);
2138 format_tlvs(tlvs, &buf, 0);
2139 return sbuf_buf(&buf);
2140}
2141
2142void isis_free_tlvs(struct isis_tlvs *tlvs)
2143{
2144 if (!tlvs)
2145 return;
2146
2147 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth);
2148 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES,
2149 &tlvs->area_addresses);
2150 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO,
2151 &tlvs->mt_router_info);
2152 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_REACH,
2153 &tlvs->oldstyle_reach);
2154 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_LAN_NEIGHBORS,
2155 &tlvs->lan_neighbor);
2156 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_LSP_ENTRY, &tlvs->lsp_entries);
2157 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_REACH,
2158 &tlvs->extended_reach);
2159 free_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_REACH, &tlvs->mt_reach);
2160 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH,
2161 &tlvs->oldstyle_ip_reach);
2162 free_tlv_protocols_supported(&tlvs->protocols_supported);
2163 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH_EXT,
2164 &tlvs->oldstyle_ip_reach_ext);
2165 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV4_ADDRESS,
2166 &tlvs->ipv4_address);
2167 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_ADDRESS,
2168 &tlvs->ipv6_address);
2169 free_tlv_te_router_id(tlvs->te_router_id);
2170 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_IP_REACH,
2171 &tlvs->extended_ip_reach);
2172 free_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IP_REACH,
2173 &tlvs->mt_ip_reach);
2174 free_tlv_dynamic_hostname(tlvs->hostname);
2175 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_REACH, &tlvs->ipv6_reach);
2176 free_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IPV6_REACH,
2177 &tlvs->mt_ipv6_reach);
2178
841791b6 2179 XFREE(MTYPE_ISIS_TLV, tlvs);
7ef5fefc
CF
2180}
2181
2182static void add_padding(struct stream *s)
2183{
2184 while (STREAM_WRITEABLE(s)) {
2185 if (STREAM_WRITEABLE(s) == 1)
2186 break;
2187 uint32_t padding_len = STREAM_WRITEABLE(s) - 2;
2188
2189 if (padding_len > 255) {
2190 if (padding_len == 256)
2191 padding_len = 254;
2192 else
2193 padding_len = 255;
2194 }
2195
2196 stream_putc(s, ISIS_TLV_PADDING);
2197 stream_putc(s, padding_len);
2198 stream_put(s, NULL, padding_len);
2199 }
2200}
2201
2202#define LSP_REM_LIFETIME_OFF 10
2203#define LSP_CHECKSUM_OFF 24
2204static void safe_auth_md5(struct stream *s, uint16_t *checksum,
2205 uint16_t *rem_lifetime)
2206{
2207 memcpy(rem_lifetime, STREAM_DATA(s) + LSP_REM_LIFETIME_OFF,
2208 sizeof(*rem_lifetime));
2209 memset(STREAM_DATA(s) + LSP_REM_LIFETIME_OFF, 0, sizeof(*rem_lifetime));
2210 memcpy(checksum, STREAM_DATA(s) + LSP_CHECKSUM_OFF, sizeof(*checksum));
2211 memset(STREAM_DATA(s) + LSP_CHECKSUM_OFF, 0, sizeof(*checksum));
2212}
2213
2214static void restore_auth_md5(struct stream *s, uint16_t checksum,
2215 uint16_t rem_lifetime)
2216{
2217 memcpy(STREAM_DATA(s) + LSP_REM_LIFETIME_OFF, &rem_lifetime,
2218 sizeof(rem_lifetime));
2219 memcpy(STREAM_DATA(s) + LSP_CHECKSUM_OFF, &checksum, sizeof(checksum));
2220}
2221
2222static void update_auth_hmac_md5(struct isis_auth *auth, struct stream *s,
2223 bool is_lsp)
2224{
2225 uint8_t digest[16];
2226 uint16_t checksum, rem_lifetime;
2227
2228 if (is_lsp)
2229 safe_auth_md5(s, &checksum, &rem_lifetime);
2230
2231 memset(STREAM_DATA(s) + auth->offset, 0, 16);
2232 hmac_md5(STREAM_DATA(s), stream_get_endp(s), auth->passwd,
2233 auth->plength, digest);
2234 memcpy(auth->value, digest, 16);
2235 memcpy(STREAM_DATA(s) + auth->offset, digest, 16);
2236
2237 if (is_lsp)
2238 restore_auth_md5(s, checksum, rem_lifetime);
2239}
2240
2241static void update_auth(struct isis_tlvs *tlvs, struct stream *s, bool is_lsp)
2242{
2243 struct isis_auth *auth_head = (struct isis_auth *)tlvs->isis_auth.head;
2244
2245 for (struct isis_auth *auth = auth_head; auth; auth = auth->next) {
2246 if (auth->type == ISIS_PASSWD_TYPE_HMAC_MD5)
2247 update_auth_hmac_md5(auth, s, is_lsp);
2248 }
2249}
2250
2251static int handle_pack_entry(struct pack_order_entry *pe,
2252 struct isis_tlvs *tlvs, struct stream *stream,
2253 struct isis_tlvs **fragment_tlvs,
2254 struct isis_tlvs *(*new_fragment)(struct list *l),
2255 struct list *new_fragment_arg)
2256{
2257 int rv;
2258
2259 if (pe->how_to_pack == ISIS_ITEMS) {
2260 struct isis_item_list *l;
2261 l = (struct isis_item_list *)(((char *)tlvs)
2262 + pe->what_to_pack);
2263 rv = pack_items(pe->context, pe->type, l, stream, fragment_tlvs,
2264 pe, new_fragment, new_fragment_arg);
2265 } else {
2266 struct isis_mt_item_list *l;
2267 l = (struct isis_mt_item_list *)(((char *)tlvs)
2268 + pe->what_to_pack);
2269 rv = pack_mt_items(pe->context, pe->type, l, stream,
2270 fragment_tlvs, pe, new_fragment,
2271 new_fragment_arg);
2272 }
2273
2274 return rv;
2275}
2276
2277static int pack_tlvs(struct isis_tlvs *tlvs, struct stream *stream,
2278 struct isis_tlvs *fragment_tlvs,
2279 struct isis_tlvs *(*new_fragment)(struct list *l),
2280 struct list *new_fragment_arg)
2281{
2282 int rv;
2283
2284 /* When fragmenting, don't add auth as it's already accounted for in the
2285 * size we are given. */
2286 if (!fragment_tlvs) {
2287 rv = pack_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth,
2288 stream, NULL, NULL, NULL, NULL);
2289 if (rv)
2290 return rv;
2291 }
2292
2293 rv = pack_tlv_protocols_supported(&tlvs->protocols_supported, stream);
2294 if (rv)
2295 return rv;
2296 if (fragment_tlvs) {
2297 copy_tlv_protocols_supported(
2298 &tlvs->protocols_supported,
2299 &fragment_tlvs->protocols_supported);
2300 }
2301
2302 rv = pack_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES,
2303 &tlvs->area_addresses, stream, NULL, NULL, NULL, NULL);
2304 if (rv)
2305 return rv;
2306 if (fragment_tlvs) {
2307 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES,
2308 &tlvs->area_addresses,
2309 &fragment_tlvs->area_addresses);
2310 }
2311
2312
2313 if (tlvs->mt_router_info_empty) {
2314 if (STREAM_WRITEABLE(stream) < 2)
2315 return 1;
2316 stream_putc(stream, ISIS_TLV_MT_ROUTER_INFO);
2317 stream_putc(stream, 0);
2318 if (fragment_tlvs)
2319 fragment_tlvs->mt_router_info_empty = true;
2320 } else {
2321 rv = pack_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO,
2322 &tlvs->mt_router_info, stream, NULL, NULL, NULL,
2323 NULL);
2324 if (rv)
2325 return rv;
2326 if (fragment_tlvs) {
2327 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO,
2328 &tlvs->mt_router_info,
2329 &fragment_tlvs->mt_router_info);
2330 }
2331 }
2332
2333 rv = pack_tlv_dynamic_hostname(tlvs->hostname, stream);
2334 if (rv)
2335 return rv;
2336 if (fragment_tlvs)
2337 fragment_tlvs->hostname =
2338 copy_tlv_dynamic_hostname(tlvs->hostname);
2339
2340 rv = pack_tlv_te_router_id(tlvs->te_router_id, stream);
2341 if (rv)
2342 return rv;
2343 if (fragment_tlvs) {
2344 fragment_tlvs->te_router_id =
2345 copy_tlv_te_router_id(tlvs->te_router_id);
2346 }
2347
2348 for (size_t pack_idx = 0; pack_idx < array_size(pack_order);
2349 pack_idx++) {
2350 rv = handle_pack_entry(&pack_order[pack_idx], tlvs, stream,
2351 fragment_tlvs ? &fragment_tlvs : NULL,
2352 new_fragment, new_fragment_arg);
2353
2354 if (rv)
2355 return rv;
2356 }
2357
2358 return 0;
2359}
2360
2361int isis_pack_tlvs(struct isis_tlvs *tlvs, struct stream *stream,
2362 size_t len_pointer, bool pad, bool is_lsp)
2363{
2364 int rv;
2365
2366 rv = pack_tlvs(tlvs, stream, NULL, NULL, NULL);
2367 if (rv)
2368 return rv;
2369
2370 if (pad)
2371 add_padding(stream);
2372
2373 if (len_pointer != (size_t)-1) {
2374 stream_putw_at(stream, len_pointer, stream_get_endp(stream));
2375 }
2376
2377 update_auth(tlvs, stream, is_lsp);
2378
2379 return 0;
2380}
2381
2382static struct isis_tlvs *new_fragment(struct list *l)
2383{
2384 struct isis_tlvs *rv = isis_alloc_tlvs();
2385
2386 listnode_add(l, rv);
2387 return rv;
2388}
2389
2390struct list *isis_fragment_tlvs(struct isis_tlvs *tlvs, size_t size)
2391{
2392 struct stream *dummy_stream = stream_new(size);
2393 struct list *rv = list_new();
2394 struct isis_tlvs *fragment_tlvs = new_fragment(rv);
2395
2396 if (pack_tlvs(tlvs, dummy_stream, fragment_tlvs, new_fragment, rv)) {
2397 struct listnode *node;
2398 for (ALL_LIST_ELEMENTS_RO(rv, node, fragment_tlvs))
2399 isis_free_tlvs(fragment_tlvs);
affe9e99 2400 list_delete_and_null(&rv);
7ef5fefc
CF
2401 }
2402
2403 stream_free(dummy_stream);
2404 return rv;
2405}
2406
2407static int unpack_tlv_unknown(enum isis_tlv_context context, uint8_t tlv_type,
2408 uint8_t tlv_len, struct stream *s,
2409 struct sbuf *log, int indent)
2410{
2411 stream_forward_getp(s, tlv_len);
2412 sbuf_push(log, indent,
2413 "Skipping unknown TLV %" PRIu8 " (%" PRIu8 " bytes)\n",
2414 tlv_type, tlv_len);
2415 return 0;
2416}
2417
2418static int unpack_tlv(enum isis_tlv_context context, size_t avail_len,
2419 struct stream *stream, struct sbuf *log, void *dest,
2420 int indent)
2421{
2422 uint8_t tlv_type, tlv_len;
2423 const struct tlv_ops *ops;
2424
2425 sbuf_push(log, indent, "Unpacking TLV...\n");
2426
2427 if (avail_len < 2) {
2428 sbuf_push(
2429 log, indent + 2,
2430 "Available data %zu too short to contain a TLV header.\n",
2431 avail_len);
2432 return 1;
2433 }
2434
2435 tlv_type = stream_getc(stream);
2436 tlv_len = stream_getc(stream);
2437
2438 sbuf_push(log, indent + 2,
2439 "Found TLV of type %" PRIu8 " and len %" PRIu8 ".\n",
2440 tlv_type, tlv_len);
2441
2442 if (avail_len < ((size_t)tlv_len) + 2) {
2443 sbuf_push(log, indent + 2,
2444 "Available data %zu too short for claimed TLV len %" PRIu8 ".\n",
2445 avail_len - 2, tlv_len);
2446 return 1;
2447 }
2448
2449 ops = tlv_table[context][tlv_type];
2450 if (ops && ops->unpack) {
2451 return ops->unpack(context, tlv_type, tlv_len, stream, log,
2452 dest, indent + 2);
2453 }
2454
2455 return unpack_tlv_unknown(context, tlv_type, tlv_len, stream, log,
2456 indent + 2);
2457}
2458
2459static int unpack_tlvs(enum isis_tlv_context context, size_t avail_len,
2460 struct stream *stream, struct sbuf *log, void *dest,
2461 int indent)
2462{
2463 int rv;
2464 size_t tlv_start, tlv_pos;
2465
2466 tlv_start = stream_get_getp(stream);
2467 tlv_pos = 0;
2468
2469 sbuf_push(log, indent, "Unpacking %zu bytes of %s...\n", avail_len,
2470 (context == ISIS_CONTEXT_LSP) ? "TLVs" : "sub-TLVs");
2471
2472 while (tlv_pos < avail_len) {
2473 rv = unpack_tlv(context, avail_len - tlv_pos, stream, log, dest,
2474 indent + 2);
2475 if (rv)
2476 return rv;
2477
2478 tlv_pos = stream_get_getp(stream) - tlv_start;
2479 }
2480
2481 return 0;
2482}
2483
2484int isis_unpack_tlvs(size_t avail_len, struct stream *stream,
2485 struct isis_tlvs **dest, const char **log)
2486{
2487 static struct sbuf logbuf;
2488 int indent = 0;
2489 int rv;
2490 struct isis_tlvs *result;
2491
2492 if (!sbuf_buf(&logbuf))
2493 sbuf_init(&logbuf, NULL, 0);
2494
2495 sbuf_reset(&logbuf);
2496 if (avail_len > STREAM_READABLE(stream)) {
2497 sbuf_push(&logbuf, indent,
2498 "Stream doesn't contain sufficient data. "
2499 "Claimed %zu, available %zu\n",
2500 avail_len, STREAM_READABLE(stream));
2501 return 1;
2502 }
2503
2504 result = isis_alloc_tlvs();
2505 rv = unpack_tlvs(ISIS_CONTEXT_LSP, avail_len, stream, &logbuf, result,
2506 indent);
2507
2508 *log = sbuf_buf(&logbuf);
2509 *dest = result;
2510
2511 return rv;
2512}
2513
2514#define TLV_OPS(_name_, _desc_) \
2515 static const struct tlv_ops tlv_##_name_##_ops = { \
2516 .name = _desc_, .unpack = unpack_tlv_##_name_, \
2517 }
2518
2519#define ITEM_TLV_OPS(_name_, _desc_) \
2520 static const struct tlv_ops tlv_##_name_##_ops = { \
2521 .name = _desc_, \
2522 .unpack = unpack_tlv_with_items, \
2523 \
2524 .pack_item = pack_item_##_name_, \
2525 .free_item = free_item_##_name_, \
2526 .unpack_item = unpack_item_##_name_, \
2527 .format_item = format_item_##_name_, \
2528 .copy_item = copy_item_##_name_}
2529
2530#define SUBTLV_OPS(_name_, _desc_) \
2531 static const struct tlv_ops subtlv_##_name_##_ops = { \
2532 .name = _desc_, .unpack = unpack_subtlv_##_name_, \
2533 }
2534
2535ITEM_TLV_OPS(area_address, "TLV 1 Area Addresses");
2536ITEM_TLV_OPS(oldstyle_reach, "TLV 2 IS Reachability");
2537ITEM_TLV_OPS(lan_neighbor, "TLV 6 LAN Neighbors");
2538ITEM_TLV_OPS(lsp_entry, "TLV 9 LSP Entries");
2539ITEM_TLV_OPS(auth, "TLV 10 IS-IS Auth");
2540ITEM_TLV_OPS(extended_reach, "TLV 22 Extended Reachability");
2541ITEM_TLV_OPS(oldstyle_ip_reach, "TLV 128/130 IP Reachability");
2542TLV_OPS(protocols_supported, "TLV 129 Protocols Supported");
2543ITEM_TLV_OPS(ipv4_address, "TLV 132 IPv4 Interface Address");
2544TLV_OPS(te_router_id, "TLV 134 TE Router ID");
2545ITEM_TLV_OPS(extended_ip_reach, "TLV 135 Extended IP Reachability");
2546TLV_OPS(dynamic_hostname, "TLV 137 Dynamic Hostname");
2547ITEM_TLV_OPS(mt_router_info, "TLV 229 MT Router Information");
2548ITEM_TLV_OPS(ipv6_address, "TLV 232 IPv6 Interface Address");
2549ITEM_TLV_OPS(ipv6_reach, "TLV 236 IPv6 Reachability");
2550
2551SUBTLV_OPS(ipv6_source_prefix, "Sub-TLV 22 IPv6 Source Prefix");
2552
2553static const struct tlv_ops *tlv_table[ISIS_CONTEXT_MAX][ISIS_TLV_MAX] = {
2554 [ISIS_CONTEXT_LSP] = {
2555 [ISIS_TLV_AREA_ADDRESSES] = &tlv_area_address_ops,
2556 [ISIS_TLV_OLDSTYLE_REACH] = &tlv_oldstyle_reach_ops,
2557 [ISIS_TLV_LAN_NEIGHBORS] = &tlv_lan_neighbor_ops,
2558 [ISIS_TLV_LSP_ENTRY] = &tlv_lsp_entry_ops,
2559 [ISIS_TLV_AUTH] = &tlv_auth_ops,
2560 [ISIS_TLV_EXTENDED_REACH] = &tlv_extended_reach_ops,
2561 [ISIS_TLV_MT_REACH] = &tlv_extended_reach_ops,
2562 [ISIS_TLV_OLDSTYLE_IP_REACH] = &tlv_oldstyle_ip_reach_ops,
2563 [ISIS_TLV_PROTOCOLS_SUPPORTED] = &tlv_protocols_supported_ops,
2564 [ISIS_TLV_OLDSTYLE_IP_REACH_EXT] = &tlv_oldstyle_ip_reach_ops,
2565 [ISIS_TLV_IPV4_ADDRESS] = &tlv_ipv4_address_ops,
2566 [ISIS_TLV_TE_ROUTER_ID] = &tlv_te_router_id_ops,
2567 [ISIS_TLV_EXTENDED_IP_REACH] = &tlv_extended_ip_reach_ops,
2568 [ISIS_TLV_MT_IP_REACH] = &tlv_extended_ip_reach_ops,
2569 [ISIS_TLV_DYNAMIC_HOSTNAME] = &tlv_dynamic_hostname_ops,
2570 [ISIS_TLV_MT_ROUTER_INFO] = &tlv_mt_router_info_ops,
2571 [ISIS_TLV_IPV6_ADDRESS] = &tlv_ipv6_address_ops,
2572 [ISIS_TLV_IPV6_REACH] = &tlv_ipv6_reach_ops,
2573 [ISIS_TLV_MT_IPV6_REACH] = &tlv_ipv6_reach_ops,
2574 },
2575 [ISIS_CONTEXT_SUBTLV_NE_REACH] = {},
2576 [ISIS_CONTEXT_SUBTLV_IP_REACH] = {},
2577 [ISIS_CONTEXT_SUBTLV_IPV6_REACH] = {
2578 [ISIS_SUBTLV_IPV6_SOURCE_PREFIX] = &subtlv_ipv6_source_prefix_ops,
2579 }
2580};
2581
2582/* Accessor functions */
2583
2584void isis_tlvs_add_auth(struct isis_tlvs *tlvs, struct isis_passwd *passwd)
2585{
2586 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth);
2587 init_item_list(&tlvs->isis_auth);
2588
2589 if (passwd->type == ISIS_PASSWD_TYPE_UNUSED)
2590 return;
2591
841791b6 2592 struct isis_auth *auth = XCALLOC(MTYPE_ISIS_TLV, sizeof(*auth));
7ef5fefc
CF
2593
2594 auth->type = passwd->type;
2595
2596 auth->plength = passwd->len;
2597 memcpy(auth->passwd, passwd->passwd,
2598 MIN(sizeof(auth->passwd), sizeof(passwd->passwd)));
2599
2600 if (auth->type == ISIS_PASSWD_TYPE_CLEARTXT) {
2601 auth->length = passwd->len;
2602 memcpy(auth->value, passwd->passwd,
2603 MIN(sizeof(auth->value), sizeof(passwd->passwd)));
2604 }
2605
2606 append_item(&tlvs->isis_auth, (struct isis_item *)auth);
2607}
2608
2609void isis_tlvs_add_area_addresses(struct isis_tlvs *tlvs,
2610 struct list *addresses)
2611{
2612 struct listnode *node;
2613 struct area_addr *area_addr;
2614
2615 for (ALL_LIST_ELEMENTS_RO(addresses, node, area_addr)) {
2616 struct isis_area_address *a =
841791b6 2617 XCALLOC(MTYPE_ISIS_TLV, sizeof(*a));
7ef5fefc
CF
2618
2619 a->len = area_addr->addr_len;
2620 memcpy(a->addr, area_addr->area_addr, 20);
2621 append_item(&tlvs->area_addresses, (struct isis_item *)a);
2622 }
2623}
2624
2625void isis_tlvs_add_lan_neighbors(struct isis_tlvs *tlvs, struct list *neighbors)
2626{
2627 struct listnode *node;
2628 u_char *snpa;
2629
2630 for (ALL_LIST_ELEMENTS_RO(neighbors, node, snpa)) {
2631 struct isis_lan_neighbor *n =
841791b6 2632 XCALLOC(MTYPE_ISIS_TLV, sizeof(*n));
7ef5fefc
CF
2633
2634 memcpy(n->mac, snpa, 6);
2635 append_item(&tlvs->lan_neighbor, (struct isis_item *)n);
2636 }
2637}
2638
2639void isis_tlvs_set_protocols_supported(struct isis_tlvs *tlvs,
2640 struct nlpids *nlpids)
2641{
2642 tlvs->protocols_supported.count = nlpids->count;
2643 if (tlvs->protocols_supported.protocols)
841791b6 2644 XFREE(MTYPE_ISIS_TLV, tlvs->protocols_supported.protocols);
7ef5fefc
CF
2645 if (nlpids->count) {
2646 tlvs->protocols_supported.protocols =
841791b6 2647 XCALLOC(MTYPE_ISIS_TLV, nlpids->count);
7ef5fefc
CF
2648 memcpy(tlvs->protocols_supported.protocols, nlpids->nlpids,
2649 nlpids->count);
2650 } else {
2651 tlvs->protocols_supported.protocols = NULL;
2652 }
2653}
2654
2655void isis_tlvs_add_mt_router_info(struct isis_tlvs *tlvs, uint16_t mtid,
2656 bool overload, bool attached)
2657{
841791b6 2658 struct isis_mt_router_info *i = XCALLOC(MTYPE_ISIS_TLV, sizeof(*i));
7ef5fefc
CF
2659
2660 i->overload = overload;
2661 i->attached = attached;
2662 i->mtid = mtid;
2663 append_item(&tlvs->mt_router_info, (struct isis_item *)i);
2664}
2665
2666void isis_tlvs_add_ipv4_address(struct isis_tlvs *tlvs, struct in_addr *addr)
2667{
841791b6 2668 struct isis_ipv4_address *a = XCALLOC(MTYPE_ISIS_TLV, sizeof(*a));
7ef5fefc
CF
2669 a->addr = *addr;
2670 append_item(&tlvs->ipv4_address, (struct isis_item *)a);
2671}
2672
2673
2674void isis_tlvs_add_ipv4_addresses(struct isis_tlvs *tlvs,
2675 struct list *addresses)
2676{
2677 struct listnode *node;
2678 struct prefix_ipv4 *ip_addr;
bb5c77d7 2679 unsigned int addr_count = 0;
7ef5fefc 2680
bb5c77d7 2681 for (ALL_LIST_ELEMENTS_RO(addresses, node, ip_addr)) {
7ef5fefc 2682 isis_tlvs_add_ipv4_address(tlvs, &ip_addr->prefix);
bb5c77d7
CF
2683 addr_count++;
2684 if (addr_count >= 63)
2685 break;
2686 }
7ef5fefc
CF
2687}
2688
2689void isis_tlvs_add_ipv6_addresses(struct isis_tlvs *tlvs,
2690 struct list *addresses)
2691{
2692 struct listnode *node;
2693 struct prefix_ipv6 *ip_addr;
2694
2695 for (ALL_LIST_ELEMENTS_RO(addresses, node, ip_addr)) {
2696 struct isis_ipv6_address *a =
841791b6 2697 XCALLOC(MTYPE_ISIS_TLV, sizeof(*a));
7ef5fefc
CF
2698
2699 a->addr = ip_addr->prefix;
2700 append_item(&tlvs->ipv6_address, (struct isis_item *)a);
2701 }
2702}
2703
2704typedef bool (*auth_validator_func)(struct isis_passwd *passwd,
2705 struct stream *stream,
2706 struct isis_auth *auth, bool is_lsp);
2707
2708static bool auth_validator_cleartxt(struct isis_passwd *passwd,
2709 struct stream *stream,
2710 struct isis_auth *auth, bool is_lsp)
2711{
2712 return (auth->length == passwd->len
2713 && !memcmp(auth->value, passwd->passwd, passwd->len));
2714}
2715
2716static bool auth_validator_hmac_md5(struct isis_passwd *passwd,
2717 struct stream *stream,
2718 struct isis_auth *auth, bool is_lsp)
2719{
2720 uint8_t digest[16];
2721 uint16_t checksum;
2722 uint16_t rem_lifetime;
2723
2724 if (is_lsp)
2725 safe_auth_md5(stream, &checksum, &rem_lifetime);
2726
2727 memset(STREAM_DATA(stream) + auth->offset, 0, 16);
2728 hmac_md5(STREAM_DATA(stream), stream_get_endp(stream), passwd->passwd,
2729 passwd->len, digest);
2730 memcpy(STREAM_DATA(stream) + auth->offset, auth->value, 16);
2731
2732 bool rv = !memcmp(digest, auth->value, 16);
2733
2734 if (is_lsp)
2735 restore_auth_md5(stream, checksum, rem_lifetime);
2736
2737 return rv;
2738}
2739
2740static const auth_validator_func auth_validators[] = {
2741 [ISIS_PASSWD_TYPE_CLEARTXT] = auth_validator_cleartxt,
2742 [ISIS_PASSWD_TYPE_HMAC_MD5] = auth_validator_hmac_md5,
2743};
2744
2745bool isis_tlvs_auth_is_valid(struct isis_tlvs *tlvs, struct isis_passwd *passwd,
2746 struct stream *stream, bool is_lsp)
2747{
2748 /* If no auth is set, always pass authentication */
2749 if (!passwd->type)
2750 return true;
2751
2752 /* If we don't known how to validate the auth, return invalid */
2753 if (passwd->type >= array_size(auth_validators)
2754 || !auth_validators[passwd->type])
2755 return false;
2756
2757 struct isis_auth *auth_head = (struct isis_auth *)tlvs->isis_auth.head;
2758 struct isis_auth *auth;
2759 for (auth = auth_head; auth; auth = auth->next) {
2760 if (auth->type == passwd->type)
2761 break;
2762 }
2763
2764 /* If matching auth TLV could not be found, return invalid */
2765 if (!auth)
2766 return false;
2767
2768 /* Perform validation and return result */
2769 return auth_validators[passwd->type](passwd, stream, auth, is_lsp);
2770}
2771
2772bool isis_tlvs_area_addresses_match(struct isis_tlvs *tlvs,
2773 struct list *addresses)
2774{
2775 struct isis_area_address *addr_head;
2776
2777 addr_head = (struct isis_area_address *)tlvs->area_addresses.head;
2778 for (struct isis_area_address *addr = addr_head; addr;
2779 addr = addr->next) {
2780 struct listnode *node;
2781 struct area_addr *a;
2782
2783 for (ALL_LIST_ELEMENTS_RO(addresses, node, a)) {
2784 if (a->addr_len == addr->len
2785 && !memcmp(a->area_addr, addr->addr, addr->len))
2786 return true;
2787 }
2788 }
2789
2790 return false;
2791}
2792
2793static void tlvs_area_addresses_to_adj(struct isis_tlvs *tlvs,
2794 struct isis_adjacency *adj,
2795 bool *changed)
2796{
2797 if (adj->area_address_count != tlvs->area_addresses.count) {
2798 *changed = true;
2799 adj->area_address_count = tlvs->area_addresses.count;
2800 adj->area_addresses = XREALLOC(
2801 MTYPE_ISIS_ADJACENCY_INFO, adj->area_addresses,
2802 adj->area_address_count * sizeof(*adj->area_addresses));
2803 }
2804
2805 struct isis_area_address *addr = NULL;
2806 for (unsigned int i = 0; i < tlvs->area_addresses.count; i++) {
2807 if (!addr)
2808 addr = (struct isis_area_address *)
2809 tlvs->area_addresses.head;
2810 else
2811 addr = addr->next;
2812
2813 if (adj->area_addresses[i].addr_len == addr->len
2814 && !memcmp(adj->area_addresses[i].area_addr, addr->addr,
2815 addr->len)) {
2816 continue;
2817 }
2818
2819 *changed = true;
2820 adj->area_addresses[i].addr_len = addr->len;
2821 memcpy(adj->area_addresses[i].area_addr, addr->addr, addr->len);
2822 }
2823}
2824
2825static void tlvs_protocols_supported_to_adj(struct isis_tlvs *tlvs,
2826 struct isis_adjacency *adj,
2827 bool *changed)
2828{
2829 bool ipv4_supported = false, ipv6_supported = false;
2830
2831 for (uint8_t i = 0; i < tlvs->protocols_supported.count; i++) {
2832 if (tlvs->protocols_supported.protocols[i] == NLPID_IP)
2833 ipv4_supported = true;
2834 if (tlvs->protocols_supported.protocols[i] == NLPID_IPV6)
2835 ipv6_supported = true;
2836 }
2837
2838 struct nlpids reduced = {};
2839
2840 if (ipv4_supported && ipv6_supported) {
2841 reduced.count = 2;
2842 reduced.nlpids[0] = NLPID_IP;
2843 reduced.nlpids[1] = NLPID_IPV6;
2844 } else if (ipv4_supported) {
2845 reduced.count = 1;
2846 reduced.nlpids[0] = NLPID_IP;
2847 } else if (ipv6_supported) {
2848 reduced.count = 1;
2849 reduced.nlpids[1] = NLPID_IPV6;
2850 } else {
2851 reduced.count = 0;
2852 }
2853
2854 if (adj->nlpids.count == reduced.count
2855 && !memcmp(adj->nlpids.nlpids, reduced.nlpids, reduced.count))
2856 return;
2857
2858 *changed = true;
2859 adj->nlpids.count = reduced.count;
2860 memcpy(adj->nlpids.nlpids, reduced.nlpids, reduced.count);
2861}
2862
2863static void tlvs_ipv4_addresses_to_adj(struct isis_tlvs *tlvs,
2864 struct isis_adjacency *adj,
2865 bool *changed)
2866{
2867 if (adj->ipv4_address_count != tlvs->ipv4_address.count) {
2868 *changed = true;
2869 adj->ipv4_address_count = tlvs->ipv4_address.count;
2870 adj->ipv4_addresses = XREALLOC(
2871 MTYPE_ISIS_ADJACENCY_INFO, adj->ipv4_addresses,
2872 adj->ipv4_address_count * sizeof(*adj->ipv4_addresses));
2873 }
2874
2875 struct isis_ipv4_address *addr = NULL;
2876 for (unsigned int i = 0; i < tlvs->ipv4_address.count; i++) {
2877 if (!addr)
2878 addr = (struct isis_ipv4_address *)
2879 tlvs->ipv4_address.head;
2880 else
2881 addr = addr->next;
2882
2883 if (!memcmp(&adj->ipv4_addresses[i], &addr->addr,
2884 sizeof(addr->addr)))
2885 continue;
2886
2887 *changed = true;
2888 adj->ipv4_addresses[i] = addr->addr;
2889 }
2890}
2891
2892static void tlvs_ipv6_addresses_to_adj(struct isis_tlvs *tlvs,
2893 struct isis_adjacency *adj,
2894 bool *changed)
2895{
2896 if (adj->ipv6_address_count != tlvs->ipv6_address.count) {
2897 *changed = true;
2898 adj->ipv6_address_count = tlvs->ipv6_address.count;
2899 adj->ipv6_addresses = XREALLOC(
2900 MTYPE_ISIS_ADJACENCY_INFO, adj->ipv6_addresses,
2901 adj->ipv6_address_count * sizeof(*adj->ipv6_addresses));
2902 }
2903
2904 struct isis_ipv6_address *addr = NULL;
2905 for (unsigned int i = 0; i < tlvs->ipv6_address.count; i++) {
2906 if (!addr)
2907 addr = (struct isis_ipv6_address *)
2908 tlvs->ipv6_address.head;
2909 else
2910 addr = addr->next;
2911
2912 if (!memcmp(&adj->ipv6_addresses[i], &addr->addr,
2913 sizeof(addr->addr)))
2914 continue;
2915
2916 *changed = true;
2917 adj->ipv6_addresses[i] = addr->addr;
2918 }
2919}
2920
2921void isis_tlvs_to_adj(struct isis_tlvs *tlvs, struct isis_adjacency *adj,
2922 bool *changed)
2923{
2924 *changed = false;
2925
2926 tlvs_area_addresses_to_adj(tlvs, adj, changed);
2927 tlvs_protocols_supported_to_adj(tlvs, adj, changed);
2928 tlvs_ipv4_addresses_to_adj(tlvs, adj, changed);
2929 tlvs_ipv6_addresses_to_adj(tlvs, adj, changed);
2930}
2931
2932bool isis_tlvs_own_snpa_found(struct isis_tlvs *tlvs, uint8_t *snpa)
2933{
2934 struct isis_lan_neighbor *ne_head;
2935
2936 ne_head = (struct isis_lan_neighbor *)tlvs->lan_neighbor.head;
2937 for (struct isis_lan_neighbor *ne = ne_head; ne; ne = ne->next) {
2938 if (!memcmp(ne->mac, snpa, ETH_ALEN))
2939 return true;
2940 }
2941
2942 return false;
2943}
2944
2945void isis_tlvs_add_lsp_entry(struct isis_tlvs *tlvs, struct isis_lsp *lsp)
2946{
841791b6 2947 struct isis_lsp_entry *entry = XCALLOC(MTYPE_ISIS_TLV, sizeof(*entry));
7ef5fefc
CF
2948
2949 entry->rem_lifetime = lsp->hdr.rem_lifetime;
2950 memcpy(entry->id, lsp->hdr.lsp_id, ISIS_SYS_ID_LEN + 2);
2951 entry->checksum = lsp->hdr.checksum;
2952 entry->seqno = lsp->hdr.seqno;
2953 entry->lsp = lsp;
2954
2955 append_item(&tlvs->lsp_entries, (struct isis_item *)entry);
2956}
2957
2958void isis_tlvs_add_csnp_entries(struct isis_tlvs *tlvs, uint8_t *start_id,
2959 uint8_t *stop_id, uint16_t num_lsps,
2960 dict_t *lspdb, struct isis_lsp **last_lsp)
2961{
2962 dnode_t *first = dict_lower_bound(lspdb, start_id);
2963 if (!first)
2964 return;
2965
2966 dnode_t *last = dict_upper_bound(lspdb, stop_id);
2967 dnode_t *curr = first;
2968
2969 isis_tlvs_add_lsp_entry(tlvs, first->dict_data);
2970 *last_lsp = first->dict_data;
2971
2972 while (curr) {
2973 curr = dict_next(lspdb, curr);
2974 if (curr) {
2975 isis_tlvs_add_lsp_entry(tlvs, curr->dict_data);
2976 *last_lsp = curr->dict_data;
2977 }
2978 if (curr == last || tlvs->lsp_entries.count == num_lsps)
2979 break;
2980 }
2981}
2982
2983void isis_tlvs_set_dynamic_hostname(struct isis_tlvs *tlvs,
2984 const char *hostname)
2985{
841791b6 2986 XFREE(MTYPE_ISIS_TLV, tlvs->hostname);
7ef5fefc 2987 if (hostname)
841791b6 2988 tlvs->hostname = XSTRDUP(MTYPE_ISIS_TLV, hostname);
7ef5fefc
CF
2989}
2990
2991void isis_tlvs_set_te_router_id(struct isis_tlvs *tlvs,
2992 const struct in_addr *id)
2993{
841791b6 2994 XFREE(MTYPE_ISIS_TLV, tlvs->te_router_id);
7ef5fefc
CF
2995 if (!id)
2996 return;
841791b6 2997 tlvs->te_router_id = XCALLOC(MTYPE_ISIS_TLV, sizeof(*id));
7ef5fefc
CF
2998 memcpy(tlvs->te_router_id, id, sizeof(*id));
2999}
3000
3001void isis_tlvs_add_oldstyle_ip_reach(struct isis_tlvs *tlvs,
3002 struct prefix_ipv4 *dest, uint8_t metric)
3003{
841791b6 3004 struct isis_oldstyle_ip_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
7ef5fefc
CF
3005
3006 r->metric = metric;
3007 memcpy(&r->prefix, dest, sizeof(*dest));
3008 apply_mask_ipv4(&r->prefix);
3009 append_item(&tlvs->oldstyle_ip_reach, (struct isis_item *)r);
3010}
3011
3012void isis_tlvs_add_extended_ip_reach(struct isis_tlvs *tlvs,
3013 struct prefix_ipv4 *dest, uint32_t metric)
3014{
841791b6 3015 struct isis_extended_ip_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
7ef5fefc
CF
3016
3017 r->metric = metric;
3018 memcpy(&r->prefix, dest, sizeof(*dest));
3019 apply_mask_ipv4(&r->prefix);
3020 append_item(&tlvs->extended_ip_reach, (struct isis_item *)r);
3021}
3022
3023void isis_tlvs_add_ipv6_reach(struct isis_tlvs *tlvs, uint16_t mtid,
3024 struct prefix_ipv6 *dest, uint32_t metric)
3025{
841791b6 3026 struct isis_ipv6_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
7ef5fefc
CF
3027
3028 r->metric = metric;
3029 memcpy(&r->prefix, dest, sizeof(*dest));
3030 apply_mask_ipv6(&r->prefix);
3031
3032 struct isis_item_list *l;
3033 l = (mtid == ISIS_MT_IPV4_UNICAST)
3034 ? &tlvs->ipv6_reach
3035 : isis_get_mt_items(&tlvs->mt_ipv6_reach, mtid);
3036 append_item(l, (struct isis_item *)r);
3037}
3038
3039void isis_tlvs_add_oldstyle_reach(struct isis_tlvs *tlvs, uint8_t *id,
3040 uint8_t metric)
3041{
841791b6 3042 struct isis_oldstyle_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
7ef5fefc
CF
3043
3044 r->metric = metric;
3045 memcpy(r->id, id, sizeof(r->id));
3046 append_item(&tlvs->oldstyle_reach, (struct isis_item *)r);
3047}
3048
3049void isis_tlvs_add_extended_reach(struct isis_tlvs *tlvs, uint16_t mtid,
3050 uint8_t *id, uint32_t metric,
3051 uint8_t *subtlvs, uint8_t subtlv_len)
3052{
841791b6 3053 struct isis_extended_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
7ef5fefc
CF
3054
3055 memcpy(r->id, id, sizeof(r->id));
3056 r->metric = metric;
3057 if (subtlvs && subtlv_len) {
841791b6 3058 r->subtlvs = XCALLOC(MTYPE_ISIS_TLV, subtlv_len);
7ef5fefc
CF
3059 memcpy(r->subtlvs, subtlvs, subtlv_len);
3060 r->subtlv_len = subtlv_len;
3061 }
3062
3063 struct isis_item_list *l;
3064 if (mtid == ISIS_MT_IPV4_UNICAST)
3065 l = &tlvs->extended_reach;
3066 else
3067 l = isis_get_mt_items(&tlvs->mt_reach, mtid);
3068 append_item(l, (struct isis_item *)r);
3069}
3070
3071struct isis_mt_router_info *
3072isis_tlvs_lookup_mt_router_info(struct isis_tlvs *tlvs, uint16_t mtid)
3073{
3074 if (tlvs->mt_router_info_empty)
3075 return NULL;
3076
3077 struct isis_mt_router_info *rv;
3078 for (rv = (struct isis_mt_router_info *)tlvs->mt_router_info.head; rv;
3079 rv = rv->next) {
3080 if (rv->mtid == mtid)
3081 return rv;
3082 }
3083
3084 return NULL;
3085}