diff options
| -rw-r--r-- | package/firewall/Makefile | 2 | ||||
| -rw-r--r-- | package/firewall/files/firewall.config | 61 | ||||
| -rw-r--r-- | package/firewall/files/lib/core_redirect.sh | 56 | ||||
| -rw-r--r-- | package/firewall/files/lib/core_rule.sh | 63 | ||||
| -rw-r--r-- | package/firewall/files/lib/fw.sh | 111 | ||||
| -rw-r--r-- | package/firewall/files/reflection.hotplug | 1 | 
6 files changed, 220 insertions, 74 deletions
diff --git a/package/firewall/Makefile b/package/firewall/Makefile index f8510f182..ff62309d3 100644 --- a/package/firewall/Makefile +++ b/package/firewall/Makefile @@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk  PKG_NAME:=firewall  PKG_VERSION:=2 -PKG_RELEASE:=26 +PKG_RELEASE:=27  include $(INCLUDE_DIR)/package.mk diff --git a/package/firewall/files/firewall.config b/package/firewall/files/firewall.config index c852f4b00..c7bc79825 100644 --- a/package/firewall/files/firewall.config +++ b/package/firewall/files/firewall.config @@ -8,23 +8,23 @@ config defaults  config zone  	option name		lan -	option network	'lan' -	option input	ACCEPT  -	option output	ACCEPT  -	option forward	REJECT +	option network		'lan' +	option input		ACCEPT  +	option output		ACCEPT  +	option forward		REJECT  config zone  	option name		wan -	option network	'wan' -	option input	REJECT -	option output	ACCEPT  -	option forward	REJECT +	option network		'wan' +	option input		REJECT +	option output		ACCEPT  +	option forward		REJECT  	option masq		1  -	option mtu_fix	1 +	option mtu_fix		1  config forwarding  -	option src      lan -	option dest     wan +	option src      	lan +	option dest     	wan  # We need to accept udp packets on port 68,  # see https://dev.openwrt.org/ticket/4108 @@ -33,14 +33,41 @@ config rule  	option proto		udp  	option dest_port	68  	option target		ACCEPT -	option family	ipv4 +	option family		ipv4 + +# Allow IPv4 ping +config rule +	option src		wan +	option proto		icmp +	option icmp_type	echo-request +	option family		ipv4 +	option target		ACCEPT + +# Allow essential incoming IPv6 ICMP traffic +config rule                                    +	option src		wan +	option dest		* +	option proto		icmp +	list icmp_type		router-solicitation +	list icmp_type		router-advertisement +	list icmp_type		neighbour-solicitation +	list icmp_type		neighbour-advertisement +	list icmp_type		echo-request +	list icmp_type		destination-unreachable +	list icmp_type		packet-too-big +	list icmp_type		time-exceeded +	option limit		1000/sec +	option family		ipv6 +	option target		ACCEPT -#Allow ping +# Drop leaking router advertisements on WAN  config rule -	option src wan -	option proto icmp -	option icmp_type echo-request -	option target ACCEPT +	option src		* +	option dest		wan +	option proto		icmp +	option icmp_type	router-advertisement +	option family		ipv6 +	option target		DROP  # include a file with users custom iptables rules  config include diff --git a/package/firewall/files/lib/core_redirect.sh b/package/firewall/files/lib/core_redirect.sh index 64c619e43..f511d2915 100644 --- a/package/firewall/files/lib/core_redirect.sh +++ b/package/firewall/files/lib/core_redirect.sh @@ -13,11 +13,11 @@ fw_config_get_redirect() {  		string src_dport "" \  		string dest "" \  		ipaddr dest_ip "" \ -		string dest_mac "" \  		string dest_port "" \  		string proto "tcpudp" \  		string family "" \  		string target "DNAT" \ +		string extra "" \  	} || return  	[ -n "$redirect_name" ] || redirect_name=$redirect__name  } @@ -29,26 +29,27 @@ fw_load_redirect() {  	local fwdchain natchain natopt nataddr natports srcdaddr srcdports  	if [ "$redirect_target" == "DNAT" ]; then -		[ -n "$redirect_src" -a -n "$redirect_dest_ip$redirect_dest_port" ] || { +		[ -n "${redirect_src#*}" -a -n "$redirect_dest_ip$redirect_dest_port" ] || {  			fw_log error "DNAT redirect ${redirect_name}: needs src and dest_ip or dest_port, skipping"  			return 0  		} -		fwdchain="zone_${redirect_src}${redirect_dest_ip:+_forward}" +		fwdchain="zone_${redirect_src}_forward"  		natopt="--to-destination"  		natchain="zone_${redirect_src}_prerouting"  		nataddr="$redirect_dest_ip" -		fw_get_port_range natports "$redirect_dest_port" "-" +		fw_get_port_range natports "${redirect_dest_port#!}" "-"  		fw_get_negation srcdaddr '-d' "${redirect_src_dip:+$redirect_src_dip/$redirect_src_dip_prefixlen}"  		fw_get_port_range srcdports "$redirect_src_dport" ":" +		fw_get_negation srcdports '--dport' "$srcdports"  		list_contains FW_CONNTRACK_ZONES $redirect_src || \  			append FW_CONNTRACK_ZONES $redirect_src  	elif [ "$redirect_target" == "SNAT" ]; then -		[ -n "$redirect_dest" -a -n "$redirect_src_dip" ] || { +		[ -n "${redirect_dest#*}" -a -n "$redirect_src_dip" ] || {  			fw_log error "SNAT redirect ${redirect_name}: needs dest and src_dip, skipping"  			return 0  		} @@ -58,10 +59,11 @@ fw_load_redirect() {  		natopt="--to-source"  		natchain="zone_${redirect_dest}_nat"  		nataddr="$redirect_src_dip" -		fw_get_port_range natports "$redirect_src_dport" "-" +		fw_get_port_range natports "${redirect_src_dport#!}" "-"  		fw_get_negation srcdaddr '-d' "${redirect_dest_ip:+$redirect_dest_ip/$redirect_dest_ip_prefixlen}"  		fw_get_port_range srcdports "$redirect_dest_port" ":" +		fw_get_negation srcdports '--dport' "$srcdports"  		list_contains FW_CONNTRACK_ZONES $redirect_dest || \  			append FW_CONNTRACK_ZONES $redirect_dest @@ -79,34 +81,38 @@ fw_load_redirect() {  	local srcports  	fw_get_port_range srcports "$redirect_src_port" ":" +	fw_get_negation srcports '--sport' "$srcports"  	local destaddr  	fw_get_negation destaddr '-d' "${redirect_dest_ip:+$redirect_dest_ip/$redirect_dest_ip_prefixlen}"  	local destports  	fw_get_port_range destports "${redirect_dest_port:-$redirect_src_dport}" ":" +	fw_get_negation destports '--dport' "$destports"  	[ "$redirect_proto" == "tcpudp" ] && redirect_proto="tcp udp"  	for redirect_proto in $redirect_proto; do -		local pos -		eval 'pos=$((++FW__REDIR_COUNT_'${mode#G}'_'$natchain'))' - -		fw add $mode n $natchain $redirect_target $pos { $redirect_src_ip $redirect_dest_ip } { \ -			$srcaddr $srcdaddr \ -			${redirect_proto:+-p $redirect_proto} \ -			${srcports:+--sport $srcports} \ -			${srcdports:+--dport $srcdports} \ -			${redirect_src_mac:+-m mac --mac-source $redirect_src_mac} \ -			$natopt $nataddr${natports:+:$natports} \ -		} - -		fw add $mode f ${fwdchain:-forward} ACCEPT ^ { $redirect_src_ip $redirect_dest_ip } { \ -			$srcaddr ${destaddr:--m conntrack --ctstate DNAT} \ -			${redirect_proto:+-p $redirect_proto} \ -			${srcports:+--sport $srcports} \ -			${destports:+--dport $destports} \ -			${redirect_src_mac:+-m mac --mac-source $redirect_src_mac} \ -		} +		fw_get_negation redirect_proto '-p' "$redirect_proto" +		for redirect_src_mac in ${redirect_src_mac:-""}; do +			fw_get_negation redirect_src_mac '--mac-source' "$redirect_src_mac" +			fw add $mode n $natchain $redirect_target + \ +				{ $redirect_src_ip $redirect_dest_ip } { \ +				$srcaddr $srcdaddr $redirect_proto \ +				$srcports $srcdports \ +				${redirect_src_mac:+-m mac $redirect_src_mac} \ +				$natopt $nataddr${natports:+:$natports} \ +				$redirect_options \ +			} + +			[ -n "$destaddr" ] && \ +			fw add $mode f ${fwdchain:-forward} ACCEPT + \ +				{ $redirect_src_ip $redirect_dest_ip } { \ +				$srcaddr $destaddr $redirect_proto \ +				$srcports $destports \ +				$redirect_src_mac \ +				$redirect_extra \ +			} +		done  	done  	fw_callback post redirect diff --git a/package/firewall/files/lib/core_rule.sh b/package/firewall/files/lib/core_rule.sh index 8c234a33a..64a510df9 100644 --- a/package/firewall/files/lib/core_rule.sh +++ b/package/firewall/files/lib/core_rule.sh @@ -16,24 +16,23 @@ fw_config_get_rule() {  		string proto "tcpudp" \  		string target "" \  		string family "" \ +		string limit "" \ +		string limit_burst "" \ +		string extra "" \  	} || return  	[ -n "$rule_name" ] || rule_name=$rule__name -	[ "$rule_proto" == "icmp" ] || rule_icmp_type=  }  fw_load_rule() {  	fw_config_get_rule "$1" -	[ "$rule_target" != "NOTRACK" ] || [ -n "$rule_src" ] || { +	[ "$rule_target" != "NOTRACK" ] || [ -n "$rule_src" ] || [ "$rule_src" != "*" ] || {  		fw_log error "NOTRACK rule ${rule_name}: needs src, skipping"  		return 0  	}  	fw_callback pre rule -	fw_get_port_range rule_src_port $rule_src_port -	fw_get_port_range rule_dest_port $rule_dest_port -  	local table=f  	local chain=input  	local target="${rule_target:-REJECT}" @@ -41,8 +40,22 @@ fw_load_rule() {  		table=r  		chain="zone_${rule_src}_notrack"  	else -		[ -n "$rule_src" ] && chain="zone_${rule_src}${rule_dest:+_forward}" -		[ -n "$rule_dest" ] && target="zone_${rule_dest}_${target}" +		if [ -n "$rule_src" ]; then +			if [ "$rule_src" != "*" ]; then +				chain="zone_${rule_src}${rule_dest:+_forward}" +			else +				chain="${rule_dest:+forward}" +				chain="${chain:-input}" +			fi +		fi + +		if [ -n "$rule_dest" ]; then +			if [ "$rule_dest" != "*" ]; then +				target="zone_${rule_dest}_${target}" +			elif [ "$target" = REJECT ]; then +				target=reject +			fi +		fi  	fi  	local mode @@ -54,17 +67,31 @@ fw_load_rule() {  	[ "$rule_proto" == "tcpudp" ] && rule_proto="tcp udp"  	for rule_proto in $rule_proto; do -		local rule_pos -		eval 'rule_pos=$((++FW__RULE_COUNT_'${mode#G}'_'$chain'))' - -		fw add $mode $table $chain $target $rule_pos { $rule_src_ip $rule_dest_ip } { \ -			$src_spec $dest_spec \ -			${rule_proto:+-p $rule_proto} \ -			${rule_src_port:+--sport $rule_src_port} \ -			${rule_src_mac:+-m mac --mac-source $rule_src_mac} \ -			${rule_dest_port:+--dport $rule_dest_port} \ -			${rule_icmp_type:+--icmp-type $rule_icmp_type} \ -		} +		fw_get_negation rule_proto '-p' "$rule_proto" +		for rule_src_port in ${rule_src_port:-""}; do +			fw_get_port_range rule_src_port $rule_src_port +			fw_get_negation rule_src_port '--sport' "$rule_src_port" +			for rule_dest_port in ${rule_dest_port:-""}; do +				fw_get_port_range rule_dest_port $rule_dest_port +				fw_get_negation rule_dest_port '--dport' "$rule_dest_port" +				for rule_src_mac in ${rule_src_mac:-""}; do +					fw_get_negation rule_src_mac '--mac-source' "$rule_src_mac" +					for rule_icmp_type in ${rule_icmp_type:-""}; do +						[ "$rule_proto" = "-p icmp" ] || rule_icmp_type="" +						fw add $mode $table $chain $target + \ +							{ $rule_src_ip $rule_dest_ip } { \ +							$src_spec $dest_spec $rule_proto \ +							$rule_src_port $rule_dest_port \ +							${rule_src_mac:+-m mac $rule_src_mac} \ +							${rule_icmp_type:+--icmp-type $rule_icmp_type} \ +							${rule_limit:+-m limit --limit $rule_limit \ +								${rule_limit_burst:+--limit-burst $rule_limit_burst}} \ +							$rule_extra \ +						} +					done +				done +			done +		done  	done  	fw_callback post rule diff --git a/package/firewall/files/lib/fw.sh b/package/firewall/files/lib/fw.sh index 896947241..647bcd6a5 100644 --- a/package/firewall/files/lib/fw.sh +++ b/package/firewall/files/lib/fw.sh @@ -137,10 +137,13 @@ fw__exec() { # <action> <family> <table> <chain> <target> <position> { <rules> }  	case "$tgt" in  		-) tgt= ;;  	esac + +	local rule_offset  	case "$pos" in  		^) pos=1 ;;  		$) pos= ;;  		-) pos= ;; +		+) eval "rule_offset=\${FW__RULE_OFS_${app}_${tab}_${chn}:-1}" ;;  	esac  	if ! fw__has - family || ! fw__has $tab ; then @@ -159,13 +162,29 @@ fw__exec() { # <action> <family> <table> <chain> <target> <position> { <rules> }  		fi  	fi -	local cmdline="$app --table ${tab} --${cmd} ${chn} ${pol} ${pos} ${tgt:+--jump "$tgt"}" +	local cmdline="$app --table ${tab} --${cmd} ${chn} ${pol} ${rule_offset:-${pos}} ${tgt:+--jump "$tgt"}"  	while [ $# -gt 1 ]; do -		case "$app:$1" in -			ip6tables:--icmp-type) cmdline="$cmdline --icmpv6-type" ;; -			ip6tables:icmp|ip6tables:ICMP) cmdline="$cmdline icmpv6" ;; -			iptables:--icmpv6-type) cmdline="$cmdline --icmp-type" ;; -			iptables:icmpv6) cmdline="$cmdline icmp" ;; +		# special parameter handling +		case "$1:$2" in +			-p:icmp*|--protocol:icmp*) +				[ "$app" = ip6tables ] && \ +					cmdline="$cmdline -p icmpv6" || \ +					cmdline="$cmdline -p icmp" +				shift +			;; +			--icmp-type:*|--icmpv6-type:*) +				local icmp_type +				if [ "$app" = ip6tables ] && fw_check_icmptype6 icmp_type "$2"; then +					cmdline="$cmdline $icmp_type" +				elif [ "$app" = iptables ] && fw_check_icmptype4 icmp_type "$2"; then +					cmdline="$cmdline $icmp_type" +				else +					local fam=IPv4; [ "$app" = ip6tables ] && fam=IPv6 +					fw_log info "ICMP type '$2' is not valid for $fam address family, skipping rule" +					return 1 +				fi +				shift	 +			;;  			*) cmdline="$cmdline $1" ;;  		esac  		shift @@ -175,7 +194,10 @@ fw__exec() { # <action> <family> <table> <chain> <target> <position> { <rules> }  	$cmdline -	fw__rc $? +	local rv=$? +	[ $rv -eq 0 ] && [ -n "$rule_offset" ] && \ +		export -- "FW__RULE_OFS_${app}_${tab}_${chn}=$(($rule_offset + 1))" +	fw__rc $rv  }  fw_get_port_range() { @@ -189,8 +211,8 @@ fw_get_port_range() {  	local _first=${_ports%-*}  	local _last=${_ports#*-} -	if [ "$_first" != "$_last" ]; then -		export -- "$_var=$_first$_delim$_last" +	if [ "${_first#!}" != "${_last#!}" ]; then +		export -- "$_var=$_first$_delim${_last#!}"  	else  		export -- "$_var=$_first"  	fi @@ -221,11 +243,11 @@ fw_get_family_mode() {  fw_get_negation() {  	local _var="$1"  	local _flag="$2" -	local _ipaddr="$3" +	local _value="$3" -	[ "${_ipaddr#!}" != "$_ipaddr" ] && \ -		export -n -- "$_var=! $_flag ${_ipaddr#!}" || \ -		export -n -- "$_var=${_ipaddr:+$_flag $_ipaddr}" +	[ "${_value#!}" != "$_value" ] && \ +		export -n -- "$_var=! $_flag ${_value#!}" || \ +		export -n -- "$_var=${_value:+$_flag $_value}"  }  fw_get_subnet4() { @@ -245,3 +267,66 @@ fw_get_subnet4() {  		*) export -n -- "$_var=" ;;  	esac  } + +fw_check_icmptype4() { +	local _var="$1" +	local _type="$2" +	case "$_type" in +		![0-9]*) export -n -- "$_var=! --icmp-type ${_type#!}"; return 0 ;; +		[0-9]*)  export -n -- "$_var=--icmp-type $_type";       return 0 ;; +	esac + +	[ -z "$FW_ICMP4_TYPES" ] && \ +		export FW_ICMP4_TYPES=$( +			iptables -p icmp -h 2>/dev/null | \ +			sed -n -e '/^Valid ICMP Types:/ { +				n; :r; +				/router-advertisement/d; +				/router-solicitation/d; +				s/[()]/ /g; s/[[:space:]]\+/\n/g; p; n; b r +			}' | sort -u +		) + +	local _check +	for _check in $FW_ICMP4_TYPES; do +		if [ "$_check" = "${_type#!}" ]; then +			[ "${_type#!}" != "$_type" ] && \ +				export -n -- "$_var=! --icmp-type ${_type#!}" || \ +				export -n -- "$_var=--icmp-type $_type" +			return 0 +		fi +	done + +	export -n -- "$_var=" +	return 1 +} + +fw_check_icmptype6() { +	local _var="$1" +	local _type="$2" +	case "$_type" in +		![0-9]*) export -n -- "$_var=! --icmpv6-type ${_type#!}"; return 0 ;; +		[0-9]*)  export -n -- "$_var=--icmpv6-type $_type";       return 0 ;; +	esac + +	[ -z "$FW_ICMP6_TYPES" ] && \ +	 	export FW_ICMP6_TYPES=$( +	 		ip6tables -p icmpv6 -h 2>/dev/null | \ +	 		sed -n -e '/^Valid ICMPv6 Types:/ { +	 			n; :r; s/[()]/ /g; s/[[:space:]]\+/\n/g; p; n; b r +	 		}' | sort -u +	 	) + +	local _check +	for _check in $FW_ICMP6_TYPES; do +		if [ "$_check" = "${_type#!}" ]; then +			[ "${_type#!}" != "$_type" ] && \ +				export -n -- "$_var=! --icmpv6-type ${_type#!}" || \ +				export -n -- "$_var=--icmpv6-type $_type" +			return 0 +		fi +	done + +	export -n -- "$_var=" +	return 1 +} diff --git a/package/firewall/files/reflection.hotplug b/package/firewall/files/reflection.hotplug index 33d121cec..4fd8f296d 100644 --- a/package/firewall/files/reflection.hotplug +++ b/package/firewall/files/reflection.hotplug @@ -56,6 +56,7 @@ if [ "$ACTION" = "add" ] && [ "$INTERFACE" = "wan" ]; then  		[ "$src" = wan ] && [ "$target" = DNAT ] && {  			local dest  			config_get dest "$cfg" dest "lan" +			[ "$dest" != "*" ] || return  			local net  			for net in $(find_networks "$dest"); do  | 
