diff options
-rw-r--r-- | doc/bird.sgml | 12 | ||||
-rw-r--r-- | sysdep/bsd/krt-sock.c | 13 | ||||
-rw-r--r-- | sysdep/linux/netlink/netlink.c | 29 | ||||
-rw-r--r-- | sysdep/unix/krt.Y | 5 | ||||
-rw-r--r-- | sysdep/unix/krt.c | 158 | ||||
-rw-r--r-- | sysdep/unix/krt.h | 8 | ||||
-rw-r--r-- | sysdep/unix/main.c | 2 |
7 files changed, 186 insertions, 41 deletions
diff --git a/doc/bird.sgml b/doc/bird.sgml index 4024f137..20738be3 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -1618,7 +1618,7 @@ kernel table. <p>Because the kernel protocol is partially integrated with the connected routing table, there are two limitations - it is not possible to connect more kernel protocols to the same routing table -and changing route attributes (even the kernel ones) in an export +and changing route destination/gateway in an export filter of a kernel protocol does not work. Both limitations can be overcome using another routing table and the pipe protocol. @@ -1653,6 +1653,16 @@ are translated to appropriate system (and OS-specific) route attributes. We support these attributes: <descrip> + <tag>int <cf/krt_source/</tag> The original source of the imported + kernel route. The value is system-dependent. On Linux, it is + a value of the protocol field of the route. See + /etc/iproute2/rt_protos for common values. On BSD, it is + based on STATIC and PROTOx flags. The attribute is read-only. + + <tag>int <cf/krt_metric/</tag> The kernel metric of + the route. When multiple same routes are in a kernel routing + table, the Linux kernel chooses one with lower metric. + <tag>ip <cf/krt_prefsrc/</tag> (Linux) The preferred source address. Used in source address selection for outgoing packets. Have to be one of IP addresses of the router. diff --git a/sysdep/bsd/krt-sock.c b/sysdep/bsd/krt-sock.c index 4ee5495f..9ca36d83 100644 --- a/sysdep/bsd/krt-sock.c +++ b/sysdep/bsd/krt-sock.c @@ -189,7 +189,8 @@ krt_sock_send(int cmd, rte *e) } void -krt_set_notify(struct krt_proto *p UNUSED, net *n, rte *new, rte *old) +krt_set_notify(struct krt_proto *p UNUSED, net *n, rte *new, rte *old, + struct ea_list *eattrs UNUSED) { int err = 0; @@ -255,10 +256,11 @@ krt_read_rt(struct ks_msg *msg, struct krt_proto *p, int scan) ip_addr idst, igate, imask; void *body = (char *)msg->buf; int new = (msg->rtm.rtm_type == RTM_ADD); - int src; char *errmsg = "KRT: Invalid route received"; int flags = msg->rtm.rtm_flags; int addrs = msg->rtm.rtm_addrs; + int src; + byte src2; if (!(flags & RTF_UP) && scan) SKIP("not up in scan\n"); @@ -302,12 +304,17 @@ krt_read_rt(struct ks_msg *msg, struct krt_proto *p, int scan) u32 self_mask = RTF_PROTO1; u32 alien_mask = RTF_STATIC | RTF_PROTO1 | RTF_GATEWAY; + src2 = (flags & RTF_STATIC) ? 1 : 0; + src2 |= (flags & RTF_PROTO1) ? 2 : 0; + #ifdef RTF_PROTO2 alien_mask |= RTF_PROTO2; + src2 |= (flags & RTF_PROTO2) ? 4 : 0; #endif #ifdef RTF_PROTO3 alien_mask |= RTF_PROTO3; + src2 |= (flags & RTF_PROTO3) ? 8 : 0; #endif #ifdef RTF_REJECT @@ -397,9 +404,9 @@ krt_read_rt(struct ks_msg *msg, struct krt_proto *p, int scan) e = rte_get_temp(&a); e->net = net; e->u.krt.src = src; + e->u.krt.proto = src2; /* These are probably too Linux-specific */ - e->u.krt.proto = 0; e->u.krt.type = 0; e->u.krt.metric = 0; diff --git a/sysdep/linux/netlink/netlink.c b/sysdep/linux/netlink/netlink.c index 17c369ea..c8eed0f3 100644 --- a/sysdep/linux/netlink/netlink.c +++ b/sysdep/linux/netlink/netlink.c @@ -612,7 +612,7 @@ nh_bufsize(struct mpnh *nh) } static int -nl_send_route(struct krt_proto *p, rte *e, int new) +nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int new) { eattr *ea; net *net = e->net; @@ -639,10 +639,18 @@ nl_send_route(struct krt_proto *p, rte *e, int new) r.r.rtm_scope = RT_SCOPE_UNIVERSE; nl_add_attr_ipa(&r.h, sizeof(r), RTA_DST, net->n.prefix); - if (ea = ea_find(a->eattrs, EA_KRT_PREFSRC)) + u32 metric = 0; + if (new && e->attrs->source == RTS_INHERIT) + metric = e->u.krt.metric; + if (ea = ea_find(eattrs, EA_KRT_METRIC)) + metric = ea->u.data; + if (metric != 0) + nl_add_attr_u32(&r.h, sizeof(r), RTA_PRIORITY, metric); + + if (ea = ea_find(eattrs, EA_KRT_PREFSRC)) nl_add_attr_ipa(&r.h, sizeof(r), RTA_PREFSRC, *(ip_addr *)ea->u.ptr->data); - if (ea = ea_find(a->eattrs, EA_KRT_REALM)) + if (ea = ea_find(eattrs, EA_KRT_REALM)) nl_add_attr_u32(&r.h, sizeof(r), RTA_FLOW, ea->u.data); switch (a->dest) @@ -683,15 +691,22 @@ nl_send_route(struct krt_proto *p, rte *e, int new) } void -krt_set_notify(struct krt_proto *p, net *n, rte *new, rte *old) +krt_set_notify(struct krt_proto *p, net *n, rte *new, rte *old, struct ea_list *eattrs) { int err = 0; + /* + * NULL for eattr of the old route is a little hack, but we don't + * get proper eattrs for old in rt_notify() anyway. NULL means no + * extended route attributes and therefore matches if the kernel + * route has any of them. + */ + if (old) - nl_send_route(p, old, 0); + nl_send_route(p, old, NULL, 0); if (new) - err = nl_send_route(p, new, 1); + err = nl_send_route(p, new, eattrs, 1); if (err < 0) n->n.flags |= KRF_SYNC_ERROR; @@ -886,7 +901,7 @@ nl_parse_route(struct nlmsghdr *h, int scan) e->u.krt.type = i->rtm_type; if (a[RTA_PRIORITY]) - memcpy(&e->u.krt.metric, RTA_DATA(a[RTA_PRIORITY]), sizeof(e->u.krt.metric)); + memcpy(&e->u.krt.metric, RTA_DATA(a[RTA_PRIORITY]), sizeof(e->u.krt.metric)); else e->u.krt.metric = 0; diff --git a/sysdep/unix/krt.Y b/sysdep/unix/krt.Y index 18e1e52d..c0141f57 100644 --- a/sysdep/unix/krt.Y +++ b/sysdep/unix/krt.Y @@ -17,7 +17,7 @@ CF_DEFINES CF_DECLS -CF_KEYWORDS(KERNEL, PERSIST, SCAN, TIME, LEARN, DEVICE, ROUTES) +CF_KEYWORDS(KERNEL, PERSIST, SCAN, TIME, LEARN, DEVICE, ROUTES, KRT_SOURCE, KRT_METRIC) CF_GRAMMAR @@ -90,6 +90,9 @@ kif_item: } ; +CF_ADDTO(dynamic_attr, KRT_SOURCE { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF_TEMP, T_INT, EA_KRT_SOURCE); }) +CF_ADDTO(dynamic_attr, KRT_METRIC { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF_TEMP, T_INT, EA_KRT_METRIC); }) + CF_CODE CF_END diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index 0fb8c4f9..9ef33c74 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -46,6 +46,7 @@ #include "nest/iface.h" #include "nest/route.h" #include "nest/protocol.h" +#include "filter/filter.h" #include "lib/timer.h" #include "conf/conf.h" #include "lib/string.h" @@ -53,18 +54,18 @@ #include "unix.h" #include "krt.h" -static int krt_uptodate(rte *k, rte *e); - /* * Global resources */ pool *krt_pool; +static linpool *krt_filter_lp; void krt_io_init(void) { krt_pool = rp_new(&root_pool, "Kernel Syncer"); + krt_filter_lp = lp_new(krt_pool, 4080); krt_if_io_init(); } @@ -278,12 +279,30 @@ krt_trace_in_rl(struct rate_limit *rl, struct krt_proto *p, rte *e, char *msg) static struct rate_limit rl_alien_seen, rl_alien_updated, rl_alien_created, rl_alien_ignored; +/* + * krt_same_key() specifies what (aside from the net) is the key in + * kernel routing tables. It should be OS-dependent, this is for + * Linux. It is important for asynchronous alien updates, because a + * positive update is implicitly a negative one for any old route with + * the same key. + */ + static inline int krt_same_key(rte *a, rte *b) { - return a->u.krt.proto == b->u.krt.proto && - a->u.krt.metric == b->u.krt.metric && - a->u.krt.type == b->u.krt.type; + return a->u.krt.metric == b->u.krt.metric; +} + +static inline int +krt_uptodate(rte *a, rte *b) +{ + if (a->attrs != b->attrs) + return 0; + + if (a->u.krt.proto != b->u.krt.proto) + return 0; + + return 1; } static void @@ -308,6 +327,7 @@ krt_learn_announce_delete(struct krt_proto *p, net *n) rte_update(p->p.table, n, &p->p, &p->p, NULL); } +/* Called when alien route is discovered during scan */ static void krt_learn_scan(struct krt_proto *p, rte *e) { @@ -315,7 +335,7 @@ krt_learn_scan(struct krt_proto *p, rte *e) net *n = net_get(&p->krt_table, n0->n.prefix, n0->n.pxlen); rte *m, **mm; - e->attrs->source = RTS_INHERIT; + e->attrs = rta_lookup(e->attrs); for(mm=&n->routes; m = *mm; mm=&m->next) if (krt_same_key(m, e)) @@ -340,7 +360,6 @@ krt_learn_scan(struct krt_proto *p, rte *e) krt_trace_in_rl(&rl_alien_created, p, e, "[alien] created"); if (!m) { - e->attrs = rta_lookup(e->attrs); e->next = n->routes; n->routes = e; e->u.krt.seen = 1; @@ -416,7 +435,7 @@ krt_learn_async(struct krt_proto *p, rte *e, int new) net *n = net_get(&p->krt_table, n0->n.prefix, n0->n.pxlen); rte *g, **gg, *best, **bestp, *old_best; - e->attrs->source = RTS_INHERIT; + e->attrs = rta_lookup(e->attrs); old_best = n->routes; for(gg=&n->routes; g = *gg; gg = &g->next) @@ -438,7 +457,7 @@ krt_learn_async(struct krt_proto *p, rte *e, int new) } else krt_trace_in(p, e, "[alien async] created"); - e->attrs = rta_lookup(e->attrs); + e->next = n->routes; n->routes = e; } @@ -538,7 +557,8 @@ krt_flush_routes(struct krt_proto *p) if ((n->n.flags & KRF_INSTALLED) && a->source != RTS_DEVICE && a->source != RTS_INHERIT) { - krt_set_notify(p, e->net, NULL, e); + /* FIXME: this does not work if gw is changed in export filter */ + krt_set_notify(p, e->net, NULL, e, NULL); n->n.flags &= ~KRF_INSTALLED; } } @@ -547,7 +567,7 @@ krt_flush_routes(struct krt_proto *p) } static int -krt_uptodate(rte *k, rte *e) +krt_same_dest(rte *k, rte *e) { rta *ka = k->attrs, *ea = e->attrs; @@ -559,6 +579,8 @@ krt_uptodate(rte *k, rte *e) return ipa_equal(ka->gw, ea->gw); case RTD_DEVICE: return !strcmp(ka->iface->name, ea->iface->name); + case RTD_MULTIPATH: + return mpnh_same(ka->nexthops, ea->nexthops); default: return 1; } @@ -611,10 +633,12 @@ krt_got_route(struct krt_proto *p, rte *e) old = net->routes; if ((net->n.flags & KRF_INSTALLED) && old) { - if (krt_uptodate(e, old)) - verdict = KRF_SEEN; - else + /* There may be changes in route attributes, we ignore that. + Also, this does not work well if gw is changed in export filter */ + if ((net->n.flags & KRF_SYNC_ERROR) || ! krt_same_dest(e, old)) verdict = KRF_UPDATE; + else + verdict = KRF_SEEN; } else verdict = KRF_DELETE; @@ -624,7 +648,7 @@ krt_got_route(struct krt_proto *p, rte *e) net->n.flags = (net->n.flags & ~KRF_VERDICT_MASK) | verdict; if (verdict == KRF_UPDATE || verdict == KRF_DELETE) { - /* Get a cached copy of attributes and link the route */ + /* Get a cached copy of attributes and temporarily link the route */ rta *a = e->attrs; a->source = RTS_DUMMY; e->attrs = rta_lookup(a); @@ -635,6 +659,25 @@ krt_got_route(struct krt_proto *p, rte *e) rte_free(e); } +static inline int +krt_export_rte(struct krt_proto *p, rte **new, ea_list **tmpa) +{ + struct filter *filter = p->p.out_filter; + + if (! *new) + return 0; + + if (filter == FILTER_REJECT) + return 0; + + if (filter == FILTER_ACCEPT) + return 1; + + struct proto *src = (*new)->attrs->proto; + *tmpa = src->make_tmp_attrs ? src->make_tmp_attrs(*new, krt_filter_lp) : NULL; + return f_run(filter, new, tmpa, krt_filter_lp, FF_FORCE_TMPATTR) <= F_ACCEPT; +} + static void krt_prune(struct krt_proto *p) { @@ -645,16 +688,28 @@ krt_prune(struct krt_proto *p) { net *n = (net *) f; int verdict = f->flags & KRF_VERDICT_MASK; - rte *new, *old; + rte *new, *new0, *old; + ea_list *tmpa = NULL; - if (verdict != KRF_CREATE && verdict != KRF_SEEN && verdict != KRF_IGNORE) + if (verdict == KRF_UPDATE || verdict == KRF_DELETE) { + /* Get a dummy route from krt_got_route() */ old = n->routes; n->routes = old->next; } else old = NULL; - new = n->routes; + + new = new0 = n->routes; + if (verdict == KRF_CREATE || verdict == KRF_UPDATE) + { + /* We have to run export filter to get proper 'new' route */ + if (! krt_export_rte(p, &new, &tmpa)) + { + /* Route rejected, should not happen (KRF_INSTALLED) but to be sure .. */ + verdict = (verdict == KRF_CREATE) ? KRF_IGNORE : KRF_DELETE; + } + } switch (verdict) { @@ -662,7 +717,7 @@ krt_prune(struct krt_proto *p) if (new && (f->flags & KRF_INSTALLED)) { krt_trace_in(p, new, "reinstalling"); - krt_set_notify(p, n, new, NULL); + krt_set_notify(p, n, new, NULL, tmpa); } break; case KRF_SEEN: @@ -671,17 +726,21 @@ krt_prune(struct krt_proto *p) break; case KRF_UPDATE: krt_trace_in(p, new, "updating"); - krt_set_notify(p, n, new, old); + krt_set_notify(p, n, new, old, tmpa); break; case KRF_DELETE: krt_trace_in(p, old, "deleting"); - krt_set_notify(p, n, NULL, old); + krt_set_notify(p, n, NULL, old, NULL); break; default: bug("krt_prune: invalid route status"); } + if (old) rte_free(old); + if (new != new0) + rte_free(new); + lp_flush(krt_filter_lp); f->flags &= ~KRF_VERDICT_MASK; } FIB_WALK_END; @@ -707,7 +766,7 @@ krt_got_route_async(struct krt_proto *p, rte *e, int new) if (new) { krt_trace_in(p, e, "[redirect] deleting"); - krt_set_notify(p, net, NULL, e); + krt_set_notify(p, net, NULL, e, NULL); } /* If !new, it is probably echo of our deletion */ break; @@ -781,7 +840,7 @@ krt_import_control(struct proto *P, rte **new, ea_list **attrs, struct linpool * static void krt_notify(struct proto *P, struct rtable *table UNUSED, net *net, - rte *new, rte *old, struct ea_list *attrs UNUSED) + rte *new, rte *old, struct ea_list *eattrs) { struct krt_proto *p = (struct krt_proto *) P; @@ -793,8 +852,8 @@ krt_notify(struct proto *P, struct rtable *table UNUSED, net *net, net->n.flags |= KRF_INSTALLED; else net->n.flags &= ~KRF_INSTALLED; - if (p->initialized) /* Before first scan we don't touch the routes */ - krt_set_notify(p, net, new, old); + if (p->initialized) /* Before first scan we don't touch the routes */ + krt_set_notify(p, net, new, old, eattrs); } /* @@ -907,14 +966,53 @@ krt_shutdown(struct proto *P) return PS_DOWN; } +static struct ea_list * +krt_make_tmp_attrs(rte *rt, struct linpool *pool) +{ + struct ea_list *l = lp_alloc(pool, sizeof(struct ea_list) + 2 * sizeof(eattr)); + + l->next = NULL; + l->flags = EALF_SORTED; + l->count = 2; + + l->attrs[0].id = EA_KRT_SOURCE; + l->attrs[0].flags = 0; + l->attrs[0].type = EAF_TYPE_INT | EAF_TEMP; + l->attrs[0].u.data = rt->u.krt.proto; + + l->attrs[1].id = EA_KRT_METRIC; + l->attrs[1].flags = 0; + l->attrs[1].type = EAF_TYPE_INT | EAF_TEMP; + l->attrs[1].u.data = rt->u.krt.metric; + + return l; +} + +static void +krt_store_tmp_attrs(rte *rt, struct ea_list *attrs) +{ + /* EA_KRT_SOURCE is read-only */ + rt->u.krt.metric = ea_get_int(attrs, EA_KRT_METRIC, 0); +} + +static int +krt_rte_same(rte *a, rte *b) +{ + /* src is always KRT_SRC_ALIEN and type is irrelevant */ + return (a->u.krt.proto == b->u.krt.proto) && (a->u.krt.metric == b->u.krt.metric); +} + static struct proto * krt_init(struct proto_config *c) { struct krt_proto *p = proto_new(c, sizeof(struct krt_proto)); p->p.accept_ra_types = RA_OPTIMAL; + p->p.make_tmp_attrs = krt_make_tmp_attrs; + p->p.store_tmp_attrs = krt_store_tmp_attrs; p->p.import_control = krt_import_control; p->p.rt_notify = krt_notify; + p->p.rte_same = krt_rte_same; return &p->p; } @@ -952,12 +1050,22 @@ krt_get_attr(eattr * a, byte * buf, int buflen UNUSED) { switch (a->id) { + case EA_KRT_SOURCE: + bsprintf(buf, "source"); + return GA_NAME; + + case EA_KRT_METRIC: + bsprintf(buf, "metric"); + return GA_NAME; + case EA_KRT_PREFSRC: bsprintf(buf, "prefsrc"); return GA_NAME; + case EA_KRT_REALM: bsprintf(buf, "realm"); return GA_NAME; + default: return GA_UNKNOWN; } diff --git a/sysdep/unix/krt.h b/sysdep/unix/krt.h index b0c4dc5e..19b69e49 100644 --- a/sysdep/unix/krt.h +++ b/sysdep/unix/krt.h @@ -28,8 +28,10 @@ struct kif_proto; #define KRF_DELETE 3 /* Should be deleted */ #define KRF_IGNORE 4 /* To be ignored */ -#define EA_KRT_PREFSRC EA_CODE(EAP_KRT, 0) -#define EA_KRT_REALM EA_CODE(EAP_KRT, 1) +#define EA_KRT_SOURCE EA_CODE(EAP_KRT, 0) +#define EA_KRT_METRIC EA_CODE(EAP_KRT, 1) +#define EA_KRT_PREFSRC EA_CODE(EAP_KRT, 2) +#define EA_KRT_REALM EA_CODE(EAP_KRT, 3) /* Whenever we recognize our own routes, we allow learing of foreign routes */ @@ -130,7 +132,7 @@ void krt_set_start(struct krt_proto *, int); void krt_set_shutdown(struct krt_proto *, int); int krt_capable(rte *e); -void krt_set_notify(struct krt_proto *x, net *net, rte *new, rte *old); +void krt_set_notify(struct krt_proto *p, net *n, rte *new, rte *old, struct ea_list *eattrs); /* krt-iface.c */ diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c index a4e80154..9219da9b 100644 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.c @@ -191,7 +191,7 @@ sysdep_preconfig(struct config *c) init_list(&c->logfiles); #ifdef PATH_IPROUTE_DIR - // read_iproute_table(PATH_IPROUTE_DIR "/rt_protos", "ipp_", 256); + read_iproute_table(PATH_IPROUTE_DIR "/rt_protos", "ipp_", 256); read_iproute_table(PATH_IPROUTE_DIR "/rt_realms", "ipr_", 256); read_iproute_table(PATH_IPROUTE_DIR "/rt_scopes", "ips_", 256); read_iproute_table(PATH_IPROUTE_DIR "/rt_tables", "ipt_", 256); |