Statistics
| Branch: | Revision:

iof-bird-daemon / proto / ospf / neighbor.c @ e81b440f

History | View | Annotate | Download (15.4 KB)

1
/*
2
 *        BIRD -- OSPF
3
 *
4
 *        (c) 1999 - 2004 Ondrej Filip <feela@network.cz>
5
 *
6
 *        Can be freely distributed and used under the terms of the GNU GPL.
7
 */
8

    
9
#include "ospf.h"
10

    
11
char *ospf_ns[] = { "    down",
12
  " attempt",
13
  "    init",
14
  "    2way",
15
  " exstart",
16
  "exchange",
17
  " loading",
18
  "    full"
19
};
20

    
21
const char *ospf_inm[] =
22
  { "hello received", "neighbor start", "2-way received",
23
  "negotiation done", "exstart done", "bad ls request", "load done",
24
  "adjacency ok?", "sequence mismatch", "1-way received", "kill neighbor",
25
  "inactivity timer", "line down"
26
};
27

    
28
static void neigh_chstate(struct ospf_neighbor *n, u8 state);
29
static struct ospf_neighbor *electbdr(list nl);
30
static struct ospf_neighbor *electdr(list nl);
31
static void neighbor_timer_hook(timer * timer);
32
static void rxmt_timer_hook(timer * timer);
33
static void ackd_timer_hook(timer * t);
34

    
35
static void
36
init_lists(struct ospf_neighbor *n)
37
{
38
  s_init_list(&(n->lsrql));
39
  n->lsrqh = ospf_top_new(n->pool);
40
  s_init(&(n->lsrqi), &(n->lsrql));
41

    
42
  s_init_list(&(n->lsrtl));
43
  n->lsrth = ospf_top_new(n->pool);
44
  s_init(&(n->lsrti), &(n->lsrtl));
45
}
46

    
47
/* Resets LSA request and retransmit lists.
48
 * We do not reset DB summary list iterator here, 
49
 * it is reset during entering EXCHANGE state.
50
 */
51
static void
52
reset_lists(struct ospf_neighbor *n)
53
{
54
  ospf_top_free(n->lsrqh);
55
  ospf_top_free(n->lsrth);
56
  init_lists(n);
57
}
58

    
59
struct ospf_neighbor *
60
ospf_neighbor_new(struct ospf_iface *ifa)
61
{
62
  struct proto *p = (struct proto *) (ifa->oa->po);
63
  struct proto_ospf *po = ifa->oa->po;
64
  struct pool *pool = rp_new(p->pool, "OSPF Neighbor");
65
  struct ospf_neighbor *n = mb_allocz(pool, sizeof(struct ospf_neighbor));
66

    
67
  n->pool = pool;
68
  n->ifa = ifa;
69
  add_tail(&ifa->neigh_list, NODE n);
70
  n->adj = 0;
71
  n->csn = 0;
72
  n->ldbdes = mb_allocz(pool, ifa->iface->mtu);
73
  n->state = NEIGHBOR_DOWN;
74

    
75
  init_lists(n);
76
  s_init(&(n->dbsi), &(po->lsal));
77

    
78
  n->inactim = tm_new(pool);
79
  n->inactim->data = n;
80
  n->inactim->randomize = 0;
81
  n->inactim->hook = neighbor_timer_hook;
82
  n->inactim->recurrent = 0;
83
  DBG("%s: Installing inactivity timer.\n", p->name);
84

    
85
  n->rxmt_timer = tm_new(pool);
86
  n->rxmt_timer->data = n;
87
  n->rxmt_timer->randomize = 0;
88
  n->rxmt_timer->hook = rxmt_timer_hook;
89
  n->rxmt_timer->recurrent = ifa->rxmtint;
90
  tm_start(n->rxmt_timer, n->ifa->rxmtint);
91
  DBG("%s: Installing rxmt timer.\n", p->name);
92

    
93
  n->ackd_timer = tm_new(pool);
94
  n->ackd_timer->data = n;
95
  n->ackd_timer->randomize = 0;
96
  n->ackd_timer->hook = ackd_timer_hook;
97
  n->ackd_timer->recurrent = ifa->rxmtint / 2;
98
  init_list(&n->ackl[ACKL_DIRECT]);
99
  init_list(&n->ackl[ACKL_DELAY]);
100
  tm_start(n->ackd_timer, n->ifa->rxmtint / 2);
101
  DBG("%s: Installing ackd timer.\n", p->name);
102

    
103
  return (n);
104
}
105

    
106
/**
107
 * neigh_chstate - handles changes related to new or lod state of neighbor
108
 * @n: OSPF neighbor
109
 * @state: new state
110
 *
111
 * Many actions have to be taken acording to a change of state of a neighbor. It
112
 * starts rxmt timers, call interface state machine etc.
113
 */
114

    
115
static void
116
neigh_chstate(struct ospf_neighbor *n, u8 state)
117
{
118
  u8 oldstate;
119

    
120
  oldstate = n->state;
121

    
122
  if (oldstate != state)
123
  {
124
    struct ospf_iface *ifa = n->ifa;
125
    struct proto_ospf *po = ifa->oa->po;
126
    struct proto *p = &po->proto;
127

    
128
    n->state = state;
129

    
130
    OSPF_TRACE(D_EVENTS, "Neighbor %I changes state from \"%s\" to \"%s\".",
131
               n->ip, ospf_ns[oldstate], ospf_ns[state]);
132

    
133
    if ((state == NEIGHBOR_2WAY) && (oldstate < NEIGHBOR_2WAY))
134
      ospf_iface_sm(ifa, ISM_NEICH);
135
    if ((state < NEIGHBOR_2WAY) && (oldstate >= NEIGHBOR_2WAY))
136
      ospf_iface_sm(ifa, ISM_NEICH);
137

    
138
    if (oldstate == NEIGHBOR_FULL)        /* Decrease number of adjacencies */
139
    {
140
      ifa->fadj--;
141
      schedule_rt_lsa(ifa->oa);
142
      if (ifa->type == OSPF_IT_VLINK) schedule_rt_lsa(ifa->voa);
143
      schedule_net_lsa(ifa);
144
    }
145

    
146
    if (state == NEIGHBOR_FULL)        /* Increase number of adjacencies */
147
    {
148
      ifa->fadj++;
149
      schedule_rt_lsa(ifa->oa);
150
      if (ifa->type == OSPF_IT_VLINK) schedule_rt_lsa(ifa->voa);
151
      schedule_net_lsa(ifa);
152
    }
153
    if (state == NEIGHBOR_EXSTART)
154
    {
155
      if (n->adj == 0)                /* First time adjacency */
156
      {
157
        n->dds = random_u32();
158
      }
159
      n->dds++;
160
      n->myimms.byte = 0;
161
      n->myimms.bit.ms = 1;
162
      n->myimms.bit.m = 1;
163
      n->myimms.bit.i = 1;
164
    }
165
    if (state > NEIGHBOR_EXSTART)
166
      n->myimms.bit.i = 0;
167
  }
168
}
169

    
170
static struct ospf_neighbor *
171
electbdr(list nl)
172
{
173
  struct ospf_neighbor *neigh, *n1, *n2;
174
  u32 nid;
175

    
176
  n1 = NULL;
177
  n2 = NULL;
178
  WALK_LIST(neigh, nl)                        /* First try those decl. themselves */
179
  {
180
#ifdef OSPFv2
181
    nid = ipa_to_u32(neigh->ip);
182
#else /* OSPFv3 */
183
    nid = neigh->rid;
184
#endif
185

    
186
    if (neigh->state >= NEIGHBOR_2WAY)        /* Higher than 2WAY */
187
      if (neigh->priority > 0)                /* Eligible */
188
        if (neigh->dr != nid)                /* And not decl. itself DR */
189
        {
190
          if (neigh->bdr == nid)        /* Declaring BDR */
191
          {
192
            if (n1 != NULL)
193
            {
194
              if (neigh->priority > n1->priority)
195
                n1 = neigh;
196
              else if (neigh->priority == n1->priority)
197
                if (neigh->rid > n1->rid)
198
                  n1 = neigh;
199
            }
200
            else
201
            {
202
              n1 = neigh;
203
            }
204
          }
205
          else                        /* And NOT declaring BDR */
206
          {
207
            if (n2 != NULL)
208
            {
209
              if (neigh->priority > n2->priority)
210
                n2 = neigh;
211
              else if (neigh->priority == n2->priority)
212
                if (neigh->rid > n2->rid)
213
                  n2 = neigh;
214
            }
215
            else
216
            {
217
              n2 = neigh;
218
            }
219
          }
220
        }
221
  }
222
  if (n1 == NULL)
223
    n1 = n2;
224

    
225
  return (n1);
226
}
227

    
228
static struct ospf_neighbor *
229
electdr(list nl)
230
{
231
  struct ospf_neighbor *neigh, *n;
232
  u32 nid;
233

    
234
  n = NULL;
235
  WALK_LIST(neigh, nl)                        /* And now DR */
236
  {
237
#ifdef OSPFv2
238
    nid = ipa_to_u32(neigh->ip);
239
#else /* OSPFv3 */
240
    nid = neigh->rid;
241
#endif
242

    
243
    if (neigh->state >= NEIGHBOR_2WAY)        /* Higher than 2WAY */
244
      if (neigh->priority > 0)                /* Eligible */
245
        if (neigh->dr == nid)                /* And declaring itself DR */
246
        {
247
          if (n != NULL)
248
          {
249
            if (neigh->priority > n->priority)
250
              n = neigh;
251
            else if (neigh->priority == n->priority)
252
              if (neigh->rid > n->rid)
253
                n = neigh;
254
          }
255
          else
256
          {
257
            n = neigh;
258
          }
259
        }
260
  }
261

    
262
  return (n);
263
}
264

    
265
static int
266
can_do_adj(struct ospf_neighbor *n)
267
{
268
  struct ospf_iface *ifa;
269
  struct proto *p;
270
  int i;
271

    
272
  ifa = n->ifa;
273
  p = (struct proto *) (ifa->oa->po);
274
  i = 0;
275

    
276
  switch (ifa->type)
277
  {
278
  case OSPF_IT_PTP:
279
  case OSPF_IT_VLINK:
280
    i = 1;
281
    break;
282
  case OSPF_IT_BCAST:
283
  case OSPF_IT_NBMA:
284
    switch (ifa->state)
285
    {
286
    case OSPF_IS_DOWN:
287
      bug("%s: Iface %s in down state?", p->name, ifa->iface->name);
288
      break;
289
    case OSPF_IS_WAITING:
290
      DBG("%s: Neighbor? on iface %s\n", p->name, ifa->iface->name);
291
      break;
292
    case OSPF_IS_DROTHER:
293
      if (((n->rid == ifa->drid) || (n->rid == ifa->bdrid))
294
          && (n->state >= NEIGHBOR_2WAY))
295
        i = 1;
296
      break;
297
    case OSPF_IS_PTP:
298
    case OSPF_IS_BACKUP:
299
    case OSPF_IS_DR:
300
      if (n->state >= NEIGHBOR_2WAY)
301
        i = 1;
302
      break;
303
    default:
304
      bug("%s: Iface %s in unknown state?", p->name, ifa->iface->name);
305
      break;
306
    }
307
    break;
308
  default:
309
    bug("%s: Iface %s is unknown type?", p->name, ifa->iface->name);
310
    break;
311
  }
312
  DBG("%s: Iface %s can_do_adj=%d\n", p->name, ifa->iface->name, i);
313
  return i;
314
}
315

    
316
/**
317
 * ospf_neigh_sm - ospf neighbor state machine
318
 * @n: neighor
319
 * @event: actual event
320
 *
321
 * This part implements the neighbor state machine as described in 10.3 of
322
 * RFC 2328. The only difference is that state %NEIGHBOR_ATTEMPT is not
323
 * used. We discover neighbors on nonbroadcast networks in the
324
 * same way as on broadcast networks. The only difference is in
325
 * sending hello packets. These are sent to IPs listed in
326
 * @ospf_iface->nbma_list .
327
 */
328
void
329
ospf_neigh_sm(struct ospf_neighbor *n, int event)
330
{
331
  struct proto_ospf *po = n->ifa->oa->po;
332
  struct proto *p = &po->proto;
333

    
334
  DBG("Neighbor state machine for neighbor %I, event '%s'\n", n->ip,
335
             ospf_inm[event]);
336

    
337
  switch (event)
338
  {
339
  case INM_START:
340
    neigh_chstate(n, NEIGHBOR_ATTEMPT);
341
    /* NBMA are used different way */
342
    break;
343
  case INM_HELLOREC:
344
    switch (n->state)
345
    {
346
    case NEIGHBOR_ATTEMPT:
347
    case NEIGHBOR_DOWN:
348
      neigh_chstate(n, NEIGHBOR_INIT);
349
    default:
350
      tm_start(n->inactim, n->ifa->dead);        /* Restart inactivity timer */
351
      break;
352
    }
353
    break;
354
  case INM_2WAYREC:
355
    if (n->state < NEIGHBOR_2WAY)
356
      neigh_chstate(n, NEIGHBOR_2WAY);
357
    if ((n->state == NEIGHBOR_2WAY) && can_do_adj(n))
358
      neigh_chstate(n, NEIGHBOR_EXSTART);
359
    break;
360
  case INM_NEGDONE:
361
    if (n->state == NEIGHBOR_EXSTART)
362
    {
363
      neigh_chstate(n, NEIGHBOR_EXCHANGE);
364

    
365
      /* Reset DB summary list iterator */
366
      s_get(&(n->dbsi));
367
      s_init(&(n->dbsi), &po->lsal);
368

    
369
      while (!EMPTY_LIST(n->ackl[ACKL_DELAY]))
370
      {
371
        struct lsah_n *no;
372
        no = (struct lsah_n *) HEAD(n->ackl[ACKL_DELAY]);
373
        rem_node(NODE no);
374
        mb_free(no);
375
      }
376
    }
377
    else
378
      bug("NEGDONE and I'm not in EXSTART?");
379
    break;
380
  case INM_EXDONE:
381
    neigh_chstate(n, NEIGHBOR_LOADING);
382
    break;
383
  case INM_LOADDONE:
384
    neigh_chstate(n, NEIGHBOR_FULL);
385
    break;
386
  case INM_ADJOK:
387
    switch (n->state)
388
    {
389
    case NEIGHBOR_2WAY:
390
      /* Can In build adjacency? */
391
      if (can_do_adj(n))
392
      {
393
        neigh_chstate(n, NEIGHBOR_EXSTART);
394
      }
395
      break;
396
    default:
397
      if (n->state >= NEIGHBOR_EXSTART)
398
        if (!can_do_adj(n))
399
        {
400
          reset_lists(n);
401
          neigh_chstate(n, NEIGHBOR_2WAY);
402
        }
403
      break;
404
    }
405
    break;
406
  case INM_SEQMIS:
407
  case INM_BADLSREQ:
408
    if (n->state >= NEIGHBOR_EXCHANGE)
409
    {
410
      reset_lists(n);
411
      neigh_chstate(n, NEIGHBOR_EXSTART);
412
    }
413
    break;
414
  case INM_KILLNBR:
415
  case INM_LLDOWN:
416
  case INM_INACTTIM:
417
    reset_lists(n);
418
    neigh_chstate(n, NEIGHBOR_DOWN);
419
    break;
420
  case INM_1WAYREC:
421
    reset_lists(n);
422
    neigh_chstate(n, NEIGHBOR_INIT);
423
    break;
424
  default:
425
    bug("%s: INM - Unknown event?", p->name);
426
    break;
427
  }
428
}
429

    
430
/**
431
 * bdr_election - (Backup) Designed Router election
432
 * @ifa: actual interface
433
 *
434
 * When the wait timer fires, it is time to elect (Backup) Designated Router.
435
 * Structure describing me is added to this list so every electing router
436
 * has the same list. Backup Designated Router is elected before Designated
437
 * Router. This process is described in 9.4 of RFC 2328.
438
 */
439
void
440
bdr_election(struct ospf_iface *ifa)
441
{
442
  struct proto_ospf *po = ifa->oa->po;
443
  u32 myid = po->router_id;
444
  struct ospf_neighbor *neigh, *ndr, *nbdr, me;
445
  int doadj;
446

    
447
  DBG("(B)DR election.\n");
448

    
449
  me.state = NEIGHBOR_2WAY;
450
  me.rid = myid;
451
  me.priority = ifa->priority;
452

    
453
#ifdef OSPFv2
454
  me.ip = ifa->iface->addr->ip;
455
  me.dr = ipa_to_u32(ifa->drip);
456
  me.bdr = ipa_to_u32(ifa->bdrip);
457
#else /* OSPFv3 */
458
  me.ip = ifa->lladdr;
459
  me.dr = ifa->drid;
460
  me.bdr = ifa->bdrid;
461
  me.iface_id = ifa->iface->index;
462
#endif
463

    
464
  add_tail(&ifa->neigh_list, NODE & me);
465

    
466
  nbdr = electbdr(ifa->neigh_list);
467
  ndr = electdr(ifa->neigh_list);
468

    
469
  if (ndr == NULL)
470
    ndr = nbdr;
471

    
472
  /* 9.4. (4) */
473
  if (((ifa->drid == myid) && (ndr != &me))
474
      || ((ifa->drid != myid) && (ndr == &me))
475
      || ((ifa->bdrid == myid) && (nbdr != &me))
476
      || ((ifa->bdrid != myid) && (nbdr == &me)))
477
  {
478
#ifdef OSPFv2
479
    me.dr = ndr ? ipa_to_u32(ndr->ip) : 0;
480
    me.bdr = nbdr ? ipa_to_u32(nbdr->ip) : 0;
481
#else /* OSPFv3 */
482
    me.dr = ndr ? ndr->rid : 0;
483
    me.bdr = nbdr ? nbdr->rid : 0;
484
#endif
485

    
486
    nbdr = electbdr(ifa->neigh_list);
487
    ndr = electdr(ifa->neigh_list);
488

    
489
    if (ndr == NULL)
490
      ndr = nbdr;
491
  }
492

    
493
  u32 odrid = ifa->drid;
494
  u32 obdrid = ifa->bdrid;
495
 
496
  ifa->drid = ndr ? ndr->rid : 0;
497
  ifa->drip = ndr ? ndr->ip  : IPA_NONE;
498
  ifa->bdrid = nbdr ? nbdr->rid : 0;
499
  ifa->bdrip = nbdr ? nbdr->ip  : IPA_NONE;
500

    
501
#ifdef OSPFv3
502
  ifa->dr_iface_id = ndr ? ndr->iface_id : 0;
503
#endif
504

    
505
  DBG("DR=%R, BDR=%R\n", ifa->drid, ifa->bdrid);
506

    
507
  doadj = ((ifa->drid != odrid) || (ifa->bdrid != obdrid));
508

    
509
  if (myid == ifa->drid)
510
    ospf_iface_chstate(ifa, OSPF_IS_DR);
511
  else
512
  {
513
    if (myid == ifa->bdrid)
514
      ospf_iface_chstate(ifa, OSPF_IS_BACKUP);
515
    else
516
      ospf_iface_chstate(ifa, OSPF_IS_DROTHER);
517
  }
518

    
519
  rem_node(NODE & me);
520

    
521
  if (doadj)
522
  {
523
    WALK_LIST(neigh, ifa->neigh_list)
524
    {
525
      ospf_neigh_sm(neigh, INM_ADJOK);
526
    }
527
  }
528
}
529

    
530
struct ospf_neighbor *
531
find_neigh(struct ospf_iface *ifa, u32 rid)
532
{
533
  struct ospf_neighbor *n;
534

    
535
  WALK_LIST(n, ifa->neigh_list) if (n->rid == rid)
536
    return n;
537
  return NULL;
538
}
539

    
540

    
541
/* Find a closest neighbor which is at least 2-Way */
542
struct ospf_neighbor *
543
find_neigh_noifa(struct proto_ospf *po, u32 rid)
544
{
545
  struct ospf_neighbor *n = NULL, *m;
546
  struct ospf_iface *ifa;
547

    
548
  WALK_LIST(ifa, po->iface_list) if ((m = find_neigh(ifa, rid)) != NULL)
549
  {
550
    if (m->state >= NEIGHBOR_2WAY)
551
    {
552
      if (n == NULL)
553
        n = m;
554
      else if (m->ifa->cost < n->ifa->cost)
555
        n = m;
556
    }
557
  }
558
  return n;
559
}
560

    
561
struct ospf_area *
562
ospf_find_area(struct proto_ospf *po, u32 aid)
563
{
564
  struct ospf_area *oa;
565
  WALK_LIST(oa, po->area_list)
566
    if (((struct ospf_area *) oa)->areaid == aid)
567
    return oa;
568
  return NULL;
569
}
570

    
571
/* Neighbor is inactive for a long time. Remove it. */
572
static void
573
neighbor_timer_hook(timer * timer)
574
{
575
  struct ospf_neighbor *n = (struct ospf_neighbor *) timer->data;
576
  struct ospf_iface *ifa = n->ifa;
577
  struct proto *p = &ifa->oa->po->proto;
578

    
579
  OSPF_TRACE(D_EVENTS,
580
             "Inactivity timer fired on interface %s for neighbor %I.",
581
             ifa->iface->name, n->ip);
582
  ospf_neigh_remove(n);
583
}
584

    
585
void
586
ospf_neigh_remove(struct ospf_neighbor *n)
587
{
588
  struct ospf_iface *ifa = n->ifa;
589
  struct proto *p = &ifa->oa->po->proto;
590

    
591
  s_get(&(n->dbsi));
592
  neigh_chstate(n, NEIGHBOR_DOWN);
593
  rem_node(NODE n);
594
  rfree(n->pool);
595
  OSPF_TRACE(D_EVENTS, "Deleting neigbor.");
596
}
597

    
598
void
599
ospf_sh_neigh_info(struct ospf_neighbor *n)
600
{
601
  struct ospf_iface *ifa = n->ifa;
602
  char *pos = "other";
603
  char etime[6];
604
  int exp, sec, min;
605

    
606
  exp = n->inactim->expires - now;
607
  sec = exp % 60;
608
  min = exp / 60;
609
  if (min > 59)
610
  {
611
    bsprintf(etime, "-Inf-");
612
  }
613
  else
614
  {
615
    bsprintf(etime, "%02u:%02u", min, sec);
616
  }
617

    
618
  if (n->rid == ifa->drid)
619
    pos = "dr   ";
620
  if (n->rid == ifa->bdrid)
621
    pos = "bdr  ";
622
  if ((n->ifa->type == OSPF_IT_PTP) || (n->ifa->type == OSPF_IT_VLINK))
623
    pos = "ptp  ";
624

    
625
  cli_msg(-1013, "%-1R\t%3u\t%s/%s\t%-5s\t%-10s %-1I", n->rid, n->priority,
626
          ospf_ns[n->state], pos, etime,
627
          (ifa->type == OSPF_IT_VLINK ? "vlink" : ifa->iface->name), n->ip);
628
}
629

    
630
static void
631
rxmt_timer_hook(timer * timer)
632
{
633
  struct ospf_neighbor *n = (struct ospf_neighbor *) timer->data;
634
  // struct proto *p = &n->ifa->oa->po->proto;
635
  struct top_hash_entry *en;
636

    
637
  DBG("%s: RXMT timer fired on interface %s for neigh: %I.\n",
638
      p->name, n->ifa->iface->name, n->ip);
639

    
640
  if(n->state < NEIGHBOR_EXSTART) return;
641

    
642
  if (n->state == NEIGHBOR_EXSTART)
643
  {
644
    ospf_dbdes_send(n, 1);
645
    return;
646
  }
647

    
648
  if ((n->state == NEIGHBOR_EXCHANGE) && n->myimms.bit.ms)        /* I'm master */
649
    ospf_dbdes_send(n, 0);
650

    
651

    
652
  if (n->state < NEIGHBOR_FULL)        
653
    ospf_lsreq_send(n);        /* EXCHANGE or LOADING */
654
  else
655
  {
656
    if (!EMPTY_SLIST(n->lsrtl))        /* FULL */
657
    {
658
      list uplist;
659
      slab *upslab;
660
      struct l_lsr_head *llsh;
661

    
662
      init_list(&uplist);
663
      upslab = sl_new(n->pool, sizeof(struct l_lsr_head));
664

    
665
      WALK_SLIST(en, n->lsrtl)
666
      {
667
        if ((SNODE en)->next == (SNODE en))
668
          bug("RTList is cycled");
669
        llsh = sl_alloc(upslab);
670
        llsh->lsh.id = en->lsa.id;
671
        llsh->lsh.rt = en->lsa.rt;
672
        llsh->lsh.type = en->lsa.type;
673
        DBG("Working on ID: %R, RT: %R, Type: %u\n",
674
            en->lsa.id, en->lsa.rt, en->lsa.type);
675
        add_tail(&uplist, NODE llsh);
676
      }
677
      ospf_lsupd_send_list(n, &uplist);
678
      rfree(upslab);
679
    }
680
  }
681
}
682

    
683
static void
684
ackd_timer_hook(timer * t)
685
{
686
  struct ospf_neighbor *n = t->data;
687
  ospf_lsack_send(n, ACKL_DELAY);
688
}