]> git.ipfire.org Git - thirdparty/bird.git/blame - sysdep/unix/krt.c
RIP fixup + dropping the tmp_attrs mechanism as obsolete
[thirdparty/bird.git] / sysdep / unix / krt.c
CommitLineData
2d140452
MM
1/*
2 * BIRD -- UNIX Kernel Synchronization
3 *
50fe90ed 4 * (c) 1998--2000 Martin Mares <mj@ucw.cz>
2d140452
MM
5 *
6 * Can be freely distributed and used under the terms of the GNU GPL.
7 */
8
73275d85
MM
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
2e9b2421 19 * to keep the overhead of our KRT business as low as possible and avoid maintaining
73275d85
MM
20 * a local routing table copy.
21 *
22 * The kernel syncer can work in three different modes (according to system config header):
725270cb 23 * Either with a single routing table and single KRT protocol [traditional UNIX]
2e9b2421 24 * or with many routing tables and separate KRT protocols for all of them
73275d85 25 * or with many routing tables, but every scan including all tables, so we start
f1aceff5 26 * separate KRT protocols which cooperate with each other [Linux].
73275d85
MM
27 * In this case, we keep only a single scan timer.
28 *
58f7d004
MM
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.
73275d85
MM
33 *
34 * When starting up, we cheat by looking if there is another
35 * KRT instance to be initialized later and performing table scan
725270cb 36 * only once for all the instances.
f1aceff5
OZ
37 *
38 * The code uses OS-dependent parts for kernel updates and scans. These parts are
252c7e4d 39 * in more specific sysdep directories (e.g. sysdep/linux) in functions krt_sys_*
7a2c48da 40 * and kif_sys_* (and some others like krt_replace_rte()) and krt-sys.h header file.
f1aceff5
OZ
41 * This is also used for platform specific protocol options and route attributes.
42 *
43 * There was also an old code that used traditional UNIX ioctls for these tasks.
44 * It was unmaintained and later removed. For reference, see sysdep/krt-* files
45 * in commit 396dfa9042305f62da1f56589c4b98fac57fc2f6
725270cb 46 */
73275d85
MM
47
48/*
49 * If you are brave enough, continue now. You cannot say you haven't been warned.
50 */
51
832fa033 52#undef LOCAL_DEBUG
2d140452
MM
53
54#include "nest/bird.h"
55#include "nest/iface.h"
56#include "nest/route.h"
57#include "nest/protocol.h"
c9df01d3 58#include "filter/filter.h"
7de45ba4 59#include "conf/conf.h"
7d875e09 60#include "lib/string.h"
a6f79ca5 61#include "lib/timer.h"
2d140452
MM
62
63#include "unix.h"
64#include "krt.h"
65
7e5f5ffd
MM
66/*
67 * Global resources
68 */
69
7de45ba4 70pool *krt_pool;
c9df01d3 71static linpool *krt_filter_lp;
c6964c30 72static list krt_proto_list;
7de45ba4 73
7e5f5ffd
MM
74void
75krt_io_init(void)
76{
7de45ba4 77 krt_pool = rp_new(&root_pool, "Kernel Syncer");
05d47bd5 78 krt_filter_lp = lp_new_default(krt_pool);
c6964c30 79 init_list(&krt_proto_list);
9ddbfbdd 80 krt_sys_io_init();
7e5f5ffd
MM
81}
82
83/*
84 * Interfaces
85 */
86
1e4891e4 87struct kif_proto *kif_proto;
396dfa90 88static struct kif_config *kif_cf;
7e5f5ffd 89static timer *kif_scan_timer;
21f4f0f4 90static btime kif_last_shot;
7e5f5ffd 91
153f02da
OZ
92static struct kif_iface_config kif_default_iface = {};
93
94struct kif_iface_config *
95kif_get_iface_config(struct iface *iface)
96{
97 struct kif_config *cf = (void *) (kif_proto->p.cf);
98 struct kif_iface_config *ic = (void *) iface_patt_find(&cf->iface_list, iface, NULL);
99 return ic ?: &kif_default_iface;
100}
101
7e5f5ffd
MM
102static void
103kif_scan(timer *t)
104{
105 struct kif_proto *p = t->data;
106
832fa033 107 KRT_TRACE(p, D_EVENTS, "Scanning interfaces");
21f4f0f4 108 kif_last_shot = current_time();
396dfa90 109 kif_do_scan(p);
7e5f5ffd
MM
110}
111
112static void
113kif_force_scan(void)
114{
21f4f0f4 115 if (kif_proto && ((kif_last_shot + 2 S) < current_time()))
7e5f5ffd
MM
116 {
117 kif_scan(kif_scan_timer);
a6f79ca5 118 tm_start(kif_scan_timer, ((struct kif_config *) kif_proto->p.cf)->scan_time);
7e5f5ffd
MM
119 }
120}
121
09686693
OZ
122void
123kif_request_scan(void)
124{
21f4f0f4 125 if (kif_proto && (kif_scan_timer->expires > (current_time() + 1 S)))
a6f79ca5 126 tm_start(kif_scan_timer, 1 S);
09686693
OZ
127}
128
396dfa90
OZ
129static struct proto *
130kif_init(struct proto_config *c)
131{
f4a60a9b 132 struct kif_proto *p = proto_new(c);
396dfa90
OZ
133
134 kif_sys_init(p);
135 return &p->p;
136}
137
138static int
139kif_start(struct proto *P)
140{
141 struct kif_proto *p = (struct kif_proto *) P;
142
143 kif_proto = p;
144 kif_sys_start(p);
145
146 /* Start periodic interface scanning */
a6f79ca5 147 kif_scan_timer = tm_new_init(P->pool, kif_scan, p, KIF_CF->scan_time, 0);
396dfa90 148 kif_scan(kif_scan_timer);
a6f79ca5 149 tm_start(kif_scan_timer, KIF_CF->scan_time);
396dfa90
OZ
150
151 return PS_UP;
152}
153
154static int
155kif_shutdown(struct proto *P)
156{
157 struct kif_proto *p = (struct kif_proto *) P;
158
a6f79ca5 159 tm_stop(kif_scan_timer);
396dfa90
OZ
160 kif_sys_shutdown(p);
161 kif_proto = NULL;
162
163 return PS_DOWN;
164}
165
f7fcb752
MM
166static int
167kif_reconfigure(struct proto *p, struct proto_config *new)
168{
169 struct kif_config *o = (struct kif_config *) p->cf;
170 struct kif_config *n = (struct kif_config *) new;
171
396dfa90 172 if (!kif_sys_reconfigure((struct kif_proto *) p, n, o))
f7fcb752 173 return 0;
874b8685 174
f7fcb752
MM
175 if (o->scan_time != n->scan_time)
176 {
a6f79ca5 177 tm_stop(kif_scan_timer);
21f4f0f4 178 kif_scan_timer->recurrent = n->scan_time;
f7fcb752 179 kif_scan(kif_scan_timer);
a6f79ca5 180 tm_start(kif_scan_timer, n->scan_time);
f7fcb752 181 }
874b8685 182
153f02da 183 if (!EMPTY_LIST(o->iface_list) || !EMPTY_LIST(n->iface_list))
874b8685
OZ
184 {
185 /* This is hack, we have to update a configuration
186 * to the new value just now, because it is used
153f02da 187 * for recalculation of preferred addresses.
874b8685
OZ
188 */
189 p->cf = new;
190
153f02da 191 if_recalc_all_preferred_addresses();
874b8685
OZ
192 }
193
f7fcb752
MM
194 return 1;
195}
196
396dfa90
OZ
197
198static void
199kif_preconfig(struct protocol *P UNUSED, struct config *c)
200{
201 kif_cf = NULL;
202 kif_sys_preconfig(c);
203}
204
205struct proto_config *
206kif_init_config(int class)
207{
208 if (kif_cf)
209 cf_error("Kernel device protocol already defined");
210
2bbc3083 211 kif_cf = (struct kif_config *) proto_config_new(&proto_unix_iface, class);
21f4f0f4 212 kif_cf->scan_time = 60 S;
153f02da 213 init_list(&kif_cf->iface_list);
396dfa90
OZ
214
215 kif_sys_init_config(kif_cf);
216 return (struct proto_config *) kif_cf;
217}
218
a7f23f58
OZ
219static void
220kif_copy_config(struct proto_config *dest, struct proto_config *src)
221{
222 struct kif_config *d = (struct kif_config *) dest;
223 struct kif_config *s = (struct kif_config *) src;
224
153f02da
OZ
225 /* Copy interface config list */
226 cfg_copy_list(&d->iface_list, &s->iface_list, sizeof(struct kif_iface_config));
a7f23f58
OZ
227
228 /* Fix sysdep parts */
396dfa90 229 kif_sys_copy_config(d, s);
a7f23f58
OZ
230}
231
7e5f5ffd 232struct protocol proto_unix_iface = {
4a591d4b
PT
233 .name = "Device",
234 .template = "device%d",
ee7e2ffd 235 .class = PROTOCOL_DEVICE,
f4a60a9b 236 .proto_size = sizeof(struct kif_proto),
2bbc3083 237 .config_size = sizeof(struct kif_config),
4a591d4b
PT
238 .preconfig = kif_preconfig,
239 .init = kif_init,
240 .start = kif_start,
241 .shutdown = kif_shutdown,
242 .reconfigure = kif_reconfigure,
243 .copy_config = kif_copy_config
7e5f5ffd 244};
2d140452 245
832fa033
MM
246/*
247 * Tracing of routes
248 */
249
cb530392
OZ
250static inline void
251krt_trace_in(struct krt_proto *p, rte *e, char *msg)
832fa033 252{
cb530392 253 if (p->p.debug & D_PACKETS)
fe9f1a6d 254 log(L_TRACE "%s: %N: %s", p->p.name, e->net->n.addr, msg);
832fa033
MM
255}
256
257static inline void
1123e707 258krt_trace_in_rl(struct tbf *f, struct krt_proto *p, rte *e, char *msg)
832fa033
MM
259{
260 if (p->p.debug & D_PACKETS)
fe9f1a6d 261 log_rl(f, L_TRACE "%s: %N: %s", p->p.name, e->net->n.addr, msg);
832fa033
MM
262}
263
c10421d3
MM
264/*
265 * Inherited Routes
266 */
267
268#ifdef KRT_ALLOW_LEARN
269
1123e707 270static struct tbf rl_alien = TBF_DEFAULT_LOG_LIMITS;
cb530392 271
c9df01d3
OZ
272/*
273 * krt_same_key() specifies what (aside from the net) is the key in
274 * kernel routing tables. It should be OS-dependent, this is for
275 * Linux. It is important for asynchronous alien updates, because a
276 * positive update is implicitly a negative one for any old route with
277 * the same key.
278 */
279
e42eedb9
MM
280static inline u32
281krt_metric(rte *a)
282{
283 eattr *ea = ea_find(a->attrs->eattrs, EA_KRT_METRIC);
284 return ea ? ea->u.data : 0;
285}
286
c10421d3
MM
287static inline int
288krt_same_key(rte *a, rte *b)
289{
e42eedb9 290 return (krt_metric(a) == krt_metric(b));
c9df01d3
OZ
291}
292
293static inline int
294krt_uptodate(rte *a, rte *b)
295{
e42eedb9 296 return (a->attrs == b->attrs);
c10421d3
MM
297}
298
299static void
300krt_learn_announce_update(struct krt_proto *p, rte *e)
301{
302 net *n = e->net;
303 rta *aa = rta_clone(e->attrs);
5cff1d5f 304 rte *ee = rte_get_temp(aa, p->p.main_source);
2003a184 305 rte_update(&p->p, n->n.addr, ee);
c10421d3
MM
306}
307
308static void
309krt_learn_announce_delete(struct krt_proto *p, net *n)
310{
2003a184 311 rte_update(&p->p, n->n.addr, NULL);
c10421d3
MM
312}
313
c9df01d3 314/* Called when alien route is discovered during scan */
c10421d3
MM
315static void
316krt_learn_scan(struct krt_proto *p, rte *e)
317{
318 net *n0 = e->net;
ff397df7 319 net *n = net_get(p->krt_table, n0->n.addr);
c10421d3
MM
320 rte *m, **mm;
321
c9df01d3 322 e->attrs = rta_lookup(e->attrs);
c10421d3
MM
323
324 for(mm=&n->routes; m = *mm; mm=&m->next)
325 if (krt_same_key(m, e))
326 break;
327 if (m)
328 {
329 if (krt_uptodate(m, e))
330 {
1123e707 331 krt_trace_in_rl(&rl_alien, p, e, "[alien] seen");
c10421d3 332 rte_free(e);
e42eedb9 333 m->pflags |= KRT_REF_SEEN;
c10421d3
MM
334 }
335 else
336 {
1123e707 337 krt_trace_in(p, e, "[alien] updated");
c10421d3
MM
338 *mm = m->next;
339 rte_free(m);
340 m = NULL;
341 }
342 }
343 else
1123e707 344 krt_trace_in(p, e, "[alien] created");
c10421d3
MM
345 if (!m)
346 {
c10421d3
MM
347 e->next = n->routes;
348 n->routes = e;
e42eedb9 349 e->pflags |= KRT_REF_SEEN;
c10421d3
MM
350 }
351}
352
c10421d3
MM
353static void
354krt_learn_prune(struct krt_proto *p)
355{
ff397df7 356 struct fib *fib = &p->krt_table->fib;
c10421d3
MM
357 struct fib_iterator fit;
358
832fa033 359 KRT_TRACE(p, D_EVENTS, "Pruning inherited routes");
c10421d3
MM
360
361 FIB_ITERATE_INIT(&fit, fib);
362again:
600998fc 363 FIB_ITERATE_START(fib, &fit, net, n)
c10421d3 364 {
c10421d3
MM
365 rte *e, **ee, *best, **pbest, *old_best;
366
e86cfd41
OZ
367 /*
368 * Note that old_best may be NULL even if there was an old best route in
369 * the previous step, because it might be replaced in krt_learn_scan().
370 * But in that case there is a new valid best route.
371 */
372
373 old_best = NULL;
c10421d3
MM
374 best = NULL;
375 pbest = NULL;
376 ee = &n->routes;
377 while (e = *ee)
378 {
e42eedb9 379 if (e->pflags & KRT_REF_BEST)
e86cfd41
OZ
380 old_best = e;
381
e42eedb9 382 if (!(e->pflags & KRT_REF_SEEN))
c10421d3
MM
383 {
384 *ee = e->next;
385 rte_free(e);
386 continue;
387 }
e86cfd41 388
e42eedb9 389 if (!best || krt_metric(best) > krt_metric(e))
c10421d3
MM
390 {
391 best = e;
392 pbest = ee;
393 }
e86cfd41 394
e42eedb9 395 e->pflags &= ~(KRT_REF_SEEN | KRT_REF_BEST);
c10421d3
MM
396 ee = &e->next;
397 }
398 if (!n->routes)
399 {
400 DBG("%I/%d: deleting\n", n->n.prefix, n->n.pxlen);
401 if (old_best)
e86cfd41
OZ
402 krt_learn_announce_delete(p, n);
403
600998fc
OZ
404 FIB_ITERATE_PUT(&fit);
405 fib_delete(fib, n);
c10421d3
MM
406 goto again;
407 }
e86cfd41 408
e42eedb9 409 best->pflags |= KRT_REF_BEST;
c10421d3
MM
410 *pbest = best->next;
411 best->next = n->routes;
412 n->routes = best;
e86cfd41
OZ
413
414 if ((best != old_best) || p->reload)
c10421d3 415 {
e42eedb9 416 DBG("%I/%d: announcing (metric=%d)\n", n->n.prefix, n->n.pxlen, krt_metric(best));
c10421d3 417 krt_learn_announce_update(p, best);
c10421d3
MM
418 }
419 else
e42eedb9 420 DBG("%I/%d: uptodate (metric=%d)\n", n->n.prefix, n->n.pxlen, krt_metric(best));
c10421d3 421 }
600998fc 422 FIB_ITERATE_END;
7069fc9e
OZ
423
424 p->reload = 0;
c10421d3
MM
425}
426
427static void
428krt_learn_async(struct krt_proto *p, rte *e, int new)
429{
430 net *n0 = e->net;
ff397df7 431 net *n = net_get(p->krt_table, n0->n.addr);
c10421d3
MM
432 rte *g, **gg, *best, **bestp, *old_best;
433
eb937358
MM
434 ASSERT(!e->attrs->cached);
435 e->attrs->pref = p->p.main_channel->preference;
436
c9df01d3 437 e->attrs = rta_lookup(e->attrs);
c10421d3
MM
438
439 old_best = n->routes;
440 for(gg=&n->routes; g = *gg; gg = &g->next)
441 if (krt_same_key(g, e))
442 break;
443 if (new)
444 {
445 if (g)
446 {
447 if (krt_uptodate(g, e))
448 {
832fa033 449 krt_trace_in(p, e, "[alien async] same");
c10421d3
MM
450 rte_free(e);
451 return;
452 }
832fa033 453 krt_trace_in(p, e, "[alien async] updated");
c10421d3
MM
454 *gg = g->next;
455 rte_free(g);
456 }
457 else
832fa033 458 krt_trace_in(p, e, "[alien async] created");
c9df01d3 459
c10421d3
MM
460 e->next = n->routes;
461 n->routes = e;
462 }
463 else if (!g)
464 {
832fa033 465 krt_trace_in(p, e, "[alien async] delete failed");
c10421d3
MM
466 rte_free(e);
467 return;
468 }
469 else
470 {
832fa033 471 krt_trace_in(p, e, "[alien async] removed");
c10421d3
MM
472 *gg = g->next;
473 rte_free(e);
474 rte_free(g);
475 }
476 best = n->routes;
477 bestp = &n->routes;
478 for(gg=&n->routes; g=*gg; gg=&g->next)
e86cfd41 479 {
e42eedb9 480 if (krt_metric(best) > krt_metric(g))
c10421d3
MM
481 {
482 best = g;
483 bestp = gg;
484 }
e86cfd41 485
e42eedb9 486 g->pflags &= ~KRT_REF_BEST;
e86cfd41
OZ
487 }
488
c10421d3
MM
489 if (best)
490 {
e42eedb9 491 best->pflags |= KRT_REF_BEST;
c10421d3
MM
492 *bestp = best->next;
493 best->next = n->routes;
494 n->routes = best;
495 }
e86cfd41 496
c10421d3
MM
497 if (best != old_best)
498 {
499 DBG("krt_learn_async: distributing change\n");
500 if (best)
e86cfd41 501 krt_learn_announce_update(p, best);
c10421d3 502 else
e86cfd41 503 krt_learn_announce_delete(p, n);
c10421d3
MM
504 }
505}
506
507static void
508krt_learn_init(struct krt_proto *p)
509{
510 if (KRT_CF->learn)
28b3b551
OZ
511 {
512 struct rtable_config *cf = mb_allocz(p->p.pool, sizeof(struct rtable_config));
513 cf->name = "Inherited";
514 cf->addr_type = p->p.net_type;
ff397df7 515 cf->internal = 1;
28b3b551 516
ff397df7 517 p->krt_table = rt_setup(p->p.pool, cf);
28b3b551 518 }
c10421d3
MM
519}
520
521static void
522krt_dump(struct proto *P)
523{
524 struct krt_proto *p = (struct krt_proto *) P;
525
526 if (!KRT_CF->learn)
527 return;
528 debug("KRT: Table of inheritable routes\n");
ff397df7 529 rt_dump(p->krt_table);
c10421d3
MM
530}
531
c10421d3
MM
532#endif
533
2d140452
MM
534/*
535 * Routes
536 */
537
c132acae 538static inline int
90a9c97e 539krt_is_installed(struct krt_proto *p, net *n)
c132acae 540{
90a9c97e 541 return n->routes && bmap_test(&p->p.main_channel->export_map, n->routes->id);
c132acae
OZ
542}
543
2d140452
MM
544static void
545krt_flush_routes(struct krt_proto *p)
546{
f4a60a9b 547 struct rtable *t = p->p.main_channel->table;
2d140452 548
832fa033 549 KRT_TRACE(p, D_EVENTS, "Flushing kernel routes");
600998fc 550 FIB_WALK(&t->fib, net, n)
2d140452 551 {
90a9c97e 552 if (krt_is_installed(p, n))
2d140452 553 {
e14bd380 554 /* FIXME: this does not work if gw is changed in export filter */
90a9c97e 555 krt_replace_rte(p, n, NULL, n->routes);
2d140452
MM
556 }
557 }
558 FIB_WALK_END;
559}
560
78a2cc28 561static struct rte *
13c0be19 562krt_export_net(struct krt_proto *p, net *net, rte **rt_free)
78a2cc28 563{
f4a60a9b 564 struct channel *c = p->p.main_channel;
0b39b1cb 565 const struct filter *filter = c->out_filter;
78a2cc28
OZ
566 rte *rt;
567
f4a60a9b 568 if (c->ra_mode == RA_MERGED)
13c0be19 569 return rt_export_merged(c, net, rt_free, krt_filter_lp, 1);
8d9eef17 570
78a2cc28
OZ
571 rt = net->routes;
572 *rt_free = NULL;
573
574 if (!rte_is_valid(rt))
575 return NULL;
576
577 if (filter == FILTER_REJECT)
578 return NULL;
579
c132acae 580 /* We could run krt_preexport() here, but it is already handled by krt_is_installed() */
78a2cc28
OZ
581
582 if (filter == FILTER_ACCEPT)
583 goto accept;
584
13c0be19 585 if (f_run(filter, &rt, krt_filter_lp, FF_SILENT) > F_ACCEPT)
78a2cc28
OZ
586 goto reject;
587
588
589accept:
590 if (rt != net->routes)
591 *rt_free = rt;
592 return rt;
593
594reject:
595 if (rt != net->routes)
596 rte_free(rt);
597 return NULL;
598}
599
2d140452 600static int
c9df01d3 601krt_same_dest(rte *k, rte *e)
2d140452
MM
602{
603 rta *ka = k->attrs, *ea = e->attrs;
604
605 if (ka->dest != ea->dest)
606 return 0;
4e276a89
JMM
607
608 if (ka->dest == RTD_UNICAST)
609 return nexthop_same(&(ka->nh), &(ea->nh));
610
611 return 1;
2d140452
MM
612}
613
614/*
615 * This gets called back when the low-level scanning code discovers a route.
616 * We expect that the route is a temporary rte and its attributes are uncached.
617 */
618
619void
e42eedb9 620krt_got_route(struct krt_proto *p, rte *e, s8 src)
2d140452 621{
7d767c5a
OZ
622 rte *new = NULL, *rt_free = NULL;
623 net *n = e->net;
e42eedb9 624 e->pflags = 0;
2d140452 625
ff2857b0 626#ifdef KRT_ALLOW_LEARN
e42eedb9 627 switch (src)
c10421d3 628 {
ff2857b0 629 case KRT_SRC_KERNEL:
7d767c5a 630 goto ignore;
c10421d3 631
ff2857b0 632 case KRT_SRC_REDIRECT:
7d767c5a 633 goto delete;
ff2857b0
OZ
634
635 case KRT_SRC_ALIEN:
c10421d3
MM
636 if (KRT_CF->learn)
637 krt_learn_scan(p, e);
638 else
c197d44e 639 {
1123e707 640 krt_trace_in_rl(&rl_alien, p, e, "[alien] ignored");
c197d44e
MM
641 rte_free(e);
642 }
c10421d3
MM
643 return;
644 }
645#endif
ff2857b0 646 /* The rest is for KRT_SRC_BIRD (or KRT_SRC_UNKNOWN) */
c10421d3 647
2d140452 648
7d767c5a 649 /* We wait for the initial feed to have correct installed state */
0c791f87 650 if (!p->ready)
7d767c5a 651 goto ignore;
0c791f87 652
7d767c5a
OZ
653 if (!krt_is_installed(p, n))
654 goto delete;
78a2cc28 655
7d767c5a 656 new = krt_export_net(p, n, &rt_free);
78a2cc28 657
7d767c5a
OZ
658 /* Rejected by filters */
659 if (!new)
660 goto delete;
78a2cc28 661
7d767c5a
OZ
662 /* Route to this destination was already seen. Strange, but it happens... */
663 if (bmap_test(&p->seen_map, new->id))
664 goto aseen;
78a2cc28 665
7d767c5a
OZ
666 /* Mark route as seen */
667 bmap_set(&p->seen_map, new->id);
78a2cc28 668
7d767c5a
OZ
669 /* TODO: There also may be changes in route eattrs, we ignore that for now. */
670 if (!bmap_test(&p->sync_map, new->id) || !krt_same_dest(e, new))
671 goto update;
2d140452 672
7d767c5a
OZ
673 goto seen;
674
675seen:
676 krt_trace_in(p, e, "seen");
677 goto done;
678
679aseen:
680 krt_trace_in(p, e, "already seen");
681 goto done;
682
683ignore:
684 krt_trace_in(p, e, "ignored");
685 goto done;
686
687update:
688 krt_trace_in(p, new, "updating");
689 krt_replace_rte(p, n, new, e);
690 goto done;
691
692delete:
693 krt_trace_in(p, e, "deleting");
694 krt_replace_rte(p, n, NULL, e);
695 goto done;
696
697done:
698 rte_free(e);
699
700 if (rt_free)
701 rte_free(rt_free);
702
703 lp_flush(krt_filter_lp);
704}
705
706static void
707krt_init_scan(struct krt_proto *p)
708{
709 bmap_reset(&p->seen_map, 1024);
2d140452
MM
710}
711
712static void
713krt_prune(struct krt_proto *p)
714{
f4a60a9b 715 struct rtable *t = p->p.main_channel->table;
2d140452 716
832fa033 717 KRT_TRACE(p, D_EVENTS, "Pruning table %s", t->name);
600998fc 718 FIB_WALK(&t->fib, net, n)
7d767c5a
OZ
719 {
720 if (p->ready && krt_is_installed(p, n) && !bmap_test(&p->seen_map, n->routes->id))
2d140452 721 {
7d767c5a
OZ
722 rte *rt_free = NULL;
723 rte *new = krt_export_net(p, n, &rt_free);
2d140452 724
7d767c5a
OZ
725 if (new)
726 {
727 krt_trace_in(p, new, "installing");
728 krt_replace_rte(p, n, new, NULL);
729 }
c9df01d3 730
78a2cc28
OZ
731 if (rt_free)
732 rte_free(rt_free);
7d767c5a 733
c9df01d3 734 lp_flush(krt_filter_lp);
2d140452 735 }
7d767c5a 736 }
2d140452 737 FIB_WALK_END;
c10421d3
MM
738
739#ifdef KRT_ALLOW_LEARN
740 if (KRT_CF->learn)
741 krt_learn_prune(p);
742#endif
0c791f87
OZ
743
744 if (p->ready)
745 p->initialized = 1;
2d140452
MM
746}
747
e16155ae 748void
e42eedb9 749krt_got_route_async(struct krt_proto *p, rte *e, int new, s8 src)
e16155ae
MM
750{
751 net *net = e->net;
e42eedb9 752 e->pflags = 0;
e16155ae 753
e42eedb9 754 switch (src)
e16155ae
MM
755 {
756 case KRT_SRC_BIRD:
d4cebc6b
JMM
757 /* Should be filtered by the back end */
758 bug("BIRD originated routes should not get here.");
ff2857b0 759
e16155ae 760 case KRT_SRC_REDIRECT:
ff2857b0
OZ
761 if (new)
762 {
763 krt_trace_in(p, e, "[redirect] deleting");
13c0be19 764 krt_replace_rte(p, net, NULL, e);
ff2857b0
OZ
765 }
766 /* If !new, it is probably echo of our deletion */
e16155ae 767 break;
ff2857b0 768
c10421d3 769#ifdef KRT_ALLOW_LEARN
1bc4b2cc 770 case KRT_SRC_ALIEN:
c10421d3 771 if (KRT_CF->learn)
e16155ae 772 {
c10421d3
MM
773 krt_learn_async(p, e, new);
774 return;
e16155ae 775 }
c10421d3 776#endif
e16155ae 777 }
c10421d3 778 rte_free(e);
e16155ae
MM
779}
780
2d140452
MM
781/*
782 * Periodic scanning
783 */
784
c6964c30
OZ
785
786#ifdef CONFIG_ALL_TABLES_AT_ONCE
787
788static timer *krt_scan_timer;
789static int krt_scan_count;
790
2d140452 791static void
6578a604 792krt_scan(timer *t UNUSED)
2d140452 793{
7de45ba4 794 struct krt_proto *p;
7d767c5a 795 node *n;
2d140452 796
7e5f5ffd 797 kif_force_scan();
c6964c30
OZ
798
799 /* We need some node to decide whether to print the debug messages or not */
800 p = SKIP_BACK(struct krt_proto, krt_node, HEAD(krt_proto_list));
801 KRT_TRACE(p, D_EVENTS, "Scanning routing table");
802
7d767c5a
OZ
803 WALK_LIST2(p, n, krt_proto_list, krt_node)
804 krt_init_scan(p);
805
c6964c30
OZ
806 krt_do_scan(NULL);
807
7d767c5a 808 WALK_LIST2(p, n, krt_proto_list, krt_node)
c6964c30 809 krt_prune(p);
c6964c30
OZ
810}
811
812static void
813krt_scan_timer_start(struct krt_proto *p)
814{
815 if (!krt_scan_count)
a6f79ca5 816 krt_scan_timer = tm_new_init(krt_pool, krt_scan, NULL, KRT_CF->scan_time, 0);
c6964c30
OZ
817
818 krt_scan_count++;
f8cc7396 819
a6f79ca5 820 tm_start(krt_scan_timer, 1 S);
c6964c30
OZ
821}
822
823static void
3e236955 824krt_scan_timer_stop(struct krt_proto *p UNUSED)
c6964c30
OZ
825{
826 krt_scan_count--;
827
828 if (!krt_scan_count)
829 {
830 rfree(krt_scan_timer);
831 krt_scan_timer = NULL;
7de45ba4 832 }
c6964c30
OZ
833}
834
0c791f87
OZ
835static void
836krt_scan_timer_kick(struct krt_proto *p UNUSED)
837{
a6f79ca5 838 tm_start(krt_scan_timer, 0);
0c791f87
OZ
839}
840
7de45ba4 841#else
c6964c30
OZ
842
843static void
844krt_scan(timer *t)
845{
846 struct krt_proto *p = t->data;
847
848 kif_force_scan();
849
832fa033 850 KRT_TRACE(p, D_EVENTS, "Scanning routing table");
7d767c5a 851 krt_init_scan(p);
396dfa90 852 krt_do_scan(p);
7e5f5ffd 853 krt_prune(p);
2d140452
MM
854}
855
c6964c30
OZ
856static void
857krt_scan_timer_start(struct krt_proto *p)
858{
a6f79ca5
OZ
859 p->scan_timer = tm_new_init(p->p.pool, krt_scan, p, KRT_CF->scan_time, 0);
860 tm_start(p->scan_timer, 1 S);
c6964c30
OZ
861}
862
863static void
864krt_scan_timer_stop(struct krt_proto *p)
865{
a6f79ca5 866 tm_stop(p->scan_timer);
c6964c30
OZ
867}
868
0c791f87 869static void
252c7e4d 870krt_scan_timer_kick(struct krt_proto *p)
0c791f87 871{
a6f79ca5 872 tm_start(p->scan_timer, 0);
0c791f87
OZ
873}
874
c6964c30
OZ
875#endif
876
877
878
396dfa90 879
c10421d3
MM
880/*
881 * Updates
882 */
396dfa90 883
c429d4a4 884static int
d5a32563 885krt_preexport(struct proto *P, rte *e)
c429d4a4 886{
62e64905 887 // struct krt_proto *p = (struct krt_proto *) P;
5cff1d5f 888 if (e->src->proto == P)
c429d4a4
OZ
889 return -1;
890
c429d4a4
OZ
891 if (!krt_capable(e))
892 return -1;
893
894 return 0;
895}
c10421d3
MM
896
897static void
4bdf1881 898krt_rt_notify(struct proto *P, struct channel *ch UNUSED, net *net,
13c0be19 899 rte *new, rte *old)
c10421d3
MM
900{
901 struct krt_proto *p = (struct krt_proto *) P;
902
a92cf57d 903 if (config->shutdown)
f990fc61 904 return;
c132acae
OZ
905
906#ifdef CONFIG_SINGLE_ROUTE
907 /*
908 * Implicit withdraw - when the imported kernel route becomes the best one,
909 * we know that the previous one exported to the kernel was already removed,
910 * but if we processed the update as usual, we would send withdraw to the
911 * kernel, which would remove the new imported route instead.
912 */
913 rte *best = net->routes;
914 if (!new && best && (best->attrs->src->proto == P))
915 return;
916#endif
917
c9df01d3 918 if (p->initialized) /* Before first scan we don't touch the routes */
13c0be19 919 krt_replace_rte(p, net, new, old);
c10421d3
MM
920}
921
252c7e4d
OZ
922static void
923krt_if_notify(struct proto *P, uint flags, struct iface *iface UNUSED)
924{
925 struct krt_proto *p = (struct krt_proto *) P;
926
927 /*
928 * When interface went down, we should remove routes to it. In the ideal world,
929 * OS kernel would send us route removal notifications in such cases, but we
930 * cannot rely on it as it is often not true. E.g. Linux kernel removes related
931 * routes when an interface went down, but it does not notify userspace about
932 * that. To be sure, we just schedule a scan to ensure synchronization.
933 */
934
935 if ((flags & IF_CHANGE_DOWN) && KRT_CF->learn)
936 krt_scan_timer_kick(p);
937}
938
f4a60a9b
OZ
939static void
940krt_reload_routes(struct channel *C)
252c7e4d 941{
f4a60a9b 942 struct krt_proto *p = (void *) C->proto;
252c7e4d
OZ
943
944 /* Although we keep learned routes in krt_table, we rather schedule a scan */
945
946 if (KRT_CF->learn)
7069fc9e
OZ
947 {
948 p->reload = 1;
252c7e4d 949 krt_scan_timer_kick(p);
7069fc9e 950 }
252c7e4d
OZ
951}
952
0c791f87 953static void
f4a60a9b 954krt_feed_end(struct channel *C)
0c791f87 955{
f4a60a9b 956 struct krt_proto *p = (void *) C->proto;
0c791f87
OZ
957
958 p->ready = 1;
959 krt_scan_timer_kick(p);
960}
961
962
2d140452
MM
963/*
964 * Protocol glue
965 */
966
396dfa90 967struct krt_config *krt_cf;
7e5f5ffd 968
f4a60a9b
OZ
969static void
970krt_preconfig(struct protocol *P UNUSED, struct config *c)
971{
972 krt_cf = NULL;
973 krt_sys_preconfig(c);
974}
975
976static void
977krt_postconfig(struct proto_config *CF)
978{
979 struct krt_config *cf = (void *) CF;
980
2a8cc725
OZ
981 /* Do not check templates at all */
982 if (cf->c.class == SYM_TEMPLATE)
983 return;
984
47d92d8f 985 if (! proto_cf_main_channel(CF))
f4a60a9b
OZ
986 cf_error("Channel not specified");
987
988#ifdef CONFIG_ALL_TABLES_AT_ONCE
989 if (krt_cf->scan_time != cf->scan_time)
990 cf_error("All kernel syncers must use the same table scan interval");
991#endif
992
ace3072e
OZ
993 struct channel_config *cc = proto_cf_main_channel(CF);
994 struct rtable_config *tab = cc->table;
f4a60a9b
OZ
995 if (tab->krt_attached)
996 cf_error("Kernel syncer (%s) already attached to table %s", tab->krt_attached->name, tab->name);
997 tab->krt_attached = CF;
998
ace3072e
OZ
999 if (cf->merge_paths)
1000 {
1001 cc->ra_mode = RA_MERGED;
1002 cc->merge_limit = cf->merge_paths;
1003 }
1004
f4a60a9b
OZ
1005 krt_sys_postconfig(cf);
1006}
1007
396dfa90 1008static struct proto *
f4a60a9b 1009krt_init(struct proto_config *CF)
7de45ba4 1010{
f4a60a9b
OZ
1011 struct krt_proto *p = proto_new(CF);
1012 // struct krt_config *cf = (void *) CF;
1013
1014 p->p.main_channel = proto_add_channel(&p->p, proto_cf_main_channel(CF));
7de45ba4 1015
14375237 1016 p->p.preexport = krt_preexport;
252c7e4d
OZ
1017 p->p.rt_notify = krt_rt_notify;
1018 p->p.if_notify = krt_if_notify;
1019 p->p.reload_routes = krt_reload_routes;
9aed29e6 1020 p->p.feed_end = krt_feed_end;
7de45ba4 1021
396dfa90
OZ
1022 krt_sys_init(p);
1023 return &p->p;
7de45ba4
MM
1024}
1025
2d140452
MM
1026static int
1027krt_start(struct proto *P)
1028{
1029 struct krt_proto *p = (struct krt_proto *) P;
7de45ba4 1030
f4a60a9b 1031 switch (p->p.net_type)
29a64162 1032 {
be17805c
OZ
1033 case NET_IP4: p->af = AF_INET; break;
1034 case NET_IP6: p->af = AF_INET6; break;
1035 case NET_IP6_SADR: p->af = AF_INET6; break;
1d213067 1036#ifdef AF_MPLS
be17805c 1037 case NET_MPLS: p->af = AF_MPLS; break;
1d213067 1038#endif
d14f8c3c 1039 default: log(L_ERR "KRT: Tried to start with strange net type: %d", p->p.net_type); return PS_START; break;
29a64162
OZ
1040 }
1041
cc75b3e1 1042 bmap_init(&p->sync_map, p->p.pool, 1024);
7d767c5a 1043 bmap_init(&p->seen_map, p->p.pool, 1024);
c6964c30 1044 add_tail(&krt_proto_list, &p->krt_node);
2d140452 1045
c10421d3
MM
1046#ifdef KRT_ALLOW_LEARN
1047 krt_learn_init(p);
1048#endif
1049
9ddbfbdd
JMM
1050 if (!krt_sys_start(p))
1051 {
1052 rem_node(&p->krt_node);
1053 return PS_START;
1054 }
2d140452 1055
c6964c30 1056 krt_scan_timer_start(p);
2d140452 1057
f4a60a9b
OZ
1058 if (p->p.gr_recovery && KRT_CF->graceful_restart)
1059 p->p.main_channel->gr_wait = 1;
0c791f87 1060
2d140452
MM
1061 return PS_UP;
1062}
1063
7de45ba4 1064static int
2d140452
MM
1065krt_shutdown(struct proto *P)
1066{
1067 struct krt_proto *p = (struct krt_proto *) P;
1068
c6964c30 1069 krt_scan_timer_stop(p);
7e5f5ffd 1070
3d574679 1071 /* FIXME we should flush routes even when persist during reconfiguration */
8a68316e 1072 if (p->initialized && !KRT_CF->persist && (P->down_code != PDC_CMD_GR_DOWN))
2d140452
MM
1073 krt_flush_routes(p);
1074
0c791f87
OZ
1075 p->ready = 0;
1076 p->initialized = 0;
1077
9ddbfbdd
JMM
1078 if (p->p.proto_state == PS_START)
1079 return PS_DOWN;
7de45ba4 1080
9ddbfbdd 1081 krt_sys_shutdown(p);
c6964c30 1082 rem_node(&p->krt_node);
cc75b3e1 1083 bmap_free(&p->sync_map);
2d140452 1084
2d140452
MM
1085 return PS_DOWN;
1086}
1087
396dfa90 1088static int
f4a60a9b 1089krt_reconfigure(struct proto *p, struct proto_config *CF)
72aed1a0 1090{
f4a60a9b
OZ
1091 struct krt_config *o = (void *) p->cf;
1092 struct krt_config *n = (void *) CF;
1093
1094 if (!proto_configure_channel(p, &p->main_channel, proto_cf_main_channel(CF)))
1095 return 0;
72aed1a0 1096
396dfa90
OZ
1097 if (!krt_sys_reconfigure((struct krt_proto *) p, n, o))
1098 return 0;
9ba2798c 1099
0c791f87 1100 /* persist, graceful restart need not be the same */
e5ff7929 1101 return o->scan_time == n->scan_time && o->learn == n->learn;
2d140452
MM
1102}
1103
396dfa90
OZ
1104struct proto_config *
1105krt_init_config(int class)
aa8761de 1106{
396dfa90
OZ
1107#ifndef CONFIG_MULTIPLE_TABLES
1108 if (krt_cf)
1109 cf_error("Kernel protocol already defined");
1110#endif
1111
2bbc3083 1112 krt_cf = (struct krt_config *) proto_config_new(&proto_unix_kernel, class);
21f4f0f4 1113 krt_cf->scan_time = 60 S;
aa8761de 1114
396dfa90
OZ
1115 krt_sys_init_config(krt_cf);
1116 return (struct proto_config *) krt_cf;
aa8761de
MM
1117}
1118
a7f23f58
OZ
1119static void
1120krt_copy_config(struct proto_config *dest, struct proto_config *src)
1121{
1122 struct krt_config *d = (struct krt_config *) dest;
1123 struct krt_config *s = (struct krt_config *) src;
1124
a7f23f58 1125 /* Fix sysdep parts */
396dfa90 1126 krt_sys_copy_config(d, s);
a7f23f58 1127}
71ca7716
OZ
1128
1129static int
258be565 1130krt_get_attr(const eattr *a, byte *buf, int buflen)
71ca7716
OZ
1131{
1132 switch (a->id)
1133 {
72aed1a0
OZ
1134 case EA_KRT_SOURCE:
1135 bsprintf(buf, "source");
1136 return GA_NAME;
1137
9ba2798c
OZ
1138 case EA_KRT_METRIC:
1139 bsprintf(buf, "metric");
1140 return GA_NAME;
1141
71ca7716 1142 default:
9fdf9d29 1143 return krt_sys_get_attr(a, buf, buflen);
71ca7716
OZ
1144 }
1145}
1146
1147
be17805c
OZ
1148#ifdef CONFIG_IP6_SADR_KERNEL
1149#define MAYBE_IP6_SADR NB_IP6_SADR
1150#else
1151#define MAYBE_IP6_SADR 0
1152#endif
1153
1154#ifdef HAVE_MPLS_KERNEL
1155#define MAYBE_MPLS NB_MPLS
1156#else
1157#define MAYBE_MPLS 0
1158#endif
1159
2d140452 1160struct protocol proto_unix_kernel = {
4a591d4b
PT
1161 .name = "Kernel",
1162 .template = "kernel%d",
ee7e2ffd 1163 .class = PROTOCOL_KERNEL,
4a591d4b 1164 .preference = DEF_PREF_INHERITED,
be17805c 1165 .channel_mask = NB_IP | MAYBE_IP6_SADR | MAYBE_MPLS,
f4a60a9b 1166 .proto_size = sizeof(struct krt_proto),
2bbc3083 1167 .config_size = sizeof(struct krt_config),
4a591d4b
PT
1168 .preconfig = krt_preconfig,
1169 .postconfig = krt_postconfig,
1170 .init = krt_init,
1171 .start = krt_start,
1172 .shutdown = krt_shutdown,
1173 .reconfigure = krt_reconfigure,
1174 .copy_config = krt_copy_config,
1175 .get_attr = krt_get_attr,
c10421d3 1176#ifdef KRT_ALLOW_LEARN
4a591d4b 1177 .dump = krt_dump,
c10421d3 1178#endif
2d140452 1179};