virtif_user.c revision 1.5 1 /* $NetBSD: virtif_user.c,v 1.5 2019/01/27 02:08:50 pgoyette 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 __KERNEL_RCSID(0, "$NetBSD: virtif_user.c,v 1.5 2019/01/27 02:08:50 pgoyette Exp $");
30
31 #ifndef _KERNEL
32 #include <sys/types.h>
33 #include <sys/ioctl.h>
34 #include <sys/uio.h>
35
36 #include <assert.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <inttypes.h>
40 #include <poll.h>
41 #include <pthread.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46
47 #ifdef __linux__
48 #include <net/if.h>
49 #include <linux/if_tun.h>
50 #endif
51
52 #include <rump/rumpuser_component.h>
53
54 #include "if_virt.h"
55 #include "virtif_user.h"
56
57 #if VIFHYPER_REVISION != 20140313
58 #error VIFHYPER_REVISION mismatch
59 #endif
60
61 struct virtif_user {
62 struct virtif_sc *viu_virtifsc;
63 int viu_devnum;
64
65 int viu_fd;
66 int viu_pipe[2];
67 pthread_t viu_rcvthr;
68
69 int viu_dying;
70
71 char viu_rcvbuf[9018]; /* jumbo frame max len */
72 };
73
74 static int
75 opentapdev(int devnum)
76 {
77 int fd = -1;
78
79 #if defined(__NetBSD__) || defined(__DragonFly__)
80 char tapdev[64];
81
82 snprintf(tapdev, sizeof(tapdev), "/dev/tap%d", devnum);
83 fd = open(tapdev, O_RDWR);
84 if (fd == -1) {
85 fprintf(stderr, "rumpcomp_virtif_create: can't open %s: "
86 "%s\n", tapdev, strerror(errno));
87 }
88
89 #elif defined(__linux__)
90 struct ifreq ifr;
91 char devname[16];
92
93 fd = open("/dev/net/tun", O_RDWR);
94 if (fd == -1) {
95 fprintf(stderr, "rumpcomp_virtif_create: can't open %s: "
96 "%s\n", "/dev/net/tun", strerror(errno));
97 return -1;
98 }
99
100 snprintf(devname, sizeof(devname), "tun%d", devnum);
101 memset(&ifr, 0, sizeof(ifr));
102 ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
103 strncpy(ifr.ifr_name, devname, sizeof(ifr.ifr_name)-1);
104
105 if (ioctl(fd, TUNSETIFF, &ifr) == -1) {
106 close(fd);
107 fd = -1;
108 }
109
110 #else
111 fprintf(stderr, "virtif not supported on this platform\n");
112 #endif
113
114 return fd;
115 }
116
117 static void
118 closetapdev(struct virtif_user *viu)
119 {
120
121 close(viu->viu_fd);
122 }
123
124 static void *
125 rcvthread(void *aaargh)
126 {
127 struct virtif_user *viu = aaargh;
128 struct pollfd pfd[2];
129 struct iovec iov;
130 ssize_t nn = 0;
131 int prv;
132
133 rumpuser_component_kthread();
134
135 pfd[0].fd = viu->viu_fd;
136 pfd[0].events = POLLIN;
137 pfd[1].fd = viu->viu_pipe[0];
138 pfd[1].events = POLLIN;
139
140 while (!viu->viu_dying) {
141 prv = poll(pfd, 2, -1);
142 if (prv == 0)
143 continue;
144 if (prv == -1) {
145 /* XXX */
146 fprintf(stderr, "virt%d: poll error: %d\n",
147 viu->viu_devnum, errno);
148 sleep(1);
149 continue;
150 }
151 if (pfd[1].revents & POLLIN)
152 continue;
153
154 nn = read(viu->viu_fd,
155 viu->viu_rcvbuf, sizeof(viu->viu_rcvbuf));
156 if (nn == -1 && errno == EAGAIN)
157 continue;
158
159 if (nn < 1) {
160 /* XXX */
161 fprintf(stderr, "virt%d: receive failed\n",
162 viu->viu_devnum);
163 sleep(1);
164 continue;
165 }
166 iov.iov_base = viu->viu_rcvbuf;
167 iov.iov_len = nn;
168
169 rumpuser_component_schedule(NULL);
170 VIF_DELIVERPKT(viu->viu_virtifsc, &iov, 1);
171 rumpuser_component_unschedule();
172 }
173
174 assert(viu->viu_dying);
175
176 rumpuser_component_kthread_release();
177 return NULL;
178 }
179
180 int
181 VIFHYPER_CREATE(const char *devstr, struct virtif_sc *vif_sc, uint8_t *enaddr,
182 struct virtif_user **viup)
183 {
184 struct virtif_user *viu = NULL;
185 void *cookie;
186 int devnum;
187 int rv;
188
189 cookie = rumpuser_component_unschedule();
190
191 /*
192 * Since this interface doesn't do LINKSTR, we know devstr to be
193 * well-formatted.
194 */
195 devnum = atoi(devstr);
196
197 viu = calloc(1, sizeof(*viu));
198 if (viu == NULL) {
199 rv = errno;
200 goto oerr1;
201 }
202 viu->viu_virtifsc = vif_sc;
203
204 viu->viu_fd = opentapdev(devnum);
205 if (viu->viu_fd == -1) {
206 rv = errno;
207 goto oerr2;
208 }
209 viu->viu_devnum = devnum;
210
211 if (pipe(viu->viu_pipe) == -1) {
212 rv = errno;
213 goto oerr3;
214 }
215
216 if ((rv = pthread_create(&viu->viu_rcvthr, NULL, rcvthread, viu)) != 0)
217 goto oerr4;
218
219 rumpuser_component_schedule(cookie);
220 *viup = viu;
221 return 0;
222
223 oerr4:
224 close(viu->viu_pipe[0]);
225 close(viu->viu_pipe[1]);
226 oerr3:
227 closetapdev(viu);
228 oerr2:
229 free(viu);
230 oerr1:
231 rumpuser_component_schedule(cookie);
232 return rumpuser_component_errtrans(rv);
233 }
234
235 void
236 VIFHYPER_SEND(struct virtif_user *viu,
237 struct iovec *iov, size_t iovlen)
238 {
239 void *cookie = rumpuser_component_unschedule();
240 ssize_t idontcare __attribute__((__unused__));
241
242 /*
243 * no need to check for return value; packets may be dropped
244 *
245 * ... sorry, I spoke too soon. We need to check it because
246 * apparently gcc reinvented const poisoning and it's very
247 * hard to say "thanks, I know I'm not using the result,
248 * but please STFU and let's get on with something useful".
249 * So let's trick gcc into letting us share the compiler
250 * experience.
251 */
252 idontcare = writev(viu->viu_fd, iov, iovlen);
253
254 rumpuser_component_schedule(cookie);
255 }
256
257 int
258 VIFHYPER_DYING(struct virtif_user *viu)
259 {
260 void *cookie = rumpuser_component_unschedule();
261
262 viu->viu_dying = 1;
263 if (write(viu->viu_pipe[1],
264 &viu->viu_dying, sizeof(viu->viu_dying)) == -1) {
265 /*
266 * this is here mostly to avoid a compiler warning
267 * about ignoring the return value of write()
268 */
269 fprintf(stderr, "%s: failed to signal thread\n",
270 VIF_STRING(VIFHYPER_DYING));
271 }
272
273 rumpuser_component_schedule(cookie);
274
275 return 0;
276 }
277
278 void
279 VIFHYPER_DESTROY(struct virtif_user *viu)
280 {
281 void *cookie = rumpuser_component_unschedule();
282
283 pthread_join(viu->viu_rcvthr, NULL);
284 closetapdev(viu);
285 close(viu->viu_pipe[0]);
286 close(viu->viu_pipe[1]);
287 free(viu);
288
289 rumpuser_component_schedule(cookie);
290 }
291 #endif
292