OpenVPN
mss.c
Go to the documentation of this file.
1/*
2 * OpenVPN -- An application to securely tunnel IP networks
3 * over a single TCP/UDP port, with support for SSL/TLS-based
4 * session authentication and key exchange,
5 * packet encryption, packet authentication, and
6 * packet compression.
7 *
8 * Copyright (C) 2002-2025 OpenVPN Inc <sales@openvpn.net>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, see <https://www.gnu.org/licenses/>.
21 */
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#include "syshead.h"
28#include "error.h"
29#include "mss.h"
30#include "crypto.h"
31#include "ssl_common.h"
32#include "memdbg.h"
33#include "forward.h"
34
35/*
36 * Lower MSS on TCP SYN packets to fix MTU
37 * problems which arise from protocol
38 * encapsulation.
39 */
40
41/*
42 * IPv4 packet: find TCP header, check flags for "SYN"
43 * if yes, hand to mss_fixup_dowork()
44 */
45void
46mss_fixup_ipv4(struct buffer *buf, uint16_t maxmss)
47{
48 const struct openvpn_iphdr *pip;
49 int hlen;
50
51 if (BLEN(buf) < (int)sizeof(struct openvpn_iphdr))
52 {
53 return;
54 }
55
56 verify_align_4(buf);
57 pip = (struct openvpn_iphdr *)BPTR(buf);
58
60
61 if (pip->protocol == OPENVPN_IPPROTO_TCP && ntohs(pip->tot_len) == BLEN(buf)
62 && (ntohs(pip->frag_off) & OPENVPN_IP_OFFMASK) == 0 && hlen <= BLEN(buf)
63 && BLEN(buf) - hlen >= (int)sizeof(struct openvpn_tcphdr))
64 {
65 struct buffer newbuf = *buf;
66 if (buf_advance(&newbuf, hlen))
67 {
68 struct openvpn_tcphdr *tc = (struct openvpn_tcphdr *)BPTR(&newbuf);
70 {
71 mss_fixup_dowork(&newbuf, maxmss);
72 }
73 }
74 }
75}
76
77/*
78 * IPv6 packet: find TCP header, check flags for "SYN"
79 * if yes, hand to mss_fixup_dowork()
80 * (IPv6 header structure is sufficiently different from IPv4...)
81 */
82void
83mss_fixup_ipv6(struct buffer *buf, uint16_t maxmss)
84{
85 const struct openvpn_ipv6hdr *pip6;
86 struct buffer newbuf;
87
88 if (BLEN(buf) < (int)sizeof(struct openvpn_ipv6hdr))
89 {
90 return;
91 }
92
93 verify_align_4(buf);
94 pip6 = (struct openvpn_ipv6hdr *)BPTR(buf);
95
96 /* do we have the full IPv6 packet?
97 * "payload_len" does not include IPv6 header (+40 bytes)
98 */
99 if (BLEN(buf) != (int)ntohs(pip6->payload_len) + 40)
100 {
101 return;
102 }
103
104 /* follow header chain until we reach final header, then check for TCP
105 *
106 * An IPv6 packet could, theoretically, have a chain of multiple headers
107 * before the final header (TCP, UDP, ...), so we'd need to walk that
108 * chain (see RFC 2460 and RFC 6564 for details).
109 *
110 * In practice, "most typically used" extension headers (AH, routing,
111 * fragment, mobility) are very unlikely to be seen inside an OpenVPN
112 * tun, so for now, we only handle the case of "single next header = TCP"
113 */
114 if (pip6->nexthdr != OPENVPN_IPPROTO_TCP)
115 {
116 return;
117 }
118
119 /* skip IPv6 header (40 bytes),
120 * verify remainder is large enough to contain a full TCP header
121 */
122 newbuf = *buf;
123 if (buf_advance(&newbuf, 40) && BLEN(&newbuf) >= (int)sizeof(struct openvpn_tcphdr))
124 {
125 struct openvpn_tcphdr *tc = (struct openvpn_tcphdr *)BPTR(&newbuf);
127 {
128 mss_fixup_dowork(&newbuf, maxmss - 20);
129 }
130 }
131}
132
133/*
134 * change TCP MSS option in SYN/SYN-ACK packets, if present
135 * this is generic for IPv4 and IPv6, as the TCP header is the same
136 */
137
138void
139mss_fixup_dowork(struct buffer *buf, uint16_t maxmss)
140{
141 int olen, optlen;
142 uint8_t *opt;
143
144 if (BLEN(buf) < (int)sizeof(struct openvpn_tcphdr))
145 {
146 return;
147 }
148
149 verify_align_4(buf);
150 struct openvpn_tcphdr *tc = (struct openvpn_tcphdr *)BPTR(buf);
151 int hlen = OPENVPN_TCPH_GET_DOFF(tc->doff_res);
152
153 /* Invalid header length or header without options. */
154 if (hlen <= (int)sizeof(struct openvpn_tcphdr) || hlen > BLEN(buf))
155 {
156 return;
157 }
158
159 for (olen = hlen - (int)sizeof(struct openvpn_tcphdr), opt = (uint8_t *)(tc + 1); olen > 1;
160 olen -= optlen, opt += optlen)
161 {
162 if (*opt == OPENVPN_TCPOPT_EOL)
163 {
164 break;
165 }
166 if (*opt == OPENVPN_TCPOPT_NOP)
167 {
168 optlen = 1;
169 continue;
170 }
171
172 optlen = *(opt + 1);
173 if (optlen <= 0 || optlen > olen)
174 {
175 break;
176 }
177 if (*opt == OPENVPN_TCPOPT_MAXSEG)
178 {
179 if (optlen != OPENVPN_TCPOLEN_MAXSEG)
180 {
181 continue;
182 }
183 uint16_t mssval = (uint16_t)(opt[2] << 8) + opt[3];
184 if (mssval > maxmss)
185 {
186 dmsg(D_MSS, "MSS: %" PRIu16 " -> %" PRIu16, mssval, maxmss);
187 opt[2] = (uint8_t)((maxmss >> 8) & 0xff);
188 opt[3] = (uint8_t)(maxmss & 0xff);
189 int32_t accumulate = htons(mssval);
190 accumulate -= htons(maxmss);
191 ADJUST_CHECKSUM(accumulate, tc->check);
192 }
193 }
194 }
195}
196
197static inline size_t
198adjust_payload_max_cbc(const struct key_type *kt, size_t target)
199{
200 if (!cipher_kt_mode_cbc(kt->cipher))
201 {
202 /* With stream ciphers (or block cipher in stream modes like CFB, AEAD)
203 * we can just use the target as is */
204 return target;
205 }
206 else
207 {
208 /* With CBC we need at least one extra byte for padding and then need
209 * to ensure that the resulting CBC ciphertext length, which is always
210 * a multiple of the block size, is not larger than the target value */
211 size_t block_size = cipher_kt_block_size(kt->cipher);
212 target = round_down_size(target, block_size);
213 return target - 1;
214 }
215}
216
217static size_t
218get_ip_encap_overhead(const struct options *options, const struct link_socket_info *lsi)
219{
220 /* Add the overhead of the encapsulating IP packets */
221 sa_family_t af;
222 int proto;
223
224 if (lsi && lsi->lsa)
225 {
226 af = lsi->lsa->actual.dest.addr.sa.sa_family;
227 proto = lsi->proto;
228 }
229 else
230 {
231 /* In the early init before the connection is established or we
232 * are in listen mode we can only make an educated guess
233 * from the af of the connection entry, in p2mp this will be
234 * later updated */
235 af = options->ce.af;
236 proto = options->ce.proto;
237 }
238 return datagram_overhead(af, proto);
239}
240
241static void
242frame_calculate_fragment(struct frame *frame, struct key_type *kt, const struct options *options,
243 struct link_socket_info *lsi)
244{
245#if defined(ENABLE_FRAGMENT)
246 size_t overhead;
247
248 overhead = frame_calculate_protocol_header_size(kt, options, false);
249
251 {
252 overhead += get_ip_encap_overhead(options, lsi);
253 }
254
255 size_t target = options->ce.fragment - overhead;
256 /* The 4 bytes of header that fragment adds itself. The other extra payload
257 * bytes (Ethernet header/compression) are handled by the fragment code
258 * just as part of the payload and therefore automatically taken into
259 * account if the packet needs to fragmented */
261
262 if (cipher_kt_mode_cbc(kt->cipher))
263 {
264 /* The packet id gets added to *each* fragment in CBC mode, so we need
265 * to account for it */
267 }
268#endif
269}
270
271static void
272frame_calculate_mssfix(struct frame *frame, struct key_type *kt, const struct options *options,
273 struct link_socket_info *lsi)
274{
276 {
277 /* we subtract IPv4 and TCP overhead here, mssfix method will add the
278 * extra 20 for IPv6 */
279 frame->mss_fix = (uint16_t)(options->ce.mssfix - (20 + 20));
280 return;
281 }
282
283 size_t overhead, payload_overhead;
284
285 overhead = frame_calculate_protocol_header_size(kt, options, false);
286
287 /* Calculate the number of bytes that the payload differs from the payload
288 * MTU. This are fragment/compression/ethernet headers */
290
291 /* We are in a "liberal" position with respect to MSS,
292 * i.e. we assume that MSS can be calculated from MTU
293 * by subtracting out only the IP and TCP header sizes
294 * without options.
295 *
296 * (RFC 879, section 7). */
297
299 {
300 /* Add the overhead of the encapsulating IP packets */
301 overhead += get_ip_encap_overhead(options, lsi);
302 }
303
304 /* Add 20 bytes for the IPv4 header and 20 byte for the TCP header of the
305 * payload, the mssfix method will add 20 extra if payload is IPv6 */
306 payload_overhead += 20 + 20;
307
308 /* Calculate the maximum MSS value from the max link layer size specified
309 * by ce.mssfix */
310
311 /* This is the target value our payload needs to be smaller */
312 size_t target = options->ce.mssfix - overhead;
313 frame->mss_fix = (uint16_t)(adjust_payload_max_cbc(kt, target) - payload_overhead);
314}
315
316void
317frame_calculate_dynamic(struct frame *frame, struct key_type *kt, const struct options *options,
318 struct link_socket_info *lsi)
319{
320 if (options->ce.fragment > 0)
321 {
323 }
324
325 if (options->ce.mssfix > 0)
326 {
328 }
329}
330
331/*
332 * Adjust frame structure based on a Path MTU value given
333 * to us by the OS.
334 */
335void
337{
338 struct link_socket_info *lsi = get_link_socket_info(c);
339 struct options *o = &c->options;
340
341 int pmtu = c->c2.link_sockets[0]->mtu;
342 sa_family_t af = lsi->lsa->actual.dest.addr.sa.sa_family;
343 int proto = lsi->proto;
344
345 int encap_overhead = datagram_overhead(af, proto);
346
347 /* check if mssfix and fragment need to be adjusted */
348 if (pmtu < o->ce.mssfix || (o->ce.mssfix_encap && pmtu < o->ce.mssfix + encap_overhead))
349 {
350 const char *mtustr = o->ce.mssfix_encap ? " mtu" : "";
352 "Note adjusting 'mssfix %d%s' to 'mssfix %d mtu' "
353 "according to path MTU discovery",
354 o->ce.mssfix, mtustr, pmtu);
355 o->ce.mssfix = pmtu;
356 o->ce.mssfix_encap = true;
357 frame_calculate_dynamic(&c->c2.frame, &c->c1.ks.key_type, o, lsi);
358 }
359
360#if defined(ENABLE_FRAGMENT)
361 if (pmtu < o->ce.fragment || (o->ce.fragment_encap && pmtu < o->ce.fragment + encap_overhead))
362 {
363 const char *mtustr = o->ce.fragment_encap ? " mtu" : "";
365 "Note adjusting 'fragment %d%s' to 'fragment %d mtu' "
366 "according to path MTU discovery",
367 o->ce.fragment, mtustr, pmtu);
368 o->ce.fragment = pmtu;
369 o->ce.fragment_encap = true;
371 }
372#endif
373}
#define BPTR(buf)
Definition buffer.h:123
static bool buf_advance(struct buffer *buf, int size)
Definition buffer.h:616
#define verify_align_4(ptr)
Definition buffer.h:983
#define BLEN(buf)
Definition buffer.h:126
Data Channel Cryptography Module.
bool cipher_kt_mode_cbc(const char *ciphername)
Check if the supplied cipher is a supported CBC mode cipher.
int cipher_kt_block_size(const char *ciphername)
Returns the block size of the cipher, in bytes.
#define D_MSS
Definition errlevel.h:127
#define D_MTU_INFO
Definition errlevel.h:104
Interface functions to the internal and external multiplexers.
static struct link_socket_info * get_link_socket_info(struct context *c)
Definition forward.h:333
static size_t round_down_size(size_t num, size_t multiple)
Rounds down num to the nearest multiple of multiple.
Definition integer.h:233
static int clamp_size_to_int(size_t size)
Definition integer.h:43
static void frame_calculate_fragment(struct frame *frame, struct key_type *kt, const struct options *options, struct link_socket_info *lsi)
Definition mss.c:242
void mss_fixup_dowork(struct buffer *buf, uint16_t maxmss)
Definition mss.c:139
void frame_calculate_dynamic(struct frame *frame, struct key_type *kt, const struct options *options, struct link_socket_info *lsi)
Set the –mssfix option.
Definition mss.c:317
void mss_fixup_ipv6(struct buffer *buf, uint16_t maxmss)
Definition mss.c:83
void mss_fixup_ipv4(struct buffer *buf, uint16_t maxmss)
Definition mss.c:46
void frame_adjust_path_mtu(struct context *c)
Checks and adjusts the fragment and mssfix value according to the discovered path mtu value.
Definition mss.c:336
static size_t get_ip_encap_overhead(const struct options *options, const struct link_socket_info *lsi)
Definition mss.c:218
static void frame_calculate_mssfix(struct frame *frame, struct key_type *kt, const struct options *options, struct link_socket_info *lsi)
Definition mss.c:272
static size_t adjust_payload_max_cbc(const struct key_type *kt, size_t target)
Definition mss.c:198
size_t frame_calculate_protocol_header_size(const struct key_type *kt, const struct options *options, bool occ)
Calculates the size of the OpenVPN protocol header.
Definition mtu.c:61
unsigned int calc_packet_id_size_dc(const struct options *options, const struct key_type *kt)
Return the size of the packet ID size that is currently in use by cipher and options for the data cha...
Definition mtu.c:51
size_t frame_calculate_payload_overhead(size_t extra_tun, const struct options *options, const struct key_type *kt)
Calculates the size of the payload overhead according to tun-mtu and tap overhead.
Definition mtu.c:98
#define dmsg(flags,...)
Definition error.h:172
#define msg(flags,...)
Definition error.h:152
#define OPENVPN_TCPOPT_NOP
Definition proto.h:191
#define OPENVPN_IPH_GET_LEN(v)
Definition proto.h:92
#define OPENVPN_TCPH_GET_DOFF(d)
Definition proto.h:172
#define OPENVPN_TCPOPT_MAXSEG
Definition proto.h:192
#define OPENVPN_TCPOPT_EOL
Definition proto.h:190
#define OPENVPN_IP_OFFMASK
Definition proto.h:99
#define ADJUST_CHECKSUM(acc, cksum)
Definition proto.h:215
#define OPENVPN_TCPH_SYN_MASK
Definition proto.h:176
#define OPENVPN_TCPOLEN_MAXSEG
Definition proto.h:193
#define OPENVPN_IPPROTO_TCP
Definition proto.h:105
static int datagram_overhead(sa_family_t af, int proto)
Control Channel Common Data Structures.
Wrapper structure for dynamically allocated memory.
Definition buffer.h:60
int len
Length in bytes of the actual content within the allocated memory.
Definition buffer.h:65
int mssfix
Definition options.h:144
bool mssfix_encap
Definition options.h:146
bool fragment_encap
Definition options.h:142
int fragment
Definition options.h:141
int proto
Definition options.h:109
sa_family_t af
Definition options.h:110
bool mssfix_fixed
Definition options.h:148
struct key_schedule ks
Definition openvpn.h:164
struct frame frame
Definition openvpn.h:248
struct frame frame_fragment
Definition openvpn.h:253
struct link_socket ** link_sockets
Definition openvpn.h:237
Contains all state information for one tunnel.
Definition openvpn.h:474
struct context_2 c2
Level 2 context.
Definition openvpn.h:517
struct options options
Options loaded from command line or configuration file.
Definition openvpn.h:475
struct context_1 c1
Level 1 context.
Definition openvpn.h:516
Packet geometry parameters.
Definition mtu.h:103
int extra_tun
Maximum number of bytes in excess of the tun/tap MTU that might be read from or written to the virtua...
Definition mtu.h:151
uint16_t mss_fix
The actual MSS value that should be written to the payload packets.
Definition mtu.h:124
int max_fragment_size
The maximum size of a fragment.
Definition mtu.h:130
struct key_type key_type
Definition openvpn.h:57
const char * cipher
const name of the cipher
Definition crypto.h:142
uint16_t frag_off
Definition proto.h:100
uint8_t protocol
Definition proto.h:108
uint16_t tot_len
Definition proto.h:96
uint8_t version_len
Definition proto.h:93
uint8_t nexthdr
Definition proto.h:124
uint16_t payload_len
Definition proto.h:123
union openvpn_sockaddr::@27 addr
struct sockaddr sa
Definition socket_util.h:42
uint8_t doff_res
Definition proto.h:173
uint16_t check
Definition proto.h:186
uint8_t flags
Definition proto.h:183
struct connection_entry ce
Definition options.h:293
unsigned short sa_family_t
Definition syshead.h:396