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 19958f042808af5c7471e17a9557f6e1ce6f3bae
Author: Zhe Weng <[email protected]>
AuthorDate: Tue Dec 13 18:10:43 2022 +0800

    apps/system: Add iptables command.
    
    Signed-off-by: Zhe Weng <[email protected]>
---
 system/iptables/Kconfig    |  32 ++++
 system/iptables/Make.defs  |  23 +++
 system/iptables/Makefile   |  30 +++
 system/iptables/iptables.c | 459 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 544 insertions(+)

diff --git a/system/iptables/Kconfig b/system/iptables/Kconfig
new file mode 100644
index 000000000..e3f24b333
--- /dev/null
+++ b/system/iptables/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_IPTABLES
+       tristate "'iptables' command"
+       default n
+       depends on NET_IPTABLES
+       select NETUTILS_NETLIB
+       select SYSTEM_ARGTABLE3
+       ---help---
+               Enable support for the 'iptables' command.
+
+if SYSTEM_IPTABLES
+
+config SYSTEM_IPTABLES_PROGNAME
+       string "iptables program name"
+       default "iptables"
+       ---help---
+               This is the name of the program that will be used when the NSH 
ELF
+               program is installed.
+
+config SYSTEM_IPTABLES_PRIORITY
+       int "iptables task priority"
+       default 100
+
+config SYSTEM_IPTABLES_STACKSIZE
+       int "iptables stack size"
+       default DEFAULT_TASK_STACKSIZE
+
+endif
diff --git a/system/iptables/Make.defs b/system/iptables/Make.defs
new file mode 100644
index 000000000..52469d42b
--- /dev/null
+++ b/system/iptables/Make.defs
@@ -0,0 +1,23 @@
+############################################################################
+# apps/system/iptables/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_IPTABLES),)
+CONFIGURED_APPS += $(APPDIR)/system/iptables
+endif
diff --git a/system/iptables/Makefile b/system/iptables/Makefile
new file mode 100644
index 000000000..5950333f8
--- /dev/null
+++ b/system/iptables/Makefile
@@ -0,0 +1,30 @@
+############################################################################
+# apps/system/iptables/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_IPTABLES_PROGNAME)
+PRIORITY = $(CONFIG_SYSTEM_IPTABLES_PRIORITY)
+STACKSIZE = $(CONFIG_SYSTEM_IPTABLES_STACKSIZE)
+MODULE = $(CONFIG_SYSTEM_IPTABLES)
+
+MAINSRC = iptables.c
+
+include $(APPDIR)/Application.mk
diff --git a/system/iptables/iptables.c b/system/iptables/iptables.c
new file mode 100644
index 000000000..edba92daa
--- /dev/null
+++ b/system/iptables/iptables.c
@@ -0,0 +1,459 @@
+/****************************************************************************
+ * apps/system/iptables/iptables.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 <arpa/inet.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <nuttx/net/netfilter/ip_tables.h>
+#include <nuttx/net/netfilter/nf_nat.h>
+
+#include "argtable3.h"
+#include "netutils/netlib.h"
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* TODO: Our getopt(), which argtable3 depends, does not support nonoptions
+ * in the middle, so commands like 'iptables -I chain rulenum -j MASQUERADE'
+ * cannot be supported, because the 'rulenum' will stop getopt() logic and
+ * the -j option will never be parsed. Although we write 'rulenum' option
+ * here, it may not work well, but it will work when our getopt() is updated.
+ */
+
+struct iptables_args_s
+{
+  FAR struct arg_str *table;
+
+  FAR struct arg_str *append_chain;
+  FAR struct arg_str *insert_chain;
+  FAR struct arg_str *delete_chain;
+  FAR struct arg_str *flush_chain;
+  FAR struct arg_str *list_chain;
+
+  FAR struct arg_int *rulenum;
+
+  FAR struct arg_str *target;
+  FAR struct arg_str *outifname;
+
+  FAR struct arg_end *end;
+};
+
+enum iptables_command_e
+{
+  COMMAND_APPEND,
+  COMMAND_INSERT,
+  COMMAND_DELETE,
+  COMMAND_FLUSH,
+  COMMAND_LIST,
+  COMMAND_NUM,
+};
+
+struct iptables_command_s
+{
+  enum iptables_command_e cmd;
+  enum nf_inet_hooks hook;
+  int rulenum;
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static FAR const char *g_hooknames[] =
+{
+  "PREROUTING", "INPUT", "FORWARD", "OUTPUT", "POSTROUTING"
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: iptables_showusage
+ *
+ * Description:
+ *   Show usage of the demo program
+ *
+ ****************************************************************************/
+
+static void iptables_showusage(FAR const char *progname, FAR void** argtable)
+{
+  printf("USAGE: %s -t table -[AD] chain rule-specification\n", progname);
+  printf("       %s -t table -I chain [rulenum] rule-specification\n",
+         progname);
+  printf("       %s -t table -D chain rulenum\n", progname);
+  printf("       %s -t table -[FL] [chain]\n", progname);
+  printf("iptables command:\n");
+  arg_print_glossary(stdout, argtable, NULL);
+}
+
+/****************************************************************************
+ * Name: iptables_parse_hook
+ *
+ * Description:
+ *   Get hook from string
+ *
+ ****************************************************************************/
+
+static enum nf_inet_hooks iptables_parse_hook(FAR const char *str)
+{
+  unsigned int hook;
+
+  if (str == NULL || strlen(str) == 0) /* Might be no input (-F/-L). */
+    {
+      return NF_INET_NUMHOOKS;
+    }
+
+  for (hook = 0; hook < NF_INET_NUMHOOKS; hook++)
+    {
+      if (strcmp(str, g_hooknames[hook]) == 0)
+        {
+          return hook;
+        }
+    }
+
+  printf("Failed to parse hook: %s, default to %d\n", str, NF_INET_NUMHOOKS);
+  return NF_INET_NUMHOOKS; /* Failed to parse. */
+}
+
+/****************************************************************************
+ * Name: iptables_command
+ *
+ * Description:
+ *   Get command from arg list
+ *
+ ****************************************************************************/
+
+static struct iptables_command_s
+iptables_command(FAR const struct iptables_args_s *args)
+{
+  struct iptables_command_s ret =
+    {
+      COMMAND_NUM,
+      NF_INET_NUMHOOKS,
+      -1
+    };
+
+  if (args->rulenum->count > 0)
+    {
+      ret.rulenum = *args->rulenum->ival;
+    }
+
+  /* Flush & list with no chain specified will result in count > 0 and empty
+   * string in sval[0], then we map the hook to NF_INET_NUMHOOKS.
+   */
+
+  if (args->flush_chain->count > 0)
+    {
+      ret.cmd = COMMAND_FLUSH;
+      ret.hook = iptables_parse_hook(args->flush_chain->sval[0]);
+    }
+  else if (args->list_chain->count > 0)
+    {
+      ret.cmd = COMMAND_LIST;
+      ret.hook = iptables_parse_hook(args->list_chain->sval[0]);
+    }
+  else if (args->append_chain->count > 0)
+    {
+      ret.cmd = COMMAND_APPEND;
+      ret.hook = iptables_parse_hook(args->append_chain->sval[0]);
+    }
+  else if (args->insert_chain->count > 0)
+    {
+      ret.cmd = COMMAND_INSERT;
+      ret.hook = iptables_parse_hook(args->insert_chain->sval[0]);
+      ret.rulenum = MAX(ret.rulenum, 1);
+    }
+  else if (args->delete_chain->count > 0)
+    {
+      ret.cmd = COMMAND_DELETE;
+      ret.hook = iptables_parse_hook(args->delete_chain->sval[0]);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: iptables_addr2str
+ *
+ * Description:
+ *   Format address and mask to ip/preflen string.
+ *
+ ****************************************************************************/
+
+static FAR char *iptables_addr2str(struct in_addr addr, struct in_addr msk,
+                                   FAR char *buf, size_t bufflen)
+{
+  unsigned int preflen = popcount(msk.s_addr);
+  if (preflen != 0)
+    {
+      snprintf(buf, bufflen, "%s/%d", inet_ntoa(addr), preflen);
+    }
+  else
+    {
+      snprintf(buf, bufflen, "anywhere");
+    }
+
+  return buf;
+}
+
+/****************************************************************************
+ * Name: iptables_print_chain
+ *
+ * Description:
+ *   Print all rules in a chain
+ *
+ ****************************************************************************/
+
+static void iptables_print_chain(FAR const struct ipt_replace *repl,
+                                 enum nf_inet_hooks hook)
+{
+  const char fmt[] = "%12s %4s %4s %16s %16s\n";
+  char src[INET_ADDRSTRLEN];
+  char dst[INET_ADDRSTRLEN];
+  FAR struct ipt_entry *entry;
+  FAR uint8_t *head = (FAR uint8_t *)repl->entries + repl->hook_entry[hook];
+  int size = repl->underflow[hook] - repl->hook_entry[hook];
+
+  printf("Chain %s\n", g_hooknames[hook]);
+  printf(fmt, "target", "idev", "odev", "source", "destination");
+
+  ipt_entry_for_every(entry, head, size)
+    {
+      FAR struct xt_entry_target *target = IPT_TARGET(entry);
+      printf(fmt, target->u.user.name, entry->ip.iniface, entry->ip.outiface,
+        iptables_addr2str(entry->ip.src, entry->ip.smsk, src, sizeof(src)),
+        iptables_addr2str(entry->ip.dst, entry->ip.dmsk, dst, sizeof(dst)));
+    }
+
+  printf("\n");
+}
+
+/****************************************************************************
+ * Name: iptables_list
+ *
+ * Description:
+ *   List all rules in a table
+ *
+ ****************************************************************************/
+
+static int iptables_list(FAR const char *table, enum nf_inet_hooks hook)
+{
+  FAR struct ipt_replace *repl = netlib_ipt_prepare(table);
+  unsigned int cur_hook;
+
+  if (repl == NULL)
+    {
+      printf("Failed to read table %s from kernel!\n", table);
+      return -EIO;
+    }
+
+  for (cur_hook = 0; cur_hook < NF_INET_NUMHOOKS; cur_hook++)
+    {
+      if ((repl->valid_hooks & (1 << cur_hook)) != 0 &&
+          (hook == NF_INET_NUMHOOKS || hook == cur_hook))
+        {
+          iptables_print_chain(repl, cur_hook);
+        }
+    }
+
+  free(repl);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: iptables_nat_command
+ *
+ * Description:
+ *   Do a NAT command
+ *
+ ****************************************************************************/
+
+static int iptables_nat_command(FAR const struct iptables_command_s *cmd,
+                                FAR const char *ifname)
+{
+  FAR struct ipt_replace *repl = netlib_ipt_prepare(TABLE_NAME_NAT);
+  FAR struct ipt_entry *entry  = NULL;
+  int ret;
+
+  if (repl == NULL)
+    {
+      printf("Failed to read table '" TABLE_NAME_NAT "' from kernel!\n");
+      return -EIO;
+    }
+
+  if (ifname && strlen(ifname) > 0) /* No ifname if we delete with rulenum. */
+    {
+      entry = netlib_ipt_masquerade_entry(ifname);
+      if (entry == NULL)
+        {
+          printf("Failed to prepare entry for dev %s!\n", ifname);
+          ret = -ENOMEM;
+          goto errout_with_repl;
+        }
+    }
+
+  switch (cmd->cmd)
+    {
+      case COMMAND_APPEND:
+        ret = netlib_ipt_append(&repl, entry, cmd->hook);
+        break;
+
+      case COMMAND_INSERT:
+        ret = netlib_ipt_insert(&repl, entry, cmd->hook, cmd->rulenum);
+        break;
+
+      case COMMAND_DELETE:
+        ret = netlib_ipt_delete(repl, entry, cmd->hook, cmd->rulenum);
+        break;
+
+      default: /* Other commands should not call into this function. */
+        ret = -EINVAL;
+        break;
+    }
+
+  if (ret == OK)
+    {
+      ret = netlib_ipt_commit(repl);
+    }
+
+  if (entry)
+    {
+      free(entry);
+    }
+
+errout_with_repl:
+  free(repl);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: iptables_nat
+ *
+ * Description:
+ *   Apply rules for NAT
+ *
+ ****************************************************************************/
+
+static int iptables_nat(FAR const struct iptables_args_s *args)
+{
+  struct iptables_command_s cmd = iptables_command(args);
+
+  switch (cmd.cmd)
+    {
+      case COMMAND_FLUSH:
+        return netlib_ipt_flush(TABLE_NAME_NAT, cmd.hook);
+
+      case COMMAND_LIST:
+        return iptables_list(TABLE_NAME_NAT, cmd.hook);
+
+      case COMMAND_APPEND:
+      case COMMAND_INSERT:
+      case COMMAND_DELETE:
+        if (args->outifname->count == 0 &&
+            !(cmd.cmd == COMMAND_DELETE && cmd.rulenum > 0))
+          {
+            printf("Table '" TABLE_NAME_NAT "' needs an out interface!\n");
+            return -EINVAL;
+          }
+
+        if (args->target->count > 0 &&
+            strcmp(args->target->sval[0], XT_MASQUERADE_TARGET))
+          {
+            printf("Only target '" XT_MASQUERADE_TARGET
+                  "' is supported for table '" TABLE_NAME_NAT "'!\n");
+            return -EINVAL;
+          }
+
+        return iptables_nat_command(&cmd, args->outifname->sval[0]);
+
+      default:
+        printf("No supported command specified!\n");
+        return -EINVAL;
+    }
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int main(int argc, FAR char *argv[])
+{
+  struct iptables_args_s args;
+  int nerrors;
+
+  args.table        = arg_str1("t", "table", "table", "table to manipulate");
+
+  args.append_chain = arg_str0("A", "append", "chain",
+                               "Append a rule to chain");
+  args.insert_chain = arg_str0("I", "insert", "chain",
+                               "Insert a rule to chain at rulenum "
+                               "(default = 1)");
+  args.delete_chain = arg_str0("D", "delete", "chain",
+                               "Delete matching rule from chain");
+  args.flush_chain  = arg_str0("F", "flush", "chain",
+                               "Delete all rules in chain or all chains");
+  args.list_chain   = arg_str0("L", "list", "chain",
+                               "List all rules in chain or all chains");
+
+  args.rulenum      = arg_int0(NULL, NULL, "rulenum", "Rule num (1=first)");
+
+  args.target       = arg_str0("j", "jump", "target", "target for rule");
+  args.outifname    = arg_str0("o", "out-interface", "dev",
+                               "output network interface name");
+
+  args.end          = arg_end(1);
+
+  /* The chain of -F or -L is optional. */
+
+  args.flush_chain->hdr.flag |= ARG_HASOPTVALUE;
+  args.list_chain->hdr.flag  |= ARG_HASOPTVALUE;
+
+  nerrors = arg_parse(argc, argv, (FAR void**)&args);
+  if (nerrors != 0)
+    {
+      arg_print_errors(stderr, args.end, argv[0]);
+      iptables_showusage(argv[0], (FAR void**)&args);
+      return 0;
+    }
+
+  if (strcmp(args.table->sval[0], TABLE_NAME_NAT) == 0)
+    {
+      int ret = iptables_nat(&args);
+      if (ret < 0)
+        {
+          printf("iptables got error on NAT: %d!\n", ret);
+        }
+
+      return ret;
+    }
+
+  printf("Unknown table: %s\n", args.table->sval[0]);
+  return 0;
+}

Reply via email to