qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [Patch] Add -net dump option
@ 2009-02-04 10:22 Tristan Gingold
  2009-02-06 18:53 ` Anthony Liguori
  2009-02-07  4:09 ` Luca Bigliardi
  0 siblings, 2 replies; 13+ messages in thread
From: Tristan Gingold @ 2009-02-04 10:22 UTC (permalink / raw)
  To: qemu-devel

Hi,

this patch add a new network pseudo nic that dump traffic to a tcpdump
compatible file.  I have found this feature useful to debug network protocols
with the user stack.

Signed-off-by: Tristan Gingold <gingold@adacore.com>
Tristan.


diff --git a/qemu-doc.texi b/qemu-doc.texi
index b2fa19e..73c133a 100644
--- a/qemu-doc.texi
+++ b/qemu-doc.texi
@@ -739,6 +739,15 @@ 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.tcpdump} by
+default).  At most @var{len} bytes (64 by default) by packet are stored.  The
+dump can be analyzed with tools such as tcpdump.
+
+For better results you'd better to use this option before the @samp{-net user}
+one as the user stack may send replies before the initial packet was propagated
+to all receivers.
+
 @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

diff --git a/net.c b/net.c
index 8d9b3de..872a17e 100644
--- a/net.c
+++ b/net.c
@@ -1064,6 +1064,75 @@ static int net_vde_init(VLANState *vlan, const char *model,
 }
 #endif
 
+static VLANClientState *tcpdump_vc;
+static FILE *tcpdump_file;
+static int tcpdump_caplen;
+
+#define TCPDUMP_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 tcpdump_receive(void *opaque, const uint8_t *buf, int size)
+{
+    struct pcap_sf_pkthdr hdr;
+    int64_t ts = muldiv64 (qemu_get_clock(vm_clock),1000000, ticks_per_sec);
+    int caplen = size > tcpdump_caplen ? tcpdump_caplen : size;
+
+    hdr.ts.tv_sec = ts / 1000000000LL;
+    hdr.ts.tv_usec = ts % 1000000;
+    hdr.caplen = caplen;
+    hdr.len = size;
+    fwrite(&hdr, sizeof(hdr), 1, tcpdump_file);
+    fwrite(buf, caplen, 1, tcpdump_file);
+}
+
+static int net_tcpdump_init(VLANState *vlan, const char *device,
+			    const char *name, const char *filename, int len)
+{
+    struct pcap_file_hdr hdr;
+
+    tcpdump_file = fopen(filename, "wb");
+    if (!tcpdump_file) {
+	fprintf(stderr, "-net dump: can't open %s\n", filename);
+	exit(1);
+    }
+
+    tcpdump_caplen = len;
+
+    hdr.magic = TCPDUMP_MAGIC;
+    hdr.version_major = 2;
+    hdr.version_minor = 4;
+    hdr.thiszone = 0;
+    hdr.sigfigs = 0;
+    hdr.snaplen = tcpdump_caplen;
+    hdr.linktype = 1;
+
+    fwrite(&hdr, sizeof(hdr), 1, tcpdump_file);
+
+    tcpdump_vc = qemu_new_vlan_client(vlan, device, name,
+				      tcpdump_receive, NULL, NULL);
+    snprintf(tcpdump_vc->info_str, sizeof(tcpdump_vc->info_str),
+	     "tcpdump to %s (len=%d)", filename, len);
+    return 0;
+}
+
 /* network connection */
 typedef struct NetSocketState {
     VLANClientState *vc;
@@ -1701,6 +1770,18 @@ 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 = 64;
+        if (get_param_value(buf, sizeof(buf), "len", p) > 0) {
+            len = strtol(buf, NULL, 0);
+	}
+
+        if (!get_param_value(buf, sizeof(buf), "file", p)) {
+	    strcpy(buf, "qemu.tcpdump");
+        }
+        vlan->nb_host_devs++;
+        ret = net_tcpdump_init(vlan, device, name, buf, len);
+    } else
     {
         fprintf(stderr, "Unknown network device: %s\n", device);
         if (name)

diff --git a/vl.c b/vl.c
index 3676537..69dbe63 100644
--- a/vl.c
+++ b/vl.c
@@ -3943,6 +3943,8 @@ static void help(int exitcode)
            "                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"
 #ifdef CONFIG_SLIRP

^ permalink raw reply related	[flat|nested] 13+ messages in thread
* [Qemu-devel] [Patch] Add -net dump option
@ 2009-03-05 10:10 Tristan Gingold
  0 siblings, 0 replies; 13+ messages in thread
From: Tristan Gingold @ 2009-03-05 10:10 UTC (permalink / raw)
  To: qemu-devel; +Cc: aliguori

Hi,

latest version of the patch inlined.

Signed-off-by: Tristan Gingold <gingold@adacore.com>

Thanks,
Tristan.


Index: vl.c
===================================================================
--- vl.c	(revision 6685)
+++ vl.c	(working copy)
@@ -3987,6 +3987,8 @@
            "                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"
 #ifdef CONFIG_SLIRP
Index: net.c
===================================================================
--- net.c	(revision 6685)
+++ net.c	(working copy)
@@ -27,6 +27,7 @@
 #include "sysemu.h"
 #include "qemu-timer.h"
 #include "qemu-char.h"
+#include "qemu-log.h"
 #include "audio/audio.h"
 
 #include <unistd.h>
@@ -324,14 +325,14 @@
     return strdup(buf);
 }
 
-VLANClientState *qemu_new_vlan_client(VLANState *vlan,
-                                      const char *model,
-                                      const char *name,
-                                      IOReadHandler *fd_read,
-                                      IOCanRWHandler *fd_can_read,
-                                      void *opaque)
+static VLANClientState *qemu_new_vlan_client_common(VLANState *vlan,
+						    const char *model,
+						    const char *name,
+						    IOReadHandler *fd_read,
+						    IOCanRWHandler *fd_can_read,
+						    void *opaque)
 {
-    VLANClientState *vc, **pvc;
+    VLANClientState *vc;
     vc = qemu_mallocz(sizeof(VLANClientState));
     vc->model = strdup(model);
     if (name)
@@ -343,14 +344,58 @@
     vc->opaque = opaque;
     vc->vlan = vlan;
 
-    vc->next = NULL;
+    return vc;
+}
+
+VLANClientState *qemu_new_vlan_client(VLANState *vlan,
+                                      const char *model,
+                                      const char *name,
+                                      IOReadHandler *fd_read,
+                                      IOCanRWHandler *fd_can_read,
+                                      void *opaque)
+{
+    VLANClientState *vc, **pvc;
+
+    vc = qemu_new_vlan_client_common(vlan, model, name, fd_read, fd_can_read,
+				     opaque);
+    /* Append but before last_client.  */
     pvc = &vlan->first_client;
-    while (*pvc != NULL)
+    while (*pvc != NULL) {
+	if ((*pvc)->last_client) {
+	    vc->next = *pvc;
+	    break;
+	}
         pvc = &(*pvc)->next;
+    }
     *pvc = vc;
     return vc;
 }
 
+static VLANClientState *qemu_new_vlan_client_tail(VLANState *vlan,
+						  const char *model,
+						  const char *name,
+						  IOReadHandler *fd_read,
+						  IOCanRWHandler *fd_can_read,
+						  void *opaque)
+{
+    VLANClientState *vc, **pvc;
+
+    vc = qemu_new_vlan_client_common(vlan, model, name, fd_read, fd_can_read,
+				     opaque);
+    vc->last_client = 1;
+    pvc = &vlan->first_client;
+    while (*pvc != NULL) {
+	if ((*pvc)->last_client) {
+	    fprintf(stderr, "Multiple vlan last client (%s and %s)\n",
+		    (*pvc)->name, name);
+	    exit(1);
+	}
+        pvc = &(*pvc)->next;
+    }
+    *pvc = vc;
+    return vc;
+}
+
 void qemu_del_vlan_client(VLANClientState *vc)
 {
     VLANClientState **pvc = &vc->vlan->first_client;
@@ -516,8 +561,8 @@
         slirp_inited = 1;
         slirp_init(slirp_restrict, slirp_ip);
     }
-    slirp_vc = qemu_new_vlan_client(vlan, model, name,
-                                    slirp_receive, NULL, NULL);
+    slirp_vc = qemu_new_vlan_client_tail(vlan, model, name,
+					 slirp_receive, NULL, NULL);
     slirp_vc->info_str[0] = '\0';
     return 0;
 }
@@ -1088,6 +1133,91 @@
 }
 #endif
 
+typedef struct DumpState {
+    VLANClientState *tcpdump_vc;
+    FILE *tcpdump_file;
+    int tcpdump_caplen;
+} DumpState;
+
+#define TCPDUMP_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 tcpdump_receive(void *opaque, const uint8_t *buf, int size)
+{
+    DumpState *ds = (DumpState *)opaque;
+    struct pcap_sf_pkthdr hdr;
+    int64_t ts;
+    int caplen;
+
+    /* Early return in case of previous error.  */
+    if (ds->tcpdump_file == NULL)
+	return;
+
+    ts = muldiv64 (qemu_get_clock(vm_clock),1000000, ticks_per_sec);
+    caplen = size > ds->tcpdump_caplen ? ds->tcpdump_caplen : size;
+
+    hdr.ts.tv_sec = ts / 1000000000LL;
+    hdr.ts.tv_usec = ts % 1000000;
+    hdr.caplen = caplen;
+    hdr.len = size;
+    if (fwrite(&hdr, sizeof(hdr), 1, ds->tcpdump_file) != 1
+	|| fwrite(buf, caplen, 1, ds->tcpdump_file) != 1) {
+	qemu_log("-net dump write error - stop dump\n");
+	fclose(ds->tcpdump_file);
+	ds->tcpdump_file = NULL;
+    }
+}
+
+static int net_tcpdump_init(VLANState *vlan, const char *device,
+			    const char *name, const char *filename, int len)
+{
+    struct pcap_file_hdr hdr;
+    DumpState *ds;
+
+    ds = qemu_malloc (sizeof (DumpState));
+    ds->tcpdump_file = fopen(filename, "wb");
+    if (!ds->tcpdump_file) {
+	fprintf(stderr, "-net dump: can't open %s\n", filename);
+	exit(1);
+    }
+
+    ds->tcpdump_caplen = len;
+
+    hdr.magic = TCPDUMP_MAGIC;
+    hdr.version_major = 2;
+    hdr.version_minor = 4;
+    hdr.thiszone = 0;
+    hdr.sigfigs = 0;
+    hdr.snaplen = ds->tcpdump_caplen;
+    hdr.linktype = 1;
+
+    fwrite(&hdr, sizeof(hdr), 1, ds->tcpdump_file);
+
+    ds->tcpdump_vc = qemu_new_vlan_client(vlan, device, name,
+					  tcpdump_receive, NULL, ds);
+    snprintf(ds->tcpdump_vc->info_str, sizeof(ds->tcpdump_vc->info_str),
+	     "tcpdump to %s (len=%d)", filename, len);
+    return 0;
+}
+
 /* network connection */
 typedef struct NetSocketState {
     VLANClientState *vc;
@@ -1762,6 +1892,20 @@
 	ret = net_vde_init(vlan, device, name, vde_sock, vde_port, vde_group, vde_mode);
     } else
 #endif
+    if (!strcmp(device, "dump")) {
+	int len = 64;
+        if (get_param_value(buf, sizeof(buf), "len", p) > 0) {
+            len = strtol(buf, NULL, 0);
+	}
+
+        if (!get_param_value(buf, sizeof(buf), "file", p)) {
+	    if (vlan_id == 0)
+		strcpy(buf, "qemu.tcpdump");
+	    else
+		snprintf(buf, sizeof(buf), "qemu-vlan%d.tcpdump", vlan_id);
+        }
+        ret = net_tcpdump_init(vlan, device, name, buf, len);
+    } else
     {
         fprintf(stderr, "Unknown network device: %s\n", device);
         if (name)
Index: net.h
===================================================================
--- net.h	(revision 6685)
+++ net.h	(working copy)
@@ -19,6 +19,9 @@
     IOCanRWHandler *fd_can_read;
     LinkStatusChanged *link_status_changed;
     int link_down;
+    /* If set this client must be the last in the VLAN (currently set only
+       by slirp).  */
+    int last_client;
     void *opaque;
     struct VLANClientState *next;
     struct VLANState *vlan;
Index: qemu-doc.texi
===================================================================
--- qemu-doc.texi	(revision 6685)
+++ qemu-doc.texi	(working copy)
@@ -742,6 +742,11 @@
 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.tcpdump} by
+default).  At most @var{len} bytes (64 by default) by packet are stored.  The
+dump can be analyzed with tools such as tcpdump.
+
 @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

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2009-03-05 10:10 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-02-04 10:22 [Qemu-devel] [Patch] Add -net dump option Tristan Gingold
2009-02-06 18:53 ` Anthony Liguori
2009-02-10 11:00   ` Tristan Gingold
2009-02-10 12:15     ` Jamie Lokier
2009-02-10 12:59       ` Tristan Gingold
2009-02-10 22:05         ` Jamie Lokier
2009-02-13 14:01     ` [Qemu-devel] Ping: [Patch] Add -net dump option (v2) Tristan Gingold
2009-02-23 15:30     ` [Qemu-devel] Ping*2: [Patch-v2] Add -net dump option Tristan Gingold
2009-02-27 19:09     ` [Qemu-devel] [Patch] " Anthony Liguori
2009-02-07  4:09 ` Luca Bigliardi
2009-02-10 11:01   ` Tristan Gingold
2009-02-10 17:04     ` [Qemu-devel] SET rezo-actu DIGEST GAYET Thierry
  -- strict thread matches above, loose matches on Subject: below --
2009-03-05 10:10 [Qemu-devel] [Patch] Add -net dump option Tristan Gingold

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).