diff --git a/Shorewall/compiler b/Shorewall/compiler index 5226b31a4..ace88b396 100755 --- a/Shorewall/compiler +++ b/Shorewall/compiler @@ -6,11 +6,6 @@ # # (c) 1999,2000,2001,2002,2003,2004,2005,2006 - Tom Eastep (teastep@shorewall.net) # -# tcstart from tc4shorewall Version 0.5 -# (c) 2005 Arne Bernin -# Modified by Tom Eastep for integration into the Shorewall distribution -# published under GPL Version 2# -# # Complete documentation is available at http://shorewall.net # # This program is free software; you can redistribute it and/or modify @@ -1861,292 +1856,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 last_device= - 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_to_kbit $1) - echo $(( $rate * ( 128 / $r2q ) )) - } - - # 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 dev indent - - dev=$(chain_base $device) - - save_command "if qt ip link ls dev $device; then" - indent="$INDENT" - INDENT="$INDENT " - save_command ${dev}_exists=Yes - save_command qt tc qdisc del dev $device root - save_command qt tc qdisc del dev $device ingress - - defmark=$(get_defmark_for_dev $device) - - run_tc qdisc add dev $device root handle $devnum: htb default 1$defmark - - save_command "${dev}_mtu=\$(get_device_mtu $device)" - run_tc "class add dev $device parent $devnum: classid $devnum:1 htb rate $outband mtu \$${dev}_mtu" - - 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 ${dev}_devnum=$devnum - devnum=$(($devnum + 1)) - - save_progress_message_short " TC Device $tcdev defined." - INDENT="$indent" - save_command else - INDENT="$INDENT " - save_command error_message "\"WARNING: Device $device not found -- traffic-shaping configuration skipped\"" - save_command "${dev}_exists=" - INDENT="$indent" - save_command "fi" - save_command - - return 0 - } - - add_tc_class() { - local full classid tospair tosmask quantum indent - - dev=$(chain_base $device) - - save_command "if [ -n \"\$${dev}_exists\" ] ; then" - indent="$INDENT" - INDENT="$INDENT " - - 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=\$${dev}_devnum - # - # Convert HEX/OCTAL mark representation to decimal - # - mark=$(($mark)) - - classid=$devnum:1$mark - - [ -n "$devnum" ] || fatal_error "Device $device not defined in $devfile" - - quantum=$(calculate_quantum $rate) - - save_command "[ \$${dev}_mtu -gt $quantum ] && quantum=\$${dev}_mtu || quantum=$quantum" - run_tc "class add dev $device parent $devnum:1 classid $classid htb rate $rate ceil $ceil prio $prio mtu \$${dev}_mtu quantum \$quantum" - - 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 - - save_progress_message_short " TC Class $tcdev defined." - INDENT="$indent" - save_command fi - save_command - - return 0 - } - - 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; do - expandv device inband outband - tcdev="$device $inband $outband" - add_root_tc && progress_message " 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 " TC Class $tcdev defined." - last_device=$device - done < $TMP_DIR/tcclasses - fi -} - # # Process a TC Rule - $MARKING_CHAIN is assumed to contain the name of the # default marking chain @@ -2551,6 +2260,8 @@ setup_tc1() { save_progress_message "Setting up Traffic Control..." append_file $TC_SCRIPT elif [ -n "$TC_ENABLED" ]; then + strip_file_and_lib_load tcdevices tc + strip_file_and_lib_load tcclasses tc setup_traffic_shaping fi } diff --git a/Shorewall/firewall b/Shorewall/firewall index 17669d7e0..3cb7921c4 100755 --- a/Shorewall/firewall +++ b/Shorewall/firewall @@ -546,368 +546,6 @@ check_disabled_startup() { fi } -# -# Add a host or networks to a zone -# -add_to_zone() # $1...${n-1} = [:] $n = zone -{ - local interface host zone z h z1 z2 chain - local dhcp_interfaces blacklist_interfaces maclist_interfaces - local tcpflags_interfaces newhostlist= - local rulenum source_chain dest_hosts iface hosts hostlist= - - nat_chain_exists() # $1 = chain name - { - qt $IPTABLES -t nat -L $1 -n - } - - do_iptables() # $@ = command - { - [ -n "$BRIDGING" ] && [ -f $TMP_DIR/physdev ] && rm -f $TMP_DIR/physdev - [ -n "$IPRANGE_MATCH" ] && [ -f $TMP_DIR/iprange ] && rm -f $TMP_DIR/iprange - - if ! $IPTABLES $@ ; then - error_message "ERROR: Can't add $newhost to zone $zone" - fi - } - - # - # Load $zones - # - determine_zones - # - # Validate Interfaces File - # - validate_interfaces_file - # - # Validate Hosts File - # - validate_hosts_file - # - # Validate IPSec File - # - f=$(find_file $IPSECFILE) - - [ -f $f ] && setup_ipsec $f - # - # Normalize host list - # - while [ $# -gt 1 ]; do - interface=${1%%:*} - host=${1#*:} - # - # Be sure that the interface was dynamic at last [re]start - # - if ! chain_exists $(input_chain $interface) ; then - startup_error "Unknown interface $interface" - fi - - if ! chain_exists $(dynamic_in $interface) ; then - startup_error "At last Shorewall [re]start, DYNAMIC_ZONES=No in shorewall.conf" - fi - - if [ -z "$host" ]; then - hostlist="$hostlist $interface:0.0.0.0/0" - else - for h in $(separate_list $host); do - hostlist="$hostlist $interface:$h" - done - fi - - shift - done - # - # Validate Zone - # - zone=$1 - - validate_zone $zone || startup_error "Unknown zone: $zone" - - [ "$zone" = $FW ] && startup_error "Can't add $1 to firewall zone" - - # - # Be sure that Shorewall has been restarted using a DZ-aware version of the code - # - [ -f ${VARDIR}/chains ] || startup_error "${VARDIR}/chains -- file not found" - [ -f ${VARDIR}/zones ] || startup_error "${VARDIR}/zones -- file not found" - # - # Check for duplicates and create a new zone state file - # - > ${VARDIR}/zones_$$ - - while read z type hosts; do - if [ "$z" = "$zone" ]; then - for h in $hostlist; do - list_search $h $hosts - if [ "$?" -gt 0 ]; then - newhostlist="$newhostlist $h" - else - error_message "$h already in zone $zone" - fi - done - - [ -z "$hosts" ] && hosts=$newhostlist || hosts="$hosts $newhostlist" - fi - - eval ${z}_hosts=\"$hosts\" - - echo "$z $type $hosts" >> ${VARDIR}/zones_$$ - done < ${VARDIR}/zones - - mv -f ${VARDIR}/zones_$$ ${VARDIR}/zones - - TERMINATOR=fatal_error - # - # Create a new Zone state file - # - for newhost in $newhostlist; do - # - # Isolate interface and host parts - # - interface=${newhost%%:*} - host=${newhost#*:} - # - # If the zone passed in the command has a dnat chain then insert a rule in - # the nat table PREROUTING chain to jump to that chain when the source - # matches the new host(s)# - # - chain=${zone}_dnat - - if nat_chain_exists $chain; then - do_iptables -t nat -A $(dynamic_in $interface) $(source_ip_range $host) $(match_ipsec_in $zone $newhost) -j $chain - fi - # - # Insert new rules into the filter table for the passed interface - # - while read z1 z2 chain; do - [ "$z1" = "$z2" ] && op="-I" || op="-A" - if [ "$z1" = "$zone" ]; then - if [ "$z2" = "$FW" ]; then - do_iptables $op $(dynamic_in $interface) $(match_source_hosts $host) $(match_ipsec_in $z1 $newhost) -j $chain - else - source_chain=$(dynamic_fwd $interface) - if is_ipsec_host $z1 $newhost ; then - do_iptables $op $source_chain $(match_source_hosts $host) $(match_ipsec_in $z1 $newhost) -j ${z1}_frwd - else - eval dest_hosts=\"\$${z2}_hosts\" - - for h in $dest_hosts; do - iface=${h%%:*} - hosts=${h#*:} - - if [ "$iface" != "$interface" -o "$hosts" != "$host" ]; then - do_iptables $op $source_chain $(match_source_hosts $host) -o $iface $(match_dest_hosts $hosts) $(match_ipsec_out $z2 $h) -j $chain - fi - done - fi - fi - elif [ "$z2" = "$zone" ]; then - if [ "$z1" = "$FW" ]; then - # - # Add a rule to the dynamic out chain for the interface - # - do_iptables $op $(dynamic_out $interface) $(match_dest_hosts $host) $(match_ipsec_out $z2 $newhost) -j $chain - else - eval source_hosts=\"\$${z1}_hosts\" - - for h in $source_hosts; do - iface=${h%%:*} - hosts=${h#*:} - - if [ "$iface" != "$interface" -o "$hosts" != "$host" ]; then - if is_ipsec_host $z1 $h; then - do_iptables $op ${z1}_dyn -o $interface $(match_dest_hosts $host) $(match_ipsec_out $z2 $newhost) -j $chain - else - do_iptables $op $(dynamic_fwd $iface) $(match_source_hosts $hosts) -o $interface $(match_dest_hosts $host) $(match_ipsec_out $z2 $newhost) -j $chain - fi - fi - done - fi - fi - done < ${VARDIR}/chains - - progress_message "$newhost added to zone $zone" - - done - - rm -rf $TMP_DIR -} - -# -# Delete a host or networks from a zone -# -delete_from_zone() # $1 = [:] $2 = zone -{ - local interface host zone z h z1 z2 chain delhost - local dhcp_interfaces blacklist_interfaces maclist_interfaces tcpflags_interfaces - local rulenum source_chain dest_hosts iface hosts hostlist= - - # - # Load $zones - # - determine_zones - # - # Validate Interfaces File - # - validate_interfaces_file - # - # Validate Hosts File - # - validate_hosts_file - # - # Validate IPSec File - # - f=$(find_file ipsec) - - [ -f $f ] && setup_ipsec $f - - # - # Normalize host list - # - while [ $# -gt 1 ]; do - interface=${1%%:*} - host=${1#*:} - # - # Be sure that the interface was dynamic at last [re]start - # - if ! chain_exists $(input_chain $interface) ; then - startup_error "Unknown interface $interface" - fi - - if ! chain_exists $(dynamic_in $interface) ; then - startup_error "At last Shorewall [re]start, DYNAMIC_ZONES=No in shorewall.conf" - fi - - if [ -z "$host" ]; then - hostlist="$hostlist $interface:0.0.0.0/0" - else - for h in $(separate_list $host); do - hostlist="$hostlist $interface:$h" - done - fi - - shift - done - # - # Validate Zone - # - zone=$1 - - validate_zone $zone || startup_error "Unknown zone: $zone" - - [ "$zone" = $FW ] && startup_error "Can't delete from the firewall zone" - - # - # Be sure that Shorewall has been restarted using a DZ-aware version of the code - # - [ -f ${VARDIR}/chains ] || startup_error "${VARDIR}/chains -- file not found" - [ -f ${VARDIR}/zones ] || startup_error "${VARDIR}/zones -- file not found" - # - # Delete the passed hosts from the zone state file - # - > ${VARDIR}/zones_$$ - - while read z hosts; do - if [ "$z" = "$zone" ]; then - temp=$hosts - hosts= - - for host in $hostlist; do - found= - for h in $temp; do - if [ "$h" = "$host" ]; then - found=Yes - break - fi - done - - [ -n "$found" ] || error_message "WARNING: $host does not appear to be in zone $zone" - done - - for h in $temp; do - found= - for host in $hostlist; do - if [ "$h" = "$host" ]; then - found=Yes - break - fi - done - - [ -n "$found" ] || hosts="$hosts $h" - done - fi - - eval ${z}_hosts=\"$hosts\" - - echo "$z $hosts" >> ${VARDIR}/zones_$$ - done < ${VARDIR}/zones - - mv -f ${VARDIR}/zones_$$ ${VARDIR}/zones - - TERMINATOR=fatal_error - - for delhost in $hostlist; do - interface=${delhost%%:*} - host=${delhost#*:} - # - # Delete any nat table entries for the host(s) - # - qt_iptables -t nat -D $(dynamic_in $interface) $(match_source_hosts $host) $(match_ipsec_in $zone $delhost) -j ${zone}_dnat - # - # Delete rules rules the input chains for the passed interface - # - while read z1 z2 chain; do - if [ "$z1" = "$zone" ]; then - if [ "$z2" = "$FW" ]; then - qt_iptables -D $(dynamic_in $interface) $(match_source_hosts $host) $(match_ipsec_in $z1 $delhost) -j $chain - else - source_chain=$(dynamic_fwd $interface) - if is_ipsec_host $z1 $delhost ; then - qt_iptables -D $source_chain $(match_source_hosts $host) $(match_ipsec_in $z1 $newhost) -j ${z1}_frwd - else - eval dest_hosts=\"\$${z2}_hosts\" - - [ "$z2" = "$zone" ] && dest_hosts="$dest_hosts $hostlist" - - for h in $dest_hosts; do - iface=${h%%:*} - hosts=${h#*:} - - if [ "$iface" != "$interface" -o "$hosts" != "$host" ]; then - qt_iptables -D $source_chain $(match_source_hosts $host) -o $iface $(match_dest_hosts $hosts) $(match_ipsec_out $z2 $h) -j $chain - fi - done - fi - fi - elif [ "$z2" = "$zone" ]; then - if [ "$z1" = "$FW" ]; then - qt_iptables -D $(dynamic_out $interface) $(match_dest_hosts $host) $(match_ipsec_out $z2 $delhost) -j $chain - else - eval source_hosts=\"\$${z1}_hosts\" - - for h in $source_hosts; do - iface=${h%%:*} - hosts=${h#*:} - - if [ "$iface" != "$interface" -o "$hosts" != "$host" ]; then - if is_ipsec_host $z1 $h; then - qt_iptables -D ${z1}_dyn -o $interface $(match_dest_hosts $host) $(match_ipsec_out $z2 $delhost) -j $chain - else - qt_iptables -D $(dynamic_fwd $iface) $(match_source_hosts $hosts) -o $interface $(match_dest_hosts $host) $(match_ipsec_out $z2 $delhost) -j $chain - fi - fi - done - fi - fi - done < ${VARDIR}/chains - - progress_message "$delhost removed from zone $zone" - - done - - rm -rf $TMP_DIR -} - # # Give Usage Information # @@ -995,6 +633,7 @@ case "$COMMAND" in add) [ $# -lt 3 ] && usage do_initialize + lib_load dynamiczones "The add command" my_mutex_on if ! shorewall_is_started ; then echo "Shorewall Not Started" @@ -1009,6 +648,7 @@ case "$COMMAND" in delete) [ $# -lt 3 ] && usage + lib_load dynamiczones "The delete command" do_initialize my_mutex_on if ! shorewall_is_started ; then diff --git a/Shorewall/functions b/Shorewall/functions index df85c7790..7ac8ab7c0 100644 --- a/Shorewall/functions +++ b/Shorewall/functions @@ -1770,11 +1770,13 @@ lib_avail() # $1 = Name of the Library [ -f ${SHAREDIR}/lib.$1 ] } -strip_file_and_lib_load() # $1 = logical file name, $3 = Error message heading if the library cannot be found +strip_file_and_lib_load() # $1 = logical file name, $2 = library to load if the stripped file is non-empty { - strip_file $1 + local f=$(find_file $1) - [ -s $TMP_DIR/$1 ] && lib_load $2 "$3" + strip_file $1 $f + + [ -s $TMP_DIR/$1 ] && lib_load $2 "A non-empty $1 file ($f)" } # @@ -1785,7 +1787,6 @@ strip_file_and_lib_load() # $1 = logical file name, $3 = Error message heading i LEFTSHIFT='<<' - # # Convert an IP address in dot quad format to an integer # @@ -3073,8 +3074,13 @@ do_initialize() { BLACKLISTNEWONLY=$(added_param_value_no BLACKLISTNEWONLY $BLACKLISTNEWONLY) DISABLE_IPV6=$(added_param_value_no DISABLE_IPV6 $DISABLE_IPV6) BRIDGING=$(added_param_value_no BRIDGING $BRIDGING) + DYNAMIC_ZONES=$(added_param_value_no DYNAMIC_ZONES $DYNAMIC_ZONES) - [ -n "$DYNAMIC_ZONES" -a -n "$EXPORT" ] && fatal_error "DYNAMIC_ZONES=Yes is incompatible with the -e option" + if [ -n "$DYNAMIC_ZONES" ]; then + [ -n "$EXPORT" ] && fatal_error "DYNAMIC_ZONES=Yes is incompatible with the -e option" + lib_avail dynamiczones || error_message "WARNING: DYNAMIC_ZONES=Yes requires the Shorewall dynamiczones library (${SHAREDIR}/lib.dynamiczones) which is not installed" + fi + STARTUP_ENABLED=$(added_param_value_yes STARTUP_ENABLED $STARTUP_ENABLED) RETAIN_ALIASES=$(added_param_value_no RETAIN_ALIASES $RETAIN_ALIASES) [ -n "${ADD_IP_ALIASES}${ADD_SNAT_ALIASES}" ] || RETAIN_ALIASES= diff --git a/Shorewall/install.sh b/Shorewall/install.sh index fc46e7696..0f1393fec 100755 --- a/Shorewall/install.sh +++ b/Shorewall/install.sh @@ -647,6 +647,15 @@ for f in macro.* ; do echo "Macro ${f#*.} file installed as ${PREFIX}/usr/share/shorewall/$f" done # +# Install the libraries +# +for f in lib.* ; do + if [ -f $f ]; then + install_file $f ${PREFIX}/usr/share/shorewall/$f 0644 + echo "Macro ${f#*.} file installed as ${PREFIX}/usr/share/shorewall/$f" + fi +done +# # Install the program skeleton files # for f in prog.* ; do diff --git a/Shorewall/lib.dynamiczones b/Shorewall/lib.dynamiczones index f11059795..2d5dd28af 100644 --- a/Shorewall/lib.dynamiczones +++ b/Shorewall/lib.dynamiczones @@ -6,11 +6,6 @@ # # (c) 1999,2000,2001,2002,2003,2004,2005,2006 - Tom Eastep (teastep@shorewall.net) # -# tcstart from tc4shorewall Version 0.5 -# (c) 2005 Arne Bernin -# Modified by Tom Eastep for integration into the Shorewall distribution -# published under GPL Version 2# -# # Complete documentation is available at http://shorewall.net # # This program is free software; you can redistribute it and/or modify @@ -26,3 +21,364 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA +# +# Add a host or networks to a zone +# +add_to_zone() # $1...${n-1} = [:] $n = zone +{ + local interface host zone z h z1 z2 chain + local dhcp_interfaces blacklist_interfaces maclist_interfaces + local tcpflags_interfaces newhostlist= + local rulenum source_chain dest_hosts iface hosts hostlist= + + nat_chain_exists() # $1 = chain name + { + qt $IPTABLES -t nat -L $1 -n + } + + do_iptables() # $@ = command + { + [ -n "$BRIDGING" ] && [ -f $TMP_DIR/physdev ] && rm -f $TMP_DIR/physdev + [ -n "$IPRANGE_MATCH" ] && [ -f $TMP_DIR/iprange ] && rm -f $TMP_DIR/iprange + + if ! $IPTABLES $@ ; then + error_message "ERROR: Can't add $newhost to zone $zone" + fi + } + + # + # Load $zones + # + determine_zones + # + # Validate Interfaces File + # + validate_interfaces_file + # + # Validate Hosts File + # + validate_hosts_file + # + # Validate IPSec File + # + f=$(find_file $IPSECFILE) + + [ -f $f ] && setup_ipsec $f + # + # Normalize host list + # + while [ $# -gt 1 ]; do + interface=${1%%:*} + host=${1#*:} + # + # Be sure that the interface was dynamic at last [re]start + # + if ! chain_exists $(input_chain $interface) ; then + startup_error "Unknown interface $interface" + fi + + if ! chain_exists $(dynamic_in $interface) ; then + startup_error "At last Shorewall [re]start, DYNAMIC_ZONES=No in shorewall.conf" + fi + + if [ -z "$host" ]; then + hostlist="$hostlist $interface:0.0.0.0/0" + else + for h in $(separate_list $host); do + hostlist="$hostlist $interface:$h" + done + fi + + shift + done + # + # Validate Zone + # + zone=$1 + + validate_zone $zone || startup_error "Unknown zone: $zone" + + [ "$zone" = $FW ] && startup_error "Can't add $1 to firewall zone" + + # + # Be sure that Shorewall has been restarted using a DZ-aware version of the code + # + [ -f ${VARDIR}/chains ] || startup_error "${VARDIR}/chains -- file not found" + [ -f ${VARDIR}/zones ] || startup_error "${VARDIR}/zones -- file not found" + # + # Check for duplicates and create a new zone state file + # + > ${VARDIR}/zones_$$ + + while read z type hosts; do + if [ "$z" = "$zone" ]; then + for h in $hostlist; do + list_search $h $hosts + if [ "$?" -gt 0 ]; then + newhostlist="$newhostlist $h" + else + error_message "$h already in zone $zone" + fi + done + + [ -z "$hosts" ] && hosts=$newhostlist || hosts="$hosts $newhostlist" + fi + + eval ${z}_hosts=\"$hosts\" + + echo "$z $type $hosts" >> ${VARDIR}/zones_$$ + done < ${VARDIR}/zones + + mv -f ${VARDIR}/zones_$$ ${VARDIR}/zones + + TERMINATOR=fatal_error + # + # Create a new Zone state file + # + for newhost in $newhostlist; do + # + # Isolate interface and host parts + # + interface=${newhost%%:*} + host=${newhost#*:} + # + # If the zone passed in the command has a dnat chain then insert a rule in + # the nat table PREROUTING chain to jump to that chain when the source + # matches the new host(s)# + # + chain=${zone}_dnat + + if nat_chain_exists $chain; then + do_iptables -t nat -A $(dynamic_in $interface) $(source_ip_range $host) $(match_ipsec_in $zone $newhost) -j $chain + fi + # + # Insert new rules into the filter table for the passed interface + # + while read z1 z2 chain; do + [ "$z1" = "$z2" ] && op="-I" || op="-A" + if [ "$z1" = "$zone" ]; then + if [ "$z2" = "$FW" ]; then + do_iptables $op $(dynamic_in $interface) $(match_source_hosts $host) $(match_ipsec_in $z1 $newhost) -j $chain + else + source_chain=$(dynamic_fwd $interface) + if is_ipsec_host $z1 $newhost ; then + do_iptables $op $source_chain $(match_source_hosts $host) $(match_ipsec_in $z1 $newhost) -j ${z1}_frwd + else + eval dest_hosts=\"\$${z2}_hosts\" + + for h in $dest_hosts; do + iface=${h%%:*} + hosts=${h#*:} + + if [ "$iface" != "$interface" -o "$hosts" != "$host" ]; then + do_iptables $op $source_chain $(match_source_hosts $host) -o $iface $(match_dest_hosts $hosts) $(match_ipsec_out $z2 $h) -j $chain + fi + done + fi + fi + elif [ "$z2" = "$zone" ]; then + if [ "$z1" = "$FW" ]; then + # + # Add a rule to the dynamic out chain for the interface + # + do_iptables $op $(dynamic_out $interface) $(match_dest_hosts $host) $(match_ipsec_out $z2 $newhost) -j $chain + else + eval source_hosts=\"\$${z1}_hosts\" + + for h in $source_hosts; do + iface=${h%%:*} + hosts=${h#*:} + + if [ "$iface" != "$interface" -o "$hosts" != "$host" ]; then + if is_ipsec_host $z1 $h; then + do_iptables $op ${z1}_dyn -o $interface $(match_dest_hosts $host) $(match_ipsec_out $z2 $newhost) -j $chain + else + do_iptables $op $(dynamic_fwd $iface) $(match_source_hosts $hosts) -o $interface $(match_dest_hosts $host) $(match_ipsec_out $z2 $newhost) -j $chain + fi + fi + done + fi + fi + done < ${VARDIR}/chains + + progress_message "$newhost added to zone $zone" + + done + + rm -rf $TMP_DIR +} + +# +# Delete a host or networks from a zone +# +delete_from_zone() # $1 = [:] $2 = zone +{ + local interface host zone z h z1 z2 chain delhost + local dhcp_interfaces blacklist_interfaces maclist_interfaces tcpflags_interfaces + local rulenum source_chain dest_hosts iface hosts hostlist= + + # + # Load $zones + # + determine_zones + # + # Validate Interfaces File + # + validate_interfaces_file + # + # Validate Hosts File + # + validate_hosts_file + # + # Validate IPSec File + # + f=$(find_file ipsec) + + [ -f $f ] && setup_ipsec $f + + # + # Normalize host list + # + while [ $# -gt 1 ]; do + interface=${1%%:*} + host=${1#*:} + # + # Be sure that the interface was dynamic at last [re]start + # + if ! chain_exists $(input_chain $interface) ; then + startup_error "Unknown interface $interface" + fi + + if ! chain_exists $(dynamic_in $interface) ; then + startup_error "At last Shorewall [re]start, DYNAMIC_ZONES=No in shorewall.conf" + fi + + if [ -z "$host" ]; then + hostlist="$hostlist $interface:0.0.0.0/0" + else + for h in $(separate_list $host); do + hostlist="$hostlist $interface:$h" + done + fi + + shift + done + # + # Validate Zone + # + zone=$1 + + validate_zone $zone || startup_error "Unknown zone: $zone" + + [ "$zone" = $FW ] && startup_error "Can't delete from the firewall zone" + + # + # Be sure that Shorewall has been restarted using a DZ-aware version of the code + # + [ -f ${VARDIR}/chains ] || startup_error "${VARDIR}/chains -- file not found" + [ -f ${VARDIR}/zones ] || startup_error "${VARDIR}/zones -- file not found" + # + # Delete the passed hosts from the zone state file + # + > ${VARDIR}/zones_$$ + + while read z hosts; do + if [ "$z" = "$zone" ]; then + temp=$hosts + hosts= + + for host in $hostlist; do + found= + for h in $temp; do + if [ "$h" = "$host" ]; then + found=Yes + break + fi + done + + [ -n "$found" ] || error_message "WARNING: $host does not appear to be in zone $zone" + done + + for h in $temp; do + found= + for host in $hostlist; do + if [ "$h" = "$host" ]; then + found=Yes + break + fi + done + + [ -n "$found" ] || hosts="$hosts $h" + done + fi + + eval ${z}_hosts=\"$hosts\" + + echo "$z $hosts" >> ${VARDIR}/zones_$$ + done < ${VARDIR}/zones + + mv -f ${VARDIR}/zones_$$ ${VARDIR}/zones + + TERMINATOR=fatal_error + + for delhost in $hostlist; do + interface=${delhost%%:*} + host=${delhost#*:} + # + # Delete any nat table entries for the host(s) + # + qt_iptables -t nat -D $(dynamic_in $interface) $(match_source_hosts $host) $(match_ipsec_in $zone $delhost) -j ${zone}_dnat + # + # Delete rules rules the input chains for the passed interface + # + while read z1 z2 chain; do + if [ "$z1" = "$zone" ]; then + if [ "$z2" = "$FW" ]; then + qt_iptables -D $(dynamic_in $interface) $(match_source_hosts $host) $(match_ipsec_in $z1 $delhost) -j $chain + else + source_chain=$(dynamic_fwd $interface) + if is_ipsec_host $z1 $delhost ; then + qt_iptables -D $source_chain $(match_source_hosts $host) $(match_ipsec_in $z1 $newhost) -j ${z1}_frwd + else + eval dest_hosts=\"\$${z2}_hosts\" + + [ "$z2" = "$zone" ] && dest_hosts="$dest_hosts $hostlist" + + for h in $dest_hosts; do + iface=${h%%:*} + hosts=${h#*:} + + if [ "$iface" != "$interface" -o "$hosts" != "$host" ]; then + qt_iptables -D $source_chain $(match_source_hosts $host) -o $iface $(match_dest_hosts $hosts) $(match_ipsec_out $z2 $h) -j $chain + fi + done + fi + fi + elif [ "$z2" = "$zone" ]; then + if [ "$z1" = "$FW" ]; then + qt_iptables -D $(dynamic_out $interface) $(match_dest_hosts $host) $(match_ipsec_out $z2 $delhost) -j $chain + else + eval source_hosts=\"\$${z1}_hosts\" + + for h in $source_hosts; do + iface=${h%%:*} + hosts=${h#*:} + + if [ "$iface" != "$interface" -o "$hosts" != "$host" ]; then + if is_ipsec_host $z1 $h; then + qt_iptables -D ${z1}_dyn -o $interface $(match_dest_hosts $host) $(match_ipsec_out $z2 $delhost) -j $chain + else + qt_iptables -D $(dynamic_fwd $iface) $(match_source_hosts $hosts) -o $interface $(match_dest_hosts $host) $(match_ipsec_out $z2 $delhost) -j $chain + fi + fi + done + fi + fi + done < ${VARDIR}/chains + + progress_message "$delhost removed from zone $zone" + + done + + rm -rf $TMP_DIR +} diff --git a/Shorewall/lib.tc b/Shorewall/lib.tc new file mode 100644 index 000000000..800fc2016 --- /dev/null +++ b/Shorewall/lib.tc @@ -0,0 +1,310 @@ +#!/bin/sh +# +# Shorewall 3.2 -- /usr/share/shorewall/lib.tc +# +# This program is under GPL [http://www.gnu.org/copyleft/gpl.htm] +# +# (c) 1999,2000,2001,2002,2003,2004,2005,2006 - Tom Eastep (teastep@shorewall.net) +# +# tcstart from tc4shorewall Version 0.5 +# (c) 2005 Arne Bernin +# Modified by Tom Eastep for integration into the Shorewall distribution +# published under GPL Version 2# +# +# 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., 675 Mass Ave, Cambridge, MA 02139, USA + +# +# 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 last_device= + 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_to_kbit $1) + echo $(( $rate * ( 128 / $r2q ) )) + } + + # 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 dev indent + + dev=$(chain_base $device) + + save_command "if qt ip link ls dev $device; then" + indent="$INDENT" + INDENT="$INDENT " + save_command ${dev}_exists=Yes + save_command qt tc qdisc del dev $device root + save_command qt tc qdisc del dev $device ingress + + defmark=$(get_defmark_for_dev $device) + + run_tc qdisc add dev $device root handle $devnum: htb default 1$defmark + + save_command "${dev}_mtu=\$(get_device_mtu $device)" + run_tc "class add dev $device parent $devnum: classid $devnum:1 htb rate $outband mtu \$${dev}_mtu" + + 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 ${dev}_devnum=$devnum + devnum=$(($devnum + 1)) + + save_progress_message_short " TC Device $tcdev defined." + INDENT="$indent" + save_command else + INDENT="$INDENT " + save_command error_message "\"WARNING: Device $device not found -- traffic-shaping configuration skipped\"" + save_command "${dev}_exists=" + INDENT="$indent" + save_command "fi" + save_command + + return 0 + } + + add_tc_class() { + local full classid tospair tosmask quantum indent + + dev=$(chain_base $device) + + save_command "if [ -n \"\$${dev}_exists\" ] ; then" + indent="$INDENT" + INDENT="$INDENT " + + 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=\$${dev}_devnum + # + # Convert HEX/OCTAL mark representation to decimal + # + mark=$(($mark)) + + classid=$devnum:1$mark + + [ -n "$devnum" ] || fatal_error "Device $device not defined in $devfile" + + quantum=$(calculate_quantum $rate) + + save_command "[ \$${dev}_mtu -gt $quantum ] && quantum=\$${dev}_mtu || quantum=$quantum" + run_tc "class add dev $device parent $devnum:1 classid $classid htb rate $rate ceil $ceil prio $prio mtu \$${dev}_mtu quantum \$quantum" + + 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 + + save_progress_message_short " TC Class $tcdev defined." + INDENT="$indent" + save_command fi + save_command + + return 0 + } + + 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; do + expandv device inband outband + tcdev="$device $inband $outband" + add_root_tc && progress_message " 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 " TC Class $tcdev defined." + last_device=$device + done < $TMP_DIR/tcclasses + fi +} diff --git a/Shorewall/shorewall.spec b/Shorewall/shorewall.spec index 681cd7460..2b9439986 100644 --- a/Shorewall/shorewall.spec +++ b/Shorewall/shorewall.spec @@ -115,6 +115,8 @@ fi %attr(0555,root,root) /usr/share/shorewall/firewall %attr(0555,root,root) /usr/share/shorewall/help %attr(0644,root,root) /usr/share/shorewall/Limit +%attr(0444,root,root) /usr/share/shorewall/lib.dynamiczones +%attr(0444,root,root) /usr/share/shorewall/lib.tc %attr(0644,root,root) /usr/share/shorewall/macro.AllowICMPs %attr(0644,root,root) /usr/share/shorewall/macro.Amanda %attr(0644,root,root) /usr/share/shorewall/macro.Auth