summaryrefslogtreecommitdiffstats
path: root/VNFs/DPPD-PROX/handle_gre_decap_encap.c
diff options
context:
space:
mode:
Diffstat (limited to 'VNFs/DPPD-PROX/handle_gre_decap_encap.c')
-rw-r--r--VNFs/DPPD-PROX/handle_gre_decap_encap.c462
1 files changed, 462 insertions, 0 deletions
diff --git a/VNFs/DPPD-PROX/handle_gre_decap_encap.c b/VNFs/DPPD-PROX/handle_gre_decap_encap.c
new file mode 100644
index 00000000..41f6dd33
--- /dev/null
+++ b/VNFs/DPPD-PROX/handle_gre_decap_encap.c
@@ -0,0 +1,462 @@
+/*
+// 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_byteorder.h>
+#include <rte_cycles.h>
+#include <rte_hash.h>
+#include <rte_ip.h>
+
+#include "prox_malloc.h"
+#include "task_init.h"
+#include "lconf.h"
+#include "defines.h"
+#include "stats.h"
+#include "tx_pkt.h"
+#include "hash_entry_types.h"
+#include "prefetch.h"
+#include "prox_cksum.h"
+#include "gre.h"
+#include "etypes.h"
+#include "log.h"
+#include "quit.h"
+#include "prox_assert.h"
+#include "pkt_prototypes.h"
+#include "quit.h"
+
+struct cpe_gre_key {
+ struct ether_addr clt_mac;
+ uint16_t pad;
+} __attribute__((__packed__));
+
+struct cpe_gre_data {
+ uint32_t gre_id;
+ uint32_t cpe_ip;
+ uint64_t tsc;
+#ifdef GRE_TP
+ uint64_t tp_tsc;
+ double tp_tbsize;
+#endif
+} __attribute__((__packed__));
+
+struct task_gre_decap {
+ struct task_base base;
+ struct rte_hash *cpe_gre_hash;
+ struct cpe_gre_data *cpe_gre_data;
+ struct lcore_cfg *lconf;
+ uint8_t runtime_flags;
+ uint8_t mapping[PROX_MAX_PORTS];
+ uint32_t bucket_index;
+ int offload_crc;
+ const void* key_ptr[16];
+ struct cpe_gre_key key[16];
+ uint64_t cpe_timeout;
+#ifdef GRE_TP
+ double cycles_per_byte;
+ uint32_t tb_size;
+#endif
+};
+
+static void handle_gre_decap_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts);
+static void handle_gre_encap_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts);
+
+static inline uint8_t handle_gre_encap(struct task_gre_decap *task, struct rte_mbuf *mbuf, struct cpe_gre_data *table);
+static inline void handle_gre_encap16(struct task_gre_decap *task, struct rte_mbuf **mbufs, uint16_t n_pkts, uint8_t *out);
+static inline uint8_t handle_gre_decap(struct task_gre_decap *tbase, struct rte_mbuf *mbuf);
+
+void update_arp_entries_gre(void *data);
+
+static void init_cpe_gre_hash(struct task_args *targ)
+{
+ char name[64];
+ uint8_t socket_id;
+ uint8_t lcore_id;
+ uint8_t table_part;
+
+ /* Already set up by other task */
+ if (targ->cpe_gre_hash) {
+ return;
+ }
+
+ lcore_id = targ->lconf->id;
+ socket_id = rte_lcore_to_socket_id(lcore_id);
+ sprintf(name, "core_%u_CPE_GRE_Table", targ->lconf->id);
+ table_part = targ->nb_slave_threads;
+
+ if (table_part == 0)
+ table_part = 1;
+ if (!rte_is_power_of_2(table_part)) {
+ table_part = rte_align32pow2(table_part) >> 1;
+ }
+
+ struct rte_hash_parameters hash_params = {
+ .name = name,
+ .entries = MAX_GRE / table_part,
+ .bucket_entries = GRE_BUCKET_ENTRIES,
+ .key_len = sizeof(struct cpe_gre_key),
+ .hash_func_init_val = 0,
+ .socket_id = socket_id
+ };
+
+ struct rte_hash* phash = rte_hash_create(&hash_params);
+ struct cpe_gre_data *cpe_gre_data = prox_zmalloc(MAX_GRE / table_part, socket_id);
+
+ PROX_PANIC(phash == NULL, "Unable to allocate memory for IPv4 hash table on core %u\n", lcore_id);
+
+ for (uint8_t task_id = 0; task_id < targ->lconf->n_tasks_all; ++task_id) {
+ enum task_mode smode = targ->lconf->targs[task_id].mode;
+ if (smode == GRE_DECAP || smode == GRE_ENCAP) {
+ targ->lconf->targs[task_id].cpe_gre_hash = phash;
+ targ->lconf->targs[task_id].cpe_gre_data = cpe_gre_data;
+ }
+ }
+}
+
+static void init_task_gre_decap(struct task_base *tbase, struct task_args *targ)
+{
+ struct task_gre_decap *task = (struct task_gre_decap *)tbase;
+
+ init_cpe_gre_hash(targ);
+ task->cpe_gre_hash = targ->cpe_gre_hash;
+ task->cpe_gre_data = targ->cpe_gre_data;
+ task->runtime_flags = targ->runtime_flags;
+ task->lconf = targ->lconf;
+ task->cpe_timeout = msec_to_tsc(targ->cpe_table_timeout_ms);
+
+ targ->lconf->period_func = update_arp_entries_gre;
+ targ->lconf->period_data = tbase;
+ targ->lconf->period_timeout = msec_to_tsc(500) / NUM_VCPES;
+
+ for (uint8_t i = 0; i < 16; ++i) {
+ task->key_ptr[i] = &task->key[i];
+ }
+}
+
+static void init_task_gre_encap(struct task_base *tbase, struct task_args *targ)
+{
+ struct task_gre_decap *task = (struct task_gre_decap *)tbase;
+
+ init_cpe_gre_hash(targ);
+ task->cpe_gre_hash = targ->cpe_gre_hash;
+ task->cpe_gre_data = targ->cpe_gre_data;
+ task->runtime_flags = targ->runtime_flags;
+ task->lconf = targ->lconf;
+
+ struct port_cfg *port = find_reachable_task_sending_to_port(targ);
+ if (port) {
+ task->offload_crc = port->capabilities.tx_offload_cksum;
+ }
+
+#ifdef GRE_TP
+ if (targ->tb_rate) {
+ task->cycles_per_byte = ((double)rte_get_tsc_hz()) / ((double)targ->tb_rate);
+ task->tb_size = targ->tb_size != 0 ? targ->tb_size : 1520;
+ }
+ else {
+ /* traffic policing disabled */
+ task->cycles_per_byte = 0;
+ }
+#endif
+}
+
+static struct task_init task_init_gre_decap = {
+ .mode = GRE_DECAP,
+ .mode_str = "gredecap",
+ .init = init_task_gre_decap,
+ .handle = handle_gre_decap_bulk,
+ .size = sizeof(struct task_gre_decap)
+};
+
+static struct task_init task_init_gre_encap = {
+ .mode = GRE_ENCAP,
+ .mode_str = "greencap",
+ .init = init_task_gre_encap,
+ .handle = handle_gre_encap_bulk,
+ .size = sizeof(struct task_gre_decap)
+};
+
+__attribute__((constructor)) static void reg_task_gre(void)
+{
+ reg_task(&task_init_gre_decap);
+ reg_task(&task_init_gre_encap);
+}
+
+void handle_gre_decap_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts)
+{
+ struct task_gre_decap *task = (struct task_gre_decap *)tbase;
+ uint8_t out[MAX_PKT_BURST];
+ uint16_t j;
+
+ prefetch_first(mbufs, n_pkts);
+
+ 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
+ out[j] = handle_gre_decap(task, mbufs[j]);
+ }
+#ifdef PROX_PREFETCH_OFFSET
+ PREFETCH0(rte_pktmbuf_mtod(mbufs[n_pkts - 1], void *));
+ for (; j < n_pkts; ++j) {
+ out[j] = handle_gre_decap(task, mbufs[j]);
+ }
+#endif
+
+ task->base.tx_pkt(&task->base, mbufs, n_pkts, out);
+}
+
+struct gre_packet {
+ struct ether_hdr eth;
+ struct ipv4_hdr ip;
+ struct gre_hdr gre;
+ union {
+ struct ether_hdr eth2;
+ struct ipv4_hdr ip2;
+ };
+} __attribute__((__packed__));
+
+/* Handle ipv4 over GRE and Ethernet over GRE. In case of ipv4 over
+ GRE remove gre and ipv4 header and retain space for ethernet
+ header. In case of Eth over GRE remove external eth, gre and ipv4
+ headers and return pointer to payload */
+static inline struct ether_hdr *gre_decap(struct gre_hdr *pgre, struct rte_mbuf *mbuf)
+{
+ int16_t hsize = 0;
+ if (pgre->type == ETYPE_EoGRE) {
+ hsize = sizeof(struct ether_hdr) + sizeof(struct ipv4_hdr) + sizeof(struct gre_hdr);
+ }
+ else if (pgre->type == ETYPE_IPv4) {
+ /* retain sizeof(struct ether_hdr) */
+ hsize = sizeof(struct ipv4_hdr) + sizeof(struct gre_hdr);
+ }
+ else {
+ return NULL;
+ }
+
+ return (struct ether_hdr *)rte_pktmbuf_adj(mbuf, hsize);
+}
+
+static inline uint8_t handle_gre_decap(struct task_gre_decap *task, struct rte_mbuf *mbuf)
+{
+ struct ipv4_hdr *pip = (struct ipv4_hdr *)(rte_pktmbuf_mtod(mbuf, struct ether_hdr *) + 1);
+
+ if (pip->next_proto_id != IPPROTO_GRE) {
+ plog_warn("Invalid packet proto_id = 0x%x expect 0x%x\n",
+ pip->next_proto_id, IPPROTO_GRE);
+ return OUT_DISCARD;
+ }
+
+ struct cpe_gre_data data;
+ struct cpe_gre_key key;
+ struct gre_hdr *pgre = (struct gre_hdr *)(pip + 1);
+ data.gre_id = pgre->gre_id;
+ data.cpe_ip = pip->src_addr;
+
+ struct ether_hdr *peth = gre_decap(pgre, mbuf);
+ PROX_PANIC(peth != 0, "Failed to gre_decap");
+
+ pip = (struct ipv4_hdr *)(peth + 1);
+
+/* emulate client MAC for test purposes */
+#if 1
+ if (pgre->type == ETYPE_IPv4) {
+ struct ether_hdr eth = {
+ .d_addr = {.addr_bytes =
+ {0x0A, 0x02, 0x0A, 0x0A, 0x00, 0x01}},
+ .s_addr = {.addr_bytes =
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ .ether_type = ETYPE_IPv4
+ };
+ uint32_t hip = rte_bswap32(pip->src_addr);
+ eth.s_addr.addr_bytes[2] = (hip >> 24) & 0xFF;
+ eth.s_addr.addr_bytes[3] = (hip >> 16) & 0xFF;
+ eth.s_addr.addr_bytes[4] = (hip >> 8) & 0xFF;
+ eth.s_addr.addr_bytes[5] = (hip) & 0xFF;
+ rte_memcpy(peth, &eth, sizeof(struct ether_hdr));
+ }
+ ether_addr_copy(&peth->s_addr, &key.clt_mac);
+#endif
+
+ data.tsc = rte_rdtsc() + task->cpe_timeout;
+
+ int32_t hash_index = rte_hash_add_key(task->cpe_gre_hash, &key);
+ if (unlikely(hash_index < 0)) {
+ plog_warn("Failed to add key, gre %x\n", data.gre_id);
+ }
+ else if (unlikely(hash_index >= MAX_GRE)) {
+ plog_warn("Failed to add: Invalid hash_index = 0x%x\n",
+ hash_index);
+ return OUT_DISCARD;
+ }
+ rte_memcpy(&task->cpe_gre_data[hash_index], &data, sizeof(data));
+ if (task->runtime_flags & TASK_TX_CRC) {
+ prox_ip_cksum(mbuf, pip, sizeof(struct ether_hdr), sizeof(struct ipv4_hdr), task->offload_crc);
+ }
+
+ return 0;
+}
+
+void handle_gre_encap_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts)
+{
+ struct task_gre_decap *task = (struct task_gre_decap *)tbase;
+ uint8_t out[MAX_PKT_BURST];
+ uint16_t done = 0;
+
+ while (n_pkts) {
+ uint16_t chopped = RTE_MIN(n_pkts, 16);
+ prefetch_pkts(mbufs, chopped);
+ handle_gre_encap16(task, mbufs, chopped, out + done);
+ mbufs += chopped;
+ n_pkts -= chopped;
+ done += chopped;
+ }
+
+ task->base.tx_pkt(&task->base, mbufs - done, done, out);
+}
+
+#define DO_ENC_ETH_OVER_GRE 1
+#define DO_ENC_IP_OVER_GRE 0
+
+static inline void handle_gre_encap16(struct task_gre_decap *task, struct rte_mbuf **mbufs, uint16_t n_pkts, uint8_t *out)
+{
+ for (uint8_t i = 0; i < n_pkts; ++i) {
+ struct ether_hdr *peth = rte_pktmbuf_mtod(mbufs[i], struct ether_hdr *);
+ ether_addr_copy(&peth->d_addr, &task->key[i].clt_mac);
+ }
+
+ int32_t hash_index[16];
+ rte_hash_lookup_bulk(task->cpe_gre_hash, task->key_ptr, n_pkts, hash_index);
+ for (uint8_t i = 0; i < n_pkts; ++i ) {
+ if (unlikely(hash_index[i] < 0)) {
+ plog_warn("Invalid hash_index (<0) = 0x%x\n", hash_index[i]);
+ out[i] = OUT_DISCARD;
+ }
+ else if (unlikely(hash_index[i] >= MAX_GRE)) {
+ plog_warn("Invalid hash_index = 0x%x\n", hash_index[i]);
+ out[i] = OUT_DISCARD;
+ }
+ rte_prefetch0(&task->cpe_gre_data[hash_index[i]]);
+ }
+
+ for (uint8_t i = 0; i < n_pkts; ++i ) {
+ if (likely(out[i] != OUT_DISCARD)) {
+ out[i] = handle_gre_encap(task, mbufs[i], &task->cpe_gre_data[hash_index[i]]);
+ }
+ }
+}
+
+#ifdef DO_ENC_ETH_OVER_GRE
+#define PKT_PREPEND_LEN (sizeof(struct ether_hdr) + sizeof(struct ipv4_hdr) + sizeof(struct gre_hdr))
+#elif DO_ENC_IP_OVER_GRE
+#define PKT_PREPEND_LEN (sizeof(struct ipv4_hdr) + sizeof(struct gre_hdr))
+#else
+
+static inline uint8_t handle_gre_encap(struct task_gre_decap *task, struct rte_mbuf *mbuf, struct cpe_gre_data *table)
+{
+ struct ether_hdr *peth = rte_pktmbuf_mtod(mbuf, struct ether_hdr *);
+ struct ipv4_hdr *pip = (struct ipv4_hdr *)(peth + 1);
+ uint16_t ip_len = rte_be_to_cpu_16(pip->total_length);
+
+ struct cpe_gre_key key;
+ ether_addr_copy(&peth->d_addr, &key.clt_mac);
+
+#ifdef GRE_TP
+ /* policing enabled */
+ if (task->cycles_per_byte) {
+ const uint16_t pkt_size = rte_pktmbuf_pkt_len(mbuf) + ETHER_CRC_LEN;
+ uint64_t tsc_now = rte_rdtsc();
+ if (table->tp_tbsize < pkt_size) {
+ uint64_t cycles_diff = tsc_now - table->tp_tsc;
+ double dB = ((double)cycles_diff) / task->cycles_per_byte;
+ if (dB > (double)task->tb_size) {
+ dB = task->tb_size;
+ }
+ if ((table->tp_tbsize + dB) >= pkt_size) {
+ table->tp_tbsize += dB;
+ table->tp_tsc = tsc_now;
+ }
+ else {
+ TASK_STATS_ADD_DROP_DISCARD(&task->base.aux->stats, 1);
+ return OUT_DISCARD;
+ }
+ }
+ table->tp_tbsize -= pkt_size;
+ }
+#endif /* GRE_TP */
+
+ /* reuse ethernet header from payload, retain payload (ip) in
+ case of DO_ENC_IP_OVER_GRE */
+ peth = (struct ether_hdr *)rte_pktmbuf_prepend(mbuf, PKT_PREPEND_LEN);
+ PREFETCH0(peth);
+ ip_len += PKT_PREPEND_LEN;
+
+ pip = (struct ipv4_hdr *)(peth + 1);
+ struct gre_hdr *pgre = (struct gre_hdr *)(pip + 1);
+
+ struct ether_hdr eth = {
+ .d_addr = {.addr_bytes = {0x0A, 0x0A, 0x0A, 0xC8, 0x00, 0x02}},
+ .s_addr = {.addr_bytes = {0x0A, 0x0A, 0x0A, 0xC8, 0x00, 0x01}},
+ .ether_type = ETYPE_IPv4
+ };
+ rte_memcpy(peth, &eth, sizeof(struct ether_hdr));
+
+ rte_memcpy(pgre, &gre_hdr_proto, sizeof(struct gre_hdr));
+#if DO_ENC_ETH_OVER_GRE
+ pgre->type = ETYPE_EoGRE;
+#elif DO_ENC_IP_OVER_GRE
+ pgre->type = ETYPE_IPv4;
+#endif
+ pgre->gre_id = table->gre_id;
+
+ rte_memcpy(pip, &tunnel_ip_proto, sizeof(struct ipv4_hdr));
+ pip->src_addr = 0x02010a0a; //emulate port ip
+ pip->dst_addr = table->cpe_ip;
+ pip->total_length = rte_cpu_to_be_16(ip_len);
+
+ if (task->runtime_flags & TASK_TX_CRC) {
+ prox_ip_cksum(mbuf, pip, sizeof(struct ether_hdr), sizeof(struct ipv4_hdr), task->offload_crc);
+ }
+
+ return 0;
+}
+
+void update_arp_entries_gre(void *data)
+{
+ uint64_t cur_tsc = rte_rdtsc();
+ struct task_gre_decap *task = (struct task_gre_decap *)data;
+
+#if RTE_VERSION >= RTE_VERSION_NUM(2,1,0,0)
+ // rte_hash_iterate might take a long time if no entries found => we should not use it here
+ // struct rte_hash is now internal.....
+ // => Not implemented
+#else
+ uint32_t *sig_bucket = (hash_sig_t *)&(task->cpe_gre_hash->sig_tbl[task->bucket_index * task->cpe_gre_hash->sig_tbl_bucket_size]);
+ uint32_t table_index = task->bucket_index * task->cpe_gre_hash->bucket_entries;
+
+ uint8_t *entry_bucket =
+ (uint8_t *) & task->cpe_gre_hash->key_tbl[task->bucket_index * task->cpe_gre_hash->bucket_entries * task->cpe_gre_hash->key_tbl_key_size];
+
+ for (uint32_t pos = 0; pos < task->cpe_gre_hash->bucket_entries; ++pos, ++table_index) {
+ struct cpe_gre_entry *key = (struct cpe_gre_entry *)&entry_bucket[pos * task->cpe_gre_hash->key_tbl_key_size];
+ if (task->cpe_gre_data[table_index].tsc < cur_tsc) {
+ sig_bucket[pos] = 0;
+ task->cpe_gre_data[table_index].tsc = UINT64_MAX;
+ }
+ }
+ ++task->bucket_index;
+ task->bucket_index &= task->cpe_gre_hash->bucket_bitmask;
+#endif
+}