github.com/sagernet/quic-go@v0.43.1-beta.1/sys_conn_df_darwin.go (about) 1 //go:build darwin 2 3 package quic 4 5 import ( 6 "errors" 7 "strconv" 8 "strings" 9 "syscall" 10 11 "github.com/sagernet/quic-go/internal/utils" 12 "golang.org/x/sys/unix" 13 ) 14 15 func setDF(rawConn syscall.RawConn) (bool, error) { 16 // Setting DF bit is only supported from macOS11 17 // https://github.com/chromium/chromium/blob/117.0.5881.2/net/socket/udp_socket_posix.cc#L555 18 if supportsDF, err := isAtLeastMacOS11(); !supportsDF || err != nil { 19 return false, err 20 } 21 22 // Enabling IP_DONTFRAG will force the kernel to return "sendto: message too long" 23 // and the datagram will not be fragmented 24 var errDFIPv4, errDFIPv6 error 25 if err := rawConn.Control(func(fd uintptr) { 26 errDFIPv4 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_DONTFRAG, 1) 27 errDFIPv6 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_DONTFRAG, 1) 28 }); err != nil { 29 return false, err 30 } 31 switch { 32 case errDFIPv4 == nil && errDFIPv6 == nil: 33 utils.DefaultLogger.Debugf("Setting DF for IPv4 and IPv6.") 34 case errDFIPv4 == nil && errDFIPv6 != nil: 35 utils.DefaultLogger.Debugf("Setting DF for IPv4.") 36 case errDFIPv4 != nil && errDFIPv6 == nil: 37 utils.DefaultLogger.Debugf("Setting DF for IPv6.") 38 // On macOS, the syscall for setting DF bit for IPv4 fails on dual-stack listeners. 39 // Treat the connection as not having DF enabled, even though the DF bit will be set 40 // when used for IPv6. 41 // See https://github.com/sagernet/quic-go/issues/3793 for details. 42 return false, nil 43 case errDFIPv4 != nil && errDFIPv6 != nil: 44 return false, errors.New("setting DF failed for both IPv4 and IPv6") 45 } 46 return true, nil 47 } 48 49 func isSendMsgSizeErr(err error) bool { 50 return errors.Is(err, unix.EMSGSIZE) 51 } 52 53 func isRecvMsgSizeErr(error) bool { return false } 54 55 func isAtLeastMacOS11() (bool, error) { 56 uname := &unix.Utsname{} 57 err := unix.Uname(uname) 58 if err != nil { 59 return false, err 60 } 61 62 release := string(uname.Release[:]) 63 if idx := strings.Index(release, "."); idx != -1 { 64 version, err := strconv.Atoi(release[:idx]) 65 if err != nil { 66 return false, err 67 } 68 // Darwin version 20 is macOS version 11 69 // https://en.wikipedia.org/wiki/Darwin_(operating_system)#Darwin_20_onwards 70 return version >= 20, nil 71 } 72 return false, nil 73 }