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