]> git.proxmox.com Git - mirror_frr.git/blame - isisd/isis_tlvs.c
isisd: fix display of the Extended IPv4 reachability TLV
[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
bd507085
CF
110/* Functions for Sub-TLV 3 SR Prefix-SID */
111
112static struct isis_item *copy_item_prefix_sid(struct isis_item *i)
113{
114 struct isis_prefix_sid *sid = (struct isis_prefix_sid *)i;
115 struct isis_prefix_sid *rv = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*rv));
116
117 rv->flags = sid->flags;
118 rv->algorithm = sid->algorithm;
119 rv->value = sid->value;
120 return (struct isis_item *)rv;
121}
122
123static void format_item_prefix_sid(uint16_t mtid, struct isis_item *i,
124 struct sbuf *buf, int indent)
125{
126 struct isis_prefix_sid *sid = (struct isis_prefix_sid *)i;
127
128 sbuf_push(buf, indent, "SR Prefix-SID:\n");
129 sbuf_push(buf, indent, " Flags:%s%s%s%s%s%s\n",
130 sid->flags & ISIS_PREFIX_SID_READVERTISED ? " READVERTISED" : "",
131 sid->flags & ISIS_PREFIX_SID_NODE ? " NODE" : "",
132 sid->flags & ISIS_PREFIX_SID_NO_PHP ? " NO_PHP" : "",
133 sid->flags & ISIS_PREFIX_SID_EXPLICIT_NULL ? " EXPLICIT-NULL" : "",
134 sid->flags & ISIS_PREFIX_SID_VALUE ? " VALUE" : "",
135 sid->flags & ISIS_PREFIX_SID_LOCAL ? " LOCAL" : "");
136 sbuf_push(buf, indent, " Algorithm: %" PRIu8 "\n", sid->algorithm);
137 if (sid->flags & ISIS_PREFIX_SID_VALUE) {
138 sbuf_push(buf, indent, "Label: %" PRIu32 "\n", sid->value);
139 } else {
140 sbuf_push(buf, indent, "Index: %" PRIu32 "\n", sid->value);
141 }
142}
143
144static void free_item_prefix_sid(struct isis_item *i)
145{
146 XFREE(MTYPE_ISIS_SUBTLV, i);
147}
148
149static int pack_item_prefix_sid(struct isis_item *i, struct stream *s)
150{
151 struct isis_prefix_sid *sid = (struct isis_prefix_sid *)i;
152
153 uint8_t size = (sid->flags & ISIS_PREFIX_SID_VALUE) ? 5 : 6;
154
155 if (STREAM_WRITEABLE(s) < size)
156 return 1;
157
158 stream_putc(s, sid->flags);
159 stream_putc(s, sid->algorithm);
160
161 if (sid->flags & ISIS_PREFIX_SID_VALUE) {
162 stream_put3(s, sid->value);
163 } else {
164 stream_putl(s, sid->value);
165 }
166
167 return 0;
168}
169
170static int unpack_item_prefix_sid(uint16_t mtid, uint8_t len, struct stream *s,
171 struct sbuf *log, void *dest, int indent)
172{
173 struct isis_subtlvs *subtlvs = dest;
3e300703
DL
174 struct isis_prefix_sid sid = {
175 };
bd507085
CF
176
177 sbuf_push(log, indent, "Unpacking SR Prefix-SID...\n");
178
179 if (len < 5) {
180 sbuf_push(log, indent,
181 "Not enough data left. (expected 5 or more bytes, got %" PRIu8 ")\n",
182 len);
183 return 1;
184 }
185
186 sid.flags = stream_getc(s);
187 if ((sid.flags & ISIS_PREFIX_SID_VALUE)
188 != (sid.flags & ISIS_PREFIX_SID_LOCAL)) {
189 sbuf_push(log, indent, "Flags inplausible: Local Flag needs to match Value Flag\n");
190 return 0;
191 }
192
193 sid.algorithm = stream_getc(s);
194
195 uint8_t expected_size = (sid.flags & ISIS_PREFIX_SID_VALUE) ? 5 : 6;
196 if (len != expected_size) {
197 sbuf_push(log, indent,
198 "TLV size differs from expected size. "
199 "(expected %u but got %" PRIu8 ")\n",
200 expected_size, len);
201 return 1;
202 }
203
204 if (sid.flags & ISIS_PREFIX_SID_VALUE) {
205 sid.value = stream_get3(s);
206 } else {
207 sid.value = stream_getl(s);
208 }
209
210 format_item_prefix_sid(mtid, (struct isis_item *)&sid, log, indent + 2);
211 append_item(&subtlvs->prefix_sids, copy_item_prefix_sid((struct isis_item *)&sid));
212 return 0;
213}
214
7ef5fefc
CF
215/* Functions for Sub-TVL ??? IPv6 Source Prefix */
216
217static struct prefix_ipv6 *copy_subtlv_ipv6_source_prefix(struct prefix_ipv6 *p)
218{
219 if (!p)
220 return NULL;
221
222 struct prefix_ipv6 *rv = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*rv));
223 rv->family = p->family;
224 rv->prefixlen = p->prefixlen;
225 memcpy(&rv->prefix, &p->prefix, sizeof(rv->prefix));
226 return rv;
227}
228
229static void format_subtlv_ipv6_source_prefix(struct prefix_ipv6 *p,
230 struct sbuf *buf, int indent)
231{
232 if (!p)
233 return;
234
235 char prefixbuf[PREFIX2STR_BUFFER];
236 sbuf_push(buf, indent, "IPv6 Source Prefix: %s\n",
237 prefix2str(p, prefixbuf, sizeof(prefixbuf)));
238}
239
240static int pack_subtlv_ipv6_source_prefix(struct prefix_ipv6 *p,
241 struct stream *s)
242{
243 if (!p)
244 return 0;
245
246 if (STREAM_WRITEABLE(s) < 3 + (unsigned)PSIZE(p->prefixlen))
247 return 1;
248
249 stream_putc(s, ISIS_SUBTLV_IPV6_SOURCE_PREFIX);
250 stream_putc(s, 1 + PSIZE(p->prefixlen));
251 stream_putc(s, p->prefixlen);
252 stream_put(s, &p->prefix, PSIZE(p->prefixlen));
253 return 0;
254}
255
256static int unpack_subtlv_ipv6_source_prefix(enum isis_tlv_context context,
257 uint8_t tlv_type, uint8_t tlv_len,
258 struct stream *s, struct sbuf *log,
259 void *dest, int indent)
260{
261 struct isis_subtlvs *subtlvs = dest;
262 struct prefix_ipv6 p = {
263 .family = AF_INET6,
264 };
265
266 sbuf_push(log, indent, "Unpacking IPv6 Source Prefix Sub-TLV...\n");
267
268 if (tlv_len < 1) {
98c5bc15
CF
269 sbuf_push(log, indent,
270 "Not enough data left. (expected 1 or more bytes, got %" PRIu8 ")\n",
271 tlv_len);
7ef5fefc
CF
272 return 1;
273 }
274
275 p.prefixlen = stream_getc(s);
276 if (p.prefixlen > 128) {
277 sbuf_push(log, indent, "Prefixlen %u is inplausible for IPv6\n",
278 p.prefixlen);
279 return 1;
280 }
281
282 if (tlv_len != 1 + PSIZE(p.prefixlen)) {
283 sbuf_push(
284 log, indent,
285 "TLV size differs from expected size for the prefixlen. "
286 "(expected %u but got %" PRIu8 ")\n",
287 1 + PSIZE(p.prefixlen), tlv_len);
288 return 1;
289 }
290
291 stream_get(&p.prefix, s, PSIZE(p.prefixlen));
292
293 if (subtlvs->source_prefix) {
294 sbuf_push(
295 log, indent,
296 "WARNING: source prefix Sub-TLV present multiple times.\n");
297 /* Ignore all but first occurrence of the source prefix Sub-TLV
298 */
299 return 0;
300 }
301
302 subtlvs->source_prefix = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(p));
303 memcpy(subtlvs->source_prefix, &p, sizeof(p));
304 return 0;
305}
bd507085
CF
306static void init_item_list(struct isis_item_list *items);
307static struct isis_item *copy_item(enum isis_tlv_context context,
308 enum isis_tlv_type type,
309 struct isis_item *item);
310static void copy_items(enum isis_tlv_context context, enum isis_tlv_type type,
311 struct isis_item_list *src, struct isis_item_list *dest);
312static void format_items_(uint16_t mtid, enum isis_tlv_context context,
313 enum isis_tlv_type type, struct isis_item_list *items,
314 struct sbuf *buf, int indent);
315#define format_items(...) format_items_(ISIS_MT_IPV4_UNICAST, __VA_ARGS__)
316static void free_items(enum isis_tlv_context context, enum isis_tlv_type type,
317 struct isis_item_list *items);
318static int pack_items_(uint16_t mtid, enum isis_tlv_context context,
319 enum isis_tlv_type type, struct isis_item_list *items,
320 struct stream *s, struct isis_tlvs **fragment_tlvs,
321 struct pack_order_entry *pe,
322 struct isis_tlvs *(*new_fragment)(struct list *l),
323 struct list *new_fragment_arg);
324#define pack_items(...) pack_items_(ISIS_MT_IPV4_UNICAST, __VA_ARGS__)
7ef5fefc
CF
325
326/* Functions related to subtlvs */
327
bd507085 328static struct isis_subtlvs *isis_alloc_subtlvs(enum isis_tlv_context context)
7ef5fefc
CF
329{
330 struct isis_subtlvs *result;
331
332 result = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*result));
bd507085
CF
333 result->context = context;
334
335 init_item_list(&result->prefix_sids);
7ef5fefc
CF
336
337 return result;
338}
339
340static struct isis_subtlvs *copy_subtlvs(struct isis_subtlvs *subtlvs)
341{
342 if (!subtlvs)
343 return NULL;
344
345 struct isis_subtlvs *rv = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*rv));
346
bd507085
CF
347 rv->context = subtlvs->context;
348
349 copy_items(subtlvs->context, ISIS_SUBTLV_PREFIX_SID,
350 &subtlvs->prefix_sids, &rv->prefix_sids);
351
7ef5fefc
CF
352 rv->source_prefix =
353 copy_subtlv_ipv6_source_prefix(subtlvs->source_prefix);
354 return rv;
355}
356
357static void format_subtlvs(struct isis_subtlvs *subtlvs, struct sbuf *buf,
358 int indent)
359{
bd507085
CF
360 format_items(subtlvs->context, ISIS_SUBTLV_PREFIX_SID,
361 &subtlvs->prefix_sids, buf, indent);
362
7ef5fefc
CF
363 format_subtlv_ipv6_source_prefix(subtlvs->source_prefix, buf, indent);
364}
365
366static void isis_free_subtlvs(struct isis_subtlvs *subtlvs)
367{
368 if (!subtlvs)
369 return;
370
bd507085
CF
371 free_items(subtlvs->context, ISIS_SUBTLV_PREFIX_SID,
372 &subtlvs->prefix_sids);
373
7ef5fefc
CF
374 XFREE(MTYPE_ISIS_SUBTLV, subtlvs->source_prefix);
375
376 XFREE(MTYPE_ISIS_SUBTLV, subtlvs);
377}
378
379static int pack_subtlvs(struct isis_subtlvs *subtlvs, struct stream *s)
380{
381 int rv;
382 size_t subtlv_len_pos = stream_get_endp(s);
383
384 if (STREAM_WRITEABLE(s) < 1)
385 return 1;
386
387 stream_putc(s, 0); /* Put 0 as subtlvs length, filled in later */
388
bd507085
CF
389 rv = pack_items(subtlvs->context, ISIS_SUBTLV_PREFIX_SID,
390 &subtlvs->prefix_sids, s, NULL, NULL, NULL, NULL);
391 if (rv)
392 return rv;
393
7ef5fefc
CF
394 rv = pack_subtlv_ipv6_source_prefix(subtlvs->source_prefix, s);
395 if (rv)
396 return rv;
397
398 size_t subtlv_len = stream_get_endp(s) - subtlv_len_pos - 1;
399 if (subtlv_len > 255)
400 return 1;
401
402 stream_putc_at(s, subtlv_len_pos, subtlv_len);
403 return 0;
404}
405
406static int unpack_tlvs(enum isis_tlv_context context, size_t avail_len,
407 struct stream *stream, struct sbuf *log, void *dest,
408 int indent);
409
410/* Functions related to TLVs 1 Area Addresses */
411
412static struct isis_item *copy_item_area_address(struct isis_item *i)
413{
414 struct isis_area_address *addr = (struct isis_area_address *)i;
841791b6 415 struct isis_area_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
416
417 rv->len = addr->len;
418 memcpy(rv->addr, addr->addr, addr->len);
419 return (struct isis_item *)rv;
420}
421
422static void format_item_area_address(uint16_t mtid, struct isis_item *i,
423 struct sbuf *buf, int indent)
424{
425 struct isis_area_address *addr = (struct isis_area_address *)i;
426
427 sbuf_push(buf, indent, "Area Address: %s\n",
428 isonet_print(addr->addr, addr->len));
429}
430
431static void free_item_area_address(struct isis_item *i)
432{
841791b6 433 XFREE(MTYPE_ISIS_TLV, i);
7ef5fefc
CF
434}
435
436static int pack_item_area_address(struct isis_item *i, struct stream *s)
437{
438 struct isis_area_address *addr = (struct isis_area_address *)i;
439
440 if (STREAM_WRITEABLE(s) < (unsigned)1 + addr->len)
441 return 1;
442 stream_putc(s, addr->len);
443 stream_put(s, addr->addr, addr->len);
444 return 0;
445}
446
447static int unpack_item_area_address(uint16_t mtid, uint8_t len,
448 struct stream *s, struct sbuf *log,
449 void *dest, int indent)
450{
451 struct isis_tlvs *tlvs = dest;
452 struct isis_area_address *rv = NULL;
453
454 sbuf_push(log, indent, "Unpack area address...\n");
455 if (len < 1) {
456 sbuf_push(
457 log, indent,
458 "Not enough data left. (Expected 1 byte of address length, got %" PRIu8
459 ")\n",
460 len);
461 goto out;
462 }
463
841791b6 464 rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
465 rv->len = stream_getc(s);
466
467 if (len < 1 + rv->len) {
468 sbuf_push(log, indent, "Not enough data left. (Expected %" PRIu8
469 " bytes of address, got %" PRIu8 ")\n",
470 rv->len, len - 1);
471 goto out;
472 }
473
474 if (rv->len < 1 || rv->len > 20) {
475 sbuf_push(log, indent,
476 "Implausible area address length %" PRIu8 "\n",
477 rv->len);
478 goto out;
479 }
480
481 stream_get(rv->addr, s, rv->len);
482
483 format_item_area_address(ISIS_MT_IPV4_UNICAST, (struct isis_item *)rv,
484 log, indent + 2);
485 append_item(&tlvs->area_addresses, (struct isis_item *)rv);
486 return 0;
487out:
841791b6 488 XFREE(MTYPE_ISIS_TLV, rv);
7ef5fefc
CF
489 return 1;
490}
491
492/* Functions related to TLV 2 (Old-Style) IS Reach */
493static struct isis_item *copy_item_oldstyle_reach(struct isis_item *i)
494{
495 struct isis_oldstyle_reach *r = (struct isis_oldstyle_reach *)i;
841791b6 496 struct isis_oldstyle_reach *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
497
498 memcpy(rv->id, r->id, 7);
499 rv->metric = r->metric;
500 return (struct isis_item *)rv;
501}
502
503static void format_item_oldstyle_reach(uint16_t mtid, struct isis_item *i,
504 struct sbuf *buf, int indent)
505{
506 struct isis_oldstyle_reach *r = (struct isis_oldstyle_reach *)i;
507
508 sbuf_push(buf, indent, "IS Reachability: %s (Metric: %" PRIu8 ")\n",
509 isis_format_id(r->id, 7), r->metric);
510}
511
512static void free_item_oldstyle_reach(struct isis_item *i)
513{
841791b6 514 XFREE(MTYPE_ISIS_TLV, i);
7ef5fefc
CF
515}
516
517static int pack_item_oldstyle_reach(struct isis_item *i, struct stream *s)
518{
519 struct isis_oldstyle_reach *r = (struct isis_oldstyle_reach *)i;
520
521 if (STREAM_WRITEABLE(s) < 11)
522 return 1;
523
524 stream_putc(s, r->metric);
525 stream_putc(s, 0x80); /* delay metric - unsupported */
526 stream_putc(s, 0x80); /* expense metric - unsupported */
527 stream_putc(s, 0x80); /* error metric - unsupported */
528 stream_put(s, r->id, 7);
529
530 return 0;
531}
532
533static int unpack_item_oldstyle_reach(uint16_t mtid, uint8_t len,
534 struct stream *s, struct sbuf *log,
535 void *dest, int indent)
536{
537 struct isis_tlvs *tlvs = dest;
538
539 sbuf_push(log, indent, "Unpack oldstyle reach...\n");
540 if (len < 11) {
541 sbuf_push(
542 log, indent,
543 "Not enough data left.(Expected 11 bytes of reach information, got %" PRIu8
544 ")\n",
545 len);
546 return 1;
547 }
548
841791b6 549 struct isis_oldstyle_reach *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
550 rv->metric = stream_getc(s);
551 if ((rv->metric & 0x3f) != rv->metric) {
552 sbuf_push(log, indent, "Metric has unplausible format\n");
553 rv->metric &= 0x3f;
554 }
555 stream_forward_getp(s, 3); /* Skip other metrics */
556 stream_get(rv->id, s, 7);
557
558 format_item_oldstyle_reach(mtid, (struct isis_item *)rv, log,
559 indent + 2);
560 append_item(&tlvs->oldstyle_reach, (struct isis_item *)rv);
561 return 0;
562}
563
564/* Functions related to TLV 6 LAN Neighbors */
565static struct isis_item *copy_item_lan_neighbor(struct isis_item *i)
566{
567 struct isis_lan_neighbor *n = (struct isis_lan_neighbor *)i;
841791b6 568 struct isis_lan_neighbor *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
569
570 memcpy(rv->mac, n->mac, 6);
571 return (struct isis_item *)rv;
572}
573
574static void format_item_lan_neighbor(uint16_t mtid, struct isis_item *i,
575 struct sbuf *buf, int indent)
576{
577 struct isis_lan_neighbor *n = (struct isis_lan_neighbor *)i;
578
579 sbuf_push(buf, indent, "LAN Neighbor: %s\n", isis_format_id(n->mac, 6));
580}
581
582static void free_item_lan_neighbor(struct isis_item *i)
583{
841791b6 584 XFREE(MTYPE_ISIS_TLV, i);
7ef5fefc
CF
585}
586
587static int pack_item_lan_neighbor(struct isis_item *i, struct stream *s)
588{
589 struct isis_lan_neighbor *n = (struct isis_lan_neighbor *)i;
590
591 if (STREAM_WRITEABLE(s) < 6)
592 return 1;
593
594 stream_put(s, n->mac, 6);
595
596 return 0;
597}
598
599static int unpack_item_lan_neighbor(uint16_t mtid, uint8_t len,
600 struct stream *s, struct sbuf *log,
601 void *dest, int indent)
602{
603 struct isis_tlvs *tlvs = dest;
604
605 sbuf_push(log, indent, "Unpack LAN neighbor...\n");
606 if (len < 6) {
607 sbuf_push(
608 log, indent,
609 "Not enough data left.(Expected 6 bytes of mac, got %" PRIu8
610 ")\n",
611 len);
612 return 1;
613 }
614
841791b6 615 struct isis_lan_neighbor *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
616 stream_get(rv->mac, s, 6);
617
618 format_item_lan_neighbor(mtid, (struct isis_item *)rv, log, indent + 2);
619 append_item(&tlvs->lan_neighbor, (struct isis_item *)rv);
620 return 0;
621}
622
623/* Functions related to TLV 9 LSP Entry */
624static struct isis_item *copy_item_lsp_entry(struct isis_item *i)
625{
626 struct isis_lsp_entry *e = (struct isis_lsp_entry *)i;
841791b6 627 struct isis_lsp_entry *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
628
629 rv->rem_lifetime = e->rem_lifetime;
630 memcpy(rv->id, e->id, sizeof(rv->id));
631 rv->seqno = e->seqno;
632 rv->checksum = e->checksum;
633
634 return (struct isis_item *)rv;
635}
636
637static void format_item_lsp_entry(uint16_t mtid, struct isis_item *i,
638 struct sbuf *buf, int indent)
639{
640 struct isis_lsp_entry *e = (struct isis_lsp_entry *)i;
641
996c9314
LB
642 sbuf_push(buf, indent,
643 "LSP Entry: %s, seq 0x%08" PRIx32 ", cksum 0x%04" PRIx16
644 ", lifetime %" PRIu16 "s\n",
7ef5fefc
CF
645 isis_format_id(e->id, 8), e->seqno, e->checksum,
646 e->rem_lifetime);
647}
648
649static void free_item_lsp_entry(struct isis_item *i)
650{
841791b6 651 XFREE(MTYPE_ISIS_TLV, i);
7ef5fefc
CF
652}
653
654static int pack_item_lsp_entry(struct isis_item *i, struct stream *s)
655{
656 struct isis_lsp_entry *e = (struct isis_lsp_entry *)i;
657
658 if (STREAM_WRITEABLE(s) < 16)
659 return 1;
660
661 stream_putw(s, e->rem_lifetime);
662 stream_put(s, e->id, 8);
663 stream_putl(s, e->seqno);
664 stream_putw(s, e->checksum);
665
666 return 0;
667}
668
669static int unpack_item_lsp_entry(uint16_t mtid, uint8_t len, struct stream *s,
670 struct sbuf *log, void *dest, int indent)
671{
672 struct isis_tlvs *tlvs = dest;
673
674 sbuf_push(log, indent, "Unpack LSP entry...\n");
675 if (len < 16) {
676 sbuf_push(
677 log, indent,
678 "Not enough data left. (Expected 16 bytes of LSP info, got %" PRIu8,
679 len);
680 return 1;
681 }
682
841791b6 683 struct isis_lsp_entry *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
684 rv->rem_lifetime = stream_getw(s);
685 stream_get(rv->id, s, 8);
686 rv->seqno = stream_getl(s);
687 rv->checksum = stream_getw(s);
688
689 format_item_lsp_entry(mtid, (struct isis_item *)rv, log, indent + 2);
690 append_item(&tlvs->lsp_entries, (struct isis_item *)rv);
691 return 0;
692}
693
694/* Functions related to TLVs 22/222 Extended Reach/MT Reach */
695
696static struct isis_item *copy_item_extended_reach(struct isis_item *i)
697{
698 struct isis_extended_reach *r = (struct isis_extended_reach *)i;
841791b6 699 struct isis_extended_reach *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
700
701 memcpy(rv->id, r->id, 7);
702 rv->metric = r->metric;
703
704 if (r->subtlvs && r->subtlv_len) {
841791b6 705 rv->subtlvs = XCALLOC(MTYPE_ISIS_TLV, r->subtlv_len);
7ef5fefc
CF
706 memcpy(rv->subtlvs, r->subtlvs, r->subtlv_len);
707 rv->subtlv_len = r->subtlv_len;
708 }
709
710 return (struct isis_item *)rv;
711}
712
713static void format_item_extended_reach(uint16_t mtid, struct isis_item *i,
714 struct sbuf *buf, int indent)
715{
716 struct isis_extended_reach *r = (struct isis_extended_reach *)i;
717
718 sbuf_push(buf, indent, "%s Reachability: %s (Metric: %u)",
719 (mtid == ISIS_MT_IPV4_UNICAST) ? "Extended" : "MT",
720 isis_format_id(r->id, 7), r->metric);
721 if (mtid != ISIS_MT_IPV4_UNICAST)
722 sbuf_push(buf, 0, " %s", isis_mtid2str(mtid));
723 sbuf_push(buf, 0, "\n");
724
725 if (r->subtlv_len && r->subtlvs)
996c9314
LB
726 mpls_te_print_detail(buf, indent + 2, r->subtlvs,
727 r->subtlv_len);
7ef5fefc
CF
728}
729
730static void free_item_extended_reach(struct isis_item *i)
731{
732 struct isis_extended_reach *item = (struct isis_extended_reach *)i;
841791b6
CF
733 XFREE(MTYPE_ISIS_TLV, item->subtlvs);
734 XFREE(MTYPE_ISIS_TLV, item);
7ef5fefc
CF
735}
736
737static int pack_item_extended_reach(struct isis_item *i, struct stream *s)
738{
739 struct isis_extended_reach *r = (struct isis_extended_reach *)i;
740
741 if (STREAM_WRITEABLE(s) < 11 + (unsigned)r->subtlv_len)
742 return 1;
743 stream_put(s, r->id, sizeof(r->id));
744 stream_put3(s, r->metric);
745 stream_putc(s, r->subtlv_len);
746 stream_put(s, r->subtlvs, r->subtlv_len);
747 return 0;
748}
749
750static int unpack_item_extended_reach(uint16_t mtid, uint8_t len,
751 struct stream *s, struct sbuf *log,
752 void *dest, int indent)
753{
754 struct isis_tlvs *tlvs = dest;
755 struct isis_extended_reach *rv = NULL;
756 uint8_t subtlv_len;
757 struct isis_item_list *items;
758
759 if (mtid == ISIS_MT_IPV4_UNICAST) {
760 items = &tlvs->extended_reach;
761 } else {
762 items = isis_get_mt_items(&tlvs->mt_reach, mtid);
763 }
764
765 sbuf_push(log, indent, "Unpacking %s reachability...\n",
766 (mtid == ISIS_MT_IPV4_UNICAST) ? "extended" : "mt");
767
768 if (len < 11) {
98c5bc15
CF
769 sbuf_push(log, indent,
770 "Not enough data left. (expected 11 or more bytes, got %"
771 PRIu8 ")\n",
772 len);
7ef5fefc
CF
773 goto out;
774 }
775
841791b6 776 rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
777 stream_get(rv->id, s, 7);
778 rv->metric = stream_get3(s);
779 subtlv_len = stream_getc(s);
780
781 format_item_extended_reach(mtid, (struct isis_item *)rv, log,
782 indent + 2);
783
784 if ((size_t)len < ((size_t)11) + subtlv_len) {
785 sbuf_push(log, indent,
786 "Not enough data left for subtlv size %" PRIu8
787 ", there are only %" PRIu8 " bytes left.\n",
788 subtlv_len, len - 11);
789 goto out;
790 }
791
792 sbuf_push(log, indent, "Storing %" PRIu8 " bytes of subtlvs\n",
793 subtlv_len);
794
795 if (subtlv_len) {
796 size_t subtlv_start = stream_get_getp(s);
797
798 if (unpack_tlvs(ISIS_CONTEXT_SUBTLV_NE_REACH, subtlv_len, s,
799 log, NULL, indent + 4)) {
800 goto out;
801 }
802
803 stream_set_getp(s, subtlv_start);
804
841791b6 805 rv->subtlvs = XCALLOC(MTYPE_ISIS_TLV, subtlv_len);
7ef5fefc
CF
806 stream_get(rv->subtlvs, s, subtlv_len);
807 rv->subtlv_len = subtlv_len;
808 }
809
810 append_item(items, (struct isis_item *)rv);
811 return 0;
812out:
813 if (rv)
814 free_item_extended_reach((struct isis_item *)rv);
815
816 return 1;
817}
818
819/* Functions related to TLV 128 (Old-Style) IP Reach */
820static struct isis_item *copy_item_oldstyle_ip_reach(struct isis_item *i)
821{
822 struct isis_oldstyle_ip_reach *r = (struct isis_oldstyle_ip_reach *)i;
823 struct isis_oldstyle_ip_reach *rv =
841791b6 824 XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
825
826 rv->metric = r->metric;
827 rv->prefix = r->prefix;
828 return (struct isis_item *)rv;
829}
830
831static void format_item_oldstyle_ip_reach(uint16_t mtid, struct isis_item *i,
832 struct sbuf *buf, int indent)
833{
834 struct isis_oldstyle_ip_reach *r = (struct isis_oldstyle_ip_reach *)i;
835 char prefixbuf[PREFIX2STR_BUFFER];
836
837 sbuf_push(buf, indent, "IP Reachability: %s (Metric: %" PRIu8 ")\n",
996c9314
LB
838 prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf)),
839 r->metric);
7ef5fefc
CF
840}
841
842static void free_item_oldstyle_ip_reach(struct isis_item *i)
843{
841791b6 844 XFREE(MTYPE_ISIS_TLV, i);
7ef5fefc
CF
845}
846
847static int pack_item_oldstyle_ip_reach(struct isis_item *i, struct stream *s)
848{
849 struct isis_oldstyle_ip_reach *r = (struct isis_oldstyle_ip_reach *)i;
850
851 if (STREAM_WRITEABLE(s) < 12)
852 return 1;
853
854 stream_putc(s, r->metric);
855 stream_putc(s, 0x80); /* delay metric - unsupported */
856 stream_putc(s, 0x80); /* expense metric - unsupported */
857 stream_putc(s, 0x80); /* error metric - unsupported */
858 stream_put(s, &r->prefix.prefix, 4);
859
860 struct in_addr mask;
861 masklen2ip(r->prefix.prefixlen, &mask);
862 stream_put(s, &mask, sizeof(mask));
863
864 return 0;
865}
866
867static int unpack_item_oldstyle_ip_reach(uint16_t mtid, uint8_t len,
868 struct stream *s, struct sbuf *log,
869 void *dest, int indent)
870{
871 sbuf_push(log, indent, "Unpack oldstyle ip reach...\n");
872 if (len < 12) {
873 sbuf_push(
874 log, indent,
875 "Not enough data left.(Expected 12 bytes of reach information, got %" PRIu8
876 ")\n",
877 len);
878 return 1;
879 }
880
881 struct isis_oldstyle_ip_reach *rv =
841791b6 882 XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
883 rv->metric = stream_getc(s);
884 if ((rv->metric & 0x7f) != rv->metric) {
885 sbuf_push(log, indent, "Metric has unplausible format\n");
886 rv->metric &= 0x7f;
887 }
888 stream_forward_getp(s, 3); /* Skip other metrics */
889 rv->prefix.family = AF_INET;
890 stream_get(&rv->prefix.prefix, s, 4);
891
892 struct in_addr mask;
893 stream_get(&mask, s, 4);
894 rv->prefix.prefixlen = ip_masklen(mask);
895
896 format_item_oldstyle_ip_reach(mtid, (struct isis_item *)rv, log,
897 indent + 2);
898 append_item(dest, (struct isis_item *)rv);
899 return 0;
900}
901
902
903/* Functions related to TLV 129 protocols supported */
904
905static void copy_tlv_protocols_supported(struct isis_protocols_supported *src,
906 struct isis_protocols_supported *dest)
907{
908 if (!src->protocols || !src->count)
909 return;
910 dest->count = src->count;
841791b6 911 dest->protocols = XCALLOC(MTYPE_ISIS_TLV, src->count);
7ef5fefc
CF
912 memcpy(dest->protocols, src->protocols, src->count);
913}
914
915static void format_tlv_protocols_supported(struct isis_protocols_supported *p,
916 struct sbuf *buf, int indent)
917{
918 if (!p || !p->count || !p->protocols)
919 return;
920
921 sbuf_push(buf, indent, "Protocols Supported: ");
922 for (uint8_t i = 0; i < p->count; i++) {
923 sbuf_push(buf, 0, "%s%s", nlpid2str(p->protocols[i]),
924 (i + 1 < p->count) ? ", " : "");
925 }
926 sbuf_push(buf, 0, "\n");
927}
928
929static void free_tlv_protocols_supported(struct isis_protocols_supported *p)
930{
841791b6 931 XFREE(MTYPE_ISIS_TLV, p->protocols);
7ef5fefc
CF
932}
933
934static int pack_tlv_protocols_supported(struct isis_protocols_supported *p,
935 struct stream *s)
936{
937 if (!p || !p->count || !p->protocols)
938 return 0;
939
940 if (STREAM_WRITEABLE(s) < (unsigned)(p->count + 2))
941 return 1;
942
943 stream_putc(s, ISIS_TLV_PROTOCOLS_SUPPORTED);
944 stream_putc(s, p->count);
945 stream_put(s, p->protocols, p->count);
946 return 0;
947}
948
949static int unpack_tlv_protocols_supported(enum isis_tlv_context context,
950 uint8_t tlv_type, uint8_t tlv_len,
951 struct stream *s, struct sbuf *log,
952 void *dest, int indent)
953{
954 struct isis_tlvs *tlvs = dest;
955
956 sbuf_push(log, indent, "Unpacking Protocols Supported TLV...\n");
957 if (!tlv_len) {
958 sbuf_push(log, indent, "WARNING: No protocols included\n");
959 return 0;
960 }
961 if (tlvs->protocols_supported.protocols) {
962 sbuf_push(
963 log, indent,
964 "WARNING: protocols supported TLV present multiple times.\n");
965 stream_forward_getp(s, tlv_len);
966 return 0;
967 }
968
969 tlvs->protocols_supported.count = tlv_len;
841791b6 970 tlvs->protocols_supported.protocols = XCALLOC(MTYPE_ISIS_TLV, tlv_len);
7ef5fefc
CF
971 stream_get(tlvs->protocols_supported.protocols, s, tlv_len);
972
973 format_tlv_protocols_supported(&tlvs->protocols_supported, log,
974 indent + 2);
975 return 0;
976}
977
978/* Functions related to TLV 132 IPv4 Interface addresses */
979static struct isis_item *copy_item_ipv4_address(struct isis_item *i)
980{
981 struct isis_ipv4_address *a = (struct isis_ipv4_address *)i;
841791b6 982 struct isis_ipv4_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
983
984 rv->addr = a->addr;
985 return (struct isis_item *)rv;
986}
987
988static void format_item_ipv4_address(uint16_t mtid, struct isis_item *i,
989 struct sbuf *buf, int indent)
990{
991 struct isis_ipv4_address *a = (struct isis_ipv4_address *)i;
992 char addrbuf[INET_ADDRSTRLEN];
993
994 inet_ntop(AF_INET, &a->addr, addrbuf, sizeof(addrbuf));
995 sbuf_push(buf, indent, "IPv4 Interface Address: %s\n", addrbuf);
996}
997
998static void free_item_ipv4_address(struct isis_item *i)
999{
841791b6 1000 XFREE(MTYPE_ISIS_TLV, i);
7ef5fefc
CF
1001}
1002
1003static int pack_item_ipv4_address(struct isis_item *i, struct stream *s)
1004{
1005 struct isis_ipv4_address *a = (struct isis_ipv4_address *)i;
1006
1007 if (STREAM_WRITEABLE(s) < 4)
1008 return 1;
1009
1010 stream_put(s, &a->addr, 4);
1011
1012 return 0;
1013}
1014
1015static int unpack_item_ipv4_address(uint16_t mtid, uint8_t len,
1016 struct stream *s, struct sbuf *log,
1017 void *dest, int indent)
1018{
1019 struct isis_tlvs *tlvs = dest;
1020
1021 sbuf_push(log, indent, "Unpack IPv4 Interface address...\n");
1022 if (len < 4) {
1023 sbuf_push(
1024 log, indent,
1025 "Not enough data left.(Expected 4 bytes of IPv4 address, got %" PRIu8
1026 ")\n",
1027 len);
1028 return 1;
1029 }
1030
841791b6 1031 struct isis_ipv4_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1032 stream_get(&rv->addr, s, 4);
1033
1034 format_item_ipv4_address(mtid, (struct isis_item *)rv, log, indent + 2);
1035 append_item(&tlvs->ipv4_address, (struct isis_item *)rv);
1036 return 0;
1037}
1038
1039
1040/* Functions related to TLV 232 IPv6 Interface addresses */
1041static struct isis_item *copy_item_ipv6_address(struct isis_item *i)
1042{
1043 struct isis_ipv6_address *a = (struct isis_ipv6_address *)i;
841791b6 1044 struct isis_ipv6_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1045
1046 rv->addr = a->addr;
1047 return (struct isis_item *)rv;
1048}
1049
1050static void format_item_ipv6_address(uint16_t mtid, struct isis_item *i,
1051 struct sbuf *buf, int indent)
1052{
1053 struct isis_ipv6_address *a = (struct isis_ipv6_address *)i;
1054 char addrbuf[INET6_ADDRSTRLEN];
1055
1056 inet_ntop(AF_INET6, &a->addr, addrbuf, sizeof(addrbuf));
1057 sbuf_push(buf, indent, "IPv6 Interface Address: %s\n", addrbuf);
1058}
1059
1060static void free_item_ipv6_address(struct isis_item *i)
1061{
841791b6 1062 XFREE(MTYPE_ISIS_TLV, i);
7ef5fefc
CF
1063}
1064
1065static int pack_item_ipv6_address(struct isis_item *i, struct stream *s)
1066{
1067 struct isis_ipv6_address *a = (struct isis_ipv6_address *)i;
1068
1069 if (STREAM_WRITEABLE(s) < 16)
1070 return 1;
1071
1072 stream_put(s, &a->addr, 16);
1073
1074 return 0;
1075}
1076
1077static int unpack_item_ipv6_address(uint16_t mtid, uint8_t len,
1078 struct stream *s, struct sbuf *log,
1079 void *dest, int indent)
1080{
1081 struct isis_tlvs *tlvs = dest;
1082
1083 sbuf_push(log, indent, "Unpack IPv6 Interface address...\n");
1084 if (len < 16) {
1085 sbuf_push(
1086 log, indent,
1087 "Not enough data left.(Expected 16 bytes of IPv6 address, got %" PRIu8
1088 ")\n",
1089 len);
1090 return 1;
1091 }
1092
841791b6 1093 struct isis_ipv6_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1094 stream_get(&rv->addr, s, 16);
1095
1096 format_item_ipv6_address(mtid, (struct isis_item *)rv, log, indent + 2);
1097 append_item(&tlvs->ipv6_address, (struct isis_item *)rv);
1098 return 0;
1099}
1100
1101
1102/* Functions related to TLV 229 MT Router information */
1103static struct isis_item *copy_item_mt_router_info(struct isis_item *i)
1104{
1105 struct isis_mt_router_info *info = (struct isis_mt_router_info *)i;
841791b6 1106 struct isis_mt_router_info *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1107
1108 rv->overload = info->overload;
1109 rv->attached = info->attached;
1110 rv->mtid = info->mtid;
1111 return (struct isis_item *)rv;
1112}
1113
1114static void format_item_mt_router_info(uint16_t mtid, struct isis_item *i,
1115 struct sbuf *buf, int indent)
1116{
1117 struct isis_mt_router_info *info = (struct isis_mt_router_info *)i;
1118
1119 sbuf_push(buf, indent, "MT Router Info: %s%s%s\n",
98c5bc15
CF
1120 isis_mtid2str(info->mtid),
1121 info->overload ? " Overload" : "",
7ef5fefc
CF
1122 info->attached ? " Attached" : "");
1123}
1124
1125static void free_item_mt_router_info(struct isis_item *i)
1126{
841791b6 1127 XFREE(MTYPE_ISIS_TLV, i);
7ef5fefc
CF
1128}
1129
1130static int pack_item_mt_router_info(struct isis_item *i, struct stream *s)
1131{
1132 struct isis_mt_router_info *info = (struct isis_mt_router_info *)i;
1133
1134 if (STREAM_WRITEABLE(s) < 2)
1135 return 1;
1136
1137 uint16_t entry = info->mtid;
1138
1139 if (info->overload)
1140 entry |= ISIS_MT_OL_MASK;
1141 if (info->attached)
1142 entry |= ISIS_MT_AT_MASK;
1143
1144 stream_putw(s, entry);
1145
1146 return 0;
1147}
1148
1149static int unpack_item_mt_router_info(uint16_t mtid, uint8_t len,
1150 struct stream *s, struct sbuf *log,
1151 void *dest, int indent)
1152{
1153 struct isis_tlvs *tlvs = dest;
1154
1155 sbuf_push(log, indent, "Unpack MT Router info...\n");
1156 if (len < 2) {
1157 sbuf_push(
1158 log, indent,
1159 "Not enough data left.(Expected 2 bytes of MT info, got %" PRIu8
1160 ")\n",
1161 len);
1162 return 1;
1163 }
1164
841791b6 1165 struct isis_mt_router_info *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1166
1167 uint16_t entry = stream_getw(s);
1168 rv->overload = entry & ISIS_MT_OL_MASK;
1169 rv->attached = entry & ISIS_MT_AT_MASK;
1170 rv->mtid = entry & ISIS_MT_MASK;
1171
1172 format_item_mt_router_info(mtid, (struct isis_item *)rv, log,
1173 indent + 2);
1174 append_item(&tlvs->mt_router_info, (struct isis_item *)rv);
1175 return 0;
1176}
1177
1178/* Functions related to TLV 134 TE Router ID */
1179
1180static struct in_addr *copy_tlv_te_router_id(const struct in_addr *id)
1181{
1182 if (!id)
1183 return NULL;
1184
841791b6 1185 struct in_addr *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1186 memcpy(rv, id, sizeof(*rv));
1187 return rv;
1188}
1189
1190static void format_tlv_te_router_id(const struct in_addr *id, struct sbuf *buf,
1191 int indent)
1192{
1193 if (!id)
1194 return;
1195
1196 char addrbuf[INET_ADDRSTRLEN];
1197 inet_ntop(AF_INET, id, addrbuf, sizeof(addrbuf));
1198 sbuf_push(buf, indent, "TE Router ID: %s\n", addrbuf);
1199}
1200
1201static void free_tlv_te_router_id(struct in_addr *id)
1202{
841791b6 1203 XFREE(MTYPE_ISIS_TLV, id);
7ef5fefc
CF
1204}
1205
1206static int pack_tlv_te_router_id(const struct in_addr *id, struct stream *s)
1207{
1208 if (!id)
1209 return 0;
1210
1211 if (STREAM_WRITEABLE(s) < (unsigned)(2 + sizeof(*id)))
1212 return 1;
1213
1214 stream_putc(s, ISIS_TLV_TE_ROUTER_ID);
1215 stream_putc(s, 4);
1216 stream_put(s, id, 4);
1217 return 0;
1218}
1219
1220static int unpack_tlv_te_router_id(enum isis_tlv_context context,
1221 uint8_t tlv_type, uint8_t tlv_len,
1222 struct stream *s, struct sbuf *log,
1223 void *dest, int indent)
1224{
1225 struct isis_tlvs *tlvs = dest;
1226
1227 sbuf_push(log, indent, "Unpacking TE Router ID TLV...\n");
1228 if (tlv_len != 4) {
1229 sbuf_push(log, indent, "WARNING: Length invalid\n");
1230 return 1;
1231 }
1232
1233 if (tlvs->te_router_id) {
1234 sbuf_push(log, indent,
1235 "WARNING: TE Router ID present multiple times.\n");
1236 stream_forward_getp(s, tlv_len);
1237 return 0;
1238 }
1239
841791b6 1240 tlvs->te_router_id = XCALLOC(MTYPE_ISIS_TLV, 4);
7ef5fefc
CF
1241 stream_get(tlvs->te_router_id, s, 4);
1242 format_tlv_te_router_id(tlvs->te_router_id, log, indent + 2);
1243 return 0;
1244}
1245
1246
1247/* Functions related to TLVs 135/235 extended IP reach/MT IP Reach */
1248
1249static struct isis_item *copy_item_extended_ip_reach(struct isis_item *i)
1250{
1251 struct isis_extended_ip_reach *r = (struct isis_extended_ip_reach *)i;
1252 struct isis_extended_ip_reach *rv =
841791b6 1253 XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1254
1255 rv->metric = r->metric;
1256 rv->down = r->down;
1257 rv->prefix = r->prefix;
1258
1259 return (struct isis_item *)rv;
1260}
1261
1262static void format_item_extended_ip_reach(uint16_t mtid, struct isis_item *i,
1263 struct sbuf *buf, int indent)
1264{
1265 struct isis_extended_ip_reach *r = (struct isis_extended_ip_reach *)i;
1266 char prefixbuf[PREFIX2STR_BUFFER];
1267
1268 sbuf_push(buf, indent, "%s IP Reachability: %s (Metric: %u)%s",
1269 (mtid == ISIS_MT_IPV4_UNICAST) ? "Extended" : "MT",
98c5bc15
CF
1270 prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf)), r->metric,
1271 r->down ? " Down" : "");
7ef5fefc
CF
1272 if (mtid != ISIS_MT_IPV4_UNICAST)
1273 sbuf_push(buf, 0, " %s", isis_mtid2str(mtid));
1274 sbuf_push(buf, 0, "\n");
9826647e
RW
1275
1276 if (r->subtlvs) {
1277 sbuf_push(buf, indent, " Subtlvs:\n");
1278 format_subtlvs(r->subtlvs, buf, indent + 4);
1279 }
7ef5fefc
CF
1280}
1281
1282static void free_item_extended_ip_reach(struct isis_item *i)
1283{
1284 struct isis_extended_ip_reach *item =
1285 (struct isis_extended_ip_reach *)i;
bd507085 1286 isis_free_subtlvs(item->subtlvs);
841791b6 1287 XFREE(MTYPE_ISIS_TLV, item);
7ef5fefc
CF
1288}
1289
1290static int pack_item_extended_ip_reach(struct isis_item *i, struct stream *s)
1291{
1292 struct isis_extended_ip_reach *r = (struct isis_extended_ip_reach *)i;
1293 uint8_t control;
1294
1295 if (STREAM_WRITEABLE(s) < 5)
1296 return 1;
1297 stream_putl(s, r->metric);
1298
1299 control = r->down ? ISIS_EXTENDED_IP_REACH_DOWN : 0;
1300 control |= r->prefix.prefixlen;
bd507085
CF
1301 control |= r->subtlvs ? ISIS_EXTENDED_IP_REACH_SUBTLV : 0;
1302
7ef5fefc
CF
1303 stream_putc(s, control);
1304
1305 if (STREAM_WRITEABLE(s) < (unsigned)PSIZE(r->prefix.prefixlen))
1306 return 1;
1307 stream_put(s, &r->prefix.prefix.s_addr, PSIZE(r->prefix.prefixlen));
bd507085
CF
1308
1309 if (r->subtlvs)
1310 return pack_subtlvs(r->subtlvs, s);
7ef5fefc
CF
1311 return 0;
1312}
1313
1314static int unpack_item_extended_ip_reach(uint16_t mtid, uint8_t len,
1315 struct stream *s, struct sbuf *log,
1316 void *dest, int indent)
1317{
1318 struct isis_tlvs *tlvs = dest;
1319 struct isis_extended_ip_reach *rv = NULL;
1320 size_t consume;
1321 uint8_t control, subtlv_len;
1322 struct isis_item_list *items;
1323
1324 if (mtid == ISIS_MT_IPV4_UNICAST) {
1325 items = &tlvs->extended_ip_reach;
1326 } else {
1327 items = isis_get_mt_items(&tlvs->mt_ip_reach, mtid);
1328 }
1329
1330 sbuf_push(log, indent, "Unpacking %s IPv4 reachability...\n",
1331 (mtid == ISIS_MT_IPV4_UNICAST) ? "extended" : "mt");
1332
1333 consume = 5;
1334 if (len < consume) {
98c5bc15
CF
1335 sbuf_push(log, indent,
1336 "Not enough data left. (expected 5 or more bytes, got %" PRIu8 ")\n",
1337 len);
7ef5fefc
CF
1338 goto out;
1339 }
1340
841791b6 1341 rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1342
1343 rv->metric = stream_getl(s);
1344 control = stream_getc(s);
1345 rv->down = (control & ISIS_EXTENDED_IP_REACH_DOWN);
1346 rv->prefix.family = AF_INET;
1347 rv->prefix.prefixlen = control & 0x3f;
1348 if (rv->prefix.prefixlen > 32) {
1349 sbuf_push(log, indent, "Prefixlen %u is inplausible for IPv4\n",
1350 rv->prefix.prefixlen);
1351 goto out;
1352 }
1353
1354 consume += PSIZE(rv->prefix.prefixlen);
1355 if (len < consume) {
98c5bc15
CF
1356 sbuf_push(log, indent,
1357 "Expected %u bytes of prefix, but only %u bytes available.\n",
1358 PSIZE(rv->prefix.prefixlen), len - 5);
7ef5fefc
CF
1359 goto out;
1360 }
1361 stream_get(&rv->prefix.prefix.s_addr, s, PSIZE(rv->prefix.prefixlen));
1362 in_addr_t orig_prefix = rv->prefix.prefix.s_addr;
1363 apply_mask_ipv4(&rv->prefix);
1364 if (orig_prefix != rv->prefix.prefix.s_addr)
1365 sbuf_push(log, indent + 2,
1366 "WARNING: Prefix had hostbits set.\n");
1367 format_item_extended_ip_reach(mtid, (struct isis_item *)rv, log,
1368 indent + 2);
1369
1370 if (control & ISIS_EXTENDED_IP_REACH_SUBTLV) {
1371 consume += 1;
1372 if (len < consume) {
98c5bc15
CF
1373 sbuf_push(log, indent,
1374 "Expected 1 byte of subtlv len, but no more data present.\n");
7ef5fefc
CF
1375 goto out;
1376 }
1377 subtlv_len = stream_getc(s);
1378
1379 if (!subtlv_len) {
98c5bc15
CF
1380 sbuf_push(log, indent + 2,
1381 " WARNING: subtlv bit is set, but there are no subtlvs.\n");
7ef5fefc
CF
1382 }
1383 consume += subtlv_len;
1384 if (len < consume) {
98c5bc15
CF
1385 sbuf_push(log, indent,
1386 "Expected %" PRIu8
1387 " bytes of subtlvs, but only %u bytes available.\n",
1388 subtlv_len,
1389 len - 6 - PSIZE(rv->prefix.prefixlen));
7ef5fefc
CF
1390 goto out;
1391 }
bd507085
CF
1392
1393 rv->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IP_REACH);
1394 if (unpack_tlvs(ISIS_CONTEXT_SUBTLV_IP_REACH, subtlv_len, s,
1395 log, rv->subtlvs, indent + 4)) {
1396 goto out;
1397 }
7ef5fefc
CF
1398 }
1399
1400 append_item(items, (struct isis_item *)rv);
1401 return 0;
1402out:
1403 if (rv)
1404 free_item_extended_ip_reach((struct isis_item *)rv);
1405 return 1;
1406}
1407
1408/* Functions related to TLV 137 Dynamic Hostname */
1409
1410static char *copy_tlv_dynamic_hostname(const char *hostname)
1411{
1412 if (!hostname)
1413 return NULL;
1414
841791b6 1415 return XSTRDUP(MTYPE_ISIS_TLV, hostname);
7ef5fefc
CF
1416}
1417
1418static void format_tlv_dynamic_hostname(const char *hostname, struct sbuf *buf,
1419 int indent)
1420{
1421 if (!hostname)
1422 return;
1423
1424 sbuf_push(buf, indent, "Hostname: %s\n", hostname);
1425}
1426
1427static void free_tlv_dynamic_hostname(char *hostname)
1428{
841791b6 1429 XFREE(MTYPE_ISIS_TLV, hostname);
7ef5fefc
CF
1430}
1431
1432static int pack_tlv_dynamic_hostname(const char *hostname, struct stream *s)
1433{
1434 if (!hostname)
1435 return 0;
1436
1437 uint8_t name_len = strlen(hostname);
1438
1439 if (STREAM_WRITEABLE(s) < (unsigned)(2 + name_len))
1440 return 1;
1441
1442 stream_putc(s, ISIS_TLV_DYNAMIC_HOSTNAME);
1443 stream_putc(s, name_len);
1444 stream_put(s, hostname, name_len);
1445 return 0;
1446}
1447
1448static int unpack_tlv_dynamic_hostname(enum isis_tlv_context context,
1449 uint8_t tlv_type, uint8_t tlv_len,
1450 struct stream *s, struct sbuf *log,
1451 void *dest, int indent)
1452{
1453 struct isis_tlvs *tlvs = dest;
1454
1455 sbuf_push(log, indent, "Unpacking Dynamic Hostname TLV...\n");
1456 if (!tlv_len) {
1457 sbuf_push(log, indent, "WARNING: No hostname included\n");
1458 return 0;
1459 }
1460
1461 if (tlvs->hostname) {
1462 sbuf_push(log, indent,
1463 "WARNING: Hostname present multiple times.\n");
1464 stream_forward_getp(s, tlv_len);
1465 return 0;
1466 }
1467
841791b6 1468 tlvs->hostname = XCALLOC(MTYPE_ISIS_TLV, tlv_len + 1);
7ef5fefc
CF
1469 stream_get(tlvs->hostname, s, tlv_len);
1470 tlvs->hostname[tlv_len] = '\0';
1471
1472 bool sane = true;
1473 for (uint8_t i = 0; i < tlv_len; i++) {
1474 if ((unsigned char)tlvs->hostname[i] > 127
a7ce0ad1 1475 || !isprint((int)tlvs->hostname[i])) {
7ef5fefc
CF
1476 sane = false;
1477 tlvs->hostname[i] = '?';
1478 }
1479 }
1480 if (!sane) {
1481 sbuf_push(
1482 log, indent,
1483 "WARNING: Hostname contained non-printable/non-ascii characters.\n");
1484 }
1485
1486 return 0;
1487}
1488
41a145f1
CF
1489/* Functions related to TLV 150 Spine-Leaf-Extension */
1490
1491static struct isis_spine_leaf *copy_tlv_spine_leaf(
1492 const struct isis_spine_leaf *spine_leaf)
1493{
1494 if (!spine_leaf)
1495 return NULL;
1496
1497 struct isis_spine_leaf *rv = XMALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
1498 memcpy(rv, spine_leaf, sizeof(*rv));
1499
1500 return rv;
1501}
1502
1503static void format_tlv_spine_leaf(const struct isis_spine_leaf *spine_leaf,
1504 struct sbuf *buf, int indent)
1505{
1506 if (!spine_leaf)
1507 return;
1508
1509 sbuf_push(buf, indent, "Spine-Leaf-Extension:\n");
1510 if (spine_leaf->has_tier) {
1511 if (spine_leaf->tier == ISIS_TIER_UNDEFINED) {
1512 sbuf_push(buf, indent, " Tier: undefined\n");
1513 } else {
1514 sbuf_push(buf, indent, " Tier: %" PRIu8 "\n",
1515 spine_leaf->tier);
1516 }
1517 }
1518
1519 sbuf_push(buf, indent, " Flags:%s%s%s\n",
1520 spine_leaf->is_leaf ? " LEAF" : "",
1521 spine_leaf->is_spine ? " SPINE" : "",
1522 spine_leaf->is_backup ? " BACKUP" : "");
1523
1524}
1525
1526static void free_tlv_spine_leaf(struct isis_spine_leaf *spine_leaf)
1527{
1528 XFREE(MTYPE_ISIS_TLV, spine_leaf);
1529}
1530
1531#define ISIS_SPINE_LEAF_FLAG_TIER 0x08
1532#define ISIS_SPINE_LEAF_FLAG_BACKUP 0x04
1533#define ISIS_SPINE_LEAF_FLAG_SPINE 0x02
1534#define ISIS_SPINE_LEAF_FLAG_LEAF 0x01
1535
1536static int pack_tlv_spine_leaf(const struct isis_spine_leaf *spine_leaf,
1537 struct stream *s)
1538{
1539 if (!spine_leaf)
1540 return 0;
1541
1542 uint8_t tlv_len = 2;
1543
1544 if (STREAM_WRITEABLE(s) < (unsigned)(2 + tlv_len))
1545 return 1;
1546
1547 stream_putc(s, ISIS_TLV_SPINE_LEAF_EXT);
1548 stream_putc(s, tlv_len);
1549
1550 uint16_t spine_leaf_flags = 0;
1551
1552 if (spine_leaf->has_tier) {
1553 spine_leaf_flags |= ISIS_SPINE_LEAF_FLAG_TIER;
1554 spine_leaf_flags |= spine_leaf->tier << 12;
1555 }
1556
1557 if (spine_leaf->is_leaf)
1558 spine_leaf_flags |= ISIS_SPINE_LEAF_FLAG_LEAF;
1559
1560 if (spine_leaf->is_spine)
1561 spine_leaf_flags |= ISIS_SPINE_LEAF_FLAG_SPINE;
1562
1563 if (spine_leaf->is_backup)
1564 spine_leaf_flags |= ISIS_SPINE_LEAF_FLAG_BACKUP;
1565
1566 stream_putw(s, spine_leaf_flags);
1567
1568 return 0;
1569}
1570
1571static int unpack_tlv_spine_leaf(enum isis_tlv_context context,
1572 uint8_t tlv_type, uint8_t tlv_len,
1573 struct stream *s, struct sbuf *log,
1574 void *dest, int indent)
1575{
1576 struct isis_tlvs *tlvs = dest;
1577
1578 sbuf_push(log, indent, "Unpacking Spine Leaf Extension TLV...\n");
1579 if (tlv_len < 2) {
1580 sbuf_push(log, indent, "WARNING: Unexepected TLV size\n");
1581 stream_forward_getp(s, tlv_len);
1582 return 0;
1583 }
1584
1585 if (tlvs->spine_leaf) {
1586 sbuf_push(log, indent,
1587 "WARNING: Spine Leaf Extension TLV present multiple times.\n");
1588 stream_forward_getp(s, tlv_len);
1589 return 0;
1590 }
1591
1592 tlvs->spine_leaf = XCALLOC(MTYPE_ISIS_TLV, sizeof(*tlvs->spine_leaf));
1593
1594 uint16_t spine_leaf_flags = stream_getw(s);
1595
1596 if (spine_leaf_flags & ISIS_SPINE_LEAF_FLAG_TIER) {
1597 tlvs->spine_leaf->has_tier = true;
1598 tlvs->spine_leaf->tier = spine_leaf_flags >> 12;
1599 }
1600
1601 tlvs->spine_leaf->is_leaf = spine_leaf_flags & ISIS_SPINE_LEAF_FLAG_LEAF;
1602 tlvs->spine_leaf->is_spine = spine_leaf_flags & ISIS_SPINE_LEAF_FLAG_SPINE;
1603 tlvs->spine_leaf->is_backup = spine_leaf_flags & ISIS_SPINE_LEAF_FLAG_BACKUP;
1604
1605 stream_forward_getp(s, tlv_len - 2);
1606 return 0;
1607}
1608
9fe21208
CF
1609/* Functions related to TLV 240 P2P Three-Way Adjacency */
1610
1611const char *isis_threeway_state_name(enum isis_threeway_state state)
1612{
1613 switch (state) {
1614 case ISIS_THREEWAY_DOWN:
1615 return "Down";
1616 case ISIS_THREEWAY_INITIALIZING:
1617 return "Initializing";
1618 case ISIS_THREEWAY_UP:
1619 return "Up";
1620 default:
1621 return "Invalid!";
1622 }
1623}
1624
1625static struct isis_threeway_adj *copy_tlv_threeway_adj(
1626 const struct isis_threeway_adj *threeway_adj)
1627{
1628 if (!threeway_adj)
1629 return NULL;
1630
1631 struct isis_threeway_adj *rv = XMALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
1632 memcpy(rv, threeway_adj, sizeof(*rv));
1633
1634 return rv;
1635}
1636
1637static void format_tlv_threeway_adj(const struct isis_threeway_adj *threeway_adj,
1638 struct sbuf *buf, int indent)
1639{
1640 if (!threeway_adj)
1641 return;
1642
1643 sbuf_push(buf, indent, "P2P Three-Way Adjacency:\n");
1644 sbuf_push(buf, indent, " State: %s (%d)\n",
1645 isis_threeway_state_name(threeway_adj->state),
1646 threeway_adj->state);
1647 sbuf_push(buf, indent, " Extended Local Circuit ID: %" PRIu32 "\n",
1648 threeway_adj->local_circuit_id);
1649 if (!threeway_adj->neighbor_set)
1650 return;
1651
1652 sbuf_push(buf, indent, " Neighbor System ID: %s\n",
1653 isis_format_id(threeway_adj->neighbor_id, 6));
1654 sbuf_push(buf, indent, " Neighbor Extended Circuit ID: %" PRIu32 "\n",
1655 threeway_adj->neighbor_circuit_id);
1656}
1657
1658static void free_tlv_threeway_adj(struct isis_threeway_adj *threeway_adj)
1659{
1660 XFREE(MTYPE_ISIS_TLV, threeway_adj);
1661}
1662
1663static int pack_tlv_threeway_adj(const struct isis_threeway_adj *threeway_adj,
1664 struct stream *s)
1665{
1666 if (!threeway_adj)
1667 return 0;
1668
1669 uint8_t tlv_len = (threeway_adj->neighbor_set) ? 15 : 5;
1670
1671 if (STREAM_WRITEABLE(s) < (unsigned)(2 + tlv_len))
1672 return 1;
1673
1674 stream_putc(s, ISIS_TLV_THREE_WAY_ADJ);
1675 stream_putc(s, tlv_len);
1676 stream_putc(s, threeway_adj->state);
1677 stream_putl(s, threeway_adj->local_circuit_id);
1678
1679 if (threeway_adj->neighbor_set) {
1680 stream_put(s, threeway_adj->neighbor_id, 6);
1681 stream_putl(s, threeway_adj->neighbor_circuit_id);
1682 }
1683
1684 return 0;
1685}
1686
1687static int unpack_tlv_threeway_adj(enum isis_tlv_context context,
1688 uint8_t tlv_type, uint8_t tlv_len,
1689 struct stream *s, struct sbuf *log,
1690 void *dest, int indent)
1691{
1692 struct isis_tlvs *tlvs = dest;
1693
1694 sbuf_push(log, indent, "Unpacking P2P Three-Way Adjacency TLV...\n");
1695 if (tlv_len != 5 && tlv_len != 15) {
1696 sbuf_push(log, indent, "WARNING: Unexepected TLV size\n");
1697 stream_forward_getp(s, tlv_len);
1698 return 0;
1699 }
1700
1701 if (tlvs->threeway_adj) {
1702 sbuf_push(log, indent,
1703 "WARNING: P2P Three-Way Adjacency TLV present multiple times.\n");
1704 stream_forward_getp(s, tlv_len);
1705 return 0;
1706 }
1707
1708 tlvs->threeway_adj = XCALLOC(MTYPE_ISIS_TLV, sizeof(*tlvs->threeway_adj));
1709
1710 tlvs->threeway_adj->state = stream_getc(s);
1711 tlvs->threeway_adj->local_circuit_id = stream_getl(s);
1712
1713 if (tlv_len == 15) {
1714 tlvs->threeway_adj->neighbor_set = true;
1715 stream_get(tlvs->threeway_adj->neighbor_id, s, 6);
1716 tlvs->threeway_adj->neighbor_circuit_id = stream_getl(s);
1717 }
1718
1719 return 0;
1720}
1721
7ef5fefc
CF
1722/* Functions related to TLVs 236/237 IPv6/MT-IPv6 reach */
1723
1724static struct isis_item *copy_item_ipv6_reach(struct isis_item *i)
1725{
1726 struct isis_ipv6_reach *r = (struct isis_ipv6_reach *)i;
841791b6 1727 struct isis_ipv6_reach *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1728 rv->metric = r->metric;
1729 rv->down = r->down;
1730 rv->external = r->external;
1731 rv->prefix = r->prefix;
1732 rv->subtlvs = copy_subtlvs(r->subtlvs);
1733
1734 return (struct isis_item *)rv;
1735}
1736
1737static void format_item_ipv6_reach(uint16_t mtid, struct isis_item *i,
1738 struct sbuf *buf, int indent)
1739{
1740 struct isis_ipv6_reach *r = (struct isis_ipv6_reach *)i;
1741 char prefixbuf[PREFIX2STR_BUFFER];
1742
1743 sbuf_push(buf, indent, "%sIPv6 Reachability: %s (Metric: %u)%s%s",
1744 (mtid == ISIS_MT_IPV4_UNICAST) ? "" : "MT ",
1745 prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf)),
98c5bc15
CF
1746 r->metric,
1747 r->down ? " Down" : "",
7ef5fefc
CF
1748 r->external ? " External" : "");
1749 if (mtid != ISIS_MT_IPV4_UNICAST)
1750 sbuf_push(buf, 0, " %s", isis_mtid2str(mtid));
1751 sbuf_push(buf, 0, "\n");
1752
1753 if (r->subtlvs) {
1754 sbuf_push(buf, indent, " Subtlvs:\n");
1755 format_subtlvs(r->subtlvs, buf, indent + 4);
1756 }
1757}
1758
1759static void free_item_ipv6_reach(struct isis_item *i)
1760{
1761 struct isis_ipv6_reach *item = (struct isis_ipv6_reach *)i;
1762
1763 isis_free_subtlvs(item->subtlvs);
841791b6 1764 XFREE(MTYPE_ISIS_TLV, item);
7ef5fefc
CF
1765}
1766
1767static int pack_item_ipv6_reach(struct isis_item *i, struct stream *s)
1768{
1769 struct isis_ipv6_reach *r = (struct isis_ipv6_reach *)i;
1770 uint8_t control;
1771
1772 if (STREAM_WRITEABLE(s) < 6)
1773 return 1;
1774 stream_putl(s, r->metric);
1775
1776 control = r->down ? ISIS_IPV6_REACH_DOWN : 0;
1777 control |= r->external ? ISIS_IPV6_REACH_EXTERNAL : 0;
1778 control |= r->subtlvs ? ISIS_IPV6_REACH_SUBTLV : 0;
1779
1780 stream_putc(s, control);
1781 stream_putc(s, r->prefix.prefixlen);
1782
1783 if (STREAM_WRITEABLE(s) < (unsigned)PSIZE(r->prefix.prefixlen))
1784 return 1;
1785 stream_put(s, &r->prefix.prefix.s6_addr, PSIZE(r->prefix.prefixlen));
1786
1787 if (r->subtlvs)
1788 return pack_subtlvs(r->subtlvs, s);
1789
1790 return 0;
1791}
1792
1793static int unpack_item_ipv6_reach(uint16_t mtid, uint8_t len, struct stream *s,
1794 struct sbuf *log, void *dest, int indent)
1795{
1796 struct isis_tlvs *tlvs = dest;
1797 struct isis_ipv6_reach *rv = NULL;
1798 size_t consume;
1799 uint8_t control, subtlv_len;
1800 struct isis_item_list *items;
1801
1802 if (mtid == ISIS_MT_IPV4_UNICAST) {
1803 items = &tlvs->ipv6_reach;
1804 } else {
1805 items = isis_get_mt_items(&tlvs->mt_ipv6_reach, mtid);
1806 }
1807
1808 sbuf_push(log, indent, "Unpacking %sIPv6 reachability...\n",
1809 (mtid == ISIS_MT_IPV4_UNICAST) ? "" : "mt ");
1810 consume = 6;
1811 if (len < consume) {
98c5bc15
CF
1812 sbuf_push(log, indent,
1813 "Not enough data left. (expected 6 or more bytes, got %"
1814 PRIu8 ")\n",
1815 len);
7ef5fefc
CF
1816 goto out;
1817 }
1818
841791b6 1819 rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1820
1821 rv->metric = stream_getl(s);
1822 control = stream_getc(s);
1823 rv->down = (control & ISIS_IPV6_REACH_DOWN);
1824 rv->external = (control & ISIS_IPV6_REACH_EXTERNAL);
1825
1826 rv->prefix.family = AF_INET6;
1827 rv->prefix.prefixlen = stream_getc(s);
1828 if (rv->prefix.prefixlen > 128) {
1829 sbuf_push(log, indent, "Prefixlen %u is inplausible for IPv6\n",
1830 rv->prefix.prefixlen);
1831 goto out;
1832 }
1833
1834 consume += PSIZE(rv->prefix.prefixlen);
1835 if (len < consume) {
98c5bc15
CF
1836 sbuf_push(log, indent,
1837 "Expected %u bytes of prefix, but only %u bytes available.\n",
1838 PSIZE(rv->prefix.prefixlen), len - 6);
7ef5fefc
CF
1839 goto out;
1840 }
1841 stream_get(&rv->prefix.prefix.s6_addr, s, PSIZE(rv->prefix.prefixlen));
1842 struct in6_addr orig_prefix = rv->prefix.prefix;
1843 apply_mask_ipv6(&rv->prefix);
1844 if (memcmp(&orig_prefix, &rv->prefix.prefix, sizeof(orig_prefix)))
1845 sbuf_push(log, indent + 2,
1846 "WARNING: Prefix had hostbits set.\n");
1847 format_item_ipv6_reach(mtid, (struct isis_item *)rv, log, indent + 2);
1848
1849 if (control & ISIS_IPV6_REACH_SUBTLV) {
1850 consume += 1;
1851 if (len < consume) {
98c5bc15
CF
1852 sbuf_push(log, indent,
1853 "Expected 1 byte of subtlv len, but no more data persent.\n");
7ef5fefc
CF
1854 goto out;
1855 }
1856 subtlv_len = stream_getc(s);
1857
1858 if (!subtlv_len) {
98c5bc15
CF
1859 sbuf_push(log, indent + 2,
1860 " WARNING: subtlv bit set, but there are no subtlvs.\n");
7ef5fefc
CF
1861 }
1862 consume += subtlv_len;
1863 if (len < consume) {
98c5bc15
CF
1864 sbuf_push(log, indent,
1865 "Expected %" PRIu8
1866 " bytes of subtlvs, but only %u bytes available.\n",
1867 subtlv_len,
1868 len - 6 - PSIZE(rv->prefix.prefixlen));
7ef5fefc
CF
1869 goto out;
1870 }
1871
bd507085 1872 rv->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IPV6_REACH);
7ef5fefc
CF
1873 if (unpack_tlvs(ISIS_CONTEXT_SUBTLV_IPV6_REACH, subtlv_len, s,
1874 log, rv->subtlvs, indent + 4)) {
1875 goto out;
1876 }
1877 }
1878
1879 append_item(items, (struct isis_item *)rv);
1880 return 0;
1881out:
1882 if (rv)
1883 free_item_ipv6_reach((struct isis_item *)rv);
1884 return 1;
1885}
1886
1887/* Functions related to TLV 10 Authentication */
1888static struct isis_item *copy_item_auth(struct isis_item *i)
1889{
1890 struct isis_auth *auth = (struct isis_auth *)i;
841791b6 1891 struct isis_auth *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1892
1893 rv->type = auth->type;
1894 rv->length = auth->length;
1895 memcpy(rv->value, auth->value, sizeof(rv->value));
1896 return (struct isis_item *)rv;
1897}
1898
1899static void format_item_auth(uint16_t mtid, struct isis_item *i,
1900 struct sbuf *buf, int indent)
1901{
1902 struct isis_auth *auth = (struct isis_auth *)i;
1903 char obuf[768];
1904
1905 sbuf_push(buf, indent, "Authentication:\n");
1906 switch (auth->type) {
1907 case ISIS_PASSWD_TYPE_CLEARTXT:
1908 zlog_sanitize(obuf, sizeof(obuf), auth->value, auth->length);
1909 sbuf_push(buf, indent, " Password: %s\n", obuf);
1910 break;
1911 case ISIS_PASSWD_TYPE_HMAC_MD5:
f7813c7c
A
1912 for (unsigned int j = 0; j < 16; j++) {
1913 snprintf(obuf + 2 * j, sizeof(obuf) - 2 * j,
1914 "%02" PRIx8, auth->value[j]);
7ef5fefc
CF
1915 }
1916 sbuf_push(buf, indent, " HMAC-MD5: %s\n", obuf);
1917 break;
1918 default:
1919 sbuf_push(buf, indent, " Unknown (%" PRIu8 ")\n", auth->type);
1920 break;
5b94ec50 1921 }
7ef5fefc
CF
1922}
1923
1924static void free_item_auth(struct isis_item *i)
1925{
841791b6 1926 XFREE(MTYPE_ISIS_TLV, i);
7ef5fefc
CF
1927}
1928
1929static int pack_item_auth(struct isis_item *i, struct stream *s)
1930{
1931 struct isis_auth *auth = (struct isis_auth *)i;
1932
1933 if (STREAM_WRITEABLE(s) < 1)
1934 return 1;
1935 stream_putc(s, auth->type);
1936
1937 switch (auth->type) {
1938 case ISIS_PASSWD_TYPE_CLEARTXT:
1939 if (STREAM_WRITEABLE(s) < auth->length)
1940 return 1;
1941 stream_put(s, auth->passwd, auth->length);
1942 break;
1943 case ISIS_PASSWD_TYPE_HMAC_MD5:
1944 if (STREAM_WRITEABLE(s) < 16)
1945 return 1;
1946 auth->offset = stream_get_endp(s);
1947 stream_put(s, NULL, 16);
1948 break;
1949 default:
1950 return 1;
1951 }
1952
1953 return 0;
1954}
1955
1956static int unpack_item_auth(uint16_t mtid, uint8_t len, struct stream *s,
1957 struct sbuf *log, void *dest, int indent)
1958{
1959 struct isis_tlvs *tlvs = dest;
1960
1961 sbuf_push(log, indent, "Unpack Auth TLV...\n");
1962 if (len < 1) {
1963 sbuf_push(
1964 log, indent,
1965 "Not enough data left.(Expected 1 bytes of auth type, got %" PRIu8
1966 ")\n",
1967 len);
1968 return 1;
1969 }
1970
841791b6 1971 struct isis_auth *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1972
1973 rv->type = stream_getc(s);
1974 rv->length = len - 1;
1975
1976 if (rv->type == ISIS_PASSWD_TYPE_HMAC_MD5 && rv->length != 16) {
1977 sbuf_push(
1978 log, indent,
1979 "Unexpected auth length for HMAC-MD5 (expected 16, got %" PRIu8
1980 ")\n",
1981 rv->length);
841791b6 1982 XFREE(MTYPE_ISIS_TLV, rv);
7ef5fefc
CF
1983 return 1;
1984 }
1985
1986 rv->offset = stream_get_getp(s);
1987 stream_get(rv->value, s, rv->length);
1988 format_item_auth(mtid, (struct isis_item *)rv, log, indent + 2);
1989 append_item(&tlvs->isis_auth, (struct isis_item *)rv);
1990 return 0;
1991}
1992
5f77d901
CF
1993/* Functions related to TLV 13 Purge Originator */
1994
1995static struct isis_purge_originator *copy_tlv_purge_originator(
1996 struct isis_purge_originator *poi)
1997{
1998 if (!poi)
1999 return NULL;
2000
2001 struct isis_purge_originator *rv;
2002
2003 rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
2004 rv->sender_set = poi->sender_set;
2005 memcpy(rv->generator, poi->generator, sizeof(rv->generator));
2006 if (poi->sender_set)
2007 memcpy(rv->sender, poi->sender, sizeof(rv->sender));
2008 return rv;
2009}
2010
2011static void format_tlv_purge_originator(struct isis_purge_originator *poi,
2012 struct sbuf *buf, int indent)
2013{
2014 if (!poi)
2015 return;
2016
2017 sbuf_push(buf, indent, "Purge Originator Identification:\n");
2018 sbuf_push(buf, indent, " Generator: %s\n",
2019 isis_format_id(poi->generator, sizeof(poi->generator)));
2020 if (poi->sender_set) {
2021 sbuf_push(buf, indent, " Received-From: %s\n",
2022 isis_format_id(poi->sender, sizeof(poi->sender)));
2023 }
2024}
2025
2026static void free_tlv_purge_originator(struct isis_purge_originator *poi)
2027{
2028 XFREE(MTYPE_ISIS_TLV, poi);
2029}
2030
2031static int pack_tlv_purge_originator(struct isis_purge_originator *poi,
2032 struct stream *s)
2033{
2034 if (!poi)
2035 return 0;
2036
2037 uint8_t data_len = 1 + sizeof(poi->generator);
2038
2039 if (poi->sender_set)
2040 data_len += sizeof(poi->sender);
2041
2042 if (STREAM_WRITEABLE(s) < (unsigned)(2 + data_len))
2043 return 1;
2044
2045 stream_putc(s, ISIS_TLV_PURGE_ORIGINATOR);
2046 stream_putc(s, data_len);
2047 stream_putc(s, poi->sender_set ? 2 : 1);
2048 stream_put(s, poi->generator, sizeof(poi->generator));
2049 if (poi->sender_set)
2050 stream_put(s, poi->sender, sizeof(poi->sender));
2051 return 0;
2052}
2053
2054static int unpack_tlv_purge_originator(enum isis_tlv_context context,
2055 uint8_t tlv_type, uint8_t tlv_len,
2056 struct stream *s, struct sbuf *log,
2057 void *dest, int indent)
2058{
2059 struct isis_tlvs *tlvs = dest;
3e300703 2060 struct isis_purge_originator poi = {};
5f77d901
CF
2061
2062 sbuf_push(log, indent, "Unpacking Purge Originator Identification TLV...\n");
2063 if (tlv_len < 7) {
2064 sbuf_push(log, indent, "Not enough data left. (Expected at least 7 bytes, got %"
2065 PRIu8 ")\n", tlv_len);
2066 return 1;
2067 }
2068
2069 uint8_t number_of_ids = stream_getc(s);
2070
2071 if (number_of_ids == 1) {
2072 poi.sender_set = false;
2073 } else if (number_of_ids == 2) {
2074 poi.sender_set = true;
2075 } else {
2076 sbuf_push(log, indent, "Got invalid value for number of system IDs: %"
2077 PRIu8 ")\n", number_of_ids);
2078 return 1;
2079 }
2080
2081 if (tlv_len != 1 + 6 * number_of_ids) {
2082 sbuf_push(log, indent, "Incorrect tlv len for number of IDs.\n");
2083 return 1;
2084 }
2085
2086 stream_get(poi.generator, s, sizeof(poi.generator));
2087 if (poi.sender_set)
2088 stream_get(poi.sender, s, sizeof(poi.sender));
2089
2090 if (tlvs->purge_originator) {
2091 sbuf_push(log, indent,
2092 "WARNING: Purge originator present multiple times, ignoring.\n");
2093 return 0;
2094 }
2095
2096 tlvs->purge_originator = copy_tlv_purge_originator(&poi);
2097 return 0;
2098}
2099
2100
7ef5fefc
CF
2101/* Functions relating to item TLVs */
2102
2103static void init_item_list(struct isis_item_list *items)
2104{
2105 items->head = NULL;
2106 items->tail = &items->head;
2107 items->count = 0;
2108}
2109
2110static struct isis_item *copy_item(enum isis_tlv_context context,
2111 enum isis_tlv_type type,
2112 struct isis_item *item)
2113{
2114 const struct tlv_ops *ops = tlv_table[context][type];
2115
2116 if (ops && ops->copy_item)
2117 return ops->copy_item(item);
2118
2119 assert(!"Unknown item tlv type!");
2120 return NULL;
2121}
2122
2123static void copy_items(enum isis_tlv_context context, enum isis_tlv_type type,
2124 struct isis_item_list *src, struct isis_item_list *dest)
2125{
2126 struct isis_item *item;
2127
2128 init_item_list(dest);
2129
2130 for (item = src->head; item; item = item->next) {
2131 append_item(dest, copy_item(context, type, item));
2132 }
2133}
2134
2135static void format_item(uint16_t mtid, enum isis_tlv_context context,
2136 enum isis_tlv_type type, struct isis_item *i,
2137 struct sbuf *buf, int indent)
2138{
2139 const struct tlv_ops *ops = tlv_table[context][type];
2140
2141 if (ops && ops->format_item) {
2142 ops->format_item(mtid, i, buf, indent);
2143 return;
2144 }
2145
2146 assert(!"Unknown item tlv type!");
2147}
2148
2149static void format_items_(uint16_t mtid, enum isis_tlv_context context,
2150 enum isis_tlv_type type, struct isis_item_list *items,
2151 struct sbuf *buf, int indent)
2152{
2153 struct isis_item *i;
2154
2155 for (i = items->head; i; i = i->next)
2156 format_item(mtid, context, type, i, buf, indent);
2157}
7ef5fefc
CF
2158
2159static void free_item(enum isis_tlv_context tlv_context,
2160 enum isis_tlv_type tlv_type, struct isis_item *item)
2161{
2162 const struct tlv_ops *ops = tlv_table[tlv_context][tlv_type];
2163
2164 if (ops && ops->free_item) {
2165 ops->free_item(item);
2166 return;
2167 }
2168
2169 assert(!"Unknown item tlv type!");
2170}
2171
2172static void free_items(enum isis_tlv_context context, enum isis_tlv_type type,
2173 struct isis_item_list *items)
2174{
2175 struct isis_item *item, *next_item;
2176
2177 for (item = items->head; item; item = next_item) {
2178 next_item = item->next;
2179 free_item(context, type, item);
2180 }
2181}
2182
2183static int pack_item(enum isis_tlv_context context, enum isis_tlv_type type,
2184 struct isis_item *i, struct stream *s,
2185 struct isis_tlvs **fragment_tlvs,
2186 struct pack_order_entry *pe, uint16_t mtid)
2187{
2188 const struct tlv_ops *ops = tlv_table[context][type];
2189
2190 if (ops && ops->pack_item) {
2191 return ops->pack_item(i, s);
2192 }
2193
2194 assert(!"Unknown item tlv type!");
2195 return 1;
2196}
2197
98c5bc15 2198static void add_item_to_fragment(struct isis_item *i, struct pack_order_entry *pe,
7ef5fefc
CF
2199 struct isis_tlvs *fragment_tlvs, uint16_t mtid)
2200{
2201 struct isis_item_list *l;
2202
2203 if (pe->how_to_pack == ISIS_ITEMS) {
98c5bc15 2204 l = (struct isis_item_list *)(((char *)fragment_tlvs) + pe->what_to_pack);
7ef5fefc
CF
2205 } else {
2206 struct isis_mt_item_list *m;
98c5bc15 2207 m = (struct isis_mt_item_list *)(((char *)fragment_tlvs) + pe->what_to_pack);
7ef5fefc
CF
2208 l = isis_get_mt_items(m, mtid);
2209 }
2210
2211 append_item(l, copy_item(pe->context, pe->type, i));
2212}
2213
2214static int pack_items_(uint16_t mtid, enum isis_tlv_context context,
2215 enum isis_tlv_type type, struct isis_item_list *items,
2216 struct stream *s, struct isis_tlvs **fragment_tlvs,
2217 struct pack_order_entry *pe,
2218 struct isis_tlvs *(*new_fragment)(struct list *l),
2219 struct list *new_fragment_arg)
2220{
2221 size_t len_pos, last_len, len;
2222 struct isis_item *item = NULL;
2223 int rv;
2224
2225 if (!items->head)
2226 return 0;
2227
2228top:
2229 if (STREAM_WRITEABLE(s) < 2)
2230 goto too_long;
2231
2232 stream_putc(s, type);
2233 len_pos = stream_get_endp(s);
2234 stream_putc(s, 0); /* Put 0 as length for now */
2235
2236 if (context == ISIS_CONTEXT_LSP && IS_COMPAT_MT_TLV(type)
2237 && mtid != ISIS_MT_IPV4_UNICAST) {
2238 if (STREAM_WRITEABLE(s) < 2)
2239 goto too_long;
2240 stream_putw(s, mtid);
2241 }
2242
2243 if (context == ISIS_CONTEXT_LSP && type == ISIS_TLV_OLDSTYLE_REACH) {
2244 if (STREAM_WRITEABLE(s) < 1)
2245 goto too_long;
2246 stream_putc(s, 0); /* Virtual flag is set to 0 */
2247 }
2248
2249 last_len = len = 0;
2250 for (item = item ? item : items->head; item; item = item->next) {
2251 rv = pack_item(context, type, item, s, fragment_tlvs, pe, mtid);
2252 if (rv)
2253 goto too_long;
2254
2255 len = stream_get_endp(s) - len_pos - 1;
2256
2257 /* Multiple auths don't go into one TLV, so always break */
2258 if (context == ISIS_CONTEXT_LSP && type == ISIS_TLV_AUTH) {
2259 item = item->next;
2260 break;
2261 }
2262
bd507085
CF
2263 /* Multiple prefix-sids don't go into one TLV, so always break */
2264 if (type == ISIS_SUBTLV_PREFIX_SID
2265 && (context == ISIS_CONTEXT_SUBTLV_IP_REACH
2266 || context == ISIS_CONTEXT_SUBTLV_IPV6_REACH)) {
2267 item = item->next;
2268 break;
2269 }
2270
7ef5fefc
CF
2271 if (len > 255) {
2272 if (!last_len) /* strange, not a single item fit */
2273 return 1;
2274 /* drop last tlv, otherwise, its too long */
2275 stream_set_endp(s, len_pos + 1 + last_len);
2276 len = last_len;
2277 break;
2278 }
2279
2280 if (fragment_tlvs)
2281 add_item_to_fragment(item, pe, *fragment_tlvs, mtid);
2282
2283 last_len = len;
2284 }
2285
2286 stream_putc_at(s, len_pos, len);
2287 if (item)
2288 goto top;
2289
2290 return 0;
2291too_long:
2292 if (!fragment_tlvs)
2293 return 1;
2294 stream_reset(s);
2295 *fragment_tlvs = new_fragment(new_fragment_arg);
2296 goto top;
2297}
2298#define pack_items(...) pack_items_(ISIS_MT_IPV4_UNICAST, __VA_ARGS__)
2299
2300static void append_item(struct isis_item_list *dest, struct isis_item *item)
2301{
2302 *dest->tail = item;
2303 dest->tail = &(*dest->tail)->next;
2304 dest->count++;
2305}
2306
d43d2df5
CF
2307static struct isis_item *last_item(struct isis_item_list *list)
2308{
2309 return container_of(list->tail, struct isis_item, next);
2310}
2311
7ef5fefc
CF
2312static int unpack_item(uint16_t mtid, enum isis_tlv_context context,
2313 uint8_t tlv_type, uint8_t len, struct stream *s,
2314 struct sbuf *log, void *dest, int indent)
2315{
2316 const struct tlv_ops *ops = tlv_table[context][tlv_type];
2317
2318 if (ops && ops->unpack_item)
2319 return ops->unpack_item(mtid, len, s, log, dest, indent);
2320
2321 assert(!"Unknown item tlv type!");
2322 sbuf_push(log, indent, "Unknown item tlv type!\n");
2323 return 1;
2324}
2325
2326static int unpack_tlv_with_items(enum isis_tlv_context context,
2327 uint8_t tlv_type, uint8_t tlv_len,
2328 struct stream *s, struct sbuf *log, void *dest,
2329 int indent)
2330{
2331 size_t tlv_start;
2332 size_t tlv_pos;
2333 int rv;
2334 uint16_t mtid;
2335
2336 tlv_start = stream_get_getp(s);
2337 tlv_pos = 0;
2338
2339 if (context == ISIS_CONTEXT_LSP && IS_COMPAT_MT_TLV(tlv_type)) {
2340 if (tlv_len < 2) {
2341 sbuf_push(log, indent,
2342 "TLV is too short to contain MTID\n");
2343 return 1;
2344 }
2345 mtid = stream_getw(s) & ISIS_MT_MASK;
2346 tlv_pos += 2;
2347 sbuf_push(log, indent, "Unpacking as MT %s item TLV...\n",
2348 isis_mtid2str(mtid));
2349 } else {
2350 sbuf_push(log, indent, "Unpacking as item TLV...\n");
2351 mtid = ISIS_MT_IPV4_UNICAST;
2352 }
2353
2354 if (context == ISIS_CONTEXT_LSP
2355 && tlv_type == ISIS_TLV_OLDSTYLE_REACH) {
2356 if (tlv_len - tlv_pos < 1) {
2357 sbuf_push(log, indent,
2358 "TLV is too short for old style reach\n");
2359 return 1;
2360 }
2361 stream_forward_getp(s, 1);
2362 tlv_pos += 1;
2363 }
2364
2365 if (context == ISIS_CONTEXT_LSP
2366 && tlv_type == ISIS_TLV_OLDSTYLE_IP_REACH) {
2367 struct isis_tlvs *tlvs = dest;
2368 dest = &tlvs->oldstyle_ip_reach;
2369 } else if (context == ISIS_CONTEXT_LSP
2370 && tlv_type == ISIS_TLV_OLDSTYLE_IP_REACH_EXT) {
2371 struct isis_tlvs *tlvs = dest;
2372 dest = &tlvs->oldstyle_ip_reach_ext;
2373 }
2374
2375 if (context == ISIS_CONTEXT_LSP
2376 && tlv_type == ISIS_TLV_MT_ROUTER_INFO) {
2377 struct isis_tlvs *tlvs = dest;
2378 tlvs->mt_router_info_empty = (tlv_pos >= (size_t)tlv_len);
2379 }
2380
2381 while (tlv_pos < (size_t)tlv_len) {
2382 rv = unpack_item(mtid, context, tlv_type, tlv_len - tlv_pos, s,
2383 log, dest, indent + 2);
2384 if (rv)
2385 return rv;
2386
2387 tlv_pos = stream_get_getp(s) - tlv_start;
2388 }
2389
2390 return 0;
2391}
2392
2393/* Functions to manipulate mt_item_lists */
2394
2395static int isis_mt_item_list_cmp(const struct isis_item_list *a,
2396 const struct isis_item_list *b)
2397{
2398 if (a->mtid < b->mtid)
2399 return -1;
2400 if (a->mtid > b->mtid)
2401 return 1;
2402 return 0;
2403}
2404
2405RB_PROTOTYPE(isis_mt_item_list, isis_item_list, mt_tree, isis_mt_item_list_cmp);
2406RB_GENERATE(isis_mt_item_list, isis_item_list, mt_tree, isis_mt_item_list_cmp);
2407
2408struct isis_item_list *isis_get_mt_items(struct isis_mt_item_list *m,
2409 uint16_t mtid)
2410{
2411 struct isis_item_list *rv;
2412
2413 rv = isis_lookup_mt_items(m, mtid);
2414 if (!rv) {
2415 rv = XCALLOC(MTYPE_ISIS_MT_ITEM_LIST, sizeof(*rv));
2416 init_item_list(rv);
2417 rv->mtid = mtid;
2418 RB_INSERT(isis_mt_item_list, m, rv);
2419 }
2420
2421 return rv;
2422}
2423
2424struct isis_item_list *isis_lookup_mt_items(struct isis_mt_item_list *m,
2425 uint16_t mtid)
2426{
2427 struct isis_item_list key = {.mtid = mtid};
2428
2429 return RB_FIND(isis_mt_item_list, m, &key);
2430}
2431
2432static void free_mt_items(enum isis_tlv_context context,
2433 enum isis_tlv_type type, struct isis_mt_item_list *m)
2434{
2435 struct isis_item_list *n, *nnext;
2436
a2addae8 2437 RB_FOREACH_SAFE (n, isis_mt_item_list, m, nnext) {
7ef5fefc
CF
2438 free_items(context, type, n);
2439 RB_REMOVE(isis_mt_item_list, m, n);
2440 XFREE(MTYPE_ISIS_MT_ITEM_LIST, n);
2441 }
2442}
2443
2444static void format_mt_items(enum isis_tlv_context context,
2445 enum isis_tlv_type type,
2446 struct isis_mt_item_list *m, struct sbuf *buf,
2447 int indent)
2448{
2449 struct isis_item_list *n;
2450
a2addae8 2451 RB_FOREACH (n, isis_mt_item_list, m) {
7ef5fefc
CF
2452 format_items_(n->mtid, context, type, n, buf, indent);
2453 }
2454}
2455
2456static int pack_mt_items(enum isis_tlv_context context, enum isis_tlv_type type,
2457 struct isis_mt_item_list *m, struct stream *s,
2458 struct isis_tlvs **fragment_tlvs,
2459 struct pack_order_entry *pe,
2460 struct isis_tlvs *(*new_fragment)(struct list *l),
2461 struct list *new_fragment_arg)
2462{
2463 struct isis_item_list *n;
2464
a2addae8 2465 RB_FOREACH (n, isis_mt_item_list, m) {
7ef5fefc
CF
2466 int rv;
2467
2468 rv = pack_items_(n->mtid, context, type, n, s, fragment_tlvs,
2469 pe, new_fragment, new_fragment_arg);
2470 if (rv)
2471 return rv;
2472 }
2473
2474 return 0;
2475}
2476
2477static void copy_mt_items(enum isis_tlv_context context,
2478 enum isis_tlv_type type,
2479 struct isis_mt_item_list *src,
2480 struct isis_mt_item_list *dest)
2481{
2482 struct isis_item_list *n;
2483
2484 RB_INIT(isis_mt_item_list, dest);
2485
a2addae8 2486 RB_FOREACH (n, isis_mt_item_list, src) {
7ef5fefc
CF
2487 copy_items(context, type, n, isis_get_mt_items(dest, n->mtid));
2488 }
2489}
2490
2491/* Functions related to tlvs in general */
2492
2493struct isis_tlvs *isis_alloc_tlvs(void)
2494{
2495 struct isis_tlvs *result;
2496
841791b6 2497 result = XCALLOC(MTYPE_ISIS_TLV, sizeof(*result));
7ef5fefc
CF
2498
2499 init_item_list(&result->isis_auth);
2500 init_item_list(&result->area_addresses);
2501 init_item_list(&result->mt_router_info);
2502 init_item_list(&result->oldstyle_reach);
2503 init_item_list(&result->lan_neighbor);
2504 init_item_list(&result->lsp_entries);
2505 init_item_list(&result->extended_reach);
2506 RB_INIT(isis_mt_item_list, &result->mt_reach);
2507 init_item_list(&result->oldstyle_ip_reach);
2508 init_item_list(&result->oldstyle_ip_reach_ext);
2509 init_item_list(&result->ipv4_address);
2510 init_item_list(&result->ipv6_address);
2511 init_item_list(&result->extended_ip_reach);
2512 RB_INIT(isis_mt_item_list, &result->mt_ip_reach);
2513 init_item_list(&result->ipv6_reach);
2514 RB_INIT(isis_mt_item_list, &result->mt_ipv6_reach);
2515
2516 return result;
2517}
2518
2519struct isis_tlvs *isis_copy_tlvs(struct isis_tlvs *tlvs)
2520{
841791b6 2521 struct isis_tlvs *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
2522
2523 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth,
2524 &rv->isis_auth);
2525
5f77d901
CF
2526 rv->purge_originator =
2527 copy_tlv_purge_originator(tlvs->purge_originator);
2528
7ef5fefc
CF
2529 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES,
2530 &tlvs->area_addresses, &rv->area_addresses);
2531
2532 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO,
2533 &tlvs->mt_router_info, &rv->mt_router_info);
2534
b9d4a380 2535 rv->mt_router_info_empty = tlvs->mt_router_info_empty;
7ef5fefc
CF
2536
2537 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_REACH,
2538 &tlvs->oldstyle_reach, &rv->oldstyle_reach);
2539
2540 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_LAN_NEIGHBORS,
2541 &tlvs->lan_neighbor, &rv->lan_neighbor);
2542
2543 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_LSP_ENTRY, &tlvs->lsp_entries,
2544 &rv->lsp_entries);
2545
2546 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_REACH,
2547 &tlvs->extended_reach, &rv->extended_reach);
2548
2549 copy_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_REACH, &tlvs->mt_reach,
2550 &rv->mt_reach);
2551
2552 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH,
2553 &tlvs->oldstyle_ip_reach, &rv->oldstyle_ip_reach);
2554
2555 copy_tlv_protocols_supported(&tlvs->protocols_supported,
2556 &rv->protocols_supported);
2557
2558 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH_EXT,
2559 &tlvs->oldstyle_ip_reach_ext, &rv->oldstyle_ip_reach_ext);
2560
2561 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV4_ADDRESS, &tlvs->ipv4_address,
2562 &rv->ipv4_address);
2563
2564 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_ADDRESS, &tlvs->ipv6_address,
2565 &rv->ipv6_address);
2566
2567 rv->te_router_id = copy_tlv_te_router_id(tlvs->te_router_id);
2568
2569 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_IP_REACH,
2570 &tlvs->extended_ip_reach, &rv->extended_ip_reach);
2571
2572 copy_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IP_REACH,
2573 &tlvs->mt_ip_reach, &rv->mt_ip_reach);
2574
2575 rv->hostname = copy_tlv_dynamic_hostname(tlvs->hostname);
2576
2577 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_REACH, &tlvs->ipv6_reach,
2578 &rv->ipv6_reach);
2579
2580 copy_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IPV6_REACH,
2581 &tlvs->mt_ipv6_reach, &rv->mt_ipv6_reach);
2582
9fe21208
CF
2583 rv->threeway_adj = copy_tlv_threeway_adj(tlvs->threeway_adj);
2584
41a145f1
CF
2585 rv->spine_leaf = copy_tlv_spine_leaf(tlvs->spine_leaf);
2586
7ef5fefc
CF
2587 return rv;
2588}
2589
2590static void format_tlvs(struct isis_tlvs *tlvs, struct sbuf *buf, int indent)
2591{
2592 format_tlv_protocols_supported(&tlvs->protocols_supported, buf, indent);
2593
2594 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth, buf,
2595 indent);
2596
5f77d901
CF
2597 format_tlv_purge_originator(tlvs->purge_originator, buf, indent);
2598
7ef5fefc
CF
2599 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES,
2600 &tlvs->area_addresses, buf, indent);
2601
2602 if (tlvs->mt_router_info_empty) {
2603 sbuf_push(buf, indent, "MT Router Info: None\n");
2604 } else {
2605 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO,
2606 &tlvs->mt_router_info, buf, indent);
2607 }
2608
2609 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_REACH,
2610 &tlvs->oldstyle_reach, buf, indent);
2611
2612 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_LAN_NEIGHBORS,
2613 &tlvs->lan_neighbor, buf, indent);
2614
2615 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_LSP_ENTRY, &tlvs->lsp_entries,
2616 buf, indent);
2617
2618 format_tlv_dynamic_hostname(tlvs->hostname, buf, indent);
2619 format_tlv_te_router_id(tlvs->te_router_id, buf, indent);
2620
2621 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_REACH,
2622 &tlvs->extended_reach, buf, indent);
2623
2624 format_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_REACH, &tlvs->mt_reach,
2625 buf, indent);
2626
2627 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH,
2628 &tlvs->oldstyle_ip_reach, buf, indent);
2629
2630 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH_EXT,
2631 &tlvs->oldstyle_ip_reach_ext, buf, indent);
2632
2633 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV4_ADDRESS,
2634 &tlvs->ipv4_address, buf, indent);
2635
2636 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_ADDRESS,
2637 &tlvs->ipv6_address, buf, indent);
2638
2639 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_IP_REACH,
2640 &tlvs->extended_ip_reach, buf, indent);
2641
2642 format_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IP_REACH,
2643 &tlvs->mt_ip_reach, buf, indent);
2644
2645 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_REACH, &tlvs->ipv6_reach,
2646 buf, indent);
2647
2648 format_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IPV6_REACH,
2649 &tlvs->mt_ipv6_reach, buf, indent);
9fe21208
CF
2650
2651 format_tlv_threeway_adj(tlvs->threeway_adj, buf, indent);
41a145f1
CF
2652
2653 format_tlv_spine_leaf(tlvs->spine_leaf, buf, indent);
7ef5fefc
CF
2654}
2655
2656const char *isis_format_tlvs(struct isis_tlvs *tlvs)
2657{
2658 static struct sbuf buf;
2659
2660 if (!sbuf_buf(&buf))
2661 sbuf_init(&buf, NULL, 0);
2662
2663 sbuf_reset(&buf);
2664 format_tlvs(tlvs, &buf, 0);
2665 return sbuf_buf(&buf);
2666}
2667
2668void isis_free_tlvs(struct isis_tlvs *tlvs)
2669{
2670 if (!tlvs)
2671 return;
2672
2673 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth);
5f77d901 2674 free_tlv_purge_originator(tlvs->purge_originator);
7ef5fefc
CF
2675 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES,
2676 &tlvs->area_addresses);
2677 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO,
2678 &tlvs->mt_router_info);
2679 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_REACH,
2680 &tlvs->oldstyle_reach);
2681 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_LAN_NEIGHBORS,
2682 &tlvs->lan_neighbor);
2683 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_LSP_ENTRY, &tlvs->lsp_entries);
2684 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_REACH,
2685 &tlvs->extended_reach);
2686 free_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_REACH, &tlvs->mt_reach);
2687 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH,
2688 &tlvs->oldstyle_ip_reach);
2689 free_tlv_protocols_supported(&tlvs->protocols_supported);
2690 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH_EXT,
2691 &tlvs->oldstyle_ip_reach_ext);
2692 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV4_ADDRESS,
2693 &tlvs->ipv4_address);
2694 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_ADDRESS,
2695 &tlvs->ipv6_address);
2696 free_tlv_te_router_id(tlvs->te_router_id);
2697 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_IP_REACH,
2698 &tlvs->extended_ip_reach);
2699 free_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IP_REACH,
2700 &tlvs->mt_ip_reach);
2701 free_tlv_dynamic_hostname(tlvs->hostname);
2702 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_REACH, &tlvs->ipv6_reach);
2703 free_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IPV6_REACH,
2704 &tlvs->mt_ipv6_reach);
9fe21208 2705 free_tlv_threeway_adj(tlvs->threeway_adj);
41a145f1 2706 free_tlv_spine_leaf(tlvs->spine_leaf);
7ef5fefc 2707
841791b6 2708 XFREE(MTYPE_ISIS_TLV, tlvs);
7ef5fefc
CF
2709}
2710
2711static void add_padding(struct stream *s)
2712{
2713 while (STREAM_WRITEABLE(s)) {
2714 if (STREAM_WRITEABLE(s) == 1)
2715 break;
2716 uint32_t padding_len = STREAM_WRITEABLE(s) - 2;
2717
2718 if (padding_len > 255) {
2719 if (padding_len == 256)
2720 padding_len = 254;
2721 else
2722 padding_len = 255;
2723 }
2724
2725 stream_putc(s, ISIS_TLV_PADDING);
2726 stream_putc(s, padding_len);
2727 stream_put(s, NULL, padding_len);
2728 }
2729}
2730
2731#define LSP_REM_LIFETIME_OFF 10
2732#define LSP_CHECKSUM_OFF 24
2733static void safe_auth_md5(struct stream *s, uint16_t *checksum,
2734 uint16_t *rem_lifetime)
2735{
2736 memcpy(rem_lifetime, STREAM_DATA(s) + LSP_REM_LIFETIME_OFF,
2737 sizeof(*rem_lifetime));
2738 memset(STREAM_DATA(s) + LSP_REM_LIFETIME_OFF, 0, sizeof(*rem_lifetime));
2739 memcpy(checksum, STREAM_DATA(s) + LSP_CHECKSUM_OFF, sizeof(*checksum));
2740 memset(STREAM_DATA(s) + LSP_CHECKSUM_OFF, 0, sizeof(*checksum));
2741}
2742
2743static void restore_auth_md5(struct stream *s, uint16_t checksum,
2744 uint16_t rem_lifetime)
2745{
2746 memcpy(STREAM_DATA(s) + LSP_REM_LIFETIME_OFF, &rem_lifetime,
2747 sizeof(rem_lifetime));
2748 memcpy(STREAM_DATA(s) + LSP_CHECKSUM_OFF, &checksum, sizeof(checksum));
2749}
2750
2751static void update_auth_hmac_md5(struct isis_auth *auth, struct stream *s,
2752 bool is_lsp)
2753{
2754 uint8_t digest[16];
2755 uint16_t checksum, rem_lifetime;
2756
2757 if (is_lsp)
2758 safe_auth_md5(s, &checksum, &rem_lifetime);
2759
2760 memset(STREAM_DATA(s) + auth->offset, 0, 16);
2761 hmac_md5(STREAM_DATA(s), stream_get_endp(s), auth->passwd,
2762 auth->plength, digest);
2763 memcpy(auth->value, digest, 16);
2764 memcpy(STREAM_DATA(s) + auth->offset, digest, 16);
2765
2766 if (is_lsp)
2767 restore_auth_md5(s, checksum, rem_lifetime);
2768}
2769
2770static void update_auth(struct isis_tlvs *tlvs, struct stream *s, bool is_lsp)
2771{
2772 struct isis_auth *auth_head = (struct isis_auth *)tlvs->isis_auth.head;
2773
2774 for (struct isis_auth *auth = auth_head; auth; auth = auth->next) {
2775 if (auth->type == ISIS_PASSWD_TYPE_HMAC_MD5)
2776 update_auth_hmac_md5(auth, s, is_lsp);
2777 }
2778}
2779
2780static int handle_pack_entry(struct pack_order_entry *pe,
2781 struct isis_tlvs *tlvs, struct stream *stream,
2782 struct isis_tlvs **fragment_tlvs,
2783 struct isis_tlvs *(*new_fragment)(struct list *l),
2784 struct list *new_fragment_arg)
2785{
2786 int rv;
2787
2788 if (pe->how_to_pack == ISIS_ITEMS) {
2789 struct isis_item_list *l;
2790 l = (struct isis_item_list *)(((char *)tlvs)
2791 + pe->what_to_pack);
2792 rv = pack_items(pe->context, pe->type, l, stream, fragment_tlvs,
2793 pe, new_fragment, new_fragment_arg);
2794 } else {
2795 struct isis_mt_item_list *l;
2796 l = (struct isis_mt_item_list *)(((char *)tlvs)
2797 + pe->what_to_pack);
2798 rv = pack_mt_items(pe->context, pe->type, l, stream,
2799 fragment_tlvs, pe, new_fragment,
2800 new_fragment_arg);
2801 }
2802
2803 return rv;
2804}
2805
2806static int pack_tlvs(struct isis_tlvs *tlvs, struct stream *stream,
2807 struct isis_tlvs *fragment_tlvs,
2808 struct isis_tlvs *(*new_fragment)(struct list *l),
2809 struct list *new_fragment_arg)
2810{
2811 int rv;
2812
2813 /* When fragmenting, don't add auth as it's already accounted for in the
2814 * size we are given. */
2815 if (!fragment_tlvs) {
996c9314
LB
2816 rv = pack_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH,
2817 &tlvs->isis_auth, stream, NULL, NULL, NULL,
2818 NULL);
7ef5fefc
CF
2819 if (rv)
2820 return rv;
2821 }
2822
5f77d901
CF
2823 rv = pack_tlv_purge_originator(tlvs->purge_originator, stream);
2824 if (rv)
2825 return rv;
2826 if (fragment_tlvs) {
2827 fragment_tlvs->purge_originator =
2828 copy_tlv_purge_originator(tlvs->purge_originator);
2829 }
2830
7ef5fefc
CF
2831 rv = pack_tlv_protocols_supported(&tlvs->protocols_supported, stream);
2832 if (rv)
2833 return rv;
2834 if (fragment_tlvs) {
2835 copy_tlv_protocols_supported(
2836 &tlvs->protocols_supported,
2837 &fragment_tlvs->protocols_supported);
2838 }
2839
2840 rv = pack_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES,
2841 &tlvs->area_addresses, stream, NULL, NULL, NULL, NULL);
2842 if (rv)
2843 return rv;
2844 if (fragment_tlvs) {
2845 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES,
2846 &tlvs->area_addresses,
2847 &fragment_tlvs->area_addresses);
2848 }
2849
2850
2851 if (tlvs->mt_router_info_empty) {
2852 if (STREAM_WRITEABLE(stream) < 2)
2853 return 1;
2854 stream_putc(stream, ISIS_TLV_MT_ROUTER_INFO);
2855 stream_putc(stream, 0);
2856 if (fragment_tlvs)
2857 fragment_tlvs->mt_router_info_empty = true;
2858 } else {
2859 rv = pack_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO,
2860 &tlvs->mt_router_info, stream, NULL, NULL, NULL,
2861 NULL);
2862 if (rv)
2863 return rv;
2864 if (fragment_tlvs) {
2865 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO,
2866 &tlvs->mt_router_info,
2867 &fragment_tlvs->mt_router_info);
2868 }
2869 }
2870
2871 rv = pack_tlv_dynamic_hostname(tlvs->hostname, stream);
2872 if (rv)
2873 return rv;
2874 if (fragment_tlvs)
2875 fragment_tlvs->hostname =
2876 copy_tlv_dynamic_hostname(tlvs->hostname);
2877
2878 rv = pack_tlv_te_router_id(tlvs->te_router_id, stream);
2879 if (rv)
2880 return rv;
2881 if (fragment_tlvs) {
2882 fragment_tlvs->te_router_id =
2883 copy_tlv_te_router_id(tlvs->te_router_id);
2884 }
2885
9fe21208
CF
2886 rv = pack_tlv_threeway_adj(tlvs->threeway_adj, stream);
2887 if (rv)
2888 return rv;
2889 if (fragment_tlvs) {
2890 fragment_tlvs->threeway_adj =
2891 copy_tlv_threeway_adj(tlvs->threeway_adj);
2892 }
2893
41a145f1
CF
2894 rv = pack_tlv_spine_leaf(tlvs->spine_leaf, stream);
2895 if (rv)
2896 return rv;
2897 if (fragment_tlvs) {
2898 fragment_tlvs->spine_leaf =
2899 copy_tlv_spine_leaf(tlvs->spine_leaf);
2900 }
2901
7ef5fefc
CF
2902 for (size_t pack_idx = 0; pack_idx < array_size(pack_order);
2903 pack_idx++) {
2904 rv = handle_pack_entry(&pack_order[pack_idx], tlvs, stream,
2905 fragment_tlvs ? &fragment_tlvs : NULL,
2906 new_fragment, new_fragment_arg);
2907
2908 if (rv)
2909 return rv;
2910 }
2911
2912 return 0;
2913}
2914
2915int isis_pack_tlvs(struct isis_tlvs *tlvs, struct stream *stream,
2916 size_t len_pointer, bool pad, bool is_lsp)
2917{
2918 int rv;
2919
2920 rv = pack_tlvs(tlvs, stream, NULL, NULL, NULL);
2921 if (rv)
2922 return rv;
2923
2924 if (pad)
2925 add_padding(stream);
2926
2927 if (len_pointer != (size_t)-1) {
2928 stream_putw_at(stream, len_pointer, stream_get_endp(stream));
2929 }
2930
2931 update_auth(tlvs, stream, is_lsp);
2932
2933 return 0;
2934}
2935
2936static struct isis_tlvs *new_fragment(struct list *l)
2937{
2938 struct isis_tlvs *rv = isis_alloc_tlvs();
2939
2940 listnode_add(l, rv);
2941 return rv;
2942}
2943
2944struct list *isis_fragment_tlvs(struct isis_tlvs *tlvs, size_t size)
2945{
2946 struct stream *dummy_stream = stream_new(size);
2947 struct list *rv = list_new();
2948 struct isis_tlvs *fragment_tlvs = new_fragment(rv);
2949
2950 if (pack_tlvs(tlvs, dummy_stream, fragment_tlvs, new_fragment, rv)) {
2951 struct listnode *node;
2952 for (ALL_LIST_ELEMENTS_RO(rv, node, fragment_tlvs))
2953 isis_free_tlvs(fragment_tlvs);
6a154c88 2954 list_delete(&rv);
7ef5fefc
CF
2955 }
2956
2957 stream_free(dummy_stream);
2958 return rv;
2959}
2960
2961static int unpack_tlv_unknown(enum isis_tlv_context context, uint8_t tlv_type,
2962 uint8_t tlv_len, struct stream *s,
2963 struct sbuf *log, int indent)
2964{
2965 stream_forward_getp(s, tlv_len);
2966 sbuf_push(log, indent,
2967 "Skipping unknown TLV %" PRIu8 " (%" PRIu8 " bytes)\n",
2968 tlv_type, tlv_len);
2969 return 0;
2970}
2971
2972static int unpack_tlv(enum isis_tlv_context context, size_t avail_len,
2973 struct stream *stream, struct sbuf *log, void *dest,
2974 int indent)
2975{
2976 uint8_t tlv_type, tlv_len;
2977 const struct tlv_ops *ops;
2978
2979 sbuf_push(log, indent, "Unpacking TLV...\n");
2980
2981 if (avail_len < 2) {
2982 sbuf_push(
2983 log, indent + 2,
2984 "Available data %zu too short to contain a TLV header.\n",
2985 avail_len);
2986 return 1;
2987 }
2988
2989 tlv_type = stream_getc(stream);
2990 tlv_len = stream_getc(stream);
2991
2992 sbuf_push(log, indent + 2,
2993 "Found TLV of type %" PRIu8 " and len %" PRIu8 ".\n",
2994 tlv_type, tlv_len);
2995
2996 if (avail_len < ((size_t)tlv_len) + 2) {
98c5bc15
CF
2997 sbuf_push(log, indent + 2,
2998 "Available data %zu too short for claimed TLV len %" PRIu8 ".\n",
2999 avail_len - 2, tlv_len);
7ef5fefc
CF
3000 return 1;
3001 }
3002
3003 ops = tlv_table[context][tlv_type];
3004 if (ops && ops->unpack) {
3005 return ops->unpack(context, tlv_type, tlv_len, stream, log,
3006 dest, indent + 2);
3007 }
3008
3009 return unpack_tlv_unknown(context, tlv_type, tlv_len, stream, log,
3010 indent + 2);
3011}
3012
3013static int unpack_tlvs(enum isis_tlv_context context, size_t avail_len,
3014 struct stream *stream, struct sbuf *log, void *dest,
3015 int indent)
3016{
3017 int rv;
3018 size_t tlv_start, tlv_pos;
3019
3020 tlv_start = stream_get_getp(stream);
3021 tlv_pos = 0;
3022
3023 sbuf_push(log, indent, "Unpacking %zu bytes of %s...\n", avail_len,
3024 (context == ISIS_CONTEXT_LSP) ? "TLVs" : "sub-TLVs");
3025
3026 while (tlv_pos < avail_len) {
3027 rv = unpack_tlv(context, avail_len - tlv_pos, stream, log, dest,
3028 indent + 2);
3029 if (rv)
3030 return rv;
3031
3032 tlv_pos = stream_get_getp(stream) - tlv_start;
3033 }
3034
3035 return 0;
3036}
3037
3038int isis_unpack_tlvs(size_t avail_len, struct stream *stream,
3039 struct isis_tlvs **dest, const char **log)
3040{
3041 static struct sbuf logbuf;
3042 int indent = 0;
3043 int rv;
3044 struct isis_tlvs *result;
3045
3046 if (!sbuf_buf(&logbuf))
3047 sbuf_init(&logbuf, NULL, 0);
3048
3049 sbuf_reset(&logbuf);
3050 if (avail_len > STREAM_READABLE(stream)) {
3051 sbuf_push(&logbuf, indent,
3052 "Stream doesn't contain sufficient data. "
3053 "Claimed %zu, available %zu\n",
3054 avail_len, STREAM_READABLE(stream));
3055 return 1;
3056 }
3057
3058 result = isis_alloc_tlvs();
3059 rv = unpack_tlvs(ISIS_CONTEXT_LSP, avail_len, stream, &logbuf, result,
3060 indent);
3061
3062 *log = sbuf_buf(&logbuf);
3063 *dest = result;
3064
3065 return rv;
3066}
3067
3068#define TLV_OPS(_name_, _desc_) \
3069 static const struct tlv_ops tlv_##_name_##_ops = { \
3070 .name = _desc_, .unpack = unpack_tlv_##_name_, \
3071 }
3072
3073#define ITEM_TLV_OPS(_name_, _desc_) \
3074 static const struct tlv_ops tlv_##_name_##_ops = { \
3075 .name = _desc_, \
3076 .unpack = unpack_tlv_with_items, \
98c5bc15 3077 \
7ef5fefc
CF
3078 .pack_item = pack_item_##_name_, \
3079 .free_item = free_item_##_name_, \
3080 .unpack_item = unpack_item_##_name_, \
3081 .format_item = format_item_##_name_, \
3082 .copy_item = copy_item_##_name_}
3083
3084#define SUBTLV_OPS(_name_, _desc_) \
3085 static const struct tlv_ops subtlv_##_name_##_ops = { \
3086 .name = _desc_, .unpack = unpack_subtlv_##_name_, \
3087 }
3088
bd507085
CF
3089#define ITEM_SUBTLV_OPS(_name_, _desc_) \
3090 ITEM_TLV_OPS(_name_, _desc_)
3091
7ef5fefc
CF
3092ITEM_TLV_OPS(area_address, "TLV 1 Area Addresses");
3093ITEM_TLV_OPS(oldstyle_reach, "TLV 2 IS Reachability");
3094ITEM_TLV_OPS(lan_neighbor, "TLV 6 LAN Neighbors");
3095ITEM_TLV_OPS(lsp_entry, "TLV 9 LSP Entries");
3096ITEM_TLV_OPS(auth, "TLV 10 IS-IS Auth");
5f77d901 3097TLV_OPS(purge_originator, "TLV 13 Purge Originator Identification");
7ef5fefc
CF
3098ITEM_TLV_OPS(extended_reach, "TLV 22 Extended Reachability");
3099ITEM_TLV_OPS(oldstyle_ip_reach, "TLV 128/130 IP Reachability");
3100TLV_OPS(protocols_supported, "TLV 129 Protocols Supported");
3101ITEM_TLV_OPS(ipv4_address, "TLV 132 IPv4 Interface Address");
3102TLV_OPS(te_router_id, "TLV 134 TE Router ID");
3103ITEM_TLV_OPS(extended_ip_reach, "TLV 135 Extended IP Reachability");
3104TLV_OPS(dynamic_hostname, "TLV 137 Dynamic Hostname");
41a145f1 3105TLV_OPS(spine_leaf, "TLV 150 Spine Leaf Extensions");
7ef5fefc 3106ITEM_TLV_OPS(mt_router_info, "TLV 229 MT Router Information");
9fe21208 3107TLV_OPS(threeway_adj, "TLV 240 P2P Three-Way Adjacency");
7ef5fefc
CF
3108ITEM_TLV_OPS(ipv6_address, "TLV 232 IPv6 Interface Address");
3109ITEM_TLV_OPS(ipv6_reach, "TLV 236 IPv6 Reachability");
3110
bd507085 3111ITEM_SUBTLV_OPS(prefix_sid, "Sub-TLV 3 SR Prefix-SID");
7ef5fefc
CF
3112SUBTLV_OPS(ipv6_source_prefix, "Sub-TLV 22 IPv6 Source Prefix");
3113
98c5bc15
CF
3114static const struct tlv_ops *tlv_table[ISIS_CONTEXT_MAX][ISIS_TLV_MAX] = {
3115 [ISIS_CONTEXT_LSP] = {
3116 [ISIS_TLV_AREA_ADDRESSES] = &tlv_area_address_ops,
3117 [ISIS_TLV_OLDSTYLE_REACH] = &tlv_oldstyle_reach_ops,
3118 [ISIS_TLV_LAN_NEIGHBORS] = &tlv_lan_neighbor_ops,
3119 [ISIS_TLV_LSP_ENTRY] = &tlv_lsp_entry_ops,
3120 [ISIS_TLV_AUTH] = &tlv_auth_ops,
5f77d901 3121 [ISIS_TLV_PURGE_ORIGINATOR] = &tlv_purge_originator_ops,
98c5bc15
CF
3122 [ISIS_TLV_EXTENDED_REACH] = &tlv_extended_reach_ops,
3123 [ISIS_TLV_MT_REACH] = &tlv_extended_reach_ops,
3124 [ISIS_TLV_OLDSTYLE_IP_REACH] = &tlv_oldstyle_ip_reach_ops,
3125 [ISIS_TLV_PROTOCOLS_SUPPORTED] = &tlv_protocols_supported_ops,
3126 [ISIS_TLV_OLDSTYLE_IP_REACH_EXT] = &tlv_oldstyle_ip_reach_ops,
3127 [ISIS_TLV_IPV4_ADDRESS] = &tlv_ipv4_address_ops,
3128 [ISIS_TLV_TE_ROUTER_ID] = &tlv_te_router_id_ops,
3129 [ISIS_TLV_EXTENDED_IP_REACH] = &tlv_extended_ip_reach_ops,
3130 [ISIS_TLV_MT_IP_REACH] = &tlv_extended_ip_reach_ops,
3131 [ISIS_TLV_DYNAMIC_HOSTNAME] = &tlv_dynamic_hostname_ops,
41a145f1 3132 [ISIS_TLV_SPINE_LEAF_EXT] = &tlv_spine_leaf_ops,
98c5bc15 3133 [ISIS_TLV_MT_ROUTER_INFO] = &tlv_mt_router_info_ops,
9fe21208 3134 [ISIS_TLV_THREE_WAY_ADJ] = &tlv_threeway_adj_ops,
98c5bc15
CF
3135 [ISIS_TLV_IPV6_ADDRESS] = &tlv_ipv6_address_ops,
3136 [ISIS_TLV_IPV6_REACH] = &tlv_ipv6_reach_ops,
3137 [ISIS_TLV_MT_IPV6_REACH] = &tlv_ipv6_reach_ops,
3138 },
3e300703 3139 [ISIS_CONTEXT_SUBTLV_NE_REACH] = {},
bd507085
CF
3140 [ISIS_CONTEXT_SUBTLV_IP_REACH] = {
3141 [ISIS_SUBTLV_PREFIX_SID] = &tlv_prefix_sid_ops,
3142 },
98c5bc15 3143 [ISIS_CONTEXT_SUBTLV_IPV6_REACH] = {
bd507085 3144 [ISIS_SUBTLV_PREFIX_SID] = &tlv_prefix_sid_ops,
98c5bc15
CF
3145 [ISIS_SUBTLV_IPV6_SOURCE_PREFIX] = &subtlv_ipv6_source_prefix_ops,
3146 }
3147};
7ef5fefc
CF
3148
3149/* Accessor functions */
3150
3151void isis_tlvs_add_auth(struct isis_tlvs *tlvs, struct isis_passwd *passwd)
3152{
3153 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth);
3154 init_item_list(&tlvs->isis_auth);
3155
3156 if (passwd->type == ISIS_PASSWD_TYPE_UNUSED)
3157 return;
3158
841791b6 3159 struct isis_auth *auth = XCALLOC(MTYPE_ISIS_TLV, sizeof(*auth));
7ef5fefc
CF
3160
3161 auth->type = passwd->type;
3162
3163 auth->plength = passwd->len;
3164 memcpy(auth->passwd, passwd->passwd,
3165 MIN(sizeof(auth->passwd), sizeof(passwd->passwd)));
3166
3167 if (auth->type == ISIS_PASSWD_TYPE_CLEARTXT) {
3168 auth->length = passwd->len;
3169 memcpy(auth->value, passwd->passwd,
3170 MIN(sizeof(auth->value), sizeof(passwd->passwd)));
3171 }
3172
3173 append_item(&tlvs->isis_auth, (struct isis_item *)auth);
3174}
3175
3176void isis_tlvs_add_area_addresses(struct isis_tlvs *tlvs,
3177 struct list *addresses)
3178{
3179 struct listnode *node;
3180 struct area_addr *area_addr;
3181
3182 for (ALL_LIST_ELEMENTS_RO(addresses, node, area_addr)) {
3183 struct isis_area_address *a =
841791b6 3184 XCALLOC(MTYPE_ISIS_TLV, sizeof(*a));
7ef5fefc
CF
3185
3186 a->len = area_addr->addr_len;
3187 memcpy(a->addr, area_addr->area_addr, 20);
3188 append_item(&tlvs->area_addresses, (struct isis_item *)a);
3189 }
3190}
3191
3192void isis_tlvs_add_lan_neighbors(struct isis_tlvs *tlvs, struct list *neighbors)
3193{
3194 struct listnode *node;
d7c0a89a 3195 uint8_t *snpa;
7ef5fefc
CF
3196
3197 for (ALL_LIST_ELEMENTS_RO(neighbors, node, snpa)) {
3198 struct isis_lan_neighbor *n =
841791b6 3199 XCALLOC(MTYPE_ISIS_TLV, sizeof(*n));
7ef5fefc
CF
3200
3201 memcpy(n->mac, snpa, 6);
3202 append_item(&tlvs->lan_neighbor, (struct isis_item *)n);
3203 }
3204}
3205
3206void isis_tlvs_set_protocols_supported(struct isis_tlvs *tlvs,
3207 struct nlpids *nlpids)
3208{
3209 tlvs->protocols_supported.count = nlpids->count;
0a22ddfb 3210 XFREE(MTYPE_ISIS_TLV, tlvs->protocols_supported.protocols);
7ef5fefc
CF
3211 if (nlpids->count) {
3212 tlvs->protocols_supported.protocols =
841791b6 3213 XCALLOC(MTYPE_ISIS_TLV, nlpids->count);
7ef5fefc
CF
3214 memcpy(tlvs->protocols_supported.protocols, nlpids->nlpids,
3215 nlpids->count);
3216 } else {
3217 tlvs->protocols_supported.protocols = NULL;
3218 }
3219}
3220
3221void isis_tlvs_add_mt_router_info(struct isis_tlvs *tlvs, uint16_t mtid,
3222 bool overload, bool attached)
3223{
841791b6 3224 struct isis_mt_router_info *i = XCALLOC(MTYPE_ISIS_TLV, sizeof(*i));
7ef5fefc
CF
3225
3226 i->overload = overload;
3227 i->attached = attached;
3228 i->mtid = mtid;
3229 append_item(&tlvs->mt_router_info, (struct isis_item *)i);
3230}
3231
3232void isis_tlvs_add_ipv4_address(struct isis_tlvs *tlvs, struct in_addr *addr)
3233{
841791b6 3234 struct isis_ipv4_address *a = XCALLOC(MTYPE_ISIS_TLV, sizeof(*a));
7ef5fefc
CF
3235 a->addr = *addr;
3236 append_item(&tlvs->ipv4_address, (struct isis_item *)a);
3237}
3238
3239
3240void isis_tlvs_add_ipv4_addresses(struct isis_tlvs *tlvs,
3241 struct list *addresses)
3242{
3243 struct listnode *node;
3244 struct prefix_ipv4 *ip_addr;
bb5c77d7 3245 unsigned int addr_count = 0;
7ef5fefc 3246
bb5c77d7 3247 for (ALL_LIST_ELEMENTS_RO(addresses, node, ip_addr)) {
7ef5fefc 3248 isis_tlvs_add_ipv4_address(tlvs, &ip_addr->prefix);
bb5c77d7
CF
3249 addr_count++;
3250 if (addr_count >= 63)
3251 break;
3252 }
7ef5fefc
CF
3253}
3254
3255void isis_tlvs_add_ipv6_addresses(struct isis_tlvs *tlvs,
3256 struct list *addresses)
3257{
3258 struct listnode *node;
3259 struct prefix_ipv6 *ip_addr;
3260
3261 for (ALL_LIST_ELEMENTS_RO(addresses, node, ip_addr)) {
3262 struct isis_ipv6_address *a =
841791b6 3263 XCALLOC(MTYPE_ISIS_TLV, sizeof(*a));
7ef5fefc
CF
3264
3265 a->addr = ip_addr->prefix;
3266 append_item(&tlvs->ipv6_address, (struct isis_item *)a);
3267 }
3268}
3269
3270typedef bool (*auth_validator_func)(struct isis_passwd *passwd,
3271 struct stream *stream,
3272 struct isis_auth *auth, bool is_lsp);
3273
3274static bool auth_validator_cleartxt(struct isis_passwd *passwd,
3275 struct stream *stream,
3276 struct isis_auth *auth, bool is_lsp)
3277{
3278 return (auth->length == passwd->len
3279 && !memcmp(auth->value, passwd->passwd, passwd->len));
3280}
3281
3282static bool auth_validator_hmac_md5(struct isis_passwd *passwd,
3283 struct stream *stream,
3284 struct isis_auth *auth, bool is_lsp)
3285{
3286 uint8_t digest[16];
3287 uint16_t checksum;
3288 uint16_t rem_lifetime;
3289
3290 if (is_lsp)
3291 safe_auth_md5(stream, &checksum, &rem_lifetime);
3292
3293 memset(STREAM_DATA(stream) + auth->offset, 0, 16);
3294 hmac_md5(STREAM_DATA(stream), stream_get_endp(stream), passwd->passwd,
3295 passwd->len, digest);
3296 memcpy(STREAM_DATA(stream) + auth->offset, auth->value, 16);
3297
3298 bool rv = !memcmp(digest, auth->value, 16);
3299
3300 if (is_lsp)
3301 restore_auth_md5(stream, checksum, rem_lifetime);
3302
3303 return rv;
3304}
3305
3306static const auth_validator_func auth_validators[] = {
3307 [ISIS_PASSWD_TYPE_CLEARTXT] = auth_validator_cleartxt,
3308 [ISIS_PASSWD_TYPE_HMAC_MD5] = auth_validator_hmac_md5,
3309};
3310
3380c990
EDP
3311int isis_tlvs_auth_is_valid(struct isis_tlvs *tlvs, struct isis_passwd *passwd,
3312 struct stream *stream, bool is_lsp)
7ef5fefc
CF
3313{
3314 /* If no auth is set, always pass authentication */
3315 if (!passwd->type)
3380c990 3316 return ISIS_AUTH_OK;
7ef5fefc
CF
3317
3318 /* If we don't known how to validate the auth, return invalid */
3319 if (passwd->type >= array_size(auth_validators)
3320 || !auth_validators[passwd->type])
3380c990 3321 return ISIS_AUTH_NO_VALIDATOR;
7ef5fefc
CF
3322
3323 struct isis_auth *auth_head = (struct isis_auth *)tlvs->isis_auth.head;
3324 struct isis_auth *auth;
3325 for (auth = auth_head; auth; auth = auth->next) {
3326 if (auth->type == passwd->type)
3327 break;
3328 }
3329
3330 /* If matching auth TLV could not be found, return invalid */
3331 if (!auth)
3380c990
EDP
3332 return ISIS_AUTH_TYPE_FAILURE;
3333
7ef5fefc
CF
3334
3335 /* Perform validation and return result */
3380c990
EDP
3336 if (auth_validators[passwd->type](passwd, stream, auth, is_lsp))
3337 return ISIS_AUTH_OK;
3338 else
3339 return ISIS_AUTH_FAILURE;
7ef5fefc
CF
3340}
3341
3342bool isis_tlvs_area_addresses_match(struct isis_tlvs *tlvs,
3343 struct list *addresses)
3344{
3345 struct isis_area_address *addr_head;
3346
3347 addr_head = (struct isis_area_address *)tlvs->area_addresses.head;
3348 for (struct isis_area_address *addr = addr_head; addr;
3349 addr = addr->next) {
3350 struct listnode *node;
3351 struct area_addr *a;
3352
3353 for (ALL_LIST_ELEMENTS_RO(addresses, node, a)) {
3354 if (a->addr_len == addr->len
3355 && !memcmp(a->area_addr, addr->addr, addr->len))
3356 return true;
3357 }
3358 }
3359
3360 return false;
3361}
3362
3363static void tlvs_area_addresses_to_adj(struct isis_tlvs *tlvs,
3364 struct isis_adjacency *adj,
3365 bool *changed)
3366{
3367 if (adj->area_address_count != tlvs->area_addresses.count) {
3368 *changed = true;
3369 adj->area_address_count = tlvs->area_addresses.count;
3370 adj->area_addresses = XREALLOC(
3371 MTYPE_ISIS_ADJACENCY_INFO, adj->area_addresses,
3372 adj->area_address_count * sizeof(*adj->area_addresses));
3373 }
3374
3375 struct isis_area_address *addr = NULL;
3376 for (unsigned int i = 0; i < tlvs->area_addresses.count; i++) {
3377 if (!addr)
3378 addr = (struct isis_area_address *)
3379 tlvs->area_addresses.head;
3380 else
3381 addr = addr->next;
3382
3383 if (adj->area_addresses[i].addr_len == addr->len
3384 && !memcmp(adj->area_addresses[i].area_addr, addr->addr,
3385 addr->len)) {
3386 continue;
3387 }
3388
3389 *changed = true;
3390 adj->area_addresses[i].addr_len = addr->len;
3391 memcpy(adj->area_addresses[i].area_addr, addr->addr, addr->len);
3392 }
3393}
3394
3395static void tlvs_protocols_supported_to_adj(struct isis_tlvs *tlvs,
3396 struct isis_adjacency *adj,
3397 bool *changed)
3398{
3399 bool ipv4_supported = false, ipv6_supported = false;
3400
3401 for (uint8_t i = 0; i < tlvs->protocols_supported.count; i++) {
3402 if (tlvs->protocols_supported.protocols[i] == NLPID_IP)
3403 ipv4_supported = true;
3404 if (tlvs->protocols_supported.protocols[i] == NLPID_IPV6)
3405 ipv6_supported = true;
3406 }
3407
3e300703 3408 struct nlpids reduced = {};
7ef5fefc
CF
3409
3410 if (ipv4_supported && ipv6_supported) {
3411 reduced.count = 2;
3412 reduced.nlpids[0] = NLPID_IP;
3413 reduced.nlpids[1] = NLPID_IPV6;
3414 } else if (ipv4_supported) {
3415 reduced.count = 1;
3416 reduced.nlpids[0] = NLPID_IP;
3417 } else if (ipv6_supported) {
3418 reduced.count = 1;
4773e4f8 3419 reduced.nlpids[0] = NLPID_IPV6;
7ef5fefc
CF
3420 } else {
3421 reduced.count = 0;
3422 }
3423
3424 if (adj->nlpids.count == reduced.count
3425 && !memcmp(adj->nlpids.nlpids, reduced.nlpids, reduced.count))
3426 return;
3427
3428 *changed = true;
3429 adj->nlpids.count = reduced.count;
3430 memcpy(adj->nlpids.nlpids, reduced.nlpids, reduced.count);
3431}
3432
3433static void tlvs_ipv4_addresses_to_adj(struct isis_tlvs *tlvs,
3434 struct isis_adjacency *adj,
3435 bool *changed)
3436{
3437 if (adj->ipv4_address_count != tlvs->ipv4_address.count) {
3438 *changed = true;
3439 adj->ipv4_address_count = tlvs->ipv4_address.count;
3440 adj->ipv4_addresses = XREALLOC(
3441 MTYPE_ISIS_ADJACENCY_INFO, adj->ipv4_addresses,
3442 adj->ipv4_address_count * sizeof(*adj->ipv4_addresses));
3443 }
3444
3445 struct isis_ipv4_address *addr = NULL;
3446 for (unsigned int i = 0; i < tlvs->ipv4_address.count; i++) {
3447 if (!addr)
3448 addr = (struct isis_ipv4_address *)
3449 tlvs->ipv4_address.head;
3450 else
3451 addr = addr->next;
3452
3453 if (!memcmp(&adj->ipv4_addresses[i], &addr->addr,
3454 sizeof(addr->addr)))
3455 continue;
3456
3457 *changed = true;
3458 adj->ipv4_addresses[i] = addr->addr;
3459 }
3460}
3461
3462static void tlvs_ipv6_addresses_to_adj(struct isis_tlvs *tlvs,
3463 struct isis_adjacency *adj,
3464 bool *changed)
3465{
3466 if (adj->ipv6_address_count != tlvs->ipv6_address.count) {
3467 *changed = true;
3468 adj->ipv6_address_count = tlvs->ipv6_address.count;
3469 adj->ipv6_addresses = XREALLOC(
3470 MTYPE_ISIS_ADJACENCY_INFO, adj->ipv6_addresses,
3471 adj->ipv6_address_count * sizeof(*adj->ipv6_addresses));
3472 }
3473
3474 struct isis_ipv6_address *addr = NULL;
3475 for (unsigned int i = 0; i < tlvs->ipv6_address.count; i++) {
3476 if (!addr)
3477 addr = (struct isis_ipv6_address *)
3478 tlvs->ipv6_address.head;
3479 else
3480 addr = addr->next;
3481
3482 if (!memcmp(&adj->ipv6_addresses[i], &addr->addr,
3483 sizeof(addr->addr)))
3484 continue;
3485
3486 *changed = true;
3487 adj->ipv6_addresses[i] = addr->addr;
3488 }
3489}
3490
3491void isis_tlvs_to_adj(struct isis_tlvs *tlvs, struct isis_adjacency *adj,
3492 bool *changed)
3493{
3494 *changed = false;
3495
3496 tlvs_area_addresses_to_adj(tlvs, adj, changed);
3497 tlvs_protocols_supported_to_adj(tlvs, adj, changed);
3498 tlvs_ipv4_addresses_to_adj(tlvs, adj, changed);
3499 tlvs_ipv6_addresses_to_adj(tlvs, adj, changed);
3500}
3501
3502bool isis_tlvs_own_snpa_found(struct isis_tlvs *tlvs, uint8_t *snpa)
3503{
3504 struct isis_lan_neighbor *ne_head;
3505
3506 ne_head = (struct isis_lan_neighbor *)tlvs->lan_neighbor.head;
3507 for (struct isis_lan_neighbor *ne = ne_head; ne; ne = ne->next) {
3508 if (!memcmp(ne->mac, snpa, ETH_ALEN))
3509 return true;
3510 }
3511
3512 return false;
3513}
3514
3515void isis_tlvs_add_lsp_entry(struct isis_tlvs *tlvs, struct isis_lsp *lsp)
3516{
841791b6 3517 struct isis_lsp_entry *entry = XCALLOC(MTYPE_ISIS_TLV, sizeof(*entry));
7ef5fefc
CF
3518
3519 entry->rem_lifetime = lsp->hdr.rem_lifetime;
3520 memcpy(entry->id, lsp->hdr.lsp_id, ISIS_SYS_ID_LEN + 2);
3521 entry->checksum = lsp->hdr.checksum;
3522 entry->seqno = lsp->hdr.seqno;
3523 entry->lsp = lsp;
3524
3525 append_item(&tlvs->lsp_entries, (struct isis_item *)entry);
3526}
3527
3528void isis_tlvs_add_csnp_entries(struct isis_tlvs *tlvs, uint8_t *start_id,
3529 uint8_t *stop_id, uint16_t num_lsps,
4bef0ec4
DL
3530 struct lspdb_head *head,
3531 struct isis_lsp **last_lsp)
7ef5fefc 3532{
4bef0ec4
DL
3533 struct isis_lsp searchfor;
3534 struct isis_lsp *first, *lsp;
3535
3536 memcpy(&searchfor.hdr.lsp_id, start_id, sizeof(searchfor.hdr.lsp_id));
3537 first = lspdb_find_gteq(head, &searchfor);
7ef5fefc
CF
3538 if (!first)
3539 return;
3540
4bef0ec4
DL
3541 for_each_from (lspdb, head, lsp, first) {
3542 if (memcmp(lsp->hdr.lsp_id, stop_id, sizeof(lsp->hdr.lsp_id))
3543 > 0 || tlvs->lsp_entries.count == num_lsps)
7ef5fefc 3544 break;
4bef0ec4
DL
3545
3546 isis_tlvs_add_lsp_entry(tlvs, lsp);
3547 *last_lsp = lsp;
7ef5fefc
CF
3548 }
3549}
3550
3551void isis_tlvs_set_dynamic_hostname(struct isis_tlvs *tlvs,
3552 const char *hostname)
3553{
841791b6 3554 XFREE(MTYPE_ISIS_TLV, tlvs->hostname);
7ef5fefc 3555 if (hostname)
841791b6 3556 tlvs->hostname = XSTRDUP(MTYPE_ISIS_TLV, hostname);
7ef5fefc
CF
3557}
3558
3559void isis_tlvs_set_te_router_id(struct isis_tlvs *tlvs,
3560 const struct in_addr *id)
3561{
841791b6 3562 XFREE(MTYPE_ISIS_TLV, tlvs->te_router_id);
7ef5fefc
CF
3563 if (!id)
3564 return;
841791b6 3565 tlvs->te_router_id = XCALLOC(MTYPE_ISIS_TLV, sizeof(*id));
7ef5fefc
CF
3566 memcpy(tlvs->te_router_id, id, sizeof(*id));
3567}
3568
3569void isis_tlvs_add_oldstyle_ip_reach(struct isis_tlvs *tlvs,
3570 struct prefix_ipv4 *dest, uint8_t metric)
3571{
841791b6 3572 struct isis_oldstyle_ip_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
7ef5fefc
CF
3573
3574 r->metric = metric;
3575 memcpy(&r->prefix, dest, sizeof(*dest));
3576 apply_mask_ipv4(&r->prefix);
3577 append_item(&tlvs->oldstyle_ip_reach, (struct isis_item *)r);
3578}
3579
3580void isis_tlvs_add_extended_ip_reach(struct isis_tlvs *tlvs,
3581 struct prefix_ipv4 *dest, uint32_t metric)
3582{
841791b6 3583 struct isis_extended_ip_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
7ef5fefc
CF
3584
3585 r->metric = metric;
3586 memcpy(&r->prefix, dest, sizeof(*dest));
3587 apply_mask_ipv4(&r->prefix);
3588 append_item(&tlvs->extended_ip_reach, (struct isis_item *)r);
3589}
3590
3591void isis_tlvs_add_ipv6_reach(struct isis_tlvs *tlvs, uint16_t mtid,
3592 struct prefix_ipv6 *dest, uint32_t metric)
3593{
841791b6 3594 struct isis_ipv6_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
7ef5fefc
CF
3595
3596 r->metric = metric;
3597 memcpy(&r->prefix, dest, sizeof(*dest));
3598 apply_mask_ipv6(&r->prefix);
3599
3600 struct isis_item_list *l;
3601 l = (mtid == ISIS_MT_IPV4_UNICAST)
3602 ? &tlvs->ipv6_reach
3603 : isis_get_mt_items(&tlvs->mt_ipv6_reach, mtid);
3604 append_item(l, (struct isis_item *)r);
3605}
3606
d43d2df5
CF
3607void isis_tlvs_add_ipv6_dstsrc_reach(struct isis_tlvs *tlvs, uint16_t mtid,
3608 struct prefix_ipv6 *dest,
3609 struct prefix_ipv6 *src,
3610 uint32_t metric)
3611{
3612 isis_tlvs_add_ipv6_reach(tlvs, mtid, dest, metric);
3613 struct isis_item_list *l = isis_get_mt_items(&tlvs->mt_ipv6_reach,
3614 mtid);
3615
3616 struct isis_ipv6_reach *r = (struct isis_ipv6_reach*)last_item(l);
bd507085 3617 r->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IPV6_REACH);
d43d2df5
CF
3618 r->subtlvs->source_prefix = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*src));
3619 memcpy(r->subtlvs->source_prefix, src, sizeof(*src));
3620}
3621
7ef5fefc
CF
3622void isis_tlvs_add_oldstyle_reach(struct isis_tlvs *tlvs, uint8_t *id,
3623 uint8_t metric)
3624{
841791b6 3625 struct isis_oldstyle_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
7ef5fefc
CF
3626
3627 r->metric = metric;
3628 memcpy(r->id, id, sizeof(r->id));
3629 append_item(&tlvs->oldstyle_reach, (struct isis_item *)r);
3630}
3631
3632void isis_tlvs_add_extended_reach(struct isis_tlvs *tlvs, uint16_t mtid,
3633 uint8_t *id, uint32_t metric,
3634 uint8_t *subtlvs, uint8_t subtlv_len)
3635{
841791b6 3636 struct isis_extended_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
7ef5fefc
CF
3637
3638 memcpy(r->id, id, sizeof(r->id));
3639 r->metric = metric;
3640 if (subtlvs && subtlv_len) {
841791b6 3641 r->subtlvs = XCALLOC(MTYPE_ISIS_TLV, subtlv_len);
7ef5fefc
CF
3642 memcpy(r->subtlvs, subtlvs, subtlv_len);
3643 r->subtlv_len = subtlv_len;
3644 }
3645
3646 struct isis_item_list *l;
3647 if (mtid == ISIS_MT_IPV4_UNICAST)
3648 l = &tlvs->extended_reach;
3649 else
3650 l = isis_get_mt_items(&tlvs->mt_reach, mtid);
3651 append_item(l, (struct isis_item *)r);
3652}
3653
9fe21208
CF
3654void isis_tlvs_add_threeway_adj(struct isis_tlvs *tlvs,
3655 enum isis_threeway_state state,
3656 uint32_t local_circuit_id,
3657 const uint8_t *neighbor_id,
3658 uint32_t neighbor_circuit_id)
3659{
3660 assert(!tlvs->threeway_adj);
3661
3662 tlvs->threeway_adj = XCALLOC(MTYPE_ISIS_TLV, sizeof(*tlvs->threeway_adj));
3663 tlvs->threeway_adj->state = state;
3664 tlvs->threeway_adj->local_circuit_id = local_circuit_id;
3665
3666 if (neighbor_id) {
3667 tlvs->threeway_adj->neighbor_set = true;
3668 memcpy(tlvs->threeway_adj->neighbor_id, neighbor_id, 6);
3669 tlvs->threeway_adj->neighbor_circuit_id = neighbor_circuit_id;
3670 }
3671}
3672
41a145f1
CF
3673void isis_tlvs_add_spine_leaf(struct isis_tlvs *tlvs, uint8_t tier,
3674 bool has_tier, bool is_leaf, bool is_spine,
3675 bool is_backup)
3676{
3677 assert(!tlvs->spine_leaf);
3678
3679 tlvs->spine_leaf = XCALLOC(MTYPE_ISIS_TLV, sizeof(*tlvs->spine_leaf));
3680
3681 if (has_tier) {
3682 tlvs->spine_leaf->tier = tier;
3683 }
3684
3685 tlvs->spine_leaf->has_tier = has_tier;
3686 tlvs->spine_leaf->is_leaf = is_leaf;
3687 tlvs->spine_leaf->is_spine = is_spine;
3688 tlvs->spine_leaf->is_backup = is_backup;
3689}
3690
7ef5fefc
CF
3691struct isis_mt_router_info *
3692isis_tlvs_lookup_mt_router_info(struct isis_tlvs *tlvs, uint16_t mtid)
3693{
3694 if (tlvs->mt_router_info_empty)
3695 return NULL;
3696
3697 struct isis_mt_router_info *rv;
3698 for (rv = (struct isis_mt_router_info *)tlvs->mt_router_info.head; rv;
3699 rv = rv->next) {
3700 if (rv->mtid == mtid)
3701 return rv;
3702 }
3703
3704 return NULL;
3705}
2c92bee4
CF
3706
3707void isis_tlvs_set_purge_originator(struct isis_tlvs *tlvs,
3708 const uint8_t *generator,
3709 const uint8_t *sender)
3710{
3711 assert(!tlvs->purge_originator);
3712
3713 tlvs->purge_originator = XCALLOC(MTYPE_ISIS_TLV,
3714 sizeof(*tlvs->purge_originator));
3715 memcpy(tlvs->purge_originator->generator, generator,
3716 sizeof(tlvs->purge_originator->generator));
3717 if (sender) {
3718 tlvs->purge_originator->sender_set = true;
3719 memcpy(tlvs->purge_originator->sender, sender,
3720 sizeof(tlvs->purge_originator->sender));
3721 }
3722}