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