Skip to content

Commit 589b474

Browse files
Florian Westphalummakynes
authored andcommitted
netfilter: nf_flow_table: fix offload for flows that are subject to xfrm
This makes the previously added 'encap test' pass. Because its possible that the xfrm dst entry becomes stale while such a flow is offloaded, we need to call dst_check() -- the notifier that handles this for non-tunneled traffic isn't sufficient, because SA or or policies might have changed. If dst becomes stale the flow offload entry will be tagged for teardown and packets will be passed to 'classic' forwarding path. Removing the entry right away is problematic, as this would introduce a race condition with the gc worker. In case flow is long-lived, it could eventually be offloaded again once the gc worker removes the entry from the flow table. Signed-off-by: Florian Westphal <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent 0ca1bbb commit 589b474

File tree

1 file changed

+43
-0
lines changed

1 file changed

+43
-0
lines changed

net/netfilter/nf_flow_table_ip.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,25 @@ static bool nf_flow_exceeds_mtu(const struct sk_buff *skb, unsigned int mtu)
214214
return true;
215215
}
216216

217+
static int nf_flow_offload_dst_check(struct dst_entry *dst)
218+
{
219+
if (unlikely(dst_xfrm(dst)))
220+
return dst_check(dst, 0) ? 0 : -1;
221+
222+
return 0;
223+
}
224+
225+
static unsigned int nf_flow_xmit_xfrm(struct sk_buff *skb,
226+
const struct nf_hook_state *state,
227+
struct dst_entry *dst)
228+
{
229+
skb_orphan(skb);
230+
skb_dst_set_noref(skb, dst);
231+
skb->tstamp = 0;
232+
dst_output(state->net, state->sk, skb);
233+
return NF_STOLEN;
234+
}
235+
217236
unsigned int
218237
nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
219238
const struct nf_hook_state *state)
@@ -254,13 +273,25 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
254273
if (nf_flow_state_check(flow, ip_hdr(skb)->protocol, skb, thoff))
255274
return NF_ACCEPT;
256275

276+
if (nf_flow_offload_dst_check(&rt->dst)) {
277+
flow_offload_teardown(flow);
278+
return NF_ACCEPT;
279+
}
280+
257281
if (nf_flow_nat_ip(flow, skb, thoff, dir) < 0)
258282
return NF_DROP;
259283

260284
flow->timeout = (u32)jiffies + NF_FLOW_TIMEOUT;
261285
iph = ip_hdr(skb);
262286
ip_decrease_ttl(iph);
263287

288+
if (unlikely(dst_xfrm(&rt->dst))) {
289+
memset(skb->cb, 0, sizeof(struct inet_skb_parm));
290+
IPCB(skb)->iif = skb->dev->ifindex;
291+
IPCB(skb)->flags = IPSKB_FORWARDED;
292+
return nf_flow_xmit_xfrm(skb, state, &rt->dst);
293+
}
294+
264295
skb->dev = outdev;
265296
nexthop = rt_nexthop(rt, flow->tuplehash[!dir].tuple.src_v4.s_addr);
266297
skb_dst_set_noref(skb, &rt->dst);
@@ -467,6 +498,11 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
467498
sizeof(*ip6h)))
468499
return NF_ACCEPT;
469500

501+
if (nf_flow_offload_dst_check(&rt->dst)) {
502+
flow_offload_teardown(flow);
503+
return NF_ACCEPT;
504+
}
505+
470506
if (skb_try_make_writable(skb, sizeof(*ip6h)))
471507
return NF_DROP;
472508

@@ -477,6 +513,13 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
477513
ip6h = ipv6_hdr(skb);
478514
ip6h->hop_limit--;
479515

516+
if (unlikely(dst_xfrm(&rt->dst))) {
517+
memset(skb->cb, 0, sizeof(struct inet6_skb_parm));
518+
IP6CB(skb)->iif = skb->dev->ifindex;
519+
IP6CB(skb)->flags = IP6SKB_FORWARDED;
520+
return nf_flow_xmit_xfrm(skb, state, &rt->dst);
521+
}
522+
480523
skb->dev = outdev;
481524
nexthop = rt6_nexthop(rt, &flow->tuplehash[!dir].tuple.src_v6);
482525
skb_dst_set_noref(skb, &rt->dst);

0 commit comments

Comments
 (0)