remove iptables sockopt special cases

iptables sockopts were kludged into an unnecessary check, this properly
relegates them to the {get,set}SockOptIP functions.

PiperOrigin-RevId: 328395135
This commit is contained in:
Kevin Krakauer
2020-08-25 13:43:26 -07:00
committed by gVisor bot
parent 3b2e50f539
commit 3cba0a41d9
5 changed files with 104 additions and 142 deletions
+67 -70
View File
@@ -983,53 +983,12 @@ func (s *SocketOperations) GetSockOpt(t *kernel.Task, level, name int, outPtr us
return &val, nil
}
if s.skType == linux.SOCK_RAW && level == linux.IPPROTO_IP {
switch name {
case linux.IPT_SO_GET_INFO:
if outLen < linux.SizeOfIPTGetinfo {
return nil, syserr.ErrInvalidArgument
}
if s.family != linux.AF_INET {
return nil, syserr.ErrInvalidArgument
}
stack := inet.StackFromContext(t)
if stack == nil {
return nil, syserr.ErrNoDevice
}
info, err := netfilter.GetInfo(t, stack.(*Stack).Stack, outPtr)
if err != nil {
return nil, err
}
return &info, nil
case linux.IPT_SO_GET_ENTRIES:
if outLen < linux.SizeOfIPTGetEntries {
return nil, syserr.ErrInvalidArgument
}
if s.family != linux.AF_INET {
return nil, syserr.ErrInvalidArgument
}
stack := inet.StackFromContext(t)
if stack == nil {
return nil, syserr.ErrNoDevice
}
entries, err := netfilter.GetEntries4(t, stack.(*Stack).Stack, outPtr, outLen)
if err != nil {
return nil, err
}
return &entries, nil
}
}
return GetSockOpt(t, s, s.Endpoint, s.family, s.skType, level, name, outLen)
return GetSockOpt(t, s, s.Endpoint, s.family, s.skType, level, name, outPtr, outLen)
}
// GetSockOpt can be used to implement the linux syscall getsockopt(2) for
// sockets backed by a commonEndpoint.
func GetSockOpt(t *kernel.Task, s socket.SocketOps, ep commonEndpoint, family int, skType linux.SockType, level, name, outLen int) (marshal.Marshallable, *syserr.Error) {
func GetSockOpt(t *kernel.Task, s socket.SocketOps, ep commonEndpoint, family int, skType linux.SockType, level, name int, outPtr usermem.Addr, outLen int) (marshal.Marshallable, *syserr.Error) {
switch level {
case linux.SOL_SOCKET:
return getSockOptSocket(t, s, ep, family, skType, name, outLen)
@@ -1041,7 +1000,7 @@ func GetSockOpt(t *kernel.Task, s socket.SocketOps, ep commonEndpoint, family in
return getSockOptIPv6(t, ep, name, outLen)
case linux.SOL_IP:
return getSockOptIP(t, ep, name, outLen, family)
return getSockOptIP(t, s, ep, name, outPtr, outLen, family)
case linux.SOL_UDP,
linux.SOL_ICMPV6,
@@ -1560,7 +1519,7 @@ func getSockOptIPv6(t *kernel.Task, ep commonEndpoint, name, outLen int) (marsha
}
// getSockOptIP implements GetSockOpt when level is SOL_IP.
func getSockOptIP(t *kernel.Task, ep commonEndpoint, name, outLen int, family int) (marshal.Marshallable, *syserr.Error) {
func getSockOptIP(t *kernel.Task, s socket.SocketOps, ep commonEndpoint, name int, outPtr usermem.Addr, outLen int, family int) (marshal.Marshallable, *syserr.Error) {
switch name {
case linux.IP_TTL:
if outLen < sizeOfInt32 {
@@ -1676,6 +1635,46 @@ func getSockOptIP(t *kernel.Task, ep commonEndpoint, name, outLen int, family in
a, _ := ConvertAddress(linux.AF_INET, tcpip.FullAddress(v))
return a.(*linux.SockAddrInet), nil
case linux.IPT_SO_GET_INFO:
if outLen < linux.SizeOfIPTGetinfo {
return nil, syserr.ErrInvalidArgument
}
// Only valid for raw IPv4 sockets.
if family, skType, _ := s.Type(); family != linux.AF_INET || skType != linux.SOCK_RAW {
return nil, syserr.ErrProtocolNotAvailable
}
stack := inet.StackFromContext(t)
if stack == nil {
return nil, syserr.ErrNoDevice
}
info, err := netfilter.GetInfo(t, stack.(*Stack).Stack, outPtr)
if err != nil {
return nil, err
}
return &info, nil
case linux.IPT_SO_GET_ENTRIES:
if outLen < linux.SizeOfIPTGetEntries {
return nil, syserr.ErrInvalidArgument
}
// Only valid for raw IPv4 sockets.
if family, skType, _ := s.Type(); family != linux.AF_INET || skType != linux.SOCK_RAW {
return nil, syserr.ErrProtocolNotAvailable
}
stack := inet.StackFromContext(t)
if stack == nil {
return nil, syserr.ErrNoDevice
}
entries, err := netfilter.GetEntries4(t, stack.(*Stack).Stack, outPtr, outLen)
if err != nil {
return nil, err
}
return &entries, nil
default:
emitUnimplementedEventIP(t, name)
}
@@ -1709,29 +1708,6 @@ func (s *SocketOperations) SetSockOpt(t *kernel.Task, level int, name int, optVa
return nil
}
if s.skType == linux.SOCK_RAW && level == linux.SOL_IP {
switch name {
case linux.IPT_SO_SET_REPLACE:
if len(optVal) < linux.SizeOfIPTReplace {
return syserr.ErrInvalidArgument
}
if s.family != linux.AF_INET {
return syserr.ErrInvalidArgument
}
stack := inet.StackFromContext(t)
if stack == nil {
return syserr.ErrNoDevice
}
// Stack must be a netstack stack.
return netfilter.SetEntries(stack.(*Stack).Stack, optVal)
case linux.IPT_SO_SET_ADD_COUNTERS:
// TODO(gvisor.dev/issue/170): Counter support.
return nil
}
}
return SetSockOpt(t, s, s.Endpoint, level, name, optVal)
}
@@ -1749,7 +1725,7 @@ func SetSockOpt(t *kernel.Task, s socket.SocketOps, ep commonEndpoint, level int
return setSockOptIPv6(t, ep, name, optVal)
case linux.SOL_IP:
return setSockOptIP(t, ep, name, optVal)
return setSockOptIP(t, s, ep, name, optVal)
case linux.SOL_UDP,
linux.SOL_ICMPV6,
@@ -2160,7 +2136,7 @@ func parseIntOrChar(buf []byte) (int32, *syserr.Error) {
}
// setSockOptIP implements SetSockOpt when level is SOL_IP.
func setSockOptIP(t *kernel.Task, ep commonEndpoint, name int, optVal []byte) *syserr.Error {
func setSockOptIP(t *kernel.Task, s socket.SocketOps, ep commonEndpoint, name int, optVal []byte) *syserr.Error {
switch name {
case linux.IP_MULTICAST_TTL:
v, err := parseIntOrChar(optVal)
@@ -2280,6 +2256,27 @@ func setSockOptIP(t *kernel.Task, ep commonEndpoint, name int, optVal []byte) *s
}
return syserr.TranslateNetstackError(ep.SetSockOptBool(tcpip.IPHdrIncludedOption, v != 0))
case linux.IPT_SO_SET_REPLACE:
if len(optVal) < linux.SizeOfIPTReplace {
return syserr.ErrInvalidArgument
}
// Only valid for raw IPv4 sockets.
if family, skType, _ := s.Type(); family != linux.AF_INET || skType != linux.SOCK_RAW {
return syserr.ErrProtocolNotAvailable
}
stack := inet.StackFromContext(t)
if stack == nil {
return syserr.ErrNoDevice
}
// Stack must be a netstack stack.
return netfilter.SetEntries(stack.(*Stack).Stack, optVal)
case linux.IPT_SO_SET_ADD_COUNTERS:
// TODO(gvisor.dev/issue/170): Counter support.
return nil
case linux.IP_ADD_SOURCE_MEMBERSHIP,
linux.IP_BIND_ADDRESS_NO_PORT,
linux.IP_BLOCK_SOURCE,
+1 -67
View File
@@ -21,10 +21,8 @@ import (
"gvisor.dev/gvisor/pkg/sentry/arch"
fslock "gvisor.dev/gvisor/pkg/sentry/fs/lock"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/sockfs"
"gvisor.dev/gvisor/pkg/sentry/inet"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/socket"
"gvisor.dev/gvisor/pkg/sentry/socket/netfilter"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/syserr"
"gvisor.dev/gvisor/pkg/syserror"
@@ -233,48 +231,7 @@ func (s *SocketVFS2) GetSockOpt(t *kernel.Task, level, name int, outPtr usermem.
return &val, nil
}
if s.skType == linux.SOCK_RAW && level == linux.IPPROTO_IP {
switch name {
case linux.IPT_SO_GET_INFO:
if outLen < linux.SizeOfIPTGetinfo {
return nil, syserr.ErrInvalidArgument
}
if s.family != linux.AF_INET {
return nil, syserr.ErrInvalidArgument
}
stack := inet.StackFromContext(t)
if stack == nil {
return nil, syserr.ErrNoDevice
}
info, err := netfilter.GetInfo(t, stack.(*Stack).Stack, outPtr)
if err != nil {
return nil, err
}
return &info, nil
case linux.IPT_SO_GET_ENTRIES:
if outLen < linux.SizeOfIPTGetEntries {
return nil, syserr.ErrInvalidArgument
}
if s.family != linux.AF_INET {
return nil, syserr.ErrInvalidArgument
}
stack := inet.StackFromContext(t)
if stack == nil {
return nil, syserr.ErrNoDevice
}
entries, err := netfilter.GetEntries4(t, stack.(*Stack).Stack, outPtr, outLen)
if err != nil {
return nil, err
}
return &entries, nil
}
}
return GetSockOpt(t, s, s.Endpoint, s.family, s.skType, level, name, outLen)
return GetSockOpt(t, s, s.Endpoint, s.family, s.skType, level, name, outPtr, outLen)
}
// SetSockOpt implements the linux syscall setsockopt(2) for sockets backed by
@@ -304,29 +261,6 @@ func (s *SocketVFS2) SetSockOpt(t *kernel.Task, level int, name int, optVal []by
return nil
}
if s.skType == linux.SOCK_RAW && level == linux.SOL_IP {
switch name {
case linux.IPT_SO_SET_REPLACE:
if len(optVal) < linux.SizeOfIPTReplace {
return syserr.ErrInvalidArgument
}
if s.family != linux.AF_INET {
return syserr.ErrInvalidArgument
}
stack := inet.StackFromContext(t)
if stack == nil {
return syserr.ErrNoDevice
}
// Stack must be a netstack stack.
return netfilter.SetEntries(stack.(*Stack).Stack, optVal)
case linux.IPT_SO_SET_ADD_COUNTERS:
// TODO(gvisor.dev/issue/170): Counter support.
return nil
}
}
return SetSockOpt(t, s, s.Endpoint, level, name, optVal)
}
+1 -1
View File
@@ -194,7 +194,7 @@ func (s *SocketOperations) Ioctl(ctx context.Context, _ *fs.File, io usermem.IO,
// GetSockOpt implements the linux syscall getsockopt(2) for sockets backed by
// a transport.Endpoint.
func (s *SocketOperations) GetSockOpt(t *kernel.Task, level, name int, outPtr usermem.Addr, outLen int) (marshal.Marshallable, *syserr.Error) {
return netstack.GetSockOpt(t, s, s.ep, linux.AF_UNIX, s.ep.Type(), level, name, outLen)
return netstack.GetSockOpt(t, s, s.ep, linux.AF_UNIX, s.ep.Type(), level, name, outPtr, outLen)
}
// Listen implements the linux syscall listen(2) for sockets backed by
+1 -1
View File
@@ -91,7 +91,7 @@ func NewFileDescription(ep transport.Endpoint, stype linux.SockType, flags uint3
// GetSockOpt implements the linux syscall getsockopt(2) for sockets backed by
// a transport.Endpoint.
func (s *SocketVFS2) GetSockOpt(t *kernel.Task, level, name int, outPtr usermem.Addr, outLen int) (marshal.Marshallable, *syserr.Error) {
return netstack.GetSockOpt(t, s, s.ep, linux.AF_UNIX, s.ep.Type(), level, name, outLen)
return netstack.GetSockOpt(t, s, s.ep, linux.AF_UNIX, s.ep.Type(), level, name, outPtr, outLen)
}
// blockingAccept implements a blocking version of accept(2), that is, if no
+34 -3
View File
@@ -67,12 +67,43 @@ TEST(IPTablesBasic, FailSockoptNonRaw) {
struct ipt_getinfo info = {};
snprintf(info.name, XT_TABLE_MAXNAMELEN, "%s", kNatTablename);
socklen_t info_size = sizeof(info);
EXPECT_THAT(getsockopt(sock, IPPROTO_IP, IPT_SO_GET_INFO, &info, &info_size),
EXPECT_THAT(getsockopt(sock, SOL_IP, IPT_SO_GET_INFO, &info, &info_size),
SyscallFailsWithErrno(ENOPROTOOPT));
ASSERT_THAT(close(sock), SyscallSucceeds());
}
TEST(IPTablesBasic, GetInfoErrorPrecedence) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
int sock;
ASSERT_THAT(sock = socket(AF_INET, SOCK_DGRAM, 0), SyscallSucceeds());
// When using the wrong type of socket and a too-short optlen, we should get
// EINVAL.
struct ipt_getinfo info = {};
snprintf(info.name, XT_TABLE_MAXNAMELEN, "%s", kNatTablename);
socklen_t info_size = sizeof(info) - 1;
ASSERT_THAT(getsockopt(sock, SOL_IP, IPT_SO_GET_INFO, &info, &info_size),
SyscallFailsWithErrno(EINVAL));
}
TEST(IPTablesBasic, GetEntriesErrorPrecedence) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
int sock;
ASSERT_THAT(sock = socket(AF_INET, SOCK_DGRAM, 0), SyscallSucceeds());
// When using the wrong type of socket and a too-short optlen, we should get
// EINVAL.
struct ipt_get_entries entries = {};
socklen_t entries_size = sizeof(struct ipt_get_entries) - 1;
snprintf(entries.name, XT_TABLE_MAXNAMELEN, "%s", kNatTablename);
ASSERT_THAT(
getsockopt(sock, SOL_IP, IPT_SO_GET_ENTRIES, &entries, &entries_size),
SyscallFailsWithErrno(EINVAL));
}
// Fixture for iptables tests.
class IPTablesTest : public ::testing::Test {
protected:
@@ -112,7 +143,7 @@ TEST_F(IPTablesTest, InitialState) {
struct ipt_getinfo info = {};
snprintf(info.name, XT_TABLE_MAXNAMELEN, "%s", kNatTablename);
socklen_t info_size = sizeof(info);
ASSERT_THAT(getsockopt(s_, IPPROTO_IP, IPT_SO_GET_INFO, &info, &info_size),
ASSERT_THAT(getsockopt(s_, SOL_IP, IPT_SO_GET_INFO, &info, &info_size),
SyscallSucceeds());
// The nat table supports PREROUTING, and OUTPUT.
@@ -148,7 +179,7 @@ TEST_F(IPTablesTest, InitialState) {
snprintf(entries->name, XT_TABLE_MAXNAMELEN, "%s", kNatTablename);
entries->size = info.size;
ASSERT_THAT(
getsockopt(s_, IPPROTO_IP, IPT_SO_GET_ENTRIES, entries, &entries_size),
getsockopt(s_, SOL_IP, IPT_SO_GET_ENTRIES, entries, &entries_size),
SyscallSucceeds());
// Verify the name and size.