]> git.ipfire.org Git - thirdparty/bird.git/blob - sysdep/unix/krt.c
Syntax error fix for systems without CONFIG_SELF_CONSCIOUS (KRT_ALLOW_LEARN)
[thirdparty/bird.git] / sysdep / unix / krt.c
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 /**
10 * DOC: Kernel synchronization
11 *
12 * This system dependent module implements the Kernel and Device protocol,
13 * that is synchronization of interface lists and routing tables with the
14 * OS kernel.
15 *
16 * The whole kernel synchronization is a bit messy and touches some internals
17 * of the routing table engine, because routing table maintenance is a typical
18 * example of the proverbial compatibility between different Unices and we want
19 * to keep the overhead of our KRT business as low as possible and avoid maintaining
20 * a local routing table copy.
21 *
22 * The kernel syncer can work in three different modes (according to system config header):
23 * Either with a single routing table and single KRT protocol [traditional UNIX]
24 * or with many routing tables and separate KRT protocols for all of them
25 * or with many routing tables, but every scan including all tables, so we start
26 * separate KRT protocols which cooperate with each other [Linux 2.2].
27 * In this case, we keep only a single scan timer.
28 *
29 * We use FIB node flags in the routing table to keep track of route
30 * synchronization status. We also attach temporary &rte's to the routing table,
31 * but it cannot do any harm to the rest of BIRD since table synchronization is
32 * an atomic process.
33 *
34 * When starting up, we cheat by looking if there is another
35 * KRT instance to be initialized later and performing table scan
36 * only once for all the instances.
37 */
38
39 /*
40 * If you are brave enough, continue now. You cannot say you haven't been warned.
41 */
42
43 #undef LOCAL_DEBUG
44
45 #include "nest/bird.h"
46 #include "nest/iface.h"
47 #include "nest/route.h"
48 #include "nest/protocol.h"
49 #include "lib/timer.h"
50 #include "conf/conf.h"
51 #include "lib/string.h"
52
53 #include "unix.h"
54 #include "krt.h"
55
56 static int krt_uptodate(rte *k, rte *e);
57
58 /*
59 * Global resources
60 */
61
62 pool *krt_pool;
63
64 void
65 krt_io_init(void)
66 {
67 krt_pool = rp_new(&root_pool, "Kernel Syncer");
68 krt_if_io_init();
69 }
70
71 /*
72 * Interfaces
73 */
74
75 struct proto_config *cf_kif;
76
77 static struct kif_proto *kif_proto;
78 static timer *kif_scan_timer;
79 static bird_clock_t kif_last_shot;
80
81 static void
82 kif_preconfig(struct protocol *P UNUSED, struct config *c UNUSED)
83 {
84 cf_kif = NULL;
85 }
86
87 static void
88 kif_scan(timer *t)
89 {
90 struct kif_proto *p = t->data;
91
92 KRT_TRACE(p, D_EVENTS, "Scanning interfaces");
93 kif_last_shot = now;
94 krt_if_scan(p);
95 }
96
97 static void
98 kif_force_scan(void)
99 {
100 if (kif_proto && kif_last_shot + 2 < now)
101 {
102 kif_scan(kif_scan_timer);
103 tm_start(kif_scan_timer, ((struct kif_config *) kif_proto->p.cf)->scan_time);
104 }
105 }
106
107 static struct proto *
108 kif_init(struct proto_config *c)
109 {
110 struct kif_proto *p = proto_new(c, sizeof(struct kif_proto));
111 return &p->p;
112 }
113
114 static int
115 kif_start(struct proto *P)
116 {
117 struct kif_proto *p = (struct kif_proto *) P;
118
119 kif_proto = p;
120 krt_if_start(p);
121
122 /* Start periodic interface scanning */
123 kif_scan_timer = tm_new(P->pool);
124 kif_scan_timer->hook = kif_scan;
125 kif_scan_timer->data = p;
126 kif_scan_timer->recurrent = KIF_CF->scan_time;
127 kif_scan(kif_scan_timer);
128 tm_start(kif_scan_timer, KIF_CF->scan_time);
129
130 return PS_UP;
131 }
132
133 static int
134 kif_shutdown(struct proto *P)
135 {
136 struct kif_proto *p = (struct kif_proto *) P;
137
138 tm_stop(kif_scan_timer);
139 krt_if_shutdown(p);
140 kif_proto = NULL;
141
142 if_start_update(); /* Remove all interfaces */
143 if_end_update();
144 /*
145 * FIXME: Is it really a good idea? It causes routes to be flushed,
146 * but at the same time it avoids sending of these deletions to the kernel,
147 * because krt thinks the kernel itself has already removed the route
148 * when downing the interface. Sad.
149 */
150
151 return PS_DOWN;
152 }
153
154 static int
155 kif_reconfigure(struct proto *p, struct proto_config *new)
156 {
157 struct kif_config *o = (struct kif_config *) p->cf;
158 struct kif_config *n = (struct kif_config *) new;
159
160 if (!kif_params_same(&o->iface, &n->iface))
161 return 0;
162 if (o->scan_time != n->scan_time)
163 {
164 tm_stop(kif_scan_timer);
165 kif_scan_timer->recurrent = n->scan_time;
166 kif_scan(kif_scan_timer);
167 tm_start(kif_scan_timer, n->scan_time);
168 }
169 return 1;
170 }
171
172 struct protocol proto_unix_iface = {
173 name: "Device",
174 template: "device%d",
175 preconfig: kif_preconfig,
176 init: kif_init,
177 start: kif_start,
178 shutdown: kif_shutdown,
179 reconfigure: kif_reconfigure,
180 };
181
182 /*
183 * Tracing of routes
184 */
185
186 static inline void
187 krt_trace_in(struct krt_proto *p, rte *e, char *msg)
188 {
189 if (p->p.debug & D_PACKETS)
190 log(L_TRACE "%s: %I/%d: %s", p->p.name, e->net->n.prefix, e->net->n.pxlen, msg);
191 }
192
193 static inline void
194 krt_trace_in_rl(struct rate_limit *rl, struct krt_proto *p, rte *e, char *msg)
195 {
196 if (p->p.debug & D_PACKETS)
197 log_rl(rl, L_TRACE "%s: %I/%d: %s", p->p.name, e->net->n.prefix, e->net->n.pxlen, msg);
198 }
199
200 /*
201 * Inherited Routes
202 */
203
204 #ifdef KRT_ALLOW_LEARN
205
206 static struct rate_limit rl_alien_seen, rl_alien_updated, rl_alien_created, rl_alien_ignored;
207
208 static inline int
209 krt_same_key(rte *a, rte *b)
210 {
211 return a->u.krt.proto == b->u.krt.proto &&
212 a->u.krt.metric == b->u.krt.metric &&
213 a->u.krt.type == b->u.krt.type;
214 }
215
216 static void
217 krt_learn_announce_update(struct krt_proto *p, rte *e)
218 {
219 net *n = e->net;
220 rta *aa = rta_clone(e->attrs);
221 rte *ee = rte_get_temp(aa);
222 net *nn = net_get(p->p.table, n->n.prefix, n->n.pxlen);
223 ee->net = nn;
224 ee->pflags = 0;
225 ee->pref = p->p.preference;
226 ee->u.krt = e->u.krt;
227 rte_update(p->p.table, nn, &p->p, ee);
228 }
229
230 static void
231 krt_learn_announce_delete(struct krt_proto *p, net *n)
232 {
233 n = net_find(p->p.table, n->n.prefix, n->n.pxlen);
234 if (n)
235 rte_update(p->p.table, n, &p->p, NULL);
236 }
237
238 static void
239 krt_learn_scan(struct krt_proto *p, rte *e)
240 {
241 net *n0 = e->net;
242 net *n = net_get(&p->krt_table, n0->n.prefix, n0->n.pxlen);
243 rte *m, **mm;
244
245 e->attrs->source = RTS_INHERIT;
246
247 for(mm=&n->routes; m = *mm; mm=&m->next)
248 if (krt_same_key(m, e))
249 break;
250 if (m)
251 {
252 if (krt_uptodate(m, e))
253 {
254 krt_trace_in_rl(&rl_alien_seen, p, e, "[alien] seen");
255 rte_free(e);
256 m->u.krt.seen = 1;
257 }
258 else
259 {
260 krt_trace_in_rl(&rl_alien_updated, p, e, "[alien] updated");
261 *mm = m->next;
262 rte_free(m);
263 m = NULL;
264 }
265 }
266 else
267 krt_trace_in_rl(&rl_alien_created, p, e, "[alien] created");
268 if (!m)
269 {
270 e->attrs = rta_lookup(e->attrs);
271 e->next = n->routes;
272 n->routes = e;
273 e->u.krt.seen = 1;
274 }
275 }
276
277 static void
278 krt_learn_prune(struct krt_proto *p)
279 {
280 struct fib *fib = &p->krt_table.fib;
281 struct fib_iterator fit;
282
283 KRT_TRACE(p, D_EVENTS, "Pruning inherited routes");
284
285 FIB_ITERATE_INIT(&fit, fib);
286 again:
287 FIB_ITERATE_START(fib, &fit, f)
288 {
289 net *n = (net *) f;
290 rte *e, **ee, *best, **pbest, *old_best;
291
292 old_best = n->routes;
293 best = NULL;
294 pbest = NULL;
295 ee = &n->routes;
296 while (e = *ee)
297 {
298 if (!e->u.krt.seen)
299 {
300 *ee = e->next;
301 rte_free(e);
302 continue;
303 }
304 if (!best || best->u.krt.metric > e->u.krt.metric)
305 {
306 best = e;
307 pbest = ee;
308 }
309 e->u.krt.seen = 0;
310 ee = &e->next;
311 }
312 if (!n->routes)
313 {
314 DBG("%I/%d: deleting\n", n->n.prefix, n->n.pxlen);
315 if (old_best)
316 {
317 krt_learn_announce_delete(p, n);
318 n->n.flags &= ~KRF_INSTALLED;
319 }
320 FIB_ITERATE_PUT(&fit, f);
321 fib_delete(fib, f);
322 goto again;
323 }
324 *pbest = best->next;
325 best->next = n->routes;
326 n->routes = best;
327 if (best != old_best || !(n->n.flags & KRF_INSTALLED))
328 {
329 DBG("%I/%d: announcing (metric=%d)\n", n->n.prefix, n->n.pxlen, best->u.krt.metric);
330 krt_learn_announce_update(p, best);
331 n->n.flags |= KRF_INSTALLED;
332 }
333 else
334 DBG("%I/%d: uptodate (metric=%d)\n", n->n.prefix, n->n.pxlen, best->u.krt.metric);
335 }
336 FIB_ITERATE_END(f);
337 }
338
339 static void
340 krt_learn_async(struct krt_proto *p, rte *e, int new)
341 {
342 net *n0 = e->net;
343 net *n = net_get(&p->krt_table, n0->n.prefix, n0->n.pxlen);
344 rte *g, **gg, *best, **bestp, *old_best;
345
346 e->attrs->source = RTS_INHERIT;
347
348 old_best = n->routes;
349 for(gg=&n->routes; g = *gg; gg = &g->next)
350 if (krt_same_key(g, e))
351 break;
352 if (new)
353 {
354 if (g)
355 {
356 if (krt_uptodate(g, e))
357 {
358 krt_trace_in(p, e, "[alien async] same");
359 rte_free(e);
360 return;
361 }
362 krt_trace_in(p, e, "[alien async] updated");
363 *gg = g->next;
364 rte_free(g);
365 }
366 else
367 krt_trace_in(p, e, "[alien async] created");
368 e->attrs = rta_lookup(e->attrs);
369 e->next = n->routes;
370 n->routes = e;
371 }
372 else if (!g)
373 {
374 krt_trace_in(p, e, "[alien async] delete failed");
375 rte_free(e);
376 return;
377 }
378 else
379 {
380 krt_trace_in(p, e, "[alien async] removed");
381 *gg = g->next;
382 rte_free(e);
383 rte_free(g);
384 }
385 best = n->routes;
386 bestp = &n->routes;
387 for(gg=&n->routes; g=*gg; gg=&g->next)
388 if (best->u.krt.metric > g->u.krt.metric)
389 {
390 best = g;
391 bestp = gg;
392 }
393 if (best)
394 {
395 *bestp = best->next;
396 best->next = n->routes;
397 n->routes = best;
398 }
399 if (best != old_best)
400 {
401 DBG("krt_learn_async: distributing change\n");
402 if (best)
403 {
404 krt_learn_announce_update(p, best);
405 n->n.flags |= KRF_INSTALLED;
406 }
407 else
408 {
409 n->routes = NULL;
410 krt_learn_announce_delete(p, n);
411 n->n.flags &= ~KRF_INSTALLED;
412 }
413 }
414 }
415
416 static void
417 krt_learn_init(struct krt_proto *p)
418 {
419 if (KRT_CF->learn)
420 rt_setup(p->p.pool, &p->krt_table, "Inherited", NULL);
421 }
422
423 static void
424 krt_dump(struct proto *P)
425 {
426 struct krt_proto *p = (struct krt_proto *) P;
427
428 if (!KRT_CF->learn)
429 return;
430 debug("KRT: Table of inheritable routes\n");
431 rt_dump(&p->krt_table);
432 }
433
434 static void
435 krt_dump_attrs(rte *e)
436 {
437 debug(" [m=%d,p=%d,t=%d]", e->u.krt.metric, e->u.krt.proto, e->u.krt.type);
438 }
439
440 #endif
441
442 /*
443 * Routes
444 */
445
446 #ifdef CONFIG_ALL_TABLES_AT_ONCE
447 static timer *krt_scan_timer;
448 static int krt_instance_count;
449 static list krt_instance_list;
450 #endif
451
452 static void
453 krt_flush_routes(struct krt_proto *p)
454 {
455 struct rtable *t = p->p.table;
456
457 KRT_TRACE(p, D_EVENTS, "Flushing kernel routes");
458 FIB_WALK(&t->fib, f)
459 {
460 net *n = (net *) f;
461 rte *e = n->routes;
462 if (e)
463 {
464 rta *a = e->attrs;
465 if ((n->n.flags & KRF_INSTALLED) &&
466 a->source != RTS_DEVICE && a->source != RTS_INHERIT)
467 {
468 krt_set_notify(p, e->net, NULL, e);
469 n->n.flags &= ~KRF_INSTALLED;
470 }
471 }
472 }
473 FIB_WALK_END;
474 }
475
476 static int
477 krt_uptodate(rte *k, rte *e)
478 {
479 rta *ka = k->attrs, *ea = e->attrs;
480
481 if (ka->dest != ea->dest)
482 return 0;
483 switch (ka->dest)
484 {
485 case RTD_ROUTER:
486 return ipa_equal(ka->gw, ea->gw);
487 case RTD_DEVICE:
488 return !strcmp(ka->iface->name, ea->iface->name);
489 default:
490 return 1;
491 }
492 }
493
494 /*
495 * This gets called back when the low-level scanning code discovers a route.
496 * We expect that the route is a temporary rte and its attributes are uncached.
497 */
498
499 void
500 krt_got_route(struct krt_proto *p, rte *e)
501 {
502 rte *old;
503 net *net = e->net;
504 int verdict;
505 #ifdef KRT_ALLOW_LEARN
506 int src = e->u.krt.src;
507 #endif
508
509 #ifdef CONFIG_AUTO_ROUTES
510 if (e->attrs->dest == RTD_DEVICE)
511 {
512 /* It's a device route. Probably a kernel-generated one. */
513 verdict = KRF_IGNORE;
514 goto sentenced;
515 }
516 #endif
517
518 #ifdef KRT_ALLOW_LEARN
519 if (src == KRT_SRC_ALIEN)
520 {
521 if (KRT_CF->learn)
522 krt_learn_scan(p, e);
523 else
524 {
525 krt_trace_in_rl(&rl_alien_ignored, p, e, "alien route, ignored");
526 rte_free(e);
527 }
528 return;
529 }
530 #endif
531
532 if (net->n.flags & KRF_VERDICT_MASK)
533 {
534 /* Route to this destination was already seen. Strange, but it happens... */
535 krt_trace_in(p, e, "already seen");
536 rte_free(e);
537 return;
538 }
539
540 if (net->n.flags & KRF_INSTALLED)
541 {
542 old = net->routes;
543 ASSERT(old);
544 if (krt_uptodate(e, old))
545 verdict = KRF_SEEN;
546 else
547 verdict = KRF_UPDATE;
548 }
549 else
550 verdict = KRF_DELETE;
551
552 sentenced:
553 krt_trace_in(p, e, ((char *[]) { "?", "seen", "will be updated", "will be removed", "ignored" }) [verdict]);
554 net->n.flags = (net->n.flags & ~KRF_VERDICT_MASK) | verdict;
555 if (verdict == KRF_UPDATE || verdict == KRF_DELETE)
556 {
557 /* Get a cached copy of attributes and link the route */
558 rta *a = e->attrs;
559 a->source = RTS_DUMMY;
560 e->attrs = rta_lookup(a);
561 e->next = net->routes;
562 net->routes = e;
563 }
564 else
565 rte_free(e);
566 }
567
568 static void
569 krt_prune(struct krt_proto *p)
570 {
571 struct rtable *t = p->p.table;
572
573 KRT_TRACE(p, D_EVENTS, "Pruning table %s", t->name);
574 FIB_WALK(&t->fib, f)
575 {
576 net *n = (net *) f;
577 int verdict = f->flags & KRF_VERDICT_MASK;
578 rte *new, *old;
579
580 if (verdict != KRF_CREATE && verdict != KRF_SEEN && verdict != KRF_IGNORE)
581 {
582 old = n->routes;
583 n->routes = old->next;
584 }
585 else
586 old = NULL;
587 new = n->routes;
588
589 switch (verdict)
590 {
591 case KRF_CREATE:
592 if (new && (f->flags & KRF_INSTALLED))
593 {
594 krt_trace_in(p, new, "reinstalling");
595 krt_set_notify(p, n, new, NULL);
596 }
597 break;
598 case KRF_SEEN:
599 case KRF_IGNORE:
600 /* Nothing happens */
601 break;
602 case KRF_UPDATE:
603 krt_trace_in(p, new, "updating");
604 krt_set_notify(p, n, new, old);
605 break;
606 case KRF_DELETE:
607 krt_trace_in(p, old, "deleting");
608 krt_set_notify(p, n, NULL, old);
609 break;
610 default:
611 bug("krt_prune: invalid route status");
612 }
613 if (old)
614 rte_free(old);
615 f->flags &= ~KRF_VERDICT_MASK;
616 }
617 FIB_WALK_END;
618
619 #ifdef KRT_ALLOW_LEARN
620 if (KRT_CF->learn)
621 krt_learn_prune(p);
622 #endif
623 p->initialized = 1;
624 }
625
626 void
627 krt_got_route_async(struct krt_proto *p, rte *e, int new UNUSED)
628 {
629 net *net = e->net;
630 int src = e->u.krt.src;
631
632 switch (src)
633 {
634 case KRT_SRC_BIRD:
635 ASSERT(0); /* Should be filtered by the back end */
636 case KRT_SRC_REDIRECT:
637 DBG("It's a redirect, kill him! Kill! Kill!\n");
638 krt_set_notify(p, net, NULL, e);
639 break;
640 #ifdef KRT_ALLOW_LEARN
641 case KRT_SRC_ALIEN:
642 if (KRT_CF->learn)
643 {
644 krt_learn_async(p, e, new);
645 return;
646 }
647 #endif
648 }
649 rte_free(e);
650 }
651
652 /*
653 * Periodic scanning
654 */
655
656 static void
657 krt_scan(timer *t UNUSED)
658 {
659 struct krt_proto *p;
660
661 kif_force_scan();
662 #ifdef CONFIG_ALL_TABLES_AT_ONCE
663 {
664 void *q;
665 /* We need some node to decide whether to print the debug messages or not */
666 p = SKIP_BACK(struct krt_proto, instance_node, HEAD(krt_instance_list));
667 if (p->instance_node.next)
668 KRT_TRACE(p, D_EVENTS, "Scanning routing table");
669 krt_scan_fire(NULL);
670 WALK_LIST(q, krt_instance_list)
671 {
672 p = SKIP_BACK(struct krt_proto, instance_node, q);
673 krt_prune(p);
674 }
675 }
676 #else
677 p = t->data;
678 KRT_TRACE(p, D_EVENTS, "Scanning routing table");
679 krt_scan_fire(p);
680 krt_prune(p);
681 #endif
682 }
683
684 /*
685 * Updates
686 */
687
688 static void
689 krt_notify(struct proto *P, net *net, rte *new, rte *old, struct ea_list *attrs UNUSED)
690 {
691 struct krt_proto *p = (struct krt_proto *) P;
692
693 if (shutting_down)
694 return;
695 if (new && (!krt_capable(new) || new->attrs->source == RTS_INHERIT))
696 new = NULL;
697 if (!(net->n.flags & KRF_INSTALLED))
698 old = NULL;
699 if (new)
700 net->n.flags |= KRF_INSTALLED;
701 else
702 net->n.flags &= ~KRF_INSTALLED;
703 if (p->initialized) /* Before first scan we don't touch the routes */
704 krt_set_notify(p, net, new, old);
705 }
706
707 /*
708 * Protocol glue
709 */
710
711 struct proto_config *cf_krt;
712
713 static void
714 krt_preconfig(struct protocol *P UNUSED, struct config *c)
715 {
716 cf_krt = NULL;
717 krt_scan_preconfig(c);
718 }
719
720 static void
721 krt_postconfig(struct proto_config *C)
722 {
723 struct krt_config *c = (struct krt_config *) C;
724
725 #ifdef CONFIG_ALL_TABLES_AT_ONCE
726 struct krt_config *first = (struct krt_config *) cf_krt;
727 if (first->scan_time != c->scan_time)
728 cf_error("All kernel syncers must use the same table scan interval");
729 #endif
730
731 if (C->table->krt_attached)
732 cf_error("Kernel syncer (%s) already attached to table %s", C->table->krt_attached->name, C->table->name);
733 C->table->krt_attached = C;
734 krt_scan_postconfig(c);
735 }
736
737 static timer *
738 krt_start_timer(struct krt_proto *p)
739 {
740 timer *t;
741
742 t = tm_new(p->krt_pool);
743 t->hook = krt_scan;
744 t->data = p;
745 t->recurrent = KRT_CF->scan_time;
746 tm_start(t, 0);
747 return t;
748 }
749
750 static int
751 krt_start(struct proto *P)
752 {
753 struct krt_proto *p = (struct krt_proto *) P;
754 int first = 1;
755
756 #ifdef CONFIG_ALL_TABLES_AT_ONCE
757 if (!krt_instance_count++)
758 init_list(&krt_instance_list);
759 else
760 first = 0;
761 p->krt_pool = krt_pool;
762 add_tail(&krt_instance_list, &p->instance_node);
763 #else
764 p->krt_pool = P->pool;
765 #endif
766
767 #ifdef KRT_ALLOW_LEARN
768 krt_learn_init(p);
769 #endif
770
771 krt_scan_start(p, first);
772 krt_set_start(p, first);
773
774 /* Start periodic routing table scanning */
775 #ifdef CONFIG_ALL_TABLES_AT_ONCE
776 if (first)
777 krt_scan_timer = krt_start_timer(p);
778 else
779 tm_start(krt_scan_timer, 0);
780 p->scan_timer = krt_scan_timer;
781 #else
782 p->scan_timer = krt_start_timer(p);
783 #endif
784
785 return PS_UP;
786 }
787
788 static int
789 krt_shutdown(struct proto *P)
790 {
791 struct krt_proto *p = (struct krt_proto *) P;
792 int last = 1;
793
794 #ifdef CONFIG_ALL_TABLES_AT_ONCE
795 rem_node(&p->instance_node);
796 if (--krt_instance_count)
797 last = 0;
798 else
799 #endif
800 tm_stop(p->scan_timer);
801
802 /* FIXME we should flush routes even when persist during reconfiguration */
803 if (p->initialized && !KRT_CF->persist)
804 krt_flush_routes(p);
805
806 krt_set_shutdown(p, last);
807 krt_scan_shutdown(p, last);
808
809 #ifdef CONFIG_ALL_TABLES_AT_ONCE
810 if (last)
811 rfree(krt_scan_timer);
812 #endif
813
814 return PS_DOWN;
815 }
816
817 static struct proto *
818 krt_init(struct proto_config *c)
819 {
820 struct krt_proto *p = proto_new(c, sizeof(struct krt_proto));
821
822 p->p.rt_notify = krt_notify;
823 p->p.min_scope = SCOPE_HOST;
824 return &p->p;
825 }
826
827 static int
828 krt_reconfigure(struct proto *p, struct proto_config *new)
829 {
830 struct krt_config *o = (struct krt_config *) p->cf;
831 struct krt_config *n = (struct krt_config *) new;
832
833 return o->scan_time == n->scan_time
834 && o->learn == n->learn /* persist needn't be the same */
835 && krt_set_params_same(&o->set, &n->set)
836 && krt_scan_params_same(&o->scan, &n->scan)
837 ;
838 }
839
840 struct protocol proto_unix_kernel = {
841 name: "Kernel",
842 template: "kernel%d",
843 preconfig: krt_preconfig,
844 postconfig: krt_postconfig,
845 init: krt_init,
846 start: krt_start,
847 shutdown: krt_shutdown,
848 reconfigure: krt_reconfigure,
849 #ifdef KRT_ALLOW_LEARN
850 dump: krt_dump,
851 dump_attrs: krt_dump_attrs,
852 #endif
853 };