1// Copyright 2015 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5//go:build !illumos 6 7package net 8 9import ( 10 "internal/syscall/unix" 11 "runtime" 12 "syscall" 13 "time" 14) 15 16// Some macros of TCP Keep-Alive options on Solaris 11.4 may 17// differ from those on OpenSolaris-based derivatives. 18const ( 19 sysTCP_KEEPIDLE = 0x1D 20 sysTCP_KEEPINTVL = 0x1E 21 sysTCP_KEEPCNT = 0x1F 22) 23 24func setKeepAliveIdle(fd *netFD, d time.Duration) error { 25 if !unix.SupportTCPKeepAliveIdleIntvlCNT() { 26 return setKeepAliveIdleAndIntervalAndCount(fd, d, -1, -1) 27 } 28 29 if d == 0 { 30 d = defaultTCPKeepAliveIdle 31 } else if d < 0 { 32 return nil 33 } 34 // The kernel expects seconds so round to next highest second. 35 secs := int(roundDurationUp(d, time.Second)) 36 err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, sysTCP_KEEPIDLE, secs) 37 runtime.KeepAlive(fd) 38 return wrapSyscallError("setsockopt", err) 39} 40 41func setKeepAliveInterval(fd *netFD, d time.Duration) error { 42 if !unix.SupportTCPKeepAliveIdleIntvlCNT() { 43 return syscall.EPROTOTYPE 44 } 45 46 if d == 0 { 47 d = defaultTCPKeepAliveInterval 48 } else if d < 0 { 49 return nil 50 } 51 // The kernel expects seconds so round to next highest second. 52 secs := int(roundDurationUp(d, time.Second)) 53 err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, sysTCP_KEEPINTVL, secs) 54 runtime.KeepAlive(fd) 55 return wrapSyscallError("setsockopt", err) 56} 57 58func setKeepAliveCount(fd *netFD, n int) error { 59 if !unix.SupportTCPKeepAliveIdleIntvlCNT() { 60 return syscall.EPROTOTYPE 61 } 62 63 if n == 0 { 64 n = defaultTCPKeepAliveCount 65 } else if n < 0 { 66 return nil 67 } 68 err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, sysTCP_KEEPCNT, n) 69 runtime.KeepAlive(fd) 70 return wrapSyscallError("setsockopt", err) 71} 72 73// setKeepAliveIdleAndIntervalAndCount serves for Solaris prior to 11.4 by simulating 74// the TCP_KEEPIDLE, TCP_KEEPINTVL, and TCP_KEEPCNT with `TCP_KEEPALIVE_THRESHOLD` + `TCP_KEEPALIVE_ABORT_THRESHOLD`. 75func setKeepAliveIdleAndIntervalAndCount(fd *netFD, idle, interval time.Duration, count int) error { 76 if idle == 0 { 77 idle = defaultTCPKeepAliveIdle 78 } 79 80 // The kernel expects milliseconds so round to next highest 81 // millisecond. 82 if idle > 0 { 83 msecs := int(roundDurationUp(idle, time.Millisecond)) 84 err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, syscall.TCP_KEEPALIVE_THRESHOLD, msecs) 85 runtime.KeepAlive(fd) 86 if err != nil { 87 return wrapSyscallError("setsockopt", err) 88 } 89 } 90 91 if interval == 0 { 92 interval = defaultTCPKeepAliveInterval 93 } 94 if count == 0 { 95 count = defaultTCPKeepAliveCount 96 } 97 // TCP_KEEPINTVL and TCP_KEEPCNT are not available on Solaris 98 // prior to 11.4, so it's pointless to "leave it unchanged" 99 // with negative value for only one of them. On the other hand, 100 // setting both to negative values should pragmatically leave the 101 // TCP_KEEPALIVE_ABORT_THRESHOLD unchanged. 102 abortIdle := int(roundDurationUp(interval, time.Millisecond)) * count 103 if abortIdle < 0 { 104 return syscall.ENOPROTOOPT 105 } 106 if interval < 0 && count < 0 { 107 abortIdle = -1 108 } 109 110 if abortIdle > 0 { 111 // Note that the consequent probes will not be sent at equal intervals on Solaris, 112 // but will be sent using the exponential backoff algorithm. 113 err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, syscall.TCP_KEEPALIVE_ABORT_THRESHOLD, abortIdle) 114 runtime.KeepAlive(fd) 115 return wrapSyscallError("setsockopt", err) 116 } 117 118 return nil 119} 120