Import Upstream version 1.2.2
[quagga-debian.git] / isisd / isis_dr.c
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
50 const char *
51 isis_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
68 int
69 isis_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
85 int
86 isis_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
103 static int
104 isis_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
126 int
127 isis_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
238 int
239 isis_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
288 int
289 isis_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 }