Add cli commands for table entries in softnic pipeline objects. Signed-off-by: Cristian Dumitrescu <cristian.dumitre...@intel.com> Signed-off-by: Jasvinder Singh <jasvinder.si...@intel.com> --- drivers/net/softnic/rte_eth_softnic_cli.c | 2187 ++++++++++++++++++++--- drivers/net/softnic/rte_eth_softnic_internals.h | 35 + drivers/net/softnic/rte_eth_softnic_thread.c | 1316 ++++++++++++++ 3 files changed, 3319 insertions(+), 219 deletions(-)
diff --git a/drivers/net/softnic/rte_eth_softnic_cli.c b/drivers/net/softnic/rte_eth_softnic_cli.c index 8b65a54..59688ff 100644 --- a/drivers/net/softnic/rte_eth_softnic_cli.c +++ b/drivers/net/softnic/rte_eth_softnic_cli.c @@ -7,6 +7,9 @@ #include <stdlib.h> #include <string.h> +#include <rte_common.h> +#include <rte_cycles.h> + #include "rte_eth_softnic_internals.h" #include "parser.h" @@ -1593,272 +1596,2018 @@ cmd_softnic_pipeline_port_in_disable(struct pmd_internals *softnic, } /** - * thread <thread_id> pipeline <pipeline_name> enable + * <match> ::= + * + * match + * acl + * priority <priority> + * ipv4 | ipv6 <sa> <sa_depth> <da> <da_depth> + * <sp0> <sp1> <dp0> <dp1> <proto> + * | array <pos> + * | hash + * raw <key> + * | ipv4_5tuple <sa> <da> <sp> <dp> <proto> + * | ipv6_5tuple <sa> <da> <sp> <dp> <proto> + * | ipv4_addr <addr> + * | ipv6_addr <addr> + * | qinq <svlan> <cvlan> + * | lpm + * ipv4 | ipv6 <addr> <depth> */ -static void -cmd_softnic_thread_pipeline_enable(struct pmd_internals *softnic, - char **tokens, +struct pkt_key_qinq { + uint16_t ethertype_svlan; + uint16_t svlan; + uint16_t ethertype_cvlan; + uint16_t cvlan; +} __attribute__((__packed__)); + +struct pkt_key_ipv4_5tuple { + uint8_t time_to_live; + uint8_t proto; + uint16_t hdr_checksum; + uint32_t sa; + uint32_t da; + uint16_t sp; + uint16_t dp; +} __attribute__((__packed__)); + +struct pkt_key_ipv6_5tuple { + uint16_t payload_length; + uint8_t proto; + uint8_t hop_limit; + uint8_t sa[16]; + uint8_t da[16]; + uint16_t sp; + uint16_t dp; +} __attribute__((__packed__)); + +struct pkt_key_ipv4_addr { + uint32_t addr; +} __attribute__((__packed__)); + +struct pkt_key_ipv6_addr { + uint8_t addr[16]; +} __attribute__((__packed__)); + +static uint32_t +parse_match(char **tokens, uint32_t n_tokens, char *out, - size_t out_size) + size_t out_size, + struct softnic_table_rule_match *m) { - char *pipeline_name; - uint32_t thread_id; - int status; - - if (n_tokens != 5) { - snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); - return; - } + memset(m, 0, sizeof(*m)); - if (softnic_parser_read_uint32(&thread_id, tokens[1]) != 0) { - snprintf(out, out_size, MSG_ARG_INVALID, "thread_id"); - return; - } + if (n_tokens < 2) + return 0; - if (strcmp(tokens[2], "pipeline") != 0) { - snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline"); - return; + if (strcmp(tokens[0], "match") != 0) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "match"); + return 0; } - pipeline_name = tokens[3]; + if (strcmp(tokens[1], "acl") == 0) { + if (n_tokens < 14) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + return 0; + } - if (strcmp(tokens[4], "enable") != 0) { - snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable"); - return; - } + m->match_type = TABLE_ACL; - status = softnic_thread_pipeline_enable(softnic, thread_id, pipeline_name); - if (status) { - snprintf(out, out_size, MSG_CMD_FAIL, "thread pipeline enable"); - return; - } -} + if (strcmp(tokens[2], "priority") != 0) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "priority"); + return 0; + } -/** - * thread <thread_id> pipeline <pipeline_name> disable - */ -static void -cmd_softnic_thread_pipeline_disable(struct pmd_internals *softnic, - char **tokens, - uint32_t n_tokens, - char *out, - size_t out_size) -{ - char *pipeline_name; - uint32_t thread_id; - int status; + if (softnic_parser_read_uint32(&m->match.acl.priority, + tokens[3]) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "priority"); + return 0; + } - if (n_tokens != 5) { - snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); - return; - } + if (strcmp(tokens[4], "ipv4") == 0) { + struct in_addr saddr, daddr; - if (softnic_parser_read_uint32(&thread_id, tokens[1]) != 0) { - snprintf(out, out_size, MSG_ARG_INVALID, "thread_id"); - return; - } + m->match.acl.ip_version = 1; - if (strcmp(tokens[2], "pipeline") != 0) { - snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline"); - return; - } + if (softnic_parse_ipv4_addr(tokens[5], &saddr) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "sa"); + return 0; + } + m->match.acl.ipv4.sa = rte_be_to_cpu_32(saddr.s_addr); - pipeline_name = tokens[3]; + if (softnic_parse_ipv4_addr(tokens[7], &daddr) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "da"); + return 0; + } + m->match.acl.ipv4.da = rte_be_to_cpu_32(daddr.s_addr); + } else if (strcmp(tokens[4], "ipv6") == 0) { + struct in6_addr saddr, daddr; - if (strcmp(tokens[4], "disable") != 0) { - snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable"); - return; - } + m->match.acl.ip_version = 0; - status = softnic_thread_pipeline_disable(softnic, thread_id, pipeline_name); - if (status) { - snprintf(out, out_size, MSG_CMD_FAIL, - "thread pipeline disable"); - return; - } -} + if (softnic_parse_ipv6_addr(tokens[5], &saddr) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "sa"); + return 0; + } + memcpy(m->match.acl.ipv6.sa, saddr.s6_addr, 16); -void -softnic_cli_process(char *in, char *out, size_t out_size, void *arg) -{ - char *tokens[CMD_MAX_TOKENS]; - uint32_t n_tokens = RTE_DIM(tokens); - struct pmd_internals *softnic = arg; - int status; + if (softnic_parse_ipv6_addr(tokens[7], &daddr) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "da"); + return 0; + } + memcpy(m->match.acl.ipv6.da, daddr.s6_addr, 16); + } else { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, + "ipv4 or ipv6"); + return 0; + } - if (is_comment(in)) - return; + if (softnic_parser_read_uint32(&m->match.acl.sa_depth, + tokens[6]) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "sa_depth"); + return 0; + } - status = softnic_parse_tokenize_string(in, tokens, &n_tokens); - if (status) { - snprintf(out, out_size, MSG_ARG_TOO_MANY, ""); - return; - } + if (softnic_parser_read_uint32(&m->match.acl.da_depth, + tokens[8]) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "da_depth"); + return 0; + } - if (n_tokens == 0) - return; + if (softnic_parser_read_uint16(&m->match.acl.sp0, tokens[9]) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "sp0"); + return 0; + } - if (strcmp(tokens[0], "mempool") == 0) { - cmd_mempool(softnic, tokens, n_tokens, out, out_size); - return; - } + if (softnic_parser_read_uint16(&m->match.acl.sp1, tokens[10]) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "sp1"); + return 0; + } - if (strcmp(tokens[0], "link") == 0) { - cmd_link(softnic, tokens, n_tokens, out, out_size); - return; - } + if (softnic_parser_read_uint16(&m->match.acl.dp0, tokens[11]) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "dp0"); + return 0; + } - if (strcmp(tokens[0], "swq") == 0) { - cmd_swq(softnic, tokens, n_tokens, out, out_size); - return; - } + if (softnic_parser_read_uint16(&m->match.acl.dp1, tokens[12]) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "dp1"); + return 0; + } - if (strcmp(tokens[0], "tap") == 0) { - cmd_tap(softnic, tokens, n_tokens, out, out_size); - return; - } + if (softnic_parser_read_uint8(&m->match.acl.proto, tokens[13]) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "proto"); + return 0; + } - if (strcmp(tokens[0], "port") == 0) { - cmd_port_in_action_profile(softnic, tokens, n_tokens, out, out_size); - return; - } + m->match.acl.proto_mask = 0xff; - if (strcmp(tokens[0], "table") == 0) { - cmd_table_action_profile(softnic, tokens, n_tokens, out, out_size); - return; - } + return 14; + } /* acl */ - if (strcmp(tokens[0], "pipeline") == 0) { - if (n_tokens >= 3 && - (strcmp(tokens[2], "period") == 0)) { - cmd_pipeline(softnic, tokens, n_tokens, out, out_size); - return; + if (strcmp(tokens[1], "array") == 0) { + if (n_tokens < 3) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + return 0; } - if (n_tokens >= 5 && - (strcmp(tokens[2], "port") == 0) && - (strcmp(tokens[3], "in") == 0) && - (strcmp(tokens[4], "bsz") == 0)) { - cmd_pipeline_port_in(softnic, tokens, n_tokens, out, out_size); - return; - } + m->match_type = TABLE_ARRAY; - if (n_tokens >= 5 && - (strcmp(tokens[2], "port") == 0) && - (strcmp(tokens[3], "out") == 0) && - (strcmp(tokens[4], "bsz") == 0)) { - cmd_pipeline_port_out(softnic, tokens, n_tokens, out, out_size); - return; + if (softnic_parser_read_uint32(&m->match.array.pos, tokens[2]) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "pos"); + return 0; } - if (n_tokens >= 4 && - (strcmp(tokens[2], "table") == 0) && - (strcmp(tokens[3], "match") == 0)) { - cmd_pipeline_table(softnic, tokens, n_tokens, out, out_size); - return; - } + return 3; + } /* array */ - if (n_tokens >= 6 && - (strcmp(tokens[2], "port") == 0) && - (strcmp(tokens[3], "in") == 0) && - (strcmp(tokens[5], "table") == 0)) { - cmd_pipeline_port_in_table(softnic, tokens, n_tokens, - out, out_size); - return; + if (strcmp(tokens[1], "hash") == 0) { + if (n_tokens < 3) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + return 0; } - if (n_tokens >= 6 && - (strcmp(tokens[2], "port") == 0) && - (strcmp(tokens[3], "in") == 0) && - (strcmp(tokens[5], "enable") == 0)) { - cmd_softnic_pipeline_port_in_enable(softnic, tokens, n_tokens, - out, out_size); - return; - } + m->match_type = TABLE_HASH; - if (n_tokens >= 6 && - (strcmp(tokens[2], "port") == 0) && - (strcmp(tokens[3], "in") == 0) && - (strcmp(tokens[5], "disable") == 0)) { - cmd_softnic_pipeline_port_in_disable(softnic, tokens, n_tokens, - out, out_size); - return; - } - } + if (strcmp(tokens[2], "raw") == 0) { + uint32_t key_size = TABLE_RULE_MATCH_SIZE_MAX; - if (strcmp(tokens[0], "thread") == 0) { - if ((n_tokens >= 5) && - (strcmp(tokens[4], "enable") == 0)) { - cmd_softnic_thread_pipeline_enable(softnic, tokens, n_tokens, - out, out_size); - return; - } + if (n_tokens < 4) { + snprintf(out, out_size, MSG_ARG_MISMATCH, + tokens[0]); + return 0; + } - if ((n_tokens >= 5) && - (strcmp(tokens[4], "disable") == 0)) { - cmd_softnic_thread_pipeline_disable(softnic, tokens, n_tokens, - out, out_size); - return; - } - } + if (softnic_parse_hex_string(tokens[3], + m->match.hash.key, &key_size) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "key"); + return 0; + } - snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]); -} + return 4; + } /* hash raw */ -int -softnic_cli_script_process(struct pmd_internals *softnic, - const char *file_name, - size_t msg_in_len_max, - size_t msg_out_len_max) -{ - char *msg_in = NULL, *msg_out = NULL; - FILE *f = NULL; + if (strcmp(tokens[2], "ipv4_5tuple") == 0) { + struct pkt_key_ipv4_5tuple *ipv4 = + (struct pkt_key_ipv4_5tuple *)m->match.hash.key; + struct in_addr saddr, daddr; + uint16_t sp, dp; + uint8_t proto; - /* Check input arguments */ - if (file_name == NULL || - strlen(file_name) == 0 || - msg_in_len_max == 0 || - msg_out_len_max == 0) - return -EINVAL; + if (n_tokens < 8) { + snprintf(out, out_size, MSG_ARG_MISMATCH, + tokens[0]); + return 0; + } - msg_in = malloc(msg_in_len_max + 1); - msg_out = malloc(msg_out_len_max + 1); - if (msg_in == NULL || - msg_out == NULL) { - free(msg_out); - free(msg_in); - return -ENOMEM; - } + if (softnic_parse_ipv4_addr(tokens[3], &saddr) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "sa"); + return 0; + } - /* Open input file */ - f = fopen(file_name, "r"); - if (f == NULL) { - free(msg_out); - free(msg_in); - return -EIO; - } + if (softnic_parse_ipv4_addr(tokens[4], &daddr) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "da"); + return 0; + } - /* Read file */ - for ( ; ; ) { - if (fgets(msg_in, msg_in_len_max + 1, f) == NULL) - break; + if (softnic_parser_read_uint16(&sp, tokens[5]) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "sp"); + return 0; + } - printf("%s", msg_in); - msg_out[0] = 0; + if (softnic_parser_read_uint16(&dp, tokens[6]) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "dp"); + return 0; + } - softnic_cli_process(msg_in, - msg_out, - msg_out_len_max, - softnic); + if (softnic_parser_read_uint8(&proto, tokens[7]) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, + "proto"); + return 0; + } - if (strlen(msg_out)) - printf("%s", msg_out); - } + ipv4->sa = saddr.s_addr; + ipv4->da = daddr.s_addr; + ipv4->sp = rte_cpu_to_be_16(sp); + ipv4->dp = rte_cpu_to_be_16(dp); + ipv4->proto = proto; + + return 8; + } /* hash ipv4_5tuple */ + + if (strcmp(tokens[2], "ipv6_5tuple") == 0) { + struct pkt_key_ipv6_5tuple *ipv6 = + (struct pkt_key_ipv6_5tuple *)m->match.hash.key; + struct in6_addr saddr, daddr; + uint16_t sp, dp; + uint8_t proto; + + if (n_tokens < 8) { + snprintf(out, out_size, MSG_ARG_MISMATCH, + tokens[0]); + return 0; + } - /* Close file */ - fclose(f); - free(msg_out); - free(msg_in); - return 0; + if (softnic_parse_ipv6_addr(tokens[3], &saddr) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "sa"); + return 0; + } + + if (softnic_parse_ipv6_addr(tokens[4], &daddr) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "da"); + return 0; + } + + if (softnic_parser_read_uint16(&sp, tokens[5]) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "sp"); + return 0; + } + + if (softnic_parser_read_uint16(&dp, tokens[6]) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "dp"); + return 0; + } + + if (softnic_parser_read_uint8(&proto, tokens[7]) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, + "proto"); + return 0; + } + + memcpy(ipv6->sa, saddr.s6_addr, 16); + memcpy(ipv6->da, daddr.s6_addr, 16); + ipv6->sp = rte_cpu_to_be_16(sp); + ipv6->dp = rte_cpu_to_be_16(dp); + ipv6->proto = proto; + + return 8; + } /* hash ipv6_5tuple */ + + if (strcmp(tokens[2], "ipv4_addr") == 0) { + struct pkt_key_ipv4_addr *ipv4_addr = + (struct pkt_key_ipv4_addr *)m->match.hash.key; + struct in_addr addr; + + if (n_tokens < 4) { + snprintf(out, out_size, MSG_ARG_MISMATCH, + tokens[0]); + return 0; + } + + if (softnic_parse_ipv4_addr(tokens[3], &addr) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, + "addr"); + return 0; + } + + ipv4_addr->addr = addr.s_addr; + + return 4; + } /* hash ipv4_addr */ + + if (strcmp(tokens[2], "ipv6_addr") == 0) { + struct pkt_key_ipv6_addr *ipv6_addr = + (struct pkt_key_ipv6_addr *)m->match.hash.key; + struct in6_addr addr; + + if (n_tokens < 4) { + snprintf(out, out_size, MSG_ARG_MISMATCH, + tokens[0]); + return 0; + } + + if (softnic_parse_ipv6_addr(tokens[3], &addr) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, + "addr"); + return 0; + } + + memcpy(ipv6_addr->addr, addr.s6_addr, 16); + + return 4; + } /* hash ipv6_5tuple */ + + if (strcmp(tokens[2], "qinq") == 0) { + struct pkt_key_qinq *qinq = + (struct pkt_key_qinq *)m->match.hash.key; + uint16_t svlan, cvlan; + + if (n_tokens < 5) { + snprintf(out, out_size, MSG_ARG_MISMATCH, + tokens[0]); + return 0; + } + + if ((softnic_parser_read_uint16(&svlan, tokens[3]) != 0) || + svlan > 0xFFF) { + snprintf(out, out_size, MSG_ARG_INVALID, + "svlan"); + return 0; + } + + if ((softnic_parser_read_uint16(&cvlan, tokens[4]) != 0) || + cvlan > 0xFFF) { + snprintf(out, out_size, MSG_ARG_INVALID, + "cvlan"); + return 0; + } + + qinq->svlan = rte_cpu_to_be_16(svlan); + qinq->cvlan = rte_cpu_to_be_16(cvlan); + + return 5; + } /* hash qinq */ + + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + return 0; + } /* hash */ + + if (strcmp(tokens[1], "lpm") == 0) { + if (n_tokens < 5) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + return 0; + } + + m->match_type = TABLE_LPM; + + if (strcmp(tokens[2], "ipv4") == 0) { + struct in_addr addr; + + m->match.lpm.ip_version = 1; + + if (softnic_parse_ipv4_addr(tokens[3], &addr) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, + "addr"); + return 0; + } + + m->match.lpm.ipv4 = rte_be_to_cpu_32(addr.s_addr); + } else if (strcmp(tokens[2], "ipv6") == 0) { + struct in6_addr addr; + + m->match.lpm.ip_version = 0; + + if (softnic_parse_ipv6_addr(tokens[3], &addr) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, + "addr"); + return 0; + } + + memcpy(m->match.lpm.ipv6, addr.s6_addr, 16); + } else { + snprintf(out, out_size, MSG_ARG_MISMATCH, + "ipv4 or ipv6"); + return 0; + } + + if (softnic_parser_read_uint8(&m->match.lpm.depth, tokens[4]) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "depth"); + return 0; + } + + return 5; + } /* lpm */ + + snprintf(out, out_size, MSG_ARG_MISMATCH, + "acl or array or hash or lpm"); + return 0; +} + +/** + * table_action ::= + * + * action + * fwd + * drop + * | port <port_id> + * | meta + * | table <table_id> + * [balance <out0> ... <out7>] + * [meter + * tc0 meter <meter_profile_id> policer g <pa> y <pa> r <pa> + * [tc1 meter <meter_profile_id> policer g <pa> y <pa> r <pa> + * tc2 meter <meter_profile_id> policer g <pa> y <pa> r <pa> + * tc3 meter <meter_profile_id> policer g <pa> y <pa> r <pa>]] + * [tm subport <subport_id> pipe <pipe_id>] + * [encap + * ether <da> <sa> + * | vlan <da> <sa> <pcp> <dei> <vid> + * | qinq <da> <sa> <pcp> <dei> <vid> <pcp> <dei> <vid> + * | mpls unicast | multicast + * <da> <sa> + * label0 <label> <tc> <ttl> + * [label1 <label> <tc> <ttl> + * [label2 <label> <tc> <ttl> + * [label3 <label> <tc> <ttl>]]] + * | pppoe <da> <sa> <session_id>] + * [nat ipv4 | ipv6 <addr> <port>] + * [ttl dec | keep] + * [stats] + * [time] + * + * where: + * <pa> ::= g | y | r | drop + */ +static uint32_t +parse_table_action_fwd(char **tokens, + uint32_t n_tokens, + struct softnic_table_rule_action *a) +{ + if (n_tokens == 0 || + (strcmp(tokens[0], "fwd") != 0)) + return 0; + + tokens++; + n_tokens--; + + if (n_tokens && (strcmp(tokens[0], "drop") == 0)) { + a->fwd.action = RTE_PIPELINE_ACTION_DROP; + a->action_mask |= 1 << RTE_TABLE_ACTION_FWD; + return 1 + 1; + } + + if (n_tokens && (strcmp(tokens[0], "port") == 0)) { + uint32_t id; + + if (n_tokens < 2 || + softnic_parser_read_uint32(&id, tokens[1])) + return 0; + + a->fwd.action = RTE_PIPELINE_ACTION_PORT; + a->fwd.id = id; + a->action_mask |= 1 << RTE_TABLE_ACTION_FWD; + return 1 + 2; + } + + if (n_tokens && (strcmp(tokens[0], "meta") == 0)) { + a->fwd.action = RTE_PIPELINE_ACTION_PORT_META; + a->action_mask |= 1 << RTE_TABLE_ACTION_FWD; + return 1 + 1; + } + + if (n_tokens && (strcmp(tokens[0], "table") == 0)) { + uint32_t id; + + if (n_tokens < 2 || + softnic_parser_read_uint32(&id, tokens[1])) + return 0; + + a->fwd.action = RTE_PIPELINE_ACTION_TABLE; + a->fwd.id = id; + a->action_mask |= 1 << RTE_TABLE_ACTION_FWD; + return 1 + 2; + } + + return 0; +} + +static uint32_t +parse_table_action_balance(char **tokens, + uint32_t n_tokens, + struct softnic_table_rule_action *a) +{ + uint32_t i; + + if (n_tokens == 0 || + (strcmp(tokens[0], "balance") != 0)) + return 0; + + tokens++; + n_tokens--; + + if (n_tokens < RTE_TABLE_ACTION_LB_TABLE_SIZE) + return 0; + + for (i = 0; i < RTE_TABLE_ACTION_LB_TABLE_SIZE; i++) + if (softnic_parser_read_uint32(&a->lb.out[i], tokens[i]) != 0) + return 0; + + a->action_mask |= 1 << RTE_TABLE_ACTION_LB; + return 1 + RTE_TABLE_ACTION_LB_TABLE_SIZE; +} + +static int +parse_policer_action(char *token, enum rte_table_action_policer *a) +{ + if (strcmp(token, "g") == 0) { + *a = RTE_TABLE_ACTION_POLICER_COLOR_GREEN; + return 0; + } + + if (strcmp(token, "y") == 0) { + *a = RTE_TABLE_ACTION_POLICER_COLOR_YELLOW; + return 0; + } + + if (strcmp(token, "r") == 0) { + *a = RTE_TABLE_ACTION_POLICER_COLOR_RED; + return 0; + } + + if (strcmp(token, "drop") == 0) { + *a = RTE_TABLE_ACTION_POLICER_DROP; + return 0; + } + + return -1; +} + +static uint32_t +parse_table_action_meter_tc(char **tokens, + uint32_t n_tokens, + struct rte_table_action_mtr_tc_params *mtr) +{ + if (n_tokens < 9 || + strcmp(tokens[0], "meter") || + softnic_parser_read_uint32(&mtr->meter_profile_id, tokens[1]) || + strcmp(tokens[2], "policer") || + strcmp(tokens[3], "g") || + parse_policer_action(tokens[4], &mtr->policer[e_RTE_METER_GREEN]) || + strcmp(tokens[5], "y") || + parse_policer_action(tokens[6], &mtr->policer[e_RTE_METER_YELLOW]) || + strcmp(tokens[7], "r") || + parse_policer_action(tokens[8], &mtr->policer[e_RTE_METER_RED])) + return 0; + + return 9; +} + +static uint32_t +parse_table_action_meter(char **tokens, + uint32_t n_tokens, + struct softnic_table_rule_action *a) +{ + if (n_tokens == 0 || + strcmp(tokens[0], "meter")) + return 0; + + tokens++; + n_tokens--; + + if (n_tokens < 10 || + strcmp(tokens[0], "tc0") || + (parse_table_action_meter_tc(tokens + 1, + n_tokens - 1, + &a->mtr.mtr[0]) == 0)) + return 0; + + tokens += 10; + n_tokens -= 10; + + if (n_tokens == 0 || + strcmp(tokens[0], "tc1")) { + a->mtr.tc_mask = 1; + a->action_mask |= 1 << RTE_TABLE_ACTION_MTR; + return 1 + 10; + } + + if (n_tokens < 30 || + (parse_table_action_meter_tc(tokens + 1, + n_tokens - 1, &a->mtr.mtr[1]) == 0) || + strcmp(tokens[10], "tc2") || + (parse_table_action_meter_tc(tokens + 11, + n_tokens - 11, &a->mtr.mtr[2]) == 0) || + strcmp(tokens[20], "tc3") || + (parse_table_action_meter_tc(tokens + 21, + n_tokens - 21, &a->mtr.mtr[3]) == 0)) + return 0; + + a->mtr.tc_mask = 0xF; + a->action_mask |= 1 << RTE_TABLE_ACTION_MTR; + return 1 + 10 + 3 * 10; +} + +static uint32_t +parse_table_action_tm(char **tokens, + uint32_t n_tokens, + struct softnic_table_rule_action *a) +{ + uint32_t subport_id, pipe_id; + + if (n_tokens < 5 || + strcmp(tokens[0], "tm") || + strcmp(tokens[1], "subport") || + softnic_parser_read_uint32(&subport_id, tokens[2]) || + strcmp(tokens[3], "pipe") || + softnic_parser_read_uint32(&pipe_id, tokens[4])) + return 0; + + a->tm.subport_id = subport_id; + a->tm.pipe_id = pipe_id; + a->action_mask |= 1 << RTE_TABLE_ACTION_TM; + return 5; +} + +static uint32_t +parse_table_action_encap(char **tokens, + uint32_t n_tokens, + struct softnic_table_rule_action *a) +{ + if (n_tokens == 0 || + strcmp(tokens[0], "encap")) + return 0; + + tokens++; + n_tokens--; + + /* ether */ + if (n_tokens && (strcmp(tokens[0], "ether") == 0)) { + if (n_tokens < 3 || + softnic_parse_mac_addr(tokens[1], &a->encap.ether.ether.da) || + softnic_parse_mac_addr(tokens[2], &a->encap.ether.ether.sa)) + return 0; + + a->encap.type = RTE_TABLE_ACTION_ENCAP_ETHER; + a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP; + return 1 + 3; + } + + /* vlan */ + if (n_tokens && (strcmp(tokens[0], "vlan") == 0)) { + uint32_t pcp, dei, vid; + + if (n_tokens < 6 || + softnic_parse_mac_addr(tokens[1], &a->encap.vlan.ether.da) || + softnic_parse_mac_addr(tokens[2], &a->encap.vlan.ether.sa) || + softnic_parser_read_uint32(&pcp, tokens[3]) || + pcp > 0x7 || + softnic_parser_read_uint32(&dei, tokens[4]) || + dei > 0x1 || + softnic_parser_read_uint32(&vid, tokens[5]) || + vid > 0xFFF) + return 0; + + a->encap.vlan.vlan.pcp = pcp & 0x7; + a->encap.vlan.vlan.dei = dei & 0x1; + a->encap.vlan.vlan.vid = vid & 0xFFF; + a->encap.type = RTE_TABLE_ACTION_ENCAP_VLAN; + a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP; + return 1 + 6; + } + + /* qinq */ + if (n_tokens && (strcmp(tokens[0], "qinq") == 0)) { + uint32_t svlan_pcp, svlan_dei, svlan_vid; + uint32_t cvlan_pcp, cvlan_dei, cvlan_vid; + + if (n_tokens < 9 || + softnic_parse_mac_addr(tokens[1], &a->encap.qinq.ether.da) || + softnic_parse_mac_addr(tokens[2], &a->encap.qinq.ether.sa) || + softnic_parser_read_uint32(&svlan_pcp, tokens[3]) || + svlan_pcp > 0x7 || + softnic_parser_read_uint32(&svlan_dei, tokens[4]) || + svlan_dei > 0x1 || + softnic_parser_read_uint32(&svlan_vid, tokens[5]) || + svlan_vid > 0xFFF || + softnic_parser_read_uint32(&cvlan_pcp, tokens[6]) || + cvlan_pcp > 0x7 || + softnic_parser_read_uint32(&cvlan_dei, tokens[7]) || + cvlan_dei > 0x1 || + softnic_parser_read_uint32(&cvlan_vid, tokens[8]) || + cvlan_vid > 0xFFF) + return 0; + + a->encap.qinq.svlan.pcp = svlan_pcp & 0x7; + a->encap.qinq.svlan.dei = svlan_dei & 0x1; + a->encap.qinq.svlan.vid = svlan_vid & 0xFFF; + a->encap.qinq.cvlan.pcp = cvlan_pcp & 0x7; + a->encap.qinq.cvlan.dei = cvlan_dei & 0x1; + a->encap.qinq.cvlan.vid = cvlan_vid & 0xFFF; + a->encap.type = RTE_TABLE_ACTION_ENCAP_QINQ; + a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP; + return 1 + 9; + } + + /* mpls */ + if (n_tokens && (strcmp(tokens[0], "mpls") == 0)) { + uint32_t label, tc, ttl; + + if (n_tokens < 8) + return 0; + + if (strcmp(tokens[1], "unicast") == 0) + a->encap.mpls.unicast = 1; + else if (strcmp(tokens[1], "multicast") == 0) + a->encap.mpls.unicast = 0; + else + return 0; + + if (softnic_parse_mac_addr(tokens[2], &a->encap.mpls.ether.da) || + softnic_parse_mac_addr(tokens[3], &a->encap.mpls.ether.sa) || + strcmp(tokens[4], "label0") || + softnic_parser_read_uint32(&label, tokens[5]) || + label > 0xFFFFF || + softnic_parser_read_uint32(&tc, tokens[6]) || + tc > 0x7 || + softnic_parser_read_uint32(&ttl, tokens[7]) || + ttl > 0x3F) + return 0; + + a->encap.mpls.mpls[0].label = label; + a->encap.mpls.mpls[0].tc = tc; + a->encap.mpls.mpls[0].ttl = ttl; + + tokens += 8; + n_tokens -= 8; + + if (n_tokens == 0 || + strcmp(tokens[0], "label1")) { + a->encap.mpls.mpls_count = 1; + a->encap.type = RTE_TABLE_ACTION_ENCAP_MPLS; + a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP; + return 1 + 8; + } + + if (n_tokens < 4 || + softnic_parser_read_uint32(&label, tokens[1]) || + label > 0xFFFFF || + softnic_parser_read_uint32(&tc, tokens[2]) || + tc > 0x7 || + softnic_parser_read_uint32(&ttl, tokens[3]) || + ttl > 0x3F) + return 0; + + a->encap.mpls.mpls[1].label = label; + a->encap.mpls.mpls[1].tc = tc; + a->encap.mpls.mpls[1].ttl = ttl; + + tokens += 4; + n_tokens -= 4; + + if (n_tokens == 0 || + strcmp(tokens[0], "label2")) { + a->encap.mpls.mpls_count = 2; + a->encap.type = RTE_TABLE_ACTION_ENCAP_MPLS; + a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP; + return 1 + 8 + 4; + } + + if (n_tokens < 4 || + softnic_parser_read_uint32(&label, tokens[1]) || + label > 0xFFFFF || + softnic_parser_read_uint32(&tc, tokens[2]) || + tc > 0x7 || + softnic_parser_read_uint32(&ttl, tokens[3]) || + ttl > 0x3F) + return 0; + + a->encap.mpls.mpls[2].label = label; + a->encap.mpls.mpls[2].tc = tc; + a->encap.mpls.mpls[2].ttl = ttl; + + tokens += 4; + n_tokens -= 4; + + if (n_tokens == 0 || + strcmp(tokens[0], "label3")) { + a->encap.mpls.mpls_count = 3; + a->encap.type = RTE_TABLE_ACTION_ENCAP_MPLS; + a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP; + return 1 + 8 + 4 + 4; + } + + if (n_tokens < 4 || + softnic_parser_read_uint32(&label, tokens[1]) || + label > 0xFFFFF || + softnic_parser_read_uint32(&tc, tokens[2]) || + tc > 0x7 || + softnic_parser_read_uint32(&ttl, tokens[3]) || + ttl > 0x3F) + return 0; + + a->encap.mpls.mpls[3].label = label; + a->encap.mpls.mpls[3].tc = tc; + a->encap.mpls.mpls[3].ttl = ttl; + + a->encap.mpls.mpls_count = 4; + a->encap.type = RTE_TABLE_ACTION_ENCAP_MPLS; + a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP; + return 1 + 8 + 4 + 4 + 4; + } + + /* pppoe */ + if (n_tokens && (strcmp(tokens[0], "pppoe") == 0)) { + if (n_tokens < 4 || + softnic_parse_mac_addr(tokens[1], &a->encap.pppoe.ether.da) || + softnic_parse_mac_addr(tokens[2], &a->encap.pppoe.ether.sa) || + softnic_parser_read_uint16(&a->encap.pppoe.pppoe.session_id, + tokens[3])) + return 0; + + a->encap.type = RTE_TABLE_ACTION_ENCAP_PPPOE; + a->action_mask |= 1 << RTE_TABLE_ACTION_ENCAP; + return 1 + 4; + } + + return 0; +} + +static uint32_t +parse_table_action_nat(char **tokens, + uint32_t n_tokens, + struct softnic_table_rule_action *a) +{ + if (n_tokens < 4 || + strcmp(tokens[0], "nat")) + return 0; + + if (strcmp(tokens[1], "ipv4") == 0) { + struct in_addr addr; + uint16_t port; + + if (softnic_parse_ipv4_addr(tokens[2], &addr) || + softnic_parser_read_uint16(&port, tokens[3])) + return 0; + + a->nat.ip_version = 1; + a->nat.addr.ipv4 = rte_be_to_cpu_32(addr.s_addr); + a->nat.port = port; + a->action_mask |= 1 << RTE_TABLE_ACTION_NAT; + return 4; + } + + if (strcmp(tokens[1], "ipv6") == 0) { + struct in6_addr addr; + uint16_t port; + + if (softnic_parse_ipv6_addr(tokens[2], &addr) || + softnic_parser_read_uint16(&port, tokens[3])) + return 0; + + a->nat.ip_version = 0; + memcpy(a->nat.addr.ipv6, addr.s6_addr, 16); + a->nat.port = port; + a->action_mask |= 1 << RTE_TABLE_ACTION_NAT; + return 4; + } + + return 0; +} + +static uint32_t +parse_table_action_ttl(char **tokens, + uint32_t n_tokens, + struct softnic_table_rule_action *a) +{ + if (n_tokens < 2 || + strcmp(tokens[0], "ttl")) + return 0; + + if (strcmp(tokens[1], "dec") == 0) + a->ttl.decrement = 1; + else if (strcmp(tokens[1], "keep") == 0) + a->ttl.decrement = 0; + else + return 0; + + a->action_mask |= 1 << RTE_TABLE_ACTION_TTL; + return 2; +} + +static uint32_t +parse_table_action_stats(char **tokens, + uint32_t n_tokens, + struct softnic_table_rule_action *a) +{ + if (n_tokens < 1 || + strcmp(tokens[0], "stats")) + return 0; + + a->stats.n_packets = 0; + a->stats.n_bytes = 0; + a->action_mask |= 1 << RTE_TABLE_ACTION_STATS; + return 1; +} + +static uint32_t +parse_table_action_time(char **tokens, + uint32_t n_tokens, + struct softnic_table_rule_action *a) +{ + if (n_tokens < 1 || + strcmp(tokens[0], "time")) + return 0; + + a->time.time = rte_rdtsc(); + a->action_mask |= 1 << RTE_TABLE_ACTION_TIME; + return 1; +} + +static uint32_t +parse_table_action(char **tokens, + uint32_t n_tokens, + char *out, + size_t out_size, + struct softnic_table_rule_action *a) +{ + uint32_t n_tokens0 = n_tokens; + + memset(a, 0, sizeof(*a)); + + if (n_tokens < 2 || + strcmp(tokens[0], "action")) + return 0; + + tokens++; + n_tokens--; + + if (n_tokens && (strcmp(tokens[0], "fwd") == 0)) { + uint32_t n; + + n = parse_table_action_fwd(tokens, n_tokens, a); + if (n == 0) { + snprintf(out, out_size, MSG_ARG_INVALID, + "action fwd"); + return 0; + } + + tokens += n; + n_tokens -= n; + } + + if (n_tokens && (strcmp(tokens[0], "balance") == 0)) { + uint32_t n; + + n = parse_table_action_balance(tokens, n_tokens, a); + if (n == 0) { + snprintf(out, out_size, MSG_ARG_INVALID, + "action balance"); + return 0; + } + + tokens += n; + n_tokens -= n; + } + + if (n_tokens && (strcmp(tokens[0], "meter") == 0)) { + uint32_t n; + + n = parse_table_action_meter(tokens, n_tokens, a); + if (n == 0) { + snprintf(out, out_size, MSG_ARG_INVALID, + "action meter"); + return 0; + } + + tokens += n; + n_tokens -= n; + } + + if (n_tokens && (strcmp(tokens[0], "tm") == 0)) { + uint32_t n; + + n = parse_table_action_tm(tokens, n_tokens, a); + if (n == 0) { + snprintf(out, out_size, MSG_ARG_INVALID, + "action tm"); + return 0; + } + + tokens += n; + n_tokens -= n; + } + + if (n_tokens && (strcmp(tokens[0], "encap") == 0)) { + uint32_t n; + + n = parse_table_action_encap(tokens, n_tokens, a); + if (n == 0) { + snprintf(out, out_size, MSG_ARG_INVALID, + "action encap"); + return 0; + } + + tokens += n; + n_tokens -= n; + } + + if (n_tokens && (strcmp(tokens[0], "nat") == 0)) { + uint32_t n; + + n = parse_table_action_nat(tokens, n_tokens, a); + if (n == 0) { + snprintf(out, out_size, MSG_ARG_INVALID, + "action nat"); + return 0; + } + + tokens += n; + n_tokens -= n; + } + + if (n_tokens && (strcmp(tokens[0], "ttl") == 0)) { + uint32_t n; + + n = parse_table_action_ttl(tokens, n_tokens, a); + if (n == 0) { + snprintf(out, out_size, MSG_ARG_INVALID, + "action ttl"); + return 0; + } + + tokens += n; + n_tokens -= n; + } + + if (n_tokens && (strcmp(tokens[0], "stats") == 0)) { + uint32_t n; + + n = parse_table_action_stats(tokens, n_tokens, a); + if (n == 0) { + snprintf(out, out_size, MSG_ARG_INVALID, + "action stats"); + return 0; + } + + tokens += n; + n_tokens -= n; + } + + if (n_tokens && (strcmp(tokens[0], "time") == 0)) { + uint32_t n; + + n = parse_table_action_time(tokens, n_tokens, a); + if (n == 0) { + snprintf(out, out_size, MSG_ARG_INVALID, + "action time"); + return 0; + } + + tokens += n; + n_tokens -= n; + } + + if (n_tokens0 - n_tokens == 1) { + snprintf(out, out_size, MSG_ARG_INVALID, "action"); + return 0; + } + + return n_tokens0 - n_tokens; +} + +/** + * pipeline <pipeline_name> table <table_id> rule add + * match <match> + * action <table_action> + */ +static void +cmd_softnic_pipeline_table_rule_add(struct pmd_internals *softnic, + char **tokens, + uint32_t n_tokens, + char *out, + size_t out_size) +{ + struct softnic_table_rule_match m; + struct softnic_table_rule_action a; + char *pipeline_name; + void *data; + uint32_t table_id, t0, n_tokens_parsed; + int status; + + if (n_tokens < 8) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + return; + } + + pipeline_name = tokens[1]; + + if (strcmp(tokens[2], "table") != 0) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table"); + return; + } + + if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "table_id"); + return; + } + + if (strcmp(tokens[4], "rule") != 0) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule"); + return; + } + + if (strcmp(tokens[5], "add") != 0) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add"); + return; + } + + t0 = 6; + + /* match */ + n_tokens_parsed = parse_match(tokens + t0, + n_tokens - t0, + out, + out_size, + &m); + if (n_tokens_parsed == 0) + return; + t0 += n_tokens_parsed; + + /* action */ + n_tokens_parsed = parse_table_action(tokens + t0, + n_tokens - t0, + out, + out_size, + &a); + if (n_tokens_parsed == 0) + return; + t0 += n_tokens_parsed; + + if (t0 != n_tokens) { + snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]); + return; + } + + status = softnic_pipeline_table_rule_add(softnic, + pipeline_name, + table_id, + &m, + &a, + &data); + if (status) { + snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]); + return; + } +} + +/** + * pipeline <pipeline_name> table <table_id> rule add + * match + * default + * action + * fwd + * drop + * | port <port_id> + * | meta + * | table <table_id> + */ +static void +cmd_softnic_pipeline_table_rule_add_default(struct pmd_internals *softnic, + char **tokens, + uint32_t n_tokens, + char *out, + size_t out_size) +{ + struct softnic_table_rule_action action; + void *data; + char *pipeline_name; + uint32_t table_id; + int status; + + if (n_tokens != 11 && + n_tokens != 12) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + return; + } + + pipeline_name = tokens[1]; + + if (strcmp(tokens[2], "table") != 0) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table"); + return; + } + + if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "table_id"); + return; + } + + if (strcmp(tokens[4], "rule") != 0) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule"); + return; + } + + if (strcmp(tokens[5], "add") != 0) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add"); + return; + } + + if (strcmp(tokens[6], "match") != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "match"); + return; + } + + if (strcmp(tokens[7], "default") != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "default"); + return; + } + + if (strcmp(tokens[8], "action") != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "action"); + return; + } + + if (strcmp(tokens[9], "fwd") != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "fwd"); + return; + } + + action.action_mask = 1 << RTE_TABLE_ACTION_FWD; + + if (strcmp(tokens[10], "drop") == 0) { + if (n_tokens != 11) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + return; + } + + action.fwd.action = RTE_PIPELINE_ACTION_DROP; + } else if (strcmp(tokens[10], "port") == 0) { + uint32_t id; + + if (n_tokens != 12) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + return; + } + + if (softnic_parser_read_uint32(&id, tokens[11]) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "port_id"); + return; + } + + action.fwd.action = RTE_PIPELINE_ACTION_PORT; + action.fwd.id = id; + } else if (strcmp(tokens[10], "meta") == 0) { + if (n_tokens != 11) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + return; + } + + action.fwd.action = RTE_PIPELINE_ACTION_PORT_META; + } else if (strcmp(tokens[10], "table") == 0) { + uint32_t id; + + if (n_tokens != 12) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + return; + } + + if (softnic_parser_read_uint32(&id, tokens[11]) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "table_id"); + return; + } + + action.fwd.action = RTE_PIPELINE_ACTION_TABLE; + action.fwd.id = id; + } else { + snprintf(out, out_size, MSG_ARG_INVALID, + "drop or port or meta or table"); + return; + } + + status = softnic_pipeline_table_rule_add_default(softnic, + pipeline_name, + table_id, + &action, + &data); + if (status) { + snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]); + return; + } +} + +/** + * pipeline <pipeline_name> table <table_id> rule add bulk <file_name> <n_rules> + * + * File <file_name>: + * - line format: match <match> action <action> + */ +static int +cli_rule_file_process(const char *file_name, + size_t line_len_max, + struct softnic_table_rule_match *m, + struct softnic_table_rule_action *a, + uint32_t *n_rules, + uint32_t *line_number, + char *out, + size_t out_size); + +static void +cmd_softnic_pipeline_table_rule_add_bulk(struct pmd_internals *softnic, + char **tokens, + uint32_t n_tokens, + char *out, + size_t out_size) +{ + struct softnic_table_rule_match *match; + struct softnic_table_rule_action *action; + void **data; + char *pipeline_name, *file_name; + uint32_t table_id, n_rules, n_rules_parsed, line_number; + int status; + + if (n_tokens != 9) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + return; + } + + pipeline_name = tokens[1]; + + if (strcmp(tokens[2], "table") != 0) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table"); + return; + } + + if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "table_id"); + return; + } + + if (strcmp(tokens[4], "rule") != 0) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule"); + return; + } + + if (strcmp(tokens[5], "add") != 0) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add"); + return; + } + + if (strcmp(tokens[6], "bulk") != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "bulk"); + return; + } + + file_name = tokens[7]; + + if ((softnic_parser_read_uint32(&n_rules, tokens[8]) != 0) || + n_rules == 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "n_rules"); + return; + } + + /* Memory allocation. */ + match = calloc(n_rules, sizeof(struct softnic_table_rule_match)); + action = calloc(n_rules, sizeof(struct softnic_table_rule_action)); + data = calloc(n_rules, sizeof(void *)); + if (match == NULL || + action == NULL || + data == NULL) { + snprintf(out, out_size, MSG_OUT_OF_MEMORY); + free(data); + free(action); + free(match); + return; + } + + /* Load rule file */ + n_rules_parsed = n_rules; + status = cli_rule_file_process(file_name, + 1024, + match, + action, + &n_rules_parsed, + &line_number, + out, + out_size); + if (status) { + snprintf(out, out_size, MSG_FILE_ERR, file_name, line_number); + free(data); + free(action); + free(match); + return; + } + if (n_rules_parsed != n_rules) { + snprintf(out, out_size, MSG_FILE_NOT_ENOUGH, file_name); + free(data); + free(action); + free(match); + return; + } + + /* Rule bulk add */ + status = softnic_pipeline_table_rule_add_bulk(softnic, + pipeline_name, + table_id, + match, + action, + data, + &n_rules); + if (status) { + snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]); + free(data); + free(action); + free(match); + return; + } + + /* Memory free */ + free(data); + free(action); + free(match); +} + +/** + * pipeline <pipeline_name> table <table_id> rule delete + * match <match> + */ +static void +cmd_softnic_pipeline_table_rule_delete(struct pmd_internals *softnic, + char **tokens, + uint32_t n_tokens, + char *out, + size_t out_size) +{ + struct softnic_table_rule_match m; + char *pipeline_name; + uint32_t table_id, n_tokens_parsed, t0; + int status; + + if (n_tokens < 8) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + return; + } + + pipeline_name = tokens[1]; + + if (strcmp(tokens[2], "table") != 0) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table"); + return; + } + + if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "table_id"); + return; + } + + if (strcmp(tokens[4], "rule") != 0) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule"); + return; + } + + if (strcmp(tokens[5], "delete") != 0) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete"); + return; + } + + t0 = 6; + + /* match */ + n_tokens_parsed = parse_match(tokens + t0, + n_tokens - t0, + out, + out_size, + &m); + if (n_tokens_parsed == 0) + return; + t0 += n_tokens_parsed; + + if (n_tokens != t0) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + return; + } + + status = softnic_pipeline_table_rule_delete(softnic, + pipeline_name, + table_id, + &m); + if (status) { + snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]); + return; + } +} + +/** + * pipeline <pipeline_name> table <table_id> rule delete + * match + * default + */ +static void +cmd_softnic_pipeline_table_rule_delete_default(struct pmd_internals *softnic, + char **tokens, + uint32_t n_tokens, + char *out, + size_t out_size) +{ + char *pipeline_name; + uint32_t table_id; + int status; + + if (n_tokens != 8) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + return; + } + + pipeline_name = tokens[1]; + + if (strcmp(tokens[2], "table") != 0) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "table"); + return; + } + + if (softnic_parser_read_uint32(&table_id, tokens[3]) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "table_id"); + return; + } + + if (strcmp(tokens[4], "rule") != 0) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rule"); + return; + } + + if (strcmp(tokens[5], "delete") != 0) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "delete"); + return; + } + + if (strcmp(tokens[6], "match") != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "match"); + return; + } + + if (strcmp(tokens[7], "default") != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "default"); + return; + } + + status = softnic_pipeline_table_rule_delete_default(softnic, + pipeline_name, + table_id); + if (status) { + snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]); + return; + } +} + +/** + * thread <thread_id> pipeline <pipeline_name> enable + */ +static void +cmd_softnic_thread_pipeline_enable(struct pmd_internals *softnic, + char **tokens, + uint32_t n_tokens, + char *out, + size_t out_size) +{ + char *pipeline_name; + uint32_t thread_id; + int status; + + if (n_tokens != 5) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + return; + } + + if (softnic_parser_read_uint32(&thread_id, tokens[1]) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "thread_id"); + return; + } + + if (strcmp(tokens[2], "pipeline") != 0) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline"); + return; + } + + pipeline_name = tokens[3]; + + if (strcmp(tokens[4], "enable") != 0) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "enable"); + return; + } + + status = softnic_thread_pipeline_enable(softnic, thread_id, pipeline_name); + if (status) { + snprintf(out, out_size, MSG_CMD_FAIL, "thread pipeline enable"); + return; + } +} + +/** + * thread <thread_id> pipeline <pipeline_name> disable + */ +static void +cmd_softnic_thread_pipeline_disable(struct pmd_internals *softnic, + char **tokens, + uint32_t n_tokens, + char *out, + size_t out_size) +{ + char *pipeline_name; + uint32_t thread_id; + int status; + + if (n_tokens != 5) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + return; + } + + if (softnic_parser_read_uint32(&thread_id, tokens[1]) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "thread_id"); + return; + } + + if (strcmp(tokens[2], "pipeline") != 0) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "pipeline"); + return; + } + + pipeline_name = tokens[3]; + + if (strcmp(tokens[4], "disable") != 0) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "disable"); + return; + } + + status = softnic_thread_pipeline_disable(softnic, thread_id, pipeline_name); + if (status) { + snprintf(out, out_size, MSG_CMD_FAIL, + "thread pipeline disable"); + return; + } +} + +void +softnic_cli_process(char *in, char *out, size_t out_size, void *arg) +{ + char *tokens[CMD_MAX_TOKENS]; + uint32_t n_tokens = RTE_DIM(tokens); + struct pmd_internals *softnic = arg; + int status; + + if (is_comment(in)) + return; + + status = softnic_parse_tokenize_string(in, tokens, &n_tokens); + if (status) { + snprintf(out, out_size, MSG_ARG_TOO_MANY, ""); + return; + } + + if (n_tokens == 0) + return; + + if (strcmp(tokens[0], "mempool") == 0) { + cmd_mempool(softnic, tokens, n_tokens, out, out_size); + return; + } + + if (strcmp(tokens[0], "link") == 0) { + cmd_link(softnic, tokens, n_tokens, out, out_size); + return; + } + + if (strcmp(tokens[0], "swq") == 0) { + cmd_swq(softnic, tokens, n_tokens, out, out_size); + return; + } + + if (strcmp(tokens[0], "tap") == 0) { + cmd_tap(softnic, tokens, n_tokens, out, out_size); + return; + } + + if (strcmp(tokens[0], "port") == 0) { + cmd_port_in_action_profile(softnic, tokens, n_tokens, out, out_size); + return; + } + + if (strcmp(tokens[0], "table") == 0) { + cmd_table_action_profile(softnic, tokens, n_tokens, out, out_size); + return; + } + + if (strcmp(tokens[0], "pipeline") == 0) { + if (n_tokens >= 3 && + (strcmp(tokens[2], "period") == 0)) { + cmd_pipeline(softnic, tokens, n_tokens, out, out_size); + return; + } + + if (n_tokens >= 5 && + (strcmp(tokens[2], "port") == 0) && + (strcmp(tokens[3], "in") == 0) && + (strcmp(tokens[4], "bsz") == 0)) { + cmd_pipeline_port_in(softnic, tokens, n_tokens, out, out_size); + return; + } + + if (n_tokens >= 5 && + (strcmp(tokens[2], "port") == 0) && + (strcmp(tokens[3], "out") == 0) && + (strcmp(tokens[4], "bsz") == 0)) { + cmd_pipeline_port_out(softnic, tokens, n_tokens, out, out_size); + return; + } + + if (n_tokens >= 4 && + (strcmp(tokens[2], "table") == 0) && + (strcmp(tokens[3], "match") == 0)) { + cmd_pipeline_table(softnic, tokens, n_tokens, out, out_size); + return; + } + + if (n_tokens >= 6 && + (strcmp(tokens[2], "port") == 0) && + (strcmp(tokens[3], "in") == 0) && + (strcmp(tokens[5], "table") == 0)) { + cmd_pipeline_port_in_table(softnic, tokens, n_tokens, + out, out_size); + return; + } + + if (n_tokens >= 6 && + (strcmp(tokens[2], "port") == 0) && + (strcmp(tokens[3], "in") == 0) && + (strcmp(tokens[5], "enable") == 0)) { + cmd_softnic_pipeline_port_in_enable(softnic, tokens, n_tokens, + out, out_size); + return; + } + + if (n_tokens >= 6 && + (strcmp(tokens[2], "port") == 0) && + (strcmp(tokens[3], "in") == 0) && + (strcmp(tokens[5], "disable") == 0)) { + cmd_softnic_pipeline_port_in_disable(softnic, tokens, n_tokens, + out, out_size); + return; + } + + if (n_tokens >= 7 && + (strcmp(tokens[2], "table") == 0) && + (strcmp(tokens[4], "rule") == 0) && + (strcmp(tokens[5], "add") == 0) && + (strcmp(tokens[6], "match") == 0)) { + if (n_tokens >= 8 && + (strcmp(tokens[7], "default") == 0)) { + cmd_softnic_pipeline_table_rule_add_default(softnic, tokens, + n_tokens, out, out_size); + return; + } + + cmd_softnic_pipeline_table_rule_add(softnic, tokens, n_tokens, + out, out_size); + return; + } + + if (n_tokens >= 7 && + (strcmp(tokens[2], "table") == 0) && + (strcmp(tokens[4], "rule") == 0) && + (strcmp(tokens[5], "add") == 0) && + (strcmp(tokens[6], "bulk") == 0)) { + cmd_softnic_pipeline_table_rule_add_bulk(softnic, tokens, + n_tokens, out, out_size); + return; + } + + if (n_tokens >= 7 && + (strcmp(tokens[2], "table") == 0) && + (strcmp(tokens[4], "rule") == 0) && + (strcmp(tokens[5], "delete") == 0) && + (strcmp(tokens[6], "match") == 0)) { + if (n_tokens >= 8 && + (strcmp(tokens[7], "default") == 0)) { + cmd_softnic_pipeline_table_rule_delete_default(softnic, tokens, + n_tokens, out, out_size); + return; + } + + cmd_softnic_pipeline_table_rule_delete(softnic, tokens, n_tokens, + out, out_size); + return; + } + } + + if (strcmp(tokens[0], "thread") == 0) { + if (n_tokens >= 5 && + (strcmp(tokens[4], "enable") == 0)) { + cmd_softnic_thread_pipeline_enable(softnic, tokens, n_tokens, + out, out_size); + return; + } + + if (n_tokens >= 5 && + (strcmp(tokens[4], "disable") == 0)) { + cmd_softnic_thread_pipeline_disable(softnic, tokens, n_tokens, + out, out_size); + return; + } + } + + snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]); +} + +int +softnic_cli_script_process(struct pmd_internals *softnic, + const char *file_name, + size_t msg_in_len_max, + size_t msg_out_len_max) +{ + char *msg_in = NULL, *msg_out = NULL; + FILE *f = NULL; + + /* Check input arguments */ + if (file_name == NULL || + (strlen(file_name) == 0) || + msg_in_len_max == 0 || + msg_out_len_max == 0) + return -EINVAL; + + msg_in = malloc(msg_in_len_max + 1); + msg_out = malloc(msg_out_len_max + 1); + if (msg_in == NULL || + msg_out == NULL) { + free(msg_out); + free(msg_in); + return -ENOMEM; + } + + /* Open input file */ + f = fopen(file_name, "r"); + if (f == NULL) { + free(msg_out); + free(msg_in); + return -EIO; + } + + /* Read file */ + for ( ; ; ) { + if (fgets(msg_in, msg_in_len_max + 1, f) == NULL) + break; + + printf("%s", msg_in); + msg_out[0] = 0; + + softnic_cli_process(msg_in, + msg_out, + msg_out_len_max, + softnic); + + if (strlen(msg_out)) + printf("%s", msg_out); + } + + /* Close file */ + fclose(f); + free(msg_out); + free(msg_in); + return 0; +} + +static int +cli_rule_file_process(const char *file_name, + size_t line_len_max, + struct softnic_table_rule_match *m, + struct softnic_table_rule_action *a, + uint32_t *n_rules, + uint32_t *line_number, + char *out, + size_t out_size) +{ + FILE *f = NULL; + char *line = NULL; + uint32_t rule_id, line_id; + int status = 0; + + /* Check input arguments */ + if (file_name == NULL || + (strlen(file_name) == 0) || + line_len_max == 0) { + *line_number = 0; + return -EINVAL; + } + + /* Memory allocation */ + line = malloc(line_len_max + 1); + if (line == NULL) { + *line_number = 0; + return -ENOMEM; + } + + /* Open file */ + f = fopen(file_name, "r"); + if (f == NULL) { + *line_number = 0; + free(line); + return -EIO; + } + + /* Read file */ + for (line_id = 1, rule_id = 0; rule_id < *n_rules; line_id++) { + char *tokens[CMD_MAX_TOKENS]; + uint32_t n_tokens, n_tokens_parsed, t0; + + /* Read next line from file. */ + if (fgets(line, line_len_max + 1, f) == NULL) + break; + + /* Comment. */ + if (is_comment(line)) + continue; + + /* Parse line. */ + n_tokens = RTE_DIM(tokens); + status = softnic_parse_tokenize_string(line, tokens, &n_tokens); + if (status) { + status = -EINVAL; + break; + } + + /* Empty line. */ + if (n_tokens == 0) + continue; + t0 = 0; + + /* Rule match. */ + n_tokens_parsed = parse_match(tokens + t0, + n_tokens - t0, + out, + out_size, + &m[rule_id]); + if (n_tokens_parsed == 0) { + status = -EINVAL; + break; + } + t0 += n_tokens_parsed; + + /* Rule action. */ + n_tokens_parsed = parse_table_action(tokens + t0, + n_tokens - t0, + out, + out_size, + &a[rule_id]); + if (n_tokens_parsed == 0) { + status = -EINVAL; + break; + } + t0 += n_tokens_parsed; + + /* Line completed. */ + if (t0 < n_tokens) { + status = -EINVAL; + break; + } + + /* Increment rule count */ + rule_id++; + } + + /* Close file */ + fclose(f); + + /* Memory free */ + free(line); + + *n_rules = rule_id; + *line_number = line_id; + return status; } diff --git a/drivers/net/softnic/rte_eth_softnic_internals.h b/drivers/net/softnic/rte_eth_softnic_internals.h index 8163487..6593b30 100644 --- a/drivers/net/softnic/rte_eth_softnic_internals.h +++ b/drivers/net/softnic/rte_eth_softnic_internals.h @@ -774,6 +774,41 @@ softnic_pipeline_port_in_disable(struct pmd_internals *p, const char *pipeline_name, uint32_t port_id); +int +softnic_pipeline_table_rule_add(struct pmd_internals *p, + const char *pipeline_name, + uint32_t table_id, + struct softnic_table_rule_match *match, + struct softnic_table_rule_action *action, + void **data); + +int +softnic_pipeline_table_rule_add_bulk(struct pmd_internals *p, + const char *pipeline_name, + uint32_t table_id, + struct softnic_table_rule_match *match, + struct softnic_table_rule_action *action, + void **data, + uint32_t *n_rules); + +int +softnic_pipeline_table_rule_add_default(struct pmd_internals *p, + const char *pipeline_name, + uint32_t table_id, + struct softnic_table_rule_action *action, + void **data); + +int +softnic_pipeline_table_rule_delete(struct pmd_internals *p, + const char *pipeline_name, + uint32_t table_id, + struct softnic_table_rule_match *match); + +int +softnic_pipeline_table_rule_delete_default(struct pmd_internals *p, + const char *pipeline_name, + uint32_t table_id); + /** * Thread */ diff --git a/drivers/net/softnic/rte_eth_softnic_thread.c b/drivers/net/softnic/rte_eth_softnic_thread.c index 5341fbf..07cbf97 100644 --- a/drivers/net/softnic/rte_eth_softnic_thread.c +++ b/drivers/net/softnic/rte_eth_softnic_thread.c @@ -4,10 +4,16 @@ #include <stdlib.h> +#include <rte_common.h> #include <rte_cycles.h> #include <rte_lcore.h> #include <rte_ring.h> +#include <rte_table_acl.h> +#include <rte_table_array.h> +#include <rte_table_hash.h> +#include <rte_table_lpm.h> +#include <rte_table_lpm_ipv6.h> #include "rte_eth_softnic_internals.h" /** @@ -504,16 +510,71 @@ enum pipeline_req_type { PIPELINE_REQ_PORT_IN_ENABLE, PIPELINE_REQ_PORT_IN_DISABLE, + /* Table */ + PIPELINE_REQ_TABLE_RULE_ADD, + PIPELINE_REQ_TABLE_RULE_ADD_DEFAULT, + PIPELINE_REQ_TABLE_RULE_ADD_BULK, + PIPELINE_REQ_TABLE_RULE_DELETE, + PIPELINE_REQ_TABLE_RULE_DELETE_DEFAULT, + PIPELINE_REQ_MAX }; +struct pipeline_msg_req_table_rule_add { + struct softnic_table_rule_match match; + struct softnic_table_rule_action action; +}; + +struct pipeline_msg_req_table_rule_add_default { + struct softnic_table_rule_action action; +}; + +struct pipeline_msg_req_table_rule_add_bulk { + struct softnic_table_rule_match *match; + struct softnic_table_rule_action *action; + void **data; + uint32_t n_rules; + int bulk; +}; + +struct pipeline_msg_req_table_rule_delete { + struct softnic_table_rule_match match; +}; + struct pipeline_msg_req { enum pipeline_req_type type; uint32_t id; /* Port IN, port OUT or table ID */ + + RTE_STD_C11 + union { + struct pipeline_msg_req_table_rule_add table_rule_add; + struct pipeline_msg_req_table_rule_add_default table_rule_add_default; + struct pipeline_msg_req_table_rule_add_bulk table_rule_add_bulk; + struct pipeline_msg_req_table_rule_delete table_rule_delete; + }; +}; + +struct pipeline_msg_rsp_table_rule_add { + void *data; +}; + +struct pipeline_msg_rsp_table_rule_add_default { + void *data; +}; + +struct pipeline_msg_rsp_table_rule_add_bulk { + uint32_t n_rules; }; struct pipeline_msg_rsp { int status; + + RTE_STD_C11 + union { + struct pipeline_msg_rsp_table_rule_add table_rule_add; + struct pipeline_msg_rsp_table_rule_add_default table_rule_add_default; + struct pipeline_msg_rsp_table_rule_add_bulk table_rule_add_bulk; + }; }; /** @@ -650,6 +711,641 @@ softnic_pipeline_port_in_disable(struct pmd_internals *softnic, return status; } +static int +match_check(struct softnic_table_rule_match *match, + struct pipeline *p, + uint32_t table_id) +{ + struct softnic_table *table; + + if (match == NULL || + p == NULL || + table_id >= p->n_tables) + return -1; + + table = &p->table[table_id]; + if (match->match_type != table->params.match_type) + return -1; + + switch (match->match_type) { + case TABLE_ACL: + { + struct softnic_table_acl_params *t = &table->params.match.acl; + struct softnic_table_rule_match_acl *r = &match->match.acl; + + if ((r->ip_version && (t->ip_version == 0)) || + ((r->ip_version == 0) && t->ip_version)) + return -1; + + if (r->ip_version) { + if (r->sa_depth > 32 || + r->da_depth > 32) + return -1; + } else { + if (r->sa_depth > 128 || + r->da_depth > 128) + return -1; + } + return 0; + } + + case TABLE_ARRAY: + return 0; + + case TABLE_HASH: + return 0; + + case TABLE_LPM: + { + struct softnic_table_lpm_params *t = &table->params.match.lpm; + struct softnic_table_rule_match_lpm *r = &match->match.lpm; + + if ((r->ip_version && (t->key_size != 4)) || + ((r->ip_version == 0) && (t->key_size != 16))) + return -1; + + if (r->ip_version) { + if (r->depth > 32) + return -1; + } else { + if (r->depth > 128) + return -1; + } + return 0; + } + + case TABLE_STUB: + return -1; + + default: + return -1; + } +} + +static int +action_check(struct softnic_table_rule_action *action, + struct pipeline *p, + uint32_t table_id) +{ + struct softnic_table_action_profile *ap; + + if (action == NULL || + p == NULL || + table_id >= p->n_tables) + return -1; + + ap = p->table[table_id].ap; + if (action->action_mask != ap->params.action_mask) + return -1; + + if (action->action_mask & (1LLU << RTE_TABLE_ACTION_FWD)) { + if (action->fwd.action == RTE_PIPELINE_ACTION_PORT && + action->fwd.id >= p->n_ports_out) + return -1; + + if (action->fwd.action == RTE_PIPELINE_ACTION_TABLE && + action->fwd.id >= p->n_tables) + return -1; + } + + if (action->action_mask & (1LLU << RTE_TABLE_ACTION_MTR)) { + uint32_t tc_mask0 = (1 << ap->params.mtr.n_tc) - 1; + uint32_t tc_mask1 = action->mtr.tc_mask; + + if (tc_mask1 != tc_mask0) + return -1; + } + + if (action->action_mask & (1LLU << RTE_TABLE_ACTION_TM)) { + uint32_t n_subports_per_port = + ap->params.tm.n_subports_per_port; + uint32_t n_pipes_per_subport = + ap->params.tm.n_pipes_per_subport; + uint32_t subport_id = action->tm.subport_id; + uint32_t pipe_id = action->tm.pipe_id; + + if (subport_id >= n_subports_per_port || + pipe_id >= n_pipes_per_subport) + return -1; + } + + if (action->action_mask & (1LLU << RTE_TABLE_ACTION_ENCAP)) { + uint64_t encap_mask = ap->params.encap.encap_mask; + enum rte_table_action_encap_type type = action->encap.type; + + if ((encap_mask & (1LLU << type)) == 0) + return -1; + } + + if (action->action_mask & (1LLU << RTE_TABLE_ACTION_NAT)) { + int ip_version0 = ap->params.common.ip_version; + int ip_version1 = action->nat.ip_version; + + if ((ip_version1 && (ip_version0 == 0)) || + ((ip_version1 == 0) && ip_version0)) + return -1; + } + + return 0; +} + +static int +action_default_check(struct softnic_table_rule_action *action, + struct pipeline *p, + uint32_t table_id) +{ + if (action == NULL || + action->action_mask != (1LLU << RTE_TABLE_ACTION_FWD) || + p == NULL || + table_id >= p->n_tables) + return -1; + + if (action->action_mask & (1LLU << RTE_TABLE_ACTION_FWD)) { + if (action->fwd.action == RTE_PIPELINE_ACTION_PORT && + action->fwd.id >= p->n_ports_out) + return -1; + + if (action->fwd.action == RTE_PIPELINE_ACTION_TABLE && + action->fwd.id >= p->n_tables) + return -1; + } + + return 0; +} + +union table_rule_match_low_level { + struct rte_table_acl_rule_add_params acl_add; + struct rte_table_acl_rule_delete_params acl_delete; + struct rte_table_array_key array; + uint8_t hash[TABLE_RULE_MATCH_SIZE_MAX]; + struct rte_table_lpm_key lpm_ipv4; + struct rte_table_lpm_ipv6_key lpm_ipv6; +}; + +static int +match_convert(struct softnic_table_rule_match *mh, + union table_rule_match_low_level *ml, + int add); + +static int +action_convert(struct rte_table_action *a, + struct softnic_table_rule_action *action, + struct rte_pipeline_table_entry *data); + +int +softnic_pipeline_table_rule_add(struct pmd_internals *softnic, + const char *pipeline_name, + uint32_t table_id, + struct softnic_table_rule_match *match, + struct softnic_table_rule_action *action, + void **data) +{ + struct pipeline *p; + struct pipeline_msg_req *req; + struct pipeline_msg_rsp *rsp; + int status; + + /* Check input params */ + if (pipeline_name == NULL || + match == NULL || + action == NULL || + data == NULL) + return -1; + + p = softnic_pipeline_find(softnic, pipeline_name); + if (p == NULL || + table_id >= p->n_tables || + match_check(match, p, table_id) || + action_check(action, p, table_id)) + return -1; + + if (!pipeline_is_running(p)) { + struct rte_table_action *a = p->table[table_id].a; + union table_rule_match_low_level match_ll; + struct rte_pipeline_table_entry *data_in, *data_out; + int key_found; + uint8_t *buffer; + + buffer = calloc(TABLE_RULE_ACTION_SIZE_MAX, sizeof(uint8_t)); + if (buffer == NULL) + return -1; + + /* Table match-action rule conversion */ + data_in = (struct rte_pipeline_table_entry *)buffer; + + status = match_convert(match, &match_ll, 1); + if (status) { + free(buffer); + return -1; + } + + status = action_convert(a, action, data_in); + if (status) { + free(buffer); + return -1; + } + + /* Add rule (match, action) to table */ + status = rte_pipeline_table_entry_add(p->p, + table_id, + &match_ll, + data_in, + &key_found, + &data_out); + if (status) { + free(buffer); + return -1; + } + + /* Write Response */ + *data = data_out; + + free(buffer); + return 0; + } + + /* Allocate request */ + req = pipeline_msg_alloc(); + if (req == NULL) + return -1; + + /* Write request */ + req->type = PIPELINE_REQ_TABLE_RULE_ADD; + req->id = table_id; + memcpy(&req->table_rule_add.match, match, sizeof(*match)); + memcpy(&req->table_rule_add.action, action, sizeof(*action)); + + /* Send request and wait for response */ + rsp = pipeline_msg_send_recv(p, req); + if (rsp == NULL) + return -1; + + /* Read response */ + status = rsp->status; + if (status == 0) + *data = rsp->table_rule_add.data; + + /* Free response */ + pipeline_msg_free(rsp); + + return status; +} + +int +softnic_pipeline_table_rule_add_default(struct pmd_internals *softnic, + const char *pipeline_name, + uint32_t table_id, + struct softnic_table_rule_action *action, + void **data) +{ + struct pipeline *p; + struct pipeline_msg_req *req; + struct pipeline_msg_rsp *rsp; + int status; + + /* Check input params */ + if (pipeline_name == NULL || + action == NULL || + data == NULL) + return -1; + + p = softnic_pipeline_find(softnic, pipeline_name); + if (p == NULL || + table_id >= p->n_tables || + action_default_check(action, p, table_id)) + return -1; + + if (!pipeline_is_running(p)) { + struct rte_pipeline_table_entry *data_in, *data_out; + uint8_t *buffer; + + buffer = calloc(TABLE_RULE_ACTION_SIZE_MAX, sizeof(uint8_t)); + if (buffer == NULL) + return -1; + + /* Apply actions */ + data_in = (struct rte_pipeline_table_entry *)buffer; + + data_in->action = action->fwd.action; + if (action->fwd.action == RTE_PIPELINE_ACTION_PORT) + data_in->port_id = action->fwd.id; + if (action->fwd.action == RTE_PIPELINE_ACTION_TABLE) + data_in->table_id = action->fwd.id; + + /* Add default rule to table */ + status = rte_pipeline_table_default_entry_add(p->p, + table_id, + data_in, + &data_out); + if (status) { + free(buffer); + return -1; + } + + /* Write Response */ + *data = data_out; + + free(buffer); + return 0; + } + + /* Allocate request */ + req = pipeline_msg_alloc(); + if (req == NULL) + return -1; + + /* Write request */ + req->type = PIPELINE_REQ_TABLE_RULE_ADD_DEFAULT; + req->id = table_id; + memcpy(&req->table_rule_add_default.action, action, sizeof(*action)); + + /* Send request and wait for response */ + rsp = pipeline_msg_send_recv(p, req); + if (rsp == NULL) + return -1; + + /* Read response */ + status = rsp->status; + if (status == 0) + *data = rsp->table_rule_add_default.data; + + /* Free response */ + pipeline_msg_free(rsp); + + return status; +} + +int +softnic_pipeline_table_rule_add_bulk(struct pmd_internals *softnic, + const char *pipeline_name, + uint32_t table_id, + struct softnic_table_rule_match *match, + struct softnic_table_rule_action *action, + void **data, + uint32_t *n_rules) +{ + struct pipeline *p; + struct pipeline_msg_req *req; + struct pipeline_msg_rsp *rsp; + uint32_t i; + int status; + + /* Check input params */ + if (pipeline_name == NULL || + match == NULL || + action == NULL || + data == NULL || + n_rules == NULL || + (*n_rules == 0)) + return -1; + + p = softnic_pipeline_find(softnic, pipeline_name); + if (p == NULL || + table_id >= p->n_tables) + return -1; + + for (i = 0; i < *n_rules; i++) + if (match_check(match, p, table_id) || + action_check(action, p, table_id)) + return -1; + + if (!pipeline_is_running(p)) { + struct rte_table_action *a = p->table[table_id].a; + union table_rule_match_low_level *match_ll; + uint8_t *action_ll; + void **match_ll_ptr; + struct rte_pipeline_table_entry **action_ll_ptr; + struct rte_pipeline_table_entry **entries_ptr = + (struct rte_pipeline_table_entry **)data; + uint32_t bulk = + (p->table[table_id].params.match_type == TABLE_ACL) ? 1 : 0; + int *found; + + /* Memory allocation */ + match_ll = calloc(*n_rules, sizeof(union table_rule_match_low_level)); + action_ll = calloc(*n_rules, TABLE_RULE_ACTION_SIZE_MAX); + match_ll_ptr = calloc(*n_rules, sizeof(void *)); + action_ll_ptr = + calloc(*n_rules, sizeof(struct rte_pipeline_table_entry *)); + found = calloc(*n_rules, sizeof(int)); + + if (match_ll == NULL || + action_ll == NULL || + match_ll_ptr == NULL || + action_ll_ptr == NULL || + found == NULL) + goto fail; + + for (i = 0; i < *n_rules; i++) { + match_ll_ptr[i] = (void *)&match_ll[i]; + action_ll_ptr[i] = + (struct rte_pipeline_table_entry *)&action_ll[i * TABLE_RULE_ACTION_SIZE_MAX]; + } + + /* Rule match conversion */ + for (i = 0; i < *n_rules; i++) { + status = match_convert(&match[i], match_ll_ptr[i], 1); + if (status) + goto fail; + } + + /* Rule action conversion */ + for (i = 0; i < *n_rules; i++) { + status = action_convert(a, &action[i], action_ll_ptr[i]); + if (status) + goto fail; + } + + /* Add rule (match, action) to table */ + if (bulk) { + status = rte_pipeline_table_entry_add_bulk(p->p, + table_id, + match_ll_ptr, + action_ll_ptr, + *n_rules, + found, + entries_ptr); + if (status) + *n_rules = 0; + } else { + for (i = 0; i < *n_rules; i++) { + status = rte_pipeline_table_entry_add(p->p, + table_id, + match_ll_ptr[i], + action_ll_ptr[i], + &found[i], + &entries_ptr[i]); + if (status) { + *n_rules = i; + break; + } + } + } + + /* Free */ + free(found); + free(action_ll_ptr); + free(match_ll_ptr); + free(action_ll); + free(match_ll); + + return status; + +fail: + free(found); + free(action_ll_ptr); + free(match_ll_ptr); + free(action_ll); + free(match_ll); + + *n_rules = 0; + return -1; + } + + /* Allocate request */ + req = pipeline_msg_alloc(); + if (req == NULL) + return -1; + + /* Write request */ + req->type = PIPELINE_REQ_TABLE_RULE_ADD_BULK; + req->id = table_id; + req->table_rule_add_bulk.match = match; + req->table_rule_add_bulk.action = action; + req->table_rule_add_bulk.data = data; + req->table_rule_add_bulk.n_rules = *n_rules; + req->table_rule_add_bulk.bulk = + (p->table[table_id].params.match_type == TABLE_ACL) ? 1 : 0; + + /* Send request and wait for response */ + rsp = pipeline_msg_send_recv(p, req); + if (rsp == NULL) + return -1; + + /* Read response */ + status = rsp->status; + if (status == 0) + *n_rules = rsp->table_rule_add_bulk.n_rules; + + /* Free response */ + pipeline_msg_free(rsp); + + return status; +} + +int +softnic_pipeline_table_rule_delete(struct pmd_internals *softnic, + const char *pipeline_name, + uint32_t table_id, + struct softnic_table_rule_match *match) +{ + struct pipeline *p; + struct pipeline_msg_req *req; + struct pipeline_msg_rsp *rsp; + int status; + + /* Check input params */ + if (pipeline_name == NULL || + match == NULL) + return -1; + + p = softnic_pipeline_find(softnic, pipeline_name); + if (p == NULL || + table_id >= p->n_tables || + match_check(match, p, table_id)) + return -1; + + if (!pipeline_is_running(p)) { + union table_rule_match_low_level match_ll; + int key_found; + + status = match_convert(match, &match_ll, 0); + if (status) + return -1; + + status = rte_pipeline_table_entry_delete(p->p, + table_id, + &match_ll, + &key_found, + NULL); + + return status; + } + + /* Allocate request */ + req = pipeline_msg_alloc(); + if (req == NULL) + return -1; + + /* Write request */ + req->type = PIPELINE_REQ_TABLE_RULE_DELETE; + req->id = table_id; + memcpy(&req->table_rule_delete.match, match, sizeof(*match)); + + /* Send request and wait for response */ + rsp = pipeline_msg_send_recv(p, req); + if (rsp == NULL) + return -1; + + /* Read response */ + status = rsp->status; + + /* Free response */ + pipeline_msg_free(rsp); + + return status; +} + +int +softnic_pipeline_table_rule_delete_default(struct pmd_internals *softnic, + const char *pipeline_name, + uint32_t table_id) +{ + struct pipeline *p; + struct pipeline_msg_req *req; + struct pipeline_msg_rsp *rsp; + int status; + + /* Check input params */ + if (pipeline_name == NULL) + return -1; + + p = softnic_pipeline_find(softnic, pipeline_name); + if (p == NULL || + table_id >= p->n_tables) + return -1; + + if (!pipeline_is_running(p)) { + status = rte_pipeline_table_default_entry_delete(p->p, + table_id, + NULL); + + return status; + } + + /* Allocate request */ + req = pipeline_msg_alloc(); + if (req == NULL) + return -1; + + /* Write request */ + req->type = PIPELINE_REQ_TABLE_RULE_DELETE_DEFAULT; + req->id = table_id; + + /* Send request and wait for response */ + rsp = pipeline_msg_send_recv(p, req); + if (rsp == NULL) + return -1; + + /* Read response */ + status = rsp->status; + + /* Free response */ + pipeline_msg_free(rsp); + + return status; +} + /** * Data plane threads: message handling */ @@ -703,6 +1399,606 @@ pipeline_msg_handle_port_in_disable(struct pipeline_data *p, return rsp; } +static int +match_convert_ipv6_depth(uint32_t depth, uint32_t *depth32) +{ + if (depth > 128) + return -1; + + switch (depth / 32) { + case 0: + depth32[0] = depth; + depth32[1] = 0; + depth32[2] = 0; + depth32[3] = 0; + return 0; + + case 1: + depth32[0] = 32; + depth32[1] = depth - 32; + depth32[2] = 0; + depth32[3] = 0; + return 0; + + case 2: + depth32[0] = 32; + depth32[1] = 32; + depth32[2] = depth - 64; + depth32[3] = 0; + return 0; + + case 3: + depth32[0] = 32; + depth32[1] = 32; + depth32[2] = 32; + depth32[3] = depth - 96; + return 0; + + case 4: + depth32[0] = 32; + depth32[1] = 32; + depth32[2] = 32; + depth32[3] = 32; + return 0; + + default: + return -1; + } +} + +static int +match_convert(struct softnic_table_rule_match *mh, + union table_rule_match_low_level *ml, + int add) +{ + memset(ml, 0, sizeof(*ml)); + + switch (mh->match_type) { + case TABLE_ACL: + if (mh->match.acl.ip_version) + if (add) { + ml->acl_add.field_value[0].value.u8 = + mh->match.acl.proto; + ml->acl_add.field_value[0].mask_range.u8 = + mh->match.acl.proto_mask; + + ml->acl_add.field_value[1].value.u32 = + mh->match.acl.ipv4.sa; + ml->acl_add.field_value[1].mask_range.u32 = + mh->match.acl.sa_depth; + + ml->acl_add.field_value[2].value.u32 = + mh->match.acl.ipv4.da; + ml->acl_add.field_value[2].mask_range.u32 = + mh->match.acl.da_depth; + + ml->acl_add.field_value[3].value.u16 = + mh->match.acl.sp0; + ml->acl_add.field_value[3].mask_range.u16 = + mh->match.acl.sp1; + + ml->acl_add.field_value[4].value.u16 = + mh->match.acl.dp0; + ml->acl_add.field_value[4].mask_range.u16 = + mh->match.acl.dp1; + + ml->acl_add.priority = + (int32_t)mh->match.acl.priority; + } else { + ml->acl_delete.field_value[0].value.u8 = + mh->match.acl.proto; + ml->acl_delete.field_value[0].mask_range.u8 = + mh->match.acl.proto_mask; + + ml->acl_delete.field_value[1].value.u32 = + mh->match.acl.ipv4.sa; + ml->acl_delete.field_value[1].mask_range.u32 = + mh->match.acl.sa_depth; + + ml->acl_delete.field_value[2].value.u32 = + mh->match.acl.ipv4.da; + ml->acl_delete.field_value[2].mask_range.u32 = + mh->match.acl.da_depth; + + ml->acl_delete.field_value[3].value.u16 = + mh->match.acl.sp0; + ml->acl_delete.field_value[3].mask_range.u16 = + mh->match.acl.sp1; + + ml->acl_delete.field_value[4].value.u16 = + mh->match.acl.dp0; + ml->acl_delete.field_value[4].mask_range.u16 = + mh->match.acl.dp1; + } + else + if (add) { + uint32_t *sa32 = + (uint32_t *)mh->match.acl.ipv6.sa; + uint32_t *da32 = + (uint32_t *)mh->match.acl.ipv6.da; + uint32_t sa32_depth[4], da32_depth[4]; + int status; + + status = match_convert_ipv6_depth(mh->match.acl.sa_depth, + sa32_depth); + if (status) + return status; + + status = match_convert_ipv6_depth( + mh->match.acl.da_depth, + da32_depth); + if (status) + return status; + + ml->acl_add.field_value[0].value.u8 = + mh->match.acl.proto; + ml->acl_add.field_value[0].mask_range.u8 = + mh->match.acl.proto_mask; + + ml->acl_add.field_value[1].value.u32 = sa32[0]; + ml->acl_add.field_value[1].mask_range.u32 = + sa32_depth[0]; + ml->acl_add.field_value[2].value.u32 = sa32[1]; + ml->acl_add.field_value[2].mask_range.u32 = + sa32_depth[1]; + ml->acl_add.field_value[3].value.u32 = sa32[2]; + ml->acl_add.field_value[3].mask_range.u32 = + sa32_depth[2]; + ml->acl_add.field_value[4].value.u32 = sa32[3]; + ml->acl_add.field_value[4].mask_range.u32 = + sa32_depth[3]; + + ml->acl_add.field_value[5].value.u32 = da32[0]; + ml->acl_add.field_value[5].mask_range.u32 = + da32_depth[0]; + ml->acl_add.field_value[6].value.u32 = da32[1]; + ml->acl_add.field_value[6].mask_range.u32 = + da32_depth[1]; + ml->acl_add.field_value[7].value.u32 = da32[2]; + ml->acl_add.field_value[7].mask_range.u32 = + da32_depth[2]; + ml->acl_add.field_value[8].value.u32 = da32[3]; + ml->acl_add.field_value[8].mask_range.u32 = + da32_depth[3]; + + ml->acl_add.field_value[9].value.u16 = + mh->match.acl.sp0; + ml->acl_add.field_value[9].mask_range.u16 = + mh->match.acl.sp1; + + ml->acl_add.field_value[10].value.u16 = + mh->match.acl.dp0; + ml->acl_add.field_value[10].mask_range.u16 = + mh->match.acl.dp1; + + ml->acl_add.priority = + (int32_t)mh->match.acl.priority; + } else { + uint32_t *sa32 = + (uint32_t *)mh->match.acl.ipv6.sa; + uint32_t *da32 = + (uint32_t *)mh->match.acl.ipv6.da; + uint32_t sa32_depth[4], da32_depth[4]; + int status; + + status = match_convert_ipv6_depth(mh->match.acl.sa_depth, + sa32_depth); + if (status) + return status; + + status = match_convert_ipv6_depth(mh->match.acl.da_depth, + da32_depth); + if (status) + return status; + + ml->acl_delete.field_value[0].value.u8 = + mh->match.acl.proto; + ml->acl_delete.field_value[0].mask_range.u8 = + mh->match.acl.proto_mask; + + ml->acl_delete.field_value[1].value.u32 = + sa32[0]; + ml->acl_delete.field_value[1].mask_range.u32 = + sa32_depth[0]; + ml->acl_delete.field_value[2].value.u32 = + sa32[1]; + ml->acl_delete.field_value[2].mask_range.u32 = + sa32_depth[1]; + ml->acl_delete.field_value[3].value.u32 = + sa32[2]; + ml->acl_delete.field_value[3].mask_range.u32 = + sa32_depth[2]; + ml->acl_delete.field_value[4].value.u32 = + sa32[3]; + ml->acl_delete.field_value[4].mask_range.u32 = + sa32_depth[3]; + + ml->acl_delete.field_value[5].value.u32 = + da32[0]; + ml->acl_delete.field_value[5].mask_range.u32 = + da32_depth[0]; + ml->acl_delete.field_value[6].value.u32 = + da32[1]; + ml->acl_delete.field_value[6].mask_range.u32 = + da32_depth[1]; + ml->acl_delete.field_value[7].value.u32 = + da32[2]; + ml->acl_delete.field_value[7].mask_range.u32 = + da32_depth[2]; + ml->acl_delete.field_value[8].value.u32 = + da32[3]; + ml->acl_delete.field_value[8].mask_range.u32 = + da32_depth[3]; + + ml->acl_delete.field_value[9].value.u16 = + mh->match.acl.sp0; + ml->acl_delete.field_value[9].mask_range.u16 = + mh->match.acl.sp1; + + ml->acl_delete.field_value[10].value.u16 = + mh->match.acl.dp0; + ml->acl_delete.field_value[10].mask_range.u16 = + mh->match.acl.dp1; + } + return 0; + + case TABLE_ARRAY: + ml->array.pos = mh->match.array.pos; + return 0; + + case TABLE_HASH: + memcpy(ml->hash, mh->match.hash.key, sizeof(ml->hash)); + return 0; + + case TABLE_LPM: + if (mh->match.lpm.ip_version) { + ml->lpm_ipv4.ip = mh->match.lpm.ipv4; + ml->lpm_ipv4.depth = mh->match.lpm.depth; + } else { + memcpy(ml->lpm_ipv6.ip, + mh->match.lpm.ipv6, sizeof(ml->lpm_ipv6.ip)); + ml->lpm_ipv6.depth = mh->match.lpm.depth; + } + + return 0; + + default: + return -1; + } +} + +static int +action_convert(struct rte_table_action *a, + struct softnic_table_rule_action *action, + struct rte_pipeline_table_entry *data) +{ + int status; + + /* Apply actions */ + if (action->action_mask & (1LLU << RTE_TABLE_ACTION_FWD)) { + status = rte_table_action_apply(a, + data, + RTE_TABLE_ACTION_FWD, + &action->fwd); + + if (status) + return status; + } + + if (action->action_mask & (1LLU << RTE_TABLE_ACTION_LB)) { + status = rte_table_action_apply(a, + data, + RTE_TABLE_ACTION_LB, + &action->lb); + + if (status) + return status; + } + + if (action->action_mask & (1LLU << RTE_TABLE_ACTION_MTR)) { + status = rte_table_action_apply(a, + data, + RTE_TABLE_ACTION_MTR, + &action->mtr); + + if (status) + return status; + } + + if (action->action_mask & (1LLU << RTE_TABLE_ACTION_TM)) { + status = rte_table_action_apply(a, + data, + RTE_TABLE_ACTION_TM, + &action->tm); + + if (status) + return status; + } + + if (action->action_mask & (1LLU << RTE_TABLE_ACTION_ENCAP)) { + status = rte_table_action_apply(a, + data, + RTE_TABLE_ACTION_ENCAP, + &action->encap); + + if (status) + return status; + } + + if (action->action_mask & (1LLU << RTE_TABLE_ACTION_NAT)) { + status = rte_table_action_apply(a, + data, + RTE_TABLE_ACTION_NAT, + &action->nat); + + if (status) + return status; + } + + if (action->action_mask & (1LLU << RTE_TABLE_ACTION_TTL)) { + status = rte_table_action_apply(a, + data, + RTE_TABLE_ACTION_TTL, + &action->ttl); + + if (status) + return status; + } + + if (action->action_mask & (1LLU << RTE_TABLE_ACTION_STATS)) { + status = rte_table_action_apply(a, + data, + RTE_TABLE_ACTION_STATS, + &action->stats); + + if (status) + return status; + } + + if (action->action_mask & (1LLU << RTE_TABLE_ACTION_TIME)) { + status = rte_table_action_apply(a, + data, + RTE_TABLE_ACTION_TIME, + &action->time); + + if (status) + return status; + } + + return 0; +} + +static struct pipeline_msg_rsp * +pipeline_msg_handle_table_rule_add(struct pipeline_data *p, + struct pipeline_msg_req *req) +{ + union table_rule_match_low_level match_ll; + struct pipeline_msg_rsp *rsp = (struct pipeline_msg_rsp *)req; + struct softnic_table_rule_match *match = &req->table_rule_add.match; + struct softnic_table_rule_action *action = &req->table_rule_add.action; + struct rte_pipeline_table_entry *data_in, *data_out; + uint32_t table_id = req->id; + int key_found, status; + struct rte_table_action *a = p->table_data[table_id].a; + + /* Apply actions */ + memset(p->buffer, 0, sizeof(p->buffer)); + data_in = (struct rte_pipeline_table_entry *)p->buffer; + + status = match_convert(match, &match_ll, 1); + if (status) { + rsp->status = -1; + return rsp; + } + + status = action_convert(a, action, data_in); + if (status) { + rsp->status = -1; + return rsp; + } + + status = rte_pipeline_table_entry_add(p->p, + table_id, + &match_ll, + data_in, + &key_found, + &data_out); + if (status) { + rsp->status = -1; + return rsp; + } + + /* Write response */ + rsp->status = 0; + rsp->table_rule_add.data = data_out; + + return rsp; +} + +static struct pipeline_msg_rsp * +pipeline_msg_handle_table_rule_add_default(struct pipeline_data *p, + struct pipeline_msg_req *req) +{ + struct pipeline_msg_rsp *rsp = (struct pipeline_msg_rsp *)req; + struct softnic_table_rule_action *action = &req->table_rule_add_default.action; + struct rte_pipeline_table_entry *data_in, *data_out; + uint32_t table_id = req->id; + int status; + + /* Apply actions */ + memset(p->buffer, 0, sizeof(p->buffer)); + data_in = (struct rte_pipeline_table_entry *)p->buffer; + + data_in->action = action->fwd.action; + if (action->fwd.action == RTE_PIPELINE_ACTION_PORT) + data_in->port_id = action->fwd.id; + if (action->fwd.action == RTE_PIPELINE_ACTION_TABLE) + data_in->table_id = action->fwd.id; + + /* Add default rule to table */ + status = rte_pipeline_table_default_entry_add(p->p, + table_id, + data_in, + &data_out); + if (status) { + rsp->status = -1; + return rsp; + } + + /* Write response */ + rsp->status = 0; + rsp->table_rule_add_default.data = data_out; + + return rsp; +} + +static struct pipeline_msg_rsp * +pipeline_msg_handle_table_rule_add_bulk(struct pipeline_data *p, + struct pipeline_msg_req *req) +{ + struct pipeline_msg_rsp *rsp = (struct pipeline_msg_rsp *)req; + + uint32_t table_id = req->id; + struct softnic_table_rule_match *match = req->table_rule_add_bulk.match; + struct softnic_table_rule_action *action = req->table_rule_add_bulk.action; + struct rte_pipeline_table_entry **data = + (struct rte_pipeline_table_entry **)req->table_rule_add_bulk.data; + uint32_t n_rules = req->table_rule_add_bulk.n_rules; + uint32_t bulk = req->table_rule_add_bulk.bulk; + + struct rte_table_action *a = p->table_data[table_id].a; + union table_rule_match_low_level *match_ll; + uint8_t *action_ll; + void **match_ll_ptr; + struct rte_pipeline_table_entry **action_ll_ptr; + int *found, status; + uint32_t i; + + /* Memory allocation */ + match_ll = calloc(n_rules, sizeof(union table_rule_match_low_level)); + action_ll = calloc(n_rules, TABLE_RULE_ACTION_SIZE_MAX); + match_ll_ptr = calloc(n_rules, sizeof(void *)); + action_ll_ptr = + calloc(n_rules, sizeof(struct rte_pipeline_table_entry *)); + found = calloc(n_rules, sizeof(int)); + + if (match_ll == NULL || + action_ll == NULL || + match_ll_ptr == NULL || + action_ll_ptr == NULL || + found == NULL) + goto fail; + + for (i = 0; i < n_rules; i++) { + match_ll_ptr[i] = (void *)&match_ll[i]; + action_ll_ptr[i] = + (struct rte_pipeline_table_entry *)&action_ll[i * TABLE_RULE_ACTION_SIZE_MAX]; + } + + /* Rule match conversion */ + for (i = 0; i < n_rules; i++) { + status = match_convert(&match[i], match_ll_ptr[i], 1); + if (status) + goto fail; + } + + /* Rule action conversion */ + for (i = 0; i < n_rules; i++) { + status = action_convert(a, &action[i], action_ll_ptr[i]); + if (status) + goto fail; + } + + /* Add rule (match, action) to table */ + if (bulk) { + status = rte_pipeline_table_entry_add_bulk(p->p, + table_id, + match_ll_ptr, + action_ll_ptr, + n_rules, + found, + data); + if (status) + n_rules = 0; + } else { + for (i = 0; i < n_rules; i++) { + status = rte_pipeline_table_entry_add(p->p, + table_id, + match_ll_ptr[i], + action_ll_ptr[i], + &found[i], + &data[i]); + if (status) { + n_rules = i; + break; + } + } + } + + /* Write response */ + rsp->status = 0; + rsp->table_rule_add_bulk.n_rules = n_rules; + + /* Free */ + free(found); + free(action_ll_ptr); + free(match_ll_ptr); + free(action_ll); + free(match_ll); + + return rsp; + +fail: + free(found); + free(action_ll_ptr); + free(match_ll_ptr); + free(action_ll); + free(match_ll); + + rsp->status = -1; + rsp->table_rule_add_bulk.n_rules = 0; + return rsp; +} + +static struct pipeline_msg_rsp * +pipeline_msg_handle_table_rule_delete(struct pipeline_data *p, + struct pipeline_msg_req *req) +{ + union table_rule_match_low_level match_ll; + struct pipeline_msg_rsp *rsp = (struct pipeline_msg_rsp *)req; + struct softnic_table_rule_match *match = &req->table_rule_delete.match; + uint32_t table_id = req->id; + int key_found, status; + + status = match_convert(match, &match_ll, 0); + if (status) { + rsp->status = -1; + return rsp; + } + + rsp->status = rte_pipeline_table_entry_delete(p->p, + table_id, + &match_ll, + &key_found, + NULL); + + return rsp; +} + +static struct pipeline_msg_rsp * +pipeline_msg_handle_table_rule_delete_default(struct pipeline_data *p, + struct pipeline_msg_req *req) +{ + struct pipeline_msg_rsp *rsp = (struct pipeline_msg_rsp *)req; + uint32_t table_id = req->id; + + rsp->status = rte_pipeline_table_default_entry_delete(p->p, + table_id, + NULL); + + return rsp; +} + static void pipeline_msg_handle(struct pipeline_data *p) { @@ -723,6 +2019,26 @@ pipeline_msg_handle(struct pipeline_data *p) rsp = pipeline_msg_handle_port_in_disable(p, req); break; + case PIPELINE_REQ_TABLE_RULE_ADD: + rsp = pipeline_msg_handle_table_rule_add(p, req); + break; + + case PIPELINE_REQ_TABLE_RULE_ADD_DEFAULT: + rsp = pipeline_msg_handle_table_rule_add_default(p, req); + break; + + case PIPELINE_REQ_TABLE_RULE_ADD_BULK: + rsp = pipeline_msg_handle_table_rule_add_bulk(p, req); + break; + + case PIPELINE_REQ_TABLE_RULE_DELETE: + rsp = pipeline_msg_handle_table_rule_delete(p, req); + break; + + case PIPELINE_REQ_TABLE_RULE_DELETE_DEFAULT: + rsp = pipeline_msg_handle_table_rule_delete_default(p, req); + break; + default: rsp = (struct pipeline_msg_rsp *)req; rsp->status = -1; -- 2.9.3