Home | History | Annotate | Download | only in net
      1 """Convenience methods for use to manipulate traffic control settings.
      2 
      3 see http://linux.die.net/man/8/tc for details about traffic controls in linux.
      4 
      5 Example
      6   import common
      7   from autotest_lib.client.bin.net.net_tc import *
      8   from autotest_lib.client.bin.net.net_utils import *
      9 
     10   class mock_netif(object):
     11 
     12     def __init__(self, name):
     13         self._name = name
     14 
     15     def get_name(self):
     16         return self._name
     17 
     18 
     19   netem_qdisc = netem()
     20   netem_qdisc.add_param('loss 100%')
     21 
     22   ack_filter = u32filter()
     23   ack_filter.add_rule('match ip protocol 6 0xff')
     24   ack_filter.add_rule('match u8 0x10 0x10 at nexthdr+13')
     25   ack_filter.set_dest_qdisc(netem_qdisc)
     26 
     27   root_qdisc = prio()
     28   root_qdisc.get_class(2).set_leaf_qdisc(netem_qdisc)
     29   root_qdisc.add_filter(ack_filter)
     30 
     31   lo_if = mock_netif('lo')
     32 
     33   root_qdisc.setup(lo_if)
     34 
     35   # run test here ...
     36   root_qdisc.restore(lo_if)
     37 
     38 """
     39 
     40 import commands, os, re
     41 import common
     42 from autotest_lib.client.common_lib import error
     43 from autotest_lib.client.bin.net import net_utils
     44 
     45 # TODO (chavey) clean up those global here and new_handle()
     46 handle_counter = 0
     47 INCR = 100
     48 
     49 
     50 def new_handle():
     51     global handle_counter
     52     handle_counter += INCR
     53     return handle_counter
     54 
     55 
     56 class tcclass(object):
     57 
     58     def __init__(self, handle, minor, leaf_qdisc=None):
     59         self._parent_class = None
     60         self._children = []
     61         self._leaf_qdisc = leaf_qdisc
     62         self._handle = handle
     63         self._minor = minor
     64 
     65 
     66     def get_leaf_qdisc(self):
     67         return self._leaf_qdisc
     68 
     69 
     70     def set_leaf_qdisc(self, leaf_qdisc):
     71         leaf_qdisc.set_parent_class(self)
     72         self._leaf_qdisc = leaf_qdisc
     73 
     74 
     75     def get_parent_class(self):
     76         return self._parent_class
     77 
     78 
     79     def set_parent_class(self, parent_class):
     80         self._parent_class = parent_class
     81 
     82 
     83     def get_minor(self):
     84         return self._minor
     85 
     86 
     87     def id(self):
     88         return '%s:%s' % (self._handle, self._minor)
     89 
     90 
     91     def add_child(self, child_class):
     92         child_class.set_parent_class(self)
     93         if child_class not in self._children:
     94             self._child.append(child_class)
     95 
     96 
     97     def setup(self, netif):
     98         # setup leaf qdisc
     99         if self._leaf_qdisc:
    100             self._leaf_qdisc.setup(netif)
    101 
    102         # setup child classes
    103         for child in self._children:
    104             child.setup()
    105 
    106 
    107     def restore(self, netif):
    108         # restore child classes
    109         children_copy = list(self._children)
    110         children_copy.reverse()
    111         for child in children_copy:
    112             child.restore()
    113 
    114         # restore leaf qdisc
    115         if self._leaf_qdisc:
    116             self._leaf_qdisc.restore(netif)
    117 
    118 
    119 class tcfilter(object):
    120 
    121     _tc_cmd = 'tc filter %(cmd)s dev %(dev)s parent %(parent)s protocol ' \
    122                '%(protocol)s prio %(priority)s %(filtertype)s \\\n ' \
    123                '%(rules)s \\\n  flowid %(flowid)s'
    124 
    125     conf_device = 'dev'
    126     conf_parent = 'parent'
    127     conf_type = 'filtertype'
    128     conf_protocol = 'protocol'
    129     conf_priority = 'priority'
    130     conf_flowid = 'flowid'
    131     conf_command = 'cmd'
    132     conf_rules = 'cmd'
    133     conf_qdiscid = 'qdiscid'
    134     conf_name = 'name'
    135     conf_params = 'params'
    136 
    137 
    138     def __init__(self):
    139         self._parent_qdisc = None
    140         self._dest_qdisc = None
    141         self._protocol = 'ip'
    142         self._priority = 1
    143         self._handle = None
    144         self._tc_conf = None
    145 
    146 
    147     def get_parent_qdisc(self):
    148         return self._parent_qdisc
    149 
    150 
    151     def set_parent_qdisc(self, parent_qdisc):
    152         self._parent_qdisc = parent_qdisc
    153 
    154 
    155     def get_dest_qdisc(self):
    156         return self._dest_qdisc
    157 
    158 
    159     def set_dest_qdisc(self, dest_qdisc):
    160         self._dest_qdisc = dest_qdisc
    161 
    162 
    163     def get_protocol(self):
    164         return self._protocol
    165 
    166 
    167     def set_protocol(self, protocol):
    168         self._protocol = protocol
    169 
    170 
    171     def get_priority(self):
    172         return self._priority
    173 
    174 
    175     def set_priority(self, priority):
    176         self._priority = priority
    177 
    178 
    179     def get_handle(self):
    180         return self._handle
    181 
    182 
    183     def set_handle(self, handle):
    184         self._handle = handle
    185 
    186 
    187     def _get_tc_conf(self, netif):
    188         if self._tc_conf:
    189             return self._tc_conf
    190         self._tc_conf = dict()
    191         self._tc_conf[tcfilter.conf_device] = netif.get_name()
    192         self._tc_conf[tcfilter.conf_parent] = self._parent_qdisc.id()
    193         self._tc_conf[tcfilter.conf_type] = self.filtertype
    194         self._tc_conf[tcfilter.conf_protocol] = self._protocol
    195         self._tc_conf[tcfilter.conf_priotity] = self._priority
    196         self._tc_conf[tcfilter.conf_flowid] = (
    197             self._dest_qdisc.get_parent_class().id())
    198         return self._tc_conf
    199 
    200 
    201     def tc_cmd(self, tc_conf):
    202         print self._tc_cmd % tc_conf
    203 
    204 
    205     def setup(self, netif):
    206         pass
    207 
    208 
    209     def restore(self, netif):
    210         pass
    211 
    212 
    213 class u32filter(tcfilter):
    214 
    215     filtertype = 'u32'
    216 
    217     def __init__(self):
    218         super(u32filter, self).__init__()
    219         self._rules = []
    220 
    221 
    222     def _filter_rules(self):
    223         return ' \\\n  '.join(self._rules)
    224 
    225 
    226     def add_rule(self, rule):
    227         self._rules.append(rule)
    228 
    229 
    230     def setup(self, netif):
    231         tc_conf = self._get_tc_conf(netif)
    232         tc_conf[tcfilter.conf_cmd] = 'add'
    233         tc_conf[tcfilter.conf_rules] = self._filter_rules()
    234         self.tc_cmd(tc_conf)
    235 
    236 
    237     def restore(self, netif):
    238         tc_conf = self._get_tc_conf(netif)
    239         tc_conf[tcfilter.conf_cmd] = 'del'
    240         tc_conf[tcfilter.conf_rules] = self._filter_rules()
    241         self.tc_cmd(tc_conf)
    242 
    243 #TODO (ncrao): generate some typical rules: ack, syn, synack,
    244 #              dport/sport, daddr/sddr, etc.
    245 class qdisc(object):
    246 
    247     # tc command
    248     _tc_cmd = 'tc qdisc %(cmd)s dev %(dev)s %(parent)s ' \
    249               'handle %(qdiscid)s %(name)s %(params)s'
    250 
    251     def __init__(self, handle):
    252         self._handle = handle
    253         self._parent_class = None
    254         self._tc_conf = None
    255 
    256 
    257     def get_handle(self):
    258         return self._handle
    259 
    260 
    261     def get_parent_class(self):
    262         return self._parent_class
    263 
    264 
    265     def set_parent_class(self, parent_class):
    266         self._parent_class = parent_class
    267 
    268 
    269     def _get_tc_conf(self, netif):
    270         if self._tc_conf:
    271             return self._tc_conf
    272         self._tc_conf = dict()
    273         self._tc_conf[tcfilter.conf_device] = netif.get_name()
    274         if self._parent_class:
    275             self._tc_conf[tcfilter.conf_parent] = ('parent %s' %
    276                                                    self._parent_class.id())
    277         else:
    278             self._tc_conf[tcfilter.conf_parent] = 'root'
    279         self._tc_conf[tcfilter.conf_qdiscid] = self.id()
    280         self._tc_conf[tcfilter.conf_name] = self.name
    281         self._tc_conf[tcfilter.conf_params] = ''
    282         return self._tc_conf
    283 
    284 
    285     def id(self):
    286         return '%s:0' % self._handle
    287 
    288 
    289     def tc_cmd(self, tc_conf):
    290         print self._tc_cmd % tc_conf
    291 
    292 
    293     def setup(self, netif):
    294         tc_conf = self._get_tc_conf(netif)
    295         tc_conf[tcfilter.conf_command] = 'add'
    296         self.tc_cmd(tc_conf)
    297 
    298 
    299     def restore(self, netif):
    300         tc_conf = self._get_tc_conf(netif)
    301         tc_conf[tcfilter.conf_command] = 'del'
    302         self.tc_cmd(tc_conf)
    303 
    304 
    305 class classful_qdisc(qdisc):
    306 
    307     classful = True
    308 
    309     def __init__(self, handle):
    310         super(classful_qdisc, self).__init__(handle)
    311         self._classes = []
    312         self._filters = []
    313 
    314 
    315     def add_class(self, child_class):
    316         self._classes.append(child_class)
    317 
    318 
    319     def add_filter(self, filter):
    320         filter.set_parent_qdisc(self)
    321         self._filters.append(filter)
    322 
    323 
    324     def setup(self, netif):
    325         super(classful_qdisc, self).setup(netif)
    326 
    327         # setup child classes
    328         for child in self._classes:
    329             child.setup(netif)
    330 
    331         # setup filters
    332         for filter in self._filters:
    333             filter.setup(netif)
    334 
    335 
    336     def restore(self, netif):
    337         # restore filters
    338         filters_copy = list(self._filters)
    339         filters_copy.reverse()
    340         for filter in filters_copy:
    341             filter.restore(netif)
    342 
    343         # restore child classes
    344         classes_copy = list(self._classes)
    345         classes_copy.reverse()
    346         for child in classes_copy:
    347             child.restore(netif)
    348 
    349         super(classful_qdisc, self).restore(netif)
    350 
    351 
    352 class prio(classful_qdisc):
    353 
    354     name = 'prio'
    355 
    356     def __init__(self, handle=new_handle(), bands=3):
    357         super(prio, self).__init__(handle)
    358         self._bands = bands
    359         for counter in range(bands):
    360             self.add_class(tcclass(handle, counter + 1))
    361 
    362 
    363     def setup(self, netif):
    364         super(prio, self).setup(netif)
    365 
    366 
    367     def get_class(self, band):
    368         if band > self._bands:
    369             raise error.TestError('error inserting %s at band %s' % \
    370                                   (qdisc.name, band))
    371         return self._classes[band]
    372 
    373 
    374 class classless_qdisc(qdisc):
    375 
    376     classful = False
    377 
    378     def __init__(self, handle):
    379         super(classless_qdisc, self).__init__(handle)
    380 
    381 
    382 class pfifo(classless_qdisc):
    383 
    384     name = 'pfifo'
    385 
    386     def __init__(self, handle=new_handle()):
    387         super(pfifo, self).__init__(handle)
    388 
    389 
    390     def setup(self, netif):
    391         super(pfifo, self).setup(netif)
    392 
    393 
    394 class netem(classless_qdisc):
    395 
    396     name = 'netem'
    397 
    398     def __init__(self, handle=new_handle()):
    399         super(netem, self).__init__(handle)
    400         self._params = list()
    401 
    402 
    403     def add_param(self, param):
    404         self._params.append(param)
    405 
    406 
    407     def setup(self, netif):
    408         super(netem, self).setup(netif)
    409         tc_conf = self._get_tc_conf(netif)
    410         tc_conf[tcfilter.conf_command] = 'change'
    411         tc_conf[tcfilter.conf_params] = ' '.join(self._params)
    412         self.tc_cmd(tc_conf)
    413