This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 3a844d8dd748c71bc762919b122735ca06cd148d
Author: Zhe Weng <[email protected]>
AuthorDate: Wed Mar 20 11:22:24 2024 +0800

    net/netlink: Add NETLINK_NETFILTER's CONNTRACK support
    
    Now we only supports conntrack info from NAT entries, to let our apps in 
user space know the mapping status in NAT.
    
    Signed-off-by: Zhe Weng <[email protected]>
---
 include/netpacket/netlink.h     | 173 ++++++++++
 net/nat/ipv4_nat_entry.c        |   9 +
 net/nat/ipv6_nat_entry.c        |   9 +
 net/netlink/CMakeLists.txt      |   4 +
 net/netlink/Kconfig             |   9 +
 net/netlink/Make.defs           |   4 +
 net/netlink/netlink.h           |  35 +-
 net/netlink/netlink_netfilter.c | 703 ++++++++++++++++++++++++++++++++++++++++
 net/netlink/netlink_sockif.c    |  14 +
 9 files changed, 959 insertions(+), 1 deletion(-)

diff --git a/include/netpacket/netlink.h b/include/netpacket/netlink.h
index 2b443ac6a9..da4e13e165 100644
--- a/include/netpacket/netlink.h
+++ b/include/netpacket/netlink.h
@@ -437,6 +437,160 @@
 
 #define RTM_F_CLONED  0x200  /* This route is cloned */
 
+/* Attribute definitions for struct nfattr **********************************/
+
+/* Macros to handle attribute lists */
+
+#define NFNL_NFA_NEST         0x8000
+#define NFA_TYPE(attr)        ((attr)->nfa_type & 0x7fff)
+
+#define NFA_MASK              (sizeof(uint32_t) - 1)
+#define NFA_ALIGN(n)          (((n) + NFA_MASK) & ~NFA_MASK)
+#define NFA_OK(nfa, n) \
+  ((n) >= (int)sizeof(struct nfattr) && \
+   (nfa)->nfa_len >= sizeof(struct nfattr) && \
+   (nfa)->nfa_len <= (n))
+#define NFA_NEXT(nfa, attrlen) \
+  ((attrlen) -= NFA_ALIGN((nfa)->nfa_len), \
+   (FAR struct nfattr *)(((FAR char *)(nfa)) + NFA_ALIGN((nfa)->nfa_len)))
+#define NFA_LENGTH(len)       ((sizeof(struct nfattr)) + (len))
+#define NFA_SPACE(len)        NFA_ALIGN(NFA_LENGTH(len))
+#define NFA_DATA(nfa)         ((FAR void *)(((FAR char *)(nfa)) + 
NFA_LENGTH(0)))
+#define NFA_PAYLOAD(nfa)      ((int)((nfa)->nfa_len) - NFA_LENGTH(0))
+
+/* Definitions for struct nfgenmsg ******************************************/
+
+#define NFM_NFA(n)           ((FAR struct nfattr *) \
+                              (((FAR char *)(n)) + \
+                              NLMSG_ALIGN(sizeof(struct nfgenmsg))))
+#define NFM_PAYLOAD(n)        NLMSG_PAYLOAD(n, sizeof(struct nfgenmsg))
+
+/* Definitions for NETLINK_NETFILTER ****************************************/
+
+#define NFNETLINK_V0          0
+
+/* netfilter netlink message types are split in two pieces:
+ * 8 bit subsystem, 8bit operation.
+ */
+
+#define NFNL_SUBSYS_ID(x)     (((x) & 0xff00) >> 8)
+#define NFNL_MSG_TYPE(x)      ((x) & 0x00ff)
+
+/* subsystems */
+
+#define NFNL_SUBSYS_NONE              0
+#define NFNL_SUBSYS_CTNETLINK         1
+#define NFNL_SUBSYS_CTNETLINK_EXP     2
+#define NFNL_SUBSYS_QUEUE             3
+#define NFNL_SUBSYS_ULOG              4
+#define NFNL_SUBSYS_OSF               5
+#define NFNL_SUBSYS_IPSET             6
+#define NFNL_SUBSYS_ACCT              7
+#define NFNL_SUBSYS_CTNETLINK_TIMEOUT 8
+#define NFNL_SUBSYS_CTHELPER          9
+#define NFNL_SUBSYS_NFTABLES          10
+#define NFNL_SUBSYS_NFT_COMPAT        11
+#define NFNL_SUBSYS_HOOK              12
+#define NFNL_SUBSYS_COUNT             13
+
+/* NETLINK_NETFILTER: subsystem CTNL (ip conntrack netlink) message types */
+
+#define IPCTNL_MSG_CT_NEW             0
+#define IPCTNL_MSG_CT_GET             1
+#define IPCTNL_MSG_CT_DELETE          2
+#define IPCTNL_MSG_CT_GET_CTRZERO     3
+#define IPCTNL_MSG_CT_GET_STATS_CPU   4
+#define IPCTNL_MSG_CT_GET_STATS       5
+#define IPCTNL_MSG_CT_GET_DYING       6
+#define IPCTNL_MSG_CT_GET_UNCONFIRMED 7
+#define IPCTNL_MSG_MAX                8
+
+/* NETLINK_NETFILTER: Conntrack attributes */
+
+#define CTA_UNSPEC            0
+#define CTA_TUPLE_ORIG        1
+#define CTA_TUPLE_REPLY       2
+#define CTA_STATUS            3
+#define CTA_PROTOINFO         4
+#define CTA_HELP              5
+#define CTA_NAT_SRC           6
+#define CTA_TIMEOUT           7
+#define CTA_MARK              8
+#define CTA_COUNTERS_ORIG     9
+#define CTA_COUNTERS_REPLY    10
+#define CTA_USE               11
+#define CTA_ID                12
+#define CTA_NAT_DST           13
+#define CTA_TUPLE_MASTER      14
+#define CTA_SEQ_ADJ_ORIG      15
+#define CTA_NAT_SEQ_ADJ_ORIG  CTA_SEQ_ADJ_ORIG
+#define CTA_SEQ_ADJ_REPLY     16
+#define CTA_NAT_SEQ_ADJ_REPLY CTA_SEQ_ADJ_REPLY
+#define CTA_ZONE              18
+#define CTA_SECCTX            19
+#define CTA_TIMESTAMP         20
+#define CTA_MARK_MASK         21
+#define CTA_LABELS            22
+#define CTA_LABELS_MASK       23
+#define CTA_SYNPROXY          24
+#define CTA_FILTER            25
+#define CTA_STATUS_MASK       26
+#define CTA_MAX               26
+
+/* NETLINK_NETFILTER: Conntrack tuple attributes */
+
+#define CTA_TUPLE_UNSPEC      0
+#define CTA_TUPLE_IP          1
+#define CTA_TUPLE_PROTO       2
+#define CTA_TUPLE_ZONE        3
+#define CTA_TUPLE_MAX         3
+
+/* NETLINK_NETFILTER: Conntrack IP attributes */
+
+#define CTA_IP_UNSPEC         0
+#define CTA_IP_V4_SRC         1
+#define CTA_IP_V4_DST         2
+#define CTA_IP_V6_SRC         3
+#define CTA_IP_V6_DST         4
+#define CTA_IP_MAX            4
+
+/* NETLINK_NETFILTER: Conntrack protocol attributes */
+
+#define CTA_PROTO_UNSPEC      0
+#define CTA_PROTO_NUM         1
+#define CTA_PROTO_SRC_PORT    2
+#define CTA_PROTO_DST_PORT    3
+#define CTA_PROTO_ICMP_ID     4
+#define CTA_PROTO_ICMP_TYPE   5
+#define CTA_PROTO_ICMP_CODE   6
+#define CTA_PROTO_ICMPV6_ID   7
+#define CTA_PROTO_ICMPV6_TYPE 8
+#define CTA_PROTO_ICMPV6_CODE 9
+#define CTA_PROTO_MAX         9
+
+/* NFnetlink multicast groups (userspace) */
+
+#define NF_NETLINK_CONNTRACK_NEW         0x00000001
+#define NF_NETLINK_CONNTRACK_UPDATE      0x00000002
+#define NF_NETLINK_CONNTRACK_DESTROY     0x00000004
+#define NF_NETLINK_CONNTRACK_EXP_NEW     0x00000008
+#define NF_NETLINK_CONNTRACK_EXP_UPDATE  0x00000010
+#define NF_NETLINK_CONNTRACK_EXP_DESTROY 0x00000020
+
+/* NFnetlink multicast groups */
+
+#define NFNLGRP_NONE                  0
+#define NFNLGRP_CONNTRACK_NEW         1
+#define NFNLGRP_CONNTRACK_UPDATE      2
+#define NFNLGRP_CONNTRACK_DESTROY     3
+#define NFNLGRP_CONNTRACK_EXP_NEW     4
+#define NFNLGRP_CONNTRACK_EXP_UPDATE  5
+#define NFNLGRP_CONNTRACK_EXP_DESTROY 6
+#define NFNLGRP_NFTABLES              7
+#define NFNLGRP_ACCT_QUOTA            8
+#define NFNLGRP_NFTRACE               9
+#define NFNLGRP_MAX                   9
+
 /****************************************************************************
  * Public Type Definitions
  ****************************************************************************/
@@ -584,6 +738,25 @@ struct nla_bitfield32
   uint32_t selector;
 };
 
+/* NETLINK_NETFILTER Message Structures *************************************/
+
+/* These attributes should be manipulated using only the NFA_* */
+
+struct nfattr
+{
+  uint16_t nfa_len;
+  uint16_t nfa_type;
+};
+
+/* General form of address family dependent message. */
+
+struct nfgenmsg
+{
+  uint8_t  nfgen_family; /* AF_xxx */
+  uint8_t  version;      /* nfnetlink version */
+  uint16_t res_id;       /* resource id */
+};
+
 /****************************************************************************
  *              Neighbor Discovery userland options
  */
diff --git a/net/nat/ipv4_nat_entry.c b/net/nat/ipv4_nat_entry.c
index c961e1de86..6793ecfdcd 100644
--- a/net/nat/ipv4_nat_entry.c
+++ b/net/nat/ipv4_nat_entry.c
@@ -33,6 +33,7 @@
 #include <nuttx/nuttx.h>
 
 #include "nat/nat.h"
+#include "netlink/netlink.h"
 
 #ifdef CONFIG_NET_NAT44
 
@@ -145,6 +146,10 @@ ipv4_nat_entry_create(uint8_t protocol,
   hashtable_add(g_nat44_outbound, &entry->hash_outbound,
                 ipv4_nat_outbound_key(local_ip, local_port, protocol));
 
+#ifdef CONFIG_NETLINK_NETFILTER
+  netlink_conntrack_notify(IPCTNL_MSG_CT_NEW, PF_INET, entry);
+#endif
+
   return entry;
 }
 
@@ -175,6 +180,10 @@ static void ipv4_nat_entry_delete(FAR ipv4_nat_entry_t 
*entry)
                                          entry->local_port,
                                          entry->protocol));
 
+#ifdef CONFIG_NETLINK_NETFILTER
+  netlink_conntrack_notify(IPCTNL_MSG_CT_DELETE, PF_INET, entry);
+#endif
+
   kmm_free(entry);
 }
 
diff --git a/net/nat/ipv6_nat_entry.c b/net/nat/ipv6_nat_entry.c
index e4795380a9..c8f8493faf 100644
--- a/net/nat/ipv6_nat_entry.c
+++ b/net/nat/ipv6_nat_entry.c
@@ -34,6 +34,7 @@
 
 #include "inet/inet.h"
 #include "nat/nat.h"
+#include "netlink/netlink.h"
 
 #ifdef CONFIG_NET_NAT66
 
@@ -135,6 +136,10 @@ ipv6_nat_entry_create(uint8_t protocol, const 
net_ipv6addr_t external_ip,
   hashtable_add(g_nat66_outbound, &entry->hash_outbound,
                 ipv6_nat_hash_key(local_ip, local_port, protocol));
 
+#ifdef CONFIG_NETLINK_NETFILTER
+  netlink_conntrack_notify(IPCTNL_MSG_CT_NEW, PF_INET6, entry);
+#endif
+
   return entry;
 }
 
@@ -168,6 +173,10 @@ static void ipv6_nat_entry_delete(FAR ipv6_nat_entry_t 
*entry)
                                      entry->local_port,
                                      entry->protocol));
 
+#ifdef CONFIG_NETLINK_NETFILTER
+  netlink_conntrack_notify(IPCTNL_MSG_CT_DELETE, PF_INET6, entry);
+#endif
+
   kmm_free(entry);
 }
 
diff --git a/net/netlink/CMakeLists.txt b/net/netlink/CMakeLists.txt
index feb14b41af..6f525c6d57 100644
--- a/net/netlink/CMakeLists.txt
+++ b/net/netlink/CMakeLists.txt
@@ -27,5 +27,9 @@ if(CONFIG_NET_NETLINK)
     list(APPEND SRCS netlink_route.c netlink_attr.c)
   endif()
 
+  if(CONFIG_NETLINK_NETFILTER)
+    list(APPEND SRCS netlink_netfilter.c)
+  endif()
+
   target_sources(net PRIVATE ${SRCS})
 endif()
diff --git a/net/netlink/Kconfig b/net/netlink/Kconfig
index cacf2eaf91..71d60885fd 100644
--- a/net/netlink/Kconfig
+++ b/net/netlink/Kconfig
@@ -117,6 +117,15 @@ config NETLINK_VALIDATE_POLICY
                you pass in are valid.
 
 endif # NETLINK_ROUTE
+
+config NETLINK_NETFILTER
+       bool "Netlink Netfilter protocol"
+       default n
+       depends on NET_NAT
+       ---help---
+               Support the NETLINK_NETFILTER protocol option, mainly
+               for conntrack with NAT.
+
 endmenu # Netlink Protocols
 endif # NET_NETLINK
 endmenu # Netlink Socket Support
diff --git a/net/netlink/Make.defs b/net/netlink/Make.defs
index b18597cca3..363e9f2a93 100644
--- a/net/netlink/Make.defs
+++ b/net/netlink/Make.defs
@@ -29,6 +29,10 @@ ifeq ($(CONFIG_NETLINK_ROUTE),y)
 NET_CSRCS += netlink_route.c netlink_attr.c
 endif
 
+ifeq ($(CONFIG_NETLINK_NETFILTER),y)
+NET_CSRCS += netlink_netfilter.c
+endif
+
 # Include netlink build support
 
 DEPPATH += --dep-path netlink
diff --git a/net/netlink/netlink.h b/net/netlink/netlink.h
index 320f792438..9e8fc9e584 100644
--- a/net/netlink/netlink.h
+++ b/net/netlink/netlink.h
@@ -543,7 +543,40 @@ int nla_parse(FAR struct nlattr **tb, int maxtype,
               FAR const struct nlattr *head,
               int len, FAR const struct nla_policy *policy,
               FAR struct netlink_ext_ack *extack);
-#endif
+#endif /* CONFIG_NETLINK_ROUTE */
+
+/****************************************************************************
+ * Name: netlink_netfilter_sendto
+ *
+ * Description:
+ *   Perform the sendto() operation for the NETLINK_NETFILTER protocol.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NETLINK_NETFILTER
+ssize_t netlink_netfilter_sendto(NETLINK_HANDLE handle,
+                                 FAR const struct nlmsghdr *nlmsg,
+                                 size_t len, int flags,
+                                 FAR const struct sockaddr_nl *to,
+                                 socklen_t tolen);
+
+/****************************************************************************
+ * Name: netlink_conntrack_notify
+ *
+ * Description:
+ *   Perform the conntrack broadcast for the NETLINK_NETFILTER protocol.
+ *
+ * Input Parameters:
+ *   type      - The type of the message, IPCTNL_MSG_CT_*
+ *   domain    - The domain of the message
+ *   nat_entry - The NAT entry
+ *
+ ****************************************************************************/
+
+void netlink_conntrack_notify(uint8_t type, uint8_t domain,
+                              FAR const void *nat_entry);
+
+#endif /* CONFIG_NETLINK_NETFILTER */
 
 #undef EXTERN
 #ifdef __cplusplus
diff --git a/net/netlink/netlink_netfilter.c b/net/netlink/netlink_netfilter.c
new file mode 100644
index 0000000000..1fbdc90003
--- /dev/null
+++ b/net/netlink/netlink_netfilter.c
@@ -0,0 +1,703 @@
+/****************************************************************************
+ * net/netlink/netlink_netfilter.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <netpacket/netlink.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/net/net.h>
+#include <nuttx/net/icmp.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/netlink.h>
+
+#include "inet/inet.h"
+#include "nat/nat.h"
+#include "netlink/netlink.h"
+#include "utils/utils.h"
+
+#ifdef CONFIG_NETLINK_NETFILTER
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct nfnl_sendto_request_s
+{
+  struct nlmsghdr hdr;
+  struct nfgenmsg msg;
+};
+
+struct nfnl_info_s
+{
+  NETLINK_HANDLE handle;
+  FAR const struct nfnl_sendto_request_s *req;
+};
+
+struct nfnl_ipv4addr_s
+{
+  struct nfattr attr;
+  in_addr_t     addr;
+};
+
+struct nfnl_ipv6addr_s
+{
+  struct nfattr  attr;
+  net_ipv6addr_t addr;
+};
+
+struct nfnl_attr_u8_s
+{
+  struct nfattr attr;
+  uint8_t       value;
+  uint8_t       pad[3];
+};
+
+struct nfnl_attr_u16_s
+{
+  struct nfattr attr;
+  uint16_t      value;
+  uint16_t      pad[1];
+};
+
+/* Struct of a conntrack tuple
+ * +------+--------------+-----------------+
+ * | attr | CTA_TUPLE_IP | CTA_TUPLE_PROTO |
+ * +------+--------------+-----------------+
+ */
+
+/* CTA_TUPLE_IP definitions */
+
+struct conntrack_tuple_ipv4_s
+{
+  struct nfattr attr;
+  struct nfnl_ipv4addr_s src;
+  struct nfnl_ipv4addr_s dst;
+};
+
+struct conntrack_tuple_ipv6_s
+{
+  struct nfattr attr;
+  struct nfnl_ipv6addr_s src;
+  struct nfnl_ipv6addr_s dst;
+};
+
+/* CTA_TUPLE_PROTO definitions */
+
+struct conntrack_tuple_tcpudp_s
+{
+  struct nfattr attr;
+  struct nfnl_attr_u8_s  proto;
+  struct nfnl_attr_u16_s sport;
+  struct nfnl_attr_u16_s dport;
+};
+
+struct conntrack_tuple_icmp_s
+{
+  struct nfattr attr;
+  struct nfnl_attr_u8_s  proto;
+  struct nfnl_attr_u16_s id;
+  struct nfnl_attr_u8_s  type;
+  struct nfnl_attr_u8_s  code;
+};
+
+/* Struct of a conntrack response
+ * +-----+-----+-----------------+----------------+
+ * | hdr | msg | tuple of origin | tuple of reply |
+ * +-----+-----+-----------------+----------------+
+ */
+
+struct conntrack_recvfrom_response_s
+{
+  struct nlmsghdr hdr;
+  struct nfgenmsg msg;
+  uint8_t         data[1];
+};
+
+#define SIZEOF_CTNL_RECVFROM_RESPONSE_S(n) \
+  (sizeof(struct conntrack_recvfrom_response_s) + (n) - 1)
+
+struct conntrack_recvfrom_rsplist_s
+{
+  sq_entry_t flink;
+  struct conntrack_recvfrom_response_s payload;
+};
+
+#define SIZEOF_CTNL_RECVFROM_RSPLIST_S(n) \
+  (sizeof(struct conntrack_recvfrom_rsplist_s) + (n) - 1)
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: netlink_conntrack_tuple_size
+ *
+ * Description:
+ *   Get the size of a CTA_TUPLE. Struct of a conntrack tuple:
+ *     +------+--------------+-----------------+
+ *     | attr | CTA_TUPLE_IP | CTA_TUPLE_PROTO |
+ *     +------+--------------+-----------------+
+ *
+ * Input Parameters:
+ *   domain - The domain of the tuple
+ *   proto  - The protocol of the tuple
+ *
+ * Returned Value:
+ *   The size of the tuple.
+ *
+ ****************************************************************************/
+
+static ssize_t netlink_conntrack_tuple_size(uint8_t domain, uint8_t proto)
+{
+  size_t size = sizeof(struct nfattr);
+
+  switch (domain)
+    {
+      case PF_INET:
+        size += sizeof(struct conntrack_tuple_ipv4_s);
+        break;
+
+      case PF_INET6:
+        size += sizeof(struct conntrack_tuple_ipv6_s);
+        break;
+
+      default:
+        return -EINVAL;
+    }
+
+  switch (proto)
+    {
+      case IPPROTO_TCP:
+      case IPPROTO_UDP:
+        size += sizeof(struct conntrack_tuple_tcpudp_s);
+        break;
+
+      case IPPROTO_ICMP:
+      case IPPROTO_ICMP6:
+        size += sizeof(struct conntrack_tuple_icmp_s);
+        break;
+
+      default:
+        return -EINVAL;
+    }
+
+  return size;
+}
+
+/****************************************************************************
+ * Name: netlink_conntrack_fill_ip
+ *
+ * Description:
+ *   Fill the data of a CTA_TUPLE_IP.
+ *
+ * Input Parameters:
+ *   buf    - The buffer to fill
+ *   domain - The domain of the addresses
+ *   src    - The source address
+ *   dst    - The destination address
+ *
+ * Returned Value:
+ *   The size of the filled data.
+ *
+ ****************************************************************************/
+
+static size_t netlink_conntrack_fill_ip(FAR void *buf, uint8_t domain,
+                                        FAR const void *src,
+                                        FAR const void *dst)
+{
+#ifdef CONFIG_NET_NAT44
+  if (domain == PF_INET)
+    {
+      FAR struct conntrack_tuple_ipv4_s *tuple_ipv4 = buf;
+
+      tuple_ipv4->attr.nfa_len  = sizeof(struct conntrack_tuple_ipv4_s);
+      tuple_ipv4->attr.nfa_type = CTA_TUPLE_IP | NFNL_NFA_NEST;
+
+      tuple_ipv4->src.attr.nfa_len  = NFA_LENGTH(sizeof(in_addr_t));
+      tuple_ipv4->src.attr.nfa_type = CTA_IP_V4_SRC;
+      net_ipv4addr_hdrcopy(&tuple_ipv4->src.addr, src);
+
+      tuple_ipv4->dst.attr.nfa_len  = NFA_LENGTH(sizeof(in_addr_t));
+      tuple_ipv4->dst.attr.nfa_type = CTA_IP_V4_DST;
+      net_ipv4addr_hdrcopy(&tuple_ipv4->dst.addr, dst);
+
+      return tuple_ipv4->attr.nfa_len;
+    }
+#endif
+
+#ifdef CONFIG_NET_NAT66
+  if (domain == PF_INET6)
+    {
+      FAR struct conntrack_tuple_ipv6_s *tuple_ipv6 = buf;
+
+      tuple_ipv6->attr.nfa_len  = sizeof(struct conntrack_tuple_ipv6_s);
+      tuple_ipv6->attr.nfa_type = CTA_TUPLE_IP | NFNL_NFA_NEST;
+
+      tuple_ipv6->src.attr.nfa_len  = NFA_LENGTH(sizeof(net_ipv6addr_t));
+      tuple_ipv6->src.attr.nfa_type = CTA_IP_V6_SRC;
+      net_ipv6addr_hdrcopy(tuple_ipv6->src.addr, src);
+
+      tuple_ipv6->dst.attr.nfa_len  = NFA_LENGTH(sizeof(net_ipv6addr_t));
+      tuple_ipv6->dst.attr.nfa_type = CTA_IP_V6_DST;
+      net_ipv6addr_hdrcopy(tuple_ipv6->dst.addr, dst);
+
+      return tuple_ipv6->attr.nfa_len;
+    }
+#endif
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: netlink_conntrack_fill_proto
+ *
+ * Description:
+ *   Fill the data of a CTA_TUPLE_PROTO.
+ *
+ * Input Parameters:
+ *   buf   - The buffer to fill
+ *   proto - The protocol of the tuple
+ *   sport - The source port
+ *   dport - The destination port
+ *   reply - True if the tuple is a reply
+ *
+ * Returned Value:
+ *   The size of the filled data.
+ *
+ ****************************************************************************/
+
+static size_t netlink_conntrack_fill_proto(FAR void *buf, uint8_t proto,
+                                           uint16_t sport, uint16_t dport,
+                                           bool reply)
+{
+  switch (proto)
+    {
+#ifdef CONFIG_NET_TCP
+      case IPPROTO_TCP:
+#endif
+#ifdef CONFIG_NET_UDP
+      case IPPROTO_UDP:
+#endif
+#if defined(CONFIG_NET_TCP) || defined(CONFIG_NET_UDP)
+        {
+          FAR struct conntrack_tuple_tcpudp_s *tuple_tcpudp = buf;
+
+          tuple_tcpudp->attr.nfa_len  =
+                                     sizeof(struct conntrack_tuple_tcpudp_s);
+          tuple_tcpudp->attr.nfa_type = CTA_TUPLE_PROTO | NFNL_NFA_NEST;
+
+          tuple_tcpudp->proto.attr.nfa_len  = NFA_LENGTH(sizeof(uint8_t));
+          tuple_tcpudp->proto.attr.nfa_type = CTA_PROTO_NUM;
+          tuple_tcpudp->proto.value         = proto;
+
+          tuple_tcpudp->sport.attr.nfa_len  = NFA_LENGTH(sizeof(uint16_t));
+          tuple_tcpudp->sport.attr.nfa_type = CTA_PROTO_SRC_PORT;
+          tuple_tcpudp->sport.value         = sport;
+
+          tuple_tcpudp->dport.attr.nfa_len  = NFA_LENGTH(sizeof(uint16_t));
+          tuple_tcpudp->dport.attr.nfa_type = CTA_PROTO_DST_PORT;
+          tuple_tcpudp->dport.value         = dport;
+
+          return tuple_tcpudp->attr.nfa_len;
+        }
+#endif
+
+#ifdef CONFIG_NET_ICMP
+      case IPPROTO_ICMP:
+#endif
+#ifdef CONFIG_NET_ICMPv6
+      case IPPROTO_ICMP6:
+#endif
+#if defined(CONFIG_NET_ICMP) || defined(CONFIG_NET_ICMPv6)
+        {
+          FAR struct conntrack_tuple_icmp_s *tuple_icmp = buf;
+
+          tuple_icmp->attr.nfa_len  = sizeof(struct conntrack_tuple_icmp_s);
+          tuple_icmp->attr.nfa_type = CTA_TUPLE_PROTO | NFNL_NFA_NEST;
+
+          tuple_icmp->proto.attr.nfa_len  = NFA_LENGTH(sizeof(uint8_t));
+          tuple_icmp->proto.attr.nfa_type = CTA_PROTO_NUM;
+          tuple_icmp->proto.value         = proto;
+
+          tuple_icmp->id.attr.nfa_len     = NFA_LENGTH(sizeof(uint16_t));
+          tuple_icmp->id.value            = reply ? dport : sport;
+
+          tuple_icmp->type.attr.nfa_len   = NFA_LENGTH(sizeof(uint8_t));
+
+          tuple_icmp->code.attr.nfa_len   = NFA_LENGTH(sizeof(uint8_t));
+          tuple_icmp->code.value          = 0;
+
+#ifdef CONFIG_NET_ICMP
+          if (proto == IPPROTO_ICMP)
+            {
+              tuple_icmp->id.attr.nfa_type   = CTA_PROTO_ICMP_ID;
+              tuple_icmp->type.attr.nfa_type = CTA_PROTO_ICMP_TYPE;
+              tuple_icmp->type.value         = reply ? ICMP_ECHO_REPLY :
+                                                       ICMP_ECHO_REQUEST;
+              tuple_icmp->code.attr.nfa_type = CTA_PROTO_ICMP_CODE;
+            }
+#endif
+
+#ifdef CONFIG_NET_ICMPv6
+          if (proto == IPPROTO_ICMP6)
+            {
+              tuple_icmp->id.attr.nfa_type   = CTA_PROTO_ICMPV6_ID;
+              tuple_icmp->type.attr.nfa_type = CTA_PROTO_ICMPV6_TYPE;
+              tuple_icmp->type.value         = reply ? ICMPv6_ECHO_REPLY :
+                                                       ICMPv6_ECHO_REQUEST;
+              tuple_icmp->code.attr.nfa_type = CTA_PROTO_ICMPV6_CODE;
+            }
+#endif
+
+          return tuple_icmp->attr.nfa_len;
+        }
+#endif
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: netlink_get_ipv4/ipv6_conntrack
+ *
+ * Description:
+ *   Get the conntrack response corresponding to an NAT entry.
+ *
+ ****************************************************************************/
+
+static FAR struct netlink_response_s *
+netlink_get_conntrack(FAR const struct nlmsghdr *req, uint16_t flags,
+                      uint8_t type, uint8_t domain, uint8_t proto,
+                      FAR const void *lipaddr, uint16_t lport,
+                      FAR const void *eipaddr, uint16_t eport,
+                      FAR const void *ripaddr, uint16_t rport)
+{
+  FAR struct conntrack_recvfrom_rsplist_s *entry;
+  FAR struct nfattr *tuple;
+  ssize_t tuple_size = netlink_conntrack_tuple_size(domain, proto);
+  size_t  offset     = 0;
+  size_t  allocsize;
+  size_t  rspsize;
+
+  if (tuple_size < 0)
+    {
+      nerr("ERROR: Failed to get tuple size in response.\n");
+      return NULL;
+    }
+
+  rspsize   = SIZEOF_CTNL_RECVFROM_RESPONSE_S(tuple_size * 2);
+  allocsize = SIZEOF_CTNL_RECVFROM_RSPLIST_S(tuple_size * 2);
+
+  entry = kmm_malloc(allocsize);
+  if (entry == NULL)
+    {
+      nerr("ERROR: Failed to allocate response buffer.\n");
+      return NULL;
+    }
+
+  entry->payload.hdr.nlmsg_len   = rspsize;
+  entry->payload.hdr.nlmsg_type  = type | (NFNL_SUBSYS_CTNETLINK << 8);
+  entry->payload.hdr.nlmsg_flags = flags;
+  entry->payload.hdr.nlmsg_seq   = req ? req->nlmsg_seq : 0;
+  entry->payload.hdr.nlmsg_pid   = req ? req->nlmsg_pid : 0;
+
+  entry->payload.msg.nfgen_family = domain;
+  entry->payload.msg.version      = NFNETLINK_V0;
+  entry->payload.msg.res_id       = 0;
+
+  /* CTA_TUPLE_ORIG */
+
+  tuple = (FAR struct nfattr *)&entry->payload.data[offset];
+  tuple->nfa_len  = tuple_size;
+  tuple->nfa_type = CTA_TUPLE_ORIG | NFNL_NFA_NEST;
+  offset += sizeof(struct nfattr);
+
+  offset += netlink_conntrack_fill_ip(&entry->payload.data[offset], domain,
+                                      lipaddr, ripaddr);
+  offset += netlink_conntrack_fill_proto(&entry->payload.data[offset], proto,
+                                         lport, rport, false);
+
+  DEBUGASSERT(offset == tuple_size);
+
+  /* CTA_TUPLE_REPLY */
+
+  tuple = (FAR struct nfattr *)&entry->payload.data[offset];
+  tuple->nfa_len  = tuple_size;
+  tuple->nfa_type = CTA_TUPLE_REPLY | NFNL_NFA_NEST;
+  offset += sizeof(struct nfattr);
+
+  offset += netlink_conntrack_fill_ip(&entry->payload.data[offset], domain,
+                                      ripaddr, eipaddr);
+  offset += netlink_conntrack_fill_proto(&entry->payload.data[offset], proto,
+                                         rport, eport, true);
+
+  DEBUGASSERT(offset == tuple_size * 2);
+
+  return (FAR struct netlink_response_s *)entry;
+}
+
+#ifdef CONFIG_NET_NAT44
+static FAR struct netlink_response_s *
+netlink_get_ipv4_conntrack(FAR const struct nlmsghdr *req,
+                           FAR const ipv4_nat_entry_t *entry,
+                           uint16_t flags, uint8_t type)
+{
+#ifndef CONFIG_NET_NAT44_SYMMETRIC
+  const in_addr_t any = INADDR_ANY;
+#endif
+  return netlink_get_conntrack(req, flags, type, PF_INET, entry->protocol,
+                               &entry->local_ip, entry->local_port,
+                               &entry->external_ip, entry->external_port,
+#ifdef CONFIG_NET_NAT44_SYMMETRIC
+                               &entry->peer_ip, entry->peer_port
+#else
+                               &any, 0 /* Zero-address */
+#endif
+                              );
+}
+#endif
+
+#ifdef CONFIG_NET_NAT66
+static FAR struct netlink_response_s *
+netlink_get_ipv6_conntrack(FAR const struct nlmsghdr *req,
+                           FAR const ipv6_nat_entry_t *entry,
+                           uint16_t flags, uint8_t type)
+{
+  return netlink_get_conntrack(req, flags, type, PF_INET6, entry->protocol,
+                               entry->local_ip, entry->local_port,
+                               entry->external_ip, entry->external_port,
+#ifdef CONFIG_NET_NAT66_SYMMETRIC
+                               entry->peer_ip, entry->peer_port
+#else
+                               g_ipv6_unspecaddr, 0 /* Zero-address */
+#endif
+      );
+}
+#endif
+
+/****************************************************************************
+ * Name: netlink_add_ipv4/ipv6_conntrack
+ *
+ * Description:
+ *   Add the conntrack response of an IPv4/IPv6 NAT entry.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_NAT44
+static void netlink_add_ipv4_conntrack(FAR ipv4_nat_entry_t *entry,
+                                       FAR void *arg)
+{
+  FAR struct nfnl_info_s *info = arg;
+  FAR struct netlink_response_s *resp;
+  uint16_t flags = NLM_F_MULTI | NLM_F_DUMP_FILTERED;
+
+  resp = netlink_get_ipv4_conntrack(&info->req->hdr, entry, flags,
+                                    IPCTNL_MSG_CT_NEW);
+  if (resp != NULL)
+    {
+      netlink_add_response(info->handle, resp);
+    }
+}
+#endif
+
+#ifdef CONFIG_NET_NAT66
+static void netlink_add_ipv6_conntrack(FAR ipv6_nat_entry_t *entry,
+                                       FAR void *arg)
+{
+  FAR struct nfnl_info_s *info = arg;
+  FAR struct netlink_response_s *resp;
+  uint16_t flags = NLM_F_MULTI | NLM_F_DUMP_FILTERED;
+
+  resp = netlink_get_ipv6_conntrack(&info->req->hdr, entry, flags,
+                                    IPCTNL_MSG_CT_NEW);
+
+  if (resp != NULL)
+    {
+      netlink_add_response(info->handle, resp);
+    }
+}
+#endif
+
+/****************************************************************************
+ * Name: netlink_list_conntrack
+ *
+ * Description:
+ *   Return the entire NAT table.
+ *
+ ****************************************************************************/
+
+static int netlink_list_conntrack(NETLINK_HANDLE handle,
+                                 FAR const struct nfnl_sendto_request_s *req)
+{
+  struct nfnl_info_s info;
+  uint8_t type = NFNL_MSG_TYPE(req->hdr.nlmsg_type);
+  if (type != IPCTNL_MSG_CT_GET)
+    {
+      return -ENOSYS;
+    }
+
+  info.handle = handle;
+  info.req    = req;
+
+  switch (req->msg.nfgen_family)
+    {
+#ifdef CONFIG_NET_NAT44
+      case AF_INET:
+        ipv4_nat_entry_foreach(netlink_add_ipv4_conntrack, &info);
+        break;
+#endif
+
+#ifdef CONFIG_NET_NAT66
+      case AF_INET6:
+        ipv6_nat_entry_foreach(netlink_add_ipv6_conntrack, &info);
+        break;
+#endif
+
+      default:
+        return -ENOSYS;
+    }
+
+  return netlink_add_terminator(handle, &req->hdr, 0);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: netlink_netfilter_sendto
+ *
+ * Description:
+ *   Perform the sendto() operation for the NETLINK_NETFILTER protocol.
+ *
+ ****************************************************************************/
+
+ssize_t netlink_netfilter_sendto(NETLINK_HANDLE handle,
+                                 FAR const struct nlmsghdr *nlmsg,
+                                 size_t len, int flags,
+                                 FAR const struct sockaddr_nl *to,
+                                 socklen_t tolen)
+{
+  FAR const struct nfnl_sendto_request_s *req =
+    (FAR const struct nfnl_sendto_request_s *)nlmsg;
+  ssize_t ret = -ENOSYS;
+  uint8_t subsys;
+
+  DEBUGASSERT(handle != NULL && nlmsg != NULL &&
+              nlmsg->nlmsg_len >= sizeof(struct nlmsghdr) &&
+              len >= sizeof(struct nlmsghdr) &&
+              len >= nlmsg->nlmsg_len && to != NULL &&
+              tolen >= sizeof(struct sockaddr_nl));
+
+  /* Split subsys and type from nlmsg->nlmsg_type */
+
+  subsys = NFNL_SUBSYS_ID(nlmsg->nlmsg_type);
+
+  /* Handle according to the subsystem */
+
+  switch (subsys)
+    {
+      case NFNL_SUBSYS_CTNETLINK:
+        ret = netlink_list_conntrack(handle, req);
+        break;
+    }
+
+  /* On success, return the size of the request that was processed */
+
+  if (ret >= 0)
+    {
+      ret = len;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: netlink_conntrack_notify
+ *
+ * Description:
+ *   Perform the conntrack broadcast for the NETLINK_NETFILTER protocol.
+ *
+ * Input Parameters:
+ *   type      - The type of the message, IPCTNL_MSG_CT_*
+ *   domain    - The domain of the message
+ *   nat_entry - The NAT entry
+ *
+ ****************************************************************************/
+
+void netlink_conntrack_notify(uint8_t type, uint8_t domain,
+                              FAR const void *nat_entry)
+{
+  FAR struct netlink_response_s *resp;
+  uint16_t flags;
+  int group;
+
+  switch (type)
+    {
+      case IPCTNL_MSG_CT_NEW:
+        group = NFNLGRP_CONNTRACK_NEW;
+        flags = NLM_F_EXCL | NLM_F_CREATE;
+        break;
+
+      case IPCTNL_MSG_CT_DELETE:
+        group = NFNLGRP_CONNTRACK_DESTROY;
+        flags = 0;
+        break;
+
+      default:
+        return;
+    }
+
+  switch (domain)
+    {
+#ifdef CONFIG_NET_NAT44
+      case PF_INET:
+        resp = netlink_get_ipv4_conntrack(NULL, nat_entry, flags, type);
+        break;
+#endif
+
+#ifdef CONFIG_NET_NAT66
+      case PF_INET6:
+        resp = netlink_get_ipv6_conntrack(NULL, nat_entry, flags, type);
+        break;
+#endif
+
+      default:
+        return;
+    }
+
+  if (resp != NULL)
+    {
+      netlink_add_broadcast(group, resp);
+    }
+}
+
+#endif /* CONFIG_NETLINK_NETFILTER */
diff --git a/net/netlink/netlink_sockif.c b/net/netlink/netlink_sockif.c
index abfa27d9fc..72de037697 100644
--- a/net/netlink/netlink_sockif.c
+++ b/net/netlink/netlink_sockif.c
@@ -129,6 +129,11 @@ static int netlink_setup(FAR struct socket *psock)
         break;
 #endif
 
+#ifdef CONFIG_NETLINK_NETFILTER
+      case NETLINK_NETFILTER:
+        break;
+#endif
+
       default:
         return -EPROTONOSUPPORT;
     }
@@ -621,6 +626,15 @@ static ssize_t netlink_sendmsg(FAR struct socket *psock,
         break;
 #endif
 
+#ifdef CONFIG_NETLINK_NETFILTER
+      case NETLINK_NETFILTER:
+        ret = netlink_netfilter_sendto(conn, nlmsg,
+                                       msg->msg_iov->iov_len, flags,
+                                       (FAR const struct sockaddr_nl *)to,
+                                       tolen);
+        break;
+#endif
+
       default:
        ret = -EOPNOTSUPP;
        break;

Reply via email to