From 473f7d7361ff687bcbd5751095b2c8f87dcae223 Mon Sep 17 00:00:00 2001 From: teastep Date: Sun, 27 Aug 2006 17:27:48 +0000 Subject: [PATCH] Back out first round of modularization git-svn-id: https://shorewall.svn.sourceforge.net/svnroot/shorewall/trunk@4445 fbd18981-670d-0410-9b5c-8dc0c1a9a2bb --- Shorewall/README.txt | 2 +- Shorewall/changelog.txt | 350 ++- Shorewall/compiler | 4120 +++++++++++++++++++++++++----------- Shorewall/fallback.sh | 2 +- Shorewall/firewall | 1582 +++----------- Shorewall/functions | 3130 ++++++++++++++++++++++++++- Shorewall/help | 46 +- Shorewall/install.sh | 187 +- Shorewall/prog.footer | 24 +- Shorewall/prog.header | 1 + Shorewall/releasenotes.txt | 901 +++++++- Shorewall/shorewall | 105 +- Shorewall/shorewall.conf | 2 +- Shorewall/shorewall.spec | 19 +- Shorewall/started | 4 +- Shorewall/uninstall.sh | 2 +- 16 files changed, 7684 insertions(+), 2793 deletions(-) diff --git a/Shorewall/README.txt b/Shorewall/README.txt index 1fcaf0afa..bdd78a39b 100644 --- a/Shorewall/README.txt +++ b/Shorewall/README.txt @@ -1 +1 @@ -This is the Shorewall Development 3.3 branch of CVS. +This is the Shorewall Stable 3.2 branch of CVS. diff --git a/Shorewall/changelog.txt b/Shorewall/changelog.txt index f66affdb3..e4a7b01ee 100644 --- a/Shorewall/changelog.txt +++ b/Shorewall/changelog.txt @@ -1,22 +1,354 @@ -Changes in 3.3.0 +Changes in 3.2.4 -1) Remove dynamic zone capability. +1) Move 'do_initialize()' to functions. -2) Fixed output of 'hits' command under busybox 1.2.0. +2) Move common config file parsing to functions. -3) Remove requirement for extended marks with 'track'. +3) Fix handling of 'start' command with directory name. -4) Fixed output of 'hits' with spaces as delimiters in /etc/services. +-------------------------------------------------------------------------------- +Changes in 3.2.3 -5) Fixed modules/xmodules snafu. +1) Add 'export' command. -6) Correct handling of CONFIG_PATH when EXPORT=Yes. +2) Apply Cedric Schieli's patch for the functions file. -7) Merge shorewall.conf handling changes. +3) Implement TC_EXPERT. -8) Re-implement the 'try' command. +4) Correct 'del_ip_addr' screwup. +5) Make 'detectnets' fatal with default route. +6) Make 'check -e' behave properly with TC +7) Fix SUBSYSLOCK. +8) Fix mss= and the firewall zone. +9) Add Natanael Copa's fix for BusyBox ash. + +10) Ensure that interface is UP and configured in multi-ISP 'optional' + detection. + +11) Fix "shorewall add" command + +12) Fix "shorewall refresh" so that subsequent "shorewall save" works + correctly. + +13 Fix DETECT_DNAT_IPADDRS=Yes address detection bug. + +-------------------------------------------------------------------------------- +Changes in 3.2.2 + +1) Correct handling of shorewall.conf options in exported scripts. + +2) Avoid creating empty files /nat and /proxyarp. + +3) Add -f option to 'show' command. + +4) Avoid enabling deferred output hook processing during capabilities + probe. + +5) Add -n option to install.sh + +6) Add -s option to "shorewall [re]load" + +7) Add 'optional' option to providers file. + +8) Add 'reset' command to prog.footer. +-------------------------------------------------------------------------------- +Changes in 3.2.1 + +1) Change the detection of physdev match to use + --physdev-out. Preparation for removal of physdev-out match + capability. + +2) Add missing edits to configuration parameters in firewall script. + +3) Fix 'hits' formatting under BusyBox 1.2.0. + +4) Remove requirement for extended marks with 'track'. + +5) Fixed output of 'hits' with spaces as delimiters in /etc/services. + +6) Fixed modules/xmodules snafu. + +7) Fix version in shorewall.conf. + +8) Add /usr/share/shorewall-lite: to the front of CONFIG_PATH in + /usr/share/shorewall/configfiles/shorewall.conf. + +------------------------------------------------------------------------------- +Changes in 3.2.0 Final + +1) Avoid extraneous double quotes in log rules generated at run-time. + +Changes in 3.2.0 RC 6 + +1) Correct generation of the balanced default route. + +2) Allow 'detect' in the ADDRESS column of the masq file. + +3) Correct some permission problems. + +------------------------------------------------------------------------------- +Changes in 3.2.0 RC 5 + +1) Fix DOA 'LITEDIR' problem in /sbin/shorewall. + +2) Stop the compiler from running iptables. + +3) Avoid problem with ash. + +4) Make the 'try' command use the correct SHOREWALL_SHELL. + +5) Don't defer Action/chain extension script processing until + run-time. + +6) Run extension script for policy chains. + +------------------------------------------------------------------------------- +Changes in 3.2.0 RC 4 + +1) Fix permissions on Limit file. + +2) Make progress messages product-specific. + +3) Add 'reload' command. + +------------------------------------------------------------------------------- +Changes in 3.2.0 RC 3 + +1) Remove hard directory references from compiled programs. + +2) Fix /nat <-> /proxyarp typo. + +3) Avoid use of symbolic link for /sbin/shorewall + +------------------------------------------------------------------------------- +Changes in 3.2.0 RC 2 + +1) Update versions. + +2) Rationalize the use of IPTABLES and LOGFORMAT. + +3) Allow Shorewall/Shorewall-lite coexistance under RPM + +------------------------------------------------------------------------------- +Changes in 3.2.0 RC 1 + +1) Update versions. + +------------------------------------------------------------------------------- +Changes in 3.2.0 Beta 8 + +1) Issue more helpful BRIDGING=No error messages. + +2) Implement "all-" in rules file. + +3) Add xmodules file. + +4) Detect devices in tcdevices entries. + +5) Fix for white-space in log prefix. + +6) Fix rule parsing of single excluded MAC address. + +------------------------------------------------------------------------------- +Changes in 3.2.0 Beta 7 + +1) Fix mark/mask validation. + +2) Restore traffic control to 'refresh'. + +3) Detect MTU for entries in /etc/shorewall/tcdevices. + +4) Avoid fatal error after missing forwardUPnP rule warning. + +------------------------------------------------------------------------------- +Changes in 3.2.0 Beta 6 + +1) Fix tc "notfound" errors when 'restart' is run out of ip-up.local. + +2) Allow 'detectnets' to work. + +3) Add TOS column to tcrules. + +4) Fix 'proxyarp' interface attribute handling. + +5) Fix default route generation in providers handling. + +6) Change interraction of 'track' and PREROUTING marking. + +------------------------------------------------------------------------------- +Changes in 3.2.0 Beta 5 + +1) Fix compilation problem on LEAF Bering. + +2) Remove traffic shaping code from the 'firewall' script to avoid + unmaintainable code duplication. + +3) Fix DETECT_DNAT_IPADDRS=No bug. + +4) Handle absense of mangle FORWARD chain. + +5) Rename the rtrules file to route_rules. + +6) Fix deletion of SNAT ip addresses. + +7) Accomodate ancient kernel's with no FORWARD or POSTROUTING in mangle. + +8) Clear SUBSYSLOCK on Debian/Ubuntu installs. + +------------------------------------------------------------------------------- +Changes in 3.2.0 Beta 4 + +1) Fix 'routeback' with bridge ports. + +2) Add support for explicit routing rules. + +3) Fix mktempdir problem. + +4) Implement HIGH_ROUTE_MARKS + +Changes in 3.2.0 Beta 3 + +1) Correct handling of verbosity in the 'try' command. + +2) Add IMPLICIT_CONTINUE option to shorewall.conf. + +3) Fix SAME/ADD_SNAT_ALIASES interaction. + +------------------------------------------------------------------------------- +Changes in 3.2.0 Beta 2 + +1) Make "shorewall start -f" work correctly. + +2) Remove SUBSYSLOCK code from default and debian footers. + +3) Add 'refreshed' extension script. + +4) Implement 'logdrop' and 'logreject' + +------------------------------------------------------------------------------- +Changes in 3.1.x. and 3.2.x + +1) Removal of dynamic zones. + +2) Implement 'generate' command. + +3) Implement 'super-quiet' mode using multiple -q options (e.g., -qq). + +4) Add back dynamic zones. + +5) Allow remote compiles. + +6) Change output of 'generate' to always be the file name entered (do not + prepend /var/lib/shorewall/) + +7) Remove some restrictions on remote compiles. + +8) Add error checking to generated script. + +9) Merge Fabio Longerai's 'length' patch. + +10) Add the "-p" option to the compile command. + +11) Fix 'check' bug in setup_masq + +12) Break compiler/firewall into two files + +13) Make Shoreall quiet for a change. + +14) Make "Compile-and-go" the only mode of operation. + +15) Remove -p + +16) Apply Tuomo's patches for IPSEC and Noecho. + +17) Fix bridging + +18) Fix QUEUE when used in the ESTABLISHED section. + +19) Apply Ed Suominen's patch to tcrules. +------------------------------------------------------------------------------- +3.1.5 + +20) Speed up compilation by rewriting 'fix_bang()'. + +21) Correct GATEWAY handling in the providers file. + +22) Remove sub-zone exclusion from DNAT/REDIRECT. + +23) Add compiled-program/library versioning scheme. + +------------------------------------------------------------------------------- +3.1.6 + +24) Apply Steven Springl's help patch. + +25) Fix 'allow/drop/reject' while Shorewall not running. + +26) Implement bi-directional macros. + +27) Fix TC bridge port handling. + +28) Fix/document "check -e" + +29) Automatically use capabilities file when non-root. + +30) Correct typo in help file ("help drop"). + +31) Added 'tcpsyn' + +------------------------------------------------------------------------------- +3.1.7 + +32) Change 'tcpsyn' to 'tcp:syn' + +33) Remove superfluous rules in MAC validation. + +34) Correct Makefile. + +35) Add -t option + +36) Restore log messages. + +37) Fix "shorewall capabilities" with VERBOSITY < 2. + +------------------------------------------------------------------------------- +3.1.8 + +38) Remove compile-time running of extension scripts. + +39) Correctly handle interfaces named 'inet'. + +40) SUBSYSLOCK functionality restored. + +------------------------------------------------------------------------------- +3.1.9 + +41) Fix Provider route generation when a specific gateway is specified. + +42) Be sure that restore file name is preserved regardless of 'set --' in + define_firewall().) + +43) Add Simon's redhat prog files. + +44) Add 'delete_nat' to compiled program. + +45) Move 'shorecap' to /usr/share/shorewall + +46) Add debian prog files. + +47) Correct syntax error in validate_policy() +------------------------------------------------------------------------------- +3.2.0 Beta 1. + +48) Streamlined some code in setup_tc1() + +49) Process /etc/shorewall/params at run-time. + +50) Add new modules to /etc/shorewall/modules. + +51) Make default behavior of "compile" distribution-neutral. diff --git a/Shorewall/compiler b/Shorewall/compiler index aa6d9689e..5c02b75cf 100755 --- a/Shorewall/compiler +++ b/Shorewall/compiler @@ -53,55 +53,17 @@ fatal_error() # $* = Error Message } # -# Load a library functions +# We include this for compatibility with the 'firewall' script. It distinguishes between +# Fatal Errors (stop or restore required) and Startup Errors (errors detected before the firewall +# state has been changed. This allows us to use common parsing routines in both programs. # -clib_load() # $1 = library name, $2=name of 'loaded' symbol, $3-n = Fatal Error Message +startup_error() { - eval local loaded="\$${2}" - - local clib=${SHAREDIR}/clib.${1} - - if [ -z "$loaded" ]; then - if [ -x $clib ]; then - . $clib - else - shift 2 - fatal_error "$@" - fi - fi -} - -strip_file_and_clib_load() # $1 = logical file, $2 = library to load if logical file non-empty, $3 = name of 'loaded' symbol -{ - local f=$(find_file $1) - - strip_file $1 $f - - if [ -s $TMP_DIR/$1 ]; then - clib_load $2 $3 "Entries in the $f file require the Shorewall compiler module ${SHAREDIR}/clib.$2 which is not installed" - fi -} - -strip_file_and_lib_load() # $1 = logical file, $2 = library to load if logical file non-empty, $3 = name of 'loaded' symbol, $4-n = Fatal Error Message -{ - local f=$(find_file $1) - - strip_file $1 $f - - if [ -s $TMP_DIR/$1 ]; then - lib_load $2 $3 "Entries in the $f file require the Shorewall library ${SHAREDIR}/lib.$2 which is not installed" - fi -} - -try_clib_load() # $1 = library name, $2=name of 'loaded' symbol -{ - eval local loaded="\$${2}" - - local clib=${SHAREDIR}/clib.${1} - - if [ -z "$loaded" ]; then - [ -x $clib ] && . $clib - fi + echo " ERROR: $@" >&2 + [ -n "$TMP_DIR" ] && rm -rf $TMP_DIR + [ -n "$OUTPUT" ] && rm -f $OUTPUT + kill $$ + exit 2 } # @@ -218,6 +180,13 @@ qt_iptables() { save_command qt \$IPTABLES $@ } +# +# Generate a command to run tc +# +run_tc() { + save_command run_tc $@ +} + # # Add the implicit ACCEPT rules at the end of a rules file section # @@ -365,7 +334,7 @@ chain_exists() # $1 = chain name } # -# Create a mangle chainhttp://www1.shorewall.net/ +# Create a mangle chain # # Create a variable exists_mangle_${1} and set its value to Yes to indicate that # the chain now exists. @@ -448,638 +417,362 @@ deletechain() # $1 = name of chain } # -# Determine if a chain is a policy chain +# Process the providers file # -is_policy_chain() # $1 = name of chain +setup_providers() { - eval test \"\$${1}_is_policy\" = Yes -} + 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= -# -# Set a standard chain's policy -# -setpolicy() # $1 = name of chain, $2 = policy -{ - run_iptables -P $1 $2 -} + 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__ + } -# -# 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 -} + 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 -# -# Flush one of the NAT table chains -# -flushnat() # $1 = name of chain -{ - run_iptables -t nat -F $1 -} +__EOF__ + } -# -# 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() + balance_default_route() # $1 = weight { - local z zones="$ZONES" merged= + balance=yes - 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 + 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 - ZONES="$ZONES $zone" + 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 } - ZONES= - IPV4_ZONES= - IPSEC_ZONES= + add_a_provider() { + local t n iface option optional= - [ "$IPSECFILE" = zones ] && new_zone_file=Yes || test -n "${FW:=fw}" + [ -n "$MANGLE_ENABLED" ] || fatal_error "Providers require mangle support in your kernel and iptables" - while read zone type rest; do - expandv zone type - - case $zone in - *:*) - parents=${zone#*:} - zone=${zone%:*} - [ -n "$zone" ] || fatal_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" ] && fatal_error "Sub-zones of the firewall zone are not allowed" - list_search $parent $ZONES || fatal_error "Parent zone not defined: $parent" - done - - [ ${#zone} -gt 5 ] && fatal_error "Zone name longer than 5 characters: $zone" - - case "$zone" in - [0-9*]) - fatal_error "Illegal zone name \"$zone\" in zones file" - ;; - all|none) - fatal_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 && fatal_error "Zone $zone is defined more than once" - merge_zone - IPV4_ZONES="$IPV4_ZONES $zone" - ;; - ipsec|IPSEC|ipsec4|IPSEC4) - list_search $zone $ZONES $FW && fatal_error "Zone $zone is defined more than once" - [ -n "$POLICY_MATCH" ] || fatal_error "Your kernel and/or iptables does not support policy match" - clib_load tunnels CLIB_TUNNELS_LOADED "The $type zone type requires Shorewall compiler module clib.tunnels which is not installed" - eval ${zone}_is_ipsec=Yes - eval ${zone}_is_complex=Yes - merge_zone - IPSEC_ZONES="$IPSEC_ZONES $zone" - ;; - firewall) - [ -n "$FW" ] && fatal_error "Only one firewall zone may be defined" - list_search $zone $ZONES && fatal_error "Zone $zone is defined more than once" - [ -n "$parents" ] && fatal_error "The firewall zone may not be nested" - for r in $rest; do - [ "x$r" = x- ] || fatal_error "OPTIONS not allowed on the firewall zone" - done - FW=$zone - ;; - *) - fatal_error "Invalid Zone Type: $type" - ;; - esac - - eval ${zone}_type=$type - else - list_search $zone $ZONES $FW && fatal_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" ] && fatal_error "No ipv4 or ipsec Zones Defined" - - [ -z "$FW" ] && fatal_error "No Firewall Zone Defined" -} - -# -# 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 -} - -# -# 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 -} - -# -# 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 -} - -# -# Jacket for ip_range() that takes care of iprange match -# - -# -# 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 + for t in $PROVIDERS local main default unspec; do + if [ "$t" = "$table" ]; then + fatal_error "Duplicate Provider: $table, provider: \"$provider\"" 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 && \ - fatal_error "Invalid zone definition for zone $zone" - list_search $interface:0/0 $hosts && \ - fatal_error "Invalid zone definition for zone $zone" - eval ${zone}_is_complex=Yes - else - if [ -z "$interfaces" ]; then - interfaces=$interface - else - interfaces="$interfaces $interface" - 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 ${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 -} + eval ${table}_number=$number + indent >&3 << __EOF__ # -# Validate the zone names and options in the interfaces file +# Add Provider $table ($number) # -validate_interfaces_file() { - local wildcard - local found_obsolete_option= - local z interface networks options r iface option +__EOF__ + save_command "if interface_is_up $interface && [ \"\$(find_first_interface_address_if_any $interface)\" != 0.0.0.0 ]; then" + save_indent1="$INDENT" + INDENT="$INDENT " - 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 || fatal_error "Invalid zone ($z) in record \"$r\"" - fi - - list_search $interface $ALL_INTERFACES && \ - fatal_error "Duplicate Interface $interface" - - wildcard= - - case $interface in - *:*|+) - fatal_error "Invalid Interface Name: $interface" - ;; - *+) - wildcard=Yes - ;; - esac - - ALL_INTERFACES="$ALL_INTERFACES $interface" - options=$(separate_list $options) iface=$(chain_base $interface) + + save_command "${iface}_up=Yes" + + save_command "qt ip route flush table $number" - eval ${iface}_broadcast="$networks" - eval ${iface}_zone="$z" - eval ${iface}_options=\"$options\" + 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 - for option in $options; do + 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 -) ;; - dhcp|tcpflags|arp_filter|routefilter|maclist|logmartians|sourceroute|blacklist|proxyarp|nosmurfs|upnp|-) + 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" ;; - norfc1918) - 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__ + balance=*) + balance_default_route ${option#*=} ;; - arp_ignore=*) - eval ${iface}_arp_ignore=${option#*=} + balance) + balance_default_route 1 ;; - arp_ignore) - eval ${iface}_arp_ignore=1 + loose) + loose=Yes ;; - detectnets) - [ -n "$wildcard" ] && \ - fatal_error "The \"detectnets\" option may not be used with a wild-card interface" - [ -n "$EXPORT" ] && \ - fatal_error "'detectnets' not permitted with the -e run-line option" - ;; - routeback) - [ -n "$z" ] || fatal_error "The routeback option may not be specified on a multi-zone interface" + optional) + optional=Yes ;; *) - error_message "WARNING: Invalid option ($option) in record \"$r\"" + error_message "WARNING: Invalid option ($option) ignored in provider \"$provider\"" ;; esac done - done < $TMP_DIR/interfaces - [ -z "$ALL_INTERFACES" ] && fatal_error "No Interfaces Defined" -} + 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 - check_bridge_port() - { - list_search ${interface}:${1} $zports || zports="$zports ${interface}:${1}" - list_search $1 $ALL_PORTS || ALL_PORTS="$ALL_PORTS $1" - } + f=$(find_file route_rules) - while read z hosts options; do - expandv z hosts options - r="$z $hosts $options" - validate_zone1 $z || fatal_error "Invalid zone ($z) in record \"$r\"" + if [ -f $f ]; then + strip_file route_rules $f - case $hosts in - *:*) + if [ -s $TMP_DIR/route_rules ]; then + progress_message2 "$DOING $f..." - interface=${hosts%%:*} - iface=$(chain_base $interface) + save_command - list_search $interface $ALL_INTERFACES || \ - fatal_error "Unknown interface ($interface) in record \"$r\"" + 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 - hosts=${hosts#*:} - ;; - *) - fatal_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%:*} && \ - fatal_error "Bridged interfaces may not be defined in ${CONFDIR}/interfaces: $host" - check_bridge_port ${host%%:*} - ;; - *.*.*) - ;; - *+|+*) - eval ${z}_is_complex=Yes - ;; - *) - known_interface $host && \ - fatal_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 - ;; - *) - fatal_error "BRIDGING=Yes is needed for this zone definition: $r" - ;; - esac - fi - - for option in $(separate_list $options) ; do - case $option in - norfc1918|blacklist|tcpflags|nosmurfs|-) - ;; - maclist) - clib_load maclist CLIB_MACLIST_LOADED "The maclist host option requires Shorewall compiler module clib.maclist which is not installed" - ;; - ipsec) - clib_load tunnels CLIB_TUNNELS_LOADED "The ipsec host option requires Shorewall compiler module clib.tunnels which is not installed" - [ -n "$POLICY_MATCH" ] || \ - fatal_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" + save_command "run_ip route flush cache" + INDENT="$save_indent" + save_command "fi" + save_command + fi } # @@ -1137,6 +830,8 @@ validate_policy() fi done + strip_file policy + while read client server policy loglevel synparams; do expandv client server policy loglevel synparams @@ -1263,14 +958,6 @@ find_bcastdetect_interfaces() { done } -# -# Flush and delete all user-defined chains in the filter table -# -deleteallchains() { - run_iptables -F - run_iptables -X -} - # # Set /proc/sys/net/ipv4/ip_forward based on $IP_FORWARDING # @@ -1290,119 +977,6 @@ setup_forwarding() { esac } -# -# 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= - - 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 - -} - # # For each entry in the CRITICALHOSTS global list, add INPUT and OUTPUT rules to # enable traffic to/from those hosts. @@ -1431,6 +1005,595 @@ disable_critical_hosts() done } +# +# 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 +} + +# +# 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 + +} + +# +# 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 +} + # # Set up SYN flood protection # @@ -1467,6 +1630,206 @@ setup_syn_flood_chains() 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 + + } + +# +# Setup Static Network Address Translation (NAT) +# +setup_nat() { + local external= interface= internal= allints= localnat= policyin= policyout= + + validate_one() #1 = Variable Name, $2 = Column name, $3 = value + { + case $3 in + Yes|yes) + ;; + No|no) + eval ${1}= + ;; + *) + [ -n "$3" ] && \ + fatal_error "Invalid value ($3) for $2 in entry \"$external $interface $internal $allints $localnat\"" + ;; + esac + } + + do_one_nat() { + local add_ip_aliases=$ADD_IP_ALIASES iface=${interface%:*} + + if [ -n "$add_ip_aliases" ]; then + case $interface in + *:) + interface=${interface%:} + add_ip_aliases= + ;; + *) + [ -n "$RETAIN_ALIASES" ] || save_command del_ip_addr $external $iface + ;; + esac + else + interface=${interface%:} + fi + + validate_one allints "ALL INTERFACES" $allints + validate_one localnat "LOCAL" $localnat + + if [ -n "$allints" ]; then + addnatrule nat_in -d $external $policyin -j DNAT --to-destination $internal + addnatrule nat_out -s $internal $policyout -j SNAT --to-source $external + else + addnatrule $(input_chain $iface) -d $external $policyin -j DNAT --to-destination $internal + addnatrule $(output_chain $iface) -s $internal $policyout -j SNAT --to-source $external + fi + + [ -n "$localnat" ] && \ + run_iptables2 -t nat -A OUTPUT -d $external $policyout -j DNAT --to-destination $internal + + if [ -n "$add_ip_aliases" ]; then + list_search $external $ALIASES_TO_ADD || \ + ALIASES_TO_ADD="$ALIASES_TO_ADD $external $interface" + fi + } + # + # At this point, we're just interested in the network translation + # + > $STATEDIR/nat + + if [ -n "$POLICY_MATCH" ]; then + policyin="-m policy --pol none --dir in" + policyout="-m policy --pol none --dir out" + fi + + [ -n "$RETAIN_ALIASES" ] || save_progress_message "Setting up one-to-one NAT..." + + while read external interface internal allints localnat; do + expandv external interface internal allints localnat + + do_one_nat + + progress_message_and_save " Host $internal NAT $external on $interface" + done < $TMP_DIR/nat +} + +# +# Delete existing Static NAT +# +delete_nat() { + run_iptables -t nat -F + run_iptables -t nat -X + + [ -d $STATEDIR ] && touch $STATEDIR/nat + + indent >&3 << __EOF__ + +if [ -f \${VARDIR}/nat ]; then + while read external interface; do + del_ip_addr \$external \$interface + done < \${VARDIR}/nat + + rm -f \${VARDIR}/nat +fi + +__EOF__ +} + +# +# Setup Network Mapping (NETMAP) +# +setup_netmap() { + + while read type net1 interface net2 ; do + expandv type net1 interface net2 + + list_search $interface $ALL_INTERFACES || \ + fatal_error "Unknown interface $interface in entry \"$type $net1 $interface $net2\"" + + case $type in + DNAT) + addnatrule $(input_chain $interface) -d $net1 -j NETMAP --to $net2 + ;; + SNAT) + addnatrule $(output_chain $interface) -s $net1 -j NETMAP --to $net2 + ;; + *) + fatal_error "Invalid type $type in entry \"$type $net1 $interface $net2\"" + ;; + esac + + progress_message_and_save " Network $net1 on $interface mapped to $net2 ($type)" + + done < $TMP_DIR/netmap +} + +# +# 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 +} + # # Set up an exclusion chain # @@ -1498,6 +1861,1060 @@ build_exclusion_chain() # $1 = variable to store chain name into $2 = table, $3 eval $1=$c } +# +# 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) + + 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 + + defmark=$(get_defmark_for_dev $device) + + run_tc qdisc add dev $device root handle $devnum: htb default 1$defmark + + 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" + + 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)) + + 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 + + return 0 + } + + add_tc_class() { + local full classid tospair tosmask quantum indent + + dev=$(chain_base $device) + + save_command "if [ -n \"\$${dev}_exists\" ] ; then" + indent="$INDENT" + INDENT="$INDENT " + + 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) + + 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" + + 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 + + save_progress_message_short " TC Class $tcdev defined." + INDENT="$indent" + save_command fi + save_command + + return 0 + } + + strip_file tcdevices $devfile + strip_file tcclasses $classfile + + validate_tcdevices_file + validate_tcclasses_file + + if [ -s $TMP_DIR/tcdevices ]; then + 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 +} + +# +# 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 = tcpost ] || 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 = tcpost ] || 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" + save_progress_message_short " TC Rule \\\"$rule\\\" Added" +} + +# +# 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__ +} + +# +# Refresh queuing and classes +# +refresh_tc() { + + + if [ -n "$CLEAR_TC" ]; then + delete_tc + save_command + fi + + [ -n "$MARK_IN_FORWARD_CHAIN" ] && chain=tcfor || chain=tcpre + + # + # Flush the TC mangle chains + # + + if [ -n "$MANGLE_FORWARD" ]; then + run_iptables -t mangle -F tcfor + run_iptables -t mangle -F tcpost + fi + + run_iptables -t mangle -F tcpre + run_iptables -t mangle -F tcout + # + # Remove all exclusion chains from the mangle table + # + indent >&3 << __EOF__ + +$IPTABLES -t mangle -L -n | grep '^Chain excl_' | while read junk chain rest; do + run_iptables -t mangle -F \$chain + run_iptables -t mangle -X \$chain +done + +__EOF__ + # + # Process the TC Rules File + # + save_progress_message "Refreshing Traffic Control Rules..." + + while read mark sources dests proto ports sports user testval; do + expandv mark sources dests proto ports sports user testval tos + rule=$(echo "$mark $sources $dests $proto $ports $sports $user $testval $tos") + process_tc_rule + done < $TMP_DIR/tcrules + + if [ -n "$TC_SCRIPT" ]; then + save_progress_message "Refreshing Traffic Shaping" + run_user_exit $TC_SCRIPT + elif [ -n "$TC_ENABLED" ]; then + setup_traffic_shaping + fi +} + +# +# Compile refresh of the firewall +# +compile_refresh_firewall() +{ + local INDENT="" + local DOING="Compiling Refresh of" + local DONE="Compiled" + + save_command "refresh_firewall()" + save_command "{" + INDENT=" " + + append_file refresh + + # + # Blacklist + # + refresh_blacklist + + ecn=$(find_file ecn) + + if [ -f $ecn ] && [ -n "$MANGLE_ENABLED" ]; then + save_progress_message "Refreshing ECN" + setup_ecn $ecn + fi + # + # Refresh Traffic Control + # + [ -n "$MANGLE_ENABLED" ] && refresh_tc + + append_file refreshed + + save_command "cp -f \$(my_pathname) \${VARDIR}/.restore" + + INDENT="" + + save_command "}" + +} + +# +# 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 + +} + # # Add one Filter Rule from an action -- Helper function for the action file processor # @@ -1933,22 +3350,20 @@ process_action() # $1 = chain (Chain to add the rules to) # Report Result # progress_message " Rule \"$rule\" $DONE." - save_progress_message_short " Rule \"$rule\" added." + save_progress_message_short " Rule \\\"$rule\\\" added." } # # Source the extension script for an action, if any # -process_action_file() # $1 = File Name, $2 - n = Arguments +process_action_file() # $1 = File Name { local user_exit=$(find_file $1) - shift - if [ -f $user_exit ]; then progress_message "Processing $user_exit ..." - . $user_exit $@ + . $user_exit fi } @@ -2005,7 +3420,7 @@ createlogactionchain() # $1 = Action Name, $2 = Log Level [: Log Tag ] [ none = "${LEVEL%\!}" ] && LEVEL= - process_action_file $1 "$CHAIN" "$LEVEL" "$TAG" + process_action_file $1 eval ${action}_chains=\"\$${action}_chains $level $CHAIN\" @@ -2162,6 +3577,75 @@ substitute_action() # $1 = parameter, $2 = action esac } +# +# 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 +} + # # The next three functions implement the three phases of action processing. # @@ -2194,6 +3678,10 @@ process_actions1() { USEDACTIONS= + strip_file actions + + strip_file actions.std ${SHAREDIR}/actions.std + for inputfile in actions.std actions; do while read xaction rest; do [ "x$rest" = x ] || fatal_error "Invalid Action: $xaction $rest" @@ -2240,12 +3728,52 @@ process_actions1() { eval requiredby=\"\$requiredby_${xaction}\" list_search $xtarget $requiredby || eval requiredby_${xaction}=\"$requiredby $xtarget\" else - try_clib_load macros CLIB_MACROS_LOADED - if [ -n "$CLIB_MACROS_LOADED" ]; then - verify_macro_from_action - else - rule="$xtarget $xclients $xservers $xprotocol $xports $xcports $xratelimit $xuserspec" - fatal_error "Invalid TARGET in rule \"$rule\" (do you need the clib.macros Shorewall Library?)" + 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 fi ;; @@ -2512,7 +4040,70 @@ __EOF__ expandv xclients xservers xprotocol xports xcports xratelimit xuserspec if [ -n "$is_macro" ]; then - 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" else rule="$xtarget $xclients $xservers $xprotocol $xports $xcports $xratelimit $xuserspec" process_action $xchain $xaction1 $xaction2 $xclients $xservers $xprotocol $xports $xcports $xratelimit $xuserspec @@ -2573,7 +4164,7 @@ add_nat_rule() { save_command if [ $(list_count1 $interfaces) -eq 1 ]; then - save_command "addr=\$(find_first_interface_address $interface)" + save_command "addr=\$(find_first_interface_address $interfaces)" else save_command "addr=" for interface in $interfaces; do @@ -3477,8 +5068,111 @@ __EOF__ # # Report Result # - progress_message " Rule \"$rule\" $DONE." - save_progress_message_short " Rule \"$rule\" added." + progress_message " Rule \"$rule\" $DONE." + save_progress_message_short " Rule \\\"$rule\\\" added." +} + +# +# 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" + } # @@ -3635,27 +5329,20 @@ process_rules() ;; esac - try_clib_load macros CLIB_MACROS_LOADED - - if [ -n "$CLIB_MACROS_LOADED" ]; then - f=macro.$xtarget1 - - if [ -f $TMP_DIR/$f ]; then + f=macro.$xtarget1 + + if [ -f $TMP_DIR/$f ]; then + do_it Yes + else + fn=$(find_file $f) + + if [ -f $fn ]; then + strip_file $f $fn do_it Yes else - fn=$(find_file $f) - - if [ -f $fn ]; then - strip_file $f $fn - do_it Yes - else - rule="$xtarget $xclients $xservers $xprotocol $xports $xcports $xaddress $xratelimit $xuserspec" - fatal_error "Invalid Action in rule \"$rule\"" - fi + rule="$xtarget $xclients $xservers $xprotocol $xports $xcports $xaddress $xratelimit $xuserspec" + fatal_error "Invalid Action in rule \"$rule\"" fi - else - rule="$xtarget $xclients $xservers $xprotocol $xports $xcports $xaddress $xratelimit $xuserspec" - fatal_error "Invalid Action in rule \"$rule\" (do you need the clib.macros Shorewall compile-time library?)" fi fi ;; @@ -3675,6 +5362,200 @@ process_rules() SECTION=DONE } +# +# 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 +} + # # Display elements of a list with leading white space # @@ -3877,6 +5758,40 @@ rules_chain() # $1 = source zone, $2 = destination zone 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 + +} + # # Set up Source NAT (including masquerading) # @@ -4274,6 +6189,8 @@ __EOF__ } + strip_file masq $1 + if [ -n "$NAT_ENABLED" ]; then progress_message2 "$DOING Masquerading/SNAT" save_progress_message "Setting up Masquerading/SNAT..." @@ -4389,12 +6306,15 @@ process_blacklist_rec() { # setup_blacklist() { local hosts="$(find_hosts_by_option blacklist)" + local f=$(find_file blacklist) local disposition=$BLACKLIST_DISPOSITION local ipsec policy - if [ -n "$hosts" -a -s $TMP_DIR/blacklist ]; then + if [ -n "$hosts" -a -f $f ]; then progress_message2 "$DOING Blacklisting..." + strip_file blacklist $f + createchain blacklst no [ -n "$BLACKLISTNEWONLY" ] && state="-m state --state NEW,INVALID" || state= @@ -4430,29 +6350,42 @@ setup_blacklist() { # Refresh the Black List # refresh_blacklist() { + local f=$(find_file blacklist) local disposition=$BLACKLIST_DISPOSITION + local indent="$INDENT" progress_message2 "$DOING Black List..." save_progress_message "Loading Black List..." + strip_file blacklist $f + [ "$disposition" = REJECT ] && disposition=reject + save_command "if chain_exists blacklst; then" + INDENT="$INDENT " + run_iptables -F blacklst while read networks protocol ports; do expandv networks protocol ports process_blacklist_rec done < $TMP_DIR/blacklist + + INDENT="$indent" + save_command "fi" } # # Verify the Black List # validate_blacklist() { + local f=$(find_file blacklist) local disposition=$BLACKLIST_DISPOSITION progress_message2 "Checking Black List..." + strip_file blacklist $f + [ "$disposition" = REJECT ] && disposition=reject while read networks protocol ports; do @@ -4535,6 +6468,12 @@ initialize_netfilter () { # accepting new connections. # + strip_file rules + strip_file proxyarp + strip_file maclist + strip_file nat + strip_file netmap + progress_message2 "Pre-processing Actions..." process_actions1 @@ -4543,13 +6482,13 @@ initialize_netfilter () { deletechain shorewall if [ -n "$NAT_ENABLED" ]; then - [ -n "$CLIB_NAT_LOADED" ] && delete_nat + delete_nat for chain in PREROUTING POSTROUTING OUTPUT; do qt_iptables -t nat -P $chain ACCEPT done fi - [ -n "$CLIB_PROXYARP_LOADED" ] && delete_proxy_arp + delete_proxy_arp if [ -n "$MANGLE_ENABLED" ]; then run_iptables -t mangle -F @@ -4567,7 +6506,7 @@ initialize_netfilter () { done fi - [ -n "$CLIB_TCRULES_LOADED" -a -n "$CLEAR_TC" ] && delete_tc + [ -n "$CLEAR_TC" ] && delete_tc progress_message2 "Deleting user chains..." @@ -4629,6 +6568,8 @@ initialize_netfilter () { progress_message2 "$DOING $f ..." + strip_file routestopped $f + process_routestopped -A if [ -n "$DISABLE_IPV6" ]; then @@ -4651,10 +6592,9 @@ initialize_netfilter () { run_iptables -A $chain -p udp --dport 53 -j ACCEPT done - if [ -n "$CLIB_ACCOUNTING_LOADED" ]; then - accounting_file=$(find_file accounting) - setup_accounting $accounting_file - fi + accounting_file=$(find_file accounting) + + [ -f $accounting_file ] && setup_accounting $accounting_file createchain reject no createchain dynamic no @@ -4842,6 +6782,8 @@ __EOF__ save_progress_message "Setting up RFC1918 Filtering..." + strip_file rfc1918 + createchain norfc1918 no createchain rfc1918 no @@ -5132,6 +7074,23 @@ fi __EOF__ done fi + + if [ -n "$DYNAMIC_ZONES" ]; then + progress_message "$DOING Dynamic Zone Chains..." + + for interface in $ALL_INTERFACES; do + for chain in $(dynamic_chains $interface); do + createchain $chain no + done + + chain=$(dynamic_in $interface) + createnatchain $chain + + run_iptables -A $(input_chain $interface) -j $chain + run_iptables -A $(forward_chain $interface) -j $(dynamic_fwd $interface) + run_iptables -A OUTPUT -o $interface -j $(dynamic_out $interface) + done + fi # # UPnP # @@ -5248,6 +7207,14 @@ activate_rules() fi } + # + # Create a dynamic chain for a zone and jump to it from a second chain + # + create_zone_dyn_chain() # $1 = zone, $2 = second chain + { + createchain ${1}_dyn No + run_iptables -A $2 -j ${1}_dyn + } # # Add jumps to early SNAT chains # @@ -5255,6 +7222,12 @@ activate_rules() addnatjump POSTROUTING $(snat_chain $interface) -o $interface done # + # Add jumps for dynamic nat chains + # + [ -n "$DYNAMIC_ZONES" ] && for interface in $ALL_INTERFACES ; do + addrulejump PREROUTING $(dynamic_in $interface) -i $interface + done + # # Add jumps from the builtin chains to the nat chains # addnatjump PREROUTING nat_in @@ -5285,8 +7258,10 @@ activate_rules() if [ -n "$is_ipsec" ]; then eval source_hosts=\$${zone}_hosts + [ -n "$DYNAMIC_ZONES" ] && create_zone_dyn_chain $zone $frwd_chain else eval source_hosts=\$${zone}_ipsec_hosts + [ -n "$DYNAMIC_ZONES" -a -n "$source_hosts" ] && create_zone_dyn_chain $zone $frwd_chain fi for host in $source_hosts; do @@ -5312,6 +7287,11 @@ activate_rules() echo $zone $type $source_hosts >> $STATEDIR/zones + if [ -n "$DYNAMIC_ZONES" ]; then + echo "$FW $zone $chain1" >> $STATEDIR/chains + echo "$zone $FW $chain2" >> $STATEDIR/chains + fi + need_broadcast= for host in $source_hosts; do @@ -5361,6 +7341,8 @@ activate_rules() [ -z "$chain" ] && continue # CONTINUE policy and there is no canonical chain. + [ -n "$DYNAMIC_ZONES" ] && echo "$zone $zone1 $chain" >> $STATEDIR/chains + if [ $zone = $zone1 ]; then # # Try not to generate superfluous intra-zone rules @@ -5594,10 +7576,6 @@ __EOF__ done fi -__EOF__ - - if [ -n "$CLIB_PROXYARP_LOADED" ]; then - cat >&3 << __EOF__ if [ -f \${VARDIR}/proxyarp ]; then while read address interface external haveroute; do qt arp -i \$external -d \$address pub @@ -5612,9 +7590,7 @@ __EOF__ done __EOF__ - fi - - [ -n "$CLIB_TCRULES_LOADED" ] && [ -n "$CLEAR_TC" ] && save_command "delete_tc1" + [ -n "$CLEAR_TC" ] && save_command "delete_tc1" [ -n "$DISABLE_IPV6" ] && save_command "disable_ipv6" @@ -5802,7 +7778,7 @@ compile_firewall() # $1 = File Name [ -h $dir ] && fatal_error "$dir is a Symbolic Link" [ -d $outfile ] && fatal_error "$outfile is a Directory" [ -h $outfile ] && fatal_error "$outfile is a Symbolic Link" - [ -f $outfile -a ! -x $outfile ] && fatal_error "$outfile exists and is not a compiled script" + [ -f $outfile -a ! -x $outfile ] && fatal_error "$outfile exists and is not a restore file" DOING=Compiling DONE=compiled @@ -5848,7 +7824,7 @@ __EOF__ compile_stop_firewall - cat >&3 << __EOF__ +cat >&3 << __EOF__ # # Set policy of chain \$1 to \$2 @@ -5981,9 +7957,17 @@ combine_list() echo \$o } +__EOF__ + # + # The following function(s) can be removed when we require version 30200 (below) + # + cat >&3 << __EOF__ +# +# Determine if an interface is up +# interface_is_up() { - test -n "\$(ip link ls dev \$1 | grep -e '[<,]UP[,>]')" + [ -n "\$(ip link ls dev \$1 |grep -e '[<,]UP[,>]')" ] } # @@ -6101,50 +8085,38 @@ __EOF__ initialize_netfilter - if [ -n "$CLIB_PROXYARP_LOADED" ]; then - progress_message2 "$DOING Proxy ARP"; setup_proxy_arp - fi + progress_message2 "$DOING Proxy ARP"; setup_proxy_arp # # [re]-Establish routing # - if [ -n "$CLIB_PROVIDERS_LOADED" ]; then - setup_providers $(find_file providers) - [ -n "$ROUTEMARK_INTERFACES" ] && setup_routes - fi - - if [ -n "$CLIB_NAT_LOADED" ]; then - progress_message2 "$DOING NAT..."; setup_nat - progress_message2 "$DOING NETMAP..."; setup_netmap - fi + setup_providers $(find_file providers) + [ -n "$ROUTEMARK_INTERFACES" ] && setup_routes + progress_message2 "$DOING NAT..."; setup_nat + progress_message2 "$DOING NETMAP..."; setup_netmap progress_message2 "$DOING Common Rules"; add_common_rules save_progress_message "Setting up SYN Flood Protection..." setup_syn_flood_chains - if [ -n "$CLIB_TUNNELS_LOADED" ]; then - save_progress_message "Setting up IPSEC management..." - setup_ipsec - elif [ -n "$IPSEC_ZONES" ]; then - fatal_error "IPSEC Zones are defined but the Shorewall clib.tunnels module is not loaded" - fi + save_progress_message "Setting up IPSEC management..." - if [ -n "$CLIB_MACLIST_LOADED" ]; then - maclist_hosts=$(find_hosts_by_option maclist) + setup_ipsec - if [ -n "$maclist_hosts" ]; then - save_progress_message "Setting up MAC Filtration..." - setup_mac_lists - fi + maclist_hosts=$(find_hosts_by_option maclist) + + if [ -n "$maclist_hosts" ]; then + save_progress_message "Setting up MAC Filtration..." + setup_mac_lists fi progress_message2 "$DOING $(find_file rules)..." save_progress_message "Setting up Rules..." process_rules - if [ -n "$CLIB_TUNNELS_LOADED" ]; then - tunnels=$(find_file tunnels) + tunnels=$(find_file tunnels) + if [ -f $tunnels ]; then progress_message2 "$DOING $tunnels..." save_progress_message "Setting up Tunnels..." setup_tunnels $tunnels @@ -6165,26 +8137,20 @@ __EOF__ fi if [ -n "$MANGLE_ENABLED" ]; then - if [ -n "$CLIB_TOS_LOADED" ]; then - tos=$(find_file tos) - if [ -f $tos ]; then - save_progress_message "Setting up TOS..." - process_tos $tos - fi + tos=$(find_file tos) + if [ -f $tos ]; then + save_progress_message "Setting up TOS..." + process_tos $tos fi - if [ -n "$CLIB_ECN_LOADED" ]; then - ecn=$(find_file ecn) - if [ -f $ecn ]; then - save_progress_message "Setting up ECN..." - setup_ecn $ecn - fi + ecn=$(find_file ecn) + if [ -f $ecn ]; then + save_progress_message "Setting up ECN..." + setup_ecn $ecn fi - if [ -n "$CLIB_TCRULES_LOADED" ]; then - save_progress_message "Setting up TC Rules..." - setup_tc - fi + save_progress_message "Setting up TC Rules..." + setup_tc fi progress_message2 "$DOING Rule Activation..." @@ -6195,12 +8161,7 @@ __EOF__ save_command add_ip_aliases $ALIASES_TO_ADD fi - files="zones" - - [ -n "$CLIB_PROXYARP_LOADED" ] && files="$files proxyarp" - [ -n "$CLIB_NAT_LOADED" ] && files="$files nat" - - for file in chains $files; do + for file in chains nat proxyarp zones; do save_command "cat > \${VARDIR}/$file $LEFTSHIFT __EOF__" cat $STATEDIR/$file >&3 save_command_unindented __EOF__ @@ -6278,6 +8239,8 @@ restore_firewall() __EOF__ + compile_refresh_firewall + exec 3>&- if [ -n "$checking" ]; then @@ -6307,440 +8270,6 @@ __EOF__ } -# -# 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 "" - ;; - *) - fatal_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 "" - ;; - *) - fatal_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= - FUNCTIONS= - VERSION_FILE= - LOGFORMAT= - LOGRULENUMBERS= - ADMINISABSENTMINDED= - BLACKLISTNEWONLY= - MODULE_SUFFIX= - ACTIONS= - USEDACTIONS= - SMURF_LOG_LEVEL= - DISABLE_IPV6= - BRIDGING= - 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= - - SHAREDIR=/usr/share/shorewall - VARDIR=/var/lib/shorewall - [ -z "$EXPORT" ] && CONFDIR=/etc/shorewall || CONFDIR=${SHAREDIR}/configfiles - - FUNCTIONS=${SHAREDIR}/functions - - [ -n "${VERBOSE:=2}" ] - # - # Load Libraries shared with run-time - # - if [ -f $FUNCTIONS ]; then - [ $VERBOSE -ge 2 ] && echo "Loading $FUNCTIONS..." - . $FUNCTIONS - else - fatal_error "$FUNCTIONS does not exist!" - fi - - TMP_DIR=$(mktempdir) - - [ -n "$TMP_DIR" ] && chmod 700 $TMP_DIR || \ - fatal_error "Can't create a temporary directory" - - trap "[ -n "$OUTPUT" ] && rm -f $OUTPUT;rm -rf $TMP_DIR; exit 2" 1 2 3 4 5 6 9 - - 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) - 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 configuration files and load modules - # - strip_file zones - strip_file policy - strip_file interfaces - strip_file hosts - strip_file routestopped - strip_file actions - strip_file actions.std ${SHAREDIR}/actions.std - strip_file masq - strip_file blacklist - strip_file rules - strip_file rfc1918 - strip_file_and_clib_load proxyarp proxyarp CLIB_PROXYARP_LOADED - strip_file_and_clib_load maclist maclist CLIB_MACLIST_LOADED - strip_file_and_clib_load nat nat CLIB_NAT_LOADED - strip_file_and_clib_load netmap nat CLIB_NAT_LOADED - strip_file_and_clib_load tcrules tcrules CLIB_TCRULES_LOADED - strip_file_and_clib_load accounting accounting CLIB_ACCOUNTING_LOADED - strip_file_and_clib_load ecn ecn CLIB_ECN_LOADED - strip_file_and_clib_load providers providers CLIB_PROVIDERS_LOADED - strip_file_and_clib_load tos tos CLIB_TOS_LOADED - strip_file_and_clib_load ipsec tunnels CLIB_TUNNELS_LOADED - strip_file_and_clib_load tunnels tunnels CLIB_TUNNELS_LOADED - strip_file_and_lib_load tcdevices tc LIB_TC_LOADED - strip_file_and_lib_load tcclasses tc LIB_TC_LOADED - - if [ -n "$CLIB_TCRULES_LOADED" ]; then - lib_load tcrules LIB_TCRULES_LOADED "Entries in the tcrules file require Shorewall library module lib.tcrules which is not installed" - fi - # - # 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= -} - -# -# Give Usage Information -# -usage() { - echo "Usage: $0 [debug] check|compile }" - exit 1 -} - # # E X E C U T I O N B E G I N S H E R E # @@ -6755,6 +8284,23 @@ NOLOCK= trap "exit 2" 1 2 3 4 5 6 9 +SHAREDIR=/usr/share/shorewall +VARDIR=/var/lib/shorewall +[ -z "$EXPORT" ] && CONFDIR=/etc/shorewall || CONFDIR=${SHAREDIR}/configfiles + +FUNCTIONS=${SHAREDIR}/functions + +[ -n "${VERBOSE:=2}" ] + +if [ -f $FUNCTIONS ]; then + [ $VERBOSE -ge 2 ] && echo "Loading $FUNCTIONS..." + . $FUNCTIONS +else + fatal_error "$FUNCTIONS does not exist!" +fi + +PROGRAM=compiler + COMMAND="$1" case "$COMMAND" in diff --git a/Shorewall/fallback.sh b/Shorewall/fallback.sh index c0e4b6171..b7616e7ee 100755 --- a/Shorewall/fallback.sh +++ b/Shorewall/fallback.sh @@ -28,7 +28,7 @@ # shown below. Simply run this script to revert to your prior version of # Shoreline Firewall. -VERSION=3.3.0 +VERSION=3.2.3 usage() # $1 = exit status { diff --git a/Shorewall/firewall b/Shorewall/firewall index 223e48f67..17669d7e0 100755 --- a/Shorewall/firewall +++ b/Shorewall/firewall @@ -38,7 +38,6 @@ # byte counts # shorewall clear Remove all Shorewall chains # and rules/policies. -# shorewall refresh . Rebuild the common chain # # Mutual exclusion -- These functions are jackets for the mutual exclusion # routines in $FUNCTIONS. They invoke @@ -171,490 +170,6 @@ deletechain() # $1 = name of chain qt $IPTABLES -L $1 -n && qt $IPTABLES -F $1 && qt $IPTABLES -X $1 } -# -# 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) - ;; - *) - 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" ] || fatal_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" -} - -# -# 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 -} - -# -# 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 - else - echo -i $1 - fi -} - -match_dest_dev() -{ - if [ -n "$BRIDGING" ]; then - known_port $1 && physdev_echo "--physdev-out $1" || echo -o $1 - else - echo -o $1 - fi -} - -# -# -# 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) - 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 - ;; - 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" -} - # # Find broadcast addresses -- if we are compiling a script and 'detect' is specified for an interface # the function returns nothing for that interface @@ -670,128 +185,6 @@ find_broadcasts() { 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#*:} - run_iptables $1 INPUT -i $interface $(source_ip_range $networks) -j ACCEPT - [ -z "$ADMINISABSENTMINDED" -o $COMMAND != stop ] && \ - run_iptables $1 OUTPUT -o $interface $(dest_ip_range $networks) -j ACCEPT - - matched= - - if list_search $host $source ; then - run_iptables $1 FORWARD -i $interface $(source_ip_range $networks) -j ACCEPT - matched=Yes - fi - - if list_search $host $dest ; then - run_iptables $1 FORWARD -o $interface $(dest_ip_range $networks) -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= - - [ -f $TMP_DIR/routestopped ] || 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 - -} - # # For each entry in the CRITICALHOSTS global list, add INPUT and OUTPUT rules to # enable traffic to/from those hosts. @@ -1111,53 +504,6 @@ delete_nat() { } # -# Setup ECN disabling rules -# -setup_ecn() # $1 = file name -{ - local interfaces="" - local hosts= - local h - - strip_file ecn $1 - - progress_message2 "Processing $1..." - - while read interface host; do - expandv interface host - list_search $interface $ALL_INTERFACES || \ - startup_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 "Setting up 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 " ECN Disabled to $h through $interface" - done - fi -} - # Display elements of a list with leading white space # display_list() # $1 = List Title, rest of $* = list to display @@ -1165,124 +511,6 @@ display_list() # $1 = List Title, rest of $* = list to display [ $# -gt 1 ] && echo " $*" } -# -# Add a record to the blacklst chain -# -# $source = address match -# $proto = protocol selector -# $dport = destination port selector -# -add_blacklist_rule() { - if [ -n "$BLACKLIST_LOGLEVEL" ]; then - log_rule $BLACKLIST_LOGLEVEL blacklst $BLACKLIST_DISPOSITION $(fix_bang $source $proto $dport) - fi - - run_iptables2 -A blacklst $source $proto $dport -j $disposition -} - -# -# Process a record from the blacklist file -# -# $networks = address/networks -# $protocol = Protocol Number/Name -# $port = Port Number/Name -# -process_blacklist_rec() { - local source - local addr - local proto - local dport - local temp - local setname - - for addr in $(separate_list $networks); do - case $addr in - -) - source= - ;; - ~*) - addr=$(echo $addr | sed 's/~//;s/-/:/g') - source="--match mac --mac-source $addr" - ;; - *) - source="$(source_ip_range $addr)" - ;; - esac - - if [ -n "$protocol" ]; then - proto=" -p $protocol " - - case $protocol in - tcp|TCP|6|udp|UDP|17) - if [ -n "$ports" ]; then - if [ -n "$MULTIPORT" -a \ - "$ports" != "${ports%,*}" -a \ - "$ports" = "${ports%:*}" -a \ - $(list_count $ports) -le 15 ] - then - dport="-m multiport --dports $ports" - add_blacklist_rule - else - for dport in $(separate_list $ports); do - dport="--dport $dport" - add_blacklist_rule - done - fi - else - add_blacklist_rule - fi - ;; - icmp|ICMP|0) - if [ -n "$ports" ]; then - for dport in $(separate_list $ports); do - dport="--icmp-type $dport" - add_blacklist_rule - done - else - add_blacklist_rule - fi - ;; - *) - add_blacklist_rule - ;; - esac - else - add_blacklist_rule - fi - - if [ -n "$ports" ]; then - addr="$addr $protocol $ports" - elif [ -n "$protocol" ]; then - addr="$addr $protocol" - fi - - progress_message " $addr added to Black List" - done -} - -# -# Refresh the Black List -# -refresh_blacklist() { - local f=$(find_file blacklist) - local disposition=$BLACKLIST_DISPOSITION - - if qt $IPTABLES -L blacklst -n ; then - progress_message2 "Loading Black List..." - - strip_file blacklist $f - - [ "$disposition" = REJECT ] && disposition=reject - - run_iptables -F blacklst - - while read networks protocol ports; do - expandv networks protocol ports - process_blacklist_rec - done < $TMP_DIR/blacklist - fi -} - # # Verify that kernel has netfilter support # @@ -1319,513 +547,372 @@ check_disabled_startup() { } # -# Refresh queuing and classes +# Add a host or networks to a zone # -refresh_tc() { - # - # tc4shorewall (setup_traffic_shaping()) uses this function to report progress - # - progress_message_and_save() +add_to_zone() # $1...${n-1} = [:] $n = zone +{ + local interface host zone z h z1 z2 chain + local dhcp_interfaces blacklist_interfaces maclist_interfaces + local tcpflags_interfaces newhostlist= + local rulenum source_chain dest_hosts iface hosts hostlist= + + nat_chain_exists() # $1 = chain name { - progress_message $@ + qt $IPTABLES -t nat -L $1 -n } - echo "Refreshing Traffic Control Rules..." + do_iptables() # $@ = command + { + [ -n "$BRIDGING" ] && [ -f $TMP_DIR/physdev ] && rm -f $TMP_DIR/physdev + [ -n "$IPRANGE_MATCH" ] && [ -f $TMP_DIR/iprange ] && rm -f $TMP_DIR/iprange - [ -n "$CLEAR_TC" ] && delete_tc1 - - [ -n "$MARK_IN_FORWARD_CHAIN" ] && chain=tcfor || chain=tcpre - - # - # Flush the TC mangle chains - # - - if [ -n "$MANGLE_FORWARD" ]; then - run_iptables -t mangle -F tcfor - run_iptables -t mangle -F tcpost - fi - - run_iptables -t mangle -F tcpre - run_iptables -t mangle -F tcout - # - # Remove all exclusion chains from the mangle table - # - $IPTABLES -t mangle -L -n | grep '^Chain excl_' | while read junk chain rest; do - run_iptables -t mangle -F $chain - run_iptables -t mangle -X $chain - done - - # - # Process the TC Rules File - # - strip_file tcrules - - if [ -s $TMP_DIR/tcrules ]; then - lib_load tcrules LIB_TCRULES_LOADED "Entries in the tcrules file require the Shorewall library lib.tcrules which is not installed" - - while read mark sources dests proto ports sports user testval; do - expandv mark sources dests proto ports sports user testval tos - rule=$(echo "$mark $sources $dests $proto $ports $sports $user $testval $tos") - process_tc_rule - done < $TMP_DIR/tcrules - fi - - if [ -n "$TC_SCRIPT" ]; then - run_user_exit $TC_SCRIPT - elif [ -n "$TC_ENABLED" ]; then - strip_file tcdevices - strip_file tcclasses - - if [ -s $TMP_DIR/tcdevices -o -s $TMP_DIR/tcclasses ]; then - lib_load tc LIB_TC_LOADED "Entries in the tcdevices file require the Shorewall library lib.tc which is not installed" - setup_traffic_shaping + if ! $IPTABLES $@ ; then + error_message "ERROR: Can't add $newhost to zone $zone" fi - fi - -} - -# -# Refresh the firewall -# -refresh_firewall() -{ - DOING=Refreshing - DONE=Refreshed - - progress_message3 "Refreshing Shorewall..." - - progress_message2 "Determining Zones and Interfaces..." + } + # + # Load $zones + # determine_zones - + # + # Validate Interfaces File + # validate_interfaces_file + # + # Validate Hosts File + # + validate_hosts_file + # + # Validate IPSec File + # + f=$(find_file $IPSECFILE) - determine_interfaces + [ -f $f ] && setup_ipsec $f + # + # Normalize host list + # + while [ $# -gt 1 ]; do + interface=${1%%:*} + host=${1#*:} + # + # Be sure that the interface was dynamic at last [re]start + # + if ! chain_exists $(input_chain $interface) ; then + startup_error "Unknown interface $interface" + fi - run_user_exit refresh + if ! chain_exists $(dynamic_in $interface) ; then + startup_error "At last Shorewall [re]start, DYNAMIC_ZONES=No in shorewall.conf" + fi + + if [ -z "$host" ]; then + hostlist="$hostlist $interface:0.0.0.0/0" + else + for h in $(separate_list $host); do + hostlist="$hostlist $interface:$h" + done + fi + + shift + done + # + # Validate Zone + # + zone=$1 + + validate_zone $zone || startup_error "Unknown zone: $zone" + + [ "$zone" = $FW ] && startup_error "Can't add $1 to firewall zone" # - # Blacklist + # Be sure that Shorewall has been restarted using a DZ-aware version of the code # - strip_file blacklist - - refresh_blacklist - - ecn=$(find_file ecn) - - [ -f $ecn ] && [ -n "$MANGLE_ENABLED" ] && setup_ecn $ecn - + [ -f ${VARDIR}/chains ] || startup_error "${VARDIR}/chains -- file not found" + [ -f ${VARDIR}/zones ] || startup_error "${VARDIR}/zones -- file not found" # - # Refresh Traffic Control + # Check for duplicates and create a new zone state file # - [ -n "$MANGLE_ENABLED" ] && refresh_tc + > ${VARDIR}/zones_$$ - run_user_exit refreshed + while read z type hosts; do + if [ "$z" = "$zone" ]; then + for h in $hostlist; do + list_search $h $hosts + if [ "$?" -gt 0 ]; then + newhostlist="$newhostlist $h" + else + error_message "$h already in zone $zone" + fi + done - report "Shorewall Refreshed" + [ -z "$hosts" ] && hosts=$newhostlist || hosts="$hosts $newhostlist" + fi + + eval ${z}_hosts=\"$hosts\" + + echo "$z $type $hosts" >> ${VARDIR}/zones_$$ + done < ${VARDIR}/zones + + mv -f ${VARDIR}/zones_$$ ${VARDIR}/zones + + TERMINATOR=fatal_error + # + # Create a new Zone state file + # + for newhost in $newhostlist; do + # + # Isolate interface and host parts + # + interface=${newhost%%:*} + host=${newhost#*:} + # + # If the zone passed in the command has a dnat chain then insert a rule in + # the nat table PREROUTING chain to jump to that chain when the source + # matches the new host(s)# + # + chain=${zone}_dnat + + if nat_chain_exists $chain; then + do_iptables -t nat -A $(dynamic_in $interface) $(source_ip_range $host) $(match_ipsec_in $zone $newhost) -j $chain + fi + # + # Insert new rules into the filter table for the passed interface + # + while read z1 z2 chain; do + [ "$z1" = "$z2" ] && op="-I" || op="-A" + if [ "$z1" = "$zone" ]; then + if [ "$z2" = "$FW" ]; then + do_iptables $op $(dynamic_in $interface) $(match_source_hosts $host) $(match_ipsec_in $z1 $newhost) -j $chain + else + source_chain=$(dynamic_fwd $interface) + if is_ipsec_host $z1 $newhost ; then + do_iptables $op $source_chain $(match_source_hosts $host) $(match_ipsec_in $z1 $newhost) -j ${z1}_frwd + else + eval dest_hosts=\"\$${z2}_hosts\" + + for h in $dest_hosts; do + iface=${h%%:*} + hosts=${h#*:} + + if [ "$iface" != "$interface" -o "$hosts" != "$host" ]; then + do_iptables $op $source_chain $(match_source_hosts $host) -o $iface $(match_dest_hosts $hosts) $(match_ipsec_out $z2 $h) -j $chain + fi + done + fi + fi + elif [ "$z2" = "$zone" ]; then + if [ "$z1" = "$FW" ]; then + # + # Add a rule to the dynamic out chain for the interface + # + do_iptables $op $(dynamic_out $interface) $(match_dest_hosts $host) $(match_ipsec_out $z2 $newhost) -j $chain + else + eval source_hosts=\"\$${z1}_hosts\" + + for h in $source_hosts; do + iface=${h%%:*} + hosts=${h#*:} + + if [ "$iface" != "$interface" -o "$hosts" != "$host" ]; then + if is_ipsec_host $z1 $h; then + do_iptables $op ${z1}_dyn -o $interface $(match_dest_hosts $host) $(match_ipsec_out $z2 $newhost) -j $chain + else + do_iptables $op $(dynamic_fwd $iface) $(match_source_hosts $hosts) -o $interface $(match_dest_hosts $host) $(match_ipsec_out $z2 $newhost) -j $chain + fi + fi + done + fi + fi + done < ${VARDIR}/chains + + progress_message "$newhost added to zone $zone" + + done rm -rf $TMP_DIR } # -# Determine the value for a parameter that defaults to Yes +# Delete a host or networks from a zone # -added_param_value_yes() # $1 = Parameter Name, $2 = Parameter value +delete_from_zone() # $1 = [:] $2 = zone { - local val="$2" + local interface host zone z h z1 z2 chain delhost + local dhcp_interfaces blacklist_interfaces maclist_interfaces tcpflags_interfaces + local rulenum source_chain dest_hosts iface hosts hostlist= - 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 + # Load $zones # - # Establish termination function + determine_zones # - TERMINATOR=startup_error + # Validate Interfaces File # - # Clear all configuration variables + validate_interfaces_file # - 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= - FUNCTIONS= - VERSION_FILE= - LOGFORMAT= - LOGRULENUMBERS= - ADMINISABSENTMINDED= - BLACKLISTNEWONLY= - MODULE_SUFFIX= - ACTIONS= - USEDACTIONS= - SMURF_LOG_LEVEL= - DISABLE_IPV6= - BRIDGING= - PKTTYPE= - USEPKTYPE= - RETAIN_ALIASES= - DELAYBLACKLISTLOAD= - LOGTAGONLY= - LOGALLNEW= - RFC1918_STRICT= - MACLIST_TTL= - SAVE_IPSETS= - RESTOREFILE= - MAPOLDACTIONS= - IMPLICIT_CONTINUE= - HIGH_ROUTE_MARKS= - TC_EXPERT= + # Validate Hosts File + # + validate_hosts_file + # + # Validate IPSec File + # + f=$(find_file ipsec) - RESTOREBASE= - 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= + [ -f $f ] && setup_ipsec $f - SHAREDIR=/usr/share/shorewall - VARDIR=/var/lib/shorewall - CONFDIR=/etc/shorewall - - FUNCTIONS=${SHAREDIR}/functions - - if [ -f $FUNCTIONS ]; then - [ $VERBOSE -gt 1 ] && echo "Loading $FUNCTIONS..." - . $FUNCTIONS - else - startup_error "$FUNCTIONS does not exist!" - fi - - TMP_DIR=$(mktempdir) - - [ -n "$TMP_DIR" ] && chmod 700 $TMP_DIR || \ - startup_error "Can't create a temporary directory" - - trap "[ -n "$RESTOREBASE" ] && rm -f $RESTOREBASE;rm -rf $TMP_DIR; my_mutex_off; exit 2" 1 2 3 4 5 6 9 - - 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 - startup_error "Cannot read $config (Hint: Are you root?)" + # + # Normalize host list + # + while [ $# -gt 1 ]; do + interface=${1%%:*} + host=${1#*:} + # + # Be sure that the interface was dynamic at last [re]start + # + if ! chain_exists $(input_chain $interface) ; then + startup_error "Unknown interface $interface" fi - else - startup_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" ]; then - - load_kernel_modules - - if [ -z "$IPTABLES" ]; then - IPTABLES=$(mywhich iptables 2> /dev/null) - - [ -z "$IPTABLES" ] && startup_error "Can't find iptables executable" - else - [ -e "$IPTABLES" ] || startup_error "\$IPTABLES=$IPTABLES does not exist or is not executable" + if ! chain_exists $(dynamic_in $interface) ; then + startup_error "At last Shorewall [re]start, DYNAMIC_ZONES=No in shorewall.conf" fi - determine_capabilities - [ -d ${VARDIR} ] || mkdir -p ${VARDIR} + if [ -z "$host" ]; then + hostlist="$hostlist $interface:0.0.0.0/0" + else + for h in $(separate_list $host); do + hostlist="$hostlist $interface:$h" + done + fi - else - f=$(find_file capabilities) + shift + done + # + # Validate Zone + # + zone=$1 - [ -f $f ] && . $f || startup_error "The -e flag requires a capabilities file" - fi + validate_zone $zone || startup_error "Unknown zone: $zone" - ALLOWRELATED="$(added_param_value_yes ALLOWRELATED $ALLOWRELATED)" - [ -n "$ALLOWRELATED" ] || \ - startup_error "ALLOWRELATED=No is not supported" - ADD_IP_ALIASES="$(added_param_value_yes ADD_IP_ALIASES $ADD_IP_ALIASES)" + [ "$zone" = $FW ] && startup_error "Can't delete from the firewall zone" - if [ -n "${LOGRATE}${LOGBURST}" ]; then - LOGLIMIT="--match limit" - [ -n "$LOGRATE" ] && LOGLIMIT="$LOGLIMIT --limit $LOGRATE" - [ -n "$LOGBURST" ] && LOGLIMIT="$LOGLIMIT --limit-burst $LOGBURST" - fi + # + # Be sure that Shorewall has been restarted using a DZ-aware version of the code + # + [ -f ${VARDIR}/chains ] || startup_error "${VARDIR}/chains -- file not found" + [ -f ${VARDIR}/zones ] || startup_error "${VARDIR}/zones -- file not found" + # + # Delete the passed hosts from the zone state file + # + > ${VARDIR}/zones_$$ - if [ -n "$IP_FORWARDING" ]; then - case "$IP_FORWARDING" in - [Oo][Nn]|[Oo][Ff][Ff]|[Kk][Ee][Ee][Pp]) - ;; - *) - startup_error "Invalid value ($IP_FORWARDING) for IP_FORWARDING" - ;; - esac - else - IP_FORWARDING=On - fi + while read z hosts; do + if [ "$z" = "$zone" ]; then + temp=$hosts + hosts= - [ -n "${BLACKLIST_DISPOSITION:=DROP}" ] + for host in $hostlist; do + found= + for h in $temp; do + if [ "$h" = "$host" ]; then + found=Yes + break + fi + done - case "$CLAMPMSS" in - [0-9]*) - ;; - *) - CLAMPMSS=$(added_param_value_no CLAMPMSS $CLAMPMSS) - ;; - esac + [ -n "$found" ] || error_message "WARNING: $host does not appear to be in zone $zone" + done - 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" ] && \ - startup_error "FORWARDPING=Yes is no longer supported" + for h in $temp; do + found= + for host in $hostlist; do + if [ "$h" = "$host" ]; then + found=Yes + break + fi + done - maclist_target=reject + [ -n "$found" ] || hosts="$hosts $h" + done + fi - if [ -n "$MACLIST_DISPOSITION" ] ; then - case $MACLIST_DISPOSITION in - REJECT) - ;; - DROP) - maclist_target=DROP - ;; - ACCEPT) - maclist_target=RETURN - ;; - *) - startup_error "Invalid value ($MACLIST_DISPOSITION) for MACLIST_DISPOSITION" - ;; - esac - else - MACLIST_DISPOSITION=REJECT - fi + eval ${z}_hosts=\"$hosts\" - if [ -n "$TCP_FLAGS_DISPOSITION" ] ; then - case $TCP_FLAGS_DISPOSITION in - REJECT|ACCEPT|DROP) - ;; - *) - startup_error "Invalid value ($TCP_FLAGS_DISPOSITION) for TCP_FLAGS_DISPOSITION" - ;; - esac - else - TCP_FLAGS_DISPOSITION=DROP - fi + echo "$z $hosts" >> ${VARDIR}/zones_$$ + done < ${VARDIR}/zones - [ -n "${RFC1918_LOG_LEVEL:=info}" ] + mv -f ${VARDIR}/zones_$$ ${VARDIR}/zones - 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) + TERMINATOR=fatal_error - 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 - startup_error "Invalid LOGFORMAT string: \"$LOGFORMAT\"" + for delhost in $hostlist; do + interface=${delhost%%:*} + host=${delhost#*:} + # + # Delete any nat table entries for the host(s) + # + qt_iptables -t nat -D $(dynamic_in $interface) $(match_source_hosts $host) $(match_ipsec_in $zone $delhost) -j ${zone}_dnat + # + # Delete rules rules the input chains for the passed interface + # + while read z1 z2 chain; do + if [ "$z1" = "$zone" ]; then + if [ "$z2" = "$FW" ]; then + qt_iptables -D $(dynamic_in $interface) $(match_source_hosts $host) $(match_ipsec_in $z1 $delhost) -j $chain + else + source_chain=$(dynamic_fwd $interface) + if is_ipsec_host $z1 $delhost ; then + qt_iptables -D $source_chain $(match_source_hosts $host) $(match_ipsec_in $z1 $newhost) -j ${z1}_frwd + else + eval dest_hosts=\"\$${z2}_hosts\" + + [ "$z2" = "$zone" ] && dest_hosts="$dest_hosts $hostlist" + + for h in $dest_hosts; do + iface=${h%%:*} + hosts=${h#*:} + + if [ "$iface" != "$interface" -o "$hosts" != "$host" ]; then + qt_iptables -D $source_chain $(match_source_hosts $host) -o $iface $(match_dest_hosts $hosts) $(match_ipsec_out $z2 $h) -j $chain + fi + done + fi + fi + elif [ "$z2" = "$zone" ]; then + if [ "$z1" = "$FW" ]; then + qt_iptables -D $(dynamic_out $interface) $(match_dest_hosts $host) $(match_ipsec_out $z2 $delhost) -j $chain + else + eval source_hosts=\"\$${z1}_hosts\" + + for h in $source_hosts; do + iface=${h%%:*} + hosts=${h#*:} + + if [ "$iface" != "$interface" -o "$hosts" != "$host" ]; then + if is_ipsec_host $z1 $h; then + qt_iptables -D ${z1}_dyn -o $interface $(match_dest_hosts $host) $(match_ipsec_out $z2 $delhost) -j $chain + else + qt_iptables -D $(dynamic_fwd $iface) $(match_source_hosts $hosts) -o $interface $(match_dest_hosts $host) $(match_ipsec_out $z2 $delhost) -j $chain + fi + fi + done + fi fi - else - temp=$(printf "$LOGFORMAT" fooxx barxx 2> /dev/null) - if [ $? -ne 0 ]; then - startup_error "Invalid LOGFORMAT string: \"$LOGFORMAT\"" - fi - fi + done < ${VARDIR}/chains - [ ${#temp} -le 29 ] || startup_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) - 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= + progress_message "$delhost removed from zone $zone" - [ -n "$HIGH_ROUTE_MARKS" -a -z "$XCONNMARK" ] && startup_error "HIGH_ROUTE_MARKS=Yes requires extended CONNMARK target, extended CONNMARK match support and extended MARK support" - - case ${IPSECFILE:=ipsec} in - ipsec|zones) - ;; - *) - startup_error "Invalid value ($IPSECFILE) for IPSECFILE option" - ;; - esac - - case ${MACLIST_TABLE:=filter} in - filter) - ;; - mangle) - [ $MACLIST_DISPOSITION = reject ] && startup_error "MACLIST_DISPOSITION=REJECT is not allowed with MACLIST_TABLE=mangle" - ;; *) - startup_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 ] || startup_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" ] || startup_error "Traffic Shaping requires mangle support in your kernel and iptables" - fi - - [ "x${SHOREWALL_DIR}" = "x." ] && SHOREWALL_DIR="$PWD" - - # - # 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 - startup_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 + done + rm -rf $TMP_DIR } # # Give Usage Information # usage() { - echo "Usage: $0 [debug] {start|stop|reset|restart|refresh|clear}" + echo "Usage: $0 [debug] {start|stop|reset|restart|clear}" exit 1 } @@ -1843,6 +930,21 @@ NOLOCK= trap "my_mutex_off; exit 2" 1 2 3 4 5 6 9 +SHAREDIR=/usr/share/shorewall +VARDIR=/var/lib/shorewall +CONFDIR=/etc/shorewall + +FUNCTIONS=${SHAREDIR}/functions + +if [ -f $FUNCTIONS ]; then + [ $VERBOSE -ge 2 ] && echo "Loading $FUNCTIONS..." + . $FUNCTIONS +else + startup_error "$FUNCTIONS does not exist!" +fi + +PROGRAM=firewall + COMMAND="$1" case "$COMMAND" in @@ -1879,20 +981,6 @@ case "$COMMAND" in my_mutex_off ;; - refresh) - [ $# -ne 1 ] && usage - do_initialize - my_mutex_on - if ! shorewall_is_started ; then - echo "Shorewall Not Started" >&2 - [ -n "$TMP_DIR" ] && rm -rf $TMP_DIR - my_mutex_off - exit 2; - fi - refresh_firewall; - my_mutex_off - ;; - clear) [ $# -ne 1 ] && usage do_initialize @@ -1904,6 +992,36 @@ case "$COMMAND" in my_mutex_off ;; + add) + [ $# -lt 3 ] && usage + do_initialize + my_mutex_on + if ! shorewall_is_started ; then + echo "Shorewall Not Started" + [ -n "$TMP_DIR" ] && rm -rf $TMP_DIR + my_mutex_off + exit 2; + fi + shift + add_to_zone $@ + my_mutex_off + ;; + + delete) + [ $# -lt 3 ] && usage + do_initialize + my_mutex_on + if ! shorewall_is_started ; then + echo "Shorewall Not Started" + [ -n "$TMP_DIR" ] && rm -rf $TMP_DIR + my_mutex_off + exit 2; + fi + shift + delete_from_zone $@ + my_mutex_off + ;; + call) # # Undocumented way to call functions in ${SHAREDIR}/firewall directly diff --git a/Shorewall/functions b/Shorewall/functions index 53ec10c95..0cd9a5ea2 100644 --- a/Shorewall/functions +++ b/Shorewall/functions @@ -21,32 +21,3116 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA -LIBVERSION=30192 - -# -# Load a run-time library -# -lib_load() # $1 = library name, $2=name of 'loaded' symbol, $3-n = Fatal Error Message -{ - eval local loaded="\$${2}" - - if [ -z "$loaded" ]; then - - local lib=${SHAREDIR}/lib.${1} - - if [ -x $lib ]; then - . $lib - else - shift 2 - fatal_error "$@" - fi - fi -} +LIBVERSION=30200 [ -n "${VARDIR:=/var/lib/shorewall}" ] [ -n "${SHAREDIR:=/usr/share/shorewall}" ] [ -n "${CONFDIR:=/etc/shorewall}" ] -SHOREWALL_LIBRARY=Loaded +# +# Message to stderr +# +error_message() # $* = Error Message +{ + echo " $@" >&2 +} -lib_load base LIB_BASE_LOADED "Installation Error: ${SHAREDIR}/lib.base not found" +# 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 ${zoned}_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 +} + +# +# 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) + [ -n "$DYNAMIC_ZONES" -a -n "$EXPORT" ] && fatal_error "DYNAMIC_ZONES=Yes is incompatible with the -e option" + 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= +} + +# +# Give Usage Information +# +usage() { + echo "Usage: $0 [debug] check|compile }" + exit 1 +} + +SHOREWALL_LIBRARY=Loaded diff --git a/Shorewall/help b/Shorewall/help index da87a9e43..aafdd718a 100755 --- a/Shorewall/help +++ b/Shorewall/help @@ -28,6 +28,28 @@ case $1 in +add) + echo "add: add [:] ... + Adds a list of hosts or subnets to a dynamic zone usually used with VPN's. + + shorewall add interface:host-list ... zone - Adds the specified interface + (and host-list if included) to the specified zone. + + A host-list is a comma-separated list whose elements are: + + A host or network address + The name of a bridge port + The name of a bridge port followed by a colon (":") and a host or + network address. + + Example: + + shorewall add ipsec0:192.0.2.24 vpn1 -- adds the address 192.0.2.24 + from interface ipsec0 to the zone vpn1. + + See also \"help host\"" + ;; + address|host) echo "<$1>: May be either a host IP address such as 192.168.1.4 or a network address in @@ -88,7 +110,7 @@ debug) If you include the keyword debug as the first argument to any of these commands: - start|stop|restart|reset|clear|refresh|check|compile + start|stop|restart|reset|clear|refresh|check|add|delete|compile then a shell trace of the command is produced. For example: @@ -100,6 +122,28 @@ debug) The word 'trace' is a synonym for 'debug'." ;; +delete) + echo "delete: delete [:] ... + Deletes a list of hosts or networks from a dynamic zone usually used with VPN's. + + shorewall delete interface[:host-list] ... zone - Deletes the specified + interfaces (and host list if included) from the specified zone. + + A host-list is a comma-separated list whose elements are: + + A host or network address + The name of a bridge port + The name of a bridge port followed by a colon (":") and a host or + network address. + + Example: + + shorewall delete ipsec0:192.0.2.24 vpn1 -- deletes the address + 192.0.2.24 from interface ipsec0 from zone vpn1 + + See also \"help host\"" + ;; + drop) echo "$1: $1
... Causes packets from the specified
to be ignored diff --git a/Shorewall/install.sh b/Shorewall/install.sh index bf9e1e547..eb7bd998a 100755 --- a/Shorewall/install.sh +++ b/Shorewall/install.sh @@ -22,19 +22,7 @@ # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA # -VERSION=3.3.0 - -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 -} +VERSION=3.2.3 usage() # $1 = exit status { @@ -43,11 +31,6 @@ usage() # $1 = exit status echo " $ME -v" echo " $ME -h" echo " $ME -n" - echo " $ME -c" - echo " $ME -l [ ... ]" - echo " $ME -L [ ... ]" - echo " $ME -m" - echo " $ME -s" exit $1 } @@ -149,7 +132,9 @@ install_file_with_backup() # $1 = source $2 = target $3 = mode $4 = (optional) b # DEST is the SysVInit script directory # INIT is the name of the script in the $DEST directory # RUNLEVELS is the chkconfig parmeters for firewall +# ARGS is "yes" if we've already parsed an argument # +ARGS="" if [ -z "$DEST" ] ; then DEST="/etc/init.d" @@ -172,10 +157,6 @@ if [ -z "$GROUP" ] ; then fi NOBACKUP= -NOCONFIGFILES= -XLIBS= -XCLIBS= -NOMACROS= while [ $# -gt 0 ] ; do case "$1" in @@ -189,43 +170,12 @@ while [ $# -gt 0 ] ; do -n) NOBACKUP=Yes ;; - -c) - NOCONFIGFILES=Yes - ;; - -m) - NOMACROS=Yes - ;; - -l) - while [ $# -gt 1 ]; do - case $2 in - -*) - break - ;; - *) - XLIBS="$XLIBS $2" - shift - ;; - esac - done - ;; - -L) - while [ $# -gt 1 ]; do - case $2 in - -*) - break - ;; - *) - XCLIBS="$XCLIBS $2" - shift - ;; - esac - done - ;; *) usage 1 ;; esac shift + ARGS="yes" done PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin:/usr/local/sbin @@ -304,34 +254,33 @@ echo "Shorewall script installed in ${PREFIX}${DEST}/$INIT" # mkdir -p ${PREFIX}/etc/shorewall mkdir -p ${PREFIX}/usr/share/shorewall -[ -n "$NOCONFIGFILES" ] || mkdir -p ${PREFIX}/usr/share/shorewall/configfiles +mkdir -p ${PREFIX}/usr/share/shorewall/configfiles mkdir -p ${PREFIX}/var/lib/shorewall chmod 755 ${PREFIX}/etc/shorewall chmod 755 ${PREFIX}/usr/share/shorewall -[ -n "$NOCONFIGFILES" ] || chmod 755 ${PREFIX}/usr/share/shorewall/configfiles +chmod 755 ${PREFIX}/usr/share/shorewall/configfiles -if [ -z "$NOCONFIGFILES" ]; then - # - # Install the config file - # - run_install $OWNERSHIP -m 0644 shorewall.conf ${PREFIX}/usr/share/shorewall/configfiles/shorewall.conf +# +# Install the config file +# +run_install $OWNERSHIP -m 0644 shorewall.conf ${PREFIX}/usr/share/shorewall/configfiles/shorewall.conf - qt mywhich perl && perl -p -w -i -e 's|^CONFIG_PATH=.*|CONFIG_PATH=/usr/share/shorewall/configfiles:/usr/share/shorewall|;' ${PREFIX}/usr/share/shorewall/configfiles/shorewall.conf -fi +qt mywhich perl && perl -p -w -i -e 's|^CONFIG_PATH=.*|CONFIG_PATH=/usr/share/shorewall/configfiles:/usr/share/shorewall|;' ${PREFIX}/usr/share/shorewall/configfiles/shorewall.conf if [ ! -f ${PREFIX}/etc/shorewall/shorewall.conf ]; then - run_install $OWNERSHIP -m 0644 shorewall.conf ${PREFIX}/etc/shorewall/shorewall.conf - echo "Config file installed as ${PREFIX}/etc/shorewall/shorewall.conf" + run_install $OWNERSHIP -m 0644 shorewall.conf ${PREFIX}/etc/shorewall/shorewall.conf + echo "Config file installed as ${PREFIX}/etc/shorewall/shorewall.conf" fi + if [ -n "$ARCHLINUX" ] ; then sed -e 's!LOGFILE=/var/log/messages!LOGFILE=/var/log/messages.log!' -i ${PREFIX}/etc/shorewall/shorewall.conf fi # # Install the zones file # -[ -n "$NOCONFIGFILES" ] || run_install $OWNERSHIP -m 0644 zones ${PREFIX}/usr/share/shorewall/configfiles/zones +run_install $OWNERSHIP -m 0644 zones ${PREFIX}/usr/share/shorewall/configfiles/zones if [ ! -f ${PREFIX}/etc/shorewall/zones ]; then run_install $OWNERSHIP -m 0744 zones ${PREFIX}/etc/shorewall/zones @@ -364,7 +313,7 @@ echo "Help command executor installed in ${PREFIX}/usr/share/shorewall/help" # # Install the policy file # -[ -n "$NOCONFIGFILES" ] || run_install $OWNERSHIP -m 0644 policy ${PREFIX}/usr/share/shorewall/configfiles/policy +run_install $OWNERSHIP -m 0644 policy ${PREFIX}/usr/share/shorewall/configfiles/policy if [ ! -f ${PREFIX}/etc/shorewall/policy ]; then run_install $OWNERSHIP -m 0600 policy ${PREFIX}/etc/shorewall/policy @@ -373,7 +322,7 @@ fi # # Install the interfaces file # -[ -n "$NOCONFIGFILES" ] || run_install $OWNERSHIP -m 0644 interfaces ${PREFIX}/usr/share/shorewall/configfiles/interfaces +run_install $OWNERSHIP -m 0644 interfaces ${PREFIX}/usr/share/shorewall/configfiles/interfaces if [ ! -f ${PREFIX}/etc/shorewall/interfaces ]; then run_install $OWNERSHIP -m 0600 interfaces ${PREFIX}/etc/shorewall/interfaces @@ -382,7 +331,7 @@ fi # # Install the ipsec file # -[ -n "$NOCONFIGFILES" ] || run_install $OWNERSHIP -m 0644 ipsec ${PREFIX}/usr/share/shorewall/configfiles/ipsec +run_install $OWNERSHIP -m 0644 ipsec ${PREFIX}/usr/share/shorewall/configfiles/ipsec if [ ! -f ${PREFIX}/etc/shorewall/ipsec ]; then run_install $OWNERSHIP -m 0600 ipsec ${PREFIX}/etc/shorewall/ipsec @@ -392,7 +341,7 @@ fi # # Install the hosts file # -[ -n "$NOCONFIGFILES" ] || run_install $OWNERSHIP -m 0644 hosts ${PREFIX}/usr/share/shorewall/configfiles/hosts +run_install $OWNERSHIP -m 0644 hosts ${PREFIX}/usr/share/shorewall/configfiles/hosts if [ ! -f ${PREFIX}/etc/shorewall/hosts ]; then run_install $OWNERSHIP -m 0600 hosts ${PREFIX}/etc/shorewall/hosts @@ -401,7 +350,7 @@ fi # # Install the rules file # -[ -n "$NOCONFIGFILES" ] || run_install $OWNERSHIP -m 0644 rules ${PREFIX}/usr/share/shorewall/configfiles/rules +run_install $OWNERSHIP -m 0644 rules ${PREFIX}/usr/share/shorewall/configfiles/rules if [ ! -f ${PREFIX}/etc/shorewall/rules ]; then run_install $OWNERSHIP -m 0600 rules ${PREFIX}/etc/shorewall/rules @@ -410,7 +359,7 @@ fi # # Install the NAT file # -[ -n "$NOCONFIGFILES" ] || run_install $OWNERSHIP -m 0644 nat ${PREFIX}/usr/share/shorewall/configfiles/nat +run_install $OWNERSHIP -m 0644 nat ${PREFIX}/usr/share/shorewall/configfiles/nat if [ ! -f ${PREFIX}/etc/shorewall/nat ]; then run_install $OWNERSHIP -m 0600 nat ${PREFIX}/etc/shorewall/nat @@ -419,7 +368,7 @@ fi # # Install the NETMAP file # -[ -n "$NOCONFIGFILES" ] || run_install $OWNERSHIP -m 0644 netmap ${PREFIX}/usr/share/shorewall/configfiles/netmap +run_install $OWNERSHIP -m 0644 netmap ${PREFIX}/usr/share/shorewall/configfiles/netmap if [ ! -f ${PREFIX}/etc/shorewall/netmap ]; then run_install $OWNERSHIP -m 0600 netmap ${PREFIX}/etc/shorewall/netmap @@ -428,7 +377,7 @@ fi # # Install the Parameters file # -[ -n "$NOCONFIGFILES" ] || run_install $OWNERSHIP -m 0644 params ${PREFIX}/usr/share/shorewall/configfiles/params +run_install $OWNERSHIP -m 0644 params ${PREFIX}/usr/share/shorewall/configfiles/params if [ ! -f ${PREFIX}/etc/shorewall/params ]; then run_install $OWNERSHIP -m 0644 params ${PREFIX}/etc/shorewall/params @@ -437,7 +386,7 @@ fi # # Install the proxy ARP file # -[ -n "$NOCONFIGFILES" ] || run_install $OWNERSHIP -m 0644 proxyarp ${PREFIX}/usr/share/shorewall/configfiles/proxyarp +run_install $OWNERSHIP -m 0644 proxyarp ${PREFIX}/usr/share/shorewall/configfiles/proxyarp if [ ! -f ${PREFIX}/etc/shorewall/proxyarp ]; then run_install $OWNERSHIP -m 0600 proxyarp ${PREFIX}/etc/shorewall/proxyarp @@ -446,7 +395,7 @@ fi # # Install the Stopped Routing file # -[ -n "$NOCONFIGFILES" ] || run_install $OWNERSHIP -m 0644 routestopped ${PREFIX}/usr/share/shorewall/configfiles/routestopped +run_install $OWNERSHIP -m 0644 routestopped ${PREFIX}/usr/share/shorewall/configfiles/routestopped if [ ! -f ${PREFIX}/etc/shorewall/routestopped ]; then run_install $OWNERSHIP -m 0600 routestopped ${PREFIX}/etc/shorewall/routestopped @@ -455,7 +404,7 @@ fi # # Install the Mac List file # -[ -n "$NOCONFIGFILES" ] || run_install $OWNERSHIP -m 0644 maclist ${PREFIX}/usr/share/shorewall/configfiles/maclist +run_install $OWNERSHIP -m 0644 maclist ${PREFIX}/usr/share/shorewall/configfiles/maclist if [ ! -f ${PREFIX}/etc/shorewall/maclist ]; then run_install $OWNERSHIP -m 0600 maclist ${PREFIX}/etc/shorewall/maclist @@ -464,7 +413,7 @@ fi # # Install the Masq file # -[ -n "$NOCONFIGFILES" ] || run_install $OWNERSHIP -m 0644 masq ${PREFIX}/usr/share/shorewall/configfiles/masq +run_install $OWNERSHIP -m 0644 masq ${PREFIX}/usr/share/shorewall/configfiles/masq if [ ! -f ${PREFIX}/etc/shorewall/masq ]; then run_install $OWNERSHIP -m 0600 masq ${PREFIX}/etc/shorewall/masq @@ -482,7 +431,7 @@ echo "Xmodules file installed as ${PREFIX}/usr/share/shorewall/xmodules" # # Install the TC Rules file # -[ -n "$NOCONFIGFILES" ] || run_install $OWNERSHIP -m 0644 tcrules ${PREFIX}/usr/share/shorewall/configfiles/tcrules +run_install $OWNERSHIP -m 0644 tcrules ${PREFIX}/usr/share/shorewall/configfiles/tcrules if [ ! -f ${PREFIX}/etc/shorewall/tcrules ]; then run_install $OWNERSHIP -m 0600 tcrules ${PREFIX}/etc/shorewall/tcrules @@ -492,7 +441,7 @@ fi # # Install the TOS file # -[ -n "$NOCONFIGFILES" ] || run_install $OWNERSHIP -m 0644 tos ${PREFIX}/usr/share/shorewall/configfiles/tos +run_install $OWNERSHIP -m 0644 tos ${PREFIX}/usr/share/shorewall/configfiles/tos if [ ! -f ${PREFIX}/etc/shorewall/tos ]; then run_install $OWNERSHIP -m 0600 tos ${PREFIX}/etc/shorewall/tos @@ -501,7 +450,7 @@ fi # # Install the Tunnels file # -[ -n "$NOCONFIGFILES" ] || run_install $OWNERSHIP -m 0644 tunnels ${PREFIX}/usr/share/shorewall/configfiles/tunnels +run_install $OWNERSHIP -m 0644 tunnels ${PREFIX}/usr/share/shorewall/configfiles/tunnels if [ ! -f ${PREFIX}/etc/shorewall/tunnels ]; then run_install $OWNERSHIP -m 0600 tunnels ${PREFIX}/etc/shorewall/tunnels @@ -510,7 +459,7 @@ fi # # Install the blacklist file # -[ -n "$NOCONFIGFILES" ] || run_install $OWNERSHIP -m 0644 blacklist ${PREFIX}/usr/share/shorewall/configfiles/blacklist +run_install $OWNERSHIP -m 0644 blacklist ${PREFIX}/usr/share/shorewall/configfiles/blacklist if [ ! -f ${PREFIX}/etc/shorewall/blacklist ]; then run_install $OWNERSHIP -m 0600 blacklist ${PREFIX}/etc/shorewall/blacklist @@ -529,7 +478,7 @@ delete_file ${PREFIX}/usr/share/shorewall/tcstart # # Install the Providers file # -[ -n "$NOCONFIGFILES" ] || run_install $OWNERSHIP -m 0644 providers ${PREFIX}/usr/share/shorewall/configfiles/providers +run_install $OWNERSHIP -m 0644 providers ${PREFIX}/usr/share/shorewall/configfiles/providers if [ ! -f ${PREFIX}/etc/shorewall/providers ]; then run_install $OWNERSHIP -m 0600 providers ${PREFIX}/etc/shorewall/providers @@ -539,7 +488,7 @@ fi # # Install the Route Rules file # -[ -n "$NOCONFIGFILES" ] || run_install $OWNERSHIP -m 0644 route_rules ${PREFIX}/usr/share/shorewall/configfiles/route_rules +run_install $OWNERSHIP -m 0644 route_rules ${PREFIX}/usr/share/shorewall/configfiles/route_rules if [ ! -f ${PREFIX}/etc/shorewall/route_rules ]; then run_install $OWNERSHIP -m 0600 route_rules ${PREFIX}/etc/shorewall/route_rules @@ -549,7 +498,7 @@ fi # # Install the tcclasses file # -[ -n "$NOCONFIGFILES" ] || run_install $OWNERSHIP -m 0644 tcclasses ${PREFIX}/usr/share/shorewall/configfiles/tcclasses +run_install $OWNERSHIP -m 0644 tcclasses ${PREFIX}/usr/share/shorewall/configfiles/tcclasses if [ ! -f ${PREFIX}/etc/shorewall/tcclasses ]; then run_install $OWNERSHIP -m 0600 tcclasses ${PREFIX}/etc/shorewall/tcclasses @@ -559,7 +508,7 @@ fi # # Install the tcdevices file # -[ -n "$NOCONFIGFILES" ] || run_install $OWNERSHIP -m 0644 tcdevices ${PREFIX}/usr/share/shorewall/configfiles/tcdevices +run_install $OWNERSHIP -m 0644 tcdevices ${PREFIX}/usr/share/shorewall/configfiles/tcdevices if [ ! -f ${PREFIX}/etc/shorewall/tcdevices ]; then run_install $OWNERSHIP -m 0600 tcdevices ${PREFIX}/etc/shorewall/tcdevices @@ -579,7 +528,7 @@ echo "Default config path file installed as ${PREFIX}/usr/share/shorewall/config # # Install the init file # -[ -n "$NOCONFIGFILES" ] || run_install $OWNERSHIP -m 0644 init ${PREFIX}/usr/share/shorewall/configfiles/init +run_install $OWNERSHIP -m 0644 init ${PREFIX}/usr/share/shorewall/configfiles/init if [ ! -f ${PREFIX}/etc/shorewall/init ]; then run_install $OWNERSHIP -m 0600 init ${PREFIX}/etc/shorewall/init @@ -588,7 +537,7 @@ fi # # Install the initdone file # -[ -n "$NOCONFIGFILES" ] || run_install $OWNERSHIP -m 0644 initdone ${PREFIX}/usr/share/shorewall/configfiles/initdone +run_install $OWNERSHIP -m 0644 initdone ${PREFIX}/usr/share/shorewall/configfiles/initdone if [ ! -f ${PREFIX}/etc/shorewall/initdone ]; then run_install $OWNERSHIP -m 0600 initdone ${PREFIX}/etc/shorewall/initdone @@ -597,7 +546,7 @@ fi # # Install the start file # -[ -n "$NOCONFIGFILES" ] || run_install $OWNERSHIP -m 0644 start ${PREFIX}/usr/share/shorewall/configfiles/start +run_install $OWNERSHIP -m 0644 start ${PREFIX}/usr/share/shorewall/configfiles/start if [ ! -f ${PREFIX}/etc/shorewall/start ]; then run_install $OWNERSHIP -m 0600 start ${PREFIX}/etc/shorewall/start @@ -606,7 +555,7 @@ fi # # Install the stop file # -[ -n "$NOCONFIGFILES" ] || run_install $OWNERSHIP -m 0644 stop ${PREFIX}/usr/share/shorewall/configfiles/stop +run_install $OWNERSHIP -m 0644 stop ${PREFIX}/usr/share/shorewall/configfiles/stop if [ ! -f ${PREFIX}/etc/shorewall/stop ]; then run_install $OWNERSHIP -m 0600 stop ${PREFIX}/etc/shorewall/stop @@ -615,7 +564,7 @@ fi # # Install the stopped file # -[ -n "$NOCONFIGFILES" ] || run_install $OWNERSHIP -m 0644 stopped ${PREFIX}/usr/share/shorewall/configfiles/stopped +run_install $OWNERSHIP -m 0644 stopped ${PREFIX}/usr/share/shorewall/configfiles/stopped if [ ! -f ${PREFIX}/etc/shorewall/stopped ]; then run_install $OWNERSHIP -m 0600 stopped ${PREFIX}/etc/shorewall/stopped @@ -624,7 +573,7 @@ fi # # Install the ECN file # -[ -n "$NOCONFIGFILES" ] || run_install $OWNERSHIP -m 0644 ecn ${PREFIX}/usr/share/shorewall/configfiles/ecn +run_install $OWNERSHIP -m 0644 ecn ${PREFIX}/usr/share/shorewall/configfiles/ecn if [ ! -f ${PREFIX}/etc/shorewall/ecn ]; then run_install $OWNERSHIP -m 0600 ecn ${PREFIX}/etc/shorewall/ecn @@ -633,7 +582,7 @@ fi # # Install the Accounting file # -[ -n "$NOCONFIGFILES" ] || run_install $OWNERSHIP -m 0644 accounting ${PREFIX}/usr/share/shorewall/configfiles/accounting +run_install $OWNERSHIP -m 0644 accounting ${PREFIX}/usr/share/shorewall/configfiles/accounting if [ ! -f ${PREFIX}/etc/shorewall/accounting ]; then run_install $OWNERSHIP -m 0600 accounting ${PREFIX}/etc/shorewall/accounting @@ -642,7 +591,7 @@ fi # # Install the Continue file # -[ -n "$NOCONFIGFILES" ] || run_install $OWNERSHIP -m 0644 continue ${PREFIX}/usr/share/shorewall/configfiles/continue +run_install $OWNERSHIP -m 0644 continue ${PREFIX}/usr/share/shorewall/configfiles/continue if [ ! -f ${PREFIX}/etc/shorewall/continue ]; then run_install $OWNERSHIP -m 0600 continue ${PREFIX}/etc/shorewall/continue @@ -651,7 +600,7 @@ fi # # Install the Started file # -[ -n "$NOCONFIGFILES" ] || run_install $OWNERSHIP -m 0644 started ${PREFIX}/usr/share/shorewall/configfiles/started +run_install $OWNERSHIP -m 0644 started ${PREFIX}/usr/share/shorewall/configfiles/started if [ ! -f ${PREFIX}/etc/shorewall/started ]; then run_install $OWNERSHIP -m 0600 started ${PREFIX}/etc/shorewall/started @@ -666,7 +615,7 @@ echo "Standard actions file installed as ${PREFIX}/etc/shorewall/actions.std" # # Install the Actions file # -[ -n "$NOCONFIGFILES" ] || run_install $OWNERSHIP -m 0644 actions ${PREFIX}/usr/share/shorewall/configfiles/actions +run_install $OWNERSHIP -m 0644 actions ${PREFIX}/usr/share/shorewall/configfiles/actions if [ ! -f ${PREFIX}/etc/shorewall/actions ]; then run_install $OWNERSHIP -m 0644 actions ${PREFIX}/etc/shorewall/actions @@ -676,9 +625,10 @@ fi # # Install the Makefile # -[ -n "$NOCONFIGFILES" ] || run_install $OWNERSHIP -m 0644 Makefile ${PREFIX}/usr/share/shorewall/configfiles/Makefile +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 # @@ -686,50 +636,17 @@ 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 +# Install the Macro files # -for f in clib.* ; do - case $f in - *.\*) - ;; - *) - if ! list_search ${f#clib.} $XCLIBS ; then - install_file $f ${PREFIX}/usr/share/shorewall/$f 0555 - echo "Compiler library ${f#*.} installed as ${PREFIX}/usr/share/shorewall/$f" - fi - ;; - esac +for f in macro.* ; do + install_file $f ${PREFIX}/usr/share/shorewall/$f 0644 + echo "Macro ${f#*.} file installed as ${PREFIX}/usr/share/shorewall/$f" done # -# Install the Common Library files -# -for f in lib.* ; do - if ! list_search ${f#lib.} $XLIBS ; then - install_file $f ${PREFIX}/usr/share/shorewall/$f 0555 - echo "Library ${f#*.} installed as ${PREFIX}/usr/share/shorewall/$f" - fi -done - -if [ -z "$NOMACROS" ]; then - # - # Install the Macro files - # - for f in macro.* ; do - case $f in - *.\*) - ;; - *) - install_file $f ${PREFIX}/usr/share/shorewall/$f 0644 - echo "Macro ${f#*.} file installed as ${PREFIX}/usr/share/shorewall/$f" - ;; - esac - done -fi -# # Install the program skeleton files # for f in prog.* ; do diff --git a/Shorewall/prog.footer b/Shorewall/prog.footer index bf7c4895a..9d221e390 100644 --- a/Shorewall/prog.footer +++ b/Shorewall/prog.footer @@ -2,7 +2,7 @@ # Give Usage Information # usage() { - echo "Usage: $0 [ -q ] [ -v ] [ -n ] [ start|stop|clear|reset|restart|status|version ]" + echo "Usage: $0 [ -q ] [ -v ] [ -n ] [ start|stop|clear|reset|refresh|restart|status|version ]" exit $1 } ################################################################################ @@ -11,14 +11,11 @@ usage() { initialize # -# Start trace if first arg is "debug" or "trace" (the awkward code works around -# a bug in BusyBox ash on some distros). +# Start trace if first arg is "debug" or "trace" # -if [ $# -gt 1 ]; then - if [ "$1" = "debug" ] || [ "$1" = "trace" ]; then - set -x - shift - fi +if [ $# -gt 1 ] && [ "x$1" = "xdebug" -o "x$1" = "xtrace" ]; then + set -x + shift fi finished=0 @@ -114,6 +111,17 @@ case "$COMMAND" in fi progress_message3 "done." ;; + refresh) + if shorewall_is_started; then + progress_message3 "Refreshing $PRODUCT...." + refresh_firewall + status=$? + progress_message3 "done." + else + echo "$PRODUCT is not running" >&2 + status=2 + fi + ;; restore) restore_firewall status=$? diff --git a/Shorewall/prog.header b/Shorewall/prog.header index c5b5a29ee..a98469b36 100644 --- a/Shorewall/prog.header +++ b/Shorewall/prog.header @@ -15,6 +15,7 @@ # Commands are: # # start Starts the firewall +# refresh Refresh the firewall # restart Restarts the firewall # reload Reload the firewall # clear Removes all firewall rules diff --git a/Shorewall/releasenotes.txt b/Shorewall/releasenotes.txt index 87ee1b502..6674061d5 100644 --- a/Shorewall/releasenotes.txt +++ b/Shorewall/releasenotes.txt @@ -1,65 +1,846 @@ -Shorewall 3.3.0 +Shorewall 3.2.4 + + Note to users upgrading from Shorewall 2.x or 3.0 + + Most problems associated with upgrades come from two causes: + + - The user didn't read and follow the migration considerations in these + release notes. + + - The user mis-handled the /etc/shorewall/shorewall.conf file during + upgrade. Shorewall is designed to allow the default behavior of + the product to evolve over time. To make this possible, the design + assumes that you will not replace your current shorewall.conf file + during upgrades. If you feel absolutely compelled to have the latest + comments and options in your shorewall.conf then you must proceed + carefully. + + While you are at it, if you have a file named /etc/shorewall/rfc1918 then + please check that file. If it has addresses listed that are NOT in one of + these three ranges, then please rename the file to + /etc/shorewall/rfc1918.old. + + 10.0.0.0 - 10.255.255.255 + 172.16.0.0 - 172.31.255.255 + 192.168.0.0 - 192.168.255.255 + + If you have a file named /etc/shorewall/modules, please remove + it. The default modules file is now located in /usr/share/shorewall/ + (see the "Migration Considerations" below). + + Please see the "Migration Considerations" below for additional upgrade + information. + +Problems Corrected in 3.2.4 -Note to users upgrading from Shorewall 2.x or 3.0 +1) Previously, the directory name in the command "shorewall start + " was being dropped by "/sbin/shorewall". - Most problems associated with upgrades come from two causes: +Other changes in 3.2.4 - - The user didn't read and follow the migration considerations in these - release notes. +None. - - The user mis-handled the /etc/shorewall/shorewall.conf file during - upgrade. Shorewall is designed to allow the default behavior of - the product to evolve over time. To make this possible, the design - assumes that you will not replace your current shorewall.conf file - during upgrades. If you feel absolutely compelled to have the latest - comments and options in your shorewall.conf then you must proceed - carefully. +Migration Considerations: + + 1) If you are upgrading from Shorewall 2.x, it is essential that you read + the Shorewall 3.0.8 (or later) release notes: + + http://www.shorewall.net/pub/shorewall/3.0/shorewall-3.0.8/releasenotes.txt + + 2) A number of macros have been split into two. The macros affected are: + + IMAP LDAP NNTP POP3 SMTP + + Each of these macros now handles only traffic on the native (plaintext) + port. There is a corresponding macro with S added to the end of the + name for the SSL version of the same protocol. Thus each macro results + in the insertion of only one port per invocation. + + The Web macro has not been split, but two new macros, HTTP and HTTPS have + been created. The Web macro is deprecated in favour of these new macros, + and may be removed from future Shorewall releases. + + These changes have been made to ensure no unexpected ports are opened due + to the use of macros. + + 3) In previous Shorewall releases, DNAT and REDIRECT rules supported a + special syntax for exclusion of a sub-zone from the effect of the rule. + + Example: + + Z2 is a subzone of Z1: + + DNAT Z1!Z2 loc:192.168.1.4 ... + + That feature has never worked correctly when Z2 is a dynamic zone. + Furthermore, now that Shorewall supports exclusion lists, the capability + is redundant since the above rule can now be written in the form: + + DNAT Z1:! loc:192.168.1.4 ... + + Beginning with Shorewall 3.2.0, the special exclusion syntax will no + longer be supported. + + 4) Important if you use the QUEUE target. + + In the /etc/shorewall/rules file and in actions, you may now specify + 'tcp:syn' in the PROTO column. 'tcp:syn' is equivalent to 'tcp' but also + requires that the SYN flag is set and the RST, FIN and ACK flags be + off ("--syn" is added to the iptables rule). + + As part of this change, Shorewall no longer adds the "--syn" option + to TCP rules that specify QUEUE as their target. + + 5) Extension Scripts may require change + + In previous releases, extension scripts were executed during [re]start + by using the Bourne Shell "." operator. In addition to executing commands + during [re]start, these scripts had to "save" the commands to be executed + during "shorewall restore". + + This clumsiness has been eliminated in Shorewall 3.2. In Shorewall 3.2, + extension scripts are copied in-line into the compiled program and are + executed in-line during "start", "restart" and "restore". This + applies to all extension scripts except those associated with a + chain or action -- those extension scripts continue to be processed + at compile time. + + This new approach has two implications for existing scripts. + + a) It is no longer necessary to save the commands; so functions like + 'save_command', 'run_and_save_command' and 'ensure_and_save_command' + need no longer be called. For convenience, the generated program will + supply functions with these names: + + save_command() - does nothing + run_and_save_command() - runs the passed command + ensure_and_save_command() - runs the passed command and + stops/restores the firewall if the + command fails. + + These functions should provide for transparent migration of + scripts that use them until you can get around to eliminating + their use completely. + + b) When the extension script is copied into the compiled program, it + is indented to line up with the surrounding code. If you have 'awk' + installed on your system, the Shorewall compiler will correctly handle + line continuation (last character on the line = "\"). If you do not + have awk, it will not be possible to use line-continuation in your + extension scripts. + + In no case is it possible to continue a quoted string over multiple lines + without having additional whitespace inserted into the string. + + 6) Beginning with this release, the way in which packet marking in the + PREROUTING chain interracts with the 'track' option in /etc/shorewall/providers + has changed in two ways: + + a) Packets arriving on a tracked interface are now passed to the PREROUTING + marking chain so that they may be marked with a mark other than the + 'track' mark (the connection still retains the 'track' mark). + + b) When HIGH_ROUTE_MARKS=Yes, you can still clear the mark on packets + in the PREROUTING chain (i.e., you can specify a mark value of zero). + + 7) Kernel version 2.6.16 introduces 'xtables', a new common packet + filtering and connection tracking facility that supports both IPv4 + and IPv6. Because a different set of kernel modules must be loaded + for xtables, Shorewall now includes two 'modules' files: + + a) /usr/share/shorewall/modules -- the former + /etc/shorewall/modules + + b) /usr/share/shorewall/xmodules -- a new file that support + xtables. + + If you wish to use the new file, then simply execute this command: + + cp -f /usr/share/shorewall/xmodules /etc/shorewall/modules + + 8) Previously, CLASSIFY tcrules were always processed out of the + POSTROUTING chain. Beginning with this release, they are processed + out of the POSTROUTING chain *except* when the SOURCE is + $FW[:
] in which case the rule is processed out of the + OUTPUT chain. + + With correctly-coded rulesets, this change should have no + effect. Users having incorrectly-coded tcrules may need to change + them. + + Example: + + #MARK/ SOURCE DEST PROTO DEST SOURCE + #CLASSIFY PORTS(S) PORT(S) + 1:110 $FW eth3 tcp - 22 + + While the user may have expected this rule to only affect traffic + from the firewall itself, the rule was really equivalent to this one: + + #MARK/ SOURCE DEST PROTO DEST SOURCE + #CLASSIFY PORTS(S) PORT(S) + 1:110 0.0.0.0/0 eth3 tcp - 22 + + So after this change, the second rule will be required rather than + the first if that is what was really wanted. + + New Features: + + 1) Shorewall has always been very noisy (lots of messages). No longer. + + You set the default level of verbosity using the VERBOSITY option in + shorewall.conf. If you don't set it (as would be the case if you use your + old shorewall.conf file) then VERBOSITY defaults to a value of 2 which + results in behavior compatible with previous Shorewall versions. + A value of 1 suppresses some of the output (like the old -q option did) + while a value of 0 makes Shorewall almost silent. A value of -1 + suppresses all output except warning and error messages. + + The value specified in the 3.2 shorewall.conf is 1. So you can make + Shorewall as verbose as previously using a single -v and you can make it + almost silent by using a single -q. + + If VERBOSITY is set at 2, you can still make a command nearly + silent by using two "q"s (e.g., shorewall -qq restart). + + In summary, each "q" subtracts one from VERBOSITY while each "v" adds one + to VERBOSITY. + + The "shorewall show log", "shorewall logwatch" and "shorewall dump" + commands require VERBOSITY to be greater than or equal to 3 to + display MAC addresses.This is consistent with the previous + implementation which required a single -v to enable MAC display but + means that if you set VERBOSITY=0 in shorewall.conf, then you will + need to include -vvv in commands that display log records in order + to have MACs displayed. + + To make the display of MAC addresses less cumbersome, a '-m' option has + been added to the "show" and logwatch commands: + + shorewall show -m log + shorewall logwatch -m + + 2) A new 'shorewall compile' command has been added. + + shorewall compile [ -e ] [ ]