• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * lib/route/link/inet6.c	AF_INET6 link operations
3  *
4  *	This library is free software; you can redistribute it and/or
5  *	modify it under the terms of the GNU Lesser General Public
6  *	License as published by the Free Software Foundation version 2.1
7  *	of the License.
8  *
9  * Copyright (c) 2010 Thomas Graf <tgraf@suug.ch>
10  */
11 
12 #include <netlink-private/netlink.h>
13 #include <netlink/netlink.h>
14 #include <netlink/attr.h>
15 #include <netlink/route/rtnl.h>
16 #include <netlink/route/link/inet6.h>
17 #include <netlink-private/route/link/api.h>
18 
19 #include "netlink-private/utils.h"
20 
21 #define I6_ADDR_GEN_MODE_UNKNOWN	UINT8_MAX
22 
23 struct inet6_data
24 {
25 	uint32_t		i6_flags;
26 	struct ifla_cacheinfo	i6_cacheinfo;
27 	uint32_t		i6_conf[DEVCONF_MAX];
28 	struct in6_addr		i6_token;
29 	uint8_t			i6_addr_gen_mode;
30 };
31 
inet6_alloc(struct rtnl_link * link)32 static void *inet6_alloc(struct rtnl_link *link)
33 {
34 	struct inet6_data *i6;
35 
36 	i6 = calloc(1, sizeof(struct inet6_data));
37 	if (i6)
38 		i6->i6_addr_gen_mode = I6_ADDR_GEN_MODE_UNKNOWN;
39 
40 	return i6;
41 }
42 
inet6_clone(struct rtnl_link * link,void * data)43 static void *inet6_clone(struct rtnl_link *link, void *data)
44 {
45 	struct inet6_data *i6;
46 
47 	if ((i6 = inet6_alloc(link)))
48 		memcpy(i6, data, sizeof(*i6));
49 
50 	return i6;
51 }
52 
inet6_free(struct rtnl_link * link,void * data)53 static void inet6_free(struct rtnl_link *link, void *data)
54 {
55 	free(data);
56 }
57 
58 static struct nla_policy inet6_policy[IFLA_INET6_MAX+1] = {
59 	[IFLA_INET6_FLAGS]		= { .type = NLA_U32 },
60 	[IFLA_INET6_CACHEINFO]		= { .minlen = sizeof(struct ifla_cacheinfo) },
61 	[IFLA_INET6_CONF]		= { .minlen = 4 },
62 	[IFLA_INET6_STATS]		= { .minlen = 8 },
63 	[IFLA_INET6_ICMP6STATS]		= { .minlen = 8 },
64 	[IFLA_INET6_TOKEN]		= { .minlen = sizeof(struct in6_addr) },
65 	[IFLA_INET6_ADDR_GEN_MODE]	= { .type = NLA_U8 },
66 };
67 
68 static const uint8_t map_stat_id_from_IPSTATS_MIB_v1[__IPSTATS_MIB_MAX] = {
69 	/* 14a196807482e6fc74f15fc03176d5c08880588f^:include/linux/snmp.h
70 	 * version before the API change in commit 14a196807482e6fc74f15fc03176d5c08880588f.
71 	 * This version was valid since commit edf391ff17232f097d72441c9ad467bcb3b5db18, which
72 	 * predates support for parsing IFLA_PROTINFO in libnl3. Such an even older meaning of
73 	 * the flags is not supported in libnl3. */
74 	[ 1] = RTNL_LINK_IP6_INPKTS,                    /* IPSTATS_MIB_INPKTS                   */
75 	[ 2] = RTNL_LINK_IP6_INHDRERRORS,               /* IPSTATS_MIB_INHDRERRORS              */
76 	[ 3] = RTNL_LINK_IP6_INTOOBIGERRORS,            /* IPSTATS_MIB_INTOOBIGERRORS           */
77 	[ 4] = RTNL_LINK_IP6_INNOROUTES,                /* IPSTATS_MIB_INNOROUTES               */
78 	[ 5] = RTNL_LINK_IP6_INADDRERRORS,              /* IPSTATS_MIB_INADDRERRORS             */
79 	[ 6] = RTNL_LINK_IP6_INUNKNOWNPROTOS,           /* IPSTATS_MIB_INUNKNOWNPROTOS          */
80 	[ 7] = RTNL_LINK_IP6_INTRUNCATEDPKTS,           /* IPSTATS_MIB_INTRUNCATEDPKTS          */
81 	[ 8] = RTNL_LINK_IP6_INDISCARDS,                /* IPSTATS_MIB_INDISCARDS               */
82 	[ 9] = RTNL_LINK_IP6_INDELIVERS,                /* IPSTATS_MIB_INDELIVERS               */
83 	[10] = RTNL_LINK_IP6_OUTFORWDATAGRAMS,          /* IPSTATS_MIB_OUTFORWDATAGRAMS         */
84 	[11] = RTNL_LINK_IP6_OUTPKTS,                   /* IPSTATS_MIB_OUTPKTS                  */
85 	[12] = RTNL_LINK_IP6_OUTDISCARDS,               /* IPSTATS_MIB_OUTDISCARDS              */
86 	[13] = RTNL_LINK_IP6_OUTNOROUTES,               /* IPSTATS_MIB_OUTNOROUTES              */
87 	[14] = RTNL_LINK_IP6_REASMTIMEOUT,              /* IPSTATS_MIB_REASMTIMEOUT             */
88 	[15] = RTNL_LINK_IP6_REASMREQDS,                /* IPSTATS_MIB_REASMREQDS               */
89 	[16] = RTNL_LINK_IP6_REASMOKS,                  /* IPSTATS_MIB_REASMOKS                 */
90 	[17] = RTNL_LINK_IP6_REASMFAILS,                /* IPSTATS_MIB_REASMFAILS               */
91 	[18] = RTNL_LINK_IP6_FRAGOKS,                   /* IPSTATS_MIB_FRAGOKS                  */
92 	[19] = RTNL_LINK_IP6_FRAGFAILS,                 /* IPSTATS_MIB_FRAGFAILS                */
93 	[20] = RTNL_LINK_IP6_FRAGCREATES,               /* IPSTATS_MIB_FRAGCREATES              */
94 	[21] = RTNL_LINK_IP6_INMCASTPKTS,               /* IPSTATS_MIB_INMCASTPKTS              */
95 	[22] = RTNL_LINK_IP6_OUTMCASTPKTS,              /* IPSTATS_MIB_OUTMCASTPKTS             */
96 	[23] = RTNL_LINK_IP6_INBCASTPKTS,               /* IPSTATS_MIB_INBCASTPKTS              */
97 	[24] = RTNL_LINK_IP6_OUTBCASTPKTS,              /* IPSTATS_MIB_OUTBCASTPKTS             */
98 	[25] = RTNL_LINK_IP6_INOCTETS,                  /* IPSTATS_MIB_INOCTETS                 */
99 	[26] = RTNL_LINK_IP6_OUTOCTETS,                 /* IPSTATS_MIB_OUTOCTETS                */
100 	[27] = RTNL_LINK_IP6_INMCASTOCTETS,             /* IPSTATS_MIB_INMCASTOCTETS            */
101 	[28] = RTNL_LINK_IP6_OUTMCASTOCTETS,            /* IPSTATS_MIB_OUTMCASTOCTETS           */
102 	[29] = RTNL_LINK_IP6_INBCASTOCTETS,             /* IPSTATS_MIB_INBCASTOCTETS            */
103 	[30] = RTNL_LINK_IP6_OUTBCASTOCTETS,            /* IPSTATS_MIB_OUTBCASTOCTETS           */
104 };
105 
106 static const uint8_t map_stat_id_from_IPSTATS_MIB_v2[__IPSTATS_MIB_MAX] = {
107 	/* d8ec26d7f8287f5788a494f56e8814210f0e64be:include/uapi/linux/snmp.h
108 	 * version since the API change in commit 14a196807482e6fc74f15fc03176d5c08880588f */
109 	[ 1] = RTNL_LINK_IP6_INPKTS,                    /* IPSTATS_MIB_INPKTS                   */
110 	[ 2] = RTNL_LINK_IP6_INOCTETS,                  /* IPSTATS_MIB_INOCTETS                 */
111 	[ 3] = RTNL_LINK_IP6_INDELIVERS,                /* IPSTATS_MIB_INDELIVERS               */
112 	[ 4] = RTNL_LINK_IP6_OUTFORWDATAGRAMS,          /* IPSTATS_MIB_OUTFORWDATAGRAMS         */
113 	[ 5] = RTNL_LINK_IP6_OUTPKTS,                   /* IPSTATS_MIB_OUTPKTS                  */
114 	[ 6] = RTNL_LINK_IP6_OUTOCTETS,                 /* IPSTATS_MIB_OUTOCTETS                */
115 	[ 7] = RTNL_LINK_IP6_INHDRERRORS,               /* IPSTATS_MIB_INHDRERRORS              */
116 	[ 8] = RTNL_LINK_IP6_INTOOBIGERRORS,            /* IPSTATS_MIB_INTOOBIGERRORS           */
117 	[ 9] = RTNL_LINK_IP6_INNOROUTES,                /* IPSTATS_MIB_INNOROUTES               */
118 	[10] = RTNL_LINK_IP6_INADDRERRORS,              /* IPSTATS_MIB_INADDRERRORS             */
119 	[11] = RTNL_LINK_IP6_INUNKNOWNPROTOS,           /* IPSTATS_MIB_INUNKNOWNPROTOS          */
120 	[12] = RTNL_LINK_IP6_INTRUNCATEDPKTS,           /* IPSTATS_MIB_INTRUNCATEDPKTS          */
121 	[13] = RTNL_LINK_IP6_INDISCARDS,                /* IPSTATS_MIB_INDISCARDS               */
122 	[14] = RTNL_LINK_IP6_OUTDISCARDS,               /* IPSTATS_MIB_OUTDISCARDS              */
123 	[15] = RTNL_LINK_IP6_OUTNOROUTES,               /* IPSTATS_MIB_OUTNOROUTES              */
124 	[16] = RTNL_LINK_IP6_REASMTIMEOUT,              /* IPSTATS_MIB_REASMTIMEOUT             */
125 	[17] = RTNL_LINK_IP6_REASMREQDS,                /* IPSTATS_MIB_REASMREQDS               */
126 	[18] = RTNL_LINK_IP6_REASMOKS,                  /* IPSTATS_MIB_REASMOKS                 */
127 	[19] = RTNL_LINK_IP6_REASMFAILS,                /* IPSTATS_MIB_REASMFAILS               */
128 	[20] = RTNL_LINK_IP6_FRAGOKS,                   /* IPSTATS_MIB_FRAGOKS                  */
129 	[21] = RTNL_LINK_IP6_FRAGFAILS,                 /* IPSTATS_MIB_FRAGFAILS                */
130 	[22] = RTNL_LINK_IP6_FRAGCREATES,               /* IPSTATS_MIB_FRAGCREATES              */
131 	[23] = RTNL_LINK_IP6_INMCASTPKTS,               /* IPSTATS_MIB_INMCASTPKTS              */
132 	[24] = RTNL_LINK_IP6_OUTMCASTPKTS,              /* IPSTATS_MIB_OUTMCASTPKTS             */
133 	[25] = RTNL_LINK_IP6_INBCASTPKTS,               /* IPSTATS_MIB_INBCASTPKTS              */
134 	[26] = RTNL_LINK_IP6_OUTBCASTPKTS,              /* IPSTATS_MIB_OUTBCASTPKTS             */
135 	[27] = RTNL_LINK_IP6_INMCASTOCTETS,             /* IPSTATS_MIB_INMCASTOCTETS            */
136 	[28] = RTNL_LINK_IP6_OUTMCASTOCTETS,            /* IPSTATS_MIB_OUTMCASTOCTETS           */
137 	[29] = RTNL_LINK_IP6_INBCASTOCTETS,             /* IPSTATS_MIB_INBCASTOCTETS            */
138 	[30] = RTNL_LINK_IP6_OUTBCASTOCTETS,            /* IPSTATS_MIB_OUTBCASTOCTETS           */
139 	[31] = RTNL_LINK_IP6_CSUMERRORS,                /* IPSTATS_MIB_CSUMERRORS               */
140 	[32] = RTNL_LINK_IP6_NOECTPKTS,                 /* IPSTATS_MIB_NOECTPKTS                */
141 	[33] = RTNL_LINK_IP6_ECT1PKTS,                  /* IPSTATS_MIB_ECT1PKTS                 */
142 	[34] = RTNL_LINK_IP6_ECT0PKTS,                  /* IPSTATS_MIB_ECT0PKTS                 */
143 	[35] = RTNL_LINK_IP6_CEPKTS,                    /* IPSTATS_MIB_CEPKTS                   */
144 };
145 
inet6_parse_protinfo(struct rtnl_link * link,struct nlattr * attr,void * data)146 static int inet6_parse_protinfo(struct rtnl_link *link, struct nlattr *attr,
147 				void *data)
148 {
149 	struct inet6_data *i6 = data;
150 	struct nlattr *tb[IFLA_INET6_MAX+1];
151 	int err;
152 
153 	err = nla_parse_nested(tb, IFLA_INET6_MAX, attr, inet6_policy);
154 	if (err < 0)
155 		return err;
156 	if (tb[IFLA_INET6_CONF] && nla_len(tb[IFLA_INET6_CONF]) % 4)
157 		return -EINVAL;
158 	if (tb[IFLA_INET6_STATS] && nla_len(tb[IFLA_INET6_STATS]) % 8)
159 		return -EINVAL;
160 	if (tb[IFLA_INET6_ICMP6STATS] && nla_len(tb[IFLA_INET6_ICMP6STATS]) % 8)
161 		return -EINVAL;
162 
163 	if (tb[IFLA_INET6_FLAGS])
164 		i6->i6_flags = nla_get_u32(tb[IFLA_INET6_FLAGS]);
165 
166 	if (tb[IFLA_INET6_CACHEINFO])
167 		nla_memcpy(&i6->i6_cacheinfo, tb[IFLA_INET6_CACHEINFO],
168 			   sizeof(i6->i6_cacheinfo));
169 
170 	if (tb[IFLA_INET6_CONF])
171 		nla_memcpy(&i6->i6_conf, tb[IFLA_INET6_CONF],
172 			   sizeof(i6->i6_conf));
173 
174 	if (tb[IFLA_INET6_TOKEN])
175 		nla_memcpy(&i6->i6_token, tb[IFLA_INET6_TOKEN],
176 		           sizeof(struct in6_addr));
177 
178 	if (tb[IFLA_INET6_ADDR_GEN_MODE])
179 		i6->i6_addr_gen_mode = nla_get_u8 (tb[IFLA_INET6_ADDR_GEN_MODE]);
180 
181 	/*
182 	 * Due to 32bit data alignment, these addresses must be copied to an
183 	 * aligned location prior to access.
184 	 */
185 	if (tb[IFLA_INET6_STATS]) {
186 		unsigned char *cnt = nla_data(tb[IFLA_INET6_STATS]);
187 		uint64_t stat;
188 		int i;
189 		int len = nla_len(tb[IFLA_INET6_STATS]) / 8;
190 		const uint8_t *map_stat_id = map_stat_id_from_IPSTATS_MIB_v2;
191 
192 		if (len < 32 ||
193 		    (tb[IFLA_INET6_ICMP6STATS] && nla_len(tb[IFLA_INET6_ICMP6STATS]) < 6)) {
194 			/* kernel commit 14a196807482e6fc74f15fc03176d5c08880588f reordered the values.
195 			 * The later commit 6a5dc9e598fe90160fee7de098fa319665f5253e added values
196 			 * IPSTATS_MIB_CSUMERRORS/ICMP6_MIB_CSUMERRORS. If the netlink is shorter
197 			 * then this, assume that the kernel uses the previous meaning of the
198 			 * enumeration. */
199 			map_stat_id = map_stat_id_from_IPSTATS_MIB_v1;
200 		}
201 
202 		len = min_t(int, __IPSTATS_MIB_MAX, len);
203 		for (i = 1; i < len; i++) {
204 			memcpy(&stat, &cnt[i * sizeof(stat)], sizeof(stat));
205 			rtnl_link_set_stat(link, map_stat_id[i], stat);
206 		}
207 	}
208 
209 	if (tb[IFLA_INET6_ICMP6STATS]) {
210 		unsigned char *cnt = nla_data(tb[IFLA_INET6_ICMP6STATS]);
211 		uint64_t stat;
212 		int i;
213 		int len = min_t(int, __ICMP6_MIB_MAX, nla_len(tb[IFLA_INET6_ICMP6STATS]) / 8);
214 
215 		for (i = 1; i < len; i++) {
216 			memcpy(&stat, &cnt[i * sizeof(stat)], sizeof(stat));
217 			rtnl_link_set_stat(link, RTNL_LINK_ICMP6_INMSGS + i - 1,
218 					   stat);
219 		}
220 	}
221 
222 	return 0;
223 }
224 
inet6_fill_af(struct rtnl_link * link,struct nl_msg * msg,void * data)225 static int inet6_fill_af(struct rtnl_link *link, struct nl_msg *msg, void *data)
226 {
227 	struct inet6_data *id = data;
228 
229 	if (id->i6_addr_gen_mode != I6_ADDR_GEN_MODE_UNKNOWN)
230 		NLA_PUT_U8(msg, IFLA_INET6_ADDR_GEN_MODE, id->i6_addr_gen_mode);
231 
232 	return 0;
233 
234 nla_put_failure:
235 	return -NLE_MSGSIZE;
236 }
237 
238 /* These live in include/net/if_inet6.h and should be moved to include/linux */
239 #define IF_RA_OTHERCONF	0x80
240 #define IF_RA_MANAGED	0x40
241 #define IF_RA_RCVD	0x20
242 #define IF_RS_SENT	0x10
243 #define IF_READY	0x80000000
244 
245 static const struct trans_tbl inet6_flags[] = {
246 	__ADD(IF_RA_OTHERCONF, ra_otherconf),
247 	__ADD(IF_RA_MANAGED, ra_managed),
248 	__ADD(IF_RA_RCVD, ra_rcvd),
249 	__ADD(IF_RS_SENT, rs_sent),
250 	__ADD(IF_READY, ready),
251 };
252 
rtnl_link_inet6_flags2str(int flags,char * buf,size_t len)253 char *rtnl_link_inet6_flags2str(int flags, char *buf, size_t len)
254 {
255 	return __flags2str(flags, buf, len, inet6_flags,
256 			   ARRAY_SIZE(inet6_flags));
257 }
258 
rtnl_link_inet6_str2flags(const char * name)259 int rtnl_link_inet6_str2flags(const char *name)
260 {
261 	return __str2flags(name, inet6_flags, ARRAY_SIZE(inet6_flags));
262 }
263 
264 static const struct trans_tbl inet6_devconf[] = {
265 	__ADD(DEVCONF_FORWARDING, forwarding),
266 	__ADD(DEVCONF_HOPLIMIT, hoplimit),
267 	__ADD(DEVCONF_MTU6, mtu6),
268 	__ADD(DEVCONF_ACCEPT_RA, accept_ra),
269 	__ADD(DEVCONF_ACCEPT_REDIRECTS, accept_redirects),
270 	__ADD(DEVCONF_AUTOCONF, autoconf),
271 	__ADD(DEVCONF_DAD_TRANSMITS, dad_transmits),
272 	__ADD(DEVCONF_RTR_SOLICITS, rtr_solicits),
273 	__ADD(DEVCONF_RTR_SOLICIT_INTERVAL, rtr_solicit_interval),
274 	__ADD(DEVCONF_RTR_SOLICIT_DELAY, rtr_solicit_delay),
275 	__ADD(DEVCONF_USE_TEMPADDR, use_tempaddr),
276 	__ADD(DEVCONF_TEMP_VALID_LFT, temp_valid_lft),
277 	__ADD(DEVCONF_TEMP_PREFERED_LFT, temp_prefered_lft),
278 	__ADD(DEVCONF_REGEN_MAX_RETRY, regen_max_retry),
279 	__ADD(DEVCONF_MAX_DESYNC_FACTOR, max_desync_factor),
280 	__ADD(DEVCONF_MAX_ADDRESSES, max_addresses),
281 	__ADD(DEVCONF_FORCE_MLD_VERSION, force_mld_version),
282 	__ADD(DEVCONF_ACCEPT_RA_DEFRTR, accept_ra_defrtr),
283 	__ADD(DEVCONF_ACCEPT_RA_PINFO, accept_ra_pinfo),
284 	__ADD(DEVCONF_ACCEPT_RA_RTR_PREF, accept_ra_rtr_pref),
285 	__ADD(DEVCONF_RTR_PROBE_INTERVAL, rtr_probe_interval),
286 	__ADD(DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN, accept_ra_rt_info),
287 	__ADD(DEVCONF_PROXY_NDP, proxy_ndp),
288 	__ADD(DEVCONF_OPTIMISTIC_DAD, optimistic_dad),
289 	__ADD(DEVCONF_ACCEPT_SOURCE_ROUTE, accept_source_route),
290 	__ADD(DEVCONF_MC_FORWARDING, mc_forwarding),
291 	__ADD(DEVCONF_DISABLE_IPV6, disable_ipv6),
292 	__ADD(DEVCONF_ACCEPT_DAD, accept_dad),
293 	__ADD(DEVCONF_FORCE_TLLAO, force_tllao),
294 };
295 
inet6_devconf2str(int type,char * buf,size_t len)296 static char *inet6_devconf2str(int type, char *buf, size_t len)
297 {
298 	return __type2str(type, buf, len, inet6_devconf,
299 			  ARRAY_SIZE(inet6_devconf));
300 }
301 
302 static const struct trans_tbl inet6_addr_gen_mode[] = {
303 	__ADD(IN6_ADDR_GEN_MODE_EUI64, eui64),
304 	__ADD(IN6_ADDR_GEN_MODE_NONE, none),
305 	__ADD(IN6_ADDR_GEN_MODE_STABLE_PRIVACY, stable_privacy),
306 };
307 
rtnl_link_inet6_addrgenmode2str(uint8_t mode,char * buf,size_t len)308 const char *rtnl_link_inet6_addrgenmode2str(uint8_t mode, char *buf, size_t len)
309 {
310 	return __type2str(mode, buf, len, inet6_addr_gen_mode,
311 			  ARRAY_SIZE(inet6_addr_gen_mode));
312 }
313 
rtnl_link_inet6_str2addrgenmode(const char * mode)314 uint8_t rtnl_link_inet6_str2addrgenmode(const char *mode)
315 {
316 	return (uint8_t) __str2type(mode, inet6_addr_gen_mode,
317 			            ARRAY_SIZE(inet6_addr_gen_mode));
318 }
319 
inet6_dump_details(struct rtnl_link * link,struct nl_dump_params * p,void * data)320 static void inet6_dump_details(struct rtnl_link *link,
321 				struct nl_dump_params *p, void *data)
322 {
323 	struct inet6_data *i6 = data;
324 	struct nl_addr *addr;
325 	int i, n = 0;
326 	char buf[64];
327 
328 	nl_dump_line(p, "    ipv6 max-reasm-len %s",
329 	             nl_size2str(i6->i6_cacheinfo.max_reasm_len, buf, sizeof(buf)));
330 
331 	nl_dump(p, " <%s>\n",
332 	        rtnl_link_inet6_flags2str(i6->i6_flags, buf, sizeof(buf)));
333 
334 	nl_dump_line(p, "      create-stamp %.2fs reachable-time %s",
335 	             (double) i6->i6_cacheinfo.tstamp / 100.,
336 	             nl_msec2str(i6->i6_cacheinfo.reachable_time, buf, sizeof(buf)));
337 
338 	nl_dump(p, " retrans-time %s\n",
339 	        nl_msec2str(i6->i6_cacheinfo.retrans_time, buf, sizeof(buf)));
340 
341 	addr = nl_addr_build(AF_INET6, &i6->i6_token, sizeof(i6->i6_token));
342 	nl_dump(p, "      token %s\n",
343 	        nl_addr2str(addr, buf, sizeof(buf)));
344 	nl_addr_put(addr);
345 
346 	nl_dump(p, "      link-local address mode %s\n",
347 	        rtnl_link_inet6_addrgenmode2str(i6->i6_addr_gen_mode,
348 	                                        buf, sizeof(buf)));
349 
350 	nl_dump_line(p, "      devconf:\n");
351 	nl_dump_line(p, "      ");
352 
353 	for (i = 0; i < DEVCONF_MAX; i++) {
354 		char buf2[64];
355 		uint32_t value = i6->i6_conf[i];
356 		int x, offset;
357 
358 		switch (i) {
359 		case DEVCONF_TEMP_VALID_LFT:
360 		case DEVCONF_TEMP_PREFERED_LFT:
361 			nl_msec2str((uint64_t) value * 1000., buf2, sizeof(buf2));
362 			break;
363 
364 		case DEVCONF_RTR_PROBE_INTERVAL:
365 		case DEVCONF_RTR_SOLICIT_INTERVAL:
366 		case DEVCONF_RTR_SOLICIT_DELAY:
367 			nl_msec2str(value, buf2, sizeof(buf2));
368 			break;
369 
370 		default:
371 			snprintf(buf2, sizeof(buf2), "%u", value);
372 			break;
373 		}
374 
375 		inet6_devconf2str(i, buf, sizeof(buf));
376 
377 		offset = 23 - strlen(buf2);
378 		if (offset < 0)
379 			offset = 0;
380 
381 		for (x = strlen(buf); x < offset; x++)
382 			buf[x] = ' ';
383 
384 		_nl_strncpy_trunc(&buf[offset], buf2, sizeof(buf) - offset);
385 
386 		nl_dump_line(p, "%s", buf);
387 
388 		if (++n == 3) {
389 			nl_dump(p, "\n");
390 			nl_dump_line(p, "      ");
391 			n = 0;
392 		} else
393 			nl_dump(p, "  ");
394 	}
395 
396 	if (n != 0)
397 		nl_dump(p, "\n");
398 }
399 
inet6_dump_stats(struct rtnl_link * link,struct nl_dump_params * p,void * data)400 static void inet6_dump_stats(struct rtnl_link *link,
401 			     struct nl_dump_params *p, void *data)
402 {
403 	double octets;
404 	char *octetsUnit;
405 
406 	nl_dump(p, "    IPv6:       InPkts           InOctets     "
407 		   "    InDiscards         InDelivers\n");
408 	nl_dump(p, "    %18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_INPKTS]);
409 
410 	octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_INOCTETS],
411 				      &octetsUnit);
412 	if (octets)
413 		nl_dump(p, "%14.2f %3s ", octets, octetsUnit);
414 	else
415 		nl_dump(p, "%16" PRIu64 " B ", 0);
416 
417 	nl_dump(p, "%18" PRIu64 " %18" PRIu64 "\n",
418 		link->l_stats[RTNL_LINK_IP6_INDISCARDS],
419 		link->l_stats[RTNL_LINK_IP6_INDELIVERS]);
420 
421 	nl_dump(p, "               OutPkts          OutOctets     "
422 		   "   OutDiscards        OutForwards\n");
423 
424 	nl_dump(p, "    %18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_OUTPKTS]);
425 
426 	octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_OUTOCTETS],
427 				      &octetsUnit);
428 	if (octets)
429 		nl_dump(p, "%14.2f %3s ", octets, octetsUnit);
430 	else
431 		nl_dump(p, "%16" PRIu64 " B ", 0);
432 
433 	nl_dump(p, "%18" PRIu64 " %18" PRIu64 "\n",
434 		link->l_stats[RTNL_LINK_IP6_OUTDISCARDS],
435 		link->l_stats[RTNL_LINK_IP6_OUTFORWDATAGRAMS]);
436 
437 	nl_dump(p, "           InMcastPkts      InMcastOctets     "
438 		   "   InBcastPkts     InBcastOctests\n");
439 
440 	nl_dump(p, "    %18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_INMCASTPKTS]);
441 
442 	octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_INMCASTOCTETS],
443 				      &octetsUnit);
444 	if (octets)
445 		nl_dump(p, "%14.2f %3s ", octets, octetsUnit);
446 	else
447 		nl_dump(p, "%16" PRIu64 " B ", 0);
448 
449 	nl_dump(p, "%18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_INBCASTPKTS]);
450 	octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_INBCASTOCTETS],
451 				      &octetsUnit);
452 	if (octets)
453 		nl_dump(p, "%14.2f %3s\n", octets, octetsUnit);
454 	else
455 		nl_dump(p, "%16" PRIu64 " B\n", 0);
456 
457 	nl_dump(p, "          OutMcastPkts     OutMcastOctets     "
458 		   "  OutBcastPkts    OutBcastOctests\n");
459 
460 	nl_dump(p, "    %18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_OUTMCASTPKTS]);
461 
462 	octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_OUTMCASTOCTETS],
463 				      &octetsUnit);
464 	if (octets)
465 		nl_dump(p, "%14.2f %3s ", octets, octetsUnit);
466 	else
467 		nl_dump(p, "%16" PRIu64 " B ", 0);
468 
469 	nl_dump(p, "%18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_OUTBCASTPKTS]);
470 	octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_OUTBCASTOCTETS],
471 				      &octetsUnit);
472 	if (octets)
473 		nl_dump(p, "%14.2f %3s\n", octets, octetsUnit);
474 	else
475 		nl_dump(p, "%16" PRIu64 " B\n", 0);
476 
477 	nl_dump(p, "              ReasmOKs         ReasmFails     "
478 		   "    ReasmReqds       ReasmTimeout\n");
479 	nl_dump(p, "    %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n",
480 		link->l_stats[RTNL_LINK_IP6_REASMOKS],
481 		link->l_stats[RTNL_LINK_IP6_REASMFAILS],
482 		link->l_stats[RTNL_LINK_IP6_REASMREQDS],
483 		link->l_stats[RTNL_LINK_IP6_REASMTIMEOUT]);
484 
485 	nl_dump(p, "               FragOKs          FragFails    "
486 		   "    FragCreates\n");
487 	nl_dump(p, "    %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n",
488 		link->l_stats[RTNL_LINK_IP6_FRAGOKS],
489 		link->l_stats[RTNL_LINK_IP6_FRAGFAILS],
490 		link->l_stats[RTNL_LINK_IP6_FRAGCREATES]);
491 
492 	nl_dump(p, "           InHdrErrors      InTooBigErrors   "
493 		   "     InNoRoutes       InAddrErrors\n");
494 	nl_dump(p, "    %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n",
495 		link->l_stats[RTNL_LINK_IP6_INHDRERRORS],
496 		link->l_stats[RTNL_LINK_IP6_INTOOBIGERRORS],
497 		link->l_stats[RTNL_LINK_IP6_INNOROUTES],
498 		link->l_stats[RTNL_LINK_IP6_INADDRERRORS]);
499 
500 	nl_dump(p, "       InUnknownProtos     InTruncatedPkts   "
501 		   "    OutNoRoutes       InCsumErrors\n");
502 	nl_dump(p, "    %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n",
503 		link->l_stats[RTNL_LINK_IP6_INUNKNOWNPROTOS],
504 		link->l_stats[RTNL_LINK_IP6_INTRUNCATEDPKTS],
505 		link->l_stats[RTNL_LINK_IP6_OUTNOROUTES],
506 		link->l_stats[RTNL_LINK_IP6_CSUMERRORS]);
507 
508 	nl_dump(p, "           InNoECTPkts          InECT1Pkts   "
509 		   "     InECT0Pkts           InCEPkts\n");
510 	nl_dump(p, "    %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n",
511 		link->l_stats[RTNL_LINK_IP6_NOECTPKTS],
512 		link->l_stats[RTNL_LINK_IP6_ECT1PKTS],
513 		link->l_stats[RTNL_LINK_IP6_ECT0PKTS],
514 		link->l_stats[RTNL_LINK_IP6_CEPKTS]);
515 
516 	nl_dump(p, "    ICMPv6:     InMsgs           InErrors        "
517 		   "    OutMsgs          OutErrors       InCsumErrors\n");
518 	nl_dump(p, "    %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n",
519 		link->l_stats[RTNL_LINK_ICMP6_INMSGS],
520 		link->l_stats[RTNL_LINK_ICMP6_INERRORS],
521 		link->l_stats[RTNL_LINK_ICMP6_OUTMSGS],
522 		link->l_stats[RTNL_LINK_ICMP6_OUTERRORS],
523 		link->l_stats[RTNL_LINK_ICMP6_CSUMERRORS]);
524 }
525 
526 static const struct nla_policy protinfo_policy = {
527 	.type			= NLA_NESTED,
528 };
529 
530 static struct rtnl_link_af_ops inet6_ops = {
531 	.ao_family			= AF_INET6,
532 	.ao_alloc			= &inet6_alloc,
533 	.ao_clone			= &inet6_clone,
534 	.ao_free			= &inet6_free,
535 	.ao_parse_protinfo		= &inet6_parse_protinfo,
536 	.ao_parse_af			= &inet6_parse_protinfo,
537 	.ao_fill_af			= &inet6_fill_af,
538 	.ao_dump[NL_DUMP_DETAILS]	= &inet6_dump_details,
539 	.ao_dump[NL_DUMP_STATS]		= &inet6_dump_stats,
540 	.ao_protinfo_policy		= &protinfo_policy,
541 };
542 
543 /**
544  * Return IPv6 specific flags
545  * @arg link		Link object
546  * @arg out_flags	Flags on success
547  *
548  * Returns the link's IPv6 flags.
549  *
550  * @return 0 on success
551  * @return -NLE_NOATTR configuration setting not available
552  */
rtnl_link_inet6_get_flags(struct rtnl_link * link,uint32_t * out_flags)553 int rtnl_link_inet6_get_flags(struct rtnl_link *link, uint32_t* out_flags)
554 {
555 	struct inet6_data *id = NULL;
556 
557 	if (!(id = rtnl_link_af_data(link, &inet6_ops)))
558 		return -NLE_NOATTR;
559 
560 	*out_flags = id->i6_flags;
561 	return 0;
562 }
563 
564 /**
565  * Set IPv6 specific flags
566  * @arg link		Link object
567  * @arg flags		Flags to set
568  *
569  * Sets the link's IPv6 specific flags. Overwrites currently set flags.
570  *
571  * @return 0 on success
572  * @return -NLE_NOMEM could not allocate inet6 data
573  */
rtnl_link_inet6_set_flags(struct rtnl_link * link,uint32_t flags)574 int rtnl_link_inet6_set_flags(struct rtnl_link *link, uint32_t flags)
575 {
576 	struct inet6_data *id;
577 
578 	if (!(id = rtnl_link_af_alloc(link, &inet6_ops)))
579 		return -NLE_NOMEM;
580 
581 	id->i6_flags = flags;
582 	return 0;
583 }
584 
585 /**
586  * Get IPv6 tokenized interface identifier
587  * @arg link		Link object
588  * @arg token		Tokenized interface identifier on success
589  *
590  * Returns the link's IPv6 tokenized interface identifier.
591  *
592  * @return 0 on success
593  * @return -NLE_NOMEM  failure to allocate struct nl_addr result
594  * @return -NLE_NOATTR configuration setting not available
595  * @return -NLE_NOADDR tokenized interface identifier is not set
596  */
rtnl_link_inet6_get_token(struct rtnl_link * link,struct nl_addr ** addr)597 int rtnl_link_inet6_get_token(struct rtnl_link *link, struct nl_addr **addr)
598 {
599 	struct inet6_data *id;
600 
601 	if (!(id = rtnl_link_af_data(link, &inet6_ops)))
602 		return -NLE_NOATTR;
603 
604 	*addr = nl_addr_build(AF_INET6, &id->i6_token, sizeof(id->i6_token));
605 	if (!*addr)
606 		return -NLE_NOMEM;
607 	if (nl_addr_iszero(*addr)) {
608 		nl_addr_put(*addr);
609 		*addr = NULL;
610 		return -NLE_NOADDR;
611 	}
612 
613 	return 0;
614 }
615 
616 /**
617  * Set IPv6 tokenized interface identifier
618  * @arg link		Link object
619  * @arg token		Tokenized interface identifier
620  *
621  * Sets the link's IPv6 tokenized interface identifier.
622  *
623  * @return 0 on success
624  * @return -NLE_NOMEM could not allocate inet6 data
625  * @return -NLE_INVAL addr is not a valid inet6 address
626  */
rtnl_link_inet6_set_token(struct rtnl_link * link,struct nl_addr * addr)627 int rtnl_link_inet6_set_token(struct rtnl_link *link, struct nl_addr *addr)
628 {
629 	struct inet6_data *id;
630 
631 	if ((nl_addr_get_family(addr) != AF_INET6) ||
632 	    (nl_addr_get_len(addr) != sizeof(id->i6_token)))
633 		return -NLE_INVAL;
634 
635 	if (!(id = rtnl_link_af_alloc(link, &inet6_ops)))
636 		return -NLE_NOMEM;
637 
638 	memcpy(&id->i6_token, nl_addr_get_binary_addr(addr),
639 	       sizeof(id->i6_token));
640 	return 0;
641 }
642 
643 /**
644  * Get IPv6 link-local address generation mode
645  * @arg link		Link object
646  * @arg mode		Generation mode on success
647  *
648  * Returns the link's IPv6 link-local address generation mode.
649  *
650  * @return 0 on success
651  * @return -NLE_NOATTR configuration setting not available
652  * @return -NLE_INVAL generation mode unknown. If the link was received via
653  *                    netlink, it means that address generation mode is not
654  *                    supported by the kernel.
655  */
rtnl_link_inet6_get_addr_gen_mode(struct rtnl_link * link,uint8_t * mode)656 int rtnl_link_inet6_get_addr_gen_mode(struct rtnl_link *link, uint8_t *mode)
657 {
658 	struct inet6_data *id;
659 
660 	if (!(id = rtnl_link_af_data(link, &inet6_ops)))
661 		return -NLE_NOATTR;
662 
663 	if (id->i6_addr_gen_mode == I6_ADDR_GEN_MODE_UNKNOWN)
664 		return -NLE_INVAL;
665 
666 	*mode = id->i6_addr_gen_mode;
667 	return 0;
668 }
669 
670 /**
671  * Set IPv6 link-local address generation mode
672  * @arg link		Link object
673  * @arg mode		Generation mode
674  *
675  * Sets the link's IPv6 link-local address generation mode.
676  *
677  * @return 0 on success
678  * @return -NLE_NOMEM could not allocate inet6 data
679  */
rtnl_link_inet6_set_addr_gen_mode(struct rtnl_link * link,uint8_t mode)680 int rtnl_link_inet6_set_addr_gen_mode(struct rtnl_link *link, uint8_t mode)
681 {
682 	struct inet6_data *id;
683 
684 	if (!(id = rtnl_link_af_alloc(link, &inet6_ops)))
685 		return -NLE_NOMEM;
686 
687 	id->i6_addr_gen_mode = mode;
688 	return 0;
689 }
690 
inet6_init(void)691 static void __init inet6_init(void)
692 {
693 	rtnl_link_af_register(&inet6_ops);
694 }
695 
inet6_exit(void)696 static void __exit inet6_exit(void)
697 {
698 	rtnl_link_af_unregister(&inet6_ops);
699 }
700