Add networking code
Signed-off-by: Martin J. Bligh <mbligh@google.com>
git-svn-id: http://test.kernel.org/svn/autotest/trunk@2480 592f7852-d20e-0410-864c-8624ca9c26a4
diff --git a/client/bin/net/net_tc.py b/client/bin/net/net_tc.py
new file mode 100755
index 0000000..06cf8bb
--- /dev/null
+++ b/client/bin/net/net_tc.py
@@ -0,0 +1,413 @@
+"""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, utils
+from autotest_lib.client.bin import autotest_utils
+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)