53118939a3def0831bda1c1ebedc5b4de10baf8d
[openwrt/staging/neocturne.git] /
1 From 2738d9d963bd1f06d5114c2b4fa5771a95703991 Mon Sep 17 00:00:00 2001
2 From: Ritaro Takenaka <ritarot634@gmail.com>
3 Date: Tue, 17 May 2022 12:55:30 +0200
4 Subject: [PATCH] netfilter: flowtable: move dst_check to packet path
5
6 Fixes sporadic IPv6 packet loss when flow offloading is enabled.
7
8 IPv6 route GC and flowtable GC are not synchronized.
9 When dst_cache becomes stale and a packet passes through the flow before
10 the flowtable GC teardowns it, the packet can be dropped.
11 So, it is necessary to check dst every time in packet path.
12
13 Fixes: 227e1e4d0d6c ("netfilter: nf_flowtable: skip device lookup from interface index")
14 Signed-off-by: Ritaro Takenaka <ritarot634@gmail.com>
15 Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
16 ---
17 net/netfilter/nf_flow_table_core.c | 23 +----------------------
18 net/netfilter/nf_flow_table_ip.c | 19 +++++++++++++++++++
19 2 files changed, 20 insertions(+), 22 deletions(-)
20
21 --- a/net/netfilter/nf_flow_table_core.c
22 +++ b/net/netfilter/nf_flow_table_core.c
23 @@ -436,33 +436,12 @@ nf_flow_table_iterate(struct nf_flowtabl
24 return err;
25 }
26
27 -static bool flow_offload_stale_dst(struct flow_offload_tuple *tuple)
28 -{
29 - struct dst_entry *dst;
30 -
31 - if (tuple->xmit_type == FLOW_OFFLOAD_XMIT_NEIGH ||
32 - tuple->xmit_type == FLOW_OFFLOAD_XMIT_XFRM) {
33 - dst = tuple->dst_cache;
34 - if (!dst_check(dst, tuple->dst_cookie))
35 - return true;
36 - }
37 -
38 - return false;
39 -}
40 -
41 -static bool nf_flow_has_stale_dst(struct flow_offload *flow)
42 -{
43 - return flow_offload_stale_dst(&flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple) ||
44 - flow_offload_stale_dst(&flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple);
45 -}
46 -
47 static void nf_flow_offload_gc_step(struct flow_offload *flow, void *data)
48 {
49 struct nf_flowtable *flow_table = data;
50
51 if (nf_flow_has_expired(flow) ||
52 - nf_ct_is_dying(flow->ct) ||
53 - nf_flow_has_stale_dst(flow))
54 + nf_ct_is_dying(flow->ct))
55 set_bit(NF_FLOW_TEARDOWN, &flow->flags);
56
57 if (test_bit(NF_FLOW_TEARDOWN, &flow->flags)) {
58 --- a/net/netfilter/nf_flow_table_ip.c
59 +++ b/net/netfilter/nf_flow_table_ip.c
60 @@ -229,6 +229,15 @@ static bool nf_flow_exceeds_mtu(const st
61 return true;
62 }
63
64 +static inline bool nf_flow_dst_check(struct flow_offload_tuple *tuple)
65 +{
66 + if (tuple->xmit_type != FLOW_OFFLOAD_XMIT_NEIGH &&
67 + tuple->xmit_type != FLOW_OFFLOAD_XMIT_XFRM)
68 + return true;
69 +
70 + return dst_check(tuple->dst_cache, tuple->dst_cookie);
71 +}
72 +
73 static unsigned int nf_flow_xmit_xfrm(struct sk_buff *skb,
74 const struct nf_hook_state *state,
75 struct dst_entry *dst)
76 @@ -364,6 +373,11 @@ nf_flow_offload_ip_hook(void *priv, stru
77 if (nf_flow_state_check(flow, iph->protocol, skb, thoff))
78 return NF_ACCEPT;
79
80 + if (!nf_flow_dst_check(&tuplehash->tuple)) {
81 + flow_offload_teardown(flow);
82 + return NF_ACCEPT;
83 + }
84 +
85 if (skb_try_make_writable(skb, thoff + hdrsize))
86 return NF_DROP;
87
88 @@ -600,6 +614,11 @@ nf_flow_offload_ipv6_hook(void *priv, st
89 if (nf_flow_state_check(flow, ip6h->nexthdr, skb, thoff))
90 return NF_ACCEPT;
91
92 + if (!nf_flow_dst_check(&tuplehash->tuple)) {
93 + flow_offload_teardown(flow);
94 + return NF_ACCEPT;
95 + }
96 +
97 if (skb_try_make_writable(skb, thoff + hdrsize))
98 return NF_DROP;
99