]> git.proxmox.com Git - mirror_frr.git/blob - isisd/isis_tlvs.c
Merge pull request #892 from opensourcerouting/watchfrr-simplify
[mirror_frr.git] / isisd / isis_tlvs.c
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"
32 #include "isisd/isis_tlvs.h"
33 #include "isisd/isis_common.h"
34 #include "isisd/isis_mt.h"
35 #include "isisd/isis_misc.h"
36 #include "isisd/isis_adjacency.h"
37 #include "isisd/isis_circuit.h"
38 #include "isisd/isis_pdu.h"
39 #include "isisd/isis_lsp.h"
40 #include "isisd/isis_te.h"
41
42 DEFINE_MTYPE_STATIC(ISISD, ISIS_TLV, "ISIS TLVs")
43 DEFINE_MTYPE_STATIC(ISISD, ISIS_SUBTLV, "ISIS Sub-TLVs")
44 DEFINE_MTYPE_STATIC(ISISD, ISIS_MT_ITEM_LIST, "ISIS MT Item Lists")
45
46 typedef int (*unpack_tlv_func)(enum isis_tlv_context context, uint8_t tlv_type,
47 uint8_t tlv_len, struct stream *s,
48 struct sbuf *log, void *dest, int indent);
49 typedef int (*pack_item_func)(struct isis_item *item, struct stream *s);
50 typedef void (*free_item_func)(struct isis_item *i);
51 typedef int (*unpack_item_func)(uint16_t mtid, uint8_t len, struct stream *s,
52 struct sbuf *log, void *dest, int indent);
53 typedef void (*format_item_func)(uint16_t mtid, struct isis_item *i,
54 struct sbuf *buf, int indent);
55 typedef struct isis_item *(*copy_item_func)(struct isis_item *i);
56
57 struct 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
68 enum how_to_pack {
69 ISIS_ITEMS,
70 ISIS_MT_ITEMS,
71 };
72
73 struct pack_order_entry {
74 enum isis_tlv_context context;
75 enum isis_tlv_type type;
76 enum how_to_pack how_to_pack;
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
86 static struct pack_order_entry pack_order[] = {
87 PACK_ENTRY(OLDSTYLE_REACH, ISIS_ITEMS, oldstyle_reach),
88 PACK_ENTRY(LAN_NEIGHBORS, ISIS_ITEMS, lan_neighbor),
89 PACK_ENTRY(LSP_ENTRY, ISIS_ITEMS, lsp_entries),
90 PACK_ENTRY(EXTENDED_REACH, ISIS_ITEMS, extended_reach),
91 PACK_ENTRY(MT_REACH, ISIS_MT_ITEMS, mt_reach),
92 PACK_ENTRY(OLDSTYLE_IP_REACH, ISIS_ITEMS, oldstyle_ip_reach),
93 PACK_ENTRY(OLDSTYLE_IP_REACH_EXT, ISIS_ITEMS, oldstyle_ip_reach_ext),
94 PACK_ENTRY(IPV4_ADDRESS, ISIS_ITEMS, ipv4_address),
95 PACK_ENTRY(IPV6_ADDRESS, ISIS_ITEMS, ipv6_address),
96 PACK_ENTRY(EXTENDED_IP_REACH, ISIS_ITEMS, extended_ip_reach),
97 PACK_ENTRY(MT_IP_REACH, ISIS_MT_ITEMS, mt_ip_reach),
98 PACK_ENTRY(IPV6_REACH, ISIS_ITEMS, ipv6_reach),
99 PACK_ENTRY(MT_IPV6_REACH, ISIS_MT_ITEMS, mt_ipv6_reach)};
100
101 /* This is a forward definition. The table is actually initialized
102 * in at the bottom. */
103 static const struct tlv_ops *tlv_table[ISIS_CONTEXT_MAX][ISIS_TLV_MAX];
104
105 /* End of _ops forward definition. */
106
107 /* Prototypes */
108 static void append_item(struct isis_item_list *dest, struct isis_item *item);
109
110 /* Functions for Sub-TVL ??? IPv6 Source Prefix */
111
112 static 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
124 static 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
135 static 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
151 static int unpack_subtlv_ipv6_source_prefix(enum isis_tlv_context context,
152 uint8_t tlv_type, uint8_t tlv_len,
153 struct stream *s, struct sbuf *log,
154 void *dest, int indent)
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
204 static 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
213 static 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
225 static 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
231 static 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
241 static 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
263 static 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
269 static struct isis_item *copy_item_area_address(struct isis_item *i)
270 {
271 struct isis_area_address *addr = (struct isis_area_address *)i;
272 struct isis_area_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
273
274 rv->len = addr->len;
275 memcpy(rv->addr, addr->addr, addr->len);
276 return (struct isis_item *)rv;
277 }
278
279 static 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
288 static void free_item_area_address(struct isis_item *i)
289 {
290 XFREE(MTYPE_ISIS_TLV, i);
291 }
292
293 static 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
304 static int unpack_item_area_address(uint16_t mtid, uint8_t len,
305 struct stream *s, struct sbuf *log,
306 void *dest, int indent)
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
321 rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
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;
344 out:
345 XFREE(MTYPE_ISIS_TLV, rv);
346 return 1;
347 }
348
349 /* Functions related to TLV 2 (Old-Style) IS Reach */
350 static struct isis_item *copy_item_oldstyle_reach(struct isis_item *i)
351 {
352 struct isis_oldstyle_reach *r = (struct isis_oldstyle_reach *)i;
353 struct isis_oldstyle_reach *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
354
355 memcpy(rv->id, r->id, 7);
356 rv->metric = r->metric;
357 return (struct isis_item *)rv;
358 }
359
360 static 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
369 static void free_item_oldstyle_reach(struct isis_item *i)
370 {
371 XFREE(MTYPE_ISIS_TLV, i);
372 }
373
374 static 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
390 static int unpack_item_oldstyle_reach(uint16_t mtid, uint8_t len,
391 struct stream *s, struct sbuf *log,
392 void *dest, int indent)
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
406 struct isis_oldstyle_reach *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
407 rv->metric = stream_getc(s);
408 if ((rv->metric & 0x3f) != rv->metric) {
409 sbuf_push(log, indent, "Metric has unplausible format\n");
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 */
422 static struct isis_item *copy_item_lan_neighbor(struct isis_item *i)
423 {
424 struct isis_lan_neighbor *n = (struct isis_lan_neighbor *)i;
425 struct isis_lan_neighbor *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
426
427 memcpy(rv->mac, n->mac, 6);
428 return (struct isis_item *)rv;
429 }
430
431 static 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
439 static void free_item_lan_neighbor(struct isis_item *i)
440 {
441 XFREE(MTYPE_ISIS_TLV, i);
442 }
443
444 static 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
456 static int unpack_item_lan_neighbor(uint16_t mtid, uint8_t len,
457 struct stream *s, struct sbuf *log,
458 void *dest, int indent)
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
472 struct isis_lan_neighbor *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
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 */
481 static struct isis_item *copy_item_lsp_entry(struct isis_item *i)
482 {
483 struct isis_lsp_entry *e = (struct isis_lsp_entry *)i;
484 struct isis_lsp_entry *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
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
494 static 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
505 static void free_item_lsp_entry(struct isis_item *i)
506 {
507 XFREE(MTYPE_ISIS_TLV, i);
508 }
509
510 static 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
525 static 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
539 struct isis_lsp_entry *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
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
552 static struct isis_item *copy_item_extended_reach(struct isis_item *i)
553 {
554 struct isis_extended_reach *r = (struct isis_extended_reach *)i;
555 struct isis_extended_reach *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
556
557 memcpy(rv->id, r->id, 7);
558 rv->metric = r->metric;
559
560 if (r->subtlvs && r->subtlv_len) {
561 rv->subtlvs = XCALLOC(MTYPE_ISIS_TLV, r->subtlv_len);
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
569 static 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
585 static void free_item_extended_reach(struct isis_item *i)
586 {
587 struct isis_extended_reach *item = (struct isis_extended_reach *)i;
588 XFREE(MTYPE_ISIS_TLV, item->subtlvs);
589 XFREE(MTYPE_ISIS_TLV, item);
590 }
591
592 static 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
605 static 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
631 rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
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
660 rv->subtlvs = XCALLOC(MTYPE_ISIS_TLV, subtlv_len);
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;
667 out:
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 */
675 static 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 =
679 XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
680
681 rv->metric = r->metric;
682 rv->prefix = r->prefix;
683 return (struct isis_item *)rv;
684 }
685
686 static 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
696 static void free_item_oldstyle_ip_reach(struct isis_item *i)
697 {
698 XFREE(MTYPE_ISIS_TLV, i);
699 }
700
701 static 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
721 static 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 =
736 XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
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
759 static 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;
765 dest->protocols = XCALLOC(MTYPE_ISIS_TLV, src->count);
766 memcpy(dest->protocols, src->protocols, src->count);
767 }
768
769 static 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
783 static void free_tlv_protocols_supported(struct isis_protocols_supported *p)
784 {
785 XFREE(MTYPE_ISIS_TLV, p->protocols);
786 }
787
788 static 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
803 static 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;
824 tlvs->protocols_supported.protocols = XCALLOC(MTYPE_ISIS_TLV, tlv_len);
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 */
833 static struct isis_item *copy_item_ipv4_address(struct isis_item *i)
834 {
835 struct isis_ipv4_address *a = (struct isis_ipv4_address *)i;
836 struct isis_ipv4_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
837
838 rv->addr = a->addr;
839 return (struct isis_item *)rv;
840 }
841
842 static 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
852 static void free_item_ipv4_address(struct isis_item *i)
853 {
854 XFREE(MTYPE_ISIS_TLV, i);
855 }
856
857 static 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
869 static 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
885 struct isis_ipv4_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
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 */
895 static struct isis_item *copy_item_ipv6_address(struct isis_item *i)
896 {
897 struct isis_ipv6_address *a = (struct isis_ipv6_address *)i;
898 struct isis_ipv6_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
899
900 rv->addr = a->addr;
901 return (struct isis_item *)rv;
902 }
903
904 static 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
914 static void free_item_ipv6_address(struct isis_item *i)
915 {
916 XFREE(MTYPE_ISIS_TLV, i);
917 }
918
919 static 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
931 static 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
947 struct isis_ipv6_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
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 */
957 static 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;
960 struct isis_mt_router_info *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
961
962 rv->overload = info->overload;
963 rv->attached = info->attached;
964 rv->mtid = info->mtid;
965 return (struct isis_item *)rv;
966 }
967
968 static 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
979 static void free_item_mt_router_info(struct isis_item *i)
980 {
981 XFREE(MTYPE_ISIS_TLV, i);
982 }
983
984 static 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
1003 static 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
1019 struct isis_mt_router_info *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
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
1034 static struct in_addr *copy_tlv_te_router_id(const struct in_addr *id)
1035 {
1036 if (!id)
1037 return NULL;
1038
1039 struct in_addr *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
1040 memcpy(rv, id, sizeof(*rv));
1041 return rv;
1042 }
1043
1044 static 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
1055 static void free_tlv_te_router_id(struct in_addr *id)
1056 {
1057 XFREE(MTYPE_ISIS_TLV, id);
1058 }
1059
1060 static 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
1074 static 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
1094 tlvs->te_router_id = XCALLOC(MTYPE_ISIS_TLV, 4);
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
1103 static 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 =
1107 XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
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
1116 static 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
1131 static 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;
1135 XFREE(MTYPE_ISIS_TLV, item);
1136 }
1137
1138 static 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
1157 static 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
1184 rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
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;
1242 out:
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
1250 static char *copy_tlv_dynamic_hostname(const char *hostname)
1251 {
1252 if (!hostname)
1253 return NULL;
1254
1255 return XSTRDUP(MTYPE_ISIS_TLV, hostname);
1256 }
1257
1258 static 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
1267 static void free_tlv_dynamic_hostname(char *hostname)
1268 {
1269 XFREE(MTYPE_ISIS_TLV, hostname);
1270 }
1271
1272 static 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
1288 static 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
1308 tlvs->hostname = XCALLOC(MTYPE_ISIS_TLV, tlv_len + 1);
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
1331 static struct isis_item *copy_item_ipv6_reach(struct isis_item *i)
1332 {
1333 struct isis_ipv6_reach *r = (struct isis_ipv6_reach *)i;
1334 struct isis_ipv6_reach *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
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
1344 static 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
1366 static 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);
1371 XFREE(MTYPE_ISIS_TLV, item);
1372 }
1373
1374 static 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
1400 static 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
1426 rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
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;
1488 out:
1489 if (rv)
1490 free_item_ipv6_reach((struct isis_item *)rv);
1491 return 1;
1492 }
1493
1494 /* Functions related to TLV 10 Authentication */
1495 static struct isis_item *copy_item_auth(struct isis_item *i)
1496 {
1497 struct isis_auth *auth = (struct isis_auth *)i;
1498 struct isis_auth *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
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
1506 static 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
1531 static void free_item_auth(struct isis_item *i)
1532 {
1533 XFREE(MTYPE_ISIS_TLV, i);
1534 }
1535
1536 static 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
1563 static 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
1578 struct isis_auth *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
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);
1589 XFREE(MTYPE_ISIS_TLV, rv);
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
1602 static 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
1609 static 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
1622 static 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
1634 static 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
1648 static 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
1659 static 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
1672 static 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
1683 static 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
1698 static 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
1714 static 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
1728 top:
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;
1783 too_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
1792 static 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
1799 static 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
1813 static 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
1882 static 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
1892 RB_PROTOTYPE(isis_mt_item_list, isis_item_list, mt_tree, isis_mt_item_list_cmp);
1893 RB_GENERATE(isis_mt_item_list, isis_item_list, mt_tree, isis_mt_item_list_cmp);
1894
1895 struct 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
1911 struct 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
1919 static 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
1924 RB_FOREACH_SAFE(n, isis_mt_item_list, m, nnext)
1925 {
1926 free_items(context, type, n);
1927 RB_REMOVE(isis_mt_item_list, m, n);
1928 XFREE(MTYPE_ISIS_MT_ITEM_LIST, n);
1929 }
1930 }
1931
1932 static void format_mt_items(enum isis_tlv_context context,
1933 enum isis_tlv_type type,
1934 struct isis_mt_item_list *m, struct sbuf *buf,
1935 int indent)
1936 {
1937 struct isis_item_list *n;
1938
1939 RB_FOREACH(n, isis_mt_item_list, m)
1940 {
1941 format_items_(n->mtid, context, type, n, buf, indent);
1942 }
1943 }
1944
1945 static int pack_mt_items(enum isis_tlv_context context, enum isis_tlv_type type,
1946 struct isis_mt_item_list *m, struct stream *s,
1947 struct isis_tlvs **fragment_tlvs,
1948 struct pack_order_entry *pe,
1949 struct isis_tlvs *(*new_fragment)(struct list *l),
1950 struct list *new_fragment_arg)
1951 {
1952 struct isis_item_list *n;
1953
1954 RB_FOREACH(n, isis_mt_item_list, m)
1955 {
1956 int rv;
1957
1958 rv = pack_items_(n->mtid, context, type, n, s, fragment_tlvs,
1959 pe, new_fragment, new_fragment_arg);
1960 if (rv)
1961 return rv;
1962 }
1963
1964 return 0;
1965 }
1966
1967 static void copy_mt_items(enum isis_tlv_context context,
1968 enum isis_tlv_type type,
1969 struct isis_mt_item_list *src,
1970 struct isis_mt_item_list *dest)
1971 {
1972 struct isis_item_list *n;
1973
1974 RB_INIT(isis_mt_item_list, dest);
1975
1976 RB_FOREACH(n, isis_mt_item_list, src)
1977 {
1978 copy_items(context, type, n, isis_get_mt_items(dest, n->mtid));
1979 }
1980 }
1981
1982 /* Functions related to tlvs in general */
1983
1984 struct isis_tlvs *isis_alloc_tlvs(void)
1985 {
1986 struct isis_tlvs *result;
1987
1988 result = XCALLOC(MTYPE_ISIS_TLV, sizeof(*result));
1989
1990 init_item_list(&result->isis_auth);
1991 init_item_list(&result->area_addresses);
1992 init_item_list(&result->mt_router_info);
1993 init_item_list(&result->oldstyle_reach);
1994 init_item_list(&result->lan_neighbor);
1995 init_item_list(&result->lsp_entries);
1996 init_item_list(&result->extended_reach);
1997 RB_INIT(isis_mt_item_list, &result->mt_reach);
1998 init_item_list(&result->oldstyle_ip_reach);
1999 init_item_list(&result->oldstyle_ip_reach_ext);
2000 init_item_list(&result->ipv4_address);
2001 init_item_list(&result->ipv6_address);
2002 init_item_list(&result->extended_ip_reach);
2003 RB_INIT(isis_mt_item_list, &result->mt_ip_reach);
2004 init_item_list(&result->ipv6_reach);
2005 RB_INIT(isis_mt_item_list, &result->mt_ipv6_reach);
2006
2007 return result;
2008 }
2009
2010 struct isis_tlvs *isis_copy_tlvs(struct isis_tlvs *tlvs)
2011 {
2012 struct isis_tlvs *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
2013
2014 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth,
2015 &rv->isis_auth);
2016
2017 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES,
2018 &tlvs->area_addresses, &rv->area_addresses);
2019
2020 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO,
2021 &tlvs->mt_router_info, &rv->mt_router_info);
2022
2023 tlvs->mt_router_info_empty = rv->mt_router_info_empty;
2024
2025 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_REACH,
2026 &tlvs->oldstyle_reach, &rv->oldstyle_reach);
2027
2028 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_LAN_NEIGHBORS,
2029 &tlvs->lan_neighbor, &rv->lan_neighbor);
2030
2031 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_LSP_ENTRY, &tlvs->lsp_entries,
2032 &rv->lsp_entries);
2033
2034 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_REACH,
2035 &tlvs->extended_reach, &rv->extended_reach);
2036
2037 copy_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_REACH, &tlvs->mt_reach,
2038 &rv->mt_reach);
2039
2040 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH,
2041 &tlvs->oldstyle_ip_reach, &rv->oldstyle_ip_reach);
2042
2043 copy_tlv_protocols_supported(&tlvs->protocols_supported,
2044 &rv->protocols_supported);
2045
2046 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH_EXT,
2047 &tlvs->oldstyle_ip_reach_ext, &rv->oldstyle_ip_reach_ext);
2048
2049 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV4_ADDRESS, &tlvs->ipv4_address,
2050 &rv->ipv4_address);
2051
2052 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_ADDRESS, &tlvs->ipv6_address,
2053 &rv->ipv6_address);
2054
2055 rv->te_router_id = copy_tlv_te_router_id(tlvs->te_router_id);
2056
2057 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_IP_REACH,
2058 &tlvs->extended_ip_reach, &rv->extended_ip_reach);
2059
2060 copy_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IP_REACH,
2061 &tlvs->mt_ip_reach, &rv->mt_ip_reach);
2062
2063 rv->hostname = copy_tlv_dynamic_hostname(tlvs->hostname);
2064
2065 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_REACH, &tlvs->ipv6_reach,
2066 &rv->ipv6_reach);
2067
2068 copy_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IPV6_REACH,
2069 &tlvs->mt_ipv6_reach, &rv->mt_ipv6_reach);
2070
2071 return rv;
2072 }
2073
2074 static void format_tlvs(struct isis_tlvs *tlvs, struct sbuf *buf, int indent)
2075 {
2076 format_tlv_protocols_supported(&tlvs->protocols_supported, buf, indent);
2077
2078 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth, buf,
2079 indent);
2080
2081 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES,
2082 &tlvs->area_addresses, buf, indent);
2083
2084 if (tlvs->mt_router_info_empty) {
2085 sbuf_push(buf, indent, "MT Router Info: None\n");
2086 } else {
2087 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO,
2088 &tlvs->mt_router_info, buf, indent);
2089 }
2090
2091 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_REACH,
2092 &tlvs->oldstyle_reach, buf, indent);
2093
2094 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_LAN_NEIGHBORS,
2095 &tlvs->lan_neighbor, buf, indent);
2096
2097 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_LSP_ENTRY, &tlvs->lsp_entries,
2098 buf, indent);
2099
2100 format_tlv_dynamic_hostname(tlvs->hostname, buf, indent);
2101 format_tlv_te_router_id(tlvs->te_router_id, buf, indent);
2102
2103 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_REACH,
2104 &tlvs->extended_reach, buf, indent);
2105
2106 format_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_REACH, &tlvs->mt_reach,
2107 buf, indent);
2108
2109 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH,
2110 &tlvs->oldstyle_ip_reach, buf, indent);
2111
2112 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH_EXT,
2113 &tlvs->oldstyle_ip_reach_ext, buf, indent);
2114
2115 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV4_ADDRESS,
2116 &tlvs->ipv4_address, buf, indent);
2117
2118 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_ADDRESS,
2119 &tlvs->ipv6_address, buf, indent);
2120
2121 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_IP_REACH,
2122 &tlvs->extended_ip_reach, buf, indent);
2123
2124 format_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IP_REACH,
2125 &tlvs->mt_ip_reach, buf, indent);
2126
2127 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_REACH, &tlvs->ipv6_reach,
2128 buf, indent);
2129
2130 format_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IPV6_REACH,
2131 &tlvs->mt_ipv6_reach, buf, indent);
2132 }
2133
2134 const char *isis_format_tlvs(struct isis_tlvs *tlvs)
2135 {
2136 static struct sbuf buf;
2137
2138 if (!sbuf_buf(&buf))
2139 sbuf_init(&buf, NULL, 0);
2140
2141 sbuf_reset(&buf);
2142 format_tlvs(tlvs, &buf, 0);
2143 return sbuf_buf(&buf);
2144 }
2145
2146 void isis_free_tlvs(struct isis_tlvs *tlvs)
2147 {
2148 if (!tlvs)
2149 return;
2150
2151 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth);
2152 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES,
2153 &tlvs->area_addresses);
2154 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO,
2155 &tlvs->mt_router_info);
2156 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_REACH,
2157 &tlvs->oldstyle_reach);
2158 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_LAN_NEIGHBORS,
2159 &tlvs->lan_neighbor);
2160 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_LSP_ENTRY, &tlvs->lsp_entries);
2161 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_REACH,
2162 &tlvs->extended_reach);
2163 free_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_REACH, &tlvs->mt_reach);
2164 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH,
2165 &tlvs->oldstyle_ip_reach);
2166 free_tlv_protocols_supported(&tlvs->protocols_supported);
2167 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH_EXT,
2168 &tlvs->oldstyle_ip_reach_ext);
2169 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV4_ADDRESS,
2170 &tlvs->ipv4_address);
2171 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_ADDRESS,
2172 &tlvs->ipv6_address);
2173 free_tlv_te_router_id(tlvs->te_router_id);
2174 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_IP_REACH,
2175 &tlvs->extended_ip_reach);
2176 free_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IP_REACH,
2177 &tlvs->mt_ip_reach);
2178 free_tlv_dynamic_hostname(tlvs->hostname);
2179 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_REACH, &tlvs->ipv6_reach);
2180 free_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IPV6_REACH,
2181 &tlvs->mt_ipv6_reach);
2182
2183 XFREE(MTYPE_ISIS_TLV, tlvs);
2184 }
2185
2186 static void add_padding(struct stream *s)
2187 {
2188 while (STREAM_WRITEABLE(s)) {
2189 if (STREAM_WRITEABLE(s) == 1)
2190 break;
2191 uint32_t padding_len = STREAM_WRITEABLE(s) - 2;
2192
2193 if (padding_len > 255) {
2194 if (padding_len == 256)
2195 padding_len = 254;
2196 else
2197 padding_len = 255;
2198 }
2199
2200 stream_putc(s, ISIS_TLV_PADDING);
2201 stream_putc(s, padding_len);
2202 stream_put(s, NULL, padding_len);
2203 }
2204 }
2205
2206 #define LSP_REM_LIFETIME_OFF 10
2207 #define LSP_CHECKSUM_OFF 24
2208 static void safe_auth_md5(struct stream *s, uint16_t *checksum,
2209 uint16_t *rem_lifetime)
2210 {
2211 memcpy(rem_lifetime, STREAM_DATA(s) + LSP_REM_LIFETIME_OFF,
2212 sizeof(*rem_lifetime));
2213 memset(STREAM_DATA(s) + LSP_REM_LIFETIME_OFF, 0, sizeof(*rem_lifetime));
2214 memcpy(checksum, STREAM_DATA(s) + LSP_CHECKSUM_OFF, sizeof(*checksum));
2215 memset(STREAM_DATA(s) + LSP_CHECKSUM_OFF, 0, sizeof(*checksum));
2216 }
2217
2218 static void restore_auth_md5(struct stream *s, uint16_t checksum,
2219 uint16_t rem_lifetime)
2220 {
2221 memcpy(STREAM_DATA(s) + LSP_REM_LIFETIME_OFF, &rem_lifetime,
2222 sizeof(rem_lifetime));
2223 memcpy(STREAM_DATA(s) + LSP_CHECKSUM_OFF, &checksum, sizeof(checksum));
2224 }
2225
2226 static void update_auth_hmac_md5(struct isis_auth *auth, struct stream *s,
2227 bool is_lsp)
2228 {
2229 uint8_t digest[16];
2230 uint16_t checksum, rem_lifetime;
2231
2232 if (is_lsp)
2233 safe_auth_md5(s, &checksum, &rem_lifetime);
2234
2235 memset(STREAM_DATA(s) + auth->offset, 0, 16);
2236 hmac_md5(STREAM_DATA(s), stream_get_endp(s), auth->passwd,
2237 auth->plength, digest);
2238 memcpy(auth->value, digest, 16);
2239 memcpy(STREAM_DATA(s) + auth->offset, digest, 16);
2240
2241 if (is_lsp)
2242 restore_auth_md5(s, checksum, rem_lifetime);
2243 }
2244
2245 static void update_auth(struct isis_tlvs *tlvs, struct stream *s, bool is_lsp)
2246 {
2247 struct isis_auth *auth_head = (struct isis_auth *)tlvs->isis_auth.head;
2248
2249 for (struct isis_auth *auth = auth_head; auth; auth = auth->next) {
2250 if (auth->type == ISIS_PASSWD_TYPE_HMAC_MD5)
2251 update_auth_hmac_md5(auth, s, is_lsp);
2252 }
2253 }
2254
2255 static int handle_pack_entry(struct pack_order_entry *pe,
2256 struct isis_tlvs *tlvs, struct stream *stream,
2257 struct isis_tlvs **fragment_tlvs,
2258 struct isis_tlvs *(*new_fragment)(struct list *l),
2259 struct list *new_fragment_arg)
2260 {
2261 int rv;
2262
2263 if (pe->how_to_pack == ISIS_ITEMS) {
2264 struct isis_item_list *l;
2265 l = (struct isis_item_list *)(((char *)tlvs)
2266 + pe->what_to_pack);
2267 rv = pack_items(pe->context, pe->type, l, stream, fragment_tlvs,
2268 pe, new_fragment, new_fragment_arg);
2269 } else {
2270 struct isis_mt_item_list *l;
2271 l = (struct isis_mt_item_list *)(((char *)tlvs)
2272 + pe->what_to_pack);
2273 rv = pack_mt_items(pe->context, pe->type, l, stream,
2274 fragment_tlvs, pe, new_fragment,
2275 new_fragment_arg);
2276 }
2277
2278 return rv;
2279 }
2280
2281 static int pack_tlvs(struct isis_tlvs *tlvs, struct stream *stream,
2282 struct isis_tlvs *fragment_tlvs,
2283 struct isis_tlvs *(*new_fragment)(struct list *l),
2284 struct list *new_fragment_arg)
2285 {
2286 int rv;
2287
2288 /* When fragmenting, don't add auth as it's already accounted for in the
2289 * size we are given. */
2290 if (!fragment_tlvs) {
2291 rv = pack_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth,
2292 stream, NULL, NULL, NULL, NULL);
2293 if (rv)
2294 return rv;
2295 }
2296
2297 rv = pack_tlv_protocols_supported(&tlvs->protocols_supported, stream);
2298 if (rv)
2299 return rv;
2300 if (fragment_tlvs) {
2301 copy_tlv_protocols_supported(
2302 &tlvs->protocols_supported,
2303 &fragment_tlvs->protocols_supported);
2304 }
2305
2306 rv = pack_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES,
2307 &tlvs->area_addresses, stream, NULL, NULL, NULL, NULL);
2308 if (rv)
2309 return rv;
2310 if (fragment_tlvs) {
2311 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES,
2312 &tlvs->area_addresses,
2313 &fragment_tlvs->area_addresses);
2314 }
2315
2316
2317 if (tlvs->mt_router_info_empty) {
2318 if (STREAM_WRITEABLE(stream) < 2)
2319 return 1;
2320 stream_putc(stream, ISIS_TLV_MT_ROUTER_INFO);
2321 stream_putc(stream, 0);
2322 if (fragment_tlvs)
2323 fragment_tlvs->mt_router_info_empty = true;
2324 } else {
2325 rv = pack_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO,
2326 &tlvs->mt_router_info, stream, NULL, NULL, NULL,
2327 NULL);
2328 if (rv)
2329 return rv;
2330 if (fragment_tlvs) {
2331 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO,
2332 &tlvs->mt_router_info,
2333 &fragment_tlvs->mt_router_info);
2334 }
2335 }
2336
2337 rv = pack_tlv_dynamic_hostname(tlvs->hostname, stream);
2338 if (rv)
2339 return rv;
2340 if (fragment_tlvs)
2341 fragment_tlvs->hostname =
2342 copy_tlv_dynamic_hostname(tlvs->hostname);
2343
2344 rv = pack_tlv_te_router_id(tlvs->te_router_id, stream);
2345 if (rv)
2346 return rv;
2347 if (fragment_tlvs) {
2348 fragment_tlvs->te_router_id =
2349 copy_tlv_te_router_id(tlvs->te_router_id);
2350 }
2351
2352 for (size_t pack_idx = 0; pack_idx < array_size(pack_order);
2353 pack_idx++) {
2354 rv = handle_pack_entry(&pack_order[pack_idx], tlvs, stream,
2355 fragment_tlvs ? &fragment_tlvs : NULL,
2356 new_fragment, new_fragment_arg);
2357
2358 if (rv)
2359 return rv;
2360 }
2361
2362 return 0;
2363 }
2364
2365 int isis_pack_tlvs(struct isis_tlvs *tlvs, struct stream *stream,
2366 size_t len_pointer, bool pad, bool is_lsp)
2367 {
2368 int rv;
2369
2370 rv = pack_tlvs(tlvs, stream, NULL, NULL, NULL);
2371 if (rv)
2372 return rv;
2373
2374 if (pad)
2375 add_padding(stream);
2376
2377 if (len_pointer != (size_t)-1) {
2378 stream_putw_at(stream, len_pointer, stream_get_endp(stream));
2379 }
2380
2381 update_auth(tlvs, stream, is_lsp);
2382
2383 return 0;
2384 }
2385
2386 static struct isis_tlvs *new_fragment(struct list *l)
2387 {
2388 struct isis_tlvs *rv = isis_alloc_tlvs();
2389
2390 listnode_add(l, rv);
2391 return rv;
2392 }
2393
2394 struct list *isis_fragment_tlvs(struct isis_tlvs *tlvs, size_t size)
2395 {
2396 struct stream *dummy_stream = stream_new(size);
2397 struct list *rv = list_new();
2398 struct isis_tlvs *fragment_tlvs = new_fragment(rv);
2399
2400 if (pack_tlvs(tlvs, dummy_stream, fragment_tlvs, new_fragment, rv)) {
2401 struct listnode *node;
2402 for (ALL_LIST_ELEMENTS_RO(rv, node, fragment_tlvs))
2403 isis_free_tlvs(fragment_tlvs);
2404 list_delete(rv);
2405 rv = NULL;
2406 }
2407
2408 stream_free(dummy_stream);
2409 return rv;
2410 }
2411
2412 static int unpack_tlv_unknown(enum isis_tlv_context context, uint8_t tlv_type,
2413 uint8_t tlv_len, struct stream *s,
2414 struct sbuf *log, int indent)
2415 {
2416 stream_forward_getp(s, tlv_len);
2417 sbuf_push(log, indent,
2418 "Skipping unknown TLV %" PRIu8 " (%" PRIu8 " bytes)\n",
2419 tlv_type, tlv_len);
2420 return 0;
2421 }
2422
2423 static int unpack_tlv(enum isis_tlv_context context, size_t avail_len,
2424 struct stream *stream, struct sbuf *log, void *dest,
2425 int indent)
2426 {
2427 uint8_t tlv_type, tlv_len;
2428 const struct tlv_ops *ops;
2429
2430 sbuf_push(log, indent, "Unpacking TLV...\n");
2431
2432 if (avail_len < 2) {
2433 sbuf_push(
2434 log, indent + 2,
2435 "Available data %zu too short to contain a TLV header.\n",
2436 avail_len);
2437 return 1;
2438 }
2439
2440 tlv_type = stream_getc(stream);
2441 tlv_len = stream_getc(stream);
2442
2443 sbuf_push(log, indent + 2,
2444 "Found TLV of type %" PRIu8 " and len %" PRIu8 ".\n",
2445 tlv_type, tlv_len);
2446
2447 if (avail_len < ((size_t)tlv_len) + 2) {
2448 sbuf_push(log, indent + 2,
2449 "Available data %zu too short for claimed TLV len %" PRIu8 ".\n",
2450 avail_len - 2, tlv_len);
2451 return 1;
2452 }
2453
2454 ops = tlv_table[context][tlv_type];
2455 if (ops && ops->unpack) {
2456 return ops->unpack(context, tlv_type, tlv_len, stream, log,
2457 dest, indent + 2);
2458 }
2459
2460 return unpack_tlv_unknown(context, tlv_type, tlv_len, stream, log,
2461 indent + 2);
2462 }
2463
2464 static int unpack_tlvs(enum isis_tlv_context context, size_t avail_len,
2465 struct stream *stream, struct sbuf *log, void *dest,
2466 int indent)
2467 {
2468 int rv;
2469 size_t tlv_start, tlv_pos;
2470
2471 tlv_start = stream_get_getp(stream);
2472 tlv_pos = 0;
2473
2474 sbuf_push(log, indent, "Unpacking %zu bytes of %s...\n", avail_len,
2475 (context == ISIS_CONTEXT_LSP) ? "TLVs" : "sub-TLVs");
2476
2477 while (tlv_pos < avail_len) {
2478 rv = unpack_tlv(context, avail_len - tlv_pos, stream, log, dest,
2479 indent + 2);
2480 if (rv)
2481 return rv;
2482
2483 tlv_pos = stream_get_getp(stream) - tlv_start;
2484 }
2485
2486 return 0;
2487 }
2488
2489 int isis_unpack_tlvs(size_t avail_len, struct stream *stream,
2490 struct isis_tlvs **dest, const char **log)
2491 {
2492 static struct sbuf logbuf;
2493 int indent = 0;
2494 int rv;
2495 struct isis_tlvs *result;
2496
2497 if (!sbuf_buf(&logbuf))
2498 sbuf_init(&logbuf, NULL, 0);
2499
2500 sbuf_reset(&logbuf);
2501 if (avail_len > STREAM_READABLE(stream)) {
2502 sbuf_push(&logbuf, indent,
2503 "Stream doesn't contain sufficient data. "
2504 "Claimed %zu, available %zu\n",
2505 avail_len, STREAM_READABLE(stream));
2506 return 1;
2507 }
2508
2509 result = isis_alloc_tlvs();
2510 rv = unpack_tlvs(ISIS_CONTEXT_LSP, avail_len, stream, &logbuf, result,
2511 indent);
2512
2513 *log = sbuf_buf(&logbuf);
2514 *dest = result;
2515
2516 return rv;
2517 }
2518
2519 #define TLV_OPS(_name_, _desc_) \
2520 static const struct tlv_ops tlv_##_name_##_ops = { \
2521 .name = _desc_, .unpack = unpack_tlv_##_name_, \
2522 }
2523
2524 #define ITEM_TLV_OPS(_name_, _desc_) \
2525 static const struct tlv_ops tlv_##_name_##_ops = { \
2526 .name = _desc_, \
2527 .unpack = unpack_tlv_with_items, \
2528 \
2529 .pack_item = pack_item_##_name_, \
2530 .free_item = free_item_##_name_, \
2531 .unpack_item = unpack_item_##_name_, \
2532 .format_item = format_item_##_name_, \
2533 .copy_item = copy_item_##_name_}
2534
2535 #define SUBTLV_OPS(_name_, _desc_) \
2536 static const struct tlv_ops subtlv_##_name_##_ops = { \
2537 .name = _desc_, .unpack = unpack_subtlv_##_name_, \
2538 }
2539
2540 ITEM_TLV_OPS(area_address, "TLV 1 Area Addresses");
2541 ITEM_TLV_OPS(oldstyle_reach, "TLV 2 IS Reachability");
2542 ITEM_TLV_OPS(lan_neighbor, "TLV 6 LAN Neighbors");
2543 ITEM_TLV_OPS(lsp_entry, "TLV 9 LSP Entries");
2544 ITEM_TLV_OPS(auth, "TLV 10 IS-IS Auth");
2545 ITEM_TLV_OPS(extended_reach, "TLV 22 Extended Reachability");
2546 ITEM_TLV_OPS(oldstyle_ip_reach, "TLV 128/130 IP Reachability");
2547 TLV_OPS(protocols_supported, "TLV 129 Protocols Supported");
2548 ITEM_TLV_OPS(ipv4_address, "TLV 132 IPv4 Interface Address");
2549 TLV_OPS(te_router_id, "TLV 134 TE Router ID");
2550 ITEM_TLV_OPS(extended_ip_reach, "TLV 135 Extended IP Reachability");
2551 TLV_OPS(dynamic_hostname, "TLV 137 Dynamic Hostname");
2552 ITEM_TLV_OPS(mt_router_info, "TLV 229 MT Router Information");
2553 ITEM_TLV_OPS(ipv6_address, "TLV 232 IPv6 Interface Address");
2554 ITEM_TLV_OPS(ipv6_reach, "TLV 236 IPv6 Reachability");
2555
2556 SUBTLV_OPS(ipv6_source_prefix, "Sub-TLV 22 IPv6 Source Prefix");
2557
2558 static const struct tlv_ops *tlv_table[ISIS_CONTEXT_MAX][ISIS_TLV_MAX] = {
2559 [ISIS_CONTEXT_LSP] = {
2560 [ISIS_TLV_AREA_ADDRESSES] = &tlv_area_address_ops,
2561 [ISIS_TLV_OLDSTYLE_REACH] = &tlv_oldstyle_reach_ops,
2562 [ISIS_TLV_LAN_NEIGHBORS] = &tlv_lan_neighbor_ops,
2563 [ISIS_TLV_LSP_ENTRY] = &tlv_lsp_entry_ops,
2564 [ISIS_TLV_AUTH] = &tlv_auth_ops,
2565 [ISIS_TLV_EXTENDED_REACH] = &tlv_extended_reach_ops,
2566 [ISIS_TLV_MT_REACH] = &tlv_extended_reach_ops,
2567 [ISIS_TLV_OLDSTYLE_IP_REACH] = &tlv_oldstyle_ip_reach_ops,
2568 [ISIS_TLV_PROTOCOLS_SUPPORTED] = &tlv_protocols_supported_ops,
2569 [ISIS_TLV_OLDSTYLE_IP_REACH_EXT] = &tlv_oldstyle_ip_reach_ops,
2570 [ISIS_TLV_IPV4_ADDRESS] = &tlv_ipv4_address_ops,
2571 [ISIS_TLV_TE_ROUTER_ID] = &tlv_te_router_id_ops,
2572 [ISIS_TLV_EXTENDED_IP_REACH] = &tlv_extended_ip_reach_ops,
2573 [ISIS_TLV_MT_IP_REACH] = &tlv_extended_ip_reach_ops,
2574 [ISIS_TLV_DYNAMIC_HOSTNAME] = &tlv_dynamic_hostname_ops,
2575 [ISIS_TLV_MT_ROUTER_INFO] = &tlv_mt_router_info_ops,
2576 [ISIS_TLV_IPV6_ADDRESS] = &tlv_ipv6_address_ops,
2577 [ISIS_TLV_IPV6_REACH] = &tlv_ipv6_reach_ops,
2578 [ISIS_TLV_MT_IPV6_REACH] = &tlv_ipv6_reach_ops,
2579 },
2580 [ISIS_CONTEXT_SUBTLV_NE_REACH] = {},
2581 [ISIS_CONTEXT_SUBTLV_IP_REACH] = {},
2582 [ISIS_CONTEXT_SUBTLV_IPV6_REACH] = {
2583 [ISIS_SUBTLV_IPV6_SOURCE_PREFIX] = &subtlv_ipv6_source_prefix_ops,
2584 }
2585 };
2586
2587 /* Accessor functions */
2588
2589 void isis_tlvs_add_auth(struct isis_tlvs *tlvs, struct isis_passwd *passwd)
2590 {
2591 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth);
2592 init_item_list(&tlvs->isis_auth);
2593
2594 if (passwd->type == ISIS_PASSWD_TYPE_UNUSED)
2595 return;
2596
2597 struct isis_auth *auth = XCALLOC(MTYPE_ISIS_TLV, sizeof(*auth));
2598
2599 auth->type = passwd->type;
2600
2601 auth->plength = passwd->len;
2602 memcpy(auth->passwd, passwd->passwd,
2603 MIN(sizeof(auth->passwd), sizeof(passwd->passwd)));
2604
2605 if (auth->type == ISIS_PASSWD_TYPE_CLEARTXT) {
2606 auth->length = passwd->len;
2607 memcpy(auth->value, passwd->passwd,
2608 MIN(sizeof(auth->value), sizeof(passwd->passwd)));
2609 }
2610
2611 append_item(&tlvs->isis_auth, (struct isis_item *)auth);
2612 }
2613
2614 void isis_tlvs_add_area_addresses(struct isis_tlvs *tlvs,
2615 struct list *addresses)
2616 {
2617 struct listnode *node;
2618 struct area_addr *area_addr;
2619
2620 for (ALL_LIST_ELEMENTS_RO(addresses, node, area_addr)) {
2621 struct isis_area_address *a =
2622 XCALLOC(MTYPE_ISIS_TLV, sizeof(*a));
2623
2624 a->len = area_addr->addr_len;
2625 memcpy(a->addr, area_addr->area_addr, 20);
2626 append_item(&tlvs->area_addresses, (struct isis_item *)a);
2627 }
2628 }
2629
2630 void isis_tlvs_add_lan_neighbors(struct isis_tlvs *tlvs, struct list *neighbors)
2631 {
2632 struct listnode *node;
2633 u_char *snpa;
2634
2635 for (ALL_LIST_ELEMENTS_RO(neighbors, node, snpa)) {
2636 struct isis_lan_neighbor *n =
2637 XCALLOC(MTYPE_ISIS_TLV, sizeof(*n));
2638
2639 memcpy(n->mac, snpa, 6);
2640 append_item(&tlvs->lan_neighbor, (struct isis_item *)n);
2641 }
2642 }
2643
2644 void isis_tlvs_set_protocols_supported(struct isis_tlvs *tlvs,
2645 struct nlpids *nlpids)
2646 {
2647 tlvs->protocols_supported.count = nlpids->count;
2648 if (tlvs->protocols_supported.protocols)
2649 XFREE(MTYPE_ISIS_TLV, tlvs->protocols_supported.protocols);
2650 if (nlpids->count) {
2651 tlvs->protocols_supported.protocols =
2652 XCALLOC(MTYPE_ISIS_TLV, nlpids->count);
2653 memcpy(tlvs->protocols_supported.protocols, nlpids->nlpids,
2654 nlpids->count);
2655 } else {
2656 tlvs->protocols_supported.protocols = NULL;
2657 }
2658 }
2659
2660 void isis_tlvs_add_mt_router_info(struct isis_tlvs *tlvs, uint16_t mtid,
2661 bool overload, bool attached)
2662 {
2663 struct isis_mt_router_info *i = XCALLOC(MTYPE_ISIS_TLV, sizeof(*i));
2664
2665 i->overload = overload;
2666 i->attached = attached;
2667 i->mtid = mtid;
2668 append_item(&tlvs->mt_router_info, (struct isis_item *)i);
2669 }
2670
2671 void isis_tlvs_add_ipv4_address(struct isis_tlvs *tlvs, struct in_addr *addr)
2672 {
2673 struct isis_ipv4_address *a = XCALLOC(MTYPE_ISIS_TLV, sizeof(*a));
2674 a->addr = *addr;
2675 append_item(&tlvs->ipv4_address, (struct isis_item *)a);
2676 }
2677
2678
2679 void isis_tlvs_add_ipv4_addresses(struct isis_tlvs *tlvs,
2680 struct list *addresses)
2681 {
2682 struct listnode *node;
2683 struct prefix_ipv4 *ip_addr;
2684 unsigned int addr_count = 0;
2685
2686 for (ALL_LIST_ELEMENTS_RO(addresses, node, ip_addr)) {
2687 isis_tlvs_add_ipv4_address(tlvs, &ip_addr->prefix);
2688 addr_count++;
2689 if (addr_count >= 63)
2690 break;
2691 }
2692 }
2693
2694 void isis_tlvs_add_ipv6_addresses(struct isis_tlvs *tlvs,
2695 struct list *addresses)
2696 {
2697 struct listnode *node;
2698 struct prefix_ipv6 *ip_addr;
2699
2700 for (ALL_LIST_ELEMENTS_RO(addresses, node, ip_addr)) {
2701 struct isis_ipv6_address *a =
2702 XCALLOC(MTYPE_ISIS_TLV, sizeof(*a));
2703
2704 a->addr = ip_addr->prefix;
2705 append_item(&tlvs->ipv6_address, (struct isis_item *)a);
2706 }
2707 }
2708
2709 typedef bool (*auth_validator_func)(struct isis_passwd *passwd,
2710 struct stream *stream,
2711 struct isis_auth *auth, bool is_lsp);
2712
2713 static bool auth_validator_cleartxt(struct isis_passwd *passwd,
2714 struct stream *stream,
2715 struct isis_auth *auth, bool is_lsp)
2716 {
2717 return (auth->length == passwd->len
2718 && !memcmp(auth->value, passwd->passwd, passwd->len));
2719 }
2720
2721 static bool auth_validator_hmac_md5(struct isis_passwd *passwd,
2722 struct stream *stream,
2723 struct isis_auth *auth, bool is_lsp)
2724 {
2725 uint8_t digest[16];
2726 uint16_t checksum;
2727 uint16_t rem_lifetime;
2728
2729 if (is_lsp)
2730 safe_auth_md5(stream, &checksum, &rem_lifetime);
2731
2732 memset(STREAM_DATA(stream) + auth->offset, 0, 16);
2733 hmac_md5(STREAM_DATA(stream), stream_get_endp(stream), passwd->passwd,
2734 passwd->len, digest);
2735 memcpy(STREAM_DATA(stream) + auth->offset, auth->value, 16);
2736
2737 bool rv = !memcmp(digest, auth->value, 16);
2738
2739 if (is_lsp)
2740 restore_auth_md5(stream, checksum, rem_lifetime);
2741
2742 return rv;
2743 }
2744
2745 static const auth_validator_func auth_validators[] = {
2746 [ISIS_PASSWD_TYPE_CLEARTXT] = auth_validator_cleartxt,
2747 [ISIS_PASSWD_TYPE_HMAC_MD5] = auth_validator_hmac_md5,
2748 };
2749
2750 bool isis_tlvs_auth_is_valid(struct isis_tlvs *tlvs, struct isis_passwd *passwd,
2751 struct stream *stream, bool is_lsp)
2752 {
2753 /* If no auth is set, always pass authentication */
2754 if (!passwd->type)
2755 return true;
2756
2757 /* If we don't known how to validate the auth, return invalid */
2758 if (passwd->type >= array_size(auth_validators)
2759 || !auth_validators[passwd->type])
2760 return false;
2761
2762 struct isis_auth *auth_head = (struct isis_auth *)tlvs->isis_auth.head;
2763 struct isis_auth *auth;
2764 for (auth = auth_head; auth; auth = auth->next) {
2765 if (auth->type == passwd->type)
2766 break;
2767 }
2768
2769 /* If matching auth TLV could not be found, return invalid */
2770 if (!auth)
2771 return false;
2772
2773 /* Perform validation and return result */
2774 return auth_validators[passwd->type](passwd, stream, auth, is_lsp);
2775 }
2776
2777 bool isis_tlvs_area_addresses_match(struct isis_tlvs *tlvs,
2778 struct list *addresses)
2779 {
2780 struct isis_area_address *addr_head;
2781
2782 addr_head = (struct isis_area_address *)tlvs->area_addresses.head;
2783 for (struct isis_area_address *addr = addr_head; addr;
2784 addr = addr->next) {
2785 struct listnode *node;
2786 struct area_addr *a;
2787
2788 for (ALL_LIST_ELEMENTS_RO(addresses, node, a)) {
2789 if (a->addr_len == addr->len
2790 && !memcmp(a->area_addr, addr->addr, addr->len))
2791 return true;
2792 }
2793 }
2794
2795 return false;
2796 }
2797
2798 static void tlvs_area_addresses_to_adj(struct isis_tlvs *tlvs,
2799 struct isis_adjacency *adj,
2800 bool *changed)
2801 {
2802 if (adj->area_address_count != tlvs->area_addresses.count) {
2803 *changed = true;
2804 adj->area_address_count = tlvs->area_addresses.count;
2805 adj->area_addresses = XREALLOC(
2806 MTYPE_ISIS_ADJACENCY_INFO, adj->area_addresses,
2807 adj->area_address_count * sizeof(*adj->area_addresses));
2808 }
2809
2810 struct isis_area_address *addr = NULL;
2811 for (unsigned int i = 0; i < tlvs->area_addresses.count; i++) {
2812 if (!addr)
2813 addr = (struct isis_area_address *)
2814 tlvs->area_addresses.head;
2815 else
2816 addr = addr->next;
2817
2818 if (adj->area_addresses[i].addr_len == addr->len
2819 && !memcmp(adj->area_addresses[i].area_addr, addr->addr,
2820 addr->len)) {
2821 continue;
2822 }
2823
2824 *changed = true;
2825 adj->area_addresses[i].addr_len = addr->len;
2826 memcpy(adj->area_addresses[i].area_addr, addr->addr, addr->len);
2827 }
2828 }
2829
2830 static void tlvs_protocols_supported_to_adj(struct isis_tlvs *tlvs,
2831 struct isis_adjacency *adj,
2832 bool *changed)
2833 {
2834 bool ipv4_supported = false, ipv6_supported = false;
2835
2836 for (uint8_t i = 0; i < tlvs->protocols_supported.count; i++) {
2837 if (tlvs->protocols_supported.protocols[i] == NLPID_IP)
2838 ipv4_supported = true;
2839 if (tlvs->protocols_supported.protocols[i] == NLPID_IPV6)
2840 ipv6_supported = true;
2841 }
2842
2843 struct nlpids reduced = {};
2844
2845 if (ipv4_supported && ipv6_supported) {
2846 reduced.count = 2;
2847 reduced.nlpids[0] = NLPID_IP;
2848 reduced.nlpids[1] = NLPID_IPV6;
2849 } else if (ipv4_supported) {
2850 reduced.count = 1;
2851 reduced.nlpids[0] = NLPID_IP;
2852 } else if (ipv6_supported) {
2853 reduced.count = 1;
2854 reduced.nlpids[1] = NLPID_IPV6;
2855 } else {
2856 reduced.count = 0;
2857 }
2858
2859 if (adj->nlpids.count == reduced.count
2860 && !memcmp(adj->nlpids.nlpids, reduced.nlpids, reduced.count))
2861 return;
2862
2863 *changed = true;
2864 adj->nlpids.count = reduced.count;
2865 memcpy(adj->nlpids.nlpids, reduced.nlpids, reduced.count);
2866 }
2867
2868 static void tlvs_ipv4_addresses_to_adj(struct isis_tlvs *tlvs,
2869 struct isis_adjacency *adj,
2870 bool *changed)
2871 {
2872 if (adj->ipv4_address_count != tlvs->ipv4_address.count) {
2873 *changed = true;
2874 adj->ipv4_address_count = tlvs->ipv4_address.count;
2875 adj->ipv4_addresses = XREALLOC(
2876 MTYPE_ISIS_ADJACENCY_INFO, adj->ipv4_addresses,
2877 adj->ipv4_address_count * sizeof(*adj->ipv4_addresses));
2878 }
2879
2880 struct isis_ipv4_address *addr = NULL;
2881 for (unsigned int i = 0; i < tlvs->ipv4_address.count; i++) {
2882 if (!addr)
2883 addr = (struct isis_ipv4_address *)
2884 tlvs->ipv4_address.head;
2885 else
2886 addr = addr->next;
2887
2888 if (!memcmp(&adj->ipv4_addresses[i], &addr->addr,
2889 sizeof(addr->addr)))
2890 continue;
2891
2892 *changed = true;
2893 adj->ipv4_addresses[i] = addr->addr;
2894 }
2895 }
2896
2897 static void tlvs_ipv6_addresses_to_adj(struct isis_tlvs *tlvs,
2898 struct isis_adjacency *adj,
2899 bool *changed)
2900 {
2901 if (adj->ipv6_address_count != tlvs->ipv6_address.count) {
2902 *changed = true;
2903 adj->ipv6_address_count = tlvs->ipv6_address.count;
2904 adj->ipv6_addresses = XREALLOC(
2905 MTYPE_ISIS_ADJACENCY_INFO, adj->ipv6_addresses,
2906 adj->ipv6_address_count * sizeof(*adj->ipv6_addresses));
2907 }
2908
2909 struct isis_ipv6_address *addr = NULL;
2910 for (unsigned int i = 0; i < tlvs->ipv6_address.count; i++) {
2911 if (!addr)
2912 addr = (struct isis_ipv6_address *)
2913 tlvs->ipv6_address.head;
2914 else
2915 addr = addr->next;
2916
2917 if (!memcmp(&adj->ipv6_addresses[i], &addr->addr,
2918 sizeof(addr->addr)))
2919 continue;
2920
2921 *changed = true;
2922 adj->ipv6_addresses[i] = addr->addr;
2923 }
2924 }
2925
2926 void isis_tlvs_to_adj(struct isis_tlvs *tlvs, struct isis_adjacency *adj,
2927 bool *changed)
2928 {
2929 *changed = false;
2930
2931 tlvs_area_addresses_to_adj(tlvs, adj, changed);
2932 tlvs_protocols_supported_to_adj(tlvs, adj, changed);
2933 tlvs_ipv4_addresses_to_adj(tlvs, adj, changed);
2934 tlvs_ipv6_addresses_to_adj(tlvs, adj, changed);
2935 }
2936
2937 bool isis_tlvs_own_snpa_found(struct isis_tlvs *tlvs, uint8_t *snpa)
2938 {
2939 struct isis_lan_neighbor *ne_head;
2940
2941 ne_head = (struct isis_lan_neighbor *)tlvs->lan_neighbor.head;
2942 for (struct isis_lan_neighbor *ne = ne_head; ne; ne = ne->next) {
2943 if (!memcmp(ne->mac, snpa, ETH_ALEN))
2944 return true;
2945 }
2946
2947 return false;
2948 }
2949
2950 void isis_tlvs_add_lsp_entry(struct isis_tlvs *tlvs, struct isis_lsp *lsp)
2951 {
2952 struct isis_lsp_entry *entry = XCALLOC(MTYPE_ISIS_TLV, sizeof(*entry));
2953
2954 entry->rem_lifetime = lsp->hdr.rem_lifetime;
2955 memcpy(entry->id, lsp->hdr.lsp_id, ISIS_SYS_ID_LEN + 2);
2956 entry->checksum = lsp->hdr.checksum;
2957 entry->seqno = lsp->hdr.seqno;
2958 entry->lsp = lsp;
2959
2960 append_item(&tlvs->lsp_entries, (struct isis_item *)entry);
2961 }
2962
2963 void isis_tlvs_add_csnp_entries(struct isis_tlvs *tlvs, uint8_t *start_id,
2964 uint8_t *stop_id, uint16_t num_lsps,
2965 dict_t *lspdb, struct isis_lsp **last_lsp)
2966 {
2967 dnode_t *first = dict_lower_bound(lspdb, start_id);
2968 if (!first)
2969 return;
2970
2971 dnode_t *last = dict_upper_bound(lspdb, stop_id);
2972 dnode_t *curr = first;
2973
2974 isis_tlvs_add_lsp_entry(tlvs, first->dict_data);
2975 *last_lsp = first->dict_data;
2976
2977 while (curr) {
2978 curr = dict_next(lspdb, curr);
2979 if (curr) {
2980 isis_tlvs_add_lsp_entry(tlvs, curr->dict_data);
2981 *last_lsp = curr->dict_data;
2982 }
2983 if (curr == last || tlvs->lsp_entries.count == num_lsps)
2984 break;
2985 }
2986 }
2987
2988 void isis_tlvs_set_dynamic_hostname(struct isis_tlvs *tlvs,
2989 const char *hostname)
2990 {
2991 XFREE(MTYPE_ISIS_TLV, tlvs->hostname);
2992 if (hostname)
2993 tlvs->hostname = XSTRDUP(MTYPE_ISIS_TLV, hostname);
2994 }
2995
2996 void isis_tlvs_set_te_router_id(struct isis_tlvs *tlvs,
2997 const struct in_addr *id)
2998 {
2999 XFREE(MTYPE_ISIS_TLV, tlvs->te_router_id);
3000 if (!id)
3001 return;
3002 tlvs->te_router_id = XCALLOC(MTYPE_ISIS_TLV, sizeof(*id));
3003 memcpy(tlvs->te_router_id, id, sizeof(*id));
3004 }
3005
3006 void isis_tlvs_add_oldstyle_ip_reach(struct isis_tlvs *tlvs,
3007 struct prefix_ipv4 *dest, uint8_t metric)
3008 {
3009 struct isis_oldstyle_ip_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
3010
3011 r->metric = metric;
3012 memcpy(&r->prefix, dest, sizeof(*dest));
3013 apply_mask_ipv4(&r->prefix);
3014 append_item(&tlvs->oldstyle_ip_reach, (struct isis_item *)r);
3015 }
3016
3017 void isis_tlvs_add_extended_ip_reach(struct isis_tlvs *tlvs,
3018 struct prefix_ipv4 *dest, uint32_t metric)
3019 {
3020 struct isis_extended_ip_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
3021
3022 r->metric = metric;
3023 memcpy(&r->prefix, dest, sizeof(*dest));
3024 apply_mask_ipv4(&r->prefix);
3025 append_item(&tlvs->extended_ip_reach, (struct isis_item *)r);
3026 }
3027
3028 void isis_tlvs_add_ipv6_reach(struct isis_tlvs *tlvs, uint16_t mtid,
3029 struct prefix_ipv6 *dest, uint32_t metric)
3030 {
3031 struct isis_ipv6_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
3032
3033 r->metric = metric;
3034 memcpy(&r->prefix, dest, sizeof(*dest));
3035 apply_mask_ipv6(&r->prefix);
3036
3037 struct isis_item_list *l;
3038 l = (mtid == ISIS_MT_IPV4_UNICAST)
3039 ? &tlvs->ipv6_reach
3040 : isis_get_mt_items(&tlvs->mt_ipv6_reach, mtid);
3041 append_item(l, (struct isis_item *)r);
3042 }
3043
3044 void isis_tlvs_add_oldstyle_reach(struct isis_tlvs *tlvs, uint8_t *id,
3045 uint8_t metric)
3046 {
3047 struct isis_oldstyle_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
3048
3049 r->metric = metric;
3050 memcpy(r->id, id, sizeof(r->id));
3051 append_item(&tlvs->oldstyle_reach, (struct isis_item *)r);
3052 }
3053
3054 void isis_tlvs_add_extended_reach(struct isis_tlvs *tlvs, uint16_t mtid,
3055 uint8_t *id, uint32_t metric,
3056 uint8_t *subtlvs, uint8_t subtlv_len)
3057 {
3058 struct isis_extended_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
3059
3060 memcpy(r->id, id, sizeof(r->id));
3061 r->metric = metric;
3062 if (subtlvs && subtlv_len) {
3063 r->subtlvs = XCALLOC(MTYPE_ISIS_TLV, subtlv_len);
3064 memcpy(r->subtlvs, subtlvs, subtlv_len);
3065 r->subtlv_len = subtlv_len;
3066 }
3067
3068 struct isis_item_list *l;
3069 if (mtid == ISIS_MT_IPV4_UNICAST)
3070 l = &tlvs->extended_reach;
3071 else
3072 l = isis_get_mt_items(&tlvs->mt_reach, mtid);
3073 append_item(l, (struct isis_item *)r);
3074 }
3075
3076 struct isis_mt_router_info *
3077 isis_tlvs_lookup_mt_router_info(struct isis_tlvs *tlvs, uint16_t mtid)
3078 {
3079 if (tlvs->mt_router_info_empty)
3080 return NULL;
3081
3082 struct isis_mt_router_info *rv;
3083 for (rv = (struct isis_mt_router_info *)tlvs->mt_router_info.head; rv;
3084 rv = rv->next) {
3085 if (rv->mtid == mtid)
3086 return rv;
3087 }
3088
3089 return NULL;
3090 }