]> git.proxmox.com Git - mirror_frr.git/blame - isisd/isis_dr.c
debian: add pkg-config to build-depends
[mirror_frr.git] / isisd / isis_dr.c
CommitLineData
eb5d44eb 1/*
2 * IS-IS Rout(e)ing protocol - isis_dr.c
ac4d0be5 3 * IS-IS designated router related routines
eb5d44eb 4 *
5 * Copyright (C) 2001,2002 Sampo Saaristo
ac4d0be5 6 * Tampere University of Technology
eb5d44eb 7 * Institute of Communications Engineering
8 *
ac4d0be5 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)
eb5d44eb 12 * any later version.
13 *
ac4d0be5 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
eb5d44eb 17 * more details.
18
ac4d0be5 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.,
eb5d44eb 21 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 */
23
24
25#include <zebra.h>
eb5d44eb 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_tlv.h"
46#include "isisd/isis_lsp.h"
47#include "isisd/isis_dr.h"
48#include "isisd/isis_events.h"
49
ac4d0be5 50const char *isis_disflag2string(int disflag)
f390d2c7 51{
eb5d44eb 52
ac4d0be5 53 switch (disflag) {
54 case ISIS_IS_NOT_DIS:
55 return "is not DIS";
56 case ISIS_IS_DIS:
57 return "is DIS";
58 case ISIS_WAS_DIS:
59 return "was DIS";
60 default:
61 return "unknown DIS state";
62 }
63 return NULL; /* not reached */
eb5d44eb 64}
65
ac4d0be5 66int isis_run_dr_l1(struct thread *thread)
eb5d44eb 67{
ac4d0be5 68 struct isis_circuit *circuit;
f390d2c7 69
ac4d0be5 70 circuit = THREAD_ARG(thread);
71 assert(circuit);
eb5d44eb 72
ac4d0be5 73 if (circuit->u.bc.run_dr_elect[0])
74 zlog_warn("isis_run_dr(): run_dr_elect already set for l1");
f390d2c7 75
ac4d0be5 76 circuit->u.bc.t_run_dr[0] = NULL;
77 circuit->u.bc.run_dr_elect[0] = 1;
f390d2c7 78
ac4d0be5 79 return ISIS_OK;
eb5d44eb 80}
81
ac4d0be5 82int isis_run_dr_l2(struct thread *thread)
eb5d44eb 83{
ac4d0be5 84 struct isis_circuit *circuit;
f390d2c7 85
ac4d0be5 86 circuit = THREAD_ARG(thread);
87 assert(circuit);
eb5d44eb 88
ac4d0be5 89 if (circuit->u.bc.run_dr_elect[1])
90 zlog_warn("isis_run_dr(): run_dr_elect already set for l2");
f390d2c7 91
92
ac4d0be5 93 circuit->u.bc.t_run_dr[1] = NULL;
94 circuit->u.bc.run_dr_elect[1] = 1;
f390d2c7 95
ac4d0be5 96 return ISIS_OK;
eb5d44eb 97}
98
ac4d0be5 99static int isis_check_dr_change(struct isis_adjacency *adj, int level)
eb5d44eb 100{
ac4d0be5 101 int i;
102
103 if (adj->dis_record[level - 1].dis
104 != adj->dis_record[(1 * ISIS_LEVELS) + level - 1].dis)
105 /* was there a DIS state transition ? */
eb5d44eb 106 {
ac4d0be5 107 adj->dischanges[level - 1]++;
108 /* ok rotate the history list through */
109 for (i = DIS_RECORDS - 1; i > 0; i--) {
110 adj->dis_record[(i * ISIS_LEVELS) + level - 1].dis =
111 adj->dis_record[((i - 1) * ISIS_LEVELS) + level
112 - 1]
113 .dis;
114 adj->dis_record[(i * ISIS_LEVELS) + level - 1]
115 .last_dis_change =
116 adj->dis_record[((i - 1) * ISIS_LEVELS) + level
117 - 1]
118 .last_dis_change;
119 }
eb5d44eb 120 }
ac4d0be5 121 return ISIS_OK;
eb5d44eb 122}
123
ac4d0be5 124int isis_dr_elect(struct isis_circuit *circuit, int level)
eb5d44eb 125{
ac4d0be5 126 struct list *adjdb;
127 struct listnode *node;
128 struct isis_adjacency *adj, *adj_dr = NULL;
129 struct list *list = list_new();
130 u_char own_prio;
131 int biggest_prio = -1;
132 int cmp_res, retval = ISIS_OK;
133
134 own_prio = circuit->priority[level - 1];
135 adjdb = circuit->u.bc.adjdb[level - 1];
136
137 if (!adjdb) {
138 zlog_warn("isis_dr_elect() adjdb == NULL");
139 list_delete(list);
140 return ISIS_WARNING;
f390d2c7 141 }
ac4d0be5 142 isis_adj_build_up_list(adjdb, list);
143
144 /*
145 * Loop the adjacencies and find the one with the biggest priority
146 */
147 for (ALL_LIST_ELEMENTS_RO(list, node, adj)) {
148 /* clear flag for show output */
149 adj->dis_record[level - 1].dis = ISIS_IS_NOT_DIS;
150 adj->dis_record[level - 1].last_dis_change = time(NULL);
151
152 if (adj->prio[level - 1] > biggest_prio) {
153 biggest_prio = adj->prio[level - 1];
154 adj_dr = adj;
155 } else if (adj->prio[level - 1] == biggest_prio) {
156 /*
157 * Comparison of MACs breaks a tie
158 */
159 if (adj_dr) {
160 cmp_res = memcmp(adj_dr->snpa, adj->snpa,
161 ETH_ALEN);
162 if (cmp_res < 0) {
163 adj_dr = adj;
164 }
165 if (cmp_res == 0)
166 zlog_warn(
167 "isis_dr_elect(): multiple adjacencies with same SNPA");
168 } else {
169 adj_dr = adj;
170 }
f390d2c7 171 }
f390d2c7 172 }
ac4d0be5 173
174 if (!adj_dr) {
175 /*
176 * Could not find the DR - means we are alone. Resign if we were
177 * DR.
178 */
179 if (circuit->u.bc.is_dr[level - 1])
180 retval = isis_dr_resign(circuit, level);
181 list_delete(list);
182 return retval;
183 }
184
185 /*
186 * Now we have the DR adjacency, compare it to self
187 */
188 if (adj_dr->prio[level - 1] < own_prio
189 || (adj_dr->prio[level - 1] == own_prio
190 && memcmp(adj_dr->snpa, circuit->u.bc.snpa, ETH_ALEN) < 0)) {
191 adj_dr->dis_record[level - 1].dis = ISIS_IS_NOT_DIS;
192 adj_dr->dis_record[level - 1].last_dis_change = time(NULL);
193
194 /* rotate the history log */
195 for (ALL_LIST_ELEMENTS_RO(list, node, adj))
196 isis_check_dr_change(adj, level);
197
198 /* We are the DR, commence DR */
199 if (circuit->u.bc.is_dr[level - 1] == 0 && listcount(list) > 0)
200 retval = isis_dr_commence(circuit, level);
201 } else {
202 /* ok we have found the DIS - lets mark the adjacency */
203 /* set flag for show output */
204 adj_dr->dis_record[level - 1].dis = ISIS_IS_DIS;
205 adj_dr->dis_record[level - 1].last_dis_change = time(NULL);
206
207 /* now loop through a second time to check if there has been a
208 * DIS change
209 * if yes rotate the history log
210 */
211
212 for (ALL_LIST_ELEMENTS_RO(list, node, adj))
213 isis_check_dr_change(adj, level);
214
215 /*
216 * We are not DR - if we were -> resign
217 */
218 if (circuit->u.bc.is_dr[level - 1])
219 retval = isis_dr_resign(circuit, level);
220 }
221 list_delete(list);
222 return retval;
eb5d44eb 223}
224
ac4d0be5 225int isis_dr_resign(struct isis_circuit *circuit, int level)
eb5d44eb 226{
ac4d0be5 227 u_char id[ISIS_SYS_ID_LEN + 2];
f390d2c7 228
ac4d0be5 229 zlog_debug("isis_dr_resign l%d", level);
eb5d44eb 230
ac4d0be5 231 circuit->u.bc.is_dr[level - 1] = 0;
232 circuit->u.bc.run_dr_elect[level - 1] = 0;
233 THREAD_TIMER_OFF(circuit->u.bc.t_run_dr[level - 1]);
234 THREAD_TIMER_OFF(circuit->u.bc.t_refresh_pseudo_lsp[level - 1]);
235 circuit->lsp_regenerate_pending[level - 1] = 0;
f390d2c7 236
ac4d0be5 237 memcpy(id, isis->sysid, ISIS_SYS_ID_LEN);
238 LSP_PSEUDO_ID(id) = circuit->circuit_id;
239 LSP_FRAGMENT(id) = 0;
240 lsp_purge_pseudo(id, circuit, level);
eb5d44eb 241
ac4d0be5 242 if (level == 1) {
243 memset(circuit->u.bc.l1_desig_is, 0, ISIS_SYS_ID_LEN + 1);
f390d2c7 244
ac4d0be5 245 THREAD_TIMER_OFF(circuit->t_send_csnp[0]);
f390d2c7 246
ac4d0be5 247 THREAD_TIMER_ON(master, circuit->u.bc.t_run_dr[0],
248 isis_run_dr_l1, circuit,
249 2 * circuit->hello_interval[0]);
f390d2c7 250
ac4d0be5 251 THREAD_TIMER_ON(master, circuit->t_send_psnp[0], send_l1_psnp,
252 circuit,
253 isis_jitter(circuit->psnp_interval[level - 1],
254 PSNP_JITTER));
255 } else {
256 memset(circuit->u.bc.l2_desig_is, 0, ISIS_SYS_ID_LEN + 1);
f390d2c7 257
ac4d0be5 258 THREAD_TIMER_OFF(circuit->t_send_csnp[1]);
f390d2c7 259
ac4d0be5 260 THREAD_TIMER_ON(master, circuit->u.bc.t_run_dr[1],
261 isis_run_dr_l2, circuit,
262 2 * circuit->hello_interval[1]);
f390d2c7 263
ac4d0be5 264 THREAD_TIMER_ON(master, circuit->t_send_psnp[1], send_l2_psnp,
265 circuit,
266 isis_jitter(circuit->psnp_interval[level - 1],
267 PSNP_JITTER));
268 }
f390d2c7 269
ac4d0be5 270 thread_add_event(master, isis_event_dis_status_change, circuit, 0);
eb5d44eb 271
ac4d0be5 272 return ISIS_OK;
eb5d44eb 273}
274
ac4d0be5 275int isis_dr_commence(struct isis_circuit *circuit, int level)
eb5d44eb 276{
ac4d0be5 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_TIMER_ON(master, circuit->u.bc.t_run_dr[0],
286 isis_run_dr_l1, circuit,
287 2 * circuit->hello_interval[0]);
288 else
289 THREAD_TIMER_ON(master, circuit->u.bc.t_run_dr[1],
290 isis_run_dr_l2, circuit,
291 2 * circuit->hello_interval[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_TIMER_ON(master, circuit->u.bc.t_run_dr[0],
312 isis_run_dr_l1, circuit,
313 2 * circuit->hello_interval[0]);
314
315 THREAD_TIMER_ON(master, circuit->t_send_csnp[0], send_l1_csnp,
316 circuit,
317 isis_jitter(circuit->csnp_interval[level - 1],
318 CSNP_JITTER));
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_TIMER_ON(master, circuit->u.bc.t_run_dr[1],
338 isis_run_dr_l2, circuit,
339 2 * circuit->hello_interval[1]);
340
341 THREAD_TIMER_ON(master, circuit->t_send_csnp[1], send_l2_csnp,
342 circuit,
343 isis_jitter(circuit->csnp_interval[level - 1],
344 CSNP_JITTER));
f390d2c7 345 }
eb5d44eb 346
ac4d0be5 347 thread_add_event(master, isis_event_dis_status_change, circuit, 0);
f390d2c7 348
ac4d0be5 349 return ISIS_OK;
eb5d44eb 350}