]> git.proxmox.com Git - mirror_frr.git/blame - isisd/isis_dr.c
Merge pull request #12837 from donaldsharp/unlikely_routemap
[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"
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 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
e6685141 51void 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 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;
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 251int 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}