]>
Commit | Line | Data |
---|---|---|
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 | 70 | pool *krt_pool; |
c9df01d3 | 71 | static linpool *krt_filter_lp; |
c6964c30 | 72 | static list krt_proto_list; |
7de45ba4 | 73 | |
7e5f5ffd MM |
74 | void |
75 | krt_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 | 87 | struct kif_proto *kif_proto; |
396dfa90 | 88 | static struct kif_config *kif_cf; |
7e5f5ffd | 89 | static timer *kif_scan_timer; |
21f4f0f4 | 90 | static btime kif_last_shot; |
7e5f5ffd | 91 | |
153f02da OZ |
92 | static struct kif_iface_config kif_default_iface = {}; |
93 | ||
94 | struct kif_iface_config * | |
95 | kif_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 |
102 | static void |
103 | kif_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 | ||
112 | static void | |
113 | kif_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 |
122 | void |
123 | kif_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 |
129 | static struct proto * |
130 | kif_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 | ||
138 | static int | |
139 | kif_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 | ||
154 | static int | |
155 | kif_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 |
166 | static int |
167 | kif_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 | |
198 | static void | |
199 | kif_preconfig(struct protocol *P UNUSED, struct config *c) | |
200 | { | |
201 | kif_cf = NULL; | |
202 | kif_sys_preconfig(c); | |
203 | } | |
204 | ||
205 | struct proto_config * | |
206 | kif_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 |
219 | static void |
220 | kif_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 | 232 | struct 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 |
250 | static inline void |
251 | krt_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 | ||
257 | static inline void | |
1123e707 | 258 | krt_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 | 270 | static 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 |
280 | static inline u32 |
281 | krt_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 |
287 | static inline int |
288 | krt_same_key(rte *a, rte *b) | |
289 | { | |
e42eedb9 | 290 | return (krt_metric(a) == krt_metric(b)); |
c9df01d3 OZ |
291 | } |
292 | ||
293 | static inline int | |
294 | krt_uptodate(rte *a, rte *b) | |
295 | { | |
e42eedb9 | 296 | return (a->attrs == b->attrs); |
c10421d3 MM |
297 | } |
298 | ||
299 | static void | |
300 | krt_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 | ||
308 | static void | |
309 | krt_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 |
315 | static void |
316 | krt_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 |
353 | static void |
354 | krt_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); | |
362 | again: | |
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 | ||
427 | static void | |
428 | krt_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 | ||
507 | static void | |
508 | krt_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 | ||
521 | static void | |
522 | krt_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 | 538 | static inline int |
90a9c97e | 539 | krt_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 |
544 | static void |
545 | krt_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 | 561 | static struct rte * |
13c0be19 | 562 | krt_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 | ||
589 | accept: | |
590 | if (rt != net->routes) | |
591 | *rt_free = rt; | |
592 | return rt; | |
593 | ||
594 | reject: | |
595 | if (rt != net->routes) | |
596 | rte_free(rt); | |
597 | return NULL; | |
598 | } | |
599 | ||
2d140452 | 600 | static int |
c9df01d3 | 601 | krt_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 | ||
619 | void | |
e42eedb9 | 620 | krt_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 | ||
675 | seen: | |
676 | krt_trace_in(p, e, "seen"); | |
677 | goto done; | |
678 | ||
679 | aseen: | |
680 | krt_trace_in(p, e, "already seen"); | |
681 | goto done; | |
682 | ||
683 | ignore: | |
684 | krt_trace_in(p, e, "ignored"); | |
685 | goto done; | |
686 | ||
687 | update: | |
688 | krt_trace_in(p, new, "updating"); | |
689 | krt_replace_rte(p, n, new, e); | |
690 | goto done; | |
691 | ||
692 | delete: | |
693 | krt_trace_in(p, e, "deleting"); | |
694 | krt_replace_rte(p, n, NULL, e); | |
695 | goto done; | |
696 | ||
697 | done: | |
698 | rte_free(e); | |
699 | ||
700 | if (rt_free) | |
701 | rte_free(rt_free); | |
702 | ||
703 | lp_flush(krt_filter_lp); | |
704 | } | |
705 | ||
706 | static void | |
707 | krt_init_scan(struct krt_proto *p) | |
708 | { | |
709 | bmap_reset(&p->seen_map, 1024); | |
2d140452 MM |
710 | } |
711 | ||
712 | static void | |
713 | krt_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 | 748 | void |
e42eedb9 | 749 | krt_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 | ||
788 | static timer *krt_scan_timer; | |
789 | static int krt_scan_count; | |
790 | ||
2d140452 | 791 | static void |
6578a604 | 792 | krt_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 | ||
812 | static void | |
813 | krt_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 | ||
823 | static void | |
3e236955 | 824 | krt_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 |
835 | static void |
836 | krt_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 | |
843 | static void | |
844 | krt_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 |
856 | static void |
857 | krt_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 | ||
863 | static void | |
864 | krt_scan_timer_stop(struct krt_proto *p) | |
865 | { | |
a6f79ca5 | 866 | tm_stop(p->scan_timer); |
c6964c30 OZ |
867 | } |
868 | ||
0c791f87 | 869 | static void |
252c7e4d | 870 | krt_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 | 884 | static int |
d5a32563 | 885 | krt_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 | |
897 | static void | |
4bdf1881 | 898 | krt_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 |
922 | static void |
923 | krt_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 |
939 | static void |
940 | krt_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 | 953 | static void |
f4a60a9b | 954 | krt_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 | 967 | struct krt_config *krt_cf; |
7e5f5ffd | 968 | |
f4a60a9b OZ |
969 | static void |
970 | krt_preconfig(struct protocol *P UNUSED, struct config *c) | |
971 | { | |
972 | krt_cf = NULL; | |
973 | krt_sys_preconfig(c); | |
974 | } | |
975 | ||
976 | static void | |
977 | krt_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 | 1008 | static struct proto * |
f4a60a9b | 1009 | krt_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 |
1026 | static int |
1027 | krt_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 | 1064 | static int |
2d140452 MM |
1065 | krt_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 | 1088 | static int |
f4a60a9b | 1089 | krt_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 |
1104 | struct proto_config * |
1105 | krt_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 |
1119 | static void |
1120 | krt_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 | |
1129 | static int | |
258be565 | 1130 | krt_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 | 1160 | struct 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 | }; |