├── .github └── workflows │ └── NanoPi-R2S RK3328 OpenWrt 19.07 Build.yml ├── CHANGELOG.md ├── README.md ├── patches ├── 001-add-full_cone_nat.patch └── 011-fix-zerotier-aes.patch ├── r2s-rk3328-config └── rk3328.xml /.github/workflows/NanoPi-R2S RK3328 OpenWrt 19.07 Build.yml: -------------------------------------------------------------------------------- 1 | name: NanoPi-R2S RK3328 OpenWrt 19.07 Build 2 | 3 | on: 4 | push: 5 | paths: 6 | - 'CHANGELOG.md' 7 | 8 | jobs: 9 | 10 | build: 11 | 12 | runs-on: self-hosted 13 | 14 | steps: 15 | 16 | - name: Checkout 17 | uses: actions/checkout@master 18 | with: 19 | ref: master 20 | 21 | - name: Initialize Environment 22 | env: 23 | DEBIAN_FRONTEND: noninteractive 24 | run: | 25 | sudo apt-get update 26 | sudo rm -rf /usr/share/dotnet /usr/local/lib/android/sdk 27 | sudo docker image prune -a -f 28 | sudo apt-get -y install unzip subversion 29 | sudo apt-get -y purge dotnet* ghc* google* llvm* mysql* php* zulu* firefox hhvm 30 | sudo apt-get -y autoremove --purge 31 | wget -O - https://raw.githubusercontent.com/friendlyarm/build-env-on-ubuntu-bionic/master/install.sh | bash 32 | 33 | #- name: Setup Debug Session 34 | # uses: P3TERX/debugger-action@master 35 | 36 | - name: Install Repo 37 | run: | 38 | git clone https://github.com/friendlyarm/repo 39 | sudo cp repo/repo /usr/bin/ 40 | 41 | - name: Download Source 42 | run: | 43 | rm -rf code 44 | mkdir code 45 | cd code 46 | repo init -u https://github.com/friendlyarm/friendlywrt_manifests -b master-v19.07.5 -m rk3328.xml --repo-url=https://github.com/friendlyarm/repo --no-clone-bundle 47 | cp ../rk3328.xml ../code/.repo/manifests/rk3328.xml 48 | repo sync -c --no-clone-bundle -j8 49 | 50 | - name: Merge LEDE 51 | run: | 52 | cd code 53 | git clone https://github.com/coolsnowwolf/lede 54 | cd friendlywrt 55 | cp -r ../lede/package/lean package/ 56 | cp -r ../lede/package/libs/pcre package/libs/ 57 | sed -i 's/^src-git luci.*/src-git luci https:\/\/github.com\/coolsnowwolf\/luci/' feeds.conf.default 58 | sed -i 's/^src-git packages.*/src-git packages https:\/\/github.com\/coolsnowwolf\/packages;openwrt-19.07/' feeds.conf.default 59 | echo 'src-git node https://github.com/nxhack/openwrt-node-packages.git' >> feeds.conf.default 60 | 61 | - name: Install Extra Packages 62 | run: | 63 | cd code/friendlywrt/package 64 | 65 | git clone https://github.com/rufengsuixing/luci-app-adguardhome 66 | 67 | git clone https://github.com/jerrykuku/lua-maxminddb.git 68 | git clone https://github.com/jerrykuku/luci-app-vssr.git 69 | 70 | rm -rf lean/luci-theme-argon 71 | git clone -b 18.06 https://github.com/jerrykuku/luci-theme-argon.git 72 | 73 | svn co https://github.com/vernesong/OpenClash/trunk/luci-app-openclash 74 | 75 | svn co https://github.com/xiaorouji/openwrt-passwall/trunk/luci-app-passwall 76 | svn co https://github.com/xiaorouji/openwrt-passwall/trunk/tcping 77 | svn co https://github.com/xiaorouji/openwrt-passwall/trunk/ssocks 78 | svn co https://github.com/xiaorouji/openwrt-passwall/trunk/trojan-plus 79 | svn co https://github.com/xiaorouji/openwrt-passwall/trunk/trojan-go 80 | svn co https://github.com/xiaorouji/openwrt-passwall/trunk/naiveproxy 81 | svn co https://github.com/xiaorouji/openwrt-passwall/trunk/brook 82 | svn co https://github.com/xiaorouji/openwrt-passwall/trunk/chinadns-ng 83 | svn co https://github.com/xiaorouji/openwrt-passwall/trunk/xray-core 84 | svn co https://github.com/xiaorouji/openwrt-passwall/trunk/xray-plugin 85 | 86 | svn co https://github.com/songchenwen/nanopi-r2s/trunk/luci-app-r2sflasher 87 | 88 | svn co https://github.com/pymumu/smartdns/trunk/package/openwrt smartdns 89 | svn co https://github.com/pymumu/luci-app-smartdns/branches/lede luci-app-smartdns 90 | 91 | svn co https://github.com/lisaac/luci-app-dockerman/trunk/applications/luci-app-dockerman 92 | 93 | git clone https://github.com/jerrykuku/luci-app-jd-dailybonus.git 94 | git clone https://github.com/NateLol/luci-app-oled 95 | 96 | - name: Install Clash Binaries 97 | run: | 98 | cd code/friendlywrt/package/base-files/files 99 | mkdir -p etc/openclash/core 100 | # for updates, go to: https://github.com/Dreamacro/clash/releases 101 | wget -qO- https://github.com/Dreamacro/clash/releases/download/v1.4.2/clash-linux-armv8-v1.4.2.gz | gunzip -c > etc/openclash/core/clash 102 | # for updates, go to: https://github.com/vernesong/OpenClash/releases/tag/TUN-Premium 103 | wget -qO- https://github.com/vernesong/OpenClash/releases/download/TUN-Premium/clash-linux-armv8-2021.03.10.gz | gunzip -c > etc/openclash/core/clash_tun 104 | # for updates, go to: https://github.com/vernesong/OpenClash/releases/tag/TUN 105 | wget -qO- https://github.com/vernesong/OpenClash/releases/download/TUN/clash-linux-armv8.tar.gz | tar xOvz > etc/openclash/core/clash_game 106 | chmod +x etc/openclash/core/clash* 107 | 108 | - name: Update Target.mk 109 | run: | 110 | cd code/friendlywrt/include 111 | sed -i 's/dnsmasq /dnsmasq-full default-settings luci /' target.mk 112 | 113 | - name: Update Feeds 114 | run: | 115 | cd code/friendlywrt 116 | ./scripts/feeds update -a 117 | ./scripts/feeds install -a 118 | 119 | - name: Install Mods 120 | run: | 121 | cd code/friendlywrt 122 | 123 | rm -rf feeds/packages/libs/libcap 124 | svn co https://github.com/openwrt/packages/trunk/libs/libcap feeds/packages/libs/libcap 125 | 126 | rm -rf package/feeds/packages/netdata 127 | svn co https://github.com/openwrt/packages/trunk/admin/netdata package/feeds/packages/netdata 128 | 129 | rm -rf feeds/packages/libs/libnatpmp 130 | svn co https://github.com/coolsnowwolf/packages/trunk/libs/libnatpmp feeds/packages/libs/libnatpmp 131 | 132 | rm -rf feeds/packages/lang/luasec 133 | svn co https://github.com/coolsnowwolf/packages/trunk/lang/luasec feeds/packages/lang/luasec 134 | 135 | sed -i '/STAMP_BUILT/d' feeds/packages/utils/runc/Makefile 136 | sed -i '/STAMP_BUILT/d' feeds/packages/utils/containerd/Makefile 137 | 138 | sed -i '/upx/d' package/lean/frp/Makefile 139 | sed -i '/upx/d' package/lean/UnblockNeteaseMusicGo/Makefile 140 | sed -i '/upx/d' package/trojan-go/Makefile 141 | 142 | wget -O package/kernel/kmod-sched-cake/Makefile https://raw.githubusercontent.com/coolsnowwolf/lede/master/package/kernel/kmod-sched-cake-oot/Makefile 143 | wget -O feeds/packages/libs/libxml2/Makefile https://raw.githubusercontent.com/coolsnowwolf/packages/master/libs/libxml2/Makefile 144 | 145 | sed -i "/redirect_https/d" package/network/services/uhttpd/files/uhttpd.config 146 | sed -i '/Load Average/i\\t\t<%:CPU Temperature%><%=luci.sys.exec("cut -c1-2 /sys/class/thermal/thermal_zone0/temp")%>' feeds/luci/modules/luci-mod-admin-full/luasrc/view/admin_status/index.htm 147 | sed -i 's/pcdata(boardinfo.system or "?")/"ARMv8"/' feeds/luci/modules/luci-mod-admin-full/luasrc/view/admin_status/index.htm 148 | sed -i 's/services/vpn/g' package/feeds/luci/luci-app-openvpn/luasrc/controller/openvpn.lua 149 | sed -i 's/resolv.conf.d\/resolv.conf.auto/resolv.conf.auto/g' package/lean/luci-app-flowoffload/root/etc/init.d/flowoffload 150 | 151 | sed -i '/done/imkfs.ext4 /dev/mmcblk0p2 && mkdir /mnt/mmcblk0p2 && mount /dev/mmcblk0p2 /mnt/mmcblk0p2 && mkdir /mnt/mmcblk0p2/docker\n' package/base-files/files/root/setup.sh 152 | sed -i "/done/iblock detect | sed \"s/enabled\\\t'0'/enabled\\\t'1'/\" > /etc/config/fstab\n" package/base-files/files/root/setup.sh 153 | sed -i '/done/i[ -f /etc/init.d/dockerman ] && /etc/init.d/dockerman restart\n' package/base-files/files/root/setup.sh 154 | 155 | sed -i 's/\/opt\/docker/\/mnt\/mmcblk0p2\/docker/g' package/luci-app-dockerman/root/etc/config/dockerman 156 | sed -i 's/\/root/\/mnt\/mmcblk0p2/g' package/luci-app-r2sflasher/root/usr/bin/rom_flash 157 | 158 | - name: Patch Kernel 159 | run: | 160 | cd code/kernel/ 161 | #git apply ../../patches/001-add-full_cone_nat.patch 162 | 163 | - name: Custom Configure Files 164 | run: | 165 | rm -f code/friendlywrt/.config* 166 | cp r2s-rk3328-config code/configs/config_rk3328 167 | 168 | - name: Set Default Values 169 | run: | 170 | cd code/friendlywrt 171 | sed -i '/uci commit luci/i\uci set luci.main.mediaurlbase=/luci-static/argon' package/lean/default-settings/files/zzz-default-settings 172 | 173 | - name: Install UPX 174 | run: | 175 | ln -s /usr/bin/upx-ucl code/friendlywrt/staging_dir/host/bin/upx 176 | 177 | - name: Build OpenWrt 178 | run: | 179 | cd code 180 | sed -i 's/set -eu/set -u/' scripts/mk-friendlywrt.sh 181 | ./build.sh nanopi_r2s.mk 182 | 183 | - name: Fix Rootfs Owner and Group 184 | run: | 185 | sudo df -lh 186 | lodev=$(sudo losetup -f) 187 | echo "found unused loop dev $lodev" 188 | sudo losetup -o 100663296 $lodev code/out/*.img 189 | #sudo mkfs -t ext4 $lodev 190 | sudo rm -rf /mnt/friendlywrt-tmp 191 | sudo mkdir -p /mnt/friendlywrt-tmp 192 | sudo mount $lodev /mnt/friendlywrt-tmp 193 | sudo chown -R root:root /mnt/friendlywrt-tmp 194 | sudo umount /mnt/friendlywrt-tmp 195 | sudo losetup -d $lodev 196 | 197 | - name: Assemble Artifact 198 | run: | 199 | rm -rf ./artifact/ 200 | mkdir -p ./artifact/ 201 | 202 | cp code/out/*.img.zip ./artifact/ 203 | cp code/friendlywrt/.config ./artifact/ 204 | 205 | - name: Upload Artifact 206 | uses: actions/upload-artifact@master 207 | with: 208 | name: FriendlyWrt_NanoPi-R2S RK3328 v19.07 209 | path: ./artifact/ 210 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 20210303 2 | * 日常更新 3 | * 修复 trogan-go 无法运行的问题 4 | 5 | ## 20210210 6 | * Docker 7 | 8 | ## 20210208 9 | * 集成 luci-app-dockerman 10 | * 修复 UnblockNeteaseMusicGo 无法启动的问题 11 | 12 | ## 20210206 13 | * 开机挂载 Docker 资料分区 14 | 15 | ## 20210202 16 | * Make OTA Work Again 17 | 18 | ## 20210130 19 | * 更新 netdata 20 | * 修复 OpenClash 依赖问题 21 | 22 | ## 20210124 23 | * 修复 frp 无法启动的问题 24 | 25 | ## 20210121 26 | * 更新到 19.07.5 27 | * Kernel 5.10.2 28 | * 新增 jerrykuku/luci-app-vssr 29 | * 新增 xiaorouji/openwrt-passwall 30 | 31 | ## 20201102 32 | * 常规更新 33 | 34 | ## 20200909 35 | * 更新 SmartDNS 36 | 37 | ## 20200819 38 | * 移除 luci-app-clash 39 | * OpenClash 内置 TUN/Game 二进制文件 40 | 41 | ## 20200817 42 | * Clash 更新到 v1.1.0 43 | 44 | ## 20200815 45 | * 开启 luci-app-adguardhome 46 | * 开启 luci-app-jd-dailybonus 47 | * 替换 node 为 https://github.com/nxhack/openwrt-node-packages/ 48 | 49 | ## 20200809 50 | * 新增 luci-app-oled 51 | 52 | ## 20200723 53 | * Docker 54 | 55 | ## 20200718 56 | * Kernel 5.4.50 57 | * Clash 更新到 1.0.0 58 | 59 | ## 20200515 60 | * Kernel 5.4.40 61 | * 支持 RTL8812AU 62 | 63 | ## 20200509 64 | * 修正 Clash 二进制文件权限 65 | * 修正 luci-app-clash 在 menuconfig 的分类 66 | * 将 luci-app-openvpn 菜单项移至 vpn 67 | 68 | ## 20200508 69 | * 集成 smartdns 70 | * 集成 luci-app-smartdns 71 | * Clash 更新到 0.20.0 72 | 73 | ## 20200418 74 | * 集成 luci-app-r2sflasher 75 | 76 | ## 20200417 77 | * 重新整理 yml 78 | * 因应上游调整 79 | 80 | ## 20200403 81 | * Kernel 5.4.29 82 | 83 | ## 20200329 84 | * 移除 Flow Offloading 补丁 85 | 86 | ## 20200326 87 | * 默认不开启 Full Cone NAT 与 Flow Offloading 88 | 89 | ## 20200311 90 | * 添加 xt_FLOWOFFLOAD 91 | * 添加 RTL8821CU 源代码 92 | 93 | ## 20200308 94 | * 只允许从 LAN 访问 SSH 95 | * 更换 luci-app-unblockmusic 96 | 97 | ## 20200229 98 | * 官方修复每次重启 Mac 地址变化问题 99 | 100 | ## 20200227 101 | * 支持 RTL8821CU 芯片的 USB WiFi 设备,已知支持列表: 102 | - [COMFAST 726B](https://u.jd.com/DOkkhX) 103 | - [COMFAST CF-759BF](https://u.jd.com/C2ivH7) 104 | * 官方 Kernel 更新到 5.4.22 105 | * 更新 argon 主题 106 | * 官方修复 LED 问题 107 | 108 | ## 20200226 109 | * 添加 frpc 和 npc 110 | * 支持 Full Cone NAT 111 | 112 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 使用 Github Actions 在线编译 NanoPi-R2S 固件 2 | 3 | * NanoPi R2S CNC 官方金属壳版 购买链接: [https://s.click.taobao.com/ZPSFRyu](https://s.click.taobao.com/ZPSFRyu) 4 | * 推荐朗科 32G TF 卡 ¥17.90 购买链接: [https://u.jd.com/wDywo8y](https://u.jd.com/wDywo8y) 5 | 6 | ## 说明 7 | * 管理 IP: 192.168.2.1 8 | * 默认管理密码: password 9 | 10 | ## 特色 11 | * 支持 RTL8821CU/RTL8822BU/RTL8812AU 芯片的 USB WiFi 设备,已知支持列表: 12 | - [COMFAST 726B](https://u.jd.com/ISyZWQh) 13 | - [COMFAST CF-759BF](https://u.jd.com/IRyZhYG) 14 | - [COMFAST CF-927BF](https://u.jd.com/I2yv0kA) 15 | * 集成 [OpenClash](https://github.com/vernesong/OpenClash) 及其 core/tun/game binaries 16 | * 集成 [HelloWorld](https://github.com/jerrykuku/luci-app-vssr) 17 | * 集成 [Passwall](https://github.com/xiaorouji/openwrt-passwall) 18 | * 集成 [luci-app-adguardhome](https://github.com/rufengsuixing/luci-app-adguardhome) 19 | * 集成 [coolsnowwolf/packages](https://github.com/coolsnowwolf/packages), [coolsnowwolf/luci](https://github.com/coolsnowwolf/luci) 与 [coolsnowwolf/lede/package/lean](https://github.com/coolsnowwolf/lede/tree/master/package/lean) 20 | * 集成 [luci-theme-argon](https://github.com/jerrykuku/luci-theme-argon) 21 | * 集成 [luci-app-r2sflasher](https://github.com/songchenwen/nanopi-r2s/tree/master/luci-app-r2sflasher) 22 | * 集成 [Smartdns](https://github.com/pymumu/smartdns) 与 luci-app-smartdns 23 | * 集成 [luci-app-oled](https://github.com/NateLol/luci-app-oled) 24 | * 集成 [luci-app-jd-dailybonus](https://github.com/jerrykuku/luci-app-jd-dailybonus) 25 | * 集成 [luci-app-dockerman](https://github.com/lisaac/luci-app-dockerman) 26 | 27 | ## 用法 28 | Fork 到自己的账号下,将 `.github/workflows` 下 `.yml` 文件中的 `runs-on: self-hosted` 改成 `runs-on: ubuntu-latest`(因为我是自己的服务器上编译,更快),编辑文件 `CHANGELOG.md` 触发编译动作。 29 | 30 | ## 注意 31 | 产品发布初期,官方代码每天都在变,遇到无法编译时,请过来查看 `.yml` 与 `config` 最新异动。 32 | 33 | ## 参考 34 | * [使用Github的Actions功能在线编译NanoPi-R1S固件(包含H5和H3)](https://totoro.site/index.php/archives/70/) 35 | * [skytotwo/NanoPi-R1S-Build-By-Actions](https://github.com/skytotwo/NanoPi-R1S-Build-By-Actions) 36 | * [klever1988/nanopi-openwrt](https://github.com/klever1988/nanopi-openwrt) 37 | * [yangliu/NanoPi-R2S](https://github.com/yangliu/NanoPi-R2S) 38 | * [maxming2333/NanoPi-R2S](https://github.com/maxming2333/NanoPi-R2S) 39 | * [songchenwen/nanopi-r2s](https://github.com/songchenwen/nanopi-r2s) 40 | * [fanck0605/nanopi_r2s](https://github.com/fanck0605/nanopi_r2s) 41 | -------------------------------------------------------------------------------- /patches/001-add-full_cone_nat.patch: -------------------------------------------------------------------------------- 1 | diff --git a/arch/arm64/configs/nanopi-r2_linux_defconfig b/arch/arm64/configs/nanopi-r2_linux_defconfig 2 | index 240a9bf57..9f8f37ca7 100644 3 | --- a/arch/arm64/configs/nanopi-r2_linux_defconfig 4 | +++ b/arch/arm64/configs/nanopi-r2_linux_defconfig 5 | @@ -1665,3 +1665,4 @@ CONFIG_SCHEDSTATS=y 6 | CONFIG_DEBUG_SPINLOCK=y 7 | CONFIG_FUNCTION_TRACER=y 8 | CONFIG_BLK_DEV_IO_TRACE=y 9 | +CONFIG_NETFILTER_XT_TARGET_FULLCONENAT=y 10 | diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig 11 | index f17b40211..99f691a67 100644 12 | --- a/net/ipv4/netfilter/Kconfig 13 | +++ b/net/ipv4/netfilter/Kconfig 14 | @@ -239,6 +239,15 @@ config IP_NF_TARGET_NETMAP 15 | (e.g. when running oldconfig). It selects 16 | CONFIG_NETFILTER_XT_TARGET_NETMAP. 17 | 18 | +config IP_NF_TARGET_FULLCONENAT 19 | + tristate "FULLCONENAT target support" 20 | + depends on NETFILTER_ADVANCED 21 | + select NETFILTER_XT_TARGET_FULLCONENAT 22 | + ---help--- 23 | + This is a backwards-compat option for the user's convenience 24 | + (e.g. when running oldconfig). It selects 25 | + CONFIG_NETFILTER_XT_TARGET_FULLCONENAT. 26 | + 27 | config IP_NF_TARGET_REDIRECT 28 | tristate "REDIRECT target support" 29 | depends on NETFILTER_ADVANCED 30 | diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig 31 | index 91efae88e..17f5c748a 100644 32 | --- a/net/netfilter/Kconfig 33 | +++ b/net/netfilter/Kconfig 34 | @@ -956,6 +956,14 @@ config NETFILTER_XT_TARGET_NETMAP 35 | 36 | To compile it as a module, choose M here. If unsure, say N. 37 | 38 | +config NETFILTER_XT_TARGET_FULLCONENAT 39 | + tristate '"FULLCONENAT" target support' 40 | + depends on NF_NAT 41 | + ---help--- 42 | + Full Cone NAT 43 | + 44 | + To compile it as a module, choose M here. If unsure, say N. 45 | + 46 | config NETFILTER_XT_TARGET_NFLOG 47 | tristate '"NFLOG" target support' 48 | default m if NETFILTER_ADVANCED=n 49 | diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile 50 | index 4fc075b61..2b588d5a5 100644 51 | --- a/net/netfilter/Makefile 52 | +++ b/net/netfilter/Makefile 53 | @@ -209,3 +209,6 @@ obj-$(CONFIG_IP_SET) += ipset/ 54 | 55 | # IPVS 56 | obj-$(CONFIG_IP_VS) += ipvs/ 57 | + 58 | +# Full cone NAT 59 | +obj-$(CONFIG_NETFILTER_XT_TARGET_FULLCONENAT) += xt_FULLCONENAT.o 60 | diff --git a/net/netfilter/xt_FULLCONENAT.c b/net/netfilter/xt_FULLCONENAT.c 61 | new file mode 100644 62 | index 000000000..8555b54e2 63 | --- /dev/null 64 | +++ b/net/netfilter/xt_FULLCONENAT.c 65 | @@ -0,0 +1,733 @@ 66 | +/* 67 | + * Copyright (c) 2018 Chion Tang 68 | + * 69 | + * This program is free software; you can redistribute it and/or modify 70 | + * it under the terms of the GNU General Public License version 2 as 71 | + * published by the Free Software Foundation. 72 | + */ 73 | + 74 | +#include 75 | +#include 76 | +#include 77 | +#include 78 | +#include 79 | +#include 80 | +#include 81 | +#include 82 | +#include 83 | +#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS 84 | +#include 85 | +#endif 86 | +#include 87 | +#include 88 | +#include 89 | +#include 90 | +#include 91 | +#include 92 | +#include 93 | +#include 94 | +#include 95 | + 96 | +#define HASH_2(x, y) ((x + y) / 2 * (x + y + 1) + y) 97 | + 98 | +#define HASHTABLE_BUCKET_BITS 10 99 | + 100 | +#ifndef NF_NAT_RANGE_PROTO_RANDOM_FULLY 101 | +#define NF_NAT_RANGE_PROTO_RANDOM_FULLY (1 << 4) 102 | +#endif 103 | + 104 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) 105 | + 106 | +static inline int nf_ct_netns_get(struct net *net, u8 nfproto) { return 0; } 107 | + 108 | +static inline void nf_ct_netns_put(struct net *net, u8 nfproto) {} 109 | + 110 | +static inline struct net_device *xt_in(const struct xt_action_param *par) { 111 | + return par->in; 112 | +} 113 | + 114 | +static inline struct net_device *xt_out(const struct xt_action_param *par) { 115 | + return par->out; 116 | +} 117 | + 118 | +static inline unsigned int xt_hooknum(const struct xt_action_param *par) { 119 | + return par->hooknum; 120 | +} 121 | + 122 | +#endif 123 | + 124 | +struct nat_mapping_original_tuple { 125 | + struct nf_conntrack_tuple tuple; 126 | + 127 | + struct list_head node; 128 | +}; 129 | + 130 | +struct nat_mapping { 131 | + uint16_t port; /* external UDP port */ 132 | + int ifindex; /* external interface index*/ 133 | + 134 | + __be32 int_addr; /* internal source ip address */ 135 | + uint16_t int_port; /* internal source port */ 136 | + 137 | + int refer_count; /* how many references linked to this mapping 138 | + * aka. length of original_tuple_list */ 139 | + 140 | + struct list_head original_tuple_list; 141 | + 142 | + struct hlist_node node_by_ext_port; 143 | + struct hlist_node node_by_int_src; 144 | + 145 | +}; 146 | + 147 | +struct tuple_list { 148 | + struct nf_conntrack_tuple tuple_original; 149 | + struct nf_conntrack_tuple tuple_reply; 150 | + struct list_head list; 151 | +}; 152 | + 153 | +#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS 154 | +struct notifier_block ct_event_notifier; 155 | +#else 156 | +struct nf_ct_event_notifier ct_event_notifier; 157 | +#endif 158 | +int tg_refer_count = 0; 159 | +int ct_event_notifier_registered = 0; 160 | + 161 | +static DEFINE_MUTEX(nf_ct_net_event_lock); 162 | + 163 | +static DEFINE_HASHTABLE(mapping_table_by_ext_port, HASHTABLE_BUCKET_BITS); 164 | +static DEFINE_HASHTABLE(mapping_table_by_int_src, HASHTABLE_BUCKET_BITS); 165 | + 166 | +static DEFINE_SPINLOCK(fullconenat_lock); 167 | + 168 | +static LIST_HEAD(dying_tuple_list); 169 | +static DEFINE_SPINLOCK(dying_tuple_list_lock); 170 | +static void gc_worker(struct work_struct *work); 171 | +static struct workqueue_struct *wq __read_mostly = NULL; 172 | +static DECLARE_DELAYED_WORK(gc_worker_wk, gc_worker); 173 | + 174 | +static char tuple_tmp_string[512]; 175 | +/* non-atomic: can only be called serially within lock zones. */ 176 | +static char* nf_ct_stringify_tuple(const struct nf_conntrack_tuple *t) { 177 | + snprintf(tuple_tmp_string, sizeof(tuple_tmp_string), "%pI4:%hu -> %pI4:%hu", 178 | + &t->src.u3.ip, be16_to_cpu(t->src.u.all), 179 | + &t->dst.u3.ip, be16_to_cpu(t->dst.u.all)); 180 | + return tuple_tmp_string; 181 | +} 182 | + 183 | +static struct nat_mapping* allocate_mapping(const __be32 int_addr, const uint16_t int_port, const uint16_t port, const int ifindex) { 184 | + struct nat_mapping *p_new; 185 | + u32 hash_src; 186 | + 187 | + p_new = kmalloc(sizeof(struct nat_mapping), GFP_ATOMIC); 188 | + if (p_new == NULL) { 189 | + pr_debug("xt_FULLCONENAT: ERROR: kmalloc() for new nat_mapping failed.\n"); 190 | + return NULL; 191 | + } 192 | + p_new->port = port; 193 | + p_new->int_addr = int_addr; 194 | + p_new->int_port = int_port; 195 | + p_new->ifindex = ifindex; 196 | + p_new->refer_count = 0; 197 | + (p_new->original_tuple_list).next = &(p_new->original_tuple_list); 198 | + (p_new->original_tuple_list).prev = &(p_new->original_tuple_list); 199 | + 200 | + hash_src = HASH_2(int_addr, (u32)int_port); 201 | + 202 | + hash_add(mapping_table_by_ext_port, &p_new->node_by_ext_port, port); 203 | + hash_add(mapping_table_by_int_src, &p_new->node_by_int_src, hash_src); 204 | + 205 | + pr_debug("xt_FULLCONENAT: new mapping allocated for %pI4:%d ==> %d\n", 206 | + &p_new->int_addr, p_new->int_port, p_new->port); 207 | + 208 | + return p_new; 209 | +} 210 | + 211 | +static void add_original_tuple_to_mapping(struct nat_mapping *mapping, const struct nf_conntrack_tuple* original_tuple) { 212 | + struct nat_mapping_original_tuple *item = kmalloc(sizeof(struct nat_mapping_original_tuple), GFP_ATOMIC); 213 | + if (item == NULL) { 214 | + pr_debug("xt_FULLCONENAT: ERROR: kmalloc() for nat_mapping_original_tuple failed.\n"); 215 | + return; 216 | + } 217 | + memcpy(&item->tuple, original_tuple, sizeof(struct nf_conntrack_tuple)); 218 | + list_add(&item->node, &mapping->original_tuple_list); 219 | + (mapping->refer_count)++; 220 | +} 221 | + 222 | +static struct nat_mapping* get_mapping_by_ext_port(const uint16_t port, const int ifindex) { 223 | + struct nat_mapping *p_current; 224 | + 225 | + hash_for_each_possible(mapping_table_by_ext_port, p_current, node_by_ext_port, port) { 226 | + if (p_current->port == port && p_current->ifindex == ifindex) { 227 | + return p_current; 228 | + } 229 | + } 230 | + 231 | + return NULL; 232 | +} 233 | + 234 | +static struct nat_mapping* get_mapping_by_int_src(const __be32 src_ip, const uint16_t src_port) { 235 | + struct nat_mapping *p_current; 236 | + u32 hash_src = HASH_2(src_ip, (u32)src_port); 237 | + 238 | + hash_for_each_possible(mapping_table_by_int_src, p_current, node_by_int_src, hash_src) { 239 | + if (p_current->int_addr == src_ip && p_current->int_port == src_port) { 240 | + return p_current; 241 | + } 242 | + } 243 | + 244 | + return NULL; 245 | +} 246 | + 247 | +static void kill_mapping(struct nat_mapping *mapping) { 248 | + struct list_head *iter, *tmp; 249 | + struct nat_mapping_original_tuple *original_tuple_item; 250 | + 251 | + if (mapping == NULL) { 252 | + return; 253 | + } 254 | + 255 | + list_for_each_safe(iter, tmp, &mapping->original_tuple_list) { 256 | + original_tuple_item = list_entry(iter, struct nat_mapping_original_tuple, node); 257 | + list_del(&original_tuple_item->node); 258 | + kfree(original_tuple_item); 259 | + } 260 | + 261 | + hash_del(&mapping->node_by_ext_port); 262 | + hash_del(&mapping->node_by_int_src); 263 | + kfree(mapping); 264 | +} 265 | + 266 | +static void destroy_mappings(void) { 267 | + struct nat_mapping *p_current; 268 | + struct hlist_node *tmp; 269 | + int i; 270 | + 271 | + spin_lock_bh(&fullconenat_lock); 272 | + 273 | + hash_for_each_safe(mapping_table_by_ext_port, i, tmp, p_current, node_by_ext_port) { 274 | + kill_mapping(p_current); 275 | + } 276 | + 277 | + spin_unlock_bh(&fullconenat_lock); 278 | +} 279 | + 280 | +/* check if a mapping is valid. 281 | + * possibly delete and free an invalid mapping. 282 | + * the mapping should not be used anymore after check_mapping() returns 0. */ 283 | +static int check_mapping(struct nat_mapping* mapping, struct net *net, const struct nf_conntrack_zone *zone) { 284 | + struct list_head *iter, *tmp; 285 | + struct nat_mapping_original_tuple *original_tuple_item; 286 | + struct nf_conntrack_tuple_hash *tuple_hash; 287 | + struct nf_conn *ct; 288 | + 289 | + if (mapping == NULL) { 290 | + return 0; 291 | + } 292 | + 293 | + if (mapping->port == 0 || mapping->int_addr == 0 || mapping->int_port == 0 || mapping->ifindex == -1) { 294 | + return 0; 295 | + } 296 | + 297 | + /* for dying/unconfirmed conntrack tuples, an IPCT_DESTROY event may NOT be fired. 298 | + * so we manually kill one of those tuples once we acquire one. */ 299 | + 300 | + list_for_each_safe(iter, tmp, &mapping->original_tuple_list) { 301 | + original_tuple_item = list_entry(iter, struct nat_mapping_original_tuple, node); 302 | + 303 | + tuple_hash = nf_conntrack_find_get(net, zone, &original_tuple_item->tuple); 304 | + 305 | + if (tuple_hash == NULL) { 306 | + pr_debug("xt_FULLCONENAT: check_mapping(): tuple %s dying/unconfirmed. free this tuple.\n", nf_ct_stringify_tuple(&original_tuple_item->tuple)); 307 | + 308 | + list_del(&original_tuple_item->node); 309 | + kfree(original_tuple_item); 310 | + (mapping->refer_count)--; 311 | + } else { 312 | + ct = nf_ct_tuplehash_to_ctrack(tuple_hash); 313 | + if (ct != NULL) 314 | + nf_ct_put(ct); 315 | + } 316 | + 317 | + } 318 | + 319 | + /* kill the mapping if need */ 320 | + pr_debug("xt_FULLCONENAT: check_mapping() refer_count for mapping at ext_port %d is now %d\n", mapping->port, mapping->refer_count); 321 | + if (mapping->refer_count <= 0) { 322 | + pr_debug("xt_FULLCONENAT: check_mapping(): kill dying/unconfirmed mapping at ext port %d\n", mapping->port); 323 | + kill_mapping(mapping); 324 | + return 0; 325 | + } else { 326 | + return 1; 327 | + } 328 | +} 329 | + 330 | +static void handle_dying_tuples(void) { 331 | + struct list_head *iter, *tmp, *iter_2, *tmp_2; 332 | + struct tuple_list *item; 333 | + struct nf_conntrack_tuple *ct_tuple; 334 | + struct nat_mapping *mapping; 335 | + __be32 ip; 336 | + uint16_t port; 337 | + struct nat_mapping_original_tuple *original_tuple_item; 338 | + 339 | + spin_lock_bh(&fullconenat_lock); 340 | + spin_lock_bh(&dying_tuple_list_lock); 341 | + 342 | + list_for_each_safe(iter, tmp, &dying_tuple_list) { 343 | + item = list_entry(iter, struct tuple_list, list); 344 | + 345 | + /* we dont know the conntrack direction for now so we try in both ways. */ 346 | + ct_tuple = &(item->tuple_original); 347 | + ip = (ct_tuple->src).u3.ip; 348 | + port = be16_to_cpu((ct_tuple->src).u.udp.port); 349 | + mapping = get_mapping_by_int_src(ip, port); 350 | + if (mapping == NULL) { 351 | + ct_tuple = &(item->tuple_reply); 352 | + ip = (ct_tuple->src).u3.ip; 353 | + port = be16_to_cpu((ct_tuple->src).u.udp.port); 354 | + mapping = get_mapping_by_int_src(ip, port); 355 | + if (mapping != NULL) { 356 | + pr_debug("xt_FULLCONENAT: handle_dying_tuples(): INBOUND dying conntrack at ext port %d\n", mapping->port); 357 | + } 358 | + } else { 359 | + pr_debug("xt_FULLCONENAT: handle_dying_tuples(): OUTBOUND dying conntrack at ext port %d\n", mapping->port); 360 | + } 361 | + 362 | + if (mapping == NULL) { 363 | + goto next; 364 | + } 365 | + 366 | + /* look for the corresponding out-dated tuple and free it */ 367 | + list_for_each_safe(iter_2, tmp_2, &mapping->original_tuple_list) { 368 | + original_tuple_item = list_entry(iter_2, struct nat_mapping_original_tuple, node); 369 | + 370 | + if (nf_ct_tuple_equal(&original_tuple_item->tuple, &(item->tuple_original))) { 371 | + pr_debug("xt_FULLCONENAT: handle_dying_tuples(): tuple %s expired. free this tuple.\n", 372 | + nf_ct_stringify_tuple(&original_tuple_item->tuple)); 373 | + list_del(&original_tuple_item->node); 374 | + kfree(original_tuple_item); 375 | + (mapping->refer_count)--; 376 | + } 377 | + } 378 | + 379 | + /* then kill the mapping if needed*/ 380 | + pr_debug("xt_FULLCONENAT: handle_dying_tuples(): refer_count for mapping at ext_port %d is now %d\n", mapping->port, mapping->refer_count); 381 | + if (mapping->refer_count <= 0) { 382 | + pr_debug("xt_FULLCONENAT: handle_dying_tuples(): kill expired mapping at ext port %d\n", mapping->port); 383 | + kill_mapping(mapping); 384 | + } 385 | + 386 | +next: 387 | + list_del(&item->list); 388 | + kfree(item); 389 | + } 390 | + 391 | + spin_unlock_bh(&dying_tuple_list_lock); 392 | + spin_unlock_bh(&fullconenat_lock); 393 | +} 394 | + 395 | +static void gc_worker(struct work_struct *work) { 396 | + handle_dying_tuples(); 397 | +} 398 | + 399 | +/* conntrack destroy event callback function */ 400 | +#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS 401 | +static int ct_event_cb(struct notifier_block *this, unsigned long events, void *ptr) { 402 | + struct nf_ct_event *item = ptr; 403 | +#else 404 | +static int ct_event_cb(unsigned int events, struct nf_ct_event *item) { 405 | +#endif 406 | + struct nf_conn *ct; 407 | + struct nf_conntrack_tuple *ct_tuple_reply, *ct_tuple_original; 408 | + uint8_t protonum; 409 | + struct tuple_list *dying_tuple_item; 410 | + 411 | + ct = item->ct; 412 | + /* we handle only conntrack destroy events */ 413 | + if (ct == NULL || !(events & (1 << IPCT_DESTROY))) { 414 | + return 0; 415 | + } 416 | + 417 | + ct_tuple_original = &(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); 418 | + 419 | + ct_tuple_reply = &(ct->tuplehash[IP_CT_DIR_REPLY].tuple); 420 | + 421 | + protonum = (ct_tuple_original->dst).protonum; 422 | + if (protonum != IPPROTO_UDP) { 423 | + return 0; 424 | + } 425 | + 426 | + dying_tuple_item = kmalloc(sizeof(struct tuple_list), GFP_ATOMIC); 427 | + 428 | + if (dying_tuple_item == NULL) { 429 | + pr_debug("xt_FULLCONENAT: warning: ct_event_cb(): kmalloc failed.\n"); 430 | + return 0; 431 | + } 432 | + 433 | + memcpy(&(dying_tuple_item->tuple_original), ct_tuple_original, sizeof(struct nf_conntrack_tuple)); 434 | + memcpy(&(dying_tuple_item->tuple_reply), ct_tuple_reply, sizeof(struct nf_conntrack_tuple)); 435 | + 436 | + spin_lock_bh(&dying_tuple_list_lock); 437 | + 438 | + list_add(&(dying_tuple_item->list), &dying_tuple_list); 439 | + 440 | + spin_unlock_bh(&dying_tuple_list_lock); 441 | + 442 | + if (wq != NULL) 443 | + queue_delayed_work(wq, &gc_worker_wk, msecs_to_jiffies(100)); 444 | + 445 | + return 0; 446 | +} 447 | + 448 | +static __be32 get_device_ip(const struct net_device* dev) { 449 | + struct in_device* in_dev; 450 | + struct in_ifaddr* if_info; 451 | + __be32 result; 452 | + 453 | + if (dev == NULL) { 454 | + return 0; 455 | + } 456 | + 457 | + rcu_read_lock(); 458 | + in_dev = dev->ip_ptr; 459 | + if (in_dev == NULL) { 460 | + rcu_read_unlock(); 461 | + return 0; 462 | + } 463 | + if_info = in_dev->ifa_list; 464 | + if (if_info) { 465 | + result = if_info->ifa_local; 466 | + rcu_read_unlock(); 467 | + return result; 468 | + } else { 469 | + rcu_read_unlock(); 470 | + return 0; 471 | + } 472 | +} 473 | + 474 | +static uint16_t find_appropriate_port(struct net *net, const struct nf_conntrack_zone *zone, const uint16_t original_port, const int ifindex, const struct nf_nat_ipv4_range *range) { 475 | + uint16_t min, start, selected, range_size, i; 476 | + struct nat_mapping* mapping = NULL; 477 | + 478 | + if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) { 479 | + min = be16_to_cpu((range->min).udp.port); 480 | + range_size = be16_to_cpu((range->max).udp.port) - min + 1; 481 | + } else { 482 | + /* minimum port is 1024. same behavior as default linux NAT. */ 483 | + min = 1024; 484 | + range_size = 65535 - min + 1; 485 | + } 486 | + 487 | + if ((range->flags & NF_NAT_RANGE_PROTO_RANDOM) 488 | + || (range->flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY)) { 489 | + /* for now we do the same thing for both --random and --random-fully */ 490 | + 491 | + /* select a random starting point */ 492 | + start = (uint16_t)(prandom_u32() % (u32)range_size); 493 | + } else { 494 | + 495 | + if ((original_port >= min && original_port <= min + range_size - 1) 496 | + || !(range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)) { 497 | + /* 1. try to preserve the port if it's available */ 498 | + mapping = get_mapping_by_ext_port(original_port, ifindex); 499 | + if (mapping == NULL || !(check_mapping(mapping, net, zone))) { 500 | + return original_port; 501 | + } 502 | + } 503 | + 504 | + /* otherwise, we start from zero */ 505 | + start = 0; 506 | + } 507 | + 508 | + for (i = 0; i < range_size; i++) { 509 | + /* 2. try to find an available port */ 510 | + selected = min + ((start + i) % range_size); 511 | + mapping = get_mapping_by_ext_port(selected, ifindex); 512 | + if (mapping == NULL || !(check_mapping(mapping, net, zone))) { 513 | + return selected; 514 | + } 515 | + } 516 | + 517 | + /* 3. at least we tried. override a previous mapping. */ 518 | + selected = min + start; 519 | + mapping = get_mapping_by_ext_port(selected, ifindex); 520 | + kill_mapping(mapping); 521 | + 522 | + return selected; 523 | +} 524 | + 525 | +static unsigned int fullconenat_tg(struct sk_buff *skb, const struct xt_action_param *par) 526 | +{ 527 | + const struct nf_nat_ipv4_multi_range_compat *mr; 528 | + const struct nf_nat_ipv4_range *range; 529 | + 530 | + const struct nf_conntrack_zone *zone; 531 | + struct net *net; 532 | + struct nf_conn *ct; 533 | + enum ip_conntrack_info ctinfo; 534 | + struct nf_conntrack_tuple *ct_tuple, *ct_tuple_origin; 535 | + 536 | + struct net_device *net_dev; 537 | + 538 | + struct nat_mapping *mapping, *src_mapping; 539 | + unsigned int ret; 540 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) 541 | + struct nf_nat_range2 newrange; 542 | +#else 543 | + struct nf_nat_range newrange; 544 | +#endif 545 | + 546 | + __be32 new_ip, ip; 547 | + uint16_t port, original_port, want_port; 548 | + uint8_t protonum; 549 | + int ifindex; 550 | + 551 | + ip = 0; 552 | + original_port = 0; 553 | + src_mapping = NULL; 554 | + 555 | + mr = par->targinfo; 556 | + range = &mr->range[0]; 557 | + 558 | + mapping = NULL; 559 | + ret = XT_CONTINUE; 560 | + 561 | + ct = nf_ct_get(skb, &ctinfo); 562 | + net = nf_ct_net(ct); 563 | + zone = nf_ct_zone(ct); 564 | + 565 | + memset(&newrange.min_addr, 0, sizeof(newrange.min_addr)); 566 | + memset(&newrange.max_addr, 0, sizeof(newrange.max_addr)); 567 | + newrange.flags = mr->range[0].flags | NF_NAT_RANGE_MAP_IPS; 568 | + newrange.min_proto = mr->range[0].min; 569 | + newrange.max_proto = mr->range[0].max; 570 | + 571 | + if (xt_hooknum(par) == NF_INET_PRE_ROUTING) { 572 | + /* inbound packets */ 573 | + ifindex = xt_in(par)->ifindex; 574 | + 575 | + ct_tuple_origin = &(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); 576 | + 577 | + protonum = (ct_tuple_origin->dst).protonum; 578 | + if (protonum != IPPROTO_UDP) { 579 | + return ret; 580 | + } 581 | + ip = (ct_tuple_origin->dst).u3.ip; 582 | + port = be16_to_cpu((ct_tuple_origin->dst).u.udp.port); 583 | + 584 | + /* get the corresponding ifindex by the dst_ip (aka. external ip of this host), 585 | + * in case the packet needs to be forwarded from another inbound interface. */ 586 | + net_dev = ip_dev_find(net, ip); 587 | + if (net_dev != NULL) { 588 | + ifindex = net_dev->ifindex; 589 | + dev_put(net_dev); 590 | + } 591 | + 592 | + spin_lock_bh(&fullconenat_lock); 593 | + 594 | + /* find an active mapping based on the inbound port */ 595 | + mapping = get_mapping_by_ext_port(port, ifindex); 596 | + if (mapping == NULL) { 597 | + spin_unlock_bh(&fullconenat_lock); 598 | + return ret; 599 | + } 600 | + if (check_mapping(mapping, net, zone)) { 601 | + newrange.flags = NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED; 602 | + newrange.min_addr.ip = mapping->int_addr; 603 | + newrange.max_addr.ip = mapping->int_addr; 604 | + newrange.min_proto.udp.port = cpu_to_be16(mapping->int_port); 605 | + newrange.max_proto = newrange.min_proto; 606 | + 607 | + pr_debug("xt_FULLCONENAT: %s ==> %pI4:%d\n", nf_ct_stringify_tuple(ct_tuple_origin), &mapping->int_addr, mapping->int_port); 608 | + 609 | + ret = nf_nat_setup_info(ct, &newrange, HOOK2MANIP(xt_hooknum(par))); 610 | + 611 | + if (ret == NF_ACCEPT) { 612 | + add_original_tuple_to_mapping(mapping, ct_tuple_origin); 613 | + pr_debug("xt_FULLCONENAT: fullconenat_tg(): INBOUND: refer_count for mapping at ext_port %d is now %d\n", mapping->port, mapping->refer_count); 614 | + } 615 | + } 616 | + spin_unlock_bh(&fullconenat_lock); 617 | + return ret; 618 | + 619 | + 620 | + } else if (xt_hooknum(par) == NF_INET_POST_ROUTING) { 621 | + /* outbound packets */ 622 | + ifindex = xt_out(par)->ifindex; 623 | + 624 | + ct_tuple_origin = &(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); 625 | + protonum = (ct_tuple_origin->dst).protonum; 626 | + 627 | + spin_lock_bh(&fullconenat_lock); 628 | + 629 | + if (protonum == IPPROTO_UDP) { 630 | + ip = (ct_tuple_origin->src).u3.ip; 631 | + original_port = be16_to_cpu((ct_tuple_origin->src).u.udp.port); 632 | + 633 | + src_mapping = get_mapping_by_int_src(ip, original_port); 634 | + if (src_mapping != NULL && check_mapping(src_mapping, net, zone)) { 635 | + 636 | + /* outbound nat: if a previously established mapping is active, 637 | + * we will reuse that mapping. */ 638 | + 639 | + newrange.flags = NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED; 640 | + newrange.min_proto.udp.port = cpu_to_be16(src_mapping->port); 641 | + newrange.max_proto = newrange.min_proto; 642 | + 643 | + } else { 644 | + 645 | + /* if not, we find a new external port to map to. 646 | + * the SNAT may fail so we should re-check the mapped port later. */ 647 | + want_port = find_appropriate_port(net, zone, original_port, ifindex, range); 648 | + 649 | + newrange.flags = NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED; 650 | + newrange.min_proto.udp.port = cpu_to_be16(want_port); 651 | + newrange.max_proto = newrange.min_proto; 652 | + 653 | + src_mapping = NULL; 654 | + 655 | + } 656 | + } 657 | + 658 | + if(mr->range[0].flags & NF_NAT_RANGE_MAP_IPS) { 659 | + newrange.min_addr.ip = mr->range[0].min_ip; 660 | + newrange.max_addr.ip = mr->range[0].max_ip; 661 | + } else { 662 | + new_ip = get_device_ip(skb->dev); 663 | + newrange.min_addr.ip = new_ip; 664 | + newrange.max_addr.ip = new_ip; 665 | + } 666 | + 667 | + /* do SNAT now */ 668 | + ret = nf_nat_setup_info(ct, &newrange, HOOK2MANIP(xt_hooknum(par))); 669 | + 670 | + if (protonum != IPPROTO_UDP || ret != NF_ACCEPT) { 671 | + /* for non-UDP packets and failed SNAT, bailout */ 672 | + spin_unlock_bh(&fullconenat_lock); 673 | + return ret; 674 | + } 675 | + 676 | + /* the reply tuple contains the mapped port. */ 677 | + ct_tuple = &(ct->tuplehash[IP_CT_DIR_REPLY].tuple); 678 | + /* this is the resulted mapped port. */ 679 | + port = be16_to_cpu((ct_tuple->dst).u.udp.port); 680 | + 681 | + pr_debug("xt_FULLCONENAT: %s ==> %d\n", nf_ct_stringify_tuple(ct_tuple_origin), port); 682 | + 683 | + /* save the mapping information into our mapping table */ 684 | + mapping = src_mapping; 685 | + if (mapping == NULL || !check_mapping(mapping, net, zone)) { 686 | + mapping = allocate_mapping(ip, original_port, port, ifindex); 687 | + } 688 | + if (mapping != NULL) { 689 | + add_original_tuple_to_mapping(mapping, ct_tuple_origin); 690 | + pr_debug("xt_FULLCONENAT: fullconenat_tg(): OUTBOUND: refer_count for mapping at ext_port %d is now %d\n", mapping->port, mapping->refer_count); 691 | + } 692 | + 693 | + spin_unlock_bh(&fullconenat_lock); 694 | + return ret; 695 | + } 696 | + 697 | + return ret; 698 | +} 699 | + 700 | +static int fullconenat_tg_check(const struct xt_tgchk_param *par) 701 | +{ 702 | + mutex_lock(&nf_ct_net_event_lock); 703 | + 704 | + tg_refer_count++; 705 | + 706 | + pr_debug("xt_FULLCONENAT: fullconenat_tg_check(): tg_refer_count is now %d\n", tg_refer_count); 707 | + 708 | + if (tg_refer_count == 1) { 709 | + nf_ct_netns_get(par->net, par->family); 710 | +#ifdef CONFIG_NF_CONNTRACK_CHAIN_EVENTS 711 | + ct_event_notifier.notifier_call = ct_event_cb; 712 | +#else 713 | + ct_event_notifier.fcn = ct_event_cb; 714 | +#endif 715 | + 716 | + if (nf_conntrack_register_notifier(par->net, &ct_event_notifier) == 0) { 717 | + ct_event_notifier_registered = 1; 718 | + pr_debug("xt_FULLCONENAT: fullconenat_tg_check(): ct_event_notifier registered\n"); 719 | + } else { 720 | + printk("xt_FULLCONENAT: warning: failed to register a conntrack notifier. Disable active GC for mappings.\n"); 721 | + } 722 | + 723 | + } 724 | + 725 | + mutex_unlock(&nf_ct_net_event_lock); 726 | + 727 | + return 0; 728 | +} 729 | + 730 | +static void fullconenat_tg_destroy(const struct xt_tgdtor_param *par) 731 | +{ 732 | + mutex_lock(&nf_ct_net_event_lock); 733 | + 734 | + tg_refer_count--; 735 | + 736 | + pr_debug("xt_FULLCONENAT: fullconenat_tg_destroy(): tg_refer_count is now %d\n", tg_refer_count); 737 | + 738 | + if (tg_refer_count == 0) { 739 | + if (ct_event_notifier_registered) { 740 | + nf_conntrack_unregister_notifier(par->net, &ct_event_notifier); 741 | + ct_event_notifier_registered = 0; 742 | + 743 | + pr_debug("xt_FULLCONENAT: fullconenat_tg_destroy(): ct_event_notifier unregistered\n"); 744 | + 745 | + } 746 | + nf_ct_netns_put(par->net, par->family); 747 | + } 748 | + 749 | + mutex_unlock(&nf_ct_net_event_lock); 750 | +} 751 | + 752 | +static struct xt_target tg_reg[] __read_mostly = { 753 | + { 754 | + .name = "FULLCONENAT", 755 | + .family = NFPROTO_IPV4, 756 | + .revision = 0, 757 | + .target = fullconenat_tg, 758 | + .targetsize = sizeof(struct nf_nat_ipv4_multi_range_compat), 759 | + .table = "nat", 760 | + .hooks = (1 << NF_INET_PRE_ROUTING) | 761 | + (1 << NF_INET_POST_ROUTING), 762 | + .checkentry = fullconenat_tg_check, 763 | + .destroy = fullconenat_tg_destroy, 764 | + .me = THIS_MODULE, 765 | + }, 766 | +}; 767 | + 768 | +static int __init fullconenat_tg_init(void) 769 | +{ 770 | + wq = create_singlethread_workqueue("xt_FULLCONENAT"); 771 | + if (wq == NULL) { 772 | + printk("xt_FULLCONENAT: warning: failed to create workqueue\n"); 773 | + } 774 | + 775 | + return xt_register_targets(tg_reg, ARRAY_SIZE(tg_reg)); 776 | +} 777 | + 778 | +static void fullconenat_tg_exit(void) 779 | +{ 780 | + xt_unregister_targets(tg_reg, ARRAY_SIZE(tg_reg)); 781 | + 782 | + if (wq) { 783 | + cancel_delayed_work_sync(&gc_worker_wk); 784 | + flush_workqueue(wq); 785 | + destroy_workqueue(wq); 786 | + } 787 | + 788 | + handle_dying_tuples(); 789 | + destroy_mappings(); 790 | +} 791 | + 792 | +module_init(fullconenat_tg_init); 793 | +module_exit(fullconenat_tg_exit); 794 | + 795 | +MODULE_LICENSE("GPL"); 796 | +MODULE_DESCRIPTION("Xtables: implementation of RFC3489 full cone NAT"); 797 | +MODULE_AUTHOR("Chion Tang "); 798 | +MODULE_ALIAS("ipt_FULLCONENAT"); 799 | -------------------------------------------------------------------------------- /patches/011-fix-zerotier-aes.patch: -------------------------------------------------------------------------------- 1 | --- a/make-linux.mk 2 | +++ b/make-linux.mk 3 | @@ -216,11 +216,11 @@ ifeq ($(CC_MACH),armv7ve) 4 | endif 5 | ifeq ($(CC_MACH),arm64) 6 | ZT_ARCHITECTURE=4 7 | - override DEFS+=-DZT_NO_TYPE_PUNNING -DZT_ARCH_ARM_HAS_NEON -march=armv8-a+aes+crypto -mtune=generic -mstrict-align 8 | + override DEFS+=-DZT_NO_TYPE_PUNNING -DZT_ARCH_ARM_HAS_NEON -march=armv8-a+crypto -mtune=generic -mstrict-align 9 | endif 10 | ifeq ($(CC_MACH),aarch64) 11 | ZT_ARCHITECTURE=4 12 | - override DEFS+=-DZT_NO_TYPE_PUNNING -DZT_ARCH_ARM_HAS_NEON -march=armv8-a+aes+crypto -mtune=generic -mstrict-align 13 | + override DEFS+=-DZT_NO_TYPE_PUNNING -DZT_ARCH_ARM_HAS_NEON -march=armv8-a+crypto -mtune=generic -mstrict-align 14 | endif 15 | ifeq ($(CC_MACH),mipsel) 16 | ZT_ARCHITECTURE=5 17 | -------------------------------------------------------------------------------- /rk3328.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | --------------------------------------------------------------------------------