diff options
author | jardin <jardin> | 2003-12-23 09:09:43 +0100 |
---|---|---|
committer | jardin <jardin> | 2003-12-23 09:09:43 +0100 |
commit | eb5d44eb8dcf25a1b328e57d1eabb1f89e3bc59b (patch) | |
tree | 2973e8563fcbd4a8cf901d211ff4f8de00c36381 /isisd/isis_dr.c | |
parent | Reorder free(f); unlink(f); to unlink before freeing. (diff) | |
download | frr-eb5d44eb8dcf25a1b328e57d1eabb1f89e3bc59b.tar.xz frr-eb5d44eb8dcf25a1b328e57d1eabb1f89e3bc59b.zip |
Initial revision
Diffstat (limited to 'isisd/isis_dr.c')
-rw-r--r-- | isisd/isis_dr.c | 373 |
1 files changed, 373 insertions, 0 deletions
diff --git a/isisd/isis_dr.c b/isisd/isis_dr.c new file mode 100644 index 000000000..5b7d23e6d --- /dev/null +++ b/isisd/isis_dr.c @@ -0,0 +1,373 @@ +/* + * IS-IS Rout(e)ing protocol - isis_dr.c + * IS-IS designated router related routines + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public Licenseas published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#include <zebra.h> +#include <net/ethernet.h> + +#include "log.h" +#include "hash.h" +#include "thread.h" +#include "linklist.h" +#include "vty.h" +#include "stream.h" +#include "if.h" + +#include "isisd/dict.h" +#include "isisd/isis_constants.h" +#include "isisd/isis_common.h" +#include "isisd/isis_misc.h" +#include "isisd/isis_flags.h" +#include "isisd/isis_circuit.h" +#include "isisd/isisd.h" +#include "isisd/isis_adjacency.h" +#include "isisd/isis_constants.h" +#include "isisd/isis_pdu.h" +#include "isisd/isis_tlv.h" +#include "isisd/isis_lsp.h" +#include "isisd/isis_dr.h" +#include "isisd/isis_events.h" + +extern struct isis *isis; +extern struct thread_master *master; + +char * +isis_disflag2string (int disflag) { + + switch (disflag) { + case ISIS_IS_NOT_DIS: + return "is not DIS"; + case ISIS_IS_DIS: + return "is DIS"; + case ISIS_WAS_DIS: + return "was DIS"; + default: + return "unknown DIS state"; + } + return NULL; /* not reached */ +} + + + +int +isis_run_dr_l1 (struct thread *thread) +{ + struct isis_circuit *circuit; + + circuit = THREAD_ARG (thread); + assert (circuit); + + if (circuit->u.bc.run_dr_elect[0]) + zlog_warn ("isis_run_dr(): run_dr_elect already set for l1"); + + circuit->u.bc.run_dr_elect[0] = 1; + + return ISIS_OK; +} + +int +isis_run_dr_l2 (struct thread *thread) +{ + struct isis_circuit *circuit; + + circuit = THREAD_ARG (thread); + assert (circuit); + + if (circuit->u.bc.run_dr_elect[1]) + zlog_warn ("isis_run_dr(): run_dr_elect already set for l2"); + + + circuit->u.bc.run_dr_elect[1] = 1; + + return ISIS_OK; +} + +int +isis_check_dr_change (struct isis_adjacency *adj, int level) +{ + int i; + + if ( adj->dis_record[level-1].dis != + adj->dis_record[(1*ISIS_LEVELS) + level - 1].dis) + /* was there a DIS state transition ? */ + { + adj->dischanges[level-1]++; + /* ok rotate the history list through */ + for (i = DIS_RECORDS - 1; i > 0; i--) + { + adj->dis_record[(i * ISIS_LEVELS) + level - 1].dis = + adj->dis_record[((i-1) * ISIS_LEVELS) + level - 1].dis; + adj->dis_record[(i * ISIS_LEVELS) + level - 1].last_dis_change = + adj->dis_record[((i-1) * ISIS_LEVELS) + level - 1].last_dis_change; + } + } + return ISIS_OK; +} + +int +isis_dr_elect (struct isis_circuit *circuit, int level) +{ + struct list *adjdb; + struct listnode *node; + struct isis_adjacency *adj, *adj_dr = NULL; + struct list *list = list_new (); + u_char own_prio; + int biggest_prio = -1; + int cmp_res, retval = ISIS_OK; + + own_prio = circuit->u.bc.priority[level - 1]; + adjdb = circuit->u.bc.adjdb[level - 1]; + + if (!adjdb) { + zlog_warn ("isis_dr_elect() adjdb == NULL"); + retval = ISIS_WARNING; + list_delete (list); + goto out; + } + isis_adj_build_up_list (adjdb, list); + + /* + * Loop the adjacencies and find the one with the biggest priority + */ + for (node = listhead (list); node; nextnode (node)) { + adj = getdata (node); + /* clear flag for show output */ + adj->dis_record[level-1].dis = ISIS_IS_NOT_DIS; + adj->dis_record[level-1].last_dis_change = time (NULL); + + if (adj->prio[level-1] > biggest_prio) { + biggest_prio = adj->prio[level-1]; + adj_dr = adj; + } else if (adj->prio[level-1] == biggest_prio) { + /* + * Comparison of MACs breaks a tie + */ + if (adj_dr) { + cmp_res = memcmp (adj_dr->snpa, adj->snpa, ETH_ALEN); + if (cmp_res < 0) { + adj_dr = adj; + } + if (cmp_res == 0) + zlog_warn ("isis_dr_elect(): multiple adjacencies with same SNPA"); + } else { + adj_dr = adj; + } + } + } + + if (!adj_dr) { + /* + * Could not find the DR - means we are alone and thus the DR + */ + if ( !circuit->u.bc.is_dr[level - 1]) { + list_delete (list); + list = NULL; + return isis_dr_commence (circuit, level); + } + goto out; + } + + /* + * Now we have the DR adjacency, compare it to self + */ + if (adj_dr->prio[level-1] < own_prio || (adj_dr->prio[level-1] == own_prio && + memcmp (adj_dr->snpa, circuit->u.bc.snpa, + ETH_ALEN) < 0)) { + if (!circuit->u.bc.is_dr[level - 1]) { + /* + * We are the DR -> commence + */ + list_delete (list); + return isis_dr_commence (circuit, level); + } + } else { + + /* ok we have found the DIS - lets mark the adjacency */ + /* set flag for show output */ + adj_dr->dis_record[level - 1].dis = ISIS_IS_DIS; + adj_dr->dis_record[level - 1].last_dis_change = time(NULL); + + /* now loop through a second time to check if there has been a DIS change + * if yes rotate the history log + */ + + for (node = listhead (list); node; nextnode (node)) { + adj = getdata (node); + isis_check_dr_change(adj, level); + } + + /* + * We are not DR - if we were -> resign + */ + + if (circuit->u.bc.is_dr[level - 1]) { + list_delete (list); + return isis_dr_resign (circuit, level); + } + } + out: + if (list) + list_delete (list); + return retval; +} + +int +isis_dr_resign (struct isis_circuit *circuit, int level) +{ + u_char id[ISIS_SYS_ID_LEN + 2]; + + zlog_info ("isis_dr_resign l%d", level); + + circuit->u.bc.is_dr[level - 1] = 0; + circuit->u.bc.run_dr_elect[level - 1] = 0; + if (circuit->u.bc.t_run_dr[level - 1]) { + thread_cancel (circuit->u.bc.t_run_dr[level - 1]); + circuit->u.bc.t_run_dr[level - 1] = NULL; + } + if (circuit->u.bc.t_refresh_pseudo_lsp[level - 1]) { + thread_cancel (circuit->u.bc.t_refresh_pseudo_lsp[level - 1]); + circuit->u.bc.t_refresh_pseudo_lsp[level - 1] = NULL; + } + + memcpy (id, isis->sysid, ISIS_SYS_ID_LEN); + LSP_PSEUDO_ID(id) = circuit->circuit_id; + LSP_FRAGMENT(id) = 0; + lsp_purge_dr (id, circuit, level); + + if (level == 1) { + memset (circuit->u.bc.l1_desig_is, 0, ISIS_SYS_ID_LEN + 1); + + if (circuit->t_send_csnp[0]) + thread_cancel (circuit->t_send_csnp[0]); + + circuit->u.bc.t_run_dr[0] = + thread_add_timer (master, isis_run_dr_l1, circuit, + 2 * circuit->hello_interval[1]); + + circuit->t_send_psnp[0] = + thread_add_timer (master, + send_l1_psnp, + circuit, + isis_jitter (circuit->psnp_interval[level - 1], + PSNP_JITTER)); + } else { + memset (circuit->u.bc.l2_desig_is, 0, ISIS_SYS_ID_LEN + 1); + + if (circuit->t_send_csnp[0]) + thread_cancel (circuit->t_send_csnp[0]); + + circuit->u.bc.t_run_dr[1] = + thread_add_timer (master, isis_run_dr_l2, circuit, + 2 * circuit->hello_interval[1]); + circuit->t_send_psnp[1] = + thread_add_timer (master, + send_l2_psnp, + circuit, + isis_jitter (circuit->psnp_interval[level - 1], + PSNP_JITTER)); + } + + thread_add_event (master, isis_event_dis_status_change, circuit, 0); + + return ISIS_OK; +} + +int +isis_dr_commence (struct isis_circuit *circuit, int level) +{ + u_char old_dr[ISIS_SYS_ID_LEN + 2]; + + zlog_info ("isis_dr_commence l%d", level); + + /* Lets keep a pause in DR election */ + circuit->u.bc.run_dr_elect[level - 1] = 0; + if (level == 1) + circuit->u.bc.t_run_dr[0] = + thread_add_timer (master, isis_run_dr_l1, circuit, + 2 * circuit->hello_multiplier[0] * + circuit->hello_interval[0]); + else + circuit->u.bc.t_run_dr[1] = + thread_add_timer (master, isis_run_dr_l2, circuit, + 2 * circuit->hello_multiplier[1] * + circuit->hello_interval[1]); + circuit->u.bc.is_dr[level - 1] = 1; + + if (level == 1) { + memcpy (old_dr, circuit->u.bc.l1_desig_is, ISIS_SYS_ID_LEN + 1); + LSP_FRAGMENT (old_dr) = 0; + if (LSP_PSEUDO_ID(old_dr)) { + /* there was a dr elected, purge its LSPs from the db */ + lsp_purge_dr (old_dr, circuit, level); + } + memcpy (circuit->u.bc.l1_desig_is, isis->sysid, ISIS_SYS_ID_LEN); + *(circuit->u.bc.l1_desig_is + ISIS_SYS_ID_LEN) = circuit->circuit_id; + + assert (circuit->circuit_id); /* must be non-zero */ + /* if (circuit->t_send_l1_psnp) + thread_cancel (circuit->t_send_l1_psnp); */ + lsp_l1_pseudo_generate (circuit); + + circuit->u.bc.t_run_dr[0] = + thread_add_timer (master, isis_run_dr_l1, circuit, + 2 * circuit->hello_interval[0]); + + circuit->t_send_csnp[0] = thread_add_timer (master, + send_l1_csnp, + circuit, + isis_jitter + (circuit->csnp_interval[level-1], + CSNP_JITTER)); + } else { + memcpy (old_dr, circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1); + LSP_FRAGMENT (old_dr) = 0; + if (LSP_PSEUDO_ID(old_dr)) { + /* there was a dr elected, purge its LSPs from the db */ + lsp_purge_dr (old_dr, circuit, level); + } + memcpy (circuit->u.bc.l2_desig_is, isis->sysid, ISIS_SYS_ID_LEN); + *(circuit->u.bc.l2_desig_is + ISIS_SYS_ID_LEN) = circuit->circuit_id; + + assert (circuit->circuit_id); /* must be non-zero */ + /* if (circuit->t_send_l1_psnp) + thread_cancel (circuit->t_send_l1_psnp); */ + lsp_l2_pseudo_generate (circuit); + + circuit->u.bc.t_run_dr[1] = + thread_add_timer (master, isis_run_dr_l2, circuit, + 2 * circuit->hello_interval[1]); + + circuit->t_send_csnp[1] = + thread_add_timer (master, + send_l2_csnp, + circuit, + isis_jitter (circuit->csnp_interval[level-1], + CSNP_JITTER)); + + } + + thread_add_event (master, isis_event_dis_status_change, circuit, 0); + + return ISIS_OK; +} + |