-
-
Save majek/5e8fd12e7a1663cea63877920fe86f18 to your computer and use it in GitHub Desktop.
udp-gro-forwarding-bug.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/env python3 | |
''' | |
$ unshare -Urn python3 udp-gro-forwarding-bug.py | |
15:25:33.947820 tap0 In IP 10.0.0.2.55892 > 1.1.1.1.5021: UDP, length 4000 | |
15:25:33.947847 lo P IP 10.0.0.2.55892 > 1.1.1.1.5021: UDP, bad length 4000 > 1392 | |
15:25:33.947849 lo P IP 10.0.0.2.43690 > 1.1.1.1.43690: UDP, bad length 43682 > 1392 | |
15:25:33.947852 lo P IP 10.0.0.2.43690 > 1.1.1.1.43690: UDP, bad length 43682 > 1200 | |
''' | |
import os | |
import fcntl | |
import struct | |
import binascii | |
import subprocess | |
import sys | |
import time | |
import signal | |
tapname = "tap0" | |
ethname = "lo" | |
os.system("ip l set %s up" % ethname) | |
os.system("ip tuntap del mode tap %s" % tapname) | |
os.system("ip tuntap add mode tap name %s" % tapname) | |
os.system("ip l set %s up" % tapname) | |
os.system("ip l set dev %s addr 00:44:33:22:11:00" % tapname) | |
os.system("ip a add 10.0.0.1/24 dev %s" % tapname) | |
os.system("ip neigh add 10.0.0.2 dev %s lladdr 00:11:22:33:44:55" % tapname) | |
os.system("tc qdisc add dev %s ingress" % tapname) | |
os.system("tc filter add dev %s ingress pref 10 protocol ip u32 match ip src 10.0.0.2 action mirred egress redirect dev %s" % (tapname, ethname)) | |
# see https://github.com/the-tcpdump-group/tcpdump/issues/1062 | |
# some versions of tcpdump refuse to work under `unshare` | |
pid = subprocess.Popen(("strace -e trace=setgroups -o /dev/null -e inject=setgroups:retval=0 tcpdump -ln -Zroot -q -iany -c4 host 10.0.0.2" % ()).split(), stdout=sys.stdout) | |
pid.poll() | |
time.sleep(0.3) | |
TUNSETIFF = 0x400454ca | |
TUNSETVNETHDRSZ = 0x400454d8 | |
TUNSETOFFLOAD = 0x400454d0 | |
IFF_TAP = 0x0002 | |
IFF_NO_PI = 0x1000 | |
IFF_VNET_HDR = 0x4000 | |
TUN_F_CSUM = 1 | |
TUN_F_TSO4 = 2 | |
TUN_F_TSO6 = 4 | |
TUN_F_USO4 = 0x20 | |
TUN_F_USO6 = 0x40 | |
tap_fd = os.open("/dev/net/tun", os.O_RDWR) | |
ifs = fcntl.ioctl(tap_fd, | |
TUNSETIFF, | |
struct.pack("16sH", | |
tapname.encode(), | |
IFF_TAP | IFF_NO_PI | IFF_VNET_HDR)) | |
ifname = ifs[:16].strip(b"\x00") | |
fcntl.ioctl(tap_fd, TUNSETVNETHDRSZ, struct.pack("I", 12)) | |
off_flags = TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 | TUN_F_USO4 | TUN_F_USO6 | |
fcntl.ioctl(tap_fd, TUNSETOFFLOAD, off_flags); | |
''' | |
struct virtio_net_hdr { | |
#define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 | |
#define VIRTIO_NET_HDR_F_DATA_VALID 2 | |
#define VIRTIO_NET_HDR_F_RSC_INFO 4 | |
uint8_t flags; | |
#define VIRTIO_NET_HDR_GSO_NONE 0 | |
#define VIRTIO_NET_HDR_GSO_TCPV4 1 | |
#define VIRTIO_NET_HDR_GSO_UDP 3 | |
#define VIRTIO_NET_HDR_GSO_TCPV6 4 | |
#define VIRTIO_NET_HDR_GSO_UDP_L4 5 | |
#define VIRTIO_NET_HDR_GSO_ECN 0x80 | |
uint8_t gso_type; | |
uint16_t hdr_len; | |
uint16_t gso_size; | |
uint16_t csum_start; | |
uint16_t csum_offset; | |
uint16_t num_buffers; | |
}; | |
vhdr: | |
flags=0x1 --> VIRTIO_NET_HDR_F_NEEDS_CSUM | |
gso_type=0x5 --> VIRTIO_NET_HDR_GSO_UDP_L4 | |
hdr_len = 0x002a --> 42 (14+20+8) | |
gso_size = 0x0578 --> 1400 | |
csum_start = 0x0022 --> offset 34 | |
csum_offset = 0x0006 --> offset 6 | |
num_buffers = 0 | |
''' | |
payload = ''' | |
01 05 2a00 7805 2200 0600 0000 | |
004433221100 001122334455 0800 | |
45000fbc 52ce0000 40110c60 | |
0a000002 01010101 | |
da54 139d 0fa8e6a8 | |
''' | |
payload = binascii.a2b_hex(''.join(payload.split())) + (b'\xaa' * 4000) | |
os.write(tap_fd, payload) | |
time.sleep(0.1) | |
pid.send_signal(signal.SIGINT) | |
pid.send_signal(signal.SIGINT) | |
pid.wait() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment