You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by GitBox <gi...@apache.org> on 2022/12/28 09:19:40 UTC

[GitHub] [nuttx-apps] pkarashchenko commented on a diff in pull request #1479: apps: Add iptables compatible netlib & command.

pkarashchenko commented on code in PR #1479:
URL: https://github.com/apache/nuttx-apps/pull/1479#discussion_r1058181848


##########
netutils/netlib/netlib_iptables.c:
##########
@@ -0,0 +1,570 @@
+/****************************************************************************
+ * apps/netutils/netlib/netlib_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 <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netfilter/ip_tables.h>
+#include <nuttx/net/netfilter/netfilter.h>
+#include <nuttx/net/netfilter/nf_nat.h>
+
+#include "netutils/netlib.h"
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Following struct represents the layout of an entry with masquerade
+ * target (without matches). Mainly used to simplify entry creation.
+ */
+
+struct ipt_masquerade_entry_s
+{
+  struct ipt_entry entry;
+  struct /* compatible with IPT_FILL_ENTRY and standard/error target */
+    {
+      struct xt_entry_target target;
+      struct nf_nat_ipv4_multi_range_compat cfg;
+    } target;
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: netlib_ipt_entry_by_rulenum
+ *
+ * Description:
+ *   Get entry in repl at rulenum (1 = first) in hook.
+ *
+ * Input Parameters:
+ *   repl       - The config (to set into kernel later).
+ *   hook       - The hook of the entry.
+ *   rulenum    - The place to get.
+ *   allow_last - Whether allow to get last entry (at underflow), may insert
+ *                entry just before last entry, but don't delete last entry.
+ *
+ ****************************************************************************/
+
+static FAR struct ipt_entry *
+netlib_ipt_entry_by_rulenum(FAR struct ipt_replace *repl,
+                            enum nf_inet_hooks hook, int rulenum,
+                            bool allow_last)
+{
+  FAR struct ipt_entry *e;
+  FAR uint8_t *head = (FAR uint8_t *)repl->entries + repl->hook_entry[hook];
+  int size = repl->underflow[hook] - repl->hook_entry[hook];
+
+  ipt_entry_for_every(e, head, size)
+    {
+      if (--rulenum <= 0)
+        {
+          return e;
+        }
+    }
+
+  return (allow_last && rulenum == 1) ? e : NULL;
+}
+
+/****************************************************************************
+ * Name: netlib_ipt_insert_internal
+ *
+ * Description:
+ *   Insert an entry into config at insert_point.
+ *
+ * Input Parameters:
+ *   repl         - The config (to set into kernel later).
+ *   entry        - The entry to insert.
+ *   hook         - The hook of the entry.
+ *   insert_point - The offset to put the entry.
+ *
+ ****************************************************************************/
+
+static int netlib_ipt_insert_internal(FAR struct ipt_replace **replace,
+                                      FAR const struct ipt_entry *entry,
+                                      enum nf_inet_hooks hook,
+                                      unsigned int insert_point)
+{
+  FAR struct ipt_replace *repl = *replace;
+  FAR uint8_t *base;
+  size_t new_size;
+
+  new_size = sizeof(*repl) + repl->size + entry->next_offset;
+  repl = realloc(repl, new_size);
+  if (repl == NULL)
+    {
+      return -ENOMEM;
+    }
+
+  /* Insert new entry into entry table. */
+
+  base = (FAR uint8_t *)repl->entries;
+  memmove(base + insert_point + entry->next_offset, base + insert_point,
+          repl->size - insert_point);
+  memcpy(base + insert_point, entry, entry->next_offset);
+
+  /* Adjust metadata. */
+
+  repl->num_entries++;
+  repl->size += entry->next_offset;
+
+  /* Adjust hook_entry and underflow. */
+
+  repl->underflow[hook++] += entry->next_offset;
+  for (; hook < NF_INET_NUMHOOKS; hook++)
+    {
+      if (repl->valid_hooks & (1 << hook))
+        {
+          repl->hook_entry[hook] += entry->next_offset;
+          repl->underflow[hook] += entry->next_offset;
+        }
+    }
+
+  *replace = repl;
+  return OK;
+}
+
+/****************************************************************************
+ * Name: netlib_ipt_delete_internal
+ *
+ * Description:
+ *   Delete an entry from config.
+ *
+ * Input Parameters:
+ *   repl   - The config (to set into kernel later).
+ *   entry  - The entry to remove, should be in repl.
+ *   hook   - The hook of the entry.
+ *
+ ****************************************************************************/
+
+static void netlib_ipt_delete_internal(FAR struct ipt_replace *repl,
+                                       FAR struct ipt_entry *entry,
+                                       enum nf_inet_hooks hook)
+{
+  unsigned int delete_len = entry->next_offset;
+
+  /* Adjust metadata. */
+
+  repl->num_entries--;
+  repl->size -= delete_len;
+
+  /* Remove entry from entry table. */
+
+  memmove((FAR uint8_t *)entry, (FAR uint8_t *)entry + delete_len,
+          repl->size - ((uintptr_t)entry - (uintptr_t)repl->entries));
+
+  /* Adjust hook_entry and underflow. */
+
+  repl->underflow[hook++] -= delete_len;
+  for (; hook < NF_INET_NUMHOOKS; hook++)
+    {
+      if (repl->valid_hooks & (1 << hook))
+        {
+          repl->hook_entry[hook] -= delete_len;
+          repl->underflow[hook] -= delete_len;
+        }
+    }
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: netlib_ipt_prepare
+ *
+ * Description:
+ *   Read current config from kernel space.
+ *
+ * Input Parameters:
+ *   table  - The table name to read from.
+ *
+ * Returned Value:
+ *   The pointer to the config, or NULL if failed.
+ *   Caller must free it after use.
+ *
+ ****************************************************************************/
+
+FAR struct ipt_replace *netlib_ipt_prepare(FAR const char *table)
+{
+  struct ipt_getinfo info;
+  FAR struct ipt_get_entries *entries;
+  FAR struct ipt_replace *repl = NULL;
+  socklen_t len;
+  int sockfd;
+
+  if (table == NULL)
+    {
+      return NULL;
+    }
+
+  sockfd = socket(NET_SOCK_FAMILY, NET_SOCK_TYPE, NET_SOCK_PROTOCOL);
+  if (sockfd < 0)
+    {
+      fprintf(stderr, "Failed to create socket %d!\n", get_errno());

Review Comment:
   `get_errno` is a KERNEL level API, so I suppose just `errno` should be used in apps



##########
system/iptables/iptables.c:
##########
@@ -0,0 +1,462 @@
+/****************************************************************************
+ * 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[] =
+{
+  [NF_INET_PRE_ROUTING]  = "PREROUTING",
+  [NF_INET_LOCAL_IN]     = "INPUT",
+  [NF_INET_FORWARD]      = "FORWARD",
+  [NF_INET_LOCAL_OUT]    = "OUTPUT",
+  [NF_INET_POST_ROUTING] = "POSTROUTING",

Review Comment:
   can we have c89 compatible init?



##########
system/iptables/iptables.c:
##########
@@ -0,0 +1,462 @@
+/****************************************************************************
+ * 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[] =
+{
+  [NF_INET_PRE_ROUTING]  = "PREROUTING",
+  [NF_INET_LOCAL_IN]     = "INPUT",
+  [NF_INET_FORWARD]      = "FORWARD",
+  [NF_INET_LOCAL_OUT]    = "OUTPUT",
+  [NF_INET_POST_ROUTING] = "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;

Review Comment:
   optional
   ```suggestion
           ret = -EINVAL;
           break;
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org