Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,26 @@ class UdpSocket

auto buf_size = static_cast<int>(bytes);
sock_fd_.setsockopt(SOL_SOCKET, SO_RCVBUF, buf_size).value_or_throw();

// The kernel silently clamps SO_RCVBUF to net.core.rmem_max (default ~212 KB).
// Read back the actual value to detect this.
int actual = 0;
socklen_t len = sizeof(actual);
if (::getsockopt(sock_fd_.get(), SOL_SOCKET, SO_RCVBUF, &actual, &len) == 0) {
// The kernel internally doubles SO_RCVBUF, so `actual` is normally ~2x requested.
// When the requested value is near INT32_MAX, the doubled value may overflow or
// the kernel may round down slightly, so only flag significant differences (>4KB).
// The real clamping case (e.g. 5.4MB requested, 212KB rmem_max -> actual=424KB)
// has a difference of millions of bytes, well above this threshold.
if (actual < buf_size && (buf_size - actual) > 4096) {
throw SocketError(
"SO_RCVBUF was clamped by the kernel: requested " + std::to_string(buf_size) +
" bytes, got " + std::to_string(actual) +
" bytes. Increase net.core.rmem_max: sudo sysctl -w net.core.rmem_max=" +
std::to_string(buf_size));
}
}

return std::move(*this);
}

Expand Down
27 changes: 27 additions & 0 deletions src/nebula_core/nebula_core_hw_interfaces/test/common/test_udp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,33 @@ TEST(TestUdp, TestBufferResize)
UsageError);
}

TEST(TestUdp, TestBufferClampingDetected)
{
auto rmem_max_maybe = read_sys_param("net.core.rmem_max");
if (!rmem_max_maybe.has_value()) GTEST_SKIP() << rmem_max_maybe.error();
size_t rmem_max = rmem_max_maybe.value();

// Nebula's configured buffer size for Pandar128E4X
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The UDP code is part of nebula_core and as such is vendor-independent. Let's best remove the mention of Pandar128E4X here.

constexpr size_t nebula_buf_size = 5400000;

if (rmem_max >= nebula_buf_size) {
Comment on lines +107 to +109
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's test the same cases on all systems, regardless of actual rmem_max:

  1. read rmem_max
  2. test set_socket_buffer_size(rmem_max) (should succeed)
  3. test set_socket_buffer_size(2*rmem_max) (should succeed)
  4. test set_socket_buffer_size(2*rmem_max+1) (should fail)

// On a properly configured system, requesting 5.4 MB should succeed because
// the kernel doubles SO_RCVBUF internally, so actual (10.8 MB) >= requested (5.4 MB).
ASSERT_NO_THROW(
UdpSocket::Builder(g_localhost_ip, g_host_port)
.set_socket_buffer_size(nebula_buf_size)
.bind());
} else {
// On a system with default rmem_max (~212 KB), requesting 5.4 MB should throw
// SocketError because getsockopt will return ~2*rmem_max < 5.4 MB.
ASSERT_THROW(
UdpSocket::Builder(g_localhost_ip, g_host_port)
.set_socket_buffer_size(nebula_buf_size)
.bind(),
SocketError);
}
}

TEST(TestUdp, TestCorrectUsageIsEnforced)
{
// The following functions can be called in any order, any number of times
Expand Down
Loading