Home | History | Annotate | Download | only in tests-mx32
      1 /*
      2  * Check decoding of struct msghdr ancillary data.
      3  *
      4  * Copyright (c) 2016 Dmitry V. Levin <ldv (at) 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 
     60 static struct cmsghdr *
     61 get_cmsghdr(void *const page, const size_t len)
     62 {
     63 	return page - CMSG_ALIGN(len);
     64 }
     65 
     66 static void
     67 print_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 
     91 static void
     92 test_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 
    144 static void
    145 test_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 
    206 static void
    207 test_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 
    233 static void
    234 test_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 
    278 static void
    279 test_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 
    324 static void
    325 test_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 
    379 static void
    380 print_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 
    389 static void
    390 test_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 
    436 static void
    437 test_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 
    461 static void
    462 test_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 
    535 static void
    536 test_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 
    574 static void
    575 test_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 
    600 static void
    601 test_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 
    624 static void
    625 print_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 
    642 static void
    643 test_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
    671 struct 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 
    682 static void
    683 test_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
    724 static void
    725 test_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 
    754 static void
    755 test_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 
    785 static void
    786 test_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 
    806 static void
    807 test_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 
    839 int 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