xref: /aosp_15_r20/external/libnl/lib/route/link/api.c (revision 4dc78e53d49367fa8e61b07018507c90983a077d)
1 /* SPDX-License-Identifier: LGPL-2.1-only */
2 /*
3  * Copyright (c) 2003-2008 Thomas Graf <[email protected]>
4  */
5 
6 /**
7  * @ingroup link
8  * @defgroup link_API Link Modules API
9  * @brief API for modules implementing specific link types/semantics.
10  *
11  * @par 1) Registering/Unregistering a new link info type
12  * @code
13  * static struct rtnl_link_info_ops vlan_info_ops = {
14  * 	.io_name		= "vlan",
15  * 	.io_alloc		= vlan_alloc,
16  * 	.io_parse		= vlan_parse,
17  * 	.io_dump[NL_DUMP_BRIEF]	= vlan_dump_brief,
18  * 	.io_dump[NL_DUMP_FULL]	= vlan_dump_full,
19  * 	.io_free		= vlan_free,
20  * };
21  *
22  * static void _nl_init vlan_init(void)
23  * {
24  * 	rtnl_link_register_info(&vlan_info_ops);
25  * }
26  *
27  * static void _nl_exit vlan_exit(void)
28  * {
29  * 	rtnl_link_unregister_info(&vlan_info_ops);
30  * }
31  * @endcode
32  *
33  * @{
34  */
35 
36 #include "nl-default.h"
37 
38 #include <netlink/netlink.h>
39 #include <netlink/utils.h>
40 #include <netlink/route/link.h>
41 
42 #include "nl-route.h"
43 #include "link-api.h"
44 
45 static NL_LIST_HEAD(info_ops);
46 
47 /* lock protecting info_ops and af_ops */
48 static NL_RW_LOCK(info_lock);
49 
__rtnl_link_info_ops_lookup(const char * name)50 static struct rtnl_link_info_ops *__rtnl_link_info_ops_lookup(const char *name)
51 {
52 	struct rtnl_link_info_ops *ops;
53 
54 	nl_list_for_each_entry(ops, &info_ops, io_list)
55 		if (!strcmp(ops->io_name, name))
56 			return ops;
57 
58 	return NULL;
59 }
60 
61 /**
62  * @name Link Info Modules
63  * @{
64  */
65 
66 /**
67  * Return operations of a specific link info type
68  * @arg name		Name of link info type.
69  *
70  * @note The returned pointer must be given back using rtnl_link_info_ops_put()
71  *
72  * @return Pointer to operations or NULL if unavailable.
73  */
rtnl_link_info_ops_lookup(const char * name)74 struct rtnl_link_info_ops *rtnl_link_info_ops_lookup(const char *name)
75 {
76 	struct rtnl_link_info_ops *ops;
77 
78 	nl_write_lock(&info_lock);
79 	if ((ops = __rtnl_link_info_ops_lookup(name)))
80 		ops->io_refcnt++;
81 	nl_write_unlock(&info_lock);
82 
83 	return ops;
84 }
85 
86 /**
87  * Take reference to a set of operations.
88  * @arg ops		Link info operations.
89  */
rtnl_link_info_ops_get(struct rtnl_link_info_ops * ops)90 void rtnl_link_info_ops_get(struct rtnl_link_info_ops *ops)
91 {
92 	if (!ops)
93 		return;
94 
95 	nl_write_lock(&info_lock);
96 	ops->io_refcnt++;
97 	nl_write_unlock(&info_lock);
98 }
99 
100 /**
101  * Give back reference to a set of operations.
102  * @arg ops		Link info operations.
103  */
rtnl_link_info_ops_put(struct rtnl_link_info_ops * ops)104 void rtnl_link_info_ops_put(struct rtnl_link_info_ops *ops)
105 {
106 	if (!ops)
107 		return;
108 
109 	nl_write_lock(&info_lock);
110 	_nl_assert(ops->io_refcnt > 0);
111 	ops->io_refcnt--;
112 	nl_write_unlock(&info_lock);
113 }
114 
115 /**
116  * Register operations for a link info type
117  * @arg ops		Link info operations
118  *
119  * This function must be called by modules implementing a specific link
120  * info type. It will make the operations implemented by the module
121  * available for everyone else.
122  *
123  * @return 0 on success or a negative error code.
124  * @return -NLE_INVAL Link info name not specified.
125  * @return -NLE_EXIST Operations for address family already registered.
126  */
rtnl_link_register_info(struct rtnl_link_info_ops * ops)127 int rtnl_link_register_info(struct rtnl_link_info_ops *ops)
128 {
129 	int err = 0;
130 
131 	if (ops->io_name == NULL)
132 		return -NLE_INVAL;
133 
134 	nl_write_lock(&info_lock);
135 	if (__rtnl_link_info_ops_lookup(ops->io_name)) {
136 		err = -NLE_EXIST;
137 		goto errout;
138 	}
139 
140 	NL_DBG(1, "Registered link info operations %s\n", ops->io_name);
141 
142 	nl_list_add_tail(&ops->io_list, &info_ops);
143 errout:
144 	nl_write_unlock(&info_lock);
145 
146 	return err;
147 }
148 
149 /**
150  * Unregister operations for a link info type
151  * @arg ops		Link info operations
152  *
153  * This function must be called if a module implementing a specific link
154  * info type is unloaded or becomes unavailable. It must provide a
155  * set of operations which have previously been registered using
156  * rtnl_link_register_info().
157  *
158  * @return 0 on success or a negative error code
159  * @return _NLE_OPNOTSUPP Link info operations not registered.
160  * @return -NLE_BUSY Link info operations still in use.
161  */
rtnl_link_unregister_info(struct rtnl_link_info_ops * ops)162 int rtnl_link_unregister_info(struct rtnl_link_info_ops *ops)
163 {
164 	struct rtnl_link_info_ops *t;
165 	int err = -NLE_OPNOTSUPP;
166 
167 	nl_write_lock(&info_lock);
168 
169 	nl_list_for_each_entry(t, &info_ops, io_list) {
170 		if (t == ops) {
171 			_nl_assert(t->io_refcnt >= 0);
172 			if (t->io_refcnt > 0) {
173 				err = -NLE_BUSY;
174 				goto errout;
175 			}
176 
177 			nl_list_del(&t->io_list);
178 
179 			NL_DBG(1, "Unregistered link info operations %s\n",
180 				ops->io_name);
181 			err = 0;
182 			goto errout;
183 		}
184 	}
185 
186 errout:
187 	nl_write_unlock(&info_lock);
188 
189 	return err;
190 }
191 
192 /** @} */
193 
194 /**
195  * @name Link Address Family Modules
196  * @{
197  */
198 
199 static struct rtnl_link_af_ops *af_ops[AF_MAX];
200 
201 /**
202  * Return operations of a specific link address family
203  * @arg family		Address family
204  *
205  * @note The returned pointer must be given back using rtnl_link_af_ops_put()
206  *
207  * @return Pointer to operations or NULL if unavailable.
208  */
rtnl_link_af_ops_lookup(const unsigned int family)209 struct rtnl_link_af_ops *rtnl_link_af_ops_lookup(const unsigned int family)
210 {
211 	if (family == AF_UNSPEC || family >= AF_MAX)
212 		return NULL;
213 
214 	nl_write_lock(&info_lock);
215 	if (af_ops[family])
216 		af_ops[family]->ao_refcnt++;
217 	nl_write_unlock(&info_lock);
218 
219 	return af_ops[family];
220 }
221 
222 /**
223  * Give back reference to a set of operations.
224  * @arg ops		Address family operations.
225  */
rtnl_link_af_ops_put(struct rtnl_link_af_ops * ops)226 void rtnl_link_af_ops_put(struct rtnl_link_af_ops *ops)
227 {
228 	if (ops) {
229 		nl_write_lock(&info_lock);
230 		ops->ao_refcnt--;
231 		nl_write_unlock(&info_lock);
232 	}
233 }
234 
235 /**
236  * Allocate and return data buffer for link address family modules
237  * @arg link		Link object
238  * @arg ops		Address family operations
239  *
240  * This function must be called by link address family modules in all
241  * cases where the API does not provide the data buffer as argument
242  * already. This typically includes set functions the module provides.
243  * Calling this function is strictly required to ensure proper allocation
244  * of the buffer upon first use. Link objects will NOT proactively
245  * allocate a data buffer for each registered link address family.
246  *
247  * @return Pointer to data buffer or NULL on error.
248  */
rtnl_link_af_alloc(struct rtnl_link * link,const struct rtnl_link_af_ops * ops)249 void *rtnl_link_af_alloc(struct rtnl_link *link,
250 			 const struct rtnl_link_af_ops *ops)
251 {
252 	int family;
253 
254 	if (!link || !ops)
255 		BUG();
256 
257 	family = ops->ao_family;
258 
259 	if (!link->l_af_data[family]) {
260 		if (!ops->ao_alloc)
261 			BUG();
262 
263 		link->l_af_data[family] = ops->ao_alloc(link);
264 		if (!link->l_af_data[family])
265 			return NULL;
266 	}
267 
268 	return link->l_af_data[family];
269 }
270 
271 /**
272  * Return data buffer for link address family modules
273  * @arg link		Link object
274  * @arg ops		Address family operations
275  *
276  * This function returns a pointer to the data buffer for the specified link
277  * address family module or NULL if the buffer was not allocated yet. This
278  * function is typically used by get functions of modules which are not
279  * interested in having the data buffer allocated if no values have been set
280  * yet.
281  *
282  * @return Pointer to data buffer or NULL on error.
283  */
rtnl_link_af_data(const struct rtnl_link * link,const struct rtnl_link_af_ops * ops)284 void *rtnl_link_af_data(const struct rtnl_link *link,
285 			const struct rtnl_link_af_ops *ops)
286 {
287 	if (!link || !ops)
288 		BUG();
289 
290 	return link->l_af_data[ops->ao_family];
291 }
292 
293 /**
294  * Register operations for a link address family
295  * @arg ops		Address family operations
296  *
297  * This function must be called by modules implementing a specific link
298  * address family. It will make the operations implemented by the module
299  * available for everyone else.
300  *
301  * @return 0 on success or a negative error code.
302  * @return -NLE_INVAL Address family is out of range (0..AF_MAX)
303  * @return -NLE_EXIST Operations for address family already registered.
304  */
rtnl_link_af_register(struct rtnl_link_af_ops * ops)305 int rtnl_link_af_register(struct rtnl_link_af_ops *ops)
306 {
307 	int err = 0;
308 
309 	if (ops->ao_family == AF_UNSPEC || ops->ao_family >= AF_MAX)
310 		return -NLE_INVAL;
311 
312 	nl_write_lock(&info_lock);
313 	if (af_ops[ops->ao_family]) {
314 		err = -NLE_EXIST;
315 		goto errout;
316 	}
317 
318 	ops->ao_refcnt = 0;
319 	af_ops[ops->ao_family] = ops;
320 
321 	NL_DBG(1, "Registered link address family operations %u\n",
322 		ops->ao_family);
323 
324 errout:
325 	nl_write_unlock(&info_lock);
326 
327 	return err;
328 }
329 
330 /**
331  * Unregister operations for a link address family
332  * @arg ops		Address family operations
333  *
334  * This function must be called if a module implementing a specific link
335  * address family is unloaded or becomes unavailable. It must provide a
336  * set of operations which have previously been registered using
337  * rtnl_link_af_register().
338  *
339  * @return 0 on success or a negative error code
340  * @return -NLE_INVAL ops is NULL
341  * @return -NLE_OBJ_NOTFOUND Address family operations not registered.
342  * @return -NLE_BUSY Address family operations still in use.
343  */
rtnl_link_af_unregister(struct rtnl_link_af_ops * ops)344 int rtnl_link_af_unregister(struct rtnl_link_af_ops *ops)
345 {
346 	int err = -NLE_INVAL;
347 
348 	if (!ops)
349 		return err;
350 
351 	nl_write_lock(&info_lock);
352 	if (!af_ops[ops->ao_family]) {
353 		err = -NLE_OBJ_NOTFOUND;
354 		goto errout;
355 	}
356 
357 	if (ops->ao_refcnt > 0) {
358 		err = -NLE_BUSY;
359 		goto errout;
360 	}
361 
362 	af_ops[ops->ao_family] = NULL;
363 
364 	NL_DBG(1, "Unregistered link address family operations %u\n",
365 		ops->ao_family);
366 
367 errout:
368 	nl_write_unlock(&info_lock);
369 
370 	return err;
371 }
372 
373 /**
374  * Compare af data for a link address family
375  * @arg a		Link object a
376  * @arg b		Link object b
377  * @arg family		af data family
378  *
379  * This function will compare af_data between two links
380  * a and b of family given by arg family
381  *
382  * @return 0 if address family specific data matches or is not present
383  * or != 0 if it mismatches.
384  */
rtnl_link_af_data_compare(struct rtnl_link * a,struct rtnl_link * b,int family)385 int rtnl_link_af_data_compare(struct rtnl_link *a, struct rtnl_link *b,
386 			      int family)
387 {
388 	struct rtnl_link_af_ops *af_ops;
389 	int ret = 0;
390 
391 	if (!a->l_af_data[family] && !b->l_af_data[family])
392 		return 0;
393 
394 	if (!a->l_af_data[family] || !b->l_af_data[family])
395 		return ~0;
396 
397 	af_ops = rtnl_link_af_ops_lookup(family);
398 	if (!af_ops)
399 		return ~0;
400 
401 	if (af_ops->ao_compare == NULL) {
402 		ret = ~0;
403 		goto out;
404 	}
405 
406 	ret = af_ops->ao_compare(a, b, family, ~0, 0);
407 
408 out:
409 	rtnl_link_af_ops_put(af_ops);
410 
411 	return ret;
412 }
413 
414 /**
415  * Compare link info data
416  * @arg a              Link object a
417  * @arg b              Link object b
418  *
419  * This function will compare link_info data between two links
420  * a and b
421  *
422  * @return 0 if link_info data matches or is not present
423  * or != 0 if it mismatches.
424  */
rtnl_link_info_data_compare(struct rtnl_link * a,struct rtnl_link * b,int flags)425 int rtnl_link_info_data_compare(struct rtnl_link *a, struct rtnl_link *b, int flags)
426 {
427 	if (a->l_info_ops != b->l_info_ops)
428 		return ~0;
429 
430 	if (!a->l_info_ops || !a->l_info_ops->io_compare)
431 		return 0;
432 
433 	return a->l_info_ops->io_compare(a, b, flags);
434 }
435 
436 /** @} */
437 
438 /** @} */
439 
440