• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/openvpn/src/openvpn/
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-2010 OpenVPN Technologies, 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
20 *  along with this program (see the file COPYING included with this
21 *  distribution); if not, write to the Free Software Foundation, Inc.,
22 *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23 */
24
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#elif defined(_MSC_VER)
28#include "config-msvc.h"
29#endif
30
31#include "syshead.h"
32#include "error.h"
33#include "mss.h"
34#include "memdbg.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 */
46void
47mss_fixup_ipv4 (struct buffer *buf, int maxmss)
48{
49  const struct openvpn_iphdr *pip;
50  int hlen;
51
52  if (BLEN (buf) < (int) sizeof (struct openvpn_iphdr))
53    return;
54
55  verify_align_4 (buf);
56  pip = (struct openvpn_iphdr *) BPTR (buf);
57
58  hlen = OPENVPN_IPH_GET_LEN (pip->version_len);
59
60  if (pip->protocol == OPENVPN_IPPROTO_TCP
61      && ntohs (pip->tot_len) == BLEN (buf)
62      && (ntohs (pip->frag_off) & OPENVPN_IP_OFFMASK) == 0
63      && hlen <= BLEN (buf)
64      && BLEN (buf) - hlen
65         >= (int) sizeof (struct openvpn_tcphdr))
66    {
67      struct buffer newbuf = *buf;
68      if (buf_advance (&newbuf, hlen))
69	{
70	  struct openvpn_tcphdr *tc = (struct openvpn_tcphdr *) BPTR (&newbuf);
71	  if (tc->flags & OPENVPN_TCPH_SYN_MASK)
72	    mss_fixup_dowork (&newbuf, (uint16_t) maxmss);
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, int maxmss)
84{
85  const struct openvpn_ipv6hdr *pip6;
86  struct buffer newbuf;
87
88  if (BLEN (buf) < (int) sizeof (struct openvpn_ipv6hdr))
89    return;
90
91  verify_align_4 (buf);
92  pip6 = (struct openvpn_ipv6hdr *) BPTR (buf);
93
94  /* do we have the full IPv6 packet?
95   * "payload_len" does not include IPv6 header (+40 bytes)
96   */
97  if (BLEN (buf) != (int) ntohs(pip6->payload_len)+40 )
98    return;
99
100  /* follow header chain until we reach final header, then check for TCP
101   *
102   * An IPv6 packet could, theoretically, have a chain of multiple headers
103   * before the final header (TCP, UDP, ...), so we'd need to walk that
104   * chain (see RFC 2460 and RFC 6564 for details).
105   *
106   * In practice, "most typically used" extention headers (AH, routing,
107   * fragment, mobility) are very unlikely to be seen inside an OpenVPN
108   * tun, so for now, we only handle the case of "single next header = TCP"
109   */
110  if ( pip6->nexthdr != OPENVPN_IPPROTO_TCP )
111    return;
112
113  newbuf = *buf;
114  if ( buf_advance( &newbuf, 40 ) )
115    {
116      struct openvpn_tcphdr *tc = (struct openvpn_tcphdr *) BPTR (&newbuf);
117      if (tc->flags & OPENVPN_TCPH_SYN_MASK)
118	    mss_fixup_dowork (&newbuf, (uint16_t) maxmss-20);
119    }
120}
121
122/*
123 * change TCP MSS option in SYN/SYN-ACK packets, if present
124 * this is generic for IPv4 and IPv6, as the TCP header is the same
125 */
126
127void
128mss_fixup_dowork (struct buffer *buf, uint16_t maxmss)
129{
130  int hlen, olen, optlen;
131  uint8_t *opt;
132  uint16_t *mss;
133  int accumulate;
134  struct openvpn_tcphdr *tc;
135
136  ASSERT (BLEN (buf) >= (int) sizeof (struct openvpn_tcphdr));
137
138  verify_align_4 (buf);
139  tc = (struct openvpn_tcphdr *) BPTR (buf);
140  hlen = OPENVPN_TCPH_GET_DOFF (tc->doff_res);
141
142  /* Invalid header length or header without options. */
143  if (hlen <= (int) sizeof (struct openvpn_tcphdr)
144      || hlen > BLEN (buf))
145    return;
146
147  for (olen = hlen - sizeof (struct openvpn_tcphdr),
148	 opt = (uint8_t *)(tc + 1);
149       olen > 0;
150       olen -= optlen, opt += optlen) {
151    if (*opt == OPENVPN_TCPOPT_EOL)
152      break;
153    else if (*opt == OPENVPN_TCPOPT_NOP)
154      optlen = 1;
155    else {
156      optlen = *(opt + 1);
157      if (optlen <= 0 || optlen > olen)
158        break;
159      if (*opt == OPENVPN_TCPOPT_MAXSEG) {
160        if (optlen != OPENVPN_TCPOLEN_MAXSEG)
161          continue;
162        mss = (uint16_t *)(opt + 2);
163        if (ntohs (*mss) > maxmss) {
164          dmsg (D_MSS, "MSS: %d -> %d",
165               (int) ntohs (*mss),
166	       (int) maxmss);
167          accumulate = *mss;
168          *mss = htons (maxmss);
169          accumulate -= *mss;
170          ADJUST_CHECKSUM (accumulate, tc->check);
171        }
172      }
173    }
174  }
175}
176