diff options
-rw-r--r-- | sysdep/linux/netlink.c | 5 | ||||
-rw-r--r-- | sysdep/unix/krt.c | 107 |
2 files changed, 71 insertions, 41 deletions
diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index c4d52255..9f206e1c 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -703,6 +703,11 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int new) r.r.rtm_scope = RT_SCOPE_UNIVERSE; nl_add_attr_ipa(&r.h, sizeof(r), RTA_DST, net->n.prefix); + /* For route delete, we do not specify route attributes */ + if (!new) + return nl_exchange(&r.h); + + if (ea = ea_find(eattrs, EA_KRT_METRIC)) nl_add_attr_u32(&r.h, sizeof(r), RTA_PRIORITY, ea->u.data); diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index cfb623ce..d8d28c7c 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -592,6 +592,44 @@ krt_flush_routes(struct krt_proto *p) FIB_WALK_END; } +static struct rte * +krt_export_net(struct krt_proto *p, net *net, rte **rt_free, ea_list **tmpa) +{ + struct filter *filter = p->p.main_ahook->out_filter; + rte *rt; + + rt = net->routes; + *rt_free = NULL; + + if (!rte_is_valid(rt)) + return NULL; + + if (filter == FILTER_REJECT) + return NULL; + + struct proto *src = rt->attrs->src->proto; + *tmpa = src->make_tmp_attrs ? src->make_tmp_attrs(rt, krt_filter_lp) : NULL; + + /* We could run krt_import_control() here, but it is already handled by KRF_INSTALLED */ + + if (filter == FILTER_ACCEPT) + goto accept; + + if (f_run(filter, &rt, tmpa, krt_filter_lp, FF_FORCE_TMPATTR) > F_ACCEPT) + goto reject; + + +accept: + if (rt != net->routes) + *rt_free = rt; + return rt; + +reject: + if (rt != net->routes) + rte_free(rt); + return NULL; +} + static int krt_same_dest(rte *k, rte *e) { @@ -620,7 +658,6 @@ krt_same_dest(rte *k, rte *e) void krt_got_route(struct krt_proto *p, rte *e) { - rte *old; net *net = e->net; int verdict; @@ -663,15 +700,26 @@ krt_got_route(struct krt_proto *p, rte *e) goto sentenced; } - old = net->routes; - if ((net->n.flags & KRF_INSTALLED) && rte_is_valid(old)) + if (net->n.flags & KRF_INSTALLED) { - /* 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)) + rte *new, *rt_free; + ea_list *tmpa; + + new = krt_export_net(p, net, &rt_free, &tmpa); + + /* TODO: There also may be changes in route eattrs, we ignore that for now. */ + + if (!new) + verdict = KRF_DELETE; + else if ((net->n.flags & KRF_SYNC_ERROR) || !krt_same_dest(e, new)) verdict = KRF_UPDATE; else verdict = KRF_SEEN; + + if (rt_free) + rte_free(rt_free); + + lp_flush(krt_filter_lp); } else verdict = KRF_DELETE; @@ -692,25 +740,6 @@ 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.main_ahook->out_filter; - - if (! *new) - return 0; - - if (filter == FILTER_REJECT) - return 0; - - if (filter == FILTER_ACCEPT) - return 1; - - struct proto *src = (*new)->attrs->src->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) { @@ -721,7 +750,7 @@ krt_prune(struct krt_proto *p) { net *n = (net *) f; int verdict = f->flags & KRF_VERDICT_MASK; - rte *new, *new0, *old; + rte *new, *old, *rt_free = NULL; ea_list *tmpa = NULL; if (verdict == KRF_UPDATE || verdict == KRF_DELETE) @@ -733,23 +762,18 @@ krt_prune(struct krt_proto *p) else old = NULL; - 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; - } + new = krt_export_net(p, n, &rt_free, &tmpa); + + if (!new) + verdict = (verdict == KRF_CREATE) ? KRF_IGNORE : KRF_DELETE; else - { - ea_list **x = &tmpa; - while (*x) - x = &((*x)->next); - *x = new ? new->attrs->eattrs : NULL; - } + tmpa = ea_append(tmpa, new->attrs->eattrs); } + else + new = NULL; switch (verdict) { @@ -778,8 +802,8 @@ krt_prune(struct krt_proto *p) if (old) rte_free(old); - if (new != new0) - rte_free(new); + if (rt_free) + rte_free(rt_free); lp_flush(krt_filter_lp); f->flags &= ~KRF_VERDICT_MASK; } @@ -974,7 +998,8 @@ krt_import_control(struct proto *P, rte **new, ea_list **attrs, struct linpool * * We will remove KRT_INSTALLED flag, which stops such withdraw to be * processed in krt_rt_notify() and krt_replace_rte(). */ - e->net->n.flags &= ~KRF_INSTALLED; + if (e == e->net->routes) + e->net->n.flags &= ~KRF_INSTALLED; #endif return -1; } |