github.com/tumi8/quic-go@v0.37.4-tum/sys_conn_df_linux.go (about) 1 //go:build linux 2 3 package quic 4 5 import ( 6 "errors" 7 "log" 8 "os" 9 "strconv" 10 "syscall" 11 "unsafe" 12 13 "golang.org/x/sys/unix" 14 15 "github.com/tumi8/quic-go/noninternal/utils" 16 ) 17 18 func setDF(rawConn syscall.RawConn) (bool, error) { 19 // Enabling IP_MTU_DISCOVER will force the kernel to return "sendto: message too long" 20 // and the datagram will not be fragmented 21 var errDFIPv4, errDFIPv6 error 22 if err := rawConn.Control(func(fd uintptr) { 23 errDFIPv4 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_MTU_DISCOVER, unix.IP_PMTUDISC_DO) 24 errDFIPv6 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_MTU_DISCOVER, unix.IPV6_PMTUDISC_DO) 25 }); err != nil { 26 return false, err 27 } 28 switch { 29 case errDFIPv4 == nil && errDFIPv6 == nil: 30 utils.DefaultLogger.Debugf("Setting DF for IPv4 and IPv6.") 31 case errDFIPv4 == nil && errDFIPv6 != nil: 32 utils.DefaultLogger.Debugf("Setting DF for IPv4.") 33 case errDFIPv4 != nil && errDFIPv6 == nil: 34 utils.DefaultLogger.Debugf("Setting DF for IPv6.") 35 case errDFIPv4 != nil && errDFIPv6 != nil: 36 return false, errors.New("setting DF failed for both IPv4 and IPv6") 37 } 38 return true, nil 39 } 40 41 func maybeSetGSO(rawConn syscall.RawConn) bool { 42 enable, _ := strconv.ParseBool(os.Getenv("QUIC_GO_ENABLE_GSO")) 43 if !enable { 44 return false 45 } 46 47 var setErr error 48 if err := rawConn.Control(func(fd uintptr) { 49 setErr = unix.SetsockoptInt(int(fd), syscall.IPPROTO_UDP, unix.UDP_SEGMENT, 1) 50 }); err != nil { 51 setErr = err 52 } 53 if setErr != nil { 54 log.Println("failed to enable GSO") 55 return false 56 } 57 return true 58 } 59 60 func isSendMsgSizeErr(err error) bool { 61 // https://man7.org/linux/man-pages/man7/udp.7.html 62 return errors.Is(err, unix.EMSGSIZE) 63 } 64 65 func isRecvMsgSizeErr(err error) bool { return false } 66 67 func appendUDPSegmentSizeMsg(b []byte, size uint16) []byte { 68 startLen := len(b) 69 const dataLen = 2 // payload is a uint16 70 b = append(b, make([]byte, unix.CmsgSpace(dataLen))...) 71 h := (*unix.Cmsghdr)(unsafe.Pointer(&b[startLen])) 72 h.Level = syscall.IPPROTO_UDP 73 h.Type = unix.UDP_SEGMENT 74 h.SetLen(unix.CmsgLen(dataLen)) 75 76 // UnixRights uses the private `data` method, but I *think* this achieves the same goal. 77 offset := startLen + unix.CmsgSpace(0) 78 *(*uint16)(unsafe.Pointer(&b[offset])) = size 79 return b 80 }