@@ -5,19 +5,75 @@ use crate::stream::TransportFeatures;
5
5
use arrayvec:: ArrayVec ;
6
6
use core:: ops:: Deref ;
7
7
use s2n_quic_core:: { ensure, inet:: ExplicitCongestionNotification } ;
8
+ use s2n_quic_platform:: features;
8
9
use std:: io:: IoSlice ;
9
10
10
- /// The maximum number of segments in sendmsg calls
11
- ///
12
- /// From <https://elixir.bootlin.com/linux/v6.8.7/source/include/uapi/linux/uio.h#L27>
13
- /// > #define UIO_FASTIOV 8
14
- pub const MAX_COUNT : usize = if cfg ! ( target_os = "linux" ) { 8 } else { 1 } ;
11
+ /// The maximum size of a IP+UDP header
12
+ const IPV4_HEADER_LEN : u16 = 20 ;
13
+ const IPV6_HEADER_LEN : u16 = 40 ;
14
+ const UDP_HEADER_LEN : u16 = 8 ;
15
+
16
+ const fn min_u16 ( a : u16 , b : u16 ) -> u16 {
17
+ if a < b {
18
+ a
19
+ } else {
20
+ b
21
+ }
22
+ }
15
23
16
- /// The maximum payload allowed in sendmsg calls using UDP
24
+ /// The maximum number of segments in sendmsg calls
17
25
///
18
- /// From <https://github.com/torvalds/linux/blob/8cd26fd90c1ad7acdcfb9f69ca99d13aa7b24561/net/ipv4/ip_output.c#L987-L995>
19
- /// > Linux enforces a u16::MAX - IP_HEADER_LEN - UDP_HEADER_LEN
20
- pub const MAX_TOTAL : u16 = u16:: MAX - 50 ;
26
+ /// From <https://elixir.bootlin.com/linux/v6.8.7/source/include/uapi/linux/uio.h#L28>
27
+ /// > #define UIO_MAXIOV 1024
28
+ pub const MAX_COUNT : usize = if features:: gso:: IS_SUPPORTED {
29
+ // base the max segments on the max datagram size for the default ethernet mtu
30
+ let max_datagram_size = 1500 - min_u16 ( IPV4_HEADER_LEN , IPV6_HEADER_LEN ) - UDP_HEADER_LEN ;
31
+
32
+ ( MAX_TOTAL / max_datagram_size) as _
33
+ } else {
34
+ // only a single segment can be sent per syscall
35
+ 1
36
+ } ;
37
+
38
+ /// The maximum payload allowed in sendmsg calls using IPv4+UDP
39
+ const MAX_TOTAL_IPV4 : u16 = if cfg ! ( target_os = "linux" ) {
40
+ // From <https://github.com/torvalds/linux/blob/8cd26fd90c1ad7acdcfb9f69ca99d13aa7b24561/net/ipv4/ip_output.c#L987-L995>
41
+ // > Linux enforces a u16::MAX - IP_HEADER_LEN - UDP_HEADER_LEN
42
+ u16:: MAX - IPV4_HEADER_LEN - UDP_HEADER_LEN
43
+ } else {
44
+ 9001 - IPV4_HEADER_LEN - UDP_HEADER_LEN
45
+ } ;
46
+
47
+ /// The maximum payload allowed in sendmsg calls using IPv6+UDP
48
+ const MAX_TOTAL_IPV6 : u16 = if cfg ! ( target_os = "linux" ) {
49
+ // IPv6 doesn't include the IP header size in the calculation
50
+ u16:: MAX - UDP_HEADER_LEN
51
+ } else {
52
+ 9001 - IPV6_HEADER_LEN - UDP_HEADER_LEN
53
+ } ;
54
+
55
+ /// The minimum payload size between the IPv4 and IPv6 sizes
56
+ pub const MAX_TOTAL : u16 = min_u16 ( MAX_TOTAL_IPV4 , MAX_TOTAL_IPV6 ) ;
57
+
58
+ #[ test]
59
+ fn max_total_test ( ) {
60
+ let tests = [ ( "127.0.0.1:0" , MAX_TOTAL_IPV4 ) , ( "[::1]:0" , MAX_TOTAL_IPV6 ) ] ;
61
+
62
+ for ( addr, total) in tests {
63
+ let socket = std:: net:: UdpSocket :: bind ( addr) . unwrap ( ) ;
64
+ let addr = socket. local_addr ( ) . unwrap ( ) ;
65
+
66
+ let mut buffer = vec ! [ 0u8 ; total as usize + 1 ] ;
67
+
68
+ // This behavior may not be consistent across kernel versions so the check is disabled by default
69
+ let _ = socket. send_to ( & buffer, addr) ;
70
+
71
+ buffer. pop ( ) . unwrap ( ) ;
72
+ socket
73
+ . send_to ( & buffer, addr)
74
+ . expect ( "send should succeed when limited to MAX_TOTAL" ) ;
75
+ }
76
+ }
21
77
22
78
type Segments < ' a > = ArrayVec < IoSlice < ' a > , MAX_COUNT > ;
23
79
0 commit comments