diff options
Diffstat (limited to 'VNFs/DPPD-PROX/handle_acl.c')
-rw-r--r-- | VNFs/DPPD-PROX/handle_acl.c | 314 |
1 files changed, 314 insertions, 0 deletions
diff --git a/VNFs/DPPD-PROX/handle_acl.c b/VNFs/DPPD-PROX/handle_acl.c new file mode 100644 index 00000000..03949360 --- /dev/null +++ b/VNFs/DPPD-PROX/handle_acl.c @@ -0,0 +1,314 @@ +/* +// 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 <rte_mbuf.h> +#include <rte_acl.h> +#include <rte_ip.h> +#include <rte_cycles.h> +#include <rte_version.h> + +#include "prox_lua.h" +#include "prox_lua_types.h" + +#include "log.h" +#include "quit.h" +#include "parse_utils.h" +#include "ip_subnet.h" +#include "handle_acl.h" +#include "acl_field_def.h" +#include "task_init.h" +#include "task_base.h" +#include "lconf.h" +#include "prefetch.h" +#include "etypes.h" + +struct task_acl { + struct task_base base; + struct rte_acl_ctx *context; + const uint8_t *ptuples[64]; + + uint32_t n_rules; + uint32_t n_max_rules; + + void *field_defs; + size_t field_defs_size; + uint32_t n_field_defs; +}; + +static void set_tc(struct rte_mbuf *mbuf, uint32_t tc) +{ +#if RTE_VERSION >= RTE_VERSION_NUM(1,8,0,0) + uint32_t subport, pipe, traffic_class, queue; + enum rte_meter_color color; + + rte_sched_port_pkt_read_tree_path(mbuf, &subport, &pipe, &traffic_class, &queue); + color = rte_sched_port_pkt_read_color(mbuf); + + rte_sched_port_pkt_write(mbuf, subport, pipe, tc, queue, color); +#else + struct rte_sched_port_hierarchy *sched = + (struct rte_sched_port_hierarchy *) &mbuf->pkt.hash.sched; + sched->traffic_class = tc; +#endif +} + +static int handle_acl_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts) +{ + struct task_acl *task = (struct task_acl *)tbase; + uint32_t results[64]; + uint8_t out[MAX_PKT_BURST]; + uint16_t j; + +#ifdef PROX_PREFETCH_OFFSET + for (j = 0; j < PROX_PREFETCH_OFFSET && j < n_pkts; ++j) { + PREFETCH0(mbufs[j]); + } + for (j = 1; j < PROX_PREFETCH_OFFSET && j < n_pkts; ++j) { + PREFETCH0(rte_pktmbuf_mtod(mbufs[j - 1], void *)); + } +#endif + for (j = 0; j + PREFETCH_OFFSET < n_pkts; ++j) { +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(mbufs[j + PREFETCH_OFFSET]); + PREFETCH0(rte_pktmbuf_mtod(mbufs[j + PREFETCH_OFFSET - 1], void *)); +#endif + /* TODO: detect version_ihl != 0x45. Extract relevant + fields of that packet and point ptuples[j] to the + extracted verion. Note that this is very unlikely. */ + task->ptuples[j] = rte_pktmbuf_mtod(mbufs[j], uint8_t *); + } +#ifdef PROX_PREFETCH_OFFSET + PREFETCH0(rte_pktmbuf_mtod(mbufs[n_pkts - 1], void *)); + for (; j < n_pkts; ++j) { + task->ptuples[j] = rte_pktmbuf_mtod(mbufs[j], uint8_t *); + } +#endif + + rte_acl_classify(task->context, (const uint8_t **)task->ptuples, results, n_pkts, 1); + + for (uint8_t i = 0; i < n_pkts; ++i) { + switch (results[i]) { + default: + case ACL_NOT_SET: + case ACL_DROP: + out[i] = OUT_DISCARD; + break; + case ACL_ALLOW: + out[i] = 0; + case ACL_RATE_LIMIT: + set_tc(mbufs[i], 3); + break; + }; + } + + return task->base.tx_pkt(&task->base, mbufs, n_pkts, out); +} + +static void acl_msg(struct task_base *tbase, void **data, uint16_t n_msgs) +{ + struct task_acl *task = (struct task_acl *)tbase; + struct acl4_rule **new_rules = (struct acl4_rule **)data; + uint16_t i; + + for (i = 0; i < n_msgs; ++i) { + if (task->n_rules == task->n_max_rules) { + plog_err("Failed to add %d rule%s (already at maximum number of rules (%d))", + n_msgs - i, (n_msgs - i)? "s" : "", task->n_max_rules); + break; + } + + new_rules[i]->data.priority = ++task->n_rules; + rte_acl_add_rules(task->context, (struct rte_acl_rule*) new_rules[i], 1); + } + + /* No need to rebuild if no rules have been added */ + if (!i) { + return ; + } + + struct rte_acl_config acl_build_param; + /* Perform builds */ + acl_build_param.num_categories = 1; + + acl_build_param.num_fields = task->n_field_defs; + rte_memcpy(&acl_build_param.defs, task->field_defs, task->field_defs_size); + + int ret; + PROX_PANIC((ret = rte_acl_build(task->context, &acl_build_param)), + "Failed to build ACL trie (%d)\n", ret); +} + +static void init_task_acl(struct task_base *tbase, struct task_args *targ) +{ + struct task_acl *task = (struct task_acl *)tbase; + int use_qinq = targ->flags & TASK_ARG_QINQ_ACL; + + char name[PATH_MAX]; + struct rte_acl_param acl_param; + + /* Create ACL contexts */ + snprintf(name, sizeof(name), "acl-%d-%d", targ->lconf->id, targ->task); + + if (use_qinq) { + task->n_field_defs = RTE_DIM(pkt_qinq_ipv4_udp_defs); + task->field_defs = pkt_qinq_ipv4_udp_defs; + task->field_defs_size = sizeof(pkt_qinq_ipv4_udp_defs); + } else { + task->n_field_defs = RTE_DIM(pkt_eth_ipv4_udp_defs); + task->field_defs = pkt_eth_ipv4_udp_defs; + task->field_defs_size = sizeof(pkt_eth_ipv4_udp_defs); + } + + acl_param.name = name; + acl_param.socket_id = rte_lcore_to_socket_id(targ->lconf->id); + acl_param.rule_size = RTE_ACL_RULE_SZ(task->n_field_defs); + acl_param.max_rule_num = targ->n_max_rules; + + task->n_max_rules = targ->n_max_rules; + task->context = rte_acl_create(&acl_param); + + PROX_PANIC(task->context == NULL, "Failed to create ACL context\n"); + uint32_t free_rules = targ->n_max_rules; + + PROX_PANIC(!strcmp(targ->rules, ""), "No rule specified for ACL\n"); + + int ret = lua_to_rules(prox_lua(), GLOBAL, targ->rules, task->context, &free_rules, use_qinq, targ->qinq_tag); + PROX_PANIC(ret, "Failed to read rules from config:\n%s\n", get_lua_to_errors()); + task->n_rules = targ->n_max_rules - free_rules; + + plog_info("Configured %d rules\n", task->n_rules); + + if (task->n_rules) { + struct rte_acl_config acl_build_param; + /* Perform builds */ + acl_build_param.num_categories = 1; +#if RTE_VERSION >= RTE_VERSION_NUM(2,1,0,0) + acl_build_param.max_size = 0; +#endif + + acl_build_param.num_fields = task->n_field_defs; + rte_memcpy(&acl_build_param.defs, task->field_defs, task->field_defs_size); + + plog_info("Building trie structure\n"); + PROX_PANIC(rte_acl_build(task->context, &acl_build_param), + "Failed to build ACL trie\n"); + } + + targ->lconf->ctrl_timeout = freq_to_tsc(targ->ctrl_freq); + targ->lconf->ctrl_func_m[targ->task] = acl_msg; +} + +int str_to_rule(struct acl4_rule *rule, char** fields, int n_rules, int use_qinq) +{ + uint32_t svlan, svlan_mask; + uint32_t cvlan, cvlan_mask; + + uint32_t ip_proto, ip_proto_mask; + + struct ip4_subnet ip_src; + struct ip4_subnet ip_dst; + + uint32_t sport_lo, sport_hi; + uint32_t dport_lo, dport_hi; + + enum acl_action class = ACL_NOT_SET; + char class_str[24]; + + PROX_PANIC(parse_int_mask(&svlan, &svlan_mask, fields[0]), "Error parsing svlan: %s\n", get_parse_err()); + PROX_PANIC(parse_int_mask(&cvlan, &cvlan_mask, fields[1]), "Error parsing cvlan: %s\n", get_parse_err()); + PROX_PANIC(parse_int_mask(&ip_proto, &ip_proto_mask, fields[2]), "Error parsing ip protocol: %s\n", get_parse_err()); + PROX_PANIC(parse_ip4_cidr(&ip_src, fields[3]), "Error parsing source IP subnet: %s\n", get_parse_err()); + PROX_PANIC(parse_ip4_cidr(&ip_dst, fields[4]), "Error parsing dest IP subnet: %s\n", get_parse_err()); + + PROX_PANIC(parse_range(&sport_lo, &sport_hi, fields[5]), "Error parsing source port range: %s\n", get_parse_err()); + PROX_PANIC(parse_range(&dport_lo, &dport_hi, fields[6]), "Error parsing destination port range: %s\n", get_parse_err()); + + PROX_PANIC(parse_str(class_str, fields[7], sizeof(class_str)), "Error parsing action: %s\n", get_parse_err()); + + if (!strcmp(class_str, "drop")) { + class = ACL_DROP; + } + else if (!strcmp(class_str, "allow")) { + class = ACL_ALLOW; + } + else if (!strcmp(class_str, "rate limit")) { + class = ACL_RATE_LIMIT; + } + else { + plog_err("unknown class type: %s\n", class_str); + } + + rule->data.userdata = class; /* allow, drop or ratelimit */ + 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; + rule->fields[0].mask_range.u8 = ip_proto_mask; + rule->fields[1].value.u32 = ip_src.ip; + rule->fields[1].mask_range.u32 = ip_src.prefix; + + rule->fields[2].value.u32 = ip_dst.ip; + rule->fields[2].mask_range.u32 = ip_dst.prefix; + + rule->fields[3].value.u16 = sport_lo; + rule->fields[3].mask_range.u16 = sport_hi; + + rule->fields[4].value.u16 = dport_lo; + rule->fields[4].mask_range.u16 = dport_hi; + + if (use_qinq) { + rule->fields[5].value.u16 = rte_bswap16(ETYPE_8021ad); + 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; + 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; + 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; + } + return 0; +} + +static struct task_init task_init_acl = { + .mode_str = "acl", + .init = init_task_acl, + .handle = handle_acl_bulk, + .size = sizeof(struct task_acl) +}; + +__attribute__((constructor)) static void reg_task_acl(void) +{ + reg_task(&task_init_acl); +} |