Import Upstream version 1.2.2
[quagga-debian.git] / isisd / isis_adjacency.c
1 /*
2  * IS-IS Rout(e)ing protocol - isis_adjacency.c   
3  *                             handling of IS-IS adjacencies
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 #include <zebra.h>
25
26 #include "log.h"
27 #include "memory.h"
28 #include "hash.h"
29 #include "vty.h"
30 #include "linklist.h"
31 #include "thread.h"
32 #include "if.h"
33 #include "stream.h"
34
35 #include "isisd/dict.h"
36 #include "isisd/include-netbsd/iso.h"
37 #include "isisd/isis_constants.h"
38 #include "isisd/isis_common.h"
39 #include "isisd/isis_flags.h"
40 #include "isisd/isisd.h"
41 #include "isisd/isis_circuit.h"
42 #include "isisd/isis_adjacency.h"
43 #include "isisd/isis_misc.h"
44 #include "isisd/isis_dr.h"
45 #include "isisd/isis_dynhn.h"
46 #include "isisd/isis_pdu.h"
47 #include "isisd/isis_tlv.h"
48 #include "isisd/isis_lsp.h"
49 #include "isisd/isis_spf.h"
50 #include "isisd/isis_events.h"
51
52 extern struct isis *isis;
53
54 static struct isis_adjacency *
55 adj_alloc (const u_char *id)
56 {
57   struct isis_adjacency *adj;
58
59   adj = XCALLOC (MTYPE_ISIS_ADJACENCY, sizeof (struct isis_adjacency));
60   memcpy (adj->sysid, id, ISIS_SYS_ID_LEN);
61
62   return adj;
63 }
64
65 struct isis_adjacency *
66 isis_new_adj (const u_char * id, const u_char * snpa, int level,
67               struct isis_circuit *circuit)
68 {
69   struct isis_adjacency *adj;
70   int i;
71
72   adj = adj_alloc (id);         /* P2P kludge */
73
74   if (adj == NULL)
75     {
76       zlog_err ("Out of memory!");
77       return NULL;
78     }
79
80   if (snpa) {
81     memcpy (adj->snpa, snpa, ETH_ALEN);
82   } else {
83     memset (adj->snpa, ' ', ETH_ALEN);
84   }
85
86   adj->circuit = circuit;
87   adj->level = level;
88   adj->flaps = 0;
89   adj->last_flap = time (NULL);
90   if (circuit->circ_type == CIRCUIT_T_BROADCAST)
91     {
92       listnode_add (circuit->u.bc.adjdb[level - 1], adj);
93       adj->dischanges[level - 1] = 0;
94       for (i = 0; i < DIS_RECORDS; i++) /* clear N DIS state change records */
95         {
96           adj->dis_record[(i * ISIS_LEVELS) + level - 1].dis
97             = ISIS_UNKNOWN_DIS;
98           adj->dis_record[(i * ISIS_LEVELS) + level - 1].last_dis_change
99             = time (NULL);
100         }
101     }
102
103   return adj;
104 }
105
106 struct isis_adjacency *
107 isis_adj_lookup (const u_char * sysid, struct list *adjdb)
108 {
109   struct isis_adjacency *adj;
110   struct listnode *node;
111
112   for (ALL_LIST_ELEMENTS_RO (adjdb, node, adj))
113     if (memcmp (adj->sysid, sysid, ISIS_SYS_ID_LEN) == 0)
114       return adj;
115
116   return NULL;
117 }
118
119 struct isis_adjacency *
120 isis_adj_lookup_snpa (const u_char * ssnpa, struct list *adjdb)
121 {
122   struct listnode *node;
123   struct isis_adjacency *adj;
124
125   for (ALL_LIST_ELEMENTS_RO (adjdb, node, adj))
126     if (memcmp (adj->snpa, ssnpa, ETH_ALEN) == 0)
127       return adj;
128
129   return NULL;
130 }
131
132 void
133 isis_delete_adj (void *arg)
134 {
135   struct isis_adjacency *adj = arg;
136
137   if (!adj)
138     return;
139
140   THREAD_TIMER_OFF (adj->t_expire);
141
142   /* remove from SPF trees */
143   spftree_area_adj_del (adj->circuit->area, adj);
144
145   if (adj->area_addrs)
146     list_delete (adj->area_addrs);
147   if (adj->ipv4_addrs)
148     list_delete (adj->ipv4_addrs);
149 #ifdef HAVE_IPV6
150   if (adj->ipv6_addrs)
151     list_delete (adj->ipv6_addrs);
152 #endif
153
154   XFREE (MTYPE_ISIS_ADJACENCY, adj);
155   return;
156 }
157
158 static const char *
159 adj_state2string (int state)
160 {
161
162   switch (state)
163     {
164     case ISIS_ADJ_INITIALIZING:
165       return "Initializing";
166     case ISIS_ADJ_UP:
167       return "Up";
168     case ISIS_ADJ_DOWN:
169       return "Down";
170     default:
171       return "Unknown";
172     }
173
174   return NULL;                  /* not reached */
175 }
176
177 void
178 isis_adj_state_change (struct isis_adjacency *adj, enum isis_adj_state new_state,
179                        const char *reason)
180 {
181   int old_state;
182   int level;
183   struct isis_circuit *circuit;
184
185   old_state = adj->adj_state;
186   adj->adj_state = new_state;
187
188   circuit = adj->circuit;
189
190   if (isis->debugs & DEBUG_ADJ_PACKETS)
191     {
192       zlog_debug ("ISIS-Adj (%s): Adjacency state change %d->%d: %s",
193                  circuit->area->area_tag,
194                  old_state, new_state, reason ? reason : "unspecified");
195     }
196
197   if (circuit->area->log_adj_changes)
198     {
199       const char *adj_name;
200       struct isis_dynhn *dyn;
201
202       dyn = dynhn_find_by_id (adj->sysid);
203       if (dyn)
204         adj_name = (const char *)dyn->name.name;
205       else
206         adj_name = sysid_print (adj->sysid);
207
208       zlog_info ("%%ADJCHANGE: Adjacency to %s (%s) changed from %s to %s, %s",
209                  adj_name,
210                  adj->circuit->interface->name,
211                  adj_state2string (old_state),
212                  adj_state2string (new_state),
213                  reason ? reason : "unspecified");
214     }
215
216   if (circuit->circ_type == CIRCUIT_T_BROADCAST)
217     {
218       for (level = IS_LEVEL_1; level <= IS_LEVEL_2; level++)
219       {
220         if ((adj->level & level) == 0)
221           continue;
222         if (new_state == ISIS_ADJ_UP)
223         {
224           circuit->upadjcount[level - 1]++;
225           isis_event_adjacency_state_change (adj, new_state);
226           /* update counter & timers for debugging purposes */
227           adj->last_flap = time (NULL);
228           adj->flaps++;
229         }
230         else if (new_state == ISIS_ADJ_DOWN)
231         {
232           listnode_delete (circuit->u.bc.adjdb[level - 1], adj);
233           circuit->upadjcount[level - 1]--;
234           if (circuit->upadjcount[level - 1] == 0)
235             {
236               /* Clean lsp_queue when no adj is up. */
237               if (circuit->lsp_queue)
238                 list_delete_all_node (circuit->lsp_queue);
239             }
240           isis_event_adjacency_state_change (adj, new_state);
241           isis_delete_adj (adj);
242         }
243
244         if (circuit->u.bc.lan_neighs[level - 1])
245           {
246             list_delete_all_node (circuit->u.bc.lan_neighs[level - 1]);
247             isis_adj_build_neigh_list (circuit->u.bc.adjdb[level - 1],
248                                        circuit->u.bc.lan_neighs[level - 1]);
249           }
250
251         /* On adjacency state change send new pseudo LSP if we are the DR */
252         if (circuit->u.bc.is_dr[level - 1])
253           lsp_regenerate_schedule_pseudo (circuit, level);
254       }
255     }
256   else if (circuit->circ_type == CIRCUIT_T_P2P)
257     {
258       for (level = IS_LEVEL_1; level <= IS_LEVEL_2; level++)
259       {
260         if ((adj->level & level) == 0)
261           continue;
262         if (new_state == ISIS_ADJ_UP)
263         {
264           circuit->upadjcount[level - 1]++;
265           isis_event_adjacency_state_change (adj, new_state);
266
267           if (adj->sys_type == ISIS_SYSTYPE_UNKNOWN)
268             send_hello (circuit, level);
269
270           /* update counter & timers for debugging purposes */
271           adj->last_flap = time (NULL);
272           adj->flaps++;
273
274           /* 7.3.17 - going up on P2P -> send CSNP */
275           /* FIXME: yup, I know its wrong... but i will do it! (for now) */
276           send_csnp (circuit, level);
277         }
278         else if (new_state == ISIS_ADJ_DOWN)
279         {
280           if (adj->circuit->u.p2p.neighbor == adj)
281             adj->circuit->u.p2p.neighbor = NULL;
282           circuit->upadjcount[level - 1]--;
283           if (circuit->upadjcount[level - 1] == 0)
284             {
285               /* Clean lsp_queue when no adj is up. */
286               if (circuit->lsp_queue)
287                 list_delete_all_node (circuit->lsp_queue);
288             }
289           isis_event_adjacency_state_change (adj, new_state);
290           isis_delete_adj (adj);
291         }
292       }
293     }
294
295   return;
296 }
297
298
299 void
300 isis_adj_print (struct isis_adjacency *adj)
301 {
302   struct isis_dynhn *dyn;
303   struct listnode *node;
304   struct in_addr *ipv4_addr;
305 #ifdef HAVE_IPV6
306   struct in6_addr *ipv6_addr;
307   u_char ip6[INET6_ADDRSTRLEN];
308 #endif /* HAVE_IPV6 */
309
310   if (!adj)
311     return;
312   dyn = dynhn_find_by_id (adj->sysid);
313   if (dyn)
314     zlog_debug ("%s", dyn->name.name);
315
316   zlog_debug ("SystemId %20s SNPA %s, level %d\nHolding Time %d",
317               sysid_print (adj->sysid), snpa_print (adj->snpa),
318               adj->level, adj->hold_time);
319   if (adj->ipv4_addrs && listcount (adj->ipv4_addrs) > 0)
320     {
321       zlog_debug ("IPv4 Address(es):");
322
323       for (ALL_LIST_ELEMENTS_RO (adj->ipv4_addrs, node, ipv4_addr))
324         zlog_debug ("%s", inet_ntoa (*ipv4_addr));
325     }
326
327 #ifdef HAVE_IPV6
328   if (adj->ipv6_addrs && listcount (adj->ipv6_addrs) > 0)
329     {
330       zlog_debug ("IPv6 Address(es):");
331       for (ALL_LIST_ELEMENTS_RO (adj->ipv6_addrs, node, ipv6_addr))
332         {
333           inet_ntop (AF_INET6, ipv6_addr, (char *)ip6, INET6_ADDRSTRLEN);
334           zlog_debug ("%s", ip6);
335         }
336     }
337 #endif /* HAVE_IPV6 */
338   zlog_debug ("Speaks: %s", nlpid2string (&adj->nlpids));
339
340   return;
341 }
342
343 int
344 isis_adj_expire (struct thread *thread)
345 {
346   struct isis_adjacency *adj;
347
348   /*
349    * Get the adjacency
350    */
351   adj = THREAD_ARG (thread);
352   assert (adj);
353   adj->t_expire = NULL;
354
355   /* trigger the adj expire event */
356   isis_adj_state_change (adj, ISIS_ADJ_DOWN, "holding time expired");
357
358   return 0;
359 }
360
361 /*
362  * show isis neighbor [detail]
363  */
364 void
365 isis_adj_print_vty (struct isis_adjacency *adj, struct vty *vty, char detail)
366 {
367 #ifdef HAVE_IPV6
368   struct in6_addr *ipv6_addr;
369   u_char ip6[INET6_ADDRSTRLEN];
370 #endif /* HAVE_IPV6 */
371   struct in_addr *ip_addr;
372   time_t now;
373   struct isis_dynhn *dyn;
374   int level;
375   struct listnode *node;
376
377   dyn = dynhn_find_by_id (adj->sysid);
378   if (dyn)
379     vty_out (vty, "  %-20s", dyn->name.name);
380   else
381     vty_out (vty, "  %-20s", sysid_print (adj->sysid));
382
383   if (detail == ISIS_UI_LEVEL_BRIEF)
384     {
385       if (adj->circuit)
386         vty_out (vty, "%-12s", adj->circuit->interface->name);
387       else
388         vty_out (vty, "NULL circuit!");
389       vty_out (vty, "%-3u", adj->level);        /* level */
390       vty_out (vty, "%-13s", adj_state2string (adj->adj_state));
391       now = time (NULL);
392       if (adj->last_upd)
393         vty_out (vty, "%-9llu",
394                  (unsigned long long)adj->last_upd + adj->hold_time - now);
395       else
396         vty_out (vty, "-        ");
397       vty_out (vty, "%-10s", snpa_print (adj->snpa));
398       vty_out (vty, "%s", VTY_NEWLINE);
399     }
400
401   if (detail == ISIS_UI_LEVEL_DETAIL)
402     {
403       level = adj->level;
404       vty_out (vty, "%s", VTY_NEWLINE);
405       if (adj->circuit)
406         vty_out (vty, "    Interface: %s", adj->circuit->interface->name);
407       else
408         vty_out (vty, "    Interface: NULL circuit");
409       vty_out (vty, ", Level: %u", adj->level); /* level */
410       vty_out (vty, ", State: %s", adj_state2string (adj->adj_state));
411       now = time (NULL);
412       if (adj->last_upd)
413         vty_out (vty, ", Expires in %s",
414                  time2string (adj->last_upd + adj->hold_time - now));
415       else
416         vty_out (vty, ", Expires in %s", time2string (adj->hold_time));
417       vty_out (vty, "%s", VTY_NEWLINE);
418       vty_out (vty, "    Adjacency flaps: %u", adj->flaps);
419       vty_out (vty, ", Last: %s ago", time2string (now - adj->last_flap));
420       vty_out (vty, "%s", VTY_NEWLINE);
421       vty_out (vty, "    Circuit type: %s", circuit_t2string (adj->circuit_t));
422       vty_out (vty, ", Speaks: %s", nlpid2string (&adj->nlpids));
423       vty_out (vty, "%s", VTY_NEWLINE);
424       vty_out (vty, "    SNPA: %s", snpa_print (adj->snpa));
425       if (adj->circuit && (adj->circuit->circ_type == CIRCUIT_T_BROADCAST))
426       {
427         dyn = dynhn_find_by_id (adj->lanid);
428         if (dyn)
429           vty_out (vty, ", LAN id: %s.%02x",
430               dyn->name.name, adj->lanid[ISIS_SYS_ID_LEN]);
431         else
432           vty_out (vty, ", LAN id: %s.%02x",
433               sysid_print (adj->lanid), adj->lanid[ISIS_SYS_ID_LEN]);
434
435         vty_out (vty, "%s", VTY_NEWLINE);
436         vty_out (vty, "    LAN Priority: %u", adj->prio[adj->level - 1]);
437
438         vty_out (vty, ", %s, DIS flaps: %u, Last: %s ago",
439             isis_disflag2string (adj->dis_record[ISIS_LEVELS + level - 1].
440               dis), adj->dischanges[level - 1],
441             time2string (now -
442               (adj->dis_record[ISIS_LEVELS + level - 1].
443                last_dis_change)));
444       }
445       vty_out (vty, "%s", VTY_NEWLINE);
446
447       if (adj->area_addrs && listcount (adj->area_addrs) > 0)
448         {
449           struct area_addr *area_addr;
450           vty_out (vty, "    Area Address(es):%s", VTY_NEWLINE);
451           for (ALL_LIST_ELEMENTS_RO (adj->area_addrs, node, area_addr))
452             vty_out (vty, "      %s%s", isonet_print (area_addr->area_addr,
453                      area_addr->addr_len), VTY_NEWLINE);
454         }
455       if (adj->ipv4_addrs && listcount (adj->ipv4_addrs) > 0)
456         {
457           vty_out (vty, "    IPv4 Address(es):%s", VTY_NEWLINE);
458           for (ALL_LIST_ELEMENTS_RO (adj->ipv4_addrs, node, ip_addr))
459             vty_out (vty, "      %s%s", inet_ntoa (*ip_addr), VTY_NEWLINE);
460         }
461 #ifdef HAVE_IPV6
462       if (adj->ipv6_addrs && listcount (adj->ipv6_addrs) > 0)
463         {
464           vty_out (vty, "    IPv6 Address(es):%s", VTY_NEWLINE);
465           for (ALL_LIST_ELEMENTS_RO (adj->ipv6_addrs, node, ipv6_addr))
466             {
467               inet_ntop (AF_INET6, ipv6_addr, (char *)ip6, INET6_ADDRSTRLEN);
468               vty_out (vty, "      %s%s", ip6, VTY_NEWLINE);
469             }
470         }
471 #endif /* HAVE_IPV6 */
472       vty_out (vty, "%s", VTY_NEWLINE);
473     }
474   return;
475 }
476
477 void
478 isis_adj_build_neigh_list (struct list *adjdb, struct list *list)
479 {
480   struct isis_adjacency *adj;
481   struct listnode *node;
482
483   if (!list)
484     {
485       zlog_warn ("isis_adj_build_neigh_list(): NULL list");
486       return;
487     }
488
489   for (ALL_LIST_ELEMENTS_RO (adjdb, node, adj))
490     {
491       if (!adj)
492         {
493           zlog_warn ("isis_adj_build_neigh_list(): NULL adj");
494           return;
495         }
496
497       if ((adj->adj_state == ISIS_ADJ_UP ||
498            adj->adj_state == ISIS_ADJ_INITIALIZING))
499         listnode_add (list, adj->snpa);
500     }
501   return;
502 }
503
504 void
505 isis_adj_build_up_list (struct list *adjdb, struct list *list)
506 {
507   struct isis_adjacency *adj;
508   struct listnode *node;
509
510   if (adjdb == NULL) {
511     zlog_warn ("isis_adj_build_up_list(): adjacency DB is empty");
512     return;
513   }
514
515   if (!list)
516     {
517       zlog_warn ("isis_adj_build_up_list(): NULL list");
518       return;
519     }
520
521   for (ALL_LIST_ELEMENTS_RO (adjdb, node, adj))
522     {
523       if (!adj)
524         {
525           zlog_warn ("isis_adj_build_up_list(): NULL adj");
526           return;
527         }
528
529       if (adj->adj_state == ISIS_ADJ_UP)
530         listnode_add (list, adj);
531     }
532
533   return;
534 }