diff --git a/include/netutils/netlib.h b/include/netutils/netlib.h index 4975bd7f2..2f4dee012 100644 --- a/include/netutils/netlib.h +++ b/include/netutils/netlib.h @@ -390,8 +390,11 @@ int netlib_obtain_ipv6addr(FAR const char *ifname); struct ipt_replace; /* Forward reference */ struct ipt_entry; /* Forward reference */ +struct ip6t_replace; /* Forward reference */ +struct ip6t_entry; /* Forward reference */ enum nf_inet_hooks; /* Forward reference */ +# ifdef CONFIG_NET_IPv4 FAR struct ipt_replace *netlib_ipt_prepare(FAR const char *table); int netlib_ipt_commit(FAR const struct ipt_replace *repl); int netlib_ipt_flush(FAR const char *table, enum nf_inet_hooks hook); @@ -409,15 +412,40 @@ int netlib_ipt_delete(FAR struct ipt_replace *repl, int netlib_ipt_fillifname(FAR struct ipt_entry *entry, FAR const char *inifname, FAR const char *outifname); -# ifdef CONFIG_NET_NAT +# ifdef CONFIG_NET_NAT FAR struct ipt_entry *netlib_ipt_masquerade_entry(FAR const char *ifname); -# endif -# ifdef CONFIG_NET_IPFILTER +# endif +# ifdef CONFIG_NET_IPFILTER FAR struct ipt_entry *netlib_ipt_filter_entry(FAR const char *target, int verdict, uint8_t match_proto); -# endif -#endif +# endif +# endif /* CONFIG_NET_IPv4 */ +# ifdef CONFIG_NET_IPv6 +FAR struct ip6t_replace *netlib_ip6t_prepare(FAR const char *table); +int netlib_ip6t_commit(FAR const struct ip6t_replace *repl); +int netlib_ip6t_flush(FAR const char *table, enum nf_inet_hooks hook); +int netlib_ip6t_policy(FAR const char *table, enum nf_inet_hooks hook, + int verdict); +int netlib_ip6t_append(FAR struct ip6t_replace **repl, + FAR const struct ip6t_entry *entry, + enum nf_inet_hooks hook); +int netlib_ip6t_insert(FAR struct ip6t_replace **repl, + FAR const struct ip6t_entry *entry, + enum nf_inet_hooks hook, int rulenum); +int netlib_ip6t_delete(FAR struct ip6t_replace *repl, + FAR const struct ip6t_entry *entry, + enum nf_inet_hooks hook, int rulenum); +int netlib_ip6t_fillifname(FAR struct ip6t_entry *entry, + FAR const char *inifname, + FAR const char *outifname); +# ifdef CONFIG_NET_IPFILTER +FAR struct ip6t_entry *netlib_ip6t_filter_entry(FAR const char *target, + int verdict, + uint8_t match_proto); +# endif +# endif /* CONFIG_NET_IPv6 */ +#endif /* CONFIG_NET_IPTABLES */ #ifdef CONFIG_NETLINK_NETFILTER /* Netfilter connection tracking support */ diff --git a/netutils/netlib/CMakeLists.txt b/netutils/netlib/CMakeLists.txt index bb1bacbb4..73c20227e 100644 --- a/netutils/netlib/CMakeLists.txt +++ b/netutils/netlib/CMakeLists.txt @@ -73,6 +73,9 @@ if(CONFIG_NETUTILS_NETLIB) if(CONFIG_NETLINK_ROUTE) list(APPEND SRCS netlib_getnbtab.c) endif() + if(CONFIG_NET_IPTABLES) + list(APPEND SRCS netlib_ip6tables.c) + endif() endif() # Device support diff --git a/netutils/netlib/Makefile b/netutils/netlib/Makefile index 9b0525924..4e3e00d2b 100644 --- a/netutils/netlib/Makefile +++ b/netutils/netlib/Makefile @@ -74,6 +74,9 @@ endif ifeq ($(CONFIG_NETLINK_ROUTE),y) CSRCS += netlib_getnbtab.c endif +ifeq ($(CONFIG_NET_IPTABLES),y) +CSRCS += netlib_ip6tables.c +endif endif # Device support diff --git a/netutils/netlib/netlib_ip6tables.c b/netutils/netlib/netlib_ip6tables.c new file mode 100644 index 000000000..59915077b --- /dev/null +++ b/netutils/netlib/netlib_ip6tables.c @@ -0,0 +1,753 @@ +/**************************************************************************** + * apps/netutils/netlib/netlib_ip6tables.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 "netutils/netlib.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define IP6T_FILL_MATCH(e, match_name) \ + do \ + { \ + strlcpy((e)->match.u.user.name, (match_name), \ + sizeof((e)->match.u.user.name)); \ + (e)->match.u.match_size = offsetof(typeof(*(e)), target) - \ + offsetof(typeof(*(e)), match); \ + } \ + while(0) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct ip6t_filter_entry_s +{ + struct ip6t_entry entry; + + /* Compatible with ACCEPT/DROP/REJECT target. */ + + struct xt_standard_target target; +}; + +struct ip6t_filter_tcp_entry_s +{ + struct ip6t_entry entry; + struct xt_entry_match match; + struct xt_tcp tcp; + struct xt_standard_target target; +}; + +struct ip6t_filter_udp_entry_s +{ + struct ip6t_entry entry; + struct xt_entry_match match; + struct xt_udp udp; + struct xt_standard_target target; +}; + +struct ip6t_filter_icmp_entry_s +{ + struct ip6t_entry entry; + struct xt_entry_match match; + struct ip6t_icmp icmp; + struct xt_standard_target target; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: netlib_ip6t_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 ip6t_entry * +netlib_ip6t_entry_by_rulenum(FAR struct ip6t_replace *repl, + enum nf_inet_hooks hook, int rulenum, + bool allow_last) +{ + FAR struct ip6t_entry *e; + FAR uint8_t *head = (FAR uint8_t *)repl->entries + repl->hook_entry[hook]; + int size = repl->underflow[hook] - repl->hook_entry[hook]; + + ip6t_entry_for_every(e, head, size) + { + if (--rulenum <= 0) + { + return e; + } + } + + return (allow_last && rulenum == 1) ? e : NULL; +} + +/**************************************************************************** + * Name: netlib_ip6t_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_ip6t_insert_internal(FAR struct ip6t_replace **replace, + FAR const struct ip6t_entry *entry, + enum nf_inet_hooks hook, + unsigned int insert_point) +{ + FAR struct ip6t_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_ip6t_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_ip6t_delete_internal(FAR struct ip6t_replace *repl, + FAR struct ip6t_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_ip6t_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 ip6t_replace *netlib_ip6t_prepare(FAR const char *table) +{ + struct ip6t_getinfo info; + FAR struct ip6t_get_entries *entries; + FAR struct ip6t_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", errno); + return NULL; + } + + strlcpy(info.name, table, sizeof(info.name)); + len = sizeof(info); + + if (getsockopt(sockfd, IPPROTO_IPV6, IP6T_SO_GET_INFO, &info, &len) < 0) + { + fprintf(stderr, "Failed to get info for table %s %d!\n", table, errno); + goto errout; + } + + len = sizeof(*entries) + info.size; + entries = malloc(len); + if (entries == NULL) + { + goto errout; + } + + strlcpy(entries->name, table, sizeof(entries->name)); + entries->size = info.size; + if (getsockopt(sockfd, IPPROTO_IPV6, IP6T_SO_GET_ENTRIES, entries, &len) + < 0) + { + fprintf(stderr, "Failed to get entries for table %s %d!\n", + table, errno); + goto errout_with_entries; + } + + repl = malloc(sizeof(*repl) + info.size); + if (repl == NULL) + { + goto errout_with_entries; + } + + strlcpy(repl->name, table, sizeof(repl->name)); + + repl->valid_hooks = info.valid_hooks; + repl->num_entries = info.num_entries; + repl->size = info.size; + repl->num_counters = 0; + repl->counters = NULL; + + memcpy(repl->hook_entry, info.hook_entry, sizeof(repl->hook_entry)); + memcpy(repl->underflow, info.underflow, sizeof(repl->underflow)); + memcpy(repl->entries, entries->entrytable, info.size); + +errout_with_entries: + free(entries); + +errout: + close(sockfd); + return repl; +} + +/**************************************************************************** + * Name: netlib_ip6t_commit + * + * Description: + * Set config into kernel space. + * + * Input Parameters: + * repl - The config to commit. + * + ****************************************************************************/ + +int netlib_ip6t_commit(FAR const struct ip6t_replace *repl) +{ + int ret; + int sockfd; + + if (repl == NULL) + { + return -EINVAL; + } + + sockfd = socket(NET_SOCK_FAMILY, NET_SOCK_TYPE, NET_SOCK_PROTOCOL); + if (sockfd < 0) + { + fprintf(stderr, "Failed to create socket %d!\n", errno); + return -errno; + } + + ret = setsockopt(sockfd, IPPROTO_IPV6, IP6T_SO_SET_REPLACE, repl, + sizeof(*repl) + repl->size); + if (ret < 0) + { + ret = -errno; + fprintf(stderr, "Failed to commit %d!\n", ret); + } + + close(sockfd); + return ret; +} + +/**************************************************************************** + * Name: netlib_ip6t_flush + * + * Description: + * Flush all config in the table. + * + * Input Parameters: + * table - The table name to flush. + * hook - The hook to flush, NF_INET_NUMHOOKS for all. + * + ****************************************************************************/ + +int netlib_ip6t_flush(FAR const char *table, enum nf_inet_hooks hook) +{ + FAR struct ip6t_replace *repl = netlib_ip6t_prepare(table); + unsigned int cur_hook; + int ret; + + if (repl == NULL) + { + fprintf(stderr, "Failed to read table %s from kernel!\n", table); + return -EIO; + } + + if (hook != NF_INET_NUMHOOKS && (repl->valid_hooks & (1 << hook)) == 0) + { + fprintf(stderr, "Invalid hook number %d for table %s!\n", hook, table); + ret = -EINVAL; + goto errout; + } + + 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)) + { + /* Remove all user entries in current hook. */ + + while (repl->underflow[cur_hook] > repl->hook_entry[cur_hook]) + { + ret = netlib_ip6t_delete(repl, NULL, cur_hook, 1); + if (ret < 0) + { + goto errout; + } + } + } + } + + ret = netlib_ip6t_commit(repl); + +errout: + free(repl); + return ret; +} + +/**************************************************************************** + * Name: netlib_ip6t_policy + * + * Description: + * Set policy for the table. It's a common operation, but may only take + * effect on filter-related tables. + * + * Input Parameters: + * table - The table name to set policy. + * hook - The hook to set policy. + * verdict - The verdict to set. + * + ****************************************************************************/ + +int netlib_ip6t_policy(FAR const char *table, enum nf_inet_hooks hook, + int verdict) +{ + FAR struct ip6t_replace *repl = netlib_ip6t_prepare(table); + FAR struct ip6t_entry *entry; + FAR struct xt_standard_target *target; + int ret; + + if (repl == NULL) + { + fprintf(stderr, "Failed to read table %s from kernel!\n", table); + return -EIO; + } + + if ((repl->valid_hooks & (1 << hook)) == 0) + { + fprintf(stderr, "Invalid hook number %d for table %s!\n", hook, table); + ret = -EINVAL; + goto errout; + } + + /* The underflow entry is the default policy of the chain. */ + + entry = (FAR struct ip6t_entry *)((uintptr_t)repl->entries + + repl->underflow[hook]); + target = (FAR struct xt_standard_target *)IP6T_TARGET(entry); + if (strcmp(target->target.u.user.name, XT_STANDARD_TARGET) != 0) + { + fprintf(stderr, "Wrong target %s!\n", target->target.u.user.name); + ret = -EINVAL; + goto errout; + } + + target->verdict = verdict; + + ret = netlib_ip6t_commit(repl); + +errout: + free(repl); + return ret; +} + +/**************************************************************************** + * Name: netlib_ip6t_append + * + * Description: + * Append an entry into config, will be put to as last config of the chain + * corresponding to hook. + * + * Input Parameters: + * repl - The config (to set into kernel later). + * entry - The entry to append. + * hook - The hook of the entry. + * + ****************************************************************************/ + +int netlib_ip6t_append(FAR struct ip6t_replace **repl, + FAR const struct ip6t_entry *entry, + enum nf_inet_hooks hook) +{ + if (repl == NULL || *repl == NULL || entry == NULL) + { + return -EINVAL; + } + + if (((*repl)->valid_hooks & (1 << hook)) == 0) + { + fprintf(stderr, "Not valid hook %d for this table!\n", hook); + return -EINVAL; + } + + return netlib_ip6t_insert_internal(repl, entry, hook, + (*repl)->underflow[hook]); +} + +/**************************************************************************** + * Name: netlib_ip6t_insert + * + * Description: + * Insert an entry into config, will be put to as first config of the chain + * corresponding to hook. + * + * Input Parameters: + * repl - The config (to set into kernel later). + * entry - The entry to insert. + * hook - The hook of the entry. + * rulenum - The place to insert, 1 = first. + * + ****************************************************************************/ + +int netlib_ip6t_insert(FAR struct ip6t_replace **repl, + FAR const struct ip6t_entry *entry, + enum nf_inet_hooks hook, int rulenum) +{ + FAR struct ip6t_entry *e; + + if (repl == NULL || *repl == NULL || entry == NULL || rulenum <= 0) + { + fprintf(stderr, "Not valid param %p, %p, rulenum %d!\n", + repl, entry, rulenum); + return -EINVAL; + } + + if (((*repl)->valid_hooks & (1 << hook)) == 0) + { + fprintf(stderr, "Not valid hook %d for this table!\n", hook); + return -EINVAL; + } + + e = netlib_ip6t_entry_by_rulenum(*repl, hook, rulenum, true); + if (e == NULL) + { + fprintf(stderr, "Rulenum %d too big!\n", rulenum); + return -EINVAL; + } + + return netlib_ip6t_insert_internal(repl, entry, hook, + (uintptr_t)e - (uintptr_t)(*repl)->entries); +} + +/**************************************************************************** + * Name: netlib_ip6t_delete + * + * Description: + * Delete an entry from config. + * + * Input Parameters: + * repl - The config (to set into kernel later). + * entry - The entry to delete, choose either entry or rulenum. + * hook - The hook of the entry. + * rulenum - The place to delete, 1 = first, set entry to NULL to use this. + * + ****************************************************************************/ + +int netlib_ip6t_delete(FAR struct ip6t_replace *repl, + FAR const struct ip6t_entry *entry, + enum nf_inet_hooks hook, int rulenum) +{ + FAR struct ip6t_entry *e; + FAR uint8_t *head; + int size; + + if (repl == NULL || (entry == NULL && rulenum <= 0)) + { + fprintf(stderr, "Not valid param %p, %p, rulenum %d!\n", + repl, entry, rulenum); + return -EINVAL; + } + + if ((repl->valid_hooks & (1 << hook)) == 0) + { + fprintf(stderr, "Not valid hook %d for this table!\n", hook); + return -EINVAL; + } + + if (entry == NULL) /* Use rulenum instead. */ + { + e = netlib_ip6t_entry_by_rulenum(repl, hook, rulenum, false); + if (e == NULL) + { + fprintf(stderr, "Rulenum %d too big!\n", rulenum); + return -EINVAL; + } + + netlib_ip6t_delete_internal(repl, e, hook); + return OK; + } + + head = (FAR uint8_t *)repl->entries + repl->hook_entry[hook]; + size = repl->underflow[hook] - repl->hook_entry[hook]; + ip6t_entry_for_every(e, head, size) + { + if (e->next_offset == entry->next_offset && + e->target_offset == entry->target_offset && + memcmp(&e->ipv6, &entry->ipv6, sizeof(struct ip6t_ip6)) == 0 && + memcmp(&e->elems, &entry->elems, + e->next_offset - offsetof(struct ip6t_entry, elems)) == 0) + { + netlib_ip6t_delete_internal(repl, e, hook); + return OK; + } + } + + return -ENOENT; +} + +/**************************************************************************** + * Name: netlib_ip6t_fillifname + * + * Description: + * Fill inifname and outifname into entry. + * + * Input Parameters: + * entry - The entry to fill. + * inifname - The input device name, NULL for no change. + * outifname - The output device name, NULL for no change. + * + ****************************************************************************/ + +int netlib_ip6t_fillifname(FAR struct ip6t_entry *entry, + FAR const char *inifname, + FAR const char *outifname) +{ + size_t len; + + if (entry == NULL) + { + return -EINVAL; + } + + if (inifname != NULL) + { + len = strlen(inifname); + if (len + 1 > IFNAMSIZ) + { + fprintf(stderr, "Too long inifname %s!\n", inifname); + return -EINVAL; + } + + strlcpy(entry->ipv6.iniface, inifname, sizeof(entry->ipv6.iniface)); + memset(entry->ipv6.iniface_mask, 0xff, len + 1); + } + + if (outifname != NULL) + { + len = strlen(outifname); + if (len + 1 > IFNAMSIZ) + { + fprintf(stderr, "Too long outifname %s!\n", outifname); + return -EINVAL; + } + + strlcpy(entry->ipv6.outiface, outifname, sizeof(entry->ipv6.outiface)); + memset(entry->ipv6.outiface_mask, 0xff, len + 1); + } + + return OK; +} + +/**************************************************************************** + * Name: netlib_ip6t_filter_entry + * + * Description: + * Alloc an entry with filter target. + * + * Input Parameters: + * target - The target name to apply. + * verdict - The verdict to set, compatible with reject target's code + * match_proto - The protocol match type in the entry, 0 for no match. + * + * Returned Value: + * The pointer to the entry, or NULL if failed. + * Caller must free it after use. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_IPFILTER +FAR struct ip6t_entry *netlib_ip6t_filter_entry(FAR const char *target, + int verdict, + uint8_t match_proto) +{ + if (target == NULL) + { + fprintf(stderr, "Empty target!\n"); + return NULL; + } + + switch (match_proto) + { + case 0: + { + FAR struct ip6t_filter_entry_s *entry = zalloc(sizeof(*entry)); + if (entry == NULL) + { + return NULL; + } + + IP6T_FILL_ENTRY(entry, target); + entry->target.verdict = verdict; + return &entry->entry; + } + + case IPPROTO_TCP: + { + FAR struct ip6t_filter_tcp_entry_s *entry = zalloc(sizeof(*entry)); + if (entry == NULL) + { + return NULL; + } + + IP6T_FILL_ENTRY(entry, target); + IP6T_FILL_MATCH(entry, XT_MATCH_NAME_TCP); + entry->target.verdict = verdict; + return &entry->entry; + } + + case IPPROTO_UDP: + { + FAR struct ip6t_filter_udp_entry_s *entry = zalloc(sizeof(*entry)); + if (entry == NULL) + { + return NULL; + } + + IP6T_FILL_ENTRY(entry, target); + IP6T_FILL_MATCH(entry, XT_MATCH_NAME_UDP); + entry->target.verdict = verdict; + return &entry->entry; + } + + case IPPROTO_ICMP6: + { + FAR struct ip6t_filter_icmp_entry_s *entry = + zalloc(sizeof(*entry)); + if (entry == NULL) + { + return NULL; + } + + IP6T_FILL_ENTRY(entry, target); + IP6T_FILL_MATCH(entry, XT_MATCH_NAME_ICMP6); + entry->target.verdict = verdict; + return &entry->entry; + } + + default: + return NULL; + } +} +#endif