]> granicus.if.org Git - strace/blob - tests/msg_control.c
tests: use ARG_STR in msg_control.test
[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 <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 }