xref: /netbsd-src/sys/rump/net/lib/libvirtif/virtif_user.c (revision deb6f0161a9109e7de9b519dc8dfb9478668dcdd)
1 /*	$NetBSD: virtif_user.c,v 1.3 2014/03/14 10:06:22 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 != 20140313
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 int
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 		/*
263 		 * this is here mostly to avoid a compiler warning
264 		 * about ignoring the return value of write()
265 		 */
266 		fprintf(stderr, "%s: failed to signal thread\n",
267 		    VIF_STRING(VIFHYPER_DYING));
268 	}
269 
270 	rumpuser_component_schedule(cookie);
271 
272 	return 0;
273 }
274 
275 void
276 VIFHYPER_DESTROY(struct virtif_user *viu)
277 {
278 	void *cookie = rumpuser_component_unschedule();
279 
280 	pthread_join(viu->viu_rcvthr, NULL);
281 	closetapdev(viu);
282 	close(viu->viu_pipe[0]);
283 	close(viu->viu_pipe[1]);
284 	free(viu);
285 
286 	rumpuser_component_schedule(cookie);
287 }
288 #endif
289