net: pkt_filter: Introduce additional hooks for pkt_filter
The additional hooks provide infrastructure to construct rules on another network stack levels. Main benefit of this approach is packets are pre-parsed and e.g. IP filter is easier to implement. These hooks are equivalent of prerouting and local_in in linux's netfilter. Signed-off-by: Marcin Gasiorek <marcin.gasiorek@nordicsemi.no>
This commit is contained in:
parent
eb16ab551f
commit
5894bec82f
|
@ -1275,6 +1275,37 @@ static inline bool net_pkt_filter_recv_ok(struct net_pkt *pkt)
|
||||||
|
|
||||||
#endif /* CONFIG_NET_PKT_FILTER */
|
#endif /* CONFIG_NET_PKT_FILTER */
|
||||||
|
|
||||||
|
#if defined(CONFIG_NET_PKT_FILTER) && \
|
||||||
|
(defined(CONFIG_NET_PKT_FILTER_IPV4_HOOK) || defined(CONFIG_NET_PKT_FILTER_IPV6_HOOK))
|
||||||
|
|
||||||
|
bool net_pkt_filter_ip_recv_ok(struct net_pkt *pkt);
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
static inline bool net_pkt_filter_ip_recv_ok(struct net_pkt *pkt)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(pkt);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_NET_PKT_FILTER_IPV4_HOOK || CONFIG_NET_PKT_FILTER_IPV6_HOOK */
|
||||||
|
|
||||||
|
#if defined(CONFIG_NET_PKT_FILTER) && defined(CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK)
|
||||||
|
|
||||||
|
bool net_pkt_filter_local_in_recv_ok(struct net_pkt *pkt);
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
static inline bool net_pkt_filter_local_in_recv_ok(struct net_pkt *pkt)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(pkt);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_NET_PKT_FILTER && CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK */
|
||||||
|
|
||||||
/* @endcond */
|
/* @endcond */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -67,6 +67,12 @@ struct npf_rule_list {
|
||||||
extern struct npf_rule_list npf_send_rules;
|
extern struct npf_rule_list npf_send_rules;
|
||||||
/** @brief rule list applied to incoming packets */
|
/** @brief rule list applied to incoming packets */
|
||||||
extern struct npf_rule_list npf_recv_rules;
|
extern struct npf_rule_list npf_recv_rules;
|
||||||
|
/** @brief rule list applied for local incoming packets */
|
||||||
|
extern struct npf_rule_list npf_local_in_recv_rules;
|
||||||
|
/** @brief rule list applied for IPv4 incoming packets */
|
||||||
|
extern struct npf_rule_list npf_ipv4_recv_rules;
|
||||||
|
/** @brief rule list applied for IPv6 incoming packets */
|
||||||
|
extern struct npf_rule_list npf_ipv6_recv_rules;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Insert a rule at the front of given rule list
|
* @brief Insert a rule at the front of given rule list
|
||||||
|
@ -111,6 +117,27 @@ bool npf_remove_all_rules(struct npf_rule_list *rules);
|
||||||
#define npf_remove_all_send_rules() npf_remove_all_rules(&npf_send_rules)
|
#define npf_remove_all_send_rules() npf_remove_all_rules(&npf_send_rules)
|
||||||
#define npf_remove_all_recv_rules() npf_remove_all_rules(&npf_recv_rules)
|
#define npf_remove_all_recv_rules() npf_remove_all_rules(&npf_recv_rules)
|
||||||
|
|
||||||
|
#ifdef CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK
|
||||||
|
#define npf_insert_local_in_recv_rule(rule) npf_insert_rule(&npf_local_in_recv_rules, rule)
|
||||||
|
#define npf_append_local_in_recv_rule(rule) npf_append_rule(&npf_local_in_recv_rules, rule)
|
||||||
|
#define npf_remove_local_in_recv_rule(rule) npf_remove_rule(&npf_local_in_recv_rules, rule)
|
||||||
|
#define npf_remove_all_local_in_recv_rules() npf_remove_all_rules(&npf_local_in_recv_rules)
|
||||||
|
#endif /* CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK */
|
||||||
|
|
||||||
|
#ifdef CONFIG_NET_PKT_FILTER_IPV4_HOOK
|
||||||
|
#define npf_insert_ipv4_recv_rule(rule) npf_insert_rule(&npf_ipv4_recv_rules, rule)
|
||||||
|
#define npf_append_ipv4_recv_rule(rule) npf_append_rule(&npf_ipv4_recv_rules, rule)
|
||||||
|
#define npf_remove_ipv4_recv_rule(rule) npf_remove_rule(&npf_ipv4_recv_rules, rule)
|
||||||
|
#define npf_remove_all_ipv4_recv_rules() npf_remove_all_rules(&npf_ipv4_recv_rules)
|
||||||
|
#endif /* CONFIG_NET_PKT_FILTER_IPV4_HOOK */
|
||||||
|
|
||||||
|
#ifdef CONFIG_NET_PKT_FILTER_IPV6_HOOK
|
||||||
|
#define npf_insert_ipv6_recv_rule(rule) npf_insert_rule(&npf_ipv6_recv_rules, rule)
|
||||||
|
#define npf_append_ipv6_recv_rule(rule) npf_append_rule(&npf_ipv6_recv_rules, rule)
|
||||||
|
#define npf_remove_ipv6_recv_rule(rule) npf_remove_rule(&npf_ipv6_recv_rules, rule)
|
||||||
|
#define npf_remove_all_ipv6_recv_rules() npf_remove_all_rules(&npf_ipv6_recv_rules)
|
||||||
|
#endif /* CONFIG_NET_PKT_FILTER_IPV6_HOOK */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Statically define one packet filter rule
|
* @brief Statically define one packet filter rule
|
||||||
*
|
*
|
||||||
|
@ -296,6 +323,60 @@ extern npf_test_fn_t npf_size_inbounds;
|
||||||
.test.fn = npf_size_inbounds, \
|
.test.fn = npf_size_inbounds, \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @cond INTERNAL_HIDDEN */
|
||||||
|
|
||||||
|
struct npf_test_ip {
|
||||||
|
struct npf_test test;
|
||||||
|
uint8_t addr_family;
|
||||||
|
void *ipaddr;
|
||||||
|
uint32_t ipaddr_num;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern npf_test_fn_t npf_ip_src_addr_match;
|
||||||
|
extern npf_test_fn_t npf_ip_src_addr_unmatch;
|
||||||
|
|
||||||
|
/** @endcond */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Statically define a "ip address allowlist" packet filter condition
|
||||||
|
*
|
||||||
|
* This tests if the packet source ip address matches any of the ip
|
||||||
|
* addresses contained in the provided set.
|
||||||
|
*
|
||||||
|
* @param _name Name of the condition
|
||||||
|
* @param _ip_addr_array Array of <tt>struct in_addr</tt> or <tt>struct in6_addr</tt> items to test
|
||||||
|
*against
|
||||||
|
* @param _ip_addr_num number of IP addresses in the array
|
||||||
|
* @param _af Addresses family type (AF_INET / AF_INET6) in the array
|
||||||
|
*/
|
||||||
|
#define NPF_IP_SRC_ADDR_ALLOWLIST(_name, _ip_addr_array, _ip_addr_num, _af) \
|
||||||
|
struct npf_test_ip _name = { \
|
||||||
|
.addr_family = _af, \
|
||||||
|
.ipaddr = (_ip_addr_array), \
|
||||||
|
.ipaddr_num = _ip_addr_num, \
|
||||||
|
.test.fn = npf_ip_src_addr_match, \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Statically define a "ip address blocklist" packet filter condition
|
||||||
|
*
|
||||||
|
* This tests if the packet source ip address matches any of the ip
|
||||||
|
* addresses contained in the provided set.
|
||||||
|
*
|
||||||
|
* @param _name Name of the condition
|
||||||
|
* @param _ip_addr_array Array of <tt>struct in_addr</tt> or <tt>struct in6_addr</tt> items to test
|
||||||
|
*against
|
||||||
|
* @param _ip_addr_num number of IP addresses in the array
|
||||||
|
* @param _af Addresses family type (AF_INET / AF_INET6) in the array
|
||||||
|
*/
|
||||||
|
#define NPF_IP_SRC_ADDR_BLOCKLIST(_name, _ip_addr_array, _ip_addr_num, _af) \
|
||||||
|
struct npf_test_ip _name = { \
|
||||||
|
.addr_family = _af, \
|
||||||
|
.ipaddr = (_ip_addr_array), \
|
||||||
|
.ipaddr_num = _ip_addr_num, \
|
||||||
|
.test.fn = npf_ip_src_addr_unmatch, \
|
||||||
|
}
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -576,6 +576,11 @@ enum net_verdict net_conn_input(struct net_pkt *pkt,
|
||||||
uint8_t pkt_family = net_pkt_family(pkt);
|
uint8_t pkt_family = net_pkt_family(pkt);
|
||||||
uint16_t src_port = 0U, dst_port = 0U;
|
uint16_t src_port = 0U, dst_port = 0U;
|
||||||
|
|
||||||
|
if (!net_pkt_filter_local_in_recv_ok(pkt)) {
|
||||||
|
/* drop the packet */
|
||||||
|
return NET_DROP;
|
||||||
|
}
|
||||||
|
|
||||||
if (IS_ENABLED(CONFIG_NET_IP) && (pkt_family == AF_INET || pkt_family == AF_INET6)) {
|
if (IS_ENABLED(CONFIG_NET_IP) && (pkt_family == AF_INET || pkt_family == AF_INET6)) {
|
||||||
if (IS_ENABLED(CONFIG_NET_UDP) && proto == IPPROTO_UDP) {
|
if (IS_ENABLED(CONFIG_NET_UDP) && proto == IPPROTO_UDP) {
|
||||||
src_port = proto_hdr->udp->src_port;
|
src_port = proto_hdr->udp->src_port;
|
||||||
|
|
|
@ -302,6 +302,15 @@ enum net_verdict net_ipv4_input(struct net_pkt *pkt)
|
||||||
goto drop;
|
goto drop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
net_pkt_set_ipv4_ttl(pkt, hdr->ttl);
|
||||||
|
|
||||||
|
net_pkt_set_family(pkt, PF_INET);
|
||||||
|
|
||||||
|
if (!net_pkt_filter_ip_recv_ok(pkt)) {
|
||||||
|
/* drop the packet */
|
||||||
|
return NET_DROP;
|
||||||
|
}
|
||||||
|
|
||||||
if ((!net_ipv4_is_my_addr((struct in_addr *)hdr->dst) &&
|
if ((!net_ipv4_is_my_addr((struct in_addr *)hdr->dst) &&
|
||||||
!net_ipv4_is_addr_mcast((struct in_addr *)hdr->dst) &&
|
!net_ipv4_is_addr_mcast((struct in_addr *)hdr->dst) &&
|
||||||
!(hdr->proto == IPPROTO_UDP &&
|
!(hdr->proto == IPPROTO_UDP &&
|
||||||
|
@ -326,10 +335,6 @@ enum net_verdict net_ipv4_input(struct net_pkt *pkt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
net_pkt_set_ipv4_ttl(pkt, hdr->ttl);
|
|
||||||
|
|
||||||
net_pkt_set_family(pkt, PF_INET);
|
|
||||||
|
|
||||||
if (IS_ENABLED(CONFIG_NET_IPV4_FRAGMENT)) {
|
if (IS_ENABLED(CONFIG_NET_IPV4_FRAGMENT)) {
|
||||||
/* Check if this is a fragmented packet, and if so, handle reassembly */
|
/* Check if this is a fragmented packet, and if so, handle reassembly */
|
||||||
if ((ntohs(*((uint16_t *)&hdr->offset[0])) &
|
if ((ntohs(*((uint16_t *)&hdr->offset[0])) &
|
||||||
|
|
|
@ -520,6 +520,11 @@ enum net_verdict net_ipv6_input(struct net_pkt *pkt, bool is_loopback)
|
||||||
net_pkt_set_ipv6_hop_limit(pkt, NET_IPV6_HDR(pkt)->hop_limit);
|
net_pkt_set_ipv6_hop_limit(pkt, NET_IPV6_HDR(pkt)->hop_limit);
|
||||||
net_pkt_set_family(pkt, PF_INET6);
|
net_pkt_set_family(pkt, PF_INET6);
|
||||||
|
|
||||||
|
if (!net_pkt_filter_ip_recv_ok(pkt)) {
|
||||||
|
/* drop the packet */
|
||||||
|
return NET_DROP;
|
||||||
|
}
|
||||||
|
|
||||||
if (IS_ENABLED(CONFIG_NET_ROUTE_MCAST) &&
|
if (IS_ENABLED(CONFIG_NET_ROUTE_MCAST) &&
|
||||||
net_ipv6_is_addr_mcast((struct in6_addr *)hdr->dst)) {
|
net_ipv6_is_addr_mcast((struct in6_addr *)hdr->dst)) {
|
||||||
/* If the packet is a multicast packet and multicast routing
|
/* If the packet is a multicast packet and multicast routing
|
||||||
|
|
|
@ -12,6 +12,28 @@ config NET_PKT_FILTER
|
||||||
transmission and reception.
|
transmission and reception.
|
||||||
|
|
||||||
if NET_PKT_FILTER
|
if NET_PKT_FILTER
|
||||||
|
|
||||||
|
config NET_PKT_FILTER_IPV4_HOOK
|
||||||
|
bool "Additional network packet filtering hook inside IPv4 stack"
|
||||||
|
depends on NET_IPV4
|
||||||
|
help
|
||||||
|
This additional hook provides infrastructure to construct custom
|
||||||
|
rules on the IP packet.
|
||||||
|
|
||||||
|
config NET_PKT_FILTER_IPV6_HOOK
|
||||||
|
bool "Additional network packet filtering hook inside IPv6 stack"
|
||||||
|
depends on NET_IPV6
|
||||||
|
help
|
||||||
|
This additional hook provides infrastructure to construct custom
|
||||||
|
rules on the IP packet.
|
||||||
|
|
||||||
|
config NET_PKT_FILTER_LOCAL_IN_HOOK
|
||||||
|
bool "Additional network packet filtering hook for connection input"
|
||||||
|
depends on NET_IP
|
||||||
|
help
|
||||||
|
This additional hook provides infrastructure to construct custom
|
||||||
|
rules for e.g. TCP/UDP packets.
|
||||||
|
|
||||||
module = NET_PKT_FILTER
|
module = NET_PKT_FILTER
|
||||||
module-dep = NET_LOG
|
module-dep = NET_LOG
|
||||||
module-str = Log level for packet filtering
|
module-str = Log level for packet filtering
|
||||||
|
|
|
@ -25,6 +25,50 @@ struct npf_rule_list npf_recv_rules = {
|
||||||
.lock = { },
|
.lock = { },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK
|
||||||
|
struct npf_rule_list npf_local_in_recv_rules = {
|
||||||
|
.rule_head = SYS_SLIST_STATIC_INIT(&local_in_recv_rules.rule_head),
|
||||||
|
.lock = { },
|
||||||
|
};
|
||||||
|
#endif /* CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK */
|
||||||
|
|
||||||
|
#ifdef CONFIG_NET_PKT_FILTER_IPV4_HOOK
|
||||||
|
struct npf_rule_list npf_ipv4_recv_rules = {
|
||||||
|
.rule_head = SYS_SLIST_STATIC_INIT(&ipv4_recv_rules.rule_head),
|
||||||
|
.lock = { },
|
||||||
|
};
|
||||||
|
#endif /* CONFIG_NET_PKT_FILTER_IPV4_HOOK */
|
||||||
|
|
||||||
|
#ifdef CONFIG_NET_PKT_FILTER_IPV6_HOOK
|
||||||
|
struct npf_rule_list npf_ipv6_recv_rules = {
|
||||||
|
.rule_head = SYS_SLIST_STATIC_INIT(&ipv6_recv_rules.rule_head),
|
||||||
|
.lock = { },
|
||||||
|
};
|
||||||
|
#endif /* CONFIG_NET_PKT_FILTER_IPV6_HOOK */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helper function
|
||||||
|
*/
|
||||||
|
static struct npf_rule_list *get_ip_rules(uint8_t pf)
|
||||||
|
{
|
||||||
|
switch (pf) {
|
||||||
|
case PF_INET:
|
||||||
|
#ifdef CONFIG_NET_PKT_FILTER_IPV4_HOOK
|
||||||
|
return &npf_ipv4_recv_rules;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case PF_INET6:
|
||||||
|
#ifdef CONFIG_NET_PKT_FILTER_IPV6_HOOK
|
||||||
|
return &npf_ipv6_recv_rules;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Rule application
|
* Rule application
|
||||||
*/
|
*/
|
||||||
|
@ -98,6 +142,31 @@ bool net_pkt_filter_recv_ok(struct net_pkt *pkt)
|
||||||
return result == NET_OK;
|
return result == NET_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK
|
||||||
|
bool net_pkt_filter_local_in_recv_ok(struct net_pkt *pkt)
|
||||||
|
{
|
||||||
|
enum net_verdict result = lock_evaluate(&npf_local_in_recv_rules, pkt);
|
||||||
|
|
||||||
|
return result == NET_OK;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_NET_PKT_FILTER_LOCAL_IN_HOOK */
|
||||||
|
|
||||||
|
#if defined(CONFIG_NET_PKT_FILTER_IPV4_HOOK) || defined(CONFIG_NET_PKT_FILTER_IPV6_HOOK)
|
||||||
|
bool net_pkt_filter_ip_recv_ok(struct net_pkt *pkt)
|
||||||
|
{
|
||||||
|
struct npf_rule_list *rules = get_ip_rules(net_pkt_family(pkt));
|
||||||
|
|
||||||
|
if (!rules) {
|
||||||
|
NET_DBG("no rules");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum net_verdict result = lock_evaluate(rules, pkt);
|
||||||
|
|
||||||
|
return result == NET_OK;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_NET_PKT_FILTER_IPV4_HOOK || CONFIG_NET_PKT_FILTER_IPV6_HOOK */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Rule management
|
* Rule management
|
||||||
*/
|
*/
|
||||||
|
@ -199,3 +268,32 @@ bool npf_size_inbounds(struct npf_test *test, struct net_pkt *pkt)
|
||||||
|
|
||||||
return pkt_size >= bounds->min && pkt_size <= bounds->max;
|
return pkt_size >= bounds->min && pkt_size <= bounds->max;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool npf_ip_src_addr_match(struct npf_test *test, struct net_pkt *pkt)
|
||||||
|
{
|
||||||
|
struct npf_test_ip *test_ip =
|
||||||
|
CONTAINER_OF(test, struct npf_test_ip, test);
|
||||||
|
uint8_t pkt_family = net_pkt_family(pkt);
|
||||||
|
|
||||||
|
for (uint32_t ip_it = 0; ip_it < test_ip->ipaddr_num; ip_it++) {
|
||||||
|
if (IS_ENABLED(CONFIG_NET_IPV4) && pkt_family == AF_INET) {
|
||||||
|
struct in_addr *addr = (struct in_addr *)NET_IPV4_HDR(pkt)->src;
|
||||||
|
|
||||||
|
if (net_ipv4_addr_cmp(addr, &((struct in_addr *)test_ip->ipaddr)[ip_it])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (IS_ENABLED(CONFIG_NET_IPV6) && pkt_family == AF_INET6) {
|
||||||
|
struct in6_addr *addr = (struct in6_addr *)NET_IPV6_HDR(pkt)->src;
|
||||||
|
|
||||||
|
if (net_ipv6_addr_cmp(addr, &((struct in6_addr *)test_ip->ipaddr)[ip_it])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool npf_ip_src_addr_unmatch(struct npf_test *test, struct net_pkt *pkt)
|
||||||
|
{
|
||||||
|
return !npf_ip_src_addr_match(test, pkt);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue