412 lines
9.4 KiB
Python
412 lines
9.4 KiB
Python
"""Convenience methods for use to manipulate traffic control settings.
|
|
|
|
see http://linux.die.net/man/8/tc for details about traffic controls in linux.
|
|
|
|
Example
|
|
import common
|
|
from autotest_lib.client.bin.net.net_tc import *
|
|
from autotest_lib.client.bin.net.net_utils import *
|
|
|
|
class mock_netif(object):
|
|
|
|
def __init__(self, name):
|
|
self._name = name
|
|
|
|
def get_name(self):
|
|
return self._name
|
|
|
|
|
|
netem_qdisc = netem()
|
|
netem_qdisc.add_param('loss 100%')
|
|
|
|
ack_filter = u32filter()
|
|
ack_filter.add_rule('match ip protocol 6 0xff')
|
|
ack_filter.add_rule('match u8 0x10 0x10 at nexthdr+13')
|
|
ack_filter.set_dest_qdisc(netem_qdisc)
|
|
|
|
root_qdisc = prio()
|
|
root_qdisc.get_class(2).set_leaf_qdisc(netem_qdisc)
|
|
root_qdisc.add_filter(ack_filter)
|
|
|
|
lo_if = mock_netif('lo')
|
|
|
|
root_qdisc.setup(lo_if)
|
|
|
|
# run test here ...
|
|
root_qdisc.restore(lo_if)
|
|
|
|
"""
|
|
|
|
import commands, os, re
|
|
import common
|
|
from autotest_lib.client.common_lib import error
|
|
from autotest_lib.client.bin.net import net_utils
|
|
|
|
# TODO (chavey) clean up those global here and new_handle()
|
|
handle_counter = 0
|
|
INCR = 100
|
|
|
|
|
|
def new_handle():
|
|
global handle_counter
|
|
handle_counter += INCR
|
|
return handle_counter
|
|
|
|
|
|
class tcclass(object):
|
|
|
|
def __init__(self, handle, minor, leaf_qdisc=None):
|
|
self._parent_class = None
|
|
self._children = []
|
|
self._leaf_qdisc = leaf_qdisc
|
|
self._handle = handle
|
|
self._minor = minor
|
|
|
|
|
|
def get_leaf_qdisc(self):
|
|
return self._leaf_qdisc
|
|
|
|
|
|
def set_leaf_qdisc(self, leaf_qdisc):
|
|
leaf_qdisc.set_parent_class(self)
|
|
self._leaf_qdisc = leaf_qdisc
|
|
|
|
|
|
def get_parent_class(self):
|
|
return self._parent_class
|
|
|
|
|
|
def set_parent_class(self, parent_class):
|
|
self._parent_class = parent_class
|
|
|
|
|
|
def get_minor(self):
|
|
return self._minor
|
|
|
|
|
|
def id(self):
|
|
return '%s:%s' % (self._handle, self._minor)
|
|
|
|
|
|
def add_child(self, child_class):
|
|
child_class.set_parent_class(self)
|
|
if child_class not in self._children:
|
|
self._child.append(child_class)
|
|
|
|
|
|
def setup(self, netif):
|
|
# setup leaf qdisc
|
|
if self._leaf_qdisc:
|
|
self._leaf_qdisc.setup(netif)
|
|
|
|
# setup child classes
|
|
for child in self._children:
|
|
child.setup()
|
|
|
|
|
|
def restore(self, netif):
|
|
# restore child classes
|
|
children_copy = list(self._children)
|
|
children_copy.reverse()
|
|
for child in children_copy:
|
|
child.restore()
|
|
|
|
# restore leaf qdisc
|
|
if self._leaf_qdisc:
|
|
self._leaf_qdisc.restore(netif)
|
|
|
|
|
|
class tcfilter(object):
|
|
|
|
_tc_cmd = 'tc filter %(cmd)s dev %(dev)s parent %(parent)s protocol ' \
|
|
'%(protocol)s prio %(priority)s %(filtertype)s \\\n ' \
|
|
'%(rules)s \\\n flowid %(flowid)s'
|
|
|
|
conf_device = 'dev'
|
|
conf_parent = 'parent'
|
|
conf_type = 'filtertype'
|
|
conf_protocol = 'protocol'
|
|
conf_priority = 'priority'
|
|
conf_flowid = 'flowid'
|
|
conf_command = 'cmd'
|
|
conf_rules = 'cmd'
|
|
conf_qdiscid = 'qdiscid'
|
|
conf_name = 'name'
|
|
conf_params = 'params'
|
|
|
|
|
|
def __init__(self):
|
|
self._parent_qdisc = None
|
|
self._dest_qdisc = None
|
|
self._protocol = 'ip'
|
|
self._priority = 1
|
|
self._handle = None
|
|
self._tc_conf = None
|
|
|
|
|
|
def get_parent_qdisc(self):
|
|
return self._parent_qdisc
|
|
|
|
|
|
def set_parent_qdisc(self, parent_qdisc):
|
|
self._parent_qdisc = parent_qdisc
|
|
|
|
|
|
def get_dest_qdisc(self):
|
|
return self._dest_qdisc
|
|
|
|
|
|
def set_dest_qdisc(self, dest_qdisc):
|
|
self._dest_qdisc = dest_qdisc
|
|
|
|
|
|
def get_protocol(self):
|
|
return self._protocol
|
|
|
|
|
|
def set_protocol(self, protocol):
|
|
self._protocol = protocol
|
|
|
|
|
|
def get_priority(self):
|
|
return self._priority
|
|
|
|
|
|
def set_priority(self, priority):
|
|
self._priority = priority
|
|
|
|
|
|
def get_handle(self):
|
|
return self._handle
|
|
|
|
|
|
def set_handle(self, handle):
|
|
self._handle = handle
|
|
|
|
|
|
def _get_tc_conf(self, netif):
|
|
if self._tc_conf:
|
|
return self._tc_conf
|
|
self._tc_conf = dict()
|
|
self._tc_conf[tcfilter.conf_device] = netif.get_name()
|
|
self._tc_conf[tcfilter.conf_parent] = self._parent_qdisc.id()
|
|
self._tc_conf[tcfilter.conf_type] = self.filtertype
|
|
self._tc_conf[tcfilter.conf_protocol] = self._protocol
|
|
self._tc_conf[tcfilter.conf_priotity] = self._priority
|
|
self._tc_conf[tcfilter.conf_flowid] = (
|
|
self._dest_qdisc.get_parent_class().id())
|
|
return self._tc_conf
|
|
|
|
|
|
def tc_cmd(self, tc_conf):
|
|
print self._tc_cmd % tc_conf
|
|
|
|
|
|
def setup(self, netif):
|
|
pass
|
|
|
|
|
|
def restore(self, netif):
|
|
pass
|
|
|
|
|
|
class u32filter(tcfilter):
|
|
|
|
filtertype = 'u32'
|
|
|
|
def __init__(self):
|
|
super(u32filter, self).__init__()
|
|
self._rules = []
|
|
|
|
|
|
def _filter_rules(self):
|
|
return ' \\\n '.join(self._rules)
|
|
|
|
|
|
def add_rule(self, rule):
|
|
self._rules.append(rule)
|
|
|
|
|
|
def setup(self, netif):
|
|
tc_conf = self._get_tc_conf(netif)
|
|
tc_conf[tcfilter.conf_cmd] = 'add'
|
|
tc_conf[tcfilter.conf_rules] = self._filter_rules()
|
|
self.tc_cmd(tc_conf)
|
|
|
|
|
|
def restore(self, netif):
|
|
tc_conf = self._get_tc_conf(netif)
|
|
tc_conf[tcfilter.conf_cmd] = 'del'
|
|
tc_conf[tcfilter.conf_rules] = self._filter_rules()
|
|
self.tc_cmd(tc_conf)
|
|
|
|
#TODO (ncrao): generate some typical rules: ack, syn, synack,
|
|
# dport/sport, daddr/sddr, etc.
|
|
class qdisc(object):
|
|
|
|
# tc command
|
|
_tc_cmd = 'tc qdisc %(cmd)s dev %(dev)s %(parent)s ' \
|
|
'handle %(qdiscid)s %(name)s %(params)s'
|
|
|
|
def __init__(self, handle):
|
|
self._handle = handle
|
|
self._parent_class = None
|
|
self._tc_conf = None
|
|
|
|
|
|
def get_handle(self):
|
|
return self._handle
|
|
|
|
|
|
def get_parent_class(self):
|
|
return self._parent_class
|
|
|
|
|
|
def set_parent_class(self, parent_class):
|
|
self._parent_class = parent_class
|
|
|
|
|
|
def _get_tc_conf(self, netif):
|
|
if self._tc_conf:
|
|
return self._tc_conf
|
|
self._tc_conf = dict()
|
|
self._tc_conf[tcfilter.conf_device] = netif.get_name()
|
|
if self._parent_class:
|
|
self._tc_conf[tcfilter.conf_parent] = ('parent %s' %
|
|
self._parent_class.id())
|
|
else:
|
|
self._tc_conf[tcfilter.conf_parent] = 'root'
|
|
self._tc_conf[tcfilter.conf_qdiscid] = self.id()
|
|
self._tc_conf[tcfilter.conf_name] = self.name
|
|
self._tc_conf[tcfilter.conf_params] = ''
|
|
return self._tc_conf
|
|
|
|
|
|
def id(self):
|
|
return '%s:0' % self._handle
|
|
|
|
|
|
def tc_cmd(self, tc_conf):
|
|
print self._tc_cmd % tc_conf
|
|
|
|
|
|
def setup(self, netif):
|
|
tc_conf = self._get_tc_conf(netif)
|
|
tc_conf[tcfilter.conf_command] = 'add'
|
|
self.tc_cmd(tc_conf)
|
|
|
|
|
|
def restore(self, netif):
|
|
tc_conf = self._get_tc_conf(netif)
|
|
tc_conf[tcfilter.conf_command] = 'del'
|
|
self.tc_cmd(tc_conf)
|
|
|
|
|
|
class classful_qdisc(qdisc):
|
|
|
|
classful = True
|
|
|
|
def __init__(self, handle):
|
|
super(classful_qdisc, self).__init__(handle)
|
|
self._classes = []
|
|
self._filters = []
|
|
|
|
|
|
def add_class(self, child_class):
|
|
self._classes.append(child_class)
|
|
|
|
|
|
def add_filter(self, filter):
|
|
filter.set_parent_qdisc(self)
|
|
self._filters.append(filter)
|
|
|
|
|
|
def setup(self, netif):
|
|
super(classful_qdisc, self).setup(netif)
|
|
|
|
# setup child classes
|
|
for child in self._classes:
|
|
child.setup(netif)
|
|
|
|
# setup filters
|
|
for filter in self._filters:
|
|
filter.setup(netif)
|
|
|
|
|
|
def restore(self, netif):
|
|
# restore filters
|
|
filters_copy = list(self._filters)
|
|
filters_copy.reverse()
|
|
for filter in filters_copy:
|
|
filter.restore(netif)
|
|
|
|
# restore child classes
|
|
classes_copy = list(self._classes)
|
|
classes_copy.reverse()
|
|
for child in classes_copy:
|
|
child.restore(netif)
|
|
|
|
super(classful_qdisc, self).restore(netif)
|
|
|
|
|
|
class prio(classful_qdisc):
|
|
|
|
name = 'prio'
|
|
|
|
def __init__(self, handle=new_handle(), bands=3):
|
|
super(prio, self).__init__(handle)
|
|
self._bands = bands
|
|
for counter in range(bands):
|
|
self.add_class(tcclass(handle, counter + 1))
|
|
|
|
|
|
def setup(self, netif):
|
|
super(prio, self).setup(netif)
|
|
|
|
|
|
def get_class(self, band):
|
|
if band > self._bands:
|
|
raise error.TestError('error inserting %s at band %s' % \
|
|
(qdisc.name, band))
|
|
return self._classes[band]
|
|
|
|
|
|
class classless_qdisc(qdisc):
|
|
|
|
classful = False
|
|
|
|
def __init__(self, handle):
|
|
super(classless_qdisc, self).__init__(handle)
|
|
|
|
|
|
class pfifo(classless_qdisc):
|
|
|
|
name = 'pfifo'
|
|
|
|
def __init__(self, handle=new_handle()):
|
|
super(pfifo, self).__init__(handle)
|
|
|
|
|
|
def setup(self, netif):
|
|
super(pfifo, self).setup(netif)
|
|
|
|
|
|
class netem(classless_qdisc):
|
|
|
|
name = 'netem'
|
|
|
|
def __init__(self, handle=new_handle()):
|
|
super(netem, self).__init__(handle)
|
|
self._params = list()
|
|
|
|
|
|
def add_param(self, param):
|
|
self._params.append(param)
|
|
|
|
|
|
def setup(self, netif):
|
|
super(netem, self).setup(netif)
|
|
tc_conf = self._get_tc_conf(netif)
|
|
tc_conf[tcfilter.conf_command] = 'change'
|
|
tc_conf[tcfilter.conf_params] = ' '.join(self._params)
|
|
self.tc_cmd(tc_conf)
|