1/*
2 * OSPF version 2  Neighbor State Machine
3 * From RFC2328 [OSPF Version 2]
4 * Copyright (C) 1999, 2000 Toshiaki Takada
5 *
6 * This file is part of GNU Zebra.
7 *
8 * GNU Zebra is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2, or (at your option) any
11 * later version.
12 *
13 * GNU Zebra is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with GNU Zebra; see the file COPYING.  If not, write to the Free
20 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
21 * 02111-1307, USA.
22 */
23
24#include <zebra.h>
25
26#include "thread.h"
27#include "memory.h"
28#include "hash.h"
29#include "linklist.h"
30#include "prefix.h"
31#include "if.h"
32#include "table.h"
33#include "stream.h"
34#include "table.h"
35#include "log.h"
36
37#include "ospfd/ospfd.h"
38#include "ospfd/ospf_interface.h"
39#include "ospfd/ospf_ism.h"
40#include "ospfd/ospf_asbr.h"
41#include "ospfd/ospf_lsa.h"
42#include "ospfd/ospf_lsdb.h"
43#include "ospfd/ospf_neighbor.h"
44#include "ospfd/ospf_nsm.h"
45#include "ospfd/ospf_network.h"
46#include "ospfd/ospf_packet.h"
47#include "ospfd/ospf_dump.h"
48#include "ospfd/ospf_flood.h"
49#include "ospfd/ospf_abr.h"
50#include "ospfd/ospf_snmp.h"
51
52static void nsm_clear_adj (struct ospf_neighbor *);
53
54/* OSPF NSM Timer functions. */
55static int
56ospf_inactivity_timer (struct thread *thread)
57{
58  struct ospf_neighbor *nbr;
59
60  nbr = THREAD_ARG (thread);
61  nbr->t_inactivity = NULL;
62
63  if (IS_DEBUG_OSPF (nsm, NSM_TIMERS))
64    zlog (NULL, LOG_DEBUG, "NSM[%s:%s]: Timer (Inactivity timer expire)",
65	  IF_NAME (nbr->oi), inet_ntoa (nbr->router_id));
66
67  OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_InactivityTimer);
68
69  return 0;
70}
71
72static int
73ospf_db_desc_timer (struct thread *thread)
74{
75  struct ospf_neighbor *nbr;
76
77  nbr = THREAD_ARG (thread);
78  nbr->t_db_desc = NULL;
79
80  if (IS_DEBUG_OSPF (nsm, NSM_TIMERS))
81    zlog (NULL, LOG_DEBUG, "NSM[%s:%s]: Timer (DD Retransmit timer expire)",
82	  IF_NAME (nbr->oi), inet_ntoa (nbr->src));
83
84  /* resent last send DD packet. */
85  assert (nbr->last_send);
86  ospf_db_desc_resend (nbr);
87
88  /* DD Retransmit timer set. */
89  OSPF_NSM_TIMER_ON (nbr->t_db_desc, ospf_db_desc_timer, nbr->v_db_desc);
90
91  return 0;
92}
93
94/* Hook function called after ospf NSM event is occured.
95 *
96 * Set/clear any timers whose condition is implicit to the neighbour
97 * state. There may be other timers which are set/unset according to other
98 * state.
99 *
100 * We rely on this function to properly clear timers in lower states,
101 * particularly before deleting a neighbour.
102 */
103static void
104nsm_timer_set (struct ospf_neighbor *nbr)
105{
106  switch (nbr->state)
107    {
108    case NSM_Deleted:
109    case NSM_Down:
110      OSPF_NSM_TIMER_OFF (nbr->t_inactivity);
111      OSPF_NSM_TIMER_OFF (nbr->t_hello_reply);
112    case NSM_Attempt:
113    case NSM_Init:
114    case NSM_TwoWay:
115      OSPF_NSM_TIMER_OFF (nbr->t_db_desc);
116      OSPF_NSM_TIMER_OFF (nbr->t_ls_upd);
117      OSPF_NSM_TIMER_OFF (nbr->t_ls_req);
118      break;
119    case NSM_ExStart:
120      OSPF_NSM_TIMER_ON (nbr->t_db_desc, ospf_db_desc_timer, nbr->v_db_desc);
121      OSPF_NSM_TIMER_OFF (nbr->t_ls_upd);
122      OSPF_NSM_TIMER_OFF (nbr->t_ls_req);
123      break;
124    case NSM_Exchange:
125      OSPF_NSM_TIMER_ON (nbr->t_ls_upd, ospf_ls_upd_timer, nbr->v_ls_upd);
126      if (!IS_SET_DD_MS (nbr->dd_flags))
127	OSPF_NSM_TIMER_OFF (nbr->t_db_desc);
128      break;
129    case NSM_Loading:
130    case NSM_Full:
131    default:
132      OSPF_NSM_TIMER_OFF (nbr->t_db_desc);
133      break;
134    }
135}
136
137/* 10.4 of RFC2328, indicate whether an adjacency is appropriate with
138 * the given neighbour
139 */
140static int
141nsm_should_adj (struct ospf_neighbor *nbr)
142{
143  struct ospf_interface *oi = nbr->oi;
144
145      /* These network types must always form adjacencies. */
146  if (oi->type == OSPF_IFTYPE_POINTOPOINT
147      || oi->type == OSPF_IFTYPE_POINTOMULTIPOINT
148      || oi->type == OSPF_IFTYPE_VIRTUALLINK
149      /* Router itself is the DRouter or the BDRouter. */
150      || IPV4_ADDR_SAME (&oi->address->u.prefix4, &DR (oi))
151      || IPV4_ADDR_SAME (&oi->address->u.prefix4, &BDR (oi))
152      /* Neighboring Router is the DRouter or the BDRouter. */
153      || IPV4_ADDR_SAME (&nbr->address.u.prefix4, &DR (oi))
154      || IPV4_ADDR_SAME (&nbr->address.u.prefix4, &BDR (oi)))
155    return 1;
156
157  return 0;
158}
159
160/* OSPF NSM functions. */
161static int
162nsm_packet_received (struct ospf_neighbor *nbr)
163{
164  /* Start or Restart Inactivity Timer. */
165  OSPF_NSM_TIMER_OFF (nbr->t_inactivity);
166
167  OSPF_NSM_TIMER_ON (nbr->t_inactivity, ospf_inactivity_timer,
168		     nbr->v_inactivity);
169
170  if (nbr->oi->type == OSPF_IFTYPE_NBMA && nbr->nbr_nbma)
171    OSPF_POLL_TIMER_OFF (nbr->nbr_nbma->t_poll);
172
173  return 0;
174}
175
176static int
177nsm_start (struct ospf_neighbor *nbr)
178{
179  if (nbr->nbr_nbma)
180      OSPF_POLL_TIMER_OFF (nbr->nbr_nbma->t_poll);
181
182  OSPF_NSM_TIMER_OFF (nbr->t_inactivity);
183
184  OSPF_NSM_TIMER_ON (nbr->t_inactivity, ospf_inactivity_timer,
185                     nbr->v_inactivity);
186
187  return 0;
188}
189
190static int
191nsm_twoway_received (struct ospf_neighbor *nbr)
192{
193  return (nsm_should_adj (nbr) ? NSM_ExStart : NSM_TwoWay);
194}
195
196int
197ospf_db_summary_count (struct ospf_neighbor *nbr)
198{
199  return ospf_lsdb_count_all (&nbr->db_sum);
200}
201
202int
203ospf_db_summary_isempty (struct ospf_neighbor *nbr)
204{
205  return ospf_lsdb_isempty (&nbr->db_sum);
206}
207
208static int
209ospf_db_summary_add (struct ospf_neighbor *nbr, struct ospf_lsa *lsa)
210{
211#ifdef HAVE_OPAQUE_LSA
212  switch (lsa->data->type)
213    {
214    case OSPF_OPAQUE_LINK_LSA:
215      /* Exclude type-9 LSAs that does not have the same "oi" with "nbr". */
216      if (nbr->oi && ospf_if_exists (lsa->oi) != nbr->oi)
217          return 0;
218      break;
219    case OSPF_OPAQUE_AREA_LSA:
220      /*
221       * It is assured by the caller function "nsm_negotiation_done()"
222       * that every given LSA belongs to the same area with "nbr".
223       */
224      break;
225    case OSPF_OPAQUE_AS_LSA:
226    default:
227      break;
228    }
229#endif /* HAVE_OPAQUE_LSA */
230
231  /* Stay away from any Local Translated Type-7 LSAs */
232  if (CHECK_FLAG (lsa->flags, OSPF_LSA_LOCAL_XLT))
233    return 0;
234
235  if (IS_LSA_MAXAGE (lsa))
236    ospf_ls_retransmit_add (nbr, lsa);
237  else
238    ospf_lsdb_add (&nbr->db_sum, lsa);
239
240  return 0;
241}
242
243void
244ospf_db_summary_clear (struct ospf_neighbor *nbr)
245{
246  struct ospf_lsdb *lsdb;
247  int i;
248
249  lsdb = &nbr->db_sum;
250  for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++)
251    {
252      struct route_table *table = lsdb->type[i].db;
253      struct route_node *rn;
254
255      for (rn = route_top (table); rn; rn = route_next (rn))
256	if (rn->info)
257	  ospf_lsdb_delete (&nbr->db_sum, rn->info);
258    }
259}
260
261
262
263/* The area link state database consists of the router-LSAs,
264   network-LSAs and summary-LSAs contained in the area structure,
265   along with the AS-external-LSAs contained in the global structure.
266   AS-external-LSAs are omitted from a virtual neighbor's Database
267   summary list.  AS-external-LSAs are omitted from the Database
268   summary list if the area has been configured as a stub. */
269static int
270nsm_negotiation_done (struct ospf_neighbor *nbr)
271{
272  struct ospf_area *area = nbr->oi->area;
273  struct ospf_lsa *lsa;
274  struct route_node *rn;
275
276  LSDB_LOOP (ROUTER_LSDB (area), rn, lsa)
277    ospf_db_summary_add (nbr, lsa);
278  LSDB_LOOP (NETWORK_LSDB (area), rn, lsa)
279    ospf_db_summary_add (nbr, lsa);
280  LSDB_LOOP (SUMMARY_LSDB (area), rn, lsa)
281    ospf_db_summary_add (nbr, lsa);
282  LSDB_LOOP (ASBR_SUMMARY_LSDB (area), rn, lsa)
283    ospf_db_summary_add (nbr, lsa);
284
285#ifdef HAVE_OPAQUE_LSA
286  /* Process only if the neighbor is opaque capable. */
287  if (CHECK_FLAG (nbr->options, OSPF_OPTION_O))
288    {
289      LSDB_LOOP (OPAQUE_LINK_LSDB (area), rn, lsa)
290	ospf_db_summary_add (nbr, lsa);
291      LSDB_LOOP (OPAQUE_AREA_LSDB (area), rn, lsa)
292	ospf_db_summary_add (nbr, lsa);
293    }
294#endif /* HAVE_OPAQUE_LSA */
295
296  if (CHECK_FLAG (nbr->options, OSPF_OPTION_NP))
297    {
298      LSDB_LOOP (NSSA_LSDB (area), rn, lsa)
299	ospf_db_summary_add (nbr, lsa);
300    }
301
302  if (nbr->oi->type != OSPF_IFTYPE_VIRTUALLINK
303      && area->external_routing == OSPF_AREA_DEFAULT)
304    LSDB_LOOP (EXTERNAL_LSDB (nbr->oi->ospf), rn, lsa)
305      ospf_db_summary_add (nbr, lsa);
306
307#ifdef HAVE_OPAQUE_LSA
308  if (CHECK_FLAG (nbr->options, OSPF_OPTION_O)
309      && (nbr->oi->type != OSPF_IFTYPE_VIRTUALLINK
310	  && area->external_routing == OSPF_AREA_DEFAULT))
311    LSDB_LOOP (OPAQUE_AS_LSDB (nbr->oi->ospf), rn, lsa)
312      ospf_db_summary_add (nbr, lsa);
313#endif /* HAVE_OPAQUE_LSA */
314
315  return 0;
316}
317
318static int
319nsm_exchange_done (struct ospf_neighbor *nbr)
320{
321  if (ospf_ls_request_isempty (nbr))
322    return NSM_Full;
323
324  /* Send Link State Request. */
325  ospf_ls_req_send (nbr);
326
327  return NSM_Loading;
328}
329
330static int
331nsm_adj_ok (struct ospf_neighbor *nbr)
332{
333  int next_state = nbr->state;
334  int adj = nsm_should_adj (nbr);
335
336  if (nbr->state == NSM_TwoWay && adj == 1)
337    next_state = NSM_ExStart;
338  else if (nbr->state >= NSM_ExStart && adj == 0)
339    next_state = NSM_TwoWay;
340
341  return next_state;
342}
343
344/* Clear adjacency related state for a neighbour, intended where nbr
345 * transitions from > ExStart (i.e. a Full or forming adjacency)
346 * to <= ExStart.
347 */
348static void
349nsm_clear_adj (struct ospf_neighbor *nbr)
350{
351  /* Clear Database Summary list. */
352  if (!ospf_db_summary_isempty (nbr))
353    ospf_db_summary_clear (nbr);
354
355  /* Clear Link State Request list. */
356  if (!ospf_ls_request_isempty (nbr))
357    ospf_ls_request_delete_all (nbr);
358
359  /* Clear Link State Retransmission list. */
360  if (!ospf_ls_retransmit_isempty (nbr))
361    ospf_ls_retransmit_clear (nbr);
362
363#ifdef HAVE_OPAQUE_LSA
364  if (CHECK_FLAG (nbr->options, OSPF_OPTION_O))
365    UNSET_FLAG (nbr->options, OSPF_OPTION_O);
366#endif /* HAVE_OPAQUE_LSA */
367}
368
369static int
370nsm_kill_nbr (struct ospf_neighbor *nbr)
371{
372  /* killing nbr_self is invalid */
373  if (nbr == nbr->oi->nbr_self)
374    {
375      assert (nbr != nbr->oi->nbr_self);
376      return 0;
377    }
378
379  if (nbr->oi->type == OSPF_IFTYPE_NBMA && nbr->nbr_nbma != NULL)
380    {
381      struct ospf_nbr_nbma *nbr_nbma = nbr->nbr_nbma;
382
383      nbr_nbma->nbr = NULL;
384      nbr_nbma->state_change = nbr->state_change;
385
386      nbr->nbr_nbma = NULL;
387
388      OSPF_POLL_TIMER_ON (nbr_nbma->t_poll, ospf_poll_timer,
389			  nbr_nbma->v_poll);
390
391      if (IS_DEBUG_OSPF (nsm, NSM_EVENTS))
392	zlog_debug ("NSM[%s:%s]: Down (PollIntervalTimer scheduled)",
393		   IF_NAME (nbr->oi), inet_ntoa (nbr->address.u.prefix4));
394    }
395
396  return 0;
397}
398
399/* Neighbor State Machine */
400struct {
401  int (*func) (struct ospf_neighbor *);
402  int next_state;
403} NSM [OSPF_NSM_STATE_MAX][OSPF_NSM_EVENT_MAX] =
404{
405  {
406    /* DependUpon: dummy state. */
407    { NULL,                    NSM_DependUpon }, /* NoEvent           */
408    { NULL,                    NSM_DependUpon }, /* PacketReceived    */
409    { NULL,                    NSM_DependUpon }, /* Start             */
410    { NULL,                    NSM_DependUpon }, /* 2-WayReceived     */
411    { NULL,                    NSM_DependUpon }, /* NegotiationDone   */
412    { NULL,                    NSM_DependUpon }, /* ExchangeDone      */
413    { NULL,                    NSM_DependUpon }, /* BadLSReq          */
414    { NULL,                    NSM_DependUpon }, /* LoadingDone       */
415    { NULL,                    NSM_DependUpon }, /* AdjOK?            */
416    { NULL,                    NSM_DependUpon }, /* SeqNumberMismatch */
417    { NULL,                    NSM_DependUpon }, /* 1-WayReceived     */
418    { NULL,                    NSM_DependUpon }, /* KillNbr           */
419    { NULL,                    NSM_DependUpon }, /* InactivityTimer   */
420    { NULL,                    NSM_DependUpon }, /* LLDown            */
421  },
422  {
423    /* Deleted: dummy state. */
424    { NULL,                    NSM_Deleted    }, /* NoEvent           */
425    { NULL,                    NSM_Deleted    }, /* PacketReceived    */
426    { NULL,                    NSM_Deleted    }, /* Start             */
427    { NULL,                    NSM_Deleted    }, /* 2-WayReceived     */
428    { NULL,                    NSM_Deleted    }, /* NegotiationDone   */
429    { NULL,                    NSM_Deleted    }, /* ExchangeDone      */
430    { NULL,                    NSM_Deleted    }, /* BadLSReq          */
431    { NULL,                    NSM_Deleted    }, /* LoadingDone       */
432    { NULL,                    NSM_Deleted    }, /* AdjOK?            */
433    { NULL,                    NSM_Deleted    }, /* SeqNumberMismatch */
434    { NULL,                    NSM_Deleted    }, /* 1-WayReceived     */
435    { NULL,                    NSM_Deleted    }, /* KillNbr           */
436    { NULL,                    NSM_Deleted    }, /* InactivityTimer   */
437    { NULL,                    NSM_Deleted    }, /* LLDown            */
438  },
439  {
440    /* Down: */
441    { NULL,                    NSM_DependUpon }, /* NoEvent           */
442    { nsm_packet_received,     NSM_Init       }, /* PacketReceived    */
443    { nsm_start,               NSM_Attempt    }, /* Start             */
444    { NULL,                    NSM_Down       }, /* 2-WayReceived     */
445    { NULL,                    NSM_Down       }, /* NegotiationDone   */
446    { NULL,                    NSM_Down       }, /* ExchangeDone      */
447    { NULL,                    NSM_Down       }, /* BadLSReq          */
448    { NULL,                    NSM_Down       }, /* LoadingDone       */
449    { NULL,                    NSM_Down       }, /* AdjOK?            */
450    { NULL,                    NSM_Down       }, /* SeqNumberMismatch */
451    { NULL,                    NSM_Down       }, /* 1-WayReceived     */
452    { nsm_kill_nbr,            NSM_Deleted    }, /* KillNbr           */
453    { nsm_kill_nbr,            NSM_Deleted    }, /* InactivityTimer   */
454    { nsm_kill_nbr,            NSM_Deleted    }, /* LLDown            */
455  },
456  {
457    /* Attempt: */
458    { NULL,                    NSM_DependUpon }, /* NoEvent           */
459    { nsm_packet_received,     NSM_Init       }, /* PacketReceived    */
460    { NULL,                    NSM_Attempt    }, /* Start             */
461    { NULL,                    NSM_Attempt    }, /* 2-WayReceived     */
462    { NULL,                    NSM_Attempt    }, /* NegotiationDone   */
463    { NULL,                    NSM_Attempt    }, /* ExchangeDone      */
464    { NULL,                    NSM_Attempt    }, /* BadLSReq          */
465    { NULL,                    NSM_Attempt    }, /* LoadingDone       */
466    { NULL,                    NSM_Attempt    }, /* AdjOK?            */
467    { NULL,                    NSM_Attempt    }, /* SeqNumberMismatch */
468    { NULL,                    NSM_Attempt    }, /* 1-WayReceived     */
469    { nsm_kill_nbr,            NSM_Deleted    }, /* KillNbr           */
470    { nsm_kill_nbr,            NSM_Deleted    }, /* InactivityTimer   */
471    { nsm_kill_nbr,            NSM_Deleted    }, /* LLDown            */
472  },
473  {
474    /* Init: */
475    { NULL,                    NSM_DependUpon }, /* NoEvent           */
476    { nsm_packet_received,     NSM_Init      }, /* PacketReceived    */
477    { NULL,                    NSM_Init       }, /* Start             */
478    { nsm_twoway_received,     NSM_DependUpon }, /* 2-WayReceived     */
479    { NULL,                    NSM_Init       }, /* NegotiationDone   */
480    { NULL,                    NSM_Init       }, /* ExchangeDone      */
481    { NULL,                    NSM_Init       }, /* BadLSReq          */
482    { NULL,                    NSM_Init       }, /* LoadingDone       */
483    { NULL,                    NSM_Init       }, /* AdjOK?            */
484    { NULL,                    NSM_Init       }, /* SeqNumberMismatch */
485    { NULL,                    NSM_Init       }, /* 1-WayReceived     */
486    { nsm_kill_nbr,            NSM_Deleted    }, /* KillNbr           */
487    { nsm_kill_nbr,            NSM_Deleted    }, /* InactivityTimer   */
488    { nsm_kill_nbr,            NSM_Deleted    }, /* LLDown            */
489  },
490  {
491    /* 2-Way: */
492    { NULL,                    NSM_DependUpon }, /* NoEvent           */
493    { nsm_packet_received,     NSM_TwoWay     }, /* HelloReceived     */
494    { NULL,                    NSM_TwoWay     }, /* Start             */
495    { NULL,                    NSM_TwoWay     }, /* 2-WayReceived     */
496    { NULL,                    NSM_TwoWay     }, /* NegotiationDone   */
497    { NULL,                    NSM_TwoWay     }, /* ExchangeDone      */
498    { NULL,                    NSM_TwoWay     }, /* BadLSReq          */
499    { NULL,                    NSM_TwoWay     }, /* LoadingDone       */
500    { nsm_adj_ok,              NSM_DependUpon }, /* AdjOK?            */
501    { NULL,                    NSM_TwoWay     }, /* SeqNumberMismatch */
502    { NULL,                    NSM_Init       }, /* 1-WayReceived     */
503    { nsm_kill_nbr,            NSM_Deleted    }, /* KillNbr           */
504    { nsm_kill_nbr,            NSM_Deleted    }, /* InactivityTimer   */
505    { nsm_kill_nbr,            NSM_Deleted    }, /* LLDown            */
506  },
507  {
508    /* ExStart: */
509    { NULL,                    NSM_DependUpon }, /* NoEvent           */
510    { nsm_packet_received,     NSM_ExStart    }, /* PacaketReceived   */
511    { NULL,                    NSM_ExStart    }, /* Start             */
512    { NULL,                    NSM_ExStart    }, /* 2-WayReceived     */
513    { nsm_negotiation_done,    NSM_Exchange   }, /* NegotiationDone   */
514    { NULL,                    NSM_ExStart    }, /* ExchangeDone      */
515    { NULL,                    NSM_ExStart    }, /* BadLSReq          */
516    { NULL,                    NSM_ExStart    }, /* LoadingDone       */
517    { nsm_adj_ok,              NSM_DependUpon }, /* AdjOK?            */
518    { NULL,                    NSM_ExStart    }, /* SeqNumberMismatch */
519    { NULL,                    NSM_Init       }, /* 1-WayReceived     */
520    { nsm_kill_nbr,            NSM_Deleted    }, /* KillNbr           */
521    { nsm_kill_nbr,            NSM_Deleted    }, /* InactivityTimer   */
522    { nsm_kill_nbr,            NSM_Deleted    }, /* LLDown            */
523  },
524  {
525    /* Exchange: */
526    { NULL,                    NSM_DependUpon }, /* NoEvent           */
527    { nsm_packet_received,     NSM_Exchange   }, /* PacketReceived    */
528    { NULL,                    NSM_Exchange   }, /* Start             */
529    { NULL,                    NSM_Exchange   }, /* 2-WayReceived     */
530    { NULL,                    NSM_Exchange   }, /* NegotiationDone   */
531    { nsm_exchange_done,       NSM_DependUpon }, /* ExchangeDone      */
532    { NULL,                    NSM_ExStart    }, /* BadLSReq          */
533    { NULL,                    NSM_Exchange   }, /* LoadingDone       */
534    { nsm_adj_ok,              NSM_DependUpon }, /* AdjOK?            */
535    { NULL,                    NSM_ExStart    }, /* SeqNumberMismatch */
536    { NULL,                    NSM_Init       }, /* 1-WayReceived     */
537    { nsm_kill_nbr,            NSM_Deleted    }, /* KillNbr           */
538    { nsm_kill_nbr,            NSM_Deleted    }, /* InactivityTimer   */
539    { nsm_kill_nbr,            NSM_Deleted    }, /* LLDown            */
540  },
541  {
542    /* Loading: */
543    { NULL,                    NSM_DependUpon }, /* NoEvent           */
544    { nsm_packet_received,     NSM_Loading    }, /* PacketReceived    */
545    { NULL,                    NSM_Loading    }, /* Start             */
546    { NULL,                    NSM_Loading    }, /* 2-WayReceived     */
547    { NULL,                    NSM_Loading    }, /* NegotiationDone   */
548    { NULL,                    NSM_Loading    }, /* ExchangeDone      */
549    { NULL,                    NSM_ExStart    }, /* BadLSReq          */
550    { NULL,                    NSM_Full       }, /* LoadingDone       */
551    { nsm_adj_ok,              NSM_DependUpon }, /* AdjOK?            */
552    { NULL,                    NSM_ExStart    }, /* SeqNumberMismatch */
553    { NULL,                    NSM_Init       }, /* 1-WayReceived     */
554    { nsm_kill_nbr,            NSM_Deleted    }, /* KillNbr           */
555    { nsm_kill_nbr,            NSM_Deleted    }, /* InactivityTimer   */
556    { nsm_kill_nbr,            NSM_Deleted    }, /* LLDown            */
557  },
558  { /* Full: */
559    { NULL,                    NSM_DependUpon }, /* NoEvent           */
560    { nsm_packet_received,     NSM_Full       }, /* PacketReceived    */
561    { NULL,                    NSM_Full       }, /* Start             */
562    { NULL,                    NSM_Full       }, /* 2-WayReceived     */
563    { NULL,                    NSM_Full       }, /* NegotiationDone   */
564    { NULL,                    NSM_Full       }, /* ExchangeDone      */
565    { NULL,                    NSM_ExStart    }, /* BadLSReq          */
566    { NULL,                    NSM_Full       }, /* LoadingDone       */
567    { nsm_adj_ok,              NSM_DependUpon }, /* AdjOK?            */
568    { NULL,                    NSM_ExStart    }, /* SeqNumberMismatch */
569    { NULL,                    NSM_Init       }, /* 1-WayReceived     */
570    { nsm_kill_nbr,            NSM_Deleted    }, /* KillNbr           */
571    { nsm_kill_nbr,            NSM_Deleted    }, /* InactivityTimer   */
572    { nsm_kill_nbr,            NSM_Deleted    }, /* LLDown            */
573  },
574};
575
576static const char *ospf_nsm_event_str[] =
577{
578  "NoEvent",
579  "PacketReceived",
580  "Start",
581  "2-WayReceived",
582  "NegotiationDone",
583  "ExchangeDone",
584  "BadLSReq",
585  "LoadingDone",
586  "AdjOK?",
587  "SeqNumberMismatch",
588  "1-WayReceived",
589  "KillNbr",
590  "InactivityTimer",
591  "LLDown",
592};
593
594static void
595nsm_notice_state_change (struct ospf_neighbor *nbr, int next_state, int event)
596{
597  /* Logging change of status. */
598  if (IS_DEBUG_OSPF (nsm, NSM_STATUS))
599    zlog_debug ("NSM[%s:%s]: State change %s -> %s (%s)",
600               IF_NAME (nbr->oi), inet_ntoa (nbr->router_id),
601               LOOKUP (ospf_nsm_state_msg, nbr->state),
602               LOOKUP (ospf_nsm_state_msg, next_state),
603               ospf_nsm_event_str [event]);
604
605  /* Optionally notify about adjacency changes */
606  if (CHECK_FLAG(nbr->oi->ospf->config, OSPF_LOG_ADJACENCY_CHANGES) &&
607      (CHECK_FLAG(nbr->oi->ospf->config, OSPF_LOG_ADJACENCY_DETAIL) ||
608       (next_state == NSM_Full) || (next_state < nbr->state)))
609    zlog_notice("AdjChg: Nbr %s on %s: %s -> %s (%s)",
610                inet_ntoa (nbr->router_id), IF_NAME (nbr->oi),
611                LOOKUP (ospf_nsm_state_msg, nbr->state),
612                LOOKUP (ospf_nsm_state_msg, next_state),
613                ospf_nsm_event_str [event]);
614
615  /* Advance in NSM */
616  if (next_state > nbr->state)
617    nbr->ts_last_progress = recent_relative_time ();
618  else /* regression in NSM */
619    {
620      nbr->ts_last_regress = recent_relative_time ();
621      nbr->last_regress_str = ospf_nsm_event_str [event];
622    }
623
624#ifdef HAVE_SNMP
625  /* Terminal state or regression */
626  if ((next_state == NSM_Full)
627      || (next_state == NSM_TwoWay)
628      || (next_state < nbr->state))
629    {
630      /* ospfVirtNbrStateChange */
631      if (nbr->oi->type == OSPF_IFTYPE_VIRTUALLINK)
632        ospfTrapVirtNbrStateChange(nbr);
633      /* ospfNbrStateChange trap  */
634      else
635        /* To/From FULL, only managed by DR */
636        if (((next_state != NSM_Full) && (nbr->state != NSM_Full))
637            || (nbr->oi->state == ISM_DR))
638          ospfTrapNbrStateChange(nbr);
639    }
640#endif
641}
642
643static void
644nsm_change_state (struct ospf_neighbor *nbr, int state)
645{
646  struct ospf_interface *oi = nbr->oi;
647  struct ospf_area *vl_area = NULL;
648  u_char old_state;
649  int x;
650  int force = 1;
651
652  /* Preserve old status. */
653  old_state = nbr->state;
654
655  /* Change to new status. */
656  nbr->state = state;
657
658  /* Statistics. */
659  nbr->state_change++;
660
661  if (oi->type == OSPF_IFTYPE_VIRTUALLINK)
662    vl_area = ospf_area_lookup_by_area_id (oi->ospf, oi->vl_data->vl_area_id);
663
664  /* Generate NeighborChange ISM event.
665   *
666   * In response to NeighborChange, DR election is rerun. The information
667   * from the election process is required by the router-lsa construction.
668   *
669   * Therefore, trigger the event prior to refreshing the LSAs. */
670  switch (oi->state) {
671  case ISM_DROther:
672  case ISM_Backup:
673  case ISM_DR:
674    if ((old_state < NSM_TwoWay && state >= NSM_TwoWay) ||
675        (old_state >= NSM_TwoWay && state < NSM_TwoWay))
676      OSPF_ISM_EVENT_EXECUTE (oi, ISM_NeighborChange);
677    break;
678  default:
679    /* ISM_PointToPoint -> ISM_Down, ISM_Loopback -> ISM_Down, etc. */
680    break;
681  }
682
683  /* One of the neighboring routers changes to/from the FULL state. */
684  if ((old_state != NSM_Full && state == NSM_Full) ||
685      (old_state == NSM_Full && state != NSM_Full))
686    {
687      if (state == NSM_Full)
688	{
689	  oi->full_nbrs++;
690	  oi->area->full_nbrs++;
691
692          ospf_check_abr_status (oi->ospf);
693
694	  if (oi->type == OSPF_IFTYPE_VIRTUALLINK && vl_area)
695            if (++vl_area->full_vls == 1)
696	      ospf_schedule_abr_task (oi->ospf);
697
698	  /* kevinm: refresh any redistributions */
699	  for (x = ZEBRA_ROUTE_SYSTEM; x < ZEBRA_ROUTE_MAX; x++)
700	    {
701	      if (x == ZEBRA_ROUTE_OSPF || x == ZEBRA_ROUTE_OSPF6)
702		continue;
703	      ospf_external_lsa_refresh_type (oi->ospf, x, force);
704	    }
705          /* XXX: Clearly some thing is wrong with refresh of external LSAs
706           * this added to hack around defaults not refreshing after a timer
707           * jump.
708           */
709          ospf_external_lsa_refresh_default (oi->ospf);
710	}
711      else
712	{
713	  oi->full_nbrs--;
714	  oi->area->full_nbrs--;
715
716          ospf_check_abr_status (oi->ospf);
717
718	  if (oi->type == OSPF_IFTYPE_VIRTUALLINK && vl_area)
719	    if (vl_area->full_vls > 0)
720	      if (--vl_area->full_vls == 0)
721		ospf_schedule_abr_task (oi->ospf);
722	}
723
724      zlog_info ("nsm_change_state(%s, %s -> %s): "
725		 "scheduling new router-LSA origination",
726		 inet_ntoa (nbr->router_id),
727		 LOOKUP(ospf_nsm_state_msg, old_state),
728		 LOOKUP(ospf_nsm_state_msg, state));
729
730      ospf_router_lsa_update_area (oi->area);
731
732      if (oi->type == OSPF_IFTYPE_VIRTUALLINK)
733	{
734	  struct ospf_area *vl_area =
735	    ospf_area_lookup_by_area_id (oi->ospf, oi->vl_data->vl_area_id);
736
737	  if (vl_area)
738	    ospf_router_lsa_update_area (vl_area);
739	}
740
741      /* Originate network-LSA. */
742      if (oi->state == ISM_DR)
743	{
744	  if (oi->network_lsa_self && oi->full_nbrs == 0)
745	    {
746	      ospf_lsa_flush_area (oi->network_lsa_self, oi->area);
747	      ospf_lsa_unlock (&oi->network_lsa_self);
748	      oi->network_lsa_self = NULL;
749	    }
750	  else
751	    ospf_network_lsa_update (oi);
752	}
753    }
754
755#ifdef HAVE_OPAQUE_LSA
756  ospf_opaque_nsm_change (nbr, old_state);
757#endif /* HAVE_OPAQUE_LSA */
758
759  /* State changes from > ExStart to <= ExStart should clear any Exchange
760   * or Full/LSA Update related lists and state.
761   * Potential causal events: BadLSReq, SeqNumberMismatch, AdjOK?
762   */
763  if ((old_state > NSM_ExStart) && (state <= NSM_ExStart))
764    nsm_clear_adj (nbr);
765
766  /* Start DD exchange protocol */
767  if (state == NSM_ExStart)
768    {
769      if (nbr->dd_seqnum == 0)
770	nbr->dd_seqnum = quagga_time (NULL);
771      else
772	nbr->dd_seqnum++;
773
774      nbr->dd_flags = OSPF_DD_FLAG_I|OSPF_DD_FLAG_M|OSPF_DD_FLAG_MS;
775      ospf_db_desc_send (nbr);
776    }
777
778  /* clear cryptographic sequence number */
779  if (state == NSM_Down)
780    nbr->crypt_seqnum = 0;
781
782  /* Preserve old status? */
783}
784
785/* Execute NSM event process. */
786int
787ospf_nsm_event (struct thread *thread)
788{
789  int event;
790  int next_state;
791  struct ospf_neighbor *nbr;
792
793  nbr = THREAD_ARG (thread);
794  event = THREAD_VAL (thread);
795
796  if (IS_DEBUG_OSPF (nsm, NSM_EVENTS))
797    zlog_debug ("NSM[%s:%s]: %s (%s)", IF_NAME (nbr->oi),
798	       inet_ntoa (nbr->router_id),
799	       LOOKUP (ospf_nsm_state_msg, nbr->state),
800	       ospf_nsm_event_str [event]);
801
802  next_state = NSM [nbr->state][event].next_state;
803
804  /* Call function. */
805  if (NSM [nbr->state][event].func != NULL)
806    {
807      int func_state = (*(NSM [nbr->state][event].func))(nbr);
808
809      if (NSM [nbr->state][event].next_state == NSM_DependUpon)
810        next_state = func_state;
811      else if (func_state)
812        {
813          /* There's a mismatch between the FSM tables and what an FSM
814           * action/state-change function returned. State changes which
815           * do not have conditional/DependUpon next-states should not
816           * try set next_state.
817           */
818          zlog_warn ("NSM[%s:%s]: %s (%s): "
819                     "Warning: action tried to change next_state to %s",
820                     IF_NAME (nbr->oi), inet_ntoa (nbr->router_id),
821                     LOOKUP (ospf_nsm_state_msg, nbr->state),
822                     ospf_nsm_event_str [event],
823                     LOOKUP (ospf_nsm_state_msg, func_state));
824        }
825    }
826
827  assert (next_state != NSM_DependUpon);
828
829  /* If state is changed. */
830  if (next_state != nbr->state)
831    {
832      nsm_notice_state_change (nbr, next_state, event);
833      nsm_change_state (nbr, next_state);
834    }
835
836  /* Make sure timer is set. */
837  nsm_timer_set (nbr);
838
839  /* When event is NSM_KillNbr, InactivityTimer or LLDown, the neighbor
840   * is deleted.
841   *
842   * Rather than encode knowledge here of which events lead to NBR
843   * delete, we take our cue from the NSM table, via the dummy
844   * 'Deleted' neighbour state.
845   */
846  if (nbr->state == NSM_Deleted)
847    ospf_nbr_delete (nbr);
848
849  return 0;
850}
851
852/* Check loading state. */
853void
854ospf_check_nbr_loading (struct ospf_neighbor *nbr)
855{
856  if (nbr->state == NSM_Loading)
857    {
858      if (ospf_ls_request_isempty (nbr))
859	OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_LoadingDone);
860      else if (nbr->ls_req_last == NULL)
861	ospf_ls_req_event (nbr);
862    }
863}
864