]>
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 | ||
151 | static void | |
152 | static_announce_marked(void *P) | |
153 | { | |
154 | struct static_proto *p = P; | |
155 | ||
156 | BUFFER_WALK(p->marked, r) | |
157 | static_announce_rte(P, r); | |
158 | ||
159 | BUFFER_FLUSH(p->marked); | |
f6bd2066 MM |
160 | } |
161 | ||
538264cf OZ |
162 | static void |
163 | static_bfd_notify(struct bfd_request *req); | |
164 | ||
165 | static void | |
7126cadf | 166 | static_update_bfd(struct static_proto *p, struct static_route *r) |
538264cf | 167 | { |
7126cadf OZ |
168 | /* The @r is a RTD_UNICAST next hop, may be a dummy node */ |
169 | ||
538264cf OZ |
170 | struct neighbor *nb = r->neigh; |
171 | int bfd_up = (nb->scope > 0) && r->use_bfd; | |
172 | ||
173 | if (bfd_up && !r->bfd_req) | |
174 | { | |
175 | // ip_addr local = ipa_nonzero(r->local) ? r->local : nb->ifa->ip; | |
cf7ff995 OZ |
176 | r->bfd_req = bfd_request_session(p->p.pool, r->via, nb->ifa->ip, |
177 | nb->iface, p->p.vrf, | |
9d3fc306 | 178 | static_bfd_notify, r, NULL); |
538264cf OZ |
179 | } |
180 | ||
181 | if (!bfd_up && r->bfd_req) | |
182 | { | |
183 | rfree(r->bfd_req); | |
184 | r->bfd_req = NULL; | |
185 | } | |
186 | } | |
187 | ||
9852f810 | 188 | static int |
7126cadf | 189 | static_decide(struct static_proto *p, struct static_route *r) |
9852f810 | 190 | { |
7126cadf | 191 | /* The @r is a RTD_UNICAST next hop, may be a dummy node */ |
9852f810 | 192 | |
7126cadf OZ |
193 | struct static_config *cf = (void *) p->p.cf; |
194 | uint old_active = r->active; | |
f2010f9c | 195 | |
69a8259c | 196 | if (r->neigh->scope < 0) |
7126cadf | 197 | goto fail; |
9852f810 | 198 | |
69a8259c | 199 | if (cf->check_link && !(r->neigh->iface->flags & IF_LINK_UP)) |
7126cadf | 200 | goto fail; |
9852f810 | 201 | |
7126cadf OZ |
202 | if (r->bfd_req && (r->bfd_req->state != BFD_STATE_UP)) |
203 | goto fail; | |
538264cf | 204 | |
7126cadf OZ |
205 | r->active = 1; |
206 | return !old_active; | |
9852f810 | 207 | |
7126cadf OZ |
208 | fail: |
209 | r->active = 0; | |
210 | return old_active; | |
211 | } | |
9852f810 | 212 | |
295ae16d | 213 | static void |
7126cadf | 214 | static_add_rte(struct static_proto *p, struct static_route *r) |
295ae16d | 215 | { |
7126cadf OZ |
216 | if (r->dest == RTD_UNICAST) |
217 | { | |
218 | struct static_route *r2; | |
219 | struct neighbor *n; | |
f2010f9c | 220 | |
7126cadf | 221 | for (r2 = r; r2; r2 = r2->mp_next) |
295ae16d | 222 | { |
586c1800 OZ |
223 | n = neigh_find(&p->p, r2->via, r2->iface, NEF_STICKY | |
224 | (r2->onlink ? NEF_ONLINK : 0) | | |
225 | (ipa_zero(r2->via) ? NEF_IFACE : 0)); | |
7126cadf OZ |
226 | |
227 | if (!n) | |
9852f810 | 228 | { |
7126cadf OZ |
229 | log(L_WARN "Invalid next hop %I of static route %N", r2->via, r2->net); |
230 | continue; | |
9852f810 OZ |
231 | } |
232 | ||
7126cadf OZ |
233 | r2->neigh = n; |
234 | r2->chain = n->data; | |
235 | n->data = r2; | |
236 | ||
237 | static_update_bfd(p, r2); | |
238 | static_decide(p, r2); | |
295ae16d | 239 | } |
7126cadf OZ |
240 | } |
241 | ||
242 | static_announce_rte(p, r); | |
295ae16d MM |
243 | } |
244 | ||
538264cf | 245 | static void |
7126cadf | 246 | static_reset_rte(struct static_proto *p UNUSED, struct static_route *r) |
538264cf OZ |
247 | { |
248 | struct static_route *r2; | |
4116db18 | 249 | |
7126cadf OZ |
250 | for (r2 = r; r2; r2 = r2->mp_next) |
251 | { | |
252 | r2->neigh = NULL; | |
253 | r2->chain = NULL; | |
d360f129 | 254 | |
7126cadf OZ |
255 | r2->state = 0; |
256 | r2->active = 0; | |
f2010f9c | 257 | |
7126cadf OZ |
258 | rfree(r2->bfd_req); |
259 | r2->bfd_req = NULL; | |
260 | } | |
261 | } | |
f2010f9c | 262 | |
7126cadf OZ |
263 | static void |
264 | static_remove_rte(struct static_proto *p, struct static_route *r) | |
265 | { | |
266 | if (r->state) | |
3347aaaf | 267 | rte_update2(p->p.main_channel, r->net, NULL, static_get_source(p, r->index)); |
f2010f9c | 268 | |
7126cadf | 269 | static_reset_rte(p, r); |
a1bf6440 MM |
270 | } |
271 | ||
7126cadf OZ |
272 | |
273 | static inline int | |
274 | static_same_dest(struct static_route *x, struct static_route *y) | |
e4bfafa1 | 275 | { |
7126cadf OZ |
276 | if (x->dest != y->dest) |
277 | return 0; | |
e4bfafa1 | 278 | |
7126cadf | 279 | switch (x->dest) |
538264cf | 280 | { |
7126cadf OZ |
281 | case RTD_UNICAST: |
282 | for (; x && y; x = x->mp_next, y = y->mp_next) | |
283 | { | |
284 | if (!ipa_equal(x->via, y->via) || | |
285 | (x->iface != y->iface) || | |
a1f5e514 | 286 | (x->onlink != y->onlink) || |
7126cadf | 287 | (x->weight != y->weight) || |
a1f5e514 | 288 | (x->use_bfd != y->use_bfd) || |
3c744164 JMM |
289 | (!x->mls != !y->mls) || |
290 | ((x->mls) && (y->mls) && (x->mls->len != y->mls->len))) | |
7126cadf OZ |
291 | return 0; |
292 | ||
3c744164 JMM |
293 | if (!x->mls) |
294 | continue; | |
295 | ||
296 | for (uint i = 0; i < x->mls->len; i++) | |
297 | if (x->mls->stack[i] != y->mls->stack[i]) | |
7126cadf OZ |
298 | return 0; |
299 | } | |
300 | return !x && !y; | |
53434e44 | 301 | |
7126cadf | 302 | case RTDX_RECURSIVE: |
3c744164 JMM |
303 | if (!ipa_equal(x->via, y->via) || |
304 | (!x->mls != !y->mls) || | |
305 | ((x->mls) && (y->mls) && (x->mls->len != y->mls->len))) | |
306 | return 0; | |
307 | ||
308 | if (!x->mls) | |
309 | return 1; | |
310 | ||
311 | for (uint i = 0; i < x->mls->len; i++) | |
312 | if (x->mls->stack[i] != y->mls->stack[i]) | |
313 | return 0; | |
314 | ||
315 | return 1; | |
f4a60a9b | 316 | |
7126cadf OZ |
317 | default: |
318 | return 1; | |
319 | } | |
e4bfafa1 MM |
320 | } |
321 | ||
7126cadf OZ |
322 | static inline int |
323 | static_same_rte(struct static_route *or, struct static_route *nr) | |
4116db18 | 324 | { |
7126cadf | 325 | /* Note that i_same() requires arguments in (new, old) order */ |
4c553c5a | 326 | return static_same_dest(or, nr) && f_same(nr->cmds, or->cmds); |
4116db18 OZ |
327 | } |
328 | ||
538264cf | 329 | static void |
7126cadf | 330 | static_reconfigure_rte(struct static_proto *p, struct static_route *or, struct static_route *nr) |
538264cf | 331 | { |
7126cadf OZ |
332 | if ((or->state == SRS_CLEAN) && !static_same_rte(or, nr)) |
333 | nr->state = SRS_DIRTY; | |
334 | else | |
335 | nr->state = or->state; | |
538264cf | 336 | |
7126cadf OZ |
337 | static_add_rte(p, nr); |
338 | static_reset_rte(p, or); | |
538264cf | 339 | } |
4116db18 | 340 | |
7126cadf | 341 | |
a1bf6440 MM |
342 | static void |
343 | static_neigh_notify(struct neighbor *n) | |
344 | { | |
7126cadf | 345 | struct static_proto *p = (void *) n->proto; |
980297d2 MM |
346 | struct static_route *r; |
347 | ||
d93a43a5 | 348 | DBG("Static: neighbor notify for %I: iface %p\n", n->addr, n->iface); |
7126cadf | 349 | for (r = n->data; r; r = r->chain) |
538264cf OZ |
350 | { |
351 | static_update_bfd(p, r); | |
7126cadf OZ |
352 | |
353 | if (static_decide(p, r)) | |
354 | static_mark_rte(p, r->mp_head); | |
538264cf OZ |
355 | } |
356 | } | |
9852f810 | 357 | |
538264cf OZ |
358 | static void |
359 | static_bfd_notify(struct bfd_request *req) | |
360 | { | |
361 | struct static_route *r = req->data; | |
7126cadf | 362 | struct static_proto *p = (void *) r->neigh->proto; |
9852f810 | 363 | |
538264cf OZ |
364 | // if (req->down) TRACE(D_EVENTS, "BFD session down for nbr %I on %s", XXXX); |
365 | ||
7126cadf OZ |
366 | if (static_decide(p, r)) |
367 | static_mark_rte(p, r->mp_head); | |
a1bf6440 MM |
368 | } |
369 | ||
7126cadf | 370 | static int |
3347aaaf | 371 | static_rte_better(rte *new, rte *old) |
8d9eef17 | 372 | { |
3347aaaf OZ |
373 | u32 n = ea_get_int(new->attrs->eattrs, EA_GEN_IGP_METRIC, IGP_METRIC_UNKNOWN); |
374 | u32 o = ea_get_int(old->attrs->eattrs, EA_GEN_IGP_METRIC, IGP_METRIC_UNKNOWN); | |
375 | return n < o; | |
376 | } | |
377 | ||
378 | static int | |
379 | static_rte_mergable(rte *pri, rte *sec) | |
380 | { | |
381 | u32 a = ea_get_int(pri->attrs->eattrs, EA_GEN_IGP_METRIC, IGP_METRIC_UNKNOWN); | |
382 | u32 b = ea_get_int(sec->attrs->eattrs, EA_GEN_IGP_METRIC, IGP_METRIC_UNKNOWN); | |
383 | return a == b; | |
8d9eef17 OZ |
384 | } |
385 | ||
3347aaaf | 386 | static void static_index_routes(struct static_config *cf); |
a1bf6440 | 387 | |
f4a60a9b OZ |
388 | static void |
389 | static_postconfig(struct proto_config *CF) | |
390 | { | |
391 | struct static_config *cf = (void *) CF; | |
392 | struct static_route *r; | |
393 | ||
394 | if (EMPTY_LIST(CF->channels)) | |
395 | cf_error("Channel not specified"); | |
396 | ||
ffb38dfb OZ |
397 | struct channel_config *cc = proto_cf_main_channel(CF); |
398 | ||
399 | if (!cf->igp_table_ip4) | |
400 | cf->igp_table_ip4 = (cc->table->addr_type == NET_IP4) ? | |
401 | cc->table : cf->c.global->def_tables[NET_IP4]; | |
402 | ||
403 | if (!cf->igp_table_ip6) | |
404 | cf->igp_table_ip6 = (cc->table->addr_type == NET_IP6) ? | |
405 | cc->table : cf->c.global->def_tables[NET_IP6]; | |
406 | ||
7126cadf | 407 | WALK_LIST(r, cf->routes) |
f2010f9c JMM |
408 | if (r->net && (r->net->type != CF->net_type)) |
409 | cf_error("Route %N incompatible with channel type", r->net); | |
3347aaaf OZ |
410 | |
411 | static_index_routes(cf); | |
f4a60a9b OZ |
412 | } |
413 | ||
e9e3dc26 | 414 | static struct proto * |
f4a60a9b | 415 | static_init(struct proto_config *CF) |
a1bf6440 | 416 | { |
f4a60a9b | 417 | struct proto *P = proto_new(CF); |
ffb38dfb OZ |
418 | struct static_proto *p = (void *) P; |
419 | struct static_config *cf = (void *) CF; | |
a1bf6440 | 420 | |
f4a60a9b | 421 | P->main_channel = proto_add_channel(P, proto_cf_main_channel(CF)); |
094d2bdb | 422 | |
f4a60a9b | 423 | P->neigh_notify = static_neigh_notify; |
3347aaaf | 424 | P->rte_better = static_rte_better; |
f4a60a9b OZ |
425 | P->rte_mergable = static_rte_mergable; |
426 | ||
ffb38dfb OZ |
427 | if (cf->igp_table_ip4) |
428 | p->igp_table_ip4 = cf->igp_table_ip4->table; | |
429 | ||
430 | if (cf->igp_table_ip6) | |
431 | p->igp_table_ip6 = cf->igp_table_ip6->table; | |
432 | ||
f4a60a9b | 433 | return P; |
a1bf6440 MM |
434 | } |
435 | ||
7126cadf OZ |
436 | static int |
437 | static_start(struct proto *P) | |
c1cefd7b | 438 | { |
7126cadf OZ |
439 | struct static_proto *p = (void *) P; |
440 | struct static_config *cf = (void *) P->cf; | |
441 | struct static_route *r; | |
9852f810 | 442 | |
7126cadf | 443 | if (!static_lp) |
05d47bd5 | 444 | static_lp = lp_new(&root_pool, LP_GOOD_SIZE(1024)); |
9852f810 | 445 | |
ffb38dfb OZ |
446 | if (p->igp_table_ip4) |
447 | rt_lock_table(p->igp_table_ip4); | |
448 | ||
449 | if (p->igp_table_ip6) | |
450 | rt_lock_table(p->igp_table_ip6); | |
4116db18 | 451 | |
961671c0 | 452 | p->event = ev_new_init(p->p.pool, static_announce_marked, p); |
295ae16d | 453 | |
7126cadf | 454 | BUFFER_INIT(p->marked, p->p.pool, 4); |
1321e12a | 455 | |
7126cadf OZ |
456 | /* We have to go UP before routes could be installed */ |
457 | proto_notify_state(P, PS_UP); | |
1321e12a | 458 | |
7126cadf OZ |
459 | WALK_LIST(r, cf->routes) |
460 | static_add_rte(p, r); | |
295ae16d | 461 | |
7126cadf OZ |
462 | return PS_UP; |
463 | } | |
f2010f9c | 464 | |
7126cadf OZ |
465 | static int |
466 | static_shutdown(struct proto *P) | |
467 | { | |
468 | struct static_proto *p = (void *) P; | |
469 | struct static_config *cf = (void *) P->cf; | |
470 | struct static_route *r; | |
c1cefd7b | 471 | |
7126cadf OZ |
472 | /* Just reset the flag, the routes will be flushed by the nest */ |
473 | WALK_LIST(r, cf->routes) | |
474 | static_reset_rte(p, r); | |
d40c2659 | 475 | |
7126cadf OZ |
476 | return PS_DOWN; |
477 | } | |
f2010f9c | 478 | |
7126cadf OZ |
479 | static void |
480 | static_cleanup(struct proto *P) | |
481 | { | |
ffb38dfb OZ |
482 | struct static_proto *p = (void *) P; |
483 | ||
484 | if (p->igp_table_ip4) | |
485 | rt_unlock_table(p->igp_table_ip4); | |
d40c2659 | 486 | |
ffb38dfb OZ |
487 | if (p->igp_table_ip6) |
488 | rt_unlock_table(p->igp_table_ip6); | |
7126cadf | 489 | } |
d40c2659 | 490 | |
7126cadf OZ |
491 | static void |
492 | static_dump_rte(struct static_route *r) | |
493 | { | |
3347aaaf | 494 | debug("%-1N (%u): ", r->net, r->index); |
7126cadf OZ |
495 | if (r->dest == RTD_UNICAST) |
496 | if (r->iface && ipa_zero(r->via)) | |
497 | debug("dev %s\n", r->iface->name); | |
498 | else | |
499 | debug("via %I%J\n", r->via, r->iface); | |
500 | else | |
501 | debug("rtd %d\n", r->dest); | |
502 | } | |
d40c2659 | 503 | |
7126cadf OZ |
504 | static void |
505 | static_dump(struct proto *P) | |
506 | { | |
507 | struct static_config *c = (void *) P->cf; | |
508 | struct static_route *r; | |
f2010f9c | 509 | |
7126cadf OZ |
510 | debug("Static routes:\n"); |
511 | WALK_LIST(r, c->routes) | |
512 | static_dump_rte(r); | |
295ae16d MM |
513 | } |
514 | ||
ffb38dfb | 515 | #define IGP_TABLE(cf, sym) ((cf)->igp_table_##sym ? (cf)->igp_table_##sym ->table : NULL ) |
4116db18 | 516 | |
3347aaaf OZ |
517 | static inline int srt_equal(const struct static_route *a, const struct static_route *b) |
518 | { return net_equal(a->net, b->net) && (a->index == b->index); } | |
519 | ||
520 | static inline int srt_compare(const struct static_route *a, const struct static_route *b) | |
521 | { return net_compare(a->net, b->net) ?: uint_cmp(a->index, b->index); } | |
522 | ||
523 | static inline int srt_compare_qsort(const void *A, const void *B) | |
7126cadf | 524 | { |
3347aaaf OZ |
525 | return srt_compare(*(const struct static_route * const *)A, |
526 | *(const struct static_route * const *)B); | |
527 | } | |
528 | ||
529 | static void | |
530 | static_index_routes(struct static_config *cf) | |
531 | { | |
532 | struct static_route *rt, **buf; | |
533 | uint num, i, v; | |
534 | ||
535 | num = list_length(&cf->routes); | |
536 | buf = xmalloc(num * sizeof(void *)); | |
537 | ||
538 | /* Initialize with sequential indexes to ensure stable sorting */ | |
539 | i = 0; | |
540 | WALK_LIST(rt, cf->routes) | |
541 | { | |
542 | buf[i] = rt; | |
543 | rt->index = i++; | |
544 | } | |
545 | ||
546 | qsort(buf, num, sizeof(struct static_route *), srt_compare_qsort); | |
547 | ||
548 | /* Compute proper indexes - sequential for routes with same network */ | |
549 | for (i = 0, v = 0, rt = NULL; i < num; i++, v++) | |
550 | { | |
551 | if (rt && !net_equal(buf[i]->net, rt->net)) | |
552 | v = 0; | |
553 | ||
554 | rt = buf[i]; | |
555 | rt->index = v; | |
556 | } | |
557 | ||
558 | xfree(buf); | |
7126cadf OZ |
559 | } |
560 | ||
d272fe22 | 561 | static int |
7126cadf | 562 | static_reconfigure(struct proto *P, struct proto_config *CF) |
d272fe22 | 563 | { |
7126cadf OZ |
564 | struct static_proto *p = (void *) P; |
565 | struct static_config *o = (void *) P->cf; | |
f4a60a9b | 566 | struct static_config *n = (void *) CF; |
7126cadf | 567 | struct static_route *r, *r2, *or, *nr; |
295ae16d | 568 | |
ffb38dfb OZ |
569 | /* Check change in IGP tables */ |
570 | if ((IGP_TABLE(o, ip4) != IGP_TABLE(n, ip4)) || | |
571 | (IGP_TABLE(o, ip6) != IGP_TABLE(n, ip6))) | |
4116db18 OZ |
572 | return 0; |
573 | ||
7126cadf | 574 | if (!proto_configure_channel(P, &P->main_channel, proto_cf_main_channel(CF))) |
f4a60a9b OZ |
575 | return 0; |
576 | ||
7126cadf OZ |
577 | p->p.cf = CF; |
578 | ||
579 | /* Reset route lists in neighbor entries */ | |
580 | WALK_LIST(r, o->routes) | |
581 | for (r2 = r; r2; r2 = r2->mp_next) | |
582 | if (r2->neigh) | |
583 | r2->neigh->data = NULL; | |
584 | ||
585 | /* Reconfigure initial matching sequence */ | |
586 | for (or = HEAD(o->routes), nr = HEAD(n->routes); | |
3347aaaf | 587 | NODE_VALID(or) && NODE_VALID(nr) && srt_equal(or, nr); |
7126cadf OZ |
588 | or = NODE_NEXT(or), nr = NODE_NEXT(nr)) |
589 | static_reconfigure_rte(p, or, nr); | |
590 | ||
591 | if (!NODE_VALID(or) && !NODE_VALID(nr)) | |
592 | return 1; | |
593 | ||
594 | /* Reconfigure remaining routes, sort them to find matching pairs */ | |
595 | struct static_route *or2, *nr2, **orbuf, **nrbuf; | |
596 | uint ornum = 0, nrnum = 0, orpos = 0, nrpos = 0, i; | |
597 | ||
598 | for (or2 = or; NODE_VALID(or2); or2 = NODE_NEXT(or2)) | |
599 | ornum++; | |
600 | ||
601 | for (nr2 = nr; NODE_VALID(nr2); nr2 = NODE_NEXT(nr2)) | |
602 | nrnum++; | |
603 | ||
604 | orbuf = xmalloc(ornum * sizeof(void *)); | |
605 | nrbuf = xmalloc(nrnum * sizeof(void *)); | |
f2010f9c | 606 | |
7126cadf OZ |
607 | for (i = 0, or2 = or; i < ornum; i++, or2 = NODE_NEXT(or2)) |
608 | orbuf[i] = or2; | |
609 | ||
610 | for (i = 0, nr2 = nr; i < nrnum; i++, nr2 = NODE_NEXT(nr2)) | |
611 | nrbuf[i] = nr2; | |
612 | ||
3347aaaf OZ |
613 | qsort(orbuf, ornum, sizeof(struct static_route *), srt_compare_qsort); |
614 | qsort(nrbuf, nrnum, sizeof(struct static_route *), srt_compare_qsort); | |
7126cadf OZ |
615 | |
616 | while ((orpos < ornum) && (nrpos < nrnum)) | |
f2010f9c | 617 | { |
3347aaaf | 618 | int x = srt_compare(orbuf[orpos], nrbuf[nrpos]); |
7126cadf OZ |
619 | if (x < 0) |
620 | static_remove_rte(p, orbuf[orpos++]); | |
621 | else if (x > 0) | |
622 | static_add_rte(p, nrbuf[nrpos++]); | |
623 | else | |
624 | static_reconfigure_rte(p, orbuf[orpos++], nrbuf[nrpos++]); | |
f2010f9c | 625 | } |
295ae16d | 626 | |
7126cadf OZ |
627 | while (orpos < ornum) |
628 | static_remove_rte(p, orbuf[orpos++]); | |
538264cf | 629 | |
7126cadf OZ |
630 | while (nrpos < nrnum) |
631 | static_add_rte(p, nrbuf[nrpos++]); | |
d272fe22 | 632 | |
7126cadf OZ |
633 | xfree(orbuf); |
634 | xfree(nrbuf); | |
a7f23f58 | 635 | |
7126cadf | 636 | return 1; |
a7f23f58 OZ |
637 | } |
638 | ||
639 | static void | |
640 | static_copy_config(struct proto_config *dest, struct proto_config *src) | |
641 | { | |
642 | struct static_config *d = (struct static_config *) dest; | |
643 | struct static_config *s = (struct static_config *) src; | |
644 | ||
7126cadf | 645 | struct static_route *srt, *snh; |
a7f23f58 | 646 | |
7126cadf OZ |
647 | /* Copy route list */ |
648 | init_list(&d->routes); | |
649 | WALK_LIST(srt, s->routes) | |
650 | { | |
651 | struct static_route *drt = NULL, *dnh = NULL, **dnp = &drt; | |
feed8226 | 652 | |
7126cadf | 653 | for (snh = srt; snh; snh = snh->mp_next) |
feed8226 | 654 | { |
7126cadf OZ |
655 | dnh = cfg_alloc(sizeof(struct static_route)); |
656 | memcpy(dnh, snh, sizeof(struct static_route)); | |
1678bc07 | 657 | memset(&dnh->n, 0, sizeof(node)); |
7126cadf OZ |
658 | |
659 | if (!drt) | |
660 | add_tail(&d->routes, &(dnh->n)); | |
661 | ||
662 | *dnp = dnh; | |
663 | dnp = &(dnh->mp_next); | |
664 | ||
665 | if (snh->mp_head) | |
666 | dnh->mp_head = drt; | |
feed8226 | 667 | } |
7126cadf | 668 | } |
4e276a89 | 669 | } |
9852f810 | 670 | |
3347aaaf OZ |
671 | static void |
672 | static_get_route_info(rte *rte, byte *buf) | |
673 | { | |
674 | eattr *a = ea_find(rte->attrs->eattrs, EA_GEN_IGP_METRIC); | |
675 | if (a) | |
676 | buf += bsprintf(buf, " (%d/%u)", rte->pref, a->u.data); | |
677 | else | |
678 | buf += bsprintf(buf, " (%d)", rte->pref); | |
679 | } | |
680 | ||
4e276a89 JMM |
681 | static void |
682 | static_show_rt(struct static_route *r) | |
683 | { | |
7126cadf OZ |
684 | switch (r->dest) |
685 | { | |
686 | case RTD_UNICAST: | |
4e276a89 | 687 | { |
4e276a89 | 688 | struct static_route *r2; |
7126cadf OZ |
689 | |
690 | cli_msg(-1009, "%N", r->net); | |
4e276a89 | 691 | for (r2 = r; r2; r2 = r2->mp_next) |
f2010f9c | 692 | { |
7126cadf | 693 | if (r2->iface && ipa_zero(r2->via)) |
a1f5e514 OZ |
694 | cli_msg(-1009, "\tdev %s%s", r2->iface->name, |
695 | r2->active ? "" : " (dormant)"); | |
7126cadf | 696 | else |
a1f5e514 OZ |
697 | cli_msg(-1009, "\tvia %I%J%s%s%s", r2->via, r2->iface, |
698 | r2->onlink ? " onlink" : "", | |
699 | r2->bfd_req ? " (bfd)" : "", | |
700 | r2->active ? "" : " (dormant)"); | |
f2010f9c | 701 | } |
7126cadf OZ |
702 | break; |
703 | } | |
704 | ||
705 | case RTD_NONE: | |
706 | case RTD_BLACKHOLE: | |
707 | case RTD_UNREACHABLE: | |
708 | case RTD_PROHIBIT: | |
709 | cli_msg(-1009, "%N\t%s", r->net, rta_dest_names[r->dest]); | |
710 | break; | |
711 | ||
712 | case RTDX_RECURSIVE: | |
713 | cli_msg(-1009, "%N\trecursive %I", r->net, r->via); | |
714 | break; | |
4e276a89 | 715 | } |
feed8226 MM |
716 | } |
717 | ||
718 | void | |
719 | static_show(struct proto *P) | |
720 | { | |
721 | struct static_config *c = (void *) P->cf; | |
722 | struct static_route *r; | |
723 | ||
7126cadf | 724 | WALK_LIST(r, c->routes) |
f2010f9c | 725 | static_show_rt(r); |
feed8226 | 726 | } |
7126cadf OZ |
727 | |
728 | ||
729 | struct protocol proto_static = { | |
730 | .name = "Static", | |
731 | .template = "static%d", | |
ee7e2ffd | 732 | .class = PROTOCOL_STATIC, |
7126cadf OZ |
733 | .preference = DEF_PREF_STATIC, |
734 | .channel_mask = NB_ANY, | |
735 | .proto_size = sizeof(struct static_proto), | |
736 | .config_size = sizeof(struct static_config), | |
737 | .postconfig = static_postconfig, | |
738 | .init = static_init, | |
739 | .dump = static_dump, | |
740 | .start = static_start, | |
741 | .shutdown = static_shutdown, | |
742 | .cleanup = static_cleanup, | |
743 | .reconfigure = static_reconfigure, | |
3347aaaf OZ |
744 | .copy_config = static_copy_config, |
745 | .get_route_info = static_get_route_info, | |
7126cadf | 746 | }; |