Annotation of qemu/net/tap.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * QEMU System Emulator
        !             3:  *
        !             4:  * Copyright (c) 2003-2008 Fabrice Bellard
        !             5:  * Copyright (c) 2009 Red Hat, Inc.
        !             6:  *
        !             7:  * Permission is hereby granted, free of charge, to any person obtaining a copy
        !             8:  * of this software and associated documentation files (the "Software"), to deal
        !             9:  * in the Software without restriction, including without limitation the rights
        !            10:  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        !            11:  * copies of the Software, and to permit persons to whom the Software is
        !            12:  * furnished to do so, subject to the following conditions:
        !            13:  *
        !            14:  * The above copyright notice and this permission notice shall be included in
        !            15:  * all copies or substantial portions of the Software.
        !            16:  *
        !            17:  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        !            18:  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        !            19:  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
        !            20:  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        !            21:  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        !            22:  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
        !            23:  * THE SOFTWARE.
        !            24:  */
        !            25: 
        !            26: #include "net/tap.h"
        !            27: 
        !            28: #include "config-host.h"
        !            29: 
        !            30: #include <signal.h>
        !            31: #include <sys/ioctl.h>
        !            32: #include <sys/stat.h>
        !            33: #include <sys/wait.h>
        !            34: #include <sys/socket.h>
        !            35: #include <net/if.h>
        !            36: 
        !            37: #include "net.h"
        !            38: #include "sysemu.h"
        !            39: #include "qemu-char.h"
        !            40: #include "qemu-common.h"
        !            41: 
        !            42: #include "net/tap-linux.h"
        !            43: 
        !            44: /* Maximum GSO packet size (64k) plus plenty of room for
        !            45:  * the ethernet and virtio_net headers
        !            46:  */
        !            47: #define TAP_BUFSIZE (4096 + 65536)
        !            48: 
        !            49: typedef struct TAPState {
        !            50:     VLANClientState nc;
        !            51:     int fd;
        !            52:     char down_script[1024];
        !            53:     char down_script_arg[128];
        !            54:     uint8_t buf[TAP_BUFSIZE];
        !            55:     unsigned int read_poll : 1;
        !            56:     unsigned int write_poll : 1;
        !            57:     unsigned int has_vnet_hdr : 1;
        !            58:     unsigned int using_vnet_hdr : 1;
        !            59:     unsigned int has_ufo: 1;
        !            60: } TAPState;
        !            61: 
        !            62: static int launch_script(const char *setup_script, const char *ifname, int fd);
        !            63: 
        !            64: static int tap_can_send(void *opaque);
        !            65: static void tap_send(void *opaque);
        !            66: static void tap_writable(void *opaque);
        !            67: 
        !            68: static void tap_update_fd_handler(TAPState *s)
        !            69: {
        !            70:     qemu_set_fd_handler2(s->fd,
        !            71:                          s->read_poll  ? tap_can_send : NULL,
        !            72:                          s->read_poll  ? tap_send     : NULL,
        !            73:                          s->write_poll ? tap_writable : NULL,
        !            74:                          s);
        !            75: }
        !            76: 
        !            77: static void tap_read_poll(TAPState *s, int enable)
        !            78: {
        !            79:     s->read_poll = !!enable;
        !            80:     tap_update_fd_handler(s);
        !            81: }
        !            82: 
        !            83: static void tap_write_poll(TAPState *s, int enable)
        !            84: {
        !            85:     s->write_poll = !!enable;
        !            86:     tap_update_fd_handler(s);
        !            87: }
        !            88: 
        !            89: static void tap_writable(void *opaque)
        !            90: {
        !            91:     TAPState *s = opaque;
        !            92: 
        !            93:     tap_write_poll(s, 0);
        !            94: 
        !            95:     qemu_flush_queued_packets(&s->nc);
        !            96: }
        !            97: 
        !            98: static ssize_t tap_write_packet(TAPState *s, const struct iovec *iov, int iovcnt)
        !            99: {
        !           100:     ssize_t len;
        !           101: 
        !           102:     do {
        !           103:         len = writev(s->fd, iov, iovcnt);
        !           104:     } while (len == -1 && errno == EINTR);
        !           105: 
        !           106:     if (len == -1 && errno == EAGAIN) {
        !           107:         tap_write_poll(s, 1);
        !           108:         return 0;
        !           109:     }
        !           110: 
        !           111:     return len;
        !           112: }
        !           113: 
        !           114: static ssize_t tap_receive_iov(VLANClientState *nc, const struct iovec *iov,
        !           115:                                int iovcnt)
        !           116: {
        !           117:     TAPState *s = DO_UPCAST(TAPState, nc, nc);
        !           118:     const struct iovec *iovp = iov;
        !           119:     struct iovec iov_copy[iovcnt + 1];
        !           120:     struct virtio_net_hdr hdr = { 0, };
        !           121: 
        !           122:     if (s->has_vnet_hdr && !s->using_vnet_hdr) {
        !           123:         iov_copy[0].iov_base = &hdr;
        !           124:         iov_copy[0].iov_len =  sizeof(hdr);
        !           125:         memcpy(&iov_copy[1], iov, iovcnt * sizeof(*iov));
        !           126:         iovp = iov_copy;
        !           127:         iovcnt++;
        !           128:     }
        !           129: 
        !           130:     return tap_write_packet(s, iovp, iovcnt);
        !           131: }
        !           132: 
        !           133: static ssize_t tap_receive_raw(VLANClientState *nc, const uint8_t *buf, size_t size)
        !           134: {
        !           135:     TAPState *s = DO_UPCAST(TAPState, nc, nc);
        !           136:     struct iovec iov[2];
        !           137:     int iovcnt = 0;
        !           138:     struct virtio_net_hdr hdr = { 0, };
        !           139: 
        !           140:     if (s->has_vnet_hdr) {
        !           141:         iov[iovcnt].iov_base = &hdr;
        !           142:         iov[iovcnt].iov_len  = sizeof(hdr);
        !           143:         iovcnt++;
        !           144:     }
        !           145: 
        !           146:     iov[iovcnt].iov_base = (char *)buf;
        !           147:     iov[iovcnt].iov_len  = size;
        !           148:     iovcnt++;
        !           149: 
        !           150:     return tap_write_packet(s, iov, iovcnt);
        !           151: }
        !           152: 
        !           153: static ssize_t tap_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
        !           154: {
        !           155:     TAPState *s = DO_UPCAST(TAPState, nc, nc);
        !           156:     struct iovec iov[1];
        !           157: 
        !           158:     if (s->has_vnet_hdr && !s->using_vnet_hdr) {
        !           159:         return tap_receive_raw(nc, buf, size);
        !           160:     }
        !           161: 
        !           162:     iov[0].iov_base = (char *)buf;
        !           163:     iov[0].iov_len  = size;
        !           164: 
        !           165:     return tap_write_packet(s, iov, 1);
        !           166: }
        !           167: 
        !           168: static int tap_can_send(void *opaque)
        !           169: {
        !           170:     TAPState *s = opaque;
        !           171: 
        !           172:     return qemu_can_send_packet(&s->nc);
        !           173: }
        !           174: 
        !           175: #ifndef __sun__
        !           176: ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen)
        !           177: {
        !           178:     return read(tapfd, buf, maxlen);
        !           179: }
        !           180: #endif
        !           181: 
        !           182: static void tap_send_completed(VLANClientState *nc, ssize_t len)
        !           183: {
        !           184:     TAPState *s = DO_UPCAST(TAPState, nc, nc);
        !           185:     tap_read_poll(s, 1);
        !           186: }
        !           187: 
        !           188: static void tap_send(void *opaque)
        !           189: {
        !           190:     TAPState *s = opaque;
        !           191:     int size;
        !           192: 
        !           193:     do {
        !           194:         uint8_t *buf = s->buf;
        !           195: 
        !           196:         size = tap_read_packet(s->fd, s->buf, sizeof(s->buf));
        !           197:         if (size <= 0) {
        !           198:             break;
        !           199:         }
        !           200: 
        !           201:         if (s->has_vnet_hdr && !s->using_vnet_hdr) {
        !           202:             buf  += sizeof(struct virtio_net_hdr);
        !           203:             size -= sizeof(struct virtio_net_hdr);
        !           204:         }
        !           205: 
        !           206:         size = qemu_send_packet_async(&s->nc, buf, size, tap_send_completed);
        !           207:         if (size == 0) {
        !           208:             tap_read_poll(s, 0);
        !           209:         }
        !           210:     } while (size > 0 && qemu_can_send_packet(&s->nc));
        !           211: }
        !           212: 
        !           213: int tap_has_ufo(VLANClientState *nc)
        !           214: {
        !           215:     TAPState *s = DO_UPCAST(TAPState, nc, nc);
        !           216: 
        !           217:     assert(nc->info->type == NET_CLIENT_TYPE_TAP);
        !           218: 
        !           219:     return s->has_ufo;
        !           220: }
        !           221: 
        !           222: int tap_has_vnet_hdr(VLANClientState *nc)
        !           223: {
        !           224:     TAPState *s = DO_UPCAST(TAPState, nc, nc);
        !           225: 
        !           226:     assert(nc->info->type == NET_CLIENT_TYPE_TAP);
        !           227: 
        !           228:     return s->has_vnet_hdr;
        !           229: }
        !           230: 
        !           231: void tap_using_vnet_hdr(VLANClientState *nc, int using_vnet_hdr)
        !           232: {
        !           233:     TAPState *s = DO_UPCAST(TAPState, nc, nc);
        !           234: 
        !           235:     using_vnet_hdr = using_vnet_hdr != 0;
        !           236: 
        !           237:     assert(nc->info->type == NET_CLIENT_TYPE_TAP);
        !           238:     assert(s->has_vnet_hdr == using_vnet_hdr);
        !           239: 
        !           240:     s->using_vnet_hdr = using_vnet_hdr;
        !           241: }
        !           242: 
        !           243: void tap_set_offload(VLANClientState *nc, int csum, int tso4,
        !           244:                      int tso6, int ecn, int ufo)
        !           245: {
        !           246:     TAPState *s = DO_UPCAST(TAPState, nc, nc);
        !           247: 
        !           248:     return tap_fd_set_offload(s->fd, csum, tso4, tso6, ecn, ufo);
        !           249: }
        !           250: 
        !           251: static void tap_cleanup(VLANClientState *nc)
        !           252: {
        !           253:     TAPState *s = DO_UPCAST(TAPState, nc, nc);
        !           254: 
        !           255:     qemu_purge_queued_packets(nc);
        !           256: 
        !           257:     if (s->down_script[0])
        !           258:         launch_script(s->down_script, s->down_script_arg, s->fd);
        !           259: 
        !           260:     tap_read_poll(s, 0);
        !           261:     tap_write_poll(s, 0);
        !           262:     close(s->fd);
        !           263: }
        !           264: 
        !           265: /* fd support */
        !           266: 
        !           267: static NetClientInfo net_tap_info = {
        !           268:     .type = NET_CLIENT_TYPE_TAP,
        !           269:     .size = sizeof(TAPState),
        !           270:     .receive = tap_receive,
        !           271:     .receive_raw = tap_receive_raw,
        !           272:     .receive_iov = tap_receive_iov,
        !           273:     .cleanup = tap_cleanup,
        !           274: };
        !           275: 
        !           276: static TAPState *net_tap_fd_init(VLANState *vlan,
        !           277:                                  const char *model,
        !           278:                                  const char *name,
        !           279:                                  int fd,
        !           280:                                  int vnet_hdr)
        !           281: {
        !           282:     VLANClientState *nc;
        !           283:     TAPState *s;
        !           284: 
        !           285:     nc = qemu_new_net_client(&net_tap_info, vlan, NULL, model, name);
        !           286: 
        !           287:     s = DO_UPCAST(TAPState, nc, nc);
        !           288: 
        !           289:     s->fd = fd;
        !           290:     s->has_vnet_hdr = vnet_hdr != 0;
        !           291:     s->using_vnet_hdr = 0;
        !           292:     s->has_ufo = tap_probe_has_ufo(s->fd);
        !           293:     tap_set_offload(&s->nc, 0, 0, 0, 0, 0);
        !           294:     tap_read_poll(s, 1);
        !           295:     return s;
        !           296: }
        !           297: 
        !           298: static int launch_script(const char *setup_script, const char *ifname, int fd)
        !           299: {
        !           300:     sigset_t oldmask, mask;
        !           301:     int pid, status;
        !           302:     char *args[3];
        !           303:     char **parg;
        !           304: 
        !           305:     sigemptyset(&mask);
        !           306:     sigaddset(&mask, SIGCHLD);
        !           307:     sigprocmask(SIG_BLOCK, &mask, &oldmask);
        !           308: 
        !           309:     /* try to launch network script */
        !           310:     pid = fork();
        !           311:     if (pid == 0) {
        !           312:         int open_max = sysconf(_SC_OPEN_MAX), i;
        !           313: 
        !           314:         for (i = 0; i < open_max; i++) {
        !           315:             if (i != STDIN_FILENO &&
        !           316:                 i != STDOUT_FILENO &&
        !           317:                 i != STDERR_FILENO &&
        !           318:                 i != fd) {
        !           319:                 close(i);
        !           320:             }
        !           321:         }
        !           322:         parg = args;
        !           323:         *parg++ = (char *)setup_script;
        !           324:         *parg++ = (char *)ifname;
        !           325:         *parg++ = NULL;
        !           326:         execv(setup_script, args);
        !           327:         _exit(1);
        !           328:     } else if (pid > 0) {
        !           329:         while (waitpid(pid, &status, 0) != pid) {
        !           330:             /* loop */
        !           331:         }
        !           332:         sigprocmask(SIG_SETMASK, &oldmask, NULL);
        !           333: 
        !           334:         if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
        !           335:             return 0;
        !           336:         }
        !           337:     }
        !           338:     fprintf(stderr, "%s: could not launch network script\n", setup_script);
        !           339:     return -1;
        !           340: }
        !           341: 
        !           342: static int net_tap_init(QemuOpts *opts, int *vnet_hdr)
        !           343: {
        !           344:     int fd, vnet_hdr_required;
        !           345:     char ifname[128] = {0,};
        !           346:     const char *setup_script;
        !           347: 
        !           348:     if (qemu_opt_get(opts, "ifname")) {
        !           349:         pstrcpy(ifname, sizeof(ifname), qemu_opt_get(opts, "ifname"));
        !           350:     }
        !           351: 
        !           352:     *vnet_hdr = qemu_opt_get_bool(opts, "vnet_hdr", 1);
        !           353:     if (qemu_opt_get(opts, "vnet_hdr")) {
        !           354:         vnet_hdr_required = *vnet_hdr;
        !           355:     } else {
        !           356:         vnet_hdr_required = 0;
        !           357:     }
        !           358: 
        !           359:     TFR(fd = tap_open(ifname, sizeof(ifname), vnet_hdr, vnet_hdr_required));
        !           360:     if (fd < 0) {
        !           361:         return -1;
        !           362:     }
        !           363: 
        !           364:     setup_script = qemu_opt_get(opts, "script");
        !           365:     if (setup_script &&
        !           366:         setup_script[0] != '\0' &&
        !           367:         strcmp(setup_script, "no") != 0 &&
        !           368:         launch_script(setup_script, ifname, fd)) {
        !           369:         close(fd);
        !           370:         return -1;
        !           371:     }
        !           372: 
        !           373:     qemu_opt_set(opts, "ifname", ifname);
        !           374: 
        !           375:     return fd;
        !           376: }
        !           377: 
        !           378: int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan)
        !           379: {
        !           380:     TAPState *s;
        !           381:     int fd, vnet_hdr = 0;
        !           382: 
        !           383:     if (qemu_opt_get(opts, "fd")) {
        !           384:         if (qemu_opt_get(opts, "ifname") ||
        !           385:             qemu_opt_get(opts, "script") ||
        !           386:             qemu_opt_get(opts, "downscript") ||
        !           387:             qemu_opt_get(opts, "vnet_hdr")) {
        !           388:             qemu_error("ifname=, script=, downscript= and vnet_hdr= is invalid with fd=\n");
        !           389:             return -1;
        !           390:         }
        !           391: 
        !           392:         fd = net_handle_fd_param(mon, qemu_opt_get(opts, "fd"));
        !           393:         if (fd == -1) {
        !           394:             return -1;
        !           395:         }
        !           396: 
        !           397:         fcntl(fd, F_SETFL, O_NONBLOCK);
        !           398: 
        !           399:         vnet_hdr = tap_probe_vnet_hdr(fd);
        !           400:     } else {
        !           401:         if (!qemu_opt_get(opts, "script")) {
        !           402:             qemu_opt_set(opts, "script", DEFAULT_NETWORK_SCRIPT);
        !           403:         }
        !           404: 
        !           405:         if (!qemu_opt_get(opts, "downscript")) {
        !           406:             qemu_opt_set(opts, "downscript", DEFAULT_NETWORK_DOWN_SCRIPT);
        !           407:         }
        !           408: 
        !           409:         fd = net_tap_init(opts, &vnet_hdr);
        !           410:         if (fd == -1) {
        !           411:             return -1;
        !           412:         }
        !           413:     }
        !           414: 
        !           415:     s = net_tap_fd_init(vlan, "tap", name, fd, vnet_hdr);
        !           416:     if (!s) {
        !           417:         close(fd);
        !           418:         return -1;
        !           419:     }
        !           420: 
        !           421:     if (tap_set_sndbuf(s->fd, opts) < 0) {
        !           422:         return -1;
        !           423:     }
        !           424: 
        !           425:     if (qemu_opt_get(opts, "fd")) {
        !           426:         snprintf(s->nc.info_str, sizeof(s->nc.info_str), "fd=%d", fd);
        !           427:     } else {
        !           428:         const char *ifname, *script, *downscript;
        !           429: 
        !           430:         ifname     = qemu_opt_get(opts, "ifname");
        !           431:         script     = qemu_opt_get(opts, "script");
        !           432:         downscript = qemu_opt_get(opts, "downscript");
        !           433: 
        !           434:         snprintf(s->nc.info_str, sizeof(s->nc.info_str),
        !           435:                  "ifname=%s,script=%s,downscript=%s",
        !           436:                  ifname, script, downscript);
        !           437: 
        !           438:         if (strcmp(downscript, "no") != 0) {
        !           439:             snprintf(s->down_script, sizeof(s->down_script), "%s", downscript);
        !           440:             snprintf(s->down_script_arg, sizeof(s->down_script_arg), "%s", ifname);
        !           441:         }
        !           442:     }
        !           443: 
        !           444:     if (vlan) {
        !           445:         vlan->nb_host_devs++;
        !           446:     }
        !           447: 
        !           448:     return 0;
        !           449: }

unix.superglobalmegacorp.com