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 #include <netlink-local.h>
00044 #include <netlink/netlink.h>
00045 #include <netlink/cache.h>
00046 #include <netlink/object.h>
00047 #include <netlink/utils.h>
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058 int nl_cache_nitems(struct nl_cache *cache)
00059 {
00060 return cache->c_nitems;
00061 }
00062
00063
00064
00065
00066
00067
00068 int nl_cache_nitems_filter(struct nl_cache *cache, struct nl_object *filter)
00069 {
00070 struct nl_object *obj;
00071 int nitems = 0;
00072
00073 if (cache->c_ops == NULL)
00074 BUG();
00075
00076 nl_list_for_each_entry(obj, &cache->c_items, ce_list) {
00077 if (filter && !nl_object_match_filter(obj, filter))
00078 continue;
00079
00080 nitems++;
00081 }
00082
00083 return nitems;
00084 }
00085
00086
00087
00088
00089
00090
00091 int nl_cache_is_empty(struct nl_cache *cache)
00092 {
00093 return nl_list_empty(&cache->c_items);
00094 }
00095
00096
00097
00098
00099
00100 struct nl_cache_ops *nl_cache_get_ops(struct nl_cache *cache)
00101 {
00102 return cache->c_ops;
00103 }
00104
00105
00106
00107
00108
00109 struct nl_object *nl_cache_get_first(struct nl_cache *cache)
00110 {
00111 if (nl_list_empty(&cache->c_items))
00112 return NULL;
00113
00114 return nl_list_entry(cache->c_items.next,
00115 struct nl_object, ce_list);
00116 }
00117
00118
00119
00120
00121
00122 struct nl_object *nl_cache_get_last(struct nl_cache *cache)
00123 {
00124 if (nl_list_empty(&cache->c_items))
00125 return NULL;
00126
00127 return nl_list_entry(cache->c_items.prev,
00128 struct nl_object, ce_list);
00129 }
00130
00131
00132
00133
00134
00135 struct nl_object *nl_cache_get_next(struct nl_object *obj)
00136 {
00137 if (nl_list_at_tail(obj, &obj->ce_cache->c_items, ce_list))
00138 return NULL;
00139 else
00140 return nl_list_entry(obj->ce_list.next,
00141 struct nl_object, ce_list);
00142 }
00143
00144
00145
00146
00147
00148 struct nl_object *nl_cache_get_prev(struct nl_object *obj)
00149 {
00150 if (nl_list_at_head(obj, &obj->ce_cache->c_items, ce_list))
00151 return NULL;
00152 else
00153 return nl_list_entry(obj->ce_list.prev,
00154 struct nl_object, ce_list);
00155 }
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173 struct nl_cache *nl_cache_alloc(struct nl_cache_ops *ops)
00174 {
00175 struct nl_cache *cache;
00176
00177 cache = calloc(1, sizeof(*cache));
00178 if (!cache)
00179 return NULL;
00180
00181 nl_init_list_head(&cache->c_items);
00182 cache->c_ops = ops;
00183
00184 NL_DBG(2, "Allocated cache %p <%s>.\n", cache, nl_cache_name(cache));
00185
00186 return cache;
00187 }
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205 int nl_cache_alloc_and_fill(struct nl_cache_ops *ops, struct nl_sock *sock,
00206 struct nl_cache **result)
00207 {
00208 struct nl_cache *cache;
00209 int err;
00210
00211 if (!(cache = nl_cache_alloc(ops)))
00212 return -NLE_NOMEM;
00213
00214 if (sock && (err = nl_cache_refill(sock, cache)) < 0) {
00215 nl_cache_free(cache);
00216 return err;
00217 }
00218
00219 *result = cache;
00220 return 0;
00221 }
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236 int nl_cache_alloc_name(const char *kind, struct nl_cache **result)
00237 {
00238 struct nl_cache_ops *ops;
00239 struct nl_cache *cache;
00240
00241 ops = nl_cache_ops_lookup(kind);
00242 if (!ops)
00243 return -NLE_NOCACHE;
00244
00245 if (!(cache = nl_cache_alloc(ops)))
00246 return -NLE_NOMEM;
00247
00248 *result = cache;
00249 return 0;
00250 }
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267 struct nl_cache *nl_cache_subset(struct nl_cache *orig,
00268 struct nl_object *filter)
00269 {
00270 struct nl_cache *cache;
00271 struct nl_object *obj;
00272
00273 if (!filter)
00274 BUG();
00275
00276 cache = nl_cache_alloc(orig->c_ops);
00277 if (!cache)
00278 return NULL;
00279
00280 nl_list_for_each_entry(obj, &orig->c_items, ce_list) {
00281 if (!nl_object_match_filter(obj, filter))
00282 continue;
00283
00284 nl_cache_add(cache, obj);
00285 }
00286
00287 return cache;
00288 }
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301 void nl_cache_clear(struct nl_cache *cache)
00302 {
00303 struct nl_object *obj, *tmp;
00304
00305 NL_DBG(1, "Clearing cache %p <%s>...\n", cache, nl_cache_name(cache));
00306
00307 nl_list_for_each_entry_safe(obj, tmp, &cache->c_items, ce_list)
00308 nl_cache_remove(obj);
00309 }
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320 void nl_cache_free(struct nl_cache *cache)
00321 {
00322 if (!cache)
00323 return;
00324
00325 nl_cache_clear(cache);
00326 NL_DBG(1, "Freeing cache %p <%s>...\n", cache, nl_cache_name(cache));
00327 free(cache);
00328 }
00329
00330
00331
00332
00333
00334
00335
00336
00337 static int __cache_add(struct nl_cache *cache, struct nl_object *obj)
00338 {
00339 obj->ce_cache = cache;
00340
00341 nl_list_add_tail(&obj->ce_list, &cache->c_items);
00342 cache->c_nitems++;
00343
00344 NL_DBG(1, "Added %p to cache %p <%s>.\n",
00345 obj, cache, nl_cache_name(cache));
00346
00347 return 0;
00348 }
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372 int nl_cache_add(struct nl_cache *cache, struct nl_object *obj)
00373 {
00374 struct nl_object *new;
00375
00376 if (cache->c_ops->co_obj_ops != obj->ce_ops)
00377 return -NLE_OBJ_MISMATCH;
00378
00379 if (!nl_list_empty(&obj->ce_list)) {
00380 new = nl_object_clone(obj);
00381 if (!new)
00382 return -NLE_NOMEM;
00383 } else {
00384 nl_object_get(obj);
00385 new = obj;
00386 }
00387
00388 return __cache_add(cache, new);
00389 }
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408
00409 int nl_cache_move(struct nl_cache *cache, struct nl_object *obj)
00410 {
00411 if (cache->c_ops->co_obj_ops != obj->ce_ops)
00412 return -NLE_OBJ_MISMATCH;
00413
00414 NL_DBG(3, "Moving object %p to cache %p\n", obj, cache);
00415
00416
00417
00418 nl_object_get(obj);
00419
00420 if (!nl_list_empty(&obj->ce_list))
00421 nl_cache_remove(obj);
00422
00423 return __cache_add(cache, obj);
00424 }
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436 void nl_cache_remove(struct nl_object *obj)
00437 {
00438 struct nl_cache *cache = obj->ce_cache;
00439
00440 if (cache == NULL)
00441 return;
00442
00443 nl_list_del(&obj->ce_list);
00444 obj->ce_cache = NULL;
00445 nl_object_put(obj);
00446 cache->c_nitems--;
00447
00448 NL_DBG(1, "Deleted %p from cache %p <%s>.\n",
00449 obj, cache, nl_cache_name(cache));
00450 }
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467 void nl_cache_set_arg1(struct nl_cache *cache, int arg)
00468 {
00469 cache->c_iarg1 = arg;
00470 }
00471
00472
00473
00474
00475
00476
00477
00478
00479
00480 void nl_cache_set_arg2(struct nl_cache *cache, int arg)
00481 {
00482 cache->c_iarg2 = arg;
00483 }
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505
00506
00507
00508
00509 static int nl_cache_request_full_dump(struct nl_sock *sk,
00510 struct nl_cache *cache)
00511 {
00512 NL_DBG(2, "Requesting dump from kernel for cache %p <%s>...\n",
00513 cache, nl_cache_name(cache));
00514
00515 if (cache->c_ops->co_request_update == NULL)
00516 return -NLE_OPNOTSUPP;
00517
00518 return cache->c_ops->co_request_update(cache, sk);
00519 }
00520
00521
00522 struct update_xdata {
00523 struct nl_cache_ops *ops;
00524 struct nl_parser_param *params;
00525 };
00526
00527 static int update_msg_parser(struct nl_msg *msg, void *arg)
00528 {
00529 struct update_xdata *x = arg;
00530
00531 return nl_cache_parse(x->ops, &msg->nm_src, msg->nm_nlh, x->params);
00532 }
00533
00534
00535
00536
00537
00538
00539
00540
00541 static int __cache_pickup(struct nl_sock *sk, struct nl_cache *cache,
00542 struct nl_parser_param *param)
00543 {
00544 int err;
00545 struct nl_cb *cb;
00546 struct update_xdata x = {
00547 .ops = cache->c_ops,
00548 .params = param,
00549 };
00550
00551 NL_DBG(1, "Picking up answer for cache %p <%s>...\n",
00552 cache, nl_cache_name(cache));
00553
00554 cb = nl_cb_clone(sk->s_cb);
00555 if (cb == NULL)
00556 return -NLE_NOMEM;
00557
00558 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, update_msg_parser, &x);
00559
00560 err = nl_recvmsgs(sk, cb);
00561 if (err < 0)
00562 NL_DBG(2, "While picking up for %p <%s>, recvmsgs() returned " \
00563 "%d: %s", cache, nl_cache_name(cache),
00564 err, nl_geterror(err));
00565
00566 nl_cb_put(cb);
00567
00568 return err;
00569 }
00570
00571 static int pickup_cb(struct nl_object *c, struct nl_parser_param *p)
00572 {
00573 return nl_cache_add((struct nl_cache *) p->pp_arg, c);
00574 }
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584
00585
00586 int nl_cache_pickup(struct nl_sock *sk, struct nl_cache *cache)
00587 {
00588 struct nl_parser_param p = {
00589 .pp_cb = pickup_cb,
00590 .pp_arg = cache,
00591 };
00592
00593 return __cache_pickup(sk, cache, &p);
00594 }
00595
00596 static int cache_include(struct nl_cache *cache, struct nl_object *obj,
00597 struct nl_msgtype *type, change_func_t cb, void *data)
00598 {
00599 struct nl_object *old;
00600
00601 switch (type->mt_act) {
00602 case NL_ACT_NEW:
00603 case NL_ACT_DEL:
00604 old = nl_cache_search(cache, obj);
00605 if (old) {
00606 nl_cache_remove(old);
00607 if (type->mt_act == NL_ACT_DEL) {
00608 if (cb)
00609 cb(cache, old, NL_ACT_DEL, data);
00610 nl_object_put(old);
00611 }
00612 }
00613
00614 if (type->mt_act == NL_ACT_NEW) {
00615 nl_cache_move(cache, obj);
00616 if (old == NULL && cb)
00617 cb(cache, obj, NL_ACT_NEW, data);
00618 else if (old) {
00619 if (nl_object_diff(old, obj) && cb)
00620 cb(cache, obj, NL_ACT_CHANGE, data);
00621
00622 nl_object_put(old);
00623 }
00624 }
00625 break;
00626 default:
00627 NL_DBG(2, "Unknown action associated to object %p\n", obj);
00628 return 0;
00629 }
00630
00631 return 0;
00632 }
00633
00634 int nl_cache_include(struct nl_cache *cache, struct nl_object *obj,
00635 change_func_t change_cb, void *data)
00636 {
00637 struct nl_cache_ops *ops = cache->c_ops;
00638 int i;
00639
00640 if (ops->co_obj_ops != obj->ce_ops)
00641 return -NLE_OBJ_MISMATCH;
00642
00643 for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++)
00644 if (ops->co_msgtypes[i].mt_id == obj->ce_msgtype)
00645 return cache_include(cache, obj, &ops->co_msgtypes[i],
00646 change_cb, data);
00647
00648 return -NLE_MSGTYPE_NOSUPPORT;
00649 }
00650
00651 static int resync_cb(struct nl_object *c, struct nl_parser_param *p)
00652 {
00653 struct nl_cache_assoc *ca = p->pp_arg;
00654
00655 return nl_cache_include(ca->ca_cache, c, ca->ca_change, ca->ca_change_data);
00656 }
00657
00658 int nl_cache_resync(struct nl_sock *sk, struct nl_cache *cache,
00659 change_func_t change_cb, void *data)
00660 {
00661 struct nl_object *obj, *next;
00662 struct nl_cache_assoc ca = {
00663 .ca_cache = cache,
00664 .ca_change = change_cb,
00665 .ca_change_data = data,
00666 };
00667 struct nl_parser_param p = {
00668 .pp_cb = resync_cb,
00669 .pp_arg = &ca,
00670 };
00671 int err;
00672
00673 NL_DBG(1, "Resyncing cache %p <%s>...\n", cache, nl_cache_name(cache));
00674
00675 restart:
00676
00677 nl_cache_mark_all(cache);
00678
00679 err = nl_cache_request_full_dump(sk, cache);
00680 if (err < 0)
00681 goto errout;
00682
00683 err = __cache_pickup(sk, cache, &p);
00684 if (err == -NLE_DUMP_INTR)
00685 goto restart;
00686 else if (err < 0)
00687 goto errout;
00688
00689 nl_list_for_each_entry_safe(obj, next, &cache->c_items, ce_list) {
00690 if (nl_object_is_marked(obj)) {
00691 nl_object_get(obj);
00692 nl_cache_remove(obj);
00693 if (change_cb)
00694 change_cb(cache, obj, NL_ACT_DEL, data);
00695 nl_object_put(obj);
00696 }
00697 }
00698
00699 NL_DBG(1, "Finished resyncing %p <%s>\n", cache, nl_cache_name(cache));
00700
00701 err = 0;
00702 errout:
00703 return err;
00704 }
00705
00706
00707
00708
00709
00710
00711
00712
00713
00714 int nl_cache_parse(struct nl_cache_ops *ops, struct sockaddr_nl *who,
00715 struct nlmsghdr *nlh, struct nl_parser_param *params)
00716 {
00717 int i, err;
00718
00719 if (!nlmsg_valid_hdr(nlh, ops->co_hdrsize))
00720 return -NLE_MSG_TOOSHORT;
00721
00722 for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++) {
00723 if (ops->co_msgtypes[i].mt_id == nlh->nlmsg_type) {
00724 err = ops->co_msg_parser(ops, who, nlh, params);
00725 if (err != -NLE_OPNOTSUPP)
00726 goto errout;
00727 }
00728 }
00729
00730
00731 err = -NLE_MSGTYPE_NOSUPPORT;
00732 errout:
00733 return err;
00734 }
00735
00736
00737
00738
00739
00740
00741
00742
00743
00744
00745
00746
00747 int nl_cache_parse_and_add(struct nl_cache *cache, struct nl_msg *msg)
00748 {
00749 struct nl_parser_param p = {
00750 .pp_cb = pickup_cb,
00751 .pp_arg = cache,
00752 };
00753
00754 return nl_cache_parse(cache->c_ops, NULL, nlmsg_hdr(msg), &p);
00755 }
00756
00757
00758
00759
00760
00761
00762
00763
00764
00765
00766
00767 int nl_cache_refill(struct nl_sock *sk, struct nl_cache *cache)
00768 {
00769 int err;
00770
00771 restart:
00772 err = nl_cache_request_full_dump(sk, cache);
00773 if (err < 0)
00774 return err;
00775
00776 NL_DBG(2, "Upading cache %p <%s>, request sent, waiting for dump...\n",
00777 cache, nl_cache_name(cache));
00778 nl_cache_clear(cache);
00779
00780 err = nl_cache_pickup(sk, cache);
00781 if (err == -NLE_DUMP_INTR) {
00782 fprintf(stderr, "dump interrupted, restarting!\n");
00783 goto restart;
00784 }
00785
00786 return err;
00787 }
00788
00789
00790
00791
00792
00793
00794
00795
00796
00797
00798
00799
00800
00801
00802
00803
00804
00805
00806
00807
00808
00809
00810
00811 struct nl_object *nl_cache_search(struct nl_cache *cache,
00812 struct nl_object *needle)
00813 {
00814 struct nl_object *obj;
00815
00816 nl_list_for_each_entry(obj, &cache->c_items, ce_list) {
00817 if (nl_object_identical(obj, needle)) {
00818 nl_object_get(obj);
00819 return obj;
00820 }
00821 }
00822
00823 return NULL;
00824 }
00825
00826
00827
00828
00829
00830
00831
00832
00833 void nl_cache_mark_all(struct nl_cache *cache)
00834 {
00835 struct nl_object *obj;
00836
00837 NL_DBG(2, "Marking all objects in cache %p <%s>...\n",
00838 cache, nl_cache_name(cache));
00839
00840 nl_list_for_each_entry(obj, &cache->c_items, ce_list)
00841 nl_object_mark(obj);
00842 }
00843
00844
00845
00846
00847
00848
00849
00850
00851
00852
00853
00854
00855
00856
00857
00858 void nl_cache_dump(struct nl_cache *cache, struct nl_dump_params *params)
00859 {
00860 nl_cache_dump_filter(cache, params, NULL);
00861 }
00862
00863
00864
00865
00866
00867
00868
00869
00870
00871
00872 void nl_cache_dump_filter(struct nl_cache *cache,
00873 struct nl_dump_params *params,
00874 struct nl_object *filter)
00875 {
00876 int type = params ? params->dp_type : NL_DUMP_DETAILS;
00877 struct nl_object_ops *ops;
00878 struct nl_object *obj;
00879
00880 NL_DBG(2, "Dumping cache %p <%s> filter %p\n",
00881 cache, nl_cache_name(cache), filter);
00882
00883 if (type > NL_DUMP_MAX || type < 0)
00884 BUG();
00885
00886 if (cache->c_ops == NULL)
00887 BUG();
00888
00889 ops = cache->c_ops->co_obj_ops;
00890 if (!ops->oo_dump[type])
00891 return;
00892
00893 nl_list_for_each_entry(obj, &cache->c_items, ce_list) {
00894 if (filter && !nl_object_match_filter(obj, filter))
00895 continue;
00896
00897 NL_DBG(4, "Dumping object %p...\n", obj);
00898 dump_from_ops(obj, params);
00899 }
00900 }
00901
00902
00903
00904
00905
00906
00907
00908
00909
00910
00911
00912
00913
00914
00915
00916
00917
00918 void nl_cache_foreach(struct nl_cache *cache,
00919 void (*cb)(struct nl_object *, void *), void *arg)
00920 {
00921 nl_cache_foreach_filter(cache, NULL, cb, arg);
00922 }
00923
00924
00925
00926
00927
00928
00929
00930
00931
00932
00933
00934
00935 void nl_cache_foreach_filter(struct nl_cache *cache, struct nl_object *filter,
00936 void (*cb)(struct nl_object *, void *), void *arg)
00937 {
00938 struct nl_object *obj, *tmp;
00939
00940 if (cache->c_ops == NULL)
00941 BUG();
00942
00943 nl_list_for_each_entry_safe(obj, tmp, &cache->c_items, ce_list) {
00944 if (filter) {
00945 int diff = nl_object_match_filter(obj, filter);
00946
00947 NL_DBG(3, "%p<->%p object difference: %x\n",
00948 obj, filter, diff);
00949
00950 if (!diff)
00951 continue;
00952 }
00953
00954
00955 nl_object_get(obj);
00956
00957 cb(obj, arg);
00958
00959 nl_object_put(obj);
00960 }
00961 }
00962
00963
00964
00965