diff --git a/Shorewall-common/releasenotes.txt b/Shorewall-common/releasenotes.txt
index 6ff97ca09..a8cfcc968 100644
--- a/Shorewall-common/releasenotes.txt
+++ b/Shorewall-common/releasenotes.txt
@@ -255,13 +255,14 @@ New Features in 4.1.6.
DEST PORT(S)
A comma-separated list of destination ports. May only
be given if the PROTO is tcp, udp, icmp or
- sctp. Port ranges may not be used. Specify "-"
- if any PORT should match.
+ sctp. Port ranges may be used, except with the PROTO is
+ icmp. Specify "-" if any PORT should match.
SOURCE PORT(S)
A comma-separated list of source port. May only be
given if the PROTO is tcp, udp or sctp. Port ranges
- may not be used. Specify "-" if any PORT should match.
+ may be used unless the protocol is icmp. Specify "-" if
+ any PORT should match.
Entries in /etc/shorewall/tcfilters generate U32 tc filters which
may be displayed using the "shorewall show filters" ("shorewall-lite
diff --git a/Shorewall-perl/Shorewall/IPAddrs.pm b/Shorewall-perl/Shorewall/IPAddrs.pm
index 0029e8c9c..1b2564bf2 100644
--- a/Shorewall-perl/Shorewall/IPAddrs.pm
+++ b/Shorewall-perl/Shorewall/IPAddrs.pm
@@ -43,6 +43,7 @@ our @EXPORT = qw( ALLIPv4
validate_host
validate_range
ip_range_explicit
+ expand_port_range
allipv4
rfc1918_neworks
resolve_proto
@@ -355,4 +356,36 @@ sub validate_icmp( $ ) {
fatal_error "Invalid ICMP Type ($type)"
}
+sub expand_port_range( $$ ) {
+ my ( $proto, $range ) = @_;
+ my ( $first, $last ) = split /:/, $range, 2;
+
+ if ( defined $last ) {
+ my @result;
+ ( $first , $last ) = ( validate_port( $proto, $first ) , validate_port( $proto, $last ) );
+
+ while ( $first <= $last ) {
+ my $mask = 0xffff;
+ my $y = 2;
+ my $z = 1;
+
+ while ( ( $first % $y ) == 0 && ( $first + $y ) < $last ) {
+ $mask <<= 1;
+ $z = $y;
+ $y <<= 1;
+ }
+
+ push @result, sprintf( '%04x', $first ) , sprintf( '%04x' , $mask & 0xffff );
+ $first += $z;
+ }
+
+ fatal_error "Invalid port range ($range)" unless @result;
+
+ @result;
+
+ } else {
+ ( sprintf( '%04x' , validate_port( $proto, $first ) ) , 'ffff' );
+ }
+}
+
1;
diff --git a/Shorewall-perl/Shorewall/Tc.pm b/Shorewall-perl/Shorewall/Tc.pm
index 71726a5a3..4736fee7b 100644
--- a/Shorewall-perl/Shorewall/Tc.pm
+++ b/Shorewall-perl/Shorewall/Tc.pm
@@ -591,47 +591,61 @@ sub process_tc_filter( $$$$$$ ) {
fatal_error "Only TCP, UDP and SCTP may specify SOURCE PORT"
unless $protonumber == TCP || $protonumber == UDP || $protonumber == SCTP;
- for my $sport ( split_list $sportlist , 'port list' ) {
- my $portnumber = in_hex4 validate_port( $protonumber , $sport );
- emit( "\nrun_tc $rule\\" ,
- " match u32 ${portnumber}0000 0xffff0000 at nexthdr+0\\" ,
- " flowid $devref->{number}:$class" );
+ for my $sportrange ( split_list $sportlist , 'port list' ) {
+ my @sportlist = expand_port_range $protonumber , $sportrange;
+
+ while ( @sportlist ) {
+ my ( $sport, $smask ) = ( shift @sportlist, shift @sportlist );
+ emit( "\nrun_tc $rule\\" ,
+ " match u32 0x${sport}0000 0x${smask}0000 at nexthdr+0\\" ,
+ " flowid $devref->{number}:$class" );
+ }
}
} else {
fatal_error "Only TCP, UDP, SCTP and ICMP may specify DEST PORT"
unless $protonumber == TCP || $protonumber == UDP || $protonumber == SCTP || $protonumber == ICMP;
- for my $port( split_list $portlist, 'port list' ) {
+ for my $portrange ( split_list $portlist, 'port list' ) {
if ( $protonumber == ICMP ) {
- fatal_error "Only TCP, UDP and SCTP may specify SOURCE PORT" if $sportlist ne '-';
- my ( $icmptype , $icmpcode ) = split '//', validate_icmp( $port );
+ my ( $icmptype , $icmpcode ) = split '//', validate_icmp( $portrange );
$icmptype = in_hex2 numeric_value $icmptype;
$icmpcode = in_hex2 numeric_value $icmpcode if defined $icmpcode;
-
+
my $rule1 = " match u8 $icmptype 0xff at nexthdr+0";
$rule1 .= "\\\n match u8 $icmpcode 0xff at nexthdr+1" if defined $icmpcode;
emit( "\nrun_tc ${rule}\\" ,
"$rule1\\" ,
" flowid $devref->{number}:$class" );
} else {
- my $portnumber = in_hex8 validate_port( $protonumber , $port );
- my $rule1 = "match u32 $portnumber 0x0000ffff at nexthdr+0";
- if ( $sportlist eq '-' ) {
- emit( "\nrun_tc ${rule}\\" ,
- " $rule1\\" ,
- " flowid $devref->{number}:$class" );
- } else {
- for my $sport ( split_list $sportlist , 'port list' ) {
- my $portnumber = in_hex4 validate_port( $protonumber , $sport );
- emit( "\nrun_tc ${rule}\\",
+ my @portlist = expand_port_range $protonumber , $portrange;
+
+ while ( @portlist ) {
+ my ( $port, $mask ) = ( shift @portlist, shift @portlist );
+
+ my $rule1 = "match u32 0x0000${port} 0x0000${mask} at nexthdr+0";
+
+ if ( $sportlist eq '-' ) {
+ emit( "\nrun_tc ${rule}\\" ,
" $rule1\\" ,
- " match u32 ${portnumber}0000 0xffff0000 at nexthdr+0\\" ,
" flowid $devref->{number}:$class" );
- }
+ } else {
+ for my $sportrange ( split_list $sportlist , 'port list' ) {
+ my @sportlist = expand_port_range $protonumber , $sportrange;
+
+ while ( @sportlist ) {
+ my ( $sport, $smask ) = ( shift @sportlist, shift @sportlist );
+
+ emit( "\nrun_tc ${rule}\\",
+ " $rule1\\" ,
+ " match u32 0x${sport}0000 0x${smask}0000 at nexthdr+0\\" ,
+ " flowid $devref->{number}:$class" );
+ }
+ }
+ }
}
- }
- }
+ }
+ }
}
}
diff --git a/docs/traffic_shaping.xml b/docs/traffic_shaping.xml
index da96bf09d..59c81bc8c 100644
--- a/docs/traffic_shaping.xml
+++ b/docs/traffic_shaping.xml
@@ -1179,10 +1179,6 @@ ip link set ifb0 up
ipsets are not supported
-
- port ranges are not supported
-
-
DNS Names are not supported
@@ -1278,8 +1274,8 @@ eth0 192.168.1.0/24 206.124.146.179
Comma-separated list of destination port names or numbers.
- May only be specified if the protocol is TCP, UDP, SCTP or
- ICMP.
+ May only be specified if the protocol is TCP, UDP, SCTP or ICMP.
+ Port ranges are supported except for ICMP.
@@ -1288,7 +1284,8 @@ eth0 192.168.1.0/24 206.124.146.179
Comma-separated list of source port names or numbers. May
- only be specified if the protocol is TCP, UDP or SCTP.
+ only be specified if the protocol is TCP, UDP or SCTP. Port ranges
+ are supported.