net: Support IP packet filter

Add a firewall compatible with Linux's iptables and ip6tables, with chains at similar points in the packet processing path.

NIC ─> ipv[46]_input ┬> ipv[46]_forward ─> [FORWARD] ┬> devif_poll_out ─> NIC
                     │                               │
                     │          ┌>  tcp  ┐           │
                     │          ├>  udp  ┤           │
                     └> [INPUT] ┼> icmp  ┼> [OUTPUT] ┘
                                ├> icmp6 ┤
                                └>  ...  ┘

Signed-off-by: Zhe Weng <wengzhe@xiaomi.com>
This commit is contained in:
Zhe Weng 2024-02-08 16:00:00 +08:00 committed by Xiang Xiao
parent 914b8367ba
commit f7181676b7
12 changed files with 1078 additions and 6 deletions

View file

@ -359,6 +359,7 @@ menuconfig NET_6LOWPAN
source "net/sixlowpan/Kconfig"
source "net/ipforward/Kconfig"
source "net/nat/Kconfig"
source "net/ipfilter/Kconfig"
source "net/netfilter/Kconfig"
source "net/ipfrag/Kconfig"

View file

@ -47,6 +47,7 @@ include sixlowpan/Make.defs
include bluetooth/Make.defs
include ieee802154/Make.defs
include devif/Make.defs
include ipfilter/Make.defs
include ipforward/Make.defs
include nat/Make.defs
include netfilter/Make.defs

View file

@ -47,6 +47,7 @@
#include "mld/mld.h"
#include "ipforward/ipforward.h"
#include "sixlowpan/sixlowpan.h"
#include "ipfilter/ipfilter.h"
#include "ipfrag/ipfrag.h"
#include "inet/inet.h"
@ -184,6 +185,32 @@ static void devif_packet_conversion(FAR struct net_driver_s *dev,
# define devif_packet_conversion(dev,pkttype)
#endif /* CONFIG_NET_6LOWPAN */
/****************************************************************************
* Name: devif_poll_local_out
*
* Description:
* Generic callback before device output to build L2 headers before sending
* with packet filter for TCP/UDP/ICMP(v6).
*
* Assumptions:
* This function is called from the MAC device driver with the network
* locked.
*
****************************************************************************/
#ifdef CONFIG_NET_IPFILTER
static int devif_poll_local_out(FAR struct net_driver_s *dev,
devif_poll_callback_t callback)
{
/* Maybe we need to reply REJECT to ourself, so filter before loopback. */
ipfilter_out(dev);
return devif_poll_out(dev, callback);
}
#else
# define devif_poll_local_out(dev, callback) devif_poll_out(dev, callback)
#endif /* CONFIG_NET_IPFILTER */
/****************************************************************************
* Name: devif_poll_pkt_connections
*
@ -397,7 +424,7 @@ static inline int devif_poll_icmp(FAR struct net_driver_s *dev,
/* Call back into the driver */
bstop = devif_poll_out(dev, callback);
bstop = devif_poll_local_out(dev, callback);
}
}
@ -444,7 +471,7 @@ static inline int devif_poll_icmpv6(FAR struct net_driver_s *dev,
/* Call back into the driver */
bstop = devif_poll_out(dev, callback);
bstop = devif_poll_local_out(dev, callback);
}
}
while (!bstop && (conn = icmpv6_nextconn(conn)) != NULL);
@ -581,7 +608,7 @@ static int devif_poll_udp_connections(FAR struct net_driver_s *dev,
/* Call back into the driver */
bstop = devif_poll_out(dev, callback);
bstop = devif_poll_local_out(dev, callback);
}
}
@ -626,7 +653,7 @@ static inline int devif_poll_tcp_connections(FAR struct net_driver_s *dev,
/* Call back into the driver */
bstop = devif_poll_out(dev, callback);
bstop = devif_poll_local_out(dev, callback);
}
}

View file

@ -105,6 +105,7 @@
#include "ipforward/ipforward.h"
#include "devif/devif.h"
#include "nat/nat.h"
#include "ipfilter/ipfilter.h"
#include "ipfrag/ipfrag.h"
#include "utils/utils.h"
@ -392,6 +393,14 @@ static int ipv4_in(FAR struct net_driver_s *dev)
goto drop;
}
#ifdef CONFIG_NET_IPFILTER
if (ipv4_filter_in(dev) != IPFILTER_TARGET_ACCEPT)
{
ninfo("Drop/Reject INPUT packet due to filter.\n");
goto done;
}
#endif
/* Now process the incoming packet according to the protocol. */
switch (ipv4->proto)
@ -434,7 +443,11 @@ static int ipv4_in(FAR struct net_driver_s *dev)
goto drop;
}
#if defined(CONFIG_NET_IPFORWARD) || \
#ifdef CONFIG_NET_IPFILTER
ipfilter_out(dev);
#endif
#if defined(CONFIG_NET_IPFORWARD) || defined(CONFIG_NET_IPFILTER) || \
(defined(CONFIG_NET_BROADCAST) && defined(NET_UDP_HAVE_STACK))
done:
#endif

View file

@ -52,6 +52,7 @@
#include "ipforward/ipforward.h"
#include "inet/inet.h"
#include "devif/devif.h"
#include "ipfilter/ipfilter.h"
#include "ipfrag/ipfrag.h"
/****************************************************************************
@ -420,6 +421,14 @@ static int ipv6_in(FAR struct net_driver_s *dev)
}
#endif
#ifdef CONFIG_NET_IPFILTER
if (ipv6_filter_in(dev) != IPFILTER_TARGET_ACCEPT)
{
ninfo("Drop/Reject INPUT packet due to filter.\n");
goto done;
}
#endif
/* Now process the incoming packet according to the protocol specified in
* the next header IPv6 field.
*/
@ -518,7 +527,11 @@ static int ipv6_in(FAR struct net_driver_s *dev)
goto drop;
}
#ifdef CONFIG_NET_IPFORWARD
#ifdef CONFIG_NET_IPFILTER
ipfilter_out(dev);
#endif
#if defined(CONFIG_NET_IPFORWARD) || defined(CONFIG_NET_IPFILTER)
done:
#endif

View file

@ -0,0 +1,27 @@
# ##############################################################################
# net/ipfilter/CMakeLists.txt
#
# 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.
#
# ##############################################################################
# IP filter source files
if(CONFIG_NET_IPFILTER)
target_sources(net PRIVATE ipfilter.c)
endif()

16
net/ipfilter/Kconfig Normal file
View file

@ -0,0 +1,16 @@
#
# For a description of the syntax of this configuration file,
# see the file kconfig-language.txt in the NuttX tools repository.
#
config NET_IPFILTER
bool "Enable IP packet filter (firewall)"
default n
depends on NET_IPv4 || NET_IPv6
---help---
Enable this option to enable the IP packet filter (firewall).
Our IP packet filter is a netfilter-like packet filter that
operates on the IP (and transport) layer. It is a stateless
packet filter that can be used to filter packets based on
source and destination IP addresses, source and destination
ports, protocol, and interface.

32
net/ipfilter/Make.defs Normal file
View file

@ -0,0 +1,32 @@
############################################################################
# net/ipfilter/Make.defs
#
# 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.
#
############################################################################
# IP filter source files
ifeq ($(CONFIG_NET_IPFILTER),y)
NET_CSRCS += ipfilter.c
# Include IP filter build support
DEPPATH += --dep-path ipfilter
VPATH += :ipfilter
endif

632
net/ipfilter/ipfilter.c Normal file
View file

@ -0,0 +1,632 @@
/****************************************************************************
* net/ipfilter/ipfilter.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 <nuttx/kmalloc.h>
#include <nuttx/net/icmpv6.h>
#include <nuttx/net/netdev.h>
#include <nuttx/net/udp.h>
#include <nuttx/queue.h>
#include "icmp/icmp.h"
#include "icmpv6/icmpv6.h"
#include "ipfilter/ipfilter.h"
#include "utils/utils.h"
#ifdef CONFIG_NET_IPFILTER
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define SPORT_MATCH(entry, sport) \
(((sport) >= (entry)->match.tcpudp.sports[0] && \
(sport) <= (entry)->match.tcpudp.sports[1]) ^ (entry)->inv_sport)
#define DPORT_MATCH(entry, dport) \
(((dport) >= (entry)->match.tcpudp.dports[0] && \
(dport) <= (entry)->match.tcpudp.dports[1]) ^ (entry)->inv_dport)
#define ICMP_MATCH(entry, icmphdr) \
(((entry)->match.icmp.type == 0xFF || \
(entry)->match.icmp.type == (icmphdr)->type) ^ (entry)->inv_icmp)
/* Getting L4 header from IPv4/IPv6 header. */
#define IPv4_L4HDR(ipv4) \
((FAR void *)((FAR uint8_t *)(ipv4) + (((ipv4)->vhl & IPv4_HLMASK) << 2)))
#define IPv6_L4HDR(ipv6, proto) \
((FAR void *)(net_ipv6_payload((FAR struct ipv6_hdr_s *)(ipv6), &(proto))))
/****************************************************************************
* Private Data
****************************************************************************/
#ifdef CONFIG_NET_IPv4
static sq_queue_t g_ipv4_filters[IPFILTER_CHAIN_MAX];
#endif
#ifdef CONFIG_NET_IPv6
static sq_queue_t g_ipv6_filters[IPFILTER_CHAIN_MAX];
#endif
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: ipfilter_match_device
*
* Description:
* Match the packet with the filter entries on devices.
*
* Input Parameters:
* entry - The filter entry to match
* indev - The network device that the packet comes from
* outdev - The network device that the packet goes to
*
* Returned Value:
* true - The input packet is matched
* false - The input packet is not matched
*
****************************************************************************/
static bool ipfilter_match_device(FAR const struct ipfilter_entry_s *entry,
FAR const struct net_driver_s *indev,
FAR const struct net_driver_s *outdev)
{
bool matched;
if (indev != NULL && entry->indev != NULL)
{
matched = (indev == entry->indev) ^ entry->inv_indev;
if (!matched)
{
return false;
}
}
if (outdev != NULL && entry->outdev != NULL)
{
matched = (outdev == entry->outdev) ^ entry->inv_outdev;
if (!matched)
{
return false;
}
}
return true;
}
/****************************************************************************
* Name: ipfilter_match_proto
*
* Description:
*
* Input Parameters:
*
* Returned Value:
*
****************************************************************************/
static bool ipfilter_match_proto(FAR const struct ipfilter_entry_s *entry,
FAR const void *l4hdr, uint8_t proto)
{
bool matched;
if (entry->proto)
{
matched = (entry->proto == proto) ^ entry->inv_proto;
if (!matched)
{
return false;
}
}
if (entry->inv_proto)
{
/* Cannot match port/type for inversed proto, just success. */
return true;
}
switch (proto)
{
case IP_PROTO_TCP:
case IP_PROTO_UDP:
if (entry->match_tcpudp)
{
/* Ports in TCP & UDP headers have same offset. */
FAR const struct udp_hdr_s *udp = l4hdr;
return SPORT_MATCH(entry, NTOHS(udp->srcport)) &&
DPORT_MATCH(entry, NTOHS(udp->destport));
}
case IP_PROTO_ICMP:
if (entry->match_icmp)
{
FAR const struct icmp_hdr_s *icmp = l4hdr;
return ICMP_MATCH(entry, icmp);
}
case IP_PROTO_ICMP6:
if (entry->match_icmp)
{
FAR const struct icmpv6_hdr_s *icmpv6 = l4hdr;
return ICMP_MATCH(entry, icmpv6);
}
default:
return true;
}
}
/****************************************************************************
* Name: ipv4_filter_match / ipv6_filter_match
*
* Description:
* Match the input packet with the filter entries in the specified chain.
*
* Input Parameters:
* indev - The network device that the packet comes from
* outdev - The network device that the packet goes to
* ipv4/ipv6 - The IPv4/IPv6 header
* chain - The chain to match the filter entries
*
* Returned Value:
* IPFILTER_TARGET_ACCEPT(0) - The input packet is accepted
* IPFILTER_TARGET_DROP(-1) - The input packet needs to be dropped
* IPFILTER_TARGET_REJECT(-2) - The input packet is rejected
*
****************************************************************************/
#ifdef CONFIG_NET_IPv4
static int ipv4_filter_match(FAR const struct net_driver_s *indev,
FAR const struct net_driver_s *outdev,
FAR const struct ipv4_hdr_s *ipv4,
enum ipfilter_chain_e chain)
{
FAR const struct ipv4_filter_entry_s *filter;
FAR const sq_queue_t *queue = &g_ipv4_filters[chain];
FAR const sq_entry_t *entry;
FAR const void *l4hdr;
in_addr_t ipaddr;
bool matched;
/* Handle unexpected status, return ACCEPT to indicate doing nothing. */
if ((indev == NULL && outdev == NULL) || ipv4 == NULL)
{
return IPFILTER_TARGET_ACCEPT;
}
l4hdr = IPv4_L4HDR(ipv4);
sq_for_every(queue, entry)
{
filter = (FAR struct ipv4_filter_entry_s *)entry;
/* Match device */
if (!ipfilter_match_device(&filter->common, indev, outdev))
{
continue;
}
/* Match addresses */
ipaddr = net_ip4addr_conv32(ipv4->srcipaddr);
matched = net_ipv4addr_maskcmp(filter->sip, ipaddr, filter->smsk)
^ filter->common.inv_srcip;
if (!matched)
{
continue;
}
ipaddr = net_ip4addr_conv32(ipv4->destipaddr);
matched = net_ipv4addr_maskcmp(filter->dip, ipaddr, filter->dmsk)
^ filter->common.inv_dstip;
if (!matched)
{
continue;
}
/* Match protocol */
if (!ipfilter_match_proto(&filter->common, l4hdr, ipv4->proto))
{
continue;
}
/* Return the target action if matched. */
return filter->common.target;
}
/* Normally there should be a default rule in chain, won't reach here. */
ninfo("No filter matched, maybe uninitialized.\n");
return IPFILTER_TARGET_ACCEPT;
}
#endif
#ifdef CONFIG_NET_IPv6
static int ipv6_filter_match(FAR const struct net_driver_s *indev,
FAR const struct net_driver_s *outdev,
FAR const struct ipv6_hdr_s *ipv6,
enum ipfilter_chain_e chain)
{
FAR const struct ipv6_filter_entry_s *filter;
FAR const sq_queue_t *queue = &g_ipv6_filters[chain];
FAR const sq_entry_t *entry;
FAR const void *l4hdr;
uint8_t proto;
bool matched;
/* Handle unexpected status, return ACCEPT to indicate doing nothing. */
if ((indev == NULL && outdev == NULL) || ipv6 == NULL)
{
return IPFILTER_TARGET_ACCEPT;
}
l4hdr = IPv6_L4HDR(ipv6, proto);
sq_for_every(queue, entry)
{
filter = (FAR struct ipv6_filter_entry_s *)entry;
/* Match device */
if (!ipfilter_match_device(&filter->common, indev, outdev))
{
continue;
}
/* Match addresses */
matched = net_ipv6addr_maskcmp(filter->sip, ipv6->srcipaddr,
filter->smsk)
^ filter->common.inv_srcip;
if (!matched)
{
continue;
}
matched = net_ipv6addr_maskcmp(filter->dip, ipv6->destipaddr,
filter->dmsk)
^ filter->common.inv_dstip;
if (!matched)
{
continue;
}
/* Match protocol */
if (!ipfilter_match_proto(&filter->common, l4hdr, proto))
{
continue;
}
/* Return the target action if matched. */
return filter->common.target;
}
/* Normally there should be a default rule in chain, won't reach here. */
ninfo("No filter matched, maybe uninitialized.\n");
return IPFILTER_TARGET_ACCEPT;
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: ipfilter_cfg_alloc
*
* Description:
* Allocate a new filter configuration entry for the given address family.
*
* Input Parameters:
* family - The address family of the filter entry
*
* Returned Value:
* A pointer to the newly allocated filter entry. NULL is returned on
* failure.
*
****************************************************************************/
FAR struct ipfilter_entry_s *ipfilter_cfg_alloc(sa_family_t family)
{
/* We may optimize alloc / free later if we really have lots of configs. */
#ifdef CONFIG_NET_IPv4
if (family == PF_INET)
{
return kmm_zalloc(sizeof(struct ipv4_filter_entry_s));
}
#endif
#ifdef CONFIG_NET_IPv6
if (family == PF_INET6)
{
return kmm_zalloc(sizeof(struct ipv6_filter_entry_s));
}
#endif
return NULL;
}
/****************************************************************************
* Name: ipfilter_cfg_add
*
* Description:
* Add a new filter configuration entry for the given address family to the
* end of specified chain.
*
* Input Parameters:
* entry - The filter entry to add
* family - The address family of the filter entry
* chain - The chain to add the filter entry to
*
* Returned Value:
* None
*
****************************************************************************/
void ipfilter_cfg_add(FAR struct ipfilter_entry_s *entry,
sa_family_t family, enum ipfilter_chain_e chain)
{
#ifdef CONFIG_NET_IPv4
if (family == PF_INET)
{
sq_addlast((FAR sq_entry_t *)entry, &g_ipv4_filters[chain]);
}
#endif
#ifdef CONFIG_NET_IPv6
if (family == PF_INET6)
{
sq_addlast((FAR sq_entry_t *)entry, &g_ipv6_filters[chain]);
}
#endif
}
/****************************************************************************
* Name: ipfilter_cfg_clear
*
* Description:
* Clear all filter configuration entries for the given address family from
* the specified chain.
*
* Input Parameters:
* family - The address family of the filter entry to clear
* chain - The chain to clear the filter entries from
*
* Returned Value:
* None
*
****************************************************************************/
void ipfilter_cfg_clear(sa_family_t family, enum ipfilter_chain_e chain)
{
#ifdef CONFIG_NET_IPv4
if (family == PF_INET)
{
FAR sq_queue_t *queue = &g_ipv4_filters[chain];
while (!sq_empty(queue))
{
kmm_free(sq_remfirst(queue));
}
}
#endif
#ifdef CONFIG_NET_IPv6
if (family == PF_INET6)
{
FAR sq_queue_t *queue = &g_ipv6_filters[chain];
while (!sq_empty(queue))
{
kmm_free(sq_remfirst(queue));
}
}
#endif
}
/****************************************************************************
* Name: ipv4_filter_in / ipv6_filter_in
*
* Description:
* Handling IPv4/IPv6 filter on local input. Do nothing if the input
* packet is accepted, set d_len to 0 if the input packet needs to be
* dropped, and set d_iob to reject reply if the input packet is rejected.
*
* Input Parameters:
* dev - The network device that the packet comes from
*
* Returned Value:
* IPFILTER_TARGET_ACCEPT(0) - The input packet is accepted
* IPFILTER_TARGET_DROP(-1) - The input packet needs to be dropped
* IPFILTER_TARGET_REJECT(-2) - The input packet is rejected
*
* Assumptions:
* The network is locked. The d_iob and d_len in the dev are set.
*
****************************************************************************/
#ifdef CONFIG_NET_IPv4
int ipv4_filter_in(FAR struct net_driver_s *dev)
{
FAR struct ipv4_hdr_s *ipv4 = IPv4BUF;
int ret = ipv4_filter_match(dev, NULL, ipv4, IPFILTER_CHAIN_INPUT);
if (ret == IPFILTER_TARGET_DROP)
{
dev->d_len = 0;
}
if (ret == IPFILTER_TARGET_REJECT)
{
/* TODO: Support more --reject-with types later. */
icmp_reply(dev, ICMP_DEST_UNREACHABLE, ICMP_NET_UNREACH);
}
return ret;
}
#endif
#ifdef CONFIG_NET_IPv6
int ipv6_filter_in(FAR struct net_driver_s *dev)
{
FAR struct ipv6_hdr_s *ipv6 = IPv6BUF;
int ret = ipv6_filter_match(dev, NULL, ipv6, IPFILTER_CHAIN_INPUT);
if (ret == IPFILTER_TARGET_DROP)
{
dev->d_len = 0;
}
if (ret == IPFILTER_TARGET_REJECT)
{
/* TODO: Support more --reject-with types later. */
icmpv6_reply(dev, ICMPv6_DEST_UNREACHABLE, ICMPv6_ADDR_UNREACH, 0);
}
return ret;
}
#endif
/****************************************************************************
* Name: ipfilter_out
*
* Description:
* Handling filter on local output. Do nothing if the output packet
* is accepted, set d_len to 0 if the output packet needs to be dropped,
* and set d_iob to reject reply if the output packet is rejected.
*
* Input Parameters:
* dev - The network device that the packet goes to
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked. Only IPv4 and IPv6 packets call this function.
*
****************************************************************************/
void ipfilter_out(FAR struct net_driver_s *dev)
{
if (dev->d_iob == NULL || dev->d_len == 0)
{
return;
}
#ifdef CONFIG_NET_IPv4
if (IFF_IS_IPv4(dev->d_flags))
{
FAR struct ipv4_hdr_s *ipv4 = IPv4BUF;
int ret = ipv4_filter_match(NULL, dev, ipv4, IPFILTER_CHAIN_OUTPUT);
if (ret == IPFILTER_TARGET_DROP)
{
dev->d_len = 0;
}
if (ret == IPFILTER_TARGET_REJECT)
{
icmp_reply(dev, ICMP_DEST_UNREACHABLE, ICMP_NET_UNREACH);
}
}
#endif
#ifdef CONFIG_NET_IPv6
if (IFF_IS_IPv6(dev->d_flags))
{
FAR struct ipv6_hdr_s *ipv6 = IPv6BUF;
int ret = ipv6_filter_match(NULL, dev, ipv6, IPFILTER_CHAIN_OUTPUT);
if (ret == IPFILTER_TARGET_DROP)
{
dev->d_len = 0;
}
if (ret == IPFILTER_TARGET_REJECT)
{
icmpv6_reply(dev, ICMPv6_DEST_UNREACHABLE, ICMPv6_ADDR_UNREACH, 0);
}
}
#endif
}
/****************************************************************************
* Name: ipv4_filter_fwd / ipv6_filter_fwd
*
* Description:
* Handling IPv4/IPv6 filter on forwarding. Just return the target action,
* and relies on the caller to do the actual drop / reject.
*
* Input Parameters:
* indev - The network device that the packet comes from
* outdev - The network device that the packet goes to
* ipv4/ipv6 - The IPv4/IPv6 header
*
* Returned Value:
* IPFILTER_TARGET_ACCEPT(0) - The input packet is accepted
* IPFILTER_TARGET_DROP(-1) - The input packet needs to be dropped
* IPFILTER_TARGET_REJECT(-2) - The input packet needs to be rejected
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
#if defined(CONFIG_NET_IPFORWARD) && defined(CONFIG_NET_IPv4)
int ipv4_filter_fwd(FAR struct net_driver_s *indev,
FAR struct net_driver_s *outdev,
FAR struct ipv4_hdr_s *ipv4)
{
return ipv4_filter_match(indev, outdev, ipv4, IPFILTER_CHAIN_FORWARD);
}
#endif
#if defined(CONFIG_NET_IPFORWARD) && defined(CONFIG_NET_IPv6)
int ipv6_filter_fwd(FAR struct net_driver_s *indev,
FAR struct net_driver_s *outdev,
FAR struct ipv6_hdr_s *ipv6)
{
return ipv6_filter_match(indev, outdev, ipv6, IPFILTER_CHAIN_FORWARD);
}
#endif
#endif /* CONFIG_NET_IPFILTER */

266
net/ipfilter/ipfilter.h Normal file
View file

@ -0,0 +1,266 @@
/****************************************************************************
* net/ipfilter/ipfilter.h
*
* 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.
*
****************************************************************************/
#ifndef __NET_IPFILTER_IPFILTER_H
#define __NET_IPFILTER_IPFILTER_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdint.h>
#include <nuttx/compiler.h>
#include <nuttx/net/ip.h>
#ifdef CONFIG_NET_IPFILTER
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define IPFILTER_TARGET_ACCEPT (0)
#define IPFILTER_TARGET_DROP (-1)
#define IPFILTER_TARGET_REJECT (-2)
/****************************************************************************
* Public Types
****************************************************************************/
enum ipfilter_chain_e
{
IPFILTER_CHAIN_INPUT = 0, /* = NF_IP_LOCAL_IN - 1, Input chain */
IPFILTER_CHAIN_FORWARD = 1, /* = NF_IP_FORWARD - 1, Forward chain */
IPFILTER_CHAIN_OUTPUT = 2, /* = NF_IP_LOCAL_OUT - 1, Output chain */
IPFILTER_CHAIN_MAX
};
/* The filter configuration entry */
struct ipfilter_entry_s
{
FAR struct ipfilter_entry_s *flink;
FAR struct net_driver_s *indev;
FAR struct net_driver_s *outdev;
union /* Matches in host byte order (because it's range) */
{
struct
{
uint16_t sports[2]; /* Source port range */
uint16_t dports[2]; /* Destination port range */
} tcpudp;
struct
{
uint8_t type; /* Type to match, 0xFF = ALL (Same as Linux) */
} icmp;
} match;
uint8_t proto; /* Protocol to match, 0 = ALL (Same as Linux) */
int8_t target;
/* Match flags, whether we need to match protocol in detail */
uint8_t match_tcpudp : 1; /* Match TCP/UDP */
uint8_t match_icmp : 1; /* Match ICMP */
/* Inverse flags */
uint8_t inv_indev : 1; /* Inverse input device */
uint8_t inv_outdev : 1; /* Inverse output device */
uint8_t inv_proto : 1; /* Inverse protocol */
uint8_t inv_srcip : 1; /* Inverse source IP */
uint8_t inv_dstip : 1; /* Inverse destination IP */
uint8_t inv_sport : 1; /* Inverse source port */
uint8_t inv_dport : 1; /* Inverse destination port */
uint8_t inv_icmp : 1; /* Inverse ICMP type */
};
struct ipv4_filter_entry_s
{
struct ipfilter_entry_s common;
/* Addresses in network byte order */
in_addr_t sip;
in_addr_t dip;
in_addr_t smsk;
in_addr_t dmsk;
};
struct ipv6_filter_entry_s
{
struct ipfilter_entry_s common;
/* Addresses in network byte order */
net_ipv6addr_t sip;
net_ipv6addr_t dip;
net_ipv6addr_t smsk;
net_ipv6addr_t dmsk;
};
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
/****************************************************************************
* Name: ipfilter_cfg_alloc
*
* Description:
* Allocate a new filter configuration entry for the given address family.
*
* Input Parameters:
* family - The address family of the filter entry
*
* Returned Value:
* A pointer to the newly allocated filter entry. NULL is returned on
* failure.
*
****************************************************************************/
FAR struct ipfilter_entry_s *ipfilter_cfg_alloc(sa_family_t family);
/****************************************************************************
* Name: ipfilter_cfg_add
*
* Description:
* Add a new filter configuration entry for the given address family to the
* end of specified chain.
*
* Input Parameters:
* entry - The filter entry to add
* family - The address family of the filter entry
* chain - The chain to add the filter entry to
*
* Returned Value:
* None
*
****************************************************************************/
void ipfilter_cfg_add(FAR struct ipfilter_entry_s *entry,
sa_family_t family, enum ipfilter_chain_e chain);
/****************************************************************************
* Name: ipfilter_cfg_clear
*
* Description:
* Clear all filter configuration entries for the given address family from
* the specified chain.
*
* Input Parameters:
* family - The address family of the filter entry to clear
* chain - The chain to clear the filter entries from
*
* Returned Value:
* None
*
****************************************************************************/
void ipfilter_cfg_clear(sa_family_t family, enum ipfilter_chain_e chain);
/****************************************************************************
* Name: ipv4_filter_in / ipv6_filter_in
*
* Description:
* Handling IPv4/IPv6 filter on local input. Do nothing if the input
* packet is accepted, set d_len to 0 if the input packet needs to be
* dropped, and set d_iob to reject reply if the input packet is rejected.
*
* Input Parameters:
* dev - The network device that the packet comes from
*
* Returned Value:
* IPFILTER_TARGET_ACCEPT(0) - The input packet is accepted
* IPFILTER_TARGET_DROP(-1) - The input packet needs to be dropped
* IPFILTER_TARGET_REJECT(-2) - The input packet is rejected
*
* Assumptions:
* The network is locked. The d_iob and d_len in the dev are set.
*
****************************************************************************/
#ifdef CONFIG_NET_IPv4
int ipv4_filter_in(FAR struct net_driver_s *dev);
#endif
#ifdef CONFIG_NET_IPv6
int ipv6_filter_in(FAR struct net_driver_s *dev);
#endif
/****************************************************************************
* Name: ipfilter_out
*
* Description:
* Handling filter on local output. Do nothing if the output packet
* is accepted, set d_len to 0 if the output packet needs to be dropped,
* and set d_iob to reject reply if the output packet is rejected.
*
* Input Parameters:
* dev - The network device that the packet goes to
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked. Only IPv4 and IPv6 packets call this function.
*
****************************************************************************/
void ipfilter_out(FAR struct net_driver_s *dev);
/****************************************************************************
* Name: ipv4_filter_fwd / ipv6_filter_fwd
*
* Description:
* Handling IPv4/IPv6 filter on forwarding. Just return the target action,
* and relies on the caller to do the actual drop / reject.
*
* Input Parameters:
* indev - The network device that the packet comes from
* outdev - The network device that the packet goes to
* ipv4/ipv6 - The IPv4/IPv6 header
*
* Returned Value:
* IPFILTER_TARGET_ACCEPT(0) - The input packet is accepted
* IPFILTER_TARGET_DROP(-1) - The input packet needs to be dropped
* IPFILTER_TARGET_REJECT(-2) - The input packet needs to be rejected
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
#if defined(CONFIG_NET_IPFORWARD) && defined(CONFIG_NET_IPv4)
int ipv4_filter_fwd(FAR struct net_driver_s *indev,
FAR struct net_driver_s *outdev,
FAR struct ipv4_hdr_s *ipv4);
#endif
#if defined(CONFIG_NET_IPFORWARD) && defined(CONFIG_NET_IPv6)
int ipv6_filter_fwd(FAR struct net_driver_s *indev,
FAR struct net_driver_s *outdev,
FAR struct ipv6_hdr_s *ipv6);
#endif
#endif /* CONFIG_NET_IPFILTER */
#endif /* __NET_IPFILTER_IPFILTER_H */

View file

@ -38,6 +38,7 @@
#include "utils/utils.h"
#include "sixlowpan/sixlowpan.h"
#include "icmp/icmp.h"
#include "ipfilter/ipfilter.h"
#include "ipforward/ipforward.h"
#include "nat/nat.h"
#include "devif/devif.h"
@ -214,6 +215,27 @@ static int ipv4_dev_forward(FAR struct net_driver_s *dev,
#endif
int ret;
#ifdef CONFIG_NET_IPFILTER
/* Do filter before forwarding, to make sure we drop silently before
* replying any other errors.
*/
ret = ipv4_filter_fwd(dev, fwddev, ipv4);
if (ret < 0)
{
ninfo("Drop/Reject FORWARD packet due to filter %d\n", ret);
/* Let ipv4_forward reply the reject. */
if (ret == IPFILTER_TARGET_REJECT)
{
ret = -ENETUNREACH;
}
goto errout;
}
#endif
/* Verify that the full packet will fit within the forwarding device's MTU
* if DF is set.
*/

View file

@ -40,6 +40,7 @@
#include "sixlowpan/sixlowpan.h"
#include "devif/devif.h"
#include "icmpv6/icmpv6.h"
#include "ipfilter/ipfilter.h"
#include "ipforward/ipforward.h"
#if defined(CONFIG_NET_IPFORWARD) && defined(CONFIG_NET_IPv6)
@ -337,6 +338,27 @@ static int ipv6_dev_forward(FAR struct net_driver_s *dev,
#endif
int ret;
#ifdef CONFIG_NET_IPFILTER
/* Do filter before forwarding, to make sure we drop silently before
* replying any other errors.
*/
ret = ipv6_filter_fwd(dev, fwddev, ipv6);
if (ret < 0)
{
ninfo("Drop/Reject FORWARD packet due to filter %d\n", ret);
/* Let ipv6_forward reply the reject. */
if (ret == IPFILTER_TARGET_REJECT)
{
ret = -ENETUNREACH;
}
goto errout;
}
#endif
/* If the interface isn't "up", we can't forward. */
if ((fwddev->d_flags & IFF_UP) == 0)