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; if not, write to the Free Software Foundation, Inc., 21 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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/dict.h" 36#include "isisd/isis_constants.h" 37#include "isisd/isis_common.h" 38#include "isisd/isis_misc.h" 39#include "isisd/isis_flags.h" 40#include "isisd/isis_circuit.h" 41#include "isisd/isisd.h" 42#include "isisd/isis_adjacency.h" 43#include "isisd/isis_constants.h" 44#include "isisd/isis_pdu.h" 45#include "isisd/isis_tlv.h" 46#include "isisd/isis_lsp.h" 47#include "isisd/isis_dr.h" 48#include "isisd/isis_events.h" 49 50const char * 51isis_disflag2string (int disflag) 52{ 53 54 switch (disflag) 55 { 56 case ISIS_IS_NOT_DIS: 57 return "is not DIS"; 58 case ISIS_IS_DIS: 59 return "is DIS"; 60 case ISIS_WAS_DIS: 61 return "was DIS"; 62 default: 63 return "unknown DIS state"; 64 } 65 return NULL; /* not reached */ 66} 67 68int 69isis_run_dr_l1 (struct thread *thread) 70{ 71 struct isis_circuit *circuit; 72 73 circuit = THREAD_ARG (thread); 74 assert (circuit); 75 76 if (circuit->u.bc.run_dr_elect[0]) 77 zlog_warn ("isis_run_dr(): run_dr_elect already set for l1"); 78 79 circuit->u.bc.t_run_dr[0] = NULL; 80 circuit->u.bc.run_dr_elect[0] = 1; 81 82 return ISIS_OK; 83} 84 85int 86isis_run_dr_l2 (struct thread *thread) 87{ 88 struct isis_circuit *circuit; 89 90 circuit = THREAD_ARG (thread); 91 assert (circuit); 92 93 if (circuit->u.bc.run_dr_elect[1]) 94 zlog_warn ("isis_run_dr(): run_dr_elect already set for l2"); 95 96 97 circuit->u.bc.t_run_dr[1] = NULL; 98 circuit->u.bc.run_dr_elect[1] = 1; 99 100 return ISIS_OK; 101} 102 103static int 104isis_check_dr_change (struct isis_adjacency *adj, int level) 105{ 106 int i; 107 108 if (adj->dis_record[level - 1].dis != 109 adj->dis_record[(1 * ISIS_LEVELS) + level - 1].dis) 110 /* was there a DIS state transition ? */ 111 { 112 adj->dischanges[level - 1]++; 113 /* ok rotate the history list through */ 114 for (i = DIS_RECORDS - 1; i > 0; i--) 115 { 116 adj->dis_record[(i * ISIS_LEVELS) + level - 1].dis = 117 adj->dis_record[((i - 1) * ISIS_LEVELS) + level - 1].dis; 118 adj->dis_record[(i * ISIS_LEVELS) + level - 1].last_dis_change = 119 adj->dis_record[((i - 1) * ISIS_LEVELS) + level - 120 1].last_dis_change; 121 } 122 } 123 return ISIS_OK; 124} 125 126int 127isis_dr_elect (struct isis_circuit *circuit, int level) 128{ 129 struct list *adjdb; 130 struct listnode *node; 131 struct isis_adjacency *adj, *adj_dr = NULL; 132 struct list *list = list_new (); 133 u_char own_prio; 134 int biggest_prio = -1; 135 int cmp_res, retval = ISIS_OK; 136 137 own_prio = circuit->priority[level - 1]; 138 adjdb = circuit->u.bc.adjdb[level - 1]; 139 140 if (!adjdb) 141 { 142 zlog_warn ("isis_dr_elect() adjdb == NULL"); 143 list_delete (list); 144 return ISIS_WARNING; 145 } 146 isis_adj_build_up_list (adjdb, list); 147 148 /* 149 * Loop the adjacencies and find the one with the biggest priority 150 */ 151 for (ALL_LIST_ELEMENTS_RO (list, node, adj)) 152 { 153 /* clear flag for show output */ 154 adj->dis_record[level - 1].dis = ISIS_IS_NOT_DIS; 155 adj->dis_record[level - 1].last_dis_change = time (NULL); 156 157 if (adj->prio[level - 1] > biggest_prio) 158 { 159 biggest_prio = adj->prio[level - 1]; 160 adj_dr = adj; 161 } 162 else if (adj->prio[level - 1] == biggest_prio) 163 { 164 /* 165 * Comparison of MACs breaks a tie 166 */ 167 if (adj_dr) 168 { 169 cmp_res = memcmp (adj_dr->snpa, adj->snpa, ETH_ALEN); 170 if (cmp_res < 0) 171 { 172 adj_dr = adj; 173 } 174 if (cmp_res == 0) 175 zlog_warn 176 ("isis_dr_elect(): multiple adjacencies with same SNPA"); 177 } 178 else 179 { 180 adj_dr = adj; 181 } 182 } 183 } 184 185 if (!adj_dr) 186 { 187 /* 188 * Could not find the DR - means we are alone. Resign if we were DR. 189 */ 190 if (circuit->u.bc.is_dr[level - 1]) 191 retval = isis_dr_resign (circuit, level); 192 list_delete (list); 193 return retval; 194 } 195 196 /* 197 * Now we have the DR adjacency, compare it to self 198 */ 199 if (adj_dr->prio[level - 1] < own_prio || 200 (adj_dr->prio[level - 1] == own_prio && 201 memcmp (adj_dr->snpa, circuit->u.bc.snpa, ETH_ALEN) < 0)) 202 { 203 adj_dr->dis_record[level - 1].dis = ISIS_IS_NOT_DIS; 204 adj_dr->dis_record[level - 1].last_dis_change = time (NULL); 205 206 /* rotate the history log */ 207 for (ALL_LIST_ELEMENTS_RO (list, node, adj)) 208 isis_check_dr_change (adj, level); 209 210 /* We are the DR, commence DR */ 211 if (circuit->u.bc.is_dr[level - 1] == 0 && listcount (list) > 0) 212 retval = isis_dr_commence (circuit, level); 213 } 214 else 215 { 216 /* ok we have found the DIS - lets mark the adjacency */ 217 /* set flag for show output */ 218 adj_dr->dis_record[level - 1].dis = ISIS_IS_DIS; 219 adj_dr->dis_record[level - 1].last_dis_change = time (NULL); 220 221 /* now loop through a second time to check if there has been a DIS change 222 * if yes rotate the history log 223 */ 224 225 for (ALL_LIST_ELEMENTS_RO (list, node, adj)) 226 isis_check_dr_change (adj, level); 227 228 /* 229 * We are not DR - if we were -> resign 230 */ 231 if (circuit->u.bc.is_dr[level - 1]) 232 retval = isis_dr_resign (circuit, level); 233 } 234 list_delete (list); 235 return retval; 236} 237 238int 239isis_dr_resign (struct isis_circuit *circuit, int level) 240{ 241 u_char id[ISIS_SYS_ID_LEN + 2]; 242 243 zlog_debug ("isis_dr_resign l%d", level); 244 245 circuit->u.bc.is_dr[level - 1] = 0; 246 circuit->u.bc.run_dr_elect[level - 1] = 0; 247 THREAD_TIMER_OFF (circuit->u.bc.t_run_dr[level - 1]); 248 THREAD_TIMER_OFF (circuit->u.bc.t_refresh_pseudo_lsp[level - 1]); 249 circuit->lsp_regenerate_pending[level - 1] = 0; 250 251 memcpy (id, isis->sysid, ISIS_SYS_ID_LEN); 252 LSP_PSEUDO_ID (id) = circuit->circuit_id; 253 LSP_FRAGMENT (id) = 0; 254 lsp_purge_pseudo (id, circuit, level); 255 256 if (level == 1) 257 { 258 memset (circuit->u.bc.l1_desig_is, 0, ISIS_SYS_ID_LEN + 1); 259 260 THREAD_TIMER_OFF (circuit->t_send_csnp[0]); 261 262 THREAD_TIMER_ON (master, circuit->u.bc.t_run_dr[0], isis_run_dr_l1, 263 circuit, 2 * circuit->hello_interval[0]); 264 265 THREAD_TIMER_ON (master, circuit->t_send_psnp[0], send_l1_psnp, circuit, 266 isis_jitter (circuit->psnp_interval[level - 1], 267 PSNP_JITTER)); 268 } 269 else 270 { 271 memset (circuit->u.bc.l2_desig_is, 0, ISIS_SYS_ID_LEN + 1); 272 273 THREAD_TIMER_OFF (circuit->t_send_csnp[1]); 274 275 THREAD_TIMER_ON (master, circuit->u.bc.t_run_dr[1], isis_run_dr_l2, 276 circuit, 2 * circuit->hello_interval[1]); 277 278 THREAD_TIMER_ON (master, circuit->t_send_psnp[1], send_l2_psnp, circuit, 279 isis_jitter (circuit->psnp_interval[level - 1], 280 PSNP_JITTER)); 281 } 282 283 thread_add_event (master, isis_event_dis_status_change, circuit, 0); 284 285 return ISIS_OK; 286} 287 288int 289isis_dr_commence (struct isis_circuit *circuit, int level) 290{ 291 u_char old_dr[ISIS_SYS_ID_LEN + 2]; 292 293 if (isis->debugs & DEBUG_EVENTS) 294 zlog_debug ("isis_dr_commence l%d", level); 295 296 /* Lets keep a pause in DR election */ 297 circuit->u.bc.run_dr_elect[level - 1] = 0; 298 if (level == 1) 299 THREAD_TIMER_ON (master, circuit->u.bc.t_run_dr[0], isis_run_dr_l1, 300 circuit, 2 * circuit->hello_interval[0]); 301 else 302 THREAD_TIMER_ON (master, circuit->u.bc.t_run_dr[1], isis_run_dr_l2, 303 circuit, 2 * circuit->hello_interval[1]); 304 circuit->u.bc.is_dr[level - 1] = 1; 305 306 if (level == 1) 307 { 308 memcpy (old_dr, circuit->u.bc.l1_desig_is, ISIS_SYS_ID_LEN + 1); 309 LSP_FRAGMENT (old_dr) = 0; 310 if (LSP_PSEUDO_ID (old_dr)) 311 { 312 /* there was a dr elected, purge its LSPs from the db */ 313 lsp_purge_pseudo (old_dr, circuit, level); 314 } 315 memcpy (circuit->u.bc.l1_desig_is, isis->sysid, ISIS_SYS_ID_LEN); 316 *(circuit->u.bc.l1_desig_is + ISIS_SYS_ID_LEN) = circuit->circuit_id; 317 318 assert (circuit->circuit_id); /* must be non-zero */ 319 /* if (circuit->t_send_l1_psnp) 320 thread_cancel (circuit->t_send_l1_psnp); */ 321 lsp_generate_pseudo (circuit, 1); 322 323 THREAD_TIMER_OFF (circuit->u.bc.t_run_dr[0]); 324 THREAD_TIMER_ON (master, circuit->u.bc.t_run_dr[0], isis_run_dr_l1, 325 circuit, 2 * circuit->hello_interval[0]); 326 327 THREAD_TIMER_ON (master, circuit->t_send_csnp[0], send_l1_csnp, circuit, 328 isis_jitter (circuit->csnp_interval[level - 1], 329 CSNP_JITTER)); 330 331 } 332 else 333 { 334 memcpy (old_dr, circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1); 335 LSP_FRAGMENT (old_dr) = 0; 336 if (LSP_PSEUDO_ID (old_dr)) 337 { 338 /* there was a dr elected, purge its LSPs from the db */ 339 lsp_purge_pseudo (old_dr, circuit, level); 340 } 341 memcpy (circuit->u.bc.l2_desig_is, isis->sysid, ISIS_SYS_ID_LEN); 342 *(circuit->u.bc.l2_desig_is + ISIS_SYS_ID_LEN) = circuit->circuit_id; 343 344 assert (circuit->circuit_id); /* must be non-zero */ 345 /* if (circuit->t_send_l1_psnp) 346 thread_cancel (circuit->t_send_l1_psnp); */ 347 lsp_generate_pseudo (circuit, 2); 348 349 THREAD_TIMER_OFF (circuit->u.bc.t_run_dr[1]); 350 THREAD_TIMER_ON (master, circuit->u.bc.t_run_dr[1], isis_run_dr_l2, 351 circuit, 2 * circuit->hello_interval[1]); 352 353 THREAD_TIMER_ON (master, circuit->t_send_csnp[1], send_l2_csnp, circuit, 354 isis_jitter (circuit->csnp_interval[level - 1], 355 CSNP_JITTER)); 356 } 357 358 thread_add_event (master, isis_event_dis_status_change, circuit, 0); 359 360 return ISIS_OK; 361} 362