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