]> git.ipfire.org Git - thirdparty/bird.git/blame - proto/static/static.c
Kernel: Do not use route replace when krt_metric differs
[thirdparty/bird.git] / proto / static / static.c
CommitLineData
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
51static linpool *static_lp;
52
3347aaaf
OZ
53static 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 56static void
7126cadf 57static_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
194withdraw:
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
202static void
203static_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
215static void
216static_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
235static void
236static_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
259static void
260static_bfd_notify(struct bfd_request *req);
261
262static void
7126cadf 263static_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 285static int
7126cadf 286static_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
305fail:
306 r->active = 0;
307 return old_active;
308}
9852f810 309
295ae16d 310static void
7126cadf 311static_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 342static void
7126cadf 343static_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
360static void
361static_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
370static inline int
371static_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
419static inline int
420static_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 426static void
7126cadf 427static_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
439static void
440static_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
455static void
456static_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
467static void
468static_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 477static int
3347aaaf 478static_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
485static int
486static_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 493static void static_index_routes(struct static_config *cf);
a1bf6440 494
f4a60a9b
OZ
495static void
496static_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 531static struct proto *
f4a60a9b 532static_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
556static int
557static_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
587static int
588static_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
603static void
604static_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
615static void
616static_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
628static void
629static_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
641static 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
644static 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
647static 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
653static void
654static_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 685static int
7126cadf 686static_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
770static void
771static_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
802static void
803static_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
812static void
813static_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
849void
850static_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
860struct 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
879void
880static_build(void)
881{
882 proto_build(&proto_static);
883}