]>
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 | ||
f6bd2066 | 52 | static void |
7126cadf | 53 | static_announce_rte(struct static_proto *p, struct static_route *r) |
f6bd2066 | 54 | { |
7126cadf OZ |
55 | rta *a = allocz(RTA_MAX_SIZE); |
56 | a->src = p->p.main_source; | |
57 | a->source = RTS_STATIC; | |
58 | a->scope = SCOPE_UNIVERSE; | |
59 | a->dest = r->dest; | |
f2010f9c | 60 | |
4e276a89 | 61 | if (r->dest == RTD_UNICAST) |
7126cadf OZ |
62 | { |
63 | struct static_route *r2; | |
64 | struct nexthop *nhs = NULL; | |
65 | ||
66 | for (r2 = r; r2; r2 = r2->mp_next) | |
9852f810 | 67 | { |
7126cadf OZ |
68 | if (!r2->active) |
69 | continue; | |
4e276a89 | 70 | |
7126cadf OZ |
71 | struct nexthop *nh = allocz(NEXTHOP_MAX_SIZE); |
72 | nh->gw = r2->via; | |
73 | nh->iface = r2->neigh->iface; | |
a1f5e514 | 74 | nh->flags = r2->onlink ? RNF_ONLINK : 0; |
7126cadf | 75 | nh->weight = r2->weight; |
3c744164 JMM |
76 | if (r2->mls) |
77 | { | |
78 | nh->labels = r2->mls->len; | |
79 | memcpy(nh->label, r2->mls->stack, r2->mls->len * sizeof(u32)); | |
80 | } | |
9852f810 | 81 | |
7126cadf OZ |
82 | nexthop_insert(&nhs, nh); |
83 | } | |
f2010f9c | 84 | |
7126cadf OZ |
85 | if (!nhs) |
86 | goto withdraw; | |
62e64905 | 87 | |
7126cadf OZ |
88 | nexthop_link(a, nhs); |
89 | } | |
62e64905 | 90 | |
4116db18 | 91 | if (r->dest == RTDX_RECURSIVE) |
ffb38dfb OZ |
92 | { |
93 | rtable *tab = ipa_is_ip4(r->via) ? p->igp_table_ip4 : p->igp_table_ip6; | |
94 | rta_set_recursive_next_hop(p->p.main_channel->table, a, tab, r->via, IPA_NONE, r->mls); | |
95 | } | |
4116db18 | 96 | |
7126cadf OZ |
97 | /* Already announced */ |
98 | if (r->state == SRS_CLEAN) | |
99 | return; | |
1321e12a | 100 | |
7126cadf OZ |
101 | /* We skip rta_lookup() here */ |
102 | rte *e = rte_get_temp(a); | |
f6bd2066 | 103 | e->pflags = 0; |
1321e12a OZ |
104 | |
105 | if (r->cmds) | |
106 | f_eval_rte(r->cmds, &e, static_lp); | |
107 | ||
7126cadf OZ |
108 | rte_update(&p->p, r->net, e); |
109 | r->state = SRS_CLEAN; | |
1321e12a OZ |
110 | |
111 | if (r->cmds) | |
112 | lp_flush(static_lp); | |
7126cadf OZ |
113 | |
114 | return; | |
115 | ||
116 | withdraw: | |
117 | if (r->state == SRS_DOWN) | |
118 | return; | |
119 | ||
120 | rte_update(&p->p, r->net, NULL); | |
121 | r->state = SRS_DOWN; | |
122 | } | |
123 | ||
124 | static void | |
125 | static_mark_rte(struct static_proto *p, struct static_route *r) | |
126 | { | |
127 | if (r->state == SRS_DIRTY) | |
128 | return; | |
129 | ||
130 | r->state = SRS_DIRTY; | |
131 | BUFFER_PUSH(p->marked) = r; | |
132 | ||
133 | if (!ev_active(p->event)) | |
134 | ev_schedule(p->event); | |
135 | } | |
136 | ||
137 | static void | |
138 | static_announce_marked(void *P) | |
139 | { | |
140 | struct static_proto *p = P; | |
141 | ||
142 | BUFFER_WALK(p->marked, r) | |
143 | static_announce_rte(P, r); | |
144 | ||
145 | BUFFER_FLUSH(p->marked); | |
f6bd2066 MM |
146 | } |
147 | ||
538264cf OZ |
148 | static void |
149 | static_bfd_notify(struct bfd_request *req); | |
150 | ||
151 | static void | |
7126cadf | 152 | static_update_bfd(struct static_proto *p, struct static_route *r) |
538264cf | 153 | { |
7126cadf OZ |
154 | /* The @r is a RTD_UNICAST next hop, may be a dummy node */ |
155 | ||
538264cf OZ |
156 | struct neighbor *nb = r->neigh; |
157 | int bfd_up = (nb->scope > 0) && r->use_bfd; | |
158 | ||
159 | if (bfd_up && !r->bfd_req) | |
160 | { | |
161 | // ip_addr local = ipa_nonzero(r->local) ? r->local : nb->ifa->ip; | |
cf7ff995 OZ |
162 | r->bfd_req = bfd_request_session(p->p.pool, r->via, nb->ifa->ip, |
163 | nb->iface, p->p.vrf, | |
9d3fc306 | 164 | static_bfd_notify, r, NULL); |
538264cf OZ |
165 | } |
166 | ||
167 | if (!bfd_up && r->bfd_req) | |
168 | { | |
169 | rfree(r->bfd_req); | |
170 | r->bfd_req = NULL; | |
171 | } | |
172 | } | |
173 | ||
9852f810 | 174 | static int |
7126cadf | 175 | static_decide(struct static_proto *p, struct static_route *r) |
9852f810 | 176 | { |
7126cadf | 177 | /* The @r is a RTD_UNICAST next hop, may be a dummy node */ |
9852f810 | 178 | |
7126cadf OZ |
179 | struct static_config *cf = (void *) p->p.cf; |
180 | uint old_active = r->active; | |
f2010f9c | 181 | |
69a8259c | 182 | if (r->neigh->scope < 0) |
7126cadf | 183 | goto fail; |
9852f810 | 184 | |
69a8259c | 185 | if (cf->check_link && !(r->neigh->iface->flags & IF_LINK_UP)) |
7126cadf | 186 | goto fail; |
9852f810 | 187 | |
7126cadf OZ |
188 | if (r->bfd_req && (r->bfd_req->state != BFD_STATE_UP)) |
189 | goto fail; | |
538264cf | 190 | |
7126cadf OZ |
191 | r->active = 1; |
192 | return !old_active; | |
9852f810 | 193 | |
7126cadf OZ |
194 | fail: |
195 | r->active = 0; | |
196 | return old_active; | |
197 | } | |
9852f810 | 198 | |
295ae16d | 199 | static void |
7126cadf | 200 | static_add_rte(struct static_proto *p, struct static_route *r) |
295ae16d | 201 | { |
7126cadf OZ |
202 | if (r->dest == RTD_UNICAST) |
203 | { | |
204 | struct static_route *r2; | |
205 | struct neighbor *n; | |
f2010f9c | 206 | |
7126cadf | 207 | for (r2 = r; r2; r2 = r2->mp_next) |
295ae16d | 208 | { |
586c1800 OZ |
209 | n = neigh_find(&p->p, r2->via, r2->iface, NEF_STICKY | |
210 | (r2->onlink ? NEF_ONLINK : 0) | | |
211 | (ipa_zero(r2->via) ? NEF_IFACE : 0)); | |
7126cadf OZ |
212 | |
213 | if (!n) | |
9852f810 | 214 | { |
7126cadf OZ |
215 | log(L_WARN "Invalid next hop %I of static route %N", r2->via, r2->net); |
216 | continue; | |
9852f810 OZ |
217 | } |
218 | ||
7126cadf OZ |
219 | r2->neigh = n; |
220 | r2->chain = n->data; | |
221 | n->data = r2; | |
222 | ||
223 | static_update_bfd(p, r2); | |
224 | static_decide(p, r2); | |
295ae16d | 225 | } |
7126cadf OZ |
226 | } |
227 | ||
228 | static_announce_rte(p, r); | |
295ae16d MM |
229 | } |
230 | ||
538264cf | 231 | static void |
7126cadf | 232 | static_reset_rte(struct static_proto *p UNUSED, struct static_route *r) |
538264cf OZ |
233 | { |
234 | struct static_route *r2; | |
4116db18 | 235 | |
7126cadf OZ |
236 | for (r2 = r; r2; r2 = r2->mp_next) |
237 | { | |
238 | r2->neigh = NULL; | |
239 | r2->chain = NULL; | |
d360f129 | 240 | |
7126cadf OZ |
241 | r2->state = 0; |
242 | r2->active = 0; | |
f2010f9c | 243 | |
7126cadf OZ |
244 | rfree(r2->bfd_req); |
245 | r2->bfd_req = NULL; | |
246 | } | |
247 | } | |
f2010f9c | 248 | |
7126cadf OZ |
249 | static void |
250 | static_remove_rte(struct static_proto *p, struct static_route *r) | |
251 | { | |
252 | if (r->state) | |
253 | rte_update(&p->p, r->net, NULL); | |
f2010f9c | 254 | |
7126cadf | 255 | static_reset_rte(p, r); |
a1bf6440 MM |
256 | } |
257 | ||
7126cadf OZ |
258 | |
259 | static inline int | |
260 | static_same_dest(struct static_route *x, struct static_route *y) | |
e4bfafa1 | 261 | { |
7126cadf OZ |
262 | if (x->dest != y->dest) |
263 | return 0; | |
e4bfafa1 | 264 | |
7126cadf | 265 | switch (x->dest) |
538264cf | 266 | { |
7126cadf OZ |
267 | case RTD_UNICAST: |
268 | for (; x && y; x = x->mp_next, y = y->mp_next) | |
269 | { | |
270 | if (!ipa_equal(x->via, y->via) || | |
271 | (x->iface != y->iface) || | |
a1f5e514 | 272 | (x->onlink != y->onlink) || |
7126cadf | 273 | (x->weight != y->weight) || |
a1f5e514 | 274 | (x->use_bfd != y->use_bfd) || |
3c744164 JMM |
275 | (!x->mls != !y->mls) || |
276 | ((x->mls) && (y->mls) && (x->mls->len != y->mls->len))) | |
7126cadf OZ |
277 | return 0; |
278 | ||
3c744164 JMM |
279 | if (!x->mls) |
280 | continue; | |
281 | ||
282 | for (uint i = 0; i < x->mls->len; i++) | |
283 | if (x->mls->stack[i] != y->mls->stack[i]) | |
7126cadf OZ |
284 | return 0; |
285 | } | |
286 | return !x && !y; | |
53434e44 | 287 | |
7126cadf | 288 | case RTDX_RECURSIVE: |
3c744164 JMM |
289 | if (!ipa_equal(x->via, y->via) || |
290 | (!x->mls != !y->mls) || | |
291 | ((x->mls) && (y->mls) && (x->mls->len != y->mls->len))) | |
292 | return 0; | |
293 | ||
294 | if (!x->mls) | |
295 | return 1; | |
296 | ||
297 | for (uint i = 0; i < x->mls->len; i++) | |
298 | if (x->mls->stack[i] != y->mls->stack[i]) | |
299 | return 0; | |
300 | ||
301 | return 1; | |
f4a60a9b | 302 | |
7126cadf OZ |
303 | default: |
304 | return 1; | |
305 | } | |
e4bfafa1 MM |
306 | } |
307 | ||
7126cadf OZ |
308 | static inline int |
309 | static_same_rte(struct static_route *or, struct static_route *nr) | |
4116db18 | 310 | { |
7126cadf | 311 | /* Note that i_same() requires arguments in (new, old) order */ |
4c553c5a | 312 | return static_same_dest(or, nr) && f_same(nr->cmds, or->cmds); |
4116db18 OZ |
313 | } |
314 | ||
538264cf | 315 | static void |
7126cadf | 316 | static_reconfigure_rte(struct static_proto *p, struct static_route *or, struct static_route *nr) |
538264cf | 317 | { |
7126cadf OZ |
318 | if ((or->state == SRS_CLEAN) && !static_same_rte(or, nr)) |
319 | nr->state = SRS_DIRTY; | |
320 | else | |
321 | nr->state = or->state; | |
538264cf | 322 | |
7126cadf OZ |
323 | static_add_rte(p, nr); |
324 | static_reset_rte(p, or); | |
538264cf | 325 | } |
4116db18 | 326 | |
7126cadf | 327 | |
a1bf6440 MM |
328 | static void |
329 | static_neigh_notify(struct neighbor *n) | |
330 | { | |
7126cadf | 331 | struct static_proto *p = (void *) n->proto; |
980297d2 MM |
332 | struct static_route *r; |
333 | ||
d93a43a5 | 334 | DBG("Static: neighbor notify for %I: iface %p\n", n->addr, n->iface); |
7126cadf | 335 | for (r = n->data; r; r = r->chain) |
538264cf OZ |
336 | { |
337 | static_update_bfd(p, r); | |
7126cadf OZ |
338 | |
339 | if (static_decide(p, r)) | |
340 | static_mark_rte(p, r->mp_head); | |
538264cf OZ |
341 | } |
342 | } | |
9852f810 | 343 | |
538264cf OZ |
344 | static void |
345 | static_bfd_notify(struct bfd_request *req) | |
346 | { | |
347 | struct static_route *r = req->data; | |
7126cadf | 348 | struct static_proto *p = (void *) r->neigh->proto; |
9852f810 | 349 | |
538264cf OZ |
350 | // if (req->down) TRACE(D_EVENTS, "BFD session down for nbr %I on %s", XXXX); |
351 | ||
7126cadf OZ |
352 | if (static_decide(p, r)) |
353 | static_mark_rte(p, r->mp_head); | |
a1bf6440 MM |
354 | } |
355 | ||
7126cadf | 356 | static int |
3e236955 | 357 | static_rte_mergable(rte *pri UNUSED, rte *sec UNUSED) |
8d9eef17 OZ |
358 | { |
359 | return 1; | |
360 | } | |
361 | ||
a1bf6440 | 362 | |
f4a60a9b OZ |
363 | static void |
364 | static_postconfig(struct proto_config *CF) | |
365 | { | |
366 | struct static_config *cf = (void *) CF; | |
367 | struct static_route *r; | |
368 | ||
369 | if (EMPTY_LIST(CF->channels)) | |
370 | cf_error("Channel not specified"); | |
371 | ||
ffb38dfb OZ |
372 | struct channel_config *cc = proto_cf_main_channel(CF); |
373 | ||
374 | if (!cf->igp_table_ip4) | |
375 | cf->igp_table_ip4 = (cc->table->addr_type == NET_IP4) ? | |
376 | cc->table : cf->c.global->def_tables[NET_IP4]; | |
377 | ||
378 | if (!cf->igp_table_ip6) | |
379 | cf->igp_table_ip6 = (cc->table->addr_type == NET_IP6) ? | |
380 | cc->table : cf->c.global->def_tables[NET_IP6]; | |
381 | ||
7126cadf | 382 | WALK_LIST(r, cf->routes) |
f2010f9c JMM |
383 | if (r->net && (r->net->type != CF->net_type)) |
384 | cf_error("Route %N incompatible with channel type", r->net); | |
f4a60a9b OZ |
385 | } |
386 | ||
e9e3dc26 | 387 | static struct proto * |
f4a60a9b | 388 | static_init(struct proto_config *CF) |
a1bf6440 | 389 | { |
f4a60a9b | 390 | struct proto *P = proto_new(CF); |
ffb38dfb OZ |
391 | struct static_proto *p = (void *) P; |
392 | struct static_config *cf = (void *) CF; | |
a1bf6440 | 393 | |
f4a60a9b | 394 | P->main_channel = proto_add_channel(P, proto_cf_main_channel(CF)); |
094d2bdb | 395 | |
f4a60a9b | 396 | P->neigh_notify = static_neigh_notify; |
f4a60a9b OZ |
397 | P->rte_mergable = static_rte_mergable; |
398 | ||
ffb38dfb OZ |
399 | if (cf->igp_table_ip4) |
400 | p->igp_table_ip4 = cf->igp_table_ip4->table; | |
401 | ||
402 | if (cf->igp_table_ip6) | |
403 | p->igp_table_ip6 = cf->igp_table_ip6->table; | |
404 | ||
f4a60a9b | 405 | return P; |
a1bf6440 MM |
406 | } |
407 | ||
7126cadf OZ |
408 | static int |
409 | static_start(struct proto *P) | |
c1cefd7b | 410 | { |
7126cadf OZ |
411 | struct static_proto *p = (void *) P; |
412 | struct static_config *cf = (void *) P->cf; | |
413 | struct static_route *r; | |
9852f810 | 414 | |
7126cadf | 415 | if (!static_lp) |
05d47bd5 | 416 | static_lp = lp_new(&root_pool, LP_GOOD_SIZE(1024)); |
9852f810 | 417 | |
ffb38dfb OZ |
418 | if (p->igp_table_ip4) |
419 | rt_lock_table(p->igp_table_ip4); | |
420 | ||
421 | if (p->igp_table_ip6) | |
422 | rt_lock_table(p->igp_table_ip6); | |
4116db18 | 423 | |
961671c0 | 424 | p->event = ev_new_init(p->p.pool, static_announce_marked, p); |
295ae16d | 425 | |
7126cadf | 426 | BUFFER_INIT(p->marked, p->p.pool, 4); |
1321e12a | 427 | |
7126cadf OZ |
428 | /* We have to go UP before routes could be installed */ |
429 | proto_notify_state(P, PS_UP); | |
1321e12a | 430 | |
7126cadf OZ |
431 | WALK_LIST(r, cf->routes) |
432 | static_add_rte(p, r); | |
295ae16d | 433 | |
7126cadf OZ |
434 | return PS_UP; |
435 | } | |
f2010f9c | 436 | |
7126cadf OZ |
437 | static int |
438 | static_shutdown(struct proto *P) | |
439 | { | |
440 | struct static_proto *p = (void *) P; | |
441 | struct static_config *cf = (void *) P->cf; | |
442 | struct static_route *r; | |
c1cefd7b | 443 | |
7126cadf OZ |
444 | /* Just reset the flag, the routes will be flushed by the nest */ |
445 | WALK_LIST(r, cf->routes) | |
446 | static_reset_rte(p, r); | |
d40c2659 | 447 | |
7126cadf OZ |
448 | return PS_DOWN; |
449 | } | |
f2010f9c | 450 | |
7126cadf OZ |
451 | static void |
452 | static_cleanup(struct proto *P) | |
453 | { | |
ffb38dfb OZ |
454 | struct static_proto *p = (void *) P; |
455 | ||
456 | if (p->igp_table_ip4) | |
457 | rt_unlock_table(p->igp_table_ip4); | |
d40c2659 | 458 | |
ffb38dfb OZ |
459 | if (p->igp_table_ip6) |
460 | rt_unlock_table(p->igp_table_ip6); | |
7126cadf | 461 | } |
d40c2659 | 462 | |
7126cadf OZ |
463 | static void |
464 | static_dump_rte(struct static_route *r) | |
465 | { | |
466 | debug("%-1N: ", r->net); | |
467 | if (r->dest == RTD_UNICAST) | |
468 | if (r->iface && ipa_zero(r->via)) | |
469 | debug("dev %s\n", r->iface->name); | |
470 | else | |
471 | debug("via %I%J\n", r->via, r->iface); | |
472 | else | |
473 | debug("rtd %d\n", r->dest); | |
474 | } | |
d40c2659 | 475 | |
7126cadf OZ |
476 | static void |
477 | static_dump(struct proto *P) | |
478 | { | |
479 | struct static_config *c = (void *) P->cf; | |
480 | struct static_route *r; | |
f2010f9c | 481 | |
7126cadf OZ |
482 | debug("Static routes:\n"); |
483 | WALK_LIST(r, c->routes) | |
484 | static_dump_rte(r); | |
295ae16d MM |
485 | } |
486 | ||
ffb38dfb | 487 | #define IGP_TABLE(cf, sym) ((cf)->igp_table_##sym ? (cf)->igp_table_##sym ->table : NULL ) |
4116db18 | 488 | |
7126cadf OZ |
489 | static inline int |
490 | static_cmp_rte(const void *X, const void *Y) | |
491 | { | |
492 | struct static_route *x = *(void **)X, *y = *(void **)Y; | |
493 | return net_compare(x->net, y->net); | |
494 | } | |
495 | ||
d272fe22 | 496 | static int |
7126cadf | 497 | static_reconfigure(struct proto *P, struct proto_config *CF) |
d272fe22 | 498 | { |
7126cadf OZ |
499 | struct static_proto *p = (void *) P; |
500 | struct static_config *o = (void *) P->cf; | |
f4a60a9b | 501 | struct static_config *n = (void *) CF; |
7126cadf | 502 | struct static_route *r, *r2, *or, *nr; |
295ae16d | 503 | |
ffb38dfb OZ |
504 | /* Check change in IGP tables */ |
505 | if ((IGP_TABLE(o, ip4) != IGP_TABLE(n, ip4)) || | |
506 | (IGP_TABLE(o, ip6) != IGP_TABLE(n, ip6))) | |
4116db18 OZ |
507 | return 0; |
508 | ||
7126cadf | 509 | if (!proto_configure_channel(P, &P->main_channel, proto_cf_main_channel(CF))) |
f4a60a9b OZ |
510 | return 0; |
511 | ||
7126cadf OZ |
512 | p->p.cf = CF; |
513 | ||
514 | /* Reset route lists in neighbor entries */ | |
515 | WALK_LIST(r, o->routes) | |
516 | for (r2 = r; r2; r2 = r2->mp_next) | |
517 | if (r2->neigh) | |
518 | r2->neigh->data = NULL; | |
519 | ||
520 | /* Reconfigure initial matching sequence */ | |
521 | for (or = HEAD(o->routes), nr = HEAD(n->routes); | |
522 | NODE_VALID(or) && NODE_VALID(nr) && net_equal(or->net, nr->net); | |
523 | or = NODE_NEXT(or), nr = NODE_NEXT(nr)) | |
524 | static_reconfigure_rte(p, or, nr); | |
525 | ||
526 | if (!NODE_VALID(or) && !NODE_VALID(nr)) | |
527 | return 1; | |
528 | ||
529 | /* Reconfigure remaining routes, sort them to find matching pairs */ | |
530 | struct static_route *or2, *nr2, **orbuf, **nrbuf; | |
531 | uint ornum = 0, nrnum = 0, orpos = 0, nrpos = 0, i; | |
532 | ||
533 | for (or2 = or; NODE_VALID(or2); or2 = NODE_NEXT(or2)) | |
534 | ornum++; | |
535 | ||
536 | for (nr2 = nr; NODE_VALID(nr2); nr2 = NODE_NEXT(nr2)) | |
537 | nrnum++; | |
538 | ||
539 | orbuf = xmalloc(ornum * sizeof(void *)); | |
540 | nrbuf = xmalloc(nrnum * sizeof(void *)); | |
f2010f9c | 541 | |
7126cadf OZ |
542 | for (i = 0, or2 = or; i < ornum; i++, or2 = NODE_NEXT(or2)) |
543 | orbuf[i] = or2; | |
544 | ||
545 | for (i = 0, nr2 = nr; i < nrnum; i++, nr2 = NODE_NEXT(nr2)) | |
546 | nrbuf[i] = nr2; | |
547 | ||
548 | qsort(orbuf, ornum, sizeof(struct static_route *), static_cmp_rte); | |
549 | qsort(nrbuf, nrnum, sizeof(struct static_route *), static_cmp_rte); | |
550 | ||
551 | while ((orpos < ornum) && (nrpos < nrnum)) | |
f2010f9c | 552 | { |
7126cadf OZ |
553 | int x = net_compare(orbuf[orpos]->net, nrbuf[nrpos]->net); |
554 | if (x < 0) | |
555 | static_remove_rte(p, orbuf[orpos++]); | |
556 | else if (x > 0) | |
557 | static_add_rte(p, nrbuf[nrpos++]); | |
558 | else | |
559 | static_reconfigure_rte(p, orbuf[orpos++], nrbuf[nrpos++]); | |
f2010f9c | 560 | } |
295ae16d | 561 | |
7126cadf OZ |
562 | while (orpos < ornum) |
563 | static_remove_rte(p, orbuf[orpos++]); | |
538264cf | 564 | |
7126cadf OZ |
565 | while (nrpos < nrnum) |
566 | static_add_rte(p, nrbuf[nrpos++]); | |
d272fe22 | 567 | |
7126cadf OZ |
568 | xfree(orbuf); |
569 | xfree(nrbuf); | |
a7f23f58 | 570 | |
7126cadf | 571 | return 1; |
a7f23f58 OZ |
572 | } |
573 | ||
574 | static void | |
575 | static_copy_config(struct proto_config *dest, struct proto_config *src) | |
576 | { | |
577 | struct static_config *d = (struct static_config *) dest; | |
578 | struct static_config *s = (struct static_config *) src; | |
579 | ||
7126cadf | 580 | struct static_route *srt, *snh; |
a7f23f58 | 581 | |
7126cadf OZ |
582 | /* Copy route list */ |
583 | init_list(&d->routes); | |
584 | WALK_LIST(srt, s->routes) | |
585 | { | |
586 | struct static_route *drt = NULL, *dnh = NULL, **dnp = &drt; | |
feed8226 | 587 | |
7126cadf | 588 | for (snh = srt; snh; snh = snh->mp_next) |
feed8226 | 589 | { |
7126cadf OZ |
590 | dnh = cfg_alloc(sizeof(struct static_route)); |
591 | memcpy(dnh, snh, sizeof(struct static_route)); | |
592 | ||
593 | if (!drt) | |
594 | add_tail(&d->routes, &(dnh->n)); | |
595 | ||
596 | *dnp = dnh; | |
597 | dnp = &(dnh->mp_next); | |
598 | ||
599 | if (snh->mp_head) | |
600 | dnh->mp_head = drt; | |
feed8226 | 601 | } |
7126cadf | 602 | } |
4e276a89 | 603 | } |
9852f810 | 604 | |
4e276a89 JMM |
605 | static void |
606 | static_show_rt(struct static_route *r) | |
607 | { | |
7126cadf OZ |
608 | switch (r->dest) |
609 | { | |
610 | case RTD_UNICAST: | |
4e276a89 | 611 | { |
4e276a89 | 612 | struct static_route *r2; |
7126cadf OZ |
613 | |
614 | cli_msg(-1009, "%N", r->net); | |
4e276a89 | 615 | for (r2 = r; r2; r2 = r2->mp_next) |
f2010f9c | 616 | { |
7126cadf | 617 | if (r2->iface && ipa_zero(r2->via)) |
a1f5e514 OZ |
618 | cli_msg(-1009, "\tdev %s%s", r2->iface->name, |
619 | r2->active ? "" : " (dormant)"); | |
7126cadf | 620 | else |
a1f5e514 OZ |
621 | cli_msg(-1009, "\tvia %I%J%s%s%s", r2->via, r2->iface, |
622 | r2->onlink ? " onlink" : "", | |
623 | r2->bfd_req ? " (bfd)" : "", | |
624 | r2->active ? "" : " (dormant)"); | |
f2010f9c | 625 | } |
7126cadf OZ |
626 | break; |
627 | } | |
628 | ||
629 | case RTD_NONE: | |
630 | case RTD_BLACKHOLE: | |
631 | case RTD_UNREACHABLE: | |
632 | case RTD_PROHIBIT: | |
633 | cli_msg(-1009, "%N\t%s", r->net, rta_dest_names[r->dest]); | |
634 | break; | |
635 | ||
636 | case RTDX_RECURSIVE: | |
637 | cli_msg(-1009, "%N\trecursive %I", r->net, r->via); | |
638 | break; | |
4e276a89 | 639 | } |
feed8226 MM |
640 | } |
641 | ||
642 | void | |
643 | static_show(struct proto *P) | |
644 | { | |
645 | struct static_config *c = (void *) P->cf; | |
646 | struct static_route *r; | |
647 | ||
7126cadf | 648 | WALK_LIST(r, c->routes) |
f2010f9c | 649 | static_show_rt(r); |
feed8226 | 650 | } |
7126cadf OZ |
651 | |
652 | ||
653 | struct protocol proto_static = { | |
654 | .name = "Static", | |
655 | .template = "static%d", | |
ee7e2ffd | 656 | .class = PROTOCOL_STATIC, |
7126cadf OZ |
657 | .preference = DEF_PREF_STATIC, |
658 | .channel_mask = NB_ANY, | |
659 | .proto_size = sizeof(struct static_proto), | |
660 | .config_size = sizeof(struct static_config), | |
661 | .postconfig = static_postconfig, | |
662 | .init = static_init, | |
663 | .dump = static_dump, | |
664 | .start = static_start, | |
665 | .shutdown = static_shutdown, | |
666 | .cleanup = static_cleanup, | |
667 | .reconfigure = static_reconfigure, | |
668 | .copy_config = static_copy_config | |
669 | }; |