Support packets in flight
[samplevnf.git] / VNFs / DPPD-PROX / handle_swap.c
1 /*
2 // Copyright (c) 2010-2020 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_udp.h>
19 #include <rte_icmp.h>
20
21 #include "task_init.h"
22 #include "task_base.h"
23 #include "lconf.h"
24 #include "log.h"
25 #include "prox_port_cfg.h"
26 #include "mpls.h"
27 #include "qinq.h"
28 #include "gre.h"
29 #include "prefetch.h"
30 #include "defines.h"
31 #include "igmp.h"
32 #include "prox_cksum.h"
33 #include "prox_compat.h"
34
35 #define MAX_STORE_PKT_SIZE      2048
36
37 struct packet {
38         unsigned int len;
39         unsigned char buf[MAX_STORE_PKT_SIZE];
40 };
41
42 struct task_swap {
43         struct task_base base;
44         struct rte_mempool *igmp_pool;
45         uint32_t flags;
46         uint32_t runtime_flags;
47         uint32_t igmp_address;
48         uint8_t src_dst_mac[12];
49         uint32_t local_ipv4;
50         int offload_crc;
51         uint64_t last_echo_req_rcvd_tsc;
52         uint64_t last_echo_rep_rcvd_tsc;
53         uint32_t n_echo_req;
54         uint32_t n_echo_rep;
55         uint32_t store_pkt_id;
56         uint32_t store_msk;
57         struct packet *store_buf;
58         FILE *fp;
59 };
60
61 #define NB_IGMP_MBUF            1024
62 #define IGMP_MBUF_SIZE          2048
63 #define NB_CACHE_IGMP_MBUF      256
64
65 #define GENEVE_PORT             0xc117 // in be
66
67 static void write_src_and_dst_mac(struct task_swap *task, struct rte_mbuf *mbuf)
68 {
69         prox_rte_ether_hdr *hdr;
70         prox_rte_ether_addr mac;
71
72         if (unlikely((task->flags & (TASK_ARG_DST_MAC_SET|TASK_ARG_SRC_MAC_SET)) == (TASK_ARG_DST_MAC_SET|TASK_ARG_SRC_MAC_SET))) {
73                 /* Source and Destination mac hardcoded */
74                 hdr = rte_pktmbuf_mtod(mbuf, prox_rte_ether_hdr *);
75                 rte_memcpy(hdr, task->src_dst_mac, sizeof(task->src_dst_mac));
76         } else {
77                 hdr = rte_pktmbuf_mtod(mbuf, prox_rte_ether_hdr *);
78                 if (unlikely((task->flags & TASK_ARG_SRC_MAC_SET) == 0)) {
79                         /* dst mac will be used as src mac */
80                         prox_rte_ether_addr_copy(&hdr->d_addr, &mac);
81                 }
82
83                 if (unlikely(task->flags & TASK_ARG_DST_MAC_SET))
84                         prox_rte_ether_addr_copy((prox_rte_ether_addr *)&task->src_dst_mac[0], &hdr->d_addr);
85                 else
86                         prox_rte_ether_addr_copy(&hdr->s_addr, &hdr->d_addr);
87
88                 if (likely(task->flags & TASK_ARG_SRC_MAC_SET)) {
89                         prox_rte_ether_addr_copy((prox_rte_ether_addr *)&task->src_dst_mac[6], &hdr->s_addr);
90                 } else {
91                         prox_rte_ether_addr_copy(&mac, &hdr->s_addr);
92                 }
93         }
94 }
95 static inline void build_mcast_mac(uint32_t ip, prox_rte_ether_addr *dst_mac)
96 {
97         // MAC address is 01:00:5e followed by 23 LSB of IP address
98         uint64_t mac = 0x0000005e0001L | ((ip & 0xFFFF7F00L) << 16);
99         memcpy(dst_mac, &mac, sizeof(prox_rte_ether_addr));
100 }
101
102 static inline void build_icmp_reply_message(struct task_base *tbase, struct rte_mbuf *mbuf)
103 {
104         struct task_swap *task = (struct task_swap *)tbase;
105         prox_rte_ether_hdr *hdr = rte_pktmbuf_mtod(mbuf, prox_rte_ether_hdr *);
106         prox_rte_ether_addr dst_mac;
107         prox_rte_ether_addr_copy(&hdr->s_addr, &dst_mac);
108         prox_rte_ether_addr_copy(&hdr->d_addr, &hdr->s_addr);
109         prox_rte_ether_addr_copy(&dst_mac, &hdr->d_addr);
110         prox_rte_ipv4_hdr *ip_hdr = (prox_rte_ipv4_hdr *)(hdr + 1);
111         ip_hdr->dst_addr = ip_hdr->src_addr;
112         ip_hdr->src_addr = task->local_ipv4;
113         prox_rte_icmp_hdr *picmp = (prox_rte_icmp_hdr *)(ip_hdr + 1);
114         picmp->icmp_type = PROX_RTE_IP_ICMP_ECHO_REPLY;
115 }
116
117 static inline void build_igmp_message(struct task_base *tbase, struct rte_mbuf *mbuf, uint32_t ip, uint8_t igmp_message)
118 {
119         struct task_swap *task = (struct task_swap *)tbase;
120         prox_rte_ether_hdr *hdr = rte_pktmbuf_mtod(mbuf, prox_rte_ether_hdr *);
121         prox_rte_ether_addr dst_mac;
122         build_mcast_mac(ip, &dst_mac);
123
124         rte_pktmbuf_pkt_len(mbuf) = 46;
125         rte_pktmbuf_data_len(mbuf) = 46;
126         init_mbuf_seg(mbuf);
127
128         prox_rte_ether_addr_copy(&dst_mac, &hdr->d_addr);
129         prox_rte_ether_addr_copy((prox_rte_ether_addr *)&task->src_dst_mac[6], &hdr->s_addr);
130         hdr->ether_type = ETYPE_IPv4;
131
132         prox_rte_ipv4_hdr *ip_hdr = (prox_rte_ipv4_hdr *)(hdr + 1);
133         ip_hdr->version_ihl = 0x45;             /**< version and header length */
134         ip_hdr->type_of_service = 0;    /**< type of service */
135         ip_hdr->total_length = rte_cpu_to_be_16(32);            /**< length of packet */
136         ip_hdr->packet_id = 0;          /**< packet ID */
137         ip_hdr->fragment_offset = 0;    /**< fragmentation offset */
138         ip_hdr->time_to_live = 1;               /**< time to live */
139         ip_hdr->next_proto_id = IPPROTO_IGMP;           /**< protocol ID */
140         ip_hdr->hdr_checksum = 0;               /**< header checksum */
141         ip_hdr->src_addr = task->local_ipv4;            /**< source address */
142         ip_hdr->dst_addr = ip;  /**< destination address */
143         struct igmpv2_hdr *pigmp = (struct igmpv2_hdr *)(ip_hdr + 1);
144         pigmp->type = igmp_message;
145         pigmp->max_resp_time = 0;
146         pigmp->checksum = 0;
147         pigmp->group_address = ip;
148         prox_ip_udp_cksum(mbuf, ip_hdr, sizeof(prox_rte_ether_hdr), sizeof(prox_rte_ipv4_hdr), task->offload_crc);
149 }
150
151 static void stop_swap(struct task_base *tbase)
152 {
153         uint32_t i, j;
154         struct task_swap *task = (struct task_swap *)tbase;
155
156         if (task->igmp_pool) {
157                 rte_mempool_free(task->igmp_pool);
158                 task->igmp_pool = NULL;
159         }
160
161         if (task->store_msk) {
162                 for (i = task->store_pkt_id & task->store_msk; i < task->store_msk + 1; i++) {
163                         if (task->store_buf[i].len) {
164                                 fprintf(task->fp, "%06d: ", i);
165                                 for (j = 0; j < task->store_buf[i].len; j++) {
166                                         fprintf(task->fp, "%02x ", task->store_buf[i].buf[j]);
167                                 }
168                                 fprintf(task->fp, "\n");
169                         }
170                 }
171                 for (i = 0; i < (task->store_pkt_id & task->store_msk); i++) {
172                         if (task->store_buf[i].len) {
173                                 fprintf(task->fp, "%06d: ", i);
174                                 for (j = 0; j < task->store_buf[i].len; j++) {
175                                         fprintf(task->fp, "%02x ", task->store_buf[i].buf[j]);
176                                 }
177                                 fprintf(task->fp, "\n");
178                         }
179                 }
180         }
181 }
182
183 static void handle_ipv6(struct task_swap *task, struct rte_mbuf *mbufs, prox_rte_ipv6_hdr *ipv6_hdr, uint8_t *out)
184 {
185         __m128i ip =  _mm_loadu_si128((__m128i*)&(ipv6_hdr->src_addr));
186         uint16_t port;
187         uint16_t payload_len;
188         prox_rte_udp_hdr *udp_hdr;
189
190         rte_mov16((uint8_t *)&(ipv6_hdr->src_addr), (uint8_t *)&(ipv6_hdr->dst_addr));  // Copy dst into src
191         rte_mov16((uint8_t *)&(ipv6_hdr->dst_addr), (uint8_t *)&ip);                    // Copy src into dst
192         switch(ipv6_hdr->proto) {
193                 case IPPROTO_TCP:
194                 case IPPROTO_UDP:
195                         payload_len = ipv6_hdr->payload_len;
196                         udp_hdr = (prox_rte_udp_hdr *)(ipv6_hdr + 1);
197                         if (unlikely(udp_hdr->dgram_len < payload_len)) {
198                                 plog_warn("Unexpected L4 len (%u) versus L3 payload len (%u) in IPv6 packet\n", udp_hdr->dgram_len, payload_len);
199                                 *out = OUT_DISCARD;
200                                 break;
201                         }
202                         port = udp_hdr->dst_port;
203                         udp_hdr->dst_port = udp_hdr->src_port;
204                         udp_hdr->src_port = port;
205                         write_src_and_dst_mac(task, mbufs);
206                         *out = 0;
207                         break;
208                 default:
209                         plog_warn("Unsupported next hop %u in IPv6 packet\n", ipv6_hdr->proto);
210                         *out = OUT_DISCARD;
211                         break;
212         }
213 }
214
215 static int handle_swap_bulk(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts)
216 {
217         struct task_swap *task = (struct task_swap *)tbase;
218         prox_rte_ether_hdr *hdr;
219         prox_rte_ether_addr mac;
220         prox_rte_ipv4_hdr *ip_hdr;
221         prox_rte_udp_hdr *udp_hdr;
222         prox_rte_ipv6_hdr *ipv6_hdr;
223         struct gre_hdr *pgre;
224         prox_rte_ipv4_hdr *inner_ip_hdr;
225         uint32_t ip;
226         uint16_t port;
227         uint8_t out[64] = {0};
228         struct mpls_hdr *mpls;
229         uint32_t mpls_len = 0;
230         struct qinq_hdr *qinq;
231         prox_rte_vlan_hdr *vlan;
232         uint16_t j;
233         struct igmpv2_hdr *pigmp;
234         prox_rte_icmp_hdr *picmp;
235         uint8_t type;
236         static int llc_printed = 0;
237         static int lldp_printed = 0;
238         static int geneve_printed = 0;
239
240         for (j = 0; j < n_pkts; ++j) {
241                 PREFETCH0(mbufs[j]);
242         }
243         for (j = 0; j < n_pkts; ++j) {
244                 PREFETCH0(rte_pktmbuf_mtod(mbufs[j], void *));
245         }
246
247         // TODO 1: check packet is long enough for Ethernet + IP + UDP = 42 bytes
248         for (uint16_t j = 0; j < n_pkts; ++j) {
249                 hdr = rte_pktmbuf_mtod(mbufs[j], prox_rte_ether_hdr *);
250                 switch (hdr->ether_type) {
251                 case ETYPE_MPLSU:
252                         mpls = (struct mpls_hdr *)(hdr + 1);
253                         while (!(mpls->bytes & 0x00010000)) {
254                                 // TODO: verify pcket length
255                                 mpls++;
256                                 mpls_len += sizeof(struct mpls_hdr);
257                         }
258                         mpls_len += sizeof(struct mpls_hdr);
259                         ip_hdr = (prox_rte_ipv4_hdr *)(mpls + 1);
260                         if (unlikely((ip_hdr->version_ihl >> 4) == 6)) {
261                                 ipv6_hdr = (prox_rte_ipv6_hdr *)(ip_hdr);
262                                 handle_ipv6(task, mbufs[j], ipv6_hdr, &out[j]);
263                                 continue;
264                         }
265                         break;
266                 case ETYPE_8021ad:
267                         qinq = (struct qinq_hdr *)hdr;
268                         if (qinq->cvlan.eth_proto != ETYPE_VLAN) {
269                                 plog_warn("Unexpected proto in QinQ = %#04x\n", qinq->cvlan.eth_proto);
270                                 out[j] = OUT_DISCARD;
271                                 continue;
272                         }
273                         if (qinq->ether_type == ETYPE_IPv4) {
274                                 ip_hdr = (prox_rte_ipv4_hdr *)(qinq + 1);
275                         } else if (qinq->ether_type == ETYPE_IPv6) {
276                                 ipv6_hdr = (prox_rte_ipv6_hdr *)(qinq + 1);
277                                 handle_ipv6(task, mbufs[j], ipv6_hdr, &out[j]);
278                                 continue;
279                         } else {
280                                 plog_warn("Unsupported packet type\n");
281                                 out[j] = OUT_DISCARD;
282                                 continue;
283                         }
284                         break;
285                 case ETYPE_VLAN:
286                         vlan = (prox_rte_vlan_hdr *)(hdr + 1);
287                         if (vlan->eth_proto == ETYPE_IPv4) {
288                                 ip_hdr = (prox_rte_ipv4_hdr *)(vlan + 1);
289                         } else if (vlan->eth_proto == ETYPE_IPv6) {
290                                 ipv6_hdr = (prox_rte_ipv6_hdr *)(vlan + 1);
291                                 handle_ipv6(task, mbufs[j], ipv6_hdr, &out[j]);
292                                 continue;
293                         } else if (vlan->eth_proto == ETYPE_VLAN) {
294                                 vlan = (prox_rte_vlan_hdr *)(vlan + 1);
295                                 if (vlan->eth_proto == ETYPE_IPv4) {
296                                         ip_hdr = (prox_rte_ipv4_hdr *)(vlan + 1);
297                                 }
298                                 else if (vlan->eth_proto == ETYPE_IPv6) {
299                                         ipv6_hdr = (prox_rte_ipv6_hdr *)(vlan + 1);
300                                         handle_ipv6(task, mbufs[j], ipv6_hdr, &out[j]);
301                                         continue;
302                                 }
303                                 else {
304                                         plog_warn("Unsupported packet type\n");
305                                         out[j] = OUT_DISCARD;
306                                         continue;
307                                 }
308                         } else {
309                                 plog_warn("Unsupported packet type\n");
310                                 out[j] = OUT_DISCARD;
311                                 continue;
312                         }
313                         break;
314                 case ETYPE_IPv4:
315                         ip_hdr = (prox_rte_ipv4_hdr *)(hdr + 1);
316                         break;
317                 case ETYPE_IPv6:
318                         ipv6_hdr = (prox_rte_ipv6_hdr *)(hdr + 1);
319                         handle_ipv6(task, mbufs[j], ipv6_hdr, &out[j]);
320                         continue;
321                 case ETYPE_LLDP:
322                         if (!lldp_printed) {
323                                 plog_info("Discarding LLDP packets (only printed once)\n");
324                                 lldp_printed = 1;
325                         }
326                         out[j] = OUT_DISCARD;
327                         continue;
328                 default:
329                         if ((rte_bswap16(hdr->ether_type) < 0x600) && (rte_bswap16(hdr->ether_type) >= 16)) {
330                                 // 802.3
331                                 struct prox_llc {
332                                         uint8_t dsap;
333                                         uint8_t lsap;
334                                         uint8_t control;
335                                 };
336                                 struct prox_llc *llc = (struct prox_llc *)(hdr + 1);
337                                 if ((llc->dsap == 0x42) && (llc->lsap == 0x42)) {
338                                         // STP Protocol
339                                         out[j] = OUT_DISCARD;
340                                         if (!llc_printed) {
341                                                 plog_info("Discarding STP packets (only printed once)\n");
342                                                 llc_printed = 1;
343                                         }
344                                         continue;
345                                 }
346                         }
347                         plog_warn("Unsupported ether_type 0x%x\n", hdr->ether_type);
348                         out[j] = OUT_DISCARD;
349                         continue;
350                 }
351                 // TODO 2 : check packet is long enough for Ethernet + IP + UDP + extra header (VLAN, MPLS, ...)
352                 // IPv4 packet
353
354                 ip = ip_hdr->dst_addr;
355                 if (unlikely((ip_hdr->version_ihl >> 4) != 4)) {
356                         out[j] = OUT_DISCARD;
357                         continue;
358                 }
359
360                 switch (ip_hdr->next_proto_id) {
361                 case IPPROTO_GRE:
362                         ip_hdr->dst_addr = ip_hdr->src_addr;
363                         ip_hdr->src_addr = ip;
364
365                         pgre = (struct gre_hdr *)(ip_hdr + 1);
366                         inner_ip_hdr = ((prox_rte_ipv4_hdr *)(pgre + 1));
367                         ip = inner_ip_hdr->dst_addr;
368                         inner_ip_hdr->dst_addr = inner_ip_hdr->src_addr;
369                         inner_ip_hdr->src_addr = ip;
370
371                         udp_hdr = (prox_rte_udp_hdr *)(inner_ip_hdr + 1);
372                         // TODO 3.1 : verify proto is UPD or TCP
373                         port = udp_hdr->dst_port;
374                         udp_hdr->dst_port = udp_hdr->src_port;
375                         udp_hdr->src_port = port;
376                         write_src_and_dst_mac(task, mbufs[j]);
377                         break;
378                 case IPPROTO_UDP:
379                 case IPPROTO_TCP:
380                         if (unlikely(task->igmp_address && PROX_RTE_IS_IPV4_MCAST(rte_be_to_cpu_32(ip)))) {
381                                 out[j] = OUT_DISCARD;
382                                 continue;
383                         }
384                         udp_hdr = (prox_rte_udp_hdr *)(ip_hdr + 1);
385                         port = udp_hdr->dst_port;
386                         ip_hdr->dst_addr = ip_hdr->src_addr;
387                         ip_hdr->src_addr = ip;
388
389                         if ((port == GENEVE_PORT) && (task->runtime_flags & TASK_DO_NOT_FWD_GENEVE)) {
390                                 if (!geneve_printed) {
391                                         plog_info("Discarding geneve (only printed once)\n");
392                                         geneve_printed = 1;
393                                 }
394                                 out[j] = OUT_DISCARD;
395                                 continue;
396                         }
397
398                         udp_hdr->dst_port = udp_hdr->src_port;
399                         udp_hdr->src_port = port;
400                         write_src_and_dst_mac(task, mbufs[j]);
401                         break;
402                 case IPPROTO_ICMP:
403                         picmp = (prox_rte_icmp_hdr *)(ip_hdr + 1);
404                         type = picmp->icmp_type;
405                         if (type == PROX_RTE_IP_ICMP_ECHO_REQUEST) {
406                                 if (ip_hdr->dst_addr == task->local_ipv4) {
407                                         task->n_echo_req++;
408                                         if (rte_rdtsc() - task->last_echo_req_rcvd_tsc > rte_get_tsc_hz()) {
409                                                 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)));
410                                                 task->n_echo_req = 0;
411                                                 task->last_echo_req_rcvd_tsc = rte_rdtsc();
412                                         }
413                                         build_icmp_reply_message(tbase, mbufs[j]);
414                                 } else {
415                                         out[j] = OUT_DISCARD;
416                                         continue;
417                                 }
418                         } else if (type == PROX_RTE_IP_ICMP_ECHO_REPLY) {
419                                 if (ip_hdr->dst_addr == task->local_ipv4) {
420                                         task->n_echo_rep++;
421                                         if (rte_rdtsc() - task->last_echo_rep_rcvd_tsc > rte_get_tsc_hz()) {
422                                                 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)));
423                                                 task->n_echo_rep = 0;
424                                                 task->last_echo_rep_rcvd_tsc = rte_rdtsc();
425                                         }
426                                 } else {
427                                         out[j] = OUT_DISCARD;
428                                         continue;
429                                 }
430                         } else {
431                                 out[j] = OUT_DISCARD;
432                                 continue;
433                         }
434                         break;
435                 case IPPROTO_IGMP:
436                         pigmp = (struct igmpv2_hdr *)(ip_hdr + 1);
437                         // TODO: check packet len
438                         type = pigmp->type;
439                         if (type == IGMP_MEMBERSHIP_QUERY) {
440                                 if (task->igmp_address) {
441                                         // We have an address registered
442                                         if ((task->igmp_address == pigmp->group_address) || (pigmp->group_address == 0)) {
443                                                 // We get a request for the registered address, or to 0.0.0.0
444                                                 build_igmp_message(tbase, mbufs[j], task->igmp_address, IGMP_MEMBERSHIP_REPORT);        // replace Membership query packet with a response
445                                         } else {
446                                                 // Discard as either we are not registered or this is a query for a different group
447                                                 out[j] = OUT_DISCARD;
448                                                 continue;
449                                         }
450                                 } else {
451                                         // Discard as either we are not registered
452                                         out[j] = OUT_DISCARD;
453                                         continue;
454                                 }
455                         } else {
456                                 // Do not forward other IGMP packets back
457                                 out[j] = OUT_DISCARD;
458                                 continue;
459                         }
460                         break;
461                 default:
462                         plog_warn("Unsupported IP protocol 0x%x\n", ip_hdr->next_proto_id);
463                         out[j] = OUT_DISCARD;
464                         continue;
465                 }
466         }
467         if (task->store_msk) {
468                 for (int i = 0; i < n_pkts; i++) {
469                         if (out[i] != OUT_DISCARD) {
470                                 hdr = rte_pktmbuf_mtod(mbufs[i], prox_rte_ether_hdr *);
471                                 memcpy(&task->store_buf[task->store_pkt_id & task->store_msk].buf, hdr, rte_pktmbuf_pkt_len(mbufs[i]));
472                                 task->store_buf[task->store_pkt_id & task->store_msk].len = rte_pktmbuf_pkt_len(mbufs[i]);
473                                 task->store_pkt_id++;
474                         }
475                 }
476         }
477         return task->base.tx_pkt(&task->base, mbufs, n_pkts, out);
478 }
479
480 void igmp_join_group(struct task_base *tbase, uint32_t igmp_address)
481 {
482         struct task_swap *task = (struct task_swap *)tbase;
483         struct rte_mbuf *igmp_mbuf;
484         uint8_t out[64] = {0};
485         int ret;
486
487         task->igmp_address = igmp_address;
488         ret = rte_mempool_get(task->igmp_pool, (void **)&igmp_mbuf);
489         if (ret != 0) {
490                 plog_err("Unable to allocate igmp mbuf\n");
491                 return;
492         }
493         build_igmp_message(tbase, igmp_mbuf, task->igmp_address, IGMP_MEMBERSHIP_REPORT);
494         task->base.tx_pkt(&task->base, &igmp_mbuf, 1, out);
495 }
496
497 void igmp_leave_group(struct task_base *tbase)
498 {
499         struct task_swap *task = (struct task_swap *)tbase;
500         struct rte_mbuf *igmp_mbuf;
501         uint8_t out[64] = {0};
502         int ret;
503
504         task->igmp_address = 0;
505         ret = rte_mempool_get(task->igmp_pool, (void **)&igmp_mbuf);
506         if (ret != 0) {
507                 plog_err("Unable to allocate igmp mbuf\n");
508                 return;
509         }
510         build_igmp_message(tbase, igmp_mbuf, task->igmp_address, IGMP_LEAVE_GROUP);
511         task->base.tx_pkt(&task->base, &igmp_mbuf, 1, out);
512 }
513
514 static void init_task_swap(struct task_base *tbase, struct task_args *targ)
515 {
516         struct task_swap *task = (struct task_swap *)tbase;
517         prox_rte_ether_addr *src_addr, *dst_addr;
518
519         /*
520          * The destination MAC of the outgoing packet is based on the config file:
521          *    - 'dst mac=xx:xx:xx:xx:xx:xx' => the pre-configured mac will be used as dst mac
522          *    - 'dst mac=packet'            => the src mac of the incoming packet is used as dst mac
523          *    - (default - no 'dst mac')    => the src mac from the incoming packet is used as dst mac
524          *
525          * The source MAC of the outgoing packet is based on the config file:
526          *    - 'src mac=xx:xx:xx:xx:xx:xx' => the pre-configured mac will be used as src mac
527          *    - 'src mac=packet'            => the dst mac of the incoming packet is used as src mac
528          *    - 'src mac=hw'                => the mac address of the tx port is used as src mac
529          *                                     An error is returned if there are no physical tx ports
530          *    - (default - no 'src mac')    => if there is physical tx port, the mac of that port is used as src mac
531          *    - (default - no 'src mac')       if there are no physical tx ports the dst mac of the incoming packet
532          */
533
534         if (targ->flags & TASK_ARG_DST_MAC_SET) {
535                 dst_addr = &targ->edaddr;
536                 memcpy(&task->src_dst_mac[0], dst_addr, sizeof(*src_addr));
537         }
538
539         PROX_PANIC(targ->flags & TASK_ARG_DO_NOT_SET_SRC_MAC, "src mac must be set in swap mode, by definition => src mac=no is not supported\n");
540         PROX_PANIC(targ->flags & TASK_ARG_DO_NOT_SET_DST_MAC, "dst mac must be set in swap mode, by definition => dst mac=no is not supported\n");
541
542         if (targ->flags & TASK_ARG_SRC_MAC_SET) {
543                 src_addr =  &targ->esaddr;
544                 memcpy(&task->src_dst_mac[6], src_addr, sizeof(*dst_addr));
545                 plog_info("\t\tCore %d: src mac set from config file\n", targ->lconf->id);
546         } else {
547                 if (targ->flags & TASK_ARG_HW_SRC_MAC)
548                         PROX_PANIC(targ->nb_txports == 0, "src mac set to hw but no tx port\n");
549                 if (targ->nb_txports) {
550                         src_addr = &prox_port_cfg[task->base.tx_params_hw.tx_port_queue[0].port].eth_addr;
551                         memcpy(&task->src_dst_mac[6], src_addr, sizeof(*dst_addr));
552                         targ->flags |= TASK_ARG_SRC_MAC_SET;
553                         plog_info("\t\tCore %d: src mac set from port\n", targ->lconf->id);
554                 }
555         }
556         task->flags = targ->flags;
557         task->runtime_flags = targ->runtime_flags;
558         task->igmp_address =  rte_cpu_to_be_32(targ->igmp_address);
559         if (task->igmp_pool == NULL) {
560                 static char name[] = "igmp0_pool";
561                 name[4]++;
562                 struct rte_mempool *ret = rte_mempool_create(name, NB_IGMP_MBUF, IGMP_MBUF_SIZE, NB_CACHE_IGMP_MBUF,
563                         sizeof(struct rte_pktmbuf_pool_private), rte_pktmbuf_pool_init, NULL, rte_pktmbuf_init, 0,
564                         rte_socket_id(), 0);
565                 PROX_PANIC(ret == NULL, "Failed to allocate IGMP memory pool on socket %u with %u elements\n",
566                         rte_socket_id(), NB_IGMP_MBUF);
567                 plog_info("\t\tMempool %p (%s) size = %u * %u cache %u, socket %d\n", ret, name, NB_IGMP_MBUF,
568                         IGMP_MBUF_SIZE, NB_CACHE_IGMP_MBUF, rte_socket_id());
569                 task->igmp_pool = ret;
570         }
571         task->local_ipv4 = rte_cpu_to_be_32(targ->local_ipv4);
572
573         struct prox_port_cfg *port = find_reachable_port(targ);
574         if (port) {
575                 task->offload_crc = port->requested_tx_offload & (RTE_ETH_TX_OFFLOAD_IPV4_CKSUM | RTE_ETH_TX_OFFLOAD_UDP_CKSUM);
576         }
577         task->store_pkt_id = 0;
578         if (targ->store_max) {
579                 char filename[256];
580                 sprintf(filename, "swap_buf_%02d_%02d", targ->lconf->id, targ->task);
581
582                 task->store_msk = targ->store_max - 1;
583                 task->store_buf = (struct packet *)malloc(sizeof(struct packet) * targ->store_max);
584                 task->fp = fopen(filename, "w+");
585                 PROX_PANIC(task->fp == NULL, "Unable to open %s\n", filename);
586         } else {
587                 task->store_msk = 0;
588         }
589 }
590
591 static struct task_init task_init_swap = {
592         .mode_str = "swap",
593         .init = init_task_swap,
594         .handle = handle_swap_bulk,
595         .flag_features = 0,
596         .size = sizeof(struct task_swap),
597         .stop_last = stop_swap
598 };
599
600 __attribute__((constructor)) static void reg_task_swap(void)
601 {
602         reg_task(&task_init_swap);
603 }