xref: /aosp_15_r20/external/libnl/python/netlink/route/tc.py (revision 4dc78e53d49367fa8e61b07018507c90983a077d)
1#
2# Copyright (c) 2011 Thomas Graf <[email protected]>
3#
4from __future__ import absolute_import
5
6__all__ = [
7    "TcCache",
8    "Tc",
9    "QdiscCache",
10    "Qdisc",
11    "TcClassCache",
12    "TcClass",
13]
14
15from .. import core as netlink
16from . import capi as capi
17from .. import util as util
18from . import link as Link
19
20TC_PACKETS = 0
21TC_BYTES = 1
22TC_RATE_BPS = 2
23TC_RATE_PPS = 3
24TC_QLEN = 4
25TC_BACKLOG = 5
26TC_DROPS = 6
27TC_REQUEUES = 7
28TC_OVERLIMITS = 9
29
30TC_H_ROOT = 0xFFFFFFFF
31TC_H_INGRESS = 0xFFFFFFF1
32
33STAT_PACKETS = 0
34STAT_BYTES = 1
35STAT_RATE_BPS = 2
36STAT_RATE_PPS = 3
37STAT_QLEN = 4
38STAT_BACKLOG = 5
39STAT_DROPS = 6
40STAT_REQUEUES = 7
41STAT_OVERLIMITS = 8
42STAT_MAX = STAT_OVERLIMITS
43
44
45class Handle(object):
46    """Traffic control handle
47
48    Representation of a traffic control handle which uniquely identifies
49    each traffic control object in its link namespace.
50
51    handle = tc.Handle('10:20')
52    handle = tc.handle('root')
53    print int(handle)
54    print str(handle)
55    """
56
57    def __init__(self, val=None):
58        if type(val) is str:
59            val = capi.tc_str2handle(val)
60        elif not val:
61            val = 0
62
63        self._val = int(val)
64
65    def __cmp__(self, other):
66        if other is None:
67            other = 0
68
69        if isinstance(other, Handle):
70            return int(self) - int(other)
71        elif isinstance(other, int):
72            return int(self) - other
73        else:
74            raise TypeError()
75
76    def __int__(self):
77        return self._val
78
79    def __str__(self):
80        return capi.rtnl_tc_handle2str(self._val, 64)[0]
81
82    def isroot(self):
83        return self._val == TC_H_ROOT or self._val == TC_H_INGRESS
84
85
86class TcCache(netlink.Cache):
87    """Cache of traffic control object"""
88
89    def __getitem__(self, key):
90        raise NotImplementedError()
91
92
93class Tc(netlink.Object):
94    def __cmp__(self, other):
95        diff = self.ifindex - other.ifindex
96        if diff == 0:
97            diff = int(self.handle) - int(other.handle)
98        return diff
99
100    def _tc_module_lookup(self):
101        self._module_lookup(self._module_path + self.kind, "init_" + self._name)
102
103    @property
104    def root(self):
105        """True if tc object is a root object"""
106        return self.parent.isroot()
107
108    @property
109    def ifindex(self):
110        """interface index"""
111        return capi.rtnl_tc_get_ifindex(self._rtnl_tc)
112
113    @ifindex.setter
114    def ifindex(self, value):
115        capi.rtnl_tc_set_ifindex(self._rtnl_tc, int(value))
116
117    @property
118    def link(self):
119        link = capi.rtnl_tc_get_link(self._rtnl_tc)
120        if not link:
121            return None
122
123        return Link.Link.from_capi(link)
124
125    @link.setter
126    def link(self, value):
127        capi.rtnl_tc_set_link(self._rtnl_tc, value._link)
128
129    @property
130    def mtu(self):
131        return capi.rtnl_tc_get_mtu(self._rtnl_tc)
132
133    @mtu.setter
134    def mtu(self, value):
135        capi.rtnl_tc_set_mtu(self._rtnl_tc, int(value))
136
137    @property
138    def mpu(self):
139        return capi.rtnl_tc_get_mpu(self._rtnl_tc)
140
141    @mpu.setter
142    def mpu(self, value):
143        capi.rtnl_tc_set_mpu(self._rtnl_tc, int(value))
144
145    @property
146    def overhead(self):
147        return capi.rtnl_tc_get_overhead(self._rtnl_tc)
148
149    @overhead.setter
150    def overhead(self, value):
151        capi.rtnl_tc_set_overhead(self._rtnl_tc, int(value))
152
153    @property
154    def linktype(self):
155        return capi.rtnl_tc_get_linktype(self._rtnl_tc)
156
157    @linktype.setter
158    def linktype(self, value):
159        capi.rtnl_tc_set_linktype(self._rtnl_tc, int(value))
160
161    @property
162    @netlink.nlattr(fmt=util.handle)
163    def handle(self):
164        return Handle(capi.rtnl_tc_get_handle(self._rtnl_tc))
165
166    @handle.setter
167    def handle(self, value):
168        capi.rtnl_tc_set_handle(self._rtnl_tc, int(value))
169
170    @property
171    @netlink.nlattr(fmt=util.handle)
172    def parent(self):
173        return Handle(capi.rtnl_tc_get_parent(self._rtnl_tc))
174
175    @parent.setter
176    def parent(self, value):
177        capi.rtnl_tc_set_parent(self._rtnl_tc, int(value))
178
179    @property
180    @netlink.nlattr(fmt=util.bold)
181    def kind(self):
182        return capi.rtnl_tc_get_kind(self._rtnl_tc)
183
184    @kind.setter
185    def kind(self, value):
186        capi.rtnl_tc_set_kind(self._rtnl_tc, value)
187        self._tc_module_lookup()
188
189    def get_stat(self, id):
190        return capi.rtnl_tc_get_stat(self._rtnl_tc, id)
191
192    @property
193    def _dev(self):
194        buf = util.kw("dev") + " "
195
196        if self.link:
197            return buf + util.string(self.link.name)
198        else:
199            return buf + util.num(self.ifindex)
200
201    def brief(self, title, nodev=False, noparent=False):
202        ret = title + " {a|kind} {a|handle}"
203
204        if not nodev:
205            ret += " {a|_dev}"
206
207        if not noparent:
208            ret += " {t|parent}"
209
210        return ret + self._module_brief()
211
212    @staticmethod
213    def details():
214        return "{t|mtu} {t|mpu} {t|overhead} {t|linktype}"
215
216    @property
217    def packets(self):
218        return self.get_stat(STAT_PACKETS)
219
220    @property
221    def bytes(self):
222        return self.get_stat(STAT_BYTES)
223
224    @property
225    def qlen(self):
226        return self.get_stat(STAT_QLEN)
227
228    @staticmethod
229    def stats(fmt):
230        return fmt.nl("{t|packets} {t|bytes} {t|qlen}")
231
232
233class QdiscCache(netlink.Cache):
234    """Cache of qdiscs"""
235
236    def __init__(self, cache=None):
237        if not cache:
238            cache = self._alloc_cache_name("route/qdisc")
239
240        self._protocol = netlink.NETLINK_ROUTE
241        self._nl_cache = cache
242
243    # 	def __getitem__(self, key):
244    #        	if type(key) is int:
245    #                        link = capi.rtnl_link_get(self._this, key)
246    #                elif type(key) is str:
247    #                        link = capi.rtnl_link_get_by_name(self._this, key)
248    #
249    # 		if qdisc is None:
250    #                        raise KeyError()
251    # 		else:
252    #                        return Qdisc._from_capi(capi.qdisc2obj(qdisc))
253
254    @staticmethod
255    def _new_object(obj):
256        return Qdisc(obj)
257
258    @staticmethod
259    def _new_cache(cache):
260        return QdiscCache(cache=cache)
261
262
263class Qdisc(Tc):
264    """Queueing discipline"""
265
266    def __init__(self, obj=None):
267        netlink.Object.__init__(self, "route/qdisc", "qdisc", obj)
268        self._module_path = "netlink.route.qdisc."
269        self._rtnl_qdisc = self._obj2type(self._nl_object)
270        self._rtnl_tc = capi.obj2tc(self._nl_object)
271
272        if self.kind:
273            self._tc_module_lookup()
274
275    @classmethod
276    def from_capi(cls, obj):
277        return cls(capi.qdisc2obj(obj))
278
279    @staticmethod
280    def _obj2type(obj):
281        return capi.obj2qdisc(obj)
282
283    @staticmethod
284    def _new_instance(obj):
285        if not obj:
286            raise ValueError()
287
288        return Qdisc(obj)
289
290    @property
291    def childs(self):
292        ret = []
293
294        if int(self.handle):
295            ret += get_cls(self.ifindex, parent=self.handle)
296
297            if self.root:
298                ret += get_class(self.ifindex, parent=TC_H_ROOT)
299
300            ret += get_class(self.ifindex, parent=self.handle)
301
302        return ret
303
304    # 	def add(self, socket, flags=None):
305    #        	if not flags:
306    #                        flags = netlink.NLM_F_CREATE
307    #
308    # 		ret = capi.rtnl_link_add(socket._sock, self._link, flags)
309    # 		if ret < 0:
310    # 			raise netlink.KernelError(ret)
311    #
312    # 	def change(self, socket, flags=0):
313    # 		"""Commit changes made to the link object"""
314    # 		if not self._orig:
315    # 			raise NetlinkError('Original link not available')
316    #        	ret = capi.rtnl_link_change(socket._sock, self._orig, self._link, flags)
317    #                if ret < 0:
318    #                        raise netlink.KernelError(ret)
319    #
320    # 	def delete(self, socket):
321    # 		"""Attempt to delete this link in the kernel"""
322    #        	ret = capi.rtnl_link_delete(socket._sock, self._link)
323    #                if ret < 0:
324    #                        raise netlink.KernelError(ret)
325
326    def format(
327        self, details=False, stats=False, nodev=False, noparent=False, indent=""
328    ):
329        """Return qdisc as formatted text"""
330        fmt = util.MyFormatter(self, indent)
331
332        buf = fmt.format(self.brief("qdisc", nodev, noparent))
333
334        if details:
335            buf += fmt.nl("\t" + self.details())
336
337        if stats:
338            buf += self.stats(fmt)
339
340        # 		if stats:
341        # 			l = [['Packets', RX_PACKETS, TX_PACKETS],
342        # 			     ['Bytes', RX_BYTES, TX_BYTES],
343        # 			     ['Errors', RX_ERRORS, TX_ERRORS],
344        # 			     ['Dropped', RX_DROPPED, TX_DROPPED],
345        # 			     ['Compressed', RX_COMPRESSED, TX_COMPRESSED],
346        # 			     ['FIFO Errors', RX_FIFO_ERR, TX_FIFO_ERR],
347        # 			     ['Length Errors', RX_LEN_ERR, None],
348        # 			     ['Over Errors', RX_OVER_ERR, None],
349        # 			     ['CRC Errors', RX_CRC_ERR, None],
350        # 			     ['Frame Errors', RX_FRAME_ERR, None],
351        # 			     ['Missed Errors', RX_MISSED_ERR, None],
352        # 			     ['Abort Errors', None, TX_ABORT_ERR],
353        # 			     ['Carrier Errors', None, TX_CARRIER_ERR],
354        # 			     ['Heartbeat Errors', None, TX_HBEAT_ERR],
355        # 			     ['Window Errors', None, TX_WIN_ERR],
356        # 			     ['Collisions', None, COLLISIONS],
357        # 			     ['Multicast', None, MULTICAST],
358        # 			     ['', None, None],
359        # 			     ['Ipv6:', None, None],
360        # 			     ['Packets', IP6_INPKTS, IP6_OUTPKTS],
361        # 			     ['Bytes', IP6_INOCTETS, IP6_OUTOCTETS],
362        # 			     ['Discards', IP6_INDISCARDS, IP6_OUTDISCARDS],
363        # 			     ['Multicast Packets', IP6_INMCASTPKTS, IP6_OUTMCASTPKTS],
364        # 			     ['Multicast Bytes', IP6_INMCASTOCTETS, IP6_OUTMCASTOCTETS],
365        # 			     ['Broadcast Packets', IP6_INBCASTPKTS, IP6_OUTBCASTPKTS],
366        # 			     ['Broadcast Bytes', IP6_INBCASTOCTETS, IP6_OUTBCASTOCTETS],
367        # 			     ['Delivers', IP6_INDELIVERS, None],
368        # 			     ['Forwarded', None, IP6_OUTFORWDATAGRAMS],
369        # 			     ['No Routes', IP6_INNOROUTES, IP6_OUTNOROUTES],
370        # 			     ['Header Errors', IP6_INHDRERRORS, None],
371        # 			     ['Too Big Errors', IP6_INTOOBIGERRORS, None],
372        # 			     ['Address Errors', IP6_INADDRERRORS, None],
373        # 			     ['Unknown Protocol', IP6_INUNKNOWNPROTOS, None],
374        # 			     ['Truncated Packets', IP6_INTRUNCATEDPKTS, None],
375        # 			     ['Reasm Timeouts', IP6_REASMTIMEOUT, None],
376        # 			     ['Reasm Requests', IP6_REASMREQDS, None],
377        # 			     ['Reasm Failures', IP6_REASMFAILS, None],
378        # 			     ['Reasm OK', IP6_REASMOKS, None],
379        # 			     ['Frag Created', None, IP6_FRAGCREATES],
380        # 			     ['Frag Failures', None, IP6_FRAGFAILS],
381        # 			     ['Frag OK', None, IP6_FRAGOKS],
382        # 			     ['', None, None],
383        # 			     ['ICMPv6:', None, None],
384        # 			     ['Messages', ICMP6_INMSGS, ICMP6_OUTMSGS],
385        # 			     ['Errors', ICMP6_INERRORS, ICMP6_OUTERRORS]]
386        #
387        # 			buf += '\n\t%s%s%s%s\n' % (33 * ' ', util.title('RX'),
388        #                        			   15 * ' ', util.title('TX'))
389        #
390        # 			for row in l:
391        # 				row[0] = util.kw(row[0])
392        #                                row[1] = self.get_stat(row[1]) if row[1] else ''
393        #                                row[2] = self.get_stat(row[2]) if row[2] else ''
394        # 				buf += '\t{0:27} {1:>16} {2:>16}\n'.format(*row)
395
396        return buf
397
398
399class TcClassCache(netlink.Cache):
400    """Cache of traffic classes"""
401
402    def __init__(self, ifindex, cache=None):
403        if not cache:
404            cache = self._alloc_cache_name("route/class")
405
406        self._protocol = netlink.NETLINK_ROUTE
407        self._nl_cache = cache
408        self._set_arg1(ifindex)
409
410    @staticmethod
411    def _new_object(obj):
412        return TcClass(obj)
413
414    def _new_cache(self, cache):
415        return TcClassCache(self.arg1, cache=cache)
416
417
418class TcClass(Tc):
419    """Traffic Class"""
420
421    def __init__(self, obj=None):
422        netlink.Object.__init__(self, "route/class", "class", obj)
423        self._module_path = "netlink.route.qdisc."
424        self._rtnl_class = self._obj2type(self._nl_object)
425        self._rtnl_tc = capi.obj2tc(self._nl_object)
426
427        if self.kind:
428            self._tc_module_lookup()
429
430    @classmethod
431    def from_capi(cls, obj):
432        return cls(capi.class2obj(obj))
433
434    @staticmethod
435    def _obj2type(obj):
436        return capi.obj2class(obj)
437
438    @staticmethod
439    def _new_instance(obj):
440        if not obj:
441            raise ValueError()
442
443        return TcClass(obj)
444
445    @property
446    def childs(self):
447        ret = []
448
449        # classes can have classifiers, child classes and leaf
450        # qdiscs
451        ret += get_cls(self.ifindex, parent=self.handle)
452        ret += get_class(self.ifindex, parent=self.handle)
453        ret += get_qdisc(self.ifindex, parent=self.handle)
454
455        return ret
456
457    def format(
458        self, details=False, _stats=False, nodev=False, noparent=False, indent=""
459    ):
460        """Return class as formatted text"""
461        fmt = util.MyFormatter(self, indent)
462
463        buf = fmt.format(self.brief("class", nodev, noparent))
464
465        if details:
466            buf += fmt.nl("\t" + self.details())
467
468        return buf
469
470
471class ClassifierCache(netlink.Cache):
472    """Cache of traffic classifiers objects"""
473
474    def __init__(self, ifindex, parent, cache=None):
475        if not cache:
476            cache = self._alloc_cache_name("route/cls")
477
478        self._protocol = netlink.NETLINK_ROUTE
479        self._nl_cache = cache
480        self._set_arg1(ifindex)
481        self._set_arg2(int(parent))
482
483    @staticmethod
484    def _new_object(obj):
485        return Classifier(obj)
486
487    def _new_cache(self, cache):
488        return ClassifierCache(self.arg1, self.arg2, cache=cache)
489
490
491class Classifier(Tc):
492    """Classifier"""
493
494    def __init__(self, obj=None):
495        netlink.Object.__init__(self, "route/cls", "cls", obj)
496        self._module_path = "netlink.route.cls."
497        self._rtnl_cls = self._obj2type(self._nl_object)
498        self._rtnl_tc = capi.obj2tc(self._nl_object)
499
500    @classmethod
501    def from_capi(cls, obj):
502        return cls(capi.cls2obj(obj))
503
504    @staticmethod
505    def _obj2type(obj):
506        return capi.obj2cls(obj)
507
508    @staticmethod
509    def _new_instance(obj):
510        if not obj:
511            raise ValueError()
512
513        return Classifier(obj)
514
515    @property
516    def priority(self):
517        return capi.rtnl_cls_get_prio(self._rtnl_cls)
518
519    @priority.setter
520    def priority(self, value):
521        capi.rtnl_cls_set_prio(self._rtnl_cls, int(value))
522
523    @property
524    def protocol(self):
525        return capi.rtnl_cls_get_protocol(self._rtnl_cls)
526
527    @protocol.setter
528    def protocol(self, value):
529        capi.rtnl_cls_set_protocol(self._rtnl_cls, int(value))
530
531    @property
532    def childs(self):
533        return []
534
535    def format(
536        self, details=False, _stats=False, nodev=False, noparent=False, indent=""
537    ):
538        """Return class as formatted text"""
539        fmt = util.MyFormatter(self, indent)
540
541        buf = fmt.format(self.brief("classifier", nodev, noparent))
542        buf += fmt.format(" {t|priority} {t|protocol}")
543
544        if details:
545            buf += fmt.nl("\t" + self.details())
546
547        return buf
548
549
550_qdisc_cache = QdiscCache()
551
552
553def get_qdisc(ifindex, handle=None, parent=None):
554    lst = []
555
556    _qdisc_cache.refill()
557
558    for qdisc in _qdisc_cache:
559        if qdisc.ifindex != ifindex:
560            continue
561        if (handle is not None) and (qdisc.handle != handle):
562            continue
563        if (parent is not None) and (qdisc.parent != parent):
564            continue
565        lst.append(qdisc)
566
567    return lst
568
569
570_class_cache = {}
571
572
573def get_class(ifindex, parent, handle=None):
574    lst = []
575
576    try:
577        cache = _class_cache[ifindex]
578    except KeyError:
579        cache = TcClassCache(ifindex)
580        _class_cache[ifindex] = cache
581
582    cache.refill()
583
584    for cl in cache:
585        if (parent is not None) and (cl.parent != parent):
586            continue
587        if (handle is not None) and (cl.handle != handle):
588            continue
589        lst.append(cl)
590
591    return lst
592
593
594_cls_cache = {}
595
596
597def get_cls(ifindex, parent, handle=None):
598
599    chain = _cls_cache.get(ifindex, {})
600
601    try:
602        cache = chain[parent]
603    except KeyError:
604        cache = ClassifierCache(ifindex, parent)
605        chain[parent] = cache
606
607    cache.refill()
608
609    if handle is None:
610        return list(cache)
611
612    return [cls for cls in cache if cls.handle == handle]
613