diff --git a/pkg/sentry/socket/netlink/BUILD b/pkg/sentry/socket/netlink/BUILD index 4bbdca48d..c47556a23 100644 --- a/pkg/sentry/socket/netlink/BUILD +++ b/pkg/sentry/socket/netlink/BUILD @@ -22,6 +22,7 @@ go_library( "//pkg/marshal/primitive", "//pkg/sentry/arch", "//pkg/sentry/fsimpl/sockfs", + "//pkg/sentry/inet", "//pkg/sentry/kernel", "//pkg/sentry/kernel/auth", "//pkg/sentry/kernel/time", diff --git a/pkg/sentry/socket/netlink/provider.go b/pkg/sentry/socket/netlink/provider.go index fe431755d..071dda67c 100644 --- a/pkg/sentry/socket/netlink/provider.go +++ b/pkg/sentry/socket/netlink/provider.go @@ -44,7 +44,7 @@ type Protocol interface { // If err == nil, any messages added to ms will be sent back to the // other end of the socket. Setting ms.Multi will cause an NLMSG_DONE // message to be sent even if ms contains no messages. - ProcessMessage(ctx context.Context, msg *nlmsg.Message, ms *nlmsg.MessageSet) *syserr.Error + ProcessMessage(ctx context.Context, s *Socket, msg *nlmsg.Message, ms *nlmsg.MessageSet) *syserr.Error } // Provider is a function that creates a new Protocol for a specific netlink diff --git a/pkg/sentry/socket/netlink/route/protocol.go b/pkg/sentry/socket/netlink/route/protocol.go index 584b941dc..c3629a4f8 100644 --- a/pkg/sentry/socket/netlink/route/protocol.go +++ b/pkg/sentry/socket/netlink/route/protocol.go @@ -70,7 +70,7 @@ func (p *Protocol) CanSend() bool { } // dumpLinks handles RTM_GETLINK dump requests. -func (p *Protocol) dumpLinks(ctx context.Context, msg *nlmsg.Message, ms *nlmsg.MessageSet) *syserr.Error { +func (p *Protocol) dumpLinks(ctx context.Context, s *netlink.Socket, msg *nlmsg.Message, ms *nlmsg.MessageSet) *syserr.Error { // NLM_F_DUMP + RTM_GETLINK messages are supposed to include an // ifinfomsg. However, Linux <3.9 only checked for rtgenmsg, and some // userspace applications (including glibc) still include rtgenmsg. @@ -88,7 +88,7 @@ func (p *Protocol) dumpLinks(ctx context.Context, msg *nlmsg.Message, ms *nlmsg. // We always send back an NLMSG_DONE. ms.Multi = true - stack := inet.StackFromContext(ctx) + stack := s.Stack() if stack == nil { // No network devices. return nil @@ -102,8 +102,8 @@ func (p *Protocol) dumpLinks(ctx context.Context, msg *nlmsg.Message, ms *nlmsg. } // getLinks handles RTM_GETLINK requests. -func (p *Protocol) getLink(ctx context.Context, msg *nlmsg.Message, ms *nlmsg.MessageSet) *syserr.Error { - stack := inet.StackFromContext(ctx) +func (p *Protocol) getLink(ctx context.Context, s *netlink.Socket, msg *nlmsg.Message, ms *nlmsg.MessageSet) *syserr.Error { + stack := s.Stack() if stack == nil { // No network devices. return nil @@ -162,8 +162,8 @@ func (p *Protocol) getLink(ctx context.Context, msg *nlmsg.Message, ms *nlmsg.Me return nil } -func (p *Protocol) newLink(ctx context.Context, msg *nlmsg.Message, ms *nlmsg.MessageSet) *syserr.Error { - stack := inet.StackFromContext(ctx) +func (p *Protocol) newLink(ctx context.Context, s *netlink.Socket, msg *nlmsg.Message, ms *nlmsg.MessageSet) *syserr.Error { + stack := s.Stack() if stack == nil { // No network stack. return syserr.ErrProtocolNotSupported @@ -173,8 +173,8 @@ func (p *Protocol) newLink(ctx context.Context, msg *nlmsg.Message, ms *nlmsg.Me } // delLink handles RTM_DELLINK requests. -func (p *Protocol) delLink(ctx context.Context, msg *nlmsg.Message, ms *nlmsg.MessageSet) *syserr.Error { - stack := inet.StackFromContext(ctx) +func (p *Protocol) delLink(ctx context.Context, s *netlink.Socket, msg *nlmsg.Message, ms *nlmsg.MessageSet) *syserr.Error { + stack := s.Stack() if stack == nil { // No network stack. return syserr.ErrProtocolNotSupported @@ -243,7 +243,7 @@ func addNewLinkMessage(ms *nlmsg.MessageSet, idx int32, i inet.Interface) { } // dumpAddrs handles RTM_GETADDR dump requests. -func (p *Protocol) dumpAddrs(ctx context.Context, msg *nlmsg.Message, ms *nlmsg.MessageSet) *syserr.Error { +func (p *Protocol) dumpAddrs(ctx context.Context, s *netlink.Socket, msg *nlmsg.Message, ms *nlmsg.MessageSet) *syserr.Error { // RTM_GETADDR dump requests need not contain anything more than the // netlink header and 1 byte protocol family common to all // NETLINK_ROUTE requests. @@ -257,7 +257,7 @@ func (p *Protocol) dumpAddrs(ctx context.Context, msg *nlmsg.Message, ms *nlmsg. // We always send back an NLMSG_DONE. ms.Multi = true - stack := inet.StackFromContext(ctx) + stack := s.Stack() if stack == nil { // No network devices. return nil @@ -382,12 +382,12 @@ func parseForDestination(msg *nlmsg.Message) ([]byte, *syserr.Error) { } // dumpRoutes handles RTM_GETROUTE requests. -func (p *Protocol) dumpRoutes(ctx context.Context, msg *nlmsg.Message, ms *nlmsg.MessageSet) *syserr.Error { +func (p *Protocol) dumpRoutes(ctx context.Context, s *netlink.Socket, msg *nlmsg.Message, ms *nlmsg.MessageSet) *syserr.Error { // RTM_GETROUTE dump requests need not contain anything more than the // netlink header and 1 byte protocol family common to all // NETLINK_ROUTE requests. - stack := inet.StackFromContext(ctx) + stack := s.Stack() if stack == nil { // No network routes. return nil @@ -457,8 +457,8 @@ func (p *Protocol) dumpRoutes(ctx context.Context, msg *nlmsg.Message, ms *nlmsg } // newAddr handles RTM_NEWADDR requests. -func (p *Protocol) newAddr(ctx context.Context, msg *nlmsg.Message, ms *nlmsg.MessageSet) *syserr.Error { - stack := inet.StackFromContext(ctx) +func (p *Protocol) newAddr(ctx context.Context, s *netlink.Socket, msg *nlmsg.Message, ms *nlmsg.MessageSet) *syserr.Error { + stack := s.Stack() if stack == nil { // No network stack. return syserr.ErrProtocolNotSupported @@ -500,6 +500,7 @@ func (p *Protocol) newAddr(ctx context.Context, msg *nlmsg.Message, ms *nlmsg.Me } case linux.IFA_ADDRESS: default: + ctx.Warningf("Unknown attribute: %v", ahdr.Type) return syserr.ErrNotSupported } } @@ -507,8 +508,8 @@ func (p *Protocol) newAddr(ctx context.Context, msg *nlmsg.Message, ms *nlmsg.Me } // delAddr handles RTM_DELADDR requests. -func (p *Protocol) delAddr(ctx context.Context, msg *nlmsg.Message, ms *nlmsg.MessageSet) *syserr.Error { - stack := inet.StackFromContext(ctx) +func (p *Protocol) delAddr(ctx context.Context, s *netlink.Socket, msg *nlmsg.Message, ms *nlmsg.MessageSet) *syserr.Error { + stack := s.Stack() if stack == nil { // No network stack. return syserr.ErrProtocolNotSupported @@ -553,7 +554,7 @@ func (p *Protocol) delAddr(ctx context.Context, msg *nlmsg.Message, ms *nlmsg.Me } // ProcessMessage implements netlink.Protocol.ProcessMessage. -func (p *Protocol) ProcessMessage(ctx context.Context, msg *nlmsg.Message, ms *nlmsg.MessageSet) *syserr.Error { +func (p *Protocol) ProcessMessage(ctx context.Context, s *netlink.Socket, msg *nlmsg.Message, ms *nlmsg.MessageSet) *syserr.Error { hdr := msg.Header() // All messages start with a 1 byte protocol family. @@ -577,28 +578,30 @@ func (p *Protocol) ProcessMessage(ctx context.Context, msg *nlmsg.Message, ms *n // supported. switch hdr.Type { case linux.RTM_GETLINK: - return p.dumpLinks(ctx, msg, ms) + return p.dumpLinks(ctx, s, msg, ms) case linux.RTM_GETADDR: - return p.dumpAddrs(ctx, msg, ms) + return p.dumpAddrs(ctx, s, msg, ms) case linux.RTM_GETROUTE: - return p.dumpRoutes(ctx, msg, ms) + return p.dumpRoutes(ctx, s, msg, ms) default: return syserr.ErrNotSupported } } else if hdr.Flags&linux.NLM_F_REQUEST == linux.NLM_F_REQUEST { switch hdr.Type { case linux.RTM_NEWLINK: - return p.newLink(ctx, msg, ms) + return p.newLink(ctx, s, msg, ms) case linux.RTM_GETLINK: - return p.getLink(ctx, msg, ms) + return p.getLink(ctx, s, msg, ms) case linux.RTM_DELLINK: - return p.delLink(ctx, msg, ms) + return p.delLink(ctx, s, msg, ms) case linux.RTM_GETROUTE: - return p.dumpRoutes(ctx, msg, ms) + return p.dumpRoutes(ctx, s, msg, ms) case linux.RTM_NEWADDR: - return p.newAddr(ctx, msg, ms) + return p.newAddr(ctx, s, msg, ms) case linux.RTM_DELADDR: - return p.delAddr(ctx, msg, ms) + return p.delAddr(ctx, s, msg, ms) + case linux.RTM_SETLINK: + return nil default: return syserr.ErrNotSupported } diff --git a/pkg/sentry/socket/netlink/socket.go b/pkg/sentry/socket/netlink/socket.go index aa7c9f4bf..e84ca545e 100644 --- a/pkg/sentry/socket/netlink/socket.go +++ b/pkg/sentry/socket/netlink/socket.go @@ -28,6 +28,7 @@ import ( "gvisor.dev/gvisor/pkg/marshal" "gvisor.dev/gvisor/pkg/marshal/primitive" "gvisor.dev/gvisor/pkg/sentry/arch" + "gvisor.dev/gvisor/pkg/sentry/inet" "gvisor.dev/gvisor/pkg/sentry/kernel" "gvisor.dev/gvisor/pkg/sentry/kernel/auth" ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time" @@ -109,6 +110,9 @@ type Socket struct { // TODO(gvisor.dev/issue/1119): We don't actually support filtering, // this is just bookkeeping for tracking add/remove. filter bool + + // netns is the network namespace associated with the socket. + netns *inet.Namespace } var _ socket.Socket = (*Socket)(nil) @@ -140,11 +144,17 @@ func New(t *kernel.Task, skType linux.SockType, protocol Protocol) (*Socket, *sy ep: ep, connection: connection, sendBufferSize: defaultSendBufferSize, + netns: t.GetNetworkNamespace(), } fd.LockFD.Init(&vfs.FileLocks{}) return fd, nil } +// Stack returns the network stack associated with the socket. +func (s *Socket) Stack() inet.Stack { + return s.netns.Stack() +} + // Release implements vfs.FileDescriptionImpl.Release. func (s *Socket) Release(ctx context.Context) { t := kernel.TaskFromContext(ctx) @@ -155,6 +165,7 @@ func (s *Socket) Release(ctx context.Context) { if s.bound { s.ports.Release(s.protocol.Protocol(), s.portID) } + s.netns.DecRef(ctx) } // Epollable implements FileDescriptionImpl.Epollable. @@ -744,7 +755,7 @@ func (s *Socket) processMessages(ctx context.Context, buf []byte) *syserr.Error } ms := nlmsg.NewMessageSet(s.portID, hdr.Seq) - if err := s.protocol.ProcessMessage(ctx, msg, ms); err != nil { + if err := s.protocol.ProcessMessage(ctx, s, msg, ms); err != nil { dumpErrorMessage(hdr, ms, err) } else if hdr.Flags&linux.NLM_F_ACK == linux.NLM_F_ACK { dumpAckMessage(hdr, ms) diff --git a/pkg/sentry/socket/netlink/uevent/protocol.go b/pkg/sentry/socket/netlink/uevent/protocol.go index 07780ca98..1f0ad2ed7 100644 --- a/pkg/sentry/socket/netlink/uevent/protocol.go +++ b/pkg/sentry/socket/netlink/uevent/protocol.go @@ -50,7 +50,7 @@ func (p *Protocol) CanSend() bool { } // ProcessMessage implements netlink.Protocol.ProcessMessage. -func (p *Protocol) ProcessMessage(ctx context.Context, msg *nlmsg.Message, ms *nlmsg.MessageSet) *syserr.Error { +func (p *Protocol) ProcessMessage(ctx context.Context, s *netlink.Socket, msg *nlmsg.Message, ms *nlmsg.MessageSet) *syserr.Error { // Silently ignore all messages. return nil } diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD index b65a58431..8fb158955 100644 --- a/test/syscalls/linux/BUILD +++ b/test/syscalls/linux/BUILD @@ -2742,8 +2742,11 @@ cc_library( ], deps = select_gtest() + [ ":socket_netlink_route_util", + ":socket_netlink_util", "//test/util:capability_util", "//test/util:cleanup", + "//test/util:file_descriptor", + "//test/util:posix_error", "//test/util:socket_util", ], alwayslink = 1, @@ -2760,7 +2763,10 @@ cc_library( ], deps = select_gtest() + [ ":socket_netlink_route_util", + ":socket_netlink_util", "//test/util:capability_util", + "//test/util:file_descriptor", + "//test/util:posix_error", "//test/util:socket_util", ], alwayslink = 1, @@ -3247,7 +3253,10 @@ cc_binary( deps = select_gtest() + [ ":ip_socket_test_util", ":socket_netlink_route_util", + ":socket_netlink_util", "//test/util:capability_util", + "//test/util:file_descriptor", + "//test/util:posix_error", "//test/util:socket_util", "//test/util:test_main", "//test/util:test_util", @@ -4094,6 +4103,7 @@ cc_binary( malloc = "//test/util:errno_safe_allocator", deps = select_gtest() + [ ":socket_netlink_route_util", + ":socket_netlink_util", "//test/util:capability_util", "//test/util:file_descriptor", "//test/util:fs_util", diff --git a/test/syscalls/linux/socket_ip_unbound_netlink.cc b/test/syscalls/linux/socket_ip_unbound_netlink.cc index 803a3b30b..7e8177d35 100644 --- a/test/syscalls/linux/socket_ip_unbound_netlink.cc +++ b/test/syscalls/linux/socket_ip_unbound_netlink.cc @@ -20,12 +20,16 @@ #include #include +#include #include "gmock/gmock.h" #include "gtest/gtest.h" #include "test/syscalls/linux/ip_socket_test_util.h" #include "test/syscalls/linux/socket_netlink_route_util.h" +#include "test/syscalls/linux/socket_netlink_util.h" #include "test/util/capability_util.h" +#include "test/util/file_descriptor.h" +#include "test/util/posix_error.h" #include "test/util/socket_util.h" #include "test/util/test_util.h" @@ -42,14 +46,17 @@ TEST_P(IPv6UnboundSocketTest, ConnectToBadLocalAddress) { // across save/restore. DisableSave ds; + FileDescriptor nlsk = + ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); + // Delete the loopback address from the loopback interface. Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink()); - EXPECT_NO_ERRNO(LinkDelLocalAddr(loopback_link.index, AF_INET6, + EXPECT_NO_ERRNO(LinkDelLocalAddr(nlsk, loopback_link.index, AF_INET6, /*prefixlen=*/128, &in6addr_loopback, sizeof(in6addr_loopback))); Cleanup defer_addr_removal = - Cleanup([loopback_link = std::move(loopback_link)] { - EXPECT_NO_ERRNO(LinkAddLocalAddr(loopback_link.index, AF_INET6, + Cleanup([loopback_link = std::move(loopback_link), &nlsk] { + EXPECT_NO_ERRNO(LinkAddLocalAddr(nlsk, loopback_link.index, AF_INET6, /*prefixlen=*/128, &in6addr_loopback, sizeof(in6addr_loopback))); }); @@ -75,15 +82,18 @@ TEST_P(IPv4UnboundSocketTest, ConnectToBadLocalAddress) { // across save/restore. DisableSave ds; + FileDescriptor nlsk = + ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); // Delete the loopback address from the loopback interface. Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink()); struct in_addr laddr; laddr.s_addr = htonl(INADDR_LOOPBACK); - EXPECT_NO_ERRNO(LinkDelLocalAddr(loopback_link.index, AF_INET, + EXPECT_NO_ERRNO(LinkDelLocalAddr(nlsk, loopback_link.index, AF_INET, /*prefixlen=*/8, &laddr, sizeof(laddr))); - Cleanup defer_addr_removal = Cleanup( - [loopback_link = std::move(loopback_link), addr = std::move(laddr)] { - EXPECT_NO_ERRNO(LinkAddLocalAddr(loopback_link.index, AF_INET, + Cleanup defer_addr_removal = + Cleanup([loopback_link = std::move(loopback_link), + addr = std::move(laddr), &nlsk] { + EXPECT_NO_ERRNO(LinkAddLocalAddr(nlsk, loopback_link.index, AF_INET, /*prefixlen=*/8, &addr, sizeof(addr))); }); TestAddress addr = V4Loopback(); diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_netlink.cc b/test/syscalls/linux/socket_ipv4_udp_unbound_netlink.cc index 020ce5d6e..20994edfe 100644 --- a/test/syscalls/linux/socket_ipv4_udp_unbound_netlink.cc +++ b/test/syscalls/linux/socket_ipv4_udp_unbound_netlink.cc @@ -17,10 +17,15 @@ #include #include +#include + #include "gtest/gtest.h" #include "test/syscalls/linux/socket_netlink_route_util.h" +#include "test/syscalls/linux/socket_netlink_util.h" #include "test/util/capability_util.h" #include "test/util/cleanup.h" +#include "test/util/file_descriptor.h" +#include "test/util/posix_error.h" namespace gvisor { namespace testing { @@ -31,19 +36,21 @@ constexpr size_t kSendBufSize = 200; // associated subnet. TEST_P(IPv4UDPUnboundSocketNetlinkTest, JoinSubnet) { SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); + FileDescriptor nlsk = + ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); // Add an IP address to the loopback interface. Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink()); struct in_addr addr; ASSERT_EQ(1, inet_pton(AF_INET, "192.0.2.1", &addr)); - ASSERT_NO_ERRNO(LinkAddLocalAddr(loopback_link.index, AF_INET, + ASSERT_NO_ERRNO(LinkAddLocalAddr(nlsk, loopback_link.index, AF_INET, /*prefixlen=*/24, &addr, sizeof(addr))); - Cleanup defer_addr_removal = Cleanup( - [loopback_link = std::move(loopback_link), addr = std::move(addr)] { - EXPECT_NO_ERRNO(LinkDelLocalAddr(loopback_link.index, AF_INET, - /*prefixlen=*/24, &addr, - sizeof(addr))); - }); + Cleanup defer_addr_removal = Cleanup([loopback_link = + std::move(loopback_link), + addr = std::move(addr), &nlsk] { + EXPECT_NO_ERRNO(LinkDelLocalAddr(nlsk, loopback_link.index, AF_INET, + /*prefixlen=*/24, &addr, sizeof(addr))); + }); auto snd_sock = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); auto rcv_sock = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); @@ -103,19 +110,21 @@ TEST_P(IPv4UDPUnboundSocketNetlinkTest, ReuseAddrSubnetDirectedBroadcast) { constexpr int kNumSocketsPerType = 2; SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); + FileDescriptor nlsk = + ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); // Add an IP address to the loopback interface. Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink()); struct in_addr addr; ASSERT_EQ(1, inet_pton(AF_INET, "192.0.2.1", &addr)); - ASSERT_NO_ERRNO(LinkAddLocalAddr(loopback_link.index, AF_INET, + ASSERT_NO_ERRNO(LinkAddLocalAddr(nlsk, loopback_link.index, AF_INET, 24 /* prefixlen */, &addr, sizeof(addr))); - Cleanup defer_addr_removal = Cleanup( - [loopback_link = std::move(loopback_link), addr = std::move(addr)] { - EXPECT_NO_ERRNO(LinkDelLocalAddr(loopback_link.index, AF_INET, - /*prefixlen=*/24, &addr, - sizeof(addr))); - }); + Cleanup defer_addr_removal = Cleanup([loopback_link = + std::move(loopback_link), + addr = std::move(addr), &nlsk] { + EXPECT_NO_ERRNO(LinkDelLocalAddr(nlsk, loopback_link.index, AF_INET, + /*prefixlen=*/24, &addr, sizeof(addr))); + }); TestAddress broadcast_address("SubnetBroadcastAddress"); broadcast_address.addr.ss_family = AF_INET; diff --git a/test/syscalls/linux/socket_ipv6_udp_unbound_netlink.cc b/test/syscalls/linux/socket_ipv6_udp_unbound_netlink.cc index 48aace78a..79abddac6 100644 --- a/test/syscalls/linux/socket_ipv6_udp_unbound_netlink.cc +++ b/test/syscalls/linux/socket_ipv6_udp_unbound_netlink.cc @@ -18,7 +18,10 @@ #include "gtest/gtest.h" #include "test/syscalls/linux/socket_netlink_route_util.h" +#include "test/syscalls/linux/socket_netlink_util.h" #include "test/util/capability_util.h" +#include "test/util/file_descriptor.h" +#include "test/util/posix_error.h" namespace gvisor { namespace testing { @@ -27,12 +30,14 @@ namespace testing { // in an associated subnet. TEST_P(IPv6UDPUnboundSocketNetlinkTest, JoinSubnet) { SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); + FileDescriptor nlsk = + ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); // Add an IP address to the loopback interface. Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink()); struct in6_addr addr; EXPECT_EQ(1, inet_pton(AF_INET6, "2001:db8::1", &addr)); - EXPECT_NO_ERRNO(LinkAddLocalAddr(loopback_link.index, AF_INET6, + EXPECT_NO_ERRNO(LinkAddLocalAddr(nlsk, loopback_link.index, AF_INET6, /*prefixlen=*/64, &addr, sizeof(addr))); // Binding to an unassigned address but an address that is in the subnet diff --git a/test/syscalls/linux/socket_netlink_route.cc b/test/syscalls/linux/socket_netlink_route.cc index 044c05bb0..337249fd4 100644 --- a/test/syscalls/linux/socket_netlink_route.cc +++ b/test/syscalls/linux/socket_netlink_route.cc @@ -657,6 +657,8 @@ TEST(NetlinkRouteTest, AddAndRemoveAddr) { // Don't do cooperative save/restore because netstack state is not restored. // TODO(gvisor.dev/issue/4595): enable cooperative save tests. const DisableSave ds; + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink()); @@ -664,32 +666,67 @@ TEST(NetlinkRouteTest, AddAndRemoveAddr) { ASSERT_EQ(inet_pton(AF_INET, "10.0.0.1", &addr), 1); // Create should succeed, as no such address in kernel. - ASSERT_NO_ERRNO(LinkAddLocalAddr(loopback_link.index, AF_INET, + ASSERT_NO_ERRNO(LinkAddLocalAddr(fd, loopback_link.index, AF_INET, /*prefixlen=*/24, &addr, sizeof(addr))); - Cleanup defer_addr_removal = Cleanup( - [loopback_link = std::move(loopback_link), addr = std::move(addr)] { - // First delete should succeed, as address exists. - EXPECT_NO_ERRNO(LinkDelLocalAddr(loopback_link.index, AF_INET, - /*prefixlen=*/24, &addr, - sizeof(addr))); + Cleanup defer_addr_removal = Cleanup([&] { + // First delete should succeed, as address exists. + EXPECT_NO_ERRNO(LinkDelLocalAddr(fd, loopback_link.index, AF_INET, + /*prefixlen=*/24, &addr, sizeof(addr))); - // Second delete should fail, as address no longer exists. - EXPECT_THAT(LinkDelLocalAddr(loopback_link.index, AF_INET, - /*prefixlen=*/24, &addr, sizeof(addr)), - PosixErrorIs(EADDRNOTAVAIL, _)); - }); + // Second delete should fail, as address no longer exists. + EXPECT_THAT(LinkDelLocalAddr(fd, loopback_link.index, AF_INET, + /*prefixlen=*/24, &addr, sizeof(addr)), + PosixErrorIs(EADDRNOTAVAIL, _)); + }); // Replace an existing address should succeed. - ASSERT_NO_ERRNO(LinkReplaceLocalAddr(loopback_link.index, AF_INET, + ASSERT_NO_ERRNO(LinkReplaceLocalAddr(fd, loopback_link.index, AF_INET, /*prefixlen=*/24, &addr, sizeof(addr))); // Create exclusive should fail, as we created the address above. - EXPECT_THAT(LinkAddExclusiveLocalAddr(loopback_link.index, AF_INET, + EXPECT_THAT(LinkAddExclusiveLocalAddr(fd, loopback_link.index, AF_INET, /*prefixlen=*/24, &addr, sizeof(addr)), PosixErrorIs(EEXIST, _)); } +TEST(NetlinkRouteTest, LinkedToNetns) { + SKIP_IF(IsRunningWithHostinet()); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); + // Don't do cooperative save/restore because netstack state is not restored. + // TODO(gvisor.dev/issue/4595): enable cooperative save tests. + const DisableSave ds; + + FileDescriptor root_netns_nlsk = + ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); + + const FileDescriptor nsfd = + ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/thread-self/ns/net", O_RDONLY)); + Cleanup defer_netns = Cleanup([&] { + ASSERT_THAT(setns(nsfd.get(), CLONE_NEWNET), SyscallSucceedsWithValue(0)); + }); + ASSERT_THAT(unshare(CLONE_NEWNET), SyscallSucceedsWithValue(0)); + FileDescriptor nlsk = + ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); + + Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink()); + + struct in_addr addr; + ASSERT_EQ(inet_pton(AF_INET, "10.0.0.1", &addr), 1); + + // Create should succeed, as no such address in kernel. + ASSERT_NO_ERRNO(LinkAddLocalAddr(nlsk, loopback_link.index, AF_INET, + /*prefixlen=*/24, &addr, sizeof(addr))); + + // No such address in the root network namespace. + EXPECT_THAT(LinkDelLocalAddr(root_netns_nlsk, loopback_link.index, AF_INET, + /*prefixlen=*/24, &addr, sizeof(addr)), + PosixErrorIs(EADDRNOTAVAIL, _)); + // The address exists in the current namespace. + EXPECT_NO_ERRNO(LinkDelLocalAddr(nlsk, loopback_link.index, AF_INET, + /*prefixlen=*/24, &addr, sizeof(addr))); +} + // GetRouteDump tests a RTM_GETROUTE + NLM_F_DUMP request. TEST(NetlinkRouteTest, GetRouteDump) { FileDescriptor fd = diff --git a/test/syscalls/linux/socket_netlink_route_util.cc b/test/syscalls/linux/socket_netlink_route_util.cc index 6c1dc7cbc..13fd7d789 100644 --- a/test/syscalls/linux/socket_netlink_route_util.cc +++ b/test/syscalls/linux/socket_netlink_route_util.cc @@ -118,11 +118,9 @@ PosixError PopulateRuleNlmsghdr(NetlinkModification modification, } // Adds or removes the specified address from the specified interface. -PosixError LinkModifyLocalAddr(int index, int family, int prefixlen, - const void* addr, int addrlen, +PosixError LinkModifyLocalAddr(FileDescriptor& fd, int index, int family, + int prefixlen, const void* addr, int addrlen, NetlinkModification modification) { - ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd, NetlinkBoundSocket(NETLINK_ROUTE)); - struct request { struct nlmsghdr hdr; struct ifaddrmsg ifaddr; @@ -297,27 +295,28 @@ PosixErrorOr LoopbackLink() { return PosixError(ENOENT, "loopback link not found"); } -PosixError LinkAddLocalAddr(int index, int family, int prefixlen, - const void* addr, int addrlen) { - return LinkModifyLocalAddr(index, family, prefixlen, addr, addrlen, +PosixError LinkAddLocalAddr(FileDescriptor& fd, int index, int family, + int prefixlen, const void* addr, int addrlen) { + return LinkModifyLocalAddr(fd, index, family, prefixlen, addr, addrlen, NetlinkModification::kAdd); } -PosixError LinkAddExclusiveLocalAddr(int index, int family, int prefixlen, - const void* addr, int addrlen) { - return LinkModifyLocalAddr(index, family, prefixlen, addr, addrlen, +PosixError LinkAddExclusiveLocalAddr(FileDescriptor& fd, int index, int family, + int prefixlen, const void* addr, + int addrlen) { + return LinkModifyLocalAddr(fd, index, family, prefixlen, addr, addrlen, NetlinkModification::kAddExclusive); } -PosixError LinkReplaceLocalAddr(int index, int family, int prefixlen, - const void* addr, int addrlen) { - return LinkModifyLocalAddr(index, family, prefixlen, addr, addrlen, +PosixError LinkReplaceLocalAddr(FileDescriptor& fd, int index, int family, + int prefixlen, const void* addr, int addrlen) { + return LinkModifyLocalAddr(fd, index, family, prefixlen, addr, addrlen, NetlinkModification::kReplace); } -PosixError LinkDelLocalAddr(int index, int family, int prefixlen, - const void* addr, int addrlen) { - return LinkModifyLocalAddr(index, family, prefixlen, addr, addrlen, +PosixError LinkDelLocalAddr(FileDescriptor& fd, int index, int family, + int prefixlen, const void* addr, int addrlen) { + return LinkModifyLocalAddr(fd, index, family, prefixlen, addr, addrlen, NetlinkModification::kDelete); } diff --git a/test/syscalls/linux/socket_netlink_route_util.h b/test/syscalls/linux/socket_netlink_route_util.h index 78ba6484b..2c4d6071f 100644 --- a/test/syscalls/linux/socket_netlink_route_util.h +++ b/test/syscalls/linux/socket_netlink_route_util.h @@ -40,21 +40,22 @@ PosixErrorOr> DumpLinks(); PosixErrorOr LoopbackLink(); // LinkAddLocalAddr adds a new IFA_LOCAL address to the interface. -PosixError LinkAddLocalAddr(int index, int family, int prefixlen, - const void* addr, int addrlen); +PosixError LinkAddLocalAddr(FileDescriptor& fd, int index, int family, + int prefixlen, const void* addr, int addrlen); // LinkAddExclusiveLocalAddr adds a new IFA_LOCAL address with NLM_F_EXCL flag // to the interface. -PosixError LinkAddExclusiveLocalAddr(int index, int family, int prefixlen, - const void* addr, int addrlen); +PosixError LinkAddExclusiveLocalAddr(FileDescriptor& fd, int index, int family, + int prefixlen, const void* addr, + int addrlen); // LinkReplaceLocalAddr replaces an IFA_LOCAL address on the interface. -PosixError LinkReplaceLocalAddr(int index, int family, int prefixlen, - const void* addr, int addrlen); +PosixError LinkReplaceLocalAddr(FileDescriptor& fd, int index, int family, + int prefixlen, const void* addr, int addrlen); // LinkDelLocalAddr removes IFA_LOCAL attribute on the interface. -PosixError LinkDelLocalAddr(int index, int family, int prefixlen, - const void* addr, int addrlen); +PosixError LinkDelLocalAddr(FileDescriptor& fd, int index, int family, + int prefixlen, const void* addr, int addrlen); // LinkChangeFlags changes interface flags. E.g. IFF_UP. PosixError LinkChangeFlags(int index, unsigned int flags, unsigned int change); diff --git a/test/syscalls/linux/tuntap.cc b/test/syscalls/linux/tuntap.cc index 169559ac2..3ab8aa581 100644 --- a/test/syscalls/linux/tuntap.cc +++ b/test/syscalls/linux/tuntap.cc @@ -33,6 +33,7 @@ #include "absl/strings/ascii.h" #include "absl/strings/str_split.h" #include "test/syscalls/linux/socket_netlink_route_util.h" +#include "test/syscalls/linux/socket_netlink_util.h" #include "test/util/capability_util.h" #include "test/util/file_descriptor.h" #include "test/util/fs_util.h" @@ -328,9 +329,10 @@ PosixErrorOr OpenAndAttachTunTap(const std::string& dev_name, ASSIGN_OR_RETURN_ERRNO(auto link, GetLinkByName(dev_name)); + ASSIGN_OR_RETURN_ERRNO(auto nlsk, NetlinkBoundSocket(NETLINK_ROUTE)); const struct in_addr dev_ipv4_addr = {.s_addr = dev_addr}; // Interface setup. - EXPECT_NO_ERRNO(LinkAddLocalAddr(link.index, AF_INET, /*prefixlen=*/24, + EXPECT_NO_ERRNO(LinkAddLocalAddr(nlsk, link.index, AF_INET, /*prefixlen=*/24, &dev_ipv4_addr, sizeof(dev_ipv4_addr))); if (!IsRunningOnGvisor()) { @@ -501,8 +503,10 @@ TEST_F(TuntapTest, TUNNoPacketInfo) { // Interface setup. auto link = ASSERT_NO_ERRNO_AND_VALUE(GetLinkByName(kTunName)); const struct in_addr dev_ipv4_addr = {.s_addr = kTapIPAddr}; - EXPECT_NO_ERRNO(LinkAddLocalAddr(link.index, AF_INET, 24, &dev_ipv4_addr, - sizeof(dev_ipv4_addr))); + FileDescriptor nlsk = + ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); + EXPECT_NO_ERRNO(LinkAddLocalAddr(nlsk, link.index, AF_INET, 24, + &dev_ipv4_addr, sizeof(dev_ipv4_addr))); ping_ip_pkt ping_req = CreatePingIPPacket(kTapPeerIPAddr, kTapIPAddr);