From 1ebb13cf08149c6bf7bce7293d32a13721f33b33 Mon Sep 17 00:00:00 2001 From: teastep Date: Sat, 6 May 2006 16:15:33 +0000 Subject: [PATCH] Restore the ability to refresh traffic control git-svn-id: https://shorewall.svn.sourceforge.net/svnroot/shorewall/trunk@3873 fbd18981-670d-0410-9b5c-8dc0c1a9a2bb --- Shorewall/changelog.txt | 6 + Shorewall/compiler | 624 ------------------------------------- Shorewall/firewall | 72 ++++- Shorewall/functions | 624 +++++++++++++++++++++++++++++++++++++ Shorewall/releasenotes.txt | 58 ++-- 5 files changed, 722 insertions(+), 662 deletions(-) diff --git a/Shorewall/changelog.txt b/Shorewall/changelog.txt index c0c793e6d..f05875063 100644 --- a/Shorewall/changelog.txt +++ b/Shorewall/changelog.txt @@ -1,3 +1,9 @@ +Changes in 3.2.0 Beta 7 + +1) Fix mark/mask validation. + +2) Restore traffic control to 'refresh'. + Changes in 3.2.0 Beta 6 1) Fix tc "notfound" errors when 'restart' is run out of ip-up.local. diff --git a/Shorewall/compiler b/Shorewall/compiler index 48fcdfc86..619fef948 100755 --- a/Shorewall/compiler +++ b/Shorewall/compiler @@ -1097,36 +1097,6 @@ __EOF__ [ -z "$ALL_INTERFACES" ] && fatal_error "No Interfaces Defined" } -# -# Check that a mark value or mask is less that 256 or that it is less than 65536 and -# that it's lower 8 bits are zero. -# -verify_mark() # $1 = value to test -{ - verify_mark2() - { - case $1 in - 0*) - [ $(($1)) -lt 256 ] && return 0 - [ -n "$HIGH_ROUTE_MARKS" ] || return 1 - [ $(($1)) -gt 65535 ] && return 1 - return $(($1 & 0xFF)) - ;; - [1-9]*) - [ $1 -lt 256 ] && return 0 - [ -n "$HIGH_ROUTE_MARKS" ] || return 1 - [ $1 -gt 65535 ] && return 1 - return $(($1 & 0xFF)) - ;; - *) - return 2 - ;; - esac - } - - verify_mark2 $1 || fatal_error "Invalid Mark or Mask value: $1" -} - # # Process the providers file # @@ -2936,600 +2906,6 @@ build_exclusion_chain() # $1 = variable to store chain name into $2 = table, $3 eval $1=$c } -# -# Arne Bernin's 'tc4shorewall' -# -setup_traffic_shaping() -{ - local mtu r2q tc_all_devices device mark rate ceil prio options devfile=$(find_file tcdevices) classfile=$(find_file tcclasses) devnum=1 - mtu=1500 - r2q=10 - - rate_to_kbit() { - local rateunit rate - rate=$1 - rateunit=$( echo $rate | sed -e 's/[0-9]*//') - rate=$( echo $rate | sed -e 's/[a-z]*//g') - - case $rateunit in - kbit) - rate=$rate - ;; - mbit) - rate=$(expr $rate \* 1024) - ;; - mbps) - rate=$(expr $rate \* 8192) - ;; - kbps) - rate=$(expr $rate \* 8) - ;; - *) - rate=$(expr $rate / 128) - ;; - esac - echo $rate - } - - calculate_quantum() { - local rate - rate=$1 - rate=$(rate_to_kbit $rate) - rate=$(expr $rate \* 128 / $r2q ) - if [ $rate -lt $mtu ] ; then - echo $mtu - else - echo $rate - fi - } - - # get given outbandwidth for device - get_outband_for_dev() { - local device inband outband - while read device inband outband; do - expandv device inband outband - tcdev="$device $inband $outband" - if [ "$1" = "$device" ] ; then - echo $outband - return - fi - done < $TMP_DIR/tcdevices - } - - check_tcclasses_options() { - while [ $# -gt 1 ]; do - shift - case $1 in - default|tcp-ack|tos-minimize-delay|tos-maximize-throughput|tos-maximize-reliability|tos-minimize-cost|tos-normal-service) - ;; - tos=0x[0-9a-f][0-9a-f]|tos=0x[0-9a-f][0-9a-f]/0x[0-9a-f][0-9a-f]) - ;; - *) - echo $1 - return 1 - ;; - esac - done - return 0 - } - - get_defmark_for_dev() { - local searchdev searchmark device ceil prio options - searchdev=$1 - - while read device mark rate ceil prio options; do - expandv device mark rate ceil prio options - options=$(separate_list $options | tr '[A-Z]' '[a-z]') - tcdev="$device $mark $rate $ceil $prio $options" - if [ "$searchdev" = "$device" ] ; then - list_search "default" $options && echo $mark &&return 0 - fi - done < $TMP_DIR/tcclasses - - return 1 - } - - check_defmark_for_dev() { - get_defmark_for_dev $1 >/dev/null - } - - validate_tcdevices_file() { - progress_message2 "Validating $devfile..." - local device local device inband outband - while read device inband outband; do - expandv device inband outband - tcdev="$device $inband $outband" - check_defmark_for_dev $device || fatal_error "Option default is not defined for any class in tcclasses for interface $device" - case $interface in - *:*|+) - fatal_error "Invalid Interface Name: $interface" - ;; - esac - list_search $device $devices && fatal_error "Interface $device is defined more than once in tcdevices" - tc_all_devices="$tc_all_devices $device" - done < $TMP_DIR/tcdevices - } - - validate_tcclasses_file() { - progress_message2 "Validating $classfile..." - local classlist device mark rate ceil prio bandw wrongopt allopts opt - allopts="" - while read device mark rate ceil prio options; do - expandv device mark rate ceil prio options - tcdev="$device $mark $rate $ceil $prio $options" - ratew=$(get_outband_for_dev $device) - options=$(separate_list $options | tr '[A-Z]' '[a-z]') - for opt in $options; do - case $opt in - tos=0x??) - opt="$opt/0xff" - ;; - esac - list_search "$device-$opt" $allopts && fatal_error "option $opt already defined in a chain for interface $device in tcclasses" - allopts="$allopts $device-$opt" - done - wrongopt=$(check_tcclasses_options $options) || fatal_error "unknown option $wrongopt for class iface $device mark $mark in tcclasses file" - if [ -z "$ratew" ] ; then - fatal_error "device $device seems not to be configured in tcdevices" - fi - list_search "$device-$mark" $classlist && fatal_error "Mark $mark for interface $device defined more than once in tcclasses" - # - # Convert HEX/OCTAL mark representation to decimal - # - mark=$(($mark)) - verify_mark $mark - [ $mark -lt 256 ] || fatal_error "Invalid Mark Value" - classlist="$classlist $device-$mark" - done < $TMP_DIR/tcclasses - } - - add_root_tc() { - local defmark - defmark=$(get_defmark_for_dev $device) - save_command qt tc qdisc del dev $device root - save_command qt tc qdisc del dev $device ingress - run_tc qdisc add dev $device root handle $devnum: htb default 1$defmark - run_tc class add dev $device parent $devnum: classid $devnum:1 htb rate $outband - run_tc qdisc add dev $device handle ffff: ingress - run_tc filter add dev $device parent ffff: protocol ip prio 50 u32 match ip src 0.0.0.0/0 police rate ${inband} burst 10k drop flowid :1 - eval $(chain_base $device)_devnum=$devnum - devnum=$(($devnum + 1)) - } - - add_tc_class() { - local full classid tospair tosmask - full=$(get_outband_for_dev $device) - full=$(rate_to_kbit $full) - - if [ -z "$prio" ] ; then - prio=1 - fi - - case $rate in - *full*) - rate=$(echo $rate | sed -e "s/full/$full/") - rate="$(($rate))kbit" - ;; - esac - - case $ceil in - *full*) - ceil=$(echo $ceil | sed -e "s/full/$full/") - ceil="$(($ceil))kbit" - ;; - esac - - eval devnum=\$$(chain_base $device)_devnum - # - # Convert HEX/OCTAL mark representation to decimal - # - mark=$(($mark)) - - classid=$devnum:1$mark - - [ -n "$devnum" ] || fatal_error "Device $device not defined in $devfile" - - run_tc class add dev $device parent $devnum:1 classid $classid htb rate $rate ceil $ceil prio $prio quantum $(calculate_quantum $rate) - run_tc qdisc add dev $device parent $classid handle 1$mark: sfq perturb 10 - # add filters - if [ -n "$CLASSIFY_TARGET" ]; then - run_iptables -t mangle -A tcpost $(match_dest_dev $device) -m mark --mark $mark/0xFF -j CLASSIFY --set-class $classid - else - run_tc filter add dev $device protocol ip parent $devnum:0 prio 1 handle $mark fw classid $classid - fi - #options - list_search "tcp-ack" $options && run_tc filter add dev $device parent $devnum:0 protocol ip prio 10 u32 match ip protocol 6 0xff match u8 0x05 0x0f at 0 match u16 0x0000 0xffc0 at 2 match u8 0x10 0xff at 33 flowid $classid - list_search "tos-minimize-delay" $options && options="$options tos=0x10/0x10" - list_search "tos-maximize-throughput" $options && options="$options tos=0x08/0x08" - list_search "tos-maximize-reliability" $options && options="$options tos=0x04/0x04" - list_search "tos-minimize-cost" $options && options="$options tos=0x02/0x02" - list_search "tos-normal-service" $options && options="$options tos=0x00/0x1e" - - for tospair in $(list_walk "tos=" $options) ; do - case $tospair in - */*) - tosmask=${tospair##*/} - ;; - *) - tosmask=0xff - ;; - esac - run_tc filter add dev $device parent $devnum:0 protocol ip prio 10 u32 match ip tos ${tospair%%/*} $tosmask flowid $classid - done - } - - strip_file tcdevices $devfile - strip_file tcclasses $classfile - - validate_tcdevices_file - validate_tcclasses_file - - if [ -s $TMP_DIR/tcdevices ]; then - save_progress_message "Setting up Traffic Control..." - progress_message2 "$DOING $devfile..." - - while read device inband outband defmark ackmark; do - expandv device inband outband defmark ackmark - tcdev="$device $inband $outband" - add_root_tc - progress_message_and_save " TC Device $tcdev defined." - done < $TMP_DIR/tcdevices - fi - - if [ -s $TMP_DIR/tcclasses ]; then - progress_message2 "$DOING $classfile..." - - while read device mark rate ceil prio options; do - expandv device mark rate ceil prio options - tcdev="$device $mark $rate $ceil $prio $options" - options=$(separate_list $options | tr '[A-Z]' '[a-z]') - add_tc_class - progress_message_and_save " TC Class $tcdev defined." - done < $TMP_DIR/tcclasses - fi -} - -# -# Process a TC Rule - $MARKING_CHAIN is assumed to contain the name of the -# default marking chain -# -process_tc_rule() -{ - local did_connmark= - - chain=$MARKING_CHAIN target="MARK --set-mark" marktest= - - verify_designator() { - [ "$chain" = tcout ] && \ - fatal_error "Chain designator not allowed when source is \$FW; rule \"$rule\"" - chain=$1 - mark="${mark%:*}" - } - - do_ipp2p() - { - [ -n "$IPP2P_MATCH" ] || fatal_error "Your kernel and/or iptables does not have IPP2P match support. 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 larg, 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:*) - [ $chain = tcpost ] || chain=tcout - r="$(source_ip_range ${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) - [ $chain = tcpost ] || chain=tcout - ;; - *) - 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 - - multiport= - - 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}--dport $port " - ;; - esac - - [ "x$sport" = "x-" ] || r="${r}--sport $sport " - - if [ -n "${excludesources}${excludedests}" ]; then - 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 - - } - - if [ "$mark" != "${mark%:*}" ]; then - case "${mark#*:}" in - p|P) - verify_designator tcpre - ;; - cp|CP) - verify_designator tcpre - do_connmark - ;; - f|F) - verify_designator tcfor - ;; - cf|CF) - verify_designator tcfor - do_connmark - ;; - c|C) - mark=${mark%:*} - do_connmark - ;; - *) - chain=tcpost - target="CLASSIFY --set-class" - ;; - esac - - fi - - 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= - ;; - *) - if [ "$chain" != tcpost ]; then - validate_mark $mark - if [ $((${mark%/*})) -gt 255 ]; then - case $chain in - tcpre|tcout) - target="MARK --or-mark" - ;; - *) - fatal_error "Invalid mark value ($mark) in rule \"$rule\"" - ;; - esac - elif [ $((${mark%/*})) -ne 0 -a -n "$HIGH_ROUTE_MARKS" -a $chain = tcpre ]; 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 $sourcess) -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 - - 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 " TC Rule \"$rule\" Added" -} - # # Setup queuing and classes # diff --git a/Shorewall/firewall b/Shorewall/firewall index ca0e5152c..95df76d4c 100755 --- a/Shorewall/firewall +++ b/Shorewall/firewall @@ -1742,12 +1742,77 @@ check_disabled_startup() { fi } +# +# Dummy functions to make refresh_tc happy +# +save_progress_message() +{ + return 0 +} + +save_command() +{ + return 0 +} + +progress_message_and_save() +{ + progress_message $@ +} + +# +# Refresh queuing and classes +# +refresh_tc() { + + echo "Refreshing Traffic Control Rules..." + + [ -n "$CLEAR_TC" ] && delete_tc1 + + [ -n "$MARK_IN_FORWARD_CHAIN" ] && chain=tcfor || chain=tcpre + + # + # Flush the TC mangle chains + # + + [ -n "$MANGLE_FORWARD" ] && run_iptables -t mangle -F tcfor + run_iptables -t mangle -F tcpre + run_iptables -t mangle -F tcout + run_iptables -t mangle -F tcpost + # + # Remove all exclusion chains from the mangle table + # + $IPTABLES -t mangle -L -n | grep '^Chain excl_' | while read junk chain rest; do + run_iptables -t mangle -F $chain + run_iptables -t mangle -X $chain + done + + # + # Process the TC Rules File + # + strip_file tcrules + + while read mark sources dests proto ports sports user testval; do + expandv mark sources dests proto ports sports user testval + rule=$(echo "$mark $sources $dests $proto $ports $sports $user $testval") + process_tc_rule + done < $TMP_DIR/tcrules + + if [ -n "$TC_SCRIPT" ]; then + run_user_exit $TC_SCRIPT + elif [ -n "$TC_ENABLED" ]; then + setup_traffic_shaping + fi + +} + # # Refresh the firewall # refresh_firewall() { - # + DOING=Refreshing + DONE=Refreshed progress_message3 "Refreshing Shorewall..." @@ -1770,6 +1835,11 @@ refresh_firewall() [ -f $ecn ] && [ -n "$MANGLE_ENABLED" ] && setup_ecn $ecn + # + # Refresh Traffic Control + # + [ -n "$MANGLE_ENABLED" ] && refresh_tc + run_user_exit refreshed report "Shorewall Refreshed" diff --git a/Shorewall/functions b/Shorewall/functions index 603f0b46f..a87d1bbbf 100644 --- a/Shorewall/functions +++ b/Shorewall/functions @@ -1442,6 +1442,630 @@ log_rule() # $1 = log level, $2 = chain, $3 = disposition , $... = predicates fo log_rule_limit $level $chain $chain $disposition "$LOGLIMIT" "" -A $@ } +# +# Check that a mark value or mask is less that 256 or that it is less than 65536 and +# that it's lower 8 bits are zero. +# +verify_mark() # $1 = value to test +{ + verify_mark2() + { + case $1 in + 0*) + [ $(($1)) -lt 256 ] && return 0 + [ -n "$HIGH_ROUTE_MARKS" ] || return 1 + [ $(($1)) -gt 65535 ] && return 1 + return $(($1 & 0xFF)) + ;; + [1-9]*) + [ $1 -lt 256 ] && return 0 + [ -n "$HIGH_ROUTE_MARKS" ] || return 1 + [ $1 -gt 65535 ] && return 1 + return $(($1 & 0xFF)) + ;; + *) + return 2 + ;; + esac + } + + verify_mark2 $1 || fatal_error "Invalid Mark or Mask value: $1" +} + +# +# Arne Bernin's 'tc4shorewall' +# +setup_traffic_shaping() +{ + local mtu r2q tc_all_devices device mark rate ceil prio options devfile=$(find_file tcdevices) classfile=$(find_file tcclasses) devnum=1 + mtu=1500 + r2q=10 + + rate_to_kbit() { + local rateunit rate + rate=$1 + rateunit=$( echo $rate | sed -e 's/[0-9]*//') + rate=$( echo $rate | sed -e 's/[a-z]*//g') + + case $rateunit in + kbit) + rate=$rate + ;; + mbit) + rate=$(expr $rate \* 1024) + ;; + mbps) + rate=$(expr $rate \* 8192) + ;; + kbps) + rate=$(expr $rate \* 8) + ;; + *) + rate=$(expr $rate / 128) + ;; + esac + echo $rate + } + + calculate_quantum() { + local rate + rate=$1 + rate=$(rate_to_kbit $rate) + rate=$(expr $rate \* 128 / $r2q ) + if [ $rate -lt $mtu ] ; then + echo $mtu + else + echo $rate + fi + } + + # get given outbandwidth for device + get_outband_for_dev() { + local device inband outband + while read device inband outband; do + expandv device inband outband + tcdev="$device $inband $outband" + if [ "$1" = "$device" ] ; then + echo $outband + return + fi + done < $TMP_DIR/tcdevices + } + + check_tcclasses_options() { + while [ $# -gt 1 ]; do + shift + case $1 in + default|tcp-ack|tos-minimize-delay|tos-maximize-throughput|tos-maximize-reliability|tos-minimize-cost|tos-normal-service) + ;; + tos=0x[0-9a-f][0-9a-f]|tos=0x[0-9a-f][0-9a-f]/0x[0-9a-f][0-9a-f]) + ;; + *) + echo $1 + return 1 + ;; + esac + done + return 0 + } + + get_defmark_for_dev() { + local searchdev searchmark device ceil prio options + searchdev=$1 + + while read device mark rate ceil prio options; do + expandv device mark rate ceil prio options + options=$(separate_list $options | tr '[A-Z]' '[a-z]') + tcdev="$device $mark $rate $ceil $prio $options" + if [ "$searchdev" = "$device" ] ; then + list_search "default" $options && echo $mark &&return 0 + fi + done < $TMP_DIR/tcclasses + + return 1 + } + + check_defmark_for_dev() { + get_defmark_for_dev $1 >/dev/null + } + + validate_tcdevices_file() { + progress_message2 "Validating $devfile..." + local device local device inband outband + while read device inband outband; do + expandv device inband outband + tcdev="$device $inband $outband" + check_defmark_for_dev $device || fatal_error "Option default is not defined for any class in tcclasses for interface $device" + case $interface in + *:*|+) + fatal_error "Invalid Interface Name: $interface" + ;; + esac + list_search $device $devices && fatal_error "Interface $device is defined more than once in tcdevices" + tc_all_devices="$tc_all_devices $device" + done < $TMP_DIR/tcdevices + } + + validate_tcclasses_file() { + progress_message2 "Validating $classfile..." + local classlist device mark rate ceil prio bandw wrongopt allopts opt + allopts="" + while read device mark rate ceil prio options; do + expandv device mark rate ceil prio options + tcdev="$device $mark $rate $ceil $prio $options" + ratew=$(get_outband_for_dev $device) + options=$(separate_list $options | tr '[A-Z]' '[a-z]') + for opt in $options; do + case $opt in + tos=0x??) + opt="$opt/0xff" + ;; + esac + list_search "$device-$opt" $allopts && fatal_error "option $opt already defined in a chain for interface $device in tcclasses" + allopts="$allopts $device-$opt" + done + wrongopt=$(check_tcclasses_options $options) || fatal_error "unknown option $wrongopt for class iface $device mark $mark in tcclasses file" + if [ -z "$ratew" ] ; then + fatal_error "device $device seems not to be configured in tcdevices" + fi + list_search "$device-$mark" $classlist && fatal_error "Mark $mark for interface $device defined more than once in tcclasses" + # + # Convert HEX/OCTAL mark representation to decimal + # + mark=$(($mark)) + verify_mark $mark + [ $mark -lt 256 ] || fatal_error "Invalid Mark Value" + classlist="$classlist $device-$mark" + done < $TMP_DIR/tcclasses + } + + add_root_tc() { + local defmark + defmark=$(get_defmark_for_dev $device) + save_command qt tc qdisc del dev $device root + save_command qt tc qdisc del dev $device ingress + run_tc qdisc add dev $device root handle $devnum: htb default 1$defmark + run_tc class add dev $device parent $devnum: classid $devnum:1 htb rate $outband + run_tc qdisc add dev $device handle ffff: ingress + run_tc filter add dev $device parent ffff: protocol ip prio 50 u32 match ip src 0.0.0.0/0 police rate ${inband} burst 10k drop flowid :1 + eval $(chain_base $device)_devnum=$devnum + devnum=$(($devnum + 1)) + } + + add_tc_class() { + local full classid tospair tosmask + full=$(get_outband_for_dev $device) + full=$(rate_to_kbit $full) + + if [ -z "$prio" ] ; then + prio=1 + fi + + case $rate in + *full*) + rate=$(echo $rate | sed -e "s/full/$full/") + rate="$(($rate))kbit" + ;; + esac + + case $ceil in + *full*) + ceil=$(echo $ceil | sed -e "s/full/$full/") + ceil="$(($ceil))kbit" + ;; + esac + + eval devnum=\$$(chain_base $device)_devnum + # + # Convert HEX/OCTAL mark representation to decimal + # + mark=$(($mark)) + + classid=$devnum:1$mark + + [ -n "$devnum" ] || fatal_error "Device $device not defined in $devfile" + + run_tc class add dev $device parent $devnum:1 classid $classid htb rate $rate ceil $ceil prio $prio quantum $(calculate_quantum $rate) + run_tc qdisc add dev $device parent $classid handle 1$mark: sfq perturb 10 + # add filters + if [ -n "$CLASSIFY_TARGET" ]; then + run_iptables -t mangle -A tcpost $(match_dest_dev $device) -m mark --mark $mark/0xFF -j CLASSIFY --set-class $classid + else + run_tc filter add dev $device protocol ip parent $devnum:0 prio 1 handle $mark fw classid $classid + fi + #options + list_search "tcp-ack" $options && run_tc filter add dev $device parent $devnum:0 protocol ip prio 10 u32 match ip protocol 6 0xff match u8 0x05 0x0f at 0 match u16 0x0000 0xffc0 at 2 match u8 0x10 0xff at 33 flowid $classid + list_search "tos-minimize-delay" $options && options="$options tos=0x10/0x10" + list_search "tos-maximize-throughput" $options && options="$options tos=0x08/0x08" + list_search "tos-maximize-reliability" $options && options="$options tos=0x04/0x04" + list_search "tos-minimize-cost" $options && options="$options tos=0x02/0x02" + list_search "tos-normal-service" $options && options="$options tos=0x00/0x1e" + + for tospair in $(list_walk "tos=" $options) ; do + case $tospair in + */*) + tosmask=${tospair##*/} + ;; + *) + tosmask=0xff + ;; + esac + run_tc filter add dev $device parent $devnum:0 protocol ip prio 10 u32 match ip tos ${tospair%%/*} $tosmask flowid $classid + done + } + + strip_file tcdevices $devfile + strip_file tcclasses $classfile + + validate_tcdevices_file + validate_tcclasses_file + + if [ -s $TMP_DIR/tcdevices ]; then + save_progress_message "Setting up Traffic Control..." + progress_message2 "$DOING $devfile..." + + while read device inband outband defmark ackmark; do + expandv device inband outband defmark ackmark + tcdev="$device $inband $outband" + add_root_tc + progress_message_and_save " TC Device $tcdev defined." + done < $TMP_DIR/tcdevices + fi + + if [ -s $TMP_DIR/tcclasses ]; then + progress_message2 "$DOING $classfile..." + + while read device mark rate ceil prio options; do + expandv device mark rate ceil prio options + tcdev="$device $mark $rate $ceil $prio $options" + options=$(separate_list $options | tr '[A-Z]' '[a-z]') + add_tc_class + progress_message_and_save " TC Class $tcdev defined." + done < $TMP_DIR/tcclasses + fi +} + +# +# Process a TC Rule - $MARKING_CHAIN is assumed to contain the name of the +# default marking chain +# +process_tc_rule() +{ + local did_connmark= + + chain=$MARKING_CHAIN target="MARK --set-mark" marktest= + + verify_designator() { + [ "$chain" = tcout ] && \ + fatal_error "Chain designator not allowed when source is \$FW; rule \"$rule\"" + chain=$1 + mark="${mark%:*}" + } + + do_ipp2p() + { + [ -n "$IPP2P_MATCH" ] || fatal_error "Your kernel and/or iptables does not have IPP2P match support. 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 larg, 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:*) + [ $chain = tcpost ] || chain=tcout + r="$(source_ip_range ${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) + [ $chain = tcpost ] || chain=tcout + ;; + *) + 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 + + multiport= + + 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}--dport $port " + ;; + esac + + [ "x$sport" = "x-" ] || r="${r}--sport $sport " + + if [ -n "${excludesources}${excludedests}" ]; then + 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 + + } + + if [ "$mark" != "${mark%:*}" ]; then + case "${mark#*:}" in + p|P) + verify_designator tcpre + ;; + cp|CP) + verify_designator tcpre + do_connmark + ;; + f|F) + verify_designator tcfor + ;; + cf|CF) + verify_designator tcfor + do_connmark + ;; + c|C) + mark=${mark%:*} + do_connmark + ;; + *) + chain=tcpost + target="CLASSIFY --set-class" + ;; + esac + + fi + + 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= + ;; + *) + if [ "$chain" != tcpost ]; then + validate_mark $mark + if [ $((${mark%/*})) -gt 255 ]; then + case $chain in + tcpre|tcout) + target="MARK --or-mark" + ;; + *) + fatal_error "Invalid mark value ($mark) in rule \"$rule\"" + ;; + esac + elif [ $((${mark%/*})) -ne 0 -a -n "$HIGH_ROUTE_MARKS" -a $chain = tcpre ]; 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 $sourcess) -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 + + 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 " TC Rule \"$rule\" Added" +} + delete_tc1() { clear_one_tc() { diff --git a/Shorewall/releasenotes.txt b/Shorewall/releasenotes.txt index 7f002bf18..c38df3533 100644 --- a/Shorewall/releasenotes.txt +++ b/Shorewall/releasenotes.txt @@ -1,4 +1,4 @@ -Shorewall 3.2.0 Beta 6 +Shorewall 3.2.0 Beta 7 Note to users upgrading from Shorewall 2.x or 3.0 @@ -27,40 +27,14 @@ Note to users upgrading from Shorewall 2.x or 3.0 Please see the "Migration Considerations" below for additional upgrade information. -Problems Corrected in 3.2.0 Beta 6 +Problems Corrected in 3.2.0 Beta 7 -1) Previously, if 'shorewall restart' was done out of an ip-up.local - script, some of the utilities like 'tc' and 'ip6tables' were not - on the PATH. +1) Previously, if 'ash' was used as the SHOREWALL_SHELL then any use of + packet marks would fail. -2) In previous 3.2.0 releases, 'detectnets' in /etc/shorewall/interfaces - produced an error message: +Other changes in 3.2.0 Beta 7 - ERROR: 'detectnets' not permitted with the -e run-line option - - even when the -e option had not been specified. - -3) Previously, the 'proxyarp' option in /etc/shorewall/interfaces caused - startup errors. - -4) Previously, specifying specific IP addresses for GATEWAY in with 'balance' - /etc/shorewall/providers caused a [re]start error. - -Other changes in 3.2.0 Beta 6 - -1) A TOS column has been added to /etc/shorewall/tcrules. This allows marking - based on the contents of the TOS field in the packet header. - -2) Beginning with this release, the way in which packet marking in the - PREROUTING chain interracts with the 'track' option in /etc/shorewall/providers - has changed in two ways: - - a) Packets *arriving* on a tracked interface are now passed to the PREROUTING - marking chain so that they may be marked with a mark other than the - 'track' mark (the connection still retains the 'track' mark). - - b) When HIGH_ROUTE_MARKS=Yes, you can still clear the mark on packets - in the PREROUTING chain (i.e., you can specify a mark value of zero). +1) 'shorewall refresh' once again refreshes the tcrules and traffic shaping. Migration Considerations: @@ -151,11 +125,7 @@ Migration Considerations: In no case is it possible to continue a quoted string over multiple lines without having additional whitespace inserted into the string. -6) The "shorewall refresh" command no longer refreshes traffic shaping. - Use "shorewall restart" instead if you need to reprocess the - tcrules, tcdevices and tcclasses files. - -7) Beginning with this release, the way in which packet marking in the +6) Beginning with this release, the way in which packet marking in the PREROUTING chain interracts with the 'track' option in /etc/shorewall/providers has changed in two ways: @@ -550,3 +520,17 @@ New Features: d) When you SAVE or RESTORE in tcrules, only the TC mark value is saved or restored. Shorewall handles saving and restoring the routing (provider) marks. + +12) A TOS column has been added to /etc/shorewall/tcrules. This allows marking + based on the contents of the TOS field in the packet header. + +13) Beginning with this release, the way in which packet marking in the + PREROUTING chain interracts with the 'track' option in /etc/shorewall/providers + has changed in two ways: + + a) Packets *arriving* on a tracked interface are now passed to the PREROUTING + marking chain so that they may be marked with a mark other than the + 'track' mark (the connection still retains the 'track' mark). + + b) When HIGH_ROUTE_MARKS=Yes, you can still clear the mark on packets + in the PREROUTING chain (i.e., you can specify a mark value of zero).