]> git.proxmox.com Git - mirror_frr.git/blob - isisd/isis_dr.c
Merge pull request #900 from qlyoung/apply-mask
[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/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"
48
49 const char *isis_disflag2string(int disflag)
50 {
51
52 switch (disflag) {
53 case ISIS_IS_NOT_DIS:
54 return "is not DIS";
55 case ISIS_IS_DIS:
56 return "is DIS";
57 case ISIS_WAS_DIS:
58 return "was DIS";
59 default:
60 return "unknown DIS state";
61 }
62 return NULL; /* not reached */
63 }
64
65 int isis_run_dr_l1(struct thread *thread)
66 {
67 struct isis_circuit *circuit;
68
69 circuit = THREAD_ARG(thread);
70 assert(circuit);
71
72 if (circuit->u.bc.run_dr_elect[0])
73 zlog_warn("isis_run_dr(): run_dr_elect already set for l1");
74
75 circuit->u.bc.t_run_dr[0] = NULL;
76 circuit->u.bc.run_dr_elect[0] = 1;
77
78 return ISIS_OK;
79 }
80
81 int isis_run_dr_l2(struct thread *thread)
82 {
83 struct isis_circuit *circuit;
84
85 circuit = THREAD_ARG(thread);
86 assert(circuit);
87
88 if (circuit->u.bc.run_dr_elect[1])
89 zlog_warn("isis_run_dr(): run_dr_elect already set for l2");
90
91
92 circuit->u.bc.t_run_dr[1] = NULL;
93 circuit->u.bc.run_dr_elect[1] = 1;
94
95 return ISIS_OK;
96 }
97
98 static int isis_check_dr_change(struct isis_adjacency *adj, int level)
99 {
100 int i;
101
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 ? */
105 {
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
111 - 1]
112 .dis;
113 adj->dis_record[(i * ISIS_LEVELS) + level - 1]
114 .last_dis_change =
115 adj->dis_record[((i - 1) * ISIS_LEVELS) + level
116 - 1]
117 .last_dis_change;
118 }
119 }
120 return ISIS_OK;
121 }
122
123 int isis_dr_elect(struct isis_circuit *circuit, int level)
124 {
125 struct list *adjdb;
126 struct listnode *node;
127 struct isis_adjacency *adj, *adj_dr = NULL;
128 struct list *list = list_new();
129 u_char own_prio;
130 int biggest_prio = -1;
131 int cmp_res, retval = ISIS_OK;
132
133 own_prio = circuit->priority[level - 1];
134 adjdb = circuit->u.bc.adjdb[level - 1];
135
136 if (!adjdb) {
137 zlog_warn("isis_dr_elect() adjdb == NULL");
138 list_delete(list);
139 return ISIS_WARNING;
140 }
141 isis_adj_build_up_list(adjdb, list);
142
143 /*
144 * Loop the adjacencies and find the one with the biggest priority
145 */
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);
150
151 if (adj->prio[level - 1] > biggest_prio) {
152 biggest_prio = adj->prio[level - 1];
153 adj_dr = adj;
154 } else if (adj->prio[level - 1] == biggest_prio) {
155 /*
156 * Comparison of MACs breaks a tie
157 */
158 if (adj_dr) {
159 cmp_res = memcmp(adj_dr->snpa, adj->snpa,
160 ETH_ALEN);
161 if (cmp_res < 0) {
162 adj_dr = adj;
163 }
164 if (cmp_res == 0)
165 zlog_warn(
166 "isis_dr_elect(): multiple adjacencies with same SNPA");
167 } else {
168 adj_dr = adj;
169 }
170 }
171 }
172
173 if (!adj_dr) {
174 /*
175 * Could not find the DR - means we are alone. Resign if we were
176 * DR.
177 */
178 if (circuit->u.bc.is_dr[level - 1])
179 retval = isis_dr_resign(circuit, level);
180 list_delete(list);
181 return retval;
182 }
183
184 /*
185 * Now we have the DR adjacency, compare it to self
186 */
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);
192
193 /* rotate the history log */
194 for (ALL_LIST_ELEMENTS_RO(list, node, adj))
195 isis_check_dr_change(adj, level);
196
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);
200 } else {
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);
205
206 /* now loop through a second time to check if there has been a
207 * DIS change
208 * if yes rotate the history log
209 */
210
211 for (ALL_LIST_ELEMENTS_RO(list, node, adj))
212 isis_check_dr_change(adj, level);
213
214 /*
215 * We are not DR - if we were -> resign
216 */
217 if (circuit->u.bc.is_dr[level - 1])
218 retval = isis_dr_resign(circuit, level);
219 }
220 list_delete(list);
221 return retval;
222 }
223
224 int isis_dr_resign(struct isis_circuit *circuit, int level)
225 {
226 u_char id[ISIS_SYS_ID_LEN + 2];
227
228 zlog_debug("isis_dr_resign l%d", level);
229
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;
235
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);
240
241 if (level == 1) {
242 memset(circuit->u.bc.l1_desig_is, 0, ISIS_SYS_ID_LEN + 1);
243
244 THREAD_TIMER_OFF(circuit->t_send_csnp[0]);
245
246 thread_add_timer(master, isis_run_dr_l1, circuit,
247 2 * circuit->hello_interval[0],
248 &circuit->u.bc.t_run_dr[0]);
249
250 thread_add_timer(master, send_l1_psnp, circuit,
251 isis_jitter(circuit->psnp_interval[level - 1],
252 PSNP_JITTER),
253 &circuit->t_send_psnp[0]);
254 } else {
255 memset(circuit->u.bc.l2_desig_is, 0, ISIS_SYS_ID_LEN + 1);
256
257 THREAD_TIMER_OFF(circuit->t_send_csnp[1]);
258
259 thread_add_timer(master, isis_run_dr_l2, circuit,
260 2 * circuit->hello_interval[1],
261 &circuit->u.bc.t_run_dr[1]);
262
263 thread_add_timer(master, send_l2_psnp, circuit,
264 isis_jitter(circuit->psnp_interval[level - 1],
265 PSNP_JITTER),
266 &circuit->t_send_psnp[1]);
267 }
268
269 thread_add_event(master, isis_event_dis_status_change, circuit, 0,
270 NULL);
271
272 return ISIS_OK;
273 }
274
275 int isis_dr_commence(struct isis_circuit *circuit, int level)
276 {
277 u_char old_dr[ISIS_SYS_ID_LEN + 2];
278
279 if (isis->debugs & DEBUG_EVENTS)
280 zlog_debug("isis_dr_commence l%d", level);
281
282 /* Lets keep a pause in DR election */
283 circuit->u.bc.run_dr_elect[level - 1] = 0;
284 if (level == 1)
285 thread_add_timer(master, isis_run_dr_l1, circuit,
286 2 * circuit->hello_interval[0],
287 &circuit->u.bc.t_run_dr[0]);
288 else
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;
293
294 if (level == 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);
300 }
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) =
303 circuit->circuit_id;
304
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);
309
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]);
314
315 thread_add_timer(master, send_l1_csnp, circuit,
316 isis_jitter(circuit->csnp_interval[level - 1],
317 CSNP_JITTER),
318 &circuit->t_send_csnp[0]);
319
320 } else {
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);
326 }
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) =
329 circuit->circuit_id;
330
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);
335
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]);
340
341 thread_add_timer(master, send_l2_csnp, circuit,
342 isis_jitter(circuit->csnp_interval[level - 1],
343 CSNP_JITTER),
344 &circuit->t_send_csnp[1]);
345 }
346
347 thread_add_event(master, isis_event_dis_status_change, circuit, 0,
348 NULL);
349
350 return ISIS_OK;
351 }