/* $Id$ */

/*
 *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
 *   Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
 *
 *   The Tcpreplay Suite of tools is free software: you can redistribute it 
 *   and/or modify it under the terms of the GNU General Public License as 
 *   published by the Free Software Foundation, either version 3 of the 
 *   License, or with the authors permission any later version.
 *
 *   The Tcpreplay Suite is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with the Tcpreplay Suite.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <ctype.h>
#include <sys/types.h>
#include <stdlib.h>

#include "config.h"
#include "defines.h"
#include "tcpedit.h"
#include "portmap.h"
#include "dlt_utils.h"

/**
 * Set output DLT plugin by using it's DLT_<type>.  Note that the user plugin
 * is DLT_USER0.
 */
int
tcpedit_set_encoder_dltplugin_byid(tcpedit_t *tcpedit, int dlt)
{
    tcpeditdlt_plugin_t *plugin;
    tcpeditdlt_t *ctx;

    assert(tcpedit);

    ctx = tcpedit->dlt_ctx;
    assert(ctx);

    if (ctx->encoder) {
        tcpedit_seterr(tcpedit, "You have already selected a DLT encoder: %s", ctx->encoder->name);
        return TCPEDIT_ERROR;
    }

    plugin = tcpedit_dlt_getplugin(ctx, dlt);
    if (plugin == NULL) {
        tcpedit_seterr(tcpedit, "No output DLT plugin decoder with DLT type: 0x%04x", dlt);
        return TCPEDIT_ERROR;
    }

    ctx->encoder = plugin;

    /* init the encoder plugin if it's not the decoder plugin too */
    if (ctx->encoder->dlt != ctx->decoder->dlt) {
        if (ctx->encoder->plugin_init(ctx) != TCPEDIT_OK) {
            /* plugin should generate the error */
            return TCPEDIT_ERROR;
        }
    }

    return TCPEDIT_OK;
}

/**
 * same as tcpedit_set_encoder_plugin_byid() except we take the DLT_<name>
 * as a string to select it
 */
int
tcpedit_set_encoder_dltplugin_byname(tcpedit_t *tcpedit, const char *name)
{
    tcpeditdlt_plugin_t *plugin;
    tcpeditdlt_t *ctx;

    assert(tcpedit);

    ctx = tcpedit->dlt_ctx;
    assert(ctx);

    if (ctx->encoder) {
        tcpedit_seterr(tcpedit, "You have already selected a DLT encoder: %s", ctx->encoder->name);
        return TCPEDIT_ERROR;
    }

    plugin = tcpedit_dlt_getplugin_byname(ctx, name);
    if (plugin == NULL) {
        tcpedit_seterr(tcpedit, "No output DLT plugin available for: %s", name);
        return TCPEDIT_ERROR;
    }

    ctx->encoder = plugin;

    /* init the encoder plugin if it's not the decoder plugin too */
    if (ctx->encoder->dlt != ctx->decoder->dlt) {
        if (ctx->encoder->plugin_init(ctx) != TCPEDIT_OK) {
            /* plugin should generate the error */
            return TCPEDIT_ERROR;
        }
    }

    return TCPEDIT_OK;
}


/**
 * Set whether we should edit broadcast & multicast IP addresses
 */
int
tcpedit_set_skip_broadcast(tcpedit_t *tcpedit, bool value)
{
    assert(tcpedit);
    tcpedit->skip_broadcast = value;
    return TCPEDIT_OK;
}

/**
 * \brief force fixing L3 & L4 data by padding or truncating packets
 */
int
tcpedit_set_fixlen(tcpedit_t *tcpedit, tcpedit_fixlen value)
{
    assert(tcpedit);
    tcpedit->fixlen = value;
    return TCPEDIT_OK;
}

/**
 * \brief should we always recalculate L3 & L4 checksums?
 */
int
tcpedit_set_fixcsum(tcpedit_t *tcpedit, bool value)
{
    assert(tcpedit);
    tcpedit->fixcsum = value;
    return TCPEDIT_OK;
}

/**
 * \brief should we remove the EFCS from the frame?
 */
int 
tcpedit_set_efcs(tcpedit_t *tcpedit, bool value)
{
    assert(tcpedit);
    tcpedit->efcs = value;
    return TCPEDIT_OK;
}

/**
 * \brief set the IPv4 TTL mode 
 */
int
tcpedit_set_ttl_mode(tcpedit_t *tcpedit, tcpedit_ttl_mode value)
{
    assert(tcpedit);
    tcpedit->ttl_mode = value;
    return TCPEDIT_OK;
}

/**
 * \brief set the IPv4 ttl value
 */
int
tcpedit_set_ttl_value(tcpedit_t *tcpedit, uint8_t value)
{
    assert(tcpedit);
    tcpedit->ttl_value = value;
    return TCPEDIT_OK;
}

/**
 * \brief set the IPv4 TOS/DiffServ/ECN byte value 
 */
int
tcpedit_set_tos(tcpedit_t *tcpedit, uint8_t value)
{
    assert(tcpedit);
    tcpedit->tos = value;
    return TCPEDIT_OK;
}

/**
 * \brief set the IPv6 Traffic Class byte value 
 */
int
tcpedit_set_tclass(tcpedit_t *tcpedit, uint8_t value)
{
    assert(tcpedit);
    tcpedit->tclass = value;
    return TCPEDIT_OK;
}

/**
 * \brief set the IPv6 Flow Label 20bit value 
 */
int
tcpedit_set_flowlabel(tcpedit_t *tcpedit, uint32_t value)
{
    assert(tcpedit);
    tcpedit->flowlabel = value;
    return TCPEDIT_OK;
}

/**
 * Set the IPv4 IP address randomization seed
 */
int
tcpedit_set_seed(tcpedit_t *tcpedit)
{
    assert(tcpedit);

    tcpedit->rewrite_ip = true;
    tcpedit->seed = 1;
    tcpedit->seed = tcpr_random(&tcpedit->seed);

    return TCPEDIT_OK;
}

/**
 * Set the MTU of the frames
 */
int 
tcpedit_set_mtu(tcpedit_t *tcpedit, int value)
{
    assert(tcpedit);
    tcpedit->mtu = value;
    return TCPEDIT_OK;
}

/**
 * Enable truncating packets to the MTU length
 */
int tcpedit_set_mtu_truncate(tcpedit_t *tcpedit, bool value)
{
    assert(tcpedit);
    tcpedit->mtu_truncate = value;
    return TCPEDIT_OK;
}

/**
 * Set the maxpacket- currently not supported
 */
int
tcpedit_set_maxpacket(tcpedit_t *tcpedit, int value)
{
    assert(tcpedit);
    tcpedit->maxpacket = value;
    return TCPEDIT_OK;
}


/**
 * \brief Set the server to client (primary) CIDR map (Pseudo NAT)
 *
 * Set the server to client (primary) CIDR map using the given string
 * which is in the format of:
 * <match cidr>:<target cidr>,...
 * 192.168.0.0/16:10.77.0.0/16,172.16.0.0/12:10.1.0.0/24
 */
int
tcpedit_set_cidrmap_s2c(tcpedit_t *tcpedit, char *value)
{
    assert(tcpedit);

    tcpedit->rewrite_ip = true;    
    if (! parse_cidr_map(&tcpedit->cidrmap1, value)) {
        tcpedit_seterr(tcpedit, "Unable to parse: %s", value);
        return TCPEDIT_ERROR;
    }
    return TCPEDIT_OK;
}

/**
 * \brief Set the client to server (secondary) CIDR map (Pseudo NAT)
 *
 * Set the client to server (secondary) CIDR map using the given string
 * which is in the format of:
 * <match cidr>:<target cidr>,...
 * 192.168.0.0/16:10.77.0.0/16,172.16.0.0/12:10.1.0.0/24
 */
int
tcpedit_set_cidrmap_c2s(tcpedit_t *tcpedit, char *value)
{
    assert(tcpedit);

    tcpedit->rewrite_ip = true;
    if (! parse_cidr_map(&tcpedit->cidrmap2, value)) {
        tcpedit_seterr(tcpedit, "Unable to parse: %s", value);
        return TCPEDIT_ERROR;
    }
    return TCPEDIT_OK;
}

/**
 * Rewrite the Source IP of any packet 
 */
int
tcpedit_set_srcip_map(tcpedit_t *tcpedit, char *value)
{
    assert(tcpedit);

    tcpedit->rewrite_ip = true;
    if (! parse_cidr_map(&tcpedit->srcipmap, value)) {
        tcpedit_seterr(tcpedit, "Unable to parse source ip map: %s", value);
        return TCPEDIT_ERROR;
    }
    return TCPEDIT_OK;
}

/**
 * Rewrite the Destination IP of any packet 
 */
int
tcpedit_set_dstip_map(tcpedit_t *tcpedit, char *value)
{
    assert(tcpedit);

    tcpedit->rewrite_ip = true;

    if (! parse_cidr_map(&tcpedit->dstipmap, value)) {
        tcpedit_seterr(tcpedit, "Unable to parse destination ip map: %s", value);
        return TCPEDIT_ERROR;
    }
    return TCPEDIT_OK;
}

/**
 * Rewrite TCP/UDP ports using the following format:
 * <src>:<dst>,...
 */
int
tcpedit_set_port_map(tcpedit_t *tcpedit, char *value)
{
    assert(tcpedit);

    if (! parse_portmap(&tcpedit->portmap, value)) {
        tcpedit_seterr(tcpedit, 
                "Unable to parse portmap: %s", value);
        return TCPEDIT_ERROR;
    }
    return TCPEDIT_OK;
}
