#---------------------------------------------------------------------------- # /etc/rc.d/fwrules-helper-ipv6 __FLI4LVER__ # helper functions for packet filter rules # # Creation: 2003-07-05 jw5 # Last Update: $Id$ #---------------------------------------------------------------------------- # include IPv6 helper functions (especially translate_ip6_net) . /etc/rc.d/base-helper.ipv6 : ${IP6TABLES:=sbin_ip6tables} : ${fwh_state6:=/var/run/fwrules-helper.state.ipv6} : ${tmpl_dir6:=/etc/fwrules.tmpl} : ${ext_dir6:=/etc/rc.d} fw_tmp6=/tmp/fwrules-helper.$$ sbin_ip6tables () { if ! /sbin/ip6tables "$@" > $fw_tmp6 2>&1 then log_error "Error executing ip6tables $@" log_error < $fw_tmp6 rm -f $fw_tmp6 return 1 fi rm -f $fw_tmp6 } [ "$IPV6_NET_N" ] || . /var/run/ip6_net.conf # read extensions for i in $ext_dir6/fwrules-*.ext.ipv6 do if [ -f $i ] then if sh -c ". $i" > $fw_tmp6 2>&1 then . $i else log_error "Error parsing extension $i, rules with this extension in them will fail." log_error < $fw_tmp6 fi rm -f $fw_tmp6 fi done assert_empty6 () { case x$2 in x) ;; *) set_error "$1: to many subexpressions after translation" ;; esac } get_limit6() { limit= case x"$1" in x | xnone) ;; *) set `echo $1 | sed -e 's/:/ /'` case x$2 in x) limit="-m limit --limit $1" ;; *) limit="-m limit --limit $1 --limit-burst $2" ;; esac ;; esac } # mangle_params6 param option # - replace ':' with ' ', '-' with ':' mangle_params6 () { echo $1 | sed -e "s/:/ /g;s/\([0-9]\+\)-\([0-9]\+\)/\1:\2/g;" } recent_p=yes do6_recent_param () { eval `echo $recent_param | sed -e 's/^\([a-z]*\)=\(.*\)/recent_tok=\1;recent_val=\2/'` recent_opt="$recent_opt --$recent_tok $recent_val" } do6_recent () { recent_opt= set `echo $1 | sed -e 's/,/ /g'` while [ "$1" ]; do case $1 in \!*) recent_param=`echo $1 | sed -e 's/^!//'` recent_neg='!' ;; *) recent_param="$1" recent_neg= ;; esac case $recent_param in name=* | seconds=* | hitcount=*) do6_recent_param $recent_param ;; set | update | rcheck | remove) recent_opt="$recent_opt $recent_neg --$recent_param" ;; rttl | rsource | rdest) recent_opt="$recent_opt --$recent_param" ;; *) set_error "recent: unknown option $recent_param" ;; esac shift done match_opt="$match_opt -m recent $recent_opt" } # mangle_ip_params6 param option # in: ip:port, ip or port[-range] # out: ip, ip_neg_opt; port, port_neg_opt, tcp_udp_needed # replaces '-' with ':' if no option given mangle_ip_params6 () { param=$1 opt=$2 case $param in \[*\]:*) set `echo $param | sed -e 's/\[//;s/\]:/ /'` ip=$1 port=$2 tcp_udp_needed='yes' ;; \[*\]/*) set `echo $param | sed -e 's/\[//;s/\]//'` ip=$1 ;; \[*\]) set `echo $param | sed -e 's/\[//;s/\]//'` ip=$1 ;; *) if echo $param | grep -q '^[-,0-9]\+$' then ip=::0/0 port=$param tcp_udp_needed='yes' else ip=$param port='' fi ;; esac get_negation6 $ip ip=$param ip_neg_opt="$neg_opt" get_negation6 $port port=$param port_neg_opt="$neg_opt" translate_ip6_net $ip ip=$res [ "$port" -a ! "$opt" ] && port="`echo $port | sed -e 's/-/:/'`" } # get_negation6 param # - removes '!' from param and sets neg_opt accordingly get_negation6 () { case $1 in !*) param=`echo $1 | sed -e 's/^!//g'` neg_opt='! ' ;; *) param=$1 neg_opt='' ;; esac } # # default matches # prot_p=yes state_p=yes length_p=yes limit_p=yes if_p=yes mac_p=yes # do6_something handle "something:" statments # # do6_prot param opt # - handles "prot:" statements do6_prot () { set `mangle_params6 $1` proto=$1 opt=$2 case $proto in icmpv6) assert_empty6 "do6_prot $*" "$3" ;; *) assert_empty6 "do6_prot $*" "$2" ;; esac get_negation6 $proto case $param in any) ;; icmpv6) prot_opt="-p $neg_opt icmpv6" case x$opt in x) ;; *) get_negation6 $opt prot_opt="$prot_opt --icmpv6-type $neg_opt $param" ;; esac ;; *) prot_opt="-p $neg_opt $param" ;; esac } do6_state () { get_negation6 $1 match_opt="$match_opt -m state $neg_opt --state $param" } do6_length () { param=`mangle_params6 $1` get_negation6 $param match_opt="$match_opt -m length $neg_opt --length $param" } do6_limit () { get_limit6 $1 match_opt="$match_opt $limit" } do6_mac () { get_negation6 $1 match_opt="$match_opt -m mac --mac-source $neg_opt $param" } do6_if_opt () { if=$3 case $if in IPV6_NET_*_DEV | ipv6_net_*_dev) translate_ip6_net $if if=$res ;; circuit-*) if [ -f /var/run/$if ] then if=`cat /var/run/$if` else set_error "*** Error: unknown circuit $if, ignoring interface restriction ***" if=any fi ;; pppoe) if [ -f /var/run/pppoe-device ] then if=`cat /var/run/pppoe-device` else set_error "*** Error: unknown circuit pppoe, ignoring interface restriction ***" if=any fi ;; esac if [ "$if" != "any" ] then if_opt="$if_opt $1 $2 $if" fi } do6_if () { set `mangle_params6 $1` assert_empty6 "do6_if $*" "$3" get_negation6 $1 if_in=$param if_in_negopt="$neg_opt" get_negation6 $2 if_out=$param if_out_negopt="$neg_opt" do6_if_opt -i "$if_in_negopt" $if_in do6_if_opt -o "$if_out_negopt" $if_out } do6_default () { rule="$*" case "$rule" in *BIDIRECTIONAL*) rule=`echo $rule | sed -e 's#\(.*\)BIDIRECTIONAL\(.*\)#\1\2#'` bidirectional='yes' ;; esac case "$rule" in *DROP*|*REJECT*|*ACCEPT*|*MASQUERADE*) case "$rule" in *NOLOG*) rule=`echo $rule | sed -e 's#\(.*\)NOLOG[^[:space:]]*\(.*\)#\1\2#'` log_opt='no' ;; *LOG*) log_prefix=`echo $rule | sed -e 's#.*LOG:\?\([^[:space:]]*\).*#\1#'` log_opt='yes' rule=`echo $rule | sed -e 's#\(.*\)LOG[^[:space:]]*\(.*\)#\1\2#'` ;; esac ;; esac set $rule # rewrite arguments case "$#" in 3) src=$1 dst=$2 action=$3 ;; 2) case $1 in \[*\]:* | dynamic*) dst=$1 action=$2 ;; \[*\] | \[*\]/* | any | IPV6_* | ipv6_* | @* ) src=$1 action=$2 ;; *) dst=$1 action=$2 ;; esac ;; 1) action=$1 ;; *) set_error "*** Error in rule $rule ***" ;; esac mangle_ip_params6 $src if is_error then set_error return fi src="$ip_neg_opt$ip" case "x$port" in x) ;; *,*) src_port_opt="-m multiport --source-ports $port_neg_opt $port" ;; *) src_port_opt="--source-port $port_neg_opt $port" ;; esac mangle_ip_params6 $dst dst="$ip_neg_opt$ip" dport=$port case "x$port" in x) ;; *,*) dst_port_opt="-m multiport --destination-ports $port_neg_opt $port" ;; *) dst_port_opt="--destination-port $port_neg_opt $port" ;; esac action_opt= case $action in NONE) ;; DROP) case x$log_prefix in x) action_opt="-j $drop";; esac ;; REJECT) case x$log_prefix in x) action_opt="-j $reject";; esac ;; MASQUERADE:*) mangle_ip_params6 `echo $action | sed -e 's/MASQUERADE://'` keep_slash action_opt="-j MASQUERADE" [ "$port" ] && action_opt="$action_opt --to-ports $port" ;; LOG:*) log_prefix=`echo $action | sed -e 's/LOG://'` action_opt="-j LOG --log-prefix $log_prefix" ;; *) action_opt="-j $action" ;; esac } # prot:(tcp|udp|gre|[0-9]{1,3}) if:in:out state:RELATED,ESTABLISHED,NEW,INVALID src[:port-port] dest[:port-port] accept|reject|drop really_execute_iptables6 () { do_action=$2 do_log='' case $do_action in *drp-log | *rej-log) if [ "$log_opt" = 'no' ] then do_action=`echo $do_action | sed -e 's#^\(.*\)-log#\1#'` fi ;; *drp | *rej) if [ "$log_opt" = 'yes' ] then do_action=${do_action}-log fi ;; *) do_log="$log_opt" ;; esac log= case "$do_log" in yes) case x$log_prefix in x) log="-j $log_target" ;; *) log="-j $log_target --log-prefix $log_prefix" ;; esac case $op in I) $IP6TABLES $1 -m comment --comment "$comment" $do_action $IP6TABLES $1 $log ;; *) $IP6TABLES $1 $log $IP6TABLES $1 -m comment --comment "$comment" $do_action ;; esac ;; *) $IP6TABLES $1 -m comment --comment "$comment" $do_action ;; esac } exec_iptables6 () { table=$1 chain=$2 opts="-t $table -$op $chain $position $if_opt $match_opt -s $src $src_port_opt -d $dst $dst_port_opt" if [ -z "$fw_rule_error" ] then if [ -z "$tcp_udp_needed" -o "$prot_opt" ] then really_execute_iptables6 "$prot_opt $opts" "$action_opt" else really_execute_iptables6 "-p tcp $opts" "$action_opt" really_execute_iptables6 "-p udp $opts" "$action_opt" fi fi } init_options6 () { table=$1 chain=$2 op=$3 if_opt='' match_opt='' prot_opt='' src='any' src_port_opt='' dst='any' dst_port_opt='' action_opt='' tcp_udp_needed='' bidirectional='' log_opt='' log_prefix='' log_target='LOG' fw_rule_error='' if [ -f $fwh_state6.$chain ] then . $fwh_state6.$chain else drop=DROP reject=REJECT fi } parse_rule6 () { parse_rule_param="$1" while true do set $parse_rule_param eval `echo $1 | sed -e 's/^[[:space:]]*/#/;s/^#\([a-z][[:alnum:]]\+\):\([^[:space:]]*\).*/present=\$\1_p;ext=\1;val=\2/;s/^#.*/ext=;/;s/.*ext=any.*/ext=/'` case x$ext in x|xdynamic) do6_default $* break ;; *) shift parse_rule_param="$*" case $present in yes) eval do6_$ext $val ;; *) log_error "invalid match $ext:" ;; esac ;; esac done } exec_rule6 () { $pre_exec if [ "$bidirectional" = 'yes' ] then exec_iptables6 $table $chain swp=$src src=$dst dst=$swp if_opt=`echo $if_opt | sed -e 's#-i #-x #' -e 's#-o #-i #' -e 's#-x #-o #'` fi exec_iptables6 $table $chain } read_tmpl () { { if [ -e $tmpl_dir6/$tmpl_name ] then cat $tmpl_dir6/$tmpl_name else grep -i "^$tmpl_name[[:space:]]" $tmpl_dir6/templates fi } | sed -e "s/^$tmpl_name //;s/#.*//;/^[[:space:]]*$/d" } # do_rule6 table chain operation rule position method comment do_rule6 () { case x$FWRULES_DO_DEBUG in x) set +x; ;; esac orig_table=$1 orig_chain=$2 orig_op=$3 orig_rule="$4" position=$5 method="$6" comment="$7" : ${method:=exec_rule6} : ${comment:="$orig_rule"} init_options6 $orig_table $orig_chain $orig_op case "$orig_rule" in *tmpl:*) eval set `echo "$orig_rule" | sed -e "s#\(.*\)[[:space:]:]*tmpl:\([^[:space:]]*\)\(.*\)#'\1' '\2' '\3'#"` rule_head="$1" rule_tail="$3" orig_rule="$rule_head $rule_tail" orig_tmpl_name=$2 tmpl_name=`echo $2 | tr A-Z a-z` tmpl_rules="`read_tmpl`" if [ -n "$tmpl_rules" ] then echo "$tmpl_rules" | while read tmpl_rule do parse_rule6 "$tmpl_rule DROP" parse_rule6 "$orig_rule" is_error || $method init_options6 $orig_table $orig_chain $orig_op done else set_error "can't find packetfilter template '$orig_tmpl_name'" fi ;; *) parse_rule6 "$orig_rule" is_error || $method esac case x$tmp_debug in xyes) set -x; ;; esac case x$debug_active in xyes) set -x; ;; esac } add_rule6 () { do_rule6 $1 $2 A "$3" '' '' "$4" } del_rule6 () { do_rule6 $1 $2 D "$3" '' '' "$4" } ins_rule6 () { do_rule6 $1 $2 I "$3" "$4" '' "$5" } do_pre_exec6 () { chain=test-chain } do_check_rule6 () { dcr_res=0 pre_exec=do_pre_exec6 base_colecho=true base_log_file=/tmp/check_rule6.$$ rm -f $base_log_file if ! $IP6TABLES -t $1 -L test-chain > /dev/null 2>&1; then do_chain6 $1 test-chain N add-test-chain fi do_rule6 $1 $2 A "$3" '' "$4" "$3" if [ -f $base_log_file ]; then echo "Invalid rule '$3'" cat $base_log_file rm -f $base_log_file dcr_res=1 else do_rule6 $1 $2 D "$3" '' "$4" "$3" fi unset pre_exec unset base_colecho unset base_log_file return $dcr_res } check_rule6 () { SCRIPT=check_rule6 do_check_rule6 filter foo "$1" '' } # setup_logging6 # setup_logging6 () { enabled=$1 chain=$2 prefix="$3" log_limit="$4" rej_limit="$5" udp_rej_limit="$6" get_limit6 "$log_limit" log_limit="$limit" get_limit6 "$rej_limit" rej_limit="$limit" get_limit6 "$udp_rej_limit" udp_rej_limit="$limit" case $chain in FORWARD) drop="fw-drp" reject="fw-rej" ;; INPUT) drop="in-drp" reject="in-rej" ;; *) log_error "setup_loggig: unknown chain $chain" drop="un-drp" reject="un-rej" ;; esac cat <<-EOF > $fwh_state6.$chain drop_name=$drop reject_name=$reject EOF $IP6TABLES -N ${drop} $IP6TABLES -N ${drop}-log $IP6TABLES -N ${reject} $IP6TABLES -N ${reject}-log $IP6TABLES -N ${reject}-fin $IP6TABLES -A ${drop}-log $log_limit -j LOG --log-prefix "${prefix}-drop " $IP6TABLES -A ${drop}-log -j DROP $IP6TABLES -A ${drop} -j DROP $IP6TABLES -A ${reject}-log $log_limit -j LOG --log-prefix "${prefix}-reject " $IP6TABLES -A ${reject}-log -j ${reject} $IP6TABLES -A ${reject} -p udp $udp_rej_limit -j ${reject}-fin $IP6TABLES -A ${reject} -p ! udp $rej_limit -j ${reject}-fin $IP6TABLES -A ${reject} -j DROP case $reject in *in*) $IP6TABLES -A ${reject}-fin -p tcp -j REJECT --reject-with tcp-reset $IP6TABLES -A ${reject}-fin -p udp -j REJECT --reject-with port-unreach $IP6TABLES -A ${reject}-fin -j DROP ;; *) $IP6TABLES -A ${reject}-fin -p ! icmpv6 -j REJECT --reject-with icmp6-adm-prohibited $IP6TABLES -A ${reject}-fin -j DROP ;; esac case $enabled in yes) drop="${drop}-log" reject="${reject}-log" ;; *) drop=DROP ;; esac cat <<-EOF >> $fwh_state6.$chain drop=$drop reject=$reject EOF } # get log chain names and default drop/reject actions get_defaults6 () { . $fwh_state6.$1 } # close_chain close_chain6 () { . $fwh_state6.$1 case "$2" in DROP) $IP6TABLES -A $1 -j $drop ;; REJECT) $IP6TABLES -A $1 -j $reject ;; ACCEPT) $IP6TABLES -P $1 ACCEPT ;; esac } do_chain6 () { table=$1 chain=$2 op=$3 name=$4 if ! $IP6TABLES -t $table -$op $chain then log_error "${name}_chain6 $chain failed..." fi } add_chain6 () { do_chain6 filter $1 N add } flush_chain6 () { do_chain6 filter $1 F flush } del_chain6 () { do_chain6 filter $1 X del } set_count6 () { echo 1 > /var/run/$1_def_end.ipv6 /sbin/ip6tables -nL $1 --line-numbers | \ while read num tail do case $num in [0-9]*) echo `expr $num + 1` > /var/run/$1_def_end.ipv6 ;; esac done } get_count6 () { res=`cat /var/run/$1_def_end.ipv6` }