The ingress device in the tuple is obtained from route in the reply direction. Use dev_fill_forward_path() instead to provide the real ingress device for this flow. Signed-off-by: Pablo Neira Ayuso <pablo@xxxxxxxxxxxxx> --- include/net/netfilter/nf_flow_table.h | 6 ++- net/netfilter/nf_flow_table_core.c | 3 +- net/netfilter/nft_flow_offload.c | 75 ++++++++++++++++++++++++++- 3 files changed, 80 insertions(+), 4 deletions(-) diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h index 08e779f149ee..ecb84d4358cc 100644 --- a/include/net/netfilter/nf_flow_table.h +++ b/include/net/netfilter/nf_flow_table.h @@ -112,9 +112,10 @@ struct flow_offload_tuple { u8 l3proto; u8 l4proto; + + /* All members above are keys for lookups, see flow_offload_hash(). */ u8 dir; enum flow_offload_xmit_type xmit_type:8; - u16 mtu; struct dst_entry *dst_cache; @@ -160,6 +161,9 @@ static inline __s32 nf_flow_timeout_delta(unsigned int timeout) struct nf_flow_route { struct { + struct { + u32 ifindex; + } in; struct dst_entry *dst; } tuple[FLOW_OFFLOAD_DIR_MAX]; }; diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c index 97f04f244961..66abc7f287a3 100644 --- a/net/netfilter/nf_flow_table_core.c +++ b/net/netfilter/nf_flow_table_core.c @@ -79,7 +79,6 @@ static int flow_offload_fill_route(struct flow_offload *flow, enum flow_offload_tuple_dir dir) { struct flow_offload_tuple *flow_tuple = &flow->tuplehash[dir].tuple; - struct dst_entry *other_dst = route->tuple[!dir].dst; struct dst_entry *dst = route->tuple[dir].dst; if (!dst_hold_safe(route->tuple[dir].dst)) @@ -94,7 +93,7 @@ static int flow_offload_fill_route(struct flow_offload *flow, break; } - flow_tuple->iifidx = other_dst->dev->ifindex; + flow_tuple->iifidx = route->tuple[dir].in.ifindex; if (dst_xfrm(dst)) flow_tuple->xmit_type = FLOW_OFFLOAD_XMIT_XFRM; diff --git a/net/netfilter/nft_flow_offload.c b/net/netfilter/nft_flow_offload.c index 3a6c84fb2c90..4b476b0a3c88 100644 --- a/net/netfilter/nft_flow_offload.c +++ b/net/netfilter/nft_flow_offload.c @@ -19,6 +19,75 @@ struct nft_flow_offload { struct nft_flowtable *flowtable; }; +static int nft_dev_fill_forward_path(const struct nf_flow_route *route, + const struct nf_conn *ct, + enum ip_conntrack_dir dir, + struct net_device_path_stack *stack) +{ + const struct dst_entry *dst_cache = route->tuple[dir].dst; + const void *daddr = &ct->tuplehash[!dir].tuple.src.u3; + unsigned char ha[ETH_ALEN]; + struct net_device *dev; + struct neighbour *n; + struct rtable *rt; + u8 nud_state; + + rt = (struct rtable *)dst_cache; + dev = rt->dst.dev; + + n = dst_neigh_lookup(dst_cache, daddr); + if (!n) + return -1; + + read_lock_bh(&n->lock); + nud_state = n->nud_state; + ether_addr_copy(ha, n->ha); + read_unlock_bh(&n->lock); + neigh_release(n); + + if (!(nud_state & NUD_VALID)) + return -1; + + return dev_fill_forward_path(dev, ha, stack); +} + +struct nft_forward_info { + u32 iifindex; +}; + +static int nft_dev_forward_path(struct nf_flow_route *route, + const struct nf_conn *ct, + enum ip_conntrack_dir dir) +{ + struct net_device_path_stack stack = {}; + struct nft_forward_info info = {}; + struct net_device_path *path; + int i, ret; + + memset(&stack, 0, sizeof(stack)); + + ret = nft_dev_fill_forward_path(route, ct, dir, &stack); + if (ret < 0) + return -1; + + for (i = stack.num_paths - 1; i >= 0; i--) { + path = &stack.path[i]; + switch (path->type) { + case DEV_PATH_ETHERNET: + info.iifindex = path->dev->ifindex; + break; + case DEV_PATH_VLAN: + return -1; + case DEV_PATH_BRIDGE: + return -1; + } + } + + route->tuple[!dir].in.ifindex = info.iifindex; + + return 0; +} + static int nft_flow_route(const struct nft_pktinfo *pkt, const struct nf_conn *ct, struct nf_flow_route *route, @@ -47,6 +116,10 @@ static int nft_flow_route(const struct nft_pktinfo *pkt, route->tuple[dir].dst = this_dst; route->tuple[!dir].dst = other_dst; + if (nft_dev_forward_path(route, ct, IP_CT_DIR_ORIGINAL) < 0 || + nft_dev_forward_path(route, ct, IP_CT_DIR_REPLY) < 0) + return -ENOENT; + return 0; } @@ -74,8 +147,8 @@ static void nft_flow_offload_eval(const struct nft_expr *expr, struct nft_flow_offload *priv = nft_expr_priv(expr); struct nf_flowtable *flowtable = &priv->flowtable->data; struct tcphdr _tcph, *tcph = NULL; + struct nf_flow_route route = {}; enum ip_conntrack_info ctinfo; - struct nf_flow_route route; struct flow_offload *flow; enum ip_conntrack_dir dir; struct nf_conn *ct; -- 2.20.1