netfilter: nft_flow_offload: skip tcp rst and fin packets [Linux 4.19.72]

This Linux kernel change "netfilter: nft_flow_offload: skip tcp rst and fin packets" is included in the Linux 4.19.72 release. This change is authored by Pablo Neira Ayuso <pablo [at]> on Tue Aug 13 17:41:13 2019 +0200. The commit for this change in Linux stable tree is a54fa5d (patch) which is from upstream commit dfe42be. The same Linux upstream change may have been applied to various maintained Linux releases and you can find all Linux releases containing changes from upstream dfe42be.

netfilter: nft_flow_offload: skip tcp rst and fin packets

[ Upstream commit dfe42be15fde16232340b8b2a57c359f51cc10d9 ]

TCP rst and fin packets do not qualify to place a flow into the
flowtable. Most likely there will be no more packets after connection
closure. Without this patch, this flow entry expires and connection
tracking picks up the entry in ESTABLISHED state using the fixup
timeout, which makes this look inconsistent to the user for a connection
that is actually already closed.

Signed-off-by: Pablo Neira Ayuso <>
Signed-off-by: Sasha Levin <>

There are 9 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 net/netfilter/nft_flow_offload.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/net/netfilter/nft_flow_offload.c b/net/netfilter/nft_flow_offload.c
index 6e0c260..69decbe 100644
--- a/net/netfilter/nft_flow_offload.c
+++ b/net/netfilter/nft_flow_offload.c
@@ -71,11 +71,11 @@ static void nft_flow_offload_eval(const struct nft_expr *expr,
    struct nft_flow_offload *priv = nft_expr_priv(expr);
    struct nf_flowtable *flowtable = &priv->flowtable->data;
+   struct tcphdr _tcph, *tcph = NULL;
    enum ip_conntrack_info ctinfo;
    struct nf_flow_route route;
    struct flow_offload *flow;
    enum ip_conntrack_dir dir;
-   bool is_tcp = false;
    struct nf_conn *ct;
    int ret;

@@ -88,7 +88,10 @@ static void nft_flow_offload_eval(const struct nft_expr *expr,

    switch (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum) {
    case IPPROTO_TCP:
-       is_tcp = true;
+       tcph = skb_header_pointer(pkt->skb, pkt->xt.thoff,
+                     sizeof(_tcph), &_tcph);
+       if (unlikely(!tcph || tcph->fin || tcph->rst))
+           goto out;
    case IPPROTO_UDP:
@@ -115,7 +118,7 @@ static void nft_flow_offload_eval(const struct nft_expr *expr,
    if (!flow)
        goto err_flow_alloc;

-   if (is_tcp) {
+   if (tcph) {
        ct->proto.tcp.seen[0].flags |= IP_CT_TCP_FLAG_BE_LIBERAL;
        ct->proto.tcp.seen[1].flags |= IP_CT_TCP_FLAG_BE_LIBERAL;

Leave a Reply

Your email address will not be published. Required fields are marked *