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