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