xref: /netbsd-src/sys/rump/net/lib/libvirtif/virtif_user.c (revision a0698ed9d41653d7a2378819ad501a285ca0d401)
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