1const TCP_HEADER_MIN_LEN: usize = 20;
8
9const TCP_OPT_MSS: u8 = 2;
11
12const TCP_OPT_MSS_LEN: u8 = 4;
14
15const TCP_FLAGS_OFFSET: usize = 13;
17
18const TCP_FLAG_SYN: u8 = 0x02;
20
21fn is_tcp_syn(tcp_header: &[u8]) -> bool {
23 if tcp_header.len() < TCP_HEADER_MIN_LEN {
24 return false;
25 }
26 (tcp_header[TCP_FLAGS_OFFSET] & TCP_FLAG_SYN) != 0
27}
28
29fn get_tcp_data_offset(tcp_header: &[u8]) -> usize {
31 if tcp_header.len() < TCP_HEADER_MIN_LEN {
32 return 0;
33 }
34 ((tcp_header[12] >> 4) as usize) * 4
35}
36
37pub fn clamp_tcp_mss(ipv6_packet: &mut [u8], max_mss: u16) -> bool {
44 if ipv6_packet.len() < 40 || ipv6_packet[0] >> 4 != 6 {
46 return false;
47 }
48
49 let next_header = ipv6_packet[6];
51 if next_header != 6 {
52 return false;
53 }
54
55 let tcp_start = 40;
57 if ipv6_packet.len() < tcp_start + TCP_HEADER_MIN_LEN {
58 return false;
59 }
60
61 let tcp_header = &ipv6_packet[tcp_start..];
62
63 if !is_tcp_syn(tcp_header) {
65 return false;
66 }
67
68 let tcp_header_len = get_tcp_data_offset(tcp_header);
70 if tcp_header_len < TCP_HEADER_MIN_LEN || tcp_header_len > tcp_header.len() {
71 return false;
72 }
73
74 let options_start = tcp_start + TCP_HEADER_MIN_LEN;
76 let options_end = tcp_start + tcp_header_len;
77
78 if options_end > ipv6_packet.len() {
79 return false;
80 }
81
82 let mut modified = false;
83 let mut i = options_start;
84
85 while i < options_end {
86 let kind = ipv6_packet[i];
87
88 if kind == 0 {
90 break;
91 }
92
93 if kind == 1 {
95 i += 1;
96 continue;
97 }
98
99 if i + 1 >= options_end {
101 break;
102 }
103
104 let length = ipv6_packet[i + 1] as usize;
105 if length < 2 || i + length > options_end {
106 break;
107 }
108
109 if kind == TCP_OPT_MSS && length == TCP_OPT_MSS_LEN as usize {
111 let current_mss = u16::from_be_bytes([ipv6_packet[i + 2], ipv6_packet[i + 3]]);
113
114 if current_mss > max_mss {
116 ipv6_packet[i + 2..i + 4].copy_from_slice(&max_mss.to_be_bytes());
117
118 recalculate_tcp_checksum(ipv6_packet, tcp_start);
120
121 modified = true;
122 }
123 break; }
125
126 i += length;
127 }
128
129 modified
130}
131
132fn recalculate_tcp_checksum(ipv6_packet: &mut [u8], tcp_start: usize) {
134 ipv6_packet[tcp_start + 16] = 0;
136 ipv6_packet[tcp_start + 17] = 0;
137
138 let src = &ipv6_packet[8..24];
140 let dst = &ipv6_packet[24..40];
141
142 let payload_len = u16::from_be_bytes([ipv6_packet[4], ipv6_packet[5]]) as usize;
144 let tcp_segment = &ipv6_packet[tcp_start..tcp_start + payload_len];
145
146 let mut sum: u32 = 0;
148
149 for chunk in src.chunks(2) {
151 sum += u16::from_be_bytes([chunk[0], chunk[1]]) as u32;
152 }
153
154 for chunk in dst.chunks(2) {
156 sum += u16::from_be_bytes([chunk[0], chunk[1]]) as u32;
157 }
158
159 sum += payload_len as u32;
161
162 sum += 6;
164
165 for chunk in tcp_segment.chunks(2) {
167 let value = if chunk.len() == 2 {
168 u16::from_be_bytes([chunk[0], chunk[1]])
169 } else {
170 u16::from_be_bytes([chunk[0], 0])
171 };
172 sum += value as u32;
173 }
174
175 while sum >> 16 != 0 {
177 sum = (sum & 0xffff) + (sum >> 16);
178 }
179
180 let checksum = !sum as u16;
182 ipv6_packet[tcp_start + 16..tcp_start + 18].copy_from_slice(&checksum.to_be_bytes());
183}
184
185#[cfg(test)]
186mod tests {
187 use super::*;
188
189 fn make_tcp_syn_packet(src: [u8; 16], dst: [u8; 16], mss: u16) -> Vec<u8> {
190 let mut packet = vec![0u8; 40 + 40]; packet[0] = 0x60; packet[4..6].copy_from_slice(&40u16.to_be_bytes()); packet[6] = 6; packet[7] = 64; packet[8..24].copy_from_slice(&src);
198 packet[24..40].copy_from_slice(&dst);
199
200 let tcp_start = 40;
202 packet[tcp_start..tcp_start + 2].copy_from_slice(&12345u16.to_be_bytes()); packet[tcp_start + 2..tcp_start + 4].copy_from_slice(&80u16.to_be_bytes()); packet[tcp_start + 4..tcp_start + 8].copy_from_slice(&1000u32.to_be_bytes()); packet[tcp_start + 8..tcp_start + 12].copy_from_slice(&0u32.to_be_bytes()); packet[tcp_start + 12] = 0xa0; packet[tcp_start + 13] = TCP_FLAG_SYN; packet[tcp_start + 14..tcp_start + 16].copy_from_slice(&8192u16.to_be_bytes()); packet[tcp_start + 20] = TCP_OPT_MSS; packet[tcp_start + 21] = TCP_OPT_MSS_LEN; packet[tcp_start + 22..tcp_start + 24].copy_from_slice(&mss.to_be_bytes()); packet[tcp_start + 24] = 0;
217
218 recalculate_tcp_checksum(&mut packet, tcp_start);
220
221 packet
222 }
223
224 #[test]
225 fn test_clamp_tcp_mss_reduces_large_mss() {
226 let src = [0xfd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1];
227 let dst = [0xfd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2];
228 let mut packet = make_tcp_syn_packet(src, dst, 1460);
229
230 let modified = clamp_tcp_mss(&mut packet, 1200);
231
232 assert!(modified);
233
234 let tcp_start = 40;
236 let mss = u16::from_be_bytes([packet[tcp_start + 22], packet[tcp_start + 23]]);
237 assert_eq!(mss, 1200);
238 }
239
240 #[test]
241 fn test_clamp_tcp_mss_leaves_small_mss_unchanged() {
242 let src = [0xfd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1];
243 let dst = [0xfd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2];
244 let mut packet = make_tcp_syn_packet(src, dst, 1000);
245
246 let modified = clamp_tcp_mss(&mut packet, 1200);
247
248 assert!(!modified);
249
250 let tcp_start = 40;
252 let mss = u16::from_be_bytes([packet[tcp_start + 22], packet[tcp_start + 23]]);
253 assert_eq!(mss, 1000);
254 }
255
256 #[test]
257 fn test_clamp_tcp_mss_ignores_non_syn() {
258 let src = [0xfd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1];
259 let dst = [0xfd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2];
260 let mut packet = make_tcp_syn_packet(src, dst, 1460);
261
262 packet[40 + 13] = 0x10; let modified = clamp_tcp_mss(&mut packet, 1200);
266
267 assert!(!modified);
268 }
269
270 #[test]
271 fn test_clamp_tcp_mss_ignores_non_tcp() {
272 let mut packet = vec![0u8; 80];
273 packet[0] = 0x60; packet[6] = 17; let modified = clamp_tcp_mss(&mut packet, 1200);
277
278 assert!(!modified);
279 }
280}