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