cgnat test case added
[samplevnf.git] / VNFs / DPPD-PROX / handle_swap.c
index 457c2fa..094199e 100644 (file)
@@ -1,5 +1,5 @@
 /*
-// Copyright (c) 2010-2017 Intel Corporation
+// Copyright (c) 2010-2020 Intel Corporation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
 
 #include <rte_mbuf.h>
 #include <rte_udp.h>
+#include <rte_icmp.h>
 
 #include "task_init.h"
 #include "task_base.h"
@@ -26,6 +27,7 @@
 #include "qinq.h"
 #include "gre.h"
 #include "prefetch.h"
+#include "defines.h"
 #include "igmp.h"
 #include "prox_cksum.h"
 #include "prox_compat.h"
@@ -38,6 +40,10 @@ struct task_swap {
        uint8_t src_dst_mac[12];
        uint32_t local_ipv4;
        int offload_crc;
+       uint64_t last_echo_req_rcvd_tsc;
+       uint64_t last_echo_rep_rcvd_tsc;
+       uint32_t n_echo_req;
+       uint32_t n_echo_rep;
 };
 
 #define NB_IGMP_MBUF           1024
@@ -55,7 +61,7 @@ static void write_src_and_dst_mac(struct task_swap *task, struct rte_mbuf *mbuf)
                rte_memcpy(hdr, task->src_dst_mac, sizeof(task->src_dst_mac));
        } else {
                hdr = rte_pktmbuf_mtod(mbuf, prox_rte_ether_hdr *);
-               if (likely((task->runtime_flags & TASK_ARG_SRC_MAC_SET) == 0)) {
+               if (unlikely((task->runtime_flags & TASK_ARG_SRC_MAC_SET) == 0)) {
                        /* dst mac will be used as src mac */
                        prox_rte_ether_addr_copy(&hdr->d_addr, &mac);
                }
@@ -65,7 +71,7 @@ static void write_src_and_dst_mac(struct task_swap *task, struct rte_mbuf *mbuf)
                else
                        prox_rte_ether_addr_copy(&hdr->s_addr, &hdr->d_addr);
 
-               if (unlikely(task->runtime_flags & TASK_ARG_SRC_MAC_SET)) {
+               if (likely(task->runtime_flags & TASK_ARG_SRC_MAC_SET)) {
                        prox_rte_ether_addr_copy((prox_rte_ether_addr *)&task->src_dst_mac[6], &hdr->s_addr);
                } else {
                        prox_rte_ether_addr_copy(&mac, &hdr->s_addr);
@@ -79,6 +85,21 @@ static inline void build_mcast_mac(uint32_t ip, prox_rte_ether_addr *dst_mac)
        memcpy(dst_mac, &mac, sizeof(prox_rte_ether_addr));
 }
 
+static inline void build_icmp_reply_message(struct task_base *tbase, struct rte_mbuf *mbuf)
+{
+       struct task_swap *task = (struct task_swap *)tbase;
+       prox_rte_ether_hdr *hdr = rte_pktmbuf_mtod(mbuf, prox_rte_ether_hdr *);
+       prox_rte_ether_addr dst_mac;
+       prox_rte_ether_addr_copy(&hdr->s_addr, &dst_mac);
+       prox_rte_ether_addr_copy(&hdr->d_addr, &hdr->s_addr);
+       prox_rte_ether_addr_copy(&dst_mac, &hdr->d_addr);
+       prox_rte_ipv4_hdr *ip_hdr = (prox_rte_ipv4_hdr *)(hdr + 1);
+       ip_hdr->dst_addr = ip_hdr->src_addr;
+       ip_hdr->src_addr = task->local_ipv4;
+       prox_rte_icmp_hdr *picmp = (prox_rte_icmp_hdr *)(ip_hdr + 1);
+       picmp->icmp_type = PROX_RTE_IP_ICMP_ECHO_REPLY;
+}
+
 static inline void build_igmp_message(struct task_base *tbase, struct rte_mbuf *mbuf, uint32_t ip, uint8_t igmp_message)
 {
        struct task_swap *task = (struct task_swap *)tbase;
@@ -113,6 +134,47 @@ static inline void build_igmp_message(struct task_base *tbase, struct rte_mbuf *
        prox_ip_udp_cksum(mbuf, ip_hdr, sizeof(prox_rte_ether_hdr), sizeof(prox_rte_ipv4_hdr), task->offload_crc);
 }
 
+static void stop_swap(struct task_base *tbase)
+{
+       struct task_swap *task = (struct task_swap *)tbase;
+       if (task->igmp_pool) {
+               rte_mempool_free(task->igmp_pool);
+               task->igmp_pool = NULL;
+       }
+}
+
+static void handle_ipv6(struct task_swap *task, struct rte_mbuf *mbufs, prox_rte_ipv6_hdr *ipv6_hdr, uint8_t *out)
+{
+       __m128i ip =  _mm_loadu_si128((__m128i*)&(ipv6_hdr->src_addr));
+       uint16_t port;
+       uint16_t payload_len;
+       prox_rte_udp_hdr *udp_hdr;
+
+       rte_mov16((uint8_t *)&(ipv6_hdr->src_addr), (uint8_t *)&(ipv6_hdr->dst_addr));  // Copy dst into src
+       rte_mov16((uint8_t *)&(ipv6_hdr->dst_addr), (uint8_t *)&ip);                    // Copy src into dst
+       switch(ipv6_hdr->proto) {
+               case IPPROTO_TCP:
+               case IPPROTO_UDP:
+                       payload_len = ipv6_hdr->payload_len;
+                       udp_hdr = (prox_rte_udp_hdr *)(ipv6_hdr + 1);
+                       if (unlikely(udp_hdr->dgram_len < payload_len)) {
+                               plog_warn("Unexpected L4 len (%u) versus L3 payload len (%u) in IPv6 packet\n", udp_hdr->dgram_len, payload_len);
+                               *out = OUT_DISCARD;
+                               break;
+                       }
+                       port = udp_hdr->dst_port;
+                       udp_hdr->dst_port = udp_hdr->src_port;
+                       udp_hdr->src_port = port;
+                       write_src_and_dst_mac(task, mbufs);
+                       *out = 0;
+                       break;
+               default:
+                       plog_warn("Unsupported next hop %u in IPv6 packet\n", ipv6_hdr->proto);
+                       *out = OUT_DISCARD;
+                       break;
+       }
+}
+
 static int handle_swap_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts)
 {
        struct task_swap *task = (struct task_swap *)tbase;
@@ -120,6 +182,7 @@ static int handle_swap_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, ui
        prox_rte_ether_addr mac;
        prox_rte_ipv4_hdr *ip_hdr;
        prox_rte_udp_hdr *udp_hdr;
+       prox_rte_ipv6_hdr *ipv6_hdr;
        struct gre_hdr *pgre;
        prox_rte_ipv4_hdr *inner_ip_hdr;
        uint32_t ip;
@@ -131,6 +194,7 @@ static int handle_swap_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, ui
        prox_rte_vlan_hdr *vlan;
        uint16_t j;
        struct igmpv2_hdr *pigmp;
+       prox_rte_icmp_hdr *picmp;
        uint8_t type;
 
        for (j = 0; j < n_pkts; ++j) {
@@ -153,6 +217,11 @@ static int handle_swap_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, ui
                        }
                        mpls_len += sizeof(struct mpls_hdr);
                        ip_hdr = (prox_rte_ipv4_hdr *)(mpls + 1);
+                       if (unlikely((ip_hdr->version_ihl >> 4) == 6)) {
+                               ipv6_hdr = (prox_rte_ipv6_hdr *)(ip_hdr);
+                               handle_ipv6(task, mbufs[j], ipv6_hdr, &out[j]);
+                               continue;
+                       }
                        break;
                case ETYPE_8021ad:
                        qinq = (struct qinq_hdr *)hdr;
@@ -161,20 +230,34 @@ static int handle_swap_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, ui
                                out[j] = OUT_DISCARD;
                                continue;
                        }
-                       ip_hdr = (prox_rte_ipv4_hdr *)(qinq + 1);
+                       if (qinq->ether_type == ETYPE_IPv4) {
+                               ip_hdr = (prox_rte_ipv4_hdr *)(qinq + 1);
+                       } else if (qinq->ether_type == ETYPE_IPv6) {
+                               ipv6_hdr = (prox_rte_ipv6_hdr *)(qinq + 1);
+                               handle_ipv6(task, mbufs[j], ipv6_hdr, &out[j]);
+                               continue;
+                       } else {
+                               plog_warn("Unsupported packet type\n");
+                               out[j] = OUT_DISCARD;
+                               continue;
+                       }
                        break;
                case ETYPE_VLAN:
                        vlan = (prox_rte_vlan_hdr *)(hdr + 1);
                        if (vlan->eth_proto == ETYPE_IPv4) {
                                ip_hdr = (prox_rte_ipv4_hdr *)(vlan + 1);
+                       } else if (vlan->eth_proto == ETYPE_IPv6) {
+                               ipv6_hdr = (prox_rte_ipv6_hdr *)(vlan + 1);
+                               handle_ipv6(task, mbufs[j], ipv6_hdr, &out[j]);
+                               continue;
                        } else if (vlan->eth_proto == ETYPE_VLAN) {
                                vlan = (prox_rte_vlan_hdr *)(vlan + 1);
                                if (vlan->eth_proto == ETYPE_IPv4) {
                                        ip_hdr = (prox_rte_ipv4_hdr *)(vlan + 1);
                                }
                                else if (vlan->eth_proto == ETYPE_IPv6) {
-                                       plog_warn("Unsupported IPv6\n");
-                                       out[j] = OUT_DISCARD;
+                                       ipv6_hdr = (prox_rte_ipv6_hdr *)(vlan + 1);
+                                       handle_ipv6(task, mbufs[j], ipv6_hdr, &out[j]);
                                        continue;
                                }
                                else {
@@ -192,8 +275,8 @@ static int handle_swap_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, ui
                        ip_hdr = (prox_rte_ipv4_hdr *)(hdr + 1);
                        break;
                case ETYPE_IPv6:
-                       plog_warn("Unsupported IPv6\n");
-                       out[j] = OUT_DISCARD;
+                       ipv6_hdr = (prox_rte_ipv6_hdr *)(hdr + 1);
+                       handle_ipv6(task, mbufs[j], ipv6_hdr, &out[j]);
                        continue;
                case ETYPE_LLDP:
                        out[j] = OUT_DISCARD;
@@ -204,7 +287,13 @@ static int handle_swap_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, ui
                        continue;
                }
                // TODO 2 : check packet is long enough for Ethernet + IP + UDP + extra header (VLAN, MPLS, ...)
+               // IPv4 packet
+
                ip = ip_hdr->dst_addr;
+               if (unlikely((ip_hdr->version_ihl >> 4) != 4)) {
+                       out[j] = OUT_DISCARD;
+                       continue;
+               }
 
                switch (ip_hdr->next_proto_id) {
                case IPPROTO_GRE:
@@ -226,7 +315,7 @@ static int handle_swap_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, ui
                        break;
                case IPPROTO_UDP:
                case IPPROTO_TCP:
-                       if (task->igmp_address && PROX_RTE_IS_IPV4_MCAST(rte_be_to_cpu_32(ip))) {
+                       if (unlikely(task->igmp_address && PROX_RTE_IS_IPV4_MCAST(rte_be_to_cpu_32(ip)))) {
                                out[j] = OUT_DISCARD;
                                continue;
                        }
@@ -239,6 +328,39 @@ static int handle_swap_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, ui
                        udp_hdr->src_port = port;
                        write_src_and_dst_mac(task, mbufs[j]);
                        break;
+               case IPPROTO_ICMP:
+                       picmp = (prox_rte_icmp_hdr *)(ip_hdr + 1);
+                       type = picmp->icmp_type;
+                       if (type == PROX_RTE_IP_ICMP_ECHO_REQUEST) {
+                               if (ip_hdr->dst_addr == task->local_ipv4) {
+                                       task->n_echo_req++;
+                                       if (rte_rdtsc() - task->last_echo_req_rcvd_tsc > rte_get_tsc_hz()) {
+                                               plog_info("Received %u Echo Request on IP "IPv4_BYTES_FMT" (last received from IP "IPv4_BYTES_FMT")\n", task->n_echo_req, IPv4_BYTES(((uint8_t*)&ip_hdr->dst_addr)), IPv4_BYTES(((uint8_t*)&ip_hdr->src_addr)));
+                                               task->n_echo_req = 0;
+                                               task->last_echo_req_rcvd_tsc = rte_rdtsc();
+                                       }
+                                       build_icmp_reply_message(tbase, mbufs[j]);
+                               } else {
+                                       out[j] = OUT_DISCARD;
+                                       continue;
+                               }
+                       } else if (type == PROX_RTE_IP_ICMP_ECHO_REPLY) {
+                               if (ip_hdr->dst_addr == task->local_ipv4) {
+                                       task->n_echo_rep++;
+                                       if (rte_rdtsc() - task->last_echo_rep_rcvd_tsc > rte_get_tsc_hz()) {
+                                               plog_info("Received %u Echo Reply on IP "IPv4_BYTES_FMT" (last received from IP "IPv4_BYTES_FMT")\n", task->n_echo_rep, IPv4_BYTES(((uint8_t*)&ip_hdr->dst_addr)), IPv4_BYTES(((uint8_t*)&ip_hdr->src_addr)));
+                                               task->n_echo_rep = 0;
+                                               task->last_echo_rep_rcvd_tsc = rte_rdtsc();
+                                       }
+                               } else {
+                                       out[j] = OUT_DISCARD;
+                                       continue;
+                               }
+                       } else {
+                               out[j] = OUT_DISCARD;
+                               continue;
+                       }
+                       break;
                case IPPROTO_IGMP:
                        pigmp = (struct igmpv2_hdr *)(ip_hdr + 1);
                        // TODO: check packet len
@@ -378,6 +500,7 @@ static struct task_init task_init_swap = {
        .handle = handle_swap_bulk,
        .flag_features = 0,
        .size = sizeof(struct task_swap),
+       .stop_last = stop_swap
 };
 
 __attribute__((constructor)) static void reg_task_swap(void)