]> git.proxmox.com Git - mirror_frr.git/blob - isisd/isis_pdu.c
pimd: Option to get IGMP groups and sources for a particular interface
[mirror_frr.git] / isisd / isis_pdu.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * IS-IS Rout(e)ing protocol - isis_pdu.c
4 * PDU processing
5 *
6 * Copyright (C) 2001,2002 Sampo Saaristo
7 * Tampere University of Technology
8 * Institute of Communications Engineering
9 */
10
11 #include <zebra.h>
12
13 #include "memory.h"
14 #include "frrevent.h"
15 #include "linklist.h"
16 #include "log.h"
17 #include "stream.h"
18 #include "vty.h"
19 #include "hash.h"
20 #include "prefix.h"
21 #include "if.h"
22 #include "checksum.h"
23 #include "md5.h"
24 #include "lib_errors.h"
25
26 #include "isisd/isis_constants.h"
27 #include "isisd/isis_common.h"
28 #include "isisd/isis_flags.h"
29 #include "isisd/isis_adjacency.h"
30 #include "isisd/isis_circuit.h"
31 #include "isisd/isis_network.h"
32 #include "isisd/isis_misc.h"
33 #include "isisd/isis_dr.h"
34 #include "isisd/isisd.h"
35 #include "isisd/isis_dynhn.h"
36 #include "isisd/isis_lsp.h"
37 #include "isisd/isis_pdu.h"
38 #include "isisd/iso_checksum.h"
39 #include "isisd/isis_csm.h"
40 #include "isisd/isis_events.h"
41 #include "isisd/isis_te.h"
42 #include "isisd/isis_mt.h"
43 #include "isisd/isis_tlvs.h"
44 #include "isisd/isis_errors.h"
45 #include "isisd/fabricd.h"
46 #include "isisd/isis_tx_queue.h"
47 #include "isisd/isis_pdu_counter.h"
48 #include "isisd/isis_nb.h"
49
50 static int ack_lsp(struct isis_lsp_hdr *hdr, struct isis_circuit *circuit,
51 int level)
52 {
53 unsigned long lenp;
54 int retval;
55 uint16_t length;
56 uint8_t pdu_type =
57 (level == IS_LEVEL_1) ? L1_PARTIAL_SEQ_NUM : L2_PARTIAL_SEQ_NUM;
58
59 isis_circuit_stream(circuit, &circuit->snd_stream);
60
61 fill_fixed_hdr(pdu_type, circuit->snd_stream);
62
63 lenp = stream_get_endp(circuit->snd_stream);
64
65 stream_putw(circuit->snd_stream, 0); /* PDU length */
66 stream_put(circuit->snd_stream, circuit->isis->sysid, ISIS_SYS_ID_LEN);
67 stream_putc(circuit->snd_stream, circuit->idx);
68 stream_putc(circuit->snd_stream, 9); /* code */
69 stream_putc(circuit->snd_stream, 16); /* len */
70
71 stream_putw(circuit->snd_stream, hdr->rem_lifetime);
72 stream_put(circuit->snd_stream, hdr->lsp_id, ISIS_SYS_ID_LEN + 2);
73 stream_putl(circuit->snd_stream, hdr->seqno);
74 stream_putw(circuit->snd_stream, hdr->checksum);
75
76 length = (uint16_t)stream_get_endp(circuit->snd_stream);
77 /* Update PDU length */
78 stream_putw_at(circuit->snd_stream, lenp, length);
79
80 pdu_counter_count(circuit->area->pdu_tx_counters, pdu_type);
81 retval = circuit->tx(circuit, level);
82 if (retval != ISIS_OK)
83 flog_err(EC_ISIS_PACKET,
84 "ISIS-Upd (%s): Send L%d LSP PSNP on %s failed",
85 circuit->area->area_tag, level,
86 circuit->interface->name);
87
88 return retval;
89 }
90
91 /*
92 * RECEIVE SIDE
93 */
94
95 struct iih_info {
96 struct isis_circuit *circuit;
97 uint8_t *ssnpa;
98 int level;
99
100 uint8_t circ_type;
101 uint8_t sys_id[ISIS_SYS_ID_LEN];
102 uint16_t holdtime;
103 uint16_t pdu_len;
104
105 uint8_t circuit_id;
106
107 uint8_t priority;
108 uint8_t dis[ISIS_SYS_ID_LEN + 1];
109
110 bool v4_usable;
111 bool v6_usable;
112
113 struct isis_tlvs *tlvs;
114 };
115
116 static int process_p2p_hello(struct iih_info *iih)
117 {
118 struct isis_threeway_adj *tw_adj = iih->tlvs->threeway_adj;
119
120 if (tw_adj) {
121 if (tw_adj->state > ISIS_THREEWAY_DOWN) {
122 if (IS_DEBUG_ADJ_PACKETS) {
123 zlog_debug("ISIS-Adj (%s): Rcvd P2P IIH from (%s) with invalid three-way state: %d",
124 iih->circuit->area->area_tag,
125 iih->circuit->interface->name,
126 tw_adj->state);
127 }
128 return ISIS_WARNING;
129 }
130
131 if (tw_adj->neighbor_set
132 && (memcmp(tw_adj->neighbor_id, iih->circuit->isis->sysid,
133 ISIS_SYS_ID_LEN)
134 || tw_adj->neighbor_circuit_id
135 != (uint32_t)iih->circuit->idx)) {
136
137 if (IS_DEBUG_ADJ_PACKETS) {
138 zlog_debug("ISIS-Adj (%s): Rcvd P2P IIH from (%s) which lists IS/Circuit different from us as neighbor.",
139 iih->circuit->area->area_tag,
140 iih->circuit->interface->name);
141 }
142
143 return ISIS_WARNING;
144 }
145 }
146
147 /*
148 * My interpertation of the ISO, if no adj exists we will create one for
149 * the circuit
150 */
151 struct isis_adjacency *adj = iih->circuit->u.p2p.neighbor;
152 /* If an adjacency exists, check it is with the source of the hello
153 * packets */
154 if (adj) {
155 if (memcmp(iih->sys_id, adj->sysid, ISIS_SYS_ID_LEN)) {
156 zlog_debug(
157 "hello source and adjacency do not match, set adj down");
158 isis_adj_state_change(&adj, ISIS_ADJ_DOWN,
159 "adj do not exist");
160 return ISIS_OK;
161 }
162 }
163 if (!adj || adj->level != iih->circ_type) {
164 if (!adj) {
165 adj = isis_new_adj(iih->sys_id, NULL, iih->circ_type,
166 iih->circuit);
167 } else {
168 adj->level = iih->circ_type;
169 }
170 iih->circuit->u.p2p.neighbor = adj;
171 /* Build lsp with the new neighbor entry when a new
172 * adjacency is formed. Set adjacency circuit type to
173 * IIH PDU header circuit type before lsp is regenerated
174 * when an adjacency is up. This will result in the new
175 * adjacency entry getting added to the lsp tlv neighbor list.
176 */
177 adj->circuit_t = iih->circ_type;
178 isis_adj_state_change(&adj, ISIS_ADJ_INITIALIZING, NULL);
179 adj->sys_type = ISIS_SYSTYPE_UNKNOWN;
180 }
181
182 if (tw_adj)
183 adj->ext_circuit_id = tw_adj->local_circuit_id;
184
185 /* 8.2.6 Monitoring point-to-point adjacencies */
186 adj->hold_time = iih->holdtime;
187 adj->last_upd = time(NULL);
188
189 bool changed;
190 isis_tlvs_to_adj(iih->tlvs, adj, &changed);
191 changed |= tlvs_to_adj_mt_set(iih->tlvs, iih->v4_usable, iih->v6_usable,
192 adj);
193
194 /* lets take care of the expiry */
195 EVENT_OFF(adj->t_expire);
196 event_add_timer(master, isis_adj_expire, adj, (long)adj->hold_time,
197 &adj->t_expire);
198
199 /* While fabricds initial sync is in progress, ignore hellos from other
200 * interfaces than the one we are performing the initial sync on. */
201 if (fabricd_initial_sync_is_in_progress(iih->circuit->area)
202 && fabricd_initial_sync_circuit(iih->circuit->area) != iih->circuit)
203 return ISIS_OK;
204
205 /* 8.2.5.2 a) a match was detected */
206 if (isis_tlvs_area_addresses_match(iih->tlvs,
207 iih->circuit->area->area_addrs)) {
208 /* 8.2.5.2 a) 2) If the system is L1 - table 5 */
209 if (iih->circuit->area->is_type == IS_LEVEL_1) {
210 switch (iih->circ_type) {
211 case IS_LEVEL_1:
212 case IS_LEVEL_1_AND_2:
213 if (adj->adj_state != ISIS_ADJ_UP
214 || adj->adj_usage == ISIS_ADJ_LEVEL1) {
215 isis_adj_process_threeway(adj, tw_adj,
216 ISIS_ADJ_LEVEL1);
217 }
218 break;
219 case IS_LEVEL_2:
220 if (adj->adj_state != ISIS_ADJ_UP) {
221 /* (7) reject - wrong system type event
222 */
223 zlog_warn("wrongSystemType");
224 return ISIS_WARNING;
225 } else if (adj->adj_usage == ISIS_ADJ_LEVEL1) {
226 /* (6) down - wrong system */
227 isis_adj_state_change(&adj,
228 ISIS_ADJ_DOWN,
229 "Wrong System");
230 }
231 break;
232 }
233 }
234
235 /* 8.2.5.2 a) 3) If the system is L1L2 - table 6 */
236 if (iih->circuit->area->is_type == IS_LEVEL_1_AND_2) {
237 switch (iih->circ_type) {
238 case IS_LEVEL_1:
239 if (adj->adj_state != ISIS_ADJ_UP
240 || adj->adj_usage == ISIS_ADJ_LEVEL1) {
241 isis_adj_process_threeway(adj, tw_adj,
242 ISIS_ADJ_LEVEL1);
243 } else if ((adj->adj_usage
244 == ISIS_ADJ_LEVEL1AND2)
245 || (adj->adj_usage
246 == ISIS_ADJ_LEVEL2)) {
247 /* (8) down - wrong system */
248 isis_adj_state_change(&adj,
249 ISIS_ADJ_DOWN,
250 "Wrong System");
251 }
252 break;
253 case IS_LEVEL_2:
254 if (adj->adj_state != ISIS_ADJ_UP
255 || adj->adj_usage == ISIS_ADJ_LEVEL2) {
256 isis_adj_process_threeway(adj, tw_adj,
257 ISIS_ADJ_LEVEL2);
258 } else if ((adj->adj_usage == ISIS_ADJ_LEVEL1)
259 || (adj->adj_usage
260 == ISIS_ADJ_LEVEL1AND2)) {
261 /* (8) down - wrong system */
262 isis_adj_state_change(&adj,
263 ISIS_ADJ_DOWN,
264 "Wrong System");
265 }
266 break;
267 case IS_LEVEL_1_AND_2:
268 if (adj->adj_state != ISIS_ADJ_UP
269 || adj->adj_usage == ISIS_ADJ_LEVEL1AND2) {
270 isis_adj_process_threeway(adj, tw_adj,
271 ISIS_ADJ_LEVEL1AND2);
272 } else if ((adj->adj_usage == ISIS_ADJ_LEVEL1)
273 || (adj->adj_usage
274 == ISIS_ADJ_LEVEL2)) {
275 /* (8) down - wrong system */
276 isis_adj_state_change(&adj,
277 ISIS_ADJ_DOWN,
278 "Wrong System");
279 }
280 break;
281 }
282 }
283
284 /* 8.2.5.2 a) 4) If the system is L2 - table 7 */
285 if (iih->circuit->area->is_type == IS_LEVEL_2) {
286 switch (iih->circ_type) {
287 case IS_LEVEL_1:
288 if (adj->adj_state != ISIS_ADJ_UP) {
289 /* (5) reject - wrong system type event
290 */
291 zlog_warn("wrongSystemType");
292 return ISIS_WARNING;
293 } else if ((adj->adj_usage
294 == ISIS_ADJ_LEVEL1AND2)
295 || (adj->adj_usage
296 == ISIS_ADJ_LEVEL2)) {
297 /* (6) down - wrong system */
298 isis_adj_state_change(&adj,
299 ISIS_ADJ_DOWN,
300 "Wrong System");
301 }
302 break;
303 case IS_LEVEL_1_AND_2:
304 case IS_LEVEL_2:
305 if (adj->adj_state != ISIS_ADJ_UP
306 || adj->adj_usage == ISIS_ADJ_LEVEL2) {
307 isis_adj_process_threeway(adj, tw_adj,
308 ISIS_ADJ_LEVEL2);
309 } else if (adj->adj_usage
310 == ISIS_ADJ_LEVEL1AND2) {
311 /* (6) down - wrong system */
312 isis_adj_state_change(&adj,
313 ISIS_ADJ_DOWN,
314 "Wrong System");
315 }
316 break;
317 }
318 }
319 }
320 /* 8.2.5.2 b) if no match was detected */
321 else if (listcount(iih->circuit->area->area_addrs) > 0) {
322 if (iih->circuit->area->is_type == IS_LEVEL_1) {
323 /* 8.2.5.2 b) 1) is_type L1 and adj is not up */
324 if (adj->adj_state != ISIS_ADJ_UP) {
325 isis_adj_state_change(&adj, ISIS_ADJ_DOWN,
326 "Area Mismatch");
327 /* 8.2.5.2 b) 2)is_type L1 and adj is up */
328 } else {
329 isis_adj_state_change(&adj, ISIS_ADJ_DOWN,
330 "Down - Area Mismatch");
331 }
332 }
333 /* 8.2.5.2 b 3 If the system is L2 or L1L2 - table 8 */
334 else {
335 switch (iih->circ_type) {
336 case IS_LEVEL_1:
337 if (adj->adj_state != ISIS_ADJ_UP) {
338 /* (6) reject - Area Mismatch event */
339 zlog_warn("AreaMismatch");
340 return ISIS_WARNING;
341 } else if (adj->adj_usage == ISIS_ADJ_LEVEL1) {
342 /* (7) down - area mismatch */
343 isis_adj_state_change(&adj,
344 ISIS_ADJ_DOWN,
345 "Area Mismatch");
346
347 } else if ((adj->adj_usage
348 == ISIS_ADJ_LEVEL1AND2)
349 || (adj->adj_usage
350 == ISIS_ADJ_LEVEL2)) {
351 /* (7) down - wrong system */
352 isis_adj_state_change(&adj,
353 ISIS_ADJ_DOWN,
354 "Wrong System");
355 }
356 break;
357 case IS_LEVEL_1_AND_2:
358 case IS_LEVEL_2:
359 if (adj->adj_state != ISIS_ADJ_UP
360 || adj->adj_usage == ISIS_ADJ_LEVEL2) {
361 isis_adj_process_threeway(adj, tw_adj,
362 ISIS_ADJ_LEVEL2);
363 } else if (adj->adj_usage == ISIS_ADJ_LEVEL1) {
364 /* (7) down - wrong system */
365 isis_adj_state_change(&adj,
366 ISIS_ADJ_DOWN,
367 "Wrong System");
368 } else if (adj->adj_usage
369 == ISIS_ADJ_LEVEL1AND2) {
370 if (iih->circ_type == IS_LEVEL_2) {
371 /* (7) down - wrong system */
372 isis_adj_state_change(
373 &adj, ISIS_ADJ_DOWN,
374 "Wrong System");
375 } else {
376 /* (7) down - area mismatch */
377 isis_adj_state_change(
378 &adj, ISIS_ADJ_DOWN,
379 "Area Mismatch");
380 }
381 }
382 break;
383 }
384 }
385 } else {
386 /* down - area mismatch */
387 isis_adj_state_change(&adj, ISIS_ADJ_DOWN, "Area Mismatch");
388 }
389
390 if (adj) {
391 if (adj->adj_state == ISIS_ADJ_UP && changed) {
392 lsp_regenerate_schedule(
393 adj->circuit->area,
394 isis_adj_usage2levels(adj->adj_usage), 0);
395 }
396
397 /* 8.2.5.2 c) if the action was up - comparing circuit IDs */
398 /* FIXME - Missing parts */
399
400 /* some of my own understanding of the ISO, why the heck does
401 * it not say what should I change the system_type to...
402 */
403 switch (adj->adj_usage) {
404 case ISIS_ADJ_LEVEL1:
405 adj->sys_type = ISIS_SYSTYPE_L1_IS;
406 break;
407 case ISIS_ADJ_LEVEL2:
408 adj->sys_type = ISIS_SYSTYPE_L2_IS;
409 break;
410 case ISIS_ADJ_LEVEL1AND2:
411 adj->sys_type = ISIS_SYSTYPE_L2_IS;
412 break;
413 case ISIS_ADJ_NONE:
414 adj->sys_type = ISIS_SYSTYPE_UNKNOWN;
415 break;
416 }
417 }
418
419 if (IS_DEBUG_ADJ_PACKETS) {
420 zlog_debug(
421 "ISIS-Adj (%s): Rcvd P2P IIH from (%s), cir type %s, cir id %hhu, length %hu",
422 iih->circuit->area->area_tag,
423 iih->circuit->interface->name,
424 circuit_t2string(iih->circuit->is_type),
425 iih->circuit->circuit_id, iih->pdu_len);
426 }
427
428 return ISIS_OK;
429 }
430
431 static int process_lan_hello(struct iih_info *iih)
432 {
433 struct isis_adjacency *adj;
434 adj = isis_adj_lookup(iih->sys_id,
435 iih->circuit->u.bc.adjdb[iih->level - 1]);
436 if ((adj == NULL) || (memcmp(adj->snpa, iih->ssnpa, ETH_ALEN))
437 || (adj->level != iih->level)) {
438 if (!adj) {
439 /* Do as in 8.4.2.5 */
440 adj = isis_new_adj(iih->sys_id, iih->ssnpa, iih->level,
441 iih->circuit);
442 } else {
443 if (iih->ssnpa) {
444 memcpy(adj->snpa, iih->ssnpa, 6);
445 } else {
446 memset(adj->snpa, ' ', 6);
447 }
448 adj->level = iih->level;
449 }
450 isis_adj_state_change(&adj, ISIS_ADJ_INITIALIZING, NULL);
451
452 if (iih->level == IS_LEVEL_1)
453 adj->sys_type = ISIS_SYSTYPE_L1_IS;
454 else
455 adj->sys_type = ISIS_SYSTYPE_L2_IS;
456 list_delete_all_node(
457 iih->circuit->u.bc.lan_neighs[iih->level - 1]);
458 isis_adj_build_neigh_list(
459 iih->circuit->u.bc.adjdb[iih->level - 1],
460 iih->circuit->u.bc.lan_neighs[iih->level - 1]);
461 }
462
463 if (adj->dis_record[iih->level - 1].dis == ISIS_IS_DIS) {
464 uint8_t *dis = (iih->level == 1)
465 ? iih->circuit->u.bc.l1_desig_is
466 : iih->circuit->u.bc.l2_desig_is;
467
468 if (memcmp(dis, iih->dis, ISIS_SYS_ID_LEN + 1)) {
469 event_add_event(master, isis_event_dis_status_change,
470 iih->circuit, 0, NULL);
471 memcpy(dis, iih->dis, ISIS_SYS_ID_LEN + 1);
472 }
473 }
474
475 adj->circuit_t = iih->circ_type;
476 adj->hold_time = iih->holdtime;
477 adj->last_upd = time(NULL);
478 adj->prio[iih->level - 1] = iih->priority;
479 memcpy(adj->lanid, iih->dis, ISIS_SYS_ID_LEN + 1);
480
481 bool changed;
482 isis_tlvs_to_adj(iih->tlvs, adj, &changed);
483 changed |= tlvs_to_adj_mt_set(iih->tlvs, iih->v4_usable, iih->v6_usable,
484 adj);
485
486 /* lets take care of the expiry */
487 EVENT_OFF(adj->t_expire);
488 event_add_timer(master, isis_adj_expire, adj, (long)adj->hold_time,
489 &adj->t_expire);
490
491 /*
492 * If the snpa for this circuit is found from LAN Neighbours TLV
493 * we have two-way communication -> adjacency can be put to state "up"
494 */
495 bool own_snpa_found =
496 isis_tlvs_own_snpa_found(iih->tlvs, iih->circuit->u.bc.snpa);
497
498 if (adj->adj_state != ISIS_ADJ_UP) {
499 if (own_snpa_found) {
500 isis_adj_state_change(
501 &adj, ISIS_ADJ_UP,
502 "own SNPA found in LAN Neighbours TLV");
503 }
504 } else {
505 if (!own_snpa_found) {
506 isis_adj_state_change(
507 &adj, ISIS_ADJ_INITIALIZING,
508 "own SNPA not found in LAN Neighbours TLV");
509 }
510 }
511
512 if (adj->adj_state == ISIS_ADJ_UP && changed)
513 lsp_regenerate_schedule(adj->circuit->area, iih->level, 0);
514
515 if (IS_DEBUG_ADJ_PACKETS) {
516 zlog_debug(
517 "ISIS-Adj (%s): Rcvd L%d LAN IIH from %s on %s, cirType %s, cirID %u, length %zd",
518 iih->circuit->area->area_tag, iih->level,
519 snpa_print(iih->ssnpa), iih->circuit->interface->name,
520 circuit_t2string(iih->circuit->is_type),
521 iih->circuit->circuit_id,
522 stream_get_endp(iih->circuit->rcv_stream));
523 }
524 return ISIS_OK;
525 }
526
527 static int pdu_len_validate(uint16_t pdu_len, struct isis_circuit *circuit)
528 {
529 if (pdu_len < stream_get_getp(circuit->rcv_stream)
530 || pdu_len > ISO_MTU(circuit)
531 || pdu_len > stream_get_endp(circuit->rcv_stream))
532 return 1;
533
534 if (pdu_len < stream_get_endp(circuit->rcv_stream))
535 stream_set_endp(circuit->rcv_stream, pdu_len);
536 return 0;
537 }
538
539 static void update_rej_adj_count(struct isis_circuit *circuit)
540 {
541 circuit->rej_adjacencies++;
542 if (circuit->is_type == IS_LEVEL_1)
543 circuit->area->rej_adjacencies[0]++;
544 else if (circuit->is_type == IS_LEVEL_2)
545 circuit->area->rej_adjacencies[1]++;
546 else {
547 circuit->area->rej_adjacencies[0]++;
548 circuit->area->rej_adjacencies[1]++;
549 }
550 }
551
552 static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit,
553 uint8_t *ssnpa)
554 {
555 /* keep a copy of the raw pdu for NB notifications */
556 size_t pdu_start = stream_get_getp(circuit->rcv_stream);
557 size_t pdu_end = stream_get_endp(circuit->rcv_stream);
558 char raw_pdu[pdu_end - pdu_start];
559 bool p2p_hello = (pdu_type == P2P_HELLO);
560 int level = p2p_hello ? 0
561 : (pdu_type == L1_LAN_HELLO) ? ISIS_LEVEL1
562 : ISIS_LEVEL2;
563 const char *pdu_name =
564 p2p_hello
565 ? "P2P IIH"
566 : (level == ISIS_LEVEL1) ? "L1 LAN IIH" : "L2 LAN IIH";
567
568 stream_get_from(raw_pdu, circuit->rcv_stream, pdu_start,
569 pdu_end - pdu_start);
570 if (IS_DEBUG_ADJ_PACKETS) {
571 zlog_debug("ISIS-Adj (%s): Rcvd %s on %s, cirType %s, cirID %u",
572 circuit->area->area_tag, pdu_name,
573 circuit->interface->name,
574 circuit_t2string(circuit->is_type),
575 circuit->circuit_id);
576 if (IS_DEBUG_PACKET_DUMP)
577 zlog_dump_data(STREAM_DATA(circuit->rcv_stream),
578 stream_get_endp(circuit->rcv_stream));
579 }
580
581 if (p2p_hello) {
582 if (circuit->circ_type != CIRCUIT_T_P2P) {
583 zlog_warn("p2p hello on non p2p circuit");
584 update_rej_adj_count(circuit);
585 #ifndef FABRICD
586 isis_notif_reject_adjacency(
587 circuit, "p2p hello on non p2p circuit",
588 raw_pdu, sizeof(raw_pdu));
589 #endif /* ifndef FABRICD */
590 return ISIS_WARNING;
591 }
592 } else {
593 if (circuit->circ_type != CIRCUIT_T_BROADCAST) {
594 zlog_warn("lan hello on non broadcast circuit");
595 update_rej_adj_count(circuit);
596 #ifndef FABRICD
597 isis_notif_reject_adjacency(
598 circuit, "lan hello on non broadcast circuit",
599 raw_pdu, sizeof(raw_pdu));
600 #endif /* ifndef FABRICD */
601 return ISIS_WARNING;
602 }
603
604 if (circuit->ext_domain) {
605 zlog_debug(
606 "level %d LAN Hello received over circuit with externalDomain = true",
607 level);
608 update_rej_adj_count(circuit);
609 #ifndef FABRICD
610 isis_notif_reject_adjacency(
611 circuit,
612 "LAN Hello received over circuit with externalDomain = true",
613 raw_pdu, sizeof(raw_pdu));
614 #endif /* ifndef FABRICD */
615 return ISIS_WARNING;
616 }
617
618 if (!(circuit->is_type & level)) {
619 if (IS_DEBUG_ADJ_PACKETS) {
620 zlog_debug(
621 "ISIS-Adj (%s): Interface level mismatch, %s",
622 circuit->area->area_tag,
623 circuit->interface->name);
624 }
625 update_rej_adj_count(circuit);
626 #ifndef FABRICD
627 isis_notif_reject_adjacency(circuit,
628 "Interface level mismatch",
629 raw_pdu, sizeof(raw_pdu));
630 #endif /* ifndef FABRICD */
631 return ISIS_WARNING;
632 }
633 }
634
635 struct iih_info iih = {
636 .circuit = circuit, .ssnpa = ssnpa, .level = level};
637
638 /* Generic IIH Header */
639 iih.circ_type = stream_getc(circuit->rcv_stream) & 0x03;
640 stream_get(iih.sys_id, circuit->rcv_stream, ISIS_SYS_ID_LEN);
641 iih.holdtime = stream_getw(circuit->rcv_stream);
642 iih.pdu_len = stream_getw(circuit->rcv_stream);
643
644 if (p2p_hello) {
645 iih.circuit_id = stream_getc(circuit->rcv_stream);
646 } else {
647 iih.priority = stream_getc(circuit->rcv_stream);
648 stream_get(iih.dis, circuit->rcv_stream, ISIS_SYS_ID_LEN + 1);
649 }
650
651 if (pdu_len_validate(iih.pdu_len, circuit)) {
652 zlog_warn(
653 "ISIS-Adj (%s): Rcvd %s from (%s) with invalid pdu length %hu",
654 circuit->area->area_tag, pdu_name,
655 circuit->interface->name, iih.pdu_len);
656 update_rej_adj_count(circuit);
657 #ifndef FABRICD
658 isis_notif_reject_adjacency(circuit, "Invalid PDU length",
659 raw_pdu, sizeof(raw_pdu));
660 #endif /* ifndef FABRICD */
661 return ISIS_WARNING;
662 }
663
664 if (!p2p_hello && !(level & iih.circ_type)) {
665 flog_err(EC_ISIS_PACKET,
666 "Level %d LAN Hello with Circuit Type %d", level,
667 iih.circ_type);
668 update_rej_adj_count(circuit);
669 #ifndef FABRICD
670 isis_notif_reject_adjacency(circuit,
671 "LAN Hello with wrong IS-level",
672 raw_pdu, sizeof(raw_pdu));
673 #endif /* ifndef FABRICD */
674 return ISIS_ERROR;
675 }
676
677 const char *error_log;
678 int retval = ISIS_WARNING;
679
680 if (isis_unpack_tlvs(STREAM_READABLE(circuit->rcv_stream),
681 circuit->rcv_stream, &iih.tlvs, &error_log)) {
682 zlog_warn("isis_unpack_tlvs() failed: %s", error_log);
683 update_rej_adj_count(circuit);
684 #ifndef FABRICD
685 isis_notif_reject_adjacency(circuit, "Failed to unpack TLVs",
686 raw_pdu, sizeof(raw_pdu));
687 #endif /* ifndef FABRICD */
688 goto out;
689 }
690
691 if (!iih.tlvs->area_addresses.count) {
692 zlog_warn("No Area addresses TLV in %s", pdu_name);
693 #ifndef FABRICD
694 /* send northbound notification */
695 isis_notif_area_mismatch(circuit, raw_pdu, sizeof(raw_pdu));
696 #endif /* ifndef FABRICD */
697 goto out;
698 }
699
700 if (!iih.tlvs->protocols_supported.count) {
701 zlog_warn("No supported protocols TLV in %s", pdu_name);
702 update_rej_adj_count(circuit);
703 #ifndef FABRICD
704 isis_notif_reject_adjacency(circuit,
705 "No supported protocols TLV",
706 raw_pdu, sizeof(raw_pdu));
707 #endif /* ifndef FABRICD */
708 goto out;
709 }
710
711 int auth_code = isis_tlvs_auth_is_valid(iih.tlvs, &circuit->passwd,
712 circuit->rcv_stream, false);
713 if (auth_code != ISIS_AUTH_OK) {
714 isis_event_auth_failure(circuit->area->area_tag,
715 "IIH authentication failure",
716 iih.sys_id);
717 #ifndef FABRICD
718 /* send northbound notification */
719 stream_get_from(raw_pdu, circuit->rcv_stream, pdu_start,
720 pdu_end - pdu_start);
721 if (auth_code == ISIS_AUTH_FAILURE) {
722 update_rej_adj_count(circuit);
723 isis_notif_authentication_failure(circuit, raw_pdu,
724 sizeof(raw_pdu));
725 } else { /* AUTH_TYPE_FAILURE or NO_VALIDATOR */
726 update_rej_adj_count(circuit);
727 isis_notif_authentication_type_failure(circuit, raw_pdu,
728 sizeof(raw_pdu));
729 }
730 #endif /* ifndef FABRICD */
731 goto out;
732 }
733
734 if (!memcmp(iih.sys_id, circuit->isis->sysid, ISIS_SYS_ID_LEN)) {
735 zlog_warn(
736 "ISIS-Adj (%s): Received IIH with own sysid on %s - discard",
737 circuit->area->area_tag, circuit->interface->name);
738 update_rej_adj_count(circuit);
739 #ifndef FABRICD
740 isis_notif_reject_adjacency(circuit,
741 "Received IIH with our own sysid",
742 raw_pdu, sizeof(raw_pdu));
743 #endif /* ifndef FABRICD */
744 goto out;
745 }
746
747 if (!p2p_hello
748 && (listcount(circuit->area->area_addrs) == 0
749 || (level == ISIS_LEVEL1
750 && !isis_tlvs_area_addresses_match(
751 iih.tlvs, circuit->area->area_addrs)))) {
752 if (IS_DEBUG_ADJ_PACKETS) {
753 zlog_debug(
754 "ISIS-Adj (%s): Area mismatch, level %d IIH on %s",
755 circuit->area->area_tag, level,
756 circuit->interface->name);
757 }
758 #ifndef FABRICD
759 /* send northbound notification */
760 isis_notif_area_mismatch(circuit, raw_pdu, sizeof(raw_pdu));
761 #endif /* ifndef FABRICD */
762 goto out;
763 }
764
765 iih.v4_usable = (fabricd_ip_addrs(circuit)
766 && iih.tlvs->ipv4_address.count);
767
768 iih.v6_usable =
769 (listcount(circuit->ipv6_link) && iih.tlvs->ipv6_address.count);
770
771 if (!iih.v4_usable && !iih.v6_usable) {
772 if (IS_DEBUG_ADJ_PACKETS) {
773 zlog_warn(
774 "ISIS-Adj (%s): Neither IPv4 nor IPv6 considered usable. Ignoring IIH",
775 circuit->area->area_tag);
776 }
777 update_rej_adj_count(circuit);
778 #ifndef FABRICD
779 isis_notif_reject_adjacency(
780 circuit, "Neither IPv4 not IPv6 considered usable",
781 raw_pdu, sizeof(raw_pdu));
782 #endif /* ifndef FABRICD */
783 goto out;
784 }
785
786 retval = p2p_hello ? process_p2p_hello(&iih) : process_lan_hello(&iih);
787 out:
788 isis_free_tlvs(iih.tlvs);
789
790 return retval;
791 }
792
793 static void lsp_flood_or_update(struct isis_lsp *lsp,
794 struct isis_circuit *circuit,
795 bool circuit_scoped)
796 {
797 if (!circuit_scoped)
798 lsp_flood(lsp, circuit);
799 else
800 fabricd_update_lsp_no_flood(lsp, circuit);
801 }
802
803 /*
804 * Process Level 1/2 Link State
805 * ISO - 10589
806 * Section 7.3.15.1 - Action on receipt of a link state PDU
807 */
808 static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit,
809 const uint8_t *ssnpa, uint8_t max_area_addrs)
810 {
811 int level;
812 bool circuit_scoped;
813 size_t pdu_start = stream_get_getp(circuit->rcv_stream);
814 size_t pdu_end = stream_get_endp(circuit->rcv_stream);
815 char raw_pdu[pdu_end - pdu_start];
816
817 stream_get_from(raw_pdu, circuit->rcv_stream, pdu_start,
818 pdu_end - pdu_start);
819
820 if (pdu_type == FS_LINK_STATE) {
821 if (!fabricd)
822 return ISIS_ERROR;
823 if (max_area_addrs != L2_CIRCUIT_FLOODING_SCOPE)
824 return ISIS_ERROR;
825 level = ISIS_LEVEL2;
826 circuit_scoped = true;
827
828 /* The stream is used verbatim for sending out new LSPDUs.
829 * So make sure we store it as an L2 LSPDU internally.
830 * (compare for the reverse in `send_lsp`) */
831 stream_putc_at(circuit->rcv_stream, 4, L2_LINK_STATE);
832 stream_putc_at(circuit->rcv_stream, 7, 0);
833 } else {
834 if (pdu_type == L1_LINK_STATE)
835 level = ISIS_LEVEL1;
836 else
837 level = ISIS_LEVEL2;
838 circuit_scoped = false;
839 }
840
841 if (IS_DEBUG_UPDATE_PACKETS) {
842 zlog_debug(
843 "ISIS-Upd (%s): Rcvd %sL%d LSP on %s, cirType %s, cirID %u",
844 circuit->area->area_tag,
845 circuit_scoped ? "Circuit scoped " : "", level,
846 circuit->interface->name,
847 circuit_t2string(circuit->is_type),
848 circuit->circuit_id);
849 if (IS_DEBUG_PACKET_DUMP)
850 zlog_dump_data(STREAM_DATA(circuit->rcv_stream),
851 stream_get_endp(circuit->rcv_stream));
852 }
853
854 struct isis_lsp_hdr hdr = {};
855
856 hdr.pdu_len = stream_getw(circuit->rcv_stream);
857 hdr.rem_lifetime = stream_getw(circuit->rcv_stream);
858 stream_get(hdr.lsp_id, circuit->rcv_stream, sizeof(hdr.lsp_id));
859 hdr.seqno = stream_getl(circuit->rcv_stream);
860 hdr.checksum = stream_getw(circuit->rcv_stream);
861 hdr.lsp_bits = stream_getc(circuit->rcv_stream);
862
863 #ifndef FABRICD
864 /* send northbound notification */
865 isis_notif_lsp_received(circuit, hdr.lsp_id, hdr.seqno, time(NULL),
866 sysid_print(hdr.lsp_id));
867 #endif /* ifndef FABRICD */
868
869 if (pdu_len_validate(hdr.pdu_len, circuit)) {
870 zlog_debug("ISIS-Upd (%s): LSP %s invalid LSP length %hu",
871 circuit->area->area_tag, rawlspid_print(hdr.lsp_id),
872 hdr.pdu_len);
873 return ISIS_WARNING;
874 }
875
876 if (IS_DEBUG_UPDATE_PACKETS) {
877 zlog_debug("ISIS-Upd (%s): Rcvd L%d LSP %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus, len %hu, on %s",
878 circuit->area->area_tag, level,
879 rawlspid_print(hdr.lsp_id), hdr.seqno, hdr.checksum,
880 hdr.rem_lifetime, hdr.pdu_len,
881 circuit->interface->name);
882 }
883
884 /* lsp is_type check */
885 if ((hdr.lsp_bits & IS_LEVEL_1) != IS_LEVEL_1) {
886 zlog_debug(
887 "ISIS-Upd (%s): LSP %s invalid LSP is type 0x%x",
888 circuit->area->area_tag, rawlspid_print(hdr.lsp_id),
889 hdr.lsp_bits & IS_LEVEL_1_AND_2);
890 /* continue as per RFC1122 Be liberal in what you accept, and
891 * conservative in what you send */
892 }
893
894 /* Checksum sanity check - FIXME: move to correct place */
895 /* 12 = sysid+pdu+remtime */
896 if (iso_csum_verify(STREAM_DATA(circuit->rcv_stream) + 12,
897 hdr.pdu_len - 12, hdr.checksum, 12)) {
898 zlog_debug(
899 "ISIS-Upd (%s): LSP %s invalid LSP checksum 0x%04hx",
900 circuit->area->area_tag, rawlspid_print(hdr.lsp_id),
901 hdr.checksum);
902 return ISIS_WARNING;
903 }
904
905 /* 7.3.15.1 a) 1 - external domain circuit will discard lsps */
906 if (circuit->ext_domain) {
907 zlog_debug(
908 "ISIS-Upd (%s): LSP %s received at level %d over circuit with externalDomain = true",
909 circuit->area->area_tag, rawlspid_print(hdr.lsp_id),
910 level);
911 return ISIS_WARNING;
912 }
913
914 /* 7.3.15.1 a) 2,3 - manualL2OnlyMode not implemented */
915 if (!(circuit->is_type & level)) {
916 zlog_debug(
917 "ISIS-Upd (%s): LSP %s received at level %d over circuit of type %s",
918 circuit->area->area_tag, rawlspid_print(hdr.lsp_id),
919 level, circuit_t2string(circuit->is_type));
920 return ISIS_WARNING;
921 }
922
923 struct isis_tlvs *tlvs = NULL;
924 int retval = ISIS_WARNING;
925 const char *error_log;
926
927 if (isis_unpack_tlvs(STREAM_READABLE(circuit->rcv_stream),
928 circuit->rcv_stream, &tlvs, &error_log)) {
929 zlog_warn("Something went wrong unpacking the LSP: %s",
930 error_log);
931 #ifndef FABRICD
932 /* send northbound notification. Note that the tlv-type and
933 * offset cannot correctly be set here as they are not returned
934 * by isis_unpack_tlvs, but in there I cannot fire a
935 * notification because I have no circuit information. So until
936 * we change the code above to return those extra fields, we
937 * will send dummy values which are ignored in the callback
938 */
939 circuit->lsp_error_counter++;
940 if (circuit->is_type == IS_LEVEL_1) {
941 circuit->area->lsp_error_counter[0]++;
942 } else if (circuit->is_type == IS_LEVEL_2) {
943 circuit->area->lsp_error_counter[1]++;
944 } else {
945 circuit->area->lsp_error_counter[0]++;
946 circuit->area->lsp_error_counter[1]++;
947 }
948
949 isis_notif_lsp_error(circuit, hdr.lsp_id, raw_pdu,
950 sizeof(raw_pdu), 0, 0);
951 #endif /* ifndef FABRICD */
952 goto out;
953 }
954
955 /* 7.3.15.1 a) 4 - need to make sure IDLength matches */
956
957 /* 7.3.15.1 a) 5 - maximum area match, can be ommited since we only use
958 * 3 */
959
960 /* 7.3.15.1 a) 7 - password check */
961 struct isis_passwd *passwd = (level == ISIS_LEVEL1)
962 ? &circuit->area->area_passwd
963 : &circuit->area->domain_passwd;
964 int auth_code = isis_tlvs_auth_is_valid(tlvs, passwd,
965 circuit->rcv_stream, true);
966 if (auth_code != ISIS_AUTH_OK) {
967 isis_event_auth_failure(circuit->area->area_tag,
968 "LSP authentication failure",
969 hdr.lsp_id);
970 #ifndef FABRICD
971 /* send northbound notification */
972 if (auth_code == ISIS_AUTH_FAILURE) {
973 circuit->auth_failures++;
974 if (circuit->is_type == IS_LEVEL_1) {
975 circuit->area->auth_failures[0]++;
976 } else if (circuit->is_type == IS_LEVEL_2) {
977 circuit->area->auth_failures[1]++;
978 } else {
979 circuit->area->auth_failures[0]++;
980 circuit->area->auth_failures[1]++;
981 }
982 isis_notif_authentication_failure(circuit, raw_pdu,
983 sizeof(raw_pdu));
984 } else { /* AUTH_TYPE_FAILURE or NO_VALIDATOR */
985 circuit->auth_type_failures++;
986 if (circuit->is_type == IS_LEVEL_1) {
987 circuit->area->auth_type_failures[0]++;
988 } else if (circuit->is_type == IS_LEVEL_2) {
989 circuit->area->auth_type_failures[1]++;
990 } else {
991 circuit->area->auth_type_failures[0]++;
992 circuit->area->auth_type_failures[1]++;
993 }
994 isis_notif_authentication_type_failure(circuit, raw_pdu,
995 sizeof(raw_pdu));
996 }
997 #endif /* ifndef FABRICD */
998 goto out;
999 }
1000
1001 /* Find the LSP in our database and compare it to this Link State header
1002 */
1003 struct isis_lsp *lsp =
1004 lsp_search(&circuit->area->lspdb[level - 1], hdr.lsp_id);
1005 int comp = 0;
1006 if (lsp)
1007 comp = lsp_compare(circuit->area->area_tag, lsp, hdr.seqno,
1008 hdr.checksum, hdr.rem_lifetime);
1009 if (lsp && (lsp->own_lsp))
1010 goto dontcheckadj;
1011
1012 /* 7.3.15.1 a) 6 - Must check that we have an adjacency of the same
1013 * level */
1014 /* for broadcast circuits, snpa should be compared */
1015
1016 if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
1017 if (!isis_adj_lookup_snpa(ssnpa,
1018 circuit->u.bc.adjdb[level - 1])) {
1019 zlog_debug("(%s): DS ======= LSP %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus on %s",
1020 circuit->area->area_tag,
1021 rawlspid_print(hdr.lsp_id), hdr.seqno,
1022 hdr.checksum, hdr.rem_lifetime,
1023 circuit->interface->name);
1024 goto out; /* Silently discard */
1025 }
1026 }
1027 /* for non broadcast, we just need to find same level adj */
1028 else {
1029 /* If no adj, or no sharing of level */
1030 if (!circuit->u.p2p.neighbor) {
1031 retval = ISIS_OK;
1032 goto out;
1033 } else {
1034 if (((level == IS_LEVEL_1)
1035 && (circuit->u.p2p.neighbor->adj_usage
1036 == ISIS_ADJ_LEVEL2))
1037 || ((level == IS_LEVEL_2)
1038 && (circuit->u.p2p.neighbor->adj_usage
1039 == ISIS_ADJ_LEVEL1)))
1040 goto out;
1041 }
1042 }
1043
1044 bool lsp_confusion;
1045
1046 dontcheckadj:
1047 /* 7.3.15.1 a) 7 - Passwords for level 1 - not implemented */
1048
1049 /* 7.3.15.1 a) 8 - Passwords for level 2 - not implemented */
1050
1051 /* 7.3.15.1 a) 9 - OriginatingLSPBufferSize - not implemented FIXME: do
1052 * it */
1053
1054 /* 7.3.16.2 - If this is an LSP from another IS with identical seq_num
1055 * but
1056 * wrong checksum, initiate a purge. */
1057 if (lsp && (lsp->hdr.seqno == hdr.seqno)
1058 && (lsp->hdr.checksum != hdr.checksum)
1059 && hdr.rem_lifetime) {
1060 zlog_warn("ISIS-Upd (%s): LSP %s seq 0x%08x with confused checksum received.",
1061 circuit->area->area_tag, rawlspid_print(hdr.lsp_id),
1062 hdr.seqno);
1063 hdr.rem_lifetime = 0;
1064 lsp_confusion = true;
1065 } else
1066 lsp_confusion = false;
1067
1068 /* 7.3.15.1 b) - If the remaining life time is 0, we perform 7.3.16.4 */
1069 if (hdr.rem_lifetime == 0) {
1070 if (!lsp) {
1071 /* 7.3.16.4 a) 1) No LSP in db -> send an ack, but don't
1072 * save */
1073 /* only needed on explicit update, eg - p2p */
1074 if (circuit->circ_type == CIRCUIT_T_P2P)
1075 ack_lsp(&hdr, circuit, level);
1076 goto out; /* FIXME: do we need a purge? */
1077 } else {
1078 if (memcmp(hdr.lsp_id, circuit->isis->sysid,
1079 ISIS_SYS_ID_LEN)) {
1080 /* LSP by some other system -> do 7.3.16.4 b) */
1081 /* 7.3.16.4 b) 1) */
1082 if (comp == LSP_NEWER) {
1083 lsp_update(lsp, &hdr, tlvs,
1084 circuit->rcv_stream,
1085 circuit->area, level,
1086 lsp_confusion);
1087 if (lsp_confusion)
1088 isis_free_tlvs(tlvs);
1089 tlvs = NULL;
1090 /* ii */
1091 lsp_flood_or_update(lsp, NULL,
1092 circuit_scoped);
1093 /* v */
1094 ISIS_FLAGS_CLEAR_ALL(
1095 lsp->SSNflags); /* FIXME:
1096 OTHER
1097 than c
1098 */
1099
1100 /* For the case of lsp confusion, flood
1101 * the purge back to its
1102 * originator so that it can react.
1103 * Otherwise, don't reflood
1104 * through incoming circuit as usual */
1105 if (!lsp_confusion) {
1106 isis_tx_queue_del(
1107 circuit->tx_queue,
1108 lsp);
1109
1110 /* iv */
1111 if (circuit->circ_type
1112 != CIRCUIT_T_BROADCAST)
1113 ISIS_SET_FLAG(
1114 lsp->SSNflags,
1115 circuit);
1116 }
1117 } /* 7.3.16.4 b) 2) */
1118 else if (comp == LSP_EQUAL) {
1119 /* i */
1120 isis_tx_queue_del(circuit->tx_queue,
1121 lsp);
1122 /* ii */
1123 if (circuit->circ_type
1124 != CIRCUIT_T_BROADCAST)
1125 ISIS_SET_FLAG(lsp->SSNflags,
1126 circuit);
1127 } /* 7.3.16.4 b) 3) */
1128 else {
1129 isis_tx_queue_add(circuit->tx_queue,
1130 lsp, TX_LSP_NORMAL);
1131 ISIS_CLEAR_FLAG(lsp->SSNflags, circuit);
1132 }
1133 } else if (lsp->hdr.rem_lifetime != 0) {
1134 /* our own LSP -> 7.3.16.4 c) */
1135 if (comp == LSP_NEWER) {
1136 #ifndef FABRICD
1137 if (lsp->hdr.seqno < hdr.seqno) {
1138 /* send northbound
1139 * notification */
1140 circuit->area
1141 ->lsp_seqno_skipped_counter++;
1142 isis_notif_seqno_skipped(
1143 circuit, hdr.lsp_id);
1144 }
1145 #endif /* ifndef FABRICD */
1146 lsp_inc_seqno(lsp, hdr.seqno);
1147 lsp_flood_or_update(lsp, NULL,
1148 circuit_scoped);
1149 } else {
1150 isis_tx_queue_add(circuit->tx_queue,
1151 lsp, TX_LSP_NORMAL);
1152 ISIS_CLEAR_FLAG(lsp->SSNflags, circuit);
1153 }
1154 if (IS_DEBUG_UPDATE_PACKETS)
1155 zlog_debug(
1156 "ISIS-Upd (%s): (1) re-originating LSP %s new seq 0x%08x",
1157 circuit->area->area_tag,
1158 rawlspid_print(hdr.lsp_id),
1159 lsp->hdr.seqno);
1160 } else {
1161 /* our own LSP with 0 remaining life time */
1162 #ifndef FABRICD
1163 /* send northbound notification */
1164 isis_notif_own_lsp_purge(circuit, hdr.lsp_id);
1165 #endif /* ifndef FABRICD */
1166 }
1167 }
1168 goto out;
1169 }
1170 /* 7.3.15.1 c) - If this is our own lsp and we don't have it initiate a
1171 * purge */
1172 if (memcmp(hdr.lsp_id, circuit->isis->sysid, ISIS_SYS_ID_LEN) == 0) {
1173 if (!lsp) {
1174 /* 7.3.16.4: initiate a purge */
1175 lsp_purge_non_exist(level, &hdr, circuit->area);
1176 retval = ISIS_OK;
1177 goto out;
1178 }
1179 /* 7.3.15.1 d) - If this is our own lsp and we have it */
1180
1181 /* In 7.3.16.1, If an Intermediate system R somewhere in the
1182 * domain
1183 * has information that the current sequence number for source S
1184 * is
1185 * "greater" than that held by S, ... */
1186
1187 if (comp == LSP_NEWER) {
1188 /* 7.3.16.1 */
1189 lsp_inc_seqno(lsp, hdr.seqno);
1190 #ifndef FABRICD
1191 /* send northbound notification */
1192 circuit->area->lsp_seqno_skipped_counter++;
1193 isis_notif_seqno_skipped(circuit, hdr.lsp_id);
1194 #endif /* ifndef FABRICD */
1195 if (IS_DEBUG_UPDATE_PACKETS) {
1196 zlog_debug(
1197 "ISIS-Upd (%s): (2) re-originating LSP %s new seq 0x%08x",
1198 circuit->area->area_tag,
1199 rawlspid_print(hdr.lsp_id),
1200 lsp->hdr.seqno);
1201 }
1202 lsp_flood(lsp, NULL);
1203 } else if (comp == LSP_EQUAL) {
1204 isis_tx_queue_del(circuit->tx_queue, lsp);
1205 if (circuit->circ_type != CIRCUIT_T_BROADCAST)
1206 ISIS_SET_FLAG(lsp->SSNflags, circuit);
1207 } else {
1208 isis_tx_queue_add(circuit->tx_queue, lsp,
1209 TX_LSP_NORMAL);
1210 ISIS_CLEAR_FLAG(lsp->SSNflags, circuit);
1211 }
1212 } else {
1213 /* 7.3.15.1 e) - This lsp originated on another system */
1214
1215 /* 7.3.15.1 e) 1) LSP newer than the one in db or no LSP in db
1216 */
1217 if ((!lsp || comp == LSP_NEWER)) {
1218 /*
1219 * If this lsp is a frag, need to see if we have zero
1220 * lsp present
1221 */
1222 struct isis_lsp *lsp0 = NULL;
1223 if (LSP_FRAGMENT(hdr.lsp_id) != 0) {
1224 uint8_t lspid[ISIS_SYS_ID_LEN + 2];
1225 memcpy(lspid, hdr.lsp_id, ISIS_SYS_ID_LEN + 1);
1226 LSP_FRAGMENT(lspid) = 0;
1227 lsp0 = lsp_search(
1228 &circuit->area->lspdb[level - 1], lspid);
1229 if (!lsp0) {
1230 zlog_debug(
1231 "Got lsp frag, while zero lsp not in database");
1232 goto out;
1233 }
1234 }
1235 /* i */
1236 if (!lsp) {
1237 lsp = lsp_new_from_recv(
1238 &hdr, tlvs, circuit->rcv_stream, lsp0,
1239 circuit->area, level);
1240 tlvs = NULL;
1241 lsp_insert(&circuit->area->lspdb[level - 1],
1242 lsp);
1243 } else /* exists, so we overwrite */
1244 {
1245 lsp_update(lsp, &hdr, tlvs, circuit->rcv_stream,
1246 circuit->area, level, false);
1247 tlvs = NULL;
1248 }
1249 lsp_flood_or_update(lsp, circuit, circuit_scoped);
1250
1251 /* iv */
1252 if (circuit->circ_type != CIRCUIT_T_BROADCAST)
1253 ISIS_SET_FLAG(lsp->SSNflags, circuit);
1254 /* FIXME: v) */
1255 }
1256 /* 7.3.15.1 e) 2) LSP equal to the one in db */
1257 else if (comp == LSP_EQUAL) {
1258 isis_tx_queue_del(circuit->tx_queue, lsp);
1259 lsp_update(lsp, &hdr, tlvs, circuit->rcv_stream,
1260 circuit->area, level, false);
1261 tlvs = NULL;
1262 if (circuit->circ_type != CIRCUIT_T_BROADCAST)
1263 ISIS_SET_FLAG(lsp->SSNflags, circuit);
1264 }
1265 /* 7.3.15.1 e) 3) LSP older than the one in db */
1266 else {
1267 isis_tx_queue_add(circuit->tx_queue, lsp,
1268 TX_LSP_NORMAL);
1269 ISIS_CLEAR_FLAG(lsp->SSNflags, circuit);
1270 }
1271 }
1272
1273 retval = ISIS_OK;
1274
1275 out:
1276 fabricd_trigger_csnp(circuit->area, circuit_scoped);
1277
1278 isis_free_tlvs(tlvs);
1279 return retval;
1280 }
1281
1282 /*
1283 * Process Sequence Numbers
1284 * ISO - 10589
1285 * Section 7.3.15.2 - Action on receipt of a sequence numbers PDU
1286 */
1287
1288 static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit,
1289 const uint8_t *ssnpa)
1290 {
1291 #ifndef FABRICD
1292 size_t pdu_start = stream_get_getp(circuit->rcv_stream);
1293 size_t pdu_end = stream_get_endp(circuit->rcv_stream);
1294 char raw_pdu[pdu_end - pdu_start];
1295 #endif /* ifndef FABRICD */
1296
1297 bool is_csnp = (pdu_type == L1_COMPLETE_SEQ_NUM
1298 || pdu_type == L2_COMPLETE_SEQ_NUM);
1299 char typechar = is_csnp ? 'C' : 'P';
1300 int level = (pdu_type == L1_COMPLETE_SEQ_NUM
1301 || pdu_type == L1_PARTIAL_SEQ_NUM)
1302 ? ISIS_LEVEL1
1303 : ISIS_LEVEL2;
1304
1305 uint16_t pdu_len = stream_getw(circuit->rcv_stream);
1306 uint8_t rem_sys_id[ISIS_SYS_ID_LEN];
1307
1308 stream_get(rem_sys_id, circuit->rcv_stream, ISIS_SYS_ID_LEN);
1309 stream_forward_getp(circuit->rcv_stream, 1); /* Circuit ID - unused */
1310
1311 uint8_t start_lsp_id[ISIS_SYS_ID_LEN + 2] = {};
1312 uint8_t stop_lsp_id[ISIS_SYS_ID_LEN + 2] = {};
1313
1314 if (is_csnp) {
1315 stream_get(start_lsp_id, circuit->rcv_stream,
1316 ISIS_SYS_ID_LEN + 2);
1317 stream_get(stop_lsp_id, circuit->rcv_stream,
1318 ISIS_SYS_ID_LEN + 2);
1319 }
1320
1321 if (pdu_len_validate(pdu_len, circuit)) {
1322 zlog_warn("Received a CSNP with bogus length %d", pdu_len);
1323 return ISIS_WARNING;
1324 }
1325
1326 if (IS_DEBUG_SNP_PACKETS) {
1327 zlog_debug(
1328 "ISIS-Snp (%s): Rcvd L%d %cSNP on %s, cirType %s, cirID %u",
1329 circuit->area->area_tag, level, typechar,
1330 circuit->interface->name,
1331 circuit_t2string(circuit->is_type),
1332 circuit->circuit_id);
1333 if (IS_DEBUG_PACKET_DUMP)
1334 zlog_dump_data(STREAM_DATA(circuit->rcv_stream),
1335 stream_get_endp(circuit->rcv_stream));
1336 }
1337
1338 /* 7.3.15.2 a) 1 - external domain circuit will discard snp pdu */
1339 if (circuit->ext_domain) {
1340
1341 zlog_debug(
1342 "ISIS-Snp (%s): Rcvd L%d %cSNP on %s, skipping: circuit externalDomain = true",
1343 circuit->area->area_tag, level, typechar,
1344 circuit->interface->name);
1345
1346 return ISIS_OK;
1347 }
1348
1349 /* 7.3.15.2 a) 2,3 - manualL2OnlyMode not implemented */
1350 if (!(circuit->is_type & level)) {
1351 zlog_debug(
1352 "ISIS-Snp (%s): Rcvd L%d %cSNP on %s, skipping: circuit type %s does not match level %d",
1353 circuit->area->area_tag, level, typechar,
1354 circuit->interface->name,
1355 circuit_t2string(circuit->is_type), level);
1356
1357 return ISIS_OK;
1358 }
1359
1360 /* 7.3.15.2 a) 4 - not applicable for CSNP only PSNPs on broadcast */
1361 if (!is_csnp && (circuit->circ_type == CIRCUIT_T_BROADCAST)
1362 && !circuit->u.bc.is_dr[level - 1]) {
1363 zlog_debug(
1364 "ISIS-Snp (%s): Rcvd L%d %cSNP from %s on %s, skipping: we are not the DIS",
1365 circuit->area->area_tag, level, typechar,
1366 snpa_print(ssnpa), circuit->interface->name);
1367
1368 return ISIS_OK;
1369 }
1370
1371 /* 7.3.15.2 a) 5 - need to make sure IDLength matches - already checked
1372 */
1373
1374 /* 7.3.15.2 a) 6 - maximum area match, can be ommited since we only use
1375 * 3
1376 * - already checked */
1377
1378 /* 7.3.15.2 a) 7 - Must check that we have an adjacency of the same
1379 * level */
1380 /* for broadcast circuits, snpa should be compared */
1381 /* FIXME : Do we need to check SNPA? */
1382 if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
1383 if (!isis_adj_lookup(rem_sys_id,
1384 circuit->u.bc.adjdb[level - 1]))
1385 return ISIS_OK; /* Silently discard */
1386 } else {
1387 if (!fabricd && !circuit->u.p2p.neighbor) {
1388 zlog_warn("no p2p neighbor on circuit %s",
1389 circuit->interface->name);
1390 return ISIS_OK; /* Silently discard */
1391 }
1392 }
1393
1394 struct isis_tlvs *tlvs;
1395 int retval = ISIS_WARNING;
1396 const char *error_log;
1397
1398 if (isis_unpack_tlvs(STREAM_READABLE(circuit->rcv_stream),
1399 circuit->rcv_stream, &tlvs, &error_log)) {
1400 zlog_warn("Something went wrong unpacking the SNP: %s",
1401 error_log);
1402 goto out;
1403 }
1404
1405 struct isis_passwd *passwd = (level == IS_LEVEL_1)
1406 ? &circuit->area->area_passwd
1407 : &circuit->area->domain_passwd;
1408
1409 if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_RECV)) {
1410 int auth_code = isis_tlvs_auth_is_valid(
1411 tlvs, passwd, circuit->rcv_stream, false);
1412 if (auth_code != ISIS_AUTH_OK) {
1413 isis_event_auth_failure(circuit->area->area_tag,
1414 "SNP authentication failure",
1415 rem_sys_id);
1416 #ifndef FABRICD
1417 /* send northbound notification */
1418 stream_get_from(raw_pdu, circuit->rcv_stream, pdu_start,
1419 pdu_end - pdu_start);
1420 if (auth_code == ISIS_AUTH_FAILURE) {
1421 circuit->auth_failures++;
1422 if (circuit->is_type == IS_LEVEL_1) {
1423 circuit->area->auth_failures[0]++;
1424 } else if (circuit->is_type == IS_LEVEL_2) {
1425 circuit->area->auth_failures[1]++;
1426 } else {
1427 circuit->area->auth_failures[0]++;
1428 circuit->area->auth_failures[1]++;
1429 }
1430 isis_notif_authentication_failure(
1431 circuit, raw_pdu, sizeof(raw_pdu));
1432 } else { /* AUTH_TYPE_FAILURE or NO_VALIDATOR */
1433 circuit->auth_type_failures++;
1434 if (circuit->is_type == IS_LEVEL_1) {
1435 circuit->area->auth_type_failures[0]++;
1436 } else if (circuit->is_type == IS_LEVEL_2) {
1437 circuit->area->auth_type_failures[1]++;
1438 } else {
1439 circuit->area->auth_type_failures[0]++;
1440 circuit->area->auth_type_failures[1]++;
1441 }
1442 isis_notif_authentication_type_failure(
1443 circuit, raw_pdu, sizeof(raw_pdu));
1444 }
1445 #endif /* ifndef FABRICD */
1446 goto out;
1447 }
1448 }
1449
1450 struct isis_lsp_entry *entry_head =
1451 (struct isis_lsp_entry *)tlvs->lsp_entries.head;
1452
1453 /* debug isis snp-packets */
1454 if (IS_DEBUG_SNP_PACKETS) {
1455 zlog_debug("ISIS-Snp (%s): Rcvd L%d %cSNP from %s on %s",
1456 circuit->area->area_tag, level, typechar,
1457 snpa_print(ssnpa), circuit->interface->name);
1458 for (struct isis_lsp_entry *entry = entry_head; entry;
1459 entry = entry->next) {
1460 zlog_debug(
1461 "ISIS-Snp (%s): %cSNP entry %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus",
1462 circuit->area->area_tag, typechar,
1463 rawlspid_print(entry->id), entry->seqno,
1464 entry->checksum, entry->rem_lifetime);
1465 }
1466 }
1467
1468 bool resync_needed = false;
1469
1470 /* 7.3.15.2 b) Actions on LSP_ENTRIES reported */
1471 for (struct isis_lsp_entry *entry = entry_head; entry;
1472 entry = entry->next) {
1473 struct isis_lsp *lsp =
1474 lsp_search(&circuit->area->lspdb[level - 1], entry->id);
1475 bool own_lsp = !memcmp(entry->id, circuit->isis->sysid,
1476 ISIS_SYS_ID_LEN);
1477 if (lsp) {
1478 /* 7.3.15.2 b) 1) is this LSP newer */
1479 int cmp = lsp_compare(circuit->area->area_tag, lsp,
1480 entry->seqno, entry->checksum,
1481 entry->rem_lifetime);
1482 /* 7.3.15.2 b) 2) if it equals, clear SRM on p2p */
1483 if (cmp == LSP_EQUAL) {
1484 /* if (circuit->circ_type !=
1485 * CIRCUIT_T_BROADCAST) */
1486 isis_tx_queue_del(circuit->tx_queue, lsp);
1487 }
1488 /* 7.3.15.2 b) 3) if it is older, clear SSN and set SRM
1489 */
1490 else if (cmp == LSP_OLDER) {
1491 ISIS_CLEAR_FLAG(lsp->SSNflags, circuit);
1492 isis_tx_queue_add(circuit->tx_queue, lsp,
1493 TX_LSP_NORMAL);
1494 }
1495 /* 7.3.15.2 b) 4) if it is newer, set SSN and clear SRM
1496 on p2p */
1497 else {
1498 if (own_lsp) {
1499 lsp_inc_seqno(lsp, entry->seqno);
1500 isis_tx_queue_add(circuit->tx_queue, lsp,
1501 TX_LSP_NORMAL);
1502 } else {
1503 ISIS_SET_FLAG(lsp->SSNflags, circuit);
1504 /* if (circuit->circ_type !=
1505 * CIRCUIT_T_BROADCAST) */
1506 isis_tx_queue_del(circuit->tx_queue, lsp);
1507 resync_needed = true;
1508 }
1509 }
1510 } else {
1511 /* 7.3.15.2 b) 5) if it was not found, and all of those
1512 * are not 0,
1513 * insert it and set SSN on it */
1514 if (entry->rem_lifetime && entry->checksum
1515 && entry->seqno
1516 && memcmp(entry->id, circuit->isis->sysid,
1517 ISIS_SYS_ID_LEN)) {
1518 struct isis_lsp *lsp0 = NULL;
1519
1520 if (LSP_FRAGMENT(entry->id)) {
1521 uint8_t lspid[ISIS_SYS_ID_LEN + 2];
1522
1523 memcpy(lspid, entry->id,
1524 ISIS_SYS_ID_LEN + 1);
1525 LSP_FRAGMENT(lspid) = 0;
1526 lsp0 = lsp_search(
1527 &circuit->area->lspdb[level - 1],
1528 lspid);
1529 if (!lsp0) {
1530 zlog_debug("Got lsp frag in snp, while zero not in database");
1531 continue;
1532 }
1533 }
1534 lsp = lsp_new(circuit->area, entry->id,
1535 entry->rem_lifetime, 0, 0,
1536 entry->checksum, lsp0, level);
1537 lsp_insert(&circuit->area->lspdb[level - 1],
1538 lsp);
1539
1540 lsp_set_all_srmflags(lsp, false);
1541 ISIS_SET_FLAG(lsp->SSNflags, circuit);
1542 resync_needed = true;
1543 }
1544 }
1545 }
1546
1547 /* 7.3.15.2 c) on CSNP set SRM for all in range which were not reported
1548 */
1549 if (is_csnp) {
1550 /*
1551 * Build a list from our own LSP db bounded with
1552 * start_lsp_id and stop_lsp_id
1553 */
1554 struct list *lsp_list = list_new();
1555 lsp_build_list_nonzero_ht(&circuit->area->lspdb[level - 1],
1556 start_lsp_id, stop_lsp_id, lsp_list);
1557
1558 /* Fixme: Find a better solution */
1559 struct listnode *node, *nnode;
1560 struct isis_lsp *lsp;
1561 for (struct isis_lsp_entry *entry = entry_head; entry;
1562 entry = entry->next) {
1563 for (ALL_LIST_ELEMENTS(lsp_list, node, nnode, lsp)) {
1564 if (lsp_id_cmp(lsp->hdr.lsp_id, entry->id)
1565 == 0) {
1566 list_delete_node(lsp_list, node);
1567 break;
1568 }
1569 }
1570 }
1571
1572 /* on remaining LSPs we set SRM (neighbor knew not of) */
1573 for (ALL_LIST_ELEMENTS_RO(lsp_list, node, lsp)) {
1574 isis_tx_queue_add(circuit->tx_queue, lsp, TX_LSP_NORMAL);
1575 resync_needed = true;
1576 }
1577
1578 /* lets free it */
1579 list_delete(&lsp_list);
1580 }
1581
1582 if (fabricd_initial_sync_is_complete(circuit->area) && resync_needed)
1583 zlog_warn("OpenFabric: Needed to resync LSPDB using CSNP!");
1584
1585 retval = ISIS_OK;
1586 out:
1587 isis_free_tlvs(tlvs);
1588 return retval;
1589 }
1590
1591 static int pdu_size(uint8_t pdu_type, uint8_t *size)
1592 {
1593 switch (pdu_type) {
1594 case L1_LAN_HELLO:
1595 case L2_LAN_HELLO:
1596 *size = ISIS_LANHELLO_HDRLEN;
1597 break;
1598 case P2P_HELLO:
1599 *size = ISIS_P2PHELLO_HDRLEN;
1600 break;
1601 case L1_LINK_STATE:
1602 case L2_LINK_STATE:
1603 case FS_LINK_STATE:
1604 *size = ISIS_LSP_HDR_LEN;
1605 break;
1606 case L1_COMPLETE_SEQ_NUM:
1607 case L2_COMPLETE_SEQ_NUM:
1608 *size = ISIS_CSNP_HDRLEN;
1609 break;
1610 case L1_PARTIAL_SEQ_NUM:
1611 case L2_PARTIAL_SEQ_NUM:
1612 *size = ISIS_PSNP_HDRLEN;
1613 break;
1614 default:
1615 return 1;
1616 }
1617 *size += ISIS_FIXED_HDR_LEN;
1618 return 0;
1619 }
1620
1621 /*
1622 * PDU Dispatcher
1623 */
1624
1625 int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa)
1626 {
1627 int retval = ISIS_OK;
1628 size_t pdu_start = stream_get_getp(circuit->rcv_stream);
1629 size_t pdu_end = stream_get_endp(circuit->rcv_stream);
1630 char raw_pdu[pdu_end - pdu_start];
1631
1632 stream_get_from(raw_pdu, circuit->rcv_stream, pdu_start,
1633 pdu_end - pdu_start);
1634
1635 /* Verify that at least the 8 bytes fixed header have been received */
1636 if (stream_get_endp(circuit->rcv_stream) < ISIS_FIXED_HDR_LEN) {
1637 flog_err(EC_ISIS_PACKET, "PDU is too short to be IS-IS.");
1638 return ISIS_ERROR;
1639 }
1640
1641 uint8_t idrp = stream_getc(circuit->rcv_stream);
1642 uint8_t length = stream_getc(circuit->rcv_stream);
1643 uint8_t version1 = stream_getc(circuit->rcv_stream);
1644 uint8_t id_len = stream_getc(circuit->rcv_stream);
1645 uint8_t pdu_type = stream_getc(circuit->rcv_stream)
1646 & 0x1f; /* bits 6-8 are reserved */
1647 uint8_t version2 = stream_getc(circuit->rcv_stream);
1648
1649 stream_forward_getp(circuit->rcv_stream, 1); /* reserved */
1650 uint8_t max_area_addrs = stream_getc(circuit->rcv_stream);
1651
1652 pdu_counter_count(circuit->area->pdu_rx_counters, pdu_type);
1653
1654 if (idrp == ISO9542_ESIS) {
1655 flog_err(EC_LIB_DEVELOPMENT,
1656 "No support for ES-IS packet IDRP=%hhx", idrp);
1657 pdu_counter_count(circuit->area->pdu_drop_counters, pdu_type);
1658 return ISIS_ERROR;
1659 }
1660
1661 if (idrp != ISO10589_ISIS) {
1662 flog_err(EC_ISIS_PACKET, "Not an IS-IS packet IDRP=%hhx",
1663 idrp);
1664 pdu_counter_count(circuit->area->pdu_drop_counters, pdu_type);
1665 return ISIS_ERROR;
1666 }
1667
1668 if (version1 != 1) {
1669 zlog_warn("Unsupported ISIS version %hhu", version1);
1670 #ifndef FABRICD
1671 /* send northbound notification */
1672 isis_notif_version_skew(circuit, version1, raw_pdu,
1673 sizeof(raw_pdu));
1674 #endif /* ifndef FABRICD */
1675 pdu_counter_count(circuit->area->pdu_drop_counters, pdu_type);
1676 return ISIS_WARNING;
1677 }
1678
1679 if (id_len != 0 && id_len != ISIS_SYS_ID_LEN) {
1680 flog_err(
1681 EC_ISIS_PACKET,
1682 "IDFieldLengthMismatch: ID Length field in a received PDU %hhu, while the parameter for this IS is %u",
1683 id_len, ISIS_SYS_ID_LEN);
1684 circuit->id_len_mismatches++;
1685 if (circuit->is_type == IS_LEVEL_1) {
1686 circuit->area->id_len_mismatches[0]++;
1687 } else if (circuit->is_type == IS_LEVEL_2) {
1688 circuit->area->id_len_mismatches[1]++;
1689 } else {
1690 circuit->area->id_len_mismatches[0]++;
1691 circuit->area->id_len_mismatches[1]++;
1692 }
1693
1694 #ifndef FABRICD
1695 /* send northbound notification */
1696 isis_notif_id_len_mismatch(circuit, id_len, raw_pdu,
1697 sizeof(raw_pdu));
1698 #endif /* ifndef FABRICD */
1699 pdu_counter_count(circuit->area->pdu_drop_counters, pdu_type);
1700 return ISIS_ERROR;
1701 }
1702
1703 uint8_t expected_length;
1704 if (pdu_size(pdu_type, &expected_length)) {
1705 zlog_warn("Unsupported ISIS PDU %hhu", pdu_type);
1706 pdu_counter_count(circuit->area->pdu_drop_counters, pdu_type);
1707 return ISIS_WARNING;
1708 }
1709
1710 if (length != expected_length) {
1711 flog_err(EC_ISIS_PACKET,
1712 "Expected fixed header length = %hhu but got %hhu",
1713 expected_length, length);
1714 pdu_counter_count(circuit->area->pdu_drop_counters, pdu_type);
1715 return ISIS_ERROR;
1716 }
1717
1718 if (stream_get_endp(circuit->rcv_stream) < length) {
1719 flog_err(
1720 EC_ISIS_PACKET,
1721 "PDU is too short to contain fixed header of given PDU type.");
1722 pdu_counter_count(circuit->area->pdu_drop_counters, pdu_type);
1723 return ISIS_ERROR;
1724 }
1725
1726 if (version2 != 1) {
1727 zlog_warn("Unsupported ISIS PDU version %hhu", version2);
1728 #ifndef FABRICD
1729 /* send northbound notification */
1730 isis_notif_version_skew(circuit, version2, raw_pdu,
1731 sizeof(raw_pdu));
1732 #endif /* ifndef FABRICD */
1733 pdu_counter_count(circuit->area->pdu_drop_counters, pdu_type);
1734 return ISIS_WARNING;
1735 }
1736
1737 if (circuit->is_passive) {
1738 zlog_warn("Received ISIS PDU on passive circuit %s",
1739 circuit->interface->name);
1740 pdu_counter_count(circuit->area->pdu_drop_counters, pdu_type);
1741 return ISIS_WARNING;
1742 }
1743
1744 /* either 3 or 0 */
1745 if (pdu_type != FS_LINK_STATE /* FS PDU doesn't contain max area addr
1746 field */
1747 && max_area_addrs != 0
1748 && max_area_addrs != circuit->isis->max_area_addrs) {
1749 flog_err(
1750 EC_ISIS_PACKET,
1751 "maximumAreaAddressesMismatch: maximumAreaAdresses in a received PDU %hhu while the parameter for this IS is %u",
1752 max_area_addrs, circuit->isis->max_area_addrs);
1753 circuit->max_area_addr_mismatches++;
1754 #ifndef FABRICD
1755 /* send northbound notification */
1756 isis_notif_max_area_addr_mismatch(circuit, max_area_addrs,
1757 raw_pdu, sizeof(raw_pdu));
1758 #endif /* ifndef FABRICD */
1759 pdu_counter_count(circuit->area->pdu_drop_counters, pdu_type);
1760 return ISIS_ERROR;
1761 }
1762
1763 switch (pdu_type) {
1764 case L1_LAN_HELLO:
1765 case L2_LAN_HELLO:
1766 case P2P_HELLO:
1767 if (fabricd && pdu_type != P2P_HELLO) {
1768 pdu_counter_count(circuit->area->pdu_drop_counters,
1769 pdu_type);
1770 return ISIS_ERROR;
1771 }
1772
1773 retval = process_hello(pdu_type, circuit, ssnpa);
1774 break;
1775 case L1_LINK_STATE:
1776 case L2_LINK_STATE:
1777 case FS_LINK_STATE:
1778 if (fabricd && pdu_type != L2_LINK_STATE &&
1779 pdu_type != FS_LINK_STATE) {
1780 pdu_counter_count(circuit->area->pdu_drop_counters,
1781 pdu_type);
1782 return ISIS_ERROR;
1783 }
1784
1785 retval = process_lsp(pdu_type, circuit, ssnpa, max_area_addrs);
1786 break;
1787 case L1_COMPLETE_SEQ_NUM:
1788 case L2_COMPLETE_SEQ_NUM:
1789 case L1_PARTIAL_SEQ_NUM:
1790 case L2_PARTIAL_SEQ_NUM:
1791 retval = process_snp(pdu_type, circuit, ssnpa);
1792 break;
1793 default:
1794 pdu_counter_count(circuit->area->pdu_drop_counters, pdu_type);
1795 return ISIS_ERROR;
1796 }
1797
1798 if (retval != ISIS_OK)
1799 pdu_counter_count(circuit->area->pdu_drop_counters, pdu_type);
1800
1801 return retval;
1802 }
1803
1804 void isis_receive(struct event *thread)
1805 {
1806 struct isis_circuit *circuit;
1807 uint8_t ssnpa[ETH_ALEN];
1808
1809 /*
1810 * Get the circuit
1811 */
1812 circuit = EVENT_ARG(thread);
1813 assert(circuit);
1814
1815 circuit->t_read = NULL;
1816
1817 isis_circuit_stream(circuit, &circuit->rcv_stream);
1818
1819 #if ISIS_METHOD != ISIS_METHOD_BPF
1820 int retval;
1821
1822 retval = circuit->rx(circuit, ssnpa);
1823
1824 if (retval == ISIS_OK)
1825 isis_handle_pdu(circuit, ssnpa);
1826 #else // ISIS_METHOD != ISIS_METHOD_BPF
1827 circuit->rx(circuit, ssnpa);
1828 #endif
1829
1830 /*
1831 * prepare for next packet.
1832 */
1833 if (!circuit->is_passive)
1834 isis_circuit_prepare(circuit);
1835 }
1836
1837 /*
1838 * SEND SIDE
1839 */
1840 void fill_fixed_hdr(uint8_t pdu_type, struct stream *stream)
1841 {
1842 uint8_t length;
1843
1844 if (pdu_size(pdu_type, &length))
1845 assert(!"Unknown PDU Type");
1846
1847 stream_putc(stream, ISO10589_ISIS); /* IDRP */
1848 stream_putc(stream, length); /* Length of fixed header */
1849 stream_putc(stream, 1); /* Version/Protocol ID Extension 1 */
1850 stream_putc(stream, 0); /* ID Length, 0 => 6 */
1851 stream_putc(stream, pdu_type);
1852 stream_putc(stream, 1); /* Subversion */
1853 stream_putc(stream, 0); /* Reserved */
1854 stream_putc(stream, 0); /* Max Area Addresses 0 => 3 */
1855 }
1856
1857 static uint8_t hello_pdu_type(struct isis_circuit *circuit, int level)
1858 {
1859 if (circuit->circ_type == CIRCUIT_T_BROADCAST)
1860 return (level == IS_LEVEL_1) ? L1_LAN_HELLO : L2_LAN_HELLO;
1861 else
1862 return P2P_HELLO;
1863 }
1864
1865 static void put_hello_hdr(struct isis_circuit *circuit, int level,
1866 size_t *len_pointer)
1867 {
1868 uint8_t pdu_type = hello_pdu_type(circuit, level);
1869
1870 isis_circuit_stream(circuit, &circuit->snd_stream);
1871 fill_fixed_hdr(pdu_type, circuit->snd_stream);
1872
1873 stream_putc(circuit->snd_stream, circuit->is_type);
1874 stream_put(circuit->snd_stream, circuit->isis->sysid, ISIS_SYS_ID_LEN);
1875
1876 uint32_t holdtime = circuit->hello_multiplier[level - 1]
1877 * circuit->hello_interval[level - 1];
1878
1879 if (holdtime > 0xffff)
1880 holdtime = 0xffff;
1881
1882 stream_putw(circuit->snd_stream, holdtime);
1883 *len_pointer = stream_get_endp(circuit->snd_stream);
1884 stream_putw(circuit->snd_stream, 0); /* length is filled in later */
1885
1886 if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
1887 uint8_t *desig_is = (level == IS_LEVEL_1)
1888 ? circuit->u.bc.l1_desig_is
1889 : circuit->u.bc.l2_desig_is;
1890 stream_putc(circuit->snd_stream, circuit->priority[level - 1]);
1891 stream_put(circuit->snd_stream, desig_is, ISIS_SYS_ID_LEN + 1);
1892 } else {
1893 stream_putc(circuit->snd_stream, circuit->circuit_id);
1894 }
1895 }
1896
1897 int send_hello(struct isis_circuit *circuit, int level)
1898 {
1899 size_t len_pointer;
1900 int retval;
1901
1902 if (circuit->is_passive)
1903 return ISIS_OK;
1904
1905 if (circuit->interface->mtu == 0) {
1906 zlog_warn("circuit has zero MTU");
1907 return ISIS_WARNING;
1908 }
1909
1910 put_hello_hdr(circuit, level, &len_pointer);
1911
1912 struct isis_tlvs *tlvs = isis_alloc_tlvs();
1913
1914 isis_tlvs_add_auth(tlvs, &circuit->passwd);
1915
1916 if (!listcount(circuit->area->area_addrs)) {
1917 isis_free_tlvs(tlvs);
1918 return ISIS_WARNING;
1919 }
1920
1921 isis_tlvs_add_area_addresses(tlvs, circuit->area->area_addrs);
1922
1923 if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
1924 isis_tlvs_add_lan_neighbors(
1925 tlvs, circuit->u.bc.lan_neighs[level - 1]);
1926 } else if (circuit->circ_type == CIRCUIT_T_P2P
1927 && !circuit->disable_threeway_adj) {
1928 uint32_t ext_circuit_id = circuit->idx;
1929 if (circuit->u.p2p.neighbor) {
1930 uint8_t threeway_state;
1931
1932 if (fabricd_initial_sync_is_in_progress(circuit->area)
1933 && fabricd_initial_sync_circuit(circuit->area) != circuit)
1934 threeway_state = ISIS_THREEWAY_DOWN;
1935 else
1936 threeway_state = circuit->u.p2p.neighbor->threeway_state;
1937 isis_tlvs_add_threeway_adj(tlvs,
1938 threeway_state,
1939 ext_circuit_id,
1940 circuit->u.p2p.neighbor->sysid,
1941 circuit->u.p2p.neighbor->ext_circuit_id);
1942 } else {
1943 isis_tlvs_add_threeway_adj(tlvs,
1944 ISIS_THREEWAY_DOWN,
1945 ext_circuit_id,
1946 NULL, 0);
1947 }
1948 }
1949
1950 isis_tlvs_set_protocols_supported(tlvs, &circuit->nlpids);
1951
1952 /*
1953 * MT Supported TLV
1954 *
1955 * TLV gets included if no topology is enabled on the interface,
1956 * if one topology other than #0 is enabled, or if multiple topologies
1957 * are enabled.
1958 */
1959 struct isis_circuit_mt_setting **mt_settings;
1960 unsigned int mt_count;
1961
1962 mt_settings = circuit_mt_settings(circuit, &mt_count);
1963 if (mt_count == 0 && area_is_mt(circuit->area)) {
1964 tlvs->mt_router_info_empty = true;
1965 } else if ((mt_count == 1
1966 && mt_settings[0]->mtid != ISIS_MT_IPV4_UNICAST)
1967 || (mt_count > 1)) {
1968 for (unsigned int i = 0; i < mt_count; i++)
1969 isis_tlvs_add_mt_router_info(tlvs, mt_settings[i]->mtid,
1970 false, false);
1971 }
1972
1973 if (circuit->ip_router) {
1974 struct list *circuit_ip_addrs = fabricd_ip_addrs(circuit);
1975
1976 if (circuit_ip_addrs)
1977 isis_tlvs_add_ipv4_addresses(tlvs, circuit_ip_addrs);
1978 }
1979
1980 if (circuit->ipv6_router)
1981 isis_tlvs_add_ipv6_addresses(tlvs, circuit->ipv6_link);
1982
1983 /* RFC6119 section 4 define TLV 233 to provide Global IPv6 address */
1984 if (circuit->ipv6_router)
1985 isis_tlvs_add_global_ipv6_addresses(tlvs,
1986 circuit->ipv6_non_link);
1987
1988 bool should_pad_hello =
1989 circuit->pad_hellos == ISIS_HELLO_PADDING_ALWAYS ||
1990 (circuit->pad_hellos ==
1991 ISIS_HELLO_PADDING_DURING_ADJACENCY_FORMATION &&
1992 circuit->upadjcount[0] + circuit->upadjcount[1] == 0);
1993
1994 if (isis_pack_tlvs(tlvs, circuit->snd_stream, len_pointer,
1995 should_pad_hello, false)) {
1996 isis_free_tlvs(tlvs);
1997 return ISIS_WARNING; /* XXX: Maybe Log TLV structure? */
1998 }
1999
2000 if (IS_DEBUG_ADJ_PACKETS) {
2001 if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
2002 zlog_debug(
2003 "ISIS-Adj (%s): Sending L%d LAN IIH on %s, length %zd",
2004 circuit->area->area_tag, level,
2005 circuit->interface->name,
2006 stream_get_endp(circuit->snd_stream));
2007 } else {
2008 zlog_debug(
2009 "ISIS-Adj (%s): Sending P2P IIH on %s, length %zd",
2010 circuit->area->area_tag,
2011 circuit->interface->name,
2012 stream_get_endp(circuit->snd_stream));
2013 }
2014 if (IS_DEBUG_PACKET_DUMP)
2015 zlog_dump_data(STREAM_DATA(circuit->snd_stream),
2016 stream_get_endp(circuit->snd_stream));
2017 }
2018
2019 isis_free_tlvs(tlvs);
2020
2021 pdu_counter_count(circuit->area->pdu_tx_counters,
2022 hello_pdu_type(circuit, level));
2023 retval = circuit->tx(circuit, level);
2024 if (retval != ISIS_OK)
2025 flog_err(EC_ISIS_PACKET,
2026 "ISIS-Adj (%s): Send L%d IIH on %s failed",
2027 circuit->area->area_tag, level,
2028 circuit->interface->name);
2029
2030 return retval;
2031 }
2032
2033 static void send_hello_cb(struct event *thread)
2034 {
2035 struct isis_circuit_arg *arg = EVENT_ARG(thread);
2036 assert(arg);
2037
2038 struct isis_circuit *circuit = arg->circuit;
2039 int level = arg->level;
2040
2041 assert(circuit);
2042
2043 if (circuit->circ_type == CIRCUIT_T_P2P) {
2044 circuit->u.p2p.t_send_p2p_hello = NULL;
2045 send_hello(circuit, 1);
2046 send_hello_sched(circuit, ISIS_LEVEL1,
2047 1000 * circuit->hello_interval[1]);
2048 return;
2049 }
2050
2051 if (circuit->circ_type != CIRCUIT_T_BROADCAST) {
2052 zlog_warn("ISIS-Hello (%s): Trying to send hello on unknown circuit type %d",
2053 circuit->area->area_tag, circuit->circ_type);
2054 return;
2055 }
2056
2057 circuit->u.bc.t_send_lan_hello[level - 1] = NULL;
2058 if (!(circuit->is_type & level)) {
2059 zlog_warn("ISIS-Hello (%s): Trying to send L%d IIH in L%d-only circuit",
2060 circuit->area->area_tag, level, 3 - level);
2061 return;
2062 }
2063
2064 if (circuit->u.bc.run_dr_elect[level - 1])
2065 isis_dr_elect(circuit, level);
2066
2067 send_hello(circuit, level);
2068
2069 /* set next timer thread */
2070 send_hello_sched(circuit, level, 1000 * circuit->hello_interval[level - 1]);
2071 }
2072
2073 static void _send_hello_sched(struct isis_circuit *circuit,
2074 struct event **threadp, int level, long delay)
2075 {
2076 if (*threadp) {
2077 if (event_timer_remain_msec(*threadp) < (unsigned long)delay)
2078 return;
2079
2080 EVENT_OFF(*threadp);
2081 }
2082
2083 event_add_timer_msec(master, send_hello_cb,
2084 &circuit->level_arg[level - 1],
2085 isis_jitter(delay, IIH_JITTER), threadp);
2086 }
2087
2088 void send_hello_sched(struct isis_circuit *circuit, int level, long delay)
2089 {
2090 if (circuit->circ_type == CIRCUIT_T_P2P) {
2091 _send_hello_sched(circuit, &circuit->u.p2p.t_send_p2p_hello,
2092 ISIS_LEVEL1, delay);
2093 return;
2094 }
2095
2096 if (circuit->circ_type != CIRCUIT_T_BROADCAST) {
2097 zlog_warn("%s: encountered unknown circuit type %d on %s",
2098 __func__, circuit->circ_type,
2099 circuit->interface->name);
2100 return;
2101 }
2102
2103 for (int loop_level = ISIS_LEVEL1; loop_level <= ISIS_LEVEL2; loop_level++) {
2104 if (!(loop_level & level))
2105 continue;
2106
2107 _send_hello_sched(
2108 circuit,
2109 &circuit->u.bc.t_send_lan_hello[loop_level - 1],
2110 loop_level,
2111 delay
2112 );
2113 }
2114 }
2115
2116
2117 /*
2118 * Count the maximum number of lsps that can be accommodated by a given size.
2119 */
2120 #define LSP_ENTRIES_LEN (10 + ISIS_SYS_ID_LEN)
2121 static uint16_t get_max_lsp_count(uint16_t size)
2122 {
2123 uint16_t tlv_count;
2124 uint16_t lsp_count;
2125 uint16_t remaining_size;
2126
2127 /* First count the full size TLVs */
2128 tlv_count = size / MAX_LSP_ENTRIES_TLV_SIZE;
2129 lsp_count = tlv_count * (MAX_LSP_ENTRIES_TLV_SIZE / LSP_ENTRIES_LEN);
2130
2131 /* The last TLV, if any */
2132 remaining_size = size % MAX_LSP_ENTRIES_TLV_SIZE;
2133 if (remaining_size - 2 >= LSP_ENTRIES_LEN)
2134 lsp_count += (remaining_size - 2) / LSP_ENTRIES_LEN;
2135
2136 return lsp_count;
2137 }
2138
2139 int send_csnp(struct isis_circuit *circuit, int level)
2140 {
2141 if (lspdb_count(&circuit->area->lspdb[level - 1]) == 0)
2142 return ISIS_OK;
2143
2144 uint8_t pdu_type = (level == ISIS_LEVEL1) ? L1_COMPLETE_SEQ_NUM
2145 : L2_COMPLETE_SEQ_NUM;
2146
2147 isis_circuit_stream(circuit, &circuit->snd_stream);
2148 fill_fixed_hdr(pdu_type, circuit->snd_stream);
2149
2150 size_t len_pointer = stream_get_endp(circuit->snd_stream);
2151
2152 stream_putw(circuit->snd_stream, 0);
2153 stream_put(circuit->snd_stream, circuit->isis->sysid, ISIS_SYS_ID_LEN);
2154 /* with zero circuit id - ref 9.10, 9.11 */
2155 stream_putc(circuit->snd_stream, 0);
2156
2157 size_t start_pointer = stream_get_endp(circuit->snd_stream);
2158 stream_put(circuit->snd_stream, 0, ISIS_SYS_ID_LEN + 2);
2159 size_t end_pointer = stream_get_endp(circuit->snd_stream);
2160 stream_put(circuit->snd_stream, 0, ISIS_SYS_ID_LEN + 2);
2161
2162 struct isis_passwd *passwd = (level == ISIS_LEVEL1)
2163 ? &circuit->area->area_passwd
2164 : &circuit->area->domain_passwd;
2165
2166 struct isis_tlvs *tlvs = isis_alloc_tlvs();
2167
2168 if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND))
2169 isis_tlvs_add_auth(tlvs, passwd);
2170
2171 size_t tlv_start = stream_get_endp(circuit->snd_stream);
2172 if (isis_pack_tlvs(tlvs, circuit->snd_stream, len_pointer, false,
2173 false)) {
2174 isis_free_tlvs(tlvs);
2175 return ISIS_WARNING;
2176 }
2177 isis_free_tlvs(tlvs);
2178
2179 uint16_t num_lsps =
2180 get_max_lsp_count(STREAM_WRITEABLE(circuit->snd_stream));
2181
2182 uint8_t start[ISIS_SYS_ID_LEN + 2];
2183 memset(start, 0x00, ISIS_SYS_ID_LEN + 2);
2184 uint8_t stop[ISIS_SYS_ID_LEN + 2];
2185 memset(stop, 0xff, ISIS_SYS_ID_LEN + 2);
2186
2187 bool loop = true;
2188 while (loop) {
2189 tlvs = isis_alloc_tlvs();
2190 if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND))
2191 isis_tlvs_add_auth(tlvs, passwd);
2192
2193 struct isis_lsp *last_lsp;
2194 isis_tlvs_add_csnp_entries(tlvs, start, stop, num_lsps,
2195 &circuit->area->lspdb[level - 1],
2196 &last_lsp);
2197 /*
2198 * Update the stop lsp_id before encoding this CSNP.
2199 */
2200 if (tlvs->lsp_entries.count < num_lsps) {
2201 memset(stop, 0xff, ISIS_SYS_ID_LEN + 2);
2202 } else {
2203 memcpy(stop, last_lsp->hdr.lsp_id, sizeof(stop));
2204 }
2205
2206 memcpy(STREAM_DATA(circuit->snd_stream) + start_pointer, start,
2207 ISIS_SYS_ID_LEN + 2);
2208 memcpy(STREAM_DATA(circuit->snd_stream) + end_pointer, stop,
2209 ISIS_SYS_ID_LEN + 2);
2210 stream_set_endp(circuit->snd_stream, tlv_start);
2211 if (isis_pack_tlvs(tlvs, circuit->snd_stream, len_pointer,
2212 false, false)) {
2213 isis_free_tlvs(tlvs);
2214 return ISIS_WARNING;
2215 }
2216
2217 if (IS_DEBUG_SNP_PACKETS) {
2218 zlog_debug(
2219 "ISIS-Snp (%s): Sending L%d CSNP on %s, length %zd",
2220 circuit->area->area_tag, level,
2221 circuit->interface->name,
2222 stream_get_endp(circuit->snd_stream));
2223 log_multiline(LOG_DEBUG, " ", "%s",
2224 isis_format_tlvs(tlvs, NULL));
2225 if (IS_DEBUG_PACKET_DUMP)
2226 zlog_dump_data(
2227 STREAM_DATA(circuit->snd_stream),
2228 stream_get_endp(circuit->snd_stream));
2229 }
2230
2231 pdu_counter_count(circuit->area->pdu_tx_counters, pdu_type);
2232 int retval = circuit->tx(circuit, level);
2233 if (retval != ISIS_OK) {
2234 flog_err(EC_ISIS_PACKET,
2235 "ISIS-Snp (%s): Send L%d CSNP on %s failed",
2236 circuit->area->area_tag, level,
2237 circuit->interface->name);
2238 isis_free_tlvs(tlvs);
2239 return retval;
2240 }
2241
2242 /*
2243 * Start lsp_id of the next CSNP should be one plus the
2244 * stop lsp_id in this current CSNP.
2245 */
2246 memcpy(start, stop, ISIS_SYS_ID_LEN + 2);
2247 loop = false;
2248 for (int i = ISIS_SYS_ID_LEN + 1; i >= 0; --i) {
2249 if (start[i] < (uint8_t)0xff) {
2250 start[i] += 1;
2251 loop = true;
2252 break;
2253 }
2254 }
2255 memset(stop, 0xff, ISIS_SYS_ID_LEN + 2);
2256 isis_free_tlvs(tlvs);
2257 }
2258
2259 return ISIS_OK;
2260 }
2261
2262 void send_l1_csnp(struct event *thread)
2263 {
2264 struct isis_circuit *circuit;
2265
2266 circuit = EVENT_ARG(thread);
2267 assert(circuit);
2268
2269 circuit->t_send_csnp[0] = NULL;
2270
2271 if ((circuit->circ_type == CIRCUIT_T_BROADCAST
2272 && circuit->u.bc.is_dr[0])
2273 || circuit->circ_type == CIRCUIT_T_P2P) {
2274 send_csnp(circuit, 1);
2275 }
2276 /* set next timer thread */
2277 event_add_timer(master, send_l1_csnp, circuit,
2278 isis_jitter(circuit->csnp_interval[0], CSNP_JITTER),
2279 &circuit->t_send_csnp[0]);
2280 }
2281
2282 void send_l2_csnp(struct event *thread)
2283 {
2284 struct isis_circuit *circuit;
2285
2286 circuit = EVENT_ARG(thread);
2287 assert(circuit);
2288
2289 circuit->t_send_csnp[1] = NULL;
2290
2291 if ((circuit->circ_type == CIRCUIT_T_BROADCAST
2292 && circuit->u.bc.is_dr[1])
2293 || circuit->circ_type == CIRCUIT_T_P2P) {
2294 send_csnp(circuit, 2);
2295 }
2296 /* set next timer thread */
2297 event_add_timer(master, send_l2_csnp, circuit,
2298 isis_jitter(circuit->csnp_interval[1], CSNP_JITTER),
2299 &circuit->t_send_csnp[1]);
2300 }
2301
2302 /*
2303 * 7.3.15.4 action on expiration of partial SNP interval
2304 * level 1
2305 */
2306 static int send_psnp(int level, struct isis_circuit *circuit)
2307 {
2308 if (circuit->circ_type == CIRCUIT_T_BROADCAST
2309 && circuit->u.bc.is_dr[level - 1])
2310 return ISIS_OK;
2311
2312 if (lspdb_count(&circuit->area->lspdb[level - 1]) == 0)
2313 return ISIS_OK;
2314
2315 if (!circuit->snd_stream)
2316 return ISIS_ERROR;
2317
2318 uint8_t pdu_type = (level == ISIS_LEVEL1) ? L1_PARTIAL_SEQ_NUM
2319 : L2_PARTIAL_SEQ_NUM;
2320
2321 isis_circuit_stream(circuit, &circuit->snd_stream);
2322 fill_fixed_hdr(pdu_type, circuit->snd_stream);
2323
2324 size_t len_pointer = stream_get_endp(circuit->snd_stream);
2325 stream_putw(circuit->snd_stream, 0); /* length is filled in later */
2326 stream_put(circuit->snd_stream, circuit->isis->sysid, ISIS_SYS_ID_LEN);
2327 stream_putc(circuit->snd_stream, circuit->idx);
2328
2329 struct isis_passwd *passwd = (level == ISIS_LEVEL1)
2330 ? &circuit->area->area_passwd
2331 : &circuit->area->domain_passwd;
2332
2333 struct isis_tlvs *tlvs = isis_alloc_tlvs();
2334
2335 if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND))
2336 isis_tlvs_add_auth(tlvs, passwd);
2337
2338 size_t tlv_start = stream_get_endp(circuit->snd_stream);
2339 if (isis_pack_tlvs(tlvs, circuit->snd_stream, len_pointer, false,
2340 false)) {
2341 isis_free_tlvs(tlvs);
2342 return ISIS_WARNING;
2343 }
2344 isis_free_tlvs(tlvs);
2345
2346 uint16_t num_lsps =
2347 get_max_lsp_count(STREAM_WRITEABLE(circuit->snd_stream));
2348
2349 while (1) {
2350 struct isis_lsp *lsp;
2351
2352 tlvs = isis_alloc_tlvs();
2353 if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND))
2354 isis_tlvs_add_auth(tlvs, passwd);
2355
2356 frr_each (lspdb, &circuit->area->lspdb[level - 1], lsp) {
2357 if (ISIS_CHECK_FLAG(lsp->SSNflags, circuit))
2358 isis_tlvs_add_lsp_entry(tlvs, lsp);
2359
2360 if (tlvs->lsp_entries.count == num_lsps)
2361 break;
2362 }
2363
2364 if (!tlvs->lsp_entries.count) {
2365 isis_free_tlvs(tlvs);
2366 return ISIS_OK;
2367 }
2368
2369 stream_set_endp(circuit->snd_stream, tlv_start);
2370 if (isis_pack_tlvs(tlvs, circuit->snd_stream, len_pointer,
2371 false, false)) {
2372 isis_free_tlvs(tlvs);
2373 return ISIS_WARNING;
2374 }
2375
2376 if (IS_DEBUG_SNP_PACKETS) {
2377 zlog_debug(
2378 "ISIS-Snp (%s): Sending L%d PSNP on %s, length %zd",
2379 circuit->area->area_tag, level,
2380 circuit->interface->name,
2381 stream_get_endp(circuit->snd_stream));
2382 log_multiline(LOG_DEBUG, " ", "%s",
2383 isis_format_tlvs(tlvs, NULL));
2384 if (IS_DEBUG_PACKET_DUMP)
2385 zlog_dump_data(
2386 STREAM_DATA(circuit->snd_stream),
2387 stream_get_endp(circuit->snd_stream));
2388 }
2389
2390 pdu_counter_count(circuit->area->pdu_tx_counters, pdu_type);
2391 int retval = circuit->tx(circuit, level);
2392 if (retval != ISIS_OK) {
2393 flog_err(EC_ISIS_PACKET,
2394 "ISIS-Snp (%s): Send L%d PSNP on %s failed",
2395 circuit->area->area_tag, level,
2396 circuit->interface->name);
2397 isis_free_tlvs(tlvs);
2398 return retval;
2399 }
2400
2401 /*
2402 * sending succeeded, we can clear SSN flags of this circuit
2403 * for the LSPs in list
2404 */
2405 struct isis_lsp_entry *entry_head;
2406 entry_head = (struct isis_lsp_entry *)tlvs->lsp_entries.head;
2407 for (struct isis_lsp_entry *entry = entry_head; entry;
2408 entry = entry->next)
2409 ISIS_CLEAR_FLAG(entry->lsp->SSNflags, circuit);
2410 isis_free_tlvs(tlvs);
2411 }
2412
2413 return ISIS_OK;
2414 }
2415
2416 void send_l1_psnp(struct event *thread)
2417 {
2418
2419 struct isis_circuit *circuit;
2420
2421 circuit = EVENT_ARG(thread);
2422 assert(circuit);
2423
2424 circuit->t_send_psnp[0] = NULL;
2425
2426 send_psnp(1, circuit);
2427 /* set next timer thread */
2428 event_add_timer(master, send_l1_psnp, circuit,
2429 isis_jitter(circuit->psnp_interval[0], PSNP_JITTER),
2430 &circuit->t_send_psnp[0]);
2431 }
2432
2433 /*
2434 * 7.3.15.4 action on expiration of partial SNP interval
2435 * level 2
2436 */
2437 void send_l2_psnp(struct event *thread)
2438 {
2439 struct isis_circuit *circuit;
2440
2441 circuit = EVENT_ARG(thread);
2442 assert(circuit);
2443
2444 circuit->t_send_psnp[1] = NULL;
2445
2446 send_psnp(2, circuit);
2447
2448 /* set next timer thread */
2449 event_add_timer(master, send_l2_psnp, circuit,
2450 isis_jitter(circuit->psnp_interval[1], PSNP_JITTER),
2451 &circuit->t_send_psnp[1]);
2452 }
2453
2454 /*
2455 * ISO 10589 - 7.3.14.3
2456 */
2457 void send_lsp(struct isis_circuit *circuit, struct isis_lsp *lsp,
2458 enum isis_tx_type tx_type)
2459 {
2460 int clear_srm = 1;
2461 int retval = ISIS_OK;
2462
2463 if (circuit->state != C_STATE_UP || circuit->is_passive == 1)
2464 goto out;
2465
2466 /*
2467 * Do not send if levels do not match
2468 */
2469 if (!(lsp->level & circuit->is_type))
2470 goto out;
2471
2472 /*
2473 * Do not send if we do not have adjacencies in state up on the circuit
2474 */
2475 if (circuit->upadjcount[lsp->level - 1] == 0)
2476 goto out;
2477
2478 /* stream_copy will assert and stop program execution if LSP is larger
2479 * than
2480 * the circuit's MTU. So handle and log this case here. */
2481 if (stream_get_endp(lsp->pdu) > stream_get_size(circuit->snd_stream)) {
2482 flog_err(
2483 EC_ISIS_PACKET,
2484 "ISIS-Upd (%s): Can't send L%d LSP %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus on %s. LSP Size is %zu while interface stream size is %zu.",
2485 circuit->area->area_tag, lsp->level,
2486 rawlspid_print(lsp->hdr.lsp_id), lsp->hdr.seqno,
2487 lsp->hdr.checksum, lsp->hdr.rem_lifetime,
2488 circuit->interface->name, stream_get_endp(lsp->pdu),
2489 stream_get_size(circuit->snd_stream));
2490 #ifndef FABRICD
2491 /* send a northbound notification */
2492 isis_notif_lsp_too_large(circuit, stream_get_endp(lsp->pdu),
2493 lsp->hdr.lsp_id);
2494 #endif /* ifndef FABRICD */
2495 if (IS_DEBUG_PACKET_DUMP)
2496 zlog_dump_data(STREAM_DATA(lsp->pdu),
2497 stream_get_endp(lsp->pdu));
2498 retval = ISIS_ERROR;
2499 goto out;
2500 }
2501
2502 /* copy our lsp to the send buffer */
2503 stream_copy(circuit->snd_stream, lsp->pdu);
2504
2505 if (tx_type == TX_LSP_CIRCUIT_SCOPED) {
2506 stream_putc_at(circuit->snd_stream, 4, FS_LINK_STATE);
2507 stream_putc_at(circuit->snd_stream, 7,
2508 L2_CIRCUIT_FLOODING_SCOPE);
2509 }
2510
2511 if (IS_DEBUG_UPDATE_PACKETS) {
2512 zlog_debug("ISIS-Upd (%s): Sending %sL%d LSP %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus on %s",
2513 circuit->area->area_tag,
2514 (tx_type == TX_LSP_CIRCUIT_SCOPED)
2515 ? "Circuit scoped " : "",
2516 lsp->level,
2517 rawlspid_print(lsp->hdr.lsp_id), lsp->hdr.seqno,
2518 lsp->hdr.checksum, lsp->hdr.rem_lifetime,
2519 circuit->interface->name);
2520 if (IS_DEBUG_PACKET_DUMP)
2521 zlog_dump_data(STREAM_DATA(circuit->snd_stream),
2522 stream_get_endp(circuit->snd_stream));
2523 }
2524
2525 uint8_t pdu_type = (tx_type == TX_LSP_CIRCUIT_SCOPED) ? FS_LINK_STATE
2526 : (lsp->level == ISIS_LEVEL1) ? L1_LINK_STATE
2527 : L2_LINK_STATE;
2528
2529 clear_srm = 0;
2530 pdu_counter_count(circuit->area->pdu_tx_counters, pdu_type);
2531 retval = circuit->tx(circuit, lsp->level);
2532 if (retval != ISIS_OK) {
2533 flog_err(EC_ISIS_PACKET,
2534 "ISIS-Upd (%s): Send L%d LSP on %s failed %s",
2535 circuit->area->area_tag, lsp->level,
2536 circuit->interface->name,
2537 (retval == ISIS_WARNING) ? "temporarily"
2538 : "permanently");
2539 }
2540
2541 out:
2542 if (clear_srm
2543 || (retval == ISIS_OK && circuit->circ_type == CIRCUIT_T_BROADCAST)
2544 || (retval != ISIS_OK && retval != ISIS_WARNING)) {
2545 /* SRM flag will trigger retransmission. We will not retransmit
2546 * if we
2547 * encountered a fatal error.
2548 * On success, they should only be cleared if it's a broadcast
2549 * circuit.
2550 * On a P2P circuit, we will wait for the ack from the neighbor
2551 * to clear
2552 * the fag.
2553 */
2554 isis_tx_queue_del(circuit->tx_queue, lsp);
2555 }
2556 }