xref: /aosp_15_r20/external/libnl/lib/route/netconf.c (revision 4dc78e53d49367fa8e61b07018507c90983a077d)
1 /* SPDX-License-Identifier: LGPL-2.1-only */
2 /*
3  * Copyright (c) 2017 David Ahern <[email protected]>
4  */
5 
6 /**
7  * @ingroup rtnl
8  * @defgroup netconf Netconf
9  * @brief
10  *
11  * @{
12  */
13 
14 #include "nl-default.h"
15 
16 #include <linux/netconf.h>
17 #include <linux/socket.h>
18 
19 #include <netlink/netlink.h>
20 #include <netlink/utils.h>
21 #include <netlink/route/netconf.h>
22 #include <netlink/hashtable.h>
23 
24 #include "nl-route.h"
25 #include "nl-priv-dynamic-core/nl-core.h"
26 #include "nl-priv-dynamic-core/cache-api.h"
27 #include "nl-priv-dynamic-core/object-api.h"
28 
29 /** @cond SKIP */
30 #define NETCONF_ATTR_FAMILY		0x0001
31 #define NETCONF_ATTR_IFINDEX		0x0002
32 #define NETCONF_ATTR_RP_FILTER		0x0004
33 #define NETCONF_ATTR_FWDING		0x0008
34 #define NETCONF_ATTR_MC_FWDING		0x0010
35 #define NETCONF_ATTR_PROXY_NEIGH	0x0020
36 #define NETCONF_ATTR_IGNORE_RT_LINKDWN	0x0040
37 #define NETCONF_ATTR_INPUT		0x0080
38 
39 struct rtnl_netconf
40 {
41 	NLHDR_COMMON
42 
43 	int	family;
44 	int	ifindex;
45 	int	rp_filter;
46 	int	forwarding;
47 	int	mc_forwarding;
48 	int	proxy_neigh;
49 	int	ignore_routes_linkdown;
50 	int	input;
51 };
52 
53 static struct nl_cache_ops rtnl_netconf_ops;
54 static struct nl_object_ops netconf_obj_ops;
55 /** @endcond */
56 
57 static struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = {
58 	[NETCONFA_IFINDEX]	 = { .type = NLA_S32 },
59 	[NETCONFA_FORWARDING]	 = { .type = NLA_S32 },
60 	[NETCONFA_MC_FORWARDING] = { .type = NLA_S32 },
61 	[NETCONFA_RP_FILTER]	 = { .type = NLA_S32 },
62 	[NETCONFA_PROXY_NEIGH]	 = { .type = NLA_S32 },
63 	[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]  = { .type = NLA_S32 },
64 };
65 
66 static struct nla_policy devconf_ipv6_policy[NETCONFA_MAX+1] = {
67 	[NETCONFA_IFINDEX]	 = { .type = NLA_S32 },
68 	[NETCONFA_FORWARDING]	 = { .type = NLA_S32 },
69 	[NETCONFA_MC_FORWARDING] = { .type = NLA_S32 },
70 	[NETCONFA_PROXY_NEIGH]	 = { .type = NLA_S32 },
71 	[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]  = { .type = NLA_S32 },
72 };
73 
74 static struct nla_policy devconf_mpls_policy[NETCONFA_MAX+1] = {
75 	[NETCONFA_IFINDEX]	 = { .type = NLA_S32 },
76 	[NETCONFA_INPUT]	 = { .type = NLA_S32 },
77 };
78 
rtnl_netconf_alloc(void)79 static struct rtnl_netconf *rtnl_netconf_alloc(void)
80 {
81 	return (struct rtnl_netconf *) nl_object_alloc(&netconf_obj_ops);
82 }
83 
netconf_msg_parser(struct nl_cache_ops * ops,struct sockaddr_nl * who,struct nlmsghdr * nlh,struct nl_parser_param * pp)84 static int netconf_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
85 			      struct nlmsghdr *nlh, struct nl_parser_param *pp)
86 {
87 	struct nlattr *tb[NETCONFA_MAX+1], *attr;
88 	struct rtnl_netconf *nc;
89 	struct netconfmsg *ncm;
90 	int err;
91 
92 	ncm = nlmsg_data(nlh);
93 	switch (ncm->ncm_family) {
94 	case AF_INET:
95 		err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX,
96 				  devconf_ipv4_policy);
97 		if (err < 0)
98 			return err;
99 		break;
100 	case AF_INET6:
101 		err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX,
102 				  devconf_ipv6_policy);
103 		if (err < 0)
104 			return err;
105 		break;
106 	case AF_MPLS:
107 		err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX,
108 				  devconf_mpls_policy);
109 		if (err < 0)
110 			return err;
111 		break;
112 	default:
113 		printf("unexpected netconf family: %d\n", ncm->ncm_family);
114 		return -1;
115 	}
116 
117 	if (!tb[NETCONFA_IFINDEX])
118 		return -1;
119 
120 	nc = rtnl_netconf_alloc();
121 	if (!nc)
122 		return -NLE_NOMEM;
123 
124 	nc->ce_msgtype = nlh->nlmsg_type;
125 	nc->family = ncm->ncm_family;
126 	nc->ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]);
127 
128 	nc->ce_mask = NETCONF_ATTR_FAMILY | NETCONF_ATTR_IFINDEX;
129 
130 
131 	if (tb[NETCONFA_RP_FILTER]) {
132 		attr = tb[NETCONFA_RP_FILTER];
133 		nc->rp_filter = nla_get_s32(attr);
134 		nc->ce_mask |= NETCONF_ATTR_RP_FILTER;
135 	}
136 
137 	if (tb[NETCONFA_FORWARDING]) {
138 		attr = tb[NETCONFA_FORWARDING];
139 		nc->forwarding = nla_get_s32(attr);
140 		nc->ce_mask |= NETCONF_ATTR_FWDING;
141 	}
142 
143 	if (tb[NETCONFA_MC_FORWARDING]) {
144 		attr = tb[NETCONFA_MC_FORWARDING];
145 		nc->mc_forwarding = nla_get_s32(attr);
146 		nc->ce_mask |= NETCONF_ATTR_MC_FWDING;
147 	}
148 
149 	if (tb[NETCONFA_PROXY_NEIGH]) {
150 		attr = tb[NETCONFA_PROXY_NEIGH];
151 		nc->proxy_neigh = nla_get_s32(attr);
152 		nc->ce_mask |= NETCONF_ATTR_PROXY_NEIGH;
153 	}
154 
155 	if (tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]) {
156 		attr = tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN];
157 		nc->ignore_routes_linkdown = nla_get_s32(attr);
158 		nc->ce_mask |= NETCONF_ATTR_IGNORE_RT_LINKDWN;
159 	}
160 
161 	if (tb[NETCONFA_INPUT]) {
162 		attr = tb[NETCONFA_INPUT];
163 		nc->input = nla_get_s32(attr);
164 		nc->ce_mask |= NETCONF_ATTR_INPUT;
165 	}
166 
167 	err = pp->pp_cb((struct nl_object *) nc, pp);
168 
169 	rtnl_netconf_put(nc);
170 	return err;
171 }
172 
netconf_request_update(struct nl_cache * cache,struct nl_sock * sk)173 static int netconf_request_update(struct nl_cache *cache, struct nl_sock *sk)
174 {
175 	struct netconfmsg nc = {
176 		.ncm_family = cache->c_iarg1,
177 	};
178 
179 	return nl_send_simple(sk, RTM_GETNETCONF, NLM_F_DUMP, &nc, sizeof(nc));
180 }
181 
netconf_dump_line(struct nl_object * obj,struct nl_dump_params * p)182 static void netconf_dump_line(struct nl_object *obj, struct nl_dump_params *p)
183 {
184 	struct rtnl_netconf *nc = (struct rtnl_netconf *) obj;
185 	struct nl_cache *link_cache;
186 	char buf[64];
187 
188 	switch(nc->family) {
189 	case AF_INET:
190 		nl_dump(p, "ipv4 ");
191 		break;
192 	case AF_INET6:
193 		nl_dump(p, "ipv6 ");
194 		break;
195 	case AF_MPLS:
196 		nl_dump(p, "mpls ");
197 		break;
198 	default:
199 		return;
200 	}
201 
202 	switch(nc->ifindex) {
203 	case NETCONFA_IFINDEX_ALL:
204 		nl_dump(p, "all ");
205 		break;
206 	case NETCONFA_IFINDEX_DEFAULT:
207 		nl_dump(p, "default ");
208 		break;
209 	default:
210 		link_cache = nl_cache_mngt_require_safe("route/link");
211 		if (link_cache) {
212 			nl_dump(p, "dev %s ",
213 				rtnl_link_i2name(link_cache, nc->ifindex,
214 						 buf, sizeof(buf)));
215 			nl_cache_put(link_cache);
216 		} else
217 			nl_dump(p, "dev %d ", nc->ifindex);
218 	}
219 
220 	if (nc->ce_mask & NETCONF_ATTR_FWDING) {
221 		nl_dump(p, "forwarding %s ",
222 			nc->forwarding ? "on" : "off");
223 	}
224 
225 	if (nc->ce_mask & NETCONF_ATTR_RP_FILTER) {
226 		if (nc->rp_filter == 0)
227 			nl_dump(p, "rp_filter off ");
228 		else if (nc->rp_filter == 1)
229 			nl_dump(p, "rp_filter strict ");
230 		else if (nc->rp_filter == 2)
231 			nl_dump(p, "rp_filter loose ");
232 		else
233 			nl_dump(p, "rp_filter unknown-mode ");
234 	}
235 
236 	if (nc->ce_mask & NETCONF_ATTR_MC_FWDING) {
237 		nl_dump(p, "mc_forwarding %s ",
238 			nc->mc_forwarding ? "on" : "off");
239 	}
240 
241 	if (nc->ce_mask & NETCONF_ATTR_PROXY_NEIGH)
242 		nl_dump(p, "proxy_neigh %d ", nc->proxy_neigh);
243 
244 	if (nc->ce_mask & NETCONF_ATTR_IGNORE_RT_LINKDWN) {
245 		nl_dump(p, "ignore_routes_with_linkdown %s ",
246 			nc->ignore_routes_linkdown ? "on" : "off");
247 	}
248 
249 	if (nc->ce_mask & NETCONF_ATTR_INPUT)
250 		nl_dump(p, "input %s ", nc->input ? "on" : "off");
251 
252 	nl_dump(p, "\n");
253 }
254 
255 static const struct trans_tbl netconf_attrs[] = {
256 	__ADD(NETCONF_ATTR_FAMILY, family),
257 	__ADD(NETCONF_ATTR_IFINDEX, ifindex),
258 	__ADD(NETCONF_ATTR_RP_FILTER, rp_filter),
259 	__ADD(NETCONF_ATTR_FWDING, forwarding),
260 	__ADD(NETCONF_ATTR_MC_FWDING, mc_forwarding),
261 	__ADD(NETCONF_ATTR_PROXY_NEIGH, proxy_neigh),
262 	__ADD(NETCONF_ATTR_IGNORE_RT_LINKDWN, ignore_routes_with_linkdown),
263 	__ADD(NETCONF_ATTR_INPUT, input),
264 };
265 
netconf_attrs2str(int attrs,char * buf,size_t len)266 static char *netconf_attrs2str(int attrs, char *buf, size_t len)
267 {
268 	return __flags2str(attrs, buf, len, netconf_attrs,
269 			   ARRAY_SIZE(netconf_attrs));
270 }
271 
netconf_keygen(struct nl_object * obj,uint32_t * hashkey,uint32_t table_sz)272 static void netconf_keygen(struct nl_object *obj, uint32_t *hashkey,
273 			   uint32_t table_sz)
274 {
275 	struct rtnl_netconf *nc = (struct rtnl_netconf *) obj;
276 	unsigned int nckey_sz;
277 	struct nc_hash_key {
278 		int        nc_family;
279 		int        nc_index;
280 	} _nl_packed nckey;
281 
282 	nckey_sz = sizeof(nckey);
283 	nckey.nc_family = nc->family;
284 	nckey.nc_index = nc->ifindex;
285 
286 	*hashkey = nl_hash(&nckey, nckey_sz, 0) % table_sz;
287 
288 	NL_DBG(5, "netconf %p key (dev %d fam %d) keysz %d, hash 0x%x\n",
289 	       nc, nckey.nc_index, nckey.nc_family, nckey_sz, *hashkey);
290 }
291 
netconf_compare(struct nl_object * _a,struct nl_object * _b,uint64_t attrs,int flags)292 static uint64_t netconf_compare(struct nl_object *_a, struct nl_object *_b,
293 			     uint64_t attrs, int flags)
294 {
295 	struct rtnl_netconf *a = (struct rtnl_netconf *) _a;
296 	struct rtnl_netconf *b = (struct rtnl_netconf *) _b;
297 	uint64_t diff = 0;
298 
299 #define _DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ATTR, a, b, EXPR)
300 	diff |= _DIFF(NETCONF_ATTR_FAMILY, a->family != b->family);
301 	diff |= _DIFF(NETCONF_ATTR_IFINDEX, a->ifindex != b->ifindex);
302 	diff |= _DIFF(NETCONF_ATTR_RP_FILTER, a->rp_filter != b->rp_filter);
303 	diff |= _DIFF(NETCONF_ATTR_FWDING, a->forwarding != b->forwarding);
304 	diff |= _DIFF(NETCONF_ATTR_MC_FWDING,
305 		      a->mc_forwarding != b->mc_forwarding);
306 	diff |= _DIFF(NETCONF_ATTR_PROXY_NEIGH,
307 		      a->proxy_neigh != b->proxy_neigh);
308 	diff |= _DIFF(NETCONF_ATTR_IGNORE_RT_LINKDWN,
309 		      a->ignore_routes_linkdown != b->ignore_routes_linkdown);
310 	diff |= _DIFF(NETCONF_ATTR_INPUT, a->input != b->input);
311 #undef _DIFF
312 
313 	return diff;
314 }
315 
netconf_update(struct nl_object * old_obj,struct nl_object * new_obj)316 static int netconf_update(struct nl_object *old_obj, struct nl_object *new_obj)
317 {
318 	struct rtnl_netconf *new_nc = (struct rtnl_netconf *) new_obj;
319 	struct rtnl_netconf *old_nc = (struct rtnl_netconf *) old_obj;
320 	int action = new_obj->ce_msgtype;
321 
322 	switch(action) {
323 	case RTM_NEWNETCONF:
324 		if (new_nc->family != old_nc->family ||
325 		    new_nc->ifindex != old_nc->ifindex)
326 			return -NLE_OPNOTSUPP;
327 
328 		if (new_nc->ce_mask & NETCONF_ATTR_RP_FILTER)
329 			old_nc->rp_filter = new_nc->rp_filter;
330 		if (new_nc->ce_mask & NETCONF_ATTR_FWDING)
331 			old_nc->forwarding = new_nc->forwarding;
332 		if (new_nc->ce_mask & NETCONF_ATTR_MC_FWDING)
333 			old_nc->mc_forwarding = new_nc->mc_forwarding;
334 		if (new_nc->ce_mask & NETCONF_ATTR_PROXY_NEIGH)
335 			old_nc->proxy_neigh = new_nc->proxy_neigh;
336 		if (new_nc->ce_mask & NETCONF_ATTR_IGNORE_RT_LINKDWN)
337 			old_nc->ignore_routes_linkdown = new_nc->ignore_routes_linkdown;
338 
339 		break;
340 	default:
341 		return -NLE_OPNOTSUPP;
342 	}
343 
344 	return NLE_SUCCESS;
345 }
346 
347 /**
348  * @name Cache Management
349  * @{
350  */
351 
rtnl_netconf_alloc_cache(struct nl_sock * sk,struct nl_cache ** result)352 int rtnl_netconf_alloc_cache(struct nl_sock *sk, struct nl_cache **result)
353 {
354 	return nl_cache_alloc_and_fill(&rtnl_netconf_ops, sk, result);
355 }
356 
357 /**
358  * Search netconf in cache
359  * @arg cache		netconf cache
360  * @arg family		Address family of interest
361  * @arg ifindex		Interface index of interest
362  *
363  * Searches netconf cache previously allocated with rtnl_netconf_alloc_cache()
364  * for given index and family
365  *
366  * The reference counter is incremented before returning the netconf entry,
367  * therefore the reference must be given back with rtnl_netconf_put() after
368  * usage.
369  *
370  * @return netconf object or NULL if no match was found.
371  */
rtnl_netconf_get_by_idx(struct nl_cache * cache,int family,int ifindex)372 struct rtnl_netconf *rtnl_netconf_get_by_idx(struct nl_cache *cache, int family,
373 					     int ifindex)
374 {
375 	struct rtnl_netconf *nc;
376 
377 	if (!ifindex || !family || cache->c_ops != &rtnl_netconf_ops)
378 		return NULL;
379 
380 	nl_list_for_each_entry(nc, &cache->c_items, ce_list) {
381 		if (nc->ifindex == ifindex &&
382 		    nc->family == family) {
383 			nl_object_get((struct nl_object *) nc);
384 			return nc;
385 		}
386 	}
387 
388 	return NULL;
389 }
390 
rtnl_netconf_put(struct rtnl_netconf * nc)391 void rtnl_netconf_put(struct rtnl_netconf *nc)
392 {
393 	nl_object_put((struct nl_object *) nc);
394 }
395 
396 /**
397  * Search netconf in cache
398  * @arg cache		netconf cache
399  * @arg family		Address family of interest
400  *
401  * Searches netconf cache previously allocated with rtnl_netconf_alloc_cache()
402  * for "all" netconf settings for given family
403  *
404  * The reference counter is incremented before returning the netconf entry,
405  * therefore the reference must be given back with rtnl_netconf_put() after
406  * usage.
407  *
408  * @return netconf object or NULL if no match was found.
409  */
rtnl_netconf_get_all(struct nl_cache * cache,int family)410 struct rtnl_netconf *rtnl_netconf_get_all(struct nl_cache *cache, int family)
411 {
412 	return rtnl_netconf_get_by_idx(cache, family, NETCONFA_IFINDEX_ALL);
413 }
414 
415 /**
416  * Search netconf in cache
417  * @arg cache		netconf cache
418  * @arg family		Address family of interest
419  *
420  * Searches netconf cache previously allocated with rtnl_netconf_alloc_cache()
421  * for "default" netconf settings for given family
422  *
423  * The reference counter is incremented before returning the netconf entry,
424  * therefore the reference must be given back with rtnl_netconf_put() after
425  * usage.
426  *
427  * @return netconf object or NULL if no match was found.
428  */
rtnl_netconf_get_default(struct nl_cache * cache,int family)429 struct rtnl_netconf *rtnl_netconf_get_default(struct nl_cache *cache, int family)
430 {
431 	return rtnl_netconf_get_by_idx(cache, family, NETCONFA_IFINDEX_DEFAULT);
432 }
433 
434 /** @} */
435 
436 /**
437  * @name Attributes
438  * @{
439  */
440 
rtnl_netconf_get_family(struct rtnl_netconf * nc,int * val)441 int rtnl_netconf_get_family(struct rtnl_netconf *nc, int *val)
442 {
443 	if (!nc)
444 		return -NLE_INVAL;
445 	if (!(nc->ce_mask & NETCONF_ATTR_FAMILY))
446 		return -NLE_MISSING_ATTR;
447 	if (val)
448 		*val = nc->family;
449 	return 0;
450 }
rtnl_netconf_get_ifindex(struct rtnl_netconf * nc,int * val)451 int rtnl_netconf_get_ifindex(struct rtnl_netconf *nc, int *val)
452 {
453 	if (!nc)
454 		return -NLE_INVAL;
455 	if (!(nc->ce_mask & NETCONF_ATTR_IFINDEX))
456 		return -NLE_MISSING_ATTR;
457 	if (val)
458 		*val = nc->ifindex;
459 	return 0;
460 }
rtnl_netconf_get_forwarding(struct rtnl_netconf * nc,int * val)461 int rtnl_netconf_get_forwarding(struct rtnl_netconf *nc, int *val)
462 {
463 	if (!nc)
464 		return -NLE_INVAL;
465 	if (!(nc->ce_mask & NETCONF_ATTR_FWDING))
466 		return -NLE_MISSING_ATTR;
467 	if (val)
468 		*val = nc->forwarding;
469 	return 0;
470 }
rtnl_netconf_get_mc_forwarding(struct rtnl_netconf * nc,int * val)471 int rtnl_netconf_get_mc_forwarding(struct rtnl_netconf *nc, int *val)
472 {
473 	if (!nc)
474 		return -NLE_INVAL;
475 	if (!(nc->ce_mask & NETCONF_ATTR_MC_FWDING))
476 		return -NLE_MISSING_ATTR;
477 	if (val)
478 		*val = nc->mc_forwarding;
479 	return 0;
480 }
rtnl_netconf_get_rp_filter(struct rtnl_netconf * nc,int * val)481 int rtnl_netconf_get_rp_filter(struct rtnl_netconf *nc, int *val)
482 {
483 	if (!nc)
484 		return -NLE_INVAL;
485 	if (!(nc->ce_mask & NETCONF_ATTR_RP_FILTER))
486 		return -NLE_MISSING_ATTR;
487 	if (val)
488 		*val = nc->rp_filter;
489 	return 0;
490 }
rtnl_netconf_get_proxy_neigh(struct rtnl_netconf * nc,int * val)491 int rtnl_netconf_get_proxy_neigh(struct rtnl_netconf *nc, int *val)
492 {
493 	if (!nc)
494 		return -NLE_INVAL;
495 	if (!(nc->ce_mask & NETCONF_ATTR_PROXY_NEIGH))
496 		return -NLE_MISSING_ATTR;
497 	if (val)
498 		*val = nc->proxy_neigh;
499 	return 0;
500 }
rtnl_netconf_get_ignore_routes_linkdown(struct rtnl_netconf * nc,int * val)501 int rtnl_netconf_get_ignore_routes_linkdown(struct rtnl_netconf *nc, int *val)
502 {
503 	if (!nc)
504 		return -NLE_INVAL;
505 	if (!(nc->ce_mask & NETCONF_ATTR_IGNORE_RT_LINKDWN))
506 		return -NLE_MISSING_ATTR;
507 	if (val)
508 		*val = nc->ignore_routes_linkdown;
509 	return 0;
510 }
rtnl_netconf_get_input(struct rtnl_netconf * nc,int * val)511 int rtnl_netconf_get_input(struct rtnl_netconf *nc, int *val)
512 {
513 	if (!nc)
514 		return -NLE_INVAL;
515 	if (!(nc->ce_mask & NETCONF_ATTR_INPUT))
516 		return -NLE_MISSING_ATTR;
517 	if (val)
518 		*val = nc->input;
519 	return 0;
520 }
521 
522 
523 /** @} */
524 
525 static struct nl_object_ops netconf_obj_ops = {
526 	.oo_name		= "route/netconf",
527 	.oo_size		= sizeof(struct rtnl_netconf),
528 	.oo_dump = {
529 	    [NL_DUMP_LINE] 	= netconf_dump_line,
530 	    [NL_DUMP_DETAILS] 	= netconf_dump_line,
531 	},
532 	.oo_compare		= netconf_compare,
533 	.oo_keygen		= netconf_keygen,
534 	.oo_update		= netconf_update,
535 	.oo_attrs2str		= netconf_attrs2str,
536 	.oo_id_attrs		= (NETCONF_ATTR_FAMILY      |
537 				   NETCONF_ATTR_IFINDEX)
538 };
539 
540 static struct nl_af_group netconf_groups[] = {
541 	{ AF_INET,	RTNLGRP_IPV4_NETCONF },
542 	{ AF_INET6,	RTNLGRP_IPV6_NETCONF },
543 	{ AF_MPLS,	RTNLGRP_MPLS_NETCONF },
544 	{ END_OF_GROUP_LIST },
545 };
546 
547 static struct nl_cache_ops rtnl_netconf_ops = {
548 	.co_name		= "route/netconf",
549 	.co_hdrsize		= sizeof(struct netconfmsg),
550 	.co_msgtypes		= {
551 					{ RTM_NEWNETCONF, NL_ACT_NEW, "new" },
552 					{ RTM_DELNETCONF, NL_ACT_DEL, "del" },
553 					{ RTM_GETNETCONF, NL_ACT_GET, "get" },
554 					END_OF_MSGTYPES_LIST,
555 				  },
556 	.co_protocol		= NETLINK_ROUTE,
557 	.co_groups		= netconf_groups,
558 	.co_request_update      = netconf_request_update,
559 	.co_msg_parser          = netconf_msg_parser,
560 	.co_obj_ops		= &netconf_obj_ops,
561 };
562 
netconf_init(void)563 static void _nl_init netconf_init(void)
564 {
565 	nl_cache_mngt_register(&rtnl_netconf_ops);
566 }
567 
netconf_exit(void)568 static void _nl_exit netconf_exit(void)
569 {
570 	nl_cache_mngt_unregister(&rtnl_netconf_ops);
571 }
572 
573 /** @} */
574