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