diff --git a/pkg/sentry/devices/tundev/tundev.go b/pkg/sentry/devices/tundev/tundev.go index 176c2055a..1849fdaf8 100644 --- a/pkg/sentry/devices/tundev/tundev.go +++ b/pkg/sentry/devices/tundev/tundev.go @@ -143,6 +143,13 @@ func (fd *tunFD) Write(ctx context.Context, src usermem.IOSequence, opts vfs.Wri if src.NumBytes() == 0 { return 0, unix.EINVAL } + mtu, err := fd.device.MTU() + if err != nil { + return 0, err + } + if int64(mtu) < src.NumBytes() { + return 0, unix.EMSGSIZE + } data := make([]byte, src.NumBytes()) if _, err := src.CopyIn(ctx, data); err != nil { return 0, err diff --git a/pkg/tcpip/link/tun/device.go b/pkg/tcpip/link/tun/device.go index d6689a752..2fce32669 100644 --- a/pkg/tcpip/link/tun/device.go +++ b/pkg/tcpip/link/tun/device.go @@ -166,6 +166,20 @@ func attachOrCreateNIC(s *stack.Stack, name, prefix string, linkCaps stack.LinkE } } +// MTU returns the tun enpoint MTU (maximum transmission unit). +func (d *Device) MTU() (uint32, error) { + d.mu.RLock() + endpoint := d.endpoint + d.mu.RUnlock() + if endpoint == nil { + return 0, linuxerr.EBADFD + } + if !endpoint.IsAttached() { + return 0, linuxerr.EIO + } + return endpoint.MTU(), nil +} + // Write inject one inbound packet to the network interface. func (d *Device) Write(data []byte) (int64, error) { d.mu.RLock() diff --git a/test/syscalls/linux/tuntap.cc b/test/syscalls/linux/tuntap.cc index e2ceba366..380d2e39e 100644 --- a/test/syscalls/linux/tuntap.cc +++ b/test/syscalls/linux/tuntap.cc @@ -13,6 +13,7 @@ // limitations under the License. #include +#include #include #include #include @@ -414,6 +415,34 @@ TEST_F(TuntapTest, PingKernel) { } } +TEST_F(TuntapTest, LargeWritesFailWithEMSGSIZE) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); + + const auto& [fd, link] = ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTunTap( + kTapName, kTapIPAddr, true /* tap */, false /* no_pi */)); + + ping_pkt ping_req = + CreatePingPacket(kMacB, kTapPeerIPAddr, kMacA, kTapIPAddr); + std::string arp_rep = + CreateArpPacket(kMacB, kTapPeerIPAddr, kMacA, kTapIPAddr); + + constexpr int kBufSize = 4096; + std::vector buf(kBufSize); + struct iovec iov[2] = { + { + .iov_base = &ping_req, + .iov_len = sizeof(ping_req), + }, + { + .iov_base = buf.data(), + .iov_len = kBufSize, + }, + }; + + // A packet is large than MTU which is 1500 by default.. + EXPECT_THAT(writev(fd.get(), iov, 2), SyscallFailsWithErrno(EMSGSIZE)); +} + TEST_F(TuntapTest, SendUdpTriggersArpResolution) { SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));