2 * IS-IS Rout(e)ing protocol - isis_dr.c
3 * IS-IS designated router related routines
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_misc.h"
39 #include "isisd/isis_flags.h"
40 #include "isisd/isis_circuit.h"
41 #include "isisd/isisd.h"
42 #include "isisd/isis_adjacency.h"
43 #include "isisd/isis_constants.h"
44 #include "isisd/isis_pdu.h"
45 #include "isisd/isis_lsp.h"
46 #include "isisd/isis_dr.h"
47 #include "isisd/isis_events.h"
49 const char *isis_disflag2string(int disflag
)
60 return "unknown DIS state";
62 return NULL
; /* not reached */
65 int isis_run_dr_l1(struct thread
*thread
)
67 struct isis_circuit
*circuit
;
69 circuit
= THREAD_ARG(thread
);
72 if (circuit
->u
.bc
.run_dr_elect
[0])
73 zlog_warn("isis_run_dr(): run_dr_elect already set for l1");
75 circuit
->u
.bc
.t_run_dr
[0] = NULL
;
76 circuit
->u
.bc
.run_dr_elect
[0] = 1;
81 int isis_run_dr_l2(struct thread
*thread
)
83 struct isis_circuit
*circuit
;
85 circuit
= THREAD_ARG(thread
);
88 if (circuit
->u
.bc
.run_dr_elect
[1])
89 zlog_warn("isis_run_dr(): run_dr_elect already set for l2");
92 circuit
->u
.bc
.t_run_dr
[1] = NULL
;
93 circuit
->u
.bc
.run_dr_elect
[1] = 1;
98 static int isis_check_dr_change(struct isis_adjacency
*adj
, int level
)
102 if (adj
->dis_record
[level
- 1].dis
103 != adj
->dis_record
[(1 * ISIS_LEVELS
) + level
- 1].dis
)
104 /* was there a DIS state transition ? */
106 adj
->dischanges
[level
- 1]++;
107 /* ok rotate the history list through */
108 for (i
= DIS_RECORDS
- 1; i
> 0; i
--) {
109 adj
->dis_record
[(i
* ISIS_LEVELS
) + level
- 1].dis
=
110 adj
->dis_record
[((i
- 1) * ISIS_LEVELS
) + level
113 adj
->dis_record
[(i
* ISIS_LEVELS
) + level
- 1]
115 adj
->dis_record
[((i
- 1) * ISIS_LEVELS
) + level
123 int isis_dr_elect(struct isis_circuit
*circuit
, int level
)
126 struct listnode
*node
;
127 struct isis_adjacency
*adj
, *adj_dr
= NULL
;
128 struct list
*list
= list_new();
130 int biggest_prio
= -1;
131 int cmp_res
, retval
= ISIS_OK
;
133 own_prio
= circuit
->priority
[level
- 1];
134 adjdb
= circuit
->u
.bc
.adjdb
[level
- 1];
137 zlog_warn("isis_dr_elect() adjdb == NULL");
141 isis_adj_build_up_list(adjdb
, list
);
144 * Loop the adjacencies and find the one with the biggest priority
146 for (ALL_LIST_ELEMENTS_RO(list
, node
, adj
)) {
147 /* clear flag for show output */
148 adj
->dis_record
[level
- 1].dis
= ISIS_IS_NOT_DIS
;
149 adj
->dis_record
[level
- 1].last_dis_change
= time(NULL
);
151 if (adj
->prio
[level
- 1] > biggest_prio
) {
152 biggest_prio
= adj
->prio
[level
- 1];
154 } else if (adj
->prio
[level
- 1] == biggest_prio
) {
156 * Comparison of MACs breaks a tie
159 cmp_res
= memcmp(adj_dr
->snpa
, adj
->snpa
,
166 "isis_dr_elect(): multiple adjacencies with same SNPA");
175 * Could not find the DR - means we are alone. Resign if we were
178 if (circuit
->u
.bc
.is_dr
[level
- 1])
179 retval
= isis_dr_resign(circuit
, level
);
185 * Now we have the DR adjacency, compare it to self
187 if (adj_dr
->prio
[level
- 1] < own_prio
188 || (adj_dr
->prio
[level
- 1] == own_prio
189 && memcmp(adj_dr
->snpa
, circuit
->u
.bc
.snpa
, ETH_ALEN
) < 0)) {
190 adj_dr
->dis_record
[level
- 1].dis
= ISIS_IS_NOT_DIS
;
191 adj_dr
->dis_record
[level
- 1].last_dis_change
= time(NULL
);
193 /* rotate the history log */
194 for (ALL_LIST_ELEMENTS_RO(list
, node
, adj
))
195 isis_check_dr_change(adj
, level
);
197 /* We are the DR, commence DR */
198 if (circuit
->u
.bc
.is_dr
[level
- 1] == 0 && listcount(list
) > 0)
199 retval
= isis_dr_commence(circuit
, level
);
201 /* ok we have found the DIS - lets mark the adjacency */
202 /* set flag for show output */
203 adj_dr
->dis_record
[level
- 1].dis
= ISIS_IS_DIS
;
204 adj_dr
->dis_record
[level
- 1].last_dis_change
= time(NULL
);
206 /* now loop through a second time to check if there has been a
208 * if yes rotate the history log
211 for (ALL_LIST_ELEMENTS_RO(list
, node
, adj
))
212 isis_check_dr_change(adj
, level
);
215 * We are not DR - if we were -> resign
217 if (circuit
->u
.bc
.is_dr
[level
- 1])
218 retval
= isis_dr_resign(circuit
, level
);
224 int isis_dr_resign(struct isis_circuit
*circuit
, int level
)
226 u_char id
[ISIS_SYS_ID_LEN
+ 2];
228 zlog_debug("isis_dr_resign l%d", level
);
230 circuit
->u
.bc
.is_dr
[level
- 1] = 0;
231 circuit
->u
.bc
.run_dr_elect
[level
- 1] = 0;
232 THREAD_TIMER_OFF(circuit
->u
.bc
.t_run_dr
[level
- 1]);
233 THREAD_TIMER_OFF(circuit
->u
.bc
.t_refresh_pseudo_lsp
[level
- 1]);
234 circuit
->lsp_regenerate_pending
[level
- 1] = 0;
236 memcpy(id
, isis
->sysid
, ISIS_SYS_ID_LEN
);
237 LSP_PSEUDO_ID(id
) = circuit
->circuit_id
;
238 LSP_FRAGMENT(id
) = 0;
239 lsp_purge_pseudo(id
, circuit
, level
);
242 memset(circuit
->u
.bc
.l1_desig_is
, 0, ISIS_SYS_ID_LEN
+ 1);
244 THREAD_TIMER_OFF(circuit
->t_send_csnp
[0]);
246 thread_add_timer(master
, isis_run_dr_l1
, circuit
,
247 2 * circuit
->hello_interval
[0],
248 &circuit
->u
.bc
.t_run_dr
[0]);
250 thread_add_timer(master
, send_l1_psnp
, circuit
,
251 isis_jitter(circuit
->psnp_interval
[level
- 1],
253 &circuit
->t_send_psnp
[0]);
255 memset(circuit
->u
.bc
.l2_desig_is
, 0, ISIS_SYS_ID_LEN
+ 1);
257 THREAD_TIMER_OFF(circuit
->t_send_csnp
[1]);
259 thread_add_timer(master
, isis_run_dr_l2
, circuit
,
260 2 * circuit
->hello_interval
[1],
261 &circuit
->u
.bc
.t_run_dr
[1]);
263 thread_add_timer(master
, send_l2_psnp
, circuit
,
264 isis_jitter(circuit
->psnp_interval
[level
- 1],
266 &circuit
->t_send_psnp
[1]);
269 thread_add_event(master
, isis_event_dis_status_change
, circuit
, 0,
275 int isis_dr_commence(struct isis_circuit
*circuit
, int level
)
277 u_char old_dr
[ISIS_SYS_ID_LEN
+ 2];
279 if (isis
->debugs
& DEBUG_EVENTS
)
280 zlog_debug("isis_dr_commence l%d", level
);
282 /* Lets keep a pause in DR election */
283 circuit
->u
.bc
.run_dr_elect
[level
- 1] = 0;
285 thread_add_timer(master
, isis_run_dr_l1
, circuit
,
286 2 * circuit
->hello_interval
[0],
287 &circuit
->u
.bc
.t_run_dr
[0]);
289 thread_add_timer(master
, isis_run_dr_l2
, circuit
,
290 2 * circuit
->hello_interval
[1],
291 &circuit
->u
.bc
.t_run_dr
[1]);
292 circuit
->u
.bc
.is_dr
[level
- 1] = 1;
295 memcpy(old_dr
, circuit
->u
.bc
.l1_desig_is
, ISIS_SYS_ID_LEN
+ 1);
296 LSP_FRAGMENT(old_dr
) = 0;
297 if (LSP_PSEUDO_ID(old_dr
)) {
298 /* there was a dr elected, purge its LSPs from the db */
299 lsp_purge_pseudo(old_dr
, circuit
, level
);
301 memcpy(circuit
->u
.bc
.l1_desig_is
, isis
->sysid
, ISIS_SYS_ID_LEN
);
302 *(circuit
->u
.bc
.l1_desig_is
+ ISIS_SYS_ID_LEN
) =
305 assert(circuit
->circuit_id
); /* must be non-zero */
306 /* if (circuit->t_send_l1_psnp)
307 thread_cancel (circuit->t_send_l1_psnp); */
308 lsp_generate_pseudo(circuit
, 1);
310 THREAD_TIMER_OFF(circuit
->u
.bc
.t_run_dr
[0]);
311 thread_add_timer(master
, isis_run_dr_l1
, circuit
,
312 2 * circuit
->hello_interval
[0],
313 &circuit
->u
.bc
.t_run_dr
[0]);
315 thread_add_timer(master
, send_l1_csnp
, circuit
,
316 isis_jitter(circuit
->csnp_interval
[level
- 1],
318 &circuit
->t_send_csnp
[0]);
321 memcpy(old_dr
, circuit
->u
.bc
.l2_desig_is
, ISIS_SYS_ID_LEN
+ 1);
322 LSP_FRAGMENT(old_dr
) = 0;
323 if (LSP_PSEUDO_ID(old_dr
)) {
324 /* there was a dr elected, purge its LSPs from the db */
325 lsp_purge_pseudo(old_dr
, circuit
, level
);
327 memcpy(circuit
->u
.bc
.l2_desig_is
, isis
->sysid
, ISIS_SYS_ID_LEN
);
328 *(circuit
->u
.bc
.l2_desig_is
+ ISIS_SYS_ID_LEN
) =
331 assert(circuit
->circuit_id
); /* must be non-zero */
332 /* if (circuit->t_send_l1_psnp)
333 thread_cancel (circuit->t_send_l1_psnp); */
334 lsp_generate_pseudo(circuit
, 2);
336 THREAD_TIMER_OFF(circuit
->u
.bc
.t_run_dr
[1]);
337 thread_add_timer(master
, isis_run_dr_l2
, circuit
,
338 2 * circuit
->hello_interval
[1],
339 &circuit
->u
.bc
.t_run_dr
[1]);
341 thread_add_timer(master
, send_l2_csnp
, circuit
,
342 isis_jitter(circuit
->csnp_interval
[level
- 1],
344 &circuit
->t_send_csnp
[1]);
347 thread_add_event(master
, isis_event_dis_status_change
, circuit
, 0,