]> git.proxmox.com Git - mirror_frr.git/blob - isisd/isis_adjacency.c
lib: enforce vrf_name_to_id by returning default_vrf when name is null
[mirror_frr.git] / isisd / isis_adjacency.c
1 /*
2 * IS-IS Rout(e)ing protocol - isis_adjacency.c
3 * handling of IS-IS adjacencies
4 *
5 * Copyright (C) 2001,2002 Sampo Saaristo
6 * Tampere University of Technology
7 * Institute of Communications Engineering
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public Licenseas published by the Free
11 * Software Foundation; either version 2 of the License, or (at your option)
12 * any later version.
13 *
14 * This program is distributed in the hope that it will be useful,but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; see the file COPYING; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24 #include <zebra.h>
25
26 #include "log.h"
27 #include "memory.h"
28 #include "hash.h"
29 #include "vty.h"
30 #include "linklist.h"
31 #include "thread.h"
32 #include "if.h"
33 #include "stream.h"
34
35 #include "isisd/dict.h"
36 #include "isisd/isis_constants.h"
37 #include "isisd/isis_common.h"
38 #include "isisd/isis_flags.h"
39 #include "isisd/isisd.h"
40 #include "isisd/isis_circuit.h"
41 #include "isisd/isis_adjacency.h"
42 #include "isisd/isis_misc.h"
43 #include "isisd/isis_dr.h"
44 #include "isisd/isis_dynhn.h"
45 #include "isisd/isis_pdu.h"
46 #include "isisd/isis_lsp.h"
47 #include "isisd/isis_spf.h"
48 #include "isisd/isis_events.h"
49 #include "isisd/isis_mt.h"
50 #include "isisd/isis_tlvs.h"
51 #include "isisd/fabricd.h"
52
53 extern struct isis *isis;
54
55 static struct isis_adjacency *adj_alloc(const uint8_t *id)
56 {
57 struct isis_adjacency *adj;
58
59 adj = XCALLOC(MTYPE_ISIS_ADJACENCY, sizeof(struct isis_adjacency));
60 memcpy(adj->sysid, id, ISIS_SYS_ID_LEN);
61
62 return adj;
63 }
64
65 struct isis_adjacency *isis_new_adj(const uint8_t *id, const uint8_t *snpa,
66 int level, struct isis_circuit *circuit)
67 {
68 struct isis_adjacency *adj;
69 int i;
70
71 adj = adj_alloc(id); /* P2P kludge */
72
73 if (snpa) {
74 memcpy(adj->snpa, snpa, ETH_ALEN);
75 } else {
76 memset(adj->snpa, ' ', ETH_ALEN);
77 }
78
79 adj->circuit = circuit;
80 adj->level = level;
81 adj->flaps = 0;
82 adj->last_flap = time(NULL);
83 adj->threeway_state = ISIS_THREEWAY_DOWN;
84 if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
85 listnode_add(circuit->u.bc.adjdb[level - 1], adj);
86 adj->dischanges[level - 1] = 0;
87 for (i = 0; i < DIS_RECORDS;
88 i++) /* clear N DIS state change records */
89 {
90 adj->dis_record[(i * ISIS_LEVELS) + level - 1].dis =
91 ISIS_UNKNOWN_DIS;
92 adj->dis_record[(i * ISIS_LEVELS) + level - 1]
93 .last_dis_change = time(NULL);
94 }
95 }
96
97 return adj;
98 }
99
100 struct isis_adjacency *isis_adj_lookup(const uint8_t *sysid, struct list *adjdb)
101 {
102 struct isis_adjacency *adj;
103 struct listnode *node;
104
105 for (ALL_LIST_ELEMENTS_RO(adjdb, node, adj))
106 if (memcmp(adj->sysid, sysid, ISIS_SYS_ID_LEN) == 0)
107 return adj;
108
109 return NULL;
110 }
111
112 struct isis_adjacency *isis_adj_lookup_snpa(const uint8_t *ssnpa,
113 struct list *adjdb)
114 {
115 struct listnode *node;
116 struct isis_adjacency *adj;
117
118 for (ALL_LIST_ELEMENTS_RO(adjdb, node, adj))
119 if (memcmp(adj->snpa, ssnpa, ETH_ALEN) == 0)
120 return adj;
121
122 return NULL;
123 }
124
125 DEFINE_HOOK(isis_adj_state_change_hook, (struct isis_adjacency *adj), (adj))
126
127 void isis_delete_adj(void *arg)
128 {
129 struct isis_adjacency *adj = arg;
130
131 if (!adj)
132 return;
133
134 THREAD_TIMER_OFF(adj->t_expire);
135 if (adj->adj_state != ISIS_ADJ_DOWN) {
136 adj->adj_state = ISIS_ADJ_DOWN;
137 hook_call(isis_adj_state_change_hook, adj);
138 }
139
140 /* remove from SPF trees */
141 spftree_area_adj_del(adj->circuit->area, adj);
142
143 if (adj->area_addresses)
144 XFREE(MTYPE_ISIS_ADJACENCY_INFO, adj->area_addresses);
145 if (adj->ipv4_addresses)
146 XFREE(MTYPE_ISIS_ADJACENCY_INFO, adj->ipv4_addresses);
147 if (adj->ipv6_addresses)
148 XFREE(MTYPE_ISIS_ADJACENCY_INFO, adj->ipv6_addresses);
149
150 adj_mt_finish(adj);
151
152 XFREE(MTYPE_ISIS_ADJACENCY, adj);
153 return;
154 }
155
156 static const char *adj_state2string(int state)
157 {
158
159 switch (state) {
160 case ISIS_ADJ_INITIALIZING:
161 return "Initializing";
162 case ISIS_ADJ_UP:
163 return "Up";
164 case ISIS_ADJ_DOWN:
165 return "Down";
166 default:
167 return "Unknown";
168 }
169
170 return NULL; /* not reached */
171 }
172
173 void isis_adj_process_threeway(struct isis_adjacency *adj,
174 struct isis_threeway_adj *tw_adj,
175 enum isis_adj_usage adj_usage)
176 {
177 enum isis_threeway_state next_tw_state = ISIS_THREEWAY_DOWN;
178
179 if (tw_adj && !adj->circuit->disable_threeway_adj) {
180 if (tw_adj->state == ISIS_THREEWAY_DOWN) {
181 next_tw_state = ISIS_THREEWAY_INITIALIZING;
182 } else if (tw_adj->state == ISIS_THREEWAY_INITIALIZING) {
183 next_tw_state = ISIS_THREEWAY_UP;
184 } else if (tw_adj->state == ISIS_THREEWAY_UP) {
185 if (adj->threeway_state == ISIS_THREEWAY_DOWN)
186 next_tw_state = ISIS_THREEWAY_DOWN;
187 else
188 next_tw_state = ISIS_THREEWAY_UP;
189 }
190 } else {
191 next_tw_state = ISIS_THREEWAY_UP;
192 }
193
194 if (next_tw_state != adj->threeway_state) {
195 if (isis->debugs & DEBUG_ADJ_PACKETS) {
196 zlog_info("ISIS-Adj (%s): Threeway state change %s to %s",
197 adj->circuit->area->area_tag,
198 isis_threeway_state_name(adj->threeway_state),
199 isis_threeway_state_name(next_tw_state));
200 }
201 }
202
203 if (next_tw_state != ISIS_THREEWAY_DOWN)
204 fabricd_initial_sync_hello(adj->circuit);
205
206 if (next_tw_state == ISIS_THREEWAY_DOWN) {
207 isis_adj_state_change(adj, ISIS_ADJ_DOWN, "Neighbor restarted");
208 return;
209 }
210
211 if (next_tw_state == ISIS_THREEWAY_UP) {
212 if (adj->adj_state != ISIS_ADJ_UP) {
213 isis_adj_state_change(adj, ISIS_ADJ_UP, NULL);
214 adj->adj_usage = adj_usage;
215 }
216 }
217
218 if (adj->threeway_state != next_tw_state) {
219 send_hello_sched(adj->circuit, 0, TRIGGERED_IIH_DELAY);
220 }
221
222 adj->threeway_state = next_tw_state;
223 }
224
225 void isis_adj_state_change(struct isis_adjacency *adj,
226 enum isis_adj_state new_state, const char *reason)
227 {
228 enum isis_adj_state old_state = adj->adj_state;
229 struct isis_circuit *circuit = adj->circuit;
230 bool del;
231
232 adj->adj_state = new_state;
233 if (new_state != old_state) {
234 send_hello_sched(circuit, adj->level, TRIGGERED_IIH_DELAY);
235 }
236
237 if (isis->debugs & DEBUG_ADJ_PACKETS) {
238 zlog_debug("ISIS-Adj (%s): Adjacency state change %d->%d: %s",
239 circuit->area->area_tag, old_state, new_state,
240 reason ? reason : "unspecified");
241 }
242
243 if (circuit->area->log_adj_changes) {
244 const char *adj_name;
245 struct isis_dynhn *dyn;
246
247 dyn = dynhn_find_by_id(adj->sysid);
248 if (dyn)
249 adj_name = dyn->hostname;
250 else
251 adj_name = sysid_print(adj->sysid);
252
253 zlog_info(
254 "%%ADJCHANGE: Adjacency to %s (%s) changed from %s to %s, %s",
255 adj_name, adj->circuit->interface->name,
256 adj_state2string(old_state),
257 adj_state2string(new_state),
258 reason ? reason : "unspecified");
259 }
260
261 #ifndef FABRICD
262 /* send northbound notification */
263 isis_notif_adj_state_change(adj, new_state, reason);
264 #endif /* ifndef FABRICD */
265
266 if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
267 del = false;
268 for (int level = IS_LEVEL_1; level <= IS_LEVEL_2; level++) {
269 if ((adj->level & level) == 0)
270 continue;
271 if (new_state == ISIS_ADJ_UP) {
272 circuit->upadjcount[level - 1]++;
273 hook_call(isis_adj_state_change_hook, adj);
274 /* update counter & timers for debugging
275 * purposes */
276 adj->last_flap = time(NULL);
277 adj->flaps++;
278 } else if (new_state == ISIS_ADJ_DOWN) {
279 listnode_delete(circuit->u.bc.adjdb[level - 1],
280 adj);
281
282 circuit->upadjcount[level - 1]--;
283 if (circuit->upadjcount[level - 1] == 0)
284 isis_tx_queue_clean(circuit->tx_queue);
285
286 hook_call(isis_adj_state_change_hook, adj);
287 del = true;
288 }
289
290 if (circuit->u.bc.lan_neighs[level - 1]) {
291 list_delete_all_node(
292 circuit->u.bc.lan_neighs[level - 1]);
293 isis_adj_build_neigh_list(
294 circuit->u.bc.adjdb[level - 1],
295 circuit->u.bc.lan_neighs[level - 1]);
296 }
297
298 /* On adjacency state change send new pseudo LSP if we
299 * are the DR */
300 if (circuit->u.bc.is_dr[level - 1])
301 lsp_regenerate_schedule_pseudo(circuit, level);
302 }
303
304 if (del)
305 isis_delete_adj(adj);
306
307 } else if (circuit->circ_type == CIRCUIT_T_P2P) {
308 del = false;
309 for (int level = IS_LEVEL_1; level <= IS_LEVEL_2; level++) {
310 if ((adj->level & level) == 0)
311 continue;
312 if (new_state == ISIS_ADJ_UP) {
313 circuit->upadjcount[level - 1]++;
314 hook_call(isis_adj_state_change_hook, adj);
315
316 /* update counter & timers for debugging
317 * purposes */
318 adj->last_flap = time(NULL);
319 adj->flaps++;
320
321 if (level == IS_LEVEL_1) {
322 thread_add_timer(master, send_l1_csnp,
323 circuit, 0,
324 &circuit->t_send_csnp[0]);
325 } else {
326 thread_add_timer(master, send_l2_csnp,
327 circuit, 0,
328 &circuit->t_send_csnp[1]);
329 }
330 } else if (new_state == ISIS_ADJ_DOWN) {
331 if (adj->circuit->u.p2p.neighbor == adj)
332 adj->circuit->u.p2p.neighbor = NULL;
333 circuit->upadjcount[level - 1]--;
334 if (circuit->upadjcount[level - 1] == 0)
335 isis_tx_queue_clean(circuit->tx_queue);
336
337 hook_call(isis_adj_state_change_hook, adj);
338 del = true;
339 }
340 }
341
342 if (del)
343 isis_delete_adj(adj);
344 }
345 }
346
347
348 void isis_adj_print(struct isis_adjacency *adj)
349 {
350 struct isis_dynhn *dyn;
351
352 if (!adj)
353 return;
354 dyn = dynhn_find_by_id(adj->sysid);
355 if (dyn)
356 zlog_debug("%s", dyn->hostname);
357
358 zlog_debug("SystemId %20s SNPA %s, level %d\nHolding Time %d",
359 sysid_print(adj->sysid), snpa_print(adj->snpa), adj->level,
360 adj->hold_time);
361 if (adj->ipv4_address_count) {
362 zlog_debug("IPv4 Address(es):");
363 for (unsigned int i = 0; i < adj->ipv4_address_count; i++)
364 zlog_debug("%s", inet_ntoa(adj->ipv4_addresses[i]));
365 }
366
367 if (adj->ipv6_address_count) {
368 zlog_debug("IPv6 Address(es):");
369 for (unsigned int i = 0; i < adj->ipv6_address_count; i++) {
370 char buf[INET6_ADDRSTRLEN];
371 inet_ntop(AF_INET6, &adj->ipv6_addresses[i], buf,
372 sizeof(buf));
373 zlog_debug("%s", buf);
374 }
375 }
376 zlog_debug("Speaks: %s", nlpid2string(&adj->nlpids));
377
378 return;
379 }
380
381 int isis_adj_expire(struct thread *thread)
382 {
383 struct isis_adjacency *adj;
384
385 /*
386 * Get the adjacency
387 */
388 adj = THREAD_ARG(thread);
389 assert(adj);
390 adj->t_expire = NULL;
391
392 /* trigger the adj expire event */
393 isis_adj_state_change(adj, ISIS_ADJ_DOWN, "holding time expired");
394
395 return 0;
396 }
397
398 /*
399 * show isis neighbor [detail]
400 */
401 void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty,
402 char detail)
403 {
404 time_t now;
405 struct isis_dynhn *dyn;
406 int level;
407
408 dyn = dynhn_find_by_id(adj->sysid);
409 if (dyn)
410 vty_out(vty, " %-20s", dyn->hostname);
411 else
412 vty_out(vty, " %-20s", sysid_print(adj->sysid));
413
414 if (detail == ISIS_UI_LEVEL_BRIEF) {
415 if (adj->circuit)
416 vty_out(vty, "%-12s", adj->circuit->interface->name);
417 else
418 vty_out(vty, "NULL circuit!");
419 vty_out(vty, "%-3u", adj->level); /* level */
420 vty_out(vty, "%-13s", adj_state2string(adj->adj_state));
421 now = time(NULL);
422 if (adj->last_upd)
423 vty_out(vty, "%-9llu",
424 (unsigned long long)adj->last_upd
425 + adj->hold_time - now);
426 else
427 vty_out(vty, "- ");
428 vty_out(vty, "%-10s", snpa_print(adj->snpa));
429 vty_out(vty, "\n");
430 }
431
432 if (detail == ISIS_UI_LEVEL_DETAIL) {
433 level = adj->level;
434 vty_out(vty, "\n");
435 if (adj->circuit)
436 vty_out(vty, " Interface: %s",
437 adj->circuit->interface->name);
438 else
439 vty_out(vty, " Interface: NULL circuit");
440 vty_out(vty, ", Level: %u", adj->level); /* level */
441 vty_out(vty, ", State: %s", adj_state2string(adj->adj_state));
442 now = time(NULL);
443 if (adj->last_upd)
444 vty_out(vty, ", Expires in %s",
445 time2string(adj->last_upd + adj->hold_time
446 - now));
447 else
448 vty_out(vty, ", Expires in %s",
449 time2string(adj->hold_time));
450 vty_out(vty, "\n");
451 vty_out(vty, " Adjacency flaps: %u", adj->flaps);
452 vty_out(vty, ", Last: %s ago",
453 time2string(now - adj->last_flap));
454 vty_out(vty, "\n");
455 vty_out(vty, " Circuit type: %s",
456 circuit_t2string(adj->circuit_t));
457 vty_out(vty, ", Speaks: %s", nlpid2string(&adj->nlpids));
458 vty_out(vty, "\n");
459 if (adj->mt_count != 1
460 || adj->mt_set[0] != ISIS_MT_IPV4_UNICAST) {
461 vty_out(vty, " Topologies:\n");
462 for (unsigned int i = 0; i < adj->mt_count; i++)
463 vty_out(vty, " %s\n",
464 isis_mtid2str(adj->mt_set[i]));
465 }
466 vty_out(vty, " SNPA: %s", snpa_print(adj->snpa));
467 if (adj->circuit
468 && (adj->circuit->circ_type == CIRCUIT_T_BROADCAST)) {
469 dyn = dynhn_find_by_id(adj->lanid);
470 if (dyn)
471 vty_out(vty, ", LAN id: %s.%02x", dyn->hostname,
472 adj->lanid[ISIS_SYS_ID_LEN]);
473 else
474 vty_out(vty, ", LAN id: %s.%02x",
475 sysid_print(adj->lanid),
476 adj->lanid[ISIS_SYS_ID_LEN]);
477
478 vty_out(vty, "\n");
479 vty_out(vty, " LAN Priority: %u",
480 adj->prio[adj->level - 1]);
481
482 vty_out(vty, ", %s, DIS flaps: %u, Last: %s ago",
483 isis_disflag2string(
484 adj->dis_record[ISIS_LEVELS + level - 1]
485 .dis),
486 adj->dischanges[level - 1],
487 time2string(now - (adj->dis_record[ISIS_LEVELS
488 + level - 1]
489 .last_dis_change)));
490 }
491 vty_out(vty, "\n");
492
493 if (adj->area_address_count) {
494 vty_out(vty, " Area Address(es):\n");
495 for (unsigned int i = 0; i < adj->area_address_count;
496 i++) {
497 vty_out(vty, " %s\n",
498 isonet_print(adj->area_addresses[i]
499 .area_addr,
500 adj->area_addresses[i]
501 .addr_len));
502 }
503 }
504 if (adj->ipv4_address_count) {
505 vty_out(vty, " IPv4 Address(es):\n");
506 for (unsigned int i = 0; i < adj->ipv4_address_count;
507 i++)
508 vty_out(vty, " %s\n",
509 inet_ntoa(adj->ipv4_addresses[i]));
510 }
511 if (adj->ipv6_address_count) {
512 vty_out(vty, " IPv6 Address(es):\n");
513 for (unsigned int i = 0; i < adj->ipv6_address_count;
514 i++) {
515 char buf[INET6_ADDRSTRLEN];
516 inet_ntop(AF_INET6, &adj->ipv6_addresses[i],
517 buf, sizeof(buf));
518 vty_out(vty, " %s\n", buf);
519 }
520 }
521 vty_out(vty, "\n");
522 }
523 return;
524 }
525
526 void isis_adj_build_neigh_list(struct list *adjdb, struct list *list)
527 {
528 struct isis_adjacency *adj;
529 struct listnode *node;
530
531 if (!list) {
532 zlog_warn("isis_adj_build_neigh_list(): NULL list");
533 return;
534 }
535
536 for (ALL_LIST_ELEMENTS_RO(adjdb, node, adj)) {
537 if (!adj) {
538 zlog_warn("isis_adj_build_neigh_list(): NULL adj");
539 return;
540 }
541
542 if ((adj->adj_state == ISIS_ADJ_UP
543 || adj->adj_state == ISIS_ADJ_INITIALIZING))
544 listnode_add(list, adj->snpa);
545 }
546 return;
547 }
548
549 void isis_adj_build_up_list(struct list *adjdb, struct list *list)
550 {
551 struct isis_adjacency *adj;
552 struct listnode *node;
553
554 if (adjdb == NULL) {
555 zlog_warn("isis_adj_build_up_list(): adjacency DB is empty");
556 return;
557 }
558
559 if (!list) {
560 zlog_warn("isis_adj_build_up_list(): NULL list");
561 return;
562 }
563
564 for (ALL_LIST_ELEMENTS_RO(adjdb, node, adj)) {
565 if (!adj) {
566 zlog_warn("isis_adj_build_up_list(): NULL adj");
567 return;
568 }
569
570 if (adj->adj_state == ISIS_ADJ_UP)
571 listnode_add(list, adj);
572 }
573
574 return;
575 }
576
577 int isis_adj_usage2levels(enum isis_adj_usage usage)
578 {
579 switch (usage) {
580 case ISIS_ADJ_LEVEL1:
581 return IS_LEVEL_1;
582 case ISIS_ADJ_LEVEL2:
583 return IS_LEVEL_2;
584 case ISIS_ADJ_LEVEL1AND2:
585 return IS_LEVEL_1 | IS_LEVEL_2;
586 default:
587 break;
588 }
589 return 0;
590 }