mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add UDP SO_REUSEADDR support to the port manager.
Next steps include adding support to the transport demuxer and the UDP endpoint. PiperOrigin-RevId: 284652151
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
load("//tools/go_stateify:defs.bzl", "go_library")
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_test")
|
||||
load("//tools/go_stateify:defs.bzl", "go_library")
|
||||
|
||||
package(licenses = ["notice"])
|
||||
|
||||
|
||||
+118
-30
@@ -41,6 +41,30 @@ type portDescriptor struct {
|
||||
port uint16
|
||||
}
|
||||
|
||||
// Flags represents the type of port reservation.
|
||||
//
|
||||
// +stateify savable
|
||||
type Flags struct {
|
||||
// MostRecent represents UDP SO_REUSEADDR.
|
||||
MostRecent bool
|
||||
|
||||
// LoadBalanced indicates SO_REUSEPORT.
|
||||
//
|
||||
// LoadBalanced takes precidence over MostRecent.
|
||||
LoadBalanced bool
|
||||
}
|
||||
|
||||
func (f Flags) bits() reuseFlag {
|
||||
var rf reuseFlag
|
||||
if f.MostRecent {
|
||||
rf |= mostRecentFlag
|
||||
}
|
||||
if f.LoadBalanced {
|
||||
rf |= loadBalancedFlag
|
||||
}
|
||||
return rf
|
||||
}
|
||||
|
||||
// PortManager manages allocating, reserving and releasing ports.
|
||||
type PortManager struct {
|
||||
mu sync.RWMutex
|
||||
@@ -54,9 +78,59 @@ type PortManager struct {
|
||||
hint uint32
|
||||
}
|
||||
|
||||
type reuseFlag int
|
||||
|
||||
const (
|
||||
mostRecentFlag reuseFlag = 1 << iota
|
||||
loadBalancedFlag
|
||||
nextFlag
|
||||
|
||||
flagMask = nextFlag - 1
|
||||
)
|
||||
|
||||
type portNode struct {
|
||||
reuse bool
|
||||
refs int
|
||||
// refs stores the count for each possible flag combination.
|
||||
refs [nextFlag]int
|
||||
}
|
||||
|
||||
func (p portNode) totalRefs() int {
|
||||
var total int
|
||||
for _, r := range p.refs {
|
||||
total += r
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
// flagRefs returns the number of references with all specified flags.
|
||||
func (p portNode) flagRefs(flags reuseFlag) int {
|
||||
var total int
|
||||
for i, r := range p.refs {
|
||||
if reuseFlag(i)&flags == flags {
|
||||
total += r
|
||||
}
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
// allRefsHave returns if all references have all specified flags.
|
||||
func (p portNode) allRefsHave(flags reuseFlag) bool {
|
||||
for i, r := range p.refs {
|
||||
if reuseFlag(i)&flags == flags && r > 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// intersectionRefs returns the set of flags shared by all references.
|
||||
func (p portNode) intersectionRefs() reuseFlag {
|
||||
intersection := flagMask
|
||||
for i, r := range p.refs {
|
||||
if r > 0 {
|
||||
intersection &= reuseFlag(i)
|
||||
}
|
||||
}
|
||||
return intersection
|
||||
}
|
||||
|
||||
// deviceNode is never empty. When it has no elements, it is removed from the
|
||||
@@ -66,30 +140,44 @@ type deviceNode map[tcpip.NICID]portNode
|
||||
// isAvailable checks whether binding is possible by device. If not binding to a
|
||||
// device, check against all portNodes. If binding to a specific device, check
|
||||
// against the unspecified device and the provided device.
|
||||
func (d deviceNode) isAvailable(reuse bool, bindToDevice tcpip.NICID) bool {
|
||||
//
|
||||
// If either of the port reuse flags is enabled on any of the nodes, all nodes
|
||||
// sharing a port must share at least one reuse flag. This matches Linux's
|
||||
// behavior.
|
||||
func (d deviceNode) isAvailable(flags Flags, bindToDevice tcpip.NICID) bool {
|
||||
flagBits := flags.bits()
|
||||
if bindToDevice == 0 {
|
||||
// Trying to binding all devices.
|
||||
if !reuse {
|
||||
if flagBits == 0 {
|
||||
// Can't bind because the (addr,port) is already bound.
|
||||
return false
|
||||
}
|
||||
intersection := flagMask
|
||||
for _, p := range d {
|
||||
if !p.reuse {
|
||||
// Can't bind because the (addr,port) was previously bound without reuse.
|
||||
i := p.intersectionRefs()
|
||||
intersection &= i
|
||||
if intersection&flagBits == 0 {
|
||||
// Can't bind because the (addr,port) was
|
||||
// previously bound without reuse.
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
intersection := flagMask
|
||||
|
||||
if p, ok := d[0]; ok {
|
||||
if !reuse || !p.reuse {
|
||||
intersection = p.intersectionRefs()
|
||||
if intersection&flagBits == 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if p, ok := d[bindToDevice]; ok {
|
||||
if !reuse || !p.reuse {
|
||||
i := p.intersectionRefs()
|
||||
intersection &= i
|
||||
if intersection&flagBits == 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -103,12 +191,12 @@ type bindAddresses map[tcpip.Address]deviceNode
|
||||
// isAvailable checks whether an IP address is available to bind to. If the
|
||||
// address is the "any" address, check all other addresses. Otherwise, just
|
||||
// check against the "any" address and the provided address.
|
||||
func (b bindAddresses) isAvailable(addr tcpip.Address, reuse bool, bindToDevice tcpip.NICID) bool {
|
||||
func (b bindAddresses) isAvailable(addr tcpip.Address, flags Flags, bindToDevice tcpip.NICID) bool {
|
||||
if addr == anyIPAddress {
|
||||
// If binding to the "any" address then check that there are no conflicts
|
||||
// with all addresses.
|
||||
for _, d := range b {
|
||||
if !d.isAvailable(reuse, bindToDevice) {
|
||||
if !d.isAvailable(flags, bindToDevice) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -117,14 +205,14 @@ func (b bindAddresses) isAvailable(addr tcpip.Address, reuse bool, bindToDevice
|
||||
|
||||
// Check that there is no conflict with the "any" address.
|
||||
if d, ok := b[anyIPAddress]; ok {
|
||||
if !d.isAvailable(reuse, bindToDevice) {
|
||||
if !d.isAvailable(flags, bindToDevice) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Check that this is no conflict with the provided address.
|
||||
if d, ok := b[addr]; ok {
|
||||
if !d.isAvailable(reuse, bindToDevice) {
|
||||
if !d.isAvailable(flags, bindToDevice) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -190,17 +278,17 @@ func (s *PortManager) pickEphemeralPort(offset, count uint32, testPort func(p ui
|
||||
}
|
||||
|
||||
// IsPortAvailable tests if the given port is available on all given protocols.
|
||||
func (s *PortManager) IsPortAvailable(networks []tcpip.NetworkProtocolNumber, transport tcpip.TransportProtocolNumber, addr tcpip.Address, port uint16, reuse bool, bindToDevice tcpip.NICID) bool {
|
||||
func (s *PortManager) IsPortAvailable(networks []tcpip.NetworkProtocolNumber, transport tcpip.TransportProtocolNumber, addr tcpip.Address, port uint16, flags Flags, bindToDevice tcpip.NICID) bool {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return s.isPortAvailableLocked(networks, transport, addr, port, reuse, bindToDevice)
|
||||
return s.isPortAvailableLocked(networks, transport, addr, port, flags, bindToDevice)
|
||||
}
|
||||
|
||||
func (s *PortManager) isPortAvailableLocked(networks []tcpip.NetworkProtocolNumber, transport tcpip.TransportProtocolNumber, addr tcpip.Address, port uint16, reuse bool, bindToDevice tcpip.NICID) bool {
|
||||
func (s *PortManager) isPortAvailableLocked(networks []tcpip.NetworkProtocolNumber, transport tcpip.TransportProtocolNumber, addr tcpip.Address, port uint16, flags Flags, bindToDevice tcpip.NICID) bool {
|
||||
for _, network := range networks {
|
||||
desc := portDescriptor{network, transport, port}
|
||||
if addrs, ok := s.allocatedPorts[desc]; ok {
|
||||
if !addrs.isAvailable(addr, reuse, bindToDevice) {
|
||||
if !addrs.isAvailable(addr, flags, bindToDevice) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -212,14 +300,14 @@ func (s *PortManager) isPortAvailableLocked(networks []tcpip.NetworkProtocolNumb
|
||||
// reserved by another endpoint. If port is zero, ReservePort will search for
|
||||
// an unreserved ephemeral port and reserve it, returning its value in the
|
||||
// "port" return value.
|
||||
func (s *PortManager) ReservePort(networks []tcpip.NetworkProtocolNumber, transport tcpip.TransportProtocolNumber, addr tcpip.Address, port uint16, reuse bool, bindToDevice tcpip.NICID) (reservedPort uint16, err *tcpip.Error) {
|
||||
func (s *PortManager) ReservePort(networks []tcpip.NetworkProtocolNumber, transport tcpip.TransportProtocolNumber, addr tcpip.Address, port uint16, flags Flags, bindToDevice tcpip.NICID) (reservedPort uint16, err *tcpip.Error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
// If a port is specified, just try to reserve it for all network
|
||||
// protocols.
|
||||
if port != 0 {
|
||||
if !s.reserveSpecificPort(networks, transport, addr, port, reuse, bindToDevice) {
|
||||
if !s.reserveSpecificPort(networks, transport, addr, port, flags, bindToDevice) {
|
||||
return 0, tcpip.ErrPortInUse
|
||||
}
|
||||
return port, nil
|
||||
@@ -227,15 +315,16 @@ func (s *PortManager) ReservePort(networks []tcpip.NetworkProtocolNumber, transp
|
||||
|
||||
// A port wasn't specified, so try to find one.
|
||||
return s.PickEphemeralPort(func(p uint16) (bool, *tcpip.Error) {
|
||||
return s.reserveSpecificPort(networks, transport, addr, p, reuse, bindToDevice), nil
|
||||
return s.reserveSpecificPort(networks, transport, addr, p, flags, bindToDevice), nil
|
||||
})
|
||||
}
|
||||
|
||||
// reserveSpecificPort tries to reserve the given port on all given protocols.
|
||||
func (s *PortManager) reserveSpecificPort(networks []tcpip.NetworkProtocolNumber, transport tcpip.TransportProtocolNumber, addr tcpip.Address, port uint16, reuse bool, bindToDevice tcpip.NICID) bool {
|
||||
if !s.isPortAvailableLocked(networks, transport, addr, port, reuse, bindToDevice) {
|
||||
func (s *PortManager) reserveSpecificPort(networks []tcpip.NetworkProtocolNumber, transport tcpip.TransportProtocolNumber, addr tcpip.Address, port uint16, flags Flags, bindToDevice tcpip.NICID) bool {
|
||||
if !s.isPortAvailableLocked(networks, transport, addr, port, flags, bindToDevice) {
|
||||
return false
|
||||
}
|
||||
flagBits := flags.bits()
|
||||
|
||||
// Reserve port on all network protocols.
|
||||
for _, network := range networks {
|
||||
@@ -250,12 +339,9 @@ func (s *PortManager) reserveSpecificPort(networks []tcpip.NetworkProtocolNumber
|
||||
d = make(deviceNode)
|
||||
m[addr] = d
|
||||
}
|
||||
if n, ok := d[bindToDevice]; ok {
|
||||
n.refs++
|
||||
d[bindToDevice] = n
|
||||
} else {
|
||||
d[bindToDevice] = portNode{reuse: reuse, refs: 1}
|
||||
}
|
||||
n := d[bindToDevice]
|
||||
n.refs[flagBits]++
|
||||
d[bindToDevice] = n
|
||||
}
|
||||
|
||||
return true
|
||||
@@ -263,10 +349,12 @@ func (s *PortManager) reserveSpecificPort(networks []tcpip.NetworkProtocolNumber
|
||||
|
||||
// ReleasePort releases the reservation on a port/IP combination so that it can
|
||||
// be reserved by other endpoints.
|
||||
func (s *PortManager) ReleasePort(networks []tcpip.NetworkProtocolNumber, transport tcpip.TransportProtocolNumber, addr tcpip.Address, port uint16, bindToDevice tcpip.NICID) {
|
||||
func (s *PortManager) ReleasePort(networks []tcpip.NetworkProtocolNumber, transport tcpip.TransportProtocolNumber, addr tcpip.Address, port uint16, flags Flags, bindToDevice tcpip.NICID) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
flagBits := flags.bits()
|
||||
|
||||
for _, network := range networks {
|
||||
desc := portDescriptor{network, transport, port}
|
||||
if m, ok := s.allocatedPorts[desc]; ok {
|
||||
@@ -278,9 +366,9 @@ func (s *PortManager) ReleasePort(networks []tcpip.NetworkProtocolNumber, transp
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
n.refs--
|
||||
n.refs[flagBits]--
|
||||
d[bindToDevice] = n
|
||||
if n.refs == 0 {
|
||||
if n.refs == [nextFlag]int{} {
|
||||
delete(d, bindToDevice)
|
||||
}
|
||||
if len(d) == 0 {
|
||||
|
||||
+123
-59
@@ -33,7 +33,7 @@ type portReserveTestAction struct {
|
||||
port uint16
|
||||
ip tcpip.Address
|
||||
want *tcpip.Error
|
||||
reuse bool
|
||||
flags Flags
|
||||
release bool
|
||||
device tcpip.NICID
|
||||
}
|
||||
@@ -50,7 +50,7 @@ func TestPortReservation(t *testing.T) {
|
||||
{port: 80, ip: fakeIPAddress1, want: nil},
|
||||
/* N.B. Order of tests matters! */
|
||||
{port: 80, ip: anyIPAddress, want: tcpip.ErrPortInUse},
|
||||
{port: 80, ip: fakeIPAddress, want: tcpip.ErrPortInUse, reuse: true},
|
||||
{port: 80, ip: fakeIPAddress, want: tcpip.ErrPortInUse, flags: Flags{LoadBalanced: true}},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -61,7 +61,7 @@ func TestPortReservation(t *testing.T) {
|
||||
/* release fakeIPAddress, but anyIPAddress is still inuse */
|
||||
{port: 22, ip: fakeIPAddress, release: true},
|
||||
{port: 22, ip: fakeIPAddress, want: tcpip.ErrPortInUse},
|
||||
{port: 22, ip: fakeIPAddress, want: tcpip.ErrPortInUse, reuse: true},
|
||||
{port: 22, ip: fakeIPAddress, want: tcpip.ErrPortInUse, flags: Flags{LoadBalanced: true}},
|
||||
/* Release port 22 from any IP address, then try to reserve fake IP address on 22 */
|
||||
{port: 22, ip: anyIPAddress, want: nil, release: true},
|
||||
{port: 22, ip: fakeIPAddress, want: nil},
|
||||
@@ -71,36 +71,36 @@ func TestPortReservation(t *testing.T) {
|
||||
actions: []portReserveTestAction{
|
||||
{port: 00, ip: fakeIPAddress, want: nil},
|
||||
{port: 00, ip: fakeIPAddress, want: nil},
|
||||
{port: 00, ip: fakeIPAddress, reuse: true, want: nil},
|
||||
{port: 00, ip: fakeIPAddress, flags: Flags{LoadBalanced: true}, want: nil},
|
||||
},
|
||||
}, {
|
||||
tname: "bind to ip with reuseport",
|
||||
actions: []portReserveTestAction{
|
||||
{port: 25, ip: fakeIPAddress, reuse: true, want: nil},
|
||||
{port: 25, ip: fakeIPAddress, reuse: true, want: nil},
|
||||
{port: 25, ip: fakeIPAddress, flags: Flags{LoadBalanced: true}, want: nil},
|
||||
{port: 25, ip: fakeIPAddress, flags: Flags{LoadBalanced: true}, want: nil},
|
||||
|
||||
{port: 25, ip: fakeIPAddress, reuse: false, want: tcpip.ErrPortInUse},
|
||||
{port: 25, ip: anyIPAddress, reuse: false, want: tcpip.ErrPortInUse},
|
||||
{port: 25, ip: fakeIPAddress, flags: Flags{}, want: tcpip.ErrPortInUse},
|
||||
{port: 25, ip: anyIPAddress, flags: Flags{}, want: tcpip.ErrPortInUse},
|
||||
|
||||
{port: 25, ip: anyIPAddress, reuse: true, want: nil},
|
||||
{port: 25, ip: anyIPAddress, flags: Flags{LoadBalanced: true}, want: nil},
|
||||
},
|
||||
}, {
|
||||
tname: "bind to inaddr any with reuseport",
|
||||
actions: []portReserveTestAction{
|
||||
{port: 24, ip: anyIPAddress, reuse: true, want: nil},
|
||||
{port: 24, ip: anyIPAddress, reuse: true, want: nil},
|
||||
{port: 24, ip: anyIPAddress, flags: Flags{LoadBalanced: true}, want: nil},
|
||||
{port: 24, ip: anyIPAddress, flags: Flags{LoadBalanced: true}, want: nil},
|
||||
|
||||
{port: 24, ip: anyIPAddress, reuse: false, want: tcpip.ErrPortInUse},
|
||||
{port: 24, ip: fakeIPAddress, reuse: false, want: tcpip.ErrPortInUse},
|
||||
{port: 24, ip: anyIPAddress, flags: Flags{}, want: tcpip.ErrPortInUse},
|
||||
{port: 24, ip: fakeIPAddress, flags: Flags{}, want: tcpip.ErrPortInUse},
|
||||
|
||||
{port: 24, ip: fakeIPAddress, reuse: true, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, release: true, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, flags: Flags{LoadBalanced: true}, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, flags: Flags{LoadBalanced: true}, release: true, want: nil},
|
||||
|
||||
{port: 24, ip: anyIPAddress, release: true},
|
||||
{port: 24, ip: anyIPAddress, reuse: false, want: tcpip.ErrPortInUse},
|
||||
{port: 24, ip: anyIPAddress, flags: Flags{LoadBalanced: true}, release: true},
|
||||
{port: 24, ip: anyIPAddress, flags: Flags{}, want: tcpip.ErrPortInUse},
|
||||
|
||||
{port: 24, ip: anyIPAddress, release: true},
|
||||
{port: 24, ip: anyIPAddress, reuse: false, want: nil},
|
||||
{port: 24, ip: anyIPAddress, flags: Flags{LoadBalanced: true}, release: true},
|
||||
{port: 24, ip: anyIPAddress, flags: Flags{}, want: nil},
|
||||
},
|
||||
}, {
|
||||
tname: "bind twice with device fails",
|
||||
@@ -125,88 +125,152 @@ func TestPortReservation(t *testing.T) {
|
||||
actions: []portReserveTestAction{
|
||||
{port: 24, ip: fakeIPAddress, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 123, want: tcpip.ErrPortInUse},
|
||||
{port: 24, ip: fakeIPAddress, device: 123, reuse: true, want: tcpip.ErrPortInUse},
|
||||
{port: 24, ip: fakeIPAddress, device: 123, flags: Flags{LoadBalanced: true}, want: tcpip.ErrPortInUse},
|
||||
{port: 24, ip: fakeIPAddress, want: tcpip.ErrPortInUse},
|
||||
{port: 24, ip: fakeIPAddress, reuse: true, want: tcpip.ErrPortInUse},
|
||||
{port: 24, ip: fakeIPAddress, flags: Flags{LoadBalanced: true}, want: tcpip.ErrPortInUse},
|
||||
},
|
||||
}, {
|
||||
tname: "bind with device",
|
||||
actions: []portReserveTestAction{
|
||||
{port: 24, ip: fakeIPAddress, device: 123, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 123, want: tcpip.ErrPortInUse},
|
||||
{port: 24, ip: fakeIPAddress, device: 123, reuse: true, want: tcpip.ErrPortInUse},
|
||||
{port: 24, ip: fakeIPAddress, device: 123, flags: Flags{LoadBalanced: true}, want: tcpip.ErrPortInUse},
|
||||
{port: 24, ip: fakeIPAddress, device: 0, want: tcpip.ErrPortInUse},
|
||||
{port: 24, ip: fakeIPAddress, device: 0, reuse: true, want: tcpip.ErrPortInUse},
|
||||
{port: 24, ip: fakeIPAddress, device: 456, reuse: true, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 0, flags: Flags{LoadBalanced: true}, want: tcpip.ErrPortInUse},
|
||||
{port: 24, ip: fakeIPAddress, device: 456, flags: Flags{LoadBalanced: true}, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 789, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, want: tcpip.ErrPortInUse},
|
||||
{port: 24, ip: fakeIPAddress, reuse: true, want: tcpip.ErrPortInUse},
|
||||
{port: 24, ip: fakeIPAddress, flags: Flags{LoadBalanced: true}, want: tcpip.ErrPortInUse},
|
||||
},
|
||||
}, {
|
||||
tname: "bind with reuse",
|
||||
tname: "bind with reuseport",
|
||||
actions: []portReserveTestAction{
|
||||
{port: 24, ip: fakeIPAddress, reuse: true, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, flags: Flags{LoadBalanced: true}, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 123, want: tcpip.ErrPortInUse},
|
||||
{port: 24, ip: fakeIPAddress, device: 123, reuse: true, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 123, flags: Flags{LoadBalanced: true}, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 0, want: tcpip.ErrPortInUse},
|
||||
{port: 24, ip: fakeIPAddress, device: 0, reuse: true, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 0, flags: Flags{LoadBalanced: true}, want: nil},
|
||||
},
|
||||
}, {
|
||||
tname: "binding with reuse and device",
|
||||
tname: "binding with reuseport and device",
|
||||
actions: []portReserveTestAction{
|
||||
{port: 24, ip: fakeIPAddress, device: 123, reuse: true, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 123, flags: Flags{LoadBalanced: true}, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 123, want: tcpip.ErrPortInUse},
|
||||
{port: 24, ip: fakeIPAddress, device: 123, reuse: true, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 123, flags: Flags{LoadBalanced: true}, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 0, want: tcpip.ErrPortInUse},
|
||||
{port: 24, ip: fakeIPAddress, device: 456, reuse: true, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 0, reuse: true, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 789, reuse: true, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 456, flags: Flags{LoadBalanced: true}, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 0, flags: Flags{LoadBalanced: true}, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 789, flags: Flags{LoadBalanced: true}, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 999, want: tcpip.ErrPortInUse},
|
||||
},
|
||||
}, {
|
||||
tname: "mixing reuse and not reuse by binding to device",
|
||||
tname: "mixing reuseport and not reuseport by binding to device",
|
||||
actions: []portReserveTestAction{
|
||||
{port: 24, ip: fakeIPAddress, device: 123, reuse: true, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 123, flags: Flags{LoadBalanced: true}, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 456, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 789, reuse: true, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 789, flags: Flags{LoadBalanced: true}, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 999, want: nil},
|
||||
},
|
||||
}, {
|
||||
tname: "can't bind to 0 after mixing reuse and not reuse",
|
||||
tname: "can't bind to 0 after mixing reuseport and not reuseport",
|
||||
actions: []portReserveTestAction{
|
||||
{port: 24, ip: fakeIPAddress, device: 123, reuse: true, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 123, flags: Flags{LoadBalanced: true}, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 456, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 0, reuse: true, want: tcpip.ErrPortInUse},
|
||||
{port: 24, ip: fakeIPAddress, device: 0, flags: Flags{LoadBalanced: true}, want: tcpip.ErrPortInUse},
|
||||
},
|
||||
}, {
|
||||
tname: "bind and release",
|
||||
actions: []portReserveTestAction{
|
||||
{port: 24, ip: fakeIPAddress, device: 123, reuse: true, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 0, reuse: true, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 345, reuse: false, want: tcpip.ErrPortInUse},
|
||||
{port: 24, ip: fakeIPAddress, device: 789, reuse: true, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 123, flags: Flags{LoadBalanced: true}, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 0, flags: Flags{LoadBalanced: true}, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 345, flags: Flags{}, want: tcpip.ErrPortInUse},
|
||||
{port: 24, ip: fakeIPAddress, device: 789, flags: Flags{LoadBalanced: true}, want: nil},
|
||||
|
||||
// Release the bind to device 0 and try again.
|
||||
{port: 24, ip: fakeIPAddress, device: 0, reuse: true, want: nil, release: true},
|
||||
{port: 24, ip: fakeIPAddress, device: 345, reuse: false, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 0, flags: Flags{LoadBalanced: true}, want: nil, release: true},
|
||||
{port: 24, ip: fakeIPAddress, device: 345, flags: Flags{}, want: nil},
|
||||
},
|
||||
}, {
|
||||
tname: "bind twice with reuse once",
|
||||
tname: "bind twice with reuseport once",
|
||||
actions: []portReserveTestAction{
|
||||
{port: 24, ip: fakeIPAddress, device: 123, reuse: false, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 0, reuse: true, want: tcpip.ErrPortInUse},
|
||||
{port: 24, ip: fakeIPAddress, device: 123, flags: Flags{}, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 0, flags: Flags{LoadBalanced: true}, want: tcpip.ErrPortInUse},
|
||||
},
|
||||
}, {
|
||||
tname: "release an unreserved device",
|
||||
actions: []portReserveTestAction{
|
||||
{port: 24, ip: fakeIPAddress, device: 123, reuse: false, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 456, reuse: false, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 123, flags: Flags{}, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 456, flags: Flags{}, want: nil},
|
||||
// The below don't exist.
|
||||
{port: 24, ip: fakeIPAddress, device: 345, reuse: false, want: nil, release: true},
|
||||
{port: 9999, ip: fakeIPAddress, device: 123, reuse: false, want: nil, release: true},
|
||||
{port: 24, ip: fakeIPAddress, device: 345, flags: Flags{}, want: nil, release: true},
|
||||
{port: 9999, ip: fakeIPAddress, device: 123, flags: Flags{}, want: nil, release: true},
|
||||
// Release all.
|
||||
{port: 24, ip: fakeIPAddress, device: 123, reuse: false, want: nil, release: true},
|
||||
{port: 24, ip: fakeIPAddress, device: 456, reuse: false, want: nil, release: true},
|
||||
{port: 24, ip: fakeIPAddress, device: 123, flags: Flags{}, want: nil, release: true},
|
||||
{port: 24, ip: fakeIPAddress, device: 456, flags: Flags{}, want: nil, release: true},
|
||||
},
|
||||
}, {
|
||||
tname: "bind with reuseaddr",
|
||||
actions: []portReserveTestAction{
|
||||
{port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true}, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 123, want: tcpip.ErrPortInUse},
|
||||
{port: 24, ip: fakeIPAddress, device: 123, flags: Flags{MostRecent: true}, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 0, want: tcpip.ErrPortInUse},
|
||||
{port: 24, ip: fakeIPAddress, device: 0, flags: Flags{MostRecent: true}, want: nil},
|
||||
},
|
||||
}, {
|
||||
tname: "bind twice with reuseaddr once",
|
||||
actions: []portReserveTestAction{
|
||||
{port: 24, ip: fakeIPAddress, device: 123, flags: Flags{}, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, device: 0, flags: Flags{MostRecent: true}, want: tcpip.ErrPortInUse},
|
||||
},
|
||||
}, {
|
||||
tname: "bind with reuseaddr and reuseport",
|
||||
actions: []portReserveTestAction{
|
||||
{port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true, LoadBalanced: true}, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true, LoadBalanced: true}, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true, LoadBalanced: true}, want: nil},
|
||||
},
|
||||
}, {
|
||||
tname: "bind with reuseaddr and reuseport, and then reuseaddr",
|
||||
actions: []portReserveTestAction{
|
||||
{port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true, LoadBalanced: true}, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true}, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, flags: Flags{LoadBalanced: true}, want: tcpip.ErrPortInUse},
|
||||
},
|
||||
}, {
|
||||
tname: "bind with reuseaddr and reuseport, and then reuseport",
|
||||
actions: []portReserveTestAction{
|
||||
{port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true, LoadBalanced: true}, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, flags: Flags{LoadBalanced: true}, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true}, want: tcpip.ErrPortInUse},
|
||||
},
|
||||
}, {
|
||||
tname: "bind with reuseaddr and reuseport twice, and then reuseaddr",
|
||||
actions: []portReserveTestAction{
|
||||
{port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true, LoadBalanced: true}, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true, LoadBalanced: true}, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true}, want: nil},
|
||||
},
|
||||
}, {
|
||||
tname: "bind with reuseaddr and reuseport twice, and then reuseport",
|
||||
actions: []portReserveTestAction{
|
||||
{port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true, LoadBalanced: true}, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true, LoadBalanced: true}, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, flags: Flags{LoadBalanced: true}, want: nil},
|
||||
},
|
||||
}, {
|
||||
tname: "bind with reuseaddr, and then reuseaddr and reuseport",
|
||||
actions: []portReserveTestAction{
|
||||
{port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true}, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true, LoadBalanced: true}, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, flags: Flags{LoadBalanced: true}, want: tcpip.ErrPortInUse},
|
||||
},
|
||||
}, {
|
||||
tname: "bind with reuseport, and then reuseaddr and reuseport",
|
||||
actions: []portReserveTestAction{
|
||||
{port: 24, ip: fakeIPAddress, flags: Flags{LoadBalanced: true}, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true, LoadBalanced: true}, want: nil},
|
||||
{port: 24, ip: fakeIPAddress, flags: Flags{MostRecent: true}, want: tcpip.ErrPortInUse},
|
||||
},
|
||||
},
|
||||
} {
|
||||
@@ -216,12 +280,12 @@ func TestPortReservation(t *testing.T) {
|
||||
|
||||
for _, test := range test.actions {
|
||||
if test.release {
|
||||
pm.ReleasePort(net, fakeTransNumber, test.ip, test.port, test.device)
|
||||
pm.ReleasePort(net, fakeTransNumber, test.ip, test.port, test.flags, test.device)
|
||||
continue
|
||||
}
|
||||
gotPort, err := pm.ReservePort(net, fakeTransNumber, test.ip, test.port, test.reuse, test.device)
|
||||
gotPort, err := pm.ReservePort(net, fakeTransNumber, test.ip, test.port, test.flags, test.device)
|
||||
if err != test.want {
|
||||
t.Fatalf("ReservePort(.., .., %s, %d, %t, %d) = %v, want %v", test.ip, test.port, test.reuse, test.device, err, test.want)
|
||||
t.Fatalf("ReservePort(.., .., %s, %d, %+v, %d) = %v, want %v", test.ip, test.port, test.flags, test.device, err, test.want)
|
||||
}
|
||||
if test.port == 0 && (gotPort == 0 || gotPort < FirstEphemeral) {
|
||||
t.Fatalf("ReservePort(.., .., .., 0) = %d, want port number >= %d to be picked", gotPort, FirstEphemeral)
|
||||
|
||||
@@ -52,6 +52,7 @@ go_library(
|
||||
"//pkg/tcpip/hash/jenkins",
|
||||
"//pkg/tcpip/header",
|
||||
"//pkg/tcpip/iptables",
|
||||
"//pkg/tcpip/ports",
|
||||
"//pkg/tcpip/seqnum",
|
||||
"//pkg/tcpip/stack",
|
||||
"//pkg/tcpip/transport/raw",
|
||||
|
||||
@@ -30,6 +30,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/tcpip/hash/jenkins"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/iptables"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/ports"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/seqnum"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
"gvisor.dev/gvisor/pkg/tmutex"
|
||||
@@ -343,6 +344,7 @@ type endpoint struct {
|
||||
// Values used to reserve a port or register a transport endpoint
|
||||
// (which ever happens first).
|
||||
boundBindToDevice tcpip.NICID
|
||||
boundPortFlags ports.Flags
|
||||
|
||||
// effectiveNetProtos contains the network protocols actually in use. In
|
||||
// most cases it will only contain "netProto", but in cases like IPv6
|
||||
@@ -737,9 +739,10 @@ func (e *endpoint) Close() {
|
||||
e.isRegistered = false
|
||||
}
|
||||
|
||||
e.stack.ReleasePort(e.effectiveNetProtos, ProtocolNumber, e.ID.LocalAddress, e.ID.LocalPort, e.boundBindToDevice)
|
||||
e.stack.ReleasePort(e.effectiveNetProtos, ProtocolNumber, e.ID.LocalAddress, e.ID.LocalPort, e.boundPortFlags, e.boundBindToDevice)
|
||||
e.isPortReserved = false
|
||||
e.boundBindToDevice = 0
|
||||
e.boundPortFlags = ports.Flags{}
|
||||
}
|
||||
|
||||
// Mark endpoint as closed.
|
||||
@@ -800,10 +803,11 @@ func (e *endpoint) cleanupLocked() {
|
||||
}
|
||||
|
||||
if e.isPortReserved {
|
||||
e.stack.ReleasePort(e.effectiveNetProtos, ProtocolNumber, e.ID.LocalAddress, e.ID.LocalPort, e.boundBindToDevice)
|
||||
e.stack.ReleasePort(e.effectiveNetProtos, ProtocolNumber, e.ID.LocalAddress, e.ID.LocalPort, e.boundPortFlags, e.boundBindToDevice)
|
||||
e.isPortReserved = false
|
||||
}
|
||||
e.boundBindToDevice = 0
|
||||
e.boundPortFlags = ports.Flags{}
|
||||
|
||||
e.route.Release()
|
||||
e.stack.CompleteTransportEndpointCleanup(e)
|
||||
@@ -1775,7 +1779,7 @@ func (e *endpoint) connect(addr tcpip.FullAddress, handshake bool, run bool) *tc
|
||||
}
|
||||
// reusePort is false below because connect cannot reuse a port even if
|
||||
// reusePort was set.
|
||||
if !e.stack.IsPortAvailable(netProtos, ProtocolNumber, e.ID.LocalAddress, p, false /* reusePort */, e.bindToDevice) {
|
||||
if !e.stack.IsPortAvailable(netProtos, ProtocolNumber, e.ID.LocalAddress, p, ports.Flags{LoadBalanced: false}, e.bindToDevice) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
@@ -1802,7 +1806,7 @@ func (e *endpoint) connect(addr tcpip.FullAddress, handshake bool, run bool) *tc
|
||||
// before Connect: in such a case we don't want to hold on to
|
||||
// reservations anymore.
|
||||
if e.isPortReserved {
|
||||
e.stack.ReleasePort(e.effectiveNetProtos, ProtocolNumber, origID.LocalAddress, origID.LocalPort, e.boundBindToDevice)
|
||||
e.stack.ReleasePort(e.effectiveNetProtos, ProtocolNumber, origID.LocalAddress, origID.LocalPort, e.boundPortFlags, e.boundBindToDevice)
|
||||
e.isPortReserved = false
|
||||
}
|
||||
|
||||
@@ -2034,28 +2038,33 @@ func (e *endpoint) Bind(addr tcpip.FullAddress) (err *tcpip.Error) {
|
||||
}
|
||||
}
|
||||
|
||||
port, err := e.stack.ReservePort(netProtos, ProtocolNumber, addr.Addr, addr.Port, e.reusePort, e.bindToDevice)
|
||||
flags := ports.Flags{
|
||||
LoadBalanced: e.reusePort,
|
||||
}
|
||||
port, err := e.stack.ReservePort(netProtos, ProtocolNumber, addr.Addr, addr.Port, flags, e.bindToDevice)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.boundBindToDevice = e.bindToDevice
|
||||
e.boundPortFlags = flags
|
||||
e.isPortReserved = true
|
||||
e.effectiveNetProtos = netProtos
|
||||
e.ID.LocalPort = port
|
||||
|
||||
// Any failures beyond this point must remove the port registration.
|
||||
defer func(bindToDevice tcpip.NICID) {
|
||||
defer func(portFlags ports.Flags, bindToDevice tcpip.NICID) {
|
||||
if err != nil {
|
||||
e.stack.ReleasePort(netProtos, ProtocolNumber, addr.Addr, port, bindToDevice)
|
||||
e.stack.ReleasePort(netProtos, ProtocolNumber, addr.Addr, port, portFlags, bindToDevice)
|
||||
e.isPortReserved = false
|
||||
e.effectiveNetProtos = nil
|
||||
e.ID.LocalPort = 0
|
||||
e.ID.LocalAddress = ""
|
||||
e.boundNICID = 0
|
||||
e.boundBindToDevice = 0
|
||||
e.boundPortFlags = ports.Flags{}
|
||||
}
|
||||
}(e.boundBindToDevice)
|
||||
}(e.boundPortFlags, e.boundBindToDevice)
|
||||
|
||||
// If an address is specified, we must ensure that it's one of our
|
||||
// local addresses.
|
||||
|
||||
@@ -34,6 +34,7 @@ go_library(
|
||||
"//pkg/tcpip/buffer",
|
||||
"//pkg/tcpip/header",
|
||||
"//pkg/tcpip/iptables",
|
||||
"//pkg/tcpip/ports",
|
||||
"//pkg/tcpip/stack",
|
||||
"//pkg/tcpip/transport/raw",
|
||||
"//pkg/waiter",
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/tcpip/buffer"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/iptables"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/ports"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
@@ -107,6 +108,7 @@ type endpoint struct {
|
||||
// Values used to reserve a port or register a transport endpoint.
|
||||
// (which ever happens first).
|
||||
boundBindToDevice tcpip.NICID
|
||||
boundPortFlags ports.Flags
|
||||
|
||||
// sendTOS represents IPv4 TOS or IPv6 TrafficClass,
|
||||
// applied while sending packets. Defaults to 0 as on Linux.
|
||||
@@ -180,8 +182,9 @@ func (e *endpoint) Close() {
|
||||
switch e.state {
|
||||
case StateBound, StateConnected:
|
||||
e.stack.UnregisterTransportEndpoint(e.RegisterNICID, e.effectiveNetProtos, ProtocolNumber, e.ID, e, e.boundBindToDevice)
|
||||
e.stack.ReleasePort(e.effectiveNetProtos, ProtocolNumber, e.ID.LocalAddress, e.ID.LocalPort, e.boundBindToDevice)
|
||||
e.stack.ReleasePort(e.effectiveNetProtos, ProtocolNumber, e.ID.LocalAddress, e.ID.LocalPort, e.boundPortFlags, e.boundBindToDevice)
|
||||
e.boundBindToDevice = 0
|
||||
e.boundPortFlags = ports.Flags{}
|
||||
}
|
||||
|
||||
for _, mem := range e.multicastMemberships {
|
||||
@@ -895,7 +898,8 @@ func (e *endpoint) Disconnect() *tcpip.Error {
|
||||
} else {
|
||||
if e.ID.LocalPort != 0 {
|
||||
// Release the ephemeral port.
|
||||
e.stack.ReleasePort(e.effectiveNetProtos, ProtocolNumber, e.ID.LocalAddress, e.ID.LocalPort, e.boundBindToDevice)
|
||||
e.stack.ReleasePort(e.effectiveNetProtos, ProtocolNumber, e.ID.LocalAddress, e.ID.LocalPort, e.boundPortFlags, e.boundBindToDevice)
|
||||
e.boundPortFlags = ports.Flags{}
|
||||
}
|
||||
e.state = StateInitial
|
||||
}
|
||||
@@ -1042,16 +1046,23 @@ func (*endpoint) Accept() (tcpip.Endpoint, *waiter.Queue, *tcpip.Error) {
|
||||
|
||||
func (e *endpoint) registerWithStack(nicID tcpip.NICID, netProtos []tcpip.NetworkProtocolNumber, id stack.TransportEndpointID) (stack.TransportEndpointID, tcpip.NICID, *tcpip.Error) {
|
||||
if e.ID.LocalPort == 0 {
|
||||
port, err := e.stack.ReservePort(netProtos, ProtocolNumber, id.LocalAddress, id.LocalPort, e.reusePort, e.bindToDevice)
|
||||
flags := ports.Flags{
|
||||
LoadBalanced: e.reusePort,
|
||||
// FIXME(b/129164367): Support SO_REUSEADDR.
|
||||
MostRecent: false,
|
||||
}
|
||||
port, err := e.stack.ReservePort(netProtos, ProtocolNumber, id.LocalAddress, id.LocalPort, flags, e.bindToDevice)
|
||||
if err != nil {
|
||||
return id, e.bindToDevice, err
|
||||
}
|
||||
e.boundPortFlags = flags
|
||||
id.LocalPort = port
|
||||
}
|
||||
|
||||
err := e.stack.RegisterTransportEndpoint(nicID, netProtos, ProtocolNumber, id, e, e.reusePort, e.bindToDevice)
|
||||
if err != nil {
|
||||
e.stack.ReleasePort(netProtos, ProtocolNumber, id.LocalAddress, id.LocalPort, e.bindToDevice)
|
||||
e.stack.ReleasePort(netProtos, ProtocolNumber, id.LocalAddress, id.LocalPort, e.boundPortFlags, e.bindToDevice)
|
||||
e.boundPortFlags = ports.Flags{}
|
||||
}
|
||||
return id, e.bindToDevice, err
|
||||
}
|
||||
|
||||
@@ -97,12 +97,12 @@ class BindToDeviceSequenceTest : public ::testing::TestWithParam<SocketKind> {
|
||||
sockets_to_close_.erase(socket_id);
|
||||
}
|
||||
|
||||
// Bind a socket with the reuse option and bind_to_device options. Checks
|
||||
// Bind a socket with the reuse options and bind_to_device options. Checks
|
||||
// that all steps succeed and that the bind command's error matches want.
|
||||
// Sets the socket_id to uniquely identify the socket bound if it is not
|
||||
// nullptr.
|
||||
void BindSocket(bool reuse, int device_id = 0, int want = 0,
|
||||
int *socket_id = nullptr) {
|
||||
void BindSocket(bool reuse_port, bool reuse_addr, int device_id = 0,
|
||||
int want = 0, int *socket_id = nullptr) {
|
||||
next_socket_id_++;
|
||||
sockets_to_close_[next_socket_id_] = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
|
||||
auto socket_fd = sockets_to_close_[next_socket_id_]->get();
|
||||
@@ -110,13 +110,20 @@ class BindToDeviceSequenceTest : public ::testing::TestWithParam<SocketKind> {
|
||||
*socket_id = next_socket_id_;
|
||||
}
|
||||
|
||||
// If reuse is indicated, do that.
|
||||
if (reuse) {
|
||||
// If reuse_port is indicated, do that.
|
||||
if (reuse_port) {
|
||||
EXPECT_THAT(setsockopt(socket_fd, SOL_SOCKET, SO_REUSEPORT, &kSockOptOn,
|
||||
sizeof(kSockOptOn)),
|
||||
SyscallSucceedsWithValue(0));
|
||||
}
|
||||
|
||||
// If reuse_addr is indicated, do that.
|
||||
if (reuse_addr) {
|
||||
EXPECT_THAT(setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &kSockOptOn,
|
||||
sizeof(kSockOptOn)),
|
||||
SyscallSucceedsWithValue(0));
|
||||
}
|
||||
|
||||
// If the device is non-zero, bind to that device.
|
||||
if (device_id != 0) {
|
||||
string device_name;
|
||||
@@ -182,129 +189,289 @@ class BindToDeviceSequenceTest : public ::testing::TestWithParam<SocketKind> {
|
||||
};
|
||||
|
||||
TEST_P(BindToDeviceSequenceTest, BindTwiceWithDeviceFails) {
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ false, /* bind_to_device */ 3));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ false, /* bind_to_device */ 3, EADDRINUSE));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(
|
||||
/* reuse_port */ false, /* reuse_addr */ false, /* bind_to_device */ 3));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 3, EADDRINUSE));
|
||||
}
|
||||
|
||||
TEST_P(BindToDeviceSequenceTest, BindToDevice) {
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ false, /* bind_to_device */ 1));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ false, /* bind_to_device */ 2));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(
|
||||
/* reuse_port */ false, /* reuse_addr */ false, /* bind_to_device */ 1));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(
|
||||
/* reuse_port */ false, /* reuse_addr */ false, /* bind_to_device */ 2));
|
||||
}
|
||||
|
||||
TEST_P(BindToDeviceSequenceTest, BindToDeviceAndThenWithoutDevice) {
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ false, /* bind_to_device */ 123));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ false, /* bind_to_device */ 0, EADDRINUSE));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 123));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 0, EADDRINUSE));
|
||||
}
|
||||
|
||||
TEST_P(BindToDeviceSequenceTest, BindWithoutDevice) {
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse */ false));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ false, /* bind_to_device */ 123, EADDRINUSE));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ true, /* bind_to_device */ 123, EADDRINUSE));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ false, /* bind_to_device */ 0, EADDRINUSE));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ true, /* bind_to_device */ 0, EADDRINUSE));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false,
|
||||
/* reuse_addr */ false));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 123, EADDRINUSE));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 123, EADDRINUSE));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 0, EADDRINUSE));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 0, EADDRINUSE));
|
||||
}
|
||||
|
||||
TEST_P(BindToDeviceSequenceTest, BindWithDevice) {
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ false, /* bind_to_device */ 123, 0));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ false, /* bind_to_device */ 123, EADDRINUSE));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ true, /* bind_to_device */ 123, EADDRINUSE));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ false, /* bind_to_device */ 0, EADDRINUSE));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ true, /* bind_to_device */ 0, EADDRINUSE));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ true, /* bind_to_device */ 456, 0));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ false, /* bind_to_device */ 789, 0));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ false, /* bind_to_device */ 0, EADDRINUSE));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ true, /* bind_to_device */ 0, EADDRINUSE));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 123, 0));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 123, EADDRINUSE));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 123, EADDRINUSE));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 0, EADDRINUSE));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 0, EADDRINUSE));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 456, 0));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 789, 0));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 0, EADDRINUSE));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 0, EADDRINUSE));
|
||||
}
|
||||
|
||||
TEST_P(BindToDeviceSequenceTest, BindWithReuse) {
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse */ true));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ false, /* bind_to_device */ 123, EADDRINUSE));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ true, /* bind_to_device */ 123));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ false, /* bind_to_device */ 0, EADDRINUSE));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse */ true, /* bind_to_device */ 0));
|
||||
BindSocket(/* reusePort */ true, /* reuse_addr */ false));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 123, EADDRINUSE));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(
|
||||
/* reuse_port */ true, /* reuse_addr */ false,
|
||||
/* bind_to_device */ 123));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 0, EADDRINUSE));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(
|
||||
/* reuse_port */ true, /* reuse_addr */ false, /* bind_to_device */ 0));
|
||||
}
|
||||
|
||||
TEST_P(BindToDeviceSequenceTest, BindingWithReuseAndDevice) {
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(
|
||||
/* reuse_port */ true, /* reuse_addr */ false, /* bind_to_device */ 123));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 123, EADDRINUSE));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(
|
||||
/* reuse_port */ true, /* reuse_addr */ false, /* bind_to_device */ 123));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 0, EADDRINUSE));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(
|
||||
/* reuse_port */ true, /* reuse_addr */ false, /* bind_to_device */ 456));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ true, /* bind_to_device */ 123));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ false, /* bind_to_device */ 123, EADDRINUSE));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ true, /* bind_to_device */ 123));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ false, /* bind_to_device */ 0, EADDRINUSE));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ true, /* bind_to_device */ 456));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse */ true));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ true, /* bind_to_device */ 789));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ false, /* bind_to_device */ 999, EADDRINUSE));
|
||||
BindSocket(/* reuse_port */ true, /* reuse_addr */ false));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(
|
||||
/* reuse_port */ true, /* reuse_addr */ false, /* bind_to_device */ 789));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 999, EADDRINUSE));
|
||||
}
|
||||
|
||||
TEST_P(BindToDeviceSequenceTest, MixingReuseAndNotReuseByBindingToDevice) {
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ true, /* bind_to_device */ 123, 0));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ false, /* bind_to_device */ 456, 0));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ true, /* bind_to_device */ 789, 0));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ false, /* bind_to_device */ 999, 0));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 123, 0));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 456, 0));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 789, 0));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 999, 0));
|
||||
}
|
||||
|
||||
TEST_P(BindToDeviceSequenceTest, CannotBindTo0AfterMixingReuseAndNotReuse) {
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ true, /* bind_to_device */ 123));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ false, /* bind_to_device */ 456));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ true, /* bind_to_device */ 0, EADDRINUSE));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(
|
||||
/* reuse_port */ true, /* reuse_addr */ false, /* bind_to_device */ 123));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 456));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 0, EADDRINUSE));
|
||||
}
|
||||
|
||||
TEST_P(BindToDeviceSequenceTest, BindAndRelease) {
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ true, /* bind_to_device */ 123));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(
|
||||
/* reuse_port */ true, /* reuse_addr */ false, /* bind_to_device */ 123));
|
||||
int to_release;
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ true, /* bind_to_device */ 0, 0, &to_release));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ false, /* bind_to_device */ 345, EADDRINUSE));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ true, /* bind_to_device */ 789));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 0, 0, &to_release));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 345, EADDRINUSE));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(
|
||||
/* reuse_port */ true, /* reuse_addr */ false, /* bind_to_device */ 789));
|
||||
// Release the bind to device 0 and try again.
|
||||
ASSERT_NO_FATAL_FAILURE(ReleaseSocket(to_release));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ false, /* bind_to_device */ 345));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 345));
|
||||
}
|
||||
|
||||
TEST_P(BindToDeviceSequenceTest, BindTwiceWithReuseOnce) {
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 123));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 0, EADDRINUSE));
|
||||
}
|
||||
|
||||
TEST_P(BindToDeviceSequenceTest, BindWithReuseAddr) {
|
||||
// FIXME(b/129164367): Support SO_REUSEADDR on UDP sockets.
|
||||
SKIP_IF(IsRunningOnGvisor());
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ false, /* bind_to_device */ 123));
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
BindSocket(/* reuse */ true, /* bind_to_device */ 0, EADDRINUSE));
|
||||
BindSocket(/* reusePort */ false, /* reuse_addr */ true));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 123, EADDRINUSE));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(
|
||||
/* reuse_port */ false, /* reuse_addr */ true, /* bind_to_device */ 123));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 0, EADDRINUSE));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(
|
||||
/* reuse_port */ false, /* reuse_addr */ true, /* bind_to_device */ 0));
|
||||
}
|
||||
|
||||
TEST_P(BindToDeviceSequenceTest,
|
||||
CannotBindTo0AfterMixingReuseAddrAndNotReuseAddr) {
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(
|
||||
/* reuse_port */ true, /* reuse_addr */ false, /* bind_to_device */ 123));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 456));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false,
|
||||
/* reuse_addr */ true,
|
||||
/* bind_to_device */ 0, EADDRINUSE));
|
||||
}
|
||||
|
||||
TEST_P(BindToDeviceSequenceTest, BindReuseAddrReusePortThenReusePort) {
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true,
|
||||
/* reuse_addr */ true,
|
||||
/* bind_to_device */ 0));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 0));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false,
|
||||
/* reuse_addr */ true,
|
||||
/* bind_to_device */ 0, EADDRINUSE));
|
||||
}
|
||||
|
||||
TEST_P(BindToDeviceSequenceTest, BindReuseAddrReusePortThenReuseAddr) {
|
||||
// FIXME(b/129164367): Support SO_REUSEADDR on UDP sockets.
|
||||
SKIP_IF(IsRunningOnGvisor());
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true,
|
||||
/* reuse_addr */ true,
|
||||
/* bind_to_device */ 0));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false,
|
||||
/* reuse_addr */ true,
|
||||
/* bind_to_device */ 0));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 0, EADDRINUSE));
|
||||
}
|
||||
|
||||
TEST_P(BindToDeviceSequenceTest, BindDoubleReuseAddrReusePortThenReusePort) {
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(
|
||||
/* reuse_port */ true, /* reuse_addr */ true, /* bind_to_device */ 0));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true,
|
||||
/* reuse_addr */ true,
|
||||
/* bind_to_device */ 0));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 0));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false,
|
||||
/* reuse_addr */ true,
|
||||
/* bind_to_device */ 0, EADDRINUSE));
|
||||
}
|
||||
|
||||
TEST_P(BindToDeviceSequenceTest, BindDoubleReuseAddrReusePortThenReuseAddr) {
|
||||
// FIXME(b/129164367): Support SO_REUSEADDR on UDP sockets.
|
||||
SKIP_IF(IsRunningOnGvisor());
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(
|
||||
/* reuse_port */ true, /* reuse_addr */ true, /* bind_to_device */ 0));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true,
|
||||
/* reuse_addr */ true,
|
||||
/* bind_to_device */ 0));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false,
|
||||
/* reuse_addr */ true,
|
||||
/* bind_to_device */ 0));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 0, EADDRINUSE));
|
||||
}
|
||||
|
||||
TEST_P(BindToDeviceSequenceTest, BindReusePortThenReuseAddrReusePort) {
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(
|
||||
/* reuse_port */ true, /* reuse_addr */ false, /* bind_to_device */ 0));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true,
|
||||
/* reuse_addr */ true,
|
||||
/* bind_to_device */ 0));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false,
|
||||
/* reuse_addr */ true,
|
||||
/* bind_to_device */ 0, EADDRINUSE));
|
||||
}
|
||||
|
||||
TEST_P(BindToDeviceSequenceTest, BindReuseAddrThenReuseAddr) {
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(
|
||||
/* reuse_port */ false, /* reuse_addr */ true, /* bind_to_device */ 0));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 0, EADDRINUSE));
|
||||
}
|
||||
|
||||
// This behavior seems like a bug?
|
||||
TEST_P(BindToDeviceSequenceTest,
|
||||
BindReuseAddrThenReuseAddrReusePortThenReuseAddr) {
|
||||
// FIXME(b/129164367): Support SO_REUSEADDR on UDP sockets.
|
||||
SKIP_IF(IsRunningOnGvisor());
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(
|
||||
/* reuse_port */ false, /* reuse_addr */ true, /* bind_to_device */ 0));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true,
|
||||
/* reuse_addr */ true,
|
||||
/* bind_to_device */ 0));
|
||||
ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true,
|
||||
/* reuse_addr */ false,
|
||||
/* bind_to_device */ 0));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(BindToDeviceTest, BindToDeviceSequenceTest,
|
||||
|
||||
Reference in New Issue
Block a user