#include <config.h>
#include "classifier.h"
#include "fuzzer.h"
#include "dp-packet.h"
#include "flow.h"
#include "openvswitch/ofp-match.h"
#include "openvswitch/ofp-print.h"
#include "openvswitch/match.h"
#include "classifier-private.h"
#include "util.h"

/* Returns a copy of 'src'.  The caller must eventually free the returned
 * miniflow with free(). */
static struct miniflow *
miniflow_clone__(const struct miniflow *src)
{
    struct miniflow *dst;
    size_t data_size;

    data_size = miniflow_alloc(&dst, 1, src);
    miniflow_clone(dst, src, data_size / sizeof(uint64_t));
    return dst;
}

/* Returns a hash value for 'flow', given 'basis'. */
static inline uint32_t
miniflow_hash__(const struct miniflow *flow, uint32_t basis)
{
    const uint64_t *p = miniflow_get_values(flow);
    size_t n_values = miniflow_n_values(flow);
    struct flowmap hash_map = FLOWMAP_EMPTY_INITIALIZER;
    uint32_t hash = basis;
    size_t idx;

    FLOWMAP_FOR_EACH_INDEX (idx, flow->map) {
        uint64_t value = *p++;

        if (value) {
            hash = hash_add64(hash, value);
            flowmap_set(&hash_map, idx, 1);
        }
    }
    map_t map;
    FLOWMAP_FOR_EACH_MAP (map, hash_map) {
        hash = hash_add64(hash, map);
    }

    return hash_finish(hash, n_values);
}

#define FLOW_U32S (FLOW_U64S * 2)

static void
toggle_masked_flow_bits(struct flow *flow, const struct flow_wildcards *mask)
{
    const uint32_t *mask_u32 = (const uint32_t *) &mask->masks;
    uint32_t *flow_u32 = (uint32_t *) flow;
    int i;

    for (i = 0; i < FLOW_U32S; i++) {
        if (mask_u32[i] != 0) {
            uint32_t bit;

            do {
                bit = 1u << random_range(32);
            } while (!(bit & mask_u32[i]));
            flow_u32[i] ^= bit;
        }
    }
}

static void
wildcard_extra_bits(struct flow_wildcards *mask)
{
    uint32_t *mask_u32 = (uint32_t *) &mask->masks;
    int i;

    for (i = 0; i < FLOW_U32S; i++) {
        if (mask_u32[i] != 0) {
            uint32_t bit;

            do {
                bit = 1u << random_range(32);
            } while (!(bit & mask_u32[i]));
            mask_u32[i] &= ~bit;
        }
    }
}

static void
test_miniflow(struct flow *flow)
{
    struct miniflow *miniflow, *miniflow2, *miniflow3;
    struct flow flow2, flow3;
    struct flow_wildcards mask;
    struct minimask *minimask;
    int i;

    const uint64_t *flow_u64 = (const uint64_t *) flow;

    /* Convert flow to miniflow. */
    miniflow = miniflow_create(flow);

    /* Obtain miniflow hash. */
    uint32_t hash = miniflow_hash_5tuple(miniflow, 0);
    ignore(hash);

    /* Check that the flow equals its miniflow. */
    for (i = 0; i < FLOW_MAX_VLAN_HEADERS; i++) {
        ovs_assert(miniflow_get_vid(miniflow, i) ==
               vlan_tci_to_vid(flow->vlans[i].tci));
    }
    for (i = 0; i < FLOW_U64S; i++) {
        ovs_assert(miniflow_get(miniflow, i) == flow_u64[i]);
    }

    /* Check that the miniflow equals itself. */
    ovs_assert(miniflow_equal(miniflow, miniflow));

    /* Convert miniflow back to flow and verify that it's the same. */
    miniflow_expand(miniflow, &flow2);
    ovs_assert(flow_equal(flow, &flow2));
    /* Check that copying a miniflow works properly. */
    miniflow2 = miniflow_clone__(miniflow);
    ovs_assert(miniflow_equal(miniflow, miniflow2));
    ovs_assert(miniflow_hash__(miniflow, 0) == miniflow_hash__(miniflow2, 0));
    miniflow_expand(miniflow2, &flow3);
    ovs_assert(flow_equal(flow, &flow3));

    /* Check that masked matches work as expected for identical flows and
         * miniflows. */
    flow_wildcards_init_for_packet(&mask, flow);
    /* Ensure that mask is not catchall just in case
     * flow_wildcards_init_for_packet returns a catchall mask
     */
    uint64_t *mask_u64 = (uint64_t *) &mask.masks;
    mask_u64[0] = 1;
    ovs_assert(!flow_wildcards_is_catchall(&mask));
    minimask = minimask_create(&mask);
    ovs_assert(!minimask_is_catchall(minimask));
    ovs_assert(miniflow_equal_in_minimask(miniflow, miniflow2, minimask));
    ovs_assert(miniflow_equal_flow_in_minimask(miniflow, &flow2, minimask));
    ovs_assert(miniflow_hash_in_minimask(miniflow, minimask, 0x12345678) ==
           flow_hash_in_minimask(flow, minimask, 0x12345678));
    ovs_assert(minimask_hash(minimask, 0) ==
           miniflow_hash__(&minimask->masks, 0));

    /* Check that masked matches work as expected for differing flows and
     * miniflows. */
    toggle_masked_flow_bits(&flow2, &mask);
    ovs_assert(!miniflow_equal_flow_in_minimask(miniflow, &flow2, minimask));
    miniflow3 = miniflow_create(&flow2);
    ovs_assert(!miniflow_equal_in_minimask(miniflow, miniflow3, minimask));

    free(miniflow);
    free(miniflow2);
    free(miniflow3);
    free(minimask);
}

static void
test_minimask_has_extra(struct flow *flow)
{
    struct flow_wildcards catchall;
    struct minimask *minicatchall;

    flow_wildcards_init_catchall(&catchall);
    minicatchall = minimask_create(&catchall);
    ovs_assert(minimask_is_catchall(minicatchall));

    struct flow_wildcards mask;
    struct minimask *minimask;

    mask.masks = *flow;
    minimask = minimask_create(&mask);
    ovs_assert(!minimask_has_extra(minimask, minimask));
    ovs_assert(minimask_has_extra(minicatchall, minimask)
           == !minimask_is_catchall(minimask));
    if (!minimask_is_catchall(minimask)) {
        struct minimask *minimask2;

        wildcard_extra_bits(&mask);
        minimask2 = minimask_create(&mask);
        ovs_assert(minimask_has_extra(minimask2, minimask));
        ovs_assert(!minimask_has_extra(minimask, minimask2));
        free(minimask2);
    }

    free(minimask);
    free(minicatchall);
}

static void
test_minimask_combine(struct flow *flow)
{
    struct flow_wildcards catchall;
    struct minimask *minicatchall;

    flow_wildcards_init_catchall(&catchall);
    minicatchall = minimask_create(&catchall);
    ovs_assert(minimask_is_catchall(minicatchall));

    struct minimask *minimask, *minimask2;
    struct flow_wildcards mask, mask2, combined, combined2;
    struct {
        struct minimask minicombined;
        uint64_t storage[FLOW_U64S];
    } m;
    struct flow flow2 = {0};

    mask.masks = *flow;
    minimask = minimask_create(&mask);

    minimask_combine(&m.minicombined, minimask, minicatchall, m.storage);
    ovs_assert(minimask_is_catchall(&m.minicombined));

    /* Create mask based on zero flow */
    mask2.masks = flow2;
    minimask2 = minimask_create(&mask2);

    minimask_combine(&m.minicombined, minimask, minimask2, m.storage);
    flow_wildcards_and(&combined, &mask, &mask2);
    minimask_expand(&m.minicombined, &combined2);
    ovs_assert(flow_wildcards_equal(&combined, &combined2));

    free(minimask);
    free(minimask2);

    free(minicatchall);
}

int
LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
    struct dp_packet packet;
    struct flow flow;
    dp_packet_use_const(&packet, data, size);
    flow_extract(&packet, &flow);

    /* Do miniflow tests. */
    test_miniflow(&flow);
    test_minimask_has_extra(&flow);
    test_minimask_combine(&flow);

    return 0;
}
