]> git.proxmox.com Git - mirror_frr.git/blob - isisd/isis_dr.c
*: auto-convert to SPDX License IDs
[mirror_frr.git] / isisd / isis_dr.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * IS-IS Rout(e)ing protocol - isis_dr.c
4 * IS-IS designated router related routines
5 *
6 * Copyright (C) 2001,2002 Sampo Saaristo
7 * Tampere University of Technology
8 * Institute of Communications Engineering
9 */
10
11
12 #include <zebra.h>
13
14 #include "log.h"
15 #include "hash.h"
16 #include "thread.h"
17 #include "linklist.h"
18 #include "vty.h"
19 #include "stream.h"
20 #include "if.h"
21
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"
34
35 const char *isis_disflag2string(int disflag)
36 {
37
38 switch (disflag) {
39 case ISIS_IS_NOT_DIS:
40 return "is not DIS";
41 case ISIS_IS_DIS:
42 return "is DIS";
43 case ISIS_WAS_DIS:
44 return "was DIS";
45 default:
46 return "unknown DIS state";
47 }
48 return NULL; /* not reached */
49 }
50
51 void isis_run_dr(struct thread *thread)
52 {
53 struct isis_circuit_arg *arg = THREAD_ARG(thread);
54
55 assert(arg);
56
57 struct isis_circuit *circuit = arg->circuit;
58 int level = arg->level;
59
60 assert(circuit);
61
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);
66 return;
67 }
68
69 if (circuit->u.bc.run_dr_elect[level - 1])
70 zlog_warn("%s: run_dr_elect already set for l%d", __func__,
71 level);
72
73 circuit->u.bc.t_run_dr[level - 1] = NULL;
74 circuit->u.bc.run_dr_elect[level - 1] = 1;
75 }
76
77 static int isis_check_dr_change(struct isis_adjacency *adj, int level)
78 {
79 int i;
80
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 ? */
84 {
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
91 - 1]
92 .dis;
93 adj->dis_record[(i * ISIS_LEVELS) + level - 1]
94 .last_dis_change =
95 adj->dis_record[((i - 1) * ISIS_LEVELS) + level
96 - 1]
97 .last_dis_change;
98 }
99 }
100 return ISIS_OK;
101 }
102
103 int isis_dr_elect(struct isis_circuit *circuit, int level)
104 {
105 struct list *adjdb;
106 struct listnode *node;
107 struct isis_adjacency *adj, *adj_dr = NULL;
108 struct list *list = list_new();
109 uint8_t own_prio;
110 int biggest_prio = -1;
111 int cmp_res, retval = ISIS_OK;
112
113 own_prio = circuit->priority[level - 1];
114 adjdb = circuit->u.bc.adjdb[level - 1];
115
116 if (!adjdb) {
117 zlog_warn("%s adjdb == NULL", __func__);
118 list_delete(&list);
119 return ISIS_WARNING;
120 }
121 isis_adj_build_up_list(adjdb, list);
122
123 /*
124 * Loop the adjacencies and find the one with the biggest priority
125 */
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);
130
131 if (adj->prio[level - 1] > biggest_prio) {
132 biggest_prio = adj->prio[level - 1];
133 adj_dr = adj;
134 } else if (adj->prio[level - 1] == biggest_prio) {
135 /*
136 * Comparison of MACs breaks a tie
137 */
138 if (adj_dr) {
139 cmp_res = memcmp(adj_dr->snpa, adj->snpa,
140 ETH_ALEN);
141 if (cmp_res < 0) {
142 adj_dr = adj;
143 }
144 if (cmp_res == 0)
145 zlog_warn(
146 "%s: multiple adjacencies with same SNPA",
147 __func__);
148 } else {
149 adj_dr = adj;
150 }
151 }
152 }
153
154 if (!adj_dr) {
155 /*
156 * Could not find the DR - means we are alone. Resign if we were
157 * DR.
158 */
159 if (circuit->u.bc.is_dr[level - 1])
160 retval = isis_dr_resign(circuit, level);
161 list_delete(&list);
162 return retval;
163 }
164
165 /*
166 * Now we have the DR adjacency, compare it to self
167 */
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);
173
174 /* rotate the history log */
175 for (ALL_LIST_ELEMENTS_RO(list, node, adj))
176 isis_check_dr_change(adj, level);
177
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);
181 } else {
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);
186
187 /* now loop through a second time to check if there has been a
188 * DIS change
189 * if yes rotate the history log
190 */
191
192 for (ALL_LIST_ELEMENTS_RO(list, node, adj))
193 isis_check_dr_change(adj, level);
194
195 /*
196 * We are not DR - if we were -> resign
197 */
198 if (circuit->u.bc.is_dr[level - 1])
199 retval = isis_dr_resign(circuit, level);
200 }
201 list_delete(&list);
202 return retval;
203 }
204
205 int isis_dr_resign(struct isis_circuit *circuit, int level)
206 {
207 uint8_t id[ISIS_SYS_ID_LEN + 2];
208
209 if (IS_DEBUG_EVENTS)
210 zlog_debug("%s l%d", __func__, level);
211
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;
217
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);
222
223 if (level == 1) {
224 memset(circuit->u.bc.l1_desig_is, 0, ISIS_SYS_ID_LEN + 1);
225
226 thread_add_timer(master, send_l1_psnp, circuit,
227 isis_jitter(circuit->psnp_interval[level - 1],
228 PSNP_JITTER),
229 &circuit->t_send_psnp[0]);
230 } else {
231 memset(circuit->u.bc.l2_desig_is, 0, ISIS_SYS_ID_LEN + 1);
232
233 thread_add_timer(master, send_l2_psnp, circuit,
234 isis_jitter(circuit->psnp_interval[level - 1],
235 PSNP_JITTER),
236 &circuit->t_send_psnp[1]);
237 }
238
239 THREAD_OFF(circuit->t_send_csnp[level - 1]);
240
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]);
245
246
247 thread_add_event(master, isis_event_dis_status_change, circuit, 0,
248 NULL);
249
250 return ISIS_OK;
251 }
252
253 int isis_dr_commence(struct isis_circuit *circuit, int level)
254 {
255 uint8_t old_dr[ISIS_SYS_ID_LEN + 2];
256
257 if (IS_DEBUG_EVENTS)
258 zlog_debug("%s l%d", __func__, level);
259
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;
263
264 if (level == 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);
270 }
271 memcpy(circuit->u.bc.l1_desig_is, circuit->isis->sysid,
272 ISIS_SYS_ID_LEN);
273 *(circuit->u.bc.l1_desig_is + ISIS_SYS_ID_LEN) =
274 circuit->circuit_id;
275
276 assert(circuit->circuit_id); /* must be non-zero */
277 lsp_generate_pseudo(circuit, 1);
278
279 thread_add_timer(master, send_l1_csnp, circuit,
280 isis_jitter(circuit->csnp_interval[level - 1],
281 CSNP_JITTER),
282 &circuit->t_send_csnp[0]);
283
284 } else {
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);
290 }
291 memcpy(circuit->u.bc.l2_desig_is, circuit->isis->sysid,
292 ISIS_SYS_ID_LEN);
293 *(circuit->u.bc.l2_desig_is + ISIS_SYS_ID_LEN) =
294 circuit->circuit_id;
295
296 assert(circuit->circuit_id); /* must be non-zero */
297 lsp_generate_pseudo(circuit, 2);
298
299 thread_add_timer(master, send_l2_csnp, circuit,
300 isis_jitter(circuit->csnp_interval[level - 1],
301 CSNP_JITTER),
302 &circuit->t_send_csnp[1]);
303 }
304
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,
310 NULL);
311
312 return ISIS_OK;
313 }