From c75b004e85c093e5832a6821b9082f66ed534518 Mon Sep 17 00:00:00 2001 From: teastep Date: Tue, 18 Mar 2003 01:14:45 +0000 Subject: [PATCH] Add additional files for 1.4.0 git-svn-id: https://shorewall.svn.sourceforge.net/svnroot/shorewall/trunk@505 fbd18981-670d-0410-9b5c-8dc0c1a9a2bb --- Lrp/etc/shorewall/ecn | 18 + Lrp/usr/share/shorewall/firewall | 4586 +++++++++++++++++++++++++++++ Lrp/usr/share/shorewall/functions | 199 ++ Lrp/usr/share/shorewall/version | 1 + 4 files changed, 4804 insertions(+) create mode 100644 Lrp/etc/shorewall/ecn create mode 100755 Lrp/usr/share/shorewall/firewall create mode 100644 Lrp/usr/share/shorewall/functions create mode 100644 Lrp/usr/share/shorewall/version diff --git a/Lrp/etc/shorewall/ecn b/Lrp/etc/shorewall/ecn new file mode 100644 index 000000000..1e96339b4 --- /dev/null +++ b/Lrp/etc/shorewall/ecn @@ -0,0 +1,18 @@ +# +# Shorewall 1.4 - /etc/shorewall/ecn +# +# Use this file to list the destinations for which you want to +# disable ECN. +# +# This feature requires kernel 2.4.20 or later. If you run 2.4.20, +# you also need the patch found at http://www.shorewall.net/ecn/patch. +# That patch is included in kernels 2.4.21 and later. +# +# INTERFACE - Interface through which host(s) communicate with +# the firewall +# HOST(S) - (Optional) Comma-separated list of IP/subnet +# If left empty or supplied as "-", +# 0.0.0.0/0 is assumed. +############################################################################## +#INTERFACE HOST(S) +#LAST LINE -- ADD YOUR ENTRIES BEFORE THIS ONE -- DO NOT REMOVE diff --git a/Lrp/usr/share/shorewall/firewall b/Lrp/usr/share/shorewall/firewall new file mode 100755 index 000000000..03630b5fe --- /dev/null +++ b/Lrp/usr/share/shorewall/firewall @@ -0,0 +1,4586 @@ +#!/bin/sh +# +# The Shoreline Firewall (Shorewall) Packet Filtering Firewall - V1.4 3/14/2003 +# +# This program is under GPL [http://www.gnu.org/copyleft/gpl.htm] +# +# (c) 1999,2000,2001,2002,2003 - Tom Eastep (teastep@shorewall.net) +# +# On most distributions, this file should be called: +# /etc/rc.d/init.d/shorewall or /etc/init.d/shorewall +# +# Complete documentation is available at http://shorewall.net +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of Version 2 of the GNU General Public License +# as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA +# +# If an error occurs while starting or restarting the firewall, the +# firewall is automatically stopped. +# +# Commands are: +# +# shorewall start Starts the firewall +# shorewall restart Restarts the firewall +# shorewall stop Stops the firewall +# shorewall status Displays firewall status +# shorewall reset Resets iptabless packet and +# byte counts +# shorewall clear Remove all Shorewall chains +# and rules/policies. +# shorewall refresh . Rebuild the common chain +# shorewall check Verify the more heavily-used +# configuration files. +# +# 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 +} + +# +# Function to count list elements +# +list_count() { + arg_count() { + echo $# + } + + arg_count `separate_list $1` +} + +# +# Mutual exclusion -- These functions are jackets for the mutual exclusion +# routines in $FUNCTIONS. They invoke +# the corresponding function in that file if the user did +# not specify "nolock" on the runline. +# +my_mutex_on() { + [ -n "$nolock" ] || { mutex_on; have_mutex=Yes; } +} + +my_mutex_off() { + [ -n "$have_mutex" ] && { mutex_off; have_mutex=; } +} + +# +# Message to stderr +# +error_message() # $* = Error Message +{ + echo " $@" >&2 +} + +# +# Fatal error -- stops the firewall after issuing the error message +# +fatal_error() # $* = Error Message +{ + echo " Error: $@" >&2 + [ $command = check ] || stop_firewall + exit 2 +} + +# +# Fatal error during startup -- generate an error message and abend with +# altering the state of the firewall +# +startup_error() # $* = Error Message +{ + echo " Error: $@" >&2 + my_mutex_off + [ -n "$TMP_DIR" ] && rm -rf $TMP_DIR + kill $$ + exit 2 +} + +# +# Send a message to STDOUT and the System Log +# +report () { # $* = message + echo "$@" + logger "$@" +} + +# +# Perform variable substitution on the passed argument and echo the result +# +expand() # $1 = contents of variable which may be the name of another variable +{ + eval echo \"$1\" +} + +# +# 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 +} + +# +# Replace all leading "!" with "! " in the passed argument list +# + +fix_bang() { + local i; + + for i in $@; do + case $i in + !*) + echo "! ${i#!}" + ;; + *) + echo $i + ;; + esac + done +} + +# +# Run iptables and if an error occurs, stop the firewall and quit +# +run_iptables() { + + if ! iptables $@ ; then + [ -z "$stopping" ] && { stop_firewall; exit 2; } + fi +} + +# +# Version of 'run_iptables' that inserts white space after "!" in the arg list +# +run_iptables2() { + + if [ "x${*%!*}" = "x$*" ]; then + # + # No "!" in the command -- just execute it + # + run_iptables $@ + return + fi + # + # Need to insert white space before each "!" + # + run_iptables `fix_bang $@` +} + +# +# Run ip and if an error occurs, stop the firewall and quit +# +run_ip() { + if ! ip $@ ; then + [ -z "$stopping" ] && { stop_firewall; exit 2; } + fi +} + +# +# Run arp and if an error occurs, stop the firewall and quit +# +run_arp() { + if ! arp $@ ; then + [ -z "$stopping" ] && { stop_firewall; exit 2; } + fi +} + +# +# Run tc and if an error occurs, stop the firewall and quit +# +run_tc() { + if ! tc $@ ; then + [ -z "$stopping" ] && { stop_firewall; exit 2; } + fi +} + +# +# Create a filter chain +# +# If the chain isn't one of the common chains then add a rule to the chain +# allowing packets that are part of an established connection. Create a +# variable ${1}_exists and set its value to Yes to indicate that the chain now +# exists. +# +createchain() # $1 = chain name, $2 = If "yes", create default rules +{ + run_iptables -N $1 + + if [ $2 = yes ]; then + state="ESTABLISHED,RELATED" + run_iptables -A $1 -m state --state $state -j ACCEPT + [ -z "$NEWNOTSYN" ] && \ + run_iptables -A $1 -m state --state NEW -p tcp ! --syn -j newnotsyn + fi + + eval ${1}_exists=Yes +} + +# +# Determine if a chain exists +# +# When we create a chain "chain", we create a variable named chain_exists and +# set its value to Yes. This function tests for the "_exists" variable +# corresponding to the passed chain having the value of "Yes". +# +havechain() # $1 = name of chain +{ + eval test \"\$${1}_exists\" = Yes +} + +# +# Query NetFilter about the existence of a filter chain +# +chain_exists() # $1 = chain name +{ + qt iptables -L $1 -n +} + +# +# Query NetFilter about the existence of a mangle chain +# +mangle_chain_exists() # $1 = chain name +{ + qt iptables -t mangle -L $1 -n +} + +# +# Ensure that a chain exists (create it if it doesn't) +# +ensurechain() # $1 = chain name +{ + havechain $1 || createchain $1 yes +} + +# +# Add a rule to a chain creating the chain if necessary +# +addrule() # $1 = chain name, remainder of arguments specify the rule +{ + ensurechain $1 + run_iptables -A $@ +} + +# +# Create a nat chain +# +# Create a variable ${1}_nat_exists and set its value to Yes to indicate that +# the chain now exists. +# +createnatchain() # $1 = chain name +{ + run_iptables -t nat -N $1 + + eval ${1}_nat_exists=Yes +} + +# +# Determine if a nat chain exists +# +# When we create a chain "chain", we create a variable named chain_nat_exists +# and set its value to Yes. This function tests for the "_exists" variable +# corresponding to the passed chain having the value of "Yes". +# +havenatchain() # $1 = name of chain +{ + eval test \"\$${1}_nat_exists\" = Yes +} + +# +# Ensure that a nat chain exists (create it if it doesn't) +# +ensurenatchain() # $1 = chain name +{ + havenatchain $1 || createnatchain $1 +} + +# +# Add a rule to a nat chain creating the chain if necessary +# +addnatrule() # $1 = chain name, remainder of arguments specify the rule +{ + ensurenatchain $1 + run_iptables2 -t nat -A $@ +} + +# +# Delete a chain if it exists +# +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 connections +# +setcontinue() # $1 = name of chain +{ + run_iptables -A $1 -m state --state ESTABLISHED -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 +} + +# +# Chain name base for an interface +# +chain_base() #$1 = interface +{ + local c=${1%%+*} + + case $c in + *.*) + echo ${c%.*}_${c#*.} + ;; + *) + echo ${c:=common} + ;; + esac +} + +# +# 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 +} + +# +# DNAT Chain from a zone +# +dnat_chain() # $1 = zone +{ + echo ${1}_dnat +} + +# +# SNAT Chain to a zone +# +snat_chain() # $1 = zone +{ + echo `chain_base $1`_snat +} + +# +# ECN Chain to an interface +# +ecn_chain() # $1 = interface +{ + echo ${1}_ecn +} + +# +# First chains for an interface +# +first_chains() #$1 = interface +{ + local c=`chain_base $1` + + echo ${c}_fwd ${c}_in +} + +# +# 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 + + while read z hosts options; do + [ "x`expand $z`" = "x$1" ] && expandv hosts && echo `separate_list $hosts` + 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 the defined hosts in each zone and generate report +# +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 [ -z "$hosts" ]; then + hosts=$interface:0.0.0.0/0 + else + hosts="$hosts $interface:0.0.0.0/0" + fi + done + + interfaces= + + for host in $hosts; do + interface=${host%:*} + if ! list_search $interface $interfaces; then + 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 + eval display=\$${zone}_display + display_list "$display 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 +} + +# +# Validate the zone names and options in the interfaces file +# +validate_interfaces_file() { + while read z interface subnet options; do + expandv z interface subnet options + r="$z $interface $subnet $options" + + [ "x$z" = "x-" ] && z= + + if [ -n "$z" ]; then + validate_zone $z || startup_error "Invalid zone ($z) in record \"$r\"" + fi + + if [ -n "`ip link show $interface 2> /dev/null | grep LOOPBACK`" ]; then + startup_error "The loopback interface ($interface) may not be defined in /etc/shorewall/interfaces" + fi + + list_search $interface $all_interfaces && \ + startup_error "Duplicate Interface $interface" + + case $interface in + *:*) + startup_error "Invalid Interface Name: $interface" + ;; + esac + + all_interfaces="$all_interfaces $interface" + options=`separate_list $options` + interface=`chain_base $interface` + + eval ${interface}_broadcast="$subnet" + eval ${interface}_zone="$z" + eval ${interface}_options=\"$options\" + + for option in $options; do + case $option in + dhcp|norfc1918|tcpflags) + ;; + routefilter|dropunclean|logunclean|blacklist|proxyarp|maclist|-) + ;; + *) + error_message "Warning: Invalid option ($option) in record \"$r\"" + ;; + esac + done + + [ -z "$all_interfaces" ] && startup_error "No Interfaces Defined" + + done < $TMP_DIR/interfaces +} + +# +# Validate the zone names and options in the hosts file +# +validate_hosts_file() { + while read z hosts options; do + expandv z hosts options + r="$z $hosts $options" + validate_zone $z || startup_error "Invalid zone ($z) in record \"$r\"" + + for host in `separate_list $hosts`; do + interface=${host%:*} + + list_search $interface $all_interfaces || \ + startup_error "Unknown interface ($interface) in record \"$r\"" + + for option in `separate_list $options`; do + case $option in + maclist|-) + ;; + *) + error_message "Warning: Invalid option ($option) in record \"$r\"" + ;; + esac + done + done + done < $TMP_DIR/hosts +} + +# +# 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'`" +} + +# +# validate the policy file +# +validate_policy() +{ + local clientwild + local serverwild + local zone + local zone1 + local pc + local chain + local policy + local loglevel + local synparams + + print_policy() # $1 = source zone, $2 = destination zone + { + [ $command != check ] || \ + [ $1 = all ] || \ + [ $2 = all ] || \ + echo " Policy for $1 to $2 is $policy" + } + + all_policy_chains= + + strip_file policy + + while read client server policy loglevel synparams; do + expandv client server policy loglevel synparams + + clientwild= + serverwild= + + case "$client" in + all|ALL) + clientwild=Yes + ;; + *) + if ! validate_zone $client; then + startup_error "Undefined zone $client" + fi + esac + + case "$server" in + all|ALL) + serverwild=Yes + ;; + *) + if ! validate_zone $server; then + startup_error "Undefined zone $server" + fi + esac + + case $policy in + ACCEPT|REJECT|DROP|CONTINUE) + ;; + *) + startup_error "Invalid policy $policy" + ;; + esac + + chain=${client}2${server} + + [ "x$chain" = "x${FW}2${FW}" ] && \ + startup_error "fw->fw policy not allowed: $policy" + + if is_policy_chain $chain ; then + startup_error "Duplicate policy $policy" + fi + + [ "x$loglevel" = "x-" ] && loglevel= + + chain=${client}2${server} + + all_policy_chains="$all_policy_chains $chain" + + eval ${chain}_is_policy=Yes + eval ${chain}_policy=$policy + eval ${chain}_loglevel=$loglevel + eval ${chain}_synparams=$synparams + + if [ -n "${clientwild}" ]; then + if [ -n "${serverwild}" ]; then + for zone in $zones $FW all; do + for zone1 in $zones $FW all; do + eval pc=\$${zone}2${zone1}_policychain + + if [ -z "$pc" ]; then + eval ${zone}2${zone1}_policychain=$chain + print_policy $zone $zone1 + fi + done + done + else + for zone in $zones $FW all; do + eval pc=\$${zone}2${server}_policychain + + if [ -z "$pc" ]; then + eval ${zone}2${server}_policychain=$chain + print_policy $zone $server + fi + done + fi + elif [ -n "$serverwild" ]; then + for zone in $zones $FW all; do + eval pc=\$${client}2${zone}_policychain + + if [ -z "$pc" ]; then + eval ${client}2${zone}_policychain=$chain + print_policy $client $zone + fi + done + else + eval ${chain}_policychain=${chain} + print_policy $client $server + fi + + done < $TMP_DIR/policy +} + +# +# Find broadcast addresses +# +find_broadcasts() { + for interface in $all_interfaces; do + eval bcast=\$`chain_base $interface`_broadcast + if [ "x$bcast" = "xdetect" ]; then + addr="`ip addr show $interface 2> /dev/null`" + if [ -n "`echo "$addr" | grep 'inet.*brd '`" ]; then + addr="`echo "$addr" | \ + grep "inet " | sed 's/^.* inet.*brd //;s/scope.*//'`" + echo $addr | cut -d' ' -f 1 + fi + elif [ "x${bcast}" != "x-" ]; then + echo `separate_list $bcast` + fi + done +} + +# +# Find interface address--returns the first IP address assigned to the passed +# device +# +find_interface_address() # $1 = interface +{ + # + # get the line of output containing the first IP address + # + addr=`ip addr show $1 2> /dev/null | grep inet | 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/inet //;s/\/.*//;s/ peer.*//' +} + +# +# 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 +} + +# +# Find hosts with the passed option +# +find_hosts_by_option() # $1 = option +{ + while read ignore hosts options; do + expandv options + list_search $1 `separate_list $options` && \ + echo `expand $hosts` + done < $TMP_DIR/hosts + + for interface in $all_interfaces; do + eval options=\$`chain_base ${interface}`_options + list_search $1 $options && \ + echo ${interface}:0.0.0.0/0 + done +} + +# +# Determine if there are interfaces of the given zone and option +# +# Returns zero if any such interfaces are found and returns one otherwise. +# +have_interfaces_in_zone_with_option() # $1 = zone, $2 = option +{ + local zne=$1 + local z + local interface + + for interface in $all_interfaces; do + eval z=\$`chain_base ${interface}`_zone + + [ "x$z" = "x$zne" ] && \ + list_search $1 $options && \ + return 0 + done + + return 1 +} + +# +# Flush and delete all user-defined chains in the filter table +# +deleteallchains() { + run_iptables -F + run_iptables -X +} + +# +# 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 + echo "Processing $user_exit ..." + . $user_exit + fi +} + +# +# Stop the Firewall +# +stop_firewall() { + # + # Turn off trace unless we were tracing "stop" or "clear" + # + case $command in + stop|clear) + ;; + check) + kill $$ + exit 2 + ;; + *) + set +x + ;; + esac + + stopping="Yes" + + terminator= + + deletechain shorewall + + run_user_exit stop + + [ -n "$MANGLE_ENABLED" ] && \ + run_iptables -t mangle -F && \ + run_iptables -t mangle -X + + [ -n "$NAT_ENABLED" ] && delete_nat + delete_proxy_arp + [ -n "$CLEAR_TC" ] && delete_tc + + setpolicy INPUT DROP + setpolicy OUTPUT DROP + setpolicy FORWARD DROP + + deleteallchains + + hosts= + + strip_file routestopped + + while read interface host; do + expandv interface host + [ "x$host" = "x-" ] && host=0.0.0.0/0 + for h in `separate_list $host`; do + hosts="$hosts $interface:$h" + done + done < $TMP_DIR/routestopped + + for host in $hosts; do + interface=${host%:*} + subnet=${host#*:} + iptables -A INPUT -i $interface -s $subnet -j ACCEPT + iptables -A OUTPUT -o $interface -d $subnet -j ACCEPT + + for host1 in $hosts; do + [ "$host" != "$host1" ] && \ + iptables -A FORWARD -i $interface -s $subnet \ + -o ${host1%:*} -d ${host1#*:} -j ACCEPT + done + done + + iptables -A INPUT -i lo -j ACCEPT + iptables -A OUTPUT -o lo -j ACCEPT + + for interface in `find_interfaces_by_option dhcp`; do + iptables -A INPUT -p udp -i $interface --dport 67:68 -j ACCEPT + iptables -A OUTPUT -p udp -o $interface --dport 67:68 -j ACCEPT + done + + case "$IP_FORWARDING" in + [Oo][Nn]) + echo 1 > /proc/sys/net/ipv4/ip_forward + ;; + [Oo][Ff][Ff]) + echo 0 > /proc/sys/net/ipv4/ip_forward + ;; + esac + + run_user_exit stopped + + logger "Shorewall Stopped" + + rm -rf $TMP_DIR + + case $command in + stop|clear) + ;; + *) + # + # The firewall is being stopped when we were trying to do something + # else. Remove the lock file and Kill the shell in case we're in a + # subshell + # + my_mutex_off + kill $$ + ;; + esac +} + +# +# Remove all rules and remove all user-defined chains +# +clear_firewall() { + stop_firewall + + run_iptables -F + + echo 1 > /proc/sys/net/ipv4/ip_forward + + setpolicy INPUT ACCEPT + setpolicy FORWARD ACCEPT + setpolicy OUTPUT ACCEPT + + run_user_exit clear + + logger "Shorewall Cleared" +} + +# +# Set up ipsec tunnels +# +setup_tunnels() # $1 = name of tunnels file +{ + local inchain + local outchain + + setup_one_ipsec() # $1 = gateway $2 = Tunnel Kind $3 = gateway zones + { + options="-m state --state NEW -j ACCEPT" + addrule $inchain -p 50 -s $1 -j ACCEPT + addrule $outchain -p 50 -d $1 -j ACCEPT + run_iptables -A $inchain -p 51 -s $1 -j ACCEPT + run_iptables -A $outchain -p 51 -d $1 -j ACCEPT + + run_iptables -A $outchain -p udp -d $1 --dport 500 --sport 500 $options + + if [ $2 = ipsec ]; then + run_iptables -A $inchain -p udp -s $1 --sport 500 --dport 500 $options + else + run_iptables -A $inchain -p udp -s $1 --dport 500 $options + run_iptables -A $inchain -p udp -s $1 --dport 4500 $options + fi + + for z in `separate_list $3`; do + if validate_zone $z; then + addrule ${FW}2${z} -p udp --sport 500 --dport 500 $options + if [ $2 = ipsec ]; then + addrule ${z}2${FW} -p udp --sport 500 --dport 500 $options + else + addrule ${z}2${FW} -p udp --dport 500 $options + addrule ${z}2${FW} -p udp --dport 4500 $options + fi + else + error_message "Warning: Invalid gateway zone ($z)" \ + " -- Tunnel \"$tunnel\" may encounter keying problems" + fi + done + + echo " IPSEC tunnel to $gateway defined." + } + + setup_one_other() # $1 = TYPE, $2 = gateway, $3 = protocol + { + addrule $inchain -p $3 -s $2 -j ACCEPT + addrule $outchain -p $3 -d $2 -j ACCEPT + + echo " $1 tunnel to $2 defined." + } + + setup_pptp_client() # $1 = gateway + { + addrule $outchain -p 47 -d $1 -j ACCEPT + addrule $inchain -p 47 -j ACCEPT + addrule $outchain -p tcp --dport 1723 -d $1 -j ACCEPT + + echo " PPTP tunnel to $1 defined." + } + + setup_pptp_server() + { + addrule $inchain -p 47 -j ACCEPT + addrule $outchain -p 47 -j ACCEPT + addrule $inchain -p tcp --dport 1723 -j ACCEPT + + echo " PPTP server defined." + } + + setup_one_openvpn() # $1 = gateway, $2 = kind[:port] + { + case $2 in + *:*) + p=${2#*:} + ;; + *) + p=5000 + ;; + esac + + addrule $inchain -p udp -s $1 --sport $p --dport $p -j ACCEPT + addrule $outchain -p udp -d $1 --sport $p --dport $p -j ACCEPT + + echo " OPENVPN 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} + case $kind in + ipsec|IPSEC) + setup_one_ipsec $gateway ipsec $z1 + ;; + ipsecnat|IPSECNAT) + setup_one_ipsec $gateway ipsecnat $z1 + ;; + ipip|IPIP) + setup_one_other IPIP $gateway 4 + ;; + gre|GRE) + setup_one_other GRE $gateway 47 + ;; + pptpclient|PPTPCLIENT) + setup_pptp_client $gateway + ;; + pptpserver|PPTPSERVER) + setup_pptp_server + ;; + openvpn|OPENVPN|openvpn:*|OPENVPN:*) + setup_one_openvpn $gateway $kind + ;; + *) + error_message "Tunnels of type $kind are not supported:" \ + "Tunnel \"$tunnel\" Ignored" + ;; + esac + else + error_message "Invalid gateway zone ($z)" \ + " -- Tunnel \"$tunnel\" Ignored" + fi + done < $TMP_DIR/tunnels +} + +# +# Setup Proxy ARP +# +setup_proxy_arp() { + + print_error() { + error_message "Invalid value for HAVEROUTE - ($haveroute)" + error_message "Entry \"$address $interface $external $haveroute\" ignored" + } + + setup_one_proxy_arp() { + case $haveroute in + [Nn][Oo]) + haveroute= + ;; + [Yy][Ee][Ss]) + ;; + *) + if [ -n "$haveroute" ]; then + print_error + return + fi + ;; + esac + + [ -z "$haveroute" ] && run_ip route add $address dev $interface + + run_arp -Ds $address $external pub + + echo 1 > /proc/sys/net/ipv4/conf/$interface/proxy_arp + echo 0 > /proc/sys/net/ipv4/conf/$external/proxy_arp + + echo $address $interface $external $haveroute >> ${STATEDIR}/proxyarp + + echo " Host $address connected to $interface added to ARP on $external" + } + + > ${STATEDIR}/proxyarp + + while read address interface external haveroute; do + expandv address interface external haveroute + setup_one_proxy_arp + done < $TMP_DIR/proxyarp + + interfaces=`find_interfaces_by_option proxyarp` + + for interface in $interfaces; do + if echo 1 > /proc/sys/net/ipv4/conf/$interface/proxy_arp 2> /dev/null; then + echo " Enabled proxy ARP on $interface" + else + error_message "Warning: Unable to enable proxy ARP on $interface" + fi + done +} + +# +# Set up MAC Verification +# +setup_mac_lists() { + local interface + local mac + local addresses + local address + local chain + local logpart + local macpart + local blob + local hosts + # + # Generate the list of interfaces having MAC verification + # + maclist_interfaces= + + for hosts in $maclist_hosts; do + 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 + + echo "Setting up MAC Verification on $maclist_interfaces..." + # + # Be sure that they are all ethernet interfaces + # + for interface in $maclist_interfaces; do + case $interface in + eth*|wlan*) + ;; + *) + fatal_error "MAC verification is only supported on ethernet and 802.11b devices: $interface" + ;; + esac + + createchain `mac_chain $interface` no + done + # + # Process the maclist file producing the verification rules + # + + while read interface mac addresses; do + expandv interface mac addresses + + chain=`mac_chain $interface` + + if ! havechain $chain ; then + fatal_error "No hosts on $interface have the maclist option specified" + fi + + macpart=`mac_match $mac` + + if [ -z "$addresses" ]; then + run_iptables -A $chain $macpart -j RETURN + else + for address in `separate_list $addresses` ; do + run_iptables2 -A $chain $macpart -s $address -j RETURN + done + fi + done < $TMP_DIR/maclist + # + # Setup Logging variables + # + if [ -n "$MACLIST_LOG_LEVEL" ]; then + if [ "$MACLIST_LOG_LEVEL" = ULOG ]; then + logpart="-j ULOG $LOGPARMS --ulog-prefix" + else + logpart="-j LOG $LOGPARMS --log-level $MACLIST_LOG_LEVEL --log-prefix" + fi + else + logpart= + fi + # + # Must take care of our own broadcasts and multicasts then terminate the verification + # chains + # + for interface in $maclist_interfaces; do + chain=`mac_chain $interface` + blob=`ip addr show $interface 2> /dev/null | grep inet | sed 's/inet //; s/brd //; s/scope.*//;'` + + [ -z "$blob" ] && \ + fatal_error "Interface $interface must be up before Shorewall can start" + + set -- $blob + + while [ $# -gt 0 ]; do + address=${1%/*} + + case $1 in + */32) + ;; + *) + run_iptables -A $chain -s $address -d $2 -j RETURN + shift + ;; + esac + + run_iptables -A $chain -s $address -d 255.255.255.255 -j RETURN + run_iptables -A $chain -s $address -d 224.0.0.0/4 -j RETURN + shift + done + + [ -n "$logpart" ] && \ + run_iptables -A $chain $logpart "Shorewall:$chain:$MACLIST_DISPOSITION:" + + run_iptables -A $chain -j $maclist_target + done + # + # Generate jumps from the input and forward chains + # + for hosts in $maclist_hosts; do + interface=${hosts%:*} + hosts=${hosts#*:} + for chain in `first_chains $interface` ; do + run_iptables -A $chain -s $hosts -m state --state NEW \ + -j `mac_chain $interface` + done + done +} + +# +# Set up SYN flood protection +# +setup_syn_flood_chain () + # $1 = policy chain + # $2 = synparams +{ + local chain=$1 + local limit=${2%:*} + local limit_burst=${2#*:} + + run_iptables -N @$chain + run_iptables -A @$chain \ + -m limit --limit $limit --limit-burst $limit_burst \ + -j RETURN + run_iptables -A @$chain -j DROP +} + +# +# Enable SYN flood protection on a chain +# +# Insert a jump rule to the protection chain from the first chain. Inserted +# as the second rule and restrict the jump to SYN packets +# +enable_syn_flood_protection() # $1 = chain, $2 = protection chain +{ + run_iptables -I $1 2 -p tcp --syn -j @$2 + echo " Enabled SYN flood protection" +} + +# +# Delete existing Proxy ARP +# +delete_proxy_arp() { + if [ -f ${STATEDIR}/proxyarp ]; then + while read address interface external haveroute; do + qt arp -i $external -d $address pub + [ -z "$haveroute" ] && qt ip route del $address dev $interface + done < ${STATEDIR}/proxyarp + + rm -f ${STATEDIR}/proxyarp + fi + + [ -d ${STATEDIR} ] && touch ${STATEDIR}/proxyarp + + for f in `ls /proc/sys/net/ipv4/conf/*/proxy_arp`; do + echo 0 > $f + done +} + +# +# Setup Static Network Address Translation (NAT) +# +setup_nat() { + local allints + # + # At this point, we're just interested in the network translation + # + > ${STATEDIR}/nat + + echo "Setting up NAT..." + + while read external interface internal allints localnat; do + expandv external interface internal allints localnat + + iface=${interface%:*} + + if [ -n "$ADD_IP_ALIASES" ]; then + qt ip addr del $external dev $iface + fi + + if [ -z "$allints" -o "$allints" = "Yes" -o "$allints" = "yes" ] + then + addnatrule nat_in -d $external -j DNAT --to-destination $internal + addnatrule nat_out -s $internal -j SNAT --to-source $external + + if [ "$localnat" = "Yes" -o "$localnat" = "yes" ]; then + run_iptables2 -t nat -A OUTPUT -d $external \ + -j DNAT --to-destination $internal + fi + else + addnatrule `input_chain $iface` \ + -d $external -j DNAT --to-destination $internal + addnatrule `output_chain $iface` \ + -s $internal -j SNAT --to-source $external + fi + + if [ -n "$ADD_IP_ALIASES" ]; then + list_search $external $aliases_to_add || \ + aliases_to_add="$aliases_to_add $external $interface" + fi + + echo " 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 + + if [ -f ${STATEDIR}/nat ]; then + while read external interface; do + qt ip addr del $external dev $interface + done < ${STATEDIR}/nat + + rm -f {$STATEDIR}/nat + fi + + [ -d ${STATEDIR} ] && touch ${STATEDIR}/nat +} + +# +# Setup ECN disabling rules +# +setup_ecn() # $1 = file name +{ + local interfaces + local hosts + local h + + strip_file ecn $1 + + echo "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 + echo "Setting up ECN control on${interfaces}..." + + for interface in $interfaces; do + chain=`ecn_chain $interface` + if mangle_chain_exists $chain; then + flushmangle $chain + else + run_iptables -t mangle -N $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 -d $h -j ECN --ecn-tcp-remove + echo " ECN Disabled to $h through $interface" + done + fi +} + +# +# Process a TC Rule - $marking_chain is assumed to contain the name of the +# default marking chain +# +process_tc_rule() +{ + add_a_tc_rule() { + r= + chain=$marking_chain + + if [ "x$source" != "x-" ]; then + case $source in + *.*.*) + r="-s $source " + ;; + ~*) + r=`mac_match $source` + ;; + $FW) + chain=tcout + ;; + *) + if ! list_search $source $all_interfaces; then + fatal_error "Unknown interface $source in rule \"$rule\"" + fi + + r="-i $source " + ;; + esac + fi + + if [ "$mark" != "${mark%:*}" ]; then + + [ "$chain" = tcout ] && \ + fatal_error "Chain designator not allowed when source is \$FW; rule \"$rule\"" + + case "${mark#*:}" in + p|P) + chain=tcpre + ;; + f|F) + chain=tcfor + ;; + *) + fatal_error "Invalid chain designator: (${mark#*:}) in rule \"$rule\"" + ;; + esac + + mark="${mark%:*}" + fi + + [ "x$dest" = "x-" ] || r="${r}-d $dest " + [ "$proto" = "all" ] || r="${r}-p $proto " + [ "x$port" = "x-" ] || r="${r}--dport $port " + [ "x$sport" = "x-" ] || r="${r}--sport $sport " + + run_iptables2 -t mangle -A $chain $r -j MARK --set-mark $mark + + } + + 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 + + echo " TC Rule \"$rule\" added" +} + +# +# Setup queuing and classes +# +setup_tc1() { + # + # Create the TC mangle chains + # + + run_iptables -t mangle -N tcpre + run_iptables -t mangle -N tcfor + run_iptables -t mangle -N tcout + # + # Process the TC Rules File + # + strip_file tcrules + + while read mark sources dests proto ports sports; do + expandv mark sources dests proto ports sports + rule=`echo "$mark $sources $dests $proto $ports $sports"` + process_tc_rule + done < $TMP_DIR/tcrules + # + # Link to the TC mangle chains from the main chains + # + + run_iptables -t mangle -A FORWARD -j tcfor + run_iptables -t mangle -A PREROUTING -j tcpre + run_iptables -t mangle -A OUTPUT -j tcout + + run_user_exit tcstart + +} + +setup_tc() { + + echo "Setting up Traffic Control Rules..." + + setup_tc1 +} + +# +# Clear Traffic Shaping +# +delete_tc() +{ + + 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 +} + +# +# Check the configuration +# +check_config() { + + disclaimer() { + echo + echo "WARNING: THE 'check' COMMAND IS TOTALLY UNSUPPORTED AND PROBLEM" + echo " REPORTS COMPLAINING ABOUT ERRORS THAT IT DIDN'T CATCH" + echo " WILL NOT BE ACCEPTED" + echo + } + + disclaimer + + echo "Verifying Configuration..." + + verify_os_version + + load_kernel_modules + + echo "Determining Zones..." + + determine_zones + + [ -z "$zones" ] && startup_error "ERROR: No Zones Defined" + + display_list "Zones:" $zones + + echo "Validating interfaces file..." + + validate_interfaces_file + + echo "Validating hosts file..." + + validate_hosts_file + + echo "Determining Hosts in Zones..." + + determine_interfaces + determine_hosts + + echo "Validating rules file..." + + rules=`find_file rules` + strip_file rules $rules + process_rules + + echo "Validating policy file..." + + validate_policy + + rm -rf $TMP_DIR + + echo "Configuration Validated" + + disclaimer + +} + +# +# Refresh queuing and classes +# +refresh_tc() { + + echo "Refreshing Traffic Control Rules..." + + [ -n "$CLEAR_TC" ] && delete_tc + + [ -n "$MARK_IN_FORWARD_CHAIN" ] && chain=tcfor || chain=tcpre + + if mangle_chain_exists $chain; then + # + # Flush the TC mangle chains + # + run_iptables -t mangle -F $chain + + run_iptables -t mangle -F tcout + # + # Process the TC Rules File + # + strip_file tcrules + + while read mark sources dests proto ports sports; do + expandv mark sources dests proto ports sports + rule=`echo "$mark $sources $dests $proto $ports $sports"` + process_tc_rule + done < $TMP_DIR/tcrules + + run_user_exit tcstart + else + setup_tc1 + fi + +} + +# +# Add a NAT rule - Helper function for the rules file processor +# +# The caller has established the following variables: +# command = The current command -- if 'check', we just go through +# the motions. +# cli = Source IP, interface or MAC Specification +# serv = Destination IP Specification +# servport = Port the server is listening on +# dest_interface = Destination Interface Specification +# proto = Protocol Specification +# addr = Original Destination Address +# dports = Destination Port Specification. 'dports' may be changed +# by this function +# cport = Source Port Specification +# multiport = String to invoke multiport match if appropriate +# +add_nat_rule() { + local chain + + # Be sure we should and can NAT + + case $logtarget in + DNAT|REDIRECT) + if [ -z "$NAT_ENABLED" ]; then + fatal_error "Rule \"$rule\" requires NAT which is disabled" + fi + ;; + *) + fatal_error "Only DNAT and REDIRECT rules may specify port mapping; rule \"$rule\"" + ;; + esac + + # Parse SNAT address if any + + if [ "$addr" != "${addr%:*}" ]; then + snat="${addr#*:}" + addr="${addr%:*}" + else + snat="" + fi + + # Set original destination address + + case $addr in + all) + addr= + ;; + detect) + addr= + if [ -n "$DETECT_DNAT_IPADDRS" -a "$source" != "$FW" ]; then + eval interfaces=\$${source}_interfaces + for interface in $interfaces; do + addr="`find_interface_address $interface` $addr" + done + fi + ;; + esac + + addr=${addr:-0.0.0.0/0} + + # Select target + + if [ -n "$serv" ]; then + servport="${servport:+:$servport}" + target1="DNAT --to-destination ${serv}${servport}" + else + target1="REDIRECT --to-port $servport" + fi + + # Generate nat table rules + + if [ $command != check ]; then + if [ "$source" = "$FW" ]; then + run_iptables2 -t nat -A OUTPUT $proto $sports -d $addr \ + $multiport $dports -j $target1 + else + chain=`dnat_chain $source` + + if [ -n "$excludezones" ]; then + chain=nonat${nonat_seq} + nonat_seq=$(($nonat_seq + 1)) + createnatchain $chain + addnatrule `dnat_chain $source` -j $chain + for z in $excludezones; do + eval hosts=\$${z}_hosts + for host in $hosts; do + for adr in $addr; do + addnatrule $chain $proto -s ${host#*:} \ + $multiport $sports -d $adr $dports -j RETURN + done + done + done + fi + + for adr in $addr; do + addnatrule $chain $proto $cli $sports \ + -d $adr $multiport $dports -j $target1 + done + fi + fi + + # Replace destination port by the new destination port + + if [ -n "$servport" ]; then + if [ -z "$multiport" ]; then + dports="--dport ${servport#*:}" + else + dports="--dports ${servport#*:}" + fi + fi + + # Handle SNAT + + if [ -n "$snat" ]; then + if [ -n "$cli" ]; then + [ $command = check ] || addnatrule `snat_chain $dest` $proto $cli $multiport \ + $sports -d $serv $dports -j SNAT --to-source $snat + else + for source_host in $source_hosts; do + [ "x${source_host#*:}" = "x0.0.0.0/0" ] && \ + error_message "Warning: SNAT will occur on all connections to this server and port - rule \"$rule\"" + + [ $command = check ] || addnatrule `snat_chain $dest` \ + -s ${source_host#*:} $proto $sports $multiport \ + -d $serv $dports -j SNAT --to-source $snat + done + fi + fi +} + +# +# Add one Filter Rule -- Helper function for the rules file processor +# +# The caller has established the following variables: +# check = current command. If 'check', we're executing a 'check' +# which only goes through the motions. +# client = SOURCE IP or MAC +# server = DESTINATION IP or interface +# protocol = Protocol +# address = Original Destination Address +# port = Destination Port +# cport = Source Port +# multioption = String to invoke multiport match if appropriate +# servport = Port the server listens on +# chain = The canonical chain for this rule +# +add_a_rule() +{ + # Set source variables + + cli= + + [ -n "$client" ] && case "$client" in + -) + ;; + *:*) + cli="-i ${client%:*} -s ${client#*:}" + ;; + *.*.*) + cli="-s $client" + ;; + ~*) + cli=`mac_match $client` + ;; + *) + cli="-i $client" + ;; + esac + + # Set destination variables + + dest_interface= + serv= + + [ -n "$server" ] && case "$server" in + -) + ;; + *.*.*) + serv=$server + ;; + ~*) + fatal_error "Rule \"$rule\" - Destination may not be specified by MAC Address" + ;; + *) + dest_interface="-o $server" + ;; + esac + + # Setup protocol and port variables + + sports= + dports= + state="-m state --state NEW" + proto=$protocol + addr=$address + servport=$serverport + multiport= + + case $proto in + tcp|udp|TCP|UDP|6|17) + if [ -n "$port" -a "x${port}" != "x-" ]; then + dports="--dport" + if [ -n "$multioption" -a "$port" != "${port%,*}" ]; then + multiport="$multioption" + dports="--dports" + fi + dports="$dports $port" + fi + + if [ -n "$cport" -a "x${cport}" != "x-" ]; then + sports="--sport" + if [ -n "$multioption" -a "$cport" != "${cport%,*}" ]; then + multiport="$multioption" + sports="--sports" + fi + sports="$sports $cport" + fi + ;; + icmp|ICMP|1) + [ -n "$port" ] && [ "x${port}" != "x-" ] && \ + dports="--icmp-type $port" + state= + ;; + all|ALL) + [ -n "$port" ] && [ "x${port}" != "x-" ] && \ + fatal_error "Port number not allowed with \"all\"; rule: \"$rule\"" + proto= + ;; + *) + state= + [ -n "$port" ] && [ "x${port}" != "x-" ] && \ + fatal_error "Port number not allowed with protocol \"$proto\"; rule: \"$rule\"" + ;; + esac + + proto="${proto:+-p $proto}" + + # Some misc. setup + + case "$logtarget" in + REJECT) + target=reject + [ -n "$servport" ] && \ + fatal_error "Server port may not be specified in a REJECT rule;"\ + "rule: \"$rule\"" + ;; + REDIRECT) + [ -n "$serv" ] && startup_error "REDIRECT rules cannot"\ + " specify a server IP; rule: \"$rule\"" + servport=${servport:=$port} + ;; + DNAT) + [ -n "$serv" ] || fatal_error "DNAT rules require a" \ + " server address; rule: \"$rule\"" + ;; + LOG) + [ -z "$loglevel" ] && fatal_error "LOG requires log level" + ;; + esac + + # Complain if the rule is really a policy + + if [ -z "$proto" -a -z "$cli" -a -z "$serv" -a -z "$servport" ]; then + error_message "Warning -- Rule \"$rule\" is a POLICY" + error_message " -- and should be moved to the policy file" + fi + + if [ -n "${serv}${servport}" ]; then + if [ $command != check ]; then + + # A specific server or server port given + + if [ -n "$addr" -a "$addr" != "$serv" ]; then + add_nat_rule + elif [ -n "$servport" -a "$servport" != "$port" ]; then + add_nat_rule + fi + + if [ -z "$dnat_only" -a $chain != ${FW}2${FW} ]; then + serv="${serv:+-d $serv}" + + if [ -n "$loglevel" ]; then + if [ "$loglevel" = ULOG ]; then + run_iptables2 -A $chain $proto $multiport \ + $state $cli $sports $serv $dports -j ULOG $LOGPARMS \ + --ulog-prefix "Shorewall:$chain:$logtarget:" + else + run_iptables2 -A $chain $proto $multiport \ + $state $cli $sports $serv $dports -j LOG $LOGPARMS \ + --log-prefix "Shorewall:$chain:$logtarget:" \ + --log-level $loglevel + fi + fi + + + run_iptables2 -A $chain $proto $multiport $state $cli $sports \ + $serv $dports -j $target + fi + fi + else + + # Destination is a simple zone + + [ -n "$addr" ] && fatal_error \ + "An ORIGINAL DESTINATION ($addr) is only allowed in" \ + " a DNAT or REDIRECT: \"$rule\"" + + if [ $command != check ]; then + if [ -n "$loglevel" ]; then + if [ "$loglevel" = ULOG ]; then + run_iptables2 -A $chain $proto $multiport \ + $dest_interface $state $cli $sports $dports -j ULOG \ + $LOGPARMS --ulog-prefix "Shorewall:$chain:$logtarget:" + else + run_iptables2 -A $chain $proto $multiport \ + $dest_interface $state $cli $sports $dports -j LOG \ + $LOGPARMS --log-prefix "Shorewall:$chain:$logtarget:" \ + --log-level $loglevel + fi + fi + + if [ $logtarget != LOG ]; then + run_iptables2 -A $chain $proto $multiport $dest_interface $state \ + $cli $sports $dports -j $target + fi + fi + fi +} + +# +# Process a record from the rules file for the 'start', 'restart' or 'check' commands +# +process_rule() # $1 = target + # $2 = clients + # $3 = servers + # $4 = protocol + # $5 = ports + # $6 = cports + # $7 = address +{ + local target="$1" + local clients="$2" + local servers="$3" + local protocol="$4" + local ports="$5" + local cports="$6" + local address="$7" + local rule="`echo $target $clients $servers $protocol $ports $cports $address`" + + # Function Body -- isolate log level + + if [ "$target" = "${target%:*}" ]; then + loglevel= + else + loglevel="${target#*:}" + target="${target%:*}" + expandv loglevel + fi + + logtarget="$target" + dnat_only= + + # Convert 1.3 Rule formats to 1.2 format + + [ "x$address" = "x-" ] && address= + + case $target in + DNAT) + target=ACCEPT + address=${address:=detect} + ;; + DNAT-) + target=ACCEPT + address=${address:=detect} + dnat_only=Yes + logtarget=DNAT + ;; + REDIRECT) + target=ACCEPT + address=${address:=all} + if [ "x-" = "x$servers" ]; then + servers=$FW + else + servers="$FW::$servers" + fi + ;; + esac + + # Parse and validate source + + if [ "$clients" = "${clients%:*}" ]; then + clientzone="$clients" + clients= + else + clientzone="${clients%%:*}" + clients="${clients#*:}" + [ -z "$clientzone" -o -z "$clients" ] && \ + fatal_error "Empty source zone or qualifier: rule \"$rule\"" + fi + + if [ "$clientzone" = "${clientzone%\!*}" ]; then + excludezones= + else + excludezones="${clientzone#*\!}" + clientzone="${clientzone%\!*}" + + [ "$logtarget" = DNAT ] || [ "$logtarget" = REDIRECT ] ||\ + fatal_error "Exclude list only allowed with DNAT or REDIRECT" + fi + + if ! validate_zone $clientzone; then + fatal_error "Undefined Client Zone in rule \"$rule\"" + fi + + # Parse and validate destination + + source=$clientzone + + [ $source = $FW ] && source_hosts= || eval source_hosts=\"\$${source}_hosts\" + + if [ "$servers" = "${servers%:*}" ] ; then + serverzone="$servers" + servers= + serverport= + else + serverzone="${servers%%:*}" + servers="${servers#*:}" + if [ "$servers" != "${servers%:*}" ] ; then + serverport="${servers#*:}" + servers="${servers%:*}" + [ -z "$serverzone" -o -z "$serverport" ] && \ + fatal_error "Empty destination zone or server port: rule \"$rule\"" + else + serverport= + [ -z "$serverzone" -o -z "$servers" ] && \ + startup_error "Empty destination zone or qualifier: rule \"$rule\"" + fi + fi + + if ! validate_zone $serverzone; then + fatal_error "Undefined Server Zone in rule \"$rule\"" + fi + + dest=$serverzone + + # Create canonical chain if necessary + + chain=${source}2${dest} + + [ $command = check ] || ensurechain $chain + + if [ "x$chain" = x${FW}2${FW} ]; then + case $logtarget in + REDIRECT) + ;; + *) + error_message "WARNING: fw -> fw rules are not supported; rule \"$rule\" ignored" + return + ;; + esac + else + [ $command = check ] || ensurechain $chain + fi + + # Generate Netfilter rule(s) + + if [ -n "$MULTIPORT" -a \ + "$ports" = "${ports%:*}" -a \ + "$cports" = "${cports%:*}" -a \ + `list_count $ports` -le 15 -a \ + `list_count $cports` -le 15 ] + then + multioption="-m multiport" + for client in `separate_list ${clients:=-}`; do + for server in `separate_list ${servers:=-}`; do + port=${ports:=-} + cport=${cports:=-} + add_a_rule + done + done + else + multioption= + for client in `separate_list ${clients:=-}`; do + for server in `separate_list ${servers:=-}`; do + for port in `separate_list ${ports:=-}`; do + for cport in `separate_list ${cports:=-}`; do + add_a_rule + done + done + done + done + fi + + if [ $command = check ]; then + echo " Rule \"$rule\" checked." + else + echo " Rule \"$rule\" added." + fi +} + +# +# Process the rules file for the 'start', 'restart' or 'check' command. +# +process_rules() # $1 = name of rules file +{ + # + # Process a rule where the source or destination is "all" + # + process_wildcard_rule() { + for yclients in $xclients; do + for yservers in $xservers; do + if [ "${yclients}" != "${yservers}" ] ; then + process_rule $xtarget $yclients $yservers $xprotocol $xports $xcports $xaddress + fi + done + done + } + + while read xtarget xclients xservers xprotocol xports xcports xaddress; do + case "${xtarget%:*}" in + + ACCEPT|DROP|REJECT|DNAT|DNAT|DNAT-|REDIRECT|LOG|CONTINUE) + expandv xclients xservers xprotocol xports xcports xaddress + + if [ "x$xclients" = xall ]; then + xclients="$zones $FW" + if [ "x$xservers" = xall ]; then + xservers="$zones $FW" + fi + process_wildcard_rule + continue + fi + + if [ "x$xservers" = xall ]; then + xservers="$zones $FW" + process_wildcard_rule + continue + fi + + process_rule $xtarget $xclients $xservers $xprotocol $xports $xcports $xaddress + ;; + *) + rule="`echo $xtarget $xclients $xservers $xprotocol $xports $xcports $xaddress`" + fatal_error "Invalid Target in rule \"$rule\"" + ;; + + esac + done < $TMP_DIR/rules +} + +# +# 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 subnet + # + src="-s $src" + ;; + ~*) + src=`mac_match $src` + ;; + *) + # + # Assume that this is a device name + # + src="-i $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 subnet + # + ;; + *) + # + # 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="-d $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 + + echo " Rule \"$rule\" added." +} + +# +# Process the tos file +# +process_tos() # $1 = name of tos file +{ + echo "Processing $1..." + + run_iptables -t mangle -N pretos + run_iptables -t mangle -N outtos + + strip_file tos $1 + + 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 +} + +# +# Load a Kernel Module +# +loadmodule() # $1 = module name, $2 - * arguments +{ + local modulename=$1 + local modulefile + + if [ -z "`lsmod | grep $modulename`" ]; then + shift + modulefile=$MODULESDIR/${modulename}.o + + if [ -f $modulefile ]; then + insmod $modulefile $* + return + fi + # + # If the modules directory contains compressed modules then we'll + # assume that insmod can load them + # + modulefile=${modulefile}.gz + + if [ -f $modulefile ]; then + insmod $modulefile $* + fi + fi +} + +# +# Display elements of a list with leading white space +# +display_list() # $1 = List Title, rest of $* = list to display +{ + [ $# -gt 1 ] && echo " $*" +} + +# +# Add rules to the "common" chain to silently drop packets addressed to any of +# the passed addresses +# +drop_broadcasts() # $* = broadcast addresses +{ + while [ $# -gt 0 ]; do + run_iptables -A common -d $1 -j DROP + shift + done +} + +# +# Add policy rule ( and possibly logging rule) to the passed chain +# +policy_rules() # $1 = chain to add rules to + # $2 = policy + # $3 = loglevel +{ + local target="$2" + + case "$target" in + ACCEPT) + ;; + + DROP) + run_iptables -A $1 -j common + ;; + REJECT) + run_iptables -A $1 -j common + target=reject + ;; + CONTINUE) + target= + ;; + *) + fatal_error "Invalid policy ($policy) for $1" + ;; + + esac + + if [ $# -eq 3 -a "x${3}" != "x-" ]; then + if [ "$3" = ULOG ]; then + run_iptables -A $1 -j ULOG $LOGPARMS \ + --ulog-prefix "Shorewall:${1}:${2}:" + else + run_iptables -A $1 -j LOG $LOGPARMS \ + --log-prefix "Shorewall:${1}:${2}:" --log-level $3 + fi + fi + + [ -n "$target" ] && run_iptables -A $1 -j $target +} + +# +# Generate default policy & log level rules for the passed client & server +# zones +# +# This function is only called when the canonical chain for this client/server +# pair is known to exist. If the default policy for this pair specifies the +# same chain then we add the policy (and logging) rule to the canonical chain; +# otherwise add a rule to the canonical chain to jump to the appropriate +# policy chain. +# +default_policy() # $1 = client $2 = server +{ + local chain="${1}2${2}" + local policy= + local loglevel= + local chain1 + + jump_to_policy_chain() { + # + # Add a jump to from the canonical chain to the policy chain. On return, + # $chain is set to the name of the policy chain + # + run_iptables -A $chain -j $chain1 + chain=$chain1 + } + + apply_default() + { + # + # Generate policy file column values from the policy chain + # + eval policy=\$${chain1}_policy + eval loglevel=\$${chain1}_loglevel + eval synparams=\$${chain1}_synparams + # + # Add the appropriate rules to the canonical chain ($chain) to enforce + # the specified policy + + if [ "$chain" = "$chain1" ]; then + # + # The policy chain is the canonical chain; add policy rule to it + # The syn flood jump has already been added if required. + # + policy_rules $chain $policy $loglevel + else + # + # The policy chain is different from the canonical chain -- approach + # depends on the policy + # + case $policy in + ACCEPT) + if [ -n "$synparams" ]; then + # + # To avoid double-counting SYN packets, enforce the policy + # in this chain. + # + enable_syn_flood_protection $chain $chain1 + policy_rules $chain $policy $loglevel + else + # + # No problem with double-counting so just jump to the + # policy chain. + # + jump_to_policy_chain + fi + ;; + CONTINUE) + # + # Silly to jump to the policy chain -- add any logging + # rules and enable SYN flood protection if requested + # + [ -n "$synparams" ] && \ + enable_syn_flood_protection $chain $chain1 + policy_rules $chain $policy $loglevel + ;; + *) + # + # DROP or REJECT policy -- enforce in the policy chain and + # enable SYN flood protection if requested. + # + [ -n "$synparams" ] && \ + enable_syn_flood_protection $chain $chain1 + jump_to_policy_chain + ;; + esac + fi + + echo " Policy $policy for $1 to $2 using chain $chain" + } + + eval chain1=\$${1}2${2}_policychain + + if [ -n "$chain1" ]; then + apply_default $1 $2 + else + fatal_error "No default policy for zone $1 to zone $2" + fi +} + +# +# Complete a standard chain +# +# - run any supplied user exit +# - search the policy file for an applicable policy and add rules as +# appropriate +# - If no applicable policy is found, add rules for an assummed +# policy of DROP INFO +# +complete_standard_chain() # $1 = chain, $2 = source zone, $3 = destination zone +{ + local policy= + local loglevel= + local policychain= + + run_user_exit $1 + + eval policychain=\$${2}2${3}_policychain + + if [ -n "$policychain" ]; then + eval policy=\$${policychain}_policy + eval loglevel=\$${policychain}_loglevel + + policy_rules $1 $policy $loglevel + else + policy_rules $1 DROP INFO + fi +} + +# +# Find the appropriate chain to pass packets from a source zone to a +# destination zone +# +# If the canonical chain for this zone pair exists, echo it's name; otherwise +# locate and echo the name of the appropriate policy chain +# +rules_chain() # $1 = source zone, $2 = destination zone +{ + local chain=${1}2${2} + + havechain $chain && { echo $chain; return; } + + eval chain=\$${chain}_policychain + + [ -n "$chain" ] && { echo $chain; return; } + + fatal_error "No appropriate chain for zone $1 to zone $2" +} + +# +# echo the list of subnets routed out of a given interface +# +get_routed_subnets() # $1 = interface name +{ + local address + local rest + + ip route show dev $1 2> /dev/null | + while read address rest; do + if [ "x$address" = xdefault ]; then + error_message "Warning: default route ignored on interface $1" + else + [ "$address" = "${address%/*}" ] && address="${address}/32" + echo $address + fi + done +} + +# +# Set up Source NAT (including masquerading) +# +setup_masq() +{ + setup_one() { + local using + + case $fullinterface in + *:*:*) + # Both alias name and subnet + destnet="${fullinterface##*:}" + fullinterface="${fullinterface%:*}" + ;; + *:*) + # Alias name OR subnet + case ${fullinterface#*:} in + *.*) + # It's a subnet + destnet="${fullinterface#*:}" + fullinterface="${fullinterface%:*}" + ;; + *) + #it's an alias name + destnet="0.0.0.0/0" + ;; + esac + ;; + *) + destnet="0.0.0.0/0" + ;; + esac + + interface=${fullinterface%:*} + + if ! list_search $interface $all_interfaces; then + fatal_error "Unknown interface $interface" + fi + + if [ "$subnet" = "${subnet%!*}" ]; then + nomasq= + else + nomasq="${subnet#*!}" + subnet="${subnet%!*}" + fi + + chain=`masq_chain $interface` + iface= + + source="$subnet" + + case $subnet in + *.*.*) + ;; + -) + # + # Note: This only works if you have the LOCAL NAT patches in the + # kernel and in the iptables utility + # + chain=OUTPUT + subnet= + source=$FW + iface="-o $interface" + ;; + *) + subnets=`get_routed_subnets $subnet` + [ -z "$subnets" ] && startup_error "Unable to determine the routes through interface $subnet" + subnet="$subnets" + ;; + esac + + if [ -n "$address" -a -n "$ADD_SNAT_ALIASES" ]; then + list_search $address $aliases_to_add || \ + aliases_to_add="$aliases_to_add $address $fullinterface" + fi + + destination=$destnet + + if [ -n "$nomasq" ]; then + newchain=masq${masq_seq} + createnatchain $newchain + + if [ -n "$subnet" ]; then + for s in $subnet; do + addnatrule $chain -d $destnet $iface -s $s -j $newchain + done + else + addnatrule $chain -d $destnet $iface -j $newchain + fi + + masq_seq=$(($masq_seq + 1)) + chain=$newchain + subnet= + iface= + destnet= + + for addr in `separate_list $nomasq`; do + addnatrule $chain -s $addr -j RETURN + done + + source="$source except $nomasq" + else + destnet="-d $destnet" + fi + + if [ -n "$subnet" ]; then + for s in $subnet; do + if [ -n "$address" ]; then + addnatrule $chain -s $s $destnet $iface -j SNAT --to-source $address + echo " To $destination from $s through ${interface} using $address" + else + addnatrule $chain -s $s $destnet $iface -j MASQUERADE + echo " To $destination from $s through ${interface}" + fi + done + elif [ -n "$address" ]; then + addnatrule $chain $destnet $iface -j SNAT --to-source $address + echo " To $destination from $source through ${interface} using $address" + else + addnatrule $chain $destnet $iface -j MASQUERADE + echo " To $destination from $source through ${interface}" + fi + + } + + strip_file masq $1 + + [ -n "$NAT_ENABLED" ] && echo "Masqueraded Subnets and Hosts:" + + while read fullinterface subnet address; do + expandv fullinterface subnet address + [ -n "$NAT_ENABLED" ] && setup_one || \ + error_message "Warning: NAT disabled; masq rule ignored" + done < $TMP_DIR/masq +} + +# +# 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 + if [ "$BLACKLIST_LOGLEVEL" = ULOG ]; then + run_iptables2 -A blacklst $source $proto $dport -j \ + ULOG $LOGPARMS --ulog-prefix \ + "Shorewall:blacklst:$BLACKLIST_DISPOSITION:" + else + run_iptables2 -A blacklst $source $proto $dport -j \ + LOG $LOGPARMS --log-prefix \ + "Shorewall:blacklst:$BLACKLIST_DISPOSITION:" \ + --log-level $BLACKLIST_LOGLEVEL + fi + fi + + run_iptables2 -A blacklst $source $proto $dport -j $disposition +} + +# +# Process a record from the blacklist file +# +# $subnet = address/subnet +# $protocol = Protocol Number/Name +# $port = Port Number/Name +# +process_blacklist_rec() { + local source + local addr + local proto + local dport + + for addr in `separate_list $subnet`; do + case $addr in + ~*) + addr=`echo $addr | sed 's/~//;s/-/:/g'` + source="--match mac --mac-source $addr" + ;; + *) + source="-s $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 + + echo " $addr added to Black List" + done +} + +# +# Setup the Black List +# +setup_blacklist() { + local interfaces=`find_interfaces_by_option blacklist` + local f=`find_file blacklist` + local disposition=$BLACKLIST_DISPOSITION + + if [ -n "$interfaces" -a -f $f ]; then + echo "Setting up Blacklisting..." + + strip_file blacklist $f + + createchain blacklst no + + for interface in $interfaces; do + for chain in `first_chains $interface`; do + run_iptables -A $chain -j blacklst + done + + echo " Blacklisting enabled on $interface" + done + + [ "$disposition" = REJECT ] && disposition=reject + + while read subnet protocol ports; do + expandv subnet protocol ports + process_blacklist_rec + done < $TMP_DIR/blacklist + + fi +} + +# +# Refresh the Black List +# +refresh_blacklist() { + local f=`find_file blacklist` + local disposition=$BLACKLIST_DISPOSITION + + if qt iptables -L blacklst -n ; then + echo "Refreshing Black List..." + + strip_file blacklist $f + + [ "$disposition" = REJECT ] && disposition=reject + + run_iptables -F blacklst + + while read subnet protocol ports; do + expandv subnet protocol ports + process_blacklist_rec + done < $TMP_DIR/blacklist + fi +} + +# +# Verify that kernel has netfilter support +# +verify_os_version() { + + osversion=`uname -r` + + case $osversion in + 2.4.*|2.5.*) + ;; + *) + startup_error "Shorewall version $version does not work with kernel version $osversion" + ;; + esac + + [ $command = start -a -n "`lsmod 2> /dev/null | grep '^ipchains'`" ] && \ + startup_error "Shorewall can't start with the ipchains kernel module loaded - see FAQ #8" +} + +# +# Add IP Aliases +# +add_ip_aliases() +{ + local external + local interface + local primary + + do_one() + { + # + # 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 the primary address + # + # Get all of the lines that contain inet addresses with broadcast + # + val=`ip addr show $interface | grep 'inet.*brd '` 2> /dev/null + + if [ -n "$val" ] ; then + # + # Hack off the leading 'inet ' (actually cut off the + # "/" as well but add it back in). + # + val="/${val#*/}" + # + # Now get the VLSM, "brd" and the broadcast address + # + val=${val%% scope*} + fi + + run_ip addr add ${external}${val} dev $interface $label + echo "$external $interface" >> ${STATEDIR}/nat + [ -n "$label" ] && label="with $label" + echo " IP Address $external added to interface $interface $label" + } + + set -- $aliases_to_add + + while [ $# -gt 0 ]; do + external=$1 + interface=$2 + label= + + if [ "$interface" != "${interface%:*}" ]; then + label="${interface#*:}" + interface="${interface%:*}" + label="label $interface:$label" + fi + + primary=`find_interface_address $interface` + shift;shift + [ "x${primary}" = "x${external}" ] || do_one + done +} + +# +# Load kernel modules required for Shorewall +# +load_kernel_modules() { + + [ -z "$MODULESDIR" ] && \ + MODULESDIR=/lib/modules/$osversion/kernel/net/ipv4/netfilter + + modules=`find_file modules` + + if [ -f $modules -a -d $MODULESDIR ]; then + echo "Loading Modules..." + . $modules + fi +} + +# Verify that the 'ip' program is installed + +verify_ip() { + qt which ip ||\ + startup_error "Shorewall $version requires the iproute package ('ip' utility)" +} + +# +# Perform Initialization +# - Delete all old rules +# - Delete all user chains +# - Set the POLICY on all standard chains and add a rule to allow packets +# that are part of established connections +# - Determine the zones +# +initialize_netfilter () { + + echo "Determining Zones..." + + determine_zones + + [ -z "$zones" ] && startup_error "No Zones Defined" + + display_list "Zones:" $zones + + echo "Validating interfaces file..." + + validate_interfaces_file + + echo "Validating hosts file..." + + validate_hosts_file + + echo "Validating Policy file..." + + validate_policy + + echo "Determining Hosts in Zones..." + + determine_interfaces + determine_hosts + + run_user_exit init + + # + # The some files might be large so strip them while the firewall is still running + # (restart command). This reduces the length of time that the firewall isn't + # accepting new connections. + # + + strip_file rules + strip_file proxyarp + strip_file maclist + strip_file nat + + terminator=fatal_error + + deletechain shorewall + + [ -n "$NAT_ENABLED" ] && delete_nat + + delete_proxy_arp + + [ -n "$MANGLE_ENABLED" ] && \ + run_iptables -t mangle -F && \ + run_iptables -t mangle -X + + [ -n "$CLEAR_TC" ] && delete_tc + + echo "Deleting user chains..." + + setpolicy INPUT DROP + setpolicy OUTPUT DROP + setpolicy FORWARD DROP + + deleteallchains + + setcontinue FORWARD + setcontinue INPUT + setcontinue OUTPUT + # + # Allow DNS lookups during startup for FQDNs and deep-six INVALID packets + # + + for chain in INPUT OUTPUT FORWARD; do + run_iptables -A $chain -p udp --dport 53 -j ACCEPT + run_iptables -A $chain -m state --state INVALID -j DROP + done + + [ -n "$CLAMPMSS" ] && \ + run_iptables -A FORWARD -p tcp \ + --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu + + + if [ -z "$NEWNOTSYN" ]; then + createchain newnotsyn no + run_user_exit newnotsyn + if [ -n "$LOGNEWNOTSYN" ]; then + if [ "$LOGNEWNOTSYN" = ULOG ]; then + run_iptables -A newnotsyn -j ULOG $LOGPARMS \ + --ulog-prefix "Shorewall:newnotsyn:DROP:" + else + run_iptables -A newnotsyn -j LOG $LOGPARMS \ + --log-prefix "Shorewall:newnotsyn:DROP:" --log-level $LOGNEWNOTSYN + fi + fi + + run_iptables -A newnotsyn -j DROP + fi + + createchain icmpdef no + createchain common no + createchain reject no + createchain dynamic no + + if [ -f /var/lib/shorewall/save ]; then + echo "Restoring dynamic rules..." + + while read target ignore1 ignore2 address rest; do + case $target in + DROP|reject) + run_iptables2 -A dynamic -s $address -j $target + ;; + *) + ;; + esac + done < /var/lib/shorewall/save + fi + + echo "Creating input Chains..." + + for interface in $all_interfaces; do + createchain `forward_chain $interface` no + run_iptables -A `forward_chain $interface` -j dynamic + createchain `input_chain $interface` no + run_iptables -A `input_chain $interface` -j dynamic + done +} + +# +# Build the common chain -- called during [re]start and refresh +# +build_common_chain() { + + # + # Common ICMP rules + # + run_user_exit icmpdef + # + # Common rules in each chain + # + common=`find_file common` + + if [ -f $common ]; then + . $common + else + . `find_file common.def` + fi + # + # New Not Syn Stuff + # + if [ -n "$NEWNOTSYN" ]; then + run_iptables -A common -p tcp --tcp-flags ACK ACK -j ACCEPT + run_iptables -A common -p tcp --tcp-flags RST RST -j ACCEPT + fi + # + # BROADCASTS + # + drop_broadcasts `find_broadcasts` +} + +# +# Construct zone-independent rules +# +add_common_rules() { + logdisp() # $1 = Chain Name + { + if [ "$RFC1918_LOG_LEVEL" = ULOG ]; then + echo "ULOG $LOGPARMS --ulog-prefix Shorewall:${1}:DROP:" + else + echo "LOG $LOGPARMS --log-prefix Shorewall:${1}:DROP: --log-level $RFC1918_LOG_LEVEL" + fi + } + # + # Reject Rules + # + run_iptables -A reject -p tcp -j REJECT --reject-with tcp-reset + run_iptables -A reject -j REJECT + # + # dropunclean rules + # + interfaces="`find_interfaces_by_option dropunclean`" + + if [ -n "$interfaces" ]; then + createchain badpkt no + + if [ -n "$LOGUNCLEAN" ]; then + if [ "$LOGUNCLEAN" = ULOG ]; then + logoptions="-j ULOG $LOGPARMS --ulog-prefix Shorewall:badpkt:DROP:" + logoptions="$logoptions --log-ip-options" + else + logoptions="-j LOG $LOGPARMS --log-prefix Shorewall:badpkt:DROP:" + logoptions="$logoptions --log-level $LOGUNCLEAN --log-ip-options" + fi + + run_iptables -A badpkt -p tcp $logoptions --log-tcp-options + run_iptables -A badpkt -p ! tcp $logoptions + fi + + run_iptables -A badpkt -j DROP + echo "Mangled/Invalid Packet filtering enabled on:" + + for interface in $interfaces; do + for chain in `first_chains $interface`; do + run_iptables -A $chain --match unclean -j badpkt + done + echo " $interface" + done + fi + # + # logunclean rules + # + interfaces="`find_interfaces_by_option logunclean`" + + if [ -n "$interfaces" ]; then + createchain logpkt no + + [ -z"$LOGUNCLEAN" ] && LOGUNCLEAN=info + + if [ "$LOGUNCLEAN" = ULOG ]; then + logoptions="-j ULOG $LOGPARMS --ulog-prefix Shorewall:logpkt:LOG:" + logoptions="$logoptions --log-ip-options" + else + logoptions="-j LOG $LOGPARMS --log-prefix Shorewall:logpkt:LOG:" + logoptions="$logoptions --log-level $LOGUNCLEAN --log-ip-options" + fi + + run_iptables -A logpkt -p tcp $logoptions --log-tcp-options + run_iptables -A logpkt -p ! tcp $logoptions + + echo "Mangled/Invalid Packet Logging enabled on:" + + for interface in $interfaces; do + for chain in `first_chains $interface`; do + run_iptables -A $chain --match unclean -j logpkt + done + echo " $interface" + done + fi + + build_common_chain + + # + # DHCP + # + echo "Adding rules for DHCP" + + for interface in `find_interfaces_by_option dhcp`; do + run_iptables -A `input_chain $interface` -p udp --dport 67:68 -j ACCEPT + run_iptables -A OUTPUT -o $interface -p udp --dport 67:68 -j ACCEPT + done + + # + # RFC 1918 + # + norfc1918_interfaces="`find_interfaces_by_option norfc1918`" + + if [ -n "$norfc1918_interfaces" ]; then + echo "Enabling RFC1918 Filtering" + + strip_file rfc1918 + + createchain rfc1918 no + + createchain logdrop no + run_iptables -A logdrop -j `logdisp rfc1918` + run_iptables -A logdrop -j DROP + + if [ -n "$MANGLE_ENABLED" ]; then + # + # Mangling is enabled -- create a chain in the mangle table to + # filter RFC1918 destination addresses. This must be done in the + # mangle table before we apply any DNAT rules in the nat table + # + # Also add a chain to log and drop any RFC1918 packets that we find + # + run_iptables -t mangle -N man1918 + run_iptables -t mangle -N logdrop + run_iptables -t mangle -A logdrop -j `logdisp man1918` + run_iptables -t mangle -A logdrop -j DROP + fi + + while read subnet target; do + case $target in + logdrop|DROP|RETURN) + ;; + *) + fatal_error "Illegal target ($target) for $subnet" + ;; + esac + + run_iptables2 -A rfc1918 -s $subnet -j $target + # + # If packet mangling is enabled, trap packets with an + # RFC1918 destination + # + if [ -n "$MANGLE_ENABLED" ]; then + run_iptables2 -t mangle -A man1918 -d $subnet -j $target + fi + done < $TMP_DIR/rfc1918 + + for interface in $norfc1918_interfaces; do + for chain in `first_chains $interface`; do + run_iptables -A $chain -m state --state NEW -j rfc1918 + done + + [ -n "$MANGLE_ENABLED" ] && \ + run_iptables -t mangle -A PREROUTING -m state --state NEW -i $interface -j man1918 + done + + fi + + interfaces=`find_interfaces_by_option tcpflags` + + if [ -n "$interfaces" ]; then + echo "Setting up TCP Flags checking..." + + createchain tcpflags no + + if [ -n "$TCP_FLAGS_LOG_LEVEL" ]; then + createchain logflags no + + if [ "$TCP_FLAGS_LOG_LEVEL" = ULOG ]; then + run_iptables -A logflags -j ULOG $LOGPARMS \ + --ulog-prefix "Shorewall:logflags:$TCP_FLAGS_DISPOSITION:" \ + --log-tcp-options --log-ip-options + else + run_iptables -A logflags -j LOG $LOGPARMS \ + --log-level $TCP_FLAGS_LOG_LEVEL \ + --log-prefix "Shorewall:logflags:$TCP_FLAGS_DISPOSITION:" \ + --log-tcp-options --log-ip-options + fi + case $TCP_FLAGS_DISPOSITION in + REJECT) + run_iptables -A logflags -j REJECT --reject-with tcp-reset + ;; + *) + run_iptables -A logflags -j $TCP_FLAGS_DISPOSITION + ;; + esac + + disposition="-j logflags" + else + disposition="-j $TCP_FLAGS_DISPOSITION" + fi + + run_iptables -A tcpflags -p tcp --tcp-flags ALL FIN,URG,PSH $disposition + run_iptables -A tcpflags -p tcp --tcp-flags ALL NONE $disposition + run_iptables -A tcpflags -p tcp --tcp-flags SYN,RST SYN,RST $disposition + run_iptables -A tcpflags -p tcp --tcp-flags SYN,FIN SYN,FIN $disposition + # + # There are a lot of probes to ports 80, 3128 and 8080 that use a source + # port of 0. This catches them even if they are directed at an IP that + # hosts a web server. + # + run_iptables -A tcpflags -p tcp --syn --sport 0 $disposition + + for interface in $interfaces; do + for chain in `first_chains $interface`; do + run_iptables -A $chain -p tcp -j tcpflags + done + done + fi + # + # Process Black List + # + setup_blacklist + + # + # Enable the Loopback interface + # + run_iptables -A INPUT -i lo -j ACCEPT + run_iptables -A OUTPUT -o lo -j ACCEPT + + # + # Route Filtering + # + for f in /proc/sys/net/ipv4/conf/*/rp_filter; do + echo 0 > $f + done + + interfaces="`find_interfaces_by_option routefilter`" + + if [ -n "$interfaces" -o -n "$ROUTE_FILTER" ]; then + echo "Setting up Kernel Route Filtering..." + + if [ -n "$ROUTE_FILTER" ]; then + echo 1 > /proc/sys/net/ipv4/conf/all/rp_filter + else + echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter + + for interface in $interfaces; do + file=/proc/sys/net/ipv4/conf/$interface/rp_filter + if [ -f $file ]; then + echo 1 > $file + else + error_message \ + "Warning: Cannot set route filtering on $interface" + fi + done + fi + fi + # + # IP Forwarding + # + case "$IP_FORWARDING" in + [Oo][Nn]) + echo 1 > /proc/sys/net/ipv4/ip_forward + echo "IP Forwarding Enabled" + ;; + [Oo][Ff][Ff]) + echo 0 > /proc/sys/net/ipv4/ip_forward + echo "IP Forwarding Disabled!" + ;; + esac +} + +# +# Scan the policy file defining the necessary chains +# Add the appropriate policy rule(s) to the end of each canonical chain +# +apply_policy_rules() { + # + # Create policy chains + # + for chain in $all_policy_chains; do + eval policy=\$${chain}_policy + eval loglevel=\$${chain}_loglevel + eval synparams=\$${chain}_synparams + + [ -n "$synparams" ] && setup_syn_flood_chain $chain $synparams + + if havechain $chain; then + [ -n "$synparams" ] && \ + run_iptables -I $chain 2 -p tcp --syn -j @$chain + else + # + # The chain doesn't exist. Create the chain and add policy + # rules + # + # We must include the ESTABLISHED and RELATED state + # rule here to account for replys and reverse + # related sessions associated with sessions going + # in the other direction + # + createchain $chain yes + + # + # If either client or server is 'all' then this MUST be + # a policy chain and we must apply the appropriate policy rules + # + # Otherwise, this is a canonical chain which will be handled in + # the for loop below + # + case $chain in + all2*|*2all) + policy_rules $chain $policy $loglevel + ;; + esac + + [ -n "$synparams" ] && \ + [ $policy = ACCEPT -o $policy = CONTINUE ] && \ + run_iptables -I $chain 2 -p tcp --syn -j @$chain + fi + + done + + # + # Add policy rules to canonical chains + # + for zone in $FW $zones; do + for zone1 in $FW $zones; do + chain=${zone}2${zone1} + if havechain $chain; then + run_user_exit $chain + default_policy $zone $zone1 + fi + done + done +} + +# +# Activate the rules +# +activate_rules() +{ + local PREROUTING_rule=1 + local POSTROUTING_rule=1 + # + # Jump to a NAT chain from one of the builtin nat chains + # + addnatjump() # $1 = BUILTIN chain, $2 = user chain, $3 - * other arguments + { + local sourcechain=$1 destchain=$2 + shift + shift + + havenatchain $destchain && \ + run_iptables -t nat -A $sourcechain $@ -j $destchain + } + + # + # Jump to a RULES chain from one of the builtin nat chains + # + # If NAT_BEFORE_RULES then append the rule to the chain; otherwise, insert + # the jump near the front of the builtin chain + # + addrulejump() # $1 = BUILTIN chain, $2 = user chain, $3 - * other arguments + { + local sourcechain=$1 destchain=$2 + shift + shift + + if havenatchain $destchain; then + if [ -n "$NAT_BEFORE_RULES" ]; then + run_iptables -t nat -A $sourcechain $@ -j $destchain + else + eval run_iptables -t nat -I $sourcechain \ + \$${sourcechain}_rule $@ -j $destchain + eval ${sourcechain}_rule=\$\(\(\$${sourcechain}_rule + 1\)\) + fi + fi + } + + # + # Add jumps from the builtin chains to the nat chains + # + addnatjump PREROUTING nat_in + addnatjump POSTROUTING nat_out + + for interface in $all_interfaces; do + addnatjump PREROUTING `input_chain $interface` -i $interface + addnatjump POSTROUTING `output_chain $interface` -o $interface + done + + > ${STATEDIR}/chains + > ${STATEDIR}/zones + + for zone in $zones; do + eval source_hosts=\$${zone}_hosts + + echo $zone $source_hosts >> ${STATEDIR}/zones + + chain1=`rules_chain $FW $zone` + chain2=`rules_chain $zone $FW` + + echo "$FW $zone $chain1" >> ${STATEDIR}/chains + echo "$zone $FW $chain2" >> ${STATEDIR}/chains + + for host in $source_hosts; do + interface=${host%:*} + subnet=${host#*:} + + run_iptables -A OUTPUT -o $interface -d $subnet -j $chain1 + + # + # Add jumps from the builtin chains for DNAT and SNAT rules + # + addrulejump PREROUTING `dnat_chain $zone` -i $interface -s $subnet + addrulejump POSTROUTING `snat_chain $zone` -o $interface -d $subnet + + run_iptables -A `input_chain $interface` -s $subnet -j $chain2 + + done + + for zone1 in $zones; do + eval dest_hosts=\$${zone1}_hosts + + chain="`rules_chain $zone $zone1`" + + echo "$zone $zone1 $chain" >> ${STATEDIR}/chains + + if havechain ${zone}2${zone1} || havechain ${zone1}2${zone}; then + have_canonical=Yes + else + have_canonical= + fi + + for host in $source_hosts; do + interface=${host%:*} + subnet=${host#*:} + chain1=`forward_chain $interface` + + if [ -n "$have_canonical" ]; then + bounce=yes + else + case $interface in + *+*) + bounce=yes + ;; + *) + bounce= + ;; + esac + fi + + for host1 in $dest_hosts; do + interface1=${host1%:*} + subnet1=${host1#*:} + + if [ $interface != $interface1 -o -n "$bounce" ]; then + run_iptables -A $chain1 -s $subnet -o $interface1 -d $subnet1 -j $chain + fi + done + done + done + done + + for interface in $all_interfaces; do + run_iptables -A FORWARD -i $interface -j `forward_chain $interface` + run_iptables -A INPUT -i $interface -j `input_chain $interface` + addnatjump POSTROUTING `masq_chain $interface` -o $interface + done + + complete_standard_chain INPUT all $FW + complete_standard_chain OUTPUT $FW all + complete_standard_chain FORWARD all all + + for chain in INPUT OUTPUT FORWARD; do + run_iptables -D $chain -m state --state ESTABLISHED -j ACCEPT + run_iptables -D $chain -p udp --dport 53 -j ACCEPT + done +} + +# +# Start/Restart the Firewall +# +define_firewall() # $1 = Command (Start or Restart) +{ + if [ -f /etc/shorewall/startup_disabled ]; then + echo " Shorewall Startup is disabled -- to enable startup" + echo " after you have completed Shorewall configuration," + echo " remove the file /etc/shorewall/startup_disabled" + + [ -n "$TMP_DIR" ] && rm -rf $TMP_DIR + my_mutex_off + exit 2 + fi + + echo "${1}ing Shorewall..." + + verify_os_version + + verify_ip + + load_kernel_modules + + echo "Initializing..." + + initialize_netfilter + + echo "Configuring Proxy ARP" + + setup_proxy_arp + + setup_nat + + echo "Adding Common Rules" + + add_common_rules + + tunnels=`find_file tunnels` + + [ -f $tunnels ] && \ + echo "Processing $tunnels..." && setup_tunnels $tunnels + + maclist_hosts=`find_hosts_by_option maclist` + + if [ -n "$maclist_hosts" ] ; then + setup_mac_lists + fi + + rules=`find_file rules` + + echo "Processing $rules..." + + process_rules $rules + + policy=`find_file policy` + + echo "Processing $policy..." + + apply_policy_rules + + masq=`find_file masq` + + [ -f $masq ] && setup_masq $masq + + tos=`find_file tos` + + [ -f $tos ] && [ -n "$MANGLE_ENABLED" ] && process_tos $tos + + ecn=`find_file ecn` + + [ -f $ecn ] && [ -n "$MANGLE_ENABLED" ] && setup_ecn $ecn + + [ -n "$TC_ENABLED" ] && setup_tc + + echo "Activating Rules..." + + activate_rules + + [ -n "$aliases_to_add" ] && \ + echo "Adding IP Addresses..." && \ + add_ip_aliases + + run_user_exit start + + createchain shorewall no + + date > $STATEDIR/restarted + + report "Shorewall ${1}ed" + + rm -rf $TMP_DIR +} + +# +# Rebuild the common chain +# +refresh_firewall() +{ + echo "Refreshing Shorewall..." + + echo "Determining Zones and Interfaces..." + + determine_zones + + validate_interfaces_file + + [ -z "$zones" ] && startup_error "No Zones Defined" + + determine_interfaces + + run_user_exit refresh + + run_iptables -F common + + echo "Adding Common Rules" + + build_common_chain + + # + # Blacklist + # + refresh_blacklist + + ecn=`find_file ecn` + + [ -f $ecn ] && [ -n "$MANGLE_ENABLED" ] && setup_ecn $ecn + # + # Refresh Traffic Control + # + [ -n "$TC_ENABLED" ] && refresh_tc + + report "Shorewall Refreshed" + + rm -rf $TMP_DIR +} + +# +# Add a host or subnet to a zone +# +add_to_zone() # $1 = [:] $2 = zone +{ + local base + + nat_chain_exists() # $1 = chain name + { + qt iptables -t nat -L $1 -n + } + + do_iptables() # $@ = command + { + if ! iptables $@ ; then + startup_error "Can't add $1 to zone $2" + fi + } + + output_rule_num() { + local num=`iptables -L OUTPUT -n --line-numbers | grep icmp | cut -d' ' -f1 | head -n1` + + [ -n "$num" ] && echo $(($num+1)) + } + # + # Isolate interface and host parts + # + interface=${1%:*} + host=${1#*:} + + [ -z "$host" ] && host="0.0.0.0/0" + # + # Load $zones + # + determine_zones + # + # Validate Zone + # + zone=$2 + + validate_zone $zone || startup_error "Unknown zone: $zone" + + [ "$zone" = $FW ] && startup_error "Can't add $1 to firewall zone" + # + # Be sure that Shorewall has been restarted using a DZ-aware version of the code + # + [ -f ${STATEDIR}/chains ] || startup_error "${STATEDIR}/chains -- file not found" + [ -f ${STATEDIR}/zones ] || startup_error "${STATEDIR}/zones -- file not found" + # + # Be sure that the interface was present at last [re]start + # + if ! chain_exists `input_chain $interface` ; then + startup_error "Unknown interface $interface" + fi + # + # Build lists of interfaces with special rules + # + dhcp_interfaces=`find_interfaces_by_option dhcp` + blacklist_interfaces=`find_interfaces_by_option blacklist` + maclist_interfaces=`find_interfaces_by_option maclist` + tcpflags_interfaces=`find_interfaces_by_option tcpflags` + # + # Normalize the first argument to this function + # + newhost="$interface:$host" + + terminator=fatal_error + # + # Create a new Zone state file + # + > ${STATEDIR}/zones_$$ + # + # Add $1 to the Zone state file + # + while read z hosts; do + if [ "$z" = "$zone" ]; then + for h in $hosts; do + if [ "$h" = "$newhost" ]; then + rm -f ${STATEDIR}/zones_$$ + startup_error "$1 already in zone $zone" + fi + done + + [ -z "$hosts" ] && hosts=$newhost || hosts="$hosts $newhost" + fi + + eval ${z}_hosts=\"$hosts\" + + echo "$z $hosts" >> ${STATEDIR}/zones_$$ + done < ${STATEDIR}/zones + + mv -f ${STATEDIR}/zones_$$ ${STATEDIR}/zones + # + # 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 -I PREROUTING -i $interface -s $host -j $chain + fi + # + # Insert new rules into the input chains for the passed interface + # + while read z1 z2 chain; do + if [ "$z1" = "$zone" ]; then + if [ "$z2" = "$FW" ]; then + # + # We will insert the rule right after the DHCP, 'ping' and + # MAC rules (if any) + # + if list_search $interface $dhcp_interfaces; then + rulenum=3 + else + rulenum=2 + fi + + if list_search $interface $maclist_interfaces; then + rulenum=$(($rulenum + 1)) + fi + + if list_search $interface $tcpflags_interfaces; then + rulenum=$(($rulenum + 1)) + fi + + do_iptables -I `input_chain $interface` $rulenum -s $host -j $chain + else + # + # Insert rules into the passed interface's forward chain + # + # We insert them after any blacklist/MAC verification rules + # + source_chain=`forward_chain $interface` + eval dest_hosts=\"\$${z2}_hosts\" + + base=`chain_base $interface` + + eval rulenum=\$${base}_rulenum + + if [ -z "$rulenum" ]; then + if list_search $interface $blacklist_interfaces; then + rulenum=3 + else + rulenum=2 + fi + + if list_search $interface $maclist_interfaces; then + rulenum=$(($rulenum + 1)) + fi + + if list_search $interface $tcpflags_interfaces; then + rulenum=$(($rulenum + 1)) + fi + fi + + for h in $dest_hosts; do + iface=${h%:*} + hosts=${h#*:} + + if [ "$iface" != "$interface" -o "$hosts" != "$host" ]; then + do_iptables -I $source_chain $rulenum -s $host -o $iface -d $hosts -j $chain + rulenum=$(($rulenum + 1)) + fi + done + + eval ${base}_rulenum=$rulenum + + fi + elif [ "$z2" = "$zone" ]; then + if [ "$z1" = "$FW" ]; then + # + # Add a rule to the OUTPUT chain -- always after the icmp * ACCEPT rule + # + do_iptables -I OUTPUT `output_rule_num` -o $interface -d $host -j $chain + else + # + # Insert rules into the source interface's forward chain + # + # We insert them after any blacklist rules + # + eval source_hosts=\"\$${z1}_hosts\" + + for h in $source_hosts; do + iface=${h%:*} + hosts=${h#*:} + + base=`chain_base $iface` + + eval rulenum=\$${base}_rulenum + + if [ -z "$rulenum" ]; then + if list_search $iface $blacklist_interfaces; then + rulenum=3 + else + rulenum=2 + fi + fi + + if [ "$iface" != "$interface" -o "$hosts" != "$host" ]; then + do_iptables -I `forward_chain $iface` $rulenum -s $hosts -o $interface -d $host -j $chain + rulenum=$(($rulenum + 1)) + fi + + eval ${base}_rulenum=$rulenum + done + fi + fi + done < ${STATEDIR}/chains + + echo "$1 added to zone $2" +} + +# +# Delete a host or subnet from a zone +# +delete_from_zone() # $1 = [:] $2 = zone +{ + # + # Delete the subnect host(s) from the zone state file + # + delete_from_zones_file() + { + > ${STATEDIR}/zones_$$ + + while read z hosts; do + if [ "$z" = "$zone" ]; then + temp=$hosts + hosts= + + for h in $temp; do + if [ "$h" = "$delhost" ]; then + echo Yes + else + hosts="$hosts $h" + fi + done + fi + + echo "$z $hosts" >> ${STATEDIR}/zones_$$ + done < ${STATEDIR}/zones + + mv -f ${STATEDIR}/zones_$$ ${STATEDIR}/zones + } + # + # Isolate interface and host parts + # + interface=${1%:*} + host=${1#*:} + + [ -z "$host" ] && host="0.0.0.0/0" + # + # Load $zones + # + determine_zones + + zone=$2 + + validate_zone $zone || startup_error "Unknown zone: $zone" + + [ "$zone" = $FW ] && startup_error "Can't remove $1 from firewall zone" + # + # Be sure that Shorewall has been restarted using a DZ-aware version of the code + # + [ -f ${STATEDIR}/chains ] || startup_error "${STATEDIR}/chains -- file not found" + [ -f ${STATEDIR}/zones ] || startup_error "${STATEDIR}/zones -- file not found" + # + # Be sure that the interface was present at last [re]start + # + if ! chain_exists `input_chain $interface` ; then + startup_error "Unknown interface $interface" + fi + # + # Normalize the first argument to this function + # + delhost="$interface:$host" + # + # Delete the passed hosts from the zone state file + # + [ -z "`delete_from_zones_file`" ] && \ + error_message "Warning: $1 does not appear to be in zone $2" + # + # Construct the zone host maps + # + while read z hosts; do + eval ${z}_hosts=\"$hosts\" + done < ${STATEDIR}/zones + + terminator=fatal_error + # + # Delete any nat table entries for the host(s) + # + qt iptables -t nat -D PREROUTING -i $interface -s $host -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 `input_chain $interface` -s $host -j $chain + else + source_chain=`forward_chain $interface` + eval dest_hosts=\"\$${z2}_hosts\" + + for h in $dest_hosts $delhost; do + iface=${h%:*} + hosts=${h#*:} + + if [ "$iface" != "$interface" -o "$hosts" != "$host" ]; then + qt iptables -D $source_chain -s $host -o $iface -d $hosts -j $chain + fi + done + fi + elif [ "$z2" = "$zone" ]; then + if [ "$z1" = "$FW" ]; then + qt iptables -D OUTPUT -o $interface -d $host -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 + qt iptables -D `forward_chain $iface` -s $hosts -o $interface -d $host -j $chain + fi + done + fi + fi + done < ${STATEDIR}/chains + + echo "$1 removed from zone $2" +} + +# +# 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 + + PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin + # + # Establish termination function + # + terminator=startup_error + # + # Clear all configuration variables + # + version= + FW= + SUBSYSLOCK= + STATEDIR= + ALLOWRELATED=Yes + LOGRATE= + LOGBURST= + LOGPARMS= + NAT_ENABLED= + MANGLE_ENABLED= + ADD_IP_ALIASES= + ADD_SNAT_ALIASES= + TC_ENABLED= + LOGUNCLEAN= + BLACKLIST_DISPOSITION= + BLACKLIST_LOGLEVEL= + CLAMPMSS= + ROUTE_FILTER= + NAT_BEFORE_RULES= + MULTIPORT= + DETECT_DNAT_IPADDRS= + MUTEX_TIMEOUT= + NEWNOTSYN= + LOGNEWNOTSYN= + FORWARDPING= + MACLIST_DISPOSITION= + MACLIST_LOG_LEVEL= + TCP_FLAGS_DISPOSITION= + TCP_FLAGS_LOG_LEVEL= + RFC1918_LOG_LEVEL= + MARK_IN_FORWARD_CHAIN= + SHARED_DIR=/usr/share/shorewall + FUNCTIONS= + VERSION_FILE= + + stopping= + have_mutex= + masq_seq=1 + nonat_seq=1 + aliases_to_add= + + TMP_DIR=/tmp/shorewall-$$ + rm -rf $TMP_DIR + mkdir -p $TMP_DIR && chmod 700 $TMP_DIR || \ + startup_error "Can't create $TMP_DIR" + + trap "rm -rf $TMP_DIR; my_mutex_off; exit 2" 1 2 3 4 5 6 9 + + FUNCTIONS=$SHARED_DIR/functions + + if [ -f $FUNCTIONS ]; then + . $FUNCTIONS + else + startup_error "$FUNCTIONS does not exist!" + fi + + VERSION_FILE=$SHARED_DIR/version + + [ -f $VERSION_FILE ] && version=`cat $VERSION_FILE` + + run_user_exit params + + config=`find_file shorewall.conf` + + if [ -f $config ]; then + echo "Processing $config..." + . $config + else + echo "$config does not exist!" >&2 + exit 2 + fi + + [ -z "${STATEDIR}" ] && STATEDIR=/var/state/shorewall + + [ -d $STATEDIR ] || mkdir -p $STATEDIR + + [ -z "$FW" ] && FW=fw + + ALLOWRELATED="`added_param_value_yes ALLOWRELATED $ALLOWRELATED`" + [ -n "$ALLOWRELATED" ] || \ + startup_error "ALLOWRELATED=No is not supported" + NAT_ENABLED="`added_param_value_yes NAT_ENABLED $NAT_ENABLED`" + MANGLE_ENABLED="`added_param_value_yes MANGLE_ENABLED $MANGLE_ENABLED`" + ADD_IP_ALIASES="`added_param_value_yes ADD_IP_ALIASES $ADD_IP_ALIASES`" + TC_ENABLED="`added_param_value_yes TC_ENABLED $TC_ENABLED`" + + if [ -n "${LOGRATE}${LOGBURST}" ]; then + LOGPARMS="--match limit" + [ -n "$LOGRATE" ] && LOGPARMS="$LOGPARMS --limit $LOGRATE" + [ -n "$LOGBURST" ] && LOGPARMS="$LOGPARMS --limit-burst $LOGBURST" + fi + + 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 + + if [ -n "$TC_ENABLED" -a -z "$MANGLE_ENABLED" ]; then + startup_error "Traffic Control requires Mangle" + fi + + [ -z "$BLACKLIST_DISPOSITION" ] && BLACKLIST_DISPOSITION=DROP + + CLAMPMSS=`added_param_value_no CLAMPMSS $CLAMPMSS` + ADD_SNAT_ALIASES=`added_param_value_no ADD_SNAT_ALIASES $ADD_SNAT_ALIASES` + ROUTE_FILTER=`added_param_value_no ROUTE_FILTER $ROUTE_FILTER` + NAT_BEFORE_RULES=`added_param_value_yes NAT_BEFORE_RULES $NAT_BEFORE_RULES` + MULTIPORT=`added_param_value_no MULTIPORT $MULTIPORT` + 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" + + NEWNOTSYN=`added_param_value_yes NEWNOTSYN $NEWNOTSYN` + + maclist_target=reject + + if [ -n "$MACLIST_DISPOSITION" ] ; then + case $MACLIST_DISPOSITION in + REJECT) + ;; + ACCEPT|DROP) + maclist_target=$MACLIST_DISPOSITION + ;; + *) + startup_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) + ;; + *) + startup_error "Invalid value ($TCP_FLAGS_DISPOSITION) for TCP_FLAGS_DISPOSITION" + ;; + esac + else + TCP_FLAGS_DISPOSITION=DROP + fi + + [ -z "$RFC1918_LOG_LEVEL" ] && 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 + if [ -n "$TC_ENABLED" ]; then + CLEAR_TC=`added_param_value_yes CLEAR_TC $CLEAR_TC` + else + CLEAR_TC= + fi + + # + # Strip the files that we use often + # + strip_file interfaces + strip_file hosts +} + +# +# Give Usage Information +# +usage() { + echo "Usage: $0 [debug] {start|stop|reset|restart|status|refresh|clear|{add|delete} [:hosts] zone}}" + exit 1 +} + +# +# E X E C U T I O N B E G I N S H E R E +# +# +# Start trace if first arg is "debug" +# +[ $# -gt 1 ] && [ "$1" = "debug" ] && { set -x ; shift ; } + +nolock= + +[ $# -gt 1 ] && [ "$1" = "nolock" ] && { nolock=Yes; shift ; } + +trap "my_mutex_off; exit 2" 1 2 3 4 5 6 9 + +command="$1" + +case "$command" in + stop) + [ $# -ne 1 ] && usage + do_initialize + my_mutex_on + echo -n "Stopping Shorewall..." + stop_firewall + [ -n "$SUBSYSLOCK" ] && rm -f $SUBSYSLOCK + echo "done." + my_mutex_off + ;; + + start) + [ $# -ne 1 ] && usage + do_initialize + my_mutex_on + if qt iptables -L shorewall -n ; then + [ -n "$SUBSYSLOCK" ] && touch $SUBSYSLOCK + echo "Shorewall Already Started" + [ -n "$TMP_DIR" ] && rm -rf $TMP_DIR + my_mutex_off + exit 0; + fi + define_firewall "Start" && [ -n "$SUBSYSLOCK" ] && touch $SUBSYSLOCK + my_mutex_off + ;; + + restart) + [ $# -ne 1 ] && usage + do_initialize + my_mutex_on + if qt iptables -L shorewall -n ; then + define_firewall "Restart" + else + echo "Shorewall Not Currently Running" + define_firewall "Start" + fi + + [ $? -eq 0 ] && [ -n "$SUBSYSLOCK" ] && touch $SUBSYSLOCK + my_mutex_off + ;; + + status) + [ $# -ne 1 ] && usage + echo "Shorewall-$version Status at $HOSTNAME - `date`" + echo + iptables -L -n -v + ;; + + reset) + [ $# -ne 1 ] && usage + do_initialize + my_mutex_on + if ! qt iptables -L shorewall -n ; then + echo "Shorewall Not Started" + [ -n "$TMP_DIR" ] && rm -rf $TMP_DIR + my_mutex_off + exit 2; + fi + iptables -Z + iptables -t nat -Z + iptables -t mangle -Z + report "Shorewall Counters Reset" + date > $STATEDIR/restarted + my_mutex_off + ;; + + refresh) + [ $# -ne 1 ] && usage + do_initialize + my_mutex_on + if ! qt iptables -L shorewall -n ; then + echo "Shorewall Not Started" + [ -n "$TMP_DIR" ] && rm -rf $TMP_DIR + my_mutex_off + exit 2; + fi + refresh_firewall; + my_mutex_off + ;; + + clear) + [ $# -ne 1 ] && usage + do_initialize + my_mutex_on + echo -n "Clearing Shorewall..." + clear_firewall + [ -n "$SUBSYSLOCK" ] && rm -f $SUBSYSLOCK + echo "done." + my_mutex_off + ;; + + check) + [ $# -ne 1 ] && usage + do_initialize + check_config + ;; + + add) + [ $# -ne 3 ] && usage + do_initialize + my_mutex_on + if ! qt iptables -L shorewall -n ; then + echo "Shorewall Not Started" + [ -n "$TMP_DIR" ] && rm -rf $TMP_DIR + my_mutex_off + exit 2; + fi + add_to_zone $2 $3 + my_mutex_off + ;; + + delete) + [ $# -ne 3 ] && usage + do_initialize + my_mutex_on + if ! qt iptables -L shorewall -n ; then + echo "Shorewall Not Started" + [ -n "$TMP_DIR" ] && rm -rf $TMP_DIR + my_mutex_off + exit 2; + fi + delete_from_zone $2 $3 + my_mutex_off + ;; + + *) + usage + ;; + +esac diff --git a/Lrp/usr/share/shorewall/functions b/Lrp/usr/share/shorewall/functions new file mode 100644 index 000000000..c14490ba6 --- /dev/null +++ b/Lrp/usr/share/shorewall/functions @@ -0,0 +1,199 @@ +#!/bin/sh +# +# Shorewall 1.4 -- /usr/lib/shorewall/functions + +# +# Suppress all output for a command +# +qt() +{ + "$@" >/dev/null 2>&1 +} + +# +# Find a File -- Look first in $SHOREWALL_DIR then in /etc/shorewall +# +find_file() +{ + if [ -n "$SHOREWALL_DIR" -a -f $SHOREWALL_DIR/$1 ]; then + echo $SHOREWALL_DIR/$1 + else + echo /etc/shorewall/$1 + fi +} + +# +# Replace commas with spaces and echo the result +# +separate_list() { + local list + local part + local newlist + # + # 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 + # + case "$@" in + *,|,*|*,,*|*[[:space:]]*) + [ -n "$terminator" ] && \ + $terminator "Invalid comma-separated list \"$@\"" + echo "Warning -- invalid comma-separated list \"$@\"" >&2 + ;; + esac + + list="$@" + part="${list%%,*}" + newlist="$part" + + while [ "x$part" != "x$list" ]; do + list="${list#*,}"; + part="${list%%,*}"; + newlist="$newlist $part"; + done + + echo "$newlist" +} + +# +# Find the zones +# +find_zones() # $1 = name of the zone file +{ + while read zone display comments; do + [ -n "$zone" ] && case "$zone" in + \#*) + ;; + $FW|multi) + echo "Reserved zone name \"$zone\" in zones file ignored" >&2 + ;; + *) + echo $zone + ;; + esac + done < $1 +} + +find_display() # $1 = zone, $2 = name of the zone file +{ + grep ^$1 $2 | while read z display comments; do + [ "x$1" = "x$z" ] && echo $display + done +} + +determine_zones() +{ + local zonefile=`find_file zones` + + multi_display=Multi-zone + + if [ -f $zonefile ]; then + zones=`find_zones $zonefile` + zones=`echo $zones` # Remove extra trash + + for zone in $zones; do + dsply=`find_display $zone $zonefile` + eval ${zone}_display=\$dsply + done + else + zones="net local dmz gw" + net_display=Net + local_display=Local + dmz_display=DMZ + gw_display=Gateway + fi + +} + +# +# The following functions may be used by apps that wish to ensure that +# the state of Shorewall isn't changing +# +# This function loads the STATEDIR variable (directory where Shorewall is to +# store state files). If your application supports alternate Shorewall +# configurations then the name of the alternate configuration directory should +# be in $SHOREWALL_DIR at the time of the call. +# +# If the shorewall.conf file does not exist, this function does not return +# +get_statedir() +{ + MUTEX_TIMEOUT= + + local config=`find_file shorewall.conf` + + if [ -f $config ]; then + . $config + else + echo "/etc/shorewall/shorewall.conf does not exist!" >&2 + exit 2 + fi + + [ -z "${STATEDIR}" ] && STATEDIR=/var/state/shorewall +} + +# +# Call this function to assert MUTEX with Shorewall. If you invoke the +# /sbin/shorewall program while holding MUTEX, 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=$STATEDIR/lock + + MUTEX_TIMEOUT=${MUTEX_TIMEOUT:-60} + + if [ $MUTEX_TIMEOUT -gt 0 ]; then + + [ -d $STATEDIR ] || mkdir -p $STATEDIR + + if qt which 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 MUTEX +# +mutex_off() +{ + rm -f $STATEDIR/lock +} + +# +# Strip comments and blank lines from a file and place the result in the +# temporary directory +# +strip_file() # $1 = Base Name of the file, $2 = Full Name of File (optional) +{ + local fname + + [ $# = 1 ] && fname=`find_file $1` || fname=$2 + + if [ -f $fname ]; then + cut -d'#' -f1 $fname | grep -v '^[[:space:]]*$' > $TMP_DIR/$1 + else + > $TMP_DIR/$1 + fi +} diff --git a/Lrp/usr/share/shorewall/version b/Lrp/usr/share/shorewall/version new file mode 100644 index 000000000..88c5fb891 --- /dev/null +++ b/Lrp/usr/share/shorewall/version @@ -0,0 +1 @@ +1.4.0