00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085 #include <netlink-local.h>
00086 #include <netlink-tc.h>
00087 #include <netlink/netlink.h>
00088 #include <netlink/utils.h>
00089 #include <netlink/route/link.h>
00090 #include <netlink/route/tc.h>
00091 #include <netlink/route/qdisc.h>
00092 #include <netlink/route/class.h>
00093 #include <netlink/route/classifier.h>
00094 #include <netlink/route/qdisc-modules.h>
00095
00096
00097 static struct nl_cache_ops rtnl_qdisc_ops;
00098
00099
00100 static struct rtnl_qdisc_ops *qdisc_ops_list;
00101
00102 static struct rtnl_qdisc_ops *qdisc_lookup_ops(const char *kind)
00103 {
00104 struct rtnl_qdisc_ops *ops;
00105
00106 for (ops = qdisc_ops_list; ops; ops = ops->qo_next)
00107 if (!strcmp(kind, ops->qo_kind))
00108 return ops;
00109
00110 return NULL;
00111 }
00112
00113 static inline struct rtnl_qdisc_ops *qdisc_ops(struct rtnl_qdisc *qdisc)
00114 {
00115 if (!qdisc->q_ops)
00116 qdisc->q_ops = qdisc_lookup_ops(qdisc->q_kind);
00117
00118 return qdisc->q_ops;
00119 }
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130 int rtnl_qdisc_register(struct rtnl_qdisc_ops *ops)
00131 {
00132 struct rtnl_qdisc_ops *o, **op;
00133
00134 if (!ops->qo_kind[0])
00135 BUG();
00136
00137 for (op = &qdisc_ops_list; (o = *op) != NULL; op = &o->qo_next)
00138 if (!strcasecmp(ops->qo_kind, o->qo_kind))
00139 return nl_errno(EEXIST);
00140
00141 ops->qo_next = NULL;
00142 *op = ops;
00143
00144 return 0;
00145 }
00146
00147
00148
00149
00150
00151 int rtnl_qdisc_unregister(struct rtnl_qdisc_ops *ops)
00152 {
00153 struct rtnl_qdisc_ops *o, **op;
00154
00155 for (op = &qdisc_ops_list; (o = *op) != NULL; op = &o->qo_next)
00156 if (!strcasecmp(ops->qo_kind, o->qo_kind))
00157 break;
00158
00159 if (!o)
00160 return nl_errno(ENOENT);
00161
00162 *op = ops->qo_next;
00163
00164 return 0;
00165 }
00166
00167
00168
00169 static int qdisc_msg_parser(struct sockaddr_nl *who, struct nlmsghdr *n,
00170 void *arg)
00171 {
00172 int err = -ENOMEM;
00173 struct nl_parser_param *pp = arg;
00174 struct rtnl_qdisc *qdisc;
00175 struct rtnl_qdisc_ops *ops;
00176
00177 qdisc = rtnl_qdisc_alloc();
00178 if (!qdisc) {
00179 err = nl_errno(ENOMEM);
00180 goto errout;
00181 }
00182
00183 qdisc->ce_msgtype = n->nlmsg_type;
00184
00185 err = tca_msg_parser(n, (struct rtnl_tca *) qdisc);
00186 if (err < 0)
00187 goto errout_free;
00188
00189 ops = qdisc_ops(qdisc);
00190 if (ops && ops->qo_msg_parser) {
00191 err = ops->qo_msg_parser(qdisc);
00192 if (err < 0)
00193 goto errout_free;
00194 }
00195
00196 err = pp->pp_cb((struct nl_object *) qdisc, pp);
00197 if (err < 0)
00198 goto errout_free;
00199
00200 return P_ACCEPT;
00201
00202 errout_free:
00203 rtnl_qdisc_put(qdisc);
00204 errout:
00205 return err;
00206 }
00207
00208 static int qdisc_request_update(struct nl_cache *c, struct nl_handle *h)
00209 {
00210 struct tcmsg tchdr = {
00211 .tcm_family = AF_UNSPEC,
00212 .tcm_ifindex = c->c_iarg1,
00213 };
00214
00215 return nl_send_simple(h, RTM_GETQDISC, NLM_F_DUMP, &tchdr,
00216 sizeof(tchdr));
00217 }
00218
00219 static void qdisc_free_data(struct nl_object *obj)
00220 {
00221 struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) obj;
00222 struct rtnl_qdisc_ops *ops;
00223
00224 tca_free_data((struct rtnl_tca *) qdisc);
00225
00226 ops = qdisc_ops(qdisc);
00227 if (ops && ops->qo_free_data)
00228 ops->qo_free_data(qdisc);
00229 }
00230
00231 static int qdisc_dump_brief(struct nl_object *obj, struct nl_dump_params *p)
00232 {
00233 struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) obj;
00234 struct rtnl_qdisc_ops *ops;
00235
00236 int line = tca_dump_brief((struct rtnl_tca *) qdisc, "qdisc", p, 0);
00237
00238 ops = qdisc_ops(qdisc);
00239 if (ops && ops->qo_dump[NL_DUMP_BRIEF])
00240 line = ops->qo_dump[NL_DUMP_BRIEF](qdisc, p, line);
00241
00242 dp_dump(p, "\n");
00243
00244 return line;
00245 }
00246
00247 static int qdisc_dump_full(struct nl_object *arg, struct nl_dump_params *p)
00248 {
00249 struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) arg;
00250 struct rtnl_qdisc_ops *ops;
00251
00252 int line = qdisc_dump_brief(arg, p);
00253
00254 line = tca_dump_full((struct rtnl_tca *) qdisc, p, line);
00255 dp_dump(p, "refcnt %u ", qdisc->q_info);
00256
00257 ops = qdisc_ops(qdisc);
00258 if (ops && ops->qo_dump[NL_DUMP_FULL])
00259 line = ops->qo_dump[NL_DUMP_FULL](qdisc, p, line);
00260
00261 dp_dump(p, "\n");
00262 return line;
00263 }
00264
00265 static int qdisc_dump_stats(struct nl_object *arg, struct nl_dump_params *p)
00266 {
00267 struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) arg;
00268 struct rtnl_qdisc_ops *ops;
00269
00270 int line = qdisc_dump_full(arg, p);
00271 line = tca_dump_stats((struct rtnl_tca *) qdisc, p, line );
00272 dp_dump(p, "\n");
00273
00274 ops = qdisc_ops(qdisc);
00275 if (ops && ops->qo_dump[NL_DUMP_STATS])
00276 line = ops->qo_dump[NL_DUMP_STATS](qdisc, p, line);
00277
00278 return line;
00279 }
00280
00281 static int qdisc_filter(struct nl_object *obj, struct nl_object *filter)
00282 {
00283 return tca_filter((struct rtnl_tca *) obj, (struct rtnl_tca *) filter);
00284 }
00285
00286
00287
00288
00289
00290
00291 static struct nl_msg *qdisc_build(struct rtnl_qdisc *qdisc, int type, int flags)
00292 {
00293 struct rtnl_qdisc_ops *ops;
00294 struct nl_msg *msg;
00295 int err;
00296
00297 msg = tca_build_msg((struct rtnl_tca *) qdisc, type, flags);
00298 if (!msg)
00299 goto errout;
00300
00301 ops = qdisc_ops(qdisc);
00302 if (ops && ops->qo_get_opts) {
00303 struct nl_msg *opts;
00304
00305 opts = ops->qo_get_opts(qdisc);
00306 if (opts) {
00307 err = nla_put_nested(msg, TCA_OPTIONS, opts);
00308 nlmsg_free(opts);
00309 if (err < 0)
00310 goto errout;
00311 }
00312 }
00313
00314 return msg;
00315 errout:
00316 nlmsg_free(msg);
00317
00318 return NULL;
00319 }
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336 struct nl_msg *rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc,
00337 int flags)
00338 {
00339 struct nl_msg *msg;
00340
00341 msg = qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_CREATE | flags);
00342 if (!msg)
00343 nl_errno(ENOMEM);
00344
00345 return msg;
00346 }
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363 int rtnl_qdisc_add(struct nl_handle *handle, struct rtnl_qdisc *qdisc,
00364 int flags)
00365 {
00366 struct nl_msg *msg;
00367 int err;
00368
00369 msg = rtnl_qdisc_build_add_request(qdisc, flags);
00370 if (!msg)
00371 return nl_errno(ENOMEM);
00372
00373 err = nl_send_auto_complete(handle, msg);
00374 if (err < 0)
00375 return err;
00376
00377 nlmsg_free(msg);
00378 return nl_wait_for_ack(handle);
00379 }
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400 struct nl_msg *rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc,
00401 struct rtnl_qdisc *new)
00402 {
00403 return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_REPLACE);
00404 }
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418 int rtnl_qdisc_change(struct nl_handle *handle, struct rtnl_qdisc *qdisc,
00419 struct rtnl_qdisc *new)
00420 {
00421 struct nl_msg *msg;
00422 int err;
00423
00424 msg = rtnl_qdisc_build_change_request(qdisc, new);
00425 if (!msg)
00426 return nl_errno(ENOMEM);
00427
00428 err = nl_send_auto_complete(handle, msg);
00429 if (err < 0)
00430 return err;
00431
00432 nlmsg_free(msg);
00433 return nl_wait_for_ack(handle);
00434 }
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450
00451
00452
00453
00454 struct nl_msg *rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc)
00455 {
00456 struct nl_msg *msg;
00457 struct tcmsg tchdr;
00458 int required = TCA_ATTR_IFINDEX | TCA_ATTR_PARENT;
00459
00460 if ((qdisc->q_mask & required) != required)
00461 BUG();
00462
00463 msg = nlmsg_build_simple(RTM_DELQDISC, 0);
00464 if (!msg)
00465 return NULL;
00466
00467 tchdr.tcm_family = AF_UNSPEC,
00468 tchdr.tcm_handle = qdisc->q_handle,
00469 tchdr.tcm_parent = qdisc->q_parent,
00470 tchdr.tcm_ifindex = qdisc->q_ifindex,
00471 nlmsg_append(msg, &tchdr, sizeof(tchdr), 1);
00472
00473 return msg;
00474 }
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486
00487 int rtnl_qdisc_delete(struct nl_handle *handle, struct rtnl_qdisc *qdisc)
00488 {
00489 struct nl_msg *msg;
00490 int err;
00491
00492 msg = rtnl_qdisc_build_delete_request(qdisc);
00493 if (!msg)
00494 return nl_errno(ENOMEM);
00495
00496 err = nl_send_auto_complete(handle, msg);
00497 if (err < 0)
00498 return err;
00499
00500 nlmsg_free(msg);
00501 return nl_wait_for_ack(handle);
00502 }
00503
00504
00505
00506
00507
00508
00509
00510
00511
00512
00513
00514
00515 struct rtnl_qdisc *rtnl_qdisc_alloc(void)
00516 {
00517 return (struct rtnl_qdisc *) nl_object_alloc_from_ops(&rtnl_qdisc_ops);
00518 }
00519
00520
00521
00522
00523
00524
00525
00526
00527 void rtnl_qdisc_put(struct rtnl_qdisc *qdisc)
00528 {
00529 nl_object_put((struct nl_object *) qdisc);
00530 }
00531
00532
00533
00534
00535
00536
00537
00538 void rtnl_qdisc_free(struct rtnl_qdisc *qdisc)
00539 {
00540 nl_object_free((struct nl_object *) qdisc);
00541 }
00542
00543
00544
00545
00546
00547
00548
00549
00550
00551
00552
00553
00554
00555
00556
00557
00558
00559
00560
00561
00562 struct nl_cache * rtnl_qdisc_alloc_cache(struct nl_handle *handle)
00563 {
00564 struct nl_cache * cache;
00565
00566 cache = nl_cache_alloc_from_ops(&rtnl_qdisc_ops);
00567 if (cache == NULL)
00568 return NULL;
00569
00570 if (nl_cache_update(handle, cache) < 0) {
00571 nl_cache_free(cache);
00572 return NULL;
00573 }
00574
00575 return cache;
00576 }
00577
00578
00579
00580
00581
00582
00583
00584
00585 struct rtnl_qdisc * rtnl_qdisc_get_by_parent(struct nl_cache *cache,
00586 int ifindex, uint32_t parent)
00587 {
00588 struct rtnl_qdisc *q;
00589
00590 if (cache->c_ops != &rtnl_qdisc_ops)
00591 return NULL;
00592
00593 nl_list_for_each_entry(q, &cache->c_items, ce_list) {
00594 if (q->q_parent == parent && q->q_ifindex == ifindex) {
00595 nl_object_get((struct nl_object *) q);
00596 return q;
00597 }
00598 }
00599
00600 return NULL;
00601 }
00602
00603
00604
00605
00606
00607
00608
00609
00610 struct rtnl_qdisc * rtnl_qdisc_get(struct nl_cache *cache,
00611 int ifindex, uint32_t handle)
00612 {
00613 struct rtnl_qdisc *q;
00614
00615 if (cache->c_ops != &rtnl_qdisc_ops)
00616 return NULL;
00617
00618 nl_list_for_each_entry(q, &cache->c_items, ce_list) {
00619 if (q->q_handle == handle && q->q_ifindex == ifindex) {
00620 nl_object_get((struct nl_object *) q);
00621 return q;
00622 }
00623 }
00624
00625 return NULL;
00626 }
00627
00628
00629
00630
00631
00632
00633
00634
00635
00636
00637
00638
00639
00640
00641 struct nl_msg *rtnl_qdisc_get_opts(struct rtnl_qdisc *qdisc)
00642 {
00643 struct rtnl_qdisc_ops *ops;
00644
00645 ops = qdisc_ops(qdisc);
00646 if (ops && ops->qo_get_opts)
00647 return ops->qo_get_opts(qdisc);
00648
00649 return NULL;
00650 }
00651
00652
00653
00654
00655
00656
00657
00658
00659
00660
00661
00662
00663
00664
00665
00666
00667 void rtnl_qdisc_foreach_child(struct rtnl_qdisc *qdisc, struct nl_cache *cache,
00668 void (*cb)(struct nl_object *, void *), void *arg)
00669 {
00670 struct rtnl_class *filter;
00671
00672 filter = rtnl_class_alloc();
00673 if (!filter)
00674 return;
00675
00676 rtnl_class_set_parent(filter, qdisc->q_handle);
00677 rtnl_class_set_ifindex(filter, qdisc->q_ifindex);
00678 rtnl_class_set_kind(filter, qdisc->q_kind);
00679
00680 nl_cache_foreach_filter(cache, (struct nl_object *) filter, cb, arg);
00681
00682 rtnl_class_put(filter);
00683 }
00684
00685
00686
00687
00688
00689
00690
00691
00692
00693 void rtnl_qdisc_foreach_cls(struct rtnl_qdisc *qdisc, struct nl_cache *cache,
00694 void (*cb)(struct nl_object *, void *), void *arg)
00695 {
00696 struct rtnl_cls *filter;
00697
00698 filter = rtnl_cls_alloc();
00699 if (!filter)
00700 return;
00701
00702 rtnl_cls_set_ifindex(filter, qdisc->q_ifindex);
00703 rtnl_cls_set_parent(filter, qdisc->q_parent);
00704
00705 nl_cache_foreach_filter(cache, (struct nl_object *) filter, cb, arg);
00706 rtnl_cls_put(filter);
00707 }
00708
00709
00710
00711
00712
00713
00714
00715
00716
00717
00718
00719
00720
00721 void rtnl_qdisc_set_ifindex(struct rtnl_qdisc *qdisc, int ifindex)
00722 {
00723 tca_set_ifindex((struct rtnl_tca *) qdisc, ifindex);
00724 }
00725
00726
00727
00728
00729
00730
00731 int rtnl_qdisc_get_ifindex(struct rtnl_qdisc *qdisc)
00732 {
00733 return tca_get_ifindex((struct rtnl_tca *) qdisc);
00734 }
00735
00736
00737
00738
00739
00740
00741 void rtnl_qdisc_set_handle(struct rtnl_qdisc *qdisc, uint32_t handle)
00742 {
00743 tca_set_handle((struct rtnl_tca *) qdisc, handle);
00744 }
00745
00746
00747
00748
00749
00750
00751 uint32_t rtnl_qdisc_get_handle(struct rtnl_qdisc *qdisc)
00752 {
00753 return tca_get_handle((struct rtnl_tca *) qdisc);
00754 }
00755
00756
00757
00758
00759
00760
00761 void rtnl_qdisc_set_parent(struct rtnl_qdisc *qdisc, uint32_t parent)
00762 {
00763 tca_set_parent((struct rtnl_tca *) qdisc, parent);
00764 }
00765
00766
00767
00768
00769
00770
00771 uint32_t rtnl_qdisc_get_parent(struct rtnl_qdisc *qdisc)
00772 {
00773 return tca_get_parent((struct rtnl_tca *) qdisc);
00774 }
00775
00776
00777
00778
00779
00780
00781 void rtnl_qdisc_set_kind(struct rtnl_qdisc *qdisc, const char *name)
00782 {
00783 tca_set_kind((struct rtnl_tca *) qdisc, name);
00784 qdisc->q_ops = qdisc_lookup_ops(name);
00785 }
00786
00787
00788
00789
00790
00791
00792 char *rtnl_qdisc_get_kind(struct rtnl_qdisc *qdisc)
00793 {
00794 return tca_get_kind((struct rtnl_tca *) qdisc);
00795 }
00796
00797
00798
00799
00800
00801
00802
00803 uint64_t rtnl_qdisc_get_stat(struct rtnl_qdisc *qdisc,
00804 enum rtnl_tc_stats_id id)
00805 {
00806 return tca_get_stat((struct rtnl_tca *) qdisc, id);
00807 }
00808
00809
00810
00811 static struct nl_cache_ops rtnl_qdisc_ops = {
00812 .co_name = "route/qdisc",
00813 .co_size = sizeof(struct rtnl_qdisc),
00814 .co_hdrsize = sizeof(struct tcmsg),
00815 .co_msgtypes = {
00816 { RTM_NEWQDISC, "new" },
00817 { RTM_DELQDISC, "delete" },
00818 { RTM_GETQDISC, "get" },
00819 { -1, NULL },
00820 },
00821 .co_protocol = NETLINK_ROUTE,
00822 .co_request_update = qdisc_request_update,
00823 .co_msg_parser = qdisc_msg_parser,
00824 .co_free_data = qdisc_free_data,
00825 .co_dump[NL_DUMP_BRIEF] = qdisc_dump_brief,
00826 .co_dump[NL_DUMP_FULL] = qdisc_dump_full,
00827 .co_dump[NL_DUMP_STATS] = qdisc_dump_stats,
00828 .co_filter = qdisc_filter,
00829 };
00830
00831 static void __init qdisc_init(void)
00832 {
00833 nl_cache_mngt_register(&rtnl_qdisc_ops);
00834 }
00835
00836 static void __exit qdisc_exit(void)
00837 {
00838 nl_cache_mngt_unregister(&rtnl_qdisc_ops);
00839 }
00840
00841