00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include <netlink-local.h>
00019 #include <netlink-tc.h>
00020 #include <netlink/netlink.h>
00021 #include <netlink/utils.h>
00022 #include <netlink/route/link.h>
00023 #include <netlink/route/tc-api.h>
00024 #include <netlink/route/qdisc.h>
00025 #include <netlink/route/class.h>
00026 #include <netlink/route/classifier.h>
00027
00028 static struct nl_cache_ops rtnl_qdisc_ops;
00029 static struct nl_object_ops qdisc_obj_ops;
00030
00031 static int qdisc_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
00032 struct nlmsghdr *n, struct nl_parser_param *pp)
00033 {
00034 struct rtnl_qdisc *qdisc;
00035 int err;
00036
00037 if (!(qdisc = rtnl_qdisc_alloc()))
00038 return -NLE_NOMEM;
00039
00040 if ((err = rtnl_tc_msg_parse(n, TC_CAST(qdisc))) < 0)
00041 goto errout;
00042
00043 err = pp->pp_cb(OBJ_CAST(qdisc), pp);
00044 errout:
00045 rtnl_qdisc_put(qdisc);
00046
00047 return err;
00048 }
00049
00050 static int qdisc_request_update(struct nl_cache *c, struct nl_sock *sk)
00051 {
00052 struct tcmsg tchdr = {
00053 .tcm_family = AF_UNSPEC,
00054 .tcm_ifindex = c->c_iarg1,
00055 };
00056
00057 return nl_send_simple(sk, RTM_GETQDISC, NLM_F_DUMP, &tchdr,
00058 sizeof(tchdr));
00059 }
00060
00061
00062
00063
00064
00065
00066 struct rtnl_qdisc *rtnl_qdisc_alloc(void)
00067 {
00068 struct rtnl_tc *tc;
00069
00070 tc = TC_CAST(nl_object_alloc(&qdisc_obj_ops));
00071 if (tc)
00072 tc->tc_type = RTNL_TC_TYPE_QDISC;
00073
00074 return (struct rtnl_qdisc *) tc;
00075 }
00076
00077 void rtnl_qdisc_put(struct rtnl_qdisc *qdisc)
00078 {
00079 nl_object_put((struct nl_object *) qdisc);
00080 }
00081
00082
00083
00084
00085
00086
00087
00088
00089 static int build_qdisc_msg(struct rtnl_qdisc *qdisc, int type, int flags,
00090 struct nl_msg **result)
00091 {
00092 if (!(qdisc->ce_mask & TCA_ATTR_IFINDEX)) {
00093 APPBUG("ifindex must be specified");
00094 return -NLE_MISSING_ATTR;
00095 }
00096
00097 return rtnl_tc_msg_build(TC_CAST(qdisc), type, flags, result);
00098 }
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114 int rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc, int flags,
00115 struct nl_msg **result)
00116 {
00117 if (!(qdisc->ce_mask & (TCA_ATTR_HANDLE | TCA_ATTR_PARENT))) {
00118 APPBUG("handle or parent must be specified");
00119 return -NLE_MISSING_ATTR;
00120 }
00121
00122 return build_qdisc_msg(qdisc, RTM_NEWQDISC, flags, result);
00123 }
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162 int rtnl_qdisc_add(struct nl_sock *sk, struct rtnl_qdisc *qdisc, int flags)
00163 {
00164 struct nl_msg *msg;
00165 int err;
00166
00167 if ((err = rtnl_qdisc_build_add_request(qdisc, flags, &msg)) < 0)
00168 return err;
00169
00170 return nl_send_sync(sk, msg);
00171 }
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188 int rtnl_qdisc_build_update_request(struct rtnl_qdisc *qdisc,
00189 struct rtnl_qdisc *new, int flags,
00190 struct nl_msg **result)
00191 {
00192 if (flags & (NLM_F_CREATE | NLM_F_EXCL)) {
00193 APPBUG("NLM_F_CREATE and NLM_F_EXCL may not be used here, "
00194 "use rtnl_qdisc_add()");
00195 return -NLE_INVAL;
00196 }
00197
00198 if (!(qdisc->ce_mask & TCA_ATTR_IFINDEX)) {
00199 APPBUG("ifindex must be specified");
00200 return -NLE_MISSING_ATTR;
00201 }
00202
00203 if (!(qdisc->ce_mask & (TCA_ATTR_HANDLE | TCA_ATTR_PARENT))) {
00204 APPBUG("handle or parent must be specified");
00205 return -NLE_MISSING_ATTR;
00206 }
00207
00208 rtnl_tc_set_ifindex(TC_CAST(new), qdisc->q_ifindex);
00209
00210 if (qdisc->ce_mask & TCA_ATTR_HANDLE)
00211 rtnl_tc_set_handle(TC_CAST(new), qdisc->q_handle);
00212
00213 if (qdisc->ce_mask & TCA_ATTR_PARENT)
00214 rtnl_tc_set_parent(TC_CAST(new), qdisc->q_parent);
00215
00216 return build_qdisc_msg(new, RTM_NEWQDISC, flags, result);
00217 }
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247 int rtnl_qdisc_update(struct nl_sock *sk, struct rtnl_qdisc *qdisc,
00248 struct rtnl_qdisc *new, int flags)
00249 {
00250 struct nl_msg *msg;
00251 int err;
00252
00253 err = rtnl_qdisc_build_update_request(qdisc, new, flags, &msg);
00254 if (err < 0)
00255 return err;
00256
00257 return nl_send_sync(sk, msg);
00258 }
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273 int rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc,
00274 struct nl_msg **result)
00275 {
00276 struct nl_msg *msg;
00277 struct tcmsg tchdr;
00278 int required = TCA_ATTR_IFINDEX | TCA_ATTR_PARENT;
00279
00280 if ((qdisc->ce_mask & required) != required) {
00281 APPBUG("ifindex and parent must be specified");
00282 return -NLE_MISSING_ATTR;
00283 }
00284
00285 if (!(msg = nlmsg_alloc_simple(RTM_DELQDISC, 0)))
00286 return -NLE_NOMEM;
00287
00288 memset(&tchdr, 0, sizeof(tchdr));
00289
00290 tchdr.tcm_family = AF_UNSPEC;
00291 tchdr.tcm_ifindex = qdisc->q_ifindex;
00292 tchdr.tcm_parent = qdisc->q_parent;
00293
00294 if (qdisc->ce_mask & TCA_ATTR_HANDLE)
00295 tchdr.tcm_handle = qdisc->q_handle;
00296
00297 if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0)
00298 goto nla_put_failure;
00299
00300 if (qdisc->ce_mask & TCA_ATTR_KIND)
00301 NLA_PUT_STRING(msg, TCA_KIND, qdisc->q_kind);
00302
00303 *result = msg;
00304 return 0;
00305
00306 nla_put_failure:
00307 nlmsg_free(msg);
00308 return -NLE_MSGSIZE;
00309 }
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340 int rtnl_qdisc_delete(struct nl_sock *sk, struct rtnl_qdisc *qdisc)
00341 {
00342 struct nl_msg *msg;
00343 int err;
00344
00345 if ((err = rtnl_qdisc_build_delete_request(qdisc, &msg)) < 0)
00346 return err;
00347
00348 return nl_send_sync(sk, msg);
00349 }
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367
00368 int rtnl_qdisc_alloc_cache(struct nl_sock *sk, struct nl_cache **result)
00369 {
00370 return nl_cache_alloc_and_fill(&rtnl_qdisc_ops, sk, result);
00371 }
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387 struct rtnl_qdisc *rtnl_qdisc_get_by_parent(struct nl_cache *cache,
00388 int ifindex, uint32_t parent)
00389 {
00390 struct rtnl_qdisc *q;
00391
00392 if (cache->c_ops != &rtnl_qdisc_ops)
00393 return NULL;
00394
00395 nl_list_for_each_entry(q, &cache->c_items, ce_list) {
00396 if (q->q_parent == parent && q->q_ifindex == ifindex) {
00397 nl_object_get((struct nl_object *) q);
00398 return q;
00399 }
00400 }
00401
00402 return NULL;
00403 }
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419 struct rtnl_qdisc *rtnl_qdisc_get(struct nl_cache *cache, int ifindex,
00420 uint32_t handle)
00421 {
00422 struct rtnl_qdisc *q;
00423
00424 if (cache->c_ops != &rtnl_qdisc_ops)
00425 return NULL;
00426
00427 nl_list_for_each_entry(q, &cache->c_items, ce_list) {
00428 if (q->q_handle == handle && q->q_ifindex == ifindex) {
00429 nl_object_get((struct nl_object *) q);
00430 return q;
00431 }
00432 }
00433
00434 return NULL;
00435 }
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450 void rtnl_qdisc_foreach_child(struct rtnl_qdisc *qdisc, struct nl_cache *cache,
00451 void (*cb)(struct nl_object *, void *), void *arg)
00452 {
00453 struct rtnl_class *filter;
00454
00455 filter = rtnl_class_alloc();
00456 if (!filter)
00457 return;
00458
00459 rtnl_tc_set_parent(TC_CAST(filter), qdisc->q_handle);
00460 rtnl_tc_set_ifindex(TC_CAST(filter), qdisc->q_ifindex);
00461 rtnl_tc_set_kind(TC_CAST(filter), qdisc->q_kind);
00462
00463 nl_cache_foreach_filter(cache, OBJ_CAST(filter), cb, arg);
00464
00465 rtnl_class_put(filter);
00466 }
00467
00468
00469
00470
00471
00472
00473
00474 void rtnl_qdisc_foreach_cls(struct rtnl_qdisc *qdisc, struct nl_cache *cache,
00475 void (*cb)(struct nl_object *, void *), void *arg)
00476 {
00477 struct rtnl_cls *filter;
00478
00479 if (!(filter = rtnl_cls_alloc()))
00480 return;
00481
00482 rtnl_tc_set_ifindex(TC_CAST(filter), qdisc->q_ifindex);
00483 rtnl_tc_set_parent(TC_CAST(filter), qdisc->q_parent);
00484
00485 nl_cache_foreach_filter(cache, OBJ_CAST(filter), cb, arg);
00486 rtnl_cls_put(filter);
00487 }
00488
00489
00490
00491
00492
00493
00494
00495
00496 int rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc,
00497 struct rtnl_qdisc *new,
00498 struct nl_msg **result)
00499 {
00500 return rtnl_qdisc_build_update_request(qdisc, new, NLM_F_REPLACE,
00501 result);
00502 }
00503
00504
00505
00506
00507
00508
00509
00510
00511 int rtnl_qdisc_change(struct nl_sock *sk, struct rtnl_qdisc *qdisc,
00512 struct rtnl_qdisc *new)
00513 {
00514 return rtnl_qdisc_update(sk, qdisc, new, NLM_F_REPLACE);
00515 }
00516
00517
00518
00519 static void qdisc_dump_details(struct rtnl_tc *tc, struct nl_dump_params *p)
00520 {
00521 struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) tc;
00522
00523 nl_dump(p, "refcnt %u ", qdisc->q_info);
00524 }
00525
00526 static struct rtnl_tc_type_ops qdisc_ops = {
00527 .tt_type = RTNL_TC_TYPE_QDISC,
00528 .tt_dump_prefix = "qdisc",
00529 .tt_dump = {
00530 [NL_DUMP_DETAILS] = qdisc_dump_details,
00531 },
00532 };
00533
00534 static struct nl_cache_ops rtnl_qdisc_ops = {
00535 .co_name = "route/qdisc",
00536 .co_hdrsize = sizeof(struct tcmsg),
00537 .co_msgtypes = {
00538 { RTM_NEWQDISC, NL_ACT_NEW, "new" },
00539 { RTM_DELQDISC, NL_ACT_DEL, "del" },
00540 { RTM_GETQDISC, NL_ACT_GET, "get" },
00541 END_OF_MSGTYPES_LIST,
00542 },
00543 .co_protocol = NETLINK_ROUTE,
00544 .co_request_update = qdisc_request_update,
00545 .co_msg_parser = qdisc_msg_parser,
00546 .co_obj_ops = &qdisc_obj_ops,
00547 };
00548
00549 static struct nl_object_ops qdisc_obj_ops = {
00550 .oo_name = "route/qdisc",
00551 .oo_size = sizeof(struct rtnl_qdisc),
00552 .oo_free_data = rtnl_tc_free_data,
00553 .oo_clone = rtnl_tc_clone,
00554 .oo_dump = {
00555 [NL_DUMP_LINE] = rtnl_tc_dump_line,
00556 [NL_DUMP_DETAILS] = rtnl_tc_dump_details,
00557 [NL_DUMP_STATS] = rtnl_tc_dump_stats,
00558 },
00559 .oo_compare = rtnl_tc_compare,
00560 .oo_id_attrs = (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE),
00561 };
00562
00563 static void __init qdisc_init(void)
00564 {
00565 rtnl_tc_type_register(&qdisc_ops);
00566 nl_cache_mngt_register(&rtnl_qdisc_ops);
00567 }
00568
00569 static void __exit qdisc_exit(void)
00570 {
00571 nl_cache_mngt_unregister(&rtnl_qdisc_ops);
00572 rtnl_tc_type_unregister(&qdisc_ops);
00573 }
00574
00575