Statistics
| Branch: | Revision:

iof-bird / bird-2.0.1 / proto / ospf / neighbor.c @ 6b3f1a54

History | View | Annotate | Download (15.7 KB)

1
/*
2
 *        BIRD -- OSPF
3
 *
4
 *        (c) 1999--2004 Ondrej Filip <feela@network.cz>
5
 *        (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org>
6
 *        (c) 2009--2014 CZ.NIC z.s.p.o.
7
 *
8
 *        Can be freely distributed and used under the terms of the GNU GPL.
9
 */
10

    
11
#include "ospf.h"
12

    
13

    
14
const char *ospf_ns_names[] = {
15
  "Down", "Attempt", "Init", "2-Way", "ExStart", "Exchange", "Loading", "Full"
16
};
17

    
18
const char *ospf_inm_names[] = {
19
  "HelloReceived", "Start", "2-WayReceived", "NegotiationDone", "ExchangeDone",
20
  "BadLSReq", "LoadingDone", "AdjOK?", "SeqNumberMismatch", "1-WayReceived",
21
  "KillNbr", "InactivityTimer", "LLDown"
22
};
23

    
24

    
25
static int can_do_adj(struct ospf_neighbor *n);
26
static void inactivity_timer_hook(timer * timer);
27
static void dbdes_timer_hook(timer *t);
28
static void lsrq_timer_hook(timer *t);
29
static void lsrt_timer_hook(timer *t);
30
static void ackd_timer_hook(timer *t);
31

    
32

    
33
static void
34
init_lists(struct ospf_proto *p, struct ospf_neighbor *n)
35
{
36
  s_init_list(&(n->lsrql));
37
  n->lsrqi = SHEAD(n->lsrql);
38
  n->lsrqh = ospf_top_new(p, n->pool);
39

    
40
  s_init_list(&(n->lsrtl));
41
  n->lsrth = ospf_top_new(p, n->pool);
42
}
43

    
44
static void
45
release_lsrtl(struct ospf_proto *p, struct ospf_neighbor *n)
46
{
47
  struct top_hash_entry *ret, *en;
48

    
49
  WALK_SLIST(ret, n->lsrtl)
50
  {
51
    en = ospf_hash_find_entry(p->gr, ret);
52
    if (en)
53
      en->ret_count--;
54
  }
55
}
56

    
57
/* Resets LSA request and retransmit lists.
58
 * We do not reset DB summary list iterator here,
59
 * it is reset during entering EXCHANGE state.
60
 */
61
static void
62
reset_lists(struct ospf_proto *p, struct ospf_neighbor *n)
63
{
64
  release_lsrtl(p, n);
65
  ospf_top_free(n->lsrqh);
66
  ospf_top_free(n->lsrth);
67
  ospf_reset_lsack_queue(n);
68

    
69
  tm_stop(n->dbdes_timer);
70
  tm_stop(n->lsrq_timer);
71
  tm_stop(n->lsrt_timer);
72
  tm_stop(n->ackd_timer);
73

    
74
  init_lists(p, n);
75
}
76

    
77
struct ospf_neighbor *
78
ospf_neighbor_new(struct ospf_iface *ifa)
79
{
80
  struct ospf_proto *p = ifa->oa->po;
81
  struct pool *pool = rp_new(p->p.pool, "OSPF Neighbor");
82
  struct ospf_neighbor *n = mb_allocz(pool, sizeof(struct ospf_neighbor));
83

    
84
  n->pool = pool;
85
  n->ifa = ifa;
86
  add_tail(&ifa->neigh_list, NODE n);
87
  n->adj = 0;
88
  n->csn = 0;
89
  n->state = NEIGHBOR_DOWN;
90

    
91
  init_lists(p, n);
92
  s_init(&(n->dbsi), &(p->lsal));
93

    
94
  init_list(&n->ackl[ACKL_DIRECT]);
95
  init_list(&n->ackl[ACKL_DELAY]);
96

    
97
  n->inactim = tm_new_init(pool, inactivity_timer_hook, n, 0, 0);
98
  n->dbdes_timer = tm_new_init(pool, dbdes_timer_hook, n, ifa->rxmtint S, 0);
99
  n->lsrq_timer = tm_new_init(pool, lsrq_timer_hook, n, ifa->rxmtint S, 0);
100
  n->lsrt_timer = tm_new_init(pool, lsrt_timer_hook, n, ifa->rxmtint S, 0);
101
  n->ackd_timer = tm_new_init(pool, ackd_timer_hook, n, ifa->rxmtint S / 2, 0);
102

    
103
  return (n);
104
}
105

    
106
static void
107
ospf_neigh_down(struct ospf_neighbor *n)
108
{
109
  struct ospf_iface *ifa = n->ifa;
110
  struct ospf_proto *p = ifa->oa->po;
111
  u32 rid = n->rid;
112

    
113
  if ((ifa->type == OSPF_IT_NBMA) || (ifa->type == OSPF_IT_PTMP))
114
  {
115
    struct nbma_node *nn = find_nbma_node(ifa, n->ip);
116
    if (nn)
117
      nn->found = 0;
118
  }
119

    
120
  s_get(&(n->dbsi));
121
  release_lsrtl(p, n);
122
  rem_node(NODE n);
123
  rfree(n->pool);
124

    
125
  OSPF_TRACE(D_EVENTS, "Neighbor %R on %s removed", rid, ifa->ifname);
126
}
127

    
128
/**
129
 * ospf_neigh_chstate - handles changes related to new or lod state of neighbor
130
 * @n: OSPF neighbor
131
 * @state: new state
132
 *
133
 * Many actions have to be taken acording to a change of state of a neighbor. It
134
 * starts rxmt timers, call interface state machine etc.
135
 */
136
static void
137
ospf_neigh_chstate(struct ospf_neighbor *n, u8 state)
138
{
139
  struct ospf_iface *ifa = n->ifa;
140
  struct ospf_proto *p = ifa->oa->po;
141
  u8 old_state = n->state;
142
  int old_fadj = ifa->fadj;
143

    
144
  if (state == old_state)
145
    return;
146

    
147
  OSPF_TRACE(D_EVENTS, "Neighbor %R on %s changed state from %s to %s",
148
             n->rid, ifa->ifname, ospf_ns_names[old_state], ospf_ns_names[state]);
149

    
150
  n->state = state;
151

    
152
  /* Increase number of partial adjacencies */
153
  if ((state == NEIGHBOR_EXCHANGE) || (state == NEIGHBOR_LOADING))
154
    p->padj++;
155

    
156
  /* Decrease number of partial adjacencies */
157
  if ((old_state == NEIGHBOR_EXCHANGE) || (old_state == NEIGHBOR_LOADING))
158
    p->padj--;
159

    
160
  /* Increase number of full adjacencies */
161
  if (state == NEIGHBOR_FULL)
162
    ifa->fadj++;
163

    
164
  /* Decrease number of full adjacencies */
165
  if (old_state == NEIGHBOR_FULL)
166
    ifa->fadj--;
167

    
168
  if (ifa->fadj != old_fadj)
169
  {
170
    /* RFC 2328 12.4 Event 4 - neighbor enters/leaves Full state */
171
    ospf_notify_rt_lsa(ifa->oa);
172
    ospf_notify_net_lsa(ifa);
173

    
174
    /* RFC 2328 12.4 Event 8 - vlink state change */
175
    if (ifa->type == OSPF_IT_VLINK)
176
      ospf_notify_rt_lsa(ifa->voa);
177
  }
178

    
179
  if (state == NEIGHBOR_EXSTART)
180
  {
181
    /* First time adjacency */
182
    if (n->adj == 0)
183
      n->dds = random_u32();
184

    
185
    n->dds++;
186
    n->myimms = DBDES_IMMS;
187

    
188
    tm_start(n->dbdes_timer, 0);
189
    tm_start(n->ackd_timer, ifa->rxmtint S / 2);
190
  }
191

    
192
  if (state > NEIGHBOR_EXSTART)
193
    n->myimms &= ~DBDES_I;
194

    
195
  /* Generate NeighborChange event if needed, see RFC 2328 9.2 */
196
  if ((state == NEIGHBOR_2WAY) && (old_state < NEIGHBOR_2WAY))
197
    ospf_iface_sm(ifa, ISM_NEICH);
198
  if ((state < NEIGHBOR_2WAY) && (old_state >= NEIGHBOR_2WAY))
199
    ospf_iface_sm(ifa, ISM_NEICH);
200
}
201

    
202
/**
203
 * ospf_neigh_sm - ospf neighbor state machine
204
 * @n: neighor
205
 * @event: actual event
206
 *
207
 * This part implements the neighbor state machine as described in 10.3 of
208
 * RFC 2328. The only difference is that state %NEIGHBOR_ATTEMPT is not
209
 * used. We discover neighbors on nonbroadcast networks in the
210
 * same way as on broadcast networks. The only difference is in
211
 * sending hello packets. These are sent to IPs listed in
212
 * @ospf_iface->nbma_list .
213
 */
214
void
215
ospf_neigh_sm(struct ospf_neighbor *n, int event)
216
{
217
  struct ospf_proto *p = n->ifa->oa->po;
218

    
219
  DBG("Neighbor state machine for %R on %s, event %s\n",
220
      n->rid, n->ifa->ifname, ospf_inm_names[event]);
221

    
222
  switch (event)
223
  {
224
  case INM_START:
225
    ospf_neigh_chstate(n, NEIGHBOR_ATTEMPT);
226
    /* NBMA are used different way */
227
    break;
228

    
229
  case INM_HELLOREC:
230
    if (n->state < NEIGHBOR_INIT)
231
      ospf_neigh_chstate(n, NEIGHBOR_INIT);
232

    
233
    /* Restart inactivity timer */
234
    tm_start(n->inactim, n->ifa->deadint S);
235
    break;
236

    
237
  case INM_2WAYREC:
238
    if (n->state < NEIGHBOR_2WAY)
239
      ospf_neigh_chstate(n, NEIGHBOR_2WAY);
240
    if ((n->state == NEIGHBOR_2WAY) && can_do_adj(n))
241
      ospf_neigh_chstate(n, NEIGHBOR_EXSTART);
242
    break;
243

    
244
  case INM_NEGDONE:
245
    if (n->state == NEIGHBOR_EXSTART)
246
    {
247
      ospf_neigh_chstate(n, NEIGHBOR_EXCHANGE);
248

    
249
      /* Reset DB summary list iterator */
250
      s_get(&(n->dbsi));
251
      s_init(&(n->dbsi), &p->lsal);
252

    
253
      /* Add MaxAge LSA entries to retransmission list */
254
      ospf_add_flushed_to_lsrt(p, n);
255
    }
256
    else
257
      bug("NEGDONE and I'm not in EXSTART?");
258
    break;
259

    
260
  case INM_EXDONE:
261
    if (!EMPTY_SLIST(n->lsrql))
262
      ospf_neigh_chstate(n, NEIGHBOR_LOADING);
263
    else
264
      ospf_neigh_chstate(n, NEIGHBOR_FULL);
265
    break;
266

    
267
  case INM_LOADDONE:
268
    ospf_neigh_chstate(n, NEIGHBOR_FULL);
269
    break;
270

    
271
  case INM_ADJOK:
272
    /* Can In build adjacency? */
273
    if ((n->state == NEIGHBOR_2WAY) && can_do_adj(n))
274
    {
275
      ospf_neigh_chstate(n, NEIGHBOR_EXSTART);
276
    }
277
    else if ((n->state >= NEIGHBOR_EXSTART) && !can_do_adj(n))
278
    {
279
      reset_lists(p, n);
280
      ospf_neigh_chstate(n, NEIGHBOR_2WAY);
281
    }
282
    break;
283

    
284
  case INM_SEQMIS:
285
  case INM_BADLSREQ:
286
    if (n->state >= NEIGHBOR_EXCHANGE)
287
    {
288
      reset_lists(p, n);
289
      ospf_neigh_chstate(n, NEIGHBOR_EXSTART);
290
    }
291
    break;
292

    
293
  case INM_KILLNBR:
294
  case INM_LLDOWN:
295
  case INM_INACTTIM:
296
    /* No need for reset_lists() */
297
    ospf_neigh_chstate(n, NEIGHBOR_DOWN);
298
    ospf_neigh_down(n);
299
    break;
300

    
301
  case INM_1WAYREC:
302
    reset_lists(p, n);
303
    ospf_neigh_chstate(n, NEIGHBOR_INIT);
304
    break;
305

    
306
  default:
307
    bug("%s: INM - Unknown event?", p->p.name);
308
    break;
309
  }
310
}
311

    
312
static int
313
can_do_adj(struct ospf_neighbor *n)
314
{
315
  struct ospf_iface *ifa = n->ifa;
316
  struct ospf_proto *p = ifa->oa->po;
317
  int i = 0;
318

    
319
  switch (ifa->type)
320
  {
321
  case OSPF_IT_PTP:
322
  case OSPF_IT_PTMP:
323
  case OSPF_IT_VLINK:
324
    i = 1;
325
    break;
326
  case OSPF_IT_BCAST:
327
  case OSPF_IT_NBMA:
328
    switch (ifa->state)
329
    {
330
    case OSPF_IS_DOWN:
331
    case OSPF_IS_LOOP:
332
      bug("%s: Iface %s in down state?", p->p.name, ifa->ifname);
333
      break;
334
    case OSPF_IS_WAITING:
335
      DBG("%s: Neighbor? on iface %s\n", p->p.name, ifa->ifname);
336
      break;
337
    case OSPF_IS_DROTHER:
338
      if (((n->rid == ifa->drid) || (n->rid == ifa->bdrid))
339
          && (n->state >= NEIGHBOR_2WAY))
340
        i = 1;
341
      break;
342
    case OSPF_IS_PTP:
343
    case OSPF_IS_BACKUP:
344
    case OSPF_IS_DR:
345
      if (n->state >= NEIGHBOR_2WAY)
346
        i = 1;
347
      break;
348
    default:
349
      bug("%s: Iface %s in unknown state?", p->p.name, ifa->ifname);
350
      break;
351
    }
352
    break;
353
  default:
354
    bug("%s: Iface %s is unknown type?", p->p.name, ifa->ifname);
355
    break;
356
  }
357
  DBG("%s: Iface %s can_do_adj=%d\n", p->p.name, ifa->ifname, i);
358
  return i;
359
}
360

    
361

    
362
static inline u32 neigh_get_id(struct ospf_proto *p, struct ospf_neighbor *n)
363
{ return ospf_is_v2(p) ? ipa_to_u32(n->ip) : n->rid; }
364

    
365
static struct ospf_neighbor *
366
elect_bdr(struct ospf_proto *p, list nl)
367
{
368
  struct ospf_neighbor *neigh, *n1, *n2;
369
  u32 nid;
370

    
371
  n1 = NULL;
372
  n2 = NULL;
373
  WALK_LIST(neigh, nl)                        /* First try those decl. themselves */
374
  {
375
    nid = neigh_get_id(p, neigh);
376

    
377
    if (neigh->state >= NEIGHBOR_2WAY)        /* Higher than 2WAY */
378
      if (neigh->priority > 0)                /* Eligible */
379
        if (neigh->dr != nid)                /* And not decl. itself DR */
380
        {
381
          if (neigh->bdr == nid)        /* Declaring BDR */
382
          {
383
            if (n1 != NULL)
384
            {
385
              if (neigh->priority > n1->priority)
386
                n1 = neigh;
387
              else if (neigh->priority == n1->priority)
388
                if (neigh->rid > n1->rid)
389
                  n1 = neigh;
390
            }
391
            else
392
            {
393
              n1 = neigh;
394
            }
395
          }
396
          else                        /* And NOT declaring BDR */
397
          {
398
            if (n2 != NULL)
399
            {
400
              if (neigh->priority > n2->priority)
401
                n2 = neigh;
402
              else if (neigh->priority == n2->priority)
403
                if (neigh->rid > n2->rid)
404
                  n2 = neigh;
405
            }
406
            else
407
            {
408
              n2 = neigh;
409
            }
410
          }
411
        }
412
  }
413
  if (n1 == NULL)
414
    n1 = n2;
415

    
416
  return (n1);
417
}
418

    
419
static struct ospf_neighbor *
420
elect_dr(struct ospf_proto *p, list nl)
421
{
422
  struct ospf_neighbor *neigh, *n;
423
  u32 nid;
424

    
425
  n = NULL;
426
  WALK_LIST(neigh, nl)                        /* And now DR */
427
  {
428
    nid = neigh_get_id(p, neigh);
429

    
430
    if (neigh->state >= NEIGHBOR_2WAY)        /* Higher than 2WAY */
431
      if (neigh->priority > 0)                /* Eligible */
432
        if (neigh->dr == nid)                /* And declaring itself DR */
433
        {
434
          if (n != NULL)
435
          {
436
            if (neigh->priority > n->priority)
437
              n = neigh;
438
            else if (neigh->priority == n->priority)
439
              if (neigh->rid > n->rid)
440
                n = neigh;
441
          }
442
          else
443
          {
444
            n = neigh;
445
          }
446
        }
447
  }
448

    
449
  return (n);
450
}
451

    
452
/**
453
 * ospf_dr_election - (Backup) Designed Router election
454
 * @ifa: actual interface
455
 *
456
 * When the wait timer fires, it is time to elect (Backup) Designated Router.
457
 * Structure describing me is added to this list so every electing router has
458
 * the same list. Backup Designated Router is elected before Designated
459
 * Router. This process is described in 9.4 of RFC 2328. The function is
460
 * supposed to be called only from ospf_iface_sm() as a part of the interface
461
 * state machine.
462
 */
463
void
464
ospf_dr_election(struct ospf_iface *ifa)
465
{
466
  struct ospf_proto *p = ifa->oa->po;
467
  struct ospf_neighbor *neigh, *ndr, *nbdr, me;
468
  u32 myid = p->router_id;
469

    
470
  DBG("(B)DR election.\n");
471

    
472
  me.state = NEIGHBOR_2WAY;
473
  me.rid = myid;
474
  me.priority = ifa->priority;
475
  me.ip = ifa->addr->ip;
476

    
477
  me.dr  = ospf_is_v2(p) ? ipa_to_u32(ifa->drip) : ifa->drid;
478
  me.bdr = ospf_is_v2(p) ? ipa_to_u32(ifa->bdrip) : ifa->bdrid;
479
  me.iface_id = ifa->iface_id;
480

    
481
  add_tail(&ifa->neigh_list, NODE & me);
482

    
483
  nbdr = elect_bdr(p, ifa->neigh_list);
484
  ndr = elect_dr(p, ifa->neigh_list);
485

    
486
  if (ndr == NULL)
487
    ndr = nbdr;
488

    
489
  /* 9.4. (4) */
490
  if (((ifa->drid == myid) && (ndr != &me))
491
      || ((ifa->drid != myid) && (ndr == &me))
492
      || ((ifa->bdrid == myid) && (nbdr != &me))
493
      || ((ifa->bdrid != myid) && (nbdr == &me)))
494
  {
495
    me.dr = ndr ? neigh_get_id(p, ndr) : 0;
496
    me.bdr = nbdr ? neigh_get_id(p, nbdr) : 0;
497

    
498
    nbdr = elect_bdr(p, ifa->neigh_list);
499
    ndr = elect_dr(p, ifa->neigh_list);
500

    
501
    if (ndr == NULL)
502
      ndr = nbdr;
503
  }
504

    
505
  rem_node(NODE & me);
506

    
507

    
508
  u32 old_drid = ifa->drid;
509
  u32 old_bdrid = ifa->bdrid;
510
  ip_addr none = ospf_is_v2(p) ? IPA_NONE4 : IPA_NONE6;
511

    
512
  ifa->drid = ndr ? ndr->rid : 0;
513
  ifa->drip = ndr ? ndr->ip  : none;
514
  ifa->dr_iface_id = ndr ? ndr->iface_id : 0;
515

    
516
  ifa->bdrid = nbdr ? nbdr->rid : 0;
517
  ifa->bdrip = nbdr ? nbdr->ip  : none;
518

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

    
521
  /* We are part of the interface state machine */
522
  if (ifa->drid == myid)
523
    ospf_iface_chstate(ifa, OSPF_IS_DR);
524
  else if (ifa->bdrid == myid)
525
    ospf_iface_chstate(ifa, OSPF_IS_BACKUP);
526
  else
527
    ospf_iface_chstate(ifa, OSPF_IS_DROTHER);
528

    
529
  /* Review neighbor adjacencies if DR or BDR changed */
530
  if ((ifa->drid != old_drid) || (ifa->bdrid != old_bdrid))
531
    WALK_LIST(neigh, ifa->neigh_list)
532
      if (neigh->state >= NEIGHBOR_2WAY)
533
        ospf_neigh_sm(neigh, INM_ADJOK);
534

    
535
  /* RFC 2328 12.4 Event 3 - DR change */
536
  if (ifa->drid != old_drid)
537
    ospf_notify_rt_lsa(ifa->oa);
538
}
539

    
540
struct ospf_neighbor *
541
find_neigh(struct ospf_iface *ifa, u32 rid)
542
{
543
  struct ospf_neighbor *n;
544
  WALK_LIST(n, ifa->neigh_list)
545
    if (n->rid == rid)
546
      return n;
547
  return NULL;
548
}
549

    
550
struct ospf_neighbor *
551
find_neigh_by_ip(struct ospf_iface *ifa, ip_addr ip)
552
{
553
  struct ospf_neighbor *n;
554
  WALK_LIST(n, ifa->neigh_list)
555
    if (ipa_equal(n->ip, ip))
556
      return n;
557
  return NULL;
558
}
559

    
560
static void
561
inactivity_timer_hook(timer * timer)
562
{
563
  struct ospf_neighbor *n = (struct ospf_neighbor *) timer->data;
564
  struct ospf_proto *p = n->ifa->oa->po;
565

    
566
  OSPF_TRACE(D_EVENTS, "Inactivity timer expired for nbr %R on %s",
567
             n->rid, n->ifa->ifname);
568
  ospf_neigh_sm(n, INM_INACTTIM);
569
}
570

    
571
static void
572
ospf_neigh_bfd_hook(struct bfd_request *req)
573
{
574
  struct ospf_neighbor *n = req->data;
575
  struct ospf_proto *p = n->ifa->oa->po;
576

    
577
  if (req->down)
578
  {
579
    OSPF_TRACE(D_EVENTS, "BFD session down for nbr %R on %s",
580
               n->rid, n->ifa->ifname);
581
    ospf_neigh_sm(n, INM_INACTTIM);
582
  }
583
}
584

    
585
void
586
ospf_neigh_update_bfd(struct ospf_neighbor *n, int use_bfd)
587
{
588
  if (use_bfd && !n->bfd_req)
589
    n->bfd_req = bfd_request_session(n->pool, n->ip, n->ifa->addr->ip, n->ifa->iface,
590
                                     ospf_neigh_bfd_hook, n);
591

    
592
  if (!use_bfd && n->bfd_req)
593
  {
594
    rfree(n->bfd_req);
595
    n->bfd_req = NULL;
596
  }
597
}
598

    
599

    
600
static void
601
dbdes_timer_hook(timer *t)
602
{
603
  struct ospf_neighbor *n = t->data;
604
  struct ospf_proto *p = n->ifa->oa->po;
605

    
606
  // OSPF_TRACE(D_EVENTS, "DBDES timer expired for nbr %R on %s", n->rid, n->ifa->ifname);
607

    
608
  if (n->state == NEIGHBOR_EXSTART)
609
    ospf_send_dbdes(p, n);
610

    
611
  if ((n->state == NEIGHBOR_EXCHANGE) && (n->myimms & DBDES_MS))
612
    ospf_rxmt_dbdes(p, n);
613
}
614

    
615
static void
616
lsrq_timer_hook(timer *t)
617
{
618
  struct ospf_neighbor *n = t->data;
619
  struct ospf_proto *p = n->ifa->oa->po;
620

    
621
  // OSPF_TRACE(D_EVENTS, "LSRQ timer expired for nbr %R on %s", n->rid, n->ifa->ifname);
622

    
623
  if ((n->state >= NEIGHBOR_EXCHANGE) && !EMPTY_SLIST(n->lsrql))
624
    ospf_send_lsreq(p, n);
625
}
626

    
627
static void
628
lsrt_timer_hook(timer *t)
629
{
630
  struct ospf_neighbor *n = t->data;
631
  struct ospf_proto *p = n->ifa->oa->po;
632

    
633
  // OSPF_TRACE(D_EVENTS, "LSRT timer expired for nbr %R on %s", n->rid, n->ifa->ifname);
634

    
635
  if ((n->state >= NEIGHBOR_EXCHANGE) && !EMPTY_SLIST(n->lsrtl))
636
    ospf_rxmt_lsupd(p, n);
637
}
638

    
639
static void
640
ackd_timer_hook(timer *t)
641
{
642
  struct ospf_neighbor *n = t->data;
643
  struct ospf_proto *p = n->ifa->oa->po;
644

    
645
  ospf_send_lsack(p, n, ACKL_DELAY);
646
}
647

    
648

    
649
void
650
ospf_sh_neigh_info(struct ospf_neighbor *n)
651
{
652
  struct ospf_iface *ifa = n->ifa;
653
  char *pos = "PtP  ";
654

    
655
  if ((ifa->type == OSPF_IT_BCAST) || (ifa->type == OSPF_IT_NBMA))
656
  {
657
    if (n->rid == ifa->drid)
658
      pos = "DR   ";
659
    else if (n->rid == ifa->bdrid)
660
      pos = "BDR  ";
661
    else
662
      pos = "Other";
663
  }
664

    
665
  cli_msg(-1013, "%-1R\t%3u\t%s/%s\t%7t\t%-10s %-1I",
666
          n->rid, n->priority, ospf_ns_names[n->state], pos,
667
          tm_remains(n->inactim), ifa->ifname, n->ip);
668
}