diff options
Diffstat (limited to 'scripts/VyattaQosTrafficShaper.pm')
-rw-r--r-- | scripts/VyattaQosTrafficShaper.pm | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/scripts/VyattaQosTrafficShaper.pm b/scripts/VyattaQosTrafficShaper.pm new file mode 100644 index 0000000..903aeeb --- /dev/null +++ b/scripts/VyattaQosTrafficShaper.pm @@ -0,0 +1,182 @@ +# Traffic shaper +# This is a extended form of Hierarchal Token Bucket with +# more admin friendly features. Similar in spirt to other shaper scripts +# such as wondershaper. + +{ + package ShaperClass; + use strict; + require VyattaConfig; + use VyattaQosMatch; + + my %fields = ( + id => undef, + _priority => undef, + _rate => undef, + _ceiling => undef, + _burst => undef, + _match => undef, + ); + + sub new { + my ( $that, $config, $id ) = @_; + my $class = ref($that) || $that; + my $self = {%fields}; + + bless $self, $class; + $self->_define($config, $id); + + return $self; + } + + sub _define { + my ( $self, $config, $id ) = @_; + my $level = $config->setLevel(); + my @matches = (); + + + $self->{_id} = sprintf "%04x", $id; + $self->{_priority} = $config->returnValue("priority"); + $self->{_rate} = $config->returnValue("rate"); + $self->{_ceiling} = $config->returnValue("ceiling"); + $self->{_burst} = $config->returnValue("burst"); + + foreach my $match ($config->listNodes("match")) { + $config->setLevel("$level match $match"); + push @matches, new VyattaQosMatch($config); + } + $self->{_match} = \@matches; + } + + sub _getPercentRate { + my ($rate, $speed) = @_; + + if ( ! defined $rate ) { + return; # leave rate undef + } + + # Rate might be a percentage of speed + if ( $rate =~ /%$/ ) { + my $percent = substr( $rate, 0, length($rate) - 1 ); + if ( $percent < 0 || $percent > 100 ) { + die "Invalid percentage bandwidth\n"; + } + + $rate = ( $percent * $speed ) / 100.; + } else { + $rate = VyattaQosUtil::getRate($rate); + } + + return $rate; + } + + sub commands { + my ( $self, $out, $dev, $speed ) = @_; + my $rate = _getPercentRate($self->{_rate}, $speed); + my $ceil = _getPercentRate($self->{_ceiling}, $speed); + my $id = $self->{_id}; + my $matches = $self->{_match}; + + die "Rate is not defined\n" if (! defined $rate ); + die "Class $id rate $rate > shaper rate $speed\n" if ($rate > $speed); + + # create the class + my $cmd ="class add dev $dev parent 1:1 classid 1:$id htb rate $rate"; + if ( defined $ceil) { + if ($ceil < $rate ) { + die "Rate limit (ceiling) $ceil < base rate $rate\n"; + } + $cmd .= " ceil $ceil"; + } + + $cmd .= " burst $self->{_burst}" if ( defined $self->{_burst} ); + $cmd .= " prio $self->{_priority}" if ( defined $self->{_priority} ); + + print {$out} $cmd . "\n"; + + # create leaf qdisc + print {$out} "qdisc add dev $dev parent 1:$id sfq\n"; + + foreach my $match (@$matches) { + $match->filter( $out, $dev, $id ); + } + } +} + +package VyattaQosTrafficShaper; +@ISA = qw/VyattaQosPolicy/; +use strict; +require VyattaConfig; +use VyattaQosUtil; + +my $defaultId = 0x4000; + +my %fields = ( + _rate => undef, + _classes => undef, +); + +# new VyattaQosTrafficShaper($config) +# Create a new instance based on config information +sub new { + my ( $that, $config ) = @_; + my $self = {%fields}; + my $class = ref($that) || $that; + + bless $self, $class; + $self->_define($config); + + return $self; +} + +# Rate can be something like "auto" or "10.2mbit" +sub _getAutoRate { + my ($rate, $dev) = @_; + + if ( $rate eq "auto" ) { + my $rate = VyattaQosUtil::interfaceRate($dev); + if ( ! defined $rate ) { + die "Auto speed setting but can't get rate from $dev\n"; + } + } else { + $rate = VyattaQosUtil::getRate($rate); + } + + return $rate; +} + +# Setup new instance. +# Assumes caller has done $config->setLevel to "traffic-shaper $name" +sub _define { + my ( $self, $config ) = @_; + my $level = $config->setLevel(); + my @classes = ( ); + + $self->{_rate} = $config->returnValue("rate"); + + $config->setLevel("$level default"); + push @classes, new ShaperClass( $config, $defaultId); + $config->setLevel($level); + + foreach my $id ( $config->listNodes("class") ) { + $config->setLevel("$level class $id"); + push @classes, new ShaperClass( $config, $id ); + } + $self->{_classes} = \@classes; +} + +sub commands { + my ( $self, $out, $dev ) = @_; + my $rate = _getAutoRate($self->{_rate}, $dev); + my $classes = $self->{_classes}; + my @tc = ( ); + + print {$out} "qdisc add dev $dev root handle 1: htb default $defaultId\n"; + print {$out} "class add dev $dev parent 1: classid 1:1 htb rate $rate\n"; + + foreach my $class (@$classes) { + $class->commands( $out, $dev, $rate ); + } +} + +1; |