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