diff --git a/Shorewall/changelog.txt b/Shorewall/changelog.txt index a5852ac7d..6560ecbef 100644 --- a/Shorewall/changelog.txt +++ b/Shorewall/changelog.txt @@ -2,6 +2,8 @@ Changes in 3.2.0 Beta 4 1) Fix 'routeback' with bridge ports. +2) Add support for explicit routing rules. + Changes in 3.2.0 Beta 3 1) Correct handling of verbosity in the 'try' command. diff --git a/Shorewall/compiler b/Shorewall/compiler index 514fdeb4a..74e9bb5a9 100755 --- a/Shorewall/compiler +++ b/Shorewall/compiler @@ -693,14 +693,14 @@ get_set_flags() # $1 = set name and optional [levels], $2 = src or dst { # # Note: There is a lot of unnecessary evaluation in this function just so my text - # editor doesn't get lost trying to follow the shell syntax for highlighting. + # editor (kate) doesn't get lost trying to follow the shell syntax for highlighting. # - local temp setname=$1 options=$2 firstcase='*\[[1-6]\]' secondcase='*\[*\]' + local temp setname=$1 options=$2 [ -n "$IPSET_MATCH" ] || fatal_error "Your kernel and/or iptables does not include ipset match: $1" case $1 in - $firstcase) + *\[[1-6]\]) eval temp='${1#*\[}' eval temp='${temp%\]}' eval setname='${1%\[*}' @@ -709,7 +709,7 @@ get_set_flags() # $1 = set name and optional [levels], $2 = src or dst temp=$(($temp - 1)) done ;; - $secondcase) + *\[*\]) eval options='${1#*\[}' eval options='${options%\]}' eval setname='${1%\[*}' @@ -1270,7 +1270,7 @@ rulenum=0 find_interface_addresses $interface | while read address; do qt ip rule del from \$address - pref=\$((20000 + \$rulenum * 1000 + $number )) + pref=\$((20000 + ($number - 1) * 256 + \$rulenum )) rulenum=\$((\$rulenum + 1)) run_ip rule add from \$address pref \$pref table $number done @@ -1287,6 +1287,56 @@ __EOF__ fi } + verify_provider() + { + local p n + + for p in $PROVIDERS; do + [ "$p" = "$1" ] && return 0 + eval n=\$${p}_number} + [ "$n" = "$1" ] && return 0 + done + + fatal_error "Unknown provider $1 in rtrule \"$rule\"" + } + + add_an_rtrule() + { + verify_provider $provider + + [ "x$source" = x- ] && source= + [ "x$dest" = x- ] && dest= || dest="to $dest" + + [ -n "${source}${dest}" ] || fatal_error "You must specify either the source or destination in an rt rule: \"$rule\"" + + [ -n "$source" ] && case $source in + *:*) + source="iif ${source%:*} from ${source#*:}" + ;; + *.*.*) + source="from $source" + ;; + *) + source="iif $source" + ;; + esac + + case "$priority" in + [0-9][0-9][0-9][0-9]|[0-9][0-9][0-9][0-9][0-9]) + ;; + *) + fatal_error "Invalid priority ($priority) in rule \"$rule\"" + ;; + esac + + priority="priority $priority" + + save_command "qt ip rule del $source $dest $priority" + save_command "run_ip rule add $source $dest $priority table $provider" + + progress_message "Routing rule \"$rule\" $DONE" + } + strip_file providers $1 if [ -s $TMP_DIR/providers ]; then @@ -1333,6 +1383,20 @@ __EOF__ \${echobin:-echo} -e "$number\t$table" >> /etc/iproute2/rt_tables __EOF__ done + + f=$(find_file rtrules) + + if [ -f $f ]; then + progress_message2 "$DOING $f..." + + strip_file rtrules $f + + while read provider priority source dest; do + expandv priority provider source dest + rule="$priority $provider $source $dest" + add_an_rtrule + done < $TMP_DIR/rtrules + fi fi save_command "run_ip route flush cache" @@ -3113,6 +3177,11 @@ process_tc_rule() [ $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) " ;; @@ -3160,11 +3229,17 @@ process_tc_rule() 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" + [ "$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) " ;; diff --git a/Shorewall/functions b/Shorewall/functions index e630cb71d..d8bc5d9d3 100644 --- a/Shorewall/functions +++ b/Shorewall/functions @@ -292,12 +292,12 @@ separate_list() { *\[*\]*) # # Where we need to embed comma-separated lists within lists, we enclose them - # within square brackets + # within square brackets (extra 'evals' are to keep my text editor (kate) from getting lost). # - firstpart=${list%%\[*} - lastpart=${list#*\[} - enclosure=${lastpart%%\]*} - lastpart=${lastpart#*\]} + eval 'firstpart=${list%%\[*}' + eval 'lastpart=${list#*\[}' + eval 'enclosure=${lastpart%%\]*}' + eval 'lastpart=${lastpart#*\]}' case $lastpart in \,*) case $firstpart in @@ -593,10 +593,6 @@ strip_file() # $1 = Base Name of the file, $2 = Full Name of File (optional) # behavior when the shell only supports 32-bit signed arithmatic and # the IP address is 128.0.0.0 or 128.0.0.1. # -# -# So that emacs doesn't get lost, we use $LEFTSHIFT rather than << -# -LEFTSHIFT='<<' # # Convert an IP address in dot quad format to an integer @@ -609,7 +605,7 @@ decodeaddr() { IFS=. for x in $1; do - temp=$(( $(( $temp $LEFTSHIFT 8 )) | $x )) + temp=$(( $(( $temp << 8 )) | $x )) done echo $temp @@ -721,7 +717,7 @@ ip_range_explicit() { ip_netmask() { local vlsm=${1#*/} - [ $vlsm -eq 0 ] && echo 0 || echo $(( -1 $LEFTSHIFT $(( 32 - $vlsm )) )) + [ $vlsm -eq 0 ] && echo 0 || echo $(( -1 << $(( 32 - $vlsm )) )) } # @@ -742,7 +738,7 @@ ip_network() { ip_broadcast() { local x=$(( 32 - ${1#*/} )) - [ $x -eq 0 ] && echo -1 || echo $(( $(( 1 $LEFTSHIFT $x )) - 1 )) + [ $x -eq 0 ] && echo -1 || echo $(( $(( 1 << $x )) - 1 )) } # @@ -772,10 +768,10 @@ in_network() # $1 = IP address, $2 = CIDR network ip_vlsm() { local mask=$(decodeaddr $1) local vlsm=0 - local x=$(( 128 $LEFTSHIFT 24 )) # 0x80000000 + local x=$(( 128 << 24 )) # 0x80000000 while [ $(( $x & $mask )) -ne 0 ]; do - [ $mask -eq $x ] && mask=0 || mask=$(( $mask $LEFTSHIFT 1 )) # Not all shells shift 0x80000000 left properly. + [ $mask -eq $x ] && mask=0 || mask=$(( $mask << 1 )) # Not all shells shift 0x80000000 left properly. vlsm=$(($vlsm + 1)) done @@ -838,7 +834,7 @@ if_match() # $1 = Name in interfaces file - may end in "+" # source_ip_range() # $1 = Address or Address Range { - case $1 in + [ $# -gt 0 ] && case $1 in *.*.*.*-*.*.*.*) case $1 in !*) @@ -866,7 +862,7 @@ source_ip_range() # $1 = Address or Address Range # dest_ip_range() # $1 = Address or Address Range { - case $1 in + [ $# -gt 0 ] && case $1 in *.*.*.*-*.*.*.*) case $1 in !*) diff --git a/Shorewall/releasenotes.txt b/Shorewall/releasenotes.txt index 8730c02b0..066db7ae1 100644 --- a/Shorewall/releasenotes.txt +++ b/Shorewall/releasenotes.txt @@ -38,7 +38,55 @@ Problems Corrected in 3.2.0 Beta 4 Other changes in 3.2.0 Beta 4 -None. +1) Shorewall now includes support for explicit routing rules when the + /etc/shorewall/providers file is used. A new file, /etc/shorewall/rtrules + can be used to add routing rules based on packet source and/or + destination. + + The file has the following columns: + + PROVIDER The provider to route the traffic through. + May be expressed either as the provider name + or the provider number. + + PRIORITY + The rule's priority which determines the order + in which the rules are processed. + + 1000-1999 Before Shorewall-generated + 'MARK' rules + + 11000- 11999 After 'MARK' rules but before + Shorewall-generated rules for + ISP interfaces. + + 26000-26999 After ISP interface rules but + before 'default' rule. + + Rules with equal priority are applied in + the order in which they appear in the file. + + SOURCE(optonal) An ip address (network or host) that + matches the source IP address in a packet. + May also be specified as an interface + name optionally followed by ":" and an + address. If the define 'lo' is specified, + the packet must originate from the firewall + itself. + + DEST(optional) An ip address (network or host) that + matches the destination IP address in a packet. + + If you choose to omit either SOURCE or DEST, + place "-" in that column (or you can simply + leave the DEST column empty). Note that you + may not omit both SOURCE and DEST. + + Example: You want all traffic coming in on eth1 to be routed to the ISP1 + provider: + + #PROVIDER PRIORITY SOURCE DEST + ISP1 1000 eth1 Migration Considerations: diff --git a/Shorewall/tcrules b/Shorewall/tcrules index 68fd87ff4..b3136ddbd 100644 --- a/Shorewall/tcrules +++ b/Shorewall/tcrules @@ -84,10 +84,13 @@ # SOURCE Source of the packet. A comma-separated list of # interface names, IP addresses, MAC addresses and/or # subnets for packets being routed through a common path. -# For example, all packets for connections masqueraded -# to eth0 from other interfaces can be matched in a -# single rule with several alternative SOURCE criteria. -# However, a connection whose packets gets to eth0 in a +# List elements may also consist of an interface name +# followed by ":" and an address +# (e.g., eth1:192.168.1.0/24). For example, all packets +# for connections masqueraded to eth0 from other +# interfaces can be matched in a single rule with +# several alternative SOURCE criteria. However, a +# connection whose packets gets to eth0 in a # different way, e.g., direct from the firewall itself, # needs a different rule. # @@ -105,8 +108,9 @@ # DEST Destination of the packet. Comma separated list of # IP addresses and/or subnets. If your kernel and # iptables include iprange match support, IP address -# ranges are also allowed. -# +# ranges are also allowed. List elements may also +# consist of an interface name followed by ":" and an +# address (e.g., eth1:192.168.1.0/24). # If the MARK column specificies a classification of # the form : then this column may also # contain an interface name.