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-private/route/link/api.h>
17
18struct inet6_data
19{
20	uint32_t		i6_flags;
21	struct ifla_cacheinfo	i6_cacheinfo;
22	uint32_t		i6_conf[DEVCONF_MAX];
23};
24
25static void *inet6_alloc(struct rtnl_link *link)
26{
27	return calloc(1, sizeof(struct inet6_data));
28}
29
30static void *inet6_clone(struct rtnl_link *link, void *data)
31{
32	struct inet6_data *i6;
33
34	if ((i6 = inet6_alloc(link)))
35		memcpy(i6, data, sizeof(*i6));
36
37	return i6;
38}
39
40static void inet6_free(struct rtnl_link *link, void *data)
41{
42	free(data);
43}
44
45static struct nla_policy inet6_policy[IFLA_INET6_MAX+1] = {
46	[IFLA_INET6_FLAGS]	= { .type = NLA_U32 },
47	[IFLA_INET6_CACHEINFO]	= { .minlen = sizeof(struct ifla_cacheinfo) },
48	[IFLA_INET6_CONF]	= { .minlen = 4 },
49	[IFLA_INET6_STATS]	= { .minlen = 8 },
50	[IFLA_INET6_ICMP6STATS]	= { .minlen = 8 },
51};
52
53static const uint8_t map_stat_id_from_IPSTATS_MIB_v1[__IPSTATS_MIB_MAX] = {
54	/* 14a196807482e6fc74f15fc03176d5c08880588f^:include/linux/snmp.h
55	 * version before the API change in commit 14a196807482e6fc74f15fc03176d5c08880588f.
56	 * This version was valid since commit edf391ff17232f097d72441c9ad467bcb3b5db18, which
57	 * predates support for parsing IFLA_PROTINFO in libnl3. Such an even older meaning of
58	 * the flags is not supported in libnl3. */
59	[ 1] = RTNL_LINK_IP6_INPKTS,                    /* IPSTATS_MIB_INPKTS                   */
60	[ 2] = RTNL_LINK_IP6_INHDRERRORS,               /* IPSTATS_MIB_INHDRERRORS              */
61	[ 3] = RTNL_LINK_IP6_INTOOBIGERRORS,            /* IPSTATS_MIB_INTOOBIGERRORS           */
62	[ 4] = RTNL_LINK_IP6_INNOROUTES,                /* IPSTATS_MIB_INNOROUTES               */
63	[ 5] = RTNL_LINK_IP6_INADDRERRORS,              /* IPSTATS_MIB_INADDRERRORS             */
64	[ 6] = RTNL_LINK_IP6_INUNKNOWNPROTOS,           /* IPSTATS_MIB_INUNKNOWNPROTOS          */
65	[ 7] = RTNL_LINK_IP6_INTRUNCATEDPKTS,           /* IPSTATS_MIB_INTRUNCATEDPKTS          */
66	[ 8] = RTNL_LINK_IP6_INDISCARDS,                /* IPSTATS_MIB_INDISCARDS               */
67	[ 9] = RTNL_LINK_IP6_INDELIVERS,                /* IPSTATS_MIB_INDELIVERS               */
68	[10] = RTNL_LINK_IP6_OUTFORWDATAGRAMS,          /* IPSTATS_MIB_OUTFORWDATAGRAMS         */
69	[11] = RTNL_LINK_IP6_OUTPKTS,                   /* IPSTATS_MIB_OUTPKTS                  */
70	[12] = RTNL_LINK_IP6_OUTDISCARDS,               /* IPSTATS_MIB_OUTDISCARDS              */
71	[13] = RTNL_LINK_IP6_OUTNOROUTES,               /* IPSTATS_MIB_OUTNOROUTES              */
72	[14] = RTNL_LINK_IP6_REASMTIMEOUT,              /* IPSTATS_MIB_REASMTIMEOUT             */
73	[15] = RTNL_LINK_IP6_REASMREQDS,                /* IPSTATS_MIB_REASMREQDS               */
74	[16] = RTNL_LINK_IP6_REASMOKS,                  /* IPSTATS_MIB_REASMOKS                 */
75	[17] = RTNL_LINK_IP6_REASMFAILS,                /* IPSTATS_MIB_REASMFAILS               */
76	[18] = RTNL_LINK_IP6_FRAGOKS,                   /* IPSTATS_MIB_FRAGOKS                  */
77	[19] = RTNL_LINK_IP6_FRAGFAILS,                 /* IPSTATS_MIB_FRAGFAILS                */
78	[20] = RTNL_LINK_IP6_FRAGCREATES,               /* IPSTATS_MIB_FRAGCREATES              */
79	[21] = RTNL_LINK_IP6_INMCASTPKTS,               /* IPSTATS_MIB_INMCASTPKTS              */
80	[22] = RTNL_LINK_IP6_OUTMCASTPKTS,              /* IPSTATS_MIB_OUTMCASTPKTS             */
81	[23] = RTNL_LINK_IP6_INBCASTPKTS,               /* IPSTATS_MIB_INBCASTPKTS              */
82	[24] = RTNL_LINK_IP6_OUTBCASTPKTS,              /* IPSTATS_MIB_OUTBCASTPKTS             */
83	[25] = RTNL_LINK_IP6_INOCTETS,                  /* IPSTATS_MIB_INOCTETS                 */
84	[26] = RTNL_LINK_IP6_OUTOCTETS,                 /* IPSTATS_MIB_OUTOCTETS                */
85	[27] = RTNL_LINK_IP6_INMCASTOCTETS,             /* IPSTATS_MIB_INMCASTOCTETS            */
86	[28] = RTNL_LINK_IP6_OUTMCASTOCTETS,            /* IPSTATS_MIB_OUTMCASTOCTETS           */
87	[29] = RTNL_LINK_IP6_INBCASTOCTETS,             /* IPSTATS_MIB_INBCASTOCTETS            */
88	[30] = RTNL_LINK_IP6_OUTBCASTOCTETS,            /* IPSTATS_MIB_OUTBCASTOCTETS           */
89};
90
91static const uint8_t map_stat_id_from_IPSTATS_MIB_v2[__IPSTATS_MIB_MAX] = {
92	/* d8ec26d7f8287f5788a494f56e8814210f0e64be:include/uapi/linux/snmp.h
93	 * version since the API change in commit 14a196807482e6fc74f15fc03176d5c08880588f */
94	[ 1] = RTNL_LINK_IP6_INPKTS,                    /* IPSTATS_MIB_INPKTS                   */
95	[ 2] = RTNL_LINK_IP6_INOCTETS,                  /* IPSTATS_MIB_INOCTETS                 */
96	[ 3] = RTNL_LINK_IP6_INDELIVERS,                /* IPSTATS_MIB_INDELIVERS               */
97	[ 4] = RTNL_LINK_IP6_OUTFORWDATAGRAMS,          /* IPSTATS_MIB_OUTFORWDATAGRAMS         */
98	[ 5] = RTNL_LINK_IP6_OUTPKTS,                   /* IPSTATS_MIB_OUTPKTS                  */
99	[ 6] = RTNL_LINK_IP6_OUTOCTETS,                 /* IPSTATS_MIB_OUTOCTETS                */
100	[ 7] = RTNL_LINK_IP6_INHDRERRORS,               /* IPSTATS_MIB_INHDRERRORS              */
101	[ 8] = RTNL_LINK_IP6_INTOOBIGERRORS,            /* IPSTATS_MIB_INTOOBIGERRORS           */
102	[ 9] = RTNL_LINK_IP6_INNOROUTES,                /* IPSTATS_MIB_INNOROUTES               */
103	[10] = RTNL_LINK_IP6_INADDRERRORS,              /* IPSTATS_MIB_INADDRERRORS             */
104	[11] = RTNL_LINK_IP6_INUNKNOWNPROTOS,           /* IPSTATS_MIB_INUNKNOWNPROTOS          */
105	[12] = RTNL_LINK_IP6_INTRUNCATEDPKTS,           /* IPSTATS_MIB_INTRUNCATEDPKTS          */
106	[13] = RTNL_LINK_IP6_INDISCARDS,                /* IPSTATS_MIB_INDISCARDS               */
107	[14] = RTNL_LINK_IP6_OUTDISCARDS,               /* IPSTATS_MIB_OUTDISCARDS              */
108	[15] = RTNL_LINK_IP6_OUTNOROUTES,               /* IPSTATS_MIB_OUTNOROUTES              */
109	[16] = RTNL_LINK_IP6_REASMTIMEOUT,              /* IPSTATS_MIB_REASMTIMEOUT             */
110	[17] = RTNL_LINK_IP6_REASMREQDS,                /* IPSTATS_MIB_REASMREQDS               */
111	[18] = RTNL_LINK_IP6_REASMOKS,                  /* IPSTATS_MIB_REASMOKS                 */
112	[19] = RTNL_LINK_IP6_REASMFAILS,                /* IPSTATS_MIB_REASMFAILS               */
113	[20] = RTNL_LINK_IP6_FRAGOKS,                   /* IPSTATS_MIB_FRAGOKS                  */
114	[21] = RTNL_LINK_IP6_FRAGFAILS,                 /* IPSTATS_MIB_FRAGFAILS                */
115	[22] = RTNL_LINK_IP6_FRAGCREATES,               /* IPSTATS_MIB_FRAGCREATES              */
116	[23] = RTNL_LINK_IP6_INMCASTPKTS,               /* IPSTATS_MIB_INMCASTPKTS              */
117	[24] = RTNL_LINK_IP6_OUTMCASTPKTS,              /* IPSTATS_MIB_OUTMCASTPKTS             */
118	[25] = RTNL_LINK_IP6_INBCASTPKTS,               /* IPSTATS_MIB_INBCASTPKTS              */
119	[26] = RTNL_LINK_IP6_OUTBCASTPKTS,              /* IPSTATS_MIB_OUTBCASTPKTS             */
120	[27] = RTNL_LINK_IP6_INMCASTOCTETS,             /* IPSTATS_MIB_INMCASTOCTETS            */
121	[28] = RTNL_LINK_IP6_OUTMCASTOCTETS,            /* IPSTATS_MIB_OUTMCASTOCTETS           */
122	[29] = RTNL_LINK_IP6_INBCASTOCTETS,             /* IPSTATS_MIB_INBCASTOCTETS            */
123	[30] = RTNL_LINK_IP6_OUTBCASTOCTETS,            /* IPSTATS_MIB_OUTBCASTOCTETS           */
124	[31] = RTNL_LINK_IP6_CSUMERRORS,                /* IPSTATS_MIB_CSUMERRORS               */
125	[32] = RTNL_LINK_IP6_NOECTPKTS,                 /* IPSTATS_MIB_NOECTPKTS                */
126	[33] = RTNL_LINK_IP6_ECT1PKTS,                  /* IPSTATS_MIB_ECT1PKTS                 */
127	[34] = RTNL_LINK_IP6_ECT0PKTS,                  /* IPSTATS_MIB_ECT0PKTS                 */
128	[35] = RTNL_LINK_IP6_CEPKTS,                    /* IPSTATS_MIB_CEPKTS                   */
129};
130
131static int inet6_parse_protinfo(struct rtnl_link *link, struct nlattr *attr,
132				void *data)
133{
134	struct inet6_data *i6 = data;
135	struct nlattr *tb[IFLA_INET6_MAX+1];
136	int err;
137
138	err = nla_parse_nested(tb, IFLA_INET6_MAX, attr, inet6_policy);
139	if (err < 0)
140		return err;
141	if (tb[IFLA_INET6_CONF] && nla_len(tb[IFLA_INET6_CONF]) % 4)
142		return -EINVAL;
143	if (tb[IFLA_INET6_STATS] && nla_len(tb[IFLA_INET6_STATS]) % 8)
144		return -EINVAL;
145	if (tb[IFLA_INET6_ICMP6STATS] && nla_len(tb[IFLA_INET6_ICMP6STATS]) % 8)
146		return -EINVAL;
147
148	if (tb[IFLA_INET6_FLAGS])
149		i6->i6_flags = nla_get_u32(tb[IFLA_INET6_FLAGS]);
150
151	if (tb[IFLA_INET6_CACHEINFO])
152		nla_memcpy(&i6->i6_cacheinfo, tb[IFLA_INET6_CACHEINFO],
153			   sizeof(i6->i6_cacheinfo));
154
155	if (tb[IFLA_INET6_CONF])
156		nla_memcpy(&i6->i6_conf, tb[IFLA_INET6_CONF],
157			   sizeof(i6->i6_conf));
158
159	/*
160	 * Due to 32bit data alignment, these addresses must be copied to an
161	 * aligned location prior to access.
162	 */
163	if (tb[IFLA_INET6_STATS]) {
164		unsigned char *cnt = nla_data(tb[IFLA_INET6_STATS]);
165		uint64_t stat;
166		int i;
167		int len = nla_len(tb[IFLA_INET6_STATS]) / 8;
168		const uint8_t *map_stat_id = map_stat_id_from_IPSTATS_MIB_v2;
169
170		if (len < 32 ||
171		    (tb[IFLA_INET6_ICMP6STATS] && nla_len(tb[IFLA_INET6_ICMP6STATS]) < 6)) {
172			/* kernel commit 14a196807482e6fc74f15fc03176d5c08880588f reordered the values.
173			 * The later commit 6a5dc9e598fe90160fee7de098fa319665f5253e added values
174			 * IPSTATS_MIB_CSUMERRORS/ICMP6_MIB_CSUMERRORS. If the netlink is shorter
175			 * then this, assume that the kernel uses the previous meaning of the
176			 * enumeration. */
177			map_stat_id = map_stat_id_from_IPSTATS_MIB_v1;
178		}
179
180		len = min_t(int, __IPSTATS_MIB_MAX, len);
181		for (i = 1; i < len; i++) {
182			memcpy(&stat, &cnt[i * sizeof(stat)], sizeof(stat));
183			rtnl_link_set_stat(link, map_stat_id[i], stat);
184		}
185	}
186
187	if (tb[IFLA_INET6_ICMP6STATS]) {
188		unsigned char *cnt = nla_data(tb[IFLA_INET6_ICMP6STATS]);
189		uint64_t stat;
190		int i;
191		int len = min_t(int, __ICMP6_MIB_MAX, nla_len(tb[IFLA_INET6_ICMP6STATS]) / 8);
192
193		for (i = 1; i < len; i++) {
194			memcpy(&stat, &cnt[i * sizeof(stat)], sizeof(stat));
195			rtnl_link_set_stat(link, RTNL_LINK_ICMP6_INMSGS + i - 1,
196					   stat);
197		}
198	}
199
200	return 0;
201}
202
203/* These live in include/net/if_inet6.h and should be moved to include/linux */
204#define IF_RA_OTHERCONF	0x80
205#define IF_RA_MANAGED	0x40
206#define IF_RA_RCVD	0x20
207#define IF_RS_SENT	0x10
208#define IF_READY	0x80000000
209
210static const struct trans_tbl inet6_flags[] = {
211	__ADD(IF_RA_OTHERCONF, ra_otherconf)
212	__ADD(IF_RA_MANAGED, ra_managed)
213	__ADD(IF_RA_RCVD, ra_rcvd)
214	__ADD(IF_RS_SENT, rs_sent)
215	__ADD(IF_READY, ready)
216};
217
218static char *inet6_flags2str(int flags, char *buf, size_t len)
219{
220	return __flags2str(flags, buf, len, inet6_flags,
221			   ARRAY_SIZE(inet6_flags));
222}
223
224static const struct trans_tbl inet6_devconf[] = {
225	__ADD(DEVCONF_FORWARDING, forwarding)
226	__ADD(DEVCONF_HOPLIMIT, hoplimit)
227	__ADD(DEVCONF_MTU6, mtu6)
228	__ADD(DEVCONF_ACCEPT_RA, accept_ra)
229	__ADD(DEVCONF_ACCEPT_REDIRECTS, accept_redirects)
230	__ADD(DEVCONF_AUTOCONF, autoconf)
231	__ADD(DEVCONF_DAD_TRANSMITS, dad_transmits)
232	__ADD(DEVCONF_RTR_SOLICITS, rtr_solicits)
233	__ADD(DEVCONF_RTR_SOLICIT_INTERVAL, rtr_solicit_interval)
234	__ADD(DEVCONF_RTR_SOLICIT_DELAY, rtr_solicit_delay)
235	__ADD(DEVCONF_USE_TEMPADDR, use_tempaddr)
236	__ADD(DEVCONF_TEMP_VALID_LFT, temp_valid_lft)
237	__ADD(DEVCONF_TEMP_PREFERED_LFT, temp_prefered_lft)
238	__ADD(DEVCONF_REGEN_MAX_RETRY, regen_max_retry)
239	__ADD(DEVCONF_MAX_DESYNC_FACTOR, max_desync_factor)
240	__ADD(DEVCONF_MAX_ADDRESSES, max_addresses)
241	__ADD(DEVCONF_FORCE_MLD_VERSION, force_mld_version)
242	__ADD(DEVCONF_ACCEPT_RA_DEFRTR, accept_ra_defrtr)
243	__ADD(DEVCONF_ACCEPT_RA_PINFO, accept_ra_pinfo)
244	__ADD(DEVCONF_ACCEPT_RA_RTR_PREF, accept_ra_rtr_pref)
245	__ADD(DEVCONF_RTR_PROBE_INTERVAL, rtr_probe_interval)
246	__ADD(DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN, accept_ra_rt_info)
247	__ADD(DEVCONF_PROXY_NDP, proxy_ndp)
248	__ADD(DEVCONF_OPTIMISTIC_DAD, optimistic_dad)
249	__ADD(DEVCONF_ACCEPT_SOURCE_ROUTE, accept_source_route)
250	__ADD(DEVCONF_MC_FORWARDING, mc_forwarding)
251	__ADD(DEVCONF_DISABLE_IPV6, disable_ipv6)
252	__ADD(DEVCONF_ACCEPT_DAD, accept_dad)
253	__ADD(DEVCONF_FORCE_TLLAO, force_tllao)
254};
255
256static char *inet6_devconf2str(int type, char *buf, size_t len)
257{
258	return __type2str(type, buf, len, inet6_devconf,
259			  ARRAY_SIZE(inet6_devconf));
260}
261
262
263static void inet6_dump_details(struct rtnl_link *link,
264				struct nl_dump_params *p, void *data)
265{
266	struct inet6_data *i6 = data;
267	char buf[64], buf2[64];
268	int i, n = 0;
269
270	nl_dump_line(p, "    ipv6 max-reasm-len %s",
271		nl_size2str(i6->i6_cacheinfo.max_reasm_len, buf, sizeof(buf)));
272
273	nl_dump(p, " <%s>\n",
274		inet6_flags2str(i6->i6_flags, buf, sizeof(buf)));
275
276
277	nl_dump_line(p, "      create-stamp %.2fs reachable-time %s",
278		(double) i6->i6_cacheinfo.tstamp / 100.,
279		nl_msec2str(i6->i6_cacheinfo.reachable_time, buf, sizeof(buf)));
280
281	nl_dump(p, " retrans-time %s\n",
282		nl_msec2str(i6->i6_cacheinfo.retrans_time, buf, sizeof(buf)));
283
284	nl_dump_line(p, "      devconf:\n");
285	nl_dump_line(p, "      ");
286
287	for (i = 0; i < DEVCONF_MAX; i++) {
288		uint32_t value = i6->i6_conf[i];
289		int x, offset;
290
291		switch (i) {
292		case DEVCONF_TEMP_VALID_LFT:
293		case DEVCONF_TEMP_PREFERED_LFT:
294			nl_msec2str((uint64_t) value * 1000., buf2, sizeof(buf2));
295			break;
296
297		case DEVCONF_RTR_PROBE_INTERVAL:
298		case DEVCONF_RTR_SOLICIT_INTERVAL:
299		case DEVCONF_RTR_SOLICIT_DELAY:
300			nl_msec2str(value, buf2, sizeof(buf2));
301			break;
302
303		default:
304			snprintf(buf2, sizeof(buf2), "%u", value);
305			break;
306
307		}
308
309		inet6_devconf2str(i, buf, sizeof(buf));
310
311		offset = 23 - strlen(buf2);
312		if (offset < 0)
313			offset = 0;
314
315		for (x = strlen(buf); x < offset; x++)
316			buf[x] = ' ';
317
318		strncpy(&buf[offset], buf2, strlen(buf2));
319
320		nl_dump_line(p, "%s", buf);
321
322		if (++n == 3) {
323			nl_dump(p, "\n");
324			nl_dump_line(p, "      ");
325			n = 0;
326		} else
327			nl_dump(p, "  ");
328	}
329
330	if (n != 0)
331		nl_dump(p, "\n");
332}
333
334static void inet6_dump_stats(struct rtnl_link *link,
335			     struct nl_dump_params *p, void *data)
336{
337	double octets;
338	char *octetsUnit;
339
340	nl_dump(p, "    IPv6:       InPkts           InOctets     "
341		   "    InDiscards         InDelivers\n");
342	nl_dump(p, "    %18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_INPKTS]);
343
344	octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_INOCTETS],
345				      &octetsUnit);
346	if (octets)
347		nl_dump(p, "%14.2f %3s ", octets, octetsUnit);
348	else
349		nl_dump(p, "%16" PRIu64 " B ", 0);
350
351	nl_dump(p, "%18" PRIu64 " %18" PRIu64 "\n",
352		link->l_stats[RTNL_LINK_IP6_INDISCARDS],
353		link->l_stats[RTNL_LINK_IP6_INDELIVERS]);
354
355	nl_dump(p, "               OutPkts          OutOctets     "
356		   "   OutDiscards        OutForwards\n");
357
358	nl_dump(p, "    %18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_OUTPKTS]);
359
360	octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_OUTOCTETS],
361				      &octetsUnit);
362	if (octets)
363		nl_dump(p, "%14.2f %3s ", octets, octetsUnit);
364	else
365		nl_dump(p, "%16" PRIu64 " B ", 0);
366
367	nl_dump(p, "%18" PRIu64 " %18" PRIu64 "\n",
368		link->l_stats[RTNL_LINK_IP6_OUTDISCARDS],
369		link->l_stats[RTNL_LINK_IP6_OUTFORWDATAGRAMS]);
370
371	nl_dump(p, "           InMcastPkts      InMcastOctets     "
372		   "   InBcastPkts     InBcastOctests\n");
373
374	nl_dump(p, "    %18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_INMCASTPKTS]);
375
376	octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_INMCASTOCTETS],
377				      &octetsUnit);
378	if (octets)
379		nl_dump(p, "%14.2f %3s ", octets, octetsUnit);
380	else
381		nl_dump(p, "%16" PRIu64 " B ", 0);
382
383	nl_dump(p, "%18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_INBCASTPKTS]);
384	octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_INBCASTOCTETS],
385				      &octetsUnit);
386	if (octets)
387		nl_dump(p, "%14.2f %3s\n", octets, octetsUnit);
388	else
389		nl_dump(p, "%16" PRIu64 " B\n", 0);
390
391	nl_dump(p, "          OutMcastPkts     OutMcastOctets     "
392		   "  OutBcastPkts    OutBcastOctests\n");
393
394	nl_dump(p, "    %18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_OUTMCASTPKTS]);
395
396	octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_OUTMCASTOCTETS],
397				      &octetsUnit);
398	if (octets)
399		nl_dump(p, "%14.2f %3s ", octets, octetsUnit);
400	else
401		nl_dump(p, "%16" PRIu64 " B ", 0);
402
403	nl_dump(p, "%18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_OUTBCASTPKTS]);
404	octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_OUTBCASTOCTETS],
405				      &octetsUnit);
406	if (octets)
407		nl_dump(p, "%14.2f %3s\n", octets, octetsUnit);
408	else
409		nl_dump(p, "%16" PRIu64 " B\n", 0);
410
411	nl_dump(p, "              ReasmOKs         ReasmFails     "
412		   "    ReasmReqds       ReasmTimeout\n");
413	nl_dump(p, "    %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n",
414		link->l_stats[RTNL_LINK_IP6_REASMOKS],
415		link->l_stats[RTNL_LINK_IP6_REASMFAILS],
416		link->l_stats[RTNL_LINK_IP6_REASMREQDS],
417		link->l_stats[RTNL_LINK_IP6_REASMTIMEOUT]);
418
419	nl_dump(p, "               FragOKs          FragFails    "
420		   "    FragCreates\n");
421	nl_dump(p, "    %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n",
422		link->l_stats[RTNL_LINK_IP6_FRAGOKS],
423		link->l_stats[RTNL_LINK_IP6_FRAGFAILS],
424		link->l_stats[RTNL_LINK_IP6_FRAGCREATES]);
425
426	nl_dump(p, "           InHdrErrors      InTooBigErrors   "
427		   "     InNoRoutes       InAddrErrors\n");
428	nl_dump(p, "    %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n",
429		link->l_stats[RTNL_LINK_IP6_INHDRERRORS],
430		link->l_stats[RTNL_LINK_IP6_INTOOBIGERRORS],
431		link->l_stats[RTNL_LINK_IP6_INNOROUTES],
432		link->l_stats[RTNL_LINK_IP6_INADDRERRORS]);
433
434	nl_dump(p, "       InUnknownProtos     InTruncatedPkts   "
435		   "    OutNoRoutes       InCsumErrors\n");
436	nl_dump(p, "    %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n",
437		link->l_stats[RTNL_LINK_IP6_INUNKNOWNPROTOS],
438		link->l_stats[RTNL_LINK_IP6_INTRUNCATEDPKTS],
439		link->l_stats[RTNL_LINK_IP6_OUTNOROUTES],
440		link->l_stats[RTNL_LINK_IP6_CSUMERRORS]);
441
442	nl_dump(p, "           InNoECTPkts          InECT1Pkts   "
443		   "     InECT0Pkts           InCEPkts\n");
444	nl_dump(p, "    %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n",
445		link->l_stats[RTNL_LINK_IP6_NOECTPKTS],
446		link->l_stats[RTNL_LINK_IP6_ECT1PKTS],
447		link->l_stats[RTNL_LINK_IP6_ECT0PKTS],
448		link->l_stats[RTNL_LINK_IP6_CEPKTS]);
449
450	nl_dump(p, "    ICMPv6:     InMsgs           InErrors        "
451		   "    OutMsgs          OutErrors       InCsumErrors\n");
452	nl_dump(p, "    %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n",
453		link->l_stats[RTNL_LINK_ICMP6_INMSGS],
454		link->l_stats[RTNL_LINK_ICMP6_INERRORS],
455		link->l_stats[RTNL_LINK_ICMP6_OUTMSGS],
456		link->l_stats[RTNL_LINK_ICMP6_OUTERRORS],
457		link->l_stats[RTNL_LINK_ICMP6_CSUMERRORS]);
458}
459
460static const struct nla_policy protinfo_policy = {
461	.type			= NLA_NESTED,
462};
463
464static struct rtnl_link_af_ops inet6_ops = {
465	.ao_family			= AF_INET6,
466	.ao_alloc			= &inet6_alloc,
467	.ao_clone			= &inet6_clone,
468	.ao_free			= &inet6_free,
469	.ao_parse_protinfo		= &inet6_parse_protinfo,
470	.ao_parse_af			= &inet6_parse_protinfo,
471	.ao_dump[NL_DUMP_DETAILS]	= &inet6_dump_details,
472	.ao_dump[NL_DUMP_STATS]		= &inet6_dump_stats,
473	.ao_protinfo_policy		= &protinfo_policy,
474};
475
476static void __init inet6_init(void)
477{
478	rtnl_link_af_register(&inet6_ops);
479}
480
481static void __exit inet6_exit(void)
482{
483	rtnl_link_af_unregister(&inet6_ops);
484}
485