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