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