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#ifdef HAVE_IF_INDEXTONAME
548	info->ipi_ifindex = if_nametoindex("lo");
549#else
550	info->ipi_ifindex = 1;
551#endif
552	info->ipi_spec_dst.s_addr = inet_addr("1.2.3.4");
553	info->ipi_addr.s_addr = inet_addr("5.6.7.8");
554
555	mh->msg_control = cmsg;
556	mh->msg_controllen = len;
557
558	int rc = sendmsg(-1, mh, 0);
559	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
560	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u, cmsg_level=SOL_IP"
561	       ", cmsg_type=%s, cmsg_data={ipi_ifindex=%s"
562	       ", ipi_spec_dst=inet_addr(\"%s\")"
563	       ", ipi_addr=inet_addr(\"%s\")}}]"
564	       ", msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
565	       (unsigned) cmsg->cmsg_len, cmsg_type_str,
566#ifdef HAVE_IF_INDEXTONAME
567	       "if_nametoindex(\"lo\")",
568#else
569	       "1",
570#endif
571	       "1.2.3.4", "5.6.7.8", len, rc, errno2name());
572}
573
574static void
575test_ip_uint(struct msghdr *const mh, void *const page,
576	     const int cmsg_type, const char *const cmsg_type_str)
577{
578	const unsigned int len = CMSG_SPACE(sizeof(int));
579	struct cmsghdr *const cmsg = get_cmsghdr(page, len);
580
581	cmsg->cmsg_len = CMSG_LEN(sizeof(int));
582	cmsg->cmsg_level = SOL_IP;
583	cmsg->cmsg_type = cmsg_type;
584
585	unsigned int *u = (void *) CMSG_DATA(cmsg);
586	*u = 0xfacefeed;
587
588	mh->msg_control = cmsg;
589	mh->msg_controllen = len;
590
591	int rc = sendmsg(-1, mh, 0);
592	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
593	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
594	       ", cmsg_level=SOL_IP, cmsg_type=%s, cmsg_data=[%u]}]"
595	       ", msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
596	       (unsigned) cmsg->cmsg_len, cmsg_type_str, *u, len,
597	       rc, errno2name());
598}
599
600static void
601test_ip_uint8_t(struct msghdr *const mh, void *const page,
602		const int cmsg_type, const char *const cmsg_type_str)
603{
604	const unsigned int len = CMSG_SPACE(1);
605	struct cmsghdr *const cmsg = get_cmsghdr(page, len);
606
607	cmsg->cmsg_len = CMSG_LEN(1);
608	cmsg->cmsg_level = SOL_IP;
609	cmsg->cmsg_type = cmsg_type;
610	*CMSG_DATA(cmsg) = 'A';
611
612	mh->msg_control = cmsg;
613	mh->msg_controllen = len;
614
615	int rc = sendmsg(-1, mh, 0);
616	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
617	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
618	       ", cmsg_level=SOL_IP, cmsg_type=%s, cmsg_data=[%#x]}]"
619	       ", msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
620	       (unsigned) cmsg->cmsg_len, cmsg_type_str,
621	       (unsigned) (uint8_t) 'A', len, rc, errno2name());
622}
623
624static void
625print_ip_opts(const void *const cmsg_data, const unsigned int data_len)
626{
627	const unsigned char *const opts = cmsg_data;
628	unsigned int i;
629	for (i = 0; i < data_len; ++i) {
630		if (i)
631			printf(", ");
632#if !VERBOSE
633		if (i >= DEFAULT_STRLEN) {
634			printf("...");
635			break;
636		}
637#endif
638		printf("0x%02x", opts[i]);
639	}
640}
641
642static void
643test_ip_opts(struct msghdr *const mh, void *const page,
644	     const int cmsg_type, const char *const cmsg_type_str,
645	     const unsigned int opts_len)
646{
647	unsigned int len = CMSG_SPACE(opts_len);
648	struct cmsghdr *cmsg = get_cmsghdr(page, len);
649
650	cmsg->cmsg_len = CMSG_LEN(opts_len);
651	cmsg->cmsg_level = SOL_IP;
652	cmsg->cmsg_type = cmsg_type;
653	unsigned int i;
654	for (i = 0; i < opts_len; ++i)
655		CMSG_DATA(cmsg)[i] = 'A' + i;
656
657	mh->msg_control = cmsg;
658	mh->msg_controllen = len;
659
660	int rc = sendmsg(-1, mh, 0);
661	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
662	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
663	       ", cmsg_level=SOL_IP, cmsg_type=%s, cmsg_data=[",
664	       (unsigned) cmsg->cmsg_len, cmsg_type_str);
665	print_ip_opts(CMSG_DATA(cmsg), opts_len);
666	printf("]}], msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
667	       len, rc, errno2name());
668}
669
670#ifdef IP_CHECKSUM
671struct sock_ee {
672	uint32_t ee_errno;
673	uint8_t  ee_origin;
674	uint8_t  ee_type;
675	uint8_t  ee_code;
676	uint8_t  ee_pad;
677	uint32_t ee_info;
678	uint32_t ee_data;
679	struct sockaddr_in offender;
680};
681
682static void
683test_ip_recverr(struct msghdr *const mh, void *const page,
684		const int cmsg_type, const char *const cmsg_type_str)
685{
686	const unsigned int len = CMSG_SPACE(sizeof(struct sock_ee));
687	struct cmsghdr *const cmsg = get_cmsghdr(page, len);
688
689	cmsg->cmsg_len = CMSG_LEN(sizeof(struct sock_ee));
690	cmsg->cmsg_level = SOL_IP;
691	cmsg->cmsg_type = cmsg_type;
692
693	struct sock_ee *const e = (struct sock_ee *) CMSG_DATA(cmsg);
694	e->ee_errno = 0xdeadbeef;
695	e->ee_origin = 2;
696	e->ee_type = 3;
697	e->ee_code = 4;
698	e->ee_info = 0xfacefeed;
699	e->ee_data = 0xbadc0ded;
700	e->offender.sin_family = AF_INET,
701	e->offender.sin_port = htons(12345),
702	e->offender.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
703
704	mh->msg_control = cmsg;
705	mh->msg_controllen = len;
706
707	int rc = sendmsg(-1, mh, 0);
708	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
709	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u, cmsg_level=SOL_IP"
710	       ", cmsg_type=%s, cmsg_data={ee_errno=%u, ee_origin=%u"
711	       ", ee_type=%u, ee_code=%u, ee_info=%u, ee_data=%u"
712	       ", offender={sa_family=AF_INET, sin_port=htons(%hu)"
713	       ", sin_addr=inet_addr(\"127.0.0.1\")}}}]"
714	       ", msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
715	       (unsigned) cmsg->cmsg_len, cmsg_type_str,
716	       e->ee_errno, e->ee_origin, e->ee_type,
717	       e->ee_code, e->ee_info, e->ee_data,
718	       ntohs(e->offender.sin_port),
719	       len, rc, errno2name());
720}
721#endif
722
723#ifdef IP_ORIGDSTADDR
724static void
725test_ip_origdstaddr(struct msghdr *const mh, void *const page,
726		    const int cmsg_type, const char *const cmsg_type_str)
727{
728	const unsigned int len = CMSG_SPACE(sizeof(struct sockaddr_in));
729	struct cmsghdr *const cmsg = get_cmsghdr(page, len);
730
731	cmsg->cmsg_len = CMSG_LEN(sizeof(struct sockaddr_in));
732	cmsg->cmsg_level = SOL_IP;
733	cmsg->cmsg_type = cmsg_type;
734
735	struct sockaddr_in *const sin = (struct sockaddr_in *) CMSG_DATA(cmsg);
736	sin->sin_family = AF_INET,
737	sin->sin_port = htons(12345),
738	sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
739
740	mh->msg_control = cmsg;
741	mh->msg_controllen = len;
742
743	int rc = sendmsg(-1, mh, 0);
744	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
745	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u, cmsg_level=SOL_IP"
746	       ", cmsg_type=%s, cmsg_data={sa_family=AF_INET"
747	       ", sin_port=htons(%hu), sin_addr=inet_addr(\"127.0.0.1\")}}]"
748	       ", msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
749	       (unsigned) cmsg->cmsg_len, cmsg_type_str,
750	       ntohs(sin->sin_port), len, rc, errno2name());
751}
752#endif
753
754static void
755test_sol_ip(struct msghdr *const mh, void *const page)
756{
757	test_ip_pktinfo(mh, page, ARG_STR(IP_PKTINFO));
758	test_ip_uint(mh, page, ARG_STR(IP_TTL));
759	test_ip_uint8_t(mh, page, ARG_STR(IP_TOS));
760	test_ip_opts(mh, page, ARG_STR(IP_RECVOPTS), 1);
761	test_ip_opts(mh, page, ARG_STR(IP_RECVOPTS), 2);
762	test_ip_opts(mh, page, ARG_STR(IP_RECVOPTS), 3);
763	test_ip_opts(mh, page, ARG_STR(IP_RECVOPTS), 4);
764	test_ip_opts(mh, page, ARG_STR(IP_RETOPTS), 5);
765	test_ip_opts(mh, page, ARG_STR(IP_RETOPTS), 6);
766	test_ip_opts(mh, page, ARG_STR(IP_RETOPTS), 7);
767	test_ip_opts(mh, page, ARG_STR(IP_RETOPTS), 8);
768	test_ip_opts(mh, page, ARG_STR(IP_RETOPTS), DEFAULT_STRLEN - 1);
769	test_ip_opts(mh, page, ARG_STR(IP_RETOPTS), DEFAULT_STRLEN);
770	test_ip_opts(mh, page, ARG_STR(IP_RETOPTS), DEFAULT_STRLEN + 1);
771#ifdef IP_CHECKSUM
772	test_ip_recverr(mh, page, ARG_STR(IP_RECVERR));
773#endif
774#ifdef IP_ORIGDSTADDR
775	test_ip_origdstaddr(mh, page, ARG_STR(IP_ORIGDSTADDR));
776#endif
777#ifdef IP_CHECKSUM
778	test_ip_uint(mh, page, ARG_STR(IP_CHECKSUM));
779#endif
780	test_scm_security(mh, CMSG_LEN(0), page, 0, CMSG_LEN(0),
781			  ARG_STR(SOL_IP));
782	test_unknown_type(mh, page, ARG_STR(SOL_IP), "IP_???");
783}
784
785static void
786test_unknown_level(struct msghdr *const mh, void *const page)
787{
788	struct cmsghdr *cmsg = get_cmsghdr(page, CMSG_LEN(0));
789
790	cmsg->cmsg_len = CMSG_LEN(0);
791	cmsg->cmsg_level = SOL_TCP;
792	cmsg->cmsg_type = 0xdeadbeef;
793
794	mh->msg_control = cmsg;
795	mh->msg_controllen = cmsg->cmsg_len;
796
797	int rc = sendmsg(-1, mh, 0);
798	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
799	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u, cmsg_level=%s"
800	       ", cmsg_type=%#x}], msg_controllen=%u, msg_flags=0}"
801	       ", 0) = %d %s (%m)\n",
802	       (unsigned) cmsg->cmsg_len, "SOL_TCP", cmsg->cmsg_type,
803	       (unsigned) mh->msg_controllen, rc, errno2name());
804}
805
806static void
807test_big_len(struct msghdr *const mh)
808{
809	int optmem_max;
810
811	if (read_int_from_file("/proc/sys/net/core/optmem_max", &optmem_max)
812	    || optmem_max <= 0 || optmem_max > 0x100000)
813		optmem_max = sizeof(long long) * (2 * IOV_MAX + 512);
814	optmem_max = (optmem_max + sizeof(long long) - 1)
815		     & ~(sizeof(long long) - 1);
816
817	const size_t len = optmem_max * 2;
818	struct cmsghdr *const cmsg = tail_alloc(len);
819	cmsg->cmsg_len = len;
820	cmsg->cmsg_level = SOL_SOCKET;
821	cmsg->cmsg_type = SCM_RIGHTS;
822
823	mh->msg_control = cmsg;
824	mh->msg_controllen = len;
825
826	int rc = sendmsg(-1, mh, 0);
827	if (EBADF != errno)
828		perror_msg_and_skip("sendmsg");
829
830	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
831	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
832	       ", cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS",
833	       (unsigned) cmsg->cmsg_len);
834	print_fds(cmsg, optmem_max);
835	printf("}, ...], msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
836	       (unsigned long) len, rc, errno2name());
837}
838
839int main(int ac, const char **av)
840{
841	int rc = sendmsg(-1, 0, 0);
842	printf("sendmsg(-1, NULL, 0) = %d %s (%m)\n", rc, errno2name());
843
844	TAIL_ALLOC_OBJECT_CONST_PTR(struct msghdr, mh);
845	memset(mh, 0, sizeof(*mh));
846	test_big_len(mh);
847
848	rc = sendmsg(-1, mh + 1, 0);
849	printf("sendmsg(-1, %p, 0) = %d %s (%m)\n",
850	       mh + 1, rc, errno2name());
851
852	void *page = tail_alloc(1) + 1;
853	mh->msg_control = page;
854	mh->msg_controllen = CMSG_LEN(0);
855	rc = sendmsg(-1, mh, 0);
856	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
857	       ", msg_iovlen=0, msg_control=%p, msg_controllen=%u"
858	       ", msg_flags=0}, 0) = %d %s (%m)\n",
859	       page, (unsigned) CMSG_LEN(0), rc, errno2name());
860
861	test_sol_socket(mh, page);
862	test_sol_ip(mh, page);
863	test_unknown_level(mh, page);
864
865	puts("+++ exited with 0 +++");
866	return 0;
867}
868