diff options
author | Robert Mustacchi <rm@joyent.com> | 2011-06-24 13:49:54 -0700 |
---|---|---|
committer | Robert Mustacchi <rm@joyent.com> | 2011-06-24 13:49:54 -0700 |
commit | 68396ea9c0fe4f75ce30b1eba2c44c43c13344bb (patch) | |
tree | 802587d411d9db461e6500c5b635043315f81c27 /net | |
download | illumos-kvm-cmd-68396ea9c0fe4f75ce30b1eba2c44c43c13344bb.tar.gz |
Initial commit of d32e8d0b8d9e0ef7cf7ab2e74548982972789dfc from qemu-kvm
Diffstat (limited to 'net')
-rw-r--r-- | net/checksum.c | 85 | ||||
-rw-r--r-- | net/checksum.h | 29 | ||||
-rw-r--r-- | net/dump.c | 159 | ||||
-rw-r--r-- | net/dump.h | 33 | ||||
-rw-r--r-- | net/queue.c | 260 | ||||
-rw-r--r-- | net/queue.h | 71 | ||||
-rw-r--r-- | net/slirp.c | 771 | ||||
-rw-r--r-- | net/slirp.h | 51 | ||||
-rw-r--r-- | net/socket.c | 600 | ||||
-rw-r--r-- | net/socket.h | 33 | ||||
-rw-r--r-- | net/tap-aix.c | 61 | ||||
-rw-r--r-- | net/tap-bsd.c | 131 | ||||
-rw-r--r-- | net/tap-haiku.c | 61 | ||||
-rw-r--r-- | net/tap-linux.c | 195 | ||||
-rw-r--r-- | net/tap-linux.h | 63 | ||||
-rw-r--r-- | net/tap-solaris.c | 227 | ||||
-rw-r--r-- | net/tap-win32.c | 751 | ||||
-rw-r--r-- | net/tap.c | 525 | ||||
-rw-r--r-- | net/tap.h | 60 | ||||
-rw-r--r-- | net/util.c | 60 | ||||
-rw-r--r-- | net/util.h | 32 | ||||
-rw-r--r-- | net/vde.c | 131 | ||||
-rw-r--r-- | net/vde.h | 36 |
23 files changed, 4425 insertions, 0 deletions
diff --git a/net/checksum.c b/net/checksum.c new file mode 100644 index 0000000..4046932 --- /dev/null +++ b/net/checksum.c @@ -0,0 +1,85 @@ +/* + * IP checksumming functions. + * (c) 2008 Gerd Hoffmann <kraxel@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "net/checksum.h" + +#define PROTO_TCP 6 +#define PROTO_UDP 17 + +uint32_t net_checksum_add(int len, uint8_t *buf) +{ + uint32_t sum = 0; + int i; + + for (i = 0; i < len; i++) { + if (i & 1) + sum += (uint32_t)buf[i]; + else + sum += (uint32_t)buf[i] << 8; + } + return sum; +} + +uint16_t net_checksum_finish(uint32_t sum) +{ + while (sum>>16) + sum = (sum & 0xFFFF)+(sum >> 16); + return ~sum; +} + +uint16_t net_checksum_tcpudp(uint16_t length, uint16_t proto, + uint8_t *addrs, uint8_t *buf) +{ + uint32_t sum = 0; + + sum += net_checksum_add(length, buf); // payload + sum += net_checksum_add(8, addrs); // src + dst address + sum += proto + length; // protocol & length + return net_checksum_finish(sum); +} + +void net_checksum_calculate(uint8_t *data, int length) +{ + int hlen, plen, proto, csum_offset; + uint16_t csum; + + if ((data[14] & 0xf0) != 0x40) + return; /* not IPv4 */ + hlen = (data[14] & 0x0f) * 4; + plen = (data[16] << 8 | data[17]) - hlen; + proto = data[23]; + + switch (proto) { + case PROTO_TCP: + csum_offset = 16; + break; + case PROTO_UDP: + csum_offset = 6; + break; + default: + return; + } + + if (plen < csum_offset+2) + return; + + data[14+hlen+csum_offset] = 0; + data[14+hlen+csum_offset+1] = 0; + csum = net_checksum_tcpudp(plen, proto, data+14+12, data+14+hlen); + data[14+hlen+csum_offset] = csum >> 8; + data[14+hlen+csum_offset+1] = csum & 0xff; +} diff --git a/net/checksum.h b/net/checksum.h new file mode 100644 index 0000000..1f05298 --- /dev/null +++ b/net/checksum.h @@ -0,0 +1,29 @@ +/* + * IP checksumming functions. + * (c) 2008 Gerd Hoffmann <kraxel@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef QEMU_NET_CHECKSUM_H +#define QEMU_NET_CHECKSUM_H + +#include <stdint.h> + +uint32_t net_checksum_add(int len, uint8_t *buf); +uint16_t net_checksum_finish(uint32_t sum); +uint16_t net_checksum_tcpudp(uint16_t length, uint16_t proto, + uint8_t *addrs, uint8_t *buf); +void net_checksum_calculate(uint8_t *data, int length); + +#endif /* QEMU_NET_CHECKSUM_H */ diff --git a/net/dump.c b/net/dump.c new file mode 100644 index 0000000..6db7ecf --- /dev/null +++ b/net/dump.c @@ -0,0 +1,159 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "dump.h" +#include "qemu-common.h" +#include "sysemu.h" +#include "qemu-error.h" +#include "qemu-log.h" + +typedef struct DumpState { + VLANClientState nc; + int fd; + int pcap_caplen; +} DumpState; + +#define PCAP_MAGIC 0xa1b2c3d4 + +struct pcap_file_hdr { + uint32_t magic; + uint16_t version_major; + uint16_t version_minor; + int32_t thiszone; + uint32_t sigfigs; + uint32_t snaplen; + uint32_t linktype; +}; + +struct pcap_sf_pkthdr { + struct { + int32_t tv_sec; + int32_t tv_usec; + } ts; + uint32_t caplen; + uint32_t len; +}; + +static ssize_t dump_receive(VLANClientState *nc, const uint8_t *buf, size_t size) +{ + DumpState *s = DO_UPCAST(DumpState, nc, nc); + struct pcap_sf_pkthdr hdr; + int64_t ts; + int caplen; + + /* Early return in case of previous error. */ + if (s->fd < 0) { + return size; + } + + ts = muldiv64(qemu_get_clock(vm_clock), 1000000, get_ticks_per_sec()); + caplen = size > s->pcap_caplen ? s->pcap_caplen : size; + + hdr.ts.tv_sec = ts / 1000000; + hdr.ts.tv_usec = ts % 1000000; + hdr.caplen = caplen; + hdr.len = size; + if (write(s->fd, &hdr, sizeof(hdr)) != sizeof(hdr) || + write(s->fd, buf, caplen) != caplen) { + qemu_log("-net dump write error - stop dump\n"); + close(s->fd); + s->fd = -1; + } + + return size; +} + +static void dump_cleanup(VLANClientState *nc) +{ + DumpState *s = DO_UPCAST(DumpState, nc, nc); + + close(s->fd); +} + +static NetClientInfo net_dump_info = { + .type = NET_CLIENT_TYPE_DUMP, + .size = sizeof(DumpState), + .receive = dump_receive, + .cleanup = dump_cleanup, +}; + +static int net_dump_init(VLANState *vlan, const char *device, + const char *name, const char *filename, int len) +{ + struct pcap_file_hdr hdr; + VLANClientState *nc; + DumpState *s; + int fd; + + fd = open(filename, O_CREAT | O_WRONLY | O_BINARY, 0644); + if (fd < 0) { + error_report("-net dump: can't open %s", filename); + return -1; + } + + hdr.magic = PCAP_MAGIC; + hdr.version_major = 2; + hdr.version_minor = 4; + hdr.thiszone = 0; + hdr.sigfigs = 0; + hdr.snaplen = len; + hdr.linktype = 1; + + if (write(fd, &hdr, sizeof(hdr)) < sizeof(hdr)) { + error_report("-net dump write error: %s", strerror(errno)); + close(fd); + return -1; + } + + nc = qemu_new_net_client(&net_dump_info, vlan, NULL, device, name); + + snprintf(nc->info_str, sizeof(nc->info_str), + "dump to %s (len=%d)", filename, len); + + s = DO_UPCAST(DumpState, nc, nc); + + s->fd = fd; + s->pcap_caplen = len; + + return 0; +} + +int net_init_dump(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan) +{ + int len; + const char *file; + char def_file[128]; + + assert(vlan); + + file = qemu_opt_get(opts, "file"); + if (!file) { + snprintf(def_file, sizeof(def_file), "qemu-vlan%d.pcap", vlan->id); + file = def_file; + } + + len = qemu_opt_get_size(opts, "len", 65536); + + return net_dump_init(vlan, "dump", name, file, len); +} diff --git a/net/dump.h b/net/dump.h new file mode 100644 index 0000000..fdc91ad --- /dev/null +++ b/net/dump.h @@ -0,0 +1,33 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef QEMU_NET_DUMP_H +#define QEMU_NET_DUMP_H + +#include "net.h" +#include "qemu-common.h" + +int net_init_dump(QemuOpts *opts, Monitor *mon, + const char *name, VLANState *vlan); + +#endif /* QEMU_NET_DUMP_H */ diff --git a/net/queue.c b/net/queue.c new file mode 100644 index 0000000..2ea6cd0 --- /dev/null +++ b/net/queue.c @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2009 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "net/queue.h" +#include "qemu-queue.h" + +/* The delivery handler may only return zero if it will call + * qemu_net_queue_flush() when it determines that it is once again able + * to deliver packets. It must also call qemu_net_queue_purge() in its + * cleanup path. + * + * If a sent callback is provided to send(), the caller must handle a + * zero return from the delivery handler by not sending any more packets + * until we have invoked the callback. Only in that case will we queue + * the packet. + * + * If a sent callback isn't provided, we just drop the packet to avoid + * unbounded queueing. + */ + +struct NetPacket { + QTAILQ_ENTRY(NetPacket) entry; + VLANClientState *sender; + unsigned flags; + int size; + NetPacketSent *sent_cb; + uint8_t data[0]; +}; + +struct NetQueue { + NetPacketDeliver *deliver; + NetPacketDeliverIOV *deliver_iov; + void *opaque; + + QTAILQ_HEAD(packets, NetPacket) packets; + + unsigned delivering : 1; +}; + +NetQueue *qemu_new_net_queue(NetPacketDeliver *deliver, + NetPacketDeliverIOV *deliver_iov, + void *opaque) +{ + NetQueue *queue; + + queue = qemu_mallocz(sizeof(NetQueue)); + + queue->deliver = deliver; + queue->deliver_iov = deliver_iov; + queue->opaque = opaque; + + QTAILQ_INIT(&queue->packets); + + queue->delivering = 0; + + return queue; +} + +void qemu_del_net_queue(NetQueue *queue) +{ + NetPacket *packet, *next; + + QTAILQ_FOREACH_SAFE(packet, &queue->packets, entry, next) { + QTAILQ_REMOVE(&queue->packets, packet, entry); + qemu_free(packet); + } + + qemu_free(queue); +} + +static ssize_t qemu_net_queue_append(NetQueue *queue, + VLANClientState *sender, + unsigned flags, + const uint8_t *buf, + size_t size, + NetPacketSent *sent_cb) +{ + NetPacket *packet; + + packet = qemu_malloc(sizeof(NetPacket) + size); + packet->sender = sender; + packet->flags = flags; + packet->size = size; + packet->sent_cb = sent_cb; + memcpy(packet->data, buf, size); + + QTAILQ_INSERT_TAIL(&queue->packets, packet, entry); + + return size; +} + +static ssize_t qemu_net_queue_append_iov(NetQueue *queue, + VLANClientState *sender, + unsigned flags, + const struct iovec *iov, + int iovcnt, + NetPacketSent *sent_cb) +{ + NetPacket *packet; + size_t max_len = 0; + int i; + + for (i = 0; i < iovcnt; i++) { + max_len += iov[i].iov_len; + } + + packet = qemu_malloc(sizeof(NetPacket) + max_len); + packet->sender = sender; + packet->sent_cb = sent_cb; + packet->flags = flags; + packet->size = 0; + + for (i = 0; i < iovcnt; i++) { + size_t len = iov[i].iov_len; + + memcpy(packet->data + packet->size, iov[i].iov_base, len); + packet->size += len; + } + + QTAILQ_INSERT_TAIL(&queue->packets, packet, entry); + + return packet->size; +} + +static ssize_t qemu_net_queue_deliver(NetQueue *queue, + VLANClientState *sender, + unsigned flags, + const uint8_t *data, + size_t size) +{ + ssize_t ret = -1; + + queue->delivering = 1; + ret = queue->deliver(sender, flags, data, size, queue->opaque); + queue->delivering = 0; + + return ret; +} + +static ssize_t qemu_net_queue_deliver_iov(NetQueue *queue, + VLANClientState *sender, + unsigned flags, + const struct iovec *iov, + int iovcnt) +{ + ssize_t ret = -1; + + queue->delivering = 1; + ret = queue->deliver_iov(sender, flags, iov, iovcnt, queue->opaque); + queue->delivering = 0; + + return ret; +} + +ssize_t qemu_net_queue_send(NetQueue *queue, + VLANClientState *sender, + unsigned flags, + const uint8_t *data, + size_t size, + NetPacketSent *sent_cb) +{ + ssize_t ret; + + if (queue->delivering) { + return qemu_net_queue_append(queue, sender, flags, data, size, NULL); + } + + ret = qemu_net_queue_deliver(queue, sender, flags, data, size); + if (ret == 0) { + qemu_net_queue_append(queue, sender, flags, data, size, sent_cb); + return 0; + } + + qemu_net_queue_flush(queue); + + return ret; +} + +ssize_t qemu_net_queue_send_iov(NetQueue *queue, + VLANClientState *sender, + unsigned flags, + const struct iovec *iov, + int iovcnt, + NetPacketSent *sent_cb) +{ + ssize_t ret; + + if (queue->delivering) { + return qemu_net_queue_append_iov(queue, sender, flags, iov, iovcnt, NULL); + } + + ret = qemu_net_queue_deliver_iov(queue, sender, flags, iov, iovcnt); + if (ret == 0) { + qemu_net_queue_append_iov(queue, sender, flags, iov, iovcnt, sent_cb); + return 0; + } + + qemu_net_queue_flush(queue); + + return ret; +} + +void qemu_net_queue_purge(NetQueue *queue, VLANClientState *from) +{ + NetPacket *packet, *next; + + QTAILQ_FOREACH_SAFE(packet, &queue->packets, entry, next) { + if (packet->sender == from) { + QTAILQ_REMOVE(&queue->packets, packet, entry); + qemu_free(packet); + } + } +} + +void qemu_net_queue_flush(NetQueue *queue) +{ + while (!QTAILQ_EMPTY(&queue->packets)) { + NetPacket *packet; + int ret; + + packet = QTAILQ_FIRST(&queue->packets); + QTAILQ_REMOVE(&queue->packets, packet, entry); + + ret = qemu_net_queue_deliver(queue, + packet->sender, + packet->flags, + packet->data, + packet->size); + if (ret == 0) { + QTAILQ_INSERT_HEAD(&queue->packets, packet, entry); + break; + } + + if (packet->sent_cb) { + packet->sent_cb(packet->sender, ret); + } + + qemu_free(packet); + } +} diff --git a/net/queue.h b/net/queue.h new file mode 100644 index 0000000..a31958e --- /dev/null +++ b/net/queue.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2009 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef QEMU_NET_QUEUE_H +#define QEMU_NET_QUEUE_H + +#include "qemu-common.h" + +typedef struct NetPacket NetPacket; +typedef struct NetQueue NetQueue; + +typedef void (NetPacketSent) (VLANClientState *sender, ssize_t ret); + +typedef ssize_t (NetPacketDeliver) (VLANClientState *sender, + unsigned flags, + const uint8_t *buf, + size_t size, + void *opaque); + +typedef ssize_t (NetPacketDeliverIOV) (VLANClientState *sender, + unsigned flags, + const struct iovec *iov, + int iovcnt, + void *opaque); + +#define QEMU_NET_PACKET_FLAG_NONE 0 +#define QEMU_NET_PACKET_FLAG_RAW (1<<0) + +NetQueue *qemu_new_net_queue(NetPacketDeliver *deliver, + NetPacketDeliverIOV *deliver_iov, + void *opaque); +void qemu_del_net_queue(NetQueue *queue); + +ssize_t qemu_net_queue_send(NetQueue *queue, + VLANClientState *sender, + unsigned flags, + const uint8_t *data, + size_t size, + NetPacketSent *sent_cb); + +ssize_t qemu_net_queue_send_iov(NetQueue *queue, + VLANClientState *sender, + unsigned flags, + const struct iovec *iov, + int iovcnt, + NetPacketSent *sent_cb); + +void qemu_net_queue_purge(NetQueue *queue, VLANClientState *from); +void qemu_net_queue_flush(NetQueue *queue); + +#endif /* QEMU_NET_QUEUE_H */ diff --git a/net/slirp.c b/net/slirp.c new file mode 100644 index 0000000..b41c60a --- /dev/null +++ b/net/slirp.c @@ -0,0 +1,771 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "net/slirp.h" + +#include "config-host.h" + +#ifndef _WIN32 +#include <sys/wait.h> +#endif +#include "net.h" +#include "monitor.h" +#include "sysemu.h" +#include "qemu_socket.h" +#include "slirp/libslirp.h" + +static int get_str_sep(char *buf, int buf_size, const char **pp, int sep) +{ + const char *p, *p1; + int len; + p = *pp; + p1 = strchr(p, sep); + if (!p1) + return -1; + len = p1 - p; + p1++; + if (buf_size > 0) { + if (len > buf_size - 1) + len = buf_size - 1; + memcpy(buf, p, len); + buf[len] = '\0'; + } + *pp = p1; + return 0; +} + +/* slirp network adapter */ + +#define SLIRP_CFG_HOSTFWD 1 +#define SLIRP_CFG_LEGACY 2 + +struct slirp_config_str { + struct slirp_config_str *next; + int flags; + char str[1024]; + int legacy_format; +}; + +typedef struct SlirpState { + VLANClientState nc; + QTAILQ_ENTRY(SlirpState) entry; + Slirp *slirp; +#ifndef _WIN32 + char smb_dir[128]; +#endif +} SlirpState; + +static struct slirp_config_str *slirp_configs; +const char *legacy_tftp_prefix; +const char *legacy_bootp_filename; +static QTAILQ_HEAD(slirp_stacks, SlirpState) slirp_stacks = + QTAILQ_HEAD_INITIALIZER(slirp_stacks); + +static int slirp_hostfwd(SlirpState *s, const char *redir_str, + int legacy_format); +static int slirp_guestfwd(SlirpState *s, const char *config_str, + int legacy_format); + +#ifndef _WIN32 +static const char *legacy_smb_export; + +static int slirp_smb(SlirpState *s, const char *exported_dir, + struct in_addr vserver_addr); +static void slirp_smb_cleanup(SlirpState *s); +#else +static inline void slirp_smb_cleanup(SlirpState *s) { } +#endif + +int slirp_can_output(void *opaque) +{ + SlirpState *s = opaque; + + return qemu_can_send_packet(&s->nc); +} + +void slirp_output(void *opaque, const uint8_t *pkt, int pkt_len) +{ + SlirpState *s = opaque; + + qemu_send_packet(&s->nc, pkt, pkt_len); +} + +static ssize_t net_slirp_receive(VLANClientState *nc, const uint8_t *buf, size_t size) +{ + SlirpState *s = DO_UPCAST(SlirpState, nc, nc); + + slirp_input(s->slirp, buf, size); + + return size; +} + +static void net_slirp_cleanup(VLANClientState *nc) +{ + SlirpState *s = DO_UPCAST(SlirpState, nc, nc); + + slirp_cleanup(s->slirp); + slirp_smb_cleanup(s); + QTAILQ_REMOVE(&slirp_stacks, s, entry); +} + +static NetClientInfo net_slirp_info = { + .type = NET_CLIENT_TYPE_SLIRP, + .size = sizeof(SlirpState), + .receive = net_slirp_receive, + .cleanup = net_slirp_cleanup, +}; + +static int net_slirp_init(VLANState *vlan, const char *model, + const char *name, int restricted, + const char *vnetwork, const char *vhost, + const char *vhostname, const char *tftp_export, + const char *bootfile, const char *vdhcp_start, + const char *vnameserver, const char *smb_export, + const char *vsmbserver) +{ + /* default settings according to historic slirp */ + struct in_addr net = { .s_addr = htonl(0x0a000200) }; /* 10.0.2.0 */ + struct in_addr mask = { .s_addr = htonl(0xffffff00) }; /* 255.255.255.0 */ + struct in_addr host = { .s_addr = htonl(0x0a000202) }; /* 10.0.2.2 */ + struct in_addr dhcp = { .s_addr = htonl(0x0a00020f) }; /* 10.0.2.15 */ + struct in_addr dns = { .s_addr = htonl(0x0a000203) }; /* 10.0.2.3 */ +#ifndef _WIN32 + struct in_addr smbsrv = { .s_addr = 0 }; +#endif + VLANClientState *nc; + SlirpState *s; + char buf[20]; + uint32_t addr; + int shift; + char *end; + struct slirp_config_str *config; + + if (!tftp_export) { + tftp_export = legacy_tftp_prefix; + } + if (!bootfile) { + bootfile = legacy_bootp_filename; + } + + if (vnetwork) { + if (get_str_sep(buf, sizeof(buf), &vnetwork, '/') < 0) { + if (!inet_aton(vnetwork, &net)) { + return -1; + } + addr = ntohl(net.s_addr); + if (!(addr & 0x80000000)) { + mask.s_addr = htonl(0xff000000); /* class A */ + } else if ((addr & 0xfff00000) == 0xac100000) { + mask.s_addr = htonl(0xfff00000); /* priv. 172.16.0.0/12 */ + } else if ((addr & 0xc0000000) == 0x80000000) { + mask.s_addr = htonl(0xffff0000); /* class B */ + } else if ((addr & 0xffff0000) == 0xc0a80000) { + mask.s_addr = htonl(0xffff0000); /* priv. 192.168.0.0/16 */ + } else if ((addr & 0xffff0000) == 0xc6120000) { + mask.s_addr = htonl(0xfffe0000); /* tests 198.18.0.0/15 */ + } else if ((addr & 0xe0000000) == 0xe0000000) { + mask.s_addr = htonl(0xffffff00); /* class C */ + } else { + mask.s_addr = htonl(0xfffffff0); /* multicast/reserved */ + } + } else { + if (!inet_aton(buf, &net)) { + return -1; + } + shift = strtol(vnetwork, &end, 10); + if (*end != '\0') { + if (!inet_aton(vnetwork, &mask)) { + return -1; + } + } else if (shift < 4 || shift > 32) { + return -1; + } else { + mask.s_addr = htonl(0xffffffff << (32 - shift)); + } + } + net.s_addr &= mask.s_addr; + host.s_addr = net.s_addr | (htonl(0x0202) & ~mask.s_addr); + dhcp.s_addr = net.s_addr | (htonl(0x020f) & ~mask.s_addr); + dns.s_addr = net.s_addr | (htonl(0x0203) & ~mask.s_addr); + } + + if (vhost && !inet_aton(vhost, &host)) { + return -1; + } + if ((host.s_addr & mask.s_addr) != net.s_addr) { + return -1; + } + + if (vdhcp_start && !inet_aton(vdhcp_start, &dhcp)) { + return -1; + } + if ((dhcp.s_addr & mask.s_addr) != net.s_addr || + dhcp.s_addr == host.s_addr || dhcp.s_addr == dns.s_addr) { + return -1; + } + + if (vnameserver && !inet_aton(vnameserver, &dns)) { + return -1; + } + if ((dns.s_addr & mask.s_addr) != net.s_addr || + dns.s_addr == host.s_addr) { + return -1; + } + +#ifndef _WIN32 + if (vsmbserver && !inet_aton(vsmbserver, &smbsrv)) { + return -1; + } +#endif + + nc = qemu_new_net_client(&net_slirp_info, vlan, NULL, model, name); + + snprintf(nc->info_str, sizeof(nc->info_str), + "net=%s, restricted=%c", inet_ntoa(net), restricted ? 'y' : 'n'); + + s = DO_UPCAST(SlirpState, nc, nc); + + s->slirp = slirp_init(restricted, net, mask, host, vhostname, + tftp_export, bootfile, dhcp, dns, s); + QTAILQ_INSERT_TAIL(&slirp_stacks, s, entry); + + for (config = slirp_configs; config; config = config->next) { + if (config->flags & SLIRP_CFG_HOSTFWD) { + if (slirp_hostfwd(s, config->str, + config->flags & SLIRP_CFG_LEGACY) < 0) + goto error; + } else { + if (slirp_guestfwd(s, config->str, + config->flags & SLIRP_CFG_LEGACY) < 0) + goto error; + } + } +#ifndef _WIN32 + if (!smb_export) { + smb_export = legacy_smb_export; + } + if (smb_export) { + if (slirp_smb(s, smb_export, smbsrv) < 0) + goto error; + } +#endif + + return 0; + +error: + qemu_del_vlan_client(nc); + return -1; +} + +static SlirpState *slirp_lookup(Monitor *mon, const char *vlan, + const char *stack) +{ + + if (vlan) { + VLANClientState *nc; + nc = qemu_find_vlan_client_by_name(mon, strtol(vlan, NULL, 0), stack); + if (!nc) { + return NULL; + } + if (strcmp(nc->model, "user")) { + monitor_printf(mon, "invalid device specified\n"); + return NULL; + } + return DO_UPCAST(SlirpState, nc, nc); + } else { + if (QTAILQ_EMPTY(&slirp_stacks)) { + monitor_printf(mon, "user mode network stack not in use\n"); + return NULL; + } + return QTAILQ_FIRST(&slirp_stacks); + } +} + +void net_slirp_hostfwd_remove(Monitor *mon, const QDict *qdict) +{ + struct in_addr host_addr = { .s_addr = INADDR_ANY }; + int host_port; + char buf[256] = ""; + const char *src_str, *p; + SlirpState *s; + int is_udp = 0; + int err; + const char *arg1 = qdict_get_str(qdict, "arg1"); + const char *arg2 = qdict_get_try_str(qdict, "arg2"); + const char *arg3 = qdict_get_try_str(qdict, "arg3"); + + if (arg2) { + s = slirp_lookup(mon, arg1, arg2); + src_str = arg3; + } else { + s = slirp_lookup(mon, NULL, NULL); + src_str = arg1; + } + if (!s) { + return; + } + + if (!src_str || !src_str[0]) + goto fail_syntax; + + p = src_str; + get_str_sep(buf, sizeof(buf), &p, ':'); + + if (!strcmp(buf, "tcp") || buf[0] == '\0') { + is_udp = 0; + } else if (!strcmp(buf, "udp")) { + is_udp = 1; + } else { + goto fail_syntax; + } + + if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { + goto fail_syntax; + } + if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) { + goto fail_syntax; + } + + host_port = atoi(p); + + err = slirp_remove_hostfwd(QTAILQ_FIRST(&slirp_stacks)->slirp, is_udp, + host_addr, host_port); + + monitor_printf(mon, "host forwarding rule for %s %s\n", src_str, + err ? "removed" : "not found"); + return; + + fail_syntax: + monitor_printf(mon, "invalid format\n"); +} + +static int slirp_hostfwd(SlirpState *s, const char *redir_str, + int legacy_format) +{ + struct in_addr host_addr = { .s_addr = INADDR_ANY }; + struct in_addr guest_addr = { .s_addr = 0 }; + int host_port, guest_port; + const char *p; + char buf[256]; + int is_udp; + char *end; + + p = redir_str; + if (!p || get_str_sep(buf, sizeof(buf), &p, ':') < 0) { + goto fail_syntax; + } + if (!strcmp(buf, "tcp") || buf[0] == '\0') { + is_udp = 0; + } else if (!strcmp(buf, "udp")) { + is_udp = 1; + } else { + goto fail_syntax; + } + + if (!legacy_format) { + if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { + goto fail_syntax; + } + if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) { + goto fail_syntax; + } + } + + if (get_str_sep(buf, sizeof(buf), &p, legacy_format ? ':' : '-') < 0) { + goto fail_syntax; + } + host_port = strtol(buf, &end, 0); + if (*end != '\0' || host_port < 1 || host_port > 65535) { + goto fail_syntax; + } + + if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { + goto fail_syntax; + } + if (buf[0] != '\0' && !inet_aton(buf, &guest_addr)) { + goto fail_syntax; + } + + guest_port = strtol(p, &end, 0); + if (*end != '\0' || guest_port < 1 || guest_port > 65535) { + goto fail_syntax; + } + + if (slirp_add_hostfwd(s->slirp, is_udp, host_addr, host_port, guest_addr, + guest_port) < 0) { + error_report("could not set up host forwarding rule '%s'", + redir_str); + return -1; + } + return 0; + + fail_syntax: + error_report("invalid host forwarding rule '%s'", redir_str); + return -1; +} + +void net_slirp_hostfwd_add(Monitor *mon, const QDict *qdict) +{ + const char *redir_str; + SlirpState *s; + const char *arg1 = qdict_get_str(qdict, "arg1"); + const char *arg2 = qdict_get_try_str(qdict, "arg2"); + const char *arg3 = qdict_get_try_str(qdict, "arg3"); + + if (arg2) { + s = slirp_lookup(mon, arg1, arg2); + redir_str = arg3; + } else { + s = slirp_lookup(mon, NULL, NULL); + redir_str = arg1; + } + if (s) { + slirp_hostfwd(s, redir_str, 0); + } + +} + +int net_slirp_redir(const char *redir_str) +{ + struct slirp_config_str *config; + + if (QTAILQ_EMPTY(&slirp_stacks)) { + config = qemu_malloc(sizeof(*config)); + pstrcpy(config->str, sizeof(config->str), redir_str); + config->flags = SLIRP_CFG_HOSTFWD | SLIRP_CFG_LEGACY; + config->next = slirp_configs; + slirp_configs = config; + return 0; + } + + return slirp_hostfwd(QTAILQ_FIRST(&slirp_stacks), redir_str, 1); +} + +#ifndef _WIN32 + +/* automatic user mode samba server configuration */ +static void slirp_smb_cleanup(SlirpState *s) +{ + char cmd[128]; + int ret; + + if (s->smb_dir[0] != '\0') { + snprintf(cmd, sizeof(cmd), "rm -rf %s", s->smb_dir); + ret = system(cmd); + if (ret == -1 || !WIFEXITED(ret)) { + error_report("'%s' failed.", cmd); + } else if (WEXITSTATUS(ret)) { + error_report("'%s' failed. Error code: %d", + cmd, WEXITSTATUS(ret)); + } + s->smb_dir[0] = '\0'; + } +} + +static int slirp_smb(SlirpState* s, const char *exported_dir, + struct in_addr vserver_addr) +{ + static int instance; + char smb_conf[128]; + char smb_cmdline[128]; + FILE *f; + + snprintf(s->smb_dir, sizeof(s->smb_dir), "/tmp/qemu-smb.%ld-%d", + (long)getpid(), instance++); + if (mkdir(s->smb_dir, 0700) < 0) { + error_report("could not create samba server dir '%s'", s->smb_dir); + return -1; + } + snprintf(smb_conf, sizeof(smb_conf), "%s/%s", s->smb_dir, "smb.conf"); + + f = fopen(smb_conf, "w"); + if (!f) { + slirp_smb_cleanup(s); + error_report("could not create samba server configuration file '%s'", + smb_conf); + return -1; + } + fprintf(f, + "[global]\n" + "private dir=%s\n" + "smb ports=0\n" + "socket address=127.0.0.1\n" + "pid directory=%s\n" + "lock directory=%s\n" + "log file=%s/log.smbd\n" + "smb passwd file=%s/smbpasswd\n" + "security = share\n" + "[qemu]\n" + "path=%s\n" + "read only=no\n" + "guest ok=yes\n", + s->smb_dir, + s->smb_dir, + s->smb_dir, + s->smb_dir, + s->smb_dir, + exported_dir + ); + fclose(f); + + snprintf(smb_cmdline, sizeof(smb_cmdline), "%s -s %s", + SMBD_COMMAND, smb_conf); + + if (slirp_add_exec(s->slirp, 0, smb_cmdline, &vserver_addr, 139) < 0) { + slirp_smb_cleanup(s); + error_report("conflicting/invalid smbserver address"); + return -1; + } + return 0; +} + +/* automatic user mode samba server configuration (legacy interface) */ +int net_slirp_smb(const char *exported_dir) +{ + struct in_addr vserver_addr = { .s_addr = 0 }; + + if (legacy_smb_export) { + fprintf(stderr, "-smb given twice\n"); + return -1; + } + legacy_smb_export = exported_dir; + if (!QTAILQ_EMPTY(&slirp_stacks)) { + return slirp_smb(QTAILQ_FIRST(&slirp_stacks), exported_dir, + vserver_addr); + } + return 0; +} + +#endif /* !defined(_WIN32) */ + +struct GuestFwd { + CharDriverState *hd; + struct in_addr server; + int port; + Slirp *slirp; +}; + +static int guestfwd_can_read(void *opaque) +{ + struct GuestFwd *fwd = opaque; + return slirp_socket_can_recv(fwd->slirp, fwd->server, fwd->port); +} + +static void guestfwd_read(void *opaque, const uint8_t *buf, int size) +{ + struct GuestFwd *fwd = opaque; + slirp_socket_recv(fwd->slirp, fwd->server, fwd->port, buf, size); +} + +static int slirp_guestfwd(SlirpState *s, const char *config_str, + int legacy_format) +{ + struct in_addr server = { .s_addr = 0 }; + struct GuestFwd *fwd; + const char *p; + char buf[128]; + char *end; + int port; + + p = config_str; + if (legacy_format) { + if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { + goto fail_syntax; + } + } else { + if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { + goto fail_syntax; + } + if (strcmp(buf, "tcp") && buf[0] != '\0') { + goto fail_syntax; + } + if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { + goto fail_syntax; + } + if (buf[0] != '\0' && !inet_aton(buf, &server)) { + goto fail_syntax; + } + if (get_str_sep(buf, sizeof(buf), &p, '-') < 0) { + goto fail_syntax; + } + } + port = strtol(buf, &end, 10); + if (*end != '\0' || port < 1 || port > 65535) { + goto fail_syntax; + } + + fwd = qemu_malloc(sizeof(struct GuestFwd)); + snprintf(buf, sizeof(buf), "guestfwd.tcp:%d", port); + fwd->hd = qemu_chr_open(buf, p, NULL); + if (!fwd->hd) { + error_report("could not open guest forwarding device '%s'", buf); + qemu_free(fwd); + return -1; + } + + if (slirp_add_exec(s->slirp, 3, fwd->hd, &server, port) < 0) { + error_report("conflicting/invalid host:port in guest forwarding " + "rule '%s'", config_str); + qemu_free(fwd); + return -1; + } + fwd->server = server; + fwd->port = port; + fwd->slirp = s->slirp; + + qemu_chr_add_handlers(fwd->hd, guestfwd_can_read, guestfwd_read, + NULL, fwd); + return 0; + + fail_syntax: + error_report("invalid guest forwarding rule '%s'", config_str); + return -1; +} + +void do_info_usernet(Monitor *mon) +{ + SlirpState *s; + + QTAILQ_FOREACH(s, &slirp_stacks, entry) { + monitor_printf(mon, "VLAN %d (%s):\n", + s->nc.vlan ? s->nc.vlan->id : -1, + s->nc.name); + slirp_connection_info(s->slirp, mon); + } +} + +static int net_init_slirp_configs(const char *name, const char *value, void *opaque) +{ + struct slirp_config_str *config; + + if (strcmp(name, "hostfwd") != 0 && strcmp(name, "guestfwd") != 0) { + return 0; + } + + config = qemu_mallocz(sizeof(*config)); + + pstrcpy(config->str, sizeof(config->str), value); + + if (!strcmp(name, "hostfwd")) { + config->flags = SLIRP_CFG_HOSTFWD; + } + + config->next = slirp_configs; + slirp_configs = config; + + return 0; +} + +int net_init_slirp(QemuOpts *opts, + Monitor *mon, + const char *name, + VLANState *vlan) +{ + struct slirp_config_str *config; + const char *vhost; + const char *vhostname; + const char *vdhcp_start; + const char *vnamesrv; + const char *tftp_export; + const char *bootfile; + const char *smb_export; + const char *vsmbsrv; + char *vnet = NULL; + int restricted = 0; + int ret; + + vhost = qemu_opt_get(opts, "host"); + vhostname = qemu_opt_get(opts, "hostname"); + vdhcp_start = qemu_opt_get(opts, "dhcpstart"); + vnamesrv = qemu_opt_get(opts, "dns"); + tftp_export = qemu_opt_get(opts, "tftp"); + bootfile = qemu_opt_get(opts, "bootfile"); + smb_export = qemu_opt_get(opts, "smb"); + vsmbsrv = qemu_opt_get(opts, "smbserver"); + + if (qemu_opt_get(opts, "ip")) { + const char *ip = qemu_opt_get(opts, "ip"); + int l = strlen(ip) + strlen("/24") + 1; + + vnet = qemu_malloc(l); + + /* emulate legacy ip= parameter */ + pstrcpy(vnet, l, ip); + pstrcat(vnet, l, "/24"); + } + + if (qemu_opt_get(opts, "net")) { + if (vnet) { + qemu_free(vnet); + } + vnet = qemu_strdup(qemu_opt_get(opts, "net")); + } + + if (qemu_opt_get(opts, "restrict") && + qemu_opt_get(opts, "restrict")[0] == 'y') { + restricted = 1; + } + + qemu_opt_foreach(opts, net_init_slirp_configs, NULL, 0); + + ret = net_slirp_init(vlan, "user", name, restricted, vnet, vhost, + vhostname, tftp_export, bootfile, vdhcp_start, + vnamesrv, smb_export, vsmbsrv); + + while (slirp_configs) { + config = slirp_configs; + slirp_configs = config->next; + qemu_free(config); + } + + qemu_free(vnet); + + return ret; +} + +int net_slirp_parse_legacy(QemuOptsList *opts_list, const char *optarg, int *ret) +{ + if (strcmp(opts_list->name, "net") != 0 || + strncmp(optarg, "channel,", strlen("channel,")) != 0) { + return 0; + } + + /* handle legacy -net channel,port:chr */ + optarg += strlen("channel,"); + + if (QTAILQ_EMPTY(&slirp_stacks)) { + struct slirp_config_str *config; + + config = qemu_malloc(sizeof(*config)); + pstrcpy(config->str, sizeof(config->str), optarg); + config->flags = SLIRP_CFG_LEGACY; + config->next = slirp_configs; + slirp_configs = config; + *ret = 0; + } else { + *ret = slirp_guestfwd(QTAILQ_FIRST(&slirp_stacks), optarg, 1); + } + + return 1; +} + diff --git a/net/slirp.h b/net/slirp.h new file mode 100644 index 0000000..c17de8e --- /dev/null +++ b/net/slirp.h @@ -0,0 +1,51 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef QEMU_NET_SLIRP_H +#define QEMU_NET_SLIRP_H + +#include "qemu-common.h" +#include "qdict.h" +#include "qemu-option.h" + +#ifdef CONFIG_SLIRP + +int net_init_slirp(QemuOpts *opts, + Monitor *mon, + const char *name, + VLANState *vlan); + +void net_slirp_hostfwd_add(Monitor *mon, const QDict *qdict); +void net_slirp_hostfwd_remove(Monitor *mon, const QDict *qdict); + +int net_slirp_redir(const char *redir_str); + +int net_slirp_parse_legacy(QemuOptsList *opts_list, const char *optarg, int *ret); + +int net_slirp_smb(const char *exported_dir); + +void do_info_usernet(Monitor *mon); + +#endif + +#endif /* QEMU_NET_SLIRP_H */ diff --git a/net/socket.c b/net/socket.c new file mode 100644 index 0000000..3182b37 --- /dev/null +++ b/net/socket.c @@ -0,0 +1,600 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "net/socket.h" + +#include "config-host.h" + +#include "net.h" +#include "qemu-char.h" +#include "qemu-common.h" +#include "qemu-error.h" +#include "qemu-option.h" +#include "qemu_socket.h" + +typedef struct NetSocketState { + VLANClientState nc; + int fd; + int state; /* 0 = getting length, 1 = getting data */ + unsigned int index; + unsigned int packet_len; + uint8_t buf[4096]; + struct sockaddr_in dgram_dst; /* contains inet host and port destination iff connectionless (SOCK_DGRAM) */ +} NetSocketState; + +typedef struct NetSocketListenState { + VLANState *vlan; + char *model; + char *name; + int fd; +} NetSocketListenState; + +/* XXX: we consider we can send the whole packet without blocking */ +static ssize_t net_socket_receive(VLANClientState *nc, const uint8_t *buf, size_t size) +{ + NetSocketState *s = DO_UPCAST(NetSocketState, nc, nc); + uint32_t len; + len = htonl(size); + + send_all(s->fd, (const uint8_t *)&len, sizeof(len)); + return send_all(s->fd, buf, size); +} + +static ssize_t net_socket_receive_dgram(VLANClientState *nc, const uint8_t *buf, size_t size) +{ + NetSocketState *s = DO_UPCAST(NetSocketState, nc, nc); + + return sendto(s->fd, (const void *)buf, size, 0, + (struct sockaddr *)&s->dgram_dst, sizeof(s->dgram_dst)); +} + +static void net_socket_send(void *opaque) +{ + NetSocketState *s = opaque; + int size, err; + unsigned l; + uint8_t buf1[4096]; + const uint8_t *buf; + + size = recv(s->fd, (void *)buf1, sizeof(buf1), 0); + if (size < 0) { + err = socket_error(); + if (err != EWOULDBLOCK) + goto eoc; + } else if (size == 0) { + /* end of connection */ + eoc: + qemu_set_fd_handler(s->fd, NULL, NULL, NULL); + closesocket(s->fd); + return; + } + buf = buf1; + while (size > 0) { + /* reassemble a packet from the network */ + switch(s->state) { + case 0: + l = 4 - s->index; + if (l > size) + l = size; + memcpy(s->buf + s->index, buf, l); + buf += l; + size -= l; + s->index += l; + if (s->index == 4) { + /* got length */ + s->packet_len = ntohl(*(uint32_t *)s->buf); + s->index = 0; + s->state = 1; + } + break; + case 1: + l = s->packet_len - s->index; + if (l > size) + l = size; + if (s->index + l <= sizeof(s->buf)) { + memcpy(s->buf + s->index, buf, l); + } else { + fprintf(stderr, "serious error: oversized packet received," + "connection terminated.\n"); + s->state = 0; + goto eoc; + } + + s->index += l; + buf += l; + size -= l; + if (s->index >= s->packet_len) { + qemu_send_packet(&s->nc, s->buf, s->packet_len); + s->index = 0; + s->state = 0; + } + break; + } + } +} + +static void net_socket_send_dgram(void *opaque) +{ + NetSocketState *s = opaque; + int size; + + size = recv(s->fd, (void *)s->buf, sizeof(s->buf), 0); + if (size < 0) + return; + if (size == 0) { + /* end of connection */ + qemu_set_fd_handler(s->fd, NULL, NULL, NULL); + return; + } + qemu_send_packet(&s->nc, s->buf, size); +} + +static int net_socket_mcast_create(struct sockaddr_in *mcastaddr, struct in_addr *localaddr) +{ + struct ip_mreq imr; + int fd; + int val, ret; + if (!IN_MULTICAST(ntohl(mcastaddr->sin_addr.s_addr))) { + fprintf(stderr, "qemu: error: specified mcastaddr \"%s\" (0x%08x) does not contain a multicast address\n", + inet_ntoa(mcastaddr->sin_addr), + (int)ntohl(mcastaddr->sin_addr.s_addr)); + return -1; + + } + fd = qemu_socket(PF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + perror("socket(PF_INET, SOCK_DGRAM)"); + return -1; + } + + val = 1; + ret=setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, + (const char *)&val, sizeof(val)); + if (ret < 0) { + perror("setsockopt(SOL_SOCKET, SO_REUSEADDR)"); + goto fail; + } + + ret = bind(fd, (struct sockaddr *)mcastaddr, sizeof(*mcastaddr)); + if (ret < 0) { + perror("bind"); + goto fail; + } + + /* Add host to multicast group */ + imr.imr_multiaddr = mcastaddr->sin_addr; + if (localaddr) { + imr.imr_interface = *localaddr; + } else { + imr.imr_interface.s_addr = htonl(INADDR_ANY); + } + + ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, + (const char *)&imr, sizeof(struct ip_mreq)); + if (ret < 0) { + perror("setsockopt(IP_ADD_MEMBERSHIP)"); + goto fail; + } + + /* Force mcast msgs to loopback (eg. several QEMUs in same host */ + val = 1; + ret=setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, + (const char *)&val, sizeof(val)); + if (ret < 0) { + perror("setsockopt(SOL_IP, IP_MULTICAST_LOOP)"); + goto fail; + } + + /* If a bind address is given, only send packets from that address */ + if (localaddr != NULL) { + ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, + (const char *)localaddr, sizeof(*localaddr)); + if (ret < 0) { + perror("setsockopt(IP_MULTICAST_IF)"); + goto fail; + } + } + + socket_set_nonblock(fd); + return fd; +fail: + if (fd >= 0) + closesocket(fd); + return -1; +} + +static void net_socket_cleanup(VLANClientState *nc) +{ + NetSocketState *s = DO_UPCAST(NetSocketState, nc, nc); + qemu_set_fd_handler(s->fd, NULL, NULL, NULL); + close(s->fd); +} + +static NetClientInfo net_dgram_socket_info = { + .type = NET_CLIENT_TYPE_SOCKET, + .size = sizeof(NetSocketState), + .receive = net_socket_receive_dgram, + .cleanup = net_socket_cleanup, +}; + +static NetSocketState *net_socket_fd_init_dgram(VLANState *vlan, + const char *model, + const char *name, + int fd, int is_connected) +{ + struct sockaddr_in saddr; + int newfd; + socklen_t saddr_len; + VLANClientState *nc; + NetSocketState *s; + + /* fd passed: multicast: "learn" dgram_dst address from bound address and save it + * Because this may be "shared" socket from a "master" process, datagrams would be recv() + * by ONLY ONE process: we must "clone" this dgram socket --jjo + */ + + if (is_connected) { + if (getsockname(fd, (struct sockaddr *) &saddr, &saddr_len) == 0) { + /* must be bound */ + if (saddr.sin_addr.s_addr==0) { + fprintf(stderr, "qemu: error: init_dgram: fd=%d unbound, cannot setup multicast dst addr\n", + fd); + return NULL; + } + /* clone dgram socket */ + newfd = net_socket_mcast_create(&saddr, NULL); + if (newfd < 0) { + /* error already reported by net_socket_mcast_create() */ + close(fd); + return NULL; + } + /* clone newfd to fd, close newfd */ + dup2(newfd, fd); + close(newfd); + + } else { + fprintf(stderr, "qemu: error: init_dgram: fd=%d failed getsockname(): %s\n", + fd, strerror(errno)); + return NULL; + } + } + + nc = qemu_new_net_client(&net_dgram_socket_info, vlan, NULL, model, name); + + snprintf(nc->info_str, sizeof(nc->info_str), + "socket: fd=%d (%s mcast=%s:%d)", + fd, is_connected ? "cloned" : "", + inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); + + s = DO_UPCAST(NetSocketState, nc, nc); + + s->fd = fd; + + qemu_set_fd_handler(s->fd, net_socket_send_dgram, NULL, s); + + /* mcast: save bound address as dst */ + if (is_connected) s->dgram_dst=saddr; + + return s; +} + +static void net_socket_connect(void *opaque) +{ + NetSocketState *s = opaque; + qemu_set_fd_handler(s->fd, net_socket_send, NULL, s); +} + +static NetClientInfo net_socket_info = { + .type = NET_CLIENT_TYPE_SOCKET, + .size = sizeof(NetSocketState), + .receive = net_socket_receive, + .cleanup = net_socket_cleanup, +}; + +static NetSocketState *net_socket_fd_init_stream(VLANState *vlan, + const char *model, + const char *name, + int fd, int is_connected) +{ + VLANClientState *nc; + NetSocketState *s; + + nc = qemu_new_net_client(&net_socket_info, vlan, NULL, model, name); + + snprintf(nc->info_str, sizeof(nc->info_str), "socket: fd=%d", fd); + + s = DO_UPCAST(NetSocketState, nc, nc); + + s->fd = fd; + + if (is_connected) { + net_socket_connect(s); + } else { + qemu_set_fd_handler(s->fd, NULL, net_socket_connect, s); + } + return s; +} + +static NetSocketState *net_socket_fd_init(VLANState *vlan, + const char *model, const char *name, + int fd, int is_connected) +{ + int so_type = -1, optlen=sizeof(so_type); + + if(getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&so_type, + (socklen_t *)&optlen)< 0) { + fprintf(stderr, "qemu: error: getsockopt(SO_TYPE) for fd=%d failed\n", fd); + return NULL; + } + switch(so_type) { + case SOCK_DGRAM: + return net_socket_fd_init_dgram(vlan, model, name, fd, is_connected); + case SOCK_STREAM: + return net_socket_fd_init_stream(vlan, model, name, fd, is_connected); + default: + /* who knows ... this could be a eg. a pty, do warn and continue as stream */ + fprintf(stderr, "qemu: warning: socket type=%d for fd=%d is not SOCK_DGRAM or SOCK_STREAM\n", so_type, fd); + return net_socket_fd_init_stream(vlan, model, name, fd, is_connected); + } + return NULL; +} + +static void net_socket_accept(void *opaque) +{ + NetSocketListenState *s = opaque; + NetSocketState *s1; + struct sockaddr_in saddr; + socklen_t len; + int fd; + + for(;;) { + len = sizeof(saddr); + fd = qemu_accept(s->fd, (struct sockaddr *)&saddr, &len); + if (fd < 0 && errno != EINTR) { + return; + } else if (fd >= 0) { + break; + } + } + s1 = net_socket_fd_init(s->vlan, s->model, s->name, fd, 1); + if (!s1) { + closesocket(fd); + } else { + snprintf(s1->nc.info_str, sizeof(s1->nc.info_str), + "socket: connection from %s:%d", + inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); + } +} + +static int net_socket_listen_init(VLANState *vlan, + const char *model, + const char *name, + const char *host_str) +{ + NetSocketListenState *s; + int fd, val, ret; + struct sockaddr_in saddr; + + if (parse_host_port(&saddr, host_str) < 0) + return -1; + + s = qemu_mallocz(sizeof(NetSocketListenState)); + + fd = qemu_socket(PF_INET, SOCK_STREAM, 0); + if (fd < 0) { + perror("socket"); + return -1; + } + socket_set_nonblock(fd); + + /* allow fast reuse */ + val = 1; + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val)); + + ret = bind(fd, (struct sockaddr *)&saddr, sizeof(saddr)); + if (ret < 0) { + perror("bind"); + return -1; + } + ret = listen(fd, 0); + if (ret < 0) { + perror("listen"); + return -1; + } + s->vlan = vlan; + s->model = qemu_strdup(model); + s->name = name ? qemu_strdup(name) : NULL; + s->fd = fd; + qemu_set_fd_handler(fd, net_socket_accept, NULL, s); + return 0; +} + +static int net_socket_connect_init(VLANState *vlan, + const char *model, + const char *name, + const char *host_str) +{ + NetSocketState *s; + int fd, connected, ret, err; + struct sockaddr_in saddr; + + if (parse_host_port(&saddr, host_str) < 0) + return -1; + + fd = qemu_socket(PF_INET, SOCK_STREAM, 0); + if (fd < 0) { + perror("socket"); + return -1; + } + socket_set_nonblock(fd); + + connected = 0; + for(;;) { + ret = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr)); + if (ret < 0) { + err = socket_error(); + if (err == EINTR || err == EWOULDBLOCK) { + } else if (err == EINPROGRESS) { + break; +#ifdef _WIN32 + } else if (err == WSAEALREADY) { + break; +#endif + } else { + perror("connect"); + closesocket(fd); + return -1; + } + } else { + connected = 1; + break; + } + } + s = net_socket_fd_init(vlan, model, name, fd, connected); + if (!s) + return -1; + snprintf(s->nc.info_str, sizeof(s->nc.info_str), + "socket: connect to %s:%d", + inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); + return 0; +} + +static int net_socket_mcast_init(VLANState *vlan, + const char *model, + const char *name, + const char *host_str, + const char *localaddr_str) +{ + NetSocketState *s; + int fd; + struct sockaddr_in saddr; + struct in_addr localaddr, *param_localaddr; + + if (parse_host_port(&saddr, host_str) < 0) + return -1; + + if (localaddr_str != NULL) { + if (inet_aton(localaddr_str, &localaddr) == 0) + return -1; + param_localaddr = &localaddr; + } else { + param_localaddr = NULL; + } + + fd = net_socket_mcast_create(&saddr, param_localaddr); + if (fd < 0) + return -1; + + s = net_socket_fd_init(vlan, model, name, fd, 0); + if (!s) + return -1; + + s->dgram_dst = saddr; + + snprintf(s->nc.info_str, sizeof(s->nc.info_str), + "socket: mcast=%s:%d", + inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); + return 0; + +} + +int net_init_socket(QemuOpts *opts, + Monitor *mon, + const char *name, + VLANState *vlan) +{ + if (qemu_opt_get(opts, "fd")) { + int fd; + + if (qemu_opt_get(opts, "listen") || + qemu_opt_get(opts, "connect") || + qemu_opt_get(opts, "mcast") || + qemu_opt_get(opts, "localaddr")) { + error_report("listen=, connect=, mcast= and localaddr= is invalid with fd=\n"); + return -1; + } + + fd = net_handle_fd_param(mon, qemu_opt_get(opts, "fd")); + if (fd == -1) { + return -1; + } + + if (!net_socket_fd_init(vlan, "socket", name, fd, 1)) { + close(fd); + return -1; + } + } else if (qemu_opt_get(opts, "listen")) { + const char *listen; + + if (qemu_opt_get(opts, "fd") || + qemu_opt_get(opts, "connect") || + qemu_opt_get(opts, "mcast") || + qemu_opt_get(opts, "localaddr")) { + error_report("fd=, connect=, mcast= and localaddr= is invalid with listen=\n"); + return -1; + } + + listen = qemu_opt_get(opts, "listen"); + + if (net_socket_listen_init(vlan, "socket", name, listen) == -1) { + return -1; + } + } else if (qemu_opt_get(opts, "connect")) { + const char *connect; + + if (qemu_opt_get(opts, "fd") || + qemu_opt_get(opts, "listen") || + qemu_opt_get(opts, "mcast") || + qemu_opt_get(opts, "localaddr")) { + error_report("fd=, listen=, mcast= and localaddr= is invalid with connect=\n"); + return -1; + } + + connect = qemu_opt_get(opts, "connect"); + + if (net_socket_connect_init(vlan, "socket", name, connect) == -1) { + return -1; + } + } else if (qemu_opt_get(opts, "mcast")) { + const char *mcast, *localaddr; + + if (qemu_opt_get(opts, "fd") || + qemu_opt_get(opts, "connect") || + qemu_opt_get(opts, "listen")) { + error_report("fd=, connect= and listen= is invalid with mcast="); + return -1; + } + + mcast = qemu_opt_get(opts, "mcast"); + localaddr = qemu_opt_get(opts, "localaddr"); + + if (net_socket_mcast_init(vlan, "socket", name, mcast, localaddr) == -1) { + return -1; + } + } else { + error_report("-socket requires fd=, listen=, connect= or mcast="); + return -1; + } + + return 0; +} diff --git a/net/socket.h b/net/socket.h new file mode 100644 index 0000000..ea46f02 --- /dev/null +++ b/net/socket.h @@ -0,0 +1,33 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef QEMU_NET_SOCKET_H +#define QEMU_NET_SOCKET_H + +#include "net.h" +#include "qemu-common.h" + +int net_init_socket(QemuOpts *opts, Monitor *mon, + const char *name, VLANState *vlan); + +#endif /* QEMU_NET_SOCKET_H */ diff --git a/net/tap-aix.c b/net/tap-aix.c new file mode 100644 index 0000000..e19aaba --- /dev/null +++ b/net/tap-aix.c @@ -0,0 +1,61 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "net/tap.h" +#include <stdio.h> + +int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required) +{ + fprintf(stderr, "no tap on AIX\n"); + return -1; +} + +int tap_set_sndbuf(int fd, QemuOpts *opts) +{ + return 0; +} + +int tap_probe_vnet_hdr(int fd) +{ + return 0; +} + +int tap_probe_has_ufo(int fd) +{ + return 0; +} + +int tap_probe_vnet_hdr_len(int fd, int len) +{ + return 0; +} + +void tap_fd_set_vnet_hdr_len(int fd, int len) +{ +} + +void tap_fd_set_offload(int fd, int csum, int tso4, + int tso6, int ecn, int ufo) +{ +} diff --git a/net/tap-bsd.c b/net/tap-bsd.c new file mode 100644 index 0000000..2f3efde --- /dev/null +++ b/net/tap-bsd.c @@ -0,0 +1,131 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "net/tap.h" +#include "qemu-common.h" +#include "sysemu.h" +#include "qemu-error.h" + +#ifdef __NetBSD__ +#include <net/if_tap.h> +#endif + +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) +#include <libutil.h> +#else +#include <util.h> +#endif + +int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required) +{ + int fd; + char *dev; + struct stat s; + +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) + /* if no ifname is given, always start the search from tap0/tun0. */ + int i; + char dname[100]; + + for (i = 0; i < 10; i++) { + if (*ifname) { + snprintf(dname, sizeof dname, "/dev/%s", ifname); + } else { +#if defined(__OpenBSD__) + snprintf(dname, sizeof dname, "/dev/tun%d", i); +#else + snprintf(dname, sizeof dname, "/dev/tap%d", i); +#endif + } + TFR(fd = open(dname, O_RDWR)); + if (fd >= 0) { + break; + } + else if (errno == ENXIO || errno == ENOENT) { + break; + } + if (*ifname) { + break; + } + } + if (fd < 0) { + error_report("warning: could not open %s (%s): no virtual network emulation", + dname, strerror(errno)); + return -1; + } +#else + TFR(fd = open("/dev/tap", O_RDWR)); + if (fd < 0) { + fprintf(stderr, "warning: could not open /dev/tap: no virtual network emulation\n"); + return -1; + } +#endif + + fstat(fd, &s); + dev = devname(s.st_rdev, S_IFCHR); + pstrcpy(ifname, ifname_size, dev); + + if (*vnet_hdr) { + /* BSD doesn't have IFF_VNET_HDR */ + *vnet_hdr = 0; + + if (vnet_hdr_required && !*vnet_hdr) { + error_report("vnet_hdr=1 requested, but no kernel " + "support for IFF_VNET_HDR available"); + close(fd); + return -1; + } + } + fcntl(fd, F_SETFL, O_NONBLOCK); + return fd; +} + +int tap_set_sndbuf(int fd, QemuOpts *opts) +{ + return 0; +} + +int tap_probe_vnet_hdr(int fd) +{ + return 0; +} + +int tap_probe_has_ufo(int fd) +{ + return 0; +} + +int tap_probe_vnet_hdr_len(int fd, int len) +{ + return 0; +} + +void tap_fd_set_vnet_hdr_len(int fd, int len) +{ +} + +void tap_fd_set_offload(int fd, int csum, int tso4, + int tso6, int ecn, int ufo) +{ +} diff --git a/net/tap-haiku.c b/net/tap-haiku.c new file mode 100644 index 0000000..91dda8e --- /dev/null +++ b/net/tap-haiku.c @@ -0,0 +1,61 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "net/tap.h" +#include <stdio.h> + +int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required) +{ + fprintf(stderr, "no tap on Haiku\n"); + return -1; +} + +int tap_set_sndbuf(int fd, QemuOpts *opts) +{ + return 0; +} + +int tap_probe_vnet_hdr(int fd) +{ + return 0; +} + +int tap_probe_has_ufo(int fd) +{ + return 0; +} + +int tap_probe_vnet_hdr_len(int fd, int len) +{ + return 0; +} + +void tap_fd_set_vnet_hdr_len(int fd, int len) +{ +} + +void tap_fd_set_offload(int fd, int csum, int tso4, + int tso6, int ecn, int ufo) +{ +} diff --git a/net/tap-linux.c b/net/tap-linux.c new file mode 100644 index 0000000..ff8cad0 --- /dev/null +++ b/net/tap-linux.c @@ -0,0 +1,195 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2009 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "net/tap.h" +#include "net/tap-linux.h" + +#include <net/if.h> +#include <sys/ioctl.h> + +#include "sysemu.h" +#include "qemu-common.h" +#include "qemu-error.h" + +#define PATH_NET_TUN "/dev/net/tun" + +int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required) +{ + struct ifreq ifr; + int fd, ret; + + TFR(fd = open(PATH_NET_TUN, O_RDWR)); + if (fd < 0) { + error_report("could not open %s: %m", PATH_NET_TUN); + return -1; + } + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + + if (*vnet_hdr) { + unsigned int features; + + if (ioctl(fd, TUNGETFEATURES, &features) == 0 && + features & IFF_VNET_HDR) { + *vnet_hdr = 1; + ifr.ifr_flags |= IFF_VNET_HDR; + } else { + *vnet_hdr = 0; + } + + if (vnet_hdr_required && !*vnet_hdr) { + error_report("vnet_hdr=1 requested, but no kernel " + "support for IFF_VNET_HDR available"); + close(fd); + return -1; + } + } + + if (ifname[0] != '\0') + pstrcpy(ifr.ifr_name, IFNAMSIZ, ifname); + else + pstrcpy(ifr.ifr_name, IFNAMSIZ, "tap%d"); + ret = ioctl(fd, TUNSETIFF, (void *) &ifr); + if (ret != 0) { + error_report("could not configure %s (%s): %m", PATH_NET_TUN, ifr.ifr_name); + close(fd); + return -1; + } + pstrcpy(ifname, ifname_size, ifr.ifr_name); + fcntl(fd, F_SETFL, O_NONBLOCK); + return fd; +} + +/* sndbuf implements a kind of flow control for tap. + * Unfortunately when it's enabled, and packets are sent + * to other guests on the same host, the receiver + * can lock up the transmitter indefinitely. + * + * To avoid packet loss, sndbuf should be set to a value lower than the tx + * queue capacity of any destination network interface. + * Ethernet NICs generally have txqueuelen=1000, so 1Mb is + * a good value, given a 1500 byte MTU. + */ +#define TAP_DEFAULT_SNDBUF 0 + +int tap_set_sndbuf(int fd, QemuOpts *opts) +{ + int sndbuf; + + sndbuf = qemu_opt_get_size(opts, "sndbuf", TAP_DEFAULT_SNDBUF); + if (!sndbuf) { + sndbuf = INT_MAX; + } + + if (ioctl(fd, TUNSETSNDBUF, &sndbuf) == -1 && qemu_opt_get(opts, "sndbuf")) { + error_report("TUNSETSNDBUF ioctl failed: %s", strerror(errno)); + return -1; + } + return 0; +} + +int tap_probe_vnet_hdr(int fd) +{ + struct ifreq ifr; + + if (ioctl(fd, TUNGETIFF, &ifr) != 0) { + error_report("TUNGETIFF ioctl() failed: %s", strerror(errno)); + return 0; + } + + return ifr.ifr_flags & IFF_VNET_HDR; +} + +int tap_probe_has_ufo(int fd) +{ + unsigned offload; + + offload = TUN_F_CSUM | TUN_F_UFO; + + if (ioctl(fd, TUNSETOFFLOAD, offload) < 0) + return 0; + + return 1; +} + +/* Verify that we can assign given length */ +int tap_probe_vnet_hdr_len(int fd, int len) +{ + int orig; + if (ioctl(fd, TUNGETVNETHDRSZ, &orig) == -1) { + return 0; + } + if (ioctl(fd, TUNSETVNETHDRSZ, &len) == -1) { + return 0; + } + /* Restore original length: we can't handle failure. */ + if (ioctl(fd, TUNSETVNETHDRSZ, &orig) == -1) { + fprintf(stderr, "TUNGETVNETHDRSZ ioctl() failed: %s. Exiting.\n", + strerror(errno)); + assert(0); + return -errno; + } + return 1; +} + +void tap_fd_set_vnet_hdr_len(int fd, int len) +{ + if (ioctl(fd, TUNSETVNETHDRSZ, &len) == -1) { + fprintf(stderr, "TUNSETVNETHDRSZ ioctl() failed: %s. Exiting.\n", + strerror(errno)); + assert(0); + } +} + +void tap_fd_set_offload(int fd, int csum, int tso4, + int tso6, int ecn, int ufo) +{ + unsigned int offload = 0; + + /* Check if our kernel supports TUNSETOFFLOAD */ + if (ioctl(fd, TUNSETOFFLOAD, 0) != 0 && errno == EINVAL) { + return; + } + + if (csum) { + offload |= TUN_F_CSUM; + if (tso4) + offload |= TUN_F_TSO4; + if (tso6) + offload |= TUN_F_TSO6; + if ((tso4 || tso6) && ecn) + offload |= TUN_F_TSO_ECN; + if (ufo) + offload |= TUN_F_UFO; + } + + if (ioctl(fd, TUNSETOFFLOAD, offload) != 0) { + offload &= ~TUN_F_UFO; + if (ioctl(fd, TUNSETOFFLOAD, offload) != 0) { + fprintf(stderr, "TUNSETOFFLOAD ioctl() failed: %s\n", + strerror(errno)); + } + } +} diff --git a/net/tap-linux.h b/net/tap-linux.h new file mode 100644 index 0000000..659e981 --- /dev/null +++ b/net/tap-linux.h @@ -0,0 +1,63 @@ +/* + * Universal TUN/TAP device driver. + * Copyright (C) 1999-2000 Maxim Krasnyansky <max_mk@yahoo.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef QEMU_TAP_H +#define QEMU_TAP_H + +#include <stdint.h> +#ifdef __linux__ + +#include <linux/ioctl.h> + +/* Ioctl defines */ +#define TUNSETIFF _IOW('T', 202, int) +#define TUNGETFEATURES _IOR('T', 207, unsigned int) +#define TUNSETOFFLOAD _IOW('T', 208, unsigned int) +#define TUNGETIFF _IOR('T', 210, unsigned int) +#define TUNSETSNDBUF _IOW('T', 212, int) +#define TUNGETVNETHDRSZ _IOR('T', 215, int) +#define TUNSETVNETHDRSZ _IOW('T', 216, int) + +#endif + +/* TUNSETIFF ifr flags */ +#define IFF_TAP 0x0002 +#define IFF_NO_PI 0x1000 +#define IFF_VNET_HDR 0x4000 + +/* Features for GSO (TUNSETOFFLOAD). */ +#define TUN_F_CSUM 0x01 /* You can hand me unchecksummed packets. */ +#define TUN_F_TSO4 0x02 /* I can handle TSO for IPv4 packets */ +#define TUN_F_TSO6 0x04 /* I can handle TSO for IPv6 packets */ +#define TUN_F_TSO_ECN 0x08 /* I can handle TSO with ECN bits. */ +#define TUN_F_UFO 0x10 /* I can handle UFO packets */ + +struct virtio_net_hdr +{ + uint8_t flags; + uint8_t gso_type; + uint16_t hdr_len; + uint16_t gso_size; + uint16_t csum_start; + uint16_t csum_offset; +}; + +struct virtio_net_hdr_mrg_rxbuf +{ + struct virtio_net_hdr hdr; + uint16_t num_buffers; /* Number of merged rx buffers */ +}; + +#endif /* QEMU_TAP_H */ diff --git a/net/tap-solaris.c b/net/tap-solaris.c new file mode 100644 index 0000000..c216d28 --- /dev/null +++ b/net/tap-solaris.c @@ -0,0 +1,227 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "net/tap.h" +#include "sysemu.h" + +#include <sys/stat.h> +#include <sys/ethernet.h> +#include <sys/sockio.h> +#include <netinet/arp.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/ip_icmp.h> // must come after ip.h +#include <netinet/udp.h> +#include <netinet/tcp.h> +#include <net/if.h> +#include <syslog.h> +#include <stropts.h> +#include "qemu-error.h" + +ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen) +{ + struct strbuf sbuf; + int f = 0; + + sbuf.maxlen = maxlen; + sbuf.buf = (char *)buf; + + return getmsg(tapfd, NULL, &sbuf, &f) >= 0 ? sbuf.len : -1; +} + +#define TUNNEWPPA (('T'<<16) | 0x0001) +/* + * Allocate TAP device, returns opened fd. + * Stores dev name in the first arg(must be large enough). + */ +static int tap_alloc(char *dev, size_t dev_size) +{ + int tap_fd, if_fd, ppa = -1; + static int ip_fd = 0; + char *ptr; + + static int arp_fd = 0; + int ip_muxid, arp_muxid; + struct strioctl strioc_if, strioc_ppa; + int link_type = I_PLINK;; + struct lifreq ifr; + char actual_name[32] = ""; + + memset(&ifr, 0x0, sizeof(ifr)); + + if( *dev ){ + ptr = dev; + while( *ptr && !qemu_isdigit((int)*ptr) ) ptr++; + ppa = atoi(ptr); + } + + /* Check if IP device was opened */ + if( ip_fd ) + close(ip_fd); + + TFR(ip_fd = open("/dev/udp", O_RDWR, 0)); + if (ip_fd < 0) { + syslog(LOG_ERR, "Can't open /dev/ip (actually /dev/udp)"); + return -1; + } + + TFR(tap_fd = open("/dev/tap", O_RDWR, 0)); + if (tap_fd < 0) { + syslog(LOG_ERR, "Can't open /dev/tap"); + return -1; + } + + /* Assign a new PPA and get its unit number. */ + strioc_ppa.ic_cmd = TUNNEWPPA; + strioc_ppa.ic_timout = 0; + strioc_ppa.ic_len = sizeof(ppa); + strioc_ppa.ic_dp = (char *)&ppa; + if ((ppa = ioctl (tap_fd, I_STR, &strioc_ppa)) < 0) + syslog (LOG_ERR, "Can't assign new interface"); + + TFR(if_fd = open("/dev/tap", O_RDWR, 0)); + if (if_fd < 0) { + syslog(LOG_ERR, "Can't open /dev/tap (2)"); + return -1; + } + if(ioctl(if_fd, I_PUSH, "ip") < 0){ + syslog(LOG_ERR, "Can't push IP module"); + return -1; + } + + if (ioctl(if_fd, SIOCGLIFFLAGS, &ifr) < 0) + syslog(LOG_ERR, "Can't get flags\n"); + + snprintf (actual_name, 32, "tap%d", ppa); + pstrcpy(ifr.lifr_name, sizeof(ifr.lifr_name), actual_name); + + ifr.lifr_ppa = ppa; + /* Assign ppa according to the unit number returned by tun device */ + + if (ioctl (if_fd, SIOCSLIFNAME, &ifr) < 0) + syslog (LOG_ERR, "Can't set PPA %d", ppa); + if (ioctl(if_fd, SIOCGLIFFLAGS, &ifr) <0) + syslog (LOG_ERR, "Can't get flags\n"); + /* Push arp module to if_fd */ + if (ioctl (if_fd, I_PUSH, "arp") < 0) + syslog (LOG_ERR, "Can't push ARP module (2)"); + + /* Push arp module to ip_fd */ + if (ioctl (ip_fd, I_POP, NULL) < 0) + syslog (LOG_ERR, "I_POP failed\n"); + if (ioctl (ip_fd, I_PUSH, "arp") < 0) + syslog (LOG_ERR, "Can't push ARP module (3)\n"); + /* Open arp_fd */ + TFR(arp_fd = open ("/dev/tap", O_RDWR, 0)); + if (arp_fd < 0) + syslog (LOG_ERR, "Can't open %s\n", "/dev/tap"); + + /* Set ifname to arp */ + strioc_if.ic_cmd = SIOCSLIFNAME; + strioc_if.ic_timout = 0; + strioc_if.ic_len = sizeof(ifr); + strioc_if.ic_dp = (char *)𝔦 + if (ioctl(arp_fd, I_STR, &strioc_if) < 0){ + syslog (LOG_ERR, "Can't set ifname to arp\n"); + } + + if((ip_muxid = ioctl(ip_fd, I_LINK, if_fd)) < 0){ + syslog(LOG_ERR, "Can't link TAP device to IP"); + return -1; + } + + if ((arp_muxid = ioctl (ip_fd, link_type, arp_fd)) < 0) + syslog (LOG_ERR, "Can't link TAP device to ARP"); + + close (if_fd); + + memset(&ifr, 0x0, sizeof(ifr)); + pstrcpy(ifr.lifr_name, sizeof(ifr.lifr_name), actual_name); + ifr.lifr_ip_muxid = ip_muxid; + ifr.lifr_arp_muxid = arp_muxid; + + if (ioctl (ip_fd, SIOCSLIFMUXID, &ifr) < 0) + { + ioctl (ip_fd, I_PUNLINK , arp_muxid); + ioctl (ip_fd, I_PUNLINK, ip_muxid); + syslog (LOG_ERR, "Can't set multiplexor id"); + } + + snprintf(dev, dev_size, "tap%d", ppa); + return tap_fd; +} + +int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required) +{ + char dev[10]=""; + int fd; + if( (fd = tap_alloc(dev, sizeof(dev))) < 0 ){ + fprintf(stderr, "Cannot allocate TAP device\n"); + return -1; + } + pstrcpy(ifname, ifname_size, dev); + if (*vnet_hdr) { + /* Solaris doesn't have IFF_VNET_HDR */ + *vnet_hdr = 0; + + if (vnet_hdr_required && !*vnet_hdr) { + error_report("vnet_hdr=1 requested, but no kernel " + "support for IFF_VNET_HDR available"); + close(fd); + return -1; + } + } + fcntl(fd, F_SETFL, O_NONBLOCK); + return fd; +} + +int tap_set_sndbuf(int fd, QemuOpts *opts) +{ + return 0; +} + +int tap_probe_vnet_hdr(int fd) +{ + return 0; +} + +int tap_probe_has_ufo(int fd) +{ + return 0; +} + +int tap_probe_vnet_hdr_len(int fd, int len) +{ + return 0; +} + +void tap_fd_set_vnet_hdr_len(int fd, int len) +{ +} + +void tap_fd_set_offload(int fd, int csum, int tso4, + int tso6, int ecn, int ufo) +{ +} diff --git a/net/tap-win32.c b/net/tap-win32.c new file mode 100644 index 0000000..081904e --- /dev/null +++ b/net/tap-win32.c @@ -0,0 +1,751 @@ +/* + * TAP-Win32 -- A kernel driver to provide virtual tap device functionality + * on Windows. Originally derived from the CIPE-Win32 + * project by Damion K. Wilson, with extensive modifications by + * James Yonan. + * + * All source code which derives from the CIPE-Win32 project is + * Copyright (C) Damion K. Wilson, 2003, and is released under the + * GPL version 2 (see below). + * + * All other source code is Copyright (C) James Yonan, 2003-2004, + * and is released under the GPL version 2 (see below). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING included with this + * distribution); if not, see <http://www.gnu.org/licenses/>. + */ + +#include "net/tap.h" + +#include "qemu-common.h" +#include "net.h" +#include "sysemu.h" +#include "qemu-error.h" +#include <stdio.h> +#include <windows.h> +#include <winioctl.h> + +//============= +// TAP IOCTLs +//============= + +#define TAP_CONTROL_CODE(request,method) \ + CTL_CODE (FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS) + +#define TAP_IOCTL_GET_MAC TAP_CONTROL_CODE (1, METHOD_BUFFERED) +#define TAP_IOCTL_GET_VERSION TAP_CONTROL_CODE (2, METHOD_BUFFERED) +#define TAP_IOCTL_GET_MTU TAP_CONTROL_CODE (3, METHOD_BUFFERED) +#define TAP_IOCTL_GET_INFO TAP_CONTROL_CODE (4, METHOD_BUFFERED) +#define TAP_IOCTL_CONFIG_POINT_TO_POINT TAP_CONTROL_CODE (5, METHOD_BUFFERED) +#define TAP_IOCTL_SET_MEDIA_STATUS TAP_CONTROL_CODE (6, METHOD_BUFFERED) +#define TAP_IOCTL_CONFIG_DHCP_MASQ TAP_CONTROL_CODE (7, METHOD_BUFFERED) +#define TAP_IOCTL_GET_LOG_LINE TAP_CONTROL_CODE (8, METHOD_BUFFERED) +#define TAP_IOCTL_CONFIG_DHCP_SET_OPT TAP_CONTROL_CODE (9, METHOD_BUFFERED) + +//================= +// Registry keys +//================= + +#define ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}" + +#define NETWORK_CONNECTIONS_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}" + +//====================== +// Filesystem prefixes +//====================== + +#define USERMODEDEVICEDIR "\\\\.\\Global\\" +#define TAPSUFFIX ".tap" + + +//====================== +// Compile time configuration +//====================== + +//#define DEBUG_TAP_WIN32 + +#define TUN_ASYNCHRONOUS_WRITES 1 + +#define TUN_BUFFER_SIZE 1560 +#define TUN_MAX_BUFFER_COUNT 32 + +/* + * The data member "buffer" must be the first element in the tun_buffer + * structure. See the function, tap_win32_free_buffer. + */ +typedef struct tun_buffer_s { + unsigned char buffer [TUN_BUFFER_SIZE]; + unsigned long read_size; + struct tun_buffer_s* next; +} tun_buffer_t; + +typedef struct tap_win32_overlapped { + HANDLE handle; + HANDLE read_event; + HANDLE write_event; + HANDLE output_queue_semaphore; + HANDLE free_list_semaphore; + HANDLE tap_semaphore; + CRITICAL_SECTION output_queue_cs; + CRITICAL_SECTION free_list_cs; + OVERLAPPED read_overlapped; + OVERLAPPED write_overlapped; + tun_buffer_t buffers[TUN_MAX_BUFFER_COUNT]; + tun_buffer_t* free_list; + tun_buffer_t* output_queue_front; + tun_buffer_t* output_queue_back; +} tap_win32_overlapped_t; + +static tap_win32_overlapped_t tap_overlapped; + +static tun_buffer_t* get_buffer_from_free_list(tap_win32_overlapped_t* const overlapped) +{ + tun_buffer_t* buffer = NULL; + WaitForSingleObject(overlapped->free_list_semaphore, INFINITE); + EnterCriticalSection(&overlapped->free_list_cs); + buffer = overlapped->free_list; +// assert(buffer != NULL); + overlapped->free_list = buffer->next; + LeaveCriticalSection(&overlapped->free_list_cs); + buffer->next = NULL; + return buffer; +} + +static void put_buffer_on_free_list(tap_win32_overlapped_t* const overlapped, tun_buffer_t* const buffer) +{ + EnterCriticalSection(&overlapped->free_list_cs); + buffer->next = overlapped->free_list; + overlapped->free_list = buffer; + LeaveCriticalSection(&overlapped->free_list_cs); + ReleaseSemaphore(overlapped->free_list_semaphore, 1, NULL); +} + +static tun_buffer_t* get_buffer_from_output_queue(tap_win32_overlapped_t* const overlapped, const int block) +{ + tun_buffer_t* buffer = NULL; + DWORD result, timeout = block ? INFINITE : 0L; + + // Non-blocking call + result = WaitForSingleObject(overlapped->output_queue_semaphore, timeout); + + switch (result) + { + // The semaphore object was signaled. + case WAIT_OBJECT_0: + EnterCriticalSection(&overlapped->output_queue_cs); + + buffer = overlapped->output_queue_front; + overlapped->output_queue_front = buffer->next; + + if(overlapped->output_queue_front == NULL) { + overlapped->output_queue_back = NULL; + } + + LeaveCriticalSection(&overlapped->output_queue_cs); + break; + + // Semaphore was nonsignaled, so a time-out occurred. + case WAIT_TIMEOUT: + // Cannot open another window. + break; + } + + return buffer; +} + +static tun_buffer_t* get_buffer_from_output_queue_immediate (tap_win32_overlapped_t* const overlapped) +{ + return get_buffer_from_output_queue(overlapped, 0); +} + +static void put_buffer_on_output_queue(tap_win32_overlapped_t* const overlapped, tun_buffer_t* const buffer) +{ + EnterCriticalSection(&overlapped->output_queue_cs); + + if(overlapped->output_queue_front == NULL && overlapped->output_queue_back == NULL) { + overlapped->output_queue_front = overlapped->output_queue_back = buffer; + } else { + buffer->next = NULL; + overlapped->output_queue_back->next = buffer; + overlapped->output_queue_back = buffer; + } + + LeaveCriticalSection(&overlapped->output_queue_cs); + + ReleaseSemaphore(overlapped->output_queue_semaphore, 1, NULL); +} + + +static int is_tap_win32_dev(const char *guid) +{ + HKEY netcard_key; + LONG status; + DWORD len; + int i = 0; + + status = RegOpenKeyEx( + HKEY_LOCAL_MACHINE, + ADAPTER_KEY, + 0, + KEY_READ, + &netcard_key); + + if (status != ERROR_SUCCESS) { + return FALSE; + } + + for (;;) { + char enum_name[256]; + char unit_string[256]; + HKEY unit_key; + char component_id_string[] = "ComponentId"; + char component_id[256]; + char net_cfg_instance_id_string[] = "NetCfgInstanceId"; + char net_cfg_instance_id[256]; + DWORD data_type; + + len = sizeof (enum_name); + status = RegEnumKeyEx( + netcard_key, + i, + enum_name, + &len, + NULL, + NULL, + NULL, + NULL); + + if (status == ERROR_NO_MORE_ITEMS) + break; + else if (status != ERROR_SUCCESS) { + return FALSE; + } + + snprintf (unit_string, sizeof(unit_string), "%s\\%s", + ADAPTER_KEY, enum_name); + + status = RegOpenKeyEx( + HKEY_LOCAL_MACHINE, + unit_string, + 0, + KEY_READ, + &unit_key); + + if (status != ERROR_SUCCESS) { + return FALSE; + } else { + len = sizeof (component_id); + status = RegQueryValueEx( + unit_key, + component_id_string, + NULL, + &data_type, + (LPBYTE)component_id, + &len); + + if (!(status != ERROR_SUCCESS || data_type != REG_SZ)) { + len = sizeof (net_cfg_instance_id); + status = RegQueryValueEx( + unit_key, + net_cfg_instance_id_string, + NULL, + &data_type, + (LPBYTE)net_cfg_instance_id, + &len); + + if (status == ERROR_SUCCESS && data_type == REG_SZ) { + if (/* !strcmp (component_id, TAP_COMPONENT_ID) &&*/ + !strcmp (net_cfg_instance_id, guid)) { + RegCloseKey (unit_key); + RegCloseKey (netcard_key); + return TRUE; + } + } + } + RegCloseKey (unit_key); + } + ++i; + } + + RegCloseKey (netcard_key); + return FALSE; +} + +static int get_device_guid( + char *name, + int name_size, + char *actual_name, + int actual_name_size) +{ + LONG status; + HKEY control_net_key; + DWORD len; + int i = 0; + int stop = 0; + + status = RegOpenKeyEx( + HKEY_LOCAL_MACHINE, + NETWORK_CONNECTIONS_KEY, + 0, + KEY_READ, + &control_net_key); + + if (status != ERROR_SUCCESS) { + return -1; + } + + while (!stop) + { + char enum_name[256]; + char connection_string[256]; + HKEY connection_key; + char name_data[256]; + DWORD name_type; + const char name_string[] = "Name"; + + len = sizeof (enum_name); + status = RegEnumKeyEx( + control_net_key, + i, + enum_name, + &len, + NULL, + NULL, + NULL, + NULL); + + if (status == ERROR_NO_MORE_ITEMS) + break; + else if (status != ERROR_SUCCESS) { + return -1; + } + + snprintf(connection_string, + sizeof(connection_string), + "%s\\%s\\Connection", + NETWORK_CONNECTIONS_KEY, enum_name); + + status = RegOpenKeyEx( + HKEY_LOCAL_MACHINE, + connection_string, + 0, + KEY_READ, + &connection_key); + + if (status == ERROR_SUCCESS) { + len = sizeof (name_data); + status = RegQueryValueEx( + connection_key, + name_string, + NULL, + &name_type, + (LPBYTE)name_data, + &len); + + if (status != ERROR_SUCCESS || name_type != REG_SZ) { + return -1; + } + else { + if (is_tap_win32_dev(enum_name)) { + snprintf(name, name_size, "%s", enum_name); + if (actual_name) { + if (strcmp(actual_name, "") != 0) { + if (strcmp(name_data, actual_name) != 0) { + RegCloseKey (connection_key); + ++i; + continue; + } + } + else { + snprintf(actual_name, actual_name_size, "%s", name_data); + } + } + stop = 1; + } + } + + RegCloseKey (connection_key); + } + ++i; + } + + RegCloseKey (control_net_key); + + if (stop == 0) + return -1; + + return 0; +} + +static int tap_win32_set_status(HANDLE handle, int status) +{ + unsigned long len = 0; + + return DeviceIoControl(handle, TAP_IOCTL_SET_MEDIA_STATUS, + &status, sizeof (status), + &status, sizeof (status), &len, NULL); +} + +static void tap_win32_overlapped_init(tap_win32_overlapped_t* const overlapped, const HANDLE handle) +{ + overlapped->handle = handle; + + overlapped->read_event = CreateEvent(NULL, FALSE, FALSE, NULL); + overlapped->write_event = CreateEvent(NULL, FALSE, FALSE, NULL); + + overlapped->read_overlapped.Offset = 0; + overlapped->read_overlapped.OffsetHigh = 0; + overlapped->read_overlapped.hEvent = overlapped->read_event; + + overlapped->write_overlapped.Offset = 0; + overlapped->write_overlapped.OffsetHigh = 0; + overlapped->write_overlapped.hEvent = overlapped->write_event; + + InitializeCriticalSection(&overlapped->output_queue_cs); + InitializeCriticalSection(&overlapped->free_list_cs); + + overlapped->output_queue_semaphore = CreateSemaphore( + NULL, // default security attributes + 0, // initial count + TUN_MAX_BUFFER_COUNT, // maximum count + NULL); // unnamed semaphore + + if(!overlapped->output_queue_semaphore) { + fprintf(stderr, "error creating output queue semaphore!\n"); + } + + overlapped->free_list_semaphore = CreateSemaphore( + NULL, // default security attributes + TUN_MAX_BUFFER_COUNT, // initial count + TUN_MAX_BUFFER_COUNT, // maximum count + NULL); // unnamed semaphore + + if(!overlapped->free_list_semaphore) { + fprintf(stderr, "error creating free list semaphore!\n"); + } + + overlapped->free_list = overlapped->output_queue_front = overlapped->output_queue_back = NULL; + + { + unsigned index; + for(index = 0; index < TUN_MAX_BUFFER_COUNT; index++) { + tun_buffer_t* element = &overlapped->buffers[index]; + element->next = overlapped->free_list; + overlapped->free_list = element; + } + } + /* To count buffers, initially no-signal. */ + overlapped->tap_semaphore = CreateSemaphore(NULL, 0, TUN_MAX_BUFFER_COUNT, NULL); + if(!overlapped->tap_semaphore) + fprintf(stderr, "error creating tap_semaphore.\n"); +} + +static int tap_win32_write(tap_win32_overlapped_t *overlapped, + const void *buffer, unsigned long size) +{ + unsigned long write_size; + BOOL result; + DWORD error; + + result = GetOverlappedResult( overlapped->handle, &overlapped->write_overlapped, + &write_size, FALSE); + + if (!result && GetLastError() == ERROR_IO_INCOMPLETE) + WaitForSingleObject(overlapped->write_event, INFINITE); + + result = WriteFile(overlapped->handle, buffer, size, + &write_size, &overlapped->write_overlapped); + + if (!result) { + switch (error = GetLastError()) + { + case ERROR_IO_PENDING: +#ifndef TUN_ASYNCHRONOUS_WRITES + WaitForSingleObject(overlapped->write_event, INFINITE); +#endif + break; + default: + return -1; + } + } + + return 0; +} + +static DWORD WINAPI tap_win32_thread_entry(LPVOID param) +{ + tap_win32_overlapped_t *overlapped = (tap_win32_overlapped_t*)param; + unsigned long read_size; + BOOL result; + DWORD dwError; + tun_buffer_t* buffer = get_buffer_from_free_list(overlapped); + + + for (;;) { + result = ReadFile(overlapped->handle, + buffer->buffer, + sizeof(buffer->buffer), + &read_size, + &overlapped->read_overlapped); + if (!result) { + dwError = GetLastError(); + if (dwError == ERROR_IO_PENDING) { + WaitForSingleObject(overlapped->read_event, INFINITE); + result = GetOverlappedResult( overlapped->handle, &overlapped->read_overlapped, + &read_size, FALSE); + if (!result) { +#ifdef DEBUG_TAP_WIN32 + LPVOID lpBuffer; + dwError = GetLastError(); + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) & lpBuffer, 0, NULL ); + fprintf(stderr, "Tap-Win32: Error GetOverlappedResult %d - %s\n", dwError, lpBuffer); + LocalFree( lpBuffer ); +#endif + } + } else { +#ifdef DEBUG_TAP_WIN32 + LPVOID lpBuffer; + FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) & lpBuffer, 0, NULL ); + fprintf(stderr, "Tap-Win32: Error ReadFile %d - %s\n", dwError, lpBuffer); + LocalFree( lpBuffer ); +#endif + } + } + + if(read_size > 0) { + buffer->read_size = read_size; + put_buffer_on_output_queue(overlapped, buffer); + ReleaseSemaphore(overlapped->tap_semaphore, 1, NULL); + buffer = get_buffer_from_free_list(overlapped); + } + } + + return 0; +} + +static int tap_win32_read(tap_win32_overlapped_t *overlapped, + uint8_t **pbuf, int max_size) +{ + int size = 0; + + tun_buffer_t* buffer = get_buffer_from_output_queue_immediate(overlapped); + + if(buffer != NULL) { + *pbuf = buffer->buffer; + size = (int)buffer->read_size; + if(size > max_size) { + size = max_size; + } + } + + return size; +} + +static void tap_win32_free_buffer(tap_win32_overlapped_t *overlapped, + uint8_t *pbuf) +{ + tun_buffer_t* buffer = (tun_buffer_t*)pbuf; + put_buffer_on_free_list(overlapped, buffer); +} + +static int tap_win32_open(tap_win32_overlapped_t **phandle, + const char *prefered_name) +{ + char device_path[256]; + char device_guid[0x100]; + int rc; + HANDLE handle; + BOOL bret; + char name_buffer[0x100] = {0, }; + struct { + unsigned long major; + unsigned long minor; + unsigned long debug; + } version; + DWORD version_len; + DWORD idThread; + + if (prefered_name != NULL) + snprintf(name_buffer, sizeof(name_buffer), "%s", prefered_name); + + rc = get_device_guid(device_guid, sizeof(device_guid), name_buffer, sizeof(name_buffer)); + if (rc) + return -1; + + snprintf (device_path, sizeof(device_path), "%s%s%s", + USERMODEDEVICEDIR, + device_guid, + TAPSUFFIX); + + handle = CreateFile ( + device_path, + GENERIC_READ | GENERIC_WRITE, + 0, + 0, + OPEN_EXISTING, + FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, + 0 ); + + if (handle == INVALID_HANDLE_VALUE) { + return -1; + } + + bret = DeviceIoControl(handle, TAP_IOCTL_GET_VERSION, + &version, sizeof (version), + &version, sizeof (version), &version_len, NULL); + + if (bret == FALSE) { + CloseHandle(handle); + return -1; + } + + if (!tap_win32_set_status(handle, TRUE)) { + return -1; + } + + tap_win32_overlapped_init(&tap_overlapped, handle); + + *phandle = &tap_overlapped; + + CreateThread(NULL, 0, tap_win32_thread_entry, + (LPVOID)&tap_overlapped, 0, &idThread); + return 0; +} + +/********************************************/ + + typedef struct TAPState { + VLANClientState nc; + tap_win32_overlapped_t *handle; + } TAPState; + +static void tap_cleanup(VLANClientState *nc) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + + qemu_del_wait_object(s->handle->tap_semaphore, NULL, NULL); + + /* FIXME: need to kill thread and close file handle: + tap_win32_close(s); + */ +} + +static ssize_t tap_receive(VLANClientState *nc, const uint8_t *buf, size_t size) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + + return tap_win32_write(s->handle, buf, size); +} + +static void tap_win32_send(void *opaque) +{ + TAPState *s = opaque; + uint8_t *buf; + int max_size = 4096; + int size; + + size = tap_win32_read(s->handle, &buf, max_size); + if (size > 0) { + qemu_send_packet(&s->nc, buf, size); + tap_win32_free_buffer(s->handle, buf); + } +} + +static NetClientInfo net_tap_win32_info = { + .type = NET_CLIENT_TYPE_TAP, + .size = sizeof(TAPState), + .receive = tap_receive, + .cleanup = tap_cleanup, +}; + +static int tap_win32_init(VLANState *vlan, const char *model, + const char *name, const char *ifname) +{ + VLANClientState *nc; + TAPState *s; + tap_win32_overlapped_t *handle; + + if (tap_win32_open(&handle, ifname) < 0) { + printf("tap: Could not open '%s'\n", ifname); + return -1; + } + + nc = qemu_new_net_client(&net_tap_win32_info, vlan, NULL, model, name); + + s = DO_UPCAST(TAPState, nc, nc); + + snprintf(s->nc.info_str, sizeof(s->nc.info_str), + "tap: ifname=%s", ifname); + + s->handle = handle; + + qemu_add_wait_object(s->handle->tap_semaphore, tap_win32_send, s); + + return 0; +} + +int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan) +{ + const char *ifname; + + ifname = qemu_opt_get(opts, "ifname"); + + if (!ifname) { + error_report("tap: no interface name"); + return -1; + } + + if (tap_win32_init(vlan, "tap", name, ifname) == -1) { + return -1; + } + + return 0; +} + +int tap_has_ufo(VLANClientState *vc) +{ + return 0; +} + +int tap_has_vnet_hdr(VLANClientState *vc) +{ + return 0; +} + +int tap_probe_vnet_hdr_len(int fd, int len) +{ + return 0; +} + +void tap_fd_set_vnet_hdr_len(int fd, int len) +{ +} + +void tap_using_vnet_hdr(VLANClientState *vc, int using_vnet_hdr) +{ +} + +void tap_set_offload(VLANClientState *vc, int csum, int tso4, + int tso6, int ecn, int ufo) +{ +} + +struct vhost_net *tap_get_vhost_net(VLANClientState *nc) +{ + return NULL; +} diff --git a/net/tap.c b/net/tap.c new file mode 100644 index 0000000..b8cd252 --- /dev/null +++ b/net/tap.c @@ -0,0 +1,525 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2009 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "net/tap.h" + +#include "config-host.h" + +#include <signal.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <sys/socket.h> +#include <net/if.h> + +#include "net.h" +#include "sysemu.h" +#include "qemu-char.h" +#include "qemu-common.h" +#include "qemu-error.h" + +#include "net/tap-linux.h" + +#include "hw/vhost_net.h" + +/* Maximum GSO packet size (64k) plus plenty of room for + * the ethernet and virtio_net headers + */ +#define TAP_BUFSIZE (4096 + 65536) + +typedef struct TAPState { + VLANClientState nc; + int fd; + char down_script[1024]; + char down_script_arg[128]; + uint8_t buf[TAP_BUFSIZE]; + unsigned int read_poll : 1; + unsigned int write_poll : 1; + unsigned int using_vnet_hdr : 1; + unsigned int has_ufo: 1; + VHostNetState *vhost_net; + unsigned host_vnet_hdr_len; +} TAPState; + +static int launch_script(const char *setup_script, const char *ifname, int fd); + +static int tap_can_send(void *opaque); +static void tap_send(void *opaque); +static void tap_writable(void *opaque); + +static void tap_update_fd_handler(TAPState *s) +{ + qemu_set_fd_handler2(s->fd, + s->read_poll ? tap_can_send : NULL, + s->read_poll ? tap_send : NULL, + s->write_poll ? tap_writable : NULL, + s); +} + +static void tap_read_poll(TAPState *s, int enable) +{ + s->read_poll = !!enable; + tap_update_fd_handler(s); +} + +static void tap_write_poll(TAPState *s, int enable) +{ + s->write_poll = !!enable; + tap_update_fd_handler(s); +} + +static void tap_writable(void *opaque) +{ + TAPState *s = opaque; + + tap_write_poll(s, 0); + + qemu_flush_queued_packets(&s->nc); +} + +static ssize_t tap_write_packet(TAPState *s, const struct iovec *iov, int iovcnt) +{ + ssize_t len; + + do { + len = writev(s->fd, iov, iovcnt); + } while (len == -1 && errno == EINTR); + + if (len == -1 && errno == EAGAIN) { + tap_write_poll(s, 1); + return 0; + } + + return len; +} + +static ssize_t tap_receive_iov(VLANClientState *nc, const struct iovec *iov, + int iovcnt) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + const struct iovec *iovp = iov; + struct iovec iov_copy[iovcnt + 1]; + struct virtio_net_hdr_mrg_rxbuf hdr = { }; + + if (s->host_vnet_hdr_len && !s->using_vnet_hdr) { + iov_copy[0].iov_base = &hdr; + iov_copy[0].iov_len = s->host_vnet_hdr_len; + memcpy(&iov_copy[1], iov, iovcnt * sizeof(*iov)); + iovp = iov_copy; + iovcnt++; + } + + return tap_write_packet(s, iovp, iovcnt); +} + +static ssize_t tap_receive_raw(VLANClientState *nc, const uint8_t *buf, size_t size) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + struct iovec iov[2]; + int iovcnt = 0; + struct virtio_net_hdr_mrg_rxbuf hdr = { }; + + if (s->host_vnet_hdr_len) { + iov[iovcnt].iov_base = &hdr; + iov[iovcnt].iov_len = s->host_vnet_hdr_len; + iovcnt++; + } + + iov[iovcnt].iov_base = (char *)buf; + iov[iovcnt].iov_len = size; + iovcnt++; + + return tap_write_packet(s, iov, iovcnt); +} + +static ssize_t tap_receive(VLANClientState *nc, const uint8_t *buf, size_t size) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + struct iovec iov[1]; + + if (s->host_vnet_hdr_len && !s->using_vnet_hdr) { + return tap_receive_raw(nc, buf, size); + } + + iov[0].iov_base = (char *)buf; + iov[0].iov_len = size; + + return tap_write_packet(s, iov, 1); +} + +static int tap_can_send(void *opaque) +{ + TAPState *s = opaque; + + return qemu_can_send_packet(&s->nc); +} + +#ifndef __sun__ +ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen) +{ + return read(tapfd, buf, maxlen); +} +#endif + +static void tap_send_completed(VLANClientState *nc, ssize_t len) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + tap_read_poll(s, 1); +} + +static void tap_send(void *opaque) +{ + TAPState *s = opaque; + int size; + + do { + uint8_t *buf = s->buf; + + size = tap_read_packet(s->fd, s->buf, sizeof(s->buf)); + if (size <= 0) { + break; + } + + if (s->host_vnet_hdr_len && !s->using_vnet_hdr) { + buf += s->host_vnet_hdr_len; + size -= s->host_vnet_hdr_len; + } + + size = qemu_send_packet_async(&s->nc, buf, size, tap_send_completed); + if (size == 0) { + tap_read_poll(s, 0); + } + } while (size > 0 && qemu_can_send_packet(&s->nc)); +} + +int tap_has_ufo(VLANClientState *nc) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + + assert(nc->info->type == NET_CLIENT_TYPE_TAP); + + return s->has_ufo; +} + +int tap_has_vnet_hdr(VLANClientState *nc) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + + assert(nc->info->type == NET_CLIENT_TYPE_TAP); + + return !!s->host_vnet_hdr_len; +} + +int tap_has_vnet_hdr_len(VLANClientState *nc, int len) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + + assert(nc->info->type == NET_CLIENT_TYPE_TAP); + + return tap_probe_vnet_hdr_len(s->fd, len); +} + +void tap_set_vnet_hdr_len(VLANClientState *nc, int len) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + + assert(nc->info->type == NET_CLIENT_TYPE_TAP); + assert(len == sizeof(struct virtio_net_hdr_mrg_rxbuf) || + len == sizeof(struct virtio_net_hdr)); + + tap_fd_set_vnet_hdr_len(s->fd, len); + s->host_vnet_hdr_len = len; +} + +void tap_using_vnet_hdr(VLANClientState *nc, int using_vnet_hdr) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + + using_vnet_hdr = using_vnet_hdr != 0; + + assert(nc->info->type == NET_CLIENT_TYPE_TAP); + assert(!!s->host_vnet_hdr_len == using_vnet_hdr); + + s->using_vnet_hdr = using_vnet_hdr; +} + +void tap_set_offload(VLANClientState *nc, int csum, int tso4, + int tso6, int ecn, int ufo) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + if (s->fd < 0) { + return; + } + + tap_fd_set_offload(s->fd, csum, tso4, tso6, ecn, ufo); +} + +static void tap_cleanup(VLANClientState *nc) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + + if (s->vhost_net) { + vhost_net_cleanup(s->vhost_net); + s->vhost_net = NULL; + } + + qemu_purge_queued_packets(nc); + + if (s->down_script[0]) + launch_script(s->down_script, s->down_script_arg, s->fd); + + tap_read_poll(s, 0); + tap_write_poll(s, 0); + close(s->fd); + s->fd = -1; +} + +static void tap_poll(VLANClientState *nc, bool enable) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + tap_read_poll(s, enable); + tap_write_poll(s, enable); +} + +int tap_get_fd(VLANClientState *nc) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + assert(nc->info->type == NET_CLIENT_TYPE_TAP); + return s->fd; +} + +/* fd support */ + +static NetClientInfo net_tap_info = { + .type = NET_CLIENT_TYPE_TAP, + .size = sizeof(TAPState), + .receive = tap_receive, + .receive_raw = tap_receive_raw, + .receive_iov = tap_receive_iov, + .poll = tap_poll, + .cleanup = tap_cleanup, +}; + +static TAPState *net_tap_fd_init(VLANState *vlan, + const char *model, + const char *name, + int fd, + int vnet_hdr) +{ + VLANClientState *nc; + TAPState *s; + + nc = qemu_new_net_client(&net_tap_info, vlan, NULL, model, name); + + s = DO_UPCAST(TAPState, nc, nc); + + s->fd = fd; + s->host_vnet_hdr_len = vnet_hdr ? sizeof(struct virtio_net_hdr) : 0; + s->using_vnet_hdr = 0; + s->has_ufo = tap_probe_has_ufo(s->fd); + tap_set_offload(&s->nc, 0, 0, 0, 0, 0); + tap_read_poll(s, 1); + s->vhost_net = NULL; + return s; +} + +static int launch_script(const char *setup_script, const char *ifname, int fd) +{ + sigset_t oldmask, mask; + int pid, status; + char *args[3]; + char **parg; + + sigemptyset(&mask); + sigaddset(&mask, SIGCHLD); + sigprocmask(SIG_BLOCK, &mask, &oldmask); + + /* try to launch network script */ + pid = fork(); + if (pid == 0) { + int open_max = sysconf(_SC_OPEN_MAX), i; + + for (i = 0; i < open_max; i++) { + if (i != STDIN_FILENO && + i != STDOUT_FILENO && + i != STDERR_FILENO && + i != fd) { + close(i); + } + } + parg = args; + *parg++ = (char *)setup_script; + *parg++ = (char *)ifname; + *parg = NULL; + execv(setup_script, args); + _exit(1); + } else if (pid > 0) { + while (waitpid(pid, &status, 0) != pid) { + /* loop */ + } + sigprocmask(SIG_SETMASK, &oldmask, NULL); + + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { + return 0; + } + } + fprintf(stderr, "%s: could not launch network script\n", setup_script); + return -1; +} + +static int net_tap_init(QemuOpts *opts, int *vnet_hdr) +{ + int fd, vnet_hdr_required; + char ifname[128] = {0,}; + const char *setup_script; + + if (qemu_opt_get(opts, "ifname")) { + pstrcpy(ifname, sizeof(ifname), qemu_opt_get(opts, "ifname")); + } + + *vnet_hdr = qemu_opt_get_bool(opts, "vnet_hdr", 1); + if (qemu_opt_get(opts, "vnet_hdr")) { + vnet_hdr_required = *vnet_hdr; + } else { + vnet_hdr_required = 0; + } + + TFR(fd = tap_open(ifname, sizeof(ifname), vnet_hdr, vnet_hdr_required)); + if (fd < 0) { + return -1; + } + + setup_script = qemu_opt_get(opts, "script"); + if (setup_script && + setup_script[0] != '\0' && + strcmp(setup_script, "no") != 0 && + launch_script(setup_script, ifname, fd)) { + close(fd); + return -1; + } + + qemu_opt_set(opts, "ifname", ifname); + + return fd; +} + +int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan) +{ + TAPState *s; + int fd, vnet_hdr = 0; + + if (qemu_opt_get(opts, "fd")) { + if (qemu_opt_get(opts, "ifname") || + qemu_opt_get(opts, "script") || + qemu_opt_get(opts, "downscript") || + qemu_opt_get(opts, "vnet_hdr")) { + error_report("ifname=, script=, downscript= and vnet_hdr= is invalid with fd="); + return -1; + } + + fd = net_handle_fd_param(mon, qemu_opt_get(opts, "fd")); + if (fd == -1) { + return -1; + } + + fcntl(fd, F_SETFL, O_NONBLOCK); + + vnet_hdr = tap_probe_vnet_hdr(fd); + } else { + if (!qemu_opt_get(opts, "script")) { + qemu_opt_set(opts, "script", DEFAULT_NETWORK_SCRIPT); + } + + if (!qemu_opt_get(opts, "downscript")) { + qemu_opt_set(opts, "downscript", DEFAULT_NETWORK_DOWN_SCRIPT); + } + + fd = net_tap_init(opts, &vnet_hdr); + if (fd == -1) { + return -1; + } + } + + s = net_tap_fd_init(vlan, "tap", name, fd, vnet_hdr); + if (!s) { + close(fd); + return -1; + } + + if (tap_set_sndbuf(s->fd, opts) < 0) { + return -1; + } + + if (qemu_opt_get(opts, "fd")) { + snprintf(s->nc.info_str, sizeof(s->nc.info_str), "fd=%d", fd); + } else { + const char *ifname, *script, *downscript; + + ifname = qemu_opt_get(opts, "ifname"); + script = qemu_opt_get(opts, "script"); + downscript = qemu_opt_get(opts, "downscript"); + + snprintf(s->nc.info_str, sizeof(s->nc.info_str), + "ifname=%s,script=%s,downscript=%s", + ifname, script, downscript); + + if (strcmp(downscript, "no") != 0) { + snprintf(s->down_script, sizeof(s->down_script), "%s", downscript); + snprintf(s->down_script_arg, sizeof(s->down_script_arg), "%s", ifname); + } + } + + if (qemu_opt_get_bool(opts, "vhost", !!qemu_opt_get(opts, "vhostfd") || + qemu_opt_get_bool(opts, "vhostforce", false))) { + int vhostfd, r; + bool force = qemu_opt_get_bool(opts, "vhostforce", false); + if (qemu_opt_get(opts, "vhostfd")) { + r = net_handle_fd_param(mon, qemu_opt_get(opts, "vhostfd")); + if (r == -1) { + return -1; + } + vhostfd = r; + } else { + vhostfd = -1; + } + s->vhost_net = vhost_net_init(&s->nc, vhostfd, force); + if (!s->vhost_net) { + error_report("vhost-net requested but could not be initialized"); + return -1; + } + } else if (qemu_opt_get(opts, "vhostfd")) { + error_report("vhostfd= is not valid without vhost"); + return -1; + } + + return 0; +} + +VHostNetState *tap_get_vhost_net(VLANClientState *nc) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + assert(nc->info->type == NET_CLIENT_TYPE_TAP); + return s->vhost_net; +} diff --git a/net/tap.h b/net/tap.h new file mode 100644 index 0000000..e44bd2b --- /dev/null +++ b/net/tap.h @@ -0,0 +1,60 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2009 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef QEMU_NET_TAP_H +#define QEMU_NET_TAP_H + +#include "qemu-common.h" +#include "qemu-option.h" + +#define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup" +#define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/qemu-ifdown" + +int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan); + +int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required); + +ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen); + +int tap_has_ufo(VLANClientState *vc); +int tap_has_vnet_hdr(VLANClientState *vc); +int tap_has_vnet_hdr_len(VLANClientState *vc, int len); +void tap_using_vnet_hdr(VLANClientState *vc, int using_vnet_hdr); +void tap_set_offload(VLANClientState *vc, int csum, int tso4, int tso6, int ecn, int ufo); +void tap_set_vnet_hdr_len(VLANClientState *vc, int len); + +int tap_set_sndbuf(int fd, QemuOpts *opts); +int tap_probe_vnet_hdr(int fd); +int tap_probe_vnet_hdr_len(int fd, int len); +int tap_probe_has_ufo(int fd); +void tap_fd_set_offload(int fd, int csum, int tso4, int tso6, int ecn, int ufo); +void tap_fd_set_vnet_hdr_len(int fd, int len); + +int tap_get_fd(VLANClientState *vc); + +struct vhost_net; +struct vhost_net *tap_get_vhost_net(VLANClientState *vc); + +#endif /* QEMU_NET_TAP_H */ diff --git a/net/util.c b/net/util.c new file mode 100644 index 0000000..1e9afbc --- /dev/null +++ b/net/util.c @@ -0,0 +1,60 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "net/util.h" +#include <errno.h> +#include <stdlib.h> + +int net_parse_macaddr(uint8_t *macaddr, const char *p) +{ + int i; + char *last_char; + long int offset; + + errno = 0; + offset = strtol(p, &last_char, 0); + if (errno == 0 && *last_char == '\0' && + offset >= 0 && offset <= 0xFFFFFF) { + macaddr[3] = (offset & 0xFF0000) >> 16; + macaddr[4] = (offset & 0xFF00) >> 8; + macaddr[5] = offset & 0xFF; + return 0; + } + + for (i = 0; i < 6; i++) { + macaddr[i] = strtol(p, (char **)&p, 16); + if (i == 5) { + if (*p != '\0') { + return -1; + } + } else { + if (*p != ':' && *p != '-') { + return -1; + } + p++; + } + } + + return 0; +} diff --git a/net/util.h b/net/util.h new file mode 100644 index 0000000..10c7da9 --- /dev/null +++ b/net/util.h @@ -0,0 +1,32 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef QEMU_NET_UTIL_H +#define QEMU_NET_UTIL_H + +#include <stdint.h> + +int net_parse_macaddr(uint8_t *macaddr, const char *p); + +#endif /* QEMU_NET_UTIL_H */ diff --git a/net/vde.c b/net/vde.c new file mode 100644 index 0000000..0b46fa6 --- /dev/null +++ b/net/vde.c @@ -0,0 +1,131 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "net/vde.h" + +#include "config-host.h" + +#include <libvdeplug.h> + +#include "net.h" +#include "qemu-char.h" +#include "qemu-common.h" +#include "qemu-option.h" +#include "sysemu.h" + +typedef struct VDEState { + VLANClientState nc; + VDECONN *vde; +} VDEState; + +static void vde_to_qemu(void *opaque) +{ + VDEState *s = opaque; + uint8_t buf[4096]; + int size; + + size = vde_recv(s->vde, (char *)buf, sizeof(buf), 0); + if (size > 0) { + qemu_send_packet(&s->nc, buf, size); + } +} + +static ssize_t vde_receive(VLANClientState *nc, const uint8_t *buf, size_t size) +{ + VDEState *s = DO_UPCAST(VDEState, nc, nc); + ssize_t ret; + + do { + ret = vde_send(s->vde, (const char *)buf, size, 0); + } while (ret < 0 && errno == EINTR); + + return ret; +} + +static void vde_cleanup(VLANClientState *nc) +{ + VDEState *s = DO_UPCAST(VDEState, nc, nc); + qemu_set_fd_handler(vde_datafd(s->vde), NULL, NULL, NULL); + vde_close(s->vde); +} + +static NetClientInfo net_vde_info = { + .type = NET_CLIENT_TYPE_VDE, + .size = sizeof(VDEState), + .receive = vde_receive, + .cleanup = vde_cleanup, +}; + +static int net_vde_init(VLANState *vlan, const char *model, + const char *name, const char *sock, + int port, const char *group, int mode) +{ + VLANClientState *nc; + VDEState *s; + VDECONN *vde; + char *init_group = (char *)group; + char *init_sock = (char *)sock; + + struct vde_open_args args = { + .port = port, + .group = init_group, + .mode = mode, + }; + + vde = vde_open(init_sock, (char *)"QEMU", &args); + if (!vde){ + return -1; + } + + nc = qemu_new_net_client(&net_vde_info, vlan, NULL, model, name); + + snprintf(nc->info_str, sizeof(nc->info_str), "sock=%s,fd=%d", + sock, vde_datafd(vde)); + + s = DO_UPCAST(VDEState, nc, nc); + + s->vde = vde; + + qemu_set_fd_handler(vde_datafd(s->vde), vde_to_qemu, NULL, s); + + return 0; +} + +int net_init_vde(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan) +{ + const char *sock; + const char *group; + int port, mode; + + sock = qemu_opt_get(opts, "sock"); + group = qemu_opt_get(opts, "group"); + + port = qemu_opt_get_number(opts, "port", 0); + mode = qemu_opt_get_number(opts, "mode", 0700); + + if (net_vde_init(vlan, "vde", name, sock, port, group, mode) == -1) { + return -1; + } + + return 0; +} diff --git a/net/vde.h b/net/vde.h new file mode 100644 index 0000000..3e6ca3e --- /dev/null +++ b/net/vde.h @@ -0,0 +1,36 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef QEMU_NET_VDE_H +#define QEMU_NET_VDE_H + +#include "qemu-common.h" +#include "qemu-option.h" + +#ifdef CONFIG_VDE + +int net_init_vde(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan); + +#endif /* CONFIG_VDE */ + +#endif /* QEMU_NET_VDE_H */ |