Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Expose UDP_MAX_SEGMENTS via System #2891

Merged
merged 2 commits into from
Sep 20, 2024
Merged
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
4 changes: 3 additions & 1 deletion Sources/CNIOLinux/include/CNIOLinux.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
#include <errno.h>
#include <pthread.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <linux/udp.h>
#include <linux/vm_sockets.h>
#include <fcntl.h>
#include <fts.h>
Expand Down Expand Up @@ -143,5 +143,7 @@ extern const unsigned int CNIOLinux_RENAME_EXCHANGE;
extern const unsigned long CNIOLinux_UTIME_OMIT;
extern const unsigned long CNIOLinux_UTIME_NOW;

extern const long CNIOLinux_UDP_MAX_SEGMENTS;

#endif
#endif
5 changes: 5 additions & 0 deletions Sources/CNIOLinux/shim.c
Original file line number Diff line number Diff line change
Expand Up @@ -215,4 +215,9 @@ const int CNIOLinux_AT_EMPTY_PATH = AT_EMPTY_PATH;
const unsigned long CNIOLinux_UTIME_OMIT = UTIME_OMIT;
const unsigned long CNIOLinux_UTIME_NOW = UTIME_NOW;


#ifdef UDP_MAX_SEGMENTS
const long CNIOLinux_UDP_MAX_SEGMENTS = UDP_MAX_SEGMENTS;
#endif
const long CNIOLinux_UDP_MAX_SEGMENTS = -1;
#endif
11 changes: 11 additions & 0 deletions Sources/NIOCore/Utilities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -257,4 +257,15 @@ extension System {
/// The option can be enabled by setting the ``ChannelOptions/Types/DatagramReceiveOffload`` channel option.
public static let supportsUDPReceiveOffload: Bool = false
#endif

/// Returns the UDP maximum segment count if the platform supports and defines it.
public static var udpMaxSegments: Int? {
#if os(Linux)
let maxSegments = CNIOLinux_UDP_MAX_SEGMENTS
if maxSegments != -1 {
return maxSegments
}
#endif
return nil
}
}
14 changes: 10 additions & 4 deletions Tests/NIOPosixTests/DatagramChannelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1495,7 +1495,10 @@ class DatagramChannelTests: XCTestCase {
func testWriteBufferAtGSOSegmentCountLimit() throws {
try XCTSkipUnless(System.supportsUDPSegmentationOffload, "UDP_SEGMENT (GSO) is not supported on this platform")

var segments = 64
let udpMaxSegments = System.udpMaxSegments ?? 64

var segments = udpMaxSegments

let segmentSize = 10
let didSet = self.firstChannel.setOption(.datagramSegmentSize, value: CInt(segmentSize))
XCTAssertNoThrow(try didSet.wait())
Expand All @@ -1514,7 +1517,7 @@ class DatagramChannelTests: XCTestCase {
self.firstChannel = try self.buildChannel(group: self.group)
let didSet = self.firstChannel.setOption(.datagramSegmentSize, value: CInt(segmentSize))
XCTAssertNoThrow(try didSet.wait())
segments = 61
segments = udpMaxSegments - 3
try send(byteCount: segments * segmentSize)
}

Expand All @@ -1525,13 +1528,16 @@ class DatagramChannelTests: XCTestCase {
func testWriteBufferAboveGSOSegmentCountLimitShouldError() throws {
try XCTSkipUnless(System.supportsUDPSegmentationOffload, "UDP_SEGMENT (GSO) is not supported on this platform")

// commonly 64 or 128 on systems which may or may not define UDP_MAX_SEGMENTS, pick the larger to ensure failure
let udpMaxSegments = System.udpMaxSegments ?? 128

let segmentSize = 10
let didSet = self.firstChannel.setOption(.datagramSegmentSize, value: CInt(segmentSize))
XCTAssertNoThrow(try didSet.wait())

let buffer = self.firstChannel.allocator.buffer(repeating: 1, count: segmentSize * 65)
let buffer = self.firstChannel.allocator.buffer(repeating: 1, count: segmentSize * udpMaxSegments + 1)
let writeData = AddressedEnvelope(remoteAddress: self.secondChannel.localAddress!, data: buffer)
// The kernel limits messages to a maximum of 64 segments; any more should result in an error.
// The kernel limits messages to a maximum of UDP_MAX_SEGMENTS segments; any more should result in an error.
XCTAssertThrowsError(try self.firstChannel.writeAndFlush(NIOAny(writeData)).wait()) {
XCTAssert($0 is IOError)
}
Expand Down
Loading