]>
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" | |
ae59cfd7 | 34 | #include "bfd.h" |
eb5d44eb | 35 | |
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 | 46 | #include "isisd/isis_lsp.h" |
3f045a08 | 47 | #include "isisd/isis_events.h" |
206f4aae | 48 | #include "isisd/isis_mt.h" |
42fe2621 | 49 | #include "isisd/isis_tlvs.h" |
8e6fb83b | 50 | #include "isisd/fabricd.h" |
2a1c520e | 51 | #include "isisd/isis_nb.h" |
eb5d44eb | 52 | |
66b9a381 DL |
53 | DEFINE_MTYPE_STATIC(ISISD, ISIS_ADJACENCY, "ISIS adjacency"); |
54 | DEFINE_MTYPE(ISISD, ISIS_ADJACENCY_INFO, "ISIS adjacency info"); | |
55 | ||
1ee746d9 | 56 | static struct isis_adjacency *adj_alloc(struct isis_circuit *circuit, |
57 | const uint8_t *id) | |
eb5d44eb | 58 | { |
d62a17ae | 59 | struct isis_adjacency *adj; |
eb5d44eb | 60 | |
d62a17ae | 61 | adj = XCALLOC(MTYPE_ISIS_ADJACENCY, sizeof(struct isis_adjacency)); |
62 | memcpy(adj->sysid, id, ISIS_SYS_ID_LEN); | |
f390d2c7 | 63 | |
1ee746d9 | 64 | adj->snmp_idx = ++circuit->snmp_adj_idx_gen; |
65 | ||
66 | if (circuit->snmp_adj_list == NULL) | |
67 | circuit->snmp_adj_list = list_new(); | |
68 | ||
69 | adj->snmp_list_node = listnode_add(circuit->snmp_adj_list, adj); | |
70 | ||
d62a17ae | 71 | return adj; |
eb5d44eb | 72 | } |
73 | ||
d7c0a89a | 74 | struct isis_adjacency *isis_new_adj(const uint8_t *id, const uint8_t *snpa, |
d62a17ae | 75 | int level, struct isis_circuit *circuit) |
eb5d44eb | 76 | { |
d62a17ae | 77 | struct isis_adjacency *adj; |
78 | int i; | |
79 | ||
1ee746d9 | 80 | adj = adj_alloc(circuit, id); /* P2P kludge */ |
d62a17ae | 81 | |
d62a17ae | 82 | if (snpa) { |
83 | memcpy(adj->snpa, snpa, ETH_ALEN); | |
84 | } else { | |
85 | memset(adj->snpa, ' ', ETH_ALEN); | |
eb5d44eb | 86 | } |
eb5d44eb | 87 | |
d62a17ae | 88 | adj->circuit = circuit; |
89 | adj->level = level; | |
90 | adj->flaps = 0; | |
91 | adj->last_flap = time(NULL); | |
42fe2621 | 92 | adj->threeway_state = ISIS_THREEWAY_DOWN; |
d62a17ae | 93 | if (circuit->circ_type == CIRCUIT_T_BROADCAST) { |
94 | listnode_add(circuit->u.bc.adjdb[level - 1], adj); | |
95 | adj->dischanges[level - 1] = 0; | |
96 | for (i = 0; i < DIS_RECORDS; | |
97 | i++) /* clear N DIS state change records */ | |
98 | { | |
99 | adj->dis_record[(i * ISIS_LEVELS) + level - 1].dis = | |
100 | ISIS_UNKNOWN_DIS; | |
101 | adj->dis_record[(i * ISIS_LEVELS) + level - 1] | |
102 | .last_dis_change = time(NULL); | |
103 | } | |
104 | } | |
26f6acaf | 105 | adj->adj_sids = list_new(); |
75eddbc3 | 106 | listnode_add(circuit->area->adjacency_list, adj); |
d62a17ae | 107 | |
108 | return adj; | |
eb5d44eb | 109 | } |
110 | ||
d7c0a89a | 111 | struct isis_adjacency *isis_adj_lookup(const uint8_t *sysid, struct list *adjdb) |
eb5d44eb | 112 | { |
d62a17ae | 113 | struct isis_adjacency *adj; |
114 | struct listnode *node; | |
eb5d44eb | 115 | |
d62a17ae | 116 | for (ALL_LIST_ELEMENTS_RO(adjdb, node, adj)) |
117 | if (memcmp(adj->sysid, sysid, ISIS_SYS_ID_LEN) == 0) | |
118 | return adj; | |
f390d2c7 | 119 | |
d62a17ae | 120 | return NULL; |
eb5d44eb | 121 | } |
122 | ||
d7c0a89a | 123 | struct isis_adjacency *isis_adj_lookup_snpa(const uint8_t *ssnpa, |
d62a17ae | 124 | struct list *adjdb) |
eb5d44eb | 125 | { |
d62a17ae | 126 | struct listnode *node; |
127 | struct isis_adjacency *adj; | |
eb5d44eb | 128 | |
d62a17ae | 129 | for (ALL_LIST_ELEMENTS_RO(adjdb, node, adj)) |
130 | if (memcmp(adj->snpa, ssnpa, ETH_ALEN) == 0) | |
131 | return adj; | |
f390d2c7 | 132 | |
d62a17ae | 133 | return NULL; |
eb5d44eb | 134 | } |
135 | ||
75eddbc3 RW |
136 | struct isis_adjacency *isis_adj_find(const struct isis_area *area, int level, |
137 | const uint8_t *sysid) | |
26f6acaf | 138 | { |
75eddbc3 | 139 | struct isis_adjacency *adj; |
26f6acaf RW |
140 | struct listnode *node; |
141 | ||
75eddbc3 RW |
142 | for (ALL_LIST_ELEMENTS_RO(area->adjacency_list, node, adj)) { |
143 | if (!(adj->level & level)) | |
144 | continue; | |
145 | ||
146 | if (!memcmp(adj->sysid, sysid, ISIS_SYS_ID_LEN)) | |
147 | return adj; | |
26f6acaf RW |
148 | } |
149 | ||
75eddbc3 | 150 | return NULL; |
26f6acaf RW |
151 | } |
152 | ||
8451921b | 153 | DEFINE_HOOK(isis_adj_state_change_hook, (struct isis_adjacency *adj), (adj)); |
a5b5e946 | 154 | |
d62a17ae | 155 | void isis_delete_adj(void *arg) |
eb5d44eb | 156 | { |
d62a17ae | 157 | struct isis_adjacency *adj = arg; |
3f045a08 | 158 | |
d62a17ae | 159 | if (!adj) |
160 | return; | |
1ee746d9 | 161 | /* Remove self from snmp list without walking the list*/ |
162 | list_delete_node(adj->circuit->snmp_adj_list, adj->snmp_list_node); | |
f390d2c7 | 163 | |
fa935aa7 | 164 | THREAD_OFF(adj->t_expire); |
690497fb | 165 | if (adj->adj_state != ISIS_ADJ_DOWN) |
a5b5e946 | 166 | adj->adj_state = ISIS_ADJ_DOWN; |
3f045a08 | 167 | |
690497fb G |
168 | hook_call(isis_adj_state_change_hook, adj); |
169 | ||
0a22ddfb QY |
170 | XFREE(MTYPE_ISIS_ADJACENCY_INFO, adj->area_addresses); |
171 | XFREE(MTYPE_ISIS_ADJACENCY_INFO, adj->ipv4_addresses); | |
173f8887 | 172 | XFREE(MTYPE_ISIS_ADJACENCY_INFO, adj->ll_ipv6_addrs); |
3f045a08 | 173 | |
d62a17ae | 174 | adj_mt_finish(adj); |
26f6acaf | 175 | list_delete(&adj->adj_sids); |
d8fba7d9 | 176 | |
75eddbc3 | 177 | listnode_delete(adj->circuit->area->adjacency_list, adj); |
d62a17ae | 178 | XFREE(MTYPE_ISIS_ADJACENCY, adj); |
179 | return; | |
eb5d44eb | 180 | } |
181 | ||
d62a17ae | 182 | static const char *adj_state2string(int state) |
3f045a08 JB |
183 | { |
184 | ||
d62a17ae | 185 | switch (state) { |
186 | case ISIS_ADJ_INITIALIZING: | |
187 | return "Initializing"; | |
188 | case ISIS_ADJ_UP: | |
189 | return "Up"; | |
190 | case ISIS_ADJ_DOWN: | |
191 | return "Down"; | |
192 | default: | |
193 | return "Unknown"; | |
194 | } | |
195 | ||
196 | return NULL; /* not reached */ | |
3f045a08 JB |
197 | } |
198 | ||
7145d5bb EDP |
199 | static const char *adj_level2string(int level) |
200 | { | |
201 | switch (level) { | |
202 | case IS_LEVEL_1: | |
203 | return "level-1"; | |
204 | case IS_LEVEL_2: | |
205 | return "level-2"; | |
206 | case IS_LEVEL_1_AND_2: | |
207 | return "level-1-2"; | |
208 | default: | |
209 | return "unknown"; | |
210 | } | |
211 | ||
212 | return NULL; /* not reached */ | |
213 | } | |
214 | ||
42fe2621 CF |
215 | void isis_adj_process_threeway(struct isis_adjacency *adj, |
216 | struct isis_threeway_adj *tw_adj, | |
217 | enum isis_adj_usage adj_usage) | |
218 | { | |
219 | enum isis_threeway_state next_tw_state = ISIS_THREEWAY_DOWN; | |
220 | ||
58e5d748 | 221 | if (tw_adj && !adj->circuit->disable_threeway_adj) { |
42fe2621 CF |
222 | if (tw_adj->state == ISIS_THREEWAY_DOWN) { |
223 | next_tw_state = ISIS_THREEWAY_INITIALIZING; | |
224 | } else if (tw_adj->state == ISIS_THREEWAY_INITIALIZING) { | |
225 | next_tw_state = ISIS_THREEWAY_UP; | |
226 | } else if (tw_adj->state == ISIS_THREEWAY_UP) { | |
227 | if (adj->threeway_state == ISIS_THREEWAY_DOWN) | |
228 | next_tw_state = ISIS_THREEWAY_DOWN; | |
229 | else | |
230 | next_tw_state = ISIS_THREEWAY_UP; | |
231 | } | |
232 | } else { | |
233 | next_tw_state = ISIS_THREEWAY_UP; | |
234 | } | |
235 | ||
236 | if (next_tw_state != adj->threeway_state) { | |
e740f9c1 | 237 | if (IS_DEBUG_ADJ_PACKETS) { |
42fe2621 CF |
238 | zlog_info("ISIS-Adj (%s): Threeway state change %s to %s", |
239 | adj->circuit->area->area_tag, | |
240 | isis_threeway_state_name(adj->threeway_state), | |
241 | isis_threeway_state_name(next_tw_state)); | |
242 | } | |
243 | } | |
244 | ||
8e6fb83b CF |
245 | if (next_tw_state != ISIS_THREEWAY_DOWN) |
246 | fabricd_initial_sync_hello(adj->circuit); | |
247 | ||
42fe2621 | 248 | if (next_tw_state == ISIS_THREEWAY_DOWN) { |
16167b31 DS |
249 | isis_adj_state_change(&adj, ISIS_ADJ_DOWN, |
250 | "Neighbor restarted"); | |
42fe2621 CF |
251 | return; |
252 | } | |
253 | ||
254 | if (next_tw_state == ISIS_THREEWAY_UP) { | |
255 | if (adj->adj_state != ISIS_ADJ_UP) { | |
16167b31 | 256 | isis_adj_state_change(&adj, ISIS_ADJ_UP, NULL); |
42fe2621 CF |
257 | adj->adj_usage = adj_usage; |
258 | } | |
259 | } | |
260 | ||
5346acec CF |
261 | if (adj->threeway_state != next_tw_state) { |
262 | send_hello_sched(adj->circuit, 0, TRIGGERED_IIH_DELAY); | |
263 | } | |
264 | ||
42fe2621 CF |
265 | adj->threeway_state = next_tw_state; |
266 | } | |
0d5b1a3a | 267 | const char *isis_adj_name(const struct isis_adjacency *adj) |
7145d5bb | 268 | { |
0d5b1a3a EDP |
269 | if (!adj) |
270 | return "NONE"; | |
271 | ||
7145d5bb EDP |
272 | struct isis_dynhn *dyn; |
273 | ||
240f48b3 | 274 | dyn = dynhn_find_by_id(adj->circuit->isis, adj->sysid); |
7145d5bb | 275 | if (dyn) |
0d5b1a3a | 276 | return dyn->hostname; |
7145d5bb | 277 | else |
0d5b1a3a EDP |
278 | return sysid_print(adj->sysid); |
279 | } | |
280 | void isis_log_adj_change(struct isis_adjacency *adj, | |
281 | enum isis_adj_state old_state, | |
282 | enum isis_adj_state new_state, const char *reason) | |
283 | { | |
7145d5bb EDP |
284 | zlog_info( |
285 | "%%ADJCHANGE: Adjacency to %s (%s) for %s changed from %s to %s, %s", | |
0d5b1a3a | 286 | isis_adj_name(adj), adj->circuit->interface->name, |
7145d5bb EDP |
287 | adj_level2string(adj->level), adj_state2string(old_state), |
288 | adj_state2string(new_state), reason ? reason : "unspecified"); | |
289 | } | |
16167b31 | 290 | void isis_adj_state_change(struct isis_adjacency **padj, |
d62a17ae | 291 | enum isis_adj_state new_state, const char *reason) |
eb5d44eb | 292 | { |
16167b31 | 293 | struct isis_adjacency *adj = *padj; |
5346acec CF |
294 | enum isis_adj_state old_state = adj->adj_state; |
295 | struct isis_circuit *circuit = adj->circuit; | |
16167b31 | 296 | bool del = false; |
d62a17ae | 297 | |
8cea0065 EDP |
298 | if (new_state == old_state) |
299 | return; | |
300 | ||
d62a17ae | 301 | adj->adj_state = new_state; |
8cea0065 | 302 | send_hello_sched(circuit, adj->level, TRIGGERED_IIH_DELAY); |
d62a17ae | 303 | |
e740f9c1 | 304 | if (IS_DEBUG_ADJ_PACKETS) { |
d62a17ae | 305 | zlog_debug("ISIS-Adj (%s): Adjacency state change %d->%d: %s", |
306 | circuit->area->area_tag, old_state, new_state, | |
307 | reason ? reason : "unspecified"); | |
308 | } | |
309 | ||
7145d5bb EDP |
310 | if (circuit->area->log_adj_changes) |
311 | isis_log_adj_change(adj, old_state, new_state, reason); | |
d62a17ae | 312 | |
de983bb8 EDP |
313 | #ifndef FABRICD |
314 | /* send northbound notification */ | |
315 | isis_notif_adj_state_change(adj, new_state, reason); | |
316 | #endif /* ifndef FABRICD */ | |
317 | ||
d62a17ae | 318 | if (circuit->circ_type == CIRCUIT_T_BROADCAST) { |
5346acec | 319 | for (int level = IS_LEVEL_1; level <= IS_LEVEL_2; level++) { |
d62a17ae | 320 | if ((adj->level & level) == 0) |
321 | continue; | |
322 | if (new_state == ISIS_ADJ_UP) { | |
1ee746d9 | 323 | circuit->adj_state_changes++; |
d62a17ae | 324 | circuit->upadjcount[level - 1]++; |
d62a17ae | 325 | /* update counter & timers for debugging |
326 | * purposes */ | |
327 | adj->last_flap = time(NULL); | |
328 | adj->flaps++; | |
8cea0065 | 329 | } else if (old_state == ISIS_ADJ_UP) { |
1ee746d9 | 330 | circuit->adj_state_changes++; |
58e16237 | 331 | |
d62a17ae | 332 | circuit->upadjcount[level - 1]--; |
58e16237 | 333 | if (circuit->upadjcount[level - 1] == 0) |
9b39405f | 334 | isis_tx_queue_clean(circuit->tx_queue); |
58e16237 | 335 | |
7209d2a4 IR |
336 | if (new_state == ISIS_ADJ_DOWN) { |
337 | listnode_delete( | |
338 | circuit->u.bc.adjdb[level - 1], | |
339 | adj); | |
340 | ||
8cea0065 | 341 | del = true; |
7209d2a4 | 342 | } |
d62a17ae | 343 | } |
344 | ||
345 | if (circuit->u.bc.lan_neighs[level - 1]) { | |
346 | list_delete_all_node( | |
347 | circuit->u.bc.lan_neighs[level - 1]); | |
348 | isis_adj_build_neigh_list( | |
349 | circuit->u.bc.adjdb[level - 1], | |
350 | circuit->u.bc.lan_neighs[level - 1]); | |
351 | } | |
352 | ||
353 | /* On adjacency state change send new pseudo LSP if we | |
354 | * are the DR */ | |
355 | if (circuit->u.bc.is_dr[level - 1]) | |
356 | lsp_regenerate_schedule_pseudo(circuit, level); | |
357 | } | |
358 | ||
d62a17ae | 359 | } else if (circuit->circ_type == CIRCUIT_T_P2P) { |
5346acec | 360 | for (int level = IS_LEVEL_1; level <= IS_LEVEL_2; level++) { |
d62a17ae | 361 | if ((adj->level & level) == 0) |
362 | continue; | |
363 | if (new_state == ISIS_ADJ_UP) { | |
364 | circuit->upadjcount[level - 1]++; | |
d62a17ae | 365 | |
d62a17ae | 366 | /* update counter & timers for debugging |
367 | * purposes */ | |
368 | adj->last_flap = time(NULL); | |
369 | adj->flaps++; | |
370 | ||
8e6fb83b CF |
371 | if (level == IS_LEVEL_1) { |
372 | thread_add_timer(master, send_l1_csnp, | |
373 | circuit, 0, | |
374 | &circuit->t_send_csnp[0]); | |
375 | } else { | |
376 | thread_add_timer(master, send_l2_csnp, | |
377 | circuit, 0, | |
378 | &circuit->t_send_csnp[1]); | |
379 | } | |
8cea0065 | 380 | } else if (old_state == ISIS_ADJ_UP) { |
d62a17ae | 381 | circuit->upadjcount[level - 1]--; |
58e16237 | 382 | if (circuit->upadjcount[level - 1] == 0) |
9b39405f | 383 | isis_tx_queue_clean(circuit->tx_queue); |
58e16237 | 384 | |
7209d2a4 IR |
385 | if (new_state == ISIS_ADJ_DOWN) { |
386 | if (adj->circuit->u.p2p.neighbor == adj) | |
387 | adj->circuit->u.p2p.neighbor = | |
388 | NULL; | |
389 | ||
8cea0065 | 390 | del = true; |
7209d2a4 | 391 | } |
d62a17ae | 392 | } |
393 | } | |
16167b31 | 394 | } |
d62a17ae | 395 | |
e4229afd EDP |
396 | hook_call(isis_adj_state_change_hook, adj); |
397 | ||
16167b31 DS |
398 | if (del) { |
399 | isis_delete_adj(adj); | |
400 | *padj = NULL; | |
d62a17ae | 401 | } |
eb5d44eb | 402 | } |
403 | ||
404 | ||
d62a17ae | 405 | void isis_adj_print(struct isis_adjacency *adj) |
eb5d44eb | 406 | { |
d62a17ae | 407 | struct isis_dynhn *dyn; |
d62a17ae | 408 | |
409 | if (!adj) | |
410 | return; | |
240f48b3 | 411 | dyn = dynhn_find_by_id(adj->circuit->isis, adj->sysid); |
d62a17ae | 412 | if (dyn) |
af8ac8f9 | 413 | zlog_debug("%s", dyn->hostname); |
d62a17ae | 414 | |
63efca0e | 415 | zlog_debug("SystemId %20s SNPA %s, level %d; Holding Time %d", |
d62a17ae | 416 | sysid_print(adj->sysid), snpa_print(adj->snpa), adj->level, |
417 | adj->hold_time); | |
0c1bd758 | 418 | if (adj->ipv4_address_count) { |
d62a17ae | 419 | zlog_debug("IPv4 Address(es):"); |
0c1bd758 | 420 | for (unsigned int i = 0; i < adj->ipv4_address_count; i++) |
a854ea43 | 421 | zlog_debug("%pI4", &adj->ipv4_addresses[i]); |
d62a17ae | 422 | } |
423 | ||
173f8887 | 424 | if (adj->ll_ipv6_count) { |
d62a17ae | 425 | zlog_debug("IPv6 Address(es):"); |
173f8887 | 426 | for (unsigned int i = 0; i < adj->ll_ipv6_count; i++) { |
0c1bd758 | 427 | char buf[INET6_ADDRSTRLEN]; |
173f8887 | 428 | inet_ntop(AF_INET6, &adj->ll_ipv6_addrs[i], buf, |
0c1bd758 CF |
429 | sizeof(buf)); |
430 | zlog_debug("%s", buf); | |
d62a17ae | 431 | } |
f390d2c7 | 432 | } |
d62a17ae | 433 | zlog_debug("Speaks: %s", nlpid2string(&adj->nlpids)); |
eb5d44eb | 434 | |
d62a17ae | 435 | return; |
eb5d44eb | 436 | } |
437 | ||
2a1c520e RW |
438 | const char *isis_adj_yang_state(enum isis_adj_state state) |
439 | { | |
440 | switch (state) { | |
441 | case ISIS_ADJ_DOWN: | |
442 | return "down"; | |
443 | case ISIS_ADJ_UP: | |
444 | return "up"; | |
445 | case ISIS_ADJ_INITIALIZING: | |
446 | return "init"; | |
447 | default: | |
448 | return "failed"; | |
449 | } | |
450 | } | |
451 | ||
cc9f21da | 452 | void isis_adj_expire(struct thread *thread) |
eb5d44eb | 453 | { |
d62a17ae | 454 | struct isis_adjacency *adj; |
eb5d44eb | 455 | |
d62a17ae | 456 | /* |
457 | * Get the adjacency | |
458 | */ | |
459 | adj = THREAD_ARG(thread); | |
460 | assert(adj); | |
461 | adj->t_expire = NULL; | |
eb5d44eb | 462 | |
d62a17ae | 463 | /* trigger the adj expire event */ |
16167b31 | 464 | isis_adj_state_change(&adj, ISIS_ADJ_DOWN, "holding time expired"); |
eb5d44eb | 465 | } |
466 | ||
a21177f2 JG |
467 | /* |
468 | * show isis neighbor [detail] json | |
469 | */ | |
470 | void isis_adj_print_json(struct isis_adjacency *adj, struct json_object *json, | |
471 | char detail) | |
472 | { | |
473 | json_object *iface_json, *ipv4_addr_json, *ipv6_link_json, | |
474 | *ipv6_non_link_json, *topo_json, *dis_flaps_json, | |
475 | *area_addr_json, *adj_sid_json; | |
476 | time_t now; | |
477 | struct isis_dynhn *dyn; | |
478 | int level; | |
479 | char buf[256]; | |
480 | ||
481 | json_object_string_add(json, "adj", isis_adj_name(adj)); | |
482 | ||
483 | if (detail == ISIS_UI_LEVEL_BRIEF) { | |
484 | if (adj->circuit) | |
485 | json_object_string_add(json, "interface", | |
486 | adj->circuit->interface->name); | |
487 | else | |
488 | json_object_string_add(json, "interface", | |
489 | "NULL circuit!"); | |
490 | json_object_int_add(json, "level", adj->level); | |
491 | json_object_string_add(json, "state", | |
492 | adj_state2string(adj->adj_state)); | |
493 | now = time(NULL); | |
494 | if (adj->last_upd) { | |
495 | if (adj->last_upd + adj->hold_time < now) | |
496 | json_object_string_add(json, "last-upd", | |
497 | "expiring"); | |
498 | else | |
499 | json_object_string_add( | |
500 | json, "expires-in", | |
501 | time2string(adj->last_upd + | |
502 | adj->hold_time - now)); | |
503 | } | |
504 | json_object_string_add(json, "snpa", snpa_print(adj->snpa)); | |
505 | } | |
506 | ||
507 | if (detail == ISIS_UI_LEVEL_DETAIL) { | |
508 | struct sr_adjacency *sra; | |
509 | struct listnode *anode; | |
510 | ||
511 | level = adj->level; | |
512 | iface_json = json_object_new_object(); | |
513 | json_object_object_add(json, "interface", iface_json); | |
514 | if (adj->circuit) | |
515 | json_object_string_add(iface_json, "name", | |
516 | adj->circuit->interface->name); | |
517 | else | |
518 | json_object_string_add(iface_json, "name", | |
519 | "null-circuit"); | |
520 | json_object_int_add(json, "level", adj->level); | |
521 | json_object_string_add(iface_json, "state", | |
522 | adj_state2string(adj->adj_state)); | |
523 | now = time(NULL); | |
524 | if (adj->last_upd) { | |
525 | if (adj->last_upd + adj->hold_time < now) | |
526 | json_object_string_add(iface_json, "last-upd", | |
527 | "expiring"); | |
528 | else | |
529 | json_object_string_add( | |
530 | json, "expires-in", | |
531 | time2string(adj->last_upd + | |
532 | adj->hold_time - now)); | |
533 | } else | |
534 | json_object_string_add(json, "expires-in", | |
535 | time2string(adj->hold_time)); | |
536 | json_object_int_add(iface_json, "adj-flaps", adj->flaps); | |
537 | json_object_string_add(iface_json, "last-ago", | |
538 | time2string(now - adj->last_flap)); | |
539 | json_object_string_add(iface_json, "circuit-type", | |
540 | circuit_t2string(adj->circuit_t)); | |
541 | json_object_string_add(iface_json, "speaks", | |
542 | nlpid2string(&adj->nlpids)); | |
543 | if (adj->mt_count != 1 || | |
544 | adj->mt_set[0] != ISIS_MT_IPV4_UNICAST) { | |
545 | topo_json = json_object_new_object(); | |
546 | json_object_object_add(iface_json, "topologies", | |
547 | topo_json); | |
548 | for (unsigned int i = 0; i < adj->mt_count; i++) { | |
549 | snprintfrr(buf, sizeof(buf), "topo-%d", i); | |
550 | json_object_string_add( | |
551 | topo_json, buf, | |
552 | isis_mtid2str(adj->mt_set[i])); | |
553 | } | |
554 | } | |
555 | json_object_string_add(iface_json, "snpa", | |
556 | snpa_print(adj->snpa)); | |
557 | if (adj->circuit && | |
558 | (adj->circuit->circ_type == CIRCUIT_T_BROADCAST)) { | |
559 | dyn = dynhn_find_by_id(adj->circuit->isis, adj->lanid); | |
560 | if (dyn) { | |
561 | snprintfrr(buf, sizeof(buf), "%s-%02x", | |
562 | dyn->hostname, | |
563 | adj->lanid[ISIS_SYS_ID_LEN]); | |
564 | json_object_string_add(iface_json, "lan-id", | |
565 | buf); | |
566 | } else { | |
567 | snprintfrr(buf, sizeof(buf), "%s-%02x", | |
568 | sysid_print(adj->lanid), | |
569 | adj->lanid[ISIS_SYS_ID_LEN]); | |
570 | json_object_string_add(iface_json, "lan-id", | |
571 | buf); | |
572 | } | |
573 | ||
574 | json_object_int_add(iface_json, "lan-prio", | |
575 | adj->prio[adj->level - 1]); | |
576 | ||
577 | dis_flaps_json = json_object_new_object(); | |
578 | json_object_object_add(iface_json, "dis-flaps", | |
579 | dis_flaps_json); | |
580 | json_object_string_add( | |
581 | dis_flaps_json, "dis-record", | |
582 | isis_disflag2string( | |
583 | adj->dis_record[ISIS_LEVELS + level - 1] | |
584 | .dis)); | |
585 | json_object_int_add(dis_flaps_json, "last", | |
586 | adj->dischanges[level - 1]); | |
587 | json_object_string_add( | |
588 | dis_flaps_json, "ago", | |
589 | time2string(now - (adj->dis_record[ISIS_LEVELS + | |
590 | level - 1] | |
591 | .last_dis_change))); | |
592 | } | |
593 | ||
594 | if (adj->area_address_count) { | |
595 | area_addr_json = json_object_new_object(); | |
596 | json_object_object_add(iface_json, "area-address", | |
597 | area_addr_json); | |
598 | for (unsigned int i = 0; i < adj->area_address_count; | |
599 | i++) { | |
600 | json_object_string_add( | |
601 | area_addr_json, "isonet", | |
602 | isonet_print(adj->area_addresses[i] | |
603 | .area_addr, | |
604 | adj->area_addresses[i] | |
605 | .addr_len)); | |
606 | } | |
607 | } | |
608 | if (adj->ipv4_address_count) { | |
609 | ipv4_addr_json = json_object_new_object(); | |
610 | json_object_object_add(iface_json, "ipv4-address", | |
611 | ipv4_addr_json); | |
612 | for (unsigned int i = 0; i < adj->ipv4_address_count; | |
613 | i++){ | |
614 | inet_ntop(AF_INET, &adj->ipv4_addresses[i], buf, | |
615 | sizeof(buf)); | |
616 | json_object_string_add(ipv4_addr_json, "ipv4", buf); | |
617 | } | |
618 | } | |
619 | if (adj->ll_ipv6_count) { | |
620 | ipv6_link_json = json_object_new_object(); | |
621 | json_object_object_add(iface_json, "ipv6-link-local", | |
622 | ipv6_link_json); | |
623 | for (unsigned int i = 0; i < adj->ll_ipv6_count; i++) { | |
624 | char buf[INET6_ADDRSTRLEN]; | |
625 | inet_ntop(AF_INET6, &adj->ll_ipv6_addrs[i], buf, | |
626 | sizeof(buf)); | |
627 | json_object_string_add(ipv6_link_json, "ipv6", | |
628 | buf); | |
629 | } | |
630 | } | |
631 | if (adj->global_ipv6_count) { | |
632 | ipv6_non_link_json = json_object_new_object(); | |
633 | json_object_object_add(iface_json, "ipv6-global", | |
634 | ipv6_non_link_json); | |
635 | for (unsigned int i = 0; i < adj->global_ipv6_count; | |
636 | i++) { | |
637 | char buf[INET6_ADDRSTRLEN]; | |
638 | inet_ntop(AF_INET6, &adj->global_ipv6_addrs[i], | |
639 | buf, sizeof(buf)); | |
640 | json_object_string_add(ipv6_non_link_json, | |
641 | "ipv6", buf); | |
642 | } | |
643 | } | |
644 | ||
645 | adj_sid_json = json_object_new_object(); | |
646 | json_object_object_add(iface_json, "adj-sid", adj_sid_json); | |
647 | for (ALL_LIST_ELEMENTS_RO(adj->adj_sids, anode, sra)) { | |
648 | const char *adj_type; | |
649 | const char *backup; | |
650 | uint32_t sid; | |
651 | ||
652 | switch (sra->adj->circuit->circ_type) { | |
653 | case CIRCUIT_T_BROADCAST: | |
654 | adj_type = "LAN Adjacency-SID"; | |
655 | sid = sra->u.ladj_sid->sid; | |
656 | break; | |
657 | case CIRCUIT_T_P2P: | |
658 | adj_type = "Adjacency-SID"; | |
659 | sid = sra->u.adj_sid->sid; | |
660 | break; | |
661 | default: | |
662 | continue; | |
663 | } | |
664 | backup = (sra->type == ISIS_SR_LAN_BACKUP) ? " (backup)" | |
665 | : ""; | |
666 | ||
667 | json_object_string_add(adj_sid_json, "nexthop", | |
668 | (sra->nexthop.family == AF_INET) | |
669 | ? "IPv4" | |
670 | : "IPv6"); | |
671 | json_object_string_add(adj_sid_json, "adj-type", | |
672 | adj_type); | |
673 | json_object_string_add(adj_sid_json, "is-backup", | |
674 | backup); | |
675 | json_object_int_add(adj_sid_json, "sid", sid); | |
676 | } | |
677 | } | |
678 | return; | |
679 | } | |
680 | ||
eb5d44eb | 681 | /* |
3f045a08 | 682 | * show isis neighbor [detail] |
eb5d44eb | 683 | */ |
d62a17ae | 684 | void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty, |
685 | char detail) | |
eb5d44eb | 686 | { |
d62a17ae | 687 | time_t now; |
688 | struct isis_dynhn *dyn; | |
689 | int level; | |
d62a17ae | 690 | |
0d5b1a3a | 691 | vty_out(vty, " %-20s", isis_adj_name(adj)); |
d62a17ae | 692 | |
693 | if (detail == ISIS_UI_LEVEL_BRIEF) { | |
694 | if (adj->circuit) | |
695 | vty_out(vty, "%-12s", adj->circuit->interface->name); | |
696 | else | |
697 | vty_out(vty, "NULL circuit!"); | |
698 | vty_out(vty, "%-3u", adj->level); /* level */ | |
699 | vty_out(vty, "%-13s", adj_state2string(adj->adj_state)); | |
700 | now = time(NULL); | |
3cbe31c7 | 701 | if (adj->last_upd) { |
ac716cdf | 702 | if (adj->last_upd + adj->hold_time < now) |
3cbe31c7 EDP |
703 | vty_out(vty, " Expiring"); |
704 | else | |
705 | vty_out(vty, " %-9llu", | |
706 | (unsigned long long)adj->last_upd | |
707 | + adj->hold_time - now); | |
708 | } else | |
d62a17ae | 709 | vty_out(vty, "- "); |
710 | vty_out(vty, "%-10s", snpa_print(adj->snpa)); | |
711 | vty_out(vty, "\n"); | |
f390d2c7 | 712 | } |
d62a17ae | 713 | |
714 | if (detail == ISIS_UI_LEVEL_DETAIL) { | |
26f6acaf RW |
715 | struct sr_adjacency *sra; |
716 | struct listnode *anode; | |
717 | ||
d62a17ae | 718 | level = adj->level; |
719 | vty_out(vty, "\n"); | |
720 | if (adj->circuit) | |
721 | vty_out(vty, " Interface: %s", | |
722 | adj->circuit->interface->name); | |
723 | else | |
724 | vty_out(vty, " Interface: NULL circuit"); | |
725 | vty_out(vty, ", Level: %u", adj->level); /* level */ | |
726 | vty_out(vty, ", State: %s", adj_state2string(adj->adj_state)); | |
727 | now = time(NULL); | |
3cbe31c7 | 728 | if (adj->last_upd) { |
ac716cdf | 729 | if (adj->last_upd + adj->hold_time < now) |
3cbe31c7 EDP |
730 | vty_out(vty, " Expiring"); |
731 | else | |
732 | vty_out(vty, ", Expires in %s", | |
733 | time2string(adj->last_upd | |
734 | + adj->hold_time - now)); | |
735 | } else | |
d62a17ae | 736 | vty_out(vty, ", Expires in %s", |
737 | time2string(adj->hold_time)); | |
738 | vty_out(vty, "\n"); | |
739 | vty_out(vty, " Adjacency flaps: %u", adj->flaps); | |
740 | vty_out(vty, ", Last: %s ago", | |
741 | time2string(now - adj->last_flap)); | |
742 | vty_out(vty, "\n"); | |
743 | vty_out(vty, " Circuit type: %s", | |
744 | circuit_t2string(adj->circuit_t)); | |
745 | vty_out(vty, ", Speaks: %s", nlpid2string(&adj->nlpids)); | |
746 | vty_out(vty, "\n"); | |
747 | if (adj->mt_count != 1 | |
748 | || adj->mt_set[0] != ISIS_MT_IPV4_UNICAST) { | |
749 | vty_out(vty, " Topologies:\n"); | |
750 | for (unsigned int i = 0; i < adj->mt_count; i++) | |
751 | vty_out(vty, " %s\n", | |
752 | isis_mtid2str(adj->mt_set[i])); | |
753 | } | |
754 | vty_out(vty, " SNPA: %s", snpa_print(adj->snpa)); | |
755 | if (adj->circuit | |
756 | && (adj->circuit->circ_type == CIRCUIT_T_BROADCAST)) { | |
240f48b3 | 757 | dyn = dynhn_find_by_id(adj->circuit->isis, adj->lanid); |
d62a17ae | 758 | if (dyn) |
af8ac8f9 | 759 | vty_out(vty, ", LAN id: %s.%02x", dyn->hostname, |
d62a17ae | 760 | adj->lanid[ISIS_SYS_ID_LEN]); |
761 | else | |
762 | vty_out(vty, ", LAN id: %s.%02x", | |
763 | sysid_print(adj->lanid), | |
764 | adj->lanid[ISIS_SYS_ID_LEN]); | |
765 | ||
766 | vty_out(vty, "\n"); | |
767 | vty_out(vty, " LAN Priority: %u", | |
768 | adj->prio[adj->level - 1]); | |
769 | ||
770 | vty_out(vty, ", %s, DIS flaps: %u, Last: %s ago", | |
771 | isis_disflag2string( | |
772 | adj->dis_record[ISIS_LEVELS + level - 1] | |
773 | .dis), | |
774 | adj->dischanges[level - 1], | |
9d303b37 DL |
775 | time2string(now - (adj->dis_record[ISIS_LEVELS |
776 | + level - 1] | |
777 | .last_dis_change))); | |
d62a17ae | 778 | } |
779 | vty_out(vty, "\n"); | |
780 | ||
0c1bd758 | 781 | if (adj->area_address_count) { |
d62a17ae | 782 | vty_out(vty, " Area Address(es):\n"); |
0c1bd758 CF |
783 | for (unsigned int i = 0; i < adj->area_address_count; |
784 | i++) { | |
d62a17ae | 785 | vty_out(vty, " %s\n", |
996c9314 LB |
786 | isonet_print(adj->area_addresses[i] |
787 | .area_addr, | |
788 | adj->area_addresses[i] | |
789 | .addr_len)); | |
0c1bd758 | 790 | } |
d62a17ae | 791 | } |
0c1bd758 | 792 | if (adj->ipv4_address_count) { |
d62a17ae | 793 | vty_out(vty, " IPv4 Address(es):\n"); |
0c1bd758 CF |
794 | for (unsigned int i = 0; i < adj->ipv4_address_count; |
795 | i++) | |
a854ea43 MS |
796 | vty_out(vty, " %pI4\n", |
797 | &adj->ipv4_addresses[i]); | |
d62a17ae | 798 | } |
173f8887 | 799 | if (adj->ll_ipv6_count) { |
d62a17ae | 800 | vty_out(vty, " IPv6 Address(es):\n"); |
173f8887 OD |
801 | for (unsigned int i = 0; i < adj->ll_ipv6_count; i++) { |
802 | char buf[INET6_ADDRSTRLEN]; | |
803 | inet_ntop(AF_INET6, &adj->ll_ipv6_addrs[i], | |
804 | buf, sizeof(buf)); | |
805 | vty_out(vty, " %s\n", buf); | |
806 | } | |
807 | } | |
808 | if (adj->global_ipv6_count) { | |
809 | vty_out(vty, " Global IPv6 Address(es):\n"); | |
810 | for (unsigned int i = 0; i < adj->global_ipv6_count; | |
0c1bd758 CF |
811 | i++) { |
812 | char buf[INET6_ADDRSTRLEN]; | |
173f8887 | 813 | inet_ntop(AF_INET6, &adj->global_ipv6_addrs[i], |
0c1bd758 CF |
814 | buf, sizeof(buf)); |
815 | vty_out(vty, " %s\n", buf); | |
d62a17ae | 816 | } |
817 | } | |
ae59cfd7 PG |
818 | if (adj->circuit && adj->circuit->bfd_config.enabled) { |
819 | vty_out(vty, " BFD is %s%s\n", | |
820 | adj->bfd_session ? "active, status " | |
821 | : "configured", | |
822 | !adj->bfd_session | |
823 | ? "" | |
824 | : bfd_get_status_str(bfd_sess_status( | |
825 | adj->bfd_session))); | |
826 | } | |
26f6acaf RW |
827 | for (ALL_LIST_ELEMENTS_RO(adj->adj_sids, anode, sra)) { |
828 | const char *adj_type; | |
829 | const char *backup; | |
830 | uint32_t sid; | |
831 | ||
832 | switch (sra->adj->circuit->circ_type) { | |
833 | case CIRCUIT_T_BROADCAST: | |
834 | adj_type = "LAN Adjacency-SID"; | |
835 | sid = sra->u.ladj_sid->sid; | |
836 | break; | |
837 | case CIRCUIT_T_P2P: | |
838 | adj_type = "Adjacency-SID"; | |
839 | sid = sra->u.adj_sid->sid; | |
840 | break; | |
841 | default: | |
842 | continue; | |
843 | } | |
844 | backup = (sra->type == ISIS_SR_LAN_BACKUP) ? " (backup)" | |
845 | : ""; | |
846 | ||
847 | vty_out(vty, " %s %s%s: %u\n", | |
848 | (sra->nexthop.family == AF_INET) ? "IPv4" | |
849 | : "IPv6", | |
850 | adj_type, backup, sid); | |
851 | } | |
d62a17ae | 852 | vty_out(vty, "\n"); |
f390d2c7 | 853 | } |
d62a17ae | 854 | return; |
eb5d44eb | 855 | } |
856 | ||
d62a17ae | 857 | void isis_adj_build_neigh_list(struct list *adjdb, struct list *list) |
eb5d44eb | 858 | { |
d62a17ae | 859 | struct isis_adjacency *adj; |
860 | struct listnode *node; | |
861 | ||
862 | if (!list) { | |
863 | zlog_warn("isis_adj_build_neigh_list(): NULL list"); | |
864 | return; | |
f390d2c7 | 865 | } |
866 | ||
d62a17ae | 867 | for (ALL_LIST_ELEMENTS_RO(adjdb, node, adj)) { |
868 | if (!adj) { | |
869 | zlog_warn("isis_adj_build_neigh_list(): NULL adj"); | |
870 | return; | |
871 | } | |
872 | ||
873 | if ((adj->adj_state == ISIS_ADJ_UP | |
874 | || adj->adj_state == ISIS_ADJ_INITIALIZING)) | |
875 | listnode_add(list, adj->snpa); | |
876 | } | |
877 | return; | |
eb5d44eb | 878 | } |
879 | ||
d62a17ae | 880 | void isis_adj_build_up_list(struct list *adjdb, struct list *list) |
eb5d44eb | 881 | { |
d62a17ae | 882 | struct isis_adjacency *adj; |
883 | struct listnode *node; | |
884 | ||
885 | if (adjdb == NULL) { | |
886 | zlog_warn("isis_adj_build_up_list(): adjacency DB is empty"); | |
887 | return; | |
888 | } | |
889 | ||
890 | if (!list) { | |
891 | zlog_warn("isis_adj_build_up_list(): NULL list"); | |
892 | return; | |
f390d2c7 | 893 | } |
894 | ||
d62a17ae | 895 | for (ALL_LIST_ELEMENTS_RO(adjdb, node, adj)) { |
896 | if (!adj) { | |
897 | zlog_warn("isis_adj_build_up_list(): NULL adj"); | |
898 | return; | |
899 | } | |
f390d2c7 | 900 | |
d62a17ae | 901 | if (adj->adj_state == ISIS_ADJ_UP) |
902 | listnode_add(list, adj); | |
903 | } | |
904 | ||
905 | return; | |
eb5d44eb | 906 | } |
d8fba7d9 | 907 | |
d62a17ae | 908 | int isis_adj_usage2levels(enum isis_adj_usage usage) |
d8fba7d9 | 909 | { |
d62a17ae | 910 | switch (usage) { |
911 | case ISIS_ADJ_LEVEL1: | |
912 | return IS_LEVEL_1; | |
913 | case ISIS_ADJ_LEVEL2: | |
914 | return IS_LEVEL_2; | |
915 | case ISIS_ADJ_LEVEL1AND2: | |
916 | return IS_LEVEL_1 | IS_LEVEL_2; | |
917 | default: | |
918 | break; | |
919 | } | |
920 | return 0; | |
d8fba7d9 | 921 | } |