1 From: Pablo Neira Ayuso <pablo@netfilter.org>
2 Date: Wed, 24 Mar 2021 02:30:39 +0100
3 Subject: [PATCH] netfilter: flowtable: use dev_fill_forward_path() to
6 Obtain the ingress device in the tuple from the route in the reply
7 direction. Use dev_fill_forward_path() instead to get the real ingress
10 Fall back to use the ingress device that the IP forwarding route
13 - dev_fill_forward_path() finds no real ingress device.
14 - the ingress device that is obtained is not part of the flowtable
16 - this route has a xfrm policy.
18 Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
21 --- a/include/net/netfilter/nf_flow_table.h
22 +++ b/include/net/netfilter/nf_flow_table.h
23 @@ -164,6 +164,9 @@ static inline __s32 nf_flow_timeout_delt
24 struct nf_flow_route {
26 struct dst_entry *dst;
30 enum flow_offload_xmit_type xmit_type;
31 } tuple[FLOW_OFFLOAD_DIR_MAX];
33 --- a/net/netfilter/nf_flow_table_core.c
34 +++ b/net/netfilter/nf_flow_table_core.c
35 @@ -79,7 +79,6 @@ static int flow_offload_fill_route(struc
36 enum flow_offload_tuple_dir dir)
38 struct flow_offload_tuple *flow_tuple = &flow->tuplehash[dir].tuple;
39 - struct dst_entry *other_dst = route->tuple[!dir].dst;
40 struct dst_entry *dst = route->tuple[dir].dst;
42 if (!dst_hold_safe(route->tuple[dir].dst))
43 @@ -94,7 +93,7 @@ static int flow_offload_fill_route(struc
47 - flow_tuple->iifidx = other_dst->dev->ifindex;
48 + flow_tuple->iifidx = route->tuple[dir].in.ifindex;
49 flow_tuple->xmit_type = route->tuple[dir].xmit_type;
50 flow_tuple->dst_cache = dst;
52 --- a/net/netfilter/nft_flow_offload.c
53 +++ b/net/netfilter/nft_flow_offload.c
54 @@ -31,14 +31,104 @@ static void nft_default_forward_path(str
55 struct dst_entry *dst_cache,
56 enum ip_conntrack_dir dir)
58 + route->tuple[!dir].in.ifindex = dst_cache->dev->ifindex;
59 route->tuple[dir].dst = dst_cache;
60 route->tuple[dir].xmit_type = nft_xmit_type(dst_cache);
63 +static int nft_dev_fill_forward_path(const struct nf_flow_route *route,
64 + const struct dst_entry *dst_cache,
65 + const struct nf_conn *ct,
66 + enum ip_conntrack_dir dir,
67 + struct net_device_path_stack *stack)
69 + const void *daddr = &ct->tuplehash[!dir].tuple.src.u3;
70 + struct net_device *dev = dst_cache->dev;
71 + unsigned char ha[ETH_ALEN];
72 + struct neighbour *n;
75 + n = dst_neigh_lookup(dst_cache, daddr);
79 + read_lock_bh(&n->lock);
80 + nud_state = n->nud_state;
81 + ether_addr_copy(ha, n->ha);
82 + read_unlock_bh(&n->lock);
85 + if (!(nud_state & NUD_VALID))
88 + return dev_fill_forward_path(dev, ha, stack);
91 +struct nft_forward_info {
92 + const struct net_device *indev;
95 +static void nft_dev_path_info(const struct net_device_path_stack *stack,
96 + struct nft_forward_info *info)
98 + const struct net_device_path *path;
101 + for (i = 0; i < stack->num_paths; i++) {
102 + path = &stack->path[i];
103 + switch (path->type) {
104 + case DEV_PATH_ETHERNET:
105 + info->indev = path->dev;
107 + case DEV_PATH_VLAN:
108 + case DEV_PATH_BRIDGE:
110 + info->indev = NULL;
116 +static bool nft_flowtable_find_dev(const struct net_device *dev,
117 + struct nft_flowtable *ft)
119 + struct nft_hook *hook;
120 + bool found = false;
122 + list_for_each_entry_rcu(hook, &ft->hook_list, list) {
123 + if (hook->ops.dev != dev)
133 +static void nft_dev_forward_path(struct nf_flow_route *route,
134 + const struct nf_conn *ct,
135 + enum ip_conntrack_dir dir,
136 + struct nft_flowtable *ft)
138 + const struct dst_entry *dst = route->tuple[dir].dst;
139 + struct net_device_path_stack stack;
140 + struct nft_forward_info info = {};
142 + if (nft_dev_fill_forward_path(route, dst, ct, dir, &stack) >= 0)
143 + nft_dev_path_info(&stack, &info);
145 + if (!info.indev || !nft_flowtable_find_dev(info.indev, ft))
148 + route->tuple[!dir].in.ifindex = info.indev->ifindex;
151 static int nft_flow_route(const struct nft_pktinfo *pkt,
152 const struct nf_conn *ct,
153 struct nf_flow_route *route,
154 - enum ip_conntrack_dir dir)
155 + enum ip_conntrack_dir dir,
156 + struct nft_flowtable *ft)
158 struct dst_entry *this_dst = skb_dst(pkt->skb);
159 struct dst_entry *other_dst = NULL;
160 @@ -63,6 +153,12 @@ static int nft_flow_route(const struct n
161 nft_default_forward_path(route, this_dst, dir);
162 nft_default_forward_path(route, other_dst, !dir);
164 + if (route->tuple[dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH &&
165 + route->tuple[!dir].xmit_type == FLOW_OFFLOAD_XMIT_NEIGH) {
166 + nft_dev_forward_path(route, ct, dir, ft);
167 + nft_dev_forward_path(route, ct, !dir, ft);
173 @@ -90,8 +186,8 @@ static void nft_flow_offload_eval(const
174 struct nft_flow_offload *priv = nft_expr_priv(expr);
175 struct nf_flowtable *flowtable = &priv->flowtable->data;
176 struct tcphdr _tcph, *tcph = NULL;
177 + struct nf_flow_route route = {};
178 enum ip_conntrack_info ctinfo;
179 - struct nf_flow_route route;
180 struct flow_offload *flow;
181 enum ip_conntrack_dir dir;
183 @@ -128,7 +224,7 @@ static void nft_flow_offload_eval(const
186 dir = CTINFO2DIR(ctinfo);
187 - if (nft_flow_route(pkt, ct, &route, dir) < 0)
188 + if (nft_flow_route(pkt, ct, &route, dir, priv->flowtable) < 0)
191 flow = flow_offload_alloc(ct);