2 * IS-IS Rout(e)ing protocol - isis_adjacency.c
3 * handling of IS-IS adjacencies
5 * Copyright (C) 2001,2002 Sampo Saaristo
6 * Tampere University of Technology
7 * Institute of Communications Engineering
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)
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
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
35 #include "isisd/isis_constants.h"
36 #include "isisd/isis_common.h"
37 #include "isisd/isis_flags.h"
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"
45 #include "isisd/isis_lsp.h"
46 #include "isisd/isis_spf.h"
47 #include "isisd/isis_events.h"
48 #include "isisd/isis_mt.h"
49 #include "isisd/isis_tlvs.h"
50 #include "isisd/fabricd.h"
51 #include "isisd/isis_nb.h"
53 extern struct isis
*isis
;
55 static struct isis_adjacency
*adj_alloc(const uint8_t *id
)
57 struct isis_adjacency
*adj
;
59 adj
= XCALLOC(MTYPE_ISIS_ADJACENCY
, sizeof(struct isis_adjacency
));
60 memcpy(adj
->sysid
, id
, ISIS_SYS_ID_LEN
);
65 struct isis_adjacency
*isis_new_adj(const uint8_t *id
, const uint8_t *snpa
,
66 int level
, struct isis_circuit
*circuit
)
68 struct isis_adjacency
*adj
;
71 adj
= adj_alloc(id
); /* P2P kludge */
74 memcpy(adj
->snpa
, snpa
, ETH_ALEN
);
76 memset(adj
->snpa
, ' ', ETH_ALEN
);
79 adj
->circuit
= circuit
;
82 adj
->last_flap
= time(NULL
);
83 adj
->threeway_state
= ISIS_THREEWAY_DOWN
;
84 if (circuit
->circ_type
== CIRCUIT_T_BROADCAST
) {
85 listnode_add(circuit
->u
.bc
.adjdb
[level
- 1], adj
);
86 adj
->dischanges
[level
- 1] = 0;
87 for (i
= 0; i
< DIS_RECORDS
;
88 i
++) /* clear N DIS state change records */
90 adj
->dis_record
[(i
* ISIS_LEVELS
) + level
- 1].dis
=
92 adj
->dis_record
[(i
* ISIS_LEVELS
) + level
- 1]
93 .last_dis_change
= time(NULL
);
100 struct isis_adjacency
*isis_adj_lookup(const uint8_t *sysid
, struct list
*adjdb
)
102 struct isis_adjacency
*adj
;
103 struct listnode
*node
;
105 for (ALL_LIST_ELEMENTS_RO(adjdb
, node
, adj
))
106 if (memcmp(adj
->sysid
, sysid
, ISIS_SYS_ID_LEN
) == 0)
112 struct isis_adjacency
*isis_adj_lookup_snpa(const uint8_t *ssnpa
,
115 struct listnode
*node
;
116 struct isis_adjacency
*adj
;
118 for (ALL_LIST_ELEMENTS_RO(adjdb
, node
, adj
))
119 if (memcmp(adj
->snpa
, ssnpa
, ETH_ALEN
) == 0)
125 DEFINE_HOOK(isis_adj_state_change_hook
, (struct isis_adjacency
*adj
), (adj
))
127 void isis_delete_adj(void *arg
)
129 struct isis_adjacency
*adj
= arg
;
134 THREAD_TIMER_OFF(adj
->t_expire
);
135 if (adj
->adj_state
!= ISIS_ADJ_DOWN
) {
136 adj
->adj_state
= ISIS_ADJ_DOWN
;
137 hook_call(isis_adj_state_change_hook
, adj
);
140 /* remove from SPF trees */
141 spftree_area_adj_del(adj
->circuit
->area
, adj
);
143 XFREE(MTYPE_ISIS_ADJACENCY_INFO
, adj
->area_addresses
);
144 XFREE(MTYPE_ISIS_ADJACENCY_INFO
, adj
->ipv4_addresses
);
145 XFREE(MTYPE_ISIS_ADJACENCY_INFO
, adj
->ipv6_addresses
);
149 XFREE(MTYPE_ISIS_ADJACENCY
, adj
);
153 static const char *adj_state2string(int state
)
157 case ISIS_ADJ_INITIALIZING
:
158 return "Initializing";
167 return NULL
; /* not reached */
170 void isis_adj_process_threeway(struct isis_adjacency
*adj
,
171 struct isis_threeway_adj
*tw_adj
,
172 enum isis_adj_usage adj_usage
)
174 enum isis_threeway_state next_tw_state
= ISIS_THREEWAY_DOWN
;
176 if (tw_adj
&& !adj
->circuit
->disable_threeway_adj
) {
177 if (tw_adj
->state
== ISIS_THREEWAY_DOWN
) {
178 next_tw_state
= ISIS_THREEWAY_INITIALIZING
;
179 } else if (tw_adj
->state
== ISIS_THREEWAY_INITIALIZING
) {
180 next_tw_state
= ISIS_THREEWAY_UP
;
181 } else if (tw_adj
->state
== ISIS_THREEWAY_UP
) {
182 if (adj
->threeway_state
== ISIS_THREEWAY_DOWN
)
183 next_tw_state
= ISIS_THREEWAY_DOWN
;
185 next_tw_state
= ISIS_THREEWAY_UP
;
188 next_tw_state
= ISIS_THREEWAY_UP
;
191 if (next_tw_state
!= adj
->threeway_state
) {
192 if (isis
->debugs
& DEBUG_ADJ_PACKETS
) {
193 zlog_info("ISIS-Adj (%s): Threeway state change %s to %s",
194 adj
->circuit
->area
->area_tag
,
195 isis_threeway_state_name(adj
->threeway_state
),
196 isis_threeway_state_name(next_tw_state
));
200 if (next_tw_state
!= ISIS_THREEWAY_DOWN
)
201 fabricd_initial_sync_hello(adj
->circuit
);
203 if (next_tw_state
== ISIS_THREEWAY_DOWN
) {
204 isis_adj_state_change(adj
, ISIS_ADJ_DOWN
, "Neighbor restarted");
208 if (next_tw_state
== ISIS_THREEWAY_UP
) {
209 if (adj
->adj_state
!= ISIS_ADJ_UP
) {
210 isis_adj_state_change(adj
, ISIS_ADJ_UP
, NULL
);
211 adj
->adj_usage
= adj_usage
;
215 if (adj
->threeway_state
!= next_tw_state
) {
216 send_hello_sched(adj
->circuit
, 0, TRIGGERED_IIH_DELAY
);
219 adj
->threeway_state
= next_tw_state
;
222 void isis_adj_state_change(struct isis_adjacency
*adj
,
223 enum isis_adj_state new_state
, const char *reason
)
225 enum isis_adj_state old_state
= adj
->adj_state
;
226 struct isis_circuit
*circuit
= adj
->circuit
;
229 adj
->adj_state
= new_state
;
230 if (new_state
!= old_state
) {
231 send_hello_sched(circuit
, adj
->level
, TRIGGERED_IIH_DELAY
);
234 if (isis
->debugs
& DEBUG_ADJ_PACKETS
) {
235 zlog_debug("ISIS-Adj (%s): Adjacency state change %d->%d: %s",
236 circuit
->area
->area_tag
, old_state
, new_state
,
237 reason
? reason
: "unspecified");
240 if (circuit
->area
->log_adj_changes
) {
241 const char *adj_name
;
242 struct isis_dynhn
*dyn
;
244 dyn
= dynhn_find_by_id(adj
->sysid
);
246 adj_name
= dyn
->hostname
;
248 adj_name
= sysid_print(adj
->sysid
);
251 "%%ADJCHANGE: Adjacency to %s (%s) changed from %s to %s, %s",
252 adj_name
, adj
->circuit
->interface
->name
,
253 adj_state2string(old_state
),
254 adj_state2string(new_state
),
255 reason
? reason
: "unspecified");
258 circuit
->adj_state_changes
++;
260 /* send northbound notification */
261 isis_notif_adj_state_change(adj
, new_state
, reason
);
262 #endif /* ifndef FABRICD */
264 if (circuit
->circ_type
== CIRCUIT_T_BROADCAST
) {
266 for (int level
= IS_LEVEL_1
; level
<= IS_LEVEL_2
; level
++) {
267 if ((adj
->level
& level
) == 0)
269 if (new_state
== ISIS_ADJ_UP
) {
270 circuit
->upadjcount
[level
- 1]++;
271 hook_call(isis_adj_state_change_hook
, adj
);
272 /* update counter & timers for debugging
274 adj
->last_flap
= time(NULL
);
276 } else if (new_state
== ISIS_ADJ_DOWN
) {
277 listnode_delete(circuit
->u
.bc
.adjdb
[level
- 1],
280 circuit
->upadjcount
[level
- 1]--;
281 if (circuit
->upadjcount
[level
- 1] == 0)
282 isis_tx_queue_clean(circuit
->tx_queue
);
284 hook_call(isis_adj_state_change_hook
, adj
);
288 if (circuit
->u
.bc
.lan_neighs
[level
- 1]) {
289 list_delete_all_node(
290 circuit
->u
.bc
.lan_neighs
[level
- 1]);
291 isis_adj_build_neigh_list(
292 circuit
->u
.bc
.adjdb
[level
- 1],
293 circuit
->u
.bc
.lan_neighs
[level
- 1]);
296 /* On adjacency state change send new pseudo LSP if we
298 if (circuit
->u
.bc
.is_dr
[level
- 1])
299 lsp_regenerate_schedule_pseudo(circuit
, level
);
303 isis_delete_adj(adj
);
305 } else if (circuit
->circ_type
== CIRCUIT_T_P2P
) {
307 for (int level
= IS_LEVEL_1
; level
<= IS_LEVEL_2
; level
++) {
308 if ((adj
->level
& level
) == 0)
310 if (new_state
== ISIS_ADJ_UP
) {
311 circuit
->upadjcount
[level
- 1]++;
312 hook_call(isis_adj_state_change_hook
, adj
);
314 /* update counter & timers for debugging
316 adj
->last_flap
= time(NULL
);
319 if (level
== IS_LEVEL_1
) {
320 thread_add_timer(master
, send_l1_csnp
,
322 &circuit
->t_send_csnp
[0]);
324 thread_add_timer(master
, send_l2_csnp
,
326 &circuit
->t_send_csnp
[1]);
328 } else if (new_state
== ISIS_ADJ_DOWN
) {
329 if (adj
->circuit
->u
.p2p
.neighbor
== adj
)
330 adj
->circuit
->u
.p2p
.neighbor
= NULL
;
331 circuit
->upadjcount
[level
- 1]--;
332 if (circuit
->upadjcount
[level
- 1] == 0)
333 isis_tx_queue_clean(circuit
->tx_queue
);
335 hook_call(isis_adj_state_change_hook
, adj
);
341 isis_delete_adj(adj
);
346 void isis_adj_print(struct isis_adjacency
*adj
)
348 struct isis_dynhn
*dyn
;
352 dyn
= dynhn_find_by_id(adj
->sysid
);
354 zlog_debug("%s", dyn
->hostname
);
356 zlog_debug("SystemId %20s SNPA %s, level %d\nHolding Time %d",
357 sysid_print(adj
->sysid
), snpa_print(adj
->snpa
), adj
->level
,
359 if (adj
->ipv4_address_count
) {
360 zlog_debug("IPv4 Address(es):");
361 for (unsigned int i
= 0; i
< adj
->ipv4_address_count
; i
++)
362 zlog_debug("%s", inet_ntoa(adj
->ipv4_addresses
[i
]));
365 if (adj
->ipv6_address_count
) {
366 zlog_debug("IPv6 Address(es):");
367 for (unsigned int i
= 0; i
< adj
->ipv6_address_count
; i
++) {
368 char buf
[INET6_ADDRSTRLEN
];
369 inet_ntop(AF_INET6
, &adj
->ipv6_addresses
[i
], buf
,
371 zlog_debug("%s", buf
);
374 zlog_debug("Speaks: %s", nlpid2string(&adj
->nlpids
));
379 const char *isis_adj_yang_state(enum isis_adj_state state
)
386 case ISIS_ADJ_INITIALIZING
:
393 int isis_adj_expire(struct thread
*thread
)
395 struct isis_adjacency
*adj
;
400 adj
= THREAD_ARG(thread
);
402 adj
->t_expire
= NULL
;
404 /* trigger the adj expire event */
405 isis_adj_state_change(adj
, ISIS_ADJ_DOWN
, "holding time expired");
411 * show isis neighbor [detail]
413 void isis_adj_print_vty(struct isis_adjacency
*adj
, struct vty
*vty
,
417 struct isis_dynhn
*dyn
;
420 dyn
= dynhn_find_by_id(adj
->sysid
);
422 vty_out(vty
, " %-20s", dyn
->hostname
);
424 vty_out(vty
, " %-20s", sysid_print(adj
->sysid
));
426 if (detail
== ISIS_UI_LEVEL_BRIEF
) {
428 vty_out(vty
, "%-12s", adj
->circuit
->interface
->name
);
430 vty_out(vty
, "NULL circuit!");
431 vty_out(vty
, "%-3u", adj
->level
); /* level */
432 vty_out(vty
, "%-13s", adj_state2string(adj
->adj_state
));
435 vty_out(vty
, "%-9llu",
436 (unsigned long long)adj
->last_upd
437 + adj
->hold_time
- now
);
440 vty_out(vty
, "%-10s", snpa_print(adj
->snpa
));
444 if (detail
== ISIS_UI_LEVEL_DETAIL
) {
448 vty_out(vty
, " Interface: %s",
449 adj
->circuit
->interface
->name
);
451 vty_out(vty
, " Interface: NULL circuit");
452 vty_out(vty
, ", Level: %u", adj
->level
); /* level */
453 vty_out(vty
, ", State: %s", adj_state2string(adj
->adj_state
));
456 vty_out(vty
, ", Expires in %s",
457 time2string(adj
->last_upd
+ adj
->hold_time
460 vty_out(vty
, ", Expires in %s",
461 time2string(adj
->hold_time
));
463 vty_out(vty
, " Adjacency flaps: %u", adj
->flaps
);
464 vty_out(vty
, ", Last: %s ago",
465 time2string(now
- adj
->last_flap
));
467 vty_out(vty
, " Circuit type: %s",
468 circuit_t2string(adj
->circuit_t
));
469 vty_out(vty
, ", Speaks: %s", nlpid2string(&adj
->nlpids
));
471 if (adj
->mt_count
!= 1
472 || adj
->mt_set
[0] != ISIS_MT_IPV4_UNICAST
) {
473 vty_out(vty
, " Topologies:\n");
474 for (unsigned int i
= 0; i
< adj
->mt_count
; i
++)
475 vty_out(vty
, " %s\n",
476 isis_mtid2str(adj
->mt_set
[i
]));
478 vty_out(vty
, " SNPA: %s", snpa_print(adj
->snpa
));
480 && (adj
->circuit
->circ_type
== CIRCUIT_T_BROADCAST
)) {
481 dyn
= dynhn_find_by_id(adj
->lanid
);
483 vty_out(vty
, ", LAN id: %s.%02x", dyn
->hostname
,
484 adj
->lanid
[ISIS_SYS_ID_LEN
]);
486 vty_out(vty
, ", LAN id: %s.%02x",
487 sysid_print(adj
->lanid
),
488 adj
->lanid
[ISIS_SYS_ID_LEN
]);
491 vty_out(vty
, " LAN Priority: %u",
492 adj
->prio
[adj
->level
- 1]);
494 vty_out(vty
, ", %s, DIS flaps: %u, Last: %s ago",
496 adj
->dis_record
[ISIS_LEVELS
+ level
- 1]
498 adj
->dischanges
[level
- 1],
499 time2string(now
- (adj
->dis_record
[ISIS_LEVELS
505 if (adj
->area_address_count
) {
506 vty_out(vty
, " Area Address(es):\n");
507 for (unsigned int i
= 0; i
< adj
->area_address_count
;
509 vty_out(vty
, " %s\n",
510 isonet_print(adj
->area_addresses
[i
]
512 adj
->area_addresses
[i
]
516 if (adj
->ipv4_address_count
) {
517 vty_out(vty
, " IPv4 Address(es):\n");
518 for (unsigned int i
= 0; i
< adj
->ipv4_address_count
;
520 vty_out(vty
, " %s\n",
521 inet_ntoa(adj
->ipv4_addresses
[i
]));
523 if (adj
->ipv6_address_count
) {
524 vty_out(vty
, " IPv6 Address(es):\n");
525 for (unsigned int i
= 0; i
< adj
->ipv6_address_count
;
527 char buf
[INET6_ADDRSTRLEN
];
528 inet_ntop(AF_INET6
, &adj
->ipv6_addresses
[i
],
530 vty_out(vty
, " %s\n", buf
);
538 void isis_adj_build_neigh_list(struct list
*adjdb
, struct list
*list
)
540 struct isis_adjacency
*adj
;
541 struct listnode
*node
;
544 zlog_warn("isis_adj_build_neigh_list(): NULL list");
548 for (ALL_LIST_ELEMENTS_RO(adjdb
, node
, adj
)) {
550 zlog_warn("isis_adj_build_neigh_list(): NULL adj");
554 if ((adj
->adj_state
== ISIS_ADJ_UP
555 || adj
->adj_state
== ISIS_ADJ_INITIALIZING
))
556 listnode_add(list
, adj
->snpa
);
561 void isis_adj_build_up_list(struct list
*adjdb
, struct list
*list
)
563 struct isis_adjacency
*adj
;
564 struct listnode
*node
;
567 zlog_warn("isis_adj_build_up_list(): adjacency DB is empty");
572 zlog_warn("isis_adj_build_up_list(): NULL list");
576 for (ALL_LIST_ELEMENTS_RO(adjdb
, node
, adj
)) {
578 zlog_warn("isis_adj_build_up_list(): NULL adj");
582 if (adj
->adj_state
== ISIS_ADJ_UP
)
583 listnode_add(list
, adj
);
589 int isis_adj_usage2levels(enum isis_adj_usage usage
)
592 case ISIS_ADJ_LEVEL1
:
594 case ISIS_ADJ_LEVEL2
:
596 case ISIS_ADJ_LEVEL1AND2
:
597 return IS_LEVEL_1
| IS_LEVEL_2
;