]>
Commit | Line | Data |
---|---|---|
eb5d44eb | 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 | |
f3ccedaa | 8 | * Copyright (C) 2013-2015 Christian Franke <chris@opensourcerouting.org> |
eb5d44eb | 9 | * |
10 | * This program is free software; you can redistribute it and/or modify it | |
f3ccedaa | 11 | * under the terms of the GNU General Public License as published by the Free |
eb5d44eb | 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. | |
896014f4 DL |
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 | |
eb5d44eb | 23 | */ |
15935e9a | 24 | |
eb5d44eb | 25 | #include <zebra.h> |
eb5d44eb | 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" | |
6a270cd9 | 37 | #include "checksum.h" |
3f045a08 | 38 | #include "md5.h" |
f3ccedaa | 39 | #include "table.h" |
eb5d44eb | 40 | |
41 | #include "isisd/dict.h" | |
42 | #include "isisd/isis_constants.h" | |
43 | #include "isisd/isis_common.h" | |
3f045a08 | 44 | #include "isisd/isis_flags.h" |
eb5d44eb | 45 | #include "isisd/isis_circuit.h" |
46 | #include "isisd/isisd.h" | |
47 | #include "isisd/isis_tlv.h" | |
48 | #include "isisd/isis_lsp.h" | |
49 | #include "isisd/isis_pdu.h" | |
50 | #include "isisd/isis_dynhn.h" | |
51 | #include "isisd/isis_misc.h" | |
eb5d44eb | 52 | #include "isisd/isis_csm.h" |
53 | #include "isisd/isis_adjacency.h" | |
54 | #include "isisd/isis_spf.h" | |
f8c06e2c | 55 | #include "isisd/isis_te.h" |
99894f9a | 56 | #include "isisd/isis_mt.h" |
eb5d44eb | 57 | |
73d1aead | 58 | /* staticly assigned vars for printing purposes */ |
59 | char lsp_bits_string[200]; /* FIXME: enough ? */ | |
60 | ||
3f045a08 JB |
61 | static int lsp_l1_refresh (struct thread *thread); |
62 | static int lsp_l2_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 | ||
f390d2c7 | 66 | int |
67 | lsp_id_cmp (u_char * id1, u_char * id2) | |
68 | { | |
eb5d44eb | 69 | return memcmp (id1, id2, ISIS_SYS_ID_LEN + 2); |
70 | } | |
71 | ||
72 | dict_t * | |
f390d2c7 | 73 | lsp_db_init (void) |
eb5d44eb | 74 | { |
75 | dict_t *dict; | |
f390d2c7 | 76 | |
77 | dict = dict_create (DICTCOUNT_T_MAX, (dict_comp_t) lsp_id_cmp); | |
78 | ||
eb5d44eb | 79 | return dict; |
80 | } | |
81 | ||
82 | struct isis_lsp * | |
f390d2c7 | 83 | lsp_search (u_char * id, dict_t * lspdb) |
eb5d44eb | 84 | { |
85 | dnode_t *node; | |
86 | ||
f390d2c7 | 87 | #ifdef EXTREME_DEBUG |
eb5d44eb | 88 | dnode_t *dn; |
89 | ||
529d65b3 | 90 | zlog_debug ("searching db"); |
f390d2c7 | 91 | for (dn = dict_first (lspdb); dn; dn = dict_next (lspdb, dn)) |
92 | { | |
3f045a08 | 93 | zlog_debug ("%s\t%pX", rawlspid_print ((u_char *) dnode_getkey (dn)), |
529d65b3 | 94 | dnode_get (dn)); |
f390d2c7 | 95 | } |
eb5d44eb | 96 | #endif /* EXTREME DEBUG */ |
97 | ||
98 | node = dict_lookup (lspdb, id); | |
f390d2c7 | 99 | |
eb5d44eb | 100 | if (node) |
f390d2c7 | 101 | return (struct isis_lsp *) dnode_get (node); |
eb5d44eb | 102 | |
103 | return NULL; | |
104 | } | |
105 | ||
92365889 | 106 | static void |
eb5d44eb | 107 | lsp_clear_data (struct isis_lsp *lsp) |
108 | { | |
109 | if (!lsp) | |
110 | return; | |
f390d2c7 | 111 | |
3f045a08 JB |
112 | if (lsp->tlv_data.hostname) |
113 | isis_dynhn_remove (lsp->lsp_header->lsp_id); | |
114 | ||
f390d2c7 | 115 | if (lsp->own_lsp) |
116 | { | |
117 | if (lsp->tlv_data.nlpids) | |
3f045a08 | 118 | XFREE (MTYPE_ISIS_TLV, lsp->tlv_data.nlpids); |
f390d2c7 | 119 | if (lsp->tlv_data.hostname) |
3f045a08 JB |
120 | XFREE (MTYPE_ISIS_TLV, lsp->tlv_data.hostname); |
121 | if (lsp->tlv_data.router_id) | |
122 | XFREE (MTYPE_ISIS_TLV, lsp->tlv_data.router_id); | |
f390d2c7 | 123 | } |
eb5d44eb | 124 | |
3f045a08 | 125 | free_tlvs (&lsp->tlv_data); |
eb5d44eb | 126 | } |
127 | ||
92365889 | 128 | static void |
eb5d44eb | 129 | lsp_destroy (struct isis_lsp *lsp) |
130 | { | |
3f045a08 JB |
131 | struct listnode *cnode, *lnode, *lnnode; |
132 | struct isis_lsp *lsp_in_list; | |
133 | struct isis_circuit *circuit; | |
134 | ||
eb5d44eb | 135 | if (!lsp) |
136 | return; | |
f390d2c7 | 137 | |
cd4ab724 | 138 | if (lsp->area->circuit_list) { |
139 | for (ALL_LIST_ELEMENTS_RO (lsp->area->circuit_list, cnode, circuit)) | |
140 | { | |
141 | if (circuit->lsp_queue == NULL) | |
142 | continue; | |
143 | for (ALL_LIST_ELEMENTS (circuit->lsp_queue, lnode, lnnode, lsp_in_list)) | |
144 | if (lsp_in_list == lsp) | |
145 | list_delete_node(circuit->lsp_queue, lnode); | |
146 | } | |
147 | } | |
3f045a08 JB |
148 | ISIS_FLAGS_CLEAR_ALL (lsp->SSNflags); |
149 | ISIS_FLAGS_CLEAR_ALL (lsp->SRMflags); | |
150 | ||
eb5d44eb | 151 | lsp_clear_data (lsp); |
f390d2c7 | 152 | |
153 | if (LSP_FRAGMENT (lsp->lsp_header->lsp_id) == 0 && lsp->lspu.frags) | |
154 | { | |
eb5d44eb | 155 | list_delete (lsp->lspu.frags); |
3f045a08 | 156 | lsp->lspu.frags = NULL; |
f390d2c7 | 157 | } |
158 | ||
3f045a08 | 159 | isis_spf_schedule (lsp->area, lsp->level); |
3f045a08 | 160 | |
eb5d44eb | 161 | if (lsp->pdu) |
162 | stream_free (lsp->pdu); | |
163 | XFREE (MTYPE_ISIS_LSP, lsp); | |
164 | } | |
165 | ||
f390d2c7 | 166 | void |
167 | lsp_db_destroy (dict_t * lspdb) | |
eb5d44eb | 168 | { |
169 | dnode_t *dnode, *next; | |
170 | struct isis_lsp *lsp; | |
f390d2c7 | 171 | |
eb5d44eb | 172 | dnode = dict_first (lspdb); |
f390d2c7 | 173 | while (dnode) |
174 | { | |
175 | next = dict_next (lspdb, dnode); | |
176 | lsp = dnode_get (dnode); | |
177 | lsp_destroy (lsp); | |
178 | dict_delete_free (lspdb, dnode); | |
179 | dnode = next; | |
180 | } | |
181 | ||
eb5d44eb | 182 | dict_free (lspdb); |
f390d2c7 | 183 | |
eb5d44eb | 184 | return; |
185 | } | |
186 | ||
187 | /* | |
188 | * Remove all the frags belonging to the given lsp | |
189 | */ | |
92365889 | 190 | static void |
f390d2c7 | 191 | lsp_remove_frags (struct list *frags, dict_t * lspdb) |
eb5d44eb | 192 | { |
193 | dnode_t *dnode; | |
1eb8ef25 | 194 | struct listnode *lnode, *lnnode; |
eb5d44eb | 195 | struct isis_lsp *lsp; |
196 | ||
1eb8ef25 | 197 | for (ALL_LIST_ELEMENTS (frags, lnode, lnnode, lsp)) |
f390d2c7 | 198 | { |
f390d2c7 | 199 | dnode = dict_lookup (lspdb, lsp->lsp_header->lsp_id); |
200 | lsp_destroy (lsp); | |
201 | dnode_destroy (dict_delete (lspdb, dnode)); | |
202 | } | |
203 | ||
eb5d44eb | 204 | list_delete_all_node (frags); |
f390d2c7 | 205 | |
eb5d44eb | 206 | return; |
207 | } | |
208 | ||
209 | void | |
f390d2c7 | 210 | lsp_search_and_destroy (u_char * id, dict_t * lspdb) |
eb5d44eb | 211 | { |
212 | dnode_t *node; | |
213 | struct isis_lsp *lsp; | |
214 | ||
215 | node = dict_lookup (lspdb, id); | |
f390d2c7 | 216 | if (node) |
217 | { | |
218 | node = dict_delete (lspdb, node); | |
219 | lsp = dnode_get (node); | |
220 | /* | |
221 | * If this is a zero lsp, remove all the frags now | |
222 | */ | |
223 | if (LSP_FRAGMENT (lsp->lsp_header->lsp_id) == 0) | |
224 | { | |
225 | if (lsp->lspu.frags) | |
226 | lsp_remove_frags (lsp->lspu.frags, lspdb); | |
227 | } | |
228 | else | |
229 | { | |
230 | /* | |
231 | * else just remove this frag, from the zero lsps' frag list | |
232 | */ | |
233 | if (lsp->lspu.zero_lsp && lsp->lspu.zero_lsp->lspu.frags) | |
234 | listnode_delete (lsp->lspu.zero_lsp->lspu.frags, lsp); | |
235 | } | |
236 | lsp_destroy (lsp); | |
237 | dnode_destroy (node); | |
238 | } | |
eb5d44eb | 239 | } |
240 | ||
241 | /* | |
242 | * Compares a LSP to given values | |
243 | * Params are given in net order | |
244 | */ | |
f390d2c7 | 245 | int |
246 | lsp_compare (char *areatag, struct isis_lsp *lsp, u_int32_t seq_num, | |
eb5d44eb | 247 | u_int16_t checksum, u_int16_t rem_lifetime) |
248 | { | |
f390d2c7 | 249 | /* no point in double ntohl on seqnum */ |
250 | if (lsp->lsp_header->seq_num == seq_num && | |
eb5d44eb | 251 | lsp->lsp_header->checksum == checksum && |
252 | /*comparing with 0, no need to do ntohl */ | |
253 | ((lsp->lsp_header->rem_lifetime == 0 && rem_lifetime == 0) || | |
f390d2c7 | 254 | (lsp->lsp_header->rem_lifetime != 0 && rem_lifetime != 0))) |
255 | { | |
256 | if (isis->debugs & DEBUG_SNP_PACKETS) | |
257 | { | |
3f045a08 | 258 | zlog_debug ("ISIS-Snp (%s): Compare LSP %s seq 0x%08x, cksum 0x%04x," |
529d65b3 | 259 | " lifetime %us", |
260 | areatag, | |
261 | rawlspid_print (lsp->lsp_header->lsp_id), | |
262 | ntohl (lsp->lsp_header->seq_num), | |
263 | ntohs (lsp->lsp_header->checksum), | |
264 | ntohs (lsp->lsp_header->rem_lifetime)); | |
265 | zlog_debug ("ISIS-Snp (%s): is equal to ours seq 0x%08x," | |
266 | " cksum 0x%04x, lifetime %us", | |
267 | areatag, | |
268 | ntohl (seq_num), ntohs (checksum), ntohs (rem_lifetime)); | |
f390d2c7 | 269 | } |
270 | return LSP_EQUAL; | |
271 | } | |
eb5d44eb | 272 | |
0250758d CF |
273 | /* |
274 | * LSPs with identical checksums should only be treated as newer if: | |
275 | * a) The current LSP has a remaining lifetime != 0 and the other LSP has a | |
276 | * remaining lifetime == 0. In this case, we should participate in the purge | |
277 | * and should not treat the current LSP with remaining lifetime == 0 as older. | |
278 | * b) The LSP has an incorrect checksum. In this case, we need to react as given | |
279 | * in 7.3.16.2. | |
280 | */ | |
281 | if (ntohl (seq_num) > ntohl (lsp->lsp_header->seq_num) | |
282 | || (ntohl(seq_num) == ntohl(lsp->lsp_header->seq_num) | |
283 | && ( (lsp->lsp_header->rem_lifetime != 0 | |
284 | && rem_lifetime == 0) | |
285 | || lsp->lsp_header->checksum != checksum))) | |
f390d2c7 | 286 | { |
287 | if (isis->debugs & DEBUG_SNP_PACKETS) | |
288 | { | |
3f045a08 | 289 | zlog_debug ("ISIS-Snp (%s): Compare LSP %s seq 0x%08x, cksum 0x%04x," |
529d65b3 | 290 | " lifetime %us", |
291 | areatag, | |
292 | rawlspid_print (lsp->lsp_header->lsp_id), | |
293 | ntohl (seq_num), ntohs (checksum), ntohs (rem_lifetime)); | |
294 | zlog_debug ("ISIS-Snp (%s): is newer than ours seq 0x%08x, " | |
295 | "cksum 0x%04x, lifetime %us", | |
296 | areatag, | |
297 | ntohl (lsp->lsp_header->seq_num), | |
298 | ntohs (lsp->lsp_header->checksum), | |
299 | ntohs (lsp->lsp_header->rem_lifetime)); | |
f390d2c7 | 300 | } |
301 | return LSP_NEWER; | |
302 | } | |
303 | if (isis->debugs & DEBUG_SNP_PACKETS) | |
304 | { | |
529d65b3 | 305 | zlog_debug |
3f045a08 | 306 | ("ISIS-Snp (%s): Compare LSP %s seq 0x%08x, cksum 0x%04x, lifetime %us", |
f390d2c7 | 307 | areatag, rawlspid_print (lsp->lsp_header->lsp_id), ntohl (seq_num), |
308 | ntohs (checksum), ntohs (rem_lifetime)); | |
529d65b3 | 309 | zlog_debug ("ISIS-Snp (%s): is older than ours seq 0x%08x," |
310 | " cksum 0x%04x, lifetime %us", areatag, | |
311 | ntohl (lsp->lsp_header->seq_num), | |
312 | ntohs (lsp->lsp_header->checksum), | |
313 | ntohs (lsp->lsp_header->rem_lifetime)); | |
f390d2c7 | 314 | } |
eb5d44eb | 315 | |
316 | return LSP_OLDER; | |
317 | } | |
318 | ||
3f045a08 JB |
319 | static void |
320 | lsp_auth_add (struct isis_lsp *lsp) | |
321 | { | |
322 | struct isis_passwd *passwd; | |
323 | unsigned char hmac_md5_hash[ISIS_AUTH_MD5_SIZE]; | |
324 | ||
325 | /* | |
326 | * Add the authentication info if its present | |
327 | */ | |
328 | (lsp->level == IS_LEVEL_1) ? (passwd = &lsp->area->area_passwd) : | |
329 | (passwd = &lsp->area->domain_passwd); | |
330 | switch (passwd->type) | |
331 | { | |
332 | /* Cleartext */ | |
333 | case ISIS_PASSWD_TYPE_CLEARTXT: | |
334 | memcpy (&lsp->tlv_data.auth_info, passwd, sizeof (struct isis_passwd)); | |
335 | tlv_add_authinfo (passwd->type, passwd->len, passwd->passwd, lsp->pdu); | |
336 | break; | |
337 | ||
338 | /* HMAC MD5 */ | |
339 | case ISIS_PASSWD_TYPE_HMAC_MD5: | |
340 | /* Remember where TLV is written so we can later | |
341 | * overwrite the MD5 hash */ | |
342 | lsp->auth_tlv_offset = stream_get_endp (lsp->pdu); | |
343 | memset(&hmac_md5_hash, 0, ISIS_AUTH_MD5_SIZE); | |
344 | lsp->tlv_data.auth_info.type = ISIS_PASSWD_TYPE_HMAC_MD5; | |
345 | lsp->tlv_data.auth_info.len = ISIS_AUTH_MD5_SIZE; | |
346 | memcpy (&lsp->tlv_data.auth_info.passwd, hmac_md5_hash, | |
347 | ISIS_AUTH_MD5_SIZE); | |
348 | tlv_add_authinfo (passwd->type, ISIS_AUTH_MD5_SIZE, hmac_md5_hash, | |
349 | lsp->pdu); | |
350 | break; | |
351 | ||
352 | default: | |
353 | break; | |
354 | } | |
355 | } | |
356 | ||
357 | static void | |
358 | lsp_auth_update (struct isis_lsp *lsp) | |
359 | { | |
360 | struct isis_passwd *passwd; | |
361 | unsigned char hmac_md5_hash[ISIS_AUTH_MD5_SIZE]; | |
362 | uint16_t checksum, rem_lifetime; | |
363 | ||
364 | /* For HMAC MD5 we need to recompute the md5 hash and store it */ | |
365 | (lsp->level == IS_LEVEL_1) ? (passwd = &lsp->area->area_passwd) : | |
366 | (passwd = &lsp->area->domain_passwd); | |
367 | if (passwd->type != ISIS_PASSWD_TYPE_HMAC_MD5) | |
368 | return; | |
369 | ||
370 | /* | |
371 | * In transient conditions (when net is configured where authentication | |
372 | * config and lsp regenerate schedule is not yet run), there could be | |
373 | * an own_lsp with auth_tlv_offset set to 0. In such a case, simply | |
374 | * return, when lsp_regenerate is run, lsp will have auth tlv. | |
375 | */ | |
376 | if (lsp->auth_tlv_offset == 0) | |
377 | return; | |
378 | ||
379 | /* | |
380 | * RFC 5304 set auth value, checksum and remaining lifetime to zero | |
381 | * before computation and reset to old values after computation. | |
382 | */ | |
383 | checksum = lsp->lsp_header->checksum; | |
384 | rem_lifetime = lsp->lsp_header->rem_lifetime; | |
385 | lsp->lsp_header->checksum = 0; | |
386 | lsp->lsp_header->rem_lifetime = 0; | |
387 | /* Set the authentication value as well to zero */ | |
388 | memset (STREAM_DATA (lsp->pdu) + lsp->auth_tlv_offset + 3, | |
389 | 0, ISIS_AUTH_MD5_SIZE); | |
390 | /* Compute autentication value */ | |
391 | hmac_md5 (STREAM_DATA (lsp->pdu), stream_get_endp(lsp->pdu), | |
392 | (unsigned char *) &passwd->passwd, passwd->len, | |
f2bce9a5 | 393 | (unsigned char *) &hmac_md5_hash); |
3f045a08 JB |
394 | /* Copy the hash into the stream */ |
395 | memcpy (STREAM_DATA (lsp->pdu) + lsp->auth_tlv_offset + 3, | |
396 | hmac_md5_hash, ISIS_AUTH_MD5_SIZE); | |
397 | memcpy (&lsp->tlv_data.auth_info.passwd, hmac_md5_hash, | |
398 | ISIS_AUTH_MD5_SIZE); | |
399 | /* Copy back the checksum and remaining lifetime */ | |
400 | lsp->lsp_header->checksum = checksum; | |
401 | lsp->lsp_header->rem_lifetime = rem_lifetime; | |
402 | } | |
403 | ||
f390d2c7 | 404 | void |
eb5d44eb | 405 | lsp_inc_seqnum (struct isis_lsp *lsp, u_int32_t seq_num) |
406 | { | |
407 | u_int32_t newseq; | |
f390d2c7 | 408 | |
eb5d44eb | 409 | if (seq_num == 0 || ntohl (lsp->lsp_header->seq_num) > seq_num) |
410 | newseq = ntohl (lsp->lsp_header->seq_num) + 1; | |
411 | else | |
3f045a08 | 412 | newseq = seq_num + 1; |
f390d2c7 | 413 | |
eb5d44eb | 414 | lsp->lsp_header->seq_num = htonl (newseq); |
3f045a08 JB |
415 | |
416 | /* Recompute authentication and checksum information */ | |
417 | lsp_auth_update (lsp); | |
418 | /* ISO 10589 - 7.3.11 Generation of the checksum | |
419 | * The checksum shall be computed over all fields in the LSP which appear | |
420 | * after the Remaining Lifetime field. This field (and those appearing | |
421 | * before it) are excluded so that the LSP may be aged by systems without | |
422 | * requiring recomputation. | |
423 | */ | |
424 | fletcher_checksum(STREAM_DATA (lsp->pdu) + 12, | |
425 | ntohs (lsp->lsp_header->pdu_len) - 12, 12); | |
426 | ||
427 | isis_spf_schedule (lsp->area, lsp->level); | |
eb5d44eb | 428 | |
429 | return; | |
430 | } | |
431 | ||
432 | /* | |
433 | * Genetates checksum for LSP and its frags | |
434 | */ | |
92365889 | 435 | static void |
eb5d44eb | 436 | lsp_seqnum_update (struct isis_lsp *lsp0) |
437 | { | |
438 | struct isis_lsp *lsp; | |
3fdb2dd9 | 439 | struct listnode *node; |
f390d2c7 | 440 | |
eb5d44eb | 441 | lsp_inc_seqnum (lsp0, 0); |
442 | ||
443 | if (!lsp0->lspu.frags) | |
444 | return; | |
445 | ||
3fdb2dd9 | 446 | for (ALL_LIST_ELEMENTS_RO (lsp0->lspu.frags, node, lsp)) |
1eb8ef25 | 447 | lsp_inc_seqnum (lsp, 0); |
f390d2c7 | 448 | |
eb5d44eb | 449 | return; |
450 | } | |
451 | ||
e38e0df0 | 452 | static u_int8_t |
7ed55a41 | 453 | lsp_bits_generate (int level, int overload_bit, int attached_bit) |
e38e0df0 SV |
454 | { |
455 | u_int8_t lsp_bits = 0; | |
456 | if (level == IS_LEVEL_1) | |
457 | lsp_bits = IS_LEVEL_1; | |
458 | else | |
459 | lsp_bits = IS_LEVEL_1_AND_2; | |
460 | if (overload_bit) | |
461 | lsp_bits |= overload_bit; | |
7ed55a41 AN |
462 | if (attached_bit) |
463 | lsp_bits |= attached_bit; | |
e38e0df0 SV |
464 | return lsp_bits; |
465 | } | |
466 | ||
92365889 | 467 | static void |
f390d2c7 | 468 | lsp_update_data (struct isis_lsp *lsp, struct stream *stream, |
3f045a08 | 469 | struct isis_area *area, int level) |
eb5d44eb | 470 | { |
f390d2c7 | 471 | uint32_t expected = 0, found; |
eb5d44eb | 472 | int retval; |
f390d2c7 | 473 | |
3f045a08 JB |
474 | /* free the old lsp data */ |
475 | lsp_clear_data (lsp); | |
476 | ||
eb5d44eb | 477 | /* copying only the relevant part of our stream */ |
3f045a08 JB |
478 | if (lsp->pdu != NULL) |
479 | stream_free (lsp->pdu); | |
15935e9a | 480 | lsp->pdu = stream_dup (stream); |
3f045a08 | 481 | |
eb5d44eb | 482 | /* setting pointers to the correct place */ |
f390d2c7 | 483 | lsp->isis_header = (struct isis_fixed_hdr *) (STREAM_DATA (lsp->pdu)); |
484 | lsp->lsp_header = (struct isis_link_state_hdr *) (STREAM_DATA (lsp->pdu) + | |
485 | ISIS_FIXED_HDR_LEN); | |
3f045a08 JB |
486 | lsp->area = area; |
487 | lsp->level = level; | |
eb5d44eb | 488 | lsp->age_out = ZERO_AGE_LIFETIME; |
f390d2c7 | 489 | lsp->installed = time (NULL); |
eb5d44eb | 490 | /* |
491 | * Get LSP data i.e. TLVs | |
492 | */ | |
493 | expected |= TLVFLAG_AUTH_INFO; | |
494 | expected |= TLVFLAG_AREA_ADDRS; | |
495 | expected |= TLVFLAG_IS_NEIGHS; | |
eb5d44eb | 496 | expected |= TLVFLAG_NLPID; |
497 | if (area->dynhostname) | |
498 | expected |= TLVFLAG_DYN_HOSTNAME; | |
f390d2c7 | 499 | if (area->newmetric) |
500 | { | |
501 | expected |= TLVFLAG_TE_IS_NEIGHS; | |
502 | expected |= TLVFLAG_TE_IPV4_REACHABILITY; | |
503 | expected |= TLVFLAG_TE_ROUTER_ID; | |
504 | } | |
99894f9a | 505 | expected |= TLVFLAG_MT_ROUTER_INFORMATION; |
eb5d44eb | 506 | expected |= TLVFLAG_IPV4_ADDR; |
507 | expected |= TLVFLAG_IPV4_INT_REACHABILITY; | |
508 | expected |= TLVFLAG_IPV4_EXT_REACHABILITY; | |
eb5d44eb | 509 | expected |= TLVFLAG_IPV6_ADDR; |
510 | expected |= TLVFLAG_IPV6_REACHABILITY; | |
eb5d44eb | 511 | |
3f045a08 JB |
512 | retval = parse_tlvs (area->area_tag, STREAM_DATA (lsp->pdu) + |
513 | ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN, | |
514 | ntohs (lsp->lsp_header->pdu_len) - | |
515 | ISIS_FIXED_HDR_LEN - ISIS_LSP_HDR_LEN, | |
516 | &expected, &found, &lsp->tlv_data, | |
517 | NULL); | |
518 | if (retval != ISIS_OK) | |
519 | { | |
520 | zlog_warn ("Could not parse LSP"); | |
521 | return; | |
522 | } | |
f390d2c7 | 523 | |
3f045a08 | 524 | if ((found & TLVFLAG_DYN_HOSTNAME) && (area->dynhostname)) |
f390d2c7 | 525 | { |
e38e0df0 SV |
526 | isis_dynhn_insert (lsp->lsp_header->lsp_id, lsp->tlv_data.hostname, |
527 | (lsp->lsp_header->lsp_bits & LSPBIT_IST) == | |
528 | IS_LEVEL_1_AND_2 ? IS_LEVEL_2 : IS_LEVEL_1); | |
f390d2c7 | 529 | } |
eb5d44eb | 530 | |
3f045a08 | 531 | return; |
eb5d44eb | 532 | } |
533 | ||
534 | void | |
3f045a08 JB |
535 | lsp_update (struct isis_lsp *lsp, struct stream *stream, |
536 | struct isis_area *area, int level) | |
eb5d44eb | 537 | { |
4eda93ab | 538 | dnode_t *dnode = NULL; |
a96d8d10 | 539 | |
3f045a08 JB |
540 | /* Remove old LSP from database. This is required since the |
541 | * lsp_update_data will free the lsp->pdu (which has the key, lsp_id) | |
542 | * and will update it with the new data in the stream. */ | |
4eda93ab | 543 | dnode = dict_lookup (area->lspdb[level - 1], lsp->lsp_header->lsp_id); |
544 | if (dnode) | |
545 | dnode_destroy (dict_delete (area->lspdb[level - 1], dnode)); | |
a96d8d10 | 546 | |
07f2fb13 CF |
547 | if (lsp->own_lsp) |
548 | { | |
549 | zlog_err("ISIS-Upd (%s): BUG updating LSP %s still marked as own LSP", | |
550 | area->area_tag, rawlspid_print(lsp->lsp_header->lsp_id)); | |
551 | lsp_clear_data(lsp); | |
552 | lsp->own_lsp = 0; | |
553 | } | |
554 | ||
eb5d44eb | 555 | /* rebuild the lsp data */ |
3f045a08 | 556 | lsp_update_data (lsp, stream, area, level); |
a96d8d10 | 557 | |
3f045a08 JB |
558 | /* insert the lsp back into the database */ |
559 | lsp_insert (lsp, area->lspdb[level - 1]); | |
eb5d44eb | 560 | } |
561 | ||
eb5d44eb | 562 | /* creation of LSP directly from what we received */ |
563 | struct isis_lsp * | |
f390d2c7 | 564 | lsp_new_from_stream_ptr (struct stream *stream, |
565 | u_int16_t pdu_len, struct isis_lsp *lsp0, | |
3f045a08 | 566 | struct isis_area *area, int level) |
eb5d44eb | 567 | { |
568 | struct isis_lsp *lsp; | |
569 | ||
aac372f4 | 570 | lsp = XCALLOC (MTYPE_ISIS_LSP, sizeof (struct isis_lsp)); |
3f045a08 | 571 | lsp_update_data (lsp, stream, area, level); |
f390d2c7 | 572 | |
573 | if (lsp0 == NULL) | |
574 | { | |
575 | /* | |
576 | * zero lsp -> create the list for fragments | |
577 | */ | |
578 | lsp->lspu.frags = list_new (); | |
579 | } | |
580 | else | |
581 | { | |
582 | /* | |
583 | * a fragment -> set the backpointer and add this to zero lsps frag list | |
584 | */ | |
585 | lsp->lspu.zero_lsp = lsp0; | |
586 | listnode_add (lsp0->lspu.frags, lsp); | |
587 | } | |
588 | ||
eb5d44eb | 589 | return lsp; |
590 | } | |
591 | ||
592 | struct isis_lsp * | |
b20ccb3a CF |
593 | lsp_new(struct isis_area *area, u_char * lsp_id, |
594 | u_int16_t rem_lifetime, u_int32_t seq_num, | |
595 | u_int8_t lsp_bits, u_int16_t checksum, int level) | |
eb5d44eb | 596 | { |
597 | struct isis_lsp *lsp; | |
598 | ||
aac372f4 | 599 | lsp = XCALLOC (MTYPE_ISIS_LSP, sizeof (struct isis_lsp)); |
b20ccb3a | 600 | lsp->area = area; |
d66fa221 | 601 | |
b20ccb3a | 602 | lsp->pdu = stream_new(LLC_LEN + area->lsp_mtu); |
eb5d44eb | 603 | if (LSP_FRAGMENT (lsp_id) == 0) |
604 | lsp->lspu.frags = list_new (); | |
f390d2c7 | 605 | lsp->isis_header = (struct isis_fixed_hdr *) (STREAM_DATA (lsp->pdu)); |
606 | lsp->lsp_header = (struct isis_link_state_hdr *) | |
607 | (STREAM_DATA (lsp->pdu) + ISIS_FIXED_HDR_LEN); | |
608 | ||
eb5d44eb | 609 | /* at first we fill the FIXED HEADER */ |
3f045a08 | 610 | (level == IS_LEVEL_1) ? fill_fixed_hdr (lsp->isis_header, L1_LINK_STATE) : |
f390d2c7 | 611 | fill_fixed_hdr (lsp->isis_header, L2_LINK_STATE); |
612 | ||
eb5d44eb | 613 | /* now for the LSP HEADER */ |
614 | /* Minimal LSP PDU size */ | |
f390d2c7 | 615 | lsp->lsp_header->pdu_len = htons (ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); |
eb5d44eb | 616 | memcpy (lsp->lsp_header->lsp_id, lsp_id, ISIS_SYS_ID_LEN + 2); |
f390d2c7 | 617 | lsp->lsp_header->checksum = checksum; /* Provided in network order */ |
eb5d44eb | 618 | lsp->lsp_header->seq_num = htonl (seq_num); |
619 | lsp->lsp_header->rem_lifetime = htons (rem_lifetime); | |
620 | lsp->lsp_header->lsp_bits = lsp_bits; | |
621 | lsp->level = level; | |
622 | lsp->age_out = ZERO_AGE_LIFETIME; | |
623 | ||
9985f83c | 624 | stream_forward_endp (lsp->pdu, ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); |
eb5d44eb | 625 | |
c89c05dd | 626 | if (isis->debugs & DEBUG_EVENTS) |
3f045a08 | 627 | zlog_debug ("New LSP with ID %s-%02x-%02x len %d seqnum %08x", |
c89c05dd | 628 | sysid_print (lsp_id), LSP_PSEUDO_ID (lsp->lsp_header->lsp_id), |
629 | LSP_FRAGMENT (lsp->lsp_header->lsp_id), | |
3f045a08 | 630 | ntohl (lsp->lsp_header->pdu_len), |
c89c05dd | 631 | ntohl (lsp->lsp_header->seq_num)); |
eb5d44eb | 632 | |
633 | return lsp; | |
634 | } | |
635 | ||
636 | void | |
f390d2c7 | 637 | lsp_insert (struct isis_lsp *lsp, dict_t * lspdb) |
eb5d44eb | 638 | { |
639 | dict_alloc_insert (lspdb, lsp->lsp_header->lsp_id, lsp); | |
3f045a08 JB |
640 | if (lsp->lsp_header->seq_num != 0) |
641 | { | |
642 | isis_spf_schedule (lsp->area, lsp->level); | |
3f045a08 | 643 | } |
eb5d44eb | 644 | } |
645 | ||
646 | /* | |
647 | * Build a list of LSPs with non-zero ht bounded by start and stop ids | |
648 | */ | |
f390d2c7 | 649 | void |
650 | lsp_build_list_nonzero_ht (u_char * start_id, u_char * stop_id, | |
651 | struct list *list, dict_t * lspdb) | |
eb5d44eb | 652 | { |
653 | dnode_t *first, *last, *curr; | |
654 | ||
655 | first = dict_lower_bound (lspdb, start_id); | |
656 | if (!first) | |
657 | return; | |
f390d2c7 | 658 | |
eb5d44eb | 659 | last = dict_upper_bound (lspdb, stop_id); |
f390d2c7 | 660 | |
eb5d44eb | 661 | curr = first; |
f390d2c7 | 662 | |
663 | if (((struct isis_lsp *) (curr->dict_data))->lsp_header->rem_lifetime) | |
eb5d44eb | 664 | listnode_add (list, first->dict_data); |
665 | ||
f390d2c7 | 666 | while (curr) |
667 | { | |
668 | curr = dict_next (lspdb, curr); | |
669 | if (curr && | |
670 | ((struct isis_lsp *) (curr->dict_data))->lsp_header->rem_lifetime) | |
671 | listnode_add (list, curr->dict_data); | |
672 | if (curr == last) | |
673 | break; | |
674 | } | |
675 | ||
eb5d44eb | 676 | return; |
677 | } | |
678 | ||
679 | /* | |
3f045a08 | 680 | * Build a list of num_lsps LSPs bounded by start_id and stop_id. |
eb5d44eb | 681 | */ |
f390d2c7 | 682 | void |
3f045a08 | 683 | lsp_build_list (u_char * start_id, u_char * stop_id, u_char num_lsps, |
f390d2c7 | 684 | struct list *list, dict_t * lspdb) |
eb5d44eb | 685 | { |
3f045a08 | 686 | u_char count; |
eb5d44eb | 687 | dnode_t *first, *last, *curr; |
688 | ||
689 | first = dict_lower_bound (lspdb, start_id); | |
690 | if (!first) | |
691 | return; | |
f390d2c7 | 692 | |
eb5d44eb | 693 | last = dict_upper_bound (lspdb, stop_id); |
f390d2c7 | 694 | |
eb5d44eb | 695 | curr = first; |
f390d2c7 | 696 | |
eb5d44eb | 697 | listnode_add (list, first->dict_data); |
3f045a08 | 698 | count = 1; |
eb5d44eb | 699 | |
f390d2c7 | 700 | while (curr) |
701 | { | |
702 | curr = dict_next (lspdb, curr); | |
703 | if (curr) | |
3f045a08 JB |
704 | { |
705 | listnode_add (list, curr->dict_data); | |
706 | count++; | |
707 | } | |
708 | if (count == num_lsps || curr == last) | |
709 | break; | |
f390d2c7 | 710 | } |
711 | ||
eb5d44eb | 712 | return; |
713 | } | |
714 | ||
715 | /* | |
716 | * Build a list of LSPs with SSN flag set for the given circuit | |
717 | */ | |
718 | void | |
3f045a08 JB |
719 | lsp_build_list_ssn (struct isis_circuit *circuit, u_char num_lsps, |
720 | struct list *list, dict_t * lspdb) | |
eb5d44eb | 721 | { |
722 | dnode_t *dnode, *next; | |
723 | struct isis_lsp *lsp; | |
3f045a08 | 724 | u_char count = 0; |
f390d2c7 | 725 | |
eb5d44eb | 726 | dnode = dict_first (lspdb); |
f390d2c7 | 727 | while (dnode != NULL) |
728 | { | |
729 | next = dict_next (lspdb, dnode); | |
730 | lsp = dnode_get (dnode); | |
731 | if (ISIS_CHECK_FLAG (lsp->SSNflags, circuit)) | |
3f045a08 JB |
732 | { |
733 | listnode_add (list, lsp); | |
734 | ++count; | |
735 | } | |
736 | if (count == num_lsps) | |
737 | break; | |
f390d2c7 | 738 | dnode = next; |
739 | } | |
740 | ||
eb5d44eb | 741 | return; |
742 | } | |
743 | ||
92365889 | 744 | static void |
eb5d44eb | 745 | lsp_set_time (struct isis_lsp *lsp) |
746 | { | |
747 | assert (lsp); | |
f390d2c7 | 748 | |
749 | if (lsp->lsp_header->rem_lifetime == 0) | |
750 | { | |
3f045a08 JB |
751 | if (lsp->age_out > 0) |
752 | lsp->age_out--; | |
f390d2c7 | 753 | return; |
754 | } | |
eb5d44eb | 755 | |
f390d2c7 | 756 | lsp->lsp_header->rem_lifetime = |
eb5d44eb | 757 | htons (ntohs (lsp->lsp_header->rem_lifetime) - 1); |
758 | } | |
759 | ||
92365889 | 760 | static void |
f390d2c7 | 761 | lspid_print (u_char * lsp_id, u_char * trg, char dynhost, char frag) |
eb5d44eb | 762 | { |
763 | struct isis_dynhn *dyn = NULL; | |
f390d2c7 | 764 | u_char id[SYSID_STRLEN]; |
eb5d44eb | 765 | |
766 | if (dynhost) | |
767 | dyn = dynhn_find_by_id (lsp_id); | |
768 | else | |
769 | dyn = NULL; | |
770 | ||
771 | if (dyn) | |
3f045a08 JB |
772 | sprintf ((char *)id, "%.14s", dyn->name.name); |
773 | else if (!memcmp (isis->sysid, lsp_id, ISIS_SYS_ID_LEN) && dynhost) | |
774 | sprintf ((char *)id, "%.14s", unix_hostname ()); | |
f390d2c7 | 775 | else |
f390d2c7 | 776 | memcpy (id, sysid_print (lsp_id), 15); |
eb5d44eb | 777 | if (frag) |
f7c43dcb | 778 | sprintf ((char *)trg, "%s.%02x-%02x", id, LSP_PSEUDO_ID (lsp_id), |
f390d2c7 | 779 | LSP_FRAGMENT (lsp_id)); |
eb5d44eb | 780 | else |
f7c43dcb | 781 | sprintf ((char *)trg, "%s.%02x", id, LSP_PSEUDO_ID (lsp_id)); |
eb5d44eb | 782 | } |
783 | ||
f390d2c7 | 784 | /* Convert the lsp attribute bits to attribute string */ |
1cd80845 | 785 | const char * |
f390d2c7 | 786 | lsp_bits2string (u_char * lsp_bits) |
787 | { | |
788 | char *pos = lsp_bits_string; | |
eb5d44eb | 789 | |
f390d2c7 | 790 | if (!*lsp_bits) |
eb5d44eb | 791 | return " none"; |
792 | ||
793 | /* we only focus on the default metric */ | |
794 | pos += sprintf (pos, "%d/", | |
f390d2c7 | 795 | ISIS_MASK_LSP_ATT_DEFAULT_BIT (*lsp_bits) ? 1 : 0); |
eb5d44eb | 796 | |
797 | pos += sprintf (pos, "%d/", | |
f390d2c7 | 798 | ISIS_MASK_LSP_PARTITION_BIT (*lsp_bits) ? 1 : 0); |
799 | ||
800 | pos += sprintf (pos, "%d", ISIS_MASK_LSP_OL_BIT (*lsp_bits) ? 1 : 0); | |
eb5d44eb | 801 | |
eb5d44eb | 802 | *(pos) = '\0'; |
eb5d44eb | 803 | |
f390d2c7 | 804 | return lsp_bits_string; |
eb5d44eb | 805 | } |
806 | ||
807 | /* this function prints the lsp on show isis database */ | |
3f045a08 JB |
808 | void |
809 | lsp_print (struct isis_lsp *lsp, struct vty *vty, char dynhost) | |
eb5d44eb | 810 | { |
eb5d44eb | 811 | u_char LSPid[255]; |
3f045a08 | 812 | char age_out[8]; |
eb5d44eb | 813 | |
814 | lspid_print (lsp->lsp_header->lsp_id, LSPid, dynhost, 1); | |
3f045a08 JB |
815 | vty_out (vty, "%-21s%c ", LSPid, lsp->own_lsp ? '*' : ' '); |
816 | vty_out (vty, "%5u ", ntohs (lsp->lsp_header->pdu_len)); | |
817 | vty_out (vty, "0x%08x ", ntohl (lsp->lsp_header->seq_num)); | |
818 | vty_out (vty, "0x%04x ", ntohs (lsp->lsp_header->checksum)); | |
f390d2c7 | 819 | if (ntohs (lsp->lsp_header->rem_lifetime) == 0) |
3f045a08 JB |
820 | { |
821 | snprintf (age_out, 8, "(%u)", lsp->age_out); | |
822 | age_out[7] = '\0'; | |
823 | vty_out (vty, "%7s ", age_out); | |
824 | } | |
eb5d44eb | 825 | else |
3f045a08 | 826 | vty_out (vty, " %5u ", ntohs (lsp->lsp_header->rem_lifetime)); |
5c7571d4 | 827 | vty_out (vty, "%s\n", |
96ade3ed | 828 | lsp_bits2string(&lsp->lsp_header->lsp_bits)); |
eb5d44eb | 829 | } |
830 | ||
206f4aae CF |
831 | static void |
832 | lsp_print_mt_reach(struct list *list, struct vty *vty, | |
833 | char dynhost, uint16_t mtid) | |
834 | { | |
835 | struct listnode *node; | |
836 | struct te_is_neigh *neigh; | |
837 | ||
838 | for (ALL_LIST_ELEMENTS_RO (list, node, neigh)) | |
839 | { | |
840 | u_char lspid[255]; | |
841 | ||
842 | lspid_print(neigh->neigh_id, lspid, dynhost, 0); | |
843 | if (mtid == ISIS_MT_IPV4_UNICAST) | |
844 | { | |
5c7571d4 | 845 | vty_out(vty, " Metric : %-8u IS-Extended : %s\n", |
96ade3ed | 846 | GET_TE_METRIC(neigh), lspid); |
206f4aae CF |
847 | } |
848 | else | |
849 | { | |
5c7571d4 | 850 | vty_out(vty, " Metric : %-8u MT-Reach : %s %s\n", |
96ade3ed QY |
851 | GET_TE_METRIC(neigh), lspid, |
852 | isis_mtid2str(mtid)); | |
206f4aae CF |
853 | } |
854 | if (IS_MPLS_TE(isisMplsTE)) | |
855 | mpls_te_print_detail(vty, neigh); | |
856 | } | |
857 | } | |
858 | ||
c3ae3127 CF |
859 | static void |
860 | lsp_print_mt_ipv6_reach(struct list *list, struct vty *vty, uint16_t mtid) | |
861 | { | |
862 | struct listnode *node; | |
863 | struct ipv6_reachability *ipv6_reach; | |
864 | struct in6_addr in6; | |
865 | u_char buff[BUFSIZ]; | |
866 | ||
867 | for (ALL_LIST_ELEMENTS_RO (list, node, ipv6_reach)) | |
868 | { | |
869 | memset (&in6, 0, sizeof (in6)); | |
870 | memcpy (in6.s6_addr, ipv6_reach->prefix, | |
871 | PSIZE (ipv6_reach->prefix_len)); | |
872 | inet_ntop (AF_INET6, &in6, (char *)buff, BUFSIZ); | |
873 | if (mtid == ISIS_MT_IPV4_UNICAST) | |
874 | { | |
875 | if ((ipv6_reach->control_info & | |
876 | CTRL_INFO_DISTRIBUTION) == DISTRIBUTION_INTERNAL) | |
181039f3 | 877 | vty_out (vty, " Metric : %-8" PRIu32 " IPv6-Internal : %s/%d\n", |
96ade3ed QY |
878 | ntohl (ipv6_reach->metric), |
879 | buff, ipv6_reach->prefix_len); | |
c3ae3127 | 880 | else |
181039f3 | 881 | vty_out (vty, " Metric : %-8" PRIu32 " IPv6-External : %s/%d\n", |
96ade3ed QY |
882 | ntohl (ipv6_reach->metric), |
883 | buff, ipv6_reach->prefix_len); | |
c3ae3127 CF |
884 | } |
885 | else | |
886 | { | |
887 | if ((ipv6_reach->control_info & | |
888 | CTRL_INFO_DISTRIBUTION) == DISTRIBUTION_INTERNAL) | |
181039f3 | 889 | vty_out (vty, " Metric : %-8" PRIu32 " IPv6-MT-Int : %s/%d %s\n", |
96ade3ed QY |
890 | ntohl (ipv6_reach->metric), |
891 | buff, ipv6_reach->prefix_len, | |
892 | isis_mtid2str(mtid)); | |
c3ae3127 | 893 | else |
181039f3 | 894 | vty_out (vty, " Metric : %-8" PRIu32 " IPv6-MT-Ext : %s/%d %s\n", |
96ade3ed QY |
895 | ntohl (ipv6_reach->metric), |
896 | buff, ipv6_reach->prefix_len, | |
897 | isis_mtid2str(mtid)); | |
c3ae3127 CF |
898 | } |
899 | } | |
900 | } | |
901 | ||
902 | static void | |
903 | lsp_print_mt_ipv4_reach(struct list *list, struct vty *vty, uint16_t mtid) | |
904 | { | |
905 | struct listnode *node; | |
906 | struct te_ipv4_reachability *te_ipv4_reach; | |
907 | ||
908 | for (ALL_LIST_ELEMENTS_RO (list, node, te_ipv4_reach)) | |
909 | { | |
910 | if (mtid == ISIS_MT_IPV4_UNICAST) | |
911 | { | |
912 | /* FIXME: There should be better way to output this stuff. */ | |
181039f3 | 913 | vty_out (vty, " Metric : %-8" PRIu32 " IPv4-Extended : %s/%d\n", |
96ade3ed QY |
914 | ntohl (te_ipv4_reach->te_metric), |
915 | inet_ntoa (newprefix2inaddr (&te_ipv4_reach->prefix_start, | |
916 | te_ipv4_reach->control)), | |
917 | te_ipv4_reach->control & 0x3F); | |
c3ae3127 CF |
918 | } |
919 | else | |
920 | { | |
921 | /* FIXME: There should be better way to output this stuff. */ | |
181039f3 | 922 | vty_out (vty, " Metric : %-8" PRIu32 " IPv4-MT : %s/%d %s\n", |
96ade3ed QY |
923 | ntohl (te_ipv4_reach->te_metric), |
924 | inet_ntoa (newprefix2inaddr (&te_ipv4_reach->prefix_start, | |
925 | te_ipv4_reach->control)), | |
926 | te_ipv4_reach->control & 0x3F, | |
927 | isis_mtid2str(mtid)); | |
c3ae3127 CF |
928 | } |
929 | } | |
930 | } | |
931 | ||
3f045a08 JB |
932 | void |
933 | lsp_print_detail (struct isis_lsp *lsp, struct vty *vty, char dynhost) | |
eb5d44eb | 934 | { |
eb5d44eb | 935 | struct area_addr *area_addr; |
f390d2c7 | 936 | int i; |
3fdb2dd9 | 937 | struct listnode *lnode; |
eb5d44eb | 938 | struct is_neigh *is_neigh; |
eb5d44eb | 939 | struct ipv4_reachability *ipv4_reach; |
940 | struct in_addr *ipv4_addr; | |
99894f9a | 941 | struct mt_router_info *mt_router_info; |
c3ae3127 | 942 | struct tlv_mt_ipv6_reachs *mt_ipv6_reachs; |
206f4aae | 943 | struct tlv_mt_neighbors *mt_is_neigh; |
c3ae3127 | 944 | struct tlv_mt_ipv4_reachs *mt_ipv4_reachs; |
eb5d44eb | 945 | u_char LSPid[255]; |
946 | u_char hostname[255]; | |
eb5d44eb | 947 | u_char ipv4_reach_prefix[20]; |
948 | u_char ipv4_reach_mask[20]; | |
949 | u_char ipv4_address[20]; | |
950 | ||
951 | lspid_print (lsp->lsp_header->lsp_id, LSPid, dynhost, 1); | |
3f045a08 | 952 | lsp_print (lsp, vty, dynhost); |
eb5d44eb | 953 | |
954 | /* for all area address */ | |
f390d2c7 | 955 | if (lsp->tlv_data.area_addrs) |
3fdb2dd9 | 956 | for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.area_addrs, lnode, area_addr)) |
f390d2c7 | 957 | { |
5c7571d4 | 958 | vty_out (vty, " Area Address: %s\n", |
96ade3ed | 959 | isonet_print(area_addr->area_addr, area_addr->addr_len)); |
f390d2c7 | 960 | } |
1eb8ef25 | 961 | |
eb5d44eb | 962 | /* for the nlpid tlv */ |
f390d2c7 | 963 | if (lsp->tlv_data.nlpids) |
964 | { | |
965 | for (i = 0; i < lsp->tlv_data.nlpids->count; i++) | |
966 | { | |
967 | switch (lsp->tlv_data.nlpids->nlpids[i]) | |
968 | { | |
969 | case NLPID_IP: | |
970 | case NLPID_IPV6: | |
5c7571d4 | 971 | vty_out (vty, " NLPID : 0x%X\n", |
96ade3ed | 972 | lsp->tlv_data.nlpids->nlpids[i]); |
f390d2c7 | 973 | break; |
974 | default: | |
5c7571d4 | 975 | vty_out (vty, " NLPID : %s\n", "unknown"); |
f390d2c7 | 976 | break; |
977 | } | |
978 | } | |
979 | } | |
eb5d44eb | 980 | |
99894f9a CF |
981 | for (ALL_LIST_ELEMENTS_RO(lsp->tlv_data.mt_router_info, lnode, mt_router_info)) |
982 | { | |
5c7571d4 | 983 | vty_out (vty, " MT : %s%s\n", |
99894f9a | 984 | isis_mtid2str(mt_router_info->mtid), |
96ade3ed | 985 | mt_router_info->overload ? " (overload)" : ""); |
99894f9a CF |
986 | } |
987 | ||
eb5d44eb | 988 | /* for the hostname tlv */ |
f390d2c7 | 989 | if (lsp->tlv_data.hostname) |
990 | { | |
3f045a08 | 991 | bzero (hostname, sizeof (hostname)); |
f390d2c7 | 992 | memcpy (hostname, lsp->tlv_data.hostname->name, |
993 | lsp->tlv_data.hostname->namelen); | |
5c7571d4 | 994 | vty_out (vty, " Hostname : %s\n", hostname); |
f390d2c7 | 995 | } |
eb5d44eb | 996 | |
3f045a08 JB |
997 | /* authentication tlv */ |
998 | if (lsp->tlv_data.auth_info.type != ISIS_PASSWD_TYPE_UNUSED) | |
999 | { | |
1000 | if (lsp->tlv_data.auth_info.type == ISIS_PASSWD_TYPE_HMAC_MD5) | |
5c7571d4 | 1001 | vty_out (vty, " Auth type : md5\n"); |
3f045a08 | 1002 | else if (lsp->tlv_data.auth_info.type == ISIS_PASSWD_TYPE_CLEARTXT) |
5c7571d4 | 1003 | vty_out (vty, " Auth type : clear text\n"); |
3f045a08 | 1004 | } |
f390d2c7 | 1005 | |
1cd80845 | 1006 | /* TE router id */ |
1007 | if (lsp->tlv_data.router_id) | |
1008 | { | |
1009 | memcpy (ipv4_address, inet_ntoa (lsp->tlv_data.router_id->id), | |
1010 | sizeof (ipv4_address)); | |
5c7571d4 | 1011 | vty_out (vty, " Router ID : %s\n", ipv4_address); |
1cd80845 | 1012 | } |
1013 | ||
3f045a08 JB |
1014 | if (lsp->tlv_data.ipv4_addrs) |
1015 | for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv4_addrs, lnode, ipv4_addr)) | |
1016 | { | |
1017 | memcpy (ipv4_address, inet_ntoa (*ipv4_addr), sizeof (ipv4_address)); | |
5c7571d4 | 1018 | vty_out (vty, " IPv4 Address: %s\n", ipv4_address); |
3f045a08 JB |
1019 | } |
1020 | ||
1cd80845 | 1021 | /* for the IS neighbor tlv */ |
1022 | if (lsp->tlv_data.is_neighs) | |
3fdb2dd9 | 1023 | for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.is_neighs, lnode, is_neigh)) |
1cd80845 | 1024 | { |
1025 | lspid_print (is_neigh->neigh_id, LSPid, dynhost, 0); | |
181039f3 | 1026 | vty_out (vty, " Metric : %-8" PRIu8 " IS : %s\n", |
96ade3ed | 1027 | is_neigh->metrics.metric_default, LSPid); |
1cd80845 | 1028 | } |
1cd80845 | 1029 | |
eb5d44eb | 1030 | /* for the internal reachable tlv */ |
1031 | if (lsp->tlv_data.ipv4_int_reachs) | |
3fdb2dd9 | 1032 | for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv4_int_reachs, lnode, |
1033 | ipv4_reach)) | |
f390d2c7 | 1034 | { |
1035 | memcpy (ipv4_reach_prefix, inet_ntoa (ipv4_reach->prefix), | |
1036 | sizeof (ipv4_reach_prefix)); | |
1037 | memcpy (ipv4_reach_mask, inet_ntoa (ipv4_reach->mask), | |
1038 | sizeof (ipv4_reach_mask)); | |
181039f3 | 1039 | vty_out (vty, " Metric : %-8" PRIu8 " IPv4-Internal : %s %s\n", |
96ade3ed QY |
1040 | ipv4_reach->metrics.metric_default, ipv4_reach_prefix, |
1041 | ipv4_reach_mask); | |
f390d2c7 | 1042 | } |
2097cd8a | 1043 | |
1044 | /* for the external reachable tlv */ | |
1045 | if (lsp->tlv_data.ipv4_ext_reachs) | |
3fdb2dd9 | 1046 | for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv4_ext_reachs, lnode, |
1047 | ipv4_reach)) | |
f390d2c7 | 1048 | { |
1049 | memcpy (ipv4_reach_prefix, inet_ntoa (ipv4_reach->prefix), | |
1050 | sizeof (ipv4_reach_prefix)); | |
1051 | memcpy (ipv4_reach_mask, inet_ntoa (ipv4_reach->mask), | |
1052 | sizeof (ipv4_reach_mask)); | |
181039f3 | 1053 | vty_out (vty, " Metric : %-8" PRIu8 " IPv4-External : %s %s\n", |
96ade3ed QY |
1054 | ipv4_reach->metrics.metric_default, ipv4_reach_prefix, |
1055 | ipv4_reach_mask); | |
f390d2c7 | 1056 | } |
c3ae3127 | 1057 | |
2097cd8a | 1058 | /* IPv6 tlv */ |
c3ae3127 CF |
1059 | lsp_print_mt_ipv6_reach(lsp->tlv_data.ipv6_reachs, vty, |
1060 | ISIS_MT_IPV4_UNICAST); | |
1061 | ||
1062 | /* MT IPv6 reachability tlv */ | |
1063 | for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.mt_ipv6_reachs, lnode, mt_ipv6_reachs)) | |
1064 | lsp_print_mt_ipv6_reach(mt_ipv6_reachs->list, vty, mt_ipv6_reachs->mtid); | |
1eb8ef25 | 1065 | |
1cd80845 | 1066 | /* TE IS neighbor tlv */ |
206f4aae CF |
1067 | lsp_print_mt_reach(lsp->tlv_data.te_is_neighs, vty, |
1068 | dynhost, ISIS_MT_IPV4_UNICAST); | |
1069 | ||
1070 | /* MT IS neighbor tlv */ | |
1071 | for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.mt_is_neighs, lnode, mt_is_neigh)) | |
1072 | lsp_print_mt_reach(mt_is_neigh->list, vty, dynhost, mt_is_neigh->mtid); | |
eb5d44eb | 1073 | |
1cd80845 | 1074 | /* TE IPv4 tlv */ |
c3ae3127 CF |
1075 | lsp_print_mt_ipv4_reach(lsp->tlv_data.te_ipv4_reachs, vty, |
1076 | ISIS_MT_IPV4_UNICAST); | |
1077 | ||
1078 | /* MT IPv4 reachability tlv */ | |
1079 | for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.mt_ipv4_reachs, lnode, mt_ipv4_reachs)) | |
1080 | lsp_print_mt_ipv4_reach(mt_ipv4_reachs->list, vty, mt_ipv4_reachs->mtid); | |
1081 | ||
6d3c2ed4 | 1082 | vty_out (vty, "\n"); |
eb5d44eb | 1083 | |
f390d2c7 | 1084 | return; |
eb5d44eb | 1085 | } |
1086 | ||
1087 | /* print all the lsps info in the local lspdb */ | |
f390d2c7 | 1088 | int |
1089 | lsp_print_all (struct vty *vty, dict_t * lspdb, char detail, char dynhost) | |
eb5d44eb | 1090 | { |
1091 | ||
f390d2c7 | 1092 | dnode_t *node = dict_first (lspdb), *next; |
eb5d44eb | 1093 | int lsp_count = 0; |
1094 | ||
f390d2c7 | 1095 | if (detail == ISIS_UI_LEVEL_BRIEF) |
1096 | { | |
1097 | while (node != NULL) | |
1098 | { | |
1099 | /* I think it is unnecessary, so I comment it out */ | |
1100 | /* dict_contains (lspdb, node); */ | |
1101 | next = dict_next (lspdb, node); | |
3f045a08 | 1102 | lsp_print (dnode_get (node), vty, dynhost); |
f390d2c7 | 1103 | node = next; |
1104 | lsp_count++; | |
1105 | } | |
1106 | } | |
1107 | else if (detail == ISIS_UI_LEVEL_DETAIL) | |
1108 | { | |
1109 | while (node != NULL) | |
1110 | { | |
1111 | next = dict_next (lspdb, node); | |
3f045a08 | 1112 | lsp_print_detail (dnode_get (node), vty, dynhost); |
f390d2c7 | 1113 | node = next; |
1114 | lsp_count++; | |
1115 | } | |
eb5d44eb | 1116 | } |
eb5d44eb | 1117 | |
1118 | return lsp_count; | |
1119 | } | |
1120 | ||
206f4aae CF |
1121 | static void |
1122 | _lsp_tlv_fit (struct isis_lsp *lsp, struct list **from, struct list **to, | |
1123 | int frag_thold, | |
1124 | unsigned int tlv_build_func (struct list *, struct stream *, | |
1125 | void *arg), | |
1126 | void *arg) | |
1127 | { | |
1128 | while (*from && listcount(*from)) | |
1129 | { | |
1130 | unsigned int count; | |
1131 | ||
1132 | count = tlv_build_func(*from, lsp->pdu, arg); | |
1133 | ||
1134 | if (listcount(*to) != 0 || count != listcount(*from)) | |
1135 | { | |
1136 | struct listnode *node, *nnode; | |
1137 | void *elem; | |
1138 | ||
1139 | for (ALL_LIST_ELEMENTS(*from, node, nnode, elem)) | |
1140 | { | |
1141 | if (!count) | |
1142 | break; | |
1143 | listnode_add (*to, elem); | |
1144 | list_delete_node (*from, node); | |
1145 | --count; | |
1146 | } | |
1147 | } | |
1148 | else | |
1149 | { | |
1150 | list_free (*to); | |
1151 | *to = *from; | |
1152 | *from = NULL; | |
1153 | } | |
1154 | } | |
1155 | } | |
1156 | ||
eb5d44eb | 1157 | #define FRAG_THOLD(S,T) \ |
3f045a08 | 1158 | ((STREAM_SIZE(S)*T)/100) |
eb5d44eb | 1159 | |
1160 | /* stream*, area->lsp_frag_threshold, increment */ | |
1161 | #define FRAG_NEEDED(S,T,I) \ | |
1162 | (STREAM_SIZE(S)-STREAM_REMAIN(S)+(I) > FRAG_THOLD(S,T)) | |
1163 | ||
aa4376ec | 1164 | /* FIXME: It shouldn't be necessary to pass tlvsize here, TLVs can have |
1165 | * variable length (TE TLVs, sub TLVs). */ | |
92365889 | 1166 | static void |
eb5d44eb | 1167 | lsp_tlv_fit (struct isis_lsp *lsp, struct list **from, struct list **to, |
f390d2c7 | 1168 | int tlvsize, int frag_thold, |
1169 | int tlv_build_func (struct list *, struct stream *)) | |
eb5d44eb | 1170 | { |
1171 | int count, i; | |
f390d2c7 | 1172 | |
eb5d44eb | 1173 | /* can we fit all ? */ |
f390d2c7 | 1174 | if (!FRAG_NEEDED (lsp->pdu, frag_thold, listcount (*from) * tlvsize + 2)) |
1175 | { | |
1176 | tlv_build_func (*from, lsp->pdu); | |
3f045a08 JB |
1177 | if (listcount (*to) != 0) |
1178 | { | |
1179 | struct listnode *node, *nextnode; | |
1180 | void *elem; | |
1181 | ||
1182 | for (ALL_LIST_ELEMENTS (*from, node, nextnode, elem)) | |
1183 | { | |
1184 | listnode_add (*to, elem); | |
1185 | list_delete_node (*from, node); | |
1186 | } | |
1187 | } | |
1188 | else | |
1189 | { | |
1190 | list_free (*to); | |
1191 | *to = *from; | |
1192 | *from = NULL; | |
1193 | } | |
f390d2c7 | 1194 | } |
1195 | else if (!FRAG_NEEDED (lsp->pdu, frag_thold, tlvsize + 2)) | |
1196 | { | |
1197 | /* fit all we can */ | |
1198 | count = FRAG_THOLD (lsp->pdu, frag_thold) - 2 - | |
1199 | (STREAM_SIZE (lsp->pdu) - STREAM_REMAIN (lsp->pdu)); | |
3f045a08 JB |
1200 | count = count / tlvsize; |
1201 | if (count > (int)listcount (*from)) | |
1202 | count = listcount (*from); | |
f390d2c7 | 1203 | for (i = 0; i < count; i++) |
1204 | { | |
1eb8ef25 | 1205 | listnode_add (*to, listgetdata (listhead (*from))); |
1206 | listnode_delete (*from, listgetdata (listhead (*from))); | |
f390d2c7 | 1207 | } |
1208 | tlv_build_func (*to, lsp->pdu); | |
1209 | } | |
9985f83c | 1210 | lsp->lsp_header->pdu_len = htons (stream_get_endp (lsp->pdu)); |
eb5d44eb | 1211 | return; |
1212 | } | |
1213 | ||
3f045a08 JB |
1214 | static u_int16_t |
1215 | lsp_rem_lifetime (struct isis_area *area, int level) | |
1216 | { | |
1217 | u_int16_t rem_lifetime; | |
1218 | ||
1219 | /* Add jitter to configured LSP lifetime */ | |
1220 | rem_lifetime = isis_jitter (area->max_lsp_lifetime[level - 1], | |
1221 | MAX_AGE_JITTER); | |
1222 | ||
1223 | /* No jitter if the max refresh will be less than configure gen interval */ | |
f3d2b281 CF |
1224 | /* N.B. this calucation is acceptable since rem_lifetime is in [332,65535] at |
1225 | * this point */ | |
3f045a08 JB |
1226 | if (area->lsp_gen_interval[level - 1] > (rem_lifetime - 300)) |
1227 | rem_lifetime = area->max_lsp_lifetime[level - 1]; | |
1228 | ||
1229 | return rem_lifetime; | |
1230 | } | |
1231 | ||
1232 | static u_int16_t | |
1233 | lsp_refresh_time (struct isis_lsp *lsp, u_int16_t rem_lifetime) | |
1234 | { | |
1235 | struct isis_area *area = lsp->area; | |
1236 | int level = lsp->level; | |
1237 | u_int16_t refresh_time; | |
1238 | ||
1239 | /* Add jitter to LSP refresh time */ | |
1240 | refresh_time = isis_jitter (area->lsp_refresh[level - 1], | |
1241 | MAX_LSP_GEN_JITTER); | |
1242 | ||
1243 | /* RFC 4444 : make sure the refresh time is at least less than 300 | |
1244 | * of the remaining lifetime and more than gen interval */ | |
1245 | if (refresh_time <= area->lsp_gen_interval[level - 1] || | |
1246 | refresh_time > (rem_lifetime - 300)) | |
1247 | refresh_time = rem_lifetime - 300; | |
1248 | ||
f3d2b281 CF |
1249 | /* In cornercases, refresh_time might be <= lsp_gen_interval, however |
1250 | * we accept this violation to satisfy refresh_time <= rem_lifetime - 300 */ | |
3f045a08 JB |
1251 | |
1252 | return refresh_time; | |
1253 | } | |
1254 | ||
92365889 | 1255 | static struct isis_lsp * |
f390d2c7 | 1256 | lsp_next_frag (u_char frag_num, struct isis_lsp *lsp0, struct isis_area *area, |
1257 | int level) | |
eb5d44eb | 1258 | { |
1259 | struct isis_lsp *lsp; | |
f390d2c7 | 1260 | u_char frag_id[ISIS_SYS_ID_LEN + 2]; |
1261 | ||
eb5d44eb | 1262 | memcpy (frag_id, lsp0->lsp_header->lsp_id, ISIS_SYS_ID_LEN + 1); |
1263 | LSP_FRAGMENT (frag_id) = frag_num; | |
3f045a08 | 1264 | /* FIXME add authentication TLV for fragment LSPs */ |
eb5d44eb | 1265 | lsp = lsp_search (frag_id, area->lspdb[level - 1]); |
f390d2c7 | 1266 | if (lsp) |
1267 | { | |
3f045a08 | 1268 | /* Clear the TLVs */ |
f390d2c7 | 1269 | lsp_clear_data (lsp); |
f390d2c7 | 1270 | return lsp; |
eb5d44eb | 1271 | } |
b20ccb3a | 1272 | lsp = lsp_new (area, frag_id, ntohs(lsp0->lsp_header->rem_lifetime), 0, |
7ed55a41 AN |
1273 | lsp_bits_generate (level, area->overload_bit, |
1274 | area->attached_bit), 0, level); | |
3f045a08 | 1275 | lsp->area = area; |
eb5d44eb | 1276 | lsp->own_lsp = 1; |
f390d2c7 | 1277 | lsp_insert (lsp, area->lspdb[level - 1]); |
eb5d44eb | 1278 | listnode_add (lsp0->lspu.frags, lsp); |
1279 | lsp->lspu.zero_lsp = lsp0; | |
eb5d44eb | 1280 | return lsp; |
1281 | } | |
1282 | ||
f3ccedaa CF |
1283 | static void |
1284 | lsp_build_ext_reach_ipv4(struct isis_lsp *lsp, struct isis_area *area, | |
1285 | struct tlvs *tlv_data) | |
1286 | { | |
1287 | struct route_table *er_table; | |
1288 | struct route_node *rn; | |
1289 | struct prefix_ipv4 *ipv4; | |
1290 | struct isis_ext_info *info; | |
1291 | struct ipv4_reachability *ipreach; | |
1292 | struct te_ipv4_reachability *te_ipreach; | |
1293 | ||
1294 | er_table = get_ext_reach(area, AF_INET, lsp->level); | |
1295 | if (!er_table) | |
1296 | return; | |
1297 | ||
1298 | for (rn = route_top(er_table); rn; rn = route_next(rn)) | |
1299 | { | |
1300 | if (!rn->info) | |
1301 | continue; | |
1302 | ||
1303 | ipv4 = (struct prefix_ipv4*)&rn->p; | |
1304 | info = rn->info; | |
1305 | if (area->oldmetric) | |
1306 | { | |
1307 | if (tlv_data->ipv4_ext_reachs == NULL) | |
1308 | { | |
1309 | tlv_data->ipv4_ext_reachs = list_new(); | |
1310 | tlv_data->ipv4_ext_reachs->del = free_tlv; | |
1311 | } | |
1312 | ipreach = XMALLOC(MTYPE_ISIS_TLV, sizeof(*ipreach)); | |
1313 | ||
1314 | ipreach->prefix.s_addr = ipv4->prefix.s_addr; | |
1315 | masklen2ip(ipv4->prefixlen, &ipreach->mask); | |
1316 | ipreach->prefix.s_addr &= ipreach->mask.s_addr; | |
1317 | ||
1318 | if ((info->metric & 0x3f) != info->metric) | |
1319 | ipreach->metrics.metric_default = 0x3f; | |
1320 | else | |
1321 | ipreach->metrics.metric_default = info->metric; | |
1322 | ipreach->metrics.metric_expense = METRICS_UNSUPPORTED; | |
1323 | ipreach->metrics.metric_error = METRICS_UNSUPPORTED; | |
1324 | ipreach->metrics.metric_delay = METRICS_UNSUPPORTED; | |
1325 | listnode_add(tlv_data->ipv4_ext_reachs, ipreach); | |
1326 | } | |
1327 | if (area->newmetric) | |
1328 | { | |
1329 | if (tlv_data->te_ipv4_reachs == NULL) | |
1330 | { | |
1331 | tlv_data->te_ipv4_reachs = list_new(); | |
1332 | tlv_data->te_ipv4_reachs->del = free_tlv; | |
1333 | } | |
1334 | te_ipreach = | |
1335 | XCALLOC(MTYPE_ISIS_TLV, | |
1336 | sizeof(*te_ipreach) - 1 + PSIZE(ipv4->prefixlen)); | |
1337 | if (info->metric > MAX_WIDE_PATH_METRIC) | |
1338 | te_ipreach->te_metric = htonl(MAX_WIDE_PATH_METRIC); | |
1339 | else | |
1340 | te_ipreach->te_metric = htonl(info->metric); | |
1341 | te_ipreach->control = ipv4->prefixlen & 0x3f; | |
1342 | memcpy(&te_ipreach->prefix_start, &ipv4->prefix.s_addr, | |
1343 | PSIZE(ipv4->prefixlen)); | |
1344 | listnode_add(tlv_data->te_ipv4_reachs, te_ipreach); | |
1345 | } | |
1346 | } | |
1347 | } | |
1348 | ||
c3ae3127 CF |
1349 | static struct list * |
1350 | tlv_get_ipv6_reach_list(struct isis_area *area, struct tlvs *tlv_data) | |
1351 | { | |
1352 | uint16_t mtid = isis_area_ipv6_topology(area); | |
1353 | if (mtid == ISIS_MT_IPV4_UNICAST) | |
1354 | { | |
1355 | if (!tlv_data->ipv6_reachs) | |
1356 | { | |
1357 | tlv_data->ipv6_reachs = list_new(); | |
1358 | tlv_data->ipv6_reachs->del = free_tlv; | |
1359 | } | |
1360 | return tlv_data->ipv6_reachs; | |
1361 | } | |
1362 | ||
1363 | struct tlv_mt_ipv6_reachs *reachs = tlvs_get_mt_ipv6_reachs(tlv_data, mtid); | |
1364 | return reachs->list; | |
1365 | } | |
1366 | ||
f3ccedaa CF |
1367 | static void |
1368 | lsp_build_ext_reach_ipv6(struct isis_lsp *lsp, struct isis_area *area, | |
1369 | struct tlvs *tlv_data) | |
1370 | { | |
1371 | struct route_table *er_table; | |
1372 | struct route_node *rn; | |
1373 | struct prefix_ipv6 *ipv6; | |
1374 | struct isis_ext_info *info; | |
1375 | struct ipv6_reachability *ip6reach; | |
c3ae3127 | 1376 | struct list *reach_list = NULL; |
f3ccedaa CF |
1377 | |
1378 | er_table = get_ext_reach(area, AF_INET6, lsp->level); | |
1379 | if (!er_table) | |
1380 | return; | |
1381 | ||
1382 | for (rn = route_top(er_table); rn; rn = route_next(rn)) | |
1383 | { | |
1384 | if (!rn->info) | |
1385 | continue; | |
1386 | ||
1387 | ipv6 = (struct prefix_ipv6*)&rn->p; | |
1388 | info = rn->info; | |
1389 | ||
c3ae3127 CF |
1390 | if (!reach_list) |
1391 | reach_list = tlv_get_ipv6_reach_list(area, tlv_data); | |
1392 | ||
f3ccedaa CF |
1393 | ip6reach = XCALLOC(MTYPE_ISIS_TLV, sizeof(*ip6reach)); |
1394 | if (info->metric > MAX_WIDE_PATH_METRIC) | |
1395 | ip6reach->metric = htonl(MAX_WIDE_PATH_METRIC); | |
1396 | else | |
1397 | ip6reach->metric = htonl(info->metric); | |
1398 | ip6reach->control_info = DISTRIBUTION_EXTERNAL; | |
1399 | ip6reach->prefix_len = ipv6->prefixlen; | |
1400 | memcpy(ip6reach->prefix, ipv6->prefix.s6_addr, sizeof(ip6reach->prefix)); | |
c3ae3127 | 1401 | listnode_add(reach_list, ip6reach); |
f3ccedaa CF |
1402 | } |
1403 | } | |
1404 | ||
1405 | static void | |
1406 | lsp_build_ext_reach (struct isis_lsp *lsp, struct isis_area *area, | |
1407 | struct tlvs *tlv_data) | |
1408 | { | |
1409 | lsp_build_ext_reach_ipv4(lsp, area, tlv_data); | |
1410 | lsp_build_ext_reach_ipv6(lsp, area, tlv_data); | |
1411 | } | |
1412 | ||
eb5d44eb | 1413 | /* |
1414 | * Builds the LSP data part. This func creates a new frag whenever | |
1415 | * area->lsp_frag_threshold is exceeded. | |
1416 | */ | |
92365889 | 1417 | static void |
3f045a08 | 1418 | lsp_build (struct isis_lsp *lsp, struct isis_area *area) |
eb5d44eb | 1419 | { |
1420 | struct is_neigh *is_neigh; | |
aa4376ec | 1421 | struct te_is_neigh *te_is_neigh; |
3fdb2dd9 | 1422 | struct listnode *node, *ipnode; |
eb5d44eb | 1423 | int level = lsp->level; |
1424 | struct isis_circuit *circuit; | |
1425 | struct prefix_ipv4 *ipv4; | |
1426 | struct ipv4_reachability *ipreach; | |
aa4376ec | 1427 | struct te_ipv4_reachability *te_ipreach; |
eb5d44eb | 1428 | struct isis_adjacency *nei; |
8c8829a6 | 1429 | struct prefix_ipv6 *ipv6, ip6prefix; |
c3ae3127 | 1430 | struct list *ipv6_reachs = NULL; |
eb5d44eb | 1431 | struct ipv6_reachability *ip6reach; |
eb5d44eb | 1432 | struct tlvs tlv_data; |
1433 | struct isis_lsp *lsp0 = lsp; | |
18a6dce6 | 1434 | struct in_addr *routerid; |
3f045a08 JB |
1435 | uint32_t expected = 0, found = 0; |
1436 | uint32_t metric; | |
1437 | u_char zero_id[ISIS_SYS_ID_LEN + 1]; | |
1438 | int retval = ISIS_OK; | |
14872644 CF |
1439 | char buf[BUFSIZ]; |
1440 | ||
1441 | lsp_debug("ISIS (%s): Constructing local system LSP for level %d", area->area_tag, level); | |
3f045a08 JB |
1442 | |
1443 | /* | |
1444 | * Building the zero lsp | |
1445 | */ | |
1446 | memset (zero_id, 0, ISIS_SYS_ID_LEN + 1); | |
1447 | ||
1448 | /* Reset stream endp. Stream is always there and on every LSP refresh only | |
1449 | * TLV part of it is overwritten. So we must seek past header we will not | |
1450 | * touch. */ | |
1451 | stream_reset (lsp->pdu); | |
1452 | stream_forward_endp (lsp->pdu, ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); | |
1453 | ||
1454 | /* | |
1455 | * Add the authentication info if its present | |
1456 | */ | |
1457 | lsp_auth_add (lsp); | |
eb5d44eb | 1458 | |
1459 | /* | |
1460 | * First add the tlvs related to area | |
1461 | */ | |
f390d2c7 | 1462 | |
eb5d44eb | 1463 | /* Area addresses */ |
1464 | if (lsp->tlv_data.area_addrs == NULL) | |
1465 | lsp->tlv_data.area_addrs = list_new (); | |
1466 | list_add_list (lsp->tlv_data.area_addrs, area->area_addrs); | |
3f045a08 JB |
1467 | if (listcount (lsp->tlv_data.area_addrs) > 0) |
1468 | tlv_add_area_addrs (lsp->tlv_data.area_addrs, lsp->pdu); | |
1469 | ||
eb5d44eb | 1470 | /* Protocols Supported */ |
56c1f7d8 | 1471 | if (area->ip_circuits > 0 || area->ipv6_circuits > 0) |
eb5d44eb | 1472 | { |
aac372f4 | 1473 | lsp->tlv_data.nlpids = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct nlpids)); |
eb5d44eb | 1474 | lsp->tlv_data.nlpids->count = 0; |
f390d2c7 | 1475 | if (area->ip_circuits > 0) |
1476 | { | |
14872644 | 1477 | lsp_debug("ISIS (%s): Found IPv4 circuit, adding IPv4 to NLPIDs", area->area_tag); |
f390d2c7 | 1478 | lsp->tlv_data.nlpids->count++; |
1479 | lsp->tlv_data.nlpids->nlpids[0] = NLPID_IP; | |
1480 | } | |
f390d2c7 | 1481 | if (area->ipv6_circuits > 0) |
1482 | { | |
14872644 | 1483 | lsp_debug("ISIS (%s): Found IPv6 circuit, adding IPv6 to NLPIDs", area->area_tag); |
f390d2c7 | 1484 | lsp->tlv_data.nlpids->count++; |
1485 | lsp->tlv_data.nlpids->nlpids[lsp->tlv_data.nlpids->count - 1] = | |
1486 | NLPID_IPV6; | |
1487 | } | |
3f045a08 | 1488 | tlv_add_nlpid (lsp->tlv_data.nlpids, lsp->pdu); |
eb5d44eb | 1489 | } |
3f045a08 | 1490 | |
99894f9a CF |
1491 | if (area_is_mt(area)) |
1492 | { | |
1493 | lsp_debug("ISIS (%s): Adding MT router tlv...", area->area_tag); | |
1494 | lsp->tlv_data.mt_router_info = list_new(); | |
1495 | lsp->tlv_data.mt_router_info->del = free_tlv; | |
1496 | ||
1497 | struct isis_area_mt_setting **mt_settings; | |
1498 | unsigned int mt_count; | |
1499 | ||
1500 | mt_settings = area_mt_settings(area, &mt_count); | |
1501 | for (unsigned int i = 0; i < mt_count; i++) | |
1502 | { | |
1503 | struct mt_router_info *info; | |
1504 | ||
1505 | info = XCALLOC(MTYPE_ISIS_TLV, sizeof(*info)); | |
1506 | info->mtid = mt_settings[i]->mtid; | |
1507 | info->overload = mt_settings[i]->overload; | |
1508 | listnode_add(lsp->tlv_data.mt_router_info, info); | |
1509 | lsp_debug("ISIS (%s): MT %s", area->area_tag, isis_mtid2str(info->mtid)); | |
1510 | } | |
1511 | tlv_add_mt_router_info (lsp->tlv_data.mt_router_info, lsp->pdu); | |
1512 | } | |
1513 | else | |
1514 | { | |
1515 | lsp_debug("ISIS (%s): Not adding MT router tlv (disabled)", area->area_tag); | |
1516 | } | |
eb5d44eb | 1517 | /* Dynamic Hostname */ |
f390d2c7 | 1518 | if (area->dynhostname) |
1519 | { | |
e316f9ab CF |
1520 | const char *hostname = unix_hostname(); |
1521 | size_t hostname_len = strlen(hostname); | |
1522 | ||
f390d2c7 | 1523 | lsp->tlv_data.hostname = XMALLOC (MTYPE_ISIS_TLV, |
1524 | sizeof (struct hostname)); | |
9e867fe6 | 1525 | |
e316f9ab CF |
1526 | strncpy((char *)lsp->tlv_data.hostname->name, hostname, |
1527 | sizeof(lsp->tlv_data.hostname->name)); | |
1528 | if (hostname_len <= MAX_TLV_LEN) | |
1529 | lsp->tlv_data.hostname->namelen = hostname_len; | |
1530 | else | |
1531 | lsp->tlv_data.hostname->namelen = MAX_TLV_LEN; | |
1532 | ||
14872644 CF |
1533 | lsp_debug("ISIS (%s): Adding dynamic hostname '%.*s'", area->area_tag, |
1534 | lsp->tlv_data.hostname->namelen, lsp->tlv_data.hostname->name); | |
3f045a08 | 1535 | tlv_add_dynamic_hostname (lsp->tlv_data.hostname, lsp->pdu); |
f390d2c7 | 1536 | } |
14872644 CF |
1537 | else |
1538 | { | |
1539 | lsp_debug("ISIS (%s): Not adding dynamic hostname (disabled)", area->area_tag); | |
1540 | } | |
eb5d44eb | 1541 | |
81ad8f6a | 1542 | /* IPv4 address and TE router ID TLVs. In case of the first one we don't |
1543 | * follow "C" vendor, but "J" vendor behavior - one IPv4 address is put into | |
1544 | * LSP and this address is same as router id. */ | |
3f045a08 | 1545 | if (isis->router_id != 0) |
18a6dce6 | 1546 | { |
14872644 CF |
1547 | inet_ntop(AF_INET, &isis->router_id, buf, sizeof(buf)); |
1548 | lsp_debug("ISIS (%s): Adding router ID %s as IPv4 tlv.", area->area_tag, buf); | |
18a6dce6 | 1549 | if (lsp->tlv_data.ipv4_addrs == NULL) |
be7d65d9 | 1550 | { |
1551 | lsp->tlv_data.ipv4_addrs = list_new (); | |
1552 | lsp->tlv_data.ipv4_addrs->del = free_tlv; | |
1553 | } | |
18a6dce6 | 1554 | |
1555 | routerid = XMALLOC (MTYPE_ISIS_TLV, sizeof (struct in_addr)); | |
3f045a08 | 1556 | routerid->s_addr = isis->router_id; |
18a6dce6 | 1557 | listnode_add (lsp->tlv_data.ipv4_addrs, routerid); |
81ad8f6a | 1558 | tlv_add_in_addr (routerid, lsp->pdu, IPV4_ADDR); |
18a6dce6 | 1559 | |
81ad8f6a | 1560 | /* Exactly same data is put into TE router ID TLV, but only if new style |
1561 | * TLV's are in use. */ | |
1562 | if (area->newmetric) | |
1563 | { | |
14872644 | 1564 | lsp_debug("ISIS (%s): Adding router ID also as TE router ID tlv.", area->area_tag); |
81ad8f6a | 1565 | lsp->tlv_data.router_id = XMALLOC (MTYPE_ISIS_TLV, |
1566 | sizeof (struct in_addr)); | |
3f045a08 JB |
1567 | lsp->tlv_data.router_id->id.s_addr = isis->router_id; |
1568 | tlv_add_in_addr (&lsp->tlv_data.router_id->id, lsp->pdu, | |
1569 | TE_ROUTER_ID); | |
81ad8f6a | 1570 | } |
18a6dce6 | 1571 | } |
14872644 CF |
1572 | else |
1573 | { | |
1574 | lsp_debug("ISIS (%s): Router ID is unset. Not adding tlv.", area->area_tag); | |
1575 | } | |
f1082d19 | 1576 | |
81ad8f6a | 1577 | memset (&tlv_data, 0, sizeof (struct tlvs)); |
1578 | ||
14872644 CF |
1579 | lsp_debug("ISIS (%s): Adding circuit specific information.", area->area_tag); |
1580 | ||
eb5d44eb | 1581 | /* |
1582 | * Then build lists of tlvs related to circuits | |
1583 | */ | |
3fdb2dd9 | 1584 | for (ALL_LIST_ELEMENTS_RO (area->circuit_list, node, circuit)) |
f390d2c7 | 1585 | { |
14872644 CF |
1586 | if (!circuit->interface) |
1587 | lsp_debug("ISIS (%s): Processing %s circuit %p with unknown interface", | |
1588 | area->area_tag, circuit_type2string(circuit->circ_type), circuit); | |
1589 | else | |
1590 | lsp_debug("ISIS (%s): Processing %s circuit %s", | |
1591 | area->area_tag, circuit_type2string(circuit->circ_type), circuit->interface->name); | |
1592 | ||
f390d2c7 | 1593 | if (circuit->state != C_STATE_UP) |
14872644 CF |
1594 | { |
1595 | lsp_debug("ISIS (%s): Circuit is not up, ignoring.", area->area_tag); | |
1596 | continue; | |
1597 | } | |
eb5d44eb | 1598 | |
f390d2c7 | 1599 | /* |
1600 | * Add IPv4 internal reachability of this circuit | |
1601 | */ | |
1602 | if (circuit->ip_router && circuit->ip_addrs && | |
1603 | circuit->ip_addrs->count > 0) | |
1604 | { | |
14872644 | 1605 | lsp_debug("ISIS (%s): Circuit has IPv4 active, adding respective TLVs.", area->area_tag); |
aa4376ec | 1606 | if (area->oldmetric) |
f390d2c7 | 1607 | { |
aa4376ec | 1608 | if (tlv_data.ipv4_int_reachs == NULL) |
1609 | { | |
1610 | tlv_data.ipv4_int_reachs = list_new (); | |
1611 | tlv_data.ipv4_int_reachs->del = free_tlv; | |
1612 | } | |
3fdb2dd9 | 1613 | for (ALL_LIST_ELEMENTS_RO (circuit->ip_addrs, ipnode, ipv4)) |
aa4376ec | 1614 | { |
1615 | ipreach = | |
1616 | XMALLOC (MTYPE_ISIS_TLV, sizeof (struct ipv4_reachability)); | |
47a928fb DL |
1617 | ipreach->metrics.metric_default = circuit->metric[level - 1]; |
1618 | ipreach->metrics.metric_expense = METRICS_UNSUPPORTED; | |
1619 | ipreach->metrics.metric_error = METRICS_UNSUPPORTED; | |
1620 | ipreach->metrics.metric_delay = METRICS_UNSUPPORTED; | |
aa4376ec | 1621 | masklen2ip (ipv4->prefixlen, &ipreach->mask); |
1622 | ipreach->prefix.s_addr = ((ipreach->mask.s_addr) & | |
1623 | (ipv4->prefix.s_addr)); | |
14872644 CF |
1624 | inet_ntop(AF_INET, &ipreach->prefix.s_addr, buf, sizeof(buf)); |
1625 | lsp_debug("ISIS (%s): Adding old-style IP reachability for %s/%d", | |
1626 | area->area_tag, buf, ipv4->prefixlen); | |
aa4376ec | 1627 | listnode_add (tlv_data.ipv4_int_reachs, ipreach); |
1628 | } | |
f390d2c7 | 1629 | } |
aa4376ec | 1630 | if (area->newmetric) |
f390d2c7 | 1631 | { |
aa4376ec | 1632 | if (tlv_data.te_ipv4_reachs == NULL) |
1633 | { | |
1634 | tlv_data.te_ipv4_reachs = list_new (); | |
1635 | tlv_data.te_ipv4_reachs->del = free_tlv; | |
1636 | } | |
3fdb2dd9 | 1637 | for (ALL_LIST_ELEMENTS_RO (circuit->ip_addrs, ipnode, ipv4)) |
aa4376ec | 1638 | { |
1639 | /* FIXME All this assumes that we have no sub TLVs. */ | |
1640 | te_ipreach = XCALLOC (MTYPE_ISIS_TLV, | |
1641 | sizeof (struct te_ipv4_reachability) + | |
1642 | ((ipv4->prefixlen + 7)/8) - 1); | |
309ddb19 | 1643 | |
1644 | if (area->oldmetric) | |
47a928fb | 1645 | te_ipreach->te_metric = htonl (circuit->metric[level - 1]); |
309ddb19 | 1646 | else |
1647 | te_ipreach->te_metric = htonl (circuit->te_metric[level - 1]); | |
1648 | ||
aa4376ec | 1649 | te_ipreach->control = (ipv4->prefixlen & 0x3F); |
1650 | memcpy (&te_ipreach->prefix_start, &ipv4->prefix.s_addr, | |
1651 | (ipv4->prefixlen + 7)/8); | |
14872644 CF |
1652 | inet_ntop(AF_INET, &ipv4->prefix.s_addr, buf, sizeof(buf)); |
1653 | lsp_debug("ISIS (%s): Adding te-style IP reachability for %s/%d", | |
1654 | area->area_tag, buf, ipv4->prefixlen); | |
aa4376ec | 1655 | listnode_add (tlv_data.te_ipv4_reachs, te_ipreach); |
1656 | } | |
f390d2c7 | 1657 | } |
f390d2c7 | 1658 | } |
3f045a08 | 1659 | |
f390d2c7 | 1660 | /* |
1661 | * Add IPv6 reachability of this circuit | |
1662 | */ | |
1663 | if (circuit->ipv6_router && circuit->ipv6_non_link && | |
1664 | circuit->ipv6_non_link->count > 0) | |
1665 | { | |
c3ae3127 CF |
1666 | if (!ipv6_reachs) |
1667 | ipv6_reachs = tlv_get_ipv6_reach_list(area, &tlv_data); | |
f390d2c7 | 1668 | |
3fdb2dd9 | 1669 | for (ALL_LIST_ELEMENTS_RO (circuit->ipv6_non_link, ipnode, ipv6)) |
f390d2c7 | 1670 | { |
f390d2c7 | 1671 | ip6reach = |
aac372f4 | 1672 | XCALLOC (MTYPE_ISIS_TLV, sizeof (struct ipv6_reachability)); |
309ddb19 | 1673 | |
1674 | if (area->oldmetric) | |
1675 | ip6reach->metric = | |
47a928fb | 1676 | htonl (circuit->metric[level - 1]); |
309ddb19 | 1677 | else |
1678 | ip6reach->metric = htonl (circuit->te_metric[level - 1]); | |
1679 | ||
f390d2c7 | 1680 | ip6reach->control_info = 0; |
1681 | ip6reach->prefix_len = ipv6->prefixlen; | |
8c8829a6 CF |
1682 | memcpy(&ip6prefix, ipv6, sizeof(ip6prefix)); |
1683 | apply_mask_ipv6(&ip6prefix); | |
14872644 | 1684 | |
8c8829a6 | 1685 | inet_ntop(AF_INET6, &ip6prefix.prefix.s6_addr, buf, sizeof(buf)); |
14872644 CF |
1686 | lsp_debug("ISIS (%s): Adding IPv6 reachability for %s/%d", |
1687 | area->area_tag, buf, ipv6->prefixlen); | |
1688 | ||
8c8829a6 | 1689 | memcpy (ip6reach->prefix, ip6prefix.prefix.s6_addr, |
6785157b | 1690 | sizeof (ip6reach->prefix)); |
c3ae3127 | 1691 | listnode_add (ipv6_reachs, ip6reach); |
f390d2c7 | 1692 | } |
1693 | } | |
f390d2c7 | 1694 | |
1695 | switch (circuit->circ_type) | |
1696 | { | |
1697 | case CIRCUIT_T_BROADCAST: | |
3f045a08 | 1698 | if (level & circuit->is_type) |
f390d2c7 | 1699 | { |
aa4376ec | 1700 | if (area->oldmetric) |
f390d2c7 | 1701 | { |
aa4376ec | 1702 | if (tlv_data.is_neighs == NULL) |
1703 | { | |
1704 | tlv_data.is_neighs = list_new (); | |
1705 | tlv_data.is_neighs->del = free_tlv; | |
1706 | } | |
1707 | is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct is_neigh)); | |
3f045a08 | 1708 | if (level == IS_LEVEL_1) |
aa4376ec | 1709 | memcpy (is_neigh->neigh_id, |
1710 | circuit->u.bc.l1_desig_is, ISIS_SYS_ID_LEN + 1); | |
1711 | else | |
1712 | memcpy (is_neigh->neigh_id, | |
1713 | circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1); | |
47a928fb DL |
1714 | is_neigh->metrics.metric_default = circuit->metric[level - 1]; |
1715 | is_neigh->metrics.metric_expense = METRICS_UNSUPPORTED; | |
1716 | is_neigh->metrics.metric_error = METRICS_UNSUPPORTED; | |
1717 | is_neigh->metrics.metric_delay = METRICS_UNSUPPORTED; | |
3f045a08 JB |
1718 | if (!memcmp (is_neigh->neigh_id, zero_id, |
1719 | ISIS_SYS_ID_LEN + 1)) | |
14872644 CF |
1720 | { |
1721 | XFREE (MTYPE_ISIS_TLV, is_neigh); | |
1722 | lsp_debug("ISIS (%s): No DIS for circuit, not adding old-style IS neighbor.", | |
1723 | area->area_tag); | |
1724 | } | |
3f045a08 | 1725 | else |
14872644 CF |
1726 | { |
1727 | listnode_add (tlv_data.is_neighs, is_neigh); | |
1728 | lsp_debug("ISIS (%s): Adding DIS %s.%02x as old-style neighbor", | |
1729 | area->area_tag, sysid_print(is_neigh->neigh_id), | |
1730 | LSP_PSEUDO_ID(is_neigh->neigh_id)); | |
1731 | } | |
f390d2c7 | 1732 | } |
aa4376ec | 1733 | if (area->newmetric) |
1734 | { | |
aa4376ec | 1735 | if (tlv_data.te_is_neighs == NULL) |
1736 | { | |
1737 | tlv_data.te_is_neighs = list_new (); | |
1738 | tlv_data.te_is_neighs->del = free_tlv; | |
1739 | } | |
1740 | te_is_neigh = XCALLOC (MTYPE_ISIS_TLV, | |
1741 | sizeof (struct te_is_neigh)); | |
3f045a08 | 1742 | if (level == IS_LEVEL_1) |
aa4376ec | 1743 | memcpy (te_is_neigh->neigh_id, |
1744 | circuit->u.bc.l1_desig_is, ISIS_SYS_ID_LEN + 1); | |
1745 | else | |
1746 | memcpy (te_is_neigh->neigh_id, | |
1747 | circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1); | |
309ddb19 | 1748 | if (area->oldmetric) |
47a928fb | 1749 | metric = circuit->metric[level - 1]; |
309ddb19 | 1750 | else |
3f045a08 JB |
1751 | metric = circuit->te_metric[level - 1]; |
1752 | SET_TE_METRIC(te_is_neigh, metric); | |
1753 | if (!memcmp (te_is_neigh->neigh_id, zero_id, | |
1754 | ISIS_SYS_ID_LEN + 1)) | |
14872644 CF |
1755 | { |
1756 | XFREE (MTYPE_ISIS_TLV, te_is_neigh); | |
1757 | lsp_debug("ISIS (%s): No DIS for circuit, not adding te-style IS neighbor.", | |
1758 | area->area_tag); | |
1759 | } | |
3f045a08 | 1760 | else |
14872644 | 1761 | { |
f8c06e2c OD |
1762 | /* Check if MPLS_TE is activate */ |
1763 | if (IS_MPLS_TE(isisMplsTE) && HAS_LINK_PARAMS(circuit->interface)) | |
1764 | /* Add SubTLVs & Adjust real size of SubTLVs */ | |
1765 | te_is_neigh->sub_tlvs_length = add_te_subtlvs(te_is_neigh->sub_tlvs, circuit->mtc); | |
1766 | else | |
1767 | /* Or keep only TE metric with no SubTLVs if MPLS_TE is off */ | |
1768 | te_is_neigh->sub_tlvs_length = 0; | |
1769 | ||
206f4aae CF |
1770 | tlvs_add_mt_bcast(&tlv_data, circuit, level, te_is_neigh); |
1771 | XFREE(MTYPE_ISIS_TLV, te_is_neigh); | |
14872644 | 1772 | } |
aa4376ec | 1773 | } |
f390d2c7 | 1774 | } |
14872644 CF |
1775 | else |
1776 | { | |
1777 | lsp_debug("ISIS (%s): Circuit is not active for current level. Not adding IS neighbors", | |
1778 | area->area_tag); | |
1779 | } | |
f390d2c7 | 1780 | break; |
1781 | case CIRCUIT_T_P2P: | |
1782 | nei = circuit->u.p2p.neighbor; | |
1783 | if (nei && (level & nei->circuit_t)) | |
1784 | { | |
aa4376ec | 1785 | if (area->oldmetric) |
f390d2c7 | 1786 | { |
aa4376ec | 1787 | if (tlv_data.is_neighs == NULL) |
1788 | { | |
1789 | tlv_data.is_neighs = list_new (); | |
1790 | tlv_data.is_neighs->del = free_tlv; | |
1791 | } | |
1792 | is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct is_neigh)); | |
1793 | memcpy (is_neigh->neigh_id, nei->sysid, ISIS_SYS_ID_LEN); | |
47a928fb DL |
1794 | is_neigh->metrics.metric_default = circuit->metric[level - 1]; |
1795 | is_neigh->metrics.metric_expense = METRICS_UNSUPPORTED; | |
1796 | is_neigh->metrics.metric_error = METRICS_UNSUPPORTED; | |
1797 | is_neigh->metrics.metric_delay = METRICS_UNSUPPORTED; | |
aa4376ec | 1798 | listnode_add (tlv_data.is_neighs, is_neigh); |
14872644 CF |
1799 | lsp_debug("ISIS (%s): Adding old-style is reach for %s", area->area_tag, |
1800 | sysid_print(is_neigh->neigh_id)); | |
aa4376ec | 1801 | } |
1802 | if (area->newmetric) | |
1803 | { | |
1804 | uint32_t metric; | |
1805 | ||
1806 | if (tlv_data.te_is_neighs == NULL) | |
1807 | { | |
1808 | tlv_data.te_is_neighs = list_new (); | |
1809 | tlv_data.te_is_neighs->del = free_tlv; | |
1810 | } | |
1811 | te_is_neigh = XCALLOC (MTYPE_ISIS_TLV, | |
1812 | sizeof (struct te_is_neigh)); | |
1813 | memcpy (te_is_neigh->neigh_id, nei->sysid, ISIS_SYS_ID_LEN); | |
3f045a08 JB |
1814 | metric = circuit->te_metric[level - 1]; |
1815 | SET_TE_METRIC(te_is_neigh, metric); | |
f8c06e2c OD |
1816 | /* Check if MPLS_TE is activate */ |
1817 | if (IS_MPLS_TE(isisMplsTE) && HAS_LINK_PARAMS(circuit->interface)) | |
1818 | /* Update Local and Remote IP address for MPLS TE circuit parameters */ | |
1819 | /* NOTE sure that it is the pertinent place for that updates */ | |
1820 | /* Local IP address could be updated in isis_circuit.c - isis_circuit_add_addr() */ | |
1821 | /* But, where update remote IP address ? in isis_pdu.c - process_p2p_hello() ? */ | |
1822 | ||
1823 | /* Add SubTLVs & Adjust real size of SubTLVs */ | |
1824 | te_is_neigh->sub_tlvs_length = add_te_subtlvs(te_is_neigh->sub_tlvs, circuit->mtc); | |
1825 | else | |
1826 | /* Or keep only TE metric with no SubTLVs if MPLS_TE is off */ | |
1827 | te_is_neigh->sub_tlvs_length = 0; | |
206f4aae CF |
1828 | |
1829 | tlvs_add_mt_p2p(&tlv_data, circuit, te_is_neigh); | |
1830 | XFREE(MTYPE_ISIS_TLV, te_is_neigh); | |
f390d2c7 | 1831 | } |
f390d2c7 | 1832 | } |
14872644 CF |
1833 | else |
1834 | { | |
1835 | lsp_debug("ISIS (%s): No adjacency for given level on this circuit. Not adding IS neighbors", | |
1836 | area->area_tag); | |
1837 | } | |
f390d2c7 | 1838 | break; |
3f045a08 JB |
1839 | case CIRCUIT_T_LOOPBACK: |
1840 | break; | |
f390d2c7 | 1841 | default: |
1842 | zlog_warn ("lsp_area_create: unknown circuit type"); | |
1843 | } | |
eb5d44eb | 1844 | } |
eb5d44eb | 1845 | |
f3ccedaa CF |
1846 | lsp_build_ext_reach(lsp, area, &tlv_data); |
1847 | ||
14872644 CF |
1848 | lsp_debug("ISIS (%s): LSP construction is complete. Serializing...", area->area_tag); |
1849 | ||
f390d2c7 | 1850 | while (tlv_data.ipv4_int_reachs && listcount (tlv_data.ipv4_int_reachs)) |
1851 | { | |
1852 | if (lsp->tlv_data.ipv4_int_reachs == NULL) | |
1853 | lsp->tlv_data.ipv4_int_reachs = list_new (); | |
1854 | lsp_tlv_fit (lsp, &tlv_data.ipv4_int_reachs, | |
1855 | &lsp->tlv_data.ipv4_int_reachs, | |
1856 | IPV4_REACH_LEN, area->lsp_frag_threshold, | |
f3ccedaa | 1857 | tlv_add_ipv4_int_reachs); |
f390d2c7 | 1858 | if (tlv_data.ipv4_int_reachs && listcount (tlv_data.ipv4_int_reachs)) |
1859 | lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, | |
1860 | lsp0, area, level); | |
1861 | } | |
3f045a08 | 1862 | |
f3ccedaa CF |
1863 | while (tlv_data.ipv4_ext_reachs && listcount (tlv_data.ipv4_ext_reachs)) |
1864 | { | |
1865 | if (lsp->tlv_data.ipv4_ext_reachs == NULL) | |
1866 | lsp->tlv_data.ipv4_ext_reachs = list_new (); | |
1867 | lsp_tlv_fit (lsp, &tlv_data.ipv4_ext_reachs, | |
1868 | &lsp->tlv_data.ipv4_ext_reachs, | |
1869 | IPV4_REACH_LEN, area->lsp_frag_threshold, | |
1870 | tlv_add_ipv4_ext_reachs); | |
1871 | if (tlv_data.ipv4_ext_reachs && listcount (tlv_data.ipv4_ext_reachs)) | |
1872 | lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, | |
1873 | lsp0, area, level); | |
1874 | } | |
1875 | ||
aa4376ec | 1876 | while (tlv_data.te_ipv4_reachs && listcount (tlv_data.te_ipv4_reachs)) |
1877 | { | |
1878 | if (lsp->tlv_data.te_ipv4_reachs == NULL) | |
1879 | lsp->tlv_data.te_ipv4_reachs = list_new (); | |
c3ae3127 CF |
1880 | _lsp_tlv_fit (lsp, &tlv_data.te_ipv4_reachs, &lsp->tlv_data.te_ipv4_reachs, |
1881 | area->lsp_frag_threshold, tlv_add_te_ipv4_reachs, NULL); | |
aa4376ec | 1882 | if (tlv_data.te_ipv4_reachs && listcount (tlv_data.te_ipv4_reachs)) |
1883 | lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, | |
1884 | lsp0, area, level); | |
1885 | } | |
f390d2c7 | 1886 | |
c3ae3127 CF |
1887 | struct tlv_mt_ipv4_reachs *mt_ipv4_reachs; |
1888 | for (ALL_LIST_ELEMENTS_RO(tlv_data.mt_ipv4_reachs, node, mt_ipv4_reachs)) | |
1889 | { | |
1890 | while (mt_ipv4_reachs->list && listcount(mt_ipv4_reachs->list)) | |
1891 | { | |
1892 | struct tlv_mt_ipv4_reachs *frag_mt_ipv4_reachs; | |
1893 | ||
1894 | frag_mt_ipv4_reachs = tlvs_get_mt_ipv4_reachs(&lsp->tlv_data, mt_ipv4_reachs->mtid); | |
1895 | _lsp_tlv_fit (lsp, &mt_ipv4_reachs->list, &frag_mt_ipv4_reachs->list, | |
1896 | area->lsp_frag_threshold, tlv_add_te_ipv4_reachs, | |
1897 | &mt_ipv4_reachs->mtid); | |
1898 | if (mt_ipv4_reachs->list && listcount(mt_ipv4_reachs->list)) | |
1899 | lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, | |
1900 | lsp0, area, level); | |
1901 | } | |
1902 | } | |
1903 | ||
f390d2c7 | 1904 | while (tlv_data.ipv6_reachs && listcount (tlv_data.ipv6_reachs)) |
1905 | { | |
1906 | if (lsp->tlv_data.ipv6_reachs == NULL) | |
1907 | lsp->tlv_data.ipv6_reachs = list_new (); | |
c3ae3127 CF |
1908 | _lsp_tlv_fit (lsp, &tlv_data.ipv6_reachs, &lsp->tlv_data.ipv6_reachs, |
1909 | area->lsp_frag_threshold, tlv_add_ipv6_reachs, NULL); | |
f390d2c7 | 1910 | if (tlv_data.ipv6_reachs && listcount (tlv_data.ipv6_reachs)) |
1911 | lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, | |
1912 | lsp0, area, level); | |
1913 | } | |
f390d2c7 | 1914 | |
c3ae3127 CF |
1915 | struct tlv_mt_ipv6_reachs *mt_ipv6_reachs; |
1916 | for (ALL_LIST_ELEMENTS_RO(tlv_data.mt_ipv6_reachs, node, mt_ipv6_reachs)) | |
1917 | { | |
1918 | while (mt_ipv6_reachs->list && listcount(mt_ipv6_reachs->list)) | |
1919 | { | |
1920 | struct tlv_mt_ipv6_reachs *frag_mt_ipv6_reachs; | |
1921 | ||
1922 | frag_mt_ipv6_reachs = tlvs_get_mt_ipv6_reachs(&lsp->tlv_data, mt_ipv6_reachs->mtid); | |
1923 | _lsp_tlv_fit (lsp, &mt_ipv6_reachs->list, &frag_mt_ipv6_reachs->list, | |
1924 | area->lsp_frag_threshold, tlv_add_ipv6_reachs, | |
1925 | &mt_ipv6_reachs->mtid); | |
1926 | if (mt_ipv6_reachs->list && listcount(mt_ipv6_reachs->list)) | |
1927 | lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, | |
1928 | lsp0, area, level); | |
1929 | } | |
1930 | } | |
1931 | ||
f390d2c7 | 1932 | while (tlv_data.is_neighs && listcount (tlv_data.is_neighs)) |
1933 | { | |
1934 | if (lsp->tlv_data.is_neighs == NULL) | |
1935 | lsp->tlv_data.is_neighs = list_new (); | |
1936 | lsp_tlv_fit (lsp, &tlv_data.is_neighs, | |
1937 | &lsp->tlv_data.is_neighs, | |
1938 | IS_NEIGHBOURS_LEN, area->lsp_frag_threshold, | |
1939 | tlv_add_is_neighs); | |
1940 | if (tlv_data.is_neighs && listcount (tlv_data.is_neighs)) | |
1941 | lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, | |
1942 | lsp0, area, level); | |
1943 | } | |
eb5d44eb | 1944 | |
aa4376ec | 1945 | while (tlv_data.te_is_neighs && listcount (tlv_data.te_is_neighs)) |
1946 | { | |
1947 | if (lsp->tlv_data.te_is_neighs == NULL) | |
1948 | lsp->tlv_data.te_is_neighs = list_new (); | |
206f4aae CF |
1949 | _lsp_tlv_fit (lsp, &tlv_data.te_is_neighs, &lsp->tlv_data.te_is_neighs, |
1950 | area->lsp_frag_threshold, tlv_add_te_is_neighs, NULL); | |
aa4376ec | 1951 | if (tlv_data.te_is_neighs && listcount (tlv_data.te_is_neighs)) |
1952 | lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, | |
1953 | lsp0, area, level); | |
1954 | } | |
206f4aae CF |
1955 | |
1956 | struct tlv_mt_neighbors *mt_neighs; | |
1957 | for (ALL_LIST_ELEMENTS_RO(tlv_data.mt_is_neighs, node, mt_neighs)) | |
1958 | { | |
1959 | while (mt_neighs->list && listcount(mt_neighs->list)) | |
1960 | { | |
1961 | struct tlv_mt_neighbors *frag_mt_neighs; | |
1962 | ||
1963 | frag_mt_neighs = tlvs_get_mt_neighbors(&lsp->tlv_data, mt_neighs->mtid); | |
1964 | _lsp_tlv_fit (lsp, &mt_neighs->list, &frag_mt_neighs->list, | |
1965 | area->lsp_frag_threshold, tlv_add_te_is_neighs, | |
1966 | &mt_neighs->mtid); | |
1967 | if (mt_neighs->list && listcount(mt_neighs->list)) | |
1968 | lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, | |
1969 | lsp0, area, level); | |
1970 | } | |
1971 | } | |
1972 | ||
1973 | ||
3f045a08 | 1974 | lsp->lsp_header->pdu_len = htons (stream_get_endp (lsp->pdu)); |
aa4376ec | 1975 | |
1976 | free_tlvs (&tlv_data); | |
3f045a08 JB |
1977 | |
1978 | /* Validate the LSP */ | |
1979 | retval = parse_tlvs (area->area_tag, STREAM_DATA (lsp->pdu) + | |
1980 | ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN, | |
1981 | stream_get_endp (lsp->pdu) - | |
1982 | ISIS_FIXED_HDR_LEN - ISIS_LSP_HDR_LEN, | |
1983 | &expected, &found, &tlv_data, NULL); | |
1984 | assert (retval == ISIS_OK); | |
1985 | ||
eb5d44eb | 1986 | return; |
1987 | } | |
eb5d44eb | 1988 | |
1989 | /* | |
3f045a08 | 1990 | * 7.3.7 and 7.3.9 Generation on non-pseudonode LSPs |
eb5d44eb | 1991 | */ |
3f045a08 JB |
1992 | int |
1993 | lsp_generate (struct isis_area *area, int level) | |
f390d2c7 | 1994 | { |
eb5d44eb | 1995 | struct isis_lsp *oldlsp, *newlsp; |
1996 | u_int32_t seq_num = 0; | |
1997 | u_char lspid[ISIS_SYS_ID_LEN + 2]; | |
3f045a08 JB |
1998 | u_int16_t rem_lifetime, refresh_time; |
1999 | ||
2000 | if ((area == NULL) || (area->is_type & level) != level) | |
2001 | return ISIS_ERROR; | |
eb5d44eb | 2002 | |
2003 | memset (&lspid, 0, ISIS_SYS_ID_LEN + 2); | |
2004 | memcpy (&lspid, isis->sysid, ISIS_SYS_ID_LEN); | |
2005 | ||
2006 | /* only builds the lsp if the area shares the level */ | |
3f045a08 JB |
2007 | oldlsp = lsp_search (lspid, area->lspdb[level - 1]); |
2008 | if (oldlsp) | |
2009 | { | |
2010 | /* FIXME: we should actually initiate a purge */ | |
2011 | seq_num = ntohl (oldlsp->lsp_header->seq_num); | |
2012 | lsp_search_and_destroy (oldlsp->lsp_header->lsp_id, | |
2013 | area->lspdb[level - 1]); | |
2014 | } | |
2015 | rem_lifetime = lsp_rem_lifetime (area, level); | |
b20ccb3a | 2016 | newlsp = lsp_new (area, lspid, rem_lifetime, seq_num, |
7ed55a41 AN |
2017 | area->is_type | area->overload_bit | area->attached_bit, |
2018 | 0, level); | |
3f045a08 JB |
2019 | newlsp->area = area; |
2020 | newlsp->own_lsp = 1; | |
2021 | ||
2022 | lsp_insert (newlsp, area->lspdb[level - 1]); | |
2023 | /* build_lsp_data (newlsp, area); */ | |
2024 | lsp_build (newlsp, area); | |
2025 | /* time to calculate our checksum */ | |
2026 | lsp_seqnum_update (newlsp); | |
414766a1 | 2027 | newlsp->last_generated = time(NULL); |
3f045a08 JB |
2028 | lsp_set_all_srmflags (newlsp); |
2029 | ||
2030 | refresh_time = lsp_refresh_time (newlsp, rem_lifetime); | |
414766a1 | 2031 | |
3f045a08 | 2032 | THREAD_TIMER_OFF (area->t_lsp_refresh[level - 1]); |
414766a1 | 2033 | area->lsp_regenerate_pending[level - 1] = 0; |
3f045a08 | 2034 | if (level == IS_LEVEL_1) |
ffa2c898 QY |
2035 | thread_add_timer(master, lsp_l1_refresh, area, refresh_time, |
2036 | &area->t_lsp_refresh[level - 1]); | |
3f045a08 | 2037 | else if (level == IS_LEVEL_2) |
ffa2c898 QY |
2038 | thread_add_timer(master, lsp_l2_refresh, area, refresh_time, |
2039 | &area->t_lsp_refresh[level - 1]); | |
f390d2c7 | 2040 | |
3f045a08 | 2041 | if (isis->debugs & DEBUG_UPDATE_PACKETS) |
f390d2c7 | 2042 | { |
3f045a08 JB |
2043 | zlog_debug ("ISIS-Upd (%s): Building L%d LSP %s, len %d, " |
2044 | "seq 0x%08x, cksum 0x%04x, lifetime %us refresh %us", | |
2045 | area->area_tag, level, | |
2046 | rawlspid_print (newlsp->lsp_header->lsp_id), | |
2047 | ntohl (newlsp->lsp_header->pdu_len), | |
2048 | ntohl (newlsp->lsp_header->seq_num), | |
2049 | ntohs (newlsp->lsp_header->checksum), | |
2050 | ntohs (newlsp->lsp_header->rem_lifetime), | |
2051 | refresh_time); | |
f390d2c7 | 2052 | } |
414766a1 CF |
2053 | sched_debug("ISIS (%s): Built L%d LSP. Set triggered regenerate to non-pending.", |
2054 | area->area_tag, level); | |
eb5d44eb | 2055 | |
2056 | return ISIS_OK; | |
2057 | } | |
2058 | ||
2059 | /* | |
3f045a08 | 2060 | * Search own LSPs, update holding time and set SRM |
eb5d44eb | 2061 | */ |
92365889 | 2062 | static int |
3f045a08 | 2063 | lsp_regenerate (struct isis_area *area, int level) |
eb5d44eb | 2064 | { |
e8aca32f | 2065 | dict_t *lspdb; |
eb5d44eb | 2066 | struct isis_lsp *lsp, *frag; |
2067 | struct listnode *node; | |
2068 | u_char lspid[ISIS_SYS_ID_LEN + 2]; | |
3f045a08 JB |
2069 | u_int16_t rem_lifetime, refresh_time; |
2070 | ||
2071 | if ((area == NULL) || (area->is_type & level) != level) | |
2072 | return ISIS_ERROR; | |
eb5d44eb | 2073 | |
e8aca32f DL |
2074 | lspdb = area->lspdb[level - 1]; |
2075 | ||
eb5d44eb | 2076 | memset (lspid, 0, ISIS_SYS_ID_LEN + 2); |
2077 | memcpy (lspid, isis->sysid, ISIS_SYS_ID_LEN); | |
f390d2c7 | 2078 | |
eb5d44eb | 2079 | lsp = lsp_search (lspid, lspdb); |
eb5d44eb | 2080 | |
f390d2c7 | 2081 | if (!lsp) |
2082 | { | |
3f045a08 JB |
2083 | zlog_err ("ISIS-Upd (%s): lsp_regenerate: no L%d LSP found!", |
2084 | area->area_tag, level); | |
f390d2c7 | 2085 | return ISIS_ERROR; |
2086 | } | |
2087 | ||
2088 | lsp_clear_data (lsp); | |
3f045a08 | 2089 | lsp_build (lsp, area); |
7ed55a41 AN |
2090 | lsp->lsp_header->lsp_bits = lsp_bits_generate (level, area->overload_bit, |
2091 | area->attached_bit); | |
3f045a08 JB |
2092 | rem_lifetime = lsp_rem_lifetime (area, level); |
2093 | lsp->lsp_header->rem_lifetime = htons (rem_lifetime); | |
eb5d44eb | 2094 | lsp_seqnum_update (lsp); |
f390d2c7 | 2095 | |
3f045a08 JB |
2096 | lsp->last_generated = time (NULL); |
2097 | lsp_set_all_srmflags (lsp); | |
2098 | for (ALL_LIST_ELEMENTS_RO (lsp->lspu.frags, node, frag)) | |
f390d2c7 | 2099 | { |
e38e0df0 | 2100 | frag->lsp_header->lsp_bits = lsp_bits_generate (level, |
7ed55a41 AN |
2101 | area->overload_bit, |
2102 | area->attached_bit); | |
3f045a08 JB |
2103 | /* Set the lifetime values of all the fragments to the same value, |
2104 | * so that no fragment expires before the lsp is refreshed. | |
2105 | */ | |
2106 | frag->lsp_header->rem_lifetime = htons (rem_lifetime); | |
2107 | lsp_set_all_srmflags (frag); | |
f390d2c7 | 2108 | } |
eb5d44eb | 2109 | |
3f045a08 JB |
2110 | refresh_time = lsp_refresh_time (lsp, rem_lifetime); |
2111 | if (level == IS_LEVEL_1) | |
ffa2c898 QY |
2112 | thread_add_timer(master, lsp_l1_refresh, area, refresh_time, |
2113 | &area->t_lsp_refresh[level - 1]); | |
3f045a08 | 2114 | else if (level == IS_LEVEL_2) |
ffa2c898 QY |
2115 | thread_add_timer(master, lsp_l2_refresh, area, refresh_time, |
2116 | &area->t_lsp_refresh[level - 1]); | |
414766a1 | 2117 | area->lsp_regenerate_pending[level - 1] = 0; |
3f045a08 JB |
2118 | |
2119 | if (isis->debugs & DEBUG_UPDATE_PACKETS) | |
f390d2c7 | 2120 | { |
3f045a08 JB |
2121 | zlog_debug ("ISIS-Upd (%s): Refreshing our L%d LSP %s, len %d, " |
2122 | "seq 0x%08x, cksum 0x%04x, lifetime %us refresh %us", | |
2123 | area->area_tag, level, | |
2124 | rawlspid_print (lsp->lsp_header->lsp_id), | |
2125 | ntohl (lsp->lsp_header->pdu_len), | |
2126 | ntohl (lsp->lsp_header->seq_num), | |
2127 | ntohs (lsp->lsp_header->checksum), | |
2128 | ntohs (lsp->lsp_header->rem_lifetime), | |
2129 | refresh_time); | |
f390d2c7 | 2130 | } |
414766a1 CF |
2131 | sched_debug("ISIS (%s): Rebuilt L%d LSP. Set triggered regenerate to non-pending.", |
2132 | area->area_tag, level); | |
eb5d44eb | 2133 | |
eb5d44eb | 2134 | return ISIS_OK; |
2135 | } | |
2136 | ||
eb5d44eb | 2137 | /* |
3f045a08 | 2138 | * Something has changed or periodic refresh -> regenerate LSP |
eb5d44eb | 2139 | */ |
3f045a08 JB |
2140 | static int |
2141 | lsp_l1_refresh (struct thread *thread) | |
eb5d44eb | 2142 | { |
2143 | struct isis_area *area; | |
eb5d44eb | 2144 | |
2145 | area = THREAD_ARG (thread); | |
2146 | assert (area); | |
f390d2c7 | 2147 | |
eb5d44eb | 2148 | area->t_lsp_refresh[0] = NULL; |
3f045a08 | 2149 | area->lsp_regenerate_pending[0] = 0; |
eb5d44eb | 2150 | |
3f045a08 JB |
2151 | if ((area->is_type & IS_LEVEL_1) == 0) |
2152 | return ISIS_ERROR; | |
d70f99e1 | 2153 | |
414766a1 | 2154 | sched_debug("ISIS (%s): LSP L1 refresh timer expired. Refreshing LSP...", area->area_tag); |
3f045a08 | 2155 | return lsp_regenerate (area, IS_LEVEL_1); |
eb5d44eb | 2156 | } |
2157 | ||
3f045a08 JB |
2158 | static int |
2159 | lsp_l2_refresh (struct thread *thread) | |
eb5d44eb | 2160 | { |
2161 | struct isis_area *area; | |
eb5d44eb | 2162 | |
2163 | area = THREAD_ARG (thread); | |
2164 | assert (area); | |
f390d2c7 | 2165 | |
eb5d44eb | 2166 | area->t_lsp_refresh[1] = NULL; |
eb5d44eb | 2167 | area->lsp_regenerate_pending[1] = 0; |
f390d2c7 | 2168 | |
3f045a08 JB |
2169 | if ((area->is_type & IS_LEVEL_2) == 0) |
2170 | return ISIS_ERROR; | |
2171 | ||
414766a1 | 2172 | sched_debug("ISIS (%s): LSP L2 refresh timer expired. Refreshing LSP...", area->area_tag); |
3f045a08 | 2173 | return lsp_regenerate (area, IS_LEVEL_2); |
eb5d44eb | 2174 | } |
2175 | ||
f390d2c7 | 2176 | int |
3f045a08 | 2177 | lsp_regenerate_schedule (struct isis_area *area, int level, int all_pseudo) |
eb5d44eb | 2178 | { |
2179 | struct isis_lsp *lsp; | |
2180 | u_char id[ISIS_SYS_ID_LEN + 2]; | |
2181 | time_t now, diff; | |
414766a1 | 2182 | long timeout; |
3f045a08 JB |
2183 | struct listnode *cnode; |
2184 | struct isis_circuit *circuit; | |
2185 | int lvl; | |
2186 | ||
2187 | if (area == NULL) | |
2188 | return ISIS_ERROR; | |
2189 | ||
414766a1 CF |
2190 | sched_debug("ISIS (%s): Scheduling regeneration of %s LSPs, %sincluding PSNs", |
2191 | area->area_tag, circuit_t2string(level), all_pseudo ? "" : "not "); | |
2192 | ||
f390d2c7 | 2193 | memcpy (id, isis->sysid, ISIS_SYS_ID_LEN); |
2194 | LSP_PSEUDO_ID (id) = LSP_FRAGMENT (id) = 0; | |
eb5d44eb | 2195 | now = time (NULL); |
3f045a08 JB |
2196 | |
2197 | for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; lvl++) | |
f390d2c7 | 2198 | { |
3f045a08 JB |
2199 | if (!((level & lvl) && (area->is_type & lvl))) |
2200 | continue; | |
2201 | ||
414766a1 CF |
2202 | sched_debug("ISIS (%s): Checking whether L%d needs to be scheduled", |
2203 | area->area_tag, lvl); | |
2204 | ||
3f045a08 | 2205 | if (area->lsp_regenerate_pending[lvl - 1]) |
414766a1 CF |
2206 | { |
2207 | struct timeval remain = thread_timer_remain(area->t_lsp_refresh[lvl - 1]); | |
2208 | sched_debug("ISIS (%s): Regeneration is already pending, nothing todo." | |
2209 | " (Due in %lld.%03lld seconds)", area->area_tag, | |
2210 | (long long)remain.tv_sec, (long long)remain.tv_usec / 1000); | |
2211 | continue; | |
2212 | } | |
3f045a08 JB |
2213 | |
2214 | lsp = lsp_search (id, area->lspdb[lvl - 1]); | |
2215 | if (!lsp) | |
414766a1 CF |
2216 | { |
2217 | sched_debug("ISIS (%s): We do not have any LSPs to regenerate, nothing todo.", | |
2218 | area->area_tag); | |
2219 | continue; | |
2220 | } | |
3f045a08 | 2221 | |
f390d2c7 | 2222 | /* |
2223 | * Throttle avoidance | |
2224 | */ | |
414766a1 CF |
2225 | sched_debug("ISIS (%s): Will schedule regen timer. Last run was: %lld, Now is: %lld", |
2226 | area->area_tag, (long long)lsp->last_generated, (long long)now); | |
3f045a08 | 2227 | THREAD_TIMER_OFF (area->t_lsp_refresh[lvl - 1]); |
f390d2c7 | 2228 | diff = now - lsp->last_generated; |
3f045a08 JB |
2229 | if (diff < area->lsp_gen_interval[lvl - 1]) |
2230 | { | |
414766a1 CF |
2231 | timeout = 1000 * (area->lsp_gen_interval[lvl - 1] - diff); |
2232 | sched_debug("ISIS (%s): Scheduling in %ld ms to match configured lsp_gen_interval", | |
2233 | area->area_tag, timeout); | |
3f045a08 | 2234 | } |
f390d2c7 | 2235 | else |
3f045a08 | 2236 | { |
4836b537 MZ |
2237 | /* |
2238 | * lsps are not regenerated if lsp_regenerate function is called | |
2239 | * directly. However if the lsp_regenerate call is queued for | |
2240 | * later execution it works. | |
2241 | */ | |
414766a1 CF |
2242 | timeout = 100; |
2243 | sched_debug("ISIS (%s): Last generation was more than lsp_gen_interval ago." | |
2244 | " Scheduling for execution in %ld ms.", area->area_tag, timeout); | |
2245 | } | |
2246 | ||
2247 | area->lsp_regenerate_pending[lvl - 1] = 1; | |
2248 | if (lvl == IS_LEVEL_1) | |
2249 | { | |
ffa2c898 QY |
2250 | thread_add_timer_msec(master, lsp_l1_refresh, area, timeout, |
2251 | &area->t_lsp_refresh[lvl - 1]); | |
414766a1 CF |
2252 | } |
2253 | else if (lvl == IS_LEVEL_2) | |
2254 | { | |
ffa2c898 QY |
2255 | thread_add_timer_msec(master, lsp_l2_refresh, area, timeout, |
2256 | &area->t_lsp_refresh[lvl - 1]); | |
3f045a08 | 2257 | } |
f390d2c7 | 2258 | } |
3f045a08 JB |
2259 | |
2260 | if (all_pseudo) | |
f390d2c7 | 2261 | { |
3f045a08 JB |
2262 | for (ALL_LIST_ELEMENTS_RO (area->circuit_list, cnode, circuit)) |
2263 | lsp_regenerate_schedule_pseudo (circuit, level); | |
f390d2c7 | 2264 | } |
2265 | ||
2266 | return ISIS_OK; | |
eb5d44eb | 2267 | } |
2268 | ||
2269 | /* | |
2270 | * Funcs for pseudonode LSPs | |
2271 | */ | |
2272 | ||
2273 | /* | |
2274 | * 7.3.8 and 7.3.10 Generation of level 1 and 2 pseudonode LSPs | |
2275 | */ | |
92365889 | 2276 | static void |
f390d2c7 | 2277 | lsp_build_pseudo (struct isis_lsp *lsp, struct isis_circuit *circuit, |
2278 | int level) | |
eb5d44eb | 2279 | { |
2280 | struct isis_adjacency *adj; | |
2281 | struct is_neigh *is_neigh; | |
aa4376ec | 2282 | struct te_is_neigh *te_is_neigh; |
eb5d44eb | 2283 | struct es_neigh *es_neigh; |
2284 | struct list *adj_list; | |
3fdb2dd9 | 2285 | struct listnode *node; |
14872644 CF |
2286 | struct isis_area *area = circuit->area; |
2287 | ||
2288 | lsp_debug("ISIS (%s): Constructing pseudo LSP %s for interface %s level %d", | |
2289 | area->area_tag, rawlspid_print(lsp->lsp_header->lsp_id), | |
2290 | circuit->interface->name, level); | |
f390d2c7 | 2291 | |
eb5d44eb | 2292 | lsp->level = level; |
3f045a08 | 2293 | /* RFC3787 section 4 SHOULD not set overload bit in pseudo LSPs */ |
7ed55a41 AN |
2294 | lsp->lsp_header->lsp_bits = lsp_bits_generate (level, 0, |
2295 | circuit->area->attached_bit); | |
eb5d44eb | 2296 | |
2297 | /* | |
2298 | * add self to IS neighbours | |
2299 | */ | |
aa4376ec | 2300 | if (circuit->area->oldmetric) |
f390d2c7 | 2301 | { |
aa4376ec | 2302 | if (lsp->tlv_data.is_neighs == NULL) |
2303 | { | |
2304 | lsp->tlv_data.is_neighs = list_new (); | |
2305 | lsp->tlv_data.is_neighs->del = free_tlv; | |
2306 | } | |
2307 | is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct is_neigh)); | |
2308 | ||
2309 | memcpy (&is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN); | |
2310 | listnode_add (lsp->tlv_data.is_neighs, is_neigh); | |
14872644 CF |
2311 | lsp_debug("ISIS (%s): Adding %s.%02x as old-style neighbor (self)", |
2312 | area->area_tag, sysid_print(is_neigh->neigh_id), | |
2313 | LSP_PSEUDO_ID(is_neigh->neigh_id)); | |
f390d2c7 | 2314 | } |
aa4376ec | 2315 | if (circuit->area->newmetric) |
2316 | { | |
2317 | if (lsp->tlv_data.te_is_neighs == NULL) | |
2318 | { | |
2319 | lsp->tlv_data.te_is_neighs = list_new (); | |
2320 | lsp->tlv_data.te_is_neighs->del = free_tlv; | |
2321 | } | |
2322 | te_is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct te_is_neigh)); | |
15935e9a | 2323 | |
aa4376ec | 2324 | memcpy (&te_is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN); |
2325 | listnode_add (lsp->tlv_data.te_is_neighs, te_is_neigh); | |
14872644 CF |
2326 | lsp_debug("ISIS (%s): Adding %s.%02x as te-style neighbor (self)", |
2327 | area->area_tag, sysid_print(te_is_neigh->neigh_id), | |
2328 | LSP_PSEUDO_ID(te_is_neigh->neigh_id)); | |
aa4376ec | 2329 | } |
f390d2c7 | 2330 | |
2331 | adj_list = list_new (); | |
2332 | isis_adj_build_up_list (circuit->u.bc.adjdb[level - 1], adj_list); | |
2333 | ||
3fdb2dd9 | 2334 | for (ALL_LIST_ELEMENTS_RO (adj_list, node, adj)) |
f390d2c7 | 2335 | { |
3f045a08 | 2336 | if (adj->level & level) |
f390d2c7 | 2337 | { |
3f045a08 JB |
2338 | if ((level == IS_LEVEL_1 && adj->sys_type == ISIS_SYSTYPE_L1_IS) || |
2339 | (level == IS_LEVEL_1 && adj->sys_type == ISIS_SYSTYPE_L2_IS && | |
aa4376ec | 2340 | adj->adj_usage == ISIS_ADJ_LEVEL1AND2) || |
3f045a08 | 2341 | (level == IS_LEVEL_2 && adj->sys_type == ISIS_SYSTYPE_L2_IS)) |
f390d2c7 | 2342 | { |
2343 | /* an IS neighbour -> add it */ | |
aa4376ec | 2344 | if (circuit->area->oldmetric) |
2345 | { | |
2346 | is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct is_neigh)); | |
15935e9a | 2347 | |
aa4376ec | 2348 | memcpy (&is_neigh->neigh_id, adj->sysid, ISIS_SYS_ID_LEN); |
2349 | listnode_add (lsp->tlv_data.is_neighs, is_neigh); | |
14872644 CF |
2350 | lsp_debug("ISIS (%s): Adding %s.%02x as old-style neighbor (peer)", |
2351 | area->area_tag, sysid_print(is_neigh->neigh_id), | |
2352 | LSP_PSEUDO_ID(is_neigh->neigh_id)); | |
aa4376ec | 2353 | } |
2354 | if (circuit->area->newmetric) | |
2355 | { | |
2356 | te_is_neigh = XCALLOC (MTYPE_ISIS_TLV, | |
2357 | sizeof (struct te_is_neigh)); | |
2358 | memcpy (&te_is_neigh->neigh_id, adj->sysid, ISIS_SYS_ID_LEN); | |
2359 | listnode_add (lsp->tlv_data.te_is_neighs, te_is_neigh); | |
14872644 CF |
2360 | lsp_debug("ISIS (%s): Adding %s.%02x as te-style neighbor (peer)", |
2361 | area->area_tag, sysid_print(te_is_neigh->neigh_id), | |
2362 | LSP_PSEUDO_ID(te_is_neigh->neigh_id)); | |
aa4376ec | 2363 | } |
f390d2c7 | 2364 | } |
3f045a08 | 2365 | else if (level == IS_LEVEL_1 && adj->sys_type == ISIS_SYSTYPE_ES) |
f390d2c7 | 2366 | { |
2367 | /* an ES neigbour add it, if we are building level 1 LSP */ | |
2368 | /* FIXME: the tlv-format is hard to use here */ | |
2369 | if (lsp->tlv_data.es_neighs == NULL) | |
2370 | { | |
2371 | lsp->tlv_data.es_neighs = list_new (); | |
2372 | lsp->tlv_data.es_neighs->del = free_tlv; | |
2373 | } | |
15935e9a | 2374 | es_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct es_neigh)); |
2375 | ||
f390d2c7 | 2376 | memcpy (&es_neigh->first_es_neigh, adj->sysid, ISIS_SYS_ID_LEN); |
aac372f4 | 2377 | listnode_add (lsp->tlv_data.es_neighs, es_neigh); |
14872644 CF |
2378 | lsp_debug("ISIS (%s): Adding %s as ES neighbor (peer)", |
2379 | area->area_tag, sysid_print(es_neigh->first_es_neigh)); | |
f390d2c7 | 2380 | } |
14872644 CF |
2381 | else |
2382 | { | |
2383 | lsp_debug("ISIS (%s): Ignoring neighbor %s, level does not match", | |
2384 | area->area_tag, sysid_print(adj->sysid)); | |
2385 | } | |
2386 | } | |
2387 | else | |
2388 | { | |
2389 | lsp_debug("ISIS (%s): Ignoring neighbor %s, level does not intersect", | |
2390 | area->area_tag, sysid_print(adj->sysid)); | |
f390d2c7 | 2391 | } |
eb5d44eb | 2392 | } |
3f045a08 | 2393 | list_delete (adj_list); |
f390d2c7 | 2394 | |
14872644 CF |
2395 | lsp_debug("ISIS (%s): Pseudo LSP construction is complete.", area->area_tag); |
2396 | ||
c0fb2a52 | 2397 | /* Reset endp of stream to overwrite only TLV part of it. */ |
c89c05dd | 2398 | stream_reset (lsp->pdu); |
c0fb2a52 | 2399 | stream_forward_endp (lsp->pdu, ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); |
2400 | ||
eb5d44eb | 2401 | /* |
2402 | * Add the authentication info if it's present | |
2403 | */ | |
3f045a08 | 2404 | lsp_auth_add (lsp); |
eb5d44eb | 2405 | |
2406 | if (lsp->tlv_data.is_neighs && listcount (lsp->tlv_data.is_neighs) > 0) | |
2407 | tlv_add_is_neighs (lsp->tlv_data.is_neighs, lsp->pdu); | |
2408 | ||
aa4376ec | 2409 | if (lsp->tlv_data.te_is_neighs && listcount (lsp->tlv_data.te_is_neighs) > 0) |
206f4aae | 2410 | tlv_add_te_is_neighs (lsp->tlv_data.te_is_neighs, lsp->pdu, NULL); |
aa4376ec | 2411 | |
eb5d44eb | 2412 | if (lsp->tlv_data.es_neighs && listcount (lsp->tlv_data.es_neighs) > 0) |
2413 | tlv_add_is_neighs (lsp->tlv_data.es_neighs, lsp->pdu); | |
2414 | ||
9985f83c | 2415 | lsp->lsp_header->pdu_len = htons (stream_get_endp (lsp->pdu)); |
f390d2c7 | 2416 | |
3f045a08 JB |
2417 | /* Recompute authentication and checksum information */ |
2418 | lsp_auth_update (lsp); | |
2419 | fletcher_checksum(STREAM_DATA (lsp->pdu) + 12, | |
2420 | ntohs (lsp->lsp_header->pdu_len) - 12, 12); | |
eb5d44eb | 2421 | |
2422 | return; | |
2423 | } | |
2424 | ||
3f045a08 JB |
2425 | int |
2426 | lsp_generate_pseudo (struct isis_circuit *circuit, int level) | |
2427 | { | |
2428 | dict_t *lspdb = circuit->area->lspdb[level - 1]; | |
2429 | struct isis_lsp *lsp; | |
2430 | u_char lsp_id[ISIS_SYS_ID_LEN + 2]; | |
2431 | u_int16_t rem_lifetime, refresh_time; | |
2432 | ||
2433 | if ((circuit->is_type & level) != level || | |
2434 | (circuit->state != C_STATE_UP) || | |
2435 | (circuit->circ_type != CIRCUIT_T_BROADCAST) || | |
2436 | (circuit->u.bc.is_dr[level - 1] == 0)) | |
2437 | return ISIS_ERROR; | |
2438 | ||
2439 | memcpy (lsp_id, isis->sysid, ISIS_SYS_ID_LEN); | |
2440 | LSP_FRAGMENT (lsp_id) = 0; | |
2441 | LSP_PSEUDO_ID (lsp_id) = circuit->circuit_id; | |
2442 | ||
2443 | /* | |
2444 | * If for some reason have a pseudo LSP in the db already -> regenerate | |
2445 | */ | |
2446 | if (lsp_search (lsp_id, lspdb)) | |
2447 | return lsp_regenerate_schedule_pseudo (circuit, level); | |
2448 | ||
2449 | rem_lifetime = lsp_rem_lifetime (circuit->area, level); | |
2450 | /* RFC3787 section 4 SHOULD not set overload bit in pseudo LSPs */ | |
b20ccb3a | 2451 | lsp = lsp_new (circuit->area, lsp_id, rem_lifetime, 1, |
7ed55a41 AN |
2452 | circuit->area->is_type | circuit->area->attached_bit, |
2453 | 0, level); | |
3f045a08 JB |
2454 | lsp->area = circuit->area; |
2455 | ||
2456 | lsp_build_pseudo (lsp, circuit, level); | |
2457 | ||
2458 | lsp->own_lsp = 1; | |
2459 | lsp_insert (lsp, lspdb); | |
2460 | lsp_set_all_srmflags (lsp); | |
2461 | ||
2462 | refresh_time = lsp_refresh_time (lsp, rem_lifetime); | |
2463 | THREAD_TIMER_OFF (circuit->u.bc.t_refresh_pseudo_lsp[level - 1]); | |
2464 | circuit->lsp_regenerate_pending[level - 1] = 0; | |
2465 | if (level == IS_LEVEL_1) | |
ffa2c898 QY |
2466 | thread_add_timer(master, lsp_l1_refresh_pseudo, circuit, refresh_time, |
2467 | &circuit->u.bc.t_refresh_pseudo_lsp[level - 1]); | |
3f045a08 | 2468 | else if (level == IS_LEVEL_2) |
ffa2c898 QY |
2469 | thread_add_timer(master, lsp_l2_refresh_pseudo, circuit, refresh_time, |
2470 | &circuit->u.bc.t_refresh_pseudo_lsp[level - 1]); | |
3f045a08 JB |
2471 | |
2472 | if (isis->debugs & DEBUG_UPDATE_PACKETS) | |
2473 | { | |
2474 | zlog_debug ("ISIS-Upd (%s): Building L%d Pseudo LSP %s, len %d, " | |
2475 | "seq 0x%08x, cksum 0x%04x, lifetime %us, refresh %us", | |
2476 | circuit->area->area_tag, level, | |
2477 | rawlspid_print (lsp->lsp_header->lsp_id), | |
2478 | ntohl (lsp->lsp_header->pdu_len), | |
2479 | ntohl (lsp->lsp_header->seq_num), | |
2480 | ntohs (lsp->lsp_header->checksum), | |
2481 | ntohs (lsp->lsp_header->rem_lifetime), | |
2482 | refresh_time); | |
2483 | } | |
2484 | ||
2485 | return ISIS_OK; | |
2486 | } | |
2487 | ||
92365889 | 2488 | static int |
3f045a08 | 2489 | lsp_regenerate_pseudo (struct isis_circuit *circuit, int level) |
eb5d44eb | 2490 | { |
2491 | dict_t *lspdb = circuit->area->lspdb[level - 1]; | |
2492 | struct isis_lsp *lsp; | |
2493 | u_char lsp_id[ISIS_SYS_ID_LEN + 2]; | |
3f045a08 JB |
2494 | u_int16_t rem_lifetime, refresh_time; |
2495 | ||
2496 | if ((circuit->is_type & level) != level || | |
2497 | (circuit->state != C_STATE_UP) || | |
2498 | (circuit->circ_type != CIRCUIT_T_BROADCAST) || | |
2499 | (circuit->u.bc.is_dr[level - 1] == 0)) | |
2500 | return ISIS_ERROR; | |
f390d2c7 | 2501 | |
eb5d44eb | 2502 | memcpy (lsp_id, isis->sysid, ISIS_SYS_ID_LEN); |
f390d2c7 | 2503 | LSP_PSEUDO_ID (lsp_id) = circuit->circuit_id; |
2504 | LSP_FRAGMENT (lsp_id) = 0; | |
2505 | ||
eb5d44eb | 2506 | lsp = lsp_search (lsp_id, lspdb); |
f390d2c7 | 2507 | |
2508 | if (!lsp) | |
2509 | { | |
3f045a08 JB |
2510 | zlog_err ("lsp_regenerate_pseudo: no l%d LSP %s found!", |
2511 | level, rawlspid_print (lsp_id)); | |
f390d2c7 | 2512 | return ISIS_ERROR; |
2513 | } | |
2514 | lsp_clear_data (lsp); | |
eb5d44eb | 2515 | |
2516 | lsp_build_pseudo (lsp, circuit, level); | |
2517 | ||
3f045a08 | 2518 | /* RFC3787 section 4 SHOULD not set overload bit in pseudo LSPs */ |
7ed55a41 AN |
2519 | lsp->lsp_header->lsp_bits = lsp_bits_generate (level, 0, |
2520 | circuit->area->attached_bit); | |
3f045a08 JB |
2521 | rem_lifetime = lsp_rem_lifetime (circuit->area, level); |
2522 | lsp->lsp_header->rem_lifetime = htons (rem_lifetime); | |
eb5d44eb | 2523 | lsp_inc_seqnum (lsp, 0); |
3f045a08 JB |
2524 | lsp->last_generated = time (NULL); |
2525 | lsp_set_all_srmflags (lsp); | |
2526 | ||
2527 | refresh_time = lsp_refresh_time (lsp, rem_lifetime); | |
2528 | if (level == IS_LEVEL_1) | |
ffa2c898 QY |
2529 | thread_add_timer(master, lsp_l1_refresh_pseudo, circuit, refresh_time, |
2530 | &circuit->u.bc.t_refresh_pseudo_lsp[level - 1]); | |
3f045a08 | 2531 | else if (level == IS_LEVEL_2) |
ffa2c898 QY |
2532 | thread_add_timer(master, lsp_l2_refresh_pseudo, circuit, refresh_time, |
2533 | &circuit->u.bc.t_refresh_pseudo_lsp[level - 1]); | |
f390d2c7 | 2534 | |
2535 | if (isis->debugs & DEBUG_UPDATE_PACKETS) | |
2536 | { | |
3f045a08 JB |
2537 | zlog_debug ("ISIS-Upd (%s): Refreshing L%d Pseudo LSP %s, len %d, " |
2538 | "seq 0x%08x, cksum 0x%04x, lifetime %us, refresh %us", | |
2539 | circuit->area->area_tag, level, | |
2540 | rawlspid_print (lsp->lsp_header->lsp_id), | |
2541 | ntohl (lsp->lsp_header->pdu_len), | |
2542 | ntohl (lsp->lsp_header->seq_num), | |
2543 | ntohs (lsp->lsp_header->checksum), | |
2544 | ntohs (lsp->lsp_header->rem_lifetime), | |
2545 | refresh_time); | |
f390d2c7 | 2546 | } |
eb5d44eb | 2547 | |
eb5d44eb | 2548 | return ISIS_OK; |
2549 | } | |
2550 | ||
3f045a08 JB |
2551 | /* |
2552 | * Something has changed or periodic refresh -> regenerate pseudo LSP | |
2553 | */ | |
2554 | static int | |
eb5d44eb | 2555 | lsp_l1_refresh_pseudo (struct thread *thread) |
2556 | { | |
2557 | struct isis_circuit *circuit; | |
3f045a08 | 2558 | u_char id[ISIS_SYS_ID_LEN + 2]; |
eb5d44eb | 2559 | |
f390d2c7 | 2560 | circuit = THREAD_ARG (thread); |
2561 | ||
13c48f72 | 2562 | circuit->u.bc.t_refresh_pseudo_lsp[0] = NULL; |
3f045a08 | 2563 | circuit->lsp_regenerate_pending[0] = 0; |
13c48f72 | 2564 | |
3f045a08 JB |
2565 | if ((circuit->u.bc.is_dr[0] == 0) || |
2566 | (circuit->is_type & IS_LEVEL_1) == 0) | |
2567 | { | |
2568 | memcpy (id, isis->sysid, ISIS_SYS_ID_LEN); | |
2569 | LSP_PSEUDO_ID (id) = circuit->circuit_id; | |
2570 | LSP_FRAGMENT (id) = 0; | |
2571 | lsp_purge_pseudo (id, circuit, IS_LEVEL_1); | |
2572 | return ISIS_ERROR; | |
2573 | } | |
eb5d44eb | 2574 | |
3f045a08 | 2575 | return lsp_regenerate_pseudo (circuit, IS_LEVEL_1); |
eb5d44eb | 2576 | } |
2577 | ||
3f045a08 | 2578 | static int |
eb5d44eb | 2579 | lsp_l2_refresh_pseudo (struct thread *thread) |
2580 | { | |
2581 | struct isis_circuit *circuit; | |
3f045a08 | 2582 | u_char id[ISIS_SYS_ID_LEN + 2]; |
f390d2c7 | 2583 | |
3f045a08 | 2584 | circuit = THREAD_ARG (thread); |
f390d2c7 | 2585 | |
13c48f72 | 2586 | circuit->u.bc.t_refresh_pseudo_lsp[1] = NULL; |
3f045a08 | 2587 | circuit->lsp_regenerate_pending[1] = 0; |
13c48f72 | 2588 | |
3f045a08 JB |
2589 | if ((circuit->u.bc.is_dr[1] == 0) || |
2590 | (circuit->is_type & IS_LEVEL_2) == 0) | |
2591 | { | |
2592 | memcpy (id, isis->sysid, ISIS_SYS_ID_LEN); | |
2593 | LSP_PSEUDO_ID (id) = circuit->circuit_id; | |
2594 | LSP_FRAGMENT (id) = 0; | |
2595 | lsp_purge_pseudo (id, circuit, IS_LEVEL_2); | |
2596 | return ISIS_ERROR; | |
2597 | } | |
eb5d44eb | 2598 | |
3f045a08 | 2599 | return lsp_regenerate_pseudo (circuit, IS_LEVEL_2); |
eb5d44eb | 2600 | } |
2601 | ||
f390d2c7 | 2602 | int |
3f045a08 | 2603 | lsp_regenerate_schedule_pseudo (struct isis_circuit *circuit, int level) |
eb5d44eb | 2604 | { |
2605 | struct isis_lsp *lsp; | |
3f045a08 JB |
2606 | u_char lsp_id[ISIS_SYS_ID_LEN + 2]; |
2607 | time_t now, diff; | |
414766a1 | 2608 | long timeout; |
3f045a08 | 2609 | int lvl; |
414766a1 | 2610 | struct isis_area *area = circuit->area; |
eb5d44eb | 2611 | |
f7535236 | 2612 | if (circuit->circ_type != CIRCUIT_T_BROADCAST || |
3f045a08 JB |
2613 | circuit->state != C_STATE_UP) |
2614 | return ISIS_OK; | |
eb5d44eb | 2615 | |
414766a1 CF |
2616 | sched_debug("ISIS (%s): Scheduling regeneration of %s pseudo LSP for interface %s", |
2617 | area->area_tag, circuit_t2string(level), circuit->interface->name); | |
2618 | ||
3f045a08 JB |
2619 | memcpy (lsp_id, isis->sysid, ISIS_SYS_ID_LEN); |
2620 | LSP_PSEUDO_ID (lsp_id) = circuit->circuit_id; | |
2621 | LSP_FRAGMENT (lsp_id) = 0; | |
2622 | now = time (NULL); | |
f390d2c7 | 2623 | |
3f045a08 JB |
2624 | for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; lvl++) |
2625 | { | |
414766a1 CF |
2626 | sched_debug("ISIS (%s): Checking whether L%d pseudo LSP needs to be scheduled", |
2627 | area->area_tag, lvl); | |
2628 | ||
3f045a08 | 2629 | if (!((level & lvl) && (circuit->is_type & lvl))) |
414766a1 CF |
2630 | { |
2631 | sched_debug("ISIS (%s): Level is not active on circuit", | |
2632 | area->area_tag); | |
2633 | continue; | |
2634 | } | |
eb5d44eb | 2635 | |
414766a1 CF |
2636 | if (circuit->u.bc.is_dr[lvl - 1] == 0) |
2637 | { | |
2638 | sched_debug("ISIS (%s): This IS is not DR, nothing to do.", | |
2639 | area->area_tag); | |
2640 | continue; | |
2641 | } | |
2642 | ||
2643 | if (circuit->lsp_regenerate_pending[lvl - 1]) | |
2644 | { | |
2645 | struct timeval remain = | |
2646 | thread_timer_remain(circuit->u.bc.t_refresh_pseudo_lsp[lvl - 1]); | |
2647 | sched_debug("ISIS (%s): Regenerate is already pending, nothing todo." | |
2648 | " (Due in %lld.%03lld seconds)", area->area_tag, | |
2649 | (long long)remain.tv_sec, (long long)remain.tv_usec/1000); | |
2650 | continue; | |
2651 | } | |
eb5d44eb | 2652 | |
3f045a08 JB |
2653 | lsp = lsp_search (lsp_id, circuit->area->lspdb[lvl - 1]); |
2654 | if (!lsp) | |
414766a1 CF |
2655 | { |
2656 | sched_debug("ISIS (%s): Pseudonode LSP does not exist yet, nothing to regenerate.", | |
2657 | area->area_tag); | |
2658 | continue; | |
2659 | } | |
eb5d44eb | 2660 | |
3f045a08 JB |
2661 | /* |
2662 | * Throttle avoidance | |
2663 | */ | |
414766a1 CF |
2664 | sched_debug("ISIS (%s): Will schedule PSN regen timer. Last run was: %lld, Now is: %lld", |
2665 | area->area_tag, (long long)lsp->last_generated, (long long) now); | |
3f045a08 JB |
2666 | THREAD_TIMER_OFF (circuit->u.bc.t_refresh_pseudo_lsp[lvl - 1]); |
2667 | diff = now - lsp->last_generated; | |
2668 | if (diff < circuit->area->lsp_gen_interval[lvl - 1]) | |
2669 | { | |
414766a1 CF |
2670 | timeout = 1000 * (circuit->area->lsp_gen_interval[lvl - 1] - diff); |
2671 | sched_debug("ISIS (%s): Sechduling in %ld ms to match configured lsp_gen_interval", | |
2672 | area->area_tag, timeout); | |
3f045a08 JB |
2673 | } |
2674 | else | |
2675 | { | |
414766a1 CF |
2676 | timeout = 100; |
2677 | sched_debug("ISIS (%s): Last generation was more than lsp_gen_interval ago." | |
2678 | " Scheduling for execution in %ld ms.", area->area_tag, timeout); | |
2679 | } | |
2680 | ||
2681 | circuit->lsp_regenerate_pending[lvl - 1] = 1; | |
2682 | ||
2683 | if (lvl == IS_LEVEL_1) | |
2684 | { | |
ffa2c898 QY |
2685 | thread_add_timer_msec(master, lsp_l1_refresh_pseudo, circuit, |
2686 | timeout, | |
2687 | &circuit->u.bc.t_refresh_pseudo_lsp[lvl - 1]); | |
414766a1 CF |
2688 | } |
2689 | else if (lvl == IS_LEVEL_2) | |
2690 | { | |
ffa2c898 QY |
2691 | thread_add_timer_msec(master, lsp_l2_refresh_pseudo, circuit, |
2692 | timeout, | |
2693 | &circuit->u.bc.t_refresh_pseudo_lsp[lvl - 1]); | |
3f045a08 JB |
2694 | } |
2695 | } | |
eb5d44eb | 2696 | |
3f045a08 | 2697 | return ISIS_OK; |
f390d2c7 | 2698 | } |
eb5d44eb | 2699 | |
2700 | /* | |
2701 | * Walk through LSPs for an area | |
2702 | * - set remaining lifetime | |
2703 | * - set LSPs with SRMflag set for sending | |
2704 | */ | |
f390d2c7 | 2705 | int |
eb5d44eb | 2706 | lsp_tick (struct thread *thread) |
2707 | { | |
2708 | struct isis_area *area; | |
2709 | struct isis_circuit *circuit; | |
2710 | struct isis_lsp *lsp; | |
2711 | struct list *lsp_list; | |
3fdb2dd9 | 2712 | struct listnode *lspnode, *cnode; |
eb5d44eb | 2713 | dnode_t *dnode, *dnode_next; |
2714 | int level; | |
3f045a08 | 2715 | u_int16_t rem_lifetime; |
eb5d44eb | 2716 | |
2717 | lsp_list = list_new (); | |
f390d2c7 | 2718 | |
eb5d44eb | 2719 | area = THREAD_ARG (thread); |
2720 | assert (area); | |
13c48f72 | 2721 | area->t_tick = NULL; |
ffa2c898 | 2722 | thread_add_timer(master, lsp_tick, area, 1, &area->t_tick); |
eb5d44eb | 2723 | |
2724 | /* | |
2725 | * Build a list of LSPs with (any) SRMflag set | |
2726 | * and removed the ones that have aged out | |
2727 | */ | |
f390d2c7 | 2728 | for (level = 0; level < ISIS_LEVELS; level++) |
2729 | { | |
2730 | if (area->lspdb[level] && dict_count (area->lspdb[level]) > 0) | |
3f045a08 JB |
2731 | { |
2732 | for (dnode = dict_first (area->lspdb[level]); | |
2733 | dnode != NULL; dnode = dnode_next) | |
2734 | { | |
2735 | dnode_next = dict_next (area->lspdb[level], dnode); | |
2736 | lsp = dnode_get (dnode); | |
2737 | ||
2738 | /* | |
2739 | * The lsp rem_lifetime is kept at 0 for MaxAge or | |
2740 | * ZeroAgeLifetime depending on explicit purge or | |
2741 | * natural age out. So schedule spf only once when | |
2742 | * the first time rem_lifetime becomes 0. | |
2743 | */ | |
2744 | rem_lifetime = ntohs(lsp->lsp_header->rem_lifetime); | |
2745 | lsp_set_time (lsp); | |
2746 | ||
2747 | /* | |
2748 | * Schedule may run spf which should be done only after | |
2749 | * the lsp rem_lifetime becomes 0 for the first time. | |
2750 | * ISO 10589 - 7.3.16.4 first paragraph. | |
2751 | */ | |
2752 | if (rem_lifetime == 1 && lsp->lsp_header->seq_num != 0) | |
2753 | { | |
2754 | /* 7.3.16.4 a) set SRM flags on all */ | |
2755 | lsp_set_all_srmflags (lsp); | |
2756 | /* 7.3.16.4 b) retain only the header FIXME */ | |
2757 | /* 7.3.16.4 c) record the time to purge FIXME */ | |
2758 | /* run/schedule spf */ | |
2759 | /* isis_spf_schedule is called inside lsp_destroy() below; | |
2760 | * so it is not needed here. */ | |
2761 | /* isis_spf_schedule (lsp->area, lsp->level); */ | |
2762 | } | |
2763 | ||
2764 | if (lsp->age_out == 0) | |
2765 | { | |
2766 | zlog_debug ("ISIS-Upd (%s): L%u LSP %s seq 0x%08x aged out", | |
2767 | area->area_tag, | |
2768 | lsp->level, | |
2769 | rawlspid_print (lsp->lsp_header->lsp_id), | |
2770 | ntohl (lsp->lsp_header->seq_num)); | |
3f045a08 JB |
2771 | lsp_destroy (lsp); |
2772 | lsp = NULL; | |
2773 | dict_delete_free (area->lspdb[level], dnode); | |
2774 | } | |
2775 | else if (flags_any_set (lsp->SRMflags)) | |
2776 | listnode_add (lsp_list, lsp); | |
2777 | } | |
2778 | ||
2779 | /* | |
2780 | * Send LSPs on circuits indicated by the SRMflags | |
2781 | */ | |
2782 | if (listcount (lsp_list) > 0) | |
2783 | { | |
1eb8ef25 | 2784 | for (ALL_LIST_ELEMENTS_RO (area->circuit_list, cnode, circuit)) |
3f045a08 JB |
2785 | { |
2786 | int diff = time (NULL) - circuit->lsp_queue_last_cleared; | |
2787 | if (circuit->lsp_queue == NULL || | |
2788 | diff < MIN_LSP_TRANS_INTERVAL) | |
2789 | continue; | |
3fdb2dd9 | 2790 | for (ALL_LIST_ELEMENTS_RO (lsp_list, lspnode, lsp)) |
3f045a08 JB |
2791 | { |
2792 | if (circuit->upadjcount[lsp->level - 1] && | |
2793 | ISIS_CHECK_FLAG (lsp->SRMflags, circuit)) | |
2794 | { | |
2795 | /* Add the lsp only if it is not already in lsp | |
2796 | * queue */ | |
2797 | if (! listnode_lookup (circuit->lsp_queue, lsp)) | |
2798 | { | |
2799 | listnode_add (circuit->lsp_queue, lsp); | |
ffa2c898 QY |
2800 | thread_add_event(master, send_lsp, circuit, 0, |
2801 | NULL); | |
3f045a08 JB |
2802 | } |
2803 | } | |
2804 | } | |
2805 | } | |
2806 | list_delete_all_node (lsp_list); | |
2807 | } | |
2808 | } | |
eb5d44eb | 2809 | } |
eb5d44eb | 2810 | |
2811 | list_delete (lsp_list); | |
2812 | ||
2813 | return ISIS_OK; | |
2814 | } | |
2815 | ||
eb5d44eb | 2816 | void |
3f045a08 | 2817 | lsp_purge_pseudo (u_char * id, struct isis_circuit *circuit, int level) |
eb5d44eb | 2818 | { |
2819 | struct isis_lsp *lsp; | |
3f045a08 JB |
2820 | u_int16_t seq_num; |
2821 | u_int8_t lsp_bits; | |
f390d2c7 | 2822 | |
eb5d44eb | 2823 | lsp = lsp_search (id, circuit->area->lspdb[level - 1]); |
3f045a08 JB |
2824 | if (!lsp) |
2825 | return; | |
f390d2c7 | 2826 | |
3f045a08 JB |
2827 | /* store old values */ |
2828 | seq_num = lsp->lsp_header->seq_num; | |
2829 | lsp_bits = lsp->lsp_header->lsp_bits; | |
2830 | ||
2831 | /* reset stream */ | |
2832 | lsp_clear_data (lsp); | |
2833 | stream_reset (lsp->pdu); | |
2834 | ||
2835 | /* update header */ | |
2836 | lsp->lsp_header->pdu_len = htons (ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); | |
2837 | memcpy (lsp->lsp_header->lsp_id, id, ISIS_SYS_ID_LEN + 2); | |
2838 | lsp->lsp_header->checksum = 0; | |
2839 | lsp->lsp_header->seq_num = seq_num; | |
2840 | lsp->lsp_header->rem_lifetime = 0; | |
2841 | lsp->lsp_header->lsp_bits = lsp_bits; | |
2842 | lsp->level = level; | |
2843 | lsp->age_out = lsp->area->max_lsp_lifetime[level-1]; | |
2844 | stream_forward_endp (lsp->pdu, ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); | |
2845 | ||
2846 | /* | |
2847 | * Add and update the authentication info if its present | |
2848 | */ | |
2849 | lsp_auth_add (lsp); | |
2850 | lsp->lsp_header->pdu_len = htons (stream_get_endp (lsp->pdu)); | |
2851 | lsp_auth_update (lsp); | |
2852 | fletcher_checksum(STREAM_DATA (lsp->pdu) + 12, | |
2853 | ntohs (lsp->lsp_header->pdu_len) - 12, 12); | |
2854 | ||
2855 | lsp_set_all_srmflags (lsp); | |
f390d2c7 | 2856 | |
eb5d44eb | 2857 | return; |
2858 | } | |
2859 | ||
2860 | /* | |
2861 | * Purge own LSP that is received and we don't have. | |
2862 | * -> Do as in 7.3.16.4 | |
2863 | */ | |
2864 | void | |
17baea9c CF |
2865 | lsp_purge_non_exist (int level, |
2866 | struct isis_link_state_hdr *lsp_hdr, | |
f390d2c7 | 2867 | struct isis_area *area) |
eb5d44eb | 2868 | { |
2869 | struct isis_lsp *lsp; | |
2870 | ||
2871 | /* | |
2872 | * We need to create the LSP to be purged | |
2873 | */ | |
aac372f4 | 2874 | lsp = XCALLOC (MTYPE_ISIS_LSP, sizeof (struct isis_lsp)); |
3f045a08 | 2875 | lsp->area = area; |
17baea9c | 2876 | lsp->level = level; |
b20ccb3a | 2877 | lsp->pdu = stream_new(LLC_LEN + area->lsp_mtu); |
f390d2c7 | 2878 | lsp->isis_header = (struct isis_fixed_hdr *) STREAM_DATA (lsp->pdu); |
3f045a08 | 2879 | fill_fixed_hdr (lsp->isis_header, (lsp->level == IS_LEVEL_1) ? L1_LINK_STATE |
f390d2c7 | 2880 | : L2_LINK_STATE); |
2881 | lsp->lsp_header = (struct isis_link_state_hdr *) (STREAM_DATA (lsp->pdu) + | |
2882 | ISIS_FIXED_HDR_LEN); | |
eb5d44eb | 2883 | memcpy (lsp->lsp_header, lsp_hdr, ISIS_LSP_HDR_LEN); |
3f045a08 | 2884 | stream_forward_endp (lsp->pdu, ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); |
f390d2c7 | 2885 | |
eb5d44eb | 2886 | /* |
2887 | * Set the remaining lifetime to 0 | |
2888 | */ | |
2889 | lsp->lsp_header->rem_lifetime = 0; | |
3f045a08 JB |
2890 | |
2891 | /* | |
2892 | * Add and update the authentication info if its present | |
2893 | */ | |
2894 | lsp_auth_add (lsp); | |
2895 | lsp_auth_update (lsp); | |
2896 | ||
2897 | /* | |
2898 | * Update the PDU length to header plus any authentication TLV. | |
2899 | */ | |
2900 | lsp->lsp_header->pdu_len = htons (stream_get_endp (lsp->pdu)); | |
2901 | ||
eb5d44eb | 2902 | /* |
2903 | * Put the lsp into LSPdb | |
2904 | */ | |
f390d2c7 | 2905 | lsp_insert (lsp, area->lspdb[lsp->level - 1]); |
eb5d44eb | 2906 | |
2907 | /* | |
2908 | * Send in to whole area | |
2909 | */ | |
3f045a08 | 2910 | lsp_set_all_srmflags (lsp); |
f390d2c7 | 2911 | |
eb5d44eb | 2912 | return; |
2913 | } | |
2914 | ||
3f045a08 JB |
2915 | void lsp_set_all_srmflags (struct isis_lsp *lsp) |
2916 | { | |
2917 | struct listnode *node; | |
2918 | struct isis_circuit *circuit; | |
2919 | ||
2920 | assert (lsp); | |
2921 | ||
2922 | ISIS_FLAGS_CLEAR_ALL(lsp->SRMflags); | |
2923 | ||
2924 | if (lsp->area) | |
2925 | { | |
2926 | struct list *circuit_list = lsp->area->circuit_list; | |
2927 | for (ALL_LIST_ELEMENTS_RO (circuit_list, node, circuit)) | |
2928 | { | |
2929 | ISIS_SET_FLAG(lsp->SRMflags, circuit); | |
2930 | } | |
2931 | } | |
2932 | } |