netfilter: nf_tables: add packets conntrack state to debug trace info
authorFlorian Westphal <fw@strlen.de>
Thu, 22 May 2025 13:49:34 +0000 (15:49 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Fri, 23 May 2025 11:57:12 +0000 (13:57 +0200)
Add the minimal relevant info needed for userspace ("nftables monitor
trace") to provide the conntrack view of the packet:

- state (new, related, established)
- direction (original, reply)
- status (e.g., if connection is subject to dnat)
- id (allows to query ctnetlink for remaining conntrack state info)

Example:
trace id a62 inet filter PRE_RAW packet: iif "enp0s3" ether [..]
  [..]
trace id a62 inet filter PRE_MANGLE conntrack: ct direction original ct state new ct id 32
trace id a62 inet filter PRE_MANGLE packet: [..]
 [..]
trace id a62 inet filter IN conntrack: ct direction original ct state new ct status dnat-done ct id 32
 [..]

In this case one can see that while NAT is active, the new connection
isn't subject to a translation.

Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/uapi/linux/netfilter/nf_tables.h
net/netfilter/nf_tables_trace.c

index 7d6bc19..2beb30b 100644 (file)
@@ -1841,6 +1841,10 @@ enum nft_xfrm_keys {
  * @NFTA_TRACE_MARK: nfmark (NLA_U32)
  * @NFTA_TRACE_NFPROTO: nf protocol processed (NLA_U32)
  * @NFTA_TRACE_POLICY: policy that decided fate of packet (NLA_U32)
+ * @NFTA_TRACE_CT_ID: conntrack id (NLA_U32)
+ * @NFTA_TRACE_CT_DIRECTION: packets direction (NLA_U8)
+ * @NFTA_TRACE_CT_STATUS: conntrack status (NLA_U32)
+ * @NFTA_TRACE_CT_STATE: packet state (new, established, ...) (NLA_U32)
  */
 enum nft_trace_attributes {
        NFTA_TRACE_UNSPEC,
@@ -1861,6 +1865,10 @@ enum nft_trace_attributes {
        NFTA_TRACE_NFPROTO,
        NFTA_TRACE_POLICY,
        NFTA_TRACE_PAD,
+       NFTA_TRACE_CT_ID,
+       NFTA_TRACE_CT_DIRECTION,
+       NFTA_TRACE_CT_STATUS,
+       NFTA_TRACE_CT_STATE,
        __NFTA_TRACE_MAX
 };
 #define NFTA_TRACE_MAX (__NFTA_TRACE_MAX - 1)
index 580c552..ae3fe87 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/netfilter.h>
 #include <linux/netfilter/nfnetlink.h>
 #include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_conntrack.h>
 #include <net/netfilter/nf_tables_core.h>
 #include <net/netfilter/nf_tables.h>
 
@@ -90,6 +91,49 @@ static int nf_trace_fill_dev_info(struct sk_buff *nlskb,
        return 0;
 }
 
+static int nf_trace_fill_ct_info(struct sk_buff *nlskb,
+                                const struct sk_buff *skb)
+{
+       const struct nf_ct_hook *ct_hook;
+       enum ip_conntrack_info ctinfo;
+       const struct nf_conn *ct;
+       u32 state;
+
+       ct_hook = rcu_dereference(nf_ct_hook);
+       if (!ct_hook)
+               return 0;
+
+       ct = nf_ct_get(skb, &ctinfo);
+       if (!ct) {
+               if (ctinfo != IP_CT_UNTRACKED) /* not seen by conntrack or invalid */
+                       return 0;
+
+               state = NF_CT_STATE_UNTRACKED_BIT;
+       } else {
+               state = NF_CT_STATE_BIT(ctinfo);
+       }
+
+       if (nla_put_be32(nlskb, NFTA_TRACE_CT_STATE, htonl(state)))
+               return -1;
+
+       if (ct) {
+               u32 id = ct_hook->get_id(&ct->ct_general);
+               u32 status = READ_ONCE(ct->status);
+               u8 dir = CTINFO2DIR(ctinfo);
+
+               if (nla_put_u8(nlskb, NFTA_TRACE_CT_DIRECTION, dir))
+                       return -1;
+
+               if (nla_put_be32(nlskb, NFTA_TRACE_CT_ID, (__force __be32)id))
+                       return -1;
+
+               if (status && nla_put_be32(nlskb, NFTA_TRACE_CT_STATUS, htonl(status)))
+                       return -1;
+       }
+
+       return 0;
+}
+
 static int nf_trace_fill_pkt_info(struct sk_buff *nlskb,
                                  const struct nft_pktinfo *pkt)
 {
@@ -210,7 +254,11 @@ void nft_trace_notify(const struct nft_pktinfo *pkt,
                nla_total_size(sizeof(__be32)) +        /* trace type */
                nla_total_size(0) +                     /* VERDICT, nested */
                        nla_total_size(sizeof(u32)) +   /* verdict code */
-               nla_total_size(sizeof(u32)) +           /* id */
+               nla_total_size(sizeof(u32)) +           /* ct id */
+               nla_total_size(sizeof(u8)) +            /* ct direction */
+               nla_total_size(sizeof(u32)) +           /* ct state */
+               nla_total_size(sizeof(u32)) +           /* ct status */
+               nla_total_size(sizeof(u32)) +           /* trace id */
                nla_total_size(NFT_TRACETYPE_LL_HSIZE) +
                nla_total_size(NFT_TRACETYPE_NETWORK_HSIZE) +
                nla_total_size(NFT_TRACETYPE_TRANSPORT_HSIZE) +
@@ -291,6 +339,10 @@ void nft_trace_notify(const struct nft_pktinfo *pkt,
 
                if (nf_trace_fill_pkt_info(skb, pkt))
                        goto nla_put_failure;
+
+               if (nf_trace_fill_ct_info(skb, pkt->skb))
+                       goto nla_put_failure;
+
                info->packet_dumped = true;
        }