@@ -117,6 +117,7 @@ struct flow_offload_tuple {
u8 dir;
enum flow_offload_xmit_type xmit_type:8;
u16 mtu;
+ u32 oifidx;
struct dst_entry *dst_cache;
};
@@ -164,6 +165,9 @@ struct nf_flow_route {
struct {
u32 ifindex;
} in;
+ struct {
+ u32 ifindex;
+ } out;
struct dst_entry *dst;
} tuple[FLOW_OFFLOAD_DIR_MAX];
};
@@ -94,6 +94,7 @@ static int flow_offload_fill_route(struct flow_offload *flow,
}
flow_tuple->iifidx = route->tuple[dir].in.ifindex;
+ flow_tuple->oifidx = route->tuple[dir].out.ifindex;
if (dst_xfrm(dst))
flow_tuple->xmit_type = FLOW_OFFLOAD_XMIT_XFRM;
@@ -228,6 +228,15 @@ static int nf_flow_offload_dst_check(struct dst_entry *dst)
return 0;
}
+static struct net_device *nf_flow_outdev_lookup(struct net *net, int oifidx,
+ struct net_device *dev)
+{
+ if (oifidx == dev->ifindex)
+ return dev;
+
+ return dev_get_by_index_rcu(net, oifidx);
+}
+
static unsigned int nf_flow_xmit_xfrm(struct sk_buff *skb,
const struct nf_hook_state *state,
struct dst_entry *dst)
@@ -267,7 +276,6 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
dir = tuplehash->tuple.dir;
flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
rt = (struct rtable *)flow->tuplehash[dir].tuple.dst_cache;
- outdev = rt->dst.dev;
if (unlikely(nf_flow_exceeds_mtu(skb, flow->tuplehash[dir].tuple.mtu)))
return NF_ACCEPT;
@@ -286,6 +294,13 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
return NF_ACCEPT;
}
+ outdev = nf_flow_outdev_lookup(state->net, tuplehash->tuple.oifidx,
+ rt->dst.dev);
+ if (!outdev) {
+ flow_offload_teardown(flow);
+ return NF_ACCEPT;
+ }
+
if (nf_flow_nat_ip(flow, skb, thoff, dir) < 0)
return NF_DROP;
@@ -517,7 +532,6 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
dir = tuplehash->tuple.dir;
flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
rt = (struct rt6_info *)flow->tuplehash[dir].tuple.dst_cache;
- outdev = rt->dst.dev;
if (unlikely(nf_flow_exceeds_mtu(skb, flow->tuplehash[dir].tuple.mtu)))
return NF_ACCEPT;
@@ -533,6 +547,13 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
return NF_ACCEPT;
}
+ outdev = nf_flow_outdev_lookup(state->net, tuplehash->tuple.oifidx,
+ rt->dst.dev);
+ if (!outdev) {
+ flow_offload_teardown(flow);
+ return NF_ACCEPT;
+ }
+
if (skb_try_make_writable(skb, sizeof(*ip6h)))
return NF_DROP;
@@ -84,6 +84,7 @@ static int nft_dev_forward_path(struct nf_flow_route *route,
}
route->tuple[!dir].in.ifindex = info.iifindex;
+ route->tuple[dir].out.ifindex = info.iifindex;
return 0;
}
The egress device in the tuple is obtained from route. Use dev_fill_forward_path() instead to provide the real ingress device for this flow whenever this is available. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> --- include/net/netfilter/nf_flow_table.h | 4 ++++ net/netfilter/nf_flow_table_core.c | 1 + net/netfilter/nf_flow_table_ip.c | 25 +++++++++++++++++++++++-- net/netfilter/nft_flow_offload.c | 1 + 4 files changed, 29 insertions(+), 2 deletions(-)