Skip to content

Commit

Permalink
netlink: allow to up/down interfaces via RTM_NEWLINK
Browse files Browse the repository at this point in the history
It is required to run Docker in gVisor.
Netstack interfaces are always up, so this is no-op.

PiperOrigin-RevId: 576220175
  • Loading branch information
avagin authored and gvisor-bot committed Oct 24, 2023
1 parent ea4f007 commit d377e45
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 0 deletions.
68 changes: 68 additions & 0 deletions pkg/sentry/socket/netlink/route/protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,72 @@ func (p *Protocol) getLink(ctx context.Context, msg *netlink.Message, ms *netlin
return nil
}

func (p *Protocol) newLink(ctx context.Context, msg *netlink.Message, ms *netlink.MessageSet) *syserr.Error {
stack := inet.StackFromContext(ctx)
if stack == nil {
// No network stack.
return syserr.ErrProtocolNotSupported
}

var ifinfomsg linux.InterfaceInfoMessage
attrs, ok := msg.GetData(&ifinfomsg)
if !ok {
return syserr.ErrInvalidArgument
}
for !attrs.Empty() {
// The index is unspecified, search by the interface name.
ahdr, value, rest, ok := attrs.ParseFirst()
if !ok {
return syserr.ErrInvalidArgument
}
attrs = rest
switch ahdr.Type {
case linux.IFLA_IFNAME:
if len(value) < 1 {
return syserr.ErrInvalidArgument
}
if ifinfomsg.Index != 0 {
// Device name changing isn't supported yet.
return syserr.ErrNotSupported
}
ifname := string(value[:len(value)-1])
for idx, ifa := range stack.Interfaces() {
if ifname == ifa.Name {
ifinfomsg.Index = idx
break
}
}
default:
ctx.Warningf("unexpected attribute: %x", ahdr.Type)
return syserr.ErrNotSupported
}
}
if ifinfomsg.Index == 0 {
return syserr.ErrNoDevice
}

flags := msg.Header().Flags
if flags&linux.NLM_F_EXCL != 0 {
return syserr.ErrExists
}
if flags&linux.NLM_F_REPLACE != 0 {
return syserr.ErrExists
}

if ifinfomsg.Flags != 0 || ifinfomsg.Change != 0 {
if ifinfomsg.Change & ^uint32(linux.IFF_UP) != 0 {
ctx.Warningf("Unsupported ifi_change flags: %x", ifinfomsg.Change)
return syserr.ErrInvalidArgument
}
if ifinfomsg.Flags & ^uint32(linux.IFF_UP) != 0 {
ctx.Warningf("Unsupported ifi_flags: %x", ifinfomsg.Change)
return syserr.ErrInvalidArgument
}
// Netstack interfaces are always up.
}
return nil
}

// delLink handles RTM_DELLINK requests.
func (p *Protocol) delLink(ctx context.Context, msg *netlink.Message, ms *netlink.MessageSet) *syserr.Error {
stack := inet.StackFromContext(ctx)
Expand Down Expand Up @@ -576,6 +642,8 @@ func (p *Protocol) ProcessMessage(ctx context.Context, msg *netlink.Message, ms
}
} 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)
case linux.RTM_GETLINK:
return p.getLink(ctx, msg, ms)
case linux.RTM_DELLINK:
Expand Down
26 changes: 26 additions & 0 deletions test/syscalls/linux/socket_netlink_route.cc
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,32 @@ TEST(NetlinkRouteTest, GetLinkByIndex) {
EXPECT_TRUE(found) << "Netlink response does not contain any links.";
}

TEST(NetlinkRouteTest, LinkUp) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
SKIP_IF(IsRunningWithHostinet());

Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink());

FileDescriptor fd =
ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE));

struct request {
struct nlmsghdr hdr;
struct ifinfomsg ifm;
};

struct request req = {};
req.hdr.nlmsg_len = sizeof(req);
req.hdr.nlmsg_type = RTM_NEWLINK;
req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
req.hdr.nlmsg_seq = kSeq;
req.ifm.ifi_family = AF_UNSPEC;
req.ifm.ifi_index = loopback_link.index;
req.ifm.ifi_change = IFF_UP;
req.ifm.ifi_flags = IFF_UP;
EXPECT_NO_ERRNO(NetlinkRequestAckOrError(fd, kSeq, &req, sizeof(req)));
}

TEST(NetlinkRouteTest, GetLinkByName) {
Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink());

Expand Down

0 comments on commit d377e45

Please sign in to comment.