Support packets in flight
[samplevnf.git] / VNFs / DPPD-PROX / handle_nat.c
1 /*
2 // Copyright (c) 2010-2017 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //     http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16
17 #include <rte_mbuf.h>
18 #include <rte_hash.h>
19 #include <rte_hash_crc.h>
20 #include <rte_ether.h>
21 #include <rte_ip.h>
22 #include <rte_version.h>
23 #include <rte_byteorder.h>
24
25 #include "prox_lua_types.h"
26 #include "prox_lua.h"
27 #include "prox_malloc.h"
28 #include "prox_cksum.h"
29 #include "prefetch.h"
30 #include "etypes.h"
31 #include "log.h"
32 #include "quit.h"
33 #include "task_init.h"
34 #include "task_base.h"
35 #include "lconf.h"
36 #include "log.h"
37 #include "prox_port_cfg.h"
38
39 struct task_nat {
40         struct task_base base;
41         struct rte_hash  *hash;
42         uint32_t         *entries;
43         int              use_src;
44         int              offload_crc;
45 };
46
47 struct pkt_eth_ipv4 {
48         prox_rte_ether_hdr ether_hdr;
49         prox_rte_ipv4_hdr  ipv4_hdr;
50 } __attribute__((packed)) __attribute__((__aligned__(2)));
51
52 static int handle_nat(struct task_nat *task, struct rte_mbuf *mbuf)
53 {
54         uint32_t *ip_addr;
55         struct pkt_eth_ipv4 *pkt = rte_pktmbuf_mtod(mbuf, struct pkt_eth_ipv4 *);
56         int ret;
57
58         /* Currently, only support eth/ipv4 packets */
59         if (pkt->ether_hdr.ether_type != ETYPE_IPv4)
60                 return OUT_DISCARD;
61         if (task->use_src)
62                 ip_addr = &(pkt->ipv4_hdr.src_addr);
63         else
64                 ip_addr = &(pkt->ipv4_hdr.dst_addr);
65
66         ret = rte_hash_lookup(task->hash, ip_addr);
67
68         /* Drop all packets for which no translation has been
69            configured. */
70         if (ret < 0)
71                 return OUT_DISCARD;
72
73         *ip_addr = task->entries[ret];
74         prox_ip_udp_cksum(mbuf, &pkt->ipv4_hdr, sizeof(prox_rte_ether_hdr), sizeof(prox_rte_ipv4_hdr), task->offload_crc);
75         return 0;
76 }
77
78 static int handle_nat_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts)
79 {
80         struct task_nat *task = (struct task_nat *)tbase;
81         uint8_t out[MAX_PKT_BURST];
82         uint16_t j;
83         prefetch_first(mbufs, n_pkts);
84         for (j = 0; j + PREFETCH_OFFSET < n_pkts; ++j) {
85 #ifdef PROX_PREFETCH_OFFSET
86                 PREFETCH0(mbufs[j + PREFETCH_OFFSET]);
87                 PREFETCH0(rte_pktmbuf_mtod(mbufs[j + PREFETCH_OFFSET - 1], void *));
88 #endif
89                 out[j] = handle_nat(task, mbufs[j]);
90         }
91 #ifdef PROX_PREFETCH_OFFSET
92         PREFETCH0(rte_pktmbuf_mtod(mbufs[n_pkts - 1], void *));
93         for (; j < n_pkts; ++j) {
94                 out[j] = handle_nat(task, mbufs[j]);
95         }
96 #endif
97         return task->base.tx_pkt(&task->base, mbufs, n_pkts, out);
98 }
99
100 static int lua_to_hash_nat(struct lua_State *L, enum lua_place from, const char *name,
101                            uint8_t socket, struct rte_hash **hash, uint32_t **entries)
102 {
103         struct rte_hash *ret_hash;
104         uint32_t *ret_entries;
105         uint32_t n_entries;
106         uint32_t ip_from, ip_to;
107         int ret, pop;
108
109         if ((pop = lua_getfrom(L, from, name)) < 0)
110                 return -1;
111
112         lua_len(L, -1);
113         n_entries = lua_tointeger(L, -1);
114         lua_pop(L, 1);
115
116         PROX_PANIC(n_entries == 0, "No entries for NAT\n");
117
118         static char hash_name[30] = "000_hash_nat_table";
119
120         const struct rte_hash_parameters hash_params = {
121                 .name = hash_name,
122                 .entries = n_entries * 4,
123                 .key_len = sizeof(ip_from),
124                 .hash_func = rte_hash_crc,
125                 .hash_func_init_val = 0,
126                 .socket_id = socket,
127         };
128
129         ret_hash = rte_hash_create(&hash_params);
130         PROX_PANIC(ret_hash == NULL, "Failed to set up hash table for NAT\n");
131         name++;
132         ret_entries = prox_zmalloc(n_entries * sizeof(ip_to), socket);
133         PROX_PANIC(ret_entries == NULL, "Failed to allocate memory for NAT %u entries\n", n_entries);
134
135         lua_pushnil(L);
136         while (lua_next(L, -2)) {
137                 if (lua_to_ip(L, TABLE, "from", &ip_from) ||
138                     lua_to_ip(L, TABLE, "to", &ip_to))
139                         return -1;
140
141                 ip_from = rte_bswap32(ip_from);
142                 ip_to = rte_bswap32(ip_to);
143
144                 ret = rte_hash_lookup(ret_hash, (const void *)&ip_from);
145                 PROX_PANIC(ret >= 0, "Key %x already exists in NAT hash table\n", ip_from);
146
147                 ret = rte_hash_add_key(ret_hash, (const void *)&ip_from);
148
149                 PROX_PANIC(ret < 0, "Failed to add Key %x to NAT hash table\n", ip_from);
150                 ret_entries[ret] = ip_to;
151                 lua_pop(L, 1);
152         }
153
154         lua_pop(L, pop);
155
156         *hash = ret_hash;
157         *entries = ret_entries;
158         return 0;
159 }
160
161 static void init_task_nat(struct task_base *tbase, struct task_args *targ)
162 {
163         struct task_nat *task = (struct task_nat *)tbase;
164         const int socket_id = rte_lcore_to_socket_id(targ->lconf->id);
165         int ret;
166
167         /* Use destination IP by default. */
168         task->use_src = targ->use_src;
169
170         PROX_PANIC(!strcmp(targ->nat_table, ""), "No nat table specified\n");
171         ret = lua_to_hash_nat(prox_lua(), GLOBAL, targ->nat_table, socket_id, &task->hash, &task->entries);
172         PROX_PANIC(ret != 0, "Failed to load NAT table from lua:\n%s\n", get_lua_to_errors());
173         struct prox_port_cfg *port = find_reachable_port(targ);
174         if (port) {
175                 task->offload_crc = port->requested_tx_offload & (RTE_ETH_TX_OFFLOAD_IPV4_CKSUM | RTE_ETH_TX_OFFLOAD_UDP_CKSUM);
176         }
177
178 }
179
180 /* Basic static nat. */
181 static struct task_init task_init_nat = {
182         .mode_str = "nat",
183         .init = init_task_nat,
184         .handle = handle_nat_bulk,
185 #ifdef SOFT_CRC
186         .flag_features = TASK_FEATURE_TXQ_FLAGS_NOOFFLOADS,
187 #else
188         .flag_features = 0,
189 #endif
190         .size = sizeof(struct task_nat),
191 };
192
193 __attribute__((constructor)) static void reg_task_nat(void)
194 {
195         reg_task(&task_init_nat);
196 }