1 /* SPDX-License-Identifier: LGPL-2.1-only */
2 /*
3 * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
4 */
5
6 /**
7 * @ingroup core_types
8 * @defgroup object Object (Cacheable)
9 *
10 * Generic object data type, for inheritance purposes to implement cacheable
11 * data types.
12 *
13 * Related sections in the development guide:
14 *
15 * @{
16 *
17 * Header
18 * ------
19 * ~~~~{.c}
20 * #include <netlink/object.h>
21 * ~~~~
22 */
23
24 #include <netlink-private/netlink.h>
25 #include <netlink/netlink.h>
26 #include <netlink/cache.h>
27 #include <netlink/object.h>
28 #include <netlink/utils.h>
29
obj_ops(struct nl_object * obj)30 static inline struct nl_object_ops *obj_ops(struct nl_object *obj)
31 {
32 if (!obj->ce_ops)
33 BUG();
34
35 return obj->ce_ops;
36 }
37
38 /**
39 * @name Object Creation/Deletion
40 * @{
41 */
42
43 /**
44 * Allocate a new object of kind specified by the operations handle
45 * @arg ops cache operations handle
46 * @return The new object or NULL
47 */
nl_object_alloc(struct nl_object_ops * ops)48 struct nl_object *nl_object_alloc(struct nl_object_ops *ops)
49 {
50 struct nl_object *new;
51
52 if (ops->oo_size < sizeof(*new))
53 BUG();
54
55 new = calloc(1, ops->oo_size);
56 if (!new)
57 return NULL;
58
59 new->ce_refcnt = 1;
60 nl_init_list_head(&new->ce_list);
61
62 new->ce_ops = ops;
63 if (ops->oo_constructor)
64 ops->oo_constructor(new);
65
66 NL_DBG(4, "Allocated new object %p\n", new);
67
68 return new;
69 }
70
71 /**
72 * Allocate new object of kind specified by the name
73 * @arg kind name of object type
74 * @arg result Result pointer
75 *
76 * @return 0 on success or a negative error code.
77 */
nl_object_alloc_name(const char * kind,struct nl_object ** result)78 int nl_object_alloc_name(const char *kind, struct nl_object **result)
79 {
80 struct nl_cache_ops *ops;
81
82 ops = nl_cache_ops_lookup_safe(kind);
83 if (!ops)
84 return -NLE_OPNOTSUPP;
85
86 *result = nl_object_alloc(ops->co_obj_ops);
87 nl_cache_ops_put(ops);
88 if (!*result)
89 return -NLE_NOMEM;
90
91 return 0;
92 }
93
94 struct nl_derived_object {
95 NLHDR_COMMON
96 char data;
97 };
98
99 /**
100 * Allocate a new object and copy all data from an existing object
101 * @arg obj object to inherite data from
102 * @return The new object or NULL.
103 */
nl_object_clone(struct nl_object * obj)104 struct nl_object *nl_object_clone(struct nl_object *obj)
105 {
106 struct nl_object *new;
107 struct nl_object_ops *ops;
108 int doff = offsetof(struct nl_derived_object, data);
109 int size;
110
111 if (!obj)
112 return NULL;
113
114 ops = obj_ops(obj);
115 new = nl_object_alloc(ops);
116 if (!new)
117 return NULL;
118
119 size = ops->oo_size - doff;
120 if (size < 0)
121 BUG();
122
123 new->ce_ops = obj->ce_ops;
124 new->ce_msgtype = obj->ce_msgtype;
125 new->ce_mask = obj->ce_mask;
126
127 if (size)
128 memcpy((char *)new + doff, (char *)obj + doff, size);
129
130 /* Note that the base implementation already initializes @new via memcpy().
131 * That means, simple fields don't need to be handled via oo_clone().
132 * However, this is only a shallow-copy, so oo_clone() MUST fix all
133 * pointer values accordingly. */
134
135 if (ops->oo_clone) {
136 if (ops->oo_clone(new, obj) < 0) {
137 nl_object_free(new);
138 return NULL;
139 }
140 } else if (size && ops->oo_free_data)
141 BUG();
142
143 return new;
144 }
145
146 /**
147 * Merge a cacheable object
148 * @arg dst object to be merged into
149 * @arg src new object to be merged into dst
150 *
151 * @return 0 or a negative error code.
152 */
nl_object_update(struct nl_object * dst,struct nl_object * src)153 int nl_object_update(struct nl_object *dst, struct nl_object *src)
154 {
155 struct nl_object_ops *ops = obj_ops(dst);
156
157 if (ops->oo_update)
158 return ops->oo_update(dst, src);
159
160 return -NLE_OPNOTSUPP;
161 }
162
163 /**
164 * Free a cacheable object
165 * @arg obj object to free
166 *
167 * @return 0 or a negative error code.
168 */
nl_object_free(struct nl_object * obj)169 void nl_object_free(struct nl_object *obj)
170 {
171 struct nl_object_ops *ops;
172
173 if (!obj)
174 return;
175
176 ops = obj_ops(obj);
177
178 if (obj->ce_refcnt > 0)
179 NL_DBG(1, "Warning: Freeing object in use...\n");
180
181 if (obj->ce_cache)
182 nl_cache_remove(obj);
183
184 if (ops->oo_free_data)
185 ops->oo_free_data(obj);
186
187 NL_DBG(4, "Freed object %p\n", obj);
188
189 free(obj);
190 }
191
192 /** @} */
193
194 /**
195 * @name Reference Management
196 * @{
197 */
198
199 /**
200 * Acquire a reference on a object
201 * @arg obj object to acquire reference from
202 */
nl_object_get(struct nl_object * obj)203 void nl_object_get(struct nl_object *obj)
204 {
205 obj->ce_refcnt++;
206 NL_DBG(4, "New reference to object %p, total %d\n",
207 obj, obj->ce_refcnt);
208 }
209
210 /**
211 * Release a reference from an object
212 * @arg obj object to release reference from
213 */
nl_object_put(struct nl_object * obj)214 void nl_object_put(struct nl_object *obj)
215 {
216 if (!obj)
217 return;
218
219 obj->ce_refcnt--;
220 NL_DBG(4, "Returned object reference %p, %d remaining\n",
221 obj, obj->ce_refcnt);
222
223 if (obj->ce_refcnt < 0)
224 BUG();
225
226 if (obj->ce_refcnt <= 0)
227 nl_object_free(obj);
228 }
229
230 /**
231 * Check whether this object is used by multiple users
232 * @arg obj object to check
233 * @return true or false
234 */
nl_object_shared(struct nl_object * obj)235 int nl_object_shared(struct nl_object *obj)
236 {
237 return obj->ce_refcnt > 1;
238 }
239
240 /** @} */
241
242 /**
243 * @name Marks
244 * @{
245 */
246
247 /**
248 * Add mark to object
249 * @arg obj Object to mark
250 */
nl_object_mark(struct nl_object * obj)251 void nl_object_mark(struct nl_object *obj)
252 {
253 obj->ce_flags |= NL_OBJ_MARK;
254 }
255
256 /**
257 * Remove mark from object
258 * @arg obj Object to unmark
259 */
nl_object_unmark(struct nl_object * obj)260 void nl_object_unmark(struct nl_object *obj)
261 {
262 obj->ce_flags &= ~NL_OBJ_MARK;
263 }
264
265 /**
266 * Return true if object is marked
267 * @arg obj Object to check
268 * @return true if object is marked, otherwise false
269 */
nl_object_is_marked(struct nl_object * obj)270 int nl_object_is_marked(struct nl_object *obj)
271 {
272 return (obj->ce_flags & NL_OBJ_MARK);
273 }
274
275 /** @} */
276
277 /**
278 * @name Utillities
279 * @{
280 */
281
282 /**
283 * Dump this object according to the specified parameters
284 * @arg obj object to dump
285 * @arg params dumping parameters
286 */
nl_object_dump(struct nl_object * obj,struct nl_dump_params * params)287 void nl_object_dump(struct nl_object *obj, struct nl_dump_params *params)
288 {
289 if (params->dp_buf)
290 memset(params->dp_buf, 0, params->dp_buflen);
291
292 dump_from_ops(obj, params);
293 }
294
nl_object_dump_buf(struct nl_object * obj,char * buf,size_t len)295 void nl_object_dump_buf(struct nl_object *obj, char *buf, size_t len)
296 {
297 struct nl_dump_params dp = {
298 .dp_buf = buf,
299 .dp_buflen = len,
300 };
301
302 nl_object_dump(obj, &dp);
303 }
304
305 /**
306 * Check if the identifiers of two objects are identical
307 * @arg a an object
308 * @arg b another object of same type
309 *
310 * @return true if both objects have equal identifiers, otherwise false.
311 */
nl_object_identical(struct nl_object * a,struct nl_object * b)312 int nl_object_identical(struct nl_object *a, struct nl_object *b)
313 {
314 struct nl_object_ops *ops;
315 uint64_t req_attrs_a;
316 uint64_t req_attrs_b;
317
318 if (a == b)
319 return 1;
320
321 /* Both objects must be of same type */
322 ops = obj_ops(a);
323 if (ops != obj_ops(b))
324 return 0;
325
326 /* Can't judge unless we can compare */
327 if (ops->oo_compare == NULL)
328 return 0;
329
330 if (ops->oo_id_attrs_get) {
331 req_attrs_a = ops->oo_id_attrs_get(a);
332 req_attrs_b = ops->oo_id_attrs_get(b);
333 } else if (ops->oo_id_attrs) {
334 req_attrs_a = ops->oo_id_attrs;
335 req_attrs_b = req_attrs_a;
336 } else {
337 req_attrs_a = UINT64_MAX;
338 req_attrs_b = req_attrs_a;
339 }
340
341 req_attrs_a &= a->ce_mask;
342 req_attrs_b &= b->ce_mask;
343
344 /* Both objects must provide all required attributes to uniquely
345 * identify an object */
346 if (req_attrs_a != req_attrs_b)
347 return 0;
348
349 return !(ops->oo_compare(a, b, req_attrs_a, ID_COMPARISON));
350 }
351
352 /**
353 * Compute bitmask representing difference in attribute values
354 * @arg a an object
355 * @arg b another object of same type
356 *
357 * The bitmask returned is specific to an object type, each bit set represents
358 * an attribute which mismatches in either of the two objects. Unavailability
359 * of an attribute in one object and presence in the other is regarded a
360 * mismatch as well.
361 *
362 * @return Bitmask describing differences or 0 if they are completely identical.
363 */
nl_object_diff64(struct nl_object * a,struct nl_object * b)364 uint64_t nl_object_diff64(struct nl_object *a, struct nl_object *b)
365 {
366 struct nl_object_ops *ops = obj_ops(a);
367
368 if (ops != obj_ops(b) || ops->oo_compare == NULL)
369 return UINT64_MAX;
370
371 return ops->oo_compare(a, b, UINT64_MAX, 0);
372 }
373
374 /**
375 * Compute 32-bit bitmask representing difference in attribute values
376 * @arg a an object
377 * @arg b another object of same type
378 *
379 * The bitmask returned is specific to an object type, each bit set represents
380 * an attribute which mismatches in either of the two objects. Unavailability
381 * of an attribute in one object and presence in the other is regarded a
382 * mismatch as well.
383 *
384 * @return Bitmask describing differences or 0 if they are completely identical.
385 * 32nd bit indicates if higher bits from the 64-bit compare were
386 * different.
387 */
nl_object_diff(struct nl_object * a,struct nl_object * b)388 uint32_t nl_object_diff(struct nl_object *a, struct nl_object *b)
389 {
390 uint64_t diff;
391
392 diff = nl_object_diff64(a, b);
393
394 return (diff & ~((uint64_t) 0xFFFFFFFF))
395 ? (uint32_t) diff | (1 << 31)
396 : (uint32_t) diff;
397 }
398
399 /**
400 * Match a filter against an object
401 * @arg obj object to check
402 * @arg filter object of same type acting as filter
403 *
404 * @return 1 if the object matches the filter or 0
405 * if no filter procedure is available or if the
406 * filter does not match.
407 */
nl_object_match_filter(struct nl_object * obj,struct nl_object * filter)408 int nl_object_match_filter(struct nl_object *obj, struct nl_object *filter)
409 {
410 struct nl_object_ops *ops = obj_ops(obj);
411
412 if (ops != obj_ops(filter) || ops->oo_compare == NULL)
413 return 0;
414
415 return !(ops->oo_compare(obj, filter, filter->ce_mask,
416 LOOSE_COMPARISON));
417 }
418
419 /**
420 * Convert bitmask of attributes to a character string
421 * @arg obj object of same type as attribute bitmask
422 * @arg attrs bitmask of attribute types
423 * @arg buf destination buffer
424 * @arg len length of destination buffer
425 *
426 * Converts the bitmask of attribute types into a list of attribute
427 * names separated by comas.
428 *
429 * @return destination buffer.
430 */
nl_object_attrs2str(struct nl_object * obj,uint32_t attrs,char * buf,size_t len)431 char *nl_object_attrs2str(struct nl_object *obj, uint32_t attrs,
432 char *buf, size_t len)
433 {
434 struct nl_object_ops *ops = obj_ops(obj);
435
436 if (ops->oo_attrs2str != NULL)
437 return ops->oo_attrs2str(attrs, buf, len);
438 else {
439 memset(buf, 0, len);
440 return buf;
441 }
442 }
443
444 /**
445 * Return list of attributes present in an object
446 * @arg obj an object
447 * @arg buf destination buffer
448 * @arg len length of destination buffer
449 *
450 * @return destination buffer.
451 */
nl_object_attr_list(struct nl_object * obj,char * buf,size_t len)452 char *nl_object_attr_list(struct nl_object *obj, char *buf, size_t len)
453 {
454 return nl_object_attrs2str(obj, obj->ce_mask, buf, len);
455 }
456
457 /**
458 * Generate object hash key
459 * @arg obj the object
460 * @arg hashkey destination buffer to be used for key stream
461 * @arg hashtbl_sz hash table size
462 *
463 * @return hash key in destination buffer
464 */
nl_object_keygen(struct nl_object * obj,uint32_t * hashkey,uint32_t hashtbl_sz)465 void nl_object_keygen(struct nl_object *obj, uint32_t *hashkey,
466 uint32_t hashtbl_sz)
467 {
468 struct nl_object_ops *ops = obj_ops(obj);
469
470 if (ops->oo_keygen)
471 ops->oo_keygen(obj, hashkey, hashtbl_sz);
472 else
473 *hashkey = 0;
474
475 return;
476 }
477
478 /** @} */
479
480 /**
481 * @name Attributes
482 * @{
483 */
484
485 /**
486 * Return number of references held
487 * @arg obj object
488 *
489 * @return The number of references held to this object
490 */
nl_object_get_refcnt(struct nl_object * obj)491 int nl_object_get_refcnt(struct nl_object *obj)
492 {
493 return obj->ce_refcnt;
494 }
495
496 /**
497 * Return cache the object is associated with
498 * @arg obj object
499 *
500 * @note The returned pointer is not protected with a reference counter,
501 * it is your responsibility.
502 *
503 * @return Pointer to cache or NULL if not associated with a cache.
504 */
nl_object_get_cache(struct nl_object * obj)505 struct nl_cache *nl_object_get_cache(struct nl_object *obj)
506 {
507 return obj->ce_cache;
508 }
509
510 /**
511 * Return the object's type
512 * @arg obj object
513 *
514 * FIXME: link to list of object types
515 *
516 * @return Name of the object type
517 */
nl_object_get_type(const struct nl_object * obj)518 const char *nl_object_get_type(const struct nl_object *obj)
519 {
520 if (!obj->ce_ops)
521 BUG();
522
523 return obj->ce_ops->oo_name;
524 }
525
526 /**
527 * Return the netlink message type the object was derived from
528 * @arg obj object
529 *
530 * @return Netlink message type or 0.
531 */
nl_object_get_msgtype(const struct nl_object * obj)532 int nl_object_get_msgtype(const struct nl_object *obj)
533 {
534 return obj->ce_msgtype;
535 }
536
537 /**
538 * Return object operations structure
539 * @arg obj object
540 *
541 * @return Pointer to the object operations structure
542 */
nl_object_get_ops(const struct nl_object * obj)543 struct nl_object_ops *nl_object_get_ops(const struct nl_object *obj)
544 {
545 return obj->ce_ops;
546 }
547
548 /**
549 * Return object id attribute mask
550 * @arg obj object
551 *
552 * @return object id attribute mask
553 */
nl_object_get_id_attrs(struct nl_object * obj)554 uint32_t nl_object_get_id_attrs(struct nl_object *obj)
555 {
556 struct nl_object_ops *ops = obj_ops(obj);
557 uint32_t id_attrs;
558
559 if (!ops)
560 return 0;
561
562 if (ops->oo_id_attrs_get)
563 id_attrs = ops->oo_id_attrs_get(obj);
564 else
565 id_attrs = ops->oo_id_attrs;
566
567 return id_attrs;
568 }
569
570 /** @} */
571
572 /** @} */
573