]> git.proxmox.com Git - mirror_frr.git/blob - isisd/isis_dr.c
zebra: Fix label manager memory leak (#5680)
[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 int 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->schedfrom, thread->schedfrom_line);
78 return ISIS_WARNING;
79 }
80
81 if (circuit->u.bc.run_dr_elect[level - 1])
82 zlog_warn("isis_run_dr(): run_dr_elect already set for l%d", level);
83
84 circuit->u.bc.t_run_dr[level - 1] = NULL;
85 circuit->u.bc.run_dr_elect[level - 1] = 1;
86
87 return ISIS_OK;
88 }
89
90 static int isis_check_dr_change(struct isis_adjacency *adj, int level)
91 {
92 int i;
93
94 if (adj->dis_record[level - 1].dis
95 != adj->dis_record[(1 * ISIS_LEVELS) + level - 1].dis)
96 /* was there a DIS state transition ? */
97 {
98 adj->dischanges[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 zlog_debug("isis_dr_resign l%d", level);
221
222 circuit->u.bc.is_dr[level - 1] = 0;
223 circuit->u.bc.run_dr_elect[level - 1] = 0;
224 THREAD_TIMER_OFF(circuit->u.bc.t_run_dr[level - 1]);
225 THREAD_TIMER_OFF(circuit->u.bc.t_refresh_pseudo_lsp[level - 1]);
226 circuit->lsp_regenerate_pending[level - 1] = 0;
227
228 memcpy(id, isis->sysid, ISIS_SYS_ID_LEN);
229 LSP_PSEUDO_ID(id) = circuit->circuit_id;
230 LSP_FRAGMENT(id) = 0;
231 lsp_purge_pseudo(id, circuit, level);
232
233 if (level == 1) {
234 memset(circuit->u.bc.l1_desig_is, 0, ISIS_SYS_ID_LEN + 1);
235
236 thread_add_timer(master, send_l1_psnp, circuit,
237 isis_jitter(circuit->psnp_interval[level - 1],
238 PSNP_JITTER),
239 &circuit->t_send_psnp[0]);
240 } else {
241 memset(circuit->u.bc.l2_desig_is, 0, ISIS_SYS_ID_LEN + 1);
242
243 thread_add_timer(master, send_l2_psnp, circuit,
244 isis_jitter(circuit->psnp_interval[level - 1],
245 PSNP_JITTER),
246 &circuit->t_send_psnp[1]);
247 }
248
249 THREAD_TIMER_OFF(circuit->t_send_csnp[level - 1]);
250
251 thread_add_timer(master, isis_run_dr,
252 &circuit->level_arg[level - 1],
253 2 * circuit->hello_interval[level - 1],
254 &circuit->u.bc.t_run_dr[level - 1]);
255
256
257 thread_add_event(master, isis_event_dis_status_change, circuit, 0,
258 NULL);
259
260 return ISIS_OK;
261 }
262
263 int isis_dr_commence(struct isis_circuit *circuit, int level)
264 {
265 uint8_t old_dr[ISIS_SYS_ID_LEN + 2];
266
267 if (isis->debugs & DEBUG_EVENTS)
268 zlog_debug("isis_dr_commence l%d", level);
269
270 /* Lets keep a pause in DR election */
271 circuit->u.bc.run_dr_elect[level - 1] = 0;
272 circuit->u.bc.is_dr[level - 1] = 1;
273
274 if (level == 1) {
275 memcpy(old_dr, circuit->u.bc.l1_desig_is, ISIS_SYS_ID_LEN + 1);
276 LSP_FRAGMENT(old_dr) = 0;
277 if (LSP_PSEUDO_ID(old_dr)) {
278 /* there was a dr elected, purge its LSPs from the db */
279 lsp_purge_pseudo(old_dr, circuit, level);
280 }
281 memcpy(circuit->u.bc.l1_desig_is, isis->sysid, ISIS_SYS_ID_LEN);
282 *(circuit->u.bc.l1_desig_is + ISIS_SYS_ID_LEN) =
283 circuit->circuit_id;
284
285 assert(circuit->circuit_id); /* must be non-zero */
286 /* if (circuit->t_send_l1_psnp)
287 thread_cancel (circuit->t_send_l1_psnp); */
288 lsp_generate_pseudo(circuit, 1);
289
290 thread_add_timer(master, send_l1_csnp, circuit,
291 isis_jitter(circuit->csnp_interval[level - 1],
292 CSNP_JITTER),
293 &circuit->t_send_csnp[0]);
294
295 } else {
296 memcpy(old_dr, circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1);
297 LSP_FRAGMENT(old_dr) = 0;
298 if (LSP_PSEUDO_ID(old_dr)) {
299 /* there was a dr elected, purge its LSPs from the db */
300 lsp_purge_pseudo(old_dr, circuit, level);
301 }
302 memcpy(circuit->u.bc.l2_desig_is, isis->sysid, ISIS_SYS_ID_LEN);
303 *(circuit->u.bc.l2_desig_is + ISIS_SYS_ID_LEN) =
304 circuit->circuit_id;
305
306 assert(circuit->circuit_id); /* must be non-zero */
307 /* if (circuit->t_send_l1_psnp)
308 thread_cancel (circuit->t_send_l1_psnp); */
309 lsp_generate_pseudo(circuit, 2);
310
311 thread_add_timer(master, send_l2_csnp, circuit,
312 isis_jitter(circuit->csnp_interval[level - 1],
313 CSNP_JITTER),
314 &circuit->t_send_csnp[1]);
315 }
316
317 thread_add_timer(master, isis_run_dr,
318 &circuit->level_arg[level - 1],
319 2 * circuit->hello_interval[level - 1],
320 &circuit->u.bc.t_run_dr[level - 1]);
321 thread_add_event(master, isis_event_dis_status_change, circuit, 0,
322 NULL);
323
324 return ISIS_OK;
325 }