[PATCH 2/2] inet6: add link IPv6 address generation mode support

Dan Williams dcbw at redhat.com
Fri Jul 25 12:36:29 PDT 2014


Signed-off-by: Dan Williams <dcbw at redhat.com>
---
The only interesting thing here is that we want to make sure the
caller can figure out if the kernel never sent
IFLA_INET6_ADDR_GEN_MODE, thus the error returned in 
rtnl_link_inet6_get_addr_gen_mode().  There's some precedent
for that error handling, eg in inet.c when no cache info exists.

 include/Makefile.am                |   1 +
 include/netlink/route/link/inet6.h |  37 +++++++++++++
 lib/route/link/inet6.c             | 107 ++++++++++++++++++++++++++++++++++---
 3 files changed, 139 insertions(+), 6 deletions(-)
 create mode 100644 include/netlink/route/link/inet6.h

diff --git a/include/Makefile.am b/include/Makefile.am
index 9833def..90f647c 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -44,14 +44,15 @@ nobase_libnlinclude_HEADERS = \
 	netlink/route/cls/police.h \
 	netlink/route/cls/u32.h \
 	netlink/route/link/api.h \
 	netlink/route/link/bonding.h \
 	netlink/route/link/bridge.h \
 	netlink/route/link/can.h \
 	netlink/route/link/inet.h \
+	netlink/route/link/inet6.h \
 	netlink/route/link/info-api.h \
 	netlink/route/link/macvlan.h \
 	netlink/route/link/vlan.h \
 	netlink/route/link/vxlan.h \
 	netlink/route/link/veth.h \
 	netlink/route/link/ip6tnl.h \
 	netlink/route/link/ipgre.h \
diff --git a/include/netlink/route/link/inet6.h b/include/netlink/route/link/inet6.h
new file mode 100644
index 0000000..8ffeab2
--- /dev/null
+++ b/include/netlink/route/link/inet6.h
@@ -0,0 +1,37 @@
+/*
+ * netlink/route/link/inet6.h	INET6 Link Module
+ *
+ *	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) 2014 Dan Williams <dcbw at redhat.com>
+ */
+
+#ifndef NETLINK_LINK_INET6_H_
+#define NETLINK_LINK_INET6_H_
+
+#include <netlink/netlink.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+const char *		rtnl_link_inet6_addrgenmode2str  (uint8_t mode,
+							  char *buf,
+							  size_t len);
+
+uint8_t			rtnl_link_inet6_str2addrgenmode  (const char *mode);
+
+extern int		rtnl_link_inet6_get_addr_gen_mode(struct rtnl_link *,
+							  uint8_t *);
+
+extern int		rtnl_link_inet6_set_addr_gen_mode(struct rtnl_link *,
+							  uint8_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/route/link/inet6.c b/lib/route/link/inet6.c
index 6fa2741..43bc387 100644
--- a/lib/route/link/inet6.c
+++ b/lib/route/link/inet6.c
@@ -11,24 +11,33 @@
 
 #include <netlink-private/netlink.h>
 #include <netlink/netlink.h>
 #include <netlink/attr.h>
 #include <netlink/route/rtnl.h>
 #include <netlink-private/route/link/api.h>
 
+#define I6_ADDR_GEN_MODE_UNKNOWN	UCHAR_MAX
+
 struct inet6_data
 {
 	uint32_t		i6_flags;
 	struct ifla_cacheinfo	i6_cacheinfo;
 	uint32_t		i6_conf[DEVCONF_MAX];
+	uint8_t			i6_addr_gen_mode;
 };
 
 static void *inet6_alloc(struct rtnl_link *link)
 {
-	return calloc(1, sizeof(struct inet6_data));
+	struct inet6_data *i6;
+
+	i6 = calloc(1, sizeof(struct inet6_data));
+	if (i6)
+		i6->i6_addr_gen_mode = I6_ADDR_GEN_MODE_UNKNOWN;
+
+	return i6;
 }
 
 static void *inet6_clone(struct rtnl_link *link, void *data)
 {
 	struct inet6_data *i6;
 
 	if ((i6 = inet6_alloc(link)))
@@ -39,19 +48,20 @@ static void *inet6_clone(struct rtnl_link *link, void *data)
 
 static void inet6_free(struct rtnl_link *link, void *data)
 {
 	free(data);
 }
 
 static struct nla_policy inet6_policy[IFLA_INET6_MAX+1] = {
-	[IFLA_INET6_FLAGS]	= { .type = NLA_U32 },
-	[IFLA_INET6_CACHEINFO]	= { .minlen = sizeof(struct ifla_cacheinfo) },
-	[IFLA_INET6_CONF]	= { .minlen = 4 },
-	[IFLA_INET6_STATS]	= { .minlen = 8 },
-	[IFLA_INET6_ICMP6STATS]	= { .minlen = 8 },
+	[IFLA_INET6_FLAGS]		= { .type = NLA_U32 },
+	[IFLA_INET6_CACHEINFO]		= { .minlen = sizeof(struct ifla_cacheinfo) },
+	[IFLA_INET6_CONF]		= { .minlen = 4 },
+	[IFLA_INET6_STATS]		= { .minlen = 8 },
+	[IFLA_INET6_ICMP6STATS]		= { .minlen = 8 },
+	[IFLA_INET6_ADDR_GEN_MODE]	= { .type = NLA_U8 },
 };
 
 static const uint8_t map_stat_id_from_IPSTATS_MIB_v1[__IPSTATS_MIB_MAX] = {
 	/* 14a196807482e6fc74f15fc03176d5c08880588f^:include/linux/snmp.h
 	 * version before the API change in commit 14a196807482e6fc74f15fc03176d5c08880588f.
 	 * This version was valid since commit edf391ff17232f097d72441c9ad467bcb3b5db18, which
 	 * predates support for parsing IFLA_PROTINFO in libnl3. Such an even older meaning of
@@ -151,14 +161,17 @@ static int inet6_parse_protinfo(struct rtnl_link *link, struct nlattr *attr,
 	if (tb[IFLA_INET6_CACHEINFO])
 		nla_memcpy(&i6->i6_cacheinfo, tb[IFLA_INET6_CACHEINFO],
 			   sizeof(i6->i6_cacheinfo));
 
 	if (tb[IFLA_INET6_CONF])
 		nla_memcpy(&i6->i6_conf, tb[IFLA_INET6_CONF],
 			   sizeof(i6->i6_conf));
+
+	if (tb[IFLA_INET6_ADDR_GEN_MODE])
+		i6->i6_addr_gen_mode = nla_get_u8 (tb[IFLA_INET6_ADDR_GEN_MODE]);
  
 	/*
 	 * Due to 32bit data alignment, these addresses must be copied to an
 	 * aligned location prior to access.
 	 */
 	if (tb[IFLA_INET6_STATS]) {
 		unsigned char *cnt = nla_data(tb[IFLA_INET6_STATS]);
@@ -196,14 +209,27 @@ static int inet6_parse_protinfo(struct rtnl_link *link, struct nlattr *attr,
 					   stat);
 		}
 	}
 
 	return 0;
 }
 
+static int inet6_fill_af(struct rtnl_link *link, struct nl_msg *msg, void *data)
+{
+	struct inet6_data *id = data;
+
+	if (id->i6_addr_gen_mode != I6_ADDR_GEN_MODE_UNKNOWN)
+		NLA_PUT_U8(msg, IFLA_INET6_ADDR_GEN_MODE, id->i6_addr_gen_mode);
+
+	return 0;
+
+nla_put_failure:
+	return -NLE_MSGSIZE;
+}
+
 /* These live in include/net/if_inet6.h and should be moved to include/linux */
 #define IF_RA_OTHERCONF	0x80
 #define IF_RA_MANAGED	0x40
 #define IF_RA_RCVD	0x20
 #define IF_RS_SENT	0x10
 #define IF_READY	0x80000000
 
@@ -255,14 +281,30 @@ static const struct trans_tbl inet6_devconf[] = {
 
 static char *inet6_devconf2str(int type, char *buf, size_t len)
 {
 	return __type2str(type, buf, len, inet6_devconf,
 			  ARRAY_SIZE(inet6_devconf));
 }
 
+static const struct trans_tbl inet6_addr_gen_mode[] = {
+	__ADD(IN6_ADDR_GEN_MODE_EUI64, eui64)
+	__ADD(IN6_ADDR_GEN_MODE_NONE, none)
+};
+
+const char *rtnl_link_inet6_addrgenmode2str(uint8_t mode, char *buf, size_t len)
+{
+	return __type2str(mode, buf, len, inet6_addr_gen_mode,
+			  ARRAY_SIZE(inet6_addr_gen_mode));
+}
+
+uint8_t rtnl_link_inet6_str2addrgenmode(const char *mode)
+{
+	return (uint8_t) __str2type(mode, inet6_addr_gen_mode,
+			            ARRAY_SIZE(inet6_addr_gen_mode));
+}
 
 static void inet6_dump_details(struct rtnl_link *link,
 				struct nl_dump_params *p, void *data)
 {
 	struct inet6_data *i6 = data;
 	char buf[64], buf2[64];
 	int i, n = 0;
@@ -277,14 +319,18 @@ static void inet6_dump_details(struct rtnl_link *link,
 	nl_dump_line(p, "      create-stamp %.2fs reachable-time %s",
 		(double) i6->i6_cacheinfo.tstamp / 100.,
 		nl_msec2str(i6->i6_cacheinfo.reachable_time, buf, sizeof(buf)));
 
 	nl_dump(p, " retrans-time %s\n",
 		nl_msec2str(i6->i6_cacheinfo.retrans_time, buf, sizeof(buf)));
 
+	nl_dump(p, " link-local address mode %s\n",
+		rtnl_link_inet6_addrgenmode2str(i6->i6_addr_gen_mode,
+						buf, sizeof(buf)));
+
 	nl_dump_line(p, "      devconf:\n");
 	nl_dump_line(p, "      ");
 
 	for (i = 0; i < DEVCONF_MAX; i++) {
 		uint32_t value = i6->i6_conf[i];
 		int x, offset;
 
@@ -464,19 +510,68 @@ static const struct nla_policy protinfo_policy = {
 static struct rtnl_link_af_ops inet6_ops = {
 	.ao_family			= AF_INET6,
 	.ao_alloc			= &inet6_alloc,
 	.ao_clone			= &inet6_clone,
 	.ao_free			= &inet6_free,
 	.ao_parse_protinfo		= &inet6_parse_protinfo,
 	.ao_parse_af			= &inet6_parse_protinfo,
+	.ao_fill_af			= &inet6_fill_af,
 	.ao_dump[NL_DUMP_DETAILS]	= &inet6_dump_details,
 	.ao_dump[NL_DUMP_STATS]		= &inet6_dump_stats,
 	.ao_protinfo_policy		= &protinfo_policy,
 };
 
+/**
+ * Get IPv6 link-local address generation mode
+ * @arg link		Link object
+ * @arg mode		Generation mode on success
+ *
+ * Returns the link's IPv6 link-local address generation mode.
+ *
+ * @return 0 on success
+ * @return -NLE_NOATTR configuration setting not available
+ * @return -NLE_INVAL generation mode unknown. If the link was received via
+ *                    netlink, it means that address generation mode is not
+ *                    supported by the kernel.
+ */
+int rtnl_link_inet6_get_addr_gen_mode(struct rtnl_link *link, uint8_t *mode)
+{
+	struct inet6_data *id;
+
+	if (!(id = rtnl_link_af_alloc(link, &inet6_ops)))
+		return -NLE_NOATTR;
+
+	if (id->i6_addr_gen_mode == I6_ADDR_GEN_MODE_UNKNOWN)
+		return -NLE_INVAL;
+
+	*mode = id->i6_addr_gen_mode;
+	return 0;
+}
+
+/**
+ * Set IPv6 link-local address generation mode
+ * @arg link		Link object
+ * @arg mode		Generation mode
+ *
+ * Sets the link's IPv6 link-local address generation mode.
+ *
+ * @return 0 on success
+ * @return -NLE_NOATTR configuration setting not available
+ */
+int rtnl_link_inet6_set_addr_gen_mode(struct rtnl_link *link, uint8_t mode)
+{
+	struct inet6_data *id;
+
+	if (!(id = rtnl_link_af_alloc(link, &inet6_ops)))
+		return -NLE_NOATTR;
+
+	id->i6_addr_gen_mode = mode;
+	return 0;
+}
+
 static void __init inet6_init(void)
 {
 	rtnl_link_af_register(&inet6_ops);
 }
 
 static void __exit inet6_exit(void)
 {
-- 
1.9.3





More information about the libnl mailing list