Files
Ayush RanjanandgVisor bot 69172ebe3d Deflake //test/syscalls:packet_mmap_test_native.
This change replaces general `HaveCapability(CAP_NET_RAW)` checks with the more
specific `HavePacketSocketCapability()` function across all tests.

Similar to b6d0f8496a ("Update Have[RawIP/Packet]SocketCapability() to open
raw/packet sockets.")

PiperOrigin-RevId: 881601376
2026-03-10 13:42:11 -07:00

706 lines
26 KiB
C++

// 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.
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <linux/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <poll.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <unistd.h>
#include <cerrno>
#include <cstdint>
#include <cstring>
#include <string>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/util/capability_util.h"
#include "test/util/cleanup.h"
#include "test/util/file_descriptor.h"
#include "test/util/logging.h"
#include "test/util/posix_error.h"
#include "test/util/socket_util.h"
#include "test/util/test_util.h"
#include "test/util/thread_util.h"
namespace gvisor {
namespace testing {
namespace {
PosixErrorOr<void*> MakePacketMmapRing(int fd, const sockaddr* bind_addr,
int bind_addr_size, tpacket_req* req,
int version = TPACKET_V1) {
RETURN_ERROR_IF_SYSCALL_FAIL(
setsockopt(fd, SOL_PACKET, PACKET_VERSION, &version, sizeof(version)));
RETURN_ERROR_IF_SYSCALL_FAIL(
setsockopt(fd, SOL_PACKET, PACKET_RX_RING, req, sizeof(*req)));
RETURN_ERROR_IF_SYSCALL_FAIL(bind(fd, bind_addr, bind_addr_size));
uint32_t sz = req->tp_block_size * req->tp_block_nr;
return mmap(0, sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
}
// Tests that setting the RX ring works and fails if constraints are not met.
TEST(PacketMmapTest, SetRXRingFailsBadRequests) {
SKIP_IF(
!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability(SOCK_DGRAM, 0)));
FileDescriptor mmap_sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_PACKET, SOCK_DGRAM, 0));
// tp_block_size must be a multiple of tp_frame_size.
tpacket_req req = {
.tp_block_size = 100,
.tp_block_nr = 1,
.tp_frame_size = 64,
.tp_frame_nr = 1,
};
ASSERT_THAT(setsockopt(mmap_sock.get(), SOL_PACKET, PACKET_RX_RING, &req,
sizeof(req)),
SyscallFailsWithErrno(EINVAL));
// tp_frame_size mut be greater than TPACKET_HDR_LENGTH.
req = {
.tp_block_size = 100,
.tp_block_nr = 1,
.tp_frame_size = 10,
.tp_frame_nr = 1,
};
ASSERT_THAT(setsockopt(mmap_sock.get(), SOL_PACKET, PACKET_RX_RING, &req,
sizeof(req)),
SyscallFailsWithErrno(EINVAL));
// tp_frame_size must be a multiple of TPACKET_ALIGNMENT.
req = {
.tp_block_size = 200,
.tp_block_nr = 1,
.tp_frame_size = 100,
.tp_frame_nr = 1,
};
ASSERT_THAT(setsockopt(mmap_sock.get(), SOL_PACKET, PACKET_RX_RING, &req,
sizeof(req)),
SyscallFailsWithErrno(EINVAL));
// tp_frame_nr must be exactly frames_per_block / tp_block_nr.
req = {
.tp_block_size = 100,
.tp_block_nr = 1,
.tp_frame_size = 100,
.tp_frame_nr = 2,
};
ASSERT_THAT(setsockopt(mmap_sock.get(), SOL_PACKET, PACKET_RX_RING, &req,
sizeof(req)),
SyscallFailsWithErrno(EINVAL));
// tp_block_nr must be at least 1.
req = {
.tp_block_size = 100,
.tp_block_nr = 0,
.tp_frame_size = 100,
.tp_frame_nr = 1,
};
ASSERT_THAT(setsockopt(mmap_sock.get(), SOL_PACKET, PACKET_RX_RING, &req,
sizeof(req)),
SyscallFailsWithErrno(EINVAL));
}
TEST(PacketMmapTest, Basic) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(
HavePacketSocketCapability(SOCK_DGRAM, ETH_P_IP)));
sockaddr_ll bind_addr = {
.sll_family = AF_PACKET,
.sll_protocol = htons(ETH_P_IP),
.sll_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex()),
.sll_halen = ETH_ALEN,
};
FileDescriptor mmap_sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_PACKET, SOCK_DGRAM, 0));
uint32_t tp_frame_size = 65536 + 128;
uint32_t tp_block_size = tp_frame_size * 32;
uint32_t tp_block_nr = 2;
uint32_t tp_frame_nr = (tp_block_size * tp_block_nr) / tp_frame_size;
tpacket_req req = {
.tp_block_size = tp_block_size,
.tp_block_nr = tp_block_nr,
.tp_frame_size = tp_frame_size,
.tp_frame_nr = tp_frame_nr,
};
void* ring = ASSERT_NO_ERRNO_AND_VALUE(MakePacketMmapRing(
mmap_sock.get(), reinterpret_cast<const sockaddr*>(&bind_addr),
sizeof(bind_addr), &req));
auto ring_cleanup = Cleanup([ring, tp_block_size, tp_block_nr] {
ASSERT_THAT(munmap(ring, tp_block_size * tp_block_nr), SyscallSucceeds());
});
std::string kMessage = "123abc";
ASSERT_THAT(
sendto(mmap_sock.get(), kMessage.c_str(), kMessage.size(), 0 /* flags */,
reinterpret_cast<const sockaddr*>(&bind_addr), sizeof(bind_addr)),
SyscallSucceeds());
tpacket_hdr* hdr = reinterpret_cast<tpacket_hdr*>(ring);
struct pollfd pollset;
pollset.fd = mmap_sock.get();
pollset.revents = 0;
pollset.events = POLLIN | POLLRDNORM | POLLERR;
ASSERT_THAT(poll(&pollset, 1, -1), SyscallSucceeds());
EXPECT_EQ(hdr->tp_status & TP_STATUS_USER, 1);
EXPECT_EQ(hdr->tp_len, kMessage.size());
EXPECT_EQ(hdr->tp_snaplen, kMessage.size());
EXPECT_STREQ((char*)(hdr) + hdr->tp_net, kMessage.c_str());
}
TEST(PacketMmapTest, FillBlocks) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(
HavePacketSocketCapability(SOCK_DGRAM, ETH_P_IP)));
sockaddr_ll bind_addr = {
.sll_family = AF_PACKET,
.sll_protocol = htons(ETH_P_IP),
.sll_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex()),
.sll_halen = ETH_ALEN,
};
FileDescriptor mmap_sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_PACKET, SOCK_DGRAM, 0));
uint32_t tp_frame_size = 65536 + 128;
uint32_t tp_block_size = tp_frame_size * 32;
uint32_t tp_block_nr = 2;
uint32_t tp_frame_nr = (tp_block_size * tp_block_nr) / tp_frame_size;
tpacket_req req = {
.tp_block_size = tp_block_size,
.tp_block_nr = tp_block_nr,
.tp_frame_size = tp_frame_size,
.tp_frame_nr = tp_frame_nr,
};
void* ring = ASSERT_NO_ERRNO_AND_VALUE(MakePacketMmapRing(
mmap_sock.get(), reinterpret_cast<const sockaddr*>(&bind_addr),
sizeof(bind_addr), &req));
auto ring_cleanup = Cleanup([ring, tp_block_size, tp_block_nr] {
ASSERT_THAT(munmap(ring, tp_block_size * tp_block_nr), SyscallSucceeds());
});
std::string kMessage = "123abc";
for (uint32_t i = 0; i < tp_frame_nr; i++) {
ASSERT_THAT(
sendto(mmap_sock.get(), kMessage.c_str(), kMessage.size(),
0 /* flags */, reinterpret_cast<const sockaddr*>(&bind_addr),
sizeof(bind_addr)),
SyscallSucceeds());
}
// This send will wrap around to the first frame, but the ring buffer
// should still be full.
std::string kNewMessage = "HELLO!!!";
ASSERT_THAT(
sendto(mmap_sock.get(), kNewMessage.c_str(), kNewMessage.size(),
0 /* flags */, reinterpret_cast<const sockaddr*>(&bind_addr),
sizeof(bind_addr)),
SyscallSucceeds());
struct tpacket_hdr* hdr = reinterpret_cast<struct tpacket_hdr*>(ring);
struct pollfd pollset;
pollset.fd = mmap_sock.get();
pollset.revents = 0;
pollset.events = POLLIN | POLLRDNORM | POLLERR;
ASSERT_THAT(poll(&pollset, 1, -1), SyscallSucceeds());
EXPECT_EQ(hdr->tp_status & TP_STATUS_USER, 1);
// We should not see the new message. It was dropped.
EXPECT_STREQ((char*)(hdr) + hdr->tp_net, kMessage.c_str());
// Wait until the last frame has been processed.
hdr = reinterpret_cast<struct tpacket_hdr*>(
reinterpret_cast<char*>(ring) + ((tp_frame_nr - 1) * tp_frame_size));
while (!(hdr->tp_status & TP_STATUS_USER)) {
absl::SleepFor(absl::Milliseconds(100));
}
// Mark all frames as kernel owned.
for (uint32_t i = 0; i < tp_frame_nr; i++) {
hdr = reinterpret_cast<tpacket_hdr*>(
(reinterpret_cast<char*>(ring) + (i * tp_frame_size)));
ASSERT_EQ(hdr->tp_status & TP_STATUS_USER, 1);
hdr->tp_status = TP_STATUS_KERNEL;
}
ASSERT_THAT(
sendto(mmap_sock.get(), kNewMessage.data(), kNewMessage.size(),
0 /* flags */, reinterpret_cast<const sockaddr*>(&bind_addr),
sizeof(bind_addr)),
SyscallSucceeds());
hdr = reinterpret_cast<struct tpacket_hdr*>(ring);
pollset.fd = mmap_sock.get();
pollset.revents = 0;
pollset.events = POLLIN | POLLRDNORM | POLLERR;
ASSERT_THAT(poll(&pollset, 1, -1), SyscallSucceeds());
EXPECT_EQ(hdr->tp_status & TP_STATUS_USER, 1);
// We should now see the new message.
EXPECT_STREQ((char*)(hdr) + hdr->tp_net, kNewMessage.c_str());
}
TEST(PacketMmapTest, ZeroSizeRing) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(
HavePacketSocketCapability(SOCK_DGRAM, ETH_P_IP)));
sockaddr_ll bind_addr = {
.sll_family = AF_PACKET,
.sll_protocol = htons(ETH_P_IP),
.sll_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex()),
.sll_halen = ETH_ALEN,
};
FileDescriptor mmap_sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_PACKET, SOCK_DGRAM, 0));
tpacket_req req = {};
ASSERT_NO_ERRNO_AND_VALUE(MakePacketMmapRing(
mmap_sock.get(), reinterpret_cast<const sockaddr*>(&bind_addr),
sizeof(bind_addr), &req));
std::string kMessage = "123abc";
ASSERT_THAT(
sendto(mmap_sock.get(), kMessage.c_str(), kMessage.size(), 0 /* flags */,
reinterpret_cast<const sockaddr*>(&bind_addr), sizeof(bind_addr)),
SyscallSucceeds());
}
TEST(PacketMmapTest, ConcurrentReadWrite) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(
HavePacketSocketCapability(SOCK_DGRAM, ETH_P_IP)));
sockaddr_ll bind_addr = {
.sll_family = AF_PACKET,
.sll_protocol = htons(ETH_P_IP),
.sll_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex()),
.sll_halen = ETH_ALEN,
};
FileDescriptor mmap_sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_PACKET, SOCK_DGRAM, 0));
uint32_t tp_frame_size = 65536 + 128;
uint32_t tp_block_size = tp_frame_size * 32;
uint32_t tp_block_nr = 2;
uint32_t tp_frame_nr = (tp_block_size * tp_block_nr) / tp_frame_size;
tpacket_req req = {
.tp_block_size = tp_block_size,
.tp_block_nr = tp_block_nr,
.tp_frame_size = tp_frame_size,
.tp_frame_nr = tp_frame_nr,
};
void* ring = ASSERT_NO_ERRNO_AND_VALUE(MakePacketMmapRing(
mmap_sock.get(), reinterpret_cast<const sockaddr*>(&bind_addr),
sizeof(bind_addr), &req));
auto ring_cleanup = Cleanup([ring, tp_block_size, tp_block_nr] {
ASSERT_THAT(munmap(ring, tp_block_size * tp_block_nr), SyscallSucceeds());
});
const std::string kMessage = "123abc";
ScopedThread sender([&] {
for (uint32_t i = 0; i < tp_frame_nr; i++) {
ASSERT_THAT(
sendto(mmap_sock.get(), kMessage.c_str(), kMessage.size(),
0 /* flags */, reinterpret_cast<const sockaddr*>(&bind_addr),
sizeof(bind_addr)),
SyscallSucceeds());
}
});
ScopedThread receiver([&] {
struct tpacket_hdr* hdr = reinterpret_cast<struct tpacket_hdr*>(ring);
for (uint32_t i = 0; i < tp_frame_nr; i++) {
struct pollfd pollset;
pollset.fd = mmap_sock.get();
pollset.revents = 0;
pollset.events = POLLIN | POLLRDNORM | POLLERR;
ASSERT_THAT(poll(&pollset, 1, -1), SyscallSucceeds());
EXPECT_EQ(hdr->tp_status & TP_STATUS_USER, 1);
EXPECT_STREQ((char*)(hdr) + hdr->tp_net, kMessage.data());
}
});
sender.Join();
receiver.Join();
}
TEST(PacketMmapTest, RawPacket) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(
HavePacketSocketCapability(SOCK_RAW, ETH_P_ALL)));
sockaddr_ll bind_addr = {
.sll_family = AF_PACKET,
.sll_protocol = htons(ETH_P_ALL),
.sll_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex()),
.sll_halen = ETH_ALEN,
};
FileDescriptor mmap_sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)));
uint32_t tp_frame_size = 65536 + 128;
uint32_t tp_block_size = tp_frame_size * 32;
uint32_t tp_block_nr = 2;
uint32_t tp_frame_nr = (tp_block_size * tp_block_nr) / tp_frame_size;
tpacket_req req = {
.tp_block_size = tp_block_size,
.tp_block_nr = tp_block_nr,
.tp_frame_size = tp_frame_size,
.tp_frame_nr = tp_frame_nr,
};
void* ring = ASSERT_NO_ERRNO_AND_VALUE(MakePacketMmapRing(
mmap_sock.get(), reinterpret_cast<const sockaddr*>(&bind_addr),
sizeof(bind_addr), &req));
auto ring_cleanup = Cleanup([ring, tp_block_size, tp_block_nr] {
ASSERT_THAT(munmap(ring, tp_block_size * tp_block_nr), SyscallSucceeds());
});
char buffer[1024];
struct ethhdr* eth = (struct ethhdr*)buffer;
char dest_mac[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
memcpy(eth->h_dest, dest_mac, ETH_ALEN);
char src_mac[] = {0x11, 0x11, 0x11, 0x11, 0x11, 0x11};
memcpy(eth->h_source, src_mac, ETH_ALEN);
eth->h_proto = htons(ETH_P_IP);
std::string kMessage = "123abc";
memcpy(buffer + ETH_HLEN, kMessage.data(), kMessage.size());
ASSERT_THAT(
sendto(mmap_sock.get(), buffer, ETH_HLEN + kMessage.size(), 0 /* flags */,
reinterpret_cast<const sockaddr*>(&bind_addr), sizeof(bind_addr)),
SyscallSucceeds());
tpacket_hdr* hdr = reinterpret_cast<tpacket_hdr*>(ring);
struct pollfd pollset;
pollset.fd = mmap_sock.get();
pollset.revents = 0;
pollset.events = POLLIN | POLLRDNORM | POLLERR;
ASSERT_THAT(poll(&pollset, 1, -1), SyscallSucceeds());
EXPECT_EQ(hdr->tp_status & TP_STATUS_USER, 1);
EXPECT_EQ(hdr->tp_len, ETH_HLEN + kMessage.size());
EXPECT_EQ(hdr->tp_snaplen, ETH_HLEN + kMessage.size());
EXPECT_EQ(memcmp((char*)(hdr) + hdr->tp_mac, buffer, ETH_HLEN), 0);
EXPECT_STREQ((char*)(hdr) + hdr->tp_net, kMessage.c_str());
}
TEST(PacketMmapTest, SetRingAfterMmapFails) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(
HavePacketSocketCapability(SOCK_RAW, ETH_P_ALL)));
sockaddr_ll bind_addr = {
.sll_family = AF_PACKET,
.sll_protocol = htons(ETH_P_ALL),
.sll_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex()),
.sll_halen = ETH_ALEN,
};
FileDescriptor mmap_sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)));
uint32_t tp_frame_size = 65536 + 128;
uint32_t tp_block_size = tp_frame_size * 32;
uint32_t tp_block_nr = 2;
uint32_t tp_frame_nr = (tp_block_size * tp_block_nr) / tp_frame_size;
tpacket_req req = {
.tp_block_size = tp_block_size,
.tp_block_nr = tp_block_nr,
.tp_frame_size = tp_frame_size,
.tp_frame_nr = tp_frame_nr,
};
void* ring = ASSERT_NO_ERRNO_AND_VALUE(MakePacketMmapRing(
mmap_sock.get(), reinterpret_cast<const sockaddr*>(&bind_addr),
sizeof(bind_addr), &req));
auto ring_cleanup = Cleanup([ring, tp_block_size, tp_block_nr] {
ASSERT_THAT(munmap(ring, tp_block_size * tp_block_nr), SyscallSucceeds());
});
EXPECT_THAT(setsockopt(mmap_sock.get(), SOL_PACKET, PACKET_RX_RING, &req,
sizeof(req)),
SyscallFailsWithErrno(EBUSY));
}
TEST(PacketMmapTest, MmapCopy) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(
HavePacketSocketCapability(SOCK_RAW, ETH_P_ALL)));
sockaddr_ll bind_addr = {
.sll_family = AF_PACKET,
.sll_protocol = htons(ETH_P_ALL),
.sll_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex()),
.sll_halen = ETH_ALEN,
};
FileDescriptor mmap_sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)));
uint32_t tp_frame_size = 256;
uint32_t tp_block_size = tp_frame_size * 32;
uint32_t tp_block_nr = 2;
uint32_t tp_frame_nr = (tp_block_size * tp_block_nr) / tp_frame_size;
tpacket_req req = {
.tp_block_size = tp_block_size,
.tp_block_nr = tp_block_nr,
.tp_frame_size = tp_frame_size,
.tp_frame_nr = tp_frame_nr,
};
void* ring = ASSERT_NO_ERRNO_AND_VALUE(MakePacketMmapRing(
mmap_sock.get(), reinterpret_cast<const sockaddr*>(&bind_addr),
sizeof(bind_addr), &req));
auto ring_cleanup = Cleanup([ring, tp_block_size, tp_block_nr] {
ASSERT_THAT(munmap(ring, tp_block_size * tp_block_nr), SyscallSucceeds());
});
std::string kMessage = "123abc" + std::string(1000, '*');
ASSERT_THAT(
sendto(mmap_sock.get(), kMessage.c_str(), kMessage.size(), 0 /* flags */,
reinterpret_cast<const sockaddr*>(&bind_addr), sizeof(bind_addr)),
SyscallSucceeds());
// Wait for the packet to become available on both sockets.
struct pollfd pfd = {};
pfd.fd = mmap_sock.get();
pfd.revents = 0;
pfd.events = POLLIN | POLLRDNORM | POLLERR;
ASSERT_THAT(poll(&pfd, 1, -1), SyscallSucceeds());
char buf[1024];
socklen_t src_len = sizeof(kMessage);
EXPECT_THAT(recvfrom(mmap_sock.get(), buf, sizeof(buf), 0,
reinterpret_cast<sockaddr*>(&bind_addr), &src_len),
SyscallSucceedsWithValue(kMessage.size()));
tpacket_hdr* hdr = reinterpret_cast<tpacket_hdr*>(ring);
EXPECT_EQ(hdr->tp_status & (TP_STATUS_USER | TP_STATUS_COPY),
TP_STATUS_USER | TP_STATUS_COPY);
EXPECT_EQ(hdr->tp_snaplen, tp_frame_size - hdr->tp_mac);
}
TEST(PacketMmapTest, SetVersion) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(
HavePacketSocketCapability(SOCK_DGRAM, ETH_P_IP)));
FileDescriptor mmap_sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_PACKET, SOCK_DGRAM, 0));
int version = TPACKET_V2;
EXPECT_THAT(setsockopt(mmap_sock.get(), SOL_PACKET, PACKET_VERSION, &version,
sizeof(version)),
SyscallSucceeds());
version = TPACKET_V1;
EXPECT_THAT(setsockopt(mmap_sock.get(), SOL_PACKET, PACKET_VERSION, &version,
sizeof(version)),
SyscallSucceeds());
version = TPACKET_V3;
EXPECT_THAT(setsockopt(mmap_sock.get(), SOL_PACKET, PACKET_VERSION, &version,
sizeof(version)),
SyscallFailsWithErrno(EINVAL));
version = TPACKET_V1 + 100;
EXPECT_THAT(setsockopt(mmap_sock.get(), SOL_PACKET, PACKET_VERSION, &version,
sizeof(version)),
SyscallFailsWithErrno(EINVAL));
}
TEST(PacketMmapTest, BasicV2) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(
HavePacketSocketCapability(SOCK_DGRAM, ETH_P_IP)));
sockaddr_ll bind_addr = {
.sll_family = AF_PACKET,
.sll_protocol = htons(ETH_P_IP),
.sll_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex()),
.sll_halen = ETH_ALEN,
};
FileDescriptor mmap_sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_PACKET, SOCK_DGRAM, 0));
uint32_t tp_frame_size = 65536 + 128;
uint32_t tp_block_size = tp_frame_size * 32;
uint32_t tp_block_nr = 2;
uint32_t tp_frame_nr = (tp_block_size * tp_block_nr) / tp_frame_size;
tpacket_req req = {
.tp_block_size = tp_block_size,
.tp_block_nr = tp_block_nr,
.tp_frame_size = tp_frame_size,
.tp_frame_nr = tp_frame_nr,
};
void* ring = ASSERT_NO_ERRNO_AND_VALUE(MakePacketMmapRing(
mmap_sock.get(), reinterpret_cast<const sockaddr*>(&bind_addr),
sizeof(bind_addr), &req, TPACKET_V2));
auto ring_cleanup = Cleanup([ring, tp_block_size, tp_block_nr] {
ASSERT_THAT(munmap(ring, tp_block_size * tp_block_nr), SyscallSucceeds());
});
std::string kMessage = "123abc";
ASSERT_THAT(
sendto(mmap_sock.get(), kMessage.c_str(), kMessage.size(), 0 /* flags */,
reinterpret_cast<const sockaddr*>(&bind_addr), sizeof(bind_addr)),
SyscallSucceeds());
tpacket2_hdr* hdr = reinterpret_cast<tpacket2_hdr*>(ring);
struct pollfd pollset;
pollset.fd = mmap_sock.get();
pollset.revents = 0;
pollset.events = POLLIN | POLLRDNORM | POLLERR;
ASSERT_THAT(poll(&pollset, 1, -1), SyscallSucceeds());
EXPECT_EQ(hdr->tp_status & TP_STATUS_USER, 1);
EXPECT_EQ(hdr->tp_len, kMessage.size());
EXPECT_EQ(hdr->tp_snaplen, kMessage.size());
EXPECT_STREQ((char*)(hdr) + hdr->tp_net, kMessage.c_str());
}
TEST(PacketMMmapTest, GetPacketHdrLen) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(
HavePacketSocketCapability(SOCK_DGRAM, ETH_P_IP)));
FileDescriptor mmap_sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_PACKET, SOCK_DGRAM, 0));
int32_t val = TPACKET_V1;
socklen_t val_len = sizeof(val);
EXPECT_THAT(
getsockopt(mmap_sock.get(), SOL_PACKET, PACKET_HDRLEN, &val, &val_len),
SyscallSucceeds());
EXPECT_EQ(val, sizeof(tpacket_hdr));
val = TPACKET_V2;
EXPECT_THAT(
getsockopt(mmap_sock.get(), SOL_PACKET, PACKET_HDRLEN, &val, &val_len),
SyscallSucceeds());
EXPECT_EQ(val, sizeof(tpacket2_hdr));
val = TPACKET_V3;
EXPECT_THAT(
getsockopt(mmap_sock.get(), SOL_PACKET, PACKET_HDRLEN, &val, &val_len),
SyscallFailsWithErrno(EINVAL));
}
TEST(PacketMmapTest, PacketReserve) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(
HavePacketSocketCapability(SOCK_DGRAM, ETH_P_IP)));
sockaddr_ll bind_addr = {
.sll_family = AF_PACKET,
.sll_protocol = htons(ETH_P_IP),
.sll_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex()),
.sll_halen = ETH_ALEN,
};
FileDescriptor mmap_sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_PACKET, SOCK_DGRAM, 0));
uint32_t tp_frame_size = 65536 + 128;
uint32_t tp_block_size = tp_frame_size * 32;
uint32_t tp_block_nr = 2;
uint32_t tp_frame_nr = (tp_block_size * tp_block_nr) / tp_frame_size;
tpacket_req req = {
.tp_block_size = tp_block_size,
.tp_block_nr = tp_block_nr,
.tp_frame_size = tp_frame_size,
.tp_frame_nr = tp_frame_nr,
};
int reserve = 20;
ASSERT_THAT(setsockopt(mmap_sock.get(), SOL_PACKET, PACKET_RESERVE, &reserve,
sizeof(reserve)),
SyscallSucceeds());
void* ring = ASSERT_NO_ERRNO_AND_VALUE(MakePacketMmapRing(
mmap_sock.get(), reinterpret_cast<const sockaddr*>(&bind_addr),
sizeof(bind_addr), &req, TPACKET_V2));
auto ring_cleanup = Cleanup([ring, tp_block_size, tp_block_nr] {
ASSERT_THAT(munmap(ring, tp_block_size * tp_block_nr), SyscallSucceeds());
});
std::string kMessage = "123abc";
ASSERT_THAT(
sendto(mmap_sock.get(), kMessage.c_str(), kMessage.size(), 0 /* flags */,
reinterpret_cast<const sockaddr*>(&bind_addr), sizeof(bind_addr)),
SyscallSucceeds());
tpacket2_hdr* hdr = reinterpret_cast<tpacket2_hdr*>(ring);
struct pollfd pollset;
pollset.fd = mmap_sock.get();
pollset.revents = 0;
pollset.events = POLLIN | POLLRDNORM | POLLERR;
ASSERT_THAT(poll(&pollset, 1, -1), SyscallSucceeds());
EXPECT_EQ(hdr->tp_status & TP_STATUS_USER, 1);
EXPECT_EQ(hdr->tp_len, kMessage.size());
EXPECT_EQ(hdr->tp_snaplen, kMessage.size());
EXPECT_STREQ((char*)(hdr) + hdr->tp_net, kMessage.c_str());
// PACKET_MMAP always adds a min 16 bytes between the sockaddr_ll and the
// packet data.
EXPECT_EQ(
hdr->tp_net,
TPACKET_ALIGN(sizeof(tpacket2_hdr) + sizeof(sockaddr_ll) + 16) + reserve);
}
TEST(PacketMmapTest, PacketStatistics) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(
HavePacketSocketCapability(SOCK_DGRAM, ETH_P_IP)));
sockaddr_ll bind_addr = {
.sll_family = AF_PACKET,
.sll_protocol = htons(ETH_P_IP),
.sll_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex()),
.sll_halen = ETH_ALEN,
};
FileDescriptor mmap_sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_PACKET, SOCK_DGRAM, 0));
uint32_t tp_frame_size = 65536 + 128;
uint32_t tp_block_size = tp_frame_size * 32;
uint32_t tp_block_nr = 2;
uint32_t tp_frame_nr = (tp_block_size * tp_block_nr) / tp_frame_size;
tpacket_req req = {
.tp_block_size = tp_block_size,
.tp_block_nr = tp_block_nr,
.tp_frame_size = tp_frame_size,
.tp_frame_nr = tp_frame_nr,
};
void* ring = ASSERT_NO_ERRNO_AND_VALUE(MakePacketMmapRing(
mmap_sock.get(), reinterpret_cast<const sockaddr*>(&bind_addr),
sizeof(bind_addr), &req, TPACKET_V2));
auto ring_cleanup = Cleanup([ring, tp_block_size, tp_block_nr] {
ASSERT_THAT(munmap(ring, tp_block_size * tp_block_nr), SyscallSucceeds());
});
std::string kMessage = "123abc";
for (uint32_t i = 0; i < tp_frame_nr; i++) {
ASSERT_THAT(
sendto(mmap_sock.get(), kMessage.c_str(), kMessage.size(),
0 /* flags */, reinterpret_cast<const sockaddr*>(&bind_addr),
sizeof(bind_addr)),
SyscallSucceeds());
}
// After sending tp_frame_nr packets the buffer is full and all future sent
// packets will be dropped.
int expected_dropped = 20;
for (int i = 0; i < expected_dropped; i++) {
ASSERT_THAT(
sendto(mmap_sock.get(), kMessage.c_str(), kMessage.size(),
0 /* flags */, reinterpret_cast<const sockaddr*>(&bind_addr),
sizeof(bind_addr)),
SyscallSucceeds());
}
struct tpacket_stats stats;
socklen_t stats_len = sizeof(stats);
EXPECT_THAT(getsockopt(mmap_sock.get(), SOL_PACKET, PACKET_STATISTICS, &stats,
&stats_len),
SyscallSucceeds());
EXPECT_EQ(stats.tp_drops, expected_dropped);
EXPECT_EQ(stats.tp_packets, tp_frame_nr + expected_dropped);
}
} // namespace
} // namespace testing
} // namespace gvisor
int main(int argc, char** argv) {
// Some tests depend on delivering a signal to the main thread. Block the
// target signal so that any other threads created by TestInit will also have
// the signal blocked.
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGALRM);
TEST_PCHECK(sigprocmask(SIG_BLOCK, &set, nullptr) == 0);
gvisor::testing::TestInit(&argc, &argv);
return gvisor::testing::RunAllTests();
}