diff options
| author | jardin <jardin> | 2003-12-23 08:09:43 +0000 | 
|---|---|---|
| committer | jardin <jardin> | 2003-12-23 08:09:43 +0000 | 
| commit | eb5d44eb8dcf25a1b328e57d1eabb1f89e3bc59b (patch) | |
| tree | 2973e8563fcbd4a8cf901d211ff4f8de00c36381 /isisd/isis_dr.c | |
| parent | 3dbf99698a3be2e920871c3127ea089e061a127c (diff) | |
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 0000000000..5b7d23e6da --- /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; +} +  | 
