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.