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