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