Coverage Report

Created: 2025-07-14 06:48

/src/frr/ospfd/ospf_ism.c
Line
Count
Source (jump to first uncovered line)
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
 * OSPF version 2  Interface State Machine
4
 *   From RFC2328 [OSPF Version 2]
5
 * Copyright (C) 1999, 2000 Toshiaki Takada
6
 */
7
8
#include <zebra.h>
9
10
#include "frrevent.h"
11
#include "linklist.h"
12
#include "prefix.h"
13
#include "if.h"
14
#include "table.h"
15
#include "log.h"
16
17
#include "ospfd/ospfd.h"
18
#include "ospfd/ospf_interface.h"
19
#include "ospfd/ospf_ism.h"
20
#include "ospfd/ospf_asbr.h"
21
#include "ospfd/ospf_lsa.h"
22
#include "ospfd/ospf_lsdb.h"
23
#include "ospfd/ospf_neighbor.h"
24
#include "ospfd/ospf_nsm.h"
25
#include "ospfd/ospf_network.h"
26
#include "ospfd/ospf_dump.h"
27
#include "ospfd/ospf_packet.h"
28
#include "ospfd/ospf_flood.h"
29
#include "ospfd/ospf_abr.h"
30
31
DEFINE_HOOK(ospf_ism_change,
32
      (struct ospf_interface * oi, int state, int oldstate),
33
      (oi, state, oldstate));
34
35
/* elect DR and BDR. Refer to RFC2319 section 9.4 */
36
static struct ospf_neighbor *ospf_dr_election_sub(struct list *routers)
37
0
{
38
0
  struct listnode *node;
39
0
  struct ospf_neighbor *nbr, *max = NULL;
40
41
  /* Choose highest router priority.
42
     In case of tie, choose highest Router ID. */
43
0
  for (ALL_LIST_ELEMENTS_RO(routers, node, nbr)) {
44
0
    if (max == NULL)
45
0
      max = nbr;
46
0
    else {
47
0
      if (max->priority < nbr->priority)
48
0
        max = nbr;
49
0
      else if (max->priority == nbr->priority)
50
0
        if (IPV4_ADDR_CMP(&max->router_id,
51
0
              &nbr->router_id)
52
0
            < 0)
53
0
          max = nbr;
54
0
    }
55
0
  }
56
57
0
  return max;
58
0
}
59
60
static struct ospf_neighbor *ospf_elect_dr(struct ospf_interface *oi,
61
             struct list *el_list)
62
0
{
63
0
  struct list *dr_list;
64
0
  struct listnode *node;
65
0
  struct ospf_neighbor *nbr, *dr = NULL, *bdr = NULL;
66
67
0
  dr_list = list_new();
68
69
  /* Add neighbors to the list. */
70
0
  for (ALL_LIST_ELEMENTS_RO(el_list, node, nbr)) {
71
    /* neighbor declared to be DR. */
72
0
    if (NBR_IS_DR(nbr))
73
0
      listnode_add(dr_list, nbr);
74
75
    /* Preserve neighbor BDR. */
76
0
    if (IPV4_ADDR_SAME(&BDR(oi), &nbr->address.u.prefix4))
77
0
      bdr = nbr;
78
0
  }
79
80
  /* Elect Designated Router. */
81
0
  if (listcount(dr_list) > 0)
82
0
    dr = ospf_dr_election_sub(dr_list);
83
0
  else
84
0
    dr = bdr;
85
86
  /* Set DR to interface. */
87
0
  if (dr)
88
0
    DR(oi) = dr->address.u.prefix4;
89
0
  else
90
0
    DR(oi).s_addr = 0;
91
92
0
  list_delete(&dr_list);
93
94
0
  return dr;
95
0
}
96
97
static struct ospf_neighbor *ospf_elect_bdr(struct ospf_interface *oi,
98
              struct list *el_list)
99
0
{
100
0
  struct list *bdr_list, *no_dr_list;
101
0
  struct listnode *node;
102
0
  struct ospf_neighbor *nbr, *bdr = NULL;
103
104
0
  bdr_list = list_new();
105
0
  no_dr_list = list_new();
106
107
  /* Add neighbors to the list. */
108
0
  for (ALL_LIST_ELEMENTS_RO(el_list, node, nbr)) {
109
    /* neighbor declared to be DR. */
110
0
    if (NBR_IS_DR(nbr))
111
0
      continue;
112
113
    /* neighbor declared to be BDR. */
114
0
    if (NBR_IS_BDR(nbr))
115
0
      listnode_add(bdr_list, nbr);
116
117
0
    listnode_add(no_dr_list, nbr);
118
0
  }
119
120
  /* Elect Backup Designated Router. */
121
0
  if (listcount(bdr_list) > 0)
122
0
    bdr = ospf_dr_election_sub(bdr_list);
123
0
  else
124
0
    bdr = ospf_dr_election_sub(no_dr_list);
125
126
  /* Set BDR to interface. */
127
0
  if (bdr)
128
0
    BDR(oi) = bdr->address.u.prefix4;
129
0
  else
130
0
    BDR(oi).s_addr = 0;
131
132
0
  list_delete(&bdr_list);
133
0
  list_delete(&no_dr_list);
134
135
0
  return bdr;
136
0
}
137
138
static int ospf_ism_state(struct ospf_interface *oi)
139
0
{
140
0
  if (IPV4_ADDR_SAME(&DR(oi), &oi->address->u.prefix4))
141
0
    return ISM_DR;
142
0
  else if (IPV4_ADDR_SAME(&BDR(oi), &oi->address->u.prefix4))
143
0
    return ISM_Backup;
144
0
  else
145
0
    return ISM_DROther;
146
0
}
147
148
static void ospf_dr_eligible_routers(struct route_table *nbrs,
149
             struct list *el_list)
150
0
{
151
0
  struct route_node *rn;
152
0
  struct ospf_neighbor *nbr;
153
154
0
  for (rn = route_top(nbrs); rn; rn = route_next(rn))
155
0
    if ((nbr = rn->info) != NULL)
156
      /* Ignore 0.0.0.0 node*/
157
0
      if (nbr->router_id.s_addr != INADDR_ANY)
158
        /* Is neighbor eligible? */
159
0
        if (nbr->priority > 0)
160
          /* Is neighbor upper 2-Way? */
161
0
          if (nbr->state >= NSM_TwoWay)
162
0
            listnode_add(el_list, nbr);
163
0
}
164
165
/* Generate AdjOK? NSM event. */
166
static void ospf_dr_change(struct ospf *ospf, struct route_table *nbrs)
167
0
{
168
0
  struct route_node *rn;
169
0
  struct ospf_neighbor *nbr;
170
171
0
  for (rn = route_top(nbrs); rn; rn = route_next(rn)) {
172
0
    nbr = rn->info;
173
174
0
    if (!nbr)
175
0
      continue;
176
177
    /*
178
     * Ignore 0.0.0.0 node
179
     * Is neighbor 2-Way?
180
     * Ignore myself
181
     */
182
0
    if (nbr->router_id.s_addr != INADDR_ANY
183
0
        && nbr->state >= NSM_TwoWay
184
0
        && !IPV4_ADDR_SAME(&nbr->router_id, &ospf->router_id))
185
0
      OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_AdjOK);
186
0
  }
187
0
}
188
189
int ospf_dr_election(struct ospf_interface *oi)
190
0
{
191
0
  struct in_addr old_dr, old_bdr;
192
0
  int old_state, new_state;
193
0
  struct list *el_list;
194
195
  /* backup current values. */
196
0
  old_dr = DR(oi);
197
0
  old_bdr = BDR(oi);
198
0
  old_state = oi->state;
199
200
0
  el_list = list_new();
201
202
  /* List eligible routers. */
203
0
  ospf_dr_eligible_routers(oi->nbrs, el_list);
204
205
  /* First election of DR and BDR. */
206
0
  ospf_elect_bdr(oi, el_list);
207
0
  ospf_elect_dr(oi, el_list);
208
209
0
  new_state = ospf_ism_state(oi);
210
211
0
  if (IS_DEBUG_OSPF(ism, ISM_STATUS)) {
212
0
    zlog_debug("DR-Election[1st]: Backup %pI4", &BDR(oi));
213
0
    zlog_debug("DR-Election[1st]: DR     %pI4", &DR(oi));
214
0
  }
215
216
0
  if (new_state != old_state
217
0
      && !(new_state == ISM_DROther && old_state < ISM_DROther)) {
218
0
    ospf_elect_bdr(oi, el_list);
219
0
    ospf_elect_dr(oi, el_list);
220
221
0
    new_state = ospf_ism_state(oi);
222
223
0
    if (IS_DEBUG_OSPF(ism, ISM_STATUS)) {
224
0
      zlog_debug("DR-Election[2nd]: Backup %pI4", &BDR(oi));
225
0
      zlog_debug("DR-Election[2nd]: DR     %pI4", &DR(oi));
226
0
    }
227
0
  }
228
229
0
  list_delete(&el_list);
230
231
  /* if DR or BDR changes, cause AdjOK? neighbor event. */
232
0
  if (!IPV4_ADDR_SAME(&old_dr, &DR(oi))
233
0
      || !IPV4_ADDR_SAME(&old_bdr, &BDR(oi)))
234
0
    ospf_dr_change(oi->ospf, oi->nbrs);
235
236
0
  return new_state;
237
0
}
238
239
240
void ospf_hello_timer(struct event *thread)
241
0
{
242
0
  struct ospf_interface *oi;
243
244
0
  oi = EVENT_ARG(thread);
245
0
  oi->t_hello = NULL;
246
247
  /* Check if the GR hello-delay is active. */
248
0
  if (oi->gr.hello_delay.t_grace_send)
249
0
    return;
250
251
0
  if (IS_DEBUG_OSPF(ism, ISM_TIMERS))
252
0
    zlog_debug("ISM[%s]: Timer (Hello timer expire)", IF_NAME(oi));
253
254
  /* Sending hello packet. */
255
0
  ospf_hello_send(oi);
256
257
  /* Hello timer set. */
258
0
  OSPF_HELLO_TIMER_ON(oi);
259
0
}
260
261
static void ospf_wait_timer(struct event *thread)
262
0
{
263
0
  struct ospf_interface *oi;
264
0
265
0
  oi = EVENT_ARG(thread);
266
0
  oi->t_wait = NULL;
267
0
268
0
  if (IS_DEBUG_OSPF(ism, ISM_TIMERS))
269
0
    zlog_debug("ISM[%s]: Timer (Wait timer expire)", IF_NAME(oi));
270
0
271
0
  OSPF_ISM_EVENT_SCHEDULE(oi, ISM_WaitTimer);
272
0
}
273
274
/* Hook function called after ospf ISM event is occurred. And vty's
275
   network command invoke this function after making interface
276
   structure. */
277
static void ism_timer_set(struct ospf_interface *oi)
278
0
{
279
0
  switch (oi->state) {
280
0
  case ISM_Down:
281
    /* First entry point of ospf interface state machine. In this
282
       state
283
       interface parameters must be set to initial values, and
284
       timers are
285
       reset also. */
286
0
    EVENT_OFF(oi->t_hello);
287
0
    EVENT_OFF(oi->t_wait);
288
0
    EVENT_OFF(oi->t_ls_ack);
289
0
    EVENT_OFF(oi->gr.hello_delay.t_grace_send);
290
0
    break;
291
0
  case ISM_Loopback:
292
    /* In this state, the interface may be looped back and will be
293
       unavailable for regular data traffic. */
294
0
    EVENT_OFF(oi->t_hello);
295
0
    EVENT_OFF(oi->t_wait);
296
0
    EVENT_OFF(oi->t_ls_ack);
297
0
    EVENT_OFF(oi->gr.hello_delay.t_grace_send);
298
0
    break;
299
0
  case ISM_Waiting:
300
    /* The router is trying to determine the identity of DRouter and
301
       BDRouter. The router begin to receive and send Hello Packets.
302
       */
303
    /* send first hello immediately */
304
0
    OSPF_ISM_TIMER_MSEC_ON(oi->t_hello, ospf_hello_timer, 1);
305
0
    OSPF_ISM_TIMER_ON(oi->t_wait, ospf_wait_timer,
306
0
          OSPF_IF_PARAM(oi, v_wait));
307
0
    EVENT_OFF(oi->t_ls_ack);
308
0
    break;
309
0
  case ISM_PointToPoint:
310
    /* The interface connects to a physical Point-to-point network
311
       or
312
       virtual link. The router attempts to form an adjacency with
313
       neighboring router. Hello packets are also sent. */
314
    /* send first hello immediately */
315
0
    OSPF_ISM_TIMER_MSEC_ON(oi->t_hello, ospf_hello_timer, 1);
316
0
    EVENT_OFF(oi->t_wait);
317
0
    OSPF_ISM_TIMER_ON(oi->t_ls_ack, ospf_ls_ack_timer,
318
0
          oi->v_ls_ack);
319
0
    break;
320
0
  case ISM_DROther:
321
    /* The network type of the interface is broadcast or NBMA
322
       network,
323
       and the router itself is neither Designated Router nor
324
       Backup Designated Router. */
325
0
    OSPF_HELLO_TIMER_ON(oi);
326
0
    EVENT_OFF(oi->t_wait);
327
0
    OSPF_ISM_TIMER_ON(oi->t_ls_ack, ospf_ls_ack_timer,
328
0
          oi->v_ls_ack);
329
0
    break;
330
0
  case ISM_Backup:
331
    /* The network type of the interface is broadcast os NBMA
332
       network,
333
       and the router is Backup Designated Router. */
334
0
    OSPF_HELLO_TIMER_ON(oi);
335
0
    EVENT_OFF(oi->t_wait);
336
0
    OSPF_ISM_TIMER_ON(oi->t_ls_ack, ospf_ls_ack_timer,
337
0
          oi->v_ls_ack);
338
0
    break;
339
0
  case ISM_DR:
340
    /* The network type of the interface is broadcast or NBMA
341
       network,
342
       and the router is Designated Router. */
343
0
    OSPF_HELLO_TIMER_ON(oi);
344
0
    EVENT_OFF(oi->t_wait);
345
0
    OSPF_ISM_TIMER_ON(oi->t_ls_ack, ospf_ls_ack_timer,
346
0
          oi->v_ls_ack);
347
0
    break;
348
0
  }
349
0
}
350
351
static int ism_interface_up(struct ospf_interface *oi)
352
0
{
353
0
  int next_state = 0;
354
355
  /* if network type is point-to-point, Point-to-MultiPoint or virtual
356
     link,
357
     the state transitions to Point-to-Point. */
358
0
  if (oi->type == OSPF_IFTYPE_POINTOPOINT
359
0
      || oi->type == OSPF_IFTYPE_POINTOMULTIPOINT
360
0
      || oi->type == OSPF_IFTYPE_VIRTUALLINK)
361
0
    next_state = ISM_PointToPoint;
362
  /* Else if the router is not eligible to DR, the state transitions to
363
     DROther. */
364
0
  else if (PRIORITY(oi) == 0) /* router is eligible? */
365
0
    next_state = ISM_DROther;
366
0
  else
367
    /* Otherwise, the state transitions to Waiting. */
368
0
    next_state = ISM_Waiting;
369
370
0
  if (oi->type == OSPF_IFTYPE_NBMA)
371
0
    ospf_nbr_nbma_if_update(oi->ospf, oi);
372
373
  /*  ospf_ism_event (t); */
374
0
  return next_state;
375
0
}
376
377
static int ism_loop_ind(struct ospf_interface *oi)
378
0
{
379
  /* call ism_interface_down. */
380
  /* ret = ism_interface_down (oi); */
381
382
0
  return 0;
383
0
}
384
385
/* Interface down event handler. */
386
static int ism_interface_down(struct ospf_interface *oi)
387
0
{
388
0
  ospf_if_cleanup(oi);
389
0
  return 0;
390
0
}
391
392
393
static int ism_backup_seen(struct ospf_interface *oi)
394
0
{
395
0
  return ospf_dr_election(oi);
396
0
}
397
398
static int ism_wait_timer(struct ospf_interface *oi)
399
0
{
400
0
  return ospf_dr_election(oi);
401
0
}
402
403
static int ism_neighbor_change(struct ospf_interface *oi)
404
0
{
405
0
  return ospf_dr_election(oi);
406
0
}
407
408
static int ism_ignore(struct ospf_interface *oi)
409
0
{
410
0
  if (IS_DEBUG_OSPF(ism, ISM_EVENTS))
411
0
    zlog_debug("ISM[%s]: ism_ignore called", IF_NAME(oi));
412
413
0
  return 0;
414
0
}
415
416
/* Interface State Machine */
417
const struct {
418
  int (*func)(struct ospf_interface *);
419
  int next_state;
420
} ISM[OSPF_ISM_STATE_MAX][OSPF_ISM_EVENT_MAX] = {
421
  {
422
    /* DependUpon: dummy state. */
423
    {ism_ignore, ISM_DependUpon}, /* NoEvent        */
424
    {ism_ignore, ISM_DependUpon}, /* InterfaceUp    */
425
    {ism_ignore, ISM_DependUpon}, /* WaitTimer      */
426
    {ism_ignore, ISM_DependUpon}, /* BackupSeen     */
427
    {ism_ignore, ISM_DependUpon}, /* NeighborChange */
428
    {ism_ignore, ISM_DependUpon}, /* LoopInd        */
429
    {ism_ignore, ISM_DependUpon}, /* UnloopInd      */
430
    {ism_ignore, ISM_DependUpon}, /* InterfaceDown  */
431
  },
432
  {
433
    /* Down:*/
434
    {ism_ignore, ISM_DependUpon},       /* NoEvent        */
435
    {ism_interface_up, ISM_DependUpon}, /* InterfaceUp    */
436
    {ism_ignore, ISM_Down},       /* WaitTimer      */
437
    {ism_ignore, ISM_Down},       /* BackupSeen     */
438
    {ism_ignore, ISM_Down},       /* NeighborChange */
439
    {ism_loop_ind, ISM_Loopback},       /* LoopInd        */
440
    {ism_ignore, ISM_Down},       /* UnloopInd      */
441
    {ism_interface_down, ISM_Down},     /* InterfaceDown  */
442
  },
443
  {
444
    /* Loopback: */
445
    {ism_ignore, ISM_DependUpon},   /* NoEvent        */
446
    {ism_ignore, ISM_Loopback},     /* InterfaceUp    */
447
    {ism_ignore, ISM_Loopback},     /* WaitTimer      */
448
    {ism_ignore, ISM_Loopback},     /* BackupSeen     */
449
    {ism_ignore, ISM_Loopback},     /* NeighborChange */
450
    {ism_ignore, ISM_Loopback},     /* LoopInd        */
451
    {ism_ignore, ISM_Down},   /* UnloopInd      */
452
    {ism_interface_down, ISM_Down}, /* InterfaceDown  */
453
  },
454
  {
455
    /* Waiting: */
456
    {ism_ignore, ISM_DependUpon},      /* NoEvent        */
457
    {ism_ignore, ISM_Waiting},   /* InterfaceUp    */
458
    {ism_wait_timer, ISM_DependUpon},  /* WaitTimer      */
459
    {ism_backup_seen, ISM_DependUpon}, /* BackupSeen     */
460
    {ism_ignore, ISM_Waiting},   /* NeighborChange */
461
    {ism_loop_ind, ISM_Loopback},      /* LoopInd        */
462
    {ism_ignore, ISM_Waiting},   /* UnloopInd      */
463
    {ism_interface_down, ISM_Down},    /* InterfaceDown  */
464
  },
465
  {
466
    /* Point-to-Point: */
467
    {ism_ignore, ISM_DependUpon},   /* NoEvent        */
468
    {ism_ignore, ISM_PointToPoint}, /* InterfaceUp    */
469
    {ism_ignore, ISM_PointToPoint}, /* WaitTimer      */
470
    {ism_ignore, ISM_PointToPoint}, /* BackupSeen     */
471
    {ism_ignore, ISM_PointToPoint}, /* NeighborChange */
472
    {ism_loop_ind, ISM_Loopback},   /* LoopInd        */
473
    {ism_ignore, ISM_PointToPoint}, /* UnloopInd      */
474
    {ism_interface_down, ISM_Down}, /* InterfaceDown  */
475
  },
476
  {
477
    /* DROther: */
478
    {ism_ignore, ISM_DependUpon},   /* NoEvent        */
479
    {ism_ignore, ISM_DROther},       /* InterfaceUp    */
480
    {ism_ignore, ISM_DROther},       /* WaitTimer      */
481
    {ism_ignore, ISM_DROther},       /* BackupSeen     */
482
    {ism_neighbor_change, ISM_DependUpon}, /* NeighborChange */
483
    {ism_loop_ind, ISM_Loopback},   /* LoopInd        */
484
    {ism_ignore, ISM_DROther},       /* UnloopInd      */
485
    {ism_interface_down, ISM_Down}, /* InterfaceDown  */
486
  },
487
  {
488
    /* Backup: */
489
    {ism_ignore, ISM_DependUpon},   /* NoEvent        */
490
    {ism_ignore, ISM_Backup},       /* InterfaceUp    */
491
    {ism_ignore, ISM_Backup},       /* WaitTimer      */
492
    {ism_ignore, ISM_Backup},       /* BackupSeen     */
493
    {ism_neighbor_change, ISM_DependUpon}, /* NeighborChange */
494
    {ism_loop_ind, ISM_Loopback},   /* LoopInd        */
495
    {ism_ignore, ISM_Backup},       /* UnloopInd      */
496
    {ism_interface_down, ISM_Down}, /* InterfaceDown  */
497
  },
498
  {
499
    /* DR: */
500
    {ism_ignore, ISM_DependUpon},   /* NoEvent        */
501
    {ism_ignore, ISM_DR},          /* InterfaceUp    */
502
    {ism_ignore, ISM_DR},          /* WaitTimer      */
503
    {ism_ignore, ISM_DR},          /* BackupSeen     */
504
    {ism_neighbor_change, ISM_DependUpon}, /* NeighborChange */
505
    {ism_loop_ind, ISM_Loopback},   /* LoopInd        */
506
    {ism_ignore, ISM_DR},          /* UnloopInd      */
507
    {ism_interface_down, ISM_Down}, /* InterfaceDown  */
508
  },
509
};
510
511
static const char *const ospf_ism_event_str[] = {
512
  "NoEvent",  "InterfaceUp", "WaitTimer", "BackupSeen",
513
  "NeighborChange", "LoopInd",     "UnLoopInd", "InterfaceDown",
514
};
515
516
static void ism_change_state(struct ospf_interface *oi, int state)
517
0
{
518
0
  int old_state;
519
0
  struct ospf_lsa *lsa;
520
521
  /* Logging change of state. */
522
0
  if (IS_DEBUG_OSPF(ism, ISM_STATUS))
523
0
    zlog_debug("ISM[%s]: State change %s -> %s", IF_NAME(oi),
524
0
         lookup_msg(ospf_ism_state_msg, oi->state, NULL),
525
0
         lookup_msg(ospf_ism_state_msg, state, NULL));
526
527
0
  old_state = oi->state;
528
0
  oi->state = state;
529
0
  oi->state_change++;
530
531
0
  hook_call(ospf_ism_change, oi, state, old_state);
532
533
  /* Set multicast memberships appropriately for new state. */
534
0
  ospf_if_set_multicast(oi);
535
536
0
  if (old_state == ISM_Down || state == ISM_Down)
537
0
    ospf_check_abr_status(oi->ospf);
538
539
  /* Originate router-LSA. */
540
0
  if (state == ISM_Down) {
541
0
    if (oi->area->act_ints > 0)
542
0
      oi->area->act_ints--;
543
0
  } else if (old_state == ISM_Down)
544
0
    oi->area->act_ints++;
545
546
  /* schedule router-LSA originate. */
547
0
  ospf_router_lsa_update_area(oi->area);
548
549
  /* Originate network-LSA. */
550
0
  if (old_state != ISM_DR && state == ISM_DR)
551
0
    ospf_network_lsa_update(oi);
552
0
  else if (old_state == ISM_DR && state != ISM_DR) {
553
    /* Free self originated network LSA. */
554
0
    lsa = oi->network_lsa_self;
555
0
    if (lsa)
556
0
      ospf_lsa_flush_area(lsa, oi->area);
557
558
0
    ospf_lsa_unlock(&oi->network_lsa_self);
559
0
    oi->network_lsa_self = NULL;
560
0
  }
561
562
0
  ospf_opaque_ism_change(oi, old_state);
563
564
  /* Check area border status.  */
565
0
  ospf_check_abr_status(oi->ospf);
566
0
}
567
568
/* Execute ISM event process. */
569
void ospf_ism_event(struct event *thread)
570
0
{
571
0
  int event;
572
0
  int next_state;
573
0
  struct ospf_interface *oi;
574
575
0
  oi = EVENT_ARG(thread);
576
0
  event = EVENT_VAL(thread);
577
578
  /* Call function. */
579
0
  next_state = (*(ISM[oi->state][event].func))(oi);
580
581
0
  if (!next_state)
582
0
    next_state = ISM[oi->state][event].next_state;
583
584
0
  if (IS_DEBUG_OSPF(ism, ISM_EVENTS))
585
0
    zlog_debug("ISM[%s]: %s (%s)", IF_NAME(oi),
586
0
         lookup_msg(ospf_ism_state_msg, oi->state, NULL),
587
0
         ospf_ism_event_str[event]);
588
589
  /* If state is changed. */
590
0
  if (next_state != oi->state)
591
0
    ism_change_state(oi, next_state);
592
593
  /* Make sure timer is set. */
594
0
  ism_timer_set(oi);
595
0
}