blob: 5b7d23e6daf0bb1a814b0d77992f50f41da6afcc [file] [log] [blame]
jardineb5d44e2003-12-23 08:09:43 +00001/*
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; if not, write to the Free Software Foundation, Inc.,
21 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 */
23
24
25#include <zebra.h>
26#include <net/ethernet.h>
27
28#include "log.h"
29#include "hash.h"
30#include "thread.h"
31#include "linklist.h"
32#include "vty.h"
33#include "stream.h"
34#include "if.h"
35
36#include "isisd/dict.h"
37#include "isisd/isis_constants.h"
38#include "isisd/isis_common.h"
39#include "isisd/isis_misc.h"
40#include "isisd/isis_flags.h"
41#include "isisd/isis_circuit.h"
42#include "isisd/isisd.h"
43#include "isisd/isis_adjacency.h"
44#include "isisd/isis_constants.h"
45#include "isisd/isis_pdu.h"
46#include "isisd/isis_tlv.h"
47#include "isisd/isis_lsp.h"
48#include "isisd/isis_dr.h"
49#include "isisd/isis_events.h"
50
51extern struct isis *isis;
52extern struct thread_master *master;
53
54char *
55isis_disflag2string (int disflag) {
56
57 switch (disflag) {
58 case ISIS_IS_NOT_DIS:
59 return "is not DIS";
60 case ISIS_IS_DIS:
61 return "is DIS";
62 case ISIS_WAS_DIS:
63 return "was DIS";
64 default:
65 return "unknown DIS state";
66 }
67 return NULL; /* not reached */
68}
69
70
71
72int
73isis_run_dr_l1 (struct thread *thread)
74{
75 struct isis_circuit *circuit;
76
77 circuit = THREAD_ARG (thread);
78 assert (circuit);
79
80 if (circuit->u.bc.run_dr_elect[0])
81 zlog_warn ("isis_run_dr(): run_dr_elect already set for l1");
82
83 circuit->u.bc.run_dr_elect[0] = 1;
84
85 return ISIS_OK;
86}
87
88int
89isis_run_dr_l2 (struct thread *thread)
90{
91 struct isis_circuit *circuit;
92
93 circuit = THREAD_ARG (thread);
94 assert (circuit);
95
96 if (circuit->u.bc.run_dr_elect[1])
97 zlog_warn ("isis_run_dr(): run_dr_elect already set for l2");
98
99
100 circuit->u.bc.run_dr_elect[1] = 1;
101
102 return ISIS_OK;
103}
104
105int
106isis_check_dr_change (struct isis_adjacency *adj, int level)
107{
108 int i;
109
110 if ( adj->dis_record[level-1].dis !=
111 adj->dis_record[(1*ISIS_LEVELS) + level - 1].dis)
112 /* was there a DIS state transition ? */
113 {
114 adj->dischanges[level-1]++;
115 /* ok rotate the history list through */
116 for (i = DIS_RECORDS - 1; i > 0; i--)
117 {
118 adj->dis_record[(i * ISIS_LEVELS) + level - 1].dis =
119 adj->dis_record[((i-1) * ISIS_LEVELS) + level - 1].dis;
120 adj->dis_record[(i * ISIS_LEVELS) + level - 1].last_dis_change =
121 adj->dis_record[((i-1) * ISIS_LEVELS) + level - 1].last_dis_change;
122 }
123 }
124 return ISIS_OK;
125}
126
127int
128isis_dr_elect (struct isis_circuit *circuit, int level)
129{
130 struct list *adjdb;
131 struct listnode *node;
132 struct isis_adjacency *adj, *adj_dr = NULL;
133 struct list *list = list_new ();
134 u_char own_prio;
135 int biggest_prio = -1;
136 int cmp_res, retval = ISIS_OK;
137
138 own_prio = circuit->u.bc.priority[level - 1];
139 adjdb = circuit->u.bc.adjdb[level - 1];
140
141 if (!adjdb) {
142 zlog_warn ("isis_dr_elect() adjdb == NULL");
143 retval = ISIS_WARNING;
144 list_delete (list);
145 goto out;
146 }
147 isis_adj_build_up_list (adjdb, list);
148
149 /*
150 * Loop the adjacencies and find the one with the biggest priority
151 */
152 for (node = listhead (list); node; nextnode (node)) {
153 adj = getdata (node);
154 /* clear flag for show output */
155 adj->dis_record[level-1].dis = ISIS_IS_NOT_DIS;
156 adj->dis_record[level-1].last_dis_change = time (NULL);
157
158 if (adj->prio[level-1] > biggest_prio) {
159 biggest_prio = adj->prio[level-1];
160 adj_dr = adj;
161 } else if (adj->prio[level-1] == biggest_prio) {
162 /*
163 * Comparison of MACs breaks a tie
164 */
165 if (adj_dr) {
166 cmp_res = memcmp (adj_dr->snpa, adj->snpa, ETH_ALEN);
167 if (cmp_res < 0) {
168 adj_dr = adj;
169 }
170 if (cmp_res == 0)
171 zlog_warn ("isis_dr_elect(): multiple adjacencies with same SNPA");
172 } else {
173 adj_dr = adj;
174 }
175 }
176 }
177
178 if (!adj_dr) {
179 /*
180 * Could not find the DR - means we are alone and thus the DR
181 */
182 if ( !circuit->u.bc.is_dr[level - 1]) {
183 list_delete (list);
184 list = NULL;
185 return isis_dr_commence (circuit, level);
186 }
187 goto out;
188 }
189
190 /*
191 * Now we have the DR adjacency, compare it to self
192 */
193 if (adj_dr->prio[level-1] < own_prio || (adj_dr->prio[level-1] == own_prio &&
194 memcmp (adj_dr->snpa, circuit->u.bc.snpa,
195 ETH_ALEN) < 0)) {
196 if (!circuit->u.bc.is_dr[level - 1]) {
197 /*
198 * We are the DR -> commence
199 */
200 list_delete (list);
201 return isis_dr_commence (circuit, level);
202 }
203 } else {
204
205 /* ok we have found the DIS - lets mark the adjacency */
206 /* set flag for show output */
207 adj_dr->dis_record[level - 1].dis = ISIS_IS_DIS;
208 adj_dr->dis_record[level - 1].last_dis_change = time(NULL);
209
210 /* now loop through a second time to check if there has been a DIS change
211 * if yes rotate the history log
212 */
213
214 for (node = listhead (list); node; nextnode (node)) {
215 adj = getdata (node);
216 isis_check_dr_change(adj, level);
217 }
218
219 /*
220 * We are not DR - if we were -> resign
221 */
222
223 if (circuit->u.bc.is_dr[level - 1]) {
224 list_delete (list);
225 return isis_dr_resign (circuit, level);
226 }
227 }
228 out:
229 if (list)
230 list_delete (list);
231 return retval;
232}
233
234int
235isis_dr_resign (struct isis_circuit *circuit, int level)
236{
237 u_char id[ISIS_SYS_ID_LEN + 2];
238
239 zlog_info ("isis_dr_resign l%d", level);
240
241 circuit->u.bc.is_dr[level - 1] = 0;
242 circuit->u.bc.run_dr_elect[level - 1] = 0;
243 if (circuit->u.bc.t_run_dr[level - 1]) {
244 thread_cancel (circuit->u.bc.t_run_dr[level - 1]);
245 circuit->u.bc.t_run_dr[level - 1] = NULL;
246 }
247 if (circuit->u.bc.t_refresh_pseudo_lsp[level - 1]) {
248 thread_cancel (circuit->u.bc.t_refresh_pseudo_lsp[level - 1]);
249 circuit->u.bc.t_refresh_pseudo_lsp[level - 1] = NULL;
250 }
251
252 memcpy (id, isis->sysid, ISIS_SYS_ID_LEN);
253 LSP_PSEUDO_ID(id) = circuit->circuit_id;
254 LSP_FRAGMENT(id) = 0;
255 lsp_purge_dr (id, circuit, level);
256
257 if (level == 1) {
258 memset (circuit->u.bc.l1_desig_is, 0, ISIS_SYS_ID_LEN + 1);
259
260 if (circuit->t_send_csnp[0])
261 thread_cancel (circuit->t_send_csnp[0]);
262
263 circuit->u.bc.t_run_dr[0] =
264 thread_add_timer (master, isis_run_dr_l1, circuit,
265 2 * circuit->hello_interval[1]);
266
267 circuit->t_send_psnp[0] =
268 thread_add_timer (master,
269 send_l1_psnp,
270 circuit,
271 isis_jitter (circuit->psnp_interval[level - 1],
272 PSNP_JITTER));
273 } else {
274 memset (circuit->u.bc.l2_desig_is, 0, ISIS_SYS_ID_LEN + 1);
275
276 if (circuit->t_send_csnp[0])
277 thread_cancel (circuit->t_send_csnp[0]);
278
279 circuit->u.bc.t_run_dr[1] =
280 thread_add_timer (master, isis_run_dr_l2, circuit,
281 2 * circuit->hello_interval[1]);
282 circuit->t_send_psnp[1] =
283 thread_add_timer (master,
284 send_l2_psnp,
285 circuit,
286 isis_jitter (circuit->psnp_interval[level - 1],
287 PSNP_JITTER));
288 }
289
290 thread_add_event (master, isis_event_dis_status_change, circuit, 0);
291
292 return ISIS_OK;
293}
294
295int
296isis_dr_commence (struct isis_circuit *circuit, int level)
297{
298 u_char old_dr[ISIS_SYS_ID_LEN + 2];
299
300 zlog_info ("isis_dr_commence l%d", level);
301
302 /* Lets keep a pause in DR election */
303 circuit->u.bc.run_dr_elect[level - 1] = 0;
304 if (level == 1)
305 circuit->u.bc.t_run_dr[0] =
306 thread_add_timer (master, isis_run_dr_l1, circuit,
307 2 * circuit->hello_multiplier[0] *
308 circuit->hello_interval[0]);
309 else
310 circuit->u.bc.t_run_dr[1] =
311 thread_add_timer (master, isis_run_dr_l2, circuit,
312 2 * circuit->hello_multiplier[1] *
313 circuit->hello_interval[1]);
314 circuit->u.bc.is_dr[level - 1] = 1;
315
316 if (level == 1) {
317 memcpy (old_dr, circuit->u.bc.l1_desig_is, ISIS_SYS_ID_LEN + 1);
318 LSP_FRAGMENT (old_dr) = 0;
319 if (LSP_PSEUDO_ID(old_dr)) {
320 /* there was a dr elected, purge its LSPs from the db */
321 lsp_purge_dr (old_dr, circuit, level);
322 }
323 memcpy (circuit->u.bc.l1_desig_is, isis->sysid, ISIS_SYS_ID_LEN);
324 *(circuit->u.bc.l1_desig_is + ISIS_SYS_ID_LEN) = circuit->circuit_id;
325
326 assert (circuit->circuit_id); /* must be non-zero */
327 /* if (circuit->t_send_l1_psnp)
328 thread_cancel (circuit->t_send_l1_psnp); */
329 lsp_l1_pseudo_generate (circuit);
330
331 circuit->u.bc.t_run_dr[0] =
332 thread_add_timer (master, isis_run_dr_l1, circuit,
333 2 * circuit->hello_interval[0]);
334
335 circuit->t_send_csnp[0] = thread_add_timer (master,
336 send_l1_csnp,
337 circuit,
338 isis_jitter
339 (circuit->csnp_interval[level-1],
340 CSNP_JITTER));
341 } else {
342 memcpy (old_dr, circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1);
343 LSP_FRAGMENT (old_dr) = 0;
344 if (LSP_PSEUDO_ID(old_dr)) {
345 /* there was a dr elected, purge its LSPs from the db */
346 lsp_purge_dr (old_dr, circuit, level);
347 }
348 memcpy (circuit->u.bc.l2_desig_is, isis->sysid, ISIS_SYS_ID_LEN);
349 *(circuit->u.bc.l2_desig_is + ISIS_SYS_ID_LEN) = circuit->circuit_id;
350
351 assert (circuit->circuit_id); /* must be non-zero */
352 /* if (circuit->t_send_l1_psnp)
353 thread_cancel (circuit->t_send_l1_psnp); */
354 lsp_l2_pseudo_generate (circuit);
355
356 circuit->u.bc.t_run_dr[1] =
357 thread_add_timer (master, isis_run_dr_l2, circuit,
358 2 * circuit->hello_interval[1]);
359
360 circuit->t_send_csnp[1] =
361 thread_add_timer (master,
362 send_l2_csnp,
363 circuit,
364 isis_jitter (circuit->csnp_interval[level-1],
365 CSNP_JITTER));
366
367 }
368
369 thread_add_event (master, isis_event_dis_status_change, circuit, 0);
370
371 return ISIS_OK;
372}
373