1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * IS-IS Rout(e)ing protocol - isis_pdu.c
6 * Copyright (C) 2001,2002 Sampo Saaristo
7 * Tampere University of Technology
8 * Institute of Communications Engineering
24 #include "lib_errors.h"
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"
50 static int ack_lsp(struct isis_lsp_hdr
*hdr
, struct isis_circuit
*circuit
,
57 (level
== IS_LEVEL_1
) ? L1_PARTIAL_SEQ_NUM
: L2_PARTIAL_SEQ_NUM
;
59 isis_circuit_stream(circuit
, &circuit
->snd_stream
);
61 fill_fixed_hdr(pdu_type
, circuit
->snd_stream
);
63 lenp
= stream_get_endp(circuit
->snd_stream
);
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 */
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
);
76 length
= (uint16_t)stream_get_endp(circuit
->snd_stream
);
77 /* Update PDU length */
78 stream_putw_at(circuit
->snd_stream
, lenp
, length
);
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
);
96 struct isis_circuit
*circuit
;
101 uint8_t sys_id
[ISIS_SYS_ID_LEN
];
108 uint8_t dis
[ISIS_SYS_ID_LEN
+ 1];
113 struct isis_tlvs
*tlvs
;
116 static int process_p2p_hello(struct iih_info
*iih
)
118 struct isis_threeway_adj
*tw_adj
= iih
->tlvs
->threeway_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
,
131 if (tw_adj
->neighbor_set
132 && (memcmp(tw_adj
->neighbor_id
, iih
->circuit
->isis
->sysid
,
134 || tw_adj
->neighbor_circuit_id
135 != (uint32_t)iih
->circuit
->idx
)) {
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
);
148 * My interpertation of the ISO, if no adj exists we will create one for
151 struct isis_adjacency
*adj
= iih
->circuit
->u
.p2p
.neighbor
;
152 /* If an adjacency exists, check it is with the source of the hello
155 if (memcmp(iih
->sys_id
, adj
->sysid
, ISIS_SYS_ID_LEN
)) {
157 "hello source and adjacency do not match, set adj down");
158 isis_adj_state_change(&adj
, ISIS_ADJ_DOWN
,
163 if (!adj
|| adj
->level
!= iih
->circ_type
) {
165 adj
= isis_new_adj(iih
->sys_id
, NULL
, iih
->circ_type
,
168 adj
->level
= iih
->circ_type
;
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.
177 adj
->circuit_t
= iih
->circ_type
;
178 isis_adj_state_change(&adj
, ISIS_ADJ_INITIALIZING
, NULL
);
179 adj
->sys_type
= ISIS_SYSTYPE_UNKNOWN
;
183 adj
->ext_circuit_id
= tw_adj
->local_circuit_id
;
185 /* 8.2.6 Monitoring point-to-point adjacencies */
186 adj
->hold_time
= iih
->holdtime
;
187 adj
->last_upd
= time(NULL
);
190 isis_tlvs_to_adj(iih
->tlvs
, adj
, &changed
);
191 changed
|= tlvs_to_adj_mt_set(iih
->tlvs
, iih
->v4_usable
, iih
->v6_usable
,
194 /* lets take care of the expiry */
195 THREAD_OFF(adj
->t_expire
);
196 thread_add_timer(master
, isis_adj_expire
, adj
, (long)adj
->hold_time
,
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
)
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
) {
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
,
220 if (adj
->adj_state
!= ISIS_ADJ_UP
) {
221 /* (7) reject - wrong system type event
223 zlog_warn("wrongSystemType");
225 } else if (adj
->adj_usage
== ISIS_ADJ_LEVEL1
) {
226 /* (6) down - wrong system */
227 isis_adj_state_change(&adj
,
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
) {
239 if (adj
->adj_state
!= ISIS_ADJ_UP
240 || adj
->adj_usage
== ISIS_ADJ_LEVEL1
) {
241 isis_adj_process_threeway(adj
, tw_adj
,
243 } else if ((adj
->adj_usage
244 == ISIS_ADJ_LEVEL1AND2
)
246 == ISIS_ADJ_LEVEL2
)) {
247 /* (8) down - wrong system */
248 isis_adj_state_change(&adj
,
254 if (adj
->adj_state
!= ISIS_ADJ_UP
255 || adj
->adj_usage
== ISIS_ADJ_LEVEL2
) {
256 isis_adj_process_threeway(adj
, tw_adj
,
258 } else if ((adj
->adj_usage
== ISIS_ADJ_LEVEL1
)
260 == ISIS_ADJ_LEVEL1AND2
)) {
261 /* (8) down - wrong system */
262 isis_adj_state_change(&adj
,
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
)
274 == ISIS_ADJ_LEVEL2
)) {
275 /* (8) down - wrong system */
276 isis_adj_state_change(&adj
,
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
) {
288 if (adj
->adj_state
!= ISIS_ADJ_UP
) {
289 /* (5) reject - wrong system type event
291 zlog_warn("wrongSystemType");
293 } else if ((adj
->adj_usage
294 == ISIS_ADJ_LEVEL1AND2
)
296 == ISIS_ADJ_LEVEL2
)) {
297 /* (6) down - wrong system */
298 isis_adj_state_change(&adj
,
303 case IS_LEVEL_1_AND_2
:
305 if (adj
->adj_state
!= ISIS_ADJ_UP
306 || adj
->adj_usage
== ISIS_ADJ_LEVEL2
) {
307 isis_adj_process_threeway(adj
, tw_adj
,
309 } else if (adj
->adj_usage
310 == ISIS_ADJ_LEVEL1AND2
) {
311 /* (6) down - wrong system */
312 isis_adj_state_change(&adj
,
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
,
327 /* 8.2.5.2 b) 2)is_type L1 and adj is up */
329 isis_adj_state_change(&adj
, ISIS_ADJ_DOWN
,
330 "Down - Area Mismatch");
333 /* 8.2.5.2 b 3 If the system is L2 or L1L2 - table 8 */
335 switch (iih
->circ_type
) {
337 if (adj
->adj_state
!= ISIS_ADJ_UP
) {
338 /* (6) reject - Area Mismatch event */
339 zlog_warn("AreaMismatch");
341 } else if (adj
->adj_usage
== ISIS_ADJ_LEVEL1
) {
342 /* (7) down - area mismatch */
343 isis_adj_state_change(&adj
,
347 } else if ((adj
->adj_usage
348 == ISIS_ADJ_LEVEL1AND2
)
350 == ISIS_ADJ_LEVEL2
)) {
351 /* (7) down - wrong system */
352 isis_adj_state_change(&adj
,
357 case IS_LEVEL_1_AND_2
:
359 if (adj
->adj_state
!= ISIS_ADJ_UP
360 || adj
->adj_usage
== ISIS_ADJ_LEVEL2
) {
361 isis_adj_process_threeway(adj
, tw_adj
,
363 } else if (adj
->adj_usage
== ISIS_ADJ_LEVEL1
) {
364 /* (7) down - wrong system */
365 isis_adj_state_change(&adj
,
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(
376 /* (7) down - area mismatch */
377 isis_adj_state_change(
386 /* down - area mismatch */
387 isis_adj_state_change(&adj
, ISIS_ADJ_DOWN
, "Area Mismatch");
391 if (adj
->adj_state
== ISIS_ADJ_UP
&& changed
) {
392 lsp_regenerate_schedule(
394 isis_adj_usage2levels(adj
->adj_usage
), 0);
397 /* 8.2.5.2 c) if the action was up - comparing circuit IDs */
398 /* FIXME - Missing parts */
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...
403 switch (adj
->adj_usage
) {
404 case ISIS_ADJ_LEVEL1
:
405 adj
->sys_type
= ISIS_SYSTYPE_L1_IS
;
407 case ISIS_ADJ_LEVEL2
:
408 adj
->sys_type
= ISIS_SYSTYPE_L2_IS
;
410 case ISIS_ADJ_LEVEL1AND2
:
411 adj
->sys_type
= ISIS_SYSTYPE_L2_IS
;
414 adj
->sys_type
= ISIS_SYSTYPE_UNKNOWN
;
419 if (IS_DEBUG_ADJ_PACKETS
) {
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
);
431 static int process_lan_hello(struct iih_info
*iih
)
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
)) {
439 /* Do as in 8.4.2.5 */
440 adj
= isis_new_adj(iih
->sys_id
, iih
->ssnpa
, iih
->level
,
444 memcpy(adj
->snpa
, iih
->ssnpa
, 6);
446 memset(adj
->snpa
, ' ', 6);
448 adj
->level
= iih
->level
;
450 isis_adj_state_change(&adj
, ISIS_ADJ_INITIALIZING
, NULL
);
452 if (iih
->level
== IS_LEVEL_1
)
453 adj
->sys_type
= ISIS_SYSTYPE_L1_IS
;
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]);
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
;
468 if (memcmp(dis
, iih
->dis
, ISIS_SYS_ID_LEN
+ 1)) {
469 thread_add_event(master
, isis_event_dis_status_change
,
470 iih
->circuit
, 0, NULL
);
471 memcpy(dis
, iih
->dis
, ISIS_SYS_ID_LEN
+ 1);
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);
482 isis_tlvs_to_adj(iih
->tlvs
, adj
, &changed
);
483 changed
|= tlvs_to_adj_mt_set(iih
->tlvs
, iih
->v4_usable
, iih
->v6_usable
,
486 /* lets take care of the expiry */
487 THREAD_OFF(adj
->t_expire
);
488 thread_add_timer(master
, isis_adj_expire
, adj
, (long)adj
->hold_time
,
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"
495 bool own_snpa_found
=
496 isis_tlvs_own_snpa_found(iih
->tlvs
, iih
->circuit
->u
.bc
.snpa
);
498 if (adj
->adj_state
!= ISIS_ADJ_UP
) {
499 if (own_snpa_found
) {
500 isis_adj_state_change(
502 "own SNPA found in LAN Neighbours TLV");
505 if (!own_snpa_found
) {
506 isis_adj_state_change(
507 &adj
, ISIS_ADJ_INITIALIZING
,
508 "own SNPA not found in LAN Neighbours TLV");
512 if (adj
->adj_state
== ISIS_ADJ_UP
&& changed
)
513 lsp_regenerate_schedule(adj
->circuit
->area
, iih
->level
, 0);
515 if (IS_DEBUG_ADJ_PACKETS
) {
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
));
527 static int pdu_len_validate(uint16_t pdu_len
, struct isis_circuit
*circuit
)
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
))
534 if (pdu_len
< stream_get_endp(circuit
->rcv_stream
))
535 stream_set_endp(circuit
->rcv_stream
, pdu_len
);
539 static void update_rej_adj_count(struct isis_circuit
*circuit
)
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]++;
547 circuit
->area
->rej_adjacencies
[0]++;
548 circuit
->area
->rej_adjacencies
[1]++;
552 static int process_hello(uint8_t pdu_type
, struct isis_circuit
*circuit
,
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
563 const char *pdu_name
=
566 : (level
== ISIS_LEVEL1
) ? "L1 LAN IIH" : "L2 LAN IIH";
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
));
582 if (circuit
->circ_type
!= CIRCUIT_T_P2P
) {
583 zlog_warn("p2p hello on non p2p circuit");
584 update_rej_adj_count(circuit
);
586 isis_notif_reject_adjacency(
587 circuit
, "p2p hello on non p2p circuit",
588 raw_pdu
, sizeof(raw_pdu
));
589 #endif /* ifndef FABRICD */
593 if (circuit
->circ_type
!= CIRCUIT_T_BROADCAST
) {
594 zlog_warn("lan hello on non broadcast circuit");
595 update_rej_adj_count(circuit
);
597 isis_notif_reject_adjacency(
598 circuit
, "lan hello on non broadcast circuit",
599 raw_pdu
, sizeof(raw_pdu
));
600 #endif /* ifndef FABRICD */
604 if (circuit
->ext_domain
) {
606 "level %d LAN Hello received over circuit with externalDomain = true",
608 update_rej_adj_count(circuit
);
610 isis_notif_reject_adjacency(
612 "LAN Hello received over circuit with externalDomain = true",
613 raw_pdu
, sizeof(raw_pdu
));
614 #endif /* ifndef FABRICD */
618 if (!(circuit
->is_type
& level
)) {
619 if (IS_DEBUG_ADJ_PACKETS
) {
621 "ISIS-Adj (%s): Interface level mismatch, %s",
622 circuit
->area
->area_tag
,
623 circuit
->interface
->name
);
625 update_rej_adj_count(circuit
);
627 isis_notif_reject_adjacency(circuit
,
628 "Interface level mismatch",
629 raw_pdu
, sizeof(raw_pdu
));
630 #endif /* ifndef FABRICD */
635 struct iih_info iih
= {
636 .circuit
= circuit
, .ssnpa
= ssnpa
, .level
= level
};
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
);
645 iih
.circuit_id
= stream_getc(circuit
->rcv_stream
);
647 iih
.priority
= stream_getc(circuit
->rcv_stream
);
648 stream_get(iih
.dis
, circuit
->rcv_stream
, ISIS_SYS_ID_LEN
+ 1);
651 if (pdu_len_validate(iih
.pdu_len
, circuit
)) {
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
);
658 isis_notif_reject_adjacency(circuit
, "Invalid PDU length",
659 raw_pdu
, sizeof(raw_pdu
));
660 #endif /* ifndef FABRICD */
664 if (!p2p_hello
&& !(level
& iih
.circ_type
)) {
665 flog_err(EC_ISIS_PACKET
,
666 "Level %d LAN Hello with Circuit Type %d", level
,
668 update_rej_adj_count(circuit
);
670 isis_notif_reject_adjacency(circuit
,
671 "LAN Hello with wrong IS-level",
672 raw_pdu
, sizeof(raw_pdu
));
673 #endif /* ifndef FABRICD */
677 const char *error_log
;
678 int retval
= ISIS_WARNING
;
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
);
685 isis_notif_reject_adjacency(circuit
, "Failed to unpack TLVs",
686 raw_pdu
, sizeof(raw_pdu
));
687 #endif /* ifndef FABRICD */
691 if (!iih
.tlvs
->area_addresses
.count
) {
692 zlog_warn("No Area addresses TLV in %s", pdu_name
);
694 /* send northbound notification */
695 isis_notif_area_mismatch(circuit
, raw_pdu
, sizeof(raw_pdu
));
696 #endif /* ifndef FABRICD */
700 if (!iih
.tlvs
->protocols_supported
.count
) {
701 zlog_warn("No supported protocols TLV in %s", pdu_name
);
702 update_rej_adj_count(circuit
);
704 isis_notif_reject_adjacency(circuit
,
705 "No supported protocols TLV",
706 raw_pdu
, sizeof(raw_pdu
));
707 #endif /* ifndef FABRICD */
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",
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
,
725 } else { /* AUTH_TYPE_FAILURE or NO_VALIDATOR */
726 update_rej_adj_count(circuit
);
727 isis_notif_authentication_type_failure(circuit
, raw_pdu
,
730 #endif /* ifndef FABRICD */
734 if (!memcmp(iih
.sys_id
, circuit
->isis
->sysid
, ISIS_SYS_ID_LEN
)) {
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
);
740 isis_notif_reject_adjacency(circuit
,
741 "Received IIH with our own sysid",
742 raw_pdu
, sizeof(raw_pdu
));
743 #endif /* ifndef FABRICD */
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
) {
754 "ISIS-Adj (%s): Area mismatch, level %d IIH on %s",
755 circuit
->area
->area_tag
, level
,
756 circuit
->interface
->name
);
759 /* send northbound notification */
760 isis_notif_area_mismatch(circuit
, raw_pdu
, sizeof(raw_pdu
));
761 #endif /* ifndef FABRICD */
765 iih
.v4_usable
= (fabricd_ip_addrs(circuit
)
766 && iih
.tlvs
->ipv4_address
.count
);
769 (listcount(circuit
->ipv6_link
) && iih
.tlvs
->ipv6_address
.count
);
771 if (!iih
.v4_usable
&& !iih
.v6_usable
) {
772 if (IS_DEBUG_ADJ_PACKETS
) {
774 "ISIS-Adj (%s): Neither IPv4 nor IPv6 considered usable. Ignoring IIH",
775 circuit
->area
->area_tag
);
777 update_rej_adj_count(circuit
);
779 isis_notif_reject_adjacency(
780 circuit
, "Neither IPv4 not IPv6 considered usable",
781 raw_pdu
, sizeof(raw_pdu
));
782 #endif /* ifndef FABRICD */
786 retval
= p2p_hello
? process_p2p_hello(&iih
) : process_lan_hello(&iih
);
788 isis_free_tlvs(iih
.tlvs
);
793 static void lsp_flood_or_update(struct isis_lsp
*lsp
,
794 struct isis_circuit
*circuit
,
798 lsp_flood(lsp
, circuit
);
800 fabricd_update_lsp_no_flood(lsp
, circuit
);
804 * Process Level 1/2 Link State
806 * Section 7.3.15.1 - Action on receipt of a link state PDU
808 static int process_lsp(uint8_t pdu_type
, struct isis_circuit
*circuit
,
809 const uint8_t *ssnpa
, uint8_t max_area_addrs
)
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
];
817 stream_get_from(raw_pdu
, circuit
->rcv_stream
, pdu_start
,
818 pdu_end
- pdu_start
);
820 if (pdu_type
== FS_LINK_STATE
) {
823 if (max_area_addrs
!= L2_CIRCUIT_FLOODING_SCOPE
)
826 circuit_scoped
= true;
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);
834 if (pdu_type
== L1_LINK_STATE
)
838 circuit_scoped
= false;
841 if (IS_DEBUG_UPDATE_PACKETS
) {
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
));
854 struct isis_lsp_hdr hdr
= {};
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
);
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 */
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
),
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
);
884 /* lsp is_type check */
885 if ((hdr
.lsp_bits
& IS_LEVEL_1
) != IS_LEVEL_1
) {
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 */
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)) {
899 "ISIS-Upd (%s): LSP %s invalid LSP checksum 0x%04hx",
900 circuit
->area
->area_tag
, rawlspid_print(hdr
.lsp_id
),
905 /* 7.3.15.1 a) 1 - external domain circuit will discard lsps */
906 if (circuit
->ext_domain
) {
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
),
914 /* 7.3.15.1 a) 2,3 - manualL2OnlyMode not implemented */
915 if (!(circuit
->is_type
& level
)) {
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
));
923 struct isis_tlvs
*tlvs
= NULL
;
924 int retval
= ISIS_WARNING
;
925 const char *error_log
;
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",
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
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]++;
945 circuit
->area
->lsp_error_counter
[0]++;
946 circuit
->area
->lsp_error_counter
[1]++;
949 isis_notif_lsp_error(circuit
, hdr
.lsp_id
, raw_pdu
,
950 sizeof(raw_pdu
), 0, 0);
951 #endif /* ifndef FABRICD */
955 /* 7.3.15.1 a) 4 - need to make sure IDLength matches */
957 /* 7.3.15.1 a) 5 - maximum area match, can be ommited since we only use
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",
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]++;
979 circuit
->area
->auth_failures
[0]++;
980 circuit
->area
->auth_failures
[1]++;
982 isis_notif_authentication_failure(circuit
, 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]++;
991 circuit
->area
->auth_type_failures
[0]++;
992 circuit
->area
->auth_type_failures
[1]++;
994 isis_notif_authentication_type_failure(circuit
, raw_pdu
,
997 #endif /* ifndef FABRICD */
1001 /* Find the LSP in our database and compare it to this Link State header
1003 struct isis_lsp
*lsp
=
1004 lsp_search(&circuit
->area
->lspdb
[level
- 1], hdr
.lsp_id
);
1007 comp
= lsp_compare(circuit
->area
->area_tag
, lsp
, hdr
.seqno
,
1008 hdr
.checksum
, hdr
.rem_lifetime
);
1009 if (lsp
&& (lsp
->own_lsp
))
1012 /* 7.3.15.1 a) 6 - Must check that we have an adjacency of the same
1014 /* for broadcast circuits, snpa should be compared */
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 */
1027 /* for non broadcast, we just need to find same level adj */
1029 /* If no adj, or no sharing of level */
1030 if (!circuit
->u
.p2p
.neighbor
) {
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
)))
1047 /* 7.3.15.1 a) 7 - Passwords for level 1 - not implemented */
1049 /* 7.3.15.1 a) 8 - Passwords for level 2 - not implemented */
1051 /* 7.3.15.1 a) 9 - OriginatingLSPBufferSize - not implemented FIXME: do
1054 /* 7.3.16.2 - If this is an LSP from another IS with identical seq_num
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
),
1063 hdr
.rem_lifetime
= 0;
1064 lsp_confusion
= true;
1066 lsp_confusion
= false;
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) {
1071 /* 7.3.16.4 a) 1) No LSP in db -> send an ack, but don't
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? */
1078 if (memcmp(hdr
.lsp_id
, circuit
->isis
->sysid
,
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
,
1088 isis_free_tlvs(tlvs
);
1091 lsp_flood_or_update(lsp
, NULL
,
1094 ISIS_FLAGS_CLEAR_ALL(
1095 lsp
->SSNflags
); /* FIXME:
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
) {
1111 if (circuit
->circ_type
1112 != CIRCUIT_T_BROADCAST
)
1117 } /* 7.3.16.4 b) 2) */
1118 else if (comp
== LSP_EQUAL
) {
1120 isis_tx_queue_del(circuit
->tx_queue
,
1123 if (circuit
->circ_type
1124 != CIRCUIT_T_BROADCAST
)
1125 ISIS_SET_FLAG(lsp
->SSNflags
,
1127 } /* 7.3.16.4 b) 3) */
1129 isis_tx_queue_add(circuit
->tx_queue
,
1130 lsp
, TX_LSP_NORMAL
);
1131 ISIS_CLEAR_FLAG(lsp
->SSNflags
, circuit
);
1133 } else if (lsp
->hdr
.rem_lifetime
!= 0) {
1134 /* our own LSP -> 7.3.16.4 c) */
1135 if (comp
== LSP_NEWER
) {
1137 if (lsp
->hdr
.seqno
< hdr
.seqno
) {
1141 ->lsp_seqno_skipped_counter
++;
1142 isis_notif_seqno_skipped(
1143 circuit
, hdr
.lsp_id
);
1145 #endif /* ifndef FABRICD */
1146 lsp_inc_seqno(lsp
, hdr
.seqno
);
1147 lsp_flood_or_update(lsp
, NULL
,
1150 isis_tx_queue_add(circuit
->tx_queue
,
1151 lsp
, TX_LSP_NORMAL
);
1152 ISIS_CLEAR_FLAG(lsp
->SSNflags
, circuit
);
1154 if (IS_DEBUG_UPDATE_PACKETS
)
1156 "ISIS-Upd (%s): (1) re-originating LSP %s new seq 0x%08x",
1157 circuit
->area
->area_tag
,
1158 rawlspid_print(hdr
.lsp_id
),
1161 /* our own LSP with 0 remaining life time */
1163 /* send northbound notification */
1164 isis_notif_own_lsp_purge(circuit
, hdr
.lsp_id
);
1165 #endif /* ifndef FABRICD */
1170 /* 7.3.15.1 c) - If this is our own lsp and we don't have it initiate a
1172 if (memcmp(hdr
.lsp_id
, circuit
->isis
->sysid
, ISIS_SYS_ID_LEN
) == 0) {
1174 /* 7.3.16.4: initiate a purge */
1175 lsp_purge_non_exist(level
, &hdr
, circuit
->area
);
1179 /* 7.3.15.1 d) - If this is our own lsp and we have it */
1181 /* In 7.3.16.1, If an Intermediate system R somewhere in the
1183 * has information that the current sequence number for source S
1185 * "greater" than that held by S, ... */
1187 if (comp
== LSP_NEWER
) {
1189 lsp_inc_seqno(lsp
, hdr
.seqno
);
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
) {
1197 "ISIS-Upd (%s): (2) re-originating LSP %s new seq 0x%08x",
1198 circuit
->area
->area_tag
,
1199 rawlspid_print(hdr
.lsp_id
),
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
);
1208 isis_tx_queue_add(circuit
->tx_queue
, lsp
,
1210 ISIS_CLEAR_FLAG(lsp
->SSNflags
, circuit
);
1213 /* 7.3.15.1 e) - This lsp originated on another system */
1215 /* 7.3.15.1 e) 1) LSP newer than the one in db or no LSP in db
1217 if ((!lsp
|| comp
== LSP_NEWER
)) {
1219 * If this lsp is a frag, need to see if we have zero
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;
1228 &circuit
->area
->lspdb
[level
- 1], lspid
);
1231 "Got lsp frag, while zero lsp not in database");
1237 lsp
= lsp_new_from_recv(
1238 &hdr
, tlvs
, circuit
->rcv_stream
, lsp0
,
1239 circuit
->area
, level
);
1241 lsp_insert(&circuit
->area
->lspdb
[level
- 1],
1243 } else /* exists, so we overwrite */
1245 lsp_update(lsp
, &hdr
, tlvs
, circuit
->rcv_stream
,
1246 circuit
->area
, level
, false);
1249 lsp_flood_or_update(lsp
, circuit
, circuit_scoped
);
1252 if (circuit
->circ_type
!= CIRCUIT_T_BROADCAST
)
1253 ISIS_SET_FLAG(lsp
->SSNflags
, circuit
);
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);
1262 if (circuit
->circ_type
!= CIRCUIT_T_BROADCAST
)
1263 ISIS_SET_FLAG(lsp
->SSNflags
, circuit
);
1265 /* 7.3.15.1 e) 3) LSP older than the one in db */
1267 isis_tx_queue_add(circuit
->tx_queue
, lsp
,
1269 ISIS_CLEAR_FLAG(lsp
->SSNflags
, circuit
);
1276 fabricd_trigger_csnp(circuit
->area
, circuit_scoped
);
1278 isis_free_tlvs(tlvs
);
1283 * Process Sequence Numbers
1285 * Section 7.3.15.2 - Action on receipt of a sequence numbers PDU
1288 static int process_snp(uint8_t pdu_type
, struct isis_circuit
*circuit
,
1289 const uint8_t *ssnpa
)
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 */
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
)
1305 uint16_t pdu_len
= stream_getw(circuit
->rcv_stream
);
1306 uint8_t rem_sys_id
[ISIS_SYS_ID_LEN
];
1308 stream_get(rem_sys_id
, circuit
->rcv_stream
, ISIS_SYS_ID_LEN
);
1309 stream_forward_getp(circuit
->rcv_stream
, 1); /* Circuit ID - unused */
1311 uint8_t start_lsp_id
[ISIS_SYS_ID_LEN
+ 2] = {};
1312 uint8_t stop_lsp_id
[ISIS_SYS_ID_LEN
+ 2] = {};
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);
1321 if (pdu_len_validate(pdu_len
, circuit
)) {
1322 zlog_warn("Received a CSNP with bogus length %d", pdu_len
);
1323 return ISIS_WARNING
;
1326 if (IS_DEBUG_SNP_PACKETS
) {
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
));
1338 /* 7.3.15.2 a) 1 - external domain circuit will discard snp pdu */
1339 if (circuit
->ext_domain
) {
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
);
1349 /* 7.3.15.2 a) 2,3 - manualL2OnlyMode not implemented */
1350 if (!(circuit
->is_type
& level
)) {
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
);
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]) {
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
);
1371 /* 7.3.15.2 a) 5 - need to make sure IDLength matches - already checked
1374 /* 7.3.15.2 a) 6 - maximum area match, can be ommited since we only use
1376 * - already checked */
1378 /* 7.3.15.2 a) 7 - Must check that we have an adjacency of the same
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 */
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 */
1394 struct isis_tlvs
*tlvs
;
1395 int retval
= ISIS_WARNING
;
1396 const char *error_log
;
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",
1405 struct isis_passwd
*passwd
= (level
== IS_LEVEL_1
)
1406 ? &circuit
->area
->area_passwd
1407 : &circuit
->area
->domain_passwd
;
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",
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]++;
1427 circuit
->area
->auth_failures
[0]++;
1428 circuit
->area
->auth_failures
[1]++;
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]++;
1439 circuit
->area
->auth_type_failures
[0]++;
1440 circuit
->area
->auth_type_failures
[1]++;
1442 isis_notif_authentication_type_failure(
1443 circuit
, raw_pdu
, sizeof(raw_pdu
));
1445 #endif /* ifndef FABRICD */
1450 struct isis_lsp_entry
*entry_head
=
1451 (struct isis_lsp_entry
*)tlvs
->lsp_entries
.head
;
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
) {
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
);
1468 bool resync_needed
= false;
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
,
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
);
1488 /* 7.3.15.2 b) 3) if it is older, clear SSN and set SRM
1490 else if (cmp
== LSP_OLDER
) {
1491 ISIS_CLEAR_FLAG(lsp
->SSNflags
, circuit
);
1492 isis_tx_queue_add(circuit
->tx_queue
, lsp
,
1495 /* 7.3.15.2 b) 4) if it is newer, set SSN and clear SRM
1499 lsp_inc_seqno(lsp
, entry
->seqno
);
1500 isis_tx_queue_add(circuit
->tx_queue
, lsp
,
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;
1511 /* 7.3.15.2 b) 5) if it was not found, and all of those
1513 * insert it and set SSN on it */
1514 if (entry
->rem_lifetime
&& entry
->checksum
1516 && memcmp(entry
->id
, circuit
->isis
->sysid
,
1518 struct isis_lsp
*lsp0
= NULL
;
1520 if (LSP_FRAGMENT(entry
->id
)) {
1521 uint8_t lspid
[ISIS_SYS_ID_LEN
+ 2];
1523 memcpy(lspid
, entry
->id
,
1524 ISIS_SYS_ID_LEN
+ 1);
1525 LSP_FRAGMENT(lspid
) = 0;
1527 &circuit
->area
->lspdb
[level
- 1],
1530 zlog_debug("Got lsp frag in snp, while zero not in database");
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],
1540 lsp_set_all_srmflags(lsp
, false);
1541 ISIS_SET_FLAG(lsp
->SSNflags
, circuit
);
1542 resync_needed
= true;
1547 /* 7.3.15.2 c) on CSNP set SRM for all in range which were not reported
1551 * Build a list from our own LSP db bounded with
1552 * start_lsp_id and stop_lsp_id
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
);
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
)
1566 list_delete_node(lsp_list
, node
);
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;
1579 list_delete(&lsp_list
);
1582 if (fabricd_initial_sync_is_complete(circuit
->area
) && resync_needed
)
1583 zlog_warn("OpenFabric: Needed to resync LSPDB using CSNP!");
1587 isis_free_tlvs(tlvs
);
1591 static int pdu_size(uint8_t pdu_type
, uint8_t *size
)
1596 *size
= ISIS_LANHELLO_HDRLEN
;
1599 *size
= ISIS_P2PHELLO_HDRLEN
;
1604 *size
= ISIS_LSP_HDR_LEN
;
1606 case L1_COMPLETE_SEQ_NUM
:
1607 case L2_COMPLETE_SEQ_NUM
:
1608 *size
= ISIS_CSNP_HDRLEN
;
1610 case L1_PARTIAL_SEQ_NUM
:
1611 case L2_PARTIAL_SEQ_NUM
:
1612 *size
= ISIS_PSNP_HDRLEN
;
1617 *size
+= ISIS_FIXED_HDR_LEN
;
1625 int isis_handle_pdu(struct isis_circuit
*circuit
, uint8_t *ssnpa
)
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
];
1632 stream_get_from(raw_pdu
, circuit
->rcv_stream
, pdu_start
,
1633 pdu_end
- pdu_start
);
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.");
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
);
1649 stream_forward_getp(circuit
->rcv_stream
, 1); /* reserved */
1650 uint8_t max_area_addrs
= stream_getc(circuit
->rcv_stream
);
1652 pdu_counter_count(circuit
->area
->pdu_rx_counters
, pdu_type
);
1654 if (idrp
== ISO9542_ESIS
) {
1655 flog_err(EC_LIB_DEVELOPMENT
,
1656 "No support for ES-IS packet IDRP=%hhx", idrp
);
1660 if (idrp
!= ISO10589_ISIS
) {
1661 flog_err(EC_ISIS_PACKET
, "Not an IS-IS packet IDRP=%hhx",
1666 if (version1
!= 1) {
1667 zlog_warn("Unsupported ISIS version %hhu", version1
);
1669 /* send northbound notification */
1670 isis_notif_version_skew(circuit
, version1
, raw_pdu
,
1672 #endif /* ifndef FABRICD */
1673 return ISIS_WARNING
;
1676 if (id_len
!= 0 && id_len
!= ISIS_SYS_ID_LEN
) {
1679 "IDFieldLengthMismatch: ID Length field in a received PDU %hhu, while the parameter for this IS is %u",
1680 id_len
, ISIS_SYS_ID_LEN
);
1681 circuit
->id_len_mismatches
++;
1682 if (circuit
->is_type
== IS_LEVEL_1
) {
1683 circuit
->area
->id_len_mismatches
[0]++;
1684 } else if (circuit
->is_type
== IS_LEVEL_2
) {
1685 circuit
->area
->id_len_mismatches
[1]++;
1687 circuit
->area
->id_len_mismatches
[0]++;
1688 circuit
->area
->id_len_mismatches
[1]++;
1692 /* send northbound notification */
1693 isis_notif_id_len_mismatch(circuit
, id_len
, raw_pdu
,
1695 #endif /* ifndef FABRICD */
1699 uint8_t expected_length
;
1700 if (pdu_size(pdu_type
, &expected_length
)) {
1701 zlog_warn("Unsupported ISIS PDU %hhu", pdu_type
);
1702 return ISIS_WARNING
;
1705 if (length
!= expected_length
) {
1706 flog_err(EC_ISIS_PACKET
,
1707 "Expected fixed header length = %hhu but got %hhu",
1708 expected_length
, length
);
1712 if (stream_get_endp(circuit
->rcv_stream
) < length
) {
1715 "PDU is too short to contain fixed header of given PDU type.");
1719 if (version2
!= 1) {
1720 zlog_warn("Unsupported ISIS PDU version %hhu", version2
);
1722 /* send northbound notification */
1723 isis_notif_version_skew(circuit
, version2
, raw_pdu
,
1725 #endif /* ifndef FABRICD */
1726 return ISIS_WARNING
;
1729 if (circuit
->is_passive
) {
1730 zlog_warn("Received ISIS PDU on passive circuit %s",
1731 circuit
->interface
->name
);
1732 return ISIS_WARNING
;
1736 if (pdu_type
!= FS_LINK_STATE
/* FS PDU doesn't contain max area addr
1738 && max_area_addrs
!= 0
1739 && max_area_addrs
!= circuit
->isis
->max_area_addrs
) {
1742 "maximumAreaAddressesMismatch: maximumAreaAdresses in a received PDU %hhu while the parameter for this IS is %u",
1743 max_area_addrs
, circuit
->isis
->max_area_addrs
);
1744 circuit
->max_area_addr_mismatches
++;
1746 /* send northbound notification */
1747 isis_notif_max_area_addr_mismatch(circuit
, max_area_addrs
,
1748 raw_pdu
, sizeof(raw_pdu
));
1749 #endif /* ifndef FABRICD */
1757 if (fabricd
&& pdu_type
!= P2P_HELLO
)
1759 retval
= process_hello(pdu_type
, circuit
, ssnpa
);
1765 && pdu_type
!= L2_LINK_STATE
1766 && pdu_type
!= FS_LINK_STATE
)
1768 retval
= process_lsp(pdu_type
, circuit
, ssnpa
, max_area_addrs
);
1770 case L1_COMPLETE_SEQ_NUM
:
1771 case L2_COMPLETE_SEQ_NUM
:
1772 case L1_PARTIAL_SEQ_NUM
:
1773 case L2_PARTIAL_SEQ_NUM
:
1774 retval
= process_snp(pdu_type
, circuit
, ssnpa
);
1783 void isis_receive(struct thread
*thread
)
1785 struct isis_circuit
*circuit
;
1786 uint8_t ssnpa
[ETH_ALEN
];
1791 circuit
= THREAD_ARG(thread
);
1794 circuit
->t_read
= NULL
;
1796 isis_circuit_stream(circuit
, &circuit
->rcv_stream
);
1798 #if ISIS_METHOD != ISIS_METHOD_BPF
1801 retval
= circuit
->rx(circuit
, ssnpa
);
1803 if (retval
== ISIS_OK
)
1804 isis_handle_pdu(circuit
, ssnpa
);
1805 #else // ISIS_METHOD != ISIS_METHOD_BPF
1806 circuit
->rx(circuit
, ssnpa
);
1810 * prepare for next packet.
1812 if (!circuit
->is_passive
)
1813 isis_circuit_prepare(circuit
);
1819 void fill_fixed_hdr(uint8_t pdu_type
, struct stream
*stream
)
1823 if (pdu_size(pdu_type
, &length
))
1824 assert(!"Unknown PDU Type");
1826 stream_putc(stream
, ISO10589_ISIS
); /* IDRP */
1827 stream_putc(stream
, length
); /* Length of fixed header */
1828 stream_putc(stream
, 1); /* Version/Protocol ID Extension 1 */
1829 stream_putc(stream
, 0); /* ID Length, 0 => 6 */
1830 stream_putc(stream
, pdu_type
);
1831 stream_putc(stream
, 1); /* Subversion */
1832 stream_putc(stream
, 0); /* Reserved */
1833 stream_putc(stream
, 0); /* Max Area Addresses 0 => 3 */
1836 static uint8_t hello_pdu_type(struct isis_circuit
*circuit
, int level
)
1838 if (circuit
->circ_type
== CIRCUIT_T_BROADCAST
)
1839 return (level
== IS_LEVEL_1
) ? L1_LAN_HELLO
: L2_LAN_HELLO
;
1844 static void put_hello_hdr(struct isis_circuit
*circuit
, int level
,
1845 size_t *len_pointer
)
1847 uint8_t pdu_type
= hello_pdu_type(circuit
, level
);
1849 isis_circuit_stream(circuit
, &circuit
->snd_stream
);
1850 fill_fixed_hdr(pdu_type
, circuit
->snd_stream
);
1852 stream_putc(circuit
->snd_stream
, circuit
->is_type
);
1853 stream_put(circuit
->snd_stream
, circuit
->isis
->sysid
, ISIS_SYS_ID_LEN
);
1855 uint32_t holdtime
= circuit
->hello_multiplier
[level
- 1]
1856 * circuit
->hello_interval
[level
- 1];
1858 if (holdtime
> 0xffff)
1861 stream_putw(circuit
->snd_stream
, holdtime
);
1862 *len_pointer
= stream_get_endp(circuit
->snd_stream
);
1863 stream_putw(circuit
->snd_stream
, 0); /* length is filled in later */
1865 if (circuit
->circ_type
== CIRCUIT_T_BROADCAST
) {
1866 uint8_t *desig_is
= (level
== IS_LEVEL_1
)
1867 ? circuit
->u
.bc
.l1_desig_is
1868 : circuit
->u
.bc
.l2_desig_is
;
1869 stream_putc(circuit
->snd_stream
, circuit
->priority
[level
- 1]);
1870 stream_put(circuit
->snd_stream
, desig_is
, ISIS_SYS_ID_LEN
+ 1);
1872 stream_putc(circuit
->snd_stream
, circuit
->circuit_id
);
1876 int send_hello(struct isis_circuit
*circuit
, int level
)
1881 if (circuit
->is_passive
)
1884 if (circuit
->interface
->mtu
== 0) {
1885 zlog_warn("circuit has zero MTU");
1886 return ISIS_WARNING
;
1889 put_hello_hdr(circuit
, level
, &len_pointer
);
1891 struct isis_tlvs
*tlvs
= isis_alloc_tlvs();
1893 isis_tlvs_add_auth(tlvs
, &circuit
->passwd
);
1895 if (!listcount(circuit
->area
->area_addrs
)) {
1896 isis_free_tlvs(tlvs
);
1897 return ISIS_WARNING
;
1900 isis_tlvs_add_area_addresses(tlvs
, circuit
->area
->area_addrs
);
1902 if (circuit
->circ_type
== CIRCUIT_T_BROADCAST
) {
1903 isis_tlvs_add_lan_neighbors(
1904 tlvs
, circuit
->u
.bc
.lan_neighs
[level
- 1]);
1905 } else if (circuit
->circ_type
== CIRCUIT_T_P2P
1906 && !circuit
->disable_threeway_adj
) {
1907 uint32_t ext_circuit_id
= circuit
->idx
;
1908 if (circuit
->u
.p2p
.neighbor
) {
1909 uint8_t threeway_state
;
1911 if (fabricd_initial_sync_is_in_progress(circuit
->area
)
1912 && fabricd_initial_sync_circuit(circuit
->area
) != circuit
)
1913 threeway_state
= ISIS_THREEWAY_DOWN
;
1915 threeway_state
= circuit
->u
.p2p
.neighbor
->threeway_state
;
1916 isis_tlvs_add_threeway_adj(tlvs
,
1919 circuit
->u
.p2p
.neighbor
->sysid
,
1920 circuit
->u
.p2p
.neighbor
->ext_circuit_id
);
1922 isis_tlvs_add_threeway_adj(tlvs
,
1929 isis_tlvs_set_protocols_supported(tlvs
, &circuit
->nlpids
);
1934 * TLV gets included if no topology is enabled on the interface,
1935 * if one topology other than #0 is enabled, or if multiple topologies
1938 struct isis_circuit_mt_setting
**mt_settings
;
1939 unsigned int mt_count
;
1941 mt_settings
= circuit_mt_settings(circuit
, &mt_count
);
1942 if (mt_count
== 0 && area_is_mt(circuit
->area
)) {
1943 tlvs
->mt_router_info_empty
= true;
1944 } else if ((mt_count
== 1
1945 && mt_settings
[0]->mtid
!= ISIS_MT_IPV4_UNICAST
)
1946 || (mt_count
> 1)) {
1947 for (unsigned int i
= 0; i
< mt_count
; i
++)
1948 isis_tlvs_add_mt_router_info(tlvs
, mt_settings
[i
]->mtid
,
1952 if (circuit
->ip_router
) {
1953 struct list
*circuit_ip_addrs
= fabricd_ip_addrs(circuit
);
1955 if (circuit_ip_addrs
)
1956 isis_tlvs_add_ipv4_addresses(tlvs
, circuit_ip_addrs
);
1959 if (circuit
->ipv6_router
)
1960 isis_tlvs_add_ipv6_addresses(tlvs
, circuit
->ipv6_link
);
1962 /* RFC6119 section 4 define TLV 233 to provide Global IPv6 address */
1963 if (circuit
->ipv6_router
)
1964 isis_tlvs_add_global_ipv6_addresses(tlvs
,
1965 circuit
->ipv6_non_link
);
1967 if (isis_pack_tlvs(tlvs
, circuit
->snd_stream
, len_pointer
,
1968 circuit
->pad_hellos
, false)) {
1969 isis_free_tlvs(tlvs
);
1970 return ISIS_WARNING
; /* XXX: Maybe Log TLV structure? */
1973 if (IS_DEBUG_ADJ_PACKETS
) {
1974 if (circuit
->circ_type
== CIRCUIT_T_BROADCAST
) {
1976 "ISIS-Adj (%s): Sending L%d LAN IIH on %s, length %zd",
1977 circuit
->area
->area_tag
, level
,
1978 circuit
->interface
->name
,
1979 stream_get_endp(circuit
->snd_stream
));
1982 "ISIS-Adj (%s): Sending P2P IIH on %s, length %zd",
1983 circuit
->area
->area_tag
,
1984 circuit
->interface
->name
,
1985 stream_get_endp(circuit
->snd_stream
));
1987 if (IS_DEBUG_PACKET_DUMP
)
1988 zlog_dump_data(STREAM_DATA(circuit
->snd_stream
),
1989 stream_get_endp(circuit
->snd_stream
));
1992 isis_free_tlvs(tlvs
);
1994 pdu_counter_count(circuit
->area
->pdu_tx_counters
,
1995 hello_pdu_type(circuit
, level
));
1996 retval
= circuit
->tx(circuit
, level
);
1997 if (retval
!= ISIS_OK
)
1998 flog_err(EC_ISIS_PACKET
,
1999 "ISIS-Adj (%s): Send L%d IIH on %s failed",
2000 circuit
->area
->area_tag
, level
,
2001 circuit
->interface
->name
);
2006 static void send_hello_cb(struct thread
*thread
)
2008 struct isis_circuit_arg
*arg
= THREAD_ARG(thread
);
2011 struct isis_circuit
*circuit
= arg
->circuit
;
2012 int level
= arg
->level
;
2016 if (circuit
->circ_type
== CIRCUIT_T_P2P
) {
2017 circuit
->u
.p2p
.t_send_p2p_hello
= NULL
;
2018 send_hello(circuit
, 1);
2019 send_hello_sched(circuit
, ISIS_LEVEL1
,
2020 1000 * circuit
->hello_interval
[1]);
2024 if (circuit
->circ_type
!= CIRCUIT_T_BROADCAST
) {
2025 zlog_warn("ISIS-Hello (%s): Trying to send hello on unknown circuit type %d",
2026 circuit
->area
->area_tag
, circuit
->circ_type
);
2030 circuit
->u
.bc
.t_send_lan_hello
[level
- 1] = NULL
;
2031 if (!(circuit
->is_type
& level
)) {
2032 zlog_warn("ISIS-Hello (%s): Trying to send L%d IIH in L%d-only circuit",
2033 circuit
->area
->area_tag
, level
, 3 - level
);
2037 if (circuit
->u
.bc
.run_dr_elect
[level
- 1])
2038 isis_dr_elect(circuit
, level
);
2040 send_hello(circuit
, level
);
2042 /* set next timer thread */
2043 send_hello_sched(circuit
, level
, 1000 * circuit
->hello_interval
[level
- 1]);
2046 static void _send_hello_sched(struct isis_circuit
*circuit
,
2047 struct thread
**threadp
,
2048 int level
, long delay
)
2051 if (thread_timer_remain_msec(*threadp
) < (unsigned long)delay
)
2054 THREAD_OFF(*threadp
);
2057 thread_add_timer_msec(master
, send_hello_cb
,
2058 &circuit
->level_arg
[level
- 1],
2059 isis_jitter(delay
, IIH_JITTER
),
2063 void send_hello_sched(struct isis_circuit
*circuit
, int level
, long delay
)
2065 if (circuit
->circ_type
== CIRCUIT_T_P2P
) {
2066 _send_hello_sched(circuit
, &circuit
->u
.p2p
.t_send_p2p_hello
,
2067 ISIS_LEVEL1
, delay
);
2071 if (circuit
->circ_type
!= CIRCUIT_T_BROADCAST
) {
2072 zlog_warn("%s: encountered unknown circuit type %d on %s",
2073 __func__
, circuit
->circ_type
,
2074 circuit
->interface
->name
);
2078 for (int loop_level
= ISIS_LEVEL1
; loop_level
<= ISIS_LEVEL2
; loop_level
++) {
2079 if (!(loop_level
& level
))
2084 &circuit
->u
.bc
.t_send_lan_hello
[loop_level
- 1],
2093 * Count the maximum number of lsps that can be accommodated by a given size.
2095 #define LSP_ENTRIES_LEN (10 + ISIS_SYS_ID_LEN)
2096 static uint16_t get_max_lsp_count(uint16_t size
)
2100 uint16_t remaining_size
;
2102 /* First count the full size TLVs */
2103 tlv_count
= size
/ MAX_LSP_ENTRIES_TLV_SIZE
;
2104 lsp_count
= tlv_count
* (MAX_LSP_ENTRIES_TLV_SIZE
/ LSP_ENTRIES_LEN
);
2106 /* The last TLV, if any */
2107 remaining_size
= size
% MAX_LSP_ENTRIES_TLV_SIZE
;
2108 if (remaining_size
- 2 >= LSP_ENTRIES_LEN
)
2109 lsp_count
+= (remaining_size
- 2) / LSP_ENTRIES_LEN
;
2114 int send_csnp(struct isis_circuit
*circuit
, int level
)
2116 if (lspdb_count(&circuit
->area
->lspdb
[level
- 1]) == 0)
2119 uint8_t pdu_type
= (level
== ISIS_LEVEL1
) ? L1_COMPLETE_SEQ_NUM
2120 : L2_COMPLETE_SEQ_NUM
;
2122 isis_circuit_stream(circuit
, &circuit
->snd_stream
);
2123 fill_fixed_hdr(pdu_type
, circuit
->snd_stream
);
2125 size_t len_pointer
= stream_get_endp(circuit
->snd_stream
);
2127 stream_putw(circuit
->snd_stream
, 0);
2128 stream_put(circuit
->snd_stream
, circuit
->isis
->sysid
, ISIS_SYS_ID_LEN
);
2129 /* with zero circuit id - ref 9.10, 9.11 */
2130 stream_putc(circuit
->snd_stream
, 0);
2132 size_t start_pointer
= stream_get_endp(circuit
->snd_stream
);
2133 stream_put(circuit
->snd_stream
, 0, ISIS_SYS_ID_LEN
+ 2);
2134 size_t end_pointer
= stream_get_endp(circuit
->snd_stream
);
2135 stream_put(circuit
->snd_stream
, 0, ISIS_SYS_ID_LEN
+ 2);
2137 struct isis_passwd
*passwd
= (level
== ISIS_LEVEL1
)
2138 ? &circuit
->area
->area_passwd
2139 : &circuit
->area
->domain_passwd
;
2141 struct isis_tlvs
*tlvs
= isis_alloc_tlvs();
2143 if (CHECK_FLAG(passwd
->snp_auth
, SNP_AUTH_SEND
))
2144 isis_tlvs_add_auth(tlvs
, passwd
);
2146 size_t tlv_start
= stream_get_endp(circuit
->snd_stream
);
2147 if (isis_pack_tlvs(tlvs
, circuit
->snd_stream
, len_pointer
, false,
2149 isis_free_tlvs(tlvs
);
2150 return ISIS_WARNING
;
2152 isis_free_tlvs(tlvs
);
2155 get_max_lsp_count(STREAM_WRITEABLE(circuit
->snd_stream
));
2157 uint8_t start
[ISIS_SYS_ID_LEN
+ 2];
2158 memset(start
, 0x00, ISIS_SYS_ID_LEN
+ 2);
2159 uint8_t stop
[ISIS_SYS_ID_LEN
+ 2];
2160 memset(stop
, 0xff, ISIS_SYS_ID_LEN
+ 2);
2164 tlvs
= isis_alloc_tlvs();
2165 if (CHECK_FLAG(passwd
->snp_auth
, SNP_AUTH_SEND
))
2166 isis_tlvs_add_auth(tlvs
, passwd
);
2168 struct isis_lsp
*last_lsp
;
2169 isis_tlvs_add_csnp_entries(tlvs
, start
, stop
, num_lsps
,
2170 &circuit
->area
->lspdb
[level
- 1],
2173 * Update the stop lsp_id before encoding this CSNP.
2175 if (tlvs
->lsp_entries
.count
< num_lsps
) {
2176 memset(stop
, 0xff, ISIS_SYS_ID_LEN
+ 2);
2178 memcpy(stop
, last_lsp
->hdr
.lsp_id
, sizeof(stop
));
2181 memcpy(STREAM_DATA(circuit
->snd_stream
) + start_pointer
, start
,
2182 ISIS_SYS_ID_LEN
+ 2);
2183 memcpy(STREAM_DATA(circuit
->snd_stream
) + end_pointer
, stop
,
2184 ISIS_SYS_ID_LEN
+ 2);
2185 stream_set_endp(circuit
->snd_stream
, tlv_start
);
2186 if (isis_pack_tlvs(tlvs
, circuit
->snd_stream
, len_pointer
,
2188 isis_free_tlvs(tlvs
);
2189 return ISIS_WARNING
;
2192 if (IS_DEBUG_SNP_PACKETS
) {
2194 "ISIS-Snp (%s): Sending L%d CSNP on %s, length %zd",
2195 circuit
->area
->area_tag
, level
,
2196 circuit
->interface
->name
,
2197 stream_get_endp(circuit
->snd_stream
));
2198 log_multiline(LOG_DEBUG
, " ", "%s",
2199 isis_format_tlvs(tlvs
, NULL
));
2200 if (IS_DEBUG_PACKET_DUMP
)
2202 STREAM_DATA(circuit
->snd_stream
),
2203 stream_get_endp(circuit
->snd_stream
));
2206 pdu_counter_count(circuit
->area
->pdu_tx_counters
, pdu_type
);
2207 int retval
= circuit
->tx(circuit
, level
);
2208 if (retval
!= ISIS_OK
) {
2209 flog_err(EC_ISIS_PACKET
,
2210 "ISIS-Snp (%s): Send L%d CSNP on %s failed",
2211 circuit
->area
->area_tag
, level
,
2212 circuit
->interface
->name
);
2213 isis_free_tlvs(tlvs
);
2218 * Start lsp_id of the next CSNP should be one plus the
2219 * stop lsp_id in this current CSNP.
2221 memcpy(start
, stop
, ISIS_SYS_ID_LEN
+ 2);
2223 for (int i
= ISIS_SYS_ID_LEN
+ 1; i
>= 0; --i
) {
2224 if (start
[i
] < (uint8_t)0xff) {
2230 memset(stop
, 0xff, ISIS_SYS_ID_LEN
+ 2);
2231 isis_free_tlvs(tlvs
);
2237 void send_l1_csnp(struct thread
*thread
)
2239 struct isis_circuit
*circuit
;
2241 circuit
= THREAD_ARG(thread
);
2244 circuit
->t_send_csnp
[0] = NULL
;
2246 if ((circuit
->circ_type
== CIRCUIT_T_BROADCAST
2247 && circuit
->u
.bc
.is_dr
[0])
2248 || circuit
->circ_type
== CIRCUIT_T_P2P
) {
2249 send_csnp(circuit
, 1);
2251 /* set next timer thread */
2252 thread_add_timer(master
, send_l1_csnp
, circuit
,
2253 isis_jitter(circuit
->csnp_interval
[0], CSNP_JITTER
),
2254 &circuit
->t_send_csnp
[0]);
2257 void send_l2_csnp(struct thread
*thread
)
2259 struct isis_circuit
*circuit
;
2261 circuit
= THREAD_ARG(thread
);
2264 circuit
->t_send_csnp
[1] = NULL
;
2266 if ((circuit
->circ_type
== CIRCUIT_T_BROADCAST
2267 && circuit
->u
.bc
.is_dr
[1])
2268 || circuit
->circ_type
== CIRCUIT_T_P2P
) {
2269 send_csnp(circuit
, 2);
2271 /* set next timer thread */
2272 thread_add_timer(master
, send_l2_csnp
, circuit
,
2273 isis_jitter(circuit
->csnp_interval
[1], CSNP_JITTER
),
2274 &circuit
->t_send_csnp
[1]);
2278 * 7.3.15.4 action on expiration of partial SNP interval
2281 static int send_psnp(int level
, struct isis_circuit
*circuit
)
2283 if (circuit
->circ_type
== CIRCUIT_T_BROADCAST
2284 && circuit
->u
.bc
.is_dr
[level
- 1])
2287 if (lspdb_count(&circuit
->area
->lspdb
[level
- 1]) == 0)
2290 if (!circuit
->snd_stream
)
2293 uint8_t pdu_type
= (level
== ISIS_LEVEL1
) ? L1_PARTIAL_SEQ_NUM
2294 : L2_PARTIAL_SEQ_NUM
;
2296 isis_circuit_stream(circuit
, &circuit
->snd_stream
);
2297 fill_fixed_hdr(pdu_type
, circuit
->snd_stream
);
2299 size_t len_pointer
= stream_get_endp(circuit
->snd_stream
);
2300 stream_putw(circuit
->snd_stream
, 0); /* length is filled in later */
2301 stream_put(circuit
->snd_stream
, circuit
->isis
->sysid
, ISIS_SYS_ID_LEN
);
2302 stream_putc(circuit
->snd_stream
, circuit
->idx
);
2304 struct isis_passwd
*passwd
= (level
== ISIS_LEVEL1
)
2305 ? &circuit
->area
->area_passwd
2306 : &circuit
->area
->domain_passwd
;
2308 struct isis_tlvs
*tlvs
= isis_alloc_tlvs();
2310 if (CHECK_FLAG(passwd
->snp_auth
, SNP_AUTH_SEND
))
2311 isis_tlvs_add_auth(tlvs
, passwd
);
2313 size_t tlv_start
= stream_get_endp(circuit
->snd_stream
);
2314 if (isis_pack_tlvs(tlvs
, circuit
->snd_stream
, len_pointer
, false,
2316 isis_free_tlvs(tlvs
);
2317 return ISIS_WARNING
;
2319 isis_free_tlvs(tlvs
);
2322 get_max_lsp_count(STREAM_WRITEABLE(circuit
->snd_stream
));
2325 struct isis_lsp
*lsp
;
2327 tlvs
= isis_alloc_tlvs();
2328 if (CHECK_FLAG(passwd
->snp_auth
, SNP_AUTH_SEND
))
2329 isis_tlvs_add_auth(tlvs
, passwd
);
2331 frr_each (lspdb
, &circuit
->area
->lspdb
[level
- 1], lsp
) {
2332 if (ISIS_CHECK_FLAG(lsp
->SSNflags
, circuit
))
2333 isis_tlvs_add_lsp_entry(tlvs
, lsp
);
2335 if (tlvs
->lsp_entries
.count
== num_lsps
)
2339 if (!tlvs
->lsp_entries
.count
) {
2340 isis_free_tlvs(tlvs
);
2344 stream_set_endp(circuit
->snd_stream
, tlv_start
);
2345 if (isis_pack_tlvs(tlvs
, circuit
->snd_stream
, len_pointer
,
2347 isis_free_tlvs(tlvs
);
2348 return ISIS_WARNING
;
2351 if (IS_DEBUG_SNP_PACKETS
) {
2353 "ISIS-Snp (%s): Sending L%d PSNP on %s, length %zd",
2354 circuit
->area
->area_tag
, level
,
2355 circuit
->interface
->name
,
2356 stream_get_endp(circuit
->snd_stream
));
2357 log_multiline(LOG_DEBUG
, " ", "%s",
2358 isis_format_tlvs(tlvs
, NULL
));
2359 if (IS_DEBUG_PACKET_DUMP
)
2361 STREAM_DATA(circuit
->snd_stream
),
2362 stream_get_endp(circuit
->snd_stream
));
2365 pdu_counter_count(circuit
->area
->pdu_tx_counters
, pdu_type
);
2366 int retval
= circuit
->tx(circuit
, level
);
2367 if (retval
!= ISIS_OK
) {
2368 flog_err(EC_ISIS_PACKET
,
2369 "ISIS-Snp (%s): Send L%d PSNP on %s failed",
2370 circuit
->area
->area_tag
, level
,
2371 circuit
->interface
->name
);
2372 isis_free_tlvs(tlvs
);
2377 * sending succeeded, we can clear SSN flags of this circuit
2378 * for the LSPs in list
2380 struct isis_lsp_entry
*entry_head
;
2381 entry_head
= (struct isis_lsp_entry
*)tlvs
->lsp_entries
.head
;
2382 for (struct isis_lsp_entry
*entry
= entry_head
; entry
;
2383 entry
= entry
->next
)
2384 ISIS_CLEAR_FLAG(entry
->lsp
->SSNflags
, circuit
);
2385 isis_free_tlvs(tlvs
);
2391 void send_l1_psnp(struct thread
*thread
)
2394 struct isis_circuit
*circuit
;
2396 circuit
= THREAD_ARG(thread
);
2399 circuit
->t_send_psnp
[0] = NULL
;
2401 send_psnp(1, circuit
);
2402 /* set next timer thread */
2403 thread_add_timer(master
, send_l1_psnp
, circuit
,
2404 isis_jitter(circuit
->psnp_interval
[0], PSNP_JITTER
),
2405 &circuit
->t_send_psnp
[0]);
2409 * 7.3.15.4 action on expiration of partial SNP interval
2412 void send_l2_psnp(struct thread
*thread
)
2414 struct isis_circuit
*circuit
;
2416 circuit
= THREAD_ARG(thread
);
2419 circuit
->t_send_psnp
[1] = NULL
;
2421 send_psnp(2, circuit
);
2423 /* set next timer thread */
2424 thread_add_timer(master
, send_l2_psnp
, circuit
,
2425 isis_jitter(circuit
->psnp_interval
[1], PSNP_JITTER
),
2426 &circuit
->t_send_psnp
[1]);
2430 * ISO 10589 - 7.3.14.3
2432 void send_lsp(struct isis_circuit
*circuit
, struct isis_lsp
*lsp
,
2433 enum isis_tx_type tx_type
)
2436 int retval
= ISIS_OK
;
2438 if (circuit
->state
!= C_STATE_UP
|| circuit
->is_passive
== 1)
2442 * Do not send if levels do not match
2444 if (!(lsp
->level
& circuit
->is_type
))
2448 * Do not send if we do not have adjacencies in state up on the circuit
2450 if (circuit
->upadjcount
[lsp
->level
- 1] == 0)
2453 /* stream_copy will assert and stop program execution if LSP is larger
2455 * the circuit's MTU. So handle and log this case here. */
2456 if (stream_get_endp(lsp
->pdu
) > stream_get_size(circuit
->snd_stream
)) {
2459 "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.",
2460 circuit
->area
->area_tag
, lsp
->level
,
2461 rawlspid_print(lsp
->hdr
.lsp_id
), lsp
->hdr
.seqno
,
2462 lsp
->hdr
.checksum
, lsp
->hdr
.rem_lifetime
,
2463 circuit
->interface
->name
, stream_get_endp(lsp
->pdu
),
2464 stream_get_size(circuit
->snd_stream
));
2466 /* send a northbound notification */
2467 isis_notif_lsp_too_large(circuit
, stream_get_endp(lsp
->pdu
),
2469 #endif /* ifndef FABRICD */
2470 if (IS_DEBUG_PACKET_DUMP
)
2471 zlog_dump_data(STREAM_DATA(lsp
->pdu
),
2472 stream_get_endp(lsp
->pdu
));
2473 retval
= ISIS_ERROR
;
2477 /* copy our lsp to the send buffer */
2478 stream_copy(circuit
->snd_stream
, lsp
->pdu
);
2480 if (tx_type
== TX_LSP_CIRCUIT_SCOPED
) {
2481 stream_putc_at(circuit
->snd_stream
, 4, FS_LINK_STATE
);
2482 stream_putc_at(circuit
->snd_stream
, 7,
2483 L2_CIRCUIT_FLOODING_SCOPE
);
2486 if (IS_DEBUG_UPDATE_PACKETS
) {
2487 zlog_debug("ISIS-Upd (%s): Sending %sL%d LSP %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus on %s",
2488 circuit
->area
->area_tag
,
2489 (tx_type
== TX_LSP_CIRCUIT_SCOPED
)
2490 ? "Circuit scoped " : "",
2492 rawlspid_print(lsp
->hdr
.lsp_id
), lsp
->hdr
.seqno
,
2493 lsp
->hdr
.checksum
, lsp
->hdr
.rem_lifetime
,
2494 circuit
->interface
->name
);
2495 if (IS_DEBUG_PACKET_DUMP
)
2496 zlog_dump_data(STREAM_DATA(circuit
->snd_stream
),
2497 stream_get_endp(circuit
->snd_stream
));
2500 uint8_t pdu_type
= (tx_type
== TX_LSP_CIRCUIT_SCOPED
) ? FS_LINK_STATE
2501 : (lsp
->level
== ISIS_LEVEL1
) ? L1_LINK_STATE
2505 pdu_counter_count(circuit
->area
->pdu_tx_counters
, pdu_type
);
2506 retval
= circuit
->tx(circuit
, lsp
->level
);
2507 if (retval
!= ISIS_OK
) {
2508 flog_err(EC_ISIS_PACKET
,
2509 "ISIS-Upd (%s): Send L%d LSP on %s failed %s",
2510 circuit
->area
->area_tag
, lsp
->level
,
2511 circuit
->interface
->name
,
2512 (retval
== ISIS_WARNING
) ? "temporarily"
2518 || (retval
== ISIS_OK
&& circuit
->circ_type
== CIRCUIT_T_BROADCAST
)
2519 || (retval
!= ISIS_OK
&& retval
!= ISIS_WARNING
)) {
2520 /* SRM flag will trigger retransmission. We will not retransmit
2522 * encountered a fatal error.
2523 * On success, they should only be cleared if it's a broadcast
2525 * On a P2P circuit, we will wait for the ack from the neighbor
2529 isis_tx_queue_del(circuit
->tx_queue
, lsp
);