Home | History | Annotate | Line # | Download | only in libvirtif
      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
     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
    120 closetapdev(struct virtif_user *viu)
    121 {
    122 
    123 	close(viu->viu_fd);
    124 }
    125 
    126 static void *
    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
    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
    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
    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
    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