]> git.proxmox.com Git - mirror_frr.git/blame - isisd/isis_adjacency.c
isisd: track intersecting set of supported MTs for each adj
[mirror_frr.git] / isisd / isis_adjacency.c
CommitLineData
eb5d44eb 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; if not, write to the Free Software Foundation, Inc.,
21 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
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_tlv.h"
47#include "isisd/isis_lsp.h"
48#include "isisd/isis_spf.h"
49#include "isisd/isis_events.h"
eb5d44eb 50
eb5d44eb 51extern struct isis *isis;
52
92365889 53static struct isis_adjacency *
02e33d3e 54adj_alloc (const u_char *id)
eb5d44eb 55{
f390d2c7 56 struct isis_adjacency *adj;
eb5d44eb 57
aac372f4 58 adj = XCALLOC (MTYPE_ISIS_ADJACENCY, sizeof (struct isis_adjacency));
f390d2c7 59 memcpy (adj->sysid, id, ISIS_SYS_ID_LEN);
60
61 return adj;
eb5d44eb 62}
63
64struct isis_adjacency *
02e33d3e 65isis_new_adj (const u_char * id, const u_char * snpa, int level,
eb5d44eb 66 struct isis_circuit *circuit)
67{
eb5d44eb 68 struct isis_adjacency *adj;
f390d2c7 69 int i;
70
71 adj = adj_alloc (id); /* P2P kludge */
eb5d44eb 72
f390d2c7 73 if (adj == NULL)
74 {
75 zlog_err ("Out of memory!");
76 return NULL;
77 }
eb5d44eb 78
41b36e90 79 if (snpa) {
3f045a08 80 memcpy (adj->snpa, snpa, ETH_ALEN);
41b36e90 81 } else {
3f045a08 82 memset (adj->snpa, ' ', ETH_ALEN);
41b36e90
PJ
83 }
84
eb5d44eb 85 adj->circuit = circuit;
86 adj->level = level;
87 adj->flaps = 0;
88 adj->last_flap = time (NULL);
f390d2c7 89 if (circuit->circ_type == CIRCUIT_T_BROADCAST)
90 {
91 listnode_add (circuit->u.bc.adjdb[level - 1], adj);
92 adj->dischanges[level - 1] = 0;
93 for (i = 0; i < DIS_RECORDS; i++) /* clear N DIS state change records */
eb5d44eb 94 {
f390d2c7 95 adj->dis_record[(i * ISIS_LEVELS) + level - 1].dis
eb5d44eb 96 = ISIS_UNKNOWN_DIS;
f390d2c7 97 adj->dis_record[(i * ISIS_LEVELS) + level - 1].last_dis_change
98 = time (NULL);
eb5d44eb 99 }
f390d2c7 100 }
eb5d44eb 101
102 return adj;
103}
104
105struct isis_adjacency *
02e33d3e 106isis_adj_lookup (const u_char * sysid, struct list *adjdb)
eb5d44eb 107{
108 struct isis_adjacency *adj;
109 struct listnode *node;
110
1eb8ef25 111 for (ALL_LIST_ELEMENTS_RO (adjdb, node, adj))
112 if (memcmp (adj->sysid, sysid, ISIS_SYS_ID_LEN) == 0)
113 return adj;
f390d2c7 114
eb5d44eb 115 return NULL;
116}
117
eb5d44eb 118struct isis_adjacency *
02e33d3e 119isis_adj_lookup_snpa (const u_char * ssnpa, struct list *adjdb)
eb5d44eb 120{
121 struct listnode *node;
122 struct isis_adjacency *adj;
123
1eb8ef25 124 for (ALL_LIST_ELEMENTS_RO (adjdb, node, adj))
125 if (memcmp (adj->snpa, ssnpa, ETH_ALEN) == 0)
126 return adj;
f390d2c7 127
eb5d44eb 128 return NULL;
129}
130
f390d2c7 131void
3f045a08 132isis_delete_adj (void *arg)
eb5d44eb 133{
3f045a08
JB
134 struct isis_adjacency *adj = arg;
135
3fdb2dd9 136 if (!adj)
137 return;
f390d2c7 138
3f045a08
JB
139 THREAD_TIMER_OFF (adj->t_expire);
140
141 /* remove from SPF trees */
142 spftree_area_adj_del (adj->circuit->area, adj);
13fb40ac 143
3f045a08
JB
144 if (adj->area_addrs)
145 list_delete (adj->area_addrs);
eb5d44eb 146 if (adj->ipv4_addrs)
147 list_delete (adj->ipv4_addrs);
eb5d44eb 148 if (adj->ipv6_addrs)
149 list_delete (adj->ipv6_addrs);
3f045a08 150
d8fba7d9
CF
151 adj_mt_finish(adj);
152
3fdb2dd9 153 XFREE (MTYPE_ISIS_ADJACENCY, adj);
eb5d44eb 154 return;
155}
156
3f045a08
JB
157static const char *
158adj_state2string (int state)
159{
160
161 switch (state)
162 {
163 case ISIS_ADJ_INITIALIZING:
164 return "Initializing";
165 case ISIS_ADJ_UP:
166 return "Up";
167 case ISIS_ADJ_DOWN:
168 return "Down";
169 default:
170 return "Unknown";
171 }
172
173 return NULL; /* not reached */
174}
175
f390d2c7 176void
3f045a08 177isis_adj_state_change (struct isis_adjacency *adj, enum isis_adj_state new_state,
1cd80845 178 const char *reason)
eb5d44eb 179{
180 int old_state;
3f045a08 181 int level;
eb5d44eb 182 struct isis_circuit *circuit;
f390d2c7 183
eb5d44eb 184 old_state = adj->adj_state;
3f045a08 185 adj->adj_state = new_state;
eb5d44eb 186
187 circuit = adj->circuit;
f390d2c7 188
189 if (isis->debugs & DEBUG_ADJ_PACKETS)
190 {
529d65b3 191 zlog_debug ("ISIS-Adj (%s): Adjacency state change %d->%d: %s",
f390d2c7 192 circuit->area->area_tag,
3f045a08 193 old_state, new_state, reason ? reason : "unspecified");
eb5d44eb 194 }
195
3f045a08 196 if (circuit->area->log_adj_changes)
f390d2c7 197 {
3f045a08
JB
198 const char *adj_name;
199 struct isis_dynhn *dyn;
f390d2c7 200
3f045a08
JB
201 dyn = dynhn_find_by_id (adj->sysid);
202 if (dyn)
203 adj_name = (const char *)dyn->name.name;
204 else
85b123a4 205 adj_name = sysid_print (adj->sysid);
3f045a08
JB
206
207 zlog_info ("%%ADJCHANGE: Adjacency to %s (%s) changed from %s to %s, %s",
208 adj_name,
e8aca32f 209 adj->circuit->interface->name,
3f045a08
JB
210 adj_state2string (old_state),
211 adj_state2string (new_state),
212 reason ? reason : "unspecified");
f390d2c7 213 }
3f045a08
JB
214
215 if (circuit->circ_type == CIRCUIT_T_BROADCAST)
216 {
217 for (level = IS_LEVEL_1; level <= IS_LEVEL_2; level++)
218 {
219 if ((adj->level & level) == 0)
220 continue;
221 if (new_state == ISIS_ADJ_UP)
e38e0df0
SV
222 {
223 circuit->upadjcount[level - 1]++;
224 isis_event_adjacency_state_change (adj, new_state);
225 /* update counter & timers for debugging purposes */
226 adj->last_flap = time (NULL);
227 adj->flaps++;
228 }
3f045a08 229 else if (new_state == ISIS_ADJ_DOWN)
e38e0df0
SV
230 {
231 listnode_delete (circuit->u.bc.adjdb[level - 1], adj);
232 circuit->upadjcount[level - 1]--;
233 if (circuit->upadjcount[level - 1] == 0)
234 {
235 /* Clean lsp_queue when no adj is up. */
236 if (circuit->lsp_queue)
237 list_delete_all_node (circuit->lsp_queue);
238 }
239 isis_event_adjacency_state_change (adj, new_state);
240 isis_delete_adj (adj);
241 }
242
243 if (circuit->u.bc.lan_neighs[level - 1])
244 {
245 list_delete_all_node (circuit->u.bc.lan_neighs[level - 1]);
246 isis_adj_build_neigh_list (circuit->u.bc.adjdb[level - 1],
247 circuit->u.bc.lan_neighs[level - 1]);
248 }
3f045a08
JB
249
250 /* On adjacency state change send new pseudo LSP if we are the DR */
251 if (circuit->u.bc.is_dr[level - 1])
252 lsp_regenerate_schedule_pseudo (circuit, level);
253 }
f390d2c7 254 }
3f045a08
JB
255 else if (circuit->circ_type == CIRCUIT_T_P2P)
256 {
257 for (level = IS_LEVEL_1; level <= IS_LEVEL_2; level++)
258 {
259 if ((adj->level & level) == 0)
260 continue;
261 if (new_state == ISIS_ADJ_UP)
e38e0df0
SV
262 {
263 circuit->upadjcount[level - 1]++;
264 isis_event_adjacency_state_change (adj, new_state);
3f045a08 265
e38e0df0
SV
266 if (adj->sys_type == ISIS_SYSTYPE_UNKNOWN)
267 send_hello (circuit, level);
3f045a08 268
e38e0df0
SV
269 /* update counter & timers for debugging purposes */
270 adj->last_flap = time (NULL);
271 adj->flaps++;
3f045a08 272
e38e0df0
SV
273 /* 7.3.17 - going up on P2P -> send CSNP */
274 /* FIXME: yup, I know its wrong... but i will do it! (for now) */
275 send_csnp (circuit, level);
276 }
3f045a08 277 else if (new_state == ISIS_ADJ_DOWN)
e38e0df0
SV
278 {
279 if (adj->circuit->u.p2p.neighbor == adj)
280 adj->circuit->u.p2p.neighbor = NULL;
281 circuit->upadjcount[level - 1]--;
282 if (circuit->upadjcount[level - 1] == 0)
283 {
284 /* Clean lsp_queue when no adj is up. */
285 if (circuit->lsp_queue)
286 list_delete_all_node (circuit->lsp_queue);
287 }
288 isis_event_adjacency_state_change (adj, new_state);
289 isis_delete_adj (adj);
290 }
3f045a08 291 }
f390d2c7 292 }
3f045a08 293
eb5d44eb 294 return;
295}
296
297
298void
299isis_adj_print (struct isis_adjacency *adj)
300{
301 struct isis_dynhn *dyn;
302 struct listnode *node;
303 struct in_addr *ipv4_addr;
eb5d44eb 304 struct in6_addr *ipv6_addr;
f390d2c7 305 u_char ip6[INET6_ADDRSTRLEN];
f390d2c7 306
307 if (!adj)
eb5d44eb 308 return;
309 dyn = dynhn_find_by_id (adj->sysid);
310 if (dyn)
529d65b3 311 zlog_debug ("%s", dyn->name.name);
f390d2c7 312
529d65b3 313 zlog_debug ("SystemId %20s SNPA %s, level %d\nHolding Time %d",
85b123a4
CF
314 sysid_print (adj->sysid), snpa_print (adj->snpa),
315 adj->level, adj->hold_time);
f390d2c7 316 if (adj->ipv4_addrs && listcount (adj->ipv4_addrs) > 0)
317 {
3f045a08 318 zlog_debug ("IPv4 Address(es):");
f390d2c7 319
1eb8ef25 320 for (ALL_LIST_ELEMENTS_RO (adj->ipv4_addrs, node, ipv4_addr))
321 zlog_debug ("%s", inet_ntoa (*ipv4_addr));
eb5d44eb 322 }
f390d2c7 323
f390d2c7 324 if (adj->ipv6_addrs && listcount (adj->ipv6_addrs) > 0)
325 {
3f045a08 326 zlog_debug ("IPv6 Address(es):");
1eb8ef25 327 for (ALL_LIST_ELEMENTS_RO (adj->ipv6_addrs, node, ipv6_addr))
f390d2c7 328 {
f7c43dcb 329 inet_ntop (AF_INET6, ipv6_addr, (char *)ip6, INET6_ADDRSTRLEN);
529d65b3 330 zlog_debug ("%s", ip6);
f390d2c7 331 }
eb5d44eb 332 }
529d65b3 333 zlog_debug ("Speaks: %s", nlpid2string (&adj->nlpids));
eb5d44eb 334
335 return;
336}
337
f390d2c7 338int
eb5d44eb 339isis_adj_expire (struct thread *thread)
340{
341 struct isis_adjacency *adj;
eb5d44eb 342
343 /*
344 * Get the adjacency
345 */
346 adj = THREAD_ARG (thread);
347 assert (adj);
83fe45e3 348 adj->t_expire = NULL;
eb5d44eb 349
350 /* trigger the adj expire event */
351 isis_adj_state_change (adj, ISIS_ADJ_DOWN, "holding time expired");
352
353 return 0;
354}
355
eb5d44eb 356/*
3f045a08 357 * show isis neighbor [detail]
eb5d44eb 358 */
3f045a08
JB
359void
360isis_adj_print_vty (struct isis_adjacency *adj, struct vty *vty, char detail)
eb5d44eb 361{
eb5d44eb 362 struct in6_addr *ipv6_addr;
f390d2c7 363 u_char ip6[INET6_ADDRSTRLEN];
eb5d44eb 364 struct in_addr *ip_addr;
365 time_t now;
366 struct isis_dynhn *dyn;
367 int level;
368 struct listnode *node;
369
370 dyn = dynhn_find_by_id (adj->sysid);
371 if (dyn)
372 vty_out (vty, " %-20s", dyn->name.name);
f390d2c7 373 else
0bffa929 374 vty_out (vty, " %-20s", sysid_print (adj->sysid));
f390d2c7 375
376 if (detail == ISIS_UI_LEVEL_BRIEF)
377 {
378 if (adj->circuit)
379 vty_out (vty, "%-12s", adj->circuit->interface->name);
380 else
381 vty_out (vty, "NULL circuit!");
382 vty_out (vty, "%-3u", adj->level); /* level */
383 vty_out (vty, "%-13s", adj_state2string (adj->adj_state));
384 now = time (NULL);
385 if (adj->last_upd)
8f2c16aa
DL
386 vty_out (vty, "%-9llu",
387 (unsigned long long)adj->last_upd + adj->hold_time - now);
f390d2c7 388 else
389 vty_out (vty, "- ");
390 vty_out (vty, "%-10s", snpa_print (adj->snpa));
391 vty_out (vty, "%s", VTY_NEWLINE);
392 }
393
394 if (detail == ISIS_UI_LEVEL_DETAIL)
395 {
396 level = adj->level;
3f045a08 397 vty_out (vty, "%s", VTY_NEWLINE);
f390d2c7 398 if (adj->circuit)
3f045a08 399 vty_out (vty, " Interface: %s", adj->circuit->interface->name);
f390d2c7 400 else
3f045a08 401 vty_out (vty, " Interface: NULL circuit");
f390d2c7 402 vty_out (vty, ", Level: %u", adj->level); /* level */
403 vty_out (vty, ", State: %s", adj_state2string (adj->adj_state));
404 now = time (NULL);
405 if (adj->last_upd)
406 vty_out (vty, ", Expires in %s",
407 time2string (adj->last_upd + adj->hold_time - now));
408 else
409 vty_out (vty, ", Expires in %s", time2string (adj->hold_time));
3f045a08
JB
410 vty_out (vty, "%s", VTY_NEWLINE);
411 vty_out (vty, " Adjacency flaps: %u", adj->flaps);
f390d2c7 412 vty_out (vty, ", Last: %s ago", time2string (now - adj->last_flap));
3f045a08
JB
413 vty_out (vty, "%s", VTY_NEWLINE);
414 vty_out (vty, " Circuit type: %s", circuit_t2string (adj->circuit_t));
f390d2c7 415 vty_out (vty, ", Speaks: %s", nlpid2string (&adj->nlpids));
3f045a08
JB
416 vty_out (vty, "%s", VTY_NEWLINE);
417 vty_out (vty, " SNPA: %s", snpa_print (adj->snpa));
e8aca32f 418 if (adj->circuit && (adj->circuit->circ_type == CIRCUIT_T_BROADCAST))
3f045a08
JB
419 {
420 dyn = dynhn_find_by_id (adj->lanid);
421 if (dyn)
422 vty_out (vty, ", LAN id: %s.%02x",
423 dyn->name.name, adj->lanid[ISIS_SYS_ID_LEN]);
424 else
425 vty_out (vty, ", LAN id: %s.%02x",
426 sysid_print (adj->lanid), adj->lanid[ISIS_SYS_ID_LEN]);
427
428 vty_out (vty, "%s", VTY_NEWLINE);
429 vty_out (vty, " LAN Priority: %u", adj->prio[adj->level - 1]);
430
431 vty_out (vty, ", %s, DIS flaps: %u, Last: %s ago",
432 isis_disflag2string (adj->dis_record[ISIS_LEVELS + level - 1].
433 dis), adj->dischanges[level - 1],
434 time2string (now -
435 (adj->dis_record[ISIS_LEVELS + level - 1].
436 last_dis_change)));
437 }
438 vty_out (vty, "%s", VTY_NEWLINE);
f390d2c7 439
3f045a08
JB
440 if (adj->area_addrs && listcount (adj->area_addrs) > 0)
441 {
442 struct area_addr *area_addr;
443 vty_out (vty, " Area Address(es):%s", VTY_NEWLINE);
444 for (ALL_LIST_ELEMENTS_RO (adj->area_addrs, node, area_addr))
445 vty_out (vty, " %s%s", isonet_print (area_addr->area_addr,
446 area_addr->addr_len), VTY_NEWLINE);
447 }
f390d2c7 448 if (adj->ipv4_addrs && listcount (adj->ipv4_addrs) > 0)
449 {
3f045a08 450 vty_out (vty, " IPv4 Address(es):%s", VTY_NEWLINE);
1eb8ef25 451 for (ALL_LIST_ELEMENTS_RO (adj->ipv4_addrs, node, ip_addr))
452 vty_out (vty, " %s%s", inet_ntoa (*ip_addr), VTY_NEWLINE);
f390d2c7 453 }
f390d2c7 454 if (adj->ipv6_addrs && listcount (adj->ipv6_addrs) > 0)
455 {
3f045a08 456 vty_out (vty, " IPv6 Address(es):%s", VTY_NEWLINE);
5d6e2691 457 for (ALL_LIST_ELEMENTS_RO (adj->ipv6_addrs, node, ipv6_addr))
f390d2c7 458 {
f7c43dcb 459 inet_ntop (AF_INET6, ipv6_addr, (char *)ip6, INET6_ADDRSTRLEN);
f390d2c7 460 vty_out (vty, " %s%s", ip6, VTY_NEWLINE);
461 }
462 }
f390d2c7 463 vty_out (vty, "%s", VTY_NEWLINE);
464 }
eb5d44eb 465 return;
466}
467
eb5d44eb 468void
469isis_adj_build_neigh_list (struct list *adjdb, struct list *list)
eb5d44eb 470{
471 struct isis_adjacency *adj;
472 struct listnode *node;
f390d2c7 473
474 if (!list)
475 {
476 zlog_warn ("isis_adj_build_neigh_list(): NULL list");
eb5d44eb 477 return;
478 }
f390d2c7 479
1eb8ef25 480 for (ALL_LIST_ELEMENTS_RO (adjdb, node, adj))
f390d2c7 481 {
f390d2c7 482 if (!adj)
483 {
484 zlog_warn ("isis_adj_build_neigh_list(): NULL adj");
485 return;
486 }
487
488 if ((adj->adj_state == ISIS_ADJ_UP ||
489 adj->adj_state == ISIS_ADJ_INITIALIZING))
490 listnode_add (list, adj->snpa);
491 }
eb5d44eb 492 return;
493}
494
495void
496isis_adj_build_up_list (struct list *adjdb, struct list *list)
497{
498 struct isis_adjacency *adj;
499 struct listnode *node;
500
cd4ab724 501 if (adjdb == NULL) {
502 zlog_warn ("isis_adj_build_up_list(): adjacency DB is empty");
503 return;
504 }
505
f390d2c7 506 if (!list)
507 {
508 zlog_warn ("isis_adj_build_up_list(): NULL list");
eb5d44eb 509 return;
510 }
511
1eb8ef25 512 for (ALL_LIST_ELEMENTS_RO (adjdb, node, adj))
f390d2c7 513 {
f390d2c7 514 if (!adj)
515 {
516 zlog_warn ("isis_adj_build_up_list(): NULL adj");
517 return;
518 }
519
520 if (adj->adj_state == ISIS_ADJ_UP)
521 listnode_add (list, adj);
522 }
523
eb5d44eb 524 return;
525}
d8fba7d9
CF
526
527int
528isis_adj_usage2levels(enum isis_adj_usage usage)
529{
530 switch (usage)
531 {
532 case ISIS_ADJ_LEVEL1:
533 return IS_LEVEL_1;
534 case ISIS_ADJ_LEVEL2:
535 return IS_LEVEL_2;
536 case ISIS_ADJ_LEVEL1AND2:
537 return IS_LEVEL_1 | IS_LEVEL_2;
538 default:
539 break;
540 }
541 return 0;
542}