#include <stdlib.h>
#include <string.h>
#ifdef RTE_LIBRTE_KNI
#endif
#include <rte_table_hash_func.h>
#ifdef RTE_LIBRTE_KNI
#include "kni.h"
#endif
#include "link.h"
#include "mempool.h"
#include "pipeline.h"
#include "tap.h"
#include "tmgr.h"
#include "swq.h"
#include "cryptodev.h"
#ifndef PIPELINE_MSGQ_SIZE
#define PIPELINE_MSGQ_SIZE                                 64
#endif
#ifndef TABLE_LPM_NUMBER_TBL8
#define TABLE_LPM_NUMBER_TBL8                              256
#endif
static struct pipeline_list pipeline_list;
int
pipeline_init(void)
{
    TAILQ_INIT(&pipeline_list);
    return 0;
}
struct pipeline *
pipeline_find(const char *name)
{
    struct pipeline *pipeline;
    if (name == NULL)
        return NULL;
    TAILQ_FOREACH(pipeline, &pipeline_list, node)
        if (strcmp(name, pipeline->name) == 0)
            return pipeline;
    return NULL;
}
struct pipeline *
{
    char msgq_name[NAME_MAX];
    struct pipeline *pipeline;
    struct rte_pipeline *p;
    
    if ((name == NULL) ||
        pipeline_find(name) ||
        (params == NULL) ||
        (params->timer_period_ms == 0))
        return NULL;
    
    snprintf(msgq_name, sizeof(msgq_name), "%s-MSGQ-REQ", name);
        PIPELINE_MSGQ_SIZE,
        params->cpu_id,
    if (msgq_req == NULL)
        return NULL;
    snprintf(msgq_name, sizeof(msgq_name), "%s-MSGQ-RSP", name);
        PIPELINE_MSGQ_SIZE,
        params->cpu_id,
    if (msgq_rsp == NULL) {
        return NULL;
    }
    pp.name = name;
    pp.socket_id = (int) params->cpu_id;
    pp.offset_port_id = params->offset_port_id;
    if (p == NULL) {
        return NULL;
    }
    
    pipeline = calloc(1, sizeof(struct pipeline));
    if (pipeline == NULL) {
        return NULL;
    }
    
    strlcpy(pipeline->name, name, sizeof(pipeline->name));
    pipeline->p = p;
    pipeline->n_ports_in = 0;
    pipeline->n_ports_out = 0;
    pipeline->n_tables = 0;
    pipeline->msgq_req = msgq_req;
    pipeline->msgq_rsp = msgq_rsp;
    pipeline->timer_period_ms = params->timer_period_ms;
    pipeline->enabled = 0;
    pipeline->cpu_id = params->cpu_id;
    
    TAILQ_INSERT_TAIL(&pipeline_list, pipeline, node);
    return pipeline;
}
int
pipeline_port_in_create(const char *pipeline_name,
    struct port_in_params *params,
    int enabled)
{
    union {
#ifdef RTE_LIBRTE_KNI
#endif
    } pp;
    struct pipeline *pipeline;
    struct port_in *port_in;
    struct port_in_action_profile *ap;
    struct rte_port_in_action *action;
    uint32_t port_id;
    int status;
    memset(&p, 0, sizeof(p));
    memset(&pp, 0, sizeof(pp));
    
    if ((pipeline_name == NULL) ||
        (params == NULL) ||
        (params->burst_size == 0) ||
        return -1;
    pipeline = pipeline_find(pipeline_name);
    if (pipeline == NULL)
        return -1;
    ap = NULL;
    if (params->action_profile_name) {
        ap = port_in_action_profile_find(params->action_profile_name);
        if (ap == NULL)
            return -1;
    }
    switch (params->type) {
    case PORT_IN_RXQ:
    {
        struct link *link;
        link = link_find(params->dev_name);
        if (link == NULL)
            return -1;
        if (params->rxq.queue_id >= link->n_rxq)
            return -1;
        pp.ethdev.port_id = link->port_id;
        pp.ethdev.queue_id = params->rxq.queue_id;
        p.arg_create = &pp.ethdev;
        break;
    }
    case PORT_IN_SWQ:
    {
        struct swq *swq;
        swq = swq_find(params->dev_name);
        if (swq == NULL)
            return -1;
        pp.ring.ring = swq->r;
        p.arg_create = &pp.ring;
        break;
    }
    case PORT_IN_TMGR:
    {
        struct tmgr_port *tmgr_port;
        tmgr_port = tmgr_port_find(params->dev_name);
        if (tmgr_port == NULL)
            return -1;
        pp.sched.sched = tmgr_port->s;
        p.arg_create = &pp.sched;
        break;
    }
    case PORT_IN_TAP:
    {
        struct tap *tap;
        struct mempool *mempool;
        tap = tap_find(params->dev_name);
        mempool = mempool_find(params->tap.mempool_name);
        if ((tap == NULL) || (mempool == NULL))
            return -1;
        pp.fd.fd = tap->fd;
        pp.fd.mempool = mempool->m;
        pp.fd.mtu = params->tap.mtu;
        p.arg_create = &pp.fd;
        break;
    }
#ifdef RTE_LIBRTE_KNI
    case PORT_IN_KNI:
    {
        struct kni *kni;
        kni = kni_find(params->dev_name);
        if (kni == NULL)
            return -1;
        pp.kni.kni = kni->k;
        p.arg_create = &pp.kni;
        break;
    }
#endif
    case PORT_IN_SOURCE:
    {
        struct mempool *mempool;
        mempool = mempool_find(params->source.mempool_name);
        if (mempool == NULL)
            return -1;
        pp.source.mempool = mempool->m;
        pp.source.file_name = params->source.file_name;
        pp.source.n_bytes_per_pkt = params->source.n_bytes_per_pkt;
        p.arg_create = &pp.source;
        break;
    }
    case PORT_IN_CRYPTODEV:
    {
        struct cryptodev *cryptodev;
        cryptodev = cryptodev_find(params->dev_name);
        if (cryptodev == NULL)
            return -1;
        if (params->rxq.queue_id > cryptodev->n_queues - 1)
            return -1;
        pp.sym_crypto.cryptodev_id = cryptodev->dev_id;
        pp.sym_crypto.queue_id = params->cryptodev.queue_id;
        pp.sym_crypto.f_callback = params->cryptodev.f_callback;
        pp.sym_crypto.arg_callback = params->cryptodev.arg_callback;
        p.arg_create = &pp.sym_crypto;
        break;
    }
    default:
        return -1;
    }
    p.burst_size = params->burst_size;
    
    action = NULL;
    p.f_action = NULL;
    p.arg_ah = NULL;
    if (ap) {
            pipeline->cpu_id);
        if (action == NULL)
            return -1;
            action,
            &p);
        if (status) {
            return -1;
        }
    }
        &p,
        &port_id);
    if (status) {
        return -1;
    }
    if (enabled)
    
    port_in = &pipeline->port_in[pipeline->n_ports_in];
    memcpy(&port_in->params, params, sizeof(*params));
    port_in->ap = ap;
    port_in->a = action;
    pipeline->n_ports_in++;
    return 0;
}
int
pipeline_port_in_connect_to_table(const char *pipeline_name,
    uint32_t port_id,
    uint32_t table_id)
{
    struct pipeline *pipeline;
    int status;
    
    if (pipeline_name == NULL)
        return -1;
    pipeline = pipeline_find(pipeline_name);
    if ((pipeline == NULL) ||
        (port_id >= pipeline->n_ports_in) ||
        (table_id >= pipeline->n_tables))
        return -1;
    
        port_id,
        table_id);
    return status;
}
int
pipeline_port_out_create(const char *pipeline_name,
    struct port_out_params *params)
{
    union {
#ifdef RTE_LIBRTE_KNI
#endif
    } pp;
    union {
#ifdef RTE_LIBRTE_KNI
#endif
    } pp_nodrop;
    struct pipeline *pipeline;
    uint32_t port_id;
    int status;
    memset(&p, 0, sizeof(p));
    memset(&pp, 0, sizeof(pp));
    memset(&pp_nodrop, 0, sizeof(pp_nodrop));
    
    if ((pipeline_name == NULL) ||
        (params == NULL) ||
        (params->burst_size == 0) ||
        return -1;
    pipeline = pipeline_find(pipeline_name);
    if (pipeline == NULL)
        return -1;
    switch (params->type) {
    case PORT_OUT_TXQ:
    {
        struct link *link;
        link = link_find(params->dev_name);
        if (link == NULL)
            return -1;
        if (params->txq.queue_id >= link->n_txq)
            return -1;
        pp.ethdev.port_id = link->port_id;
        pp.ethdev.queue_id = params->txq.queue_id;
        pp.ethdev.tx_burst_sz = params->burst_size;
        pp_nodrop.ethdev.port_id = link->port_id;
        pp_nodrop.ethdev.queue_id = params->txq.queue_id;
        pp_nodrop.ethdev.tx_burst_sz = params->burst_size;
        pp_nodrop.ethdev.n_retries = params->n_retries;
        if (params->retry == 0) {
            p.arg_create = &pp.ethdev;
        } else {
            p.arg_create = &pp_nodrop.ethdev;
        }
        break;
    }
    case PORT_OUT_SWQ:
    {
        struct swq *swq;
        swq = swq_find(params->dev_name);
        if (swq == NULL)
            return -1;
        pp.ring.ring = swq->r;
        pp.ring.tx_burst_sz = params->burst_size;
        pp_nodrop.ring.ring = swq->r;
        pp_nodrop.ring.tx_burst_sz = params->burst_size;
        pp_nodrop.ring.n_retries = params->n_retries;
        if (params->retry == 0) {
            p.arg_create = &pp.ring;
        } else {
            p.arg_create = &pp_nodrop.ring;
        }
        break;
    }
    case PORT_OUT_TMGR:
    {
        struct tmgr_port *tmgr_port;
        tmgr_port = tmgr_port_find(params->dev_name);
        if (tmgr_port == NULL)
            return -1;
        pp.sched.sched = tmgr_port->s;
        pp.sched.tx_burst_sz = params->burst_size;
        p.arg_create = &pp.sched;
        break;
    }
    case PORT_OUT_TAP:
    {
        struct tap *tap;
        tap = tap_find(params->dev_name);
        if (tap == NULL)
            return -1;
        pp.fd.fd = tap->fd;
        pp.fd.tx_burst_sz = params->burst_size;
        pp_nodrop.fd.fd = tap->fd;
        pp_nodrop.fd.tx_burst_sz = params->burst_size;
        pp_nodrop.fd.n_retries = params->n_retries;
        if (params->retry == 0) {
            p.arg_create = &pp.fd;
        } else {
            p.arg_create = &pp_nodrop.fd;
        }
        break;
    }
#ifdef RTE_LIBRTE_KNI
    case PORT_OUT_KNI:
    {
        struct kni *kni;
        kni = kni_find(params->dev_name);
        if (kni == NULL)
            return -1;
        pp.kni.kni = kni->k;
        pp.kni.tx_burst_sz = params->burst_size;
        pp_nodrop.kni.kni = kni->k;
        pp_nodrop.kni.tx_burst_sz = params->burst_size;
        pp_nodrop.kni.n_retries = params->n_retries;
        if (params->retry == 0) {
            p.arg_create = &pp.kni;
        } else {
            p.arg_create = &pp_nodrop.kni;
        }
        break;
    }
#endif
    case PORT_OUT_SINK:
    {
        pp.sink.file_name = params->sink.file_name;
        pp.sink.max_n_pkts = params->sink.max_n_pkts;
        p.arg_create = &pp.sink;
        break;
    }
    case PORT_OUT_CRYPTODEV:
    {
        struct cryptodev *cryptodev;
        cryptodev = cryptodev_find(params->dev_name);
        if (cryptodev == NULL)
            return -1;
        if (params->cryptodev.queue_id >= cryptodev->n_queues)
            return -1;
        pp.sym_crypto.cryptodev_id = cryptodev->dev_id;
        pp.sym_crypto.queue_id = params->cryptodev.queue_id;
        pp.sym_crypto.tx_burst_sz = params->burst_size;
        pp.sym_crypto.crypto_op_offset = params->cryptodev.op_offset;
        pp_nodrop.sym_crypto.cryptodev_id = cryptodev->dev_id;
        pp_nodrop.sym_crypto.queue_id = params->cryptodev.queue_id;
        pp_nodrop.sym_crypto.tx_burst_sz = params->burst_size;
        pp_nodrop.sym_crypto.n_retries = params->retry;
        pp_nodrop.sym_crypto.crypto_op_offset =
                params->cryptodev.op_offset;
        if (params->retry == 0) {
            p.arg_create = &pp.sym_crypto;
        } else {
            p.arg_create = &pp_nodrop.sym_crypto;
        }
        break;
    }
    default:
        return -1;
    }
    p.f_action = NULL;
    p.arg_ah = NULL;
    
        &p,
        &port_id);
    if (status)
        return -1;
    
    pipeline->n_ports_out++;
    return 0;
}
    
    [0] = {
        .
type = RTE_ACL_FIELD_TYPE_BITMASK,
        .size = sizeof(uint8_t),
    },
    
    [1] = {
        .type = RTE_ACL_FIELD_TYPE_MASK,
        .size = sizeof(uint32_t),
    },
    
    [2] = {
        .type = RTE_ACL_FIELD_TYPE_MASK,
        .size = sizeof(uint32_t),
    },
    
    [3] = {
        .type = RTE_ACL_FIELD_TYPE_RANGE,
        .size = sizeof(uint16_t),
    },
    
    [4] = {
        .type = RTE_ACL_FIELD_TYPE_RANGE,
        .size = sizeof(uint16_t),
    },
};
    
    [0] = {
        .
type = RTE_ACL_FIELD_TYPE_BITMASK,
        .size = sizeof(uint8_t),
    },
    
    [1] = {
        .type = RTE_ACL_FIELD_TYPE_MASK,
        .size = sizeof(uint32_t),
    },
    [2] = {
        .type = RTE_ACL_FIELD_TYPE_MASK,
        .size = sizeof(uint32_t),
    },
    [3] = {
        .type = RTE_ACL_FIELD_TYPE_MASK,
        .size = sizeof(uint32_t),
    },
    [4] = {
        .type = RTE_ACL_FIELD_TYPE_MASK,
        .size = sizeof(uint32_t),
    },
    
    [5] = {
        .type = RTE_ACL_FIELD_TYPE_MASK,
        .size = sizeof(uint32_t),
    },
    [6] = {
        .type = RTE_ACL_FIELD_TYPE_MASK,
        .size = sizeof(uint32_t),
    },
    [7] = {
        .type = RTE_ACL_FIELD_TYPE_MASK,
        .size = sizeof(uint32_t),
    },
    [8] = {
        .type = RTE_ACL_FIELD_TYPE_MASK,
        .size = sizeof(uint32_t),
    },
    
    [9] = {
        .type = RTE_ACL_FIELD_TYPE_RANGE,
        .size = sizeof(uint16_t),
    },
    
    [10] = {
        .type = RTE_ACL_FIELD_TYPE_RANGE,
        .size = sizeof(uint16_t),
    },
};
int
pipeline_table_create(const char *pipeline_name,
    struct table_params *params)
{
    char name[NAME_MAX];
    union {
    } pp;
    struct pipeline *pipeline;
    struct table *table;
    struct table_action_profile *ap;
    struct rte_table_action *action;
    uint32_t table_id;
    int status;
    memset(&p, 0, sizeof(p));
    memset(&pp, 0, sizeof(pp));
    
    if ((pipeline_name == NULL) ||
        (params == NULL))
        return -1;
    pipeline = pipeline_find(pipeline_name);
    if ((pipeline == NULL) ||
        return -1;
    ap = NULL;
    if (params->action_profile_name) {
        ap = table_action_profile_find(params->action_profile_name);
        if (ap == NULL)
            return -1;
    }
    snprintf(name, NAME_MAX, "%s_table%u",
        pipeline_name, pipeline->n_tables);
    switch (params->match_type) {
    case TABLE_ACL:
    {
        uint32_t ip_header_offset = params->match.acl.ip_header_offset -
            (
sizeof(
struct rte_mbuf) + RTE_PKTMBUF_HEADROOM);
        uint32_t i;
        if (params->match.acl.n_rules == 0)
            return -1;
        pp.acl.name = name;
        pp.acl.n_rules = params->match.acl.n_rules;
        if (params->match.acl.ip_version) {
            memcpy(&pp.acl.field_format,
                &table_acl_field_format_ipv4,
                sizeof(table_acl_field_format_ipv4));
            pp.acl.n_rule_fields =
                RTE_DIM(table_acl_field_format_ipv4);
 
        } else {
            memcpy(&pp.acl.field_format,
                &table_acl_field_format_ipv6,
                sizeof(table_acl_field_format_ipv6));
            pp.acl.n_rule_fields =
                RTE_DIM(table_acl_field_format_ipv6);
 
        }
        for (i = 0; i < pp.acl.n_rule_fields; i++)
            pp.acl.field_format[i].offset += ip_header_offset;
        p.arg_create = &pp.acl;
        break;
    }
    case TABLE_ARRAY:
    {
        if (params->match.array.n_keys == 0)
            return -1;
        pp.array.n_entries = params->match.array.n_keys;
        pp.array.offset = params->match.array.key_offset;
        p.arg_create = &pp.array;
        break;
    }
    case TABLE_HASH:
    {
        if (params->match.hash.n_keys == 0)
            return -1;
        switch (params->match.hash.key_size) {
        case  8:
            f_hash = rte_table_hash_crc_key8;
            break;
        case 16:
            f_hash = rte_table_hash_crc_key16;
            break;
        case 24:
            f_hash = rte_table_hash_crc_key24;
            break;
        case 32:
            f_hash = rte_table_hash_crc_key32;
            break;
        case 40:
            f_hash = rte_table_hash_crc_key40;
            break;
        case 48:
            f_hash = rte_table_hash_crc_key48;
            break;
        case 56:
            f_hash = rte_table_hash_crc_key56;
            break;
        case 64:
            f_hash = rte_table_hash_crc_key64;
            break;
        default:
            return -1;
        }
        pp.hash.name = name;
        pp.hash.key_size = params->match.hash.key_size;
        pp.hash.key_offset = params->match.hash.key_offset;
        pp.hash.key_mask = params->match.hash.key_mask;
        pp.hash.n_keys = params->match.hash.n_keys;
        pp.hash.n_buckets = params->match.hash.n_buckets;
        pp.hash.f_hash = f_hash;
        pp.hash.seed = 0;
        if (params->match.hash.extendable_bucket)
            switch (params->match.hash.key_size) {
            case  8:
                ops = &rte_table_hash_key8_ext_ops;
                break;
            case 16:
                ops = &rte_table_hash_key16_ext_ops;
                break;
            default:
            }
        else
            switch (params->match.hash.key_size) {
            case  8:
                ops = &rte_table_hash_key8_lru_ops;
                break;
            case 16:
                ops = &rte_table_hash_key16_lru_ops;
                break;
            default:
            }
        p.ops = ops;
        p.arg_create = &pp.hash;
        break;
    }
    case TABLE_LPM:
    {
        if (params->match.lpm.n_rules == 0)
            return -1;
        switch (params->match.lpm.key_size) {
        case 4:
        {
            pp.lpm.name = name;
            pp.lpm.n_rules = params->match.lpm.n_rules;
            pp.lpm.number_tbl8s = TABLE_LPM_NUMBER_TBL8;
            pp.lpm.flags = 0;
            pp.lpm.entry_unique_size = p.action_data_size +
            pp.lpm.offset = params->match.lpm.key_offset;
            p.arg_create = &pp.lpm;
            break;
        }
        case 16:
        {
            pp.lpm_ipv6.name = name;
            pp.lpm_ipv6.n_rules = params->match.lpm.n_rules;
            pp.lpm_ipv6.number_tbl8s = TABLE_LPM_NUMBER_TBL8;
            pp.lpm_ipv6.entry_unique_size = p.action_data_size +
            pp.lpm_ipv6.offset = params->match.lpm.key_offset;
            p.arg_create = &pp.lpm_ipv6;
            break;
        }
        default:
            return -1;
        }
        break;
    }
    case TABLE_STUB:
    {
        p.arg_create = NULL;
        break;
    }
    default:
        return -1;
    }
    
    action = NULL;
    p.f_action_hit = NULL;
    p.f_action_miss = NULL;
    p.arg_ah = NULL;
    if (ap) {
            pipeline->cpu_id);
        if (action == NULL)
            return -1;
            action,
            &p);
        if (status ||
            ((p.action_data_size +
            TABLE_RULE_ACTION_SIZE_MAX)) {
            return -1;
        }
    }
    if (params->match_type == TABLE_LPM) {
        if (params->match.lpm.key_size == 4)
            pp.lpm.entry_unique_size = p.action_data_size +
        if (params->match.lpm.key_size == 16)
            pp.lpm_ipv6.entry_unique_size = p.action_data_size +
    }
        &p,
        &table_id);
    if (status) {
        return -1;
    }
    
    table = &pipeline->table[pipeline->n_tables];
    memcpy(&table->params, params, sizeof(*params));
    table->ap = ap;
    TAILQ_INIT(&table->rules);
    table->rule_default = NULL;
    pipeline->n_tables++;
    return 0;
}
struct table_rule *
table_rule_find(struct table *table,
    struct table_rule_match *match)
{
    struct table_rule *rule;
    TAILQ_FOREACH(rule, &table->rules, node)
        if (memcmp(&rule->match, match, sizeof(*match)) == 0)
            return rule;
    return NULL;
}
void
table_rule_add(struct table *table,
    struct table_rule *new_rule)
{
    struct table_rule *existing_rule;
    existing_rule = table_rule_find(table, &new_rule->match);
    if (existing_rule == NULL)
        TAILQ_INSERT_TAIL(&table->rules, new_rule, node);
    else {
        TAILQ_INSERT_AFTER(&table->rules, existing_rule, new_rule, node);
        TAILQ_REMOVE(&table->rules, existing_rule, node);
        free(existing_rule);
    }
}
void
table_rule_add_bulk(struct table *table,
    struct table_rule_list *list,
    uint32_t n_rules)
{
    uint32_t i;
    for (i = 0; i < n_rules; i++) {
        struct table_rule *existing_rule, *new_rule;
        new_rule = TAILQ_FIRST(list);
        if (new_rule == NULL)
            break;
        TAILQ_REMOVE(list, new_rule, node);
        existing_rule = table_rule_find(table, &new_rule->match);
        if (existing_rule == NULL)
            TAILQ_INSERT_TAIL(&table->rules, new_rule, node);
        else {
            TAILQ_INSERT_AFTER(&table->rules, existing_rule, new_rule, node);
            TAILQ_REMOVE(&table->rules, existing_rule, node);
            free(existing_rule);
        }
    }
}
void
table_rule_delete(struct table *table,
    struct table_rule_match *match)
{
    struct table_rule *rule;
    rule = table_rule_find(table, match);
    if (rule == NULL)
        return;
    TAILQ_REMOVE(&table->rules, rule, node);
    free(rule);
}
void
table_rule_default_add(struct table *table,
    struct table_rule *rule)
{
    free(table->rule_default);
    table->rule_default = rule;
}
void
table_rule_default_delete(struct table *table)
{
    free(table->rule_default);
    table->rule_default = NULL;
}