]> git.proxmox.com Git - mirror_frr.git/blob - isisd/isis_dr.c
Merge pull request #11299 from taspelund/update-svi-macip
[mirror_frr.git] / isisd / isis_dr.c
1 /*
2 * IS-IS Rout(e)ing protocol - isis_dr.c
3 * IS-IS designated router related routines
4 *
5 * Copyright (C) 2001,2002 Sampo Saaristo
6 * Tampere University of Technology
7 * Institute of Communications Engineering
8 *
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)
12 * any later version.
13 *
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
17 * more details.
18 *
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
22 */
23
24
25 #include <zebra.h>
26
27 #include "log.h"
28 #include "hash.h"
29 #include "thread.h"
30 #include "linklist.h"
31 #include "vty.h"
32 #include "stream.h"
33 #include "if.h"
34
35 #include "isisd/isis_constants.h"
36 #include "isisd/isis_common.h"
37 #include "isisd/isis_misc.h"
38 #include "isisd/isis_flags.h"
39 #include "isisd/isis_circuit.h"
40 #include "isisd/isisd.h"
41 #include "isisd/isis_adjacency.h"
42 #include "isisd/isis_constants.h"
43 #include "isisd/isis_pdu.h"
44 #include "isisd/isis_lsp.h"
45 #include "isisd/isis_dr.h"
46 #include "isisd/isis_events.h"
47
48 const char *isis_disflag2string(int disflag)
49 {
50
51 switch (disflag) {
52 case ISIS_IS_NOT_DIS:
53 return "is not DIS";
54 case ISIS_IS_DIS:
55 return "is DIS";
56 case ISIS_WAS_DIS:
57 return "was DIS";
58 default:
59 return "unknown DIS state";
60 }
61 return NULL; /* not reached */
62 }
63
64 void isis_run_dr(struct thread *thread)
65 {
66 struct isis_circuit_arg *arg = THREAD_ARG(thread);
67
68 assert(arg);
69
70 struct isis_circuit *circuit = arg->circuit;
71 int level = arg->level;
72
73 assert(circuit);
74
75 if (circuit->circ_type != CIRCUIT_T_BROADCAST) {
76 zlog_warn("%s: scheduled for non broadcast circuit from %s:%d",
77 __func__, thread->xref->xref.file,
78 thread->xref->xref.line);
79 return;
80 }
81
82 if (circuit->u.bc.run_dr_elect[level - 1])
83 zlog_warn("isis_run_dr(): run_dr_elect already set for l%d", level);
84
85 circuit->u.bc.t_run_dr[level - 1] = NULL;
86 circuit->u.bc.run_dr_elect[level - 1] = 1;
87 }
88
89 static int isis_check_dr_change(struct isis_adjacency *adj, int level)
90 {
91 int i;
92
93 if (adj->dis_record[level - 1].dis
94 != adj->dis_record[(1 * ISIS_LEVELS) + level - 1].dis)
95 /* was there a DIS state transition ? */
96 {
97 adj->dischanges[level - 1]++;
98 adj->circuit->desig_changes[level - 1]++;
99 /* ok rotate the history list through */
100 for (i = DIS_RECORDS - 1; i > 0; i--) {
101 adj->dis_record[(i * ISIS_LEVELS) + level - 1].dis =
102 adj->dis_record[((i - 1) * ISIS_LEVELS) + level
103 - 1]
104 .dis;
105 adj->dis_record[(i * ISIS_LEVELS) + level - 1]
106 .last_dis_change =
107 adj->dis_record[((i - 1) * ISIS_LEVELS) + level
108 - 1]
109 .last_dis_change;
110 }
111 }
112 return ISIS_OK;
113 }
114
115 int isis_dr_elect(struct isis_circuit *circuit, int level)
116 {
117 struct list *adjdb;
118 struct listnode *node;
119 struct isis_adjacency *adj, *adj_dr = NULL;
120 struct list *list = list_new();
121 uint8_t own_prio;
122 int biggest_prio = -1;
123 int cmp_res, retval = ISIS_OK;
124
125 own_prio = circuit->priority[level - 1];
126 adjdb = circuit->u.bc.adjdb[level - 1];
127
128 if (!adjdb) {
129 zlog_warn("isis_dr_elect() adjdb == NULL");
130 list_delete(&list);
131 return ISIS_WARNING;
132 }
133 isis_adj_build_up_list(adjdb, list);
134
135 /*
136 * Loop the adjacencies and find the one with the biggest priority
137 */
138 for (ALL_LIST_ELEMENTS_RO(list, node, adj)) {
139 /* clear flag for show output */
140 adj->dis_record[level - 1].dis = ISIS_IS_NOT_DIS;
141 adj->dis_record[level - 1].last_dis_change = time(NULL);
142
143 if (adj->prio[level - 1] > biggest_prio) {
144 biggest_prio = adj->prio[level - 1];
145 adj_dr = adj;
146 } else if (adj->prio[level - 1] == biggest_prio) {
147 /*
148 * Comparison of MACs breaks a tie
149 */
150 if (adj_dr) {
151 cmp_res = memcmp(adj_dr->snpa, adj->snpa,
152 ETH_ALEN);
153 if (cmp_res < 0) {
154 adj_dr = adj;
155 }
156 if (cmp_res == 0)
157 zlog_warn(
158 "isis_dr_elect(): multiple adjacencies with same SNPA");
159 } else {
160 adj_dr = adj;
161 }
162 }
163 }
164
165 if (!adj_dr) {
166 /*
167 * Could not find the DR - means we are alone. Resign if we were
168 * DR.
169 */
170 if (circuit->u.bc.is_dr[level - 1])
171 retval = isis_dr_resign(circuit, level);
172 list_delete(&list);
173 return retval;
174 }
175
176 /*
177 * Now we have the DR adjacency, compare it to self
178 */
179 if (adj_dr->prio[level - 1] < own_prio
180 || (adj_dr->prio[level - 1] == own_prio
181 && memcmp(adj_dr->snpa, circuit->u.bc.snpa, ETH_ALEN) < 0)) {
182 adj_dr->dis_record[level - 1].dis = ISIS_IS_NOT_DIS;
183 adj_dr->dis_record[level - 1].last_dis_change = time(NULL);
184
185 /* rotate the history log */
186 for (ALL_LIST_ELEMENTS_RO(list, node, adj))
187 isis_check_dr_change(adj, level);
188
189 /* We are the DR, commence DR */
190 if (circuit->u.bc.is_dr[level - 1] == 0 && listcount(list) > 0)
191 retval = isis_dr_commence(circuit, level);
192 } else {
193 /* ok we have found the DIS - lets mark the adjacency */
194 /* set flag for show output */
195 adj_dr->dis_record[level - 1].dis = ISIS_IS_DIS;
196 adj_dr->dis_record[level - 1].last_dis_change = time(NULL);
197
198 /* now loop through a second time to check if there has been a
199 * DIS change
200 * if yes rotate the history log
201 */
202
203 for (ALL_LIST_ELEMENTS_RO(list, node, adj))
204 isis_check_dr_change(adj, level);
205
206 /*
207 * We are not DR - if we were -> resign
208 */
209 if (circuit->u.bc.is_dr[level - 1])
210 retval = isis_dr_resign(circuit, level);
211 }
212 list_delete(&list);
213 return retval;
214 }
215
216 int isis_dr_resign(struct isis_circuit *circuit, int level)
217 {
218 uint8_t id[ISIS_SYS_ID_LEN + 2];
219
220 if (IS_DEBUG_EVENTS)
221 zlog_debug("isis_dr_resign l%d", level);
222
223 circuit->u.bc.is_dr[level - 1] = 0;
224 circuit->u.bc.run_dr_elect[level - 1] = 0;
225 thread_cancel(&circuit->u.bc.t_run_dr[level - 1]);
226 thread_cancel(&circuit->u.bc.t_refresh_pseudo_lsp[level - 1]);
227 circuit->lsp_regenerate_pending[level - 1] = 0;
228
229 memcpy(id, circuit->isis->sysid, ISIS_SYS_ID_LEN);
230 LSP_PSEUDO_ID(id) = circuit->circuit_id;
231 LSP_FRAGMENT(id) = 0;
232 lsp_purge_pseudo(id, circuit, level);
233
234 if (level == 1) {
235 memset(circuit->u.bc.l1_desig_is, 0, ISIS_SYS_ID_LEN + 1);
236
237 thread_add_timer(master, send_l1_psnp, circuit,
238 isis_jitter(circuit->psnp_interval[level - 1],
239 PSNP_JITTER),
240 &circuit->t_send_psnp[0]);
241 } else {
242 memset(circuit->u.bc.l2_desig_is, 0, ISIS_SYS_ID_LEN + 1);
243
244 thread_add_timer(master, send_l2_psnp, circuit,
245 isis_jitter(circuit->psnp_interval[level - 1],
246 PSNP_JITTER),
247 &circuit->t_send_psnp[1]);
248 }
249
250 thread_cancel(&circuit->t_send_csnp[level - 1]);
251
252 thread_add_timer(master, isis_run_dr,
253 &circuit->level_arg[level - 1],
254 2 * circuit->hello_interval[level - 1],
255 &circuit->u.bc.t_run_dr[level - 1]);
256
257
258 thread_add_event(master, isis_event_dis_status_change, circuit, 0,
259 NULL);
260
261 return ISIS_OK;
262 }
263
264 int isis_dr_commence(struct isis_circuit *circuit, int level)
265 {
266 uint8_t old_dr[ISIS_SYS_ID_LEN + 2];
267
268 if (IS_DEBUG_EVENTS)
269 zlog_debug("isis_dr_commence l%d", level);
270
271 /* Lets keep a pause in DR election */
272 circuit->u.bc.run_dr_elect[level - 1] = 0;
273 circuit->u.bc.is_dr[level - 1] = 1;
274
275 if (level == 1) {
276 memcpy(old_dr, circuit->u.bc.l1_desig_is, ISIS_SYS_ID_LEN + 1);
277 LSP_FRAGMENT(old_dr) = 0;
278 if (LSP_PSEUDO_ID(old_dr)) {
279 /* there was a dr elected, purge its LSPs from the db */
280 lsp_purge_pseudo(old_dr, circuit, level);
281 }
282 memcpy(circuit->u.bc.l1_desig_is, circuit->isis->sysid,
283 ISIS_SYS_ID_LEN);
284 *(circuit->u.bc.l1_desig_is + ISIS_SYS_ID_LEN) =
285 circuit->circuit_id;
286
287 assert(circuit->circuit_id); /* must be non-zero */
288 /* if (circuit->t_send_l1_psnp)
289 thread_cancel (circuit->t_send_l1_psnp); */
290 lsp_generate_pseudo(circuit, 1);
291
292 thread_add_timer(master, send_l1_csnp, circuit,
293 isis_jitter(circuit->csnp_interval[level - 1],
294 CSNP_JITTER),
295 &circuit->t_send_csnp[0]);
296
297 } else {
298 memcpy(old_dr, circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1);
299 LSP_FRAGMENT(old_dr) = 0;
300 if (LSP_PSEUDO_ID(old_dr)) {
301 /* there was a dr elected, purge its LSPs from the db */
302 lsp_purge_pseudo(old_dr, circuit, level);
303 }
304 memcpy(circuit->u.bc.l2_desig_is, circuit->isis->sysid,
305 ISIS_SYS_ID_LEN);
306 *(circuit->u.bc.l2_desig_is + ISIS_SYS_ID_LEN) =
307 circuit->circuit_id;
308
309 assert(circuit->circuit_id); /* must be non-zero */
310 /* if (circuit->t_send_l1_psnp)
311 thread_cancel (circuit->t_send_l1_psnp); */
312 lsp_generate_pseudo(circuit, 2);
313
314 thread_add_timer(master, send_l2_csnp, circuit,
315 isis_jitter(circuit->csnp_interval[level - 1],
316 CSNP_JITTER),
317 &circuit->t_send_csnp[1]);
318 }
319
320 thread_add_timer(master, isis_run_dr,
321 &circuit->level_arg[level - 1],
322 2 * circuit->hello_interval[level - 1],
323 &circuit->u.bc.t_run_dr[level - 1]);
324 thread_add_event(master, isis_event_dis_status_change, circuit, 0,
325 NULL);
326
327 return ISIS_OK;
328 }