diff --git a/include/nuttx/net/netfilter/ip6_tables.h b/include/nuttx/net/netfilter/ip6_tables.h index 69be30af9a..ad4675b072 100644 --- a/include/nuttx/net/netfilter/ip6_tables.h +++ b/include/nuttx/net/netfilter/ip6_tables.h @@ -71,6 +71,14 @@ #define ip6t_entry_target xt_entry_target #define ip6t_entry_match xt_entry_match +/* Foreach macro for entries. */ + +#define ip6t_entry_for_every(entry, head, size) \ + for ((entry) = (FAR struct ip6t_entry *)(head); \ + (entry) < (FAR struct ip6t_entry *)((FAR uint8_t *)(head) + (size)); \ + (entry) = (FAR struct ip6t_entry *) \ + ((FAR uint8_t *)(entry) + (entry)->next_offset)) + /**************************************************************************** * Public Types ****************************************************************************/ diff --git a/net/inet/ipv6_getsockopt.c b/net/inet/ipv6_getsockopt.c index 151fb8d610..92d2592bac 100644 --- a/net/inet/ipv6_getsockopt.c +++ b/net/inet/ipv6_getsockopt.c @@ -33,6 +33,7 @@ #include #include "mld/mld.h" +#include "netfilter/iptables.h" #include "inet/inet.h" #include "udp/udp.h" @@ -75,6 +76,13 @@ int ipv6_getsockopt(FAR struct socket *psock, int option, net_lock(); switch (option) { +#ifdef CONFIG_NET_IPTABLES + case IP6T_SO_GET_INFO: + case IP6T_SO_GET_ENTRIES: + ret = ip6t_getsockopt(psock, option, value, value_len); + break; +#endif + case IPV6_TCLASS: { FAR struct socket_conn_s *conn = psock->s_conn; diff --git a/net/inet/ipv6_setsockopt.c b/net/inet/ipv6_setsockopt.c index 13a0a36298..8a9259d565 100644 --- a/net/inet/ipv6_setsockopt.c +++ b/net/inet/ipv6_setsockopt.c @@ -33,6 +33,7 @@ #include #include "netdev/netdev.h" +#include "netfilter/iptables.h" #include "mld/mld.h" #include "inet/inet.h" #include "socket/socket.h" @@ -216,6 +217,12 @@ int ipv6_setsockopt(FAR struct socket *psock, int option, } break; +#ifdef CONFIG_NET_IPTABLES + case IP6T_SO_SET_REPLACE: + ret = ip6t_setsockopt(psock, option, value, value_len); + break; +#endif + default: nerr("ERROR: Unrecognized IPv6 option: %d\n", option); ret = -ENOPROTOOPT; diff --git a/net/netfilter/CMakeLists.txt b/net/netfilter/CMakeLists.txt index 46a13bf751..7df9a00c32 100644 --- a/net/netfilter/CMakeLists.txt +++ b/net/netfilter/CMakeLists.txt @@ -22,7 +22,13 @@ if(CONFIG_NET_IPTABLES) - set(SRCS ipt_sockopt.c) + if(CONFIG_NET_IPv4) + list(APPEND SRCS ipt_sockopt.c) + endif() + + if(CONFIG_NET_IPv6) + list(APPEND SRCS ip6t_sockopt.c) + endif() if(CONFIG_NET_NAT) list(APPEND SRCS ipt_nat.c) diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 4add680eb1..32cba6df8b 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -4,9 +4,9 @@ # config NET_IPTABLES - bool "Iptables Interface" + bool "Iptables & Ip6tables Interface" default y - depends on NET_IPv4 + depends on NET_IPv4 || NET_IPv6 depends on NET_SOCKOPTS depends on NET_NAT || NET_IPFILTER ---help--- diff --git a/net/netfilter/Make.defs b/net/netfilter/Make.defs index 505052eedb..7c26b4eb5f 100644 --- a/net/netfilter/Make.defs +++ b/net/netfilter/Make.defs @@ -22,7 +22,13 @@ ifeq ($(CONFIG_NET_IPTABLES),y) +ifeq ($(CONFIG_NET_IPv4),y) NET_CSRCS += ipt_sockopt.c +endif + +ifeq ($(CONFIG_NET_IPv6),y) +NET_CSRCS += ip6t_sockopt.c +endif ifeq ($(CONFIG_NET_NAT),y) NET_CSRCS += ipt_nat.c diff --git a/net/netfilter/ip6t_sockopt.c b/net/netfilter/ip6t_sockopt.c new file mode 100644 index 0000000000..0a4d456ff3 --- /dev/null +++ b/net/netfilter/ip6t_sockopt.c @@ -0,0 +1,440 @@ +/**************************************************************************** + * net/netfilter/ip6t_sockopt.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 +#include +#include +#include + +#include + +#include + +#include "netfilter/iptables.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define SWAP_PTR(a,b) do { FAR void *t = (a); (a) = (b); (b) = t; } while (0) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Structure to store all info we need, including table data and + * init/apply functions. + */ + +struct ip6t_table_s +{ + FAR struct ip6t_replace *repl; + FAR struct ip6t_replace *(*init_func)(void); + FAR int (*apply_func)(FAR const struct ip6t_replace *); +}; + +/* Following structs represent the layout of an entry with standard/error + * target (without matches). Mainly used to simplify initialization (entry + * creation), not suggested to use under other situations, because there + * might be matches between entry and target in data from user space. + */ + +struct ip6t_standard_entry_s +{ + struct ip6t_entry entry; + struct xt_standard_target target; +}; + +struct ip6t_error_entry_s +{ + struct ip6t_entry entry; + struct xt_error_target target; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct ip6t_table_s g_tables[] = +{ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ip6t_table_init + * + * Description: + * Try initialize the table data if not initialized. + * + ****************************************************************************/ + +static void ip6t_table_init(FAR struct ip6t_table_s *table) +{ + if (table->repl == NULL && table->init_func != NULL) + { + table->repl = table->init_func(); + } +} + +/**************************************************************************** + * Name: ip6t_table + * + * Description: + * Find table data by table name. + * + ****************************************************************************/ + +static FAR struct ip6t_table_s *ip6t_table(FAR const char *name) +{ + int i; + for (i = 0; i < nitems(g_tables); i++) + { + ip6t_table_init(&g_tables[i]); + if (g_tables[i].repl != NULL && + strcmp(g_tables[i].repl->name, name) == 0) + { + return &g_tables[i]; + } + } + + return NULL; +} + +/**************************************************************************** + * Name: ip6t_table_repl + * + * Description: + * Find table data by table name. + * + ****************************************************************************/ + +static FAR struct ip6t_replace *ip6t_table_repl(FAR const char *name) +{ + FAR struct ip6t_table_s *table = ip6t_table(name); + if (table) + { + return table->repl; + } + + return NULL; +} + +/**************************************************************************** + * Name: get_info + * + * Description: + * Fill table info into ip6t_getinfo structure. + * + * Input Parameters: + * get, len - The parameters from getsockopt. + * + ****************************************************************************/ + +static int get_info(FAR struct ip6t_getinfo *get, FAR socklen_t *len) +{ + FAR struct ip6t_replace *repl; + + if (*len != sizeof(*get)) + { + return -EINVAL; + } + + repl = ip6t_table_repl(get->name); + if (repl == NULL) + { + return -ENOENT; + } + + get->valid_hooks = repl->valid_hooks; + memcpy(get->hook_entry, repl->hook_entry, sizeof(get->hook_entry)); + memcpy(get->underflow, repl->underflow, sizeof(get->underflow)); + get->num_entries = repl->num_entries; + get->size = repl->size; + + return OK; +} + +/**************************************************************************** + * Name: get_entries + * + * Description: + * Fill entry info into ip6t_get_entries structure. + * + * Input Parameters: + * get, len - The parameters from getsockopt. + * + ****************************************************************************/ + +static int get_entries(FAR struct ip6t_get_entries *get, FAR socklen_t *len) +{ + FAR struct ip6t_replace *repl; + + if (*len < sizeof(*get) || *len != sizeof(*get) + get->size) + { + return -EINVAL; + } + + repl = ip6t_table_repl(get->name); + if (repl == NULL) + { + return -ENOENT; + } + + if (get->size != repl->size) + { + return -EAGAIN; + } + + memcpy(get->entrytable, repl->entries, get->size); + + return OK; +} + +/**************************************************************************** + * Name: check_replace + * + * Description: + * Check whether an ip6t_replace structure from user space is valid. + * + * Input Parameters: + * repl - The ip6t_replace structure to check. + * + * Returned Value: + * OK if repl is valid, otherwise -EINVAL. + * + ****************************************************************************/ + +static int check_replace(FAR const struct ip6t_replace *repl) +{ + FAR struct ip6t_entry *entry; + unsigned int entry_count = 0; + + ip6t_entry_for_every(entry, repl->entries, repl->size) + { + entry_count++; + if (entry->next_offset == 0) + { + return -EINVAL; + } + } + + if (entry_count != repl->num_entries) + { + return -EINVAL; + } + + /* May add more checks later. */ + + return OK; +} + +/**************************************************************************** + * Name: replace_entries + * + * Description: + * Apply replace data to kernel tables. + * + * Input Parameters: + * repl, len - The parameters from setsockopt. + * + ****************************************************************************/ + +static int replace_entries(FAR const struct ip6t_replace *repl, + socklen_t len) +{ + int ret; + FAR struct ip6t_replace *new_repl; + FAR struct ip6t_table_s *table = ip6t_table(repl->name); + + if (table == NULL || table->repl == NULL) + { + return -ENOENT; + } + + if (len < sizeof(*repl) || len != sizeof(*repl) + repl->size || + repl->valid_hooks != table->repl->valid_hooks) + { + return -EINVAL; + } + + /* Check replace struct before applying it. */ + + ret = check_replace(repl); + if (ret != OK) + { + return ret; + } + + new_repl = kmm_malloc(sizeof(*new_repl) + repl->size); + if (new_repl == NULL) + { + return -ENOMEM; + } + + /* Try to apply the config in replace data. */ + + ret = table->apply_func(repl); + + /* If successfully applied, save data into kernel space. */ + + if (ret == OK) + { + memcpy(new_repl, repl, sizeof(*repl) + repl->size); + SWAP_PTR(table->repl, new_repl); + } + + kmm_free(new_repl); + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ip6t_alloc_table + * + * Description: + * Allocate an initial table info with valid_hooks specified. + * Will generate a default entry with standard target for each valid hook, + * and an entry with error target at the end of entry table. + * + * Input Parameters: + * table - The name of the table. + * valid_hooks - The valid_hooks of the table, it's a bitmask. + * + * Returned Value: + * Newly generated ip6t_replace structure. + * + ****************************************************************************/ + +FAR struct ip6t_replace *ip6t_alloc_table(FAR const char *table, + unsigned int valid_hooks) +{ + FAR struct ip6t_replace *repl; + FAR struct ip6t_standard_entry_s *entry; + FAR struct ip6t_error_entry_s *error_entry; + size_t entry_size; + unsigned int hook; + unsigned int num_hooks; + + if (valid_hooks == 0 || valid_hooks > (1 << NF_INET_NUMHOOKS)) + { + return NULL; + } + + num_hooks = popcount(valid_hooks); + + /* There will be num_hooks entries with standard target (ACCEPT). */ + + entry_size = num_hooks * sizeof(struct ip6t_standard_entry_s); + + /* An error target as final entry. */ + + entry_size += sizeof(struct ip6t_error_entry_s); + + repl = kmm_zalloc(sizeof(*repl) + entry_size); + if (repl == NULL) + { + return NULL; + } + + strlcpy(repl->name, table, sizeof(repl->name)); + repl->valid_hooks = valid_hooks; + repl->num_entries = num_hooks + 1; + repl->size = entry_size; + + entry = (FAR struct ip6t_standard_entry_s *)(repl->entries); + + for (hook = 0; hook < NF_INET_NUMHOOKS; hook++) + { + if ((valid_hooks >> hook & 0x01) == 0) + { + continue; + } + + repl->hook_entry[hook] = (uintptr_t)entry - (uintptr_t)(repl->entries); + repl->underflow[hook] = repl->hook_entry[hook]; + + entry->target.verdict = -NF_ACCEPT - 1; + IPT_FILL_ENTRY(entry, XT_STANDARD_TARGET); + + entry++; + } + + error_entry = (FAR struct ip6t_error_entry_s *)entry; + strlcpy(error_entry->target.errorname, XT_ERROR_TARGET, + sizeof(error_entry->target.errorname)); + IPT_FILL_ENTRY(error_entry, XT_ERROR_TARGET); + + return repl; +} + +/**************************************************************************** + * Name: ip6t_setsockopt + * + * Description: + * setsockopt function of iptables. + * + ****************************************************************************/ + +int ip6t_setsockopt(FAR struct socket *psock, int option, + FAR const void *value, socklen_t value_len) +{ + switch (option) + { + case IP6T_SO_SET_REPLACE: + return replace_entries(value, value_len); + + default: + return -ENOPROTOOPT; + } +} + +/**************************************************************************** + * Name: ip6t_getsockopt + * + * Description: + * getsockopt function of iptables. + * + ****************************************************************************/ + +int ip6t_getsockopt(FAR struct socket *psock, int option, + FAR void *value, FAR socklen_t *value_len) +{ + switch (option) + { + case IP6T_SO_GET_INFO: + return get_info(value, value_len); + + case IP6T_SO_GET_ENTRIES: + return get_entries(value, value_len); + + default: + return -ENOPROTOOPT; + } +} diff --git a/net/netfilter/ipt_sockopt.c b/net/netfilter/ipt_sockopt.c index f4c0f48472..d1e6e0223d 100644 --- a/net/netfilter/ipt_sockopt.c +++ b/net/netfilter/ipt_sockopt.c @@ -37,7 +37,7 @@ * Pre-processor Definitions ****************************************************************************/ -#define SWAP(a,b,t) do { t = a; a = b; b = t; } while (0) +#define SWAP_PTR(a,b) do { FAR void *t = (a); (a) = (b); (b) = t; } while (0) /**************************************************************************** * Private Types @@ -242,6 +242,10 @@ static int check_replace(FAR const struct ipt_replace *repl) ipt_entry_for_every(entry, repl->entries, repl->size) { entry_count++; + if (entry->next_offset == 0) + { + return -EINVAL; + } } if (entry_count != repl->num_entries) @@ -304,9 +308,8 @@ static int replace_entries(FAR const struct ipt_replace *repl, socklen_t len) if (ret == OK) { - FAR struct ipt_replace *tmp; memcpy(new_repl, repl, sizeof(*repl) + repl->size); - SWAP(table->repl, new_repl, tmp); + SWAP_PTR(table->repl, new_repl); } kmm_free(new_repl); @@ -370,7 +373,7 @@ FAR struct ipt_replace *ipt_alloc_table(FAR const char *table, repl->num_entries = num_hooks + 1; repl->size = entry_size; - entry = (FAR struct ipt_standard_entry_s *)(repl + 1); + entry = (FAR struct ipt_standard_entry_s *)(repl->entries); for (hook = 0; hook < NF_INET_NUMHOOKS; hook++) { @@ -379,7 +382,7 @@ FAR struct ipt_replace *ipt_alloc_table(FAR const char *table, continue; } - repl->hook_entry[hook] = (uintptr_t)entry - (uintptr_t)(repl + 1); + repl->hook_entry[hook] = (uintptr_t)entry - (uintptr_t)(repl->entries); repl->underflow[hook] = repl->hook_entry[hook]; entry->target.verdict = -NF_ACCEPT - 1; diff --git a/net/netfilter/iptables.h b/net/netfilter/iptables.h index d33e3e1bcc..3a6d32911a 100644 --- a/net/netfilter/iptables.h +++ b/net/netfilter/iptables.h @@ -29,6 +29,7 @@ #include #include +#include #ifdef CONFIG_NET_IPTABLES @@ -44,8 +45,14 @@ * ****************************************************************************/ +#ifdef CONFIG_NET_IPv4 int ipt_setsockopt(FAR struct socket *psock, int option, FAR const void *value, socklen_t value_len); +#endif +#ifdef CONFIG_NET_IPv6 +int ip6t_setsockopt(FAR struct socket *psock, int option, + FAR const void *value, socklen_t value_len); +#endif /**************************************************************************** * Name: ipt_getsockopt @@ -55,8 +62,14 @@ int ipt_setsockopt(FAR struct socket *psock, int option, * ****************************************************************************/ +#ifdef CONFIG_NET_IPv4 int ipt_getsockopt(FAR struct socket *psock, int option, FAR void *value, FAR socklen_t *value_len); +#endif +#ifdef CONFIG_NET_IPv6 +int ip6t_getsockopt(FAR struct socket *psock, int option, + FAR void *value, FAR socklen_t *value_len); +#endif /**************************************************************************** * Name: ipt_alloc_table @@ -75,8 +88,14 @@ int ipt_getsockopt(FAR struct socket *psock, int option, * ****************************************************************************/ +#ifdef CONFIG_NET_IPv4 FAR struct ipt_replace *ipt_alloc_table(FAR const char *table, unsigned int valid_hooks); +#endif +#ifdef CONFIG_NET_IPv6 +FAR struct ip6t_replace *ip6t_alloc_table(FAR const char *table, + unsigned int valid_hooks); +#endif /**************************************************************************** * Name: ipt_nat_init