#!/bin/sh # # Shorewall6 4.2 -- /usr/share/shorewall6/lib.cli. # # This program is under GPL [http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt] # # (c) 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008 - Tom Eastep (teastep@shorewall.net) # # Complete documentation is available at http://shorewall.net # # This program is free software; you can redistribute it and/or modify # it under the terms of Version 2 of the GNU General Public License # as published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # This library contains the command processing code common to /sbin/shorewall6 and # /sbin/shorewall6-lite. # # # Fatal Error # fatal_error() # $@ = Message { echo " $@" >&2 exit 2 } # # Display a chain if it exists # showfirstchain() # $1 = name of chain { awk \ 'BEGIN {prnt=0; rslt=1; }; \ /^$/ { next; };\ /^Chain/ {if ( prnt == 1 ) { rslt=0; exit 0; }; };\ /Chain '$1'/ { prnt=1; }; \ { if (prnt == 1) print; };\ END { exit rslt; }' $TMPFILE } showchain() # $1 = name of chain { if [ "$firstchain" = "Yes" ]; then if showfirstchain $1; then firstchain= fi else awk \ 'BEGIN {prnt=0;};\ /^$|^ pkts/ { next; };\ /^Chain/ {if ( prnt == 1 ) exit; };\ /Chain '$1'/ { prnt=1; };\ { if (prnt == 1) print; }' $TMPFILE fi } # # Validate the value of RESTOREFILE # validate_restorefile() # $* = label { case $RESTOREFILE in */*) error_message "ERROR: $@ must specify a simple file name: $RESTOREFILE" exit 2 ;; .safe|.try) ;; .*|NONE) error_message "ERROR: Reserved File Name: $RESTOREFILE" exit 2 ;; esac } # # Clear descriptor 1 if it is a terminal # clear_term() { [ -t 1 ] && clear } # # Delay $timeout seconds -- if we're running on a recent bash2 then allow # to terminate the delay # timed_read () { read -t $timeout foo 2> /dev/null test $? -eq 2 && sleep $timeout } # # Determine if 'syslog -C' is running # syslog_circular_buffer() { local pid local tty local flags local cputime local path local args local arg ps ax 2> /dev/null | while read pid tty flags cputime path args; do case $path in syslogd|*/syslogd) for arg in $args; do if [ x$arg = x-C ]; then echo Yes return fi done ;; esac done } # # Display the last $1 packets logged # packet_log() # $1 = number of messages { if [ -n "$g_showmacs" -o $VERBOSITY -gt 2 ]; then $g_logread | grep 'IN=.* OUT=.*SRC=.*:.*DST=' | head -n$1 | tac | sed -r 's/ kernel://; s/\[.*\] //; s/0000:/:/g; s/:::+/::/g; s/:0+/:/g' | sed s/" $host $LOGFORMAT"/" "/ else $g_logread | grep 'IN=.* OUT=.*SRC=.*:.*DST=' | head -n$1 | tac | sed -r 's/ kernel://; s/MAC=.* SRC=/SRC=/; s/\[.*\] //; s/0000:/:/g; s/:::+/::/g; s/:0+/:/g' | sed s/" $host $LOGFORMAT"/" "/ fi } search_log() # $1 = IP address to search for { if [ -n "$g_showmacs" -o $VERBOSITY -gt 2 ]; then $g_logread | grep 'IN=.* OUT=.*SRC=.*\..*DST=' | grep "$1" | tac | sed -r 's/ kernel://; s/\[.*\] //; s/0000:/:/g; s/:::+/::/g; s/:0+/:/g' | sed s/" $host $LOGFORMAT"/" "/ else $g_logread | grep 'IN=.* OUT=.*SRC=.*\..*DST=' | grep "$1" | tac | sed -r 's/ kernel://; s/MAC=.* SRC=/SRC=/; s/\[.*\] //; s/0000:/:/g; s/:::+/::/g; s/:0+/:/g' | sed s/" $host $LOGFORMAT"/" "/ fi } # # Show traffic control information # show_tc() { show_one_tc() { local device device=${1%@*} qdisc=$(tc qdisc list dev $device) if [ -n "$qdisc" ]; then echo Device $device: tc -s -d qdisc show dev $device echo tc -s -d class show dev $device echo fi } if [ $# -gt 0 ]; then show_one_tc $1 else ip -o link list | while read inx interface details; do show_one_tc ${interface%:} done fi } # # Show classifier information # show_classifiers() { show_one_classifier() { local device device=${1%@*} qdisc=$(tc qdisc list dev $device) if [ -n "$qdisc" ]; then echo Device $device: tc -s filter ls dev $device echo fi } ip -o link list | while read inx interface details; do show_one_classifier ${interface%:} done } # # Watch the Firewall Log # logwatch() # $1 = timeout -- if negative, prompt each time that # an 'interesting' packet count changes { if [ -z "$LOGFILE" ]; then LOGFILE=/var/log/messages if [ -n "$(syslog_circular_buffer)" ]; then g_logread="logread | tac" elif [ -r $LOGFILE ]; then g_logread="tac $LOGFILE" else echo "LOGFILE ($LOGFILE) does not exist!" >&2 exit 2 fi fi host=$(echo $g_hostname | sed 's/\..*$//') oldrejects=$($IP6TABLES -L -v -n | grep 'LOG') if [ $1 -lt 0 ]; then timeout=$((- $1)) pause="Yes" else pause="No" timeout=$1 fi qt mywhich awk && haveawk=Yes || haveawk= while true; do clear_term echo "$banner $(date)" echo echo "Dropped/Rejected Packet Log ($LOGFILE)" echo show_reset rejects=$($IP6TABLES -L -v -n | grep 'LOG') if [ "$rejects" != "$oldrejects" ]; then oldrejects="$rejects" $g_ring_bell packet_log 40 if [ "$pause" = "Yes" ]; then echo echo $g_echo_n 'Enter any character to continue: ' read foo else timed_read fi else echo packet_log 40 timed_read fi done } # # Save currently running configuration # do_save() { local status status=0 if [ -f ${VARDIR}/firewall ]; then if $iptables_save > ${VARDIR}/restore-$$; then cp -f ${VARDIR}/firewall $g_restorepath mv -f ${VARDIR}/restore-$$ ${g_restorepath}-iptables chmod +x $g_restorepath echo " Currently-running Configuration Saved to $g_restorepath" run_user_exit save else rm -f ${VARDIR}/restore-$$ echo " ERROR: Currently-running Configuration Not Saved" >&2 status=1 fi else echo " ERROR: ${VARDIR}/firewall does not exist" >&2 status=1 fi case ${SAVE_IPSETS:=No} in [Yy]es) case ${IPSET:=ipset} in */*) if [ ! -x "$IPSET" ]; then error_message "ERROR: IPSET=$IPSET does not exist or is not executable - ipsets are not saved" IPSET= fi ;; *) IPSET="$(mywhich $IPSET)" [ -n "$IPSET" ] || error_message "ERROR: The ipset utility cannot be located - ipsets are not saved" ;; esac if [ -n "$IPSET" ]; then if [ -f /etc/debian_version ] && [ $(cat /etc/debian_version) = 5.0.3 ]; then # # The 'grep -v' is a hack for a bug in ipset's nethash implementation when xtables-addons is applied to Lenny # hack='| grep -v /31' else hack= fi if eval $IPSET -S $hack > ${VARDIR}/ipsets.tmp; then # # Don't save an 'empty' file # grep -q '^-N' ${VARDIR}/ipsets.tmp && mv -f ${VARDIR}/ipsets.tmp ${g_restorepath}-ipsets fi fi ;; [Nn]o) ;; *) error_message "WARNING: Invalid value ($SAVE_IPSETS) for SAVE_IPSETS" ;; esac return $status } save_config() { local result result=1 iptables_save=${IP6TABLES}-save [ -x $iptables_save ] || echo "$iptables_save does not exist or is not executable" >&2 if shorewall6_is_started ; then [ -d ${VARDIR} ] || mkdir -p ${VARDIR} if [ -f $g_restorepath -a ! -x $g_restorepath ]; then echo " ERROR: $g_restorepath exists and is not a saved $g_product configuration" >&2 else case $RESTOREFILE in capabilities|chains|default_route|firewall|firewall.conf|nat|proxyarp|restarted|rt_tables|save|state|undo_routing|zones) echo " ERROR: Reserved file name: $RESTOREFILE" >&2 ;; *) validate_restorefile RESTOREFILE if chain_exists dynamic; then if $IP6TABLES -L dynamic -n > ${VARDIR}/save; then echo " Dynamic Rules Saved" do_save else echo "Error Saving the Dynamic Rules" >&2 fi else do_save && rm -f ${VARDIR}/save fi ;; esac fi else echo "Shorewall6 isn't started" >&2 fi return 0 } # # Show routing configuration # show_routing() { if [ -n "$(ip -6 rule list)" ]; then heading "Routing Rules" ip -6 rule list ip -6 rule list | while read rule; do echo ${rule##* } done | sort -u | while read table; do heading "Table $table:" ip -6 route list table $table done if [ -n "$g_routecache" ]; then heading "Route Cache" ip -6 route list cache fi else heading "Routing Table" ip -6 route list fi } # # 'list dynamic' command executor # list_zone() { local sets local setname [ -n "$(mywhich ipset)" ] || fatal_error "The ipset utility cannot be located" sets=$(ipset -L -n | grep "^6_$1_") for setname in $sets; do echo "${setname#${1}_}:" ipset -L $setname | awk 'BEGIN {prnt=0;}; \ /^Members:/ {prnt=1; next; }; \ /^Bindings:/ {prnt=0; }; \ { if (prnt == 1) print " ", $1; };' done } # # Show Filter - For Shorewall6-lite, if there was an scfilter file at compile-time, # then the compiler generated another version of this function and # embedded it in the firewall.conf file. That version supersedes this # one. # show_connections_filter() { local filter local command local first command=${SHOREWALL_SHELL} filter=$(find_file scfilter) if [ -f $filter ]; then first=$(head -n1 $filter) case $first in \#!*) command=${first#\#!} ;; esac $command $filter else cat - fi } # # Show Command Executor # show_command() { local finished finished=0 local table table=filter local table_given table_given= show_macro() { foo=`grep 'This macro' $macro | sed 's/This macro //'` if [ -n "$foo" ]; then macro=${macro#*.} foo=${foo%.*} if [ ${#macro} -gt 10 ]; then echo " $macro ${foo#\#}" else $g_echo_e " $macro \t${foo#\#}" fi fi } while [ $finished -eq 0 -a $# -gt 0 ]; do option=$1 case $option in -*) option=${option#-} while [ -n "$option" ]; do case $option in -) finished=1 option= ;; v*) VERBOSITY=$(($VERBOSITY + 1 )) option=${option#v} ;; x*) g_ipt_options="-xnv" option=${option#x} ;; m*) g_showmacs=Yes option=${option#m} ;; f*) g_filemode=Yes option=${option#f} ;; t) [ $# -eq 1 ] && usage 1 case $2 in mangle|nat|filter|raw) table=$2 table_given=Yes ;; *) fatal_error "Invalid table name ($s)" ;; esac option= shift ;; l*) g_ipt_options1="--line-numbers" option=${option#l} ;; c*) g_routecache=Yes option=${option#c} ;; *) usage 1 ;; esac done shift ;; *) finished=1 ;; esac done g_ipt_options="$g_ipt_options $g_ipt_options1" [ -n "$g_debugging" ] && set -x case "$1" in connections) [ $# -gt 1 ] && usage 1 if qt mywhich conntrack ; then echo "$g_product $SHOREWALL_VERSION Connections at $g_hostname - $(date)" echo conntrack -f ipv6 -L | show_connections_filter else local count=$(cat /proc/sys/net/netfilter/nf_conntrack_count) local max=$(cat /proc/sys/net/netfilter/nf_conntrack_max) echo "$g_product $SHOREWALL_VERSION Connections ($count of $max) at $g_hostname - $(date)" echo grep '^ipv6' /proc/net/nf_conntrack | sed -r 's/0000:/:/g; s/:::+/::/g; s/:0+/:/g' | show_connections_filter fi ;; tos|mangle) [ $# -gt 1 ] && usage 1 echo "$g_product $SHOREWALL_VERSION Mangle Table at $g_hostname - $(date)" echo show_reset $IP6TABLES -t mangle -L $g_ipt_options ;; raw) [ $# -gt 1 ] && usage 1 echo "$g_product $SHOREWALL_VERSION raw Table at $g_hostname - $(date)" echo show_reset $IP6TABLES -t raw -L $g_ipt_options ;; log) [ $# -gt 2 ] && usage 1 if [ -z "$LOGFILE" ]; then LOGFILE=/var/log/messages if [ -n "$(syslog_circular_buffer)" ]; then g_logread="logread | tac" elif [ -r $LOGFILE ]; then g_logread="tac $LOGFILE" else echo "LOGFILE ($LOGFILE) does not exist!" >&2 exit 2 fi fi echo "$g_product $SHOREWALL_VERSION Log ($LOGFILE) at $g_hostname - $(date)" echo show_reset host=$(echo $g_hostname | sed 's/\..*$//') if [ $# -eq 2 ]; then search_log $2 else packet_log 20 fi ;; tc) [ $# -gt 2 ] && usage 1 echo "$g_product $SHOREWALL_VERSION Traffic Control at $g_hostname - $(date)" echo shift if [ -z "$1" ]; then $IP6TABLES -t mangle -L -n -v echo fi show_tc $1 ;; classifiers|filters) [ $# -gt 1 ] && usage 1 echo "$g_product $SHOREWALL_VERSION Classifiers at $g_hostname - $(date)" echo show_classifiers ;; zones) [ $# -gt 1 ] && usage 1 if [ -f ${VARDIR}/zones ]; then echo "$g_product $SHOREWALL_VERSION Zones at $g_hostname - $(date)" echo while read zone type hosts; do echo "$zone ($type)" for host in $hosts; do case $host in exclude) echo " exclude:" ;; *) echo " $host" ;; esac done done < ${VARDIR}/zones echo else echo " ERROR: ${VARDIR}/zones does not exist" >&2 exit 1 fi ;; capabilities) [ $# -gt 1 ] && usage 1 determine_capabilities VERBOSITY=2 if [ -n "$g_filemode" ]; then report_capabilities1 else report_capabilities fi ;; ip) [ $# -gt 1 ] && usage 1 echo "$g_product $SHOREWALL_VERSION IP at $g_hostname - $(date)" echo ip -6 addr list ;; routing) [ $# -gt 1 ] && usage 1 echo "$g_product $SHOREWALL_VERSION Routing at $g_hostname - $(date)" echo show_routing ;; config) . ${SHAREDIR}/configpath if [ -n "$g_filemode" ]; then echo "CONFIG_PATH=$CONFIG_PATH" echo "VARDIR=$VARDIR" echo "LIBEXEC=$g_libexec" [ -n "$LITEDIR" ] && echo "LITEDIR=$LITEDIR" else echo "Default CONFIG_PATH is $CONFIG_PATH" echo "Default VARDIR is $VARDIR" echo "LIBEXEC is $g_libexec" [ -n "$LITEDIR" ] && echo "LITEDIR is $LITEDIR" fi ;; chain) shift echo "$g_product $SHOREWALL_VERSION $([ $# -gt 1 ] && echo "Chains " || [ $# -gt 0 ] && echo "Chain " || echo $table Table)$* at $g_hostname - $(date)" echo show_reset if [ $# -gt 0 ]; then for chain in $*; do $IP6TABLES -t $table -L $chain $g_ipt_options done else $IP6TABLES -t $table -L $g_ipt_options fi ;; vardir) echo $VARDIR; ;; policies) [ $# -gt 1 ] && usage 1 echo "$g_product $SHOREWALL_VERSION Policies at $g_hostname - $(date)" echo [ -f ${VARDIR}/policies ] && cat ${VARDIR}/policies; ;; *) if [ "$g_product" = Shorewall6 ]; then case $1 in actions) [ $# -gt 1 ] && usage 1 echo "A_ACCEPT # Audit and accept the connection" echo "A_DROP # Audit and drop the connection" echo "A_REJECT # Audit and reject the connection " echo "allowBcast # Silently Allow Broadcast/multicast" echo "allowInvalid # Accept packets that are in the INVALID conntrack state." echo "allowinUPnP # Allow UPnP inbound (to firewall) traffic" echo "allowoutUPnP # Allow traffic from local command 'upnpd' (does not work with kernels after 2.6.13)" echo "dropBcast # Silently Drop Broadcast/multicast" echo "dropInvalid # Silently Drop packets that are in the INVALID conntrack state" echo "dropNotSyn # Silently Drop Non-syn TCP packets" echo "rejNotSyn # Silently Reject Non-syn TCP packets" if [ -f ${CONFDIR}/actions ]; then cat ${SHAREDIR}/actions.std ${CONFDIR}/actions | grep -Ev '^\#|^$' else grep -Ev '^\#|^$' ${SHAREDIR}/actions.std fi return ;; macro) [ $# -ne 2 ] && usage 1 for directory in $(split $CONFIG_PATH); do if [ -f ${directory}/macro.$2 ]; then echo "Shorewall6 $SHOREWALL_VERSION Macro $2 at $g_hostname - $(date)" cat ${directory}/macro.$2 return fi done echo " WARNING: Macro $2 not found" >&2 return ;; macros) [ $# -gt 1 ] && usage 1 for directory in $(split $CONFIG_PATH); do temp= for macro in ${directory}/macro.*; do case $macro in *\*) ;; *) if [ -z "$temp" ]; then echo echo "Macros in $directory:" echo temp=Yes fi show_macro ;; esac done done return ;; esac fi if [ $# -gt 0 ]; then if [ $1 = dynamic -a $# -gt 1 ]; then shift [ $# -eq 1 ] || usage 1 list_zone $1 return; fi [ -n "$table_given" ] || for chain in $*; do if ! qt $IP6TABLES -t $table -L $chain $g_ipt_options; then error_message "ERROR: Chain '$chain' is not recognized by $IP6TABLES." exit 1 fi done echo "$g_product $SHOREWALL_VERSION $([ $# -gt 1 ] && echo "Chains " || echo "Chain ")$* at $g_hostname - $(date)" echo show_reset for chain in $*; do $IP6TABLES -t $table -L $chain $g_ipt_options done else echo "$g_product $SHOREWALL_VERSION $table Table at $g_hostname - $(date)" echo show_reset $IP6TABLES -t $table -L $g_ipt_options fi ;; esac } # # Dump Filter - For Shorewall-lite, if there was a dumpfilter file at compile-time, # then the compiler generated another version of this function and # embedded it in the firewall.conf file. That version supersedes this # one. # dump_filter() { local filter local command local first command=${SHOREWALL_SHELL} filter=$(find_file dumpfilter) if [ -f $filter ]; then first=$(head -n1 $filter) case $first in \#!*) command=${first#\#!} ;; esac $command $filter else cat - fi } # # Dump Command Executor # do_dump_command() { local finished finished=0 while [ $finished -eq 0 -a $# -gt 0 ]; do option=$1 case $option in -*) option=${option#-} while [ -n "$option" ]; do case $option in -) finished=1 option= ;; x*) g_ipt_options="-xnv" option=${option#x} ;; m*) g_showmacs=Yes option=${option#m} ;; l*) g_ipt_options1="--line-numbers" option=${option#l} ;; c*) g_routecache=Yes option=${option#c} ;; *) usage 1 ;; esac done shift ;; *) finished=1 ;; esac done if [ -z "$LOGFILE" ]; then LOGFILE=/var/log/messages if [ -n "$(syslog_circular_buffer)" ]; then g_logread="logread | tac" elif [ -r $LOGFILE ]; then g_logread="tac $LOGFILE" else echo "LOGFILE ($LOGFILE) does not exist!" >&2 exit 2 fi fi g_ipt_options="$g_ipt_options $g_ipt_options1" [ $VERBOSITY -lt 2 ] && VERBOSITY=2 [ -n "$g_debugging" ] && set -x [ $# -eq 0 ] || usage 1 clear_term echo "$g_product $SHOREWALL_VERSION Dump at $g_hostname - $(date)" echo if [ -f /usr/share/shorewall/version ]; then echo " Shorewall $(cat /usr/share/shorewall/version)" echo fi show_reset host=$(echo $g_hostname | sed 's/\..*$//') $IP6TABLES -L $g_ipt_options heading "Log ($LOGFILE)" packet_log 20 if qt $IP6TABLES -t mangle -L -n; then heading "Mangle Table" $IP6TABLES -t mangle -L $g_ipt_options fi if qt $IP6TABLES -t raw -L -n; then heading "Raw Table" $IP6TABLES -t raw -L $g_ipt_options fi local count=$(cat /proc/sys/net/netfilter/nf_conntrack_count) local max=$(cat /proc/sys/net/netfilter/nf_conntrack_max) heading "Conntrack Table ($count out of $max)" grep '^ipv6' /proc/net/nf_conntrack heading "IP Configuration" ip -6 addr list heading "IP Stats" ip -stat link list if qt mywhich brctl; then heading "Bridges" brctl show fi if qt mywhich setkey; then heading "PFKEY SPD" setkey -DP heading "PFKEY SAD" setkey -D | grep -Ev '^[[:space:]](A:|E:)' # Don't divulge the keys fi heading "/proc" show_proc /proc/version for directory in /proc/sys/net/ipv6/conf/*; do for file in forwarding proxy_ra proxy_ndp; do show_proc $directory/$file done done show_routing heading "Neighbors" ip -6 neigh ls if qt mywhich lsmod; then heading "Modules" lsmod | grep -E '^(x_|ip6|nf_|xt_)' | sort fi determine_capabilities echo report_capabilities echo netstat -6tunap if [ -n "$TC_ENABLED" ]; then heading "Traffic Control" show_tc heading "TC Filters" show_classifiers fi } dump_command() { do_dump_command | dump_filter } # # Restore Comand Executor # restore_command() { local finished finished=0 while [ $finished -eq 0 -a $# -gt 0 ]; do option=$1 case $option in -*) option=${option#-} while [ -n "$option" ]; do case $option in -) finished=1 option= ;; n*) g_noroutes=Yes option=${option#n} ;; *) usage 1 ;; esac done shift ;; *) finished=1 ;; esac done case $# in 0) ;; 1) RESTOREFILE="$1" validate_restorefile '' ;; *) usage 1 ;; esac if [ -z "$STARTUP_ENABLED" ]; then error_message "ERROR: Startup is disabled" exit 2 fi g_restorepath=${VARDIR}/$RESTOREFILE [ -n "$nolock" ] || mutex_on if [ -x $g_restorepath ]; then if [ -x ${g_restorepath}-ipsets ] ; then echo Restoring Ipsets... $IP6TABLES -F $IP6TABLES -X $SHOREWALL_SHELL ${g_restorepath}-ipsets fi progress_message3 "Restoring Shorewall6..." run_it $g_restorepath restore && progress_message3 "$g_product restored from ${VARDIR}/$RESTOREFILE" [ -n "$nolock" ] || mutex_off else echo "File $g_restorepath: file not found" [ -n "$nolock" ] || mutex_off exit 2 fi } # # Display the time that the counters were last reset # show_reset() { [ -f ${VARDIR}/restarted ] && \ echo "Counters reset $(cat ${VARDIR}/restarted)" && \ echo } # # Display's the passed file name followed by "=" and the file's contents. # show_proc() # $1 = name of a file { [ -f $1 ] && echo " $1 = $(cat $1)" } read_yesno_with_timeout() { read -t 60 yn 2> /dev/null if [ $? -eq 2 ] then # read doesn't support timeout test -x /bin/bash || return 2 # bash is not installed so the feature is not available /bin/bash -c 'read -t 60 yn ; if [ "$yn" == "y" ] ; then exit 0 ; else exit 1 ; fi' # invoke bash and use its version of read return $? else # read supports timeout case "$yn" in y|Y) return 0 ;; *) return 1 ;; esac fi } # # Print a heading with leading and trailing black lines # heading() { echo echo "$@" echo } # # Create the appropriate -q option to pass onward # make_verbose() { local v v=$g_verbose_offset local option option=- if [ -n "$g_use_verbosity" ]; then echo "-v$g_use_verbosity" elif [ $g_verbose_offset -gt 0 ]; then while [ $v -gt 0 ]; do option="${option}v" v=$(($v - 1)) done echo $option elif [ $g_verbose_offset -lt 0 ]; then while [ $v -lt 0 ]; do option="${option}q" v=$(($v + 1)) done echo $option fi } # # Executor for drop,reject,... commands # block() # $1 = command, $2 = Finished, $3 - $n addresses { local chain chain=$1 local finished finished=$2 local which which='-s' local range range='--src-range' if ! chain_exists dynamic; then echo "Dynamic blacklisting is not enabled in the current $g_product configuration" >&2 [ -n "$nolock" ] || mutex_off exit 2 fi shift 3 while [ $# -gt 0 ]; do case $1 in from) which='-s' range='--src-range' shift continue ;; to) which='-d' range='--dst-range' shift continue ;; *-*) qt $IP6TABLES -D dynamic -m iprange $range $1 -j reject qt $IP6TABLES -D dynamic -m iprange $range $1 -j DROP qt $IP6TABLES -D dynamic -m iprange $range $1 -j logreject qt $IP6TABLES -D dynamic -m iprange $range $1 -j logdrop $IP6TABLES -A dynamic -m iprange $range $1 -j $chain || break 1 ;; *) qt $IP6TABLES -D dynamic $which $1 -j reject qt $IP6TABLES -D dynamic $which $1 -j DROP qt $IP6TABLES -D dynamic $which $1 -j logreject qt $IP6TABLES -D dynamic $which $1 -j logdrop $IP6TABLES -A dynamic $which $1 -j $chain || break 1 ;; esac echo "$1 $finished" shift done } # # Replace commas with spaces and echo the result # separate_list() { local list list="$@" local part local newlist local firstpart local lastpart local enclosure case "$list" in *,|,*|*,,*|*[[:space:]]*) # # There's been whining about us not catching embedded white space in # comma-separated lists. This is an attempt to snag some of the cases. # echo "WARNING -- invalid comma-separated list \"$@\"" >&2 ;; *\[*\]*) # # Where we need to embed comma-separated lists within lists, we enclose them # within square brackets. # firstpart=${list%%\[*} lastpart=${list#*\[} enclosure=${lastpart%%\]*} lastpart=${lastpart#*\]} case $lastpart in \,*) case $firstpart in *\,) echo "$(separate_list ${firstpart%,}) [$enclosure] $(separate_list ${lastpart#,})" ;; *) echo "$(separate_list $firstpart)[$enclosure] $(separate_list ${lastpart#,})" ;; esac ;; *) case $firstpart in *\,) echo "$(separate_list ${firstpart%,}) [$enclosure]$(separate_list $lastpart)" ;; *) echo "$(separate_list $firstpart)[$enclosure]$(separate_list $lastpart)" ;; esac ;; esac return ;; esac list="$@" part="${list%%,*}" newlist="$part" while [ "x$part" != "x$list" ]; do list="${list#*,}"; part="${list%%,*}"; newlist="$newlist $part"; done echo "$newlist" } # # add command executor # add_command() { local interface host hostlist zone ipset if ! shorewall6_is_started ; then echo "Shorewall6 Not Started" >&2 exit 2 fi case "$IPSET" in */*) ;; *) [ -n "$(mywhich $IPSET)" ] || fatal_error "The $IPSET utility cannot be located" ;; esac # # Normalize host list # while [ $# -gt 1 ]; do interface=${1%%:*} host=${1#*:} [ "$host" = "$1" ] && host= if [ -z "$host" ]; then hostlist="$hostlist $interface:::/0" else for h in $(separate_list $host); do hostlist="$hostlist $interface:$h" done fi shift done zone=$1 for host in $hostlist; do interface=${host%%:*} ipset=6_${zone}_${interface}; if ! qt $IPSET -L $ipset -n; then fatal_error "Zone $zone, interface $interface is does not have a dynamic host list" fi host=${host#*:} if $IPSET -A $ipset $host; then echo "Host $interface:$host added to zone $zone" else fatal_error "Unable to add $interface:$host to zone $zone" fi done } # # delete command executor # delete_command() { local interface host hostent hostlist zone ipset if ! shorewall6_is_started ; then echo "Shorewall6 Not Started" >&2 exit 2; fi case "$IPSET" in */*) ;; *) [ -n "$(mywhich $IPSET)" ] || fatal_error "The $IPSET utility cannot be located" ;; esac # # Normalize host list # while [ $# -gt 1 ]; do interface=${1%%:*} host=${1#*:} [ "$host" = "$1" ] && host= if [ -z "$host" ]; then hostlist="$hostlist $interface:::/0" else for h in $(separate_list $host); do hostlist="$hostlist $interface:$h" done fi shift done zone=$1 for hostent in $hostlist; do interface=${hostent%%:*} ipset=6_${zone}_${interface}; if ! qt $IPSET -L $ipset -n; then fatal_error "Zone $zone, interface $interface is does not have a dynamic host list" fi host=${hostent#*:} if $IPSET -D $ipset $host; then echo "Host $hostent deleted from zone $zone" else echo " WARNING: Unable to delete host $hostent to zone $zone" >&2 fi done } # # 'allow' command executor # allow_command() { [ -n "$g_debugging" ] && set -x [ $# -eq 1 ] && usage 1 if shorewall6_is_started ; then local which which='-s' local range range='--src-range' if ! chain_exists dynamic; then echo "Dynamic blacklisting is not enabled in the current $g_product configuration" >&2 exit 2 fi [ -n "$nolock" ] || mutex_on while [ $# -gt 1 ]; do shift case $1 in from) which='-s' range='--src-range' continue ;; to) which='-d' range='--dst-range' continue ;; *-*) if qt $IP6TABLES -D dynamic -m iprange $range $1 -j reject ||\ qt $IP6TABLES -D dynamic -m iprange $range $1 -j DROP ||\ qt $IP6TABLES -D dynamic -m iprange $range $1 -j logdrop ||\ qt $IP6TABLES -D dynamic -m iprange $range $1 -j logreject then echo "$1 Allowed" else echo "$1 Not Dropped or Rejected" fi ;; *) if qt $IP6TABLES -D dynamic $which $1 -j reject ||\ qt $IP6TABLES -D dynamic $which $1 -j DROP ||\ qt $IP6TABLES -D dynamic $which $1 -j logdrop ||\ qt $IP6TABLES -D dynamic $which $1 -j logreject then echo "$1 Allowed" else echo "$1 Not Dropped or Rejected" fi ;; esac done [ -n "$nolock" ] || mutex_off else error_message "ERROR: $g_product is not started" exit 2 fi } # # 'logwatch' command executor # logwatch_command() { shift finished=0 while [ $finished -eq 0 -a $# -ne 0 ]; do option=$1 case $option in -*) option=${option#-} [ -z "$option" ] && usage 1 while [ -n "$option" ]; do case $option in v*) VERBOSITY=$(($VERBOSITY + 1 )) option=${option#v} ;; q*) VERBOSITY=$(($VERBOSITY - 1 )) option=${option#q} ;; m*) g_showmacs=Yes option=${option#m} ;; -) finished=1 option= ;; *) usage 1 ;; esac done shift ;; *) finished=1 ;; esac done [ -n "$g_debugging" ] && set -x if [ $# -eq 1 ]; then logwatch $1 elif [ $# -eq 0 ]; then logwatch 30 else usage 1 fi } # # Determine which optional facilities are supported by iptables/netfilter # determine_capabilities() { CONNTRACK_MATCH= NEW_CONNTRACK_MATCH= OLD_CONNTRACK_MATCH= MULTIPORT= XMULTIPORT= POLICY_MATCH= PHYSDEV_MATCH= PHYSDEV_BRIDGE= IPRANGE_MATCH= RECENT_MATCH= OWNER_MATCH= IPSET_MATCH= OLD_IPSET_MATCH= IPSET_V5= CONNMARK= XCONNMARK= CONNMARK_MATCH= XCONNMARK_MATCH= RAW_TABLE= IPP2P_MATCH= OLD_IPP2P_MATCH= LENGTH_MATCH= CLASSIFY_TARGET= ENHANCED_REJECT= USEPKTTYPE= KLUDGEFREE= MARK= XMARK= EXMARK= TPROXY_TARGET= MANGLE_FORWARD= COMMENTS= ADDRTYPE= TCPMSS_MATCH= HASHLIMIT_MATCH= NFQUEUE_TARGET= REALM_MATCH= HELPER_MATCH= CONNLIMIT_MATCH= TIME_MATCH= GOTO_TARGET= IPMARK_TARGET= LOG_TARGET=Yes FLOW_FILTER= FWMARK_RT_MASK= MARK_ANYWHERE= HEADER_MATCH= ACCOUNT_TARGET= AUDIT_TARGET= IPSET_V5= chain=fooX$$ [ -n "$IP6TABLES" ] || IP6TABLES=$(mywhich ip6tables) if [ -z "$IP6TABLES" ]; then echo " ERROR: No executable iptables binary can be found on your PATH" >&2 exit 1 fi [ -n "$IP" ] || IP=$(which ip) [ -n "$IP" -a -x "$IP" ] || IP= [ "$TC" = tc -o -z "$TC" ] && TC=$(which tc) [ -n "$TC" -a -x "$TC" ] || TC= qt $IP6TABLES -t mangle -L -n && MANGLE_ENABLED=Yes || MANGLE_ENABLED= qt $IP6TABLES -F $chain qt $IP6TABLES -X $chain if ! $IP6TABLES -N $chain; then echo " ERROR: The command \"$IP6TABLES -N $chain\" failed" >&2 exit 1 fi chain1=${chain}1 qt $IP6TABLES -F $chain1 qt $IP6TABLES -X $chain1 if ! $IP6TABLES -N $chain1; then echo " ERROR: The command \"$IP6TABLES -N $chain1\" failed" >&2 exit 1 fi if ! qt $IP6TABLES -A $chain -m state --state ESTABLISHED,RELATED -j ACCEPT && ! qt $IP6TABLES -A $chain -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT; then echo " ERROR: Your kernel lacks connection tracking and/or state matching -- Shorewall will not run on this system" >&2 exit 1 fi qt $IP6TABLES -A $chain -m conntrack --ctorigdst ::1 -j ACCEPT && CONNTRACK_MATCH=Yes if [ -n "$CONNTRACK_MATCH" ]; then qt $IP6TABLES -A $chain -m conntrack -p tcp --ctorigdstport 22 -j ACCEPT && NEW_CONNTRACK_MATCH=Yes qt $IP6TABLES -A $chain -m conntrack ! --ctorigdst ::1 || OLD_CONNTRACK_MATCH=Yes fi if qt $IP6TABLES -A $chain -p tcp -m multiport --dports 21,22 -j ACCEPT; then MULTIPORT=Yes qt $IP6TABLES -A $chain -p tcp -m multiport --sports 60 -m multiport --dports 99 -j ACCEPT && KLUDEFREE=Yes fi qt $IP6TABLES -A $chain -p tcp -m multiport --dports 21:22 -j ACCEPT && XMULTIPORT=Yes qt $IP6TABLES -A $chain -m policy --pol ipsec --mode tunnel --dir in -j ACCEPT && POLICY_MATCH=Yes if qt $IP6TABLES -A $chain -m physdev --physdev-out eth0 -j ACCEPT; then PHYSDEV_MATCH=Yes qt $IP6TABLES -A $chain -m physdev --physdev-is-bridged --physdev-in eth0 --physdev-out eth0 -j ACCEPT && PHYSDEV_BRIDGE=Yes if [ -z "${KLUDGEFREE}" ]; then qt $IP6TABLES -A $chain -m physdev --physdev-in eth0 -m physdev --physdev-out eth0 -j ACCEPT && KLUDGEFREE=Yes fi fi if qt $IP6TABLES -A $chain -m iprange --src-range ::1-::2 -j ACCEPT; then IPRANGE_MATCH=Yes if [ -z "${KLUDGEFREE}" ]; then qt $IP6TABLES -A $chain -m iprange --src-range ::1-::2 -m iprange --dst-range ::1-::2 -j ACCEPT && KLUDGEFREE=Yes fi fi qt $IP6TABLES -A $chain -m recent --update -j ACCEPT && RECENT_MATCH=Yes qt $IP6TABLES -A $chain -m owner --uid-owner 0 -j ACCEPT && OWNER_MATCH=Yes if qt $IP6TABLES -A $chain -m connmark --mark 2 -j ACCEPT; then CONNMARK_MATCH=Yes qt $IP6TABLES -A $chain -m connmark --mark 2/0xFF -j ACCEPT && XCONNMARK_MATCH=Yes fi qt $IP6TABLES -A $chain -p tcp -m ipp2p --edk -j ACCEPT && IPP2P_MATCH=Yes if [ -n "$IPP2P_MATCH" ]; then qt $IP6TABLES -A $chain -p tcp -m ipp2p --ipp2p -j ACCEPT && OLD_IPP2P_MATCH=Yes fi qt $IP6TABLES -A $chain -m length --length 10:20 -j ACCEPT && LENGTH_MATCH=Yes qt $IP6TABLES -A $chain -j REJECT --reject-with icmp6-adm-prohibited && ENHANCED_REJECT=Yes qt $IP6TABLES -A $chain -j ACCEPT -m comment --comment "This is a comment" && COMMENTS=Yes if [ -n "$MANGLE_ENABLED" ]; then qt $IP6TABLES -t mangle -N $chain if qt $IP6TABLES -t mangle -A $chain -j MARK --set-mark 1; then MARK=Yes qt $IP6TABLES -t mangle -A $chain -j MARK --and-mark 0xFF && XMARK=Yes qt $IP6TABLES -t mangle -A $chain -j MARK --set-mark 1/0xFF && EXMARK=Yes fi if qt $IP6TABLES -t mangle -A $chain -j CONNMARK --save-mark; then CONNMARK=Yes qt $IP6TABLES -t mangle -A $chain -j CONNMARK --save-mark --mask 0xFF && XCONNMARK=Yes fi qt $IP6TABLES -t mangle -A $chain -j CLASSIFY --set-class 1:1 && CLASSIFY_TARGET=Yes qt $IP6TABLES -t mangle -A $chain -j IPMARK --addr src && IPMARK_TARGET=Yes qt $IP6TABLES -t mangle -A $chain -p tcp -j TPROXY --on-port 0 --tproxy-mark 1 && TPROXY_TARGET=Yes qt $IP6TABLES -t mangle -F $chain qt $IP6TABLES -t mangle -X $chain qt $IP6TABLES -t mangle -L FORWARD -n && MANGLE_FORWARD=Yes fi qt $IP6TABLES -t raw -L -n && RAW_TABLE=Yes if qt mywhich ipset; then qt ipset -X $chain # Just in case something went wrong the last time if qt ipset -N $chain hash:ip family inet6; then IPSET_V5=Yes if qt $IP6TABLES -A $chain -m set --match-set $chain src -j ACCEPT; then qt $IP6TABLES -D $chain -m set --match-set $chain src -j ACCEPT IPSET_MATCH=Yes elif qt $IP6TABLES -A $chain -m set --set $chain src -j ACCEPT; then qt $IP6TABLES -D $chain -m set --set $chain src -j ACCEPT IPSET_MATCH=Yes OLD_IPSET_MATCH=Yes fi qt ipset -X $chain fi fi qt $IP6TABLES -A $chain -m pkttype --pkt-type broadcast -j ACCEPT && USEPKTTYPE=Yes qt $IP6TABLES -A $chain -m addrtype --src-type BROADCAST -j ACCEPT && ADDRTYPE=Yes qt $IP6TABLES -A $chain -p tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1000:1500 -j ACCEPT && TCPMSS_MATCH=Yes qt $IP6TABLES -A $chain -m hashlimit --hashlimit-upto 4 --hashlimit-burst 5 --hashlimit-name $chain --hashlimit-mode dstip -j ACCEPT && HASHLIMIT_MATCH=Yes if [ -z "$HASHLIMIT_MATCH" ]; then qt $IP6TABLES -A $chain -m hashlimit --hashlimit 4 --hashlimit-burst 5 --hashlimit-name $chain --hashlimit-mode dstip -j ACCEPT && OLD_HL_MATCH=Yes HASHLIMIT_MATCH=$OLD_HL_MATCH fi qt $IP6TABLES -A $chain -j NFQUEUE --queue-num 4 && NFQUEUE_TARGET=Yes qt $IP6TABLES -A $chain -m realm --realm 4 && REALM_MATCH=Yes qt $IP6TABLES -A $chain -m helper --helper "ftp" && HELPER_MATCH=Yes qt $IP6TABLES -A $chain -m connlimit --connlimit-above 8 -j DROP && CONNLIMIT_MATCH=Yes qt $IP6TABLES -A $chain -m time --timestart 23:00 -j DROP && TIME_MATCH=Yes qt $IP6TABLES -A $chain -g $chain1 && GOTO_TARGET=Yes qt $IP6TABLES -A $chain -j LOG || LOG_TARGET= qt $IP6TABLES -A $chain -j MARK --set-mark 5 && MARK_ANYWHERE=Yes qt $IP6TABLES -A $chain -m ipv6header --header 255 && HEADER_MATCH=Yes qt $IP6TABLES -A $chain -j ACCOUNT --addr 1::/122 --tname $chain && ACCOUNT_TARGET=Yes qt $IP6TABLES -A $chain -j AUDIT --type drop && AUDIT_TARGET=Yes qt $IP6TABLES -F $chain qt $IP6TABLES -X $chain qt $IP6TABLES -F $chain1 qt $IP6TABLES -X $chain1 [ -n "$TC" ] && $TC filter add flow help 2>&1 | grep -q ^Usage && FLOW_FILTER=Yes [ -n "$IP" ] && $IP rule add help 2>&1 | grep -q /MASK && FWMARK_RT_MASK=Yes CAPVERSION=$SHOREWALL_CAPVERSION KERNELVERSION=$(printf "%d%02d%02d" $(uname -r 2> /dev/null | sed -e 's/-.*//' -e 's/^\([0-9][0-9]*\)\.\([0-9][0-9]*\)\.\([0-9][0-9]*\).*$/\1 \2 \3/g')) } report_capabilities() { report_capability() # $1 = Capability Description , $2 Capability Setting (if any) { local setting setting= [ "x$2" = "xYes" ] && setting="Available" || setting="Not available" echo " " $1: $setting } if [ $VERBOSITY -gt 1 ]; then echo "Shorewall6 has detected the following ip6tables/netfilter capabilities:" report_capability "Packet Mangling" $MANGLE_ENABLED report_capability "Multi-port Match" $MULTIPORT [ -n "$MULTIPORT" ] && report_capability "Extended Multi-port Match" $XMULTIPORT report_capability "Connection Tracking Match" $CONNTRACK_MATCH if [ -n "$CONNTRACK_MATCH" ]; then report_capability "Extended Connection Tracking Match Support" $NEW_CONNTRACK_MATCH [ -n "$OLD_CONNTRACK_MATCH" ] && report_capability "Old Connection Tracking Match Syntax" $OLD_CONNTRACK_MATCH fi report_capability "Packet Type Match" $USEPKTTYPE report_capability "Policy Match" $POLICY_MATCH report_capability "Physdev Match" $PHYSDEV_MATCH report_capability "Physdev-is-bridged Support" $PHYSDEV_BRIDGE report_capability "Packet length Match" $LENGTH_MATCH report_capability "IP range Match" $IPRANGE_MATCH report_capability "Recent Match" $RECENT_MATCH report_capability "Owner Match" $OWNER_MATCH if [ -n "$IPSET_MATCH" ]; then report_capability "Ipset Match" $IPSET_MATCH [ -n "$OLD_IPSET_MATCH" ] && report_capability "OLD_Ipset Match" $OLD_IPSET_MATCH fi report_capability "CONNMARK Target" $CONNMARK [ -n "$CONNMARK" ] && report_capability "Extended CONNMARK Target" $XCONNMARK report_capability "Connmark Match" $CONNMARK_MATCH [ -n "$CONNMARK_MATCH" ] && report_capability "Extended Connmark Match" $XCONNMARK_MATCH report_capability "Raw Table" $RAW_TABLE report_capability "IPP2P Match" $IPP2P_MATCH [ -n "$OLD_IPP2P_MATCH" ] && report_capability "Old IPP2P Match Syntax" $OLD_IPP2P_MATCH report_capability "CLASSIFY Target" $CLASSIFY_TARGET report_capability "Extended REJECT" $ENHANCED_REJECT report_capability "Repeat match" $KLUDGEFREE report_capability "MARK Target" $MARK [ -n "$MARK" ] && report_capability "Extended MARK Target" $XMARK [ -n "$XMARK" ] && report_capability "Extended MARK Target 2" $EXMARK report_capability "Mangle FORWARD Chain" $MANGLE_FORWARD report_capability "Comments" $COMMENTS report_capability "Address Type Match" $ADDRTYPE report_capability "TCPMSS Match" $TCPMSS_MATCH report_capability "Hashlimit Match" $HASHLIMIT_MATCH [ -n "$OLD_HL_MATCH" ] && report_capability "Old Hashlimit Match" $OLD_HL_MATCH report_capability "NFQUEUE Target" $NFQUEUE_TARGET report_capability "Realm Match" $REALM_MATCH report_capability "Helper Match" $HELPER_MATCH report_capability "Connlimit Match" $CONNLIMIT_MATCH report_capability "Time Match" $TIME_MATCH report_capability "Goto Support" $GOTO_TARGET report_capability "IPMARK Target" $IPMARK_TARGET report_capability "LOG Target" $LOG_TARGET report_capability "TPROXY Target" $TPROXY_TARGET report_capability "FLOW Classifier" $FLOW_FILTER report_capability "fwmark route mask" $FWMARK_RT_MASK report_capability "Mark in any table" $MARK_ANYWHERE report_capability "Header Match" $HEADER_MATCH report_capability "ACCOUNT Target" $ACCOUNT_TARGET report_capability "AUDIT Target" $AUDIT_TARGET report_capability "ipset V5" $IPSET_V5 fi [ -n "$PKTTYPE" ] || USEPKTTYPE= } report_capabilities1() { report_capability1() # $1 = Capability { eval echo $1=\$$1 } echo "#" echo "# Shorewall6 $SHOREWALL_VERSION detected the following ip6tables/netfilter capabilities - $(date)" echo "#" report_capability1 MANGLE_ENABLED report_capability1 MULTIPORT report_capability1 XMULTIPORT report_capability1 CONNTRACK_MATCH report_capability1 NEW_CONNTRACK_MATCH report_capability1 OLD_CONNTRACK_MATCH report_capability1 USEPKTTYPE report_capability1 POLICY_MATCH report_capability1 PHYSDEV_MATCH report_capability1 PHYSDEV_BRIDGE report_capability1 LENGTH_MATCH report_capability1 IPRANGE_MATCH report_capability1 RECENT_MATCH report_capability1 OWNER_MATCH report_capability1 IPSET_MATCH report_capability1 OLD_IPSET_MATCH report_capability1 CONNMARK report_capability1 XCONNMARK report_capability1 CONNMARK_MATCH report_capability1 XCONNMARK_MATCH report_capability1 RAW_TABLE report_capability1 IPP2P_MATCH report_capability1 OLD_IPP2P_MATCH report_capability1 CLASSIFY_TARGET report_capability1 ENHANCED_REJECT report_capability1 KLUDGEFREE report_capability1 MARK report_capability1 XMARK report_capability1 EXMARK report_capability1 MANGLE_FORWARD report_capability1 COMMENTS report_capability1 ADDRTYPE report_capability1 TCPMSS_MATCH report_capability1 HASHLIMIT_MATCH report_capability1 OLD_HL_MATCH report_capability1 NFQUEUE_TARGET report_capability1 REALM_MATCH report_capability1 HELPER_MATCH report_capability1 CONNLIMIT_MATCH report_capability1 TIME_MATCH report_capability1 GOTO_TARGET report_capability1 IPMARK_TARGET report_capability1 LOG_TARGET report_capability1 TPROXY_TARGET report_capability1 FLOW_FILTER report_capability1 FWMARK_RT_MASK report_capability1 MARK_ANYWHERE report_capability1 HEADER_MATCH report_capability1 ACCOUNT_TARGET report_capability1 AUDIT_TARGET report_capability1 IPSET_V5 echo CAPVERSION=$SHOREWALL_CAPVERSION echo KERNELVERSION=$KERNELVERSION }