~ubuntu-branches/ubuntu/trusty/libnl3/trusty

« back to all changes in this revision

Viewing changes to lib/route/qdisc.c

  • Committer: Bazaar Package Importer
  • Author(s): Heiko Stuebner
  • Date: 2011-05-21 19:25:13 UTC
  • Revision ID: james.westby@ubuntu.com-20110521192513-1ieyu9w9kym4bt16
Tags: upstream-3.0
ImportĀ upstreamĀ versionĀ 3.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
/**
 
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-api.h>
 
91
#include <netlink/route/qdisc.h>
 
92
#include <netlink/route/class.h>
 
93
#include <netlink/route/classifier.h>
 
94
 
 
95
static struct nl_cache_ops rtnl_qdisc_ops;
 
96
 
 
97
static int qdisc_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
 
98
                            struct nlmsghdr *n, struct nl_parser_param *pp)
 
99
{
 
100
        struct rtnl_qdisc *qdisc;
 
101
        int err;
 
102
 
 
103
        if (!(qdisc = rtnl_qdisc_alloc()))
 
104
                return -NLE_NOMEM;
 
105
 
 
106
        if ((err = rtnl_tc_msg_parse(n, TC_CAST(qdisc))) < 0)
 
107
                goto errout;
 
108
 
 
109
        err = pp->pp_cb(OBJ_CAST(qdisc), pp);
 
110
errout:
 
111
        rtnl_qdisc_put(qdisc);
 
112
 
 
113
        return err;
 
114
}
 
115
 
 
116
static int qdisc_request_update(struct nl_cache *c, struct nl_sock *sk)
 
117
{
 
118
        struct tcmsg tchdr = {
 
119
                .tcm_family = AF_UNSPEC,
 
120
                .tcm_ifindex = c->c_iarg1,
 
121
        };
 
122
 
 
123
        return nl_send_simple(sk, RTM_GETQDISC, NLM_F_DUMP, &tchdr,
 
124
                              sizeof(tchdr));
 
125
}
 
126
 
 
127
/**
 
128
 * @name QDisc Addition
 
129
 * @{
 
130
 */
 
131
 
 
132
static int qdisc_build(struct rtnl_qdisc *qdisc, int type, int flags,
 
133
                       struct nl_msg **result)
 
134
{
 
135
        return rtnl_tc_msg_build(TC_CAST(qdisc), type, flags, result);
 
136
 
 
137
#if 0
 
138
        /* Some qdiscs don't accept properly nested messages (e.g. netem). To
 
139
         * accomodate for this, they can complete the message themselves.
 
140
         */             
 
141
        else if (qops && qops->qo_build_msg) {
 
142
                err = qops->qo_build_msg(qdisc, *result);
 
143
                if (err < 0)
 
144
                        goto errout;
 
145
        }
 
146
#endif
 
147
}
 
148
 
 
149
/**
 
150
 * Build a netlink message to add a new qdisc
 
151
 * @arg qdisc           qdisc to add 
 
152
 * @arg flags           additional netlink message flags
 
153
 * @arg result          Pointer to store resulting message.
 
154
 *
 
155
 * Builds a new netlink message requesting an addition of a qdisc.
 
156
 * The netlink message header isn't fully equipped with all relevant
 
157
 * fields and must be sent out via nl_send_auto_complete() or
 
158
 * supplemented as needed. 
 
159
 *
 
160
 * Common message flags used:
 
161
 *  - NLM_F_REPLACE - replace a potential existing qdisc
 
162
 *
 
163
 * @return 0 on success or a negative error code.
 
164
 */
 
165
int rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc, int flags,
 
166
                                 struct nl_msg **result)
 
167
{
 
168
        return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_CREATE | flags, result);
 
169
}
 
170
 
 
171
/**
 
172
 * Add a new qdisc
 
173
 * @arg sk              Netlink socket.
 
174
 * @arg qdisc           qdisc to delete
 
175
 * @arg flags           additional netlink message flags
 
176
 *
 
177
 * Builds a netlink message by calling rtnl_qdisc_build_add_request(),
 
178
 * sends the request to the kernel and waits for the ACK to be
 
179
 * received and thus blocks until the request has been processed.
 
180
 *
 
181
 * Common message flags used:
 
182
 *  - NLM_F_REPLACE - replace a potential existing qdisc
 
183
 *
 
184
 * @return 0 on success or a negative error code
 
185
 */
 
186
int rtnl_qdisc_add(struct nl_sock *sk, struct rtnl_qdisc *qdisc,
 
187
                   int flags)
 
188
{
 
189
        struct nl_msg *msg;
 
190
        int err;
 
191
 
 
192
        if ((err = rtnl_qdisc_build_add_request(qdisc, flags, &msg)) < 0)
 
193
                return err;
 
194
 
 
195
        err = nl_send_auto_complete(sk, msg);
 
196
        nlmsg_free(msg);
 
197
        if (err < 0)
 
198
                return err;
 
199
 
 
200
        return wait_for_ack(sk);
 
201
}
 
202
 
 
203
/** @} */
 
204
 
 
205
/**
 
206
 * @name QDisc Modification
 
207
 * @{
 
208
 */
 
209
 
 
210
/**
 
211
 * Build a netlink message to change attributes of a existing qdisc
 
212
 * @arg qdisc           qdisc to change
 
213
 * @arg new             new qdisc attributes
 
214
 * @arg result          Pointer to store resulting message.
 
215
 *
 
216
 * Builds a new netlink message requesting an change of qdisc
 
217
 * attributes. The netlink message header isn't fully equipped
 
218
 * with all relevant fields and must be sent out via
 
219
 * nl_send_auto_complete() or supplemented as needed. 
 
220
 *
 
221
 * @return 0 on success or a negative error code.
 
222
 */
 
223
int rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc,
 
224
                                    struct rtnl_qdisc *new,
 
225
                                    struct nl_msg **result)
 
226
{
 
227
        return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_REPLACE, result);
 
228
}
 
229
 
 
230
/**
 
231
 * Change attributes of a qdisc
 
232
 * @arg sk              Netlink socket.
 
233
 * @arg qdisc           qdisc to change
 
234
 * @arg new             new qdisc attributes
 
235
 *
 
236
 * Builds a netlink message by calling rtnl_qdisc_build_change_request(),
 
237
 * sends the request to the kernel and waits for the ACK to be
 
238
 * received and thus blocks until the request has been processed.
 
239
 *
 
240
 * @return 0 on success or a negative error code
 
241
 */
 
242
int rtnl_qdisc_change(struct nl_sock *sk, struct rtnl_qdisc *qdisc,
 
243
                      struct rtnl_qdisc *new)
 
244
{
 
245
        struct nl_msg *msg;
 
246
        int err;
 
247
 
 
248
        if ((err = rtnl_qdisc_build_change_request(qdisc, new, &msg)) < 0)
 
249
                return err;
 
250
 
 
251
        err = nl_send_auto_complete(sk, msg);
 
252
        nlmsg_free(msg);
 
253
        if (err < 0)
 
254
                return err;
 
255
 
 
256
        return wait_for_ack(sk);
 
257
}
 
258
 
 
259
/** @} */
 
260
 
 
261
/**
 
262
 * @name QDisc Deletion
 
263
 * @{
 
264
 */
 
265
 
 
266
/**
 
267
 * Build a netlink request message to delete a qdisc
 
268
 * @arg qdisc           qdisc to delete
 
269
 * @arg result          Pointer to store resulting message.
 
270
 *
 
271
 * Builds a new netlink message requesting a deletion of a qdisc.
 
272
 * The netlink message header isn't fully equipped with all relevant
 
273
 * fields and must thus be sent out via nl_send_auto_complete()
 
274
 * or supplemented as needed.
 
275
 *
 
276
 * @return 0 on success or a negative error code.
 
277
 */
 
278
int rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc,
 
279
                                    struct nl_msg **result)
 
280
{
 
281
        struct nl_msg *msg;
 
282
        struct tcmsg tchdr;
 
283
        int required = TCA_ATTR_IFINDEX | TCA_ATTR_PARENT;
 
284
 
 
285
        if ((qdisc->ce_mask & required) != required)
 
286
                BUG();
 
287
 
 
288
        msg = nlmsg_alloc_simple(RTM_DELQDISC, 0);
 
289
        if (!msg)
 
290
                return -NLE_NOMEM;
 
291
 
 
292
        tchdr.tcm_family = AF_UNSPEC;
 
293
        tchdr.tcm_handle = qdisc->q_handle;
 
294
        tchdr.tcm_parent = qdisc->q_parent;
 
295
        tchdr.tcm_ifindex = qdisc->q_ifindex;
 
296
        if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) {
 
297
                nlmsg_free(msg);
 
298
                return -NLE_MSGSIZE;
 
299
        }
 
300
 
 
301
        *result = msg;
 
302
        return 0;
 
303
}
 
304
 
 
305
/**
 
306
 * Delete a qdisc
 
307
 * @arg sk              Netlink socket.
 
308
 * @arg qdisc           qdisc to delete
 
309
 *
 
310
 * Builds a netlink message by calling rtnl_qdisc_build_delete_request(),
 
311
 * sends the request to the kernel and waits for the ACK to be
 
312
 * received and thus blocks until the request has been processed.
 
313
 *
 
314
 * @return 0 on success or a negative error code
 
315
 */
 
316
int rtnl_qdisc_delete(struct nl_sock *sk, struct rtnl_qdisc *qdisc)
 
317
{
 
318
        struct nl_msg *msg;
 
319
        int err;
 
320
 
 
321
        if ((err = rtnl_qdisc_build_delete_request(qdisc, &msg)) < 0)
 
322
                return err;
 
323
 
 
324
        err = nl_send_auto_complete(sk, msg);
 
325
        nlmsg_free(msg);
 
326
        if (err < 0)
 
327
                return err;
 
328
 
 
329
        return wait_for_ack(sk);
 
330
}
 
331
 
 
332
/** @} */
 
333
 
 
334
/**
 
335
 * @name Qdisc Cache Management
 
336
 * @{
 
337
 */
 
338
 
 
339
/**
 
340
 * Build a qdisc cache including all qdiscs currently configured in
 
341
 * the kernel
 
342
 * @arg sk              Netlink socket.
 
343
 * @arg result          Pointer to store resulting message.
 
344
 *
 
345
 * Allocates a new cache, initializes it properly and updates it to
 
346
 * include all qdiscs currently configured in the kernel.
 
347
 *
 
348
 * @return 0 on success or a negative error code.
 
349
 */
 
350
int rtnl_qdisc_alloc_cache(struct nl_sock *sk, struct nl_cache **result)
 
351
{
 
352
        return nl_cache_alloc_and_fill(&rtnl_qdisc_ops, sk, result);
 
353
}
 
354
 
 
355
/**
 
356
 * Look up qdisc by its parent in the provided cache
 
357
 * @arg cache           qdisc cache
 
358
 * @arg ifindex         interface the qdisc is attached to
 
359
 * @arg parent          parent handle
 
360
 * @return pointer to qdisc inside the cache or NULL if no match was found.
 
361
 */
 
362
struct rtnl_qdisc * rtnl_qdisc_get_by_parent(struct nl_cache *cache,
 
363
                                             int ifindex, uint32_t parent)
 
364
{
 
365
        struct rtnl_qdisc *q;
 
366
 
 
367
        if (cache->c_ops != &rtnl_qdisc_ops)
 
368
                return NULL;
 
369
 
 
370
        nl_list_for_each_entry(q, &cache->c_items, ce_list) {
 
371
                if (q->q_parent == parent && q->q_ifindex == ifindex) {
 
372
                        nl_object_get((struct nl_object *) q);
 
373
                        return q;
 
374
                }
 
375
        }
 
376
 
 
377
        return NULL;
 
378
}
 
379
 
 
380
/**
 
381
 * Look up qdisc by its handle in the provided cache
 
382
 * @arg cache           qdisc cache
 
383
 * @arg ifindex         interface the qdisc is attached to
 
384
 * @arg handle          qdisc handle
 
385
 * @return pointer to qdisc inside the cache or NULL if no match was found.
 
386
 */
 
387
struct rtnl_qdisc * rtnl_qdisc_get(struct nl_cache *cache,
 
388
                                   int ifindex, uint32_t handle)
 
389
{
 
390
        struct rtnl_qdisc *q;
 
391
 
 
392
        if (cache->c_ops != &rtnl_qdisc_ops)
 
393
                return NULL;
 
394
 
 
395
        nl_list_for_each_entry(q, &cache->c_items, ce_list) {
 
396
                if (q->q_handle == handle && q->q_ifindex == ifindex) {
 
397
                        nl_object_get((struct nl_object *) q);
 
398
                        return q;
 
399
                }
 
400
        }
 
401
 
 
402
        return NULL;
 
403
}
 
404
 
 
405
/** @} */
 
406
 
 
407
/**
 
408
 * @name Allocation/Freeing
 
409
 * @{
 
410
 */
 
411
 
 
412
struct rtnl_qdisc *rtnl_qdisc_alloc(void)
 
413
{
 
414
        struct rtnl_tc *tc;
 
415
 
 
416
        tc = TC_CAST(nl_object_alloc(&qdisc_obj_ops));
 
417
        if (tc)
 
418
                tc->tc_type = RTNL_TC_TYPE_QDISC;
 
419
 
 
420
        return (struct rtnl_qdisc *) tc;
 
421
}
 
422
 
 
423
void rtnl_qdisc_put(struct rtnl_qdisc *qdisc)
 
424
{
 
425
        nl_object_put((struct nl_object *) qdisc);
 
426
}
 
427
 
 
428
/** @} */
 
429
 
 
430
/**
 
431
 * @name Iterators
 
432
 * @{
 
433
 */
 
434
 
 
435
/**
 
436
 * Call a callback for each child class of a qdisc
 
437
 * @arg qdisc           the parent qdisc
 
438
 * @arg cache           a class cache including all classes of the interface
 
439
 *                      the specified qdisc is attached to
 
440
 * @arg cb              callback function
 
441
 * @arg arg             argument to be passed to callback function
 
442
 */
 
443
void rtnl_qdisc_foreach_child(struct rtnl_qdisc *qdisc, struct nl_cache *cache,
 
444
                              void (*cb)(struct nl_object *, void *), void *arg)
 
445
{
 
446
        struct rtnl_class *filter;
 
447
        
 
448
        filter = rtnl_class_alloc();
 
449
        if (!filter)
 
450
                return;
 
451
 
 
452
        rtnl_tc_set_parent(TC_CAST(filter), qdisc->q_handle);
 
453
        rtnl_tc_set_ifindex(TC_CAST(filter), qdisc->q_ifindex);
 
454
        rtnl_tc_set_kind(TC_CAST(filter), qdisc->q_kind);
 
455
 
 
456
        nl_cache_foreach_filter(cache, OBJ_CAST(filter), cb, arg);
 
457
 
 
458
        rtnl_class_put(filter);
 
459
}
 
460
 
 
461
/**
 
462
 * Call a callback for each filter attached to the qdisc
 
463
 * @arg qdisc           the parent qdisc
 
464
 * @arg cache           a filter cache including at least all the filters
 
465
 *                      attached to the specified qdisc
 
466
 * @arg cb              callback function
 
467
 * @arg arg             argument to be passed to callback function
 
468
 */
 
469
void rtnl_qdisc_foreach_cls(struct rtnl_qdisc *qdisc, struct nl_cache *cache,
 
470
                            void (*cb)(struct nl_object *, void *), void *arg)
 
471
{
 
472
        struct rtnl_cls *filter;
 
473
 
 
474
        filter = rtnl_cls_alloc();
 
475
        if (!filter)
 
476
                return;
 
477
 
 
478
        rtnl_tc_set_ifindex((struct rtnl_tc *) filter, qdisc->q_ifindex);
 
479
        rtnl_tc_set_parent((struct rtnl_tc *) filter, qdisc->q_parent);
 
480
 
 
481
        nl_cache_foreach_filter(cache, (struct nl_object *) filter, cb, arg);
 
482
        rtnl_cls_put(filter);
 
483
}
 
484
 
 
485
/** @} */
 
486
 
 
487
static void qdisc_dump_details(struct rtnl_tc *tc, struct nl_dump_params *p)
 
488
{
 
489
        struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) tc;
 
490
 
 
491
        nl_dump(p, "refcnt %u ", qdisc->q_info);
 
492
}
 
493
 
 
494
static struct rtnl_tc_type_ops qdisc_ops = {
 
495
        .tt_type                = RTNL_TC_TYPE_QDISC,
 
496
        .tt_dump_prefix         = "qdisc",
 
497
        .tt_dump = {
 
498
            [NL_DUMP_DETAILS]   = qdisc_dump_details,
 
499
        },
 
500
};
 
501
 
 
502
static struct nl_cache_ops rtnl_qdisc_ops = {
 
503
        .co_name                = "route/qdisc",
 
504
        .co_hdrsize             = sizeof(struct tcmsg),
 
505
        .co_msgtypes            = {
 
506
                                        { RTM_NEWQDISC, NL_ACT_NEW, "new" },
 
507
                                        { RTM_DELQDISC, NL_ACT_DEL, "del" },
 
508
                                        { RTM_GETQDISC, NL_ACT_GET, "get" },
 
509
                                        END_OF_MSGTYPES_LIST,
 
510
                                  },
 
511
        .co_protocol            = NETLINK_ROUTE,
 
512
        .co_request_update      = qdisc_request_update,
 
513
        .co_msg_parser          = qdisc_msg_parser,
 
514
        .co_obj_ops             = &qdisc_obj_ops,
 
515
};
 
516
 
 
517
struct nl_object_ops qdisc_obj_ops = {
 
518
        .oo_name                = "route/qdisc",
 
519
        .oo_size                = sizeof(struct rtnl_qdisc),
 
520
        .oo_free_data           = rtnl_tc_free_data,
 
521
        .oo_clone               = rtnl_tc_clone,
 
522
        .oo_dump = {
 
523
            [NL_DUMP_LINE]      = rtnl_tc_dump_line,
 
524
            [NL_DUMP_DETAILS]   = rtnl_tc_dump_details,
 
525
            [NL_DUMP_STATS]     = rtnl_tc_dump_stats,
 
526
        },
 
527
        .oo_compare             = rtnl_tc_compare,
 
528
        .oo_id_attrs            = (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE),
 
529
};
 
530
 
 
531
static void __init qdisc_init(void)
 
532
{
 
533
        rtnl_tc_type_register(&qdisc_ops);
 
534
        nl_cache_mngt_register(&rtnl_qdisc_ops);
 
535
}
 
536
 
 
537
static void __exit qdisc_exit(void)
 
538
{
 
539
        nl_cache_mngt_unregister(&rtnl_qdisc_ops);
 
540
        rtnl_tc_type_unregister(&qdisc_ops);
 
541
}
 
542
 
 
543
/** @} */