From 471ffdd98c3a543e4935c3e76ac19301ac16c52d Mon Sep 17 00:00:00 2001 From: Stefan Brusch Date: Tue, 15 Nov 2022 16:26:38 +0100 Subject: [PATCH] bcp38: migrate to nftables Signed-off-by: Stefan Brusch --- net/bcp38/Makefile | 5 +- net/bcp38/files/bcp38.defaults | 2 - net/bcp38/files/run.sh | 94 ++++++++++++++++++---------------- 3 files changed, 53 insertions(+), 48 deletions(-) diff --git a/net/bcp38/Makefile b/net/bcp38/Makefile index c5ca841914..694b562791 100644 --- a/net/bcp38/Makefile +++ b/net/bcp38/Makefile @@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=bcp38 PKG_VERSION:=5 -PKG_RELEASE:=6 +PKG_RELEASE:=$(AUTORELEASE) PKG_LICENCE:=GPL-3.0-or-later include $(INCLUDE_DIR)/package.mk @@ -19,7 +19,6 @@ define Package/bcp38 TITLE:=BCP38 compliance URL:=https://github.com/dtaht/ceropackages-3.10 MAINTAINER:=Toke Høiland-Jørgensen - DEPENDS:=+ipset PKGARCH:=all endef @@ -27,7 +26,7 @@ define Package/bcp38/description bcp38 implements IETF BCP38 for home routers. See https://tools.ietf.org/html/bcp38. - This package provides BCP38 for IPv4 only - IPv6 uses source + This package provides BCP38 for IPv4 only - IPv6 uses source specific default routes, so no firewall configuration is needed. endef diff --git a/net/bcp38/files/bcp38.defaults b/net/bcp38/files/bcp38.defaults index d7e0d80635..f757fccd10 100644 --- a/net/bcp38/files/bcp38.defaults +++ b/net/bcp38/files/bcp38.defaults @@ -5,8 +5,6 @@ uci -q batch <<-EOT set firewall.bcp38=include set firewall.bcp38.type=script set firewall.bcp38.path=/usr/lib/bcp38/run.sh - set firewall.bcp38.family=IPv4 - set firewall.bcp38.reload=1 commit firewall EOT diff --git a/net/bcp38/files/run.sh b/net/bcp38/files/run.sh index 736ea52c63..addabe9d83 100755 --- a/net/bcp38/files/run.sh +++ b/net/bcp38/files/run.sh @@ -9,8 +9,12 @@ # Author: Toke Høiland-Jørgensen STOP=$1 -IPSET_NAME=bcp38-ipv4 -IPTABLES_CHAIN=BCP38 + +TABLE=bcp38 +FAMILY=ip +MATCHSET=bcp38-match +NOMATCHSET=bcp38-nomatch +CHAIN=bcp38 . /lib/functions.sh @@ -21,84 +25,88 @@ add_bcp38_rule() local subnet="$1" local action="$2" - if [ "$action" == "nomatch" ]; then - ipset add "$IPSET_NAME" "$subnet" nomatch - else - ipset add "$IPSET_NAME" "$subnet" - fi + setname="$MATCHSET" + [ "$action" == "nomatch" ] && setname="$NOMATCHSET" + nft add element "$FAMILY" "$TABLE" "$setname" { "$subnet" } } -detect_upstream() +detect_upstream_subnet() { local interface="$1" subnets=$(ip route show dev "$interface" | grep 'scope link' | awk '{print $1}') for subnet in $subnets; do - # ipset test doesn't work for subnets, so strip out the subnet part - # and test for that; add as exception if there's a match - addr=$(echo $subnet | sed 's|/[0-9]\+$||') - ipset test "$IPSET_NAME" $addr 2>/dev/null && add_bcp38_rule $subnet nomatch + #test for that; add as exception if there's a match + nft get element "$FAMILY" "$TABLE" "$MATCHSET" { $subnet } >/dev/null 2>/dev/null && add_bcp38_rule $subnet nomatch done } run() { - local section="$1" - local enabled + local section="$1" + local enabled local interface + local priority local detect_upstream config_get_bool enabled "$section" enabled 0 config_get interface "$section" interface config_get detect_upstream "$section" detect_upstream + config_get priority "$section" priority "2" if [ "$enabled" -eq "1" -a -n "$interface" -a -z "$STOP" ] ; then - setup_ipset - setup_iptables "$interface" + setup_table + setup_sets + setup_chains "$interface" "$priority" config_list_foreach "$section" match add_bcp38_rule match config_list_foreach "$section" nomatch add_bcp38_rule nomatch - [ "$detect_upstream" -eq "1" ] && detect_upstream "$interface" + [ "$detect_upstream" -eq "1" ] && detect_upstream_subnet "$interface" fi exit 0 } -setup_ipset() +setup_table() +{ + nft add table "$FAMILY" "$TABLE" +} + +setup_sets() { - ipset create "$IPSET_NAME" hash:net family ipv4 - ipset flush "$IPSET_NAME" + #create and flush sets + nft add set "$FAMILY" "$TABLE" "$MATCHSET" '{ type ipv4_addr; flags interval; }' + nft flush set "$FAMILY" "$TABLE" "$MATCHSET" + nft add set "$FAMILY" "$TABLE" "$NOMATCHSET" '{ type ipv4_addr; flags interval; }' + nft flush set "$FAMILY" "$TABLE" "$NOMATCHSET" } -setup_iptables() +setup_chains() { local interface="$1" - iptables -N "$IPTABLES_CHAIN" 2>/dev/null - iptables -F "$IPTABLES_CHAIN" 2>/dev/null + local priority="$2" - iptables -I output_rule -m conntrack --ctstate NEW -j "$IPTABLES_CHAIN" - iptables -I input_rule -m conntrack --ctstate NEW -j "$IPTABLES_CHAIN" - iptables -I forwarding_rule -m conntrack --ctstate NEW -j "$IPTABLES_CHAIN" + nft add chain "$FAMILY" "$TABLE" "$CHAIN" 2>/dev/null + nft flush chain "$FAMILY" "$TABLE" "$CHAIN" 2>/dev/null - # always accept DHCP traffic - iptables -A "$IPTABLES_CHAIN" -p udp --dport 67:68 --sport 67:68 -j RETURN - iptables -A "$IPTABLES_CHAIN" -o "$interface" -m set --match-set "$IPSET_NAME" dst -j REJECT --reject-with icmp-net-unreachable - iptables -A "$IPTABLES_CHAIN" -i "$interface" -m set --match-set "$IPSET_NAME" src -j DROP -} + nft add rule "$FAMILY" "$TABLE" "$CHAIN" udp dport {67,68} udp sport {67,68} counter return comment \"always accept DHCP traffic\" + nft add rule "$FAMILY" "$TABLE" "$CHAIN" oifname $interface ip daddr @"$MATCHSET" ip daddr != @"$NOMATCHSET" counter reject with icmp type host-unreachable + nft add rule "$FAMILY" "$TABLE" "$CHAIN" iifname $interface ip saddr @"$MATCHSET" ip saddr != @"$NOMATCHSET" counter drop -destroy_ipset() -{ - ipset flush "$IPSET_NAME" 2>/dev/null - ipset destroy "$IPSET_NAME" 2>/dev/null + nft add chain "$FAMILY" "$TABLE" input "{ type filter hook input priority $priority; policy accept; comment \"bcp38 filter\"; }" + nft add chain "$FAMILY" "$TABLE" forward "{ type filter hook forward priority $priority; policy accept; comment \"bcp38 filter\"; }" + nft add chain "$FAMILY" "$TABLE" output "{ type filter hook output priority $priority; policy accept; comment \"bcp38 filter\"; }" + + nft insert rule "$FAMILY" "$TABLE" input ct state new jump "$CHAIN" + nft insert rule "$FAMILY" "$TABLE" forward ct state new jump "$CHAIN" + nft insert rule "$FAMILY" "$TABLE" output ct state new jump "$CHAIN" } -destroy_iptables() +destroy_table() { - iptables -D output_rule -m conntrack --ctstate NEW -j "$IPTABLES_CHAIN" 2>/dev/null - iptables -D input_rule -m conntrack --ctstate NEW -j "$IPTABLES_CHAIN" 2>/dev/null - iptables -D forwarding_rule -m conntrack --ctstate NEW -j "$IPTABLES_CHAIN" 2>/dev/null - iptables -F "$IPTABLES_CHAIN" 2>/dev/null - iptables -X "$IPTABLES_CHAIN" 2>/dev/null + if [ "$TABLE" != "fw4" ]; then + #as of kernel 3.18 we can delete a table without need to flush it + nft delete table "$FAMILY" "$TABLE" 2>/dev/null + fi } -destroy_iptables -destroy_ipset +destroy_table config_foreach run bcp38 exit 0 -- 2.30.2