nft-fullcone: init OpenWrt package
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
This commit is contained in:
commit
90834e19a0
45
Makefile
Normal file
45
Makefile
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
# Copyright (c) 2018 Chion Tang <tech@chionlab.moe>
|
||||||
|
# Original xt_FULLCONENAT and related iptables extension author
|
||||||
|
# Copyright (c) 2019-2022 GitHub/llccd Twitter/@gNodeB
|
||||||
|
# Added IPv6 support for xt_FULLCONENAT and ip6tables extension
|
||||||
|
# Ported to recent kernel versions
|
||||||
|
# Copyright (c) 2022 Syrone Wong <wong.syrone@gmail.com>
|
||||||
|
# Massively rewrite the whole module, split the original code into library and nftables 'fullcone' expression module
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
include $(INCLUDE_DIR)/kernel.mk
|
||||||
|
|
||||||
|
PKG_NAME:=nft-fullcone
|
||||||
|
PKG_RELEASE:=1
|
||||||
|
|
||||||
|
include $(INCLUDE_DIR)/package.mk
|
||||||
|
|
||||||
|
define KernelPackage/nft-fullcone
|
||||||
|
SUBMENU:=Netfilter Extensions
|
||||||
|
DEPENDS:=@IPV6 +kmod-nft-core +kmod-nf-conntrack +kmod-nf-conntrack6
|
||||||
|
TITLE:=nftables fullcone expression support
|
||||||
|
FILES:= $(PKG_BUILD_DIR)/nft_fullcone.ko
|
||||||
|
KCONFIG:= CONFIG_NFT_FULLCONE=y CONFIG_NF_NAT=y CONFIG_NF_NAT_IPV6=y CONFIG_NF_CONNTRACK_EVENTS=y CONFIG_NF_CONNTRACK_CHAIN_EVENTS=y
|
||||||
|
PROVIDES:=$(PKG_NAME)
|
||||||
|
AUTOLOAD:=$(call AutoProbe,nft_fullcone)
|
||||||
|
endef
|
||||||
|
|
||||||
|
define KernelPackage/nft-fullcone/Description
|
||||||
|
Kernel module adds the fullcone expression that you can use
|
||||||
|
to perform NAT in the RFC3489-compatible full cone SNAT flavour.
|
||||||
|
Currently only UDP traffic is supported for full-cone NAT.
|
||||||
|
For other protos FULLCONENAT is equivalent to MASQUERADE.
|
||||||
|
endef
|
||||||
|
|
||||||
|
# make use of all CPUs
|
||||||
|
define Build/Compile
|
||||||
|
+$(MAKE) $(PKG_JOBS) $(KERNEL_MAKEOPTS) \
|
||||||
|
M="$(PKG_BUILD_DIR)" \
|
||||||
|
EXTRA_CFLAGS="$(BUILDFLAGS)" \
|
||||||
|
$(if $(CONFIG_IPv6),EXTRA_CFLAGS+="-DCONFIG_SFE_ECM",) \
|
||||||
|
modules
|
||||||
|
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call KernelPackage,nft-fullcone))
|
18
src/Kconfig
Normal file
18
src/Kconfig
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#
|
||||||
|
# Nftables/netfilter fullcone expression support
|
||||||
|
#
|
||||||
|
config NFT_FULLCONE
|
||||||
|
depends on NF_CONNTRACK
|
||||||
|
depends on NF_NAT
|
||||||
|
tristate "Netfilter nf_tables fullcone support"
|
||||||
|
help
|
||||||
|
This options adds the "fullcone" expression that you can use
|
||||||
|
to perform NAT in the RFC3489-compatible full cone SNAT flavour.
|
||||||
|
Currently only UDP traffic is supported for full-cone NAT.
|
||||||
|
For other protos FULLCONENAT is equivalent to MASQUERADE.
|
||||||
|
|
||||||
|
To compile this code as a module, choose M here: the module will be
|
||||||
|
called nft_fullcone.
|
||||||
|
|
||||||
|
If unsure, say N.
|
||||||
|
|
29
src/Lindent
Executable file
29
src/Lindent
Executable file
@ -0,0 +1,29 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
# original
|
||||||
|
#PARAM="-npro -kr -i8 -ts8 -sob -l80 -ss -ncs -cp1"
|
||||||
|
# use wide screen in 21th century
|
||||||
|
PARAM="-npro -kr -i8 -ts8 -sob -l120 -ss -ncs -cp1"
|
||||||
|
|
||||||
|
RES=$(indent --version | cut -d' ' -f3)
|
||||||
|
if [ "$RES" = "" ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
V1=$(echo $RES | cut -d'.' -f1)
|
||||||
|
V2=$(echo $RES | cut -d'.' -f2)
|
||||||
|
V3=$(echo $RES | cut -d'.' -f3)
|
||||||
|
|
||||||
|
if [ $V1 -gt 2 ]; then
|
||||||
|
PARAM="$PARAM -il0"
|
||||||
|
elif [ $V1 -eq 2 ]; then
|
||||||
|
if [ $V2 -gt 2 ]; then
|
||||||
|
PARAM="$PARAM -il0"
|
||||||
|
elif [ $V2 -eq 2 ]; then
|
||||||
|
if [ $V3 -ge 10 ]; then
|
||||||
|
PARAM="$PARAM -il0"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
indent $PARAM "$@"
|
18
src/Makefile
Normal file
18
src/Makefile
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#
|
||||||
|
# Makefile for Nftables/netfilter fullcone expression support.
|
||||||
|
#
|
||||||
|
|
||||||
|
# module name, should not have the same name as src file names
|
||||||
|
TARGET = nft_fullcone
|
||||||
|
|
||||||
|
obj-m += $(TARGET).o
|
||||||
|
|
||||||
|
$(TARGET)-objs := \
|
||||||
|
nf_nat_fullcone.o \
|
||||||
|
nft_ext_fullcone.o
|
||||||
|
|
||||||
|
# product
|
||||||
|
ccflags-y += -Werror -Wall
|
||||||
|
|
||||||
|
# develop
|
||||||
|
#ccflags-y += -Wall -Wno-unused-function
|
1611
src/nf_nat_fullcone.c
Normal file
1611
src/nf_nat_fullcone.c
Normal file
File diff suppressed because it is too large
Load Diff
156
src/nf_nat_fullcone.h
Normal file
156
src/nf_nat_fullcone.h
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Nftables NAT extension: fullcone expression support library header
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Chion Tang <tech@chionlab.moe>
|
||||||
|
* Original xt_FULLCONENAT and related iptables extension author
|
||||||
|
* Copyright (c) 2019-2022 GitHub/llccd Twitter/@gNodeB
|
||||||
|
* Added IPv6 support for xt_FULLCONENAT and ip6tables extension
|
||||||
|
* Ported to recent kernel versions
|
||||||
|
* Copyright (c) 2022 Syrone Wong <wong.syrone@gmail.com>
|
||||||
|
* Massively rewrite the whole module, split the original code into library and nftables 'fullcone' expression module
|
||||||
|
*/
|
||||||
|
#ifndef _NF_NAT_FULLCONE_H_
|
||||||
|
#define _NF_NAT_FULLCONE_H_
|
||||||
|
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/skbuff.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/inetdevice.h>
|
||||||
|
#include <linux/netfilter/nf_tables.h>
|
||||||
|
|
||||||
|
#include <net/netfilter/nf_nat.h>
|
||||||
|
#include <net/netfilter/nf_conntrack.h>
|
||||||
|
#include <net/netfilter/nf_conntrack_zones.h>
|
||||||
|
#include <net/netfilter/nf_conntrack_tuple.h>
|
||||||
|
#include <net/netfilter/nf_conntrack_core.h>
|
||||||
|
#include <net/netfilter/nf_conntrack_ecache.h>
|
||||||
|
|
||||||
|
#ifndef NF_NAT_RANGE_PROTO_RANDOM_FULLY
|
||||||
|
#define NF_NAT_RANGE_PROTO_RANDOM_FULLY (1 << 4)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
|
||||||
|
static inline int nf_ct_netns_get(struct net *net, u8 nfproto)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void nf_ct_netns_put(struct net *net, u8 nfproto)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enum nft_fullcone_attributes - nf_tables fullcone expression netlink attributes
|
||||||
|
*
|
||||||
|
* @NFTA_FULLCONE_REG_PROTO_MIN: source register of proto range start (NLA_U32: nft_registers)
|
||||||
|
* @NFTA_FULLCONE_REG_PROTO_MAX: source register of proto range end (NLA_U32: nft_registers)
|
||||||
|
* @NFTA_FULLCONE_FLAGS: NAT flags (see NF_NAT_RANGE_* in linux/netfilter/nf_nat.h) (NLA_U32)
|
||||||
|
*/
|
||||||
|
enum nft_fullcone_attributes {
|
||||||
|
NFTA_FULLCONE_UNSPEC,
|
||||||
|
NFTA_FULLCONE_REG_PROTO_MIN,
|
||||||
|
NFTA_FULLCONE_REG_PROTO_MAX,
|
||||||
|
NFTA_FULLCONE_FLAGS,
|
||||||
|
__NFTA_FULLCONE_MAX
|
||||||
|
};
|
||||||
|
#define NFTA_FULLCONE_MAX (__NFTA_FULLCONE_MAX - 1)
|
||||||
|
|
||||||
|
/* fullcone specific data structures */
|
||||||
|
|
||||||
|
struct nat_mapping_original_tuple {
|
||||||
|
struct nf_conntrack_tuple tuple;
|
||||||
|
|
||||||
|
struct list_head node;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nat_mapping {
|
||||||
|
uint16_t port; /* external source port */
|
||||||
|
__be32 addr; /* external source ip address */
|
||||||
|
|
||||||
|
__be32 int_addr; /* internal source ip address */
|
||||||
|
uint16_t int_port; /* internal source port */
|
||||||
|
|
||||||
|
int refer_count; /* how many references linked to this mapping
|
||||||
|
* aka. length of original_tuple_list */
|
||||||
|
|
||||||
|
struct list_head original_tuple_list;
|
||||||
|
|
||||||
|
struct hlist_node node_by_ext_port;
|
||||||
|
struct hlist_node node_by_int_src;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_NF_NAT_IPV6) || (IS_ENABLED(CONFIG_IPV6) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0))
|
||||||
|
struct nat_mapping6 {
|
||||||
|
uint16_t port; /* external source port */
|
||||||
|
union nf_inet_addr addr; /* external source ip address */
|
||||||
|
|
||||||
|
union nf_inet_addr int_addr; /* internal source ip address */
|
||||||
|
uint16_t int_port; /* internal source port */
|
||||||
|
|
||||||
|
int refer_count; /* how many references linked to this mapping
|
||||||
|
* aka. length of original_tuple_list */
|
||||||
|
|
||||||
|
struct list_head original_tuple_list;
|
||||||
|
|
||||||
|
struct hlist_node node_by_ext_port;
|
||||||
|
struct hlist_node node_by_int_src;
|
||||||
|
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct tuple_list {
|
||||||
|
struct nf_conntrack_tuple tuple_original;
|
||||||
|
struct nf_conntrack_tuple tuple_reply;
|
||||||
|
struct list_head list;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* fullcone specific data structures end */
|
||||||
|
|
||||||
|
// NOTE: declaration listed here must use EXPORT_SYMBOL_*
|
||||||
|
|
||||||
|
unsigned int nf_nat_fullcone_ipv4(struct sk_buff *skb, unsigned int hooknum,
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)
|
||||||
|
struct nf_nat_range2 *range,
|
||||||
|
#else
|
||||||
|
struct nf_nat_range *range,
|
||||||
|
#endif
|
||||||
|
const struct net_device *out);
|
||||||
|
|
||||||
|
unsigned int nf_nat_fullcone_ipv6(struct sk_buff *skb, unsigned int hooknum,
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)
|
||||||
|
struct nf_nat_range2 *range,
|
||||||
|
#else
|
||||||
|
struct nf_nat_range *range,
|
||||||
|
#endif
|
||||||
|
const struct net_device *out);
|
||||||
|
|
||||||
|
void nf_nat_fullcone_handle_dying_tuples(void);
|
||||||
|
void nf_nat_fullcone_destroy_mappings(void);
|
||||||
|
void nf_nat_fullcone_dying_tuple_list_add(struct list_head *new_dying);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For [FUTURE] usage
|
||||||
|
*
|
||||||
|
* from https://elixir.bootlin.com/linux/v5.15.32/source/net/netfilter/xt_nat.c#L37
|
||||||
|
static void xt_nat_convert_range(struct nf_nat_range2 *dst,
|
||||||
|
const struct nf_nat_ipv4_range *src)
|
||||||
|
{
|
||||||
|
memset(&dst->min_addr, 0, sizeof(dst->min_addr));
|
||||||
|
memset(&dst->max_addr, 0, sizeof(dst->max_addr));
|
||||||
|
// base_proto is nf_nat_range2 specific
|
||||||
|
memset(&dst->base_proto, 0, sizeof(dst->base_proto));
|
||||||
|
|
||||||
|
dst->flags = src->flags;
|
||||||
|
dst->min_addr.ip = src->min_ip;
|
||||||
|
dst->max_addr.ip = src->max_ip;
|
||||||
|
dst->min_proto = src->min;
|
||||||
|
dst->max_proto = src->max;
|
||||||
|
}
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#endif /*_NF_NAT_FULLCONE_H_ */
|
458
src/nft_ext_fullcone.c
Normal file
458
src/nft_ext_fullcone.c
Normal file
@ -0,0 +1,458 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* Nftables NAT extension: fullcone expression support
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Chion Tang <tech@chionlab.moe>
|
||||||
|
* Original xt_FULLCONENAT and related iptables extension author
|
||||||
|
* Copyright (c) 2019-2022 GitHub/llccd Twitter/@gNodeB
|
||||||
|
* Added IPv6 support for xt_FULLCONENAT and ip6tables extension
|
||||||
|
* Ported to recent kernel versions
|
||||||
|
* Copyright (c) 2022 Syrone Wong <wong.syrone@gmail.com>
|
||||||
|
* Massively rewrite the whole module, split the original code into library and nftables 'fullcone' expression module
|
||||||
|
*/
|
||||||
|
#define pr_fmt(fmt) "fullcone " KBUILD_MODNAME ": " fmt
|
||||||
|
#define NF_FULLCONE_WORKQUEUE_NAME "fullcone " KBUILD_MODNAME ": wq"
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/netlink.h>
|
||||||
|
#include <linux/netfilter.h>
|
||||||
|
#include <linux/netfilter/nf_tables.h>
|
||||||
|
#include <net/netfilter/nf_tables.h>
|
||||||
|
#include <net/netfilter/nf_nat.h>
|
||||||
|
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
|
||||||
|
#include "nf_nat_fullcone.h"
|
||||||
|
|
||||||
|
static void nft_fullcone_set_regs(const struct nft_expr *expr, const struct nft_regs *regs,
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)
|
||||||
|
struct nf_nat_range2 *range);
|
||||||
|
#else
|
||||||
|
struct nf_nat_range *range);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS
|
||||||
|
struct notifier_block ct_event_notifier;
|
||||||
|
#else
|
||||||
|
struct nf_ct_event_notifier ct_event_notifier;
|
||||||
|
#endif
|
||||||
|
static DEFINE_MUTEX(nf_ct_net_event_lock);
|
||||||
|
int ct_event_notifier_registered = 0;
|
||||||
|
|
||||||
|
int module_refer_count = 0;
|
||||||
|
|
||||||
|
static void gc_worker(struct work_struct *work);
|
||||||
|
static struct workqueue_struct *wq __read_mostly = NULL;
|
||||||
|
static DECLARE_DELAYED_WORK(gc_worker_wk, gc_worker);
|
||||||
|
|
||||||
|
static void gc_worker(struct work_struct *work)
|
||||||
|
{
|
||||||
|
nf_nat_fullcone_handle_dying_tuples();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct nft_fullcone {
|
||||||
|
u32 flags;
|
||||||
|
u8 sreg_proto_min;
|
||||||
|
u8 sreg_proto_max;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct nla_policy nft_fullcone_policy[NFTA_FULLCONE_MAX + 1] = {
|
||||||
|
[NFTA_FULLCONE_FLAGS] = {.type = NLA_U32 },
|
||||||
|
[NFTA_FULLCONE_REG_PROTO_MIN] = {.type = NLA_U32 },
|
||||||
|
[NFTA_FULLCONE_REG_PROTO_MAX] = {.type = NLA_U32 },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* conntrack destroy event callback function */
|
||||||
|
#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS
|
||||||
|
static int ct_event_cb(struct notifier_block *this, unsigned long events, void *ptr)
|
||||||
|
{
|
||||||
|
struct nf_ct_event *item = ptr;
|
||||||
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)
|
||||||
|
static int ct_event_cb(unsigned int events, const struct nf_ct_event *item)
|
||||||
|
{
|
||||||
|
#else
|
||||||
|
static int ct_event_cb(unsigned int events, struct nf_ct_event *item)
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
struct nf_conn *ct;
|
||||||
|
struct nf_conntrack_tuple *ct_tuple_reply, *ct_tuple_original;
|
||||||
|
uint8_t protonum;
|
||||||
|
struct tuple_list *dying_tuple_item;
|
||||||
|
|
||||||
|
ct = item->ct;
|
||||||
|
/* we handle only conntrack destroy events */
|
||||||
|
if (ct == NULL || !(events & (1 << IPCT_DESTROY))) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ct_tuple_original = &(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
|
||||||
|
|
||||||
|
ct_tuple_reply = &(ct->tuplehash[IP_CT_DIR_REPLY].tuple);
|
||||||
|
|
||||||
|
protonum = (ct_tuple_original->dst).protonum;
|
||||||
|
if (protonum != IPPROTO_UDP) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dying_tuple_item = kmalloc(sizeof(struct tuple_list), GFP_ATOMIC);
|
||||||
|
|
||||||
|
if (dying_tuple_item == NULL) {
|
||||||
|
pr_debug("warning: ct_event_cb(): kmalloc failed.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&(dying_tuple_item->tuple_original), ct_tuple_original, sizeof(struct nf_conntrack_tuple));
|
||||||
|
memcpy(&(dying_tuple_item->tuple_reply), ct_tuple_reply, sizeof(struct nf_conntrack_tuple));
|
||||||
|
|
||||||
|
nf_nat_fullcone_dying_tuple_list_add(&(dying_tuple_item->list));
|
||||||
|
|
||||||
|
if (wq != NULL)
|
||||||
|
queue_delayed_work(wq, &gc_worker_wk, msecs_to_jiffies(100));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) && !defined(CONFIG_NF_CONNTRACK_CHAIN_EVENTS)
|
||||||
|
static int exp_event_cb(unsigned int events, const struct nf_exp_event *item)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int nft_fullcone_validate(const struct nft_ctx *ctx, const struct nft_expr *expr, const struct nft_data **data)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
// TODO: check hooks
|
||||||
|
return nft_chain_validate_hooks(ctx->chain, (1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_POST_ROUTING));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nft_fullcone_init(const struct nft_ctx *ctx, const struct nft_expr *expr, const struct nlattr *const tb[])
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
int register_ct_notifier_ret = 0;
|
||||||
|
|
||||||
|
err = nf_ct_netns_get(ctx->net, ctx->family);
|
||||||
|
|
||||||
|
mutex_lock(&nf_ct_net_event_lock);
|
||||||
|
|
||||||
|
module_refer_count++;
|
||||||
|
|
||||||
|
pr_debug("nft_fullcone_init(): module_refer_count is now %d\n", module_refer_count);
|
||||||
|
|
||||||
|
if (module_refer_count == 1) {
|
||||||
|
#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS
|
||||||
|
ct_event_notifier.notifier_call = ct_event_cb;
|
||||||
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)
|
||||||
|
ct_event_notifier.ct_event = ct_event_cb;
|
||||||
|
ct_event_notifier.exp_event = exp_event_cb;
|
||||||
|
#else
|
||||||
|
ct_event_notifier.fcn = ct_event_cb;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) && !defined(CONFIG_NF_CONNTRACK_CHAIN_EVENTS)
|
||||||
|
if (!READ_ONCE(ctx->net->ct.nf_conntrack_event_cb)) {
|
||||||
|
nf_conntrack_register_notifier(ctx->net, &ct_event_notifier);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
register_ct_notifier_ret = nf_conntrack_register_notifier(ctx->net, &ct_event_notifier);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (register_ct_notifier_ret) {
|
||||||
|
/* non-zero means failure */
|
||||||
|
pr_warn("failed to register a conntrack notifier. Disable active GC for mappings.\n");
|
||||||
|
} else {
|
||||||
|
ct_event_notifier_registered = 1;
|
||||||
|
pr_debug("nft_fullcone_init(): ct_event_notifier registered\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&nf_ct_net_event_lock);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nft_fullcone_dump(struct sk_buff *skb, const struct nft_expr *expr)
|
||||||
|
{
|
||||||
|
const struct nft_fullcone *priv = nft_expr_priv(expr);
|
||||||
|
|
||||||
|
if (priv->flags != 0 && nla_put_be32(skb, NFTA_FULLCONE_FLAGS, htonl(priv->flags)))
|
||||||
|
goto nla_put_failure;
|
||||||
|
|
||||||
|
if (priv->sreg_proto_min) {
|
||||||
|
if (nft_dump_register(skb, NFTA_FULLCONE_REG_PROTO_MIN,
|
||||||
|
priv->sreg_proto_min) ||
|
||||||
|
nft_dump_register(skb, NFTA_FULLCONE_REG_PROTO_MAX, priv->sreg_proto_max))
|
||||||
|
goto nla_put_failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
nla_put_failure:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* nft_fullcone_set_regs sets nft_regs from nft_expr fullcone specific private data */
|
||||||
|
static void nft_fullcone_set_regs(const struct nft_expr *expr, const struct nft_regs *regs,
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)
|
||||||
|
struct nf_nat_range2 *range
|
||||||
|
#else
|
||||||
|
struct nf_nat_range *range
|
||||||
|
#endif
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// private data connected via nft_expr_type.ops <==> nft_expr_ops.type
|
||||||
|
// private data type from nft_expr_type.{policy,maxattr,ops}
|
||||||
|
// private data size from nft_expr_ops.size
|
||||||
|
struct nft_fullcone *priv = nft_expr_priv(expr);
|
||||||
|
range->flags = priv->flags;
|
||||||
|
if (priv->sreg_proto_min) {
|
||||||
|
range->min_proto.all = (__force __be16)
|
||||||
|
nft_reg_load16(®s->data[priv->sreg_proto_min]);
|
||||||
|
range->max_proto.all = (__force __be16)
|
||||||
|
nft_reg_load16(®s->data[priv->sreg_proto_max]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nft_fullcone_ipv4_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt)
|
||||||
|
{
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)
|
||||||
|
struct nf_nat_range2 range;
|
||||||
|
#else
|
||||||
|
struct nf_nat_range range;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
memset(&range, 0, sizeof(range));
|
||||||
|
nft_fullcone_set_regs(expr, regs, &range);
|
||||||
|
regs->verdict.code = nf_nat_fullcone_ipv4(pkt->skb, nft_hook(pkt), &range, nft_out(pkt));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nft_fullcone_common_destory(const struct nft_ctx *ctx)
|
||||||
|
{
|
||||||
|
mutex_lock(&nf_ct_net_event_lock);
|
||||||
|
|
||||||
|
module_refer_count--;
|
||||||
|
|
||||||
|
pr_debug("nft_fullcone_common_destory(): module_refer_count is now %d\n", module_refer_count);
|
||||||
|
|
||||||
|
if (module_refer_count == 0) {
|
||||||
|
if (ct_event_notifier_registered) {
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) && !defined(CONFIG_NF_CONNTRACK_CHAIN_EVENTS)
|
||||||
|
nf_conntrack_unregister_notifier(ctx->net);
|
||||||
|
#else
|
||||||
|
nf_conntrack_unregister_notifier(ctx->net, &ct_event_notifier);
|
||||||
|
#endif
|
||||||
|
ct_event_notifier_registered = 0;
|
||||||
|
|
||||||
|
pr_debug("nft_fullcone_common_destory(): ct_event_notifier unregistered\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&nf_ct_net_event_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nft_fullcone_ipv4_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
|
||||||
|
{
|
||||||
|
nft_fullcone_common_destory(ctx);
|
||||||
|
nf_ct_netns_put(ctx->net, NFPROTO_IPV4);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct nft_expr_type nft_fullcone_ipv4_type;
|
||||||
|
static const struct nft_expr_ops nft_fullcone_ipv4_ops = {
|
||||||
|
.type = &nft_fullcone_ipv4_type,
|
||||||
|
.size = NFT_EXPR_SIZE(sizeof(struct nft_fullcone)),
|
||||||
|
.eval = nft_fullcone_ipv4_eval,
|
||||||
|
.init = nft_fullcone_init,
|
||||||
|
.destroy = nft_fullcone_ipv4_destroy,
|
||||||
|
.dump = nft_fullcone_dump,
|
||||||
|
.validate = nft_fullcone_validate,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct nft_expr_type nft_fullcone_ipv4_type __read_mostly = {
|
||||||
|
.family = NFPROTO_IPV4,
|
||||||
|
.name = "fullcone",
|
||||||
|
.ops = &nft_fullcone_ipv4_ops,
|
||||||
|
.policy = nft_fullcone_policy,
|
||||||
|
.maxattr = NFTA_FULLCONE_MAX,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_NF_TABLES_IPV6
|
||||||
|
static void nft_fullcone_ipv6_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt)
|
||||||
|
{
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)
|
||||||
|
struct nf_nat_range2 range;
|
||||||
|
#else
|
||||||
|
struct nf_nat_range range;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
memset(&range, 0, sizeof(range));
|
||||||
|
nft_fullcone_set_regs(expr, regs, &range);
|
||||||
|
regs->verdict.code = nf_nat_fullcone_ipv6(pkt->skb, nft_hook(pkt), &range, nft_out(pkt));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nft_fullcone_ipv6_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
|
||||||
|
{
|
||||||
|
nft_fullcone_common_destory(ctx);
|
||||||
|
nf_ct_netns_put(ctx->net, NFPROTO_IPV6);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct nft_expr_type nft_fullcone_ipv6_type;
|
||||||
|
static const struct nft_expr_ops nft_fullcone_ipv6_ops = {
|
||||||
|
.type = &nft_fullcone_ipv6_type,
|
||||||
|
.size = NFT_EXPR_SIZE(sizeof(struct nft_fullcone)),
|
||||||
|
.eval = nft_fullcone_ipv6_eval,
|
||||||
|
.init = nft_fullcone_init,
|
||||||
|
.destroy = nft_fullcone_ipv6_destroy,
|
||||||
|
.dump = nft_fullcone_dump,
|
||||||
|
.validate = nft_fullcone_validate,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct nft_expr_type nft_fullcone_ipv6_type __read_mostly = {
|
||||||
|
.family = NFPROTO_IPV6,
|
||||||
|
.name = "fullcone",
|
||||||
|
.ops = &nft_fullcone_ipv6_ops,
|
||||||
|
.policy = nft_fullcone_policy,
|
||||||
|
.maxattr = NFTA_FULLCONE_MAX,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init nft_fullcone_module_init_ipv6(void)
|
||||||
|
{
|
||||||
|
return nft_register_expr(&nft_fullcone_ipv6_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nft_fullcone_module_exit_ipv6(void)
|
||||||
|
{
|
||||||
|
nft_unregister_expr(&nft_fullcone_ipv6_type);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static inline int nft_fullcone_module_init_ipv6(void)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void nft_fullcone_module_exit_ipv6(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_NF_TABLES_INET
|
||||||
|
static void nft_fullcone_inet_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt)
|
||||||
|
{
|
||||||
|
switch (nft_pf(pkt)) {
|
||||||
|
case NFPROTO_IPV4:
|
||||||
|
return nft_fullcone_ipv4_eval(expr, regs, pkt);
|
||||||
|
case NFPROTO_IPV6:
|
||||||
|
return nft_fullcone_ipv6_eval(expr, regs, pkt);
|
||||||
|
}
|
||||||
|
|
||||||
|
WARN_ON_ONCE(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nft_fullcone_inet_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
|
||||||
|
{
|
||||||
|
nft_fullcone_common_destory(ctx);
|
||||||
|
nf_ct_netns_put(ctx->net, NFPROTO_INET);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct nft_expr_type nft_fullcone_inet_type;
|
||||||
|
static const struct nft_expr_ops nft_fullcone_inet_ops = {
|
||||||
|
.type = &nft_fullcone_inet_type,
|
||||||
|
.size = NFT_EXPR_SIZE(sizeof(struct nft_fullcone)),
|
||||||
|
.eval = nft_fullcone_inet_eval,
|
||||||
|
.init = nft_fullcone_init,
|
||||||
|
.destroy = nft_fullcone_inet_destroy,
|
||||||
|
.dump = nft_fullcone_dump,
|
||||||
|
.validate = nft_fullcone_validate,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct nft_expr_type nft_fullcone_inet_type __read_mostly = {
|
||||||
|
.family = NFPROTO_INET,
|
||||||
|
.name = "fullcone",
|
||||||
|
.ops = &nft_fullcone_inet_ops,
|
||||||
|
.policy = nft_fullcone_policy,
|
||||||
|
.maxattr = NFTA_FULLCONE_MAX,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init nft_fullcone_module_init_inet(void)
|
||||||
|
{
|
||||||
|
return nft_register_expr(&nft_fullcone_inet_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nft_fullcone_module_exit_inet(void)
|
||||||
|
{
|
||||||
|
nft_unregister_expr(&nft_fullcone_inet_type);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static inline int nft_fullcone_module_init_inet(void)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void nft_fullcone_module_exit_inet(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int __init nft_fullcone_module_init(void)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = nft_fullcone_module_init_ipv6();
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = nft_fullcone_module_init_inet();
|
||||||
|
if (ret < 0) {
|
||||||
|
nft_fullcone_module_exit_ipv6();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = nft_register_expr(&nft_fullcone_ipv4_type);
|
||||||
|
if (ret < 0) {
|
||||||
|
nft_fullcone_module_exit_inet();
|
||||||
|
nft_fullcone_module_exit_ipv6();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
wq = create_singlethread_workqueue(NF_FULLCONE_WORKQUEUE_NAME);
|
||||||
|
if (wq == NULL) {
|
||||||
|
pr_err("failed to create workqueue %s\n", NF_FULLCONE_WORKQUEUE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit nft_fullcone_module_exit(void)
|
||||||
|
{
|
||||||
|
nft_fullcone_module_exit_ipv6();
|
||||||
|
nft_fullcone_module_exit_inet();
|
||||||
|
nft_unregister_expr(&nft_fullcone_ipv4_type);
|
||||||
|
|
||||||
|
if (wq) {
|
||||||
|
cancel_delayed_work_sync(&gc_worker_wk);
|
||||||
|
flush_workqueue(wq);
|
||||||
|
destroy_workqueue(wq);
|
||||||
|
}
|
||||||
|
|
||||||
|
nf_nat_fullcone_handle_dying_tuples();
|
||||||
|
nf_nat_fullcone_destroy_mappings();
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(nft_fullcone_module_init);
|
||||||
|
module_exit(nft_fullcone_module_exit);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_AUTHOR("Syrone Wong <wong.syrone@gmail.com>");
|
||||||
|
MODULE_ALIAS_NFT_EXPR("fullcone");
|
||||||
|
MODULE_DESCRIPTION("Netfilter nftables fullcone expression support of RFC3489 full cone NAT");
|
Loading…
x
Reference in New Issue
Block a user