summaryrefslogtreecommitdiffstats
path: root/VNFs/DPPD-PROX/handle_ipv6_tunnel.c
diff options
context:
space:
mode:
Diffstat (limited to 'VNFs/DPPD-PROX/handle_ipv6_tunnel.c')
-rw-r--r--VNFs/DPPD-PROX/handle_ipv6_tunnel.c466
1 files changed, 466 insertions, 0 deletions
diff --git a/VNFs/DPPD-PROX/handle_ipv6_tunnel.c b/VNFs/DPPD-PROX/handle_ipv6_tunnel.c
new file mode 100644
index 00000000..a92f9cdc
--- /dev/null
+++ b/VNFs/DPPD-PROX/handle_ipv6_tunnel.c
@@ -0,0 +1,466 @@
+/*
+// 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_ip.h>
+#include <rte_udp.h>
+#include <rte_tcp.h>
+#include <rte_table_hash.h>
+#include <rte_ether.h>
+#include <rte_version.h>
+#include <rte_byteorder.h>
+
+#include "prox_lua.h"
+#include "prox_lua_types.h"
+
+#include "tx_pkt.h"
+#include "task_init.h"
+#include "task_base.h"
+#include "prox_port_cfg.h"
+#include "prefetch.h"
+#include "lconf.h"
+#include "hash_utils.h"
+#include "etypes.h"
+#include "prox_cksum.h"
+#include "defines.h"
+#include "log.h"
+#include "quit.h"
+#include "prox_cfg.h"
+#include "parse_utils.h"
+#include "cfgfile.h"
+#include "prox_shared.h"
+
+#if RTE_VERSION < RTE_VERSION_NUM(1,8,0,0)
+#define IPPROTO_IPIP IPPROTO_IPV4
+#endif
+
+struct ipv6_tun_dest {
+ struct ipv6_addr dst_addr;
+ struct ether_addr dst_mac;
+};
+
+typedef enum ipv6_tun_dir_t {
+ TUNNEL_DIR_ENCAP = 0,
+ TUNNEL_DIR_DECAP = 1,
+} ipv6_tun_dir_t;
+
+struct task_ipv6_tun_base {
+ struct task_base base;
+ struct ether_addr src_mac;
+ uint8_t core_nb;
+ uint64_t keys[64];
+ struct rte_mbuf* fake_packets[64];
+ uint16_t lookup_port_mask; // Mask used before looking up the port
+ void* lookup_table; // Fast lookup table for bindings
+ uint32_t runtime_flags;
+ int offload_crc;
+};
+
+struct task_ipv6_decap {
+ struct task_ipv6_tun_base base;
+ struct ether_addr dst_mac;
+};
+
+struct task_ipv6_encap {
+ struct task_ipv6_tun_base base;
+ uint32_t ipaddr;
+ struct ipv6_addr local_endpoint_addr;
+ uint8_t tunnel_hop_limit;
+};
+
+#define IPv6_VERSION 6
+#ifndef IPPROTO_IPV4
+#define IPPROTO_IPV4 4
+#endif
+
+#define MAKE_KEY_FROM_FIELDS(ipv4_addr, port, port_mask) ( ((uint64_t)ipv4_addr << 16) | (port & port_mask) )
+
+static int handle_ipv6_decap_bulk(struct task_base* tbase, struct rte_mbuf** rx_mbuf, const uint16_t n_pkts);
+static int handle_ipv6_encap_bulk(struct task_base* tbase, struct rte_mbuf** rx_mbuf, const uint16_t n_pkts);
+
+static void init_lookup_table(struct task_ipv6_tun_base* ptask, struct task_args *targ)
+{
+ const int socket_id = rte_lcore_to_socket_id(targ->lconf->id);
+
+ /* The lookup table is a per-core data structure to reduce the
+ memory footprint and improve cache utilization. Since
+ operations on the hash table are not safe, the data
+ structure can't be used on a per socket or on a system wide
+ basis. */
+ ptask->lookup_table = prox_sh_find_core(targ->lconf->id, "ipv6_binding_table");
+ if (NULL == ptask->lookup_table) {
+ struct ipv6_tun_binding_table *table;
+ PROX_PANIC(!strcmp(targ->tun_bindings, ""), "No tun bindings specified\n");
+ int ret = lua_to_ip6_tun_binding(prox_lua(), GLOBAL, targ->tun_bindings, socket_id, &table);
+ PROX_PANIC(ret, "Failed to read tun_bindings config:\n %s\n", get_lua_to_errors());
+
+ struct rte_table_hash_key8_ext_params table_hash_params = {
+ .n_entries = (table->num_binding_entries * 4),
+ .n_entries_ext = (table->num_binding_entries * 2) >> 1,
+ .f_hash = hash_crc32,
+ .seed = 0,
+ .signature_offset = HASH_METADATA_OFFSET(8), // Ignored for dosig tables
+ .key_offset = HASH_METADATA_OFFSET(0),
+ };
+ plogx_info("IPv6 Tunnel allocating lookup table on socket %d\n", socket_id);
+ ptask->lookup_table = rte_table_hash_key8_ext_dosig_ops.
+ f_create(&table_hash_params, socket_id, sizeof(struct ipv6_tun_dest));
+ PROX_PANIC(ptask->lookup_table == NULL, "Error creating IPv6 Tunnel lookup table");
+
+ for (unsigned idx = 0; idx < table->num_binding_entries; idx++) {
+ int key_found = 0;
+ void* entry_in_hash = NULL;
+ struct ipv6_tun_dest data;
+ struct ipv6_tun_binding_entry* entry = &table->entry[idx];
+ uint64_t key = MAKE_KEY_FROM_FIELDS(rte_cpu_to_be_32(entry->public_ipv4), entry->public_port, ptask->lookup_port_mask);
+ rte_memcpy(&data.dst_addr, &entry->endpoint_addr, sizeof(struct ipv6_addr));
+ rte_memcpy(&data.dst_mac, &entry->next_hop_mac, sizeof(struct ether_addr));
+
+ int ret = rte_table_hash_key8_ext_dosig_ops.f_add(ptask->lookup_table, &key, &data, &key_found, &entry_in_hash);
+ PROX_PANIC(ret, "Error adding entry (%d) to binding lookup table", idx);
+ PROX_PANIC(key_found, "key_found!!! for idx=%d\n", idx);
+
+#ifdef DBG_IPV6_TUN_BINDING
+ plog_info("Bind: %x:0x%x (port_mask 0x%x) key=0x%"PRIx64"\n", entry->public_ipv4, entry->public_port, ptask->lookup_port_mask, key);
+ plog_info(" -> "IPv6_BYTES_FMT" ("MAC_BYTES_FMT")\n", IPv6_BYTES(entry->endpoint_addr.bytes), MAC_BYTES(entry->next_hop_mac.addr_bytes));
+ plog_info(" -> "IPv6_BYTES_FMT" ("MAC_BYTES_FMT")\n", IPv6_BYTES(data.dst_addr.bytes), MAC_BYTES(data.dst_mac.addr_bytes));
+ plog_info(" -> entry_in_hash=%p\n", entry_in_hash);
+#endif
+ }
+ plogx_info("IPv6 Tunnel created %d lookup table entries\n", table->num_binding_entries);
+
+ prox_sh_add_core(targ->lconf->id, "ipv6_binding_table", ptask->lookup_table);
+ }
+}
+
+static void init_task_ipv6_tun_base(struct task_ipv6_tun_base* tun_base, struct task_args* targ)
+{
+ memcpy(&tun_base->src_mac, find_reachable_port(targ), sizeof(tun_base->src_mac));
+
+ tun_base->lookup_port_mask = targ->lookup_port_mask; // Mask used before looking up the port
+
+ init_lookup_table(tun_base, targ);
+
+ for (uint32_t i = 0; i < 64; ++i) {
+ tun_base->fake_packets[i] = (struct rte_mbuf*)((uint8_t*)&tun_base->keys[i] - sizeof (struct rte_mbuf));
+ }
+
+ plogx_info("IPv6 Tunnel MAC="MAC_BYTES_FMT" port_mask=0x%x\n",
+ MAC_BYTES(tun_base->src_mac.addr_bytes), tun_base->lookup_port_mask);
+
+ struct prox_port_cfg *port = find_reachable_port(targ);
+ if (port) {
+ tun_base->offload_crc = port->capabilities.tx_offload_cksum;
+ }
+}
+
+static void init_task_ipv6_decap(struct task_base* tbase, struct task_args* targ)
+{
+ struct task_ipv6_decap* tun_task = (struct task_ipv6_decap*)tbase;
+ struct task_ipv6_tun_base* tun_base = (struct task_ipv6_tun_base*)tun_task;
+
+ init_task_ipv6_tun_base(tun_base, targ);
+ tun_base->runtime_flags = targ->runtime_flags;
+
+ memcpy(&tun_task->dst_mac, &targ->edaddr, sizeof(tun_task->dst_mac));
+}
+
+static void init_task_ipv6_encap(struct task_base* tbase, struct task_args* targ)
+{
+ struct task_ipv6_encap* tun_task = (struct task_ipv6_encap*)tbase;
+ struct task_ipv6_tun_base *tun_base = (struct task_ipv6_tun_base*)tun_task;
+
+ init_task_ipv6_tun_base(tun_base, targ);
+
+ rte_memcpy(&tun_task->local_endpoint_addr, &targ->local_ipv6, sizeof(tun_task->local_endpoint_addr));
+ tun_task->tunnel_hop_limit = targ->tunnel_hop_limit;
+ tun_base->runtime_flags = targ->runtime_flags;
+}
+
+static struct task_init task_init_ipv6_decap = {
+ .mode_str = "ipv6_decap",
+ .init = init_task_ipv6_decap,
+ .handle = handle_ipv6_decap_bulk,
+ .size = sizeof(struct task_ipv6_decap)
+};
+
+static struct task_init task_init_ipv6_encap = {
+ .mode_str = "ipv6_encap",
+ .init = init_task_ipv6_encap,
+ .handle = handle_ipv6_encap_bulk,
+ .size = sizeof(struct task_ipv6_encap)
+};
+
+__attribute__((constructor)) static void reg_task_ipv6_decap(void)
+{
+ reg_task(&task_init_ipv6_decap);
+}
+
+__attribute__((constructor)) static void reg_task_ipv6_encap(void)
+{
+ reg_task(&task_init_ipv6_encap);
+}
+
+static inline uint8_t handle_ipv6_decap(struct task_ipv6_decap* ptask, struct rte_mbuf* rx_mbuf, struct ipv6_tun_dest* tun_dest);
+static inline uint8_t handle_ipv6_encap(struct task_ipv6_encap* ptask, struct rte_mbuf* rx_mbuf, struct ipv6_tun_dest* tun_dest);
+
+static inline int extract_key_fields( __attribute__((unused)) struct task_ipv6_tun_base* ptask, struct ipv4_hdr* pip4, ipv6_tun_dir_t dir, uint32_t* pAddr, uint16_t* pPort)
+{
+ *pAddr = (dir == TUNNEL_DIR_DECAP) ? pip4->src_addr : pip4->dst_addr;
+
+ if (pip4->next_proto_id == IPPROTO_UDP) {
+ struct udp_hdr* pudp = (struct udp_hdr *)(pip4 + 1);
+ *pPort = rte_be_to_cpu_16((dir == TUNNEL_DIR_DECAP) ? pudp->src_port : pudp->dst_port);
+ }
+ else if (pip4->next_proto_id == IPPROTO_TCP) {
+ struct tcp_hdr* ptcp = (struct tcp_hdr *)(pip4 + 1);
+ *pPort = rte_be_to_cpu_16((dir == TUNNEL_DIR_DECAP) ? ptcp->src_port : ptcp->dst_port);
+ }
+ else {
+ plog_warn("IPv6 Tunnel: IPv4 packet of unexpected type proto_id=0x%x\n", pip4->next_proto_id);
+ *pPort = 0xffff;
+ return -1;
+ }
+
+ return 0;
+}
+
+static inline void extract_key(struct task_ipv6_tun_base* ptask, struct ipv4_hdr* pip4, ipv6_tun_dir_t dir, uint64_t* pkey)
+{
+ uint32_t lookup_addr;
+ uint16_t lookup_port;
+
+ if (unlikely( extract_key_fields(ptask, pip4, dir, &lookup_addr, &lookup_port))) {
+ plog_warn("IPv6 Tunnel: Unable to extract fields from packet\n");
+ *pkey = 0xffffffffL;
+ return;
+ }
+
+ *pkey = MAKE_KEY_FROM_FIELDS(lookup_addr, lookup_port, ptask->lookup_port_mask);
+}
+
+static inline struct ipv4_hdr* get_ipv4_decap(struct rte_mbuf *mbuf)
+{
+ struct ether_hdr* peth = rte_pktmbuf_mtod(mbuf, struct ether_hdr *);
+ struct ipv6_hdr* pip6 = (struct ipv6_hdr *)(peth + 1);
+ struct ipv4_hdr* pip4 = (struct ipv4_hdr*) (pip6 + 1); // TODO - Skip Option headers
+
+ return pip4;
+}
+
+static inline struct ipv4_hdr* get_ipv4_encap(struct rte_mbuf *mbuf)
+{
+ struct ether_hdr* peth = rte_pktmbuf_mtod(mbuf, struct ether_hdr *);
+ struct ipv4_hdr* pip4 = (struct ipv4_hdr *)(peth + 1);
+
+ return pip4;
+}
+
+static inline void extract_key_decap(struct task_ipv6_tun_base* ptask, struct rte_mbuf *mbuf, uint64_t* pkey)
+{
+ extract_key(ptask, get_ipv4_decap(mbuf), TUNNEL_DIR_DECAP, pkey);
+}
+
+static inline void extract_key_decap_bulk(struct task_ipv6_tun_base* ptask, struct rte_mbuf **mbufs, uint16_t n_pkts)
+{
+ for (uint16_t j = 0; j < n_pkts; ++j) {
+ extract_key_decap(ptask, mbufs[j], &ptask->keys[j]);
+ }
+}
+
+static inline void extract_key_encap(struct task_ipv6_tun_base* ptask, struct rte_mbuf *mbuf, uint64_t* pkey)
+{
+ extract_key(ptask, get_ipv4_encap(mbuf), TUNNEL_DIR_ENCAP, pkey);
+}
+
+static inline void extract_key_encap_bulk(struct task_ipv6_tun_base* ptask, struct rte_mbuf **mbufs, uint16_t n_pkts)
+{
+ for (uint16_t j = 0; j < n_pkts; ++j) {
+ extract_key_encap(ptask, mbufs[j], &ptask->keys[j]);
+ }
+}
+
+__attribute__((cold)) static void handle_error(struct task_ipv6_tun_base* ptask, struct rte_mbuf* mbuf, ipv6_tun_dir_t dir)
+{
+ uint32_t lookup_addr;
+ uint16_t lookup_port;
+ uint64_t key;
+
+ struct ipv4_hdr* pip4 = (dir == TUNNEL_DIR_DECAP) ? get_ipv4_decap(mbuf) : get_ipv4_encap(mbuf);
+ extract_key_fields(ptask, pip4, dir, &lookup_addr, &lookup_port);
+ extract_key(ptask, pip4, dir, &key);
+
+ plog_warn("IPv6 Tunnel (%s) lookup failed for "IPv4_BYTES_FMT":%d [key=0x%"PRIx64"]\n",
+ (dir == TUNNEL_DIR_DECAP) ? "decap" : "encap",
+ IPv4_BYTES(((unsigned char*)&lookup_addr)), lookup_port, key);
+}
+
+static int handle_ipv6_decap_bulk(struct task_base* tbase, struct rte_mbuf** mbufs, const uint16_t n_pkts)
+{
+ struct task_ipv6_decap* task = (struct task_ipv6_decap *)tbase;
+ uint64_t pkts_mask = RTE_LEN2MASK(n_pkts, uint64_t);
+ struct ipv6_tun_dest* entries[64];
+ uint8_t out[MAX_PKT_BURST];
+ uint64_t lookup_hit_mask;
+ uint16_t n_kept = 0;
+
+ prefetch_pkts(mbufs, n_pkts);
+
+ // Lookup to verify packets are valid for their respective tunnels (their sending lwB4)
+ extract_key_decap_bulk(&task->base, mbufs, n_pkts);
+ rte_table_hash_key8_ext_dosig_ops.f_lookup(task->base.lookup_table, task->base.fake_packets, pkts_mask, &lookup_hit_mask, (void**)entries);
+
+ if (likely(lookup_hit_mask == pkts_mask)) {
+ for (uint16_t j = 0; j < n_pkts; ++j) {
+ out[j] = handle_ipv6_decap(task, mbufs[j], entries[j]);
+ }
+ }
+ else {
+ for (uint16_t j = 0; j < n_pkts; ++j) {
+ if (unlikely(!((lookup_hit_mask >> j) & 0x1))) {
+ handle_error(&task->base, mbufs[j], TUNNEL_DIR_DECAP);
+ out[j] = OUT_DISCARD;
+ continue;
+ }
+ out[j] = handle_ipv6_decap(task, mbufs[j], entries[j]);
+ }
+ }
+
+ return task->base.base.tx_pkt(tbase, mbufs, n_pkts, out);
+}
+
+static int handle_ipv6_encap_bulk(struct task_base* tbase, struct rte_mbuf** mbufs, const uint16_t n_pkts)
+{
+ struct task_ipv6_encap* task = (struct task_ipv6_encap *)tbase;
+ uint64_t pkts_mask = RTE_LEN2MASK(n_pkts, uint64_t);
+ struct ipv6_tun_dest* entries[64];
+ uint64_t lookup_hit_mask;
+ uint8_t out[MAX_PKT_BURST];
+ uint16_t n_kept = 0;
+
+ prefetch_first(mbufs, n_pkts);
+
+ extract_key_encap_bulk(&task->base, mbufs, n_pkts);
+ rte_table_hash_key8_ext_dosig_ops.f_lookup(task->base.lookup_table, task->base.fake_packets, pkts_mask, &lookup_hit_mask, (void**)entries);
+
+ if (likely(lookup_hit_mask == pkts_mask)) {
+ for (uint16_t j = 0; j < n_pkts; ++j) {
+ out[j] = handle_ipv6_encap(task, mbufs[j], entries[j]);
+ }
+ }
+ else {
+ for (uint16_t j = 0; j < n_pkts; ++j) {
+ if (unlikely(!((lookup_hit_mask >> j) & 0x1))) {
+ handle_error(&task->base, mbufs[j], TUNNEL_DIR_ENCAP);
+ out[j] = OUT_DISCARD;
+ continue;
+ }
+ out[j] = handle_ipv6_encap(task, mbufs[j], entries[j]);
+ }
+ }
+
+ return task->base.base.tx_pkt(tbase, mbufs, n_pkts, out);
+}
+
+static inline uint8_t handle_ipv6_decap(struct task_ipv6_decap* ptask, struct rte_mbuf* rx_mbuf, __attribute__((unused)) struct ipv6_tun_dest* tun_dest)
+{
+ struct ether_hdr* peth = rte_pktmbuf_mtod(rx_mbuf, struct ether_hdr *);
+
+ if (unlikely(peth->ether_type != ETYPE_IPv6)) {
+ plog_warn("Received non IPv6 packet on ipv6 tunnel port\n");
+ // Drop packet
+ return OUT_DISCARD;
+ }
+
+ struct ipv6_hdr* pip6 = (struct ipv6_hdr *)(peth + 1);
+ int ipv6_hdr_len = sizeof(struct ipv6_hdr);
+
+ // TODO - Skip over any IPv6 Extension Header:
+ // If pip6->next_header is in (0, 43, 44, 50, 51, 60, 135), skip ahead pip->hdr_ext_len
+ // bytes and repeat. Increase ipv6_hdr_len with as much, each time.
+
+ if (unlikely(pip6->proto != IPPROTO_IPIP)) {
+ plog_warn("Received non IPv4 content within IPv6 tunnel packet\n");
+ // Drop packet
+ return OUT_DISCARD;
+ }
+
+ // Discard IPv6 encapsulation
+ rte_pktmbuf_adj(rx_mbuf, ipv6_hdr_len);
+ peth = rte_pktmbuf_mtod(rx_mbuf, struct ether_hdr *);
+
+ // Restore Ethernet header
+ ether_addr_copy(&ptask->base.src_mac, &peth->s_addr);
+ ether_addr_copy(&ptask->dst_mac, &peth->d_addr);
+ peth->ether_type = ETYPE_IPv4;
+
+ return 0;
+}
+
+static inline uint8_t handle_ipv6_encap(struct task_ipv6_encap* ptask, struct rte_mbuf* rx_mbuf, __attribute__((unused)) struct ipv6_tun_dest* tun_dest)
+{
+ //plog_info("Found tunnel endpoint:"IPv6_BYTES_FMT" ("MAC_BYTES_FMT")\n", IPv6_BYTES(tun_dest->dst_addr), MAC_BYTES(tun_dest->dst_mac.addr_bytes));
+
+ struct ether_hdr* peth = (struct ether_hdr *)(rte_pktmbuf_mtod(rx_mbuf, struct ether_hdr *));
+ struct ipv4_hdr* pip4 = (struct ipv4_hdr *)(peth + 1);
+ uint16_t ipv4_length = rte_be_to_cpu_16(pip4->total_length);
+ struct task_ipv6_tun_base* tun_base = (struct task_ipv6_tun_base*)ptask;
+
+ if (unlikely((pip4->version_ihl >> 4) != 4)) {
+ plog_warn("Received non IPv4 packet at ipv6 tunnel input\n");
+ // Drop packet
+ return OUT_DISCARD;
+ }
+
+ if (pip4->time_to_live) {
+ pip4->time_to_live--;
+ }
+ else {
+ plog_info("TTL = 0 => Dropping\n");
+ return OUT_DISCARD;
+ }
+ pip4->hdr_checksum = 0;
+
+ // Remove padding if any (we don't want to encapsulate garbage at end of IPv4 packet)
+ int padding = rte_pktmbuf_pkt_len(rx_mbuf) - (ipv4_length + sizeof(struct ether_hdr));
+ if (unlikely(padding > 0)) {
+ rte_pktmbuf_trim(rx_mbuf, padding);
+ }
+
+ // Encapsulate
+ const int extra_space = sizeof(struct ipv6_hdr);
+ peth = (struct ether_hdr *)rte_pktmbuf_prepend(rx_mbuf, extra_space);
+
+ // Ethernet Header
+ ether_addr_copy(&ptask->base.src_mac, &peth->s_addr);
+ ether_addr_copy(&tun_dest->dst_mac, &peth->d_addr);
+ peth->ether_type = ETYPE_IPv6;
+
+ // Set up IPv6 Header
+ struct ipv6_hdr* pip6 = (struct ipv6_hdr *)(peth + 1);
+ pip6->vtc_flow = rte_cpu_to_be_32(IPv6_VERSION << 28);
+ pip6->proto = IPPROTO_IPIP;
+ pip6->payload_len = rte_cpu_to_be_16(ipv4_length);
+ pip6->hop_limits = ptask->tunnel_hop_limit;
+ rte_memcpy(pip6->dst_addr, &tun_dest->dst_addr, sizeof(pip6->dst_addr));
+ rte_memcpy(pip6->src_addr, &ptask->local_endpoint_addr, sizeof(pip6->src_addr));
+
+ if (tun_base->runtime_flags & TASK_TX_CRC) {
+ // We modified the TTL in the IPv4 header, hence have to recompute the IPv4 checksum
+#define TUNNEL_L2_LEN (sizeof(struct ether_hdr) + sizeof(struct ipv6_hdr))
+ prox_ip_cksum(rx_mbuf, pip4, TUNNEL_L2_LEN, sizeof(struct ipv4_hdr), ptask->base.offload_crc);
+ }
+ return 0;
+}