Statistics
| Branch: | Revision:

iof-bird-daemon / proto / ospf / neighbor.c @ 061ab802

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 ospf_neighbor *neigh, *ndr, *nbdr, me;
443
  u32 myid;
444
  int doadj;
445
  struct proto *p = &ifa->oa->po->proto;
446

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

    
449
  myid = p->cf->global->router_id;
450

    
451
  me.state = NEIGHBOR_2WAY;
452
  me.rid = myid;
453
  me.priority = ifa->priority;
454

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

    
466
  add_tail(&ifa->neigh_list, NODE & me);
467

    
468
  nbdr = electbdr(ifa->neigh_list);
469
  ndr = electdr(ifa->neigh_list);
470

    
471
  if (ndr == NULL)
472
    ndr = nbdr;
473

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

    
488
    nbdr = electbdr(ifa->neigh_list);
489
    ndr = electdr(ifa->neigh_list);
490

    
491
    if (ndr == NULL)
492
      ndr = nbdr;
493
  }
494

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

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

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

    
509
  doadj = ((ifa->drid != odrid) || (ifa->bdrid != obdrid));
510

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

    
521
  rem_node(NODE & me);
522

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

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

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

    
542

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

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

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

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

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

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

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

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

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

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

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

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

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

    
642
  if(n->state < NEIGHBOR_EXSTART) return;
643

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

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

    
653

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

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

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

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