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