2 * BIRD -- UNIX Kernel Synchronization
4 * (c) 1998--1999 Martin Mares <mj@ucw.cz>
6 * Can be freely distributed and used under the terms of the GNU GPL.
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"
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.
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.
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.
42 * If you are brave enough, continue now. You cannot say you haven't been warned.
45 static int krt_uptodate(rte
*k
, rte
*e
);
56 krt_pool
= rp_new(&root_pool
, "Kernel Syncer");
64 struct proto_config
*cf_kif
;
66 static struct kif_proto
*kif_proto
;
67 static timer
*kif_scan_timer
;
68 static bird_clock_t kif_last_shot
;
73 struct kif_proto
*p
= t
->data
;
75 DBG("KIF: It's interface scan time...\n");
83 if (kif_proto
&& kif_last_shot
+ 2 < now
)
85 kif_scan(kif_scan_timer
);
86 tm_start(kif_scan_timer
, ((struct kif_config
*) kif_proto
->p
.cf
)->scan_time
);
91 kif_init(struct proto_config
*c
)
93 struct kif_proto
*p
= proto_new(c
, sizeof(struct kif_proto
));
98 kif_start(struct proto
*P
)
100 struct kif_proto
*p
= (struct kif_proto
*) P
;
105 /* Start periodic interface scanning */
106 kif_scan_timer
= tm_new(P
->pool
);
107 kif_scan_timer
->hook
= kif_scan
;
108 kif_scan_timer
->data
= p
;
109 kif_scan_timer
->recurrent
= KIF_CF
->scan_time
;
110 kif_scan(kif_scan_timer
);
111 tm_start(kif_scan_timer
, KIF_CF
->scan_time
);
117 kif_shutdown(struct proto
*P
)
119 struct kif_proto
*p
= (struct kif_proto
*) P
;
121 tm_stop(kif_scan_timer
);
125 if_start_update(); /* Remove all interfaces */
128 * FIXME: Is it really a good idea? It causes routes to be flushed,
129 * but at the same time it avoids sending of these deletions to the kernel,
130 * because krt thinks the kernel itself has already removed the route
131 * when downing the interface. Sad.
137 struct protocol proto_unix_iface
= {
142 shutdown
: kif_shutdown
,
149 #ifdef KRT_ALLOW_LEARN
152 krt_same_key(rte
*a
, rte
*b
)
154 return a
->u
.krt
.proto
== b
->u
.krt
.proto
&&
155 a
->u
.krt
.metric
== b
->u
.krt
.metric
&&
156 a
->u
.krt
.type
== b
->u
.krt
.type
;
160 krt_learn_announce_update(struct krt_proto
*p
, rte
*e
)
163 rta
*aa
= rta_clone(e
->attrs
);
164 rte
*ee
= rte_get_temp(aa
);
165 net
*nn
= net_get(p
->p
.table
, n
->n
.prefix
, n
->n
.pxlen
);
168 ee
->u
.krt
= e
->u
.krt
;
169 rte_update(p
->p
.table
, nn
, &p
->p
, ee
);
173 krt_learn_announce_delete(struct krt_proto
*p
, net
*n
)
175 n
= net_find(p
->p
.table
, n
->n
.prefix
, n
->n
.pxlen
);
177 rte_update(p
->p
.table
, n
, &p
->p
, NULL
);
181 krt_learn_scan(struct krt_proto
*p
, rte
*e
)
184 net
*n
= net_get(&p
->krt_table
, n0
->n
.prefix
, n0
->n
.pxlen
);
187 e
->attrs
->source
= RTS_INHERIT
;
189 for(mm
=&n
->routes
; m
= *mm
; mm
=&m
->next
)
190 if (krt_same_key(m
, e
))
194 if (krt_uptodate(m
, e
))
196 DBG("krt_learn_scan: SEEN\n");
202 DBG("krt_learn_scan: OVERRIDE\n");
209 DBG("krt_learn_scan: CREATE\n");
212 e
->attrs
= rta_lookup(e
->attrs
);
220 krt_learn_prune(struct krt_proto
*p
)
222 struct fib
*fib
= &p
->krt_table
.fib
;
223 struct fib_iterator fit
;
225 DBG("Pruning inheritance data...\n");
227 FIB_ITERATE_INIT(&fit
, fib
);
229 FIB_ITERATE_START(fib
, &fit
, f
)
232 rte
*e
, **ee
, *best
, **pbest
, *old_best
;
234 old_best
= n
->routes
;
246 if (!best
|| best
->u
.krt
.metric
> e
->u
.krt
.metric
)
256 DBG("%I/%d: deleting\n", n
->n
.prefix
, n
->n
.pxlen
);
259 krt_learn_announce_delete(p
, n
);
260 n
->n
.flags
&= ~KRF_INSTALLED
;
262 FIB_ITERATE_PUT(&fit
, f
);
267 best
->next
= n
->routes
;
269 if (best
!= old_best
|| !(n
->n
.flags
& KRF_INSTALLED
))
271 DBG("%I/%d: announcing (metric=%d)\n", n
->n
.prefix
, n
->n
.pxlen
, best
->u
.krt
.metric
);
272 krt_learn_announce_update(p
, best
);
273 n
->n
.flags
|= KRF_INSTALLED
;
276 DBG("%I/%d: uptodate (metric=%d)\n", n
->n
.prefix
, n
->n
.pxlen
, best
->u
.krt
.metric
);
282 krt_learn_async(struct krt_proto
*p
, rte
*e
, int new)
285 net
*n
= net_get(&p
->krt_table
, n0
->n
.prefix
, n0
->n
.pxlen
);
286 rte
*g
, **gg
, *best
, **bestp
, *old_best
;
288 e
->attrs
->source
= RTS_INHERIT
;
290 old_best
= n
->routes
;
291 for(gg
=&n
->routes
; g
= *gg
; gg
= &g
->next
)
292 if (krt_same_key(g
, e
))
298 if (krt_uptodate(g
, e
))
300 DBG("krt_learn_async: same\n");
304 DBG("krt_learn_async: update\n");
309 DBG("krt_learn_async: create\n");
310 e
->attrs
= rta_lookup(e
->attrs
);
316 DBG("krt_learn_async: not found\n");
322 DBG("krt_learn_async: delete\n");
329 for(gg
=&n
->routes
; g
=*gg
; gg
=&g
->next
)
330 if (best
->u
.krt
.metric
> g
->u
.krt
.metric
)
338 best
->next
= n
->routes
;
341 if (best
!= old_best
)
343 DBG("krt_learn_async: distributing change\n");
346 krt_learn_announce_update(p
, best
);
347 n
->n
.flags
|= KRF_INSTALLED
;
352 krt_learn_announce_delete(p
, n
);
353 n
->n
.flags
&= ~KRF_INSTALLED
;
359 krt_learn_init(struct krt_proto
*p
)
362 rt_setup(p
->p
.pool
, &p
->krt_table
, "Inherited");
366 krt_dump(struct proto
*P
)
368 struct krt_proto
*p
= (struct krt_proto
*) P
;
372 debug("KRT: Table of inheritable routes\n");
373 rt_dump(&p
->krt_table
);
377 krt_dump_attrs(rte
*e
)
379 debug(" [m=%d,p=%d,t=%d]", e
->u
.krt
.metric
, e
->u
.krt
.proto
, e
->u
.krt
.type
);
388 #ifdef CONFIG_ALL_TABLES_AT_ONCE
389 static timer
*krt_scan_timer
;
390 static int krt_instance_count
;
391 static list krt_instance_list
;
395 krt_flush_routes(struct krt_proto
*p
)
397 struct rtable
*t
= p
->p
.table
;
399 DBG("Flushing kernel routes...\n");
407 if (a
->source
!= RTS_DEVICE
&& a
->source
!= RTS_INHERIT
)
408 krt_set_notify(p
, e
->net
, NULL
, e
);
415 krt_uptodate(rte
*k
, rte
*e
)
417 rta
*ka
= k
->attrs
, *ea
= e
->attrs
;
419 if (ka
->dest
!= ea
->dest
)
424 return ipa_equal(ka
->gw
, ea
->gw
);
426 return !strcmp(ka
->iface
->name
, ea
->iface
->name
);
433 * This gets called back when the low-level scanning code discovers a route.
434 * We expect that the route is a temporary rte and its attributes are uncached.
438 krt_got_route(struct krt_proto
*p
, rte
*e
)
442 int src
= e
->u
.krt
.src
;
445 #ifdef CONFIG_AUTO_ROUTES
446 if (e
->attrs
->dest
== RTD_DEVICE
)
448 /* It's a device route. Probably a kernel-generated one. */
449 verdict
= KRF_IGNORE
;
454 #ifdef KRT_ALLOW_LEARN
455 if (src
== KRT_SRC_ALIEN
)
458 krt_learn_scan(p
, e
);
460 DBG("krt_parse_entry: Alien route, ignoring\n");
465 if (net
->n
.flags
& KRF_VERDICT_MASK
)
467 /* Route to this destination was already seen. Strange, but it happens... */
468 DBG("Already seen.\n");
472 if (net
->n
.flags
& KRF_INSTALLED
)
476 if (krt_uptodate(e
, old
))
479 verdict
= KRF_UPDATE
;
482 verdict
= KRF_DELETE
;
485 DBG("krt_parse_entry: verdict=%s\n", ((char *[]) { "CREATE", "SEEN", "UPDATE", "DELETE", "IGNORE" }) [verdict
]);
487 net
->n
.flags
= (net
->n
.flags
& ~KRF_VERDICT_MASK
) | verdict
;
488 if (verdict
== KRF_UPDATE
|| verdict
== KRF_DELETE
)
490 /* Get a cached copy of attributes and link the route */
492 a
->source
= RTS_DUMMY
;
493 e
->attrs
= rta_lookup(a
);
494 e
->next
= net
->routes
;
502 krt_prune(struct krt_proto
*p
)
504 struct proto
*pp
= &p
->p
;
505 struct rtable
*t
= p
->p
.table
;
508 DBG("Pruning routes in table %s...\n", t
->name
);
512 int verdict
= f
->flags
& KRF_VERDICT_MASK
;
515 if (verdict
!= KRF_CREATE
&& verdict
!= KRF_SEEN
&& verdict
!= KRF_IGNORE
)
518 n
->routes
= old
->next
;
527 if (new && (f
->flags
& KRF_INSTALLED
))
529 DBG("krt_prune: reinstalling %I/%d\n", n
->n
.prefix
, n
->n
.pxlen
);
530 krt_set_notify(p
, n
, new, NULL
);
535 /* Nothing happens */
538 DBG("krt_prune: updating %I/%d\n", n
->n
.prefix
, n
->n
.pxlen
);
539 krt_set_notify(p
, n
, new, old
);
542 DBG("krt_prune: deleting %I/%d\n", n
->n
.prefix
, n
->n
.pxlen
);
543 krt_set_notify(p
, n
, NULL
, old
);
546 bug("krt_prune: invalid route status");
550 f
->flags
&= ~KRF_VERDICT_MASK
;
554 #ifdef KRT_ALLOW_LEARN
561 krt_got_route_async(struct krt_proto
*p
, rte
*e
, int new)
564 rte
*old
= net
->routes
;
565 int src
= e
->u
.krt
.src
;
571 case KRT_SRC_REDIRECT
:
572 DBG("It's a redirect, kill him! Kill! Kill!\n");
573 krt_set_notify(p
, net
, NULL
, e
);
576 #ifdef KRT_ALLOW_LEARN
579 krt_learn_async(p
, e
, new);
586 rte_update(p
->p
.table
, net
, &p
->p
, NULL
);
601 #ifdef CONFIG_ALL_TABLES_AT_ONCE
604 DBG("KRT: It's route scan time...\n");
606 WALK_LIST(q
, krt_instance_list
)
608 p
= SKIP_BACK(struct krt_proto
, instance_node
, q
);
614 DBG("KRT: It's route scan time for %s...\n", p
->p
.name
);
625 krt_notify(struct proto
*P
, net
*net
, rte
*new, rte
*old
, struct ea_list
*tmpa
)
627 struct krt_proto
*p
= (struct krt_proto
*) P
;
629 if (new && (!krt_capable(new) || new->attrs
->source
== RTS_INHERIT
))
631 if (!(net
->n
.flags
& KRF_INSTALLED
))
634 net
->n
.flags
|= KRF_INSTALLED
;
636 net
->n
.flags
&= ~KRF_INSTALLED
;
637 krt_set_notify(p
, net
, new, old
);
644 struct proto_config
*cf_krt
;
647 krt_preconfig(struct protocol
*P
, struct config
*c
)
649 krt_scan_preconfig(c
);
653 krt_postconfig(struct proto_config
*C
)
655 struct krt_config
*c
= (struct krt_config
*) C
;
657 #ifdef CONFIG_ALL_TABLES_AT_ONCE
658 struct krt_config
*first
= (struct krt_config
*) cf_krt
;
659 if (first
->scan_time
!= c
->scan_time
)
660 cf_error("All kernel syncers must use the same table scan interval");
663 if (C
->table
->krt_attached
)
664 cf_error("Kernel syncer (%s) already attached to table %s", C
->table
->krt_attached
->name
, C
->table
->name
);
665 C
->table
->krt_attached
= C
;
666 krt_scan_postconfig(c
);
670 krt_start_timer(struct krt_proto
*p
)
674 t
= tm_new(p
->krt_pool
);
677 t
->recurrent
= KRT_CF
->scan_time
;
678 tm_start(t
, KRT_CF
->scan_time
);
683 krt_start(struct proto
*P
)
685 struct krt_proto
*p
= (struct krt_proto
*) P
;
688 #ifdef CONFIG_ALL_TABLES_AT_ONCE
689 if (!krt_instance_count
++)
690 init_list(&krt_instance_list
);
693 p
->krt_pool
= krt_pool
;
694 add_tail(&krt_instance_list
, &p
->instance_node
);
696 p
->krt_pool
= P
->pool
;
699 #ifdef KRT_ALLOW_LEARN
703 krt_scan_start(p
, first
);
704 krt_set_start(p
, first
);
706 /* Start periodic routing table scanning */
707 #ifdef CONFIG_ALL_TABLES_AT_ONCE
709 krt_scan_timer
= krt_start_timer(p
);
710 p
->scan_timer
= krt_scan_timer
;
711 /* If this is the last instance to be initialized, kick the timer */
712 if (!P
->proto
->startup_counter
)
713 krt_scan(p
->scan_timer
);
715 p
->scan_timer
= krt_start_timer(p
);
716 krt_scan(p
->scan_timer
);
723 krt_shutdown(struct proto
*P
)
725 struct krt_proto
*p
= (struct krt_proto
*) P
;
728 #ifdef CONFIG_ALL_TABLES_AT_ONCE
729 rem_node(&p
->instance_node
);
730 if (--krt_instance_count
)
734 tm_stop(p
->scan_timer
);
736 if (!KRT_CF
->persist
)
739 krt_set_shutdown(p
, last
);
740 krt_scan_shutdown(p
, last
);
742 #ifdef CONFIG_ALL_TABLES_AT_ONCE
744 rfree(krt_scan_timer
);
750 static struct proto
*
751 krt_init(struct proto_config
*c
)
753 struct krt_proto
*p
= proto_new(c
, sizeof(struct krt_proto
));
755 p
->p
.rt_notify
= krt_notify
;
756 p
->p
.min_scope
= SCOPE_HOST
;
760 struct protocol proto_unix_kernel
= {
763 preconfig
: krt_preconfig
,
764 postconfig
: krt_postconfig
,
767 shutdown
: krt_shutdown
,
768 #ifdef KRT_ALLOW_LEARN
770 dump_attrs
: krt_dump_attrs
,