Statistics
| Branch: | Revision:

iof-bird / bird-2.0.1 / proto / radv / radv.c @ 6b3f1a54

History | View | Annotate | Download (17.6 KB)

1
/*
2
 *        BIRD -- Router Advertisement
3
 *
4
 *
5
 *        Can be freely distributed and used under the terms of the GNU GPL.
6
 */
7

    
8

    
9
#include <stdlib.h>
10
#include "radv.h"
11

    
12
/**
13
 * DOC: Router Advertisements
14
 *
15
 * The RAdv protocol is implemented in two files: |radv.c| containing the
16
 * interface with BIRD core and the protocol logic and |packets.c| handling low
17
 * level protocol stuff (RX, TX and packet formats). The protocol does not
18
 * export any routes.
19
 *
20
 * The RAdv is structured in the usual way - for each handled interface there is
21
 * a structure &radv_iface that contains a state related to that interface
22
 * together with its resources (a socket, a timer). There is also a prepared RA
23
 * stored in a TX buffer of the socket associated with an iface. These iface
24
 * structures are created and removed according to iface events from BIRD core
25
 * handled by radv_if_notify() callback.
26
 *
27
 * The main logic of RAdv consists of two functions: radv_iface_notify(), which
28
 * processes asynchronous events (specified by RA_EV_* codes), and radv_timer(),
29
 * which triggers sending RAs and computes the next timeout.
30
 *
31
 * The RAdv protocol could receive routes (through radv_import_control() and
32
 * radv_rt_notify()), but only the configured trigger route is tracked (in
33
 * &active var).  When a radv protocol is reconfigured, the connected routing
34
 * table is examined (in radv_check_active()) to have proper &active value in
35
 * case of the specified trigger prefix was changed.
36
 *
37
 * Supported standards:
38
 * - RFC 4861 - main RA standard
39
 * - RFC 4191 - Default Router Preferences and More-Specific Routes
40
 * - RFC 6106 - DNS extensions (RDDNS, DNSSL)
41
 */
42

    
43
static void radv_prune_prefixes(struct radv_iface *ifa);
44
static void radv_prune_routes(struct radv_proto *p);
45

    
46
/* Invalidate cached RA packet */
47
static inline void radv_invalidate(struct radv_iface *ifa)
48
{ ifa->plen = 0; }
49

    
50
static void
51
radv_timer(timer *tm)
52
{
53
  struct radv_iface *ifa = tm->data;
54
  struct radv_proto *p = ifa->ra;
55
  btime now = current_time();
56

    
57
  RADV_TRACE(D_EVENTS, "Timer fired on %s", ifa->iface->name);
58

    
59
  if (ifa->valid_time <= now)
60
    radv_invalidate(ifa);
61

    
62
  if (ifa->prune_time <= now)
63
    radv_prune_prefixes(ifa);
64

    
65
  if (p->prune_time <= now)
66
    radv_prune_routes(p);
67

    
68
  radv_send_ra(ifa);
69

    
70
  /* Update timer */
71
  ifa->last = now;
72
  btime t = ifa->cf->min_ra_int S;
73
  btime r = (ifa->cf->max_ra_int - ifa->cf->min_ra_int) S;
74
  t += random() % (r + 1);
75

    
76
  if (ifa->initial)
77
  {
78
    t = MIN(t, MAX_INITIAL_RTR_ADVERT_INTERVAL);
79
    ifa->initial--;
80
  }
81

    
82
  tm_start(ifa->timer, t);
83
}
84

    
85
static struct radv_prefix_config default_prefix = {
86
  .onlink = 1,
87
  .autonomous = 1,
88
  .valid_lifetime = DEFAULT_VALID_LIFETIME,
89
  .preferred_lifetime = DEFAULT_PREFERRED_LIFETIME
90
};
91

    
92
static struct radv_prefix_config dead_prefix = {
93
};
94

    
95
/* Find a corresponding config for the given prefix */
96
static struct radv_prefix_config *
97
radv_prefix_match(struct radv_iface *ifa, net_addr_ip6 *px)
98
{
99
  struct radv_proto *p = ifa->ra;
100
  struct radv_config *cf = (struct radv_config *) (p->p.cf);
101
  struct radv_prefix_config *pc;
102

    
103
  WALK_LIST(pc, ifa->cf->pref_list)
104
    if (net_in_net_ip6(px, &pc->prefix))
105
      return pc;
106

    
107
  WALK_LIST(pc, cf->pref_list)
108
    if (net_in_net_ip6(px, &pc->prefix))
109
      return pc;
110

    
111
  return &default_prefix;
112
}
113

    
114
/*
115
 * Go through the list of prefixes, compare them with configs and decide if we
116
 * want them or not.
117
 */
118
static void
119
radv_prepare_prefixes(struct radv_iface *ifa)
120
{
121
  struct radv_proto *p = ifa->ra;
122
  struct radv_prefix *pfx, *next;
123
  btime now = current_time();
124

    
125
  /* First mark all the prefixes as unused */
126
  WALK_LIST(pfx, ifa->prefixes)
127
    pfx->mark = 0;
128

    
129
  /* Find all the prefixes we want to use and make sure they are in the list. */
130
  struct ifa *addr;
131
  WALK_LIST(addr, ifa->iface->addrs)
132
  {
133
    if ((addr->prefix.type != NET_IP6) ||
134
        (addr->scope <= SCOPE_LINK))
135
      continue;
136

    
137
    net_addr_ip6 *prefix = (void *) &addr->prefix;
138
    struct radv_prefix_config *pc = radv_prefix_match(ifa, prefix);
139

    
140
    if (!pc || pc->skip)
141
      continue;
142

    
143
    /* Do we have it already? */
144
    struct radv_prefix *existing = NULL;
145
    WALK_LIST(pfx, ifa->prefixes)
146
      if (net_equal_ip6(&pfx->prefix, prefix))
147
      {
148
        existing = pfx;
149
        break;
150
      }
151

    
152
    if (!existing)
153
    {
154
      RADV_TRACE(D_EVENTS, "Adding new prefix %N on %s",
155
                 prefix, ifa->iface->name);
156

    
157
      existing = mb_allocz(ifa->pool, sizeof *existing);
158
      net_copy_ip6(&existing->prefix, prefix);
159
      add_tail(&ifa->prefixes, NODE existing);
160
    }
161

    
162
    /*
163
     * Update the information (it may have changed, or even bring a prefix back
164
     * to life).
165
     */
166
    existing->valid = 1;
167
    existing->changed = now;
168
    existing->mark = 1;
169
    existing->cf = pc;
170
  }
171

    
172
  WALK_LIST_DELSAFE(pfx, next, ifa->prefixes)
173
  {
174
    if (pfx->valid && !pfx->mark)
175
    {
176
      RADV_TRACE(D_EVENTS, "Invalidating prefix %N on %s",
177
                 pfx->prefix, ifa->iface->name);
178

    
179
      pfx->valid = 0;
180
      pfx->changed = now;
181
      pfx->cf = &dead_prefix;
182
    }
183
  }
184
}
185

    
186
static void
187
radv_prune_prefixes(struct radv_iface *ifa)
188
{
189
  struct radv_proto *p = ifa->ra;
190
  btime now = current_time();
191
  btime next = TIME_INFINITY;
192
  btime expires = 0;
193

    
194
  struct radv_prefix *px, *pxn;
195
  WALK_LIST_DELSAFE(px, pxn, ifa->prefixes)
196
  {
197
    if (!px->valid)
198
    {
199
      expires = px->changed + ifa->cf->prefix_linger_time S;
200

    
201
      if (expires <= now)
202
      {
203
        RADV_TRACE(D_EVENTS, "Removing prefix %N on %s",
204
                   px->prefix, ifa->iface->name);
205

    
206
        rem_node(NODE px);
207
        mb_free(px);
208
      }
209
      else
210
        next = MIN(next, expires);
211
    }
212
  }
213

    
214
  ifa->prune_time = next;
215
}
216

    
217
static char* ev_name[] = { NULL, "Init", "Change", "RS" };
218

    
219
void
220
radv_iface_notify(struct radv_iface *ifa, int event)
221
{
222
  struct radv_proto *p = ifa->ra;
223

    
224
  if (!ifa->sk)
225
    return;
226

    
227
  RADV_TRACE(D_EVENTS, "Event %s on %s", ev_name[event], ifa->iface->name);
228

    
229
  switch (event)
230
  {
231
  case RA_EV_CHANGE:
232
    radv_invalidate(ifa);
233
  case RA_EV_INIT:
234
    ifa->initial = MAX_INITIAL_RTR_ADVERTISEMENTS;
235
    radv_prepare_prefixes(ifa);
236
    radv_prune_prefixes(ifa);
237
    break;
238

    
239
  case RA_EV_RS:
240
    break;
241
  }
242

    
243
  /* Update timer */
244
  btime t = ifa->last + ifa->cf->min_delay S - current_time();
245
  tm_start(ifa->timer, t);
246
}
247

    
248
static void
249
radv_iface_notify_all(struct radv_proto *p, int event)
250
{
251
  struct radv_iface *ifa;
252

    
253
  WALK_LIST(ifa, p->iface_list)
254
    radv_iface_notify(ifa, event);
255
}
256

    
257
static struct radv_iface *
258
radv_iface_find(struct radv_proto *p, struct iface *what)
259
{
260
  struct radv_iface *ifa;
261

    
262
  WALK_LIST(ifa, p->iface_list)
263
    if (ifa->iface == what)
264
      return ifa;
265

    
266
  return NULL;
267
}
268

    
269
static void
270
radv_iface_add(struct object_lock *lock)
271
{
272
  struct radv_iface *ifa = lock->data;
273
  struct radv_proto *p = ifa->ra;
274

    
275
  if (! radv_sk_open(ifa))
276
  {
277
    log(L_ERR "%s: Socket open failed on interface %s", p->p.name, ifa->iface->name);
278
    return;
279
  }
280

    
281
  radv_iface_notify(ifa, RA_EV_INIT);
282
}
283

    
284
static void
285
radv_iface_new(struct radv_proto *p, struct iface *iface, struct radv_iface_config *cf)
286
{
287
  struct radv_iface *ifa;
288

    
289
  RADV_TRACE(D_EVENTS, "Adding interface %s", iface->name);
290

    
291
  pool *pool = rp_new(p->p.pool, iface->name);
292
  ifa = mb_allocz(pool, sizeof(struct radv_iface));
293
  ifa->pool = pool;
294
  ifa->ra = p;
295
  ifa->cf = cf;
296
  ifa->iface = iface;
297
  ifa->addr = iface->llv6;
298
  init_list(&ifa->prefixes);
299
  ifa->prune_time = TIME_INFINITY;
300

    
301
  add_tail(&p->iface_list, NODE ifa);
302

    
303
  ifa->timer = tm_new_init(pool, radv_timer, ifa, 0, 0);
304

    
305
  struct object_lock *lock = olock_new(pool);
306
  lock->type = OBJLOCK_IP;
307
  lock->port = ICMPV6_PROTO;
308
  lock->iface = iface;
309
  lock->data = ifa;
310
  lock->hook = radv_iface_add;
311
  ifa->lock = lock;
312

    
313
  olock_acquire(lock);
314
}
315

    
316
static void
317
radv_iface_remove(struct radv_iface *ifa)
318
{
319
  struct radv_proto *p = ifa->ra;
320
  RADV_TRACE(D_EVENTS, "Removing interface %s", ifa->iface->name);
321

    
322
  rem_node(NODE ifa);
323

    
324
  rfree(ifa->pool);
325
}
326

    
327
static void
328
radv_if_notify(struct proto *P, unsigned flags, struct iface *iface)
329
{
330
  struct radv_proto *p = (struct radv_proto *) P;
331
  struct radv_config *cf = (struct radv_config *) (P->cf);
332

    
333
  if (iface->flags & IF_IGNORE)
334
    return;
335

    
336
  if (flags & IF_CHANGE_UP)
337
  {
338
    struct radv_iface_config *ic = (void *) iface_patt_find(&cf->patt_list, iface, NULL);
339

    
340
    /* Ignore non-multicast ifaces */
341
    if (!(iface->flags & IF_MULTICAST))
342
      return;
343

    
344
    /* Ignore ifaces without link-local address */
345
    if (!iface->llv6)
346
      return;
347

    
348
    if (ic)
349
      radv_iface_new(p, iface, ic);
350

    
351
    return;
352
  }
353

    
354
  struct radv_iface *ifa = radv_iface_find(p, iface);
355
  if (!ifa)
356
    return;
357

    
358
  if (flags & IF_CHANGE_DOWN)
359
  {
360
    radv_iface_remove(ifa);
361
    return;
362
  }
363

    
364
  if ((flags & IF_CHANGE_LINK) && (iface->flags & IF_LINK_UP))
365
    radv_iface_notify(ifa, RA_EV_INIT);
366
}
367

    
368
static void
369
radv_ifa_notify(struct proto *P, unsigned flags UNUSED, struct ifa *a)
370
{
371
  struct radv_proto *p = (struct radv_proto *) P;
372

    
373
  if (a->flags & IA_SECONDARY)
374
    return;
375

    
376
  if (a->scope <= SCOPE_LINK)
377
    return;
378

    
379
  struct radv_iface *ifa = radv_iface_find(p, a->iface);
380

    
381
  if (ifa)
382
    radv_iface_notify(ifa, RA_EV_CHANGE);
383
}
384

    
385
static inline int
386
radv_trigger_valid(struct radv_config *cf)
387
{
388
  return cf->trigger.type != 0;
389
}
390

    
391
static inline int
392
radv_net_match_trigger(struct radv_config *cf, net *n)
393
{
394
  return radv_trigger_valid(cf) && net_equal(n->n.addr, &cf->trigger);
395
}
396

    
397
int
398
radv_import_control(struct proto *P, rte **new, ea_list **attrs UNUSED, struct linpool *pool UNUSED)
399
{
400
  // struct radv_proto *p = (struct radv_proto *) P;
401
  struct radv_config *cf = (struct radv_config *) (P->cf);
402

    
403
  if (radv_net_match_trigger(cf, (*new)->net))
404
    return RIC_PROCESS;
405

    
406
  if (cf->propagate_routes)
407
    return RIC_PROCESS;
408
  else
409
    return RIC_DROP;
410
}
411

    
412
static void
413
radv_rt_notify(struct proto *P, struct channel *ch UNUSED, net *n, rte *new, rte *old UNUSED, ea_list *attrs)
414
{
415
  struct radv_proto *p = (struct radv_proto *) P;
416
  struct radv_config *cf = (struct radv_config *) (P->cf);
417
  struct radv_route *rt;
418
  eattr *ea;
419

    
420
  if (radv_net_match_trigger(cf, n))
421
  {
422
    u8 old_active = p->active;
423
    p->active = !!new;
424

    
425
    if (p->active == old_active)
426
      return;
427

    
428
    if (p->active)
429
      RADV_TRACE(D_EVENTS, "Triggered");
430
    else
431
      RADV_TRACE(D_EVENTS, "Suppressed");
432

    
433
    radv_iface_notify_all(p, RA_EV_CHANGE);
434
    return;
435
  }
436

    
437
  if (!cf->propagate_routes)
438
    return;
439

    
440
  /*
441
   * Some other route we want to send (or stop sending). Update the cache,
442
   * with marking a removed one as dead or creating a new one as needed.
443
   *
444
   * And yes, we exclude the trigger route on purpose.
445
   */
446

    
447
  if (new)
448
  {
449
    /* Update */
450

    
451
    ea = ea_find(attrs, EA_RA_PREFERENCE);
452
    uint preference = ea ? ea->u.data : RA_PREF_MEDIUM;
453
    uint preference_set = !!ea;
454

    
455
    ea = ea_find(attrs, EA_RA_LIFETIME);
456
    uint lifetime = ea ? ea->u.data : 0;
457
    uint lifetime_set = !!ea;
458

    
459
    if ((preference != RA_PREF_LOW) &&
460
        (preference != RA_PREF_MEDIUM) &&
461
        (preference != RA_PREF_HIGH))
462
    {
463
      log(L_WARN "%s: Invalid ra_preference value %u on route %N",
464
          p->p.name, preference, n->n.addr);
465
      preference = RA_PREF_MEDIUM;
466
      preference_set = 1;
467
      lifetime = 0;
468
      lifetime_set = 1;
469
    }
470

    
471
    rt = fib_get(&p->routes, n->n.addr);
472

    
473
    /* Ignore update if nothing changed */
474
    if (rt->valid &&
475
        (rt->preference == preference) &&
476
        (rt->preference_set == preference_set) &&
477
        (rt->lifetime == lifetime) &&
478
        (rt->lifetime_set == lifetime_set))
479
      return;
480

    
481
    if (p->routes.entries == 18)
482
      log(L_WARN "%s: More than 17 routes exported to RAdv", p->p.name);
483

    
484
    rt->valid = 1;
485
    rt->changed = current_time();
486
    rt->preference = preference;
487
    rt->preference_set = preference_set;
488
    rt->lifetime = lifetime;
489
    rt->lifetime_set = lifetime_set;
490
  }
491
  else
492
  {
493
    /* Withdraw */
494
    rt = fib_find(&p->routes, n->n.addr);
495

    
496
    if (!rt || !rt->valid)
497
      return;
498

    
499
    /* Invalidate the route */
500
    rt->valid = 0;
501
    rt->changed = current_time();
502

    
503
    /* Invalidated route will be pruned eventually */
504
    btime expires = rt->changed + cf->max_linger_time S;
505
    p->prune_time = MIN(p->prune_time, expires);
506
  }
507

    
508
  radv_iface_notify_all(p, RA_EV_CHANGE);
509
}
510

    
511
/*
512
 * Cleans up all the dead routes that expired and schedules itself to be run
513
 * again if there are more routes waiting for expiration.
514
 */
515
static void
516
radv_prune_routes(struct radv_proto *p)
517
{
518
  struct radv_config *cf = (struct radv_config *) (p->p.cf);
519
  btime now = current_time();
520
  btime next = TIME_INFINITY;
521
  btime expires = 0;
522

    
523
  /* Should not happen */
524
  if (!p->fib_up)
525
    return;
526

    
527
  struct fib_iterator fit;
528
  FIB_ITERATE_INIT(&fit, &p->routes);
529

    
530
again:
531
  FIB_ITERATE_START(&p->routes, &fit, struct radv_route, rt)
532
  {
533
    if (!rt->valid)
534
    {
535
      expires = rt->changed + cf->max_linger_time S;
536

    
537
      /* Delete expired nodes */
538
      if (expires <= now)
539
      {
540
        FIB_ITERATE_PUT(&fit);
541
        fib_delete(&p->routes, rt);
542
        goto again;
543
      }
544
      else
545
        next = MIN(next, expires);
546
    }
547
  }
548
  FIB_ITERATE_END;
549

    
550
  p->prune_time = next;
551
}
552

    
553
static int
554
radv_check_active(struct radv_proto *p)
555
{
556
  struct radv_config *cf = (struct radv_config *) (p->p.cf);
557

    
558
  if (!radv_trigger_valid(cf))
559
    return 1;
560

    
561
  struct channel *c = p->p.main_channel;
562
  return rt_examine(c->table, &cf->trigger, &p->p, c->out_filter);
563
}
564

    
565
static void
566
radv_postconfig(struct proto_config *CF)
567
{
568
  // struct radv_config *cf = (void *) CF;
569

    
570
  /* Define default channel */
571
  if (EMPTY_LIST(CF->channels))
572
    channel_config_new(NULL, net_label[NET_IP6], NET_IP6, CF);
573
}
574

    
575
static struct proto *
576
radv_init(struct proto_config *CF)
577
{
578
  struct proto *P = proto_new(CF);
579

    
580
  P->main_channel = proto_add_channel(P, proto_cf_main_channel(CF));
581

    
582
  P->import_control = radv_import_control;
583
  P->rt_notify = radv_rt_notify;
584
  P->if_notify = radv_if_notify;
585
  P->ifa_notify = radv_ifa_notify;
586

    
587
  return P;
588
}
589

    
590
static void
591
radv_set_fib(struct radv_proto *p, int up)
592
{
593
  if (up == p->fib_up)
594
    return;
595

    
596
  if (up)
597
    fib_init(&p->routes, p->p.pool, NET_IP6, sizeof(struct radv_route),
598
             OFFSETOF(struct radv_route, n), 4, NULL);
599
  else
600
    fib_free(&p->routes);
601

    
602
  p->fib_up = up;
603
  p->prune_time = TIME_INFINITY;
604
}
605

    
606
static int
607
radv_start(struct proto *P)
608
{
609
  struct radv_proto *p = (struct radv_proto *) P;
610
  struct radv_config *cf = (struct radv_config *) (P->cf);
611

    
612
  init_list(&(p->iface_list));
613
  p->valid = 1;
614
  p->active = !radv_trigger_valid(cf);
615

    
616
  p->fib_up = 0;
617
  radv_set_fib(p, cf->propagate_routes);
618
  p->prune_time = TIME_INFINITY;
619

    
620
  return PS_UP;
621
}
622

    
623
static inline void
624
radv_iface_shutdown(struct radv_iface *ifa)
625
{
626
  if (ifa->sk)
627
  {
628
    radv_invalidate(ifa);
629
    radv_send_ra(ifa);
630
  }
631
}
632

    
633
static int
634
radv_shutdown(struct proto *P)
635
{
636
  struct radv_proto *p = (struct radv_proto *) P;
637

    
638
  p->valid = 0;
639

    
640
  struct radv_iface *ifa;
641
  WALK_LIST(ifa, p->iface_list)
642
    radv_iface_shutdown(ifa);
643

    
644
  return PS_DOWN;
645
}
646

    
647
static int
648
radv_reconfigure(struct proto *P, struct proto_config *CF)
649
{
650
  struct radv_proto *p = (struct radv_proto *) P;
651
  struct radv_config *old = (struct radv_config *) (P->cf);
652
  struct radv_config *new = (struct radv_config *) CF;
653

    
654
  if (!proto_configure_channel(P, &P->main_channel, proto_cf_main_channel(CF)))
655
    return 0;
656

    
657
  P->cf = CF; /* radv_check_active() requires proper P->cf */
658
  p->active = radv_check_active(p);
659

    
660
  /* Allocate or free FIB */
661
  radv_set_fib(p, new->propagate_routes);
662

    
663
  /* We started to accept routes so we need to refeed them */
664
  if (!old->propagate_routes && new->propagate_routes)
665
    channel_request_feeding(p->p.main_channel);
666

    
667
  struct iface *iface;
668
  WALK_LIST(iface, iface_list)
669
  {
670
    if (!(iface->flags & IF_UP))
671
      continue;
672

    
673
    /* Ignore non-multicast ifaces */
674
    if (!(iface->flags & IF_MULTICAST))
675
      continue;
676

    
677
    /* Ignore ifaces without link-local address */
678
    if (!iface->llv6)
679
      continue;
680

    
681
    struct radv_iface *ifa = radv_iface_find(p, iface);
682
    struct radv_iface_config *ic = (struct radv_iface_config *)
683
      iface_patt_find(&new->patt_list, iface, NULL);
684

    
685
    if (ifa && ic)
686
    {
687
      ifa->cf = ic;
688

    
689
      /* We cheat here - always notify the change even if there isn't
690
         any. That would leads just to a few unnecessary RAs. */
691
      radv_iface_notify(ifa, RA_EV_CHANGE);
692
    }
693

    
694
    if (ifa && !ic)
695
    {
696
      radv_iface_shutdown(ifa);
697
      radv_iface_remove(ifa);
698
    }
699

    
700
    if (!ifa && ic)
701
      radv_iface_new(p, iface, ic);
702
  }
703

    
704
  return 1;
705
}
706

    
707
static void
708
radv_copy_config(struct proto_config *dest, struct proto_config *src)
709
{
710
  struct radv_config *d = (struct radv_config *) dest;
711
  struct radv_config *s = (struct radv_config *) src;
712

    
713
  /* We clean up patt_list, ifaces are non-sharable */
714
  init_list(&d->patt_list);
715

    
716
  /* We copy pref_list, shallow copy suffices */
717
  cfg_copy_list(&d->pref_list, &s->pref_list, sizeof(struct radv_prefix_config));
718
}
719

    
720
static void
721
radv_get_status(struct proto *P, byte *buf)
722
{
723
  struct radv_proto *p = (struct radv_proto *) P;
724

    
725
  if (!p->active)
726
    strcpy(buf, "Suppressed");
727
}
728

    
729
static const char *
730
radv_pref_str(u32 pref)
731
{
732
  switch (pref)
733
  {
734
    case RA_PREF_LOW:
735
      return "low";
736
    case RA_PREF_MEDIUM:
737
      return "medium";
738
    case RA_PREF_HIGH:
739
      return "high";
740
    default:
741
      return "??";
742
  }
743
}
744

    
745
/* The buffer has some minimal size */
746
static int
747
radv_get_attr(eattr *a, byte *buf, int buflen UNUSED)
748
{
749
  switch (a->id)
750
  {
751
  case EA_RA_PREFERENCE:
752
    bsprintf(buf, "preference: %s", radv_pref_str(a->u.data));
753
    return GA_FULL;
754
  case EA_RA_LIFETIME:
755
    bsprintf(buf, "lifetime");
756
    return GA_NAME;
757
  default:
758
    return GA_UNKNOWN;
759
  }
760
}
761

    
762
struct protocol proto_radv = {
763
  .name =                "RAdv",
764
  .template =                "radv%d",
765
  .attr_class =                EAP_RADV,
766
  .channel_mask =        NB_IP6,
767
  .proto_size =                sizeof(struct radv_proto),
768
  .config_size =        sizeof(struct radv_config),
769
  .postconfig =                radv_postconfig,
770
  .init =                radv_init,
771
  .start =                radv_start,
772
  .shutdown =                radv_shutdown,
773
  .reconfigure =        radv_reconfigure,
774
  .copy_config =        radv_copy_config,
775
  .get_status =                radv_get_status,
776
  .get_attr =                radv_get_attr
777
};