123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595 |
- #
- # Copyright (c) 2011 Thomas Graf <tgraf@suug.ch>
- #
- from __future__ import absolute_import
- __all__ = [
- 'TcCache',
- 'Tc',
- 'QdiscCache',
- 'Qdisc',
- 'TcClassCache',
- 'TcClass',
- ]
- from .. import core as netlink
- from . import capi as capi
- from .. import util as util
- from . import link as Link
- TC_PACKETS = 0
- TC_BYTES = 1
- TC_RATE_BPS = 2
- TC_RATE_PPS = 3
- TC_QLEN = 4
- TC_BACKLOG = 5
- TC_DROPS = 6
- TC_REQUEUES = 7
- TC_OVERLIMITS = 9
- TC_H_ROOT = 0xFFFFFFFF
- TC_H_INGRESS = 0xFFFFFFF1
- STAT_PACKETS = 0
- STAT_BYTES = 1
- STAT_RATE_BPS = 2
- STAT_RATE_PPS = 3
- STAT_QLEN = 4
- STAT_BACKLOG = 5
- STAT_DROPS = 6
- STAT_REQUEUES = 7
- STAT_OVERLIMITS = 8
- STAT_MAX = STAT_OVERLIMITS
- class Handle(object):
- """ Traffic control handle
- Representation of a traffic control handle which uniquely identifies
- each traffic control object in its link namespace.
- handle = tc.Handle('10:20')
- handle = tc.handle('root')
- print int(handle)
- print str(handle)
- """
- def __init__(self, val=None):
- if type(val) is str:
- val = capi.tc_str2handle(val)
- elif not val:
- val = 0
- self._val = int(val)
- def __cmp__(self, other):
- if other is None:
- other = 0
- if isinstance(other, Handle):
- return int(self) - int(other)
- elif isinstance(other, int):
- return int(self) - other
- else:
- raise TypeError()
- def __int__(self):
- return self._val
- def __str__(self):
- return capi.rtnl_tc_handle2str(self._val, 64)[0]
- def isroot(self):
- return self._val == TC_H_ROOT or self._val == TC_H_INGRESS
- class TcCache(netlink.Cache):
- """Cache of traffic control object"""
- def __getitem__(self, key):
- raise NotImplementedError()
- class Tc(netlink.Object):
- def __cmp__(self, other):
- diff = self.ifindex - other.ifindex
- if diff == 0:
- diff = int(self.handle) - int(other.handle)
- return diff
- def _tc_module_lookup(self):
- self._module_lookup(self._module_path + self.kind,
- 'init_' + self._name)
- @property
- def root(self):
- """True if tc object is a root object"""
- return self.parent.isroot()
- @property
- def ifindex(self):
- """interface index"""
- return capi.rtnl_tc_get_ifindex(self._rtnl_tc)
- @ifindex.setter
- def ifindex(self, value):
- capi.rtnl_tc_set_ifindex(self._rtnl_tc, int(value))
- @property
- def link(self):
- link = capi.rtnl_tc_get_link(self._rtnl_tc)
- if not link:
- return None
- return Link.Link.from_capi(link)
- @link.setter
- def link(self, value):
- capi.rtnl_tc_set_link(self._rtnl_tc, value._link)
- @property
- def mtu(self):
- return capi.rtnl_tc_get_mtu(self._rtnl_tc)
- @mtu.setter
- def mtu(self, value):
- capi.rtnl_tc_set_mtu(self._rtnl_tc, int(value))
- @property
- def mpu(self):
- return capi.rtnl_tc_get_mpu(self._rtnl_tc)
- @mpu.setter
- def mpu(self, value):
- capi.rtnl_tc_set_mpu(self._rtnl_tc, int(value))
- @property
- def overhead(self):
- return capi.rtnl_tc_get_overhead(self._rtnl_tc)
- @overhead.setter
- def overhead(self, value):
- capi.rtnl_tc_set_overhead(self._rtnl_tc, int(value))
- @property
- def linktype(self):
- return capi.rtnl_tc_get_linktype(self._rtnl_tc)
- @linktype.setter
- def linktype(self, value):
- capi.rtnl_tc_set_linktype(self._rtnl_tc, int(value))
- @property
- @netlink.nlattr(fmt=util.handle)
- def handle(self):
- return Handle(capi.rtnl_tc_get_handle(self._rtnl_tc))
- @handle.setter
- def handle(self, value):
- capi.rtnl_tc_set_handle(self._rtnl_tc, int(value))
- @property
- @netlink.nlattr(fmt=util.handle)
- def parent(self):
- return Handle(capi.rtnl_tc_get_parent(self._rtnl_tc))
- @parent.setter
- def parent(self, value):
- capi.rtnl_tc_set_parent(self._rtnl_tc, int(value))
- @property
- @netlink.nlattr(fmt=util.bold)
- def kind(self):
- return capi.rtnl_tc_get_kind(self._rtnl_tc)
- @kind.setter
- def kind(self, value):
- capi.rtnl_tc_set_kind(self._rtnl_tc, value)
- self._tc_module_lookup()
- def get_stat(self, id):
- return capi.rtnl_tc_get_stat(self._rtnl_tc, id)
- @property
- def _dev(self):
- buf = util.kw('dev') + ' '
- if self.link:
- return buf + util.string(self.link.name)
- else:
- return buf + util.num(self.ifindex)
- def brief(self, title, nodev=False, noparent=False):
- ret = title + ' {a|kind} {a|handle}'
- if not nodev:
- ret += ' {a|_dev}'
- if not noparent:
- ret += ' {t|parent}'
- return ret + self._module_brief()
- @staticmethod
- def details():
- return '{t|mtu} {t|mpu} {t|overhead} {t|linktype}'
- @property
- def packets(self):
- return self.get_stat(STAT_PACKETS)
- @property
- def bytes(self):
- return self.get_stat(STAT_BYTES)
- @property
- def qlen(self):
- return self.get_stat(STAT_QLEN)
- @staticmethod
- def stats(fmt):
- return fmt.nl('{t|packets} {t|bytes} {t|qlen}')
- class QdiscCache(netlink.Cache):
- """Cache of qdiscs"""
- def __init__(self, cache=None):
- if not cache:
- cache = self._alloc_cache_name('route/qdisc')
- self._protocol = netlink.NETLINK_ROUTE
- self._nl_cache = cache
- # def __getitem__(self, key):
- # if type(key) is int:
- # link = capi.rtnl_link_get(self._this, key)
- # elif type(key) is str:
- # link = capi.rtnl_link_get_by_name(self._this, key)
- #
- # if qdisc is None:
- # raise KeyError()
- # else:
- # return Qdisc._from_capi(capi.qdisc2obj(qdisc))
- @staticmethod
- def _new_object(obj):
- return Qdisc(obj)
- @staticmethod
- def _new_cache(cache):
- return QdiscCache(cache=cache)
- class Qdisc(Tc):
- """Queueing discipline"""
- def __init__(self, obj=None):
- netlink.Object.__init__(self, 'route/qdisc', 'qdisc', obj)
- self._module_path = 'netlink.route.qdisc.'
- self._rtnl_qdisc = self._obj2type(self._nl_object)
- self._rtnl_tc = capi.obj2tc(self._nl_object)
- if self.kind:
- self._tc_module_lookup()
- @classmethod
- def from_capi(cls, obj):
- return cls(capi.qdisc2obj(obj))
- @staticmethod
- def _obj2type(obj):
- return capi.obj2qdisc(obj)
- @staticmethod
- def _new_instance(obj):
- if not obj:
- raise ValueError()
- return Qdisc(obj)
- @property
- def childs(self):
- ret = []
- if int(self.handle):
- ret += get_cls(self.ifindex, parent=self.handle)
- if self.root:
- ret += get_class(self.ifindex, parent=TC_H_ROOT)
- ret += get_class(self.ifindex, parent=self.handle)
- return ret
- # def add(self, socket, flags=None):
- # if not flags:
- # flags = netlink.NLM_F_CREATE
- #
- # ret = capi.rtnl_link_add(socket._sock, self._link, flags)
- # if ret < 0:
- # raise netlink.KernelError(ret)
- #
- # def change(self, socket, flags=0):
- # """Commit changes made to the link object"""
- # if not self._orig:
- # raise NetlinkError('Original link not available')
- # ret = capi.rtnl_link_change(socket._sock, self._orig, self._link, flags)
- # if ret < 0:
- # raise netlink.KernelError(ret)
- #
- # def delete(self, socket):
- # """Attempt to delete this link in the kernel"""
- # ret = capi.rtnl_link_delete(socket._sock, self._link)
- # if ret < 0:
- # raise netlink.KernelError(ret)
- def format(self, details=False, stats=False, nodev=False,
- noparent=False, indent=''):
- """Return qdisc as formatted text"""
- fmt = util.MyFormatter(self, indent)
- buf = fmt.format(self.brief('qdisc', nodev, noparent))
- if details:
- buf += fmt.nl('\t' + self.details())
- if stats:
- buf += self.stats(fmt)
- # if stats:
- # l = [['Packets', RX_PACKETS, TX_PACKETS],
- # ['Bytes', RX_BYTES, TX_BYTES],
- # ['Errors', RX_ERRORS, TX_ERRORS],
- # ['Dropped', RX_DROPPED, TX_DROPPED],
- # ['Compressed', RX_COMPRESSED, TX_COMPRESSED],
- # ['FIFO Errors', RX_FIFO_ERR, TX_FIFO_ERR],
- # ['Length Errors', RX_LEN_ERR, None],
- # ['Over Errors', RX_OVER_ERR, None],
- # ['CRC Errors', RX_CRC_ERR, None],
- # ['Frame Errors', RX_FRAME_ERR, None],
- # ['Missed Errors', RX_MISSED_ERR, None],
- # ['Abort Errors', None, TX_ABORT_ERR],
- # ['Carrier Errors', None, TX_CARRIER_ERR],
- # ['Heartbeat Errors', None, TX_HBEAT_ERR],
- # ['Window Errors', None, TX_WIN_ERR],
- # ['Collisions', None, COLLISIONS],
- # ['Multicast', None, MULTICAST],
- # ['', None, None],
- # ['Ipv6:', None, None],
- # ['Packets', IP6_INPKTS, IP6_OUTPKTS],
- # ['Bytes', IP6_INOCTETS, IP6_OUTOCTETS],
- # ['Discards', IP6_INDISCARDS, IP6_OUTDISCARDS],
- # ['Multicast Packets', IP6_INMCASTPKTS, IP6_OUTMCASTPKTS],
- # ['Multicast Bytes', IP6_INMCASTOCTETS, IP6_OUTMCASTOCTETS],
- # ['Broadcast Packets', IP6_INBCASTPKTS, IP6_OUTBCASTPKTS],
- # ['Broadcast Bytes', IP6_INBCASTOCTETS, IP6_OUTBCASTOCTETS],
- # ['Delivers', IP6_INDELIVERS, None],
- # ['Forwarded', None, IP6_OUTFORWDATAGRAMS],
- # ['No Routes', IP6_INNOROUTES, IP6_OUTNOROUTES],
- # ['Header Errors', IP6_INHDRERRORS, None],
- # ['Too Big Errors', IP6_INTOOBIGERRORS, None],
- # ['Address Errors', IP6_INADDRERRORS, None],
- # ['Unknown Protocol', IP6_INUNKNOWNPROTOS, None],
- # ['Truncated Packets', IP6_INTRUNCATEDPKTS, None],
- # ['Reasm Timeouts', IP6_REASMTIMEOUT, None],
- # ['Reasm Requests', IP6_REASMREQDS, None],
- # ['Reasm Failures', IP6_REASMFAILS, None],
- # ['Reasm OK', IP6_REASMOKS, None],
- # ['Frag Created', None, IP6_FRAGCREATES],
- # ['Frag Failures', None, IP6_FRAGFAILS],
- # ['Frag OK', None, IP6_FRAGOKS],
- # ['', None, None],
- # ['ICMPv6:', None, None],
- # ['Messages', ICMP6_INMSGS, ICMP6_OUTMSGS],
- # ['Errors', ICMP6_INERRORS, ICMP6_OUTERRORS]]
- #
- # buf += '\n\t%s%s%s%s\n' % (33 * ' ', util.title('RX'),
- # 15 * ' ', util.title('TX'))
- #
- # for row in l:
- # row[0] = util.kw(row[0])
- # row[1] = self.get_stat(row[1]) if row[1] else ''
- # row[2] = self.get_stat(row[2]) if row[2] else ''
- # buf += '\t{0:27} {1:>16} {2:>16}\n'.format(*row)
- return buf
- class TcClassCache(netlink.Cache):
- """Cache of traffic classes"""
- def __init__(self, ifindex, cache=None):
- if not cache:
- cache = self._alloc_cache_name('route/class')
- self._protocol = netlink.NETLINK_ROUTE
- self._nl_cache = cache
- self._set_arg1(ifindex)
- @staticmethod
- def _new_object(obj):
- return TcClass(obj)
- def _new_cache(self, cache):
- return TcClassCache(self.arg1, cache=cache)
- class TcClass(Tc):
- """Traffic Class"""
- def __init__(self, obj=None):
- netlink.Object.__init__(self, 'route/class', 'class', obj)
- self._module_path = 'netlink.route.qdisc.'
- self._rtnl_class = self._obj2type(self._nl_object)
- self._rtnl_tc = capi.obj2tc(self._nl_object)
- if self.kind:
- self._tc_module_lookup()
- @classmethod
- def from_capi(cls, obj):
- return cls(capi.class2obj(obj))
- @staticmethod
- def _obj2type(obj):
- return capi.obj2class(obj)
- @staticmethod
- def _new_instance(obj):
- if not obj:
- raise ValueError()
- return TcClass(obj)
- @property
- def childs(self):
- ret = []
- # classes can have classifiers, child classes and leaf
- # qdiscs
- ret += get_cls(self.ifindex, parent=self.handle)
- ret += get_class(self.ifindex, parent=self.handle)
- ret += get_qdisc(self.ifindex, parent=self.handle)
- return ret
- def format(self, details=False, _stats=False, nodev=False,
- noparent=False, indent=''):
- """Return class as formatted text"""
- fmt = util.MyFormatter(self, indent)
- buf = fmt.format(self.brief('class', nodev, noparent))
- if details:
- buf += fmt.nl('\t' + self.details())
- return buf
- class ClassifierCache(netlink.Cache):
- """Cache of traffic classifiers objects"""
- def __init__(self, ifindex, parent, cache=None):
- if not cache:
- cache = self._alloc_cache_name('route/cls')
- self._protocol = netlink.NETLINK_ROUTE
- self._nl_cache = cache
- self._set_arg1(ifindex)
- self._set_arg2(int(parent))
- @staticmethod
- def _new_object(obj):
- return Classifier(obj)
- def _new_cache(self, cache):
- return ClassifierCache(self.arg1, self.arg2, cache=cache)
- class Classifier(Tc):
- """Classifier"""
- def __init__(self, obj=None):
- netlink.Object.__init__(self, 'route/cls', 'cls', obj)
- self._module_path = 'netlink.route.cls.'
- self._rtnl_cls = self._obj2type(self._nl_object)
- self._rtnl_tc = capi.obj2tc(self._nl_object)
- @classmethod
- def from_capi(cls, obj):
- return cls(capi.cls2obj(obj))
- @staticmethod
- def _obj2type(obj):
- return capi.obj2cls(obj)
- @staticmethod
- def _new_instance(obj):
- if not obj:
- raise ValueError()
- return Classifier(obj)
- @property
- def priority(self):
- return capi.rtnl_cls_get_prio(self._rtnl_cls)
- @priority.setter
- def priority(self, value):
- capi.rtnl_cls_set_prio(self._rtnl_cls, int(value))
- @property
- def protocol(self):
- return capi.rtnl_cls_get_protocol(self._rtnl_cls)
- @protocol.setter
- def protocol(self, value):
- capi.rtnl_cls_set_protocol(self._rtnl_cls, int(value))
- @property
- def childs(self):
- return []
- def format(self, details=False, _stats=False, nodev=False,
- noparent=False, indent=''):
- """Return class as formatted text"""
- fmt = util.MyFormatter(self, indent)
- buf = fmt.format(self.brief('classifier', nodev, noparent))
- buf += fmt.format(' {t|priority} {t|protocol}')
- if details:
- buf += fmt.nl('\t' + self.details())
- return buf
- _qdisc_cache = QdiscCache()
- def get_qdisc(ifindex, handle=None, parent=None):
- l = []
- _qdisc_cache.refill()
- for qdisc in _qdisc_cache:
- if qdisc.ifindex != ifindex:
- continue
- if (handle is not None) and (qdisc.handle != handle):
- continue
- if (parent is not None) and (qdisc.parent != parent):
- continue
- l.append(qdisc)
- return l
- _class_cache = {}
- def get_class(ifindex, parent, handle=None):
- l = []
- try:
- cache = _class_cache[ifindex]
- except KeyError:
- cache = TcClassCache(ifindex)
- _class_cache[ifindex] = cache
- cache.refill()
- for cl in cache:
- if (parent is not None) and (cl.parent != parent):
- continue
- if (handle is not None) and (cl.handle != handle):
- continue
- l.append(cl)
- return l
- _cls_cache = {}
- def get_cls(ifindex, parent, handle=None):
- chain = _cls_cache.get(ifindex, dict())
- try:
- cache = chain[parent]
- except KeyError:
- cache = ClassifierCache(ifindex, parent)
- chain[parent] = cache
- cache.refill()
- if handle is None:
- return [ cls for cls in cache ]
- return [ cls for cls in cache if cls.handle == handle ]
|