mirror of
https://github.com/OpenVPN/openvpn3.git
synced 2024-09-20 12:12:15 +02:00
[OVPN3-342] Generate ICMP "packet too big" reply
When receiving packed from tun which size exceeds mssfix value minus encap overhead, send ICMP "destination unreachable" / "fragmentation needed" (for IPv4) or "packet too big" (for IPv6) response. This is required for non-TCP based protocols, since for TCP we alter MSS in SYN segments. Signed-off-by: Lev Stipakov <lev@openvpn.net>
This commit is contained in:
parent
c93af60a77
commit
0c0af6781e
@ -49,6 +49,7 @@
|
||||
#include <openvpn/common/count.hpp>
|
||||
#include <openvpn/common/string.hpp>
|
||||
#include <openvpn/common/base64.hpp>
|
||||
#include <openvpn/ip/ptb.hpp>
|
||||
#include <openvpn/tun/client/tunbase.hpp>
|
||||
#include <openvpn/transport/client/transbase.hpp>
|
||||
#include <openvpn/transport/client/relay.hpp>
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// IP checksum based on Linux kernel implementation
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <openvpn/common/endian.hpp>
|
||||
#include <openvpn/common/socktypes.hpp>
|
||||
#include <openvpn/common/size.hpp>
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
#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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
#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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
136
openvpn/ip/ptb.hpp
Normal file
136
openvpn/ip/ptb.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Generates ICMP "packet too big" response
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openvpn/common/socktypes.hpp>
|
||||
#include <openvpn/ip/csum.hpp>
|
||||
#include <openvpn/ip/ip4.hpp>
|
||||
#include <openvpn/ip/ip6.hpp>
|
||||
#include <openvpn/ip/icmp4.hpp>
|
||||
#include <openvpn/ip/icmp6.hpp>
|
||||
#include <openvpn/ip/ping6.hpp>
|
||||
#include <openvpn/ip/ipcommon.hpp>
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
}
|
@ -233,6 +233,7 @@
|
||||
<ClInclude Include="..\openvpn\init\engineinit.hpp" />
|
||||
<ClInclude Include="..\openvpn\init\initprocess.hpp" />
|
||||
<ClInclude Include="..\openvpn\io\io.hpp" />
|
||||
<ClInclude Include="..\openvpn\ip\csum.hpp" />
|
||||
<ClInclude Include="..\openvpn\ip\dhcp.hpp" />
|
||||
<ClInclude Include="..\openvpn\ip\eth.hpp" />
|
||||
<ClInclude Include="..\openvpn\ip\icmp4.hpp" />
|
||||
@ -240,6 +241,9 @@
|
||||
<ClInclude Include="..\openvpn\ip\ip4.hpp" />
|
||||
<ClInclude Include="..\openvpn\ip\ip6.hpp" />
|
||||
<ClInclude Include="..\openvpn\ip\ipcommon.hpp" />
|
||||
<ClInclude Include="..\openvpn\ip\ping4.hpp" />
|
||||
<ClInclude Include="..\openvpn\ip\ping6.hpp" />
|
||||
<ClInclude Include="..\openvpn\ip\ptb.hpp" />
|
||||
<ClInclude Include="..\openvpn\ip\tcp.hpp" />
|
||||
<ClInclude Include="..\openvpn\ip\udp.hpp" />
|
||||
<ClInclude Include="..\openvpn\kovpn\kocrypto.hpp" />
|
||||
|
@ -424,6 +424,10 @@
|
||||
<ClInclude Include="..\client\ovpncli.hpp" />
|
||||
<ClInclude Include="..\openvpn\transport\mssfix.hpp" />
|
||||
<ClInclude Include="..\openvpn\ip\tcp.hpp" />
|
||||
<ClInclude Include="..\openvpn\ip\ptb.hpp" />
|
||||
<ClInclude Include="..\openvpn\ip\ping4.hpp" />
|
||||
<ClInclude Include="..\openvpn\ip\ping6.hpp" />
|
||||
<ClInclude Include="..\openvpn\ip\csum.hpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\test\ovpncli\cli.cpp" />
|
||||
|
Loading…
Reference in New Issue
Block a user