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