From patchwork Mon Nov 2 22:23:50 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Or Gerlitz X-Patchwork-Id: 57156 Received: from vger.kernel.org (vger.kernel.org [209.132.176.167]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id nA2MQtZG019359 for ; Mon, 2 Nov 2009 22:26:55 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1757216AbZKBW0s (ORCPT ); Mon, 2 Nov 2009 17:26:48 -0500 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1757215AbZKBW0s (ORCPT ); Mon, 2 Nov 2009 17:26:48 -0500 Received: from mx1.redhat.com ([209.132.183.28]:53134 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1757212AbZKBW0s (ORCPT ); Mon, 2 Nov 2009 17:26:48 -0500 Received: from int-mx05.intmail.prod.int.phx2.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.18]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id nA2MQIUa002268; Mon, 2 Nov 2009 17:26:19 -0500 Received: from redhat.com (vpn-6-135.tlv.redhat.com [10.35.6.135]) by int-mx05.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with SMTP id nA2MQENx031027; Mon, 2 Nov 2009 17:26:15 -0500 Date: Tue, 3 Nov 2009 00:23:50 +0200 From: Or Gerlitz To: avi@redhat.com, kvm@vger.kernel.org, virtualization@lists.linux-foundation.org Cc: gregory.haskins@gmail.com Subject: [PATCHv4 3/6] qemu/net: add raw backend Message-ID: <20091102222350.GD15153@redhat.com> References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: User-Agent: Mutt/1.5.19 (2009-01-05) X-Scanned-By: MIMEDefang 2.67 on 10.5.11.18 Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org diff --git a/hw/virtio-net.c b/hw/virtio-net.c index 469c6e3..2e51a6a 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -531,7 +531,8 @@ static ssize_t virtio_net_receive2(VLANClientState *vc, const uint8_t *buf, size virtqueue_pop(n->rx_vq, &elem) == 0) { if (i == 0) return -1; - fprintf(stderr, "virtio-net truncating packet\n"); + fprintf(stderr, "virtio-net truncating packet. offset %zd size %zd\n", + offset, size); exit(1); } diff --git a/net.c b/net.c index 8ac639b..1fb2f2f 100644 --- a/net.c +++ b/net.c @@ -93,6 +93,9 @@ #endif #endif +#include +#include + #if defined(__OpenBSD__) #include #endif @@ -1870,6 +1873,158 @@ static TAPState *net_tap_init(VLANState *vlan, const char *model, #endif /* !_WIN32 */ +typedef struct RAWState { + VLANClientState *vc; + int fd; + uint8_t buf[4096]; + int promisc; +} RAWState; + +static int net_raw_fd_init(Monitor *mon, const char *ifname, int promisc) +{ + int fd, ret; + struct ifreq req; + struct sockaddr_ll lladdr; + + fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (fd < 0) + fprintf(stderr, "packet socket failed\n"); + + memset(&req, 0, sizeof(req)); + strncpy(req.ifr_name, ifname, IFNAMSIZ-1); + ret = ioctl(fd, SIOCGIFINDEX, &req); + if (ret < 0) + fprintf(stderr, "SIOCGIFINDEX failed\n"); + + memset(&lladdr, 0, sizeof(lladdr)); + lladdr.sll_family = AF_PACKET; + lladdr.sll_protocol = htons(ETH_P_ALL); + lladdr.sll_ifindex = req.ifr_ifindex; + ret = bind(fd, (const struct sockaddr *)&lladdr, sizeof(lladdr)); + if (ret < 0) + fprintf(stderr, "bind failed\n"); + + /* set iface to promiscuous mode (packets sent to the VM MAC) */ + if (promisc) { + ret = ioctl(fd, SIOCGIFFLAGS, &req); + if (ret < 0) + perror("SIOCGIFFLAGS failed\n"); + req.ifr_flags |= IFF_PROMISC; + ret = ioctl(fd, SIOCSIFFLAGS, &req); + if (ret < 0) + fprintf(stderr, "SIOCSIFFLAGS to promiscous failed\n"); + } + + ret = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); + if (ret < 0) + fprintf(stderr, "O_NONBLOCK set failed\n"); + + return fd; +} + +static void raw_cleanup(VLANClientState *vc) +{ + struct ifreq req; + RAWState *s = vc->opaque; + + qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); + if (s->promisc) { + ioctl(s->fd, SIOCGIFFLAGS, &req); + req.ifr_flags &= ~IFF_PROMISC; + ioctl(s->fd, SIOCSIFFLAGS, &req); + } + close(s->fd); + qemu_free(s); +} + +static void raw_send(void *opaque); + +static int raw_can_send(void *opaque) +{ + RAWState *s = opaque; + + return qemu_can_send_packet(s->vc); +} + +static void raw_send_completed(VLANClientState *vc, ssize_t len) +{ + RAWState *s = vc->opaque; + + qemu_set_fd_handler2(s->fd, raw_can_send, raw_send, NULL, s); +} + +static void raw_send(void *opaque) +{ + RAWState *s = opaque; + int size; + + do { + size = recv(s->fd, s->buf, sizeof(s->buf), MSG_TRUNC); + if (size <= 0) + break; + + size = qemu_send_packet_async(s->vc, s->buf, size, + raw_send_completed); + if (size == 0) + qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); + + } while (size > 0); +} + +static ssize_t raw_receive_iov(VLANClientState *vc, const struct iovec *iov, + int iovcnt) +{ + ssize_t len; + RAWState *s = vc->opaque; + + do { + len = writev(s->fd, iov, iovcnt); + } while (len == -1 && (errno == EINTR || errno == EAGAIN)); + + return len; +} + +static ssize_t raw_receive(VLANClientState *vc, const uint8_t *buf, size_t size) +{ + struct iovec iov[1]; + + iov[0].iov_base = (char *)buf; + iov[0].iov_len = size; + + return raw_receive_iov(vc, iov, 1); +} + +static int net_raw_init(Monitor *mon, VLANState *vlan, const char *model, + const char *name, const char *ifname, + int promisc, int fd) +{ + RAWState *s; + + s = qemu_mallocz(sizeof(RAWState)); + + if (fd == -1) { + s->fd = net_raw_fd_init(mon, ifname, promisc); + s->promisc = promisc; + } else + s->fd = fd; + + fcntl(s->fd, F_SETFL, O_NONBLOCK); + + s->vc = qemu_new_vlan_client(vlan, model, name, NULL, raw_receive, + raw_receive_iov, raw_cleanup, s); + qemu_set_fd_handler2(s->fd, raw_can_send, raw_send, NULL, s); + + if (fd == -1) + snprintf(s->vc->info_str, sizeof(s->vc->info_str), + "raw: ifname=%s, promisc=%d", ifname, promisc); + else + snprintf(s->vc->info_str, sizeof(s->vc->info_str), + "raw: fd=%d", fd); + + vlan->nb_host_devs++; + return 0; +} + #if defined(CONFIG_VDE) typedef struct VDEState { VLANClientState *vc; @@ -2632,6 +2787,23 @@ static int net_init_nic(QemuOpts *opts, Monitor *mon) return idx; } +static int net_init_raw(QemuOpts *opts, Monitor *mon) +{ + VLANState *vlan; + int fd = -1; + vlan = qemu_find_vlan(qemu_opt_get_number(opts, "vlan", 0), 1); + if (qemu_opt_get(opts, "fd")) { + fd = net_handle_fd_param(mon, qemu_opt_get(opts, "fd")); + if (fd < 0) + return -EINVAL; + } + return net_raw_init(mon, vlan, "raw", + qemu_opt_get(opts, "name"), + qemu_opt_get(opts, "ifname"), + qemu_opt_get_bool(opts, "promisc", 0), + fd); +} + static int net_init_slirp_configs(const char *name, const char *value, void *opaque) { struct slirp_config_str *config; @@ -3136,6 +3308,26 @@ static struct { }, { /* end of list */ } }, + }, { + .type = "raw", + .init = net_init_raw, + .desc = { + NET_COMMON_PARAMS_DESC, + { + .name = "fd", + .type = QEMU_OPT_STRING, + .help = "file descriptor of an already opened raw socket", + }, { + .name = "ifname", + .type = QEMU_OPT_STRING, + .help = "interface name", + }, { + .name = "promisc", + .type = QEMU_OPT_BOOL, + .help = "enable promiscious mode at startup", + }, + { /* end of list */ } + }, #ifdef CONFIG_VDE }, { .type = "vde", diff --git a/qemu-options.hx b/qemu-options.hx index bde3e3f..0d5440f 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -825,6 +825,10 @@ DEF("net", HAS_ARG, QEMU_OPTION_net, " default of 'sndbuf=1048576' can be disabled using 'sndbuf=0'\n" #endif #endif + "-net raw[,vlan=n][,name=str],ifname=name[,promisc=m]\n" + " bound the host network interface to VLAN 'n' in a raw manner:\n" + " packets received on the interface are delivered to the vlan and\n" + " packets delivered on the vlan are sent to the interface\n" "-net socket[,vlan=n][,name=str][,fd=h][,listen=[host]:port][,connect=host:port]\n" " connect the vlan 'n' to another VLAN using a socket connection\n" "-net socket[,vlan=n][,name=str][,fd=h][,mcast=maddr:port]\n"