dump.c 5.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/*
 * 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.
 */

25
#include "clients.h"
26
#include "qemu-common.h"
27
#include "qemu/error-report.h"
28
#include "qemu/iov.h"
29 30
#include "qemu/log.h"
#include "qemu/timer.h"
31
#include "hub.h"
32 33

typedef struct DumpState {
34
    int64_t start_ts;
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
    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;
};

60
static ssize_t dump_receive_iov(DumpState *s, const struct iovec *iov, int cnt)
61 62 63 64
{
    struct pcap_sf_pkthdr hdr;
    int64_t ts;
    int caplen;
65 66
    size_t size = iov_size(iov, cnt);
    struct iovec dumpiov[cnt + 1];
67 68 69 70 71 72

    /* Early return in case of previous error. */
    if (s->fd < 0) {
        return size;
    }

L
Laurent Vivier 已提交
73
    ts = qemu_clock_get_us(QEMU_CLOCK_VIRTUAL);
74 75
    caplen = size > s->pcap_caplen ? s->pcap_caplen : size;

76
    hdr.ts.tv_sec = ts / 1000000 + s->start_ts;
77 78 79
    hdr.ts.tv_usec = ts % 1000000;
    hdr.caplen = caplen;
    hdr.len = size;
80 81 82 83 84 85

    dumpiov[0].iov_base = &hdr;
    dumpiov[0].iov_len = sizeof(hdr);
    cnt = iov_copy(&dumpiov[1], cnt, iov, cnt, 0, caplen);

    if (writev(s->fd, dumpiov, cnt + 1) != sizeof(hdr) + caplen) {
86 87 88 89 90 91 92 93
        qemu_log("-net dump write error - stop dump\n");
        close(s->fd);
        s->fd = -1;
    }

    return size;
}

94
static void dump_cleanup(DumpState *s)
95 96
{
    close(s->fd);
97
    s->fd = -1;
98 99
}

100 101
static int net_dump_state_init(DumpState *s, const char *filename,
                               int len, Error **errp)
102 103
{
    struct pcap_file_hdr hdr;
104
    struct tm tm;
105
    int fd;
106

107
    fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0644);
108
    if (fd < 0) {
109
        error_setg_errno(errp, errno, "-net dump: can't open %s", filename);
110 111 112 113 114 115 116 117
        return -1;
    }

    hdr.magic = PCAP_MAGIC;
    hdr.version_major = 2;
    hdr.version_minor = 4;
    hdr.thiszone = 0;
    hdr.sigfigs = 0;
118
    hdr.snaplen = len;
119 120
    hdr.linktype = 1;

121
    if (write(fd, &hdr, sizeof(hdr)) < sizeof(hdr)) {
122
        error_setg_errno(errp, errno, "-net dump write error");
123
        close(fd);
124 125 126
        return -1;
    }

127 128 129
    s->fd = fd;
    s->pcap_caplen = len;

130 131 132
    qemu_get_timedate(&tm, 0);
    s->start_ts = mktime(&tm);

133 134 135
    return 0;
}

136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
/* Dumping via VLAN netclient */

struct DumpNetClient {
    NetClientState nc;
    DumpState ds;
};
typedef struct DumpNetClient DumpNetClient;

static ssize_t dumpclient_receive(NetClientState *nc, const uint8_t *buf,
                                  size_t size)
{
    DumpNetClient *dc = DO_UPCAST(DumpNetClient, nc, nc);
    struct iovec iov = {
        .iov_base = (void *)buf,
        .iov_len = size
    };

    return dump_receive_iov(&dc->ds, &iov, 1);
}

static ssize_t dumpclient_receive_iov(NetClientState *nc,
                                      const struct iovec *iov, int cnt)
{
    DumpNetClient *dc = DO_UPCAST(DumpNetClient, nc, nc);

    return dump_receive_iov(&dc->ds, iov, cnt);
}

static void dumpclient_cleanup(NetClientState *nc)
{
    DumpNetClient *dc = DO_UPCAST(DumpNetClient, nc, nc);

    dump_cleanup(&dc->ds);
}

static NetClientInfo net_dump_info = {
    .type = NET_CLIENT_OPTIONS_KIND_DUMP,
    .size = sizeof(DumpNetClient),
    .receive = dumpclient_receive,
    .receive_iov = dumpclient_receive_iov,
    .cleanup = dumpclient_cleanup,
};

179
int net_init_dump(const NetClientOptions *opts, const char *name,
180
                  NetClientState *peer, Error **errp)
181
{
182
    int len, rc;
183 184
    const char *file;
    char def_file[128];
185
    const NetdevDumpOptions *dump;
186
    NetClientState *nc;
187
    DumpNetClient *dnc;
188 189 190

    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_DUMP);
    dump = opts->dump;
191

192
    assert(peer);
193

194 195 196
    if (dump->has_file) {
        file = dump->file;
    } else {
197 198 199 200 201 202 203
        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);
204 205 206
        file = def_file;
    }

207 208
    if (dump->has_len) {
        if (dump->len > INT_MAX) {
209
            error_setg(errp, "invalid length: %"PRIu64, dump->len);
210 211 212 213 214 215
            return -1;
        }
        len = dump->len;
    } else {
        len = 65536;
    }
216

217 218 219 220
    nc = qemu_new_net_client(&net_dump_info, peer, "dump", name);
    snprintf(nc->info_str, sizeof(nc->info_str),
             "dump to %s (len=%d)", file, len);

221 222
    dnc = DO_UPCAST(DumpNetClient, nc, nc);
    rc = net_dump_state_init(&dnc->ds, file, len, errp);
223 224 225 226
    if (rc) {
        qemu_del_net_client(nc);
    }
    return rc;
227
}