6fbf66fa |
/*
* 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.
* |
49979459 |
* Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net> |
6fbf66fa |
*
* 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.
* |
caa54ac3 |
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
6fbf66fa |
*/
|
c110b289 |
#ifdef HAVE_CONFIG_H
#include "config.h"
#elif defined(_MSC_VER)
#include "config-msvc.h"
#endif
|
6fbf66fa |
#include "syshead.h"
#include "common.h"
#include "buffer.h"
#include "error.h"
#include "integer.h"
#include "mtu.h" |
cbc3c5a9 |
#include "options.h" |
6fbf66fa |
#include "memdbg.h"
/* allocate a buffer for socket or tun layer */
void |
81d882d5 |
alloc_buf_sock_tun(struct buffer *buf,
const struct frame *frame,
const bool tuntap_buffer,
const unsigned int align_mask) |
6fbf66fa |
{ |
81d882d5 |
/* allocate buffer for overlapped I/O */
*buf = alloc_buf(BUF_SIZE(frame));
ASSERT(buf_init(buf, FRAME_HEADROOM_ADJ(frame, align_mask)));
buf->len = tuntap_buffer ? MAX_RW_SIZE_TUN(frame) : MAX_RW_SIZE_LINK(frame);
ASSERT(buf_safe(buf, 0)); |
6fbf66fa |
}
void |
81d882d5 |
frame_finalize(struct frame *frame,
bool link_mtu_defined,
int link_mtu,
bool tun_mtu_defined,
int tun_mtu) |
6fbf66fa |
{ |
81d882d5 |
/* Set link_mtu based on command line options */
if (tun_mtu_defined) |
6fbf66fa |
{ |
81d882d5 |
ASSERT(!link_mtu_defined);
frame->link_mtu = tun_mtu + TUN_LINK_DELTA(frame); |
6fbf66fa |
} |
81d882d5 |
else |
6fbf66fa |
{ |
81d882d5 |
ASSERT(link_mtu_defined);
frame->link_mtu = link_mtu; |
6fbf66fa |
}
|
81d882d5 |
if (TUN_MTU_SIZE(frame) < TUN_MTU_MIN) |
6fbf66fa |
{ |
81d882d5 |
msg(M_WARN, "TUN MTU value (%d) must be at least %d", TUN_MTU_SIZE(frame), TUN_MTU_MIN);
frame_print(frame, M_FATAL, "MTU is too small"); |
6fbf66fa |
}
|
81d882d5 |
frame->link_mtu_dynamic = frame->link_mtu; |
6fbf66fa |
}
/*
* Set the tun MTU dynamically.
*/
void |
81d882d5 |
frame_set_mtu_dynamic(struct frame *frame, int mtu, unsigned int flags) |
6fbf66fa |
{
#ifdef ENABLE_DEBUG |
81d882d5 |
const int orig_mtu = mtu;
const int orig_link_mtu_dynamic = frame->link_mtu_dynamic; |
6fbf66fa |
#endif
|
81d882d5 |
ASSERT(mtu >= 0); |
6fbf66fa |
|
81d882d5 |
if (flags & SET_MTU_TUN)
{
mtu += TUN_LINK_DELTA(frame);
} |
6fbf66fa |
|
81d882d5 |
if (!(flags & SET_MTU_UPPER_BOUND) || mtu < frame->link_mtu_dynamic) |
6fbf66fa |
{ |
81d882d5 |
frame->link_mtu_dynamic = constrain_int(
mtu,
EXPANDED_SIZE_MIN(frame),
EXPANDED_SIZE(frame)); |
6fbf66fa |
}
|
81d882d5 |
dmsg(D_MTU_DEBUG, "MTU DYNAMIC mtu=%d, flags=%u, %d -> %d",
orig_mtu,
flags,
orig_link_mtu_dynamic,
frame->link_mtu_dynamic); |
6fbf66fa |
}
/*
* Move extra_frame octets into extra_tun. Used by fragmenting code
* to adjust frame relative to its position in the buffer processing
* queue.
*/
void |
81d882d5 |
frame_subtract_extra(struct frame *frame, const struct frame *src) |
6fbf66fa |
{ |
81d882d5 |
frame->extra_frame -= src->extra_frame;
frame->extra_tun += src->extra_frame; |
6fbf66fa |
}
void |
81d882d5 |
frame_init_mssfix(struct frame *frame, const struct options *options) |
cbc3c5a9 |
{ |
81d882d5 |
if (options->ce.mssfix) |
cbc3c5a9 |
{ |
81d882d5 |
frame_set_mtu_dynamic(frame, options->ce.mssfix, SET_MTU_UPPER_BOUND); |
cbc3c5a9 |
}
}
void |
81d882d5 |
frame_print(const struct frame *frame,
int level,
const char *prefix) |
6fbf66fa |
{ |
81d882d5 |
struct gc_arena gc = gc_new();
struct buffer out = alloc_buf_gc(256, &gc);
if (prefix)
{
buf_printf(&out, "%s ", prefix);
}
buf_printf(&out, "[");
buf_printf(&out, " L:%d", frame->link_mtu);
buf_printf(&out, " D:%d", frame->link_mtu_dynamic);
buf_printf(&out, " EF:%d", frame->extra_frame);
buf_printf(&out, " EB:%d", frame->extra_buffer);
buf_printf(&out, " ET:%d", frame->extra_tun);
buf_printf(&out, " EL:%d", frame->extra_link);
if (frame->align_flags && frame->align_adjust)
{
buf_printf(&out, " AF:%u/%d", frame->align_flags, frame->align_adjust);
}
buf_printf(&out, " ]");
msg(level, "%s", out.data);
gc_free(&gc); |
6fbf66fa |
}
#define MTUDISC_NOT_SUPPORTED_MSG "--mtu-disc is not supported on this OS"
void |
81d882d5 |
set_mtu_discover_type(int sd, int mtu_type, sa_family_t proto_af) |
6fbf66fa |
{ |
81d882d5 |
if (mtu_type >= 0) |
6fbf66fa |
{ |
81d882d5 |
switch (proto_af)
{ |
2bed089d |
#if defined(HAVE_SETSOCKOPT) && defined(IP_MTU_DISCOVER) |
81d882d5 |
case AF_INET:
if (setsockopt
(sd, IPPROTO_IP, IP_MTU_DISCOVER, &mtu_type, sizeof(mtu_type)))
{
msg(M_ERR, "Error setting IP_MTU_DISCOVER type=%d on TCP/UDP socket",
mtu_type);
}
break;
|
2bed089d |
#endif
#if defined(HAVE_SETSOCKOPT) && defined(IPV6_MTU_DISCOVER) |
81d882d5 |
case AF_INET6:
if (setsockopt
(sd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &mtu_type, sizeof(mtu_type)))
{
msg(M_ERR, "Error setting IPV6_MTU_DISCOVER type=%d on TCP6/UDP6 socket",
mtu_type);
}
break;
|
6fbf66fa |
#endif |
81d882d5 |
default:
msg(M_FATAL, MTUDISC_NOT_SUPPORTED_MSG);
break;
} |
6fbf66fa |
}
}
int |
81d882d5 |
translate_mtu_discover_type_name(const char *name) |
6fbf66fa |
{
#if defined(IP_PMTUDISC_DONT) && defined(IP_PMTUDISC_WANT) && defined(IP_PMTUDISC_DO) |
81d882d5 |
if (!strcmp(name, "yes"))
{
return IP_PMTUDISC_DO;
}
if (!strcmp(name, "maybe"))
{
return IP_PMTUDISC_WANT;
}
if (!strcmp(name, "no"))
{
return IP_PMTUDISC_DONT;
}
msg(M_FATAL,
"invalid --mtu-disc type: '%s' -- valid types are 'yes', 'maybe', or 'no'",
name);
#else /* if defined(IP_PMTUDISC_DONT) && defined(IP_PMTUDISC_WANT) && defined(IP_PMTUDISC_DO) */
msg(M_FATAL, MTUDISC_NOT_SUPPORTED_MSG); |
6fbf66fa |
#endif |
81d882d5 |
return -1; /* NOTREACHED */ |
6fbf66fa |
}
#if EXTENDED_SOCKET_ERROR_CAPABILITY
struct probehdr
{ |
81d882d5 |
uint32_t ttl;
struct timeval tv; |
6fbf66fa |
};
const char * |
81d882d5 |
format_extended_socket_error(int fd, int *mtu, struct gc_arena *gc) |
6fbf66fa |
{ |
81d882d5 |
int res;
struct probehdr rcvbuf;
struct iovec iov;
struct msghdr msg;
struct cmsghdr *cmsg;
struct sock_extended_err *e;
struct sockaddr_in addr;
struct buffer out = alloc_buf_gc(256, gc);
char *cbuf = (char *) gc_malloc(256, false, gc);
*mtu = 0;
while (true) |
6fbf66fa |
{ |
81d882d5 |
memset(&rcvbuf, -1, sizeof(rcvbuf));
iov.iov_base = &rcvbuf;
iov.iov_len = sizeof(rcvbuf);
msg.msg_name = (uint8_t *) &addr;
msg.msg_namelen = sizeof(addr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_flags = 0;
msg.msg_control = cbuf;
msg.msg_controllen = 256; /* size of cbuf */
res = recvmsg(fd, &msg, MSG_ERRQUEUE);
if (res < 0)
{
goto exit;
}
e = NULL;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg))
{
if (cmsg->cmsg_level == SOL_IP)
{
if (cmsg->cmsg_type == IP_RECVERR)
{
e = (struct sock_extended_err *) CMSG_DATA(cmsg);
}
else
{
buf_printf(&out,"CMSG=%d|", cmsg->cmsg_type);
}
}
}
if (e == NULL)
{
buf_printf(&out, "NO-INFO|");
goto exit;
}
switch (e->ee_errno)
{
case ETIMEDOUT:
buf_printf(&out, "ETIMEDOUT|");
break;
case EMSGSIZE:
buf_printf(&out, "EMSGSIZE Path-MTU=%d|", e->ee_info);
*mtu = e->ee_info;
break;
case ECONNREFUSED:
buf_printf(&out, "ECONNREFUSED|");
break;
case EPROTO:
buf_printf(&out, "EPROTO|");
break;
case EHOSTUNREACH:
buf_printf(&out, "EHOSTUNREACH|");
break;
case ENETUNREACH:
buf_printf(&out, "ENETUNREACH|");
break;
case EACCES:
buf_printf(&out, "EACCES|");
break;
default:
buf_printf(&out, "UNKNOWN|");
break;
} |
6fbf66fa |
}
|
81d882d5 |
exit:
buf_rmtail(&out, '|');
return BSTR(&out); |
6fbf66fa |
}
void |
81d882d5 |
set_sock_extended_error_passing(int sd) |
6fbf66fa |
{ |
81d882d5 |
int on = 1;
if (setsockopt(sd, SOL_IP, IP_RECVERR, (void *) &on, sizeof(on)))
{
msg(M_WARN | M_ERRNO,
"Note: enable extended error passing on TCP/UDP socket failed (IP_RECVERR)");
} |
6fbf66fa |
}
|
81d882d5 |
#endif /* if EXTENDED_SOCKET_ERROR_CAPABILITY */ |