2020-10-02 09:24:10 -06:00
# Copyright (C) 2020 Robert Chacon
# This file is part of LibreQoS.
# LibreQoS is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
2020-10-02 09:48:55 -06:00
# LibreQoS is distributed in the hope that it will be useful,
2020-10-02 09:24:10 -06:00
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with LibreQoS. If not, see <http://www.gnu.org/licenses/>.
import random
import os
import subprocess
import time
import ipaddress
from datetime import date
def shell ( inputCommand ) :
if enableActualShellCommands :
if runShellCommandsAsSudo :
inputCommand = ' sudo ' + inputCommand
inputCommandSplit = inputCommand . split ( ' ' )
print ( inputCommand )
result = subprocess . run ( inputCommandSplit , stdout = subprocess . PIPE )
print ( result . stdout )
else :
print ( inputCommand )
def clearPriorSettings ( interfaceA , interfaceB ) :
shell ( ' tc filter delete dev ' + interfaceA )
shell ( ' tc filter delete dev ' + interfaceA + ' root ' )
shell ( ' tc qdisc delete dev ' + interfaceA )
shell ( ' tc qdisc delete dev ' + interfaceA + ' root ' )
shell ( ' tc filter delete dev ' + interfaceB )
shell ( ' tc filter delete dev ' + interfaceB + ' root ' )
shell ( ' tc qdisc delete dev ' + interfaceB )
shell ( ' tc qdisc delete dev ' + interfaceB + ' root ' )
def getHashList ( ) :
twoDigitHash = [ ]
letters = [ ' a ' , ' b ' , ' c ' , ' d ' , ' e ' , ' f ' , ' g ' , ' h ' , ' i ' , ' j ' , ' k ' , ' l ' , ' m ' , ' n ' , ' o ' , ' p ' , ' q ' , ' r ' , ' s ' , ' t ' , ' u ' , ' v ' , ' w ' , ' x ' , ' y ' , ' z ' ]
for i in range ( 10 ) :
for x in range ( 26 ) :
twoDigitHash . append ( str ( i ) + letters [ x ] )
return twoDigitHash
def createTestClientsPool ( slash16 , quantity ) :
if quantity < 5000 :
tempList = [ ]
counterC = 1
counterD = 1
mainCounter = 1
while mainCounter < quantity :
if counterD < = 255 :
ipAddr = slash16 . replace ( ' X.X ' , ' ' ) + str ( counterC ) + ' . ' + str ( counterD )
tempList . append ( ( 100 , ipAddr ) )
counterD + = 1
else :
counterC + = 1
counterD = 1
mainCounter + = 1
return tempList
else :
raise Exception
#########################################################################################################
#########################################################################################################
####################################### #######################################
####################################### Main Settings #######################################
####################################### #######################################
#########################################################################################################
#########################################################################################################
fqOrCAKE = ' cake ' #'fq_codel' or 'cake'
pipeBandwidthCapacityMbps = 1500 # How many symmetrical Mbps is the edge of this test network
interfaceA = ' ens19 ' # Interface connected to edge
interfaceB = ' ens20 ' # Interface connected to core
downSpeedDict = { 25 : 30 , 50 : 55 , 100 : 115 }
upSpeedDict = { 25 : 3 , 50 : 5 , 100 : 15 }
enableActualShellCommands = False # Allow execution of these shell commands. Default is False where commands print to console.
runShellCommandsAsSudo = False # Add 'sudo' before execution of any shell commands. Default is False.
#Clients
clientsList = [ ]
#Add arbitrary number of test clients
clientsList = createTestClientsPool ( ' 100.64.X.X ' , 50 )
#Add specific test clients
clientsList . append ( ( 100 , ' 100.65.8.2 ' ) )
#########################################################################################################
#Categorize Clients By IPv4 /16
listOfSlash16SubnetsInvolved = [ ]
clientsListWithSubnet = [ ]
for customer in clientsList :
planID , ipAddr = customer
dec1 , dec2 , dec3 , dec4 = ipAddr . split ( ' . ' )
slash16 = dec1 + ' . ' + dec2 + ' .0.0 '
if slash16 not in listOfSlash16SubnetsInvolved :
listOfSlash16SubnetsInvolved . append ( slash16 )
clientsListWithSubnet . append ( ( planID , ipAddr , slash16 , dec1 , dec2 , dec3 , dec4 ) )
#Clear Prior Configs
clearPriorSettings ( interfaceA , interfaceB )
#InterfaceA
parentIDFirstPart = 1
srcOrDst = ' dst '
classIDCounter = 101
hashIDCounter = parentIDFirstPart + 1
shell ( ' tc qdisc replace dev ' + interfaceA + ' root handle ' + str ( parentIDFirstPart ) + ' : htb default 1 ' )
shell ( ' tc class add dev ' + interfaceA + ' parent ' + str ( parentIDFirstPart ) + ' : classid ' + str ( parentIDFirstPart ) + ' :1 htb rate ' + str ( pipeBandwidthCapacityMbps ) + ' mbit ' )
for slash16 in listOfSlash16SubnetsInvolved :
#X.X.0.0
thisSlash16Dec1 = slash16 . split ( ' . ' ) [ 0 ]
thisSlash16Dec2 = slash16 . split ( ' . ' ) [ 1 ]
groupedCustomers = [ ]
for i in range ( 255 ) :
tempList = [ ]
for customer in clientsListWithSubnet :
planID , ipAddr , slash16 , dec1 , dec2 , dec3 , dec4 = customer
if ( dec1 == thisSlash16Dec1 ) and ( dec2 == thisSlash16Dec2 ) and ( dec4 == str ( i ) ) :
tempList . append ( customer )
if len ( tempList ) > 0 :
groupedCustomers . append ( tempList )
shell ( ' tc filter add dev ' + interfaceA + ' parent ' + str ( parentIDFirstPart ) + ' : prio 5 u32 ' )
shell ( ' tc filter add dev ' + interfaceA + ' parent ' + str ( parentIDFirstPart ) + ' : prio 5 handle ' + str ( hashIDCounter ) + ' : u32 divisor 256 ' )
thirdDigitCounter = 0
handleIDSecond = 1
while thirdDigitCounter < = 255 :
if len ( groupedCustomers ) > 0 :
currentCustomerList = groupedCustomers . pop ( )
tempHashList = getHashList ( )
for cust in currentCustomerList :
planID , ipAddr , slash16 , dec1 , dec2 , dec3 , dec4 = cust
twoDigitHashString = hex ( int ( dec4 ) ) . replace ( ' 0x ' , ' ' )
shell ( ' tc class add dev ' + interfaceA + ' parent ' + str ( parentIDFirstPart ) + ' :1 classid ' + str ( parentIDFirstPart ) + ' : ' + str ( classIDCounter ) + ' htb rate ' + str ( downSpeedDict [ planID ] ) + ' mbit ceil ' + str ( downSpeedDict [ planID ] ) + ' mbit prio 3 ' )
shell ( ' tc qdisc add dev ' + interfaceA + ' parent ' + str ( parentIDFirstPart ) + ' : ' + str ( classIDCounter ) + ' cake ' )
shell ( ' tc filter add dev ' + interfaceA + ' parent ' + str ( parentIDFirstPart ) + ' : prio 5 u32 ht ' + str ( hashIDCounter ) + ' : ' + twoDigitHashString + ' match ip ' + srcOrDst + ' ' + ipAddr + ' flowid 1: ' + str ( classIDCounter ) )
classIDCounter + = 1
thirdDigitCounter + = 1
if ( srcOrDst == ' dst ' ) :
startPointForHash = ' 16 ' #Position of dst-address in IP header
elif ( srcOrDst == ' src ' ) :
startPointForHash = ' 12 ' #Position of src-address in IP header
shell ( ' tc filter add dev ' + interfaceA + ' parent ' + str ( parentIDFirstPart ) + ' : prio 5 u32 ht 800:: match ip ' + srcOrDst + ' ' + thisSlash16Dec1 + ' . ' + thisSlash16Dec2 + ' .0.0/16 hashkey mask 0x000000ff at ' + startPointForHash + ' link ' + str ( hashIDCounter ) + ' : ' )
hashIDCounter + = 1
#InterfaceB
parentIDFirstPart = hashIDCounter + 1
hashIDCounter = parentIDFirstPart + 1
srcOrDst = ' src '
shell ( ' tc qdisc replace dev ' + interfaceB + ' root handle ' + str ( parentIDFirstPart ) + ' : htb default 1 ' )
shell ( ' tc class add dev ' + interfaceB + ' parent ' + str ( parentIDFirstPart ) + ' : classid ' + str ( parentIDFirstPart ) + ' :1 htb rate ' + str ( pipeBandwidthCapacityMbps ) + ' mbit ' )
for slash16 in listOfSlash16SubnetsInvolved :
#X.X.0.0
thisSlash16Dec1 = slash16 . split ( ' . ' ) [ 0 ]
thisSlash16Dec2 = slash16 . split ( ' . ' ) [ 1 ]
groupedCustomers = [ ]
for i in range ( 255 ) :
tempList = [ ]
for customer in clientsListWithSubnet :
planID , ipAddr , slash16 , dec1 , dec2 , dec3 , dec4 = customer
if ( dec1 == thisSlash16Dec1 ) and ( dec2 == thisSlash16Dec2 ) and ( dec4 == str ( i ) ) :
tempList . append ( customer )
if len ( tempList ) > 0 :
groupedCustomers . append ( tempList )
shell ( ' tc filter add dev ' + interfaceB + ' parent ' + str ( parentIDFirstPart ) + ' : prio 5 u32 ' )
shell ( ' tc filter add dev ' + interfaceB + ' parent ' + str ( parentIDFirstPart ) + ' : prio 5 handle ' + str ( hashIDCounter ) + ' : u32 divisor 256 ' )
thirdDigitCounter = 0
handleIDSecond = 1
while thirdDigitCounter < = 255 :
if len ( groupedCustomers ) > 0 :
currentCustomerList = groupedCustomers . pop ( )
tempHashList = getHashList ( )
for cust in currentCustomerList :
planID , ipAddr , slash16 , dec1 , dec2 , dec3 , dec4 = cust
twoDigitHashString = hex ( int ( dec4 ) ) . replace ( ' 0x ' , ' ' )
shell ( ' tc class add dev ' + interfaceB + ' parent ' + str ( parentIDFirstPart ) + ' :1 classid ' + str ( parentIDFirstPart ) + ' : ' + str ( classIDCounter ) + ' htb rate ' + str ( downSpeedDict [ planID ] ) + ' mbit ceil ' + str ( downSpeedDict [ planID ] ) + ' mbit prio 3 ' )
shell ( ' tc qdisc add dev ' + interfaceB + ' parent ' + str ( parentIDFirstPart ) + ' : ' + str ( classIDCounter ) + ' cake ' )
shell ( ' tc filter add dev ' + interfaceB + ' parent ' + str ( parentIDFirstPart ) + ' : prio 5 u32 ht ' + str ( hashIDCounter ) + ' : ' + twoDigitHashString + ' match ip ' + srcOrDst + ' ' + ipAddr + ' flowid 1: ' + str ( classIDCounter ) )
classIDCounter + = 1
thirdDigitCounter + = 1
if ( srcOrDst == ' dst ' ) :
startPointForHash = ' 16 ' #Position of dst-address in IP header
elif ( srcOrDst == ' src ' ) :
startPointForHash = ' 12 ' #Position of src-address in IP header
shell ( ' tc filter add dev ' + interfaceB + ' parent ' + str ( parentIDFirstPart ) + ' : prio 5 u32 ht 800:: match ip ' + srcOrDst + ' ' + thisSlash16Dec1 + ' . ' + thisSlash16Dec2 + ' .0.0/16 hashkey mask 0x000000ff at ' + startPointForHash + ' link ' + str ( hashIDCounter ) + ' : ' )
hashIDCounter + = 1
#Done
today = date . today ( )
d1 = today . strftime ( " %d / % m/ % Y " )
print ( " Successful run completed at " , d1 )
print ( " Program complete " )