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