Commit 8b9229d1 authored by Pablo Neira Ayuso's avatar Pablo Neira Ayuso

netfilter: flowtable: dst_check() from garbage collector path

Move dst_check() to the garbage collector path. Stale routes trigger the
flow entry teardown state which makes affected flows go back to the
classic forwarding path to re-evaluate flow offloading.

IPv6 requires the dst cookie to work, store it in the flow_tuple,
otherwise dst_check() always fails.

Fixes: e5075c0b ("netfilter: flowtable: call dst_check() to fall back to classic forwarding")
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 5c701e71
...@@ -129,7 +129,10 @@ struct flow_offload_tuple { ...@@ -129,7 +129,10 @@ struct flow_offload_tuple {
in_vlan_ingress:2; in_vlan_ingress:2;
u16 mtu; u16 mtu;
union { union {
struct dst_entry *dst_cache; struct {
struct dst_entry *dst_cache;
u32 dst_cookie;
};
struct { struct {
u32 ifidx; u32 ifidx;
u32 hw_ifidx; u32 hw_ifidx;
......
...@@ -74,6 +74,18 @@ struct flow_offload *flow_offload_alloc(struct nf_conn *ct) ...@@ -74,6 +74,18 @@ struct flow_offload *flow_offload_alloc(struct nf_conn *ct)
} }
EXPORT_SYMBOL_GPL(flow_offload_alloc); EXPORT_SYMBOL_GPL(flow_offload_alloc);
static u32 flow_offload_dst_cookie(struct flow_offload_tuple *flow_tuple)
{
const struct rt6_info *rt;
if (flow_tuple->l3proto == NFPROTO_IPV6) {
rt = (const struct rt6_info *)flow_tuple->dst_cache;
return rt6_get_cookie(rt);
}
return 0;
}
static int flow_offload_fill_route(struct flow_offload *flow, static int flow_offload_fill_route(struct flow_offload *flow,
const struct nf_flow_route *route, const struct nf_flow_route *route,
enum flow_offload_tuple_dir dir) enum flow_offload_tuple_dir dir)
...@@ -116,6 +128,7 @@ static int flow_offload_fill_route(struct flow_offload *flow, ...@@ -116,6 +128,7 @@ static int flow_offload_fill_route(struct flow_offload *flow,
return -1; return -1;
flow_tuple->dst_cache = dst; flow_tuple->dst_cache = dst;
flow_tuple->dst_cookie = flow_offload_dst_cookie(flow_tuple);
break; break;
} }
flow_tuple->xmit_type = route->tuple[dir].xmit_type; flow_tuple->xmit_type = route->tuple[dir].xmit_type;
...@@ -390,11 +403,33 @@ nf_flow_table_iterate(struct nf_flowtable *flow_table, ...@@ -390,11 +403,33 @@ nf_flow_table_iterate(struct nf_flowtable *flow_table,
return err; return err;
} }
static bool flow_offload_stale_dst(struct flow_offload_tuple *tuple)
{
struct dst_entry *dst;
if (tuple->xmit_type == FLOW_OFFLOAD_XMIT_NEIGH ||
tuple->xmit_type == FLOW_OFFLOAD_XMIT_XFRM) {
dst = tuple->dst_cache;
if (!dst_check(dst, tuple->dst_cookie))
return true;
}
return false;
}
static bool nf_flow_has_stale_dst(struct flow_offload *flow)
{
return flow_offload_stale_dst(&flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple) ||
flow_offload_stale_dst(&flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple);
}
static void nf_flow_offload_gc_step(struct flow_offload *flow, void *data) static void nf_flow_offload_gc_step(struct flow_offload *flow, void *data)
{ {
struct nf_flowtable *flow_table = data; struct nf_flowtable *flow_table = data;
if (nf_flow_has_expired(flow) || nf_ct_is_dying(flow->ct)) if (nf_flow_has_expired(flow) ||
nf_ct_is_dying(flow->ct) ||
nf_flow_has_stale_dst(flow))
set_bit(NF_FLOW_TEARDOWN, &flow->flags); set_bit(NF_FLOW_TEARDOWN, &flow->flags);
if (test_bit(NF_FLOW_TEARDOWN, &flow->flags)) { if (test_bit(NF_FLOW_TEARDOWN, &flow->flags)) {
......
...@@ -364,15 +364,6 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb, ...@@ -364,15 +364,6 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
if (nf_flow_state_check(flow, iph->protocol, skb, thoff)) if (nf_flow_state_check(flow, iph->protocol, skb, thoff))
return NF_ACCEPT; return NF_ACCEPT;
if (tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_NEIGH ||
tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM) {
rt = (struct rtable *)tuplehash->tuple.dst_cache;
if (!dst_check(&rt->dst, 0)) {
flow_offload_teardown(flow);
return NF_ACCEPT;
}
}
if (skb_try_make_writable(skb, thoff + hdrsize)) if (skb_try_make_writable(skb, thoff + hdrsize))
return NF_DROP; return NF_DROP;
...@@ -391,6 +382,7 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb, ...@@ -391,6 +382,7 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
nf_ct_acct_update(flow->ct, tuplehash->tuple.dir, skb->len); nf_ct_acct_update(flow->ct, tuplehash->tuple.dir, skb->len);
if (unlikely(tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)) { if (unlikely(tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)) {
rt = (struct rtable *)tuplehash->tuple.dst_cache;
memset(skb->cb, 0, sizeof(struct inet_skb_parm)); memset(skb->cb, 0, sizeof(struct inet_skb_parm));
IPCB(skb)->iif = skb->dev->ifindex; IPCB(skb)->iif = skb->dev->ifindex;
IPCB(skb)->flags = IPSKB_FORWARDED; IPCB(skb)->flags = IPSKB_FORWARDED;
...@@ -399,6 +391,7 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb, ...@@ -399,6 +391,7 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
switch (tuplehash->tuple.xmit_type) { switch (tuplehash->tuple.xmit_type) {
case FLOW_OFFLOAD_XMIT_NEIGH: case FLOW_OFFLOAD_XMIT_NEIGH:
rt = (struct rtable *)tuplehash->tuple.dst_cache;
outdev = rt->dst.dev; outdev = rt->dst.dev;
skb->dev = outdev; skb->dev = outdev;
nexthop = rt_nexthop(rt, flow->tuplehash[!dir].tuple.src_v4.s_addr); nexthop = rt_nexthop(rt, flow->tuplehash[!dir].tuple.src_v4.s_addr);
...@@ -607,15 +600,6 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb, ...@@ -607,15 +600,6 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
if (nf_flow_state_check(flow, ip6h->nexthdr, skb, thoff)) if (nf_flow_state_check(flow, ip6h->nexthdr, skb, thoff))
return NF_ACCEPT; return NF_ACCEPT;
if (tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_NEIGH ||
tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM) {
rt = (struct rt6_info *)tuplehash->tuple.dst_cache;
if (!dst_check(&rt->dst, 0)) {
flow_offload_teardown(flow);
return NF_ACCEPT;
}
}
if (skb_try_make_writable(skb, thoff + hdrsize)) if (skb_try_make_writable(skb, thoff + hdrsize))
return NF_DROP; return NF_DROP;
...@@ -633,6 +617,7 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb, ...@@ -633,6 +617,7 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
nf_ct_acct_update(flow->ct, tuplehash->tuple.dir, skb->len); nf_ct_acct_update(flow->ct, tuplehash->tuple.dir, skb->len);
if (unlikely(tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)) { if (unlikely(tuplehash->tuple.xmit_type == FLOW_OFFLOAD_XMIT_XFRM)) {
rt = (struct rt6_info *)tuplehash->tuple.dst_cache;
memset(skb->cb, 0, sizeof(struct inet6_skb_parm)); memset(skb->cb, 0, sizeof(struct inet6_skb_parm));
IP6CB(skb)->iif = skb->dev->ifindex; IP6CB(skb)->iif = skb->dev->ifindex;
IP6CB(skb)->flags = IP6SKB_FORWARDED; IP6CB(skb)->flags = IP6SKB_FORWARDED;
...@@ -641,6 +626,7 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb, ...@@ -641,6 +626,7 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
switch (tuplehash->tuple.xmit_type) { switch (tuplehash->tuple.xmit_type) {
case FLOW_OFFLOAD_XMIT_NEIGH: case FLOW_OFFLOAD_XMIT_NEIGH:
rt = (struct rt6_info *)tuplehash->tuple.dst_cache;
outdev = rt->dst.dev; outdev = rt->dst.dev;
skb->dev = outdev; skb->dev = outdev;
nexthop = rt6_nexthop(rt, &flow->tuplehash[!dir].tuple.src_v6); nexthop = rt6_nexthop(rt, &flow->tuplehash[!dir].tuple.src_v6);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment