]> git.proxmox.com Git - mirror_frr.git/blob - isisd/isis_dr.c
bgpd: fix dereference of null pointer in bgp_attr_aspath
[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 void 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->xref->xref.file,
78 thread->xref->xref.line);
79 return;
80 }
81
82 if (circuit->u.bc.run_dr_elect[level - 1])
83 zlog_warn("%s: run_dr_elect already set for l%d", __func__,
84 level);
85
86 circuit->u.bc.t_run_dr[level - 1] = NULL;
87 circuit->u.bc.run_dr_elect[level - 1] = 1;
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 adj->circuit->desig_changes[level - 1]++;
100 /* ok rotate the history list through */
101 for (i = DIS_RECORDS - 1; i > 0; i--) {
102 adj->dis_record[(i * ISIS_LEVELS) + level - 1].dis =
103 adj->dis_record[((i - 1) * ISIS_LEVELS) + level
104 - 1]
105 .dis;
106 adj->dis_record[(i * ISIS_LEVELS) + level - 1]
107 .last_dis_change =
108 adj->dis_record[((i - 1) * ISIS_LEVELS) + level
109 - 1]
110 .last_dis_change;
111 }
112 }
113 return ISIS_OK;
114 }
115
116 int isis_dr_elect(struct isis_circuit *circuit, int level)
117 {
118 struct list *adjdb;
119 struct listnode *node;
120 struct isis_adjacency *adj, *adj_dr = NULL;
121 struct list *list = list_new();
122 uint8_t own_prio;
123 int biggest_prio = -1;
124 int cmp_res, retval = ISIS_OK;
125
126 own_prio = circuit->priority[level - 1];
127 adjdb = circuit->u.bc.adjdb[level - 1];
128
129 if (!adjdb) {
130 zlog_warn("%s adjdb == NULL", __func__);
131 list_delete(&list);
132 return ISIS_WARNING;
133 }
134 isis_adj_build_up_list(adjdb, list);
135
136 /*
137 * Loop the adjacencies and find the one with the biggest priority
138 */
139 for (ALL_LIST_ELEMENTS_RO(list, node, adj)) {
140 /* clear flag for show output */
141 adj->dis_record[level - 1].dis = ISIS_IS_NOT_DIS;
142 adj->dis_record[level - 1].last_dis_change = time(NULL);
143
144 if (adj->prio[level - 1] > biggest_prio) {
145 biggest_prio = adj->prio[level - 1];
146 adj_dr = adj;
147 } else if (adj->prio[level - 1] == biggest_prio) {
148 /*
149 * Comparison of MACs breaks a tie
150 */
151 if (adj_dr) {
152 cmp_res = memcmp(adj_dr->snpa, adj->snpa,
153 ETH_ALEN);
154 if (cmp_res < 0) {
155 adj_dr = adj;
156 }
157 if (cmp_res == 0)
158 zlog_warn(
159 "%s: multiple adjacencies with same SNPA",
160 __func__);
161 } else {
162 adj_dr = adj;
163 }
164 }
165 }
166
167 if (!adj_dr) {
168 /*
169 * Could not find the DR - means we are alone. Resign if we were
170 * DR.
171 */
172 if (circuit->u.bc.is_dr[level - 1])
173 retval = isis_dr_resign(circuit, level);
174 list_delete(&list);
175 return retval;
176 }
177
178 /*
179 * Now we have the DR adjacency, compare it to self
180 */
181 if (adj_dr->prio[level - 1] < own_prio
182 || (adj_dr->prio[level - 1] == own_prio
183 && memcmp(adj_dr->snpa, circuit->u.bc.snpa, ETH_ALEN) < 0)) {
184 adj_dr->dis_record[level - 1].dis = ISIS_IS_NOT_DIS;
185 adj_dr->dis_record[level - 1].last_dis_change = time(NULL);
186
187 /* rotate the history log */
188 for (ALL_LIST_ELEMENTS_RO(list, node, adj))
189 isis_check_dr_change(adj, level);
190
191 /* We are the DR, commence DR */
192 if (circuit->u.bc.is_dr[level - 1] == 0 && listcount(list) > 0)
193 retval = isis_dr_commence(circuit, level);
194 } else {
195 /* ok we have found the DIS - lets mark the adjacency */
196 /* set flag for show output */
197 adj_dr->dis_record[level - 1].dis = ISIS_IS_DIS;
198 adj_dr->dis_record[level - 1].last_dis_change = time(NULL);
199
200 /* now loop through a second time to check if there has been a
201 * DIS change
202 * if yes rotate the history log
203 */
204
205 for (ALL_LIST_ELEMENTS_RO(list, node, adj))
206 isis_check_dr_change(adj, level);
207
208 /*
209 * We are not DR - if we were -> resign
210 */
211 if (circuit->u.bc.is_dr[level - 1])
212 retval = isis_dr_resign(circuit, level);
213 }
214 list_delete(&list);
215 return retval;
216 }
217
218 int isis_dr_resign(struct isis_circuit *circuit, int level)
219 {
220 uint8_t id[ISIS_SYS_ID_LEN + 2];
221
222 if (IS_DEBUG_EVENTS)
223 zlog_debug("%s l%d", __func__, level);
224
225 circuit->u.bc.is_dr[level - 1] = 0;
226 circuit->u.bc.run_dr_elect[level - 1] = 0;
227 THREAD_OFF(circuit->u.bc.t_run_dr[level - 1]);
228 THREAD_OFF(circuit->u.bc.t_refresh_pseudo_lsp[level - 1]);
229 circuit->lsp_regenerate_pending[level - 1] = 0;
230
231 memcpy(id, circuit->isis->sysid, ISIS_SYS_ID_LEN);
232 LSP_PSEUDO_ID(id) = circuit->circuit_id;
233 LSP_FRAGMENT(id) = 0;
234 lsp_purge_pseudo(id, circuit, level);
235
236 if (level == 1) {
237 memset(circuit->u.bc.l1_desig_is, 0, ISIS_SYS_ID_LEN + 1);
238
239 thread_add_timer(master, send_l1_psnp, circuit,
240 isis_jitter(circuit->psnp_interval[level - 1],
241 PSNP_JITTER),
242 &circuit->t_send_psnp[0]);
243 } else {
244 memset(circuit->u.bc.l2_desig_is, 0, ISIS_SYS_ID_LEN + 1);
245
246 thread_add_timer(master, send_l2_psnp, circuit,
247 isis_jitter(circuit->psnp_interval[level - 1],
248 PSNP_JITTER),
249 &circuit->t_send_psnp[1]);
250 }
251
252 THREAD_OFF(circuit->t_send_csnp[level - 1]);
253
254 thread_add_timer(master, isis_run_dr,
255 &circuit->level_arg[level - 1],
256 2 * circuit->hello_interval[level - 1],
257 &circuit->u.bc.t_run_dr[level - 1]);
258
259
260 thread_add_event(master, isis_event_dis_status_change, circuit, 0,
261 NULL);
262
263 return ISIS_OK;
264 }
265
266 int isis_dr_commence(struct isis_circuit *circuit, int level)
267 {
268 uint8_t old_dr[ISIS_SYS_ID_LEN + 2];
269
270 if (IS_DEBUG_EVENTS)
271 zlog_debug("%s l%d", __func__, level);
272
273 /* Lets keep a pause in DR election */
274 circuit->u.bc.run_dr_elect[level - 1] = 0;
275 circuit->u.bc.is_dr[level - 1] = 1;
276
277 if (level == 1) {
278 memcpy(old_dr, circuit->u.bc.l1_desig_is, ISIS_SYS_ID_LEN + 1);
279 LSP_FRAGMENT(old_dr) = 0;
280 if (LSP_PSEUDO_ID(old_dr)) {
281 /* there was a dr elected, purge its LSPs from the db */
282 lsp_purge_pseudo(old_dr, circuit, level);
283 }
284 memcpy(circuit->u.bc.l1_desig_is, circuit->isis->sysid,
285 ISIS_SYS_ID_LEN);
286 *(circuit->u.bc.l1_desig_is + ISIS_SYS_ID_LEN) =
287 circuit->circuit_id;
288
289 assert(circuit->circuit_id); /* must be non-zero */
290 lsp_generate_pseudo(circuit, 1);
291
292 thread_add_timer(master, send_l1_csnp, circuit,
293 isis_jitter(circuit->csnp_interval[level - 1],
294 CSNP_JITTER),
295 &circuit->t_send_csnp[0]);
296
297 } else {
298 memcpy(old_dr, circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1);
299 LSP_FRAGMENT(old_dr) = 0;
300 if (LSP_PSEUDO_ID(old_dr)) {
301 /* there was a dr elected, purge its LSPs from the db */
302 lsp_purge_pseudo(old_dr, circuit, level);
303 }
304 memcpy(circuit->u.bc.l2_desig_is, circuit->isis->sysid,
305 ISIS_SYS_ID_LEN);
306 *(circuit->u.bc.l2_desig_is + ISIS_SYS_ID_LEN) =
307 circuit->circuit_id;
308
309 assert(circuit->circuit_id); /* must be non-zero */
310 lsp_generate_pseudo(circuit, 2);
311
312 thread_add_timer(master, send_l2_csnp, circuit,
313 isis_jitter(circuit->csnp_interval[level - 1],
314 CSNP_JITTER),
315 &circuit->t_send_csnp[1]);
316 }
317
318 thread_add_timer(master, isis_run_dr,
319 &circuit->level_arg[level - 1],
320 2 * circuit->hello_interval[level - 1],
321 &circuit->u.bc.t_run_dr[level - 1]);
322 thread_add_event(master, isis_event_dis_status_change, circuit, 0,
323 NULL);
324
325 return ISIS_OK;
326 }