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