diff options
author | Alberto Gonzalez Iniesta <agi@inittab.org> | 2012-11-05 16:28:10 +0100 |
---|---|---|
committer | Alberto Gonzalez Iniesta <agi@inittab.org> | 2012-11-05 16:28:10 +0100 |
commit | d213c4e5576e2fd601679e0d7b2fb1262b807111 (patch) | |
tree | 5f0cc82bd0f11fb13b385417604d04c751245a92 /src/openvpn/mtu.c | |
parent | 79c8d3ef7a938f86472e549ef64e1fb820dc80c4 (diff) | |
parent | 8dd0350e1607aa30f7a043c8d5ec7a7eeb874115 (diff) |
Merge tag 'upstream/2.3_rc1'
Upstream version 2.3_rc1
Diffstat (limited to 'src/openvpn/mtu.c')
-rw-r--r-- | src/openvpn/mtu.c | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/src/openvpn/mtu.c b/src/openvpn/mtu.c new file mode 100644 index 0000000..13f3f6c --- /dev/null +++ b/src/openvpn/mtu.c @@ -0,0 +1,296 @@ +/* + * 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-2010 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 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#elif defined(_MSC_VER) +#include "config-msvc.h" +#endif + +#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 |