1 /* $NetBSD: virtif_user.c,v 1.6 2019/03/26 08:56:17 bad Exp $ */
2
3 /*
4 * Copyright (c) 2013 Antti Kantee. All Rights Reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include <sys/cdefs.h>
29 #ifdef __KERNEL_RCSID
30 __KERNEL_RCSID(0, "$NetBSD: virtif_user.c,v 1.6 2019/03/26 08:56:17 bad Exp $");
31 #endif
32
33 #ifndef _KERNEL
34 #include <sys/types.h>
35 #include <sys/ioctl.h>
36 #include <sys/uio.h>
37
38 #include <assert.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <inttypes.h>
42 #include <poll.h>
43 #include <pthread.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
48
49 #ifdef __linux__
50 #include <net/if.h>
51 #include <linux/if_tun.h>
52 #endif
53
54 #include <rump/rumpuser_component.h>
55
56 #include "if_virt.h"
57 #include "virtif_user.h"
58
59 #if VIFHYPER_REVISION != 20140313
60 #error VIFHYPER_REVISION mismatch
61 #endif
62
63 struct virtif_user {
64 struct virtif_sc *viu_virtifsc;
65 int viu_devnum;
66
67 int viu_fd;
68 int viu_pipe[2];
69 pthread_t viu_rcvthr;
70
71 int viu_dying;
72
73 char viu_rcvbuf[9018]; /* jumbo frame max len */
74 };
75
76 static int
opentapdev(int devnum)77 opentapdev(int devnum)
78 {
79 int fd = -1;
80
81 #if defined(__NetBSD__) || defined(__DragonFly__)
82 char tapdev[64];
83
84 snprintf(tapdev, sizeof(tapdev), "/dev/tap%d", devnum);
85 fd = open(tapdev, O_RDWR);
86 if (fd == -1) {
87 fprintf(stderr, "rumpcomp_virtif_create: can't open %s: "
88 "%s\n", tapdev, strerror(errno));
89 }
90
91 #elif defined(__linux__)
92 struct ifreq ifr;
93 char devname[16];
94
95 fd = open("/dev/net/tun", O_RDWR);
96 if (fd == -1) {
97 fprintf(stderr, "rumpcomp_virtif_create: can't open %s: "
98 "%s\n", "/dev/net/tun", strerror(errno));
99 return -1;
100 }
101
102 snprintf(devname, sizeof(devname), "tun%d", devnum);
103 memset(&ifr, 0, sizeof(ifr));
104 ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
105 strncpy(ifr.ifr_name, devname, sizeof(ifr.ifr_name)-1);
106
107 if (ioctl(fd, TUNSETIFF, &ifr) == -1) {
108 close(fd);
109 fd = -1;
110 }
111
112 #else
113 fprintf(stderr, "virtif not supported on this platform\n");
114 #endif
115
116 return fd;
117 }
118
119 static void
closetapdev(struct virtif_user * viu)120 closetapdev(struct virtif_user *viu)
121 {
122
123 close(viu->viu_fd);
124 }
125
126 static void *
rcvthread(void * aaargh)127 rcvthread(void *aaargh)
128 {
129 struct virtif_user *viu = aaargh;
130 struct pollfd pfd[2];
131 struct iovec iov;
132 ssize_t nn = 0;
133 int prv;
134
135 rumpuser_component_kthread();
136
137 pfd[0].fd = viu->viu_fd;
138 pfd[0].events = POLLIN;
139 pfd[1].fd = viu->viu_pipe[0];
140 pfd[1].events = POLLIN;
141
142 while (!viu->viu_dying) {
143 prv = poll(pfd, 2, -1);
144 if (prv == 0)
145 continue;
146 if (prv == -1) {
147 /* XXX */
148 fprintf(stderr, "virt%d: poll error: %d\n",
149 viu->viu_devnum, errno);
150 sleep(1);
151 continue;
152 }
153 if (pfd[1].revents & POLLIN)
154 continue;
155
156 nn = read(viu->viu_fd,
157 viu->viu_rcvbuf, sizeof(viu->viu_rcvbuf));
158 if (nn == -1 && errno == EAGAIN)
159 continue;
160
161 if (nn < 1) {
162 /* XXX */
163 fprintf(stderr, "virt%d: receive failed\n",
164 viu->viu_devnum);
165 sleep(1);
166 continue;
167 }
168 iov.iov_base = viu->viu_rcvbuf;
169 iov.iov_len = nn;
170
171 rumpuser_component_schedule(NULL);
172 VIF_DELIVERPKT(viu->viu_virtifsc, &iov, 1);
173 rumpuser_component_unschedule();
174 }
175
176 assert(viu->viu_dying);
177
178 rumpuser_component_kthread_release();
179 return NULL;
180 }
181
182 int
VIFHYPER_CREATE(const char * devstr,struct virtif_sc * vif_sc,uint8_t * enaddr,struct virtif_user ** viup)183 VIFHYPER_CREATE(const char *devstr, struct virtif_sc *vif_sc, uint8_t *enaddr,
184 struct virtif_user **viup)
185 {
186 struct virtif_user *viu = NULL;
187 void *cookie;
188 int devnum;
189 int rv;
190
191 cookie = rumpuser_component_unschedule();
192
193 /*
194 * Since this interface doesn't do LINKSTR, we know devstr to be
195 * well-formatted.
196 */
197 devnum = atoi(devstr);
198
199 viu = calloc(1, sizeof(*viu));
200 if (viu == NULL) {
201 rv = errno;
202 goto oerr1;
203 }
204 viu->viu_virtifsc = vif_sc;
205
206 viu->viu_fd = opentapdev(devnum);
207 if (viu->viu_fd == -1) {
208 rv = errno;
209 goto oerr2;
210 }
211 viu->viu_devnum = devnum;
212
213 if (pipe(viu->viu_pipe) == -1) {
214 rv = errno;
215 goto oerr3;
216 }
217
218 if ((rv = pthread_create(&viu->viu_rcvthr, NULL, rcvthread, viu)) != 0)
219 goto oerr4;
220
221 rumpuser_component_schedule(cookie);
222 *viup = viu;
223 return 0;
224
225 oerr4:
226 close(viu->viu_pipe[0]);
227 close(viu->viu_pipe[1]);
228 oerr3:
229 closetapdev(viu);
230 oerr2:
231 free(viu);
232 oerr1:
233 rumpuser_component_schedule(cookie);
234 return rumpuser_component_errtrans(rv);
235 }
236
237 void
VIFHYPER_SEND(struct virtif_user * viu,struct iovec * iov,size_t iovlen)238 VIFHYPER_SEND(struct virtif_user *viu,
239 struct iovec *iov, size_t iovlen)
240 {
241 void *cookie = rumpuser_component_unschedule();
242 ssize_t idontcare __attribute__((__unused__));
243
244 /*
245 * no need to check for return value; packets may be dropped
246 *
247 * ... sorry, I spoke too soon. We need to check it because
248 * apparently gcc reinvented const poisoning and it's very
249 * hard to say "thanks, I know I'm not using the result,
250 * but please STFU and let's get on with something useful".
251 * So let's trick gcc into letting us share the compiler
252 * experience.
253 */
254 idontcare = writev(viu->viu_fd, iov, iovlen);
255
256 rumpuser_component_schedule(cookie);
257 }
258
259 int
VIFHYPER_DYING(struct virtif_user * viu)260 VIFHYPER_DYING(struct virtif_user *viu)
261 {
262 void *cookie = rumpuser_component_unschedule();
263
264 viu->viu_dying = 1;
265 if (write(viu->viu_pipe[1],
266 &viu->viu_dying, sizeof(viu->viu_dying)) == -1) {
267 /*
268 * this is here mostly to avoid a compiler warning
269 * about ignoring the return value of write()
270 */
271 fprintf(stderr, "%s: failed to signal thread\n",
272 VIF_STRING(VIFHYPER_DYING));
273 }
274
275 rumpuser_component_schedule(cookie);
276
277 return 0;
278 }
279
280 void
VIFHYPER_DESTROY(struct virtif_user * viu)281 VIFHYPER_DESTROY(struct virtif_user *viu)
282 {
283 void *cookie = rumpuser_component_unschedule();
284
285 pthread_join(viu->viu_rcvthr, NULL);
286 closetapdev(viu);
287 close(viu->viu_pipe[0]);
288 close(viu->viu_pipe[1]);
289 free(viu);
290
291 rumpuser_component_schedule(cookie);
292 }
293 #endif
294