netlink: allow to up/down interfaces via RTM_NEWLINK

It is required to run Docker in gVisor.
Netstack interfaces are always up, so this is no-op.

PiperOrigin-RevId: 576220175
This commit is contained in:
Andrei Vagin
2023-10-24 12:09:49 -07:00
committed by gVisor bot
parent ea4f0073d4
commit d377e45376
2 changed files with 94 additions and 0 deletions
@@ -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)
@@ -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:
@@ -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());