@@ -164,6 +164,8 @@ enum nf_flow_flags {
NF_FLOW_HW_DYING,
NF_FLOW_HW_DEAD,
NF_FLOW_HW_PENDING,
+ NF_FLOW_FIN_ORIGINAL,
+ NF_FLOW_FIN_REPLY,
};
enum flow_offload_type {
@@ -19,7 +19,8 @@
#include <linux/udp.h>
static int nf_flow_state_check(struct flow_offload *flow, int proto,
- struct sk_buff *skb, unsigned int thoff)
+ struct sk_buff *skb, unsigned int thoff,
+ enum flow_offload_tuple_dir dir)
{
struct tcphdr *tcph;
@@ -27,9 +28,19 @@ static int nf_flow_state_check(struct flow_offload *flow, int proto,
return 0;
tcph = (void *)(skb_network_header(skb) + thoff);
- if (unlikely(tcph->fin || tcph->rst)) {
+ if (unlikely(tcph->rst)) {
flow_offload_teardown(flow);
return -1;
+ } else if (unlikely(tcph->fin)) {
+ if (dir == FLOW_OFFLOAD_DIR_ORIGINAL)
+ set_bit(NF_FLOW_FIN_ORIGINAL, &flow->flags);
+ else if (dir == FLOW_OFFLOAD_DIR_REPLY)
+ set_bit(NF_FLOW_FIN_REPLY, &flow->flags);
+
+ if (test_bit(NF_FLOW_FIN_ORIGINAL, &flow->flags) &&
+ test_bit(NF_FLOW_FIN_REPLY, &flow->flags))
+ flow_offload_teardown(flow);
+ return -1;
}
return 0;
@@ -373,7 +384,7 @@ static unsigned int nf_flow_queue_xmit(struct net *net, struct sk_buff *skb,
iph = (struct iphdr *)(skb_network_header(skb) + offset);
thoff = (iph->ihl * 4) + offset;
- if (nf_flow_state_check(flow, iph->protocol, skb, thoff))
+ if (nf_flow_state_check(flow, iph->protocol, skb, thoff, dir))
return NF_ACCEPT;
if (!nf_flow_dst_check(&tuplehash->tuple)) {
@@ -635,7 +646,7 @@ static int nf_flow_tuple_ipv6(struct sk_buff *skb, const struct net_device *dev,
ip6h = (struct ipv6hdr *)(skb_network_header(skb) + offset);
thoff = sizeof(*ip6h) + offset;
- if (nf_flow_state_check(flow, ip6h->nexthdr, skb, thoff))
+ if (nf_flow_state_check(flow, ip6h->nexthdr, skb, thoff, dir))
return NF_ACCEPT;
if (!nf_flow_dst_check(&tuplehash->tuple)) {