2 # Copyright (c) 2011 Thomas Graf <tgraf@suug.ch>
4 from __future__ import absolute_import
15 from .. import core as netlink
16 from . import capi as capi
17 from .. import util as util
18 from . import link as Link
30 TC_H_ROOT = 0xFFFFFFFF
31 TC_H_INGRESS = 0xFFFFFFF1
42 STAT_MAX = STAT_OVERLIMITS
46 """ Traffic control handle
48 Representation of a traffic control handle which uniquely identifies
49 each traffic control object in its link namespace.
51 handle = tc.Handle('10:20')
52 handle = tc.handle('root')
56 def __init__(self, val=None):
58 val = capi.tc_str2handle(val)
64 def __cmp__(self, other):
68 if isinstance(other, Handle):
69 return int(self) - int(other)
70 elif isinstance(other, int):
71 return int(self) - other
79 return capi.rtnl_tc_handle2str(self._val, 64)[0]
82 return self._val == TC_H_ROOT or self._val == TC_H_INGRESS
84 class TcCache(netlink.Cache):
85 """Cache of traffic control object"""
87 def __getitem__(self, key):
88 raise NotImplementedError()
90 class Tc(netlink.Object):
91 def __cmp__(self, other):
92 diff = self.ifindex - other.ifindex
94 diff = int(self.handle) - int(other.handle)
97 def _tc_module_lookup(self):
98 self._module_lookup(self._module_path + self.kind,
103 """True if tc object is a root object"""
104 return self.parent.isroot()
108 """interface index"""
109 return capi.rtnl_tc_get_ifindex(self._rtnl_tc)
112 def ifindex(self, value):
113 capi.rtnl_tc_set_ifindex(self._rtnl_tc, int(value))
117 link = capi.rtnl_tc_get_link(self._rtnl_tc)
121 return Link.Link.from_capi(link)
124 def link(self, value):
125 capi.rtnl_tc_set_link(self._rtnl_tc, value._link)
129 return capi.rtnl_tc_get_mtu(self._rtnl_tc)
132 def mtu(self, value):
133 capi.rtnl_tc_set_mtu(self._rtnl_tc, int(value))
137 return capi.rtnl_tc_get_mpu(self._rtnl_tc)
140 def mpu(self, value):
141 capi.rtnl_tc_set_mpu(self._rtnl_tc, int(value))
145 return capi.rtnl_tc_get_overhead(self._rtnl_tc)
148 def overhead(self, value):
149 capi.rtnl_tc_set_overhead(self._rtnl_tc, int(value))
153 return capi.rtnl_tc_get_linktype(self._rtnl_tc)
156 def linktype(self, value):
157 capi.rtnl_tc_set_linktype(self._rtnl_tc, int(value))
160 @netlink.nlattr(fmt=util.handle)
162 return Handle(capi.rtnl_tc_get_handle(self._rtnl_tc))
165 def handle(self, value):
166 capi.rtnl_tc_set_handle(self._rtnl_tc, int(value))
169 @netlink.nlattr(fmt=util.handle)
171 return Handle(capi.rtnl_tc_get_parent(self._rtnl_tc))
174 def parent(self, value):
175 capi.rtnl_tc_set_parent(self._rtnl_tc, int(value))
178 @netlink.nlattr(fmt=util.bold)
180 return capi.rtnl_tc_get_kind(self._rtnl_tc)
183 def kind(self, value):
184 capi.rtnl_tc_set_kind(self._rtnl_tc, value)
185 self._tc_module_lookup()
187 def get_stat(self, id):
188 return capi.rtnl_tc_get_stat(self._rtnl_tc, id)
192 buf = util.kw('dev') + ' '
195 return buf + util.string(self.link.name)
197 return buf + util.num(self.ifindex)
199 def brief(self, title, nodev=False, noparent=False):
200 ret = title + ' {a|kind} {a|handle}'
208 return ret + self._module_brief()
212 return '{t|mtu} {t|mpu} {t|overhead} {t|linktype}'
216 return self.get_stat(STAT_PACKETS)
220 return self.get_stat(STAT_BYTES)
224 return self.get_stat(STAT_QLEN)
228 return fmt.nl('{t|packets} {t|bytes} {t|qlen}')
230 class QdiscCache(netlink.Cache):
231 """Cache of qdiscs"""
233 def __init__(self, cache=None):
235 cache = self._alloc_cache_name('route/qdisc')
237 self._protocol = netlink.NETLINK_ROUTE
238 self._nl_cache = cache
240 # def __getitem__(self, key):
241 # if type(key) is int:
242 # link = capi.rtnl_link_get(self._this, key)
243 # elif type(key) is str:
244 # link = capi.rtnl_link_get_by_name(self._this, key)
249 # return Qdisc._from_capi(capi.qdisc2obj(qdisc))
252 def _new_object(obj):
256 def _new_cache(cache):
257 return QdiscCache(cache=cache)
260 """Queueing discipline"""
262 def __init__(self, obj=None):
263 netlink.Object.__init__(self, 'route/qdisc', 'qdisc', obj)
264 self._module_path = 'netlink.route.qdisc.'
265 self._rtnl_qdisc = self._obj2type(self._nl_object)
266 self._rtnl_tc = capi.obj2tc(self._nl_object)
269 self._tc_module_lookup()
272 def from_capi(cls, obj):
273 return cls(capi.qdisc2obj(obj))
277 return capi.obj2qdisc(obj)
280 def _new_instance(obj):
291 ret += get_cls(self.ifindex, parent=self.handle)
294 ret += get_class(self.ifindex, parent=TC_H_ROOT)
296 ret += get_class(self.ifindex, parent=self.handle)
300 # def add(self, socket, flags=None):
302 # flags = netlink.NLM_F_CREATE
304 # ret = capi.rtnl_link_add(socket._sock, self._link, flags)
306 # raise netlink.KernelError(ret)
308 # def change(self, socket, flags=0):
309 # """Commit changes made to the link object"""
311 # raise NetlinkError('Original link not available')
312 # ret = capi.rtnl_link_change(socket._sock, self._orig, self._link, flags)
314 # raise netlink.KernelError(ret)
316 # def delete(self, socket):
317 # """Attempt to delete this link in the kernel"""
318 # ret = capi.rtnl_link_delete(socket._sock, self._link)
320 # raise netlink.KernelError(ret)
322 def format(self, details=False, stats=False, nodev=False,
323 noparent=False, indent=''):
324 """Return qdisc as formatted text"""
325 fmt = util.MyFormatter(self, indent)
327 buf = fmt.format(self.brief('qdisc', nodev, noparent))
330 buf += fmt.nl('\t' + self.details())
333 buf += self.stats(fmt)
336 # l = [['Packets', RX_PACKETS, TX_PACKETS],
337 # ['Bytes', RX_BYTES, TX_BYTES],
338 # ['Errors', RX_ERRORS, TX_ERRORS],
339 # ['Dropped', RX_DROPPED, TX_DROPPED],
340 # ['Compressed', RX_COMPRESSED, TX_COMPRESSED],
341 # ['FIFO Errors', RX_FIFO_ERR, TX_FIFO_ERR],
342 # ['Length Errors', RX_LEN_ERR, None],
343 # ['Over Errors', RX_OVER_ERR, None],
344 # ['CRC Errors', RX_CRC_ERR, None],
345 # ['Frame Errors', RX_FRAME_ERR, None],
346 # ['Missed Errors', RX_MISSED_ERR, None],
347 # ['Abort Errors', None, TX_ABORT_ERR],
348 # ['Carrier Errors', None, TX_CARRIER_ERR],
349 # ['Heartbeat Errors', None, TX_HBEAT_ERR],
350 # ['Window Errors', None, TX_WIN_ERR],
351 # ['Collisions', None, COLLISIONS],
352 # ['Multicast', None, MULTICAST],
354 # ['Ipv6:', None, None],
355 # ['Packets', IP6_INPKTS, IP6_OUTPKTS],
356 # ['Bytes', IP6_INOCTETS, IP6_OUTOCTETS],
357 # ['Discards', IP6_INDISCARDS, IP6_OUTDISCARDS],
358 # ['Multicast Packets', IP6_INMCASTPKTS, IP6_OUTMCASTPKTS],
359 # ['Multicast Bytes', IP6_INMCASTOCTETS, IP6_OUTMCASTOCTETS],
360 # ['Broadcast Packets', IP6_INBCASTPKTS, IP6_OUTBCASTPKTS],
361 # ['Broadcast Bytes', IP6_INBCASTOCTETS, IP6_OUTBCASTOCTETS],
362 # ['Delivers', IP6_INDELIVERS, None],
363 # ['Forwarded', None, IP6_OUTFORWDATAGRAMS],
364 # ['No Routes', IP6_INNOROUTES, IP6_OUTNOROUTES],
365 # ['Header Errors', IP6_INHDRERRORS, None],
366 # ['Too Big Errors', IP6_INTOOBIGERRORS, None],
367 # ['Address Errors', IP6_INADDRERRORS, None],
368 # ['Unknown Protocol', IP6_INUNKNOWNPROTOS, None],
369 # ['Truncated Packets', IP6_INTRUNCATEDPKTS, None],
370 # ['Reasm Timeouts', IP6_REASMTIMEOUT, None],
371 # ['Reasm Requests', IP6_REASMREQDS, None],
372 # ['Reasm Failures', IP6_REASMFAILS, None],
373 # ['Reasm OK', IP6_REASMOKS, None],
374 # ['Frag Created', None, IP6_FRAGCREATES],
375 # ['Frag Failures', None, IP6_FRAGFAILS],
376 # ['Frag OK', None, IP6_FRAGOKS],
378 # ['ICMPv6:', None, None],
379 # ['Messages', ICMP6_INMSGS, ICMP6_OUTMSGS],
380 # ['Errors', ICMP6_INERRORS, ICMP6_OUTERRORS]]
382 # buf += '\n\t%s%s%s%s\n' % (33 * ' ', util.title('RX'),
383 # 15 * ' ', util.title('TX'))
386 # row[0] = util.kw(row[0])
387 # row[1] = self.get_stat(row[1]) if row[1] else ''
388 # row[2] = self.get_stat(row[2]) if row[2] else ''
389 # buf += '\t{0:27} {1:>16} {2:>16}\n'.format(*row)
393 class TcClassCache(netlink.Cache):
394 """Cache of traffic classes"""
396 def __init__(self, ifindex, cache=None):
398 cache = self._alloc_cache_name('route/class')
400 self._protocol = netlink.NETLINK_ROUTE
401 self._nl_cache = cache
402 self._set_arg1(ifindex)
405 def _new_object(obj):
408 def _new_cache(self, cache):
409 return TcClassCache(self.arg1, cache=cache)
414 def __init__(self, obj=None):
415 netlink.Object.__init__(self, 'route/class', 'class', obj)
416 self._module_path = 'netlink.route.qdisc.'
417 self._rtnl_class = self._obj2type(self._nl_object)
418 self._rtnl_tc = capi.obj2tc(self._nl_object)
421 self._tc_module_lookup()
424 def from_capi(cls, obj):
425 return cls(capi.class2obj(obj))
429 return capi.obj2class(obj)
432 def _new_instance(obj):
442 # classes can have classifiers, child classes and leaf
444 ret += get_cls(self.ifindex, parent=self.handle)
445 ret += get_class(self.ifindex, parent=self.handle)
446 ret += get_qdisc(self.ifindex, parent=self.handle)
450 def format(self, details=False, _stats=False, nodev=False,
451 noparent=False, indent=''):
452 """Return class as formatted text"""
453 fmt = util.MyFormatter(self, indent)
455 buf = fmt.format(self.brief('class', nodev, noparent))
458 buf += fmt.nl('\t' + self.details())
462 class ClassifierCache(netlink.Cache):
463 """Cache of traffic classifiers objects"""
465 def __init__(self, ifindex, parent, cache=None):
467 cache = self._alloc_cache_name('route/cls')
469 self._protocol = netlink.NETLINK_ROUTE
470 self._nl_cache = cache
471 self._set_arg1(ifindex)
472 self._set_arg2(int(parent))
475 def _new_object(obj):
476 return Classifier(obj)
478 def _new_cache(self, cache):
479 return ClassifierCache(self.arg1, self.arg2, cache=cache)
481 class Classifier(Tc):
484 def __init__(self, obj=None):
485 netlink.Object.__init__(self, 'route/cls', 'cls', obj)
486 self._module_path = 'netlink.route.cls.'
487 self._rtnl_cls = self._obj2type(self._nl_object)
488 self._rtnl_tc = capi.obj2tc(self._nl_object)
491 def from_capi(cls, obj):
492 return cls(capi.cls2obj(obj))
496 return capi.obj2cls(obj)
499 def _new_instance(obj):
503 return Classifier(obj)
507 return capi.rtnl_cls_get_prio(self._rtnl_cls)
510 def priority(self, value):
511 capi.rtnl_cls_set_prio(self._rtnl_cls, int(value))
515 return capi.rtnl_cls_get_protocol(self._rtnl_cls)
518 def protocol(self, value):
519 capi.rtnl_cls_set_protocol(self._rtnl_cls, int(value))
525 def format(self, details=False, _stats=False, nodev=False,
526 noparent=False, indent=''):
527 """Return class as formatted text"""
528 fmt = util.MyFormatter(self, indent)
530 buf = fmt.format(self.brief('classifier', nodev, noparent))
531 buf += fmt.format(' {t|priority} {t|protocol}')
534 buf += fmt.nl('\t' + self.details())
538 _qdisc_cache = QdiscCache()
540 def get_qdisc(ifindex, handle=None, parent=None):
543 _qdisc_cache.refill()
545 for qdisc in _qdisc_cache:
546 if qdisc.ifindex != ifindex:
548 if (handle is not None) and (qdisc.handle != handle):
550 if (parent is not None) and (qdisc.parent != parent):
558 def get_class(ifindex, parent, handle=None):
562 cache = _class_cache[ifindex]
564 cache = TcClassCache(ifindex)
565 _class_cache[ifindex] = cache
570 if (parent is not None) and (cl.parent != parent):
572 if (handle is not None) and (cl.handle != handle):
580 def get_cls(ifindex, parent, handle=None):
582 chain = _cls_cache.get(ifindex, dict())
585 cache = chain[parent]
587 cache = ClassifierCache(ifindex, parent)
588 chain[parent] = cache
593 return [ cls for cls in cache ]
595 return [ cls for cls in cache if cls.handle == handle ]