[PATCH libnl v2 1/4] route: Add support for netconf

David Ahern dsahern at gmail.com
Wed May 3 14:05:06 PDT 2017


Add route/netconf cache type to monitor RTM_*NETCONF messages.

v2
- change get methods to return 0/error and take 'int *val'
  which is set to the value requested

Signed-off-by: David Ahern <dsa at cumulusnetworks.com>
Signed-off-by: Balakrishnan Raman <ramanb at cumulusnetworks.com>
---
 Makefile.am                             |   2 +
 include/linux-private/linux/netconf.h   |  27 ++
 include/linux-private/linux/rtnetlink.h |   2 +
 include/netlink/route/netconf.h         |  43 +++
 lib/route/netconf.c                     | 540 ++++++++++++++++++++++++++++++++
 libnl-route-3.sym                       |   9 +
 6 files changed, 623 insertions(+)
 create mode 100644 include/linux-private/linux/netconf.h
 create mode 100644 include/netlink/route/netconf.h
 create mode 100644 lib/route/netconf.c

diff --git a/Makefile.am b/Makefile.am
index 1b95a559304f..aaf44c8a4731 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -100,6 +100,7 @@ libnlinclude_netlink_route_HEADERS = \
 	include/netlink/route/link.h \
 	include/netlink/route/neighbour.h \
 	include/netlink/route/neightbl.h \
+	include/netlink/route/netconf.h \
 	include/netlink/route/nexthop.h \
 	include/netlink/route/pktloc.h \
 	include/netlink/route/qdisc.h \
@@ -380,6 +381,7 @@ lib_libnl_route_3_la_SOURCES = \
 	lib/route/link/vxlan.c \
 	lib/route/neigh.c \
 	lib/route/neightbl.c \
+	lib/route/netconf.c \
 	lib/route/nexthop.c \
 	lib/route/pktloc.c \
 	lib/route/qdisc/blackhole.c \
diff --git a/include/linux-private/linux/netconf.h b/include/linux-private/linux/netconf.h
new file mode 100644
index 000000000000..ec14058e9b33
--- /dev/null
+++ b/include/linux-private/linux/netconf.h
@@ -0,0 +1,27 @@
+#ifndef __LINUX_NETCONF_H_
+#define __LINUX_NETCONF_H_
+
+#include <linux/types.h>
+#include <linux/netlink.h>
+
+struct netconfmsg {
+	__u8	ncm_family;
+};
+
+enum {
+	NETCONFA_UNSPEC,
+	NETCONFA_IFINDEX,
+	NETCONFA_FORWARDING,
+	NETCONFA_RP_FILTER,
+	NETCONFA_MC_FORWARDING,
+	NETCONFA_PROXY_NEIGH,
+	NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
+	__NETCONFA_MAX
+};
+#define NETCONFA_MAX	(__NETCONFA_MAX - 1)
+#define NETCONFA_ALL	-1
+
+#define NETCONFA_IFINDEX_ALL		-1
+#define NETCONFA_IFINDEX_DEFAULT	-2
+
+#endif /* __LINUX_NETCONF_H_ */
diff --git a/include/linux-private/linux/rtnetlink.h b/include/linux-private/linux/rtnetlink.h
index 32449478c72d..7d94908c04ae 100644
--- a/include/linux-private/linux/rtnetlink.h
+++ b/include/linux-private/linux/rtnetlink.h
@@ -122,6 +122,8 @@ enum {
 
 	RTM_NEWNETCONF = 80,
 #define RTM_NEWNETCONF RTM_NEWNETCONF
+	RTM_DELNETCONF,
+#define RTM_DELNETCONF RTM_DELNETCONF
 	RTM_GETNETCONF = 82,
 #define RTM_GETNETCONF RTM_GETNETCONF
 
diff --git a/include/netlink/route/netconf.h b/include/netlink/route/netconf.h
new file mode 100644
index 000000000000..d2d1f9b5261e
--- /dev/null
+++ b/include/netlink/route/netconf.h
@@ -0,0 +1,43 @@
+/*
+ * netlink/route/netconf.h		rtnetlink netconf layer
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2017 David Ahern <dsa at cumulusnetworks.com>
+ */
+
+#ifndef NETCONF_H_
+#define NETCONF_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct rtnl_netconf;
+
+int rtnl_netconf_alloc_cache(struct nl_sock *sk, struct nl_cache **result);
+
+struct rtnl_netconf *rtnl_netconf_get_by_idx(struct nl_cache *cache, int family,
+					     int ifindex);
+struct rtnl_netconf *rtnl_netconf_get_all(struct nl_cache *cache,
+					  int family);
+struct rtnl_netconf *rtnl_netconf_get_default(struct nl_cache *cache,
+					      int family);
+void rtnl_netconf_put(struct rtnl_netconf *nc);
+
+int rtnl_netconf_get_family(struct rtnl_netconf *nc, int *val);
+int rtnl_netconf_get_ifindex(struct rtnl_netconf *nc, int *val);
+int rtnl_netconf_get_forwarding(struct rtnl_netconf *nc, int *val);
+int rtnl_netconf_get_mc_forwarding(struct rtnl_netconf *nc, int *val);
+int rtnl_netconf_get_rp_filter(struct rtnl_netconf *nc, int *val);
+int rtnl_netconf_get_proxy_neigh(struct rtnl_netconf *nc, int *val);
+int rtnl_netconf_get_ignore_routes_linkdown(struct rtnl_netconf *nc, int *val);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/route/netconf.c b/lib/route/netconf.c
new file mode 100644
index 000000000000..2073c94544fe
--- /dev/null
+++ b/lib/route/netconf.c
@@ -0,0 +1,540 @@
+/*
+ * lib/route/netconf.c		netconf
+ *
+ *	This library is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation version 2.1
+ *	of the License.
+ *
+ * Copyright (c) 2017 David Ahern <dsa at cumulusnetworks.com>
+ */
+
+/**
+ * @ingroup rtnl
+ * @defgroup netconf Netconf
+ * @brief
+ *
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/utils.h>
+#include <netlink/route/netconf.h>
+#include <linux/netconf.h>
+#include <netlink/hashtable.h>
+
+/** @cond SKIP */
+#define NETCONF_ATTR_FAMILY		0x0001
+#define NETCONF_ATTR_IFINDEX		0x0002
+#define NETCONF_ATTR_RP_FILTER		0x0004
+#define NETCONF_ATTR_FWDING		0x0008
+#define NETCONF_ATTR_MC_FWDING		0x0010
+#define NETCONF_ATTR_PROXY_NEIGH	0x0020
+#define NETCONF_ATTR_IGNORE_RT_LINKDWN	0x0040
+
+struct rtnl_netconf
+{
+	NLHDR_COMMON
+
+	int	family;
+	int	ifindex;
+	int	rp_filter;
+	int	forwarding;
+	int	mc_forwarding;
+	int	proxy_neigh;
+	int	ignore_routes_linkdown;
+};
+
+static struct nl_cache_ops rtnl_netconf_ops;
+static struct nl_object_ops netconf_obj_ops;
+/** @endcond */
+
+static struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = {
+	[NETCONFA_IFINDEX]	 = { .type = NLA_S32 },
+	[NETCONFA_FORWARDING]	 = { .type = NLA_S32 },
+	[NETCONFA_MC_FORWARDING] = { .type = NLA_S32 },
+	[NETCONFA_RP_FILTER]	 = { .type = NLA_S32 },
+	[NETCONFA_PROXY_NEIGH]	 = { .type = NLA_S32 },
+	[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]  = { .type = NLA_S32 },
+};
+
+static struct nla_policy devconf_ipv6_policy[NETCONFA_MAX+1] = {
+	[NETCONFA_IFINDEX]	 = { .type = NLA_S32 },
+	[NETCONFA_FORWARDING]	 = { .type = NLA_S32 },
+	[NETCONFA_MC_FORWARDING] = { .type = NLA_S32 },
+	[NETCONFA_PROXY_NEIGH]	 = { .type = NLA_S32 },
+	[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]  = { .type = NLA_S32 },
+};
+
+static struct rtnl_netconf *rtnl_netconf_alloc(void)
+{
+	return (struct rtnl_netconf *) nl_object_alloc(&netconf_obj_ops);
+}
+
+static int netconf_clone(struct nl_object *_dst, struct nl_object *_src)
+{
+	struct rtnl_netconf *dst = nl_object_priv(_dst);
+	struct rtnl_netconf *src = nl_object_priv(_src);
+
+	*dst = *src;
+
+	return 0;
+}
+
+static int netconf_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
+			      struct nlmsghdr *nlh, struct nl_parser_param *pp)
+{
+	struct nlattr *tb[NETCONFA_MAX+1], *attr;
+	struct rtnl_netconf *nc;
+	struct netconfmsg *ncm;
+	int err;
+
+	ncm = nlmsg_data(nlh);
+	switch (ncm->ncm_family) {
+	case AF_INET:
+		err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX,
+				  devconf_ipv4_policy);
+		if (err < 0)
+			return err;
+		break;
+	case AF_INET6:
+		err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX,
+				  devconf_ipv6_policy);
+		if (err < 0)
+			return err;
+		break;
+	default:
+		printf("unexpected netconf family: %d\n", ncm->ncm_family);
+		return -1;
+	}
+
+	if (!tb[NETCONFA_IFINDEX])
+		return -1;
+
+	nc = rtnl_netconf_alloc();
+	if (!nc)
+		return -NLE_NOMEM;
+
+	nc->ce_msgtype = nlh->nlmsg_type;
+	nc->family = ncm->ncm_family;
+	nc->ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]);
+
+	nc->ce_mask = NETCONF_ATTR_FAMILY | NETCONF_ATTR_IFINDEX;
+
+
+	if (tb[NETCONFA_RP_FILTER]) {
+		attr = tb[NETCONFA_RP_FILTER];
+		nc->rp_filter = nla_get_s32(attr);
+		nc->ce_mask |= NETCONF_ATTR_RP_FILTER;
+	}
+
+	if (tb[NETCONFA_FORWARDING]) {
+		attr = tb[NETCONFA_FORWARDING];
+		nc->forwarding = nla_get_s32(attr);
+		nc->ce_mask |= NETCONF_ATTR_FWDING;
+	}
+
+	if (tb[NETCONFA_MC_FORWARDING]) {
+		attr = tb[NETCONFA_MC_FORWARDING];
+		nc->mc_forwarding = nla_get_s32(attr);
+		nc->ce_mask |= NETCONF_ATTR_MC_FWDING;
+	}
+
+	if (tb[NETCONFA_PROXY_NEIGH]) {
+		attr = tb[NETCONFA_PROXY_NEIGH];
+		nc->proxy_neigh = nla_get_s32(attr);
+		nc->ce_mask |= NETCONF_ATTR_PROXY_NEIGH;
+	}
+
+	if (tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]) {
+		attr = tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN];
+		nc->ignore_routes_linkdown = nla_get_s32(attr);
+		nc->ce_mask |= NETCONF_ATTR_IGNORE_RT_LINKDWN;
+	}
+
+	return pp->pp_cb((struct nl_object *) nc, pp);
+}
+
+static int netconf_request_update(struct nl_cache *cache, struct nl_sock *sk)
+{
+	struct netconfmsg nc = {
+		.ncm_family = cache->c_iarg1,
+	};
+
+	return nl_send_simple(sk, RTM_GETNETCONF, NLM_F_DUMP, &nc, sizeof(nc));
+}
+
+static void netconf_dump_line(struct nl_object *obj, struct nl_dump_params *p)
+{
+	struct rtnl_netconf *nc = (struct rtnl_netconf *) obj;
+	struct nl_cache *link_cache;
+	char buf[64];
+
+	switch(nc->family) {
+	case AF_INET:
+		nl_dump(p, "ipv4 ");
+		break;
+	case AF_INET6:
+		nl_dump(p, "ipv6 ");
+		break;
+	default:
+		return;
+	}
+
+	switch(nc->ifindex) {
+	case NETCONFA_IFINDEX_ALL:
+		nl_dump(p, "all ");
+		break;
+	case NETCONFA_IFINDEX_DEFAULT:
+		nl_dump(p, "default ");
+		break;
+	default:
+		link_cache = nl_cache_mngt_require_safe("route/link");
+		if (link_cache) {
+			nl_dump(p, "dev %s ",
+				rtnl_link_i2name(link_cache, nc->ifindex,
+						 buf, sizeof(buf)));
+			nl_cache_put(link_cache);
+		} else
+			nl_dump(p, "dev %d ", nc->ifindex);
+	}
+
+	if (nc->ce_mask & NETCONF_ATTR_FWDING) {
+		nl_dump(p, "forwarding %s ",
+			nc->forwarding ? "on" : "off");
+	}
+
+	if (nc->ce_mask & NETCONF_ATTR_RP_FILTER) {
+		if (nc->rp_filter == 0)
+			nl_dump(p, "rp_filter off ");
+		else if (nc->rp_filter == 1)
+			nl_dump(p, "rp_filter strict ");
+		else if (nc->rp_filter == 2)
+			nl_dump(p, "rp_filter loose ");
+		else
+			nl_dump(p, "rp_filter unknown-mode ");
+	}
+
+	if (nc->ce_mask & NETCONF_ATTR_MC_FWDING) {
+		nl_dump(p, "mc_forwarding %s ",
+			nc->mc_forwarding ? "on" : "off");
+	}
+
+	if (nc->ce_mask & NETCONF_ATTR_PROXY_NEIGH)
+		nl_dump(p, "proxy_neigh %d ", nc->proxy_neigh);
+
+	if (nc->ce_mask & NETCONF_ATTR_IGNORE_RT_LINKDWN) {
+		nl_dump(p, "ignore_routes_with_linkdown %s ",
+			nc->ignore_routes_linkdown ? "on" : "off");
+	}
+
+	nl_dump(p, "\n");
+}
+
+static const struct trans_tbl netconf_attrs[] = {
+	__ADD(NETCONF_ATTR_FAMILY, family),
+	__ADD(NETCONF_ATTR_IFINDEX, ifindex),
+	__ADD(NETCONF_ATTR_RP_FILTER, rp_filter),
+	__ADD(NETCONF_ATTR_FWDING, forwarding),
+	__ADD(NETCONF_ATTR_MC_FWDING, mc_forwarding),
+	__ADD(NETCONF_ATTR_PROXY_NEIGH, proxy_neigh),
+	__ADD(NETCONF_ATTR_IGNORE_RT_LINKDWN, ignore_routes_with_linkdown),
+};
+
+static char *netconf_attrs2str(int attrs, char *buf, size_t len)
+{
+	return __flags2str(attrs, buf, len, netconf_attrs,
+			   ARRAY_SIZE(netconf_attrs));
+}
+
+static void netconf_keygen(struct nl_object *obj, uint32_t *hashkey,
+			   uint32_t table_sz)
+{
+	struct rtnl_netconf *nc = (struct rtnl_netconf *) obj;
+	unsigned int nckey_sz;
+	struct nc_hash_key {
+		int        nc_family;
+		int        nc_index;
+	} __attribute__((packed)) nckey;
+
+	nckey_sz = sizeof(nckey);
+	nckey.nc_family = nc->family;
+	nckey.nc_index = nc->ifindex;
+
+	*hashkey = nl_hash(&nckey, nckey_sz, 0) % table_sz;
+
+	NL_DBG(5, "netconf %p key (dev %d fam %d) keysz %d, hash 0x%x\n",
+	       nc, nckey.nc_index, nckey.nc_family, nckey_sz, *hashkey);
+}
+
+static uint64_t netconf_compare(struct nl_object *_a, struct nl_object *_b,
+			     uint64_t attrs, int flags)
+{
+	struct rtnl_netconf *a = (struct rtnl_netconf *) _a;
+	struct rtnl_netconf *b = (struct rtnl_netconf *) _b;
+	uint64_t diff = 0;
+
+#define NETCONF_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, NETCONF_ATTR_##ATTR, a, b, EXPR)
+
+	diff |= NETCONF_DIFF(FAMILY,	a->family != b->family);
+	diff |= NETCONF_DIFF(IFINDEX,	a->ifindex != b->ifindex);
+	diff |= NETCONF_DIFF(RP_FILTER,	a->rp_filter != b->rp_filter);
+	diff |= NETCONF_DIFF(FWDING,	a->forwarding != b->forwarding);
+	diff |= NETCONF_DIFF(MC_FWDING,	a->mc_forwarding != b->mc_forwarding);
+	diff |= NETCONF_DIFF(PROXY_NEIGH, a->proxy_neigh != b->proxy_neigh);
+	diff |= NETCONF_DIFF(IGNORE_RT_LINKDWN,
+			a->ignore_routes_linkdown != b->ignore_routes_linkdown);
+
+#undef NETCONF_DIFF
+
+	return diff;
+}
+
+static int netconf_update(struct nl_object *old_obj, struct nl_object *new_obj)
+{
+	struct rtnl_netconf *new_nc = (struct rtnl_netconf *) new_obj;
+	struct rtnl_netconf *old_nc = (struct rtnl_netconf *) old_obj;
+	int action = new_obj->ce_msgtype;
+
+	switch(action) {
+	case RTM_NEWNETCONF:
+		if (new_nc->family != old_nc->family ||
+		    new_nc->ifindex != old_nc->ifindex)
+			return -NLE_OPNOTSUPP;
+
+		if (new_nc->ce_mask & NETCONF_ATTR_RP_FILTER)
+			old_nc->rp_filter = new_nc->rp_filter;
+		if (new_nc->ce_mask & NETCONF_ATTR_FWDING)
+			old_nc->forwarding = new_nc->forwarding;
+		if (new_nc->ce_mask & NETCONF_ATTR_MC_FWDING)
+			old_nc->mc_forwarding = new_nc->mc_forwarding;
+		if (new_nc->ce_mask & NETCONF_ATTR_PROXY_NEIGH)
+			old_nc->proxy_neigh = new_nc->proxy_neigh;
+		if (new_nc->ce_mask & NETCONF_ATTR_IGNORE_RT_LINKDWN)
+			old_nc->ignore_routes_linkdown = new_nc->ignore_routes_linkdown;
+
+		break;
+	default:
+		return -NLE_OPNOTSUPP;
+	}
+
+	return NLE_SUCCESS;
+}
+
+/**
+ * @name Cache Management
+ * @{
+ */
+
+int rtnl_netconf_alloc_cache(struct nl_sock *sk, struct nl_cache **result)
+{
+	return nl_cache_alloc_and_fill(&rtnl_netconf_ops, sk, result);
+}
+
+/**
+ * Search netconf in cache
+ * @arg cache		netconf cache
+ * @arg family		Address family of interest
+ * @arg ifindex		Interface index of interest
+ *
+ * Searches netconf cache previously allocated with rtnl_netconf_alloc_cache()
+ * for given index and family
+ *
+ * The reference counter is incremented before returning the netconf entry,
+ * therefore the reference must be given back with rtnl_netconf_put() after
+ * usage.
+ *
+ * @return netconf object or NULL if no match was found.
+ */
+struct rtnl_netconf *rtnl_netconf_get_by_idx(struct nl_cache *cache, int family,
+					     int ifindex)
+{
+	struct rtnl_netconf *nc;
+
+	if (!ifindex || !family || cache->c_ops != &rtnl_netconf_ops)
+		return NULL;
+
+	nl_list_for_each_entry(nc, &cache->c_items, ce_list) {
+		if (nc->ifindex == ifindex &&
+		    nc->family == family) {
+			nl_object_get((struct nl_object *) nc);
+			return nc;
+		}
+	}
+
+	return NULL;
+}
+
+void rtnl_netconf_put(struct rtnl_netconf *nc)
+{
+	nl_object_put((struct nl_object *) nc);
+}
+
+/**
+ * Search netconf in cache
+ * @arg cache		netconf cache
+ * @arg family		Address family of interest
+ *
+ * Searches netconf cache previously allocated with rtnl_netconf_alloc_cache()
+ * for "all" netconf settings for given family
+ *
+ * The reference counter is incremented before returning the netconf entry,
+ * therefore the reference must be given back with rtnl_netconf_put() after
+ * usage.
+ *
+ * @return netconf object or NULL if no match was found.
+ */
+struct rtnl_netconf *rtnl_netconf_get_all(struct nl_cache *cache, int family)
+{
+	return rtnl_netconf_get_by_idx(cache, family, NETCONFA_IFINDEX_ALL);
+}
+
+/**
+ * Search netconf in cache
+ * @arg cache		netconf cache
+ * @arg family		Address family of interest
+ *
+ * Searches netconf cache previously allocated with rtnl_netconf_alloc_cache()
+ * for "default" netconf settings for given family
+ *
+ * The reference counter is incremented before returning the netconf entry,
+ * therefore the reference must be given back with rtnl_netconf_put() after
+ * usage.
+ *
+ * @return netconf object or NULL if no match was found.
+ */
+struct rtnl_netconf *rtnl_netconf_get_default(struct nl_cache *cache, int family)
+{
+	return rtnl_netconf_get_by_idx(cache, family, NETCONFA_IFINDEX_DEFAULT);
+}
+
+/** @} */
+
+/**
+ * @name Attributes
+ * @{
+ */
+
+int rtnl_netconf_get_family(struct rtnl_netconf *nc, int *val)
+{
+	if (!nc)
+		return -NLE_INVAL;
+	if (!(nc->ce_mask & NETCONF_ATTR_FAMILY))
+		return -NLE_MISSING_ATTR;
+	if (val)
+		*val = nc->family;
+	return 0;
+}
+int rtnl_netconf_get_ifindex(struct rtnl_netconf *nc, int *val)
+{
+	if (!nc)
+		return -NLE_INVAL;
+	if (!(nc->ce_mask & NETCONF_ATTR_IFINDEX))
+		return -NLE_MISSING_ATTR;
+	if (val)
+		*val = nc->ifindex;
+	return 0;
+}
+int rtnl_netconf_get_forwarding(struct rtnl_netconf *nc, int *val)
+{
+	if (!nc)
+		return -NLE_INVAL;
+	if (!(nc->ce_mask & NETCONF_ATTR_FWDING))
+		return -NLE_MISSING_ATTR;
+	if (val)
+		*val = nc->forwarding;
+	return 0;
+}
+int rtnl_netconf_get_mc_forwarding(struct rtnl_netconf *nc, int *val)
+{
+	if (!nc)
+		return -NLE_INVAL;
+	if (!(nc->ce_mask & NETCONF_ATTR_MC_FWDING))
+		return -NLE_MISSING_ATTR;
+	if (val)
+		*val = nc->mc_forwarding;
+	return 0;
+}
+int rtnl_netconf_get_rp_filter(struct rtnl_netconf *nc, int *val)
+{
+	if (!nc)
+		return -NLE_INVAL;
+	if (!(nc->ce_mask & NETCONF_ATTR_RP_FILTER))
+		return -NLE_MISSING_ATTR;
+	if (val)
+		*val = nc->rp_filter;
+	return 0;
+}
+int rtnl_netconf_get_proxy_neigh(struct rtnl_netconf *nc, int *val)
+{
+	if (!nc)
+		return -NLE_INVAL;
+	if (!(nc->ce_mask & NETCONF_ATTR_PROXY_NEIGH))
+		return -NLE_MISSING_ATTR;
+	if (val)
+		*val = nc->proxy_neigh;
+	return 0;
+}
+int rtnl_netconf_get_ignore_routes_linkdown(struct rtnl_netconf *nc, int *val)
+{
+	if (!nc)
+		return -NLE_INVAL;
+	if (!(nc->ce_mask & NETCONF_ATTR_IGNORE_RT_LINKDWN))
+		return -NLE_MISSING_ATTR;
+	if (val)
+		*val = nc->ignore_routes_linkdown;
+	return 0;
+}
+
+/** @} */
+
+static struct nl_object_ops netconf_obj_ops = {
+	.oo_name		= "route/netconf",
+	.oo_size		= sizeof(struct rtnl_netconf),
+	.oo_clone		= netconf_clone,
+	.oo_dump = {
+	    [NL_DUMP_LINE] 	= netconf_dump_line,
+	    [NL_DUMP_DETAILS] 	= netconf_dump_line,
+	},
+	.oo_compare		= netconf_compare,
+	.oo_keygen		= netconf_keygen,
+	.oo_update		= netconf_update,
+	.oo_attrs2str		= netconf_attrs2str,
+	.oo_id_attrs		= (NETCONF_ATTR_FAMILY      |
+				   NETCONF_ATTR_IFINDEX)
+};
+
+static struct nl_af_group netconf_groups[] = {
+	{ AF_INET,	RTNLGRP_IPV4_NETCONF },
+	{ AF_INET6,	RTNLGRP_IPV6_NETCONF },
+	{ END_OF_GROUP_LIST },
+};
+
+static struct nl_cache_ops rtnl_netconf_ops = {
+	.co_name		= "route/netconf",
+	.co_hdrsize		= sizeof(struct netconfmsg),
+	.co_msgtypes		= {
+					{ RTM_NEWNETCONF, NL_ACT_NEW, "new" },
+					{ RTM_DELNETCONF, NL_ACT_DEL, "del" },
+					{ RTM_GETNETCONF, NL_ACT_GET, "get" },
+					END_OF_MSGTYPES_LIST,
+				  },
+	.co_protocol		= NETLINK_ROUTE,
+	.co_groups		= netconf_groups,
+	.co_request_update      = netconf_request_update,
+	.co_msg_parser          = netconf_msg_parser,
+	.co_obj_ops		= &netconf_obj_ops,
+};
+
+static void __init netconf_init(void)
+{
+	nl_cache_mngt_register(&rtnl_netconf_ops);
+}
+
+static void __exit netconf_exit(void)
+{
+	nl_cache_mngt_unregister(&rtnl_netconf_ops);
+}
+
+/** @} */
diff --git a/libnl-route-3.sym b/libnl-route-3.sym
index 1fb4371cb0c8..b17434c87a89 100644
--- a/libnl-route-3.sym
+++ b/libnl-route-3.sym
@@ -1041,4 +1041,13 @@ libnl_3_4 {
 	rtnl_link_inet6_get_flags;
 	rtnl_link_inet6_set_flags;
 	rtnl_u32_get_action;
+	rtnl_netconf_get_by_idx;
+	rtnl_netconf_get_all;
+	rtnl_netconf_get_default;
+	rtnl_netconf_put;
+	rtnl_netconf_get_family;
+	rtnl_netconf_get_ifindex;
+	rtnl_netconf_get_forwarding;
+	rtnl_netconf_get_mc_forwarding;
+	rtnl_netconf_get_rp_filter;
 } libnl_3_2_29;
-- 
2.1.4




More information about the libnl mailing list