blob: a2fddcce8fe4581d1ddd92fe69a2aa52b72b0a3c [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>
jardineb5d44e2003-12-23 08:09:43 +000026
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_tlv.h"
46#include "isisd/isis_lsp.h"
47#include "isisd/isis_dr.h"
48#include "isisd/isis_events.h"
49
50extern struct isis *isis;
51extern struct thread_master *master;
52
53char *
54isis_disflag2string (int disflag) {
55
56 switch (disflag) {
57 case ISIS_IS_NOT_DIS:
58 return "is not DIS";
59 case ISIS_IS_DIS:
60 return "is DIS";
61 case ISIS_WAS_DIS:
62 return "was DIS";
63 default:
64 return "unknown DIS state";
65 }
66 return NULL; /* not reached */
67}
68
69
70
71int
72isis_run_dr_l1 (struct thread *thread)
73{
74 struct isis_circuit *circuit;
75
76 circuit = THREAD_ARG (thread);
77 assert (circuit);
78
79 if (circuit->u.bc.run_dr_elect[0])
80 zlog_warn ("isis_run_dr(): run_dr_elect already set for l1");
81
hassoefc1e722003-12-31 20:33:23 +000082 circuit->u.bc.t_run_dr[0] = NULL;
jardineb5d44e2003-12-23 08:09:43 +000083 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
hassoefc1e722003-12-31 20:33:23 +0000100 circuit->u.bc.t_run_dr[1] = NULL;
jardineb5d44e2003-12-23 08:09:43 +0000101 circuit->u.bc.run_dr_elect[1] = 1;
102
103 return ISIS_OK;
104}
105
106int
107isis_check_dr_change (struct isis_adjacency *adj, int level)
108{
109 int i;
110
111 if ( adj->dis_record[level-1].dis !=
112 adj->dis_record[(1*ISIS_LEVELS) + level - 1].dis)
113 /* was there a DIS state transition ? */
114 {
115 adj->dischanges[level-1]++;
116 /* ok rotate the history list through */
117 for (i = DIS_RECORDS - 1; i > 0; i--)
118 {
119 adj->dis_record[(i * ISIS_LEVELS) + level - 1].dis =
120 adj->dis_record[((i-1) * ISIS_LEVELS) + level - 1].dis;
121 adj->dis_record[(i * ISIS_LEVELS) + level - 1].last_dis_change =
122 adj->dis_record[((i-1) * ISIS_LEVELS) + level - 1].last_dis_change;
123 }
124 }
125 return ISIS_OK;
126}
127
128int
129isis_dr_elect (struct isis_circuit *circuit, int level)
130{
131 struct list *adjdb;
132 struct listnode *node;
133 struct isis_adjacency *adj, *adj_dr = NULL;
134 struct list *list = list_new ();
135 u_char own_prio;
136 int biggest_prio = -1;
137 int cmp_res, retval = ISIS_OK;
138
139 own_prio = circuit->u.bc.priority[level - 1];
140 adjdb = circuit->u.bc.adjdb[level - 1];
141
142 if (!adjdb) {
143 zlog_warn ("isis_dr_elect() adjdb == NULL");
144 retval = ISIS_WARNING;
145 list_delete (list);
146 goto out;
147 }
148 isis_adj_build_up_list (adjdb, list);
149
150 /*
151 * Loop the adjacencies and find the one with the biggest priority
152 */
153 for (node = listhead (list); node; nextnode (node)) {
154 adj = getdata (node);
155 /* clear flag for show output */
156 adj->dis_record[level-1].dis = ISIS_IS_NOT_DIS;
157 adj->dis_record[level-1].last_dis_change = time (NULL);
158
159 if (adj->prio[level-1] > biggest_prio) {
160 biggest_prio = adj->prio[level-1];
161 adj_dr = adj;
162 } else if (adj->prio[level-1] == biggest_prio) {
163 /*
164 * Comparison of MACs breaks a tie
165 */
166 if (adj_dr) {
167 cmp_res = memcmp (adj_dr->snpa, adj->snpa, ETH_ALEN);
168 if (cmp_res < 0) {
169 adj_dr = adj;
170 }
171 if (cmp_res == 0)
172 zlog_warn ("isis_dr_elect(): multiple adjacencies with same SNPA");
173 } else {
174 adj_dr = adj;
175 }
176 }
177 }
178
179 if (!adj_dr) {
180 /*
181 * Could not find the DR - means we are alone and thus the DR
182 */
183 if ( !circuit->u.bc.is_dr[level - 1]) {
184 list_delete (list);
185 list = NULL;
186 return isis_dr_commence (circuit, level);
187 }
188 goto out;
189 }
190
191 /*
192 * Now we have the DR adjacency, compare it to self
193 */
194 if (adj_dr->prio[level-1] < own_prio || (adj_dr->prio[level-1] == own_prio &&
195 memcmp (adj_dr->snpa, circuit->u.bc.snpa,
196 ETH_ALEN) < 0)) {
197 if (!circuit->u.bc.is_dr[level - 1]) {
198 /*
199 * We are the DR -> commence
200 */
201 list_delete (list);
202 return isis_dr_commence (circuit, level);
203 }
204 } else {
205
206 /* ok we have found the DIS - lets mark the adjacency */
207 /* set flag for show output */
208 adj_dr->dis_record[level - 1].dis = ISIS_IS_DIS;
209 adj_dr->dis_record[level - 1].last_dis_change = time(NULL);
210
211 /* now loop through a second time to check if there has been a DIS change
212 * if yes rotate the history log
213 */
214
215 for (node = listhead (list); node; nextnode (node)) {
216 adj = getdata (node);
217 isis_check_dr_change(adj, level);
218 }
219
220 /*
221 * We are not DR - if we were -> resign
222 */
223
224 if (circuit->u.bc.is_dr[level - 1]) {
225 list_delete (list);
226 return isis_dr_resign (circuit, level);
227 }
228 }
229 out:
230 if (list)
231 list_delete (list);
232 return retval;
233}
234
235int
236isis_dr_resign (struct isis_circuit *circuit, int level)
237{
238 u_char id[ISIS_SYS_ID_LEN + 2];
239
240 zlog_info ("isis_dr_resign l%d", level);
241
242 circuit->u.bc.is_dr[level - 1] = 0;
243 circuit->u.bc.run_dr_elect[level - 1] = 0;
hassod70f99e2004-02-11 20:26:31 +0000244 THREAD_TIMER_OFF(circuit->u.bc.t_run_dr[level - 1]);
245 THREAD_TIMER_OFF(circuit->u.bc.t_refresh_pseudo_lsp[level - 1]);
jardineb5d44e2003-12-23 08:09:43 +0000246
247 memcpy (id, isis->sysid, ISIS_SYS_ID_LEN);
248 LSP_PSEUDO_ID(id) = circuit->circuit_id;
249 LSP_FRAGMENT(id) = 0;
250 lsp_purge_dr (id, circuit, level);
251
252 if (level == 1) {
253 memset (circuit->u.bc.l1_desig_is, 0, ISIS_SYS_ID_LEN + 1);
254
hassod70f99e2004-02-11 20:26:31 +0000255 THREAD_TIMER_OFF(circuit->t_send_csnp[0]);
jardineb5d44e2003-12-23 08:09:43 +0000256
hassod70f99e2004-02-11 20:26:31 +0000257 THREAD_TIMER_ON(master, circuit->u.bc.t_run_dr[0], isis_run_dr_l1,
258 circuit, 2 * circuit->hello_interval[1]);
259
260 THREAD_TIMER_ON(master, circuit->t_send_psnp[0], send_l1_psnp, circuit,
261 isis_jitter (circuit->psnp_interval[level - 1], PSNP_JITTER));
jardineb5d44e2003-12-23 08:09:43 +0000262 } else {
263 memset (circuit->u.bc.l2_desig_is, 0, ISIS_SYS_ID_LEN + 1);
264
hassod70f99e2004-02-11 20:26:31 +0000265 THREAD_TIMER_OFF(circuit->t_send_csnp[1]);
jardineb5d44e2003-12-23 08:09:43 +0000266
hassod70f99e2004-02-11 20:26:31 +0000267 THREAD_TIMER_ON(master, circuit->u.bc.t_run_dr[1], isis_run_dr_l2,
268 circuit, 2 * circuit->hello_interval[1]);
269
270 THREAD_TIMER_ON(master, circuit->t_send_psnp[1], send_l2_psnp, circuit,
271 isis_jitter (circuit->psnp_interval[level - 1], PSNP_JITTER));
jardineb5d44e2003-12-23 08:09:43 +0000272 }
273
274 thread_add_event (master, isis_event_dis_status_change, circuit, 0);
275
276 return ISIS_OK;
277}
278
279int
280isis_dr_commence (struct isis_circuit *circuit, int level)
281{
282 u_char old_dr[ISIS_SYS_ID_LEN + 2];
283
284 zlog_info ("isis_dr_commence l%d", level);
285
286 /* Lets keep a pause in DR election */
287 circuit->u.bc.run_dr_elect[level - 1] = 0;
288 if (level == 1)
hassod70f99e2004-02-11 20:26:31 +0000289 THREAD_TIMER_ON(master, circuit->u.bc.t_run_dr[0], isis_run_dr_l1,
290 circuit, 2 * circuit->hello_multiplier[0] *
jardineb5d44e2003-12-23 08:09:43 +0000291 circuit->hello_interval[0]);
292 else
hassod70f99e2004-02-11 20:26:31 +0000293 THREAD_TIMER_ON(master, circuit->u.bc.t_run_dr[1], isis_run_dr_l2,
294 circuit, 2 * circuit->hello_multiplier[1] *
jardineb5d44e2003-12-23 08:09:43 +0000295 circuit->hello_interval[1]);
296 circuit->u.bc.is_dr[level - 1] = 1;
297
298 if (level == 1) {
299 memcpy (old_dr, circuit->u.bc.l1_desig_is, ISIS_SYS_ID_LEN + 1);
300 LSP_FRAGMENT (old_dr) = 0;
301 if (LSP_PSEUDO_ID(old_dr)) {
302 /* there was a dr elected, purge its LSPs from the db */
303 lsp_purge_dr (old_dr, circuit, level);
304 }
305 memcpy (circuit->u.bc.l1_desig_is, isis->sysid, ISIS_SYS_ID_LEN);
306 *(circuit->u.bc.l1_desig_is + ISIS_SYS_ID_LEN) = circuit->circuit_id;
307
308 assert (circuit->circuit_id); /* must be non-zero */
309 /* if (circuit->t_send_l1_psnp)
310 thread_cancel (circuit->t_send_l1_psnp); */
311 lsp_l1_pseudo_generate (circuit);
312
hassod70f99e2004-02-11 20:26:31 +0000313 THREAD_TIMER_OFF(circuit->u.bc.t_run_dr[0]);
314 THREAD_TIMER_ON(master, circuit->u.bc.t_run_dr[0], isis_run_dr_l1,
315 circuit, 2 * circuit->hello_interval[0]);
316
317 THREAD_TIMER_ON(master, circuit->t_send_csnp[0], send_l1_csnp, circuit,
318 isis_jitter(circuit->csnp_interval[level-1], CSNP_JITTER));
319
jardineb5d44e2003-12-23 08:09:43 +0000320 } 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_dr (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) = circuit->circuit_id;
329
330 assert (circuit->circuit_id); /* must be non-zero */
331 /* if (circuit->t_send_l1_psnp)
332 thread_cancel (circuit->t_send_l1_psnp); */
333 lsp_l2_pseudo_generate (circuit);
334
hassod70f99e2004-02-11 20:26:31 +0000335 THREAD_TIMER_OFF(circuit->u.bc.t_run_dr[1]);
336 THREAD_TIMER_ON(master, circuit->u.bc.t_run_dr[1], isis_run_dr_l2,
337 circuit, 2 * circuit->hello_interval[1]);
jardineb5d44e2003-12-23 08:09:43 +0000338
hassod70f99e2004-02-11 20:26:31 +0000339 THREAD_TIMER_ON(master, circuit->t_send_csnp[1], send_l2_csnp, circuit,
340 isis_jitter (circuit->csnp_interval[level-1], CSNP_JITTER));
jardineb5d44e2003-12-23 08:09:43 +0000341 }
342
343 thread_add_event (master, isis_event_dis_status_change, circuit, 0);
344
345 return ISIS_OK;
346}
347