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