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