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-2008 Thomas Graf <tgraf@suug.ch>
10 */
11
12 /**
13 * @ingroup tc
14 * @defgroup qdisc Queueing Disciplines
15 *
16 * @par Qdisc Handles
17 * In general, qdiscs are identified by the major part of a traffic control
18 * handle (the upper 16 bits). A few special values exist though:
19 * - \c TC_H_ROOT: root qdisc (directly attached to the device)
20 * - \c TC_H_INGRESS: ingress qdisc (directly attached to the device)
21 * - \c TC_H_UNSPEC: unspecified qdisc (no reference)
22 *
23 * @par 1) Adding a Qdisc
24 * @code
25 * // Allocate a new empty qdisc to be filled out
26 * struct rtnl_qdisc *qdisc = rtnl_qdisc_alloc();
27 *
28 * // ... specify the kind of the Qdisc
29 * rtnl_qdisc_set_kind(qdisc, "pfifo");
30 *
31 * // Specify the device the qdisc should be attached to
32 * rtnl_qdisc_set_ifindex(qdisc, ifindex);
33 *
34 * // ... specify the parent qdisc
35 * rtnl_qdisc_set_parent(qdisc, TC_H_ROOT);
36 *
37 * // Specifying the handle is not required but makes reidentifying easier
38 * // and may help to avoid adding a qdisc twice.
39 * rtnl_qdisc_set_handle(qdisc, 0x000A0000);
40 *
41 * // Now on to specify the qdisc specific options, see the relevant qdisc
42 * // modules for documentation, in this example we set the upper limit of
43 * // the packet fifo qdisc to 64
44 * rtnl_qdisc_fifo_set_limit(qdisc, 64);
45 *
46 * rtnl_qdisc_add(handle, qdisc, NLM_R_REPLACE);
47 *
48 * // Free up the memory
49 * rtnl_qdisc_put(qdisc);
50 * @endcode
51 *
52 * @par 2) Deleting a Qdisc
53 * @code
54 * // Allocate a new empty qdisc to be filled out with the parameters
55 * // specifying the qdisc to be deleted. Alternatively a fully equiped
56 * // Qdisc object from a cache can be used.
57 * struct rtnl_qdisc *qdisc = rtnl_qdisc_alloc();
58 *
59 * // The interface index of the device the qdisc is on and the parent handle
60 * // are the least required fields to be filled out.
61 * // Note: Specify TC_H_ROOT or TC_H_INGRESS as parent handle to delete the
62 * // root respectively root ingress qdisc.
63 * rtnl_qdisc_set_ifindex(qdisc, ifindex);
64 * rtnl_qdisc_set_parent(qdisc, parent_handle);
65 *
66 * // If required for identification, the handle can be specified as well.
67 * rtnl_qdisc_set_handle(qdisc, qdisc_handle);
68 *
69 * // Not required but maybe helpful as sanity check, the kind of the qdisc
70 * // can be specified to avoid mistakes.
71 * rtnl_qdisc_set_kind(qdisc, "pfifo");
72 *
73 * // Finally delete the qdisc with rtnl_qdisc_delete(), alternatively
74 * // rtnl_qdisc_build_delete_request() can be invoked to generate an
75 * // appropritate netlink message to send out.
76 * rtnl_qdisc_delete(handle, qdisc);
77 *
78 * // Free up the memory
79 * rtnl_qdisc_put(qdisc);
80 * @endcode
81 *
82 * @{
83 */
84
85 #include <netlink-local.h>
86 #include <netlink-tc.h>
87 #include <netlink/netlink.h>
88 #include <netlink/utils.h>
89 #include <netlink/route/link.h>
90 #include <netlink/route/tc.h>
91 #include <netlink/route/qdisc.h>
92 #include <netlink/route/class.h>
93 #include <netlink/route/classifier.h>
94 #include <netlink/route/qdisc-modules.h>
95
96 static struct nl_cache_ops rtnl_qdisc_ops;
97
qdisc_msg_parser(struct nl_cache_ops * ops,struct sockaddr_nl * who,struct nlmsghdr * n,struct nl_parser_param * pp)98 static int qdisc_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
99 struct nlmsghdr *n, struct nl_parser_param *pp)
100 {
101 int err;
102 struct rtnl_qdisc *qdisc;
103 struct rtnl_qdisc_ops *qops;
104
105 qdisc = rtnl_qdisc_alloc();
106 if (!qdisc) {
107 err = -NLE_NOMEM;
108 goto errout;
109 }
110
111 qdisc->ce_msgtype = n->nlmsg_type;
112
113 err = tca_msg_parser(n, (struct rtnl_tca *) qdisc);
114 if (err < 0)
115 goto errout_free;
116
117 qops = rtnl_qdisc_lookup_ops(qdisc);
118 if (qops && qops->qo_msg_parser) {
119 err = qops->qo_msg_parser(qdisc);
120 if (err < 0)
121 goto errout_free;
122 }
123
124 err = pp->pp_cb((struct nl_object *) qdisc, pp);
125 errout_free:
126 rtnl_qdisc_put(qdisc);
127 errout:
128 return err;
129 }
130
qdisc_request_update(struct nl_cache * c,struct nl_sock * sk)131 static int qdisc_request_update(struct nl_cache *c, struct nl_sock *sk)
132 {
133 struct tcmsg tchdr = {
134 .tcm_family = AF_UNSPEC,
135 .tcm_ifindex = c->c_iarg1,
136 };
137
138 return nl_send_simple(sk, RTM_GETQDISC, NLM_F_DUMP, &tchdr,
139 sizeof(tchdr));
140 }
141
142 /**
143 * @name QDisc Addition
144 * @{
145 */
146
qdisc_build(struct rtnl_qdisc * qdisc,int type,int flags,struct nl_msg ** result)147 static int qdisc_build(struct rtnl_qdisc *qdisc, int type, int flags,
148 struct nl_msg **result)
149 {
150 struct rtnl_qdisc_ops *qops;
151 int err;
152
153 err = tca_build_msg((struct rtnl_tca *) qdisc, type, flags, result);
154 if (err < 0)
155 return err;
156
157 qops = rtnl_qdisc_lookup_ops(qdisc);
158 if (qops && qops->qo_get_opts) {
159 struct nl_msg *opts;
160
161 opts = qops->qo_get_opts(qdisc);
162 if (opts) {
163 err = nla_put_nested(*result, TCA_OPTIONS, opts);
164 nlmsg_free(opts);
165 if (err < 0)
166 goto errout;
167 }
168 }
169 /* Some qdiscs don't accept properly nested messages (e.g. netem). To
170 * accomodate for this, they can complete the message themselves.
171 */
172 else if (qops && qops->qo_build_msg) {
173 err = qops->qo_build_msg(qdisc, *result);
174 if (err < 0)
175 goto errout;
176 }
177
178 return 0;
179 errout:
180 nlmsg_free(*result);
181
182 return err;
183 }
184
185 /**
186 * Build a netlink message to add a new qdisc
187 * @arg qdisc qdisc to add
188 * @arg flags additional netlink message flags
189 * @arg result Pointer to store resulting message.
190 *
191 * Builds a new netlink message requesting an addition of a qdisc.
192 * The netlink message header isn't fully equipped with all relevant
193 * fields and must be sent out via nl_send_auto_complete() or
194 * supplemented as needed.
195 *
196 * Common message flags used:
197 * - NLM_F_REPLACE - replace a potential existing qdisc
198 *
199 * @return 0 on success or a negative error code.
200 */
rtnl_qdisc_build_add_request(struct rtnl_qdisc * qdisc,int flags,struct nl_msg ** result)201 int rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc, int flags,
202 struct nl_msg **result)
203 {
204 return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_CREATE | flags, result);
205 }
206
207 /**
208 * Add a new qdisc
209 * @arg sk Netlink socket.
210 * @arg qdisc qdisc to delete
211 * @arg flags additional netlink message flags
212 *
213 * Builds a netlink message by calling rtnl_qdisc_build_add_request(),
214 * sends the request to the kernel and waits for the ACK to be
215 * received and thus blocks until the request has been processed.
216 *
217 * Common message flags used:
218 * - NLM_F_REPLACE - replace a potential existing qdisc
219 *
220 * @return 0 on success or a negative error code
221 */
rtnl_qdisc_add(struct nl_sock * sk,struct rtnl_qdisc * qdisc,int flags)222 int rtnl_qdisc_add(struct nl_sock *sk, struct rtnl_qdisc *qdisc,
223 int flags)
224 {
225 struct nl_msg *msg;
226 int err;
227
228 if ((err = rtnl_qdisc_build_add_request(qdisc, flags, &msg)) < 0)
229 return err;
230
231 err = nl_send_auto_complete(sk, msg);
232 nlmsg_free(msg);
233 if (err < 0)
234 return err;
235
236 return wait_for_ack(sk);
237 }
238
239 /** @} */
240
241 /**
242 * @name QDisc Modification
243 * @{
244 */
245
246 /**
247 * Build a netlink message to change attributes of a existing qdisc
248 * @arg qdisc qdisc to change
249 * @arg new new qdisc attributes
250 * @arg result Pointer to store resulting message.
251 *
252 * Builds a new netlink message requesting an change of qdisc
253 * attributes. The netlink message header isn't fully equipped
254 * with all relevant fields and must be sent out via
255 * nl_send_auto_complete() or supplemented as needed.
256 *
257 * @return 0 on success or a negative error code.
258 */
rtnl_qdisc_build_change_request(struct rtnl_qdisc * qdisc,struct rtnl_qdisc * new,struct nl_msg ** result)259 int rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc,
260 struct rtnl_qdisc *new,
261 struct nl_msg **result)
262 {
263 return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_REPLACE, result);
264 }
265
266 /**
267 * Change attributes of a qdisc
268 * @arg sk Netlink socket.
269 * @arg qdisc qdisc to change
270 * @arg new new qdisc attributes
271 *
272 * Builds a netlink message by calling rtnl_qdisc_build_change_request(),
273 * sends the request to the kernel and waits for the ACK to be
274 * received and thus blocks until the request has been processed.
275 *
276 * @return 0 on success or a negative error code
277 */
rtnl_qdisc_change(struct nl_sock * sk,struct rtnl_qdisc * qdisc,struct rtnl_qdisc * new)278 int rtnl_qdisc_change(struct nl_sock *sk, struct rtnl_qdisc *qdisc,
279 struct rtnl_qdisc *new)
280 {
281 struct nl_msg *msg;
282 int err;
283
284 if ((err = rtnl_qdisc_build_change_request(qdisc, new, &msg)) < 0)
285 return err;
286
287 err = nl_send_auto_complete(sk, msg);
288 nlmsg_free(msg);
289 if (err < 0)
290 return err;
291
292 return wait_for_ack(sk);
293 }
294
295 /** @} */
296
297 /**
298 * @name QDisc Deletion
299 * @{
300 */
301
302 /**
303 * Build a netlink request message to delete a qdisc
304 * @arg qdisc qdisc to delete
305 * @arg result Pointer to store resulting message.
306 *
307 * Builds a new netlink message requesting a deletion of a qdisc.
308 * The netlink message header isn't fully equipped with all relevant
309 * fields and must thus be sent out via nl_send_auto_complete()
310 * or supplemented as needed.
311 *
312 * @return 0 on success or a negative error code.
313 */
rtnl_qdisc_build_delete_request(struct rtnl_qdisc * qdisc,struct nl_msg ** result)314 int rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc,
315 struct nl_msg **result)
316 {
317 struct nl_msg *msg;
318 struct tcmsg tchdr;
319 int required = TCA_ATTR_IFINDEX | TCA_ATTR_PARENT;
320
321 if ((qdisc->ce_mask & required) != required)
322 BUG();
323
324 msg = nlmsg_alloc_simple(RTM_DELQDISC, 0);
325 if (!msg)
326 return -NLE_NOMEM;
327
328 tchdr.tcm_family = AF_UNSPEC;
329 tchdr.tcm_handle = qdisc->q_handle;
330 tchdr.tcm_parent = qdisc->q_parent;
331 tchdr.tcm_ifindex = qdisc->q_ifindex;
332 if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) {
333 nlmsg_free(msg);
334 return -NLE_MSGSIZE;
335 }
336
337 *result = msg;
338 return 0;
339 }
340
341 /**
342 * Delete a qdisc
343 * @arg sk Netlink socket.
344 * @arg qdisc qdisc to delete
345 *
346 * Builds a netlink message by calling rtnl_qdisc_build_delete_request(),
347 * sends the request to the kernel and waits for the ACK to be
348 * received and thus blocks until the request has been processed.
349 *
350 * @return 0 on success or a negative error code
351 */
rtnl_qdisc_delete(struct nl_sock * sk,struct rtnl_qdisc * qdisc)352 int rtnl_qdisc_delete(struct nl_sock *sk, struct rtnl_qdisc *qdisc)
353 {
354 struct nl_msg *msg;
355 int err;
356
357 if ((err = rtnl_qdisc_build_delete_request(qdisc, &msg)) < 0)
358 return err;
359
360 err = nl_send_auto_complete(sk, msg);
361 nlmsg_free(msg);
362 if (err < 0)
363 return err;
364
365 return wait_for_ack(sk);
366 }
367
368 /** @} */
369
370 /**
371 * @name Qdisc Cache Management
372 * @{
373 */
374
375 /**
376 * Build a qdisc cache including all qdiscs currently configured in
377 * the kernel
378 * @arg sk Netlink socket.
379 * @arg result Pointer to store resulting message.
380 *
381 * Allocates a new cache, initializes it properly and updates it to
382 * include all qdiscs currently configured in the kernel.
383 *
384 * @return 0 on success or a negative error code.
385 */
rtnl_qdisc_alloc_cache(struct nl_sock * sk,struct nl_cache ** result)386 int rtnl_qdisc_alloc_cache(struct nl_sock *sk, struct nl_cache **result)
387 {
388 return nl_cache_alloc_and_fill(&rtnl_qdisc_ops, sk, result);
389 }
390
391 /**
392 * Look up qdisc by its parent in the provided cache
393 * @arg cache qdisc cache
394 * @arg ifindex interface the qdisc is attached to
395 * @arg parent parent handle
396 * @return pointer to qdisc inside the cache or NULL if no match was found.
397 */
rtnl_qdisc_get_by_parent(struct nl_cache * cache,int ifindex,uint32_t parent)398 struct rtnl_qdisc * rtnl_qdisc_get_by_parent(struct nl_cache *cache,
399 int ifindex, uint32_t parent)
400 {
401 struct rtnl_qdisc *q;
402
403 if (cache->c_ops != &rtnl_qdisc_ops)
404 return NULL;
405
406 nl_list_for_each_entry(q, &cache->c_items, ce_list) {
407 if (q->q_parent == parent && q->q_ifindex == ifindex) {
408 nl_object_get((struct nl_object *) q);
409 return q;
410 }
411 }
412
413 return NULL;
414 }
415
416 /**
417 * Look up qdisc by its handle in the provided cache
418 * @arg cache qdisc cache
419 * @arg ifindex interface the qdisc is attached to
420 * @arg handle qdisc handle
421 * @return pointer to qdisc inside the cache or NULL if no match was found.
422 */
rtnl_qdisc_get(struct nl_cache * cache,int ifindex,uint32_t handle)423 struct rtnl_qdisc * rtnl_qdisc_get(struct nl_cache *cache,
424 int ifindex, uint32_t handle)
425 {
426 struct rtnl_qdisc *q;
427
428 if (cache->c_ops != &rtnl_qdisc_ops)
429 return NULL;
430
431 nl_list_for_each_entry(q, &cache->c_items, ce_list) {
432 if (q->q_handle == handle && q->q_ifindex == ifindex) {
433 nl_object_get((struct nl_object *) q);
434 return q;
435 }
436 }
437
438 return NULL;
439 }
440
441 /** @} */
442
443 static struct nl_cache_ops rtnl_qdisc_ops = {
444 .co_name = "route/qdisc",
445 .co_hdrsize = sizeof(struct tcmsg),
446 .co_msgtypes = {
447 { RTM_NEWQDISC, NL_ACT_NEW, "new" },
448 { RTM_DELQDISC, NL_ACT_DEL, "del" },
449 { RTM_GETQDISC, NL_ACT_GET, "get" },
450 END_OF_MSGTYPES_LIST,
451 },
452 .co_protocol = NETLINK_ROUTE,
453 .co_request_update = qdisc_request_update,
454 .co_msg_parser = qdisc_msg_parser,
455 .co_obj_ops = &qdisc_obj_ops,
456 };
457
qdisc_init(void)458 static void __init qdisc_init(void)
459 {
460 nl_cache_mngt_register(&rtnl_qdisc_ops);
461 }
462
qdisc_exit(void)463 static void __exit qdisc_exit(void)
464 {
465 nl_cache_mngt_unregister(&rtnl_qdisc_ops);
466 }
467
468 /** @} */
469