1 /*        $NetBSD: t_sendmmsg.c,v 1.3 2019/03/16 21:46:43 christos Exp $        */
2 
3 /*-
4  * Copyright (c) 2018 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Christos Zoulas.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 #include <sys/cdefs.h>
32 __RCSID("$NetBSD: t_sendmmsg.c,v 1.3 2019/03/16 21:46:43 christos Exp $");
33 
34 #include <atf-c.h>
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <sys/ioctl.h>
38 #include <sys/wait.h>
39 
40 #include <string.h>
41 #include <time.h>
42 #include <stdint.h>
43 #include <errno.h>
44 #include <signal.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <unistd.h>
48 #include <sched.h>
49 
50 #define BUFSIZE     65536
51 
52 #define min(a, b) ((a) < (b) ? (a) : (b))
53 static int debug = 1;
54 static volatile sig_atomic_t rdied;
55 
56 static void
handle_sigchld(__unused int pid)57 handle_sigchld(__unused int pid)
58 {
59 
60           rdied = 1;
61 }
62 
63 ATF_TC(sendmmsg_basic);
ATF_TC_HEAD(sendmmsg_basic,tc)64 ATF_TC_HEAD(sendmmsg_basic, tc)
65 {
66           atf_tc_set_md_var(tc, "descr", "A basic test of sendmmsg(2)");
67 }
68 
69 static void
setsock(int fd,int type)70 setsock(int fd, int type)
71 {
72           int buflen = BUFSIZE;
73           socklen_t socklen = sizeof(buflen);
74 
75           ATF_REQUIRE_MSG(setsockopt(fd, SOL_SOCKET, type,
76               &buflen, socklen) != -1, "%s (%s)",
77               type == SO_RCVBUF ? "rcv" : "snd", strerror(errno));
78 }
79 
ATF_TC_BODY(sendmmsg_basic,tc)80 ATF_TC_BODY(sendmmsg_basic, tc)
81 {
82           int fd[2], error, cnt;
83           uint8_t *buf;
84           struct mmsghdr *mmsghdr;
85           struct iovec *iov;
86           unsigned int mmsgcnt, n;
87           int status;
88           off_t off;
89           uint8_t DGRAM[1316] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, };
90           uint8_t rgram[sizeof(DGRAM)];
91           struct sigaction sa;
92           ssize_t overf = 0;
93 
94           error = socketpair(AF_UNIX, SOCK_DGRAM, 0, fd);
95           ATF_REQUIRE_MSG(error != -1, "socketpair failed (%s)", strerror(errno));
96 
97           buf = malloc(BUFSIZE);
98           ATF_REQUIRE_MSG(buf != NULL, "malloc failed (%s)", strerror(errno));
99 
100           setsock(fd[1], SO_SNDBUF);
101 //        setsock(fd[0], SO_RCVBUF);
102 
103           mmsgcnt = BUFSIZE / sizeof(DGRAM);
104           mmsghdr = calloc(mmsgcnt, sizeof(*mmsghdr));
105           ATF_REQUIRE_MSG(mmsghdr != NULL, "malloc failed (%s)", strerror(errno));
106           iov = malloc(sizeof(*iov) * mmsgcnt);
107           ATF_REQUIRE_MSG(iov != NULL, "malloc failed (%s)", strerror(errno));
108 
109           for (off = 0, n = 0; n < mmsgcnt; n++) {
110                     iov[n].iov_base = buf + off;
111                     memcpy(iov[n].iov_base, DGRAM, sizeof(DGRAM));
112                     *(buf + off) = n;
113                     iov[n].iov_len = sizeof(DGRAM);
114                     off += iov[n].iov_len;
115                     mmsghdr[n].msg_hdr.msg_iov = &iov[n];
116                     mmsghdr[n].msg_hdr.msg_iovlen = 1;
117                     mmsghdr[n].msg_hdr.msg_name = NULL;
118                     mmsghdr[n].msg_hdr.msg_namelen = 0;
119           }
120 
121           memset(&sa, 0, sizeof(sa));
122           sa.sa_flags = SA_RESTART;
123           sa.sa_handler = &handle_sigchld;
124           sigemptyset(&sa.sa_mask);
125           error = sigaction(SIGCHLD, &sa, 0);
126           ATF_REQUIRE_MSG(error != -1, "sigaction failed (%s)",
127               strerror(errno));
128 
129           switch (fork()) {
130           case -1:
131                     ATF_REQUIRE_MSG(0, "fork failed (%s)", strerror(errno));
132                     break;
133           case 0:
134                     sched_yield();
135                     if (debug)
136                         printf("sending %u messages (max %u per syscall)\n", n,
137                               mmsgcnt);
138                     for (n = 0; n < mmsgcnt;) {
139                               if (debug)
140                                         printf("sending packet %u/%u...\n", n,
141                                             mmsgcnt);
142                               // XXX: ENOBUFS bug, on the receive side!!!
143                               // in npkt = min(mmsgsize, mmsgcnt - n);
144                               int npkt = min(3, mmsgcnt - n), a;
145                               do {
146                                         a = 0;
147                                         ATF_REQUIRE(ioctl(fd[1], FIONSPACE, &a) != -1);
148                                         printf("1 %d\n", a);
149                                         ATF_REQUIRE(ioctl(fd[0], FIONSPACE, &a) != -1);
150                                         printf("0 %d\n", a);
151                               } while ((size_t)a < sizeof(DGRAM));
152                               cnt = sendmmsg(fd[1], mmsghdr + n, npkt, 0);
153                               if (cnt == -1 && errno == ENOBUFS) {
154                                         overf++;
155                                         if (debug)
156                                                   printf("send buffer overflowed"
157                                                       " (%zu)\n",overf);
158                                         if (overf > 100)
159                                                   exit(1);
160                                         sched_yield();
161                                         sched_yield();
162                                         sched_yield();
163                                         continue;
164                               }
165                               ATF_REQUIRE_MSG(cnt != -1, "sendmmsg %u failed (%s)",
166                                   n, strerror(errno));
167                               if (debug)
168                                         printf("sendmmsg: sent %u messages\n", cnt);
169                               n += cnt;
170                               sched_yield();
171                               sched_yield();
172                               sched_yield();
173                     }
174                     if (debug)
175                               printf("done!\n");
176                     exit(0);
177                     /*NOTREACHED*/
178           default:
179                     for (n = 0; n < mmsgcnt; n++) {
180                               if (debug)
181                                         printf("receiving packet %u/%u...\n", n,
182                                             mmsgcnt);
183                               do {
184                                         if (rdied)
185                                                   break;
186                                         cnt = recv(fd[0], rgram, sizeof(rgram), 0);
187                                         ATF_REQUIRE_MSG(cnt != -1 || errno != ENOBUFS,
188                                             "recv failed (%s)", strerror(errno));
189                                         ATF_CHECK_EQ_MSG(cnt, sizeof(rgram),
190                                             "packet length");
191                                         ATF_CHECK_EQ_MSG(rgram[0], n,
192                                             "number %u != %u", rgram[0], n);
193                                         ATF_REQUIRE_MSG(memcmp(rgram + 1, DGRAM + 1,
194                                             sizeof(rgram) - 1) == 0, "bad data");
195                               } while (cnt == -1 && errno == ENOBUFS);
196                     }
197                     error = wait(&status);
198                     ATF_REQUIRE_MSG(error != -1, "wait failed (%s)",
199                         strerror(errno));
200                     ATF_REQUIRE_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 0,
201                         "receiver died");
202                     break;
203           }
204 }
205 
ATF_TP_ADD_TCS(tp)206 ATF_TP_ADD_TCS(tp)
207 {
208 
209           ATF_TP_ADD_TC(tp, sendmmsg_basic);
210 
211           return atf_no_error();
212 }
213