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 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 1732 { "pci_del", "s", pci_device_hot_remove, "pci_addr=[[<domain>:]<bus>:]<slot>", "hot remove PCI device" },
1733 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 1735 { "host_net_remove", "is", net_host_device_remove,
1736 1736 "vlan_id name", "remove host VLAN client" },
1737 1737 #endif
... ...
... ... @@ -118,6 +118,7 @@
118 118 #include "qemu-char.h"
119 119 #include "audio/audio.h"
120 120 #include "qemu_socket.h"
  121 +#include "qemu-log.h"
121 122  
122 123 #if defined(CONFIG_SLIRP)
123 124 #include "libslirp.h"
... ... @@ -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 1662 /* find or alloc a new VLAN */
1562 1663 VLANState *qemu_find_vlan(int id)
1563 1664 {
... ... @@ -1883,7 +1984,17 @@ int net_client_init(const char *device, const char *p)
1883 1984 ret = net_vde_init(vlan, device, name, vde_sock, vde_port, vde_group, vde_mode);
1884 1985 } else
1885 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 1998 fprintf(stderr, "Unknown network device: %s\n", device);
1888 1999 ret = -1;
1889 2000 goto out;
... ... @@ -1908,7 +2019,7 @@ void net_client_uninit(NICInfo *nd)
1908 2019 static int net_host_check_device(const char *device)
1909 2020 {
1910 2021 int i;
1911   - const char *valid_param_list[] = { "tap", "socket"
  2022 + const char *valid_param_list[] = { "tap", "socket", "dump"
1912 2023 #ifdef CONFIG_SLIRP
1913 2024 ,"user"
1914 2025 #endif
... ...
qemu-options.hx
... ... @@ -745,6 +745,8 @@ DEF(&quot;net&quot;, HAS_ARG, QEMU_OPTION_net, \
745 745 " Use group 'groupname' and mode 'octalmode' to change default\n"
746 746 " ownership and permissions for communication port.\n"
747 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 750 "-net none use it alone to have zero network devices; if no -net option\n"
749 751 " is provided, the default is '-net nic -net user'\n")
750 752 STEXI
... ... @@ -865,6 +867,11 @@ vde_switch -F -sock /tmp/myswitch
865 867 qemu linux.img -net nic -net vde,sock=/tmp/myswitch
866 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 875 @item -net none
869 876 Indicate that no network devices should be configured. It is used to
870 877 override the default configuration (@option{-net nic -net user}) which
... ...