1/*
2 * Check decoding of struct msghdr ancillary data.
3 *
4 * Copyright (c) 2016 Dmitry V. Levin <ldv@altlinux.org>
5 * Copyright (c) 2016-2017 The strace developers.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote products
17 *    derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "tests.h"
32#include <errno.h>
33#include <limits.h>
34#include <stddef.h>
35#include <stdio.h>
36#include <string.h>
37#include <unistd.h>
38#include <sys/socket.h>
39#include <net/if.h>
40#include <netinet/in.h>
41#include <arpa/inet.h>
42
43#include "xlat.h"
44#include "xlat/scmvals.h"
45
46#ifndef SOL_IP
47# define SOL_IP 0
48#endif
49#ifndef SOL_TCP
50# define SOL_TCP 6
51#endif
52
53#ifndef SCM_SECURITY
54# define SCM_SECURITY 3
55#endif
56
57#define MIN_SIZE_OF(type, member) \
58	(offsetof(type, member) + sizeof(((type *) 0)->member))
59
60static struct cmsghdr *
61get_cmsghdr(void *const page, const size_t len)
62{
63	return page - CMSG_ALIGN(len);
64}
65
66static void
67print_fds(const struct cmsghdr *const cmsg, const size_t cmsg_len)
68{
69	size_t nfd = cmsg_len > CMSG_LEN(0)
70		     ? (cmsg_len - CMSG_LEN(0)) / sizeof(int) : 0;
71	if (!nfd)
72		return;
73
74	printf(", cmsg_data=[");
75	int *fdp = (int *) CMSG_DATA(cmsg);
76	size_t i;
77	for (i = 0; i < nfd; ++i) {
78		if (i)
79			printf(", ");
80#if !VERBOSE
81		if (i >= DEFAULT_STRLEN) {
82			printf("...");
83			break;
84		}
85#endif
86		printf("%d", fdp[i]);
87	}
88	printf("]");
89}
90
91static void
92test_scm_rights1(struct msghdr *const mh,
93		 const size_t msg_controllen,
94		 void *const page,
95		 const void *const src,
96		 const size_t cmsg_len)
97{
98	const size_t aligned_cms_len =
99		cmsg_len > CMSG_LEN(0) ? CMSG_ALIGN(cmsg_len) : CMSG_LEN(0);
100	if (cmsg_len >= CMSG_LEN(0)
101	    && aligned_cms_len + CMSG_LEN(0) <= msg_controllen)
102		return;
103
104	struct cmsghdr *cmsg = get_cmsghdr(page, msg_controllen);
105
106	if (msg_controllen >= MIN_SIZE_OF(struct cmsghdr, cmsg_len))
107		cmsg->cmsg_len = cmsg_len;
108	if (msg_controllen >= MIN_SIZE_OF(struct cmsghdr, cmsg_level))
109		cmsg->cmsg_level = SOL_SOCKET;
110	if (msg_controllen >= MIN_SIZE_OF(struct cmsghdr, cmsg_type))
111		cmsg->cmsg_type = SCM_RIGHTS;
112
113	size_t src_len =
114		cmsg_len < msg_controllen ? cmsg_len : msg_controllen;
115	if (src_len > CMSG_LEN(0))
116		memcpy(CMSG_DATA(cmsg), src, src_len - CMSG_LEN(0));
117
118	mh->msg_control = cmsg;
119	mh->msg_controllen = msg_controllen;
120
121	int rc = sendmsg(-1, mh, 0);
122	int saved_errno = errno;
123
124	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
125	       ", msg_iovlen=0");
126	if (msg_controllen < CMSG_LEN(0)) {
127		if (msg_controllen)
128			printf(", msg_control=%p", cmsg);
129	} else {
130		printf(", msg_control=[{cmsg_len=%lu, cmsg_level=SOL_SOCKET"
131		       ", cmsg_type=SCM_RIGHTS", (unsigned long) cmsg_len);
132		print_fds(cmsg, src_len);
133		printf("}");
134		if (aligned_cms_len < msg_controllen)
135			printf(", %p", (void *) cmsg + aligned_cms_len);
136		printf("]");
137	}
138
139	errno = saved_errno;
140	printf(", msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
141	       (unsigned long) msg_controllen, rc, errno2name());
142}
143
144static void
145test_scm_rights2(struct msghdr *const mh,
146		 const size_t msg_controllen,
147		 void *const page,
148		 const int *const *const src,
149		 const size_t *const cmsg_len)
150{
151	const size_t aligned_cms_len[2] = {
152		cmsg_len[0] > CMSG_LEN(0) ? CMSG_ALIGN(cmsg_len[0]) : CMSG_LEN(0),
153		cmsg_len[1] > CMSG_LEN(0) ? CMSG_ALIGN(cmsg_len[1]) : CMSG_LEN(0)
154	};
155	if (cmsg_len[0] < CMSG_LEN(0)
156	    || aligned_cms_len[0] + CMSG_LEN(0) > msg_controllen
157	    || aligned_cms_len[0] + aligned_cms_len[1] + CMSG_LEN(0) <= msg_controllen)
158		return;
159
160	struct cmsghdr *const cmsg[2] = {
161		get_cmsghdr(page, msg_controllen),
162		(void *) get_cmsghdr(page, msg_controllen) + aligned_cms_len[0]
163	};
164	cmsg[0]->cmsg_len = cmsg_len[0];
165	cmsg[0]->cmsg_level = SOL_SOCKET;
166	cmsg[0]->cmsg_type = SCM_RIGHTS;
167	if (cmsg_len[0] > CMSG_LEN(0))
168		memcpy(CMSG_DATA(cmsg[0]), src[0], cmsg_len[0] - CMSG_LEN(0));
169
170	const size_t msg_controllen1 = msg_controllen - aligned_cms_len[0];
171	if (msg_controllen1 >= MIN_SIZE_OF(struct cmsghdr, cmsg_len))
172		cmsg[1]->cmsg_len = cmsg_len[1];
173	if (msg_controllen >= MIN_SIZE_OF(struct cmsghdr, cmsg_level))
174		cmsg[1]->cmsg_level = SOL_SOCKET;
175	if (msg_controllen >= MIN_SIZE_OF(struct cmsghdr, cmsg_type))
176		cmsg[1]->cmsg_type = SCM_RIGHTS;
177	size_t src1_len =
178		cmsg_len[1] < msg_controllen1 ? cmsg_len[1] : msg_controllen1;
179	if (src1_len > CMSG_LEN(0))
180		memcpy(CMSG_DATA(cmsg[1]), src[1], src1_len - CMSG_LEN(0));
181
182	mh->msg_control = cmsg[0];
183	mh->msg_controllen = msg_controllen;
184
185	int rc = sendmsg(-1, mh, 0);
186	int saved_errno = errno;
187
188	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
189	       ", msg_iovlen=0, msg_control=[{cmsg_len=%lu"
190	       ", cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS",
191	       (unsigned long) cmsg_len[0]);
192	print_fds(cmsg[0], cmsg_len[0]);
193	printf("}, {cmsg_len=%lu, cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS",
194	       (unsigned long) cmsg_len[1]);
195	print_fds(cmsg[1], src1_len);
196	printf("}");
197	if (aligned_cms_len[1] < msg_controllen1)
198		printf(", %p", (void *) cmsg[1] + aligned_cms_len[1]);
199	printf("]");
200
201	errno = saved_errno;
202	printf(", msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
203	       (unsigned long) msg_controllen, rc, errno2name());
204}
205
206static void
207test_scm_rights3(struct msghdr *const mh, void *const page, const size_t nfds)
208{
209	const size_t len = CMSG_SPACE(sizeof(int) * nfds);
210	struct cmsghdr *cmsg = get_cmsghdr(page, len);
211
212	cmsg->cmsg_len = CMSG_LEN(sizeof(int) * nfds);
213	cmsg->cmsg_level = SOL_SOCKET;
214	cmsg->cmsg_type = SCM_RIGHTS;
215	int *fdp = (int *) CMSG_DATA(cmsg);
216	size_t i;
217	for (i = 0; i < nfds; ++i)
218		fdp[i] = i;
219
220	mh->msg_control = cmsg;
221	mh->msg_controllen = len;
222
223	int rc = sendmsg(-1, mh, 0);
224	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
225	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
226	       ", cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS",
227	       (unsigned) cmsg->cmsg_len);
228	print_fds(cmsg, cmsg->cmsg_len);
229	printf("}], msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
230	       (unsigned long) len, rc, errno2name());
231}
232
233static void
234test_scm_timestamp(struct msghdr *const mh, void *const page)
235{
236	size_t len = CMSG_SPACE(sizeof(struct timeval));
237	struct cmsghdr *cmsg = get_cmsghdr(page, len);
238
239	cmsg->cmsg_len = CMSG_LEN(sizeof(struct timeval));
240	cmsg->cmsg_level = SOL_SOCKET;
241	cmsg->cmsg_type = SCM_TIMESTAMP;
242	struct timeval *tv = (struct timeval *) CMSG_DATA(cmsg);
243	tv->tv_sec = 123456789;
244	tv->tv_usec = 987654;
245
246	mh->msg_control = cmsg;
247	mh->msg_controllen = len;
248
249	int rc = sendmsg(-1, mh, 0);
250	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
251	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
252	       ", cmsg_level=SOL_SOCKET, cmsg_type=SCM_TIMESTAMP"
253	       ", cmsg_data={tv_sec=%lld, tv_usec=%llu}}]"
254	       ", msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
255	       (unsigned) cmsg->cmsg_len,
256	       (long long) tv->tv_sec, zero_extend_signed_to_ull(tv->tv_usec),
257	       (unsigned long) len, rc, errno2name());
258
259	len = CMSG_SPACE(sizeof(struct timeval) - sizeof(long));
260	cmsg = get_cmsghdr(page, len);
261
262	cmsg->cmsg_len = CMSG_LEN(sizeof(struct timeval) - sizeof(long));
263	cmsg->cmsg_level = SOL_SOCKET;
264	cmsg->cmsg_type = SCM_TIMESTAMP;
265
266	mh->msg_control = cmsg;
267	mh->msg_controllen = len;
268
269	rc = sendmsg(-1, mh, 0);
270	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
271	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
272	       ", cmsg_level=SOL_SOCKET, cmsg_type=SCM_TIMESTAMP, cmsg_data=?}]"
273	       ", msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
274	       (unsigned) cmsg->cmsg_len,
275	       (unsigned long) len, rc, errno2name());
276}
277
278static void
279test_scm_timestampns(struct msghdr *const mh, void *const page)
280{
281	size_t len = CMSG_SPACE(sizeof(struct timespec));
282	struct cmsghdr *cmsg = get_cmsghdr(page, len);
283
284	cmsg->cmsg_len = CMSG_LEN(sizeof(struct timespec));
285	cmsg->cmsg_level = SOL_SOCKET;
286	cmsg->cmsg_type = SCM_TIMESTAMPNS;
287	struct timespec *ts = (struct timespec *) CMSG_DATA(cmsg);
288	ts->tv_sec = 123456789;
289	ts->tv_nsec = 987654321;
290
291	mh->msg_control = cmsg;
292	mh->msg_controllen = len;
293
294	int rc = sendmsg(-1, mh, 0);
295	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
296	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
297	       ", cmsg_level=SOL_SOCKET, cmsg_type=SCM_TIMESTAMPNS"
298	       ", cmsg_data={tv_sec=%lld, tv_nsec=%llu}}]"
299	       ", msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
300	       (unsigned) cmsg->cmsg_len,
301	       (long long) ts->tv_sec, zero_extend_signed_to_ull(ts->tv_nsec),
302	       (unsigned long) len, rc, errno2name());
303
304	len = CMSG_SPACE(sizeof(struct timespec) - sizeof(long));
305	cmsg = get_cmsghdr(page, len);
306
307	cmsg->cmsg_len = CMSG_LEN(sizeof(struct timespec) - sizeof(long));
308	cmsg->cmsg_level = SOL_SOCKET;
309	cmsg->cmsg_type = SCM_TIMESTAMPNS;
310
311	mh->msg_control = cmsg;
312	mh->msg_controllen = len;
313
314	rc = sendmsg(-1, mh, 0);
315	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
316	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
317	       ", cmsg_level=SOL_SOCKET, cmsg_type=SCM_TIMESTAMPNS"
318	       ", cmsg_data=?}]"
319	       ", msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
320	       (unsigned) cmsg->cmsg_len,
321	       (unsigned long) len, rc, errno2name());
322}
323
324static void
325test_scm_timestamping(struct msghdr *const mh, void *const page)
326{
327	size_t len = CMSG_SPACE(3 * sizeof(struct timespec));
328	struct cmsghdr *cmsg = get_cmsghdr(page, len);
329
330	cmsg->cmsg_len = CMSG_LEN(3 * sizeof(struct timespec));
331	cmsg->cmsg_level = SOL_SOCKET;
332	cmsg->cmsg_type = SCM_TIMESTAMPING;
333	struct timespec *ts = (struct timespec *) CMSG_DATA(cmsg);
334	ts[0].tv_sec = 123456789;
335	ts[0].tv_nsec = 987654321;
336	ts[1].tv_sec = 123456790;
337	ts[1].tv_nsec = 987654320;
338	ts[2].tv_sec = 123456791;
339	ts[2].tv_nsec = 987654319;
340
341	mh->msg_control = cmsg;
342	mh->msg_controllen = len;
343
344	int rc = sendmsg(-1, mh, 0);
345	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
346	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
347	       ", cmsg_level=SOL_SOCKET, cmsg_type=SCM_TIMESTAMPING"
348	       ", cmsg_data=[{tv_sec=%lld, tv_nsec=%llu}"
349	       ", {tv_sec=%lld, tv_nsec=%llu}, {tv_sec=%lld, tv_nsec=%llu}]}]"
350	       ", msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
351	       (unsigned) cmsg->cmsg_len, (long long) ts[0].tv_sec,
352	       zero_extend_signed_to_ull(ts[0].tv_nsec),
353	       (long long) ts[1].tv_sec,
354	       zero_extend_signed_to_ull(ts[1].tv_nsec),
355	       (long long) ts[2].tv_sec,
356	       zero_extend_signed_to_ull(ts[2].tv_nsec),
357	       (unsigned long) len, rc, errno2name());
358
359	len = CMSG_SPACE(3 * sizeof(struct timespec) - sizeof(long));
360	cmsg = get_cmsghdr(page, len);
361
362	cmsg->cmsg_len = CMSG_LEN(3 * sizeof(struct timespec) - sizeof(long));
363	cmsg->cmsg_level = SOL_SOCKET;
364	cmsg->cmsg_type = SCM_TIMESTAMPING;
365
366	mh->msg_control = cmsg;
367	mh->msg_controllen = len;
368
369	rc = sendmsg(-1, mh, 0);
370	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
371	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
372	       ", cmsg_level=SOL_SOCKET, cmsg_type=SCM_TIMESTAMPING"
373	       ", cmsg_data=?}]"
374	       ", msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
375	       (unsigned) cmsg->cmsg_len,
376	       (unsigned long) len, rc, errno2name());
377}
378
379static void
380print_security(const struct cmsghdr *const cmsg, const size_t cmsg_len)
381{
382	int n = cmsg_len > CMSG_LEN(0) ? cmsg_len - CMSG_LEN(0) : 0;
383	if (!n)
384		return;
385
386	printf(", cmsg_data=\"%.*s\"", n, CMSG_DATA(cmsg));
387}
388
389static void
390test_scm_security(struct msghdr *const mh,
391		  const size_t msg_controllen,
392		  void *const page,
393		  const void *const src,
394		  const size_t cmsg_len,
395		  const int cmsg_level,
396		  const char *const cmsg_level_str)
397{
398	const size_t aligned_cms_len =
399		cmsg_len > CMSG_LEN(0) ? CMSG_ALIGN(cmsg_len) : CMSG_LEN(0);
400	if (cmsg_len >= CMSG_LEN(0)
401	    && aligned_cms_len + CMSG_LEN(0) <= msg_controllen)
402		return;
403
404	struct cmsghdr *cmsg = get_cmsghdr(page, msg_controllen);
405
406	cmsg->cmsg_len = cmsg_len;
407	cmsg->cmsg_level = cmsg_level;
408	cmsg->cmsg_type = SCM_SECURITY;
409
410	size_t src_len =
411		cmsg_len < msg_controllen ? cmsg_len : msg_controllen;
412	if (src_len > CMSG_LEN(0))
413		memcpy(CMSG_DATA(cmsg), src, src_len - CMSG_LEN(0));
414
415	mh->msg_control = cmsg;
416	mh->msg_controllen = msg_controllen;
417
418	int rc = sendmsg(-1, mh, 0);
419	int saved_errno = errno;
420
421	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
422	       ", msg_iovlen=0, msg_control=[{cmsg_len=%lu, cmsg_level=%s"
423	       ", cmsg_type=SCM_SECURITY",
424	       (unsigned long) cmsg_len, cmsg_level_str);
425	print_security(cmsg, src_len);
426	printf("}");
427	if (aligned_cms_len < msg_controllen)
428		printf(", %p", (void *) cmsg + aligned_cms_len);
429	printf("]");
430
431	errno = saved_errno;
432	printf(", msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
433	       (unsigned long) msg_controllen, rc, errno2name());
434}
435
436static void
437test_unknown_type(struct msghdr *const mh,
438		  void *const page,
439		  const int cmsg_level,
440		  const char *const cmsg_level_str,
441		  const char *const cmsg_type_str)
442{
443	struct cmsghdr *cmsg = get_cmsghdr(page, CMSG_LEN(0));
444
445	cmsg->cmsg_len = CMSG_LEN(0);
446	cmsg->cmsg_level = cmsg_level;
447	cmsg->cmsg_type = 0xfacefeed;
448
449	mh->msg_control = cmsg;
450	mh->msg_controllen = cmsg->cmsg_len;
451
452	int rc = sendmsg(-1, mh, 0);
453	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
454	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u, cmsg_level=%s"
455	       ", cmsg_type=%#x /* %s */}], msg_controllen=%u, msg_flags=0}"
456	       ", 0) = %d %s (%m)\n",
457	       (unsigned) cmsg->cmsg_len, cmsg_level_str, cmsg->cmsg_type,
458	       cmsg_type_str, (unsigned) mh->msg_controllen, rc, errno2name());
459}
460
461static void
462test_sol_socket(struct msghdr *const mh, void *const page)
463{
464	static const int fds0[] = { -10, -11, -12, -13 };
465	static const int fds1[] = { -15, -16, -17, -18 };
466	size_t msg_controllen, max_msg_controllen;
467
468	max_msg_controllen = CMSG_SPACE(sizeof(fds0)) + sizeof(*fds0) - 1;
469	for (msg_controllen = 0;
470	     msg_controllen <= max_msg_controllen;
471	     msg_controllen++) {
472		size_t cmsg_len;
473
474		for (cmsg_len = 0;
475		     cmsg_len <= msg_controllen + CMSG_LEN(0);
476		     cmsg_len++) {
477			test_scm_rights1(mh, msg_controllen,
478					 page, fds0, cmsg_len);
479		}
480	}
481
482	max_msg_controllen =
483		CMSG_SPACE(sizeof(fds0)) + CMSG_SPACE(sizeof(fds1)) +
484		sizeof(*fds0) - 1;
485	for (msg_controllen = CMSG_LEN(0) * 2;
486	     msg_controllen <= max_msg_controllen;
487	     msg_controllen++) {
488		static const int *const fdps[] = { fds0, fds1 };
489		size_t cmsg_len[2];
490
491		for (cmsg_len[0] = CMSG_LEN(0);
492		     CMSG_ALIGN(cmsg_len[0]) + CMSG_LEN(0) <= msg_controllen
493		     && CMSG_ALIGN(cmsg_len[0]) <= CMSG_SPACE(sizeof(fds0));
494		     cmsg_len[0]++) {
495			const size_t msg_controllen1 =
496				msg_controllen - CMSG_ALIGN(cmsg_len[0]);
497
498			for (cmsg_len[1] = 0;
499			     cmsg_len[1] <= msg_controllen1 + CMSG_LEN(0);
500			     cmsg_len[1]++) {
501				test_scm_rights2(mh, msg_controllen,
502						 page, fdps, cmsg_len);
503			}
504		}
505	}
506
507	static const char text[16] = "0123456789abcdef";
508	max_msg_controllen = CMSG_SPACE(sizeof(text)) + CMSG_LEN(0) - 1;
509	for (msg_controllen = CMSG_LEN(0);
510	     msg_controllen <= max_msg_controllen;
511	     msg_controllen++) {
512		size_t cmsg_len;
513
514		for (cmsg_len = 0;
515		     cmsg_len <= msg_controllen + CMSG_LEN(0)
516		     && cmsg_len <= CMSG_LEN(sizeof(text));
517		     cmsg_len++) {
518			test_scm_security(mh, msg_controllen,
519					  page, text, cmsg_len,
520					  ARG_STR(SOL_SOCKET));
521		}
522	}
523
524	test_scm_rights3(mh, page, DEFAULT_STRLEN - 1);
525	test_scm_rights3(mh, page, DEFAULT_STRLEN);
526	test_scm_rights3(mh, page, DEFAULT_STRLEN + 1);
527
528	test_scm_timestamp(mh, page);
529	test_scm_timestampns(mh, page);
530	test_scm_timestamping(mh, page);
531
532	test_unknown_type(mh, page, ARG_STR(SOL_SOCKET), "SCM_???");
533}
534
535static void
536test_ip_pktinfo(struct msghdr *const mh, void *const page,
537		const int cmsg_type, const char *const cmsg_type_str)
538{
539	const unsigned int len = CMSG_SPACE(sizeof(struct in_pktinfo));
540	struct cmsghdr *const cmsg = get_cmsghdr(page, len);
541
542	cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
543	cmsg->cmsg_level = SOL_IP;
544	cmsg->cmsg_type = cmsg_type;
545
546	struct in_pktinfo *const info = (struct in_pktinfo *) CMSG_DATA(cmsg);
547	info->ipi_ifindex = ifindex_lo();
548	info->ipi_spec_dst.s_addr = inet_addr("1.2.3.4");
549	info->ipi_addr.s_addr = inet_addr("5.6.7.8");
550
551	mh->msg_control = cmsg;
552	mh->msg_controllen = len;
553
554	int rc = sendmsg(-1, mh, 0);
555	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
556	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u, cmsg_level=SOL_IP"
557	       ", cmsg_type=%s, cmsg_data={ipi_ifindex=%s"
558	       ", ipi_spec_dst=inet_addr(\"%s\")"
559	       ", ipi_addr=inet_addr(\"%s\")}}]"
560	       ", msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
561	       (unsigned) cmsg->cmsg_len, cmsg_type_str,
562	       IFINDEX_LO_STR, "1.2.3.4", "5.6.7.8", len, rc, errno2name());
563}
564
565static void
566test_ip_uint(struct msghdr *const mh, void *const page,
567	     const int cmsg_type, const char *const cmsg_type_str)
568{
569	const unsigned int len = CMSG_SPACE(sizeof(int));
570	struct cmsghdr *const cmsg = get_cmsghdr(page, len);
571
572	cmsg->cmsg_len = CMSG_LEN(sizeof(int));
573	cmsg->cmsg_level = SOL_IP;
574	cmsg->cmsg_type = cmsg_type;
575
576	unsigned int *u = (void *) CMSG_DATA(cmsg);
577	*u = 0xfacefeed;
578
579	mh->msg_control = cmsg;
580	mh->msg_controllen = len;
581
582	int rc = sendmsg(-1, mh, 0);
583	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
584	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
585	       ", cmsg_level=SOL_IP, cmsg_type=%s, cmsg_data=[%u]}]"
586	       ", msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
587	       (unsigned) cmsg->cmsg_len, cmsg_type_str, *u, len,
588	       rc, errno2name());
589}
590
591static void
592test_ip_uint8_t(struct msghdr *const mh, void *const page,
593		const int cmsg_type, const char *const cmsg_type_str)
594{
595	const unsigned int len = CMSG_SPACE(1);
596	struct cmsghdr *const cmsg = get_cmsghdr(page, len);
597
598	cmsg->cmsg_len = CMSG_LEN(1);
599	cmsg->cmsg_level = SOL_IP;
600	cmsg->cmsg_type = cmsg_type;
601	*CMSG_DATA(cmsg) = 'A';
602
603	mh->msg_control = cmsg;
604	mh->msg_controllen = len;
605
606	int rc = sendmsg(-1, mh, 0);
607	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
608	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
609	       ", cmsg_level=SOL_IP, cmsg_type=%s, cmsg_data=[%#x]}]"
610	       ", msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
611	       (unsigned) cmsg->cmsg_len, cmsg_type_str,
612	       (unsigned) (uint8_t) 'A', len, rc, errno2name());
613}
614
615static void
616print_ip_opts(const void *const cmsg_data, const unsigned int data_len)
617{
618	const unsigned char *const opts = cmsg_data;
619	unsigned int i;
620	for (i = 0; i < data_len; ++i) {
621		if (i)
622			printf(", ");
623#if !VERBOSE
624		if (i >= DEFAULT_STRLEN) {
625			printf("...");
626			break;
627		}
628#endif
629		printf("0x%02x", opts[i]);
630	}
631}
632
633static void
634test_ip_opts(struct msghdr *const mh, void *const page,
635	     const int cmsg_type, const char *const cmsg_type_str,
636	     const unsigned int opts_len)
637{
638	unsigned int len = CMSG_SPACE(opts_len);
639	struct cmsghdr *cmsg = get_cmsghdr(page, len);
640
641	cmsg->cmsg_len = CMSG_LEN(opts_len);
642	cmsg->cmsg_level = SOL_IP;
643	cmsg->cmsg_type = cmsg_type;
644	unsigned int i;
645	for (i = 0; i < opts_len; ++i)
646		CMSG_DATA(cmsg)[i] = 'A' + i;
647
648	mh->msg_control = cmsg;
649	mh->msg_controllen = len;
650
651	int rc = sendmsg(-1, mh, 0);
652	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
653	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
654	       ", cmsg_level=SOL_IP, cmsg_type=%s, cmsg_data=[",
655	       (unsigned) cmsg->cmsg_len, cmsg_type_str);
656	print_ip_opts(CMSG_DATA(cmsg), opts_len);
657	printf("]}], msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
658	       len, rc, errno2name());
659}
660
661#ifdef IP_CHECKSUM
662struct sock_ee {
663	uint32_t ee_errno;
664	uint8_t  ee_origin;
665	uint8_t  ee_type;
666	uint8_t  ee_code;
667	uint8_t  ee_pad;
668	uint32_t ee_info;
669	uint32_t ee_data;
670	struct sockaddr_in offender;
671};
672
673static void
674test_ip_recverr(struct msghdr *const mh, void *const page,
675		const int cmsg_type, const char *const cmsg_type_str)
676{
677	const unsigned int len = CMSG_SPACE(sizeof(struct sock_ee));
678	struct cmsghdr *const cmsg = get_cmsghdr(page, len);
679
680	cmsg->cmsg_len = CMSG_LEN(sizeof(struct sock_ee));
681	cmsg->cmsg_level = SOL_IP;
682	cmsg->cmsg_type = cmsg_type;
683
684	struct sock_ee *const e = (struct sock_ee *) CMSG_DATA(cmsg);
685	e->ee_errno = 0xdeadbeef;
686	e->ee_origin = 2;
687	e->ee_type = 3;
688	e->ee_code = 4;
689	e->ee_info = 0xfacefeed;
690	e->ee_data = 0xbadc0ded;
691	e->offender.sin_family = AF_INET,
692	e->offender.sin_port = htons(12345),
693	e->offender.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
694
695	mh->msg_control = cmsg;
696	mh->msg_controllen = len;
697
698	int rc = sendmsg(-1, mh, 0);
699	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
700	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u, cmsg_level=SOL_IP"
701	       ", cmsg_type=%s, cmsg_data={ee_errno=%u, ee_origin=%u"
702	       ", ee_type=%u, ee_code=%u, ee_info=%u, ee_data=%u"
703	       ", offender={sa_family=AF_INET, sin_port=htons(%hu)"
704	       ", sin_addr=inet_addr(\"127.0.0.1\")}}}]"
705	       ", msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
706	       (unsigned) cmsg->cmsg_len, cmsg_type_str,
707	       e->ee_errno, e->ee_origin, e->ee_type,
708	       e->ee_code, e->ee_info, e->ee_data,
709	       ntohs(e->offender.sin_port),
710	       len, rc, errno2name());
711}
712#endif
713
714#ifdef IP_ORIGDSTADDR
715static void
716test_ip_origdstaddr(struct msghdr *const mh, void *const page,
717		    const int cmsg_type, const char *const cmsg_type_str)
718{
719	const unsigned int len = CMSG_SPACE(sizeof(struct sockaddr_in));
720	struct cmsghdr *const cmsg = get_cmsghdr(page, len);
721
722	cmsg->cmsg_len = CMSG_LEN(sizeof(struct sockaddr_in));
723	cmsg->cmsg_level = SOL_IP;
724	cmsg->cmsg_type = cmsg_type;
725
726	struct sockaddr_in *const sin = (struct sockaddr_in *) CMSG_DATA(cmsg);
727	sin->sin_family = AF_INET,
728	sin->sin_port = htons(12345),
729	sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
730
731	mh->msg_control = cmsg;
732	mh->msg_controllen = len;
733
734	int rc = sendmsg(-1, mh, 0);
735	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
736	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u, cmsg_level=SOL_IP"
737	       ", cmsg_type=%s, cmsg_data={sa_family=AF_INET"
738	       ", sin_port=htons(%hu), sin_addr=inet_addr(\"127.0.0.1\")}}]"
739	       ", msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
740	       (unsigned) cmsg->cmsg_len, cmsg_type_str,
741	       ntohs(sin->sin_port), len, rc, errno2name());
742}
743#endif
744
745static void
746test_sol_ip(struct msghdr *const mh, void *const page)
747{
748	test_ip_pktinfo(mh, page, ARG_STR(IP_PKTINFO));
749	test_ip_uint(mh, page, ARG_STR(IP_TTL));
750	test_ip_uint8_t(mh, page, ARG_STR(IP_TOS));
751	test_ip_opts(mh, page, ARG_STR(IP_RECVOPTS), 1);
752	test_ip_opts(mh, page, ARG_STR(IP_RECVOPTS), 2);
753	test_ip_opts(mh, page, ARG_STR(IP_RECVOPTS), 3);
754	test_ip_opts(mh, page, ARG_STR(IP_RECVOPTS), 4);
755	test_ip_opts(mh, page, ARG_STR(IP_RETOPTS), 5);
756	test_ip_opts(mh, page, ARG_STR(IP_RETOPTS), 6);
757	test_ip_opts(mh, page, ARG_STR(IP_RETOPTS), 7);
758	test_ip_opts(mh, page, ARG_STR(IP_RETOPTS), 8);
759	test_ip_opts(mh, page, ARG_STR(IP_RETOPTS), DEFAULT_STRLEN - 1);
760	test_ip_opts(mh, page, ARG_STR(IP_RETOPTS), DEFAULT_STRLEN);
761	test_ip_opts(mh, page, ARG_STR(IP_RETOPTS), DEFAULT_STRLEN + 1);
762#ifdef IP_CHECKSUM
763	test_ip_recverr(mh, page, ARG_STR(IP_RECVERR));
764#endif
765#ifdef IP_ORIGDSTADDR
766	test_ip_origdstaddr(mh, page, ARG_STR(IP_ORIGDSTADDR));
767#endif
768#ifdef IP_CHECKSUM
769	test_ip_uint(mh, page, ARG_STR(IP_CHECKSUM));
770#endif
771	test_scm_security(mh, CMSG_LEN(0), page, 0, CMSG_LEN(0),
772			  ARG_STR(SOL_IP));
773	test_unknown_type(mh, page, ARG_STR(SOL_IP), "IP_???");
774}
775
776static void
777test_unknown_level(struct msghdr *const mh, void *const page)
778{
779	struct cmsghdr *cmsg = get_cmsghdr(page, CMSG_LEN(0));
780
781	cmsg->cmsg_len = CMSG_LEN(0);
782	cmsg->cmsg_level = SOL_TCP;
783	cmsg->cmsg_type = 0xdeadbeef;
784
785	mh->msg_control = cmsg;
786	mh->msg_controllen = cmsg->cmsg_len;
787
788	int rc = sendmsg(-1, mh, 0);
789	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
790	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u, cmsg_level=%s"
791	       ", cmsg_type=%#x}], msg_controllen=%u, msg_flags=0}"
792	       ", 0) = %d %s (%m)\n",
793	       (unsigned) cmsg->cmsg_len, "SOL_TCP", cmsg->cmsg_type,
794	       (unsigned) mh->msg_controllen, rc, errno2name());
795}
796
797static void
798test_big_len(struct msghdr *const mh)
799{
800	int optmem_max;
801
802	if (read_int_from_file("/proc/sys/net/core/optmem_max", &optmem_max)
803	    || optmem_max <= 0 || optmem_max > 0x100000)
804		optmem_max = sizeof(long long) * (2 * IOV_MAX + 512);
805	optmem_max = (optmem_max + sizeof(long long) - 1)
806		     & ~(sizeof(long long) - 1);
807
808	const size_t len = optmem_max * 2;
809	struct cmsghdr *const cmsg = tail_alloc(len);
810	cmsg->cmsg_len = len;
811	cmsg->cmsg_level = SOL_SOCKET;
812	cmsg->cmsg_type = SCM_RIGHTS;
813
814	mh->msg_control = cmsg;
815	mh->msg_controllen = len;
816
817	int rc = sendmsg(-1, mh, 0);
818	if (EBADF != errno)
819		perror_msg_and_skip("sendmsg");
820
821	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
822	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
823	       ", cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS",
824	       (unsigned) cmsg->cmsg_len);
825	print_fds(cmsg, optmem_max);
826	printf("}, ...], msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
827	       (unsigned long) len, rc, errno2name());
828}
829
830int main(int ac, const char **av)
831{
832	int rc = sendmsg(-1, 0, 0);
833	printf("sendmsg(-1, NULL, 0) = %d %s (%m)\n", rc, errno2name());
834
835	TAIL_ALLOC_OBJECT_CONST_PTR(struct msghdr, mh);
836	memset(mh, 0, sizeof(*mh));
837	test_big_len(mh);
838
839	rc = sendmsg(-1, mh + 1, 0);
840	printf("sendmsg(-1, %p, 0) = %d %s (%m)\n",
841	       mh + 1, rc, errno2name());
842
843	void *page = tail_alloc(1) + 1;
844	mh->msg_control = page;
845	mh->msg_controllen = CMSG_LEN(0);
846	rc = sendmsg(-1, mh, 0);
847	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
848	       ", msg_iovlen=0, msg_control=%p, msg_controllen=%u"
849	       ", msg_flags=0}, 0) = %d %s (%m)\n",
850	       page, (unsigned) CMSG_LEN(0), rc, errno2name());
851
852	test_sol_socket(mh, page);
853	test_sol_ip(mh, page);
854	test_unknown_level(mh, page);
855
856	puts("+++ exited with 0 +++");
857	return 0;
858}
859