diff --git a/openvpn/client/cliproto.hpp b/openvpn/client/cliproto.hpp index db5072de..e7b2f5fd 100644 --- a/openvpn/client/cliproto.hpp +++ b/openvpn/client/cliproto.hpp @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -376,15 +377,24 @@ namespace openvpn { // encrypt packet if (buf.size()) { - Base::data_encrypt(buf); - if (buf.size()) + const ProtoContext::Config& c = Base::conf(); + if (c.mss_inter > 0 && buf.size() > c.mss_inter) { - // send packet via transport to destination - OPENVPN_LOG_CLIPROTO("Transport SEND " << server_endpoint_render() << ' ' << Base::dump_packet(buf)); - if (transport->transport_send(buf)) - Base::update_last_sent(); - else if (halt) - return; + Ptb::generate_icmp_ptb(buf, c.mss_inter); + tun->tun_send(buf); + } + else + { + Base::data_encrypt(buf); + if (buf.size()) + { + // send packet via transport to destination + OPENVPN_LOG_CLIPROTO("Transport SEND " << server_endpoint_render() << ' ' << Base::dump_packet(buf)); + if (transport->transport_send(buf)) + Base::update_last_sent(); + else if (halt) + return; + } } } diff --git a/openvpn/ip/csum.hpp b/openvpn/ip/csum.hpp index 671fb7e7..87b64c36 100644 --- a/openvpn/ip/csum.hpp +++ b/openvpn/ip/csum.hpp @@ -1,9 +1,31 @@ +// OpenVPN -- An application to securely tunnel IP networks +// over a single port, with support for SSL/TLS-based +// session authentication and key exchange, +// packet encryption, packet authentication, and +// packet compression. +// +// Copyright (C) 2012-2018 OpenVPN Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License Version 3 +// 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program in the COPYING file. +// If not, see . + // IP checksum based on Linux kernel implementation #pragma once #include +#include #include #include @@ -42,7 +64,7 @@ namespace openvpn { const bool odd = size_t(buf) & 1; if (odd) { -#ifdef __LITTLE_ENDIAN +#ifdef OPENVPN_LITTLE_ENDIAN result += (*buf << 8); #else result = *buf; @@ -81,7 +103,7 @@ namespace openvpn { } if (len & 1) { -#ifdef __LITTLE_ENDIAN +#ifdef OPENVPN_LITTLE_ENDIAN result += *buf; #else result += (*buf << 8); @@ -146,4 +168,4 @@ namespace openvpn { return cfold(compute(data, size)); } } -} +} \ No newline at end of file diff --git a/openvpn/ip/icmp4.hpp b/openvpn/ip/icmp4.hpp index d08c0286..2d20ade8 100644 --- a/openvpn/ip/icmp4.hpp +++ b/openvpn/ip/icmp4.hpp @@ -33,8 +33,11 @@ namespace openvpn { struct ICMPv4 { enum { - ECHO_REQUEST = 8, - ECHO_REPLY = 0, + ECHO_REQUEST = 8, + ECHO_REPLY = 0, + DEST_UNREACH = 3, + FRAG_NEEDED = 4, + MIN_DATA_SIZE = 8 }; struct IPv4Header head; @@ -53,6 +56,10 @@ namespace openvpn { std::uint16_t id; std::uint16_t seq_num; }; + struct { + std::uint16_t unused; + std::uint16_t nexthop_mtu; + }; }; }; } diff --git a/openvpn/ip/icmp6.hpp b/openvpn/ip/icmp6.hpp index c23e03ed..2813004a 100644 --- a/openvpn/ip/icmp6.hpp +++ b/openvpn/ip/icmp6.hpp @@ -34,8 +34,9 @@ namespace openvpn { struct ICMPv6 { enum { - ECHO_REQUEST = 128, - ECHO_REPLY = 129, + ECHO_REQUEST = 128, + ECHO_REPLY = 129, + PACKET_TOO_BIG = 2 }; struct IPv6Header head; @@ -54,6 +55,7 @@ namespace openvpn { std::uint16_t id; std::uint16_t seq_num; }; + std::uint32_t mtu; }; }; } diff --git a/openvpn/ip/ping4.hpp b/openvpn/ip/ping4.hpp index 5d232e70..50eaaffa 100644 --- a/openvpn/ip/ping4.hpp +++ b/openvpn/ip/ping4.hpp @@ -1,7 +1,23 @@ -// OpenVPN +// OpenVPN -- An application to securely tunnel IP networks +// over a single port, with support for SSL/TLS-based +// session authentication and key exchange, +// packet encryption, packet authentication, and +// packet compression. // -// Copyright (C) 2012-2017 OpenVPN Technologies, Inc. -// All rights reserved. +// Copyright (C) 2012-2018 OpenVPN Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License Version 3 +// 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program in the COPYING file. +// If not, see . #pragma once @@ -94,4 +110,4 @@ namespace openvpn { *log_info = "ECHO4_REPLY size=" + std::to_string(buf.size()) + ' ' + IPv4::Addr::from_uint32_net(icmp->head.saddr).to_string() + " -> " + IPv4::Addr::from_uint32_net(icmp->head.daddr).to_string(); } } -} +} \ No newline at end of file diff --git a/openvpn/ip/ping6.hpp b/openvpn/ip/ping6.hpp index 13f05304..19933eeb 100644 --- a/openvpn/ip/ping6.hpp +++ b/openvpn/ip/ping6.hpp @@ -1,7 +1,23 @@ -// OpenVPN +// OpenVPN -- An application to securely tunnel IP networks +// over a single port, with support for SSL/TLS-based +// session authentication and key exchange, +// packet encryption, packet authentication, and +// packet compression. // -// Copyright (C) 2012-2017 OpenVPN Technologies, Inc. -// All rights reserved. +// Copyright (C) 2012-2018 OpenVPN Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License Version 3 +// 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program in the COPYING file. +// If not, see . #pragma once @@ -20,45 +36,43 @@ namespace openvpn { namespace Ping6 { + inline static const std::uint16_t* get_addr16(const struct in6_addr *addr) + { +#if defined(_MSC_VER) + return addr->u.Word; +#elif defined(__APPLE__) + return addr->__u6_addr.__u6_addr16; +#else + return addr->s6_addr16; +#endif + } + inline std::uint16_t csum_ipv6_pseudo(const struct in6_addr *saddr, const struct in6_addr *daddr, const std::uint32_t len, const std::uint16_t proto, std::uint32_t sum) { - int carry; + int carry = 0; + std::uint32_t val = 0; - sum += (std::uint32_t)saddr->s6_addr32[0]; - carry = (sum < (std::uint32_t)saddr->s6_addr32[0]); - sum += carry; + const std::uint16_t* addr = get_addr16(saddr); + for (int i = 0; i < 4; ++i) + { + val = (std::uint32_t)(addr[i * 2] << 16) + addr[i * 2 + 1]; + sum += val; + carry = (sum < val); + sum += carry; + } - sum += (std::uint32_t)saddr->s6_addr32[1]; - carry = (sum < (std::uint32_t)saddr->s6_addr32[1]); - sum += carry; - - sum += (std::uint32_t)saddr->s6_addr32[2]; - carry = (sum < (std::uint32_t)saddr->s6_addr32[2]); - sum += carry; - - sum += (std::uint32_t)saddr->s6_addr32[3]; - carry = (sum < (std::uint32_t)saddr->s6_addr32[3]); - sum += carry; - - sum += (std::uint32_t)daddr->s6_addr32[0]; - carry = (sum < (std::uint32_t)daddr->s6_addr32[0]); - sum += carry; - - sum += (std::uint32_t)daddr->s6_addr32[1]; - carry = (sum < (std::uint32_t)daddr->s6_addr32[1]); - sum += carry; - - sum += (std::uint32_t)daddr->s6_addr32[2]; - carry = (sum < (std::uint32_t)daddr->s6_addr32[2]); - sum += carry; - - sum += (std::uint32_t)daddr->s6_addr32[3]; - carry = (sum < (std::uint32_t)daddr->s6_addr32[3]); - sum += carry; + addr = get_addr16(daddr); + for (int i = 0; i < 4; ++i) + { + val = (std::uint32_t)(addr[i * 2] << 16) + addr[i * 2 + 1]; + sum += val; + carry = (sum < val); + sum += carry; + } const std::uint32_t ulen = (std::uint32_t)htonl((std::uint32_t) len); sum += ulen; @@ -154,4 +168,4 @@ namespace openvpn { *log_info = "ECHO6_REPLY size=" + std::to_string(buf.size()) + ' ' + IPv6::Addr::from_in6_addr(&icmp->head.saddr).to_string() + " -> " + IPv6::Addr::from_in6_addr(&icmp->head.daddr).to_string(); } } -} +} \ No newline at end of file diff --git a/openvpn/ip/ptb.hpp b/openvpn/ip/ptb.hpp new file mode 100644 index 00000000..1d73d281 --- /dev/null +++ b/openvpn/ip/ptb.hpp @@ -0,0 +1,136 @@ +// OpenVPN -- An application to securely tunnel IP networks +// over a single port, with support for SSL/TLS-based +// session authentication and key exchange, +// packet encryption, packet authentication, and +// packet compression. +// +// Copyright (C) 2012-2017 OpenVPN Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License Version 3 +// 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program in the COPYING file. +// If not, see . + +// Generates ICMP "packet too big" response + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace openvpn { + class Ptb { + public: + static void generate_icmp_ptb(BufferAllocated& buf, std::uint16_t nexthop_mtu) + { + if (buf.empty()) + return; + + switch (IPCommon::version(buf[0])) + { + case IPCommon::IPv4: + if (buf.length() <= sizeof(struct IPv4Header)) + break; + + generate_icmp4_ptb(buf, nexthop_mtu); + break; + + case IPCommon::IPv6: + if (buf.length() <= sizeof(struct IPv6Header)) + break; + + generate_icmp6_ptb(buf, nexthop_mtu); + break; + } + } + + private: + static void generate_icmp6_ptb(BufferAllocated& buf, std::uint16_t nexthop_mtu) + { + // ICMPv6 data includes original IPv6 header and as many bytes of payload as possible + int data_size = std::min(buf.length(), (size_t)(nexthop_mtu - sizeof(ICMPv6))); + + // sanity check + // we use headroom for adding IPv6 + ICMPv6 headers + if ((buf.offset() < sizeof(ICMPv6)) || (buf.capacity() < (sizeof(ICMPv6) + data_size))) + return; + + IPv6Header* ipv6 = (IPv6Header*)buf.c_data(); + + uint8_t *b = buf.prepend_alloc(sizeof(ICMPv6)); + ICMPv6 *icmp = (ICMPv6 *)b; + + // IPv6 header + icmp->head.version_prio = (6 << 4); + icmp->head.flow_lbl[0] = 0; + icmp->head.flow_lbl[1] = 0; + icmp->head.flow_lbl[2] = 0; + icmp->head.payload_len = htons(sizeof(ICMPv6) - sizeof(IPv6Header) + data_size); + icmp->head.nexthdr = IPCommon::ICMPv6; + icmp->head.hop_limit = 64; + icmp->head.saddr = ipv6->daddr; + icmp->head.daddr = ipv6->saddr; + + // ICMP header + icmp->type = ICMPv6::PACKET_TOO_BIG; + icmp->code = 0; + icmp->mtu = htonl(nexthop_mtu); + icmp->checksum = 0; + icmp->checksum = Ping6::csum_icmp(icmp, sizeof(ICMPv6) + data_size); + + buf.set_size(sizeof(ICMPv6) + data_size); + } + + static void generate_icmp4_ptb(BufferAllocated& buf, std::uint16_t nexthop_mtu) + { + // ICMP data includes original IP header and first 8 bytes of payload + int data_size = sizeof(IPv4Header) + ICMPv4::MIN_DATA_SIZE; + + // sanity check + // we use headroom for adding IPv4 + ICMPv4 headers + if ((buf.offset() < sizeof(ICMPv4)) || (buf.capacity() < (sizeof(ICMPv4) + data_size))) + return; + + IPv4Header* ipv4 = (IPv4Header*)buf.c_data(); + + uint8_t *b = buf.prepend_alloc(sizeof(ICMPv4)); + ICMPv4 *icmp = (ICMPv4 *)b; + + icmp->head.saddr = ipv4->daddr; + icmp->head.daddr = ipv4->saddr; + icmp->head.version_len = IPv4Header::ver_len(IPCommon::IPv4, sizeof(IPv4Header)); + icmp->head.tos = 0; + icmp->head.tot_len = htons(sizeof(ICMPv4) + data_size); + icmp->head.id = 0; + icmp->head.frag_off = 0; + icmp->head.ttl = 64; + icmp->head.protocol = IPCommon::ICMPv4; + icmp->head.check = 0; + icmp->head.check = IPChecksum::checksum(b, sizeof(IPv4Header)); + + icmp->type = ICMPv4::DEST_UNREACH; + icmp->code = ICMPv4::FRAG_NEEDED; + icmp->unused = 0; + icmp->nexthop_mtu = htons(nexthop_mtu); + icmp->checksum = 0; + icmp->checksum = IPChecksum::checksum(b + sizeof(IPv4Header), sizeof(ICMPv4) - sizeof(IPv4Header) + data_size); + + buf.set_size(sizeof(ICMPv4) + data_size); + } + }; +} diff --git a/win/ovpn3-core.vcxproj b/win/ovpn3-core.vcxproj index 9f826847..040f60c1 100644 --- a/win/ovpn3-core.vcxproj +++ b/win/ovpn3-core.vcxproj @@ -233,6 +233,7 @@ + @@ -240,6 +241,9 @@ + + + diff --git a/win/ovpn3-core.vcxproj.filters b/win/ovpn3-core.vcxproj.filters index b57ff858..a6ce3b75 100644 --- a/win/ovpn3-core.vcxproj.filters +++ b/win/ovpn3-core.vcxproj.filters @@ -424,6 +424,10 @@ + + + +