Statistics
| Branch: | Revision:

iof-bird-daemon / sysdep / unix / krt.c @ f7fcb752

History | View | Annotate | Download (16.3 KB)

1
/*
2
 *        BIRD -- UNIX Kernel Synchronization
3
 *
4
 *        (c) 1998--2000 Martin Mares <mj@ucw.cz>
5
 *
6
 *        Can be freely distributed and used under the terms of the GNU GPL.
7
 */
8

    
9
#define LOCAL_DEBUG
10

    
11
#include "nest/bird.h"
12
#include "nest/iface.h"
13
#include "nest/route.h"
14
#include "nest/protocol.h"
15
#include "lib/timer.h"
16
#include "conf/conf.h"
17

    
18
#include "unix.h"
19
#include "krt.h"
20

    
21
/*
22
 *  The whole kernel synchronization is a bit messy and touches some internals
23
 *  of the routing table engine, because routing table maintenance is a typical
24
 *  example of the proverbial compatibility between different Unices and we want
25
 *  to keep the overhead of our krt business as low as possible and avoid maintaining
26
 *  a local routing table copy.
27
 *
28
 *  The kernel syncer can work in three different modes (according to system config header):
29
 *        o  Single routing table, single krt protocol.  [traditional Unix]
30
 *        o  Many routing tables, separate krt protocols for all of them.
31
 *        o  Many routing tables, but every scan includes all tables, so we start
32
 *           separate krt protocols which cooperate with each other.  [Linux 2.2]
33
 *           In this case, we keep only a single scan timer.
34
 *
35
 *  The hacky bits:
36
 *        o  We use FIB node flags to keep track of route synchronization status.
37
 *        o  When starting up, we cheat by looking if there is another kernel
38
 *           krt instance to be initialized later and performing table scan
39
 *           only once for all the instances.
40
 *        o  We attach temporary rte's to routing tables.
41
 *
42
 *  If you are brave enough, continue now.  You cannot say you haven't been warned.
43
 */
44

    
45
static int krt_uptodate(rte *k, rte *e);
46

    
47
/*
48
 *        Global resources
49
 */
50

    
51
pool *krt_pool;
52

    
53
void
54
krt_io_init(void)
55
{
56
  krt_pool = rp_new(&root_pool, "Kernel Syncer");
57
  krt_if_io_init();
58
}
59

    
60
/*
61
 *        Interfaces
62
 */
63

    
64
struct proto_config *cf_kif;
65

    
66
static struct kif_proto *kif_proto;
67
static timer *kif_scan_timer;
68
static bird_clock_t kif_last_shot;
69

    
70
static void
71
kif_preconfig(struct protocol *P, struct config *c)
72
{
73
  cf_kif = NULL;
74
}
75

    
76
static void
77
kif_scan(timer *t)
78
{
79
  struct kif_proto *p = t->data;
80

    
81
  DBG("KIF: It's interface scan time...\n");
82
  kif_last_shot = now;
83
  krt_if_scan(p);
84
}
85

    
86
static void
87
kif_force_scan(void)
88
{
89
  if (kif_proto && kif_last_shot + 2 < now)
90
    {
91
      kif_scan(kif_scan_timer);
92
      tm_start(kif_scan_timer, ((struct kif_config *) kif_proto->p.cf)->scan_time);
93
    }
94
}
95

    
96
static struct proto *
97
kif_init(struct proto_config *c)
98
{
99
  struct kif_proto *p = proto_new(c, sizeof(struct kif_proto));
100
  return &p->p;
101
}
102

    
103
static int
104
kif_start(struct proto *P)
105
{
106
  struct kif_proto *p = (struct kif_proto *) P;
107

    
108
  kif_proto = p;
109
  krt_if_start(p);
110

    
111
  /* Start periodic interface scanning */
112
  kif_scan_timer = tm_new(P->pool);
113
  kif_scan_timer->hook = kif_scan;
114
  kif_scan_timer->data = p;
115
  kif_scan_timer->recurrent = KIF_CF->scan_time;
116
  kif_scan(kif_scan_timer);
117
  tm_start(kif_scan_timer, KIF_CF->scan_time);
118

    
119
  return PS_UP;
120
}
121

    
122
static int
123
kif_shutdown(struct proto *P)
124
{
125
  struct kif_proto *p = (struct kif_proto *) P;
126

    
127
  tm_stop(kif_scan_timer);
128
  krt_if_shutdown(p);
129
  kif_proto = NULL;
130

    
131
  if_start_update();        /* Remove all interfaces */
132
  if_end_update();
133
  /*
134
   *  FIXME: Is it really a good idea?  It causes routes to be flushed,
135
   *  but at the same time it avoids sending of these deletions to the kernel,
136
   *  because krt thinks the kernel itself has already removed the route
137
   *  when downing the interface.  Sad.
138
   */
139

    
140
  return PS_DOWN;
141
}
142

    
143
static int
144
kif_reconfigure(struct proto *p, struct proto_config *new)
145
{
146
  struct kif_config *o = (struct kif_config *) p->cf;
147
  struct kif_config *n = (struct kif_config *) new;
148

    
149
  if (!kif_params_same(&o->iface, &n->iface))
150
    return 0;
151
  if (o->scan_time != n->scan_time)
152
    {
153
      tm_stop(kif_scan_timer);
154
      kif_scan_timer->recurrent = n->scan_time;
155
      kif_scan(kif_scan_timer);
156
      tm_start(kif_scan_timer, n->scan_time);
157
    }
158
  return 1;
159
}
160

    
161
struct protocol proto_unix_iface = {
162
  name:                "Device",
163
  priority:        100,
164
  preconfig:        kif_preconfig,
165
  init:                kif_init,
166
  start:        kif_start,
167
  shutdown:        kif_shutdown,
168
  reconfigure:        kif_reconfigure,
169
};
170

    
171
/*
172
 *        Inherited Routes
173
 */
174

    
175
#ifdef KRT_ALLOW_LEARN
176

    
177
static inline int
178
krt_same_key(rte *a, rte *b)
179
{
180
  return a->u.krt.proto == b->u.krt.proto &&
181
         a->u.krt.metric == b->u.krt.metric &&
182
         a->u.krt.type == b->u.krt.type;
183
}
184

    
185
static void
186
krt_learn_announce_update(struct krt_proto *p, rte *e)
187
{
188
  net *n = e->net;
189
  rta *aa = rta_clone(e->attrs);
190
  rte *ee = rte_get_temp(aa);
191
  net *nn = net_get(p->p.table, n->n.prefix, n->n.pxlen);
192
  ee->net = nn;
193
  ee->pflags = 0;
194
  ee->u.krt = e->u.krt;
195
  rte_update(p->p.table, nn, &p->p, ee);
196
}
197

    
198
static void
199
krt_learn_announce_delete(struct krt_proto *p, net *n)
200
{
201
  n = net_find(p->p.table, n->n.prefix, n->n.pxlen);
202
  if (n)
203
    rte_update(p->p.table, n, &p->p, NULL);
204
}
205

    
206
static void
207
krt_learn_scan(struct krt_proto *p, rte *e)
208
{
209
  net *n0 = e->net;
210
  net *n = net_get(&p->krt_table, n0->n.prefix, n0->n.pxlen);
211
  rte *m, **mm;
212

    
213
  e->attrs->source = RTS_INHERIT;
214

    
215
  for(mm=&n->routes; m = *mm; mm=&m->next)
216
    if (krt_same_key(m, e))
217
      break;
218
  if (m)
219
    {
220
      if (krt_uptodate(m, e))
221
        {
222
          DBG("krt_learn_scan: SEEN\n");
223
          rte_free(e);
224
          m->u.krt.seen = 1;
225
        }
226
      else
227
        {
228
          DBG("krt_learn_scan: OVERRIDE\n");
229
          *mm = m->next;
230
          rte_free(m);
231
          m = NULL;
232
        }
233
    }
234
  else
235
    DBG("krt_learn_scan: CREATE\n");
236
  if (!m)
237
    {
238
      e->attrs = rta_lookup(e->attrs);
239
      e->next = n->routes;
240
      n->routes = e;
241
      e->u.krt.seen = 1;
242
    }
243
}
244

    
245
static void
246
krt_learn_prune(struct krt_proto *p)
247
{
248
  struct fib *fib = &p->krt_table.fib;
249
  struct fib_iterator fit;
250

    
251
  DBG("Pruning inheritance data...\n");
252

    
253
  FIB_ITERATE_INIT(&fit, fib);
254
again:
255
  FIB_ITERATE_START(fib, &fit, f)
256
    {
257
      net *n = (net *) f;
258
      rte *e, **ee, *best, **pbest, *old_best;
259

    
260
      old_best = n->routes;
261
      best = NULL;
262
      pbest = NULL;
263
      ee = &n->routes;
264
      while (e = *ee)
265
        {
266
          if (!e->u.krt.seen)
267
            {
268
              *ee = e->next;
269
              rte_free(e);
270
              continue;
271
            }
272
          if (!best || best->u.krt.metric > e->u.krt.metric)
273
            {
274
              best = e;
275
              pbest = ee;
276
            }
277
          e->u.krt.seen = 0;
278
          ee = &e->next;
279
        }
280
      if (!n->routes)
281
        {
282
          DBG("%I/%d: deleting\n", n->n.prefix, n->n.pxlen);
283
          if (old_best)
284
            {
285
              krt_learn_announce_delete(p, n);
286
              n->n.flags &= ~KRF_INSTALLED;
287
            }
288
          FIB_ITERATE_PUT(&fit, f);
289
          fib_delete(fib, f);
290
          goto again;
291
        }
292
      *pbest = best->next;
293
      best->next = n->routes;
294
      n->routes = best;
295
      if (best != old_best || !(n->n.flags & KRF_INSTALLED))
296
        {
297
          DBG("%I/%d: announcing (metric=%d)\n", n->n.prefix, n->n.pxlen, best->u.krt.metric);
298
          krt_learn_announce_update(p, best);
299
          n->n.flags |= KRF_INSTALLED;
300
        }
301
      else
302
        DBG("%I/%d: uptodate (metric=%d)\n", n->n.prefix, n->n.pxlen, best->u.krt.metric);
303
    }
304
  FIB_ITERATE_END(f);
305
}
306

    
307
static void
308
krt_learn_async(struct krt_proto *p, rte *e, int new)
309
{
310
  net *n0 = e->net;
311
  net *n = net_get(&p->krt_table, n0->n.prefix, n0->n.pxlen);
312
  rte *g, **gg, *best, **bestp, *old_best;
313

    
314
  e->attrs->source = RTS_INHERIT;
315

    
316
  old_best = n->routes;
317
  for(gg=&n->routes; g = *gg; gg = &g->next)
318
    if (krt_same_key(g, e))
319
      break;
320
  if (new)
321
    {
322
      if (g)
323
        {
324
          if (krt_uptodate(g, e))
325
            {
326
              DBG("krt_learn_async: same\n");
327
              rte_free(e);
328
              return;
329
            }
330
          DBG("krt_learn_async: update\n");
331
          *gg = g->next;
332
          rte_free(g);
333
        }
334
      else
335
        DBG("krt_learn_async: create\n");
336
      e->attrs = rta_lookup(e->attrs);
337
      e->next = n->routes;
338
      n->routes = e;
339
    }
340
  else if (!g)
341
    {
342
      DBG("krt_learn_async: not found\n");
343
      rte_free(e);
344
      return;
345
    }
346
  else
347
    {
348
      DBG("krt_learn_async: delete\n");
349
      *gg = g->next;
350
      rte_free(e);
351
      rte_free(g);
352
    }
353
  best = n->routes;
354
  bestp = &n->routes;
355
  for(gg=&n->routes; g=*gg; gg=&g->next)
356
    if (best->u.krt.metric > g->u.krt.metric)
357
      {
358
        best = g;
359
        bestp = gg;
360
      }
361
  if (best)
362
    {
363
      *bestp = best->next;
364
      best->next = n->routes;
365
      n->routes = best;
366
    }
367
  if (best != old_best)
368
    {
369
      DBG("krt_learn_async: distributing change\n");
370
      if (best)
371
        {
372
          krt_learn_announce_update(p, best);
373
          n->n.flags |= KRF_INSTALLED;
374
        }
375
      else
376
        {
377
          n->routes = NULL;
378
          krt_learn_announce_delete(p, n);
379
          n->n.flags &= ~KRF_INSTALLED;
380
        }
381
    }
382
}
383

    
384
static void
385
krt_learn_init(struct krt_proto *p)
386
{
387
  if (KRT_CF->learn)
388
    rt_setup(p->p.pool, &p->krt_table, "Inherited");
389
}
390

    
391
static void
392
krt_dump(struct proto *P)
393
{
394
  struct krt_proto *p = (struct krt_proto *) P;
395

    
396
  if (!KRT_CF->learn)
397
    return;
398
  debug("KRT: Table of inheritable routes\n");
399
  rt_dump(&p->krt_table);
400
}
401

    
402
static void
403
krt_dump_attrs(rte *e)
404
{
405
  debug(" [m=%d,p=%d,t=%d]", e->u.krt.metric, e->u.krt.proto, e->u.krt.type);
406
}
407

    
408
#endif
409

    
410
/*
411
 *        Routes
412
 */
413

    
414
#ifdef CONFIG_ALL_TABLES_AT_ONCE
415
static timer *krt_scan_timer;
416
static int krt_instance_count;
417
static list krt_instance_list;
418
#endif
419

    
420
static void
421
krt_flush_routes(struct krt_proto *p)
422
{
423
  struct rtable *t = p->p.table;
424

    
425
  DBG("Flushing kernel routes...\n");
426
  FIB_WALK(&t->fib, f)
427
    {
428
      net *n = (net *) f;
429
      rte *e = n->routes;
430
      if (e)
431
        {
432
          rta *a = e->attrs;
433
          if (a->source != RTS_DEVICE && a->source != RTS_INHERIT)
434
            krt_set_notify(p, e->net, NULL, e);
435
        }
436
    }
437
  FIB_WALK_END;
438
}
439

    
440
static int
441
krt_uptodate(rte *k, rte *e)
442
{
443
  rta *ka = k->attrs, *ea = e->attrs;
444

    
445
  if (ka->dest != ea->dest)
446
    return 0;
447
  switch (ka->dest)
448
    {
449
    case RTD_ROUTER:
450
      return ipa_equal(ka->gw, ea->gw);
451
    case RTD_DEVICE:
452
      return !strcmp(ka->iface->name, ea->iface->name);
453
    default:
454
      return 1;
455
    }
456
}
457

    
458
/*
459
 *  This gets called back when the low-level scanning code discovers a route.
460
 *  We expect that the route is a temporary rte and its attributes are uncached.
461
 */
462

    
463
void
464
krt_got_route(struct krt_proto *p, rte *e)
465
{
466
  rte *old;
467
  net *net = e->net;
468
  int src = e->u.krt.src;
469
  int verdict;
470

    
471
#ifdef CONFIG_AUTO_ROUTES
472
  if (e->attrs->dest == RTD_DEVICE)
473
    {
474
      /* It's a device route. Probably a kernel-generated one. */
475
      verdict = KRF_IGNORE;
476
      goto sentenced;
477
    }
478
#endif
479

    
480
#ifdef KRT_ALLOW_LEARN
481
  if (src == KRT_SRC_ALIEN)
482
    {
483
      if (KRT_CF->learn)
484
        krt_learn_scan(p, e);
485
      else
486
        DBG("krt_parse_entry: Alien route, ignoring\n");
487
      return;
488
    }
489
#endif
490

    
491
  if (net->n.flags & KRF_VERDICT_MASK)
492
    {
493
      /* Route to this destination was already seen. Strange, but it happens... */
494
      DBG("Already seen.\n");
495
      return;
496
    }
497

    
498
  if (net->n.flags & KRF_INSTALLED)
499
    {
500
      old = net->routes;
501
      ASSERT(old);
502
      if (krt_uptodate(e, old))
503
        verdict = KRF_SEEN;
504
      else
505
        verdict = KRF_UPDATE;
506
    }
507
  else
508
    verdict = KRF_DELETE;
509

    
510
sentenced:
511
  DBG("krt_parse_entry: verdict=%s\n", ((char *[]) { "CREATE", "SEEN", "UPDATE", "DELETE", "IGNORE" }) [verdict]);
512

    
513
  net->n.flags = (net->n.flags & ~KRF_VERDICT_MASK) | verdict;
514
  if (verdict == KRF_UPDATE || verdict == KRF_DELETE)
515
    {
516
      /* Get a cached copy of attributes and link the route */
517
      rta *a = e->attrs;
518
      a->source = RTS_DUMMY;
519
      e->attrs = rta_lookup(a);
520
      e->next = net->routes;
521
      net->routes = e;
522
    }
523
  else
524
    rte_free(e);
525
}
526

    
527
static void
528
krt_prune(struct krt_proto *p)
529
{
530
  struct proto *pp = &p->p;
531
  struct rtable *t = p->p.table;
532
  struct fib_node *f;
533

    
534
  DBG("Pruning routes in table %s...\n", t->name);
535
  FIB_WALK(&t->fib, f)
536
    {
537
      net *n = (net *) f;
538
      int verdict = f->flags & KRF_VERDICT_MASK;
539
      rte *new, *old;
540

    
541
      if (verdict != KRF_CREATE && verdict != KRF_SEEN && verdict != KRF_IGNORE)
542
        {
543
          old = n->routes;
544
          n->routes = old->next;
545
        }
546
      else
547
        old = NULL;
548
      new = n->routes;
549

    
550
      switch (verdict)
551
        {
552
        case KRF_CREATE:
553
          if (new && (f->flags & KRF_INSTALLED))
554
            {
555
              DBG("krt_prune: reinstalling %I/%d\n", n->n.prefix, n->n.pxlen);
556
              krt_set_notify(p, n, new, NULL);
557
            }
558
          break;
559
        case KRF_SEEN:
560
        case KRF_IGNORE:
561
          /* Nothing happens */
562
          break;
563
        case KRF_UPDATE:
564
          DBG("krt_prune: updating %I/%d\n", n->n.prefix, n->n.pxlen);
565
          krt_set_notify(p, n, new, old);
566
          break;
567
        case KRF_DELETE:
568
          DBG("krt_prune: deleting %I/%d\n", n->n.prefix, n->n.pxlen);
569
          krt_set_notify(p, n, NULL, old);
570
          break;
571
        default:
572
          bug("krt_prune: invalid route status");
573
        }
574
      if (old)
575
        rte_free(old);
576
      f->flags &= ~KRF_VERDICT_MASK;
577
    }
578
  FIB_WALK_END;
579

    
580
#ifdef KRT_ALLOW_LEARN
581
  if (KRT_CF->learn)
582
    krt_learn_prune(p);
583
#endif
584
}
585

    
586
void
587
krt_got_route_async(struct krt_proto *p, rte *e, int new)
588
{
589
  net *net = e->net;
590
  rte *old = net->routes;
591
  int src = e->u.krt.src;
592

    
593
  switch (src)
594
    {
595
    case KRT_SRC_BIRD:
596
      ASSERT(0);
597
    case KRT_SRC_REDIRECT:
598
      DBG("It's a redirect, kill him! Kill! Kill!\n");
599
      krt_set_notify(p, net, NULL, e);
600
      break;
601
    case KRT_SRC_ALIEN:
602
#ifdef KRT_ALLOW_LEARN
603
      if (KRT_CF->learn)
604
        {
605
          krt_learn_async(p, e, new);
606
          return;
607
        }
608
#endif
609
      /* Fall-thru */
610
    default:
611
      DBG("Discarding\n");
612
      rte_update(p->p.table, net, &p->p, NULL);
613
    }
614
  rte_free(e);
615
}
616

    
617
/*
618
 *        Periodic scanning
619
 */
620

    
621
static void
622
krt_scan(timer *t)
623
{
624
  struct krt_proto *p;
625

    
626
  kif_force_scan();
627
#ifdef CONFIG_ALL_TABLES_AT_ONCE
628
  {
629
    void *q;
630
    DBG("KRT: It's route scan time...\n");
631
    krt_scan_fire(NULL);
632
    WALK_LIST(q, krt_instance_list)
633
      {
634
        p = SKIP_BACK(struct krt_proto, instance_node, q);
635
        krt_prune(p);
636
      }
637
  }
638
#else
639
  p = t->data;
640
  DBG("KRT: It's route scan time for %s...\n", p->p.name);
641
  krt_scan_fire(p);
642
  krt_prune(p);
643
#endif
644
}
645

    
646
/*
647
 *        Updates
648
 */
649

    
650
static void
651
krt_notify(struct proto *P, net *net, rte *new, rte *old, struct ea_list *tmpa)
652
{
653
  struct krt_proto *p = (struct krt_proto *) P;
654

    
655
  if (new && (!krt_capable(new) || new->attrs->source == RTS_INHERIT))
656
    new = NULL;
657
  if (!(net->n.flags & KRF_INSTALLED))
658
    old = NULL;
659
  if (new)
660
    net->n.flags |= KRF_INSTALLED;
661
  else
662
    net->n.flags &= ~KRF_INSTALLED;
663
  krt_set_notify(p, net, new, old);
664
}
665

    
666
/*
667
 *        Protocol glue
668
 */
669

    
670
struct proto_config *cf_krt;
671

    
672
static void
673
krt_preconfig(struct protocol *P, struct config *c)
674
{
675
  cf_krt = NULL;
676
  krt_scan_preconfig(c);
677
}
678

    
679
static void
680
krt_postconfig(struct proto_config *C)
681
{
682
  struct krt_config *c = (struct krt_config *) C;
683

    
684
#ifdef CONFIG_ALL_TABLES_AT_ONCE
685
  struct krt_config *first = (struct krt_config *) cf_krt;
686
  if (first->scan_time != c->scan_time)
687
    cf_error("All kernel syncers must use the same table scan interval");
688
#endif
689

    
690
  if (C->table->krt_attached)
691
    cf_error("Kernel syncer (%s) already attached to table %s", C->table->krt_attached->name, C->table->name);
692
  C->table->krt_attached = C;
693
  krt_scan_postconfig(c);
694
}
695

    
696
static timer *
697
krt_start_timer(struct krt_proto *p)
698
{
699
  timer *t;
700

    
701
  t = tm_new(p->krt_pool);
702
  t->hook = krt_scan;
703
  t->data = p;
704
  t->recurrent = KRT_CF->scan_time;
705
  tm_start(t, KRT_CF->scan_time);
706
  return t;
707
}
708

    
709
static int
710
krt_start(struct proto *P)
711
{
712
  struct krt_proto *p = (struct krt_proto *) P;
713
  int first = 1;
714

    
715
#ifdef CONFIG_ALL_TABLES_AT_ONCE
716
  if (!krt_instance_count++)
717
    init_list(&krt_instance_list);
718
  else
719
    first = 0;
720
  p->krt_pool = krt_pool;
721
  add_tail(&krt_instance_list, &p->instance_node);
722
#else
723
  p->krt_pool = P->pool;
724
#endif
725

    
726
#ifdef KRT_ALLOW_LEARN
727
  krt_learn_init(p);
728
#endif
729

    
730
  krt_scan_start(p, first);
731
  krt_set_start(p, first);
732

    
733
  /* Start periodic routing table scanning */
734
#ifdef CONFIG_ALL_TABLES_AT_ONCE
735
  if (first)
736
    krt_scan_timer = krt_start_timer(p);
737
  p->scan_timer = krt_scan_timer;
738
  /* If this is the last instance to be initialized, kick the timer */
739
  if (!P->proto->startup_counter)
740
    krt_scan(p->scan_timer);
741
#else
742
  p->scan_timer = krt_start_timer(p);
743
  krt_scan(p->scan_timer);
744
#endif
745

    
746
  return PS_UP;
747
}
748

    
749
static int
750
krt_shutdown(struct proto *P)
751
{
752
  struct krt_proto *p = (struct krt_proto *) P;
753
  int last = 1;
754

    
755
#ifdef CONFIG_ALL_TABLES_AT_ONCE
756
  rem_node(&p->instance_node);
757
  if (--krt_instance_count)
758
    last = 0;
759
  else
760
#endif
761
    tm_stop(p->scan_timer);
762

    
763
  if (!KRT_CF->persist)
764
    krt_flush_routes(p);
765

    
766
  krt_set_shutdown(p, last);
767
  krt_scan_shutdown(p, last);
768

    
769
#ifdef CONFIG_ALL_TABLES_AT_ONCE
770
  if (last)
771
    rfree(krt_scan_timer);
772
#endif
773

    
774
  return PS_DOWN;
775
}
776

    
777
static struct proto *
778
krt_init(struct proto_config *c)
779
{
780
  struct krt_proto *p = proto_new(c, sizeof(struct krt_proto));
781

    
782
  p->p.rt_notify = krt_notify;
783
  p->p.min_scope = SCOPE_HOST;
784
  return &p->p;
785
}
786

    
787
struct protocol proto_unix_kernel = {
788
  name:                "Kernel",
789
  priority:        80,
790
  preconfig:        krt_preconfig,
791
  postconfig:        krt_postconfig,
792
  init:                krt_init,
793
  start:        krt_start,
794
  shutdown:        krt_shutdown,
795
#ifdef KRT_ALLOW_LEARN
796
  dump:                krt_dump,
797
  dump_attrs:        krt_dump_attrs,
798
#endif
799
};