Commit bb9ea79e7a177acce6f8017eef794662ee219c97
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
Showing
3 changed files
with
121 additions
and
3 deletions
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 | ... | ... |
net.c
... | ... | @@ -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("net", 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 | ... | ... |