"""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)