#!/bin/sh # # Shorewall 3.2 -- /usr/share/shorewall/functions # # 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 LIBVERSION=30200 [ -n "${VARDIR:=/var/lib/shorewall}" ] [ -n "${SHAREDIR:=/usr/share/shorewall}" ] [ -n "${CONFDIR:=/etc/shorewall}" ] # # 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 } # # Determine if a chain is a policy chain # is_policy_chain() # $1 = name of chain { eval test \"\$${1}_is_policy\" = Yes } # # Set a standard chain's policy # setpolicy() # $1 = name of chain, $2 = policy { run_iptables -P $1 $2 } # # Set a standard chain to enable established and related connections # setcontinue() # $1 = name of chain { run_iptables -A $1 -m state --state ESTABLISHED,RELATED -j ACCEPT } # # Flush one of the NAT table chains # flushnat() # $1 = name of chain { run_iptables -t nat -F $1 } # # Flush one of the Mangle table chains # flushmangle() # $1 = name of chain { run_iptables -t mangle -F $1 } # # This function assumes that the TMP_DIR variable is set and that # its value names an existing directory. # determine_zones() { local zone parent parents rest new_zone_file= r merge_zone() { local z zones="$ZONES" merged= if [ -n "$parents" ]; then ZONES= for z in $zones; do if [ -z "$merged" ] && list_search $z $parents; then ZONES="$ZONES $zone" merged=Yes fi ZONES="$ZONES $z" done else ZONES="$ZONES $zone" fi } strip_file zones ZONES= IPV4_ZONES= IPSEC_ZONES= [ "$IPSECFILE" = zones ] && new_zone_file=Yes || test -n "${FW:=fw}" while read zone type rest; do expandv zone type case $zone in *:*) parents=${zone#*:} zone=${zone%:*} [ -n "$zone" ] || startup_error "Invalid nested zone syntax: :$parents" parents=$(separate_list $parents) eval ${zone}_parents=\"$parents\" ;; *) parents= eval ${zone}_parents= ;; esac for parent in $parents; do [ "$parent" = "$FW" ] && startup_error "Sub-zones of the firewall zone are not allowed" list_search $parent $ZONES || startup_error "Parent zone not defined: $parent" done [ ${#zone} -gt 5 ] && startup_error "Zone name longer than 5 characters: $zone" case "$zone" in [0-9*]) startup_error "Illegal zone name \"$zone\" in zones file" ;; all|none) startup_error "Reserved zone name \"$zone\" in zones file" ;; esac if [ -n "$new_zone_file" ]; then case ${type:=ipv4} in ipv4|IPv4|IPV4|plain|-) list_search $zone $ZONES $FW && startup_error "Zone $zone is defined more than once" merge_zone IPV4_ZONES="$IPV4_ZONES $zone" ;; ipsec|IPSEC|ipsec4|IPSEC4) list_search $zone $ZONES $FW && startup_error "Zone $zone is defined more than once" [ -n "$POLICY_MATCH" ] || startup_error "Your kernel and/or iptables does not support policy match" eval ${zone}_is_ipsec=Yes eval ${zone}_is_complex=Yes merge_zone IPSEC_ZONES="$IPSEC_ZONES $zone" ;; firewall) [ -n "$FW" ] && startup_error "Only one firewall zone may be defined" list_search $zone $ZONES && startup_error "Zone $zone is defined more than once" [ -n "$parents" ] && startup_error "The firewall zone may not be nested" for r in $rest; do [ "x$r" = x- ] || startup_error "OPTIONS not allowed on the firewall zone" done FW=$zone ;; *) startup_error "Invalid Zone Type: $type" ;; esac eval ${zone}_type=$type else list_search $zone $ZONES $FW && startup_error "Zone $zone is defined more than once" ZONES="$ZONES $zone" IPV4_ZONES="$IPV4_ZONES $zone" eval ${zone}_type=ipv4 fi done < $TMP_DIR/zones [ -z "$ZONES" ] && startup_error "No ipv4 or ipsec Zones Defined" [ -z "$FW" ] && startup_error "No Firewall Zone Defined" } # # Validate the zone names and options in the interfaces file # validate_interfaces_file() { local wildcard local found_obsolete_option= local z interface networks options r iface option while read z interface networks options; do expandv z interface networks options r="$z $interface $networks $options" [ "x$z" = "x-" ] && z= if [ -n "$z" ]; then validate_zone $z || startup_error "Invalid zone ($z) in record \"$r\"" fi list_search $interface $ALL_INTERFACES && \ startup_error "Duplicate Interface $interface" wildcard= case $interface in *:*|+) startup_error "Invalid Interface Name: $interface" ;; *+) wildcard=Yes ;; esac ALL_INTERFACES="$ALL_INTERFACES $interface" options=$(separate_list $options) iface=$(chain_base $interface) eval ${iface}_broadcast="$networks" eval ${iface}_zone="$z" eval ${iface}_options=\"$options\" for option in $options; do case $option in -) ;; dhcp|tcpflags|arp_filter|routefilter|maclist|logmartians|sourceroute|blacklist|proxyarp|nosmurfs|upnp|-) ;; norfc1918) if [ "$PROGRAM" = compiler ]; then indent >&3 << __EOF__ addr=\$(ip -f inet addr show $interface 2> /dev/null | grep 'inet\ ' | head -n1) if [ -n "\$addr" ]; then addr=\$(echo \$addr | sed 's/inet //;s/\/.*//;s/ peer.*//') for network in 10.0.0.0/8 176.16.0.0/12 192.168.0.0/16; do if in_network \$addr \$network; then startup_error "The 'norfc1918' option has been specified on an interface with an RFC 1918 address. Interface:$interface" fi done fi __EOF__ else addr=$(ip -f inet addr show $interface 2> /dev/null | grep inet | head -n1) if [ -n "$addr" ]; then addr=$(echo $addr | sed 's/inet //;s/\/.*//;s/ peer.*//') for network in 10.0.0.0/8 176.16.0.0/12 192.168.0.0/16; do if in_network $addr $network; then startup_error "The 'norfc1918' option may not be specified on an interface with an RFC 1918 address. Interface:$interface" fi done fi fi ;; arp_ignore=*) eval ${iface}_arp_ignore=${option#*=} ;; arp_ignore) eval ${iface}_arp_ignore=1 ;; detectnets) [ -n "$wildcard" ] && \ startup_error "The \"detectnets\" option may not be used with a wild-card interface" [ -n "$EXPORT" ] && \ startup_error "'detectnets' not permitted with the -e run-line option" ;; routeback) [ -n "$z" ] || startup_error "The routeback option may not be specified on a multi-zone interface" ;; *) error_message "WARNING: Invalid option ($option) in record \"$r\"" ;; esac done done < $TMP_DIR/interfaces [ -z "$ALL_INTERFACES" ] && startup_error "No Interfaces Defined" } # # 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 $FW; 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]*) [ "$PROGRAM" = compiler ] && 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 } # # Validate the zone names and options in the hosts file # validate_hosts_file() { local z hosts options r interface host option zports check_bridge_port() { list_search ${interface}:${1} $zports || zports="$zports ${interface}:${1}" list_search $1 $ALL_PORTS || ALL_PORTS="$ALL_PORTS $1" } while read z hosts options; do expandv z hosts options r="$z $hosts $options" validate_zone1 $z || startup_error "Invalid zone ($z) in record \"$r\"" case $hosts in *:*) interface=${hosts%%:*} iface=$(chain_base $interface) list_search $interface $ALL_INTERFACES || \ startup_error "Unknown interface ($interface) in record \"$r\"" hosts=${hosts#*:} ;; *) startup_error "Invalid HOST(S) column contents: $hosts" ;; esac eval zports=\$${z}_ports for host in $(separate_list $hosts); do if [ -n "$BRIDGING" ]; then case $host in *:*) known_interface ${host%:*} && \ startup_error "Bridged interfaces may not be defined in ${CONFDIR}/interfaces: $host" check_bridge_port ${host%%:*} ;; *.*.*) ;; *+|+*) eval ${z}_is_complex=Yes ;; *) known_interface $host && \ startup_error "Bridged interfaces may not be defined in ${CONFDIR}/interfaces: $host" check_bridge_port $host ;; esac else case $host in *.*.*) ;; *+) eval ${z}_is_complex=Yes ;; *) startup_error "BRIDGING=Yes is needed for this zone definition: $r" ;; esac fi for option in $(separate_list $options) ; do case $option in norfc1918|blacklist|maclist|tcpflags|nosmurfs|-) ;; ipsec) [ -n "$POLICY_MATCH" ] || \ startup_error "Your kernel and/or iptables does not support policy match: ipsec" eval ${z}_ipsec_hosts=\"\$${z}_ipsec_hosts $interface:$host\" eval ${z}_is_complex=Yes ;; routeback) eval ${z}_routeback=\"$interface:$host \$${z}_routeback\" ;; *) error_message "WARNING: Invalid option ($option) in record \"$r\"" ;; esac done done [ -n "$zports" ] && eval ${z}_ports=\"$zports\" done < $TMP_DIR/hosts [ -n "$ALL_PORTS" ] && progress_message2 " Bridge ports are: $ALL_PORTS" } # # Find interfaces to a given zone # # Search the variables representing the contents of the interfaces file and # for each record matching the passed ZONE, echo the expanded contents of # the "INTERFACE" column # find_interfaces() # $1 = interface zone { local zne=$1 local z local interface for interface in $ALL_INTERFACES; do eval z=\$$(chain_base $interface)_zone [ "x${z}" = x${zne} ] && echo $interface done } # # Forward Chain for an interface # forward_chain() # $1 = interface { echo $(chain_base $1)_fwd } # # Input Chain for an interface # input_chain() # $1 = interface { echo $(chain_base $1)_in } # # Output Chain for an interface # output_chain() # $1 = interface { echo $(chain_base $1)_out } # # Masquerade Chain for an interface # masq_chain() # $1 = interface { echo $(chain_base $1)_masq } # # 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 } # # Functions for creating dynamic zone rules # dynamic_fwd() # $1 = interface { echo $(chain_base $1)_dynf } dynamic_in() # $1 = interface { echo $(chain_base $1)_dyni } dynamic_out() # $1 = interface { echo $(chain_base $1)_dyno } dynamic_chains() #$1 = interface { local c=$(chain_base $1) echo ${c}_dyni ${c}_dynf ${c}_dyno } # # DNAT Chain from a zone # dnat_chain() # $1 = zone { echo ${1}_dnat } # # SNAT Chain to an interface # snat_chain() # $1 = interface { echo $(chain_base $1)_snat } # # ECN Chain to an interface # ecn_chain() # $1 = interface { echo $(chain_base $1)_ecn } # # First chains for an interface # first_chains() #$1 = interface { local c=$(chain_base $1) echo ${c}_fwd ${c}_in } # # Horrible hack to work around an iptables limitation # iprange_echo() { if [ -n "$KLUDGEFREE" ]; then echo "-m iprange $@" elif [ -f $TMP_DIR/iprange ]; then echo $@ else echo "-m iprange $@" > $TMP_DIR/iprange fi } # # Get set flags (ipsets). # get_set_flags() # $1 = set name and optional [levels], $2 = src or dst { local temp setname=$1 options=$2 [ -n "$IPSET_MATCH" ] || fatal_error "Your kernel and/or iptables does not include ipset match: $1" case $1 in *\[[1-6]\]) temp=${1#*\[} temp=${temp%\]} setname=${1%\[*} while [ $temp -gt 1 ]; do options="$options,$2" temp=$(($temp - 1)) done ;; *\[*\]) options=${1#*\[} options=${options%\]} setname=${1%\[*} ;; *) ;; esac echo "--set ${setname#+} $options" } # # Horrible hack to work around an iptables limitation # physdev_echo() { if [ -n "$KLUDGEFREE" ]; then echo -m physdev $@ elif [ -f $TMP_DIR/physdev ]; then echo $@ else echo -m physdev $@ > $TMP_DIR/physdev fi } # # We allow hosts to be specified by IP address or by physdev. These two functions # are used to produce the proper match in a netfilter rule. # match_source_hosts() { if [ -n "$BRIDGING" ]; then case $1 in *:*) physdev_echo "--physdev-in ${1%:*} $(source_ip_range ${1#*:})" ;; *.*.*.*|+*|!+*) echo $(source_ip_range $1) ;; *) physdev_echo "--physdev-in $1" ;; esac else echo $(source_ip_range $1) fi } match_dest_hosts() { if [ -n "$BRIDGING" ]; then case $1 in *:*) physdev_echo "--physdev-out ${1%:*} $(dest_ip_range ${1#*:})" ;; *.*.*.*|+*|!+*) echo $(dest_ip_range $1) ;; *) physdev_echo "--physdev-out $1" ;; esac else echo $(dest_ip_range $1) fi } # # Similarly, the source or destination in a rule can be qualified by a device name. If # the device is defined in ${CONFDIR}/interfaces then a normal interface match is # generated (-i or -o); otherwise, a physdev match is generated. #------------------------------------------------------------------------------------- # # loosely match the passed interface with those in ${CONFDIR}/interfaces. # known_interface() # $1 = interface name { local iface for iface in $ALL_INTERFACES ; do if if_match $iface $1 ; then return 0 fi done return 1 } known_port() # $1 = port name { local port for port in $ALL_PORTS ; do if if_match $port $1 ; then return 0 fi done return 1 } match_source_dev() { if [ -n "$BRIDGING" ]; then known_port $1 && physdev_echo "--physdev-in $1" || echo -i $1 elif known_interface $1; then echo -i $1 elif [ -n "$PHYSDEV_MATCH" ]; then physdev_echo "--physdev-in $1" else echo -i $1 fi } match_dest_dev() { if [ -n "$BRIDGING" ]; then known_port $1 && physdev_echo "--physdev-out $1" || echo -o $1 elif known_interface $1; then echo -o $1 elif [ -n "$PHYSDEV_MATCH" ]; then physdev_echo "--physdev-out $1" else echo -o $1 fi } verify_interface() { known_interface $1 || { [ -n "$BRIDGING" ] && known_port $1 ; } } # # Determine if communication to/from a host is encrypted using IPSEC # is_ipsec_host() # $1 = zone, $2 = host { eval local is_ipsec=\$${1}_is_ipsec eval local hosts=\"\$${1}_ipsec_hosts\" test -n "$is_ipsec" || list_search $2 $hosts } # # Generate a match for decrypted packets # match_ipsec_in() # $1 = zone, $2 = host { if is_ipsec_host $1 $2 ; then eval local options=\"\$${1}_ipsec_options \$${1}_ipsec_in_options\" echo "-m policy --pol ipsec --dir in $options" elif [ -n "$POLICY_MATCH" ]; then echo "-m policy --pol none --dir in" fi } # # Generate a match for packets that will be encrypted # match_ipsec_out() # $1 = zone, $2 = host { if is_ipsec_host $1 $2 ; then eval local options=\"\$${1}_ipsec_options \$${1}_ipsec_out_options\" echo "-m policy --pol ipsec --dir out $options" elif [ -n "$POLICY_MATCH" ]; then echo "-m policy --pol none --dir out" fi } # # Jacket for ip_range() that takes care of iprange match # firewall_ip_range() # $1 = IP address or range { [ -n "$IPRANGE_MATCH" ] && echo $1 || ip_range $1 } # # # Find hosts in a given zone # # Read hosts file and for each record matching the passed ZONE, # echo the expanded contents of the "HOST(S)" column # find_hosts() # $1 = host zone { local hosts interface address addresses while read z hosts options; do if [ "x$(expand $z)" = "x$1" ]; then expandv hosts interface=${hosts%%:*} addresses=${hosts#*:} for address in $(separate_list $addresses); do echo $interface:$address done fi done < $TMP_DIR/hosts } # # Determine the interfaces on the firewall # # For each zone, create a variable called ${zone}_interfaces. This # variable contains a space-separated list of interfaces to the zone # determine_interfaces() { for zone in $ZONES; do interfaces=$(find_interfaces $zone) interfaces=$(echo $interfaces) # Remove extra trash eval ${zone}_interfaces=\"\$interfaces\" done } # # Determine if an interface has a given option # interface_has_option() # $1 = interface, #2 = option { local options eval options=\$$(chain_base $1)_options list_search $2 $options } # # Determine the defined hosts in each zone # determine_hosts() { for zone in $ZONES; do hosts=$(find_hosts $zone) hosts=$(echo $hosts) # Remove extra trash eval interfaces=\$${zone}_interfaces for interface in $interfaces; do if interface_has_option $interface detectnets; then networks=$(get_routed_networks $interface "detectnets not allowed on interface with default route - $interface" ) else networks=0.0.0.0/0 fi for network in $networks; do if [ -z "$hosts" ]; then hosts=$interface:$network else hosts="$hosts $interface:$network" fi if interface_has_option $interface routeback; then eval ${zone}_routeback=\"$interface:$network \$${zone}_routeback\" fi done done interfaces= for host in $hosts; do interface=${host%:*} if list_search $interface $interfaces; then list_search $interface:0.0.0.0/0 $hosts && \ startup_error "Invalid zone definition for zone $zone" list_search $interface:0/0 $hosts && \ startup_error "Invalid zone definition for zone $zone" eval ${zone}_is_complex=Yes else if [ -z "$interfaces" ]; then interfaces=$interface else interfaces="$interfaces $interface" fi fi done eval ${zone}_interfaces="\$interfaces" eval ${zone}_hosts="\$hosts" if [ -n "$hosts" ]; then [ $VERBOSE -ge 1 ] && display_list "$zone Zone:" $hosts else error_message "WARNING: Zone $zone is empty" fi done } # # Ensure that the passed zone is defined in the zones file or is the firewall # validate_zone() # $1 = zone { list_search $1 $ZONES $FW } # # Ensure that the passed zone is defined in the zones file. # validate_zone1() # $1 = zone { list_search $1 $ZONES } # # Format a match by the passed MAC address # The passed address begins with "~" and uses "-" as a separator between bytes # Example: ~01-02-03-04-05-06 # mac_match() # $1 = MAC address formated as described above { echo "--match mac --mac-source $(echo $1 | sed 's/~//;s/-/:/g')" } # # Find interfaces that have the passed option specified # find_interfaces_by_option() # $1 = option { for interface in $ALL_INTERFACES; do eval options=\$$(chain_base $interface)_options list_search $1 $options && echo $interface done } # # This slightly slower version is used to find both the option and option followed # by equal sign ("=") and a value # find_interfaces_by_option1() # $1 = option { local options option for interface in $ALL_INTERFACES; do eval options=\$$(chain_base $interface)_options for option in $options; do if [ "${option%=*}" = "$1" ]; then echo $interface break fi done done } # # Find hosts with the passed option # find_hosts_by_option() # $1 = option { local ignore hosts interface address addresses options ipsec= list while read ignore hosts options; do expandv options list=$(separate_list $options) if list_search $1 $list; then list_search ipsec $list && ipsec=ipsec || ipsec=none expandv hosts interface=${hosts%%:*} addresses=${hosts#*:} for address in $(separate_list $addresses); do echo ${ipsec}^$interface:$address done fi done < $TMP_DIR/hosts for interface in $ALL_INTERFACES; do interface_has_option $interface $1 && \ echo none^${interface}:0.0.0.0/0 done } # # Flush and delete all user-defined chains in the filter table # deleteallchains() { run_iptables -F run_iptables -X } # # Process the routestopped file either adding or deleting rules # process_routestopped() # $1 = command { local hosts= interface host host1 options networks source= dest= matched while read interface host options; do expandv interface host options [ "x$host" = "x-" -o -z "$host" ] && host=0.0.0.0/0 for h in $(separate_list $host); do hosts="$hosts $interface:$h" done routeback= if [ -n "$options" ]; then for option in $(separate_list $options); do case $option in routeback) if [ -n "$routeback" ]; then error_message "WARNING: Duplicate routestopped option ignored: routeback" else routeback=Yes for h in $(separate_list $host); do run_iptables $1 FORWARD -i $interface -o $interface $(both_ip_ranges $h $h) -j ACCEPT done fi ;; source) for h in $(separate_list $host); do source="$source $interface:$h" done ;; dest) for h in $(separate_list $host); do dest="$dest $interface:$h" done ;; critical) ;; *) error_message "WARNING: Unknown routestopped option ignored: $option" ;; esac done fi done < $TMP_DIR/routestopped for host in $hosts; do interface=${host%:*} networks=${host#*:} source_range=$(source_ip_range $networks) dest_range=$(dest_ip_range $networks) run_iptables $1 INPUT -i $interface $source_range -j ACCEPT [ -z "$ADMINISABSENTMINDED" ] && \ run_iptables $1 OUTPUT -o $interface $dest_range -j ACCEPT matched= if list_search $host $source ; then run_iptables $1 FORWARD -i $interface $source_range -j ACCEPT matched=Yes fi if list_search $host $dest ; then run_iptables $1 FORWARD -o $interface $dest_range -j ACCEPT matched=Yes fi if [ -z "$matched" ]; then for host1 in $hosts; do [ "$host" != "$host1" ] && run_iptables $1 FORWARD -i $interface -o ${host1%:*} $(both_ip_ranges $networks ${host1#*:}) -j ACCEPT done fi done } process_criticalhosts() { local hosts= interface host h options networks criticalhosts= strip_file routestopped while read interface host options; do expandv interface host options [ "x$host" = "x-" -o -z "$host" ] && host=0.0.0.0/0 || host=$(separate_list $host) if [ -n "$options" ]; then for option in $(separate_list $options); do case $option in routeback|source|dest) ;; critical) for h in $host; do criticalhosts="$criticalhosts $interface:$h" done ;; *) error_message "WARNING: Unknown routestopped option ignored: $option" ;; esac done fi done < $TMP_DIR/routestopped if [ -n "$criticalhosts" ]; then CRITICALHOSTS=$criticalhosts progress_message "Critical Hosts are:$CRITICALHOSTS" fi } # # 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 if [ ! -f $TMP_DIR/$1 ]; then [ $# = 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 fi } # # Load an optional library # lib_load() # $1 = Name of the Library, $2 = Error Message heading if the library cannot be found { local lib=${SHAREDIR}/lib.$1 local loaded eval loaded=\$LIB_${1}_LOADED if [ -z "$loaded" ]; then if [ -f $lib ]; then . $lib eval LIB_${1}_LOADED=Yes else startup_error "$2 requires the Shorewall library $1 ($lib) which is not installed" fi fi } # # Determine if an optional library is available # lib_avail() # $1 = Name of the Library { [ -f ${SHAREDIR}/lib.$1 ] } strip_file_and_lib_load() # $1 = logical file name, $2 = library to load if the stripped file is non-empty { local f=$(find_file $1) strip_file $1 $f if [ -s $TMP_DIR/$1 ]; then lib_load $2 "A non-empty $1 file ($f)" return 0 fi return 1 } # # 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 } # # Query NetFilter about the existence of a filter chain # chain_exists() # $1 = chain name { qt $IPTABLES -L $1 -n } # # 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 } # # Determine if Interface is up # interface_is_up() { [ -n "$(ip link ls dev $1 | grep -e '[<,]UP[,>]')" ] } # # 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, $2-n = Fatal error message { 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 [ "$PROGRAM" = compiler ] && 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 } 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 } # # Determine the value for a parameter that defaults to Yes # added_param_value_yes() # $1 = Parameter Name, $2 = Parameter value { local val="$2" if [ -z "$val" ]; then echo "Yes" else case $val in [Yy][Ee][Ss]) echo "Yes" ;; [Nn][Oo]) echo "" ;; *) startup_error "Invalid value ($val) for $1" ;; esac fi } # # Determine the value for a parameter that defaults to No # added_param_value_no() # $1 = Parameter Name, $2 = Parameter value { local val="$2" if [ -z "$val" ]; then echo "" else case $val in [Yy][Ee][Ss]) echo "Yes" ;; [Nn][Oo]) echo "" ;; *) startup_error "Invalid value ($val) for $1" ;; esac fi } # # Initialize this program # do_initialize() { # Run all utility programs using the C locale # # Thanks to Vincent Planchenault for this tip # export LC_ALL=C # Make sure umask is sane umask 077 PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin # # Establish termination function # TERMINATOR=fatal_error # # Clear all configuration variables # VERSION= IPTABLES= FW= SUBSYSLOCK= ALLOWRELATED=Yes LOGRATE= LOGBURST= LOGPARMS= LOGLIMIT= ADD_IP_ALIASES= ADD_SNAT_ALIASES= TC_ENABLED= BLACKLIST_DISPOSITION= BLACKLIST_LOGLEVEL= CLAMPMSS= ROUTE_FILTER= LOG_MARTIANS= DETECT_DNAT_IPADDRS= MUTEX_TIMEOUT= FORWARDPING= MACLIST_DISPOSITION= MACLIST_LOG_LEVEL= TCP_FLAGS_DISPOSITION= TCP_FLAGS_LOG_LEVEL= RFC1918_LOG_LEVEL= MARK_IN_FORWARD_CHAIN= VERSION_FILE= LOGFORMAT= LOGRULENUMBERS= ADMINISABSENTMINDED= BLACKLISTNEWONLY= MODULE_SUFFIX= ACTIONS= USEDACTIONS= SMURF_LOG_LEVEL= DISABLE_IPV6= BRIDGING= DYNAMIC_ZONES= PKTTYPE= USEPKTYPE= RETAIN_ALIASES= DELAYBLACKLISTLOAD= LOGTAGONLY= LOGALLNEW= RFC1918_STRICT= MACLIST_TTL= SAVE_IPSETS= RESTOREFILE= MAPOLDACTIONS= IMPLICIT_CONTINUE= HIGH_ROUTE_MARKS= TC_EXPERT= OUTPUT= TMP_DIR= ALL_INTERFACES= ROUTEMARK_INTERFACES= IPSECMARK=256 PROVIDERS= CRITICALHOSTS= IPSECFILE= EXCLUSION_SEQ=1 STOPPING= HAVE_MUTEX= ALIASES_TO_ADD= SECTION=ESTABLISHED SECTIONS= ALL_PORTS= TMP_DIR=$(mktempdir) [ -n "$TMP_DIR" ] && chmod 700 $TMP_DIR || \ fatal_error "Can't create a temporary directory" case $PROGRAM in compiler) trap "[ -n "$OUTPUT" ] && rm -f $OUTPUT;rm -rf $TMP_DIR; exit 2" 1 2 3 4 5 6 9 ;; firewall) trap "[ -n "$RESTOREBASE" ] && rm -f $RESTOREBASE;rm -rf $TMP_DIR; my_mutex_off; exit 2" 1 2 3 4 5 6 9 ;; esac ensure_config_path VERSION_FILE=$SHAREDIR/version [ -f $VERSION_FILE ] && VERSION=$(cat $VERSION_FILE) run_user_exit params config=$(find_file shorewall.conf) if [ -f $config ]; then if [ -r $config ]; then progress_message "Processing $config..." . $config else fatal_error "Cannot read $config (Hint: Are you root?)" fi else fatal_error "$config does not exist!" fi # # Restore CONFIG_PATH if the shorewall.conf file cleared it # ensure_config_path # # Determine the capabilities of the installed iptables/netfilter # We load the kernel modules here to accurately determine # capabilities when module autoloading isn't enabled. # PKTTYPE=$(added_param_value_no PKTTYPE $PKTTYPE) [ -n "${MODULE_SUFFIX:=o gz ko o.gz ko.gz}" ] if [ -z "$EXPORT" -a "$(whoami)" = root ]; then load_kernel_modules if [ -z "$IPTABLES" ]; then IPTABLES=$(mywhich iptables 2> /dev/null) [ -z "$IPTABLES" ] && fatal_error "Can't find iptables executable" else [ -e "$IPTABLES" ] || fatal_error "\$IPTABLES=$IPTABLES does not exist or is not executable" fi determine_capabilities else f=$(find_file capabilities) [ -f $f ] && . $f || fatal_error "The -e flag requires a capabilities file" fi ALLOWRELATED="$(added_param_value_yes ALLOWRELATED $ALLOWRELATED)" [ -n "$ALLOWRELATED" ] || \ fatal_error "ALLOWRELATED=No is not supported" ADD_IP_ALIASES="$(added_param_value_yes ADD_IP_ALIASES $ADD_IP_ALIASES)" if [ -n "${LOGRATE}${LOGBURST}" ]; then LOGLIMIT="--match limit" [ -n "$LOGRATE" ] && LOGLIMIT="$LOGLIMIT --limit $LOGRATE" [ -n "$LOGBURST" ] && LOGLIMIT="$LOGLIMIT --limit-burst $LOGBURST" fi if [ -n "$IP_FORWARDING" ]; then case "$IP_FORWARDING" in [Oo][Nn]|[Oo][Ff][Ff]|[Kk][Ee][Ee][Pp]) ;; *) fatal_error "Invalid value ($IP_FORWARDING) for IP_FORWARDING" ;; esac else IP_FORWARDING=On fi [ -n "${BLACKLIST_DISPOSITION:=DROP}" ] case "$CLAMPMSS" in [0-9]*) ;; *) CLAMPMSS=$(added_param_value_no CLAMPMSS $CLAMPMSS) ;; esac ADD_SNAT_ALIASES=$(added_param_value_no ADD_SNAT_ALIASES $ADD_SNAT_ALIASES) ROUTE_FILTER=$(added_param_value_no ROUTE_FILTER $ROUTE_FILTER) LOG_MARTIANS=$(added_param_value_no LOG_MARTIANS $LOG_MARTIANS) DETECT_DNAT_IPADDRS=$(added_param_value_no DETECT_DNAT_IPADDRS $DETECT_DNAT_IPADDRS) FORWARDPING=$(added_param_value_no FORWARDPING $FORWARDPING) [ -n "$FORWARDPING" ] && \ fatal_error "FORWARDPING=Yes is no longer supported" maclist_target=reject if [ -n "$MACLIST_DISPOSITION" ] ; then case $MACLIST_DISPOSITION in REJECT) ;; DROP) maclist_target=DROP ;; ACCEPT) maclist_target=RETURN ;; *) fatal_error "Invalid value ($MACLIST_DISPOSITION) for MACLIST_DISPOSITION" ;; esac else MACLIST_DISPOSITION=REJECT fi if [ -n "$TCP_FLAGS_DISPOSITION" ] ; then case $TCP_FLAGS_DISPOSITION in REJECT|ACCEPT|DROP) ;; *) fatal_error "Invalid value ($TCP_FLAGS_DISPOSITION) for TCP_FLAGS_DISPOSITION" ;; esac else TCP_FLAGS_DISPOSITION=DROP fi [ -n "${RFC1918_LOG_LEVEL:=info}" ] MARK_IN_FORWARD_CHAIN=$(added_param_value_no MARK_IN_FORWARD_CHAIN $MARK_IN_FORWARD_CHAIN) [ -n "$MARK_IN_FORWARD_CHAIN" ] && MARKING_CHAIN=tcfor || MARKING_CHAIN=tcpre CLEAR_TC=$(added_param_value_yes CLEAR_TC $CLEAR_TC) if [ -n "$LOGFORMAT" ]; then if [ -n "$(echo $LOGFORMAT | grep '%d')" ]; then LOGRULENUMBERS=Yes temp=$(printf "$LOGFORMAT" fooxx 1 barxx 2> /dev/null) if [ $? -ne 0 ]; then fatal_error "Invalid LOGFORMAT string: \"$LOGFORMAT\"" fi else temp=$(printf "$LOGFORMAT" fooxx barxx 2> /dev/null) if [ $? -ne 0 ]; then fatal_error "Invalid LOGFORMAT string: \"$LOGFORMAT\"" fi fi [ ${#temp} -le 29 ] || fatal_error "LOGFORMAT string is longer than 29 characters: \"$LOGFORMAT\"" else LOGFORMAT="Shorewall:%s:%s:" fi ADMINISABSENTMINDED=$(added_param_value_no ADMINISABSENTMINDED $ADMINISABSENTMINDED) BLACKLISTNEWONLY=$(added_param_value_no BLACKLISTNEWONLY $BLACKLISTNEWONLY) DISABLE_IPV6=$(added_param_value_no DISABLE_IPV6 $DISABLE_IPV6) BRIDGING=$(added_param_value_no BRIDGING $BRIDGING) DYNAMIC_ZONES=$(added_param_value_no DYNAMIC_ZONES $DYNAMIC_ZONES) if [ -n "$DYNAMIC_ZONES" ]; then [ -n "$EXPORT" ] && fatal_error "DYNAMIC_ZONES=Yes is incompatible with the -e option" lib_avail dynamiczones || error_message "WARNING: DYNAMIC_ZONES=Yes requires the Shorewall dynamiczones library (${SHAREDIR}/lib.dynamiczones) which is not installed" fi STARTUP_ENABLED=$(added_param_value_yes STARTUP_ENABLED $STARTUP_ENABLED) RETAIN_ALIASES=$(added_param_value_no RETAIN_ALIASES $RETAIN_ALIASES) [ -n "${ADD_IP_ALIASES}${ADD_SNAT_ALIASES}" ] || RETAIN_ALIASES= DELAYBLACKLISTLOAD=$(added_param_value_no DELAYBLACKLISTLOAD $DELAYBLACKLISTLOAD) LOGTAGONLY=$(added_param_value_no LOGTAGONLY $LOGTAGONLY) RFC1918_STRICT=$(added_param_value_no RFC1918_STRICT $RFC1918_STRICT) SAVE_IPSETS=$(added_param_value_no SAVE_IPSETS $SAVE_IPSETS) MAPOLDACTIONS=$(added_param_value_yes MAPOLDACTIONS $MAPOLDACTIONS) FASTACCEPT=$(added_param_value_no FASTACCEPT $FASTACCEPT) IMPLICIT_CONTINUE=$(added_param_value_no IMPLICIT_CONTINUE $IMPLICIT_CONTINUE) HIGH_ROUTE_MARKS=$(added_param_value_no HIGH_ROUTE_MARKS $HIGH_ROUTE_MARKS) TC_EXPERT=$(added_param_value_no TC_EXPERT $TC_EXPERT) [ -n "$XCONNMARK_MATCH" ] || XCONNMARK= [ -n "$XMARK" ] || XCONNMARK= [ -n "$HIGH_ROUTE_MARKS" -a -z "$XCONNMARK" ] && fatal_error "HIGH_ROUTE_MARKS=Yes requires extended CONNMARK target, extended CONNMARK match support and extended MARK support" case ${IPSECFILE:=ipsec} in ipsec|zones) ;; *) fatal_error "Invalid value ($IPSECFILE) for IPSECFILE option" ;; esac case ${MACLIST_TABLE:=filter} in filter) ;; mangle) [ $MACLIST_DISPOSITION = reject ] && fatal_error "MACLIST_DISPOSITION=REJECT is not allowed with MACLIST_TABLE=mangle" ;; *) fatal_error "Invalid value ($MACLIST_TABLE) for MACLIST_TABLE option" ;; esac TC_SCRIPT= if [ -n "$TC_ENABLED" ] ; then case "$TC_ENABLED" in [Yy][Ee][Ss]) TC_ENABLED= TC_SCRIPT=$(find_file tcstart) [ -f $TC_SCRIPT ] || fatal_error "Unable to find tcstart file" ;; [Ii][Nn][Tt][Ee][Rr][Nn][Aa][Ll]) TC_ENABLED=Yes ;; [Nn][Oo]) TC_ENABLED= ;; esac else TC_ENABLED=Yes fi if [ -n "$TC_ENABLED" ];then [ -n "$MANGLE_ENABLED" ] || fatal_error "Traffic Shaping requires mangle support in your kernel and iptables" fi [ "x${SHOREWALL_DIR}" = "x." ] && SHOREWALL_DIR="$PWD" [ -n "${RESTOREFILE:=restore}" ] # # Strip the files that we use often # strip_file interfaces strip_file hosts # # Check out the user's shell # [ -n "${SHOREWALL_SHELL:=/bin/sh}" ] temp=$(decodeaddr 192.168.1.1) if [ $(encodeaddr $temp) != 192.168.1.1 ]; then fatal_error "Shell $SHOREWALL_SHELL is broken and may not be used with Shorewall" fi if [ -z "$KLUDGEFREE" ]; then rm -f $TMP_DIR/physdev rm -f $TMP_DIR/iprange fi qt mywhich awk && HAVEAWK=Yes || HAVEAWK= } SHOREWALL_LIBRARY=Loaded