netlink: socket controls a network namespace where it has been created

Each socket has to hold a reference on its network namespace.

PiperOrigin-RevId: 640691038
This commit is contained in:
Andrei Vagin
2024-06-05 16:46:01 -07:00
committed by gVisor bot
parent e8ccc27cdf
commit 01142fe599
13 changed files with 182 additions and 92 deletions
+1
View File
@@ -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",
+1 -1
View File
@@ -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
+29 -26
View File
@@ -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
}
+12 -1
View File
@@ -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)
+1 -1
View File
@@ -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
}
+10
View File
@@ -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",
@@ -20,12 +20,16 @@
#include <cstdio>
#include <cstring>
#include <utility>
#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();
@@ -17,10 +17,15 @@
#include <arpa/inet.h>
#include <poll.h>
#include <utility>
#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;
@@ -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
+51 -14
View File
@@ -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 =
@@ -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<Link> 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);
}
@@ -40,21 +40,22 @@ PosixErrorOr<std::vector<Link>> DumpLinks();
PosixErrorOr<Link> 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);
+7 -3
View File
@@ -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<TunTapInterface> 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);