]>
Commit | Line | Data |
---|---|---|
a1bf6440 MM |
1 | /* |
2 | * BIRD -- Static Route Generator | |
3 | * | |
d272fe22 | 4 | * (c) 1998--2000 Martin Mares <mj@ucw.cz> |
a1bf6440 MM |
5 | * |
6 | * Can be freely distributed and used under the terms of the GNU GPL. | |
7 | */ | |
8 | ||
3b31c538 MM |
9 | /** |
10 | * DOC: Static | |
11 | * | |
7126cadf OZ |
12 | * The Static protocol is implemented in a straightforward way. It keeps a list |
13 | * of static routes. Routes of dest RTD_UNICAST have associated sticky node in | |
14 | * the neighbor cache to be notified about gaining or losing the neighbor and | |
15 | * about interface-related events (e.g. link down). They may also have a BFD | |
16 | * request if associated with a BFD session. When a route is notified, | |
17 | * static_decide() is used to see whether the route activeness is changed. In | |
18 | * such case, the route is marked as dirty and scheduled to be announced or | |
19 | * withdrawn, which is done asynchronously from event hook. Routes of other | |
20 | * types (e.g. black holes) are announced all the time. | |
3b31c538 | 21 | * |
7126cadf OZ |
22 | * Multipath routes are a bit tricky. To represent additional next hops, dummy |
23 | * static_route nodes are used, which are chained using @mp_next field and link | |
24 | * to the master node by @mp_head field. Each next hop has a separate neighbor | |
25 | * entry and an activeness state, but the master node is used for most purposes. | |
26 | * Note that most functions DO NOT accept dummy nodes as arguments. | |
9852f810 | 27 | * |
3b31c538 MM |
28 | * The only other thing worth mentioning is that when asked for reconfiguration, |
29 | * Static not only compares the two configurations, but it also calculates | |
7126cadf OZ |
30 | * difference between the lists of static routes and it just inserts the newly |
31 | * added routes, removes the obsolete ones and reannounces changed ones. | |
3b31c538 MM |
32 | */ |
33 | ||
6b9fa320 | 34 | #undef LOCAL_DEBUG |
a1bf6440 | 35 | |
7126cadf OZ |
36 | #include <stdlib.h> |
37 | ||
a1bf6440 MM |
38 | #include "nest/bird.h" |
39 | #include "nest/iface.h" | |
40 | #include "nest/protocol.h" | |
41 | #include "nest/route.h" | |
feed8226 | 42 | #include "nest/cli.h" |
a1bf6440 | 43 | #include "conf/conf.h" |
1321e12a | 44 | #include "filter/filter.h" |
feed8226 | 45 | #include "lib/string.h" |
9852f810 | 46 | #include "lib/alloca.h" |
a1bf6440 MM |
47 | |
48 | #include "static.h" | |
49 | ||
1321e12a OZ |
50 | static linpool *static_lp; |
51 | ||
3347aaaf OZ |
52 | static inline struct rte_src * static_get_source(struct static_proto *p, uint i) |
53 | { return i ? rt_get_source(&p->p, i) : p->p.main_source; } | |
54 | ||
f6bd2066 | 55 | static void |
7126cadf | 56 | static_announce_rte(struct static_proto *p, struct static_route *r) |
f6bd2066 | 57 | { |
7126cadf | 58 | rta *a = allocz(RTA_MAX_SIZE); |
3347aaaf | 59 | a->src = static_get_source(p, r->index); |
7126cadf OZ |
60 | a->source = RTS_STATIC; |
61 | a->scope = SCOPE_UNIVERSE; | |
62 | a->dest = r->dest; | |
f2010f9c | 63 | |
4e276a89 | 64 | if (r->dest == RTD_UNICAST) |
7126cadf OZ |
65 | { |
66 | struct static_route *r2; | |
67 | struct nexthop *nhs = NULL; | |
68 | ||
69 | for (r2 = r; r2; r2 = r2->mp_next) | |
9852f810 | 70 | { |
7126cadf OZ |
71 | if (!r2->active) |
72 | continue; | |
4e276a89 | 73 | |
7126cadf OZ |
74 | struct nexthop *nh = allocz(NEXTHOP_MAX_SIZE); |
75 | nh->gw = r2->via; | |
76 | nh->iface = r2->neigh->iface; | |
a1f5e514 | 77 | nh->flags = r2->onlink ? RNF_ONLINK : 0; |
7126cadf | 78 | nh->weight = r2->weight; |
3c744164 JMM |
79 | if (r2->mls) |
80 | { | |
81 | nh->labels = r2->mls->len; | |
82 | memcpy(nh->label, r2->mls->stack, r2->mls->len * sizeof(u32)); | |
83 | } | |
9852f810 | 84 | |
7126cadf OZ |
85 | nexthop_insert(&nhs, nh); |
86 | } | |
f2010f9c | 87 | |
7126cadf OZ |
88 | if (!nhs) |
89 | goto withdraw; | |
62e64905 | 90 | |
7126cadf OZ |
91 | nexthop_link(a, nhs); |
92 | } | |
62e64905 | 93 | |
4116db18 | 94 | if (r->dest == RTDX_RECURSIVE) |
ffb38dfb OZ |
95 | { |
96 | rtable *tab = ipa_is_ip4(r->via) ? p->igp_table_ip4 : p->igp_table_ip6; | |
97 | rta_set_recursive_next_hop(p->p.main_channel->table, a, tab, r->via, IPA_NONE, r->mls); | |
98 | } | |
4116db18 | 99 | |
7126cadf OZ |
100 | /* Already announced */ |
101 | if (r->state == SRS_CLEAN) | |
102 | return; | |
1321e12a | 103 | |
7126cadf OZ |
104 | /* We skip rta_lookup() here */ |
105 | rte *e = rte_get_temp(a); | |
f6bd2066 | 106 | e->pflags = 0; |
1321e12a OZ |
107 | |
108 | if (r->cmds) | |
ea3c6c1a OZ |
109 | { |
110 | /* Create a temporary table node */ | |
111 | e->net = alloca(sizeof(net) + r->net->length); | |
112 | memset(e->net, 0, sizeof(net) + r->net->length); | |
113 | net_copy(e->net->n.addr, r->net); | |
114 | ||
115 | /* Evaluate the filter */ | |
1321e12a OZ |
116 | f_eval_rte(r->cmds, &e, static_lp); |
117 | ||
ea3c6c1a OZ |
118 | /* Remove the temporary node */ |
119 | e->net = NULL; | |
120 | } | |
121 | ||
3347aaaf | 122 | rte_update2(p->p.main_channel, r->net, e, a->src); |
7126cadf | 123 | r->state = SRS_CLEAN; |
1321e12a OZ |
124 | |
125 | if (r->cmds) | |
126 | lp_flush(static_lp); | |
7126cadf OZ |
127 | |
128 | return; | |
129 | ||
130 | withdraw: | |
131 | if (r->state == SRS_DOWN) | |
132 | return; | |
133 | ||
3347aaaf | 134 | rte_update2(p->p.main_channel, r->net, NULL, a->src); |
7126cadf OZ |
135 | r->state = SRS_DOWN; |
136 | } | |
137 | ||
138 | static void | |
139 | static_mark_rte(struct static_proto *p, struct static_route *r) | |
140 | { | |
141 | if (r->state == SRS_DIRTY) | |
142 | return; | |
143 | ||
144 | r->state = SRS_DIRTY; | |
145 | BUFFER_PUSH(p->marked) = r; | |
146 | ||
147 | if (!ev_active(p->event)) | |
148 | ev_schedule(p->event); | |
149 | } | |
150 | ||
9cf3d533 OZ |
151 | static void |
152 | static_mark_all(struct static_proto *p) | |
153 | { | |
154 | struct static_config *cf = (void *) p->p.cf; | |
155 | struct static_route *r; | |
156 | ||
157 | /* We want to reload all routes, mark them as dirty */ | |
158 | ||
159 | WALK_LIST(r, cf->routes) | |
160 | if (r->state == SRS_CLEAN) | |
161 | r->state = SRS_DIRTY; | |
162 | ||
163 | p->marked_all = 1; | |
164 | BUFFER_FLUSH(p->marked); | |
165 | ||
166 | if (!ev_active(p->event)) | |
167 | ev_schedule(p->event); | |
168 | } | |
169 | ||
170 | ||
7126cadf OZ |
171 | static void |
172 | static_announce_marked(void *P) | |
173 | { | |
174 | struct static_proto *p = P; | |
9cf3d533 OZ |
175 | struct static_config *cf = (void *) p->p.cf; |
176 | struct static_route *r; | |
7126cadf | 177 | |
9cf3d533 OZ |
178 | if (p->marked_all) |
179 | { | |
180 | WALK_LIST(r, cf->routes) | |
181 | if (r->state == SRS_DIRTY) | |
182 | static_announce_rte(p, r); | |
7126cadf | 183 | |
9cf3d533 OZ |
184 | p->marked_all = 0; |
185 | } | |
186 | else | |
187 | { | |
188 | BUFFER_WALK(p->marked, r) | |
189 | static_announce_rte(p, r); | |
190 | ||
191 | BUFFER_FLUSH(p->marked); | |
192 | } | |
f6bd2066 MM |
193 | } |
194 | ||
538264cf OZ |
195 | static void |
196 | static_bfd_notify(struct bfd_request *req); | |
197 | ||
198 | static void | |
7126cadf | 199 | static_update_bfd(struct static_proto *p, struct static_route *r) |
538264cf | 200 | { |
7126cadf OZ |
201 | /* The @r is a RTD_UNICAST next hop, may be a dummy node */ |
202 | ||
538264cf OZ |
203 | struct neighbor *nb = r->neigh; |
204 | int bfd_up = (nb->scope > 0) && r->use_bfd; | |
205 | ||
206 | if (bfd_up && !r->bfd_req) | |
207 | { | |
208 | // ip_addr local = ipa_nonzero(r->local) ? r->local : nb->ifa->ip; | |
cf7ff995 OZ |
209 | r->bfd_req = bfd_request_session(p->p.pool, r->via, nb->ifa->ip, |
210 | nb->iface, p->p.vrf, | |
9d3fc306 | 211 | static_bfd_notify, r, NULL); |
538264cf OZ |
212 | } |
213 | ||
214 | if (!bfd_up && r->bfd_req) | |
215 | { | |
216 | rfree(r->bfd_req); | |
217 | r->bfd_req = NULL; | |
218 | } | |
219 | } | |
220 | ||
9852f810 | 221 | static int |
7126cadf | 222 | static_decide(struct static_proto *p, struct static_route *r) |
9852f810 | 223 | { |
7126cadf | 224 | /* The @r is a RTD_UNICAST next hop, may be a dummy node */ |
9852f810 | 225 | |
7126cadf OZ |
226 | struct static_config *cf = (void *) p->p.cf; |
227 | uint old_active = r->active; | |
f2010f9c | 228 | |
69a8259c | 229 | if (r->neigh->scope < 0) |
7126cadf | 230 | goto fail; |
9852f810 | 231 | |
69a8259c | 232 | if (cf->check_link && !(r->neigh->iface->flags & IF_LINK_UP)) |
7126cadf | 233 | goto fail; |
9852f810 | 234 | |
7126cadf OZ |
235 | if (r->bfd_req && (r->bfd_req->state != BFD_STATE_UP)) |
236 | goto fail; | |
538264cf | 237 | |
7126cadf OZ |
238 | r->active = 1; |
239 | return !old_active; | |
9852f810 | 240 | |
7126cadf OZ |
241 | fail: |
242 | r->active = 0; | |
243 | return old_active; | |
244 | } | |
9852f810 | 245 | |
295ae16d | 246 | static void |
7126cadf | 247 | static_add_rte(struct static_proto *p, struct static_route *r) |
295ae16d | 248 | { |
7126cadf OZ |
249 | if (r->dest == RTD_UNICAST) |
250 | { | |
251 | struct static_route *r2; | |
252 | struct neighbor *n; | |
f2010f9c | 253 | |
7126cadf | 254 | for (r2 = r; r2; r2 = r2->mp_next) |
295ae16d | 255 | { |
586c1800 OZ |
256 | n = neigh_find(&p->p, r2->via, r2->iface, NEF_STICKY | |
257 | (r2->onlink ? NEF_ONLINK : 0) | | |
258 | (ipa_zero(r2->via) ? NEF_IFACE : 0)); | |
7126cadf OZ |
259 | |
260 | if (!n) | |
9852f810 | 261 | { |
7126cadf OZ |
262 | log(L_WARN "Invalid next hop %I of static route %N", r2->via, r2->net); |
263 | continue; | |
9852f810 OZ |
264 | } |
265 | ||
7126cadf OZ |
266 | r2->neigh = n; |
267 | r2->chain = n->data; | |
268 | n->data = r2; | |
269 | ||
270 | static_update_bfd(p, r2); | |
271 | static_decide(p, r2); | |
295ae16d | 272 | } |
7126cadf OZ |
273 | } |
274 | ||
275 | static_announce_rte(p, r); | |
295ae16d MM |
276 | } |
277 | ||
538264cf | 278 | static void |
7126cadf | 279 | static_reset_rte(struct static_proto *p UNUSED, struct static_route *r) |
538264cf OZ |
280 | { |
281 | struct static_route *r2; | |
4116db18 | 282 | |
7126cadf OZ |
283 | for (r2 = r; r2; r2 = r2->mp_next) |
284 | { | |
285 | r2->neigh = NULL; | |
286 | r2->chain = NULL; | |
d360f129 | 287 | |
7126cadf OZ |
288 | r2->state = 0; |
289 | r2->active = 0; | |
f2010f9c | 290 | |
7126cadf OZ |
291 | rfree(r2->bfd_req); |
292 | r2->bfd_req = NULL; | |
293 | } | |
294 | } | |
f2010f9c | 295 | |
7126cadf OZ |
296 | static void |
297 | static_remove_rte(struct static_proto *p, struct static_route *r) | |
298 | { | |
299 | if (r->state) | |
3347aaaf | 300 | rte_update2(p->p.main_channel, r->net, NULL, static_get_source(p, r->index)); |
f2010f9c | 301 | |
7126cadf | 302 | static_reset_rte(p, r); |
a1bf6440 MM |
303 | } |
304 | ||
7126cadf OZ |
305 | |
306 | static inline int | |
307 | static_same_dest(struct static_route *x, struct static_route *y) | |
e4bfafa1 | 308 | { |
7126cadf OZ |
309 | if (x->dest != y->dest) |
310 | return 0; | |
e4bfafa1 | 311 | |
7126cadf | 312 | switch (x->dest) |
538264cf | 313 | { |
7126cadf OZ |
314 | case RTD_UNICAST: |
315 | for (; x && y; x = x->mp_next, y = y->mp_next) | |
316 | { | |
317 | if (!ipa_equal(x->via, y->via) || | |
318 | (x->iface != y->iface) || | |
a1f5e514 | 319 | (x->onlink != y->onlink) || |
7126cadf | 320 | (x->weight != y->weight) || |
a1f5e514 | 321 | (x->use_bfd != y->use_bfd) || |
3c744164 JMM |
322 | (!x->mls != !y->mls) || |
323 | ((x->mls) && (y->mls) && (x->mls->len != y->mls->len))) | |
7126cadf OZ |
324 | return 0; |
325 | ||
3c744164 JMM |
326 | if (!x->mls) |
327 | continue; | |
328 | ||
329 | for (uint i = 0; i < x->mls->len; i++) | |
330 | if (x->mls->stack[i] != y->mls->stack[i]) | |
7126cadf OZ |
331 | return 0; |
332 | } | |
333 | return !x && !y; | |
53434e44 | 334 | |
7126cadf | 335 | case RTDX_RECURSIVE: |
3c744164 JMM |
336 | if (!ipa_equal(x->via, y->via) || |
337 | (!x->mls != !y->mls) || | |
338 | ((x->mls) && (y->mls) && (x->mls->len != y->mls->len))) | |
339 | return 0; | |
340 | ||
341 | if (!x->mls) | |
342 | return 1; | |
343 | ||
344 | for (uint i = 0; i < x->mls->len; i++) | |
345 | if (x->mls->stack[i] != y->mls->stack[i]) | |
346 | return 0; | |
347 | ||
348 | return 1; | |
f4a60a9b | 349 | |
7126cadf OZ |
350 | default: |
351 | return 1; | |
352 | } | |
e4bfafa1 MM |
353 | } |
354 | ||
7126cadf OZ |
355 | static inline int |
356 | static_same_rte(struct static_route *or, struct static_route *nr) | |
4116db18 | 357 | { |
7126cadf | 358 | /* Note that i_same() requires arguments in (new, old) order */ |
4c553c5a | 359 | return static_same_dest(or, nr) && f_same(nr->cmds, or->cmds); |
4116db18 OZ |
360 | } |
361 | ||
538264cf | 362 | static void |
7126cadf | 363 | static_reconfigure_rte(struct static_proto *p, struct static_route *or, struct static_route *nr) |
538264cf | 364 | { |
7126cadf OZ |
365 | if ((or->state == SRS_CLEAN) && !static_same_rte(or, nr)) |
366 | nr->state = SRS_DIRTY; | |
367 | else | |
368 | nr->state = or->state; | |
538264cf | 369 | |
7126cadf OZ |
370 | static_add_rte(p, nr); |
371 | static_reset_rte(p, or); | |
538264cf | 372 | } |
4116db18 | 373 | |
7126cadf | 374 | |
a1bf6440 MM |
375 | static void |
376 | static_neigh_notify(struct neighbor *n) | |
377 | { | |
7126cadf | 378 | struct static_proto *p = (void *) n->proto; |
980297d2 MM |
379 | struct static_route *r; |
380 | ||
d93a43a5 | 381 | DBG("Static: neighbor notify for %I: iface %p\n", n->addr, n->iface); |
7126cadf | 382 | for (r = n->data; r; r = r->chain) |
538264cf OZ |
383 | { |
384 | static_update_bfd(p, r); | |
7126cadf OZ |
385 | |
386 | if (static_decide(p, r)) | |
387 | static_mark_rte(p, r->mp_head); | |
538264cf OZ |
388 | } |
389 | } | |
9852f810 | 390 | |
538264cf OZ |
391 | static void |
392 | static_bfd_notify(struct bfd_request *req) | |
393 | { | |
394 | struct static_route *r = req->data; | |
7126cadf | 395 | struct static_proto *p = (void *) r->neigh->proto; |
9852f810 | 396 | |
538264cf OZ |
397 | // if (req->down) TRACE(D_EVENTS, "BFD session down for nbr %I on %s", XXXX); |
398 | ||
7126cadf OZ |
399 | if (static_decide(p, r)) |
400 | static_mark_rte(p, r->mp_head); | |
a1bf6440 MM |
401 | } |
402 | ||
9cf3d533 OZ |
403 | static void |
404 | static_reload_routes(struct channel *C) | |
405 | { | |
406 | struct static_proto *p = (void *) C->proto; | |
407 | ||
408 | TRACE(D_EVENTS, "Scheduling route reload"); | |
409 | ||
410 | static_mark_all(p); | |
411 | } | |
412 | ||
7126cadf | 413 | static int |
3347aaaf | 414 | static_rte_better(rte *new, rte *old) |
8d9eef17 | 415 | { |
3347aaaf OZ |
416 | u32 n = ea_get_int(new->attrs->eattrs, EA_GEN_IGP_METRIC, IGP_METRIC_UNKNOWN); |
417 | u32 o = ea_get_int(old->attrs->eattrs, EA_GEN_IGP_METRIC, IGP_METRIC_UNKNOWN); | |
418 | return n < o; | |
419 | } | |
420 | ||
421 | static int | |
422 | static_rte_mergable(rte *pri, rte *sec) | |
423 | { | |
424 | u32 a = ea_get_int(pri->attrs->eattrs, EA_GEN_IGP_METRIC, IGP_METRIC_UNKNOWN); | |
425 | u32 b = ea_get_int(sec->attrs->eattrs, EA_GEN_IGP_METRIC, IGP_METRIC_UNKNOWN); | |
426 | return a == b; | |
8d9eef17 OZ |
427 | } |
428 | ||
3347aaaf | 429 | static void static_index_routes(struct static_config *cf); |
a1bf6440 | 430 | |
f4a60a9b OZ |
431 | static void |
432 | static_postconfig(struct proto_config *CF) | |
433 | { | |
434 | struct static_config *cf = (void *) CF; | |
435 | struct static_route *r; | |
436 | ||
f761be6b | 437 | if (! proto_cf_main_channel(CF)) |
f4a60a9b OZ |
438 | cf_error("Channel not specified"); |
439 | ||
ffb38dfb OZ |
440 | struct channel_config *cc = proto_cf_main_channel(CF); |
441 | ||
442 | if (!cf->igp_table_ip4) | |
443 | cf->igp_table_ip4 = (cc->table->addr_type == NET_IP4) ? | |
444 | cc->table : cf->c.global->def_tables[NET_IP4]; | |
445 | ||
446 | if (!cf->igp_table_ip6) | |
447 | cf->igp_table_ip6 = (cc->table->addr_type == NET_IP6) ? | |
448 | cc->table : cf->c.global->def_tables[NET_IP6]; | |
449 | ||
7126cadf | 450 | WALK_LIST(r, cf->routes) |
f2010f9c JMM |
451 | if (r->net && (r->net->type != CF->net_type)) |
452 | cf_error("Route %N incompatible with channel type", r->net); | |
3347aaaf OZ |
453 | |
454 | static_index_routes(cf); | |
f4a60a9b OZ |
455 | } |
456 | ||
e9e3dc26 | 457 | static struct proto * |
f4a60a9b | 458 | static_init(struct proto_config *CF) |
a1bf6440 | 459 | { |
f4a60a9b | 460 | struct proto *P = proto_new(CF); |
ffb38dfb OZ |
461 | struct static_proto *p = (void *) P; |
462 | struct static_config *cf = (void *) CF; | |
a1bf6440 | 463 | |
f4a60a9b | 464 | P->main_channel = proto_add_channel(P, proto_cf_main_channel(CF)); |
094d2bdb | 465 | |
f4a60a9b | 466 | P->neigh_notify = static_neigh_notify; |
9cf3d533 | 467 | P->reload_routes = static_reload_routes; |
3347aaaf | 468 | P->rte_better = static_rte_better; |
f4a60a9b OZ |
469 | P->rte_mergable = static_rte_mergable; |
470 | ||
ffb38dfb OZ |
471 | if (cf->igp_table_ip4) |
472 | p->igp_table_ip4 = cf->igp_table_ip4->table; | |
473 | ||
474 | if (cf->igp_table_ip6) | |
475 | p->igp_table_ip6 = cf->igp_table_ip6->table; | |
476 | ||
f4a60a9b | 477 | return P; |
a1bf6440 MM |
478 | } |
479 | ||
7126cadf OZ |
480 | static int |
481 | static_start(struct proto *P) | |
c1cefd7b | 482 | { |
7126cadf OZ |
483 | struct static_proto *p = (void *) P; |
484 | struct static_config *cf = (void *) P->cf; | |
485 | struct static_route *r; | |
9852f810 | 486 | |
7126cadf | 487 | if (!static_lp) |
05d47bd5 | 488 | static_lp = lp_new(&root_pool, LP_GOOD_SIZE(1024)); |
9852f810 | 489 | |
ffb38dfb OZ |
490 | if (p->igp_table_ip4) |
491 | rt_lock_table(p->igp_table_ip4); | |
492 | ||
493 | if (p->igp_table_ip6) | |
494 | rt_lock_table(p->igp_table_ip6); | |
4116db18 | 495 | |
961671c0 | 496 | p->event = ev_new_init(p->p.pool, static_announce_marked, p); |
295ae16d | 497 | |
7126cadf | 498 | BUFFER_INIT(p->marked, p->p.pool, 4); |
1321e12a | 499 | |
7126cadf OZ |
500 | /* We have to go UP before routes could be installed */ |
501 | proto_notify_state(P, PS_UP); | |
1321e12a | 502 | |
7126cadf OZ |
503 | WALK_LIST(r, cf->routes) |
504 | static_add_rte(p, r); | |
295ae16d | 505 | |
7126cadf OZ |
506 | return PS_UP; |
507 | } | |
f2010f9c | 508 | |
7126cadf OZ |
509 | static int |
510 | static_shutdown(struct proto *P) | |
511 | { | |
512 | struct static_proto *p = (void *) P; | |
513 | struct static_config *cf = (void *) P->cf; | |
514 | struct static_route *r; | |
c1cefd7b | 515 | |
7126cadf OZ |
516 | /* Just reset the flag, the routes will be flushed by the nest */ |
517 | WALK_LIST(r, cf->routes) | |
518 | static_reset_rte(p, r); | |
d40c2659 | 519 | |
7126cadf OZ |
520 | return PS_DOWN; |
521 | } | |
f2010f9c | 522 | |
7126cadf OZ |
523 | static void |
524 | static_cleanup(struct proto *P) | |
525 | { | |
ffb38dfb OZ |
526 | struct static_proto *p = (void *) P; |
527 | ||
528 | if (p->igp_table_ip4) | |
529 | rt_unlock_table(p->igp_table_ip4); | |
d40c2659 | 530 | |
ffb38dfb OZ |
531 | if (p->igp_table_ip6) |
532 | rt_unlock_table(p->igp_table_ip6); | |
7126cadf | 533 | } |
d40c2659 | 534 | |
7126cadf OZ |
535 | static void |
536 | static_dump_rte(struct static_route *r) | |
537 | { | |
3347aaaf | 538 | debug("%-1N (%u): ", r->net, r->index); |
7126cadf OZ |
539 | if (r->dest == RTD_UNICAST) |
540 | if (r->iface && ipa_zero(r->via)) | |
541 | debug("dev %s\n", r->iface->name); | |
542 | else | |
543 | debug("via %I%J\n", r->via, r->iface); | |
544 | else | |
545 | debug("rtd %d\n", r->dest); | |
546 | } | |
d40c2659 | 547 | |
7126cadf OZ |
548 | static void |
549 | static_dump(struct proto *P) | |
550 | { | |
551 | struct static_config *c = (void *) P->cf; | |
552 | struct static_route *r; | |
f2010f9c | 553 | |
7126cadf OZ |
554 | debug("Static routes:\n"); |
555 | WALK_LIST(r, c->routes) | |
556 | static_dump_rte(r); | |
295ae16d MM |
557 | } |
558 | ||
ffb38dfb | 559 | #define IGP_TABLE(cf, sym) ((cf)->igp_table_##sym ? (cf)->igp_table_##sym ->table : NULL ) |
4116db18 | 560 | |
3347aaaf OZ |
561 | static inline int srt_equal(const struct static_route *a, const struct static_route *b) |
562 | { return net_equal(a->net, b->net) && (a->index == b->index); } | |
563 | ||
564 | static inline int srt_compare(const struct static_route *a, const struct static_route *b) | |
565 | { return net_compare(a->net, b->net) ?: uint_cmp(a->index, b->index); } | |
566 | ||
567 | static inline int srt_compare_qsort(const void *A, const void *B) | |
7126cadf | 568 | { |
3347aaaf OZ |
569 | return srt_compare(*(const struct static_route * const *)A, |
570 | *(const struct static_route * const *)B); | |
571 | } | |
572 | ||
573 | static void | |
574 | static_index_routes(struct static_config *cf) | |
575 | { | |
576 | struct static_route *rt, **buf; | |
577 | uint num, i, v; | |
578 | ||
579 | num = list_length(&cf->routes); | |
580 | buf = xmalloc(num * sizeof(void *)); | |
581 | ||
582 | /* Initialize with sequential indexes to ensure stable sorting */ | |
583 | i = 0; | |
584 | WALK_LIST(rt, cf->routes) | |
585 | { | |
586 | buf[i] = rt; | |
587 | rt->index = i++; | |
588 | } | |
589 | ||
590 | qsort(buf, num, sizeof(struct static_route *), srt_compare_qsort); | |
591 | ||
592 | /* Compute proper indexes - sequential for routes with same network */ | |
593 | for (i = 0, v = 0, rt = NULL; i < num; i++, v++) | |
594 | { | |
595 | if (rt && !net_equal(buf[i]->net, rt->net)) | |
596 | v = 0; | |
597 | ||
598 | rt = buf[i]; | |
599 | rt->index = v; | |
600 | } | |
601 | ||
602 | xfree(buf); | |
7126cadf OZ |
603 | } |
604 | ||
d272fe22 | 605 | static int |
7126cadf | 606 | static_reconfigure(struct proto *P, struct proto_config *CF) |
d272fe22 | 607 | { |
7126cadf OZ |
608 | struct static_proto *p = (void *) P; |
609 | struct static_config *o = (void *) P->cf; | |
f4a60a9b | 610 | struct static_config *n = (void *) CF; |
7126cadf | 611 | struct static_route *r, *r2, *or, *nr; |
295ae16d | 612 | |
ffb38dfb OZ |
613 | /* Check change in IGP tables */ |
614 | if ((IGP_TABLE(o, ip4) != IGP_TABLE(n, ip4)) || | |
615 | (IGP_TABLE(o, ip6) != IGP_TABLE(n, ip6))) | |
4116db18 OZ |
616 | return 0; |
617 | ||
7126cadf | 618 | if (!proto_configure_channel(P, &P->main_channel, proto_cf_main_channel(CF))) |
f4a60a9b OZ |
619 | return 0; |
620 | ||
7126cadf OZ |
621 | p->p.cf = CF; |
622 | ||
623 | /* Reset route lists in neighbor entries */ | |
624 | WALK_LIST(r, o->routes) | |
625 | for (r2 = r; r2; r2 = r2->mp_next) | |
626 | if (r2->neigh) | |
627 | r2->neigh->data = NULL; | |
628 | ||
629 | /* Reconfigure initial matching sequence */ | |
630 | for (or = HEAD(o->routes), nr = HEAD(n->routes); | |
3347aaaf | 631 | NODE_VALID(or) && NODE_VALID(nr) && srt_equal(or, nr); |
7126cadf OZ |
632 | or = NODE_NEXT(or), nr = NODE_NEXT(nr)) |
633 | static_reconfigure_rte(p, or, nr); | |
634 | ||
635 | if (!NODE_VALID(or) && !NODE_VALID(nr)) | |
636 | return 1; | |
637 | ||
638 | /* Reconfigure remaining routes, sort them to find matching pairs */ | |
639 | struct static_route *or2, *nr2, **orbuf, **nrbuf; | |
640 | uint ornum = 0, nrnum = 0, orpos = 0, nrpos = 0, i; | |
641 | ||
642 | for (or2 = or; NODE_VALID(or2); or2 = NODE_NEXT(or2)) | |
643 | ornum++; | |
644 | ||
645 | for (nr2 = nr; NODE_VALID(nr2); nr2 = NODE_NEXT(nr2)) | |
646 | nrnum++; | |
647 | ||
648 | orbuf = xmalloc(ornum * sizeof(void *)); | |
649 | nrbuf = xmalloc(nrnum * sizeof(void *)); | |
f2010f9c | 650 | |
7126cadf OZ |
651 | for (i = 0, or2 = or; i < ornum; i++, or2 = NODE_NEXT(or2)) |
652 | orbuf[i] = or2; | |
653 | ||
654 | for (i = 0, nr2 = nr; i < nrnum; i++, nr2 = NODE_NEXT(nr2)) | |
655 | nrbuf[i] = nr2; | |
656 | ||
3347aaaf OZ |
657 | qsort(orbuf, ornum, sizeof(struct static_route *), srt_compare_qsort); |
658 | qsort(nrbuf, nrnum, sizeof(struct static_route *), srt_compare_qsort); | |
7126cadf OZ |
659 | |
660 | while ((orpos < ornum) && (nrpos < nrnum)) | |
f2010f9c | 661 | { |
3347aaaf | 662 | int x = srt_compare(orbuf[orpos], nrbuf[nrpos]); |
7126cadf OZ |
663 | if (x < 0) |
664 | static_remove_rte(p, orbuf[orpos++]); | |
665 | else if (x > 0) | |
666 | static_add_rte(p, nrbuf[nrpos++]); | |
667 | else | |
668 | static_reconfigure_rte(p, orbuf[orpos++], nrbuf[nrpos++]); | |
f2010f9c | 669 | } |
295ae16d | 670 | |
7126cadf OZ |
671 | while (orpos < ornum) |
672 | static_remove_rte(p, orbuf[orpos++]); | |
538264cf | 673 | |
7126cadf OZ |
674 | while (nrpos < nrnum) |
675 | static_add_rte(p, nrbuf[nrpos++]); | |
d272fe22 | 676 | |
7126cadf OZ |
677 | xfree(orbuf); |
678 | xfree(nrbuf); | |
a7f23f58 | 679 | |
9cf3d533 OZ |
680 | /* All dirty routes were announced anyways */ |
681 | BUFFER_FLUSH(p->marked); | |
682 | p->marked_all = 0; | |
683 | ||
7126cadf | 684 | return 1; |
a7f23f58 OZ |
685 | } |
686 | ||
687 | static void | |
688 | static_copy_config(struct proto_config *dest, struct proto_config *src) | |
689 | { | |
690 | struct static_config *d = (struct static_config *) dest; | |
691 | struct static_config *s = (struct static_config *) src; | |
692 | ||
7126cadf | 693 | struct static_route *srt, *snh; |
a7f23f58 | 694 | |
7126cadf OZ |
695 | /* Copy route list */ |
696 | init_list(&d->routes); | |
697 | WALK_LIST(srt, s->routes) | |
698 | { | |
699 | struct static_route *drt = NULL, *dnh = NULL, **dnp = &drt; | |
feed8226 | 700 | |
7126cadf | 701 | for (snh = srt; snh; snh = snh->mp_next) |
feed8226 | 702 | { |
7126cadf OZ |
703 | dnh = cfg_alloc(sizeof(struct static_route)); |
704 | memcpy(dnh, snh, sizeof(struct static_route)); | |
1678bc07 | 705 | memset(&dnh->n, 0, sizeof(node)); |
7126cadf OZ |
706 | |
707 | if (!drt) | |
708 | add_tail(&d->routes, &(dnh->n)); | |
709 | ||
710 | *dnp = dnh; | |
711 | dnp = &(dnh->mp_next); | |
712 | ||
713 | if (snh->mp_head) | |
714 | dnh->mp_head = drt; | |
feed8226 | 715 | } |
7126cadf | 716 | } |
4e276a89 | 717 | } |
9852f810 | 718 | |
3347aaaf OZ |
719 | static void |
720 | static_get_route_info(rte *rte, byte *buf) | |
721 | { | |
722 | eattr *a = ea_find(rte->attrs->eattrs, EA_GEN_IGP_METRIC); | |
723 | if (a) | |
724 | buf += bsprintf(buf, " (%d/%u)", rte->pref, a->u.data); | |
725 | else | |
726 | buf += bsprintf(buf, " (%d)", rte->pref); | |
727 | } | |
728 | ||
4e276a89 JMM |
729 | static void |
730 | static_show_rt(struct static_route *r) | |
731 | { | |
7126cadf OZ |
732 | switch (r->dest) |
733 | { | |
734 | case RTD_UNICAST: | |
4e276a89 | 735 | { |
4e276a89 | 736 | struct static_route *r2; |
7126cadf OZ |
737 | |
738 | cli_msg(-1009, "%N", r->net); | |
4e276a89 | 739 | for (r2 = r; r2; r2 = r2->mp_next) |
f2010f9c | 740 | { |
7126cadf | 741 | if (r2->iface && ipa_zero(r2->via)) |
a1f5e514 OZ |
742 | cli_msg(-1009, "\tdev %s%s", r2->iface->name, |
743 | r2->active ? "" : " (dormant)"); | |
7126cadf | 744 | else |
a1f5e514 OZ |
745 | cli_msg(-1009, "\tvia %I%J%s%s%s", r2->via, r2->iface, |
746 | r2->onlink ? " onlink" : "", | |
747 | r2->bfd_req ? " (bfd)" : "", | |
748 | r2->active ? "" : " (dormant)"); | |
f2010f9c | 749 | } |
7126cadf OZ |
750 | break; |
751 | } | |
752 | ||
753 | case RTD_NONE: | |
754 | case RTD_BLACKHOLE: | |
755 | case RTD_UNREACHABLE: | |
756 | case RTD_PROHIBIT: | |
757 | cli_msg(-1009, "%N\t%s", r->net, rta_dest_names[r->dest]); | |
758 | break; | |
759 | ||
760 | case RTDX_RECURSIVE: | |
761 | cli_msg(-1009, "%N\trecursive %I", r->net, r->via); | |
762 | break; | |
4e276a89 | 763 | } |
feed8226 MM |
764 | } |
765 | ||
766 | void | |
767 | static_show(struct proto *P) | |
768 | { | |
769 | struct static_config *c = (void *) P->cf; | |
770 | struct static_route *r; | |
771 | ||
7126cadf | 772 | WALK_LIST(r, c->routes) |
f2010f9c | 773 | static_show_rt(r); |
feed8226 | 774 | } |
7126cadf OZ |
775 | |
776 | ||
777 | struct protocol proto_static = { | |
778 | .name = "Static", | |
779 | .template = "static%d", | |
ee7e2ffd | 780 | .class = PROTOCOL_STATIC, |
7126cadf OZ |
781 | .preference = DEF_PREF_STATIC, |
782 | .channel_mask = NB_ANY, | |
783 | .proto_size = sizeof(struct static_proto), | |
784 | .config_size = sizeof(struct static_config), | |
785 | .postconfig = static_postconfig, | |
786 | .init = static_init, | |
787 | .dump = static_dump, | |
788 | .start = static_start, | |
789 | .shutdown = static_shutdown, | |
790 | .cleanup = static_cleanup, | |
791 | .reconfigure = static_reconfigure, | |
3347aaaf OZ |
792 | .copy_config = static_copy_config, |
793 | .get_route_info = static_get_route_info, | |
7126cadf | 794 | }; |