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