Skip to content

Commit

Permalink
Implement IFLA_MTU which changes a link's MTU.
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 648618069
  • Loading branch information
milantracy authored and gvisor-bot committed Jul 2, 2024
1 parent 5d5ad19 commit 6dd4ef4
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 11 deletions.
10 changes: 9 additions & 1 deletion pkg/sentry/socket/netstack/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ func (s *Stack) SetInterface(ctx context.Context, msg *nlmsg.Message) *syserr.Er
case linux.IFLA_MASTER:
case linux.IFLA_LINKINFO:
case linux.IFLA_ADDRESS:
case linux.IFLA_MTU:
default:
ctx.Warningf("unexpected attribute: %x", attr)
return syserr.ErrNotSupported
Expand All @@ -151,7 +152,6 @@ func (s *Stack) SetInterface(ctx context.Context, msg *nlmsg.Message) *syserr.Er
if flags&(linux.NLM_F_EXCL|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)
Expand Down Expand Up @@ -192,6 +192,14 @@ func (s *Stack) setLink(id tcpip.NICID, linkAttrs map[uint16]nlmsg.BytesView) *s
if err := s.Stack.SetNICName(id, v.String()); err != nil {
return syserr.TranslateNetstackError(err)
}
case linux.IFLA_MTU:
mtu, ok := v.Uint32()
if !ok {
return syserr.ErrInvalidArgument
}
if err := s.Stack.SetNICMTU(id, mtu); err != nil {
return syserr.TranslateNetstackError(err)
}
}
}
return nil
Expand Down
25 changes: 19 additions & 6 deletions pkg/tcpip/link/loopback/loopback.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,25 @@ import (
"gvisor.dev/gvisor/pkg/tcpip/stack"
)

const (
loopbackMTU = 65536
)

// +stateify savable
type endpoint struct {
mu sync.RWMutex `state:"nosave"`
// +checklocks:mu
dispatcher stack.NetworkDispatcher
// +checklocks:mu
mtu uint32
}

// New creates a new loopback endpoint. This link-layer endpoint just turns
// outbound packets into inbound packets.
func New() stack.LinkEndpoint {
return &endpoint{}
return &endpoint{
mtu: loopbackMTU,
}
}

// Attach implements stack.LinkEndpoint.Attach. It just saves the stack network-
Expand All @@ -56,14 +64,19 @@ func (e *endpoint) IsAttached() bool {
return e.dispatcher != nil
}

// MTU implements stack.LinkEndpoint.MTU. It returns a constant that matches the
// linux loopback interface.
func (*endpoint) MTU() uint32 {
return 65536
// MTU implements stack.LinkEndpoint.MTU.
func (e *endpoint) MTU() uint32 {
e.mu.RLock()
defer e.mu.RUnlock()
return e.mtu
}

// SetMTU implements stack.LinkEndpoint.SetMTU. It has no impact.
func (*endpoint) SetMTU(uint32) {}
func (e *endpoint) SetMTU(mtu uint32) {
e.mu.Lock()
defer e.mu.Unlock()
e.mtu = mtu
}

// Capabilities implements stack.LinkEndpoint.Capabilities. Loopback advertises
// itself as supporting checksum offload, but in reality it's just omitted.
Expand Down
13 changes: 13 additions & 0 deletions pkg/tcpip/stack/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -1070,6 +1070,19 @@ func (s *Stack) SetNICName(id tcpip.NICID, name string) tcpip.Error {
return nil
}

// SetNICMTU sets a NIC's MTU.
func (s *Stack) SetNICMTU(id tcpip.NICID, mtu uint32) tcpip.Error {
s.mu.Lock()
defer s.mu.Unlock()

nic, ok := s.nics[id]
if !ok {
return &tcpip.ErrUnknownNICID{}
}
nic.NetworkLinkEndpoint.SetMTU(mtu)
return nil
}

// NICInfo captures the name and addresses assigned to a NIC.
type NICInfo struct {
Name string
Expand Down
9 changes: 9 additions & 0 deletions test/rtnetlink/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,12 @@ syscall_test(
test = "//test/rtnetlink/linux:bridge_test",
use_tmpfs = True,
)

syscall_test(
size = "small",
container = True,
overlay = True,
save = False,
test = "//test/rtnetlink/linux:setlink_test",
use_tmpfs = True,
)
6 changes: 6 additions & 0 deletions test/rtnetlink/linux/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ sh_binary(
deps = [":rtnetlink_test"],
)

sh_binary(
name = "setlink_test",
srcs = ["setlink_test.sh"],
deps = [":rtnetlink_test"],
)

sh_binary(
name = "bridge_test",
srcs = ["bridge_test.sh"],
Expand Down
30 changes: 30 additions & 0 deletions test/rtnetlink/linux/setlink_test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/bin/bash

# Copyright 2024 The gVisor Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

set -xeo pipefail
source "$(dirname "$0")/rtnetlink_test.sh"

# Create a new veth pair in the current namespace and change the MTU.
ip link add name test_veth01 type veth peer name test_veth02
ip link set test_veth01 mtu 3000
ip link show test_veth01 | grep -E "mtu (2986|3000)"
ip link del name test_veth01
# Check that test_veth02 has been destroyed.
if ! wait_for ! ip link show test_veth02; then
fail "test_veth02 hasn't been destroyed"
exit 1
fi

61 changes: 57 additions & 4 deletions test/syscalls/linux/socket_netlink_route.cc
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,8 @@ void CheckLinkMsg(const struct nlmsghdr* hdr, const Link& link) {
const struct rtattr* rta_mtu = FindRtAttr(hdr, msg, IFLA_MTU);
EXPECT_NE(nullptr, rta_mtu) << "IFLA_MTU not found in message.";
if (rta_mtu != nullptr) {
const auto mtu = *(reinterpret_cast<uint32_t*>(RTA_DATA(rta_mtu)));
const auto mtu = *(uint32_t*)(RTA_DATA(rta_mtu));
std::cout << "mtu: " << mtu << "link.mtu " << link.mtu << std::endl;
EXPECT_EQ(mtu, link.mtu);
}
}
Expand Down Expand Up @@ -264,6 +265,61 @@ TEST(NetlinkRouteTest, ChangeLinkName) {
EXPECT_TRUE(found) << "Netlink response does not contain any links.";
}

TEST(NetlinkRouteTest, ChangeMTU) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
SKIP_IF(IsRunningWithHostinet());
SKIP_IF(!IsRunningOnGvisor());
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 rtattr rtattr;
uint32_t mtu;
} req = {};

// Change the MTU.
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.rtattr.rta_type = IFLA_MTU;
req.rtattr.rta_len = RTA_LENGTH(sizeof(uint32_t));
req.mtu = 1500;
ASSERT_NE(req.mtu, loopback_link.mtu);
EXPECT_NO_ERRNO(NetlinkRequestAckOrError(fd, kSeq, &req, sizeof(req)));

// See b/348220986, this is a known issue. The interface MTU is slightly
// different because of the package header size.
loopback_link.mtu = 1486;
// Verify the new MTU.
struct searchrequest {
struct nlmsghdr hdr;
struct ifinfomsg ifm;
} search = {};
search.hdr.nlmsg_len = sizeof(search);
search.hdr.nlmsg_type = RTM_GETLINK;
search.hdr.nlmsg_flags = NLM_F_REQUEST;
search.hdr.nlmsg_seq = kSeq;
search.ifm.ifi_family = AF_UNSPEC;
search.ifm.ifi_index = loopback_link.index;

bool found = false;
ASSERT_NO_ERRNO(NetlinkRequestResponse(
fd, &search, sizeof(search),
[&](const struct nlmsghdr* hdr) {
CheckLinkMsg(hdr, loopback_link);
found = true;
},
false));
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());
Expand Down Expand Up @@ -1408,8 +1464,6 @@ TEST(NetlinkRouteTest, VethAdd) {
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));

Expand Down Expand Up @@ -1457,7 +1511,6 @@ TEST(NetlinkRouteTest, VethAdd) {
linkinfo->rta_len = (uint64_t)NLMSG_TAIL(&req.hdr) - (uint64_t)linkinfo;
EXPECT_NO_ERRNO(NetlinkRequestAckOrError(fd, kSeq, &req, req.hdr.nlmsg_len));
}

} // namespace

} // namespace testing
Expand Down

0 comments on commit 6dd4ef4

Please sign in to comment.