Multiple changes for June release
[samplevnf.git] / VNFs / DPPD-PROX / handle_master.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_hash.h>
18 #include <rte_hash_crc.h>
19 #include "prox_cfg.h"
20
21 #include "prox_globals.h"
22 #include "rx_pkt.h"
23 #include "arp.h"
24 #include "handle_master.h"
25 #include "log.h"
26 #include "mbuf_utils.h"
27 #include "etypes.h"
28 #include "defaults.h"
29 #include "prox_cfg.h"
30 #include "prox_malloc.h"
31 #include "quit.h"
32 #include "task_init.h"
33 #include "prox_port_cfg.h"
34 #include "main.h"
35 #include "lconf.h"
36 #include "input.h"
37 #include "tx_pkt.h"
38
39 #define PROX_MAX_ARP_REQUESTS   32      // Maximum number of tasks requesting the same MAC address
40
41 const char *actions_string[] = {"UPDATE_FROM_CTRL", "SEND_ARP_REQUEST_FROM_CTRL", "SEND_ARP_REPLY_FROM_CTRL", "HANDLE_ARP_TO_CTRL", "REQ_MAC_TO_CTRL"};
42
43 static struct my_arp_t arp_reply = {
44         .htype = 0x100,
45         .ptype = 8,
46         .hlen = 6,
47         .plen = 4,
48         .oper = 0x200
49 };
50 static struct my_arp_t arp_request = {
51         .htype = 0x100,
52         .ptype = 8,
53         .hlen = 6,
54         .plen = 4,
55         .oper = 0x100
56 };
57
58 struct ip_table {
59         struct ether_addr       mac;
60         struct rte_ring         *ring;
61 };
62
63 struct external_ip_table {
64         struct ether_addr       mac;
65         struct rte_ring         *rings[PROX_MAX_ARP_REQUESTS];
66         uint16_t                nb_requests;
67 };
68
69 struct port_table {
70         struct ether_addr       mac;
71         struct rte_ring         *ring;
72         uint32_t                ip;
73         uint8_t                 port;
74         uint8_t                 flags;
75 };
76
77 struct task_master {
78         struct task_base base;
79         struct rte_ring *ctrl_rx_ring;
80         struct rte_ring **ctrl_tx_rings;
81         struct ip_table *internal_ip_table;
82         struct external_ip_table *external_ip_table;
83         struct rte_hash  *external_ip_hash;
84         struct rte_hash  *internal_ip_hash;
85         struct port_table internal_port_table[PROX_MAX_PORTS];
86 };
87
88 struct ip_port {
89         uint32_t ip;
90         uint8_t port;
91 } __attribute__((packed));
92
93 static inline uint8_t get_command(struct rte_mbuf *mbuf)
94 {
95         return mbuf->udata64 & 0xFF;
96 }
97 static inline uint8_t get_task(struct rte_mbuf *mbuf)
98 {
99         return (mbuf->udata64 >> 8) & 0xFF;
100 }
101 static inline uint8_t get_core(struct rte_mbuf *mbuf)
102 {
103         return (mbuf->udata64 >> 16) & 0xFF;
104 }
105 static inline uint8_t get_port(struct rte_mbuf *mbuf)
106 {
107         return mbuf->port;
108 }
109 static inline uint32_t get_ip(struct rte_mbuf *mbuf)
110 {
111         return (mbuf->udata64 >> 32) & 0xFFFFFFFF;
112 }
113
114 void register_ip_to_ctrl_plane(struct task_base *tbase, uint32_t ip, uint8_t port_id, uint8_t core_id, uint8_t task_id)
115 {
116         struct task_master *task = (struct task_master *)tbase;
117         struct ip_port key;
118         plogx_dbg("\tregistering IP %d.%d.%d.%d with port %d core %d and task %d\n", IP4(ip), port_id, core_id, task_id);
119
120         if (port_id >= PROX_MAX_PORTS) {
121                 plog_err("Unable to register ip %d.%d.%d.%d, port %d\n", IP4(ip), port_id);
122                 return;
123         }
124
125         /* TODO - stoe multiple rings if multiple cores able to handle IP
126            Remove them when such cores are stopped and de-register IP
127         */
128         task->internal_port_table[port_id].ring = task->ctrl_tx_rings[core_id * MAX_TASKS_PER_CORE + task_id];
129         memcpy(&task->internal_port_table[port_id].mac, &prox_port_cfg[port_id].eth_addr, 6);
130         task->internal_port_table[port_id].ip = ip;
131
132         if (ip == RANDOM_IP) {
133                 task->internal_port_table[port_id].flags |= HANDLE_RANDOM_IP_FLAG;
134                 return;
135         }
136
137         key.ip = ip;
138         key.port = port_id;
139         int ret = rte_hash_add_key(task->internal_ip_hash, (const void *)&key);
140         if (unlikely(ret < 0)) {
141                 plog_err("Unable to register ip %d.%d.%d.%d\n", IP4(ip));
142                 return;
143         }
144         memcpy(&task->internal_ip_table[ret].mac, &prox_port_cfg[port_id].eth_addr, 6);
145         task->internal_ip_table[ret].ring = task->ctrl_tx_rings[core_id * MAX_TASKS_PER_CORE + task_id];
146
147 }
148
149 static inline void handle_arp_reply(struct task_base *tbase, struct rte_mbuf *mbuf)
150 {
151         struct task_master *task = (struct task_master *)tbase;
152         struct ether_hdr_arp *hdr_arp = rte_pktmbuf_mtod(mbuf, struct ether_hdr_arp *);
153         int i, ret;
154         uint32_t key = hdr_arp->arp.data.spa;
155         plogx_dbg("\tMaster handling ARP reply for ip %d.%d.%d.%d\n", IP4(key));
156
157         ret = rte_hash_lookup(task->external_ip_hash, (const void *)&key);
158         if (unlikely(ret < 0)) {
159                 // entry not found for this IP: we did not ask a request, delete the reply
160                 tx_drop(mbuf);
161         } else {
162                 // entry found for this IP
163                 uint16_t nb_requests = task->external_ip_table[ret].nb_requests;
164                 memcpy(&hdr_arp->ether_hdr.d_addr.addr_bytes, &task->external_ip_table[ret].mac, 6);
165                 // If we receive a request from multiple task for the same IP, then we update all tasks
166                 if (task->external_ip_table[ret].nb_requests) {
167                         rte_mbuf_refcnt_set(mbuf, nb_requests);
168                         for (int i = 0; i < nb_requests; i++) {
169                                 struct rte_ring *ring = task->external_ip_table[ret].rings[i];
170                                 tx_ring_ip(tbase, ring, UPDATE_FROM_CTRL, mbuf, key);
171                         }
172                         task->external_ip_table[ret].nb_requests = 0;
173                 } else {
174                         tx_drop(mbuf);
175                 }
176         }
177 }
178
179 static inline void handle_arp_request(struct task_base *tbase, struct rte_mbuf *mbuf)
180 {
181         struct task_master *task = (struct task_master *)tbase;
182         struct ether_hdr_arp *hdr_arp = rte_pktmbuf_mtod(mbuf, struct ether_hdr_arp *);
183         int i, ret;
184         uint8_t port = get_port(mbuf);
185
186         struct ip_port key;
187         key.ip = hdr_arp->arp.data.tpa;
188         key.port = port;
189         if (task->internal_port_table[port].flags & HANDLE_RANDOM_IP_FLAG) {
190                 struct ether_addr mac;
191                 plogx_dbg("\tMaster handling ARP request for ip %d.%d.%d.%d on port %d which supports random ip\n", IP4(key.ip), key.port);
192                 struct rte_ring *ring = task->internal_port_table[port].ring;
193                 create_mac(hdr_arp, &mac);
194                 mbuf->ol_flags &= ~(PKT_TX_IP_CKSUM|PKT_TX_UDP_CKSUM);
195                 build_arp_reply(hdr_arp, &mac);
196                 tx_ring(tbase, ring, ARP_REPLY_FROM_CTRL, mbuf);
197                 return;
198         }
199
200         plogx_dbg("\tMaster handling ARP request for ip %d.%d.%d.%d\n", IP4(key.ip));
201
202         ret = rte_hash_lookup(task->internal_ip_hash, (const void *)&key);
203         if (unlikely(ret < 0)) {
204                 // entry not found for this IP.
205                 plogx_dbg("Master ignoring ARP REQUEST received on un-registered IP %d.%d.%d.%d on port %d\n", IP4(hdr_arp->arp.data.tpa), port);
206                 tx_drop(mbuf);
207         } else {
208                 struct rte_ring *ring = task->internal_ip_table[ret].ring;
209                 mbuf->ol_flags &= ~(PKT_TX_IP_CKSUM|PKT_TX_UDP_CKSUM);
210                 build_arp_reply(hdr_arp, &task->internal_ip_table[ret].mac);
211                 tx_ring(tbase, ring, ARP_REPLY_FROM_CTRL, mbuf);
212         }
213 }
214
215 static inline void handle_unknown_ip(struct task_base *tbase, struct rte_mbuf *mbuf)
216 {
217         struct task_master *task = (struct task_master *)tbase;
218         struct ether_hdr_arp *hdr_arp = rte_pktmbuf_mtod(mbuf, struct ether_hdr_arp *);
219         uint8_t port = get_port(mbuf);
220         uint32_t ip_dst = get_ip(mbuf);
221         int ret1, ret2, i;
222
223         plogx_dbg("\tMaster handling unknown ip %d.%d.%d.%d for port %d\n", IP4(ip_dst), port);
224         if (unlikely(port >= PROX_MAX_PORTS)) {
225                 plogx_dbg("Port %d not found", port);
226                 tx_drop(mbuf);
227                 return;
228         }
229         uint32_t ip_src = task->internal_port_table[port].ip;
230         struct rte_ring *ring = task->ctrl_tx_rings[get_core(mbuf) * MAX_TASKS_PER_CORE + get_task(mbuf)];
231
232         if (ring == NULL) {
233                 plogx_dbg("Port %d not registered", port);
234                 tx_drop(mbuf);
235                 return;
236         }
237
238         ret2 = rte_hash_add_key(task->external_ip_hash, (const void *)&ip_dst);
239         if (unlikely(ret2 < 0)) {
240                 // entry not found for this IP: delete the reply
241                 plogx_dbg("Unable to add IP %d.%d.%d.%d in external_ip_hash\n", IP4(ip_dst));
242                 tx_drop(mbuf);
243                 return;
244         }
245
246         // If multiple tasks requesting the same info, we will need to send a reply to all of them
247         // However if one task sends multiple requests to the same IP (e.g. because it is not answering)
248         // then we should not send multiple replies to the same task
249         if (task->external_ip_table[ret2].nb_requests >= PROX_MAX_ARP_REQUESTS) {
250                 // This can only happen if really many tasks requests the same IP
251                 plogx_dbg("Unable to add request for IP %d.%d.%d.%d in external_ip_table\n", IP4(ip_dst));
252                 tx_drop(mbuf);
253                 return;
254         }
255         for (i = 0; i < task->external_ip_table[ret2].nb_requests; i++) {
256                 if (task->external_ip_table[ret2].rings[i] == ring)
257                         break;
258         }
259         if (i >= task->external_ip_table[ret2].nb_requests) {
260                 // If this is a new request i.e. a new task requesting a new IP
261                 task->external_ip_table[ret2].rings[task->external_ip_table[ret2].nb_requests] = ring;
262                 task->external_ip_table[ret2].nb_requests++;
263                 // Only needed for first request - but avoid test and copy the same 6 bytes
264                 // In most cases we will only have one request per IP.
265                 memcpy(&task->external_ip_table[ret2].mac, &task->internal_port_table[port].mac, 6);
266         }
267
268         // We send an ARP request even if one was just sent (and not yet answered) by another task
269         mbuf->ol_flags &= ~(PKT_TX_IP_CKSUM|PKT_TX_UDP_CKSUM);
270         build_arp_request(mbuf, &task->internal_port_table[port].mac, ip_dst, ip_src);
271         tx_ring(tbase, ring, ARP_REQ_FROM_CTRL, mbuf);
272 }
273
274 static inline void handle_message(struct task_base *tbase, struct rte_mbuf *mbuf, int ring_id)
275 {
276         struct ether_hdr_arp *hdr_arp = rte_pktmbuf_mtod(mbuf, struct ether_hdr_arp *);
277         int command = get_command(mbuf);
278         uint32_t ip;
279         plogx_dbg("\tMaster received %s (%x) from mbuf %p\n", actions_string[command], command, mbuf);
280
281         switch(command) {
282         case ARP_TO_CTRL:
283                 if (hdr_arp->ether_hdr.ether_type != ETYPE_ARP) {
284                         tx_drop(mbuf);
285                         plog_err("\tUnexpected message received: ARP_TO_CTRL with ether_type %x\n", hdr_arp->ether_hdr.ether_type);
286                         return;
287                 } else if (arp_is_gratuitous(hdr_arp)) {
288                         plog_info("\tReceived gratuitous packet \n");
289                         tx_drop(mbuf);
290                         return;
291                 } else if (memcmp(&hdr_arp->arp, &arp_reply, 8) == 0) {
292                         uint32_t ip = hdr_arp->arp.data.spa;
293                         handle_arp_reply(tbase, mbuf);
294                 } else if (memcmp(&hdr_arp->arp, &arp_request, 8) == 0) {
295                         handle_arp_request(tbase, mbuf);
296                 } else {
297                         plog_info("\tReceived unexpected ARP operation %d\n", hdr_arp->arp.oper);
298                         tx_drop(mbuf);
299                         return;
300                 }
301                 break;
302         case REQ_MAC_TO_CTRL:
303                 handle_unknown_ip(tbase, mbuf);
304                 break;
305         default:
306                 plogx_dbg("\tMaster received unexpected message\n");
307                 tx_drop(mbuf);
308                 break;
309         }
310 }
311
312 void init_ctrl_plane(struct task_base *tbase)
313 {
314         prox_cfg.flags |= DSF_CTRL_PLANE_ENABLED;
315         struct task_master *task = (struct task_master *)tbase;
316         int socket = rte_lcore_to_socket_id(prox_cfg.master);
317         uint32_t n_entries = MAX_ARP_ENTRIES * 4;
318         static char hash_name[30];
319         sprintf(hash_name, "A%03d_hash_arp_table", prox_cfg.master);
320         struct rte_hash_parameters hash_params = {
321                 .name = hash_name,
322                 .entries = n_entries,
323                 .key_len = sizeof(uint32_t),
324                 .hash_func = rte_hash_crc,
325                 .hash_func_init_val = 0,
326         };
327         task->external_ip_hash = rte_hash_create(&hash_params);
328         PROX_PANIC(task->external_ip_hash == NULL, "Failed to set up external ip hash\n");
329         plog_info("\texternal ip hash table allocated, with %d entries of size %d\n", hash_params.entries, hash_params.key_len);
330         task->external_ip_table = (struct external_ip_table *)prox_zmalloc(n_entries * sizeof(struct external_ip_table), socket);
331         PROX_PANIC(task->external_ip_table == NULL, "Failed to allocate memory for %u entries in external ip table\n", n_entries);
332         plog_info("\texternal ip table, with %d entries of size %ld\n", n_entries, sizeof(struct external_ip_table));
333
334         hash_name[0]++;
335         hash_params.key_len = sizeof(struct ip_port);
336         task->internal_ip_hash = rte_hash_create(&hash_params);
337         PROX_PANIC(task->internal_ip_hash == NULL, "Failed to set up internal ip hash\n");
338         plog_info("\tinternal ip hash table allocated, with %d entries of size %d\n", hash_params.entries, hash_params.key_len);
339         task->internal_ip_table = (struct ip_table *)prox_zmalloc(n_entries * sizeof(struct ip_table), socket);
340         PROX_PANIC(task->internal_ip_table == NULL, "Failed to allocate memory for %u entries in internal ip table\n", n_entries);
341         plog_info("\tinternal ip table, with %d entries of size %ld\n", n_entries, sizeof(struct ip_table));
342 }
343
344 static int handle_ctrl_plane_f(struct task_base *tbase, __attribute__((unused)) struct rte_mbuf **mbuf, uint16_t n_pkts)
345 {
346         int ring_id = 0, j, ret = 0;
347         struct rte_mbuf *mbufs[MAX_RING_BURST];
348         struct task_master *task = (struct task_master *)tbase;
349
350         /*      Handle_master works differently than other handle functions
351                 It is not handled by a DPDK dataplane core
352                 It is no thread_generic based, hence do not receive packets the same way
353         */
354
355         ret = ring_deq(task->ctrl_rx_ring, mbufs);
356         for (j = 0; j < ret; j++) {
357                 handle_message(tbase, mbufs[j], ring_id);
358         }
359         return ret;
360 }
361
362 static void init_task_master(struct task_base *tbase, struct task_args *targs)
363 {
364         if (prox_cfg.flags & DSF_CTRL_PLANE_ENABLED) {
365                 struct task_master *task = (struct task_master *)tbase;
366
367                 task->ctrl_rx_ring = targs->lconf->ctrl_rings_p[0];
368                 task->ctrl_tx_rings = ctrl_rings;
369                 init_ctrl_plane(tbase);
370                 handle_ctrl_plane = handle_ctrl_plane_f;
371         }
372 }
373
374 static struct task_init task_init_master = {
375         .mode_str = "master",
376         .init = init_task_master,
377         .handle = NULL,
378         .flag_features = TASK_FEATURE_NEVER_DISCARDS,
379         .size = sizeof(struct task_master)
380 };
381
382 __attribute__((constructor)) static void reg_task_gen(void)
383 {
384         reg_task(&task_init_master);
385 }