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