1 /*
2  * Check decoding of struct msghdr ancillary data.
3  *
4  * Copyright (c) 2016 Dmitry V. Levin <ldv@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 *
get_cmsghdr(void * const page,const size_t len)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
print_fds(const struct cmsghdr * const cmsg,const size_t cmsg_len)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
test_scm_rights1(struct msghdr * const mh,const size_t msg_controllen,void * const page,const void * const src,const size_t cmsg_len)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
test_scm_rights2(struct msghdr * const mh,const size_t msg_controllen,void * const page,const int * const * const src,const size_t * const cmsg_len)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
test_scm_rights3(struct msghdr * const mh,void * const page,const size_t nfds)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
print_security(const struct cmsghdr * const cmsg,const size_t cmsg_len)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
test_scm_security(struct msghdr * const mh,const size_t msg_controllen,void * const page,const void * const src,const size_t cmsg_len,const int cmsg_level,const char * const cmsg_level_str)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
test_unknown_type(struct msghdr * const mh,void * const page,const int cmsg_level,const char * const cmsg_level_str,const char * const cmsg_type_str)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
test_sol_socket(struct msghdr * const mh,void * const page)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
test_ip_pktinfo(struct msghdr * const mh,void * const page,const int cmsg_type,const char * const cmsg_type_str)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
test_ip_uint(struct msghdr * const mh,void * const page,const int cmsg_type,const char * const cmsg_type_str)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
test_ip_uint8_t(struct msghdr * const mh,void * const page,const int cmsg_type,const char * const cmsg_type_str)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
print_ip_opts(const void * const cmsg_data,const unsigned int data_len)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
test_ip_opts(struct msghdr * const mh,void * const page,const int cmsg_type,const char * const cmsg_type_str,const unsigned int opts_len)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
test_ip_recverr(struct msghdr * const mh,void * const page,const int cmsg_type,const char * const cmsg_type_str)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
test_ip_origdstaddr(struct msghdr * const mh,void * const page,const int cmsg_type,const char * const cmsg_type_str)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
test_sol_ip(struct msghdr * const mh,void * const page)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
test_unknown_level(struct msghdr * const mh,void * const page)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
test_big_len(struct msghdr * const mh)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 
main(int ac,const char ** av)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