1 /* SPDX-License-Identifier: LGPL-2.1-only */
2 /*
3 * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
4 */
5
6 /**
7 * @ingroup cache_mngt
8 * @defgroup cache Cache
9 *
10 * @code
11 * Cache Management | | Type Specific Cache Operations
12 *
13 * | | +----------------+ +------------+
14 * | request update | | msg_parser |
15 * | | +----------------+ +------------+
16 * +- - - - -^- - - - - - - -^- -|- - - -
17 * nl_cache_update: | | | |
18 * 1) --------- co_request_update ------+ | |
19 * | | |
20 * 2) destroy old cache +----------- pp_cb ---------|---+
21 * | | |
22 * 3) ---------- nl_recvmsgs ----------+ +- cb_valid -+
23 * +--------------+ | | | |
24 * | nl_cache_add |<-----+ + - - -v- -|- - - - - - - - - - -
25 * +--------------+ | | +-------------+
26 * | nl_recvmsgs |
27 * | | +-----|-^-----+
28 * +---v-|---+
29 * | | | nl_recv |
30 * +---------+
31 * | | Core Netlink
32 * @endcode
33 *
34 * Related sections in the development guide:
35 * - @core_doc{core_cache, Caching System}
36 *
37 * @{
38 *
39 * Header
40 * ------
41 * ~~~~{.c}
42 * #include <netlink/cache.h>
43 * ~~~~
44 */
45
46 #include <netlink-private/netlink.h>
47 #include <netlink-private/utils.h>
48 #include <netlink/netlink.h>
49 #include <netlink/cache.h>
50 #include <netlink/object.h>
51 #include <netlink/hashtable.h>
52 #include <netlink/utils.h>
53
54 /**
55 * @name Access Functions
56 * @{
57 */
58
59 /**
60 * Return the number of items in the cache
61 * @arg cache cache handle
62 */
nl_cache_nitems(struct nl_cache * cache)63 int nl_cache_nitems(struct nl_cache *cache)
64 {
65 return cache->c_nitems;
66 }
67
68 /**
69 * Return the number of items matching a filter in the cache
70 * @arg cache Cache object.
71 * @arg filter Filter object.
72 */
nl_cache_nitems_filter(struct nl_cache * cache,struct nl_object * filter)73 int nl_cache_nitems_filter(struct nl_cache *cache, struct nl_object *filter)
74 {
75 struct nl_object *obj;
76 int nitems = 0;
77
78 if (cache->c_ops == NULL)
79 BUG();
80
81 nl_list_for_each_entry(obj, &cache->c_items, ce_list) {
82 if (filter && !nl_object_match_filter(obj, filter))
83 continue;
84
85 nitems++;
86 }
87
88 return nitems;
89 }
90
91 /**
92 * Returns \b true if the cache is empty.
93 * @arg cache Cache to check
94 * @return \a true if the cache is empty, otherwise \b false is returned.
95 */
nl_cache_is_empty(struct nl_cache * cache)96 int nl_cache_is_empty(struct nl_cache *cache)
97 {
98 return nl_list_empty(&cache->c_items);
99 }
100
101 /**
102 * Return the operations set of the cache
103 * @arg cache cache handle
104 */
nl_cache_get_ops(struct nl_cache * cache)105 struct nl_cache_ops *nl_cache_get_ops(struct nl_cache *cache)
106 {
107 return cache->c_ops;
108 }
109
110 /**
111 * Return the first element in the cache
112 * @arg cache cache handle
113 */
nl_cache_get_first(struct nl_cache * cache)114 struct nl_object *nl_cache_get_first(struct nl_cache *cache)
115 {
116 if (nl_list_empty(&cache->c_items))
117 return NULL;
118
119 return nl_list_entry(cache->c_items.next,
120 struct nl_object, ce_list);
121 }
122
123 /**
124 * Return the last element in the cache
125 * @arg cache cache handle
126 */
nl_cache_get_last(struct nl_cache * cache)127 struct nl_object *nl_cache_get_last(struct nl_cache *cache)
128 {
129 if (nl_list_empty(&cache->c_items))
130 return NULL;
131
132 return nl_list_entry(cache->c_items.prev,
133 struct nl_object, ce_list);
134 }
135
136 /**
137 * Return the next element in the cache
138 * @arg obj current object
139 */
nl_cache_get_next(struct nl_object * obj)140 struct nl_object *nl_cache_get_next(struct nl_object *obj)
141 {
142 if (nl_list_at_tail(obj, &obj->ce_cache->c_items, ce_list))
143 return NULL;
144 else
145 return nl_list_entry(obj->ce_list.next,
146 struct nl_object, ce_list);
147 }
148
149 /**
150 * Return the previous element in the cache
151 * @arg obj current object
152 */
nl_cache_get_prev(struct nl_object * obj)153 struct nl_object *nl_cache_get_prev(struct nl_object *obj)
154 {
155 if (nl_list_at_head(obj, &obj->ce_cache->c_items, ce_list))
156 return NULL;
157 else
158 return nl_list_entry(obj->ce_list.prev,
159 struct nl_object, ce_list);
160 }
161
162 /** @} */
163
164 /**
165 * @name Cache Allocation/Deletion
166 * @{
167 */
168
169 /**
170 * Allocate new cache
171 * @arg ops Cache operations
172 *
173 * Allocate and initialize a new cache based on the cache operations
174 * provided.
175 *
176 * @return Allocated cache or NULL if allocation failed.
177 */
nl_cache_alloc(struct nl_cache_ops * ops)178 struct nl_cache *nl_cache_alloc(struct nl_cache_ops *ops)
179 {
180 struct nl_cache *cache;
181
182 cache = calloc(1, sizeof(*cache));
183 if (!cache)
184 return NULL;
185
186 nl_init_list_head(&cache->c_items);
187 cache->c_ops = ops;
188 cache->c_flags |= ops->co_flags;
189 cache->c_refcnt = 1;
190
191 /*
192 * If object type provides a hash keygen
193 * functions, allocate a hash table for the
194 * cache objects for faster lookups
195 */
196 if (ops->co_obj_ops->oo_keygen) {
197 int hashtable_size;
198
199 if (ops->co_hash_size)
200 hashtable_size = ops->co_hash_size;
201 else
202 hashtable_size = NL_MAX_HASH_ENTRIES;
203
204 cache->hashtable = nl_hash_table_alloc(hashtable_size);
205 }
206
207 NL_DBG(2, "Allocated cache %p <%s>.\n", cache, nl_cache_name(cache));
208
209 return cache;
210 }
211
212 /**
213 * Allocate new cache and fill it
214 * @arg ops Cache operations
215 * @arg sock Netlink socket
216 * @arg result Result pointer
217 *
218 * Allocate new cache and fill it. Equivalent to calling:
219 * @code
220 * cache = nl_cache_alloc(ops);
221 * nl_cache_refill(sock, cache);
222 * @endcode
223 *
224 * @see nl_cache_alloc
225 *
226 * @return 0 on success or a negative error code.
227 */
nl_cache_alloc_and_fill(struct nl_cache_ops * ops,struct nl_sock * sock,struct nl_cache ** result)228 int nl_cache_alloc_and_fill(struct nl_cache_ops *ops, struct nl_sock *sock,
229 struct nl_cache **result)
230 {
231 struct nl_cache *cache;
232 int err;
233
234 if (!(cache = nl_cache_alloc(ops)))
235 return -NLE_NOMEM;
236
237 if (sock && (err = nl_cache_refill(sock, cache)) < 0) {
238 nl_cache_free(cache);
239 return err;
240 }
241
242 *result = cache;
243 return 0;
244 }
245
246 /**
247 * Allocate new cache based on type name
248 * @arg kind Name of cache type
249 * @arg result Result pointer
250 *
251 * Lookup cache ops via nl_cache_ops_lookup() and allocate the cache
252 * by calling nl_cache_alloc(). Stores the allocated cache in the
253 * result pointer provided.
254 *
255 * @see nl_cache_alloc
256 *
257 * @return 0 on success or a negative error code.
258 */
nl_cache_alloc_name(const char * kind,struct nl_cache ** result)259 int nl_cache_alloc_name(const char *kind, struct nl_cache **result)
260 {
261 struct nl_cache_ops *ops;
262 struct nl_cache *cache;
263
264 ops = nl_cache_ops_lookup_safe(kind);
265 if (!ops)
266 return -NLE_NOCACHE;
267
268 cache = nl_cache_alloc(ops);
269 nl_cache_ops_put(ops);
270 if (!cache)
271 return -NLE_NOMEM;
272
273 *result = cache;
274 return 0;
275 }
276
277 /**
278 * Allocate new cache containing a subset of an existing cache
279 * @arg orig Original cache to base new cache on
280 * @arg filter Filter defining the subset to be filled into the new cache
281 *
282 * Allocates a new cache matching the type of the cache specified by
283 * \p orig. Iterates over the \p orig cache applying the specified
284 * \p filter and copies all objects that match to the new cache.
285 *
286 * The copied objects are clones but do not contain a reference to each
287 * other. Later modifications to objects in the original cache will
288 * not affect objects in the new cache.
289 *
290 * @return A newly allocated cache or NULL.
291 */
nl_cache_subset(struct nl_cache * orig,struct nl_object * filter)292 struct nl_cache *nl_cache_subset(struct nl_cache *orig,
293 struct nl_object *filter)
294 {
295 struct nl_cache *cache;
296 struct nl_object *obj;
297
298 if (!filter)
299 BUG();
300
301 cache = nl_cache_alloc(orig->c_ops);
302 if (!cache)
303 return NULL;
304
305 NL_DBG(2, "Filling subset of cache %p <%s> with filter %p into %p\n",
306 orig, nl_cache_name(orig), filter, cache);
307
308 nl_list_for_each_entry(obj, &orig->c_items, ce_list) {
309 if (!nl_object_match_filter(obj, filter))
310 continue;
311
312 nl_cache_add(cache, obj);
313 }
314
315 return cache;
316 }
317
318 /**
319 * Allocate new cache and copy the contents of an existing cache
320 * @arg cache Original cache to base new cache on
321 *
322 * Allocates a new cache matching the type of the cache specified by
323 * \p cache. Iterates over the \p cache cache and copies all objects
324 * to the new cache.
325 *
326 * The copied objects are clones but do not contain a reference to each
327 * other. Later modifications to objects in the original cache will
328 * not affect objects in the new cache.
329 *
330 * @return A newly allocated cache or NULL.
331 */
nl_cache_clone(struct nl_cache * cache)332 struct nl_cache *nl_cache_clone(struct nl_cache *cache)
333 {
334 struct nl_cache_ops *ops = nl_cache_get_ops(cache);
335 struct nl_cache *clone;
336 struct nl_object *obj;
337
338 clone = nl_cache_alloc(ops);
339 if (!clone)
340 return NULL;
341
342 NL_DBG(2, "Cloning %p into %p\n", cache, clone);
343
344 nl_list_for_each_entry(obj, &cache->c_items, ce_list)
345 nl_cache_add(clone, obj);
346
347 return clone;
348 }
349
350 /**
351 * Remove all objects of a cache.
352 * @arg cache Cache to clear
353 *
354 * The objects are unliked/removed from the cache by calling
355 * nl_cache_remove() on each object in the cache. If any of the objects
356 * to not contain any further references to them, those objects will
357 * be freed.
358 *
359 * Unlike with nl_cache_free(), the cache is not freed just emptied.
360 */
nl_cache_clear(struct nl_cache * cache)361 void nl_cache_clear(struct nl_cache *cache)
362 {
363 struct nl_object *obj, *tmp;
364
365 NL_DBG(2, "Clearing cache %p <%s>...\n", cache, nl_cache_name(cache));
366
367 nl_list_for_each_entry_safe(obj, tmp, &cache->c_items, ce_list)
368 nl_cache_remove(obj);
369 }
370
__nl_cache_free(struct nl_cache * cache)371 static void __nl_cache_free(struct nl_cache *cache)
372 {
373 nl_cache_clear(cache);
374
375 if (cache->hashtable)
376 nl_hash_table_free(cache->hashtable);
377
378 NL_DBG(2, "Freeing cache %p <%s>...\n", cache, nl_cache_name(cache));
379 free(cache);
380 }
381
382 /**
383 * Increase reference counter of cache
384 * @arg cache Cache
385 */
nl_cache_get(struct nl_cache * cache)386 void nl_cache_get(struct nl_cache *cache)
387 {
388 cache->c_refcnt++;
389
390 NL_DBG(3, "Incremented cache %p <%s> reference count to %d\n",
391 cache, nl_cache_name(cache), cache->c_refcnt);
392 }
393
394 /**
395 * Free a cache.
396 * @arg cache Cache to free.
397 *
398 * Calls nl_cache_clear() to remove all objects associated with the
399 * cache and frees the cache afterwards.
400 *
401 * @see nl_cache_clear()
402 */
nl_cache_free(struct nl_cache * cache)403 void nl_cache_free(struct nl_cache *cache)
404 {
405 if (!cache)
406 return;
407
408 cache->c_refcnt--;
409
410 NL_DBG(3, "Decremented cache %p <%s> reference count, %d remaining\n",
411 cache, nl_cache_name(cache), cache->c_refcnt);
412
413 if (cache->c_refcnt <= 0)
414 __nl_cache_free(cache);
415 }
416
nl_cache_put(struct nl_cache * cache)417 void nl_cache_put(struct nl_cache *cache)
418 {
419 nl_cache_free(cache);
420 }
421
422 /** @} */
423
424 /**
425 * @name Cache Modifications
426 * @{
427 */
428
__cache_add(struct nl_cache * cache,struct nl_object * obj)429 static int __cache_add(struct nl_cache *cache, struct nl_object *obj)
430 {
431 int ret;
432
433 obj->ce_cache = cache;
434
435 if (cache->hashtable) {
436 ret = nl_hash_table_add(cache->hashtable, obj);
437 if (ret < 0) {
438 obj->ce_cache = NULL;
439 return ret;
440 }
441 }
442
443 nl_list_add_tail(&obj->ce_list, &cache->c_items);
444 cache->c_nitems++;
445
446 NL_DBG(3, "Added object %p to cache %p <%s>, nitems %d\n",
447 obj, cache, nl_cache_name(cache), cache->c_nitems);
448
449 return 0;
450 }
451
452 /**
453 * Add object to cache.
454 * @arg cache Cache
455 * @arg obj Object to be added to the cache
456 *
457 * Adds the object \p obj to the specified \p cache. In case the object
458 * is already associated with another cache, the object is cloned before
459 * adding it to the cache. In this case, the sole reference to the object
460 * will be the one of the cache. Therefore clearing/freeing the cache
461 * will result in the object being freed again.
462 *
463 * If the object has not been associated with a cache yet, the reference
464 * counter of the object is incremented to account for the additional
465 * reference.
466 *
467 * The type of the object and cache must match, otherwise an error is
468 * returned (-NLE_OBJ_MISMATCH).
469 *
470 * @see nl_cache_move()
471 *
472 * @return 0 or a negative error code.
473 */
nl_cache_add(struct nl_cache * cache,struct nl_object * obj)474 int nl_cache_add(struct nl_cache *cache, struct nl_object *obj)
475 {
476 struct nl_object *new;
477 int ret = 0;
478
479 if (cache->c_ops->co_obj_ops != obj->ce_ops)
480 return -NLE_OBJ_MISMATCH;
481
482 if (!nl_list_empty(&obj->ce_list)) {
483 NL_DBG(3, "Object %p already in cache, cloning new object\n", obj);
484
485 new = nl_object_clone(obj);
486 if (!new)
487 return -NLE_NOMEM;
488 } else {
489 nl_object_get(obj);
490 new = obj;
491 }
492
493 ret = __cache_add(cache, new);
494 if (ret < 0)
495 nl_object_put(new);
496
497 return ret;
498 }
499
500 /**
501 * Move object from one cache to another
502 * @arg cache Cache to move object to.
503 * @arg obj Object subject to be moved
504 *
505 * Removes the the specified object \p obj from its associated cache
506 * and moves it to another cache.
507 *
508 * If the object is not associated with a cache, the function behaves
509 * just like nl_cache_add().
510 *
511 * The type of the object and cache must match, otherwise an error is
512 * returned (-NLE_OBJ_MISMATCH).
513 *
514 * @see nl_cache_add()
515 *
516 * @return 0 on success or a negative error code.
517 */
nl_cache_move(struct nl_cache * cache,struct nl_object * obj)518 int nl_cache_move(struct nl_cache *cache, struct nl_object *obj)
519 {
520 if (cache->c_ops->co_obj_ops != obj->ce_ops)
521 return -NLE_OBJ_MISMATCH;
522
523 NL_DBG(3, "Moving object %p from cache %p to cache %p\n",
524 obj, obj->ce_cache, cache);
525
526 /* Acquire reference, if already in a cache this will be
527 * reverted during removal */
528 nl_object_get(obj);
529
530 if (!nl_list_empty(&obj->ce_list))
531 nl_cache_remove(obj);
532
533 return __cache_add(cache, obj);
534 }
535
536 /**
537 * Remove object from cache.
538 * @arg obj Object to remove from cache
539 *
540 * Removes the object \c obj from the cache it is associated with. The
541 * reference counter of the object will be decremented. If the reference
542 * to the object was the only one remaining, the object will be freed.
543 *
544 * If no cache is associated with the object, this function is a NOP.
545 */
nl_cache_remove(struct nl_object * obj)546 void nl_cache_remove(struct nl_object *obj)
547 {
548 int ret;
549 struct nl_cache *cache = obj->ce_cache;
550
551 if (cache == NULL)
552 return;
553
554 if (cache->hashtable) {
555 ret = nl_hash_table_del(cache->hashtable, obj);
556 if (ret < 0)
557 NL_DBG(2, "Failed to delete %p from cache %p <%s>.\n",
558 obj, cache, nl_cache_name(cache));
559 }
560
561 nl_list_del(&obj->ce_list);
562 obj->ce_cache = NULL;
563 nl_object_put(obj);
564 cache->c_nitems--;
565
566 NL_DBG(2, "Deleted object %p from cache %p <%s>.\n",
567 obj, cache, nl_cache_name(cache));
568 }
569
570 /** @} */
571
572 /**
573 * @name Synchronization
574 * @{
575 */
576
577 /**
578 * Set synchronization arg1 of cache
579 * @arg cache Cache
580 * @arg arg argument
581 *
582 * Synchronization arguments are used to specify filters when
583 * requesting dumps from the kernel.
584 */
nl_cache_set_arg1(struct nl_cache * cache,int arg)585 void nl_cache_set_arg1(struct nl_cache *cache, int arg)
586 {
587 cache->c_iarg1 = arg;
588 }
589
590 /**
591 * Set synchronization arg2 of cache
592 * @arg cache Cache
593 * @arg arg argument
594 *
595 * Synchronization arguments are used to specify filters when
596 * requesting dumps from the kernel.
597 */
nl_cache_set_arg2(struct nl_cache * cache,int arg)598 void nl_cache_set_arg2(struct nl_cache *cache, int arg)
599 {
600 cache->c_iarg2 = arg;
601 }
602
603 /**
604 * Set cache flags
605 * @arg cache Cache
606 * @arg flags Flags
607 */
nl_cache_set_flags(struct nl_cache * cache,unsigned int flags)608 void nl_cache_set_flags(struct nl_cache *cache, unsigned int flags)
609 {
610 cache->c_flags |= flags;
611 }
612
613 /**
614 * Invoke the request-update operation
615 * @arg sk Netlink socket.
616 * @arg cache Cache
617 *
618 * This function causes the \e request-update function of the cache
619 * operations to be invoked. This usually causes a dump request to
620 * be sent over the netlink socket which triggers the kernel to dump
621 * all objects of a specific type to be dumped onto the netlink
622 * socket for pickup.
623 *
624 * The behaviour of this function depends on the implemenation of
625 * the \e request_update function of each individual type of cache.
626 *
627 * This function will not have any effects on the cache (unless the
628 * request_update implementation of the cache operations does so).
629 *
630 * Use nl_cache_pickup() to pick-up (read) the objects from the socket
631 * and fill them into the cache.
632 *
633 * @see nl_cache_pickup(), nl_cache_resync()
634 *
635 * @return 0 on success or a negative error code. Some implementations
636 * of co_request_update() return a positive number on success that is
637 * the number of bytes sent. Treat any non-negative number as success too.
638 */
nl_cache_request_full_dump(struct nl_sock * sk,struct nl_cache * cache)639 static int nl_cache_request_full_dump(struct nl_sock *sk,
640 struct nl_cache *cache)
641 {
642 if (sk->s_proto != cache->c_ops->co_protocol)
643 return -NLE_PROTO_MISMATCH;
644
645 if (cache->c_ops->co_request_update == NULL)
646 return -NLE_OPNOTSUPP;
647
648 NL_DBG(2, "Requesting update from kernel for cache %p <%s>\n",
649 cache, nl_cache_name(cache));
650
651 return cache->c_ops->co_request_update(cache, sk);
652 }
653
654 /** @cond SKIP */
655 struct update_xdata {
656 struct nl_cache_ops *ops;
657 struct nl_parser_param *params;
658 };
659
update_msg_parser(struct nl_msg * msg,void * arg)660 static int update_msg_parser(struct nl_msg *msg, void *arg)
661 {
662 struct update_xdata *x = arg;
663 int ret = 0;
664
665 ret = nl_cache_parse(x->ops, &msg->nm_src, msg->nm_nlh, x->params);
666 if (ret == -NLE_EXIST)
667 return NL_SKIP;
668 else
669 return ret;
670 }
671 /** @endcond */
672
673 /**
674 * Pick-up a netlink request-update with your own parser
675 * @arg sk Netlink socket
676 * @arg cache Cache
677 * @arg param Parser parameters
678 */
__cache_pickup(struct nl_sock * sk,struct nl_cache * cache,struct nl_parser_param * param)679 static int __cache_pickup(struct nl_sock *sk, struct nl_cache *cache,
680 struct nl_parser_param *param)
681 {
682 int err;
683 struct nl_cb *cb;
684 struct update_xdata x = {
685 .ops = cache->c_ops,
686 .params = param,
687 };
688
689 NL_DBG(2, "Picking up answer for cache %p <%s>\n",
690 cache, nl_cache_name(cache));
691
692 cb = nl_cb_clone(sk->s_cb);
693 if (cb == NULL)
694 return -NLE_NOMEM;
695
696 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, update_msg_parser, &x);
697
698 err = nl_recvmsgs(sk, cb);
699 if (err < 0)
700 NL_DBG(2, "While picking up for %p <%s>, recvmsgs() returned %d: %s\n",
701 cache, nl_cache_name(cache), err, nl_geterror(err));
702
703 nl_cb_put(cb);
704
705 return err;
706 }
707
pickup_checkdup_cb(struct nl_object * c,struct nl_parser_param * p)708 static int pickup_checkdup_cb(struct nl_object *c, struct nl_parser_param *p)
709 {
710 struct nl_cache *cache = (struct nl_cache *)p->pp_arg;
711 struct nl_object *old;
712
713 old = nl_cache_search(cache, c);
714 if (old) {
715 if (nl_object_update(old, c) == 0) {
716 nl_object_put(old);
717 return 0;
718 }
719
720 nl_cache_remove(old);
721 nl_object_put(old);
722 }
723
724 return nl_cache_add(cache, c);
725 }
726
pickup_cb(struct nl_object * c,struct nl_parser_param * p)727 static int pickup_cb(struct nl_object *c, struct nl_parser_param *p)
728 {
729 struct nl_cache *cache = p->pp_arg;
730
731 return nl_cache_add(cache, c);
732 }
733
__nl_cache_pickup(struct nl_sock * sk,struct nl_cache * cache,int checkdup)734 static int __nl_cache_pickup(struct nl_sock *sk, struct nl_cache *cache,
735 int checkdup)
736 {
737 struct nl_parser_param p;
738
739 p.pp_cb = checkdup ? pickup_checkdup_cb : pickup_cb;
740 p.pp_arg = cache;
741
742 if (sk->s_proto != cache->c_ops->co_protocol)
743 return -NLE_PROTO_MISMATCH;
744
745 return __cache_pickup(sk, cache, &p);
746 }
747
748 /**
749 * Pickup a netlink dump response and put it into a cache.
750 * @arg sk Netlink socket.
751 * @arg cache Cache to put items into.
752 *
753 * Waits for netlink messages to arrive, parses them and puts them into
754 * the specified cache. If an old object with same key attributes is
755 * present in the cache, it is replaced with the new object.
756 * If the old object type supports an update operation, an update is
757 * attempted before a replace.
758 *
759 * @return 0 on success or a negative error code.
760 */
nl_cache_pickup_checkdup(struct nl_sock * sk,struct nl_cache * cache)761 int nl_cache_pickup_checkdup(struct nl_sock *sk, struct nl_cache *cache)
762 {
763 return __nl_cache_pickup(sk, cache, 1);
764 }
765
766 /**
767 * Pickup a netlink dump response and put it into a cache.
768 * @arg sk Netlink socket.
769 * @arg cache Cache to put items into.
770 *
771 * Waits for netlink messages to arrive, parses them and puts them into
772 * the specified cache.
773 *
774 * @return 0 on success or a negative error code.
775 */
nl_cache_pickup(struct nl_sock * sk,struct nl_cache * cache)776 int nl_cache_pickup(struct nl_sock *sk, struct nl_cache *cache)
777 {
778 return __nl_cache_pickup(sk, cache, 0);
779 }
780
cache_include(struct nl_cache * cache,struct nl_object * obj,struct nl_msgtype * type,change_func_t cb,change_func_v2_t cb_v2,void * data)781 static int cache_include(struct nl_cache *cache, struct nl_object *obj,
782 struct nl_msgtype *type, change_func_t cb,
783 change_func_v2_t cb_v2, void *data)
784 {
785 struct nl_object *old;
786 struct nl_object *clone = NULL;
787 uint64_t diff = 0;
788
789 switch (type->mt_act) {
790 case NL_ACT_NEW:
791 case NL_ACT_DEL:
792 old = nl_cache_search(cache, obj);
793 if (old) {
794 if (cb_v2 && old->ce_ops->oo_update) {
795 clone = nl_object_clone(old);
796 diff = nl_object_diff64(old, obj);
797 }
798 /*
799 * Some objects types might support merging the new
800 * object with the old existing cache object.
801 * Handle them first.
802 */
803 if (nl_object_update(old, obj) == 0) {
804 if (cb_v2) {
805 cb_v2(cache, clone, obj, diff,
806 NL_ACT_CHANGE, data);
807 nl_object_put(clone);
808 } else if (cb)
809 cb(cache, old, NL_ACT_CHANGE, data);
810 nl_object_put(old);
811 return 0;
812 }
813 nl_object_put(clone);
814
815 nl_cache_remove(old);
816 if (type->mt_act == NL_ACT_DEL) {
817 if (cb_v2)
818 cb_v2(cache, old, NULL, 0, NL_ACT_DEL,
819 data);
820 else if (cb)
821 cb(cache, old, NL_ACT_DEL, data);
822 nl_object_put(old);
823 }
824 }
825
826 if (type->mt_act == NL_ACT_NEW) {
827 nl_cache_move(cache, obj);
828 if (old == NULL) {
829 if (cb_v2) {
830 cb_v2(cache, NULL, obj, 0, NL_ACT_NEW,
831 data);
832 } else if (cb)
833 cb(cache, obj, NL_ACT_NEW, data);
834 } else if (old) {
835 diff = 0;
836 if (cb || cb_v2)
837 diff = nl_object_diff64(old, obj);
838 if (diff && cb_v2) {
839 cb_v2(cache, old, obj, diff, NL_ACT_CHANGE,
840 data);
841 } else if (diff && cb)
842 cb(cache, obj, NL_ACT_CHANGE, data);
843
844 nl_object_put(old);
845 }
846 }
847 break;
848 default:
849 NL_DBG(2, "Unknown action associated to object %p\n", obj);
850 return 0;
851 }
852
853 return 0;
854 }
855
nl_cache_include(struct nl_cache * cache,struct nl_object * obj,change_func_t change_cb,void * data)856 int nl_cache_include(struct nl_cache *cache, struct nl_object *obj,
857 change_func_t change_cb, void *data)
858 {
859 struct nl_cache_ops *ops = cache->c_ops;
860 int i;
861
862 if (ops->co_obj_ops != obj->ce_ops)
863 return -NLE_OBJ_MISMATCH;
864
865 for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++)
866 if (ops->co_msgtypes[i].mt_id == obj->ce_msgtype)
867 return cache_include(cache, obj, &ops->co_msgtypes[i],
868 change_cb, NULL, data);
869
870 NL_DBG(3, "Object %p does not seem to belong to cache %p <%s>\n",
871 obj, cache, nl_cache_name(cache));
872
873 return -NLE_MSGTYPE_NOSUPPORT;
874 }
875
nl_cache_include_v2(struct nl_cache * cache,struct nl_object * obj,change_func_v2_t change_cb,void * data)876 int nl_cache_include_v2(struct nl_cache *cache, struct nl_object *obj,
877 change_func_v2_t change_cb, void *data)
878 {
879 struct nl_cache_ops *ops = cache->c_ops;
880 int i;
881
882 if (ops->co_obj_ops != obj->ce_ops)
883 return -NLE_OBJ_MISMATCH;
884
885 for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++)
886 if (ops->co_msgtypes[i].mt_id == obj->ce_msgtype)
887 return cache_include(cache, obj, &ops->co_msgtypes[i],
888 NULL, change_cb, data);
889
890 NL_DBG(3, "Object %p does not seem to belong to cache %p <%s>\n",
891 obj, cache, nl_cache_name(cache));
892
893 return -NLE_MSGTYPE_NOSUPPORT;
894 }
895
resync_cb(struct nl_object * c,struct nl_parser_param * p)896 static int resync_cb(struct nl_object *c, struct nl_parser_param *p)
897 {
898 struct nl_cache_assoc *ca = p->pp_arg;
899
900 if (ca->ca_change_v2)
901 return nl_cache_include_v2(ca->ca_cache, c, ca->ca_change_v2,
902 ca->ca_change_data);
903 else
904 return nl_cache_include(ca->ca_cache, c, ca->ca_change,
905 ca->ca_change_data);
906 }
907
nl_cache_resync(struct nl_sock * sk,struct nl_cache * cache,change_func_t change_cb,void * data)908 int nl_cache_resync(struct nl_sock *sk, struct nl_cache *cache,
909 change_func_t change_cb, void *data)
910 {
911 struct nl_object *obj, *next;
912 struct nl_af_group *grp;
913 struct nl_cache_assoc ca = {
914 .ca_cache = cache,
915 .ca_change = change_cb,
916 .ca_change_data = data,
917 };
918 struct nl_parser_param p = {
919 .pp_cb = resync_cb,
920 .pp_arg = &ca,
921 };
922 int err;
923
924 if (sk->s_proto != cache->c_ops->co_protocol)
925 return -NLE_PROTO_MISMATCH;
926
927 NL_DBG(1, "Resyncing cache %p <%s>...\n", cache, nl_cache_name(cache));
928
929 /* Mark all objects so we can see if some of them are obsolete */
930 nl_cache_mark_all(cache);
931
932 grp = cache->c_ops->co_groups;
933 do {
934 if (grp && grp->ag_group &&
935 (cache->c_flags & NL_CACHE_AF_ITER))
936 nl_cache_set_arg1(cache, grp->ag_family);
937
938 restart:
939 err = nl_cache_request_full_dump(sk, cache);
940 if (err < 0)
941 goto errout;
942
943 err = __cache_pickup(sk, cache, &p);
944 if (err == -NLE_DUMP_INTR)
945 goto restart;
946 else if (err < 0)
947 goto errout;
948
949 if (grp)
950 grp++;
951 } while (grp && grp->ag_group &&
952 (cache->c_flags & NL_CACHE_AF_ITER));
953
954 nl_list_for_each_entry_safe(obj, next, &cache->c_items, ce_list) {
955 if (nl_object_is_marked(obj)) {
956 nl_object_get(obj);
957 nl_cache_remove(obj);
958 if (change_cb)
959 change_cb(cache, obj, NL_ACT_DEL, data);
960 nl_object_put(obj);
961 }
962 }
963
964 NL_DBG(1, "Finished resyncing %p <%s>\n", cache, nl_cache_name(cache));
965
966 err = 0;
967 errout:
968 return err;
969 }
970
971 /** @} */
972
973 /**
974 * @name Parsing
975 * @{
976 */
977
978 /** @cond SKIP */
nl_cache_parse(struct nl_cache_ops * ops,struct sockaddr_nl * who,struct nlmsghdr * nlh,struct nl_parser_param * params)979 int nl_cache_parse(struct nl_cache_ops *ops, struct sockaddr_nl *who,
980 struct nlmsghdr *nlh, struct nl_parser_param *params)
981 {
982 int i, err;
983
984 if (!nlmsg_valid_hdr(nlh, ops->co_hdrsize))
985 return -NLE_MSG_TOOSHORT;
986
987 for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++) {
988 if (ops->co_msgtypes[i].mt_id == nlh->nlmsg_type) {
989 err = ops->co_msg_parser(ops, who, nlh, params);
990 if (err != -NLE_OPNOTSUPP)
991 goto errout;
992 }
993 }
994
995
996 err = -NLE_MSGTYPE_NOSUPPORT;
997 errout:
998 return err;
999 }
1000 /** @endcond */
1001
1002 /**
1003 * Parse a netlink message and add it to the cache.
1004 * @arg cache cache to add element to
1005 * @arg msg netlink message
1006 *
1007 * Parses a netlink message by calling the cache specific message parser
1008 * and adds the new element to the cache. If an old object with same key
1009 * attributes is present in the cache, it is replaced with the new object.
1010 * If the old object type supports an update operation, an update is
1011 * attempted before a replace.
1012 *
1013 * @return 0 or a negative error code.
1014 */
nl_cache_parse_and_add(struct nl_cache * cache,struct nl_msg * msg)1015 int nl_cache_parse_and_add(struct nl_cache *cache, struct nl_msg *msg)
1016 {
1017 struct nl_parser_param p = {
1018 .pp_cb = pickup_cb,
1019 .pp_arg = cache,
1020 };
1021
1022 return nl_cache_parse(cache->c_ops, NULL, nlmsg_hdr(msg), &p);
1023 }
1024
1025 /**
1026 * (Re)fill a cache with the contents in the kernel.
1027 * @arg sk Netlink socket.
1028 * @arg cache cache to update
1029 *
1030 * Clears the specified cache and fills it with the current state in
1031 * the kernel.
1032 *
1033 * @return 0 or a negative error code.
1034 */
nl_cache_refill(struct nl_sock * sk,struct nl_cache * cache)1035 int nl_cache_refill(struct nl_sock *sk, struct nl_cache *cache)
1036 {
1037 struct nl_af_group *grp;
1038 int err;
1039
1040 if (sk->s_proto != cache->c_ops->co_protocol)
1041 return -NLE_PROTO_MISMATCH;
1042
1043 nl_cache_clear(cache);
1044 grp = cache->c_ops->co_groups;
1045 do {
1046 if (grp && grp->ag_group &&
1047 (cache->c_flags & NL_CACHE_AF_ITER))
1048 nl_cache_set_arg1(cache, grp->ag_family);
1049
1050 restart:
1051 err = nl_cache_request_full_dump(sk, cache);
1052 if (err < 0)
1053 return err;
1054
1055 NL_DBG(2, "Updating cache %p <%s> for family %u, request sent, waiting for reply\n",
1056 cache, nl_cache_name(cache), grp ? grp->ag_family : AF_UNSPEC);
1057
1058 err = nl_cache_pickup(sk, cache);
1059 if (err == -NLE_DUMP_INTR) {
1060 NL_DBG(2, "Dump interrupted, restarting!\n");
1061 goto restart;
1062 } else if (err < 0)
1063 break;
1064
1065 if (grp)
1066 grp++;
1067 } while (grp && grp->ag_group &&
1068 (cache->c_flags & NL_CACHE_AF_ITER));
1069
1070 return err;
1071 }
1072
1073 /** @} */
1074
1075 /**
1076 * @name Utillities
1077 * @{
1078 */
__cache_fast_lookup(struct nl_cache * cache,struct nl_object * needle)1079 static struct nl_object *__cache_fast_lookup(struct nl_cache *cache,
1080 struct nl_object *needle)
1081 {
1082 struct nl_object *obj;
1083
1084 obj = nl_hash_table_lookup(cache->hashtable, needle);
1085 if (obj) {
1086 nl_object_get(obj);
1087 return obj;
1088 }
1089
1090 return NULL;
1091 }
1092
1093 /**
1094 * Search object in cache
1095 * @arg cache Cache
1096 * @arg needle Object to look for.
1097 *
1098 * Searches the cache for an object which matches the object \p needle.
1099 * The function nl_object_identical() is used to determine if the
1100 * objects match. If a matching object is found, the reference counter
1101 * is incremented and the object is returned.
1102 *
1103 * Therefore, if an object is returned, the reference to the object
1104 * must be returned by calling nl_object_put() after usage.
1105 *
1106 * @return Reference to object or NULL if not found.
1107 */
nl_cache_search(struct nl_cache * cache,struct nl_object * needle)1108 struct nl_object *nl_cache_search(struct nl_cache *cache,
1109 struct nl_object *needle)
1110 {
1111 struct nl_object *obj;
1112
1113 if (cache->hashtable)
1114 return __cache_fast_lookup(cache, needle);
1115
1116 nl_list_for_each_entry(obj, &cache->c_items, ce_list) {
1117 if (nl_object_identical(obj, needle)) {
1118 nl_object_get(obj);
1119 return obj;
1120 }
1121 }
1122
1123 return NULL;
1124 }
1125
1126 /**
1127 * Find object in cache
1128 * @arg cache Cache
1129 * @arg filter object acting as a filter
1130 *
1131 * Searches the cache for an object which matches the object filter.
1132 * If the filter attributes matches the object type id attributes,
1133 * and the cache supports hash lookups, a faster hashtable lookup
1134 * is used to return the object. Else, function nl_object_match_filter() is
1135 * used to determine if the objects match. If a matching object is
1136 * found, the reference counter is incremented and the object is returned.
1137 *
1138 * Therefore, if an object is returned, the reference to the object
1139 * must be returned by calling nl_object_put() after usage.
1140 *
1141 * @return Reference to object or NULL if not found.
1142 */
nl_cache_find(struct nl_cache * cache,struct nl_object * filter)1143 struct nl_object *nl_cache_find(struct nl_cache *cache,
1144 struct nl_object *filter)
1145 {
1146 struct nl_object *obj;
1147
1148 if (cache->c_ops == NULL)
1149 BUG();
1150
1151 if ((nl_object_get_id_attrs(filter) == filter->ce_mask)
1152 && cache->hashtable)
1153 return __cache_fast_lookup(cache, filter);
1154
1155 nl_list_for_each_entry(obj, &cache->c_items, ce_list) {
1156 if (nl_object_match_filter(obj, filter)) {
1157 nl_object_get(obj);
1158 return obj;
1159 }
1160 }
1161
1162 return NULL;
1163 }
1164
1165 /**
1166 * Mark all objects of a cache
1167 * @arg cache Cache
1168 *
1169 * Marks all objects of a cache by calling nl_object_mark() on each
1170 * object associated with the cache.
1171 */
nl_cache_mark_all(struct nl_cache * cache)1172 void nl_cache_mark_all(struct nl_cache *cache)
1173 {
1174 struct nl_object *obj;
1175
1176 NL_DBG(2, "Marking all objects in cache %p <%s>\n",
1177 cache, nl_cache_name(cache));
1178
1179 nl_list_for_each_entry(obj, &cache->c_items, ce_list)
1180 nl_object_mark(obj);
1181 }
1182
1183 /** @} */
1184
1185 /**
1186 * @name Dumping
1187 * @{
1188 */
1189
1190 /**
1191 * Dump all elements of a cache.
1192 * @arg cache cache to dump
1193 * @arg params dumping parameters
1194 *
1195 * Dumps all elements of the \a cache to the file descriptor \a fd.
1196 */
nl_cache_dump(struct nl_cache * cache,struct nl_dump_params * params)1197 void nl_cache_dump(struct nl_cache *cache, struct nl_dump_params *params)
1198 {
1199 nl_cache_dump_filter(cache, params, NULL);
1200 }
1201
1202 /**
1203 * Dump all elements of a cache (filtered).
1204 * @arg cache cache to dump
1205 * @arg params dumping parameters
1206 * @arg filter filter object
1207 *
1208 * Dumps all elements of the \a cache to the file descriptor \a fd
1209 * given they match the given filter \a filter.
1210 */
nl_cache_dump_filter(struct nl_cache * cache,struct nl_dump_params * params,struct nl_object * filter)1211 void nl_cache_dump_filter(struct nl_cache *cache,
1212 struct nl_dump_params *params,
1213 struct nl_object *filter)
1214 {
1215 struct nl_dump_params params_copy;
1216 struct nl_object_ops *ops;
1217 struct nl_object *obj;
1218 int type;
1219
1220 NL_DBG(2, "Dumping cache %p <%s> with filter %p\n",
1221 cache, nl_cache_name(cache), filter);
1222
1223 if (!params) {
1224 /* It doesn't really make sense that @params is an optional parameter. In the
1225 * past, nl_cache_dump() was documented that the @params would be optional, so
1226 * try to save it.
1227 *
1228 * Note that this still isn't useful, because we don't set any dump option.
1229 * It only exists not to crash applications that wrongly pass %NULL here. */
1230 _nl_assert_not_reached ();
1231 params_copy = (struct nl_dump_params) {
1232 .dp_type = NL_DUMP_DETAILS,
1233 };
1234 params = ¶ms_copy;
1235 }
1236
1237 type = params->dp_type;
1238
1239 if (type > NL_DUMP_MAX || type < 0)
1240 BUG();
1241
1242 if (cache->c_ops == NULL)
1243 BUG();
1244
1245 ops = cache->c_ops->co_obj_ops;
1246 if (!ops->oo_dump[type])
1247 return;
1248
1249 if (params->dp_buf)
1250 memset(params->dp_buf, 0, params->dp_buflen);
1251
1252 nl_list_for_each_entry(obj, &cache->c_items, ce_list) {
1253 if (filter && !nl_object_match_filter(obj, filter))
1254 continue;
1255
1256 NL_DBG(4, "Dumping object %p...\n", obj);
1257 dump_from_ops(obj, params);
1258 }
1259 }
1260
1261 /** @} */
1262
1263 /**
1264 * @name Iterators
1265 * @{
1266 */
1267
1268 /**
1269 * Call a callback on each element of the cache.
1270 * @arg cache cache to iterate on
1271 * @arg cb callback function
1272 * @arg arg argument passed to callback function
1273 *
1274 * Calls a callback function \a cb on each element of the \a cache.
1275 * The argument \a arg is passed on the callback function.
1276 */
nl_cache_foreach(struct nl_cache * cache,void (* cb)(struct nl_object *,void *),void * arg)1277 void nl_cache_foreach(struct nl_cache *cache,
1278 void (*cb)(struct nl_object *, void *), void *arg)
1279 {
1280 nl_cache_foreach_filter(cache, NULL, cb, arg);
1281 }
1282
1283 /**
1284 * Call a callback on each element of the cache (filtered).
1285 * @arg cache cache to iterate on
1286 * @arg filter filter object
1287 * @arg cb callback function
1288 * @arg arg argument passed to callback function
1289 *
1290 * Calls a callback function \a cb on each element of the \a cache
1291 * that matches the \a filter. The argument \a arg is passed on
1292 * to the callback function.
1293 */
nl_cache_foreach_filter(struct nl_cache * cache,struct nl_object * filter,void (* cb)(struct nl_object *,void *),void * arg)1294 void nl_cache_foreach_filter(struct nl_cache *cache, struct nl_object *filter,
1295 void (*cb)(struct nl_object *, void *), void *arg)
1296 {
1297 struct nl_object *obj, *tmp;
1298
1299 if (cache->c_ops == NULL)
1300 BUG();
1301
1302 nl_list_for_each_entry_safe(obj, tmp, &cache->c_items, ce_list) {
1303 if (filter) {
1304 int diff = nl_object_match_filter(obj, filter);
1305
1306 NL_DBG(3, "%p<->%p object difference: %x\n",
1307 obj, filter, diff);
1308
1309 if (!diff)
1310 continue;
1311 }
1312
1313 /* Caller may hold obj for a long time */
1314 nl_object_get(obj);
1315
1316 cb(obj, arg);
1317
1318 nl_object_put(obj);
1319 }
1320 }
1321
1322 /** @} */
1323
1324 /** @} */
1325