netfilter: flowtable: move dst_check to packet path
authorRitaro Takenaka <ritarot634@gmail.com>
Tue, 17 May 2022 10:55:30 +0000 (12:55 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Wed, 18 May 2022 15:34:26 +0000 (17:34 +0200)
Fixes sporadic IPv6 packet loss when flow offloading is enabled.

IPv6 route GC and flowtable GC are not synchronized.
When dst_cache becomes stale and a packet passes through the flow before
the flowtable GC teardowns it, the packet can be dropped.
So, it is necessary to check dst every time in packet path.

Fixes: 227e1e4d0d6c ("netfilter: nf_flowtable: skip device lookup from interface index")
Signed-off-by: Ritaro Takenaka <ritarot634@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
net/netfilter/nf_flow_table_core.c
net/netfilter/nf_flow_table_ip.c

index ebdf533..f2def06 100644 (file)
@@ -421,32 +421,11 @@ nf_flow_table_iterate(struct nf_flowtable *flow_table,
        return err;
 }
 
-static bool flow_offload_stale_dst(struct flow_offload_tuple *tuple)
-{
-       struct dst_entry *dst;
-
-       if (tuple->xmit_type == FLOW_OFFLOAD_XMIT_NEIGH ||
-           tuple->xmit_type == FLOW_OFFLOAD_XMIT_XFRM) {
-               dst = tuple->dst_cache;
-               if (!dst_check(dst, tuple->dst_cookie))
-                       return true;
-       }
-
-       return false;
-}
-
-static bool nf_flow_has_stale_dst(struct flow_offload *flow)
-{
-       return flow_offload_stale_dst(&flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple) ||
-              flow_offload_stale_dst(&flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple);
-}
-
 static void nf_flow_offload_gc_step(struct nf_flowtable *flow_table,
                                    struct flow_offload *flow, void *data)
 {
        if (nf_flow_has_expired(flow) ||
-           nf_ct_is_dying(flow->ct) ||
-           nf_flow_has_stale_dst(flow))
+           nf_ct_is_dying(flow->ct))
                flow_offload_teardown(flow);
 
        if (test_bit(NF_FLOW_TEARDOWN, &flow->flags)) {
index 32c0eb1..b350fe9 100644 (file)
@@ -248,6 +248,15 @@ static bool nf_flow_exceeds_mtu(const struct sk_buff *skb, unsigned int mtu)
        return true;
 }
 
+static inline bool nf_flow_dst_check(struct flow_offload_tuple *tuple)
+{
+       if (tuple->xmit_type != FLOW_OFFLOAD_XMIT_NEIGH &&
+           tuple->xmit_type != FLOW_OFFLOAD_XMIT_XFRM)
+               return true;
+
+       return dst_check(tuple->dst_cache, tuple->dst_cookie);
+}
+
 static unsigned int nf_flow_xmit_xfrm(struct sk_buff *skb,
                                      const struct nf_hook_state *state,
                                      struct dst_entry *dst)
@@ -367,6 +376,11 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
        if (nf_flow_state_check(flow, iph->protocol, skb, thoff))
                return NF_ACCEPT;
 
+       if (!nf_flow_dst_check(&tuplehash->tuple)) {
+               flow_offload_teardown(flow);
+               return NF_ACCEPT;
+       }
+
        if (skb_try_make_writable(skb, thoff + hdrsize))
                return NF_DROP;
 
@@ -624,6 +638,11 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
        if (nf_flow_state_check(flow, ip6h->nexthdr, skb, thoff))
                return NF_ACCEPT;
 
+       if (!nf_flow_dst_check(&tuplehash->tuple)) {
+               flow_offload_teardown(flow);
+               return NF_ACCEPT;
+       }
+
        if (skb_try_make_writable(skb, thoff + hdrsize))
                return NF_DROP;