]> git.proxmox.com Git - mirror_frr.git/blame - isisd/isis_dr.c
Merge pull request #12798 from donaldsharp/rib_match_multicast
[mirror_frr.git] / isisd / isis_dr.c
CommitLineData
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"
16#include "thread.h"
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 35const 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
cc9f21da 51void isis_run_dr(struct thread *thread)
eb5d44eb 52{
7c4f7aab 53 struct isis_circuit_arg *arg = THREAD_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 77static 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 103int 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 205int 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;
fa935aa7
DS
214 THREAD_OFF(circuit->u.bc.t_run_dr[level - 1]);
215 THREAD_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
d62a17ae 226 thread_add_timer(master, send_l1_psnp, circuit,
227 isis_jitter(circuit->psnp_interval[level - 1],
228 PSNP_JITTER),
229 &circuit->t_send_psnp[0]);
230 } else {
231 memset(circuit->u.bc.l2_desig_is, 0, ISIS_SYS_ID_LEN + 1);
f390d2c7 232
d62a17ae 233 thread_add_timer(master, send_l2_psnp, circuit,
234 isis_jitter(circuit->psnp_interval[level - 1],
235 PSNP_JITTER),
236 &circuit->t_send_psnp[1]);
237 }
f390d2c7 238
fa935aa7 239 THREAD_OFF(circuit->t_send_csnp[level - 1]);
7c4f7aab
CF
240
241 thread_add_timer(master, isis_run_dr,
242 &circuit->level_arg[level - 1],
243 2 * circuit->hello_interval[level - 1],
244 &circuit->u.bc.t_run_dr[level - 1]);
245
246
d62a17ae 247 thread_add_event(master, isis_event_dis_status_change, circuit, 0,
248 NULL);
eb5d44eb 249
d62a17ae 250 return ISIS_OK;
eb5d44eb 251}
252
d62a17ae 253int isis_dr_commence(struct isis_circuit *circuit, int level)
eb5d44eb 254{
d7c0a89a 255 uint8_t old_dr[ISIS_SYS_ID_LEN + 2];
d62a17ae 256
e740f9c1 257 if (IS_DEBUG_EVENTS)
1f46f33f 258 zlog_debug("%s l%d", __func__, level);
d62a17ae 259
260 /* Lets keep a pause in DR election */
261 circuit->u.bc.run_dr_elect[level - 1] = 0;
d62a17ae 262 circuit->u.bc.is_dr[level - 1] = 1;
263
264 if (level == 1) {
265 memcpy(old_dr, circuit->u.bc.l1_desig_is, ISIS_SYS_ID_LEN + 1);
266 LSP_FRAGMENT(old_dr) = 0;
267 if (LSP_PSEUDO_ID(old_dr)) {
268 /* there was a dr elected, purge its LSPs from the db */
269 lsp_purge_pseudo(old_dr, circuit, level);
270 }
99e5d4af 271 memcpy(circuit->u.bc.l1_desig_is, circuit->isis->sysid,
eab88f36 272 ISIS_SYS_ID_LEN);
d62a17ae 273 *(circuit->u.bc.l1_desig_is + ISIS_SYS_ID_LEN) =
274 circuit->circuit_id;
275
276 assert(circuit->circuit_id); /* must be non-zero */
d62a17ae 277 lsp_generate_pseudo(circuit, 1);
278
d62a17ae 279 thread_add_timer(master, send_l1_csnp, circuit,
280 isis_jitter(circuit->csnp_interval[level - 1],
281 CSNP_JITTER),
282 &circuit->t_send_csnp[0]);
283
284 } else {
285 memcpy(old_dr, circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1);
286 LSP_FRAGMENT(old_dr) = 0;
287 if (LSP_PSEUDO_ID(old_dr)) {
288 /* there was a dr elected, purge its LSPs from the db */
289 lsp_purge_pseudo(old_dr, circuit, level);
290 }
99e5d4af 291 memcpy(circuit->u.bc.l2_desig_is, circuit->isis->sysid,
eab88f36 292 ISIS_SYS_ID_LEN);
d62a17ae 293 *(circuit->u.bc.l2_desig_is + ISIS_SYS_ID_LEN) =
294 circuit->circuit_id;
295
296 assert(circuit->circuit_id); /* must be non-zero */
d62a17ae 297 lsp_generate_pseudo(circuit, 2);
298
d62a17ae 299 thread_add_timer(master, send_l2_csnp, circuit,
300 isis_jitter(circuit->csnp_interval[level - 1],
301 CSNP_JITTER),
302 &circuit->t_send_csnp[1]);
f390d2c7 303 }
eb5d44eb 304
0a5e562a
CF
305 thread_add_timer(master, isis_run_dr,
306 &circuit->level_arg[level - 1],
307 2 * circuit->hello_interval[level - 1],
308 &circuit->u.bc.t_run_dr[level - 1]);
d62a17ae 309 thread_add_event(master, isis_event_dis_status_change, circuit, 0,
310 NULL);
f390d2c7 311
d62a17ae 312 return ISIS_OK;
eb5d44eb 313}