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/dict.h"
36 #include "isisd/isis_constants.h"
37 #include "isisd/isis_common.h"
38 #include "isisd/isis_flags.h"
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"
46 #include "isisd/isis_lsp.h"
47 #include "isisd/isis_spf.h"
48 #include "isisd/isis_events.h"
49 #include "isisd/isis_mt.h"
51 extern struct isis
*isis
;
53 static struct isis_adjacency
*adj_alloc(const u_char
*id
)
55 struct isis_adjacency
*adj
;
57 adj
= XCALLOC(MTYPE_ISIS_ADJACENCY
, sizeof(struct isis_adjacency
));
58 memcpy(adj
->sysid
, id
, ISIS_SYS_ID_LEN
);
63 struct isis_adjacency
*isis_new_adj(const u_char
*id
, const u_char
*snpa
,
64 int level
, struct isis_circuit
*circuit
)
66 struct isis_adjacency
*adj
;
69 adj
= adj_alloc(id
); /* P2P kludge */
72 memcpy(adj
->snpa
, snpa
, ETH_ALEN
);
74 memset(adj
->snpa
, ' ', ETH_ALEN
);
77 adj
->circuit
= circuit
;
80 adj
->last_flap
= time(NULL
);
81 if (circuit
->circ_type
== CIRCUIT_T_BROADCAST
) {
82 listnode_add(circuit
->u
.bc
.adjdb
[level
- 1], adj
);
83 adj
->dischanges
[level
- 1] = 0;
84 for (i
= 0; i
< DIS_RECORDS
;
85 i
++) /* clear N DIS state change records */
87 adj
->dis_record
[(i
* ISIS_LEVELS
) + level
- 1].dis
=
89 adj
->dis_record
[(i
* ISIS_LEVELS
) + level
- 1]
90 .last_dis_change
= time(NULL
);
97 struct isis_adjacency
*isis_adj_lookup(const u_char
*sysid
, struct list
*adjdb
)
99 struct isis_adjacency
*adj
;
100 struct listnode
*node
;
102 for (ALL_LIST_ELEMENTS_RO(adjdb
, node
, adj
))
103 if (memcmp(adj
->sysid
, sysid
, ISIS_SYS_ID_LEN
) == 0)
109 struct isis_adjacency
*isis_adj_lookup_snpa(const u_char
*ssnpa
,
112 struct listnode
*node
;
113 struct isis_adjacency
*adj
;
115 for (ALL_LIST_ELEMENTS_RO(adjdb
, node
, adj
))
116 if (memcmp(adj
->snpa
, ssnpa
, ETH_ALEN
) == 0)
122 void isis_delete_adj(void *arg
)
124 struct isis_adjacency
*adj
= arg
;
129 THREAD_TIMER_OFF(adj
->t_expire
);
131 /* remove from SPF trees */
132 spftree_area_adj_del(adj
->circuit
->area
, adj
);
134 if (adj
->area_addresses
)
135 XFREE(MTYPE_ISIS_ADJACENCY_INFO
, adj
->area_addresses
);
136 if (adj
->ipv4_addresses
)
137 XFREE(MTYPE_ISIS_ADJACENCY_INFO
, adj
->ipv4_addresses
);
138 if (adj
->ipv6_addresses
)
139 XFREE(MTYPE_ISIS_ADJACENCY_INFO
, adj
->ipv6_addresses
);
143 XFREE(MTYPE_ISIS_ADJACENCY
, adj
);
147 static const char *adj_state2string(int state
)
151 case ISIS_ADJ_INITIALIZING
:
152 return "Initializing";
161 return NULL
; /* not reached */
164 void isis_adj_state_change(struct isis_adjacency
*adj
,
165 enum isis_adj_state new_state
, const char *reason
)
169 struct isis_circuit
*circuit
;
172 old_state
= adj
->adj_state
;
173 adj
->adj_state
= new_state
;
175 circuit
= adj
->circuit
;
177 if (isis
->debugs
& DEBUG_ADJ_PACKETS
) {
178 zlog_debug("ISIS-Adj (%s): Adjacency state change %d->%d: %s",
179 circuit
->area
->area_tag
, old_state
, new_state
,
180 reason
? reason
: "unspecified");
183 if (circuit
->area
->log_adj_changes
) {
184 const char *adj_name
;
185 struct isis_dynhn
*dyn
;
187 dyn
= dynhn_find_by_id(adj
->sysid
);
189 adj_name
= dyn
->hostname
;
191 adj_name
= sysid_print(adj
->sysid
);
194 "%%ADJCHANGE: Adjacency to %s (%s) changed from %s to %s, %s",
195 adj_name
, adj
->circuit
->interface
->name
,
196 adj_state2string(old_state
),
197 adj_state2string(new_state
),
198 reason
? reason
: "unspecified");
201 if (circuit
->circ_type
== CIRCUIT_T_BROADCAST
) {
203 for (level
= IS_LEVEL_1
; level
<= IS_LEVEL_2
; level
++) {
204 if ((adj
->level
& level
) == 0)
206 if (new_state
== ISIS_ADJ_UP
) {
207 circuit
->upadjcount
[level
- 1]++;
208 isis_event_adjacency_state_change(adj
,
210 /* update counter & timers for debugging
212 adj
->last_flap
= time(NULL
);
214 } else if (new_state
== ISIS_ADJ_DOWN
) {
215 listnode_delete(circuit
->u
.bc
.adjdb
[level
- 1],
218 circuit
->upadjcount
[level
- 1]--;
219 if (circuit
->upadjcount
[level
- 1] == 0)
220 isis_circuit_lsp_queue_clean(circuit
);
222 isis_event_adjacency_state_change(adj
,
227 if (circuit
->u
.bc
.lan_neighs
[level
- 1]) {
228 list_delete_all_node(
229 circuit
->u
.bc
.lan_neighs
[level
- 1]);
230 isis_adj_build_neigh_list(
231 circuit
->u
.bc
.adjdb
[level
- 1],
232 circuit
->u
.bc
.lan_neighs
[level
- 1]);
235 /* On adjacency state change send new pseudo LSP if we
237 if (circuit
->u
.bc
.is_dr
[level
- 1])
238 lsp_regenerate_schedule_pseudo(circuit
, level
);
242 isis_delete_adj(adj
);
245 } else if (circuit
->circ_type
== CIRCUIT_T_P2P
) {
247 for (level
= IS_LEVEL_1
; level
<= IS_LEVEL_2
; level
++) {
248 if ((adj
->level
& level
) == 0)
250 if (new_state
== ISIS_ADJ_UP
) {
251 circuit
->upadjcount
[level
- 1]++;
252 isis_event_adjacency_state_change(adj
,
255 if (adj
->sys_type
== ISIS_SYSTYPE_UNKNOWN
)
256 send_hello(circuit
, level
);
258 /* update counter & timers for debugging
260 adj
->last_flap
= time(NULL
);
263 /* 7.3.17 - going up on P2P -> send CSNP */
264 /* FIXME: yup, I know its wrong... but i will do
266 send_csnp(circuit
, level
);
267 } else if (new_state
== ISIS_ADJ_DOWN
) {
268 if (adj
->circuit
->u
.p2p
.neighbor
== adj
)
269 adj
->circuit
->u
.p2p
.neighbor
= NULL
;
270 circuit
->upadjcount
[level
- 1]--;
271 if (circuit
->upadjcount
[level
- 1] == 0)
272 isis_circuit_lsp_queue_clean(circuit
);
274 isis_event_adjacency_state_change(adj
,
281 isis_delete_adj(adj
);
290 void isis_adj_print(struct isis_adjacency
*adj
)
292 struct isis_dynhn
*dyn
;
296 dyn
= dynhn_find_by_id(adj
->sysid
);
298 zlog_debug("%s", dyn
->hostname
);
300 zlog_debug("SystemId %20s SNPA %s, level %d\nHolding Time %d",
301 sysid_print(adj
->sysid
), snpa_print(adj
->snpa
), adj
->level
,
303 if (adj
->ipv4_address_count
) {
304 zlog_debug("IPv4 Address(es):");
305 for (unsigned int i
= 0; i
< adj
->ipv4_address_count
; i
++)
306 zlog_debug("%s", inet_ntoa(adj
->ipv4_addresses
[i
]));
309 if (adj
->ipv6_address_count
) {
310 zlog_debug("IPv6 Address(es):");
311 for (unsigned int i
= 0; i
< adj
->ipv6_address_count
; i
++) {
312 char buf
[INET6_ADDRSTRLEN
];
313 inet_ntop(AF_INET6
, &adj
->ipv6_addresses
[i
], buf
,
315 zlog_debug("%s", buf
);
318 zlog_debug("Speaks: %s", nlpid2string(&adj
->nlpids
));
323 int isis_adj_expire(struct thread
*thread
)
325 struct isis_adjacency
*adj
;
330 adj
= THREAD_ARG(thread
);
332 adj
->t_expire
= NULL
;
334 /* trigger the adj expire event */
335 isis_adj_state_change(adj
, ISIS_ADJ_DOWN
, "holding time expired");
341 * show isis neighbor [detail]
343 void isis_adj_print_vty(struct isis_adjacency
*adj
, struct vty
*vty
,
347 struct isis_dynhn
*dyn
;
350 dyn
= dynhn_find_by_id(adj
->sysid
);
352 vty_out(vty
, " %-20s", dyn
->hostname
);
354 vty_out(vty
, " %-20s", sysid_print(adj
->sysid
));
356 if (detail
== ISIS_UI_LEVEL_BRIEF
) {
358 vty_out(vty
, "%-12s", adj
->circuit
->interface
->name
);
360 vty_out(vty
, "NULL circuit!");
361 vty_out(vty
, "%-3u", adj
->level
); /* level */
362 vty_out(vty
, "%-13s", adj_state2string(adj
->adj_state
));
365 vty_out(vty
, "%-9llu",
366 (unsigned long long)adj
->last_upd
367 + adj
->hold_time
- now
);
370 vty_out(vty
, "%-10s", snpa_print(adj
->snpa
));
374 if (detail
== ISIS_UI_LEVEL_DETAIL
) {
378 vty_out(vty
, " Interface: %s",
379 adj
->circuit
->interface
->name
);
381 vty_out(vty
, " Interface: NULL circuit");
382 vty_out(vty
, ", Level: %u", adj
->level
); /* level */
383 vty_out(vty
, ", State: %s", adj_state2string(adj
->adj_state
));
386 vty_out(vty
, ", Expires in %s",
387 time2string(adj
->last_upd
+ adj
->hold_time
390 vty_out(vty
, ", Expires in %s",
391 time2string(adj
->hold_time
));
393 vty_out(vty
, " Adjacency flaps: %u", adj
->flaps
);
394 vty_out(vty
, ", Last: %s ago",
395 time2string(now
- adj
->last_flap
));
397 vty_out(vty
, " Circuit type: %s",
398 circuit_t2string(adj
->circuit_t
));
399 vty_out(vty
, ", Speaks: %s", nlpid2string(&adj
->nlpids
));
401 if (adj
->mt_count
!= 1
402 || adj
->mt_set
[0] != ISIS_MT_IPV4_UNICAST
) {
403 vty_out(vty
, " Topologies:\n");
404 for (unsigned int i
= 0; i
< adj
->mt_count
; i
++)
405 vty_out(vty
, " %s\n",
406 isis_mtid2str(adj
->mt_set
[i
]));
408 vty_out(vty
, " SNPA: %s", snpa_print(adj
->snpa
));
410 && (adj
->circuit
->circ_type
== CIRCUIT_T_BROADCAST
)) {
411 dyn
= dynhn_find_by_id(adj
->lanid
);
413 vty_out(vty
, ", LAN id: %s.%02x", dyn
->hostname
,
414 adj
->lanid
[ISIS_SYS_ID_LEN
]);
416 vty_out(vty
, ", LAN id: %s.%02x",
417 sysid_print(adj
->lanid
),
418 adj
->lanid
[ISIS_SYS_ID_LEN
]);
421 vty_out(vty
, " LAN Priority: %u",
422 adj
->prio
[adj
->level
- 1]);
424 vty_out(vty
, ", %s, DIS flaps: %u, Last: %s ago",
426 adj
->dis_record
[ISIS_LEVELS
+ level
- 1]
428 adj
->dischanges
[level
- 1],
429 time2string(now
- (adj
->dis_record
[ISIS_LEVELS
435 if (adj
->area_address_count
) {
436 vty_out(vty
, " Area Address(es):\n");
437 for (unsigned int i
= 0; i
< adj
->area_address_count
;
439 vty_out(vty
, " %s\n",
440 isonet_print(adj
->area_addresses
[i
]
442 adj
->area_addresses
[i
]
446 if (adj
->ipv4_address_count
) {
447 vty_out(vty
, " IPv4 Address(es):\n");
448 for (unsigned int i
= 0; i
< adj
->ipv4_address_count
;
450 vty_out(vty
, " %s\n",
451 inet_ntoa(adj
->ipv4_addresses
[i
]));
453 if (adj
->ipv6_address_count
) {
454 vty_out(vty
, " IPv6 Address(es):\n");
455 for (unsigned int i
= 0; i
< adj
->ipv6_address_count
;
457 char buf
[INET6_ADDRSTRLEN
];
458 inet_ntop(AF_INET6
, &adj
->ipv6_addresses
[i
],
460 vty_out(vty
, " %s\n", buf
);
468 void isis_adj_build_neigh_list(struct list
*adjdb
, struct list
*list
)
470 struct isis_adjacency
*adj
;
471 struct listnode
*node
;
474 zlog_warn("isis_adj_build_neigh_list(): NULL list");
478 for (ALL_LIST_ELEMENTS_RO(adjdb
, node
, adj
)) {
480 zlog_warn("isis_adj_build_neigh_list(): NULL adj");
484 if ((adj
->adj_state
== ISIS_ADJ_UP
485 || adj
->adj_state
== ISIS_ADJ_INITIALIZING
))
486 listnode_add(list
, adj
->snpa
);
491 void isis_adj_build_up_list(struct list
*adjdb
, struct list
*list
)
493 struct isis_adjacency
*adj
;
494 struct listnode
*node
;
497 zlog_warn("isis_adj_build_up_list(): adjacency DB is empty");
502 zlog_warn("isis_adj_build_up_list(): NULL list");
506 for (ALL_LIST_ELEMENTS_RO(adjdb
, node
, adj
)) {
508 zlog_warn("isis_adj_build_up_list(): NULL adj");
512 if (adj
->adj_state
== ISIS_ADJ_UP
)
513 listnode_add(list
, adj
);
519 int isis_adj_usage2levels(enum isis_adj_usage usage
)
522 case ISIS_ADJ_LEVEL1
:
524 case ISIS_ADJ_LEVEL2
:
526 case ISIS_ADJ_LEVEL1AND2
:
527 return IS_LEVEL_1
| IS_LEVEL_2
;