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