]> git.proxmox.com Git - mirror_frr.git/blame - isisd/isis_tlvs.c
isisd: The RFC states that v6 addresses are limited to 16 in a hello packet
[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,
fdc615a4 408 int indent, bool *unpacked_known_tlvs);
7ef5fefc
CF
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,
fdc615a4 799 log, NULL, indent + 4, NULL)) {
7ef5fefc
CF
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");
1275}
1276
1277static void free_item_extended_ip_reach(struct isis_item *i)
1278{
1279 struct isis_extended_ip_reach *item =
1280 (struct isis_extended_ip_reach *)i;
bd507085 1281 isis_free_subtlvs(item->subtlvs);
841791b6 1282 XFREE(MTYPE_ISIS_TLV, item);
7ef5fefc
CF
1283}
1284
1285static int pack_item_extended_ip_reach(struct isis_item *i, struct stream *s)
1286{
1287 struct isis_extended_ip_reach *r = (struct isis_extended_ip_reach *)i;
1288 uint8_t control;
1289
1290 if (STREAM_WRITEABLE(s) < 5)
1291 return 1;
1292 stream_putl(s, r->metric);
1293
1294 control = r->down ? ISIS_EXTENDED_IP_REACH_DOWN : 0;
1295 control |= r->prefix.prefixlen;
bd507085
CF
1296 control |= r->subtlvs ? ISIS_EXTENDED_IP_REACH_SUBTLV : 0;
1297
7ef5fefc
CF
1298 stream_putc(s, control);
1299
1300 if (STREAM_WRITEABLE(s) < (unsigned)PSIZE(r->prefix.prefixlen))
1301 return 1;
1302 stream_put(s, &r->prefix.prefix.s_addr, PSIZE(r->prefix.prefixlen));
bd507085
CF
1303
1304 if (r->subtlvs)
1305 return pack_subtlvs(r->subtlvs, s);
7ef5fefc
CF
1306 return 0;
1307}
1308
1309static int unpack_item_extended_ip_reach(uint16_t mtid, uint8_t len,
1310 struct stream *s, struct sbuf *log,
1311 void *dest, int indent)
1312{
1313 struct isis_tlvs *tlvs = dest;
1314 struct isis_extended_ip_reach *rv = NULL;
1315 size_t consume;
1316 uint8_t control, subtlv_len;
1317 struct isis_item_list *items;
1318
1319 if (mtid == ISIS_MT_IPV4_UNICAST) {
1320 items = &tlvs->extended_ip_reach;
1321 } else {
1322 items = isis_get_mt_items(&tlvs->mt_ip_reach, mtid);
1323 }
1324
1325 sbuf_push(log, indent, "Unpacking %s IPv4 reachability...\n",
1326 (mtid == ISIS_MT_IPV4_UNICAST) ? "extended" : "mt");
1327
1328 consume = 5;
1329 if (len < consume) {
98c5bc15
CF
1330 sbuf_push(log, indent,
1331 "Not enough data left. (expected 5 or more bytes, got %" PRIu8 ")\n",
1332 len);
7ef5fefc
CF
1333 goto out;
1334 }
1335
841791b6 1336 rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1337
1338 rv->metric = stream_getl(s);
1339 control = stream_getc(s);
1340 rv->down = (control & ISIS_EXTENDED_IP_REACH_DOWN);
1341 rv->prefix.family = AF_INET;
1342 rv->prefix.prefixlen = control & 0x3f;
1343 if (rv->prefix.prefixlen > 32) {
1344 sbuf_push(log, indent, "Prefixlen %u is inplausible for IPv4\n",
1345 rv->prefix.prefixlen);
1346 goto out;
1347 }
1348
1349 consume += PSIZE(rv->prefix.prefixlen);
1350 if (len < consume) {
98c5bc15
CF
1351 sbuf_push(log, indent,
1352 "Expected %u bytes of prefix, but only %u bytes available.\n",
1353 PSIZE(rv->prefix.prefixlen), len - 5);
7ef5fefc
CF
1354 goto out;
1355 }
1356 stream_get(&rv->prefix.prefix.s_addr, s, PSIZE(rv->prefix.prefixlen));
1357 in_addr_t orig_prefix = rv->prefix.prefix.s_addr;
1358 apply_mask_ipv4(&rv->prefix);
1359 if (orig_prefix != rv->prefix.prefix.s_addr)
1360 sbuf_push(log, indent + 2,
1361 "WARNING: Prefix had hostbits set.\n");
1362 format_item_extended_ip_reach(mtid, (struct isis_item *)rv, log,
1363 indent + 2);
1364
1365 if (control & ISIS_EXTENDED_IP_REACH_SUBTLV) {
1366 consume += 1;
1367 if (len < consume) {
98c5bc15
CF
1368 sbuf_push(log, indent,
1369 "Expected 1 byte of subtlv len, but no more data present.\n");
7ef5fefc
CF
1370 goto out;
1371 }
1372 subtlv_len = stream_getc(s);
1373
1374 if (!subtlv_len) {
98c5bc15
CF
1375 sbuf_push(log, indent + 2,
1376 " WARNING: subtlv bit is set, but there are no subtlvs.\n");
7ef5fefc
CF
1377 }
1378 consume += subtlv_len;
1379 if (len < consume) {
98c5bc15
CF
1380 sbuf_push(log, indent,
1381 "Expected %" PRIu8
1382 " bytes of subtlvs, but only %u bytes available.\n",
1383 subtlv_len,
1384 len - 6 - PSIZE(rv->prefix.prefixlen));
7ef5fefc
CF
1385 goto out;
1386 }
bd507085
CF
1387
1388 rv->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IP_REACH);
fdc615a4
CF
1389 bool unpacked_known_tlvs = false;
1390
bd507085 1391 if (unpack_tlvs(ISIS_CONTEXT_SUBTLV_IP_REACH, subtlv_len, s,
fdc615a4 1392 log, rv->subtlvs, indent + 4, &unpacked_known_tlvs)) {
bd507085
CF
1393 goto out;
1394 }
fdc615a4
CF
1395 if (!unpacked_known_tlvs) {
1396 isis_free_subtlvs(rv->subtlvs);
1397 rv->subtlvs = NULL;
1398 }
7ef5fefc
CF
1399 }
1400
1401 append_item(items, (struct isis_item *)rv);
1402 return 0;
1403out:
1404 if (rv)
1405 free_item_extended_ip_reach((struct isis_item *)rv);
1406 return 1;
1407}
1408
1409/* Functions related to TLV 137 Dynamic Hostname */
1410
1411static char *copy_tlv_dynamic_hostname(const char *hostname)
1412{
1413 if (!hostname)
1414 return NULL;
1415
841791b6 1416 return XSTRDUP(MTYPE_ISIS_TLV, hostname);
7ef5fefc
CF
1417}
1418
1419static void format_tlv_dynamic_hostname(const char *hostname, struct sbuf *buf,
1420 int indent)
1421{
1422 if (!hostname)
1423 return;
1424
1425 sbuf_push(buf, indent, "Hostname: %s\n", hostname);
1426}
1427
1428static void free_tlv_dynamic_hostname(char *hostname)
1429{
841791b6 1430 XFREE(MTYPE_ISIS_TLV, hostname);
7ef5fefc
CF
1431}
1432
1433static int pack_tlv_dynamic_hostname(const char *hostname, struct stream *s)
1434{
1435 if (!hostname)
1436 return 0;
1437
1438 uint8_t name_len = strlen(hostname);
1439
1440 if (STREAM_WRITEABLE(s) < (unsigned)(2 + name_len))
1441 return 1;
1442
1443 stream_putc(s, ISIS_TLV_DYNAMIC_HOSTNAME);
1444 stream_putc(s, name_len);
1445 stream_put(s, hostname, name_len);
1446 return 0;
1447}
1448
1449static int unpack_tlv_dynamic_hostname(enum isis_tlv_context context,
1450 uint8_t tlv_type, uint8_t tlv_len,
1451 struct stream *s, struct sbuf *log,
1452 void *dest, int indent)
1453{
1454 struct isis_tlvs *tlvs = dest;
1455
1456 sbuf_push(log, indent, "Unpacking Dynamic Hostname TLV...\n");
1457 if (!tlv_len) {
1458 sbuf_push(log, indent, "WARNING: No hostname included\n");
1459 return 0;
1460 }
1461
1462 if (tlvs->hostname) {
1463 sbuf_push(log, indent,
1464 "WARNING: Hostname present multiple times.\n");
1465 stream_forward_getp(s, tlv_len);
1466 return 0;
1467 }
1468
841791b6 1469 tlvs->hostname = XCALLOC(MTYPE_ISIS_TLV, tlv_len + 1);
7ef5fefc
CF
1470 stream_get(tlvs->hostname, s, tlv_len);
1471 tlvs->hostname[tlv_len] = '\0';
1472
1473 bool sane = true;
1474 for (uint8_t i = 0; i < tlv_len; i++) {
1475 if ((unsigned char)tlvs->hostname[i] > 127
a7ce0ad1 1476 || !isprint((int)tlvs->hostname[i])) {
7ef5fefc
CF
1477 sane = false;
1478 tlvs->hostname[i] = '?';
1479 }
1480 }
1481 if (!sane) {
1482 sbuf_push(
1483 log, indent,
1484 "WARNING: Hostname contained non-printable/non-ascii characters.\n");
1485 }
1486
1487 return 0;
1488}
1489
41a145f1
CF
1490/* Functions related to TLV 150 Spine-Leaf-Extension */
1491
1492static struct isis_spine_leaf *copy_tlv_spine_leaf(
1493 const struct isis_spine_leaf *spine_leaf)
1494{
1495 if (!spine_leaf)
1496 return NULL;
1497
1498 struct isis_spine_leaf *rv = XMALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
1499 memcpy(rv, spine_leaf, sizeof(*rv));
1500
1501 return rv;
1502}
1503
1504static void format_tlv_spine_leaf(const struct isis_spine_leaf *spine_leaf,
1505 struct sbuf *buf, int indent)
1506{
1507 if (!spine_leaf)
1508 return;
1509
1510 sbuf_push(buf, indent, "Spine-Leaf-Extension:\n");
1511 if (spine_leaf->has_tier) {
1512 if (spine_leaf->tier == ISIS_TIER_UNDEFINED) {
1513 sbuf_push(buf, indent, " Tier: undefined\n");
1514 } else {
1515 sbuf_push(buf, indent, " Tier: %" PRIu8 "\n",
1516 spine_leaf->tier);
1517 }
1518 }
1519
1520 sbuf_push(buf, indent, " Flags:%s%s%s\n",
1521 spine_leaf->is_leaf ? " LEAF" : "",
1522 spine_leaf->is_spine ? " SPINE" : "",
1523 spine_leaf->is_backup ? " BACKUP" : "");
1524
1525}
1526
1527static void free_tlv_spine_leaf(struct isis_spine_leaf *spine_leaf)
1528{
1529 XFREE(MTYPE_ISIS_TLV, spine_leaf);
1530}
1531
1532#define ISIS_SPINE_LEAF_FLAG_TIER 0x08
1533#define ISIS_SPINE_LEAF_FLAG_BACKUP 0x04
1534#define ISIS_SPINE_LEAF_FLAG_SPINE 0x02
1535#define ISIS_SPINE_LEAF_FLAG_LEAF 0x01
1536
1537static int pack_tlv_spine_leaf(const struct isis_spine_leaf *spine_leaf,
1538 struct stream *s)
1539{
1540 if (!spine_leaf)
1541 return 0;
1542
1543 uint8_t tlv_len = 2;
1544
1545 if (STREAM_WRITEABLE(s) < (unsigned)(2 + tlv_len))
1546 return 1;
1547
1548 stream_putc(s, ISIS_TLV_SPINE_LEAF_EXT);
1549 stream_putc(s, tlv_len);
1550
1551 uint16_t spine_leaf_flags = 0;
1552
1553 if (spine_leaf->has_tier) {
1554 spine_leaf_flags |= ISIS_SPINE_LEAF_FLAG_TIER;
1555 spine_leaf_flags |= spine_leaf->tier << 12;
1556 }
1557
1558 if (spine_leaf->is_leaf)
1559 spine_leaf_flags |= ISIS_SPINE_LEAF_FLAG_LEAF;
1560
1561 if (spine_leaf->is_spine)
1562 spine_leaf_flags |= ISIS_SPINE_LEAF_FLAG_SPINE;
1563
1564 if (spine_leaf->is_backup)
1565 spine_leaf_flags |= ISIS_SPINE_LEAF_FLAG_BACKUP;
1566
1567 stream_putw(s, spine_leaf_flags);
1568
1569 return 0;
1570}
1571
1572static int unpack_tlv_spine_leaf(enum isis_tlv_context context,
1573 uint8_t tlv_type, uint8_t tlv_len,
1574 struct stream *s, struct sbuf *log,
1575 void *dest, int indent)
1576{
1577 struct isis_tlvs *tlvs = dest;
1578
1579 sbuf_push(log, indent, "Unpacking Spine Leaf Extension TLV...\n");
1580 if (tlv_len < 2) {
1581 sbuf_push(log, indent, "WARNING: Unexepected TLV size\n");
1582 stream_forward_getp(s, tlv_len);
1583 return 0;
1584 }
1585
1586 if (tlvs->spine_leaf) {
1587 sbuf_push(log, indent,
1588 "WARNING: Spine Leaf Extension TLV present multiple times.\n");
1589 stream_forward_getp(s, tlv_len);
1590 return 0;
1591 }
1592
1593 tlvs->spine_leaf = XCALLOC(MTYPE_ISIS_TLV, sizeof(*tlvs->spine_leaf));
1594
1595 uint16_t spine_leaf_flags = stream_getw(s);
1596
1597 if (spine_leaf_flags & ISIS_SPINE_LEAF_FLAG_TIER) {
1598 tlvs->spine_leaf->has_tier = true;
1599 tlvs->spine_leaf->tier = spine_leaf_flags >> 12;
1600 }
1601
1602 tlvs->spine_leaf->is_leaf = spine_leaf_flags & ISIS_SPINE_LEAF_FLAG_LEAF;
1603 tlvs->spine_leaf->is_spine = spine_leaf_flags & ISIS_SPINE_LEAF_FLAG_SPINE;
1604 tlvs->spine_leaf->is_backup = spine_leaf_flags & ISIS_SPINE_LEAF_FLAG_BACKUP;
1605
1606 stream_forward_getp(s, tlv_len - 2);
1607 return 0;
1608}
1609
9fe21208
CF
1610/* Functions related to TLV 240 P2P Three-Way Adjacency */
1611
1612const char *isis_threeway_state_name(enum isis_threeway_state state)
1613{
1614 switch (state) {
1615 case ISIS_THREEWAY_DOWN:
1616 return "Down";
1617 case ISIS_THREEWAY_INITIALIZING:
1618 return "Initializing";
1619 case ISIS_THREEWAY_UP:
1620 return "Up";
1621 default:
1622 return "Invalid!";
1623 }
1624}
1625
1626static struct isis_threeway_adj *copy_tlv_threeway_adj(
1627 const struct isis_threeway_adj *threeway_adj)
1628{
1629 if (!threeway_adj)
1630 return NULL;
1631
1632 struct isis_threeway_adj *rv = XMALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
1633 memcpy(rv, threeway_adj, sizeof(*rv));
1634
1635 return rv;
1636}
1637
1638static void format_tlv_threeway_adj(const struct isis_threeway_adj *threeway_adj,
1639 struct sbuf *buf, int indent)
1640{
1641 if (!threeway_adj)
1642 return;
1643
1644 sbuf_push(buf, indent, "P2P Three-Way Adjacency:\n");
1645 sbuf_push(buf, indent, " State: %s (%d)\n",
1646 isis_threeway_state_name(threeway_adj->state),
1647 threeway_adj->state);
1648 sbuf_push(buf, indent, " Extended Local Circuit ID: %" PRIu32 "\n",
1649 threeway_adj->local_circuit_id);
1650 if (!threeway_adj->neighbor_set)
1651 return;
1652
1653 sbuf_push(buf, indent, " Neighbor System ID: %s\n",
1654 isis_format_id(threeway_adj->neighbor_id, 6));
1655 sbuf_push(buf, indent, " Neighbor Extended Circuit ID: %" PRIu32 "\n",
1656 threeway_adj->neighbor_circuit_id);
1657}
1658
1659static void free_tlv_threeway_adj(struct isis_threeway_adj *threeway_adj)
1660{
1661 XFREE(MTYPE_ISIS_TLV, threeway_adj);
1662}
1663
1664static int pack_tlv_threeway_adj(const struct isis_threeway_adj *threeway_adj,
1665 struct stream *s)
1666{
1667 if (!threeway_adj)
1668 return 0;
1669
1670 uint8_t tlv_len = (threeway_adj->neighbor_set) ? 15 : 5;
1671
1672 if (STREAM_WRITEABLE(s) < (unsigned)(2 + tlv_len))
1673 return 1;
1674
1675 stream_putc(s, ISIS_TLV_THREE_WAY_ADJ);
1676 stream_putc(s, tlv_len);
1677 stream_putc(s, threeway_adj->state);
1678 stream_putl(s, threeway_adj->local_circuit_id);
1679
1680 if (threeway_adj->neighbor_set) {
1681 stream_put(s, threeway_adj->neighbor_id, 6);
1682 stream_putl(s, threeway_adj->neighbor_circuit_id);
1683 }
1684
1685 return 0;
1686}
1687
1688static int unpack_tlv_threeway_adj(enum isis_tlv_context context,
1689 uint8_t tlv_type, uint8_t tlv_len,
1690 struct stream *s, struct sbuf *log,
1691 void *dest, int indent)
1692{
1693 struct isis_tlvs *tlvs = dest;
1694
1695 sbuf_push(log, indent, "Unpacking P2P Three-Way Adjacency TLV...\n");
1696 if (tlv_len != 5 && tlv_len != 15) {
1697 sbuf_push(log, indent, "WARNING: Unexepected TLV size\n");
1698 stream_forward_getp(s, tlv_len);
1699 return 0;
1700 }
1701
1702 if (tlvs->threeway_adj) {
1703 sbuf_push(log, indent,
1704 "WARNING: P2P Three-Way Adjacency TLV present multiple times.\n");
1705 stream_forward_getp(s, tlv_len);
1706 return 0;
1707 }
1708
1709 tlvs->threeway_adj = XCALLOC(MTYPE_ISIS_TLV, sizeof(*tlvs->threeway_adj));
1710
1711 tlvs->threeway_adj->state = stream_getc(s);
1712 tlvs->threeway_adj->local_circuit_id = stream_getl(s);
1713
1714 if (tlv_len == 15) {
1715 tlvs->threeway_adj->neighbor_set = true;
1716 stream_get(tlvs->threeway_adj->neighbor_id, s, 6);
1717 tlvs->threeway_adj->neighbor_circuit_id = stream_getl(s);
1718 }
1719
1720 return 0;
1721}
1722
7ef5fefc
CF
1723/* Functions related to TLVs 236/237 IPv6/MT-IPv6 reach */
1724
1725static struct isis_item *copy_item_ipv6_reach(struct isis_item *i)
1726{
1727 struct isis_ipv6_reach *r = (struct isis_ipv6_reach *)i;
841791b6 1728 struct isis_ipv6_reach *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1729 rv->metric = r->metric;
1730 rv->down = r->down;
1731 rv->external = r->external;
1732 rv->prefix = r->prefix;
1733 rv->subtlvs = copy_subtlvs(r->subtlvs);
1734
1735 return (struct isis_item *)rv;
1736}
1737
1738static void format_item_ipv6_reach(uint16_t mtid, struct isis_item *i,
1739 struct sbuf *buf, int indent)
1740{
1741 struct isis_ipv6_reach *r = (struct isis_ipv6_reach *)i;
1742 char prefixbuf[PREFIX2STR_BUFFER];
1743
1744 sbuf_push(buf, indent, "%sIPv6 Reachability: %s (Metric: %u)%s%s",
1745 (mtid == ISIS_MT_IPV4_UNICAST) ? "" : "MT ",
1746 prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf)),
98c5bc15
CF
1747 r->metric,
1748 r->down ? " Down" : "",
7ef5fefc
CF
1749 r->external ? " External" : "");
1750 if (mtid != ISIS_MT_IPV4_UNICAST)
1751 sbuf_push(buf, 0, " %s", isis_mtid2str(mtid));
1752 sbuf_push(buf, 0, "\n");
1753
1754 if (r->subtlvs) {
1755 sbuf_push(buf, indent, " Subtlvs:\n");
1756 format_subtlvs(r->subtlvs, buf, indent + 4);
1757 }
1758}
1759
1760static void free_item_ipv6_reach(struct isis_item *i)
1761{
1762 struct isis_ipv6_reach *item = (struct isis_ipv6_reach *)i;
1763
1764 isis_free_subtlvs(item->subtlvs);
841791b6 1765 XFREE(MTYPE_ISIS_TLV, item);
7ef5fefc
CF
1766}
1767
1768static int pack_item_ipv6_reach(struct isis_item *i, struct stream *s)
1769{
1770 struct isis_ipv6_reach *r = (struct isis_ipv6_reach *)i;
1771 uint8_t control;
1772
1773 if (STREAM_WRITEABLE(s) < 6)
1774 return 1;
1775 stream_putl(s, r->metric);
1776
1777 control = r->down ? ISIS_IPV6_REACH_DOWN : 0;
1778 control |= r->external ? ISIS_IPV6_REACH_EXTERNAL : 0;
1779 control |= r->subtlvs ? ISIS_IPV6_REACH_SUBTLV : 0;
1780
1781 stream_putc(s, control);
1782 stream_putc(s, r->prefix.prefixlen);
1783
1784 if (STREAM_WRITEABLE(s) < (unsigned)PSIZE(r->prefix.prefixlen))
1785 return 1;
1786 stream_put(s, &r->prefix.prefix.s6_addr, PSIZE(r->prefix.prefixlen));
1787
1788 if (r->subtlvs)
1789 return pack_subtlvs(r->subtlvs, s);
1790
1791 return 0;
1792}
1793
1794static int unpack_item_ipv6_reach(uint16_t mtid, uint8_t len, struct stream *s,
1795 struct sbuf *log, void *dest, int indent)
1796{
1797 struct isis_tlvs *tlvs = dest;
1798 struct isis_ipv6_reach *rv = NULL;
1799 size_t consume;
1800 uint8_t control, subtlv_len;
1801 struct isis_item_list *items;
1802
1803 if (mtid == ISIS_MT_IPV4_UNICAST) {
1804 items = &tlvs->ipv6_reach;
1805 } else {
1806 items = isis_get_mt_items(&tlvs->mt_ipv6_reach, mtid);
1807 }
1808
1809 sbuf_push(log, indent, "Unpacking %sIPv6 reachability...\n",
1810 (mtid == ISIS_MT_IPV4_UNICAST) ? "" : "mt ");
1811 consume = 6;
1812 if (len < consume) {
98c5bc15
CF
1813 sbuf_push(log, indent,
1814 "Not enough data left. (expected 6 or more bytes, got %"
1815 PRIu8 ")\n",
1816 len);
7ef5fefc
CF
1817 goto out;
1818 }
1819
841791b6 1820 rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1821
1822 rv->metric = stream_getl(s);
1823 control = stream_getc(s);
1824 rv->down = (control & ISIS_IPV6_REACH_DOWN);
1825 rv->external = (control & ISIS_IPV6_REACH_EXTERNAL);
1826
1827 rv->prefix.family = AF_INET6;
1828 rv->prefix.prefixlen = stream_getc(s);
1829 if (rv->prefix.prefixlen > 128) {
1830 sbuf_push(log, indent, "Prefixlen %u is inplausible for IPv6\n",
1831 rv->prefix.prefixlen);
1832 goto out;
1833 }
1834
1835 consume += PSIZE(rv->prefix.prefixlen);
1836 if (len < consume) {
98c5bc15
CF
1837 sbuf_push(log, indent,
1838 "Expected %u bytes of prefix, but only %u bytes available.\n",
1839 PSIZE(rv->prefix.prefixlen), len - 6);
7ef5fefc
CF
1840 goto out;
1841 }
1842 stream_get(&rv->prefix.prefix.s6_addr, s, PSIZE(rv->prefix.prefixlen));
1843 struct in6_addr orig_prefix = rv->prefix.prefix;
1844 apply_mask_ipv6(&rv->prefix);
1845 if (memcmp(&orig_prefix, &rv->prefix.prefix, sizeof(orig_prefix)))
1846 sbuf_push(log, indent + 2,
1847 "WARNING: Prefix had hostbits set.\n");
1848 format_item_ipv6_reach(mtid, (struct isis_item *)rv, log, indent + 2);
1849
1850 if (control & ISIS_IPV6_REACH_SUBTLV) {
1851 consume += 1;
1852 if (len < consume) {
98c5bc15
CF
1853 sbuf_push(log, indent,
1854 "Expected 1 byte of subtlv len, but no more data persent.\n");
7ef5fefc
CF
1855 goto out;
1856 }
1857 subtlv_len = stream_getc(s);
1858
1859 if (!subtlv_len) {
98c5bc15
CF
1860 sbuf_push(log, indent + 2,
1861 " WARNING: subtlv bit set, but there are no subtlvs.\n");
7ef5fefc
CF
1862 }
1863 consume += subtlv_len;
1864 if (len < consume) {
98c5bc15
CF
1865 sbuf_push(log, indent,
1866 "Expected %" PRIu8
1867 " bytes of subtlvs, but only %u bytes available.\n",
1868 subtlv_len,
1869 len - 6 - PSIZE(rv->prefix.prefixlen));
7ef5fefc
CF
1870 goto out;
1871 }
1872
bd507085 1873 rv->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IPV6_REACH);
fdc615a4
CF
1874 bool unpacked_known_tlvs = false;
1875
7ef5fefc 1876 if (unpack_tlvs(ISIS_CONTEXT_SUBTLV_IPV6_REACH, subtlv_len, s,
fdc615a4 1877 log, rv->subtlvs, indent + 4, &unpacked_known_tlvs)) {
7ef5fefc
CF
1878 goto out;
1879 }
fdc615a4
CF
1880 if (!unpacked_known_tlvs) {
1881 isis_free_subtlvs(rv->subtlvs);
1882 rv->subtlvs = NULL;
1883 }
7ef5fefc
CF
1884 }
1885
1886 append_item(items, (struct isis_item *)rv);
1887 return 0;
1888out:
1889 if (rv)
1890 free_item_ipv6_reach((struct isis_item *)rv);
1891 return 1;
1892}
1893
1894/* Functions related to TLV 10 Authentication */
1895static struct isis_item *copy_item_auth(struct isis_item *i)
1896{
1897 struct isis_auth *auth = (struct isis_auth *)i;
841791b6 1898 struct isis_auth *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1899
1900 rv->type = auth->type;
1901 rv->length = auth->length;
1902 memcpy(rv->value, auth->value, sizeof(rv->value));
1903 return (struct isis_item *)rv;
1904}
1905
1906static void format_item_auth(uint16_t mtid, struct isis_item *i,
1907 struct sbuf *buf, int indent)
1908{
1909 struct isis_auth *auth = (struct isis_auth *)i;
1910 char obuf[768];
1911
1912 sbuf_push(buf, indent, "Authentication:\n");
1913 switch (auth->type) {
1914 case ISIS_PASSWD_TYPE_CLEARTXT:
1915 zlog_sanitize(obuf, sizeof(obuf), auth->value, auth->length);
1916 sbuf_push(buf, indent, " Password: %s\n", obuf);
1917 break;
1918 case ISIS_PASSWD_TYPE_HMAC_MD5:
f7813c7c
A
1919 for (unsigned int j = 0; j < 16; j++) {
1920 snprintf(obuf + 2 * j, sizeof(obuf) - 2 * j,
1921 "%02" PRIx8, auth->value[j]);
7ef5fefc
CF
1922 }
1923 sbuf_push(buf, indent, " HMAC-MD5: %s\n", obuf);
1924 break;
1925 default:
1926 sbuf_push(buf, indent, " Unknown (%" PRIu8 ")\n", auth->type);
1927 break;
5b94ec50 1928 }
7ef5fefc
CF
1929}
1930
1931static void free_item_auth(struct isis_item *i)
1932{
841791b6 1933 XFREE(MTYPE_ISIS_TLV, i);
7ef5fefc
CF
1934}
1935
1936static int pack_item_auth(struct isis_item *i, struct stream *s)
1937{
1938 struct isis_auth *auth = (struct isis_auth *)i;
1939
1940 if (STREAM_WRITEABLE(s) < 1)
1941 return 1;
1942 stream_putc(s, auth->type);
1943
1944 switch (auth->type) {
1945 case ISIS_PASSWD_TYPE_CLEARTXT:
1946 if (STREAM_WRITEABLE(s) < auth->length)
1947 return 1;
1948 stream_put(s, auth->passwd, auth->length);
1949 break;
1950 case ISIS_PASSWD_TYPE_HMAC_MD5:
1951 if (STREAM_WRITEABLE(s) < 16)
1952 return 1;
1953 auth->offset = stream_get_endp(s);
1954 stream_put(s, NULL, 16);
1955 break;
1956 default:
1957 return 1;
1958 }
1959
1960 return 0;
1961}
1962
1963static int unpack_item_auth(uint16_t mtid, uint8_t len, struct stream *s,
1964 struct sbuf *log, void *dest, int indent)
1965{
1966 struct isis_tlvs *tlvs = dest;
1967
1968 sbuf_push(log, indent, "Unpack Auth TLV...\n");
1969 if (len < 1) {
1970 sbuf_push(
1971 log, indent,
1972 "Not enough data left.(Expected 1 bytes of auth type, got %" PRIu8
1973 ")\n",
1974 len);
1975 return 1;
1976 }
1977
841791b6 1978 struct isis_auth *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1979
1980 rv->type = stream_getc(s);
1981 rv->length = len - 1;
1982
1983 if (rv->type == ISIS_PASSWD_TYPE_HMAC_MD5 && rv->length != 16) {
1984 sbuf_push(
1985 log, indent,
1986 "Unexpected auth length for HMAC-MD5 (expected 16, got %" PRIu8
1987 ")\n",
1988 rv->length);
841791b6 1989 XFREE(MTYPE_ISIS_TLV, rv);
7ef5fefc
CF
1990 return 1;
1991 }
1992
1993 rv->offset = stream_get_getp(s);
1994 stream_get(rv->value, s, rv->length);
1995 format_item_auth(mtid, (struct isis_item *)rv, log, indent + 2);
1996 append_item(&tlvs->isis_auth, (struct isis_item *)rv);
1997 return 0;
1998}
1999
5f77d901
CF
2000/* Functions related to TLV 13 Purge Originator */
2001
2002static struct isis_purge_originator *copy_tlv_purge_originator(
2003 struct isis_purge_originator *poi)
2004{
2005 if (!poi)
2006 return NULL;
2007
2008 struct isis_purge_originator *rv;
2009
2010 rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
2011 rv->sender_set = poi->sender_set;
2012 memcpy(rv->generator, poi->generator, sizeof(rv->generator));
2013 if (poi->sender_set)
2014 memcpy(rv->sender, poi->sender, sizeof(rv->sender));
2015 return rv;
2016}
2017
2018static void format_tlv_purge_originator(struct isis_purge_originator *poi,
2019 struct sbuf *buf, int indent)
2020{
2021 if (!poi)
2022 return;
2023
2024 sbuf_push(buf, indent, "Purge Originator Identification:\n");
2025 sbuf_push(buf, indent, " Generator: %s\n",
2026 isis_format_id(poi->generator, sizeof(poi->generator)));
2027 if (poi->sender_set) {
2028 sbuf_push(buf, indent, " Received-From: %s\n",
2029 isis_format_id(poi->sender, sizeof(poi->sender)));
2030 }
2031}
2032
2033static void free_tlv_purge_originator(struct isis_purge_originator *poi)
2034{
2035 XFREE(MTYPE_ISIS_TLV, poi);
2036}
2037
2038static int pack_tlv_purge_originator(struct isis_purge_originator *poi,
2039 struct stream *s)
2040{
2041 if (!poi)
2042 return 0;
2043
2044 uint8_t data_len = 1 + sizeof(poi->generator);
2045
2046 if (poi->sender_set)
2047 data_len += sizeof(poi->sender);
2048
2049 if (STREAM_WRITEABLE(s) < (unsigned)(2 + data_len))
2050 return 1;
2051
2052 stream_putc(s, ISIS_TLV_PURGE_ORIGINATOR);
2053 stream_putc(s, data_len);
2054 stream_putc(s, poi->sender_set ? 2 : 1);
2055 stream_put(s, poi->generator, sizeof(poi->generator));
2056 if (poi->sender_set)
2057 stream_put(s, poi->sender, sizeof(poi->sender));
2058 return 0;
2059}
2060
2061static int unpack_tlv_purge_originator(enum isis_tlv_context context,
2062 uint8_t tlv_type, uint8_t tlv_len,
2063 struct stream *s, struct sbuf *log,
2064 void *dest, int indent)
2065{
2066 struct isis_tlvs *tlvs = dest;
3e300703 2067 struct isis_purge_originator poi = {};
5f77d901
CF
2068
2069 sbuf_push(log, indent, "Unpacking Purge Originator Identification TLV...\n");
2070 if (tlv_len < 7) {
2071 sbuf_push(log, indent, "Not enough data left. (Expected at least 7 bytes, got %"
2072 PRIu8 ")\n", tlv_len);
2073 return 1;
2074 }
2075
2076 uint8_t number_of_ids = stream_getc(s);
2077
2078 if (number_of_ids == 1) {
2079 poi.sender_set = false;
2080 } else if (number_of_ids == 2) {
2081 poi.sender_set = true;
2082 } else {
2083 sbuf_push(log, indent, "Got invalid value for number of system IDs: %"
2084 PRIu8 ")\n", number_of_ids);
2085 return 1;
2086 }
2087
2088 if (tlv_len != 1 + 6 * number_of_ids) {
2089 sbuf_push(log, indent, "Incorrect tlv len for number of IDs.\n");
2090 return 1;
2091 }
2092
2093 stream_get(poi.generator, s, sizeof(poi.generator));
2094 if (poi.sender_set)
2095 stream_get(poi.sender, s, sizeof(poi.sender));
2096
2097 if (tlvs->purge_originator) {
2098 sbuf_push(log, indent,
2099 "WARNING: Purge originator present multiple times, ignoring.\n");
2100 return 0;
2101 }
2102
2103 tlvs->purge_originator = copy_tlv_purge_originator(&poi);
2104 return 0;
2105}
2106
2107
7ef5fefc
CF
2108/* Functions relating to item TLVs */
2109
2110static void init_item_list(struct isis_item_list *items)
2111{
2112 items->head = NULL;
2113 items->tail = &items->head;
2114 items->count = 0;
2115}
2116
2117static struct isis_item *copy_item(enum isis_tlv_context context,
2118 enum isis_tlv_type type,
2119 struct isis_item *item)
2120{
2121 const struct tlv_ops *ops = tlv_table[context][type];
2122
2123 if (ops && ops->copy_item)
2124 return ops->copy_item(item);
2125
2126 assert(!"Unknown item tlv type!");
2127 return NULL;
2128}
2129
2130static void copy_items(enum isis_tlv_context context, enum isis_tlv_type type,
2131 struct isis_item_list *src, struct isis_item_list *dest)
2132{
2133 struct isis_item *item;
2134
2135 init_item_list(dest);
2136
2137 for (item = src->head; item; item = item->next) {
2138 append_item(dest, copy_item(context, type, item));
2139 }
2140}
2141
2142static void format_item(uint16_t mtid, enum isis_tlv_context context,
2143 enum isis_tlv_type type, struct isis_item *i,
2144 struct sbuf *buf, int indent)
2145{
2146 const struct tlv_ops *ops = tlv_table[context][type];
2147
2148 if (ops && ops->format_item) {
2149 ops->format_item(mtid, i, buf, indent);
2150 return;
2151 }
2152
2153 assert(!"Unknown item tlv type!");
2154}
2155
2156static void format_items_(uint16_t mtid, enum isis_tlv_context context,
2157 enum isis_tlv_type type, struct isis_item_list *items,
2158 struct sbuf *buf, int indent)
2159{
2160 struct isis_item *i;
2161
2162 for (i = items->head; i; i = i->next)
2163 format_item(mtid, context, type, i, buf, indent);
2164}
7ef5fefc
CF
2165
2166static void free_item(enum isis_tlv_context tlv_context,
2167 enum isis_tlv_type tlv_type, struct isis_item *item)
2168{
2169 const struct tlv_ops *ops = tlv_table[tlv_context][tlv_type];
2170
2171 if (ops && ops->free_item) {
2172 ops->free_item(item);
2173 return;
2174 }
2175
2176 assert(!"Unknown item tlv type!");
2177}
2178
2179static void free_items(enum isis_tlv_context context, enum isis_tlv_type type,
2180 struct isis_item_list *items)
2181{
2182 struct isis_item *item, *next_item;
2183
2184 for (item = items->head; item; item = next_item) {
2185 next_item = item->next;
2186 free_item(context, type, item);
2187 }
2188}
2189
2190static int pack_item(enum isis_tlv_context context, enum isis_tlv_type type,
2191 struct isis_item *i, struct stream *s,
2192 struct isis_tlvs **fragment_tlvs,
2193 struct pack_order_entry *pe, uint16_t mtid)
2194{
2195 const struct tlv_ops *ops = tlv_table[context][type];
2196
2197 if (ops && ops->pack_item) {
2198 return ops->pack_item(i, s);
2199 }
2200
2201 assert(!"Unknown item tlv type!");
2202 return 1;
2203}
2204
98c5bc15 2205static void add_item_to_fragment(struct isis_item *i, struct pack_order_entry *pe,
7ef5fefc
CF
2206 struct isis_tlvs *fragment_tlvs, uint16_t mtid)
2207{
2208 struct isis_item_list *l;
2209
2210 if (pe->how_to_pack == ISIS_ITEMS) {
98c5bc15 2211 l = (struct isis_item_list *)(((char *)fragment_tlvs) + pe->what_to_pack);
7ef5fefc
CF
2212 } else {
2213 struct isis_mt_item_list *m;
98c5bc15 2214 m = (struct isis_mt_item_list *)(((char *)fragment_tlvs) + pe->what_to_pack);
7ef5fefc
CF
2215 l = isis_get_mt_items(m, mtid);
2216 }
2217
2218 append_item(l, copy_item(pe->context, pe->type, i));
2219}
2220
2221static int pack_items_(uint16_t mtid, enum isis_tlv_context context,
2222 enum isis_tlv_type type, struct isis_item_list *items,
2223 struct stream *s, struct isis_tlvs **fragment_tlvs,
2224 struct pack_order_entry *pe,
2225 struct isis_tlvs *(*new_fragment)(struct list *l),
2226 struct list *new_fragment_arg)
2227{
2228 size_t len_pos, last_len, len;
2229 struct isis_item *item = NULL;
2230 int rv;
2231
2232 if (!items->head)
2233 return 0;
2234
2235top:
2236 if (STREAM_WRITEABLE(s) < 2)
2237 goto too_long;
2238
2239 stream_putc(s, type);
2240 len_pos = stream_get_endp(s);
2241 stream_putc(s, 0); /* Put 0 as length for now */
2242
2243 if (context == ISIS_CONTEXT_LSP && IS_COMPAT_MT_TLV(type)
2244 && mtid != ISIS_MT_IPV4_UNICAST) {
2245 if (STREAM_WRITEABLE(s) < 2)
2246 goto too_long;
2247 stream_putw(s, mtid);
2248 }
2249
2250 if (context == ISIS_CONTEXT_LSP && type == ISIS_TLV_OLDSTYLE_REACH) {
2251 if (STREAM_WRITEABLE(s) < 1)
2252 goto too_long;
2253 stream_putc(s, 0); /* Virtual flag is set to 0 */
2254 }
2255
2256 last_len = len = 0;
2257 for (item = item ? item : items->head; item; item = item->next) {
2258 rv = pack_item(context, type, item, s, fragment_tlvs, pe, mtid);
2259 if (rv)
2260 goto too_long;
2261
2262 len = stream_get_endp(s) - len_pos - 1;
2263
2264 /* Multiple auths don't go into one TLV, so always break */
2265 if (context == ISIS_CONTEXT_LSP && type == ISIS_TLV_AUTH) {
2266 item = item->next;
2267 break;
2268 }
2269
bd507085
CF
2270 /* Multiple prefix-sids don't go into one TLV, so always break */
2271 if (type == ISIS_SUBTLV_PREFIX_SID
2272 && (context == ISIS_CONTEXT_SUBTLV_IP_REACH
2273 || context == ISIS_CONTEXT_SUBTLV_IPV6_REACH)) {
2274 item = item->next;
2275 break;
2276 }
2277
7ef5fefc
CF
2278 if (len > 255) {
2279 if (!last_len) /* strange, not a single item fit */
2280 return 1;
2281 /* drop last tlv, otherwise, its too long */
2282 stream_set_endp(s, len_pos + 1 + last_len);
2283 len = last_len;
2284 break;
2285 }
2286
2287 if (fragment_tlvs)
2288 add_item_to_fragment(item, pe, *fragment_tlvs, mtid);
2289
2290 last_len = len;
2291 }
2292
2293 stream_putc_at(s, len_pos, len);
2294 if (item)
2295 goto top;
2296
2297 return 0;
2298too_long:
2299 if (!fragment_tlvs)
2300 return 1;
2301 stream_reset(s);
2302 *fragment_tlvs = new_fragment(new_fragment_arg);
2303 goto top;
2304}
2305#define pack_items(...) pack_items_(ISIS_MT_IPV4_UNICAST, __VA_ARGS__)
2306
2307static void append_item(struct isis_item_list *dest, struct isis_item *item)
2308{
2309 *dest->tail = item;
2310 dest->tail = &(*dest->tail)->next;
2311 dest->count++;
2312}
2313
d43d2df5
CF
2314static struct isis_item *last_item(struct isis_item_list *list)
2315{
2316 return container_of(list->tail, struct isis_item, next);
2317}
2318
7ef5fefc
CF
2319static int unpack_item(uint16_t mtid, enum isis_tlv_context context,
2320 uint8_t tlv_type, uint8_t len, struct stream *s,
2321 struct sbuf *log, void *dest, int indent)
2322{
2323 const struct tlv_ops *ops = tlv_table[context][tlv_type];
2324
2325 if (ops && ops->unpack_item)
2326 return ops->unpack_item(mtid, len, s, log, dest, indent);
2327
2328 assert(!"Unknown item tlv type!");
2329 sbuf_push(log, indent, "Unknown item tlv type!\n");
2330 return 1;
2331}
2332
2333static int unpack_tlv_with_items(enum isis_tlv_context context,
2334 uint8_t tlv_type, uint8_t tlv_len,
2335 struct stream *s, struct sbuf *log, void *dest,
2336 int indent)
2337{
2338 size_t tlv_start;
2339 size_t tlv_pos;
2340 int rv;
2341 uint16_t mtid;
2342
2343 tlv_start = stream_get_getp(s);
2344 tlv_pos = 0;
2345
2346 if (context == ISIS_CONTEXT_LSP && IS_COMPAT_MT_TLV(tlv_type)) {
2347 if (tlv_len < 2) {
2348 sbuf_push(log, indent,
2349 "TLV is too short to contain MTID\n");
2350 return 1;
2351 }
2352 mtid = stream_getw(s) & ISIS_MT_MASK;
2353 tlv_pos += 2;
2354 sbuf_push(log, indent, "Unpacking as MT %s item TLV...\n",
2355 isis_mtid2str(mtid));
2356 } else {
2357 sbuf_push(log, indent, "Unpacking as item TLV...\n");
2358 mtid = ISIS_MT_IPV4_UNICAST;
2359 }
2360
2361 if (context == ISIS_CONTEXT_LSP
2362 && tlv_type == ISIS_TLV_OLDSTYLE_REACH) {
2363 if (tlv_len - tlv_pos < 1) {
2364 sbuf_push(log, indent,
2365 "TLV is too short for old style reach\n");
2366 return 1;
2367 }
2368 stream_forward_getp(s, 1);
2369 tlv_pos += 1;
2370 }
2371
2372 if (context == ISIS_CONTEXT_LSP
2373 && tlv_type == ISIS_TLV_OLDSTYLE_IP_REACH) {
2374 struct isis_tlvs *tlvs = dest;
2375 dest = &tlvs->oldstyle_ip_reach;
2376 } else if (context == ISIS_CONTEXT_LSP
2377 && tlv_type == ISIS_TLV_OLDSTYLE_IP_REACH_EXT) {
2378 struct isis_tlvs *tlvs = dest;
2379 dest = &tlvs->oldstyle_ip_reach_ext;
2380 }
2381
2382 if (context == ISIS_CONTEXT_LSP
2383 && tlv_type == ISIS_TLV_MT_ROUTER_INFO) {
2384 struct isis_tlvs *tlvs = dest;
2385 tlvs->mt_router_info_empty = (tlv_pos >= (size_t)tlv_len);
2386 }
2387
2388 while (tlv_pos < (size_t)tlv_len) {
2389 rv = unpack_item(mtid, context, tlv_type, tlv_len - tlv_pos, s,
2390 log, dest, indent + 2);
2391 if (rv)
2392 return rv;
2393
2394 tlv_pos = stream_get_getp(s) - tlv_start;
2395 }
2396
2397 return 0;
2398}
2399
2400/* Functions to manipulate mt_item_lists */
2401
2402static int isis_mt_item_list_cmp(const struct isis_item_list *a,
2403 const struct isis_item_list *b)
2404{
2405 if (a->mtid < b->mtid)
2406 return -1;
2407 if (a->mtid > b->mtid)
2408 return 1;
2409 return 0;
2410}
2411
2412RB_PROTOTYPE(isis_mt_item_list, isis_item_list, mt_tree, isis_mt_item_list_cmp);
2413RB_GENERATE(isis_mt_item_list, isis_item_list, mt_tree, isis_mt_item_list_cmp);
2414
2415struct isis_item_list *isis_get_mt_items(struct isis_mt_item_list *m,
2416 uint16_t mtid)
2417{
2418 struct isis_item_list *rv;
2419
2420 rv = isis_lookup_mt_items(m, mtid);
2421 if (!rv) {
2422 rv = XCALLOC(MTYPE_ISIS_MT_ITEM_LIST, sizeof(*rv));
2423 init_item_list(rv);
2424 rv->mtid = mtid;
2425 RB_INSERT(isis_mt_item_list, m, rv);
2426 }
2427
2428 return rv;
2429}
2430
2431struct isis_item_list *isis_lookup_mt_items(struct isis_mt_item_list *m,
2432 uint16_t mtid)
2433{
2434 struct isis_item_list key = {.mtid = mtid};
2435
2436 return RB_FIND(isis_mt_item_list, m, &key);
2437}
2438
2439static void free_mt_items(enum isis_tlv_context context,
2440 enum isis_tlv_type type, struct isis_mt_item_list *m)
2441{
2442 struct isis_item_list *n, *nnext;
2443
a2addae8 2444 RB_FOREACH_SAFE (n, isis_mt_item_list, m, nnext) {
7ef5fefc
CF
2445 free_items(context, type, n);
2446 RB_REMOVE(isis_mt_item_list, m, n);
2447 XFREE(MTYPE_ISIS_MT_ITEM_LIST, n);
2448 }
2449}
2450
2451static void format_mt_items(enum isis_tlv_context context,
2452 enum isis_tlv_type type,
2453 struct isis_mt_item_list *m, struct sbuf *buf,
2454 int indent)
2455{
2456 struct isis_item_list *n;
2457
a2addae8 2458 RB_FOREACH (n, isis_mt_item_list, m) {
7ef5fefc
CF
2459 format_items_(n->mtid, context, type, n, buf, indent);
2460 }
2461}
2462
2463static int pack_mt_items(enum isis_tlv_context context, enum isis_tlv_type type,
2464 struct isis_mt_item_list *m, struct stream *s,
2465 struct isis_tlvs **fragment_tlvs,
2466 struct pack_order_entry *pe,
2467 struct isis_tlvs *(*new_fragment)(struct list *l),
2468 struct list *new_fragment_arg)
2469{
2470 struct isis_item_list *n;
2471
a2addae8 2472 RB_FOREACH (n, isis_mt_item_list, m) {
7ef5fefc
CF
2473 int rv;
2474
2475 rv = pack_items_(n->mtid, context, type, n, s, fragment_tlvs,
2476 pe, new_fragment, new_fragment_arg);
2477 if (rv)
2478 return rv;
2479 }
2480
2481 return 0;
2482}
2483
2484static void copy_mt_items(enum isis_tlv_context context,
2485 enum isis_tlv_type type,
2486 struct isis_mt_item_list *src,
2487 struct isis_mt_item_list *dest)
2488{
2489 struct isis_item_list *n;
2490
2491 RB_INIT(isis_mt_item_list, dest);
2492
a2addae8 2493 RB_FOREACH (n, isis_mt_item_list, src) {
7ef5fefc
CF
2494 copy_items(context, type, n, isis_get_mt_items(dest, n->mtid));
2495 }
2496}
2497
2498/* Functions related to tlvs in general */
2499
2500struct isis_tlvs *isis_alloc_tlvs(void)
2501{
2502 struct isis_tlvs *result;
2503
841791b6 2504 result = XCALLOC(MTYPE_ISIS_TLV, sizeof(*result));
7ef5fefc
CF
2505
2506 init_item_list(&result->isis_auth);
2507 init_item_list(&result->area_addresses);
2508 init_item_list(&result->mt_router_info);
2509 init_item_list(&result->oldstyle_reach);
2510 init_item_list(&result->lan_neighbor);
2511 init_item_list(&result->lsp_entries);
2512 init_item_list(&result->extended_reach);
2513 RB_INIT(isis_mt_item_list, &result->mt_reach);
2514 init_item_list(&result->oldstyle_ip_reach);
2515 init_item_list(&result->oldstyle_ip_reach_ext);
2516 init_item_list(&result->ipv4_address);
2517 init_item_list(&result->ipv6_address);
2518 init_item_list(&result->extended_ip_reach);
2519 RB_INIT(isis_mt_item_list, &result->mt_ip_reach);
2520 init_item_list(&result->ipv6_reach);
2521 RB_INIT(isis_mt_item_list, &result->mt_ipv6_reach);
2522
2523 return result;
2524}
2525
2526struct isis_tlvs *isis_copy_tlvs(struct isis_tlvs *tlvs)
2527{
841791b6 2528 struct isis_tlvs *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
2529
2530 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth,
2531 &rv->isis_auth);
2532
5f77d901
CF
2533 rv->purge_originator =
2534 copy_tlv_purge_originator(tlvs->purge_originator);
2535
7ef5fefc
CF
2536 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES,
2537 &tlvs->area_addresses, &rv->area_addresses);
2538
2539 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO,
2540 &tlvs->mt_router_info, &rv->mt_router_info);
2541
b9d4a380 2542 rv->mt_router_info_empty = tlvs->mt_router_info_empty;
7ef5fefc
CF
2543
2544 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_REACH,
2545 &tlvs->oldstyle_reach, &rv->oldstyle_reach);
2546
2547 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_LAN_NEIGHBORS,
2548 &tlvs->lan_neighbor, &rv->lan_neighbor);
2549
2550 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_LSP_ENTRY, &tlvs->lsp_entries,
2551 &rv->lsp_entries);
2552
2553 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_REACH,
2554 &tlvs->extended_reach, &rv->extended_reach);
2555
2556 copy_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_REACH, &tlvs->mt_reach,
2557 &rv->mt_reach);
2558
2559 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH,
2560 &tlvs->oldstyle_ip_reach, &rv->oldstyle_ip_reach);
2561
2562 copy_tlv_protocols_supported(&tlvs->protocols_supported,
2563 &rv->protocols_supported);
2564
2565 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH_EXT,
2566 &tlvs->oldstyle_ip_reach_ext, &rv->oldstyle_ip_reach_ext);
2567
2568 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV4_ADDRESS, &tlvs->ipv4_address,
2569 &rv->ipv4_address);
2570
2571 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_ADDRESS, &tlvs->ipv6_address,
2572 &rv->ipv6_address);
2573
2574 rv->te_router_id = copy_tlv_te_router_id(tlvs->te_router_id);
2575
2576 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_IP_REACH,
2577 &tlvs->extended_ip_reach, &rv->extended_ip_reach);
2578
2579 copy_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IP_REACH,
2580 &tlvs->mt_ip_reach, &rv->mt_ip_reach);
2581
2582 rv->hostname = copy_tlv_dynamic_hostname(tlvs->hostname);
2583
2584 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_REACH, &tlvs->ipv6_reach,
2585 &rv->ipv6_reach);
2586
2587 copy_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IPV6_REACH,
2588 &tlvs->mt_ipv6_reach, &rv->mt_ipv6_reach);
2589
9fe21208
CF
2590 rv->threeway_adj = copy_tlv_threeway_adj(tlvs->threeway_adj);
2591
41a145f1
CF
2592 rv->spine_leaf = copy_tlv_spine_leaf(tlvs->spine_leaf);
2593
7ef5fefc
CF
2594 return rv;
2595}
2596
2597static void format_tlvs(struct isis_tlvs *tlvs, struct sbuf *buf, int indent)
2598{
2599 format_tlv_protocols_supported(&tlvs->protocols_supported, buf, indent);
2600
2601 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth, buf,
2602 indent);
2603
5f77d901
CF
2604 format_tlv_purge_originator(tlvs->purge_originator, buf, indent);
2605
7ef5fefc
CF
2606 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES,
2607 &tlvs->area_addresses, buf, indent);
2608
2609 if (tlvs->mt_router_info_empty) {
2610 sbuf_push(buf, indent, "MT Router Info: None\n");
2611 } else {
2612 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO,
2613 &tlvs->mt_router_info, buf, indent);
2614 }
2615
2616 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_REACH,
2617 &tlvs->oldstyle_reach, buf, indent);
2618
2619 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_LAN_NEIGHBORS,
2620 &tlvs->lan_neighbor, buf, indent);
2621
2622 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_LSP_ENTRY, &tlvs->lsp_entries,
2623 buf, indent);
2624
2625 format_tlv_dynamic_hostname(tlvs->hostname, buf, indent);
2626 format_tlv_te_router_id(tlvs->te_router_id, buf, indent);
2627
2628 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_REACH,
2629 &tlvs->extended_reach, buf, indent);
2630
2631 format_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_REACH, &tlvs->mt_reach,
2632 buf, indent);
2633
2634 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH,
2635 &tlvs->oldstyle_ip_reach, buf, indent);
2636
2637 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH_EXT,
2638 &tlvs->oldstyle_ip_reach_ext, buf, indent);
2639
2640 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV4_ADDRESS,
2641 &tlvs->ipv4_address, buf, indent);
2642
2643 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_ADDRESS,
2644 &tlvs->ipv6_address, buf, indent);
2645
2646 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_IP_REACH,
2647 &tlvs->extended_ip_reach, buf, indent);
2648
2649 format_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IP_REACH,
2650 &tlvs->mt_ip_reach, buf, indent);
2651
2652 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_REACH, &tlvs->ipv6_reach,
2653 buf, indent);
2654
2655 format_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IPV6_REACH,
2656 &tlvs->mt_ipv6_reach, buf, indent);
9fe21208
CF
2657
2658 format_tlv_threeway_adj(tlvs->threeway_adj, buf, indent);
41a145f1
CF
2659
2660 format_tlv_spine_leaf(tlvs->spine_leaf, buf, indent);
7ef5fefc
CF
2661}
2662
2663const char *isis_format_tlvs(struct isis_tlvs *tlvs)
2664{
2665 static struct sbuf buf;
2666
2667 if (!sbuf_buf(&buf))
2668 sbuf_init(&buf, NULL, 0);
2669
2670 sbuf_reset(&buf);
2671 format_tlvs(tlvs, &buf, 0);
2672 return sbuf_buf(&buf);
2673}
2674
2675void isis_free_tlvs(struct isis_tlvs *tlvs)
2676{
2677 if (!tlvs)
2678 return;
2679
2680 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth);
5f77d901 2681 free_tlv_purge_originator(tlvs->purge_originator);
7ef5fefc
CF
2682 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES,
2683 &tlvs->area_addresses);
2684 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO,
2685 &tlvs->mt_router_info);
2686 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_REACH,
2687 &tlvs->oldstyle_reach);
2688 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_LAN_NEIGHBORS,
2689 &tlvs->lan_neighbor);
2690 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_LSP_ENTRY, &tlvs->lsp_entries);
2691 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_REACH,
2692 &tlvs->extended_reach);
2693 free_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_REACH, &tlvs->mt_reach);
2694 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH,
2695 &tlvs->oldstyle_ip_reach);
2696 free_tlv_protocols_supported(&tlvs->protocols_supported);
2697 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH_EXT,
2698 &tlvs->oldstyle_ip_reach_ext);
2699 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV4_ADDRESS,
2700 &tlvs->ipv4_address);
2701 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_ADDRESS,
2702 &tlvs->ipv6_address);
2703 free_tlv_te_router_id(tlvs->te_router_id);
2704 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_IP_REACH,
2705 &tlvs->extended_ip_reach);
2706 free_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IP_REACH,
2707 &tlvs->mt_ip_reach);
2708 free_tlv_dynamic_hostname(tlvs->hostname);
2709 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_REACH, &tlvs->ipv6_reach);
2710 free_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IPV6_REACH,
2711 &tlvs->mt_ipv6_reach);
9fe21208 2712 free_tlv_threeway_adj(tlvs->threeway_adj);
41a145f1 2713 free_tlv_spine_leaf(tlvs->spine_leaf);
7ef5fefc 2714
841791b6 2715 XFREE(MTYPE_ISIS_TLV, tlvs);
7ef5fefc
CF
2716}
2717
2718static void add_padding(struct stream *s)
2719{
2720 while (STREAM_WRITEABLE(s)) {
2721 if (STREAM_WRITEABLE(s) == 1)
2722 break;
2723 uint32_t padding_len = STREAM_WRITEABLE(s) - 2;
2724
2725 if (padding_len > 255) {
2726 if (padding_len == 256)
2727 padding_len = 254;
2728 else
2729 padding_len = 255;
2730 }
2731
2732 stream_putc(s, ISIS_TLV_PADDING);
2733 stream_putc(s, padding_len);
2734 stream_put(s, NULL, padding_len);
2735 }
2736}
2737
2738#define LSP_REM_LIFETIME_OFF 10
2739#define LSP_CHECKSUM_OFF 24
2740static void safe_auth_md5(struct stream *s, uint16_t *checksum,
2741 uint16_t *rem_lifetime)
2742{
2743 memcpy(rem_lifetime, STREAM_DATA(s) + LSP_REM_LIFETIME_OFF,
2744 sizeof(*rem_lifetime));
2745 memset(STREAM_DATA(s) + LSP_REM_LIFETIME_OFF, 0, sizeof(*rem_lifetime));
2746 memcpy(checksum, STREAM_DATA(s) + LSP_CHECKSUM_OFF, sizeof(*checksum));
2747 memset(STREAM_DATA(s) + LSP_CHECKSUM_OFF, 0, sizeof(*checksum));
2748}
2749
2750static void restore_auth_md5(struct stream *s, uint16_t checksum,
2751 uint16_t rem_lifetime)
2752{
2753 memcpy(STREAM_DATA(s) + LSP_REM_LIFETIME_OFF, &rem_lifetime,
2754 sizeof(rem_lifetime));
2755 memcpy(STREAM_DATA(s) + LSP_CHECKSUM_OFF, &checksum, sizeof(checksum));
2756}
2757
2758static void update_auth_hmac_md5(struct isis_auth *auth, struct stream *s,
2759 bool is_lsp)
2760{
2761 uint8_t digest[16];
2762 uint16_t checksum, rem_lifetime;
2763
2764 if (is_lsp)
2765 safe_auth_md5(s, &checksum, &rem_lifetime);
2766
2767 memset(STREAM_DATA(s) + auth->offset, 0, 16);
2768 hmac_md5(STREAM_DATA(s), stream_get_endp(s), auth->passwd,
2769 auth->plength, digest);
2770 memcpy(auth->value, digest, 16);
2771 memcpy(STREAM_DATA(s) + auth->offset, digest, 16);
2772
2773 if (is_lsp)
2774 restore_auth_md5(s, checksum, rem_lifetime);
2775}
2776
2777static void update_auth(struct isis_tlvs *tlvs, struct stream *s, bool is_lsp)
2778{
2779 struct isis_auth *auth_head = (struct isis_auth *)tlvs->isis_auth.head;
2780
2781 for (struct isis_auth *auth = auth_head; auth; auth = auth->next) {
2782 if (auth->type == ISIS_PASSWD_TYPE_HMAC_MD5)
2783 update_auth_hmac_md5(auth, s, is_lsp);
2784 }
2785}
2786
2787static int handle_pack_entry(struct pack_order_entry *pe,
2788 struct isis_tlvs *tlvs, struct stream *stream,
2789 struct isis_tlvs **fragment_tlvs,
2790 struct isis_tlvs *(*new_fragment)(struct list *l),
2791 struct list *new_fragment_arg)
2792{
2793 int rv;
2794
2795 if (pe->how_to_pack == ISIS_ITEMS) {
2796 struct isis_item_list *l;
2797 l = (struct isis_item_list *)(((char *)tlvs)
2798 + pe->what_to_pack);
2799 rv = pack_items(pe->context, pe->type, l, stream, fragment_tlvs,
2800 pe, new_fragment, new_fragment_arg);
2801 } else {
2802 struct isis_mt_item_list *l;
2803 l = (struct isis_mt_item_list *)(((char *)tlvs)
2804 + pe->what_to_pack);
2805 rv = pack_mt_items(pe->context, pe->type, l, stream,
2806 fragment_tlvs, pe, new_fragment,
2807 new_fragment_arg);
2808 }
2809
2810 return rv;
2811}
2812
2813static int pack_tlvs(struct isis_tlvs *tlvs, struct stream *stream,
2814 struct isis_tlvs *fragment_tlvs,
2815 struct isis_tlvs *(*new_fragment)(struct list *l),
2816 struct list *new_fragment_arg)
2817{
2818 int rv;
2819
2820 /* When fragmenting, don't add auth as it's already accounted for in the
2821 * size we are given. */
2822 if (!fragment_tlvs) {
996c9314
LB
2823 rv = pack_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH,
2824 &tlvs->isis_auth, stream, NULL, NULL, NULL,
2825 NULL);
7ef5fefc
CF
2826 if (rv)
2827 return rv;
2828 }
2829
5f77d901
CF
2830 rv = pack_tlv_purge_originator(tlvs->purge_originator, stream);
2831 if (rv)
2832 return rv;
2833 if (fragment_tlvs) {
2834 fragment_tlvs->purge_originator =
2835 copy_tlv_purge_originator(tlvs->purge_originator);
2836 }
2837
7ef5fefc
CF
2838 rv = pack_tlv_protocols_supported(&tlvs->protocols_supported, stream);
2839 if (rv)
2840 return rv;
2841 if (fragment_tlvs) {
2842 copy_tlv_protocols_supported(
2843 &tlvs->protocols_supported,
2844 &fragment_tlvs->protocols_supported);
2845 }
2846
2847 rv = pack_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES,
2848 &tlvs->area_addresses, stream, NULL, NULL, NULL, NULL);
2849 if (rv)
2850 return rv;
2851 if (fragment_tlvs) {
2852 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES,
2853 &tlvs->area_addresses,
2854 &fragment_tlvs->area_addresses);
2855 }
2856
2857
2858 if (tlvs->mt_router_info_empty) {
2859 if (STREAM_WRITEABLE(stream) < 2)
2860 return 1;
2861 stream_putc(stream, ISIS_TLV_MT_ROUTER_INFO);
2862 stream_putc(stream, 0);
2863 if (fragment_tlvs)
2864 fragment_tlvs->mt_router_info_empty = true;
2865 } else {
2866 rv = pack_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO,
2867 &tlvs->mt_router_info, stream, NULL, NULL, NULL,
2868 NULL);
2869 if (rv)
2870 return rv;
2871 if (fragment_tlvs) {
2872 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO,
2873 &tlvs->mt_router_info,
2874 &fragment_tlvs->mt_router_info);
2875 }
2876 }
2877
2878 rv = pack_tlv_dynamic_hostname(tlvs->hostname, stream);
2879 if (rv)
2880 return rv;
2881 if (fragment_tlvs)
2882 fragment_tlvs->hostname =
2883 copy_tlv_dynamic_hostname(tlvs->hostname);
2884
2885 rv = pack_tlv_te_router_id(tlvs->te_router_id, stream);
2886 if (rv)
2887 return rv;
2888 if (fragment_tlvs) {
2889 fragment_tlvs->te_router_id =
2890 copy_tlv_te_router_id(tlvs->te_router_id);
2891 }
2892
9fe21208
CF
2893 rv = pack_tlv_threeway_adj(tlvs->threeway_adj, stream);
2894 if (rv)
2895 return rv;
2896 if (fragment_tlvs) {
2897 fragment_tlvs->threeway_adj =
2898 copy_tlv_threeway_adj(tlvs->threeway_adj);
2899 }
2900
41a145f1
CF
2901 rv = pack_tlv_spine_leaf(tlvs->spine_leaf, stream);
2902 if (rv)
2903 return rv;
2904 if (fragment_tlvs) {
2905 fragment_tlvs->spine_leaf =
2906 copy_tlv_spine_leaf(tlvs->spine_leaf);
2907 }
2908
7ef5fefc
CF
2909 for (size_t pack_idx = 0; pack_idx < array_size(pack_order);
2910 pack_idx++) {
2911 rv = handle_pack_entry(&pack_order[pack_idx], tlvs, stream,
2912 fragment_tlvs ? &fragment_tlvs : NULL,
2913 new_fragment, new_fragment_arg);
2914
2915 if (rv)
2916 return rv;
2917 }
2918
2919 return 0;
2920}
2921
2922int isis_pack_tlvs(struct isis_tlvs *tlvs, struct stream *stream,
2923 size_t len_pointer, bool pad, bool is_lsp)
2924{
2925 int rv;
2926
2927 rv = pack_tlvs(tlvs, stream, NULL, NULL, NULL);
2928 if (rv)
2929 return rv;
2930
2931 if (pad)
2932 add_padding(stream);
2933
2934 if (len_pointer != (size_t)-1) {
2935 stream_putw_at(stream, len_pointer, stream_get_endp(stream));
2936 }
2937
2938 update_auth(tlvs, stream, is_lsp);
2939
2940 return 0;
2941}
2942
2943static struct isis_tlvs *new_fragment(struct list *l)
2944{
2945 struct isis_tlvs *rv = isis_alloc_tlvs();
2946
2947 listnode_add(l, rv);
2948 return rv;
2949}
2950
2951struct list *isis_fragment_tlvs(struct isis_tlvs *tlvs, size_t size)
2952{
2953 struct stream *dummy_stream = stream_new(size);
2954 struct list *rv = list_new();
2955 struct isis_tlvs *fragment_tlvs = new_fragment(rv);
2956
2957 if (pack_tlvs(tlvs, dummy_stream, fragment_tlvs, new_fragment, rv)) {
2958 struct listnode *node;
2959 for (ALL_LIST_ELEMENTS_RO(rv, node, fragment_tlvs))
2960 isis_free_tlvs(fragment_tlvs);
6a154c88 2961 list_delete(&rv);
7ef5fefc
CF
2962 }
2963
2964 stream_free(dummy_stream);
2965 return rv;
2966}
2967
2968static int unpack_tlv_unknown(enum isis_tlv_context context, uint8_t tlv_type,
2969 uint8_t tlv_len, struct stream *s,
2970 struct sbuf *log, int indent)
2971{
2972 stream_forward_getp(s, tlv_len);
2973 sbuf_push(log, indent,
2974 "Skipping unknown TLV %" PRIu8 " (%" PRIu8 " bytes)\n",
2975 tlv_type, tlv_len);
2976 return 0;
2977}
2978
2979static int unpack_tlv(enum isis_tlv_context context, size_t avail_len,
2980 struct stream *stream, struct sbuf *log, void *dest,
fdc615a4 2981 int indent, bool *unpacked_known_tlvs)
7ef5fefc
CF
2982{
2983 uint8_t tlv_type, tlv_len;
2984 const struct tlv_ops *ops;
2985
2986 sbuf_push(log, indent, "Unpacking TLV...\n");
2987
2988 if (avail_len < 2) {
2989 sbuf_push(
2990 log, indent + 2,
2991 "Available data %zu too short to contain a TLV header.\n",
2992 avail_len);
2993 return 1;
2994 }
2995
2996 tlv_type = stream_getc(stream);
2997 tlv_len = stream_getc(stream);
2998
2999 sbuf_push(log, indent + 2,
3000 "Found TLV of type %" PRIu8 " and len %" PRIu8 ".\n",
3001 tlv_type, tlv_len);
3002
3003 if (avail_len < ((size_t)tlv_len) + 2) {
98c5bc15
CF
3004 sbuf_push(log, indent + 2,
3005 "Available data %zu too short for claimed TLV len %" PRIu8 ".\n",
3006 avail_len - 2, tlv_len);
7ef5fefc
CF
3007 return 1;
3008 }
3009
3010 ops = tlv_table[context][tlv_type];
3011 if (ops && ops->unpack) {
fdc615a4
CF
3012 if (unpacked_known_tlvs)
3013 *unpacked_known_tlvs = true;
7ef5fefc
CF
3014 return ops->unpack(context, tlv_type, tlv_len, stream, log,
3015 dest, indent + 2);
3016 }
3017
3018 return unpack_tlv_unknown(context, tlv_type, tlv_len, stream, log,
3019 indent + 2);
3020}
3021
3022static int unpack_tlvs(enum isis_tlv_context context, size_t avail_len,
3023 struct stream *stream, struct sbuf *log, void *dest,
fdc615a4 3024 int indent, bool *unpacked_known_tlvs)
7ef5fefc
CF
3025{
3026 int rv;
3027 size_t tlv_start, tlv_pos;
3028
3029 tlv_start = stream_get_getp(stream);
3030 tlv_pos = 0;
3031
3032 sbuf_push(log, indent, "Unpacking %zu bytes of %s...\n", avail_len,
3033 (context == ISIS_CONTEXT_LSP) ? "TLVs" : "sub-TLVs");
3034
3035 while (tlv_pos < avail_len) {
3036 rv = unpack_tlv(context, avail_len - tlv_pos, stream, log, dest,
fdc615a4 3037 indent + 2, unpacked_known_tlvs);
7ef5fefc
CF
3038 if (rv)
3039 return rv;
3040
3041 tlv_pos = stream_get_getp(stream) - tlv_start;
3042 }
3043
3044 return 0;
3045}
3046
3047int isis_unpack_tlvs(size_t avail_len, struct stream *stream,
3048 struct isis_tlvs **dest, const char **log)
3049{
3050 static struct sbuf logbuf;
3051 int indent = 0;
3052 int rv;
3053 struct isis_tlvs *result;
3054
3055 if (!sbuf_buf(&logbuf))
3056 sbuf_init(&logbuf, NULL, 0);
3057
3058 sbuf_reset(&logbuf);
3059 if (avail_len > STREAM_READABLE(stream)) {
3060 sbuf_push(&logbuf, indent,
3061 "Stream doesn't contain sufficient data. "
3062 "Claimed %zu, available %zu\n",
3063 avail_len, STREAM_READABLE(stream));
3064 return 1;
3065 }
3066
3067 result = isis_alloc_tlvs();
3068 rv = unpack_tlvs(ISIS_CONTEXT_LSP, avail_len, stream, &logbuf, result,
fdc615a4 3069 indent, NULL);
7ef5fefc
CF
3070
3071 *log = sbuf_buf(&logbuf);
3072 *dest = result;
3073
3074 return rv;
3075}
3076
3077#define TLV_OPS(_name_, _desc_) \
3078 static const struct tlv_ops tlv_##_name_##_ops = { \
3079 .name = _desc_, .unpack = unpack_tlv_##_name_, \
3080 }
3081
3082#define ITEM_TLV_OPS(_name_, _desc_) \
3083 static const struct tlv_ops tlv_##_name_##_ops = { \
3084 .name = _desc_, \
3085 .unpack = unpack_tlv_with_items, \
98c5bc15 3086 \
7ef5fefc
CF
3087 .pack_item = pack_item_##_name_, \
3088 .free_item = free_item_##_name_, \
3089 .unpack_item = unpack_item_##_name_, \
3090 .format_item = format_item_##_name_, \
3091 .copy_item = copy_item_##_name_}
3092
3093#define SUBTLV_OPS(_name_, _desc_) \
3094 static const struct tlv_ops subtlv_##_name_##_ops = { \
3095 .name = _desc_, .unpack = unpack_subtlv_##_name_, \
3096 }
3097
bd507085
CF
3098#define ITEM_SUBTLV_OPS(_name_, _desc_) \
3099 ITEM_TLV_OPS(_name_, _desc_)
3100
7ef5fefc
CF
3101ITEM_TLV_OPS(area_address, "TLV 1 Area Addresses");
3102ITEM_TLV_OPS(oldstyle_reach, "TLV 2 IS Reachability");
3103ITEM_TLV_OPS(lan_neighbor, "TLV 6 LAN Neighbors");
3104ITEM_TLV_OPS(lsp_entry, "TLV 9 LSP Entries");
3105ITEM_TLV_OPS(auth, "TLV 10 IS-IS Auth");
5f77d901 3106TLV_OPS(purge_originator, "TLV 13 Purge Originator Identification");
7ef5fefc
CF
3107ITEM_TLV_OPS(extended_reach, "TLV 22 Extended Reachability");
3108ITEM_TLV_OPS(oldstyle_ip_reach, "TLV 128/130 IP Reachability");
3109TLV_OPS(protocols_supported, "TLV 129 Protocols Supported");
3110ITEM_TLV_OPS(ipv4_address, "TLV 132 IPv4 Interface Address");
3111TLV_OPS(te_router_id, "TLV 134 TE Router ID");
3112ITEM_TLV_OPS(extended_ip_reach, "TLV 135 Extended IP Reachability");
3113TLV_OPS(dynamic_hostname, "TLV 137 Dynamic Hostname");
41a145f1 3114TLV_OPS(spine_leaf, "TLV 150 Spine Leaf Extensions");
7ef5fefc 3115ITEM_TLV_OPS(mt_router_info, "TLV 229 MT Router Information");
9fe21208 3116TLV_OPS(threeway_adj, "TLV 240 P2P Three-Way Adjacency");
7ef5fefc
CF
3117ITEM_TLV_OPS(ipv6_address, "TLV 232 IPv6 Interface Address");
3118ITEM_TLV_OPS(ipv6_reach, "TLV 236 IPv6 Reachability");
3119
bd507085 3120ITEM_SUBTLV_OPS(prefix_sid, "Sub-TLV 3 SR Prefix-SID");
7ef5fefc
CF
3121SUBTLV_OPS(ipv6_source_prefix, "Sub-TLV 22 IPv6 Source Prefix");
3122
98c5bc15
CF
3123static const struct tlv_ops *tlv_table[ISIS_CONTEXT_MAX][ISIS_TLV_MAX] = {
3124 [ISIS_CONTEXT_LSP] = {
3125 [ISIS_TLV_AREA_ADDRESSES] = &tlv_area_address_ops,
3126 [ISIS_TLV_OLDSTYLE_REACH] = &tlv_oldstyle_reach_ops,
3127 [ISIS_TLV_LAN_NEIGHBORS] = &tlv_lan_neighbor_ops,
3128 [ISIS_TLV_LSP_ENTRY] = &tlv_lsp_entry_ops,
3129 [ISIS_TLV_AUTH] = &tlv_auth_ops,
5f77d901 3130 [ISIS_TLV_PURGE_ORIGINATOR] = &tlv_purge_originator_ops,
98c5bc15
CF
3131 [ISIS_TLV_EXTENDED_REACH] = &tlv_extended_reach_ops,
3132 [ISIS_TLV_MT_REACH] = &tlv_extended_reach_ops,
3133 [ISIS_TLV_OLDSTYLE_IP_REACH] = &tlv_oldstyle_ip_reach_ops,
3134 [ISIS_TLV_PROTOCOLS_SUPPORTED] = &tlv_protocols_supported_ops,
3135 [ISIS_TLV_OLDSTYLE_IP_REACH_EXT] = &tlv_oldstyle_ip_reach_ops,
3136 [ISIS_TLV_IPV4_ADDRESS] = &tlv_ipv4_address_ops,
3137 [ISIS_TLV_TE_ROUTER_ID] = &tlv_te_router_id_ops,
3138 [ISIS_TLV_EXTENDED_IP_REACH] = &tlv_extended_ip_reach_ops,
3139 [ISIS_TLV_MT_IP_REACH] = &tlv_extended_ip_reach_ops,
3140 [ISIS_TLV_DYNAMIC_HOSTNAME] = &tlv_dynamic_hostname_ops,
41a145f1 3141 [ISIS_TLV_SPINE_LEAF_EXT] = &tlv_spine_leaf_ops,
98c5bc15 3142 [ISIS_TLV_MT_ROUTER_INFO] = &tlv_mt_router_info_ops,
9fe21208 3143 [ISIS_TLV_THREE_WAY_ADJ] = &tlv_threeway_adj_ops,
98c5bc15
CF
3144 [ISIS_TLV_IPV6_ADDRESS] = &tlv_ipv6_address_ops,
3145 [ISIS_TLV_IPV6_REACH] = &tlv_ipv6_reach_ops,
3146 [ISIS_TLV_MT_IPV6_REACH] = &tlv_ipv6_reach_ops,
3147 },
3e300703 3148 [ISIS_CONTEXT_SUBTLV_NE_REACH] = {},
bd507085
CF
3149 [ISIS_CONTEXT_SUBTLV_IP_REACH] = {
3150 [ISIS_SUBTLV_PREFIX_SID] = &tlv_prefix_sid_ops,
3151 },
98c5bc15 3152 [ISIS_CONTEXT_SUBTLV_IPV6_REACH] = {
bd507085 3153 [ISIS_SUBTLV_PREFIX_SID] = &tlv_prefix_sid_ops,
98c5bc15
CF
3154 [ISIS_SUBTLV_IPV6_SOURCE_PREFIX] = &subtlv_ipv6_source_prefix_ops,
3155 }
3156};
7ef5fefc
CF
3157
3158/* Accessor functions */
3159
3160void isis_tlvs_add_auth(struct isis_tlvs *tlvs, struct isis_passwd *passwd)
3161{
3162 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth);
3163 init_item_list(&tlvs->isis_auth);
3164
3165 if (passwd->type == ISIS_PASSWD_TYPE_UNUSED)
3166 return;
3167
841791b6 3168 struct isis_auth *auth = XCALLOC(MTYPE_ISIS_TLV, sizeof(*auth));
7ef5fefc
CF
3169
3170 auth->type = passwd->type;
3171
3172 auth->plength = passwd->len;
3173 memcpy(auth->passwd, passwd->passwd,
3174 MIN(sizeof(auth->passwd), sizeof(passwd->passwd)));
3175
3176 if (auth->type == ISIS_PASSWD_TYPE_CLEARTXT) {
3177 auth->length = passwd->len;
3178 memcpy(auth->value, passwd->passwd,
3179 MIN(sizeof(auth->value), sizeof(passwd->passwd)));
3180 }
3181
3182 append_item(&tlvs->isis_auth, (struct isis_item *)auth);
3183}
3184
3185void isis_tlvs_add_area_addresses(struct isis_tlvs *tlvs,
3186 struct list *addresses)
3187{
3188 struct listnode *node;
3189 struct area_addr *area_addr;
3190
3191 for (ALL_LIST_ELEMENTS_RO(addresses, node, area_addr)) {
3192 struct isis_area_address *a =
841791b6 3193 XCALLOC(MTYPE_ISIS_TLV, sizeof(*a));
7ef5fefc
CF
3194
3195 a->len = area_addr->addr_len;
3196 memcpy(a->addr, area_addr->area_addr, 20);
3197 append_item(&tlvs->area_addresses, (struct isis_item *)a);
3198 }
3199}
3200
3201void isis_tlvs_add_lan_neighbors(struct isis_tlvs *tlvs, struct list *neighbors)
3202{
3203 struct listnode *node;
d7c0a89a 3204 uint8_t *snpa;
7ef5fefc
CF
3205
3206 for (ALL_LIST_ELEMENTS_RO(neighbors, node, snpa)) {
3207 struct isis_lan_neighbor *n =
841791b6 3208 XCALLOC(MTYPE_ISIS_TLV, sizeof(*n));
7ef5fefc
CF
3209
3210 memcpy(n->mac, snpa, 6);
3211 append_item(&tlvs->lan_neighbor, (struct isis_item *)n);
3212 }
3213}
3214
3215void isis_tlvs_set_protocols_supported(struct isis_tlvs *tlvs,
3216 struct nlpids *nlpids)
3217{
3218 tlvs->protocols_supported.count = nlpids->count;
0a22ddfb 3219 XFREE(MTYPE_ISIS_TLV, tlvs->protocols_supported.protocols);
7ef5fefc
CF
3220 if (nlpids->count) {
3221 tlvs->protocols_supported.protocols =
841791b6 3222 XCALLOC(MTYPE_ISIS_TLV, nlpids->count);
7ef5fefc
CF
3223 memcpy(tlvs->protocols_supported.protocols, nlpids->nlpids,
3224 nlpids->count);
3225 } else {
3226 tlvs->protocols_supported.protocols = NULL;
3227 }
3228}
3229
3230void isis_tlvs_add_mt_router_info(struct isis_tlvs *tlvs, uint16_t mtid,
3231 bool overload, bool attached)
3232{
841791b6 3233 struct isis_mt_router_info *i = XCALLOC(MTYPE_ISIS_TLV, sizeof(*i));
7ef5fefc
CF
3234
3235 i->overload = overload;
3236 i->attached = attached;
3237 i->mtid = mtid;
3238 append_item(&tlvs->mt_router_info, (struct isis_item *)i);
3239}
3240
3241void isis_tlvs_add_ipv4_address(struct isis_tlvs *tlvs, struct in_addr *addr)
3242{
841791b6 3243 struct isis_ipv4_address *a = XCALLOC(MTYPE_ISIS_TLV, sizeof(*a));
7ef5fefc
CF
3244 a->addr = *addr;
3245 append_item(&tlvs->ipv4_address, (struct isis_item *)a);
3246}
3247
3248
3249void isis_tlvs_add_ipv4_addresses(struct isis_tlvs *tlvs,
3250 struct list *addresses)
3251{
3252 struct listnode *node;
3253 struct prefix_ipv4 *ip_addr;
bb5c77d7 3254 unsigned int addr_count = 0;
7ef5fefc 3255
bb5c77d7 3256 for (ALL_LIST_ELEMENTS_RO(addresses, node, ip_addr)) {
7ef5fefc 3257 isis_tlvs_add_ipv4_address(tlvs, &ip_addr->prefix);
bb5c77d7
CF
3258 addr_count++;
3259 if (addr_count >= 63)
3260 break;
3261 }
7ef5fefc
CF
3262}
3263
3264void isis_tlvs_add_ipv6_addresses(struct isis_tlvs *tlvs,
3265 struct list *addresses)
3266{
3267 struct listnode *node;
3268 struct prefix_ipv6 *ip_addr;
a443e036 3269 unsigned int addr_count = 0;
7ef5fefc
CF
3270
3271 for (ALL_LIST_ELEMENTS_RO(addresses, node, ip_addr)) {
a443e036
DS
3272 if (addr_count >= 15)
3273 break;
3274
7ef5fefc 3275 struct isis_ipv6_address *a =
841791b6 3276 XCALLOC(MTYPE_ISIS_TLV, sizeof(*a));
7ef5fefc
CF
3277
3278 a->addr = ip_addr->prefix;
3279 append_item(&tlvs->ipv6_address, (struct isis_item *)a);
a443e036 3280 addr_count++;
7ef5fefc
CF
3281 }
3282}
3283
3284typedef bool (*auth_validator_func)(struct isis_passwd *passwd,
3285 struct stream *stream,
3286 struct isis_auth *auth, bool is_lsp);
3287
3288static bool auth_validator_cleartxt(struct isis_passwd *passwd,
3289 struct stream *stream,
3290 struct isis_auth *auth, bool is_lsp)
3291{
3292 return (auth->length == passwd->len
3293 && !memcmp(auth->value, passwd->passwd, passwd->len));
3294}
3295
3296static bool auth_validator_hmac_md5(struct isis_passwd *passwd,
3297 struct stream *stream,
3298 struct isis_auth *auth, bool is_lsp)
3299{
3300 uint8_t digest[16];
3301 uint16_t checksum;
3302 uint16_t rem_lifetime;
3303
3304 if (is_lsp)
3305 safe_auth_md5(stream, &checksum, &rem_lifetime);
3306
3307 memset(STREAM_DATA(stream) + auth->offset, 0, 16);
3308 hmac_md5(STREAM_DATA(stream), stream_get_endp(stream), passwd->passwd,
3309 passwd->len, digest);
3310 memcpy(STREAM_DATA(stream) + auth->offset, auth->value, 16);
3311
3312 bool rv = !memcmp(digest, auth->value, 16);
3313
3314 if (is_lsp)
3315 restore_auth_md5(stream, checksum, rem_lifetime);
3316
3317 return rv;
3318}
3319
3320static const auth_validator_func auth_validators[] = {
3321 [ISIS_PASSWD_TYPE_CLEARTXT] = auth_validator_cleartxt,
3322 [ISIS_PASSWD_TYPE_HMAC_MD5] = auth_validator_hmac_md5,
3323};
3324
3380c990
EDP
3325int isis_tlvs_auth_is_valid(struct isis_tlvs *tlvs, struct isis_passwd *passwd,
3326 struct stream *stream, bool is_lsp)
7ef5fefc
CF
3327{
3328 /* If no auth is set, always pass authentication */
3329 if (!passwd->type)
3380c990 3330 return ISIS_AUTH_OK;
7ef5fefc
CF
3331
3332 /* If we don't known how to validate the auth, return invalid */
3333 if (passwd->type >= array_size(auth_validators)
3334 || !auth_validators[passwd->type])
3380c990 3335 return ISIS_AUTH_NO_VALIDATOR;
7ef5fefc
CF
3336
3337 struct isis_auth *auth_head = (struct isis_auth *)tlvs->isis_auth.head;
3338 struct isis_auth *auth;
3339 for (auth = auth_head; auth; auth = auth->next) {
3340 if (auth->type == passwd->type)
3341 break;
3342 }
3343
3344 /* If matching auth TLV could not be found, return invalid */
3345 if (!auth)
3380c990
EDP
3346 return ISIS_AUTH_TYPE_FAILURE;
3347
7ef5fefc
CF
3348
3349 /* Perform validation and return result */
3380c990
EDP
3350 if (auth_validators[passwd->type](passwd, stream, auth, is_lsp))
3351 return ISIS_AUTH_OK;
3352 else
3353 return ISIS_AUTH_FAILURE;
7ef5fefc
CF
3354}
3355
3356bool isis_tlvs_area_addresses_match(struct isis_tlvs *tlvs,
3357 struct list *addresses)
3358{
3359 struct isis_area_address *addr_head;
3360
3361 addr_head = (struct isis_area_address *)tlvs->area_addresses.head;
3362 for (struct isis_area_address *addr = addr_head; addr;
3363 addr = addr->next) {
3364 struct listnode *node;
3365 struct area_addr *a;
3366
3367 for (ALL_LIST_ELEMENTS_RO(addresses, node, a)) {
3368 if (a->addr_len == addr->len
3369 && !memcmp(a->area_addr, addr->addr, addr->len))
3370 return true;
3371 }
3372 }
3373
3374 return false;
3375}
3376
3377static void tlvs_area_addresses_to_adj(struct isis_tlvs *tlvs,
3378 struct isis_adjacency *adj,
3379 bool *changed)
3380{
3381 if (adj->area_address_count != tlvs->area_addresses.count) {
3382 *changed = true;
3383 adj->area_address_count = tlvs->area_addresses.count;
3384 adj->area_addresses = XREALLOC(
3385 MTYPE_ISIS_ADJACENCY_INFO, adj->area_addresses,
3386 adj->area_address_count * sizeof(*adj->area_addresses));
3387 }
3388
3389 struct isis_area_address *addr = NULL;
3390 for (unsigned int i = 0; i < tlvs->area_addresses.count; i++) {
3391 if (!addr)
3392 addr = (struct isis_area_address *)
3393 tlvs->area_addresses.head;
3394 else
3395 addr = addr->next;
3396
3397 if (adj->area_addresses[i].addr_len == addr->len
3398 && !memcmp(adj->area_addresses[i].area_addr, addr->addr,
3399 addr->len)) {
3400 continue;
3401 }
3402
3403 *changed = true;
3404 adj->area_addresses[i].addr_len = addr->len;
3405 memcpy(adj->area_addresses[i].area_addr, addr->addr, addr->len);
3406 }
3407}
3408
3409static void tlvs_protocols_supported_to_adj(struct isis_tlvs *tlvs,
3410 struct isis_adjacency *adj,
3411 bool *changed)
3412{
3413 bool ipv4_supported = false, ipv6_supported = false;
3414
3415 for (uint8_t i = 0; i < tlvs->protocols_supported.count; i++) {
3416 if (tlvs->protocols_supported.protocols[i] == NLPID_IP)
3417 ipv4_supported = true;
3418 if (tlvs->protocols_supported.protocols[i] == NLPID_IPV6)
3419 ipv6_supported = true;
3420 }
3421
3e300703 3422 struct nlpids reduced = {};
7ef5fefc
CF
3423
3424 if (ipv4_supported && ipv6_supported) {
3425 reduced.count = 2;
3426 reduced.nlpids[0] = NLPID_IP;
3427 reduced.nlpids[1] = NLPID_IPV6;
3428 } else if (ipv4_supported) {
3429 reduced.count = 1;
3430 reduced.nlpids[0] = NLPID_IP;
3431 } else if (ipv6_supported) {
3432 reduced.count = 1;
4773e4f8 3433 reduced.nlpids[0] = NLPID_IPV6;
7ef5fefc
CF
3434 } else {
3435 reduced.count = 0;
3436 }
3437
3438 if (adj->nlpids.count == reduced.count
3439 && !memcmp(adj->nlpids.nlpids, reduced.nlpids, reduced.count))
3440 return;
3441
3442 *changed = true;
3443 adj->nlpids.count = reduced.count;
3444 memcpy(adj->nlpids.nlpids, reduced.nlpids, reduced.count);
3445}
3446
3447static void tlvs_ipv4_addresses_to_adj(struct isis_tlvs *tlvs,
3448 struct isis_adjacency *adj,
3449 bool *changed)
3450{
3451 if (adj->ipv4_address_count != tlvs->ipv4_address.count) {
3452 *changed = true;
3453 adj->ipv4_address_count = tlvs->ipv4_address.count;
3454 adj->ipv4_addresses = XREALLOC(
3455 MTYPE_ISIS_ADJACENCY_INFO, adj->ipv4_addresses,
3456 adj->ipv4_address_count * sizeof(*adj->ipv4_addresses));
3457 }
3458
3459 struct isis_ipv4_address *addr = NULL;
3460 for (unsigned int i = 0; i < tlvs->ipv4_address.count; i++) {
3461 if (!addr)
3462 addr = (struct isis_ipv4_address *)
3463 tlvs->ipv4_address.head;
3464 else
3465 addr = addr->next;
3466
3467 if (!memcmp(&adj->ipv4_addresses[i], &addr->addr,
3468 sizeof(addr->addr)))
3469 continue;
3470
3471 *changed = true;
3472 adj->ipv4_addresses[i] = addr->addr;
3473 }
3474}
3475
3476static void tlvs_ipv6_addresses_to_adj(struct isis_tlvs *tlvs,
3477 struct isis_adjacency *adj,
3478 bool *changed)
3479{
3480 if (adj->ipv6_address_count != tlvs->ipv6_address.count) {
3481 *changed = true;
3482 adj->ipv6_address_count = tlvs->ipv6_address.count;
3483 adj->ipv6_addresses = XREALLOC(
3484 MTYPE_ISIS_ADJACENCY_INFO, adj->ipv6_addresses,
3485 adj->ipv6_address_count * sizeof(*adj->ipv6_addresses));
3486 }
3487
3488 struct isis_ipv6_address *addr = NULL;
3489 for (unsigned int i = 0; i < tlvs->ipv6_address.count; i++) {
3490 if (!addr)
3491 addr = (struct isis_ipv6_address *)
3492 tlvs->ipv6_address.head;
3493 else
3494 addr = addr->next;
3495
3496 if (!memcmp(&adj->ipv6_addresses[i], &addr->addr,
3497 sizeof(addr->addr)))
3498 continue;
3499
3500 *changed = true;
3501 adj->ipv6_addresses[i] = addr->addr;
3502 }
3503}
3504
3505void isis_tlvs_to_adj(struct isis_tlvs *tlvs, struct isis_adjacency *adj,
3506 bool *changed)
3507{
3508 *changed = false;
3509
3510 tlvs_area_addresses_to_adj(tlvs, adj, changed);
3511 tlvs_protocols_supported_to_adj(tlvs, adj, changed);
3512 tlvs_ipv4_addresses_to_adj(tlvs, adj, changed);
3513 tlvs_ipv6_addresses_to_adj(tlvs, adj, changed);
3514}
3515
3516bool isis_tlvs_own_snpa_found(struct isis_tlvs *tlvs, uint8_t *snpa)
3517{
3518 struct isis_lan_neighbor *ne_head;
3519
3520 ne_head = (struct isis_lan_neighbor *)tlvs->lan_neighbor.head;
3521 for (struct isis_lan_neighbor *ne = ne_head; ne; ne = ne->next) {
3522 if (!memcmp(ne->mac, snpa, ETH_ALEN))
3523 return true;
3524 }
3525
3526 return false;
3527}
3528
3529void isis_tlvs_add_lsp_entry(struct isis_tlvs *tlvs, struct isis_lsp *lsp)
3530{
841791b6 3531 struct isis_lsp_entry *entry = XCALLOC(MTYPE_ISIS_TLV, sizeof(*entry));
7ef5fefc
CF
3532
3533 entry->rem_lifetime = lsp->hdr.rem_lifetime;
3534 memcpy(entry->id, lsp->hdr.lsp_id, ISIS_SYS_ID_LEN + 2);
3535 entry->checksum = lsp->hdr.checksum;
3536 entry->seqno = lsp->hdr.seqno;
3537 entry->lsp = lsp;
3538
3539 append_item(&tlvs->lsp_entries, (struct isis_item *)entry);
3540}
3541
3542void isis_tlvs_add_csnp_entries(struct isis_tlvs *tlvs, uint8_t *start_id,
3543 uint8_t *stop_id, uint16_t num_lsps,
4bef0ec4
DL
3544 struct lspdb_head *head,
3545 struct isis_lsp **last_lsp)
7ef5fefc 3546{
4bef0ec4
DL
3547 struct isis_lsp searchfor;
3548 struct isis_lsp *first, *lsp;
3549
3550 memcpy(&searchfor.hdr.lsp_id, start_id, sizeof(searchfor.hdr.lsp_id));
3551 first = lspdb_find_gteq(head, &searchfor);
7ef5fefc
CF
3552 if (!first)
3553 return;
3554
4bef0ec4
DL
3555 for_each_from (lspdb, head, lsp, first) {
3556 if (memcmp(lsp->hdr.lsp_id, stop_id, sizeof(lsp->hdr.lsp_id))
3557 > 0 || tlvs->lsp_entries.count == num_lsps)
7ef5fefc 3558 break;
4bef0ec4
DL
3559
3560 isis_tlvs_add_lsp_entry(tlvs, lsp);
3561 *last_lsp = lsp;
7ef5fefc
CF
3562 }
3563}
3564
3565void isis_tlvs_set_dynamic_hostname(struct isis_tlvs *tlvs,
3566 const char *hostname)
3567{
841791b6 3568 XFREE(MTYPE_ISIS_TLV, tlvs->hostname);
7ef5fefc 3569 if (hostname)
841791b6 3570 tlvs->hostname = XSTRDUP(MTYPE_ISIS_TLV, hostname);
7ef5fefc
CF
3571}
3572
3573void isis_tlvs_set_te_router_id(struct isis_tlvs *tlvs,
3574 const struct in_addr *id)
3575{
841791b6 3576 XFREE(MTYPE_ISIS_TLV, tlvs->te_router_id);
7ef5fefc
CF
3577 if (!id)
3578 return;
841791b6 3579 tlvs->te_router_id = XCALLOC(MTYPE_ISIS_TLV, sizeof(*id));
7ef5fefc
CF
3580 memcpy(tlvs->te_router_id, id, sizeof(*id));
3581}
3582
3583void isis_tlvs_add_oldstyle_ip_reach(struct isis_tlvs *tlvs,
3584 struct prefix_ipv4 *dest, uint8_t metric)
3585{
841791b6 3586 struct isis_oldstyle_ip_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
7ef5fefc
CF
3587
3588 r->metric = metric;
3589 memcpy(&r->prefix, dest, sizeof(*dest));
3590 apply_mask_ipv4(&r->prefix);
3591 append_item(&tlvs->oldstyle_ip_reach, (struct isis_item *)r);
3592}
3593
3594void isis_tlvs_add_extended_ip_reach(struct isis_tlvs *tlvs,
3595 struct prefix_ipv4 *dest, uint32_t metric)
3596{
841791b6 3597 struct isis_extended_ip_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
7ef5fefc
CF
3598
3599 r->metric = metric;
3600 memcpy(&r->prefix, dest, sizeof(*dest));
3601 apply_mask_ipv4(&r->prefix);
3602 append_item(&tlvs->extended_ip_reach, (struct isis_item *)r);
3603}
3604
3605void isis_tlvs_add_ipv6_reach(struct isis_tlvs *tlvs, uint16_t mtid,
3606 struct prefix_ipv6 *dest, uint32_t metric)
3607{
841791b6 3608 struct isis_ipv6_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
7ef5fefc
CF
3609
3610 r->metric = metric;
3611 memcpy(&r->prefix, dest, sizeof(*dest));
3612 apply_mask_ipv6(&r->prefix);
3613
3614 struct isis_item_list *l;
3615 l = (mtid == ISIS_MT_IPV4_UNICAST)
3616 ? &tlvs->ipv6_reach
3617 : isis_get_mt_items(&tlvs->mt_ipv6_reach, mtid);
3618 append_item(l, (struct isis_item *)r);
3619}
3620
d43d2df5
CF
3621void isis_tlvs_add_ipv6_dstsrc_reach(struct isis_tlvs *tlvs, uint16_t mtid,
3622 struct prefix_ipv6 *dest,
3623 struct prefix_ipv6 *src,
3624 uint32_t metric)
3625{
3626 isis_tlvs_add_ipv6_reach(tlvs, mtid, dest, metric);
3627 struct isis_item_list *l = isis_get_mt_items(&tlvs->mt_ipv6_reach,
3628 mtid);
3629
3630 struct isis_ipv6_reach *r = (struct isis_ipv6_reach*)last_item(l);
bd507085 3631 r->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IPV6_REACH);
d43d2df5
CF
3632 r->subtlvs->source_prefix = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*src));
3633 memcpy(r->subtlvs->source_prefix, src, sizeof(*src));
3634}
3635
7ef5fefc
CF
3636void isis_tlvs_add_oldstyle_reach(struct isis_tlvs *tlvs, uint8_t *id,
3637 uint8_t metric)
3638{
841791b6 3639 struct isis_oldstyle_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
7ef5fefc
CF
3640
3641 r->metric = metric;
3642 memcpy(r->id, id, sizeof(r->id));
3643 append_item(&tlvs->oldstyle_reach, (struct isis_item *)r);
3644}
3645
3646void isis_tlvs_add_extended_reach(struct isis_tlvs *tlvs, uint16_t mtid,
3647 uint8_t *id, uint32_t metric,
3648 uint8_t *subtlvs, uint8_t subtlv_len)
3649{
841791b6 3650 struct isis_extended_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
7ef5fefc
CF
3651
3652 memcpy(r->id, id, sizeof(r->id));
3653 r->metric = metric;
3654 if (subtlvs && subtlv_len) {
841791b6 3655 r->subtlvs = XCALLOC(MTYPE_ISIS_TLV, subtlv_len);
7ef5fefc
CF
3656 memcpy(r->subtlvs, subtlvs, subtlv_len);
3657 r->subtlv_len = subtlv_len;
3658 }
3659
3660 struct isis_item_list *l;
3661 if (mtid == ISIS_MT_IPV4_UNICAST)
3662 l = &tlvs->extended_reach;
3663 else
3664 l = isis_get_mt_items(&tlvs->mt_reach, mtid);
3665 append_item(l, (struct isis_item *)r);
3666}
3667
9fe21208
CF
3668void isis_tlvs_add_threeway_adj(struct isis_tlvs *tlvs,
3669 enum isis_threeway_state state,
3670 uint32_t local_circuit_id,
3671 const uint8_t *neighbor_id,
3672 uint32_t neighbor_circuit_id)
3673{
3674 assert(!tlvs->threeway_adj);
3675
3676 tlvs->threeway_adj = XCALLOC(MTYPE_ISIS_TLV, sizeof(*tlvs->threeway_adj));
3677 tlvs->threeway_adj->state = state;
3678 tlvs->threeway_adj->local_circuit_id = local_circuit_id;
3679
3680 if (neighbor_id) {
3681 tlvs->threeway_adj->neighbor_set = true;
3682 memcpy(tlvs->threeway_adj->neighbor_id, neighbor_id, 6);
3683 tlvs->threeway_adj->neighbor_circuit_id = neighbor_circuit_id;
3684 }
3685}
3686
41a145f1
CF
3687void isis_tlvs_add_spine_leaf(struct isis_tlvs *tlvs, uint8_t tier,
3688 bool has_tier, bool is_leaf, bool is_spine,
3689 bool is_backup)
3690{
3691 assert(!tlvs->spine_leaf);
3692
3693 tlvs->spine_leaf = XCALLOC(MTYPE_ISIS_TLV, sizeof(*tlvs->spine_leaf));
3694
3695 if (has_tier) {
3696 tlvs->spine_leaf->tier = tier;
3697 }
3698
3699 tlvs->spine_leaf->has_tier = has_tier;
3700 tlvs->spine_leaf->is_leaf = is_leaf;
3701 tlvs->spine_leaf->is_spine = is_spine;
3702 tlvs->spine_leaf->is_backup = is_backup;
3703}
3704
7ef5fefc
CF
3705struct isis_mt_router_info *
3706isis_tlvs_lookup_mt_router_info(struct isis_tlvs *tlvs, uint16_t mtid)
3707{
3708 if (tlvs->mt_router_info_empty)
3709 return NULL;
3710
3711 struct isis_mt_router_info *rv;
3712 for (rv = (struct isis_mt_router_info *)tlvs->mt_router_info.head; rv;
3713 rv = rv->next) {
3714 if (rv->mtid == mtid)
3715 return rv;
3716 }
3717
3718 return NULL;
3719}
2c92bee4
CF
3720
3721void isis_tlvs_set_purge_originator(struct isis_tlvs *tlvs,
3722 const uint8_t *generator,
3723 const uint8_t *sender)
3724{
3725 assert(!tlvs->purge_originator);
3726
3727 tlvs->purge_originator = XCALLOC(MTYPE_ISIS_TLV,
3728 sizeof(*tlvs->purge_originator));
3729 memcpy(tlvs->purge_originator->generator, generator,
3730 sizeof(tlvs->purge_originator->generator));
3731 if (sender) {
3732 tlvs->purge_originator->sender_set = true;
3733 memcpy(tlvs->purge_originator->sender, sender,
3734 sizeof(tlvs->purge_originator->sender));
3735 }
3736}