]>
Commit | Line | Data |
---|---|---|
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 | ||
eb5d44eb | 35 | #include "isisd/isis_constants.h" |
36 | #include "isisd/isis_common.h" | |
3f045a08 | 37 | #include "isisd/isis_flags.h" |
eb5d44eb | 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" | |
3f045a08 | 45 | #include "isisd/isis_lsp.h" |
3f045a08 | 46 | #include "isisd/isis_events.h" |
206f4aae | 47 | #include "isisd/isis_mt.h" |
42fe2621 | 48 | #include "isisd/isis_tlvs.h" |
8e6fb83b | 49 | #include "isisd/fabricd.h" |
2a1c520e | 50 | #include "isisd/isis_nb.h" |
eb5d44eb | 51 | |
66b9a381 DL |
52 | DEFINE_MTYPE_STATIC(ISISD, ISIS_ADJACENCY, "ISIS adjacency"); |
53 | DEFINE_MTYPE(ISISD, ISIS_ADJACENCY_INFO, "ISIS adjacency info"); | |
54 | ||
1ee746d9 | 55 | static struct isis_adjacency *adj_alloc(struct isis_circuit *circuit, |
56 | const uint8_t *id) | |
eb5d44eb | 57 | { |
d62a17ae | 58 | struct isis_adjacency *adj; |
eb5d44eb | 59 | |
d62a17ae | 60 | adj = XCALLOC(MTYPE_ISIS_ADJACENCY, sizeof(struct isis_adjacency)); |
61 | memcpy(adj->sysid, id, ISIS_SYS_ID_LEN); | |
f390d2c7 | 62 | |
1ee746d9 | 63 | adj->snmp_idx = ++circuit->snmp_adj_idx_gen; |
64 | ||
65 | if (circuit->snmp_adj_list == NULL) | |
66 | circuit->snmp_adj_list = list_new(); | |
67 | ||
68 | adj->snmp_list_node = listnode_add(circuit->snmp_adj_list, adj); | |
69 | ||
d62a17ae | 70 | return adj; |
eb5d44eb | 71 | } |
72 | ||
d7c0a89a | 73 | struct isis_adjacency *isis_new_adj(const uint8_t *id, const uint8_t *snpa, |
d62a17ae | 74 | int level, struct isis_circuit *circuit) |
eb5d44eb | 75 | { |
d62a17ae | 76 | struct isis_adjacency *adj; |
77 | int i; | |
78 | ||
1ee746d9 | 79 | adj = adj_alloc(circuit, id); /* P2P kludge */ |
d62a17ae | 80 | |
d62a17ae | 81 | if (snpa) { |
82 | memcpy(adj->snpa, snpa, ETH_ALEN); | |
83 | } else { | |
84 | memset(adj->snpa, ' ', ETH_ALEN); | |
eb5d44eb | 85 | } |
eb5d44eb | 86 | |
d62a17ae | 87 | adj->circuit = circuit; |
88 | adj->level = level; | |
89 | adj->flaps = 0; | |
90 | adj->last_flap = time(NULL); | |
42fe2621 | 91 | adj->threeway_state = ISIS_THREEWAY_DOWN; |
d62a17ae | 92 | if (circuit->circ_type == CIRCUIT_T_BROADCAST) { |
93 | listnode_add(circuit->u.bc.adjdb[level - 1], adj); | |
94 | adj->dischanges[level - 1] = 0; | |
95 | for (i = 0; i < DIS_RECORDS; | |
96 | i++) /* clear N DIS state change records */ | |
97 | { | |
98 | adj->dis_record[(i * ISIS_LEVELS) + level - 1].dis = | |
99 | ISIS_UNKNOWN_DIS; | |
100 | adj->dis_record[(i * ISIS_LEVELS) + level - 1] | |
101 | .last_dis_change = time(NULL); | |
102 | } | |
103 | } | |
26f6acaf | 104 | adj->adj_sids = list_new(); |
75eddbc3 | 105 | listnode_add(circuit->area->adjacency_list, adj); |
d62a17ae | 106 | |
107 | return adj; | |
eb5d44eb | 108 | } |
109 | ||
d7c0a89a | 110 | struct isis_adjacency *isis_adj_lookup(const uint8_t *sysid, struct list *adjdb) |
eb5d44eb | 111 | { |
d62a17ae | 112 | struct isis_adjacency *adj; |
113 | struct listnode *node; | |
eb5d44eb | 114 | |
d62a17ae | 115 | for (ALL_LIST_ELEMENTS_RO(adjdb, node, adj)) |
116 | if (memcmp(adj->sysid, sysid, ISIS_SYS_ID_LEN) == 0) | |
117 | return adj; | |
f390d2c7 | 118 | |
d62a17ae | 119 | return NULL; |
eb5d44eb | 120 | } |
121 | ||
d7c0a89a | 122 | struct isis_adjacency *isis_adj_lookup_snpa(const uint8_t *ssnpa, |
d62a17ae | 123 | struct list *adjdb) |
eb5d44eb | 124 | { |
d62a17ae | 125 | struct listnode *node; |
126 | struct isis_adjacency *adj; | |
eb5d44eb | 127 | |
d62a17ae | 128 | for (ALL_LIST_ELEMENTS_RO(adjdb, node, adj)) |
129 | if (memcmp(adj->snpa, ssnpa, ETH_ALEN) == 0) | |
130 | return adj; | |
f390d2c7 | 131 | |
d62a17ae | 132 | return NULL; |
eb5d44eb | 133 | } |
134 | ||
75eddbc3 RW |
135 | struct isis_adjacency *isis_adj_find(const struct isis_area *area, int level, |
136 | const uint8_t *sysid) | |
26f6acaf | 137 | { |
75eddbc3 | 138 | struct isis_adjacency *adj; |
26f6acaf RW |
139 | struct listnode *node; |
140 | ||
75eddbc3 RW |
141 | for (ALL_LIST_ELEMENTS_RO(area->adjacency_list, node, adj)) { |
142 | if (!(adj->level & level)) | |
143 | continue; | |
144 | ||
145 | if (!memcmp(adj->sysid, sysid, ISIS_SYS_ID_LEN)) | |
146 | return adj; | |
26f6acaf RW |
147 | } |
148 | ||
75eddbc3 | 149 | return NULL; |
26f6acaf RW |
150 | } |
151 | ||
8451921b | 152 | DEFINE_HOOK(isis_adj_state_change_hook, (struct isis_adjacency *adj), (adj)); |
a5b5e946 | 153 | |
d62a17ae | 154 | void isis_delete_adj(void *arg) |
eb5d44eb | 155 | { |
d62a17ae | 156 | struct isis_adjacency *adj = arg; |
3f045a08 | 157 | |
d62a17ae | 158 | if (!adj) |
159 | return; | |
1ee746d9 | 160 | /* Remove self from snmp list without walking the list*/ |
161 | list_delete_node(adj->circuit->snmp_adj_list, adj->snmp_list_node); | |
f390d2c7 | 162 | |
50478845 | 163 | thread_cancel(&adj->t_expire); |
690497fb | 164 | if (adj->adj_state != ISIS_ADJ_DOWN) |
a5b5e946 | 165 | adj->adj_state = ISIS_ADJ_DOWN; |
3f045a08 | 166 | |
690497fb G |
167 | hook_call(isis_adj_state_change_hook, adj); |
168 | ||
0a22ddfb QY |
169 | XFREE(MTYPE_ISIS_ADJACENCY_INFO, adj->area_addresses); |
170 | XFREE(MTYPE_ISIS_ADJACENCY_INFO, adj->ipv4_addresses); | |
171 | XFREE(MTYPE_ISIS_ADJACENCY_INFO, adj->ipv6_addresses); | |
3f045a08 | 172 | |
d62a17ae | 173 | adj_mt_finish(adj); |
26f6acaf | 174 | list_delete(&adj->adj_sids); |
d8fba7d9 | 175 | |
75eddbc3 | 176 | listnode_delete(adj->circuit->area->adjacency_list, adj); |
d62a17ae | 177 | XFREE(MTYPE_ISIS_ADJACENCY, adj); |
178 | return; | |
eb5d44eb | 179 | } |
180 | ||
d62a17ae | 181 | static const char *adj_state2string(int state) |
3f045a08 JB |
182 | { |
183 | ||
d62a17ae | 184 | switch (state) { |
185 | case ISIS_ADJ_INITIALIZING: | |
186 | return "Initializing"; | |
187 | case ISIS_ADJ_UP: | |
188 | return "Up"; | |
189 | case ISIS_ADJ_DOWN: | |
190 | return "Down"; | |
191 | default: | |
192 | return "Unknown"; | |
193 | } | |
194 | ||
195 | return NULL; /* not reached */ | |
3f045a08 JB |
196 | } |
197 | ||
7145d5bb EDP |
198 | static const char *adj_level2string(int level) |
199 | { | |
200 | switch (level) { | |
201 | case IS_LEVEL_1: | |
202 | return "level-1"; | |
203 | case IS_LEVEL_2: | |
204 | return "level-2"; | |
205 | case IS_LEVEL_1_AND_2: | |
206 | return "level-1-2"; | |
207 | default: | |
208 | return "unknown"; | |
209 | } | |
210 | ||
211 | return NULL; /* not reached */ | |
212 | } | |
213 | ||
42fe2621 CF |
214 | void isis_adj_process_threeway(struct isis_adjacency *adj, |
215 | struct isis_threeway_adj *tw_adj, | |
216 | enum isis_adj_usage adj_usage) | |
217 | { | |
218 | enum isis_threeway_state next_tw_state = ISIS_THREEWAY_DOWN; | |
219 | ||
58e5d748 | 220 | if (tw_adj && !adj->circuit->disable_threeway_adj) { |
42fe2621 CF |
221 | if (tw_adj->state == ISIS_THREEWAY_DOWN) { |
222 | next_tw_state = ISIS_THREEWAY_INITIALIZING; | |
223 | } else if (tw_adj->state == ISIS_THREEWAY_INITIALIZING) { | |
224 | next_tw_state = ISIS_THREEWAY_UP; | |
225 | } else if (tw_adj->state == ISIS_THREEWAY_UP) { | |
226 | if (adj->threeway_state == ISIS_THREEWAY_DOWN) | |
227 | next_tw_state = ISIS_THREEWAY_DOWN; | |
228 | else | |
229 | next_tw_state = ISIS_THREEWAY_UP; | |
230 | } | |
231 | } else { | |
232 | next_tw_state = ISIS_THREEWAY_UP; | |
233 | } | |
234 | ||
235 | if (next_tw_state != adj->threeway_state) { | |
e740f9c1 | 236 | if (IS_DEBUG_ADJ_PACKETS) { |
42fe2621 CF |
237 | zlog_info("ISIS-Adj (%s): Threeway state change %s to %s", |
238 | adj->circuit->area->area_tag, | |
239 | isis_threeway_state_name(adj->threeway_state), | |
240 | isis_threeway_state_name(next_tw_state)); | |
241 | } | |
242 | } | |
243 | ||
8e6fb83b CF |
244 | if (next_tw_state != ISIS_THREEWAY_DOWN) |
245 | fabricd_initial_sync_hello(adj->circuit); | |
246 | ||
42fe2621 | 247 | if (next_tw_state == ISIS_THREEWAY_DOWN) { |
16167b31 DS |
248 | isis_adj_state_change(&adj, ISIS_ADJ_DOWN, |
249 | "Neighbor restarted"); | |
42fe2621 CF |
250 | return; |
251 | } | |
252 | ||
253 | if (next_tw_state == ISIS_THREEWAY_UP) { | |
254 | if (adj->adj_state != ISIS_ADJ_UP) { | |
16167b31 | 255 | isis_adj_state_change(&adj, ISIS_ADJ_UP, NULL); |
42fe2621 CF |
256 | adj->adj_usage = adj_usage; |
257 | } | |
258 | } | |
259 | ||
5346acec CF |
260 | if (adj->threeway_state != next_tw_state) { |
261 | send_hello_sched(adj->circuit, 0, TRIGGERED_IIH_DELAY); | |
262 | } | |
263 | ||
42fe2621 CF |
264 | adj->threeway_state = next_tw_state; |
265 | } | |
0d5b1a3a | 266 | const char *isis_adj_name(const struct isis_adjacency *adj) |
7145d5bb | 267 | { |
0d5b1a3a EDP |
268 | if (!adj) |
269 | return "NONE"; | |
270 | ||
7145d5bb EDP |
271 | struct isis_dynhn *dyn; |
272 | ||
240f48b3 | 273 | dyn = dynhn_find_by_id(adj->circuit->isis, adj->sysid); |
7145d5bb | 274 | if (dyn) |
0d5b1a3a | 275 | return dyn->hostname; |
7145d5bb | 276 | else |
0d5b1a3a EDP |
277 | return sysid_print(adj->sysid); |
278 | } | |
279 | void isis_log_adj_change(struct isis_adjacency *adj, | |
280 | enum isis_adj_state old_state, | |
281 | enum isis_adj_state new_state, const char *reason) | |
282 | { | |
7145d5bb EDP |
283 | zlog_info( |
284 | "%%ADJCHANGE: Adjacency to %s (%s) for %s changed from %s to %s, %s", | |
0d5b1a3a | 285 | isis_adj_name(adj), adj->circuit->interface->name, |
7145d5bb EDP |
286 | adj_level2string(adj->level), adj_state2string(old_state), |
287 | adj_state2string(new_state), reason ? reason : "unspecified"); | |
288 | } | |
16167b31 | 289 | void isis_adj_state_change(struct isis_adjacency **padj, |
d62a17ae | 290 | enum isis_adj_state new_state, const char *reason) |
eb5d44eb | 291 | { |
16167b31 | 292 | struct isis_adjacency *adj = *padj; |
5346acec CF |
293 | enum isis_adj_state old_state = adj->adj_state; |
294 | struct isis_circuit *circuit = adj->circuit; | |
16167b31 | 295 | bool del = false; |
d62a17ae | 296 | |
8cea0065 EDP |
297 | if (new_state == old_state) |
298 | return; | |
299 | ||
d62a17ae | 300 | adj->adj_state = new_state; |
8cea0065 | 301 | send_hello_sched(circuit, adj->level, TRIGGERED_IIH_DELAY); |
d62a17ae | 302 | |
e740f9c1 | 303 | if (IS_DEBUG_ADJ_PACKETS) { |
d62a17ae | 304 | zlog_debug("ISIS-Adj (%s): Adjacency state change %d->%d: %s", |
305 | circuit->area->area_tag, old_state, new_state, | |
306 | reason ? reason : "unspecified"); | |
307 | } | |
308 | ||
7145d5bb EDP |
309 | if (circuit->area->log_adj_changes) |
310 | isis_log_adj_change(adj, old_state, new_state, reason); | |
d62a17ae | 311 | |
de983bb8 EDP |
312 | #ifndef FABRICD |
313 | /* send northbound notification */ | |
314 | isis_notif_adj_state_change(adj, new_state, reason); | |
315 | #endif /* ifndef FABRICD */ | |
316 | ||
d62a17ae | 317 | if (circuit->circ_type == CIRCUIT_T_BROADCAST) { |
5346acec | 318 | for (int level = IS_LEVEL_1; level <= IS_LEVEL_2; level++) { |
d62a17ae | 319 | if ((adj->level & level) == 0) |
320 | continue; | |
321 | if (new_state == ISIS_ADJ_UP) { | |
1ee746d9 | 322 | circuit->adj_state_changes++; |
d62a17ae | 323 | circuit->upadjcount[level - 1]++; |
d62a17ae | 324 | /* update counter & timers for debugging |
325 | * purposes */ | |
326 | adj->last_flap = time(NULL); | |
327 | adj->flaps++; | |
8cea0065 | 328 | } else if (old_state == ISIS_ADJ_UP) { |
1ee746d9 | 329 | circuit->adj_state_changes++; |
d62a17ae | 330 | listnode_delete(circuit->u.bc.adjdb[level - 1], |
331 | adj); | |
58e16237 | 332 | |
d62a17ae | 333 | circuit->upadjcount[level - 1]--; |
58e16237 | 334 | if (circuit->upadjcount[level - 1] == 0) |
9b39405f | 335 | isis_tx_queue_clean(circuit->tx_queue); |
58e16237 | 336 | |
8cea0065 EDP |
337 | if (new_state == ISIS_ADJ_DOWN) |
338 | del = true; | |
d62a17ae | 339 | } |
340 | ||
341 | if (circuit->u.bc.lan_neighs[level - 1]) { | |
342 | list_delete_all_node( | |
343 | circuit->u.bc.lan_neighs[level - 1]); | |
344 | isis_adj_build_neigh_list( | |
345 | circuit->u.bc.adjdb[level - 1], | |
346 | circuit->u.bc.lan_neighs[level - 1]); | |
347 | } | |
348 | ||
349 | /* On adjacency state change send new pseudo LSP if we | |
350 | * are the DR */ | |
351 | if (circuit->u.bc.is_dr[level - 1]) | |
352 | lsp_regenerate_schedule_pseudo(circuit, level); | |
353 | } | |
354 | ||
d62a17ae | 355 | } else if (circuit->circ_type == CIRCUIT_T_P2P) { |
5346acec | 356 | for (int level = IS_LEVEL_1; level <= IS_LEVEL_2; level++) { |
d62a17ae | 357 | if ((adj->level & level) == 0) |
358 | continue; | |
359 | if (new_state == ISIS_ADJ_UP) { | |
360 | circuit->upadjcount[level - 1]++; | |
d62a17ae | 361 | |
d62a17ae | 362 | /* update counter & timers for debugging |
363 | * purposes */ | |
364 | adj->last_flap = time(NULL); | |
365 | adj->flaps++; | |
366 | ||
8e6fb83b CF |
367 | if (level == IS_LEVEL_1) { |
368 | thread_add_timer(master, send_l1_csnp, | |
369 | circuit, 0, | |
370 | &circuit->t_send_csnp[0]); | |
371 | } else { | |
372 | thread_add_timer(master, send_l2_csnp, | |
373 | circuit, 0, | |
374 | &circuit->t_send_csnp[1]); | |
375 | } | |
8cea0065 | 376 | } else if (old_state == ISIS_ADJ_UP) { |
d62a17ae | 377 | if (adj->circuit->u.p2p.neighbor == adj) |
378 | adj->circuit->u.p2p.neighbor = NULL; | |
379 | circuit->upadjcount[level - 1]--; | |
58e16237 | 380 | if (circuit->upadjcount[level - 1] == 0) |
9b39405f | 381 | isis_tx_queue_clean(circuit->tx_queue); |
58e16237 | 382 | |
8cea0065 EDP |
383 | if (new_state == ISIS_ADJ_DOWN) |
384 | del = true; | |
d62a17ae | 385 | } |
386 | } | |
16167b31 | 387 | } |
d62a17ae | 388 | |
e4229afd EDP |
389 | hook_call(isis_adj_state_change_hook, adj); |
390 | ||
16167b31 DS |
391 | if (del) { |
392 | isis_delete_adj(adj); | |
393 | *padj = NULL; | |
d62a17ae | 394 | } |
eb5d44eb | 395 | } |
396 | ||
397 | ||
d62a17ae | 398 | void isis_adj_print(struct isis_adjacency *adj) |
eb5d44eb | 399 | { |
d62a17ae | 400 | struct isis_dynhn *dyn; |
d62a17ae | 401 | |
402 | if (!adj) | |
403 | return; | |
240f48b3 | 404 | dyn = dynhn_find_by_id(adj->circuit->isis, adj->sysid); |
d62a17ae | 405 | if (dyn) |
af8ac8f9 | 406 | zlog_debug("%s", dyn->hostname); |
d62a17ae | 407 | |
63efca0e | 408 | zlog_debug("SystemId %20s SNPA %s, level %d; Holding Time %d", |
d62a17ae | 409 | sysid_print(adj->sysid), snpa_print(adj->snpa), adj->level, |
410 | adj->hold_time); | |
0c1bd758 | 411 | if (adj->ipv4_address_count) { |
d62a17ae | 412 | zlog_debug("IPv4 Address(es):"); |
0c1bd758 | 413 | for (unsigned int i = 0; i < adj->ipv4_address_count; i++) |
a854ea43 | 414 | zlog_debug("%pI4", &adj->ipv4_addresses[i]); |
d62a17ae | 415 | } |
416 | ||
0c1bd758 | 417 | if (adj->ipv6_address_count) { |
d62a17ae | 418 | zlog_debug("IPv6 Address(es):"); |
0c1bd758 CF |
419 | for (unsigned int i = 0; i < adj->ipv6_address_count; i++) { |
420 | char buf[INET6_ADDRSTRLEN]; | |
421 | inet_ntop(AF_INET6, &adj->ipv6_addresses[i], buf, | |
422 | sizeof(buf)); | |
423 | zlog_debug("%s", buf); | |
d62a17ae | 424 | } |
f390d2c7 | 425 | } |
d62a17ae | 426 | zlog_debug("Speaks: %s", nlpid2string(&adj->nlpids)); |
eb5d44eb | 427 | |
d62a17ae | 428 | return; |
eb5d44eb | 429 | } |
430 | ||
2a1c520e RW |
431 | const char *isis_adj_yang_state(enum isis_adj_state state) |
432 | { | |
433 | switch (state) { | |
434 | case ISIS_ADJ_DOWN: | |
435 | return "down"; | |
436 | case ISIS_ADJ_UP: | |
437 | return "up"; | |
438 | case ISIS_ADJ_INITIALIZING: | |
439 | return "init"; | |
440 | default: | |
441 | return "failed"; | |
442 | } | |
443 | } | |
444 | ||
d62a17ae | 445 | int isis_adj_expire(struct thread *thread) |
eb5d44eb | 446 | { |
d62a17ae | 447 | struct isis_adjacency *adj; |
eb5d44eb | 448 | |
d62a17ae | 449 | /* |
450 | * Get the adjacency | |
451 | */ | |
452 | adj = THREAD_ARG(thread); | |
453 | assert(adj); | |
454 | adj->t_expire = NULL; | |
eb5d44eb | 455 | |
d62a17ae | 456 | /* trigger the adj expire event */ |
16167b31 | 457 | isis_adj_state_change(&adj, ISIS_ADJ_DOWN, "holding time expired"); |
eb5d44eb | 458 | |
d62a17ae | 459 | return 0; |
eb5d44eb | 460 | } |
461 | ||
eb5d44eb | 462 | /* |
3f045a08 | 463 | * show isis neighbor [detail] |
eb5d44eb | 464 | */ |
d62a17ae | 465 | void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty, |
466 | char detail) | |
eb5d44eb | 467 | { |
d62a17ae | 468 | time_t now; |
469 | struct isis_dynhn *dyn; | |
470 | int level; | |
d62a17ae | 471 | |
0d5b1a3a | 472 | vty_out(vty, " %-20s", isis_adj_name(adj)); |
d62a17ae | 473 | |
474 | if (detail == ISIS_UI_LEVEL_BRIEF) { | |
475 | if (adj->circuit) | |
476 | vty_out(vty, "%-12s", adj->circuit->interface->name); | |
477 | else | |
478 | vty_out(vty, "NULL circuit!"); | |
479 | vty_out(vty, "%-3u", adj->level); /* level */ | |
480 | vty_out(vty, "%-13s", adj_state2string(adj->adj_state)); | |
481 | now = time(NULL); | |
3cbe31c7 EDP |
482 | if (adj->last_upd) { |
483 | if (adj->last_upd + adj->hold_time | |
484 | < (unsigned long long)now) | |
485 | vty_out(vty, " Expiring"); | |
486 | else | |
487 | vty_out(vty, " %-9llu", | |
488 | (unsigned long long)adj->last_upd | |
489 | + adj->hold_time - now); | |
490 | } else | |
d62a17ae | 491 | vty_out(vty, "- "); |
492 | vty_out(vty, "%-10s", snpa_print(adj->snpa)); | |
493 | vty_out(vty, "\n"); | |
f390d2c7 | 494 | } |
d62a17ae | 495 | |
496 | if (detail == ISIS_UI_LEVEL_DETAIL) { | |
26f6acaf RW |
497 | struct sr_adjacency *sra; |
498 | struct listnode *anode; | |
499 | ||
d62a17ae | 500 | level = adj->level; |
501 | vty_out(vty, "\n"); | |
502 | if (adj->circuit) | |
503 | vty_out(vty, " Interface: %s", | |
504 | adj->circuit->interface->name); | |
505 | else | |
506 | vty_out(vty, " Interface: NULL circuit"); | |
507 | vty_out(vty, ", Level: %u", adj->level); /* level */ | |
508 | vty_out(vty, ", State: %s", adj_state2string(adj->adj_state)); | |
509 | now = time(NULL); | |
3cbe31c7 EDP |
510 | if (adj->last_upd) { |
511 | if (adj->last_upd + adj->hold_time | |
512 | < (unsigned long long)now) | |
513 | vty_out(vty, " Expiring"); | |
514 | else | |
515 | vty_out(vty, ", Expires in %s", | |
516 | time2string(adj->last_upd | |
517 | + adj->hold_time - now)); | |
518 | } else | |
d62a17ae | 519 | vty_out(vty, ", Expires in %s", |
520 | time2string(adj->hold_time)); | |
521 | vty_out(vty, "\n"); | |
522 | vty_out(vty, " Adjacency flaps: %u", adj->flaps); | |
523 | vty_out(vty, ", Last: %s ago", | |
524 | time2string(now - adj->last_flap)); | |
525 | vty_out(vty, "\n"); | |
526 | vty_out(vty, " Circuit type: %s", | |
527 | circuit_t2string(adj->circuit_t)); | |
528 | vty_out(vty, ", Speaks: %s", nlpid2string(&adj->nlpids)); | |
529 | vty_out(vty, "\n"); | |
530 | if (adj->mt_count != 1 | |
531 | || adj->mt_set[0] != ISIS_MT_IPV4_UNICAST) { | |
532 | vty_out(vty, " Topologies:\n"); | |
533 | for (unsigned int i = 0; i < adj->mt_count; i++) | |
534 | vty_out(vty, " %s\n", | |
535 | isis_mtid2str(adj->mt_set[i])); | |
536 | } | |
537 | vty_out(vty, " SNPA: %s", snpa_print(adj->snpa)); | |
538 | if (adj->circuit | |
539 | && (adj->circuit->circ_type == CIRCUIT_T_BROADCAST)) { | |
240f48b3 | 540 | dyn = dynhn_find_by_id(adj->circuit->isis, adj->lanid); |
d62a17ae | 541 | if (dyn) |
af8ac8f9 | 542 | vty_out(vty, ", LAN id: %s.%02x", dyn->hostname, |
d62a17ae | 543 | adj->lanid[ISIS_SYS_ID_LEN]); |
544 | else | |
545 | vty_out(vty, ", LAN id: %s.%02x", | |
546 | sysid_print(adj->lanid), | |
547 | adj->lanid[ISIS_SYS_ID_LEN]); | |
548 | ||
549 | vty_out(vty, "\n"); | |
550 | vty_out(vty, " LAN Priority: %u", | |
551 | adj->prio[adj->level - 1]); | |
552 | ||
553 | vty_out(vty, ", %s, DIS flaps: %u, Last: %s ago", | |
554 | isis_disflag2string( | |
555 | adj->dis_record[ISIS_LEVELS + level - 1] | |
556 | .dis), | |
557 | adj->dischanges[level - 1], | |
9d303b37 DL |
558 | time2string(now - (adj->dis_record[ISIS_LEVELS |
559 | + level - 1] | |
560 | .last_dis_change))); | |
d62a17ae | 561 | } |
562 | vty_out(vty, "\n"); | |
563 | ||
0c1bd758 | 564 | if (adj->area_address_count) { |
d62a17ae | 565 | vty_out(vty, " Area Address(es):\n"); |
0c1bd758 CF |
566 | for (unsigned int i = 0; i < adj->area_address_count; |
567 | i++) { | |
d62a17ae | 568 | vty_out(vty, " %s\n", |
996c9314 LB |
569 | isonet_print(adj->area_addresses[i] |
570 | .area_addr, | |
571 | adj->area_addresses[i] | |
572 | .addr_len)); | |
0c1bd758 | 573 | } |
d62a17ae | 574 | } |
0c1bd758 | 575 | if (adj->ipv4_address_count) { |
d62a17ae | 576 | vty_out(vty, " IPv4 Address(es):\n"); |
0c1bd758 CF |
577 | for (unsigned int i = 0; i < adj->ipv4_address_count; |
578 | i++) | |
a854ea43 MS |
579 | vty_out(vty, " %pI4\n", |
580 | &adj->ipv4_addresses[i]); | |
d62a17ae | 581 | } |
0c1bd758 | 582 | if (adj->ipv6_address_count) { |
d62a17ae | 583 | vty_out(vty, " IPv6 Address(es):\n"); |
0c1bd758 CF |
584 | for (unsigned int i = 0; i < adj->ipv6_address_count; |
585 | i++) { | |
586 | char buf[INET6_ADDRSTRLEN]; | |
587 | inet_ntop(AF_INET6, &adj->ipv6_addresses[i], | |
588 | buf, sizeof(buf)); | |
589 | vty_out(vty, " %s\n", buf); | |
d62a17ae | 590 | } |
591 | } | |
26f6acaf RW |
592 | for (ALL_LIST_ELEMENTS_RO(adj->adj_sids, anode, sra)) { |
593 | const char *adj_type; | |
594 | const char *backup; | |
595 | uint32_t sid; | |
596 | ||
597 | switch (sra->adj->circuit->circ_type) { | |
598 | case CIRCUIT_T_BROADCAST: | |
599 | adj_type = "LAN Adjacency-SID"; | |
600 | sid = sra->u.ladj_sid->sid; | |
601 | break; | |
602 | case CIRCUIT_T_P2P: | |
603 | adj_type = "Adjacency-SID"; | |
604 | sid = sra->u.adj_sid->sid; | |
605 | break; | |
606 | default: | |
607 | continue; | |
608 | } | |
609 | backup = (sra->type == ISIS_SR_LAN_BACKUP) ? " (backup)" | |
610 | : ""; | |
611 | ||
612 | vty_out(vty, " %s %s%s: %u\n", | |
613 | (sra->nexthop.family == AF_INET) ? "IPv4" | |
614 | : "IPv6", | |
615 | adj_type, backup, sid); | |
616 | } | |
d62a17ae | 617 | vty_out(vty, "\n"); |
f390d2c7 | 618 | } |
d62a17ae | 619 | return; |
eb5d44eb | 620 | } |
621 | ||
d62a17ae | 622 | void isis_adj_build_neigh_list(struct list *adjdb, struct list *list) |
eb5d44eb | 623 | { |
d62a17ae | 624 | struct isis_adjacency *adj; |
625 | struct listnode *node; | |
626 | ||
627 | if (!list) { | |
628 | zlog_warn("isis_adj_build_neigh_list(): NULL list"); | |
629 | return; | |
f390d2c7 | 630 | } |
631 | ||
d62a17ae | 632 | for (ALL_LIST_ELEMENTS_RO(adjdb, node, adj)) { |
633 | if (!adj) { | |
634 | zlog_warn("isis_adj_build_neigh_list(): NULL adj"); | |
635 | return; | |
636 | } | |
637 | ||
638 | if ((adj->adj_state == ISIS_ADJ_UP | |
639 | || adj->adj_state == ISIS_ADJ_INITIALIZING)) | |
640 | listnode_add(list, adj->snpa); | |
641 | } | |
642 | return; | |
eb5d44eb | 643 | } |
644 | ||
d62a17ae | 645 | void isis_adj_build_up_list(struct list *adjdb, struct list *list) |
eb5d44eb | 646 | { |
d62a17ae | 647 | struct isis_adjacency *adj; |
648 | struct listnode *node; | |
649 | ||
650 | if (adjdb == NULL) { | |
651 | zlog_warn("isis_adj_build_up_list(): adjacency DB is empty"); | |
652 | return; | |
653 | } | |
654 | ||
655 | if (!list) { | |
656 | zlog_warn("isis_adj_build_up_list(): NULL list"); | |
657 | return; | |
f390d2c7 | 658 | } |
659 | ||
d62a17ae | 660 | for (ALL_LIST_ELEMENTS_RO(adjdb, node, adj)) { |
661 | if (!adj) { | |
662 | zlog_warn("isis_adj_build_up_list(): NULL adj"); | |
663 | return; | |
664 | } | |
f390d2c7 | 665 | |
d62a17ae | 666 | if (adj->adj_state == ISIS_ADJ_UP) |
667 | listnode_add(list, adj); | |
668 | } | |
669 | ||
670 | return; | |
eb5d44eb | 671 | } |
d8fba7d9 | 672 | |
d62a17ae | 673 | int isis_adj_usage2levels(enum isis_adj_usage usage) |
d8fba7d9 | 674 | { |
d62a17ae | 675 | switch (usage) { |
676 | case ISIS_ADJ_LEVEL1: | |
677 | return IS_LEVEL_1; | |
678 | case ISIS_ADJ_LEVEL2: | |
679 | return IS_LEVEL_2; | |
680 | case ISIS_ADJ_LEVEL1AND2: | |
681 | return IS_LEVEL_1 | IS_LEVEL_2; | |
682 | default: | |
683 | break; | |
684 | } | |
685 | return 0; | |
d8fba7d9 | 686 | } |