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