blob: fafc8e425b9425abf062db257c7cf3f5998162f4 [file] [log] [blame]
Jakub Grajciare74c04f2021-01-04 11:28:33 +01001/*
2 *------------------------------------------------------------------
3 * Copyright (c) 2017 Cisco and/or its affiliates.
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
18#include <stdint.h>
19#include <net/if.h>
20#include <sys/types.h>
21#include <fcntl.h>
22#include <sys/ioctl.h>
23#include <sys/socket.h>
24#include <sys/un.h>
25#include <sys/uio.h>
26#include <sys/mman.h>
27#include <sys/prctl.h>
28#include <inttypes.h>
29#include <string.h>
30#include <stdio.h>
31#include <netdb.h>
32#include <linux/ip.h>
33#include <linux/icmp.h>
34#include <arpa/inet.h>
35#include <stdlib.h>
36#include <netinet/if_ether.h>
37#include <net/if_arp.h>
38#include <asm/byteorder.h>
39#include <byteswap.h>
40#include <assert.h>
41
42#include <icmp_proto.h>
43
44static uint16_t
45cksum (void *addr, ssize_t len)
46{
47 char *data = (char *) addr;
48
49 uint32_t acc = 0xffff;
50
51 ssize_t i;
52 for (i = 0; (i + 1) < len; i += 2)
53 {
54 uint16_t word;
55 memcpy (&word, data + i, 2);
56 acc += ntohs (word);
57 if (acc > 0xffff)
58 acc -= 0xffff;
59 }
60
61 if (len & 1)
62 {
63 uint16_t word = 0;
64 memcpy (&word, data + len - 1, 1);
65 acc += ntohs (word);
66 if (acc > 0xffff)
67 acc -= 0xffff;
68 }
69 return htons (~acc);
70}
71
72int
73print_packet (void *pck)
74{
75 if (pck == NULL)
76 {
77 printf ("ICMP_PROTO: no data\n");
78 return -1;
79 }
80 struct iphdr *ip;
81 struct icmphdr *icmp;
82 ip = (struct iphdr *) pck;
83 icmp = (struct icmphdr *) (pck + sizeof (struct iphdr));
84 printf ("received packet:\n");
85 printf ("\tiphdr:\n");
86 printf ("\t\tihl: %u\n\t\tversion: %u\n\t\tlen: %u\n\t\tid: %u\n", ip->ihl,
87 ip->version, __bswap_16 (ip->tot_len), ip->id);
88 printf ("\t\tprotocol: %u\n", ip->protocol);
89
90 printf ("\t\tsaddr: ");
91 int i;
92 for (i = 0; i < 4; i++)
93 {
94 printf ("%u.", ((uint8_t *) &ip->saddr)[i]);
95 }
96 printf ("\n");
97
98 printf ("\t\tdaddr: ");
99 for (i = 0; i < 4; i++)
100 {
101 printf ("%u.", ((uint8_t *) &ip->daddr)[i]);
102 }
103 printf ("\n");
104 printf ("\ticmphdr:\n");
105 printf ("\t\ttype: %s\n",
106 (icmp->type == ICMP_ECHO) ? "ICMP_ECHO" : "ICMP_ECHOREPLY");
107
108 return 0;
109}
110
111static ssize_t
112resolve_arp (void *arp)
113{
114 struct arphdr *resp = (struct arphdr *) arp;
115
116 resp->ar_hrd = __bswap_16 (ARPHRD_ETHER);
117
118 resp->ar_pro = __bswap_16 (0x0800);
119
120 resp->ar_hln = 6;
121 resp->ar_pln = 4;
122
123 resp->ar_op = __bswap_16 (ARPOP_REPLY);
124
125 return sizeof (struct arphdr);
126}
127
128static ssize_t
129resolve_eth_arp (struct ether_arp *eth_arp, void *eth_arp_resp,
130 uint8_t ip_addr[4])
131{
132 struct ether_arp *resp = (struct ether_arp *) eth_arp_resp;
133
134 resolve_arp (&resp->ea_hdr);
135
136 memcpy (resp->arp_tha, eth_arp->arp_sha, 6);
137 memcpy (resp->arp_tpa, eth_arp->arp_spa, 4);
138
139 memcpy (
140 resp->arp_sha,
141 (((struct ether_header *) (eth_arp_resp - sizeof (struct ether_header)))
142 ->ether_shost),
143 6);
144
145 memcpy (resp->arp_spa, ip_addr, 4);
146
147 return sizeof (struct ether_arp);
148}
149
150static ssize_t
151resolve_eth (struct ether_header *eth, void *eth_resp, uint8_t hw_addr[6])
152{
153 struct ether_header *resp = (struct ether_header *) eth_resp;
154 memcpy (resp->ether_dhost, eth->ether_shost, 6);
155
156 memcpy (resp->ether_shost, hw_addr, 6);
157
158 resp->ether_type = eth->ether_type;
159
160 return sizeof (struct ether_header);
161}
162
163static ssize_t
164resolve_ip (struct iphdr *ip, void *ip_resp, uint8_t ip_addr[4])
165{
166 struct iphdr *resp = (struct iphdr *) ip_resp;
167 resp->ihl = 5;
168 resp->version = 4;
169 resp->tos = 0;
170 /*len updated later */
171 resp->tot_len = 0x0000;
172 resp->id = 0;
173 resp->frag_off = 0;
174 resp->ttl = 0x40;
175 resp->protocol = 1;
176 ((uint8_t *) &resp->saddr)[0] = ip_addr[0];
177 ((uint8_t *) &resp->saddr)[1] = ip_addr[1];
178 ((uint8_t *) &resp->saddr)[2] = ip_addr[2];
179 ((uint8_t *) &resp->saddr)[3] = ip_addr[3];
180 resp->daddr = ip->saddr;
181
182 /* resp->check = cksum (resp, sizeof (struct iphdr)); */
183
184 return sizeof (struct iphdr);
185}
186
187static ssize_t
188resolve_icmp (struct icmphdr *icmp, void *icmp_resp)
189{
190 struct icmphdr *resp = (struct icmphdr *) icmp_resp;
191 resp->type = 0x00;
192 resp->code = 0;
193 resp->un.echo.id = icmp->un.echo.id;
194 resp->un.echo.sequence = icmp->un.echo.sequence;
195
196 /*resp->checksum = cksum (resp, sizeof (struct icmphdr)); */
197
198 return sizeof (struct icmphdr);
199}
200
201int
202resolve_packet (void *in_pck, ssize_t in_size, void *out_pck,
203 uint32_t *out_size, uint8_t ip_addr[4], uint8_t hw_addr[6])
204{
205 struct ether_header *eh;
206 struct ether_arp *eah;
207 struct iphdr *ip, *ip_out;
208 struct icmphdr *icmp;
209 *out_size = 0;
210
211 if ((in_pck == NULL) || (out_pck == NULL))
212 return -1;
213
214 eh = (struct ether_header *) in_pck;
215 *out_size = resolve_eth (eh, out_pck, hw_addr);
216
217 if (eh->ether_type == 0x0608)
218 {
219 eah = (struct ether_arp *) (in_pck + *out_size);
220 *out_size += resolve_eth_arp (eah, out_pck + *out_size, ip_addr);
221 }
222 else if (eh->ether_type == 0x0008)
223 {
224#ifdef ICMP_DBG
225 print_packet (in_pck + *out_size);
226#endif
227 ip = (struct iphdr *) (in_pck + *out_size);
228 ip_out = (struct iphdr *) (out_pck + *out_size);
229 *out_size += resolve_ip (ip, out_pck + *out_size, ip_addr);
230 if (ip->protocol == 1)
231 {
232 icmp = (struct icmphdr *) (in_pck + *out_size);
233 *out_size += resolve_icmp (icmp, out_pck + *out_size);
234 ((struct icmphdr *) (out_pck + *out_size - sizeof (struct icmphdr)))
235 ->checksum = cksum (out_pck + *out_size - sizeof (struct icmphdr),
236 sizeof (struct icmphdr));
237 /* payload */
238 memcpy (out_pck + *out_size, in_pck + *out_size,
239 in_size - *out_size);
240 *out_size = in_size;
241 ip_out->tot_len =
242 __bswap_16 (*out_size - sizeof (struct ether_header));
243 ip_out->check = cksum (ip_out, sizeof (struct iphdr));
244 }
245 }
246 return 0;
247}
248
249static ssize_t
250generate_eth (struct ether_header *eh, uint8_t hw_daddr[6])
251{
252 uint8_t hw_addr[6];
253 int i;
254 for (i = 0; i < 6; i++)
255 {
256 hw_addr[i] = 'a';
257 }
258 memcpy (eh->ether_shost, hw_addr, 6);
259 memcpy (eh->ether_dhost, hw_daddr, 6);
260
261 eh->ether_type = 0x0008;
262
263 return sizeof (struct ether_header);
264}
265
266static ssize_t
267generate_ip (struct iphdr *ip, uint8_t saddr[4], uint8_t daddr[4])
268{
269 ip->ihl = 5;
270 ip->version = 4;
271 ip->tos = 0;
272 /*len updated later */
273 ip->tot_len = 0x5400;
274 ip->id = 0;
275 ip->frag_off = 0;
276 ip->ttl = 0x40;
277 ip->protocol = 1;
278 /* saddr */
279 ((uint8_t *) &ip->saddr)[0] = saddr[0];
280 ((uint8_t *) &ip->saddr)[1] = saddr[1];
281 ((uint8_t *) &ip->saddr)[2] = saddr[2];
282 ((uint8_t *) &ip->saddr)[3] = saddr[3];
283 /* daddr */
284 ((uint8_t *) &ip->daddr)[0] = daddr[0];
285 ((uint8_t *) &ip->daddr)[1] = daddr[1];
286 ((uint8_t *) &ip->daddr)[2] = daddr[2];
287 ((uint8_t *) &ip->daddr)[3] = daddr[3];
288
289 ip->check = cksum (ip, sizeof (struct iphdr));
290
291 return sizeof (struct iphdr);
292}
293
294static ssize_t
295generate_icmp (struct icmphdr *icmp, uint32_t seq)
296{
297 icmp->type = ICMP_ECHO;
298 icmp->code = 0;
299 icmp->un.echo.id = 0;
300 icmp->un.echo.sequence = seq;
301
302 return sizeof (struct icmphdr);
303}
304
305int
306generate_packet (void *pck, uint32_t *size, uint8_t saddr[4], uint8_t daddr[4],
307 uint8_t hw_daddr[6], uint32_t seq)
308{
309 struct ether_header *eh;
310 struct iphdr *ip;
311 struct icmphdr *icmp;
312
313 *size = 0;
314
315 eh = (struct ether_header *) pck;
316 *size += generate_eth (eh, hw_daddr);
317
318 ip = (struct iphdr *) (pck + *size);
319 *size += generate_ip (ip, saddr, daddr);
320
321 icmp = (struct icmphdr *) (pck + *size);
322 *size += generate_icmp (icmp, seq);
323
324 ((struct icmphdr *) (pck + *size - sizeof (struct icmphdr)))->checksum =
325 cksum (pck + *size - sizeof (struct icmphdr), sizeof (struct icmphdr));
326
327 ip->tot_len = __bswap_16 (*size - sizeof (struct ether_header));
328 ip->check = 0;
329 ip->check = cksum (ip, sizeof (struct iphdr));
330
331 return 0;
332}
333
334int
335generate_packet2 (void *pck, uint32_t *size, uint8_t saddr[4],
336 uint8_t daddr[4], uint8_t hw_daddr[6], uint32_t seq,
337 icmpr_flow_mode_t mode)
338{
339 struct ether_header *eh;
340 struct iphdr *ip;
341 struct icmphdr *icmp;
342
343 *size = 0;
344
345 if (mode == ICMPR_FLOW_MODE_ETH)
346 {
347 eh = (struct ether_header *) pck;
348 *size += generate_eth (eh, hw_daddr);
349 }
350
351 ip = (struct iphdr *) (pck + *size);
352 *size += generate_ip (ip, saddr, daddr);
353
354 icmp = (struct icmphdr *) (pck + *size);
355 *size += generate_icmp (icmp, seq);
356
357 ((struct icmphdr *) (pck + *size - sizeof (struct icmphdr)))->checksum =
358 cksum (pck + *size - sizeof (struct icmphdr), sizeof (struct icmphdr));
359
360 ip->tot_len = __bswap_16 (*size - sizeof (struct ether_header));
361 ip->check = 0;
362 ip->check = cksum (ip, sizeof (struct iphdr));
363
364 return 0;
365}
366
367#define GET_HEADER(out, hdr, src, off) \
368 do \
369 { \
370 out = (hdr *) (src + off); \
371 off += sizeof (hdr); \
372 } \
373 while (0)
374
375int
376resolve_packet_zero_copy (void *pck, uint32_t *size, uint8_t ip_addr[4],
377 uint8_t hw_addr[6])
378{
379 struct ether_header *eh;
380 struct ether_arp *eah;
381 struct iphdr *ip;
382 struct icmphdr *icmp;
383 uint32_t offset = 0;
384
385 if (pck == NULL)
386 return 0;
387
388 GET_HEADER (eh, struct ether_header, pck, offset);
389
390 memcpy (eh->ether_dhost, eh->ether_shost, 6);
391 memcpy (eh->ether_shost, hw_addr, 6);
392
393 if (eh->ether_type == 0x0608)
394 {
395 GET_HEADER (eah, struct ether_arp, pck, offset);
396 struct arphdr *arp = &eah->ea_hdr;
397
398 arp->ar_hrd = __bswap_16 (ARPHRD_ETHER);
399 arp->ar_pro = __bswap_16 (0x0800);
400
401 arp->ar_hln = 6;
402 arp->ar_pln = 4;
403
404 arp->ar_op = __bswap_16 (ARPOP_REPLY);
405
406 memcpy (eah->arp_tha, eah->arp_sha, 6);
407 memcpy (eah->arp_tpa, eah->arp_spa, 4);
408
409 memcpy (eah->arp_sha, eh->ether_shost, 6);
410 memcpy (eah->arp_spa, ip_addr, 4);
411 }
412
413 else if (eh->ether_type == 0x0008)
414 {
415 GET_HEADER (ip, struct iphdr, pck, offset);
416
417 if (ip->protocol == 1)
418 {
419 ip->ihl = 5;
420 ip->version = 4;
421 ip->tos = 0;
422 ip->tot_len = 0x0000;
423 ip->id = 0;
424 ip->frag_off = 0;
425 ip->ttl = 0x40;
426 ip->protocol = 1;
427 ip->check = 0x0000;
428 ip->daddr = ip->saddr;
429 ((uint8_t *) &ip->saddr)[0] = ip_addr[0];
430 ((uint8_t *) &ip->saddr)[1] = ip_addr[1];
431 ((uint8_t *) &ip->saddr)[2] = ip_addr[2];
432 ((uint8_t *) &ip->saddr)[3] = ip_addr[3];
433
434 GET_HEADER (icmp, struct icmphdr, pck, offset);
435
436 icmp->type = 0x00;
437 icmp->code = 0;
438 icmp->checksum = cksum (icmp, sizeof (struct icmphdr));
439
440 /* rest is payload */
441 offset = *size;
442
443 ip->tot_len = __bswap_16 (offset - sizeof (struct ether_header));
444 ip->check = cksum (ip, sizeof (struct iphdr));
445 }
446 }
447
448 assert (offset == *size && "unsupported protocol");
449 return 0;
450}
451
452int
453resolve_packet_zero_copy_add_encap (void **pck_, uint32_t *size,
454 uint8_t ip_addr[4])
455{
456 struct ether_header *eh;
457 struct iphdr *ip;
458 struct icmphdr *icmp;
459 int32_t offset = 0;
460 uint16_t encap_size = sizeof (struct ether_header);
461 void *pck = *pck_;
462
463 if (pck == NULL)
464 return 0;
465
466 *pck_ -= encap_size;
467 offset -= encap_size;
468
469 GET_HEADER (eh, struct ether_header, pck, offset);
470
471 uint8_t hw_daddr[6];
472 memset (hw_daddr, 0, sizeof (uint8_t) * 6);
473
474 generate_eth (eh, hw_daddr);
475
476 if (eh->ether_type == 0x0008)
477 {
478 GET_HEADER (ip, struct iphdr, pck, offset);
479
480 if (ip->protocol == 1)
481 {
482 ip->ihl = 5;
483 ip->version = 4;
484 ip->tos = 0;
485 ip->tot_len = 0x0000;
486 ip->id = 0;
487 ip->frag_off = 0;
488 ip->ttl = 0x40;
489 ip->protocol = 1;
490 ip->check = 0x0000;
491 ip->daddr = ip->saddr;
492 ((uint8_t *) &ip->saddr)[0] = ip_addr[0];
493 ((uint8_t *) &ip->saddr)[1] = ip_addr[1];
494 ((uint8_t *) &ip->saddr)[2] = ip_addr[2];
495 ((uint8_t *) &ip->saddr)[3] = ip_addr[3];
496
497 GET_HEADER (icmp, struct icmphdr, pck, offset);
498
499 icmp->type = 0x00;
500 icmp->code = 0;
501 icmp->checksum = cksum (icmp, sizeof (struct icmphdr));
502
503 /* rest is payload */
504 offset = *size;
505
506 ip->tot_len = __bswap_16 (offset - sizeof (struct ether_header));
507 ip->check = cksum (ip, sizeof (struct iphdr));
508 }
509 }
510
511 offset += encap_size;
512
513 assert (offset != *size &&
514 "new packet length must be increased by encap size");
515
516 /* overwrite packet size */
517 *size = offset;
518
519 return 0;
520}