netstack: disconnect an unix socket only if the address family is AF_UNSPEC

Linux allows to call connect for ANY and the zero port.

PiperOrigin-RevId: 263892534
This commit is contained in:
Andrei Vagin
2019-08-16 19:32:14 -07:00
committed by gVisor bot
parent 661b2b9f69
commit 3e4102b2ea
10 changed files with 160 additions and 57 deletions
+28 -17
View File
@@ -291,18 +291,22 @@ func bytesToIPAddress(addr []byte) tcpip.Address {
return tcpip.Address(addr)
}
// GetAddress reads an sockaddr struct from the given address and converts it
// to the FullAddress format. It supports AF_UNIX, AF_INET and AF_INET6
// addresses.
func GetAddress(sfamily int, addr []byte, strict bool) (tcpip.FullAddress, *syserr.Error) {
// AddressAndFamily reads an sockaddr struct from the given address and
// converts it to the FullAddress format. It supports AF_UNIX, AF_INET and
// AF_INET6 addresses.
//
// strict indicates whether addresses with the AF_UNSPEC family are accepted of not.
//
// AddressAndFamily returns an address, its family.
func AddressAndFamily(sfamily int, addr []byte, strict bool) (tcpip.FullAddress, uint16, *syserr.Error) {
// Make sure we have at least 2 bytes for the address family.
if len(addr) < 2 {
return tcpip.FullAddress{}, syserr.ErrInvalidArgument
return tcpip.FullAddress{}, 0, syserr.ErrInvalidArgument
}
family := usermem.ByteOrder.Uint16(addr)
if family != uint16(sfamily) && (!strict && family != linux.AF_UNSPEC) {
return tcpip.FullAddress{}, syserr.ErrAddressFamilyNotSupported
return tcpip.FullAddress{}, family, syserr.ErrAddressFamilyNotSupported
}
// Get the rest of the fields based on the address family.
@@ -310,7 +314,7 @@ func GetAddress(sfamily int, addr []byte, strict bool) (tcpip.FullAddress, *syse
case linux.AF_UNIX:
path := addr[2:]
if len(path) > linux.UnixPathMax {
return tcpip.FullAddress{}, syserr.ErrInvalidArgument
return tcpip.FullAddress{}, family, syserr.ErrInvalidArgument
}
// Drop the terminating NUL (if one exists) and everything after
// it for filesystem (non-abstract) addresses.
@@ -321,12 +325,12 @@ func GetAddress(sfamily int, addr []byte, strict bool) (tcpip.FullAddress, *syse
}
return tcpip.FullAddress{
Addr: tcpip.Address(path),
}, nil
}, family, nil
case linux.AF_INET:
var a linux.SockAddrInet
if len(addr) < sockAddrInetSize {
return tcpip.FullAddress{}, syserr.ErrInvalidArgument
return tcpip.FullAddress{}, family, syserr.ErrInvalidArgument
}
binary.Unmarshal(addr[:sockAddrInetSize], usermem.ByteOrder, &a)
@@ -334,12 +338,12 @@ func GetAddress(sfamily int, addr []byte, strict bool) (tcpip.FullAddress, *syse
Addr: bytesToIPAddress(a.Addr[:]),
Port: ntohs(a.Port),
}
return out, nil
return out, family, nil
case linux.AF_INET6:
var a linux.SockAddrInet6
if len(addr) < sockAddrInet6Size {
return tcpip.FullAddress{}, syserr.ErrInvalidArgument
return tcpip.FullAddress{}, family, syserr.ErrInvalidArgument
}
binary.Unmarshal(addr[:sockAddrInet6Size], usermem.ByteOrder, &a)
@@ -350,13 +354,13 @@ func GetAddress(sfamily int, addr []byte, strict bool) (tcpip.FullAddress, *syse
if isLinkLocal(out.Addr) {
out.NIC = tcpip.NICID(a.Scope_id)
}
return out, nil
return out, family, nil
case linux.AF_UNSPEC:
return tcpip.FullAddress{}, nil
return tcpip.FullAddress{}, family, nil
default:
return tcpip.FullAddress{}, syserr.ErrAddressFamilyNotSupported
return tcpip.FullAddress{}, 0, syserr.ErrAddressFamilyNotSupported
}
}
@@ -482,11 +486,18 @@ func (s *SocketOperations) Readiness(mask waiter.EventMask) waiter.EventMask {
// Connect implements the linux syscall connect(2) for sockets backed by
// tpcip.Endpoint.
func (s *SocketOperations) Connect(t *kernel.Task, sockaddr []byte, blocking bool) *syserr.Error {
addr, err := GetAddress(s.family, sockaddr, false /* strict */)
addr, family, err := AddressAndFamily(s.family, sockaddr, false /* strict */)
if err != nil {
return err
}
if family == linux.AF_UNSPEC {
err := s.Endpoint.Disconnect()
if err == tcpip.ErrNotSupported {
return syserr.ErrAddressFamilyNotSupported
}
return syserr.TranslateNetstackError(err)
}
// Always return right away in the non-blocking case.
if !blocking {
return syserr.TranslateNetstackError(s.Endpoint.Connect(addr))
@@ -515,7 +526,7 @@ func (s *SocketOperations) Connect(t *kernel.Task, sockaddr []byte, blocking boo
// Bind implements the linux syscall bind(2) for sockets backed by
// tcpip.Endpoint.
func (s *SocketOperations) Bind(t *kernel.Task, sockaddr []byte) *syserr.Error {
addr, err := GetAddress(s.family, sockaddr, true /* strict */)
addr, _, err := AddressAndFamily(s.family, sockaddr, true /* strict */)
if err != nil {
return err
}
@@ -2023,7 +2034,7 @@ func (s *SocketOperations) SendMsg(t *kernel.Task, src usermem.IOSequence, to []
var addr *tcpip.FullAddress
if len(to) > 0 {
addrBuf, err := GetAddress(s.family, to, true /* strict */)
addrBuf, _, err := AddressAndFamily(s.family, to, true /* strict */)
if err != nil {
return 0, err
}
+1 -1
View File
@@ -116,7 +116,7 @@ func (s *SocketOperations) Endpoint() transport.Endpoint {
// extractPath extracts and validates the address.
func extractPath(sockaddr []byte) (string, *syserr.Error) {
addr, err := epsocket.GetAddress(linux.AF_UNIX, sockaddr, true /* strict */)
addr, _, err := epsocket.AddressAndFamily(linux.AF_UNIX, sockaddr, true /* strict */)
if err != nil {
return "", err
}
+1 -1
View File
@@ -332,7 +332,7 @@ func sockAddr(t *kernel.Task, addr usermem.Addr, length uint32) string {
switch family {
case linux.AF_INET, linux.AF_INET6, linux.AF_UNIX:
fa, err := epsocket.GetAddress(int(family), b, true /* strict */)
fa, _, err := epsocket.AddressAndFamily(int(family), b, true /* strict */)
if err != nil {
return fmt.Sprintf("%#x {Family: %s, error extracting address: %v}", addr, familyStr, err)
}
+5
View File
@@ -105,6 +105,11 @@ func (*fakeTransportEndpoint) GetSockOpt(opt interface{}) *tcpip.Error {
return tcpip.ErrInvalidEndpointState
}
// Disconnect implements tcpip.Endpoint.Disconnect.
func (*fakeTransportEndpoint) Disconnect() *tcpip.Error {
return tcpip.ErrNotSupported
}
func (f *fakeTransportEndpoint) Connect(addr tcpip.FullAddress) *tcpip.Error {
f.peerAddr = addr.Addr
+3
View File
@@ -358,6 +358,9 @@ type Endpoint interface {
// ErrAddressFamilyNotSupported must be returned.
Connect(address FullAddress) *Error
// Disconnect disconnects the endpoint from its peer.
Disconnect() *Error
// Shutdown closes the read and/or write end of the endpoint connection
// to its peer.
Shutdown(flags ShutdownFlags) *Error
+5 -5
View File
@@ -428,16 +428,16 @@ func (e *endpoint) checkV4Mapped(addr *tcpip.FullAddress, allowMismatch bool) (t
return netProto, nil
}
// Disconnect implements tcpip.Endpoint.Disconnect.
func (*endpoint) Disconnect() *tcpip.Error {
return tcpip.ErrNotSupported
}
// Connect connects the endpoint to its peer. Specifying a NIC is optional.
func (e *endpoint) Connect(addr tcpip.FullAddress) *tcpip.Error {
e.mu.Lock()
defer e.mu.Unlock()
if addr.Addr == "" {
// AF_UNSPEC isn't supported.
return tcpip.ErrAddressFamilyNotSupported
}
nicid := addr.NIC
localPort := uint16(0)
switch e.state {
+5 -5
View File
@@ -349,16 +349,16 @@ func (ep *endpoint) Peek([][]byte) (int64, tcpip.ControlMessages, *tcpip.Error)
return 0, tcpip.ControlMessages{}, nil
}
// Disconnect implements tcpip.Endpoint.Disconnect.
func (*endpoint) Disconnect() *tcpip.Error {
return tcpip.ErrNotSupported
}
// Connect implements tcpip.Endpoint.Connect.
func (ep *endpoint) Connect(addr tcpip.FullAddress) *tcpip.Error {
ep.mu.Lock()
defer ep.mu.Unlock()
if addr.Addr == "" {
// AF_UNSPEC isn't supported.
return tcpip.ErrAddressFamilyNotSupported
}
if ep.closed {
return tcpip.ErrInvalidEndpointState
}
+5 -5
View File
@@ -1362,13 +1362,13 @@ func (e *endpoint) checkV4Mapped(addr *tcpip.FullAddress) (tcpip.NetworkProtocol
return netProto, nil
}
// Disconnect implements tcpip.Endpoint.Disconnect.
func (*endpoint) Disconnect() *tcpip.Error {
return tcpip.ErrNotSupported
}
// Connect connects the endpoint to its peer.
func (e *endpoint) Connect(addr tcpip.FullAddress) *tcpip.Error {
if addr.Addr == "" && addr.Port == 0 {
// AF_UNSPEC isn't supported.
return tcpip.ErrAddressFamilyNotSupported
}
return e.connect(addr, true, true)
}
+2 -4
View File
@@ -711,7 +711,8 @@ func (e *endpoint) checkV4Mapped(addr *tcpip.FullAddress, allowMismatch bool) (t
return netProto, nil
}
func (e *endpoint) disconnect() *tcpip.Error {
// Disconnect implements tcpip.Endpoint.Disconnect.
func (e *endpoint) Disconnect() *tcpip.Error {
e.mu.Lock()
defer e.mu.Unlock()
@@ -750,9 +751,6 @@ func (e *endpoint) Connect(addr tcpip.FullAddress) *tcpip.Error {
if err != nil {
return err
}
if addr.Addr == "" {
return e.disconnect()
}
if addr.Port == 0 {
// We don't support connecting to port zero.
return tcpip.ErrInvalidEndpointState
+105 -19
View File
@@ -378,16 +378,17 @@ TEST_P(UdpSocketTest, Connect) {
EXPECT_EQ(memcmp(&peer, addr_[2], addrlen_), 0);
}
TEST_P(UdpSocketTest, ConnectAny) {
void ConnectAny(AddressFamily family, int sockfd, uint16_t port) {
struct sockaddr_storage addr = {};
// Precondition check.
{
socklen_t addrlen = sizeof(addr);
EXPECT_THAT(getsockname(s_, reinterpret_cast<sockaddr*>(&addr), &addrlen),
SyscallSucceeds());
EXPECT_THAT(
getsockname(sockfd, reinterpret_cast<sockaddr*>(&addr), &addrlen),
SyscallSucceeds());
if (GetParam() == AddressFamily::kIpv4) {
if (family == AddressFamily::kIpv4) {
auto addr_out = reinterpret_cast<struct sockaddr_in*>(&addr);
EXPECT_EQ(addrlen, sizeof(*addr_out));
EXPECT_EQ(addr_out->sin_addr.s_addr, htonl(INADDR_ANY));
@@ -400,21 +401,24 @@ TEST_P(UdpSocketTest, ConnectAny) {
{
socklen_t addrlen = sizeof(addr);
EXPECT_THAT(getpeername(s_, reinterpret_cast<sockaddr*>(&addr), &addrlen),
SyscallFailsWithErrno(ENOTCONN));
EXPECT_THAT(
getpeername(sockfd, reinterpret_cast<sockaddr*>(&addr), &addrlen),
SyscallFailsWithErrno(ENOTCONN));
}
struct sockaddr_storage baddr = {};
if (GetParam() == AddressFamily::kIpv4) {
if (family == AddressFamily::kIpv4) {
auto addr_in = reinterpret_cast<struct sockaddr_in*>(&baddr);
addrlen = sizeof(*addr_in);
addr_in->sin_family = AF_INET;
addr_in->sin_addr.s_addr = htonl(INADDR_ANY);
addr_in->sin_port = port;
} else {
auto addr_in = reinterpret_cast<struct sockaddr_in6*>(&baddr);
addrlen = sizeof(*addr_in);
addr_in->sin6_family = AF_INET6;
if (GetParam() == AddressFamily::kIpv6) {
addr_in->sin6_port = port;
if (family == AddressFamily::kIpv6) {
addr_in->sin6_addr = IN6ADDR_ANY_INIT;
} else {
TestAddress const& v4_mapped_any = V4MappedAny();
@@ -424,21 +428,23 @@ TEST_P(UdpSocketTest, ConnectAny) {
}
}
ASSERT_THAT(connect(s_, reinterpret_cast<sockaddr*>(&baddr), addrlen),
// TODO(b/138658473): gVisor doesn't allow connecting to the zero port.
if (port == 0) {
SKIP_IF(IsRunningOnGvisor());
}
ASSERT_THAT(connect(sockfd, reinterpret_cast<sockaddr*>(&baddr), addrlen),
SyscallSucceeds());
}
// TODO(b/138658473): gVisor doesn't return the correct local address after
// connecting to the any address.
SKIP_IF(IsRunningOnGvisor());
// Postcondition check.
{
socklen_t addrlen = sizeof(addr);
EXPECT_THAT(getsockname(s_, reinterpret_cast<sockaddr*>(&addr), &addrlen),
SyscallSucceeds());
EXPECT_THAT(
getsockname(sockfd, reinterpret_cast<sockaddr*>(&addr), &addrlen),
SyscallSucceeds());
if (GetParam() == AddressFamily::kIpv4) {
if (family == AddressFamily::kIpv4) {
auto addr_out = reinterpret_cast<struct sockaddr_in*>(&addr);
EXPECT_EQ(addrlen, sizeof(*addr_out));
EXPECT_EQ(addr_out->sin_addr.s_addr, htonl(INADDR_LOOPBACK));
@@ -446,7 +452,7 @@ TEST_P(UdpSocketTest, ConnectAny) {
auto addr_out = reinterpret_cast<struct sockaddr_in6*>(&addr);
EXPECT_EQ(addrlen, sizeof(*addr_out));
struct in6_addr loopback;
if (GetParam() == AddressFamily::kIpv6) {
if (family == AddressFamily::kIpv6) {
loopback = IN6ADDR_LOOPBACK_INIT;
} else {
TestAddress const& v4_mapped_loopback = V4MappedLoopback();
@@ -459,11 +465,91 @@ TEST_P(UdpSocketTest, ConnectAny) {
}
addrlen = sizeof(addr);
EXPECT_THAT(getpeername(s_, reinterpret_cast<sockaddr*>(&addr), &addrlen),
SyscallFailsWithErrno(ENOTCONN));
if (port == 0) {
EXPECT_THAT(
getpeername(sockfd, reinterpret_cast<sockaddr*>(&addr), &addrlen),
SyscallFailsWithErrno(ENOTCONN));
} else {
EXPECT_THAT(
getpeername(sockfd, reinterpret_cast<sockaddr*>(&addr), &addrlen),
SyscallSucceeds());
}
}
}
TEST_P(UdpSocketTest, ConnectAny) { ConnectAny(GetParam(), s_, 0); }
TEST_P(UdpSocketTest, ConnectAnyWithPort) {
auto port = *Port(reinterpret_cast<struct sockaddr_storage*>(addr_[1]));
ConnectAny(GetParam(), s_, port);
}
void DisconnectAfterConnectAny(AddressFamily family, int sockfd, int port) {
struct sockaddr_storage addr = {};
socklen_t addrlen = sizeof(addr);
struct sockaddr_storage baddr = {};
if (family == AddressFamily::kIpv4) {
auto addr_in = reinterpret_cast<struct sockaddr_in*>(&baddr);
addrlen = sizeof(*addr_in);
addr_in->sin_family = AF_INET;
addr_in->sin_addr.s_addr = htonl(INADDR_ANY);
addr_in->sin_port = port;
} else {
auto addr_in = reinterpret_cast<struct sockaddr_in6*>(&baddr);
addrlen = sizeof(*addr_in);
addr_in->sin6_family = AF_INET6;
addr_in->sin6_port = port;
if (family == AddressFamily::kIpv6) {
addr_in->sin6_addr = IN6ADDR_ANY_INIT;
} else {
TestAddress const& v4_mapped_any = V4MappedAny();
addr_in->sin6_addr =
reinterpret_cast<const struct sockaddr_in6*>(&v4_mapped_any.addr)
->sin6_addr;
}
}
// TODO(b/138658473): gVisor doesn't allow connecting to the zero port.
if (port == 0) {
SKIP_IF(IsRunningOnGvisor());
}
ASSERT_THAT(connect(sockfd, reinterpret_cast<sockaddr*>(&baddr), addrlen),
SyscallSucceeds());
// Now the socket is bound to the loopback address.
// Disconnect
addrlen = sizeof(addr);
addr.ss_family = AF_UNSPEC;
ASSERT_THAT(connect(sockfd, reinterpret_cast<sockaddr*>(&addr), addrlen),
SyscallSucceeds());
// Check that after disconnect the socket is bound to the ANY address.
EXPECT_THAT(getsockname(sockfd, reinterpret_cast<sockaddr*>(&addr), &addrlen),
SyscallSucceeds());
if (family == AddressFamily::kIpv4) {
auto addr_out = reinterpret_cast<struct sockaddr_in*>(&addr);
EXPECT_EQ(addrlen, sizeof(*addr_out));
EXPECT_EQ(addr_out->sin_addr.s_addr, htonl(INADDR_ANY));
} else {
auto addr_out = reinterpret_cast<struct sockaddr_in6*>(&addr);
EXPECT_EQ(addrlen, sizeof(*addr_out));
struct in6_addr loopback = IN6ADDR_ANY_INIT;
EXPECT_EQ(memcmp(&addr_out->sin6_addr, &loopback, sizeof(in6_addr)), 0);
}
}
TEST_P(UdpSocketTest, DisconnectAfterConnectAny) {
DisconnectAfterConnectAny(GetParam(), s_, 0);
}
TEST_P(UdpSocketTest, DisconnectAfterConnectAnyWithPort) {
auto port = *Port(reinterpret_cast<struct sockaddr_storage*>(addr_[1]));
DisconnectAfterConnectAny(GetParam(), s_, port);
}
TEST_P(UdpSocketTest, DisconnectAfterBind) {
ASSERT_THAT(bind(s_, addr_[1], addrlen_), SyscallSucceeds());
// Connect the socket.