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