Skip to content

Instantly share code, notes, and snippets.

@marten-seemann
Last active February 20, 2024 05:57
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save marten-seemann/a549773b53f30960b966a9f4068b6e48 to your computer and use it in GitHub Desktop.
Save marten-seemann/a549773b53f30960b966a9f4068b6e48 to your computer and use it in GitHub Desktop.
package main
import (
"encoding/binary"
"log"
"net"
"syscall"
"golang.org/x/sys/unix"
)
func main() {
if err := run(); err != nil {
log.Fatal(err)
}
}
const size = 1337
const UDP_SEGMENT = 103
func run() error {
addr, err := net.ResolveUDPAddr("udp", "0.0.0.0:1234")
if err != nil {
return err
}
conn, err := net.ListenUDP("udp", addr)
if err != nil {
return err
}
sconn, err := conn.SyscallConn()
if err != nil {
return err
}
if err := sconn.Control(func(fd uintptr) {
unix.SetsockoptInt(int(fd), syscall.IPPROTO_UDP, UDP_SEGMENT, size)
}); err != nil {
return err
}
raddr, err := net.ResolveUDPAddr("udp", "8.8.8.8:53") // let's spam the Google DNS
if err != nil {
return err
}
// Write 50 kB with a single syscall.
// The kernel / NIC will split this into UDP datagrams weighing 1337 bytes each.
if _, _, err := conn.WriteMsgUDP(make([]byte, 50*1024), getCmsg(1337), raddr); err != nil {
return err
}
return nil
}
// Generate a cmsg to set the GSO packet size.
// This only works on amd64.
// See https://github.com/golang/go/issues/59653.
//
// cm = CMSG_FIRSTHDR(&msg);
// cm->cmsg_level = SOL_UDP;
// cm->cmsg_type = UDP_SEGMENT;
// cm->cmsg_len = CMSG_LEN(sizeof(uint16_t));
// *((uint16_t *) CMSG_DATA(cm)) = gso_size;
func getCmsg(size int) []byte {
b := make([]byte, 8+4+4+2)
binary.LittleEndian.PutUint64(b[:8], 18)
binary.LittleEndian.PutUint32(b[8:12], uint32(syscall.IPPROTO_UDP))
binary.LittleEndian.PutUint32(b[12:16], uint32(UDP_SEGMENT))
binary.LittleEndian.PutUint16(b[16:], uint16(size))
return b
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment