From b7166ee48e1e6fec70bbb447420524682ca8685e Mon Sep 17 00:00:00 2001 From: aliguori Date: Tue, 21 Apr 2009 19:56:28 +0000 Subject: [PATCH] net: Add support for capturing VLANs (Jan Kiszka) This patch is derived from Tristan Gingold's patch. It adds a new VLAN client type that writes all traffic on the VLAN it is attached to into a pcap file. Such a file can then be analyzed offline with Wireshark or tcpdump. Besides rebasing and some minor cleanups, the major differences to the original version are: - support for enabling/disabling via the monitor (host_net_add/remove) - no special ordering of VLAN client list, qemu_send_packet now takes care of properly ordered packets - 64k default capturing limit (I hate tcpdump's default) Signed-off-by: Jan Kiszka Signed-off-by: Anthony Liguori git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@7200 c046a42c-6fe2-441c-8c8c-71466251a162 --- monitor.c | 2 +- net.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- qemu-options.hx | 7 ++++ 3 files changed, 121 insertions(+), 3 deletions(-) diff --git a/monitor.c b/monitor.c index e7a4dc7..b323af9 100644 --- a/monitor.c +++ b/monitor.c @@ -1731,7 +1731,7 @@ static const mon_cmd_t mon_cmds[] = { { "pci_add", "sss", pci_device_hot_add, "pci_addr=auto|[[:]:] nic|storage [[vlan=n][,macaddr=addr][,model=type]] [file=file][,if=type][,bus=nr]...", "hot-add PCI device" }, { "pci_del", "s", pci_device_hot_remove, "pci_addr=[[:]:]", "hot remove PCI device" }, { "host_net_add", "ss", net_host_device_add, - "[tap,user,socket,vde] options", "add host VLAN client" }, + "[tap,user,socket,vde,dump] options", "add host VLAN client" }, { "host_net_remove", "is", net_host_device_remove, "vlan_id name", "remove host VLAN client" }, #endif diff --git a/net.c b/net.c index c6b196f..7139f4f 100644 --- a/net.c +++ b/net.c @@ -118,6 +118,7 @@ #include "qemu-char.h" #include "audio/audio.h" #include "qemu_socket.h" +#include "qemu-log.h" #if defined(CONFIG_SLIRP) #include "libslirp.h" @@ -1558,6 +1559,106 @@ static int net_socket_mcast_init(VLANState *vlan, } +typedef struct DumpState { + VLANClientState *pcap_vc; + 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 void dump_receive(void *opaque, const uint8_t *buf, int size) +{ + DumpState *s = opaque; + struct pcap_sf_pkthdr hdr; + int64_t ts; + int caplen; + + /* Early return in case of previous error. */ + if (s->fd < 0) { + return; + } + + ts = muldiv64 (qemu_get_clock(vm_clock),1000000, ticks_per_sec); + caplen = size > s->pcap_caplen ? s->pcap_caplen : size; + + hdr.ts.tv_sec = ts / 1000000000LL; + 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; + } +} + +static void net_dump_cleanup(VLANClientState *vc) +{ + DumpState *s = vc->opaque; + + close(s->fd); + qemu_free(s); +} + +static int net_dump_init(VLANState *vlan, const char *device, + const char *name, const char *filename, int len) +{ + struct pcap_file_hdr hdr; + DumpState *s; + + s = qemu_malloc(sizeof(DumpState)); + + s->fd = open(filename, O_CREAT | O_WRONLY, 0644); + if (s->fd < 0) { + fprintf(stderr, "-net dump: can't open %s\n", filename); + return -1; + } + + s->pcap_caplen = len; + + hdr.magic = PCAP_MAGIC; + hdr.version_major = 2; + hdr.version_minor = 4; + hdr.thiszone = 0; + hdr.sigfigs = 0; + hdr.snaplen = s->pcap_caplen; + hdr.linktype = 1; + + if (write(s->fd, &hdr, sizeof(hdr)) < sizeof(hdr)) { + perror("-net dump write error"); + close(s->fd); + qemu_free(s); + return -1; + } + + s->pcap_vc = qemu_new_vlan_client(vlan, device, name, dump_receive, NULL, + net_dump_cleanup, s); + snprintf(s->pcap_vc->info_str, sizeof(s->pcap_vc->info_str), + "dump to %s (len=%d)", filename, len); + return 0; +} + /* find or alloc a new VLAN */ VLANState *qemu_find_vlan(int id) { @@ -1883,7 +1984,17 @@ int net_client_init(const char *device, const char *p) ret = net_vde_init(vlan, device, name, vde_sock, vde_port, vde_group, vde_mode); } else #endif - { + if (!strcmp(device, "dump")) { + int len = 65536; + + if (get_param_value(buf, sizeof(buf), "len", p) > 0) { + len = strtol(buf, NULL, 0); + } + if (!get_param_value(buf, sizeof(buf), "file", p)) { + snprintf(buf, sizeof(buf), "qemu-vlan%d.pcap", vlan_id); + } + ret = net_dump_init(vlan, device, name, buf, len); + } else { fprintf(stderr, "Unknown network device: %s\n", device); ret = -1; goto out; @@ -1908,7 +2019,7 @@ void net_client_uninit(NICInfo *nd) static int net_host_check_device(const char *device) { int i; - const char *valid_param_list[] = { "tap", "socket" + const char *valid_param_list[] = { "tap", "socket", "dump" #ifdef CONFIG_SLIRP ,"user" #endif diff --git a/qemu-options.hx b/qemu-options.hx index 1d783e5..718b10a 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -745,6 +745,8 @@ DEF("net", HAS_ARG, QEMU_OPTION_net, \ " Use group 'groupname' and mode 'octalmode' to change default\n" " ownership and permissions for communication port.\n" #endif + "-net dump[,vlan=n][,file=f][,len=n]\n" + " dump traffic on vlan 'n' to file 'f' (max n bytes per packet)\n" "-net none use it alone to have zero network devices; if no -net option\n" " is provided, the default is '-net nic -net user'\n") STEXI @@ -865,6 +867,11 @@ vde_switch -F -sock /tmp/myswitch qemu linux.img -net nic -net vde,sock=/tmp/myswitch @end example +@item -net dump[,vlan=@var{n}][,file=@var{file}][,len=@var{len}] +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. + @item -net none Indicate that no network devices should be configured. It is used to override the default configuration (@option{-net nic -net user}) which -- 2.7.4