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