diff options
Diffstat (limited to 'qemu/net/dump.c')
-rw-r--r-- | qemu/net/dump.c | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/qemu/net/dump.c b/qemu/net/dump.c new file mode 100644 index 000000000..02c8064be --- /dev/null +++ b/qemu/net/dump.c @@ -0,0 +1,186 @@ +/* + * 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 "clients.h" +#include "qemu-common.h" +#include "qemu/error-report.h" +#include "qemu/log.h" +#include "qemu/timer.h" +#include "hub.h" + +typedef struct DumpState { + NetClientState nc; + int64_t start_ts; + 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(NetClientState *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_clock_get_ns(QEMU_CLOCK_VIRTUAL), 1000000, get_ticks_per_sec()); + caplen = size > s->pcap_caplen ? s->pcap_caplen : size; + + hdr.ts.tv_sec = ts / 1000000 + s->start_ts; + 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(NetClientState *nc) +{ + DumpState *s = DO_UPCAST(DumpState, nc, nc); + + close(s->fd); +} + +static NetClientInfo net_dump_info = { + .type = NET_CLIENT_OPTIONS_KIND_DUMP, + .size = sizeof(DumpState), + .receive = dump_receive, + .cleanup = dump_cleanup, +}; + +static int net_dump_init(NetClientState *peer, const char *device, + const char *name, const char *filename, int len, + Error **errp) +{ + struct pcap_file_hdr hdr; + NetClientState *nc; + DumpState *s; + struct tm tm; + int fd; + + fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0644); + if (fd < 0) { + error_setg_errno(errp, errno, "-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_setg_errno(errp, errno, "-net dump write error"); + close(fd); + return -1; + } + + nc = qemu_new_net_client(&net_dump_info, peer, 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; + + qemu_get_timedate(&tm, 0); + s->start_ts = mktime(&tm); + + return 0; +} + +int net_init_dump(const NetClientOptions *opts, const char *name, + NetClientState *peer, Error **errp) +{ + int len; + const char *file; + char def_file[128]; + const NetdevDumpOptions *dump; + + assert(opts->kind == NET_CLIENT_OPTIONS_KIND_DUMP); + dump = opts->dump; + + assert(peer); + + if (dump->has_file) { + file = dump->file; + } else { + int id; + int ret; + + ret = net_hub_id_for_client(peer, &id); + assert(ret == 0); /* peer must be on a hub */ + + snprintf(def_file, sizeof(def_file), "qemu-vlan%d.pcap", id); + file = def_file; + } + + if (dump->has_len) { + if (dump->len > INT_MAX) { + error_setg(errp, "invalid length: %"PRIu64, dump->len); + return -1; + } + len = dump->len; + } else { + len = 65536; + } + + return net_dump_init(peer, "dump", name, file, len, errp); +} |