(root)/
strace-6.5/
tests/
msg_control.c
       1  /*
       2   * Check decoding of struct msghdr ancillary data.
       3   *
       4   * Copyright (c) 2016 Dmitry V. Levin <ldv@strace.io>
       5   * Copyright (c) 2016-2023 The strace developers.
       6   * All rights reserved.
       7   *
       8   * SPDX-License-Identifier: GPL-2.0-or-later
       9   */
      10  
      11  #include "tests.h"
      12  #include <errno.h>
      13  #include <limits.h>
      14  #include <stddef.h>
      15  #include <stdio.h>
      16  #include <string.h>
      17  #include <unistd.h>
      18  #include <sys/socket.h>
      19  #include <net/if.h>
      20  #include <netinet/in.h>
      21  #include <arpa/inet.h>
      22  
      23  #include "kernel_time_types.h"
      24  #include "kernel_timeval.h"
      25  #include "kernel_old_timespec.h"
      26  
      27  #include "xlat.h"
      28  #define XLAT_MACROS_ONLY
      29  #include "xlat/ip_cmsg_types.h"
      30  #include "xlat/sock_options.h"
      31  #include "xlat/scmvals.h"
      32  #undef XLAT_MACROS_ONLY
      33  
      34  #ifndef SOL_IP
      35  # define SOL_IP 0
      36  #endif
      37  #ifndef SOL_TCP
      38  # define SOL_TCP 6
      39  #endif
      40  
      41  static struct cmsghdr *
      42  get_cmsghdr(void *const page, const size_t len)
      43  {
      44  	return page - CMSG_ALIGN(len);
      45  }
      46  
      47  static void
      48  print_fds(const struct cmsghdr *const cmsg, const size_t cmsg_len)
      49  {
      50  	size_t nfd = cmsg_len > CMSG_LEN(0)
      51  		     ? (cmsg_len - CMSG_LEN(0)) / sizeof(int) : 0;
      52  	if (!nfd)
      53  		return;
      54  
      55  	printf(", cmsg_data=[");
      56  	int *fdp = (int *) CMSG_DATA(cmsg);
      57  	for (size_t i = 0; i < nfd; ++i) {
      58  		if (i)
      59  			printf(", ");
      60  #if !VERBOSE
      61  		if (i >= DEFAULT_STRLEN) {
      62  			printf("...");
      63  			break;
      64  		}
      65  #endif
      66  		printf("%d", fdp[i]);
      67  	}
      68  	printf("]");
      69  }
      70  
      71  static void
      72  test_scm_rights1(struct msghdr *const mh,
      73  		 const size_t msg_controllen,
      74  		 void *const page,
      75  		 const void *const src,
      76  		 const size_t cmsg_len)
      77  {
      78  	const size_t aligned_cms_len =
      79  		cmsg_len > CMSG_LEN(0) ? CMSG_ALIGN(cmsg_len) : CMSG_LEN(0);
      80  	if (cmsg_len >= CMSG_LEN(0)
      81  	    && aligned_cms_len + CMSG_LEN(0) <= msg_controllen)
      82  		return;
      83  
      84  	struct cmsghdr *cmsg = get_cmsghdr(page, msg_controllen);
      85  
      86  	if (msg_controllen >= offsetofend(struct cmsghdr, cmsg_len))
      87  		cmsg->cmsg_len = cmsg_len;
      88  	if (msg_controllen >= offsetofend(struct cmsghdr, cmsg_level))
      89  		cmsg->cmsg_level = SOL_SOCKET;
      90  	if (msg_controllen >= offsetofend(struct cmsghdr, cmsg_type))
      91  		cmsg->cmsg_type = SCM_RIGHTS;
      92  
      93  	size_t src_len =
      94  		cmsg_len < msg_controllen ? cmsg_len : msg_controllen;
      95  	if (src_len > CMSG_LEN(0))
      96  		memcpy(CMSG_DATA(cmsg), src, src_len - CMSG_LEN(0));
      97  
      98  	mh->msg_control = cmsg;
      99  	mh->msg_controllen = msg_controllen;
     100  
     101  	int rc = sendmsg(-1, mh, 0);
     102  	int saved_errno = errno;
     103  
     104  	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
     105  	       ", msg_iovlen=0");
     106  	if (msg_controllen < CMSG_LEN(0)) {
     107  		if (msg_controllen)
     108  			printf(", msg_control=%p", cmsg);
     109  	} else {
     110  		printf(", msg_control=[{cmsg_len=%lu, cmsg_level=SOL_SOCKET"
     111  		       ", cmsg_type=SCM_RIGHTS", (unsigned long) cmsg_len);
     112  		print_fds(cmsg, src_len);
     113  		printf("}");
     114  		if (aligned_cms_len < msg_controllen)
     115  			printf(", ... /* %p */", (void *) cmsg + aligned_cms_len);
     116  		printf("]");
     117  	}
     118  
     119  	errno = saved_errno;
     120  	printf(", msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
     121  	       (unsigned long) msg_controllen, rc, errno2name());
     122  }
     123  
     124  static void
     125  test_scm_rights2(struct msghdr *const mh,
     126  		 const size_t msg_controllen,
     127  		 void *const page,
     128  		 const int *const *const src,
     129  		 const size_t *const cmsg_len)
     130  {
     131  	const size_t aligned_cms_len[2] = {
     132  		cmsg_len[0] > CMSG_LEN(0) ? CMSG_ALIGN(cmsg_len[0]) : CMSG_LEN(0),
     133  		cmsg_len[1] > CMSG_LEN(0) ? CMSG_ALIGN(cmsg_len[1]) : CMSG_LEN(0)
     134  	};
     135  	if (cmsg_len[0] < CMSG_LEN(0)
     136  	    || aligned_cms_len[0] + CMSG_LEN(0) > msg_controllen
     137  	    || aligned_cms_len[0] + aligned_cms_len[1] + CMSG_LEN(0) <= msg_controllen)
     138  		return;
     139  
     140  	struct cmsghdr *const cmsg[2] = {
     141  		get_cmsghdr(page, msg_controllen),
     142  		(void *) get_cmsghdr(page, msg_controllen) + aligned_cms_len[0]
     143  	};
     144  	cmsg[0]->cmsg_len = cmsg_len[0];
     145  	cmsg[0]->cmsg_level = SOL_SOCKET;
     146  	cmsg[0]->cmsg_type = SCM_RIGHTS;
     147  	if (cmsg_len[0] > CMSG_LEN(0))
     148  		memcpy(CMSG_DATA(cmsg[0]), src[0], cmsg_len[0] - CMSG_LEN(0));
     149  
     150  	const size_t msg_controllen1 = msg_controllen - aligned_cms_len[0];
     151  	if (msg_controllen1 >= offsetofend(struct cmsghdr, cmsg_len))
     152  		cmsg[1]->cmsg_len = cmsg_len[1];
     153  	if (msg_controllen >= offsetofend(struct cmsghdr, cmsg_level))
     154  		cmsg[1]->cmsg_level = SOL_SOCKET;
     155  	if (msg_controllen >= offsetofend(struct cmsghdr, cmsg_type))
     156  		cmsg[1]->cmsg_type = SCM_RIGHTS;
     157  	size_t src1_len =
     158  		cmsg_len[1] < msg_controllen1 ? cmsg_len[1] : msg_controllen1;
     159  	if (src1_len > CMSG_LEN(0))
     160  		memcpy(CMSG_DATA(cmsg[1]), src[1], src1_len - CMSG_LEN(0));
     161  
     162  	mh->msg_control = cmsg[0];
     163  	mh->msg_controllen = msg_controllen;
     164  
     165  	int rc = sendmsg(-1, mh, 0);
     166  	int saved_errno = errno;
     167  
     168  	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
     169  	       ", msg_iovlen=0, msg_control=[{cmsg_len=%lu"
     170  	       ", cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS",
     171  	       (unsigned long) cmsg_len[0]);
     172  	print_fds(cmsg[0], cmsg_len[0]);
     173  	printf("}, {cmsg_len=%lu, cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS",
     174  	       (unsigned long) cmsg_len[1]);
     175  	print_fds(cmsg[1], src1_len);
     176  	printf("}");
     177  	if (aligned_cms_len[1] < msg_controllen1)
     178  		printf(", ... /* %p */", (void *) cmsg[1] + aligned_cms_len[1]);
     179  	printf("]");
     180  
     181  	errno = saved_errno;
     182  	printf(", msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
     183  	       (unsigned long) msg_controllen, rc, errno2name());
     184  }
     185  
     186  static void
     187  test_scm_rights3(struct msghdr *const mh, void *const page, const size_t nfds)
     188  {
     189  	const size_t len = CMSG_SPACE(sizeof(int) * nfds);
     190  	struct cmsghdr *cmsg = get_cmsghdr(page, len);
     191  
     192  	cmsg->cmsg_len = CMSG_LEN(sizeof(int) * nfds);
     193  	cmsg->cmsg_level = SOL_SOCKET;
     194  	cmsg->cmsg_type = SCM_RIGHTS;
     195  	int *fdp = (int *) CMSG_DATA(cmsg);
     196  	for (size_t i = 0; i < nfds; ++i)
     197  		fdp[i] = i;
     198  
     199  	mh->msg_control = cmsg;
     200  	mh->msg_controllen = len;
     201  
     202  	int rc = sendmsg(-1, mh, 0);
     203  	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
     204  	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
     205  	       ", cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS",
     206  	       (unsigned) cmsg->cmsg_len);
     207  	print_fds(cmsg, cmsg->cmsg_len);
     208  	printf("}], msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
     209  	       (unsigned long) len, rc, errno2name());
     210  }
     211  
     212  static void
     213  test_scm_timestamp_old(struct msghdr *const mh, void *const page)
     214  {
     215  	static const kernel_old_timeval_t tv = {
     216  		.tv_sec = 123456789,
     217  		.tv_usec = 987654
     218  	};
     219  	size_t len = CMSG_SPACE(sizeof(tv));
     220  	struct cmsghdr *cmsg = get_cmsghdr(page, len);
     221  
     222  	cmsg->cmsg_len = CMSG_LEN(sizeof(tv));
     223  	cmsg->cmsg_level = SOL_SOCKET;
     224  	cmsg->cmsg_type = SO_TIMESTAMP_OLD;
     225  	memcpy(CMSG_DATA(cmsg), &tv, sizeof(tv));
     226  
     227  	mh->msg_control = cmsg;
     228  	mh->msg_controllen = len;
     229  
     230  	int rc = sendmsg(-1, mh, 0);
     231  	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
     232  	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
     233  	       ", cmsg_level=SOL_SOCKET, cmsg_type=SO_TIMESTAMP_OLD"
     234  	       ", cmsg_data={tv_sec=%lld, tv_usec=%llu}}]"
     235  	       ", msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
     236  	       (unsigned) cmsg->cmsg_len,
     237  	       (long long) tv.tv_sec, zero_extend_signed_to_ull(tv.tv_usec),
     238  	       (unsigned long) len, rc, errno2name());
     239  
     240  	len = CMSG_SPACE(sizeof(tv) - sizeof(long));
     241  	cmsg = get_cmsghdr(page, len);
     242  
     243  	cmsg->cmsg_len = CMSG_LEN(sizeof(tv) - sizeof(long));
     244  	cmsg->cmsg_level = SOL_SOCKET;
     245  	cmsg->cmsg_type = SO_TIMESTAMP_OLD;
     246  
     247  	mh->msg_control = cmsg;
     248  	mh->msg_controllen = len;
     249  
     250  	rc = sendmsg(-1, mh, 0);
     251  	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
     252  	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
     253  	       ", cmsg_level=SOL_SOCKET, cmsg_type=SO_TIMESTAMP_OLD, cmsg_data=???}]"
     254  	       ", msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
     255  	       (unsigned) cmsg->cmsg_len,
     256  	       (unsigned long) len, rc, errno2name());
     257  }
     258  
     259  static void
     260  test_scm_timestampns_old(struct msghdr *const mh, void *const page)
     261  {
     262  	static const kernel_old_timespec_t ts = {
     263  		.tv_sec = 123456789,
     264  		.tv_nsec = 987654321
     265  	};
     266  	size_t len = CMSG_SPACE(sizeof(ts));
     267  	struct cmsghdr *cmsg = get_cmsghdr(page, len);
     268  
     269  	cmsg->cmsg_len = CMSG_LEN(sizeof(ts));
     270  	cmsg->cmsg_level = SOL_SOCKET;
     271  	cmsg->cmsg_type = SO_TIMESTAMPNS_OLD;
     272  	memcpy(CMSG_DATA(cmsg), &ts, sizeof(ts));
     273  
     274  	mh->msg_control = cmsg;
     275  	mh->msg_controllen = len;
     276  
     277  	int rc = sendmsg(-1, mh, 0);
     278  	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
     279  	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
     280  	       ", cmsg_level=SOL_SOCKET, cmsg_type=SO_TIMESTAMPNS_OLD"
     281  	       ", cmsg_data={tv_sec=%lld, tv_nsec=%llu}}]"
     282  	       ", msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
     283  	       (unsigned) cmsg->cmsg_len,
     284  	       (long long) ts.tv_sec, zero_extend_signed_to_ull(ts.tv_nsec),
     285  	       (unsigned long) len, rc, errno2name());
     286  
     287  	len = CMSG_SPACE(sizeof(ts) - sizeof(long));
     288  	cmsg = get_cmsghdr(page, len);
     289  
     290  	cmsg->cmsg_len = CMSG_LEN(sizeof(ts) - sizeof(long));
     291  	cmsg->cmsg_level = SOL_SOCKET;
     292  	cmsg->cmsg_type = SO_TIMESTAMPNS_OLD;
     293  
     294  	mh->msg_control = cmsg;
     295  	mh->msg_controllen = len;
     296  
     297  	rc = sendmsg(-1, mh, 0);
     298  	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
     299  	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
     300  	       ", cmsg_level=SOL_SOCKET, cmsg_type=SO_TIMESTAMPNS_OLD"
     301  	       ", cmsg_data=???}]"
     302  	       ", msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
     303  	       (unsigned) cmsg->cmsg_len,
     304  	       (unsigned long) len, rc, errno2name());
     305  }
     306  
     307  static void
     308  test_scm_timestamping_old(struct msghdr *const mh, void *const page)
     309  {
     310  	static const kernel_old_timespec_t ts[] = {
     311  		{ .tv_sec = 123456789, .tv_nsec = 987654321 },
     312  		{ .tv_sec = 123456790, .tv_nsec = 987654320 },
     313  		{ .tv_sec = 123456791, .tv_nsec = 987654319 }
     314  	};
     315  	size_t len = CMSG_SPACE(sizeof(ts));
     316  	struct cmsghdr *cmsg = get_cmsghdr(page, len);
     317  
     318  	cmsg->cmsg_len = CMSG_LEN(sizeof(ts));
     319  	cmsg->cmsg_level = SOL_SOCKET;
     320  	cmsg->cmsg_type = SO_TIMESTAMPING_OLD;
     321  	memcpy(CMSG_DATA(cmsg), ts, sizeof(ts));
     322  
     323  	mh->msg_control = cmsg;
     324  	mh->msg_controllen = len;
     325  
     326  	int rc = sendmsg(-1, mh, 0);
     327  	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
     328  	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
     329  	       ", cmsg_level=SOL_SOCKET, cmsg_type=SO_TIMESTAMPING_OLD"
     330  	       ", cmsg_data=[{tv_sec=%lld, tv_nsec=%llu}"
     331  	       ", {tv_sec=%lld, tv_nsec=%llu}, {tv_sec=%lld, tv_nsec=%llu}]}]"
     332  	       ", msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
     333  	       (unsigned) cmsg->cmsg_len, (long long) ts[0].tv_sec,
     334  	       zero_extend_signed_to_ull(ts[0].tv_nsec),
     335  	       (long long) ts[1].tv_sec,
     336  	       zero_extend_signed_to_ull(ts[1].tv_nsec),
     337  	       (long long) ts[2].tv_sec,
     338  	       zero_extend_signed_to_ull(ts[2].tv_nsec),
     339  	       (unsigned long) len, rc, errno2name());
     340  
     341  	len = CMSG_SPACE(sizeof(ts) - sizeof(long));
     342  	cmsg = get_cmsghdr(page, len);
     343  
     344  	cmsg->cmsg_len = CMSG_LEN(sizeof(ts) - sizeof(long));
     345  	cmsg->cmsg_level = SOL_SOCKET;
     346  	cmsg->cmsg_type = SO_TIMESTAMPING_OLD;
     347  
     348  	mh->msg_control = cmsg;
     349  	mh->msg_controllen = len;
     350  
     351  	rc = sendmsg(-1, mh, 0);
     352  	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
     353  	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
     354  	       ", cmsg_level=SOL_SOCKET, cmsg_type=SO_TIMESTAMPING_OLD"
     355  	       ", cmsg_data=???}]"
     356  	       ", msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
     357  	       (unsigned) cmsg->cmsg_len,
     358  	       (unsigned long) len, rc, errno2name());
     359  }
     360  
     361  static void
     362  test_scm_timestamp_new(struct msghdr *const mh, void *const page)
     363  {
     364  	static const struct __kernel_sock_timeval tv = {
     365  		.tv_sec = 0xdefaceddeadbeef,
     366  		.tv_usec = 0xdec0dedcafef00d
     367  	};
     368  	size_t len = CMSG_SPACE(sizeof(tv));
     369  	struct cmsghdr *cmsg = get_cmsghdr(page, len);
     370  
     371  	cmsg->cmsg_len = CMSG_LEN(sizeof(tv));
     372  	cmsg->cmsg_level = SOL_SOCKET;
     373  	cmsg->cmsg_type = SO_TIMESTAMP_NEW;
     374  	memcpy(CMSG_DATA(cmsg), &tv, sizeof(tv));
     375  
     376  	mh->msg_control = cmsg;
     377  	mh->msg_controllen = len;
     378  
     379  	int rc = sendmsg(-1, mh, 0);
     380  	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
     381  	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
     382  	       ", cmsg_level=SOL_SOCKET, cmsg_type=SO_TIMESTAMP_NEW"
     383  	       ", cmsg_data={tv_sec=%lld, tv_usec=%llu}}]"
     384  	       ", msg_controllen=%lu, msg_flags=0}, 0) = %s\n",
     385  	       (unsigned) cmsg->cmsg_len,
     386  	       (long long) tv.tv_sec, zero_extend_signed_to_ull(tv.tv_usec),
     387  	       (unsigned long) len, sprintrc(rc));
     388  
     389  	len = CMSG_SPACE(sizeof(tv) - sizeof(long));
     390  	cmsg = get_cmsghdr(page, len);
     391  
     392  	cmsg->cmsg_len = CMSG_LEN(sizeof(tv) - sizeof(long));
     393  	cmsg->cmsg_level = SOL_SOCKET;
     394  	cmsg->cmsg_type = SO_TIMESTAMP_NEW;
     395  
     396  	mh->msg_control = cmsg;
     397  	mh->msg_controllen = len;
     398  
     399  	rc = sendmsg(-1, mh, 0);
     400  	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
     401  	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
     402  	       ", cmsg_level=SOL_SOCKET, cmsg_type=SO_TIMESTAMP_NEW, cmsg_data=???}]"
     403  	       ", msg_controllen=%lu, msg_flags=0}, 0) = %s\n",
     404  	       (unsigned) cmsg->cmsg_len,
     405  	       (unsigned long) len, sprintrc(rc));
     406  }
     407  
     408  static void
     409  test_scm_timestampns_new(struct msghdr *const mh, void *const page)
     410  {
     411  	static const struct __kernel_timespec ts = {
     412  		.tv_sec = 0xdefaceddeadbeef,
     413  		.tv_nsec = 0xdec0dedcafef00d
     414  	};
     415  	size_t len = CMSG_SPACE(sizeof(ts));
     416  	struct cmsghdr *cmsg = get_cmsghdr(page, len);
     417  
     418  	cmsg->cmsg_len = CMSG_LEN(sizeof(ts));
     419  	cmsg->cmsg_level = SOL_SOCKET;
     420  	cmsg->cmsg_type = SO_TIMESTAMPNS_NEW;
     421  	memcpy(CMSG_DATA(cmsg), &ts, sizeof(ts));
     422  
     423  	mh->msg_control = cmsg;
     424  	mh->msg_controllen = len;
     425  
     426  	int rc = sendmsg(-1, mh, 0);
     427  	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
     428  	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
     429  	       ", cmsg_level=SOL_SOCKET, cmsg_type=SO_TIMESTAMPNS_NEW"
     430  	       ", cmsg_data={tv_sec=%lld, tv_nsec=%llu}}]"
     431  	       ", msg_controllen=%lu, msg_flags=0}, 0) = %s\n",
     432  	       (unsigned) cmsg->cmsg_len,
     433  	       (long long) ts.tv_sec, zero_extend_signed_to_ull(ts.tv_nsec),
     434  	       (unsigned long) len, sprintrc(rc));
     435  
     436  	len = CMSG_SPACE(sizeof(ts) - sizeof(long));
     437  	cmsg = get_cmsghdr(page, len);
     438  
     439  	cmsg->cmsg_len = CMSG_LEN(sizeof(ts) - sizeof(long));
     440  	cmsg->cmsg_level = SOL_SOCKET;
     441  	cmsg->cmsg_type = SO_TIMESTAMPNS_NEW;
     442  
     443  	mh->msg_control = cmsg;
     444  	mh->msg_controllen = len;
     445  
     446  	rc = sendmsg(-1, mh, 0);
     447  	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
     448  	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
     449  	       ", cmsg_level=SOL_SOCKET, cmsg_type=SO_TIMESTAMPNS_NEW"
     450  	       ", cmsg_data=???}]"
     451  	       ", msg_controllen=%lu, msg_flags=0}, 0) = %s\n",
     452  	       (unsigned) cmsg->cmsg_len,
     453  	       (unsigned long) len, sprintrc(rc));
     454  }
     455  
     456  static void
     457  test_scm_timestamping_new(struct msghdr *const mh, void *const page)
     458  {
     459  	static const struct __kernel_timespec ts[] = {
     460  		{ .tv_sec = 0xdeface0deadbef1, .tv_nsec = 0xdec0de2cafef0d3 },
     461  		{ .tv_sec = 0xdeface4deadbef5, .tv_nsec = 0xdec0de6cafef0d7 },
     462  		{ .tv_sec = 0xdeface8deadbef9, .tv_nsec = 0xdec0dedcafef00d }
     463  	};
     464  	size_t len = CMSG_SPACE(sizeof(ts));
     465  	struct cmsghdr *cmsg = get_cmsghdr(page, len);
     466  
     467  	cmsg->cmsg_len = CMSG_LEN(sizeof(ts));
     468  	cmsg->cmsg_level = SOL_SOCKET;
     469  	cmsg->cmsg_type = SO_TIMESTAMPING_NEW;
     470  	memcpy(CMSG_DATA(cmsg), ts, sizeof(ts));
     471  
     472  	mh->msg_control = cmsg;
     473  	mh->msg_controllen = len;
     474  
     475  	int rc = sendmsg(-1, mh, 0);
     476  	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
     477  	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
     478  	       ", cmsg_level=SOL_SOCKET, cmsg_type=SO_TIMESTAMPING_NEW"
     479  	       ", cmsg_data=[{tv_sec=%lld, tv_nsec=%llu}"
     480  	       ", {tv_sec=%lld, tv_nsec=%llu}, {tv_sec=%lld, tv_nsec=%llu}]}]"
     481  	       ", msg_controllen=%lu, msg_flags=0}, 0) = %s\n",
     482  	       (unsigned) cmsg->cmsg_len, (long long) ts[0].tv_sec,
     483  	       zero_extend_signed_to_ull(ts[0].tv_nsec),
     484  	       (long long) ts[1].tv_sec,
     485  	       zero_extend_signed_to_ull(ts[1].tv_nsec),
     486  	       (long long) ts[2].tv_sec,
     487  	       zero_extend_signed_to_ull(ts[2].tv_nsec),
     488  	       (unsigned long) len, sprintrc(rc));
     489  
     490  	len = CMSG_SPACE(sizeof(ts) - sizeof(long));
     491  	cmsg = get_cmsghdr(page, len);
     492  
     493  	cmsg->cmsg_len = CMSG_LEN(sizeof(ts) - sizeof(long));
     494  	cmsg->cmsg_level = SOL_SOCKET;
     495  	cmsg->cmsg_type = SO_TIMESTAMPING_NEW;
     496  
     497  	mh->msg_control = cmsg;
     498  	mh->msg_controllen = len;
     499  
     500  	rc = sendmsg(-1, mh, 0);
     501  	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
     502  	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
     503  	       ", cmsg_level=SOL_SOCKET, cmsg_type=SO_TIMESTAMPING_NEW"
     504  	       ", cmsg_data=???}]"
     505  	       ", msg_controllen=%lu, msg_flags=0}, 0) = %s\n",
     506  	       (unsigned) cmsg->cmsg_len,
     507  	       (unsigned long) len, sprintrc(rc));
     508  }
     509  
     510  static void
     511  print_security(const struct cmsghdr *const cmsg, const size_t cmsg_len)
     512  {
     513  	int n = cmsg_len > CMSG_LEN(0) ? cmsg_len - CMSG_LEN(0) : 0;
     514  	if (!n)
     515  		return;
     516  
     517  	printf(", cmsg_data=\"%.*s\"", n, CMSG_DATA(cmsg));
     518  }
     519  
     520  static void
     521  test_scm_security(struct msghdr *const mh,
     522  		  const size_t msg_controllen,
     523  		  void *const page,
     524  		  const void *const src,
     525  		  const size_t cmsg_len,
     526  		  const int cmsg_level,
     527  		  const char *const cmsg_level_str)
     528  {
     529  	const size_t aligned_cms_len =
     530  		cmsg_len > CMSG_LEN(0) ? CMSG_ALIGN(cmsg_len) : CMSG_LEN(0);
     531  	if (cmsg_len >= CMSG_LEN(0)
     532  	    && aligned_cms_len + CMSG_LEN(0) <= msg_controllen)
     533  		return;
     534  
     535  	struct cmsghdr *cmsg = get_cmsghdr(page, msg_controllen);
     536  
     537  	cmsg->cmsg_len = cmsg_len;
     538  	cmsg->cmsg_level = cmsg_level;
     539  	cmsg->cmsg_type = SCM_SECURITY;
     540  
     541  	size_t src_len =
     542  		cmsg_len < msg_controllen ? cmsg_len : msg_controllen;
     543  	if (src_len > CMSG_LEN(0))
     544  		memcpy(CMSG_DATA(cmsg), src, src_len - CMSG_LEN(0));
     545  
     546  	mh->msg_control = cmsg;
     547  	mh->msg_controllen = msg_controllen;
     548  
     549  	int rc = sendmsg(-1, mh, 0);
     550  	int saved_errno = errno;
     551  
     552  	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
     553  	       ", msg_iovlen=0, msg_control=[{cmsg_len=%lu, cmsg_level=%s"
     554  	       ", cmsg_type=SCM_SECURITY",
     555  	       (unsigned long) cmsg_len, cmsg_level_str);
     556  	print_security(cmsg, src_len);
     557  	printf("}");
     558  	if (aligned_cms_len < msg_controllen)
     559  		printf(", ... /* %p */", (void *) cmsg + aligned_cms_len);
     560  	printf("]");
     561  
     562  	errno = saved_errno;
     563  	printf(", msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
     564  	       (unsigned long) msg_controllen, rc, errno2name());
     565  }
     566  
     567  static void
     568  test_unknown_type(struct msghdr *const mh,
     569  		  void *const page,
     570  		  const int cmsg_level,
     571  		  const char *const cmsg_level_str,
     572  		  const char *const cmsg_type_str)
     573  {
     574  	struct cmsghdr *cmsg = get_cmsghdr(page, CMSG_LEN(0));
     575  
     576  	cmsg->cmsg_len = CMSG_LEN(0);
     577  	cmsg->cmsg_level = cmsg_level;
     578  	cmsg->cmsg_type = 0xfacefeed;
     579  
     580  	mh->msg_control = cmsg;
     581  	mh->msg_controllen = cmsg->cmsg_len;
     582  
     583  	int rc = sendmsg(-1, mh, 0);
     584  	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
     585  	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u, cmsg_level=%s"
     586  	       ", cmsg_type=%#x /* %s */}], msg_controllen=%u, msg_flags=0}"
     587  	       ", 0) = %d %s (%m)\n",
     588  	       (unsigned) cmsg->cmsg_len, cmsg_level_str, cmsg->cmsg_type,
     589  	       cmsg_type_str, (unsigned) mh->msg_controllen, rc, errno2name());
     590  }
     591  
     592  static void
     593  test_sol_socket(struct msghdr *const mh, void *const page)
     594  {
     595  	static const int fds0[] = { -10, -11, -12, -13 };
     596  	static const int fds1[] = { -15, -16, -17, -18 };
     597  	size_t max_msg_controllen;
     598  
     599  	max_msg_controllen = CMSG_SPACE(sizeof(fds0)) + sizeof(*fds0) - 1;
     600  	for (size_t msg_controllen = 0;
     601  	     msg_controllen <= max_msg_controllen;
     602  	     msg_controllen++) {
     603  		for (size_t cmsg_len = 0;
     604  		     cmsg_len <= msg_controllen + CMSG_LEN(0);
     605  		     cmsg_len++) {
     606  			test_scm_rights1(mh, msg_controllen,
     607  					 page, fds0, cmsg_len);
     608  		}
     609  	}
     610  
     611  	max_msg_controllen =
     612  		CMSG_SPACE(sizeof(fds0)) + CMSG_SPACE(sizeof(fds1)) +
     613  		sizeof(*fds0) - 1;
     614  	for (size_t msg_controllen = CMSG_LEN(0) * 2;
     615  	     msg_controllen <= max_msg_controllen;
     616  	     msg_controllen++) {
     617  		static const int *const fdps[] = { fds0, fds1 };
     618  		size_t cmsg_len[2];
     619  
     620  		for (cmsg_len[0] = CMSG_LEN(0);
     621  		     CMSG_ALIGN(cmsg_len[0]) + CMSG_LEN(0) <= msg_controllen
     622  		     && CMSG_ALIGN(cmsg_len[0]) <= CMSG_SPACE(sizeof(fds0));
     623  		     cmsg_len[0]++) {
     624  			const size_t msg_controllen1 =
     625  				msg_controllen - CMSG_ALIGN(cmsg_len[0]);
     626  
     627  			for (cmsg_len[1] = 0;
     628  			     cmsg_len[1] <= msg_controllen1 + CMSG_LEN(0);
     629  			     cmsg_len[1]++) {
     630  				test_scm_rights2(mh, msg_controllen,
     631  						 page, fdps, cmsg_len);
     632  			}
     633  		}
     634  	}
     635  
     636  	static const char text[16] = "0123456789abcdef";
     637  	max_msg_controllen = CMSG_SPACE(sizeof(text)) + CMSG_LEN(0) - 1;
     638  	for (size_t msg_controllen = CMSG_LEN(0);
     639  	     msg_controllen <= max_msg_controllen;
     640  	     msg_controllen++) {
     641  		for (size_t cmsg_len = 0;
     642  		     cmsg_len <= msg_controllen + CMSG_LEN(0)
     643  		     && cmsg_len <= CMSG_LEN(sizeof(text));
     644  		     cmsg_len++) {
     645  			test_scm_security(mh, msg_controllen,
     646  					  page, text, cmsg_len,
     647  					  ARG_STR(SOL_SOCKET));
     648  		}
     649  	}
     650  
     651  	test_scm_rights3(mh, page, DEFAULT_STRLEN - 1);
     652  	test_scm_rights3(mh, page, DEFAULT_STRLEN);
     653  	test_scm_rights3(mh, page, DEFAULT_STRLEN + 1);
     654  
     655  	test_scm_timestamp_old(mh, page);
     656  	test_scm_timestampns_old(mh, page);
     657  	test_scm_timestamping_old(mh, page);
     658  	test_scm_timestamp_new(mh, page);
     659  	test_scm_timestampns_new(mh, page);
     660  	test_scm_timestamping_new(mh, page);
     661  
     662  	test_unknown_type(mh, page, ARG_STR(SOL_SOCKET), "SCM_???");
     663  }
     664  
     665  static void
     666  test_ip_pktinfo(struct msghdr *const mh, void *const page,
     667  		const int cmsg_type, const char *const cmsg_type_str)
     668  {
     669  	const unsigned int len = CMSG_SPACE(sizeof(struct in_pktinfo));
     670  	struct cmsghdr *const cmsg = get_cmsghdr(page, len);
     671  
     672  	cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
     673  	cmsg->cmsg_level = SOL_IP;
     674  	cmsg->cmsg_type = cmsg_type;
     675  
     676  	struct in_pktinfo *const info = (struct in_pktinfo *) CMSG_DATA(cmsg);
     677  	info->ipi_ifindex = ifindex_lo();
     678  	info->ipi_spec_dst.s_addr = inet_addr("1.2.3.4");
     679  	info->ipi_addr.s_addr = inet_addr("5.6.7.8");
     680  
     681  	mh->msg_control = cmsg;
     682  	mh->msg_controllen = len;
     683  
     684  	int rc = sendmsg(-1, mh, 0);
     685  	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
     686  	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u, cmsg_level=SOL_IP"
     687  	       ", cmsg_type=%s, cmsg_data={ipi_ifindex=%s"
     688  	       ", ipi_spec_dst=inet_addr(\"%s\")"
     689  	       ", ipi_addr=inet_addr(\"%s\")}}]"
     690  	       ", msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
     691  	       (unsigned) cmsg->cmsg_len, cmsg_type_str,
     692  	       IFINDEX_LO_STR, "1.2.3.4", "5.6.7.8", len, rc, errno2name());
     693  }
     694  
     695  static void
     696  test_ip_uint(struct msghdr *const mh, void *const page,
     697  	     const int cmsg_type, const char *const cmsg_type_str)
     698  {
     699  	const unsigned int len = CMSG_SPACE(sizeof(int));
     700  	struct cmsghdr *const cmsg = get_cmsghdr(page, len);
     701  
     702  	cmsg->cmsg_len = CMSG_LEN(sizeof(int));
     703  	cmsg->cmsg_level = SOL_IP;
     704  	cmsg->cmsg_type = cmsg_type;
     705  
     706  	unsigned int *u = (void *) CMSG_DATA(cmsg);
     707  	*u = 0xfacefeed;
     708  
     709  	mh->msg_control = cmsg;
     710  	mh->msg_controllen = len;
     711  
     712  	int rc = sendmsg(-1, mh, 0);
     713  	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
     714  	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
     715  	       ", cmsg_level=SOL_IP, cmsg_type=%s, cmsg_data=[%u]}]"
     716  	       ", msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
     717  	       (unsigned) cmsg->cmsg_len, cmsg_type_str, *u, len,
     718  	       rc, errno2name());
     719  }
     720  
     721  static void
     722  test_ip_uint8_t(struct msghdr *const mh, void *const page,
     723  		const int cmsg_type, const char *const cmsg_type_str)
     724  {
     725  	const unsigned int len = CMSG_SPACE(1);
     726  	struct cmsghdr *const cmsg = get_cmsghdr(page, len);
     727  
     728  	cmsg->cmsg_len = CMSG_LEN(1);
     729  	cmsg->cmsg_level = SOL_IP;
     730  	cmsg->cmsg_type = cmsg_type;
     731  	*CMSG_DATA(cmsg) = 'A';
     732  
     733  	mh->msg_control = cmsg;
     734  	mh->msg_controllen = len;
     735  
     736  	int rc = sendmsg(-1, mh, 0);
     737  	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
     738  	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
     739  	       ", cmsg_level=SOL_IP, cmsg_type=%s, cmsg_data=[%#x]}]"
     740  	       ", msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
     741  	       (unsigned) cmsg->cmsg_len, cmsg_type_str,
     742  	       (unsigned) (uint8_t) 'A', len, rc, errno2name());
     743  }
     744  
     745  static void
     746  print_ip_opts(const void *const cmsg_data, const unsigned int data_len)
     747  {
     748  	const unsigned char *const opts = cmsg_data;
     749  	for (unsigned int i = 0; i < data_len; ++i) {
     750  		if (i)
     751  			printf(", ");
     752  #if !VERBOSE
     753  		if (i >= DEFAULT_STRLEN) {
     754  			printf("...");
     755  			break;
     756  		}
     757  #endif
     758  		printf("0x%02x", opts[i]);
     759  	}
     760  }
     761  
     762  static void
     763  test_ip_opts(struct msghdr *const mh, void *const page,
     764  	     const int cmsg_type, const char *const cmsg_type_str,
     765  	     const unsigned int opts_len)
     766  {
     767  	unsigned int len = CMSG_SPACE(opts_len);
     768  	struct cmsghdr *cmsg = get_cmsghdr(page, len);
     769  
     770  	cmsg->cmsg_len = CMSG_LEN(opts_len);
     771  	cmsg->cmsg_level = SOL_IP;
     772  	cmsg->cmsg_type = cmsg_type;
     773  	for (unsigned int i = 0; i < opts_len; ++i)
     774  		CMSG_DATA(cmsg)[i] = 'A' + i;
     775  
     776  	mh->msg_control = cmsg;
     777  	mh->msg_controllen = len;
     778  
     779  	int rc = sendmsg(-1, mh, 0);
     780  	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
     781  	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
     782  	       ", cmsg_level=SOL_IP, cmsg_type=%s, cmsg_data=[",
     783  	       (unsigned) cmsg->cmsg_len, cmsg_type_str);
     784  	print_ip_opts(CMSG_DATA(cmsg), opts_len);
     785  	printf("]}], msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
     786  	       len, rc, errno2name());
     787  }
     788  
     789  #ifdef IP_RECVERR
     790  struct sock_ee {
     791  	uint32_t ee_errno;
     792  	uint8_t  ee_origin;
     793  	uint8_t  ee_type;
     794  	uint8_t  ee_code;
     795  	uint8_t  ee_pad;
     796  	uint32_t ee_info;
     797  	uint32_t ee_data;
     798  	struct sockaddr_in offender;
     799  };
     800  
     801  static void
     802  test_ip_recverr(struct msghdr *const mh, void *const page,
     803  		const int cmsg_type, const char *const cmsg_type_str)
     804  {
     805  	const unsigned int len = CMSG_SPACE(sizeof(struct sock_ee));
     806  	struct cmsghdr *const cmsg = get_cmsghdr(page, len);
     807  
     808  	cmsg->cmsg_len = CMSG_LEN(sizeof(struct sock_ee));
     809  	cmsg->cmsg_level = SOL_IP;
     810  	cmsg->cmsg_type = cmsg_type;
     811  
     812  	struct sock_ee *const e = (struct sock_ee *) CMSG_DATA(cmsg);
     813  	e->ee_errno = 0xdeadbeef;
     814  	e->ee_origin = 2;
     815  	e->ee_type = 3;
     816  	e->ee_code = 4;
     817  	e->ee_info = 0xfacefeed;
     818  	e->ee_data = 0xbadc0ded;
     819  	e->offender.sin_family = AF_INET,
     820  	e->offender.sin_port = htons(12345),
     821  	e->offender.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
     822  
     823  	mh->msg_control = cmsg;
     824  	mh->msg_controllen = len;
     825  
     826  	int rc = sendmsg(-1, mh, 0);
     827  	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
     828  	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u, cmsg_level=SOL_IP"
     829  	       ", cmsg_type=%s, cmsg_data={ee_errno=%u, ee_origin=%u"
     830  	       ", ee_type=%u, ee_code=%u, ee_info=%u, ee_data=%u"
     831  	       ", offender={sa_family=AF_INET, sin_port=htons(%hu)"
     832  	       ", sin_addr=inet_addr(\"127.0.0.1\")}}}]"
     833  	       ", msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
     834  	       (unsigned) cmsg->cmsg_len, cmsg_type_str,
     835  	       e->ee_errno, e->ee_origin, e->ee_type,
     836  	       e->ee_code, e->ee_info, e->ee_data,
     837  	       ntohs(e->offender.sin_port),
     838  	       len, rc, errno2name());
     839  }
     840  #endif
     841  
     842  #ifdef IP_ORIGDSTADDR
     843  static void
     844  test_ip_origdstaddr(struct msghdr *const mh, void *const page,
     845  		    const int cmsg_type, const char *const cmsg_type_str)
     846  {
     847  	const unsigned int len = CMSG_SPACE(sizeof(struct sockaddr_in));
     848  	struct cmsghdr *const cmsg = get_cmsghdr(page, len);
     849  
     850  	cmsg->cmsg_len = CMSG_LEN(sizeof(struct sockaddr_in));
     851  	cmsg->cmsg_level = SOL_IP;
     852  	cmsg->cmsg_type = cmsg_type;
     853  
     854  	struct sockaddr_in *const sin = (struct sockaddr_in *) CMSG_DATA(cmsg);
     855  	sin->sin_family = AF_INET,
     856  	sin->sin_port = htons(12345),
     857  	sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
     858  
     859  	mh->msg_control = cmsg;
     860  	mh->msg_controllen = len;
     861  
     862  	int rc = sendmsg(-1, mh, 0);
     863  	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
     864  	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u, cmsg_level=SOL_IP"
     865  	       ", cmsg_type=%s, cmsg_data={sa_family=AF_INET"
     866  	       ", sin_port=htons(%hu), sin_addr=inet_addr(\"127.0.0.1\")}}]"
     867  	       ", msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
     868  	       (unsigned) cmsg->cmsg_len, cmsg_type_str,
     869  	       ntohs(sin->sin_port), len, rc, errno2name());
     870  }
     871  #endif
     872  
     873  #ifdef IP_PROTOCOL
     874  static void
     875  test_ip_protocol(struct msghdr *const mh, void *const page,
     876  		 const int cmsg_type, const char *const cmsg_type_str)
     877  {
     878  	const unsigned int len = CMSG_SPACE(sizeof(int));
     879  	struct cmsghdr *const cmsg = get_cmsghdr(page, len);
     880  
     881  	cmsg->cmsg_len = CMSG_LEN(sizeof(int));
     882  	cmsg->cmsg_level = SOL_IP;
     883  	cmsg->cmsg_type = cmsg_type;
     884  
     885  	unsigned int *u = (void *) CMSG_DATA(cmsg);
     886  	*u = IPPROTO_RAW;
     887  
     888  	mh->msg_control = cmsg;
     889  	mh->msg_controllen = len;
     890  
     891  	int rc = sendmsg(-1, mh, 0);
     892  	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
     893  	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
     894  	       ", cmsg_level=SOL_IP, cmsg_type=%s, cmsg_data=[%s]}]"
     895  	       ", msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
     896  	       (unsigned) cmsg->cmsg_len, cmsg_type_str, "IPPROTO_RAW", len,
     897  	       rc, errno2name());
     898  }
     899  #endif
     900  
     901  static void
     902  test_sol_ip(struct msghdr *const mh, void *const page)
     903  {
     904  	test_ip_pktinfo(mh, page, ARG_STR(IP_PKTINFO));
     905  	test_ip_uint(mh, page, ARG_STR(IP_TTL));
     906  	test_ip_uint8_t(mh, page, ARG_STR(IP_TOS));
     907  	test_ip_opts(mh, page, ARG_STR(IP_RECVOPTS), 1);
     908  	test_ip_opts(mh, page, ARG_STR(IP_RECVOPTS), 2);
     909  	test_ip_opts(mh, page, ARG_STR(IP_RECVOPTS), 3);
     910  	test_ip_opts(mh, page, ARG_STR(IP_RECVOPTS), 4);
     911  	test_ip_opts(mh, page, ARG_STR(IP_RETOPTS), 5);
     912  	test_ip_opts(mh, page, ARG_STR(IP_RETOPTS), 6);
     913  	test_ip_opts(mh, page, ARG_STR(IP_RETOPTS), 7);
     914  	test_ip_opts(mh, page, ARG_STR(IP_RETOPTS), 8);
     915  	test_ip_opts(mh, page, ARG_STR(IP_RETOPTS), DEFAULT_STRLEN - 1);
     916  	test_ip_opts(mh, page, ARG_STR(IP_RETOPTS), DEFAULT_STRLEN);
     917  	test_ip_opts(mh, page, ARG_STR(IP_RETOPTS), DEFAULT_STRLEN + 1);
     918  #ifdef IP_RECVERR
     919  	test_ip_recverr(mh, page, ARG_STR(IP_RECVERR));
     920  #endif
     921  #ifdef IP_ORIGDSTADDR
     922  	test_ip_origdstaddr(mh, page, ARG_STR(IP_ORIGDSTADDR));
     923  #endif
     924  #ifdef IP_CHECKSUM
     925  	test_ip_uint(mh, page, ARG_STR(IP_CHECKSUM));
     926  #endif
     927  #ifdef IP_PROTOCOL
     928  	test_ip_protocol(mh, page, ARG_STR(IP_PROTOCOL));
     929  #endif
     930  	test_scm_security(mh, CMSG_LEN(0), page, 0, CMSG_LEN(0),
     931  			  ARG_STR(SOL_IP));
     932  	test_unknown_type(mh, page, ARG_STR(SOL_IP), "IP_???");
     933  }
     934  
     935  static void
     936  test_unknown_level(struct msghdr *const mh, void *const page)
     937  {
     938  	struct cmsghdr *cmsg = get_cmsghdr(page, CMSG_LEN(0));
     939  
     940  	cmsg->cmsg_len = CMSG_LEN(0);
     941  	cmsg->cmsg_level = SOL_TCP;
     942  	cmsg->cmsg_type = 0xdeadbeef;
     943  
     944  	mh->msg_control = cmsg;
     945  	mh->msg_controllen = cmsg->cmsg_len;
     946  
     947  	int rc = sendmsg(-1, mh, 0);
     948  	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
     949  	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u, cmsg_level=%s"
     950  	       ", cmsg_type=%#x}], msg_controllen=%u, msg_flags=0}"
     951  	       ", 0) = %d %s (%m)\n",
     952  	       (unsigned) cmsg->cmsg_len, "SOL_TCP", cmsg->cmsg_type,
     953  	       (unsigned) mh->msg_controllen, rc, errno2name());
     954  }
     955  
     956  static void
     957  test_big_len(struct msghdr *const mh)
     958  {
     959  	int optmem_max;
     960  
     961  	if (read_int_from_file("/proc/sys/net/core/optmem_max", &optmem_max)
     962  	    || optmem_max <= 0 || optmem_max > 0x100000)
     963  		optmem_max = sizeof(long long) * (2 * IOV_MAX + 512);
     964  	optmem_max = (optmem_max + sizeof(long long) - 1)
     965  		     & ~(sizeof(long long) - 1);
     966  
     967  	const size_t len = optmem_max * 2;
     968  	struct cmsghdr *const cmsg = tail_alloc(len);
     969  	cmsg->cmsg_len = len;
     970  	cmsg->cmsg_level = SOL_SOCKET;
     971  	cmsg->cmsg_type = SCM_RIGHTS;
     972  
     973  	mh->msg_control = cmsg;
     974  	mh->msg_controllen = len;
     975  
     976  	int rc = sendmsg(-1, mh, 0);
     977  	if (EBADF != errno)
     978  		perror_msg_and_skip("sendmsg");
     979  
     980  	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
     981  	       ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
     982  	       ", cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS",
     983  	       (unsigned) cmsg->cmsg_len);
     984  	print_fds(cmsg, optmem_max);
     985  	printf("}, ...], msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
     986  	       (unsigned long) len, rc, errno2name());
     987  }
     988  
     989  int main(int ac, const char **av)
     990  {
     991  	int rc = sendmsg(-1, 0, 0);
     992  	printf("sendmsg(-1, NULL, 0) = %d %s (%m)\n", rc, errno2name());
     993  
     994  	TAIL_ALLOC_OBJECT_CONST_PTR(struct msghdr, mh);
     995  	memset(mh, 0, sizeof(*mh));
     996  	test_big_len(mh);
     997  
     998  	rc = sendmsg(-1, mh + 1, 0);
     999  	printf("sendmsg(-1, %p, 0) = %d %s (%m)\n",
    1000  	       mh + 1, rc, errno2name());
    1001  
    1002  	void *page = tail_alloc(1024) + 1024;
    1003  	mh->msg_control = page;
    1004  	mh->msg_controllen = CMSG_LEN(0);
    1005  	rc = sendmsg(-1, mh, 0);
    1006  	printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
    1007  	       ", msg_iovlen=0, msg_control=%p, msg_controllen=%u"
    1008  	       ", msg_flags=0}, 0) = %d %s (%m)\n",
    1009  	       page, (unsigned) CMSG_LEN(0), rc, errno2name());
    1010  
    1011  	test_sol_socket(mh, page);
    1012  	test_sol_ip(mh, page);
    1013  	test_unknown_level(mh, page);
    1014  
    1015  	puts("+++ exited with 0 +++");
    1016  	return 0;
    1017  }