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-apps.git

commit 7e7b108ef9509176abcd03daf62a5e84a4d5c72d
Author: Zhe Weng <[email protected]>
AuthorDate: Mon Mar 25 16:36:30 2024 +0800

    netutils/netlib: Add netfilter conntrack functions
    
    Signed-off-by: Zhe Weng <[email protected]>
---
 include/netutils/netlib.h          |  64 +++++++
 netutils/netlib/CMakeLists.txt     |   6 +
 netutils/netlib/Makefile           |   6 +
 netutils/netlib/netlib_conntrack.c | 348 +++++++++++++++++++++++++++++++++++++
 4 files changed, 424 insertions(+)

diff --git a/include/netutils/netlib.h b/include/netutils/netlib.h
index e417f11a6..ba6bb618b 100644
--- a/include/netutils/netlib.h
+++ b/include/netutils/netlib.h
@@ -123,6 +123,60 @@ struct netlib_device_s
 };
 #endif /* CONFIG_NETLINK_ROUTE*/
 
+#ifdef CONFIG_NETLINK_NETFILTER
+/* Describes one connection returned by netlib_get_conntrack() */
+
+union netlib_conntrack_addr_u
+{
+#ifdef CONFIG_NET_IPv4
+  struct in_addr ipv4;
+#endif
+#ifdef CONFIG_NET_IPv6
+  struct in6_addr ipv6;
+#endif
+};
+
+struct netlib_conntrack_tuple_s
+{
+  union netlib_conntrack_addr_u src;
+  union netlib_conntrack_addr_u dst;
+
+  union
+  {
+    struct
+    {
+      uint16_t sport;
+      uint16_t dport;
+    } tcp; /* and udp */
+
+    struct
+    {
+      uint16_t id;
+      uint8_t  type;
+      uint8_t  code;
+    } icmp; /* and icmp6 */
+  } l4;
+
+  uint8_t l4proto;
+};
+
+struct netlib_conntrack_s
+{
+  struct netlib_conntrack_tuple_s orig;
+  struct netlib_conntrack_tuple_s reply;
+
+  sa_family_t family; /* AF_INET or AF_INET6 */
+  uint8_t     type;   /* IPCTNL_MSG_CT_* */
+};
+
+/* There might be many conntrack entries, so we don't use array of data, but
+ * use callback instead.
+ */
+
+typedef CODE int (*netlib_conntrack_cb_t)(FAR struct netlib_conntrack_s *ct);
+
+#endif /* CONFIG_NETLINK_NETFILTER */
+
 #ifdef CONFIG_NETUTILS_NETLIB_GENERICURLPARSER
 struct url_s
 {
@@ -355,6 +409,16 @@ FAR struct ipt_entry *netlib_ipt_masquerade_entry(FAR 
const char *ifname);
 #  endif
 #endif
 
+#ifdef CONFIG_NETLINK_NETFILTER
+/* Netfilter connection tracking support */
+
+struct nlmsghdr;  /* Forward reference */
+
+int netlib_parse_conntrack(FAR const struct nlmsghdr *nlh, size_t len,
+                           FAR struct netlib_conntrack_s *ct);
+int netlib_get_conntrack(sa_family_t family, netlib_conntrack_cb_t cb);
+#endif
+
 /* HTTP support */
 
 int netlib_parsehttpurl(FAR const char *url, uint16_t *port,
diff --git a/netutils/netlib/CMakeLists.txt b/netutils/netlib/CMakeLists.txt
index fd039bc53..e3bd0cb8b 100644
--- a/netutils/netlib/CMakeLists.txt
+++ b/netutils/netlib/CMakeLists.txt
@@ -84,6 +84,12 @@ if(CONFIG_NETUTILS_NETLIB)
     endif()
   endif()
 
+  # Netfilter connection support
+
+  if(CONFIG_NETLINK_NETFILTER)
+    list(APPEND SRCS netlib_conntrack.c)
+  endif()
+
   # These require TCP support
 
   if(CONFIG_NET_TCP)
diff --git a/netutils/netlib/Makefile b/netutils/netlib/Makefile
index 99bb54bd0..9b0525924 100644
--- a/netutils/netlib/Makefile
+++ b/netutils/netlib/Makefile
@@ -85,6 +85,12 @@ CSRCS += netlib_getroute.c
 endif
 endif
 
+# Netfilter connection support
+
+ifeq ($(CONFIG_NETLINK_NETFILTER),y)
+CSRCS += netlib_conntrack.c
+endif
+
 # These require TCP support
 
 ifeq ($(CONFIG_NET_TCP),y)
diff --git a/netutils/netlib/netlib_conntrack.c 
b/netutils/netlib/netlib_conntrack.c
new file mode 100644
index 000000000..fda39476a
--- /dev/null
+++ b/netutils/netlib/netlib_conntrack.c
@@ -0,0 +1,348 @@
+/****************************************************************************
+ * apps/netutils/netlib/netlib_conntrack.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 <netpacket/netlink.h>
+
+#include <nuttx/net/ip.h>
+
+#include "netutils/netlib.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define RXBUFFER_SIZE 256
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct netlib_conntrack_sendto_request_s
+{
+  struct nlmsghdr hdr;
+  struct nfgenmsg msg;
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: netlib_ct_parse_ip
+ ****************************************************************************/
+
+static void netlib_ct_parse_ip(FAR const struct nfattr *attr,
+                               FAR struct netlib_conntrack_tuple_s *tuple)
+{
+  FAR const struct nfattr *subattr = NFA_DATA(attr);
+  ssize_t paylen = NFA_PAYLOAD(attr);
+
+  for (; NFA_OK(subattr, paylen); subattr = NFA_NEXT(subattr, paylen))
+    {
+      switch (NFA_TYPE(subattr))
+        {
+          case CTA_IP_V4_SRC:
+            net_ipv4addr_hdrcopy(&tuple->src.ipv4, NFA_DATA(subattr));
+            break;
+          case CTA_IP_V4_DST:
+            net_ipv4addr_hdrcopy(&tuple->dst.ipv4, NFA_DATA(subattr));
+            break;
+          case CTA_IP_V6_SRC:
+            net_ipv6addr_hdrcopy(&tuple->src.ipv6, NFA_DATA(subattr));
+            break;
+          case CTA_IP_V6_DST:
+            net_ipv6addr_hdrcopy(&tuple->dst.ipv6, NFA_DATA(subattr));
+            break;
+        }
+    }
+}
+
+/****************************************************************************
+ * Name: netlib_ct_parse_proto
+ ****************************************************************************/
+
+static void netlib_ct_parse_proto(FAR const struct nfattr *attr,
+                                  FAR struct netlib_conntrack_tuple_s *tuple)
+{
+  FAR const struct nfattr *subattr = NFA_DATA(attr);
+  ssize_t paylen = NFA_PAYLOAD(attr);
+
+  for (; NFA_OK(subattr, paylen); subattr = NFA_NEXT(subattr, paylen))
+    {
+      switch (NFA_TYPE(subattr))
+        {
+          case CTA_PROTO_NUM:
+            tuple->l4proto = *(FAR uint8_t *)NFA_DATA(subattr);
+            break;
+
+          case CTA_PROTO_SRC_PORT:
+            tuple->l4.tcp.sport = ntohs(*(FAR uint16_t *)NFA_DATA(subattr));
+            break;
+
+          case CTA_PROTO_DST_PORT:
+            tuple->l4.tcp.dport = ntohs(*(FAR uint16_t *)NFA_DATA(subattr));
+            break;
+
+          case CTA_PROTO_ICMP_ID:
+          case CTA_PROTO_ICMPV6_ID:
+            tuple->l4.icmp.id = ntohs(*(FAR uint16_t *)NFA_DATA(subattr));
+            break;
+
+          case CTA_PROTO_ICMP_TYPE:
+          case CTA_PROTO_ICMPV6_TYPE:
+            tuple->l4.icmp.type = *(FAR uint8_t *)NFA_DATA(subattr);
+            break;
+
+          case CTA_PROTO_ICMP_CODE:
+          case CTA_PROTO_ICMPV6_CODE:
+            tuple->l4.icmp.code = *(FAR uint8_t *)NFA_DATA(subattr);
+            break;
+        }
+    }
+}
+
+/****************************************************************************
+ * Name: netlib_ct_parse_tuple
+ ****************************************************************************/
+
+static void netlib_ct_parse_tuple(FAR const struct nfattr *attr,
+                                  FAR struct netlib_conntrack_tuple_s *tuple)
+{
+  FAR const struct nfattr *subattr = NFA_DATA(attr);
+  ssize_t paylen = NFA_PAYLOAD(attr);
+
+  for (; NFA_OK(subattr, paylen); subattr = NFA_NEXT(subattr, paylen))
+    {
+      switch (NFA_TYPE(subattr))
+        {
+          case CTA_TUPLE_IP:
+            netlib_ct_parse_ip(subattr, tuple);
+            break;
+
+          case CTA_TUPLE_PROTO:
+            netlib_ct_parse_proto(subattr, tuple);
+            break;
+        }
+    }
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: netlib_parse_conntrack
+ *
+ * Description:
+ *   Parse a conntrack message.
+ *
+ * Input Parameters:
+ *   nlh - The netlink message to parse.
+ *   ct  - The conntrack to fill.
+ *
+ * Returned Value:
+ *   OK on success, -errno on failure.
+ *
+ ****************************************************************************/
+
+int netlib_parse_conntrack(FAR const struct nlmsghdr *nlh, size_t len,
+                           FAR struct netlib_conntrack_s *ct)
+{
+  FAR const struct nfgenmsg *nfmsg;
+  FAR const struct nfattr   *attr;
+  ssize_t                    paylen;
+
+  if (len < sizeof(struct nlmsghdr) || len < nlh->nlmsg_len)
+    {
+      fprintf(stderr, "Error message length got %zd vs %" PRIu32 "\n",
+              len, nlh->nlmsg_len);
+      return -EINVAL;
+    }
+
+  if (NFNL_SUBSYS_ID(nlh->nlmsg_type) != NFNL_SUBSYS_CTNETLINK)
+    {
+      fprintf(stderr, "Unknown message type %" PRIx32 "\n", nlh->nlmsg_type);
+      return -ENOTSUP;
+    }
+
+  nfmsg  = NLMSG_DATA(nlh);
+  attr   = NFM_NFA(nfmsg);
+  paylen = NFM_PAYLOAD(nlh);
+
+  ct->family = nfmsg->nfgen_family;
+  ct->type   = NFNL_MSG_TYPE(nlh->nlmsg_type);
+
+  for (; NFA_OK(attr, paylen); attr = NFA_NEXT(attr, paylen))
+    {
+      switch (NFA_TYPE(attr))
+        {
+          case CTA_TUPLE_ORIG:
+            netlib_ct_parse_tuple(attr, &ct->orig);
+            break;
+          case CTA_TUPLE_REPLY:
+            netlib_ct_parse_tuple(attr, &ct->reply);
+            break;
+        }
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: netlib_get_conntrack
+ *
+ * Description:
+ *   Get the conntrack table.
+ *
+ * Input Parameters:
+ *   family - The address family to get the conntrack table for.
+ *   cb     - The callback to call for each conntrack entry.
+ *
+ * Returned Value:
+ *   The number of conntrack entries processed on success, -errno on failure.
+ *
+ ****************************************************************************/
+
+int netlib_get_conntrack(sa_family_t family, netlib_conntrack_cb_t cb)
+{
+  FAR const struct nlmsghdr *nlh;
+  struct netlib_conntrack_sendto_request_s req;
+  struct netlib_conntrack_s ct;
+  struct sockaddr_nl addr;
+  static unsigned int seqno = 0;
+  unsigned int thiseq;
+  uint8_t buf[RXBUFFER_SIZE];
+  ssize_t nrecvd;
+  size_t cnt;
+  int fd;
+  int ret;
+
+  /* Create a NetLink socket with NETLINK_NETFILTER protocol */
+
+  fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_NETFILTER);
+  if (fd < 0)
+    {
+      perror("ERROR: failed to create netlink socket");
+      return -errno;
+    }
+
+  addr.nl_family = AF_NETLINK;
+  addr.nl_pad    = 0;
+  addr.nl_pid    = getpid();
+  addr.nl_groups = 0;
+
+  if (bind(fd, (FAR const struct sockaddr *)&addr, sizeof(addr)) < 0)
+    {
+      perror("ERROR: failed to bind netlink socket");
+      ret = -errno;
+      goto errout_with_socket;
+    }
+
+  /* Initialize the request */
+
+  thiseq = ++seqno;
+
+  memset(&req, 0, sizeof(req));
+  req.hdr.nlmsg_len    = NLMSG_LENGTH(sizeof(struct nfgenmsg));
+  req.hdr.nlmsg_flags  = NLM_F_REQUEST | NLM_F_DUMP;
+  req.hdr.nlmsg_seq    = thiseq;
+  req.hdr.nlmsg_type   = NFNL_SUBSYS_CTNETLINK << 8 | IPCTNL_MSG_CT_GET;
+  req.hdr.nlmsg_pid    = addr.nl_pid;
+  req.msg.nfgen_family = family;
+  req.msg.version      = NFNETLINK_V0;
+  req.msg.res_id       = 0;
+
+  ret = send(fd, &req, req.hdr.nlmsg_len, 0);
+  if (ret < 0)
+    {
+      perror("ERROR: send() failed");
+      ret = -errno;
+      goto errout_with_socket;
+    }
+
+  /* Read the response */
+
+  for (cnt = 0; ; cnt++)
+    {
+      nrecvd = recv(fd, buf, sizeof(buf), 0);
+      if (nrecvd < 0)
+        {
+          perror("ERROR: recv() failed");
+          ret = -errno;
+          goto errout_with_socket;
+        }
+
+      nlh = (FAR struct nlmsghdr *)buf;
+
+      /* Verify the data and transfer the connection info to the caller */
+
+      if (nrecvd < sizeof(struct nlmsghdr) ||
+          nlh->nlmsg_len < sizeof(struct nlmsghdr))
+        {
+          fprintf(stderr, "ERROR: Bad message\n");
+          ret = -EIO;
+          goto errout_with_socket;
+        }
+
+      /* The sequence number in the response should match the sequence
+       * number in the request (since we created the socket, this should
+       * always be true).
+       */
+
+      if (nlh->nlmsg_seq != thiseq)
+        {
+          fprintf(stderr, "ERROR: Bad sequence number in response\n");
+          ret = -EIO;
+          goto errout_with_socket;
+        }
+
+      /* Callback the connection info to the caller */
+
+      if (nlh->nlmsg_type == NLMSG_DONE)
+        {
+          ret = cnt;
+          break;
+        }
+
+      ret = netlib_parse_conntrack(nlh, nrecvd, &ct);
+      if (ret < 0)
+        {
+          fprintf(stderr, "ERROR: failed to parse conntrack message\n");
+          goto errout_with_socket;
+        }
+
+      ret = cb(&ct);
+      if (ret < 0)
+        {
+          fprintf(stderr, "ERROR: callback failed\n");
+          goto errout_with_socket;
+        }
+    }
+
+errout_with_socket:
+  close(fd);
+  return ret;
+}

Reply via email to