#include <stdint.h>
#include <inttypes.h>
#include <getopt.h>
#define RX_RING_SIZE 1024
#define TX_RING_SIZE 1024
#define NUM_MBUFS 8191
#define MBUF_CACHE_SIZE 250
#define BURST_SIZE 32
#define MAX_NUM_CLASSIFY 30
#define FLOW_CLASSIFY_MAX_RULE_NUM 91
#define FLOW_CLASSIFY_MAX_PRIORITY 8
#define FLOW_CLASSIFIER_NAME_SIZE 64
#define COMMENT_LEAD_CHAR   ('#')
#define OPTION_RULE_IPV4    "rule_ipv4"
#define RTE_LOGTYPE_FLOW_CLASSIFY   RTE_LOGTYPE_USER3
#define flow_classify_log(format, ...) \
        RTE_LOG(ERR, FLOW_CLASSIFY, format, ##__VA_ARGS__)
#define uint32_t_to_char(ip, a, b, c, d) do {\
        *a = (unsigned char)(ip >> 24 & 0xff);\
        *b = (unsigned char)(ip >> 16 & 0xff);\
        *c = (unsigned char)(ip >> 8 & 0xff);\
        *d = (unsigned char)(ip & 0xff);\
    } while (0)
enum {
    CB_FLD_SRC_ADDR,
    CB_FLD_DST_ADDR,
    CB_FLD_SRC_PORT,
    CB_FLD_SRC_PORT_DLM,
    CB_FLD_SRC_PORT_MASK,
    CB_FLD_DST_PORT,
    CB_FLD_DST_PORT_DLM,
    CB_FLD_DST_PORT_MASK,
    CB_FLD_PROTO,
    CB_FLD_PRIORITY,
    CB_FLD_NUM,
};
static struct{
    const char *rule_ipv4_name;
} parm_config;
const char cb_port_delim[] = ":";
    },
};
struct flow_classifier {
    struct rte_flow_classifier *cls;
};
struct flow_classifier_acl {
    struct flow_classifier cls;
enum {
    PROTO_FIELD_IPV4,
    SRC_FIELD_IPV4,
    DST_FIELD_IPV4,
    SRCP_FIELD_IPV4,
    DSTP_FIELD_IPV4,
    NUM_FIELDS_IPV4
};
enum {
    PROTO_INPUT_IPV4,
    SRC_INPUT_IPV4,
    DST_INPUT_IPV4,
    SRCP_DESTP_INPUT_IPV4
};
    
    {
        .
type = RTE_ACL_FIELD_TYPE_BITMASK,
        .size = sizeof(uint8_t),
    },
    
    {
        
        .type = RTE_ACL_FIELD_TYPE_BITMASK,
        .size = sizeof(uint32_t),
    },
    
    {
        
        .type = RTE_ACL_FIELD_TYPE_BITMASK,
        .size = sizeof(uint32_t),
    },
    
    {
        
        .type = RTE_ACL_FIELD_TYPE_BITMASK,
        .size = sizeof(uint16_t),
    },
    {
        
        .type = RTE_ACL_FIELD_TYPE_BITMASK,
        .size = sizeof(uint16_t),
        .field_index = DSTP_FIELD_IPV4,
        .input_index = SRCP_DESTP_INPUT_IPV4,
    },
};
static int num_classify_rules;
static struct rte_flow_classify_rule *rules[MAX_NUM_CLASSIFY];
static struct rte_flow_classify_ipv4_5tuple_stats ntuple_stats;
        .
stats = (
void **)&ntuple_stats
};
    0, 0, 0 };
    0, 0, 0 };
    .hits_set = 1,
    .bytes_set = 1,
    .hits = 0,
    .bytes = 0,
};
    &count};
static struct rte_flow_action actions[2];
static inline int
{
    const uint16_t rx_rings = 1, tx_rings = 1;
    int retval;
    uint16_t q;
        return -1;
    
    if (retval != 0)
        return retval;
    
    for (q = 0; q < rx_rings; q++) {
        if (retval < 0)
            return retval;
    }
    txconf = dev_info.default_txconf;
    
    for (q = 0; q < tx_rings; q++) {
        if (retval < 0)
            return retval;
    }
    
    if (retval < 0)
        return retval;
    
    printf("Port %u MAC: %02" PRIx8 " %02" PRIx8 " %02" PRIx8
               " %02" PRIx8 " %02" PRIx8 " %02" PRIx8 "\n",
            port,
            addr.addr_bytes[0], addr.addr_bytes[1],
            addr.addr_bytes[2], addr.addr_bytes[3],
            addr.addr_bytes[4], addr.addr_bytes[5]);
    
    return 0;
}
static __attribute__((noreturn)) void
lcore_main(struct flow_classifier *cls_app)
{
    uint16_t port;
    int ret;
    int i = 0;
            rules[7]);
    if (ret)
        printf("table_entry_delete failed [7] %d\n\n", ret);
    else
        printf("table_entry_delete succeeded [7]\n\n");
    
            printf("\n\n");
            printf("WARNING: port %u is on remote NUMA node\n",
                   port);
            printf("to polling thread.\n");
            printf("Performance will not be optimal.\n");
        }
    printf("[Ctrl+C to quit]\n");
    
    for (;;) {
        
            
                    bufs, BURST_SIZE);
                continue;
            for (i = 0; i < MAX_NUM_CLASSIFY; i++) {
                if (rules[i]) {
                        cls_app->cls,
                        bufs, nb_rx, rules[i],
                        &classify_stats);
                    if (ret)
                        printf(
                            "rule [%d] query failed ret [%d]\n\n",
                            i, ret);
                    else {
                        printf(
                        "rule[%d] count=%"PRIu64"\n",
                        i, ntuple_stats.counter1);
                        printf("proto = %d\n",
                        ntuple_stats.ipv4_5tuple.proto);
                    }
                }
            }
            
                    bufs, nb_rx);
            
                uint16_t buf;
                for (buf = nb_tx; buf < nb_rx; buf++)
            }
        }
    }
}
static int
get_cb_field(char **in, uint32_t *fd, int base, unsigned long lim,
        char dlm)
{
    unsigned long val;
    char *end;
    errno = 0;
    val = strtoul(*in, &end, base);
    if (errno != 0 || end[0] != dlm || val > lim)
        return -EINVAL;
    *fd = (uint32_t)val;
    *in = end + 1;
    return 0;
}
static int
parse_ipv4_net(char *in, uint32_t *addr, uint32_t *mask_len)
{
    uint32_t a, b, c, d, m;
    if (get_cb_field(&in, &a, 0, UINT8_MAX, '.'))
        return -EINVAL;
    if (get_cb_field(&in, &b, 0, UINT8_MAX, '.'))
        return -EINVAL;
    if (get_cb_field(&in, &c, 0, UINT8_MAX, '.'))
        return -EINVAL;
    if (get_cb_field(&in, &d, 0, UINT8_MAX, '/'))
        return -EINVAL;
    if (get_cb_field(&in, &m, 0, sizeof(uint32_t) * CHAR_BIT, 0))
        return -EINVAL;
    addr[0] = 
IPv4(a, b, c, d);
    mask_len[0] = m;
    return 0;
}
static int
{
    int i, ret;
    char *s, *sp, *in[CB_FLD_NUM];
    static const char *dlm = " \t\n";
    int dim = CB_FLD_NUM;
    uint32_t temp;
    s = str;
    for (i = 0; i != dim; i++, s = NULL) {
        in[i] = strtok_r(s, dlm, &sp);
        if (in[i] == NULL)
            return -EINVAL;
    }
    ret = parse_ipv4_net(in[CB_FLD_SRC_ADDR],
    if (ret != 0) {
        flow_classify_log("failed to read source address/mask: %s\n",
            in[CB_FLD_SRC_ADDR]);
        return ret;
    }
    ret = parse_ipv4_net(in[CB_FLD_DST_ADDR],
    if (ret != 0) {
        flow_classify_log("failed to read source address/mask: %s\n",
            in[CB_FLD_DST_ADDR]);
        return ret;
    }
    if (get_cb_field(&in[CB_FLD_SRC_PORT], &temp, 0, UINT16_MAX, 0))
        return -EINVAL;
    ntuple_filter->
src_port = (uint16_t)temp;
    if (strncmp(in[CB_FLD_SRC_PORT_DLM], cb_port_delim,
            sizeof(cb_port_delim)) != 0)
        return -EINVAL;
    if (get_cb_field(&in[CB_FLD_SRC_PORT_MASK], &temp, 0, UINT16_MAX, 0))
        return -EINVAL;
    if (get_cb_field(&in[CB_FLD_DST_PORT], &temp, 0, UINT16_MAX, 0))
        return -EINVAL;
    ntuple_filter->
dst_port = (uint16_t)temp;
    if (strncmp(in[CB_FLD_DST_PORT_DLM], cb_port_delim,
            sizeof(cb_port_delim)) != 0)
        return -EINVAL;
    if (get_cb_field(&in[CB_FLD_DST_PORT_MASK], &temp, 0, UINT16_MAX, 0))
        return -EINVAL;
    if (get_cb_field(&in[CB_FLD_PROTO], &temp, 0, UINT8_MAX, '/'))
        return -EINVAL;
    ntuple_filter->
proto = (uint8_t)temp;
    if (get_cb_field(&in[CB_FLD_PROTO], &temp, 0, UINT8_MAX, 0))
        return -EINVAL;
    if (get_cb_field(&in[CB_FLD_PRIORITY], &temp, 0, UINT16_MAX, 0))
        return -EINVAL;
    ntuple_filter->
priority = (uint16_t)temp;
    if (ntuple_filter->
priority > FLOW_CLASSIFY_MAX_PRIORITY)
 
        ret = -EINVAL;
    return ret;
}
static inline int
is_bypass_line(char *buff)
{
    int i = 0;
    
    if (buff[0] == COMMENT_LEAD_CHAR)
        return 1;
    
    while (buff[i] != '\0') {
        if (!isspace(buff[i]))
            return 0;
        i++;
    }
    return 1;
}
static uint32_t
convert_depth_to_bitmask(uint32_t depth_val)
{
    uint32_t bitmask = 0;
    int i, j;
    for (i = depth_val, j = 0; i > 0; i--, j++)
        bitmask |= (1 << (31 - j));
    return bitmask;
}
static int
        struct flow_classifier *cls_app)
{
    int ret = -1;
    int key_found;
    struct rte_flow_classify_rule *rule;
    uint8_t ipv4_proto;
    if (num_classify_rules >= MAX_NUM_CLASSIFY) {
        printf(
            "\nINFO:  classify rule capacity %d reached\n",
            num_classify_rules);
        return ret;
    }
    
    memset(&ipv4_spec, 0, sizeof(ipv4_spec));
    ipv4_spec.hdr.next_proto_id = ntuple_filter->
proto;
    ipv4_spec.hdr.src_addr = ntuple_filter->
src_ip;
    ipv4_spec.hdr.dst_addr = ntuple_filter->
dst_ip;
    ipv4_proto = ipv4_spec.hdr.next_proto_id;
    memset(&ipv4_mask, 0, sizeof(ipv4_mask));
    ipv4_mask.hdr.next_proto_id = ntuple_filter->
proto_mask;
    ipv4_mask.hdr.src_addr =
        convert_depth_to_bitmask(ipv4_mask.hdr.src_addr);
    ipv4_mask.hdr.dst_addr =
        convert_depth_to_bitmask(ipv4_mask.hdr.dst_addr);
    switch (ipv4_proto) {
    case IPPROTO_UDP:
        ipv4_udp_item.spec = &ipv4_spec;
        ipv4_udp_item.mask = &ipv4_mask;
        ipv4_udp_item.last = NULL;
        udp_spec.hdr.src_port = ntuple_filter->
src_port;
        udp_spec.hdr.dst_port = ntuple_filter->
dst_port;
        udp_spec.hdr.dgram_len = 0;
        udp_spec.hdr.dgram_cksum = 0;
        udp_mask.hdr.dgram_len = 0;
        udp_mask.hdr.dgram_cksum = 0;
        udp_item.spec = &udp_spec;
        udp_item.mask = &udp_mask;
        udp_item.last = NULL;
        attr.priority = ntuple_filter->
priority;
        pattern_ipv4_5tuple[1] = ipv4_udp_item;
        pattern_ipv4_5tuple[2] = udp_item;
        break;
    case IPPROTO_TCP:
        ipv4_tcp_item.spec = &ipv4_spec;
        ipv4_tcp_item.mask = &ipv4_mask;
        ipv4_tcp_item.last = NULL;
        memset(&tcp_spec, 0, sizeof(tcp_spec));
        tcp_spec.hdr.src_port = ntuple_filter->
src_port;
        tcp_spec.hdr.dst_port = ntuple_filter->
dst_port;
        memset(&tcp_mask, 0, sizeof(tcp_mask));
        tcp_item.spec = &tcp_spec;
        tcp_item.mask = &tcp_mask;
        tcp_item.last = NULL;
        attr.priority = ntuple_filter->
priority;
        pattern_ipv4_5tuple[1] = ipv4_tcp_item;
        pattern_ipv4_5tuple[2] = tcp_item;
        break;
    case IPPROTO_SCTP:
        ipv4_sctp_item.spec = &ipv4_spec;
        ipv4_sctp_item.mask = &ipv4_mask;
        ipv4_sctp_item.last = NULL;
        sctp_spec.hdr.src_port = ntuple_filter->
src_port;
        sctp_spec.hdr.dst_port = ntuple_filter->
dst_port;
        sctp_spec.hdr.cksum = 0;
        sctp_spec.hdr.tag = 0;
        sctp_mask.hdr.cksum = 0;
        sctp_mask.hdr.tag = 0;
        sctp_item.spec = &sctp_spec;
        sctp_item.mask = &sctp_mask;
        sctp_item.last = NULL;
        attr.priority = ntuple_filter->
priority;
        pattern_ipv4_5tuple[1] = ipv4_sctp_item;
        pattern_ipv4_5tuple[2] = sctp_item;
        break;
    default:
        return ret;
    }
    attr.ingress = 1;
    pattern_ipv4_5tuple[0] = eth_item;
    pattern_ipv4_5tuple[3] = end_item;
    actions[0] = count_action;
    actions[1] = end_action;
    
            pattern_ipv4_5tuple, actions, &error);
    if (ret) {
        printf("table entry validate failed ipv4_proto = %u\n",
            ipv4_proto);
        return ret;
    }
            cls_app->cls, &attr, pattern_ipv4_5tuple,
            actions, &key_found, &error);
    if (rule == NULL) {
        printf("table entry add failed ipv4_proto = %u\n",
            ipv4_proto);
        ret = -1;
        return ret;
    }
    rules[num_classify_rules] = rule;
    num_classify_rules++;
    return 0;
}
static int
add_rules(const char *rule_path, struct flow_classifier *cls_app)
{
    FILE *fh;
    char buff[LINE_MAX];
    unsigned int i = 0;
    unsigned int total_num = 0;
    int ret;
    fh = fopen(rule_path, "rb");
    if (fh == NULL)
        rte_exit(EXIT_FAILURE, 
"%s: fopen %s failed\n", __func__,
 
            rule_path);
    ret = fseek(fh, 0, SEEK_SET);
    if (ret)
        rte_exit(EXIT_FAILURE, 
"%s: fseek %d failed\n", __func__,
 
            ret);
    i = 0;
    while (fgets(buff, LINE_MAX, fh) != NULL) {
        i++;
        if (is_bypass_line(buff))
            continue;
        if (total_num >= FLOW_CLASSIFY_MAX_RULE_NUM - 1) {
            printf("\nINFO: classify rule capacity %d reached\n",
                total_num);
            break;
        }
        if (parse_ipv4_5tuple_rule(buff, &ntuple_filter) != 0)
                "%s Line %u: parse rules error\n",
                rule_path, i);
        if (add_classify_rule(&ntuple_filter, cls_app) != 0)
            rte_exit(EXIT_FAILURE, 
"add rule error\n");
 
        total_num++;
    }
    fclose(fh);
    return 0;
}
static void
print_usage(const char *prgname)
{
    printf("%s usage:\n", prgname);
    printf("[EAL options] --  --"OPTION_RULE_IPV4"=FILE: ");
    printf("specify the ipv4 rules file.\n");
    printf("Each rule occupies one line in the file.\n");
}
static int
parse_args(int argc, char **argv)
{
    int opt, ret;
    char **argvopt;
    int option_index;
    char *prgname = argv[0];
    static struct option lgopts[] = {
        {OPTION_RULE_IPV4, 1, 0, 0},
        {NULL, 0, 0, 0}
    };
    argvopt = argv;
    while ((opt = getopt_long(argc, argvopt, "",
                lgopts, &option_index)) != EOF) {
        switch (opt) {
        
        case 0:
            if (!strncmp(lgopts[option_index].name,
                    OPTION_RULE_IPV4,
                    sizeof(OPTION_RULE_IPV4)))
                parm_config.rule_ipv4_name = optarg;
            break;
        default:
            print_usage(prgname);
            return -1;
        }
    }
    if (optind >= 0)
        argv[optind-1] = prgname;
    ret = optind-1;
    optind = 1; 
    return ret;
}
int
main(int argc, char *argv[])
{
    uint16_t nb_ports;
    uint16_t portid;
    int ret;
    struct flow_classifier *cls_app;
    uint32_t size;
    
    if (ret < 0)
        rte_exit(EXIT_FAILURE, 
"Error with EAL initialization\n");
 
    argc -= ret;
    argv += ret;
    
    ret = parse_args(argc, argv);
    if (ret < 0)
        rte_exit(EXIT_FAILURE, 
"Invalid flow_classify parameters\n");
 
    
    if (nb_ports < 2 || (nb_ports & 1))
        rte_exit(EXIT_FAILURE, 
"Error: number of ports must be even\n");
 
    
    if (mbuf_pool == NULL)
        rte_exit(EXIT_FAILURE, 
"Cannot create mbuf pool\n");
 
    
        if (port_init(portid, mbuf_pool) != 0)
            rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu8 "\n",
 
                    portid);
        printf("\nWARNING: Too many lcores enabled. Only 1 used.\n");
    
    cls_app = 
rte_zmalloc(NULL, size, RTE_CACHE_LINE_SIZE);
    if (cls_app == NULL)
        rte_exit(EXIT_FAILURE, "Cannot allocate classifier memory\n");
 
    cls_params.
name = "flow_classifier";
    cls_params.socket_id = socket_id;
    if (cls_app->cls == NULL) {
        rte_exit(EXIT_FAILURE, 
"Cannot create classifier\n");
 
    }
    
    table_acl_params.name = "table_acl_ipv4_5tuple";
    table_acl_params.n_rules = FLOW_CLASSIFY_MAX_RULE_NUM;
    table_acl_params.n_rule_fields = 
RTE_DIM(ipv4_defs);
    memcpy(table_acl_params.field_format, ipv4_defs, sizeof(ipv4_defs));
    
    cls_table_params.arg_create = &table_acl_params;
    if (ret) {
        rte_exit(EXIT_FAILURE, 
"Failed to create classifier table\n");
 
    }
    
    if (add_rules(parm_config.rule_ipv4_name, cls_app)) {
        rte_exit(EXIT_FAILURE, 
"Failed to add rules\n");
 
    }
    
    lcore_main(cls_app);
    return 0;
}