summaryrefslogtreecommitdiff
path: root/isisd/isis_dr.c
diff options
context:
space:
mode:
Diffstat (limited to 'isisd/isis_dr.c')
-rw-r--r--isisd/isis_dr.c373
1 files changed, 373 insertions, 0 deletions
diff --git a/isisd/isis_dr.c b/isisd/isis_dr.c
new file mode 100644
index 00000000..5b7d23e6
--- /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;
+}
+