mirror of
https://github.com/OpenVPN/openvpn.git
synced 2024-11-30 21:24:13 +08:00
d7fa38f2a9
git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@4477 e7ae566f-a301-0410-adde-c780ea21d3b5
291 lines
7.1 KiB
C
291 lines
7.1 KiB
C
/*
|
|
* 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.
|
|
*
|
|
* Copyright (C) 2002-2009 OpenVPN Technologies, Inc. <sales@openvpn.net>
|
|
*
|
|
* 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program (see the file COPYING included with this
|
|
* distribution); if not, write to the Free Software Foundation, Inc.,
|
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "syshead.h"
|
|
|
|
#include "common.h"
|
|
#include "buffer.h"
|
|
#include "error.h"
|
|
#include "integer.h"
|
|
#include "mtu.h"
|
|
|
|
#include "memdbg.h"
|
|
|
|
/* allocate a buffer for socket or tun layer */
|
|
void
|
|
alloc_buf_sock_tun (struct buffer *buf,
|
|
const struct frame *frame,
|
|
const bool tuntap_buffer,
|
|
const unsigned int align_mask)
|
|
{
|
|
/* 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));
|
|
}
|
|
|
|
void
|
|
frame_finalize (struct frame *frame,
|
|
bool link_mtu_defined,
|
|
int link_mtu,
|
|
bool tun_mtu_defined,
|
|
int tun_mtu)
|
|
{
|
|
/* Set link_mtu based on command line options */
|
|
if (tun_mtu_defined)
|
|
{
|
|
ASSERT (!link_mtu_defined);
|
|
frame->link_mtu = tun_mtu + TUN_LINK_DELTA (frame);
|
|
}
|
|
else
|
|
{
|
|
ASSERT (link_mtu_defined);
|
|
frame->link_mtu = link_mtu;
|
|
}
|
|
|
|
if (TUN_MTU_SIZE (frame) < TUN_MTU_MIN)
|
|
{
|
|
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");
|
|
}
|
|
|
|
frame->link_mtu_dynamic = frame->link_mtu;
|
|
|
|
frame->extra_buffer += PAYLOAD_ALIGN;
|
|
}
|
|
|
|
/*
|
|
* Set the tun MTU dynamically.
|
|
*/
|
|
void
|
|
frame_set_mtu_dynamic (struct frame *frame, int mtu, unsigned int flags)
|
|
{
|
|
|
|
#ifdef ENABLE_DEBUG
|
|
const int orig_mtu = mtu;
|
|
const int orig_link_mtu_dynamic = frame->link_mtu_dynamic;
|
|
#endif
|
|
|
|
ASSERT (mtu >= 0);
|
|
|
|
if (flags & SET_MTU_TUN)
|
|
mtu += TUN_LINK_DELTA (frame);
|
|
|
|
if (!(flags & SET_MTU_UPPER_BOUND) || mtu < frame->link_mtu_dynamic)
|
|
{
|
|
frame->link_mtu_dynamic = constrain_int (
|
|
mtu,
|
|
EXPANDED_SIZE_MIN (frame),
|
|
EXPANDED_SIZE (frame));
|
|
}
|
|
|
|
dmsg (D_MTU_DEBUG, "MTU DYNAMIC mtu=%d, flags=%u, %d -> %d",
|
|
orig_mtu,
|
|
flags,
|
|
orig_link_mtu_dynamic,
|
|
frame->link_mtu_dynamic);
|
|
}
|
|
|
|
/*
|
|
* Move extra_frame octets into extra_tun. Used by fragmenting code
|
|
* to adjust frame relative to its position in the buffer processing
|
|
* queue.
|
|
*/
|
|
void
|
|
frame_subtract_extra (struct frame *frame, const struct frame *src)
|
|
{
|
|
frame->extra_frame -= src->extra_frame;
|
|
frame->extra_tun += src->extra_frame;
|
|
}
|
|
|
|
void
|
|
frame_print (const struct frame *frame,
|
|
int level,
|
|
const char *prefix)
|
|
{
|
|
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);
|
|
}
|
|
|
|
#define MTUDISC_NOT_SUPPORTED_MSG "--mtu-disc is not supported on this OS"
|
|
|
|
void
|
|
set_mtu_discover_type (int sd, int mtu_type)
|
|
{
|
|
if (mtu_type >= 0)
|
|
{
|
|
#if defined(HAVE_SETSOCKOPT) && defined(SOL_IP) && defined(IP_MTU_DISCOVER)
|
|
if (setsockopt
|
|
(sd, SOL_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);
|
|
#else
|
|
msg (M_FATAL, MTUDISC_NOT_SUPPORTED_MSG);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
int
|
|
translate_mtu_discover_type_name (const char *name)
|
|
{
|
|
#if defined(IP_PMTUDISC_DONT) && defined(IP_PMTUDISC_WANT) && defined(IP_PMTUDISC_DO)
|
|
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
|
|
msg (M_FATAL, MTUDISC_NOT_SUPPORTED_MSG);
|
|
#endif
|
|
return -1; /* NOTREACHED */
|
|
}
|
|
|
|
#if EXTENDED_SOCKET_ERROR_CAPABILITY
|
|
|
|
struct probehdr
|
|
{
|
|
uint32_t ttl;
|
|
struct timeval tv;
|
|
};
|
|
|
|
const char *
|
|
format_extended_socket_error (int fd, int *mtu, struct gc_arena *gc)
|
|
{
|
|
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)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
buf_rmtail (&out, '|');
|
|
return BSTR (&out);
|
|
}
|
|
|
|
void
|
|
set_sock_extended_error_passing (int sd)
|
|
{
|
|
int on = 1;
|
|
if (setsockopt (sd, SOL_IP, IP_RECVERR, &on, sizeof (on)))
|
|
msg (M_WARN | M_ERRNO,
|
|
"Note: enable extended error passing on TCP/UDP socket failed (IP_RECVERR)");
|
|
}
|
|
|
|
#endif
|