mirror of
https://github.com/hanzoai/visor.git
synced 2026-09-24 17:04:14 +00:00
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
706 lines
26 KiB
C++
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();
|
|
}
|