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_events.h"
47 #include "isisd/isis_mt.h"
48 #include "isisd/isis_tlvs.h"
49 #include "isisd/fabricd.h"
50 #include "isisd/isis_nb.h"
52 DEFINE_MTYPE_STATIC(ISISD
, ISIS_ADJACENCY
, "ISIS adjacency");
53 DEFINE_MTYPE(ISISD
, ISIS_ADJACENCY_INFO
, "ISIS adjacency info");
55 static struct isis_adjacency
*adj_alloc(struct isis_circuit
*circuit
,
58 struct isis_adjacency
*adj
;
60 adj
= XCALLOC(MTYPE_ISIS_ADJACENCY
, sizeof(struct isis_adjacency
));
61 memcpy(adj
->sysid
, id
, ISIS_SYS_ID_LEN
);
63 adj
->snmp_idx
= ++circuit
->snmp_adj_idx_gen
;
65 if (circuit
->snmp_adj_list
== NULL
)
66 circuit
->snmp_adj_list
= list_new();
68 adj
->snmp_list_node
= listnode_add(circuit
->snmp_adj_list
, adj
);
73 struct isis_adjacency
*isis_new_adj(const uint8_t *id
, const uint8_t *snpa
,
74 int level
, struct isis_circuit
*circuit
)
76 struct isis_adjacency
*adj
;
79 adj
= adj_alloc(circuit
, id
); /* P2P kludge */
82 memcpy(adj
->snpa
, snpa
, ETH_ALEN
);
84 memset(adj
->snpa
, ' ', ETH_ALEN
);
87 adj
->circuit
= circuit
;
90 adj
->last_flap
= time(NULL
);
91 adj
->threeway_state
= ISIS_THREEWAY_DOWN
;
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 */
98 adj
->dis_record
[(i
* ISIS_LEVELS
) + level
- 1].dis
=
100 adj
->dis_record
[(i
* ISIS_LEVELS
) + level
- 1]
101 .last_dis_change
= time(NULL
);
104 adj
->adj_sids
= list_new();
105 listnode_add(circuit
->area
->adjacency_list
, adj
);
110 struct isis_adjacency
*isis_adj_lookup(const uint8_t *sysid
, struct list
*adjdb
)
112 struct isis_adjacency
*adj
;
113 struct listnode
*node
;
115 for (ALL_LIST_ELEMENTS_RO(adjdb
, node
, adj
))
116 if (memcmp(adj
->sysid
, sysid
, ISIS_SYS_ID_LEN
) == 0)
122 struct isis_adjacency
*isis_adj_lookup_snpa(const uint8_t *ssnpa
,
125 struct listnode
*node
;
126 struct isis_adjacency
*adj
;
128 for (ALL_LIST_ELEMENTS_RO(adjdb
, node
, adj
))
129 if (memcmp(adj
->snpa
, ssnpa
, ETH_ALEN
) == 0)
135 struct isis_adjacency
*isis_adj_find(const struct isis_area
*area
, int level
,
136 const uint8_t *sysid
)
138 struct isis_adjacency
*adj
;
139 struct listnode
*node
;
141 for (ALL_LIST_ELEMENTS_RO(area
->adjacency_list
, node
, adj
)) {
142 if (!(adj
->level
& level
))
145 if (!memcmp(adj
->sysid
, sysid
, ISIS_SYS_ID_LEN
))
152 DEFINE_HOOK(isis_adj_state_change_hook
, (struct isis_adjacency
*adj
), (adj
));
154 void isis_delete_adj(void *arg
)
156 struct isis_adjacency
*adj
= arg
;
160 /* Remove self from snmp list without walking the list*/
161 list_delete_node(adj
->circuit
->snmp_adj_list
, adj
->snmp_list_node
);
163 thread_cancel(&adj
->t_expire
);
164 if (adj
->adj_state
!= ISIS_ADJ_DOWN
)
165 adj
->adj_state
= ISIS_ADJ_DOWN
;
167 hook_call(isis_adj_state_change_hook
, adj
);
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
);
174 list_delete(&adj
->adj_sids
);
176 listnode_delete(adj
->circuit
->area
->adjacency_list
, adj
);
177 XFREE(MTYPE_ISIS_ADJACENCY
, adj
);
181 static const char *adj_state2string(int state
)
185 case ISIS_ADJ_INITIALIZING
:
186 return "Initializing";
195 return NULL
; /* not reached */
198 static const char *adj_level2string(int level
)
205 case IS_LEVEL_1_AND_2
:
211 return NULL
; /* not reached */
214 void isis_adj_process_threeway(struct isis_adjacency
*adj
,
215 struct isis_threeway_adj
*tw_adj
,
216 enum isis_adj_usage adj_usage
)
218 enum isis_threeway_state next_tw_state
= ISIS_THREEWAY_DOWN
;
220 if (tw_adj
&& !adj
->circuit
->disable_threeway_adj
) {
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
;
229 next_tw_state
= ISIS_THREEWAY_UP
;
232 next_tw_state
= ISIS_THREEWAY_UP
;
235 if (next_tw_state
!= adj
->threeway_state
) {
236 if (IS_DEBUG_ADJ_PACKETS
) {
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
));
244 if (next_tw_state
!= ISIS_THREEWAY_DOWN
)
245 fabricd_initial_sync_hello(adj
->circuit
);
247 if (next_tw_state
== ISIS_THREEWAY_DOWN
) {
248 isis_adj_state_change(&adj
, ISIS_ADJ_DOWN
,
249 "Neighbor restarted");
253 if (next_tw_state
== ISIS_THREEWAY_UP
) {
254 if (adj
->adj_state
!= ISIS_ADJ_UP
) {
255 isis_adj_state_change(&adj
, ISIS_ADJ_UP
, NULL
);
256 adj
->adj_usage
= adj_usage
;
260 if (adj
->threeway_state
!= next_tw_state
) {
261 send_hello_sched(adj
->circuit
, 0, TRIGGERED_IIH_DELAY
);
264 adj
->threeway_state
= next_tw_state
;
266 const char *isis_adj_name(const struct isis_adjacency
*adj
)
271 struct isis_dynhn
*dyn
;
273 dyn
= dynhn_find_by_id(adj
->circuit
->isis
, adj
->sysid
);
275 return dyn
->hostname
;
277 return sysid_print(adj
->sysid
);
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
)
284 "%%ADJCHANGE: Adjacency to %s (%s) for %s changed from %s to %s, %s",
285 isis_adj_name(adj
), adj
->circuit
->interface
->name
,
286 adj_level2string(adj
->level
), adj_state2string(old_state
),
287 adj_state2string(new_state
), reason
? reason
: "unspecified");
289 void isis_adj_state_change(struct isis_adjacency
**padj
,
290 enum isis_adj_state new_state
, const char *reason
)
292 struct isis_adjacency
*adj
= *padj
;
293 enum isis_adj_state old_state
= adj
->adj_state
;
294 struct isis_circuit
*circuit
= adj
->circuit
;
297 if (new_state
== old_state
)
300 adj
->adj_state
= new_state
;
301 send_hello_sched(circuit
, adj
->level
, TRIGGERED_IIH_DELAY
);
303 if (IS_DEBUG_ADJ_PACKETS
) {
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");
309 if (circuit
->area
->log_adj_changes
)
310 isis_log_adj_change(adj
, old_state
, new_state
, reason
);
313 /* send northbound notification */
314 isis_notif_adj_state_change(adj
, new_state
, reason
);
315 #endif /* ifndef FABRICD */
317 if (circuit
->circ_type
== CIRCUIT_T_BROADCAST
) {
318 for (int level
= IS_LEVEL_1
; level
<= IS_LEVEL_2
; level
++) {
319 if ((adj
->level
& level
) == 0)
321 if (new_state
== ISIS_ADJ_UP
) {
322 circuit
->adj_state_changes
++;
323 circuit
->upadjcount
[level
- 1]++;
324 /* update counter & timers for debugging
326 adj
->last_flap
= time(NULL
);
328 } else if (old_state
== ISIS_ADJ_UP
) {
329 circuit
->adj_state_changes
++;
331 circuit
->upadjcount
[level
- 1]--;
332 if (circuit
->upadjcount
[level
- 1] == 0)
333 isis_tx_queue_clean(circuit
->tx_queue
);
335 if (new_state
== ISIS_ADJ_DOWN
) {
337 circuit
->u
.bc
.adjdb
[level
- 1],
344 if (circuit
->u
.bc
.lan_neighs
[level
- 1]) {
345 list_delete_all_node(
346 circuit
->u
.bc
.lan_neighs
[level
- 1]);
347 isis_adj_build_neigh_list(
348 circuit
->u
.bc
.adjdb
[level
- 1],
349 circuit
->u
.bc
.lan_neighs
[level
- 1]);
352 /* On adjacency state change send new pseudo LSP if we
354 if (circuit
->u
.bc
.is_dr
[level
- 1])
355 lsp_regenerate_schedule_pseudo(circuit
, level
);
358 } else if (circuit
->circ_type
== CIRCUIT_T_P2P
) {
359 for (int level
= IS_LEVEL_1
; level
<= IS_LEVEL_2
; level
++) {
360 if ((adj
->level
& level
) == 0)
362 if (new_state
== ISIS_ADJ_UP
) {
363 circuit
->upadjcount
[level
- 1]++;
365 /* update counter & timers for debugging
367 adj
->last_flap
= time(NULL
);
370 if (level
== IS_LEVEL_1
) {
371 thread_add_timer(master
, send_l1_csnp
,
373 &circuit
->t_send_csnp
[0]);
375 thread_add_timer(master
, send_l2_csnp
,
377 &circuit
->t_send_csnp
[1]);
379 } else if (old_state
== ISIS_ADJ_UP
) {
380 circuit
->upadjcount
[level
- 1]--;
381 if (circuit
->upadjcount
[level
- 1] == 0)
382 isis_tx_queue_clean(circuit
->tx_queue
);
384 if (new_state
== ISIS_ADJ_DOWN
) {
385 if (adj
->circuit
->u
.p2p
.neighbor
== adj
)
386 adj
->circuit
->u
.p2p
.neighbor
=
395 hook_call(isis_adj_state_change_hook
, adj
);
398 isis_delete_adj(adj
);
404 void isis_adj_print(struct isis_adjacency
*adj
)
406 struct isis_dynhn
*dyn
;
410 dyn
= dynhn_find_by_id(adj
->circuit
->isis
, adj
->sysid
);
412 zlog_debug("%s", dyn
->hostname
);
414 zlog_debug("SystemId %20s SNPA %s, level %d; Holding Time %d",
415 sysid_print(adj
->sysid
), snpa_print(adj
->snpa
), adj
->level
,
417 if (adj
->ipv4_address_count
) {
418 zlog_debug("IPv4 Address(es):");
419 for (unsigned int i
= 0; i
< adj
->ipv4_address_count
; i
++)
420 zlog_debug("%pI4", &adj
->ipv4_addresses
[i
]);
423 if (adj
->ipv6_address_count
) {
424 zlog_debug("IPv6 Address(es):");
425 for (unsigned int i
= 0; i
< adj
->ipv6_address_count
; i
++) {
426 char buf
[INET6_ADDRSTRLEN
];
427 inet_ntop(AF_INET6
, &adj
->ipv6_addresses
[i
], buf
,
429 zlog_debug("%s", buf
);
432 zlog_debug("Speaks: %s", nlpid2string(&adj
->nlpids
));
437 const char *isis_adj_yang_state(enum isis_adj_state state
)
444 case ISIS_ADJ_INITIALIZING
:
451 int isis_adj_expire(struct thread
*thread
)
453 struct isis_adjacency
*adj
;
458 adj
= THREAD_ARG(thread
);
460 adj
->t_expire
= NULL
;
462 /* trigger the adj expire event */
463 isis_adj_state_change(&adj
, ISIS_ADJ_DOWN
, "holding time expired");
469 * show isis neighbor [detail]
471 void isis_adj_print_vty(struct isis_adjacency
*adj
, struct vty
*vty
,
475 struct isis_dynhn
*dyn
;
478 vty_out(vty
, " %-20s", isis_adj_name(adj
));
480 if (detail
== ISIS_UI_LEVEL_BRIEF
) {
482 vty_out(vty
, "%-12s", adj
->circuit
->interface
->name
);
484 vty_out(vty
, "NULL circuit!");
485 vty_out(vty
, "%-3u", adj
->level
); /* level */
486 vty_out(vty
, "%-13s", adj_state2string(adj
->adj_state
));
489 if (adj
->last_upd
+ adj
->hold_time
< now
)
490 vty_out(vty
, " Expiring");
492 vty_out(vty
, " %-9llu",
493 (unsigned long long)adj
->last_upd
494 + adj
->hold_time
- now
);
497 vty_out(vty
, "%-10s", snpa_print(adj
->snpa
));
501 if (detail
== ISIS_UI_LEVEL_DETAIL
) {
502 struct sr_adjacency
*sra
;
503 struct listnode
*anode
;
508 vty_out(vty
, " Interface: %s",
509 adj
->circuit
->interface
->name
);
511 vty_out(vty
, " Interface: NULL circuit");
512 vty_out(vty
, ", Level: %u", adj
->level
); /* level */
513 vty_out(vty
, ", State: %s", adj_state2string(adj
->adj_state
));
516 if (adj
->last_upd
+ adj
->hold_time
< now
)
517 vty_out(vty
, " Expiring");
519 vty_out(vty
, ", Expires in %s",
520 time2string(adj
->last_upd
521 + adj
->hold_time
- now
));
523 vty_out(vty
, ", Expires in %s",
524 time2string(adj
->hold_time
));
526 vty_out(vty
, " Adjacency flaps: %u", adj
->flaps
);
527 vty_out(vty
, ", Last: %s ago",
528 time2string(now
- adj
->last_flap
));
530 vty_out(vty
, " Circuit type: %s",
531 circuit_t2string(adj
->circuit_t
));
532 vty_out(vty
, ", Speaks: %s", nlpid2string(&adj
->nlpids
));
534 if (adj
->mt_count
!= 1
535 || adj
->mt_set
[0] != ISIS_MT_IPV4_UNICAST
) {
536 vty_out(vty
, " Topologies:\n");
537 for (unsigned int i
= 0; i
< adj
->mt_count
; i
++)
538 vty_out(vty
, " %s\n",
539 isis_mtid2str(adj
->mt_set
[i
]));
541 vty_out(vty
, " SNPA: %s", snpa_print(adj
->snpa
));
543 && (adj
->circuit
->circ_type
== CIRCUIT_T_BROADCAST
)) {
544 dyn
= dynhn_find_by_id(adj
->circuit
->isis
, adj
->lanid
);
546 vty_out(vty
, ", LAN id: %s.%02x", dyn
->hostname
,
547 adj
->lanid
[ISIS_SYS_ID_LEN
]);
549 vty_out(vty
, ", LAN id: %s.%02x",
550 sysid_print(adj
->lanid
),
551 adj
->lanid
[ISIS_SYS_ID_LEN
]);
554 vty_out(vty
, " LAN Priority: %u",
555 adj
->prio
[adj
->level
- 1]);
557 vty_out(vty
, ", %s, DIS flaps: %u, Last: %s ago",
559 adj
->dis_record
[ISIS_LEVELS
+ level
- 1]
561 adj
->dischanges
[level
- 1],
562 time2string(now
- (adj
->dis_record
[ISIS_LEVELS
568 if (adj
->area_address_count
) {
569 vty_out(vty
, " Area Address(es):\n");
570 for (unsigned int i
= 0; i
< adj
->area_address_count
;
572 vty_out(vty
, " %s\n",
573 isonet_print(adj
->area_addresses
[i
]
575 adj
->area_addresses
[i
]
579 if (adj
->ipv4_address_count
) {
580 vty_out(vty
, " IPv4 Address(es):\n");
581 for (unsigned int i
= 0; i
< adj
->ipv4_address_count
;
583 vty_out(vty
, " %pI4\n",
584 &adj
->ipv4_addresses
[i
]);
586 if (adj
->ipv6_address_count
) {
587 vty_out(vty
, " IPv6 Address(es):\n");
588 for (unsigned int i
= 0; i
< adj
->ipv6_address_count
;
590 char buf
[INET6_ADDRSTRLEN
];
591 inet_ntop(AF_INET6
, &adj
->ipv6_addresses
[i
],
593 vty_out(vty
, " %s\n", buf
);
596 for (ALL_LIST_ELEMENTS_RO(adj
->adj_sids
, anode
, sra
)) {
597 const char *adj_type
;
601 switch (sra
->adj
->circuit
->circ_type
) {
602 case CIRCUIT_T_BROADCAST
:
603 adj_type
= "LAN Adjacency-SID";
604 sid
= sra
->u
.ladj_sid
->sid
;
607 adj_type
= "Adjacency-SID";
608 sid
= sra
->u
.adj_sid
->sid
;
613 backup
= (sra
->type
== ISIS_SR_LAN_BACKUP
) ? " (backup)"
616 vty_out(vty
, " %s %s%s: %u\n",
617 (sra
->nexthop
.family
== AF_INET
) ? "IPv4"
619 adj_type
, backup
, sid
);
626 void isis_adj_build_neigh_list(struct list
*adjdb
, struct list
*list
)
628 struct isis_adjacency
*adj
;
629 struct listnode
*node
;
632 zlog_warn("isis_adj_build_neigh_list(): NULL list");
636 for (ALL_LIST_ELEMENTS_RO(adjdb
, node
, adj
)) {
638 zlog_warn("isis_adj_build_neigh_list(): NULL adj");
642 if ((adj
->adj_state
== ISIS_ADJ_UP
643 || adj
->adj_state
== ISIS_ADJ_INITIALIZING
))
644 listnode_add(list
, adj
->snpa
);
649 void isis_adj_build_up_list(struct list
*adjdb
, struct list
*list
)
651 struct isis_adjacency
*adj
;
652 struct listnode
*node
;
655 zlog_warn("isis_adj_build_up_list(): adjacency DB is empty");
660 zlog_warn("isis_adj_build_up_list(): NULL list");
664 for (ALL_LIST_ELEMENTS_RO(adjdb
, node
, adj
)) {
666 zlog_warn("isis_adj_build_up_list(): NULL adj");
670 if (adj
->adj_state
== ISIS_ADJ_UP
)
671 listnode_add(list
, adj
);
677 int isis_adj_usage2levels(enum isis_adj_usage usage
)
680 case ISIS_ADJ_LEVEL1
:
682 case ISIS_ADJ_LEVEL2
:
684 case ISIS_ADJ_LEVEL1AND2
:
685 return IS_LEVEL_1
| IS_LEVEL_2
;