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:
parent
b7c6e2d5af
commit
ac15879588
@ -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
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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())
|
||||
|
@ -97,7 +97,7 @@ namespace openvpn {
|
||||
|
||||
std::string dev;
|
||||
bool up = true;
|
||||
int mtu = 1500;
|
||||
int mtu = TUN_MTU_DEFAULT;
|
||||
};
|
||||
|
||||
struct NetlinkAddr4 : public Action
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user