diff --git a/firewallmngr/Makefile b/firewallmngr/Makefile index 3203048d9..d5d9de8a8 100644 --- a/firewallmngr/Makefile +++ b/firewallmngr/Makefile @@ -5,7 +5,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=firewallmngr -PKG_VERSION:=1.0.3 +PKG_VERSION:=1.0.4 LOCAL_DEV:=0 ifneq ($(LOCAL_DEV),1) @@ -53,15 +53,21 @@ endif define Package/firewallmngr/install $(INSTALL_DIR) $(1)/etc/config + $(INSTALL_DIR) $(1)/etc/uci-defaults ifeq ($(CONFIG_FIREWALLMNGR_PORT_TRIGGER),y) $(INSTALL_DIR) $(1)/etc/init.d - $(INSTALL_DIR) $(1)/etc/config $(INSTALL_DIR) $(1)/lib/port-trigger $(INSTALL_BIN) ./files/port-trigger/etc/init.d/port-trigger $(1)/etc/init.d/ $(INSTALL_DATA) ./files/port-trigger/etc/config/port-trigger $(1)/etc/config/ $(INSTALL_DATA) ./files/port-trigger/lib/port-trigger/port_trigger.sh $(1)/lib/port-trigger/ endif + $(INSTALL_BIN) ./files/firewall.portmap $(1)/etc/ + $(INSTALL_DATA) ./files/etc/uci-defaults/95-portmap-firewall $(1)/etc/uci-defaults/ + + $(INSTALL_BIN) ./files/firewall.service $(1)/etc/ + $(INSTALL_DATA) ./files/etc/uci-defaults/97-firewall-service $(1)/etc/uci-defaults/ + $(BBFDM_INSTALL_MS_DM) $(PKG_BUILD_DIR)/src/libfirewallmngr.so $(1) $(PKG_NAME) endef diff --git a/firewallmngr/files/etc/uci-defaults/95-portmap-firewall b/firewallmngr/files/etc/uci-defaults/95-portmap-firewall new file mode 100644 index 000000000..83c54d90a --- /dev/null +++ b/firewallmngr/files/etc/uci-defaults/95-portmap-firewall @@ -0,0 +1,12 @@ +#!/bin/sh + +uci -q batch <<-EOT + delete firewall.port_hook + set firewall.port_hook=include + set firewall.port_hook.path=/etc/firewall.portmap + set firewall.port_hook.reload=1 + commit firewall +EOT + +exit 0 + diff --git a/firewallmngr/files/etc/uci-defaults/97-firewall-service b/firewallmngr/files/etc/uci-defaults/97-firewall-service new file mode 100644 index 000000000..081aee09d --- /dev/null +++ b/firewallmngr/files/etc/uci-defaults/97-firewall-service @@ -0,0 +1,12 @@ +#!/bin/sh + +uci -q batch <<-EOT + delete firewall.service_hook + set firewall.service_hook=include + set firewall.service_hook.path=/etc/firewall.service + set firewall.service_hook.reload=1 + commit firewall +EOT + +exit 0 + diff --git a/firewallmngr/files/firewall.portmap b/firewallmngr/files/firewall.portmap new file mode 100755 index 000000000..f134ae1a7 --- /dev/null +++ b/firewallmngr/files/firewall.portmap @@ -0,0 +1,74 @@ +#!/bin/sh + +. /lib/functions.sh + +log() { + echo "${@}"|logger -t firewall.dnat -p info +} + +exec_cmd() { + if ! eval "$*"; then + log "Failed to run [$*]" + fi +} + +reorder_dnat_rules() { + nat_chains=$(iptables -t nat -S | grep -E "^-N zone[a-zA-Z0-9_]+prerouting$" | cut -d' ' -f 2) + + for chain in ${nat_chains}; do + # Collect empty remote host & empty dport rules + EMPTY_HOST_PORT=$(iptables -t nat -S ${chain} | grep -E "REDIRECT|DNAT" | grep -v "\-\-dport" | grep -v "\-s ") + if [ -n "${EMPTY_HOST_PORT}" ]; then + echo "${EMPTY_HOST_PORT}" | while read cmd; do + cmd1="iptables -t nat $(echo $cmd | sed 's/-A /-D /g')" + exec_cmd $cmd1 + done + fi + + # Collect empty remote host but non empty dport rules + EMPTY_HOST=$(iptables -t nat -S ${chain} | grep -E "REDIRECT|DNAT" | grep "\-\-dport" | grep -v "\-s ") + if [ -n "${EMPTY_HOST}" ]; then + echo "${EMPTY_HOST}" | while read cmd; do + cmd1="iptables -t nat $(echo $cmd | sed 's/-A /-D /g')" + exec_cmd $cmd1 + done + fi + + # Collect non empty remote host but empty dport rules + EMPTY_PORT=$(iptables -t nat -S ${chain} | grep -E "REDIRECT|DNAT" | grep -v "\-\-dport" | grep "\-s ") + if [ -n "${EMPTY_PORT}" ]; then + echo "${EMPTY_PORT}" | while read cmd; do + cmd1="iptables -t nat $(echo $cmd | sed 's/-A /-D /g')" + exec_cmd $cmd1 + done + fi + + # Now add rules as per datamodel precedence shown below + ## Non empty remote host, empty dport + ## empty remote host, non empty dport + ## empty remote host, empty dport + if [ -n "${EMPTY_PORT}" ]; then + echo "${EMPTY_PORT}" | while read cmd; do + cmd1="iptables -t nat $(echo $cmd)" + exec_cmd $cmd1 + done + fi + + if [ -n "${EMPTY_HOST}" ]; then + echo "${EMPTY_HOST}" | while read cmd; do + cmd1="iptables -t nat $(echo $cmd)" + exec_cmd $cmd1 + done + fi + + if [ -n "${EMPTY_HOST_PORT}" ]; then + echo "${EMPTY_HOST_PORT}" | while read cmd; do + cmd1="iptables -t nat $(echo $cmd)" + exec_cmd $cmd1 + done + fi + done +} + +# Re-order portmapping rules according to precedence hierarchy +reorder_dnat_rules diff --git a/firewallmngr/files/firewall.service b/firewallmngr/files/firewall.service new file mode 100755 index 000000000..100f54a3d --- /dev/null +++ b/firewallmngr/files/firewall.service @@ -0,0 +1,163 @@ +#!/bin/sh + +. /lib/functions.sh + +log() { + echo "${@}"|logger -t firewall.service -p info +} + +exec_cmd() { + if ! eval "$*"; then + log "Failed to run [$*]" + echo "-1" + return 0 + fi + + echo "0" + return 0 +} + +add_iptable_rule() { + chain_name=$1 + protocol=$2 + dest_port=$3 + icmp_type=$4 + family=$5 + src_prefix=$6 + action=$7 + res=-1 + + cmd="-I ${chain_name}" + + if [ -n "${protocol}" ]; then + cmd="${cmd} -p $protocol" + + if [ "${protocol}" -eq 41 ] || [ "${protocol}" -eq 43 ] || [ "${protocol}" -eq 44 ] || + [ "${protocol}" -eq 58 ] || [ "${protocol}" -eq 59 ] || [ "${protocol}" -eq 60 ]; then + # Explicit v6 protocols + if [ "${family}" = "ipv4" ]; then + echo $res + return 0 + else + family="ipv6" + fi + elif [ "${protocol}" -eq 1 ]; then + # Explicit v4 protocols + if [ "${family}" = "ipv6" ]; then + echo $res + return 0 + else + family="ipv4" + fi + fi + + # Limitation:: iptables do not accept dport without specific protocol + count=$(echo "${dest_port}" | sed -n "/-1/p" | wc -l) + + if [ "${count}" -eq 0 ]; then + # dest_port not contains -1 so need to have this match criteria + count=$(echo "${dest_port}" | wc -w) + if [ "${count}" -gt 1 ]; then + # multiple ports present + port=$(echo "${dest_port}" | sed "s/ /,/g") + cmd="${cmd} -m multiport --dports ${port}" + else + cmd="${cmd} --dport ${dest_port}" + fi + fi + fi + + if [ "${family}" = "ipv4" ] || [ "${family}" = "-1" ]; then + if [ "${protocol}" -eq 1 ] && [ "${icmp_type}" -ge 0 ]; then + cmd="${cmd} --icmp-type ${icmp_type}" + fi + + if [ -z "${src_prefix}" ]; then + res=$(exec_cmd "iptables ${cmd} -m comment --comment IPtables_service_rule -j ${action}") + else + #Add ipv4 sources if any + src_list="" + for src in $src_prefix; do + ret=$(echo $src | grep ":" | wc -l) + if [ "${ret}" -eq 0 ]; then + src_list="$src,$src_list" + fi + done + + if [ -n "$src_list" ]; then + src_list=$(echo "${src_list}" | sed "s/,$//") + res=$(exec_cmd "iptables -s $src_list ${cmd} -m comment --comment IPtables_service_rule -j ${action}") + fi + fi + fi + + if [ "${family}" = "ipv6" ] || [ "${family}" = "-1" ]; then + if [ "${protocol}" -eq 58 ] && [ "${icmp_type}" -ge 0 ]; then + cmd="${cmd} --icmpv6-type ${icmp_type}" + fi + + if [ -z "${src_prefix}" ]; then + res=$(exec_cmd "ip6tables ${cmd} -m comment --comment IP6tables_service_rule -j ${action}") + else + #Add ipv6 sources if any + src_list="" + for src in $src_prefix; do + ret=$(echo $src | grep ":" | wc -l) + if [ "${ret}" -eq 1 ]; then + src_list="$src,$src_list" + fi + done + + if [ -n "$src_list" ]; then + src_list=$(echo "${src_list}" | sed "s/,$//") + res=$(exec_cmd "ip6tables -s $src_list ${cmd} -m comment --comment IP6tables_service_rule -j ${action}") + fi + fi + fi + + echo $res +} + +add_service() { + local enable proto family dest_port interface target icmp_type + + config_get enable "$1" enable "0" + config_get icmp_type "$1" icmp_type "-1" + config_get target "$1" target "Accept" + config_get interface "$1" interface "" + config_get family "$1" family "-1" + config_get dest_port "$1" dest_port "-1" + config_get proto "$1" proto "-1" + config_get src_prefix "$1" src_prefix "" + + if [ "${enable}" -eq 0 ] || [ -z "${interface}" ]; then + return 0 + fi + + action=$(echo "${target}" | tr a-z A-Z) + chain_name="zone_${interface}_input" + res=0 + + count=$(echo "${proto}" | sed -n "/-1/p" | wc -l) + + if [ "${count}" -eq 0 ]; then + # proto not contains -1 so need to have this match criteria + for protocol in $proto; do + res=$(add_iptable_rule "$chain_name" "$protocol" "$dest_port" "$icmp_type" "$family" "$src_prefix" "$action") + done + else + # proto contains -1 so no need to have this match criteria + res=$(add_iptable_rule "$chain_name" "" "$dest_port" "$icmp_type" "$family" "$src_prefix" "$action") + fi + + if [ "${res}" -ne 0 ]; then + uci -q set firewall."${1}".status="Error" + else + uci -q set firewall."${1}".status="" + fi + + uci commit firewall +} + +config_load firewall +config_foreach add_service "service"