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