summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sysdep/linux/netlink.c5
-rw-r--r--sysdep/unix/krt.c107
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;
}