Shaping-Script: Unterschied zwischen den Versionen

Aus Chaostreff Chemnitz
Keine Bearbeitungszusammenfassung
Keine Bearbeitungszusammenfassung
 
(9 dazwischenliegende Versionen von 3 Benutzern werden nicht angezeigt)
Zeile 4: Zeile 4:


Die Uhrzeit sollte möglichst automatisch aktualisiert werden. Vorsicht derzeit gibt es einen [http://www.mail-archive.com/uclibc@uclibc.org/msg07261.html Bug] in der uclibc, der dazu führt, dass in Schaltjahren die Sommerzeit zu spät beginnt wird.
Die Uhrzeit sollte möglichst automatisch aktualisiert werden. Vorsicht derzeit gibt es einen [http://www.mail-archive.com/uclibc@uclibc.org/msg07261.html Bug] in der uclibc, der dazu führt, dass in Schaltjahren die Sommerzeit zu spät beginnt wird.
 
<pre>
<pre>
~# crontab -l
root@florzrouter:~# crontab -l
# m   h   dom mon dow   command
# m h dom mon dow command
#                                     MESURE SHAPE QUOTA SPEEDMAX INITIAL_QUOTA(%) EXCLUDE_HOURS
#                                   MEASURE    SHAPE     QUOTA SPEEDMAX INITIAL_QUOTA(%) EXCLUDE_HOURS DEPENDS %
   *   *   *   *   *     /root/tc_cron eth1   eth1  9500 100000  5                0 8
   * * * * *   /root/tc_cron eth1       eth1       7500 100000 5                0 8
   *   *   *   *   *     /root/tc_cron ffc    ffc   5000 2000     5                0 0
   * * * * *   /root/tc_cron br-ffc      ffc       3000  15000    5                0 8            eth1   25
 
   *  *  * *  *    /root/tc_cron br-ffc_node br-ffc_node 2000 10000    5                0 0           ffc    90
</pre>
</pre>


'''!!! Verwendung auf eigene Gefahr !!!'''
'''!!! Verwendung auf eigene Gefahr !!!'''
(Wir können nicht garantieren, dass das Script immer fehlerfrei funktioniert.)
(Wir können nicht garantieren, dass das Script immer fehlerfrei funktioniert.)
<pre>
<pre>
#! /bin/sh
#! /bin/sh
Zeile 36: Zeile 37:
TIME_EXCLUDE_END=$7  # last  hour to exclude from todays calculation
TIME_EXCLUDE_END=$7  # last  hour to exclude from todays calculation
                       # Example: 0 8 means 00:00-07:59
                       # Example: 0 8 means 00:00-07:59
DEV_DEPEND=$8 # never go faster than this interface (optional)
DEV_DEPEND_MAX=$9 # use only % of the available speed (optional)
DEV_SHAPE_UCI=$(uci show network | grep ifname=$( echo $DEV_SHAPE | sed -e "s/br-//") | cut -d. -f2)


# calculate minimum speed
# calculate minimum speed
SPEEDMIN=$(( (($MAX_DAILY*8*1024*1024)/(60*60*24))/(2*1000) )) # 50% of bandwith when $MAX_DAILY is used completely
SPEEDMIN=$(( (($MAX_DAILY*8*1024*1024)/(60*60*24))/(2*2*1000) )) # 50% of bandwith when $MAX_DAILY is used completely
 
# calculate maximum speed
if [ -n "$DEV_DEPEND" ]; then
DEV_DEPEND_SPEEDMAX=$(( $(($(( $(tc class show dev $DEV_DEPEND | grep -oe "rate [0-9]*[kKmMgG]\{0,1\}bit" | sed "s/rate //" | sed -e "s/[kK]bit//" | sed -e "s/[mM]bit/\*1000/" | sed -e "s/[gG]bit/\*1000000/" | sed -e "s/bit/\/1000/") ))*$DEV_DEPEND_MAX))/100 ))
if [ -n "$DEV_DEPEND_SPEEDMAX" ]; then
if [ $DEV_DEPEND_SPEEDMAX -lt $SPEEDMAX ]; then
SPEEDMAX=$DEV_DEPEND_SPEEDMAX
fi
if [ $DEV_DEPEND_SPEEDMAX -lt $SPEEDMIN ]; then
SPEEDMIN=$DEV_DEPEND_SPEEDMAX
fi
fi
fi


# calculate time in percent of today
# calculate time in percent of today
Zeile 62: Zeile 79:
fi
fi
EXCLUDE=$(($EXCLUDE/1024))
EXCLUDE=$(($EXCLUDE/1024))
UPTIME=$(uptime | cut -d, -f1 | grep -oe " [0-9]\{1,\} ")
if [ ! -e /tmp/force_tc_cron_exclude ]; then
if [ ! $UPTIME -eq $UPTIME 2> /dev/null ]; then
if [ $(( $(date +%s)-$(date -d "$(date +%F) 00:00:00" +%s)-$(cat /proc/uptime | cut -d. -f1) )) -gt 0 ]; then
UPTIME=0
EXCLUDE=0
fi
echo "Can't exclude any traffic from shaping. The router needs to be running since before 00:00:00. Do \"touch /tmp/force_tc_cron_exclude\" to force exclusion."
if [ $UPTIME -le 0 ]; then
fi
EXCLUDE=0
else
echo "Can't exclude any traffic from shaping. Uptime needs to be at least one day."
if [ $(( $(date +%s)-$(date -d "$(date +%F) 00:00:00" +%s)-$(cat /proc/uptime | cut -d. -f1) )) -lt 0 ]; then
rm /tmp/force_tc_cron_exclude
fi
fi
fi


if [ $UPTIME -le 0 ]; then
COUNTER_NOW=0
TMP=$(vnstat -i ${DEV_MEASURE} --dumpdb | grep -e "^d;0;")
TMP=$(vnstat -i ${DEV_MEASURE} --dumpdb | grep -e "$(echo $(seq 0 $HOUR_NOW) | sed -e "s/ /;\\\|h;/g" | sed -e "s/^/h;/" | sed -e "s/$/;/")" | grep -oe "[0-9]\{1,\};[0-9]\{1,\}$" | sed -e "s/;/ /")
COUNTER_NOW=$(( $(echo $TMP | cut -d";" -f4) + $(echo $TMP | cut -d";" -f5) ))
for i in $TMP; do
else
COUNTER_NOW=$(($COUNTER_NOW+$i))
# compensate potential error at change to summertime (lost hour may keep value from day before which would be excluded today) -> calculate sum over all hours till now
done
COUNTER_NOW=0
COUNTER_NOW=$((($COUNTER_NOW/1024)-$EXCLUDE))                                                                                                                                                     
TMP=$(vnstat -i ${DEV_MEASURE} --dumpdb | grep -e "$(echo $(seq 0 $HOUR_NOW) | sed -e "s/ /;\\\|h;/g" | sed -e "s/^/h;/" | sed -e "s/$/;/")" | grep -oe "[0-9]\{1,\};[0-9]\{1,\}$" | sed -e "s/;/ /")
for i in $TMP; do
COUNTER_NOW=$(($COUNTER_NOW+$i))
done
COUNTER_NOW=$(($COUNTER_NOW/1024))
fi
COUNTER_NOW=$(($COUNTER_NOW-$EXCLUDE))                                                                                                                                                     
if [ $COUNTER_NOW -lt 0 ]; then                                                                                                                                                           
if [ $COUNTER_NOW -lt 0 ]; then                                                                                                                                                           
COUNTER_NOW=0                                                                                                                                                                     
COUNTER_NOW=0                                                                                                                                                                     
fi  
fi  
echo "Counter for today: ${COUNTER_NOW}MB; ${EXCLUDE}MB excluded"
LEFT_NOW=$(( $MAX_NOW-$COUNTER_NOW ))
LEFT_NOW=$(( $MAX_NOW-$COUNTER_NOW ))
PCT_MAX_NOW=$(( ($COUNTER_NOW*100) / $MAX_NOW )) #% of MAX_NOW
PCT_MAX_NOW=$(( ($COUNTER_NOW*100) / $MAX_NOW )) #% of MAX_NOW
SPEED=$(( (($LEFT_NOW*8*1024*1024)/((60*60*24*5)/1000))/1000 )) # bits left now / 0.5% of day (contingent is increased in 1% steps (+$PCT_INITIAL))
SPEED=$(( ((($LEFT_NOW*8*1024*1024)/((60*60*24*5)/1000))/1000)/2 )) # bits left now / 0.5% of day (contingent is increased in 1% steps (+$PCT_INITIAL))
if [ $SPEED -lt $SPEEDMIN ]; then
if [ $SPEED -lt $SPEEDMIN ]; then
SPEED=$SPEEDMIN
SPEED=$SPEEDMIN
Zeile 105: Zeile 116:
#disable interface if over limit
#disable interface if over limit
if [ $COUNTER_NOW -ge $MAX_DAILY ]; then
if [ $COUNTER_NOW -ge $MAX_DAILY ]; then
ifconfig $DEV_SHAPE down
ifdown $DEV_SHAPE_UCI
touch /tmp/${DEV_SHAPE_UCI}_is_down
else
else
ifconfig $DEV_SHAPE up
if [ -e /tmp/${DEV_SHAPE_UCI}_is_down ]; then
ifup $DEV_SHAPE_UCI && rm /tmp/${DEV_SHAPE_UCI}_is_down
fi
fi
fi


# debug
# debug
#echo "${LEFT_NOW}MB may be transfered after $PCT_OF_DAY% of today have passed."
echo "Counter for today: ${COUNTER_NOW}MB; ${EXCLUDE}MB excluded"                                                                                                                                                   
#echo "Limiting speed to ${SPEED}kbit/s."
echo "${LEFT_NOW}MB may be transfered after $PCT_OF_DAY% of today have passed."
echo "Limiting speed to ${SPEED}kbit/s."


##########
##########
Zeile 122: Zeile 137:
tc qdisc add dev ${DEV_SHAPE} root handle 1: htb default 10
tc qdisc add dev ${DEV_SHAPE} root handle 1: htb default 10
tc class add dev ${DEV_SHAPE} parent 1:1 classid 1:10 htb rate ${SPEED}kbit ceil ${SPEED}kbit
tc class add dev ${DEV_SHAPE} parent 1:1 classid 1:10 htb rate ${SPEED}kbit ceil ${SPEED}kbit
#tc class add dev ${DEV_SHAPE} parent 1:1 classid 1:11 htb rate 32kbit ceil 128kbit
tc filter add dev ${DEV_SHAPE} protocol ip parent 1: prio 1 u32 match ip dst 0.0.0.0/0 flowid 1:10 # DST-IP
# Zuweisung von Paketen
tc filter add dev ${DEV_SHAPE} protocol ip parent 1: prio 1 u32 match ip dst 0.0.0.0/0 flowid 1:10 ## Ziel-IP
#tc filter add dev ${DEV_SHAPE} protocol ip parent 1: prio 1 u32 match ip src 10.8.128.80/28 flowid 1:11 ## Quell-IP


############
############
Zeile 134: Zeile 146:


tc qdisc add dev ${DEV_SHAPE} handle ffff: ingress
tc qdisc add dev ${DEV_SHAPE} handle ffff: ingress
tc filter add dev ${DEV_SHAPE} parent ffff: protocol ip prio 50 u32 match ip src 0.0.0.0/0 police rate ${SPEED}kbit burst ${SPEED}kbit drop flowid :1
</pre>
</pre>


== Mögliche Verbesserungen ==
== Mögliche Verbesserungen ==
*nur die Kernelmodule laden, die für das Script tatsächlich benötigt werden.
*nur die Kernelmodule laden, die für das Script tatsächlich benötigt werden.

Aktuelle Version vom 2. Dezember 2013, 12:17 Uhr

Natürlich sind wir für Netzneutralität. Nichts desto trotz können es die äußerlichen Umstände erforderlich machen, einen Internetanschluss drosseln zu wollen. Das Script auf dieser Seite entstand, da die Netzanschlüsse des Chemnitzer Studentennetzes (CSN) tagsüber festen Beschränkungen unterliegen. Bei zweifacher Überschreitung des Tageslimits erfolgt die sofortige Sperrung des Anschlusses. Diese Regel wurde in der Vergangenheit hart umgesetzt. Dies macht beispielsweise den Betrieb eines Freifunkknotens zu einem riskanten Unterfangen.

Das Script erlaubt es daher eine feste tägliche Quota vorzugeben. Die Angabe des nicht beachteten Zeitraumes ist möglich (im CSN von 0-8 Uhr (00:00-07:59)). Der Aufruf erfolgt minütlich per cron. Getestet wurde Das Script unter OpenWRT Backfire (10.03.1, r29592) mit installiertem vnstat (muss als dienst laufen -> /etc/init.d/vnstat enable), kmod-sched und tc. Das logging durch vnstat erfolgt auf einen angesteckten USB-Stick (um den internen Flasch zu schonen).

Die Uhrzeit sollte möglichst automatisch aktualisiert werden. Vorsicht derzeit gibt es einen Bug in der uclibc, der dazu führt, dass in Schaltjahren die Sommerzeit zu spät beginnt wird.

root@florzrouter:~# crontab -l
# m  h  dom mon dow  command
#                                    MEASURE    SHAPE      QUOTA SPEEDMAX INITIAL_QUOTA(%) EXCLUDE_HOURS  DEPENDS %
  *  *  *  *  *    /root/tc_cron eth1        eth1        7500  100000  5                0 8
  *  *  *  *  *    /root/tc_cron br-ffc      ffc        3000  15000    5                0 8            eth1    25
  *  *  *  *  *    /root/tc_cron br-ffc_node br-ffc_node 2000  10000    5                0 0            ffc    90

!!! Verwendung auf eigene Gefahr !!! (Wir können nicht garantieren, dass das Script immer fehlerfrei funktioniert.)

#! /bin/sh
PATH=$PATH:/bin:/sbin:/usr/bin:/usr/sbin
for i in $(ls /lib/modules/$(uname -r)/ | grep sch_ | cut -d"." -f1); do
	insmod $i >> /dev/null 2>&1
done
insmod sch_ingress >> /dev/null 2>&1
insmod sch_sfq >> /dev/null 2>&1
insmod sch_htb >> /dev/null 2>&1
insmod cls_u32 >> /dev/null 2>&1
insmod act_police >> /dev/null 2>&1

MAX_DAILY=$3 #MB
PCT_INITIAL=$5 #% offset at 00:00:00
SPEEDMAX=$4 #kbit
DEV_MEASURE="$1"
DEV_SHAPE="$2"
TIME_EXCLUDE_START=$6 # first hour to exclude from todays calculation
TIME_EXCLUDE_END=$7   # last  hour to exclude from todays calculation
                      # Example: 0 8 means 00:00-07:59
DEV_DEPEND=$8 # never go faster than this interface (optional)
DEV_DEPEND_MAX=$9 # use only % of the available speed (optional)
DEV_SHAPE_UCI=$(uci show network | grep ifname=$( echo $DEV_SHAPE | sed -e "s/br-//") | cut -d. -f2)

# calculate minimum speed
SPEEDMIN=$(( (($MAX_DAILY*8*1024*1024)/(60*60*24))/(2*2*1000) )) # 50% of bandwith when $MAX_DAILY is used completely

# calculate maximum speed
if [ -n "$DEV_DEPEND" ]; then
	DEV_DEPEND_SPEEDMAX=$(( $(($(( $(tc class show dev $DEV_DEPEND | grep -oe "rate [0-9]*[kKmMgG]\{0,1\}bit" | sed "s/rate //" | sed -e "s/[kK]bit//" | sed -e "s/[mM]bit/\*1000/" | sed -e "s/[gG]bit/\*1000000/" | sed -e "s/bit/\/1000/") ))*$DEV_DEPEND_MAX))/100 ))
	if [ -n "$DEV_DEPEND_SPEEDMAX" ]; then
		if [ $DEV_DEPEND_SPEEDMAX -lt $SPEEDMAX ]; then
			SPEEDMAX=$DEV_DEPEND_SPEEDMAX
		fi
		if [ $DEV_DEPEND_SPEEDMAX -lt $SPEEDMIN ]; then
			SPEEDMIN=$DEV_DEPEND_SPEEDMAX
		fi
	fi
fi

# calculate time in percent of today
PCT_OF_DAY=$(( ($(date +%s) - $(date -d "$(date +%F) 00:00:00" +%s))*100 / $((60*60*24))  ))

# calculate available contingent
MAX_NOW=$(( ($MAX_DAILY*$PCT_OF_DAY)/100 + (($MAX_DAILY*$PCT_INITIAL)*( 100-$PCT_OF_DAY ))/(100*100) ))

# calculate limit
vnstat -i ${DEV_MEASURE} -u
HOUR_NOW=$(date +%H)
EXCLUDE=0
if [ $HOUR_NOW -lt $TIME_EXCLUDE_END ]; then
	TIME_EXCLUDE_END_MINUS_ONE=$HOUR_NOW
else
	TIME_EXCLUDE_END_MINUS_ONE=$(($TIME_EXCLUDE_END-1))
fi
if [ $HOUR_NOW -ge $TIME_EXCLUDE_START ]; then
	TMP=$(vnstat -i ${DEV_MEASURE} --dumpdb | grep -e "$(echo $(seq $(($TIME_EXCLUDE_START)) $(($TIME_EXCLUDE_END_MINUS_ONE))) | sed -e "s/ /;\\\|h;/g" | sed -e "s/^/h;/" | sed -e "s/$/;/")" | grep -oe "[0-9]\{1,\};[0-9]\{1,\}$" | sed -e "s/;/ /")
	for i in $TMP; do
		EXCLUDE=$(($EXCLUDE+$i))
	done
fi
EXCLUDE=$(($EXCLUDE/1024))
if [ ! -e /tmp/force_tc_cron_exclude ]; then
	if [ $(( $(date +%s)-$(date -d "$(date +%F) 00:00:00" +%s)-$(cat /proc/uptime | cut -d. -f1) )) -gt 0 ]; then
		EXCLUDE=0
		echo "Can't exclude any traffic from shaping. The router needs to be running since before 00:00:00. Do \"touch /tmp/force_tc_cron_exclude\" to force exclusion."
	fi
else
	if [ $(( $(date +%s)-$(date -d "$(date +%F) 00:00:00" +%s)-$(cat /proc/uptime | cut -d. -f1) )) -lt 0 ]; then
		rm /tmp/force_tc_cron_exclude
	fi
fi

COUNTER_NOW=0
TMP=$(vnstat -i ${DEV_MEASURE} --dumpdb | grep -e "$(echo $(seq 0 $HOUR_NOW) | sed -e "s/ /;\\\|h;/g" | sed -e "s/^/h;/" | sed -e "s/$/;/")" | grep -oe "[0-9]\{1,\};[0-9]\{1,\}$" | sed -e "s/;/ /")
for i in $TMP; do
	COUNTER_NOW=$(($COUNTER_NOW+$i))
done
COUNTER_NOW=$((($COUNTER_NOW/1024)-$EXCLUDE))                                                                                                                                                    
if [ $COUNTER_NOW -lt 0 ]; then                                                                                                                                                           
	COUNTER_NOW=0                                                                                                                                                                     
fi 
LEFT_NOW=$(( $MAX_NOW-$COUNTER_NOW ))
PCT_MAX_NOW=$(( ($COUNTER_NOW*100) / $MAX_NOW )) #% of MAX_NOW
SPEED=$(( ((($LEFT_NOW*8*1024*1024)/((60*60*24*5)/1000))/1000)/2 )) # bits left now / 0.5% of day (contingent is increased in 1% steps (+$PCT_INITIAL))
if [ $SPEED -lt $SPEEDMIN ]; then
	SPEED=$SPEEDMIN
fi
if [ $SPEED -gt $SPEEDMAX ]; then
	SPEED=$SPEEDMAX
fi
if [ $HOUR_NOW -ge $TIME_EXCLUDE_START ]; then
	if [ $HOUR_NOW -lt $TIME_EXCLUDE_END ]; then
		SPEED=$SPEEDMAX
	fi
fi

#disable interface if over limit
if [ $COUNTER_NOW -ge $MAX_DAILY ]; then
	ifdown $DEV_SHAPE_UCI
	touch /tmp/${DEV_SHAPE_UCI}_is_down
else
	if [ -e /tmp/${DEV_SHAPE_UCI}_is_down ]; then
		ifup $DEV_SHAPE_UCI && rm /tmp/${DEV_SHAPE_UCI}_is_down
	fi
fi

# debug
echo "Counter for today: ${COUNTER_NOW}MB; ${EXCLUDE}MB excluded"                                                                                                                                                    
echo "${LEFT_NOW}MB may be transfered after $PCT_OF_DAY% of today have passed."
echo "Limiting speed to ${SPEED}kbit/s."

##########
# uplink #
##########

tc qdisc del dev ${DEV_SHAPE} root

tc qdisc add dev ${DEV_SHAPE} root handle 1: htb default 10
tc class add dev ${DEV_SHAPE} parent 1:1 classid 1:10 htb rate ${SPEED}kbit ceil ${SPEED}kbit
tc filter add dev ${DEV_SHAPE} protocol ip parent 1: prio 1 u32 match ip dst 0.0.0.0/0 flowid 1:10 # DST-IP

############
# downlink #
############

tc qdisc del dev ${DEV_SHAPE} handle ffff: ingress

tc qdisc add dev ${DEV_SHAPE} handle ffff: ingress
tc filter add dev ${DEV_SHAPE} parent ffff: protocol ip prio 50 u32 match ip src 0.0.0.0/0 police rate ${SPEED}kbit burst ${SPEED}kbit drop flowid :1

Mögliche Verbesserungen

  • nur die Kernelmodule laden, die für das Script tatsächlich benötigt werden.