tc.py 16 KB

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