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