]>
Commit | Line | Data |
---|---|---|
acddc0ed | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
eb5d44eb | 2 | /* |
3 | * IS-IS Rout(e)ing protocol - isis_dr.c | |
d62a17ae | 4 | * IS-IS designated router related routines |
eb5d44eb | 5 | * |
6 | * Copyright (C) 2001,2002 Sampo Saaristo | |
d62a17ae | 7 | * Tampere University of Technology |
eb5d44eb | 8 | * Institute of Communications Engineering |
eb5d44eb | 9 | */ |
10 | ||
11 | ||
12 | #include <zebra.h> | |
eb5d44eb | 13 | |
14 | #include "log.h" | |
15 | #include "hash.h" | |
24a58196 | 16 | #include "frrevent.h" |
eb5d44eb | 17 | #include "linklist.h" |
18 | #include "vty.h" | |
19 | #include "stream.h" | |
20 | #include "if.h" | |
21 | ||
eb5d44eb | 22 | #include "isisd/isis_constants.h" |
23 | #include "isisd/isis_common.h" | |
24 | #include "isisd/isis_misc.h" | |
25 | #include "isisd/isis_flags.h" | |
26 | #include "isisd/isis_circuit.h" | |
27 | #include "isisd/isisd.h" | |
28 | #include "isisd/isis_adjacency.h" | |
29 | #include "isisd/isis_constants.h" | |
30 | #include "isisd/isis_pdu.h" | |
eb5d44eb | 31 | #include "isisd/isis_lsp.h" |
32 | #include "isisd/isis_dr.h" | |
33 | #include "isisd/isis_events.h" | |
34 | ||
d62a17ae | 35 | const char *isis_disflag2string(int disflag) |
f390d2c7 | 36 | { |
eb5d44eb | 37 | |
d62a17ae | 38 | switch (disflag) { |
39 | case ISIS_IS_NOT_DIS: | |
40 | return "is not DIS"; | |
41 | case ISIS_IS_DIS: | |
42 | return "is DIS"; | |
43 | case ISIS_WAS_DIS: | |
44 | return "was DIS"; | |
45 | default: | |
46 | return "unknown DIS state"; | |
47 | } | |
48 | return NULL; /* not reached */ | |
eb5d44eb | 49 | } |
50 | ||
e6685141 | 51 | void isis_run_dr(struct event *thread) |
eb5d44eb | 52 | { |
e16d030c | 53 | struct isis_circuit_arg *arg = EVENT_ARG(thread); |
f390d2c7 | 54 | |
7c4f7aab | 55 | assert(arg); |
f390d2c7 | 56 | |
7c4f7aab CF |
57 | struct isis_circuit *circuit = arg->circuit; |
58 | int level = arg->level; | |
eb5d44eb | 59 | |
d62a17ae | 60 | assert(circuit); |
eb5d44eb | 61 | |
7c4f7aab CF |
62 | if (circuit->circ_type != CIRCUIT_T_BROADCAST) { |
63 | zlog_warn("%s: scheduled for non broadcast circuit from %s:%d", | |
60a3efec DL |
64 | __func__, thread->xref->xref.file, |
65 | thread->xref->xref.line); | |
cc9f21da | 66 | return; |
7c4f7aab | 67 | } |
f390d2c7 | 68 | |
7c4f7aab | 69 | if (circuit->u.bc.run_dr_elect[level - 1]) |
1f46f33f | 70 | zlog_warn("%s: run_dr_elect already set for l%d", __func__, |
71 | level); | |
f390d2c7 | 72 | |
7c4f7aab CF |
73 | circuit->u.bc.t_run_dr[level - 1] = NULL; |
74 | circuit->u.bc.run_dr_elect[level - 1] = 1; | |
eb5d44eb | 75 | } |
76 | ||
d62a17ae | 77 | static int isis_check_dr_change(struct isis_adjacency *adj, int level) |
eb5d44eb | 78 | { |
d62a17ae | 79 | int i; |
80 | ||
81 | if (adj->dis_record[level - 1].dis | |
82 | != adj->dis_record[(1 * ISIS_LEVELS) + level - 1].dis) | |
83 | /* was there a DIS state transition ? */ | |
eb5d44eb | 84 | { |
d62a17ae | 85 | adj->dischanges[level - 1]++; |
1ee746d9 | 86 | adj->circuit->desig_changes[level - 1]++; |
d62a17ae | 87 | /* ok rotate the history list through */ |
88 | for (i = DIS_RECORDS - 1; i > 0; i--) { | |
89 | adj->dis_record[(i * ISIS_LEVELS) + level - 1].dis = | |
90 | adj->dis_record[((i - 1) * ISIS_LEVELS) + level | |
91 | - 1] | |
92 | .dis; | |
93 | adj->dis_record[(i * ISIS_LEVELS) + level - 1] | |
94 | .last_dis_change = | |
95 | adj->dis_record[((i - 1) * ISIS_LEVELS) + level | |
96 | - 1] | |
97 | .last_dis_change; | |
98 | } | |
eb5d44eb | 99 | } |
d62a17ae | 100 | return ISIS_OK; |
eb5d44eb | 101 | } |
102 | ||
d62a17ae | 103 | int isis_dr_elect(struct isis_circuit *circuit, int level) |
eb5d44eb | 104 | { |
d62a17ae | 105 | struct list *adjdb; |
106 | struct listnode *node; | |
107 | struct isis_adjacency *adj, *adj_dr = NULL; | |
108 | struct list *list = list_new(); | |
d7c0a89a | 109 | uint8_t own_prio; |
d62a17ae | 110 | int biggest_prio = -1; |
111 | int cmp_res, retval = ISIS_OK; | |
112 | ||
113 | own_prio = circuit->priority[level - 1]; | |
114 | adjdb = circuit->u.bc.adjdb[level - 1]; | |
115 | ||
116 | if (!adjdb) { | |
1f46f33f | 117 | zlog_warn("%s adjdb == NULL", __func__); |
6a154c88 | 118 | list_delete(&list); |
d62a17ae | 119 | return ISIS_WARNING; |
f390d2c7 | 120 | } |
d62a17ae | 121 | isis_adj_build_up_list(adjdb, list); |
122 | ||
123 | /* | |
124 | * Loop the adjacencies and find the one with the biggest priority | |
125 | */ | |
126 | for (ALL_LIST_ELEMENTS_RO(list, node, adj)) { | |
127 | /* clear flag for show output */ | |
128 | adj->dis_record[level - 1].dis = ISIS_IS_NOT_DIS; | |
129 | adj->dis_record[level - 1].last_dis_change = time(NULL); | |
130 | ||
131 | if (adj->prio[level - 1] > biggest_prio) { | |
132 | biggest_prio = adj->prio[level - 1]; | |
133 | adj_dr = adj; | |
134 | } else if (adj->prio[level - 1] == biggest_prio) { | |
135 | /* | |
136 | * Comparison of MACs breaks a tie | |
137 | */ | |
138 | if (adj_dr) { | |
139 | cmp_res = memcmp(adj_dr->snpa, adj->snpa, | |
140 | ETH_ALEN); | |
141 | if (cmp_res < 0) { | |
142 | adj_dr = adj; | |
143 | } | |
144 | if (cmp_res == 0) | |
145 | zlog_warn( | |
1f46f33f | 146 | "%s: multiple adjacencies with same SNPA", |
147 | __func__); | |
d62a17ae | 148 | } else { |
149 | adj_dr = adj; | |
150 | } | |
f390d2c7 | 151 | } |
f390d2c7 | 152 | } |
d62a17ae | 153 | |
154 | if (!adj_dr) { | |
155 | /* | |
156 | * Could not find the DR - means we are alone. Resign if we were | |
157 | * DR. | |
158 | */ | |
159 | if (circuit->u.bc.is_dr[level - 1]) | |
160 | retval = isis_dr_resign(circuit, level); | |
6a154c88 | 161 | list_delete(&list); |
d62a17ae | 162 | return retval; |
163 | } | |
164 | ||
165 | /* | |
166 | * Now we have the DR adjacency, compare it to self | |
167 | */ | |
168 | if (adj_dr->prio[level - 1] < own_prio | |
169 | || (adj_dr->prio[level - 1] == own_prio | |
170 | && memcmp(adj_dr->snpa, circuit->u.bc.snpa, ETH_ALEN) < 0)) { | |
171 | adj_dr->dis_record[level - 1].dis = ISIS_IS_NOT_DIS; | |
172 | adj_dr->dis_record[level - 1].last_dis_change = time(NULL); | |
173 | ||
174 | /* rotate the history log */ | |
175 | for (ALL_LIST_ELEMENTS_RO(list, node, adj)) | |
176 | isis_check_dr_change(adj, level); | |
177 | ||
178 | /* We are the DR, commence DR */ | |
179 | if (circuit->u.bc.is_dr[level - 1] == 0 && listcount(list) > 0) | |
180 | retval = isis_dr_commence(circuit, level); | |
181 | } else { | |
182 | /* ok we have found the DIS - lets mark the adjacency */ | |
183 | /* set flag for show output */ | |
184 | adj_dr->dis_record[level - 1].dis = ISIS_IS_DIS; | |
185 | adj_dr->dis_record[level - 1].last_dis_change = time(NULL); | |
186 | ||
187 | /* now loop through a second time to check if there has been a | |
188 | * DIS change | |
189 | * if yes rotate the history log | |
190 | */ | |
191 | ||
192 | for (ALL_LIST_ELEMENTS_RO(list, node, adj)) | |
193 | isis_check_dr_change(adj, level); | |
194 | ||
195 | /* | |
196 | * We are not DR - if we were -> resign | |
197 | */ | |
198 | if (circuit->u.bc.is_dr[level - 1]) | |
199 | retval = isis_dr_resign(circuit, level); | |
200 | } | |
6a154c88 | 201 | list_delete(&list); |
d62a17ae | 202 | return retval; |
eb5d44eb | 203 | } |
204 | ||
d62a17ae | 205 | int isis_dr_resign(struct isis_circuit *circuit, int level) |
eb5d44eb | 206 | { |
d7c0a89a | 207 | uint8_t id[ISIS_SYS_ID_LEN + 2]; |
f390d2c7 | 208 | |
e6605a89 | 209 | if (IS_DEBUG_EVENTS) |
1f46f33f | 210 | zlog_debug("%s l%d", __func__, level); |
eb5d44eb | 211 | |
d62a17ae | 212 | circuit->u.bc.is_dr[level - 1] = 0; |
213 | circuit->u.bc.run_dr_elect[level - 1] = 0; | |
e16d030c DS |
214 | EVENT_OFF(circuit->u.bc.t_run_dr[level - 1]); |
215 | EVENT_OFF(circuit->u.bc.t_refresh_pseudo_lsp[level - 1]); | |
d62a17ae | 216 | circuit->lsp_regenerate_pending[level - 1] = 0; |
f390d2c7 | 217 | |
99e5d4af | 218 | memcpy(id, circuit->isis->sysid, ISIS_SYS_ID_LEN); |
d62a17ae | 219 | LSP_PSEUDO_ID(id) = circuit->circuit_id; |
220 | LSP_FRAGMENT(id) = 0; | |
221 | lsp_purge_pseudo(id, circuit, level); | |
eb5d44eb | 222 | |
d62a17ae | 223 | if (level == 1) { |
224 | memset(circuit->u.bc.l1_desig_is, 0, ISIS_SYS_ID_LEN + 1); | |
f390d2c7 | 225 | |
907a2395 DS |
226 | event_add_timer(master, send_l1_psnp, circuit, |
227 | isis_jitter(circuit->psnp_interval[level - 1], | |
228 | PSNP_JITTER), | |
229 | &circuit->t_send_psnp[0]); | |
d62a17ae | 230 | } else { |
231 | memset(circuit->u.bc.l2_desig_is, 0, ISIS_SYS_ID_LEN + 1); | |
f390d2c7 | 232 | |
907a2395 DS |
233 | event_add_timer(master, send_l2_psnp, circuit, |
234 | isis_jitter(circuit->psnp_interval[level - 1], | |
235 | PSNP_JITTER), | |
236 | &circuit->t_send_psnp[1]); | |
d62a17ae | 237 | } |
f390d2c7 | 238 | |
e16d030c | 239 | EVENT_OFF(circuit->t_send_csnp[level - 1]); |
7c4f7aab | 240 | |
907a2395 DS |
241 | event_add_timer(master, isis_run_dr, &circuit->level_arg[level - 1], |
242 | 2 * circuit->hello_interval[level - 1], | |
243 | &circuit->u.bc.t_run_dr[level - 1]); | |
7c4f7aab CF |
244 | |
245 | ||
907a2395 | 246 | event_add_event(master, isis_event_dis_status_change, circuit, 0, NULL); |
eb5d44eb | 247 | |
d62a17ae | 248 | return ISIS_OK; |
eb5d44eb | 249 | } |
250 | ||
d62a17ae | 251 | int isis_dr_commence(struct isis_circuit *circuit, int level) |
eb5d44eb | 252 | { |
d7c0a89a | 253 | uint8_t old_dr[ISIS_SYS_ID_LEN + 2]; |
d62a17ae | 254 | |
e740f9c1 | 255 | if (IS_DEBUG_EVENTS) |
1f46f33f | 256 | zlog_debug("%s l%d", __func__, level); |
d62a17ae | 257 | |
258 | /* Lets keep a pause in DR election */ | |
259 | circuit->u.bc.run_dr_elect[level - 1] = 0; | |
d62a17ae | 260 | circuit->u.bc.is_dr[level - 1] = 1; |
261 | ||
262 | if (level == 1) { | |
263 | memcpy(old_dr, circuit->u.bc.l1_desig_is, ISIS_SYS_ID_LEN + 1); | |
264 | LSP_FRAGMENT(old_dr) = 0; | |
265 | if (LSP_PSEUDO_ID(old_dr)) { | |
266 | /* there was a dr elected, purge its LSPs from the db */ | |
267 | lsp_purge_pseudo(old_dr, circuit, level); | |
268 | } | |
99e5d4af | 269 | memcpy(circuit->u.bc.l1_desig_is, circuit->isis->sysid, |
eab88f36 | 270 | ISIS_SYS_ID_LEN); |
d62a17ae | 271 | *(circuit->u.bc.l1_desig_is + ISIS_SYS_ID_LEN) = |
272 | circuit->circuit_id; | |
273 | ||
274 | assert(circuit->circuit_id); /* must be non-zero */ | |
d62a17ae | 275 | lsp_generate_pseudo(circuit, 1); |
276 | ||
907a2395 DS |
277 | event_add_timer(master, send_l1_csnp, circuit, |
278 | isis_jitter(circuit->csnp_interval[level - 1], | |
279 | CSNP_JITTER), | |
280 | &circuit->t_send_csnp[0]); | |
d62a17ae | 281 | |
282 | } else { | |
283 | memcpy(old_dr, circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1); | |
284 | LSP_FRAGMENT(old_dr) = 0; | |
285 | if (LSP_PSEUDO_ID(old_dr)) { | |
286 | /* there was a dr elected, purge its LSPs from the db */ | |
287 | lsp_purge_pseudo(old_dr, circuit, level); | |
288 | } | |
99e5d4af | 289 | memcpy(circuit->u.bc.l2_desig_is, circuit->isis->sysid, |
eab88f36 | 290 | ISIS_SYS_ID_LEN); |
d62a17ae | 291 | *(circuit->u.bc.l2_desig_is + ISIS_SYS_ID_LEN) = |
292 | circuit->circuit_id; | |
293 | ||
294 | assert(circuit->circuit_id); /* must be non-zero */ | |
d62a17ae | 295 | lsp_generate_pseudo(circuit, 2); |
296 | ||
907a2395 DS |
297 | event_add_timer(master, send_l2_csnp, circuit, |
298 | isis_jitter(circuit->csnp_interval[level - 1], | |
299 | CSNP_JITTER), | |
300 | &circuit->t_send_csnp[1]); | |
f390d2c7 | 301 | } |
eb5d44eb | 302 | |
907a2395 DS |
303 | event_add_timer(master, isis_run_dr, &circuit->level_arg[level - 1], |
304 | 2 * circuit->hello_interval[level - 1], | |
305 | &circuit->u.bc.t_run_dr[level - 1]); | |
306 | event_add_event(master, isis_event_dis_status_change, circuit, 0, NULL); | |
f390d2c7 | 307 | |
d62a17ae | 308 | return ISIS_OK; |
eb5d44eb | 309 | } |