From fd30ce0fc565a08a3091c983f6de018a42d8c344 Mon Sep 17 00:00:00 2001 From: Dave Taht Date: Sat, 29 Mar 2014 17:59:29 +0000 Subject: [PATCH] packages: BCP38 support for openwrt This adds firewall support for blocking common invalid address ranges, using ipset. --- bcp38/Makefile | 63 +++++++++++++++++++++++++ bcp38/files/bcp38.config | 22 +++++++++ bcp38/files/bcp38.defaults | 13 ++++++ bcp38/files/run.sh | 96 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 194 insertions(+) create mode 100644 bcp38/Makefile create mode 100644 bcp38/files/bcp38.config create mode 100644 bcp38/files/bcp38.defaults create mode 100755 bcp38/files/run.sh diff --git a/bcp38/Makefile b/bcp38/Makefile new file mode 100644 index 0000000..d777e13 --- /dev/null +++ b/bcp38/Makefile @@ -0,0 +1,63 @@ +# +# Copyright (C) 2014 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# Please note this is not an officially released version of bcp38 + +include $(TOPDIR)/rules.mk + +PKG_NAME:=bcp38 +PKG_VERSION:=4 +PKG_RELEASE:=1 + +include $(INCLUDE_DIR)/package.mk + +define Package/bcp38 + SECTION:=net + CATEGORY:=Network + SUBMENU:=Routing and Redirection + TITLE:=BCP38 compliance + URL:=http://www.github.com/dtaht/bcp38 + MAINTAINER:=Dave Taht + DEPENDS:=+ipset +endef + +define Package/bcp38/description + bcp38 implements rfc bcp 38 for home routers. +endef + +define Package/bcp38/conffiles +/etc/config/bcp38 +endef + +define Build/Prepare +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/bcp38/install + $(INSTALL_DIR) $(1)/etc/config + $(INSTALL_CONF) ./files/bcp38.config $(1)/etc/config/bcp38 + $(INSTALL_DIR) $(1)/usr/lib/bcp38 + $(INSTALL_BIN) ./files/run.sh $(1)/usr/lib/bcp38/run.sh + $(INSTALL_DIR) $(1)/etc/uci-defaults + $(INSTALL_BIN) ./files/bcp38.defaults $(1)/etc/uci-defaults/bcp38 +endef + +define Package/bcp38/postinst +#!/bin/sh +[ -x /etc/uci-defaults/bcp38 ] && /etc/uci-defaults/bcp38 || exit 0 +endef + +define Package/bcp38/postrm +#!/bin/sh +uci delete firewall.bcp38 +uci commit +endef + +$(eval $(call BuildPackage,bcp38)) diff --git a/bcp38/files/bcp38.config b/bcp38/files/bcp38.config new file mode 100644 index 0000000..80431e5 --- /dev/null +++ b/bcp38/files/bcp38.config @@ -0,0 +1,22 @@ +config bcp38 + option enabled 1 + option interface 'ge00' + option detect_upstream 1 + list match '127.0.0.0/8' + list match '0.0.0.0/8' # RFC 1700 + list match '240.0.0.0/4' # RFC 5745 + list match '192.0.2.0/24' # RFC 5737 + list match '198.51.100.0/24' # RFC 5737 + list match '203.0.113.0/24' # RFC 5737 + list match '192.168.0.0/16' # RFC 1918 + list match '10.0.0.0/8' # RFC 1918 + list match '172.16.0.0/12' # RFC 1918 + list match '169.254.0.0/16' # RFC 3927 + +# list nomatch '172.26.0.0/21' # Example of something not to match +# There is a dhcp trigger to do this for the netmask of a +# double natted connection needed + +# I will argue that this level of indirection doesn't scale +# very well - see how to block china as an example +# http://www.okean.com/china.txt diff --git a/bcp38/files/bcp38.defaults b/bcp38/files/bcp38.defaults new file mode 100644 index 0000000..d7e0d80 --- /dev/null +++ b/bcp38/files/bcp38.defaults @@ -0,0 +1,13 @@ +#!/bin/sh + +uci -q batch <<-EOT + delete firewall.bcp38 + 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 + +exit 0 diff --git a/bcp38/files/run.sh b/bcp38/files/run.sh new file mode 100755 index 0000000..33ec531 --- /dev/null +++ b/bcp38/files/run.sh @@ -0,0 +1,96 @@ +#!/bin/sh + +STOP=$1 +IPSET_NAME=bcp38-ipv4 +IPTABLES_CHAIN=BCP38 + +. /lib/functions.sh + +config_load bcp38 + +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 +} + +detect_upstream() +{ + 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 + done +} + +run() { + local section="$1" + local enabled + local interface + local detect_upstream + config_get_bool enabled "$section" enabled 0 + config_get interface "$section" interface + config_get detect_upstream "$section" detect_upstream + + if [ "$enabled" -eq "1" -a -n "$interface" -a -z "$STOP" ] ; then + setup_ipset + setup_iptables "$interface" + 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" + fi + exit 0 +} + +setup_ipset() +{ + ipset create "$IPSET_NAME" hash:net family ipv4 + ipset flush "$IPSET_NAME" +} + +setup_iptables() +{ + local interface="$1" + iptables -N "$IPTABLES_CHAIN" 2>/dev/null + iptables -F "$IPTABLES_CHAIN" 2>/dev/null + + iptables -I output_rule -j "$IPTABLES_CHAIN" + iptables -I input_rule -j "$IPTABLES_CHAIN" + iptables -I forwarding_rule -j "$IPTABLES_CHAIN" + + # 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 +} + +destroy_ipset() +{ + ipset flush "$IPSET_NAME" 2>/dev/null + ipset destroy "$IPSET_NAME" 2>/dev/null +} + +destroy_iptables() +{ + iptables -D output_rule -j "$IPTABLES_CHAIN" 2>/dev/null + iptables -D input_rule -j "$IPTABLES_CHAIN" 2>/dev/null + iptables -D forwarding_rule -j "$IPTABLES_CHAIN" 2>/dev/null + iptables -F "$IPTABLES_CHAIN" 2>/dev/null + iptables -X "$IPTABLES_CHAIN" 2>/dev/null +} + +destroy_iptables +destroy_ipset +config_foreach run bcp38 + +exit 0