0
0
mirror of https://github.com/OpenVPN/openvpn3.git synced 2024-09-19 19:52:15 +02:00

support for default mssfix

Add support for default mssfix, which is calculated
based on upper bound value 1492 minus payload and
encapculation overhead.

Payload overhead includes:

 - compression byte (except for V2, which doesn't add overhead)
 - pktid (in CBC)
 - IPv4 and TCP headers

Encapculation overhead:

 - crypto overhead (for AEAD 16 bytes auth tag, 4 bytes pktid, 4 bytes opcode/peer-id = 24)
 - 2 bytes packet size for TCP transport

Also for CBC we must take padding [1..blocksize] into account.

Signed-off-by: Lev Stipakov <lev@openvpn.net>
This commit is contained in:
Lev Stipakov 2022-04-28 16:18:03 +03:00
parent b7c6e2d5af
commit ac15879588
9 changed files with 114 additions and 50 deletions

View File

@ -381,9 +381,12 @@ namespace openvpn {
if (buf.size())
{
const ProtoContext::Config& c = Base::conf();
if (c.mss_inter > 0 && buf.size() > c.mss_inter)
// when calculating mss, we take IPv4 and TCP headers into account
// here we need to add it back since we check the whole IP packet size, not just TCP payload
size_t mss_no_tcp_ip_encap = (size_t)c.mss_fix + (20 + 20);
if (c.mss_fix > 0 && buf.size() > mss_no_tcp_ip_encap)
{
Ptb::generate_icmp_ptb(buf, c.mss_inter);
Ptb::generate_icmp_ptb(buf, mss_no_tcp_ip_encap);
tun->tun_send(buf);
}
else

View File

@ -32,6 +32,7 @@
#include <openvpn/time/time.hpp>
#include <openvpn/transport/client/transbase.hpp>
#include <openvpn/transport/dco.hpp>
#include <openvpn/tun/tunmtu.hpp>
#include <openvpn/tun/builder/capture.hpp>
#include <openvpn/tun/client/tunbase.hpp>
@ -93,7 +94,7 @@ public:
// set a default MTU
if (!tun.tun_prop.mtu)
tun.tun_prop.mtu = 1500;
tun.tun_prop.mtu = TUN_MTU_DEFAULT;
// parse "dev" option
{

View File

@ -28,10 +28,9 @@
namespace openvpn {
struct MSSParms
{
MSSParms() : mssfix(0),
mtu(false)
{
}
enum {
MSSFIX_DEFAULT = 1492,
};
void parse(const OptionList& opt, bool nothrow=false)
{
@ -44,6 +43,7 @@ namespace openvpn {
if (nothrow)
{
OPENVPN_LOG("Missing mssfix value, mssfix functionality disabled");
mssfix_default = false;
return;
}
else
@ -63,17 +63,25 @@ namespace openvpn {
if (*val != "0")
{
OPENVPN_LOG("Invalid mssfix value " << *val << ", mssfix functionality disabled");
mssfix_default = false;
}
}
else
throw option_error("mssfix: parse/range issue");
}
else
{
mssfix_default = false;
}
mtu = (o->get_optional(2, 16) == "mtu");
fixed = (o->get_optional(2, 16) == "fixed");
}
}
unsigned int mssfix; // standard OpenVPN mssfix parm
bool mtu; // consider transport packet overhead in MSS adjustment
unsigned int mssfix = 0; // standard OpenVPN mssfix parm
bool mtu = false; // include overhead from IP and TCP/UDP encapsulation
bool fixed = false; // use mssfix value without any encapsulation adjustments
bool mssfix_default = true;
};
struct MSSCtrlParms

View File

@ -351,9 +351,9 @@ namespace openvpn {
int local_peer_id = -1; // -1 to disable
// MTU
unsigned int tun_mtu = 1500;
unsigned int tun_mtu = TUN_MTU_DEFAULT;
MSSParms mss_parms;
unsigned int mss_inter = 0;
unsigned int mss_fix = 0;
// Debugging
int debug_level = 1;
@ -571,6 +571,19 @@ namespace openvpn {
// mssfix
mss_parms.parse(opt, true);
if (mss_parms.mssfix_default)
{
if (tun_mtu == TUN_MTU_DEFAULT)
{
mss_parms.mssfix = MSSParms::MSSFIX_DEFAULT;
mss_parms.mtu = true;
}
else
{
mss_parms.mssfix = tun_mtu;
mss_parms.fixed = true;
}
}
// load parameters that can be present in both config file or pushed options
load_common(opt, pco, server ? LOAD_COMMON_SERVER : LOAD_COMMON_CLIENT);
@ -1636,8 +1649,8 @@ namespace openvpn {
compress->decompress(buf);
// set MSS for segments server can receive
if (proto.config->mss_inter > 0)
MSSFix::mssfix(buf, proto.config->mss_inter);
if (proto.config->mss_fix > 0)
MSSFix::mssfix(buf, proto.config->mss_fix);
}
else
buf.reset_size(); // no crypto context available
@ -1830,6 +1843,67 @@ namespace openvpn {
dck.swap(data_channel_key);
}
void calculate_mssfix(Config& c)
{
if (c.mss_parms.fixed)
{
// substract IPv4 and TCP overhead, mssfix method will add extra 20 bytes for IPv6
c.mss_fix = c.mss_parms.mssfix - (20 + 20);
OPENVPN_LOG("fixed mssfix=" << c.mss_fix);
return;
}
int payload_overhead = 0;
// compv2 doesn't increase payload size
switch (c.comp_ctx.type())
{
case CompressContext::NONE:
case CompressContext::COMP_STUBv2:
case CompressContext::LZ4v2:
break;
default:
payload_overhead += 1;
}
if (CryptoAlgs::mode(c.dc.cipher()) == CryptoAlgs::CBC_HMAC)
payload_overhead += PacketID::size(PacketID::SHORT_FORM);
// account for IPv4 and TCP headers of the payload, mssfix method will add 20 extra bytes if payload is IPv6
payload_overhead += 20 + 20;
int overhead = c.protocol.extra_transport_bytes() +
(enable_op32 ? OP_SIZE_V2 : 1) +
c.dc.context().encap_overhead();
// in CBC mode, the packet id is part of the payload size / overhead
if (CryptoAlgs::mode(c.dc.cipher()) != CryptoAlgs::CBC_HMAC)
overhead += PacketID::size(PacketID::SHORT_FORM);
if (c.mss_parms.mtu)
{
overhead += c.protocol.is_ipv6() ? sizeof(struct IPv6Header) : sizeof(struct IPv4Header);
overhead += proto.is_tcp() ? sizeof(struct TCPHeader) : sizeof(struct UDPHeader);
}
int target = c.mss_parms.mssfix - overhead;
if (CryptoAlgs::mode(c.dc.cipher()) == CryptoAlgs::CBC_HMAC)
{
// openvpn3 crypto includes blocksize in overhead, but we can be a bit smarter here
// and instead make sure that resulting ciphertext size (which is always multiple blocksize)
// is not larger than target by running down target to the nearest multiple of multiple and substracting 1.
int block_size = CryptoAlgs::block_size(c.dc.cipher());
target += block_size;
target = (target / block_size) * block_size;
target -= 1;
}
c.mss_fix = target - payload_overhead;
OPENVPN_LOG("mssfix=" << c.mss_fix << " (upper bound=" << c.mss_parms.mssfix << ", overhead=" <<
overhead << ", payload_overhead=" << payload_overhead << ", target=" << target << ")");
}
// Initialize the components of the OpenVPN data channel protocol
void init_data_channel()
{
@ -1890,34 +1964,7 @@ namespace openvpn {
// cache op32 for hot path in do_encrypt
cache_op32();
int crypto_encap = (enable_op32 ? OP_SIZE_V2 : 1) +
c.comp_ctx.extra_payload_bytes() +
PacketID::size(PacketID::SHORT_FORM) +
c.dc.context().encap_overhead();
int transport_encap = 0;
if (c.mss_parms.mtu)
{
if (proto.is_tcp())
transport_encap += sizeof(struct TCPHeader);
else
transport_encap += sizeof(struct UDPHeader);
if (c.protocol.is_ipv6())
transport_encap += sizeof(struct IPv6Header);
else
transport_encap += sizeof(struct IPv4Header);
transport_encap += c.protocol.extra_transport_bytes();
}
if (c.mss_parms.mssfix != 0)
{
OPENVPN_LOG_PROTO("MTU mssfix=" << c.mss_parms.mssfix <<
" crypto_encap=" << crypto_encap <<
" transport_encap=" << transport_encap);
c.mss_inter = c.mss_parms.mssfix - (crypto_encap + transport_encap);
}
calculate_mssfix(c);
}
void data_limit_notify(const DataLimit::Mode cdl_mode,
@ -2069,8 +2116,8 @@ namespace openvpn {
bool pid_wrap;
// set MSS for segments client can receive
if (proto.config->mss_inter > 0)
MSSFix::mssfix(buf, proto.config->mss_inter);
if (proto.config->mss_fix > 0)
MSSFix::mssfix(buf, proto.config->mss_fix);
// compress packet
if (compress)

View File

@ -36,7 +36,7 @@
namespace openvpn {
class MSSFix {
public:
static void mssfix(BufferAllocated& buf, int mss_inter)
static void mssfix(BufferAllocated& buf, int mss_fix)
{
if (buf.empty())
return;
@ -61,7 +61,7 @@ namespace openvpn {
TCPHeader* tcphdr = (TCPHeader*)(buf.data() + ipv4hlen);
int ip_payload_len = buf.length() - ipv4hlen;
do_mssfix(tcphdr, mss_inter - (sizeof(struct IPv4Header) + sizeof(struct TCPHeader)), ip_payload_len);
do_mssfix(tcphdr, mss_fix, ip_payload_len);
}
}
break;
@ -96,8 +96,8 @@ namespace openvpn {
if (payload_len >= (int) sizeof(struct TCPHeader))
{
TCPHeader *tcphdr = (TCPHeader *)(buf.data() + sizeof(struct IPv6Header));
do_mssfix(tcphdr, mss_inter - (sizeof(struct IPv6Header) + sizeof(struct TCPHeader)),
payload_len);
// mssfix is calculated for IPv4, and since IPv6 header is 20 bytes larger we need to account for it
do_mssfix(tcphdr, mss_fix - 20, payload_len);
}
}
break;

View File

@ -89,7 +89,7 @@ namespace openvpn {
{
// set a default MTU
if (!tun_prop.mtu)
tun_prop.mtu = 1500;
tun_prop.mtu = TUN_MTU_DEFAULT;
// parse "dev" option
if (dev_name.empty())

View File

@ -97,7 +97,7 @@ namespace openvpn {
std::string dev;
bool up = true;
int mtu = 1500;
int mtu = TUN_MTU_DEFAULT;
};
struct NetlinkAddr4 : public Action

View File

@ -36,6 +36,7 @@
#include <openvpn/tun/persist/tunpersist.hpp>
#include <openvpn/tun/persist/tunwrapasio.hpp>
#include <openvpn/tun/tunio.hpp>
#include <openvpn/tun/tunmtu.hpp>
#include <openvpn/tun/mac/client/tunsetup.hpp>
#ifdef TEST_EER // test emulated exclude routes
@ -199,7 +200,7 @@ namespace openvpn {
// handle MTU default
if (!po->mtu)
po->mtu = 1500;
po->mtu = TUN_MTU_DEFAULT;
OPENVPN_LOG("CAPTURED OPTIONS:" << std::endl << po->to_string());

View File

@ -25,6 +25,10 @@
#include <openvpn/common/options.hpp>
namespace openvpn {
enum {
TUN_MTU_DEFAULT = 1500,
};
inline unsigned int parse_tun_mtu(const OptionList& opt, unsigned int default_value)
{
return opt.get_num<unsigned int>("tun-mtu", 1, default_value, 576, 65535);