summaryrefslogtreecommitdiffhomepage
path: root/pkg/tcpip
diff options
context:
space:
mode:
authorgVisor bot <gvisor-bot@google.com>2021-03-09 04:45:46 +0000
committergVisor bot <gvisor-bot@google.com>2021-03-09 04:45:46 +0000
commit8aebd0c66a48e61e0741dccf191190122a42e5c7 (patch)
tree0753c40620c3a23ccc070fd6bead60b438556a16 /pkg/tcpip
parentcb52b569b89f3ca61695fbf07175cf6e4503e168 (diff)
parentabbdcebc543242862fad0984db2db0a842021917 (diff)
Merge release-20210301.0-29-gabbdcebc5 (automated)
Diffstat (limited to 'pkg/tcpip')
-rw-r--r--pkg/tcpip/errors.go13
-rw-r--r--pkg/tcpip/ports/ports.go76
-rw-r--r--pkg/tcpip/stack/stack.go12
-rw-r--r--pkg/tcpip/tcpip_state_autogen.go20
-rw-r--r--pkg/tcpip/transport/tcp/endpoint.go2
5 files changed, 101 insertions, 22 deletions
diff --git a/pkg/tcpip/errors.go b/pkg/tcpip/errors.go
index 3b7cc52f3..5d478ac32 100644
--- a/pkg/tcpip/errors.go
+++ b/pkg/tcpip/errors.go
@@ -300,6 +300,19 @@ func (*ErrInvalidOptionValue) IgnoreStats() bool {
}
func (*ErrInvalidOptionValue) String() string { return "invalid option value specified" }
+// ErrInvalidPortRange indicates an attempt to set an invalid port range.
+//
+// +stateify savable
+type ErrInvalidPortRange struct{}
+
+func (*ErrInvalidPortRange) isError() {}
+
+// IgnoreStats implements Error.
+func (*ErrInvalidPortRange) IgnoreStats() bool {
+ return true
+}
+func (*ErrInvalidPortRange) String() string { return "invalid port range" }
+
// ErrMalformedHeader indicates the operation encountered a malformed header.
//
// +stateify savable
diff --git a/pkg/tcpip/ports/ports.go b/pkg/tcpip/ports/ports.go
index 11dbdbbcf..101872b47 100644
--- a/pkg/tcpip/ports/ports.go
+++ b/pkg/tcpip/ports/ports.go
@@ -16,7 +16,6 @@
package ports
import (
- "math"
"math/rand"
"sync/atomic"
@@ -24,16 +23,7 @@ import (
"gvisor.dev/gvisor/pkg/tcpip"
)
-const (
- // FirstEphemeral is the first ephemeral port.
- FirstEphemeral = 16000
-
- // numEphemeralPorts it the mnumber of available ephemeral ports to
- // Netstack.
- numEphemeralPorts = math.MaxUint16 - FirstEphemeral + 1
-
- anyIPAddress tcpip.Address = ""
-)
+const anyIPAddress tcpip.Address = ""
type portDescriptor struct {
network tcpip.NetworkProtocolNumber
@@ -83,9 +73,16 @@ func (f Flags) Effective() Flags {
// PortManager manages allocating, reserving and releasing ports.
type PortManager struct {
+ // mu protects allocatedPorts.
+ // LOCK ORDERING: mu > ephemeralMu.
mu sync.RWMutex
allocatedPorts map[portDescriptor]bindAddresses
+ // ephemeralMu protects firstEphemeral and numEphemeral.
+ ephemeralMu sync.RWMutex
+ firstEphemeral uint16
+ numEphemeral uint16
+
// hint is used to pick ports ephemeral ports in a stable order for
// a given port offset.
//
@@ -322,7 +319,13 @@ func (b bindAddresses) isAvailable(addr tcpip.Address, flags Flags, bindToDevice
// NewPortManager creates new PortManager.
func NewPortManager() *PortManager {
- return &PortManager{allocatedPorts: make(map[portDescriptor]bindAddresses)}
+ return &PortManager{
+ allocatedPorts: make(map[portDescriptor]bindAddresses),
+ // Match Linux's default ephemeral range. See:
+ // https://github.com/torvalds/linux/blob/e54937963fa249595824439dc839c948188dea83/net/ipv4/af_inet.c#L1842
+ firstEphemeral: 32768,
+ numEphemeral: 28232,
+ }
}
// PickEphemeralPort randomly chooses a starting point and iterates over all
@@ -330,13 +333,18 @@ func NewPortManager() *PortManager {
// is suitable for its needs, and stopping when a port is found or an error
// occurs.
func (s *PortManager) PickEphemeralPort(testPort func(p uint16) (bool, tcpip.Error)) (port uint16, err tcpip.Error) {
- offset := uint32(rand.Int31n(numEphemeralPorts))
- return s.pickEphemeralPort(offset, numEphemeralPorts, testPort)
+ s.ephemeralMu.RLock()
+ firstEphemeral := s.firstEphemeral
+ numEphemeral := s.numEphemeral
+ s.ephemeralMu.RUnlock()
+
+ offset := uint16(rand.Int31n(int32(numEphemeral)))
+ return pickEphemeralPort(offset, firstEphemeral, numEphemeral, testPort)
}
// portHint atomically reads and returns the s.hint value.
-func (s *PortManager) portHint() uint32 {
- return atomic.LoadUint32(&s.hint)
+func (s *PortManager) portHint() uint16 {
+ return uint16(atomic.LoadUint32(&s.hint))
}
// incPortHint atomically increments s.hint by 1.
@@ -348,8 +356,13 @@ func (s *PortManager) incPortHint() {
// iterates over all ephemeral ports, allowing the caller to decide whether a
// given port is suitable for its needs and stopping when a port is found or an
// error occurs.
-func (s *PortManager) PickEphemeralPortStable(offset uint32, testPort func(p uint16) (bool, tcpip.Error)) (port uint16, err tcpip.Error) {
- p, err := s.pickEphemeralPort(s.portHint()+offset, numEphemeralPorts, testPort)
+func (s *PortManager) PickEphemeralPortStable(offset uint16, testPort func(p uint16) (bool, tcpip.Error)) (port uint16, err tcpip.Error) {
+ s.ephemeralMu.RLock()
+ firstEphemeral := s.firstEphemeral
+ numEphemeral := s.numEphemeral
+ s.ephemeralMu.RUnlock()
+
+ p, err := pickEphemeralPort(s.portHint()+offset, firstEphemeral, numEphemeral, testPort)
if err == nil {
s.incPortHint()
}
@@ -361,9 +374,9 @@ func (s *PortManager) PickEphemeralPortStable(offset uint32, testPort func(p uin
// and iterates over the number of ports specified by count and allows the
// caller to decide whether a given port is suitable for its needs, and stopping
// when a port is found or an error occurs.
-func (s *PortManager) pickEphemeralPort(offset, count uint32, testPort func(p uint16) (bool, tcpip.Error)) (port uint16, err tcpip.Error) {
- for i := uint32(0); i < count; i++ {
- port = uint16(FirstEphemeral + (offset+i)%count)
+func pickEphemeralPort(offset, first, count uint16, testPort func(p uint16) (bool, tcpip.Error)) (port uint16, err tcpip.Error) {
+ for i := uint16(0); i < count; i++ {
+ port = first + (offset+i)%count
ok, err := testPort(port)
if err != nil {
return 0, err
@@ -567,3 +580,24 @@ func (s *PortManager) releasePortLocked(networks []tcpip.NetworkProtocolNumber,
}
}
}
+
+// PortRange returns the UDP and TCP inclusive range of ephemeral ports used in
+// both IPv4 and IPv6.
+func (s *PortManager) PortRange() (uint16, uint16) {
+ s.ephemeralMu.RLock()
+ defer s.ephemeralMu.RUnlock()
+ return s.firstEphemeral, s.firstEphemeral + s.numEphemeral - 1
+}
+
+// SetPortRange sets the UDP and TCP IPv4 and IPv6 ephemeral port range
+// (inclusive).
+func (s *PortManager) SetPortRange(start uint16, end uint16) tcpip.Error {
+ if start > end {
+ return &tcpip.ErrInvalidPortRange{}
+ }
+ s.ephemeralMu.Lock()
+ defer s.ephemeralMu.Unlock()
+ s.firstEphemeral = start
+ s.numEphemeral = end - start + 1
+ return nil
+}
diff --git a/pkg/tcpip/stack/stack.go b/pkg/tcpip/stack/stack.go
index de94ddfda..53370c354 100644
--- a/pkg/tcpip/stack/stack.go
+++ b/pkg/tcpip/stack/stack.go
@@ -813,6 +813,18 @@ func (s *Stack) Forwarding(protocolNum tcpip.NetworkProtocolNumber) bool {
return forwardingProtocol.Forwarding()
}
+// PortRange returns the UDP and TCP inclusive range of ephemeral ports used in
+// both IPv4 and IPv6.
+func (s *Stack) PortRange() (uint16, uint16) {
+ return s.PortManager.PortRange()
+}
+
+// SetPortRange sets the UDP and TCP IPv4 and IPv6 ephemeral port range
+// (inclusive).
+func (s *Stack) SetPortRange(start uint16, end uint16) tcpip.Error {
+ return s.PortManager.SetPortRange(start, end)
+}
+
// SetRouteTable assigns the route table to be used by this stack. It
// specifies which NIC to use for given destination address ranges.
//
diff --git a/pkg/tcpip/tcpip_state_autogen.go b/pkg/tcpip/tcpip_state_autogen.go
index e628e662d..743e1b598 100644
--- a/pkg/tcpip/tcpip_state_autogen.go
+++ b/pkg/tcpip/tcpip_state_autogen.go
@@ -386,6 +386,25 @@ func (e *ErrInvalidOptionValue) afterLoad() {}
func (e *ErrInvalidOptionValue) StateLoad(stateSourceObject state.Source) {
}
+func (e *ErrInvalidPortRange) StateTypeName() string {
+ return "pkg/tcpip.ErrInvalidPortRange"
+}
+
+func (e *ErrInvalidPortRange) StateFields() []string {
+ return []string{}
+}
+
+func (e *ErrInvalidPortRange) beforeSave() {}
+
+func (e *ErrInvalidPortRange) StateSave(stateSinkObject state.Sink) {
+ e.beforeSave()
+}
+
+func (e *ErrInvalidPortRange) afterLoad() {}
+
+func (e *ErrInvalidPortRange) StateLoad(stateSourceObject state.Source) {
+}
+
func (e *ErrMalformedHeader) StateTypeName() string {
return "pkg/tcpip.ErrMalformedHeader"
}
@@ -1120,6 +1139,7 @@ func init() {
state.Register((*ErrDuplicateNICID)(nil))
state.Register((*ErrInvalidEndpointState)(nil))
state.Register((*ErrInvalidOptionValue)(nil))
+ state.Register((*ErrInvalidPortRange)(nil))
state.Register((*ErrMalformedHeader)(nil))
state.Register((*ErrMessageTooLong)(nil))
state.Register((*ErrNetworkUnreachable)(nil))
diff --git a/pkg/tcpip/transport/tcp/endpoint.go b/pkg/tcpip/transport/tcp/endpoint.go
index 687b9f459..4836f8adc 100644
--- a/pkg/tcpip/transport/tcp/endpoint.go
+++ b/pkg/tcpip/transport/tcp/endpoint.go
@@ -2220,7 +2220,7 @@ func (e *endpoint) connect(addr tcpip.FullAddress, handshake bool, run bool) tcp
portBuf := make([]byte, 2)
binary.LittleEndian.PutUint16(portBuf, e.ID.RemotePort)
h.Write(portBuf)
- portOffset := h.Sum32()
+ portOffset := uint16(h.Sum32())
var twReuse tcpip.TCPTimeWaitReuseOption
if err := e.stack.TransportProtocolOption(ProtocolNumber, &twReuse); err != nil {