diff options
Diffstat (limited to 'ics-openvpn-stripped/main/openvpn/src/openvpn/mss.c')
-rw-r--r-- | ics-openvpn-stripped/main/openvpn/src/openvpn/mss.c | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/ics-openvpn-stripped/main/openvpn/src/openvpn/mss.c b/ics-openvpn-stripped/main/openvpn/src/openvpn/mss.c new file mode 100644 index 00000000..64fd722f --- /dev/null +++ b/ics-openvpn-stripped/main/openvpn/src/openvpn/mss.c @@ -0,0 +1,175 @@ +/* + * OpenVPN -- An application to securely tunnel IP networks + * over a single TCP/UDP port, with support for SSL/TLS-based + * session authentication and key exchange, + * packet encryption, packet authentication, and + * packet compression. + * + * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (see the file COPYING included with this + * distribution); if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#elif defined(_MSC_VER) +#include "config-msvc.h" +#endif + +#include "syshead.h" +#include "error.h" +#include "mss.h" +#include "memdbg.h" + +/* + * Lower MSS on TCP SYN packets to fix MTU + * problems which arise from protocol + * encapsulation. + */ + +/* + * IPv4 packet: find TCP header, check flags for "SYN" + * if yes, hand to mss_fixup_dowork() + */ +void +mss_fixup_ipv4 (struct buffer *buf, int maxmss) +{ + const struct openvpn_iphdr *pip; + int hlen; + + if (BLEN (buf) < (int) sizeof (struct openvpn_iphdr)) + return; + + verify_align_4 (buf); + pip = (struct openvpn_iphdr *) BPTR (buf); + + hlen = OPENVPN_IPH_GET_LEN (pip->version_len); + + if (pip->protocol == OPENVPN_IPPROTO_TCP + && ntohs (pip->tot_len) == BLEN (buf) + && (ntohs (pip->frag_off) & OPENVPN_IP_OFFMASK) == 0 + && hlen <= BLEN (buf) + && BLEN (buf) - hlen + >= (int) sizeof (struct openvpn_tcphdr)) + { + struct buffer newbuf = *buf; + if (buf_advance (&newbuf, hlen)) + { + struct openvpn_tcphdr *tc = (struct openvpn_tcphdr *) BPTR (&newbuf); + if (tc->flags & OPENVPN_TCPH_SYN_MASK) + mss_fixup_dowork (&newbuf, (uint16_t) maxmss); + } + } +} + +/* + * IPv6 packet: find TCP header, check flags for "SYN" + * if yes, hand to mss_fixup_dowork() + * (IPv6 header structure is sufficiently different from IPv4...) + */ +void +mss_fixup_ipv6 (struct buffer *buf, int maxmss) +{ + const struct openvpn_ipv6hdr *pip6; + struct buffer newbuf; + + if (BLEN (buf) < (int) sizeof (struct openvpn_ipv6hdr)) + return; + + verify_align_4 (buf); + pip6 = (struct openvpn_ipv6hdr *) BPTR (buf); + + /* do we have the full IPv6 packet? + * "payload_len" does not include IPv6 header (+40 bytes) + */ + if (BLEN (buf) != (int) ntohs(pip6->payload_len)+40 ) + return; + + /* follow header chain until we reach final header, then check for TCP + * + * An IPv6 packet could, theoretically, have a chain of multiple headers + * before the final header (TCP, UDP, ...), so we'd need to walk that + * chain (see RFC 2460 and RFC 6564 for details). + * + * In practice, "most typically used" extention headers (AH, routing, + * fragment, mobility) are very unlikely to be seen inside an OpenVPN + * tun, so for now, we only handle the case of "single next header = TCP" + */ + if ( pip6->nexthdr != OPENVPN_IPPROTO_TCP ) + return; + + newbuf = *buf; + if ( buf_advance( &newbuf, 40 ) ) + { + struct openvpn_tcphdr *tc = (struct openvpn_tcphdr *) BPTR (&newbuf); + if (tc->flags & OPENVPN_TCPH_SYN_MASK) + mss_fixup_dowork (&newbuf, (uint16_t) maxmss-20); + } +} + +/* + * change TCP MSS option in SYN/SYN-ACK packets, if present + * this is generic for IPv4 and IPv6, as the TCP header is the same + */ + +void +mss_fixup_dowork (struct buffer *buf, uint16_t maxmss) +{ + int hlen, olen, optlen; + uint8_t *opt; + uint16_t *mss; + int accumulate; + struct openvpn_tcphdr *tc; + + ASSERT (BLEN (buf) >= (int) sizeof (struct openvpn_tcphdr)); + + verify_align_4 (buf); + tc = (struct openvpn_tcphdr *) BPTR (buf); + hlen = OPENVPN_TCPH_GET_DOFF (tc->doff_res); + + /* Invalid header length or header without options. */ + if (hlen <= (int) sizeof (struct openvpn_tcphdr) + || hlen > BLEN (buf)) + return; + + for (olen = hlen - sizeof (struct openvpn_tcphdr), + opt = (uint8_t *)(tc + 1); + olen > 0; + olen -= optlen, opt += optlen) { + if (*opt == OPENVPN_TCPOPT_EOL) + break; + else if (*opt == OPENVPN_TCPOPT_NOP) + optlen = 1; + else { + optlen = *(opt + 1); + if (optlen <= 0 || optlen > olen) + break; + if (*opt == OPENVPN_TCPOPT_MAXSEG) { + if (optlen != OPENVPN_TCPOLEN_MAXSEG) + continue; + mss = (uint16_t *)(opt + 2); + if (ntohs (*mss) > maxmss) { + dmsg (D_MSS, "MSS: %d -> %d", + (int) ntohs (*mss), + (int) maxmss); + accumulate = *mss; + *mss = htons (maxmss); + accumulate -= *mss; + ADJUST_CHECKSUM (accumulate, tc->check); + } + } + } + } +} |