qdisc.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. /*
  2. * lib/route/qdisc.c Queueing Disciplines
  3. *
  4. * This library is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU Lesser General Public
  6. * License as published by the Free Software Foundation version 2.1
  7. * of the License.
  8. *
  9. * Copyright (c) 2003-2011 Thomas Graf <tgraf@suug.ch>
  10. */
  11. /**
  12. * @ingroup tc
  13. * @defgroup qdisc Queueing Disciplines
  14. * @{
  15. */
  16. #include <netlink-private/netlink.h>
  17. #include <netlink-private/tc.h>
  18. #include <netlink/netlink.h>
  19. #include <netlink/utils.h>
  20. #include <netlink/route/link.h>
  21. #include <netlink-private/route/tc-api.h>
  22. #include <netlink/route/qdisc.h>
  23. #include <netlink/route/class.h>
  24. #include <netlink/route/classifier.h>
  25. static struct nl_cache_ops rtnl_qdisc_ops;
  26. static struct nl_object_ops qdisc_obj_ops;
  27. static int qdisc_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
  28. struct nlmsghdr *n, struct nl_parser_param *pp)
  29. {
  30. struct rtnl_qdisc *qdisc;
  31. int err;
  32. if (!(qdisc = rtnl_qdisc_alloc()))
  33. return -NLE_NOMEM;
  34. if ((err = rtnl_tc_msg_parse(n, TC_CAST(qdisc))) < 0)
  35. goto errout;
  36. err = pp->pp_cb(OBJ_CAST(qdisc), pp);
  37. errout:
  38. rtnl_qdisc_put(qdisc);
  39. return err;
  40. }
  41. static int qdisc_request_update(struct nl_cache *c, struct nl_sock *sk)
  42. {
  43. struct tcmsg tchdr = {
  44. .tcm_family = AF_UNSPEC,
  45. .tcm_ifindex = c->c_iarg1,
  46. };
  47. return nl_send_simple(sk, RTM_GETQDISC, NLM_F_DUMP, &tchdr,
  48. sizeof(tchdr));
  49. }
  50. /**
  51. * @name Allocation/Freeing
  52. * @{
  53. */
  54. struct rtnl_qdisc *rtnl_qdisc_alloc(void)
  55. {
  56. struct rtnl_tc *tc;
  57. tc = TC_CAST(nl_object_alloc(&qdisc_obj_ops));
  58. if (tc)
  59. tc->tc_type = RTNL_TC_TYPE_QDISC;
  60. return (struct rtnl_qdisc *) tc;
  61. }
  62. void rtnl_qdisc_put(struct rtnl_qdisc *qdisc)
  63. {
  64. nl_object_put((struct nl_object *) qdisc);
  65. }
  66. /** @} */
  67. /**
  68. * @name Addition / Modification / Deletion
  69. * @{
  70. */
  71. static int build_qdisc_msg(struct rtnl_qdisc *qdisc, int type, int flags,
  72. struct nl_msg **result)
  73. {
  74. if (!(qdisc->ce_mask & TCA_ATTR_IFINDEX)) {
  75. APPBUG("ifindex must be specified");
  76. return -NLE_MISSING_ATTR;
  77. }
  78. return rtnl_tc_msg_build(TC_CAST(qdisc), type, flags, result);
  79. }
  80. /**
  81. * Build a netlink message requesting the addition of a qdisc
  82. * @arg qdisc Qdisc to add
  83. * @arg flags Additional netlink message flags
  84. * @arg result Pointer to store resulting netlink message
  85. *
  86. * The behaviour of this function is identical to rtnl_qdisc_add() with
  87. * the exception that it will not send the message but return it int the
  88. * provided return pointer instead.
  89. *
  90. * @see rtnl_qdisc_add()
  91. *
  92. * @return 0 on success or a negative error code.
  93. */
  94. int rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc, int flags,
  95. struct nl_msg **result)
  96. {
  97. if (!(qdisc->ce_mask & (TCA_ATTR_HANDLE | TCA_ATTR_PARENT))) {
  98. APPBUG("handle or parent must be specified");
  99. return -NLE_MISSING_ATTR;
  100. }
  101. return build_qdisc_msg(qdisc, RTM_NEWQDISC, flags, result);
  102. }
  103. /**
  104. * Add qdisc
  105. * @arg sk Netlink socket
  106. * @arg qdisc Qdisc to add
  107. * @arg flags Additional netlink message flags
  108. *
  109. * Builds a \c RTM_NEWQDISC netlink message requesting the addition
  110. * of a new qdisc and sends the message to the kernel. The configuration
  111. * of the qdisc is derived from the attributes of the specified qdisc.
  112. *
  113. * The following flags may be specified:
  114. * - \c NLM_F_CREATE: Create qdisc if it does not exist, otherwise
  115. * -NLE_OBJ_NOTFOUND is returned.
  116. * - \c NLM_F_REPLACE: If another qdisc is already attached to the
  117. * parent, replace it even if the handles mismatch.
  118. * - \c NLM_F_EXCL: Return -NLE_EXISTS if a qdisc with matching
  119. * handle exists already.
  120. *
  121. * Existing qdiscs with matching handles will be updated, unless the
  122. * flag \c NLM_F_EXCL is specified. If their handles do not match, the
  123. * error -NLE_EXISTS is returned unless the flag \c NLM_F_REPLACE is
  124. * specified in which case the existing qdisc is replaced with the new
  125. * one. If no matching qdisc exists, it will be created if the flag
  126. * \c NLM_F_CREATE is set, otherwise the error -NLE_OBJ_NOTFOUND is
  127. * returned.
  128. *
  129. * After sending, the function will wait for the ACK or an eventual
  130. * error message to be received and will therefore block until the
  131. * operation has been completed.
  132. *
  133. * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause
  134. * this function to return immediately after sending. In this case,
  135. * it is the responsibility of the caller to handle any error
  136. * messages returned.
  137. *
  138. * @return 0 on success or a negative error code.
  139. */
  140. int rtnl_qdisc_add(struct nl_sock *sk, struct rtnl_qdisc *qdisc, int flags)
  141. {
  142. struct nl_msg *msg;
  143. int err;
  144. if ((err = rtnl_qdisc_build_add_request(qdisc, flags, &msg)) < 0)
  145. return err;
  146. return nl_send_sync(sk, msg);
  147. }
  148. /**
  149. * Build netlink message requesting the update of a qdisc
  150. * @arg qdisc Qdisc to update
  151. * @arg new Qdisc with updated attributes
  152. * @arg flags Additional netlink message flags
  153. * @arg result Pointer to store resulting netlink message
  154. *
  155. * The behaviour of this function is identical to rtnl_qdisc_update() with
  156. * the exception that it will not send the message but return it in the
  157. * provided return pointer instead.
  158. *
  159. * @see rtnl_qdisc_update()
  160. *
  161. * @return 0 on success or a negative error code.
  162. */
  163. int rtnl_qdisc_build_update_request(struct rtnl_qdisc *qdisc,
  164. struct rtnl_qdisc *new, int flags,
  165. struct nl_msg **result)
  166. {
  167. if (flags & (NLM_F_CREATE | NLM_F_EXCL)) {
  168. APPBUG("NLM_F_CREATE and NLM_F_EXCL may not be used here, "
  169. "use rtnl_qdisc_add()");
  170. return -NLE_INVAL;
  171. }
  172. if (!(qdisc->ce_mask & TCA_ATTR_IFINDEX)) {
  173. APPBUG("ifindex must be specified");
  174. return -NLE_MISSING_ATTR;
  175. }
  176. if (!(qdisc->ce_mask & (TCA_ATTR_HANDLE | TCA_ATTR_PARENT))) {
  177. APPBUG("handle or parent must be specified");
  178. return -NLE_MISSING_ATTR;
  179. }
  180. rtnl_tc_set_ifindex(TC_CAST(new), qdisc->q_ifindex);
  181. if (qdisc->ce_mask & TCA_ATTR_HANDLE)
  182. rtnl_tc_set_handle(TC_CAST(new), qdisc->q_handle);
  183. if (qdisc->ce_mask & TCA_ATTR_PARENT)
  184. rtnl_tc_set_parent(TC_CAST(new), qdisc->q_parent);
  185. return build_qdisc_msg(new, RTM_NEWQDISC, flags, result);
  186. }
  187. /**
  188. * Update qdisc
  189. * @arg sk Netlink socket
  190. * @arg qdisc Qdisc to update
  191. * @arg new Qdisc with updated attributes
  192. * @arg flags Additional netlink message flags
  193. *
  194. * Builds a \c RTM_NEWQDISC netlink message requesting the update
  195. * of an existing qdisc and sends the message to the kernel.
  196. *
  197. * This function is a varation of rtnl_qdisc_add() to update qdiscs
  198. * if the qdisc to be updated is available as qdisc object. The
  199. * behaviour is identical to the one of rtnl_qdisc_add except that
  200. * before constructing the message, it copies the \c ifindex,
  201. * \c handle, and \c parent from the original \p qdisc to the \p new
  202. * qdisc.
  203. *
  204. * After sending, the function will wait for the ACK or an eventual
  205. * error message to be received and will therefore block until the
  206. * operation has been completed.
  207. *
  208. * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause
  209. * this function to return immediately after sending. In this case,
  210. * it is the responsibility of the caller to handle any error
  211. * messages returned.
  212. *
  213. * @return 0 on success or a negative error code.
  214. */
  215. int rtnl_qdisc_update(struct nl_sock *sk, struct rtnl_qdisc *qdisc,
  216. struct rtnl_qdisc *new, int flags)
  217. {
  218. struct nl_msg *msg;
  219. int err;
  220. err = rtnl_qdisc_build_update_request(qdisc, new, flags, &msg);
  221. if (err < 0)
  222. return err;
  223. return nl_send_sync(sk, msg);
  224. }
  225. /**
  226. * Build netlink message requesting the deletion of a qdisc
  227. * @arg qdisc Qdisc to delete
  228. * @arg result Pointer to store resulting netlink message
  229. *
  230. * The behaviour of this function is identical to rtnl_qdisc_delete() with
  231. * the exception that it will not send the message but return it in the
  232. * provided return pointer instead.
  233. *
  234. * @see rtnl_qdisc_delete()
  235. *
  236. * @return 0 on success or a negative error code.
  237. */
  238. int rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc,
  239. struct nl_msg **result)
  240. {
  241. struct nl_msg *msg;
  242. struct tcmsg tchdr;
  243. uint32_t required = TCA_ATTR_IFINDEX | TCA_ATTR_PARENT;
  244. if ((qdisc->ce_mask & required) != required) {
  245. APPBUG("ifindex and parent must be specified");
  246. return -NLE_MISSING_ATTR;
  247. }
  248. if (!(msg = nlmsg_alloc_simple(RTM_DELQDISC, 0)))
  249. return -NLE_NOMEM;
  250. memset(&tchdr, 0, sizeof(tchdr));
  251. tchdr.tcm_family = AF_UNSPEC;
  252. tchdr.tcm_ifindex = qdisc->q_ifindex;
  253. tchdr.tcm_parent = qdisc->q_parent;
  254. if (qdisc->ce_mask & TCA_ATTR_HANDLE)
  255. tchdr.tcm_handle = qdisc->q_handle;
  256. if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0)
  257. goto nla_put_failure;
  258. if (qdisc->ce_mask & TCA_ATTR_KIND)
  259. NLA_PUT_STRING(msg, TCA_KIND, qdisc->q_kind);
  260. *result = msg;
  261. return 0;
  262. nla_put_failure:
  263. nlmsg_free(msg);
  264. return -NLE_MSGSIZE;
  265. }
  266. /**
  267. * Delete qdisc
  268. * @arg sk Netlink socket
  269. * @arg qdisc Qdisc to add
  270. *
  271. * Builds a \c RTM_NEWQDISC netlink message requesting the deletion
  272. * of a qdisc and sends the message to the kernel.
  273. *
  274. * The message is constructed out of the following attributes:
  275. * - \c ifindex and \c parent
  276. * - \c handle (optional, must match if provided)
  277. * - \c kind (optional, must match if provided)
  278. *
  279. * All other qdisc attributes including all qdisc type specific
  280. * attributes are ignored.
  281. *
  282. * After sending, the function will wait for the ACK or an eventual
  283. * error message to be received and will therefore block until the
  284. * operation has been completed.
  285. *
  286. * @note It is not possible to delete default qdiscs.
  287. *
  288. * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause
  289. * this function to return immediately after sending. In this case,
  290. * it is the responsibility of the caller to handle any error
  291. * messages returned.
  292. *
  293. * @return 0 on success or a negative error code.
  294. */
  295. int rtnl_qdisc_delete(struct nl_sock *sk, struct rtnl_qdisc *qdisc)
  296. {
  297. struct nl_msg *msg;
  298. int err;
  299. if ((err = rtnl_qdisc_build_delete_request(qdisc, &msg)) < 0)
  300. return err;
  301. return nl_send_sync(sk, msg);
  302. }
  303. /** @} */
  304. /**
  305. * @name Cache Related Functions
  306. * @{
  307. */
  308. /**
  309. * Allocate a cache and fill it with all configured qdiscs
  310. * @arg sk Netlink socket
  311. * @arg result Pointer to store the created cache
  312. *
  313. * Allocates a new qdisc cache and fills it with a list of all configured
  314. * qdiscs on all network devices. Release the cache with nl_cache_free().
  315. *
  316. * @return 0 on success or a negative error code.
  317. */
  318. int rtnl_qdisc_alloc_cache(struct nl_sock *sk, struct nl_cache **result)
  319. {
  320. return nl_cache_alloc_and_fill(&rtnl_qdisc_ops, sk, result);
  321. }
  322. /**
  323. * Search qdisc by interface index and parent
  324. * @arg cache Qdisc cache
  325. * @arg ifindex Interface index
  326. * @arg parent Handle of parent qdisc
  327. *
  328. * Searches a qdisc cache previously allocated with rtnl_qdisc_alloc_cache()
  329. * and searches for a qdisc matching the interface index and parent qdisc.
  330. *
  331. * The reference counter is incremented before returning the qdisc, therefore
  332. * the reference must be given back with rtnl_qdisc_put() after usage.
  333. *
  334. * @return pointer to qdisc inside the cache or NULL if no match was found.
  335. */
  336. struct rtnl_qdisc *rtnl_qdisc_get_by_parent(struct nl_cache *cache,
  337. int ifindex, uint32_t parent)
  338. {
  339. struct rtnl_qdisc *q;
  340. if (cache->c_ops != &rtnl_qdisc_ops)
  341. return NULL;
  342. nl_list_for_each_entry(q, &cache->c_items, ce_list) {
  343. if (q->q_parent == parent && q->q_ifindex == ifindex) {
  344. nl_object_get((struct nl_object *) q);
  345. return q;
  346. }
  347. }
  348. return NULL;
  349. }
  350. /**
  351. * Search qdisc by interface index and handle
  352. * @arg cache Qdisc cache
  353. * @arg ifindex Interface index
  354. * @arg handle Handle
  355. *
  356. * Searches a qdisc cache previously allocated with rtnl_qdisc_alloc_cache()
  357. * and searches for a qdisc matching the interface index and handle.
  358. *
  359. * The reference counter is incremented before returning the qdisc, therefore
  360. * the reference must be given back with rtnl_qdisc_put() after usage.
  361. *
  362. * @return Qdisc or NULL if no match was found.
  363. */
  364. struct rtnl_qdisc *rtnl_qdisc_get(struct nl_cache *cache, int ifindex,
  365. uint32_t handle)
  366. {
  367. struct rtnl_qdisc *q;
  368. if (cache->c_ops != &rtnl_qdisc_ops)
  369. return NULL;
  370. nl_list_for_each_entry(q, &cache->c_items, ce_list) {
  371. if (q->q_handle == handle && q->q_ifindex == ifindex) {
  372. nl_object_get((struct nl_object *) q);
  373. return q;
  374. }
  375. }
  376. return NULL;
  377. }
  378. /** @} */
  379. /**
  380. * @name Deprecated Functions
  381. * @{
  382. */
  383. /**
  384. * Call a callback for each child class of a qdisc (deprecated)
  385. *
  386. * @deprecated Use of this function is deprecated, it does not allow
  387. * to handle the out of memory situation that can occur.
  388. */
  389. void rtnl_qdisc_foreach_child(struct rtnl_qdisc *qdisc, struct nl_cache *cache,
  390. void (*cb)(struct nl_object *, void *), void *arg)
  391. {
  392. struct rtnl_class *filter;
  393. filter = rtnl_class_alloc();
  394. if (!filter)
  395. return;
  396. rtnl_tc_set_parent(TC_CAST(filter), qdisc->q_handle);
  397. rtnl_tc_set_ifindex(TC_CAST(filter), qdisc->q_ifindex);
  398. rtnl_tc_set_kind(TC_CAST(filter), qdisc->q_kind);
  399. nl_cache_foreach_filter(cache, OBJ_CAST(filter), cb, arg);
  400. rtnl_class_put(filter);
  401. }
  402. /**
  403. * Call a callback for each filter attached to the qdisc (deprecated)
  404. *
  405. * @deprecated Use of this function is deprecated, it does not allow
  406. * to handle the out of memory situation that can occur.
  407. */
  408. void rtnl_qdisc_foreach_cls(struct rtnl_qdisc *qdisc, struct nl_cache *cache,
  409. void (*cb)(struct nl_object *, void *), void *arg)
  410. {
  411. struct rtnl_cls *filter;
  412. if (!(filter = rtnl_cls_alloc()))
  413. return;
  414. rtnl_tc_set_ifindex(TC_CAST(filter), qdisc->q_ifindex);
  415. rtnl_tc_set_parent(TC_CAST(filter), qdisc->q_parent);
  416. nl_cache_foreach_filter(cache, OBJ_CAST(filter), cb, arg);
  417. rtnl_cls_put(filter);
  418. }
  419. /**
  420. * Build a netlink message requesting the update of a qdisc
  421. *
  422. * @deprecated Use of this function is deprecated in favour of
  423. * rtnl_qdisc_build_update_request() due to the missing
  424. * possibility of specifying additional flags.
  425. */
  426. int rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc,
  427. struct rtnl_qdisc *new,
  428. struct nl_msg **result)
  429. {
  430. return rtnl_qdisc_build_update_request(qdisc, new, NLM_F_REPLACE,
  431. result);
  432. }
  433. /**
  434. * Change attributes of a qdisc
  435. *
  436. * @deprecated Use of this function is deprecated in favour of
  437. * rtnl_qdisc_update() due to the missing possibility of
  438. * specifying additional flags.
  439. */
  440. int rtnl_qdisc_change(struct nl_sock *sk, struct rtnl_qdisc *qdisc,
  441. struct rtnl_qdisc *new)
  442. {
  443. return rtnl_qdisc_update(sk, qdisc, new, NLM_F_REPLACE);
  444. }
  445. /** @} */
  446. static void qdisc_dump_details(struct rtnl_tc *tc, struct nl_dump_params *p)
  447. {
  448. struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) tc;
  449. nl_dump(p, "refcnt %u ", qdisc->q_info);
  450. }
  451. static struct rtnl_tc_type_ops qdisc_ops = {
  452. .tt_type = RTNL_TC_TYPE_QDISC,
  453. .tt_dump_prefix = "qdisc",
  454. .tt_dump = {
  455. [NL_DUMP_DETAILS] = qdisc_dump_details,
  456. },
  457. };
  458. static struct nl_cache_ops rtnl_qdisc_ops = {
  459. .co_name = "route/qdisc",
  460. .co_hdrsize = sizeof(struct tcmsg),
  461. .co_msgtypes = {
  462. { RTM_NEWQDISC, NL_ACT_NEW, "new" },
  463. { RTM_DELQDISC, NL_ACT_DEL, "del" },
  464. { RTM_GETQDISC, NL_ACT_GET, "get" },
  465. END_OF_MSGTYPES_LIST,
  466. },
  467. .co_protocol = NETLINK_ROUTE,
  468. .co_groups = tc_groups,
  469. .co_request_update = qdisc_request_update,
  470. .co_msg_parser = qdisc_msg_parser,
  471. .co_obj_ops = &qdisc_obj_ops,
  472. };
  473. static struct nl_object_ops qdisc_obj_ops = {
  474. .oo_name = "route/qdisc",
  475. .oo_size = sizeof(struct rtnl_qdisc),
  476. .oo_free_data = rtnl_tc_free_data,
  477. .oo_clone = rtnl_tc_clone,
  478. .oo_dump = {
  479. [NL_DUMP_LINE] = rtnl_tc_dump_line,
  480. [NL_DUMP_DETAILS] = rtnl_tc_dump_details,
  481. [NL_DUMP_STATS] = rtnl_tc_dump_stats,
  482. },
  483. .oo_compare = rtnl_tc_compare,
  484. .oo_id_attrs = (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE),
  485. };
  486. static void __init qdisc_init(void)
  487. {
  488. rtnl_tc_type_register(&qdisc_ops);
  489. nl_cache_mngt_register(&rtnl_qdisc_ops);
  490. }
  491. static void __exit qdisc_exit(void)
  492. {
  493. nl_cache_mngt_unregister(&rtnl_qdisc_ops);
  494. rtnl_tc_type_unregister(&qdisc_ops);
  495. }
  496. /** @} */