libnl  1.1
qdisc.c
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-2006 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 
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 = -ENOMEM;
102  struct rtnl_qdisc *qdisc;
103  struct rtnl_qdisc_ops *qops;
104 
105  qdisc = rtnl_qdisc_alloc();
106  if (!qdisc) {
107  err = nl_errno(ENOMEM);
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  if (err < 0)
126  goto errout_free;
127 
128  err = P_ACCEPT;
129 
130 errout_free:
131  rtnl_qdisc_put(qdisc);
132 errout:
133  return err;
134 }
135 
136 static int qdisc_request_update(struct nl_cache *c, struct nl_handle *h)
137 {
138  struct tcmsg tchdr = {
139  .tcm_family = AF_UNSPEC,
140  .tcm_ifindex = c->c_iarg1,
141  };
142 
143  return nl_send_simple(h, RTM_GETQDISC, NLM_F_DUMP, &tchdr,
144  sizeof(tchdr));
145 }
146 
147 /**
148  * @name QDisc Addition
149  * @{
150  */
151 
152 static struct nl_msg *qdisc_build(struct rtnl_qdisc *qdisc, int type, int flags)
153 {
154  struct rtnl_qdisc_ops *qops;
155  struct nl_msg *msg;
156  int err;
157 
158  msg = tca_build_msg((struct rtnl_tca *) qdisc, type, flags);
159  if (!msg)
160  goto errout;
161 
162  qops = rtnl_qdisc_lookup_ops(qdisc);
163  if (qops && qops->qo_get_opts) {
164  struct nl_msg *opts;
165 
166  opts = qops->qo_get_opts(qdisc);
167  if (opts) {
168  err = nla_put_nested(msg, TCA_OPTIONS, opts);
169  nlmsg_free(opts);
170  if (err < 0)
171  goto errout;
172  }
173  }
174 
175  return msg;
176 errout:
177  nlmsg_free(msg);
178 
179  return NULL;
180 }
181 
182 /**
183  * Build a netlink message to add a new qdisc
184  * @arg qdisc qdisc to add
185  * @arg flags additional netlink message flags
186  *
187  * Builds a new netlink message requesting an addition of a qdisc.
188  * The netlink message header isn't fully equipped with all relevant
189  * fields and must be sent out via nl_send_auto_complete() or
190  * supplemented as needed.
191  *
192  * Common message flags used:
193  * - NLM_F_REPLACE - replace a potential existing qdisc
194  *
195  * @return New netlink message
196  */
197 struct nl_msg *rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc,
198  int flags)
199 {
200  struct nl_msg *msg;
201 
202  msg = qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_CREATE | flags);
203  if (!msg)
204  nl_errno(ENOMEM);
205 
206  return msg;
207 }
208 
209 /**
210  * Add a new qdisc
211  * @arg handle netlink handle
212  * @arg qdisc qdisc to delete
213  * @arg flags additional netlink message flags
214  *
215  * Builds a netlink message by calling rtnl_qdisc_build_add_request(),
216  * sends the request to the kernel and waits for the ACK to be
217  * received and thus blocks until the request has been processed.
218  *
219  * Common message flags used:
220  * - NLM_F_REPLACE - replace a potential existing qdisc
221  *
222  * @return 0 on success or a negative error code
223  */
224 int rtnl_qdisc_add(struct nl_handle *handle, struct rtnl_qdisc *qdisc,
225  int flags)
226 {
227  struct nl_msg *msg;
228  int err;
229 
230  msg = rtnl_qdisc_build_add_request(qdisc, flags);
231  if (!msg)
232  return nl_errno(ENOMEM);
233 
234  err = nl_send_auto_complete(handle, msg);
235  if (err < 0)
236  return err;
237 
238  nlmsg_free(msg);
239  return nl_wait_for_ack(handle);
240 }
241 
242 /** @} */
243 
244 /**
245  * @name QDisc Modification
246  * @{
247  */
248 
249 /**
250  * Build a netlink message to change attributes of a existing qdisc
251  * @arg qdisc qdisc to change
252  * @arg new new qdisc attributes
253  *
254  * Builds a new netlink message requesting an change of qdisc
255  * attributes. The netlink message header isn't fully equipped
256  * with all relevant fields and must be sent out via
257  * nl_send_auto_complete() or supplemented as needed.
258  *
259  * @return New netlink message
260  */
261 struct nl_msg *rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc,
262  struct rtnl_qdisc *new)
263 {
264  return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_REPLACE);
265 }
266 
267 /**
268  * Change attributes of a qdisc
269  * @arg handle netlink handle
270  * @arg qdisc qdisc to change
271  * @arg new new qdisc attributes
272  *
273  * Builds a netlink message by calling rtnl_qdisc_build_change_request(),
274  * sends the request to the kernel and waits for the ACK to be
275  * received and thus blocks until the request has been processed.
276  *
277  * @return 0 on success or a negative error code
278  */
279 int rtnl_qdisc_change(struct nl_handle *handle, struct rtnl_qdisc *qdisc,
280  struct rtnl_qdisc *new)
281 {
282  struct nl_msg *msg;
283  int err;
284 
285  msg = rtnl_qdisc_build_change_request(qdisc, new);
286  if (!msg)
287  return nl_errno(ENOMEM);
288 
289  err = nl_send_auto_complete(handle, msg);
290  if (err < 0)
291  return err;
292 
293  nlmsg_free(msg);
294  return nl_wait_for_ack(handle);
295 }
296 
297 /** @} */
298 
299 /**
300  * @name QDisc Deletion
301  * @{
302  */
303 
304 /**
305  * Build a netlink request message to delete a qdisc
306  * @arg qdisc qdisc to delete
307  *
308  * Builds a new netlink message requesting a deletion of a qdisc.
309  * The netlink message header isn't fully equipped with all relevant
310  * fields and must thus be sent out via nl_send_auto_complete()
311  * or supplemented as needed.
312  *
313  * @return New netlink message
314  */
315 struct nl_msg *rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc)
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 NULL;
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  nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO);
333 
334  return msg;
335 }
336 
337 /**
338  * Delete a qdisc
339  * @arg handle netlink handle
340  * @arg qdisc qdisc to delete
341  *
342  * Builds a netlink message by calling rtnl_qdisc_build_delete_request(),
343  * sends the request to the kernel and waits for the ACK to be
344  * received and thus blocks until the request has been processed.
345  *
346  * @return 0 on success or a negative error code
347  */
348 int rtnl_qdisc_delete(struct nl_handle *handle, struct rtnl_qdisc *qdisc)
349 {
350  struct nl_msg *msg;
351  int err;
352 
353  msg = rtnl_qdisc_build_delete_request(qdisc);
354  if (!msg)
355  return nl_errno(ENOMEM);
356 
357  err = nl_send_auto_complete(handle, msg);
358  if (err < 0)
359  return err;
360 
361  nlmsg_free(msg);
362  return nl_wait_for_ack(handle);
363 }
364 
365 /** @} */
366 
367 /**
368  * @name Qdisc Cache Management
369  * @{
370  */
371 
372 /**
373  * Build a qdisc cache including all qdiscs currently configured in
374  * the kernel
375  * @arg handle netlink handle
376  *
377  * Allocates a new cache, initializes it properly and updates it to
378  * include all qdiscs currently configured in the kernel.
379  *
380  * @note The caller is responsible for destroying and freeing the
381  * cache after using it.
382  * @return The cache or NULL if an error has occured.
383  */
384 struct nl_cache * rtnl_qdisc_alloc_cache(struct nl_handle *handle)
385 {
386  struct nl_cache * cache;
387 
388  cache = nl_cache_alloc(&rtnl_qdisc_ops);
389  if (cache == NULL)
390  return NULL;
391 
392  if (handle && nl_cache_refill(handle, cache) < 0) {
393  nl_cache_free(cache);
394  return NULL;
395  }
396 
397  return cache;
398 }
399 
400 /**
401  * Look up qdisc by its parent in the provided cache
402  * @arg cache qdisc cache
403  * @arg ifindex interface the qdisc is attached to
404  * @arg parent parent handle
405  * @return pointer to qdisc inside the cache or NULL if no match was found.
406  */
407 struct rtnl_qdisc * rtnl_qdisc_get_by_parent(struct nl_cache *cache,
408  int ifindex, uint32_t parent)
409 {
410  struct rtnl_qdisc *q;
411 
412  if (cache->c_ops != &rtnl_qdisc_ops)
413  return NULL;
414 
415  nl_list_for_each_entry(q, &cache->c_items, ce_list) {
416  if (q->q_parent == parent && q->q_ifindex == ifindex) {
417  nl_object_get((struct nl_object *) q);
418  return q;
419  }
420  }
421 
422  return NULL;
423 }
424 
425 /**
426  * Look up qdisc by its handle in the provided cache
427  * @arg cache qdisc cache
428  * @arg ifindex interface the qdisc is attached to
429  * @arg handle qdisc handle
430  * @return pointer to qdisc inside the cache or NULL if no match was found.
431  */
432 struct rtnl_qdisc * rtnl_qdisc_get(struct nl_cache *cache,
433  int ifindex, uint32_t handle)
434 {
435  struct rtnl_qdisc *q;
436 
437  if (cache->c_ops != &rtnl_qdisc_ops)
438  return NULL;
439 
440  nl_list_for_each_entry(q, &cache->c_items, ce_list) {
441  if (q->q_handle == handle && q->q_ifindex == ifindex) {
442  nl_object_get((struct nl_object *) q);
443  return q;
444  }
445  }
446 
447  return NULL;
448 }
449 
450 /** @} */
451 
452 static struct nl_cache_ops rtnl_qdisc_ops = {
453  .co_name = "route/qdisc",
454  .co_hdrsize = sizeof(struct tcmsg),
455  .co_msgtypes = {
456  { RTM_NEWQDISC, NL_ACT_NEW, "new" },
457  { RTM_DELQDISC, NL_ACT_DEL, "del" },
458  { RTM_GETQDISC, NL_ACT_GET, "get" },
459  END_OF_MSGTYPES_LIST,
460  },
461  .co_protocol = NETLINK_ROUTE,
462  .co_request_update = qdisc_request_update,
463  .co_msg_parser = qdisc_msg_parser,
464  .co_obj_ops = &qdisc_obj_ops,
465 };
466 
467 static void __init qdisc_init(void)
468 {
469  nl_cache_mngt_register(&rtnl_qdisc_ops);
470 }
471 
472 static void __exit qdisc_exit(void)
473 {
474  nl_cache_mngt_unregister(&rtnl_qdisc_ops);
475 }
476 
477 /** @} */
#define NLM_F_REPLACE
Replace existing matching config object with this request.
struct rtnl_qdisc * rtnl_qdisc_get_by_parent(struct nl_cache *cache, int ifindex, uint32_t parent)
Look up qdisc by its parent in the provided cache.
Definition: qdisc.c:407
uint16_t nlmsg_type
Message type (content type)
int nl_cache_mngt_unregister(struct nl_cache_ops *ops)
Unregister a set of cache operations.
Definition: cache_mngt.c:157
int nl_wait_for_ack(struct nl_handle *handle)
Wait for ACK.
Definition: nl.c:800
void nl_object_get(struct nl_object *obj)
Acquire a reference on a object.
Definition: object.c:162
void nlmsg_free(struct nl_msg *n)
Free a netlink message.
Definition: msg.c:656
Netlink message header.
struct nl_cache * rtnl_qdisc_alloc_cache(struct nl_handle *handle)
Build a qdisc cache including all qdiscs currently configured in the kernel.
Definition: qdisc.c:384
int nl_send_auto_complete(struct nl_handle *handle, struct nl_msg *msg)
Send netlink message and check & extend header values as needed.
Definition: nl.c:373
void nl_cache_free(struct nl_cache *cache)
Free a cache.
Definition: cache.c:265
struct nl_msg * rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc, int flags)
Build a netlink message to add a new qdisc.
Definition: qdisc.c:197
int nl_cache_mngt_register(struct nl_cache_ops *ops)
Register a set of cache operations.
Definition: cache_mngt.c:127
int nl_cache_refill(struct nl_handle *handle, struct nl_cache *cache)
(Re)fill a cache with the contents in the kernel.
Definition: cache.c:662
int nl_send_simple(struct nl_handle *handle, int type, int flags, void *buf, size_t size)
Send simple netlink message using nl_send_auto_complete()
Definition: nl.c:410
#define NLM_F_CREATE
Create config object if it doesn't already exist.
#define NLM_F_DUMP
Dump all entries.
Cache Operations.
Definition: cache-api.h:163
int rtnl_qdisc_change(struct nl_handle *handle, struct rtnl_qdisc *qdisc, struct rtnl_qdisc *new)
Change attributes of a qdisc.
Definition: qdisc.c:279
struct rtnl_qdisc * rtnl_qdisc_get(struct nl_cache *cache, int ifindex, uint32_t handle)
Look up qdisc by its handle in the provided cache.
Definition: qdisc.c:432
int(* qo_msg_parser)(struct rtnl_qdisc *)
TCA_OPTIONS message parser.
Definition: qdisc-modules.h:46
Netlink socket address.
Definition: netlink-kernel.h:8
int nlmsg_append(struct nl_msg *n, void *data, size_t len, int pad)
Append data to tail of a netlink message.
Definition: msg.c:549
int rtnl_qdisc_delete(struct nl_handle *handle, struct rtnl_qdisc *qdisc)
Delete a qdisc.
Definition: qdisc.c:348
struct nl_msg * nlmsg_alloc_simple(int nlmsgtype, int flags)
Allocate a new netlink message.
Definition: msg.c:448
struct nl_msg *(* qo_get_opts)(struct rtnl_qdisc *)
Must return the contents supposed to be in TCA_OPTIONS.
Definition: qdisc-modules.h:41
int nla_put_nested(struct nl_msg *n, int attrtype, struct nl_msg *nested)
Add a nested netlink attribute to a netlink message.
Definition: attr.c:536
struct nl_msg * rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc, struct rtnl_qdisc *new)
Build a netlink message to change attributes of a existing qdisc.
Definition: qdisc.c:261
struct nl_msg * rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc)
Build a netlink request message to delete a qdisc.
Definition: qdisc.c:315
int rtnl_qdisc_add(struct nl_handle *handle, struct rtnl_qdisc *qdisc, int flags)
Add a new qdisc.
Definition: qdisc.c:224
Qdisc Operations.
Definition: qdisc-modules.h:25
struct nl_cache * nl_cache_alloc(struct nl_cache_ops *ops)
Allocate an empty cache.
Definition: cache.c:173