Statistics
| Branch: | Revision:

iof-bird-daemon / proto / ospf / neighbor.c @ 04632fd7

History | View | Annotate | Download (15.9 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_set(pool, inactivity_timer_hook, n, 0, 0);
98
  n->dbdes_timer = tm_new_set(pool, dbdes_timer_hook, n, 0, ifa->rxmtint);
99
  n->lsrq_timer = tm_new_set(pool, lsrq_timer_hook, n, 0, ifa->rxmtint);
100
  n->lsrt_timer = tm_new_set(pool, lsrt_timer_hook, n, 0, ifa->rxmtint);
101
  n->ackd_timer = tm_new_set(pool, ackd_timer_hook, n, 0, ifa->rxmtint / 2);
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

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

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

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

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

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

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

    
149
  n->state = state;
150

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
360

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

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

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

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

    
415
  return (n1);
416
}
417

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

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

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

    
448
  return (n);
449
}
450

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

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

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

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

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

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

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

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

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

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

    
504
  rem_node(NODE & me);
505

    
506

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
598

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

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

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

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

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

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

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

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

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

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

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

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

    
647

    
648
void
649
ospf_sh_neigh_info(struct ospf_neighbor *n)
650
{
651
  struct ospf_iface *ifa = n->ifa;
652
  char *pos = "PtP  ";
653
  char etime[6];
654
  int exp, sec, min;
655

    
656
  exp = n->inactim->expires - now;
657
  sec = exp % 60;
658
  min = exp / 60;
659
  if (min > 59)
660
  {
661
    bsprintf(etime, "-Inf-");
662
  }
663
  else
664
  {
665
    bsprintf(etime, "%02u:%02u", min, sec);
666
  }
667

    
668
  if ((ifa->type == OSPF_IT_BCAST) || (ifa->type == OSPF_IT_NBMA))
669
  {
670
    if (n->rid == ifa->drid)
671
      pos = "DR   ";
672
    else if (n->rid == ifa->bdrid)
673
      pos = "BDR  ";
674
    else
675
      pos = "Other";
676
  }
677

    
678
  cli_msg(-1013, "%-1R\t%3u\t%s/%s\t%-5s\t%-10s %-1I", n->rid, n->priority,
679
          ospf_ns_names[n->state], pos, etime, ifa->ifname, n->ip);
680
}