diff mbox

[RFC,18/22] Network packets record/replay

Message ID 008601cf951f$f0f38650$d2da92f0$@Dovgaluk@ispras.ru
State New
Headers show

Commit Message

Pavel Dovgalyuk July 1, 2014, 11:31 a.m. UTC
These patches implement passing network packets to replay module in
record mode. New virtual network adapter is impelemented to replay the
packets when they are read from the log file.

Signed-off-by: Pavel Dovgalyuk <pavel.dovgaluk@gmail.com>
---

Comments

Eric Blake July 1, 2014, 2:48 p.m. UTC | #1
On 07/01/2014 05:31 AM, Pavel Dovgaluk wrote:
> These patches implement passing network packets to replay module in
> record mode. New virtual network adapter is impelemented to replay the

s/impelemented/implemented/

> packets when they are read from the log file.
> 
> Signed-off-by: Pavel Dovgalyuk <pavel.dovgaluk@gmail.com>
> ---

Please thread your messages in-reply-to the cover letter.

> 
> diff --git a/net/clients.h b/net/clients.h
> index 2e8feda..d245a9f 100644
> --- a/net/clients.h
> +++ b/net/clients.h
> @@ -49,6 +49,10 @@ int net_init_bridge(const NetClientOptions *opts, const char *name,
>  
>  int net_init_l2tpv3(const NetClientOptions *opts, const char *name,
>                      NetClientState *peer);
> +                    
> +int net_init_replay(const NetClientOptions *opts, const char *name,
> +                 NetClientState *peer);

Indentation is off.


> --- /dev/null
> +++ b/net/net-replay.c
> @@ -0,0 +1,56 @@
> +#include "net/net.h"

Missing a copyright statement and declaration of license.  (We prefer
GPLv2+ for new files, unless you have a strong reason otherwise, but
whatever you pick must be compatible with GPLv2-only)

> +++ b/qapi-schema.json
> @@ -2145,6 +2145,15 @@
>      '*file': 'str' } }
>  
>  ##
> +# @NetdevReplayOptions
> +#
> +# Dump network traffic to the log in replay mode.
> +#
> +##

Missing a 'Since: 2.2' designation.  Is there any ability to control
which file is used as the logging destination?

> +{ 'type': 'NetdevReplayOptions',
> +  'data': { } }
> +
Pavel Dovgalyuk July 2, 2014, 10:31 a.m. UTC | #2
> > --- /dev/null
> > +++ b/net/net-replay.c
> > @@ -0,0 +1,56 @@
> > +#include "net/net.h"
> 
> Missing a copyright statement and declaration of license.  (We prefer
> GPLv2+ for new files, unless you have a strong reason otherwise, but
> whatever you pick must be compatible with GPLv2-only)
> 
> > +++ b/qapi-schema.json
> > @@ -2145,6 +2145,15 @@
> >      '*file': 'str' } }
> >
> >  ##
> > +# @NetdevReplayOptions
> > +#
> > +# Dump network traffic to the log in replay mode.
> > +#
> > +##
> 
> Missing a 'Since: 2.2' designation.  Is there any ability to control
> which file is used as the logging destination?

There is no ability to control packets loggings.
All incoming network traffic is written into the replay log,
which name is specified in the command line.

> 
> > +{ 'type': 'NetdevReplayOptions',
> > +  'data': { } }
> > +


Pavel Dovgaluk
diff mbox

Patch

diff --git a/net/clients.h b/net/clients.h
index 2e8feda..d245a9f 100644
--- a/net/clients.h
+++ b/net/clients.h
@@ -49,6 +49,10 @@  int net_init_bridge(const NetClientOptions *opts, const char *name,
 
 int net_init_l2tpv3(const NetClientOptions *opts, const char *name,
                     NetClientState *peer);
+                    
+int net_init_replay(const NetClientOptions *opts, const char *name,
+                 NetClientState *peer);
+
 #ifdef CONFIG_VDE
 int net_init_vde(const NetClientOptions *opts, const char *name,
                  NetClientState *peer);
diff --git a/net/dump.c b/net/dump.c
index 9d3a09e..4c8ec1b 100644
--- a/net/dump.c
+++ b/net/dump.c
@@ -28,6 +28,7 @@ 
 #include "qemu/log.h"
 #include "qemu/timer.h"
 #include "hub.h"
+#include "replay/replay.h"
 
 typedef struct DumpState {
     NetClientState nc;
@@ -153,6 +154,11 @@  int net_init_dump(const NetClientOptions *opts, const char *name,
     char def_file[128];
     const NetdevDumpOptions *dump;
 
+    if (replay_mode == REPLAY_SAVE) {
+        fprintf(stderr, "Interface \"dump\" is not permitted with record\n");
+        exit(1);
+    }
+
     assert(opts->kind == NET_CLIENT_OPTIONS_KIND_DUMP);
     dump = opts->dump;
 
diff --git a/net/hub.c b/net/hub.c
index 7e0f2d6..91fdfb6 100644
--- a/net/hub.c
+++ b/net/hub.c
@@ -323,6 +323,7 @@  void net_hub_check_clients(void)
             case NET_CLIENT_OPTIONS_KIND_SOCKET:
             case NET_CLIENT_OPTIONS_KIND_VDE:
             case NET_CLIENT_OPTIONS_KIND_VHOST_USER:
+            case NET_CLIENT_OPTIONS_KIND_REPLAY:
                 has_host_dev = 1;
                 break;
             default:
diff --git a/net/net-replay.c b/net/net-replay.c
new file mode 100644
index 0000000..074ee56
--- /dev/null
+++ b/net/net-replay.c
@@ -0,0 +1,56 @@ 
+#include "net/net.h"
+#include "clients.h"
+#include "qemu-common.h"
+#include "sysemu/sysemu.h"
+#include "qemu/log.h"
+#include "replay/replay.h"
+
+typedef struct NetReplayState {
+    NetClientState nc;
+} NetReplayState;
+
+static ssize_t net_replay_receive(NetClientState *nc, const uint8_t *buf, size_t size)
+{
+    return size;
+}
+
+static void net_replay_cleanup(NetClientState *nc)
+{
+}
+
+static NetClientInfo net_replay_info = {
+    .type = NET_CLIENT_OPTIONS_KIND_REPLAY,
+    .size = sizeof(NetReplayState),
+    .receive = net_replay_receive,
+    .cleanup = net_replay_cleanup,
+};
+
+static int net_replay_init(NetClientState *vlan, const char *device,
+                         const char *name)
+{
+    NetClientState *nc;
+
+    nc = qemu_new_net_client(&net_replay_info, vlan, device, name);
+
+    snprintf(nc->info_str, sizeof(nc->info_str), "replayer");
+
+    if (replay_mode == REPLAY_SAVE) {
+        printf("Network interface \"replay\" is not permitted with record\n");
+        exit(1);
+    } else if (replay_mode == REPLAY_PLAY) {
+        replay_add_network_client(nc);
+    } else {
+        printf("Network interface \"replay\" is not permitted without replay\n");
+        exit(1);
+    }
+    
+    return 0;
+}
+
+int net_init_replay(const NetClientOptions *opts, const char *name, NetClientState *peer)
+{
+    assert(peer);
+    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_REPLAY);
+
+    return net_replay_init(peer, "replay", name);
+}
diff --git a/net/net.c b/net/net.c
index 0869c60..ea404f8 100644
--- a/net/net.c
+++ b/net/net.c
@@ -41,6 +41,7 @@ 
 #include "qapi-visit.h"
 #include "qapi/opts-visitor.h"
 #include "qapi/dealloc-visitor.h"
+#include "replay/replay.h"
 
 /* Net bridge is currently not supported for W32. */
 #if !defined(_WIN32)
@@ -799,6 +800,7 @@  static int (* const net_client_init_fun[NET_CLIENT_OPTIONS_KIND_MAX])(
         [NET_CLIENT_OPTIONS_KIND_NETMAP]    = net_init_netmap,
 #endif
         [NET_CLIENT_OPTIONS_KIND_DUMP]      = net_init_dump,
+        [NET_CLIENT_OPTIONS_KIND_REPLAY]   = net_init_replay,
 #ifdef CONFIG_NET_BRIDGE
         [NET_CLIENT_OPTIONS_KIND_BRIDGE]    = net_init_bridge,
 #endif
@@ -1264,7 +1266,12 @@  int net_init_clients(void)
         /* if no clients, we use a default config */
         qemu_opts_set(net, NULL, "type", "nic");
 #ifdef CONFIG_SLIRP
-        qemu_opts_set(net, NULL, "type", "user");
+        if (replay_mode != REPLAY_PLAY) {
+            qemu_opts_set(net, NULL, "type", "user");
+        }
+        if (replay_mode == REPLAY_PLAY) {
+            qemu_opts_set(net, NULL, "type", "replay");
+        }
 #endif
     }
 
diff --git a/net/slirp.c b/net/slirp.c
index 8fddc03..bb7030d
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -36,6 +36,7 @@ 
 #include "qemu/sockets.h"
 #include "slirp/libslirp.h"
 #include "sysemu/char.h"
+#include "replay/replay.h"
 
 static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
 {
@@ -103,7 +104,11 @@  void slirp_output(void *opaque, const uint8_t *pkt, int pkt_len)
 {
     SlirpState *s = opaque;
 
-    qemu_send_packet(&s->nc, pkt, pkt_len);
+    if (replay_mode == REPLAY_SAVE) {
+        replay_save_net_packet(&s->nc, pkt, pkt_len);
+    } else {
+        qemu_send_packet(&s->nc, pkt, pkt_len);
+    }
 }
 
 static ssize_t net_slirp_receive(NetClientState *nc, const uint8_t *buf, size_t size)
@@ -267,6 +272,13 @@  static int net_slirp_init(NetClientState *peer, const char *model,
     }
 #endif
 
+    if (replay_mode == REPLAY_PLAY) {
+        printf("Network interface \"user\" is not permitted with replay\n");
+        exit(1);
+    } else {
+        replay_add_network_client(nc);
+    }
+    
     return 0;
 
 error:
diff --git a/net/socket.c b/net/socket.c
index fb21e20..cfb0dbf
--- a/net/socket.c
+++ b/net/socket.c
@@ -32,6 +32,7 @@ 
 #include "qemu/sockets.h"
 #include "qemu/iov.h"
 #include "qemu/main-loop.h"
+#include "replay/replay.h"
 
 typedef struct NetSocketState {
     NetClientState nc;
@@ -211,7 +212,11 @@  static void net_socket_send(void *opaque)
             buf += l;
             size -= l;
             if (s->index >= s->packet_len) {
-                qemu_send_packet(&s->nc, s->buf, s->packet_len);
+                if (replay_mode == REPLAY_SAVE) {
+                    replay_save_net_packet(&s->nc, s->buf, s->packet_len);
+                } else {
+                    qemu_send_packet(&s->nc, s->buf, s->packet_len);
+                }
                 s->index = 0;
                 s->state = 0;
             }
@@ -234,7 +239,11 @@  static void net_socket_send_dgram(void *opaque)
         net_socket_write_poll(s, false);
         return;
     }
-    qemu_send_packet(&s->nc, s->buf, size);
+    if (replay_mode == REPLAY_SAVE) {
+        replay_save_net_packet(&s->nc, s->buf, size);
+    } else {
+        qemu_send_packet(&s->nc, s->buf, size);
+    }
 }
 
 static int net_socket_mcast_create(struct sockaddr_in *mcastaddr, struct in_addr *localaddr)
@@ -406,6 +415,13 @@  static NetSocketState *net_socket_fd_init_dgram(NetClientState *peer,
         s->dgram_dst = saddr;
     }
 
+    if (replay_mode == REPLAY_PLAY) {
+        printf("Network interface \"socket\" is not permitted with replay\n");
+        exit(1);
+    } else {
+        replay_add_network_client(nc);
+    }
+    
     return s;
 
 err:
@@ -452,6 +468,14 @@  static NetSocketState *net_socket_fd_init_stream(NetClientState *peer,
     } else {
         qemu_set_fd_handler(s->fd, NULL, net_socket_connect, s);
     }
+
+    if (replay_mode == REPLAY_PLAY) {
+        printf("Network interface \"socket\" is not permitted with replay\n");
+        exit(1);
+    } else {
+        replay_add_network_client(nc);
+    }
+    
     return s;
 }
 
@@ -549,6 +573,14 @@  static int net_socket_listen_init(NetClientState *peer,
     s->nc.link_down = true;
 
     qemu_set_fd_handler(s->listen_fd, net_socket_accept, NULL, s);
+
+    if (replay_mode == REPLAY_PLAY) {
+        printf("Network interface \"socked\" is not permitted with replay\n");
+        exit(1);
+    } else {
+        replay_add_network_client(nc);
+    }
+    
     return 0;
 }
 
@@ -599,6 +631,7 @@  static int net_socket_connect_init(NetClientState *peer,
     snprintf(s->nc.info_str, sizeof(s->nc.info_str),
              "socket: connect to %s:%d",
              inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
+
     return 0;
 }
 
@@ -637,8 +670,8 @@  static int net_socket_mcast_init(NetClientState *peer,
     snprintf(s->nc.info_str, sizeof(s->nc.info_str),
              "socket: mcast=%s:%d",
              inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
-    return 0;
 
+    return 0;
 }
 
 static int net_socket_udp_init(NetClientState *peer,
@@ -688,6 +721,7 @@  static int net_socket_udp_init(NetClientState *peer,
     snprintf(s->nc.info_str, sizeof(s->nc.info_str),
              "socket: udp=%s:%d",
              inet_ntoa(raddr.sin_addr), ntohs(raddr.sin_port));
+
     return 0;
 }
 
diff --git a/net/tap-win32.c b/net/tap-win32.c
index 8aee611..2de43f8 100644
--- a/net/tap-win32.c
+++ b/net/tap-win32.c
@@ -34,6 +34,7 @@ 
 #include "net/tap.h"            /* tap_has_ufo, ... */
 #include "sysemu/sysemu.h"
 #include "qemu/error-report.h"
+#include "replay/replay.h"
 #include <stdio.h>
 #include <windows.h>
 #include <winioctl.h>
@@ -99,6 +100,7 @@  typedef struct tap_win32_overlapped {
     HANDLE output_queue_semaphore;
     HANDLE free_list_semaphore;
     HANDLE tap_semaphore;
+    HANDLE hThread;
     CRITICAL_SECTION output_queue_cs;
     CRITICAL_SECTION free_list_cs;
     OVERLAPPED read_overlapped;
@@ -625,7 +627,7 @@  static int tap_win32_open(tap_win32_overlapped_t **phandle,
 
     *phandle = &tap_overlapped;
 
-    CreateThread(NULL, 0, tap_win32_thread_entry,
+    tap_overlapped.hThread = CreateThread(NULL, 0, tap_win32_thread_entry,
                  (LPVOID)&tap_overlapped, 0, &idThread);
     return 0;
 }
@@ -646,6 +648,8 @@  static void tap_cleanup(NetClientState *nc)
     /* FIXME: need to kill thread and close file handle:
        tap_win32_close(s);
     */
+    TerminateThread(s->handle->hThread, 0);
+    CloseHandle(s->handle->handle);
 }
 
 static ssize_t tap_receive(NetClientState *nc, const uint8_t *buf, size_t size)
@@ -664,7 +668,11 @@  static void tap_win32_send(void *opaque)
 
     size = tap_win32_read(s->handle, &buf, max_size);
     if (size > 0) {
-        qemu_send_packet(&s->nc, buf, size);
+        if (replay_mode == REPLAY_SAVE) {
+            replay_save_net_packet(&s->nc, buf, size);
+        } else {
+            qemu_send_packet(&s->nc, buf, size);
+        }
         tap_win32_free_buffer(s->handle, buf);
     }
 }
@@ -748,6 +756,13 @@  static int tap_win32_init(NetClientState *peer, const char *model,
 
     qemu_add_wait_object(s->handle->tap_semaphore, tap_win32_send, s);
 
+    if (replay_mode == REPLAY_PLAY) {
+        fprintf(stderr, "Network interface \"tap\" is not permitted with replay\n");
+        exit(1);
+    } else {
+        replay_add_network_client(nc);
+    }
+    
     return 0;
 }
 
diff --git a/net/tap.c b/net/tap.c
index a40f7f0..761e855
--- a/net/tap.c
+++ b/net/tap.c
@@ -39,6 +39,7 @@ 
 #include "sysemu/sysemu.h"
 #include "qemu-common.h"
 #include "qemu/error-report.h"
+#include "replay/replay.h"
 
 #include "net/tap.h"
 
@@ -203,12 +204,17 @@  static void tap_send(void *opaque)
             size -= s->host_vnet_hdr_len;
         }
 
-        size = qemu_send_packet_async(&s->nc, buf, size, tap_send_completed);
-        if (size == 0) {
-            tap_read_poll(s, false);
-            break;
-        } else if (size < 0) {
+        if (replay_mode == REPLAY_SAVE) {
+            replay_save_net_packet(&s->nc, buf, size);
             break;
+        } else {
+            size = qemu_send_packet_async(&s->nc, buf, size, tap_send_completed);
+            if (size == 0) {
+                tap_read_poll(s, false);
+                break;
+            } else if (size < 0) {
+                break;
+            }
         }
     }
 }
@@ -353,6 +359,14 @@  static TAPState *net_tap_fd_init(NetClientState *peer,
     }
     tap_read_poll(s, true);
     s->vhost_net = NULL;
+
+    if (replay_mode == REPLAY_PLAY) {
+        fprintf(stderr, "Using network interface \"tap\" is not permitted with replay option\n");
+        exit(1);
+    } else {
+        replay_add_network_client(nc);
+    }
+
     return s;
 }
 
diff --git a/net/vde.c b/net/vde.c
index 2a619fb..41d48af 100644
--- a/net/vde.c
+++ b/net/vde.c
@@ -30,6 +30,7 @@ 
 #include "qemu-common.h"
 #include "qemu/option.h"
 #include "qemu/main-loop.h"
+#include "replay/replay.h"
 
 typedef struct VDEState {
     NetClientState nc;
@@ -44,7 +45,11 @@  static void vde_to_qemu(void *opaque)
 
     size = vde_recv(s->vde, (char *)buf, sizeof(buf), 0);
     if (size > 0) {
-        qemu_send_packet(&s->nc, buf, size);
+        if (replay_mode == REPLAY_SAVE) {
+            replay_save_net_packet(&s->nc, buf, size);
+        } else {
+            qemu_send_packet(&s->nc, buf, size);
+        }
     }
 }
 
@@ -106,6 +111,13 @@  static int net_vde_init(NetClientState *peer, const char *model,
 
     qemu_set_fd_handler(vde_datafd(s->vde), vde_to_qemu, NULL, s);
 
+    if (replay_mode == REPLAY_PLAY) {
+        printf("Network interface \"vde\" is not permitted with replay\n");
+        exit(1);
+    } else {
+        replay_add_network_client(nc);
+    }
+    
     return 0;
 }
 
diff --git a/qapi-schema.json b/qapi-schema.json
index b11aad2..c5c5082 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2145,6 +2145,15 @@ 
     '*file': 'str' } }
 
 ##
+# @NetdevReplayOptions
+#
+# Dump network traffic to the log in replay mode.
+#
+##
+{ 'type': 'NetdevReplayOptions',
+  'data': { } }
+
+##
 # @NetdevBridgeOptions
 #
 # Connect a host TAP network interface to a host bridge device.
@@ -2230,6 +2239,7 @@ 
     'socket':   'NetdevSocketOptions',
     'vde':      'NetdevVdeOptions',
     'dump':     'NetdevDumpOptions',
+    'replay':   'NetdevReplayOptions',
     'bridge':   'NetdevBridgeOptions',
     'hubport':  'NetdevHubPortOptions',
     'netmap':   'NetdevNetmapOptions',

diff --git a/slirp/slirp.c b/slirp/slirp.c
index 35f819a..f6b1440 100644
--- a/slirp/slirp.c
+++ b/slirp/slirp.c
@@ -26,6 +26,7 @@ 
 #include "sysemu/char.h"
 #include "slirp.h"
 #include "hw/hw.h"
+#include "replay/replay.h"
 
 /* host loopback address */
 struct in_addr loopback_addr;
@@ -234,8 +235,10 @@  Slirp *slirp_init(int restricted, struct in_addr vnetwork,
 
     slirp->opaque = opaque;
 
-    register_savevm(NULL, "slirp", 0, 3,
-                    slirp_state_save, slirp_state_load, slirp);
+    if (replay_mode == REPLAY_NONE) {
+        register_savevm(NULL, "slirp", 0, 3,
+                        slirp_state_save, slirp_state_load, slirp);
+    }
 
     QTAILQ_INSERT_TAIL(&slirp_instances, slirp, entry);