]> granicus.if.org Git - strace/blob - tests/msg_control.c
tests: check decoding of struct msghdr ancillary data
[strace] / tests / msg_control.c
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 <stddef.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <sys/socket.h>
37 #include <net/if.h>
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
40
41 #ifndef SOL_IP
42 # define SOL_IP 0
43 #endif
44 #ifndef SOL_TCP
45 # define SOL_TCP 6
46 #endif
47
48 #ifndef SCM_SECURITY
49 # define SCM_SECURITY 3
50 #endif
51
52 #define MIN_SIZE_OF(type, member) \
53         (offsetof(type, member) + sizeof(((type *) 0)->member))
54
55 #define VAL_STR(val) val, #val
56
57 static struct cmsghdr *
58 get_cmsghdr(void *const page, const size_t len)
59 {
60         return page - CMSG_ALIGN(len);
61 }
62
63 static void
64 print_fds(const struct cmsghdr *const cmsg, const size_t cmsg_len)
65 {
66         size_t nfd = cmsg_len > CMSG_LEN(0)
67                      ? (cmsg_len - CMSG_LEN(0)) / sizeof(int) : 0;
68         if (!nfd)
69                 return;
70
71         printf(", cmsg_data=[");
72         int *fdp = (int *) CMSG_DATA(cmsg);
73         size_t i;
74         for (i = 0; i < nfd; ++i) {
75                 if (i)
76                         printf(", ");
77                 printf("%d", fdp[i]);
78         }
79         printf("]");
80 }
81
82 static void
83 test_scm_rights1(struct msghdr *const mh,
84                  const size_t msg_controllen,
85                  void *const page,
86                  const void *const src,
87                  const size_t cmsg_len)
88 {
89         const size_t aligned_cms_len =
90                 cmsg_len > CMSG_LEN(0) ? CMSG_ALIGN(cmsg_len) : CMSG_LEN(0);
91         if (cmsg_len >= CMSG_LEN(0)
92             && aligned_cms_len + CMSG_LEN(0) <= msg_controllen)
93                 return;
94
95         struct cmsghdr *cmsg = get_cmsghdr(page, msg_controllen);
96
97         if (msg_controllen >= MIN_SIZE_OF(struct cmsghdr, cmsg_len))
98                 cmsg->cmsg_len = cmsg_len;
99         if (msg_controllen >= MIN_SIZE_OF(struct cmsghdr, cmsg_level))
100                 cmsg->cmsg_level = SOL_SOCKET;
101         if (msg_controllen >= MIN_SIZE_OF(struct cmsghdr, cmsg_type))
102                 cmsg->cmsg_type = SCM_RIGHTS;
103
104         size_t src_len =
105                 cmsg_len < msg_controllen ? cmsg_len : msg_controllen;
106         if (src_len > CMSG_LEN(0))
107                 memcpy(CMSG_DATA(cmsg), src, src_len - CMSG_LEN(0));
108
109         mh->msg_control = cmsg;
110         mh->msg_controllen = msg_controllen;
111
112         int rc = sendmsg(-1, mh, 0);
113         int saved_errno = errno;
114
115         printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
116                ", msg_iovlen=0");
117         if (msg_controllen < CMSG_LEN(0)) {
118                 if (msg_controllen)
119                         printf(", msg_control=%p", cmsg);
120         } else {
121                 printf(", msg_control=[{cmsg_len=%lu, cmsg_level=SOL_SOCKET"
122                        ", cmsg_type=SCM_RIGHTS", (unsigned long) cmsg_len);
123                 print_fds(cmsg, src_len);
124                 printf("}");
125                 if (aligned_cms_len < msg_controllen)
126                         printf(", %p", (void *) cmsg + aligned_cms_len);
127                 printf("]");
128         }
129
130         errno = saved_errno;
131         printf(", msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
132                (unsigned long) msg_controllen, rc, errno2name());
133 }
134
135 static void
136 test_scm_rights2(struct msghdr *const mh,
137                  const size_t msg_controllen,
138                  void *const page,
139                  const int *const *const src,
140                  const size_t *const cmsg_len)
141 {
142         const size_t aligned_cms_len[2] = {
143                 cmsg_len[0] > CMSG_LEN(0) ? CMSG_ALIGN(cmsg_len[0]) : CMSG_LEN(0),
144                 cmsg_len[1] > CMSG_LEN(0) ? CMSG_ALIGN(cmsg_len[1]) : CMSG_LEN(0)
145         };
146         if (cmsg_len[0] < CMSG_LEN(0)
147             || aligned_cms_len[0] + CMSG_LEN(0) > msg_controllen
148             || aligned_cms_len[0] + aligned_cms_len[1] + CMSG_LEN(0) <= msg_controllen)
149                 return;
150
151         struct cmsghdr *const cmsg[2] = {
152                 get_cmsghdr(page, msg_controllen),
153                 (void *) get_cmsghdr(page, msg_controllen) + aligned_cms_len[0]
154         };
155         cmsg[0]->cmsg_len = cmsg_len[0];
156         cmsg[0]->cmsg_level = SOL_SOCKET;
157         cmsg[0]->cmsg_type = SCM_RIGHTS;
158         if (cmsg_len[0] > CMSG_LEN(0))
159                 memcpy(CMSG_DATA(cmsg[0]), src[0], cmsg_len[0] - CMSG_LEN(0));
160
161         const size_t msg_controllen1 = msg_controllen - aligned_cms_len[0];
162         if (msg_controllen1 >= MIN_SIZE_OF(struct cmsghdr, cmsg_len))
163                 cmsg[1]->cmsg_len = cmsg_len[1];
164         if (msg_controllen >= MIN_SIZE_OF(struct cmsghdr, cmsg_level))
165                 cmsg[1]->cmsg_level = SOL_SOCKET;
166         if (msg_controllen >= MIN_SIZE_OF(struct cmsghdr, cmsg_type))
167                 cmsg[1]->cmsg_type = SCM_RIGHTS;
168         size_t src1_len =
169                 cmsg_len[1] < msg_controllen1 ? cmsg_len[1] : msg_controllen1;
170         if (src1_len > CMSG_LEN(0))
171                 memcpy(CMSG_DATA(cmsg[1]), src[1], src1_len - CMSG_LEN(0));
172
173         mh->msg_control = cmsg[0];
174         mh->msg_controllen = msg_controllen;
175
176         int rc = sendmsg(-1, mh, 0);
177         int saved_errno = errno;
178
179         printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
180                ", msg_iovlen=0, msg_control=[{cmsg_len=%lu"
181                ", cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS",
182                (unsigned long) cmsg_len[0]);
183         print_fds(cmsg[0], cmsg_len[0]);
184         printf("}, {cmsg_len=%lu, cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS",
185                (unsigned long) cmsg_len[1]);
186         print_fds(cmsg[1], src1_len);
187         printf("}");
188         if (aligned_cms_len[1] < msg_controllen1)
189                 printf(", %p", (void *) cmsg[1] + aligned_cms_len[1]);
190         printf("]");
191
192         errno = saved_errno;
193         printf(", msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
194                (unsigned long) msg_controllen, rc, errno2name());
195 }
196
197 static void
198 print_security(const struct cmsghdr *const cmsg, const size_t cmsg_len)
199 {
200         int n = cmsg_len > CMSG_LEN(0) ? cmsg_len - CMSG_LEN(0) : 0;
201         if (!n)
202                 return;
203
204         printf(", cmsg_data=\"%.*s\"", n, CMSG_DATA(cmsg));
205 }
206
207 static void
208 test_scm_security(struct msghdr *const mh,
209                   const size_t msg_controllen,
210                   void *const page,
211                   const void *const src,
212                   const size_t cmsg_len,
213                   const int cmsg_level,
214                   const char *const cmsg_level_str)
215 {
216         const size_t aligned_cms_len =
217                 cmsg_len > CMSG_LEN(0) ? CMSG_ALIGN(cmsg_len) : CMSG_LEN(0);
218         if (cmsg_len >= CMSG_LEN(0)
219             && aligned_cms_len + CMSG_LEN(0) <= msg_controllen)
220                 return;
221
222         struct cmsghdr *cmsg = get_cmsghdr(page, msg_controllen);
223
224         cmsg->cmsg_len = cmsg_len;
225         cmsg->cmsg_level = cmsg_level;
226         cmsg->cmsg_type = SCM_SECURITY;
227
228         size_t src_len =
229                 cmsg_len < msg_controllen ? cmsg_len : msg_controllen;
230         if (src_len > CMSG_LEN(0))
231                 memcpy(CMSG_DATA(cmsg), src, src_len - CMSG_LEN(0));
232
233         mh->msg_control = cmsg;
234         mh->msg_controllen = msg_controllen;
235
236         int rc = sendmsg(-1, mh, 0);
237         int saved_errno = errno;
238
239         printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
240                ", msg_iovlen=0, msg_control=[{cmsg_len=%lu, cmsg_level=%s"
241                ", cmsg_type=SCM_SECURITY",
242                (unsigned long) cmsg_len, cmsg_level_str);
243         print_security(cmsg, src_len);
244         printf("}");
245         if (aligned_cms_len < msg_controllen)
246                 printf(", %p", (void *) cmsg + aligned_cms_len);
247         printf("]");
248
249         errno = saved_errno;
250         printf(", msg_controllen=%lu, msg_flags=0}, 0) = %d %s (%m)\n",
251                (unsigned long) msg_controllen, rc, errno2name());
252 }
253
254 static void
255 test_unknown_type(struct msghdr *const mh,
256                   void *const page,
257                   const int cmsg_level,
258                   const char *const cmsg_level_str,
259                   const char *const cmsg_type_str)
260 {
261         struct cmsghdr *cmsg = get_cmsghdr(page, CMSG_LEN(0));
262
263         cmsg->cmsg_len = CMSG_LEN(0);
264         cmsg->cmsg_level = cmsg_level;
265         cmsg->cmsg_type = 0xfacefeed;
266
267         mh->msg_control = cmsg;
268         mh->msg_controllen = cmsg->cmsg_len;
269
270         int rc = sendmsg(-1, mh, 0);
271         printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
272                ", msg_iovlen=0, msg_control=[{cmsg_len=%u, cmsg_level=%s"
273                ", cmsg_type=%#x /* %s */}], msg_controllen=%u, msg_flags=0}"
274                ", 0) = %d %s (%m)\n",
275                (unsigned) cmsg->cmsg_len, cmsg_level_str, cmsg->cmsg_type,
276                cmsg_type_str, (unsigned) mh->msg_controllen, rc, errno2name());
277 }
278
279 static void
280 test_sol_socket(struct msghdr *const mh, void *const page)
281 {
282         static const int fds0[] = { -10, -11, -12, -13 };
283         static const int fds1[] = { -15, -16, -17, -18 };
284         size_t msg_controllen, max_msg_controllen;
285
286         max_msg_controllen = CMSG_SPACE(sizeof(fds0)) + sizeof(*fds0) - 1;
287         for (msg_controllen = 0;
288              msg_controllen <= max_msg_controllen;
289              msg_controllen++) {
290                 size_t cmsg_len;
291
292                 for (cmsg_len = 0;
293                      cmsg_len <= msg_controllen + CMSG_LEN(0);
294                      cmsg_len++) {
295                         test_scm_rights1(mh, msg_controllen,
296                                          page, fds0, cmsg_len);
297                 }
298         }
299
300         max_msg_controllen =
301                 CMSG_SPACE(sizeof(fds0)) + CMSG_SPACE(sizeof(fds1)) +
302                 sizeof(*fds0) - 1;
303         for (msg_controllen = CMSG_LEN(0) * 2;
304              msg_controllen <= max_msg_controllen;
305              msg_controllen++) {
306                 static const int *const fdps[] = { fds0, fds1 };
307                 size_t cmsg_len[2];
308
309                 for (cmsg_len[0] = CMSG_LEN(0);
310                      CMSG_ALIGN(cmsg_len[0]) + CMSG_LEN(0) <= msg_controllen
311                      && CMSG_ALIGN(cmsg_len[0]) <= CMSG_SPACE(sizeof(fds0));
312                      cmsg_len[0]++) {
313                         const size_t msg_controllen1 =
314                                 msg_controllen - CMSG_ALIGN(cmsg_len[0]);
315
316                         for (cmsg_len[1] = 0;
317                              cmsg_len[1] <= msg_controllen1 + CMSG_LEN(0);
318                              cmsg_len[1]++) {
319                                 test_scm_rights2(mh, msg_controllen,
320                                                  page, fdps, cmsg_len);
321                         }
322                 }
323         }
324
325         static const char text[16] = "0123456789abcdef";
326         max_msg_controllen = CMSG_SPACE(sizeof(text)) + CMSG_LEN(0) - 1;
327         for (msg_controllen = CMSG_LEN(0);
328              msg_controllen <= max_msg_controllen;
329              msg_controllen++) {
330                 size_t cmsg_len;
331
332                 for (cmsg_len = 0;
333                      cmsg_len <= msg_controllen + CMSG_LEN(0)
334                      && cmsg_len <= CMSG_LEN(sizeof(text));
335                      cmsg_len++) {
336                         test_scm_security(mh, msg_controllen,
337                                           page, text, cmsg_len,
338                                           VAL_STR(SOL_SOCKET));
339                 }
340         }
341
342         test_unknown_type(mh, page, VAL_STR(SOL_SOCKET), "SCM_???");
343 }
344
345 static void
346 test_ip_pktinfo(struct msghdr *const mh, void *const page,
347                 const int cmsg_type, const char *const cmsg_type_str)
348 {
349         const unsigned int len = CMSG_SPACE(sizeof(struct in_pktinfo));
350         struct cmsghdr *const cmsg = get_cmsghdr(page, len);
351
352         cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
353         cmsg->cmsg_level = SOL_IP;
354         cmsg->cmsg_type = cmsg_type;
355
356         struct in_pktinfo *const info = (struct in_pktinfo *) CMSG_DATA(cmsg);
357 #ifdef HAVE_IF_INDEXTONAME
358         info->ipi_ifindex = if_nametoindex("lo");
359 #else
360         info->ipi_ifindex = 1;
361 #endif
362         info->ipi_spec_dst.s_addr = inet_addr("1.2.3.4");
363         info->ipi_addr.s_addr = inet_addr("5.6.7.8");
364
365         mh->msg_control = cmsg;
366         mh->msg_controllen = len;
367
368         int rc = sendmsg(-1, mh, 0);
369         printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
370                ", msg_iovlen=0, msg_control=[{cmsg_len=%u, cmsg_level=SOL_IP"
371                ", cmsg_type=%s, cmsg_data={ipi_ifindex=%s"
372                ", ipi_spec_dst=inet_addr(\"%s\")"
373                ", ipi_addr=inet_addr(\"%s\")}}]"
374                ", msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
375                (unsigned) cmsg->cmsg_len, cmsg_type_str,
376 #ifdef HAVE_IF_INDEXTONAME
377                "if_nametoindex(\"lo\")",
378 #else
379                "1",
380 #endif
381                "1.2.3.4", "5.6.7.8", len, rc, errno2name());
382 }
383
384 static void
385 test_ip_uint(struct msghdr *const mh, void *const page,
386              const int cmsg_type, const char *const cmsg_type_str)
387 {
388         const unsigned int len = CMSG_SPACE(sizeof(int));
389         struct cmsghdr *const cmsg = get_cmsghdr(page, len);
390
391         cmsg->cmsg_len = CMSG_LEN(sizeof(int));
392         cmsg->cmsg_level = SOL_IP;
393         cmsg->cmsg_type = cmsg_type;
394
395         unsigned int *u = (void *) CMSG_DATA(cmsg);
396         *u = 0xfacefeed;
397
398         mh->msg_control = cmsg;
399         mh->msg_controllen = len;
400
401         int rc = sendmsg(-1, mh, 0);
402         printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
403                ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
404                ", cmsg_level=SOL_IP, cmsg_type=%s, cmsg_data=[%u]}]"
405                ", msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
406                (unsigned) cmsg->cmsg_len, cmsg_type_str, *u, len,
407                rc, errno2name());
408 }
409
410 static void
411 test_ip_uint8_t(struct msghdr *const mh, void *const page,
412                 const int cmsg_type, const char *const cmsg_type_str)
413 {
414         const unsigned int len = CMSG_SPACE(1);
415         struct cmsghdr *const cmsg = get_cmsghdr(page, len);
416
417         cmsg->cmsg_len = CMSG_LEN(1);
418         cmsg->cmsg_level = SOL_IP;
419         cmsg->cmsg_type = cmsg_type;
420         *CMSG_DATA(cmsg) = 'A';
421
422         mh->msg_control = cmsg;
423         mh->msg_controllen = len;
424
425         int rc = sendmsg(-1, mh, 0);
426         printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
427                ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
428                ", cmsg_level=SOL_IP, cmsg_type=%s, cmsg_data=[%#x]}]"
429                ", msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
430                (unsigned) cmsg->cmsg_len, cmsg_type_str,
431                (unsigned) (uint8_t) 'A', len, rc, errno2name());
432 }
433
434 static void
435 print_ip_opts(const void *const cmsg_data, const unsigned int data_len)
436 {
437         const unsigned char *const opts = cmsg_data;
438         unsigned int i;
439         for (i = 0; i < data_len; ++i) {
440                 if (i)
441                         printf(", ");
442                 printf("0x%02x", opts[i]);
443         }
444 }
445
446 static void
447 test_ip_opts(struct msghdr *const mh, void *const page,
448              const int cmsg_type, const char *const cmsg_type_str,
449              const unsigned int opts_len)
450 {
451         unsigned int len = CMSG_SPACE(opts_len);
452         struct cmsghdr *cmsg = get_cmsghdr(page, len);
453
454         cmsg->cmsg_len = CMSG_LEN(opts_len);
455         cmsg->cmsg_level = SOL_IP;
456         cmsg->cmsg_type = cmsg_type;
457         unsigned int i;
458         for (i = 0; i < opts_len; ++i)
459                 CMSG_DATA(cmsg)[i] = 'A' + i;
460
461         mh->msg_control = cmsg;
462         mh->msg_controllen = len;
463
464         int rc = sendmsg(-1, mh, 0);
465         printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
466                ", msg_iovlen=0, msg_control=[{cmsg_len=%u"
467                ", cmsg_level=SOL_IP, cmsg_type=%s, cmsg_data=[",
468                (unsigned) cmsg->cmsg_len, cmsg_type_str);
469         print_ip_opts(CMSG_DATA(cmsg), opts_len);
470         printf("]}], msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
471                len, rc, errno2name());
472 }
473
474 #ifdef IP_CHECKSUM
475 struct sock_ee {
476         uint32_t ee_errno;
477         uint8_t  ee_origin;
478         uint8_t  ee_type;
479         uint8_t  ee_code;
480         uint8_t  ee_pad;
481         uint32_t ee_info;
482         uint32_t ee_data;
483         struct sockaddr_in offender;
484 };
485
486 static void
487 test_ip_recverr(struct msghdr *const mh, void *const page,
488                 const int cmsg_type, const char *const cmsg_type_str)
489 {
490         const unsigned int len = CMSG_SPACE(sizeof(struct sock_ee));
491         struct cmsghdr *const cmsg = get_cmsghdr(page, len);
492
493         cmsg->cmsg_len = CMSG_LEN(sizeof(struct sock_ee));
494         cmsg->cmsg_level = SOL_IP;
495         cmsg->cmsg_type = cmsg_type;
496
497         struct sock_ee *const e = (struct sock_ee *) CMSG_DATA(cmsg);
498         e->ee_errno = 0xdeadbeef;
499         e->ee_origin = 2;
500         e->ee_type = 3;
501         e->ee_code = 4;
502         e->ee_info = 0xfacefeed;
503         e->ee_data = 0xbadc0ded;
504         e->offender.sin_family = AF_INET,
505         e->offender.sin_port = htons(12345),
506         e->offender.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
507
508         mh->msg_control = cmsg;
509         mh->msg_controllen = len;
510
511         int rc = sendmsg(-1, mh, 0);
512         printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
513                ", msg_iovlen=0, msg_control=[{cmsg_len=%u, cmsg_level=SOL_IP"
514                ", cmsg_type=%s, cmsg_data={ee_errno=%u, ee_origin=%u"
515                ", ee_type=%u, ee_code=%u, ee_info=%u, ee_data=%u"
516                ", offender={sa_family=AF_INET, sin_port=htons(%hu)"
517                ", sin_addr=inet_addr(\"127.0.0.1\")}}}]"
518                ", msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
519                (unsigned) cmsg->cmsg_len, cmsg_type_str,
520                e->ee_errno, e->ee_origin, e->ee_type,
521                e->ee_code, e->ee_info, e->ee_data,
522                ntohs(e->offender.sin_port),
523                len, rc, errno2name());
524 }
525 #endif
526
527 #ifdef IP_ORIGDSTADDR
528 static void
529 test_ip_origdstaddr(struct msghdr *const mh, void *const page,
530                     const int cmsg_type, const char *const cmsg_type_str)
531 {
532         const unsigned int len = CMSG_SPACE(sizeof(struct sockaddr_in));
533         struct cmsghdr *const cmsg = get_cmsghdr(page, len);
534
535         cmsg->cmsg_len = CMSG_LEN(sizeof(struct sockaddr_in));
536         cmsg->cmsg_level = SOL_IP;
537         cmsg->cmsg_type = cmsg_type;
538
539         struct sockaddr_in *const sin = (struct sockaddr_in *) CMSG_DATA(cmsg);
540         sin->sin_family = AF_INET,
541         sin->sin_port = htons(12345),
542         sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
543
544         mh->msg_control = cmsg;
545         mh->msg_controllen = len;
546
547         int rc = sendmsg(-1, mh, 0);
548         printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
549                ", msg_iovlen=0, msg_control=[{cmsg_len=%u, cmsg_level=SOL_IP"
550                ", cmsg_type=%s, cmsg_data={sa_family=AF_INET"
551                ", sin_port=htons(%hu), sin_addr=inet_addr(\"127.0.0.1\")}}]"
552                ", msg_controllen=%u, msg_flags=0}, 0) = %d %s (%m)\n",
553                (unsigned) cmsg->cmsg_len, cmsg_type_str,
554                ntohs(sin->sin_port), len, rc, errno2name());
555 }
556 #endif
557
558 static void
559 test_sol_ip(struct msghdr *const mh, void *const page)
560 {
561         test_ip_pktinfo(mh, page, VAL_STR(IP_PKTINFO));
562         test_ip_uint(mh, page, VAL_STR(IP_TTL));
563         test_ip_uint8_t(mh, page, VAL_STR(IP_TOS));
564         test_ip_opts(mh, page, VAL_STR(IP_RECVOPTS), 1);
565         test_ip_opts(mh, page, VAL_STR(IP_RECVOPTS), 2);
566         test_ip_opts(mh, page, VAL_STR(IP_RECVOPTS), 3);
567         test_ip_opts(mh, page, VAL_STR(IP_RECVOPTS), 4);
568         test_ip_opts(mh, page, VAL_STR(IP_RETOPTS), 5);
569         test_ip_opts(mh, page, VAL_STR(IP_RETOPTS), 6);
570         test_ip_opts(mh, page, VAL_STR(IP_RETOPTS), 7);
571         test_ip_opts(mh, page, VAL_STR(IP_RETOPTS), 8);
572 #ifdef IP_CHECKSUM
573         test_ip_recverr(mh, page, VAL_STR(IP_RECVERR));
574 #endif
575 #ifdef IP_ORIGDSTADDR
576         test_ip_origdstaddr(mh, page, VAL_STR(IP_ORIGDSTADDR));
577 #endif
578 #ifdef IP_CHECKSUM
579         test_ip_uint(mh, page, VAL_STR(IP_CHECKSUM));
580 #endif
581         test_scm_security(mh, CMSG_LEN(0), page, 0, CMSG_LEN(0),
582                           VAL_STR(SOL_IP));
583         test_unknown_type(mh, page, VAL_STR(SOL_IP), "IP_???");
584 }
585
586 static void
587 test_unknown_level(struct msghdr *const mh, void *const page)
588 {
589         struct cmsghdr *cmsg = get_cmsghdr(page, CMSG_LEN(0));
590
591         cmsg->cmsg_len = CMSG_LEN(0);
592         cmsg->cmsg_level = SOL_TCP;
593         cmsg->cmsg_type = 0xdeadbeef;
594
595         mh->msg_control = cmsg;
596         mh->msg_controllen = cmsg->cmsg_len;
597
598         int rc = sendmsg(-1, mh, 0);
599         printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
600                ", msg_iovlen=0, msg_control=[{cmsg_len=%u, cmsg_level=%s"
601                ", cmsg_type=%#x}], msg_controllen=%u, msg_flags=0}"
602                ", 0) = %d %s (%m)\n",
603                (unsigned) cmsg->cmsg_len, "SOL_TCP", cmsg->cmsg_type,
604                (unsigned) mh->msg_controllen, rc, errno2name());
605 }
606
607 int main(int ac, const char **av)
608 {
609         int rc = sendmsg(-1, 0, 0);
610         printf("sendmsg(-1, NULL, 0) = %d %s (%m)\n", rc, errno2name());
611
612         struct msghdr *mh = tail_alloc(sizeof(*mh));
613         memset(mh, 0, sizeof(*mh));
614
615         rc = sendmsg(-1, mh + 1, 0);
616         printf("sendmsg(-1, %p, 0) = %d %s (%m)\n",
617                mh + 1, rc, errno2name());
618
619         void *page = tail_alloc(1) + 1;
620         mh->msg_control = page;
621         mh->msg_controllen = CMSG_LEN(0);
622         rc = sendmsg(-1, mh, 0);
623         printf("sendmsg(-1, {msg_name=NULL, msg_namelen=0, msg_iov=NULL"
624                ", msg_iovlen=0, msg_control=%p, msg_controllen=%u"
625                ", msg_flags=0}, 0) = %d %s (%m)\n",
626                page, (unsigned) CMSG_LEN(0), rc, errno2name());
627
628         test_sol_socket(mh, page);
629         test_sol_ip(mh, page);
630         test_unknown_level(mh, page);
631
632         puts("+++ exited with 0 +++");
633         return 0;
634 }