1 /*
2  * IFLA_LINKINFO netlink attribute decoding check.
3  *
4  * Copyright (c) 2018 The strace developers.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. The name of the author may not be used to endorse or promote products
16  *    derived from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include "tests.h"
31 
32 #include <inttypes.h>
33 #include <stdio.h>
34 #include <stddef.h>
35 #include <arpa/inet.h>
36 
37 #include "test_nlattr.h"
38 
39 #include <linux/if.h>
40 #include <linux/if_arp.h>
41 #ifdef HAVE_LINUX_IF_LINK_H
42 # include <linux/if_link.h>
43 #endif
44 #include <linux/rtnetlink.h>
45 
46 #define XLAT_MACROS_ONLY
47 # include <xlat/rtnl_link_attrs.h>
48 # include <xlat/rtnl_ifla_info_attrs.h>
49 #undef XLAT_MACROS_ONLY
50 
51 #define IFLA_ATTR IFLA_LINKINFO
52 #include "nlattr_ifla.h"
53 
54 #define COMMA ,
55 #define TEST_UNKNOWN_TUNNELS(fd_, nlh0_, objtype_, objtype_str_,	\
56 			     obj_, objsz_, arrstrs_, ...)		\
57 	do {								\
58 		/* 64 is guestimate for maximum unknown type len */	\
59 		char buf[8 * 2 + 64 + objsz_];				\
60 		const char **arrstrs[] = arrstrs_;			\
61 		const char ***arrstrs_pos = arrstrs;			\
62 		const char **arrstr = *arrstrs_pos;			\
63 		const char *type = NULL;				\
64 									\
65 		for (type = arrstr ? arrstr[0] : NULL; type && arrstr;	\
66 		     type = (++arrstr)[0] ? arrstr[0]			\
67 					  : (++arrstrs_pos)[0]		\
68 				             ? (arrstr = arrstrs_pos[0])[0] \
69 					     : NULL)			\
70 		{							\
71 			size_t type_len = strlen(type) + 1;		\
72 									\
73 			if (type_len > 64)				\
74 				error_msg_and_fail("Unexpectedly long "	\
75 						   "unknown type: \"%s\" " \
76 						   "(length is %zu)",	\
77 						   type, type_len);	\
78 									\
79 			struct nlattr obj_nla = {			\
80 				.nla_len = NLA_HDRLEN + (objsz_),	\
81 				.nla_type = (objtype_),			\
82 			};						\
83 									\
84 			char *pos = buf;				\
85 			memcpy(pos, type, type_len);			\
86 			pos += NLA_ALIGN(type_len);			\
87 			memcpy(pos, &obj_nla, sizeof(obj_nla));		\
88 			pos += sizeof(obj_nla);				\
89 			memcpy(pos, (obj_), (objsz_));			\
90 									\
91 			TEST_NLATTR_EX_((fd_),				\
92 					(nlh0_) - hdrlen - (pos - buf),	\
93 					hdrlen + NLA_HDRLEN,		\
94 					init_ifinfomsg, print_ifinfomsg, \
95 					IFLA_INFO_KIND, "IFLA_INFO_KIND", \
96 					type_len, objsz_ + (pos - buf),	\
97 					buf, objsz_ + (pos - buf),	\
98 					printf("\"%s\"}", type);	\
99 					printf(", {{nla_len=%zu"	\
100 				               ", nla_type=%s}, ",	\
101 					       (objsz_) + NLA_HDRLEN,	\
102 					       (objtype_str_));		\
103 									\
104 					{ __VA_ARGS__; }		\
105 									\
106 					printf("}"));			\
107 		}							\
108 	} while (0)
109 
110 #define TEST_LINKINFO_(fd_, nlh0_, nla_type_, nla_type_str_, tuntype_,	\
111 		       obj_, objsz_, pattern_, fallback_func_, ...)	\
112 	do {								\
113 		size_t tuntype_len = strlen(tuntype_) + 1;		\
114 		char *buf = tail_alloc(NLA_ALIGN(tuntype_len)		\
115 				       + NLA_HDRLEN + (objsz_));	\
116 		char *pos = buf;					\
117 									\
118 		struct nlattr obj_nla = {				\
119 			.nla_len = NLA_HDRLEN + (objsz_),		\
120 			.nla_type = (nla_type_),			\
121 		};							\
122 									\
123 		memcpy(pos, (tuntype_), tuntype_len);			\
124 		pos += NLA_ALIGN(tuntype_len);				\
125 		memcpy(pos, &obj_nla, sizeof(obj_nla));			\
126 		pos += sizeof(obj_nla);					\
127 		memcpy(pos, &(obj_), (objsz_));				\
128 									\
129 		if (fallback_func_ == print_quoted_hex) {		\
130 			TEST_NLATTR_EX_((fd_),				\
131 					(nlh0_) - NLA_HDRLEN,		\
132 					hdrlen + NLA_HDRLEN,		\
133 					init_ifinfomsg, print_ifinfomsg, \
134 					IFLA_INFO_KIND, "IFLA_INFO_KIND", \
135 					tuntype_len,			\
136 					objsz_ + (pos - buf) - 1,	\
137 					buf, objsz_ + (pos - buf) - 1,	\
138 					printf("\"%s\"}", (tuntype_));	\
139 					printf(", {{nla_len=%zu"	\
140 					       ", nla_type=%s}, ",	\
141 					       (objsz_) + NLA_HDRLEN,	\
142 					       (nla_type_str_));	\
143 					(fallback_func_)((obj_),	\
144 							 (objsz_) - 1);	\
145 					printf("}"));			\
146 		}							\
147 									\
148 		TEST_NLATTR_EX_((fd_), (nlh0_) - NLA_HDRLEN,		\
149 				hdrlen + NLA_HDRLEN,			\
150 				init_ifinfomsg, print_ifinfomsg,	\
151 				IFLA_INFO_KIND, "IFLA_INFO_KIND",	\
152 				tuntype_len, objsz_ + (pos - buf),	\
153 				buf, objsz_ + (pos - buf) - 1,		\
154 				printf("\"%s\"}", (tuntype_));		\
155 				printf(", {{nla_len=%zu, nla_type=%s}, ", \
156 				       (objsz_) + NLA_HDRLEN,		\
157 				       (nla_type_str_));		\
158 				printf("%p}",				\
159 				       RTA_DATA(NLMSG_ATTR(nlh,		\
160 				       (hdrlen + NLA_HDRLEN + (pos - buf)))) \
161 				       )				\
162 				);					\
163 									\
164 		TEST_NLATTR_EX_((fd_), (nlh0_) - NLA_HDRLEN,		\
165 				hdrlen + NLA_HDRLEN,			\
166 				init_ifinfomsg, print_ifinfomsg,	\
167 				IFLA_INFO_KIND, "IFLA_INFO_KIND",	\
168 				tuntype_len, objsz_ + (pos - buf),	\
169 				buf, objsz_ + (pos - buf),		\
170 				printf("\"%s\"}", (tuntype_));		\
171 				printf(", {{nla_len=%zu, nla_type=%s}, ", \
172 				       (objsz_) + NLA_HDRLEN,		\
173 				       (nla_type_str_));		\
174 									\
175 				{ __VA_ARGS__; }			\
176 									\
177 				printf("}"));				\
178 	} while (0)
179 
180 #define TEST_LINKINFO(fd_, nlh0_, nla_type_, tuntype_,	\
181 		      obj_, pattern_, fallback_func_, ...)	\
182 	TEST_LINKINFO_((fd_), (nlh0_), nla_type_, #nla_type_, (tuntype_), \
183 		       (obj_), sizeof(obj_), pattern_, fallback_func_,	\
184 		       __VA_ARGS__)
185 
186 #define TEST_NESTED_LINKINFO(fd_, nlh0_,				\
187 			     nla_type_, nla_type_str_, tuntype_,	\
188 			     subnla_type_, subnla_type_str_,		\
189 			     obj_, pattern_, ...)			\
190 	do {								\
191 		size_t tuntype_len = strlen(tuntype_) + 1;		\
192 		struct {						\
193 			size_t sz;					\
194 			const char *str;				\
195 		} attrs[] = { __VA_ARGS__ };				\
196 		size_t tunhdrlen;					\
197 		size_t buflen = NLA_ALIGN(tuntype_len) + NLA_HDRLEN;	\
198 		size_t attrsz = 0;					\
199 									\
200 		for (size_t i = 0; i < ARRAY_SIZE(attrs); i++)		\
201 			attrsz += NLA_HDRLEN + NLA_ALIGN(attrs[i].sz);	\
202 									\
203 		buflen += attrsz;					\
204 									\
205 		char *buf = tail_alloc(buflen);				\
206 		char *pos = buf;					\
207 									\
208 		struct nlattr nla = {					\
209 			.nla_len = NLA_HDRLEN + attrsz,			\
210 			.nla_type = (nla_type_),			\
211 		};							\
212 									\
213 		memcpy(pos, (tuntype_), tuntype_len);			\
214 		pos += NLA_ALIGN(tuntype_len);				\
215 		memcpy(pos, &nla, sizeof(nla));				\
216 		pos += sizeof(nla);					\
217 									\
218 		tunhdrlen = pos - buf;					\
219 									\
220 		nla.nla_type = subnla_type_;				\
221 									\
222 		for (size_t i = 0; i < ARRAY_SIZE(attrs); i++) {	\
223 			nla.nla_len = NLA_HDRLEN + attrs[i].sz;		\
224 			memcpy(pos, &nla, sizeof(nla));			\
225 			pos += sizeof(nla);				\
226 									\
227 			memcpy(pos, &(obj_), MIN(sizeof(obj_), attrs[i].sz)); \
228 									\
229 			if (attrs[i].sz > sizeof(obj_))			\
230 				memcpy(pos + sizeof(obj_),		\
231 				       &(pattern_),			\
232 				       attrs[i].sz - sizeof(obj_));	\
233 									\
234 			pos += NLA_ALIGN(attrs[i].sz);			\
235 		}							\
236 									\
237 		TEST_NLATTR_EX_((fd_), (nlh0_) - hdrlen - tunhdrlen,	\
238 				hdrlen + NLA_HDRLEN,			\
239 				init_ifinfomsg, print_ifinfomsg,	\
240 				IFLA_INFO_KIND, "IFLA_INFO_KIND",	\
241 				tuntype_len, buflen,			\
242 				buf, buflen,				\
243 				printf("\"%s\"}", (tuntype_));		\
244 				printf(", {{nla_len=%zu, nla_type=%s}, [", \
245 				       attrsz + NLA_HDRLEN,		\
246 				       (nla_type_str_));		\
247 									\
248 				for (size_t i = 0; i < ARRAY_SIZE(attrs); i++) \
249 					printf("%s%s{nla_len=%zu"	\
250 					       ", nla_type=%s}%s%s%s",	\
251 					       i ? ", " : "",		\
252 					       attrs[i].str ? "{": "",	\
253 					       attrs[i].sz + NLA_HDRLEN, \
254 					       subnla_type_str_,	\
255 					       attrs[i].str ? ", ": "", \
256 					       attrs[i].str ?: "",	\
257 					       attrs[i].str ? "}" : ""); \
258 									\
259 				printf("]}"));				\
260 	} while (0)
261 
262 int
main(void)263 main(void)
264 {
265 	static const uint8_t unknown_msg[] = { 0xab, 0xac, 0xdb, 0xcd };
266 	static const char *unsupported_tunnel_types[] = {
267 		"batadv", "bond",
268 		"caif", "cfhsi",
269 		"dummy",
270 		"erspan",
271 		"geneve", "gre", "gretap", "gtp",
272 		"hsr",
273 		"ifb", "ip6erspan", "ip6gre", "ip6gretap", "ip6tnl",
274 		"ipip", "ipoib", "ipvlan", "ipvtap",
275 		"lowpan",
276 		"macsec", "macvlan", "macvtap",
277 		"netdevsim", "nlmon",
278 		"openvswitch",
279 		"ppp",
280 		"rmnet",
281 		"sit",
282 		"team",
283 		"vcan", "veth", "vlan", "vrf", "vsockmon",
284 		"vti", "vti6", "vxcan", "vxlan",
285 		NULL
286 	};
287 	static const char *unsupported_xstats_types[] = {
288 		"bridge",
289 		"tun",
290 		NULL
291 	};
292 	static const char *unsupported_data_types[] = {
293 		"can",
294 		NULL
295 	};
296 
297 	skip_if_unavailable("/proc/self/fd/");
298 
299 	const int fd = create_nl_socket(NETLINK_ROUTE);
300 
301 	const unsigned int hdrlen = sizeof(struct ifinfomsg);
302 	void *nlh0 = midtail_alloc(NLMSG_SPACE(hdrlen), 2 * NLA_HDRLEN + 256);
303 
304 	static char pattern[4096];
305 	fill_memory_ex(pattern, sizeof(pattern), 'a', 'z' - 'a' + 1);
306 
307 
308 	/* unknown AF_INFO_* type */
309 	TEST_NESTED_NLATTR_OBJECT(fd, nlh0, hdrlen,
310 			   init_ifinfomsg, print_ifinfomsg,
311 			   IFLA_INFO_UNSPEC, pattern, unknown_msg,
312 			   printf("\"\\xab\\xac\\xdb\\xcd\""));
313 
314 	TEST_NESTED_NLATTR_OBJECT_EX_(fd, nlh0, hdrlen,
315 			       init_ifinfomsg, print_ifinfomsg,
316 			       6, "0x6 /* IFLA_INFO_??? */", pattern,
317 			       unknown_msg, print_quoted_hex, 1,
318 			       printf("\"\\xab\\xac\\xdb\\xcd\""));
319 
320 
321 	/* IFLA_INFO_KIND */
322 	TEST_NESTED_NLATTR_OBJECT_EX_(fd, nlh0, hdrlen,
323 				      init_ifinfomsg, print_ifinfomsg,
324 				      IFLA_INFO_KIND, "IFLA_INFO_KIND", pattern,
325 				      unknown_msg, print_quoted_stringn, 1,
326 				      printf("\"\\253\\254\\333\\315\"..."));
327 
328 
329 	/* IFLA_INFO_KIND + IFLA_INFO_UNSPEC */
330 	TEST_UNKNOWN_TUNNELS(fd, nlh0, IFLA_INFO_UNSPEC, "IFLA_INFO_UNSPEC",
331 			     unknown_msg, sizeof(unknown_msg),
332 			     {unsupported_tunnel_types COMMA
333 			      unsupported_xstats_types COMMA
334 			      unsupported_data_types COMMA
335 			      NULL},
336 			     printf("\"\\xab\\xac\\xdb\\xcd\""));
337 
338 
339 	/* IFLA_INFO_KIND + IFLA_INFO_KIND */
340 	TEST_UNKNOWN_TUNNELS(fd, nlh0, IFLA_INFO_KIND, "IFLA_INFO_KIND",
341 			     unknown_msg, sizeof(unknown_msg),
342 			     {unsupported_tunnel_types COMMA
343 			      unsupported_xstats_types COMMA
344 			      unsupported_data_types COMMA
345 			      NULL},
346 			     printf("\"\\253\\254\\333\\315\"..."));
347 
348 
349 	/* IFLA_INFO_KIND + IFLA_INFO_DATA */
350 	TEST_UNKNOWN_TUNNELS(fd, nlh0, IFLA_INFO_DATA, "IFLA_INFO_DATA",
351 			     unknown_msg, sizeof(unknown_msg),
352 			     {unsupported_tunnel_types COMMA
353 			      unsupported_data_types COMMA
354 			      NULL},
355 			     printf("\"\\xab\\xac\\xdb\\xcd\""));
356 
357 	struct val_name {
358 		unsigned int val;
359 		const char *name;
360 	};
361 
362 	static const uint64_t u64_val = 0xdeadc0defacefeedULL;
363 	static const uint32_t u32_val = 0xbadc0dedU;
364 	static const uint16_t u16_val = 0xdeed;
365 	static const uint8_t  u8_val  = 0xa1;
366 
367 	/* bridge attrs */
368 	static const struct val_name und_br_attrs[] = {
369 		{ 0, "IFLA_BR_UNSPEC" },
370 		{ 20, "IFLA_BR_GROUP_ADDR" },
371 		{ 21, "IFLA_BR_FDB_FLUSH" },
372 		{ 40, "IFLA_BR_PAD" },
373 		{ 45, "0x2d /* IFLA_BR_??? */" },
374 	};
375 
376 	for (size_t k = 0; k < ARRAY_SIZE(und_br_attrs); k++) {
377 		TEST_NESTED_LINKINFO(fd, nlh0, 2, "IFLA_INFO_DATA", "bridge",
378 				     und_br_attrs[k].val, und_br_attrs[k].name,
379 				     unknown_msg, pattern,
380 				     { 2, "\"\\xab\\xac\"" },
381 				     { 4, "\"\\xab\\xac\\xdb\\xcd\"" },
382 				     { 6,
383 					"\"\\xab\\xac\\xdb\\xcd\\x61\\x62\"" },
384 				     { 8, "\"\\xab\\xac\\xdb\\xcd\\x61\\x62"
385 					"\\x63\\x64\"" },
386 				     { 10, "\"\\xab\\xac\\xdb\\xcd\\x61\\x62"
387 					"\\x63\\x64\\x65\\x66\"" });
388 	}
389 
390 	static const struct val_name u64_br_attrs[] = {
391 		{ 16, "IFLA_BR_HELLO_TIMER" },
392 		{ 17, "IFLA_BR_TCN_TIMER" },
393 		{ 18, "IFLA_BR_TOPOLOGY_CHANGE_TIMER" },
394 		{ 19, "IFLA_BR_GC_TIMER" },
395 		{ 30, "IFLA_BR_MCAST_LAST_MEMBER_INTVL" },
396 		{ 31, "IFLA_BR_MCAST_MEMBERSHIP_INTVL" },
397 		{ 32, "IFLA_BR_MCAST_QUERIER_INTVL" },
398 		{ 33, "IFLA_BR_MCAST_QUERY_INTVL" },
399 		{ 34, "IFLA_BR_MCAST_QUERY_RESPONSE_INTVL" },
400 		{ 35, "IFLA_BR_MCAST_STARTUP_QUERY_INTVL" },
401 	};
402 
403 	for (size_t k = 0; k < ARRAY_SIZE(u64_br_attrs); k++) {
404 		TEST_NESTED_LINKINFO(fd, nlh0, 2, "IFLA_INFO_DATA", "bridge",
405 				     u64_br_attrs[k].val, u64_br_attrs[k].name,
406 				     u64_val, pattern,
407 				     { 7, "\""
408 #if WORDS_BIGENDIAN
409 					"\\xde\\xad\\xc0\\xde\\xfa\\xce\\xfe"
410 #else
411 					"\\xed\\xfe\\xce\\xfa\\xde\\xc0\\xad"
412 #endif
413 					"\"" },
414 				     { 8, "16045693111314087661" },
415 				     { 9, "16045693111314087661" });
416 	}
417 
418 	static const struct val_name u32_br_attrs[] = {
419 		{  1, "IFLA_BR_FORWARD_DELAY" },
420 		{  2, "IFLA_BR_HELLO_TIME" },
421 		{  3, "IFLA_BR_MAX_AGE" },
422 		{  4, "IFLA_BR_AGEING_TIME" },
423 		{  5, "IFLA_BR_STP_STATE" },
424 		{ 13, "IFLA_BR_ROOT_PATH_COST" },
425 		{ 26, "IFLA_BR_MCAST_HASH_ELASTICITY" },
426 		{ 27, "IFLA_BR_MCAST_HASH_MAX" },
427 		{ 28, "IFLA_BR_MCAST_LAST_MEMBER_CNT" },
428 		{ 29, "IFLA_BR_MCAST_STARTUP_QUERY_CNT" },
429 	};
430 
431 	for (size_t k = 0; k < ARRAY_SIZE(u32_br_attrs); k++) {
432 		TEST_NESTED_LINKINFO(fd, nlh0, 2, "IFLA_INFO_DATA", "bridge",
433 				     u32_br_attrs[k].val, u32_br_attrs[k].name,
434 				     u32_val, pattern,
435 				     { 3, "\""
436 #if WORDS_BIGENDIAN
437 					"\\xba\\xdc\\x0d"
438 #else
439 					"\\xed\\x0d\\xdc"
440 #endif
441 					"\"" },
442 				     { 4, "3134983661" },
443 				     { 5, "3134983661" });
444 	}
445 
446 	static const struct val_name u16_br_attrs[] = {
447 		{  6, "IFLA_BR_PRIORITY" },
448 		{ 12, "IFLA_BR_ROOT_PORT" },
449 		{ 39, "IFLA_BR_VLAN_DEFAULT_PVID" },
450 	};
451 
452 	for (size_t k = 0; k < ARRAY_SIZE(u16_br_attrs); k++) {
453 		TEST_NESTED_LINKINFO(fd, nlh0, 2, "IFLA_INFO_DATA", "bridge",
454 				     u16_br_attrs[k].val, u16_br_attrs[k].name,
455 				     u16_val, pattern,
456 				     { 1, "\""
457 #if WORDS_BIGENDIAN
458 					"\\xde"
459 #else
460 					"\\xed"
461 #endif
462 					"\"" },
463 				     { 2, "57069" },
464 				     { 3, "57069" });
465 	}
466 
467 
468 	static const struct val_name x16_br_attrs[] = {
469 		{  9, "IFLA_BR_GROUP_FWD_MASK" },
470 	};
471 
472 	for (size_t k = 0; k < ARRAY_SIZE(x16_br_attrs); k++) {
473 		TEST_NESTED_LINKINFO(fd, nlh0, 2, "IFLA_INFO_DATA", "bridge",
474 				     x16_br_attrs[k].val, x16_br_attrs[k].name,
475 				     u16_val, pattern,
476 				     { 1, "\""
477 #if WORDS_BIGENDIAN
478 					"\\xde"
479 #else
480 					"\\xed"
481 #endif
482 					"\"" },
483 				     { 2, "0xdeed" },
484 				     { 3, "0xdeed" });
485 	}
486 
487 	static const struct val_name u8_br_attrs[] = {
488 		{  7, "IFLA_BR_VLAN_FILTERING" },
489 		{ 14, "IFLA_BR_TOPOLOGY_CHANGE" },
490 		{ 15, "IFLA_BR_TOPOLOGY_CHANGE_DETECTED" },
491 		{ 22, "IFLA_BR_MCAST_ROUTER" },
492 		{ 23, "IFLA_BR_MCAST_SNOOPING" },
493 		{ 24, "IFLA_BR_MCAST_QUERY_USE_IFADDR" },
494 		{ 25, "IFLA_BR_MCAST_QUERIER" },
495 		{ 36, "IFLA_BR_NF_CALL_IPTABLES" },
496 		{ 37, "IFLA_BR_NF_CALL_IP6TABLES" },
497 		{ 38, "IFLA_BR_NF_CALL_ARPTABLES" },
498 		{ 41, "IFLA_BR_VLAN_STATS_ENABLED" },
499 		{ 42, "IFLA_BR_MCAST_STATS_ENABLED" },
500 		{ 43, "IFLA_BR_MCAST_IGMP_VERSION" },
501 		{ 44, "IFLA_BR_MCAST_MLD_VERSION" },
502 	};
503 
504 	for (size_t k = 0; k < ARRAY_SIZE(u8_br_attrs); k++) {
505 		TEST_NESTED_LINKINFO(fd, nlh0, 2, "IFLA_INFO_DATA", "bridge",
506 				     u8_br_attrs[k].val, u8_br_attrs[k].name,
507 				     u8_val, pattern,
508 				     { 0, NULL },
509 				     { 1, "161" },
510 				     { 2, "161" });
511 	}
512 
513 	unsigned short eth_p = htons(0x88C7);
514 	TEST_NESTED_LINKINFO(fd, nlh0, 2, "IFLA_INFO_DATA", "bridge",
515 			     8, "IFLA_BR_VLAN_PROTOCOL",
516 			     eth_p, pattern,
517 			     { 1, "\"\\x88\"" },
518 			     { 2, "htons(ETH_P_PREAUTH)" },
519 			     { 2, "htons(ETH_P_PREAUTH)" });
520 
521 	static const uint8_t bridge_id[]
522 		= { 0xbe, 0xef, 0xfa, 0xce, 0xde, 0xc0, 0xde, 0xad };
523 	static const struct val_name br_id_attrs[] = {
524 		{ 10, "IFLA_BR_ROOT_ID" },
525 		{ 11, "IFLA_BR_BRIDGE_ID" },
526 	};
527 
528 	for (size_t k = 0; k < ARRAY_SIZE(br_id_attrs); k++) {
529 		TEST_NESTED_LINKINFO(fd, nlh0, 2, "IFLA_INFO_DATA", "bridge",
530 				     br_id_attrs[k].val, br_id_attrs[k].name,
531 				     bridge_id, pattern,
532 				     { 7, "\"\\xbe\\xef\\xfa\\xce"
533 					  "\\xde\\xc0\\xde\"" },
534 				     { 8, "{prio=[190, 239]"
535 					  ", addr=fa:ce:de:c0:de:ad}" },
536 				     { 9, "{prio=[190, 239]"
537 					  ", addr=fa:ce:de:c0:de:ad}" });
538 	}
539 
540 	/* tun attrs */
541 	static const struct val_name u8_tun_attrs[] = {
542 		{ 4, "IFLA_TUN_PI" },
543 		{ 5, "IFLA_TUN_VNET_HDR" },
544 		{ 6, "IFLA_TUN_PERSIST" },
545 		{ 7, "IFLA_TUN_MULTI_QUEUE" },
546 	};
547 
548 	for (size_t k = 0; k < ARRAY_SIZE(u8_tun_attrs); k++) {
549 		TEST_NESTED_LINKINFO(fd, nlh0, 2, "IFLA_INFO_DATA", "tun",
550 				     u8_tun_attrs[k].val, u8_tun_attrs[k].name,
551 				     u8_val, pattern,
552 				     { 0, NULL },
553 				     { 1, "161" },
554 				     { 2, "161" });
555 	}
556 
557 	static const struct val_name u32_tun_attrs[] = {
558 		{ 8, "IFLA_TUN_NUM_QUEUES" },
559 		{ 9, "IFLA_TUN_NUM_DISABLED_QUEUES" },
560 	};
561 
562 	for (size_t k = 0; k < ARRAY_SIZE(u32_tun_attrs); k++) {
563 		TEST_NESTED_LINKINFO(fd, nlh0, 2, "IFLA_INFO_DATA", "tun",
564 				     u32_tun_attrs[k].val,
565 				     u32_tun_attrs[k].name,
566 				     u32_val, pattern,
567 				     { 3, "\""
568 #if WORDS_BIGENDIAN
569 					"\\xba\\xdc\\x0d"
570 #else
571 					"\\xed\\x0d\\xdc"
572 #endif
573 					"\"" },
574 				     { 4, "3134983661" },
575 				     { 5, "3134983661" });
576 	}
577 
578 	static const struct val_name und_tun_attrs[] = {
579 		{ 0,  "IFLA_TUN_UNSPEC" },
580 		{ 10, "0xa /* IFLA_TUN_??? */" },
581 	};
582 
583 	for (size_t k = 0; k < ARRAY_SIZE(und_tun_attrs); k++) {
584 		TEST_NESTED_LINKINFO(fd, nlh0, 2, "IFLA_INFO_DATA", "tun",
585 				     und_tun_attrs[k].val,
586 				     und_tun_attrs[k].name,
587 				     unknown_msg, pattern,
588 				     { 2, "\"\\xab\\xac\"" },
589 				     { 4, "\"\\xab\\xac\\xdb\\xcd\"" },
590 				     { 6,
591 					"\"\\xab\\xac\\xdb\\xcd\\x61\\x62\"" },
592 				     { 8, "\"\\xab\\xac\\xdb\\xcd\\x61\\x62"
593 					"\\x63\\x64\"" },
594 				     { 10, "\"\\xab\\xac\\xdb\\xcd\\x61\\x62"
595 					"\\x63\\x64\\x65\\x66\"" });
596 	}
597 
598 	static const uint32_t minus_one = 0xffffffffU;
599 	static const struct val_name uid_tun_attrs[] = {
600 		{ 1, "IFLA_TUN_OWNER" },
601 		{ 2, "IFLA_TUN_GROUP" },
602 	};
603 
604 	for (size_t k = 0; k < ARRAY_SIZE(uid_tun_attrs); k++) {
605 		TEST_NESTED_LINKINFO(fd, nlh0, 2, "IFLA_INFO_DATA", "tun",
606 				     uid_tun_attrs[k].val,
607 				     uid_tun_attrs[k].name,
608 				     u32_val, pattern,
609 				     { 3, "\""
610 #if WORDS_BIGENDIAN
611 					"\\xba\\xdc\\x0d"
612 #else
613 					"\\xed\\x0d\\xdc"
614 #endif
615 					"\"" },
616 				     { 4, "3134983661" },
617 				     { 5, "3134983661" });
618 
619 		TEST_NESTED_LINKINFO(fd, nlh0, 2, "IFLA_INFO_DATA", "tun",
620 				     uid_tun_attrs[k].val,
621 				     uid_tun_attrs[k].name,
622 				     minus_one, pattern,
623 				     { 3, "\"\\xff\\xff\\xff\"" },
624 				     { 4, "-1" },
625 				     { 5, "-1" });
626 	}
627 
628 	static const struct {
629 		uint8_t val;
630 		const char *str;
631 	} tun_types[] = {
632 		{ 0, "0 /* IFF_??? */"},
633 		{ 1, "IFF_TUN"},
634 		{ 2, "IFF_TAP"},
635 		{ 3, "0x3 /* IFF_??? */"},
636 		{ 0xda, "0xda /* IFF_??? */"},
637 	};
638 
639 	for (size_t k = 0; k < ARRAY_SIZE(tun_types); k++) {
640 		TEST_NESTED_LINKINFO(fd, nlh0, 2, "IFLA_INFO_DATA", "tun",
641 				     3, "IFLA_TUN_TYPE",
642 				     tun_types[k].val, pattern,
643 				     { 0, NULL },
644 				     { 1, tun_types[k].str },
645 				     { 2, tun_types[k].str });
646 	}
647 
648 
649 	/* IFLA_INFO_KIND + IFLA_INFO_XSTATS */
650 	TEST_UNKNOWN_TUNNELS(fd, nlh0, IFLA_INFO_XSTATS, "IFLA_INFO_XSTATS",
651 			     unknown_msg, sizeof(unknown_msg),
652 			     {unsupported_tunnel_types COMMA
653 			     /*
654 			      * can decoder decodes its data only if it's big
655 			      * enough.
656 			      */
657 			      unsupported_xstats_types COMMA
658 			      unsupported_data_types COMMA
659 			      NULL},
660 			     printf("\"\\xab\\xac\\xdb\\xcd\""));
661 
662 	uint32_t can_stats_data[] = {
663 		0xbadc0de0, 0xbadc0de1, 0xbadc0de2, 0xbadc0de3,
664 		0xbadc0de4, 0xbadc0de5,
665 	};
666 
667 	TEST_LINKINFO(fd, nlh0, IFLA_INFO_XSTATS, "can",
668 		      can_stats_data, pattern, print_quoted_hex,
669 		      printf("{bus_error=3134983648"
670 			     ", error_warning=3134983649"
671 			     ", error_passive=3134983650"
672 			     ", bus_off=3134983651"
673 			     ", arbitration_lost=3134983652"
674 			     ", restarts=3134983653}"));
675 
676 
677 	/* IFLA_INFO_KIND + IFLA_INFO_SLVAE_KIND */
678 	TEST_UNKNOWN_TUNNELS(fd, nlh0,
679 			     IFLA_INFO_SLAVE_KIND, "IFLA_INFO_SLAVE_KIND",
680 			     unknown_msg, sizeof(unknown_msg),
681 			     {unsupported_tunnel_types COMMA
682 			      unsupported_xstats_types COMMA
683 			      unsupported_data_types COMMA
684 			      NULL},
685 			     printf("\"\\253\\254\\333\\315\"..."));
686 
687 
688 	/* IFLA_INFO_KIND + IFLA_INFO_SLAVE_DATA */
689 	TEST_UNKNOWN_TUNNELS(fd, nlh0,
690 			     IFLA_INFO_SLAVE_DATA, "IFLA_INFO_SLAVE_DATA",
691 			     unknown_msg, sizeof(unknown_msg),
692 			     {unsupported_tunnel_types COMMA
693 			      unsupported_xstats_types COMMA
694 			      unsupported_data_types COMMA
695 			      NULL},
696 			     printf("\"\\xab\\xac\\xdb\\xcd\""));
697 
698 
699 	/* IFLA_INFO_KIND + unknown type */
700 	TEST_UNKNOWN_TUNNELS(fd, nlh0, 6, "0x6 /* IFLA_INFO_??? */",
701 			     unknown_msg, sizeof(unknown_msg),
702 			     {unsupported_tunnel_types COMMA
703 			      unsupported_xstats_types COMMA
704 			      unsupported_data_types COMMA
705 			      NULL},
706 			     printf("\"\\xab\\xac\\xdb\\xcd\""));
707 
708 
709 	puts("+++ exited with 0 +++");
710 	return 0;
711 }
712