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 *
isis_disflag2string(int disflag)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
isis_run_dr_l1(struct thread * thread)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
isis_run_dr_l2(struct thread * thread)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
isis_check_dr_change(struct isis_adjacency * adj,int level)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
isis_dr_elect(struct isis_circuit * circuit,int level)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
isis_dr_resign(struct isis_circuit * circuit,int level)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
isis_dr_commence(struct isis_circuit * circuit,int level)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 }
362