From afb4c51fad8cf86104803fc17457b96e86172b98 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Mon, 12 Oct 2015 10:25:01 +0200 Subject: [PATCH 01/10] net: cadence_gem: Set initial MAC address Set initial MAC address to the one specified by the command line. Signed-off-by: Sebastian Huber Reviewed-by: Jason Wang Reviewed-by: Peter Crosthwaite Signed-off-by: Jason Wang --- hw/net/cadence_gem.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index 1127223cfdc3..3639fc17f030 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -964,6 +964,7 @@ static void gem_reset(DeviceState *d) { int i; CadenceGEMState *s = CADENCE_GEM(d); + const uint8_t *a; DB_PRINT("\n"); @@ -982,6 +983,11 @@ static void gem_reset(DeviceState *d) s->regs[GEM_DESCONF5] = 0x002f2145; s->regs[GEM_DESCONF6] = 0x00000200; + /* Set MAC address */ + a = &s->conf.macaddr.a[0]; + s->regs[GEM_SPADDR1LO] = a[0] | (a[1] << 8) | (a[2] << 16) | (a[3] << 24); + s->regs[GEM_SPADDR1HI] = a[4] | (a[5] << 8); + for (i = 0; i < 4; i++) { s->sar_active[i] = false; } From 43192fcc1ac0a61cf9e20a9034eae8d1e91f35c8 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 13 Oct 2015 12:39:58 +0200 Subject: [PATCH 02/10] net/dump: Add support for receive_iov function Adding a proper receive_iov function to the net dump module. This will make it easier to support the dump filter feature for the -netdev option in later patches. Reviewed-by: Yang Hongyang Signed-off-by: Thomas Huth Signed-off-by: Jason Wang --- net/dump.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/net/dump.c b/net/dump.c index 08259afcb64e..aa0d45ddc039 100644 --- a/net/dump.c +++ b/net/dump.c @@ -25,6 +25,7 @@ #include "clients.h" #include "qemu-common.h" #include "qemu/error-report.h" +#include "qemu/iov.h" #include "qemu/log.h" #include "qemu/timer.h" #include "hub.h" @@ -57,12 +58,15 @@ struct pcap_sf_pkthdr { uint32_t len; }; -static ssize_t dump_receive(NetClientState *nc, const uint8_t *buf, size_t size) +static ssize_t dump_receive_iov(NetClientState *nc, const struct iovec *iov, + int cnt) { DumpState *s = DO_UPCAST(DumpState, nc, nc); struct pcap_sf_pkthdr hdr; int64_t ts; int caplen; + size_t size = iov_size(iov, cnt); + struct iovec dumpiov[cnt + 1]; /* Early return in case of previous error. */ if (s->fd < 0) { @@ -76,8 +80,12 @@ static ssize_t dump_receive(NetClientState *nc, const uint8_t *buf, size_t size) 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) { + + 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) { qemu_log("-net dump write error - stop dump\n"); close(s->fd); s->fd = -1; @@ -86,6 +94,15 @@ static ssize_t dump_receive(NetClientState *nc, const uint8_t *buf, size_t size) return size; } +static ssize_t dump_receive(NetClientState *nc, const uint8_t *buf, size_t size) +{ + struct iovec iov = { + .iov_base = (void *)buf, + .iov_len = size + }; + return dump_receive_iov(nc, &iov, 1); +} + static void dump_cleanup(NetClientState *nc) { DumpState *s = DO_UPCAST(DumpState, nc, nc); @@ -97,6 +114,7 @@ static NetClientInfo net_dump_info = { .type = NET_CLIENT_OPTIONS_KIND_DUMP, .size = sizeof(DumpState), .receive = dump_receive, + .receive_iov = dump_receive_iov, .cleanup = dump_cleanup, }; From 7bc3074c27bb1eae7c8ccacd920ba55454381786 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 13 Oct 2015 12:39:59 +0200 Subject: [PATCH 03/10] net/dump: Rework net-dump init functions Move the creation of the dump client from net_dump_init() into net_init_dump(), so we can later use the former function for dump via netfilter, too. Also rename net_dump_init() to net_dump_state_init() to make it easier distinguishable from net_init_dump(). Reviewed-by: Yang Hongyang Signed-off-by: Thomas Huth Signed-off-by: Jason Wang --- net/dump.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/net/dump.c b/net/dump.c index aa0d45ddc039..e6f6be02f865 100644 --- a/net/dump.c +++ b/net/dump.c @@ -118,13 +118,10 @@ static NetClientInfo net_dump_info = { .cleanup = dump_cleanup, }; -static int net_dump_init(NetClientState *peer, const char *device, - const char *name, const char *filename, int len, - Error **errp) +static int net_dump_state_init(DumpState *s, const char *filename, + int len, Error **errp) { struct pcap_file_hdr hdr; - NetClientState *nc; - DumpState *s; struct tm tm; int fd; @@ -148,13 +145,6 @@ static int net_dump_init(NetClientState *peer, const char *device, 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; @@ -167,10 +157,11 @@ static int net_dump_init(NetClientState *peer, const char *device, int net_init_dump(const NetClientOptions *opts, const char *name, NetClientState *peer, Error **errp) { - int len; + int len, rc; const char *file; char def_file[128]; const NetdevDumpOptions *dump; + NetClientState *nc; assert(opts->kind == NET_CLIENT_OPTIONS_KIND_DUMP); dump = opts->dump; @@ -200,5 +191,13 @@ int net_init_dump(const NetClientOptions *opts, const char *name, len = 65536; } - return net_dump_init(peer, "dump", name, file, len, errp); + 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); + + rc = net_dump_state_init(DO_UPCAST(DumpState, nc, nc), file, len, errp); + if (rc) { + qemu_del_net_client(nc); + } + return rc; } From 75310e3486ab205d870560b75bbcaba72acb26d7 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 13 Oct 2015 12:40:00 +0200 Subject: [PATCH 04/10] net/dump: Separate the NetClientState from the DumpState With the upcoming dumping-via-netfilter patch, the DumpState should not be related to NetClientState anymore, so move the related information to a new struct called DumpNetClient. Reviewed-by: Yang Hongyang Signed-off-by: Thomas Huth Signed-off-by: Jason Wang --- net/dump.c | 74 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 25 deletions(-) diff --git a/net/dump.c b/net/dump.c index e6f6be02f865..e3e82bd69251 100644 --- a/net/dump.c +++ b/net/dump.c @@ -31,7 +31,6 @@ #include "hub.h" typedef struct DumpState { - NetClientState nc; int64_t start_ts; int fd; int pcap_caplen; @@ -58,10 +57,8 @@ struct pcap_sf_pkthdr { uint32_t len; }; -static ssize_t dump_receive_iov(NetClientState *nc, const struct iovec *iov, - int cnt) +static ssize_t dump_receive_iov(DumpState *s, const struct iovec *iov, int cnt) { - DumpState *s = DO_UPCAST(DumpState, nc, nc); struct pcap_sf_pkthdr hdr; int64_t ts; int caplen; @@ -94,30 +91,12 @@ static ssize_t dump_receive_iov(NetClientState *nc, const struct iovec *iov, return size; } -static ssize_t dump_receive(NetClientState *nc, const uint8_t *buf, size_t size) -{ - struct iovec iov = { - .iov_base = (void *)buf, - .iov_len = size - }; - return dump_receive_iov(nc, &iov, 1); -} - -static void dump_cleanup(NetClientState *nc) +static void dump_cleanup(DumpState *s) { - DumpState *s = DO_UPCAST(DumpState, nc, nc); - close(s->fd); + s->fd = -1; } -static NetClientInfo net_dump_info = { - .type = NET_CLIENT_OPTIONS_KIND_DUMP, - .size = sizeof(DumpState), - .receive = dump_receive, - .receive_iov = dump_receive_iov, - .cleanup = dump_cleanup, -}; - static int net_dump_state_init(DumpState *s, const char *filename, int len, Error **errp) { @@ -154,6 +133,49 @@ static int net_dump_state_init(DumpState *s, const char *filename, return 0; } +/* 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, +}; + int net_init_dump(const NetClientOptions *opts, const char *name, NetClientState *peer, Error **errp) { @@ -162,6 +184,7 @@ int net_init_dump(const NetClientOptions *opts, const char *name, char def_file[128]; const NetdevDumpOptions *dump; NetClientState *nc; + DumpNetClient *dnc; assert(opts->kind == NET_CLIENT_OPTIONS_KIND_DUMP); dump = opts->dump; @@ -195,7 +218,8 @@ int net_init_dump(const NetClientOptions *opts, const char *name, snprintf(nc->info_str, sizeof(nc->info_str), "dump to %s (len=%d)", file, len); - rc = net_dump_state_init(DO_UPCAST(DumpState, nc, nc), file, len, errp); + dnc = DO_UPCAST(DumpNetClient, nc, nc); + rc = net_dump_state_init(&dnc->ds, file, len, errp); if (rc) { qemu_del_net_client(nc); } From 9d3e12e881bc97bc32ac75d146b5347136f29ca1 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 13 Oct 2015 12:40:01 +0200 Subject: [PATCH 05/10] net/dump: Provide the dumping facility as a net-filter Use the net-filter infrastructure to provide the dumping functions for netdev devices, too. Reviewed-by: Yang Hongyang Signed-off-by: Thomas Huth Signed-off-by: Jason Wang --- net/dump.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++- vl.c | 7 ++- 2 files changed, 134 insertions(+), 2 deletions(-) diff --git a/net/dump.c b/net/dump.c index e3e82bd69251..dd0555f8bdd7 100644 --- a/net/dump.c +++ b/net/dump.c @@ -28,7 +28,8 @@ #include "qemu/iov.h" #include "qemu/log.h" #include "qemu/timer.h" -#include "hub.h" +#include "qapi/visitor.h" +#include "net/filter.h" typedef struct DumpState { int64_t start_ts; @@ -225,3 +226,129 @@ int net_init_dump(const NetClientOptions *opts, const char *name, } return rc; } + +/* Dumping via filter */ + +#define TYPE_FILTER_DUMP "filter-dump" + +#define FILTER_DUMP(obj) \ + OBJECT_CHECK(NetFilterDumpState, (obj), TYPE_FILTER_DUMP) + +struct NetFilterDumpState { + NetFilterState nfs; + DumpState ds; + char *filename; + uint32_t maxlen; +}; +typedef struct NetFilterDumpState NetFilterDumpState; + +static ssize_t filter_dump_receive_iov(NetFilterState *nf, NetClientState *sndr, + unsigned flags, const struct iovec *iov, + int iovcnt, NetPacketSent *sent_cb) +{ + NetFilterDumpState *nfds = FILTER_DUMP(nf); + + dump_receive_iov(&nfds->ds, iov, iovcnt); + return 0; +} + +static void filter_dump_cleanup(NetFilterState *nf) +{ + NetFilterDumpState *nfds = FILTER_DUMP(nf); + + dump_cleanup(&nfds->ds); +} + +static void filter_dump_setup(NetFilterState *nf, Error **errp) +{ + NetFilterDumpState *nfds = FILTER_DUMP(nf); + + if (!nfds->filename) { + error_setg(errp, "dump filter needs 'file' property set!"); + return; + } + + net_dump_state_init(&nfds->ds, nfds->filename, nfds->maxlen, errp); +} + +static void filter_dump_get_maxlen(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + NetFilterDumpState *nfds = FILTER_DUMP(obj); + uint32_t value = nfds->maxlen; + + visit_type_uint32(v, &value, name, errp); +} + +static void filter_dump_set_maxlen(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + NetFilterDumpState *nfds = FILTER_DUMP(obj); + Error *local_err = NULL; + uint32_t value; + + visit_type_uint32(v, &value, name, &local_err); + if (local_err) { + goto out; + } + if (value == 0) { + error_setg(&local_err, "Property '%s.%s' doesn't take value '%u'", + object_get_typename(obj), name, value); + goto out; + } + nfds->maxlen = value; + +out: + error_propagate(errp, local_err); +} + +static char *file_dump_get_filename(Object *obj, Error **errp) +{ + NetFilterDumpState *nfds = FILTER_DUMP(obj); + + return g_strdup(nfds->filename); +} + +static void file_dump_set_filename(Object *obj, const char *value, Error **errp) +{ + NetFilterDumpState *nfds = FILTER_DUMP(obj); + + g_free(nfds->filename); + nfds->filename = g_strdup(value); +} + +static void filter_dump_instance_init(Object *obj) +{ + NetFilterDumpState *nfds = FILTER_DUMP(obj); + + nfds->maxlen = 65536; + + object_property_add(obj, "maxlen", "int", filter_dump_get_maxlen, + filter_dump_set_maxlen, NULL, NULL, NULL); + object_property_add_str(obj, "file", file_dump_get_filename, + file_dump_set_filename, NULL); +} + +static void filter_dump_class_init(ObjectClass *oc, void *data) +{ + NetFilterClass *nfc = NETFILTER_CLASS(oc); + + nfc->setup = filter_dump_setup; + nfc->cleanup = filter_dump_cleanup; + nfc->receive_iov = filter_dump_receive_iov; +} + +static const TypeInfo filter_dump_info = { + .name = TYPE_FILTER_DUMP, + .parent = TYPE_NETFILTER, + .class_init = filter_dump_class_init, + .instance_init = filter_dump_instance_init, + .instance_size = sizeof(NetFilterDumpState), +}; + +static void filter_dump_register_types(void) +{ + type_register_static(&filter_dump_info); +} + +type_init(filter_dump_register_types); diff --git a/vl.c b/vl.c index dffaf09a44a0..f5f7c3f7c455 100644 --- a/vl.c +++ b/vl.c @@ -2769,7 +2769,12 @@ static bool object_create_initial(const char *type) return false; } - if (g_str_equal(type, "filter-buffer")) { + /* + * return false for concrete netfilters since + * they depend on netdevs already existing + */ + if (g_str_equal(type, "filter-buffer") || + g_str_equal(type, "filter-dump")) { return false; } From d3e0c032f52f4fb855f9bd2892ebd175a9d975a1 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 13 Oct 2015 12:40:02 +0200 Subject: [PATCH 06/10] options: Add documentation for filter-dump Add a short description for the filter-dump command line options. Signed-off-by: Thomas Huth Signed-off-by: Jason Wang --- qemu-options.hx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/qemu-options.hx b/qemu-options.hx index edee5f4844ea..949db7f2eabd 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -2012,6 +2012,7 @@ qemu -m 512 -object memory-backend-file,id=mem,size=512M,mem-path=/hugetlbfs,sha Dump network traffic on VLAN @var{n} to file @var{file} (@file{qemu-vlan0.pcap} by default). At most @var{len} bytes (64k by default) per packet are stored. The file format is libpcap, so it can be analyzed with tools such as tcpdump or Wireshark. +Note: For devices created with '-netdev', use '-object filter-dump,...' instead. @item -net none Indicate that no network devices should be configured. It is used to @@ -3665,6 +3666,13 @@ queue @var{all|rx|tx} is an option that can be applied to any netfilter. @option{tx}: the filter is attached to the transmit queue of the netdev, where it will receive packets sent by the netdev. +@item -object filter-dump,id=@var{id},netdev=@var{dev},file=@var{filename}][,maxlen=@var{len}] + +Dump the network traffic on netdev @var{dev} to the file specified by +@var{filename}. At most @var{len} bytes (64k by default) per packet are stored. +The file format is libpcap, so it can be analyzed with tools such as tcpdump +or Wireshark. + @end table ETEXI From eedeeeffd419ab149e0b0ad5fc4b7cf5e1db6274 Mon Sep 17 00:00:00 2001 From: Shmulik Ladkani Date: Thu, 15 Oct 2015 13:54:30 +0300 Subject: [PATCH 07/10] vmxnet3: Do not fill stats if device is inactive Guest OS may issue VMXNET3_CMD_GET_STATS even before device was activated (for example in linux, after insmod but prior net-dev open). Accessing shared descriptors prior device activation is illegal as the VMXNET3State structures have not been fully initialized. As a result, guest memory gets corrupted and may lead to guest OS crashes. Fix, by not filling the stats descriptors if device is inactive. Reported-by: Leonid Shatz Acked-by: Dmitry Fleytman Signed-off-by: Dana Rubin Signed-off-by: Shmulik Ladkani Signed-off-by: Jason Wang --- hw/net/vmxnet3.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index 3c5e10dd6de7..5e3a233237ed 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -1289,6 +1289,10 @@ static uint32_t vmxnet3_get_interrupt_config(VMXNET3State *s) static void vmxnet3_fill_stats(VMXNET3State *s) { int i; + + if (!s->device_active) + return; + for (i = 0; i < s->txq_num; i++) { cpu_physical_memory_write(s->txq_descr[i].tx_stats_pa, &s->txq_descr[i].txq_stats, From 5320c2caf43cc76748a1ffa0fdcaa9eb501d3fcd Mon Sep 17 00:00:00 2001 From: Vladislav Yasevich Date: Mon, 19 Oct 2015 09:04:38 -0400 Subject: [PATCH 08/10] net: Remove duplicate data from query-rx-filter on multiqueue net devices When responding to a query-rx-filter command on a multiqueue netdev, qemu reports the data for each queue. The data, however, is not per-queue, but per device and the same data is reported multiple times. This causes confusion and may also cause extra unnecessary processing when looking at the data. Commit 638fb14169 (net: Make qmp_query_rx_filter() with name argument more obvious) partially addresses this issue, by limiting the output when the name is specified. However, when the name is not specified, the issue still persists. Signed-off-by: Vladislav Yasevich Signed-off-by: Jason Wang --- net/net.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/net.c b/net/net.c index 3c68f3faa8a6..70a3576db9bd 100644 --- a/net/net.c +++ b/net/net.c @@ -1227,6 +1227,12 @@ RxFilterInfoList *qmp_query_rx_filter(bool has_name, const char *name, continue; } + /* only query information on queue 0 since the info is per nic, + * not per queue + */ + if (nc->queue_index != 0) + continue; + if (nc->info->query_rx_filter) { info = nc->info->query_rx_filter(nc); entry = g_malloc0(sizeof(*entry)); From edc981443d5bd23e01639c2fbba4fbc2dc30204f Mon Sep 17 00:00:00 2001 From: Yang Hongyang Date: Tue, 20 Oct 2015 09:51:25 +0800 Subject: [PATCH 09/10] net: make iov_to_buf take right size argument in nc_sendv_compat() We want "buf, sizeof(buf)" here. sizeof(buffer) is the size of a pointer, which is wrong. Thanks to Paolo for pointing it out. Signed-off-by: Yang Hongyang Cc: Jason Wang Cc: Paolo Bonzini Signed-off-by: Jason Wang --- net/net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/net.c b/net/net.c index 70a3576db9bd..2f2b39e09b4c 100644 --- a/net/net.c +++ b/net/net.c @@ -708,7 +708,7 @@ static ssize_t nc_sendv_compat(NetClientState *nc, const struct iovec *iov, offset = iov[0].iov_len; } else { buffer = buf; - offset = iov_to_buf(iov, iovcnt, 0, buffer, sizeof(buffer)); + offset = iov_to_buf(iov, iovcnt, 0, buf, sizeof(buf)); } if (flags & QEMU_NET_PACKET_FLAG_RAW && nc->info->receive_raw) { From a3e8a3f382363d5fd452cfc15f90a688d70023d9 Mon Sep 17 00:00:00 2001 From: Yang Hongyang Date: Tue, 20 Oct 2015 09:51:26 +0800 Subject: [PATCH 10/10] net: free the string returned by object_get_canonical_path_component The value returned from object_get_canonical_path_component must be freed. Signed-off-by: Yang Hongyang Cc: Jason Wang Cc: Paolo Bonzini Signed-off-by: Jason Wang --- net/net.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/net.c b/net/net.c index 2f2b39e09b4c..a3e9d1a9b3a7 100644 --- a/net/net.c +++ b/net/net.c @@ -1197,10 +1197,11 @@ void print_net_client(Monitor *mon, NetClientState *nc) monitor_printf(mon, "filters:\n"); } QTAILQ_FOREACH(nf, &nc->filters, next) { - monitor_printf(mon, " - %s: type=%s%s\n", - object_get_canonical_path_component(OBJECT(nf)), + char *path = object_get_canonical_path_component(OBJECT(nf)); + monitor_printf(mon, " - %s: type=%s%s\n", path, object_get_typename(OBJECT(nf)), nf->info_str); + g_free(path); } }