diff --git a/Shorewall/clib.accounting b/Shorewall/clib.accounting new file mode 100644 index 000000000..aef6ef919 --- /dev/null +++ b/Shorewall/clib.accounting @@ -0,0 +1,253 @@ +#!/bin/sh +# +# Shorewall 3.2 -- /usr/share/shorewall/clib.tcrules +# +# This program is under GPL [http://www.gnu.org/copyleft/gpl.htm] +# +# (c) 2003,2004,2005,2006 - Tom Eastep (teastep@shorewall.net) +# +# Complete documentation is available at http://shorewall.net +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of Version 2 of the GNU General Public License +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA + +# +# Process a record from the accounting file +# +process_accounting_rule() { + rule= + rule2= + jumpchain= + user1= + + accounting_error() { + error_message "WARNING: Invalid Accounting rule" $action $chain $source $dest $proto $port $sport $user + } + + accounting_interface_error() { + error_message "WARNING: Unknown interface $1 in " $action $chain $source $dest $proto $port $sport $user + } + + accounting_interface_verify() { + verify_interface $1 || accounting_interface_error $1 + } + + jump_to_chain() { + if ! havechain $jumpchain; then + if ! createchain2 $jumpchain No; then + accounting_error + return 2 + fi + fi + + rule="$rule -j $jumpchain" + } + + do_ipp2p() { + [ -n "$IPP2P_MATCH" ] || fatal_error "Your kernel and/or iptables does not have IPP2P match support" + case $proto in + *:*) + proto=${proto#*:} + ;; + *) + proto=tcp + ;; + esac + + rule="$rule -p $proto -m ipp2p --${port:-ipp2p}" + } + + case $source in + *:*) + accounting_interface_verify ${source%:*} + rule="$(source_ip_range ${source#*:}) $(match_source_dev ${source%:*})" + ;; + *.*.*.*|+*|!+*) + rule="$(source_ip_range $source)" + ;; + -|all|any) + ;; + *) + if [ -n "$source" ]; then + accounting_interface_verify $source + rule="$(match_source_dev $source)" + fi + ;; + esac + + [ -n "$dest" ] && case $dest in + *:*) + accounting_interface_verify ${dest%:*} + rule="$rule $(dest_ip_range ${dest#*:}) $(match_dest_dev ${dest%:*})" + ;; + *.*.*.*|+*|!*) + rule="$rule $(dest_ip_range $dest)" + ;; + -|all|any) + ;; + *) + accounting_interface_verify $dest + rule="$rule $(match_dest_dev $dest)" + ;; + esac + + [ -n "$proto" ] && case $proto in + -|any|all) + ;; + ipp2p|IPP2P|ipp2p:*|IPP2P:*) + do_ipp2p + ;; + *) + rule="$rule -p $proto" + ;; + esac + + multiport= + + [ -n "$port" ] && case $port in + -|any|all) + ;; + *) + if [ -n "$MULTIPORT" ]; then + rule="$rule -m multiport --dports $port" + multiport=Yes + else + rule="$rule --dport $port" + fi + ;; + esac + + [ -n "$sport" ] && case $sport in + -|any|all) + ;; + *) + if [ -n "$MULTIPORT" ]; then + [ -n "$multiport" ] && rule="$rule --sports $sport" || rule="$rule -m multiport --sports $sport" + else + rule="$rule --sport $sport" + fi + ;; + esac + + [ -n "$user" ] && case $user in + -|any|all) + ;; + *) + [ "$chain" != OUTPUT ] && \ + fatal_error "Invalid use of a user/group: chain is not OUTPUT but $chain" + rule="$rule -m owner" + user1="$user" + + case "$user" in + !*+*) + if [ -n "${user#*+}" ]; then + rule="$rule ! --cmd-owner ${user#*+} " + fi + user1=${user%+*} + ;; + *+*) + if [ -n "${user#*+}" ]; then + rule="$rule --cmd-owner ${user#*+} " + fi + user1=${user%+*} + ;; + esac + + case "$user1" in + !*:*) + if [ "$user1" != "!:" ]; then + temp="${user1#!}" + temp="${temp%:*}" + [ -n "$temp" ] && rule="$rule ! --uid-owner $temp " + temp="${user1#*:}" + [ -n "$temp" ] && rule="$rule ! --gid-owner $temp " + fi + ;; + *:*) + if [ "$user1" != ":" ]; then + temp="${user1%:*}" + [ -n "$temp" ] && rule="$rule --uid-owner $temp " + temp="${user1#*:}" + [ -n "$temp" ] && rule="$rule --gid-owner $temp " + fi + ;; + !*) + [ "$user1" != "!" ] && rule="$rule ! --uid-owner ${user1#!} " + ;; + *) + [ -n "$user1" ] && rule="$rule --uid-owner $user1 " + ;; + esac + ;; + esac + + case $action in + COUNT) + ;; + DONE) + rule="$rule -j RETURN" + ;; + *:COUNT) + rule2="$rule" + jumpchain=${action%:*} + jump_to_chain || return + ;; + JUMP:*) + jumpchain=${action#*:} + jump_to_chain || return + ;; + *) + jumpchain=$action + jump_to_chain || return + ;; + esac + + [ "x${chain:=accounting}" = "x-" ] && chain=accounting + + ensurechain1 $chain + + if do_iptables -A $chain $(fix_bang $rule) ; then + [ -n "$rule2" ] && run_iptables2 -A $jumpchain $rule2 + progress_message " Accounting rule" $action $chain $source $dest $proto $port $sport $user $DONE + save_progress_message_short " Accounting rule $action $chain $source $dest $proto $port $sport $user Added" + else + accounting_error + fi +} + +# +# Set up Accounting +# +setup_accounting() # $1 = Name of accounting file +{ + + progress_message2 "$DOING Accounting..." + + save_progress_message "Setting up Accounting..." + + strip_file accounting $1 + + while read action chain source dest proto port sport user ; do + expandv action chain source dest proto port sport user + process_accounting_rule + done < $TMP_DIR/accounting + + if havechain accounting; then + for chain in INPUT FORWARD OUTPUT; do + run_iptables -I $chain -j accounting + done + fi + +} + +CLIB_ACCOUNTING_LOADED=Yes diff --git a/Shorewall/clib.ecn b/Shorewall/clib.ecn new file mode 100644 index 000000000..e5aabbb5d --- /dev/null +++ b/Shorewall/clib.ecn @@ -0,0 +1,80 @@ +#!/bin/sh +# +# Shorewall 3.2 -- /usr/share/shorewall/clib.ecn +# +# This program is under GPL [http://www.gnu.org/copyleft/gpl.htm] +# +# (c) 2005,2006 - Tom Eastep (teastep@shorewall.net) +# +# Complete documentation is available at http://shorewall.net +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of Version 2 of the GNU General Public License +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA + +# +# ECN Chain to an interface +# +ecn_chain() # $1 = interface +{ + echo $(chain_base $1)_ecn +} + +# +# Setup ECN disabling rules +# +setup_ecn() # $1 = file name +{ + local interfaces="" + local hosts= + local h + + strip_file ecn $1 + + progress_message2 "$DOING $1..." + + while read interface host; do + expandv interface host + list_search $interface $ALL_INTERFACES || \ + fatal_error "Unknown interface $interface" + list_search $interface $interfaces || \ + interfaces="$interfaces $interface" + [ "x$host" = "x-" ] && host= + for h in $(separate_list ${host:-0.0.0.0/0}); do + hosts="$hosts $interface:$h" + done + done < $TMP_DIR/ecn + + if [ -n "$interfaces" ]; then + progress_message "$DOING ECN control on${interfaces}..." + + for interface in $interfaces; do + chain=$(ecn_chain $interface) + if havemanglechain $chain; then + flushmangle $chain + else + createmanglechain $chain + run_iptables -t mangle -A POSTROUTING -p tcp -o $interface -j $chain + run_iptables -t mangle -A OUTPUT -p tcp -o $interface -j $chain + fi + done + + for host in $hosts; do + interface=${host%:*} + h=${host#*:} + run_iptables -t mangle -A $(ecn_chain $interface) -p tcp $(dest_ip_range $h) -j ECN --ecn-tcp-remove + progress_message_and_save " ECN Disabled to $h through $interface" + done + fi +} + +CLIB_ECN_LOADED=Yes diff --git a/Shorewall/clib.maclist b/Shorewall/clib.maclist new file mode 100644 index 000000000..d5985e779 --- /dev/null +++ b/Shorewall/clib.maclist @@ -0,0 +1,264 @@ +#!/bin/sh +# +# Shorewall 3.2 -- /usr/share/shorewall/clib.proxyarp +# +# This program is under GPL [http://www.gnu.org/copyleft/gpl.htm] +# +# (c) 2005,2006 - Tom Eastep (teastep@shorewall.net) +# +# Complete documentation is available at http://shorewall.net +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of Version 2 of the GNU General Public License +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA + +# +# MAC Verification Chain for an interface +# +mac_chain() # $1 = interface +{ + echo $(chain_base $1)_mac +} + +macrecent_target() # $1 - interface +{ + [ -n "$MACLIST_TTL" ] && echo $(chain_base $1)_rec || echo RETURN +} + +# +# Set up MAC Verification +# +setup_mac_lists() { + local interface + local mac + local addresses + local address + local chain + local chain1 + local macpart + local blob + local hosts + local ipsec + local policy= + + create_mac_chain() + { + case $MACLIST_TABLE in + filter) + createchain $1 no + ;; + *) + createmanglechain $1 + ;; + esac + } + + have_mac_chain() + { + local result + + case $MACLIST_TABLE in + filter) + havechain $1 && result=0 || result=1 + ;; + *) + havemanglechain $1 && result=0 || result=1 + ;; + esac + + return $result + } + # + # Generate the list of interfaces having MAC verification + # + maclist_interfaces= + + for hosts in $maclist_hosts; do + hosts=${hosts#*^} + interface=${hosts%%:*} + if ! list_search $interface $maclist_interfaces; then\ + if [ -z "$maclist_interfaces" ]; then + maclist_interfaces=$interface + else + maclist_interfaces="$maclist_interfaces $interface" + fi + fi + done + + progress_message "$DOING MAC Verification on $maclist_interfaces..." + # + # Create chains. + # + for interface in $maclist_interfaces; do + chain=$(mac_chain $interface) + create_mac_chain $chain + # + # If we're using the mangle table and the interface is DHCP-enabled then we need to accept DHCP broadcasts from 0.0.0.0 + # + if [ $MACLIST_TABLE = mangle ] && interface_has_option $interface dhcp; then + run_iptables -t mangle -A $chain -s 0.0.0.0 -d 255.255.255.255 -p udp --dport 67:68 -j RETURN + fi + + if [ -n "$MACLIST_TTL" ]; then + chain1=$(macrecent_target $interface) + create_mac_chain $chain1 + run_iptables -A $chain -t $MACLIST_TABLE -m recent --rcheck --seconds $MACLIST_TTL --name $chain -j RETURN + run_iptables -A $chain -t $MACLIST_TABLE -j $chain1 + run_iptables -A $chain -t $MACLIST_TABLE -m recent --update --name $chain -j RETURN + run_iptables -A $chain -t $MACLIST_TABLE -m recent --set --name $chain + fi + done + + # + # Process the maclist file producing the verification rules + # + while read disposition interface mac addresses; do + expandv disposition interface mac addresses + + level= + + case $disposition in + ACCEPT:*) + level=${disposition#*:} + disposition=ACCEPT + target=RETURN + ;; + ACCEPT) + target=RETURN + ;; + REJECT:*) + [ $MACLIST_TABLE = mangle ] && fatal_error "DISPOSITION = REJECT is incompatible with MACLIST_TABLE=mangle" + target=reject + disposition=REJECT + ;; + REJECT) + [ $MACLIST_TABLE = mangle ] && fatal_error "DISPOSITION = REJECT is incompatible with MACLIST_TABLE=mangle" + target=reject + ;; + DROP:*) + level=${disposition#*:} + disposition=DROP + target=DROP + ;; + DROP) + target=DROP + ;; + *) + addresses="$mac" + mac="$interface" + interface="$disposition" + disposition=ACCEPT + target=RETURN + ;; + esac + + physdev_part= + + if [ -n "$BRIDGING" ]; then + case $interface in + *:*) + physdev_part="-m physdev --physdev-in ${interface#*:}" + interface=${interface%:*} + ;; + esac + fi + + [ -n "$MACLIST_TTL" ] && chain=$(macrecent_target $interface) || chain=$(mac_chain $interface) + + if ! have_mac_chain $chain ; then + fatal_error "No hosts on $interface have the maclist option specified" + fi + + if [ x${mac:=-} = x- ]; then + if [ -z "$addresses" ]; then + fatal_error "You must specify a MAC address or an IP address" + else + macpart= + fi + else + macpart=$(mac_match $mac) + fi + + if [ -z "$addresses" ]; then + [ -n "$level" ] && \ + log_rule_limit $level $chain $(mac_chain $interface) $disposition "$LOGLIMIT" "" -A -t $MACLIST_TABLE $macpart $physdev_part + run_iptables -A $chain -t $MACLIST_TABLE $macpart $physdev_part -j $target + else + for address in $(separate_list $addresses) ; do + [ -n "$level" ] && \ + log_rule_limit $level $chain $(mac_chain $interface) $disposition "$LOGLIMIT" "" -A -t $MACLIST_TABLE $macpart -s $address $physdev_part + run_iptables2 -A $chain -t $MACLIST_TABLE $macpart -s $address $physdev_part -j $target + done + fi + done < $TMP_DIR/maclist + # + # Must take care of our own broadcasts and multicasts then terminate the verification + # chains + # + for interface in $maclist_interfaces; do + + [ -n "$MACLIST_TTL" ] && chain=$(macrecent_target $interface) || chain=$(mac_chain $interface) + + if [ -n "$MACLIST_LOG_LEVEL" -o $MACLIST_DISPOSITION != ACCEPT ]; then + indent >&3 << __EOF__ + +blob=\$(ip link show $interface 2> /dev/null) + +[ -z "\$blob" ] && \ + fatal_error "Interface $interface must be up before Shorewall can start" + +ip -f inet addr show $interface 2> /dev/null | grep 'inet.*brd' | sed 's/inet //; s/brd //; s/scope.*//;' | while read address broadcast; do + address=\${address%/*} + if [ -n "\$broadcast" ]; then + run_iptables -t $MACLIST_TABLE -A $chain -s \$address -d \$broadcast -j RETURN + fi + + run_iptables -t $MACLIST_TABLE -A $chain -s \$address -d 255.255.255.255 -j RETURN + run_iptables -t $MACLIST_TABLE -A $chain -s \$address -d 224.0.0.0/4 -j RETURN +done + +__EOF__ + fi + + if [ -n "$MACLIST_LOG_LEVEL" ]; then + log_rule_limit $MACLIST_LOG_LEVEL $chain $(mac_chain $interface) $MACLIST_DISPOSITION "$LOGLIMIT" "" -A -t $MACLIST_TABLE + fi + + if [ $MACLIST_DISPOSITION != ACCEPT ]; then + run_iptables -A $chain -t $MACLIST_TABLE -j $maclist_target + fi + done + # + # Generate jumps from the input and forward chains + # + for hosts in $maclist_hosts; do + ipsec=${hosts%^*} + hosts=${hosts#*^} + [ -n "$POLICY_MATCH" ] && policy="-m policy --pol $ipsec --dir in" || policy= + interface=${hosts%%:*} + hosts=${hosts#*:} + case $MACLIST_TABLE in + filter) + for chain in $(first_chains $interface) ; do + run_iptables -A $chain $(match_source_hosts $hosts) -m state --state NEW \ + $policy -j $(mac_chain $interface) + done + ;; + *) + run_iptables -t mangle -A PREROUTING -i $interface $(match_source_hosts $hosts) -m state --state NEW \ + $policy -j $(mac_chain $interface) + ;; + esac + done +} + +CLIB_MACLIST_LOADED=Yes diff --git a/Shorewall/clib.macros b/Shorewall/clib.macros new file mode 100644 index 000000000..e9f2757c1 --- /dev/null +++ b/Shorewall/clib.macros @@ -0,0 +1,313 @@ +#!/bin/sh +# +# Shorewall 3.2 -- /usr/share/shorewall/clib.macros +# +# This program is under GPL [http://www.gnu.org/copyleft/gpl.htm] +# +# (c) 2005,2006 - Tom Eastep (teastep@shorewall.net) +# +# Complete documentation is available at http://shorewall.net +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of Version 2 of the GNU General Public License +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA + +# +# This function maps old action names into their new macro equivalents +# +map_old_action() # $1 = Potential Old Action +{ + local macro= aktion + + if [ -n "$MAPOLDACTIONS" ]; then + case $1 in + */*) + echo $1 + return + ;; + *) + if [ -f $(find_file $1) ]; then + echo $1 + return + fi + + case $1 in + Allow*) + macro=${1#*w} + aktion=ACCEPT + ;; + Drop*) + macro=${1#*p} + aktion=DROP + ;; + Reject*) + macro=${1#*t} + aktion=REJECT + ;; + *) + echo $1 + return + ;; + esac + esac + + if [ -f $(find_file macro.$macro) ]; then + echo $macro/$aktion + return + fi + fi + + echo $1 +} + +# +# Combine a source/dest from the macro body with one from the macro invocation +# +merge_macro_source_dest() # $1 = source/dest from macro body, $2 = source/dest from invocation +{ + case $2 in + -) + echo ${1} + ;; + *.*.*|+*|~*|!~*) + # + # Value in the invocation is an address -- put it behind the value from the macro + # + echo ${1}:${2} + ;; + *) + echo ${2}:${1} + ;; + esac +} + +verify_macro_from_action() { + temp=$(map_old_action $temp) + + case $temp in + */*) + param=${temp#*/} + case $param in + ACCEPT|DROP|REJECT|LOG|QUEUE|CONTINUE) + ;; + *) + rule="$xtarget $xclients $xservers $xprotocol $xports $xcports $xratelimit $xuserspec" + fatal_error "Invalid Macro Parameter in rule \"$rule\"" + ;; + esac + temp=${temp%%/*} + ;; + esac + + f1=macro.${temp} + fn=$(find_file $f1) + + if [ ! -f $TMP_DIR/$f1 ]; then + # + # We must only verify macros once to ensure that they don't invoke any non-standard actions + # + if [ -f $fn ]; then + strip_file $f1 $fn + + progress_message " ..Expanding Macro $fn..." + + while read mtarget mclients mservers mprotocol mports mcports mratelimit muserspec; do + expandv mtarget + temp="${mtarget%%:*}" + case "$temp" in + ACCEPT|DROP|REJECT|LOG|QUEUE|CONTINUE|PARAM) + ;; + *) + rule="$mtarget $mclients $mservers $mprotocol $mports $mcports $mratelimit $muserspec" + fatal_error "Invalid TARGET in rule \"$rule\"" + esac + done < $TMP_DIR/$f1 + + progress_message " ..End Macro" + else + rule="$xtarget $xclients $xservers $xprotocol $xports $xcports $xratelimit $xuserspec" + fatal_error "Invalid TARGET in rule \"$rule\"" + fi + fi +} + +expand_macro_in_action() { + + xtarget1=$(map_old_action $xtarget1) + + case $xtarget1 in + */*) + param=${xtarget1#*/} + xtarget1=${xtarget1%%/*} + ;; + esac + + progress_message "..Expanding Macro $(find_file macro.$xtarget1)..." + while read mtarget mclients mservers mprotocol mports mcports mratelimit muserspec; do + expandv mtarget mclients mservers mprotocol mports mcports mratelimit muserspec + + mtarget=$(merge_levels $xaction2 $mtarget) + + case $mtarget in + PARAM|PARAM:*) + [ -n "$param" ] && mtarget=$(substitute_action $param $mtarget) || fatal_error "PARAM requires that a parameter be supplied in macro invocation" + ;; + esac + + if [ -n "$mclients" ]; then + case $mclients in + -|SOURCE) + mclients=${xclients} + ;; + DEST) + mclients=${xservers} + ;; + *) + mclients=$(merge_macro_source_dest $mclients $xclients) + ;; + esac + else + mclients=${xclients} + fi + + if [ -n "$mservers" ]; then + case $mservers in + -|DEST) + mservers=${xservers} + ;; + SOURCE) + mservers=${xclients} + ;; + *) + mservers=$(merge_macro_source_dest $mservers $xservers) + ;; + esac + else + mservers=${xserverss} + fi + + [ -n "$xprotocol" ] && [ "x${xprotocol}" != x- ] && mprotocol=$xprotocol + [ -n "$xports" ] && [ "x${xports}" != x- ] && mports=$xports + [ -n "$xcports" ] && [ "x${xcports}" != x- ] && mcports=$xcports + [ -n "$xratelimit" ] && [ "x${xratelimit}" != x- ] && mratelimit=$xratelimit + [ -n "$xuserspec" ] && [ "x${xuserspec}" != x- ] && muserspec=$xuserspec + + rule="$mtarget ${mclients:=-} ${mservers:=-} ${mprotocol:=-} ${mports:=-} ${mcports:=-} ${mratelimit:-} ${muserspec:=-}" + process_action $xchain $xaction1 $mtarget $mclients $mservers $mprotocol $mports $mcports $mratelimit $muserspec + done < $TMP_DIR/macro.$xtarget1 + progress_message "..End Macro" +} + +# +# Process a macro invocation in the rules file +# + +process_macro() # $1 = target + # $2 = param + # $2 = clients + # $3 = servers + # $4 = protocol + # $5 = ports + # $6 = cports + # $7 = address + # $8 = ratelimit + # $9 = userspec +{ + local itarget="$1" + local param="$2" + local iclients="$3" + local iservers="$4" + local iprotocol="$5" + local iports="$6" + local icports="$7" + local iaddress="$8" + local iratelimit="$9" + local iuserspec="${10}" + + progress_message "..Expanding Macro $(find_file macro.${itarget%%:*})..." + + while read mtarget mclients mservers mprotocol mports mcports mratelimit muserspec; do + expandv mtarget mclients mservers mprotocol mports mcports mratelimit muserspec + + mtarget=$(merge_levels $itarget $mtarget) + + case $mtarget in + PARAM|PARAM:*) + [ -n "$param" ] && mtarget=$(substitute_action $param $mtarget) || fatal_error "PARAM requires that a parameter be supplied in macro invocation" + ;; + esac + + case ${mtarget%%:*} in + ACCEPT|ACCEPT+|NONAT|DROP|REJECT|DNAT|DNAT-|REDIRECT|REDIRECT-|LOG|CONTINUE|QUEUE|SAME|SAME-) + ;; + *) + if list_search ${mtarget%%:*} $ACTIONS; then + if ! list_search $mtarget $USEDACTIONS; then + createactionchain $mtarget + USEDACTIONS="$USEDACTIONS $mtarget" + fi + + mtarget=$(find_logactionchain $mtarget) + else + fatal_error "Invalid Action in rule \"$mtarget ${mclients:--} ${mservers:--} ${mprotocol:--} ${mports:--} ${mcports:--} ${xaddress:--} ${mratelimit:--} ${muserspec:--}\"" + fi + ;; + esac + + if [ -n "$mclients" ]; then + case $mclients in + -|SOURCE) + mclients=${iclients} + ;; + DEST) + mclients=${iservers} + ;; + *) + mclients=$(merge_macro_source_dest $mclients $iclients) + ;; + esac + else + mclients=${iclients} + fi + + if [ -n "$mservers" ]; then + case $mservers in + -|DEST) + mservers=${iservers} + ;; + SOURCE) + mservers=${iclients} + ;; + *) + mservers=$(merge_macro_source_dest $mservers $iservers) + ;; + esac + else + mservers=${iservers} + fi + + [ -n "$iprotocol" ] && [ "x${iprotocol}" != x- ] && mprotocol=$iprotocol + [ -n "$iports" ] && [ "x${iports}" != x- ] && mports=$iports + [ -n "$icports" ] && [ "x${icports}" != x- ] && mcports=$icports + [ -n "$iratelimit" ] && [ "x${iratelimit}" != x- ] && mratelimit=$iratelimit + [ -n "$iuserspec" ] && [ "x${iuserspec}" != x- ] && muserspec=$iuserspec + + rule="$mtarget ${mclients=-} ${mservers:=-} ${mprotocol:=-} ${mports:=-} ${mcports:=-} ${xaddress:=-} ${mratelimit:=-} ${muserspec:=-}" + process_rule $mtarget $mclients $mservers $mprotocol $mports $mcports ${iaddress:=-} $mratelimit $muserspec + + done < $TMP_DIR/macro.${itarget%%:*} + + progress_message "..End Macro" + +} + +CLIB_MACROS_LOADED=Yes diff --git a/Shorewall/clib.providers b/Shorewall/clib.providers new file mode 100644 index 000000000..5ad3ce703 --- /dev/null +++ b/Shorewall/clib.providers @@ -0,0 +1,417 @@ +#!/bin/sh +# +# Shorewall 3.2 -- /usr/share/shorewall/clib.providers +# +# This program is under GPL [http://www.gnu.org/copyleft/gpl.htm] +# +# (c) 2005,2006 - Tom Eastep (teastep@shorewall.net) +# +# Complete documentation is available at http://shorewall.net +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of Version 2 of the GNU General Public License +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA + +# +# Process the providers file +# +setup_providers() +{ + local table number mark duplicate interface gateway options provider address copy route loose addresses rulenum rulebase echobin=$(mywhich echo) balance save_indent="$INDENT" mask= first=Yes save_indent1= + + copy_table() { + indent >&3 << __EOF__ +ip route show table $duplicate | while read net route; do + case \$net in + default|nexthop) + ;; + *) + run_ip route add table $number \$net \$route + ;; + esac +done +__EOF__ + } + + copy_and_edit_table() { + indent >&3 << __EOF__ +ip route show table $duplicate | while read net route; do + case \$net in + default|nexthop) + ;; + *) + case \$(find_device \$route) in + `echo $copy\) | sed 's/ /|/g'` + run_ip route add table $number \$net \$route + ;; + esac + ;; + esac +done + +__EOF__ + } + + balance_default_route() # $1 = weight + { + balance=yes + + save_command + if [ -n "$first" ]; then + if [ -n "$gateway" ] ; then + save_command "DEFAULT_ROUTE=\"nexthop via $gateway dev $interface weight $1\"" + else + save_command "DEFAULT_ROUTE=\"nexthop dev $interface weight $1\"" + fi + + first= + else + if [ -n "$gateway" ] ; then + save_command "DEFAULT_ROUTE=\"\$DEFAULT_ROUTE nexthop via $gateway dev $interface weight $1\"" + else + save_command "DEFAULT_ROUTE=\"\$DEFAULT_ROUTE nexthop dev $interface weight $1\"" + fi + fi + } + + add_a_provider() { + local t n iface option optional= + + [ -n "$MANGLE_ENABLED" ] || fatal_error "Providers require mangle support in your kernel and iptables" + + for t in $PROVIDERS local main default unspec; do + if [ "$t" = "$table" ]; then + fatal_error "Duplicate Provider: $table, provider: \"$provider\"" + fi + + eval n=\$${t}_number + # + # The following is because the %$#@ shell doesn't accept hex numbers in '-eq' tests + # + if [ $(($n)) -eq $(($number)) ]; then + fatal_error "Duplicate Provider number: $number, provider: \"$provider\"" + fi + done + + eval ${table}_number=$number + + indent >&3 << __EOF__ +# +# Add Provider $table ($number) +# +__EOF__ + save_command "if [ \"\$(find_first_interface_address_if_any $interface)\" != 0.0.0.0 ]; then" + save_indent1="$INDENT" + INDENT="$INDENT " + + iface=$(chain_base $interface) + + save_command "${iface}_up=Yes" + + save_command "qt ip route flush table $number" + + if [ "x${duplicate:=-}" != x- ]; then + if [ "x${copy:=-}" != "x-" ]; then + if [ "x${copy}" = xnone ]; then + copy=$interface + else + copy="$interface $(separate_list $copy)" + fi + copy_and_edit_table + else + copy_table + fi + fi + + if [ "x$gateway" = xdetect ] ; then + gateway='$gateway' + indent >&3 << __EOF__ +gateway=\$(detect_gateway $interface) + +if [ -n "\$gateway" ]; then + run_ip route replace \$gateway src \$(find_first_interface_address $interface) dev $interface table $number + run_ip route add default via \$gateway dev $interface table $number +else + fatal_error "Unable to detect the gateway through interface $interface" +fi + +__EOF__ + elif [ "x$gateway" != "x-" -a -n "$gateway" ]; then + indent >&3 << __EOF__ +run_ip route replace $gateway src \$(find_first_interface_address $interface) dev $interface table $number +run_ip route add default via $gateway dev $interface table $number +__EOF__ + else + gateway= + save_command "run_ip route add default dev $interface table $number" + fi + + if [ x${mark} != x- ]; then + verify_mark $mark + + if [ $(($mark)) -lt 256 ]; then + if [ -n "$HIGH_ROUTE_MARKS" ]; then + fatal_error "Invalid Mark Value ($mark) with HIGH_ROUTE_MARKS=Yes" + fi + elif [ -z "$HIGH_ROUTE_MARKS" ]; then + fatal_error "Invalid Mark Value ($mark) with HIGH_ROUTE_MARKS=No" + fi + + eval ${table}_mark=$mark + + save_command "qt ip rule del fwmark $mark" + save_command "run_ip rule add fwmark $mark pref $((10000 + $mark)) table $number" + fi + + loose= + + for option in $(separate_list $options); do + case $option in + -) + ;; + track) + list_search $interface $ROUTEMARK_INTERFACES && \ + fatal_error "Interface $interface is tracked through an earlier provider" + [ x${mark} = x- ] && fatal_error "The 'track' option requires a numeric value in the MARK column - Provider \"$provider\"" + eval ${iface}_routemark=$mark + ROUTEMARK_INTERFACES="$ROUTEMARK_INTERFACES $interface" + ;; + balance=*) + balance_default_route ${option#*=} + ;; + balance) + balance_default_route 1 + ;; + loose) + loose=Yes + ;; + optional) + optional=Yes + ;; + *) + error_message "WARNING: Invalid option ($option) ignored in provider \"$provider\"" + ;; + esac + done + + rulenum=0 + + if [ -z "$loose" ]; then + rulebase=$(( 20000 + ( 256 * ($number-1) ) )) + indent >&3 << __EOF__ + +rulenum=0 + +find_interface_addresses $interface | while read address; do + qt ip rule del from \$address + run_ip rule add from \$address pref \$(( $rulebase + \$rulenum )) table $number + rulenum=\$((\$rulenum + 1)) +done +__EOF__ + else + indent >&3 << __EOF__ + +find_interface_addresses $interface | while read address; do + qt ip rule del from \$address +done +__EOF__ + fi + + indent >&3 << __EOF__ + +progress_message " Provider $table ($number) Added" + +__EOF__ + + INDENT="$save_indent1" + save_command else + + if [ -n "$optional" ]; then + save_command " error_message \"WARNING: Interface $interface is not configured -- Provider $table ($number) not Added\"" + save_command " ${iface}_up=" + else + save_command " fatal_error \"ERROR: Interface $interface is not configured -- Provider $table ($number) Cannot be Added\"" + fi + + save_command fi + save_command + + } + + verify_provider() + { + local p n + + for p in $PROVIDERS main; do + [ "$p" = "$1" ] && return 0 + eval n=\$${p}_number} + [ "$n" = "$1" ] && return 0 + done + + fatal_error "Unknown provider $1 in route rule \"$rule\"" + } + + add_an_rtrule() + { + verify_provider $provider + + [ "x$source" = x- ] && source= + [ "x$dest" = x- ] && dest= || dest="to $dest" + + [ -n "${source}${dest}" ] || fatal_error "You must specify either the source or destination in an rt rule: \"$rule\"" + + [ -n "$source" ] && case $source in + *:*) + source="iif ${source%:*} from ${source#*:}" + ;; + *.*.*) + source="from $source" + ;; + *) + source="iif $source" + ;; + esac + + case "$priority" in + [0-9][0-9][0-9][0-9]|[0-9][0-9][0-9][0-9][0-9]) + ;; + *) + fatal_error "Invalid priority ($priority) in rule \"$rule\"" + ;; + esac + + priority="priority $priority" + + save_command "qt ip rule del $source $dest $priority" + save_command "run_ip rule add $source $dest $priority table $provider" + + progress_message "Routing rule \"$rule\" $DONE" + } + + local_number=255 + main_number=254 + default_number=253 + unspec_number=0 + + strip_file providers $1 + + if [ -s $TMP_DIR/providers ]; then + balance= + + progress_message2 "$DOING $1..." + save_command + save_command "if [ -z \"\$NOROUTES\" ]; then" + INDENT="$INDENT " + save_progress_message "Adding Providers..." + save_command "DEFAULT_ROUTE=" + + while read table number mark duplicate interface gateway options copy; do + expandv table number mark duplicate interface gateway options copy + provider="$table $number $mark $duplicate $interface $gateway $options $copy" + add_a_provider + PROVIDERS="$PROVIDERS $table" + progress_message "Provider $provider $DONE" + done < $TMP_DIR/providers + + if [ -n "$PROVIDERS" ]; then + if [ -n "$balance" ]; then + save_command "if [ -n \"\$DEFAULT_ROUTE\" ]; then" + save_command " run_ip route replace default scope global \$DEFAULT_ROUTE" + save_command " progress_message \"Default route '\$(echo \$DEFAULT_ROUTE | sed 's/\$\\s*//')' Added\"" + save_command "else" + save_command " error_message \"WARNING: No Default route added (all 'balance' providers are down)\"" + save_command "fi" + save_command + fi + + cat >&3 << __EOF__ +${INDENT}cat > /etc/iproute2/rt_tables <&3 << __EOF__ +\${echobin:-echo} -e "$number\t$table" >> /etc/iproute2/rt_tables +__EOF__ + done + + f=$(find_file route_rules) + + if [ -f $f ]; then + strip_file route_rules $f + + if [ -s $TMP_DIR/route_rules ]; then + progress_message2 "$DOING $f..." + + save_command + + while read source dest provider priority; do + expandv source dest provider priority + rule="$source $dest $priority $provider" + add_an_rtrule + done < $TMP_DIR/route_rules + fi + fi + fi + + save_command "run_ip route flush cache" + INDENT="$save_indent" + save_command "fi" + save_command + fi +} + +# +# Set up Routing +# +setup_routes() +{ + local mask=0xFF mark_op="--set-mark" save_indent="$INDENT" + + [ -n "$HIGH_ROUTE_MARKS" ] && mask=0xFF00 && mark_op="--or-mark" + + run_iptables -t mangle -A PREROUTING -m connmark ! --mark 0/$mask -j CONNMARK --restore-mark --mask $mask + run_iptables -t mangle -A OUTPUT -m connmark ! --mark 0/$mask -j CONNMARK --restore-mark --mask $mask + createmanglechain routemark + + if [ -n "$ROUTEMARK_INTERFACES" ]; then + for interface in $ROUTEMARK_INTERFACES ; do + iface=$(chain_base $interface) + eval mark_value=\$${iface}_routemark + + save_command + save_command "if [ -n \"\$${iface}_up\" ]; then" + INDENT="$INDENT " + run_iptables -t mangle -A PREROUTING -i $interface -m mark --mark 0/$mask -j routemark + run_iptables -t mangle -A routemark -i $interface -j MARK $mark_op $mark_value + INDENT="$save_indent" + save_command "fi" + done + + save_command + fi + + run_iptables -t mangle -A routemark -m mark ! --mark 0/$mask -j CONNMARK --save-mark --mask $mask + +} + +CLIB_PROVIDERS_LOADED=Yes diff --git a/Shorewall/clib.proxyarp b/Shorewall/clib.proxyarp new file mode 100644 index 000000000..72e2fb3fe --- /dev/null +++ b/Shorewall/clib.proxyarp @@ -0,0 +1,154 @@ +#!/bin/sh +# +# Shorewall 3.2 -- /usr/share/shorewall/clib.proxyarp +# +# This program is under GPL [http://www.gnu.org/copyleft/gpl.htm] +# +# (c) 2005,2006 - Tom Eastep (teastep@shorewall.net) +# +# Complete documentation is available at http://shorewall.net +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of Version 2 of the GNU General Public License +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA + +# +# Setup Proxy ARP +# +setup_proxy_arp() { + + local setlist= resetlist= + + print_error() { + error_message "Invalid value for HAVEROUTE - ($haveroute)" + error_message "Entry \"$address $interface $external $haveroute\" ignored" + } + + print_error1() { + error_message "Invalid value for PERSISTENT - ($persistent)" + error_message "Entry \"$address $interface $external $haveroute $persistent\" ignored" + } + + print_warning() { + error_message "PERSISTENT setting ignored - ($persistent)" + error_message "Entry \"$address $interface $external $haveroute $persistent\"" + } + + setup_one_proxy_arp() { + + case $haveroute in + [Nn][Oo]) + haveroute= + ;; + [Yy][Ee][Ss]) + ;; + *) + if [ -n "$haveroute" ]; then + print_error + return + fi + ;; + esac + + case $persistent in + [Nn][Oo]) + persistent= + ;; + [Yy][Ee][Ss]) + [ -z "$haveroute" ] || print_warning + ;; + *) + if [ -n "$persistent" ]; then + print_error1 + return + fi + ;; + esac + + if [ -z "$haveroute" ]; then + save_command "[ -n \"\$NOROUTES\" ] || run_ip route replace $address dev $interface" + [ -n "$persistent" ] && haveroute=yes + fi + + indent >&3 << __EOF__ +if ! arp -i $external -Ds $address $external pub; then + fatal_error "Command \"arp -i $external -Ds $address $external pub\" failed" +fi + +progress_message " Host $address connected to $interface added to ARP on $external" + +__EOF__ + echo $address $interface $external $haveroute >> $STATEDIR/proxyarp + + progress_message " Host $address connected to $interface added to ARP on $external" + } + + > $STATEDIR/proxyarp + + save_progress_message "Setting up Proxy ARP..." + + while read address interface external haveroute persistent; do + expandv address interface external haveroute persistent + list_search $interface $setlist || setlist="$setlist $interface" + list_search $external $resetlist || list_search $external $setlist || resetlist="$resetlist $external" + setup_one_proxy_arp + done < $TMP_DIR/proxyarp + + for interface in $resetlist; do + list_search $interface $setlist || \ + save_command "echo 0 > /proc/sys/net/ipv4/conf/$interface/proxy_arp" + done + + for interface in $setlist; do + save_command "echo 1 > /proc/sys/net/ipv4/conf/$interface/proxy_arp" + done + + interfaces=$(find_interfaces_by_option proxyarp) + + for interface in $interfaces; do + indent >&3 << __EOF__ +if [ -f /proc/sys/net/ipv4/conf/$interface/proxy_arp ] ; then + echo 1 > /proc/sys/net/ipv4/conf/$interface/proxy_arp +else + error_message "WARNING: Unable to enable proxy ARP on $interface" +fi + +__EOF__ + done + +} + +# +# Delete existing Proxy ARP +# +delete_proxy_arp() { + indent >&3 << __EOF__ +if [ -f \${VARDIR}/proxyarp ]; then + while read address interface external haveroute; do + qt arp -i \$external -d \$address pub + [ -z "\$haveroute" -a -z "\$NOROUTE" ] && qt ip route del \$address dev \$interface + done < \${VARDIR}/proxyarp + + rm -f \${VARDIR}/proxyarp +fi + +for f in /proc/sys/net/ipv4/conf/*; do + [ -f \$f/proxy_arp ] && echo 0 > \$f/proxy_arp +done + +__EOF__ + + [ -d $STATEDIR ] && touch $STATEDIR/proxyarp + + } + +CLIB_PROXYARP_LOADED=Yes diff --git a/Shorewall/clib.tcrules b/Shorewall/clib.tcrules new file mode 100644 index 000000000..f94b92623 --- /dev/null +++ b/Shorewall/clib.tcrules @@ -0,0 +1,134 @@ +#!/bin/sh +# +# Shorewall 3.2 -- /usr/share/shorewall/clib.tcrules +# +# This program is under GPL [http://www.gnu.org/copyleft/gpl.htm] +# +# (c) 2005,2006 - Tom Eastep (teastep@shorewall.net) +# +# Complete documentation is available at http://shorewall.net +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of Version 2 of the GNU General Public License +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA + +# +# Generate a command to run tc +# +run_tc() { + save_command run_tc $@ +} + +# +# Setup queuing and classes +# +setup_tc1() { + local mark_part= + # + # Create the TC mangle chains + # + + createmanglechain tcpre + + if [ -n "$MANGLE_FORWARD" ]; then + createmanglechain tcfor + createmanglechain tcpost + fi + + createmanglechain tcout + # + # Process the TC Rules File + # + strip_file tcrules + + while read mark sources dests proto ports sports user testval length tos; do + expandv mark sources dests proto ports sports user testval length tos + rule=$(echo "$mark $sources $dests $proto $ports $sports $user $testval $length $tos") + process_tc_rule + done < $TMP_DIR/tcrules + # + # Link to the TC mangle chains from the main chains + # + + # + # Route marks are restored in PREROUTING/OUTPUT prior to these rules. We only send + # packets that are not part of a marked connection to the 'tcpre/tcout' chains. + # + if [ -n "$ROUTEMARK_INTERFACES" -a -z "$TC_EXPERT" ]; then + mark_part="-m mark --mark 0/0xFF00" + # + # But let marks in tcpre override those assigned by 'track' + # + for interface in $ROUTEMARK_INTERFACES; do + run_iptables -t mangle -A PREROUTING -i $interface -j tcpre + done + fi + + run_iptables -t mangle -A PREROUTING $mark_part -j tcpre + run_iptables -t mangle -A OUTPUT $mark_part -j tcout + + if [ -n "$MANGLE_FORWARD" ]; then + run_iptables -t mangle -A FORWARD -j tcfor + run_iptables -t mangle -A POSTROUTING -j tcpost + fi + + if [ -n "$HIGH_ROUTE_MARKS" ]; then + for chain in INPUT FORWARD; do + run_iptables -t mangle -I $chain -j MARK --and-mark 0xFF + done + fi + + if [ -n "$TC_SCRIPT" ]; then + save_progress_message "Setting up Traffic Control..." + append_file $TC_SCRIPT + elif [ -n "$TC_ENABLED" ]; then + setup_traffic_shaping + fi +} + +setup_tc() { + + progress_message2 "$DOING Traffic Control Rules..." + + setup_tc1 +} + +# +# Clear Traffic Shaping +# +delete_tc() +{ + clear_one_tc() { + save_command "tc qdisc del dev $1 root 2> /dev/null" + save_command "tc qdisc del dev $1 ingress 2> /dev/null" + + } + + save_progress_message "Clearing Traffic Control/QOS" + + append_file tcclear + + indent >&3 << __EOF__ +ip link list | while read inx interface details; do + case \$inx in + [0-9]*) + qt tc qdisc del dev \${interface%:} root + qt tc qdisc del dev \${interface%:} ingress + ;; + *) + ;; + esac +done +__EOF__ +} + +CLIB_TCRULES_LOADED=Yes diff --git a/Shorewall/clib.tos b/Shorewall/clib.tos new file mode 100644 index 000000000..25757edcd --- /dev/null +++ b/Shorewall/clib.tos @@ -0,0 +1,218 @@ +#!/bin/sh +# +# Shorewall 3.2 -- /usr/share/shorewall/clib.tos +# +# This program is under GPL [http://www.gnu.org/copyleft/gpl.htm] +# +# (c) 2005,2006 - Tom Eastep (teastep@shorewall.net) +# +# Complete documentation is available at http://shorewall.net +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of Version 2 of the GNU General Public License +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA + +# +# Process a record from the tos file +# +# The caller has loaded the column contents from the record into the following +# variables: +# +# src dst protocol sport dport tos +# +# and has loaded a space-separated list of their values in "rule". +# +process_tos_rule() { + # + # Parse the contents of the 'src' variable + # + if [ "$src" = "${src%:*}" ]; then + srczone="$src" + src= + else + srczone="${src%:*}" + src="${src#*:}" + fi + + source= + # + # Validate the source zone + # + if validate_zone $srczone; then + source=$srczone + elif [ "$srczone" = "all" ]; then + source="all" + else + error_message "WARNING: Undefined Source Zone - rule \"$rule\" ignored" + return + fi + + [ -n "$src" ] && case "$src" in + *.*.*|+*|!+*) + # + # IP Address or networks + # + src="$(source_ip_range $src)" + ;; + ~*|!~*) + src=$(mac_match $src) + ;; + *) + # + # Assume that this is a device name + # + if ! verify_interface $src ; then + error_message "WARNING: Unknown Interface in rule \"$rule\" ignored" + return + fi + + src="$(match_source_dev $src)" + ;; + esac + + # + # Parse the contents of the 'dst' variable + # + if [ "$dst" = "${dst%:*}" ]; then + dstzone="$dst" + dst= + else + dstzone="${dst%:*}" + dst="${dst#*:}" + fi + + dest= + # + # Validate the destination zone + # + if validate_zone $dstzone; then + dest=$dstzone + elif [ "$dstzone" = "all" ]; then + dest="all" + else + error_message \ + "WARNING: Undefined Destination Zone - rule \"$rule\" ignored" + return + fi + + [ -n "$dst" ] && case "$dst" in + *.*.*|+*|!+*) + # + # IP Address or networks + # + ;; + *) + # + # Assume that this is a device name + # + error_message \ + "WARNING: Invalid Destination - rule \"$rule\" ignored" + return + ;; + esac + + # + # Setup PROTOCOL and PORT variables + # + sports="" + dports="" + + case $protocol in + tcp|udp|TCP|UDP|6|17) + [ -n "$sport" ] && [ "x${sport}" != "x-" ] && \ + sports="--sport $sport" + [ -n "$dport" ] && [ "x${dport}" != "x-" ] && \ + dports="--dport $dport" + ;; + icmp|ICMP|0) + [ -n "$dport" ] && [ "x${dport}" != "x-" ] && \ + dports="--icmp-type $dport" + ;; + all|ALL) + protocol= + ;; + *) + ;; + esac + + protocol="${protocol:+-p $protocol}" + + tos="-j TOS --set-tos $tos" + + case "$dstzone" in + all|ALL) + dst=0.0.0.0/0 + ;; + *) + [ -z "$dst" ] && eval dst=\$${dstzone}_hosts + ;; + esac + + for dest in $dst; do + dest="$(dest_ip_range $dest)" + + case $srczone in + $FW) + run_iptables2 -t mangle -A outtos \ + $protocol $dest $dports $sports $tos + ;; + all|ALL) + run_iptables2 -t mangle -A outtos \ + $protocol $dest $dports $sports $tos + run_iptables2 -t mangle -A pretos \ + $protocol $dest $dports $sports $tos + ;; + *) + if [ -n "$src" ]; then + run_iptables2 -t mangle -A pretos $src \ + $protocol $dest $dports $sports $tos + else + eval interfaces=\$${srczone}_interfaces + + for interface in $interfaces; do + run_iptables2 -t mangle -A pretos -i $interface \ + $protocol $dest $dports $sports $tos + done + fi + ;; + esac + done + + progress_message " Rule \"$rule\" $DONE." + save_progress_message "Rule \"$rule\" Added." +} + +# +# Process the tos file +# +process_tos() # $1 = name of tos file +{ + progress_message2 "$DOING $1..." + + strip_file tos $1 + + if [ -s $TMP_DIR/tos ] ; then + createmanglechain pretos + createmanglechain outtos + + while read src dst protocol sport dport tos; do + expandv src dst protocol sport dport tos + rule="$(echo $src $dst $protocol $sport $dport $tos)" + process_tos_rule + done < $TMP_DIR/tos + + run_iptables -t mangle -A PREROUTING -j pretos + run_iptables -t mangle -A OUTPUT -j outtos + fi +} + +CLIB_TOS_LOADED=Yes diff --git a/Shorewall/clib.tunnels b/Shorewall/clib.tunnels new file mode 100644 index 000000000..d8462ad63 --- /dev/null +++ b/Shorewall/clib.tunnels @@ -0,0 +1,655 @@ +#!/bin/sh +# +# Shorewall 3.2 -- /usr/share/shorewall/clib.tunnels +# +# This program is under GPL [http://www.gnu.org/copyleft/gpl.htm] +# +# (c) 2002,2003,2004,2005,2006 - Tom Eastep (teastep@shorewall.net) +# +# Complete documentation is available at http://shorewall.net +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of Version 2 of the GNU General Public License +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA + +# +# Process the ipsec information in the zones file +# +setup_ipsec() { + local zone using_ipsec= + # + # Add a --set-mss rule to the passed chain + # + set_mss1() # $1 = chain, $2 = MSS + { + eval local policy=\$${1}_policy + + if [ "$policy" != NONE ]; then + ensurechain $1 + run_iptables -I $1 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss $2 + fi + } + # + # Set up rules to set MSS to and/or from zone "$zone" + # + set_mss() # $1 = MSS value, $2 = _in, _out or "" + { + for z in $ZONES; do + case $2 in + _in) + set_mss1 ${zone}2${z} $1 + ;; + _out) + set_mss1 ${z}2${zone} $1 + ;; + *) + set_mss1 ${z}2${zone} $1 + set_mss1 ${zone}2${z} $1 + ;; + esac + done +} + + do_options() # $1 = _in, _out or "" - $2 = option list + { + local option newoptions= val + + [ x${2} = x- ] && return + + for option in $(separate_list $2); do + val=${option#*=} + + case $option in + mss=[0-9]*) set_mss $val $1 ;; + strict) newoptions="$newoptions --strict" ;; + next) newoptions="$newoptions --next" ;; + reqid=*) newoptions="$newoptions --reqid $val" ;; + spi=*) newoptions="$newoptions --spi $val" ;; + proto=*) newoptions="$newoptions --proto $val" ;; + mode=*) newoptions="$newoptions --mode $val" ;; + tunnel-src=*) newoptions="$newoptions --tunnel-src $val" ;; + tunnel-dst=*) newoptions="$newoptions --tunnel-dst $val" ;; + reqid!=*) newoptions="$newoptions ! --reqid $val" ;; + spi!=*) newoptions="$newoptions ! --spi $val" ;; + proto!=*) newoptions="$newoptions ! --proto $val" ;; + mode!=*) newoptions="$newoptions ! --mode $val" ;; + tunnel-src!=*) newoptions="$newoptions ! --tunnel-src $val" ;; + tunnel-dst!=*) newoptions="$newoptions ! --tunnel-dst $val" ;; + *) fatal_error "Invalid option \"$option\" for zone $zone" ;; + esac + done + + if [ -n "$newoptions" ]; then + [ -n "$POLICY_MATCH" ] || fatal_error "Your kernel and/or iptables does not support policy match" + eval ${zone}_is_complex=Yes + eval ${zone}_ipsec${1}_options=\"${newoptions# }\" + fi + } + + case $IPSECFILE in + zones) + f=zones + progress_message2 "$DOING IPSEC..." + ;; + *) + f=$IPSECFILE + strip_file $f + progress_message2 "$DOING $f..." + using_ipsec=Yes + ;; + esac + + while read zone type options in_options out_options mss; do + expandv zone type options in_options out_options mss + + if [ -n "$using_ipsec" ]; then + validate_zone1 $zone || fatal_error "Unknown zone: $zone" + fi + + if [ -n "$type" ]; then + if [ -n "$using_ipsec" ]; then + case $type in + No|no) + ;; + Yes|yes) + [ -n "$POLICY_MATCH" ] || fatal_error "Your kernel and/or iptables does not support policy match" + eval ${zone}_is_ipsec=Yes + eval ${zone}_is_complex=Yes + eval ${zone}_type=ipsec4 + ;; + *) + fatal_error "Invalid IPSEC column contents" + ;; + esac + fi + + do_options "" $options + do_options "_in" $in_options + do_options "_out" $out_options + fi + + done < $TMP_DIR/$f +} + +# +# Set up tunnels +# +setup_tunnels() # $1 = name of tunnels file +{ + local inchain + local outchain + local source + local dest + + setup_one_ipsec() # $1 = Tunnel Kind $2 = gateway zones + { + local kind=$1 noah= + + case $kind in + *:*) + noah=${kind#*:} + [ $noah = noah -o $noah = NOAH ] || fatal_error "Invalid IPSEC modifier $noah in tunnel \"$tunnel\"" + kind=${kind%:*} + ;; + esac + + [ $kind = IPSEC ] && kind=ipsec + + options="-m state --state NEW -j ACCEPT" + addrule2 $inchain -p 50 $source -j ACCEPT + addrule2 $outchain -p 50 $dest -j ACCEPT + + if [ -z "$noah" ]; then + run_iptables -A $inchain -p 51 $source -j ACCEPT + run_iptables -A $outchain -p 51 $dest -j ACCEPT + fi + + run_iptables -A $outchain -p udp $dest --dport 500 $options + + if [ $kind = ipsec ]; then + run_iptables -A $inchain -p udp $source --dport 500 $options + else + run_iptables -A $inchain -p udp $source --dport 500 $options + run_iptables -A $inchain -p udp $source --dport 4500 $options + fi + + for z in $(separate_list $2); do + if validate_zone $z; then + if [ -z "$POLICY_MATCH" ]; then + addrule ${z}2${FW} -p 50 $source -j ACCEPT + addrule ${FW}2${z} -p 50 $dest -j ACCEPT + if [ -z "$noah" ]; then + addrule ${z}2${FW} -p 51 $source -j ACCEPT + addrule ${FW}2${z} -p 51 $dest -j ACCEPT + fi + fi + if [ $kind = ipsec ]; then + addrule ${z}2${FW} -p udp $source --dport 500 $options + addrule ${FW}2${z} -p udp $dest --dport 500 $options + else + addrule ${z}2${FW} -p udp $source --dport 500 $options + addrule ${FW}2${z} -p udp $dest --dport 500 $options + addrule ${z}2${FW} -p udp $source --dport 4500 $options + addrule ${FW}2${z} -p udp $dest --dport 4500 $options + fi + else + fatal_error "Invalid gateway zone ($z) -- Tunnel \"$tunnel\"" + fi + done + + progress_message_and_save " IPSEC tunnel to $gateway defined." + } + + setup_one_other() # $1 = TYPE, $2 = protocol + { + addrule2 $inchain -p $2 $source -j ACCEPT + addrule2 $outchain -p $2 $dest -j ACCEPT + + progress_message_and_save " $1 tunnel to $gateway compiled." + } + + setup_pptp_client() + { + addrule2 $outchain -p 47 $dest -j ACCEPT + addrule2 $inchain -p 47 $source -j ACCEPT + addrule2 $outchain -p tcp --dport 1723 $dest -j ACCEPT + + progress_message_and_save " PPTP tunnel to $gateway defined." + } + + setup_pptp_server() + { + addrule2 $inchain -p 47 $source -j ACCEPT + addrule2 $outchain -p 47 $dest -j ACCEPT + addrule2 $inchain -p tcp --dport 1723 $source -j ACCEPT + + progress_message_and_save " PPTP server defined." + } + + setup_one_openvpn() # $1 = kind[:port] + { + local protocol=udp + local p=1194 + + case $1 in + *:*:*) + protocol=${1%:*} + protocol=${protocol#*:} + p=${1##*:} + ;; + *:tcp|*:udp|*:TCP|*:UDP) + protocol=${1#*:} + ;; + *:*) + p=${1#*:} + ;; + esac + + addrule2 $inchain -p $protocol $source --dport $p -j ACCEPT + addrule2 $outchain -p $protocol $dest --dport $p -j ACCEPT + + progress_message_and_save " OPENVPN tunnel to $gateway:$protocol:$p defined." + } + + setup_one_openvpn_server() # $1 = kind[:port] + { + local protocol=udp + local p=1194 + + case $1 in + *:*:*) + protocol=${1%:*} + protocol=${protocol#*:} + p=${1##*:} + ;; + *:tcp|*:udp|*:TCP|*:UDP) + protocol=${1#*:} + ;; + *:*) + p=${1#*:} + ;; + esac +# +# Set up ipsec tunnels +# +setup_tunnels() # $1 = name of tunnels file +{ + local inchain + local outchain + local source + local dest + + setup_one_ipsec() # $1 = Tunnel Kind $2 = gateway zones + { + local kind=$1 noah= + + case $kind in + *:*) + noah=${kind#*:} + [ $noah = noah -o $noah = NOAH ] || fatal_error "Invalid IPSEC modifier $noah in tunnel \"$tunnel\"" + kind=${kind%:*} + ;; + esac + + [ $kind = IPSEC ] && kind=ipsec + + options="-m state --state NEW -j ACCEPT" + addrule2 $inchain -p 50 $source -j ACCEPT + addrule2 $outchain -p 50 $dest -j ACCEPT + + if [ -z "$noah" ]; then + run_iptables -A $inchain -p 51 $source -j ACCEPT + run_iptables -A $outchain -p 51 $dest -j ACCEPT + fi + + run_iptables -A $outchain -p udp $dest --dport 500 $options + + if [ $kind = ipsec ]; then + run_iptables -A $inchain -p udp $source --dport 500 $options + else + run_iptables -A $inchain -p udp $source --dport 500 $options + run_iptables -A $inchain -p udp $source --dport 4500 $options + fi + + for z in $(separate_list $2); do + if validate_zone $z; then + if [ -z "$POLICY_MATCH" ]; then + addrule ${z}2${FW} -p 50 $source -j ACCEPT + addrule ${FW}2${z} -p 50 $dest -j ACCEPT + if [ -z "$noah" ]; then + addrule ${z}2${FW} -p 51 $source -j ACCEPT + addrule ${FW}2${z} -p 51 $dest -j ACCEPT + fi + fi + if [ $kind = ipsec ]; then + addrule ${z}2${FW} -p udp $source --dport 500 $options + addrule ${FW}2${z} -p udp $dest --dport 500 $options + else + addrule ${z}2${FW} -p udp $source --dport 500 $options + addrule ${FW}2${z} -p udp $dest --dport 500 $options + addrule ${z}2${FW} -p udp $source --dport 4500 $options + addrule ${FW}2${z} -p udp $dest --dport 4500 $options + fi + else + fatal_error "Invalid gateway zone ($z) -- Tunnel \"$tunnel\"" + fi + done + + progress_message_and_save " IPSEC tunnel to $gateway defined." + } + + setup_one_other() # $1 = TYPE, $2 = protocol + { + addrule2 $inchain -p $2 $source -j ACCEPT + addrule2 $outchain -p $2 $dest -j ACCEPT + + progress_message_and_save " $1 tunnel to $gateway compiled." + } + + setup_pptp_client() + { + addrule2 $outchain -p 47 $dest -j ACCEPT + addrule2 $inchain -p 47 $source -j ACCEPT + addrule2 $outchain -p tcp --dport 1723 $dest -j ACCEPT + + progress_message_and_save " PPTP tunnel to $gateway defined." + } + + setup_pptp_server() + { + addrule2 $inchain -p 47 $source -j ACCEPT + addrule2 $outchain -p 47 $dest -j ACCEPT + addrule2 $inchain -p tcp --dport 1723 $source -j ACCEPT + + progress_message_and_save " PPTP server defined." + } + + setup_one_openvpn() # $1 = kind[:port] + { + local protocol=udp + local p=1194 + + case $1 in + *:*:*) + protocol=${1%:*} + protocol=${protocol#*:} + p=${1##*:} + ;; + *:tcp|*:udp|*:TCP|*:UDP) + protocol=${1#*:} + ;; + *:*) + p=${1#*:} + ;; + esac + + addrule2 $inchain -p $protocol $source --dport $p -j ACCEPT + addrule2 $outchain -p $protocol $dest --dport $p -j ACCEPT + + progress_message_and_save " OPENVPN tunnel to $gateway:$protocol:$p defined." + } + + setup_one_openvpn_server() # $1 = kind[:port] + { + local protocol=udp + local p=1194 + + case $1 in + *:*:*) + protocol=${1%:*} + protocol=${protocol#*:} + p=${1##*:} + ;; + *:tcp|*:udp|*:TCP|*:UDP) + protocol=${1#*:} + ;; + *:*) + p=${1#*:} + ;; + esac + + addrule2 $inchain -p $protocol $source --dport $p -j ACCEPT + addrule2 $outchain -p $protocol $dest --sport $p -j ACCEPT + + progress_message_and_save " OPENVPN server tunnel from $gateway:$protocol:$p defined." + } + + setup_one_openvpn_client() # $1 = kind[:port] + { + local protocol=udp + local p=1194 + + case $1 in + *:*:*) + protocol=${1%:*} + protocol=${protocol#*:} + p=${1##*:} + ;; + *:tcp|*:udp|*:TCP|*:UDP) + protocol=${1#*:} + ;; + *:*) + p=${1#*:} + ;; + esac + + addrule2 $inchain -p $protocol $source --sport $p -j ACCEPT + addrule2 $outchain -p $protocol $dest --dport $p -j ACCEPT + + progress_message_and_save " OPENVPN client tunnel to $gateway:$protocol:$p defined." + } + + setup_one_generic() # $1 = kind:protocol[:port] + { + local protocol + local p= + + case $1 in + *:*:*) + p=${1##*:} + protocol=${1%:*} + protocol=${protocol#*:} + ;; + *:*) + protocol=${1#*:} + ;; + *) + protocol=udp + p=5000 + ;; + esac + + p=${p:+--dport $p} + + addrule2 $inchain -p $protocol $source $p -j ACCEPT + addrule2 $outchain -p $protocol $dest $p -j ACCEPT + + progress_message_and_save " GENERIC tunnel to $1:$p defined." + } + + strip_file tunnels $1 + + while read kind z gateway z1; do + expandv kind z gateway z1 + tunnel="$(echo $kind $z $gateway $z1)" + if validate_zone $z; then + inchain=${z}2${FW} + outchain=${FW}2${z} + gateway=${gateway:-0.0.0.0/0} + source=$(source_ip_range $gateway) + dest=$(dest_ip_range $gateway) + + case $kind in + ipsec|IPSEC|ipsec:*|IPSEC:*) + setup_one_ipsec $kind $z1 + ;; + ipsecnat|IPSECNAT|ipsecnat:*|IPSECNAT:*) + setup_one_ipsec $kind $z1 + ;; + ipip|IPIP) + setup_one_other IPIP 4 + ;; + gre|GRE) + setup_one_other GRE 47 + ;; + 6to4|6TO4) + setup_one_other 6to4 41 + ;; + pptpclient|PPTPCLIENT) + setup_pptp_client + ;; + pptpserver|PPTPSERVER) + setup_pptp_server + ;; + openvpn|OPENVPN|openvpn:*|OPENVPN:*) + setup_one_openvpn $kind + ;; + openvpnclient|OPENVPNCLIENT|openvpnclient:*|OPENVPNCLIENT:*) + setup_one_openvpn_client $kind + ;; + openvpnserver|OPENVPNSERVER|openvpnserver:*|OPENVPNSERVER:*) + setup_one_openvpn_server $kind + ;; + generic:*|GENERIC:*) + setup_one_generic $kind + ;; + *) + error_message "WARNING: Tunnels of type $kind are not supported:" \ + "Tunnel \"$tunnel\" Ignored" + ;; + esac + else + error_message "ERROR: Invalid gateway zone ($z)" \ + " -- Tunnel \"$tunnel\" Ignored" + fi + done < $TMP_DIR/tunnels +} + + + addrule2 $inchain -p $protocol $source --dport $p -j ACCEPT + addrule2 $outchain -p $protocol $dest --sport $p -j ACCEPT + + progress_message_and_save " OPENVPN server tunnel from $gateway:$protocol:$p defined." + } + + setup_one_openvpn_client() # $1 = kind[:port] + { + local protocol=udp + local p=1194 + + case $1 in + *:*:*) + protocol=${1%:*} + protocol=${protocol#*:} + p=${1##*:} + ;; + *:tcp|*:udp|*:TCP|*:UDP) + protocol=${1#*:} + ;; + *:*) + p=${1#*:} + ;; + esac + + addrule2 $inchain -p $protocol $source --sport $p -j ACCEPT + addrule2 $outchain -p $protocol $dest --dport $p -j ACCEPT + + progress_message_and_save " OPENVPN client tunnel to $gateway:$protocol:$p defined." + } + + setup_one_generic() # $1 = kind:protocol[:port] + { + local protocol + local p= + + case $1 in + *:*:*) + p=${1##*:} + protocol=${1%:*} + protocol=${protocol#*:} + ;; + *:*) + protocol=${1#*:} + ;; + *) + protocol=udp + p=5000 + ;; + esac + + p=${p:+--dport $p} + + addrule2 $inchain -p $protocol $source $p -j ACCEPT + addrule2 $outchain -p $protocol $dest $p -j ACCEPT + + progress_message_and_save " GENERIC tunnel to $1:$p defined." + } + + strip_file tunnels $1 + + while read kind z gateway z1; do + expandv kind z gateway z1 + tunnel="$(echo $kind $z $gateway $z1)" + if validate_zone $z; then + inchain=${z}2${FW} + outchain=${FW}2${z} + gateway=${gateway:-0.0.0.0/0} + source=$(source_ip_range $gateway) + dest=$(dest_ip_range $gateway) + + case $kind in + ipsec|IPSEC|ipsec:*|IPSEC:*) + setup_one_ipsec $kind $z1 + ;; + ipsecnat|IPSECNAT|ipsecnat:*|IPSECNAT:*) + setup_one_ipsec $kind $z1 + ;; + ipip|IPIP) + setup_one_other IPIP 4 + ;; + gre|GRE) + setup_one_other GRE 47 + ;; + 6to4|6TO4) + setup_one_other 6to4 41 + ;; + pptpclient|PPTPCLIENT) + setup_pptp_client + ;; + pptpserver|PPTPSERVER) + setup_pptp_server + ;; + openvpn|OPENVPN|openvpn:*|OPENVPN:*) + setup_one_openvpn $kind + ;; + openvpnclient|OPENVPNCLIENT|openvpnclient:*|OPENVPNCLIENT:*) + setup_one_openvpn_client $kind + ;; + openvpnserver|OPENVPNSERVER|openvpnserver:*|OPENVPNSERVER:*) + setup_one_openvpn_server $kind + ;; + generic:*|GENERIC:*) + setup_one_generic $kind + ;; + *) + error_message "WARNING: Tunnels of type $kind are not supported:" \ + "Tunnel \"$tunnel\" Ignored" + ;; + esac + else + error_message "ERROR: Invalid gateway zone ($z)" \ + " -- Tunnel \"$tunnel\" Ignored" + fi + done < $TMP_DIR/tunnels +} + +CLIB_TUNNELS_LOADED=Yes diff --git a/Shorewall/install.sh b/Shorewall/install.sh index fc46e7696..47411c714 100755 --- a/Shorewall/install.sh +++ b/Shorewall/install.sh @@ -628,7 +628,6 @@ fi run_install $OWNERSHIP -m 0644 Makefile ${PREFIX}/usr/share/shorewall/configfiles/Makefile run_install $OWNERSHIP -m 0600 Makefile ${PREFIX}/etc/shorewall/Makefile echo "Makefile installed as ${PREFIX}/etc/shorewall/Makefile" - # # Install the Action files # @@ -636,10 +635,24 @@ for f in action.* ; do install_file $f ${PREFIX}/usr/share/shorewall/$f 0644 echo "Action ${f#*.} file installed as ${PREFIX}/usr/share/shorewall/$f" done - +# install_file Limit ${PREFIX}/usr/share/shorewall/Limit 0644 echo "Limit action extension script installed as ${PREFIX}/usr/share/shorewall/Limit" # +# Install the Compiler Library files +# +for f in clib.* ; do + install_file $f ${PREFIX}/usr/share/shorewall/$f 0555 + echo "Compiler library ${f#*.} installed as ${PREFIX}/usr/share/shorewall/$f" +done +# +# Install the Common Library files +# +for f in lib.* ; do + install_file $f ${PREFIX}/usr/share/shorewall/$f 0555 + echo "Library ${f#*.} installed as ${PREFIX}/usr/share/shorewall/$f" +done +# # Install the Macro files # for f in macro.* ; do diff --git a/Shorewall/lib.base b/Shorewall/lib.base new file mode 100644 index 000000000..c3d61b24b --- /dev/null +++ b/Shorewall/lib.base @@ -0,0 +1,1578 @@ +#!/bin/sh +# +# Shorewall 3.2 -- /usr/share/shorewall/lib.base +# +# This program is under GPL [http://www.gnu.org/copyleft/gpl.htm] +# +# (c) 1999,2000,2001,2002,2003,2004,2005,2006 - Tom Eastep (teastep@shorewall.net) +# +# Complete documentation is available at http://shorewall.net +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of Version 2 of the GNU General Public License +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA + +# +# Message to stderr +# +error_message() # $* = Error Message +{ + echo " $@" >&2 +} + +# Function to truncate a string -- It uses 'cut -b -' +# rather than ${v:first:last} because light-weight shells like ash and +# dash do not support that form of expansion. +# + +truncate() # $1 = length +{ + cut -b -${1} +} + +# +# Split a colon-separated list into a space-separated list +# +split() { + local ifs=$IFS + IFS=: + set -- $1 + echo $* + IFS=$ifs +} + +# +# Search a list looking for a match -- returns zero if a match found +# 1 otherwise +# +list_search() # $1 = element to search for , $2-$n = list +{ + local e=$1 + + while [ $# -gt 1 ]; do + shift + [ "x$e" = "x$1" ] && return 0 + done + + return 1 +} + +# +# Return a space separated list of values matching +# +list_walk() # $1 = element to search for, $2-$n = list +{ + local e=$1 result= + + while [ $# -gt 1 ]; do + shift + case $1 in + $e*) + result="$result ${1##$e}" + ;; + esac + done + echo $result +} + +# +# Functions to count list elements +# - - - - - - - - - - - - - - - - +# Whitespace-separated list +# +list_count1() { + echo $# +} +# +# Comma-separated list +# +list_count() { + list_count1 $(separate_list $1) +} + +# +# Conditionally produce message +# +progress_message() # $* = Message +{ + local timestamp= + + if [ $VERBOSE -gt 1 ]; then + [ -n "$TIMESTAMP" ] && timestamp="$(date +%H:%M:%S) " + echo "${timestamp}$@" + fi +} + +progress_message2() # $* = Message +{ + local timestamp= + + if [ $VERBOSE -gt 0 ]; then + [ -n "$TIMESTAMP" ] && timestamp="$(date +%H:%M:%S) " + echo "${timestamp}$@" + fi +} + +progress_message3() # $* = Message +{ + local timestamp= + + if [ $VERBOSE -ge 0 ]; then + [ -n "$TIMESTAMP" ] && timestamp="$(date +%H:%M:%S) " + echo "${timestamp}$@" + fi +} + +# +# Suppress all output for a command +# +qt() +{ + "$@" >/dev/null 2>&1 +} + +# +# Determine if Shorewall is "running" +# +shorewall_is_started() { + qt $IPTABLES -L shorewall -n +} + +# +# Perform variable substitution on the passed argument and echo the result +# +expand() # $@ = contents of variable which may be the name of another variable +{ + eval echo \"$@\" +} + +# +# Perform variable substitition on the values of the passed list of variables +# +expandv() # $* = list of variable names +{ + local varval + + while [ $# -gt 0 ]; do + eval varval=\$${1} + eval $1=\"$varval\" + shift + done +} + +# +# Add whitespace after "!" +# +fix_bang() +{ + local result= + + while [ $# -gt 0 ]; do + case $1 in + !*) + result="$result ! ${1#!}" + ;; + *) + result="$result $1" + ;; + esac + shift + done + + echo $result +} + +# +# Echos the fully-qualified name of the calling shell program +# +my_pathname() { + cd $(dirname $0) + echo $PWD/$(basename $0) +} + +# +# Set default config path +# +ensure_config_path() { + local F=${SHAREDIR}/configpath + if [ -z "$CONFIG_PATH" ]; then + [ -f $F ] || { echo " ERROR: $F does not exist"; exit 2; } + . $F + fi +} + +# +# Find a File -- For relative file name, look first in $SHOREWALL_DIR then in ${CONFDIR} +# +find_file() +{ + local saveifs= directory + + case $1 in + /*) + echo $1 + ;; + *) + if [ -n "$SHOREWALL_DIR" -a -f $SHOREWALL_DIR/$1 ]; then + echo $SHOREWALL_DIR/$1 + else + saveifs=$IFS + IFS=: + for directory in $CONFIG_PATH; do + if [ -f $directory/$1 ]; then + echo $directory/$1 + IFS=$saveifs + return + fi + done + + IFS=$saveifs + + echo ${CONFDIR}/$1 + fi + ;; + esac +} + +# +# Get fully-qualified name of file +# +resolve_file() # $1 = file name +{ + local pwd=$PWD + + case $1 in + /*) + echo $1 + ;; + ./*) + echo ${pwd}${1#.} + ;; + ../*) + cd .. + resolve_file ${1#../} + cd $pwd + ;; + *) + echo $pwd/$1 + ;; + esac +} + +## +# Source a user exit file if it exists +# +run_user_exit() # $1 = file name +{ + local user_exit=$(find_file $1) + + if [ -f $user_exit ]; then + progress_message "Processing $user_exit ..." + . $user_exit + fi +} + +# +# Replace commas with spaces and echo the result +# +separate_list() { + local list="$@" + local part + local newlist + local firstpart + local lastpart + local enclosure + + case "$list" in + *,|,*|*,,*|*[[:space:]]*) + # + # There's been whining about us not catching embedded white space in + # comma-separated lists. This is an attempt to snag some of the cases. + # + # The 'TERMINATOR' function will be set by the 'firewall' script to + # either 'startup_error' or 'fatal_error' depending on the command and + # command phase + # + [ -n "$TERMINATOR" ] && \ + $TERMINATOR "Invalid comma-separated list \"$@\"" + echo "WARNING -- invalid comma-separated list \"$@\"" >&2 + ;; + *\[*\]*) + # + # Where we need to embed comma-separated lists within lists, we enclose them + # within square brackets. + # + firstpart=${list%%\[*} + lastpart=${list#*\[} + enclosure=${lastpart%%\]*} + lastpart=${lastpart#*\]} + case $lastpart in + \,*) + case $firstpart in + *\,) + echo "$(separate_list ${firstpart%,}) [$enclosure] $(separate_list ${lastpart#,})" + ;; + *) + echo "$(separate_list $firstpart)[$enclosure] $(separate_list ${lastpart#,})" + ;; + esac + ;; + *) + case $firstpart in + *\,) + echo "$(separate_list ${firstpart%,}) [$enclosure]$(separate_list $lastpart)" + ;; + *) + echo "$(separate_list $firstpart)[$enclosure]$(separate_list $lastpart)" + ;; + esac + ;; + esac + return + ;; + esac + + list="$@" + part="${list%%,*}" + newlist="$part" + + while [ "x$part" != "x$list" ]; do + list="${list#*,}"; + part="${list%%,*}"; + newlist="$newlist $part"; + done + + echo "$newlist" +} + +# +# Undo the effect of 'separate_list()' +# +combine_list() +{ + local f o= + + for f in $* ; do + o="${o:+$o,}$f" + done + + echo $o +} + +# +# Load a Kernel Module +# +loadmodule() # $1 = module name, $2 - * arguments +{ + local modulename=$1 + local modulefile + local suffix + moduleloader=modprobe + + if ! qt mywhich modprobe; then + moduleloader=insmod + fi + + if ! list_search $modulename $MODULES ; then + shift + + for suffix in $MODULE_SUFFIX ; do + modulefile=$MODULESDIR/${modulename}.${suffix} + + if [ -f $modulefile ]; then + case $moduleloader in + insmod) + insmod $modulefile $* + ;; + *) + modprobe $modulename $* + ;; + esac + + MODULES=$(lsmod | cut -d ' ' -f1) + break + fi + done + fi +} + +# +# Reload the Modules +# +reload_kernel_modules() { + + [ -z "$MODULESDIR" ] && MODULESDIR=/lib/modules/$(uname -r)/kernel/net/ipv4/netfilter + MODULES=$(lsmod | cut -d ' ' -f1) + + while read command; do + eval $command + done + +} + +# +# Load kernel modules required for Shorewall +# +load_kernel_modules() +{ + save_modules_dir=$MODULESDIR + + [ -z "$MODULESDIR" ] && \ + MODULESDIR=/lib/modules/$(uname -r)/kernel/net/ipv4/netfilter + + modules=$(find_file modules) + + if [ -f $modules -a -d $MODULESDIR ]; then + MODULES=$(lsmod | cut -d ' ' -f1) + progress_message "Loading Modules..." + . $modules + fi + + MODULESDIR=$save_modules_dir +} + +# +# Call this function to assert mutual exclusion with Shorewall. If you invoke the +# /sbin/shorewall program while holding mutual exclusion, you should pass "nolock" as +# the first argument. Example "shorewall nolock refresh" +# +# This function uses the lockfile utility from procmail if it exists. +# Otherwise, it uses a somewhat race-prone algorithm to attempt to simulate the +# behavior of lockfile. +# +mutex_on() +{ + local try=0 + local lockf=${VARDIR}/lock + + MUTEX_TIMEOUT=${MUTEX_TIMEOUT:-60} + + if [ $MUTEX_TIMEOUT -gt 0 ]; then + + [ -d ${VARDIR} ] || mkdir -p ${VARDIR} + + if qt mywhich lockfile; then + lockfile -${MUTEX_TIMEOUT} -r1 ${lockf} + else + while [ -f ${lockf} -a ${try} -lt ${MUTEX_TIMEOUT} ] ; do + sleep 1 + try=$((${try} + 1)) + done + + if [ ${try} -lt ${MUTEX_TIMEOUT} ] ; then + # Create the lockfile + echo $$ > ${lockf} + else + echo "Giving up on lock file ${lockf}" >&2 + fi + fi + fi +} + +# +# Call this function to release mutual exclusion +# +mutex_off() +{ + rm -f ${VARDIR}/lock +} + +# +# Determine which version of mktemp is present (if any) and set MKTEMP accortingly: +# +# None - No mktemp +# BSD - BSD mktemp (Mandrake) +# STD - mktemp.org mktemp +# +find_mktemp() { + local mktemp=`mywhich mktemp 2> /dev/null` + + if [ -n "$mktemp" ]; then + if qt mktemp -V ; then + MKTEMP=STD + else + MKTEMP=BSD + fi + else + MKTEMP=None + fi +} + +# +# create a temporary file. If a directory name is passed, the file will be created in +# that directory. Otherwise, it will be created in a temporary directory. +# +mktempfile() { + + [ -z "$MKTEMP" ] && find_mktemp + + if [ $# -gt 0 ]; then + case "$MKTEMP" in + BSD) + mktemp $1/shorewall.XXXXXX + ;; + STD) + mktemp -p $1 shorewall.XXXXXX + ;; + None) + > $1/shorewall-$$ && echo $1/shorewall-$$ + ;; + *) + error_message "ERROR:Internal error in mktempfile" + ;; + esac + else + case "$MKTEMP" in + BSD) + mktemp /tmp/shorewall.XXXXXX + ;; + STD) + mktemp -t shorewall.XXXXXX + ;; + None) + rm -f /tmp/shorewall-$$ + > /tmp/shorewall-$$ && echo /tmp/shorewall-$$ + ;; + *) + error_message "ERROR:Internal error in mktempfile" + ;; + esac + fi +} + +# +# create a temporary directory +# +mktempdir() { + + [ -z "$MKTEMP" ] && find_mktemp + + case "$MKTEMP" in + STD) + mktemp -td shorewall.XXXXXX + ;; + None|BSD) + # + # Not all versions of the BSD mktemp support the -d option under Linux + # + qt rm -rf /tmp/shorewall-$$ + mkdir -p /tmp/shorewall-$$ && chmod 700 /tmp/shorewall-$$ && echo /tmp/shorewall-$$ + ;; + *) + error_message "ERROR:Internal error in mktempdir" + ;; + esac +} + +# +# Read a file and handle "INCLUDE" directives +# + +read_file() # $1 = file name, $2 = nest count +{ + local first rest + + if [ -f $1 ]; then + while read first rest; do + if [ "x$first" = "xINCLUDE" ]; then + if [ $2 -lt 4 ]; then + read_file $(find_file $(expand ${rest%#*})) $(($2 + 1)) + else + error_message "WARNING: INCLUDE in $1 ignored (nested too deeply)" + fi + else + echo "$first $rest" + fi + done < $1 + else + [ -n "$TERMINATOR" ] && $TERMINATOR "No such file: $1" + echo "WARNING -- No such file: $1" + fi +} + +# +# Function for including one file into another +# +INCLUDE() { + . $(find_file $(expand $@)) +} + +# +# Strip comments and blank lines from a file and place the result in the +# temporary directory +# +strip_file() # $1 = Base Name of the file, $2 = Full Name of File (optional) +{ + local fname + + [ $# = 1 ] && fname=$(find_file $1) || fname=$2 + + if [ -f $fname ]; then + read_file $fname 0 | cut -d'#' -f1 | grep -v '^[[:space:]]*$' > $TMP_DIR/$1 + else + > $TMP_DIR/$1 + fi +} + +# +# Note: The following set of IP address manipulation functions have anomalous +# behavior when the shell only supports 32-bit signed arithmatic and +# the IP address is 128.0.0.0 or 128.0.0.1. +# + +LEFTSHIFT='<<' + + +# +# Convert an IP address in dot quad format to an integer +# +decodeaddr() { + local x + local temp=0 + local ifs=$IFS + + IFS=. + + for x in $1; do + temp=$(( $(( $temp $LEFTSHIFT 8 )) | $x )) + done + + echo $temp + + IFS=$ifs +} + +# +# convert an integer to dot quad format +# +encodeaddr() { + addr=$1 + local x + local y=$(($addr & 255)) + + for x in 1 2 3 ; do + addr=$(($addr >> 8)) + y=$(($addr & 255)).$y + done + + echo $y +} + +# +# Enumerate the members of an IP range -- When using a shell supporting only +# 32-bit signed arithmetic, the range cannot span 128.0.0.0. +# +# Comes in two flavors: +# +# ip_range() - produces a mimimal list of network/host addresses that spans +# the range. +# +# ip_range_explicit() - explicitly enumerates the range. +# +ip_range() { + local first last l x y z vlsm + + case $1 in + !*) + # + # Let iptables complain if it's a range + # + echo $1 + return + ;; + [0-9]*.*.*.*-*.*.*.*) + ;; + *) + echo $1 + return + ;; + esac + + first=$(decodeaddr ${1%-*}) + last=$(decodeaddr ${1#*-}) + + if [ $first -gt $last ]; then + fatal_error "Invalid IP address range: $1" + fi + + l=$(( $last + 1 )) + + while [ $first -le $last ]; do + vlsm= + x=31 + y=2 + z=1 + + while [ $(( $first % $y )) -eq 0 -a $(( $first + $y )) -le $l ]; do + vlsm=/$x + x=$(( $x - 1 )) + z=$y + y=$(( $y * 2 )) + done + + echo $(encodeaddr $first)$vlsm + first=$(($first + $z)) + done +} + +ip_range_explicit() { + local first last + + case $1 in + [0-9]*.*.*.*-*.*.*.*) + ;; + *) + echo $1 + return + ;; + esac + + first=$(decodeaddr ${1%-*}) + last=$(decodeaddr ${1#*-}) + + if [ $first -gt $last ]; then + fatal_error "Invalid IP address range: $1" + fi + + while [ $first -le $last ]; do + echo $(encodeaddr $first) + first=$(($first + 1)) + done +} + +# +# Netmask from CIDR +# +ip_netmask() { + local vlsm=${1#*/} + + [ $vlsm -eq 0 ] && echo 0 || echo $(( -1 $LEFTSHIFT $(( 32 - $vlsm )) )) +} + +# +# Network address from CIDR +# +ip_network() { + local decodedaddr=$(decodeaddr ${1%/*}) + local netmask=$(ip_netmask $1) + + echo $(encodeaddr $(($decodedaddr & $netmask))) +} + +# +# The following hack is supplied to compensate for the fact that many of +# the popular light-weight Bourne shell derivatives don't support XOR ("^"). +# + +ip_broadcast() { + local x=$(( 32 - ${1#*/} )) + + [ $x -eq 0 ] && echo -1 || echo $(( $(( 1 $LEFTSHIFT $x )) - 1 )) +} + +# +# Calculate broadcast address from CIDR +# +broadcastaddress() { + local decodedaddr=$(decodeaddr ${1%/*}) + local netmask=$(ip_netmask $1) + local broadcast=$(ip_broadcast $1) + + echo $(encodeaddr $(( $(($decodedaddr & $netmask)) | $broadcast ))) +} + +# +# Test for network membership +# +in_network() # $1 = IP address, $2 = CIDR network +{ + local netmask=$(ip_netmask $2) + + test $(( $(decodeaddr $1) & $netmask)) -eq $(( $(decodeaddr ${2%/*}) & $netmask )) +} + +# +# Netmask to VLSM +# +ip_vlsm() { + local mask=$(decodeaddr $1) + local vlsm=0 + local x=$(( 128 << 24 )) # 0x80000000 + + while [ $(( $x & $mask )) -ne 0 ]; do + [ $mask -eq $x ] && mask=0 || mask=$(( $mask $LEFTSHIFT 1 )) # Not all shells shift 0x80000000 left properly. + vlsm=$(($vlsm + 1)) + done + + if [ $(( $mask & 2147483647 )) -ne 0 ]; then # 2147483647 = 0x7fffffff + echo "Invalid net mask: $1" >&2 + else + echo $vlsm + fi +} + + +# +# Chain name base for an interface -- replace all periods with underscores in the passed name. +# The result is echoed (less trailing "+"). +# +chain_base() #$1 = interface +{ + local c=${1%%+} + + while true; do + case $c in + *.*) + c="${c%.*}_${c##*.}" + ;; + *-*) + c="${c%-*}_${c##*-}" + ;; + *%*) + c="${c%\%*}_${c##*%}" + ;; + *) + echo ${c:=common} + return + ;; + esac + done +} + +# +# Loosly Match the name of an interface +# + +if_match() # $1 = Name in interfaces file - may end in "+" + # $2 = Full interface name - may also end in "+" +{ + local pattern=${1%+} + + case $1 in + *+) + test "x$(echo $2 | truncate ${#pattern} )" = "x${pattern}" + ;; + *) + test "x$1" = "x$2" + ;; + esac +} + +# +# Source IP range +# +source_ip_range() # $1 = Address or Address Range +{ + [ $# -gt 0 ] && case $1 in + *.*.*.*-*.*.*.*) + case $1 in + !*) + iprange_echo "! --src-range ${1#!}" + ;; + *) + iprange_echo "--src-range $1" + ;; + esac + ;; + !+*) + echo "-m set ! $(get_set_flags ${1#!} src)" + ;; + +*) + echo "-m set $(get_set_flags $1 src)" + ;; + *) + echo "-s $1" + ;; + esac +} + +# +# Destination IP range +# +dest_ip_range() # $1 = Address or Address Range +{ + [ $# -gt 0 ] && case $1 in + *.*.*.*-*.*.*.*) + case $1 in + !*) + iprange_echo "! --dst-range ${1#!}" + ;; + *) + iprange_echo "--dst-range $1" + ;; + esac + ;; + !+*) + echo "-m set ! $(get_set_flags ${1#!} dst)" + ;; + +*) + echo "-m set $(get_set_flags $1 dst)" + ;; + *) + echo "-d $1" + ;; + esac +} + +both_ip_ranges() # $1 = Source address or range, $2 = dest address or range +{ + local rangeprefix= setprefix= rangematch= setmatch= + + case $1 in + *.*.*.*-*.*.*.*) + rangeprefix="-m iprange" + rangematch="--src-range $1" + ;; + !+*) + setprefix="-m set" + setmatch="! $(get_set_flags ${1#!} src)" + ;; + +*) + setprefix="-m set" + setmatch="$(get_set_flags $1 src)" + ;; + *) + rangematch="-s $1" + ;; + esac + + case $2 in + *.*.*.*-*.*.*.*) + rangeprefix="-m iprange" + rangematch="$rangematch --dst-range $2" + ;; + !+*) + setprefix="-m set" + match="$setmatch ! $(get_set_flags ${2#!} dst)" + ;; + +*) + setprefix="-m set" + setmatch="$setmatch $(get_set_flags $2 dst)" + ;; + *) + rangematch="$rangematch -d $2" + ;; + esac + + echo "$rangeprefix $rangematch $setprefix $setmatch" +} + +# +# Find the value 'dev' in the passed arguments then echo the next value +# + +find_device() { + while [ $# -gt 1 ]; do + [ "x$1" = xdev ] && echo $2 && return + shift + done +} + +# +# Find the value 'via' in the passed arguments then echo the next value +# + +find_gateway() { + while [ $# -gt 1 ]; do + [ "x$1" = xvia ] && echo $2 && return + shift + done +} + +# +# Find the value 'mtu' in the passed arguments then echo the next value +# + +find_mtu() { + while [ $# -gt 1 ]; do + [ "x$1" = xmtu ] && echo $2 && return + shift + done +} + +# +# Find the value 'peer' in the passed arguments then echo the next value up to +# "/" +# + +find_peer() { + while [ $# -gt 1 ]; do + [ "x$1" = xpeer ] && echo ${2%/*} && return + shift + done +} + +# +# Find the interfaces that have a route to the passed address - the default +# route is not used. +# + +find_rt_interface() { + ip route ls | while read addr rest; do + case $addr in + */*) + in_network ${1%/*} $addr && echo $(find_device $rest) + ;; + default) + ;; + *) + if [ "$addr" = "$1" -o "$addr/32" = "$1" ]; then + echo $(find_device $rest) + fi + ;; + esac + done +} + +# +# Try to find the gateway through an interface looking for 'nexthop' + +find_nexthop() # $1 = interface +{ + echo $(find_gateway `ip route ls | grep "[[:space:]]nexthop.* $1"`) +} + +# +# Find the default route's interface +# +find_default_interface() { + ip route ls | while read first rest; do + [ "$first" = default ] && echo $(find_device $rest) && return + done +} + +# +# Echo the name of the interface(s) that will be used to send to the +# passed address +# + +find_interface_by_address() { + local dev="$(find_rt_interface $1)" + local first rest + + [ -z "$dev" ] && dev=$(find_default_interface) + + [ -n "$dev" ] && echo $dev +} + +# +# Find the interface with the passed MAC address +# + +find_interface_by_mac() { + local mac=$1 first second rest dev + + ip link ls | while read first second rest; do + case $first in + *:) + dev=$second + ;; + *) + if [ "$second" = $mac ]; then + echo ${dev%:} + return + fi + esac + done +} + +# +# Find interface address--returns the first IP address assigned to the passed +# device +# +find_first_interface_address() # $1 = interface +{ + # + # get the line of output containing the first IP address + # + addr=$(ip -f inet addr show $1 2> /dev/null | grep 'inet .* global' | head -n1) + # + # If there wasn't one, bail out now + # + [ -n "$addr" ] || fatal_error "Can't determine the IP address of $1" + # + # Strip off the trailing VLSM mask (or the peer IP in case of a P-t-P link) + # along with everything else on the line + # + echo $addr | sed 's/\s*inet //;s/\/.*//;s/ peer.*//' +} + +find_first_interface_address_if_any() # $1 = interface +{ + # + # get the line of output containing the first IP address + # + addr=$(ip -f inet addr show $1 2> /dev/null | grep 'inet .* global' | head -n1) + # + # Strip off the trailing VLSM mask (or the peer IP in case of a P-t-P link) + # along with everything else on the line + # + [ -n "$addr" ] && echo $addr | sed 's/\s*inet //;s/\/.*//;s/ peer.*//' || echo 0.0.0.0 +} + +# +# Find interface addresses--returns the set of addresses assigned to the passed +# device +# +find_interface_addresses() # $1 = interface +{ + ip -f inet addr show $1 | grep inet\ | sed 's/\s*inet //;s/\/.*//;s/ peer.*//' +} + +# +# echo the list of networks routed out of a given interface +# +get_routed_networks() # $1 = interface name +{ + local address + local rest + + ip route show dev $1 2> /dev/null | + while read address rest; do + if [ "x$address" = xdefault ]; then + if [ $# -gt 1 ]; then + shift + fatal_error "$@" + else + "WARNING: default route ignored on interface $1" + fi + else + [ "$address" = "${address%/*}" ] && address="${address}/32" + echo $address + fi + done +} + +# +# Internal version of 'which' +# +mywhich() { + local dir + + for dir in $(split $PATH); do + if [ -x $dir/$1 ]; then + echo $dir/$1 + return 0 + fi + done + + return 2 +} + +# +# Set the Shorewall state +# +set_state () # $1 = state +{ + echo "$1 ($(date))" > ${VARDIR}/state +} + +# +# Determine which optional facilities are supported by iptables/netfilter +# +determine_capabilities() { + qt $IPTABLES -t nat -L -n && NAT_ENABLED=Yes || NAT_ENABLED= + qt $IPTABLES -t mangle -L -n && MANGLE_ENABLED=Yes || MANGLE_ENABLED= + + CONNTRACK_MATCH= + MULTIPORT= + XMULTIPORT= + POLICY_MATCH= + PHYSDEV_MATCH= + IPRANGE_MATCH= + RECENT_MATCH= + OWNER_MATCH= + IPSET_MATCH= + CONNMARK= + XCONNMARK= + CONNMARK_MATCH= + XCONNMARK_MATCH= + RAW_TABLE= + IPP2P_MATCH= + LENGTH_MATCH= + CLASSIFY_TARGET= + ENHANCED_REJECT= + USEPKTTYPE= + KLUDGEFREE= + MARK= + XMARK= + MANGLE_FORWARD= + + qt $IPTABLES -N fooX1234 + qt $IPTABLES -A fooX1234 -m conntrack --ctorigdst 192.168.1.1 -j ACCEPT && CONNTRACK_MATCH=Yes + qt $IPTABLES -A fooX1234 -p tcp -m multiport --dports 21,22 -j ACCEPT && MULTIPORT=Yes + qt $IPTABLES -A fooX1234 -p tcp -m multiport --dports 21:22 -j ACCEPT && XMULTIPORT=Yes + qt $IPTABLES -A fooX1234 -m policy --pol ipsec --mode tunnel --dir in -j ACCEPT && POLICY_MATCH=Yes + + if qt $IPTABLES -A fooX1234 -m physdev --physdev-in eth0 -j ACCEPT; then + PHYSDEV_MATCH=Yes + fi + + if qt $IPTABLES -A fooX1234 -m iprange --src-range 192.168.1.5-192.168.1.124 -j ACCEPT; then + IPRANGE_MATCH=Yes + if [ -z "${KLUDGEFREE}" ]; then + qt $IPTABLES -A fooX1234 -m iprange --src-range 192.168.1.5-192.168.1.124 -m iprange --dst-range 192.168.1.5-192.168.1.124 -j ACCEPT && KLUDGEFREE=Yes + fi + fi + + qt $IPTABLES -A fooX1234 -m recent --update -j ACCEPT && RECENT_MATCH=Yes + qt $IPTABLES -A fooX1234 -m owner --uid-owner 0 -j ACCEPT && OWNER_MATCH=Yes + + if qt $IPTABLES -A fooX1234 -m connmark --mark 2 -j ACCEPT; then + CONNMARK_MATCH=Yes + qt $IPTABLES -A fooX1234 -m connmark --mark 2/0xFF -j ACCEPT && XCONNMARK_MATCH=Yes + fi + + qt $IPTABLES -A fooX1234 -p tcp -m ipp2p --ipp2p -j ACCEPT && IPP2P_MATCH=Yes + qt $IPTABLES -A fooX1234 -m length --length 10:20 -j ACCEPT && LENGTH_MATCH=Yes + qt $IPTABLES -A fooX1234 -j REJECT --reject-with icmp-host-prohibited && ENHANCED_REJECT=Yes + + if [ -n "$MANGLE_ENABLED" ]; then + qt $IPTABLES -t mangle -N fooX1234 + + if qt $IPTABLES -t mangle -A fooX1234 -j MARK --set-mark 1; then + MARK=Yes + qt $IPTABLES -t mangle -A fooX1234 -j MARK --and-mark 0xFF && XMARK=Yes + fi + + if qt $IPTABLES -t mangle -A fooX1234 -j CONNMARK --save-mark; then + CONNMARK=Yes + qt $IPTABLES -t mangle -A fooX1234 -j CONNMARK --save-mark --mask 0xFF && XCONNMARK=Yes + fi + + qt $IPTABLES -t mangle -A fooX1234 -j CLASSIFY --set-class 1:1 && CLASSIFY_TARGET=Yes + qt $IPTABLES -t mangle -F fooX1234 + qt $IPTABLES -t mangle -X fooX1234 + qt $IPTABLES -t mangle -L FORWARD -n && MANGLE_FORWARD=Yes + fi + + qt $IPTABLES -t raw -L -n && RAW_TABLE=Yes + + if qt mywhich ipset; then + qt ipset -X fooX1234 # Just in case something went wrong the last time + + if qt ipset -N fooX1234 iphash ; then + if qt $IPTABLES -A fooX1234 -m set --set fooX1234 src -j ACCEPT; then + qt $IPTABLES -D fooX1234 -m set --set fooX1234 src -j ACCEPT + IPSET_MATCH=Yes + fi + qt ipset -X fooX1234 + fi + fi + + qt $IPTABLES -A fooX1234 -m pkttype --pkt-type broadcast -j ACCEPT && USEPKTTYPE=Yes + + qt $IPTABLES -F fooX1234 + qt $IPTABLES -X fooX1234 +} + +report_capabilities() { + report_capability() # $1 = Capability Description , $2 Capability Setting (if any) + { + local setting= + + [ "x$2" = "xYes" ] && setting="Available" || setting="Not available" + + echo " " $1: $setting + } + + if [ $VERBOSE -gt 1 ]; then + echo "Shorewall has detected the following iptables/netfilter capabilities:" + report_capability "NAT" $NAT_ENABLED + report_capability "Packet Mangling" $MANGLE_ENABLED + report_capability "Multi-port Match" $MULTIPORT + [ -n "$MULTIPORT" ] && report_capability "Extended Multi-port Match" $XMULTIPORT + report_capability "Connection Tracking Match" $CONNTRACK_MATCH + report_capability "Packet Type Match" $USEPKTTYPE + report_capability "Policy Match" $POLICY_MATCH + report_capability "Physdev Match" $PHYSDEV_MATCH + report_capability "Packet length Match" $LENGTH_MATCH + report_capability "IP range Match" $IPRANGE_MATCH + report_capability "Recent Match" $RECENT_MATCH + report_capability "Owner Match" $OWNER_MATCH + report_capability "Ipset Match" $IPSET_MATCH + report_capability "CONNMARK Target" $CONNMARK + [ -n "$CONNMARK" ] && report_capability "Extended CONNMARK Target" $XCONNMARK + report_capability "Connmark Match" $CONNMARK_MATCH + [ -n "$CONNMARK_MATCH" ] && report_capability "Extended Connmark Match" $XCONNMARK_MATCH + report_capability "Raw Table" $RAW_TABLE + report_capability "IPP2P Match" $IPP2P_MATCH + report_capability "CLASSIFY Target" $CLASSIFY_TARGET + report_capability "Extended REJECT" $ENHANCED_REJECT + report_capability "Repeat match" $KLUDGEFREE + report_capability "MARK Target" $MARK + [ -n "$MARK" ] && report_capability "Extended MARK Target" $XMARK + report_capability "Mangle FORWARD Chain" $MANGLE_FORWARD + fi + + [ -n "$PKTTYPE" ] || USEPKTTYPE= + +} + +report_capabilities1() { + report_capability1() # $1 = Capability + { + eval echo $1=\$$1 + } + + echo "#" + echo "# Shorewall $VERSION detected the following iptables/netfilter capabilities - $(date)" + echo "#" + report_capability1 NAT_ENABLED + report_capability1 MANGLE_ENABLED + report_capability1 MULTIPORT + report_capability1 XMULTIPORT + report_capability1 CONNTRACK_MATCH + report_capability1 USEPKTTYPE + report_capability1 POLICY_MATCH + report_capability1 PHYSDEV_MATCH + report_capability1 LENGTH_MATCH + report_capability1 IPRANGE_MATCH + report_capability1 RECENT_MATCH + report_capability1 OWNER_MATCH + report_capability1 IPSET_MATCH + report_capability1 CONNMARK + report_capability1 XCONNMARK + report_capability1 CONNMARK_MATCH + report_capability1 XCONNMARK_MATCH + report_capability1 RAW_TABLE + report_capability1 IPP2P_MATCH + report_capability1 CLASSIFY_TARGET + report_capability1 ENHANCED_REJECT + report_capability1 KLUDGEFREE + report_capability1 MARK + report_capability1 XMARK + report_capability1 MANGLE_FORWARD +} + +# +# Delete IP address +# +del_ip_addr() # $1 = address, $2 = interface +{ + [ $(find_first_interface_address_if_any $2) = $1 ] || qt ip addr del $1 dev $2 +} + +# Add IP Aliases +# +add_ip_aliases() # $* = List of addresses +{ + local addresses external interface inet cidr rest val arping=$(mywhich arping) + + address_details() + { + # + # Folks feel uneasy if they don't see all of the same + # decoration on these IP addresses that they see when their + # distro's net config tool adds them. In an attempt to reduce + # the anxiety level, we have the following code which sets + # the VLSM and BRD from an existing address in the same networks + # + # Get all of the lines that contain inet addresses with broadcast + # + ip -f inet addr show $interface 2> /dev/null | grep 'inet.*brd' | while read inet cidr rest ; do + case $cidr in + */*) + if in_network $external $cidr; then + echo "/${cidr#*/} brd $(broadcastaddress $cidr)" + break + fi + ;; + esac + done + } + + do_one() + { + val=$(address_details) + + ip addr add ${external}${val} dev $interface $label + [ -n "$arping" ] && qt $arping -U -c 2 -I $interface $external + echo "$external $interface" >> $STATEDIR/nat + [ -n "$label" ] && label="with $label" + progress_message " IP Address $external added to interface $interface $label" + } + + progress_message "Adding IP Addresses..." + + while [ $# -gt 0 ]; do + external=$1 + interface=$2 + label= + + if [ "$interface" != "${interface%:*}" ]; then + label="${interface#*:}" + interface="${interface%:*}" + label="label $interface:$label" + fi + + shift 2 + + list_search $external $(find_interface_addresses $interface) || do_one + done +} + +detect_gateway() # $1 = interface +{ + local interface=$1 + # + # First assume that this is some sort of point-to-point interface + # + gateway=$( find_peer $(ip addr ls $interface ) ) + # + # Maybe there's a default route through this gateway already + # + [ -n "$gateway" ] || gateway=$(find_gateway $(ip route ls dev $interface)) + # + # Last hope -- is there a load-balancing route through the interface? + # + [ -n "$gateway" ] || gateway=$(find_nexthop $interface) + # + # Be sure we found one + # + [ -n "$gateway" ] && echo $gateway +} + +# +# Disable IPV6 +# +disable_ipv6() { + local foo="$(ip -f inet6 addr ls 2> /dev/null)" + + if [ -n "$foo" ]; then + if qt mywhich ip6tables; then + ip6tables -P FORWARD DROP + ip6tables -P INPUT DROP + ip6tables -P OUTPUT DROP + ip6tables -F + ip6tables -X + ip6tables -A OUTPUT -o lo -j ACCEPT + ip6tables -A INPUT -i lo -j ACCEPT + else + error_message "WARNING: DISABLE_IPV6=Yes in shorewall.conf but this system does not appear to have ip6tables" + fi + fi +} + +# +# Add a logging rule. +# +log_rule_limit() # $1 = log level, $2 = chain, $3 = display Chain $4 = disposition , $5 = rate limit $6=log tag $7=command $... = predicates for the rule +{ + local level=$1 + local chain=$2 + local displayChain=$3 + local disposition=$4 + local rulenum= + local limit= + local tag=${6:+$6 } + local command=${7:--A} + local prefix + local base=$(chain_base $displayChain) + + limit="${5:-$LOGLIMIT}" # Do this here rather than in the declaration above to appease /bin/ash. + + shift 7 + + if [ -n "$tag" -a -n "$LOGTAGONLY" ]; then + displayChain=$tag + tag= + fi + + if [ -n "$LOGRULENUMBERS" ]; then + eval rulenum=\$${base}_logrules + + rulenum=${rulenum:-1} + + prefix="$(printf "$LOGFORMAT" $displayChain $rulenum $disposition)${tag}" + + rulenum=$(($rulenum + 1)) + eval ${base}_logrules=$rulenum + else + prefix="$(printf "$LOGFORMAT" $displayChain $disposition)${tag}" + fi + + if [ ${#prefix} -gt 29 ]; then + prefix="$(echo $prefix | truncate 29)" + error_message "WARNING: Log Prefix shortened to \"$prefix\"" + fi + + [ "$COMMAND" = compile ] && prefix="\"$prefix\"" + + case $level in + ULOG) + run_iptables $command $chain $@ $limit -j ULOG $LOGPARMS --ulog-prefix "$prefix" + ;; + *) + run_iptables $command $chain $@ $limit -j LOG $LOGPARMS --log-level $level --log-prefix "$prefix" + ;; + esac + + if [ $? -ne 0 ] ; then + [ -z "$STOPPING" ] && { stop_firewall; exit 2; } + fi +} + +log_rule() # $1 = log level, $2 = chain, $3 = disposition , $... = predicates for the rule +{ + local level=$1 + local chain=$2 + local disposition=$3 + + shift 3 + + log_rule_limit $level $chain $chain $disposition "$LOGLIMIT" "" -A $@ +} + +# +# Check that a mark value or mask is less that 256 or that it is less than 65536 and +# that it's lower 8 bits are zero. +# +verify_mark() # $1 = value to test +{ + verify_mark2() + { + case $1 in + 0*) + [ $(($1)) -lt 256 ] && return 0 + [ -n "$HIGH_ROUTE_MARKS" ] || return 1 + [ $(($1)) -gt 65535 ] && return 1 + return $(($1 & 0xFF)) + ;; + [1-9]*) + [ $1 -lt 256 ] && return 0 + [ -n "$HIGH_ROUTE_MARKS" ] || return 1 + [ $1 -gt 65535 ] && return 1 + return $(($1 & 0xFF)) + ;; + *) + return 2 + ;; + esac + } + + verify_mark2 $1 || fatal_error "Invalid Mark or Mask value: $1" +} + +# +# Detect a device's MTU +# +get_device_mtu() # $1 = device +{ + local output="$(ip link ls dev $1 2> /dev/null)" # quotes required for /bin/ash + + if [ -n "$output" ]; then + echo $(find_mtu $output) + else + echo 1500 + fi +} diff --git a/Shorewall/lib.tc b/Shorewall/lib.tc new file mode 100644 index 000000000..c96f98d62 --- /dev/null +++ b/Shorewall/lib.tc @@ -0,0 +1,336 @@ +#!/bin/sh +# +# Shorewall 3.2 -- /usr/share/shorewall/lib.tc +# +# This program is under GPL [http://www.gnu.org/copyleft/gpl.htm] +# +# (c) 1999,2000,2001,2002,2003,2004,2005,2006 - Tom Eastep (teastep@shorewall.net) +# +# tcstart from tc4shorewall Version 0.5 +# (c) 2005 Arne Bernin +# Modified by Tom Eastep for integration into the Shorewall distribution +# published under GPL Version 2# +# +# Complete documentation is available at http://shorewall.net +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of Version 2 of the GNU General Public License +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA + +# +# Arne Bernin's 'tc4shorewall' +# +setup_traffic_shaping() +{ + local mtu r2q tc_all_devices device mark rate ceil prio options devfile=$(find_file tcdevices) classfile=$(find_file tcclasses) devnum=1 last_device= + r2q=10 + + rate_to_kbit() { + local rateunit rate + rate=$1 + rateunit=$( echo $rate | sed -e 's/[0-9]*//') + rate=$( echo $rate | sed -e 's/[a-z]*//g') + + case $rateunit in + kbit) + rate=$rate + ;; + mbit) + rate=$(expr $rate \* 1024) + ;; + mbps) + rate=$(expr $rate \* 8192) + ;; + kbps) + rate=$(expr $rate \* 8) + ;; + *) + rate=$(expr $rate / 128) + ;; + esac + echo $rate + } + + calculate_quantum() { + local rate=$(rate_to_kbit $1) + echo $(( $rate * ( 128 / $r2q ) )) + } + + # get given outbandwidth for device + get_outband_for_dev() { + local device inband outband + while read device inband outband; do + expandv device inband outband + tcdev="$device $inband $outband" + if [ "$1" = "$device" ] ; then + echo $outband + return + fi + done < $TMP_DIR/tcdevices + } + + check_tcclasses_options() { + while [ $# -gt 1 ]; do + shift + case $1 in + default|tcp-ack|tos-minimize-delay|tos-maximize-throughput|tos-maximize-reliability|tos-minimize-cost|tos-normal-service) + ;; + tos=0x[0-9a-f][0-9a-f]|tos=0x[0-9a-f][0-9a-f]/0x[0-9a-f][0-9a-f]) + ;; + *) + echo $1 + return 1 + ;; + esac + done + return 0 + } + + get_defmark_for_dev() { + local searchdev searchmark device ceil prio options + searchdev=$1 + + while read device mark rate ceil prio options; do + expandv device mark rate ceil prio options + options=$(separate_list $options | tr '[A-Z]' '[a-z]') + tcdev="$device $mark $rate $ceil $prio $options" + if [ "$searchdev" = "$device" ] ; then + list_search "default" $options && echo $mark &&return 0 + fi + done < $TMP_DIR/tcclasses + + return 1 + } + + check_defmark_for_dev() { + get_defmark_for_dev $1 >/dev/null + } + + validate_tcdevices_file() { + progress_message2 "Validating $devfile..." + local device local device inband outband + while read device inband outband; do + expandv device inband outband + tcdev="$device $inband $outband" + check_defmark_for_dev $device || fatal_error "Option default is not defined for any class in tcclasses for interface $device" + case $interface in + *:*|+) + fatal_error "Invalid Interface Name: $interface" + ;; + esac + list_search $device $devices && fatal_error "Interface $device is defined more than once in tcdevices" + tc_all_devices="$tc_all_devices $device" + done < $TMP_DIR/tcdevices + } + + validate_tcclasses_file() { + progress_message2 "Validating $classfile..." + local classlist device mark rate ceil prio bandw wrongopt allopts opt + allopts="" + while read device mark rate ceil prio options; do + expandv device mark rate ceil prio options + tcdev="$device $mark $rate $ceil $prio $options" + ratew=$(get_outband_for_dev $device) + options=$(separate_list $options | tr '[A-Z]' '[a-z]') + for opt in $options; do + case $opt in + tos=0x??) + opt="$opt/0xff" + ;; + esac + list_search "$device-$opt" $allopts && fatal_error "option $opt already defined in a chain for interface $device in tcclasses" + allopts="$allopts $device-$opt" + done + wrongopt=$(check_tcclasses_options $options) || fatal_error "unknown option $wrongopt for class iface $device mark $mark in tcclasses file" + if [ -z "$ratew" ] ; then + fatal_error "device $device seems not to be configured in tcdevices" + fi + list_search "$device-$mark" $classlist && fatal_error "Mark $mark for interface $device defined more than once in tcclasses" + # + # Convert HEX/OCTAL mark representation to decimal + # + mark=$(($mark)) + verify_mark $mark + [ $mark -lt 256 ] || fatal_error "Invalid Mark Value" + classlist="$classlist $device-$mark" + done < $TMP_DIR/tcclasses + } + + add_root_tc() { + local defmark dev indent + + dev=$(chain_base $device) + + if [ "$COMMAND" = compile ]; then + save_command "if qt ip link ls dev $device; then" + indent="$INDENT" + INDENT="$INDENT " + save_command ${dev}_exists=Yes + save_command qt tc qdisc del dev $device root + save_command qt tc qdisc del dev $device ingress + elif ! qt ip link ls dev $device; then + error_message "WARNING: Device $device not found -- traffic-shaping configuration skipped" + return 1 + fi + + defmark=$(get_defmark_for_dev $device) + + run_tc qdisc add dev $device root handle $devnum: htb default 1$defmark + + if [ "$COMMAND" = compile ]; then + save_command "${dev}_mtu=\$(get_device_mtu $device)" + run_tc "class add dev $device parent $devnum: classid $devnum:1 htb rate $outband mtu \$${dev}_mtu" + else + run_tc class add dev $device parent $devnum: classid $devnum:1 htb rate $outband mtu $(get_device_mtu $device) + fi + + run_tc qdisc add dev $device handle ffff: ingress + run_tc filter add dev $device parent ffff: protocol ip prio 50 u32 match ip src 0.0.0.0/0 police rate ${inband} burst 10k drop flowid :1 + eval ${dev}_devnum=$devnum + devnum=$(($devnum + 1)) + + if [ "$COMMAND" = compile ]; then + save_progress_message_short " TC Device $tcdev defined." + INDENT="$indent" + save_command else + INDENT="$INDENT " + save_command error_message "\"WARNING: Device $device not found -- traffic-shaping configuration skipped\"" + save_command "${dev}_exists=" + INDENT="$indent" + save_command "fi" + save_command + fi + + return 0 + } + + add_tc_class() { + local full classid tospair tosmask quantum indent + + dev=$(chain_base $device) + + if [ "$COMMAND" = compile ]; then + save_command "if [ -n \"\$${dev}_exists\" ] ; then" + indent="$INDENT" + INDENT="$INDENT " + else + qt ip link ls dev $device || return 1 + fi + + full=$(get_outband_for_dev $device) + full=$(rate_to_kbit $full) + + if [ -z "$prio" ] ; then + prio=1 + fi + + case $rate in + *full*) + rate=$(echo $rate | sed -e "s/full/$full/") + rate="$(($rate))kbit" + ;; + esac + + case $ceil in + *full*) + ceil=$(echo $ceil | sed -e "s/full/$full/") + ceil="$(($ceil))kbit" + ;; + esac + + eval devnum=\$${dev}_devnum + # + # Convert HEX/OCTAL mark representation to decimal + # + mark=$(($mark)) + + classid=$devnum:1$mark + + [ -n "$devnum" ] || fatal_error "Device $device not defined in $devfile" + + quantum=$(calculate_quantum $rate) + + if [ "$COMMAND" = compile ]; then + save_command "[ \$${dev}_mtu -gt $quantum ] && quantum=\$${dev}_mtu || quantum=$quantum" + run_tc "class add dev $device parent $devnum:1 classid $classid htb rate $rate ceil $ceil prio $prio mtu \$${dev}_mtu quantum \$quantum" + else + [ "$last_device" = $device ] || mtu=$(get_device_mtu $device) + [ $mtu -gt $quantum ] && quantum=$mtu + run_tc class add dev $device parent $devnum:1 classid $classid htb rate $rate ceil $ceil prio $prio mtu $mtu quantum $quantum + fi + + run_tc qdisc add dev $device parent $classid handle 1$mark: sfq perturb 10 + # add filters + if [ -n "$CLASSIFY_TARGET" ]; then + run_iptables -t mangle -A tcpost $(match_dest_dev $device) -m mark --mark $mark/0xFF -j CLASSIFY --set-class $classid + else + run_tc filter add dev $device protocol ip parent $devnum:0 prio 1 handle $mark fw classid $classid + fi + #options + list_search "tcp-ack" $options && run_tc filter add dev $device parent $devnum:0 protocol ip prio 10 u32 match ip protocol 6 0xff match u8 0x05 0x0f at 0 match u16 0x0000 0xffc0 at 2 match u8 0x10 0xff at 33 flowid $classid + list_search "tos-minimize-delay" $options && options="$options tos=0x10/0x10" + list_search "tos-maximize-throughput" $options && options="$options tos=0x08/0x08" + list_search "tos-maximize-reliability" $options && options="$options tos=0x04/0x04" + list_search "tos-minimize-cost" $options && options="$options tos=0x02/0x02" + list_search "tos-normal-service" $options && options="$options tos=0x00/0x1e" + + for tospair in $(list_walk "tos=" $options) ; do + case $tospair in + */*) + tosmask=${tospair##*/} + ;; + *) + tosmask=0xff + ;; + esac + run_tc filter add dev $device parent $devnum:0 protocol ip prio 10 u32 match ip tos ${tospair%%/*} $tosmask flowid $classid + done + + if [ "$COMMAND" = compile ]; then + save_progress_message_short " TC Class $tcdev defined." + INDENT="$indent" + save_command fi + save_command + fi + + return 0 + } + + strip_file tcdevices $devfile + strip_file tcclasses $classfile + + validate_tcdevices_file + validate_tcclasses_file + + if [ -s $TMP_DIR/tcdevices ]; then + [ $COMMAND = compile ] && save_progress_message "Setting up Traffic Control..." + progress_message2 "$DOING $devfile..." + + while read device inband outband; do + expandv device inband outband + tcdev="$device $inband $outband" + add_root_tc && progress_message " TC Device $tcdev defined." + done < $TMP_DIR/tcdevices + fi + + if [ -s $TMP_DIR/tcclasses ]; then + progress_message2 "$DOING $classfile..." + + while read device mark rate ceil prio options; do + expandv device mark rate ceil prio options + tcdev="$device $mark $rate $ceil $prio $options" + options=$(separate_list $options | tr '[A-Z]' '[a-z]') + add_tc_class && progress_message " TC Class $tcdev defined." + last_device=$device + done < $TMP_DIR/tcclasses + fi +} diff --git a/Shorewall/lib.tcrules b/Shorewall/lib.tcrules new file mode 100644 index 000000000..3e2521338 --- /dev/null +++ b/Shorewall/lib.tcrules @@ -0,0 +1,390 @@ +#!/bin/sh +# +# Shorewall 3.2 -- /usr/share/shorewall/lib.tcrules +# +# This program is under GPL [http://www.gnu.org/copyleft/gpl.htm] +# +# (c) 1999,2000,2001,2002,2003,2004,2005,2006 - Tom Eastep (teastep@shorewall.net) +# +# tcstart from tc4shorewall Version 0.5 +# (c) 2005 Arne Bernin +# Modified by Tom Eastep for integration into the Shorewall distribution +# published under GPL Version 2# +# +# Complete documentation is available at http://shorewall.net +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of Version 2 of the GNU General Public License +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA + +# +# Process a TC Rule - $MARKING_CHAIN is assumed to contain the name of the +# default marking chain +# +process_tc_rule() +{ + local did_connmark= + + chain=$MARKING_CHAIN target="MARK --set-mark" marktest= + + verify_designator() { + [ "$chain" = tcout ] && \ + fatal_error "Chain designator not allowed when source is \$FW; rule \"$rule\"" + chain=$1 + mark="${mark%:*}" + } + + do_ipp2p() + { + [ -n "$IPP2P_MATCH" ] || fatal_error "Your kernel and/or iptables does not have IPP2P match support. Rule: \"$rule\"" + [ "x$port" = "x-" ] && port="ipp2p" + + case $proto in + *:*) + proto=${proto#*:} + ;; + *) + proto=tcp + ;; + esac + + r="${r}-p $proto -m ipp2p --${port} " + } + + verify_small_mark() + { + verify_mark $1 + [ $(($1)) -lt 256 ] || fatal_error "Mark Value ($1) too larg, rule \"$rule\"" + } + + do_connmark() + { + target="CONNMARK --set-mark" + mark=$mark/0xff + did_connmark=Yes + } + + validate_mark() + { + case $1 in + */*) + verify_mark ${1%/*} + verify_mark ${1#*/} + ;; + *) + verify_mark $1 + ;; + esac + } + + add_a_tc_rule() { + r= + + if [ "x$source" != "x-" ]; then + case $source in + $FW:*) + chain=tcout + r="$(source_ip_range ${source#*:}) " + ;; + *:*) + interface=${source%:*} + verify_interface $interface || fatal_error "Unknown interface $interface in rule \"$rule\"" + r="$(match_source_dev $interface) $(source_ip_range ${source#*:}) " + ;; + *.*.*|+*|!+*) + r="$(source_ip_range $source) " + ;; + ~*|!~*) + r="$(mac_match $source) " + ;; + $FW) + chain=tcout + ;; + *) + verify_interface $source || fatal_error "Unknown interface $source in rule \"$rule\"" + r="$(match_source_dev $source) " + ;; + esac + fi + + if [ "x${user:--}" != "x-" ]; then + + [ "$chain" != tcout ] && \ + fatal_error "Invalid use of a user/group: rule \"$rule\"" + + r="$r-m owner" + + case "$user" in + *+*) + r="$r --cmd-owner ${user#*+} " + user=${user%+*} + ;; + esac + + case "$user" in + *:*) + temp="${user%:*}" + [ -n "$temp" ] && r="$r --uid-owner $temp " + temp="${user#*:}" + [ -n "$temp" ] && r="$r --gid-owner $temp " + ;; + *) + [ -n "$user" ] && r="$r --uid-owner $user " + ;; + esac + fi + + + [ -n "$marktest" ] && r="${r}-m ${marktest}--mark $testval " + + if [ "x$dest" != "x-" ]; then + case $dest in + *:*) + [ "$chain" = tcpre ] && fatal_error "Destination interface is not allowed in the PREROUTING chain - rule \"$rule\"" + interface=${dest%:*} + verify_interface $interface || fatal_error "Unknown interface $interface in rule \"$rule\"" + r="$(match_dest_dev $interface) $(dest_ip_range ${dest#*:}) " + ;; + *.*.*|+*|!+*) + r="${r}$(dest_ip_range $dest) " + ;; + *) + [ "$chain" = tcpre ] && fatal_error "Destination interface is not allowed in the PREROUTING chain - rule \"$rule\"" + verify_interface $dest || fatal_error "Unknown interface $dest in rule \"$rule\"" + r="${r}$(match_dest_dev $dest) " + ;; + esac + fi + + if [ "x${length:=-}" != "x-" ]; then + [ -n "$LENGTH_MATCH" ] || fatal_error "Your kernel and/or iptables does not have length match support. Rule: \"$rule\"" + r="${r}-m length --length ${length} " + fi + + if [ "x${tos:=-}" != "x-" ]; then + r="${r}-m tos --tos ${tos} " + fi + + multiport= + + case $proto in + ipp2p|IPP2P|ipp2p:*|IPP2P:*) + do_ipp2p + ;; + icmp|ICMP|1) + r="${r}-p icmp " + [ "x$port" = "x-" ] || r="${r}--icmp-type $port" + ;; + *) + [ "x$proto" = "x-" ] && proto=all + [ "x$proto" = "x" ] && proto=all + [ "$proto" = "all" ] || r="${r}-p $proto " + [ "x$port" = "x-" ] || r="${r}--dport $port " + ;; + esac + + [ "x$sport" = "x-" ] || r="${r}--sport $sport " + + if [ -n "${excludesources}${excludedests}" ]; then + build_exclusion_chain chain1 mangle "$excludesources" "$excludedests" + + run_iptables2 -t mangle -A $chain $r -j $chain1 + + run_iptables -t mangle -A $chain1 -j $target $mark + else + run_iptables2 -t mangle -A $chain $r -j $target $mark + fi + + } + + if [ "$mark" != "${mark%:*}" ]; then + case "${mark#*:}" in + p|P) + verify_designator tcpre + ;; + cp|CP) + verify_designator tcpre + do_connmark + ;; + f|F) + verify_designator tcfor + ;; + cf|CF) + verify_designator tcfor + do_connmark + ;; + c|C) + mark=${mark%:*} + do_connmark + ;; + *) + chain=tcpost + target="CLASSIFY --set-class" + ;; + esac + + fi + + mask=0xffff + + case $mark in + SAVE) + [ -n "$did_connmark" ] && fatal_error "SAVE not valid with :C[FP]" + target="CONNMARK --save-mark --mask 0xFF" + mark= + ;; + SAVE/*) + [ -n "$did_connmark" ] && fatal_error "SAVE not valid with :C[FP]" + target="CONNMARK --save-mark --mask" + mark=${mark#*/} + verify_small_mark $mark + ;; + RESTORE) + [ -n "$did_connmark" ] && fatal_error "RESTORE not valid with :C[FP]" + target="CONNMARK --restore-mark --mask 0xFF" + mark= + ;; + RESTORE/*) + [ -n "$did_connmark" ] && fatal_error "RESTORE not valid with :C[FP]" + target="CONNMARK --restore-mark --mask" + mark=${mark#*/} + verify_small_mark $mark + ;; + CONTINUE) + [ -n "$did_connmark" ] && fatal_error "CONTINUE not valid with :C[FP]" + target=RETURN + mark= + ;; + *) + if [ "$chain" != tcpost ]; then + validate_mark $mark + if [ $((${mark%/*})) -gt 255 ]; then + case $chain in + tcpre|tcout) + target="MARK --or-mark" + ;; + *) + fatal_error "Invalid mark value ($mark) in rule \"$rule\"" + ;; + esac + elif [ $((${mark%/*})) -ne 0 -a -n "$HIGH_ROUTE_MARKS" -a $chain = tcpre ]; then + fatal_error "Marks < 256 may not be set in the PREROUTING chain when HIGH_ROUTE_MARKS=Yes" + fi + fi + ;; + esac + + case $testval in + -) + ;; + !*:C) + marktest="connmark ! " + testval=${testval%:*} + testval=${testval#!} + ;; + *:C) + marktest="connmark " + testval=${testval%:*} + ;; + !*) + marktest="mark ! " + testval=${testval#!} + ;; + *) + [ -n "$testval" ] && marktest="mark " + ;; + esac + + if [ -n "$marktest" ] ; then + case $testval in + */*) + verify_mark ${testval%/*} + verify_mark ${testval#*/} + ;; + *) + verify_mark $testval + testval=$testval/$mask + ;; + esac + fi + + excludesources= + + case ${sources:=-} in + *!*!*) + fatal_error "Invalid SOURCE in rule \"$rule\"" + ;; + !*) + if [ $(list_count $sources) -gt 1 ]; then + excludesources=${sources#!} + sources=- + fi + ;; + *!*) + excludesources=${sources#*!} + sources=${sources%!*} + ;; + esac + + excludedests= + + case ${dests:=-} in + *!*!*) + fatal_error "Invalid DEST in rule \"$rule\"" + ;; + !*) + if [ $(list_count $dests) -gt 1 ]; then + excludedests=${dests#*!} + dests=- + fi + ;; + *!*) + excludedests=${dests#*!} + dests=${dests%!*} + ;; + esac + + for source in $(separate_list $sources); do + for dest in $(separate_list $dests); do + for port in $(separate_list ${ports:=-}); do + for sport in $(separate_list ${sports:=-}); do + add_a_tc_rule + done + done + done + done + + progress_message " TC Rule \"$rule\" $DONE" + [ $COMMAND = compile ] && save_progress_message " TC Rule \"$rule\" Added" +} + +delete_tc1() +{ + clear_one_tc() { + tc qdisc del dev $1 root 2> /dev/null + tc qdisc del dev $1 ingress 2> /dev/null + + } + + run_user_exit tcclear + + run_ip link list | \ + while read inx interface details; do + case $inx in + [0-9]*) + clear_one_tc ${interface%:} + ;; + *) + ;; + esac + done +} diff --git a/Shorewall/shorewall.spec b/Shorewall/shorewall.spec index 8be369cbd..52ec6c8a1 100644 --- a/Shorewall/shorewall.spec +++ b/Shorewall/shorewall.spec @@ -110,10 +110,22 @@ fi %attr(0644,root,root) /usr/share/shorewall/action.Limit %attr(0644,root,root) /usr/share/shorewall/action.Reject %attr(0644,root,root) /usr/share/shorewall/action.template +%attr(0555,root,root) /usr/share/shorewall/clib.accounting +%attr(0555,root,root) /usr/share/shorewall/clib.ecn +%attr(0555,root,root) /usr/share/shorewall/clib.maclist +%attr(0555,root,root) /usr/share/shorewall/clib.macros +%attr(0555,root,root) /usr/share/shorewall/clib.providers +%attr(0555,root,root) /usr/share/shorewall/clib.proxyarp +%attr(0555,root,root) /usr/share/shorewall/clib.tcrules +%attr(0555,root,root) /usr/share/shorewall/clib.tos +%attr(0555,root,root) /usr/share/shorewall/clib.tunnels %attr(0555,root,root) /usr/share/shorewall/compiler %attr(0444,root,root) /usr/share/shorewall/functions %attr(0555,root,root) /usr/share/shorewall/firewall %attr(0555,root,root) /usr/share/shorewall/help +%attr(0555,root,root) /usr/share/shorewall/lib.base +%attr(0555,root,root) /usr/share/shorewall/lib.tc +%attr(0555,root,root) /usr/share/shorewall/lib.tcrules %attr(0644,root,root) /usr/share/shorewall/Limit %attr(0644,root,root) /usr/share/shorewall/macro.AllowICMPs %attr(0644,root,root) /usr/share/shorewall/macro.Amanda