You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2024/04/18 01:57:07 UTC

(nuttx-apps) branch master updated (000a8bcdf -> 43a8e862e)

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

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


    from 000a8bcdf Fix nuttx coding style
     new 7e7b108ef netutils/netlib: Add netfilter conntrack functions
     new 43a8e862e apps/system: Add conntrack command

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 include/netutils/netlib.h                          |  64 ++++
 netutils/netlib/CMakeLists.txt                     |   6 +
 netutils/netlib/Makefile                           |   6 +
 netutils/netlib/netlib_conntrack.c                 | 348 +++++++++++++++++++++
 system/conntrack/Kconfig                           |  32 ++
 .../touchscreen => system/conntrack}/Make.defs     |   6 +-
 {benchmarks/osperf => system/conntrack}/Makefile   |  12 +-
 system/conntrack/conntrack.c                       | 289 +++++++++++++++++
 8 files changed, 754 insertions(+), 9 deletions(-)
 create mode 100644 netutils/netlib/netlib_conntrack.c
 create mode 100644 system/conntrack/Kconfig
 copy {examples/touchscreen => system/conntrack}/Make.defs (91%)
 copy {benchmarks/osperf => system/conntrack}/Makefile (81%)
 create mode 100644 system/conntrack/conntrack.c


(nuttx-apps) 01/02: netutils/netlib: Add netfilter conntrack functions

Posted by xi...@apache.org.
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 <we...@xiaomi.com>
AuthorDate: Mon Mar 25 16:36:30 2024 +0800

    netutils/netlib: Add netfilter conntrack functions
    
    Signed-off-by: Zhe Weng <we...@xiaomi.com>
---
 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;
+}


(nuttx-apps) 02/02: apps/system: Add conntrack command

Posted by xi...@apache.org.
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 43a8e862e0d55b4cc0b5bd71535016a9fd6b76a1
Author: Zhe Weng <we...@xiaomi.com>
AuthorDate: Thu Mar 21 21:11:52 2024 +0800

    apps/system: Add conntrack command
    
    A simple tool like Linux's 'conntrack', only supports printing NAT connections now.
    
    nuttx> conntrack -E
        [NEW] icmp  src=192.168.21.66 dst=1.2.3.4 type=8 code=0 id=26739 src=1.2.3.4 dst=192.168.11.1 type=0 code=0 id=26739
        [NEW] tcp   src=192.168.21.66 dst=1.2.3.4 sport=38446 dport=80 src=1.2.3.4 dst=192.168.11.1 sport=80 dport=38446
    [DESTROY] icmp  src=192.168.21.66 dst=1.2.3.4 type=8 code=0 id=26739 src=1.2.3.4 dst=192.168.11.1 type=0 code=0 id=26739
    
    nuttx> conntrack -L
    tcp   src=192.168.21.66 dst=1.2.3.4 sport=38446 dport=80 src=1.2.3.4 dst=192.168.11.1 sport=80 dport=38446
    icmp  src=192.168.21.66 dst=1.2.3.4 type=8 code=0 id=26739 src=1.2.3.4 dst=192.168.11.1 type=0 code=0 id=26739
    conntrack: 2 flow entries have been shown.
    
    Signed-off-by: Zhe Weng <we...@xiaomi.com>
---
 system/conntrack/Kconfig     |  32 +++++
 system/conntrack/Make.defs   |  23 ++++
 system/conntrack/Makefile    |  30 +++++
 system/conntrack/conntrack.c | 289 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 374 insertions(+)

diff --git a/system/conntrack/Kconfig b/system/conntrack/Kconfig
new file mode 100644
index 000000000..7c1a8ace3
--- /dev/null
+++ b/system/conntrack/Kconfig
@@ -0,0 +1,32 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+config SYSTEM_CONNTRACK
+	tristate "conntrack command"
+	default n
+	depends on NETLINK_NETFILTER
+	select SYSTEM_ARGTABLE3
+	---help---
+		Enable support for the 'conntrack' command, a simple tool like
+		Linux's 'conntrack', only supports printing NAT connections now.
+
+if SYSTEM_CONNTRACK
+
+config SYSTEM_CONNTRACK_PROGNAME
+	string "conntrack program name"
+	default "conntrack"
+	---help---
+		This is the name of the program that will be used when the NSH ELF
+		program is installed.
+
+config SYSTEM_CONNTRACK_PRIORITY
+	int "conntrack task priority"
+	default 100
+
+config SYSTEM_CONNTRACK_STACKSIZE
+	int "conntrack stack size"
+	default DEFAULT_TASK_STACKSIZE
+
+endif
diff --git a/system/conntrack/Make.defs b/system/conntrack/Make.defs
new file mode 100644
index 000000000..9c746c6ad
--- /dev/null
+++ b/system/conntrack/Make.defs
@@ -0,0 +1,23 @@
+############################################################################
+# apps/system/conntrack/Make.defs
+#
+# 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.
+#
+############################################################################
+
+ifneq ($(CONFIG_SYSTEM_CONNTRACK),)
+CONFIGURED_APPS += $(APPDIR)/system/conntrack
+endif
diff --git a/system/conntrack/Makefile b/system/conntrack/Makefile
new file mode 100644
index 000000000..31efea8e0
--- /dev/null
+++ b/system/conntrack/Makefile
@@ -0,0 +1,30 @@
+############################################################################
+# apps/system/conntrack/Makefile
+#
+# 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.
+#
+############################################################################
+
+include $(APPDIR)/Make.defs
+
+PROGNAME = $(CONFIG_SYSTEM_CONNTRACK_PROGNAME)
+PRIORITY = $(CONFIG_SYSTEM_CONNTRACK_PRIORITY)
+STACKSIZE = $(CONFIG_SYSTEM_CONNTRACK_STACKSIZE)
+MODULE = $(CONFIG_SYSTEM_CONNTRACK)
+
+MAINSRC = conntrack.c
+
+include $(APPDIR)/Application.mk
diff --git a/system/conntrack/conntrack.c b/system/conntrack/conntrack.c
new file mode 100644
index 000000000..c58784b0d
--- /dev/null
+++ b/system/conntrack/conntrack.c
@@ -0,0 +1,289 @@
+/****************************************************************************
+ * apps/system/conntrack/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 "argtable3.h"
+#include "netutils/netlib.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define RXBUFFER_SIZE 256
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct conntrack_args_s
+{
+  FAR struct arg_lit *dump;
+  FAR struct arg_lit *event;
+  FAR struct arg_str *family;
+  FAR struct arg_end *end;
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static volatile bool g_exiting;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: sigexit
+ ****************************************************************************/
+
+static void sigexit(int signo)
+{
+  g_exiting = true;
+}
+
+/****************************************************************************
+ * Name: proto2str
+ ****************************************************************************/
+
+static FAR const char *proto2str(uint8_t proto)
+{
+  switch (proto)
+    {
+      case IPPROTO_TCP:
+        return "tcp";
+      case IPPROTO_UDP:
+        return "udp";
+      case IPPROTO_ICMP:
+        return "icmp";
+      case IPPROTO_ICMP6:
+        return "icmp6";
+      default:
+        return "";
+    }
+}
+
+/****************************************************************************
+ * Name: conntrack_print_tuple
+ ****************************************************************************/
+
+static void conntrack_print_tuple(sa_family_t family,
+                            FAR const struct netlib_conntrack_tuple_s *tuple)
+{
+  char ipstrbuf[INET6_ADDRSTRLEN];
+
+  /* src=10.88.0.88 dst=192.168.66.66 sport=45065 dport=5001 */
+
+  inet_ntop(family, &tuple->src, ipstrbuf, INET6_ADDRSTRLEN);
+  printf("src=%s ", ipstrbuf);
+  inet_ntop(family, &tuple->dst, ipstrbuf, INET6_ADDRSTRLEN);
+  printf("dst=%s ", ipstrbuf);
+
+  switch (tuple->l4proto)
+    {
+      case IPPROTO_TCP:
+      case IPPROTO_UDP:
+        printf("sport=%" PRIu16 " dport=%" PRIu16 " ",
+               tuple->l4.tcp.sport, tuple->l4.tcp.dport);
+        break;
+      case IPPROTO_ICMP:
+      case IPPROTO_ICMP6:
+        printf("type=%" PRIu8 " code=%" PRIu8 " id=%" PRIu16 " ",
+               tuple->l4.icmp.type, tuple->l4.icmp.code, tuple->l4.icmp.id);
+        break;
+    }
+}
+
+/****************************************************************************
+ * Name: conntrack_print
+ ****************************************************************************/
+
+static int conntrack_print(FAR struct netlib_conntrack_s *ct)
+{
+  /* tcp  <orig> <reply> */
+
+  printf("%-5s ", proto2str(ct->orig.l4proto));
+  conntrack_print_tuple(ct->family, &ct->orig);
+  conntrack_print_tuple(ct->family, &ct->reply);
+  printf("\n");
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: conntrack_dump
+ ****************************************************************************/
+
+static void conntrack_dump(sa_family_t family)
+{
+  ssize_t ndumped = netlib_get_conntrack(family, conntrack_print);
+  printf("conntrack: %zd flow entries have been shown.\n", ndumped);
+}
+
+/****************************************************************************
+ * Name: conntrack_monitor_socket
+ ****************************************************************************/
+
+static int conntrack_monitor_socket(uint32_t groups)
+{
+  struct sockaddr_nl addr;
+  int fd;
+
+  /* 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 = groups;
+
+  if (bind(fd, (FAR const struct sockaddr *)&addr, sizeof(addr)) < 0)
+    {
+      perror("ERROR: failed to bind netlink socket");
+      close(fd);
+      return -errno;
+    }
+
+  return fd;
+}
+
+/****************************************************************************
+ * Name: conntrack_monitor_event
+ ****************************************************************************/
+
+static void conntrack_monitor_event(void)
+{
+  FAR const struct nlmsghdr *nlh;
+  struct netlib_conntrack_s ct;
+  uint8_t buf[RXBUFFER_SIZE];
+  ssize_t len;
+  int fd;
+
+  /* Setup exit signal handler */
+
+  g_exiting = false;
+  signal(SIGINT, sigexit);
+
+  /* Create a NetLink socket with NETLINK_NETFILTER protocol */
+
+  fd = conntrack_monitor_socket(NF_NETLINK_CONNTRACK_NEW |
+                                NF_NETLINK_CONNTRACK_DESTROY);
+  if (fd < 0)
+    {
+      return;
+    }
+
+  while ((len = read(fd, buf, sizeof(buf))) >= 0 && !g_exiting)
+    {
+      nlh = (FAR struct nlmsghdr *)buf;
+
+      if (netlib_parse_conntrack(nlh, len, &ct) < 0)
+        {
+          fprintf(stderr, "Failed to parse conntrack message\n");
+          continue;
+        }
+
+      /* Only event log needs to print type prefix */
+
+      switch (ct.type)
+        {
+          case IPCTNL_MSG_CT_NEW:
+            printf("    [NEW] ");
+            break;
+
+          case IPCTNL_MSG_CT_DELETE:
+            printf("[DESTROY] ");
+            break;
+
+          default:
+            printf("[UNKNOWN] ");
+            break;
+        }
+
+      /* Print the remaining line */
+
+      conntrack_print(&ct);
+    }
+
+  close(fd);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int main(int argc, FAR char *argv[])
+{
+  struct conntrack_args_s args;
+  sa_family_t family = AF_INET;
+  int nerrors;
+
+  args.dump   = arg_lit0("L", "dump", "List connection tracking");
+  args.event  = arg_lit0("E", "event", "Display a real-time event log");
+  args.family = arg_str0("f", "family", "PROTO", "Specify L3 (ipv4, ipv6) "
+                         "protocol, only for dump option (default ipv4)");
+  args.end    = arg_end(3);
+
+  nerrors = arg_parse(argc, argv, (FAR void **)&args);
+  if (nerrors != 0 || args.dump->count + args.event->count == 0)
+    {
+      arg_print_errors(stdout, args.end, argv[0]);
+      printf("Usage:\n");
+      arg_print_glossary(stdout, (FAR void **)&args, NULL);
+      goto out;
+    }
+
+  if (args.family->count != 0)
+    {
+      if (strncmp(args.family->sval[0], "ipv6", 4) == 0)
+        {
+          family = AF_INET6;
+        }
+    }
+
+  if (args.dump->count != 0)
+    {
+      conntrack_dump(family);
+    }
+
+  if (args.event->count != 0)
+    {
+      conntrack_monitor_event();
+    }
+
+out:
+  arg_freetable((FAR void **)&args, 1);
+  return 0;
+}