Anonymous
Включить GSO на Ubuntu 22.04 LTS
Сообщение
Anonymous » 31 авг 2025, 01:05
Я работаю с устройством Linux Tun (MTU 65535), где я пишу пакеты Jumbo TCP (10 кб+), и моя цель-для ядра (через программное обеспечение GSO) или NIC (через TSO, если таковые имеются), чтобы сегментировать их на кусочки размера MTU перед отправкой через ETH0 (MTU 1500). Я понимаю, что это включает в себя создание интерфейса TUN с iff_vnet_hdr , включение GSO через TunsetOffload и позволяя сегментации сетей в сети ядра, а не выполнять это в пространстве пользователя. Тем не менее, я изо всех сил пытаюсь установить соединение, когда GSO включена, даже если та же самая установка работает нормально, когда GSO отключен. Кто -нибудь успешно добился сегментации TCP таким образом и мог бы поделиться минимальным рабочим примером или руководством (некоторые документы также будут высоко оценены)?
Код: Выделить всё
package tun
import (
"fmt"
"os"
"syscall"
"unsafe"
)
const (
IFF_TUN = 0x0001
IFF_NO_PI = 0x1000
IFF_VNET_HDR = 0x4000
TUNSETIFF = 0x400454ca
TUNSETVNETHDRSZ = 0x400454d8
TUNSETOFFLOAD = 0x400454d0
TUN_OFFLOAD_CSUM = 0x01
TUN_OFFLOAD_GSO = 0x02
)
type ifreq struct {
Name [16]byte
Flags uint16
_ [22]byte
}
func CreateTUN(name string) (*os.File, error) {
f, err := os.OpenFile("/dev/net/tun", os.O_RDWR, 0)
if err != nil {
return nil, fmt.Errorf("failed to open /dev/net/tun: %v", err)
}
var ifr ifreq
copy(ifr.Name[:], name)
ifr.Flags = IFF_TUN | IFF_NO_PI | IFF_VNET_HDR
_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), uintptr(TUNSETIFF), uintptr(unsafe.Pointer(&ifr)))
if errno != 0 {
return nil, fmt.Errorf("TUNSETIFF failed: %v", errno)
}
if err := EnableGSO(f.Fd()); err != nil {
return nil, err
}
return f, nil
}
func EnableGSO(tunFileFd uintptr) error {
hdrSize := uint32(12) // virtio-net header size
if _, _, errno := syscall.Syscall(
syscall.SYS_IOCTL, tunFileFd,
uintptr(TUNSETVNETHDRSZ),
uintptr(unsafe.Pointer(&hdrSize)),
); errno != 0 {
return fmt.Errorf("TUNSETVNETHDRSZ failed: %v", errno)
}
if _, _, errno := syscall.Syscall(
syscall.SYS_IOCTL, tunFileFd,
uintptr(TUNSETOFFLOAD),
uintptr(TUN_OFFLOAD_GSO|TUN_OFFLOAD_CSUM),
); errno != 0 {
return fmt.Errorf("TUNSETOFFLOAD failed: %v", errno)
}
fmt.Printf("Enabled GSO+CSUM with virtio-net headers on TUN (fd=%d)\n", tunFileFd)
return nil
}
< /code>
И это часть, которая добавляет vnet_header в пакет, прежде чем он будет записан в TUN: < /p>
package main
import (
"encoding/binary"
"errors"
)
const (
VIRTIO_NET_HDR_F_NEEDS_CSUM = 1
VIRTIO_NET_HDR_GSO_NONE = 0
VIRTIO_NET_HDR_GSO_TCPV4 = 1
VIRTIO_NET_HDR_GSO_TCPV6 = 4
TCP_CHECKSUM_OFFSET = 16
)
type VirtioNetHdr struct {
Flags uint8
GSOType uint8
HdrLen uint16
GSOSize uint16
CSumStart uint16
CSumOffset uint16
}
func PrependVnetHeader(pkt []byte, mtu int) ([]byte, error) {
if len(pkt) < 1 {
return nil, errors.New("empty packet")
}
ipVersion := (pkt[0] & 0xF0) >> 4
var hdr VirtioNetHdr
switch ipVersion {
case 4: // IPv4
if len(pkt) < 20 {
return nil, errors.New("invalid IPv4 packet")
}
if pkt[9] != 6 { // Only TCP
break
}
ipHeaderLen := int(pkt[0]&0x0F) * 4
tcpLen := len(pkt) - ipHeaderLen
hdr.Flags = VIRTIO_NET_HDR_F_NEEDS_CSUM
hdr.HdrLen = uint16(ipHeaderLen + 20)
hdr.CSumStart = uint16(ipHeaderLen)
hdr.CSumOffset = TCP_CHECKSUM_OFFSET
if tcpLen > mtu {
hdr.GSOType = VIRTIO_NET_HDR_GSO_TCPV4
hdr.GSOSize = uint16(mtu)
} else {
hdr.GSOType = VIRTIO_NET_HDR_GSO_NONE
}
case 6: // IPv6
if len(pkt) < 40 {
return nil, errors.New("invalid IPv6 packet")
}
if pkt[6] != 6 { // Only TCP
break
}
ipHeaderLen := 40
tcpLen := len(pkt) - ipHeaderLen
hdr.Flags = VIRTIO_NET_HDR_F_NEEDS_CSUM
hdr.HdrLen = uint16(ipHeaderLen + 20)
hdr.CSumStart = uint16(ipHeaderLen)
hdr.CSumOffset = TCP_CHECKSUM_OFFSET
if tcpLen > mtu {
hdr.GSOType = VIRTIO_NET_HDR_GSO_TCPV6
hdr.GSOSize = uint16(mtu)
} else {
hdr.GSOType = VIRTIO_NET_HDR_GSO_NONE
}
}
// Serialize header + packet
buf := make([]byte, 12+len(pkt))
buf[0] = hdr.Flags
buf[1] = hdr.GSOType
binary.LittleEndian.PutUint16(buf[2:4], hdr.HdrLen)
binary.LittleEndian.PutUint16(buf[4:6], hdr.GSOSize)
binary.LittleEndian.PutUint16(buf[6:8], hdr.CSumStart)
binary.LittleEndian.PutUint16(buf[8:10], hdr.CSumOffset)
// buf[10:12] reserved = 0
copy(buf[12:], pkt)
return buf, nil
}
< /code>
Вот мои PCAP на ETH0, когда я пытаюсь проверить его: < /p>
root@2458c936acdb:/# tcpdump -i eth0 port 80 -nn -vv -xx
tcpdump: listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
20:57:13.100414 IP (tos 0x0, ttl 63, id 60084, offset 0, flags [DF], proto TCP (6), length 60)
172.17.0.2.56018 > 150.171.28.10.80: Flags [S], cksum 0xafdf (correct), seq 3841106198, win 64240, options [mss 63900,sackOK,TS val 2498122859 ecr 0,nop,wscale 7], length 0
0x0000: 0242 fe82 3318 32e1 1aa3 4686 0800 4500
0x0010: 003c eab4 4000 3f06 f23e ac11 0002 96ab
0x0020: 1c0a dad2 0050 e4f2 a116 0000 0000 a002
0x0030: faf0 afdf 0000 0204 f99c 0402 080a 94e6
0x0040: 546b 0000 0000 0103 0307
20:57:13.103550 IP (tos 0x0, ttl 110, id 3013, offset 0, flags [DF], proto TCP (6), length 52)
150.171.28.10.80 > 172.17.0.2.56018: Flags [S.], cksum 0x870d (correct), seq 3328401895, ack 3841106199, win 65535, options [mss 1382,nop,wscale 8,nop,nop,sackOK], length 0
0x0000: 32e1 1aa3 4686 0242 fe82 3318 0800 4500
0x0010: 0034 0bc5 4000 6e06 a236 96ab 1c0a ac11
0x0020: 0002 0050 dad2 c663 61e7 e4f2 a117 8012
0x0030: ffff 870d 0000 0204 0566 0103 0308 0101
0x0040: 0402
20:57:13.492844 IP (tos 0x0, ttl 110, id 3014, offset 0, flags [DF], proto TCP (6), length 52)
150.171.28.10.80 > 172.17.0.2.56018: Flags [S.], cksum 0x870d (correct), seq 3328401895, ack 3841106199, win 65535, options [mss 1382,nop,wscale 8,nop,nop,sackOK], length 0
0x0000: 32e1 1aa3 4686 0242 fe82 3318 0800 4500
0x0010: 0034 0bc6 4000 6e06 a235 96ab 1c0a ac11
0x0020: 0002 0050 dad2 c663 61e7 e4f2 a117 8012
0x0030: ffff 870d 0000 0204 0566 0103 0308 0101
0x0040: 0402
20:57:14.115474 IP (tos 0x0, ttl 63, id 60085, offset 0, flags [DF], proto TCP (6), length 60)
172.17.0.2.56018 > 150.171.28.10.80: Flags [S], cksum 0xabe3 (correct), seq 3841106198, win 64240, options [mss 63900,sackOK,TS val 2498123879 ecr 0,nop,wscale 7], length 0
0x0000: 0242 fe82 3318 32e1 1aa3 4686 0800 4500
0x0010: 003c eab5 4000 3f06 f23d ac11 0002 96ab
0x0020: 1c0a dad2 0050 e4f2 a116 0000 0000 a002
0x0030: faf0 abe3 0000 0204 f99c 0402 080a 94e6
0x0040: 5867 0000 0000 0103 0307
20:57:14.267406 IP (tos 0x0, ttl 110, id 3015, offset 0, flags [DF], proto TCP (6), length 48)
150.171.28.10.80 > 172.17.0.2.56018: Flags [S.], cksum 0x9b1c (correct), seq 3328401895, ack 3841106199, win 65535, options [mss 1382,nop,nop,sackOK], length 0
0x0000: 32e1 1aa3 4686 0242 fe82 3318 0800 4500
0x0010: 0030 0bc7 4000 6e06 a238 96ab 1c0a ac11
0x0020: 0002 0050 dad2 c663 61e7 e4f2 a117 7012
0x0030: ffff 9b1c 0000 0204 0566 0101 0402
20:57:15.140409 IP (tos 0x0, ttl 63, id 60086, offset 0, flags [DF], proto TCP (6), length 60)
172.17.0.2.56018 > 150.171.28.10.80: Flags [S], cksum 0xa7e3 (correct), seq 3841106198, win 64240, options [mss 63900,sackOK,TS val 2498124903 ecr 0,nop,wscale 7], length 0
0x0000: 0242 fe82 3318 32e1 1aa3 4686 0800 4500
0x0010: 003c eab6 4000 3f06 f23c ac11 0002 96ab
0x0020: 1c0a dad2 0050 e4f2 a116 0000 0000 a002
0x0030: faf0 a7e3 0000 0204 f99c 0402 080a 94e6
0x0040: 5c67 0000 0000 0103 0307
20:57:15.792324 IP (tos 0x0, ttl 63, id 0, offset 0, flags [DF], proto TCP (6), length 40)
100.64.0.5.56018 > 150.171.28.10.80: Flags [R], cksum 0xbdc3 (correct), seq 0, win 0, length 0
0x0000: 0242 fe82 3318 32e1 1aa3 4686 0800 4500
0x0010: 0028 0000 4000 3f06 24d6 6440 0005 96ab
0x0020: 1c0a dad2 0050 0000 0000 0000 0000 5004
0x0030: 0000 bdc3 0000
20:57:15.820568 IP (tos 0x0, ttl 111, id 3016, offset 0, flags [DF], proto TCP (6), length 40)
150.171.28.10.80 > 172.17.0.2.56018: Flags [R], cksum 0xc79e (correct), seq 3328401896, win 0, length 0
0x0000: 32e1 1aa3 4686 0242 fe82 3318 0800 4500
0x0010: 0028 0bc8 4000 6f06 a13f 96ab 1c0a ac11
0x0020: 0002 0050 dad2 c663 61e8 e4f2 a117 5004
Похоже, что он отправляет какой -нибудь поврежденный пакет SYN, который никогда не доставляется.
Подробнее здесь:
https://stackoverflow.com/questions/797 ... -22-04-lts
1756591531
Anonymous
Я работаю с устройством Linux Tun (MTU 65535), где я пишу пакеты Jumbo TCP (10 кб+), и моя цель-для ядра (через программное обеспечение GSO) или NIC (через TSO, если таковые имеются), чтобы сегментировать их на кусочки размера MTU перед отправкой через ETH0 (MTU 1500). Я понимаю, что это включает в себя создание интерфейса TUN с iff_vnet_hdr , включение GSO через TunsetOffload и позволяя сегментации сетей в сети ядра, а не выполнять это в пространстве пользователя. Тем не менее, я изо всех сил пытаюсь установить соединение, когда GSO включена, даже если та же самая установка работает нормально, когда GSO отключен. Кто -нибудь успешно добился сегментации TCP таким образом и мог бы поделиться минимальным рабочим примером или руководством (некоторые документы также будут высоко оценены)?[code]package tun import ( "fmt" "os" "syscall" "unsafe" ) const ( IFF_TUN = 0x0001 IFF_NO_PI = 0x1000 IFF_VNET_HDR = 0x4000 TUNSETIFF = 0x400454ca TUNSETVNETHDRSZ = 0x400454d8 TUNSETOFFLOAD = 0x400454d0 TUN_OFFLOAD_CSUM = 0x01 TUN_OFFLOAD_GSO = 0x02 ) type ifreq struct { Name [16]byte Flags uint16 _ [22]byte } func CreateTUN(name string) (*os.File, error) { f, err := os.OpenFile("/dev/net/tun", os.O_RDWR, 0) if err != nil { return nil, fmt.Errorf("failed to open /dev/net/tun: %v", err) } var ifr ifreq copy(ifr.Name[:], name) ifr.Flags = IFF_TUN | IFF_NO_PI | IFF_VNET_HDR _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), uintptr(TUNSETIFF), uintptr(unsafe.Pointer(&ifr))) if errno != 0 { return nil, fmt.Errorf("TUNSETIFF failed: %v", errno) } if err := EnableGSO(f.Fd()); err != nil { return nil, err } return f, nil } func EnableGSO(tunFileFd uintptr) error { hdrSize := uint32(12) // virtio-net header size if _, _, errno := syscall.Syscall( syscall.SYS_IOCTL, tunFileFd, uintptr(TUNSETVNETHDRSZ), uintptr(unsafe.Pointer(&hdrSize)), ); errno != 0 { return fmt.Errorf("TUNSETVNETHDRSZ failed: %v", errno) } if _, _, errno := syscall.Syscall( syscall.SYS_IOCTL, tunFileFd, uintptr(TUNSETOFFLOAD), uintptr(TUN_OFFLOAD_GSO|TUN_OFFLOAD_CSUM), ); errno != 0 { return fmt.Errorf("TUNSETOFFLOAD failed: %v", errno) } fmt.Printf("Enabled GSO+CSUM with virtio-net headers on TUN (fd=%d)\n", tunFileFd) return nil } < /code> И это часть, которая добавляет vnet_header в пакет, прежде чем он будет записан в TUN: < /p> package main import ( "encoding/binary" "errors" ) const ( VIRTIO_NET_HDR_F_NEEDS_CSUM = 1 VIRTIO_NET_HDR_GSO_NONE = 0 VIRTIO_NET_HDR_GSO_TCPV4 = 1 VIRTIO_NET_HDR_GSO_TCPV6 = 4 TCP_CHECKSUM_OFFSET = 16 ) type VirtioNetHdr struct { Flags uint8 GSOType uint8 HdrLen uint16 GSOSize uint16 CSumStart uint16 CSumOffset uint16 } func PrependVnetHeader(pkt []byte, mtu int) ([]byte, error) { if len(pkt) < 1 { return nil, errors.New("empty packet") } ipVersion := (pkt[0] & 0xF0) >> 4 var hdr VirtioNetHdr switch ipVersion { case 4: // IPv4 if len(pkt) < 20 { return nil, errors.New("invalid IPv4 packet") } if pkt[9] != 6 { // Only TCP break } ipHeaderLen := int(pkt[0]&0x0F) * 4 tcpLen := len(pkt) - ipHeaderLen hdr.Flags = VIRTIO_NET_HDR_F_NEEDS_CSUM hdr.HdrLen = uint16(ipHeaderLen + 20) hdr.CSumStart = uint16(ipHeaderLen) hdr.CSumOffset = TCP_CHECKSUM_OFFSET if tcpLen > mtu { hdr.GSOType = VIRTIO_NET_HDR_GSO_TCPV4 hdr.GSOSize = uint16(mtu) } else { hdr.GSOType = VIRTIO_NET_HDR_GSO_NONE } case 6: // IPv6 if len(pkt) < 40 { return nil, errors.New("invalid IPv6 packet") } if pkt[6] != 6 { // Only TCP break } ipHeaderLen := 40 tcpLen := len(pkt) - ipHeaderLen hdr.Flags = VIRTIO_NET_HDR_F_NEEDS_CSUM hdr.HdrLen = uint16(ipHeaderLen + 20) hdr.CSumStart = uint16(ipHeaderLen) hdr.CSumOffset = TCP_CHECKSUM_OFFSET if tcpLen > mtu { hdr.GSOType = VIRTIO_NET_HDR_GSO_TCPV6 hdr.GSOSize = uint16(mtu) } else { hdr.GSOType = VIRTIO_NET_HDR_GSO_NONE } } // Serialize header + packet buf := make([]byte, 12+len(pkt)) buf[0] = hdr.Flags buf[1] = hdr.GSOType binary.LittleEndian.PutUint16(buf[2:4], hdr.HdrLen) binary.LittleEndian.PutUint16(buf[4:6], hdr.GSOSize) binary.LittleEndian.PutUint16(buf[6:8], hdr.CSumStart) binary.LittleEndian.PutUint16(buf[8:10], hdr.CSumOffset) // buf[10:12] reserved = 0 copy(buf[12:], pkt) return buf, nil } < /code> Вот мои PCAP на ETH0, когда я пытаюсь проверить его: < /p> root@2458c936acdb:/# tcpdump -i eth0 port 80 -nn -vv -xx tcpdump: listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes 20:57:13.100414 IP (tos 0x0, ttl 63, id 60084, offset 0, flags [DF], proto TCP (6), length 60) 172.17.0.2.56018 > 150.171.28.10.80: Flags [S], cksum 0xafdf (correct), seq 3841106198, win 64240, options [mss 63900,sackOK,TS val 2498122859 ecr 0,nop,wscale 7], length 0 0x0000: 0242 fe82 3318 32e1 1aa3 4686 0800 4500 0x0010: 003c eab4 4000 3f06 f23e ac11 0002 96ab 0x0020: 1c0a dad2 0050 e4f2 a116 0000 0000 a002 0x0030: faf0 afdf 0000 0204 f99c 0402 080a 94e6 0x0040: 546b 0000 0000 0103 0307 20:57:13.103550 IP (tos 0x0, ttl 110, id 3013, offset 0, flags [DF], proto TCP (6), length 52) 150.171.28.10.80 > 172.17.0.2.56018: Flags [S.], cksum 0x870d (correct), seq 3328401895, ack 3841106199, win 65535, options [mss 1382,nop,wscale 8,nop,nop,sackOK], length 0 0x0000: 32e1 1aa3 4686 0242 fe82 3318 0800 4500 0x0010: 0034 0bc5 4000 6e06 a236 96ab 1c0a ac11 0x0020: 0002 0050 dad2 c663 61e7 e4f2 a117 8012 0x0030: ffff 870d 0000 0204 0566 0103 0308 0101 0x0040: 0402 20:57:13.492844 IP (tos 0x0, ttl 110, id 3014, offset 0, flags [DF], proto TCP (6), length 52) 150.171.28.10.80 > 172.17.0.2.56018: Flags [S.], cksum 0x870d (correct), seq 3328401895, ack 3841106199, win 65535, options [mss 1382,nop,wscale 8,nop,nop,sackOK], length 0 0x0000: 32e1 1aa3 4686 0242 fe82 3318 0800 4500 0x0010: 0034 0bc6 4000 6e06 a235 96ab 1c0a ac11 0x0020: 0002 0050 dad2 c663 61e7 e4f2 a117 8012 0x0030: ffff 870d 0000 0204 0566 0103 0308 0101 0x0040: 0402 20:57:14.115474 IP (tos 0x0, ttl 63, id 60085, offset 0, flags [DF], proto TCP (6), length 60) 172.17.0.2.56018 > 150.171.28.10.80: Flags [S], cksum 0xabe3 (correct), seq 3841106198, win 64240, options [mss 63900,sackOK,TS val 2498123879 ecr 0,nop,wscale 7], length 0 0x0000: 0242 fe82 3318 32e1 1aa3 4686 0800 4500 0x0010: 003c eab5 4000 3f06 f23d ac11 0002 96ab 0x0020: 1c0a dad2 0050 e4f2 a116 0000 0000 a002 0x0030: faf0 abe3 0000 0204 f99c 0402 080a 94e6 0x0040: 5867 0000 0000 0103 0307 20:57:14.267406 IP (tos 0x0, ttl 110, id 3015, offset 0, flags [DF], proto TCP (6), length 48) 150.171.28.10.80 > 172.17.0.2.56018: Flags [S.], cksum 0x9b1c (correct), seq 3328401895, ack 3841106199, win 65535, options [mss 1382,nop,nop,sackOK], length 0 0x0000: 32e1 1aa3 4686 0242 fe82 3318 0800 4500 0x0010: 0030 0bc7 4000 6e06 a238 96ab 1c0a ac11 0x0020: 0002 0050 dad2 c663 61e7 e4f2 a117 7012 0x0030: ffff 9b1c 0000 0204 0566 0101 0402 20:57:15.140409 IP (tos 0x0, ttl 63, id 60086, offset 0, flags [DF], proto TCP (6), length 60) 172.17.0.2.56018 > 150.171.28.10.80: Flags [S], cksum 0xa7e3 (correct), seq 3841106198, win 64240, options [mss 63900,sackOK,TS val 2498124903 ecr 0,nop,wscale 7], length 0 0x0000: 0242 fe82 3318 32e1 1aa3 4686 0800 4500 0x0010: 003c eab6 4000 3f06 f23c ac11 0002 96ab 0x0020: 1c0a dad2 0050 e4f2 a116 0000 0000 a002 0x0030: faf0 a7e3 0000 0204 f99c 0402 080a 94e6 0x0040: 5c67 0000 0000 0103 0307 20:57:15.792324 IP (tos 0x0, ttl 63, id 0, offset 0, flags [DF], proto TCP (6), length 40) 100.64.0.5.56018 > 150.171.28.10.80: Flags [R], cksum 0xbdc3 (correct), seq 0, win 0, length 0 0x0000: 0242 fe82 3318 32e1 1aa3 4686 0800 4500 0x0010: 0028 0000 4000 3f06 24d6 6440 0005 96ab 0x0020: 1c0a dad2 0050 0000 0000 0000 0000 5004 0x0030: 0000 bdc3 0000 20:57:15.820568 IP (tos 0x0, ttl 111, id 3016, offset 0, flags [DF], proto TCP (6), length 40) 150.171.28.10.80 > 172.17.0.2.56018: Flags [R], cksum 0xc79e (correct), seq 3328401896, win 0, length 0 0x0000: 32e1 1aa3 4686 0242 fe82 3318 0800 4500 0x0010: 0028 0bc8 4000 6f06 a13f 96ab 1c0a ac11 0x0020: 0002 0050 dad2 c663 61e8 e4f2 a117 5004 [/code] Похоже, что он отправляет какой -нибудь поврежденный пакет SYN, который никогда не доставляется. Подробнее здесь: [url]https://stackoverflow.com/questions/79751453/enable-gso-on-ubuntu-22-04-lts[/url]