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