#!/bin/sh # # Shorewall 4.1 -- /usr/share/shorewall/lib.tcrules # # 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 - 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 is loaded by /usr/share/shorewall/compiler when the tcrules file is # non-empty. It is also loaded by the compiled firewall script under the same # condition when the script is processing the 'refresh' command. # # # Process a TC Rule - $MARKING_CHAIN is assumed to contain the name of the # default marking chain # # The caller has established values for the following variables: # # mark - MARK column # sources - SOURCE column # dests - DEST column # proto - PROTO column # ports - PORT(S) column # sports - CLIENT PORT(S) column # user - USER column # testval - TEST column # length - LENGTH column # tos - TOS column # process_tc_rule() { local did_connmark= multiport= classid= chain=$MARKING_CHAIN target="MARK --set-mark" marktest= handle_designator() { chain=$1 mark="${mark%:*}" } do_ipp2p() { [ -n "$IPP2P_MATCH" ] || fatal_error "Your kernel and/or iptables does not have IPP2P match support. TC Rule: \"$rule\"" [ "x$port" = "x-" ] && port="ipp2p" case $proto in *:*) proto=${proto#*:} ;; *) proto=tcp ;; esac r="${r}-p $proto -m ipp2p --${port} " } verify_small_mark() { verify_mark $1 [ $(($1)) -lt 256 ] || fatal_error "Mark Value ($1) too large, rule \"$rule\"" } do_connmark() { target="CONNMARK --set-mark" mark=$mark/0xff did_connmark=Yes } validate_mark() { case $1 in */*) verify_mark ${1%/*} verify_mark ${1#*/} ;; *) verify_mark $1 ;; esac } add_a_tc_rule() { r= if [ "x$source" != "x-" ]; then case $source in $FW:*) r="$(source_ip_range ${source#*:}) " ;; *:~*|*:!~*) interface=${source%:*} verify_interface $interface || fatal_error "Unknown interface $interface in rule \"$rule\"" r="$(match_source_dev $interface) $(mac_match ${source#*:}) " ;; *:*) interface=${source%:*} verify_interface $interface || fatal_error "Unknown interface $interface in rule \"$rule\"" r="$(match_source_dev $interface) $(source_ip_range ${source#*:}) " ;; *.*.*|+*|!+*) r="$(source_ip_range $source) " ;; ~*|!~*) r="$(mac_match $source) " ;; $FW) ;; *) verify_interface $source || fatal_error "Unknown interface $source in rule \"$rule\"" r="$(match_source_dev $source) " ;; esac fi if [ "x${user:--}" != "x-" ]; then [ "$chain" != tcout ] && \ fatal_error "Invalid use of a user/group: rule \"$rule\"" r="$r-m owner" case "$user" in *+*) r="$r --cmd-owner ${user#*+} " user=${user%+*} ;; esac case "$user" in *:*) temp="${user%:*}" [ -n "$temp" ] && r="$r --uid-owner $temp " temp="${user#*:}" [ -n "$temp" ] && r="$r --gid-owner $temp " ;; *) [ -n "$user" ] && r="$r --uid-owner $user " ;; esac fi [ -n "$marktest" ] && r="${r}-m ${marktest}--mark $testval " if [ "x$dest" != "x-" ]; then case $dest in *:*) [ "$chain" = tcpre ] && fatal_error "Destination interface is not allowed in the PREROUTING chain - rule \"$rule\"" interface=${dest%:*} verify_interface $interface || fatal_error "Unknown interface $interface in rule \"$rule\"" r="$(match_dest_dev $interface) $(dest_ip_range ${dest#*:}) " ;; *.*.*|+*|!+*) r="${r}$(dest_ip_range $dest) " ;; *) [ "$chain" = tcpre ] && fatal_error "Destination interface is not allowed in the PREROUTING chain - rule \"$rule\"" verify_interface $dest || fatal_error "Unknown interface $dest in rule \"$rule\"" r="${r}$(match_dest_dev $dest) " ;; esac fi if [ "x${length:=-}" != "x-" ]; then [ -n "$LENGTH_MATCH" ] || fatal_error "Your kernel and/or iptables does not have length match support. Rule: \"$rule\"" r="${r}-m length --length ${length} " fi if [ "x${tos:=-}" != "x-" ]; then r="${r}-m tos --tos ${tos} " fi case $proto in ipp2p|IPP2P|ipp2p:*|IPP2P:*) do_ipp2p ;; icmp|ICMP|1) r="${r}-p icmp " [ "x$port" = "x-" ] || r="${r}--icmp-type $port" ;; *) [ "x$proto" = "x-" ] && proto=all [ "x$proto" = "x" ] && proto=all [ "$proto" = "all" ] || r="${r}-p $proto " [ "x$port" = "x-" ] || r="${r}$multiport $port " ;; esac [ "x$sport" = "x-" ] || r="${r}--sport $sport " if [ -n "${excludesources}${excludedests}" ]; then [ $target = RETURN ] && \ fatal_error "Exclusion is currently not supported with CONTINUE" build_exclusion_chain chain1 mangle "$excludesources" "$excludedests" run_iptables2 -t mangle -A $chain $r -j $chain1 run_iptables -t mangle -A $chain1 -j $target $mark else run_iptables2 -t mangle -A $chain $r -j $target $mark fi } # # E x e c u t i o n B e g i n s H e r e # case $sources in $FW|$FW:*) chain=tcout if [ "x$mark" != "x${mark%:*}" ]; then case "${mark#*:}" in t|T) handle_designator tcpost ;; ct|CT) handle_designator tcpost do_connmark ;; c|C) mark=${mark%:*} do_connmark ;; p|P|cp|CP|f|F|cf|CF) fatal_error "Invalid chain designator for source \$FW; rule \"$rule\"" ;; *) chain=tcpost target="CLASSIFY --set-class" classid=Yes ;; esac fi ;; *) if [ "x$mark" != "x${mark%:*}" ]; then case "${mark#*:}" in p|P) handle_designator tcpre ;; cp|CP) handle_designator tcpre do_connmark ;; f|F) handle_designator tcfor ;; cf|CF) handle_designator tcfor do_connmark ;; t|T) handle_designator tcpost ;; ct|CT) handle_designator tcpost do_connmark ;; c|C) mark=${mark%:*} do_connmark ;; *) chain=tcpost classid=Yes target="CLASSIFY --set-class" ;; esac fi ;; esac mask=0xffff case $mark in SAVE) [ -n "$did_connmark" ] && fatal_error "SAVE not valid with :C[FP]" target="CONNMARK --save-mark --mask 0xFF" mark= ;; SAVE/*) [ -n "$did_connmark" ] && fatal_error "SAVE not valid with :C[FP]" target="CONNMARK --save-mark --mask" mark=${mark#*/} verify_small_mark $mark ;; RESTORE) [ -n "$did_connmark" ] && fatal_error "RESTORE not valid with :C[FP]" target="CONNMARK --restore-mark --mask 0xFF" mark= ;; RESTORE/*) [ -n "$did_connmark" ] && fatal_error "RESTORE not valid with :C[FP]" target="CONNMARK --restore-mark --mask" mark=${mark#*/} verify_small_mark $mark ;; CONTINUE) [ -n "$did_connmark" ] && fatal_error "CONTINUE not valid with :C[FP]" target=RETURN mark= ;; \|*) [ -n "$classid" ] && fatal_error "Invalid class ID: $mark" [ -n "$did_connmark" ] && fatal_error "Logical OR not valid with :C[FP]" target="MARK --or-mark" mark=${mark#|} validate_mark $mark if [ $((${mark%/*})) -lt 256 -a $((${mark%/*})) -ne 0 -a -n "$HIGH_ROUTE_MARKS" ] && [ $chain = tcpre -o $chain = tcout ]; then fatal_error "Marks < 256 may not be set in the PREROUTING or OUTPUT chains when HIGH_ROUTE_MARKS=Yes" fi ;; \&*) [ -n "$classid" ] && fatal_error "Invalid class ID: $mark" [ -n "$did_connmark" ] && fatal_error "Logical AND not valid with :C[FP]" target="MARK --and-mark" mark=${mark#&} validate_mark $mark if [ $((${mark%/*})) -lt 256 -a $((${mark%/*})) -ne 0 -a -n "$HIGH_ROUTE_MARKS" ] && [ $chain = tcpre -o $chain = tcout ]; then fatal_error "Marks < 256 may not be set in the PREROUTING chain when HIGH_ROUTE_MARKS=Yes" fi ;; *) if [ -z "$classid" ]; then validate_mark $mark if [ $((${mark%/*})) -gt 255 ]; then case $chain in tcpre|tcout) ;; *) fatal_error "Invalid mark value ($mark) in rule \"$rule\"" ;; esac elif [ $((${mark%/*})) -lt 256 -a $((${mark%/*})) -ne 0 -a -n "$HIGH_ROUTE_MARKS" ] && [ $chain = tcpre -o $chain = tcout ]; then fatal_error "Marks < 256 may not be set in the PREROUTING chain when HIGH_ROUTE_MARKS=Yes" fi fi ;; esac case $testval in -) ;; !*:C) marktest="connmark ! " testval=${testval%:*} testval=${testval#!} ;; *:C) marktest="connmark " testval=${testval%:*} ;; !*) marktest="mark ! " testval=${testval#!} ;; *) [ -n "$testval" ] && marktest="mark " ;; esac if [ -n "$marktest" ] ; then case $testval in */*) verify_mark ${testval%/*} verify_mark ${testval#*/} ;; *) verify_mark $testval testval=$testval/$mask ;; esac fi excludesources= case ${sources:=-} in *!*!*) fatal_error "Invalid SOURCE in rule \"$rule\"" ;; !*) if [ $(list_count $sources) -gt 1 ]; then excludesources=${sources#!} sources=- fi ;; *!*) excludesources=${sources#*!} sources=${sources%!*} ;; esac excludedests= case ${dests:=-} in *!*!*) fatal_error "Invalid DEST in rule \"$rule\"" ;; !*) if [ $(list_count $dests) -gt 1 ]; then excludedests=${dests#*!} dests=- fi ;; *!*) excludedests=${dests#*!} dests=${dests%!*} ;; esac multiport=--dport for source in $(separate_list $sources); do for dest in $(separate_list $dests); do for port in $(separate_list ${ports:=-}); do for sport in $(separate_list ${sports:=-}); do add_a_tc_rule done done done done progress_message " TC Rule \"$rule\" $DONE" save_progress_message_short " TC Rule \\\"$rule\\\" Added" } # # Process the tcrules file # process_tc_rules() { cat >&3 << __EOF__ # # Create Marking Rules from the tcrules file # setup_tc_rules() { __EOF__ INDENT=" " while read mark sources dests proto ports sports user testval length tos; do if [ "x$mark" = xCOMMENT ]; then if [ -n "$COMMENTS" ]; then comment=$(echo $sources $dests $proto $ports $sports $user $testval $length $tos) save_command COMMENT=\"$comment\" else error_message "COMMENT ignored -- requires comment support in iptables/Netfilter" fi else rule=$(echo "$mark $sources $dests $proto $ports $sports $user $testval $length $tos") process_tc_rule fi done < $TMP_DIR/tcrules INDENT="" save_command "}" save_command }