/*
 * 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 "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;
}
