├── Makefile ├── debian ├── Makefile ├── dkms-install.sh ├── dkms-remove.sh └── dkms.conf └── src ├── Kconfig ├── Lindent ├── Makefile ├── nf_nat_fullcone.c ├── nf_nat_fullcone.h └── nft_ext_fullcone.c /Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-only 2 | # Copyright (c) 2018 Chion Tang 3 | # Original xt_FULLCONENAT and related iptables extension author 4 | # Copyright (c) 2019-2022 GitHub/llccd Twitter/@gNodeB 5 | # Added IPv6 support for xt_FULLCONENAT and ip6tables extension 6 | # Ported to recent kernel versions 7 | # Copyright (c) 2022 Syrone Wong 8 | # Massively rewrite the whole module, split the original code into library and nftables 'fullcone' expression module 9 | 10 | include $(TOPDIR)/rules.mk 11 | include $(INCLUDE_DIR)/kernel.mk 12 | 13 | PKG_NAME:=nft-fullcone 14 | PKG_RELEASE:=1 15 | 16 | include $(INCLUDE_DIR)/package.mk 17 | 18 | define KernelPackage/nft-fullcone 19 | SUBMENU:=Netfilter Extensions 20 | DEPENDS:=@IPV6 +kmod-nft-core +kmod-nf-conntrack +kmod-nf-conntrack6 21 | TITLE:=nftables fullcone expression support 22 | FILES:= $(PKG_BUILD_DIR)/nft_fullcone.ko 23 | 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 24 | PROVIDES:=$(PKG_NAME) 25 | AUTOLOAD:=$(call AutoProbe,nft_fullcone) 26 | endef 27 | 28 | define KernelPackage/nft-fullcone/Description 29 | Kernel module adds the fullcone expression that you can use 30 | to perform NAT in the RFC3489-compatible full cone SNAT flavour. 31 | Currently only UDP traffic is supported for full-cone NAT. 32 | For other protos FULLCONENAT is equivalent to MASQUERADE. 33 | endef 34 | 35 | # make use of all CPUs 36 | define Build/Compile 37 | +$(MAKE) $(PKG_JOBS) $(KERNEL_MAKEOPTS) \ 38 | M="$(PKG_BUILD_DIR)" \ 39 | EXTRA_CFLAGS="$(BUILDFLAGS)" \ 40 | $(if $(CONFIG_IPv6),EXTRA_CFLAGS+="-DCONFIG_SFE_ECM",) \ 41 | modules 42 | 43 | endef 44 | 45 | $(eval $(call KernelPackage,nft-fullcone)) 46 | -------------------------------------------------------------------------------- /debian/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # apt update -y 3 | # apt upgrade -y 4 | # apt install --reinstall linux-headers-$(uname -r) -y 5 | # apt install build-essential autoconf autogen libtool pkg-config libgmp3-dev bison flex libreadline-dev git libedit-dev libmnl-dev make dkms -y 6 | # apt autoremove -y 7 | # 8 | 9 | obj-m += nft_fullcone.o 10 | 11 | nft_fullcone-y := ../src/nft_ext_fullcone.o ../src/nf_nat_fullcone.o 12 | 13 | KVERSION = $(shell uname -r) 14 | 15 | all: 16 | make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules 17 | 18 | install: 19 | make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules_install 20 | depmod -A 21 | modprobe nft_fullcone 22 | 23 | uninstall: 24 | rmmod nft_fullcone || echo "Please remove all nft rules with fullcone and run [rmmod nft_fullcone] manually" 25 | rm -f /lib/modules/$(KVERSION)/extra/nft_fullcone.ko && depmod -A 26 | 27 | clean: 28 | make -C /lib/modules/$(KVERSION)/build M=$(PWD) clean 29 | -------------------------------------------------------------------------------- /debian/dkms-install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PKG_DIR="$(cd ..; pwd)" 4 | PKG_NAME=nft-fullcone 5 | PKG_VERSION=1.0 6 | 7 | # apt update -y 8 | # apt upgrade -y 9 | # apt install --reinstall linux-headers-$(uname -r) -y 10 | # apt install --reinstall pve-headers pve-headers-$(uname -r) -y 11 | # apt install build-essential autoconf autogen libtool pkg-config libgmp3-dev bison flex libreadline-dev git libedit-dev libmnl-dev make dkms -y 12 | # apt autoremove -y 13 | 14 | cp dkms.conf .. 15 | cp -r ${PKG_DIR} /usr/src/${PKG_NAME}-${PKG_VERSION} 16 | 17 | dkms install -m ${PKG_NAME} -v ${PKG_VERSION} 18 | # By default, DKMS only installs the kernel module for the current kernel. 19 | # You can specify a different kernel version, for example, the one you are going to boot into. 20 | # dkms install -m ${PKG_NAME} -v ${PKG_VERSION} -k 21 | -------------------------------------------------------------------------------- /debian/dkms-remove.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PKG_NAME=nft-fullcone 4 | PKG_VERSION=1.0 5 | 6 | dkms remove ${PKG_NAME}/${PKG_VERSION} --all 7 | rm -r /usr/src/${PKG_NAME}-${PKG_VERSION} 8 | -------------------------------------------------------------------------------- /debian/dkms.conf: -------------------------------------------------------------------------------- 1 | PACKAGE_VERSION="1.0" 2 | 3 | PACKAGE_NAME="nft-fullcone" 4 | BDIR="$dkms_tree/${PACKAGE_NAME}/${PACKAGE_VERSION}/build" 5 | MAKEARGS="-C $kernel_source_dir M=${BDIR}/src V=1" 6 | CLEAN="make ${MAKEARGS} clean" 7 | MAKE="make ${MAKEARGS} modules ; find ${BDIR} -name '*.ko' -exec mv -v {} ${BDIR} \;" 8 | 9 | BUILT_MODULE_LOCATION[0]="" 10 | BUILT_MODULE_NAME[0]="nft_fullcone" 11 | DEST_MODULE_LOCATION[0]="/updates" 12 | 13 | AUTOINSTALL="yes" 14 | -------------------------------------------------------------------------------- /src/Kconfig: -------------------------------------------------------------------------------- 1 | # 2 | # Nftables/netfilter fullcone expression support 3 | # 4 | config NFT_FULLCONE 5 | depends on NF_CONNTRACK 6 | depends on NF_NAT 7 | tristate "Netfilter nf_tables fullcone support" 8 | help 9 | This options adds the "fullcone" expression that you can use 10 | to perform NAT in the RFC3489-compatible full cone SNAT flavour. 11 | Currently only UDP traffic is supported for full-cone NAT. 12 | For other protos FULLCONENAT is equivalent to MASQUERADE. 13 | 14 | To compile this code as a module, choose M here: the module will be 15 | called nft_fullcone. 16 | 17 | If unsure, say N. 18 | 19 | -------------------------------------------------------------------------------- /src/Lindent: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # SPDX-License-Identifier: GPL-2.0 3 | 4 | # original 5 | #PARAM="-npro -kr -i8 -ts8 -sob -l80 -ss -ncs -cp1" 6 | # use wide screen in 21th century 7 | PARAM="-npro -kr -i8 -ts8 -sob -l120 -ss -ncs -cp1" 8 | 9 | RES=$(indent --version | cut -d' ' -f3) 10 | if [ "$RES" = "" ]; then 11 | exit 1 12 | fi 13 | V1=$(echo $RES | cut -d'.' -f1) 14 | V2=$(echo $RES | cut -d'.' -f2) 15 | V3=$(echo $RES | cut -d'.' -f3) 16 | 17 | if [ $V1 -gt 2 ]; then 18 | PARAM="$PARAM -il0" 19 | elif [ $V1 -eq 2 ]; then 20 | if [ $V2 -gt 2 ]; then 21 | PARAM="$PARAM -il0" 22 | elif [ $V2 -eq 2 ]; then 23 | if [ $V3 -ge 10 ]; then 24 | PARAM="$PARAM -il0" 25 | fi 26 | fi 27 | fi 28 | 29 | indent $PARAM "$@" 30 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile for Nftables/netfilter fullcone expression support. 3 | # 4 | 5 | # module name, should not have the same name as src file names 6 | TARGET = nft_fullcone 7 | 8 | obj-m += $(TARGET).o 9 | 10 | $(TARGET)-objs := \ 11 | nf_nat_fullcone.o \ 12 | nft_ext_fullcone.o 13 | 14 | # product 15 | ccflags-y += -Werror -Wall 16 | 17 | # develop 18 | #ccflags-y += -Wall -Wno-unused-function 19 | -------------------------------------------------------------------------------- /src/nf_nat_fullcone.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | 3 | /* 4 | * Nftables NAT extension: fullcone expression support library 5 | * 6 | * Copyright (c) 2018 Chion Tang 7 | * Original xt_FULLCONENAT and related iptables extension author 8 | * Copyright (c) 2019-2022 GitHub/llccd Twitter/@gNodeB 9 | * Added IPv6 support for xt_FULLCONENAT and ip6tables extension 10 | * Ported to recent kernel versions 11 | * Copyright (c) 2022 Syrone Wong 12 | * Massively rewrite the whole module, split the original code into library and nftables 'fullcone' expression module 13 | */ 14 | 15 | #define pr_fmt(fmt) "fullcone " KBUILD_MODNAME ": " fmt 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS 26 | #include 27 | #endif 28 | 29 | #include 30 | #include 31 | 32 | #if IS_ENABLED(CONFIG_NF_NAT_IPV6) || (IS_ENABLED(CONFIG_IPV6) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) 33 | #include 34 | #include 35 | #include 36 | #endif 37 | 38 | #include "nf_nat_fullcone.h" 39 | 40 | /* 41 | * FULLCONE_HKEY generates u32 hash value 42 | * Modified from net/netfilter/ipset/ip_set_hash_gen.h 43 | * dataptr: a pointer 44 | * datatypelen: sizeof(struct blah) or sizeof(u32) 45 | * initval: initial value 46 | * htable_bits: hashtable bits 47 | */ 48 | #define FULLCONE_HKEY(dataptr, datatypelen, initval, htable_bits) \ 49 | ({ \ 50 | const u32 *__k = (const u32 *)(dataptr); \ 51 | u32 __l = (datatypelen) / sizeof(u32); \ 52 | \ 53 | BUILD_BUG_ON((datatypelen) % sizeof(u32) != 0); \ 54 | \ 55 | jhash2(__k, __l, (initval)) & jhash_mask((htable_bits)); \ 56 | }) 57 | 58 | #define HASHTABLE_BUCKET_BITS 10 59 | 60 | /* static variables */ 61 | 62 | static DEFINE_HASHTABLE(mapping_table_by_ext_port, HASHTABLE_BUCKET_BITS); 63 | static DEFINE_HASHTABLE(mapping_table_by_int_src, HASHTABLE_BUCKET_BITS); 64 | 65 | static DEFINE_SPINLOCK(fullconenat_lock); 66 | 67 | #if IS_ENABLED(CONFIG_NF_NAT_IPV6) || (IS_ENABLED(CONFIG_IPV6) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) 68 | static DEFINE_HASHTABLE(mapping6_table_by_ext_port, HASHTABLE_BUCKET_BITS); 69 | static DEFINE_HASHTABLE(mapping6_table_by_int_src, HASHTABLE_BUCKET_BITS); 70 | 71 | static DEFINE_SPINLOCK(fullconenat6_lock); 72 | #endif 73 | 74 | static LIST_HEAD(dying_tuple_list); 75 | static DEFINE_SPINLOCK(dying_tuple_list_lock); 76 | 77 | /* static variables end */ 78 | 79 | /* forward declaration */ 80 | 81 | #if IS_ENABLED(CONFIG_IPV6) 82 | static int nat_ipv6_dev_get_saddr(struct net *net, const struct net_device *dev, 83 | const struct in6_addr *daddr, unsigned int srcprefs, struct in6_addr *saddr); 84 | #endif 85 | 86 | #if IS_ENABLED(CONFIG_NF_NAT_IPV6) || (IS_ENABLED(CONFIG_IPV6) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) 87 | /* non-atomic: can only be called serially within lock zones. */ 88 | static char *fullcone_nf_ct_stringify_tuple6(const struct nf_conntrack_tuple 89 | *t); 90 | #endif 91 | /* non-atomic: can only be called serially within lock zones. */ 92 | static char *nf_ct_stringify_tuple(const struct nf_conntrack_tuple *t); 93 | 94 | #if IS_ENABLED(CONFIG_NF_NAT_IPV6) || (IS_ENABLED(CONFIG_IPV6) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) 95 | static struct nat_mapping6 *allocate_mapping6(const union nf_inet_addr 96 | *int_addr, 97 | const uint16_t int_port, 98 | const uint16_t port, const union nf_inet_addr *addr); 99 | #endif 100 | static struct nat_mapping *allocate_mapping(const __be32 int_addr, 101 | const uint16_t int_port, const uint16_t port, const __be32 addr); 102 | 103 | #if IS_ENABLED(CONFIG_NF_NAT_IPV6) || (IS_ENABLED(CONFIG_IPV6) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) 104 | static void add_original_tuple_to_mapping6(struct nat_mapping6 *mapping, const struct nf_conntrack_tuple 105 | *original_tuple); 106 | #endif 107 | static void add_original_tuple_to_mapping(struct nat_mapping *mapping, const struct nf_conntrack_tuple 108 | *original_tuple); 109 | 110 | #if IS_ENABLED(CONFIG_NF_NAT_IPV6) || (IS_ENABLED(CONFIG_IPV6) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) 111 | static struct nat_mapping6 *get_mapping6_by_int_src(const union nf_inet_addr 112 | *src_ip, const uint16_t src_port, const union nf_inet_addr 113 | *ext_ip); 114 | #endif 115 | 116 | static struct nat_mapping *get_mapping_by_int_src(const __be32 src_ip, const uint16_t src_port, const __be32 ext_ip); 117 | 118 | #if IS_ENABLED(CONFIG_NF_NAT_IPV6) || (IS_ENABLED(CONFIG_IPV6) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) 119 | static struct nat_mapping6 *get_mapping6_by_int_src_inrange(const union 120 | nf_inet_addr 121 | *src_ip, const uint16_t src_port, const union 122 | nf_inet_addr 123 | *min_ip, const union 124 | nf_inet_addr 125 | *max_ip); 126 | #endif 127 | static struct nat_mapping *get_mapping_by_int_src_inrange(const __be32 src_ip, 128 | const uint16_t 129 | src_port, const __be32 min_ip, const __be32 max_ip); 130 | 131 | #if IS_ENABLED(CONFIG_NF_NAT_IPV6) || (IS_ENABLED(CONFIG_IPV6) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) 132 | static void kill_mapping6(struct nat_mapping6 *mapping); 133 | #endif 134 | static void kill_mapping(struct nat_mapping *mapping); 135 | 136 | #if IS_ENABLED(CONFIG_NF_NAT_IPV6) || (IS_ENABLED(CONFIG_IPV6) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) 137 | 138 | /* check if a mapping is valid. 139 | * possibly delete and free an invalid mapping. 140 | * the mapping should not be used anymore after check_mapping6() returns 0. */ 141 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) 142 | static int check_mapping6(struct nat_mapping6 *mapping, struct net *net, const struct nf_conntrack_zone *zone); 143 | #else 144 | static int check_mapping6(struct nat_mapping6 *mapping, struct net *net, const u16 zone); 145 | #endif 146 | 147 | #endif 148 | 149 | /* check if a mapping is valid. 150 | * possibly delete and free an invalid mapping. 151 | * the mapping should not be used anymore after check_mapping() returns 0. */ 152 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) 153 | static int check_mapping(struct nat_mapping *mapping, struct net *net, const struct nf_conntrack_zone *zone); 154 | #else 155 | static int check_mapping(struct nat_mapping *mapping, struct net *net, const u16 zone); 156 | #endif 157 | 158 | #if IS_ENABLED(CONFIG_NF_NAT_IPV6) || (IS_ENABLED(CONFIG_IPV6) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) 159 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) 160 | static struct nat_mapping6 *get_mapping6_by_ext_port(const uint16_t port, const union nf_inet_addr 161 | *ext_ip, struct net *net, const struct 162 | nf_conntrack_zone *zone); 163 | #else 164 | static struct nat_mapping6 *get_mapping6_by_ext_port(const uint16_t port, const union nf_inet_addr 165 | *ext_ip, struct net *net, const u16 zone); 166 | #endif 167 | #endif 168 | 169 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) 170 | static struct nat_mapping *get_mapping_by_ext_port(const uint16_t port, const __be32 ext_ip, struct net *net, const struct 171 | nf_conntrack_zone *zone); 172 | #else 173 | static struct nat_mapping *get_mapping_by_ext_port(const uint16_t port, 174 | const __be32 ext_ip, struct net *net, const u16 zone); 175 | #endif 176 | 177 | #if IS_ENABLED(CONFIG_NF_NAT_IPV6) || (IS_ENABLED(CONFIG_IPV6) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) 178 | 179 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) 180 | static uint16_t find_appropriate_port6(struct net *net, 181 | const struct nf_conntrack_zone *zone, 182 | const uint16_t original_port, const union nf_inet_addr *ext_ip, 183 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) 184 | struct nf_nat_range2 *range); 185 | 186 | #else 187 | struct nf_nat_range *range); 188 | 189 | #endif 190 | 191 | #else 192 | static uint16_t find_appropriate_port6(struct net *net, const u16 zone, 193 | const uint16_t original_port, const union nf_inet_addr *ext_ip, 194 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) 195 | struct nf_nat_range2 *range); 196 | 197 | #else 198 | struct nf_nat_range *range); 199 | 200 | #endif 201 | #endif 202 | 203 | #endif 204 | 205 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) 206 | static uint16_t find_appropriate_port(struct net *net, 207 | const struct nf_conntrack_zone *zone, 208 | const uint16_t original_port, const __be32 ext_ip, 209 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) 210 | struct nf_nat_range2 *range); 211 | #else 212 | struct nf_nat_range *range); 213 | #endif 214 | 215 | #else 216 | static uint16_t find_appropriate_port(struct net *net, const u16 zone, 217 | const uint16_t original_port, const __be32 ext_ip, 218 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) 219 | struct nf_nat_range2 *range); 220 | #else 221 | struct nf_nat_range *range); 222 | #endif 223 | #endif 224 | 225 | #if IS_ENABLED(CONFIG_NF_NAT_IPV6) || (IS_ENABLED(CONFIG_IPV6) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) 226 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) 227 | static void find_leastused_ip6(const struct nf_conntrack_zone *zone, 228 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) 229 | struct nf_nat_range2 *range, 230 | #else 231 | struct nf_nat_range *range, 232 | #endif 233 | const union nf_inet_addr *src, 234 | const union nf_inet_addr *dst, union nf_inet_addr *var_ipp); 235 | #else 236 | static void find_leastused_ip6(const u16 zone, 237 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) 238 | struct nf_nat_range2 *range, 239 | #else 240 | struct nf_nat_range *range, 241 | #endif 242 | const union nf_inet_addr *src, 243 | const union nf_inet_addr *dst, union nf_inet_addr *var_ipp); 244 | #endif 245 | #endif 246 | 247 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) 248 | static __be32 find_leastused_ip(const struct nf_conntrack_zone *zone, 249 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) 250 | struct nf_nat_range2 *range, 251 | #else 252 | struct nf_nat_range *range, 253 | #endif 254 | const __be32 src, const __be32 dst); 255 | #else 256 | static __be32 find_leastused_ip(const u16 zone, 257 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) 258 | struct nf_nat_range2 *range, 259 | #else 260 | struct nf_nat_range *range, 261 | #endif 262 | const __be32 src, const __be32 dst); 263 | #endif 264 | 265 | /* forward declaration end */ 266 | 267 | /* non-atomic part */ 268 | 269 | static char tuple_tmp_string[512]; 270 | 271 | #if IS_ENABLED(CONFIG_NF_NAT_IPV6) || (IS_ENABLED(CONFIG_IPV6) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) 272 | /* non-atomic: can only be called serially within lock zones. */ 273 | static char *fullcone_nf_ct_stringify_tuple6(const struct nf_conntrack_tuple *t) 274 | { 275 | snprintf(tuple_tmp_string, sizeof(tuple_tmp_string), 276 | "[%pI6c]:%hu -> [%pI6c]:%hu", &t->src.u3.ip6, 277 | be16_to_cpu(t->src.u.all), &t->dst.u3.ip6, be16_to_cpu(t->dst.u.all)); 278 | return tuple_tmp_string; 279 | } 280 | #endif 281 | /* non-atomic: can only be called serially within lock zones. */ 282 | static char *nf_ct_stringify_tuple(const struct nf_conntrack_tuple *t) 283 | { 284 | snprintf(tuple_tmp_string, sizeof(tuple_tmp_string), 285 | "%pI4:%hu -> %pI4:%hu", &t->src.u3.ip, 286 | be16_to_cpu(t->src.u.all), &t->dst.u3.ip, be16_to_cpu(t->dst.u.all)); 287 | return tuple_tmp_string; 288 | } 289 | 290 | /* non-atomic part end */ 291 | 292 | void nf_nat_fullcone_dying_tuple_list_add(struct list_head *new_dying) 293 | { 294 | spin_lock_bh(&dying_tuple_list_lock); 295 | list_add(new_dying, &dying_tuple_list); 296 | spin_unlock_bh(&dying_tuple_list_lock); 297 | } 298 | 299 | EXPORT_SYMBOL_GPL(nf_nat_fullcone_dying_tuple_list_add); 300 | 301 | void nf_nat_fullcone_handle_dying_tuples(void) 302 | { 303 | struct list_head *iter, *tmp, *iter_2, *tmp_2; 304 | struct tuple_list *item; 305 | struct nf_conntrack_tuple *ct_tuple; 306 | struct nat_mapping *mapping; 307 | __be32 ip, ext_ip; 308 | uint16_t port; 309 | struct nat_mapping_original_tuple *original_tuple_item; 310 | #if IS_ENABLED(CONFIG_NF_NAT_IPV6) || (IS_ENABLED(CONFIG_IPV6) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) 311 | struct nat_mapping6 *mapping6; 312 | union nf_inet_addr *ip6, *ext_ip6; 313 | spin_lock_bh(&fullconenat6_lock); 314 | #endif 315 | 316 | spin_lock_bh(&fullconenat_lock); 317 | spin_lock_bh(&dying_tuple_list_lock); 318 | 319 | list_for_each_safe(iter, tmp, &dying_tuple_list) { 320 | item = list_entry(iter, struct tuple_list, list); 321 | 322 | /* we dont know the conntrack direction for now so we try in both ways. */ 323 | ct_tuple = &(item->tuple_original); 324 | #if IS_ENABLED(CONFIG_NF_NAT_IPV6) || (IS_ENABLED(CONFIG_IPV6) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) 325 | if (ct_tuple->src.l3num == PF_INET6) { 326 | ip6 = &(ct_tuple->src).u3; 327 | port = be16_to_cpu((ct_tuple->src).u.udp.port); 328 | ext_ip6 = &item->tuple_reply.dst.u3; 329 | mapping6 = get_mapping6_by_int_src(ip6, port, ext_ip6); 330 | if (mapping6 == NULL) { 331 | ext_ip6 = &(ct_tuple->dst).u3; 332 | ct_tuple = &(item->tuple_reply); 333 | ip6 = &(ct_tuple->src).u3; 334 | port = be16_to_cpu((ct_tuple->src).u.udp.port); 335 | mapping6 = get_mapping6_by_int_src(ip6, port, ext_ip6); 336 | if (mapping6 != NULL) { 337 | pr_debug 338 | ("nf_nat_fullcone_handle_dying_tuples(): INBOUND dying conntrack at ext port %d\n", 339 | mapping6->port); 340 | } 341 | } else { 342 | pr_debug 343 | ("nf_nat_fullcone_handle_dying_tuples(): OUTBOUND dying conntrack at ext port %d\n", 344 | mapping6->port); 345 | } 346 | 347 | if (mapping6 == NULL) { 348 | goto next; 349 | } 350 | 351 | /* look for the corresponding out-dated tuple and free it */ 352 | list_for_each_safe(iter_2, tmp_2, &mapping6->original_tuple_list) { 353 | original_tuple_item = list_entry(iter_2, struct 354 | nat_mapping_original_tuple, node); 355 | 356 | if (nf_ct_tuple_equal(&original_tuple_item->tuple, &(item->tuple_original))) { 357 | pr_debug 358 | ("nf_nat_fullcone_handle_dying_tuples(): tuple %s expired. free this tuple.\n", 359 | fullcone_nf_ct_stringify_tuple6(&original_tuple_item->tuple)); 360 | list_del(&original_tuple_item->node); 361 | kfree(original_tuple_item); 362 | (mapping6->refer_count)--; 363 | } 364 | } 365 | 366 | /* then kill the mapping if needed */ 367 | pr_debug 368 | ("nf_nat_fullcone_handle_dying_tuples(): refer_count for mapping at ext_port %d is now %d\n", 369 | mapping6->port, mapping6->refer_count); 370 | if (mapping6->refer_count <= 0) { 371 | pr_debug 372 | ("nf_nat_fullcone_handle_dying_tuples(): kill expired mapping at ext port %d\n", 373 | mapping6->port); 374 | kill_mapping6(mapping6); 375 | } 376 | goto next; 377 | } 378 | if (unlikely(ct_tuple->src.l3num != PF_INET)) 379 | #else 380 | if (ct_tuple->src.l3num != PF_INET) 381 | #endif 382 | goto next; 383 | 384 | ip = (ct_tuple->src).u3.ip; 385 | port = be16_to_cpu((ct_tuple->src).u.udp.port); 386 | ext_ip = item->tuple_reply.dst.u3.ip; 387 | mapping = get_mapping_by_int_src(ip, port, ext_ip); 388 | if (mapping == NULL) { 389 | ext_ip = (ct_tuple->dst).u3.ip; 390 | ct_tuple = &(item->tuple_reply); 391 | ip = (ct_tuple->src).u3.ip; 392 | port = be16_to_cpu((ct_tuple->src).u.udp.port); 393 | mapping = get_mapping_by_int_src(ip, port, ext_ip); 394 | if (mapping != NULL) { 395 | pr_debug 396 | ("nf_nat_fullcone_handle_dying_tuples(): INBOUND dying conntrack at ext port %d\n", 397 | mapping->port); 398 | } 399 | } else { 400 | pr_debug 401 | ("nf_nat_fullcone_handle_dying_tuples(): OUTBOUND dying conntrack at ext port %d\n", 402 | mapping->port); 403 | } 404 | 405 | if (mapping == NULL) { 406 | goto next; 407 | } 408 | 409 | /* look for the corresponding out-dated tuple and free it */ 410 | list_for_each_safe(iter_2, tmp_2, &mapping->original_tuple_list) { 411 | original_tuple_item = list_entry(iter_2, struct nat_mapping_original_tuple, node); 412 | 413 | if (nf_ct_tuple_equal(&original_tuple_item->tuple, &(item->tuple_original))) { 414 | pr_debug 415 | ("nf_nat_fullcone_handle_dying_tuples(): tuple %s expired. free this tuple.\n", 416 | nf_ct_stringify_tuple(&original_tuple_item->tuple)); 417 | list_del(&original_tuple_item->node); 418 | kfree(original_tuple_item); 419 | (mapping->refer_count)--; 420 | } 421 | } 422 | 423 | /* then kill the mapping if needed */ 424 | pr_debug 425 | ("nf_nat_fullcone_handle_dying_tuples(): refer_count for mapping at ext_port %d is now %d\n", 426 | mapping->port, mapping->refer_count); 427 | if (mapping->refer_count <= 0) { 428 | pr_debug 429 | ("nf_nat_fullcone_handle_dying_tuples(): kill expired mapping at ext port %d\n", 430 | mapping->port); 431 | kill_mapping(mapping); 432 | } 433 | 434 | next: 435 | list_del(&item->list); 436 | kfree(item); 437 | } 438 | 439 | spin_unlock_bh(&dying_tuple_list_lock); 440 | spin_unlock_bh(&fullconenat_lock); 441 | #if IS_ENABLED(CONFIG_NF_NAT_IPV6) || (IS_ENABLED(CONFIG_IPV6) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) 442 | spin_unlock_bh(&fullconenat6_lock); 443 | #endif 444 | } 445 | 446 | EXPORT_SYMBOL_GPL(nf_nat_fullcone_handle_dying_tuples); 447 | 448 | #if IS_ENABLED(CONFIG_NF_NAT_IPV6) || (IS_ENABLED(CONFIG_IPV6) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) 449 | static struct nat_mapping6 *allocate_mapping6(const union nf_inet_addr 450 | *int_addr, 451 | const uint16_t int_port, 452 | const uint16_t port, const union nf_inet_addr *addr) 453 | { 454 | struct nat_mapping6 *p_new; 455 | u32 hash_src; 456 | 457 | p_new = kmalloc(sizeof(struct nat_mapping6), GFP_ATOMIC); 458 | if (p_new == NULL) { 459 | pr_err("kmalloc() for allocate_mapping6 failed.\n"); 460 | return NULL; 461 | } 462 | p_new->addr = *addr; 463 | p_new->port = port; 464 | p_new->int_addr = *int_addr; 465 | p_new->int_port = int_port; 466 | p_new->refer_count = 0; 467 | (p_new->original_tuple_list).next = &(p_new->original_tuple_list); 468 | (p_new->original_tuple_list).prev = &(p_new->original_tuple_list); 469 | 470 | hash_src = FULLCONE_HKEY(int_addr, sizeof(union nf_inet_addr), (u32) int_port, HASHTABLE_BUCKET_BITS); 471 | //hash_src = jhash2((u32 *) int_addr->all, 4, (u32) int_port); 472 | 473 | hash_add(mapping6_table_by_ext_port, &p_new->node_by_ext_port, port); 474 | hash_add(mapping6_table_by_int_src, &p_new->node_by_int_src, hash_src); 475 | 476 | pr_debug("new mapping allocated for [%pI6c]:%d ==> [%pI6c]:%d\n", 477 | &p_new->int_addr, p_new->int_port, &p_new->addr, p_new->port); 478 | 479 | return p_new; 480 | } 481 | #endif 482 | static struct nat_mapping *allocate_mapping(const __be32 int_addr, 483 | const uint16_t int_port, const uint16_t port, const __be32 addr) 484 | { 485 | struct nat_mapping *p_new; 486 | u32 hash_src; 487 | 488 | p_new = kmalloc(sizeof(struct nat_mapping), GFP_ATOMIC); 489 | if (p_new == NULL) { 490 | pr_err("kmalloc() for allocate_mapping failed.\n"); 491 | return NULL; 492 | } 493 | p_new->addr = addr; 494 | p_new->port = port; 495 | p_new->int_addr = int_addr; 496 | p_new->int_port = int_port; 497 | p_new->refer_count = 0; 498 | (p_new->original_tuple_list).next = &(p_new->original_tuple_list); 499 | (p_new->original_tuple_list).prev = &(p_new->original_tuple_list); 500 | 501 | hash_src = FULLCONE_HKEY(&int_addr, sizeof(__be32), (u32) int_port, HASHTABLE_BUCKET_BITS); 502 | //hash_src = HASH_2(int_addr, (u32) int_port); 503 | 504 | hash_add(mapping_table_by_ext_port, &p_new->node_by_ext_port, port); 505 | hash_add(mapping_table_by_int_src, &p_new->node_by_int_src, hash_src); 506 | 507 | pr_debug("new mapping allocated for %pI4:%d ==> %pI4:%d\n", 508 | &p_new->int_addr, p_new->int_port, &p_new->addr, p_new->port); 509 | 510 | return p_new; 511 | } 512 | 513 | #if IS_ENABLED(CONFIG_NF_NAT_IPV6) || (IS_ENABLED(CONFIG_IPV6) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) 514 | static void add_original_tuple_to_mapping6(struct nat_mapping6 *mapping, const struct nf_conntrack_tuple 515 | *original_tuple) 516 | { 517 | struct nat_mapping_original_tuple *item = kmalloc(sizeof(struct nat_mapping_original_tuple), GFP_ATOMIC); 518 | if (item == NULL) { 519 | pr_err("kmalloc() for add_original_tuple_to_mapping6 failed.\n"); 520 | return; 521 | } 522 | memcpy(&item->tuple, original_tuple, sizeof(struct nf_conntrack_tuple)); 523 | list_add(&item->node, &mapping->original_tuple_list); 524 | (mapping->refer_count)++; 525 | } 526 | #endif 527 | static void add_original_tuple_to_mapping(struct nat_mapping *mapping, const struct nf_conntrack_tuple 528 | *original_tuple) 529 | { 530 | struct nat_mapping_original_tuple *item = kmalloc(sizeof(struct nat_mapping_original_tuple), GFP_ATOMIC); 531 | if (item == NULL) { 532 | pr_err("kmalloc() for add_original_tuple_to_mapping failed.\n"); 533 | return; 534 | } 535 | memcpy(&item->tuple, original_tuple, sizeof(struct nf_conntrack_tuple)); 536 | list_add(&item->node, &mapping->original_tuple_list); 537 | (mapping->refer_count)++; 538 | } 539 | 540 | #if IS_ENABLED(CONFIG_NF_NAT_IPV6) || (IS_ENABLED(CONFIG_IPV6) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) 541 | static struct nat_mapping6 *get_mapping6_by_int_src(const union nf_inet_addr 542 | *src_ip, const uint16_t src_port, const union nf_inet_addr 543 | *ext_ip) 544 | { 545 | struct nat_mapping6 *p_current; 546 | u32 hash_src = FULLCONE_HKEY(src_ip, sizeof(union nf_inet_addr), (u32) src_port, HASHTABLE_BUCKET_BITS); 547 | //u32 hash_src = jhash2((u32 *) src_ip->all, 4, (u32) src_port); 548 | 549 | hash_for_each_possible(mapping6_table_by_int_src, p_current, node_by_int_src, hash_src) { 550 | if (nf_inet_addr_cmp(&p_current->int_addr, src_ip) 551 | && p_current->int_port == src_port && nf_inet_addr_cmp(&p_current->addr, ext_ip)) { 552 | return p_current; 553 | } 554 | } 555 | 556 | return NULL; 557 | } 558 | #endif 559 | 560 | static struct nat_mapping *get_mapping_by_int_src(const __be32 src_ip, const uint16_t src_port, const __be32 ext_ip) 561 | { 562 | struct nat_mapping *p_current; 563 | u32 hash_src = FULLCONE_HKEY(&src_ip, sizeof(__be32), (u32) src_port, HASHTABLE_BUCKET_BITS); 564 | //u32 hash_src = HASH_2(src_ip, (u32) src_port); 565 | 566 | hash_for_each_possible(mapping_table_by_int_src, p_current, node_by_int_src, hash_src) { 567 | if (p_current->int_addr == src_ip && p_current->int_port == src_port && p_current->addr == ext_ip) { 568 | return p_current; 569 | } 570 | } 571 | 572 | return NULL; 573 | } 574 | 575 | #if IS_ENABLED(CONFIG_NF_NAT_IPV6) || (IS_ENABLED(CONFIG_IPV6) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) 576 | static struct nat_mapping6 *get_mapping6_by_int_src_inrange(const union 577 | nf_inet_addr 578 | *src_ip, const uint16_t src_port, const union 579 | nf_inet_addr 580 | *min_ip, const union 581 | nf_inet_addr 582 | *max_ip) 583 | { 584 | struct nat_mapping6 *p_current; 585 | 586 | u32 hash_src = FULLCONE_HKEY(src_ip, sizeof(union nf_inet_addr), (u32) src_port, HASHTABLE_BUCKET_BITS); 587 | //u32 hash_src = jhash2((u32 *) src_ip->all, 4, (u32) src_port); 588 | 589 | hash_for_each_possible(mapping6_table_by_int_src, p_current, node_by_int_src, hash_src) { 590 | if (nf_inet_addr_cmp(&p_current->int_addr, src_ip) 591 | && p_current->int_port == src_port 592 | && memcmp(&p_current->addr, min_ip, 593 | sizeof(union nf_inet_addr)) >= 0 594 | && memcmp(&p_current->addr, max_ip, sizeof(union nf_inet_addr)) <= 0) { 595 | return p_current; 596 | } 597 | } 598 | 599 | return NULL; 600 | } 601 | #endif 602 | static struct nat_mapping *get_mapping_by_int_src_inrange(const __be32 src_ip, 603 | const uint16_t 604 | src_port, const __be32 min_ip, const __be32 max_ip) 605 | { 606 | struct nat_mapping *p_current; 607 | u32 hash_src = FULLCONE_HKEY(&src_ip, sizeof(__be32), (u32) src_port, HASHTABLE_BUCKET_BITS); 608 | //u32 hash_src = HASH_2(src_ip, (u32) src_port); 609 | 610 | hash_for_each_possible(mapping_table_by_int_src, p_current, node_by_int_src, hash_src) { 611 | if (p_current->int_addr == src_ip 612 | && p_current->int_port == src_port 613 | && memcmp(&p_current->addr, &min_ip, sizeof(__be32)) >= 0 614 | && memcmp(&p_current->addr, &max_ip, sizeof(__be32)) <= 0) { 615 | return p_current; 616 | } 617 | } 618 | 619 | return NULL; 620 | } 621 | 622 | #if IS_ENABLED(CONFIG_NF_NAT_IPV6) || (IS_ENABLED(CONFIG_IPV6) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) 623 | static void kill_mapping6(struct nat_mapping6 *mapping) 624 | { 625 | struct list_head *iter, *tmp; 626 | struct nat_mapping_original_tuple *original_tuple_item; 627 | 628 | if (mapping == NULL) { 629 | return; 630 | } 631 | 632 | list_for_each_safe(iter, tmp, &mapping->original_tuple_list) { 633 | original_tuple_item = list_entry(iter, struct nat_mapping_original_tuple, node); 634 | list_del(&original_tuple_item->node); 635 | kfree(original_tuple_item); 636 | } 637 | 638 | hash_del(&mapping->node_by_ext_port); 639 | hash_del(&mapping->node_by_int_src); 640 | kfree(mapping); 641 | } 642 | #endif 643 | static void kill_mapping(struct nat_mapping *mapping) 644 | { 645 | struct list_head *iter, *tmp; 646 | struct nat_mapping_original_tuple *original_tuple_item; 647 | 648 | if (mapping == NULL) { 649 | return; 650 | } 651 | 652 | list_for_each_safe(iter, tmp, &mapping->original_tuple_list) { 653 | original_tuple_item = list_entry(iter, struct nat_mapping_original_tuple, node); 654 | list_del(&original_tuple_item->node); 655 | kfree(original_tuple_item); 656 | } 657 | 658 | hash_del(&mapping->node_by_ext_port); 659 | hash_del(&mapping->node_by_int_src); 660 | kfree(mapping); 661 | } 662 | 663 | #if IS_ENABLED(CONFIG_NF_NAT_IPV6) || (IS_ENABLED(CONFIG_IPV6) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) 664 | 665 | /* check if a mapping is valid. 666 | * possibly delete and free an invalid mapping. 667 | * the mapping should not be used anymore after check_mapping6() returns 0. */ 668 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) 669 | static int check_mapping6(struct nat_mapping6 *mapping, struct net *net, const struct nf_conntrack_zone *zone) 670 | { 671 | #else 672 | static int check_mapping6(struct nat_mapping6 *mapping, struct net *net, const u16 zone) 673 | { 674 | #endif 675 | struct list_head *iter, *tmp; 676 | struct nat_mapping_original_tuple *original_tuple_item; 677 | struct nf_conntrack_tuple_hash *tuple_hash; 678 | struct nf_conn *ct; 679 | 680 | /* for dying/unconfirmed conntrack tuples, an IPCT_DESTROY event may NOT be fired. 681 | * so we manually kill one of those tuples once we acquire one. */ 682 | 683 | list_for_each_safe(iter, tmp, &mapping->original_tuple_list) { 684 | original_tuple_item = list_entry(iter, struct nat_mapping_original_tuple, node); 685 | 686 | tuple_hash = nf_conntrack_find_get(net, zone, &original_tuple_item->tuple); 687 | 688 | if (tuple_hash == NULL) { 689 | pr_debug 690 | ("check_mapping6(): tuple %s dying/unconfirmed. free this tuple.\n", 691 | fullcone_nf_ct_stringify_tuple6(&original_tuple_item->tuple)); 692 | 693 | list_del(&original_tuple_item->node); 694 | kfree(original_tuple_item); 695 | (mapping->refer_count)--; 696 | } else { 697 | ct = nf_ct_tuplehash_to_ctrack(tuple_hash); 698 | if (likely(ct != NULL)) 699 | nf_ct_put(ct); 700 | } 701 | 702 | } 703 | 704 | /* kill the mapping if need */ 705 | pr_debug 706 | ("check_mapping6() refer_count for mapping at ext_port %d is now %d\n", 707 | mapping->port, mapping->refer_count); 708 | if (mapping->refer_count <= 0) { 709 | pr_debug("check_mapping6(): kill dying/unconfirmed mapping at ext port %d\n", mapping->port); 710 | kill_mapping6(mapping); 711 | return 0; 712 | } else { 713 | return 1; 714 | } 715 | } 716 | 717 | #endif 718 | 719 | /* check if a mapping is valid. 720 | * possibly delete and free an invalid mapping. 721 | * the mapping should not be used anymore after check_mapping() returns 0. */ 722 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) 723 | static int check_mapping(struct nat_mapping *mapping, struct net *net, const struct nf_conntrack_zone *zone) 724 | { 725 | #else 726 | static int check_mapping(struct nat_mapping *mapping, struct net *net, const u16 zone) 727 | { 728 | #endif 729 | struct list_head *iter, *tmp; 730 | struct nat_mapping_original_tuple *original_tuple_item; 731 | struct nf_conntrack_tuple_hash *tuple_hash; 732 | struct nf_conn *ct; 733 | 734 | /* for dying/unconfirmed conntrack tuples, an IPCT_DESTROY event may NOT be fired. 735 | * so we manually kill one of those tuples once we acquire one. */ 736 | 737 | list_for_each_safe(iter, tmp, &mapping->original_tuple_list) { 738 | original_tuple_item = list_entry(iter, struct nat_mapping_original_tuple, node); 739 | 740 | tuple_hash = nf_conntrack_find_get(net, zone, &original_tuple_item->tuple); 741 | 742 | if (tuple_hash == NULL) { 743 | pr_debug 744 | ("check_mapping(): tuple %s dying/unconfirmed. free this tuple.\n", 745 | nf_ct_stringify_tuple(&original_tuple_item->tuple)); 746 | 747 | list_del(&original_tuple_item->node); 748 | kfree(original_tuple_item); 749 | (mapping->refer_count)--; 750 | } else { 751 | ct = nf_ct_tuplehash_to_ctrack(tuple_hash); 752 | if (likely(ct != NULL)) 753 | nf_ct_put(ct); 754 | } 755 | 756 | } 757 | 758 | /* kill the mapping if need */ 759 | pr_debug 760 | ("check_mapping() refer_count for mapping at ext_port %d is now %d\n", mapping->port, mapping->refer_count); 761 | if (mapping->refer_count <= 0) { 762 | pr_debug("check_mapping(): kill dying/unconfirmed mapping at ext port %d\n", mapping->port); 763 | kill_mapping(mapping); 764 | return 0; 765 | } else { 766 | return 1; 767 | } 768 | } 769 | 770 | #if IS_ENABLED(CONFIG_NF_NAT_IPV6) || (IS_ENABLED(CONFIG_IPV6) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) 771 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) 772 | static struct nat_mapping6 *get_mapping6_by_ext_port(const uint16_t port, const union nf_inet_addr 773 | *ext_ip, struct net *net, const struct 774 | nf_conntrack_zone *zone) 775 | { 776 | #else 777 | static struct nat_mapping6 *get_mapping6_by_ext_port(const uint16_t port, const union nf_inet_addr 778 | *ext_ip, struct net *net, const u16 zone) 779 | { 780 | #endif 781 | struct nat_mapping6 *p_current; 782 | struct hlist_node *tmp; 783 | 784 | hash_for_each_possible_safe(mapping6_table_by_ext_port, p_current, tmp, node_by_ext_port, port) { 785 | if (p_current->port == port && check_mapping6(p_current, net, zone) 786 | && nf_inet_addr_cmp(&p_current->addr, ext_ip)) { 787 | return p_current; 788 | } 789 | } 790 | 791 | return NULL; 792 | } 793 | #endif 794 | 795 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) 796 | static struct nat_mapping *get_mapping_by_ext_port(const uint16_t port, const __be32 ext_ip, struct net *net, const struct 797 | nf_conntrack_zone *zone) 798 | { 799 | #else 800 | static struct nat_mapping *get_mapping_by_ext_port(const uint16_t port, 801 | const __be32 ext_ip, struct net *net, const u16 zone) 802 | { 803 | #endif 804 | struct nat_mapping *p_current; 805 | struct hlist_node *tmp; 806 | 807 | hash_for_each_possible_safe(mapping_table_by_ext_port, p_current, tmp, node_by_ext_port, port) { 808 | if (p_current->port == port && check_mapping(p_current, net, zone) 809 | && p_current->addr == ext_ip) { 810 | return p_current; 811 | } 812 | } 813 | 814 | return NULL; 815 | } 816 | 817 | #if IS_ENABLED(CONFIG_NF_NAT_IPV6) || (IS_ENABLED(CONFIG_IPV6) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) 818 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) 819 | static uint16_t find_appropriate_port6(struct net *net, 820 | const struct nf_conntrack_zone *zone, 821 | const uint16_t original_port, const union nf_inet_addr *ext_ip, 822 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) 823 | struct nf_nat_range2 *range) 824 | #else 825 | struct nf_nat_range *range) 826 | #endif 827 | #else 828 | static uint16_t find_appropriate_port6(struct net *net, const u16 zone, 829 | const uint16_t original_port, const union nf_inet_addr *ext_ip, 830 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) 831 | struct nf_nat_range2 *range) 832 | #else 833 | struct nf_nat_range *range) 834 | #endif 835 | #endif 836 | { 837 | uint16_t min, start, selected, range_size, i; 838 | struct nat_mapping6 *mapping = NULL; 839 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) 840 | // nf_nat_range2 specific 841 | memset(&range->base_proto, 0, sizeof(range->base_proto)); 842 | #endif 843 | if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) { 844 | min = be16_to_cpu((range->min_proto).udp.port); 845 | range_size = be16_to_cpu((range->max_proto).udp.port) - min + 1; 846 | } else { 847 | /* minimum port is 1024. same behavior as default linux NAT. */ 848 | min = 1024; 849 | range_size = 65535 - min + 1; 850 | } 851 | 852 | if ((range->flags & NF_NAT_RANGE_PROTO_RANDOM) 853 | || (range->flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY)) { 854 | /* for now we do the same thing for both --random and --random-fully */ 855 | 856 | /* select a random starting point */ 857 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) 858 | start = (uint16_t) (get_random_u32() % (u32) range_size); 859 | #else 860 | start = (uint16_t) (prandom_u32() % (u32) range_size); 861 | #endif 862 | } else { 863 | 864 | if ((original_port >= min && original_port <= min + range_size - 1) 865 | || !(range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)) { 866 | /* 1. try to preserve the port if it's available */ 867 | mapping = get_mapping6_by_ext_port(original_port, ext_ip, net, zone); 868 | if (mapping == NULL) { 869 | return original_port; 870 | } 871 | } 872 | 873 | /* otherwise, we start from zero */ 874 | start = 0; 875 | } 876 | 877 | for (i = 0; i < range_size; i++) { 878 | /* 2. try to find an available port */ 879 | selected = min + ((start + i) % range_size); 880 | mapping = get_mapping6_by_ext_port(selected, ext_ip, net, zone); 881 | if (mapping == NULL) { 882 | return selected; 883 | } 884 | } 885 | 886 | /* 3. at least we tried. override a previous mapping. */ 887 | selected = min + start; 888 | mapping = get_mapping6_by_ext_port(selected, ext_ip, net, zone); 889 | kill_mapping6(mapping); 890 | 891 | return selected; 892 | } 893 | #endif 894 | 895 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) 896 | static uint16_t find_appropriate_port(struct net *net, 897 | const struct nf_conntrack_zone *zone, 898 | const uint16_t original_port, const __be32 ext_ip, 899 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) 900 | struct nf_nat_range2 *range) 901 | #else 902 | struct nf_nat_range *range) 903 | #endif 904 | #else 905 | static uint16_t find_appropriate_port(struct net *net, const u16 zone, 906 | const uint16_t original_port, const __be32 ext_ip, 907 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) 908 | struct nf_nat_range2 *range) 909 | #else 910 | struct nf_nat_range *range) 911 | #endif 912 | #endif 913 | { 914 | uint16_t min, start, selected, range_size, i; 915 | struct nat_mapping *mapping = NULL; 916 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) 917 | // nf_nat_range2 specific 918 | memset(&range->base_proto, 0, sizeof(range->base_proto)); 919 | #endif 920 | if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) { 921 | min = be16_to_cpu((range->min_proto).udp.port); 922 | range_size = be16_to_cpu((range->max_proto).udp.port) - min + 1; 923 | } else { 924 | /* minimum port is 1024. same behavior as default linux NAT. */ 925 | min = 1024; 926 | range_size = 65535 - min + 1; 927 | } 928 | 929 | if ((range->flags & NF_NAT_RANGE_PROTO_RANDOM) 930 | || (range->flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY)) { 931 | /* for now we do the same thing for both --random and --random-fully */ 932 | 933 | /* select a random starting point */ 934 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) 935 | start = (uint16_t) (get_random_u32() % (u32) range_size); 936 | #else 937 | start = (uint16_t) (prandom_u32() % (u32) range_size); 938 | #endif 939 | } else { 940 | 941 | if ((original_port >= min && original_port <= min + range_size - 1) 942 | || !(range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)) { 943 | /* 1. try to preserve the port if it's available */ 944 | mapping = get_mapping_by_ext_port(original_port, ext_ip, net, zone); 945 | if (mapping == NULL) { 946 | return original_port; 947 | } 948 | } 949 | 950 | /* otherwise, we start from zero */ 951 | start = 0; 952 | } 953 | 954 | for (i = 0; i < range_size; i++) { 955 | /* 2. try to find an available port */ 956 | selected = min + ((start + i) % range_size); 957 | mapping = get_mapping_by_ext_port(selected, ext_ip, net, zone); 958 | if (mapping == NULL) { 959 | return selected; 960 | } 961 | } 962 | 963 | /* 3. at least we tried. override a previous mapping. */ 964 | selected = min + start; 965 | mapping = get_mapping_by_ext_port(selected, ext_ip, net, zone); 966 | kill_mapping(mapping); 967 | 968 | return selected; 969 | } 970 | 971 | #if IS_ENABLED(CONFIG_NF_NAT_IPV6) || (IS_ENABLED(CONFIG_IPV6) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) 972 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) 973 | static void find_leastused_ip6(const struct nf_conntrack_zone *zone, 974 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) 975 | struct nf_nat_range2 *range, 976 | #else 977 | struct nf_nat_range *range, 978 | #endif 979 | const union nf_inet_addr *src, 980 | const union nf_inet_addr *dst, union nf_inet_addr *var_ipp) 981 | #else 982 | static void find_leastused_ip6(const u16 zone, 983 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) 984 | struct nf_nat_range2 *range, 985 | #else 986 | struct nf_nat_range *range, 987 | #endif 988 | const union nf_inet_addr *src, 989 | const union nf_inet_addr *dst, union nf_inet_addr *var_ipp) 990 | #endif 991 | { 992 | unsigned int i; 993 | /* Host order */ 994 | u32 minip, maxip, j, dist; 995 | bool full_range; 996 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) 997 | // nf_nat_range2 specific 998 | memset(&(range->base_proto), 0, sizeof(range->base_proto)); 999 | #endif 1000 | 1001 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) 1002 | j = FULLCONE_HKEY(src, sizeof(union nf_inet_addr), 1003 | range->flags & NF_NAT_RANGE_PERSISTENT ? 0 : dst->all[3] ^ zone->id, HASHTABLE_BUCKET_BITS); 1004 | //j = jhash2((u32 *) src, 4, range->flags & NF_NAT_RANGE_PERSISTENT ? 0 : dst->all[3] ^ zone->id); 1005 | #else 1006 | j = FULLCONE_HKEY(src, sizeof(union nf_inet_addr), 1007 | range->flags & NF_NAT_RANGE_PERSISTENT ? 0 : dst->all[3] ^ zone, HASHTABLE_BUCKET_BITS); 1008 | //j = jhash2((u32 *) src, 4, range->flags & NF_NAT_RANGE_PERSISTENT ? 0 : dst->all[3] ^ zone); 1009 | #endif 1010 | 1011 | full_range = false; 1012 | for (i = 0; i <= 3; i++) { 1013 | /* If first bytes of the address are at the maximum, use the 1014 | * distance. Otherwise use the full range. */ 1015 | if (!full_range) { 1016 | minip = ntohl(range->min_addr.all[i]); 1017 | maxip = ntohl(range->max_addr.all[i]); 1018 | dist = maxip - minip + 1; 1019 | } else { 1020 | minip = 0; 1021 | dist = ~0; 1022 | } 1023 | 1024 | var_ipp->all[i] = (__force __be32) htonl(minip + reciprocal_scale(j, dist)); 1025 | if (var_ipp->all[i] != range->max_addr.all[i]) 1026 | full_range = true; 1027 | 1028 | if (!(range->flags & NF_NAT_RANGE_PERSISTENT)) 1029 | j ^= (__force u32) dst->all[i]; 1030 | } 1031 | } 1032 | #endif 1033 | 1034 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) 1035 | static __be32 find_leastused_ip(const struct nf_conntrack_zone *zone, 1036 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) 1037 | struct nf_nat_range2 *range, 1038 | #else 1039 | struct nf_nat_range *range, 1040 | #endif 1041 | const __be32 src, const __be32 dst) 1042 | #else 1043 | static __be32 find_leastused_ip(const u16 zone, 1044 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) 1045 | struct nf_nat_range2 *range, 1046 | #else 1047 | struct nf_nat_range *range, 1048 | #endif 1049 | const __be32 src, const __be32 dst) 1050 | #endif 1051 | { 1052 | /* Host order */ 1053 | u32 minip, maxip, j, dist; 1054 | 1055 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) 1056 | // nf_nat_range2 specific 1057 | memset(&(range->base_proto), 0, sizeof(range->base_proto)); 1058 | #endif 1059 | 1060 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) 1061 | j = FULLCONE_HKEY(&src, sizeof(__be32), range->flags & NF_NAT_RANGE_PERSISTENT ? 0 : dst ^ zone->id, 1062 | HASHTABLE_BUCKET_BITS); 1063 | //j = jhash_1word((u32) src, range->flags & NF_NAT_RANGE_PERSISTENT ? 0 : dst ^ zone->id); 1064 | #else 1065 | j = FULLCONE_HKEY(&src, sizeof(__be32), range->flags & NF_NAT_RANGE_PERSISTENT ? 0 : dst ^ zone, 1066 | HASHTABLE_BUCKET_BITS); 1067 | //j = jhash_1word((u32) src, range->flags & NF_NAT_RANGE_PERSISTENT ? 0 : dst ^ zone); 1068 | #endif 1069 | 1070 | minip = ntohl(range->min_addr.ip); 1071 | maxip = ntohl(range->max_addr.ip); 1072 | dist = maxip - minip + 1; 1073 | 1074 | return (__be32) htonl(minip + reciprocal_scale(j, dist)); 1075 | } 1076 | 1077 | void nf_nat_fullcone_destroy_mappings(void) 1078 | { 1079 | struct nat_mapping *p_current; 1080 | #if IS_ENABLED(CONFIG_NF_NAT_IPV6) || (IS_ENABLED(CONFIG_IPV6) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) 1081 | struct nat_mapping6 *p6_current; 1082 | #endif 1083 | struct hlist_node *tmp; 1084 | int i; 1085 | 1086 | spin_lock_bh(&fullconenat_lock); 1087 | 1088 | hash_for_each_safe(mapping_table_by_ext_port, i, tmp, p_current, node_by_ext_port) { 1089 | kill_mapping(p_current); 1090 | } 1091 | 1092 | spin_unlock_bh(&fullconenat_lock); 1093 | 1094 | #if IS_ENABLED(CONFIG_NF_NAT_IPV6) || (IS_ENABLED(CONFIG_IPV6) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) 1095 | spin_lock_bh(&fullconenat6_lock); 1096 | 1097 | hash_for_each_safe(mapping6_table_by_ext_port, i, tmp, p6_current, node_by_ext_port) { 1098 | kill_mapping6(p6_current); 1099 | } 1100 | 1101 | spin_unlock_bh(&fullconenat6_lock); 1102 | #endif 1103 | } 1104 | 1105 | EXPORT_SYMBOL_GPL(nf_nat_fullcone_destroy_mappings); 1106 | 1107 | /* 1108 | * nfproto choices 1109 | * enum { 1110 | NFPROTO_INET = 1, 1111 | NFPROTO_IPV4 = 2, 1112 | NFPROTO_IPV6 = 10, 1113 | }; 1114 | */ 1115 | static unsigned int nf_nat_handle_prerouting(u8 nfproto, struct sk_buff *skb, unsigned int hooknum, 1116 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) 1117 | struct nf_nat_range2 *newrange) 1118 | #else 1119 | struct nf_nat_range *newrange) 1120 | #endif 1121 | { 1122 | unsigned int ret; 1123 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) 1124 | const struct nf_conntrack_zone *zone; 1125 | #else 1126 | u16 zone; 1127 | #endif 1128 | struct net *net; 1129 | struct nf_conn *ct; 1130 | enum ip_conntrack_info ctinfo; 1131 | struct nf_conntrack_tuple *ct_tuple_origin; 1132 | 1133 | uint16_t port, original_port; 1134 | uint8_t protonum; 1135 | 1136 | /* NFPROTO specific def */ 1137 | struct nat_mapping *mapping; 1138 | struct nat_mapping6 *mapping_6; 1139 | 1140 | __be32 ip; 1141 | union nf_inet_addr *ip_6; 1142 | /* NFPROTO specific def end */ 1143 | 1144 | WARN_ON(!(nfproto == NFPROTO_IPV4 || nfproto == NFPROTO_IPV6)); 1145 | 1146 | /* NFPROTO specific init */ 1147 | mapping = NULL; 1148 | mapping_6 = NULL; 1149 | 1150 | ip = 0; 1151 | ip_6 = NULL; 1152 | /* NFPROTO specific init end */ 1153 | 1154 | original_port = 0; 1155 | ret = NFT_CONTINUE; // BUG: use XT_CONTINUE for Xtables 1156 | 1157 | ct = nf_ct_get(skb, &ctinfo); 1158 | net = nf_ct_net(ct); 1159 | zone = nf_ct_zone(ct); 1160 | 1161 | ct_tuple_origin = &(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); 1162 | 1163 | protonum = (ct_tuple_origin->dst).protonum; 1164 | if (protonum != IPPROTO_UDP) { 1165 | // Currently only UDP traffic is supported for full-cone NAT. 1166 | // For other protos FULLCONENAT is equivalent to MASQUERADE. 1167 | return ret; 1168 | } 1169 | 1170 | if (nfproto == NFPROTO_IPV4) { 1171 | ip = (ct_tuple_origin->dst).u3.ip; 1172 | } else if (nfproto == NFPROTO_IPV6) { 1173 | ip_6 = &(ct_tuple_origin->dst).u3; 1174 | } 1175 | 1176 | port = be16_to_cpu((ct_tuple_origin->dst).u.udp.port); 1177 | 1178 | if (nfproto == NFPROTO_IPV4) { 1179 | spin_lock_bh(&fullconenat_lock); 1180 | } else if (nfproto == NFPROTO_IPV6) { 1181 | spin_lock_bh(&fullconenat6_lock); 1182 | } 1183 | 1184 | /* find an active mapping based on the inbound port */ 1185 | if (nfproto == NFPROTO_IPV4) { 1186 | mapping = get_mapping_by_ext_port(port, ip, net, zone); 1187 | } else if (nfproto == NFPROTO_IPV6) { 1188 | mapping_6 = get_mapping6_by_ext_port(port, ip_6, net, zone); 1189 | } 1190 | 1191 | if (nfproto == NFPROTO_IPV4) { 1192 | if (mapping == NULL) { 1193 | goto unlock; 1194 | } 1195 | } else if (nfproto == NFPROTO_IPV6) { 1196 | if (mapping_6 == NULL) { 1197 | goto unlock; 1198 | } 1199 | } 1200 | 1201 | newrange->flags = NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED; 1202 | if (nfproto == NFPROTO_IPV4) { 1203 | newrange->min_addr.ip = mapping->int_addr; 1204 | newrange->max_addr.ip = mapping->int_addr; 1205 | newrange->min_proto.udp.port = cpu_to_be16(mapping->int_port); 1206 | } else if (nfproto == NFPROTO_IPV6) { 1207 | newrange->min_addr = mapping_6->int_addr; 1208 | newrange->max_addr = mapping_6->int_addr; 1209 | newrange->min_proto.udp.port = cpu_to_be16(mapping_6->int_port); 1210 | } 1211 | newrange->max_proto = newrange->min_proto; 1212 | 1213 | if (nfproto == NFPROTO_IPV4) { 1214 | pr_debug(" %s ==> %pI4:%d\n", 1215 | nf_ct_stringify_tuple(ct_tuple_origin), &mapping->int_addr, mapping->int_port); 1216 | } else if (nfproto == NFPROTO_IPV6) { 1217 | pr_debug(" %s ==> [%pI6c]:%d\n", 1218 | fullcone_nf_ct_stringify_tuple6(ct_tuple_origin), &mapping_6->int_addr, mapping_6->int_port); 1219 | } 1220 | 1221 | ret = nf_nat_setup_info(ct, newrange, HOOK2MANIP(hooknum)); 1222 | 1223 | if (ret == NF_ACCEPT) { 1224 | if (nfproto == NFPROTO_IPV4) { 1225 | add_original_tuple_to_mapping(mapping, ct_tuple_origin); 1226 | pr_debug 1227 | ("INBOUND: refer_count for mapping at ext_port %d is now %d\n", 1228 | mapping->port, mapping->refer_count); 1229 | } else if (nfproto == NFPROTO_IPV6) { 1230 | add_original_tuple_to_mapping6(mapping_6, ct_tuple_origin); 1231 | pr_debug 1232 | ("INBOUND: refer_count for mapping_6 at ext_port %d is now %d\n", 1233 | mapping_6->port, mapping_6->refer_count); 1234 | } 1235 | 1236 | } 1237 | 1238 | unlock: 1239 | if (nfproto == NFPROTO_IPV4) { 1240 | spin_unlock_bh(&fullconenat_lock); 1241 | } else if (nfproto == NFPROTO_IPV6) { 1242 | spin_unlock_bh(&fullconenat6_lock); 1243 | } 1244 | 1245 | return ret; 1246 | 1247 | } 1248 | 1249 | static unsigned int nf_nat_handle_postrouting(u8 nfproto, struct sk_buff *skb, unsigned int hooknum, 1250 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) 1251 | struct nf_nat_range2 *range, struct nf_nat_range2 *newrange, 1252 | #else 1253 | struct nf_nat_range *range, struct nf_nat_range *newrange, 1254 | #endif 1255 | const struct net_device *out) 1256 | { 1257 | unsigned int ret; 1258 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) 1259 | const struct nf_conntrack_zone *zone; 1260 | #else 1261 | u16 zone; 1262 | #endif 1263 | struct net *net; 1264 | struct nf_conn *ct; 1265 | enum ip_conntrack_info ctinfo; 1266 | struct nf_conn_nat *nat; 1267 | struct nf_conntrack_tuple *ct_tuple, *ct_tuple_origin; 1268 | uint16_t port, original_port, want_port; 1269 | uint8_t protonum; 1270 | bool is_src_mapping_active; 1271 | 1272 | /* NFPROTO specific def */ 1273 | struct nat_mapping *mapping, *src_mapping; 1274 | struct nat_mapping6 *mapping_6, *src_mapping_6; 1275 | 1276 | __be32 ip; 1277 | union nf_inet_addr *ip_6; 1278 | 1279 | const struct rtable *rt; 1280 | __be32 newsrc, nh; 1281 | 1282 | /* NFPROTO specific def end */ 1283 | 1284 | WARN_ON(!(nfproto == NFPROTO_IPV4 || nfproto == NFPROTO_IPV6)); 1285 | 1286 | /* NFPROTO specific init */ 1287 | mapping = NULL; 1288 | src_mapping = NULL; 1289 | mapping_6 = NULL; 1290 | src_mapping_6 = NULL; 1291 | 1292 | ip = 0; 1293 | ip_6 = NULL; 1294 | /* NFPROTO specific init end */ 1295 | 1296 | original_port = 0; 1297 | ret = NFT_CONTINUE; // BUG: use XT_CONTINUE for Xtables 1298 | 1299 | ct = nf_ct_get(skb, &ctinfo); 1300 | net = nf_ct_net(ct); 1301 | zone = nf_ct_zone(ct); 1302 | 1303 | ct_tuple_origin = &(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); 1304 | protonum = (ct_tuple_origin->dst).protonum; 1305 | 1306 | if (range->flags & NF_NAT_RANGE_MAP_IPS) { 1307 | if (nfproto == NFPROTO_IPV4) { 1308 | newrange->min_addr.ip = range->min_addr.ip; 1309 | newrange->max_addr.ip = range->max_addr.ip; 1310 | } else if (nfproto == NFPROTO_IPV6) { 1311 | newrange->min_addr = range->min_addr; 1312 | newrange->max_addr = range->max_addr; 1313 | } 1314 | 1315 | } else { 1316 | if (nfproto == NFPROTO_IPV4) { 1317 | rt = skb_rtable(skb); 1318 | nh = rt_nexthop(rt, ip_hdr(skb)->daddr); 1319 | newsrc = inet_select_addr(out, nh, RT_SCOPE_UNIVERSE); 1320 | 1321 | if (unlikely(!newsrc)) 1322 | return NF_DROP; 1323 | newrange->min_addr.ip = newsrc; 1324 | newrange->max_addr.ip = newsrc; 1325 | } else if (nfproto == NFPROTO_IPV6) { 1326 | if (unlikely 1327 | (nat_ipv6_dev_get_saddr 1328 | (nf_ct_net(ct), out, &ipv6_hdr(skb)->daddr, 0, &(newrange->min_addr.in6)) < 0)) 1329 | return NF_DROP; 1330 | newrange->max_addr = newrange->min_addr; 1331 | 1332 | } 1333 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) 1334 | nat = nf_ct_nat_ext_add(ct); 1335 | #else 1336 | nat = nfct_nat(ct); 1337 | #endif 1338 | if (likely(nat)) 1339 | nat->masq_index = out->ifindex; 1340 | 1341 | } 1342 | 1343 | if (protonum == IPPROTO_UDP) { 1344 | if (nfproto == NFPROTO_IPV4) { 1345 | ip = (ct_tuple_origin->src).u3.ip; 1346 | } else if (nfproto == NFPROTO_IPV6) { 1347 | ip_6 = &(ct_tuple_origin->src).u3; 1348 | } 1349 | 1350 | original_port = be16_to_cpu((ct_tuple_origin->src).u.udp.port); 1351 | 1352 | if (nfproto == NFPROTO_IPV4) { 1353 | spin_lock_bh(&fullconenat_lock); 1354 | } else if (nfproto == NFPROTO_IPV6) { 1355 | spin_lock_bh(&fullconenat6_lock); 1356 | } 1357 | 1358 | if (nfproto == NFPROTO_IPV4) { 1359 | if (newrange->min_addr.ip != newrange->max_addr.ip) 1360 | src_mapping = 1361 | get_mapping_by_int_src_inrange(ip, 1362 | original_port, 1363 | newrange->min_addr.ip, newrange->max_addr.ip); 1364 | else 1365 | src_mapping = get_mapping_by_int_src(ip, original_port, newrange->min_addr.ip); 1366 | } else if (nfproto == NFPROTO_IPV6) { 1367 | if (!nf_inet_addr_cmp(&newrange->min_addr, &newrange->max_addr)) 1368 | src_mapping_6 = 1369 | get_mapping6_by_int_src_inrange(ip_6, 1370 | original_port, 1371 | &newrange->min_addr, &newrange->max_addr); 1372 | else 1373 | src_mapping_6 = get_mapping6_by_int_src(ip_6, original_port, &newrange->min_addr); 1374 | } 1375 | 1376 | if (nfproto == NFPROTO_IPV4) { 1377 | is_src_mapping_active = src_mapping != NULL && check_mapping(src_mapping, net, zone); 1378 | } else if (nfproto == NFPROTO_IPV6) { 1379 | is_src_mapping_active = src_mapping_6 != NULL && check_mapping6(src_mapping_6, net, zone); 1380 | } 1381 | 1382 | if (is_src_mapping_active) { 1383 | 1384 | /* outbound nat: if a previously established mapping is active, 1385 | * we will reuse that mapping. */ 1386 | 1387 | newrange->flags = NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED; 1388 | if (nfproto == NFPROTO_IPV4) { 1389 | newrange->min_proto.udp.port = cpu_to_be16(src_mapping->port); 1390 | } else if (nfproto == NFPROTO_IPV6) { 1391 | newrange->min_proto.udp.port = cpu_to_be16(src_mapping_6->port); 1392 | } 1393 | 1394 | newrange->max_proto = newrange->min_proto; 1395 | 1396 | if (nfproto == NFPROTO_IPV4) { 1397 | if (newrange->min_addr.ip != newrange->max_addr.ip) { 1398 | newrange->min_addr.ip = src_mapping->addr; 1399 | newrange->max_addr.ip = newrange->min_addr.ip; 1400 | } 1401 | } else if (nfproto == NFPROTO_IPV6) { 1402 | if (!nf_inet_addr_cmp(&newrange->min_addr, &newrange->max_addr)) { 1403 | newrange->min_addr = src_mapping_6->addr; 1404 | newrange->max_addr = newrange->min_addr; 1405 | } 1406 | } 1407 | 1408 | } else { 1409 | 1410 | /* if not, we find a new external IP:port to map to. 1411 | * the SNAT may fail so we should re-check the mapped port later. */ 1412 | 1413 | if (nfproto == NFPROTO_IPV4) { 1414 | if (newrange->min_addr.ip != newrange->max_addr.ip) { 1415 | newrange->min_addr.ip = 1416 | find_leastused_ip(zone, range, ip, (ct_tuple_origin->dst).u3.ip); 1417 | newrange->max_addr.ip = newrange->min_addr.ip; 1418 | } 1419 | want_port = 1420 | find_appropriate_port(net, zone, original_port, newrange->min_addr.ip, range); 1421 | } else if (nfproto == NFPROTO_IPV6) { 1422 | 1423 | if (!nf_inet_addr_cmp(&newrange->min_addr, &newrange->max_addr)) { 1424 | find_leastused_ip6(zone, range, ip_6, 1425 | &(ct_tuple_origin->dst).u3, &newrange->min_addr); 1426 | newrange->max_addr = newrange->min_addr; 1427 | } 1428 | 1429 | want_port = 1430 | find_appropriate_port6(net, zone, original_port, &newrange->min_addr, range); 1431 | } 1432 | 1433 | newrange->flags = NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED; 1434 | newrange->min_proto.udp.port = cpu_to_be16(want_port); 1435 | newrange->max_proto = newrange->min_proto; 1436 | 1437 | if (nfproto == NFPROTO_IPV4) { 1438 | src_mapping = NULL; 1439 | } else if (nfproto == NFPROTO_IPV6) { 1440 | src_mapping_6 = NULL; 1441 | } 1442 | 1443 | } 1444 | } 1445 | 1446 | /* do SNAT now */ 1447 | ret = nf_nat_setup_info(ct, newrange, HOOK2MANIP(hooknum)); 1448 | 1449 | if (protonum != IPPROTO_UDP) { 1450 | /* non-UDP packets, bailout */ 1451 | goto out; 1452 | } 1453 | if (ret != NF_ACCEPT) { 1454 | /* failed SNAT, bailout */ 1455 | goto unlock; 1456 | } 1457 | 1458 | /* the reply tuple contains the mapped port. */ 1459 | ct_tuple = &(ct->tuplehash[IP_CT_DIR_REPLY].tuple); 1460 | /* this is the resulted mapped port. */ 1461 | port = be16_to_cpu((ct_tuple->dst).u.udp.port); 1462 | 1463 | if (nfproto == NFPROTO_IPV4) { 1464 | pr_debug(" %s ==> %d\n", nf_ct_stringify_tuple(ct_tuple_origin), port); 1465 | } else if (nfproto == NFPROTO_IPV6) { 1466 | pr_debug(" %s ==> %d\n", fullcone_nf_ct_stringify_tuple6(ct_tuple_origin), port); 1467 | } 1468 | 1469 | /* save the mapping information into our mapping table */ 1470 | 1471 | if (nfproto == NFPROTO_IPV4) { 1472 | mapping = src_mapping; 1473 | if (mapping == NULL) { 1474 | mapping = allocate_mapping(ip, original_port, port, (ct_tuple->dst).u3.ip); 1475 | } 1476 | if (likely(mapping != NULL)) { 1477 | add_original_tuple_to_mapping(mapping, ct_tuple_origin); 1478 | pr_debug 1479 | (" OUTBOUND: refer_count for mapping at ext_port %d is now %d\n", 1480 | mapping->port, mapping->refer_count); 1481 | } 1482 | } else if (nfproto == NFPROTO_IPV6) { 1483 | mapping_6 = src_mapping_6; 1484 | if (mapping_6 == NULL) { 1485 | mapping_6 = allocate_mapping6(ip_6, original_port, port, &(ct_tuple->dst).u3); 1486 | } 1487 | if (likely(mapping_6 != NULL)) { 1488 | add_original_tuple_to_mapping6(mapping_6, ct_tuple_origin); 1489 | pr_debug 1490 | ("OUTBOUND: refer_count for mapping at ext_port %d is now %d\n", 1491 | mapping_6->port, mapping_6->refer_count); 1492 | } 1493 | } 1494 | 1495 | unlock: 1496 | if (nfproto == NFPROTO_IPV4) { 1497 | spin_unlock_bh(&fullconenat_lock); 1498 | } else if (nfproto == NFPROTO_IPV6) { 1499 | spin_unlock_bh(&fullconenat6_lock); 1500 | } 1501 | 1502 | out: 1503 | return ret; 1504 | } 1505 | 1506 | unsigned int nf_nat_fullcone_ipv4(struct sk_buff *skb, unsigned int hooknum, 1507 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) 1508 | struct nf_nat_range2 *range, 1509 | #else 1510 | struct nf_nat_range *range, 1511 | #endif 1512 | const struct net_device *out) 1513 | { 1514 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) 1515 | struct nf_nat_range2 newrange; 1516 | #else 1517 | struct nf_nat_range newrange; 1518 | #endif 1519 | 1520 | WARN_ON(!(hooknum == NF_INET_POST_ROUTING || hooknum == NF_INET_PRE_ROUTING)); 1521 | 1522 | memset(&newrange.min_addr, 0, sizeof(newrange.min_addr)); 1523 | memset(&newrange.max_addr, 0, sizeof(newrange.max_addr)); 1524 | newrange.flags = range->flags | NF_NAT_RANGE_MAP_IPS; 1525 | newrange.min_proto = range->min_proto; 1526 | newrange.max_proto = range->max_proto; 1527 | 1528 | switch (hooknum) { 1529 | case NF_INET_PRE_ROUTING: 1530 | /* inbound packets */ 1531 | return nf_nat_handle_prerouting(NFPROTO_IPV4, skb, hooknum, &newrange); 1532 | case NF_INET_POST_ROUTING: 1533 | /* outbound packets */ 1534 | return nf_nat_handle_postrouting(NFPROTO_IPV4, skb, hooknum, range, &newrange, out); 1535 | } 1536 | 1537 | WARN_ON(1); 1538 | // logical error 1539 | return 5; 1540 | } 1541 | 1542 | EXPORT_SYMBOL_GPL(nf_nat_fullcone_ipv4); 1543 | 1544 | #if IS_ENABLED(CONFIG_IPV6) 1545 | static int 1546 | nat_ipv6_dev_get_saddr(struct net *net, const struct net_device *dev, 1547 | const struct in6_addr *daddr, unsigned int srcprefs, struct in6_addr *saddr) 1548 | { 1549 | #ifdef CONFIG_IPV6_MODULE 1550 | const struct nf_ipv6_ops *v6_ops = nf_get_ipv6_ops(); 1551 | 1552 | if (!v6_ops) 1553 | return -EHOSTUNREACH; 1554 | 1555 | return v6_ops->dev_get_saddr(net, dev, daddr, srcprefs, saddr); 1556 | #else 1557 | return ipv6_dev_get_saddr(net, dev, daddr, srcprefs, saddr); 1558 | #endif 1559 | } 1560 | #endif 1561 | 1562 | unsigned int nf_nat_fullcone_ipv6(struct sk_buff *skb, unsigned int hooknum, 1563 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) 1564 | struct nf_nat_range2 *range, 1565 | #else 1566 | struct nf_nat_range *range, 1567 | #endif 1568 | const struct net_device *out) 1569 | { 1570 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) 1571 | struct nf_nat_range2 newrange; 1572 | #else 1573 | struct nf_nat_range newrange; 1574 | #endif 1575 | 1576 | WARN_ON(!(hooknum == NF_INET_POST_ROUTING || hooknum == NF_INET_PRE_ROUTING)); 1577 | 1578 | memset(&newrange.min_addr, 0, sizeof(newrange.min_addr)); 1579 | memset(&newrange.max_addr, 0, sizeof(newrange.max_addr)); 1580 | newrange.flags = range->flags | NF_NAT_RANGE_MAP_IPS; 1581 | newrange.min_proto = range->min_proto; 1582 | newrange.max_proto = range->max_proto; 1583 | 1584 | switch (hooknum) { 1585 | case NF_INET_PRE_ROUTING: 1586 | /* inbound packets */ 1587 | return nf_nat_handle_prerouting(NFPROTO_IPV6, skb, hooknum, &newrange); 1588 | case NF_INET_POST_ROUTING: 1589 | /* outbound packets */ 1590 | return nf_nat_handle_postrouting(NFPROTO_IPV6, skb, hooknum, range, &newrange, out); 1591 | } 1592 | 1593 | WARN_ON(1); 1594 | // logical error 1595 | return 5; 1596 | } 1597 | 1598 | EXPORT_SYMBOL_GPL(nf_nat_fullcone_ipv6); 1599 | -------------------------------------------------------------------------------- /src/nf_nat_fullcone.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | 3 | /* 4 | * Nftables NAT extension: fullcone expression support library header 5 | * 6 | * Copyright (c) 2018 Chion Tang 7 | * Original xt_FULLCONENAT and related iptables extension author 8 | * Copyright (c) 2019-2022 GitHub/llccd Twitter/@gNodeB 9 | * Added IPv6 support for xt_FULLCONENAT and ip6tables extension 10 | * Ported to recent kernel versions 11 | * Copyright (c) 2022 Syrone Wong 12 | * Massively rewrite the whole module, split the original code into library and nftables 'fullcone' expression module 13 | */ 14 | #ifndef _NF_NAT_FULLCONE_H_ 15 | #define _NF_NAT_FULLCONE_H_ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #ifndef NF_NAT_RANGE_PROTO_RANDOM_FULLY 31 | #define NF_NAT_RANGE_PROTO_RANDOM_FULLY (1 << 4) 32 | #endif 33 | 34 | #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) 35 | static inline int nf_ct_netns_get(struct net *net, u8 nfproto) 36 | { 37 | return 0; 38 | } 39 | 40 | static inline void nf_ct_netns_put(struct net *net, u8 nfproto) 41 | { 42 | } 43 | #endif 44 | 45 | /** 46 | * enum nft_fullcone_attributes - nf_tables fullcone expression netlink attributes 47 | * 48 | * @NFTA_FULLCONE_REG_PROTO_MIN: source register of proto range start (NLA_U32: nft_registers) 49 | * @NFTA_FULLCONE_REG_PROTO_MAX: source register of proto range end (NLA_U32: nft_registers) 50 | * @NFTA_FULLCONE_FLAGS: NAT flags (see NF_NAT_RANGE_* in linux/netfilter/nf_nat.h) (NLA_U32) 51 | */ 52 | enum nft_fullcone_attributes { 53 | NFTA_FULLCONE_UNSPEC, 54 | NFTA_FULLCONE_REG_PROTO_MIN, 55 | NFTA_FULLCONE_REG_PROTO_MAX, 56 | NFTA_FULLCONE_FLAGS, 57 | __NFTA_FULLCONE_MAX 58 | }; 59 | #define NFTA_FULLCONE_MAX (__NFTA_FULLCONE_MAX - 1) 60 | 61 | /* fullcone specific data structures */ 62 | 63 | struct nat_mapping_original_tuple { 64 | struct nf_conntrack_tuple tuple; 65 | 66 | struct list_head node; 67 | }; 68 | 69 | struct nat_mapping { 70 | uint16_t port; /* external source port */ 71 | __be32 addr; /* external source ip address */ 72 | 73 | __be32 int_addr; /* internal source ip address */ 74 | uint16_t int_port; /* internal source port */ 75 | 76 | int refer_count; /* how many references linked to this mapping 77 | * aka. length of original_tuple_list */ 78 | 79 | struct list_head original_tuple_list; 80 | 81 | struct hlist_node node_by_ext_port; 82 | struct hlist_node node_by_int_src; 83 | 84 | }; 85 | 86 | #if IS_ENABLED(CONFIG_NF_NAT_IPV6) || (IS_ENABLED(CONFIG_IPV6) && LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) 87 | struct nat_mapping6 { 88 | uint16_t port; /* external source port */ 89 | union nf_inet_addr addr; /* external source ip address */ 90 | 91 | union nf_inet_addr int_addr; /* internal source ip address */ 92 | uint16_t int_port; /* internal source port */ 93 | 94 | int refer_count; /* how many references linked to this mapping 95 | * aka. length of original_tuple_list */ 96 | 97 | struct list_head original_tuple_list; 98 | 99 | struct hlist_node node_by_ext_port; 100 | struct hlist_node node_by_int_src; 101 | 102 | }; 103 | #endif 104 | 105 | struct tuple_list { 106 | struct nf_conntrack_tuple tuple_original; 107 | struct nf_conntrack_tuple tuple_reply; 108 | struct list_head list; 109 | }; 110 | 111 | /* fullcone specific data structures end */ 112 | 113 | // NOTE: declaration listed here must use EXPORT_SYMBOL_* 114 | 115 | unsigned int nf_nat_fullcone_ipv4(struct sk_buff *skb, unsigned int hooknum, 116 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) 117 | struct nf_nat_range2 *range, 118 | #else 119 | struct nf_nat_range *range, 120 | #endif 121 | const struct net_device *out); 122 | 123 | unsigned int nf_nat_fullcone_ipv6(struct sk_buff *skb, unsigned int hooknum, 124 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) 125 | struct nf_nat_range2 *range, 126 | #else 127 | struct nf_nat_range *range, 128 | #endif 129 | const struct net_device *out); 130 | 131 | void nf_nat_fullcone_handle_dying_tuples(void); 132 | void nf_nat_fullcone_destroy_mappings(void); 133 | void nf_nat_fullcone_dying_tuple_list_add(struct list_head *new_dying); 134 | 135 | /* 136 | * For [FUTURE] usage 137 | * 138 | * from https://elixir.bootlin.com/linux/v5.15.32/source/net/netfilter/xt_nat.c#L37 139 | static void xt_nat_convert_range(struct nf_nat_range2 *dst, 140 | const struct nf_nat_ipv4_range *src) 141 | { 142 | memset(&dst->min_addr, 0, sizeof(dst->min_addr)); 143 | memset(&dst->max_addr, 0, sizeof(dst->max_addr)); 144 | // base_proto is nf_nat_range2 specific 145 | memset(&dst->base_proto, 0, sizeof(dst->base_proto)); 146 | 147 | dst->flags = src->flags; 148 | dst->min_addr.ip = src->min_ip; 149 | dst->max_addr.ip = src->max_ip; 150 | dst->min_proto = src->min; 151 | dst->max_proto = src->max; 152 | } 153 | * 154 | */ 155 | 156 | #endif /*_NF_NAT_FULLCONE_H_ */ 157 | -------------------------------------------------------------------------------- /src/nft_ext_fullcone.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Nftables NAT extension: fullcone expression support 4 | * 5 | * Copyright (c) 2018 Chion Tang 6 | * Original xt_FULLCONENAT and related iptables extension author 7 | * Copyright (c) 2019-2022 GitHub/llccd Twitter/@gNodeB 8 | * Added IPv6 support for xt_FULLCONENAT and ip6tables extension 9 | * Ported to recent kernel versions 10 | * Copyright (c) 2022 Syrone Wong 11 | * Massively rewrite the whole module, split the original code into library and nftables 'fullcone' expression module 12 | */ 13 | #define pr_fmt(fmt) "fullcone " KBUILD_MODNAME ": " fmt 14 | #define NF_FULLCONE_WORKQUEUE_NAME "fullcone " KBUILD_MODNAME ": wq" 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | 27 | #include "nf_nat_fullcone.h" 28 | 29 | static void nft_fullcone_set_regs(const struct nft_expr *expr, const struct nft_regs *regs, 30 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) 31 | struct nf_nat_range2 *range); 32 | #else 33 | struct nf_nat_range *range); 34 | #endif 35 | 36 | #ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS 37 | struct notifier_block ct_event_notifier; 38 | #else 39 | struct nf_ct_event_notifier ct_event_notifier; 40 | #endif 41 | static DEFINE_MUTEX(nf_ct_net_event_lock); 42 | int ct_event_notifier_registered = 0; 43 | 44 | int module_refer_count = 0; 45 | 46 | static void gc_worker(struct work_struct *work); 47 | static struct workqueue_struct *wq __read_mostly = NULL; 48 | static DECLARE_DELAYED_WORK(gc_worker_wk, gc_worker); 49 | 50 | static void gc_worker(struct work_struct *work) 51 | { 52 | nf_nat_fullcone_handle_dying_tuples(); 53 | } 54 | 55 | struct nft_fullcone { 56 | u32 flags; 57 | u8 sreg_proto_min; 58 | u8 sreg_proto_max; 59 | }; 60 | 61 | static const struct nla_policy nft_fullcone_policy[NFTA_FULLCONE_MAX + 1] = { 62 | [NFTA_FULLCONE_FLAGS] = {.type = NLA_U32 }, 63 | [NFTA_FULLCONE_REG_PROTO_MIN] = {.type = NLA_U32 }, 64 | [NFTA_FULLCONE_REG_PROTO_MAX] = {.type = NLA_U32 }, 65 | }; 66 | 67 | /* conntrack destroy event callback function */ 68 | #ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS 69 | static int ct_event_cb(struct notifier_block *this, unsigned long events, void *ptr) 70 | { 71 | struct nf_ct_event *item = ptr; 72 | #elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) 73 | static int ct_event_cb(unsigned int events, const struct nf_ct_event *item) 74 | { 75 | #else 76 | static int ct_event_cb(unsigned int events, struct nf_ct_event *item) 77 | { 78 | #endif 79 | struct nf_conn *ct; 80 | struct nf_conntrack_tuple *ct_tuple_reply, *ct_tuple_original; 81 | uint8_t protonum; 82 | struct tuple_list *dying_tuple_item; 83 | 84 | ct = item->ct; 85 | /* we handle only conntrack destroy events */ 86 | if (ct == NULL || !(events & (1 << IPCT_DESTROY))) { 87 | return 0; 88 | } 89 | 90 | ct_tuple_original = &(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); 91 | 92 | ct_tuple_reply = &(ct->tuplehash[IP_CT_DIR_REPLY].tuple); 93 | 94 | protonum = (ct_tuple_original->dst).protonum; 95 | if (protonum != IPPROTO_UDP) { 96 | return 0; 97 | } 98 | 99 | dying_tuple_item = kmalloc(sizeof(struct tuple_list), GFP_ATOMIC); 100 | 101 | if (dying_tuple_item == NULL) { 102 | pr_debug("warning: ct_event_cb(): kmalloc failed.\n"); 103 | return 0; 104 | } 105 | 106 | memcpy(&(dying_tuple_item->tuple_original), ct_tuple_original, sizeof(struct nf_conntrack_tuple)); 107 | memcpy(&(dying_tuple_item->tuple_reply), ct_tuple_reply, sizeof(struct nf_conntrack_tuple)); 108 | 109 | nf_nat_fullcone_dying_tuple_list_add(&(dying_tuple_item->list)); 110 | 111 | if (wq != NULL) 112 | queue_delayed_work(wq, &gc_worker_wk, msecs_to_jiffies(100)); 113 | 114 | return 0; 115 | } 116 | 117 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) && !defined(CONFIG_NF_CONNTRACK_CHAIN_EVENTS) 118 | static int exp_event_cb(unsigned int events, const struct nf_exp_event *item) 119 | { 120 | return 0; 121 | } 122 | #endif 123 | 124 | static int nft_fullcone_validate(const struct nft_ctx *ctx, const struct nft_expr *expr, const struct nft_data **data) 125 | { 126 | int err; 127 | 128 | err = nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT); 129 | if (err < 0) 130 | return err; 131 | 132 | // TODO: check hooks 133 | return nft_chain_validate_hooks(ctx->chain, (1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_POST_ROUTING)); 134 | } 135 | 136 | static int nft_fullcone_init(const struct nft_ctx *ctx, const struct nft_expr *expr, const struct nlattr *const tb[]) 137 | { 138 | int err; 139 | int register_ct_notifier_ret = 0; 140 | 141 | err = nf_ct_netns_get(ctx->net, ctx->family); 142 | 143 | mutex_lock(&nf_ct_net_event_lock); 144 | 145 | module_refer_count++; 146 | 147 | pr_debug("nft_fullcone_init(): module_refer_count is now %d\n", module_refer_count); 148 | 149 | if (module_refer_count == 1) { 150 | #ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS 151 | ct_event_notifier.notifier_call = ct_event_cb; 152 | #elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) 153 | ct_event_notifier.ct_event = ct_event_cb; 154 | ct_event_notifier.exp_event = exp_event_cb; 155 | #else 156 | ct_event_notifier.fcn = ct_event_cb; 157 | #endif 158 | 159 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) && !defined(CONFIG_NF_CONNTRACK_CHAIN_EVENTS) 160 | if (!READ_ONCE(ctx->net->ct.nf_conntrack_event_cb)) { 161 | nf_conntrack_register_notifier(ctx->net, &ct_event_notifier); 162 | } 163 | #else 164 | register_ct_notifier_ret = nf_conntrack_register_notifier(ctx->net, &ct_event_notifier); 165 | #endif 166 | 167 | if (register_ct_notifier_ret) { 168 | /* non-zero means failure */ 169 | pr_warn("failed to register a conntrack notifier. Disable active GC for mappings.\n"); 170 | } else { 171 | ct_event_notifier_registered = 1; 172 | pr_debug("nft_fullcone_init(): ct_event_notifier registered\n"); 173 | } 174 | 175 | } 176 | 177 | mutex_unlock(&nf_ct_net_event_lock); 178 | 179 | return err; 180 | } 181 | 182 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 2, 0) 183 | static int nft_fullcone_dump(struct sk_buff *skb, const struct nft_expr *expr, bool reset) 184 | #else 185 | static int nft_fullcone_dump(struct sk_buff *skb, const struct nft_expr *expr) 186 | #endif 187 | { 188 | const struct nft_fullcone *priv = nft_expr_priv(expr); 189 | 190 | if (priv->flags != 0 && nla_put_be32(skb, NFTA_FULLCONE_FLAGS, htonl(priv->flags))) 191 | goto nla_put_failure; 192 | 193 | if (priv->sreg_proto_min) { 194 | if (nft_dump_register(skb, NFTA_FULLCONE_REG_PROTO_MIN, 195 | priv->sreg_proto_min) || 196 | nft_dump_register(skb, NFTA_FULLCONE_REG_PROTO_MAX, priv->sreg_proto_max)) 197 | goto nla_put_failure; 198 | } 199 | 200 | return 0; 201 | 202 | nla_put_failure: 203 | return -1; 204 | } 205 | 206 | /* nft_fullcone_set_regs sets nft_regs from nft_expr fullcone specific private data */ 207 | static void nft_fullcone_set_regs(const struct nft_expr *expr, const struct nft_regs *regs, 208 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) 209 | struct nf_nat_range2 *range 210 | #else 211 | struct nf_nat_range *range 212 | #endif 213 | ) 214 | { 215 | // private data connected via nft_expr_type.ops <==> nft_expr_ops.type 216 | // private data type from nft_expr_type.{policy,maxattr,ops} 217 | // private data size from nft_expr_ops.size 218 | struct nft_fullcone *priv = nft_expr_priv(expr); 219 | range->flags = priv->flags; 220 | if (priv->sreg_proto_min) { 221 | range->min_proto.all = (__force __be16) 222 | nft_reg_load16(®s->data[priv->sreg_proto_min]); 223 | range->max_proto.all = (__force __be16) 224 | nft_reg_load16(®s->data[priv->sreg_proto_max]); 225 | } 226 | } 227 | 228 | static void nft_fullcone_ipv4_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) 229 | { 230 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) 231 | struct nf_nat_range2 range; 232 | #else 233 | struct nf_nat_range range; 234 | #endif 235 | 236 | memset(&range, 0, sizeof(range)); 237 | nft_fullcone_set_regs(expr, regs, &range); 238 | regs->verdict.code = nf_nat_fullcone_ipv4(pkt->skb, nft_hook(pkt), &range, nft_out(pkt)); 239 | } 240 | 241 | static void nft_fullcone_common_destory(const struct nft_ctx *ctx) 242 | { 243 | mutex_lock(&nf_ct_net_event_lock); 244 | 245 | module_refer_count--; 246 | 247 | pr_debug("nft_fullcone_common_destory(): module_refer_count is now %d\n", module_refer_count); 248 | 249 | if (module_refer_count == 0) { 250 | if (ct_event_notifier_registered) { 251 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) && !defined(CONFIG_NF_CONNTRACK_CHAIN_EVENTS) 252 | nf_conntrack_unregister_notifier(ctx->net); 253 | #else 254 | nf_conntrack_unregister_notifier(ctx->net, &ct_event_notifier); 255 | #endif 256 | ct_event_notifier_registered = 0; 257 | 258 | pr_debug("nft_fullcone_common_destory(): ct_event_notifier unregistered\n"); 259 | 260 | } 261 | } 262 | 263 | mutex_unlock(&nf_ct_net_event_lock); 264 | } 265 | 266 | static void nft_fullcone_ipv4_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr) 267 | { 268 | nft_fullcone_common_destory(ctx); 269 | nf_ct_netns_put(ctx->net, NFPROTO_IPV4); 270 | } 271 | 272 | static struct nft_expr_type nft_fullcone_ipv4_type; 273 | static const struct nft_expr_ops nft_fullcone_ipv4_ops = { 274 | .type = &nft_fullcone_ipv4_type, 275 | .size = NFT_EXPR_SIZE(sizeof(struct nft_fullcone)), 276 | .eval = nft_fullcone_ipv4_eval, 277 | .init = nft_fullcone_init, 278 | .destroy = nft_fullcone_ipv4_destroy, 279 | .dump = nft_fullcone_dump, 280 | .validate = nft_fullcone_validate, 281 | }; 282 | 283 | static struct nft_expr_type nft_fullcone_ipv4_type __read_mostly = { 284 | .family = NFPROTO_IPV4, 285 | .name = "fullcone", 286 | .ops = &nft_fullcone_ipv4_ops, 287 | .policy = nft_fullcone_policy, 288 | .maxattr = NFTA_FULLCONE_MAX, 289 | .owner = THIS_MODULE, 290 | }; 291 | 292 | #ifdef CONFIG_NF_TABLES_IPV6 293 | static void nft_fullcone_ipv6_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) 294 | { 295 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) 296 | struct nf_nat_range2 range; 297 | #else 298 | struct nf_nat_range range; 299 | #endif 300 | 301 | memset(&range, 0, sizeof(range)); 302 | nft_fullcone_set_regs(expr, regs, &range); 303 | regs->verdict.code = nf_nat_fullcone_ipv6(pkt->skb, nft_hook(pkt), &range, nft_out(pkt)); 304 | } 305 | 306 | static void nft_fullcone_ipv6_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr) 307 | { 308 | nft_fullcone_common_destory(ctx); 309 | nf_ct_netns_put(ctx->net, NFPROTO_IPV6); 310 | } 311 | 312 | static struct nft_expr_type nft_fullcone_ipv6_type; 313 | static const struct nft_expr_ops nft_fullcone_ipv6_ops = { 314 | .type = &nft_fullcone_ipv6_type, 315 | .size = NFT_EXPR_SIZE(sizeof(struct nft_fullcone)), 316 | .eval = nft_fullcone_ipv6_eval, 317 | .init = nft_fullcone_init, 318 | .destroy = nft_fullcone_ipv6_destroy, 319 | .dump = nft_fullcone_dump, 320 | .validate = nft_fullcone_validate, 321 | }; 322 | 323 | static struct nft_expr_type nft_fullcone_ipv6_type __read_mostly = { 324 | .family = NFPROTO_IPV6, 325 | .name = "fullcone", 326 | .ops = &nft_fullcone_ipv6_ops, 327 | .policy = nft_fullcone_policy, 328 | .maxattr = NFTA_FULLCONE_MAX, 329 | .owner = THIS_MODULE, 330 | }; 331 | 332 | static int __init nft_fullcone_module_init_ipv6(void) 333 | { 334 | return nft_register_expr(&nft_fullcone_ipv6_type); 335 | } 336 | 337 | static void nft_fullcone_module_exit_ipv6(void) 338 | { 339 | nft_unregister_expr(&nft_fullcone_ipv6_type); 340 | } 341 | #else 342 | static inline int nft_fullcone_module_init_ipv6(void) 343 | { 344 | return 0; 345 | } 346 | 347 | static inline void nft_fullcone_module_exit_ipv6(void) 348 | { 349 | } 350 | #endif 351 | 352 | #ifdef CONFIG_NF_TABLES_INET 353 | static void nft_fullcone_inet_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) 354 | { 355 | switch (nft_pf(pkt)) { 356 | case NFPROTO_IPV4: 357 | return nft_fullcone_ipv4_eval(expr, regs, pkt); 358 | case NFPROTO_IPV6: 359 | return nft_fullcone_ipv6_eval(expr, regs, pkt); 360 | } 361 | 362 | WARN_ON_ONCE(1); 363 | } 364 | 365 | static void nft_fullcone_inet_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr) 366 | { 367 | nft_fullcone_common_destory(ctx); 368 | nf_ct_netns_put(ctx->net, NFPROTO_INET); 369 | } 370 | 371 | static struct nft_expr_type nft_fullcone_inet_type; 372 | static const struct nft_expr_ops nft_fullcone_inet_ops = { 373 | .type = &nft_fullcone_inet_type, 374 | .size = NFT_EXPR_SIZE(sizeof(struct nft_fullcone)), 375 | .eval = nft_fullcone_inet_eval, 376 | .init = nft_fullcone_init, 377 | .destroy = nft_fullcone_inet_destroy, 378 | .dump = nft_fullcone_dump, 379 | .validate = nft_fullcone_validate, 380 | }; 381 | 382 | static struct nft_expr_type nft_fullcone_inet_type __read_mostly = { 383 | .family = NFPROTO_INET, 384 | .name = "fullcone", 385 | .ops = &nft_fullcone_inet_ops, 386 | .policy = nft_fullcone_policy, 387 | .maxattr = NFTA_FULLCONE_MAX, 388 | .owner = THIS_MODULE, 389 | }; 390 | 391 | static int __init nft_fullcone_module_init_inet(void) 392 | { 393 | return nft_register_expr(&nft_fullcone_inet_type); 394 | } 395 | 396 | static void nft_fullcone_module_exit_inet(void) 397 | { 398 | nft_unregister_expr(&nft_fullcone_inet_type); 399 | } 400 | #else 401 | static inline int nft_fullcone_module_init_inet(void) 402 | { 403 | return 0; 404 | } 405 | 406 | static inline void nft_fullcone_module_exit_inet(void) 407 | { 408 | } 409 | #endif 410 | 411 | static int __init nft_fullcone_module_init(void) 412 | { 413 | int ret; 414 | 415 | ret = nft_fullcone_module_init_ipv6(); 416 | if (ret < 0) 417 | return ret; 418 | 419 | ret = nft_fullcone_module_init_inet(); 420 | if (ret < 0) { 421 | nft_fullcone_module_exit_ipv6(); 422 | return ret; 423 | } 424 | 425 | ret = nft_register_expr(&nft_fullcone_ipv4_type); 426 | if (ret < 0) { 427 | nft_fullcone_module_exit_inet(); 428 | nft_fullcone_module_exit_ipv6(); 429 | return ret; 430 | } 431 | 432 | wq = create_singlethread_workqueue(NF_FULLCONE_WORKQUEUE_NAME); 433 | if (wq == NULL) { 434 | pr_err("failed to create workqueue %s\n", NF_FULLCONE_WORKQUEUE_NAME); 435 | } 436 | 437 | return ret; 438 | } 439 | 440 | static void __exit nft_fullcone_module_exit(void) 441 | { 442 | nft_fullcone_module_exit_ipv6(); 443 | nft_fullcone_module_exit_inet(); 444 | nft_unregister_expr(&nft_fullcone_ipv4_type); 445 | 446 | if (wq) { 447 | cancel_delayed_work_sync(&gc_worker_wk); 448 | flush_workqueue(wq); 449 | destroy_workqueue(wq); 450 | } 451 | 452 | nf_nat_fullcone_handle_dying_tuples(); 453 | nf_nat_fullcone_destroy_mappings(); 454 | } 455 | 456 | module_init(nft_fullcone_module_init); 457 | module_exit(nft_fullcone_module_exit); 458 | 459 | MODULE_LICENSE("GPL"); 460 | MODULE_AUTHOR("Syrone Wong "); 461 | MODULE_ALIAS_NFT_EXPR("fullcone"); 462 | MODULE_DESCRIPTION("Netfilter nftables fullcone expression support of RFC3489 full cone NAT"); 463 | --------------------------------------------------------------------------------