#!/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 "$SHOWMACS" -o $VERBOSE -gt 2 ]; then $LOGREAD | grep 'IN=.* OUT=.*SRC=.*:.*DST=' | head -n$1 | tac | sed 's/ kernel://; s/\[.*\] //' | sed s/" $host $LOGFORMAT"/" "/ else $LOGREAD | grep 'IN=.* OUT=.*SRC=.*:.*DST=' | head -n$1 | tac | sed 's/ kernel://; s/MAC=.* SRC=/SRC=/; s/\[.*\] '// | 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 } ip -o link list | while read inx interface details; do show_one_tc ${interface%:} done } # # 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 { host=$(echo $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" $RING_BELL packet_log 40 if [ "$pause" = "Yes" ]; then echo echo $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 # 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 $RESTOREPATH -a ! -x $RESTOREPATH ]; then echo " ERROR: $RESTOREPATH exists and is not a saved $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 $IP6TABLES -L dynamic -n > ${VARDIR}/save; then echo " Dynamic Rules Saved" if [ -f ${VARDIR}/firewall ]; then if $iptables_save > ${VARDIR}/restore-$$; then cp -f ${VARDIR}/firewall $RESTOREPATH mv -f ${VARDIR}/restore-$$ ${RESTOREPATH}-iptables chmod +x $RESTOREPATH echo " Currently-running Configuration Saved to $RESTOREPATH" run_user_exit save else rm -f ${VARDIR}/restore-$$ echo " ERROR: Currently-running Configuration Not Saved" >&2 fi else echo " ERROR: ${VARDIR}/firewall does not exist" >&2 fi else echo "Error Saving the Dynamic Rules" >&2 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 else heading "Routing Table" ip -6 route list 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 $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*) VERBOSE=$(($VERBOSE + 1 )) option=${option#v} ;; x*) IPT_OPTIONS="-xnv" option=${option#x} ;; m*) SHOWMACS=Yes option=${option#m} ;; f*) 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*) IPT_OPTIONS1="--line-numbers" option=${option#l} ;; *) usage 1 ;; esac done shift ;; *) finished=1 ;; esac done IPT_OPTIONS="$IPT_OPTIONS $IPT_OPTIONS1" [ -n "$debugging" ] && set -x case "$1" in connections) [ $# -gt 1 ] && usage 1 local count=$(cat /proc/sys/net/netfilter/nf_conntrack_count) local max=$(cat /proc/sys/net/netfilter/nf_conntrack_max) echo "$PRODUCT $version Connections ($count of $max) at $HOSTNAME - $(date)" echo grep '^ipv6' /proc/net/nf_conntrack ;; tos|mangle) [ $# -gt 1 ] && usage 1 echo "$PRODUCT $version Mangle Table at $HOSTNAME - $(date)" echo show_reset $IP6TABLES -t mangle -L $IPT_OPTIONS ;; raw) [ $# -gt 1 ] && usage 1 echo "$PRODUCT $version raw Table at $HOSTNAME - $(date)" echo show_reset $IP6TABLES -t raw -L $IPT_OPTIONS ;; log) [ $# -gt 1 ] && usage 1 echo "$PRODUCT $version Log ($LOGFILE) at $HOSTNAME - $(date)" echo show_reset host=$(echo $HOSTNAME | sed 's/\..*$//') packet_log 20 ;; tc) [ $# -gt 1 ] && usage 1 echo "$PRODUCT $version Traffic Control at $HOSTNAME - $(date)" echo show_tc ;; classifiers|filters) [ $# -gt 1 ] && usage 1 echo "$PRODUCT $version Classifiers at $HOSTNAME - $(date)" echo show_classifiers ;; zones) [ $# -gt 1 ] && usage 1 if [ -f ${VARDIR}/zones ]; then echo "$PRODUCT $version Zones at $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 VERBOSE=2 if [ -n "$FILEMODE" ]; then report_capabilities1 else report_capabilities fi ;; ip) [ $# -gt 1 ] && usage 1 echo "$PRODUCT $version IP at $HOSTNAME - $(date)" echo ip -6 addr list ;; routing) [ $# -gt 1 ] && usage 1 echo "$PRODUCT $version Routing at $HOSTNAME - $(date)" echo show_routing ;; config) . ${SHAREDIR}/configpath echo "Default CONFIG_PATH is $CONFIG_PATH" [ -n "$LITEDIR" ] && echo "LITEDIR is $LITEDIR" ;; chain) shift echo "$PRODUCT $version $([ $# -gt 1 ] && echo "Chains " || [ $# -gt 0 ] && echo "Chain " || echo $table Table)$* at $HOSTNAME - $(date)" echo show_reset if [ $# -gt 0 ]; then for chain in $*; do $IP6TABLES -t $table -L $chain $IPT_OPTIONS done else $IP6TABLES -t $table -L $IPT_OPTIONS fi ;; vardir) echo $VARDIR; ;; policies) [ $# -gt 1 ] && usage 1 echo "$PRODUCT $version Policies at $HOSTNAME - $(date)" echo [ -f ${VARDIR}/policies ] && cat ${VARDIR}/policies; ;; *) if [ "$PRODUCT" = Shorewall6 ]; then case $1 in actions) [ $# -gt 1 ] && usage 1 echo "allowBcast # Accept Multicast and Anycast Packets" echo "dropBcast # Silently Drop Multicast and Anycast Packets" echo "allowInvalid # Accept packets that are in the INVALID conntrack state." 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 ;; 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 [ -n "$table_given" ] || for chain in $*; do if ! qt $IP6TABLES -t $table -L $chain $IPT_OPTIONS; then echo "usage $(basename $0) show [ -x ] [ -m ] [-f] [ -t {filter|mangle} ] [ {chain [ [ ... ]|actions|capabilities|classifiers|config|connections|filters|ip|log|macros|mangle|routing|tc|zones} ] " >&2 exit 1 fi done echo "$PRODUCT $version $([ $# -gt 1 ] && echo "Chains " || echo "Chain ")$* at $HOSTNAME - $(date)" echo show_reset for chain in $*; do $IP6TABLES -t $table -L $chain $IPT_OPTIONS done else echo "$PRODUCT $version $table Table at $HOSTNAME - $(date)" echo show_reset $IP6TABLES -t $table -L $IPT_OPTIONS fi ;; esac } # # Dump Command Executor # 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*) IPT_OPTIONS="-xnv" option=${option#x} ;; m*) SHOWMACS=Yes option=${option#m} ;; l*) IPT_OPTIONS1="--line-numbers" option=${option#l} ;; *) usage 1 ;; esac done shift ;; *) finished=1 ;; esac done IPT_OPTIONS="$IPT_OPTIONS $IPT_OPTIONS1" [ $VERBOSE -lt 2 ] && VERBOSE=2 [ -n "$debugging" ] && set -x [ $# -eq 0 ] || usage 1 clear_term echo "$PRODUCT $version Dump at $HOSTNAME - $(date)" echo if [ -f /usr/share/shorewall/version ]; then echo " Shorewall $(cat /usr/share/shorewall/version)" echo fi show_reset host=$(echo $HOSTNAME | sed 's/\..*$//') $IP6TABLES -L $IPT_OPTIONS heading "Log ($LOGFILE)" packet_log 20 heading "Mangle Table" $IP6TABLES -t mangle -L $IPT_OPTIONS heading "Raw Table" $IP6TABLES -t raw -L $IPT_OPTIONS 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 -tunap if [ -n "$TC_ENABLED" ]; then heading "Traffic Control" show_tc heading "TC Filters" show_classifiers fi } # # 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*) 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 RESTOREPATH=${VARDIR}/$RESTOREFILE export NOROUTES [ -n "$nolock" ] || mutex_on if [ -x $RESTOREPATH ]; then if [ -x ${RESTOREPATH}-ipsets ] ; then echo Restoring Ipsets... $IP6TABLES -F $IP6TABLES -X $SHOREWALL_SHELL ${RESTOREPATH}-ipsets fi progress_message3 "Restoring Shorewall6..." $SHOREWALL_SHELL $RESTOREPATH restore && progress_message3 "$PRODUCT restored from ${VARDIR}/$RESTOREFILE" [ -n "$nolock" ] || mutex_off else echo "File $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=$VERBOSE_OFFSET local option option=- if [ -n "$USE_VERBOSITY" ]; then echo "-v$USE_VERBOSITY" elif [ $VERBOSE_OFFSET -gt 0 ]; then while [ $v -gt 0 ]; do option="${option}v" v=$(($v - 1)) done echo $option elif [ $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 shift 3 while [ $# -gt 0 ]; do case $1 in *-*) qt $IP6TABLES -D dynamic -m iprange --src-range $1 -j reject qt $IP6TABLES -D dynamic -m iprange --src-range $1 -j DROP qt $IP6TABLES -D dynamic -m iprange --src-range $1 -j logreject qt $IP6TABLES -D dynamic -m iprange --src-range $1 -j logdrop $IP6TABLES -A dynamic -m iprange --src-range $1 -j $chain || break 1 ;; *) qt $IP6TABLES -D dynamic -s $1 -j reject qt $IP6TABLES -D dynamic -s $1 -j DROP qt $IP6TABLES -D dynamic -s $1 -j logreject qt $IP6TABLES -D dynamic -s $1 -j logdrop $IP6TABLES -A dynamic -s $1 -j $chain || break 1 ;; esac echo "$1 $finished" shift done } # # 'hits' commmand executor # hits_command() { local finished finished=0 local today today= 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= ;; t*) today=$(date +'^%b %_d.*') option=${option#t} ;; *) usage 1 ;; esac done shift ;; *) finished=1 ;; esac done [ $# -eq 0 ] || usage 1 clear_term echo "$PRODUCT $version Hits at $HOSTNAME - $(date)" echo timeout=30 if $LOGREAD | grep -q "${today}IN=.* OUT=" ; then echo " HITS IP DATE" echo " ---- --------------- ------" $LOGREAD | grep "${today}IN=.* OUT=" | sed 's/\(.\{6\}\)\(.*SRC=\)\(.*\)\( DST=.*\)/\3 \1/' | sort | uniq -c | sort -rn | while read count address month day; do printf '%7d %-15s %3s %2d\n' $count $address $month $day done echo "" echo " HITS IP PORT" echo " ---- --------------- -----" $LOGREAD | grep "${today}IN=.* OUT=" | sed 's/\(.*SRC=\)\(.*\)\( DST=.*DPT=\)\([0-9]\{1,5\}\)\(.*\)/\2 \4/ t s/\(.*SRC=\)\(.*\)\( DST=.*\)/\2/' | sort | uniq -c | sort -rn | while read count address port; do printf '%7d %-15s %d\n' $count $address $port done echo "" echo " HITS DATE" echo " ---- ------" $LOGREAD | grep "${today}IN=.* OUT=" | sed 's/\(.\{6\}\)\(.*\)/\1/' | sort | uniq -c | sort -rn | while read count month day; do printf '%7d %3s %2d\n' $count $month $day done echo "" echo " HITS PORT SERVICE(S)" echo " ---- ----- ----------" $LOGREAD | grep "${today}IN=.* OUT=.*DPT" | sed 's/\(.*DPT=\)\([0-9]\{1,5\}\)\(.*\)/\2/' | sort | uniq -c | sort -rn | while read count port ; do # List all services defined for the given port srv=$(grep "^[^#].*\\b$port/" /etc/services | cut -f 1 | cut -f 1 -d' ' | sort -u) srv=$(echo $srv | sed 's/ /,/g') if [ -n "$srv" ] ; then printf '%7d %5d %s\n' $count $port $srv else printf '%7d %5d\n' $count $port fi done fi } # # 'allow' command executor # allow_command() { [ -n "$debugging" ] && set -x [ $# -eq 1 ] && usage 1 if shorewall6_is_started ; then [ -n "$nolock" ] || mutex_on while [ $# -gt 1 ]; do shift case $1 in *-*) if qt $IP6TABLES -D dynamic -m iprange --src-range $1 -j reject ||\ qt $IP6TABLES -D dynamic -m iprange --src-range $1 -j DROP ||\ qt $IP6TABLES -D dynamic -m iprange --src-range $1 -j logdrop ||\ qt $IP6TABLES -D dynamic -m iprange --src-range $1 -j logreject then echo "$1 Allowed" else echo "$1 Not Dropped or Rejected" fi ;; *) if qt $IP6TABLES -D dynamic -s $1 -j reject ||\ qt $IP6TABLES -D dynamic -s $1 -j DROP ||\ qt $IP6TABLES -D dynamic -s $1 -j logdrop ||\ qt $IP6TABLES -D dynamic -s $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: $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*) VERBOSE=$(($VERBOSE + 1 )) option=${option#v} ;; q*) VERBOSE=$(($VERBOSE - 1 )) option=${option#q} ;; m*) SHOWMACS=Yes option=${option#m} ;; -) finished=1 option= ;; *) usage 1 ;; esac done shift ;; *) finished=1 ;; esac done [ -n "$debugging" ] && set -x if [ $# -eq 1 ]; then logwatch $1 elif [ $# -eq 0 ]; then logwatch 30 else usage 1 fi }