]> git.proxmox.com Git - mirror_frr.git/blame - isisd/isis_tlvs.c
*: Convert numeric 32 into IPV4_MAX_BITLEN for prefixlen
[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 *
1b3f47d0
OD
6 * Copyright (C) 2019 Olivier Dugeon - Orange Labs (for TE and SR)
7 *
7ef5fefc
CF
8 * This file is part of FRR.
9 *
10 * FRR is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the
12 * Free Software Foundation; either version 2, or (at your option) any
13 * later version.
14 *
15 * FRR is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with FRR; see the file COPYING. If not, write to the Free
22 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
23 * 02111-1307, USA.
24 */
25#include <zebra.h>
26
6252100f 27#ifdef CRYPTO_INTERNAL
7ef5fefc 28#include "md5.h"
6252100f 29#endif
7ef5fefc
CF
30#include "memory.h"
31#include "stream.h"
32#include "sbuf.h"
1b3f47d0 33#include "network.h"
7ef5fefc
CF
34
35#include "isisd/isisd.h"
841791b6 36#include "isisd/isis_tlvs.h"
7ef5fefc
CF
37#include "isisd/isis_common.h"
38#include "isisd/isis_mt.h"
39#include "isisd/isis_misc.h"
40#include "isisd/isis_adjacency.h"
41#include "isisd/isis_circuit.h"
42#include "isisd/isis_pdu.h"
43#include "isisd/isis_lsp.h"
44#include "isisd/isis_te.h"
26f6acaf 45#include "isisd/isis_sr.h"
7ef5fefc 46
bf8d3d6a
DL
47DEFINE_MTYPE_STATIC(ISISD, ISIS_TLV, "ISIS TLVs");
48DEFINE_MTYPE(ISISD, ISIS_SUBTLV, "ISIS Sub-TLVs");
49DEFINE_MTYPE_STATIC(ISISD, ISIS_MT_ITEM_LIST, "ISIS MT Item Lists");
7ef5fefc
CF
50
51typedef int (*unpack_tlv_func)(enum isis_tlv_context context, uint8_t tlv_type,
52 uint8_t tlv_len, struct stream *s,
53 struct sbuf *log, void *dest, int indent);
7ce31bab
DS
54typedef int (*pack_item_func)(struct isis_item *item, struct stream *s,
55 size_t *min_length);
7ef5fefc
CF
56typedef void (*free_item_func)(struct isis_item *i);
57typedef int (*unpack_item_func)(uint16_t mtid, uint8_t len, struct stream *s,
58 struct sbuf *log, void *dest, int indent);
59typedef void (*format_item_func)(uint16_t mtid, struct isis_item *i,
60 struct sbuf *buf, int indent);
61typedef struct isis_item *(*copy_item_func)(struct isis_item *i);
62
63struct tlv_ops {
64 const char *name;
65 unpack_tlv_func unpack;
66
67 pack_item_func pack_item;
68 free_item_func free_item;
69 unpack_item_func unpack_item;
70 format_item_func format_item;
71 copy_item_func copy_item;
72};
73
74enum how_to_pack {
75 ISIS_ITEMS,
76 ISIS_MT_ITEMS,
77};
78
79struct pack_order_entry {
80 enum isis_tlv_context context;
81 enum isis_tlv_type type;
82 enum how_to_pack how_to_pack;
83 size_t what_to_pack;
84};
85#define PACK_ENTRY(t, h, w) \
86 { \
87 .context = ISIS_CONTEXT_LSP, .type = ISIS_TLV_##t, \
88 .how_to_pack = (h), \
89 .what_to_pack = offsetof(struct isis_tlvs, w), \
90 }
91
2b64873d 92static const struct pack_order_entry pack_order[] = {
7ef5fefc
CF
93 PACK_ENTRY(OLDSTYLE_REACH, ISIS_ITEMS, oldstyle_reach),
94 PACK_ENTRY(LAN_NEIGHBORS, ISIS_ITEMS, lan_neighbor),
95 PACK_ENTRY(LSP_ENTRY, ISIS_ITEMS, lsp_entries),
96 PACK_ENTRY(EXTENDED_REACH, ISIS_ITEMS, extended_reach),
97 PACK_ENTRY(MT_REACH, ISIS_MT_ITEMS, mt_reach),
98 PACK_ENTRY(OLDSTYLE_IP_REACH, ISIS_ITEMS, oldstyle_ip_reach),
99 PACK_ENTRY(OLDSTYLE_IP_REACH_EXT, ISIS_ITEMS, oldstyle_ip_reach_ext),
100 PACK_ENTRY(IPV4_ADDRESS, ISIS_ITEMS, ipv4_address),
101 PACK_ENTRY(IPV6_ADDRESS, ISIS_ITEMS, ipv6_address),
102 PACK_ENTRY(EXTENDED_IP_REACH, ISIS_ITEMS, extended_ip_reach),
103 PACK_ENTRY(MT_IP_REACH, ISIS_MT_ITEMS, mt_ip_reach),
104 PACK_ENTRY(IPV6_REACH, ISIS_ITEMS, ipv6_reach),
1b3f47d0
OD
105 PACK_ENTRY(MT_IPV6_REACH, ISIS_MT_ITEMS, mt_ipv6_reach)
106};
7ef5fefc
CF
107
108/* This is a forward definition. The table is actually initialized
109 * in at the bottom. */
2b64873d 110static const struct tlv_ops *const tlv_table[ISIS_CONTEXT_MAX][ISIS_TLV_MAX];
7ef5fefc
CF
111
112/* End of _ops forward definition. */
113
114/* Prototypes */
115static void append_item(struct isis_item_list *dest, struct isis_item *item);
1b3f47d0 116static void init_item_list(struct isis_item_list *items);
7ef5fefc 117
1b3f47d0
OD
118/* Functions for Extended IS Reachability SubTLVs a.k.a Traffic Engineering */
119struct isis_ext_subtlvs *isis_alloc_ext_subtlvs(void)
120{
121 struct isis_ext_subtlvs *ext;
122
123 ext = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(struct isis_ext_subtlvs));
124 init_item_list(&ext->adj_sid);
125 init_item_list(&ext->lan_sid);
126
127 return ext;
128}
129
130/*
131 * mtid parameter is used to determine if Adjacency is related to IPv4 or IPv6.
132 * A negative value could be used to skip copy of Adjacency SID.
133 */
134static struct isis_ext_subtlvs *
135copy_item_ext_subtlvs(struct isis_ext_subtlvs *exts, int16_t mtid)
136{
137 struct isis_ext_subtlvs *rv = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*rv));
138 struct isis_adj_sid *adj;
139 struct isis_lan_adj_sid *lan;
140
141 memcpy(rv, exts, sizeof(struct isis_ext_subtlvs));
142 init_item_list(&rv->adj_sid);
143 init_item_list(&rv->lan_sid);
144
145 UNSET_SUBTLV(rv, EXT_ADJ_SID);
146 UNSET_SUBTLV(rv, EXT_LAN_ADJ_SID);
147
148 /* Copy Adj SID and LAN Adj SID list for IPv4 if needed */
149 for (adj = (struct isis_adj_sid *)exts->adj_sid.head; adj != NULL;
150 adj = adj->next) {
151 if ((mtid != -1)
152 && (((mtid == ISIS_MT_IPV4_UNICAST)
153 && (adj->family != AF_INET))
154 || ((mtid == ISIS_MT_IPV6_UNICAST)
155 && (adj->family != AF_INET6))))
156 continue;
157
158 struct isis_adj_sid *new;
159
160 new = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(struct isis_adj_sid));
161 new->family = adj->family;
162 new->flags = adj->flags;
163 new->weight = adj->weight;
164 new->sid = adj->sid;
165 append_item(&rv->adj_sid, (struct isis_item *)new);
166 SET_SUBTLV(rv, EXT_ADJ_SID);
167 }
bd507085 168
1b3f47d0
OD
169 for (lan = (struct isis_lan_adj_sid *)exts->lan_sid.head; lan != NULL;
170 lan = lan->next) {
171 if ((mtid != -1)
172 && (((mtid == ISIS_MT_IPV4_UNICAST)
173 && (lan->family != AF_INET))
174 || ((mtid == ISIS_MT_IPV6_UNICAST)
175 && (lan->family != AF_INET6))))
176 continue;
177
178 struct isis_lan_adj_sid *new;
179
180 new = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(struct isis_lan_adj_sid));
181 new->family = lan->family;
182 new->flags = lan->flags;
183 new->weight = lan->weight;
184 memcpy(new->neighbor_id, lan->neighbor_id, 6);
185 new->sid = lan->sid;
186 append_item(&rv->lan_sid, (struct isis_item *)new);
187 SET_SUBTLV(rv, EXT_LAN_ADJ_SID);
188 }
189
190 return rv;
191}
192
193/* mtid parameter is used to manage multi-topology i.e. IPv4 / IPv6 */
194static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts,
195 struct sbuf *buf, int indent,
196 uint16_t mtid)
197{
198
199 char ibuf[PREFIX2STR_BUFFER];
200
201 /* Standard metrics */
202 if (IS_SUBTLV(exts, EXT_ADM_GRP))
6cde4b45 203 sbuf_push(buf, indent, "Administrative Group: 0x%x\n",
1b3f47d0
OD
204 exts->adm_group);
205 if (IS_SUBTLV(exts, EXT_LLRI)) {
6cde4b45 206 sbuf_push(buf, indent, "Link Local ID: %u\n",
1b3f47d0 207 exts->local_llri);
6cde4b45 208 sbuf_push(buf, indent, "Link Remote ID: %u\n",
1b3f47d0
OD
209 exts->remote_llri);
210 }
211 if (IS_SUBTLV(exts, EXT_LOCAL_ADDR))
a854ea43
MS
212 sbuf_push(buf, indent, "Local Interface IP Address(es): %pI4\n",
213 &exts->local_addr);
1b3f47d0 214 if (IS_SUBTLV(exts, EXT_NEIGH_ADDR))
a854ea43
MS
215 sbuf_push(buf, indent, "Remote Interface IP Address(es): %pI4\n",
216 &exts->neigh_addr);
1b3f47d0
OD
217 if (IS_SUBTLV(exts, EXT_LOCAL_ADDR6))
218 sbuf_push(buf, indent, "Local Interface IPv6 Address(es): %s\n",
219 inet_ntop(AF_INET6, &exts->local_addr6, ibuf,
220 PREFIX2STR_BUFFER));
221 if (IS_SUBTLV(exts, EXT_NEIGH_ADDR6))
222 sbuf_push(buf, indent, "Remote Interface IPv6 Address(es): %s\n",
223 inet_ntop(AF_INET6, &exts->local_addr6, ibuf,
224 PREFIX2STR_BUFFER));
225 if (IS_SUBTLV(exts, EXT_MAX_BW))
226 sbuf_push(buf, indent, "Maximum Bandwidth: %g (Bytes/sec)\n",
227 exts->max_bw);
228 if (IS_SUBTLV(exts, EXT_MAX_RSV_BW))
229 sbuf_push(buf, indent,
230 "Maximum Reservable Bandwidth: %g (Bytes/sec)\n",
231 exts->max_rsv_bw);
232 if (IS_SUBTLV(exts, EXT_UNRSV_BW)) {
233 sbuf_push(buf, indent, "Unreserved Bandwidth:\n");
234 for (int j = 0; j < MAX_CLASS_TYPE; j += 2) {
235 sbuf_push(buf, indent + 2,
236 "[%d]: %g (Bytes/sec),\t[%d]: %g (Bytes/sec)\n",
237 j, exts->unrsv_bw[j],
238 j + 1, exts->unrsv_bw[j + 1]);
239 }
240 }
241 if (IS_SUBTLV(exts, EXT_TE_METRIC))
242 sbuf_push(buf, indent, "Traffic Engineering Metric: %u\n",
243 exts->te_metric);
244 if (IS_SUBTLV(exts, EXT_RMT_AS))
245 sbuf_push(buf, indent,
6cde4b45 246 "Inter-AS TE Remote AS number: %u\n",
1b3f47d0
OD
247 exts->remote_as);
248 if (IS_SUBTLV(exts, EXT_RMT_IP))
249 sbuf_push(buf, indent,
a854ea43
MS
250 "Inter-AS TE Remote ASBR IP address: %pI4\n",
251 &exts->remote_ip);
1b3f47d0
OD
252 /* Extended metrics */
253 if (IS_SUBTLV(exts, EXT_DELAY))
254 sbuf_push(buf, indent,
6cde4b45 255 "%s Average Link Delay: %u (micro-sec)\n",
1b3f47d0
OD
256 IS_ANORMAL(exts->delay) ? "Anomalous" : "Normal",
257 exts->delay);
258 if (IS_SUBTLV(exts, EXT_MM_DELAY)) {
6cde4b45 259 sbuf_push(buf, indent, "%s Min/Max Link Delay: %u / %u (micro-sec)\n",
1b3f47d0
OD
260 IS_ANORMAL(exts->min_delay) ? "Anomalous" : "Normal",
261 exts->min_delay & TE_EXT_MASK,
262 exts->max_delay & TE_EXT_MASK);
263 }
264 if (IS_SUBTLV(exts, EXT_DELAY_VAR)) {
265 sbuf_push(buf, indent,
6cde4b45 266 "Delay Variation: %u (micro-sec)\n",
1b3f47d0
OD
267 exts->delay_var & TE_EXT_MASK);
268 }
269 if (IS_SUBTLV(exts, EXT_PKT_LOSS))
270 sbuf_push(buf, indent, "%s Link Packet Loss: %g (%%)\n",
271 IS_ANORMAL(exts->pkt_loss) ? "Anomalous" : "Normal",
272 (float)((exts->pkt_loss & TE_EXT_MASK)
273 * LOSS_PRECISION));
274 if (IS_SUBTLV(exts, EXT_RES_BW))
275 sbuf_push(buf, indent,
276 "Unidir. Residual Bandwidth: %g (Bytes/sec)\n",
277 exts->res_bw);
278 if (IS_SUBTLV(exts, EXT_AVA_BW))
279 sbuf_push(buf, indent,
280 "Unidir. Available Bandwidth: %g (Bytes/sec)\n",
281 exts->ava_bw);
282 if (IS_SUBTLV(exts, EXT_USE_BW))
283 sbuf_push(buf, indent,
284 "Unidir. Utilized Bandwidth: %g (Bytes/sec)\n",
285 exts->use_bw);
f2333421 286 /* Segment Routing Adjacency as per RFC8667 section #2.2.1 */
1b3f47d0
OD
287 if (IS_SUBTLV(exts, EXT_ADJ_SID)) {
288 struct isis_adj_sid *adj;
289
290 for (adj = (struct isis_adj_sid *)exts->adj_sid.head; adj;
291 adj = adj->next) {
292 if (((mtid == ISIS_MT_IPV4_UNICAST)
293 && (adj->family != AF_INET))
294 || ((mtid == ISIS_MT_IPV6_UNICAST)
295 && (adj->family != AF_INET6)))
296 continue;
297 sbuf_push(
298 buf, indent,
6cde4b45 299 "Adjacency-SID: %u, Weight: %hhu, Flags: F:%c B:%c, V:%c, L:%c, S:%c, P:%c\n",
1b3f47d0
OD
300 adj->sid, adj->weight,
301 adj->flags & EXT_SUBTLV_LINK_ADJ_SID_FFLG ? '1'
302 : '0',
303 adj->flags & EXT_SUBTLV_LINK_ADJ_SID_BFLG ? '1'
304 : '0',
305 adj->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG ? '1'
306 : '0',
307 adj->flags & EXT_SUBTLV_LINK_ADJ_SID_LFLG ? '1'
308 : '0',
309 adj->flags & EXT_SUBTLV_LINK_ADJ_SID_SFLG ? '1'
310 : '0',
311 adj->flags & EXT_SUBTLV_LINK_ADJ_SID_PFLG
312 ? '1'
313 : '0');
314 }
315 }
f2333421 316 /* Segment Routing LAN-Adjacency as per RFC8667 section #2.2.2 */
1b3f47d0
OD
317 if (IS_SUBTLV(exts, EXT_LAN_ADJ_SID)) {
318 struct isis_lan_adj_sid *lan;
319
320 for (lan = (struct isis_lan_adj_sid *)exts->lan_sid.head;
321 lan; lan = lan->next) {
322 if (((mtid == ISIS_MT_IPV4_UNICAST)
323 && (lan->family != AF_INET))
324 || ((mtid == ISIS_MT_IPV6_UNICAST)
325 && (lan->family != AF_INET6)))
326 continue;
327 sbuf_push(buf, indent,
6cde4b45 328 "Lan-Adjacency-SID: %u, Weight: %hhu, Flags: F:%c B:%c, V:%c, L:%c, S:%c, P:%c\n"
1b3f47d0
OD
329 " Neighbor-ID: %s\n",
330 lan->sid, lan->weight,
331 lan->flags & EXT_SUBTLV_LINK_ADJ_SID_FFLG
332 ? '1'
333 : '0',
334 lan->flags & EXT_SUBTLV_LINK_ADJ_SID_BFLG
335 ? '1'
336 : '0',
337 lan->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG
338 ? '1'
339 : '0',
340 lan->flags & EXT_SUBTLV_LINK_ADJ_SID_LFLG
341 ? '1'
342 : '0',
343 lan->flags & EXT_SUBTLV_LINK_ADJ_SID_SFLG
344 ? '1'
345 : '0',
346 lan->flags & EXT_SUBTLV_LINK_ADJ_SID_PFLG
347 ? '1'
348 : '0',
349 isis_format_id(lan->neighbor_id, 6));
350 }
351 }
352}
353
354static void free_item_ext_subtlvs(struct isis_ext_subtlvs *exts)
355{
356 struct isis_item *item, *next_item;
357
358 /* First, free Adj SID and LAN Adj SID list if needed */
359 for (item = exts->adj_sid.head; item; item = next_item) {
360 next_item = item->next;
361 XFREE(MTYPE_ISIS_SUBTLV, item);
362 }
363 for (item = exts->lan_sid.head; item; item = next_item) {
364 next_item = item->next;
365 XFREE(MTYPE_ISIS_SUBTLV, item);
366 }
367 XFREE(MTYPE_ISIS_SUBTLV, exts);
368}
369
370static int pack_item_ext_subtlvs(struct isis_ext_subtlvs *exts,
7ce31bab 371 struct stream *s, size_t *min_len)
1b3f47d0
OD
372{
373 uint8_t size;
374
7ce31bab
DS
375 if (STREAM_WRITEABLE(s) < ISIS_SUBTLV_MAX_SIZE) {
376 *min_len = ISIS_SUBTLV_MAX_SIZE;
1b3f47d0 377 return 1;
7ce31bab 378 }
1b3f47d0
OD
379
380 if (IS_SUBTLV(exts, EXT_ADM_GRP)) {
381 stream_putc(s, ISIS_SUBTLV_ADMIN_GRP);
382 stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
383 stream_putl(s, exts->adm_group);
384 }
385 if (IS_SUBTLV(exts, EXT_LLRI)) {
386 stream_putc(s, ISIS_SUBTLV_LLRI);
387 stream_putc(s, ISIS_SUBTLV_LLRI_SIZE);
388 stream_putl(s, exts->local_llri);
389 stream_putl(s, exts->remote_llri);
390 }
391 if (IS_SUBTLV(exts, EXT_LOCAL_ADDR)) {
392 stream_putc(s, ISIS_SUBTLV_LOCAL_IPADDR);
393 stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
394 stream_put(s, &exts->local_addr.s_addr, 4);
395 }
396 if (IS_SUBTLV(exts, EXT_NEIGH_ADDR)) {
397 stream_putc(s, ISIS_SUBTLV_RMT_IPADDR);
398 stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
399 stream_put(s, &exts->neigh_addr.s_addr, 4);
400 }
401 if (IS_SUBTLV(exts, EXT_LOCAL_ADDR6)) {
402 stream_putc(s, ISIS_SUBTLV_LOCAL_IPADDR6);
403 stream_putc(s, ISIS_SUBTLV_IPV6_ADDR_SIZE);
404 stream_put(s, &exts->local_addr6, 16);
405 }
406 if (IS_SUBTLV(exts, EXT_NEIGH_ADDR6)) {
407 stream_putc(s, ISIS_SUBTLV_RMT_IPADDR6);
408 stream_putc(s, ISIS_SUBTLV_IPV6_ADDR_SIZE);
409 stream_put(s, &exts->neigh_addr6, 16);
410 }
411 if (IS_SUBTLV(exts, EXT_MAX_BW)) {
412 stream_putc(s, ISIS_SUBTLV_MAX_BW);
413 stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
414 stream_putf(s, exts->max_bw);
415 }
416 if (IS_SUBTLV(exts, EXT_MAX_RSV_BW)) {
417 stream_putc(s, ISIS_SUBTLV_MAX_RSV_BW);
418 stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
419 stream_putf(s, exts->max_rsv_bw);
420 }
421 if (IS_SUBTLV(exts, EXT_UNRSV_BW)) {
422 stream_putc(s, ISIS_SUBTLV_UNRSV_BW);
423 stream_putc(s, ISIS_SUBTLV_UNRSV_BW_SIZE);
424 for (int j = 0; j < MAX_CLASS_TYPE; j++)
425 stream_putf(s, exts->unrsv_bw[j]);
426 }
427 if (IS_SUBTLV(exts, EXT_TE_METRIC)) {
428 stream_putc(s, ISIS_SUBTLV_TE_METRIC);
429 stream_putc(s, ISIS_SUBTLV_TE_METRIC_SIZE);
430 stream_put3(s, exts->te_metric);
431 }
432 if (IS_SUBTLV(exts, EXT_RMT_AS)) {
433 stream_putc(s, ISIS_SUBTLV_RAS);
434 stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
435 stream_putl(s, exts->remote_as);
436 }
437 if (IS_SUBTLV(exts, EXT_RMT_IP)) {
438 stream_putc(s, ISIS_SUBTLV_RIP);
439 stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
440 stream_put(s, &exts->remote_ip.s_addr, 4);
441 }
442 if (IS_SUBTLV(exts, EXT_DELAY)) {
443 stream_putc(s, ISIS_SUBTLV_AV_DELAY);
444 stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
445 stream_putl(s, exts->delay);
446 }
447 if (IS_SUBTLV(exts, EXT_MM_DELAY)) {
448 stream_putc(s, ISIS_SUBTLV_MM_DELAY);
449 stream_putc(s, ISIS_SUBTLV_MM_DELAY_SIZE);
450 stream_putl(s, exts->min_delay);
451 stream_putl(s, exts->max_delay);
452 }
453 if (IS_SUBTLV(exts, EXT_DELAY_VAR)) {
454 stream_putc(s, ISIS_SUBTLV_DELAY_VAR);
455 stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
456 stream_putl(s, exts->delay_var);
457 }
458 if (IS_SUBTLV(exts, EXT_PKT_LOSS)) {
459 stream_putc(s, ISIS_SUBTLV_PKT_LOSS);
460 stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
461 stream_putl(s, exts->pkt_loss);
462 }
463 if (IS_SUBTLV(exts, EXT_RES_BW)) {
464 stream_putc(s, ISIS_SUBTLV_RES_BW);
465 stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
466 stream_putf(s, exts->res_bw);
467 }
468 if (IS_SUBTLV(exts, EXT_AVA_BW)) {
469 stream_putc(s, ISIS_SUBTLV_AVA_BW);
470 stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
471 stream_putf(s, exts->ava_bw);
472 }
473 if (IS_SUBTLV(exts, EXT_USE_BW)) {
474 stream_putc(s, ISIS_SUBTLV_USE_BW);
475 stream_putc(s, ISIS_SUBTLV_DEF_SIZE);
476 stream_putf(s, exts->use_bw);
477 }
f2333421 478 /* Segment Routing Adjacency as per RFC8667 section #2.2.1 */
1b3f47d0
OD
479 if (IS_SUBTLV(exts, EXT_ADJ_SID)) {
480 struct isis_adj_sid *adj;
481
482 for (adj = (struct isis_adj_sid *)exts->adj_sid.head; adj;
483 adj = adj->next) {
484 stream_putc(s, ISIS_SUBTLV_ADJ_SID);
485 size = ISIS_SUBTLV_ADJ_SID_SIZE;
486 if (!(adj->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG))
487 size++;
488 stream_putc(s, size);
489 stream_putc(s, adj->flags);
490 stream_putc(s, adj->weight);
491 if (adj->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG)
492 stream_put3(s, adj->sid);
493 else
494 stream_putl(s, adj->sid);
495
496 }
497 }
f2333421 498 /* Segment Routing LAN-Adjacency as per RFC8667 section #2.2.2 */
1b3f47d0
OD
499 if (IS_SUBTLV(exts, EXT_LAN_ADJ_SID)) {
500 struct isis_lan_adj_sid *lan;
501
502 for (lan = (struct isis_lan_adj_sid *)exts->lan_sid.head; lan;
503 lan = lan->next) {
504 stream_putc(s, ISIS_SUBTLV_LAN_ADJ_SID);
505 size = ISIS_SUBTLV_LAN_ADJ_SID_SIZE;
506 if (!(lan->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG))
507 size++;
508 stream_putc(s, size);
509 stream_putc(s, lan->flags);
510 stream_putc(s, lan->weight);
511 stream_put(s, lan->neighbor_id, 6);
512 if (lan->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG)
513 stream_put3(s, lan->sid);
514 else
515 stream_putl(s, lan->sid);
516 }
517 }
518
519 return 0;
520}
521
522static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s,
523 struct sbuf *log, void *dest, int indent)
524{
525 uint8_t sum = 0;
526 uint8_t subtlv_type;
527 uint8_t subtlv_len;
528
529 struct isis_extended_reach *rv = dest;
530 struct isis_ext_subtlvs *exts = isis_alloc_ext_subtlvs();
531
532 rv->subtlvs = exts;
533
534 /*
535 * Parse subTLVs until reach subTLV length
536 * Check that it remains at least 2 bytes: subTLV Type & Length
537 */
538 while (len > sum + 2) {
539 /* Read SubTLV Type and Length */
540 subtlv_type = stream_getc(s);
541 subtlv_len = stream_getc(s);
542 if (subtlv_len > len - sum) {
47b13e9b 543 sbuf_push(log, indent, "TLV %hhu: Available data %u is less than TLV size %u !\n",
1b3f47d0
OD
544 subtlv_type, len - sum, subtlv_len);
545 return 1;
546 }
547
548 switch (subtlv_type) {
549 /* Standard Metric as defined in RFC5305 */
550 case ISIS_SUBTLV_ADMIN_GRP:
551 if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
552 sbuf_push(log, indent,
553 "TLV size does not match expected size for Administrative Group!\n");
554 } else {
555 exts->adm_group = stream_getl(s);
556 SET_SUBTLV(exts, EXT_ADM_GRP);
557 }
558 break;
559 case ISIS_SUBTLV_LLRI:
560 if (subtlv_len != ISIS_SUBTLV_LLRI_SIZE) {
561 sbuf_push(log, indent,
562 "TLV size does not match expected size for Link ID!\n");
563 } else {
564 exts->local_llri = stream_getl(s);
565 exts->remote_llri = stream_getl(s);
566 SET_SUBTLV(exts, EXT_LLRI);
567 }
568 break;
569 case ISIS_SUBTLV_LOCAL_IPADDR:
570 if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
571 sbuf_push(log, indent,
572 "TLV size does not match expected size for Local IP address!\n");
573 } else {
574 stream_get(&exts->local_addr.s_addr, s, 4);
575 SET_SUBTLV(exts, EXT_LOCAL_ADDR);
576 }
577 break;
578 case ISIS_SUBTLV_RMT_IPADDR:
579 if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
580 sbuf_push(log, indent,
581 "TLV size does not match expected size for Remote IP address!\n");
582 } else {
583 stream_get(&exts->neigh_addr.s_addr, s, 4);
584 SET_SUBTLV(exts, EXT_NEIGH_ADDR);
585 }
586 break;
587 case ISIS_SUBTLV_LOCAL_IPADDR6:
588 if (subtlv_len != ISIS_SUBTLV_IPV6_ADDR_SIZE) {
589 sbuf_push(log, indent,
590 "TLV size does not match expected size for Local IPv6 address!\n");
591 } else {
592 stream_get(&exts->local_addr6, s, 16);
593 SET_SUBTLV(exts, EXT_LOCAL_ADDR6);
594 }
595 break;
596 case ISIS_SUBTLV_RMT_IPADDR6:
597 if (subtlv_len != ISIS_SUBTLV_IPV6_ADDR_SIZE) {
598 sbuf_push(log, indent,
599 "TLV size does not match expected size for Remote IPv6 address!\n");
600 } else {
601 stream_get(&exts->neigh_addr6, s, 16);
602 SET_SUBTLV(exts, EXT_NEIGH_ADDR6);
603 }
604 break;
605 case ISIS_SUBTLV_MAX_BW:
606 if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
607 sbuf_push(log, indent,
608 "TLV size does not match expected size for Maximum Bandwidth!\n");
609 } else {
610 exts->max_bw = stream_getf(s);
611 SET_SUBTLV(exts, EXT_MAX_BW);
612 }
613 break;
614 case ISIS_SUBTLV_MAX_RSV_BW:
615 if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
616 sbuf_push(log, indent,
617 "TLV size does not match expected size for Maximum Reservable Bandwidth!\n");
618 } else {
619 exts->max_rsv_bw = stream_getf(s);
620 SET_SUBTLV(exts, EXT_MAX_RSV_BW);
621 }
622 break;
623 case ISIS_SUBTLV_UNRSV_BW:
624 if (subtlv_len != ISIS_SUBTLV_UNRSV_BW_SIZE) {
625 sbuf_push(log, indent,
626 "TLV size does not match expected size for Unreserved Bandwidth!\n");
627 } else {
628 for (int i = 0; i < MAX_CLASS_TYPE; i++)
629 exts->unrsv_bw[i] = stream_getf(s);
630 SET_SUBTLV(exts, EXT_UNRSV_BW);
631 }
632 break;
633 case ISIS_SUBTLV_TE_METRIC:
634 if (subtlv_len != ISIS_SUBTLV_TE_METRIC_SIZE) {
635 sbuf_push(log, indent,
636 "TLV size does not match expected size for Traffic Engineering Metric!\n");
637 } else {
638 exts->te_metric = stream_get3(s);
639 SET_SUBTLV(exts, EXT_TE_METRIC);
640 }
641 break;
642 case ISIS_SUBTLV_RAS:
643 if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
644 sbuf_push(log, indent,
645 "TLV size does not match expected size for Remote AS number!\n");
646 } else {
647 exts->remote_as = stream_getl(s);
648 SET_SUBTLV(exts, EXT_RMT_AS);
649 }
650 break;
651 case ISIS_SUBTLV_RIP:
652 if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
653 sbuf_push(log, indent,
654 "TLV size does not match expected size for Remote ASBR IP Address!\n");
655 } else {
656 stream_get(&exts->remote_ip.s_addr, s, 4);
657 SET_SUBTLV(exts, EXT_RMT_IP);
658 }
659 break;
660 /* Extended Metrics as defined in RFC 7810 */
661 case ISIS_SUBTLV_AV_DELAY:
662 if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
663 sbuf_push(log, indent,
664 "TLV size does not match expected size for Average Link Delay!\n");
665 } else {
666 exts->delay = stream_getl(s);
667 SET_SUBTLV(exts, EXT_DELAY);
668 }
669 break;
670 case ISIS_SUBTLV_MM_DELAY:
671 if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
672 sbuf_push(log, indent,
673 "TLV size does not match expected size for Min/Max Link Delay!\n");
674 } else {
675 exts->min_delay = stream_getl(s);
676 exts->max_delay = stream_getl(s);
677 SET_SUBTLV(exts, EXT_MM_DELAY);
678 }
679 break;
680 case ISIS_SUBTLV_DELAY_VAR:
681 if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
682 sbuf_push(log, indent,
683 "TLV size does not match expected size for Delay Variation!\n");
684 } else {
685 exts->delay_var = stream_getl(s);
686 SET_SUBTLV(exts, EXT_DELAY_VAR);
687 }
688 break;
689 case ISIS_SUBTLV_PKT_LOSS:
690 if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
691 sbuf_push(log, indent,
692 "TLV size does not match expected size for Link Packet Loss!\n");
693 } else {
694 exts->pkt_loss = stream_getl(s);
695 SET_SUBTLV(exts, EXT_PKT_LOSS);
696 }
697 break;
698 case ISIS_SUBTLV_RES_BW:
699 if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
700 sbuf_push(log, indent,
701 "TLV size does not match expected size for Unidirectional Residual Bandwidth!\n");
702 } else {
703 exts->res_bw = stream_getf(s);
704 SET_SUBTLV(exts, EXT_RES_BW);
705 }
706 break;
707 case ISIS_SUBTLV_AVA_BW:
708 if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
709 sbuf_push(log, indent,
710 "TLV size does not match expected size for Unidirectional Available Bandwidth!\n");
711 } else {
712 exts->ava_bw = stream_getf(s);
713 SET_SUBTLV(exts, EXT_AVA_BW);
714 }
715 break;
716 case ISIS_SUBTLV_USE_BW:
717 if (subtlv_len != ISIS_SUBTLV_DEF_SIZE) {
718 sbuf_push(log, indent,
719 "TLV size does not match expected size for Unidirectional Utilized Bandwidth!\n");
720 } else {
721 exts->use_bw = stream_getf(s);
722 SET_SUBTLV(exts, EXT_USE_BW);
723 }
724 break;
f2333421 725 /* Segment Routing Adjacency as per RFC8667 section #2.2.1 */
1b3f47d0
OD
726 case ISIS_SUBTLV_ADJ_SID:
727 if (subtlv_len != ISIS_SUBTLV_ADJ_SID_SIZE
728 && subtlv_len != ISIS_SUBTLV_ADJ_SID_SIZE + 1) {
729 sbuf_push(log, indent,
730 "TLV size does not match expected size for Adjacency SID!\n");
731 } else {
732 struct isis_adj_sid *adj;
733
734 adj = XCALLOC(MTYPE_ISIS_SUBTLV,
735 sizeof(struct isis_adj_sid));
736 adj->flags = stream_getc(s);
737 adj->weight = stream_getc(s);
738 if (adj->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG) {
739 adj->sid = stream_get3(s);
740 adj->sid &= MPLS_LABEL_VALUE_MASK;
741 } else {
742 adj->sid = stream_getl(s);
743 }
744 if (mtid == ISIS_MT_IPV4_UNICAST)
745 adj->family = AF_INET;
746 if (mtid == ISIS_MT_IPV6_UNICAST)
747 adj->family = AF_INET6;
748 append_item(&exts->adj_sid,
749 (struct isis_item *)adj);
750 SET_SUBTLV(exts, EXT_ADJ_SID);
751 }
752 break;
f2333421 753 /* Segment Routing LAN-Adjacency as per RFC8667 section 2.2.2 */
1b3f47d0
OD
754 case ISIS_SUBTLV_LAN_ADJ_SID:
755 if (subtlv_len != ISIS_SUBTLV_LAN_ADJ_SID_SIZE
756 && subtlv_len != ISIS_SUBTLV_LAN_ADJ_SID_SIZE + 1) {
757 sbuf_push(log, indent,
758 "TLV size does not match expected size for LAN-Adjacency SID!\n");
759 } else {
760 struct isis_lan_adj_sid *lan;
761
762 lan = XCALLOC(MTYPE_ISIS_SUBTLV,
763 sizeof(struct isis_lan_adj_sid));
764 lan->flags = stream_getc(s);
765 lan->weight = stream_getc(s);
766 stream_get(&(lan->neighbor_id), s,
767 ISIS_SYS_ID_LEN);
768 if (lan->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG) {
769 lan->sid = stream_get3(s);
770 lan->sid &= MPLS_LABEL_VALUE_MASK;
771 } else {
772 lan->sid = stream_getl(s);
773 }
774 if (mtid == ISIS_MT_IPV4_UNICAST)
775 lan->family = AF_INET;
776 if (mtid == ISIS_MT_IPV6_UNICAST)
777 lan->family = AF_INET6;
778 append_item(&exts->lan_sid,
779 (struct isis_item *)lan);
780 SET_SUBTLV(exts, EXT_LAN_ADJ_SID);
781 }
782 break;
783 default:
784 /* Skip unknown TLV */
785 stream_forward_getp(s, subtlv_len);
786 break;
787 }
788 sum += subtlv_len + ISIS_SUBTLV_HDR_SIZE;
789 }
790
791 return 0;
792}
793
f2333421 794/* Functions for Sub-TLV 3 SR Prefix-SID as per RFC8667 section 2.1 */
bd507085
CF
795static struct isis_item *copy_item_prefix_sid(struct isis_item *i)
796{
797 struct isis_prefix_sid *sid = (struct isis_prefix_sid *)i;
798 struct isis_prefix_sid *rv = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*rv));
799
800 rv->flags = sid->flags;
801 rv->algorithm = sid->algorithm;
802 rv->value = sid->value;
803 return (struct isis_item *)rv;
804}
805
806static void format_item_prefix_sid(uint16_t mtid, struct isis_item *i,
807 struct sbuf *buf, int indent)
808{
809 struct isis_prefix_sid *sid = (struct isis_prefix_sid *)i;
810
1b3f47d0 811 sbuf_push(buf, indent, "SR Prefix-SID ");
bd507085 812 if (sid->flags & ISIS_PREFIX_SID_VALUE) {
6cde4b45 813 sbuf_push(buf, 0, "Label: %u, ", sid->value);
bd507085 814 } else {
6cde4b45 815 sbuf_push(buf, 0, "Index: %u, ", sid->value);
bd507085 816 }
6cde4b45 817 sbuf_push(buf, 0, "Algorithm: %hhu, ", sid->algorithm);
1b3f47d0
OD
818 sbuf_push(buf, 0, "Flags:%s%s%s%s%s%s\n",
819 sid->flags & ISIS_PREFIX_SID_READVERTISED ? " READVERTISED"
820 : "",
821 sid->flags & ISIS_PREFIX_SID_NODE ? " NODE" : "",
822 sid->flags & ISIS_PREFIX_SID_NO_PHP ? " NO-PHP" : " PHP",
823 sid->flags & ISIS_PREFIX_SID_EXPLICIT_NULL ? " EXPLICIT-NULL"
824 : "",
825 sid->flags & ISIS_PREFIX_SID_VALUE ? " VALUE" : "",
826 sid->flags & ISIS_PREFIX_SID_LOCAL ? " LOCAL" : "");
bd507085
CF
827}
828
829static void free_item_prefix_sid(struct isis_item *i)
830{
831 XFREE(MTYPE_ISIS_SUBTLV, i);
832}
833
7ce31bab
DS
834static int pack_item_prefix_sid(struct isis_item *i, struct stream *s,
835 size_t *min_len)
bd507085
CF
836{
837 struct isis_prefix_sid *sid = (struct isis_prefix_sid *)i;
838
839 uint8_t size = (sid->flags & ISIS_PREFIX_SID_VALUE) ? 5 : 6;
840
7ce31bab
DS
841 if (STREAM_WRITEABLE(s) < size) {
842 *min_len = size;
bd507085 843 return 1;
7ce31bab 844 }
bd507085
CF
845
846 stream_putc(s, sid->flags);
847 stream_putc(s, sid->algorithm);
848
849 if (sid->flags & ISIS_PREFIX_SID_VALUE) {
850 stream_put3(s, sid->value);
851 } else {
852 stream_putl(s, sid->value);
853 }
854
855 return 0;
856}
857
858static int unpack_item_prefix_sid(uint16_t mtid, uint8_t len, struct stream *s,
859 struct sbuf *log, void *dest, int indent)
860{
861 struct isis_subtlvs *subtlvs = dest;
3e300703
DL
862 struct isis_prefix_sid sid = {
863 };
bd507085
CF
864
865 sbuf_push(log, indent, "Unpacking SR Prefix-SID...\n");
866
867 if (len < 5) {
868 sbuf_push(log, indent,
6cde4b45 869 "Not enough data left. (expected 5 or more bytes, got %hhu)\n",
bd507085
CF
870 len);
871 return 1;
872 }
873
874 sid.flags = stream_getc(s);
8b1e3453
RW
875 if (!!(sid.flags & ISIS_PREFIX_SID_VALUE)
876 != !!(sid.flags & ISIS_PREFIX_SID_LOCAL)) {
1b3f47d0 877 sbuf_push(log, indent, "Flags implausible: Local Flag needs to match Value Flag\n");
8b1e3453 878 return 1;
bd507085
CF
879 }
880
881 sid.algorithm = stream_getc(s);
882
1b3f47d0
OD
883 uint8_t expected_size = (sid.flags & ISIS_PREFIX_SID_VALUE)
884 ? ISIS_SUBTLV_PREFIX_SID_SIZE
885 : ISIS_SUBTLV_PREFIX_SID_SIZE + 1;
bd507085
CF
886 if (len != expected_size) {
887 sbuf_push(log, indent,
6cde4b45 888 "TLV size differs from expected size. (expected %u but got %hhu)\n",
bd507085
CF
889 expected_size, len);
890 return 1;
891 }
892
893 if (sid.flags & ISIS_PREFIX_SID_VALUE) {
894 sid.value = stream_get3(s);
26f6acaf
RW
895 if (!IS_MPLS_UNRESERVED_LABEL(sid.value)) {
896 sbuf_push(log, indent, "Invalid absolute SID %u\n",
897 sid.value);
898 return 1;
899 }
bd507085
CF
900 } else {
901 sid.value = stream_getl(s);
902 }
903
904 format_item_prefix_sid(mtid, (struct isis_item *)&sid, log, indent + 2);
905 append_item(&subtlvs->prefix_sids, copy_item_prefix_sid((struct isis_item *)&sid));
906 return 0;
907}
908
7ef5fefc
CF
909/* Functions for Sub-TVL ??? IPv6 Source Prefix */
910
911static struct prefix_ipv6 *copy_subtlv_ipv6_source_prefix(struct prefix_ipv6 *p)
912{
913 if (!p)
914 return NULL;
915
916 struct prefix_ipv6 *rv = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*rv));
917 rv->family = p->family;
918 rv->prefixlen = p->prefixlen;
919 memcpy(&rv->prefix, &p->prefix, sizeof(rv->prefix));
920 return rv;
921}
922
923static void format_subtlv_ipv6_source_prefix(struct prefix_ipv6 *p,
924 struct sbuf *buf, int indent)
925{
926 if (!p)
927 return;
928
929 char prefixbuf[PREFIX2STR_BUFFER];
930 sbuf_push(buf, indent, "IPv6 Source Prefix: %s\n",
931 prefix2str(p, prefixbuf, sizeof(prefixbuf)));
932}
933
934static int pack_subtlv_ipv6_source_prefix(struct prefix_ipv6 *p,
935 struct stream *s)
936{
937 if (!p)
938 return 0;
939
940 if (STREAM_WRITEABLE(s) < 3 + (unsigned)PSIZE(p->prefixlen))
941 return 1;
942
943 stream_putc(s, ISIS_SUBTLV_IPV6_SOURCE_PREFIX);
944 stream_putc(s, 1 + PSIZE(p->prefixlen));
945 stream_putc(s, p->prefixlen);
946 stream_put(s, &p->prefix, PSIZE(p->prefixlen));
947 return 0;
948}
949
950static int unpack_subtlv_ipv6_source_prefix(enum isis_tlv_context context,
951 uint8_t tlv_type, uint8_t tlv_len,
952 struct stream *s, struct sbuf *log,
953 void *dest, int indent)
954{
955 struct isis_subtlvs *subtlvs = dest;
956 struct prefix_ipv6 p = {
957 .family = AF_INET6,
958 };
959
960 sbuf_push(log, indent, "Unpacking IPv6 Source Prefix Sub-TLV...\n");
961
962 if (tlv_len < 1) {
98c5bc15 963 sbuf_push(log, indent,
6cde4b45 964 "Not enough data left. (expected 1 or more bytes, got %hhu)\n",
98c5bc15 965 tlv_len);
7ef5fefc
CF
966 return 1;
967 }
968
969 p.prefixlen = stream_getc(s);
13ccce6e 970 if (p.prefixlen > IPV6_MAX_BITLEN) {
1b3f47d0 971 sbuf_push(log, indent, "Prefixlen %u is implausible for IPv6\n",
7ef5fefc
CF
972 p.prefixlen);
973 return 1;
974 }
975
976 if (tlv_len != 1 + PSIZE(p.prefixlen)) {
977 sbuf_push(
978 log, indent,
6cde4b45 979 "TLV size differs from expected size for the prefixlen. (expected %u but got %hhu)\n",
7ef5fefc
CF
980 1 + PSIZE(p.prefixlen), tlv_len);
981 return 1;
982 }
983
984 stream_get(&p.prefix, s, PSIZE(p.prefixlen));
985
986 if (subtlvs->source_prefix) {
987 sbuf_push(
988 log, indent,
989 "WARNING: source prefix Sub-TLV present multiple times.\n");
990 /* Ignore all but first occurrence of the source prefix Sub-TLV
991 */
992 return 0;
993 }
994
995 subtlvs->source_prefix = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(p));
996 memcpy(subtlvs->source_prefix, &p, sizeof(p));
997 return 0;
998}
1b3f47d0 999
bd507085
CF
1000static struct isis_item *copy_item(enum isis_tlv_context context,
1001 enum isis_tlv_type type,
1002 struct isis_item *item);
1003static void copy_items(enum isis_tlv_context context, enum isis_tlv_type type,
1004 struct isis_item_list *src, struct isis_item_list *dest);
1005static void format_items_(uint16_t mtid, enum isis_tlv_context context,
1006 enum isis_tlv_type type, struct isis_item_list *items,
1007 struct sbuf *buf, int indent);
1008#define format_items(...) format_items_(ISIS_MT_IPV4_UNICAST, __VA_ARGS__)
1009static void free_items(enum isis_tlv_context context, enum isis_tlv_type type,
1010 struct isis_item_list *items);
1011static int pack_items_(uint16_t mtid, enum isis_tlv_context context,
1012 enum isis_tlv_type type, struct isis_item_list *items,
1013 struct stream *s, struct isis_tlvs **fragment_tlvs,
2b64873d 1014 const struct pack_order_entry *pe,
bd507085
CF
1015 struct isis_tlvs *(*new_fragment)(struct list *l),
1016 struct list *new_fragment_arg);
1017#define pack_items(...) pack_items_(ISIS_MT_IPV4_UNICAST, __VA_ARGS__)
7ef5fefc
CF
1018
1019/* Functions related to subtlvs */
1020
bd507085 1021static struct isis_subtlvs *isis_alloc_subtlvs(enum isis_tlv_context context)
7ef5fefc
CF
1022{
1023 struct isis_subtlvs *result;
1024
1025 result = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*result));
bd507085
CF
1026 result->context = context;
1027
1028 init_item_list(&result->prefix_sids);
7ef5fefc
CF
1029
1030 return result;
1031}
1032
1033static struct isis_subtlvs *copy_subtlvs(struct isis_subtlvs *subtlvs)
1034{
1035 if (!subtlvs)
1036 return NULL;
1037
1038 struct isis_subtlvs *rv = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*rv));
1039
bd507085
CF
1040 rv->context = subtlvs->context;
1041
1042 copy_items(subtlvs->context, ISIS_SUBTLV_PREFIX_SID,
1043 &subtlvs->prefix_sids, &rv->prefix_sids);
1044
7ef5fefc
CF
1045 rv->source_prefix =
1046 copy_subtlv_ipv6_source_prefix(subtlvs->source_prefix);
1047 return rv;
1048}
1049
1050static void format_subtlvs(struct isis_subtlvs *subtlvs, struct sbuf *buf,
1051 int indent)
1052{
bd507085
CF
1053 format_items(subtlvs->context, ISIS_SUBTLV_PREFIX_SID,
1054 &subtlvs->prefix_sids, buf, indent);
1055
7ef5fefc
CF
1056 format_subtlv_ipv6_source_prefix(subtlvs->source_prefix, buf, indent);
1057}
1058
1059static void isis_free_subtlvs(struct isis_subtlvs *subtlvs)
1060{
1061 if (!subtlvs)
1062 return;
1063
bd507085
CF
1064 free_items(subtlvs->context, ISIS_SUBTLV_PREFIX_SID,
1065 &subtlvs->prefix_sids);
1066
7ef5fefc
CF
1067 XFREE(MTYPE_ISIS_SUBTLV, subtlvs->source_prefix);
1068
1069 XFREE(MTYPE_ISIS_SUBTLV, subtlvs);
1070}
1071
1072static int pack_subtlvs(struct isis_subtlvs *subtlvs, struct stream *s)
1073{
1074 int rv;
1075 size_t subtlv_len_pos = stream_get_endp(s);
1076
1077 if (STREAM_WRITEABLE(s) < 1)
1078 return 1;
1079
1080 stream_putc(s, 0); /* Put 0 as subtlvs length, filled in later */
1081
bd507085
CF
1082 rv = pack_items(subtlvs->context, ISIS_SUBTLV_PREFIX_SID,
1083 &subtlvs->prefix_sids, s, NULL, NULL, NULL, NULL);
1084 if (rv)
1085 return rv;
1086
7ef5fefc
CF
1087 rv = pack_subtlv_ipv6_source_prefix(subtlvs->source_prefix, s);
1088 if (rv)
1089 return rv;
1090
1091 size_t subtlv_len = stream_get_endp(s) - subtlv_len_pos - 1;
1092 if (subtlv_len > 255)
1093 return 1;
1094
1095 stream_putc_at(s, subtlv_len_pos, subtlv_len);
1096 return 0;
1097}
1098
1099static int unpack_tlvs(enum isis_tlv_context context, size_t avail_len,
1100 struct stream *stream, struct sbuf *log, void *dest,
bf555bf0 1101 int indent, bool *unpacked_known_tlvs);
7ef5fefc
CF
1102
1103/* Functions related to TLVs 1 Area Addresses */
1104
1105static struct isis_item *copy_item_area_address(struct isis_item *i)
1106{
1107 struct isis_area_address *addr = (struct isis_area_address *)i;
841791b6 1108 struct isis_area_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1109
1110 rv->len = addr->len;
1111 memcpy(rv->addr, addr->addr, addr->len);
1112 return (struct isis_item *)rv;
1113}
1114
1115static void format_item_area_address(uint16_t mtid, struct isis_item *i,
1116 struct sbuf *buf, int indent)
1117{
1118 struct isis_area_address *addr = (struct isis_area_address *)i;
1119
1120 sbuf_push(buf, indent, "Area Address: %s\n",
1121 isonet_print(addr->addr, addr->len));
1122}
1123
1124static void free_item_area_address(struct isis_item *i)
1125{
841791b6 1126 XFREE(MTYPE_ISIS_TLV, i);
7ef5fefc
CF
1127}
1128
7ce31bab
DS
1129static int pack_item_area_address(struct isis_item *i, struct stream *s,
1130 size_t *min_len)
7ef5fefc
CF
1131{
1132 struct isis_area_address *addr = (struct isis_area_address *)i;
1133
7ce31bab
DS
1134 if (STREAM_WRITEABLE(s) < (unsigned)1 + addr->len) {
1135 *min_len = (unsigned)1 + addr->len;
7ef5fefc 1136 return 1;
7ce31bab 1137 }
7ef5fefc
CF
1138 stream_putc(s, addr->len);
1139 stream_put(s, addr->addr, addr->len);
1140 return 0;
1141}
1142
1143static int unpack_item_area_address(uint16_t mtid, uint8_t len,
1144 struct stream *s, struct sbuf *log,
1145 void *dest, int indent)
1146{
1147 struct isis_tlvs *tlvs = dest;
1148 struct isis_area_address *rv = NULL;
1149
1150 sbuf_push(log, indent, "Unpack area address...\n");
1151 if (len < 1) {
1152 sbuf_push(
1153 log, indent,
6cde4b45 1154 "Not enough data left. (Expected 1 byte of address length, got %hhu)\n",
7ef5fefc
CF
1155 len);
1156 goto out;
1157 }
1158
841791b6 1159 rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1160 rv->len = stream_getc(s);
1161
1162 if (len < 1 + rv->len) {
47b13e9b 1163 sbuf_push(log, indent, "Not enough data left. (Expected %hhu bytes of address, got %u)\n",
7ef5fefc
CF
1164 rv->len, len - 1);
1165 goto out;
1166 }
1167
1168 if (rv->len < 1 || rv->len > 20) {
1169 sbuf_push(log, indent,
6cde4b45 1170 "Implausible area address length %hhu\n",
7ef5fefc
CF
1171 rv->len);
1172 goto out;
1173 }
1174
1175 stream_get(rv->addr, s, rv->len);
1176
1177 format_item_area_address(ISIS_MT_IPV4_UNICAST, (struct isis_item *)rv,
1178 log, indent + 2);
1179 append_item(&tlvs->area_addresses, (struct isis_item *)rv);
1180 return 0;
1181out:
841791b6 1182 XFREE(MTYPE_ISIS_TLV, rv);
7ef5fefc
CF
1183 return 1;
1184}
1185
1186/* Functions related to TLV 2 (Old-Style) IS Reach */
1187static struct isis_item *copy_item_oldstyle_reach(struct isis_item *i)
1188{
1189 struct isis_oldstyle_reach *r = (struct isis_oldstyle_reach *)i;
841791b6 1190 struct isis_oldstyle_reach *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1191
1192 memcpy(rv->id, r->id, 7);
1193 rv->metric = r->metric;
1194 return (struct isis_item *)rv;
1195}
1196
1197static void format_item_oldstyle_reach(uint16_t mtid, struct isis_item *i,
1198 struct sbuf *buf, int indent)
1199{
1200 struct isis_oldstyle_reach *r = (struct isis_oldstyle_reach *)i;
1201
6cde4b45 1202 sbuf_push(buf, indent, "IS Reachability: %s (Metric: %hhu)\n",
7ef5fefc
CF
1203 isis_format_id(r->id, 7), r->metric);
1204}
1205
1206static void free_item_oldstyle_reach(struct isis_item *i)
1207{
841791b6 1208 XFREE(MTYPE_ISIS_TLV, i);
7ef5fefc
CF
1209}
1210
7ce31bab
DS
1211static int pack_item_oldstyle_reach(struct isis_item *i, struct stream *s,
1212 size_t *min_len)
7ef5fefc
CF
1213{
1214 struct isis_oldstyle_reach *r = (struct isis_oldstyle_reach *)i;
1215
7ce31bab
DS
1216 if (STREAM_WRITEABLE(s) < 11) {
1217 *min_len = 11;
7ef5fefc 1218 return 1;
7ce31bab 1219 }
7ef5fefc
CF
1220
1221 stream_putc(s, r->metric);
1222 stream_putc(s, 0x80); /* delay metric - unsupported */
1223 stream_putc(s, 0x80); /* expense metric - unsupported */
1224 stream_putc(s, 0x80); /* error metric - unsupported */
1225 stream_put(s, r->id, 7);
1226
1227 return 0;
1228}
1229
1230static int unpack_item_oldstyle_reach(uint16_t mtid, uint8_t len,
1231 struct stream *s, struct sbuf *log,
1232 void *dest, int indent)
1233{
1234 struct isis_tlvs *tlvs = dest;
1235
1236 sbuf_push(log, indent, "Unpack oldstyle reach...\n");
1237 if (len < 11) {
1238 sbuf_push(
1239 log, indent,
6cde4b45 1240 "Not enough data left.(Expected 11 bytes of reach information, got %hhu)\n",
7ef5fefc
CF
1241 len);
1242 return 1;
1243 }
1244
841791b6 1245 struct isis_oldstyle_reach *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1246 rv->metric = stream_getc(s);
1247 if ((rv->metric & 0x3f) != rv->metric) {
1248 sbuf_push(log, indent, "Metric has unplausible format\n");
1249 rv->metric &= 0x3f;
1250 }
1251 stream_forward_getp(s, 3); /* Skip other metrics */
1252 stream_get(rv->id, s, 7);
1253
1254 format_item_oldstyle_reach(mtid, (struct isis_item *)rv, log,
1255 indent + 2);
1256 append_item(&tlvs->oldstyle_reach, (struct isis_item *)rv);
1257 return 0;
1258}
1259
1260/* Functions related to TLV 6 LAN Neighbors */
1261static struct isis_item *copy_item_lan_neighbor(struct isis_item *i)
1262{
1263 struct isis_lan_neighbor *n = (struct isis_lan_neighbor *)i;
841791b6 1264 struct isis_lan_neighbor *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1265
1266 memcpy(rv->mac, n->mac, 6);
1267 return (struct isis_item *)rv;
1268}
1269
1270static void format_item_lan_neighbor(uint16_t mtid, struct isis_item *i,
1271 struct sbuf *buf, int indent)
1272{
1273 struct isis_lan_neighbor *n = (struct isis_lan_neighbor *)i;
1274
1275 sbuf_push(buf, indent, "LAN Neighbor: %s\n", isis_format_id(n->mac, 6));
1276}
1277
1278static void free_item_lan_neighbor(struct isis_item *i)
1279{
841791b6 1280 XFREE(MTYPE_ISIS_TLV, i);
7ef5fefc
CF
1281}
1282
7ce31bab
DS
1283static int pack_item_lan_neighbor(struct isis_item *i, struct stream *s,
1284 size_t *min_len)
7ef5fefc
CF
1285{
1286 struct isis_lan_neighbor *n = (struct isis_lan_neighbor *)i;
1287
7ce31bab
DS
1288 if (STREAM_WRITEABLE(s) < 6) {
1289 *min_len = 6;
7ef5fefc 1290 return 1;
7ce31bab 1291 }
7ef5fefc
CF
1292
1293 stream_put(s, n->mac, 6);
1294
1295 return 0;
1296}
1297
1298static int unpack_item_lan_neighbor(uint16_t mtid, uint8_t len,
1299 struct stream *s, struct sbuf *log,
1300 void *dest, int indent)
1301{
1302 struct isis_tlvs *tlvs = dest;
1303
1304 sbuf_push(log, indent, "Unpack LAN neighbor...\n");
1305 if (len < 6) {
1306 sbuf_push(
1307 log, indent,
6cde4b45 1308 "Not enough data left.(Expected 6 bytes of mac, got %hhu)\n",
7ef5fefc
CF
1309 len);
1310 return 1;
1311 }
1312
841791b6 1313 struct isis_lan_neighbor *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1314 stream_get(rv->mac, s, 6);
1315
1316 format_item_lan_neighbor(mtid, (struct isis_item *)rv, log, indent + 2);
1317 append_item(&tlvs->lan_neighbor, (struct isis_item *)rv);
1318 return 0;
1319}
1320
1321/* Functions related to TLV 9 LSP Entry */
1322static struct isis_item *copy_item_lsp_entry(struct isis_item *i)
1323{
1324 struct isis_lsp_entry *e = (struct isis_lsp_entry *)i;
841791b6 1325 struct isis_lsp_entry *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1326
1327 rv->rem_lifetime = e->rem_lifetime;
1328 memcpy(rv->id, e->id, sizeof(rv->id));
1329 rv->seqno = e->seqno;
1330 rv->checksum = e->checksum;
1331
1332 return (struct isis_item *)rv;
1333}
1334
1335static void format_item_lsp_entry(uint16_t mtid, struct isis_item *i,
1336 struct sbuf *buf, int indent)
1337{
1338 struct isis_lsp_entry *e = (struct isis_lsp_entry *)i;
1339
996c9314 1340 sbuf_push(buf, indent,
6cde4b45 1341 "LSP Entry: %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus\n",
7ef5fefc
CF
1342 isis_format_id(e->id, 8), e->seqno, e->checksum,
1343 e->rem_lifetime);
1344}
1345
1346static void free_item_lsp_entry(struct isis_item *i)
1347{
841791b6 1348 XFREE(MTYPE_ISIS_TLV, i);
7ef5fefc
CF
1349}
1350
7ce31bab
DS
1351static int pack_item_lsp_entry(struct isis_item *i, struct stream *s,
1352 size_t *min_len)
7ef5fefc
CF
1353{
1354 struct isis_lsp_entry *e = (struct isis_lsp_entry *)i;
1355
7ce31bab
DS
1356 if (STREAM_WRITEABLE(s) < 16) {
1357 *min_len = 16;
7ef5fefc 1358 return 1;
7ce31bab 1359 }
7ef5fefc
CF
1360
1361 stream_putw(s, e->rem_lifetime);
1362 stream_put(s, e->id, 8);
1363 stream_putl(s, e->seqno);
1364 stream_putw(s, e->checksum);
1365
1366 return 0;
1367}
1368
1369static int unpack_item_lsp_entry(uint16_t mtid, uint8_t len, struct stream *s,
1370 struct sbuf *log, void *dest, int indent)
1371{
1372 struct isis_tlvs *tlvs = dest;
1373
1374 sbuf_push(log, indent, "Unpack LSP entry...\n");
1375 if (len < 16) {
1376 sbuf_push(
1377 log, indent,
6cde4b45 1378 "Not enough data left. (Expected 16 bytes of LSP info, got %hhu",
7ef5fefc
CF
1379 len);
1380 return 1;
1381 }
1382
841791b6 1383 struct isis_lsp_entry *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1384 rv->rem_lifetime = stream_getw(s);
1385 stream_get(rv->id, s, 8);
1386 rv->seqno = stream_getl(s);
1387 rv->checksum = stream_getw(s);
1388
1389 format_item_lsp_entry(mtid, (struct isis_item *)rv, log, indent + 2);
1390 append_item(&tlvs->lsp_entries, (struct isis_item *)rv);
1391 return 0;
1392}
1393
1394/* Functions related to TLVs 22/222 Extended Reach/MT Reach */
1395
1396static struct isis_item *copy_item_extended_reach(struct isis_item *i)
1397{
1398 struct isis_extended_reach *r = (struct isis_extended_reach *)i;
841791b6 1399 struct isis_extended_reach *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1400
1401 memcpy(rv->id, r->id, 7);
1402 rv->metric = r->metric;
1403
1b3f47d0
OD
1404 if (r->subtlvs)
1405 rv->subtlvs = copy_item_ext_subtlvs(r->subtlvs, -1);
7ef5fefc
CF
1406
1407 return (struct isis_item *)rv;
1408}
1409
1410static void format_item_extended_reach(uint16_t mtid, struct isis_item *i,
1411 struct sbuf *buf, int indent)
1412{
1413 struct isis_extended_reach *r = (struct isis_extended_reach *)i;
1414
1415 sbuf_push(buf, indent, "%s Reachability: %s (Metric: %u)",
1416 (mtid == ISIS_MT_IPV4_UNICAST) ? "Extended" : "MT",
1417 isis_format_id(r->id, 7), r->metric);
1418 if (mtid != ISIS_MT_IPV4_UNICAST)
1419 sbuf_push(buf, 0, " %s", isis_mtid2str(mtid));
1420 sbuf_push(buf, 0, "\n");
1421
1b3f47d0
OD
1422 if (r->subtlvs)
1423 format_item_ext_subtlvs(r->subtlvs, buf, indent + 2, mtid);
7ef5fefc
CF
1424}
1425
1426static void free_item_extended_reach(struct isis_item *i)
1427{
1428 struct isis_extended_reach *item = (struct isis_extended_reach *)i;
1b3f47d0
OD
1429 if (item->subtlvs != NULL)
1430 free_item_ext_subtlvs(item->subtlvs);
841791b6 1431 XFREE(MTYPE_ISIS_TLV, item);
7ef5fefc
CF
1432}
1433
7ce31bab
DS
1434static int pack_item_extended_reach(struct isis_item *i, struct stream *s,
1435 size_t *min_len)
7ef5fefc
CF
1436{
1437 struct isis_extended_reach *r = (struct isis_extended_reach *)i;
1b3f47d0
OD
1438 size_t len;
1439 size_t len_pos;
7ef5fefc 1440
7ce31bab
DS
1441 if (STREAM_WRITEABLE(s) < 11 + ISIS_SUBTLV_MAX_SIZE) {
1442 *min_len = 11 + ISIS_SUBTLV_MAX_SIZE;
7ef5fefc 1443 return 1;
7ce31bab 1444 }
1b3f47d0 1445
7ef5fefc
CF
1446 stream_put(s, r->id, sizeof(r->id));
1447 stream_put3(s, r->metric);
1b3f47d0
OD
1448 len_pos = stream_get_endp(s);
1449 /* Real length will be adjust after adding subTLVs */
1450 stream_putc(s, 11);
1451 if (r->subtlvs)
7ce31bab 1452 pack_item_ext_subtlvs(r->subtlvs, s, min_len);
1b3f47d0
OD
1453 /* Adjust length */
1454 len = stream_get_endp(s) - len_pos - 1;
1455 stream_putc_at(s, len_pos, len);
7ef5fefc
CF
1456 return 0;
1457}
1458
1459static int unpack_item_extended_reach(uint16_t mtid, uint8_t len,
1460 struct stream *s, struct sbuf *log,
1461 void *dest, int indent)
1462{
1463 struct isis_tlvs *tlvs = dest;
1464 struct isis_extended_reach *rv = NULL;
1465 uint8_t subtlv_len;
1466 struct isis_item_list *items;
1467
1468 if (mtid == ISIS_MT_IPV4_UNICAST) {
1469 items = &tlvs->extended_reach;
1470 } else {
1471 items = isis_get_mt_items(&tlvs->mt_reach, mtid);
1472 }
1473
1474 sbuf_push(log, indent, "Unpacking %s reachability...\n",
1475 (mtid == ISIS_MT_IPV4_UNICAST) ? "extended" : "mt");
1476
1477 if (len < 11) {
98c5bc15 1478 sbuf_push(log, indent,
6cde4b45 1479 "Not enough data left. (expected 11 or more bytes, got %hhu)\n",
98c5bc15 1480 len);
7ef5fefc
CF
1481 goto out;
1482 }
1483
841791b6 1484 rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1485 stream_get(rv->id, s, 7);
1486 rv->metric = stream_get3(s);
1487 subtlv_len = stream_getc(s);
1488
7ef5fefc
CF
1489 if ((size_t)len < ((size_t)11) + subtlv_len) {
1490 sbuf_push(log, indent,
47b13e9b 1491 "Not enough data left for subtlv size %hhu, there are only %u bytes left.\n",
7ef5fefc
CF
1492 subtlv_len, len - 11);
1493 goto out;
1494 }
1495
6cde4b45 1496 sbuf_push(log, indent, "Storing %hhu bytes of subtlvs\n",
7ef5fefc
CF
1497 subtlv_len);
1498
1499 if (subtlv_len) {
1b3f47d0
OD
1500 if (unpack_item_ext_subtlvs(mtid, subtlv_len, s, log, rv,
1501 indent + 4)) {
7ef5fefc
CF
1502 goto out;
1503 }
7ef5fefc
CF
1504 }
1505
1b3f47d0
OD
1506 format_item_extended_reach(mtid, (struct isis_item *)rv, log,
1507 indent + 2);
7ef5fefc
CF
1508 append_item(items, (struct isis_item *)rv);
1509 return 0;
1510out:
1511 if (rv)
1512 free_item_extended_reach((struct isis_item *)rv);
1513
1514 return 1;
1515}
1516
1517/* Functions related to TLV 128 (Old-Style) IP Reach */
1518static struct isis_item *copy_item_oldstyle_ip_reach(struct isis_item *i)
1519{
1520 struct isis_oldstyle_ip_reach *r = (struct isis_oldstyle_ip_reach *)i;
1521 struct isis_oldstyle_ip_reach *rv =
841791b6 1522 XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1523
1524 rv->metric = r->metric;
1525 rv->prefix = r->prefix;
1526 return (struct isis_item *)rv;
1527}
1528
1529static void format_item_oldstyle_ip_reach(uint16_t mtid, struct isis_item *i,
1530 struct sbuf *buf, int indent)
1531{
1532 struct isis_oldstyle_ip_reach *r = (struct isis_oldstyle_ip_reach *)i;
1533 char prefixbuf[PREFIX2STR_BUFFER];
1534
6cde4b45 1535 sbuf_push(buf, indent, "IP Reachability: %s (Metric: %hhu)\n",
996c9314
LB
1536 prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf)),
1537 r->metric);
7ef5fefc
CF
1538}
1539
1540static void free_item_oldstyle_ip_reach(struct isis_item *i)
1541{
841791b6 1542 XFREE(MTYPE_ISIS_TLV, i);
7ef5fefc
CF
1543}
1544
7ce31bab
DS
1545static int pack_item_oldstyle_ip_reach(struct isis_item *i, struct stream *s,
1546 size_t *min_len)
7ef5fefc
CF
1547{
1548 struct isis_oldstyle_ip_reach *r = (struct isis_oldstyle_ip_reach *)i;
1549
7ce31bab
DS
1550 if (STREAM_WRITEABLE(s) < 12) {
1551 *min_len = 12;
7ef5fefc 1552 return 1;
7ce31bab 1553 }
7ef5fefc
CF
1554
1555 stream_putc(s, r->metric);
1556 stream_putc(s, 0x80); /* delay metric - unsupported */
1557 stream_putc(s, 0x80); /* expense metric - unsupported */
1558 stream_putc(s, 0x80); /* error metric - unsupported */
1559 stream_put(s, &r->prefix.prefix, 4);
1560
1561 struct in_addr mask;
1562 masklen2ip(r->prefix.prefixlen, &mask);
1563 stream_put(s, &mask, sizeof(mask));
1564
1565 return 0;
1566}
1567
1568static int unpack_item_oldstyle_ip_reach(uint16_t mtid, uint8_t len,
1569 struct stream *s, struct sbuf *log,
1570 void *dest, int indent)
1571{
1572 sbuf_push(log, indent, "Unpack oldstyle ip reach...\n");
1573 if (len < 12) {
1574 sbuf_push(
1575 log, indent,
6cde4b45 1576 "Not enough data left.(Expected 12 bytes of reach information, got %hhu)\n",
7ef5fefc
CF
1577 len);
1578 return 1;
1579 }
1580
1581 struct isis_oldstyle_ip_reach *rv =
841791b6 1582 XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1583 rv->metric = stream_getc(s);
1584 if ((rv->metric & 0x7f) != rv->metric) {
1585 sbuf_push(log, indent, "Metric has unplausible format\n");
1586 rv->metric &= 0x7f;
1587 }
1588 stream_forward_getp(s, 3); /* Skip other metrics */
1589 rv->prefix.family = AF_INET;
1590 stream_get(&rv->prefix.prefix, s, 4);
1591
1592 struct in_addr mask;
1593 stream_get(&mask, s, 4);
1594 rv->prefix.prefixlen = ip_masklen(mask);
1595
1596 format_item_oldstyle_ip_reach(mtid, (struct isis_item *)rv, log,
1597 indent + 2);
1598 append_item(dest, (struct isis_item *)rv);
1599 return 0;
1600}
1601
1602
1603/* Functions related to TLV 129 protocols supported */
1604
1605static void copy_tlv_protocols_supported(struct isis_protocols_supported *src,
1606 struct isis_protocols_supported *dest)
1607{
1608 if (!src->protocols || !src->count)
1609 return;
1610 dest->count = src->count;
841791b6 1611 dest->protocols = XCALLOC(MTYPE_ISIS_TLV, src->count);
7ef5fefc
CF
1612 memcpy(dest->protocols, src->protocols, src->count);
1613}
1614
1615static void format_tlv_protocols_supported(struct isis_protocols_supported *p,
1616 struct sbuf *buf, int indent)
1617{
1618 if (!p || !p->count || !p->protocols)
1619 return;
1620
1621 sbuf_push(buf, indent, "Protocols Supported: ");
1622 for (uint8_t i = 0; i < p->count; i++) {
1623 sbuf_push(buf, 0, "%s%s", nlpid2str(p->protocols[i]),
1624 (i + 1 < p->count) ? ", " : "");
1625 }
1626 sbuf_push(buf, 0, "\n");
1627}
1628
1629static void free_tlv_protocols_supported(struct isis_protocols_supported *p)
1630{
841791b6 1631 XFREE(MTYPE_ISIS_TLV, p->protocols);
7ef5fefc
CF
1632}
1633
1634static int pack_tlv_protocols_supported(struct isis_protocols_supported *p,
1635 struct stream *s)
1636{
1637 if (!p || !p->count || !p->protocols)
1638 return 0;
1639
1640 if (STREAM_WRITEABLE(s) < (unsigned)(p->count + 2))
1641 return 1;
1642
1643 stream_putc(s, ISIS_TLV_PROTOCOLS_SUPPORTED);
1644 stream_putc(s, p->count);
1645 stream_put(s, p->protocols, p->count);
1646 return 0;
1647}
1648
1649static int unpack_tlv_protocols_supported(enum isis_tlv_context context,
1650 uint8_t tlv_type, uint8_t tlv_len,
1651 struct stream *s, struct sbuf *log,
1652 void *dest, int indent)
1653{
1654 struct isis_tlvs *tlvs = dest;
1655
1656 sbuf_push(log, indent, "Unpacking Protocols Supported TLV...\n");
1657 if (!tlv_len) {
1658 sbuf_push(log, indent, "WARNING: No protocols included\n");
1659 return 0;
1660 }
1661 if (tlvs->protocols_supported.protocols) {
1662 sbuf_push(
1663 log, indent,
1664 "WARNING: protocols supported TLV present multiple times.\n");
1665 stream_forward_getp(s, tlv_len);
1666 return 0;
1667 }
1668
1669 tlvs->protocols_supported.count = tlv_len;
841791b6 1670 tlvs->protocols_supported.protocols = XCALLOC(MTYPE_ISIS_TLV, tlv_len);
7ef5fefc
CF
1671 stream_get(tlvs->protocols_supported.protocols, s, tlv_len);
1672
1673 format_tlv_protocols_supported(&tlvs->protocols_supported, log,
1674 indent + 2);
1675 return 0;
1676}
1677
1678/* Functions related to TLV 132 IPv4 Interface addresses */
1679static struct isis_item *copy_item_ipv4_address(struct isis_item *i)
1680{
1681 struct isis_ipv4_address *a = (struct isis_ipv4_address *)i;
841791b6 1682 struct isis_ipv4_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1683
1684 rv->addr = a->addr;
1685 return (struct isis_item *)rv;
1686}
1687
1688static void format_item_ipv4_address(uint16_t mtid, struct isis_item *i,
1689 struct sbuf *buf, int indent)
1690{
1691 struct isis_ipv4_address *a = (struct isis_ipv4_address *)i;
1692 char addrbuf[INET_ADDRSTRLEN];
1693
1694 inet_ntop(AF_INET, &a->addr, addrbuf, sizeof(addrbuf));
1695 sbuf_push(buf, indent, "IPv4 Interface Address: %s\n", addrbuf);
1696}
1697
1698static void free_item_ipv4_address(struct isis_item *i)
1699{
841791b6 1700 XFREE(MTYPE_ISIS_TLV, i);
7ef5fefc
CF
1701}
1702
7ce31bab
DS
1703static int pack_item_ipv4_address(struct isis_item *i, struct stream *s,
1704 size_t *min_len)
7ef5fefc
CF
1705{
1706 struct isis_ipv4_address *a = (struct isis_ipv4_address *)i;
1707
7ce31bab
DS
1708 if (STREAM_WRITEABLE(s) < 4) {
1709 *min_len = 4;
7ef5fefc 1710 return 1;
7ce31bab 1711 }
7ef5fefc
CF
1712
1713 stream_put(s, &a->addr, 4);
1714
1715 return 0;
1716}
1717
1718static int unpack_item_ipv4_address(uint16_t mtid, uint8_t len,
1719 struct stream *s, struct sbuf *log,
1720 void *dest, int indent)
1721{
1722 struct isis_tlvs *tlvs = dest;
1723
1724 sbuf_push(log, indent, "Unpack IPv4 Interface address...\n");
1725 if (len < 4) {
1726 sbuf_push(
1727 log, indent,
6cde4b45 1728 "Not enough data left.(Expected 4 bytes of IPv4 address, got %hhu)\n",
7ef5fefc
CF
1729 len);
1730 return 1;
1731 }
1732
841791b6 1733 struct isis_ipv4_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1734 stream_get(&rv->addr, s, 4);
1735
1736 format_item_ipv4_address(mtid, (struct isis_item *)rv, log, indent + 2);
1737 append_item(&tlvs->ipv4_address, (struct isis_item *)rv);
1738 return 0;
1739}
1740
1741
1742/* Functions related to TLV 232 IPv6 Interface addresses */
1743static struct isis_item *copy_item_ipv6_address(struct isis_item *i)
1744{
1745 struct isis_ipv6_address *a = (struct isis_ipv6_address *)i;
841791b6 1746 struct isis_ipv6_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1747
1748 rv->addr = a->addr;
1749 return (struct isis_item *)rv;
1750}
1751
1752static void format_item_ipv6_address(uint16_t mtid, struct isis_item *i,
1753 struct sbuf *buf, int indent)
1754{
1755 struct isis_ipv6_address *a = (struct isis_ipv6_address *)i;
1756 char addrbuf[INET6_ADDRSTRLEN];
1757
1758 inet_ntop(AF_INET6, &a->addr, addrbuf, sizeof(addrbuf));
1759 sbuf_push(buf, indent, "IPv6 Interface Address: %s\n", addrbuf);
1760}
1761
1762static void free_item_ipv6_address(struct isis_item *i)
1763{
841791b6 1764 XFREE(MTYPE_ISIS_TLV, i);
7ef5fefc
CF
1765}
1766
7ce31bab
DS
1767static int pack_item_ipv6_address(struct isis_item *i, struct stream *s,
1768 size_t *min_len)
7ef5fefc
CF
1769{
1770 struct isis_ipv6_address *a = (struct isis_ipv6_address *)i;
1771
7ce31bab
DS
1772 if (STREAM_WRITEABLE(s) < 16) {
1773 *min_len = 16;
7ef5fefc 1774 return 1;
7ce31bab 1775 }
7ef5fefc
CF
1776
1777 stream_put(s, &a->addr, 16);
1778
1779 return 0;
1780}
1781
1782static int unpack_item_ipv6_address(uint16_t mtid, uint8_t len,
1783 struct stream *s, struct sbuf *log,
1784 void *dest, int indent)
1785{
1786 struct isis_tlvs *tlvs = dest;
1787
1788 sbuf_push(log, indent, "Unpack IPv6 Interface address...\n");
1789 if (len < 16) {
1790 sbuf_push(
1791 log, indent,
6cde4b45 1792 "Not enough data left.(Expected 16 bytes of IPv6 address, got %hhu)\n",
7ef5fefc
CF
1793 len);
1794 return 1;
1795 }
1796
841791b6 1797 struct isis_ipv6_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1798 stream_get(&rv->addr, s, 16);
1799
1800 format_item_ipv6_address(mtid, (struct isis_item *)rv, log, indent + 2);
1801 append_item(&tlvs->ipv6_address, (struct isis_item *)rv);
1802 return 0;
1803}
1804
1805
1806/* Functions related to TLV 229 MT Router information */
1807static struct isis_item *copy_item_mt_router_info(struct isis_item *i)
1808{
1809 struct isis_mt_router_info *info = (struct isis_mt_router_info *)i;
841791b6 1810 struct isis_mt_router_info *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1811
1812 rv->overload = info->overload;
1813 rv->attached = info->attached;
1814 rv->mtid = info->mtid;
1815 return (struct isis_item *)rv;
1816}
1817
1818static void format_item_mt_router_info(uint16_t mtid, struct isis_item *i,
1819 struct sbuf *buf, int indent)
1820{
1821 struct isis_mt_router_info *info = (struct isis_mt_router_info *)i;
1822
1823 sbuf_push(buf, indent, "MT Router Info: %s%s%s\n",
98c5bc15
CF
1824 isis_mtid2str(info->mtid),
1825 info->overload ? " Overload" : "",
7ef5fefc
CF
1826 info->attached ? " Attached" : "");
1827}
1828
1829static void free_item_mt_router_info(struct isis_item *i)
1830{
841791b6 1831 XFREE(MTYPE_ISIS_TLV, i);
7ef5fefc
CF
1832}
1833
7ce31bab
DS
1834static int pack_item_mt_router_info(struct isis_item *i, struct stream *s,
1835 size_t *min_len)
7ef5fefc
CF
1836{
1837 struct isis_mt_router_info *info = (struct isis_mt_router_info *)i;
1838
7ce31bab
DS
1839 if (STREAM_WRITEABLE(s) < 2) {
1840 *min_len = 2;
7ef5fefc 1841 return 1;
7ce31bab 1842 }
7ef5fefc
CF
1843
1844 uint16_t entry = info->mtid;
1845
1846 if (info->overload)
1847 entry |= ISIS_MT_OL_MASK;
1848 if (info->attached)
1849 entry |= ISIS_MT_AT_MASK;
1850
1851 stream_putw(s, entry);
1852
1853 return 0;
1854}
1855
1856static int unpack_item_mt_router_info(uint16_t mtid, uint8_t len,
1857 struct stream *s, struct sbuf *log,
1858 void *dest, int indent)
1859{
1860 struct isis_tlvs *tlvs = dest;
1861
1862 sbuf_push(log, indent, "Unpack MT Router info...\n");
1863 if (len < 2) {
1864 sbuf_push(
1865 log, indent,
6cde4b45 1866 "Not enough data left.(Expected 2 bytes of MT info, got %hhu)\n",
7ef5fefc
CF
1867 len);
1868 return 1;
1869 }
1870
841791b6 1871 struct isis_mt_router_info *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1872
1873 uint16_t entry = stream_getw(s);
1874 rv->overload = entry & ISIS_MT_OL_MASK;
1875 rv->attached = entry & ISIS_MT_AT_MASK;
1876 rv->mtid = entry & ISIS_MT_MASK;
1877
1878 format_item_mt_router_info(mtid, (struct isis_item *)rv, log,
1879 indent + 2);
1880 append_item(&tlvs->mt_router_info, (struct isis_item *)rv);
1881 return 0;
1882}
1883
1884/* Functions related to TLV 134 TE Router ID */
1885
1886static struct in_addr *copy_tlv_te_router_id(const struct in_addr *id)
1887{
1888 if (!id)
1889 return NULL;
1890
841791b6 1891 struct in_addr *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1892 memcpy(rv, id, sizeof(*rv));
1893 return rv;
1894}
1895
1896static void format_tlv_te_router_id(const struct in_addr *id, struct sbuf *buf,
1897 int indent)
1898{
1899 if (!id)
1900 return;
1901
1902 char addrbuf[INET_ADDRSTRLEN];
1903 inet_ntop(AF_INET, id, addrbuf, sizeof(addrbuf));
1904 sbuf_push(buf, indent, "TE Router ID: %s\n", addrbuf);
1905}
1906
1907static void free_tlv_te_router_id(struct in_addr *id)
1908{
841791b6 1909 XFREE(MTYPE_ISIS_TLV, id);
7ef5fefc
CF
1910}
1911
1912static int pack_tlv_te_router_id(const struct in_addr *id, struct stream *s)
1913{
1914 if (!id)
1915 return 0;
1916
1917 if (STREAM_WRITEABLE(s) < (unsigned)(2 + sizeof(*id)))
1918 return 1;
1919
1920 stream_putc(s, ISIS_TLV_TE_ROUTER_ID);
1921 stream_putc(s, 4);
1922 stream_put(s, id, 4);
1923 return 0;
1924}
1925
1926static int unpack_tlv_te_router_id(enum isis_tlv_context context,
1927 uint8_t tlv_type, uint8_t tlv_len,
1928 struct stream *s, struct sbuf *log,
1929 void *dest, int indent)
1930{
1931 struct isis_tlvs *tlvs = dest;
1932
1933 sbuf_push(log, indent, "Unpacking TE Router ID TLV...\n");
1934 if (tlv_len != 4) {
1935 sbuf_push(log, indent, "WARNING: Length invalid\n");
1936 return 1;
1937 }
1938
1939 if (tlvs->te_router_id) {
1940 sbuf_push(log, indent,
1941 "WARNING: TE Router ID present multiple times.\n");
1942 stream_forward_getp(s, tlv_len);
1943 return 0;
1944 }
1945
841791b6 1946 tlvs->te_router_id = XCALLOC(MTYPE_ISIS_TLV, 4);
7ef5fefc
CF
1947 stream_get(tlvs->te_router_id, s, 4);
1948 format_tlv_te_router_id(tlvs->te_router_id, log, indent + 2);
1949 return 0;
1950}
1951
1952
1953/* Functions related to TLVs 135/235 extended IP reach/MT IP Reach */
1954
1955static struct isis_item *copy_item_extended_ip_reach(struct isis_item *i)
1956{
1957 struct isis_extended_ip_reach *r = (struct isis_extended_ip_reach *)i;
1958 struct isis_extended_ip_reach *rv =
841791b6 1959 XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
1960
1961 rv->metric = r->metric;
1962 rv->down = r->down;
1963 rv->prefix = r->prefix;
1b3f47d0 1964 rv->subtlvs = copy_subtlvs(r->subtlvs);
7ef5fefc
CF
1965
1966 return (struct isis_item *)rv;
1967}
1968
1969static void format_item_extended_ip_reach(uint16_t mtid, struct isis_item *i,
1970 struct sbuf *buf, int indent)
1971{
1972 struct isis_extended_ip_reach *r = (struct isis_extended_ip_reach *)i;
1973 char prefixbuf[PREFIX2STR_BUFFER];
1974
1975 sbuf_push(buf, indent, "%s IP Reachability: %s (Metric: %u)%s",
1976 (mtid == ISIS_MT_IPV4_UNICAST) ? "Extended" : "MT",
98c5bc15
CF
1977 prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf)), r->metric,
1978 r->down ? " Down" : "");
7ef5fefc
CF
1979 if (mtid != ISIS_MT_IPV4_UNICAST)
1980 sbuf_push(buf, 0, " %s", isis_mtid2str(mtid));
1981 sbuf_push(buf, 0, "\n");
9826647e
RW
1982
1983 if (r->subtlvs) {
1984 sbuf_push(buf, indent, " Subtlvs:\n");
1985 format_subtlvs(r->subtlvs, buf, indent + 4);
1986 }
7ef5fefc
CF
1987}
1988
1989static void free_item_extended_ip_reach(struct isis_item *i)
1990{
1991 struct isis_extended_ip_reach *item =
1992 (struct isis_extended_ip_reach *)i;
bd507085 1993 isis_free_subtlvs(item->subtlvs);
841791b6 1994 XFREE(MTYPE_ISIS_TLV, item);
7ef5fefc
CF
1995}
1996
7ce31bab
DS
1997static int pack_item_extended_ip_reach(struct isis_item *i, struct stream *s,
1998 size_t *min_len)
7ef5fefc
CF
1999{
2000 struct isis_extended_ip_reach *r = (struct isis_extended_ip_reach *)i;
2001 uint8_t control;
2002
7ce31bab
DS
2003 if (STREAM_WRITEABLE(s) < 5) {
2004 *min_len = 5;
7ef5fefc 2005 return 1;
7ce31bab 2006 }
7ef5fefc
CF
2007 stream_putl(s, r->metric);
2008
2009 control = r->down ? ISIS_EXTENDED_IP_REACH_DOWN : 0;
2010 control |= r->prefix.prefixlen;
bd507085
CF
2011 control |= r->subtlvs ? ISIS_EXTENDED_IP_REACH_SUBTLV : 0;
2012
7ef5fefc
CF
2013 stream_putc(s, control);
2014
7ce31bab
DS
2015 if (STREAM_WRITEABLE(s) < (unsigned)PSIZE(r->prefix.prefixlen)) {
2016 *min_len = 5 + (unsigned)PSIZE(r->prefix.prefixlen);
7ef5fefc 2017 return 1;
7ce31bab 2018 }
7ef5fefc 2019 stream_put(s, &r->prefix.prefix.s_addr, PSIZE(r->prefix.prefixlen));
bd507085
CF
2020
2021 if (r->subtlvs)
2022 return pack_subtlvs(r->subtlvs, s);
7ef5fefc
CF
2023 return 0;
2024}
2025
2026static int unpack_item_extended_ip_reach(uint16_t mtid, uint8_t len,
2027 struct stream *s, struct sbuf *log,
2028 void *dest, int indent)
2029{
2030 struct isis_tlvs *tlvs = dest;
2031 struct isis_extended_ip_reach *rv = NULL;
2032 size_t consume;
2033 uint8_t control, subtlv_len;
2034 struct isis_item_list *items;
2035
2036 if (mtid == ISIS_MT_IPV4_UNICAST) {
2037 items = &tlvs->extended_ip_reach;
2038 } else {
2039 items = isis_get_mt_items(&tlvs->mt_ip_reach, mtid);
2040 }
2041
2042 sbuf_push(log, indent, "Unpacking %s IPv4 reachability...\n",
2043 (mtid == ISIS_MT_IPV4_UNICAST) ? "extended" : "mt");
2044
2045 consume = 5;
2046 if (len < consume) {
98c5bc15 2047 sbuf_push(log, indent,
6cde4b45 2048 "Not enough data left. (expected 5 or more bytes, got %hhu)\n",
98c5bc15 2049 len);
7ef5fefc
CF
2050 goto out;
2051 }
2052
841791b6 2053 rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
2054
2055 rv->metric = stream_getl(s);
2056 control = stream_getc(s);
2057 rv->down = (control & ISIS_EXTENDED_IP_REACH_DOWN);
2058 rv->prefix.family = AF_INET;
2059 rv->prefix.prefixlen = control & 0x3f;
12256b84 2060 if (rv->prefix.prefixlen > IPV4_MAX_BITLEN) {
1b3f47d0 2061 sbuf_push(log, indent, "Prefixlen %u is implausible for IPv4\n",
7ef5fefc
CF
2062 rv->prefix.prefixlen);
2063 goto out;
2064 }
2065
2066 consume += PSIZE(rv->prefix.prefixlen);
2067 if (len < consume) {
98c5bc15
CF
2068 sbuf_push(log, indent,
2069 "Expected %u bytes of prefix, but only %u bytes available.\n",
2070 PSIZE(rv->prefix.prefixlen), len - 5);
7ef5fefc
CF
2071 goto out;
2072 }
2073 stream_get(&rv->prefix.prefix.s_addr, s, PSIZE(rv->prefix.prefixlen));
2074 in_addr_t orig_prefix = rv->prefix.prefix.s_addr;
2075 apply_mask_ipv4(&rv->prefix);
2076 if (orig_prefix != rv->prefix.prefix.s_addr)
2077 sbuf_push(log, indent + 2,
2078 "WARNING: Prefix had hostbits set.\n");
2079 format_item_extended_ip_reach(mtid, (struct isis_item *)rv, log,
2080 indent + 2);
2081
2082 if (control & ISIS_EXTENDED_IP_REACH_SUBTLV) {
2083 consume += 1;
2084 if (len < consume) {
98c5bc15
CF
2085 sbuf_push(log, indent,
2086 "Expected 1 byte of subtlv len, but no more data present.\n");
7ef5fefc
CF
2087 goto out;
2088 }
2089 subtlv_len = stream_getc(s);
2090
2091 if (!subtlv_len) {
98c5bc15
CF
2092 sbuf_push(log, indent + 2,
2093 " WARNING: subtlv bit is set, but there are no subtlvs.\n");
7ef5fefc
CF
2094 }
2095 consume += subtlv_len;
2096 if (len < consume) {
98c5bc15 2097 sbuf_push(log, indent,
6cde4b45 2098 "Expected %hhu bytes of subtlvs, but only %u bytes available.\n",
98c5bc15
CF
2099 subtlv_len,
2100 len - 6 - PSIZE(rv->prefix.prefixlen));
7ef5fefc
CF
2101 goto out;
2102 }
bd507085
CF
2103
2104 rv->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IP_REACH);
bf555bf0
CF
2105 bool unpacked_known_tlvs = false;
2106
bd507085 2107 if (unpack_tlvs(ISIS_CONTEXT_SUBTLV_IP_REACH, subtlv_len, s,
bf555bf0 2108 log, rv->subtlvs, indent + 4, &unpacked_known_tlvs)) {
bd507085
CF
2109 goto out;
2110 }
bf555bf0
CF
2111 if (!unpacked_known_tlvs) {
2112 isis_free_subtlvs(rv->subtlvs);
2113 rv->subtlvs = NULL;
2114 }
7ef5fefc
CF
2115 }
2116
2117 append_item(items, (struct isis_item *)rv);
2118 return 0;
2119out:
2120 if (rv)
2121 free_item_extended_ip_reach((struct isis_item *)rv);
2122 return 1;
2123}
2124
2125/* Functions related to TLV 137 Dynamic Hostname */
2126
2127static char *copy_tlv_dynamic_hostname(const char *hostname)
2128{
2129 if (!hostname)
2130 return NULL;
2131
841791b6 2132 return XSTRDUP(MTYPE_ISIS_TLV, hostname);
7ef5fefc
CF
2133}
2134
2135static void format_tlv_dynamic_hostname(const char *hostname, struct sbuf *buf,
2136 int indent)
2137{
2138 if (!hostname)
2139 return;
2140
2141 sbuf_push(buf, indent, "Hostname: %s\n", hostname);
2142}
2143
2144static void free_tlv_dynamic_hostname(char *hostname)
2145{
841791b6 2146 XFREE(MTYPE_ISIS_TLV, hostname);
7ef5fefc
CF
2147}
2148
2149static int pack_tlv_dynamic_hostname(const char *hostname, struct stream *s)
2150{
2151 if (!hostname)
2152 return 0;
2153
2154 uint8_t name_len = strlen(hostname);
2155
2156 if (STREAM_WRITEABLE(s) < (unsigned)(2 + name_len))
2157 return 1;
2158
2159 stream_putc(s, ISIS_TLV_DYNAMIC_HOSTNAME);
2160 stream_putc(s, name_len);
2161 stream_put(s, hostname, name_len);
2162 return 0;
2163}
2164
2165static int unpack_tlv_dynamic_hostname(enum isis_tlv_context context,
2166 uint8_t tlv_type, uint8_t tlv_len,
2167 struct stream *s, struct sbuf *log,
2168 void *dest, int indent)
2169{
2170 struct isis_tlvs *tlvs = dest;
2171
2172 sbuf_push(log, indent, "Unpacking Dynamic Hostname TLV...\n");
2173 if (!tlv_len) {
2174 sbuf_push(log, indent, "WARNING: No hostname included\n");
2175 return 0;
2176 }
2177
2178 if (tlvs->hostname) {
2179 sbuf_push(log, indent,
2180 "WARNING: Hostname present multiple times.\n");
2181 stream_forward_getp(s, tlv_len);
2182 return 0;
2183 }
2184
841791b6 2185 tlvs->hostname = XCALLOC(MTYPE_ISIS_TLV, tlv_len + 1);
7ef5fefc
CF
2186 stream_get(tlvs->hostname, s, tlv_len);
2187 tlvs->hostname[tlv_len] = '\0';
2188
2189 bool sane = true;
2190 for (uint8_t i = 0; i < tlv_len; i++) {
2191 if ((unsigned char)tlvs->hostname[i] > 127
fefa5e0f 2192 || !isprint((unsigned char)tlvs->hostname[i])) {
7ef5fefc
CF
2193 sane = false;
2194 tlvs->hostname[i] = '?';
2195 }
2196 }
2197 if (!sane) {
2198 sbuf_push(
2199 log, indent,
2200 "WARNING: Hostname contained non-printable/non-ascii characters.\n");
2201 }
2202
2203 return 0;
2204}
2205
41a145f1
CF
2206/* Functions related to TLV 150 Spine-Leaf-Extension */
2207
2208static struct isis_spine_leaf *copy_tlv_spine_leaf(
2209 const struct isis_spine_leaf *spine_leaf)
2210{
2211 if (!spine_leaf)
2212 return NULL;
2213
2214 struct isis_spine_leaf *rv = XMALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
2215 memcpy(rv, spine_leaf, sizeof(*rv));
2216
2217 return rv;
2218}
2219
2220static void format_tlv_spine_leaf(const struct isis_spine_leaf *spine_leaf,
2221 struct sbuf *buf, int indent)
2222{
2223 if (!spine_leaf)
2224 return;
2225
2226 sbuf_push(buf, indent, "Spine-Leaf-Extension:\n");
2227 if (spine_leaf->has_tier) {
2228 if (spine_leaf->tier == ISIS_TIER_UNDEFINED) {
2229 sbuf_push(buf, indent, " Tier: undefined\n");
2230 } else {
6cde4b45 2231 sbuf_push(buf, indent, " Tier: %hhu\n",
41a145f1
CF
2232 spine_leaf->tier);
2233 }
2234 }
2235
2236 sbuf_push(buf, indent, " Flags:%s%s%s\n",
2237 spine_leaf->is_leaf ? " LEAF" : "",
2238 spine_leaf->is_spine ? " SPINE" : "",
2239 spine_leaf->is_backup ? " BACKUP" : "");
2240
2241}
2242
2243static void free_tlv_spine_leaf(struct isis_spine_leaf *spine_leaf)
2244{
2245 XFREE(MTYPE_ISIS_TLV, spine_leaf);
2246}
2247
2248#define ISIS_SPINE_LEAF_FLAG_TIER 0x08
2249#define ISIS_SPINE_LEAF_FLAG_BACKUP 0x04
2250#define ISIS_SPINE_LEAF_FLAG_SPINE 0x02
2251#define ISIS_SPINE_LEAF_FLAG_LEAF 0x01
2252
2253static int pack_tlv_spine_leaf(const struct isis_spine_leaf *spine_leaf,
2254 struct stream *s)
2255{
2256 if (!spine_leaf)
2257 return 0;
2258
2259 uint8_t tlv_len = 2;
2260
2261 if (STREAM_WRITEABLE(s) < (unsigned)(2 + tlv_len))
2262 return 1;
2263
2264 stream_putc(s, ISIS_TLV_SPINE_LEAF_EXT);
2265 stream_putc(s, tlv_len);
2266
2267 uint16_t spine_leaf_flags = 0;
2268
2269 if (spine_leaf->has_tier) {
2270 spine_leaf_flags |= ISIS_SPINE_LEAF_FLAG_TIER;
2271 spine_leaf_flags |= spine_leaf->tier << 12;
2272 }
2273
2274 if (spine_leaf->is_leaf)
2275 spine_leaf_flags |= ISIS_SPINE_LEAF_FLAG_LEAF;
2276
2277 if (spine_leaf->is_spine)
2278 spine_leaf_flags |= ISIS_SPINE_LEAF_FLAG_SPINE;
2279
2280 if (spine_leaf->is_backup)
2281 spine_leaf_flags |= ISIS_SPINE_LEAF_FLAG_BACKUP;
2282
2283 stream_putw(s, spine_leaf_flags);
2284
2285 return 0;
2286}
2287
2288static int unpack_tlv_spine_leaf(enum isis_tlv_context context,
2289 uint8_t tlv_type, uint8_t tlv_len,
2290 struct stream *s, struct sbuf *log,
2291 void *dest, int indent)
2292{
2293 struct isis_tlvs *tlvs = dest;
2294
2295 sbuf_push(log, indent, "Unpacking Spine Leaf Extension TLV...\n");
2296 if (tlv_len < 2) {
377e2dd3 2297 sbuf_push(log, indent, "WARNING: Unexpected TLV size\n");
41a145f1
CF
2298 stream_forward_getp(s, tlv_len);
2299 return 0;
2300 }
2301
2302 if (tlvs->spine_leaf) {
2303 sbuf_push(log, indent,
2304 "WARNING: Spine Leaf Extension TLV present multiple times.\n");
2305 stream_forward_getp(s, tlv_len);
2306 return 0;
2307 }
2308
2309 tlvs->spine_leaf = XCALLOC(MTYPE_ISIS_TLV, sizeof(*tlvs->spine_leaf));
2310
2311 uint16_t spine_leaf_flags = stream_getw(s);
2312
2313 if (spine_leaf_flags & ISIS_SPINE_LEAF_FLAG_TIER) {
2314 tlvs->spine_leaf->has_tier = true;
2315 tlvs->spine_leaf->tier = spine_leaf_flags >> 12;
2316 }
2317
2318 tlvs->spine_leaf->is_leaf = spine_leaf_flags & ISIS_SPINE_LEAF_FLAG_LEAF;
2319 tlvs->spine_leaf->is_spine = spine_leaf_flags & ISIS_SPINE_LEAF_FLAG_SPINE;
2320 tlvs->spine_leaf->is_backup = spine_leaf_flags & ISIS_SPINE_LEAF_FLAG_BACKUP;
2321
2322 stream_forward_getp(s, tlv_len - 2);
2323 return 0;
2324}
2325
9fe21208
CF
2326/* Functions related to TLV 240 P2P Three-Way Adjacency */
2327
2328const char *isis_threeway_state_name(enum isis_threeway_state state)
2329{
2330 switch (state) {
2331 case ISIS_THREEWAY_DOWN:
2332 return "Down";
2333 case ISIS_THREEWAY_INITIALIZING:
2334 return "Initializing";
2335 case ISIS_THREEWAY_UP:
2336 return "Up";
2337 default:
2338 return "Invalid!";
2339 }
2340}
2341
2342static struct isis_threeway_adj *copy_tlv_threeway_adj(
2343 const struct isis_threeway_adj *threeway_adj)
2344{
2345 if (!threeway_adj)
2346 return NULL;
2347
2348 struct isis_threeway_adj *rv = XMALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
2349 memcpy(rv, threeway_adj, sizeof(*rv));
2350
2351 return rv;
2352}
2353
2354static void format_tlv_threeway_adj(const struct isis_threeway_adj *threeway_adj,
2355 struct sbuf *buf, int indent)
2356{
2357 if (!threeway_adj)
2358 return;
2359
2360 sbuf_push(buf, indent, "P2P Three-Way Adjacency:\n");
2361 sbuf_push(buf, indent, " State: %s (%d)\n",
2362 isis_threeway_state_name(threeway_adj->state),
2363 threeway_adj->state);
6cde4b45 2364 sbuf_push(buf, indent, " Extended Local Circuit ID: %u\n",
9fe21208
CF
2365 threeway_adj->local_circuit_id);
2366 if (!threeway_adj->neighbor_set)
2367 return;
2368
2369 sbuf_push(buf, indent, " Neighbor System ID: %s\n",
2370 isis_format_id(threeway_adj->neighbor_id, 6));
6cde4b45 2371 sbuf_push(buf, indent, " Neighbor Extended Circuit ID: %u\n",
9fe21208
CF
2372 threeway_adj->neighbor_circuit_id);
2373}
2374
2375static void free_tlv_threeway_adj(struct isis_threeway_adj *threeway_adj)
2376{
2377 XFREE(MTYPE_ISIS_TLV, threeway_adj);
2378}
2379
2380static int pack_tlv_threeway_adj(const struct isis_threeway_adj *threeway_adj,
2381 struct stream *s)
2382{
2383 if (!threeway_adj)
2384 return 0;
2385
2386 uint8_t tlv_len = (threeway_adj->neighbor_set) ? 15 : 5;
2387
2388 if (STREAM_WRITEABLE(s) < (unsigned)(2 + tlv_len))
2389 return 1;
2390
2391 stream_putc(s, ISIS_TLV_THREE_WAY_ADJ);
2392 stream_putc(s, tlv_len);
2393 stream_putc(s, threeway_adj->state);
2394 stream_putl(s, threeway_adj->local_circuit_id);
2395
2396 if (threeway_adj->neighbor_set) {
2397 stream_put(s, threeway_adj->neighbor_id, 6);
2398 stream_putl(s, threeway_adj->neighbor_circuit_id);
2399 }
2400
2401 return 0;
2402}
2403
2404static int unpack_tlv_threeway_adj(enum isis_tlv_context context,
2405 uint8_t tlv_type, uint8_t tlv_len,
2406 struct stream *s, struct sbuf *log,
2407 void *dest, int indent)
2408{
2409 struct isis_tlvs *tlvs = dest;
2410
2411 sbuf_push(log, indent, "Unpacking P2P Three-Way Adjacency TLV...\n");
2412 if (tlv_len != 5 && tlv_len != 15) {
377e2dd3 2413 sbuf_push(log, indent, "WARNING: Unexpected TLV size\n");
9fe21208
CF
2414 stream_forward_getp(s, tlv_len);
2415 return 0;
2416 }
2417
2418 if (tlvs->threeway_adj) {
2419 sbuf_push(log, indent,
2420 "WARNING: P2P Three-Way Adjacency TLV present multiple times.\n");
2421 stream_forward_getp(s, tlv_len);
2422 return 0;
2423 }
2424
2425 tlvs->threeway_adj = XCALLOC(MTYPE_ISIS_TLV, sizeof(*tlvs->threeway_adj));
2426
2427 tlvs->threeway_adj->state = stream_getc(s);
2428 tlvs->threeway_adj->local_circuit_id = stream_getl(s);
2429
2430 if (tlv_len == 15) {
2431 tlvs->threeway_adj->neighbor_set = true;
2432 stream_get(tlvs->threeway_adj->neighbor_id, s, 6);
2433 tlvs->threeway_adj->neighbor_circuit_id = stream_getl(s);
2434 }
2435
2436 return 0;
2437}
2438
7ef5fefc 2439/* Functions related to TLVs 236/237 IPv6/MT-IPv6 reach */
7ef5fefc
CF
2440static struct isis_item *copy_item_ipv6_reach(struct isis_item *i)
2441{
2442 struct isis_ipv6_reach *r = (struct isis_ipv6_reach *)i;
841791b6 2443 struct isis_ipv6_reach *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
a064a7b8 2444
7ef5fefc
CF
2445 rv->metric = r->metric;
2446 rv->down = r->down;
2447 rv->external = r->external;
2448 rv->prefix = r->prefix;
2449 rv->subtlvs = copy_subtlvs(r->subtlvs);
2450
2451 return (struct isis_item *)rv;
2452}
2453
2454static void format_item_ipv6_reach(uint16_t mtid, struct isis_item *i,
2455 struct sbuf *buf, int indent)
2456{
2457 struct isis_ipv6_reach *r = (struct isis_ipv6_reach *)i;
2458 char prefixbuf[PREFIX2STR_BUFFER];
2459
2460 sbuf_push(buf, indent, "%sIPv6 Reachability: %s (Metric: %u)%s%s",
2461 (mtid == ISIS_MT_IPV4_UNICAST) ? "" : "MT ",
2462 prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf)),
98c5bc15
CF
2463 r->metric,
2464 r->down ? " Down" : "",
7ef5fefc
CF
2465 r->external ? " External" : "");
2466 if (mtid != ISIS_MT_IPV4_UNICAST)
2467 sbuf_push(buf, 0, " %s", isis_mtid2str(mtid));
2468 sbuf_push(buf, 0, "\n");
2469
2470 if (r->subtlvs) {
2471 sbuf_push(buf, indent, " Subtlvs:\n");
2472 format_subtlvs(r->subtlvs, buf, indent + 4);
2473 }
2474}
2475
2476static void free_item_ipv6_reach(struct isis_item *i)
2477{
2478 struct isis_ipv6_reach *item = (struct isis_ipv6_reach *)i;
2479
2480 isis_free_subtlvs(item->subtlvs);
841791b6 2481 XFREE(MTYPE_ISIS_TLV, item);
7ef5fefc
CF
2482}
2483
7ce31bab
DS
2484static int pack_item_ipv6_reach(struct isis_item *i, struct stream *s,
2485 size_t *min_len)
7ef5fefc
CF
2486{
2487 struct isis_ipv6_reach *r = (struct isis_ipv6_reach *)i;
2488 uint8_t control;
2489
7ce31bab
DS
2490 if (STREAM_WRITEABLE(s) < 6 + (unsigned)PSIZE(r->prefix.prefixlen)) {
2491 *min_len = 6 + (unsigned)PSIZE(r->prefix.prefixlen);
7ef5fefc 2492 return 1;
7ce31bab 2493 }
7ef5fefc
CF
2494 stream_putl(s, r->metric);
2495
2496 control = r->down ? ISIS_IPV6_REACH_DOWN : 0;
2497 control |= r->external ? ISIS_IPV6_REACH_EXTERNAL : 0;
2498 control |= r->subtlvs ? ISIS_IPV6_REACH_SUBTLV : 0;
2499
2500 stream_putc(s, control);
2501 stream_putc(s, r->prefix.prefixlen);
2502
7ef5fefc
CF
2503 stream_put(s, &r->prefix.prefix.s6_addr, PSIZE(r->prefix.prefixlen));
2504
2505 if (r->subtlvs)
2506 return pack_subtlvs(r->subtlvs, s);
2507
2508 return 0;
2509}
2510
2511static int unpack_item_ipv6_reach(uint16_t mtid, uint8_t len, struct stream *s,
2512 struct sbuf *log, void *dest, int indent)
2513{
2514 struct isis_tlvs *tlvs = dest;
2515 struct isis_ipv6_reach *rv = NULL;
2516 size_t consume;
2517 uint8_t control, subtlv_len;
2518 struct isis_item_list *items;
2519
2520 if (mtid == ISIS_MT_IPV4_UNICAST) {
2521 items = &tlvs->ipv6_reach;
2522 } else {
2523 items = isis_get_mt_items(&tlvs->mt_ipv6_reach, mtid);
2524 }
2525
2526 sbuf_push(log, indent, "Unpacking %sIPv6 reachability...\n",
2527 (mtid == ISIS_MT_IPV4_UNICAST) ? "" : "mt ");
2528 consume = 6;
2529 if (len < consume) {
98c5bc15 2530 sbuf_push(log, indent,
6cde4b45 2531 "Not enough data left. (expected 6 or more bytes, got %hhu)\n",
98c5bc15 2532 len);
7ef5fefc
CF
2533 goto out;
2534 }
2535
841791b6 2536 rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
2537
2538 rv->metric = stream_getl(s);
2539 control = stream_getc(s);
2540 rv->down = (control & ISIS_IPV6_REACH_DOWN);
2541 rv->external = (control & ISIS_IPV6_REACH_EXTERNAL);
2542
2543 rv->prefix.family = AF_INET6;
2544 rv->prefix.prefixlen = stream_getc(s);
13ccce6e 2545 if (rv->prefix.prefixlen > IPV6_MAX_BITLEN) {
1b3f47d0 2546 sbuf_push(log, indent, "Prefixlen %u is implausible for IPv6\n",
7ef5fefc
CF
2547 rv->prefix.prefixlen);
2548 goto out;
2549 }
2550
2551 consume += PSIZE(rv->prefix.prefixlen);
2552 if (len < consume) {
98c5bc15
CF
2553 sbuf_push(log, indent,
2554 "Expected %u bytes of prefix, but only %u bytes available.\n",
2555 PSIZE(rv->prefix.prefixlen), len - 6);
7ef5fefc
CF
2556 goto out;
2557 }
2558 stream_get(&rv->prefix.prefix.s6_addr, s, PSIZE(rv->prefix.prefixlen));
2559 struct in6_addr orig_prefix = rv->prefix.prefix;
1b3f47d0 2560
7ef5fefc
CF
2561 apply_mask_ipv6(&rv->prefix);
2562 if (memcmp(&orig_prefix, &rv->prefix.prefix, sizeof(orig_prefix)))
2563 sbuf_push(log, indent + 2,
2564 "WARNING: Prefix had hostbits set.\n");
2565 format_item_ipv6_reach(mtid, (struct isis_item *)rv, log, indent + 2);
2566
2567 if (control & ISIS_IPV6_REACH_SUBTLV) {
2568 consume += 1;
2569 if (len < consume) {
98c5bc15
CF
2570 sbuf_push(log, indent,
2571 "Expected 1 byte of subtlv len, but no more data persent.\n");
7ef5fefc
CF
2572 goto out;
2573 }
2574 subtlv_len = stream_getc(s);
2575
2576 if (!subtlv_len) {
98c5bc15
CF
2577 sbuf_push(log, indent + 2,
2578 " WARNING: subtlv bit set, but there are no subtlvs.\n");
7ef5fefc
CF
2579 }
2580 consume += subtlv_len;
2581 if (len < consume) {
98c5bc15 2582 sbuf_push(log, indent,
6cde4b45 2583 "Expected %hhu bytes of subtlvs, but only %u bytes available.\n",
98c5bc15
CF
2584 subtlv_len,
2585 len - 6 - PSIZE(rv->prefix.prefixlen));
7ef5fefc
CF
2586 goto out;
2587 }
2588
bd507085 2589 rv->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IPV6_REACH);
bf555bf0
CF
2590 bool unpacked_known_tlvs = false;
2591
7ef5fefc 2592 if (unpack_tlvs(ISIS_CONTEXT_SUBTLV_IPV6_REACH, subtlv_len, s,
bf555bf0 2593 log, rv->subtlvs, indent + 4, &unpacked_known_tlvs)) {
7ef5fefc
CF
2594 goto out;
2595 }
bf555bf0
CF
2596 if (!unpacked_known_tlvs) {
2597 isis_free_subtlvs(rv->subtlvs);
2598 rv->subtlvs = NULL;
2599 }
7ef5fefc
CF
2600 }
2601
2602 append_item(items, (struct isis_item *)rv);
2603 return 0;
2604out:
2605 if (rv)
2606 free_item_ipv6_reach((struct isis_item *)rv);
2607 return 1;
2608}
2609
f2333421 2610/* Functions related to TLV 242 Router Capability as per RFC7981 */
1b3f47d0
OD
2611static struct isis_router_cap *copy_tlv_router_cap(
2612 const struct isis_router_cap *router_cap)
2613{
a064a7b8 2614 struct isis_router_cap *rv;
1b3f47d0
OD
2615
2616 if (!router_cap)
2617 return NULL;
2618
a064a7b8
DS
2619 rv = XMALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
2620
1b3f47d0
OD
2621 memcpy(rv, router_cap, sizeof(*rv));
2622
2623 return rv;
2624}
2625
2626static void format_tlv_router_cap(const struct isis_router_cap *router_cap,
2627 struct sbuf *buf, int indent)
2628{
2629 char addrbuf[INET_ADDRSTRLEN];
2630
2631 if (!router_cap)
2632 return;
2633
2634 /* Router ID and Flags */
2635 inet_ntop(AF_INET, &router_cap->router_id, addrbuf, sizeof(addrbuf));
2636 sbuf_push(buf, indent, "Router Capability:");
2637 sbuf_push(buf, indent, " %s , D:%c, S:%c\n", addrbuf,
2638 router_cap->flags & ISIS_ROUTER_CAP_FLAG_D ? '1' : '0',
2639 router_cap->flags & ISIS_ROUTER_CAP_FLAG_S ? '1' : '0');
2640
f2333421 2641 /* Segment Routing Global Block as per RFC8667 section #3.1 */
1b3f47d0 2642 if (router_cap->srgb.range_size != 0)
d8391312
OD
2643 sbuf_push(
2644 buf, indent,
2645 " Segment Routing: I:%s V:%s, Global Block Base: %u Range: %u\n",
d47d6089
RW
2646 IS_SR_IPV4(&router_cap->srgb) ? "1" : "0",
2647 IS_SR_IPV6(&router_cap->srgb) ? "1" : "0",
1b3f47d0
OD
2648 router_cap->srgb.lower_bound,
2649 router_cap->srgb.range_size);
2650
d8391312
OD
2651 /* Segment Routing Local Block as per RFC8667 section #3.3 */
2652 if (router_cap->srlb.range_size != 0)
2653 sbuf_push(buf, indent, " SR Local Block Base: %u Range: %u\n",
2654 router_cap->srlb.lower_bound,
2655 router_cap->srlb.range_size);
2656
f2333421 2657 /* Segment Routing Algorithms as per RFC8667 section #3.2 */
1b3f47d0 2658 if (router_cap->algo[0] != SR_ALGORITHM_UNSET) {
d8391312
OD
2659 sbuf_push(buf, indent, " SR Algorithm:\n");
2660 for (int i = 0; i < SR_ALGORITHM_COUNT; i++)
1b3f47d0 2661 if (router_cap->algo[i] != SR_ALGORITHM_UNSET)
d8391312
OD
2662 sbuf_push(buf, indent, " %u: %s\n", i,
2663 router_cap->algo[i] == 0
2664 ? "SPF"
2665 : "Strict SPF");
1b3f47d0
OD
2666 }
2667
f2333421 2668 /* Segment Routing Node MSD as per RFC8491 section #2 */
1b3f47d0 2669 if (router_cap->msd != 0)
d8391312
OD
2670 sbuf_push(buf, indent, " Node Maximum SID Depth: %u\n",
2671 router_cap->msd);
1b3f47d0
OD
2672}
2673
2674static void free_tlv_router_cap(struct isis_router_cap *router_cap)
2675{
2676 XFREE(MTYPE_ISIS_TLV, router_cap);
2677}
2678
2679static int pack_tlv_router_cap(const struct isis_router_cap *router_cap,
2680 struct stream *s)
2681{
2682 size_t tlv_len = ISIS_ROUTER_CAP_SIZE;
2683 size_t len_pos;
2684 uint8_t nb_algo;
2685
2686 if (!router_cap)
2687 return 0;
2688
2689 /* Compute Maximum TLV size */
2690 tlv_len += ISIS_SUBTLV_SID_LABEL_RANGE_SIZE
2691 + ISIS_SUBTLV_HDR_SIZE
2692 + ISIS_SUBTLV_ALGORITHM_SIZE
2693 + ISIS_SUBTLV_NODE_MSD_SIZE;
2694
2695 if (STREAM_WRITEABLE(s) < (unsigned int)(2 + tlv_len))
2696 return 1;
2697
2698 /* Add Router Capability TLV 242 with Router ID and Flags */
2699 stream_putc(s, ISIS_TLV_ROUTER_CAPABILITY);
2700 /* Real length will be adjusted later */
2701 len_pos = stream_get_endp(s);
2702 stream_putc(s, tlv_len);
2703 stream_put_ipv4(s, router_cap->router_id.s_addr);
2704 stream_putc(s, router_cap->flags);
2705
f2333421 2706 /* Add SRGB if set as per RFC8667 section #3.1 */
1b3f47d0
OD
2707 if ((router_cap->srgb.range_size != 0)
2708 && (router_cap->srgb.lower_bound != 0)) {
2709 stream_putc(s, ISIS_SUBTLV_SID_LABEL_RANGE);
2710 stream_putc(s, ISIS_SUBTLV_SID_LABEL_RANGE_SIZE);
2711 stream_putc(s, router_cap->srgb.flags);
2712 stream_put3(s, router_cap->srgb.range_size);
2713 stream_putc(s, ISIS_SUBTLV_SID_LABEL);
2714 stream_putc(s, ISIS_SUBTLV_SID_LABEL_SIZE);
2715 stream_put3(s, router_cap->srgb.lower_bound);
2716
f2333421 2717 /* Then SR Algorithm if set as per RFC8667 section #3.2 */
1b3f47d0
OD
2718 for (nb_algo = 0; nb_algo < SR_ALGORITHM_COUNT; nb_algo++)
2719 if (router_cap->algo[nb_algo] == SR_ALGORITHM_UNSET)
2720 break;
2721 if (nb_algo > 0) {
2722 stream_putc(s, ISIS_SUBTLV_ALGORITHM);
2723 stream_putc(s, nb_algo);
2724 for (int i = 0; i < nb_algo; i++)
2725 stream_putc(s, router_cap->algo[i]);
2726 }
d8391312
OD
2727
2728 /* Local Block if defined as per RFC8667 section #3.3 */
2729 if ((router_cap->srlb.range_size != 0)
2730 && (router_cap->srlb.lower_bound != 0)) {
2731 stream_putc(s, ISIS_SUBTLV_SRLB);
2732 stream_putc(s, ISIS_SUBTLV_SID_LABEL_RANGE_SIZE);
2733 /* No Flags are defined for SRLB */
2734 stream_putc(s, 0);
2735 stream_put3(s, router_cap->srlb.range_size);
2736 stream_putc(s, ISIS_SUBTLV_SID_LABEL);
2737 stream_putc(s, ISIS_SUBTLV_SID_LABEL_SIZE);
2738 stream_put3(s, router_cap->srlb.lower_bound);
2739 }
2740
f2333421 2741 /* And finish with MSD if set as per RFC8491 section #2 */
1b3f47d0
OD
2742 if (router_cap->msd != 0) {
2743 stream_putc(s, ISIS_SUBTLV_NODE_MSD);
2744 stream_putc(s, ISIS_SUBTLV_NODE_MSD_SIZE);
2745 stream_putc(s, MSD_TYPE_BASE_MPLS_IMPOSITION);
2746 stream_putc(s, router_cap->msd);
2747 }
2748 }
2749
2750 /* Adjust TLV length which depends on subTLVs presence */
2751 tlv_len = stream_get_endp(s) - len_pos - 1;
2752 stream_putc_at(s, len_pos, tlv_len);
2753
2754 return 0;
2755}
2756
2757static int unpack_tlv_router_cap(enum isis_tlv_context context,
2758 uint8_t tlv_type, uint8_t tlv_len,
2759 struct stream *s, struct sbuf *log,
2760 void *dest, int indent)
2761{
2762 struct isis_tlvs *tlvs = dest;
d8391312 2763 struct isis_router_cap *rcap;
1b3f47d0
OD
2764 uint8_t type;
2765 uint8_t length;
2766 uint8_t subtlv_len;
e075df3a 2767 uint8_t size;
1b3f47d0
OD
2768
2769 sbuf_push(log, indent, "Unpacking Router Capability TLV...\n");
2770 if (tlv_len < ISIS_ROUTER_CAP_SIZE) {
2771 sbuf_push(log, indent, "WARNING: Unexpected TLV size\n");
2772 stream_forward_getp(s, tlv_len);
2773 return 0;
2774 }
2775
2776 if (tlvs->router_cap) {
2777 sbuf_push(log, indent,
2778 "WARNING: Router Capability TLV present multiple times.\n");
2779 stream_forward_getp(s, tlv_len);
2780 return 0;
2781 }
2782
2783 /* Allocate router cap structure and initialize SR Algorithms */
d8391312 2784 rcap = XCALLOC(MTYPE_ISIS_TLV, sizeof(struct isis_router_cap));
1b3f47d0 2785 for (int i = 0; i < SR_ALGORITHM_COUNT; i++)
d8391312 2786 rcap->algo[i] = SR_ALGORITHM_UNSET;
1b3f47d0
OD
2787
2788 /* Get Router ID and Flags */
d8391312
OD
2789 rcap->router_id.s_addr = stream_get_ipv4(s);
2790 rcap->flags = stream_getc(s);
1b3f47d0
OD
2791
2792 /* Parse remaining part of the TLV if present */
2793 subtlv_len = tlv_len - ISIS_ROUTER_CAP_SIZE;
2794 while (subtlv_len > 2) {
1b3f47d0
OD
2795 uint8_t msd_type;
2796
2797 type = stream_getc(s);
2798 length = stream_getc(s);
2799 switch (type) {
2800 case ISIS_SUBTLV_SID_LABEL_RANGE:
e075df3a
OD
2801 /* Check that SRGB is correctly formated */
2802 if (length < SUBTLV_RANGE_LABEL_SIZE
2803 || length > SUBTLV_RANGE_INDEX_SIZE) {
2804 stream_forward_getp(s, length);
2805 continue;
2806 }
d8391312
OD
2807 /* Only one SRGB is supported. Skip subsequent one */
2808 if (rcap->srgb.range_size != 0) {
2809 stream_forward_getp(s, length);
2810 continue;
2811 }
2812 rcap->srgb.flags = stream_getc(s);
2813 rcap->srgb.range_size = stream_get3(s);
1b3f47d0
OD
2814 /* Skip Type and get Length of SID Label */
2815 stream_getc(s);
e075df3a
OD
2816 size = stream_getc(s);
2817 if (size == ISIS_SUBTLV_SID_LABEL_SIZE)
d8391312 2818 rcap->srgb.lower_bound = stream_get3(s);
1b3f47d0 2819 else
d8391312 2820 rcap->srgb.lower_bound = stream_getl(s);
1b3f47d0
OD
2821
2822 /* SRGB sanity checks. */
d8391312
OD
2823 if (rcap->srgb.range_size == 0
2824 || (rcap->srgb.lower_bound <= MPLS_LABEL_RESERVED_MAX)
2825 || ((rcap->srgb.lower_bound + rcap->srgb.range_size - 1)
1b3f47d0
OD
2826 > MPLS_LABEL_UNRESERVED_MAX)) {
2827 sbuf_push(log, indent, "Invalid label range. Reset SRGB\n");
d8391312
OD
2828 rcap->srgb.lower_bound = 0;
2829 rcap->srgb.range_size = 0;
1b3f47d0 2830 }
e075df3a
OD
2831 /* Only one range is supported. Skip subsequent one */
2832 size = length - (size + SUBTLV_SR_BLOCK_SIZE);
2833 if (size > 0)
2834 stream_forward_getp(s, length);
1b3f47d0
OD
2835 break;
2836 case ISIS_SUBTLV_ALGORITHM:
2837 /* Only 2 algorithms are supported: SPF & Strict SPF */
d8391312 2838 stream_get(&rcap->algo, s,
1b3f47d0
OD
2839 length > SR_ALGORITHM_COUNT
2840 ? SR_ALGORITHM_COUNT
2841 : length);
2842 if (length > SR_ALGORITHM_COUNT)
2843 stream_forward_getp(
2844 s, length - SR_ALGORITHM_COUNT);
2845 break;
d8391312 2846 case ISIS_SUBTLV_SRLB:
e075df3a
OD
2847 /* Check that SRLB is correctly formated */
2848 if (length < SUBTLV_RANGE_LABEL_SIZE
2849 || length > SUBTLV_RANGE_INDEX_SIZE) {
2850 stream_forward_getp(s, length);
2851 continue;
2852 }
d8391312
OD
2853 /* RFC 8667 section #3.3: Only one SRLB is authorized */
2854 if (rcap->srlb.range_size != 0) {
2855 stream_forward_getp(s, length);
2856 continue;
2857 }
2858 /* Ignore Flags which are not defined */
2859 stream_getc(s);
2860 rcap->srlb.range_size = stream_get3(s);
2861 /* Skip Type and get Length of SID Label */
2862 stream_getc(s);
e075df3a
OD
2863 size = stream_getc(s);
2864 if (size == ISIS_SUBTLV_SID_LABEL_SIZE)
d8391312
OD
2865 rcap->srlb.lower_bound = stream_get3(s);
2866 else
2867 rcap->srlb.lower_bound = stream_getl(s);
2868
2869 /* SRLB sanity checks. */
2870 if (rcap->srlb.range_size == 0
2871 || (rcap->srlb.lower_bound <= MPLS_LABEL_RESERVED_MAX)
2872 || ((rcap->srlb.lower_bound + rcap->srlb.range_size - 1)
2873 > MPLS_LABEL_UNRESERVED_MAX)) {
2874 sbuf_push(log, indent, "Invalid label range. Reset SRLB\n");
2875 rcap->srlb.lower_bound = 0;
2876 rcap->srlb.range_size = 0;
2877 }
e075df3a
OD
2878 /* Only one range is supported. Skip subsequent one */
2879 size = length - (size + SUBTLV_SR_BLOCK_SIZE);
2880 if (size > 0)
2881 stream_forward_getp(s, length);
d8391312 2882 break;
1b3f47d0 2883 case ISIS_SUBTLV_NODE_MSD:
e075df3a
OD
2884 /* Check that MSD is correctly formated */
2885 if (length < MSD_TLV_SIZE) {
2886 stream_forward_getp(s, length);
2887 continue;
2888 }
1b3f47d0 2889 msd_type = stream_getc(s);
d8391312 2890 rcap->msd = stream_getc(s);
1b3f47d0
OD
2891 /* Only BMI-MSD type has been defined in RFC 8491 */
2892 if (msd_type != MSD_TYPE_BASE_MPLS_IMPOSITION)
d8391312 2893 rcap->msd = 0;
e075df3a
OD
2894 /* Only one MSD is standardized. Skip others */
2895 if (length > MSD_TLV_SIZE)
2896 stream_forward_getp(s, length - MSD_TLV_SIZE);
1b3f47d0
OD
2897 break;
2898 default:
2899 stream_forward_getp(s, length);
2900 break;
2901 }
2902 subtlv_len = subtlv_len - length - 2;
2903 }
d8391312 2904 tlvs->router_cap = rcap;
1b3f47d0
OD
2905 return 0;
2906}
2907
7ef5fefc
CF
2908/* Functions related to TLV 10 Authentication */
2909static struct isis_item *copy_item_auth(struct isis_item *i)
2910{
2911 struct isis_auth *auth = (struct isis_auth *)i;
841791b6 2912 struct isis_auth *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
2913
2914 rv->type = auth->type;
2915 rv->length = auth->length;
2916 memcpy(rv->value, auth->value, sizeof(rv->value));
2917 return (struct isis_item *)rv;
2918}
2919
2920static void format_item_auth(uint16_t mtid, struct isis_item *i,
2921 struct sbuf *buf, int indent)
2922{
2923 struct isis_auth *auth = (struct isis_auth *)i;
2924 char obuf[768];
2925
2926 sbuf_push(buf, indent, "Authentication:\n");
2927 switch (auth->type) {
2928 case ISIS_PASSWD_TYPE_CLEARTXT:
2929 zlog_sanitize(obuf, sizeof(obuf), auth->value, auth->length);
2930 sbuf_push(buf, indent, " Password: %s\n", obuf);
2931 break;
2932 case ISIS_PASSWD_TYPE_HMAC_MD5:
f7813c7c
A
2933 for (unsigned int j = 0; j < 16; j++) {
2934 snprintf(obuf + 2 * j, sizeof(obuf) - 2 * j,
6cde4b45 2935 "%02hhx", auth->value[j]);
7ef5fefc
CF
2936 }
2937 sbuf_push(buf, indent, " HMAC-MD5: %s\n", obuf);
2938 break;
2939 default:
6cde4b45 2940 sbuf_push(buf, indent, " Unknown (%hhu)\n", auth->type);
7ef5fefc 2941 break;
5b94ec50 2942 }
7ef5fefc
CF
2943}
2944
2945static void free_item_auth(struct isis_item *i)
2946{
841791b6 2947 XFREE(MTYPE_ISIS_TLV, i);
7ef5fefc
CF
2948}
2949
7ce31bab
DS
2950static int pack_item_auth(struct isis_item *i, struct stream *s,
2951 size_t *min_len)
7ef5fefc
CF
2952{
2953 struct isis_auth *auth = (struct isis_auth *)i;
2954
7ce31bab
DS
2955 if (STREAM_WRITEABLE(s) < 1) {
2956 *min_len = 1;
7ef5fefc 2957 return 1;
7ce31bab 2958 }
7ef5fefc
CF
2959 stream_putc(s, auth->type);
2960
2961 switch (auth->type) {
2962 case ISIS_PASSWD_TYPE_CLEARTXT:
7ce31bab
DS
2963 if (STREAM_WRITEABLE(s) < auth->length) {
2964 *min_len = 1 + auth->length;
7ef5fefc 2965 return 1;
7ce31bab 2966 }
7ef5fefc
CF
2967 stream_put(s, auth->passwd, auth->length);
2968 break;
2969 case ISIS_PASSWD_TYPE_HMAC_MD5:
7ce31bab
DS
2970 if (STREAM_WRITEABLE(s) < 16) {
2971 *min_len = 1 + 16;
7ef5fefc 2972 return 1;
7ce31bab 2973 }
7ef5fefc
CF
2974 auth->offset = stream_get_endp(s);
2975 stream_put(s, NULL, 16);
2976 break;
2977 default:
2978 return 1;
2979 }
2980
2981 return 0;
2982}
2983
2984static int unpack_item_auth(uint16_t mtid, uint8_t len, struct stream *s,
2985 struct sbuf *log, void *dest, int indent)
2986{
2987 struct isis_tlvs *tlvs = dest;
2988
2989 sbuf_push(log, indent, "Unpack Auth TLV...\n");
2990 if (len < 1) {
2991 sbuf_push(
2992 log, indent,
6cde4b45 2993 "Not enough data left.(Expected 1 bytes of auth type, got %hhu)\n",
7ef5fefc
CF
2994 len);
2995 return 1;
2996 }
2997
841791b6 2998 struct isis_auth *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
2999
3000 rv->type = stream_getc(s);
3001 rv->length = len - 1;
3002
3003 if (rv->type == ISIS_PASSWD_TYPE_HMAC_MD5 && rv->length != 16) {
3004 sbuf_push(
3005 log, indent,
6cde4b45 3006 "Unexpected auth length for HMAC-MD5 (expected 16, got %hhu)\n",
7ef5fefc 3007 rv->length);
841791b6 3008 XFREE(MTYPE_ISIS_TLV, rv);
7ef5fefc
CF
3009 return 1;
3010 }
3011
3012 rv->offset = stream_get_getp(s);
3013 stream_get(rv->value, s, rv->length);
3014 format_item_auth(mtid, (struct isis_item *)rv, log, indent + 2);
3015 append_item(&tlvs->isis_auth, (struct isis_item *)rv);
3016 return 0;
3017}
3018
5f77d901
CF
3019/* Functions related to TLV 13 Purge Originator */
3020
3021static struct isis_purge_originator *copy_tlv_purge_originator(
3022 struct isis_purge_originator *poi)
3023{
3024 if (!poi)
3025 return NULL;
3026
3027 struct isis_purge_originator *rv;
3028
3029 rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
3030 rv->sender_set = poi->sender_set;
3031 memcpy(rv->generator, poi->generator, sizeof(rv->generator));
3032 if (poi->sender_set)
3033 memcpy(rv->sender, poi->sender, sizeof(rv->sender));
3034 return rv;
3035}
3036
3037static void format_tlv_purge_originator(struct isis_purge_originator *poi,
3038 struct sbuf *buf, int indent)
3039{
3040 if (!poi)
3041 return;
3042
3043 sbuf_push(buf, indent, "Purge Originator Identification:\n");
3044 sbuf_push(buf, indent, " Generator: %s\n",
3045 isis_format_id(poi->generator, sizeof(poi->generator)));
3046 if (poi->sender_set) {
3047 sbuf_push(buf, indent, " Received-From: %s\n",
3048 isis_format_id(poi->sender, sizeof(poi->sender)));
3049 }
3050}
3051
3052static void free_tlv_purge_originator(struct isis_purge_originator *poi)
3053{
3054 XFREE(MTYPE_ISIS_TLV, poi);
3055}
3056
3057static int pack_tlv_purge_originator(struct isis_purge_originator *poi,
3058 struct stream *s)
3059{
3060 if (!poi)
3061 return 0;
3062
3063 uint8_t data_len = 1 + sizeof(poi->generator);
3064
3065 if (poi->sender_set)
3066 data_len += sizeof(poi->sender);
3067
3068 if (STREAM_WRITEABLE(s) < (unsigned)(2 + data_len))
3069 return 1;
3070
3071 stream_putc(s, ISIS_TLV_PURGE_ORIGINATOR);
3072 stream_putc(s, data_len);
3073 stream_putc(s, poi->sender_set ? 2 : 1);
3074 stream_put(s, poi->generator, sizeof(poi->generator));
3075 if (poi->sender_set)
3076 stream_put(s, poi->sender, sizeof(poi->sender));
3077 return 0;
3078}
3079
3080static int unpack_tlv_purge_originator(enum isis_tlv_context context,
3081 uint8_t tlv_type, uint8_t tlv_len,
3082 struct stream *s, struct sbuf *log,
3083 void *dest, int indent)
3084{
3085 struct isis_tlvs *tlvs = dest;
3e300703 3086 struct isis_purge_originator poi = {};
5f77d901
CF
3087
3088 sbuf_push(log, indent, "Unpacking Purge Originator Identification TLV...\n");
3089 if (tlv_len < 7) {
6cde4b45 3090 sbuf_push(log, indent, "Not enough data left. (Expected at least 7 bytes, got %hhu)\n", tlv_len);
5f77d901
CF
3091 return 1;
3092 }
3093
3094 uint8_t number_of_ids = stream_getc(s);
3095
3096 if (number_of_ids == 1) {
3097 poi.sender_set = false;
3098 } else if (number_of_ids == 2) {
3099 poi.sender_set = true;
3100 } else {
6cde4b45 3101 sbuf_push(log, indent, "Got invalid value for number of system IDs: %hhu)\n", number_of_ids);
5f77d901
CF
3102 return 1;
3103 }
3104
3105 if (tlv_len != 1 + 6 * number_of_ids) {
3106 sbuf_push(log, indent, "Incorrect tlv len for number of IDs.\n");
3107 return 1;
3108 }
3109
3110 stream_get(poi.generator, s, sizeof(poi.generator));
3111 if (poi.sender_set)
3112 stream_get(poi.sender, s, sizeof(poi.sender));
3113
3114 if (tlvs->purge_originator) {
3115 sbuf_push(log, indent,
3116 "WARNING: Purge originator present multiple times, ignoring.\n");
3117 return 0;
3118 }
3119
3120 tlvs->purge_originator = copy_tlv_purge_originator(&poi);
3121 return 0;
3122}
3123
3124
7ef5fefc
CF
3125/* Functions relating to item TLVs */
3126
3127static void init_item_list(struct isis_item_list *items)
3128{
3129 items->head = NULL;
3130 items->tail = &items->head;
3131 items->count = 0;
3132}
3133
3134static struct isis_item *copy_item(enum isis_tlv_context context,
3135 enum isis_tlv_type type,
3136 struct isis_item *item)
3137{
3138 const struct tlv_ops *ops = tlv_table[context][type];
3139
3140 if (ops && ops->copy_item)
3141 return ops->copy_item(item);
3142
3143 assert(!"Unknown item tlv type!");
3144 return NULL;
3145}
3146
3147static void copy_items(enum isis_tlv_context context, enum isis_tlv_type type,
3148 struct isis_item_list *src, struct isis_item_list *dest)
3149{
3150 struct isis_item *item;
3151
3152 init_item_list(dest);
3153
3154 for (item = src->head; item; item = item->next) {
3155 append_item(dest, copy_item(context, type, item));
3156 }
3157}
3158
3159static void format_item(uint16_t mtid, enum isis_tlv_context context,
3160 enum isis_tlv_type type, struct isis_item *i,
3161 struct sbuf *buf, int indent)
3162{
3163 const struct tlv_ops *ops = tlv_table[context][type];
3164
3165 if (ops && ops->format_item) {
3166 ops->format_item(mtid, i, buf, indent);
3167 return;
3168 }
3169
3170 assert(!"Unknown item tlv type!");
3171}
3172
3173static void format_items_(uint16_t mtid, enum isis_tlv_context context,
3174 enum isis_tlv_type type, struct isis_item_list *items,
3175 struct sbuf *buf, int indent)
3176{
3177 struct isis_item *i;
3178
3179 for (i = items->head; i; i = i->next)
3180 format_item(mtid, context, type, i, buf, indent);
3181}
7ef5fefc
CF
3182
3183static void free_item(enum isis_tlv_context tlv_context,
3184 enum isis_tlv_type tlv_type, struct isis_item *item)
3185{
3186 const struct tlv_ops *ops = tlv_table[tlv_context][tlv_type];
3187
3188 if (ops && ops->free_item) {
3189 ops->free_item(item);
3190 return;
3191 }
3192
3193 assert(!"Unknown item tlv type!");
3194}
3195
3196static void free_items(enum isis_tlv_context context, enum isis_tlv_type type,
3197 struct isis_item_list *items)
3198{
3199 struct isis_item *item, *next_item;
3200
3201 for (item = items->head; item; item = next_item) {
3202 next_item = item->next;
3203 free_item(context, type, item);
3204 }
3205}
3206
3207static int pack_item(enum isis_tlv_context context, enum isis_tlv_type type,
7ce31bab 3208 struct isis_item *i, struct stream *s, size_t *min_len,
7ef5fefc 3209 struct isis_tlvs **fragment_tlvs,
2b64873d 3210 const struct pack_order_entry *pe, uint16_t mtid)
7ef5fefc
CF
3211{
3212 const struct tlv_ops *ops = tlv_table[context][type];
3213
3214 if (ops && ops->pack_item) {
7ce31bab 3215 return ops->pack_item(i, s, min_len);
7ef5fefc
CF
3216 }
3217
3218 assert(!"Unknown item tlv type!");
3219 return 1;
3220}
3221
2b64873d
DL
3222static void add_item_to_fragment(struct isis_item *i,
3223 const struct pack_order_entry *pe,
7ef5fefc
CF
3224 struct isis_tlvs *fragment_tlvs, uint16_t mtid)
3225{
3226 struct isis_item_list *l;
3227
3228 if (pe->how_to_pack == ISIS_ITEMS) {
98c5bc15 3229 l = (struct isis_item_list *)(((char *)fragment_tlvs) + pe->what_to_pack);
7ef5fefc
CF
3230 } else {
3231 struct isis_mt_item_list *m;
98c5bc15 3232 m = (struct isis_mt_item_list *)(((char *)fragment_tlvs) + pe->what_to_pack);
7ef5fefc
CF
3233 l = isis_get_mt_items(m, mtid);
3234 }
3235
3236 append_item(l, copy_item(pe->context, pe->type, i));
3237}
3238
3239static int pack_items_(uint16_t mtid, enum isis_tlv_context context,
3240 enum isis_tlv_type type, struct isis_item_list *items,
3241 struct stream *s, struct isis_tlvs **fragment_tlvs,
2b64873d 3242 const struct pack_order_entry *pe,
7ef5fefc
CF
3243 struct isis_tlvs *(*new_fragment)(struct list *l),
3244 struct list *new_fragment_arg)
3245{
3246 size_t len_pos, last_len, len;
3247 struct isis_item *item = NULL;
3248 int rv;
7ce31bab 3249 size_t min_len = 0;
7ef5fefc
CF
3250
3251 if (!items->head)
3252 return 0;
3253
3254top:
3255 if (STREAM_WRITEABLE(s) < 2)
3256 goto too_long;
3257
3258 stream_putc(s, type);
3259 len_pos = stream_get_endp(s);
3260 stream_putc(s, 0); /* Put 0 as length for now */
3261
3262 if (context == ISIS_CONTEXT_LSP && IS_COMPAT_MT_TLV(type)
3263 && mtid != ISIS_MT_IPV4_UNICAST) {
3264 if (STREAM_WRITEABLE(s) < 2)
3265 goto too_long;
3266 stream_putw(s, mtid);
3267 }
3268
3269 if (context == ISIS_CONTEXT_LSP && type == ISIS_TLV_OLDSTYLE_REACH) {
3270 if (STREAM_WRITEABLE(s) < 1)
3271 goto too_long;
3272 stream_putc(s, 0); /* Virtual flag is set to 0 */
3273 }
3274
3275 last_len = len = 0;
3276 for (item = item ? item : items->head; item; item = item->next) {
7ce31bab
DS
3277 rv = pack_item(context, type, item, s, &min_len, fragment_tlvs,
3278 pe, mtid);
7ef5fefc
CF
3279 if (rv)
3280 goto too_long;
3281
3282 len = stream_get_endp(s) - len_pos - 1;
3283
3284 /* Multiple auths don't go into one TLV, so always break */
3285 if (context == ISIS_CONTEXT_LSP && type == ISIS_TLV_AUTH) {
3286 item = item->next;
3287 break;
3288 }
3289
bd507085
CF
3290 /* Multiple prefix-sids don't go into one TLV, so always break */
3291 if (type == ISIS_SUBTLV_PREFIX_SID
3292 && (context == ISIS_CONTEXT_SUBTLV_IP_REACH
3293 || context == ISIS_CONTEXT_SUBTLV_IPV6_REACH)) {
3294 item = item->next;
3295 break;
3296 }
3297
7ef5fefc
CF
3298 if (len > 255) {
3299 if (!last_len) /* strange, not a single item fit */
3300 return 1;
3301 /* drop last tlv, otherwise, its too long */
3302 stream_set_endp(s, len_pos + 1 + last_len);
3303 len = last_len;
3304 break;
3305 }
3306
3307 if (fragment_tlvs)
3308 add_item_to_fragment(item, pe, *fragment_tlvs, mtid);
3309
3310 last_len = len;
3311 }
3312
3313 stream_putc_at(s, len_pos, len);
3314 if (item)
3315 goto top;
3316
3317 return 0;
3318too_long:
3319 if (!fragment_tlvs)
3320 return 1;
3321 stream_reset(s);
7ce31bab
DS
3322 if (STREAM_WRITEABLE(s) < min_len)
3323 return 1;
7ef5fefc
CF
3324 *fragment_tlvs = new_fragment(new_fragment_arg);
3325 goto top;
3326}
3327#define pack_items(...) pack_items_(ISIS_MT_IPV4_UNICAST, __VA_ARGS__)
3328
3329static void append_item(struct isis_item_list *dest, struct isis_item *item)
3330{
3331 *dest->tail = item;
3332 dest->tail = &(*dest->tail)->next;
3333 dest->count++;
3334}
3335
1b3f47d0
OD
3336static void delete_item(struct isis_item_list *dest, struct isis_item *del)
3337{
3338 struct isis_item *item, *prev = NULL, *next;
3339
3340 /* Sanity Check */
3341 if ((dest == NULL) || (del == NULL))
3342 return;
3343
3344 /*
3345 * TODO: delete is tricky because "dest" is a singly linked list.
3346 * We need to switch a doubly linked list.
3347 */
3348 for (item = dest->head; item; item = next) {
3349 if (item->next == del) {
3350 prev = item;
3351 break;
3352 }
3353 next = item->next;
3354 }
3355 if (prev)
3356 prev->next = del->next;
3357 if (dest->head == del)
3358 dest->head = del->next;
3359 if ((struct isis_item *)dest->tail == del) {
3360 *dest->tail = prev;
3361 if (prev)
3362 dest->tail = &(*dest->tail)->next;
3363 else
3364 dest->tail = &dest->head;
3365 }
3366 dest->count--;
3367}
3368
d43d2df5
CF
3369static struct isis_item *last_item(struct isis_item_list *list)
3370{
3371 return container_of(list->tail, struct isis_item, next);
3372}
3373
7ef5fefc
CF
3374static int unpack_item(uint16_t mtid, enum isis_tlv_context context,
3375 uint8_t tlv_type, uint8_t len, struct stream *s,
3376 struct sbuf *log, void *dest, int indent)
3377{
3378 const struct tlv_ops *ops = tlv_table[context][tlv_type];
3379
3380 if (ops && ops->unpack_item)
3381 return ops->unpack_item(mtid, len, s, log, dest, indent);
3382
3383 assert(!"Unknown item tlv type!");
3384 sbuf_push(log, indent, "Unknown item tlv type!\n");
3385 return 1;
3386}
3387
3388static int unpack_tlv_with_items(enum isis_tlv_context context,
3389 uint8_t tlv_type, uint8_t tlv_len,
3390 struct stream *s, struct sbuf *log, void *dest,
3391 int indent)
3392{
3393 size_t tlv_start;
3394 size_t tlv_pos;
3395 int rv;
3396 uint16_t mtid;
3397
3398 tlv_start = stream_get_getp(s);
3399 tlv_pos = 0;
3400
3401 if (context == ISIS_CONTEXT_LSP && IS_COMPAT_MT_TLV(tlv_type)) {
3402 if (tlv_len < 2) {
3403 sbuf_push(log, indent,
3404 "TLV is too short to contain MTID\n");
3405 return 1;
3406 }
3407 mtid = stream_getw(s) & ISIS_MT_MASK;
3408 tlv_pos += 2;
3409 sbuf_push(log, indent, "Unpacking as MT %s item TLV...\n",
3410 isis_mtid2str(mtid));
3411 } else {
3412 sbuf_push(log, indent, "Unpacking as item TLV...\n");
3413 mtid = ISIS_MT_IPV4_UNICAST;
3414 }
3415
3416 if (context == ISIS_CONTEXT_LSP
3417 && tlv_type == ISIS_TLV_OLDSTYLE_REACH) {
3418 if (tlv_len - tlv_pos < 1) {
3419 sbuf_push(log, indent,
3420 "TLV is too short for old style reach\n");
3421 return 1;
3422 }
3423 stream_forward_getp(s, 1);
3424 tlv_pos += 1;
3425 }
3426
3427 if (context == ISIS_CONTEXT_LSP
3428 && tlv_type == ISIS_TLV_OLDSTYLE_IP_REACH) {
3429 struct isis_tlvs *tlvs = dest;
3430 dest = &tlvs->oldstyle_ip_reach;
3431 } else if (context == ISIS_CONTEXT_LSP
3432 && tlv_type == ISIS_TLV_OLDSTYLE_IP_REACH_EXT) {
3433 struct isis_tlvs *tlvs = dest;
3434 dest = &tlvs->oldstyle_ip_reach_ext;
3435 }
3436
3437 if (context == ISIS_CONTEXT_LSP
3438 && tlv_type == ISIS_TLV_MT_ROUTER_INFO) {
3439 struct isis_tlvs *tlvs = dest;
3440 tlvs->mt_router_info_empty = (tlv_pos >= (size_t)tlv_len);
3441 }
3442
3443 while (tlv_pos < (size_t)tlv_len) {
3444 rv = unpack_item(mtid, context, tlv_type, tlv_len - tlv_pos, s,
3445 log, dest, indent + 2);
3446 if (rv)
3447 return rv;
3448
3449 tlv_pos = stream_get_getp(s) - tlv_start;
3450 }
3451
3452 return 0;
3453}
3454
3455/* Functions to manipulate mt_item_lists */
3456
3457static int isis_mt_item_list_cmp(const struct isis_item_list *a,
3458 const struct isis_item_list *b)
3459{
3460 if (a->mtid < b->mtid)
3461 return -1;
3462 if (a->mtid > b->mtid)
3463 return 1;
3464 return 0;
3465}
3466
3467RB_PROTOTYPE(isis_mt_item_list, isis_item_list, mt_tree, isis_mt_item_list_cmp);
3468RB_GENERATE(isis_mt_item_list, isis_item_list, mt_tree, isis_mt_item_list_cmp);
3469
3470struct isis_item_list *isis_get_mt_items(struct isis_mt_item_list *m,
3471 uint16_t mtid)
3472{
3473 struct isis_item_list *rv;
3474
3475 rv = isis_lookup_mt_items(m, mtid);
3476 if (!rv) {
3477 rv = XCALLOC(MTYPE_ISIS_MT_ITEM_LIST, sizeof(*rv));
3478 init_item_list(rv);
3479 rv->mtid = mtid;
3480 RB_INSERT(isis_mt_item_list, m, rv);
3481 }
3482
3483 return rv;
3484}
3485
3486struct isis_item_list *isis_lookup_mt_items(struct isis_mt_item_list *m,
3487 uint16_t mtid)
3488{
3489 struct isis_item_list key = {.mtid = mtid};
3490
3491 return RB_FIND(isis_mt_item_list, m, &key);
3492}
3493
3494static void free_mt_items(enum isis_tlv_context context,
3495 enum isis_tlv_type type, struct isis_mt_item_list *m)
3496{
3497 struct isis_item_list *n, *nnext;
3498
a2addae8 3499 RB_FOREACH_SAFE (n, isis_mt_item_list, m, nnext) {
7ef5fefc
CF
3500 free_items(context, type, n);
3501 RB_REMOVE(isis_mt_item_list, m, n);
3502 XFREE(MTYPE_ISIS_MT_ITEM_LIST, n);
3503 }
3504}
3505
3506static void format_mt_items(enum isis_tlv_context context,
3507 enum isis_tlv_type type,
3508 struct isis_mt_item_list *m, struct sbuf *buf,
3509 int indent)
3510{
3511 struct isis_item_list *n;
3512
a2addae8 3513 RB_FOREACH (n, isis_mt_item_list, m) {
7ef5fefc
CF
3514 format_items_(n->mtid, context, type, n, buf, indent);
3515 }
3516}
3517
3518static int pack_mt_items(enum isis_tlv_context context, enum isis_tlv_type type,
3519 struct isis_mt_item_list *m, struct stream *s,
3520 struct isis_tlvs **fragment_tlvs,
2b64873d 3521 const struct pack_order_entry *pe,
7ef5fefc
CF
3522 struct isis_tlvs *(*new_fragment)(struct list *l),
3523 struct list *new_fragment_arg)
3524{
3525 struct isis_item_list *n;
3526
a2addae8 3527 RB_FOREACH (n, isis_mt_item_list, m) {
7ef5fefc
CF
3528 int rv;
3529
3530 rv = pack_items_(n->mtid, context, type, n, s, fragment_tlvs,
3531 pe, new_fragment, new_fragment_arg);
3532 if (rv)
3533 return rv;
3534 }
3535
3536 return 0;
3537}
3538
3539static void copy_mt_items(enum isis_tlv_context context,
3540 enum isis_tlv_type type,
3541 struct isis_mt_item_list *src,
3542 struct isis_mt_item_list *dest)
3543{
3544 struct isis_item_list *n;
3545
3546 RB_INIT(isis_mt_item_list, dest);
3547
a2addae8 3548 RB_FOREACH (n, isis_mt_item_list, src) {
7ef5fefc
CF
3549 copy_items(context, type, n, isis_get_mt_items(dest, n->mtid));
3550 }
3551}
3552
3553/* Functions related to tlvs in general */
3554
3555struct isis_tlvs *isis_alloc_tlvs(void)
3556{
3557 struct isis_tlvs *result;
3558
841791b6 3559 result = XCALLOC(MTYPE_ISIS_TLV, sizeof(*result));
7ef5fefc
CF
3560
3561 init_item_list(&result->isis_auth);
3562 init_item_list(&result->area_addresses);
3563 init_item_list(&result->mt_router_info);
3564 init_item_list(&result->oldstyle_reach);
3565 init_item_list(&result->lan_neighbor);
3566 init_item_list(&result->lsp_entries);
3567 init_item_list(&result->extended_reach);
3568 RB_INIT(isis_mt_item_list, &result->mt_reach);
3569 init_item_list(&result->oldstyle_ip_reach);
3570 init_item_list(&result->oldstyle_ip_reach_ext);
3571 init_item_list(&result->ipv4_address);
3572 init_item_list(&result->ipv6_address);
3573 init_item_list(&result->extended_ip_reach);
3574 RB_INIT(isis_mt_item_list, &result->mt_ip_reach);
3575 init_item_list(&result->ipv6_reach);
3576 RB_INIT(isis_mt_item_list, &result->mt_ipv6_reach);
3577
3578 return result;
3579}
3580
3581struct isis_tlvs *isis_copy_tlvs(struct isis_tlvs *tlvs)
3582{
841791b6 3583 struct isis_tlvs *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv));
7ef5fefc
CF
3584
3585 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth,
3586 &rv->isis_auth);
3587
5f77d901
CF
3588 rv->purge_originator =
3589 copy_tlv_purge_originator(tlvs->purge_originator);
3590
7ef5fefc
CF
3591 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES,
3592 &tlvs->area_addresses, &rv->area_addresses);
3593
3594 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO,
3595 &tlvs->mt_router_info, &rv->mt_router_info);
3596
b9d4a380 3597 rv->mt_router_info_empty = tlvs->mt_router_info_empty;
7ef5fefc
CF
3598
3599 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_REACH,
3600 &tlvs->oldstyle_reach, &rv->oldstyle_reach);
3601
3602 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_LAN_NEIGHBORS,
3603 &tlvs->lan_neighbor, &rv->lan_neighbor);
3604
3605 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_LSP_ENTRY, &tlvs->lsp_entries,
3606 &rv->lsp_entries);
3607
3608 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_REACH,
3609 &tlvs->extended_reach, &rv->extended_reach);
3610
3611 copy_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_REACH, &tlvs->mt_reach,
3612 &rv->mt_reach);
3613
3614 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH,
3615 &tlvs->oldstyle_ip_reach, &rv->oldstyle_ip_reach);
3616
3617 copy_tlv_protocols_supported(&tlvs->protocols_supported,
3618 &rv->protocols_supported);
3619
3620 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH_EXT,
3621 &tlvs->oldstyle_ip_reach_ext, &rv->oldstyle_ip_reach_ext);
3622
3623 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV4_ADDRESS, &tlvs->ipv4_address,
3624 &rv->ipv4_address);
3625
3626 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_ADDRESS, &tlvs->ipv6_address,
3627 &rv->ipv6_address);
3628
3629 rv->te_router_id = copy_tlv_te_router_id(tlvs->te_router_id);
3630
3631 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_IP_REACH,
3632 &tlvs->extended_ip_reach, &rv->extended_ip_reach);
3633
3634 copy_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IP_REACH,
3635 &tlvs->mt_ip_reach, &rv->mt_ip_reach);
3636
3637 rv->hostname = copy_tlv_dynamic_hostname(tlvs->hostname);
3638
3639 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_REACH, &tlvs->ipv6_reach,
3640 &rv->ipv6_reach);
3641
3642 copy_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IPV6_REACH,
3643 &tlvs->mt_ipv6_reach, &rv->mt_ipv6_reach);
3644
9fe21208
CF
3645 rv->threeway_adj = copy_tlv_threeway_adj(tlvs->threeway_adj);
3646
1b3f47d0
OD
3647 rv->router_cap = copy_tlv_router_cap(tlvs->router_cap);
3648
41a145f1
CF
3649 rv->spine_leaf = copy_tlv_spine_leaf(tlvs->spine_leaf);
3650
7ef5fefc
CF
3651 return rv;
3652}
3653
3654static void format_tlvs(struct isis_tlvs *tlvs, struct sbuf *buf, int indent)
3655{
3656 format_tlv_protocols_supported(&tlvs->protocols_supported, buf, indent);
3657
3658 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth, buf,
3659 indent);
3660
5f77d901
CF
3661 format_tlv_purge_originator(tlvs->purge_originator, buf, indent);
3662
7ef5fefc
CF
3663 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES,
3664 &tlvs->area_addresses, buf, indent);
3665
3666 if (tlvs->mt_router_info_empty) {
3667 sbuf_push(buf, indent, "MT Router Info: None\n");
3668 } else {
3669 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO,
3670 &tlvs->mt_router_info, buf, indent);
3671 }
3672
3673 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_REACH,
3674 &tlvs->oldstyle_reach, buf, indent);
3675
3676 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_LAN_NEIGHBORS,
3677 &tlvs->lan_neighbor, buf, indent);
3678
3679 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_LSP_ENTRY, &tlvs->lsp_entries,
3680 buf, indent);
3681
3682 format_tlv_dynamic_hostname(tlvs->hostname, buf, indent);
3683 format_tlv_te_router_id(tlvs->te_router_id, buf, indent);
1b3f47d0 3684 format_tlv_router_cap(tlvs->router_cap, buf, indent);
7ef5fefc
CF
3685
3686 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_REACH,
3687 &tlvs->extended_reach, buf, indent);
3688
3689 format_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_REACH, &tlvs->mt_reach,
3690 buf, indent);
3691
3692 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH,
3693 &tlvs->oldstyle_ip_reach, buf, indent);
3694
3695 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH_EXT,
3696 &tlvs->oldstyle_ip_reach_ext, buf, indent);
3697
3698 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV4_ADDRESS,
3699 &tlvs->ipv4_address, buf, indent);
3700
3701 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_ADDRESS,
3702 &tlvs->ipv6_address, buf, indent);
3703
3704 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_IP_REACH,
3705 &tlvs->extended_ip_reach, buf, indent);
3706
3707 format_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IP_REACH,
3708 &tlvs->mt_ip_reach, buf, indent);
3709
3710 format_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_REACH, &tlvs->ipv6_reach,
3711 buf, indent);
3712
3713 format_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IPV6_REACH,
3714 &tlvs->mt_ipv6_reach, buf, indent);
9fe21208
CF
3715
3716 format_tlv_threeway_adj(tlvs->threeway_adj, buf, indent);
41a145f1
CF
3717
3718 format_tlv_spine_leaf(tlvs->spine_leaf, buf, indent);
7ef5fefc
CF
3719}
3720
3721const char *isis_format_tlvs(struct isis_tlvs *tlvs)
3722{
3723 static struct sbuf buf;
3724
3725 if (!sbuf_buf(&buf))
3726 sbuf_init(&buf, NULL, 0);
3727
3728 sbuf_reset(&buf);
3729 format_tlvs(tlvs, &buf, 0);
3730 return sbuf_buf(&buf);
3731}
3732
3733void isis_free_tlvs(struct isis_tlvs *tlvs)
3734{
3735 if (!tlvs)
3736 return;
3737
3738 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth);
5f77d901 3739 free_tlv_purge_originator(tlvs->purge_originator);
7ef5fefc
CF
3740 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES,
3741 &tlvs->area_addresses);
3742 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO,
3743 &tlvs->mt_router_info);
3744 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_REACH,
3745 &tlvs->oldstyle_reach);
3746 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_LAN_NEIGHBORS,
3747 &tlvs->lan_neighbor);
3748 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_LSP_ENTRY, &tlvs->lsp_entries);
3749 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_REACH,
3750 &tlvs->extended_reach);
3751 free_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_REACH, &tlvs->mt_reach);
3752 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH,
3753 &tlvs->oldstyle_ip_reach);
3754 free_tlv_protocols_supported(&tlvs->protocols_supported);
3755 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH_EXT,
3756 &tlvs->oldstyle_ip_reach_ext);
3757 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV4_ADDRESS,
3758 &tlvs->ipv4_address);
3759 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_ADDRESS,
3760 &tlvs->ipv6_address);
3761 free_tlv_te_router_id(tlvs->te_router_id);
3762 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_IP_REACH,
3763 &tlvs->extended_ip_reach);
3764 free_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IP_REACH,
3765 &tlvs->mt_ip_reach);
3766 free_tlv_dynamic_hostname(tlvs->hostname);
3767 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_REACH, &tlvs->ipv6_reach);
3768 free_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IPV6_REACH,
3769 &tlvs->mt_ipv6_reach);
9fe21208 3770 free_tlv_threeway_adj(tlvs->threeway_adj);
1b3f47d0 3771 free_tlv_router_cap(tlvs->router_cap);
41a145f1 3772 free_tlv_spine_leaf(tlvs->spine_leaf);
7ef5fefc 3773
841791b6 3774 XFREE(MTYPE_ISIS_TLV, tlvs);
7ef5fefc
CF
3775}
3776
3777static void add_padding(struct stream *s)
3778{
3779 while (STREAM_WRITEABLE(s)) {
3780 if (STREAM_WRITEABLE(s) == 1)
3781 break;
3782 uint32_t padding_len = STREAM_WRITEABLE(s) - 2;
3783
3784 if (padding_len > 255) {
3785 if (padding_len == 256)
3786 padding_len = 254;
3787 else
3788 padding_len = 255;
3789 }
3790
3791 stream_putc(s, ISIS_TLV_PADDING);
3792 stream_putc(s, padding_len);
3793 stream_put(s, NULL, padding_len);
3794 }
3795}
3796
3797#define LSP_REM_LIFETIME_OFF 10
3798#define LSP_CHECKSUM_OFF 24
3799static void safe_auth_md5(struct stream *s, uint16_t *checksum,
3800 uint16_t *rem_lifetime)
3801{
3802 memcpy(rem_lifetime, STREAM_DATA(s) + LSP_REM_LIFETIME_OFF,
3803 sizeof(*rem_lifetime));
3804 memset(STREAM_DATA(s) + LSP_REM_LIFETIME_OFF, 0, sizeof(*rem_lifetime));
3805 memcpy(checksum, STREAM_DATA(s) + LSP_CHECKSUM_OFF, sizeof(*checksum));
3806 memset(STREAM_DATA(s) + LSP_CHECKSUM_OFF, 0, sizeof(*checksum));
3807}
3808
3809static void restore_auth_md5(struct stream *s, uint16_t checksum,
3810 uint16_t rem_lifetime)
3811{
3812 memcpy(STREAM_DATA(s) + LSP_REM_LIFETIME_OFF, &rem_lifetime,
3813 sizeof(rem_lifetime));
3814 memcpy(STREAM_DATA(s) + LSP_CHECKSUM_OFF, &checksum, sizeof(checksum));
3815}
3816
3817static void update_auth_hmac_md5(struct isis_auth *auth, struct stream *s,
3818 bool is_lsp)
3819{
3820 uint8_t digest[16];
3821 uint16_t checksum, rem_lifetime;
3822
3823 if (is_lsp)
3824 safe_auth_md5(s, &checksum, &rem_lifetime);
3825
3826 memset(STREAM_DATA(s) + auth->offset, 0, 16);
6252100f
MR
3827#ifdef CRYPTO_OPENSSL
3828 uint8_t *result = (uint8_t *)HMAC(EVP_md5(), auth->passwd,
3829 auth->plength, STREAM_DATA(s),
3830 stream_get_endp(s), NULL, NULL);
3831
3832 memcpy(digest, result, 16);
3833#elif CRYPTO_INTERNAL
7ef5fefc
CF
3834 hmac_md5(STREAM_DATA(s), stream_get_endp(s), auth->passwd,
3835 auth->plength, digest);
6252100f 3836#endif
7ef5fefc
CF
3837 memcpy(auth->value, digest, 16);
3838 memcpy(STREAM_DATA(s) + auth->offset, digest, 16);
3839
3840 if (is_lsp)
3841 restore_auth_md5(s, checksum, rem_lifetime);
3842}
3843
3844static void update_auth(struct isis_tlvs *tlvs, struct stream *s, bool is_lsp)
3845{
3846 struct isis_auth *auth_head = (struct isis_auth *)tlvs->isis_auth.head;
3847
3848 for (struct isis_auth *auth = auth_head; auth; auth = auth->next) {
3849 if (auth->type == ISIS_PASSWD_TYPE_HMAC_MD5)
3850 update_auth_hmac_md5(auth, s, is_lsp);
3851 }
3852}
3853
2b64873d 3854static int handle_pack_entry(const struct pack_order_entry *pe,
7ef5fefc
CF
3855 struct isis_tlvs *tlvs, struct stream *stream,
3856 struct isis_tlvs **fragment_tlvs,
3857 struct isis_tlvs *(*new_fragment)(struct list *l),
3858 struct list *new_fragment_arg)
3859{
3860 int rv;
3861
3862 if (pe->how_to_pack == ISIS_ITEMS) {
3863 struct isis_item_list *l;
3864 l = (struct isis_item_list *)(((char *)tlvs)
3865 + pe->what_to_pack);
3866 rv = pack_items(pe->context, pe->type, l, stream, fragment_tlvs,
3867 pe, new_fragment, new_fragment_arg);
3868 } else {
3869 struct isis_mt_item_list *l;
3870 l = (struct isis_mt_item_list *)(((char *)tlvs)
3871 + pe->what_to_pack);
3872 rv = pack_mt_items(pe->context, pe->type, l, stream,
3873 fragment_tlvs, pe, new_fragment,
3874 new_fragment_arg);
3875 }
3876
3877 return rv;
3878}
3879
3880static int pack_tlvs(struct isis_tlvs *tlvs, struct stream *stream,
3881 struct isis_tlvs *fragment_tlvs,
3882 struct isis_tlvs *(*new_fragment)(struct list *l),
3883 struct list *new_fragment_arg)
3884{
3885 int rv;
3886
3887 /* When fragmenting, don't add auth as it's already accounted for in the
3888 * size we are given. */
3889 if (!fragment_tlvs) {
996c9314
LB
3890 rv = pack_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH,
3891 &tlvs->isis_auth, stream, NULL, NULL, NULL,
3892 NULL);
7ef5fefc
CF
3893 if (rv)
3894 return rv;
3895 }
3896
5f77d901
CF
3897 rv = pack_tlv_purge_originator(tlvs->purge_originator, stream);
3898 if (rv)
3899 return rv;
3900 if (fragment_tlvs) {
3901 fragment_tlvs->purge_originator =
3902 copy_tlv_purge_originator(tlvs->purge_originator);
3903 }
3904
7ef5fefc
CF
3905 rv = pack_tlv_protocols_supported(&tlvs->protocols_supported, stream);
3906 if (rv)
3907 return rv;
3908 if (fragment_tlvs) {
3909 copy_tlv_protocols_supported(
3910 &tlvs->protocols_supported,
3911 &fragment_tlvs->protocols_supported);
3912 }
3913
3914 rv = pack_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES,
3915 &tlvs->area_addresses, stream, NULL, NULL, NULL, NULL);
3916 if (rv)
3917 return rv;
3918 if (fragment_tlvs) {
3919 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES,
3920 &tlvs->area_addresses,
3921 &fragment_tlvs->area_addresses);
3922 }
3923
3924
3925 if (tlvs->mt_router_info_empty) {
3926 if (STREAM_WRITEABLE(stream) < 2)
3927 return 1;
3928 stream_putc(stream, ISIS_TLV_MT_ROUTER_INFO);
3929 stream_putc(stream, 0);
3930 if (fragment_tlvs)
3931 fragment_tlvs->mt_router_info_empty = true;
3932 } else {
3933 rv = pack_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO,
3934 &tlvs->mt_router_info, stream, NULL, NULL, NULL,
3935 NULL);
3936 if (rv)
3937 return rv;
3938 if (fragment_tlvs) {
3939 copy_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO,
3940 &tlvs->mt_router_info,
3941 &fragment_tlvs->mt_router_info);
3942 }
3943 }
3944
3945 rv = pack_tlv_dynamic_hostname(tlvs->hostname, stream);
3946 if (rv)
3947 return rv;
3948 if (fragment_tlvs)
3949 fragment_tlvs->hostname =
3950 copy_tlv_dynamic_hostname(tlvs->hostname);
3951
1b3f47d0
OD
3952 rv = pack_tlv_router_cap(tlvs->router_cap, stream);
3953 if (rv)
3954 return rv;
3955 if (fragment_tlvs) {
3956 fragment_tlvs->router_cap =
3957 copy_tlv_router_cap(tlvs->router_cap);
3958 }
3959
7ef5fefc
CF
3960 rv = pack_tlv_te_router_id(tlvs->te_router_id, stream);
3961 if (rv)
3962 return rv;
3963 if (fragment_tlvs) {
3964 fragment_tlvs->te_router_id =
3965 copy_tlv_te_router_id(tlvs->te_router_id);
3966 }
3967
9fe21208
CF
3968 rv = pack_tlv_threeway_adj(tlvs->threeway_adj, stream);
3969 if (rv)
3970 return rv;
3971 if (fragment_tlvs) {
3972 fragment_tlvs->threeway_adj =
3973 copy_tlv_threeway_adj(tlvs->threeway_adj);
3974 }
3975
41a145f1
CF
3976 rv = pack_tlv_spine_leaf(tlvs->spine_leaf, stream);
3977 if (rv)
3978 return rv;
3979 if (fragment_tlvs) {
3980 fragment_tlvs->spine_leaf =
3981 copy_tlv_spine_leaf(tlvs->spine_leaf);
3982 }
3983
7ef5fefc
CF
3984 for (size_t pack_idx = 0; pack_idx < array_size(pack_order);
3985 pack_idx++) {
3986 rv = handle_pack_entry(&pack_order[pack_idx], tlvs, stream,
3987 fragment_tlvs ? &fragment_tlvs : NULL,
3988 new_fragment, new_fragment_arg);
3989
3990 if (rv)
3991 return rv;
3992 }
3993
3994 return 0;
3995}
3996
3997int isis_pack_tlvs(struct isis_tlvs *tlvs, struct stream *stream,
3998 size_t len_pointer, bool pad, bool is_lsp)
3999{
4000 int rv;
4001
4002 rv = pack_tlvs(tlvs, stream, NULL, NULL, NULL);
4003 if (rv)
4004 return rv;
4005
4006 if (pad)
4007 add_padding(stream);
4008
4009 if (len_pointer != (size_t)-1) {
4010 stream_putw_at(stream, len_pointer, stream_get_endp(stream));
4011 }
4012
4013 update_auth(tlvs, stream, is_lsp);
4014
4015 return 0;
4016}
4017
4018static struct isis_tlvs *new_fragment(struct list *l)
4019{
4020 struct isis_tlvs *rv = isis_alloc_tlvs();
4021
4022 listnode_add(l, rv);
4023 return rv;
4024}
4025
4026struct list *isis_fragment_tlvs(struct isis_tlvs *tlvs, size_t size)
4027{
4028 struct stream *dummy_stream = stream_new(size);
4029 struct list *rv = list_new();
4030 struct isis_tlvs *fragment_tlvs = new_fragment(rv);
4031
4032 if (pack_tlvs(tlvs, dummy_stream, fragment_tlvs, new_fragment, rv)) {
4033 struct listnode *node;
4034 for (ALL_LIST_ELEMENTS_RO(rv, node, fragment_tlvs))
4035 isis_free_tlvs(fragment_tlvs);
6a154c88 4036 list_delete(&rv);
7ef5fefc
CF
4037 }
4038
4039 stream_free(dummy_stream);
4040 return rv;
4041}
4042
4043static int unpack_tlv_unknown(enum isis_tlv_context context, uint8_t tlv_type,
4044 uint8_t tlv_len, struct stream *s,
4045 struct sbuf *log, int indent)
4046{
4047 stream_forward_getp(s, tlv_len);
4048 sbuf_push(log, indent,
6cde4b45 4049 "Skipping unknown TLV %hhu (%hhu bytes)\n",
7ef5fefc
CF
4050 tlv_type, tlv_len);
4051 return 0;
4052}
4053
4054static int unpack_tlv(enum isis_tlv_context context, size_t avail_len,
4055 struct stream *stream, struct sbuf *log, void *dest,
bf555bf0 4056 int indent, bool *unpacked_known_tlvs)
7ef5fefc
CF
4057{
4058 uint8_t tlv_type, tlv_len;
4059 const struct tlv_ops *ops;
4060
4061 sbuf_push(log, indent, "Unpacking TLV...\n");
4062
4063 if (avail_len < 2) {
4064 sbuf_push(
4065 log, indent + 2,
4066 "Available data %zu too short to contain a TLV header.\n",
4067 avail_len);
4068 return 1;
4069 }
4070
4071 tlv_type = stream_getc(stream);
4072 tlv_len = stream_getc(stream);
4073
4074 sbuf_push(log, indent + 2,
6cde4b45 4075 "Found TLV of type %hhu and len %hhu.\n",
7ef5fefc
CF
4076 tlv_type, tlv_len);
4077
4078 if (avail_len < ((size_t)tlv_len) + 2) {
98c5bc15 4079 sbuf_push(log, indent + 2,
6cde4b45 4080 "Available data %zu too short for claimed TLV len %hhu.\n",
98c5bc15 4081 avail_len - 2, tlv_len);
7ef5fefc
CF
4082 return 1;
4083 }
4084
4085 ops = tlv_table[context][tlv_type];
4086 if (ops && ops->unpack) {
bf555bf0
CF
4087 if (unpacked_known_tlvs)
4088 *unpacked_known_tlvs = true;
7ef5fefc
CF
4089 return ops->unpack(context, tlv_type, tlv_len, stream, log,
4090 dest, indent + 2);
4091 }
4092
4093 return unpack_tlv_unknown(context, tlv_type, tlv_len, stream, log,
4094 indent + 2);
4095}
4096
4097static int unpack_tlvs(enum isis_tlv_context context, size_t avail_len,
4098 struct stream *stream, struct sbuf *log, void *dest,
bf555bf0 4099 int indent, bool *unpacked_known_tlvs)
7ef5fefc
CF
4100{
4101 int rv;
4102 size_t tlv_start, tlv_pos;
4103
4104 tlv_start = stream_get_getp(stream);
4105 tlv_pos = 0;
4106
4107 sbuf_push(log, indent, "Unpacking %zu bytes of %s...\n", avail_len,
4108 (context == ISIS_CONTEXT_LSP) ? "TLVs" : "sub-TLVs");
4109
4110 while (tlv_pos < avail_len) {
4111 rv = unpack_tlv(context, avail_len - tlv_pos, stream, log, dest,
bf555bf0 4112 indent + 2, unpacked_known_tlvs);
7ef5fefc
CF
4113 if (rv)
4114 return rv;
4115
4116 tlv_pos = stream_get_getp(stream) - tlv_start;
4117 }
4118
4119 return 0;
4120}
4121
4122int isis_unpack_tlvs(size_t avail_len, struct stream *stream,
4123 struct isis_tlvs **dest, const char **log)
4124{
4125 static struct sbuf logbuf;
4126 int indent = 0;
4127 int rv;
4128 struct isis_tlvs *result;
4129
4130 if (!sbuf_buf(&logbuf))
4131 sbuf_init(&logbuf, NULL, 0);
4132
4133 sbuf_reset(&logbuf);
4134 if (avail_len > STREAM_READABLE(stream)) {
4135 sbuf_push(&logbuf, indent,
3efd0893 4136 "Stream doesn't contain sufficient data. Claimed %zu, available %zu\n",
7ef5fefc
CF
4137 avail_len, STREAM_READABLE(stream));
4138 return 1;
4139 }
4140
4141 result = isis_alloc_tlvs();
4142 rv = unpack_tlvs(ISIS_CONTEXT_LSP, avail_len, stream, &logbuf, result,
bf555bf0 4143 indent, NULL);
7ef5fefc
CF
4144
4145 *log = sbuf_buf(&logbuf);
4146 *dest = result;
4147
4148 return rv;
4149}
4150
4151#define TLV_OPS(_name_, _desc_) \
4152 static const struct tlv_ops tlv_##_name_##_ops = { \
4153 .name = _desc_, .unpack = unpack_tlv_##_name_, \
4154 }
4155
4156#define ITEM_TLV_OPS(_name_, _desc_) \
4157 static const struct tlv_ops tlv_##_name_##_ops = { \
4158 .name = _desc_, \
4159 .unpack = unpack_tlv_with_items, \
98c5bc15 4160 \
7ef5fefc
CF
4161 .pack_item = pack_item_##_name_, \
4162 .free_item = free_item_##_name_, \
4163 .unpack_item = unpack_item_##_name_, \
4164 .format_item = format_item_##_name_, \
4165 .copy_item = copy_item_##_name_}
4166
4167#define SUBTLV_OPS(_name_, _desc_) \
4168 static const struct tlv_ops subtlv_##_name_##_ops = { \
4169 .name = _desc_, .unpack = unpack_subtlv_##_name_, \
4170 }
4171
bd507085
CF
4172#define ITEM_SUBTLV_OPS(_name_, _desc_) \
4173 ITEM_TLV_OPS(_name_, _desc_)
4174
7ef5fefc
CF
4175ITEM_TLV_OPS(area_address, "TLV 1 Area Addresses");
4176ITEM_TLV_OPS(oldstyle_reach, "TLV 2 IS Reachability");
4177ITEM_TLV_OPS(lan_neighbor, "TLV 6 LAN Neighbors");
4178ITEM_TLV_OPS(lsp_entry, "TLV 9 LSP Entries");
4179ITEM_TLV_OPS(auth, "TLV 10 IS-IS Auth");
5f77d901 4180TLV_OPS(purge_originator, "TLV 13 Purge Originator Identification");
7ef5fefc
CF
4181ITEM_TLV_OPS(extended_reach, "TLV 22 Extended Reachability");
4182ITEM_TLV_OPS(oldstyle_ip_reach, "TLV 128/130 IP Reachability");
4183TLV_OPS(protocols_supported, "TLV 129 Protocols Supported");
4184ITEM_TLV_OPS(ipv4_address, "TLV 132 IPv4 Interface Address");
4185TLV_OPS(te_router_id, "TLV 134 TE Router ID");
4186ITEM_TLV_OPS(extended_ip_reach, "TLV 135 Extended IP Reachability");
4187TLV_OPS(dynamic_hostname, "TLV 137 Dynamic Hostname");
41a145f1 4188TLV_OPS(spine_leaf, "TLV 150 Spine Leaf Extensions");
7ef5fefc 4189ITEM_TLV_OPS(mt_router_info, "TLV 229 MT Router Information");
9fe21208 4190TLV_OPS(threeway_adj, "TLV 240 P2P Three-Way Adjacency");
7ef5fefc
CF
4191ITEM_TLV_OPS(ipv6_address, "TLV 232 IPv6 Interface Address");
4192ITEM_TLV_OPS(ipv6_reach, "TLV 236 IPv6 Reachability");
1b3f47d0 4193TLV_OPS(router_cap, "TLV 242 Router Capability");
7ef5fefc 4194
bd507085 4195ITEM_SUBTLV_OPS(prefix_sid, "Sub-TLV 3 SR Prefix-SID");
7ef5fefc
CF
4196SUBTLV_OPS(ipv6_source_prefix, "Sub-TLV 22 IPv6 Source Prefix");
4197
2b64873d 4198static const struct tlv_ops *const tlv_table[ISIS_CONTEXT_MAX][ISIS_TLV_MAX] = {
98c5bc15
CF
4199 [ISIS_CONTEXT_LSP] = {
4200 [ISIS_TLV_AREA_ADDRESSES] = &tlv_area_address_ops,
4201 [ISIS_TLV_OLDSTYLE_REACH] = &tlv_oldstyle_reach_ops,
4202 [ISIS_TLV_LAN_NEIGHBORS] = &tlv_lan_neighbor_ops,
4203 [ISIS_TLV_LSP_ENTRY] = &tlv_lsp_entry_ops,
4204 [ISIS_TLV_AUTH] = &tlv_auth_ops,
5f77d901 4205 [ISIS_TLV_PURGE_ORIGINATOR] = &tlv_purge_originator_ops,
98c5bc15 4206 [ISIS_TLV_EXTENDED_REACH] = &tlv_extended_reach_ops,
98c5bc15
CF
4207 [ISIS_TLV_OLDSTYLE_IP_REACH] = &tlv_oldstyle_ip_reach_ops,
4208 [ISIS_TLV_PROTOCOLS_SUPPORTED] = &tlv_protocols_supported_ops,
4209 [ISIS_TLV_OLDSTYLE_IP_REACH_EXT] = &tlv_oldstyle_ip_reach_ops,
4210 [ISIS_TLV_IPV4_ADDRESS] = &tlv_ipv4_address_ops,
4211 [ISIS_TLV_TE_ROUTER_ID] = &tlv_te_router_id_ops,
4212 [ISIS_TLV_EXTENDED_IP_REACH] = &tlv_extended_ip_reach_ops,
98c5bc15 4213 [ISIS_TLV_DYNAMIC_HOSTNAME] = &tlv_dynamic_hostname_ops,
41a145f1 4214 [ISIS_TLV_SPINE_LEAF_EXT] = &tlv_spine_leaf_ops,
1b3f47d0 4215 [ISIS_TLV_MT_REACH] = &tlv_extended_reach_ops,
98c5bc15
CF
4216 [ISIS_TLV_MT_ROUTER_INFO] = &tlv_mt_router_info_ops,
4217 [ISIS_TLV_IPV6_ADDRESS] = &tlv_ipv6_address_ops,
1b3f47d0 4218 [ISIS_TLV_MT_IP_REACH] = &tlv_extended_ip_reach_ops,
98c5bc15
CF
4219 [ISIS_TLV_IPV6_REACH] = &tlv_ipv6_reach_ops,
4220 [ISIS_TLV_MT_IPV6_REACH] = &tlv_ipv6_reach_ops,
1b3f47d0
OD
4221 [ISIS_TLV_THREE_WAY_ADJ] = &tlv_threeway_adj_ops,
4222 [ISIS_TLV_ROUTER_CAPABILITY] = &tlv_router_cap_ops,
98c5bc15 4223 },
3e300703 4224 [ISIS_CONTEXT_SUBTLV_NE_REACH] = {},
bd507085
CF
4225 [ISIS_CONTEXT_SUBTLV_IP_REACH] = {
4226 [ISIS_SUBTLV_PREFIX_SID] = &tlv_prefix_sid_ops,
4227 },
98c5bc15 4228 [ISIS_CONTEXT_SUBTLV_IPV6_REACH] = {
bd507085 4229 [ISIS_SUBTLV_PREFIX_SID] = &tlv_prefix_sid_ops,
98c5bc15
CF
4230 [ISIS_SUBTLV_IPV6_SOURCE_PREFIX] = &subtlv_ipv6_source_prefix_ops,
4231 }
4232};
7ef5fefc
CF
4233
4234/* Accessor functions */
4235
4236void isis_tlvs_add_auth(struct isis_tlvs *tlvs, struct isis_passwd *passwd)
4237{
4238 free_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth);
4239 init_item_list(&tlvs->isis_auth);
4240
4241 if (passwd->type == ISIS_PASSWD_TYPE_UNUSED)
4242 return;
4243
841791b6 4244 struct isis_auth *auth = XCALLOC(MTYPE_ISIS_TLV, sizeof(*auth));
7ef5fefc
CF
4245
4246 auth->type = passwd->type;
4247
4248 auth->plength = passwd->len;
4249 memcpy(auth->passwd, passwd->passwd,
4250 MIN(sizeof(auth->passwd), sizeof(passwd->passwd)));
4251
4252 if (auth->type == ISIS_PASSWD_TYPE_CLEARTXT) {
4253 auth->length = passwd->len;
4254 memcpy(auth->value, passwd->passwd,
4255 MIN(sizeof(auth->value), sizeof(passwd->passwd)));
4256 }
4257
4258 append_item(&tlvs->isis_auth, (struct isis_item *)auth);
4259}
4260
4261void isis_tlvs_add_area_addresses(struct isis_tlvs *tlvs,
4262 struct list *addresses)
4263{
4264 struct listnode *node;
4265 struct area_addr *area_addr;
4266
4267 for (ALL_LIST_ELEMENTS_RO(addresses, node, area_addr)) {
4268 struct isis_area_address *a =
841791b6 4269 XCALLOC(MTYPE_ISIS_TLV, sizeof(*a));
7ef5fefc
CF
4270
4271 a->len = area_addr->addr_len;
4272 memcpy(a->addr, area_addr->area_addr, 20);
4273 append_item(&tlvs->area_addresses, (struct isis_item *)a);
4274 }
4275}
4276
4277void isis_tlvs_add_lan_neighbors(struct isis_tlvs *tlvs, struct list *neighbors)
4278{
4279 struct listnode *node;
d7c0a89a 4280 uint8_t *snpa;
7ef5fefc
CF
4281
4282 for (ALL_LIST_ELEMENTS_RO(neighbors, node, snpa)) {
4283 struct isis_lan_neighbor *n =
841791b6 4284 XCALLOC(MTYPE_ISIS_TLV, sizeof(*n));
7ef5fefc
CF
4285
4286 memcpy(n->mac, snpa, 6);
4287 append_item(&tlvs->lan_neighbor, (struct isis_item *)n);
4288 }
4289}
4290
4291void isis_tlvs_set_protocols_supported(struct isis_tlvs *tlvs,
4292 struct nlpids *nlpids)
4293{
4294 tlvs->protocols_supported.count = nlpids->count;
0a22ddfb 4295 XFREE(MTYPE_ISIS_TLV, tlvs->protocols_supported.protocols);
7ef5fefc
CF
4296 if (nlpids->count) {
4297 tlvs->protocols_supported.protocols =
841791b6 4298 XCALLOC(MTYPE_ISIS_TLV, nlpids->count);
7ef5fefc
CF
4299 memcpy(tlvs->protocols_supported.protocols, nlpids->nlpids,
4300 nlpids->count);
4301 } else {
4302 tlvs->protocols_supported.protocols = NULL;
4303 }
4304}
4305
4306void isis_tlvs_add_mt_router_info(struct isis_tlvs *tlvs, uint16_t mtid,
4307 bool overload, bool attached)
4308{
841791b6 4309 struct isis_mt_router_info *i = XCALLOC(MTYPE_ISIS_TLV, sizeof(*i));
7ef5fefc
CF
4310
4311 i->overload = overload;
4312 i->attached = attached;
4313 i->mtid = mtid;
4314 append_item(&tlvs->mt_router_info, (struct isis_item *)i);
4315}
4316
4317void isis_tlvs_add_ipv4_address(struct isis_tlvs *tlvs, struct in_addr *addr)
4318{
841791b6 4319 struct isis_ipv4_address *a = XCALLOC(MTYPE_ISIS_TLV, sizeof(*a));
7ef5fefc
CF
4320 a->addr = *addr;
4321 append_item(&tlvs->ipv4_address, (struct isis_item *)a);
4322}
4323
4324
4325void isis_tlvs_add_ipv4_addresses(struct isis_tlvs *tlvs,
4326 struct list *addresses)
4327{
4328 struct listnode *node;
4329 struct prefix_ipv4 *ip_addr;
bb5c77d7 4330 unsigned int addr_count = 0;
7ef5fefc 4331
bb5c77d7 4332 for (ALL_LIST_ELEMENTS_RO(addresses, node, ip_addr)) {
7ef5fefc 4333 isis_tlvs_add_ipv4_address(tlvs, &ip_addr->prefix);
bb5c77d7
CF
4334 addr_count++;
4335 if (addr_count >= 63)
4336 break;
4337 }
7ef5fefc
CF
4338}
4339
4340void isis_tlvs_add_ipv6_addresses(struct isis_tlvs *tlvs,
4341 struct list *addresses)
4342{
4343 struct listnode *node;
4344 struct prefix_ipv6 *ip_addr;
1f8286c9 4345 unsigned int addr_count = 0;
7ef5fefc
CF
4346
4347 for (ALL_LIST_ELEMENTS_RO(addresses, node, ip_addr)) {
1f8286c9
DS
4348 if (addr_count >= 15)
4349 break;
4350
7ef5fefc 4351 struct isis_ipv6_address *a =
841791b6 4352 XCALLOC(MTYPE_ISIS_TLV, sizeof(*a));
7ef5fefc
CF
4353
4354 a->addr = ip_addr->prefix;
4355 append_item(&tlvs->ipv6_address, (struct isis_item *)a);
1f8286c9 4356 addr_count++;
7ef5fefc
CF
4357 }
4358}
4359
4360typedef bool (*auth_validator_func)(struct isis_passwd *passwd,
4361 struct stream *stream,
4362 struct isis_auth *auth, bool is_lsp);
4363
4364static bool auth_validator_cleartxt(struct isis_passwd *passwd,
4365 struct stream *stream,
4366 struct isis_auth *auth, bool is_lsp)
4367{
4368 return (auth->length == passwd->len
4369 && !memcmp(auth->value, passwd->passwd, passwd->len));
4370}
4371
4372static bool auth_validator_hmac_md5(struct isis_passwd *passwd,
4373 struct stream *stream,
4374 struct isis_auth *auth, bool is_lsp)
4375{
4376 uint8_t digest[16];
4377 uint16_t checksum;
4378 uint16_t rem_lifetime;
4379
4380 if (is_lsp)
4381 safe_auth_md5(stream, &checksum, &rem_lifetime);
4382
4383 memset(STREAM_DATA(stream) + auth->offset, 0, 16);
6252100f
MR
4384#ifdef CRYPTO_OPENSSL
4385 uint8_t *result = (uint8_t *)HMAC(EVP_md5(), passwd->passwd,
4386 passwd->len, STREAM_DATA(stream),
4387 stream_get_endp(stream), NULL, NULL);
4388
4389 memcpy(digest, result, 16);
4390#elif CRYPTO_INTERNAL
7ef5fefc
CF
4391 hmac_md5(STREAM_DATA(stream), stream_get_endp(stream), passwd->passwd,
4392 passwd->len, digest);
6252100f 4393#endif
7ef5fefc
CF
4394 memcpy(STREAM_DATA(stream) + auth->offset, auth->value, 16);
4395
4396 bool rv = !memcmp(digest, auth->value, 16);
4397
4398 if (is_lsp)
4399 restore_auth_md5(stream, checksum, rem_lifetime);
4400
4401 return rv;
4402}
4403
4404static const auth_validator_func auth_validators[] = {
4405 [ISIS_PASSWD_TYPE_CLEARTXT] = auth_validator_cleartxt,
4406 [ISIS_PASSWD_TYPE_HMAC_MD5] = auth_validator_hmac_md5,
4407};
4408
3380c990
EDP
4409int isis_tlvs_auth_is_valid(struct isis_tlvs *tlvs, struct isis_passwd *passwd,
4410 struct stream *stream, bool is_lsp)
7ef5fefc
CF
4411{
4412 /* If no auth is set, always pass authentication */
4413 if (!passwd->type)
3380c990 4414 return ISIS_AUTH_OK;
7ef5fefc
CF
4415
4416 /* If we don't known how to validate the auth, return invalid */
4417 if (passwd->type >= array_size(auth_validators)
4418 || !auth_validators[passwd->type])
3380c990 4419 return ISIS_AUTH_NO_VALIDATOR;
7ef5fefc
CF
4420
4421 struct isis_auth *auth_head = (struct isis_auth *)tlvs->isis_auth.head;
4422 struct isis_auth *auth;
4423 for (auth = auth_head; auth; auth = auth->next) {
4424 if (auth->type == passwd->type)
4425 break;
4426 }
4427
4428 /* If matching auth TLV could not be found, return invalid */
4429 if (!auth)
3380c990
EDP
4430 return ISIS_AUTH_TYPE_FAILURE;
4431
7ef5fefc
CF
4432
4433 /* Perform validation and return result */
3380c990
EDP
4434 if (auth_validators[passwd->type](passwd, stream, auth, is_lsp))
4435 return ISIS_AUTH_OK;
4436 else
4437 return ISIS_AUTH_FAILURE;
7ef5fefc
CF
4438}
4439
4440bool isis_tlvs_area_addresses_match(struct isis_tlvs *tlvs,
4441 struct list *addresses)
4442{
4443 struct isis_area_address *addr_head;
4444
4445 addr_head = (struct isis_area_address *)tlvs->area_addresses.head;
4446 for (struct isis_area_address *addr = addr_head; addr;
4447 addr = addr->next) {
4448 struct listnode *node;
4449 struct area_addr *a;
4450
4451 for (ALL_LIST_ELEMENTS_RO(addresses, node, a)) {
4452 if (a->addr_len == addr->len
4453 && !memcmp(a->area_addr, addr->addr, addr->len))
4454 return true;
4455 }
4456 }
4457
4458 return false;
4459}
4460
4461static void tlvs_area_addresses_to_adj(struct isis_tlvs *tlvs,
4462 struct isis_adjacency *adj,
4463 bool *changed)
4464{
4465 if (adj->area_address_count != tlvs->area_addresses.count) {
033c6d28
DS
4466 uint32_t oc = adj->area_address_count;
4467
7ef5fefc
CF
4468 *changed = true;
4469 adj->area_address_count = tlvs->area_addresses.count;
4470 adj->area_addresses = XREALLOC(
4471 MTYPE_ISIS_ADJACENCY_INFO, adj->area_addresses,
4472 adj->area_address_count * sizeof(*adj->area_addresses));
033c6d28
DS
4473
4474 for (; oc < adj->area_address_count; oc++) {
4475 adj->area_addresses[oc].addr_len = 0;
4476 memset(&adj->area_addresses[oc].area_addr, 0,
4477 sizeof(adj->area_addresses[oc].area_addr));
4478 }
7ef5fefc
CF
4479 }
4480
4481 struct isis_area_address *addr = NULL;
4482 for (unsigned int i = 0; i < tlvs->area_addresses.count; i++) {
4483 if (!addr)
4484 addr = (struct isis_area_address *)
4485 tlvs->area_addresses.head;
4486 else
4487 addr = addr->next;
4488
4489 if (adj->area_addresses[i].addr_len == addr->len
4490 && !memcmp(adj->area_addresses[i].area_addr, addr->addr,
4491 addr->len)) {
4492 continue;
4493 }
4494
4495 *changed = true;
4496 adj->area_addresses[i].addr_len = addr->len;
4497 memcpy(adj->area_addresses[i].area_addr, addr->addr, addr->len);
4498 }
4499}
4500
4501static void tlvs_protocols_supported_to_adj(struct isis_tlvs *tlvs,
4502 struct isis_adjacency *adj,
4503 bool *changed)
4504{
4505 bool ipv4_supported = false, ipv6_supported = false;
4506
4507 for (uint8_t i = 0; i < tlvs->protocols_supported.count; i++) {
4508 if (tlvs->protocols_supported.protocols[i] == NLPID_IP)
4509 ipv4_supported = true;
4510 if (tlvs->protocols_supported.protocols[i] == NLPID_IPV6)
4511 ipv6_supported = true;
4512 }
4513
3e300703 4514 struct nlpids reduced = {};
7ef5fefc
CF
4515
4516 if (ipv4_supported && ipv6_supported) {
4517 reduced.count = 2;
4518 reduced.nlpids[0] = NLPID_IP;
4519 reduced.nlpids[1] = NLPID_IPV6;
4520 } else if (ipv4_supported) {
4521 reduced.count = 1;
4522 reduced.nlpids[0] = NLPID_IP;
4523 } else if (ipv6_supported) {
4524 reduced.count = 1;
4773e4f8 4525 reduced.nlpids[0] = NLPID_IPV6;
7ef5fefc
CF
4526 } else {
4527 reduced.count = 0;
4528 }
4529
4530 if (adj->nlpids.count == reduced.count
4531 && !memcmp(adj->nlpids.nlpids, reduced.nlpids, reduced.count))
4532 return;
4533
4534 *changed = true;
4535 adj->nlpids.count = reduced.count;
4536 memcpy(adj->nlpids.nlpids, reduced.nlpids, reduced.count);
4537}
4538
30563683 4539DEFINE_HOOK(isis_adj_ip_enabled_hook, (struct isis_adjacency *adj, int family),
8451921b 4540 (adj, family));
30563683 4541DEFINE_HOOK(isis_adj_ip_disabled_hook,
8451921b 4542 (struct isis_adjacency *adj, int family), (adj, family));
30563683 4543
7ef5fefc
CF
4544static void tlvs_ipv4_addresses_to_adj(struct isis_tlvs *tlvs,
4545 struct isis_adjacency *adj,
4546 bool *changed)
4547{
30563683
RW
4548 bool ipv4_enabled = false;
4549
4550 if (adj->ipv4_address_count == 0 && tlvs->ipv4_address.count > 0)
4551 ipv4_enabled = true;
4552 else if (adj->ipv4_address_count > 0 && tlvs->ipv4_address.count == 0)
4553 hook_call(isis_adj_ip_disabled_hook, adj, AF_INET);
4554
7ef5fefc 4555 if (adj->ipv4_address_count != tlvs->ipv4_address.count) {
033c6d28
DS
4556 uint32_t oc = adj->ipv4_address_count;
4557
7ef5fefc
CF
4558 *changed = true;
4559 adj->ipv4_address_count = tlvs->ipv4_address.count;
4560 adj->ipv4_addresses = XREALLOC(
4561 MTYPE_ISIS_ADJACENCY_INFO, adj->ipv4_addresses,
4562 adj->ipv4_address_count * sizeof(*adj->ipv4_addresses));
033c6d28
DS
4563
4564 for (; oc < adj->ipv4_address_count; oc++) {
4565 memset(&adj->ipv4_addresses[oc], 0,
4566 sizeof(adj->ipv4_addresses[oc]));
4567 }
7ef5fefc
CF
4568 }
4569
4570 struct isis_ipv4_address *addr = NULL;
4571 for (unsigned int i = 0; i < tlvs->ipv4_address.count; i++) {
4572 if (!addr)
4573 addr = (struct isis_ipv4_address *)
4574 tlvs->ipv4_address.head;
4575 else
4576 addr = addr->next;
4577
4578 if (!memcmp(&adj->ipv4_addresses[i], &addr->addr,
4579 sizeof(addr->addr)))
4580 continue;
4581
4582 *changed = true;
4583 adj->ipv4_addresses[i] = addr->addr;
4584 }
30563683
RW
4585
4586 if (ipv4_enabled)
4587 hook_call(isis_adj_ip_enabled_hook, adj, AF_INET);
7ef5fefc
CF
4588}
4589
4590static void tlvs_ipv6_addresses_to_adj(struct isis_tlvs *tlvs,
4591 struct isis_adjacency *adj,
4592 bool *changed)
4593{
30563683
RW
4594 bool ipv6_enabled = false;
4595
4596 if (adj->ipv6_address_count == 0 && tlvs->ipv6_address.count > 0)
4597 ipv6_enabled = true;
4598 else if (adj->ipv6_address_count > 0 && tlvs->ipv6_address.count == 0)
4599 hook_call(isis_adj_ip_disabled_hook, adj, AF_INET6);
4600
7ef5fefc 4601 if (adj->ipv6_address_count != tlvs->ipv6_address.count) {
033c6d28
DS
4602 uint32_t oc = adj->ipv6_address_count;
4603
7ef5fefc
CF
4604 *changed = true;
4605 adj->ipv6_address_count = tlvs->ipv6_address.count;
4606 adj->ipv6_addresses = XREALLOC(
4607 MTYPE_ISIS_ADJACENCY_INFO, adj->ipv6_addresses,
4608 adj->ipv6_address_count * sizeof(*adj->ipv6_addresses));
033c6d28
DS
4609
4610 for (; oc < adj->ipv6_address_count; oc++) {
4611 memset(&adj->ipv6_addresses[oc], 0,
4612 sizeof(adj->ipv6_addresses[oc]));
4613 }
7ef5fefc
CF
4614 }
4615
4616 struct isis_ipv6_address *addr = NULL;
4617 for (unsigned int i = 0; i < tlvs->ipv6_address.count; i++) {
4618 if (!addr)
4619 addr = (struct isis_ipv6_address *)
4620 tlvs->ipv6_address.head;
4621 else
4622 addr = addr->next;
4623
4624 if (!memcmp(&adj->ipv6_addresses[i], &addr->addr,
4625 sizeof(addr->addr)))
4626 continue;
4627
4628 *changed = true;
4629 adj->ipv6_addresses[i] = addr->addr;
4630 }
30563683
RW
4631
4632 if (ipv6_enabled)
4633 hook_call(isis_adj_ip_enabled_hook, adj, AF_INET6);
7ef5fefc
CF
4634}
4635
4636void isis_tlvs_to_adj(struct isis_tlvs *tlvs, struct isis_adjacency *adj,
4637 bool *changed)
4638{
4639 *changed = false;
4640
4641 tlvs_area_addresses_to_adj(tlvs, adj, changed);
4642 tlvs_protocols_supported_to_adj(tlvs, adj, changed);
4643 tlvs_ipv4_addresses_to_adj(tlvs, adj, changed);
4644 tlvs_ipv6_addresses_to_adj(tlvs, adj, changed);
4645}
4646
4647bool isis_tlvs_own_snpa_found(struct isis_tlvs *tlvs, uint8_t *snpa)
4648{
4649 struct isis_lan_neighbor *ne_head;
4650
4651 ne_head = (struct isis_lan_neighbor *)tlvs->lan_neighbor.head;
4652 for (struct isis_lan_neighbor *ne = ne_head; ne; ne = ne->next) {
4653 if (!memcmp(ne->mac, snpa, ETH_ALEN))
4654 return true;
4655 }
4656
4657 return false;
4658}
4659
4660void isis_tlvs_add_lsp_entry(struct isis_tlvs *tlvs, struct isis_lsp *lsp)
4661{
841791b6 4662 struct isis_lsp_entry *entry = XCALLOC(MTYPE_ISIS_TLV, sizeof(*entry));
7ef5fefc
CF
4663
4664 entry->rem_lifetime = lsp->hdr.rem_lifetime;
4665 memcpy(entry->id, lsp->hdr.lsp_id, ISIS_SYS_ID_LEN + 2);
4666 entry->checksum = lsp->hdr.checksum;
4667 entry->seqno = lsp->hdr.seqno;
4668 entry->lsp = lsp;
4669
4670 append_item(&tlvs->lsp_entries, (struct isis_item *)entry);
4671}
4672
4673void isis_tlvs_add_csnp_entries(struct isis_tlvs *tlvs, uint8_t *start_id,
4674 uint8_t *stop_id, uint16_t num_lsps,
4bef0ec4
DL
4675 struct lspdb_head *head,
4676 struct isis_lsp **last_lsp)
7ef5fefc 4677{
4bef0ec4
DL
4678 struct isis_lsp searchfor;
4679 struct isis_lsp *first, *lsp;
4680
4681 memcpy(&searchfor.hdr.lsp_id, start_id, sizeof(searchfor.hdr.lsp_id));
4682 first = lspdb_find_gteq(head, &searchfor);
7ef5fefc
CF
4683 if (!first)
4684 return;
4685
81fddbe7 4686 frr_each_from (lspdb, head, lsp, first) {
4bef0ec4
DL
4687 if (memcmp(lsp->hdr.lsp_id, stop_id, sizeof(lsp->hdr.lsp_id))
4688 > 0 || tlvs->lsp_entries.count == num_lsps)
7ef5fefc 4689 break;
4bef0ec4
DL
4690
4691 isis_tlvs_add_lsp_entry(tlvs, lsp);
4692 *last_lsp = lsp;
7ef5fefc
CF
4693 }
4694}
4695
4696void isis_tlvs_set_dynamic_hostname(struct isis_tlvs *tlvs,
4697 const char *hostname)
4698{
841791b6 4699 XFREE(MTYPE_ISIS_TLV, tlvs->hostname);
7ef5fefc 4700 if (hostname)
841791b6 4701 tlvs->hostname = XSTRDUP(MTYPE_ISIS_TLV, hostname);
7ef5fefc
CF
4702}
4703
1b3f47d0
OD
4704/* Set Router Capability TLV parameters */
4705void isis_tlvs_set_router_capability(struct isis_tlvs *tlvs,
4706 const struct isis_router_cap *cap)
4707{
4708 XFREE(MTYPE_ISIS_TLV, tlvs->router_cap);
4709 if (!cap)
4710 return;
4711
4712 tlvs->router_cap = XCALLOC(MTYPE_ISIS_TLV, sizeof(*tlvs->router_cap));
4713 *tlvs->router_cap = *cap;
4714}
4715
7ef5fefc
CF
4716void isis_tlvs_set_te_router_id(struct isis_tlvs *tlvs,
4717 const struct in_addr *id)
4718{
841791b6 4719 XFREE(MTYPE_ISIS_TLV, tlvs->te_router_id);
7ef5fefc
CF
4720 if (!id)
4721 return;
841791b6 4722 tlvs->te_router_id = XCALLOC(MTYPE_ISIS_TLV, sizeof(*id));
7ef5fefc
CF
4723 memcpy(tlvs->te_router_id, id, sizeof(*id));
4724}
4725
4726void isis_tlvs_add_oldstyle_ip_reach(struct isis_tlvs *tlvs,
4727 struct prefix_ipv4 *dest, uint8_t metric)
4728{
841791b6 4729 struct isis_oldstyle_ip_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
7ef5fefc
CF
4730
4731 r->metric = metric;
4732 memcpy(&r->prefix, dest, sizeof(*dest));
4733 apply_mask_ipv4(&r->prefix);
4734 append_item(&tlvs->oldstyle_ip_reach, (struct isis_item *)r);
4735}
4736
f2333421 4737/* Add IS-IS SR Adjacency-SID subTLVs */
1b3f47d0
OD
4738void isis_tlvs_add_adj_sid(struct isis_ext_subtlvs *exts,
4739 struct isis_adj_sid *adj)
4740{
4741 append_item(&exts->adj_sid, (struct isis_item *)adj);
4742 SET_SUBTLV(exts, EXT_ADJ_SID);
4743}
4744
f2333421 4745/* Delete IS-IS SR Adjacency-SID subTLVs */
1b3f47d0
OD
4746void isis_tlvs_del_adj_sid(struct isis_ext_subtlvs *exts,
4747 struct isis_adj_sid *adj)
4748{
4749 delete_item(&exts->adj_sid, (struct isis_item *)adj);
4750 XFREE(MTYPE_ISIS_SUBTLV, adj);
4751 if (exts->adj_sid.count == 0)
4752 UNSET_SUBTLV(exts, EXT_ADJ_SID);
4753}
4754
f2333421 4755/* Add IS-IS SR LAN-Adjacency-SID subTLVs */
1b3f47d0
OD
4756void isis_tlvs_add_lan_adj_sid(struct isis_ext_subtlvs *exts,
4757 struct isis_lan_adj_sid *lan)
4758{
4759 append_item(&exts->lan_sid, (struct isis_item *)lan);
4760 SET_SUBTLV(exts, EXT_LAN_ADJ_SID);
4761}
4762
f2333421 4763/* Delete IS-IS SR LAN-Adjacency-SID subTLVs */
1b3f47d0
OD
4764void isis_tlvs_del_lan_adj_sid(struct isis_ext_subtlvs *exts,
4765 struct isis_lan_adj_sid *lan)
4766{
4767 delete_item(&exts->lan_sid, (struct isis_item *)lan);
4768 XFREE(MTYPE_ISIS_SUBTLV, lan);
4769 if (exts->lan_sid.count == 0)
4770 UNSET_SUBTLV(exts, EXT_LAN_ADJ_SID);
4771}
4772
7ef5fefc 4773void isis_tlvs_add_extended_ip_reach(struct isis_tlvs *tlvs,
26f6acaf
RW
4774 struct prefix_ipv4 *dest, uint32_t metric,
4775 bool external, struct sr_prefix_cfg *pcfg)
7ef5fefc 4776{
841791b6 4777 struct isis_extended_ip_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
7ef5fefc
CF
4778
4779 r->metric = metric;
4780 memcpy(&r->prefix, dest, sizeof(*dest));
4781 apply_mask_ipv4(&r->prefix);
26f6acaf
RW
4782 if (pcfg) {
4783 struct isis_prefix_sid *psid =
4784 XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*psid));
4785
4786 isis_sr_prefix_cfg2subtlv(pcfg, external, psid);
4787 r->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IP_REACH);
4788 append_item(&r->subtlvs->prefix_sids, (struct isis_item *)psid);
4789 }
7ef5fefc
CF
4790 append_item(&tlvs->extended_ip_reach, (struct isis_item *)r);
4791}
4792
4793void isis_tlvs_add_ipv6_reach(struct isis_tlvs *tlvs, uint16_t mtid,
26f6acaf
RW
4794 struct prefix_ipv6 *dest, uint32_t metric,
4795 bool external, struct sr_prefix_cfg *pcfg)
7ef5fefc 4796{
841791b6 4797 struct isis_ipv6_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
7ef5fefc
CF
4798
4799 r->metric = metric;
4800 memcpy(&r->prefix, dest, sizeof(*dest));
4801 apply_mask_ipv6(&r->prefix);
26f6acaf
RW
4802 if (pcfg) {
4803 struct isis_prefix_sid *psid =
4804 XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*psid));
4805
4806 isis_sr_prefix_cfg2subtlv(pcfg, external, psid);
4807 r->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IP_REACH);
4808 append_item(&r->subtlvs->prefix_sids, (struct isis_item *)psid);
4809 }
7ef5fefc
CF
4810
4811 struct isis_item_list *l;
4812 l = (mtid == ISIS_MT_IPV4_UNICAST)
4813 ? &tlvs->ipv6_reach
4814 : isis_get_mt_items(&tlvs->mt_ipv6_reach, mtid);
4815 append_item(l, (struct isis_item *)r);
4816}
4817
d43d2df5
CF
4818void isis_tlvs_add_ipv6_dstsrc_reach(struct isis_tlvs *tlvs, uint16_t mtid,
4819 struct prefix_ipv6 *dest,
4820 struct prefix_ipv6 *src,
4821 uint32_t metric)
4822{
26f6acaf 4823 isis_tlvs_add_ipv6_reach(tlvs, mtid, dest, metric, false, NULL);
d43d2df5
CF
4824 struct isis_item_list *l = isis_get_mt_items(&tlvs->mt_ipv6_reach,
4825 mtid);
4826
4827 struct isis_ipv6_reach *r = (struct isis_ipv6_reach*)last_item(l);
bd507085 4828 r->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IPV6_REACH);
d43d2df5
CF
4829 r->subtlvs->source_prefix = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*src));
4830 memcpy(r->subtlvs->source_prefix, src, sizeof(*src));
4831}
4832
7ef5fefc
CF
4833void isis_tlvs_add_oldstyle_reach(struct isis_tlvs *tlvs, uint8_t *id,
4834 uint8_t metric)
4835{
841791b6 4836 struct isis_oldstyle_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
7ef5fefc
CF
4837
4838 r->metric = metric;
4839 memcpy(r->id, id, sizeof(r->id));
4840 append_item(&tlvs->oldstyle_reach, (struct isis_item *)r);
4841}
4842
4843void isis_tlvs_add_extended_reach(struct isis_tlvs *tlvs, uint16_t mtid,
4844 uint8_t *id, uint32_t metric,
1b3f47d0 4845 struct isis_ext_subtlvs *exts)
7ef5fefc 4846{
841791b6 4847 struct isis_extended_reach *r = XCALLOC(MTYPE_ISIS_TLV, sizeof(*r));
7ef5fefc
CF
4848
4849 memcpy(r->id, id, sizeof(r->id));
4850 r->metric = metric;
1b3f47d0
OD
4851 if (exts)
4852 r->subtlvs = copy_item_ext_subtlvs(exts, mtid);
7ef5fefc
CF
4853
4854 struct isis_item_list *l;
4855 if (mtid == ISIS_MT_IPV4_UNICAST)
4856 l = &tlvs->extended_reach;
4857 else
4858 l = isis_get_mt_items(&tlvs->mt_reach, mtid);
4859 append_item(l, (struct isis_item *)r);
4860}
4861
9fe21208
CF
4862void isis_tlvs_add_threeway_adj(struct isis_tlvs *tlvs,
4863 enum isis_threeway_state state,
4864 uint32_t local_circuit_id,
4865 const uint8_t *neighbor_id,
4866 uint32_t neighbor_circuit_id)
4867{
4868 assert(!tlvs->threeway_adj);
4869
4870 tlvs->threeway_adj = XCALLOC(MTYPE_ISIS_TLV, sizeof(*tlvs->threeway_adj));
4871 tlvs->threeway_adj->state = state;
4872 tlvs->threeway_adj->local_circuit_id = local_circuit_id;
4873
4874 if (neighbor_id) {
4875 tlvs->threeway_adj->neighbor_set = true;
4876 memcpy(tlvs->threeway_adj->neighbor_id, neighbor_id, 6);
4877 tlvs->threeway_adj->neighbor_circuit_id = neighbor_circuit_id;
4878 }
4879}
4880
41a145f1
CF
4881void isis_tlvs_add_spine_leaf(struct isis_tlvs *tlvs, uint8_t tier,
4882 bool has_tier, bool is_leaf, bool is_spine,
4883 bool is_backup)
4884{
4885 assert(!tlvs->spine_leaf);
4886
4887 tlvs->spine_leaf = XCALLOC(MTYPE_ISIS_TLV, sizeof(*tlvs->spine_leaf));
4888
4889 if (has_tier) {
4890 tlvs->spine_leaf->tier = tier;
4891 }
4892
4893 tlvs->spine_leaf->has_tier = has_tier;
4894 tlvs->spine_leaf->is_leaf = is_leaf;
4895 tlvs->spine_leaf->is_spine = is_spine;
4896 tlvs->spine_leaf->is_backup = is_backup;
4897}
4898
7ef5fefc
CF
4899struct isis_mt_router_info *
4900isis_tlvs_lookup_mt_router_info(struct isis_tlvs *tlvs, uint16_t mtid)
4901{
df2c1f3d 4902 if (!tlvs || tlvs->mt_router_info_empty)
7ef5fefc
CF
4903 return NULL;
4904
4905 struct isis_mt_router_info *rv;
4906 for (rv = (struct isis_mt_router_info *)tlvs->mt_router_info.head; rv;
4907 rv = rv->next) {
4908 if (rv->mtid == mtid)
4909 return rv;
4910 }
4911
4912 return NULL;
4913}
2c92bee4
CF
4914
4915void isis_tlvs_set_purge_originator(struct isis_tlvs *tlvs,
4916 const uint8_t *generator,
4917 const uint8_t *sender)
4918{
4919 assert(!tlvs->purge_originator);
4920
4921 tlvs->purge_originator = XCALLOC(MTYPE_ISIS_TLV,
4922 sizeof(*tlvs->purge_originator));
4923 memcpy(tlvs->purge_originator->generator, generator,
4924 sizeof(tlvs->purge_originator->generator));
4925 if (sender) {
4926 tlvs->purge_originator->sender_set = true;
4927 memcpy(tlvs->purge_originator->sender, sender,
4928 sizeof(tlvs->purge_originator->sender));
4929 }
4930}