diff options
author | Florian Pritz <bluewind@xinu.at> | 2016-08-19 13:58:35 +0200 |
---|---|---|
committer | Florian Pritz <bluewind@xinu.at> | 2016-08-19 13:58:35 +0200 |
commit | 266a60532c05961b4b9115b05cb418663aaa3fc3 (patch) | |
tree | 8b5d8b78c7c22eefbd10aab29f441d3da1636783 | |
parent | 875df57f2bf02b5944dfab0f5e32507990d1f4bf (diff) | |
download | bin-266a60532c05961b4b9115b05cb418663aaa3fc3.tar.gz bin-266a60532c05961b4b9115b05cb418663aaa3fc3.tar.xz |
Moving shaping scripts to dedicated repo
Signed-off-by: Florian Pritz <bluewind@xinu.at>
-rwxr-xr-x | hfsc_shaper.sh | 274 | ||||
-rwxr-xr-x | qos.pl | 314 |
2 files changed, 0 insertions, 588 deletions
diff --git a/hfsc_shaper.sh b/hfsc_shaper.sh deleted file mode 100755 index 3504c68..0000000 --- a/hfsc_shaper.sh +++ /dev/null @@ -1,274 +0,0 @@ -#!/bin/bash -#---------------------------------------------------- -# File: hfsc_shaper.sh -# Version: 0.2 -# Edited: Florian "Bluewind" Pritz <bluewind@xinu.at> -# Author: Maciej Bliziński, http://automatthias.wordpress.com/ -#---------------------------------------------------- -# -# Special Thanks to -# Maciej Bliziński, http://automatthias.wordpress.com/ -# -# References: -# http://www.voip-info.org/wiki/view/QoS+Linux+with+HFSC -# http://www.nslu2-linux.org/wiki/HowTo/EnableTrafficShaping -# http://www.cs.cmu.edu/~hzhang/HFSC/main.html - -######################################################################## -# CONFIGURATION -######################################################################## - -# Uplink and downlink speeds -# Normally use a bit lower values than your real speed, but -# you should experiment a bit -# downlink is unused -#DOWNLINK=75000 -UPLINK=7400 - -# Device that connects you to the Internet -DEV="extern0" - -# Traffic classes: -# 1:2 Interactive (SSH, DNS, ACK, Quake) -# 1:3 Low latency (VoIP) -# 1:4 Browsing (HTTP, HTTPs) -# 1:5 Default -# 1:6 Middle-low priority -# 1:7 Lowest priority - -# Interactive class: SSH Terminal, DNS and gaming (Quake) -INTERACTIVEPORTS="22 23 53 3389 5900 22222 6667 7000 44400 7776 4949 11030 11239 143 445 25765" - -# VoIP telephony -#VOIPPORTS="5060:5100 10000:11000 5000:5059 8000:8016 5004 1720 1731" -VOIPPORTS="7977 9987" -# IP addresses of the VoIP phones, -# if none, set VOIPIPS="" -VOIPIPS="" - - -# WWW, jabber and IRC -BROWSINGPORTS="80 443 8080 993" - -# Everything unspecified will be here (inbetween Browsing and Data) - -# FTP, Mail... -DATAPORTS="110 25 21 137:139 4662 4664 " - -# The lowest priority traffic: eDonkey, Bittorrent, etc. -#P2PPORTS="6881:6999 36892 8333" -P2PPORTS="" - -######################################################################## -# CONFIGURATION ENDS HERE -######################################################################## - -if [ -z "$DEV" ] ; then - echo "$0: device not set, aborting." - exit -1 -fi - -function stop() { - # Reset everything to a known state (cleared) - tc qdisc del dev $DEV root &> /dev/null - tc qdisc del dev $DEV ingress &> /dev/null - - # Flush and delete tables - iptables -t mangle --delete POSTROUTING -o $DEV -j THESHAPER &> /dev/null - iptables -t mangle --flush THESHAPER &> /dev/null - iptables -t mangle --delete-chain THESHAPER &> /dev/null -} - -function start() { - #if [ -z "$DOWNLINK" ] ; then - #echo "$0: start requires a downlink speed, aborting." - #exit -1 - #fi - if [ -z "$UPLINK" ] ; then - echo "$0: start requires an uplink speed, aborting." - exit -1 - fi - - # add HFSC root qdisc - tc qdisc add dev $DEV root handle 1: hfsc default 5 - - # add main rate limit class - tc class add dev $DEV parent 1: classid 1:1 hfsc \ - sc rate ${UPLINK}kbit ul rate ${UPLINK}kbit - - # Interactive traffic: guarantee full uplink for 50ms, then - # 5/10 of the uplink - tc class add dev $DEV parent 1:1 classid 1:2 hfsc \ - sc m1 ${UPLINK}kbit d 50ms m2 $((5*$UPLINK/10))kbit \ - ul rate ${UPLINK}kbit - - # VoIP: guarantee full uplink for 200ms, then 3/10 - tc class add dev $DEV parent 1:1 classid 1:3 hfsc \ - sc m1 ${UPLINK}kbit d 200ms m2 $((3*$UPLINK/10))kbit \ - ul rate ${UPLINK}kbit - - # Browsing: guarantee 3/10 uplink for 200ms, then - # guarantee 1/10 - tc class add dev $DEV parent 1:1 classid 1:4 hfsc \ - sc m1 $((3*$UPLINK/10))kbit d 200ms m2 $((1*$UPLINK/10))kbit \ - ul rate ${UPLINK}kbit - - # Default traffic: guarantee 1/10 uplink for 100ms, - # then guarantee 3/20 - tc class add dev $DEV parent 1:1 classid 1:5 hfsc \ - sc m1 $((1*$UPLINK/10))kbit d 100ms m2 $((3*$UPLINK/20))kbit \ - ul rate ${UPLINK}kbit - - # Middle-low taffic: don't guarantee anything for the first 5 seconds, - # then guarantee 1/10 - tc class add dev $DEV parent 1:1 classid 1:6 hfsc \ - sc m1 0 d 5s m2 $((1*$UPLINK/10))kbit \ - ul rate ${UPLINK}kbit - - # Lowest taffic: don't guarantee anything for the first 10 seconds, - # then guarantee 1/20 - #ls m2 $((1*$UPLINK/200))kbit \ - tc class add dev $DEV parent 1:1 classid 1:7 hfsc \ - sc m1 0 d 5s m2 10kbit \ - ul rate $((UPLINK-200))kbit - - # add THESHAPER chain to the mangle table in iptables - iptables -t mangle --new-chain THESHAPER - iptables -t mangle --insert POSTROUTING -o $DEV -j THESHAPER - - # Type of service filters (see /etc/iproute2/rt_dsfield) - iptables -t mangle -A THESHAPER \ - -m tos --tos 0x10 \ - -j CLASSIFY --set-class 1:2 - - iptables -t mangle -A THESHAPER \ - -m tos --tos 0x08 \ - -j CLASSIFY --set-class 1:7 - - # To speed up downloads while an upload is going on, put short ACK - # packets in the interactive class - iptables -t mangle -A THESHAPER \ - -p tcp \ - -m tcp --tcp-flags FIN,SYN,RST,ACK ACK \ - -m length --length :64 \ - -j CLASSIFY --set-class 1:2 - - # put large (512+) icmp packets in default category - iptables -t mangle -A THESHAPER \ - -p icmp \ - -m length --length 512: \ - -j CLASSIFY --set-class 1:5 - - # small ICMP in the interactive class - iptables -t mangle -A THESHAPER \ - -p icmp \ - -m length --length :512 \ - -j CLASSIFY --set-class 1:2 - - setclassbyport() { - port=$1 - CLASS=$2 - iptables -t mangle -A THESHAPER -p udp --sport $port -j CLASSIFY --set-class $CLASS - iptables -t mangle -A THESHAPER -p udp --dport $port -j CLASSIFY --set-class $CLASS - iptables -t mangle -A THESHAPER -p tcp --sport $port -j CLASSIFY --set-class $CLASS - iptables -t mangle -A THESHAPER -p tcp --dport $port -j CLASSIFY --set-class $CLASS - } - - for port in $INTERACTIVEPORTS; do setclassbyport $port 1:2; done - for port in $VOIPPORTS; do setclassbyport $port 1:3; done - for port in $BROWSINGPORTS; do setclassbyport $port 1:4; done - for port in $DATAPORTS; do setclassbyport $port 1:6; done - for port in $P2PPORTS; do setclassbyport $port 1:7; done - - for VOIP in $VOIPIPS; do - iptables -t mangle -A THESHAPER --src $VOIP -j CLASSIFY --set-class 1:3 - iptables -t mangle -A THESHAPER --dst $VOIP -j CLASSIFY --set-class 1:3 - done - - # put large (1024+) https packets in default category - iptables -t mangle -A THESHAPER \ - -p tcp --dport 443 \ - -m length --length 1024: \ - -j CLASSIFY --set-class 1:6 - - # put large (1024+) http packets in default category - iptables -t mangle -A THESHAPER \ - -p tcp --dport 80 \ - -m length --length 1024: \ - -j CLASSIFY --set-class 1:6 - - # put large (1024+) packets in default category - #iptables -t mangle -A THESHAPER \ - #-p tcp \ - #-m length --length 1024: \ - #-j CLASSIFY --set-class 1:6 - - # put large (1024+) ssh packets in default category - iptables -t mangle -A THESHAPER \ - -p tcp --dport 22 \ - -m length --length 1024: \ - -j CLASSIFY --set-class 1:6 - - # put all traffic from user torrent into p2p category (only works for the host this script runs on) - #iptables -t mangle -A THESHAPER \ - #--match owner --uid-owner 169 -j CLASSIFY --set-class 1:7 - - # Try to control the incoming traffic as well. - # Set up ingress qdisc - #tc qdisc add dev $DEV handle ffff: ingress - - # Filter everything that is coming in too fast - # It's mostly HTTP downloads that keep jamming the downlink, so try to restrict - # them to 95/100 of the downlink. - - # FIXME: slows down too much - #tc filter add dev $DEV parent ffff: protocol ip prio 50 \ - #u32 match ip src 0.0.0.0/0 \ - #match ip protocol 6 0xff \ - #match ip sport 80 0xffff \ - #police rate $((95*${DOWNLINK}/100))kbit \ - #burst 10k drop flowid :1 - - #tc filter add dev $DEV parent ffff: protocol ip prio 50 \ - #u32 match ip src 0.0.0.0/0 \ - #police rate $((95*${DOWNLINK}/100))kbit \ - #burst $((95*${DOWNLINK}/100*2)) drop flowid :1 -} - -function status() { - echo "[qdisc]" - tc -s qdisc show dev $DEV - - echo "" - echo "[class]" - tc -s class show dev $DEV - - echo "" - echo "[filter]" - tc -s filter show dev $DEV - - echo "" - echo "[iptables]" - iptables -n -t mangle -L THESHAPER -v -x -} - -case "$1" in - status) - status - ;; - stop) - stop - ;; - start) - start - ;; - restart) - stop - start - ;; - *) - echo "$0 [ACTION] [device]" - echo "ACTION := { start | stop | status | restart }" - exit - ;; -esac @@ -1,314 +0,0 @@ -#!/usr/bin/perl -T -use warnings; -use strict; - -use v5.10; -use autodie; -use File::Slurp; -use List::Util qw(reduce); -use POSIX; -use Time::HiRes qw(sleep time); - -=head1 NAME - -qos.pl - Show some network QoS statistics - -=head1 SYNOPSIS - -qos.pl [interval [history_size]] - - Options: - interval: Set the sampling interval in seconds - history_size: Set the number of samples to produce averages with - -=head1 DESCRIPTION - -Use with hfsc_shaper.sh. - -This programm will output QoS metrics. If an interval is set, it will output -the metrics roughly once per interval. If a history size N is set, it will -display the average traffic per second using N samples. - -The output can be adjusted in the source code of this script. - -=cut - -my $device = "extern0"; -my %classes = ( - "1:2" => "interactive", - "1:3" => "voip", - "1:4" => "browsing", - "1:5" => "default", - "1:6" => "mid-low", - "1:7" => "low/data", -); - -my $show_debug_info = 0; - -my @display_rows = ( - [qw(time space interface-name)], - #[qw(1:2 1:5 space total-up-tc)], - #[qw(1:3 1:6 space total-up)], - #[qw(1:4 1:7 space total-down)], - [qw(1:2 1:5)], - [qw(1:3 1:6)], - [qw(1:4 1:7)], - [], - [qw(total-up-tc total-up total-down)], -); - -sub untaint { - my $data = shift; - my $regex = shift; - - $data =~ m/^($regex)$/ or die "Failed to untaint: $data"; - return $1; -} - -sub format_bytes { - my $bytes = shift; - my $boundry = 2048; - my $format; - my $unit; - - my @suffix = qw(B KiB MiB GiB TiB); - - for (@suffix) { - $unit = $_; - last if (abs($bytes) < $boundry); - $bytes /= 1024; - } - - if ($unit eq "B") { - $format = "%.0f %s"; - } else { - $format = "%.2f %s"; - } - - return sprintf $format, $bytes, $unit; -} - -sub parse_tc_output { - my $output = shift; - my $timestamp = shift; - my $history_values = shift; - my $history_size_limit = shift; - - my $class = undef; - my %results = (); - - for (split /^/, $output) { - if (m/^class [a-z]+ (?<class>[^ ]+)/) { - $class = $+{class}; - } - - if ($class && defined($classes{$class})) { - if (m/ Sent (?<sent>[0-9]+) bytes/) { - $results{$class} = $+{sent}; - - if ($history_size_limit > 0) { - # keep history of previous values - if (!defined($history_values->{$class})) { - $history_values->{$class} = []; - push @{$history_values->{$class}}, { - value => $+{sent}, - value_diff => 0, - time_diff => 0, - time => $timestamp, - }; - } else { - my $last_time = $history_values->{$class}[-1]{time}; - my $last_value = $history_values->{$class}[-1]{value}; - push @{$history_values->{$class}}, { - value => $+{sent}, - value_diff => $+{sent} - $last_value, - time_diff => $timestamp - $last_time, - time => $timestamp, - }; - } - - # limit history size - if (0+@{$history_values->{$class}} > $history_size_limit) { - splice @{$history_values->{$class}}, 0, 1; - } - } - } - } - - if (m/^$/) { - $class = undef; - } - } - - return \%results; -} - -sub get_interface_speed { - my $interface = shift; - my $history_size_limit = shift; - - state $speed_history = {}; - - my $tx_bytes = read_file("/sys/class/net/$interface/statistics/tx_bytes"); - my $rx_bytes = read_file("/sys/class/net/$interface/statistics/rx_bytes"); - my $timestamp = time; - - if (!defined($speed_history->{$interface})) { - $speed_history->{$interface} = []; - push @{$speed_history->{$interface}}, { - rx_value => $rx_bytes, - tx_value => $tx_bytes, - rx_diff => 0, - tx_diff => 0, - time => $timestamp, - time_diff => 0, - }; - } else { - my $last_time = $speed_history->{$interface}[-1]{time}; - my $last_rx = $speed_history->{$interface}[-1]{rx_value}; - my $last_tx = $speed_history->{$interface}[-1]{tx_value}; - push @{$speed_history->{$interface}}, { - rx_value => $rx_bytes, - tx_value => $tx_bytes, - rx_diff => $rx_bytes - $last_rx, - tx_diff => $tx_bytes - $last_tx, - time => $timestamp, - time_diff => $timestamp - $last_time, - }; - } - - # limit history size - if (0+@{$speed_history->{$interface}} > $history_size_limit) { - splice @{$speed_history->{$interface}}, 0, 1; - } - - my $total_time = reduce {$a + $b->{time_diff}} 0, @{$speed_history->{$interface}}; - my $total_rx = reduce {$a + $b->{rx_diff}} 0, @{$speed_history->{$interface}}; - my $total_tx = reduce {$a + $b->{tx_diff}} 0, @{$speed_history->{$interface}}; - - my $rx_speed = 0; - my $tx_speed = 0; - - $rx_speed = $total_rx/$total_time if $total_time != 0; - $tx_speed = $total_tx/$total_time if $total_time != 0; - - return ($rx_speed, $tx_speed); -} - -sub print_output_table { - my $results = shift; - my $history_values = shift; - my $history_size_limit = shift; - my $starttime = shift; - my $after_tc = shift; - my $interval = shift; - my $first_run = shift; - - my $output_buffer = ""; - - my $global_speed = 0; - my ($rx_speed, $tx_speed); - - if ($history_size_limit > 0) { - ($rx_speed, $tx_speed) = get_interface_speed($device, $history_size_limit); - } - - for my $class_id (keys %classes) { - if ($history_values->{$class_id}) { - my $total_time = reduce {$a + $b->{time_diff}} 0, @{$history_values->{$class_id}}; - my $total_value = reduce {$a + $b->{value_diff}} 0, @{$history_values->{$class_id}}; - my $speed = 0; - - $speed = $total_value / $total_time if $total_time != 0; - $global_speed += $speed; - } - } - - for my $row (@display_rows) { - for my $col (@{$row}) { - if ($col =~ /^total-.*/) { - if ($history_size_limit > 0) { - if ($col eq "total-up") { - $output_buffer .= sprintf "%11s/s up (tc)", format_bytes($global_speed); - } elsif ($col eq "total-up-tc") { - $output_buffer .= sprintf "%11s/s up (interface)", format_bytes($tx_speed); - } elsif ($col eq "total-down") { - $output_buffer .= sprintf "%11s/s down (interface)", format_bytes($rx_speed); - } - } - } elsif ($col eq "space") { - $output_buffer .= " "x4; - } elsif ($col eq "spaaaaaace") { - if ($interval) { - $output_buffer .= " "x81; - } else { - $output_buffer .= " "x14; - } - } elsif ($col eq "interface-name") { - $output_buffer .= $device; - } elsif ($col eq "time") { - $output_buffer .= POSIX::strftime("%Y-%m-%d %H:%M:%S ", localtime); - } else { - my $class_id = $col; - if ($history_values->{$class_id}) { - my $total_time = reduce {$a + $b->{time_diff}} 0, @{$history_values->{$class_id}}; - my $total_value = reduce {$a + $b->{value_diff}} 0, @{$history_values->{$class_id}}; - my $speed = 0; - - $speed = $total_value / $total_time if $total_time != 0; - - $output_buffer .= sprintf "%14s (%s): %11s %11s/s", $classes{$class_id}, $class_id, format_bytes($results->{$class_id}), format_bytes($speed); - } else { - $output_buffer .= sprintf "%14s (%s): %11s", $classes{$class_id}, $class_id, format_bytes($results->{$class_id}); - } - } - } - $output_buffer .= "\n"; - } - - my $runtime = time - $starttime; - my $runtime_tc = $after_tc - $starttime; - printf "WARNING: processing took %0.4fs, skipping sleep. Consider raising interval!\n", $runtime if $runtime > $interval and $interval > 0; - - $output_buffer = sprintf "Runtime for this iteration: %0.4fs (tc: %0.4fs = %0.2f%%)\n%s", $runtime, $runtime_tc, $runtime_tc / $runtime * 100, $output_buffer if $show_debug_info; - if (!$first_run) { - my $newline_count = (split /^/, $output_buffer); - # move cursor to start of previous table and clear screen - printf "[%dF[0J", $newline_count; - } - print $output_buffer; -} - -sub main { - my $interval = 0; - my %history_values = (); - my $history_size_limit = 0; - my $first_run = 1; - - if (0+@ARGV >= 1) { - $interval = $ARGV[0]; - } - - if (0+@ARGV >= 2) { - $history_size_limit = $ARGV[1]; - } - - $ENV{PATH} = untaint($ENV{PATH}, qr(.*)); - - while (1) { - my $starttime = time; - my $stats = `tc -s class show dev $device`; - my $after_tc = time; - - my $results = parse_tc_output($stats, $after_tc, \%history_values, $history_size_limit); - - print_output_table($results, \%history_values, $history_size_limit, $starttime, $after_tc, $interval, $first_run); - - $first_run = 0; - last unless $interval > 0; - my $runtime = time - $starttime; - sleep($interval - $runtime) unless $runtime > $interval; - } -} - -main(); |