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-2018 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 "memdbg.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  */
45 void
46 mss_fixup_ipv4(struct buffer *buf, int 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 
59  hlen = OPENVPN_IPH_GET_LEN(pip->version_len);
60 
61  if (pip->protocol == OPENVPN_IPPROTO_TCP
62  && ntohs(pip->tot_len) == BLEN(buf)
63  && (ntohs(pip->frag_off) & OPENVPN_IP_OFFMASK) == 0
64  && hlen <= BLEN(buf)
65  && BLEN(buf) - hlen
66  >= (int) sizeof(struct openvpn_tcphdr))
67  {
68  struct buffer newbuf = *buf;
69  if (buf_advance(&newbuf, hlen))
70  {
71  struct openvpn_tcphdr *tc = (struct openvpn_tcphdr *) BPTR(&newbuf);
72  if (tc->flags & OPENVPN_TCPH_SYN_MASK)
73  {
74  mss_fixup_dowork(&newbuf, (uint16_t) maxmss);
75  }
76  }
77  }
78 }
79 
80 /*
81  * IPv6 packet: find TCP header, check flags for "SYN"
82  * if yes, hand to mss_fixup_dowork()
83  * (IPv6 header structure is sufficiently different from IPv4...)
84  */
85 void
86 mss_fixup_ipv6(struct buffer *buf, int maxmss)
87 {
88  const struct openvpn_ipv6hdr *pip6;
89  struct buffer newbuf;
90 
91  if (BLEN(buf) < (int) sizeof(struct openvpn_ipv6hdr))
92  {
93  return;
94  }
95 
96  verify_align_4(buf);
97  pip6 = (struct openvpn_ipv6hdr *) BPTR(buf);
98 
99  /* do we have the full IPv6 packet?
100  * "payload_len" does not include IPv6 header (+40 bytes)
101  */
102  if (BLEN(buf) != (int) ntohs(pip6->payload_len)+40)
103  {
104  return;
105  }
106 
107  /* follow header chain until we reach final header, then check for TCP
108  *
109  * An IPv6 packet could, theoretically, have a chain of multiple headers
110  * before the final header (TCP, UDP, ...), so we'd need to walk that
111  * chain (see RFC 2460 and RFC 6564 for details).
112  *
113  * In practice, "most typically used" extention headers (AH, routing,
114  * fragment, mobility) are very unlikely to be seen inside an OpenVPN
115  * tun, so for now, we only handle the case of "single next header = TCP"
116  */
117  if (pip6->nexthdr != OPENVPN_IPPROTO_TCP)
118  {
119  return;
120  }
121 
122  /* skip IPv6 header (40 bytes),
123  * verify remainder is large enough to contain a full TCP header
124  */
125  newbuf = *buf;
126  if (buf_advance( &newbuf, 40 )
127  && BLEN(&newbuf) >= (int) sizeof(struct openvpn_tcphdr))
128  {
129  struct openvpn_tcphdr *tc = (struct openvpn_tcphdr *) BPTR(&newbuf);
130  if (tc->flags & OPENVPN_TCPH_SYN_MASK)
131  {
132  mss_fixup_dowork(&newbuf, (uint16_t) maxmss-20);
133  }
134  }
135 }
136 
137 /*
138  * change TCP MSS option in SYN/SYN-ACK packets, if present
139  * this is generic for IPv4 and IPv6, as the TCP header is the same
140  */
141 
142 void
143 mss_fixup_dowork(struct buffer *buf, uint16_t maxmss)
144 {
145  int hlen, olen, optlen;
146  uint8_t *opt;
147  uint16_t mssval;
148  int accumulate;
149  struct openvpn_tcphdr *tc;
150 
151  if (BLEN(buf) < (int) sizeof(struct openvpn_tcphdr))
152  {
153  return;
154  }
155 
156  verify_align_4(buf);
157  tc = (struct openvpn_tcphdr *) BPTR(buf);
158  hlen = OPENVPN_TCPH_GET_DOFF(tc->doff_res);
159 
160  /* Invalid header length or header without options. */
161  if (hlen <= (int) sizeof(struct openvpn_tcphdr)
162  || hlen > BLEN(buf))
163  {
164  return;
165  }
166 
167  for (olen = hlen - sizeof(struct openvpn_tcphdr),
168  opt = (uint8_t *)(tc + 1);
169  olen > 1;
170  olen -= optlen, opt += optlen)
171  {
172  if (*opt == OPENVPN_TCPOPT_EOL)
173  {
174  break;
175  }
176  else if (*opt == OPENVPN_TCPOPT_NOP)
177  {
178  optlen = 1;
179  }
180  else
181  {
182  optlen = *(opt + 1);
183  if (optlen <= 0 || optlen > olen)
184  {
185  break;
186  }
187  if (*opt == OPENVPN_TCPOPT_MAXSEG)
188  {
189  if (optlen != OPENVPN_TCPOLEN_MAXSEG)
190  {
191  continue;
192  }
193  mssval = (opt[2]<<8)+opt[3];
194  if (mssval > maxmss)
195  {
196  dmsg(D_MSS, "MSS: %d -> %d", (int) mssval, (int) maxmss);
197  accumulate = htons(mssval);
198  opt[2] = (maxmss>>8)&0xff;
199  opt[3] = maxmss&0xff;
200  accumulate -= htons(maxmss);
201  ADJUST_CHECKSUM(accumulate, tc->check);
202  }
203  }
204  }
205  }
206 }
uint8_t version_len
Definition: proto.h:87
uint16_t check
Definition: proto.h:157
uint8_t flags
Definition: proto.h:154
#define OPENVPN_IPH_GET_LEN(v)
Definition: proto.h:86
uint8_t protocol
Definition: proto.h:101
#define D_MSS
Definition: errlevel.h:124
uint16_t frag_off
Definition: proto.h:94
static bool buf_advance(struct buffer *buf, int size)
Definition: buffer.h:638
void mss_fixup_ipv6(struct buffer *buf, int maxmss)
Definition: mss.c:86
#define OPENVPN_TCPH_SYN_MASK
Definition: proto.h:147
#define OPENVPN_TCPOLEN_MAXSEG
Definition: proto.h:164
void mss_fixup_dowork(struct buffer *buf, uint16_t maxmss)
Definition: mss.c:143
#define OPENVPN_IPPROTO_TCP
Definition: proto.h:99
#define OPENVPN_IP_OFFMASK
Definition: proto.h:93
#define OPENVPN_TCPOPT_NOP
Definition: proto.h:162
#define BPTR(buf)
Definition: buffer.h:124
#define ADJUST_CHECKSUM(acc, cksum)
Definition: proto.h:184
#define OPENVPN_TCPOPT_EOL
Definition: proto.h:161
uint8_t nexthdr
Definition: proto.h:116
#define dmsg
Definition: error.h:174
#define BLEN(buf)
Definition: buffer.h:127
unsigned __int8 uint8_t
Definition: config-msvc.h:123
uint16_t tot_len
Definition: proto.h:90
#define OPENVPN_TCPH_GET_DOFF(d)
Definition: proto.h:143
#define OPENVPN_TCPOPT_MAXSEG
Definition: proto.h:163
Wrapper structure for dynamically allocated memory.
Definition: buffer.h:60
#define verify_align_4(ptr)
Definition: buffer.h:980
unsigned __int16 uint16_t
Definition: config-msvc.h:122
uint8_t doff_res
Definition: proto.h:144
void mss_fixup_ipv4(struct buffer *buf, int maxmss)
Definition: mss.c:46
uint16_t payload_len
Definition: proto.h:115