]> git.proxmox.com Git - mirror_frr.git/blob - isisd/isis_lsp.c
Merge pull request #3306 from opensourcerouting/bugfix/isis-csnp-handling
[mirror_frr.git] / isisd / isis_lsp.c
1 /*
2 * IS-IS Rout(e)ing protocol - isis_lsp.c
3 * LSP processing
4 *
5 * Copyright (C) 2001,2002 Sampo Saaristo
6 * Tampere University of Technology
7 * Institute of Communications Engineering
8 * Copyright (C) 2013-2015 Christian Franke <chris@opensourcerouting.org>
9 *
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the Free
12 * Software Foundation; either version 2 of the License, or (at your option)
13 * any later version.
14 *
15 * This program is distributed in the hope that it will be useful,but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
18 * more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; see the file COPYING; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25 #include <zebra.h>
26
27 #include "linklist.h"
28 #include "thread.h"
29 #include "vty.h"
30 #include "stream.h"
31 #include "memory.h"
32 #include "log.h"
33 #include "prefix.h"
34 #include "command.h"
35 #include "hash.h"
36 #include "if.h"
37 #include "checksum.h"
38 #include "md5.h"
39 #include "table.h"
40 #include "srcdest_table.h"
41 #include "lib_errors.h"
42
43 #include "isisd/dict.h"
44 #include "isisd/isis_constants.h"
45 #include "isisd/isis_common.h"
46 #include "isisd/isis_flags.h"
47 #include "isisd/isis_circuit.h"
48 #include "isisd/isisd.h"
49 #include "isisd/isis_lsp.h"
50 #include "isisd/isis_pdu.h"
51 #include "isisd/isis_dynhn.h"
52 #include "isisd/isis_misc.h"
53 #include "isisd/isis_csm.h"
54 #include "isisd/isis_adjacency.h"
55 #include "isisd/isis_spf.h"
56 #include "isisd/isis_te.h"
57 #include "isisd/isis_mt.h"
58 #include "isisd/isis_tlvs.h"
59 #include "isisd/fabricd.h"
60 #include "isisd/isis_tx_queue.h"
61
62 static int lsp_refresh(struct thread *thread);
63 static int lsp_l1_refresh_pseudo(struct thread *thread);
64 static int lsp_l2_refresh_pseudo(struct thread *thread);
65
66 int lsp_id_cmp(uint8_t *id1, uint8_t *id2)
67 {
68 return memcmp(id1, id2, ISIS_SYS_ID_LEN + 2);
69 }
70
71 dict_t *lsp_db_init(void)
72 {
73 dict_t *dict;
74
75 dict = dict_create(DICTCOUNT_T_MAX, (dict_comp_t)lsp_id_cmp);
76
77 return dict;
78 }
79
80 struct isis_lsp *lsp_search(uint8_t *id, dict_t *lspdb)
81 {
82 dnode_t *node;
83
84 #ifdef EXTREME_DEBUG
85 dnode_t *dn;
86
87 zlog_debug("searching db");
88 for (dn = dict_first(lspdb); dn; dn = dict_next(lspdb, dn)) {
89 zlog_debug("%s\t%pX",
90 rawlspid_print((uint8_t *)dnode_getkey(dn)),
91 dnode_get(dn));
92 }
93 #endif /* EXTREME DEBUG */
94
95 node = dict_lookup(lspdb, id);
96
97 if (node)
98 return (struct isis_lsp *)dnode_get(node);
99
100 return NULL;
101 }
102
103 static void lsp_clear_data(struct isis_lsp *lsp)
104 {
105 if (!lsp)
106 return;
107
108 isis_free_tlvs(lsp->tlvs);
109 lsp->tlvs = NULL;
110 }
111
112 static void lsp_remove_frags(struct list *frags, dict_t *lspdb);
113
114 static void lsp_destroy(struct isis_lsp *lsp)
115 {
116 struct listnode *cnode;
117 struct isis_circuit *circuit;
118
119 if (!lsp)
120 return;
121
122 for (ALL_LIST_ELEMENTS_RO(lsp->area->circuit_list, cnode, circuit))
123 isis_tx_queue_del(circuit->tx_queue, lsp);
124
125 ISIS_FLAGS_CLEAR_ALL(lsp->SSNflags);
126
127 lsp_clear_data(lsp);
128
129 if (!LSP_FRAGMENT(lsp->hdr.lsp_id)) {
130 if (lsp->lspu.frags) {
131 lsp_remove_frags(lsp->lspu.frags,
132 lsp->area->lspdb[lsp->level - 1]);
133 list_delete(&lsp->lspu.frags);
134 }
135 } else {
136 if (lsp->lspu.zero_lsp
137 && lsp->lspu.zero_lsp->lspu.frags) {
138 listnode_delete(lsp->lspu.zero_lsp->lspu.frags, lsp);
139 }
140 }
141
142 isis_spf_schedule(lsp->area, lsp->level);
143
144 if (lsp->pdu)
145 stream_free(lsp->pdu);
146 XFREE(MTYPE_ISIS_LSP, lsp);
147 }
148
149 void lsp_db_destroy(dict_t *lspdb)
150 {
151 dnode_t *dnode, *next;
152 struct isis_lsp *lsp;
153
154 dnode = dict_first(lspdb);
155 while (dnode) {
156 next = dict_next(lspdb, dnode);
157 lsp = dnode_get(dnode);
158 lsp_destroy(lsp);
159 dict_delete_free(lspdb, dnode);
160 dnode = next;
161 }
162
163 dict_free(lspdb);
164
165 return;
166 }
167
168 /*
169 * Remove all the frags belonging to the given lsp
170 */
171 static void lsp_remove_frags(struct list *frags, dict_t *lspdb)
172 {
173 dnode_t *dnode;
174 struct listnode *lnode, *lnnode;
175 struct isis_lsp *lsp;
176
177 for (ALL_LIST_ELEMENTS(frags, lnode, lnnode, lsp)) {
178 dnode = dict_lookup(lspdb, lsp->hdr.lsp_id);
179 lsp_destroy(lsp);
180 dnode_destroy(dict_delete(lspdb, dnode));
181 }
182 }
183
184 void lsp_search_and_destroy(uint8_t *id, dict_t *lspdb)
185 {
186 dnode_t *node;
187 struct isis_lsp *lsp;
188
189 node = dict_lookup(lspdb, id);
190 if (node) {
191 node = dict_delete(lspdb, node);
192 lsp = dnode_get(node);
193 /*
194 * If this is a zero lsp, remove all the frags now
195 */
196 if (LSP_FRAGMENT(lsp->hdr.lsp_id) == 0) {
197 if (lsp->lspu.frags)
198 lsp_remove_frags(lsp->lspu.frags, lspdb);
199 } else {
200 /*
201 * else just remove this frag, from the zero lsps' frag
202 * list
203 */
204 if (lsp->lspu.zero_lsp
205 && lsp->lspu.zero_lsp->lspu.frags)
206 listnode_delete(lsp->lspu.zero_lsp->lspu.frags,
207 lsp);
208 }
209 lsp_destroy(lsp);
210 dnode_destroy(node);
211 }
212 }
213
214 /*
215 * Compares a LSP to given values
216 * Params are given in net order
217 */
218 int lsp_compare(char *areatag, struct isis_lsp *lsp, uint32_t seqno,
219 uint16_t checksum, uint16_t rem_lifetime)
220 {
221 if (lsp->hdr.seqno == seqno && lsp->hdr.checksum == checksum
222 && ((lsp->hdr.rem_lifetime == 0 && rem_lifetime == 0)
223 || (lsp->hdr.rem_lifetime != 0 && rem_lifetime != 0))) {
224 if (isis->debugs & DEBUG_SNP_PACKETS) {
225 zlog_debug(
226 "ISIS-Snp (%s): Compare LSP %s seq 0x%08" PRIx32
227 ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16
228 "s",
229 areatag, rawlspid_print(lsp->hdr.lsp_id),
230 lsp->hdr.seqno, lsp->hdr.checksum,
231 lsp->hdr.rem_lifetime);
232 zlog_debug(
233 "ISIS-Snp (%s): is equal to ours seq 0x%08" PRIx32
234 ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16
235 "s",
236 areatag, seqno, checksum, rem_lifetime);
237 }
238 return LSP_EQUAL;
239 }
240
241 /*
242 * LSPs with identical checksums should only be treated as newer if:
243 * a) The current LSP has a remaining lifetime != 0 and the other LSP
244 * has a
245 * remaining lifetime == 0. In this case, we should participate in
246 * the purge
247 * and should not treat the current LSP with remaining lifetime == 0
248 * as older.
249 * b) The LSP has an incorrect checksum. In this case, we need to react
250 * as given
251 * in 7.3.16.2.
252 */
253 if (seqno > lsp->hdr.seqno
254 || (seqno == lsp->hdr.seqno
255 && ((lsp->hdr.rem_lifetime != 0 && rem_lifetime == 0)
256 || lsp->hdr.checksum != checksum))) {
257 if (isis->debugs & DEBUG_SNP_PACKETS) {
258 zlog_debug(
259 "ISIS-Snp (%s): Compare LSP %s seq 0x%08" PRIx32
260 ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16
261 "s",
262 areatag, rawlspid_print(lsp->hdr.lsp_id), seqno,
263 checksum, rem_lifetime);
264 zlog_debug(
265 "ISIS-Snp (%s): is newer than ours seq 0x%08" PRIx32
266 ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16
267 "s",
268 areatag, lsp->hdr.seqno, lsp->hdr.checksum,
269 lsp->hdr.rem_lifetime);
270 }
271 return LSP_NEWER;
272 }
273 if (isis->debugs & DEBUG_SNP_PACKETS) {
274 zlog_debug("ISIS-Snp (%s): Compare LSP %s seq 0x%08" PRIx32
275 ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16 "s",
276 areatag, rawlspid_print(lsp->hdr.lsp_id), seqno,
277 checksum, rem_lifetime);
278 zlog_debug(
279 "ISIS-Snp (%s): is older than ours seq 0x%08" PRIx32
280 ", cksum 0x%04" PRIx16 ", lifetime %" PRIu16 "s",
281 areatag, lsp->hdr.seqno, lsp->hdr.checksum,
282 lsp->hdr.rem_lifetime);
283 }
284
285 return LSP_OLDER;
286 }
287
288 static void put_lsp_hdr(struct isis_lsp *lsp, size_t *len_pointer, bool keep)
289 {
290 uint8_t pdu_type =
291 (lsp->level == IS_LEVEL_1) ? L1_LINK_STATE : L2_LINK_STATE;
292 struct isis_lsp_hdr *hdr = &lsp->hdr;
293 struct stream *stream = lsp->pdu;
294 size_t orig_getp = 0, orig_endp = 0;
295
296 if (keep) {
297 orig_getp = stream_get_getp(lsp->pdu);
298 orig_endp = stream_get_endp(lsp->pdu);
299 }
300
301 stream_set_getp(lsp->pdu, 0);
302 stream_set_endp(lsp->pdu, 0);
303
304 fill_fixed_hdr(pdu_type, stream);
305
306 if (len_pointer)
307 *len_pointer = stream_get_endp(stream);
308 stream_putw(stream, hdr->pdu_len);
309 stream_putw(stream, hdr->rem_lifetime);
310 stream_put(stream, hdr->lsp_id, sizeof(hdr->lsp_id));
311 stream_putl(stream, hdr->seqno);
312 stream_putw(stream, hdr->checksum);
313 stream_putc(stream, hdr->lsp_bits);
314
315 if (keep) {
316 stream_set_endp(lsp->pdu, orig_endp);
317 stream_set_getp(lsp->pdu, orig_getp);
318 }
319 }
320
321 static void lsp_add_auth(struct isis_lsp *lsp)
322 {
323 struct isis_passwd *passwd;
324 passwd = (lsp->level == IS_LEVEL_1) ? &lsp->area->area_passwd
325 : &lsp->area->domain_passwd;
326 isis_tlvs_add_auth(lsp->tlvs, passwd);
327 }
328
329 static void lsp_pack_pdu(struct isis_lsp *lsp)
330 {
331 if (!lsp->tlvs)
332 lsp->tlvs = isis_alloc_tlvs();
333
334 lsp_add_auth(lsp);
335
336 size_t len_pointer;
337 put_lsp_hdr(lsp, &len_pointer, false);
338 isis_pack_tlvs(lsp->tlvs, lsp->pdu, len_pointer, false, true);
339
340 lsp->hdr.pdu_len = stream_get_endp(lsp->pdu);
341 lsp->hdr.checksum =
342 ntohs(fletcher_checksum(STREAM_DATA(lsp->pdu) + 12,
343 stream_get_endp(lsp->pdu) - 12, 12));
344 }
345
346 void lsp_inc_seqno(struct isis_lsp *lsp, uint32_t seqno)
347 {
348 uint32_t newseq;
349
350 if (seqno == 0 || lsp->hdr.seqno > seqno)
351 newseq = lsp->hdr.seqno + 1;
352 else
353 newseq = seqno + 1;
354
355 lsp->hdr.seqno = newseq;
356
357 lsp_pack_pdu(lsp);
358 isis_spf_schedule(lsp->area, lsp->level);
359 }
360
361 static void lsp_purge_add_poi(struct isis_lsp *lsp,
362 const uint8_t *sender)
363 {
364 if (!lsp->area->purge_originator)
365 return;
366
367 /* add purge originator identification */
368 if (!lsp->tlvs)
369 lsp->tlvs = isis_alloc_tlvs();
370 isis_tlvs_set_purge_originator(lsp->tlvs, isis->sysid, sender);
371 isis_tlvs_set_dynamic_hostname(lsp->tlvs, cmd_hostname_get());
372 }
373
374 static void lsp_purge(struct isis_lsp *lsp, int level,
375 const uint8_t *sender)
376 {
377 /* reset stream */
378 lsp_clear_data(lsp);
379 stream_reset(lsp->pdu);
380
381 /* update header */
382 lsp->hdr.checksum = 0;
383 lsp->hdr.rem_lifetime = 0;
384 lsp->level = level;
385 lsp->age_out = lsp->area->max_lsp_lifetime[level - 1];
386
387 lsp_purge_add_poi(lsp, sender);
388
389 lsp_pack_pdu(lsp);
390 lsp_flood(lsp, NULL);
391 }
392
393 /*
394 * Generates checksum for LSP and its frags
395 */
396 static void lsp_seqno_update(struct isis_lsp *lsp0)
397 {
398 struct isis_lsp *lsp;
399 struct listnode *node;
400
401 lsp_inc_seqno(lsp0, 0);
402
403 if (!lsp0->lspu.frags)
404 return;
405
406 for (ALL_LIST_ELEMENTS_RO(lsp0->lspu.frags, node, lsp)) {
407 if (lsp->tlvs)
408 lsp_inc_seqno(lsp, 0);
409 else
410 lsp_purge(lsp, lsp0->level, NULL);
411 }
412
413 return;
414 }
415
416 static uint8_t lsp_bits_generate(int level, int overload_bit, int attached_bit)
417 {
418 uint8_t lsp_bits = 0;
419 if (level == IS_LEVEL_1)
420 lsp_bits = IS_LEVEL_1;
421 else
422 lsp_bits = IS_LEVEL_1_AND_2;
423 if (overload_bit)
424 lsp_bits |= overload_bit;
425 if (attached_bit)
426 lsp_bits |= attached_bit;
427 return lsp_bits;
428 }
429
430 static void lsp_update_data(struct isis_lsp *lsp, struct isis_lsp_hdr *hdr,
431 struct isis_tlvs *tlvs, struct stream *stream,
432 struct isis_area *area, int level)
433 {
434 /* free the old lsp data */
435 lsp_clear_data(lsp);
436
437 /* copying only the relevant part of our stream */
438 if (lsp->pdu != NULL)
439 stream_free(lsp->pdu);
440 lsp->pdu = stream_dup(stream);
441
442 memcpy(&lsp->hdr, hdr, sizeof(lsp->hdr));
443 lsp->area = area;
444 lsp->level = level;
445 lsp->age_out = ZERO_AGE_LIFETIME;
446 lsp->installed = time(NULL);
447
448 lsp->tlvs = tlvs;
449
450 if (area->dynhostname && lsp->tlvs->hostname
451 && lsp->hdr.rem_lifetime) {
452 isis_dynhn_insert(lsp->hdr.lsp_id, lsp->tlvs->hostname,
453 (lsp->hdr.lsp_bits & LSPBIT_IST)
454 == IS_LEVEL_1_AND_2
455 ? IS_LEVEL_2
456 : IS_LEVEL_1);
457 }
458
459 return;
460 }
461
462 static void lsp_link_fragment(struct isis_lsp *lsp, struct isis_lsp *lsp0)
463 {
464 if (!LSP_FRAGMENT(lsp->hdr.lsp_id)) {
465 /* zero lsp -> create list to store fragments */
466 lsp->lspu.frags = list_new();
467 } else {
468 /* fragment -> set backpointer and add to zero lsps list */
469 assert(lsp0);
470 lsp->lspu.zero_lsp = lsp0;
471 listnode_add(lsp0->lspu.frags, lsp);
472 }
473 }
474
475 void lsp_update(struct isis_lsp *lsp, struct isis_lsp_hdr *hdr,
476 struct isis_tlvs *tlvs, struct stream *stream,
477 struct isis_area *area, int level, bool confusion)
478 {
479 if (lsp->own_lsp) {
480 flog_err(
481 EC_LIB_DEVELOPMENT,
482 "ISIS-Upd (%s): BUG updating LSP %s still marked as own LSP",
483 area->area_tag, rawlspid_print(lsp->hdr.lsp_id));
484 lsp_clear_data(lsp);
485 lsp->own_lsp = 0;
486 }
487
488 if (confusion) {
489 lsp_purge(lsp, level, NULL);
490 } else {
491 lsp_update_data(lsp, hdr, tlvs, stream, area, level);
492 }
493
494 if (LSP_FRAGMENT(lsp->hdr.lsp_id) && !lsp->lspu.zero_lsp) {
495 uint8_t lspid[ISIS_SYS_ID_LEN + 2];
496 struct isis_lsp *lsp0;
497
498 memcpy(lspid, lsp->hdr.lsp_id, ISIS_SYS_ID_LEN + 1);
499 LSP_FRAGMENT(lspid) = 0;
500 lsp0 = lsp_search(lspid, area->lspdb[level - 1]);
501 if (lsp0)
502 lsp_link_fragment(lsp, lsp0);
503 }
504
505 if (lsp->hdr.seqno)
506 isis_spf_schedule(lsp->area, lsp->level);
507 }
508
509 /* creation of LSP directly from what we received */
510 struct isis_lsp *lsp_new_from_recv(struct isis_lsp_hdr *hdr,
511 struct isis_tlvs *tlvs,
512 struct stream *stream, struct isis_lsp *lsp0,
513 struct isis_area *area, int level)
514 {
515 struct isis_lsp *lsp;
516
517 lsp = XCALLOC(MTYPE_ISIS_LSP, sizeof(struct isis_lsp));
518 lsp_update_data(lsp, hdr, tlvs, stream, area, level);
519 lsp_link_fragment(lsp, lsp0);
520
521 return lsp;
522 }
523
524 static void lsp_adjust_stream(struct isis_lsp *lsp)
525 {
526 if (lsp->pdu) {
527 if (STREAM_SIZE(lsp->pdu) == LLC_LEN + lsp->area->lsp_mtu)
528 return;
529 stream_free(lsp->pdu);
530 }
531
532 lsp->pdu = stream_new(LLC_LEN + lsp->area->lsp_mtu);
533 }
534
535 struct isis_lsp *lsp_new(struct isis_area *area, uint8_t *lsp_id,
536 uint16_t rem_lifetime, uint32_t seqno,
537 uint8_t lsp_bits, uint16_t checksum,
538 struct isis_lsp *lsp0, int level)
539 {
540 struct isis_lsp *lsp;
541
542 lsp = XCALLOC(MTYPE_ISIS_LSP, sizeof(struct isis_lsp));
543 lsp->area = area;
544
545 lsp_adjust_stream(lsp);
546
547 /* Minimal LSP PDU size */
548 lsp->hdr.pdu_len = ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN;
549 memcpy(lsp->hdr.lsp_id, lsp_id, sizeof(lsp->hdr.lsp_id));
550 lsp->hdr.checksum = checksum;
551 lsp->hdr.seqno = seqno;
552 lsp->hdr.rem_lifetime = rem_lifetime;
553 lsp->hdr.lsp_bits = lsp_bits;
554 lsp->level = level;
555 lsp->age_out = ZERO_AGE_LIFETIME;
556 lsp_link_fragment(lsp, lsp0);
557 put_lsp_hdr(lsp, NULL, false);
558
559 if (isis->debugs & DEBUG_EVENTS)
560 zlog_debug("New LSP with ID %s-%02x-%02x len %d seqnum %08x",
561 sysid_print(lsp_id), LSP_PSEUDO_ID(lsp->hdr.lsp_id),
562 LSP_FRAGMENT(lsp->hdr.lsp_id), lsp->hdr.pdu_len,
563 lsp->hdr.seqno);
564
565 return lsp;
566 }
567
568 void lsp_insert(struct isis_lsp *lsp, dict_t *lspdb)
569 {
570 dict_alloc_insert(lspdb, lsp->hdr.lsp_id, lsp);
571 if (lsp->hdr.seqno)
572 isis_spf_schedule(lsp->area, lsp->level);
573 }
574
575 /*
576 * Build a list of LSPs with non-zero ht bounded by start and stop ids
577 */
578 void lsp_build_list_nonzero_ht(uint8_t *start_id, uint8_t *stop_id,
579 struct list *list, dict_t *lspdb)
580 {
581 for (dnode_t *curr = dict_lower_bound(lspdb, start_id);
582 curr; curr = dict_next(lspdb, curr)) {
583 struct isis_lsp *lsp = curr->dict_data;
584
585 if (memcmp(lsp->hdr.lsp_id, stop_id,
586 ISIS_SYS_ID_LEN + 2) > 0)
587 break;
588
589 if (lsp->hdr.rem_lifetime)
590 listnode_add(list, lsp);
591 }
592 }
593
594 static void lsp_set_time(struct isis_lsp *lsp)
595 {
596 assert(lsp);
597
598 if (lsp->hdr.rem_lifetime == 0) {
599 if (lsp->age_out > 0)
600 lsp->age_out--;
601 return;
602 }
603
604 lsp->hdr.rem_lifetime--;
605 if (lsp->pdu && stream_get_endp(lsp->pdu) >= 12)
606 stream_putw_at(lsp->pdu, 10, lsp->hdr.rem_lifetime);
607 }
608
609 static void lspid_print(uint8_t *lsp_id, uint8_t *trg, char dynhost, char frag)
610 {
611 struct isis_dynhn *dyn = NULL;
612 uint8_t id[SYSID_STRLEN];
613
614 if (dynhost)
615 dyn = dynhn_find_by_id(lsp_id);
616 else
617 dyn = NULL;
618
619 if (dyn)
620 sprintf((char *)id, "%.14s", dyn->hostname);
621 else if (!memcmp(isis->sysid, lsp_id, ISIS_SYS_ID_LEN) && dynhost)
622 sprintf((char *)id, "%.14s", cmd_hostname_get());
623 else
624 memcpy(id, sysid_print(lsp_id), 15);
625 if (frag)
626 sprintf((char *)trg, "%s.%02x-%02x", id, LSP_PSEUDO_ID(lsp_id),
627 LSP_FRAGMENT(lsp_id));
628 else
629 sprintf((char *)trg, "%s.%02x", id, LSP_PSEUDO_ID(lsp_id));
630 }
631
632 /* Convert the lsp attribute bits to attribute string */
633 static const char *lsp_bits2string(uint8_t lsp_bits, char *buf, size_t buf_size)
634 {
635 char *pos = buf;
636
637 if (!lsp_bits)
638 return " none";
639
640 if (buf_size < 2 * 3)
641 return " error";
642
643 /* we only focus on the default metric */
644 pos += sprintf(pos, "%d/",
645 ISIS_MASK_LSP_ATT_DEFAULT_BIT(lsp_bits) ? 1 : 0);
646
647 pos += sprintf(pos, "%d/",
648 ISIS_MASK_LSP_PARTITION_BIT(lsp_bits) ? 1 : 0);
649
650 sprintf(pos, "%d", ISIS_MASK_LSP_OL_BIT(lsp_bits) ? 1 : 0);
651
652 return buf;
653 }
654
655 /* this function prints the lsp on show isis database */
656 void lsp_print(struct isis_lsp *lsp, struct vty *vty, char dynhost)
657 {
658 uint8_t LSPid[255];
659 char age_out[8];
660 char b[200];
661
662 lspid_print(lsp->hdr.lsp_id, LSPid, dynhost, 1);
663 vty_out(vty, "%-21s%c ", LSPid, lsp->own_lsp ? '*' : ' ');
664 vty_out(vty, "%5" PRIu16 " ", lsp->hdr.pdu_len);
665 vty_out(vty, "0x%08" PRIx32 " ", lsp->hdr.seqno);
666 vty_out(vty, "0x%04" PRIx16 " ", lsp->hdr.checksum);
667 if (lsp->hdr.rem_lifetime == 0) {
668 snprintf(age_out, 8, "(%d)", lsp->age_out);
669 age_out[7] = '\0';
670 vty_out(vty, "%7s ", age_out);
671 } else
672 vty_out(vty, " %5" PRIu16 " ", lsp->hdr.rem_lifetime);
673 vty_out(vty, "%s\n", lsp_bits2string(lsp->hdr.lsp_bits, b, sizeof(b)));
674 }
675
676 void lsp_print_detail(struct isis_lsp *lsp, struct vty *vty, char dynhost)
677 {
678 lsp_print(lsp, vty, dynhost);
679 if (lsp->tlvs)
680 vty_multiline(vty, " ", "%s", isis_format_tlvs(lsp->tlvs));
681 vty_out(vty, "\n");
682 }
683
684 /* print all the lsps info in the local lspdb */
685 int lsp_print_all(struct vty *vty, dict_t *lspdb, char detail, char dynhost)
686 {
687
688 dnode_t *node = dict_first(lspdb), *next;
689 int lsp_count = 0;
690
691 if (detail == ISIS_UI_LEVEL_BRIEF) {
692 while (node != NULL) {
693 /* I think it is unnecessary, so I comment it out */
694 /* dict_contains (lspdb, node); */
695 next = dict_next(lspdb, node);
696 lsp_print(dnode_get(node), vty, dynhost);
697 node = next;
698 lsp_count++;
699 }
700 } else if (detail == ISIS_UI_LEVEL_DETAIL) {
701 while (node != NULL) {
702 next = dict_next(lspdb, node);
703 lsp_print_detail(dnode_get(node), vty, dynhost);
704 node = next;
705 lsp_count++;
706 }
707 }
708
709 return lsp_count;
710 }
711
712 static uint16_t lsp_rem_lifetime(struct isis_area *area, int level)
713 {
714 uint16_t rem_lifetime;
715
716 /* Add jitter to configured LSP lifetime */
717 rem_lifetime =
718 isis_jitter(area->max_lsp_lifetime[level - 1], MAX_AGE_JITTER);
719
720 /* No jitter if the max refresh will be less than configure gen interval
721 */
722 /* N.B. this calucation is acceptable since rem_lifetime is in
723 * [332,65535] at
724 * this point */
725 if (area->lsp_gen_interval[level - 1] > (rem_lifetime - 300))
726 rem_lifetime = area->max_lsp_lifetime[level - 1];
727
728 return rem_lifetime;
729 }
730
731 static uint16_t lsp_refresh_time(struct isis_lsp *lsp, uint16_t rem_lifetime)
732 {
733 struct isis_area *area = lsp->area;
734 int level = lsp->level;
735 uint16_t refresh_time;
736
737 /* Add jitter to LSP refresh time */
738 refresh_time =
739 isis_jitter(area->lsp_refresh[level - 1], MAX_LSP_GEN_JITTER);
740
741 /* RFC 4444 : make sure the refresh time is at least less than 300
742 * of the remaining lifetime and more than gen interval */
743 if (refresh_time <= area->lsp_gen_interval[level - 1]
744 || refresh_time > (rem_lifetime - 300))
745 refresh_time = rem_lifetime - 300;
746
747 /* In cornercases, refresh_time might be <= lsp_gen_interval, however
748 * we accept this violation to satisfy refresh_time <= rem_lifetime -
749 * 300 */
750
751 return refresh_time;
752 }
753
754 static void lsp_build_ext_reach_ipv4(struct isis_lsp *lsp,
755 struct isis_area *area)
756 {
757 struct route_table *er_table = get_ext_reach(area, AF_INET, lsp->level);
758 if (!er_table)
759 return;
760
761 for (struct route_node *rn = route_top(er_table); rn;
762 rn = route_next(rn)) {
763 if (!rn->info)
764 continue;
765
766 struct prefix_ipv4 *ipv4 = (struct prefix_ipv4 *)&rn->p;
767 struct isis_ext_info *info = rn->info;
768
769 uint32_t metric = info->metric;
770 if (metric > MAX_WIDE_PATH_METRIC)
771 metric = MAX_WIDE_PATH_METRIC;
772 if (area->oldmetric && metric > 0x3f)
773 metric = 0x3f;
774
775 if (area->oldmetric)
776 isis_tlvs_add_oldstyle_ip_reach(lsp->tlvs, ipv4,
777 metric);
778 if (area->newmetric)
779 isis_tlvs_add_extended_ip_reach(lsp->tlvs, ipv4,
780 metric);
781 }
782 }
783
784 static void lsp_build_ext_reach_ipv6(struct isis_lsp *lsp,
785 struct isis_area *area)
786 {
787 struct route_table *er_table =
788 get_ext_reach(area, AF_INET6, lsp->level);
789 if (!er_table)
790 return;
791
792 for (struct route_node *rn = route_top(er_table); rn;
793 rn = srcdest_route_next(rn)) {
794 if (!rn->info)
795 continue;
796 struct isis_ext_info *info = rn->info;
797
798 struct prefix_ipv6 *p, *src_p;
799 srcdest_rnode_prefixes(rn, (const struct prefix **)&p,
800 (const struct prefix **)&src_p);
801
802 uint32_t metric = info->metric;
803 if (info->metric > MAX_WIDE_PATH_METRIC)
804 metric = MAX_WIDE_PATH_METRIC;
805
806 if (!src_p || !src_p->prefixlen) {
807 isis_tlvs_add_ipv6_reach(lsp->tlvs,
808 isis_area_ipv6_topology(area),
809 p, metric);
810 } else if (isis_area_ipv6_dstsrc_enabled(area)) {
811 isis_tlvs_add_ipv6_dstsrc_reach(lsp->tlvs,
812 ISIS_MT_IPV6_DSTSRC,
813 p, src_p, metric);
814 }
815 }
816 }
817
818 static void lsp_build_ext_reach(struct isis_lsp *lsp, struct isis_area *area)
819 {
820 lsp_build_ext_reach_ipv4(lsp, area);
821 lsp_build_ext_reach_ipv6(lsp, area);
822 }
823
824 static struct isis_lsp *lsp_next_frag(uint8_t frag_num, struct isis_lsp *lsp0,
825 struct isis_area *area, int level)
826 {
827 struct isis_lsp *lsp;
828 uint8_t frag_id[ISIS_SYS_ID_LEN + 2];
829
830 memcpy(frag_id, lsp0->hdr.lsp_id, ISIS_SYS_ID_LEN + 1);
831 LSP_FRAGMENT(frag_id) = frag_num;
832
833 lsp = lsp_search(frag_id, area->lspdb[level - 1]);
834 if (lsp) {
835 lsp_clear_data(lsp);
836 if (!lsp->lspu.zero_lsp)
837 lsp_link_fragment(lsp, lsp0);
838 return lsp;
839 }
840
841 lsp = lsp_new(area, frag_id, lsp0->hdr.rem_lifetime, 0,
842 lsp_bits_generate(level, area->overload_bit,
843 area->attached_bit),
844 0, lsp0, level);
845 lsp->own_lsp = 1;
846 lsp_insert(lsp, area->lspdb[level - 1]);
847 return lsp;
848 }
849
850 /*
851 * Builds the LSP data part. This func creates a new frag whenever
852 * area->lsp_frag_threshold is exceeded.
853 */
854 static void lsp_build(struct isis_lsp *lsp, struct isis_area *area)
855 {
856 int level = lsp->level;
857 char buf[PREFIX2STR_BUFFER];
858 struct listnode *node;
859 struct isis_lsp *frag;
860
861 lsp_clear_data(lsp);
862 for (ALL_LIST_ELEMENTS_RO(lsp->lspu.frags, node, frag))
863 lsp_clear_data(frag);
864
865 lsp->tlvs = isis_alloc_tlvs();
866 lsp_debug("ISIS (%s): Constructing local system LSP for level %d",
867 area->area_tag, level);
868
869 lsp->hdr.lsp_bits = lsp_bits_generate(level, area->overload_bit,
870 area->attached_bit);
871
872 lsp_add_auth(lsp);
873
874 isis_tlvs_add_area_addresses(lsp->tlvs, area->area_addrs);
875
876 /* Protocols Supported */
877 if (area->ip_circuits > 0 || area->ipv6_circuits > 0) {
878 struct nlpids nlpids = {.count = 0};
879 if (area->ip_circuits > 0) {
880 lsp_debug(
881 "ISIS (%s): Found IPv4 circuit, adding IPv4 to NLPIDs",
882 area->area_tag);
883 nlpids.nlpids[nlpids.count] = NLPID_IP;
884 nlpids.count++;
885 }
886 if (area->ipv6_circuits > 0) {
887 lsp_debug(
888 "ISIS (%s): Found IPv6 circuit, adding IPv6 to NLPIDs",
889 area->area_tag);
890 nlpids.nlpids[nlpids.count] = NLPID_IPV6;
891 nlpids.count++;
892 }
893 isis_tlvs_set_protocols_supported(lsp->tlvs, &nlpids);
894 }
895
896 if (area_is_mt(area)) {
897 lsp_debug("ISIS (%s): Adding MT router tlv...", area->area_tag);
898
899 struct isis_area_mt_setting **mt_settings;
900 unsigned int mt_count;
901
902 mt_settings = area_mt_settings(area, &mt_count);
903 for (unsigned int i = 0; i < mt_count; i++) {
904 isis_tlvs_add_mt_router_info(
905 lsp->tlvs, mt_settings[i]->mtid,
906 mt_settings[i]->overload, false);
907 lsp_debug("ISIS (%s): MT %s", area->area_tag,
908 isis_mtid2str(mt_settings[i]->mtid));
909 }
910 } else {
911 lsp_debug("ISIS (%s): Not adding MT router tlv (disabled)",
912 area->area_tag);
913 }
914 /* Dynamic Hostname */
915 if (area->dynhostname) {
916 isis_tlvs_set_dynamic_hostname(lsp->tlvs, cmd_hostname_get());
917 lsp_debug("ISIS (%s): Adding dynamic hostname '%s'",
918 area->area_tag, cmd_hostname_get());
919 } else {
920 lsp_debug("ISIS (%s): Not adding dynamic hostname (disabled)",
921 area->area_tag);
922 }
923
924 /* IPv4 address and TE router ID TLVs. In case of the first one we don't
925 * follow "C" vendor, but "J" vendor behavior - one IPv4 address is put
926 * into
927 * LSP and this address is same as router id. */
928 if (isis->router_id != 0) {
929 struct in_addr id = {.s_addr = isis->router_id};
930 inet_ntop(AF_INET, &id, buf, sizeof(buf));
931 lsp_debug("ISIS (%s): Adding router ID %s as IPv4 tlv.",
932 area->area_tag, buf);
933 isis_tlvs_add_ipv4_address(lsp->tlvs, &id);
934
935 /* Exactly same data is put into TE router ID TLV, but only if
936 * new style
937 * TLV's are in use. */
938 if (area->newmetric) {
939
940 lsp_debug(
941 "ISIS (%s): Adding router ID also as TE router ID tlv.",
942 area->area_tag);
943 isis_tlvs_set_te_router_id(lsp->tlvs, &id);
944 }
945 } else {
946 lsp_debug("ISIS (%s): Router ID is unset. Not adding tlv.",
947 area->area_tag);
948 }
949
950 lsp_debug("ISIS (%s): Adding circuit specific information.",
951 area->area_tag);
952
953 if (fabricd) {
954 lsp_debug(
955 "ISIS (%s): Adding tier %" PRIu8 " spine-leaf-extension tlv.",
956 area->area_tag, fabricd_tier(area));
957 isis_tlvs_add_spine_leaf(lsp->tlvs, fabricd_tier(area), true,
958 false, false, false);
959 }
960
961 struct isis_circuit *circuit;
962 for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) {
963 if (!circuit->interface)
964 lsp_debug(
965 "ISIS (%s): Processing %s circuit %p with unknown interface",
966 area->area_tag,
967 circuit_type2string(circuit->circ_type),
968 circuit);
969 else
970 lsp_debug("ISIS (%s): Processing %s circuit %s",
971 area->area_tag,
972 circuit_type2string(circuit->circ_type),
973 circuit->interface->name);
974
975 if (circuit->state != C_STATE_UP) {
976 lsp_debug("ISIS (%s): Circuit is not up, ignoring.",
977 area->area_tag);
978 continue;
979 }
980
981 uint32_t metric = area->oldmetric
982 ? circuit->metric[level - 1]
983 : circuit->te_metric[level - 1];
984
985 if (circuit->ip_router && circuit->ip_addrs
986 && circuit->ip_addrs->count > 0) {
987 lsp_debug(
988 "ISIS (%s): Circuit has IPv4 active, adding respective TLVs.",
989 area->area_tag);
990 struct listnode *ipnode;
991 struct prefix_ipv4 *ipv4;
992 for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, ipnode,
993 ipv4)) {
994 if (area->oldmetric) {
995 lsp_debug(
996 "ISIS (%s): Adding old-style IP reachability for %s",
997 area->area_tag,
998 prefix2str(ipv4, buf,
999 sizeof(buf)));
1000 isis_tlvs_add_oldstyle_ip_reach(
1001 lsp->tlvs, ipv4, metric);
1002 }
1003
1004 if (area->newmetric) {
1005 lsp_debug(
1006 "ISIS (%s): Adding te-style IP reachability for %s",
1007 area->area_tag,
1008 prefix2str(ipv4, buf,
1009 sizeof(buf)));
1010 isis_tlvs_add_extended_ip_reach(
1011 lsp->tlvs, ipv4, metric);
1012 }
1013 }
1014 }
1015
1016 if (circuit->ipv6_router && circuit->ipv6_non_link
1017 && circuit->ipv6_non_link->count > 0) {
1018 struct listnode *ipnode;
1019 struct prefix_ipv6 *ipv6;
1020 for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_non_link,
1021 ipnode, ipv6)) {
1022 lsp_debug(
1023 "ISIS (%s): Adding IPv6 reachability for %s",
1024 area->area_tag,
1025 prefix2str(ipv6, buf, sizeof(buf)));
1026 isis_tlvs_add_ipv6_reach(
1027 lsp->tlvs,
1028 isis_area_ipv6_topology(area), ipv6,
1029 metric);
1030 }
1031 }
1032
1033 switch (circuit->circ_type) {
1034 case CIRCUIT_T_BROADCAST:
1035 if (level & circuit->is_type) {
1036 uint8_t *ne_id =
1037 (level == IS_LEVEL_1)
1038 ? circuit->u.bc.l1_desig_is
1039 : circuit->u.bc.l2_desig_is;
1040
1041 if (LSP_PSEUDO_ID(ne_id)) {
1042 if (area->oldmetric) {
1043 lsp_debug(
1044 "ISIS (%s): Adding DIS %s.%02x as old-style neighbor",
1045 area->area_tag,
1046 sysid_print(ne_id),
1047 LSP_PSEUDO_ID(ne_id));
1048 isis_tlvs_add_oldstyle_reach(
1049 lsp->tlvs, ne_id,
1050 metric);
1051 }
1052 if (area->newmetric) {
1053 uint8_t subtlvs[256];
1054 uint8_t subtlv_len;
1055
1056 if (IS_MPLS_TE(isisMplsTE)
1057 && circuit->interface
1058 && HAS_LINK_PARAMS(
1059 circuit->interface))
1060 subtlv_len = add_te_subtlvs(
1061 subtlvs,
1062 circuit->mtc);
1063 else
1064 subtlv_len = 0;
1065
1066 tlvs_add_mt_bcast(
1067 lsp->tlvs, circuit,
1068 level, ne_id, metric,
1069 subtlvs, subtlv_len);
1070 }
1071 }
1072 } else {
1073 lsp_debug(
1074 "ISIS (%s): Circuit is not active for current level. Not adding IS neighbors",
1075 area->area_tag);
1076 }
1077 break;
1078 case CIRCUIT_T_P2P: {
1079 struct isis_adjacency *nei = circuit->u.p2p.neighbor;
1080 if (nei && nei->adj_state == ISIS_ADJ_UP
1081 && (level & nei->circuit_t)) {
1082 uint8_t ne_id[7];
1083 memcpy(ne_id, nei->sysid, ISIS_SYS_ID_LEN);
1084 LSP_PSEUDO_ID(ne_id) = 0;
1085
1086 if (area->oldmetric) {
1087 lsp_debug(
1088 "ISIS (%s): Adding old-style is reach for %s",
1089 area->area_tag,
1090 sysid_print(ne_id));
1091 isis_tlvs_add_oldstyle_reach(
1092 lsp->tlvs, ne_id, metric);
1093 }
1094 if (area->newmetric) {
1095 uint8_t subtlvs[256];
1096 uint8_t subtlv_len;
1097
1098 if (IS_MPLS_TE(isisMplsTE)
1099 && circuit->interface != NULL
1100 && HAS_LINK_PARAMS(
1101 circuit->interface))
1102 /* Update Local and Remote IP
1103 * address for MPLS TE circuit
1104 * parameters */
1105 /* NOTE sure that it is the
1106 * pertinent place for that
1107 * updates */
1108 /* Local IP address could be
1109 * updated in isis_circuit.c -
1110 * isis_circuit_add_addr() */
1111 /* But, where update remote IP
1112 * address ? in isis_pdu.c -
1113 * process_p2p_hello() ? */
1114
1115 /* Add SubTLVs & Adjust real
1116 * size of SubTLVs */
1117 subtlv_len = add_te_subtlvs(
1118 subtlvs, circuit->mtc);
1119 else
1120 /* Or keep only TE metric with
1121 * no SubTLVs if MPLS_TE is off
1122 */
1123 subtlv_len = 0;
1124
1125 uint32_t neighbor_metric;
1126 if (fabricd_tier(area) == 0) {
1127 neighbor_metric = 0xffe;
1128 } else {
1129 neighbor_metric = metric;
1130 }
1131
1132 tlvs_add_mt_p2p(lsp->tlvs, circuit,
1133 ne_id, neighbor_metric,
1134 subtlvs, subtlv_len);
1135 }
1136 } else {
1137 lsp_debug(
1138 "ISIS (%s): No adjacency for given level on this circuit. Not adding IS neighbors",
1139 area->area_tag);
1140 }
1141 } break;
1142 case CIRCUIT_T_LOOPBACK:
1143 break;
1144 default:
1145 zlog_warn("lsp_area_create: unknown circuit type");
1146 }
1147 }
1148
1149 lsp_build_ext_reach(lsp, area);
1150
1151 struct isis_tlvs *tlvs = lsp->tlvs;
1152 lsp->tlvs = NULL;
1153
1154 lsp_adjust_stream(lsp);
1155 lsp_pack_pdu(lsp);
1156 size_t tlv_space = STREAM_WRITEABLE(lsp->pdu) - LLC_LEN;
1157 lsp_clear_data(lsp);
1158
1159 struct list *fragments = isis_fragment_tlvs(tlvs, tlv_space);
1160 if (!fragments) {
1161 zlog_warn("BUG: could not fragment own LSP:");
1162 log_multiline(LOG_WARNING, " ", "%s",
1163 isis_format_tlvs(tlvs));
1164 isis_free_tlvs(tlvs);
1165 return;
1166 }
1167 isis_free_tlvs(tlvs);
1168
1169 bool fragment_overflow = false;
1170 frag = lsp;
1171 for (ALL_LIST_ELEMENTS_RO(fragments, node, tlvs)) {
1172 if (node != listhead(fragments)) {
1173 if (LSP_FRAGMENT(frag->hdr.lsp_id) == 255) {
1174 if (!fragment_overflow) {
1175 fragment_overflow = true;
1176 zlog_warn(
1177 "ISIS (%s): Too much information for 256 fragments",
1178 area->area_tag);
1179 }
1180 isis_free_tlvs(tlvs);
1181 continue;
1182 }
1183
1184 frag = lsp_next_frag(LSP_FRAGMENT(frag->hdr.lsp_id) + 1,
1185 lsp, area, level);
1186 lsp_adjust_stream(frag);
1187 }
1188 frag->tlvs = tlvs;
1189 }
1190
1191 list_delete(&fragments);
1192 lsp_debug("ISIS (%s): LSP construction is complete. Serializing...",
1193 area->area_tag);
1194 return;
1195 }
1196
1197 /*
1198 * 7.3.7 and 7.3.9 Generation on non-pseudonode LSPs
1199 */
1200 int lsp_generate(struct isis_area *area, int level)
1201 {
1202 struct isis_lsp *oldlsp, *newlsp;
1203 uint32_t seq_num = 0;
1204 uint8_t lspid[ISIS_SYS_ID_LEN + 2];
1205 uint16_t rem_lifetime, refresh_time;
1206
1207 if ((area == NULL) || (area->is_type & level) != level)
1208 return ISIS_ERROR;
1209
1210 memset(&lspid, 0, ISIS_SYS_ID_LEN + 2);
1211 memcpy(&lspid, isis->sysid, ISIS_SYS_ID_LEN);
1212
1213 /* only builds the lsp if the area shares the level */
1214 oldlsp = lsp_search(lspid, area->lspdb[level - 1]);
1215 if (oldlsp) {
1216 /* FIXME: we should actually initiate a purge */
1217 seq_num = oldlsp->hdr.seqno;
1218 lsp_search_and_destroy(oldlsp->hdr.lsp_id,
1219 area->lspdb[level - 1]);
1220 }
1221 rem_lifetime = lsp_rem_lifetime(area, level);
1222 newlsp =
1223 lsp_new(area, lspid, rem_lifetime, seq_num,
1224 area->is_type | area->overload_bit | area->attached_bit,
1225 0, NULL, level);
1226 newlsp->area = area;
1227 newlsp->own_lsp = 1;
1228
1229 lsp_insert(newlsp, area->lspdb[level - 1]);
1230 /* build_lsp_data (newlsp, area); */
1231 lsp_build(newlsp, area);
1232 /* time to calculate our checksum */
1233 lsp_seqno_update(newlsp);
1234 newlsp->last_generated = time(NULL);
1235 lsp_flood(newlsp, NULL);
1236
1237 refresh_time = lsp_refresh_time(newlsp, rem_lifetime);
1238
1239 THREAD_TIMER_OFF(area->t_lsp_refresh[level - 1]);
1240 area->lsp_regenerate_pending[level - 1] = 0;
1241 thread_add_timer(master, lsp_refresh,
1242 &area->lsp_refresh_arg[level - 1], refresh_time,
1243 &area->t_lsp_refresh[level - 1]);
1244
1245 if (isis->debugs & DEBUG_UPDATE_PACKETS) {
1246 zlog_debug("ISIS-Upd (%s): Building L%d LSP %s, len %" PRIu16
1247 ", seq 0x%08" PRIx32 ", cksum 0x%04" PRIx16
1248 ", lifetime %" PRIu16 "s refresh %" PRIu16 "s",
1249 area->area_tag, level,
1250 rawlspid_print(newlsp->hdr.lsp_id),
1251 newlsp->hdr.pdu_len, newlsp->hdr.seqno,
1252 newlsp->hdr.checksum, newlsp->hdr.rem_lifetime,
1253 refresh_time);
1254 }
1255 sched_debug(
1256 "ISIS (%s): Built L%d LSP. Set triggered regenerate to non-pending.",
1257 area->area_tag, level);
1258
1259 return ISIS_OK;
1260 }
1261
1262 /*
1263 * Search own LSPs, update holding time and flood
1264 */
1265 static int lsp_regenerate(struct isis_area *area, int level)
1266 {
1267 dict_t *lspdb;
1268 struct isis_lsp *lsp, *frag;
1269 struct listnode *node;
1270 uint8_t lspid[ISIS_SYS_ID_LEN + 2];
1271 uint16_t rem_lifetime, refresh_time;
1272
1273 if ((area == NULL) || (area->is_type & level) != level)
1274 return ISIS_ERROR;
1275
1276 lspdb = area->lspdb[level - 1];
1277
1278 memset(lspid, 0, ISIS_SYS_ID_LEN + 2);
1279 memcpy(lspid, isis->sysid, ISIS_SYS_ID_LEN);
1280
1281 lsp = lsp_search(lspid, lspdb);
1282
1283 if (!lsp) {
1284 flog_err(EC_LIB_DEVELOPMENT,
1285 "ISIS-Upd (%s): lsp_regenerate: no L%d LSP found!",
1286 area->area_tag, level);
1287 return ISIS_ERROR;
1288 }
1289
1290 lsp_clear_data(lsp);
1291 lsp_build(lsp, area);
1292 rem_lifetime = lsp_rem_lifetime(area, level);
1293 lsp->hdr.rem_lifetime = rem_lifetime;
1294 lsp->last_generated = time(NULL);
1295 lsp_flood(lsp, NULL);
1296 for (ALL_LIST_ELEMENTS_RO(lsp->lspu.frags, node, frag)) {
1297 frag->hdr.lsp_bits = lsp_bits_generate(
1298 level, area->overload_bit, area->attached_bit);
1299 /* Set the lifetime values of all the fragments to the same
1300 * value,
1301 * so that no fragment expires before the lsp is refreshed.
1302 */
1303 frag->hdr.rem_lifetime = rem_lifetime;
1304 frag->age_out = ZERO_AGE_LIFETIME;
1305 lsp_flood(frag, NULL);
1306 }
1307 lsp_seqno_update(lsp);
1308
1309 refresh_time = lsp_refresh_time(lsp, rem_lifetime);
1310 thread_add_timer(master, lsp_refresh,
1311 &area->lsp_refresh_arg[level - 1], refresh_time,
1312 &area->t_lsp_refresh[level - 1]);
1313 area->lsp_regenerate_pending[level - 1] = 0;
1314
1315 if (isis->debugs & DEBUG_UPDATE_PACKETS) {
1316 zlog_debug(
1317 "ISIS-Upd (%s): Refreshed our L%d LSP %s, len %" PRIu16
1318 ", seq 0x%08" PRIx32 ", cksum 0x%04" PRIx16
1319 ", lifetime %" PRIu16 "s refresh %" PRIu16 "s",
1320 area->area_tag, level, rawlspid_print(lsp->hdr.lsp_id),
1321 lsp->hdr.pdu_len, lsp->hdr.seqno, lsp->hdr.checksum,
1322 lsp->hdr.rem_lifetime, refresh_time);
1323 }
1324 sched_debug(
1325 "ISIS (%s): Rebuilt L%d LSP. Set triggered regenerate to non-pending.",
1326 area->area_tag, level);
1327
1328 return ISIS_OK;
1329 }
1330
1331 /*
1332 * Something has changed or periodic refresh -> regenerate LSP
1333 */
1334 static int lsp_refresh(struct thread *thread)
1335 {
1336 struct lsp_refresh_arg *arg = THREAD_ARG(thread);
1337
1338 assert(arg);
1339
1340 struct isis_area *area = arg->area;
1341
1342 assert(area);
1343
1344 int level = arg->level;
1345
1346 area->t_lsp_refresh[level - 1] = NULL;
1347 area->lsp_regenerate_pending[level - 1] = 0;
1348
1349 if ((area->is_type & level) == 0)
1350 return ISIS_ERROR;
1351
1352 if (monotime_since(&area->last_lsp_refresh_event[level - 1], NULL) < 50000L) {
1353 sched_debug("ISIS (%s): Still unstable, postpone LSP L%d refresh",
1354 area->area_tag, level);
1355 _lsp_regenerate_schedule(area, level, 0, false,
1356 __func__, __FILE__, __LINE__);
1357 return 0;
1358 }
1359
1360 sched_debug(
1361 "ISIS (%s): LSP L%d refresh timer expired. Refreshing LSP...",
1362 area->area_tag, level);
1363 return lsp_regenerate(area, level);
1364 }
1365
1366 int _lsp_regenerate_schedule(struct isis_area *area, int level,
1367 int all_pseudo, bool postpone,
1368 const char *func, const char *file,
1369 int line)
1370 {
1371 struct isis_lsp *lsp;
1372 uint8_t id[ISIS_SYS_ID_LEN + 2];
1373 time_t now, diff;
1374 long timeout;
1375 struct listnode *cnode;
1376 struct isis_circuit *circuit;
1377 int lvl;
1378
1379 if (area == NULL)
1380 return ISIS_ERROR;
1381
1382 sched_debug(
1383 "ISIS (%s): Scheduling regeneration of %s LSPs, %sincluding PSNs"
1384 " Caller: %s %s:%d",
1385 area->area_tag, circuit_t2string(level),
1386 all_pseudo ? "" : "not ",
1387 func, file, line);
1388
1389 memcpy(id, isis->sysid, ISIS_SYS_ID_LEN);
1390 LSP_PSEUDO_ID(id) = LSP_FRAGMENT(id) = 0;
1391 now = time(NULL);
1392
1393 for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; lvl++) {
1394 if (!((level & lvl) && (area->is_type & lvl)))
1395 continue;
1396
1397 if (postpone) {
1398 monotime(&area->last_lsp_refresh_event[lvl - 1]);
1399 }
1400
1401 sched_debug(
1402 "ISIS (%s): Checking whether L%d needs to be scheduled",
1403 area->area_tag, lvl);
1404
1405 if (area->lsp_regenerate_pending[lvl - 1]) {
1406 struct timeval remain = thread_timer_remain(
1407 area->t_lsp_refresh[lvl - 1]);
1408 sched_debug(
1409 "ISIS (%s): Regeneration is already pending, nothing todo."
1410 " (Due in %lld.%03lld seconds)",
1411 area->area_tag, (long long)remain.tv_sec,
1412 (long long)remain.tv_usec / 1000);
1413 continue;
1414 }
1415
1416 lsp = lsp_search(id, area->lspdb[lvl - 1]);
1417 if (!lsp) {
1418 sched_debug(
1419 "ISIS (%s): We do not have any LSPs to regenerate, nothing todo.",
1420 area->area_tag);
1421 continue;
1422 }
1423
1424 /*
1425 * Throttle avoidance
1426 */
1427 sched_debug(
1428 "ISIS (%s): Will schedule regen timer. Last run was: %lld, Now is: %lld",
1429 area->area_tag, (long long)lsp->last_generated,
1430 (long long)now);
1431 THREAD_TIMER_OFF(area->t_lsp_refresh[lvl - 1]);
1432 diff = now - lsp->last_generated;
1433 if (diff < area->lsp_gen_interval[lvl - 1]) {
1434 timeout =
1435 1000 * (area->lsp_gen_interval[lvl - 1] - diff);
1436 sched_debug(
1437 "ISIS (%s): Scheduling in %ld ms to match configured lsp_gen_interval",
1438 area->area_tag, timeout);
1439 } else {
1440 /*
1441 * lsps are not regenerated if lsp_regenerate function
1442 * is called
1443 * directly. However if the lsp_regenerate call is
1444 * queued for
1445 * later execution it works.
1446 */
1447 timeout = 100;
1448 sched_debug(
1449 "ISIS (%s): Last generation was more than lsp_gen_interval ago."
1450 " Scheduling for execution in %ld ms.",
1451 area->area_tag, timeout);
1452 }
1453
1454 area->lsp_regenerate_pending[lvl - 1] = 1;
1455 thread_add_timer_msec(master, lsp_refresh,
1456 &area->lsp_refresh_arg[lvl - 1],
1457 timeout,
1458 &area->t_lsp_refresh[lvl - 1]);
1459 }
1460
1461 if (all_pseudo) {
1462 for (ALL_LIST_ELEMENTS_RO(area->circuit_list, cnode, circuit))
1463 lsp_regenerate_schedule_pseudo(circuit, level);
1464 }
1465
1466 return ISIS_OK;
1467 }
1468
1469 /*
1470 * Funcs for pseudonode LSPs
1471 */
1472
1473 /*
1474 * 7.3.8 and 7.3.10 Generation of level 1 and 2 pseudonode LSPs
1475 */
1476 static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit,
1477 int level)
1478 {
1479 struct isis_adjacency *adj;
1480 struct list *adj_list;
1481 struct listnode *node;
1482 struct isis_area *area = circuit->area;
1483
1484 lsp_clear_data(lsp);
1485 lsp->tlvs = isis_alloc_tlvs();
1486 lsp_debug(
1487 "ISIS (%s): Constructing pseudo LSP %s for interface %s level %d",
1488 area->area_tag, rawlspid_print(lsp->hdr.lsp_id),
1489 circuit->interface->name, level);
1490
1491 lsp->level = level;
1492 /* RFC3787 section 4 SHOULD not set overload bit in pseudo LSPs */
1493 lsp->hdr.lsp_bits =
1494 lsp_bits_generate(level, 0, circuit->area->attached_bit);
1495
1496 /*
1497 * add self to IS neighbours
1498 */
1499 uint8_t ne_id[ISIS_SYS_ID_LEN + 1];
1500
1501 memcpy(ne_id, isis->sysid, ISIS_SYS_ID_LEN);
1502 LSP_PSEUDO_ID(ne_id) = 0;
1503
1504 if (circuit->area->oldmetric) {
1505 isis_tlvs_add_oldstyle_reach(lsp->tlvs, ne_id, 0);
1506 lsp_debug(
1507 "ISIS (%s): Adding %s.%02x as old-style neighbor (self)",
1508 area->area_tag, sysid_print(ne_id),
1509 LSP_PSEUDO_ID(ne_id));
1510 }
1511 if (circuit->area->newmetric) {
1512 isis_tlvs_add_extended_reach(lsp->tlvs, ISIS_MT_IPV4_UNICAST,
1513 ne_id, 0, NULL, 0);
1514 lsp_debug(
1515 "ISIS (%s): Adding %s.%02x as te-style neighbor (self)",
1516 area->area_tag, sysid_print(ne_id),
1517 LSP_PSEUDO_ID(ne_id));
1518 }
1519
1520 adj_list = list_new();
1521 isis_adj_build_up_list(circuit->u.bc.adjdb[level - 1], adj_list);
1522
1523 for (ALL_LIST_ELEMENTS_RO(adj_list, node, adj)) {
1524 if (!(adj->level & level)) {
1525 lsp_debug(
1526 "ISIS (%s): Ignoring neighbor %s, level does not intersect",
1527 area->area_tag, sysid_print(adj->sysid));
1528 continue;
1529 }
1530
1531 if (!(level == IS_LEVEL_1
1532 && adj->sys_type == ISIS_SYSTYPE_L1_IS)
1533 && !(level == IS_LEVEL_1
1534 && adj->sys_type == ISIS_SYSTYPE_L2_IS
1535 && adj->adj_usage == ISIS_ADJ_LEVEL1AND2)
1536 && !(level == IS_LEVEL_2
1537 && adj->sys_type == ISIS_SYSTYPE_L2_IS)) {
1538 lsp_debug(
1539 "ISIS (%s): Ignoring neighbor %s, level does not match",
1540 area->area_tag, sysid_print(adj->sysid));
1541 continue;
1542 }
1543
1544 memcpy(ne_id, adj->sysid, ISIS_SYS_ID_LEN);
1545 if (circuit->area->oldmetric) {
1546 isis_tlvs_add_oldstyle_reach(lsp->tlvs, ne_id, 0);
1547 lsp_debug(
1548 "ISIS (%s): Adding %s.%02x as old-style neighbor (peer)",
1549 area->area_tag, sysid_print(ne_id),
1550 LSP_PSEUDO_ID(ne_id));
1551 }
1552 if (circuit->area->newmetric) {
1553 isis_tlvs_add_extended_reach(lsp->tlvs,
1554 ISIS_MT_IPV4_UNICAST,
1555 ne_id, 0, NULL, 0);
1556 lsp_debug(
1557 "ISIS (%s): Adding %s.%02x as te-style neighbor (peer)",
1558 area->area_tag, sysid_print(ne_id),
1559 LSP_PSEUDO_ID(ne_id));
1560 }
1561 }
1562 list_delete(&adj_list);
1563 return;
1564 }
1565
1566 int lsp_generate_pseudo(struct isis_circuit *circuit, int level)
1567 {
1568 dict_t *lspdb = circuit->area->lspdb[level - 1];
1569 struct isis_lsp *lsp;
1570 uint8_t lsp_id[ISIS_SYS_ID_LEN + 2];
1571 uint16_t rem_lifetime, refresh_time;
1572
1573 if ((circuit->is_type & level) != level
1574 || (circuit->state != C_STATE_UP)
1575 || (circuit->circ_type != CIRCUIT_T_BROADCAST)
1576 || (circuit->u.bc.is_dr[level - 1] == 0))
1577 return ISIS_ERROR;
1578
1579 memcpy(lsp_id, isis->sysid, ISIS_SYS_ID_LEN);
1580 LSP_FRAGMENT(lsp_id) = 0;
1581 LSP_PSEUDO_ID(lsp_id) = circuit->circuit_id;
1582
1583 /*
1584 * If for some reason have a pseudo LSP in the db already -> regenerate
1585 */
1586 if (lsp_search(lsp_id, lspdb))
1587 return lsp_regenerate_schedule_pseudo(circuit, level);
1588
1589 rem_lifetime = lsp_rem_lifetime(circuit->area, level);
1590 /* RFC3787 section 4 SHOULD not set overload bit in pseudo LSPs */
1591 lsp = lsp_new(circuit->area, lsp_id, rem_lifetime, 1,
1592 circuit->area->is_type | circuit->area->attached_bit, 0,
1593 NULL, level);
1594 lsp->area = circuit->area;
1595
1596 lsp_build_pseudo(lsp, circuit, level);
1597 lsp_pack_pdu(lsp);
1598 lsp->own_lsp = 1;
1599 lsp_insert(lsp, lspdb);
1600 lsp_flood(lsp, NULL);
1601
1602 refresh_time = lsp_refresh_time(lsp, rem_lifetime);
1603 THREAD_TIMER_OFF(circuit->u.bc.t_refresh_pseudo_lsp[level - 1]);
1604 circuit->lsp_regenerate_pending[level - 1] = 0;
1605 if (level == IS_LEVEL_1)
1606 thread_add_timer(
1607 master, lsp_l1_refresh_pseudo, circuit, refresh_time,
1608 &circuit->u.bc.t_refresh_pseudo_lsp[level - 1]);
1609 else if (level == IS_LEVEL_2)
1610 thread_add_timer(
1611 master, lsp_l2_refresh_pseudo, circuit, refresh_time,
1612 &circuit->u.bc.t_refresh_pseudo_lsp[level - 1]);
1613
1614 if (isis->debugs & DEBUG_UPDATE_PACKETS) {
1615 zlog_debug(
1616 "ISIS-Upd (%s): Built L%d Pseudo LSP %s, len %" PRIu16
1617 ", seq 0x%08" PRIx32 ", cksum 0x%04" PRIx16
1618 ", lifetime %" PRIu16 "s, refresh %" PRIu16 "s",
1619 circuit->area->area_tag, level,
1620 rawlspid_print(lsp->hdr.lsp_id), lsp->hdr.pdu_len,
1621 lsp->hdr.seqno, lsp->hdr.checksum,
1622 lsp->hdr.rem_lifetime, refresh_time);
1623 }
1624
1625 return ISIS_OK;
1626 }
1627
1628 static int lsp_regenerate_pseudo(struct isis_circuit *circuit, int level)
1629 {
1630 dict_t *lspdb = circuit->area->lspdb[level - 1];
1631 struct isis_lsp *lsp;
1632 uint8_t lsp_id[ISIS_SYS_ID_LEN + 2];
1633 uint16_t rem_lifetime, refresh_time;
1634
1635 if ((circuit->is_type & level) != level
1636 || (circuit->state != C_STATE_UP)
1637 || (circuit->circ_type != CIRCUIT_T_BROADCAST)
1638 || (circuit->u.bc.is_dr[level - 1] == 0))
1639 return ISIS_ERROR;
1640
1641 memcpy(lsp_id, isis->sysid, ISIS_SYS_ID_LEN);
1642 LSP_PSEUDO_ID(lsp_id) = circuit->circuit_id;
1643 LSP_FRAGMENT(lsp_id) = 0;
1644
1645 lsp = lsp_search(lsp_id, lspdb);
1646
1647 if (!lsp) {
1648 flog_err(EC_LIB_DEVELOPMENT,
1649 "lsp_regenerate_pseudo: no l%d LSP %s found!", level,
1650 rawlspid_print(lsp_id));
1651 return ISIS_ERROR;
1652 }
1653
1654 rem_lifetime = lsp_rem_lifetime(circuit->area, level);
1655 lsp->hdr.rem_lifetime = rem_lifetime;
1656 lsp_build_pseudo(lsp, circuit, level);
1657 lsp_inc_seqno(lsp, 0);
1658 lsp->last_generated = time(NULL);
1659 lsp_flood(lsp, NULL);
1660
1661 refresh_time = lsp_refresh_time(lsp, rem_lifetime);
1662 if (level == IS_LEVEL_1)
1663 thread_add_timer(
1664 master, lsp_l1_refresh_pseudo, circuit, refresh_time,
1665 &circuit->u.bc.t_refresh_pseudo_lsp[level - 1]);
1666 else if (level == IS_LEVEL_2)
1667 thread_add_timer(
1668 master, lsp_l2_refresh_pseudo, circuit, refresh_time,
1669 &circuit->u.bc.t_refresh_pseudo_lsp[level - 1]);
1670
1671 if (isis->debugs & DEBUG_UPDATE_PACKETS) {
1672 zlog_debug(
1673 "ISIS-Upd (%s): Refreshed L%d Pseudo LSP %s, len %" PRIu16
1674 ", seq 0x%08" PRIx32 ", cksum 0x%04" PRIx16
1675 ", lifetime %" PRIu16 "s, refresh %" PRIu16 "s",
1676 circuit->area->area_tag, level,
1677 rawlspid_print(lsp->hdr.lsp_id), lsp->hdr.pdu_len,
1678 lsp->hdr.seqno, lsp->hdr.checksum,
1679 lsp->hdr.rem_lifetime, refresh_time);
1680 }
1681
1682 return ISIS_OK;
1683 }
1684
1685 /*
1686 * Something has changed or periodic refresh -> regenerate pseudo LSP
1687 */
1688 static int lsp_l1_refresh_pseudo(struct thread *thread)
1689 {
1690 struct isis_circuit *circuit;
1691 uint8_t id[ISIS_SYS_ID_LEN + 2];
1692
1693 circuit = THREAD_ARG(thread);
1694
1695 circuit->u.bc.t_refresh_pseudo_lsp[0] = NULL;
1696 circuit->lsp_regenerate_pending[0] = 0;
1697
1698 if ((circuit->u.bc.is_dr[0] == 0)
1699 || (circuit->is_type & IS_LEVEL_1) == 0) {
1700 memcpy(id, isis->sysid, ISIS_SYS_ID_LEN);
1701 LSP_PSEUDO_ID(id) = circuit->circuit_id;
1702 LSP_FRAGMENT(id) = 0;
1703 lsp_purge_pseudo(id, circuit, IS_LEVEL_1);
1704 return ISIS_ERROR;
1705 }
1706
1707 return lsp_regenerate_pseudo(circuit, IS_LEVEL_1);
1708 }
1709
1710 static int lsp_l2_refresh_pseudo(struct thread *thread)
1711 {
1712 struct isis_circuit *circuit;
1713 uint8_t id[ISIS_SYS_ID_LEN + 2];
1714
1715 circuit = THREAD_ARG(thread);
1716
1717 circuit->u.bc.t_refresh_pseudo_lsp[1] = NULL;
1718 circuit->lsp_regenerate_pending[1] = 0;
1719
1720 if ((circuit->u.bc.is_dr[1] == 0)
1721 || (circuit->is_type & IS_LEVEL_2) == 0) {
1722 memcpy(id, isis->sysid, ISIS_SYS_ID_LEN);
1723 LSP_PSEUDO_ID(id) = circuit->circuit_id;
1724 LSP_FRAGMENT(id) = 0;
1725 lsp_purge_pseudo(id, circuit, IS_LEVEL_2);
1726 return ISIS_ERROR;
1727 }
1728
1729 return lsp_regenerate_pseudo(circuit, IS_LEVEL_2);
1730 }
1731
1732 int lsp_regenerate_schedule_pseudo(struct isis_circuit *circuit, int level)
1733 {
1734 struct isis_lsp *lsp;
1735 uint8_t lsp_id[ISIS_SYS_ID_LEN + 2];
1736 time_t now, diff;
1737 long timeout;
1738 int lvl;
1739 struct isis_area *area = circuit->area;
1740
1741 if (circuit->circ_type != CIRCUIT_T_BROADCAST
1742 || circuit->state != C_STATE_UP)
1743 return ISIS_OK;
1744
1745 sched_debug(
1746 "ISIS (%s): Scheduling regeneration of %s pseudo LSP for interface %s",
1747 area->area_tag, circuit_t2string(level),
1748 circuit->interface->name);
1749
1750 memcpy(lsp_id, isis->sysid, ISIS_SYS_ID_LEN);
1751 LSP_PSEUDO_ID(lsp_id) = circuit->circuit_id;
1752 LSP_FRAGMENT(lsp_id) = 0;
1753 now = time(NULL);
1754
1755 for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; lvl++) {
1756 sched_debug(
1757 "ISIS (%s): Checking whether L%d pseudo LSP needs to be scheduled",
1758 area->area_tag, lvl);
1759
1760 if (!((level & lvl) && (circuit->is_type & lvl))) {
1761 sched_debug("ISIS (%s): Level is not active on circuit",
1762 area->area_tag);
1763 continue;
1764 }
1765
1766 if (circuit->u.bc.is_dr[lvl - 1] == 0) {
1767 sched_debug(
1768 "ISIS (%s): This IS is not DR, nothing to do.",
1769 area->area_tag);
1770 continue;
1771 }
1772
1773 if (circuit->lsp_regenerate_pending[lvl - 1]) {
1774 struct timeval remain = thread_timer_remain(
1775 circuit->u.bc.t_refresh_pseudo_lsp[lvl - 1]);
1776 sched_debug(
1777 "ISIS (%s): Regenerate is already pending, nothing todo."
1778 " (Due in %lld.%03lld seconds)",
1779 area->area_tag, (long long)remain.tv_sec,
1780 (long long)remain.tv_usec / 1000);
1781 continue;
1782 }
1783
1784 lsp = lsp_search(lsp_id, circuit->area->lspdb[lvl - 1]);
1785 if (!lsp) {
1786 sched_debug(
1787 "ISIS (%s): Pseudonode LSP does not exist yet, nothing to regenerate.",
1788 area->area_tag);
1789 continue;
1790 }
1791
1792 /*
1793 * Throttle avoidance
1794 */
1795 sched_debug(
1796 "ISIS (%s): Will schedule PSN regen timer. Last run was: %lld, Now is: %lld",
1797 area->area_tag, (long long)lsp->last_generated,
1798 (long long)now);
1799 THREAD_TIMER_OFF(circuit->u.bc.t_refresh_pseudo_lsp[lvl - 1]);
1800 diff = now - lsp->last_generated;
1801 if (diff < circuit->area->lsp_gen_interval[lvl - 1]) {
1802 timeout =
1803 1000 * (circuit->area->lsp_gen_interval[lvl - 1]
1804 - diff);
1805 sched_debug(
1806 "ISIS (%s): Sechduling in %ld ms to match configured lsp_gen_interval",
1807 area->area_tag, timeout);
1808 } else {
1809 timeout = 100;
1810 sched_debug(
1811 "ISIS (%s): Last generation was more than lsp_gen_interval ago."
1812 " Scheduling for execution in %ld ms.",
1813 area->area_tag, timeout);
1814 }
1815
1816 circuit->lsp_regenerate_pending[lvl - 1] = 1;
1817
1818 if (lvl == IS_LEVEL_1) {
1819 thread_add_timer_msec(
1820 master, lsp_l1_refresh_pseudo, circuit, timeout,
1821 &circuit->u.bc.t_refresh_pseudo_lsp[lvl - 1]);
1822 } else if (lvl == IS_LEVEL_2) {
1823 thread_add_timer_msec(
1824 master, lsp_l2_refresh_pseudo, circuit, timeout,
1825 &circuit->u.bc.t_refresh_pseudo_lsp[lvl - 1]);
1826 }
1827 }
1828
1829 return ISIS_OK;
1830 }
1831
1832 /*
1833 * Walk through LSPs for an area
1834 * - set remaining lifetime
1835 */
1836 int lsp_tick(struct thread *thread)
1837 {
1838 struct isis_area *area;
1839 struct isis_lsp *lsp;
1840 dnode_t *dnode, *dnode_next;
1841 int level;
1842 uint16_t rem_lifetime;
1843 bool fabricd_sync_incomplete = false;
1844
1845 area = THREAD_ARG(thread);
1846 assert(area);
1847 area->t_tick = NULL;
1848 thread_add_timer(master, lsp_tick, area, 1, &area->t_tick);
1849
1850 struct isis_circuit *fabricd_init_c = fabricd_initial_sync_circuit(area);
1851
1852 /*
1853 * Remove LSPs which have aged out
1854 */
1855 for (level = 0; level < ISIS_LEVELS; level++) {
1856 if (area->lspdb[level] && dict_count(area->lspdb[level]) > 0) {
1857 for (dnode = dict_first(area->lspdb[level]);
1858 dnode != NULL; dnode = dnode_next) {
1859 dnode_next =
1860 dict_next(area->lspdb[level], dnode);
1861 lsp = dnode_get(dnode);
1862
1863 /*
1864 * The lsp rem_lifetime is kept at 0 for MaxAge
1865 * or
1866 * ZeroAgeLifetime depending on explicit purge
1867 * or
1868 * natural age out. So schedule spf only once
1869 * when
1870 * the first time rem_lifetime becomes 0.
1871 */
1872 rem_lifetime = lsp->hdr.rem_lifetime;
1873 lsp_set_time(lsp);
1874
1875 /*
1876 * Schedule may run spf which should be done
1877 * only after
1878 * the lsp rem_lifetime becomes 0 for the first
1879 * time.
1880 * ISO 10589 - 7.3.16.4 first paragraph.
1881 */
1882 if (rem_lifetime == 1 && lsp->hdr.seqno != 0) {
1883 /* 7.3.16.4 a) set SRM flags on all */
1884 /* 7.3.16.4 b) retain only the header */
1885 if (lsp->area->purge_originator)
1886 lsp_purge(lsp, lsp->level, NULL);
1887 else
1888 lsp_flood(lsp, NULL);
1889 /* 7.3.16.4 c) record the time to purge
1890 * FIXME */
1891 isis_spf_schedule(lsp->area, lsp->level);
1892 }
1893
1894 if (lsp->age_out == 0) {
1895 zlog_debug(
1896 "ISIS-Upd (%s): L%u LSP %s seq "
1897 "0x%08" PRIx32 " aged out",
1898 area->area_tag, lsp->level,
1899 rawlspid_print(lsp->hdr.lsp_id),
1900 lsp->hdr.seqno);
1901 lsp_destroy(lsp);
1902 lsp = NULL;
1903 dict_delete_free(area->lspdb[level],
1904 dnode);
1905 }
1906
1907 if (fabricd_init_c && lsp) {
1908 fabricd_sync_incomplete |=
1909 ISIS_CHECK_FLAG(lsp->SSNflags,
1910 fabricd_init_c);
1911 }
1912 }
1913 }
1914 }
1915
1916 if (fabricd_init_c
1917 && !fabricd_sync_incomplete
1918 && !isis_tx_queue_len(fabricd_init_c->tx_queue)) {
1919 fabricd_initial_sync_finish(area);
1920 }
1921
1922 return ISIS_OK;
1923 }
1924
1925 void lsp_purge_pseudo(uint8_t *id, struct isis_circuit *circuit, int level)
1926 {
1927 struct isis_lsp *lsp;
1928
1929 lsp = lsp_search(id, circuit->area->lspdb[level - 1]);
1930 if (!lsp)
1931 return;
1932
1933 lsp_purge(lsp, level, NULL);
1934 }
1935
1936 /*
1937 * Purge own LSP that is received and we don't have.
1938 * -> Do as in 7.3.16.4
1939 */
1940 void lsp_purge_non_exist(int level, struct isis_lsp_hdr *hdr,
1941 struct isis_area *area)
1942 {
1943 struct isis_lsp *lsp;
1944
1945 /*
1946 * We need to create the LSP to be purged
1947 */
1948 lsp = XCALLOC(MTYPE_ISIS_LSP, sizeof(struct isis_lsp));
1949 lsp->area = area;
1950 lsp->level = level;
1951 lsp_adjust_stream(lsp);
1952 lsp->age_out = ZERO_AGE_LIFETIME;
1953
1954 memcpy(&lsp->hdr, hdr, sizeof(lsp->hdr));
1955 lsp->hdr.rem_lifetime = 0;
1956
1957 lsp_purge_add_poi(lsp, NULL);
1958
1959 lsp_pack_pdu(lsp);
1960
1961 lsp_insert(lsp, area->lspdb[lsp->level - 1]);
1962 lsp_flood(lsp, NULL);
1963
1964 return;
1965 }
1966
1967 void lsp_set_all_srmflags(struct isis_lsp *lsp, bool set)
1968 {
1969 struct listnode *node;
1970 struct isis_circuit *circuit;
1971
1972 assert(lsp);
1973
1974 if (!lsp->area)
1975 return;
1976
1977 struct list *circuit_list = lsp->area->circuit_list;
1978 for (ALL_LIST_ELEMENTS_RO(circuit_list, node, circuit)) {
1979 if (set) {
1980 isis_tx_queue_add(circuit->tx_queue, lsp,
1981 TX_LSP_NORMAL);
1982 } else {
1983 isis_tx_queue_del(circuit->tx_queue, lsp);
1984 }
1985 }
1986 }
1987
1988 void lsp_flood(struct isis_lsp *lsp, struct isis_circuit *circuit)
1989 {
1990 if (!fabricd) {
1991 lsp_set_all_srmflags(lsp, true);
1992 if (circuit)
1993 isis_tx_queue_del(circuit->tx_queue, lsp);
1994 } else {
1995 fabricd_lsp_flood(lsp);
1996 }
1997 }
1998
1999 static int lsp_handle_adj_state_change(struct isis_adjacency *adj)
2000 {
2001 lsp_regenerate_schedule(adj->circuit->area, IS_LEVEL_1 | IS_LEVEL_2, 0);
2002 return 0;
2003 }
2004
2005 void lsp_init(void)
2006 {
2007 hook_register(isis_adj_state_change_hook,
2008 lsp_handle_adj_state_change);
2009 }