net/netfilter: Add filter table in iptables.

Signed-off-by: Zhe Weng <wengzhe@xiaomi.com>
This commit is contained in:
Zhe Weng 2024-02-26 17:36:48 +08:00 committed by Xiang Xiao
parent f7181676b7
commit 9637c10696
8 changed files with 514 additions and 4 deletions

View File

@ -58,6 +58,10 @@
#define IPT_INV_PROTO XT_INV_PROTO
#define IPT_INV_MASK 0x7F /* All possible flag bits mask. */
/* Values for "inv" field for struct ipt_icmp. */
#define IPT_ICMP_INV 0x01 /* Invert the sense of type/code test */
/* Standard return verdict, or do jump. */
#define IPT_STANDARD_TARGET XT_STANDARD_TARGET
@ -78,8 +82,10 @@
(entry) = (FAR struct ipt_entry *) \
((FAR uint8_t *)(entry) + (entry)->next_offset))
/* Get pointer to target from an entry pointer. */
/* Get pointer to match / target from an entry pointer. */
#define IPT_MATCH(e) \
((FAR struct xt_entry_match *)((FAR struct ipt_entry *)(e) + 1))
#define IPT_TARGET(e) \
((FAR struct xt_entry_target *)((FAR uint8_t *)(e) + (e)->target_offset))
@ -88,9 +94,10 @@
#define IPT_FILL_ENTRY(e, target_name) \
do \
{ \
(e)->entry.target_offset = sizeof((e)->entry); \
(e)->entry.target_offset = offsetof(typeof(*(e)), target); \
(e)->entry.next_offset = sizeof(*(e)); \
(e)->target.target.u.target_size = sizeof(*(e)) - sizeof((e)->entry); \
(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)); \
} \
@ -269,6 +276,15 @@ struct ipt_get_entries
struct ipt_entry entrytable[0];
};
/* ICMP matching stuff */
struct ipt_icmp
{
uint8_t type; /* type to match */
uint8_t code[2]; /* range of code */
uint8_t invflags; /* Inverse flags */
};
/****************************************************************************
* Inline functions
****************************************************************************/

View File

@ -39,9 +39,36 @@
#define XT_INV_PROTO 0x40 /* Invert the sense of PROTO. */
/* Values for "inv" field in struct xt_tcp. */
#define XT_TCP_INV_SRCPT 0x01 /* Invert the sense of source ports. */
#define XT_TCP_INV_DSTPT 0x02 /* Invert the sense of dest ports. */
#define XT_TCP_INV_FLAGS 0x04 /* Invert the sense of TCP flags. */
#define XT_TCP_INV_OPTION 0x08 /* Invert the sense of option test. */
#define XT_TCP_INV_MASK 0x0F /* All possible flags. */
/* Values for "invflags" field in struct xt_udp. */
#define XT_UDP_INV_SRCPT 0x01 /* Invert the sense of source ports. */
#define XT_UDP_INV_DSTPT 0x02 /* Invert the sense of dest ports. */
#define XT_UDP_INV_MASK 0x03 /* All possible flags. */
/* Target names */
#define XT_STANDARD_TARGET "" /* Standard return verdict, or do jump. */
#define XT_ERROR_TARGET "ERROR"
#define XT_MASQUERADE_TARGET "MASQUERADE"
#define XT_REJECT_TARGET "REJECT"
/* Match name to simplify our code */
#define XT_MATCH_NAME_TCP "tcp"
#define XT_MATCH_NAME_UDP "udp"
#define XT_MATCH_NAME_ICMP "icmp"
/* Table name to simplify our code */
#define XT_TABLE_NAME_FILTER "filter"
/* For standard target */
@ -156,4 +183,25 @@ struct xt_entry_match
unsigned char data[1];
};
/* TCP matching stuff */
struct xt_tcp
{
uint16_t spts[2]; /* Source port range. */
uint16_t dpts[2]; /* Destination port range. */
uint8_t option; /* TCP Option iff non-zero */
uint8_t flg_mask; /* TCP flags mask byte */
uint8_t flg_cmp; /* TCP flags compare byte */
uint8_t invflags; /* Inverse flags */
};
/* UDP matching stuff */
struct xt_udp
{
uint16_t spts[2]; /* Source port range. */
uint16_t dpts[2]; /* Destination port range. */
uint8_t invflags; /* Inverse flags */
};
#endif /* __INCLUDE_NUTTX_NET_NETFILTER_X_TABLES_H */

View File

@ -28,5 +28,9 @@ if(CONFIG_NET_IPTABLES)
list(APPEND SRCS ipt_nat.c)
endif()
if(CONFIG_NET_IPFILTER)
list(APPEND SRCS ipt_filter.c)
endif()
target_sources(net PRIVATE ${SRCS})
endif()

View File

@ -8,6 +8,6 @@ config NET_IPTABLES
default y
depends on NET_IPv4
depends on NET_SOCKOPTS
depends on NET_NAT # May change dependency if we have firewall later.
depends on NET_NAT || NET_IPFILTER
---help---
Enable or disable iptables compatible interface (for NAT).

View File

@ -28,6 +28,10 @@ ifeq ($(CONFIG_NET_NAT),y)
NET_CSRCS += ipt_nat.c
endif
ifeq ($(CONFIG_NET_IPFILTER),y)
NET_CSRCS += ipt_filter.c
endif
# Include Netfilter build support
DEPPATH += --dep-path netfilter

408
net/netfilter/ipt_filter.c Normal file
View File

@ -0,0 +1,408 @@
/****************************************************************************
* net/netfilter/ipt_filter.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 <debug.h>
#include <errno.h>
#include <stdint.h>
#include <string.h>
#include <nuttx/kmalloc.h>
#include <nuttx/net/netfilter/ip_tables.h>
#include <nuttx/net/netfilter/x_tables.h>
#include "ipfilter/ipfilter.h"
#include "netdev/netdev.h"
#include "netfilter/iptables.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define FILTER_VALID_HOOKS ((1 << NF_INET_LOCAL_IN) | \
(1 << NF_INET_FORWARD) | \
(1 << NF_INET_LOCAL_OUT))
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: convert_chain
*
* Description:
* Convert iptables chain to ipfilter chain.
*
****************************************************************************/
static enum ipfilter_chain_e convert_chain(enum nf_inet_hooks hook)
{
switch (hook)
{
case NF_INET_LOCAL_IN:
return IPFILTER_CHAIN_INPUT;
case NF_INET_FORWARD:
return IPFILTER_CHAIN_FORWARD;
case NF_INET_LOCAL_OUT:
default:
return IPFILTER_CHAIN_OUTPUT;
}
}
/****************************************************************************
* Name: convert_invflags
*
* Description:
* Convert iptables invflags to ipfilter invflags.
*
* Input Parameters:
* entry - The ipfilter entry to be filled.
* invflags - The iptables invflags to be converted.
*
****************************************************************************/
static void convert_invflags(FAR struct ipfilter_entry_s *entry,
uint8_t invflags)
{
entry->inv_indev = !!(invflags & IPT_INV_VIA_IN);
entry->inv_outdev = !!(invflags & IPT_INV_VIA_OUT);
entry->inv_proto = !!(invflags & IPT_INV_PROTO);
entry->inv_srcip = !!(invflags & IPT_INV_SRCIP);
entry->inv_dstip = !!(invflags & IPT_INV_DSTIP);
}
/****************************************************************************
* Name: convert_tcpudp
*
* Description:
* Convert iptables tcp/udp match to ipfilter entry.
*
* Input Parameters:
* entry - The ipfilter entry to be filled.
* spts - The source ports to be converted.
* dpts - The destination ports to be converted.
* invflags - The iptables tcp/udp invflags to be converted.
*
****************************************************************************/
static void convert_tcpudp(FAR struct ipfilter_entry_s *entry,
uint16_t spts[2], uint16_t dpts[2],
uint8_t invflags)
{
entry->match.tcpudp.sports[0] = spts[0];
entry->match.tcpudp.sports[1] = spts[1];
entry->match.tcpudp.dports[0] = dpts[0];
entry->match.tcpudp.dports[1] = dpts[1];
entry->inv_sport = !!(invflags & XT_TCP_INV_SRCPT);
entry->inv_dport = !!(invflags & XT_TCP_INV_DSTPT);
entry->match_tcpudp = 1;
}
/****************************************************************************
* Name: convert_icmp
*
* Description:
* Convert iptables icmp match to ipfilter entry.
*
* Input Parameters:
* entry - The ipfilter entry to be filled.
* type - The icmp type to be converted.
* invflags - The iptables icmp invflags to be converted.
*
****************************************************************************/
static void convert_icmp(FAR struct ipfilter_entry_s *entry, uint8_t type,
uint8_t invflags)
{
entry->match.icmp.type = type;
entry->inv_icmp = !!(invflags & IPT_ICMP_INV);
entry->match_icmp = 1;
}
/****************************************************************************
* Name: convert_target
*
* Description:
* Convert iptables target to ipfilter target.
*
* Input Parameters:
* target - The iptables target to be converted.
*
* Returned Value:
* The converted ipfilter target.
*
****************************************************************************/
static uint8_t convert_target(FAR const struct xt_entry_target *target)
{
if (strcmp(target->u.user.name, XT_REJECT_TARGET) == 0)
{
return IPFILTER_TARGET_REJECT;
}
if (strcmp(target->u.user.name, XT_STANDARD_TARGET) == 0)
{
int verdict = ((FAR const struct xt_standard_target *)target)->verdict;
verdict = -verdict - 1;
if (verdict == NF_ACCEPT)
{
return IPFILTER_TARGET_ACCEPT;
}
else if (verdict == NF_DROP)
{
return IPFILTER_TARGET_DROP;
}
}
nwarn("WARNING: Unsupported target %s\n", target->u.user.name);
return IPFILTER_TARGET_DROP;
}
/****************************************************************************
* Name: convert_entry
*
* Description:
* Convert iptables entry to ipfilter entry.
*
* Input Parameters:
* entry - The iptables entry to be converted.
*
* Returned Value:
* The converted ipfilter entry.
*
****************************************************************************/
static FAR struct ipv4_filter_entry_s *
convert_entry(FAR const struct ipt_entry *entry)
{
FAR const struct xt_entry_match *match;
FAR const struct xt_entry_target *target;
FAR struct ipv4_filter_entry_s *filter =
(FAR struct ipv4_filter_entry_s *)ipfilter_cfg_alloc(PF_INET);
if (filter == NULL)
{
return NULL;
}
match = IPT_MATCH(entry);
target = IPT_TARGET(entry);
/* Convert common fields */
filter->sip = entry->ip.src.s_addr;
filter->dip = entry->ip.dst.s_addr;
filter->smsk = entry->ip.smsk.s_addr;
filter->dmsk = entry->ip.dmsk.s_addr;
filter->common.indev = netdev_findbyname(entry->ip.iniface);
filter->common.outdev = netdev_findbyname(entry->ip.outiface);
filter->common.proto = entry->ip.proto;
filter->common.target = convert_target(target);
convert_invflags(&filter->common, entry->ip.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->ip.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_ICMP:
if (strcmp(match->u.user.name, XT_MATCH_NAME_ICMP) == 0)
{
FAR struct ipt_icmp *icmp = (FAR struct ipt_icmp *)(match + 1);
convert_icmp(&filter->common, icmp->type, icmp->invflags);
}
break;
default:
break;
}
skip_match:
return filter;
}
/****************************************************************************
* Name: adjust_filter
*
* Description:
* Adjust filter config according to the iptables config.
*
* Input Parameters:
* repl - The config got from user space to control filter table.
*
****************************************************************************/
static void adjust_filter(FAR const struct ipt_replace *repl)
{
FAR const struct ipt_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_INET, 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++;
ipt_entry_for_every(entry, head, size)
{
FAR struct ipv4_filter_entry_s *filter = convert_entry(entry);
if (filter != NULL)
{
ipfilter_cfg_add(&filter->common, PF_INET, chain);
}
else
{
nwarn("WARNING: Failed to convert entry!\n");
}
}
}
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: ipt_filter_init
*
* Description:
* Init filter table data.
*
****************************************************************************/
FAR struct ipt_replace *ipt_filter_init(void)
{
return ipt_alloc_table(XT_TABLE_NAME_FILTER, FILTER_VALID_HOOKS);
}
/****************************************************************************
* Name: ipt_filter_apply
*
* Description:
* Try to apply filter rules, will do nothing if failed.
*
* Input Parameters:
* repl - The config got from user space to control filter table.
*
****************************************************************************/
int ipt_filter_apply(FAR const struct ipt_replace *repl)
{
FAR const struct ipt_entry *entry;
FAR const struct xt_entry_match *match;
FAR const struct xt_entry_target *target;
/* Check config first. */
ipt_entry_for_every(entry, repl->entries, repl->size)
{
match = IPT_MATCH(entry);
target = IPT_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->ip.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->ip.proto != IPPROTO_UDP)
{
nwarn("WARNING: UDP match for non-UDP protocol\n");
return -EINVAL;
}
if (strcmp(match->u.user.name, XT_MATCH_NAME_ICMP) == 0 &&
entry->ip.proto != IPPROTO_ICMP)
{
nwarn("WARNING: ICMP match for non-ICMP 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_filter(repl);
return OK;
}

View File

@ -81,6 +81,9 @@ static struct ipt_table_s g_tables[] =
#ifdef CONFIG_NET_NAT
{NULL, ipt_nat_init, ipt_nat_apply},
#endif
#ifdef CONFIG_NET_IPFILTER
{NULL, ipt_filter_init, ipt_filter_apply},
#endif
};
/****************************************************************************

View File

@ -105,5 +105,32 @@ FAR struct ipt_replace *ipt_nat_init(void);
int ipt_nat_apply(FAR const struct ipt_replace *repl);
#endif
/****************************************************************************
* Name: ipt_filter_init
*
* Description:
* Init filter table data.
*
****************************************************************************/
#ifdef CONFIG_NET_IPFILTER
FAR struct ipt_replace *ipt_filter_init(void);
#endif
/****************************************************************************
* Name: ipt_filter_apply
*
* Description:
* Try to apply filter rules, will do nothing if failed.
*
* Input Parameters:
* repl - The config got from user space to control filter table.
*
****************************************************************************/
#ifdef CONFIG_NET_IPFILTER
int ipt_filter_apply(FAR const struct ipt_replace *repl);
#endif
#endif /* CONFIG_NET_IPTABLES */
#endif /* __NET_NETFILTER_IPTABLES_H */