record/replay: add network support
This patch adds support of recording and replaying network packets in irount rr mode. Record and replay for network interactions is performed with the network filter. Each backend must have its own instance of the replay filter as follows: -netdev user,id=net1 -device rtl8139,netdev=net1 -object filter-replay,id=replay,netdev=net1 Replay network filter is used to record and replay network packets. While recording the virtual machine this filter puts all packets coming from the outer world into the log. In replay mode packets from the log are injected into the network device. All interactions with network backend in replay mode are disabled. v5 changes: - using iov_to_buf function instead of loop Signed-off-by: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru> Signed-off-by: Jason Wang <jasowang@redhat.com>
This commit is contained in:
parent
e92fbc753d
commit
646c5478c0
@ -195,3 +195,17 @@ Queue is flushed at checkpoints and information about processed requests
|
|||||||
is recorded to the log. In replay phase the queue is matched with
|
is recorded to the log. In replay phase the queue is matched with
|
||||||
events read from the log. Therefore block devices requests are processed
|
events read from the log. Therefore block devices requests are processed
|
||||||
deterministically.
|
deterministically.
|
||||||
|
|
||||||
|
Network devices
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Record and replay for network interactions is performed with the network filter.
|
||||||
|
Each backend must have its own instance of the replay filter as follows:
|
||||||
|
-netdev user,id=net1 -device rtl8139,netdev=net1
|
||||||
|
-object filter-replay,id=replay,netdev=net1
|
||||||
|
|
||||||
|
Replay network filter is used to record and replay network packets. While
|
||||||
|
recording the virtual machine this filter puts all packets coming from
|
||||||
|
the outer world into the log. In replay mode packets from the log are
|
||||||
|
injected into the network device. All interactions with network backend
|
||||||
|
in replay mode are disabled.
|
||||||
|
@ -39,6 +39,8 @@ enum ReplayCheckpoint {
|
|||||||
};
|
};
|
||||||
typedef enum ReplayCheckpoint ReplayCheckpoint;
|
typedef enum ReplayCheckpoint ReplayCheckpoint;
|
||||||
|
|
||||||
|
typedef struct ReplayNetState ReplayNetState;
|
||||||
|
|
||||||
extern ReplayMode replay_mode;
|
extern ReplayMode replay_mode;
|
||||||
|
|
||||||
/* Replay process control functions */
|
/* Replay process control functions */
|
||||||
@ -137,4 +139,14 @@ void replay_char_read_all_save_error(int res);
|
|||||||
/*! Writes character read_all execution result into the replay log. */
|
/*! Writes character read_all execution result into the replay log. */
|
||||||
void replay_char_read_all_save_buf(uint8_t *buf, int offset);
|
void replay_char_read_all_save_buf(uint8_t *buf, int offset);
|
||||||
|
|
||||||
|
/* Network */
|
||||||
|
|
||||||
|
/*! Registers replay network filter attached to some backend. */
|
||||||
|
ReplayNetState *replay_register_net(NetFilterState *nfs);
|
||||||
|
/*! Unregisters replay network filter. */
|
||||||
|
void replay_unregister_net(ReplayNetState *rns);
|
||||||
|
/*! Called to write network packet to the replay log. */
|
||||||
|
void replay_net_packet_event(ReplayNetState *rns, unsigned flags,
|
||||||
|
const struct iovec *iov, int iovcnt);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -19,3 +19,4 @@ common-obj-y += filter-mirror.o
|
|||||||
common-obj-y += colo-compare.o
|
common-obj-y += colo-compare.o
|
||||||
common-obj-y += colo.o
|
common-obj-y += colo.o
|
||||||
common-obj-y += filter-rewriter.o
|
common-obj-y += filter-rewriter.o
|
||||||
|
common-obj-y += filter-replay.o
|
||||||
|
92
net/filter-replay.c
Normal file
92
net/filter-replay.c
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* filter-replay.c
|
||||||
|
*
|
||||||
|
* Copyright (c) 2010-2016 Institute for System Programming
|
||||||
|
* of the Russian Academy of Sciences.
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||||
|
* See the COPYING file in the top-level directory.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "clients.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
#include "qemu-common.h"
|
||||||
|
#include "qemu/error-report.h"
|
||||||
|
#include "qemu/iov.h"
|
||||||
|
#include "qemu/log.h"
|
||||||
|
#include "qemu/timer.h"
|
||||||
|
#include "qapi/visitor.h"
|
||||||
|
#include "net/filter.h"
|
||||||
|
#include "sysemu/replay.h"
|
||||||
|
|
||||||
|
#define TYPE_FILTER_REPLAY "filter-replay"
|
||||||
|
|
||||||
|
#define FILTER_REPLAY(obj) \
|
||||||
|
OBJECT_CHECK(NetFilterReplayState, (obj), TYPE_FILTER_REPLAY)
|
||||||
|
|
||||||
|
struct NetFilterReplayState {
|
||||||
|
NetFilterState nfs;
|
||||||
|
ReplayNetState *rns;
|
||||||
|
};
|
||||||
|
typedef struct NetFilterReplayState NetFilterReplayState;
|
||||||
|
|
||||||
|
static ssize_t filter_replay_receive_iov(NetFilterState *nf,
|
||||||
|
NetClientState *sndr,
|
||||||
|
unsigned flags,
|
||||||
|
const struct iovec *iov,
|
||||||
|
int iovcnt, NetPacketSent *sent_cb)
|
||||||
|
{
|
||||||
|
NetFilterReplayState *nfrs = FILTER_REPLAY(nf);
|
||||||
|
switch (replay_mode) {
|
||||||
|
case REPLAY_MODE_RECORD:
|
||||||
|
if (nf->netdev == sndr) {
|
||||||
|
replay_net_packet_event(nfrs->rns, flags, iov, iovcnt);
|
||||||
|
return iov_size(iov, iovcnt);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
case REPLAY_MODE_PLAY:
|
||||||
|
/* Drop all packets in replay mode.
|
||||||
|
Packets from the log will be injected by the replay module. */
|
||||||
|
return iov_size(iov, iovcnt);
|
||||||
|
default:
|
||||||
|
/* Pass all the packets. */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void filter_replay_instance_init(Object *obj)
|
||||||
|
{
|
||||||
|
NetFilterReplayState *nfrs = FILTER_REPLAY(obj);
|
||||||
|
nfrs->rns = replay_register_net(&nfrs->nfs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void filter_replay_instance_finalize(Object *obj)
|
||||||
|
{
|
||||||
|
NetFilterReplayState *nfrs = FILTER_REPLAY(obj);
|
||||||
|
replay_unregister_net(nfrs->rns);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void filter_replay_class_init(ObjectClass *oc, void *data)
|
||||||
|
{
|
||||||
|
NetFilterClass *nfc = NETFILTER_CLASS(oc);
|
||||||
|
|
||||||
|
nfc->receive_iov = filter_replay_receive_iov;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const TypeInfo filter_replay_info = {
|
||||||
|
.name = TYPE_FILTER_REPLAY,
|
||||||
|
.parent = TYPE_NETFILTER,
|
||||||
|
.class_init = filter_replay_class_init,
|
||||||
|
.instance_init = filter_replay_instance_init,
|
||||||
|
.instance_finalize = filter_replay_instance_finalize,
|
||||||
|
.instance_size = sizeof(NetFilterReplayState),
|
||||||
|
};
|
||||||
|
|
||||||
|
static void filter_replay_register_types(void)
|
||||||
|
{
|
||||||
|
type_register_static(&filter_replay_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
type_init(filter_replay_register_types);
|
@ -5,3 +5,4 @@ common-obj-y += replay-time.o
|
|||||||
common-obj-y += replay-input.o
|
common-obj-y += replay-input.o
|
||||||
common-obj-y += replay-char.o
|
common-obj-y += replay-char.o
|
||||||
common-obj-y += replay-snapshot.o
|
common-obj-y += replay-snapshot.o
|
||||||
|
common-obj-y += replay-net.o
|
||||||
|
@ -54,6 +54,9 @@ static void replay_run_event(Event *event)
|
|||||||
case REPLAY_ASYNC_EVENT_BLOCK:
|
case REPLAY_ASYNC_EVENT_BLOCK:
|
||||||
aio_bh_call(event->opaque);
|
aio_bh_call(event->opaque);
|
||||||
break;
|
break;
|
||||||
|
case REPLAY_ASYNC_EVENT_NET:
|
||||||
|
replay_event_net_run(event->opaque);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
error_report("Replay: invalid async event ID (%d) in the queue",
|
error_report("Replay: invalid async event ID (%d) in the queue",
|
||||||
event->event_kind);
|
event->event_kind);
|
||||||
@ -189,6 +192,9 @@ static void replay_save_event(Event *event, int checkpoint)
|
|||||||
case REPLAY_ASYNC_EVENT_BLOCK:
|
case REPLAY_ASYNC_EVENT_BLOCK:
|
||||||
replay_put_qword(event->id);
|
replay_put_qword(event->id);
|
||||||
break;
|
break;
|
||||||
|
case REPLAY_ASYNC_EVENT_NET:
|
||||||
|
replay_event_net_save(event->opaque);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
error_report("Unknown ID %" PRId64 " of replay event", event->id);
|
error_report("Unknown ID %" PRId64 " of replay event", event->id);
|
||||||
exit(1);
|
exit(1);
|
||||||
@ -252,6 +258,11 @@ static Event *replay_read_event(int checkpoint)
|
|||||||
read_id = replay_get_qword();
|
read_id = replay_get_qword();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case REPLAY_ASYNC_EVENT_NET:
|
||||||
|
event = g_malloc0(sizeof(Event));
|
||||||
|
event->event_kind = read_event_kind;
|
||||||
|
event->opaque = replay_event_net_load();
|
||||||
|
return event;
|
||||||
default:
|
default:
|
||||||
error_report("Unknown ID %d of replay event", read_event_kind);
|
error_report("Unknown ID %d of replay event", read_event_kind);
|
||||||
exit(1);
|
exit(1);
|
||||||
|
@ -50,6 +50,7 @@ enum ReplayAsyncEventKind {
|
|||||||
REPLAY_ASYNC_EVENT_INPUT_SYNC,
|
REPLAY_ASYNC_EVENT_INPUT_SYNC,
|
||||||
REPLAY_ASYNC_EVENT_CHAR_READ,
|
REPLAY_ASYNC_EVENT_CHAR_READ,
|
||||||
REPLAY_ASYNC_EVENT_BLOCK,
|
REPLAY_ASYNC_EVENT_BLOCK,
|
||||||
|
REPLAY_ASYNC_EVENT_NET,
|
||||||
REPLAY_ASYNC_COUNT
|
REPLAY_ASYNC_COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -161,6 +162,15 @@ void replay_event_char_read_save(void *opaque);
|
|||||||
/*! Reads char event read from the file. */
|
/*! Reads char event read from the file. */
|
||||||
void *replay_event_char_read_load(void);
|
void *replay_event_char_read_load(void);
|
||||||
|
|
||||||
|
/* Network devices */
|
||||||
|
|
||||||
|
/*! Called to run network event. */
|
||||||
|
void replay_event_net_run(void *opaque);
|
||||||
|
/*! Writes network event to the file. */
|
||||||
|
void replay_event_net_save(void *opaque);
|
||||||
|
/*! Reads network from the file. */
|
||||||
|
void *replay_event_net_load(void);
|
||||||
|
|
||||||
/* VMState-related functions */
|
/* VMState-related functions */
|
||||||
|
|
||||||
/* Registers replay VMState.
|
/* Registers replay VMState.
|
||||||
|
102
replay/replay-net.c
Normal file
102
replay/replay-net.c
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* replay-net.c
|
||||||
|
*
|
||||||
|
* Copyright (c) 2010-2016 Institute for System Programming
|
||||||
|
* of the Russian Academy of Sciences.
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||||
|
* See the COPYING file in the top-level directory.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "qemu/error-report.h"
|
||||||
|
#include "sysemu/replay.h"
|
||||||
|
#include "replay-internal.h"
|
||||||
|
#include "sysemu/sysemu.h"
|
||||||
|
#include "net/net.h"
|
||||||
|
#include "net/filter.h"
|
||||||
|
#include "qemu/iov.h"
|
||||||
|
|
||||||
|
struct ReplayNetState {
|
||||||
|
NetFilterState *nfs;
|
||||||
|
int id;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct NetEvent {
|
||||||
|
uint8_t id;
|
||||||
|
uint32_t flags;
|
||||||
|
uint8_t *data;
|
||||||
|
size_t size;
|
||||||
|
} NetEvent;
|
||||||
|
|
||||||
|
static NetFilterState **network_filters;
|
||||||
|
static int network_filters_count;
|
||||||
|
|
||||||
|
ReplayNetState *replay_register_net(NetFilterState *nfs)
|
||||||
|
{
|
||||||
|
ReplayNetState *rns = g_new0(ReplayNetState, 1);
|
||||||
|
rns->nfs = nfs;
|
||||||
|
rns->id = network_filters_count++;
|
||||||
|
network_filters = g_realloc(network_filters,
|
||||||
|
network_filters_count
|
||||||
|
* sizeof(*network_filters));
|
||||||
|
network_filters[network_filters_count - 1] = nfs;
|
||||||
|
return rns;
|
||||||
|
}
|
||||||
|
|
||||||
|
void replay_unregister_net(ReplayNetState *rns)
|
||||||
|
{
|
||||||
|
network_filters[rns->id] = NULL;
|
||||||
|
g_free(rns);
|
||||||
|
}
|
||||||
|
|
||||||
|
void replay_net_packet_event(ReplayNetState *rns, unsigned flags,
|
||||||
|
const struct iovec *iov, int iovcnt)
|
||||||
|
{
|
||||||
|
NetEvent *event = g_new(NetEvent, 1);
|
||||||
|
event->flags = flags;
|
||||||
|
event->data = g_malloc(iov_size(iov, iovcnt));
|
||||||
|
event->size = iov_size(iov, iovcnt);
|
||||||
|
event->id = rns->id;
|
||||||
|
iov_to_buf(iov, iovcnt, 0, event->data, event->size);
|
||||||
|
|
||||||
|
replay_add_event(REPLAY_ASYNC_EVENT_NET, event, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void replay_event_net_run(void *opaque)
|
||||||
|
{
|
||||||
|
NetEvent *event = opaque;
|
||||||
|
struct iovec iov = {
|
||||||
|
.iov_base = (void *)event->data,
|
||||||
|
.iov_len = event->size
|
||||||
|
};
|
||||||
|
|
||||||
|
assert(event->id < network_filters_count);
|
||||||
|
|
||||||
|
qemu_netfilter_pass_to_next(network_filters[event->id]->netdev,
|
||||||
|
event->flags, &iov, 1, network_filters[event->id]);
|
||||||
|
|
||||||
|
g_free(event->data);
|
||||||
|
g_free(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void replay_event_net_save(void *opaque)
|
||||||
|
{
|
||||||
|
NetEvent *event = opaque;
|
||||||
|
|
||||||
|
replay_put_byte(event->id);
|
||||||
|
replay_put_dword(event->flags);
|
||||||
|
replay_put_array(event->data, event->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *replay_event_net_load(void)
|
||||||
|
{
|
||||||
|
NetEvent *event = g_new(NetEvent, 1);
|
||||||
|
|
||||||
|
event->id = replay_get_byte();
|
||||||
|
event->flags = replay_get_dword();
|
||||||
|
replay_get_array_alloc(&event->data, &event->size);
|
||||||
|
|
||||||
|
return event;
|
||||||
|
}
|
@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
/* Current version of the replay mechanism.
|
/* Current version of the replay mechanism.
|
||||||
Increase it when file format changes. */
|
Increase it when file format changes. */
|
||||||
#define REPLAY_VERSION 0xe02004
|
#define REPLAY_VERSION 0xe02005
|
||||||
/* Size of replay log header */
|
/* Size of replay log header */
|
||||||
#define HEADER_SIZE (sizeof(uint32_t) + sizeof(uint64_t))
|
#define HEADER_SIZE (sizeof(uint32_t) + sizeof(uint64_t))
|
||||||
|
|
||||||
|
3
vl.c
3
vl.c
@ -2859,7 +2859,8 @@ static bool object_create_initial(const char *type)
|
|||||||
g_str_equal(type, "filter-mirror") ||
|
g_str_equal(type, "filter-mirror") ||
|
||||||
g_str_equal(type, "filter-redirector") ||
|
g_str_equal(type, "filter-redirector") ||
|
||||||
g_str_equal(type, "colo-compare") ||
|
g_str_equal(type, "colo-compare") ||
|
||||||
g_str_equal(type, "filter-rewriter")) {
|
g_str_equal(type, "filter-rewriter") ||
|
||||||
|
g_str_equal(type, "filter-replay")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user