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