Add support for SO_ERROR to packet sockets.

Packet sockets also seem to allow double binding and do not return an error on
linux. This was tested by running the syscall test in a linux namespace as root
and the current test DoubleBind fails@HEAD.

Passes after this change.

Updates #173

PiperOrigin-RevId: 321445137
This commit is contained in:
Bhasker Hariharan
2020-07-15 14:56:39 -07:00
committed by gVisor bot
parent fef90c61c6
commit 857d03f258
5 changed files with 107 additions and 10 deletions
+1 -1
View File
@@ -22,7 +22,7 @@ import (
// Mapping for tcpip.Error types.
var (
ErrUnknownProtocol = New(tcpip.ErrUnknownProtocol.String(), linux.EINVAL)
ErrUnknownNICID = New(tcpip.ErrUnknownNICID.String(), linux.EINVAL)
ErrUnknownNICID = New(tcpip.ErrUnknownNICID.String(), linux.ENODEV)
ErrUnknownDevice = New(tcpip.ErrUnknownDevice.String(), linux.ENODEV)
ErrUnknownProtocolOption = New(tcpip.ErrUnknownProtocolOption.String(), linux.ENOPROTOOPT)
ErrDuplicateNICID = New(tcpip.ErrDuplicateNICID.String(), linux.EEXIST)
+23 -2
View File
@@ -79,6 +79,11 @@ type endpoint struct {
closed bool
stats tcpip.TransportEndpointStats `state:"nosave"`
bound bool
boundNIC tcpip.NICID
// lastErrorMu protects lastError.
lastErrorMu sync.Mutex `state:"nosave"`
lastError *tcpip.Error `state:".(string)"`
}
// NewEndpoint returns a new packet endpoint.
@@ -229,12 +234,14 @@ func (ep *endpoint) Bind(addr tcpip.FullAddress) *tcpip.Error {
ep.mu.Lock()
defer ep.mu.Unlock()
if ep.bound {
return tcpip.ErrAlreadyBound
if ep.bound && ep.boundNIC == addr.NIC {
// If the NIC being bound is the same then just return success.
return nil
}
// Unregister endpoint with all the nics.
ep.stack.UnregisterPacketEndpoint(0, ep.netProto, ep)
ep.bound = false
// Bind endpoint to receive packets from specific interface.
if err := ep.stack.RegisterPacketEndpoint(addr.NIC, ep.netProto, ep); err != nil {
@@ -242,6 +249,7 @@ func (ep *endpoint) Bind(addr tcpip.FullAddress) *tcpip.Error {
}
ep.bound = true
ep.boundNIC = addr.NIC
return nil
}
@@ -336,8 +344,21 @@ func (ep *endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) *tcpip.Error {
}
}
func (ep *endpoint) takeLastError() *tcpip.Error {
ep.lastErrorMu.Lock()
defer ep.lastErrorMu.Unlock()
err := ep.lastError
ep.lastError = nil
return err
}
// GetSockOpt implements tcpip.Endpoint.GetSockOpt.
func (ep *endpoint) GetSockOpt(opt interface{}) *tcpip.Error {
switch opt.(type) {
case tcpip.ErrorOption:
return ep.takeLastError()
}
return tcpip.ErrNotSupported
}
@@ -15,6 +15,7 @@
package packet
import (
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
"gvisor.dev/gvisor/pkg/tcpip/stack"
)
@@ -70,3 +71,21 @@ func (ep *endpoint) afterLoad() {
panic(*err)
}
}
// saveLastError is invoked by stateify.
func (ep *endpoint) saveLastError() string {
if ep.lastError == nil {
return ""
}
return ep.lastError.String()
}
// loadLastError is invoked by stateify.
func (ep *endpoint) loadLastError(s string) {
if s == "" {
return
}
ep.lastError = tcpip.StringToError(s)
}
+6 -7
View File
@@ -343,7 +343,7 @@ TEST_P(CookedPacketTest, BindReceive) {
}
// Double Bind socket.
TEST_P(CookedPacketTest, DoubleBind) {
TEST_P(CookedPacketTest, DoubleBindSucceeds) {
struct sockaddr_ll bind_addr = {};
bind_addr.sll_family = AF_PACKET;
bind_addr.sll_protocol = htons(GetParam());
@@ -354,12 +354,11 @@ TEST_P(CookedPacketTest, DoubleBind) {
SyscallSucceeds());
// Binding socket again should fail.
ASSERT_THAT(
bind(socket_, reinterpret_cast<struct sockaddr*>(&bind_addr),
sizeof(bind_addr)),
// Linux 4.09 returns EINVAL here, but some time before 4.19 it switched
// to EADDRINUSE.
AnyOf(SyscallFailsWithErrno(EADDRINUSE), SyscallFailsWithErrno(EINVAL)));
ASSERT_THAT(bind(socket_, reinterpret_cast<struct sockaddr*>(&bind_addr),
sizeof(bind_addr)),
// Linux 4.09 returns EINVAL here, but some time before 4.19 it
// switched to EADDRINUSE.
SyscallSucceeds());
}
// Bind and verify we do not receive data on interface which is not bound
+58
View File
@@ -559,6 +559,64 @@ TEST_P(RawPacketTest, SetSocketSendBuf) {
ASSERT_EQ(quarter_sz, val);
}
TEST_P(RawPacketTest, GetSocketError) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
int val = 0;
socklen_t val_len = sizeof(val);
ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_ERROR, &val, &val_len),
SyscallSucceeds());
ASSERT_EQ(val, 0);
}
TEST_P(RawPacketTest, GetSocketErrorBind) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
{
// Bind to the loopback device.
struct sockaddr_ll bind_addr = {};
bind_addr.sll_family = AF_PACKET;
bind_addr.sll_protocol = htons(GetParam());
bind_addr.sll_ifindex = GetLoopbackIndex();
ASSERT_THAT(bind(s_, reinterpret_cast<struct sockaddr*>(&bind_addr),
sizeof(bind_addr)),
SyscallSucceeds());
// SO_ERROR should return no errors.
int val = 0;
socklen_t val_len = sizeof(val);
ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_ERROR, &val, &val_len),
SyscallSucceeds());
ASSERT_EQ(val, 0);
}
{
// Now try binding to an invalid interface.
struct sockaddr_ll bind_addr = {};
bind_addr.sll_family = AF_PACKET;
bind_addr.sll_protocol = htons(GetParam());
bind_addr.sll_ifindex = 0xffff; // Just pick a really large number.
// Binding should fail with EINVAL
ASSERT_THAT(bind(s_, reinterpret_cast<struct sockaddr*>(&bind_addr),
sizeof(bind_addr)),
SyscallFailsWithErrno(ENODEV));
// SO_ERROR does not return error when the device is invalid.
// On Linux there is just one odd ball condition where this can return
// an error where the device was valid and then removed or disabled
// between the first check for index and the actual registration of
// the packet endpoint. On Netstack this is not possible as the stack
// global mutex is held during registration and check.
int val = 0;
socklen_t val_len = sizeof(val);
ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_ERROR, &val, &val_len),
SyscallSucceeds());
ASSERT_EQ(val, 0);
}
}
#ifndef __fuchsia__
TEST_P(RawPacketTest, SetSocketDetachFilterNoInstalledFilter) {