diff --git a/Sources/CNIOLinux/include/CNIOLinux.h b/Sources/CNIOLinux/include/CNIOLinux.h index dd4bf976c8e..63d58d2e62a 100644 --- a/Sources/CNIOLinux/include/CNIOLinux.h +++ b/Sources/CNIOLinux/include/CNIOLinux.h @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include #include @@ -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 diff --git a/Sources/CNIOLinux/shim.c b/Sources/CNIOLinux/shim.c index 4187317bf05..955c440063c 100644 --- a/Sources/CNIOLinux/shim.c +++ b/Sources/CNIOLinux/shim.c @@ -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 diff --git a/Sources/NIOCore/Utilities.swift b/Sources/NIOCore/Utilities.swift index 6184c388c67..795aa3ff42d 100644 --- a/Sources/NIOCore/Utilities.swift +++ b/Sources/NIOCore/Utilities.swift @@ -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 + } } diff --git a/Tests/NIOPosixTests/DatagramChannelTests.swift b/Tests/NIOPosixTests/DatagramChannelTests.swift index 37f44337c1a..8db44985b4d 100644 --- a/Tests/NIOPosixTests/DatagramChannelTests.swift +++ b/Tests/NIOPosixTests/DatagramChannelTests.swift @@ -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()) @@ -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) } @@ -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) }