Commit bb9ea79e7a177acce6f8017eef794662ee219c97

Authored by aliguori
1 parent 10300216

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 <jan.kiszka@siemens.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@7200 c046a42c-6fe2-441c-8c8c-71466251a162
monitor.c
@@ -1731,7 +1731,7 @@ static const mon_cmd_t mon_cmds[] = { @@ -1731,7 +1731,7 @@ static const mon_cmd_t mon_cmds[] = {
1731 { "pci_add", "sss", pci_device_hot_add, "pci_addr=auto|[[<domain>:]<bus>:]<slot> nic|storage [[vlan=n][,macaddr=addr][,model=type]] [file=file][,if=type][,bus=nr]...", "hot-add PCI device" }, 1731 { "pci_add", "sss", pci_device_hot_add, "pci_addr=auto|[[<domain>:]<bus>:]<slot> nic|storage [[vlan=n][,macaddr=addr][,model=type]] [file=file][,if=type][,bus=nr]...", "hot-add PCI device" },
1732 { "pci_del", "s", pci_device_hot_remove, "pci_addr=[[<domain>:]<bus>:]<slot>", "hot remove PCI device" }, 1732 { "pci_del", "s", pci_device_hot_remove, "pci_addr=[[<domain>:]<bus>:]<slot>", "hot remove PCI device" },
1733 { "host_net_add", "ss", net_host_device_add, 1733 { "host_net_add", "ss", net_host_device_add,
1734 - "[tap,user,socket,vde] options", "add host VLAN client" }, 1734 + "[tap,user,socket,vde,dump] options", "add host VLAN client" },
1735 { "host_net_remove", "is", net_host_device_remove, 1735 { "host_net_remove", "is", net_host_device_remove,
1736 "vlan_id name", "remove host VLAN client" }, 1736 "vlan_id name", "remove host VLAN client" },
1737 #endif 1737 #endif
@@ -118,6 +118,7 @@ @@ -118,6 +118,7 @@
118 #include "qemu-char.h" 118 #include "qemu-char.h"
119 #include "audio/audio.h" 119 #include "audio/audio.h"
120 #include "qemu_socket.h" 120 #include "qemu_socket.h"
  121 +#include "qemu-log.h"
121 122
122 #if defined(CONFIG_SLIRP) 123 #if defined(CONFIG_SLIRP)
123 #include "libslirp.h" 124 #include "libslirp.h"
@@ -1558,6 +1559,106 @@ static int net_socket_mcast_init(VLANState *vlan, @@ -1558,6 +1559,106 @@ static int net_socket_mcast_init(VLANState *vlan,
1558 1559
1559 } 1560 }
1560 1561
  1562 +typedef struct DumpState {
  1563 + VLANClientState *pcap_vc;
  1564 + int fd;
  1565 + int pcap_caplen;
  1566 +} DumpState;
  1567 +
  1568 +#define PCAP_MAGIC 0xa1b2c3d4
  1569 +
  1570 +struct pcap_file_hdr {
  1571 + uint32_t magic;
  1572 + uint16_t version_major;
  1573 + uint16_t version_minor;
  1574 + int32_t thiszone;
  1575 + uint32_t sigfigs;
  1576 + uint32_t snaplen;
  1577 + uint32_t linktype;
  1578 +};
  1579 +
  1580 +struct pcap_sf_pkthdr {
  1581 + struct {
  1582 + int32_t tv_sec;
  1583 + int32_t tv_usec;
  1584 + } ts;
  1585 + uint32_t caplen;
  1586 + uint32_t len;
  1587 +};
  1588 +
  1589 +static void dump_receive(void *opaque, const uint8_t *buf, int size)
  1590 +{
  1591 + DumpState *s = opaque;
  1592 + struct pcap_sf_pkthdr hdr;
  1593 + int64_t ts;
  1594 + int caplen;
  1595 +
  1596 + /* Early return in case of previous error. */
  1597 + if (s->fd < 0) {
  1598 + return;
  1599 + }
  1600 +
  1601 + ts = muldiv64 (qemu_get_clock(vm_clock),1000000, ticks_per_sec);
  1602 + caplen = size > s->pcap_caplen ? s->pcap_caplen : size;
  1603 +
  1604 + hdr.ts.tv_sec = ts / 1000000000LL;
  1605 + hdr.ts.tv_usec = ts % 1000000;
  1606 + hdr.caplen = caplen;
  1607 + hdr.len = size;
  1608 + if (write(s->fd, &hdr, sizeof(hdr)) != sizeof(hdr) ||
  1609 + write(s->fd, buf, caplen) != caplen) {
  1610 + qemu_log("-net dump write error - stop dump\n");
  1611 + close(s->fd);
  1612 + s->fd = -1;
  1613 + }
  1614 +}
  1615 +
  1616 +static void net_dump_cleanup(VLANClientState *vc)
  1617 +{
  1618 + DumpState *s = vc->opaque;
  1619 +
  1620 + close(s->fd);
  1621 + qemu_free(s);
  1622 +}
  1623 +
  1624 +static int net_dump_init(VLANState *vlan, const char *device,
  1625 + const char *name, const char *filename, int len)
  1626 +{
  1627 + struct pcap_file_hdr hdr;
  1628 + DumpState *s;
  1629 +
  1630 + s = qemu_malloc(sizeof(DumpState));
  1631 +
  1632 + s->fd = open(filename, O_CREAT | O_WRONLY, 0644);
  1633 + if (s->fd < 0) {
  1634 + fprintf(stderr, "-net dump: can't open %s\n", filename);
  1635 + return -1;
  1636 + }
  1637 +
  1638 + s->pcap_caplen = len;
  1639 +
  1640 + hdr.magic = PCAP_MAGIC;
  1641 + hdr.version_major = 2;
  1642 + hdr.version_minor = 4;
  1643 + hdr.thiszone = 0;
  1644 + hdr.sigfigs = 0;
  1645 + hdr.snaplen = s->pcap_caplen;
  1646 + hdr.linktype = 1;
  1647 +
  1648 + if (write(s->fd, &hdr, sizeof(hdr)) < sizeof(hdr)) {
  1649 + perror("-net dump write error");
  1650 + close(s->fd);
  1651 + qemu_free(s);
  1652 + return -1;
  1653 + }
  1654 +
  1655 + s->pcap_vc = qemu_new_vlan_client(vlan, device, name, dump_receive, NULL,
  1656 + net_dump_cleanup, s);
  1657 + snprintf(s->pcap_vc->info_str, sizeof(s->pcap_vc->info_str),
  1658 + "dump to %s (len=%d)", filename, len);
  1659 + return 0;
  1660 +}
  1661 +
1561 /* find or alloc a new VLAN */ 1662 /* find or alloc a new VLAN */
1562 VLANState *qemu_find_vlan(int id) 1663 VLANState *qemu_find_vlan(int id)
1563 { 1664 {
@@ -1883,7 +1984,17 @@ int net_client_init(const char *device, const char *p) @@ -1883,7 +1984,17 @@ int net_client_init(const char *device, const char *p)
1883 ret = net_vde_init(vlan, device, name, vde_sock, vde_port, vde_group, vde_mode); 1984 ret = net_vde_init(vlan, device, name, vde_sock, vde_port, vde_group, vde_mode);
1884 } else 1985 } else
1885 #endif 1986 #endif
1886 - { 1987 + if (!strcmp(device, "dump")) {
  1988 + int len = 65536;
  1989 +
  1990 + if (get_param_value(buf, sizeof(buf), "len", p) > 0) {
  1991 + len = strtol(buf, NULL, 0);
  1992 + }
  1993 + if (!get_param_value(buf, sizeof(buf), "file", p)) {
  1994 + snprintf(buf, sizeof(buf), "qemu-vlan%d.pcap", vlan_id);
  1995 + }
  1996 + ret = net_dump_init(vlan, device, name, buf, len);
  1997 + } else {
1887 fprintf(stderr, "Unknown network device: %s\n", device); 1998 fprintf(stderr, "Unknown network device: %s\n", device);
1888 ret = -1; 1999 ret = -1;
1889 goto out; 2000 goto out;
@@ -1908,7 +2019,7 @@ void net_client_uninit(NICInfo *nd) @@ -1908,7 +2019,7 @@ void net_client_uninit(NICInfo *nd)
1908 static int net_host_check_device(const char *device) 2019 static int net_host_check_device(const char *device)
1909 { 2020 {
1910 int i; 2021 int i;
1911 - const char *valid_param_list[] = { "tap", "socket" 2022 + const char *valid_param_list[] = { "tap", "socket", "dump"
1912 #ifdef CONFIG_SLIRP 2023 #ifdef CONFIG_SLIRP
1913 ,"user" 2024 ,"user"
1914 #endif 2025 #endif
qemu-options.hx
@@ -745,6 +745,8 @@ DEF(&quot;net&quot;, HAS_ARG, QEMU_OPTION_net, \ @@ -745,6 +745,8 @@ DEF(&quot;net&quot;, HAS_ARG, QEMU_OPTION_net, \
745 " Use group 'groupname' and mode 'octalmode' to change default\n" 745 " Use group 'groupname' and mode 'octalmode' to change default\n"
746 " ownership and permissions for communication port.\n" 746 " ownership and permissions for communication port.\n"
747 #endif 747 #endif
  748 + "-net dump[,vlan=n][,file=f][,len=n]\n"
  749 + " dump traffic on vlan 'n' to file 'f' (max n bytes per packet)\n"
748 "-net none use it alone to have zero network devices; if no -net option\n" 750 "-net none use it alone to have zero network devices; if no -net option\n"
749 " is provided, the default is '-net nic -net user'\n") 751 " is provided, the default is '-net nic -net user'\n")
750 STEXI 752 STEXI
@@ -865,6 +867,11 @@ vde_switch -F -sock /tmp/myswitch @@ -865,6 +867,11 @@ vde_switch -F -sock /tmp/myswitch
865 qemu linux.img -net nic -net vde,sock=/tmp/myswitch 867 qemu linux.img -net nic -net vde,sock=/tmp/myswitch
866 @end example 868 @end example
867 869
  870 +@item -net dump[,vlan=@var{n}][,file=@var{file}][,len=@var{len}]
  871 +Dump network traffic on VLAN @var{n} to file @var{file} (@file{qemu-vlan0.pcap} by default).
  872 +At most @var{len} bytes (64k by default) per packet are stored. The file format is
  873 +libpcap, so it can be analyzed with tools such as tcpdump or Wireshark.
  874 +
868 @item -net none 875 @item -net none
869 Indicate that no network devices should be configured. It is used to 876 Indicate that no network devices should be configured. It is used to
870 override the default configuration (@option{-net nic -net user}) which 877 override the default configuration (@option{-net nic -net user}) which