From 7286b2518ec8e4398b512ce95def9166a7af2e4a Mon Sep 17 00:00:00 2001 From: Deepak S Date: Thu, 13 Jul 2017 21:26:50 -0700 Subject: Adding PROX(Packet pROcessing eXecution engine) VNF to sampleVNF JIRA: SAMPLEVNF-55 PROX is a DPDK-based application implementing Telco use-cases such as a simplified BRAS/BNG, light-weight AFTR... It also allows configuring finer grained network functions like QoS, Routing, load-balancing... (We are moving PROX version v039 to sampleVNF https://01.org/intel-data-plane-performance-demonstrators/prox-overview) Change-Id: Ia3cb02cf0e49ac5596e922c197ff7e010293d033 Signed-off-by: Deepak S --- VNFs/DPPD-PROX/prox_lua_types.c | 1156 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 1156 insertions(+) create mode 100644 VNFs/DPPD-PROX/prox_lua_types.c (limited to 'VNFs/DPPD-PROX/prox_lua_types.c') diff --git a/VNFs/DPPD-PROX/prox_lua_types.c b/VNFs/DPPD-PROX/prox_lua_types.c new file mode 100644 index 00000000..7a0b6e08 --- /dev/null +++ b/VNFs/DPPD-PROX/prox_lua_types.c @@ -0,0 +1,1156 @@ +/* +// Copyright (c) 2010-2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "prox_malloc.h" +#include "etypes.h" +#include "prox_lua.h" +#include "log.h" +#include "quit.h" +#include "defines.h" +#include "prox_globals.h" +#include "prox_lua_types.h" +#include "ip_subnet.h" +#include "hash_entry_types.h" +#include "handle_qinq_encap4.h" +#include "toeplitz.h" +#include "handle_lb_5tuple.h" + +#if RTE_VERSION < RTE_VERSION_NUM(1,8,0,0) +#define RTE_CACHE_LINE_SIZE CACHE_LINE_SIZE +#endif + +static char error_str[1024]; +static char *cur_pos; + +const char *get_lua_to_errors(void) +{ + return error_str; +} + +static void null_terminate_error(void) +{ + size_t diff = cur_pos - error_str; + + if (diff >= sizeof(error_str) && + error_str[sizeof(error_str) - 1] != 0) + error_str[sizeof(error_str) - 1] = 0; +} + +__attribute__((format(printf, 1, 2))) static void set_err(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + + cur_pos = error_str; + cur_pos += vsnprintf(cur_pos, sizeof(error_str) - (cur_pos - error_str), fmt, ap); + null_terminate_error(); + + va_end(ap); +} + +__attribute__((format(printf, 1, 2))) static void concat_err(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + + cur_pos += vsnprintf(cur_pos, sizeof(error_str) - (cur_pos - error_str), fmt, ap); + null_terminate_error(); + + va_end(ap); +} + +/* Make sure that an element is on the top of the stack (zero on success) */ +int lua_getfrom(struct lua_State *L, enum lua_place from, const char *name) +{ + switch (from) { + case STACK: + return lua_gettop(L) > 0? 0 : -1; + case TABLE: + if (!lua_istable(L, -1)) { + set_err("Failed to get field '%s' from table (no table)\n", name); + return -1; + } + + lua_pushstring(L, name); + lua_gettable(L, -2); + if (lua_isnil(L, -1)) { + set_err("Field '%s' is missing from table\n", name); + lua_pop(L, 1); + return -1; + } + return 1; + case GLOBAL: + lua_getglobal(L, name); + if (lua_isnil(L, -1)) { + set_err("Couldn't find global data '%s'\n", name); + lua_pop(L, 1); + return -1; + } + return 1; + } + return -1; +} + +int lua_to_ip(struct lua_State *L, enum lua_place from, const char *name, uint32_t *ip) +{ + uint32_t n_entries; + uint32_t ip_array[4]; + ptrdiff_t v; + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + lua_len(L, -1); + n_entries = lua_tointeger(L, -1); + lua_pop(L, 1); + + if (n_entries != 4) { + set_err("Invalid IPv4 format\n"); + return -1; + } + + *ip = 0; + for (int i = 0; i < 4; ++i) { + lua_pushinteger(L, i + 1); + lua_gettable(L, -2); + v = lua_tointeger(L, -1); + lua_pop(L, 1); + if (!(v >= 0 && v <= 255)) { + set_err("Invalid IPv4 format\n"); + return -1; + } + *ip |= v << (24 - i*8); + } + + lua_pop(L, pop); + return 0; +} + +int lua_to_ip6(struct lua_State *L, enum lua_place from, const char *name, uint8_t *ip) +{ + uint32_t n_entries; + ptrdiff_t v; + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + lua_len(L, -1); + n_entries = lua_tointeger(L, -1); + lua_pop(L, 1); + + if (n_entries != 16) { + set_err("Invalid IPv6 format\n"); + return -1; + } + + for (int i = 0; i < 16; ++i) { + lua_pushinteger(L, i + 1); + lua_gettable(L, -2); + v = lua_tointeger(L, -1); + lua_pop(L, 1); + ip[i] = v; + } + + lua_pop(L, pop); + return 0; +} + +int lua_to_mac(struct lua_State *L, enum lua_place from, const char *name, struct ether_addr *mac) +{ + uint32_t n_entries; + uint32_t mac_array[4]; + ptrdiff_t v; + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + lua_len(L, -1); + n_entries = lua_tointeger(L, -1); + lua_pop(L, 1); + + if (n_entries != 6) { + set_err("Invalid MAC format\n"); + return -1; + } + + for (int i = 0; i < 6; ++i) { + lua_pushinteger(L, i + 1); + lua_gettable(L, -2); + v = lua_tointeger(L, -1); + lua_pop(L, 1); + if (!(v >= 0 && v <= 255)) { + set_err("Invalid MAC format\n"); + return -1; + } + mac->addr_bytes[i] = v; + } + + lua_pop(L, pop); + return 0; +} + +int lua_to_cidr(struct lua_State *L, enum lua_place from, const char *name, struct ip4_subnet *cidr) +{ + uint32_t depth, ip; + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) { + set_err("CIDR is not a table\n"); + return -1; + } + + if (lua_to_ip(L, TABLE, "ip", &ip) || + lua_to_int(L, TABLE, "depth", &depth)) { + return -1; + } + cidr->ip = ip; + cidr->prefix = depth; + + lua_pop(L, pop); + return 0; +} + +int lua_to_cidr6(struct lua_State *L, enum lua_place from, const char *name, struct ip6_subnet *cidr) +{ + uint32_t depth; + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) { + set_err("CIDR6 is not a table\n"); + return -1; + } + + if (lua_to_ip6(L, TABLE, "ip6", cidr->ip) || + lua_to_int(L, TABLE, "depth", &depth)) { + return -1; + } + cidr->prefix = depth; + + lua_pop(L, pop); + return 0; +} + +int lua_to_val_mask(struct lua_State *L, enum lua_place from, const char *name, struct val_mask *val_mask) +{ + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) { + set_err("data entry is not a table\n"); + return -1; + } + + if (lua_to_int(L, TABLE, "val", &val_mask->val) || + lua_to_int(L, TABLE, "mask", &val_mask->mask)) + return -1; + + lua_pop(L, pop); + return 0; +} + +int lua_to_val_range(struct lua_State *L, enum lua_place from, const char *name, struct val_range *val_range) +{ + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) { + set_err("data entry is not a table\n"); + return -1; + } + + if (lua_to_int(L, TABLE, "beg", &val_range->beg) || + lua_to_int(L, TABLE, "end", &val_range->end)) + return -1; + + lua_pop(L, pop); + return 0; +} + +int lua_to_action(struct lua_State *L, enum lua_place from, const char *name, enum acl_action *action) +{ + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_isstring(L, -1)) { + set_err("data entry is not a table\n"); + return -1; + } + + const char *s = lua_tostring(L, -1); + + if (!strcmp(s, "drop")) + *action = ACL_DROP; + else if (!strcmp(s, "allow")) + *action = ACL_ALLOW; + else if (!strcmp(s, "rate_limit")) + *action = ACL_RATE_LIMIT; + else + return -1; + + lua_pop(L, pop); + return 0; +} + +int lua_to_string(struct lua_State *L, enum lua_place from, const char *name, char *dst, size_t size) +{ + const char *str; + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_isstring(L, -1)) { + plog_err("data is not an integer\n"); + return -1; + } + str = lua_tostring(L, -1); + + strncpy(dst, str, size); + + lua_pop(L, pop); + return 0; +} + +int lua_to_port(struct lua_State *L, enum lua_place from, const char *name, uint16_t *port) +{ + double tmp = 0; + int ret; + + ret = lua_to_double(L, from, name, &tmp); + if (ret == 0) + *port = tmp; + return ret; +} + +int lua_to_int(struct lua_State *L, enum lua_place from, const char *name, uint32_t *val) +{ + double tmp = 0; + int ret; + + ret = lua_to_double(L, from, name, &tmp); + if (ret == 0) + *val = tmp; + return ret; +} + +int lua_to_double(struct lua_State *L, enum lua_place from, const char *name, double *val) +{ + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_isnumber(L, -1)) { + set_err("data is not a number\n"); + return -1; + } + *val = lua_tonumber(L, -1); + + lua_pop(L, pop); + return 0; +} + +int lua_to_routes4_entry(struct lua_State *L, enum lua_place from, const char *name, struct ip4_subnet *cidr, uint32_t *nh_idx) +{ + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) { + set_err("Can't read routes4 entry since data is not a table\n"); + return -1; + } + + if (lua_to_cidr(L, TABLE, "cidr", cidr) || + lua_to_int(L, TABLE, "next_hop_id", nh_idx)) { + return -1; + } + + lua_pop(L, pop); + return 0; +} + +int lua_to_next_hop(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, struct next_hop **nh) +{ + struct next_hop *ret; + uint32_t next_hop_index; + uint32_t port_id; + uint32_t ip; + uint32_t mpls; + struct ether_addr mac; + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) { + set_err("Can't read next hop since data is not a table\n"); + return -1; + } + + ret = prox_zmalloc(sizeof(*ret) * MAX_HOP_INDEX, socket); + PROX_PANIC(ret == NULL, "Could not allocate memory for next hop\n"); + + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_to_int(L, TABLE, "id", &next_hop_index) || + lua_to_int(L, TABLE, "port_id", &port_id) || + lua_to_ip(L, TABLE, "ip", &ip) || + lua_to_mac(L, TABLE, "mac", &mac) || + lua_to_int(L, TABLE, "mpls", &mpls)) + return -1; + + PROX_PANIC(port_id >= PROX_MAX_PORTS, "Port id too high (only supporting %d ports)\n", PROX_MAX_PORTS); + PROX_PANIC(next_hop_index >= MAX_HOP_INDEX, "Next-hop to high (only supporting %d next hops)\n", MAX_HOP_INDEX); + + ret[next_hop_index].mac_port.out_idx = port_id; + ret[next_hop_index].ip_dst = ip; + + ret[next_hop_index].mac_port.mac = mac; + ret[next_hop_index].mpls = mpls; + + lua_pop(L, 1); + } + + *nh = ret; + lua_pop(L, pop); + return 0; +} + +int lua_to_next_hop6(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, struct next_hop6 **nh) +{ + struct next_hop6 *ret; + uint32_t next_hop_index, port_id, mpls; + struct ether_addr mac; + uint8_t ip[16]; + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) { + set_err("Data is not a table\n"); + return -1; + } + + ret = prox_zmalloc(sizeof(*ret) * MAX_HOP_INDEX, socket); + PROX_PANIC(ret == NULL, "Could not allocate memory for next hop\n"); + + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_to_int(L, TABLE, "id", &next_hop_index) || + lua_to_int(L, TABLE, "port_id", &port_id) || + lua_to_ip6(L, TABLE, "ip6", ip) || + lua_to_mac(L, TABLE, "mac", &mac) || + lua_to_int(L, TABLE, "mpls", &mpls)) + return -1; + + PROX_PANIC(port_id >= PROX_MAX_PORTS, "Port id too high (only supporting %d ports)\n", PROX_MAX_PORTS); + PROX_PANIC(next_hop_index >= MAX_HOP_INDEX, "Next-hop to high (only supporting %d next hops)\n", MAX_HOP_INDEX); + + ret[next_hop_index].mac_port.out_idx = port_id; + memcpy(ret[next_hop_index].ip_dst,ip, 16); + + ret[next_hop_index].mac_port.mac = mac; + ret[next_hop_index].mpls = mpls; + + lua_pop(L, 1); + } + + *nh = ret; + lua_pop(L, pop); + return 0; +} + +int lua_to_routes4(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, struct lpm4 *lpm) +{ + struct ip4_subnet dst; + uint32_t next_hop_index; + uint32_t n_loaded_rules; + uint32_t n_tot_rules; + struct rte_lpm *new_lpm; + char lpm_name[64]; + int ret; + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + snprintf(lpm_name, sizeof(lpm_name), "IPv4_lpm_s%u", socket); + + if (!lua_istable(L, -1)) { + set_err("Data is not a table\n"); + return -1; + } + + lua_len(L, -1); + n_tot_rules = lua_tointeger(L, -1); + n_loaded_rules = 0; + lua_pop(L, 1); +#if RTE_VERSION >= RTE_VERSION_NUM(16,4,0,1) + struct rte_lpm_config conf; + conf.max_rules = 2 * n_tot_rules; + conf.number_tbl8s = 256; + conf.flags = 0; + new_lpm = rte_lpm_create(lpm_name, socket, &conf); +#else + new_lpm = rte_lpm_create(lpm_name, socket, 2 * n_tot_rules, 0); +#endif + PROX_PANIC(NULL == new_lpm, "Failed to allocate lpm\n"); + + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_to_routes4_entry(L, STACK, NULL, &dst, &next_hop_index)) { + set_err("Failed to read entry while setting up lpm\n"); + return -1; + } + ret = rte_lpm_add(new_lpm, dst.ip, dst.prefix, next_hop_index); + + if (ret != 0) { + set_err("Failed to add (%d) index %u ip %x/%u to lpm\n", + ret, next_hop_index, dst.ip, dst.prefix); + } + else if (++n_loaded_rules % 10000 == 0) { + plog_info("Route %d added\n", n_loaded_rules); + } + + lua_pop(L, 1); + } + + lpm->rte_lpm = new_lpm; + lpm->n_used_rules = n_loaded_rules; + lpm->n_free_rules = 2 * n_tot_rules - n_loaded_rules; + + lua_pop(L, pop); + return 0; +} + +int lua_to_lpm4(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, struct lpm4 **lpm) +{ + struct lpm4 *ret; + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + ret = prox_zmalloc(sizeof(struct lpm4), socket); + + if (!lua_istable(L, -1)) { + set_err("Can't read lpm4 since data is not a table\n"); + return -1; + } + + if (lua_to_routes4(L, TABLE, "routes", socket, ret) || + lua_to_next_hop(L, TABLE, "next_hops", socket, &ret->next_hops)) { + return -1; + } + + if (ret->rte_lpm) + plog_info("Loaded %d routes\n", ret->n_used_rules); + + *lpm = ret; + lua_pop(L, pop); + return 0; +} + +int lua_to_lpm6(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, struct lpm6 **lpm) +{ + struct lpm6 *ret; + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) { + set_err("Lpm6 is not a table\n"); + return -1; + } + + ret = prox_zmalloc(sizeof(struct lpm6), socket); + + if (lua_to_routes6(L, TABLE, "routes6", socket, ret) || + lua_to_next_hop6(L, TABLE, "next_hops6", socket, &ret->next_hops)) + return -1; + + if (ret->rte_lpm6) + plog_info("Loaded %d routes\n", ret->n_used_rules); + + *lpm = ret; + + lua_pop(L, pop); + return 0; +} + +static int lua_to_lpm6_entry(struct lua_State *L, enum lua_place from, const char *name, struct ip6_subnet *cidr, uint32_t *nh_idx) +{ + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) { + set_err("lpm6 entry is not a table\n"); + return -1; + } + if (lua_to_cidr6(L, TABLE, "cidr6", cidr) || + lua_to_int(L, TABLE, "next_hop_id", nh_idx)) { + return -1; + } + + lua_pop(L, pop); + return 0; +} + +int lua_to_routes6(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, struct lpm6 *lpm) +{ + struct ip6_subnet dst; + uint32_t next_hop_index; + uint32_t n_loaded_rules; + struct rte_lpm6 *new_lpm; + struct rte_lpm6_config config; + uint32_t n_tot_rules; + char lpm_name[64]; + int ret; + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + snprintf(lpm_name, sizeof(lpm_name), "IPv6_lpm_s%u", socket); + + if (!lua_istable(L, -1)) { + set_err("Data is not a table\n"); + return -1; + } + + lua_len(L, -1); + n_tot_rules = lua_tointeger(L, -1); + n_loaded_rules = 0; + lua_pop(L, 1); + + config.max_rules = n_tot_rules; + config.number_tbl8s = (1 << 16); + config.flags = 0; + + new_lpm = rte_lpm6_create(lpm_name, socket, &config); + PROX_PANIC(NULL == new_lpm, "Failed to allocate lpm\n"); + + lua_pushnil(L); + while (lua_next(L, -2)) { + + if (lua_to_lpm6_entry(L, STACK, NULL, &dst, &next_hop_index)) { + concat_err("Failed to read entry while setting up lpm\n"); + return -1; + } + + ret = rte_lpm6_add(new_lpm, dst.ip, dst.prefix, next_hop_index); + + if (ret != 0) { + plog_warn("Failed to add (%d) index %u, %d\n", + ret, next_hop_index, dst.prefix); + } + else if (++n_loaded_rules % 10000 == 0) { + plog_info("Route %d added\n", n_loaded_rules); + } + + lua_pop(L, 1); + } + + lpm->rte_lpm6 = new_lpm; + lpm->n_used_rules = n_loaded_rules; + lpm->n_free_rules = 2 * n_tot_rules - n_loaded_rules; + + lua_pop(L, pop); + return 0; +} + +int lua_to_dscp(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, uint8_t **dscp) +{ + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) { + set_err("DSCP is not a table\n"); + return -1; + } + + uint32_t dscp_bits, tc, queue; + int status; + *dscp = prox_zmalloc(64, socket); + PROX_PANIC(dscp == NULL, "Error creating dscp table"); + + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_to_int(L, TABLE, "dscp", &dscp_bits) || + lua_to_int(L, TABLE, "tc", &tc) || + lua_to_int(L, TABLE, "queue", &queue)) { + concat_err("Failed to read dscp config\n"); + return -1; + } + + lua_pop(L, 1); + + (*dscp)[dscp_bits] = tc << 2 | queue; + } + + lua_pop(L, pop); + return 0; +} + +int lua_to_qinq_gre_map(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, struct qinq_gre_map **qinq_gre_map) +{ + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) { + if (from != STACK) + set_err("QinQ to gre map is not a table\n"); + else + set_err("QinQ to gre map %s is not a table\n", name); + return -1; + } + + struct qinq_gre_map *ret; + uint32_t svlan, cvlan; + uint16_t be_svlan, be_cvlan; + uint32_t user; + uint32_t gre_id; + + uint32_t n_entries; + uint32_t idx = 0; + + lua_len(L, -1); + n_entries = lua_tointeger(L, -1); + lua_pop(L, 1); + + size_t mem_size = 0; + mem_size += sizeof(struct qinq_gre_map); + mem_size += n_entries * sizeof(struct qinq_gre_entry); + + ret = prox_zmalloc(mem_size, socket); + PROX_PANIC(ret == NULL, "Error creating gre_qinq map"); + + ret->count = n_entries; + + lua_pushnil(L); + while (lua_next(L, -2)) { + + if (lua_to_int(L, TABLE, "svlan_id", &svlan) || + lua_to_int(L, TABLE, "cvlan_id", &cvlan) || + lua_to_int(L, TABLE, "gre_id", &gre_id) || + lua_to_int(L, TABLE, "user_id", &user)) { + concat_err("Failed to read user table config\n"); + return -1; + } + + be_svlan = rte_bswap16((uint16_t)svlan); + be_cvlan = rte_bswap16((uint16_t)cvlan); + + ret->entries[idx].user = user; + ret->entries[idx].svlan = be_svlan; + ret->entries[idx].cvlan = be_cvlan; + ret->entries[idx].gre_id = gre_id; + ret->entries[idx].rss = toeplitz_hash((uint8_t *)&be_cvlan, 4); + + plog_dbg("elem %u: be_svlan=%x, be_cvlan=%x, rss_input=%x, rss=%x, gre_id=%x\n", + idx, be_svlan, be_cvlan, be_cvlan, ret->entries[idx].rss, gre_id); + + idx++; + lua_pop(L, 1); + } + + *qinq_gre_map = ret; + + lua_pop(L, pop); + return 0; +} + +int lua_to_user_table(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, uint16_t **user_table) +{ + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) { + set_err("Data is not a table\n"); + return -1; + } + + uint32_t svlan, cvlan; + uint16_t be_svlan, be_cvlan; + uint32_t user; + + *user_table = prox_zmalloc(0x1000000 * sizeof(uint16_t), socket); + PROX_PANIC(*user_table == NULL, "Error creating user table"); + + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_to_int(L, TABLE, "svlan_id", &svlan) || + lua_to_int(L, TABLE, "cvlan_id", &cvlan) || + lua_to_int(L, TABLE, "user_id", &user)) { + concat_err("Failed to read user table config\n"); + return -1; + } + + be_svlan = rte_bswap16((uint16_t)svlan); + be_cvlan = rte_bswap16((uint16_t)cvlan); + + (*user_table)[PKT_TO_LUTQINQ(be_svlan, be_cvlan)] = user; + + lua_pop(L, 1); + } + + lua_pop(L, pop); + return 0; +} + +int lua_to_ip6_tun_binding(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, struct ipv6_tun_binding_table **data) +{ + struct ipv6_tun_binding_table *ret; + uint32_t n_entries; + uint32_t idx = 0; + uint32_t port = 0; + size_t memsize = 0; + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) { + set_err("Can't read IPv6 tunnel bindings entry since ret is not a table\n"); + return -1; + } + + lua_len(L, -1); + n_entries = lua_tointeger(L, -1); + lua_pop(L, 1); + + memsize = sizeof(struct ipv6_tun_binding_table); + memsize += n_entries * sizeof(struct ipv6_tun_binding_entry); + + ret = prox_zmalloc(memsize, socket); + + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_to_ip6(L, TABLE, "ip6", ret->entry[idx].endpoint_addr.bytes) || + lua_to_mac(L, TABLE, "mac", &ret->entry[idx].next_hop_mac) || + lua_to_ip(L, TABLE, "ip", &ret->entry[idx].public_ipv4) || + lua_to_int(L, TABLE, "port", &port)) + return -1; + + ret->entry[idx].public_port = port; + idx++; + lua_pop(L, 1); + } + ret->num_binding_entries = idx; + + plog_info("\tRead %d IPv6 Tunnel Binding entries\n", idx); + + *data = ret; + + lua_pop(L, pop); + return 0; +} + +int lua_to_cpe_table_data(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, struct cpe_table_data **data) +{ + struct cpe_table_data *ret; + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) { + set_err("Can't read IPv6 tunnel bindings entry since ret is not a table\n"); + return -1; + } + + /* Each entry in the input table expands to multiple entries + depending on the number of hosts within the subnet. For + this reason, go through the whole table and find out how + many entries will be added in total. */ + struct ip4_subnet cidr; + uint32_t n_entries = 0; + uint32_t port_idx, gre_id, svlan, cvlan, user; + struct ether_addr mac; + uint32_t idx = 0; + + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_to_cidr(L, TABLE, "cidr", &cidr)) + return -1; + n_entries += ip4_subet_get_n_hosts(&cidr); + lua_pop(L, 1); + } + + ret = prox_zmalloc(sizeof(*ret) + n_entries * sizeof(struct cpe_table_entry), 0); + + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_to_int(L, TABLE, "dest_id", &port_idx) || + lua_to_int(L, TABLE, "gre_id", &gre_id) || + lua_to_int(L, TABLE, "svlan_id", &svlan) || + lua_to_int(L, TABLE, "cvlan_id", &cvlan) || + lua_to_cidr(L, TABLE, "cidr", &cidr) || + lua_to_mac(L, TABLE, "mac", &mac) || + lua_to_int(L, TABLE, "user_id", &user)) + return -1; + + uint32_t n_hosts = ip4_subet_get_n_hosts(&cidr); + + for (uint32_t i = 0; i < n_hosts; ++i) { + ret->entries[idx].port_idx = port_idx; + ret->entries[idx].gre_id = gre_id; + ret->entries[idx].svlan = rte_bswap16(svlan); + ret->entries[idx].cvlan = rte_bswap16(cvlan); + ret->entries[idx].eth_addr = mac; + ret->entries[idx].user = user; + + PROX_PANIC(ip4_subnet_to_host(&cidr, i, &ret->entries[idx].ip), "Invalid host in address\n"); + ret->entries[idx].ip = rte_bswap32(ret->entries[idx].ip); + idx++; + } + + lua_pop(L, 1); + } + + ret->n_entries = n_entries; + *data = ret; + + lua_pop(L, pop); + return 0; +} + +struct acl4_rule { + struct rte_acl_rule_data data; + struct rte_acl_field fields[9]; +}; + +int lua_to_rules(struct lua_State *L, enum lua_place from, const char *name, struct rte_acl_ctx *ctx, uint32_t* n_max_rules, int use_qinq, uint16_t qinq_tag) +{ + int pop; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) { + set_err("Can't read rules since data is not a table\n"); + return -1; + } + + struct val_mask svlan, cvlan, ip_proto; + struct ip4_subnet src_cidr, dst_cidr; + struct val_range sport, dport; + enum acl_action action; + uint32_t n_rules = 0; + lua_pushnil(L); + while (lua_next(L, -2)) { + if (n_rules == *n_max_rules) { + set_err("Too many rules"); + return -1; + } + if (use_qinq) { + if (lua_to_val_mask(L, TABLE, "svlan_id", &svlan) || + lua_to_val_mask(L, TABLE, "cvlan_id", &cvlan)) + return -1; + } + + if (lua_to_val_mask(L, TABLE, "ip_proto", &ip_proto) || + lua_to_cidr(L, TABLE, "src_cidr", &src_cidr) || + lua_to_cidr(L, TABLE, "dst_cidr", &dst_cidr) || + lua_to_val_range(L, TABLE, "sport", &sport) || + lua_to_val_range(L, TABLE, "dport", &dport) || + lua_to_action(L, TABLE, "action", &action)) + return -1; + + struct acl4_rule rule; + + rule.data.userdata = action; /* allow, drop or rate_limit */ + rule.data.category_mask = 1; + rule.data.priority = n_rules++; + + /* Configuration for rules is done in little-endian so no bswap is needed here.. */ + + rule.fields[0].value.u8 = ip_proto.val; + rule.fields[0].mask_range.u8 = ip_proto.mask; + rule.fields[1].value.u32 = src_cidr.ip; + rule.fields[1].mask_range.u32 = src_cidr.prefix; + + rule.fields[2].value.u32 = dst_cidr.ip; + rule.fields[2].mask_range.u32 = dst_cidr.prefix; + + rule.fields[3].value.u16 = sport.beg; + rule.fields[3].mask_range.u16 = sport.end; + + rule.fields[4].value.u16 = dport.beg; + rule.fields[4].mask_range.u16 = dport.end; + + if (use_qinq) { + rule.fields[5].value.u16 = rte_bswap16(qinq_tag); + rule.fields[5].mask_range.u16 = 0xffff; + + /* To mask out the TCI and only keep the VID, the mask should be 0x0fff */ + rule.fields[6].value.u16 = svlan.val; + rule.fields[6].mask_range.u16 = svlan.mask; + + rule.fields[7].value.u16 = rte_bswap16(ETYPE_VLAN); + rule.fields[7].mask_range.u16 = 0xffff; + + rule.fields[8].value.u16 = cvlan.val; + rule.fields[8].mask_range.u16 = cvlan.mask; + } + else { + /* Reuse first ethertype from vlan to check if packet is IPv4 packet */ + rule.fields[5].value.u16 = rte_bswap16(ETYPE_IPv4); + rule.fields[5].mask_range.u16 = 0xffff; + + /* Other fields are ignored */ + rule.fields[6].value.u16 = 0; + rule.fields[6].mask_range.u16 = 0; + rule.fields[7].value.u16 = 0; + rule.fields[7].mask_range.u16 = 0; + rule.fields[8].value.u16 = 0; + rule.fields[8].mask_range.u16 = 0; + } + + rte_acl_add_rules(ctx, (struct rte_acl_rule*) &rule, 1); + lua_pop(L, 1); + } + + *n_max_rules -= n_rules; + lua_pop(L, pop); + return 0; +} + +static inline uint32_t ipv4_hash_crc(const void *data, __rte_unused uint32_t data_len, uint32_t init_val) +{ + const union ipv4_5tuple_host *k; + uint32_t t; + const uint32_t *p; + + k = data; + t = k->proto; + p = (const uint32_t *)&k->port_src; + + init_val = rte_hash_crc_4byte(t, init_val); + init_val = rte_hash_crc_4byte(k->ip_src, init_val); + init_val = rte_hash_crc_4byte(k->ip_dst, init_val); + init_val = rte_hash_crc_4byte(*p, init_val); + return (init_val); +} + +int lua_to_tuples(struct lua_State *L, enum lua_place from, const char *name, uint8_t socket, struct rte_hash **lookup_hash, uint8_t **out_if) +{ + int pop; + char s[64]; + + if ((pop = lua_getfrom(L, from, name)) < 0) + return -1; + + if (!lua_istable(L, -1)) { + plog_err("Can't read rules since data is not a table\n"); + return -1; + } + + lua_len(L, -1); + uint32_t n_tot_tuples = lua_tointeger(L, -1); + lua_pop(L, 1); + + struct rte_hash_parameters ipv4_l3fwd_hash_params = { + .name = NULL, + .entries = n_tot_tuples * 4, + .key_len = sizeof(union ipv4_5tuple_host), +#if RTE_VERSION < RTE_VERSION_NUM(2, 1, 0, 0) + .bucket_entries = 4, +#endif + .hash_func = ipv4_hash_crc, + .hash_func_init_val = 0, + }; + + /* create lb_5tuple hash - same hash is shared between cores on same socket */ + snprintf(s, sizeof(s), "ipv4_l3fwd_hash_%d", socket); + if ((*lookup_hash = rte_hash_find_existing(s)) == NULL) { + ipv4_l3fwd_hash_params.name = s; + ipv4_l3fwd_hash_params.socket_id = socket; + *lookup_hash = rte_hash_create(&ipv4_l3fwd_hash_params); + PROX_PANIC(*lookup_hash == NULL, "Unable to create the lb_5tuple hash\n"); + } + + lua_pushnil(L); + while (lua_next(L, -2)) { + uint32_t if_out, ip_src, ip_dst, port_src, port_dst, proto; + union ipv4_5tuple_host newkey; + + if (lua_to_int(L, TABLE, "if_out", &if_out) || + lua_to_int(L, TABLE, "ip_src", &ip_src) || + lua_to_int(L, TABLE, "ip_dst", &ip_dst) || + lua_to_int(L, TABLE, "port_src", &port_src) || + lua_to_int(L, TABLE, "port_dst", &port_dst) || + lua_to_int(L, TABLE, "proto", &proto)) { + plog_err("Failed to read user table config\n"); + return -1; + } + + newkey.ip_dst = rte_cpu_to_be_32(ip_dst); + newkey.ip_src = rte_cpu_to_be_32(ip_src); + newkey.port_dst = rte_cpu_to_be_16((uint16_t)port_dst); + newkey.port_src = rte_cpu_to_be_16((uint16_t)port_src); + newkey.proto = (uint8_t)proto; + newkey.pad0 = 0; + newkey.pad1 = 0; + + int32_t ret = rte_hash_add_key(*lookup_hash, (void *) &newkey); + PROX_PANIC(ret < 0, "Unable to add entry (err code %d)\n", ret); + (*out_if)[ret] = (uint8_t) if_out; + + lua_pop(L, 1); + } + lua_pop(L, pop); + return 0; +} -- cgit 1.2.3-korg