]>
Commit | Line | Data |
---|---|---|
2d140452 MM |
1 | /* |
2 | * BIRD -- UNIX Kernel Synchronization | |
3 | * | |
4 | * (c) 1998--1999 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 | ||
17 | #include "unix.h" | |
18 | #include "krt.h" | |
19 | ||
c10421d3 MM |
20 | static int krt_uptodate(rte *k, rte *e); |
21 | ||
7e5f5ffd MM |
22 | /* |
23 | * Global resources | |
24 | */ | |
25 | ||
26 | void | |
27 | krt_io_init(void) | |
28 | { | |
29 | krt_if_io_init(); | |
30 | } | |
31 | ||
32 | /* | |
33 | * Interfaces | |
34 | */ | |
35 | ||
36 | struct proto_config *cf_kif; | |
37 | ||
38 | static struct kif_proto *kif_proto; | |
39 | static timer *kif_scan_timer; | |
40 | static bird_clock_t kif_last_shot; | |
41 | ||
42 | static void | |
43 | kif_scan(timer *t) | |
44 | { | |
45 | struct kif_proto *p = t->data; | |
46 | ||
47 | DBG("KIF: It's interface scan time...\n"); | |
48 | kif_last_shot = now; | |
49 | krt_if_scan(p); | |
50 | } | |
51 | ||
52 | static void | |
53 | kif_force_scan(void) | |
54 | { | |
55 | if (kif_proto && kif_last_shot + 2 < now) | |
56 | { | |
57 | kif_scan(kif_scan_timer); | |
58 | tm_start(kif_scan_timer, ((struct kif_config *) kif_proto->p.cf)->scan_time); | |
59 | } | |
60 | } | |
61 | ||
62 | static struct proto * | |
63 | kif_init(struct proto_config *c) | |
64 | { | |
65 | struct kif_proto *p = proto_new(c, sizeof(struct kif_proto)); | |
66 | return &p->p; | |
67 | } | |
68 | ||
69 | static int | |
70 | kif_start(struct proto *P) | |
71 | { | |
72 | struct kif_proto *p = (struct kif_proto *) P; | |
73 | ||
74 | kif_proto = p; | |
75 | krt_if_start(p); | |
76 | ||
77 | /* Start periodic interface scanning */ | |
78 | kif_scan_timer = tm_new(P->pool); | |
79 | kif_scan_timer->hook = kif_scan; | |
80 | kif_scan_timer->data = p; | |
81 | kif_scan_timer->recurrent = KIF_CF->scan_time; | |
82 | kif_scan(kif_scan_timer); | |
83 | tm_start(kif_scan_timer, KIF_CF->scan_time); | |
84 | ||
85 | return PS_UP; | |
86 | } | |
87 | ||
88 | static int | |
89 | kif_shutdown(struct proto *P) | |
90 | { | |
91 | struct kif_proto *p = (struct kif_proto *) P; | |
92 | ||
93 | tm_stop(kif_scan_timer); | |
94 | krt_if_shutdown(p); | |
95 | kif_proto = NULL; | |
96 | ||
97 | if_start_update(); /* Remove all interfaces */ | |
98 | if_end_update(); | |
99 | ||
100 | return PS_DOWN; | |
101 | } | |
102 | ||
103 | struct protocol proto_unix_iface = { | |
104 | name: "Device", | |
105 | priority: 100, | |
106 | init: kif_init, | |
107 | start: kif_start, | |
108 | shutdown: kif_shutdown, | |
109 | }; | |
2d140452 | 110 | |
c10421d3 MM |
111 | /* |
112 | * Inherited Routes | |
113 | */ | |
114 | ||
115 | #ifdef KRT_ALLOW_LEARN | |
116 | ||
117 | static inline int | |
118 | krt_same_key(rte *a, rte *b) | |
119 | { | |
120 | return a->u.krt.proto == b->u.krt.proto && | |
121 | a->u.krt.metric == b->u.krt.metric && | |
122 | a->u.krt.type == b->u.krt.type; | |
123 | } | |
124 | ||
125 | static void | |
126 | krt_learn_announce_update(struct krt_proto *p, rte *e) | |
127 | { | |
128 | net *n = e->net; | |
129 | rta *aa = rta_clone(e->attrs); | |
130 | rte *ee = rte_get_temp(aa); | |
08e2d625 | 131 | net *nn = net_get(p->p.table, n->n.prefix, n->n.pxlen); |
c10421d3 MM |
132 | ee->net = nn; |
133 | ee->pflags = 0; | |
134 | ee->u.krt = e->u.krt; | |
4f1a6d27 | 135 | rte_update(p->p.table, nn, &p->p, ee); |
c10421d3 MM |
136 | } |
137 | ||
138 | static void | |
139 | krt_learn_announce_delete(struct krt_proto *p, net *n) | |
140 | { | |
08e2d625 | 141 | n = net_find(p->p.table, n->n.prefix, n->n.pxlen); |
c10421d3 | 142 | if (n) |
4f1a6d27 | 143 | rte_update(p->p.table, n, &p->p, NULL); |
c10421d3 MM |
144 | } |
145 | ||
146 | static void | |
147 | krt_learn_scan(struct krt_proto *p, rte *e) | |
148 | { | |
149 | net *n0 = e->net; | |
08e2d625 | 150 | net *n = net_get(&p->krt_table, n0->n.prefix, n0->n.pxlen); |
c10421d3 MM |
151 | rte *m, **mm; |
152 | ||
153 | e->attrs->source = RTS_INHERIT; | |
154 | ||
155 | for(mm=&n->routes; m = *mm; mm=&m->next) | |
156 | if (krt_same_key(m, e)) | |
157 | break; | |
158 | if (m) | |
159 | { | |
160 | if (krt_uptodate(m, e)) | |
161 | { | |
162 | DBG("krt_learn_scan: SEEN\n"); | |
163 | rte_free(e); | |
164 | m->u.krt.seen = 1; | |
165 | } | |
166 | else | |
167 | { | |
168 | DBG("krt_learn_scan: OVERRIDE\n"); | |
169 | *mm = m->next; | |
170 | rte_free(m); | |
171 | m = NULL; | |
172 | } | |
173 | } | |
174 | else | |
175 | DBG("krt_learn_scan: CREATE\n"); | |
176 | if (!m) | |
177 | { | |
178 | e->attrs = rta_lookup(e->attrs); | |
179 | e->next = n->routes; | |
180 | n->routes = e; | |
181 | e->u.krt.seen = 1; | |
182 | } | |
183 | } | |
184 | ||
185 | /* FIXME: Add dump function */ | |
186 | ||
187 | static void | |
188 | krt_learn_prune(struct krt_proto *p) | |
189 | { | |
190 | struct fib *fib = &p->krt_table.fib; | |
191 | struct fib_iterator fit; | |
192 | ||
193 | DBG("Pruning inheritance data...\n"); | |
194 | ||
195 | FIB_ITERATE_INIT(&fit, fib); | |
196 | again: | |
197 | FIB_ITERATE_START(fib, &fit, f) | |
198 | { | |
199 | net *n = (net *) f; | |
200 | rte *e, **ee, *best, **pbest, *old_best; | |
201 | ||
202 | old_best = n->routes; | |
203 | best = NULL; | |
204 | pbest = NULL; | |
205 | ee = &n->routes; | |
206 | while (e = *ee) | |
207 | { | |
208 | if (!e->u.krt.seen) | |
209 | { | |
210 | *ee = e->next; | |
211 | rte_free(e); | |
212 | continue; | |
213 | } | |
214 | if (!best || best->u.krt.metric > e->u.krt.metric) | |
215 | { | |
216 | best = e; | |
217 | pbest = ee; | |
218 | } | |
219 | e->u.krt.seen = 0; | |
220 | ee = &e->next; | |
221 | } | |
222 | if (!n->routes) | |
223 | { | |
224 | DBG("%I/%d: deleting\n", n->n.prefix, n->n.pxlen); | |
225 | if (old_best) | |
226 | { | |
227 | krt_learn_announce_delete(p, n); | |
228 | n->n.flags &= ~KRF_INSTALLED; | |
229 | } | |
230 | FIB_ITERATE_PUT(&fit, f); | |
231 | fib_delete(fib, f); | |
232 | goto again; | |
233 | } | |
234 | *pbest = best->next; | |
235 | best->next = n->routes; | |
236 | n->routes = best; | |
237 | if (best != old_best || !(n->n.flags & KRF_INSTALLED)) | |
238 | { | |
239 | DBG("%I/%d: announcing (metric=%d)\n", n->n.prefix, n->n.pxlen, best->u.krt.metric); | |
240 | krt_learn_announce_update(p, best); | |
241 | n->n.flags |= KRF_INSTALLED; | |
242 | } | |
243 | else | |
244 | DBG("%I/%d: uptodate (metric=%d)\n", n->n.prefix, n->n.pxlen, best->u.krt.metric); | |
245 | } | |
246 | FIB_ITERATE_END(f); | |
247 | } | |
248 | ||
249 | static void | |
250 | krt_learn_async(struct krt_proto *p, rte *e, int new) | |
251 | { | |
252 | net *n0 = e->net; | |
08e2d625 | 253 | net *n = net_get(&p->krt_table, n0->n.prefix, n0->n.pxlen); |
c10421d3 MM |
254 | rte *g, **gg, *best, **bestp, *old_best; |
255 | ||
256 | e->attrs->source = RTS_INHERIT; | |
257 | ||
258 | old_best = n->routes; | |
259 | for(gg=&n->routes; g = *gg; gg = &g->next) | |
260 | if (krt_same_key(g, e)) | |
261 | break; | |
262 | if (new) | |
263 | { | |
264 | if (g) | |
265 | { | |
266 | if (krt_uptodate(g, e)) | |
267 | { | |
268 | DBG("krt_learn_async: same\n"); | |
269 | rte_free(e); | |
270 | return; | |
271 | } | |
272 | DBG("krt_learn_async: update\n"); | |
273 | *gg = g->next; | |
274 | rte_free(g); | |
275 | } | |
276 | else | |
277 | DBG("krt_learn_async: create\n"); | |
278 | e->attrs = rta_lookup(e->attrs); | |
279 | e->next = n->routes; | |
280 | n->routes = e; | |
281 | } | |
282 | else if (!g) | |
283 | { | |
284 | DBG("krt_learn_async: not found\n"); | |
285 | rte_free(e); | |
286 | return; | |
287 | } | |
288 | else | |
289 | { | |
290 | DBG("krt_learn_async: delete\n"); | |
291 | *gg = g->next; | |
292 | rte_free(e); | |
293 | rte_free(g); | |
294 | } | |
295 | best = n->routes; | |
296 | bestp = &n->routes; | |
297 | for(gg=&n->routes; g=*gg; gg=&g->next) | |
298 | if (best->u.krt.metric > g->u.krt.metric) | |
299 | { | |
300 | best = g; | |
301 | bestp = gg; | |
302 | } | |
303 | if (best) | |
304 | { | |
305 | *bestp = best->next; | |
306 | best->next = n->routes; | |
307 | n->routes = best; | |
308 | } | |
309 | if (best != old_best) | |
310 | { | |
311 | DBG("krt_learn_async: distributing change\n"); | |
312 | if (best) | |
313 | { | |
314 | krt_learn_announce_update(p, best); | |
315 | n->n.flags |= KRF_INSTALLED; | |
316 | } | |
317 | else | |
318 | { | |
319 | n->routes = NULL; | |
320 | krt_learn_announce_delete(p, n); | |
321 | n->n.flags &= ~KRF_INSTALLED; | |
322 | } | |
323 | } | |
324 | } | |
325 | ||
326 | static void | |
327 | krt_learn_init(struct krt_proto *p) | |
328 | { | |
329 | if (KRT_CF->learn) | |
330 | rt_setup(p->p.pool, &p->krt_table, "Inherited"); | |
331 | } | |
332 | ||
333 | static void | |
334 | krt_dump(struct proto *P) | |
335 | { | |
336 | struct krt_proto *p = (struct krt_proto *) P; | |
337 | ||
338 | if (!KRT_CF->learn) | |
339 | return; | |
340 | debug("KRT: Table of inheritable routes\n"); | |
341 | rt_dump(&p->krt_table); | |
342 | } | |
343 | ||
344 | static void | |
345 | krt_dump_attrs(rte *e) | |
346 | { | |
347 | debug(" [m=%d,p=%d,t=%d]", e->u.krt.metric, e->u.krt.proto, e->u.krt.type); | |
348 | } | |
349 | ||
350 | #endif | |
351 | ||
2d140452 MM |
352 | /* |
353 | * Routes | |
354 | */ | |
355 | ||
356 | static void | |
357 | krt_flush_routes(struct krt_proto *p) | |
358 | { | |
4f1a6d27 | 359 | struct rtable *t = p->p.table; |
2d140452 MM |
360 | |
361 | DBG("Flushing kernel routes...\n"); | |
2d140452 MM |
362 | FIB_WALK(&t->fib, f) |
363 | { | |
364 | net *n = (net *) f; | |
365 | rte *e = n->routes; | |
366 | if (e) | |
367 | { | |
368 | rta *a = e->attrs; | |
369 | if (a->source != RTS_DEVICE && a->source != RTS_INHERIT) | |
c10421d3 | 370 | krt_set_notify(p, e->net, NULL, e); |
2d140452 MM |
371 | } |
372 | } | |
373 | FIB_WALK_END; | |
374 | } | |
375 | ||
2d140452 MM |
376 | /* FIXME: Synchronization of multiple routing tables? */ |
377 | ||
378 | static int | |
379 | krt_uptodate(rte *k, rte *e) | |
380 | { | |
381 | rta *ka = k->attrs, *ea = e->attrs; | |
382 | ||
383 | if (ka->dest != ea->dest) | |
384 | return 0; | |
385 | switch (ka->dest) | |
386 | { | |
387 | case RTD_ROUTER: | |
388 | return ipa_equal(ka->gw, ea->gw); | |
389 | case RTD_DEVICE: | |
390 | return !strcmp(ka->iface->name, ea->iface->name); | |
391 | default: | |
392 | return 1; | |
393 | } | |
394 | } | |
395 | ||
396 | /* | |
397 | * This gets called back when the low-level scanning code discovers a route. | |
398 | * We expect that the route is a temporary rte and its attributes are uncached. | |
399 | */ | |
400 | ||
401 | void | |
402 | krt_got_route(struct krt_proto *p, rte *e) | |
403 | { | |
404 | rte *old; | |
405 | net *net = e->net; | |
c10421d3 | 406 | int src = e->u.krt.src; |
2d140452 MM |
407 | int verdict; |
408 | ||
c10421d3 MM |
409 | #ifdef CONFIG_AUTO_ROUTES |
410 | if (e->attrs->dest == RTD_DEVICE) | |
411 | { | |
412 | /* It's a device route. Probably a kernel-generated one. */ | |
413 | verdict = KRF_IGNORE; | |
414 | goto sentenced; | |
415 | } | |
416 | #endif | |
417 | ||
418 | #ifdef KRT_ALLOW_LEARN | |
419 | if (src == KRT_SRC_ALIEN) | |
420 | { | |
421 | if (KRT_CF->learn) | |
422 | krt_learn_scan(p, e); | |
423 | else | |
424 | DBG("krt_parse_entry: Alien route, ignoring\n"); | |
425 | return; | |
426 | } | |
427 | #endif | |
428 | ||
429 | if (net->n.flags & KRF_VERDICT_MASK) | |
2d140452 MM |
430 | { |
431 | /* Route to this destination was already seen. Strange, but it happens... */ | |
432 | DBG("Already seen.\n"); | |
433 | return; | |
434 | } | |
435 | ||
c10421d3 | 436 | if (net->n.flags & KRF_INSTALLED) |
2d140452 | 437 | { |
c10421d3 MM |
438 | old = net->routes; |
439 | ASSERT(old); | |
440 | if (krt_uptodate(e, old)) | |
2d140452 MM |
441 | verdict = KRF_SEEN; |
442 | else | |
443 | verdict = KRF_UPDATE; | |
444 | } | |
2d140452 MM |
445 | else |
446 | verdict = KRF_DELETE; | |
447 | ||
c10421d3 MM |
448 | sentenced: |
449 | DBG("krt_parse_entry: verdict=%s\n", ((char *[]) { "CREATE", "SEEN", "UPDATE", "DELETE", "IGNORE" }) [verdict]); | |
2d140452 | 450 | |
c10421d3 MM |
451 | net->n.flags = (net->n.flags & ~KRF_VERDICT_MASK) | verdict; |
452 | if (verdict == KRF_UPDATE || verdict == KRF_DELETE) | |
2d140452 MM |
453 | { |
454 | /* Get a cached copy of attributes and link the route */ | |
455 | rta *a = e->attrs; | |
456 | a->source = RTS_DUMMY; | |
457 | e->attrs = rta_lookup(a); | |
458 | e->next = net->routes; | |
459 | net->routes = e; | |
460 | } | |
461 | else | |
462 | rte_free(e); | |
463 | } | |
464 | ||
465 | static void | |
466 | krt_prune(struct krt_proto *p) | |
467 | { | |
468 | struct proto *pp = &p->p; | |
4f1a6d27 | 469 | struct rtable *t = p->p.table; |
2d140452 MM |
470 | struct fib_node *f; |
471 | ||
472 | DBG("Pruning routes...\n"); | |
2d140452 MM |
473 | FIB_WALK(&t->fib, f) |
474 | { | |
475 | net *n = (net *) f; | |
c10421d3 | 476 | int verdict = f->flags & KRF_VERDICT_MASK; |
2d140452 MM |
477 | rte *new, *old; |
478 | ||
c10421d3 | 479 | if (verdict != KRF_CREATE && verdict != KRF_SEEN && verdict != KRF_IGNORE) |
2d140452 MM |
480 | { |
481 | old = n->routes; | |
482 | n->routes = old->next; | |
483 | } | |
484 | else | |
485 | old = NULL; | |
486 | new = n->routes; | |
487 | ||
488 | switch (verdict) | |
489 | { | |
490 | case KRF_CREATE: | |
c10421d3 | 491 | if (new && (f->flags & KRF_INSTALLED)) |
2d140452 | 492 | { |
c10421d3 MM |
493 | DBG("krt_prune: reinstalling %I/%d\n", n->n.prefix, n->n.pxlen); |
494 | krt_set_notify(p, n, new, NULL); | |
2d140452 MM |
495 | } |
496 | break; | |
497 | case KRF_SEEN: | |
c10421d3 | 498 | case KRF_IGNORE: |
2d140452 MM |
499 | /* Nothing happens */ |
500 | break; | |
501 | case KRF_UPDATE: | |
502 | DBG("krt_prune: updating %I/%d\n", n->n.prefix, n->n.pxlen); | |
c10421d3 | 503 | krt_set_notify(p, n, new, old); |
2d140452 MM |
504 | break; |
505 | case KRF_DELETE: | |
506 | DBG("krt_prune: deleting %I/%d\n", n->n.prefix, n->n.pxlen); | |
c10421d3 | 507 | krt_set_notify(p, n, NULL, old); |
2d140452 MM |
508 | break; |
509 | default: | |
510 | bug("krt_prune: invalid route status"); | |
511 | } | |
2d140452 MM |
512 | if (old) |
513 | rte_free(old); | |
c10421d3 | 514 | f->flags &= ~KRF_VERDICT_MASK; |
2d140452 MM |
515 | } |
516 | FIB_WALK_END; | |
c10421d3 MM |
517 | |
518 | #ifdef KRT_ALLOW_LEARN | |
519 | if (KRT_CF->learn) | |
520 | krt_learn_prune(p); | |
521 | #endif | |
2d140452 MM |
522 | } |
523 | ||
e16155ae MM |
524 | void |
525 | krt_got_route_async(struct krt_proto *p, rte *e, int new) | |
526 | { | |
527 | net *net = e->net; | |
528 | rte *old = net->routes; | |
c10421d3 | 529 | int src = e->u.krt.src; |
e16155ae MM |
530 | |
531 | switch (src) | |
532 | { | |
533 | case KRT_SRC_BIRD: | |
534 | ASSERT(0); | |
535 | case KRT_SRC_REDIRECT: | |
536 | DBG("It's a redirect, kill him! Kill! Kill!\n"); | |
c10421d3 | 537 | krt_set_notify(p, net, NULL, e); |
e16155ae | 538 | break; |
c10421d3 MM |
539 | case KRT_SRC_ALIEN: |
540 | #ifdef KRT_ALLOW_LEARN | |
541 | if (KRT_CF->learn) | |
e16155ae | 542 | { |
c10421d3 MM |
543 | krt_learn_async(p, e, new); |
544 | return; | |
e16155ae | 545 | } |
c10421d3 MM |
546 | #endif |
547 | /* Fall-thru */ | |
548 | default: | |
549 | DBG("Discarding\n"); | |
4f1a6d27 | 550 | rte_update(p->p.table, net, &p->p, NULL); |
e16155ae | 551 | } |
c10421d3 | 552 | rte_free(e); |
e16155ae MM |
553 | } |
554 | ||
2d140452 MM |
555 | /* |
556 | * Periodic scanning | |
557 | */ | |
558 | ||
559 | static timer *krt_scan_timer; | |
560 | ||
561 | static void | |
562 | krt_scan(timer *t) | |
563 | { | |
564 | struct krt_proto *p = t->data; | |
565 | ||
7e5f5ffd MM |
566 | kif_force_scan(); |
567 | DBG("KRT: It's route scan time...\n"); | |
568 | krt_scan_fire(p); | |
569 | krt_prune(p); | |
2d140452 MM |
570 | } |
571 | ||
c10421d3 MM |
572 | /* |
573 | * Updates | |
574 | */ | |
575 | ||
576 | static void | |
bb027be1 | 577 | krt_notify(struct proto *P, net *net, rte *new, rte *old, struct ea_list *tmpa) |
c10421d3 MM |
578 | { |
579 | struct krt_proto *p = (struct krt_proto *) P; | |
580 | ||
581 | if (new && (!krt_capable(new) || new->attrs->source == RTS_INHERIT)) | |
582 | new = NULL; | |
583 | if (!(net->n.flags & KRF_INSTALLED)) | |
584 | old = NULL; | |
585 | if (new) | |
586 | net->n.flags |= KRF_INSTALLED; | |
587 | else | |
588 | net->n.flags &= ~KRF_INSTALLED; | |
589 | krt_set_notify(p, net, new, old); | |
590 | } | |
591 | ||
2d140452 MM |
592 | /* |
593 | * Protocol glue | |
594 | */ | |
595 | ||
7e5f5ffd MM |
596 | struct proto_config *cf_krt; |
597 | ||
2d140452 MM |
598 | static int |
599 | krt_start(struct proto *P) | |
600 | { | |
601 | struct krt_proto *p = (struct krt_proto *) P; | |
602 | ||
c10421d3 MM |
603 | #ifdef KRT_ALLOW_LEARN |
604 | krt_learn_init(p); | |
605 | #endif | |
606 | ||
2d140452 MM |
607 | krt_scan_start(p); |
608 | krt_set_start(p); | |
609 | ||
7e5f5ffd | 610 | /* Start periodic routing table scanning */ |
2d140452 MM |
611 | krt_scan_timer = tm_new(P->pool); |
612 | krt_scan_timer->hook = krt_scan; | |
613 | krt_scan_timer->data = p; | |
614 | krt_scan_timer->recurrent = KRT_CF->scan_time; | |
615 | krt_scan(krt_scan_timer); | |
616 | tm_start(krt_scan_timer, KRT_CF->scan_time); | |
617 | ||
618 | return PS_UP; | |
619 | } | |
620 | ||
621 | int | |
622 | krt_shutdown(struct proto *P) | |
623 | { | |
624 | struct krt_proto *p = (struct krt_proto *) P; | |
625 | ||
7e5f5ffd MM |
626 | tm_stop(krt_scan_timer); |
627 | ||
2d140452 MM |
628 | if (!KRT_CF->persist) |
629 | krt_flush_routes(p); | |
630 | ||
631 | krt_set_shutdown(p); | |
632 | krt_scan_shutdown(p); | |
633 | ||
2d140452 MM |
634 | return PS_DOWN; |
635 | } | |
636 | ||
2d140452 MM |
637 | static struct proto * |
638 | krt_init(struct proto_config *c) | |
639 | { | |
640 | struct krt_proto *p = proto_new(c, sizeof(struct krt_proto)); | |
641 | ||
c10421d3 | 642 | p->p.rt_notify = krt_notify; |
2d140452 MM |
643 | return &p->p; |
644 | } | |
645 | ||
646 | struct protocol proto_unix_kernel = { | |
647 | name: "Kernel", | |
7e5f5ffd | 648 | priority: 80, |
2d140452 MM |
649 | init: krt_init, |
650 | start: krt_start, | |
651 | shutdown: krt_shutdown, | |
c10421d3 MM |
652 | #ifdef KRT_ALLOW_LEARN |
653 | dump: krt_dump, | |
654 | dump_attrs: krt_dump_attrs, | |
655 | #endif | |
2d140452 | 656 | }; |