Home | History | Annotate | Line # | Download | only in libvirtif
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