1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * IS-IS Rout(e)ing protocol - isis_dr.c
4 * IS-IS designated router related routines
6 * Copyright (C) 2001,2002 Sampo Saaristo
7 * Tampere University of Technology
8 * Institute of Communications Engineering
22 #include "isisd/isis_constants.h"
23 #include "isisd/isis_common.h"
24 #include "isisd/isis_misc.h"
25 #include "isisd/isis_flags.h"
26 #include "isisd/isis_circuit.h"
27 #include "isisd/isisd.h"
28 #include "isisd/isis_adjacency.h"
29 #include "isisd/isis_constants.h"
30 #include "isisd/isis_pdu.h"
31 #include "isisd/isis_lsp.h"
32 #include "isisd/isis_dr.h"
33 #include "isisd/isis_events.h"
35 const char *isis_disflag2string(int disflag
)
46 return "unknown DIS state";
48 return NULL
; /* not reached */
51 void isis_run_dr(struct thread
*thread
)
53 struct isis_circuit_arg
*arg
= THREAD_ARG(thread
);
57 struct isis_circuit
*circuit
= arg
->circuit
;
58 int level
= arg
->level
;
62 if (circuit
->circ_type
!= CIRCUIT_T_BROADCAST
) {
63 zlog_warn("%s: scheduled for non broadcast circuit from %s:%d",
64 __func__
, thread
->xref
->xref
.file
,
65 thread
->xref
->xref
.line
);
69 if (circuit
->u
.bc
.run_dr_elect
[level
- 1])
70 zlog_warn("%s: run_dr_elect already set for l%d", __func__
,
73 circuit
->u
.bc
.t_run_dr
[level
- 1] = NULL
;
74 circuit
->u
.bc
.run_dr_elect
[level
- 1] = 1;
77 static int isis_check_dr_change(struct isis_adjacency
*adj
, int level
)
81 if (adj
->dis_record
[level
- 1].dis
82 != adj
->dis_record
[(1 * ISIS_LEVELS
) + level
- 1].dis
)
83 /* was there a DIS state transition ? */
85 adj
->dischanges
[level
- 1]++;
86 adj
->circuit
->desig_changes
[level
- 1]++;
87 /* ok rotate the history list through */
88 for (i
= DIS_RECORDS
- 1; i
> 0; i
--) {
89 adj
->dis_record
[(i
* ISIS_LEVELS
) + level
- 1].dis
=
90 adj
->dis_record
[((i
- 1) * ISIS_LEVELS
) + level
93 adj
->dis_record
[(i
* ISIS_LEVELS
) + level
- 1]
95 adj
->dis_record
[((i
- 1) * ISIS_LEVELS
) + level
103 int isis_dr_elect(struct isis_circuit
*circuit
, int level
)
106 struct listnode
*node
;
107 struct isis_adjacency
*adj
, *adj_dr
= NULL
;
108 struct list
*list
= list_new();
110 int biggest_prio
= -1;
111 int cmp_res
, retval
= ISIS_OK
;
113 own_prio
= circuit
->priority
[level
- 1];
114 adjdb
= circuit
->u
.bc
.adjdb
[level
- 1];
117 zlog_warn("%s adjdb == NULL", __func__
);
121 isis_adj_build_up_list(adjdb
, list
);
124 * Loop the adjacencies and find the one with the biggest priority
126 for (ALL_LIST_ELEMENTS_RO(list
, node
, adj
)) {
127 /* clear flag for show output */
128 adj
->dis_record
[level
- 1].dis
= ISIS_IS_NOT_DIS
;
129 adj
->dis_record
[level
- 1].last_dis_change
= time(NULL
);
131 if (adj
->prio
[level
- 1] > biggest_prio
) {
132 biggest_prio
= adj
->prio
[level
- 1];
134 } else if (adj
->prio
[level
- 1] == biggest_prio
) {
136 * Comparison of MACs breaks a tie
139 cmp_res
= memcmp(adj_dr
->snpa
, adj
->snpa
,
146 "%s: multiple adjacencies with same SNPA",
156 * Could not find the DR - means we are alone. Resign if we were
159 if (circuit
->u
.bc
.is_dr
[level
- 1])
160 retval
= isis_dr_resign(circuit
, level
);
166 * Now we have the DR adjacency, compare it to self
168 if (adj_dr
->prio
[level
- 1] < own_prio
169 || (adj_dr
->prio
[level
- 1] == own_prio
170 && memcmp(adj_dr
->snpa
, circuit
->u
.bc
.snpa
, ETH_ALEN
) < 0)) {
171 adj_dr
->dis_record
[level
- 1].dis
= ISIS_IS_NOT_DIS
;
172 adj_dr
->dis_record
[level
- 1].last_dis_change
= time(NULL
);
174 /* rotate the history log */
175 for (ALL_LIST_ELEMENTS_RO(list
, node
, adj
))
176 isis_check_dr_change(adj
, level
);
178 /* We are the DR, commence DR */
179 if (circuit
->u
.bc
.is_dr
[level
- 1] == 0 && listcount(list
) > 0)
180 retval
= isis_dr_commence(circuit
, level
);
182 /* ok we have found the DIS - lets mark the adjacency */
183 /* set flag for show output */
184 adj_dr
->dis_record
[level
- 1].dis
= ISIS_IS_DIS
;
185 adj_dr
->dis_record
[level
- 1].last_dis_change
= time(NULL
);
187 /* now loop through a second time to check if there has been a
189 * if yes rotate the history log
192 for (ALL_LIST_ELEMENTS_RO(list
, node
, adj
))
193 isis_check_dr_change(adj
, level
);
196 * We are not DR - if we were -> resign
198 if (circuit
->u
.bc
.is_dr
[level
- 1])
199 retval
= isis_dr_resign(circuit
, level
);
205 int isis_dr_resign(struct isis_circuit
*circuit
, int level
)
207 uint8_t id
[ISIS_SYS_ID_LEN
+ 2];
210 zlog_debug("%s l%d", __func__
, level
);
212 circuit
->u
.bc
.is_dr
[level
- 1] = 0;
213 circuit
->u
.bc
.run_dr_elect
[level
- 1] = 0;
214 THREAD_OFF(circuit
->u
.bc
.t_run_dr
[level
- 1]);
215 THREAD_OFF(circuit
->u
.bc
.t_refresh_pseudo_lsp
[level
- 1]);
216 circuit
->lsp_regenerate_pending
[level
- 1] = 0;
218 memcpy(id
, circuit
->isis
->sysid
, ISIS_SYS_ID_LEN
);
219 LSP_PSEUDO_ID(id
) = circuit
->circuit_id
;
220 LSP_FRAGMENT(id
) = 0;
221 lsp_purge_pseudo(id
, circuit
, level
);
224 memset(circuit
->u
.bc
.l1_desig_is
, 0, ISIS_SYS_ID_LEN
+ 1);
226 thread_add_timer(master
, send_l1_psnp
, circuit
,
227 isis_jitter(circuit
->psnp_interval
[level
- 1],
229 &circuit
->t_send_psnp
[0]);
231 memset(circuit
->u
.bc
.l2_desig_is
, 0, ISIS_SYS_ID_LEN
+ 1);
233 thread_add_timer(master
, send_l2_psnp
, circuit
,
234 isis_jitter(circuit
->psnp_interval
[level
- 1],
236 &circuit
->t_send_psnp
[1]);
239 THREAD_OFF(circuit
->t_send_csnp
[level
- 1]);
241 thread_add_timer(master
, isis_run_dr
,
242 &circuit
->level_arg
[level
- 1],
243 2 * circuit
->hello_interval
[level
- 1],
244 &circuit
->u
.bc
.t_run_dr
[level
- 1]);
247 thread_add_event(master
, isis_event_dis_status_change
, circuit
, 0,
253 int isis_dr_commence(struct isis_circuit
*circuit
, int level
)
255 uint8_t old_dr
[ISIS_SYS_ID_LEN
+ 2];
258 zlog_debug("%s l%d", __func__
, level
);
260 /* Lets keep a pause in DR election */
261 circuit
->u
.bc
.run_dr_elect
[level
- 1] = 0;
262 circuit
->u
.bc
.is_dr
[level
- 1] = 1;
265 memcpy(old_dr
, circuit
->u
.bc
.l1_desig_is
, ISIS_SYS_ID_LEN
+ 1);
266 LSP_FRAGMENT(old_dr
) = 0;
267 if (LSP_PSEUDO_ID(old_dr
)) {
268 /* there was a dr elected, purge its LSPs from the db */
269 lsp_purge_pseudo(old_dr
, circuit
, level
);
271 memcpy(circuit
->u
.bc
.l1_desig_is
, circuit
->isis
->sysid
,
273 *(circuit
->u
.bc
.l1_desig_is
+ ISIS_SYS_ID_LEN
) =
276 assert(circuit
->circuit_id
); /* must be non-zero */
277 lsp_generate_pseudo(circuit
, 1);
279 thread_add_timer(master
, send_l1_csnp
, circuit
,
280 isis_jitter(circuit
->csnp_interval
[level
- 1],
282 &circuit
->t_send_csnp
[0]);
285 memcpy(old_dr
, circuit
->u
.bc
.l2_desig_is
, ISIS_SYS_ID_LEN
+ 1);
286 LSP_FRAGMENT(old_dr
) = 0;
287 if (LSP_PSEUDO_ID(old_dr
)) {
288 /* there was a dr elected, purge its LSPs from the db */
289 lsp_purge_pseudo(old_dr
, circuit
, level
);
291 memcpy(circuit
->u
.bc
.l2_desig_is
, circuit
->isis
->sysid
,
293 *(circuit
->u
.bc
.l2_desig_is
+ ISIS_SYS_ID_LEN
) =
296 assert(circuit
->circuit_id
); /* must be non-zero */
297 lsp_generate_pseudo(circuit
, 2);
299 thread_add_timer(master
, send_l2_csnp
, circuit
,
300 isis_jitter(circuit
->csnp_interval
[level
- 1],
302 &circuit
->t_send_csnp
[1]);
305 thread_add_timer(master
, isis_run_dr
,
306 &circuit
->level_arg
[level
- 1],
307 2 * circuit
->hello_interval
[level
- 1],
308 &circuit
->u
.bc
.t_run_dr
[level
- 1]);
309 thread_add_event(master
, isis_event_dis_status_change
, circuit
, 0,