1 // SPDX-License-Identifier: GPL-2.0
3 * Inject packets with all sorts of encapsulation into the kernel.
5 * IPv4/IPv6 outer layer 3
6 * GRE/GUE/BARE outer layer 4, where bare is IPIP/SIT/IPv4-in-IPv6/..
7 * IPv4/IPv6 inner layer 3
13 #include <arpa/inet.h>
14 #include <asm/byteorder.h>
17 #include <linux/if_packet.h>
18 #include <linux/if_ether.h>
19 #include <linux/ipv6.h>
20 #include <netinet/ip.h>
21 #include <netinet/in.h>
22 #include <netinet/udp.h>
28 #include <sys/ioctl.h>
29 #include <sys/socket.h>
32 #include <sys/types.h>
35 #define CFG_PORT_INNER 8000
37 /* Add some protocol definitions that do not exist in userspace */
42 } __attribute__((packed
));
47 #if defined(__LITTLE_ENDIAN_BITFIELD)
51 #elif defined (__BIG_ENDIAN_BITFIELD)
56 #error "Please fix <asm/byteorder.h>"
65 static uint8_t cfg_dsfield_inner
;
66 static uint8_t cfg_dsfield_outer
;
67 static uint8_t cfg_encap_proto
;
68 static bool cfg_expect_failure
= false;
69 static int cfg_l3_extra
= AF_UNSPEC
; /* optional SIT prefix */
70 static int cfg_l3_inner
= AF_UNSPEC
;
71 static int cfg_l3_outer
= AF_UNSPEC
;
72 static int cfg_num_pkt
= 10;
73 static int cfg_num_secs
= 0;
74 static char cfg_payload_char
= 'a';
75 static int cfg_payload_len
= 100;
76 static int cfg_port_gue
= 6080;
77 static bool cfg_only_rx
;
78 static bool cfg_only_tx
;
79 static int cfg_src_port
= 9;
81 static char buf
[ETH_DATA_LEN
];
83 #define INIT_ADDR4(name, addr4, port) \
84 static struct sockaddr_in name = { \
85 .sin_family = AF_INET, \
86 .sin_port = __constant_htons(port), \
87 .sin_addr.s_addr = __constant_htonl(addr4), \
90 #define INIT_ADDR6(name, addr6, port) \
91 static struct sockaddr_in6 name = { \
92 .sin6_family = AF_INET6, \
93 .sin6_port = __constant_htons(port), \
97 INIT_ADDR4(in_daddr4
, INADDR_LOOPBACK
, CFG_PORT_INNER
)
98 INIT_ADDR4(in_saddr4
, INADDR_LOOPBACK
+ 2, 0)
99 INIT_ADDR4(out_daddr4
, INADDR_LOOPBACK
, 0)
100 INIT_ADDR4(out_saddr4
, INADDR_LOOPBACK
+ 1, 0)
101 INIT_ADDR4(extra_daddr4
, INADDR_LOOPBACK
, 0)
102 INIT_ADDR4(extra_saddr4
, INADDR_LOOPBACK
+ 1, 0)
104 INIT_ADDR6(in_daddr6
, IN6ADDR_LOOPBACK_INIT
, CFG_PORT_INNER
)
105 INIT_ADDR6(in_saddr6
, IN6ADDR_LOOPBACK_INIT
, 0)
106 INIT_ADDR6(out_daddr6
, IN6ADDR_LOOPBACK_INIT
, 0)
107 INIT_ADDR6(out_saddr6
, IN6ADDR_LOOPBACK_INIT
, 0)
108 INIT_ADDR6(extra_daddr6
, IN6ADDR_LOOPBACK_INIT
, 0)
109 INIT_ADDR6(extra_saddr6
, IN6ADDR_LOOPBACK_INIT
, 0)
111 static unsigned long util_gettime(void)
115 gettimeofday(&tv
, NULL
);
116 return (tv
.tv_sec
* 1000) + (tv
.tv_usec
/ 1000);
119 static void util_printaddr(const char *msg
, struct sockaddr
*addr
)
121 unsigned long off
= 0;
122 char nbuf
[INET6_ADDRSTRLEN
];
124 switch (addr
->sa_family
) {
126 off
= __builtin_offsetof(struct sockaddr_in
, sin_addr
);
129 off
= __builtin_offsetof(struct sockaddr_in6
, sin6_addr
);
132 error(1, 0, "printaddr: unsupported family %u\n",
136 if (!inet_ntop(addr
->sa_family
, ((void *) addr
) + off
, nbuf
,
138 error(1, errno
, "inet_ntop");
140 fprintf(stderr
, "%s: %s\n", msg
, nbuf
);
143 static unsigned long add_csum_hword(const uint16_t *start
, int num_u16
)
145 unsigned long sum
= 0;
148 for (i
= 0; i
< num_u16
; i
++)
154 static uint16_t build_ip_csum(const uint16_t *start
, int num_u16
,
157 sum
+= add_csum_hword(start
, num_u16
);
160 sum
= (sum
& 0xffff) + (sum
>> 16);
165 static void build_ipv4_header(void *header
, uint8_t proto
,
166 uint32_t src
, uint32_t dst
,
167 int payload_len
, uint8_t tos
)
169 struct iphdr
*iph
= header
;
175 iph
->tot_len
= htons(sizeof(*iph
) + payload_len
);
176 iph
->id
= htons(1337);
177 iph
->protocol
= proto
;
180 iph
->check
= build_ip_csum((void *) iph
, iph
->ihl
<< 1, 0);
183 static void ipv6_set_dsfield(struct ipv6hdr
*ip6h
, uint8_t dsfield
)
185 uint16_t val
, *ptr
= (uint16_t *)ip6h
;
189 val
|= ((uint16_t) dsfield
) << 4;
193 static void build_ipv6_header(void *header
, uint8_t proto
,
194 struct sockaddr_in6
*src
,
195 struct sockaddr_in6
*dst
,
196 int payload_len
, uint8_t dsfield
)
198 struct ipv6hdr
*ip6h
= header
;
201 ip6h
->payload_len
= htons(payload_len
);
202 ip6h
->nexthdr
= proto
;
204 ipv6_set_dsfield(ip6h
, dsfield
);
206 memcpy(&ip6h
->saddr
, &src
->sin6_addr
, sizeof(ip6h
->saddr
));
207 memcpy(&ip6h
->daddr
, &dst
->sin6_addr
, sizeof(ip6h
->daddr
));
210 static uint16_t build_udp_v4_csum(const struct iphdr
*iph
,
211 const struct udphdr
*udph
,
214 unsigned long pseudo_sum
;
215 int num_u16
= sizeof(iph
->saddr
); /* halfwords: twice byte len */
217 pseudo_sum
= add_csum_hword((void *) &iph
->saddr
, num_u16
);
218 pseudo_sum
+= htons(IPPROTO_UDP
);
219 pseudo_sum
+= udph
->len
;
220 return build_ip_csum((void *) udph
, num_words
, pseudo_sum
);
223 static uint16_t build_udp_v6_csum(const struct ipv6hdr
*ip6h
,
224 const struct udphdr
*udph
,
227 unsigned long pseudo_sum
;
228 int num_u16
= sizeof(ip6h
->saddr
); /* halfwords: twice byte len */
230 pseudo_sum
= add_csum_hword((void *) &ip6h
->saddr
, num_u16
);
231 pseudo_sum
+= htons(ip6h
->nexthdr
);
232 pseudo_sum
+= ip6h
->payload_len
;
233 return build_ip_csum((void *) udph
, num_words
, pseudo_sum
);
236 static void build_udp_header(void *header
, int payload_len
,
237 uint16_t dport
, int family
)
239 struct udphdr
*udph
= header
;
240 int len
= sizeof(*udph
) + payload_len
;
242 udph
->source
= htons(cfg_src_port
);
243 udph
->dest
= htons(dport
);
244 udph
->len
= htons(len
);
246 if (family
== AF_INET
)
247 udph
->check
= build_udp_v4_csum(header
- sizeof(struct iphdr
),
250 udph
->check
= build_udp_v6_csum(header
- sizeof(struct ipv6hdr
),
254 static void build_gue_header(void *header
, uint8_t proto
)
256 struct guehdr
*gueh
= header
;
258 gueh
->proto_ctype
= proto
;
261 static void build_gre_header(void *header
, uint16_t proto
)
263 struct grehdr
*greh
= header
;
265 greh
->protocol
= htons(proto
);
268 static int l3_length(int family
)
270 if (family
== AF_INET
)
271 return sizeof(struct iphdr
);
273 return sizeof(struct ipv6hdr
);
276 static int build_packet(void)
278 int ol3_len
= 0, ol4_len
= 0, il3_len
= 0, il4_len
= 0;
282 el3_len
= l3_length(cfg_l3_extra
);
284 /* calculate header offsets */
285 if (cfg_encap_proto
) {
286 ol3_len
= l3_length(cfg_l3_outer
);
288 if (cfg_encap_proto
== IPPROTO_GRE
)
289 ol4_len
= sizeof(struct grehdr
);
290 else if (cfg_encap_proto
== IPPROTO_UDP
)
291 ol4_len
= sizeof(struct udphdr
) + sizeof(struct guehdr
);
294 il3_len
= l3_length(cfg_l3_inner
);
295 il4_len
= sizeof(struct udphdr
);
297 if (el3_len
+ ol3_len
+ ol4_len
+ il3_len
+ il4_len
+ cfg_payload_len
>=
299 error(1, 0, "packet too large\n");
302 * Fill packet from inside out, to calculate correct checksums.
303 * But create ip before udp headers, as udp uses ip for pseudo-sum.
305 memset(buf
+ el3_len
+ ol3_len
+ ol4_len
+ il3_len
+ il4_len
,
306 cfg_payload_char
, cfg_payload_len
);
308 /* add zero byte for udp csum padding */
309 buf
[el3_len
+ ol3_len
+ ol4_len
+ il3_len
+ il4_len
+ cfg_payload_len
] = 0;
311 switch (cfg_l3_inner
) {
313 build_ipv4_header(buf
+ el3_len
+ ol3_len
+ ol4_len
,
315 in_saddr4
.sin_addr
.s_addr
,
316 in_daddr4
.sin_addr
.s_addr
,
317 il4_len
+ cfg_payload_len
,
321 build_ipv6_header(buf
+ el3_len
+ ol3_len
+ ol4_len
,
323 &in_saddr6
, &in_daddr6
,
324 il4_len
+ cfg_payload_len
,
329 build_udp_header(buf
+ el3_len
+ ol3_len
+ ol4_len
+ il3_len
,
330 cfg_payload_len
, CFG_PORT_INNER
, cfg_l3_inner
);
332 if (!cfg_encap_proto
)
333 return il3_len
+ il4_len
+ cfg_payload_len
;
335 switch (cfg_l3_outer
) {
337 build_ipv4_header(buf
+ el3_len
, cfg_encap_proto
,
338 out_saddr4
.sin_addr
.s_addr
,
339 out_daddr4
.sin_addr
.s_addr
,
340 ol4_len
+ il3_len
+ il4_len
+ cfg_payload_len
,
344 build_ipv6_header(buf
+ el3_len
, cfg_encap_proto
,
345 &out_saddr6
, &out_daddr6
,
346 ol4_len
+ il3_len
+ il4_len
+ cfg_payload_len
,
351 switch (cfg_encap_proto
) {
353 build_gue_header(buf
+ el3_len
+ ol3_len
+ ol4_len
-
354 sizeof(struct guehdr
),
355 cfg_l3_inner
== PF_INET
? IPPROTO_IPIP
357 build_udp_header(buf
+ el3_len
+ ol3_len
,
358 sizeof(struct guehdr
) + il3_len
+ il4_len
+
360 cfg_port_gue
, cfg_l3_outer
);
363 build_gre_header(buf
+ el3_len
+ ol3_len
,
364 cfg_l3_inner
== PF_INET
? ETH_P_IP
369 switch (cfg_l3_extra
) {
371 build_ipv4_header(buf
,
372 cfg_l3_outer
== PF_INET
? IPPROTO_IPIP
374 extra_saddr4
.sin_addr
.s_addr
,
375 extra_daddr4
.sin_addr
.s_addr
,
376 ol3_len
+ ol4_len
+ il3_len
+ il4_len
+
380 build_ipv6_header(buf
,
381 cfg_l3_outer
== PF_INET
? IPPROTO_IPIP
383 &extra_saddr6
, &extra_daddr6
,
384 ol3_len
+ ol4_len
+ il3_len
+ il4_len
+
389 return el3_len
+ ol3_len
+ ol4_len
+ il3_len
+ il4_len
+
393 /* sender transmits encapsulated over RAW or unencap'd over UDP */
394 static int setup_tx(void)
399 family
= cfg_l3_extra
;
400 else if (cfg_l3_outer
)
401 family
= cfg_l3_outer
;
403 family
= cfg_l3_inner
;
405 fd
= socket(family
, SOCK_RAW
, IPPROTO_RAW
);
407 error(1, errno
, "socket tx");
410 if (cfg_l3_extra
== PF_INET
)
411 ret
= connect(fd
, (void *) &extra_daddr4
,
412 sizeof(extra_daddr4
));
414 ret
= connect(fd
, (void *) &extra_daddr6
,
415 sizeof(extra_daddr6
));
417 error(1, errno
, "connect tx");
418 } else if (cfg_l3_outer
) {
419 /* connect to destination if not encapsulated */
420 if (cfg_l3_outer
== PF_INET
)
421 ret
= connect(fd
, (void *) &out_daddr4
,
424 ret
= connect(fd
, (void *) &out_daddr6
,
427 error(1, errno
, "connect tx");
429 /* otherwise using loopback */
430 if (cfg_l3_inner
== PF_INET
)
431 ret
= connect(fd
, (void *) &in_daddr4
,
434 ret
= connect(fd
, (void *) &in_daddr6
,
437 error(1, errno
, "connect tx");
443 /* receiver reads unencapsulated UDP */
444 static int setup_rx(void)
448 fd
= socket(cfg_l3_inner
, SOCK_DGRAM
, 0);
450 error(1, errno
, "socket rx");
452 if (cfg_l3_inner
== PF_INET
)
453 ret
= bind(fd
, (void *) &in_daddr4
, sizeof(in_daddr4
));
455 ret
= bind(fd
, (void *) &in_daddr6
, sizeof(in_daddr6
));
457 error(1, errno
, "bind rx");
462 static int do_tx(int fd
, const char *pkt
, int len
)
466 ret
= write(fd
, pkt
, len
);
468 error(1, errno
, "send");
470 error(1, errno
, "send: len (%d < %d)\n", ret
, len
);
475 static int do_poll(int fd
, short events
, int timeout
)
483 ret
= poll(&pfd
, 1, timeout
);
485 error(1, errno
, "poll");
486 if (ret
&& !(pfd
.revents
& POLLIN
))
487 error(1, errno
, "poll: unexpected event 0x%x\n", pfd
.revents
);
492 static int do_rx(int fd
)
498 ret
= recv(fd
, &rbuf
, 1, MSG_DONTWAIT
);
499 if (ret
== -1 && errno
== EAGAIN
)
502 error(1, errno
, "recv");
503 if (rbuf
!= cfg_payload_char
)
504 error(1, 0, "recv: payload mismatch");
511 static int do_main(void)
513 unsigned long tstop
, treport
, tcur
;
514 int fdt
= -1, fdr
= -1, len
, tx
= 0, rx
= 0;
521 len
= build_packet();
523 tcur
= util_gettime();
524 treport
= tcur
+ 1000;
525 tstop
= tcur
+ (cfg_num_secs
* 1000);
529 tx
+= do_tx(fdt
, buf
, len
);
535 tcur
= util_gettime();
538 if (tcur
>= treport
) {
539 fprintf(stderr
, "pkts: tx=%u rx=%u\n", tx
, rx
);
542 treport
= tcur
+ 1000;
545 if (tx
== cfg_num_pkt
)
550 /* read straggler packets, if any */
552 tstop
= util_gettime() + 100;
554 tcur
= util_gettime();
558 do_poll(fdr
, POLLIN
, tstop
- tcur
);
563 fprintf(stderr
, "pkts: tx=%u rx=%u\n", tx
, rx
);
565 if (fdr
!= -1 && close(fdr
))
566 error(1, errno
, "close rx");
567 if (fdt
!= -1 && close(fdt
))
568 error(1, errno
, "close tx");
571 * success (== 0) only if received all packets
572 * unless failure is expected, in which case none must arrive.
574 if (cfg_expect_failure
)
581 static void __attribute__((noreturn
)) usage(const char *filepath
)
583 fprintf(stderr
, "Usage: %s [-e gre|gue|bare|none] [-i 4|6] [-l len] "
584 "[-O 4|6] [-o 4|6] [-n num] [-t secs] [-R] [-T] "
585 "[-s <osrc> [-d <odst>] [-S <isrc>] [-D <idst>] "
586 "[-x <otos>] [-X <itos>] [-f <isport>] [-F]\n",
591 static void parse_addr(int family
, void *addr
, const char *optarg
)
595 ret
= inet_pton(family
, optarg
, addr
);
597 error(1, errno
, "inet_pton");
599 error(1, 0, "inet_pton: bad string");
602 static void parse_addr4(struct sockaddr_in
*addr
, const char *optarg
)
604 parse_addr(AF_INET
, &addr
->sin_addr
, optarg
);
607 static void parse_addr6(struct sockaddr_in6
*addr
, const char *optarg
)
609 parse_addr(AF_INET6
, &addr
->sin6_addr
, optarg
);
612 static int parse_protocol_family(const char *filepath
, const char *optarg
)
614 if (!strcmp(optarg
, "4"))
616 if (!strcmp(optarg
, "6"))
622 static void parse_opts(int argc
, char **argv
)
626 while ((c
= getopt(argc
, argv
, "d:D:e:f:Fhi:l:n:o:O:Rs:S:t:Tx:X:")) != -1) {
629 if (cfg_l3_outer
== AF_UNSPEC
)
630 error(1, 0, "-d must be preceded by -o");
631 if (cfg_l3_outer
== AF_INET
)
632 parse_addr4(&out_daddr4
, optarg
);
634 parse_addr6(&out_daddr6
, optarg
);
637 if (cfg_l3_inner
== AF_UNSPEC
)
638 error(1, 0, "-D must be preceded by -i");
639 if (cfg_l3_inner
== AF_INET
)
640 parse_addr4(&in_daddr4
, optarg
);
642 parse_addr6(&in_daddr6
, optarg
);
645 if (!strcmp(optarg
, "gre"))
646 cfg_encap_proto
= IPPROTO_GRE
;
647 else if (!strcmp(optarg
, "gue"))
648 cfg_encap_proto
= IPPROTO_UDP
;
649 else if (!strcmp(optarg
, "bare"))
650 cfg_encap_proto
= IPPROTO_IPIP
;
651 else if (!strcmp(optarg
, "none"))
652 cfg_encap_proto
= IPPROTO_IP
; /* == 0 */
657 cfg_src_port
= strtol(optarg
, NULL
, 0);
660 cfg_expect_failure
= true;
666 if (!strcmp(optarg
, "4"))
667 cfg_l3_inner
= PF_INET
;
668 else if (!strcmp(optarg
, "6"))
669 cfg_l3_inner
= PF_INET6
;
674 cfg_payload_len
= strtol(optarg
, NULL
, 0);
677 cfg_num_pkt
= strtol(optarg
, NULL
, 0);
680 cfg_l3_outer
= parse_protocol_family(argv
[0], optarg
);
683 cfg_l3_extra
= parse_protocol_family(argv
[0], optarg
);
689 if (cfg_l3_outer
== AF_INET
)
690 parse_addr4(&out_saddr4
, optarg
);
692 parse_addr6(&out_saddr6
, optarg
);
695 if (cfg_l3_inner
== AF_INET
)
696 parse_addr4(&in_saddr4
, optarg
);
698 parse_addr6(&in_saddr6
, optarg
);
701 cfg_num_secs
= strtol(optarg
, NULL
, 0);
707 cfg_dsfield_outer
= strtol(optarg
, NULL
, 0);
710 cfg_dsfield_inner
= strtol(optarg
, NULL
, 0);
715 if (cfg_only_rx
&& cfg_only_tx
)
716 error(1, 0, "options: cannot combine rx-only and tx-only");
718 if (cfg_encap_proto
&& cfg_l3_outer
== AF_UNSPEC
)
719 error(1, 0, "options: must specify outer with encap");
720 else if ((!cfg_encap_proto
) && cfg_l3_outer
!= AF_UNSPEC
)
721 error(1, 0, "options: cannot combine no-encap and outer");
722 else if ((!cfg_encap_proto
) && cfg_l3_extra
!= AF_UNSPEC
)
723 error(1, 0, "options: cannot combine no-encap and extra");
725 if (cfg_l3_inner
== AF_UNSPEC
)
726 cfg_l3_inner
= AF_INET6
;
727 if (cfg_l3_inner
== AF_INET6
&& cfg_encap_proto
== IPPROTO_IPIP
)
728 cfg_encap_proto
= IPPROTO_IPV6
;
731 * on decap, if outer encountered congestion (CE == 0x3),
732 * but inner cannot encode ECN (NoECT == 0x0), then drop packet.
734 if (((cfg_dsfield_outer
& 0x3) == 0x3) &&
735 ((cfg_dsfield_inner
& 0x3) == 0x0))
736 cfg_expect_failure
= true;
739 static void print_opts(void)
741 if (cfg_l3_inner
== PF_INET6
) {
742 util_printaddr("inner.dest6", (void *) &in_daddr6
);
743 util_printaddr("inner.source6", (void *) &in_saddr6
);
745 util_printaddr("inner.dest4", (void *) &in_daddr4
);
746 util_printaddr("inner.source4", (void *) &in_saddr4
);
752 fprintf(stderr
, "encap proto: %u\n", cfg_encap_proto
);
754 if (cfg_l3_outer
== PF_INET6
) {
755 util_printaddr("outer.dest6", (void *) &out_daddr6
);
756 util_printaddr("outer.source6", (void *) &out_saddr6
);
758 util_printaddr("outer.dest4", (void *) &out_daddr4
);
759 util_printaddr("outer.source4", (void *) &out_saddr4
);
765 if (cfg_l3_outer
== PF_INET6
) {
766 util_printaddr("extra.dest6", (void *) &extra_daddr6
);
767 util_printaddr("extra.source6", (void *) &extra_saddr6
);
769 util_printaddr("extra.dest4", (void *) &extra_daddr4
);
770 util_printaddr("extra.source4", (void *) &extra_saddr4
);
775 int main(int argc
, char **argv
)
777 parse_opts(argc
, argv
);