W00t, my first patch got applied :) Thank you.

Here goes version 0.3 of my packet capture patch. I rewritten it to be a custom 
VLANClient which implements the capturing part in its fd_read proceudre, rather 
than adding additional properties to the VLAN structure. Monitor support is 
also present.

Also, could somebody please give me any hints regarding my questions described 
here: http://lists.gnu.org/archive/html/qemu-devel/2007-12/msg00296.html (Qemu 
architectural questions)? Any pointers would be greatly appreciated.

Best regards.




      __________________________________________________________
Sent from Yahoo! Mail - a smarter inbox http://uk.mail.yahoo.com
Index: vl.c
===================================================================
RCS file: /sources/qemu/qemu/vl.c,v
retrieving revision 1.388
diff -u -r1.388 vl.c
--- vl.c	17 Dec 2007 04:42:28 -0000	1.388
+++ vl.c	18 Dec 2007 07:30:26 -0000
@@ -237,6 +237,19 @@
 static CPUState *next_cpu;
 static int event_pending = 1;
 
+/* File header which needs to be written at the start of each PCAP file*/
+static const PCAPHeader pcap_file_header = {
+    0xa1b2c3d4, /* Magic header - used to determine file endianess */
+    2, /* Version major */
+    4, /* Version minor */
+    0,
+    0,
+    MAX_CAPTURED_PACKET_SIZE,
+    1	/* Ethernet */
+};
+/* Used to return in case a memory allocation fails (because obviously we can't allocate memory then!) */
+static char *pcap_memory_error = "Failed to allocate memory";
+
 #define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR)
 
 /***********************************************************/
@@ -4669,6 +4682,7 @@
     char buf[1024];
     int vlan_id, ret;
     VLANState *vlan;
+    char *error_message;
 
     p = str;
     q = device;
@@ -4788,6 +4802,15 @@
         }
         vlan->nb_host_devs++;
     } else
+    if (!strcmp(device, "capture")) {
+        error_message = pcap_add_capture(vlan_id, (char*)get_param_value(buf, sizeof(buf), "file", p));
+        if (error_message) {
+            fprintf(stderr, "%s\n", error_message);
+            free(error_message);
+            return -1;
+        }
+        ret = 0;
+    } else    
     {
         fprintf(stderr, "Unknown network device: %s\n", device);
         return -1;
@@ -4799,6 +4822,152 @@
     return ret;
 }
 
+/* VLAN Packet capture */
+
+static void pcap_receive(void *opaque, const uint8_t *buf, int size) {
+    PCAPStateInfo *pcap_state;
+    
+    pcap_state = (PCAPStateInfo*)opaque;
+    
+    pcap_state->packet_header.timestamp_sec = time(NULL);
+    if (pcap_state->packet_header.timestamp_sec == pcap_state->last_packet_time) {
+        if (pcap_state->packet_header.timestamp_usec < 1000000)
+            ++pcap_state->packet_header.timestamp_usec;
+    } else {
+        pcap_state->packet_header.timestamp_usec = 0;
+        pcap_state->last_packet_time = pcap_state->packet_header.timestamp_sec;
+    }
+        
+    pcap_state->packet_header.orig_len = size;
+    pcap_state->packet_header.saved_len = (size > MAX_CAPTURED_PACKET_SIZE) ? MAX_CAPTURED_PACKET_SIZE : size;
+    write(pcap_state->fh, &pcap_state->packet_header, sizeof(PCAPPacketHeader));
+    write(pcap_state->fh, buf, pcap_state->packet_header.saved_len);    
+}
+
+/* Add capture to a given VLAN. */
+/* Returns NULL on success, the error message in case of an error. */
+/* If an error message is returned, the caller must "free" it */
+char* pcap_add_capture(int vlan_id, const char *filename)
+{
+    VLANState *vlan;
+    VLANClientState *vlan_client;
+    char *error_message;
+    PCAPStateInfo *pcap_state;
+    
+    if (NULL == filename)
+        filename = DEFAULT_CAPTURE_FILENAME;        
+    
+    for(vlan = first_vlan; vlan != NULL; vlan = vlan->next)
+        if (vlan_id == vlan->id)
+            break;
+    if (!vlan) {
+        asprintf(&error_message, "Error locating VLAN %d to attach for capture.", vlan_id);
+        return error_message;
+    }
+    
+    //check if already capturing
+    vlan_client = vlan->first_client;
+    while (NULL != vlan_client) {
+        if (pcap_receive == vlan_client->fd_read) {
+            asprintf(&error_message, "VLAN %d is already capturing. "
+                "Only one capture per VLAN is possible.", vlan_id);
+            return error_message;
+        }
+        vlan_client = vlan_client->next;
+    }
+    
+    //not capturing, try to start / attach it
+    pcap_state = qemu_mallocz(sizeof(PCAPStateInfo));
+    if (!pcap_state)
+        return pcap_memory_error;
+    vlan_client = qemu_new_vlan_client(vlan, &pcap_receive, NULL, pcap_state);
+    if (!vlan_client) {
+        free(pcap_state);
+        return pcap_memory_error;
+    }
+    
+    pcap_state->fh = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
+    if (pcap_state->fh < 0) {
+        free(pcap_state);
+        free(vlan_client);
+        
+        asprintf(&error_message, "Failed to open capture file \"%s\": %d\n", filename, errno);
+        return error_message;
+    }    
+    
+    //write the header to the file
+    write(pcap_state->fh, &pcap_file_header, sizeof(pcap_file_header));
+    
+    //success
+    return NULL;
+}
+
+/* (Tries to) remove capture from a given VLAN */
+/* Returns NULL on success, the error message in case of an error. */
+/* If an error message is returned, the caller must "free" it */
+char* pcap_remove_capture(int vlan_id)
+{
+    VLANState *vlan;
+    VLANClientState *vlan_client, *vlan_temp;
+    char *error_message;
+    PCAPStateInfo *pcap_state;
+    
+    for(vlan = first_vlan; vlan != NULL; vlan = vlan->next)
+        if (vlan_id == vlan->id)
+            break;
+    if (!vlan) {
+        asprintf(&error_message, "Error locating VLAN %d to remove capture.", vlan_id);
+        return error_message;
+    }
+    
+    vlan_client = NULL;
+    //two cases
+    //if the first vlan client is the pcap one
+    if (pcap_receive == vlan->first_client->fd_read) {
+        vlan_client = vlan->first_client;
+        vlan->first_client = vlan_client->next;
+    } else {
+        //search through the list
+        vlan_client = vlan->first_client;
+        while (NULL != vlan_client->next) {
+            if (pcap_receive == vlan_client->next->fd_read)
+                break;
+            vlan_client = vlan_client->next;
+        }
+        if (NULL != vlan_client && NULL != vlan_client->next) {
+            vlan_temp = vlan_client->next;
+            vlan_client->next = vlan_temp->next;
+            vlan_client = vlan_temp;
+        } else {
+            vlan_client = NULL;
+        }
+    }
+    
+    if (NULL == vlan_client) {
+        asprintf(&error_message, "No active capture found for VLAN %d", vlan_id);
+        return error_message;
+    }
+    
+    pcap_state = (PCAPStateInfo*)vlan_client->opaque;
+    close(pcap_state->fh);
+    free(pcap_state);
+    free(vlan_client);
+    
+    return NULL;
+}
+
+/* Removes the capture from all VLAN's */
+void pcap_remove_all_capture(void)
+{
+    VLANState *vlan;
+    char *error_message;
+    
+    for(vlan = first_vlan; vlan != NULL; vlan = vlan->next) {
+        error_message = pcap_remove_capture(vlan->id);
+        if (error_message) free(error_message);
+    }
+}
+
 void do_info_network(void)
 {
     VLANState *vlan;
@@ -7540,6 +7709,10 @@
            "                connect the vlan 'n' to another VLAN using a socket connection\n"
            "-net socket[,vlan=n][,fd=h][,mcast=maddr:port]\n"
            "                connect the vlan 'n' to multicast maddr and port\n"
+           "-net capture[,vlan=n][,file=filename]\n"
+           "                captures the traffic flowing through VLAN 'n' to the file\n"
+           "                'filename' in tcpdump format. In case filename is not specified,\n"
+           "                the capture will be saved to " DEFAULT_CAPTURE_FILENAME "\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"
            "\n"
@@ -8948,6 +9121,9 @@
 
 	close(fd);
     }
+    
+    /* stop capturing packets (close capture files) on exit */
+    atexit(pcap_remove_all_capture);    
 
     main_loop();
     quit_timers();
@@ -8969,5 +9145,6 @@
         }
     }
 #endif
+    
     return 0;
 }
Index: net.h
===================================================================
RCS file: /sources/qemu/qemu/net.h,v
retrieving revision 1.1
diff -u -r1.1 net.h
--- net.h	17 Nov 2007 17:14:38 -0000	1.1
+++ net.h	18 Dec 2007 07:30:26 -0000
@@ -1,6 +1,41 @@
 #ifndef QEMU_NET_H
 #define QEMU_NET_H
 
+/* PCAP support */
+/* source: http://wiki.wireshark.org/Development/LibpcapFileFormat */
+typedef struct {
+    uint32_t magic_number;
+    uint16_t version_major;
+    uint16_t version_minor;
+    int32_t thiszone;
+    uint32_t sigfigs;
+    uint32_t snaplen;
+    uint32_t network;
+} PCAPHeader;
+
+#define MAX_CAPTURED_PACKET_SIZE 0xFFFF
+#define DEFAULT_CAPTURE_FILENAME "qemu.pcap"
+
+typedef struct {
+	uint32_t timestamp_sec;
+	uint32_t timestamp_usec;
+	uint32_t saved_len;
+	uint32_t orig_len;
+} PCAPPacketHeader;
+
+/* used to keep the state for the packet capture process */
+typedef struct {
+    //filehandle of the capture file
+    int fh;
+    time_t last_packet_time;
+    //defined here to avoid reallocation in the code
+    PCAPPacketHeader packet_header;
+} PCAPStateInfo;
+
+char* pcap_add_capture(int vlan_id, const char *filename);
+char* pcap_remove_capture(int vlan_id);
+void pcap_remove_all_capture(void);
+
 /* VLANs support */
 
 typedef struct VLANClientState VLANClientState;
Index: monitor.c
===================================================================
RCS file: /sources/qemu/qemu/monitor.c,v
retrieving revision 1.93
diff -u -r1.93 monitor.c
--- monitor.c	17 Dec 2007 03:15:51 -0000	1.93
+++ monitor.c	18 Dec 2007 07:30:27 -0000
@@ -1255,6 +1255,38 @@
 }
 #endif
 
+void do_net_capture (const char *path,
+                     int has_vlan, int vlan_id)
+{
+    char *error_message;
+    
+    if (!has_vlan) vlan_id = 0;
+    
+    error_message = pcap_add_capture(vlan_id, path);
+    if (error_message) {
+        term_printf("%s\n", error_message);
+        free(error_message);
+        return;
+    }
+    
+    return;
+}
+
+void do_stop_net_capture(int has_vlan, int vlan_id)
+{
+    char *error_message;
+    
+    if (has_vlan) {
+        error_message = pcap_remove_capture(vlan_id);
+        if (error_message) {
+            term_printf("%s\n", error_message);
+            free(error_message);
+            return;
+        }
+    } else
+        pcap_remove_all_capture();
+}
+
 static term_cmd_t term_cmds[] = {
     { "help|?", "s?", do_help,
       "[cmd]", "show the help" },
@@ -1326,6 +1358,10 @@
        "capture index", "stop capture" },
     { "memsave", "lis", do_memory_save,
       "addr size file", "save to disk virtual memory dump starting at 'addr' of size 'size'", },
+    { "netcapture", "si?", do_net_capture,
+      "[vlan]", "saves the traffic flowing through the given vlan to the file (default vlan=0)" },
+    { "stopnetcapture", "i?", do_stop_net_capture,
+      "[vlan]", "stops the capture of the specified vlan. if no vlan specified, stops all the capture" },            
     { NULL, NULL, },
 };
 

Reply via email to