net/netfilter: Add filter table in ip6tables

Signed-off-by: Zhe Weng <wengzhe@xiaomi.com>
This commit is contained in:
Zhe Weng 2024-05-07 11:45:27 +08:00 committed by Xiang Xiao
parent c72edb0637
commit 2c303f213f
5 changed files with 250 additions and 6 deletions

View file

@ -65,7 +65,16 @@
#define IP6T_INV_PROTO XT_INV_PROTO
#define IP6T_INV_MASK 0x7F /* All possible flag bits mask. */
/* Values for "inv" field for struct ip6t_icmp. */
#define IP6T_ICMP_INV 0x01 /* Invert the sense of type/code test */
/* Standard return verdict, or do jump. */
#define IP6T_STANDARD_TARGET XT_STANDARD_TARGET
/* Error verdict. */
#define IP6T_ERROR_TARGET XT_ERROR_TARGET
#define ip6t_entry_target xt_entry_target
@ -79,6 +88,27 @@
(entry) = (FAR struct ip6t_entry *) \
((FAR uint8_t *)(entry) + (entry)->next_offset))
/* Get pointer to match from an entry pointer. */
#define IP6T_MATCH(e) \
((FAR struct xt_entry_match *)((FAR struct ip6t_entry *)(e) + 1))
#define IP6T_TARGET(e) \
((FAR struct xt_entry_target *)((FAR uint8_t *)(e) + (e)->target_offset))
/* Auto fill common fields of entry and target. */
#define IP6T_FILL_ENTRY(e, target_name) \
do \
{ \
(e)->entry.target_offset = offsetof(typeof(*(e)), target); \
(e)->entry.next_offset = sizeof(*(e)); \
(e)->target.target.u.target_size = sizeof(*(e)) - \
(e)->entry.target_offset; \
strlcpy((e)->target.target.u.user.name, (target_name), \
sizeof((e)->target.target.u.user.name)); \
} \
while(0)
/****************************************************************************
* Public Types
****************************************************************************/
@ -248,6 +278,15 @@ struct ip6t_replace
struct ip6t_entry entries[1];
};
/* ICMPv6 matching stuff */
struct ip6t_icmp
{
uint8_t type; /* type to match */
uint8_t code[2]; /* range of code */
uint8_t invflags; /* Inverse flags */
};
/****************************************************************************
* Inline functions
****************************************************************************/

View file

@ -65,6 +65,7 @@
#define XT_MATCH_NAME_TCP "tcp"
#define XT_MATCH_NAME_UDP "udp"
#define XT_MATCH_NAME_ICMP "icmp"
#define XT_MATCH_NAME_ICMP6 "icmp6"
/* Table name to simplify our code */

View file

@ -78,6 +78,9 @@ struct ip6t_error_entry_s
static struct ip6t_table_s g_tables[] =
{
#ifdef CONFIG_NET_IPFILTER
{NULL, ip6t_filter_init, ip6t_filter_apply},
#endif
};
/****************************************************************************
@ -381,7 +384,7 @@ FAR struct ip6t_replace *ip6t_alloc_table(FAR const char *table,
repl->underflow[hook] = repl->hook_entry[hook];
entry->target.verdict = -NF_ACCEPT - 1;
IPT_FILL_ENTRY(entry, XT_STANDARD_TARGET);
IP6T_FILL_ENTRY(entry, XT_STANDARD_TARGET);
entry++;
}
@ -389,7 +392,7 @@ FAR struct ip6t_replace *ip6t_alloc_table(FAR const char *table,
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);
IP6T_FILL_ENTRY(error_entry, XT_ERROR_TARGET);
return repl;
}

View file

@ -199,8 +199,9 @@ static uint8_t convert_target(FAR const struct xt_entry_target *target)
*
****************************************************************************/
#ifdef CONFIG_NET_IPv4
static FAR struct ipv4_filter_entry_s *
convert_entry(FAR const struct ipt_entry *entry)
convert_ipv4entry(FAR const struct ipt_entry *entry)
{
FAR const struct xt_entry_match *match;
FAR const struct xt_entry_target *target;
@ -271,6 +272,83 @@ convert_entry(FAR const struct ipt_entry *entry)
skip_match:
return filter;
}
#endif
#ifdef CONFIG_NET_IPv6
static FAR struct ipv6_filter_entry_s *
convert_ipv6entry(FAR const struct ip6t_entry *entry)
{
FAR const struct xt_entry_match *match;
FAR const struct xt_entry_target *target;
FAR struct ipv6_filter_entry_s *filter =
(FAR struct ipv6_filter_entry_s *)ipfilter_cfg_alloc(PF_INET6);
if (filter == NULL)
{
return NULL;
}
match = IP6T_MATCH(entry);
target = IP6T_TARGET(entry);
/* Convert common fields */
net_ipv6addr_copy(filter->sip, entry->ipv6.src.s6_addr16);
net_ipv6addr_copy(filter->dip, entry->ipv6.dst.s6_addr16);
net_ipv6addr_copy(filter->smsk, entry->ipv6.smsk.s6_addr16);
net_ipv6addr_copy(filter->dmsk, entry->ipv6.dmsk.s6_addr16);
filter->common.indev = netdev_findbyname(entry->ipv6.iniface);
filter->common.outdev = netdev_findbyname(entry->ipv6.outiface);
filter->common.proto = entry->ipv6.proto;
filter->common.target = convert_target(target);
convert_invflags(&filter->common, entry->ipv6.invflags);
/* Convert match fields */
if (entry->target_offset < sizeof(struct xt_entry_match))
{
ninfo("No match inside entry, skip match conversion.\n");
goto skip_match;
}
switch (entry->ipv6.proto)
{
case IPPROTO_TCP:
if (strcmp(match->u.user.name, XT_MATCH_NAME_TCP) == 0)
{
FAR struct xt_tcp *tcp = (FAR struct xt_tcp *)(match + 1);
convert_tcpudp(&filter->common, tcp->spts, tcp->dpts,
tcp->invflags);
}
break;
case IPPROTO_UDP:
if (strcmp(match->u.user.name, XT_MATCH_NAME_TCP) == 0)
{
FAR struct xt_udp *udp = (FAR struct xt_udp *)(match + 1);
convert_tcpudp(&filter->common, udp->spts, udp->dpts,
udp->invflags);
}
break;
case IPPROTO_ICMP6:
if (strcmp(match->u.user.name, XT_MATCH_NAME_ICMP6) == 0)
{
FAR struct ip6t_icmp *icmp6 =
(FAR struct ip6t_icmp *)(match + 1);
convert_icmp(&filter->common, icmp6->type, icmp6->invflags);
}
break;
default:
break;
}
skip_match:
return filter;
}
#endif
/****************************************************************************
* Name: adjust_filter
@ -283,7 +361,8 @@ skip_match:
*
****************************************************************************/
static void adjust_filter(FAR const struct ipt_replace *repl)
#ifdef CONFIG_NET_IPv4
static void adjust_ipv4filter(FAR const struct ipt_replace *repl)
{
FAR const struct ipt_entry *entry;
FAR const uint8_t *head;
@ -309,7 +388,7 @@ static void adjust_filter(FAR const struct ipt_replace *repl)
ipt_entry_for_every(entry, head, size)
{
FAR struct ipv4_filter_entry_s *filter = convert_entry(entry);
FAR struct ipv4_filter_entry_s *filter = convert_ipv4entry(entry);
if (filter != NULL)
{
ipfilter_cfg_add(&filter->common, PF_INET, chain);
@ -321,6 +400,48 @@ static void adjust_filter(FAR const struct ipt_replace *repl)
}
}
}
#endif
#ifdef CONFIG_NET_IPv6
static void adjust_ipv6filter(FAR const struct ip6t_replace *repl)
{
FAR const struct ip6t_entry *entry;
FAR const uint8_t *head;
enum ipfilter_chain_e chain;
enum nf_inet_hooks hook;
size_t size;
for (hook = NF_INET_LOCAL_IN; hook <= NF_INET_LOCAL_OUT; hook++)
{
/* Clear all filter config first. */
chain = convert_chain(hook);
ipfilter_cfg_clear(PF_INET6, chain);
/* Set filter config according to iptables config. */
head = (FAR const uint8_t *)repl->entries + repl->hook_entry[hook];
size = repl->underflow[hook] - repl->hook_entry[hook];
/* We need the underflow entry as the default of the chain. */
size++;
ip6t_entry_for_every(entry, head, size)
{
FAR struct ipv6_filter_entry_s *filter = convert_ipv6entry(entry);
if (filter != NULL)
{
ipfilter_cfg_add(&filter->common, PF_INET6, chain);
}
else
{
nwarn("WARNING: Failed to convert entry!\n");
}
}
}
}
#endif
/****************************************************************************
* Public Functions
@ -334,10 +455,19 @@ static void adjust_filter(FAR const struct ipt_replace *repl)
*
****************************************************************************/
#ifdef CONFIG_NET_IPv4
FAR struct ipt_replace *ipt_filter_init(void)
{
return ipt_alloc_table(XT_TABLE_NAME_FILTER, FILTER_VALID_HOOKS);
}
#endif
#ifdef CONFIG_NET_IPv6
FAR struct ip6t_replace *ip6t_filter_init(void)
{
return ip6t_alloc_table(XT_TABLE_NAME_FILTER, FILTER_VALID_HOOKS);
}
#endif
/****************************************************************************
* Name: ipt_filter_apply
@ -350,6 +480,7 @@ FAR struct ipt_replace *ipt_filter_init(void)
*
****************************************************************************/
#ifdef CONFIG_NET_IPv4
int ipt_filter_apply(FAR const struct ipt_replace *repl)
{
FAR const struct ipt_entry *entry;
@ -402,7 +533,67 @@ int ipt_filter_apply(FAR const struct ipt_replace *repl)
/* Set config table into ip filter. */
adjust_filter(repl);
adjust_ipv4filter(repl);
return OK;
}
#endif
#ifdef CONFIG_NET_IPv6
int ip6t_filter_apply(FAR const struct ip6t_replace *repl)
{
FAR const struct ip6t_entry *entry;
FAR const struct xt_entry_match *match;
FAR const struct xt_entry_target *target;
/* Check config first. */
ip6t_entry_for_every(entry, repl->entries, repl->size)
{
match = IP6T_MATCH(entry);
target = IP6T_TARGET(entry);
/* Check match type matches the protocol */
if (entry->target_offset >= sizeof(struct xt_entry_match))
{
if (strcmp(match->u.user.name, XT_MATCH_NAME_TCP) == 0 &&
entry->ipv6.proto != IPPROTO_TCP)
{
nwarn("WARNING: TCP match for non-TCP protocol\n");
return -EINVAL;
}
if (strcmp(match->u.user.name, XT_MATCH_NAME_UDP) == 0 &&
entry->ipv6.proto != IPPROTO_UDP)
{
nwarn("WARNING: UDP match for non-UDP protocol\n");
return -EINVAL;
}
if (strcmp(match->u.user.name, XT_MATCH_NAME_ICMP6) == 0 &&
entry->ipv6.proto != IPPROTO_ICMP6)
{
nwarn("WARNING: ICMP6 match for non-ICMP6 protocol\n");
return -EINVAL;
}
}
/* Check target type */
if (strcmp(target->u.user.name, XT_REJECT_TARGET) != 0 &&
strcmp(target->u.user.name, XT_STANDARD_TARGET) != 0 &&
strcmp(target->u.user.name, XT_ERROR_TARGET) != 0)
{
nwarn("WARNING: Unsupported target %s\n", target->u.user.name);
return -EINVAL;
}
}
/* Set config table into ip filter. */
adjust_ipv6filter(repl);
return OK;
}
#endif

View file

@ -133,7 +133,12 @@ int ipt_nat_apply(FAR const struct ipt_replace *repl);
****************************************************************************/
#ifdef CONFIG_NET_IPFILTER
# ifdef CONFIG_NET_IPv4
FAR struct ipt_replace *ipt_filter_init(void);
# endif
# ifdef CONFIG_NET_IPv6
FAR struct ip6t_replace *ip6t_filter_init(void);
# endif
#endif
/****************************************************************************
@ -148,7 +153,12 @@ FAR struct ipt_replace *ipt_filter_init(void);
****************************************************************************/
#ifdef CONFIG_NET_IPFILTER
# ifdef CONFIG_NET_IPv4
int ipt_filter_apply(FAR const struct ipt_replace *repl);
# endif
# ifdef CONFIG_NET_IPv6
int ip6t_filter_apply(FAR const struct ip6t_replace *repl);
# endif
#endif
#endif /* CONFIG_NET_IPTABLES */