diff options
Diffstat (limited to 'src/openvpn/occ.c')
-rw-r--r-- | src/openvpn/occ.c | 399 |
1 files changed, 399 insertions, 0 deletions
diff --git a/src/openvpn/occ.c b/src/openvpn/occ.c new file mode 100644 index 0000000..ff48706 --- /dev/null +++ b/src/openvpn/occ.c @@ -0,0 +1,399 @@ +/* + * 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" + +#ifdef ENABLE_OCC + +#include "occ.h" + +#include "memdbg.h" + +#include "forward-inline.h" +#include "occ-inline.h" + +/* + * This random string identifies an OpenVPN + * Configuration Control packet. + * It should be of sufficient length and randomness + * so as not to collide with other tunnel data. + * + * The OCC protocol is as follows: + * + * occ_magic -- (16 octets) + * + * type [OCC_REQUEST | OCC_REPLY] (1 octet) + * null terminated options string if OCC_REPLY (variable) + * + * When encryption is used, the OCC packet + * is encapsulated within the encrypted + * envelope. + * + * OCC_STRING_SIZE must be set to sizeof (occ_magic) + */ + +const uint8_t occ_magic[] = { + 0x28, 0x7f, 0x34, 0x6b, 0xd4, 0xef, 0x7a, 0x81, + 0x2d, 0x56, 0xb8, 0xd3, 0xaf, 0xc5, 0x45, 0x9c +}; + +static const struct mtu_load_test mtu_load_test_sequence[] = { + + {OCC_MTU_LOAD_REQUEST, -1000}, + {OCC_MTU_LOAD, -1000}, + {OCC_MTU_LOAD_REQUEST, -1000}, + {OCC_MTU_LOAD, -1000}, + {OCC_MTU_LOAD_REQUEST, -1000}, + {OCC_MTU_LOAD, -1000}, + + {OCC_MTU_LOAD_REQUEST, -750}, + {OCC_MTU_LOAD, -750}, + {OCC_MTU_LOAD_REQUEST, -750}, + {OCC_MTU_LOAD, -750}, + {OCC_MTU_LOAD_REQUEST, -750}, + {OCC_MTU_LOAD, -750}, + + {OCC_MTU_LOAD_REQUEST, -500}, + {OCC_MTU_LOAD, -500}, + {OCC_MTU_LOAD_REQUEST, -500}, + {OCC_MTU_LOAD, -500}, + {OCC_MTU_LOAD_REQUEST, -500}, + {OCC_MTU_LOAD, -500}, + + {OCC_MTU_LOAD_REQUEST, -400}, + {OCC_MTU_LOAD, -400}, + {OCC_MTU_LOAD_REQUEST, -400}, + {OCC_MTU_LOAD, -400}, + {OCC_MTU_LOAD_REQUEST, -400}, + {OCC_MTU_LOAD, -400}, + + {OCC_MTU_LOAD_REQUEST, -300}, + {OCC_MTU_LOAD, -300}, + {OCC_MTU_LOAD_REQUEST, -300}, + {OCC_MTU_LOAD, -300}, + {OCC_MTU_LOAD_REQUEST, -300}, + {OCC_MTU_LOAD, -300}, + + {OCC_MTU_LOAD_REQUEST, -200}, + {OCC_MTU_LOAD, -200}, + {OCC_MTU_LOAD_REQUEST, -200}, + {OCC_MTU_LOAD, -200}, + {OCC_MTU_LOAD_REQUEST, -200}, + {OCC_MTU_LOAD, -200}, + + {OCC_MTU_LOAD_REQUEST, -150}, + {OCC_MTU_LOAD, -150}, + {OCC_MTU_LOAD_REQUEST, -150}, + {OCC_MTU_LOAD, -150}, + {OCC_MTU_LOAD_REQUEST, -150}, + {OCC_MTU_LOAD, -150}, + + {OCC_MTU_LOAD_REQUEST, -100}, + {OCC_MTU_LOAD, -100}, + {OCC_MTU_LOAD_REQUEST, -100}, + {OCC_MTU_LOAD, -100}, + {OCC_MTU_LOAD_REQUEST, -100}, + {OCC_MTU_LOAD, -100}, + + {OCC_MTU_LOAD_REQUEST, -50}, + {OCC_MTU_LOAD, -50}, + {OCC_MTU_LOAD_REQUEST, -50}, + {OCC_MTU_LOAD, -50}, + {OCC_MTU_LOAD_REQUEST, -50}, + {OCC_MTU_LOAD, -50}, + + {OCC_MTU_LOAD_REQUEST, 0}, + {OCC_MTU_LOAD, 0}, + {OCC_MTU_LOAD_REQUEST, 0}, + {OCC_MTU_LOAD, 0}, + {OCC_MTU_LOAD_REQUEST, 0}, + {OCC_MTU_LOAD, 0}, + + {OCC_MTU_REQUEST, 0}, + {OCC_MTU_REQUEST, 0}, + {OCC_MTU_REQUEST, 0}, + {OCC_MTU_REQUEST, 0}, + {OCC_MTU_REQUEST, 0}, + {OCC_MTU_REQUEST, 0}, + {OCC_MTU_REQUEST, 0}, + {OCC_MTU_REQUEST, 0}, + {OCC_MTU_REQUEST, 0}, + {OCC_MTU_REQUEST, 0}, + + {-1, 0} +}; + +void +check_send_occ_req_dowork (struct context *c) +{ + if (++c->c2.occ_n_tries >= OCC_N_TRIES) + { + if (c->options.ce.remote) + /* + * No OCC_REPLY from peer after repeated attempts. + * Give up. + */ + msg (D_SHOW_OCC, + "NOTE: failed to obtain options consistency info from peer -- " + "this could occur if the remote peer is running a version of " + PACKAGE_NAME + " before 1.5-beta8 or if there is a network connectivity problem, and will not necessarily prevent " + PACKAGE_NAME + " from running (" counter_format " bytes received from peer, " counter_format + " bytes authenticated data channel traffic) -- you can disable the options consistency " + "check with --disable-occ.", + c->c2.link_read_bytes, + c->c2.link_read_bytes_auth); + event_timeout_clear (&c->c2.occ_interval); + } + else + { + c->c2.occ_op = OCC_REQUEST; + + /* + * If we don't hear back from peer, send another + * OCC_REQUEST in OCC_INTERVAL_SECONDS. + */ + event_timeout_reset (&c->c2.occ_interval); + } +} + +void +check_send_occ_load_test_dowork (struct context *c) +{ + if (CONNECTION_ESTABLISHED (c)) + { + const struct mtu_load_test *entry; + + if (!c->c2.occ_mtu_load_n_tries) + msg (M_INFO, + "NOTE: Beginning empirical MTU test -- results should be available in 3 to 4 minutes."); + + entry = &mtu_load_test_sequence[c->c2.occ_mtu_load_n_tries++]; + if (entry->op >= 0) + { + c->c2.occ_op = entry->op; + c->c2.occ_mtu_load_size = + EXPANDED_SIZE (&c->c2.frame) + entry->delta; + } + else + { + msg (M_INFO, + "NOTE: failed to empirically measure MTU (requires " PACKAGE_NAME " 1.5 or higher at other end of connection)."); + event_timeout_clear (&c->c2.occ_mtu_load_test_interval); + c->c2.occ_mtu_load_n_tries = 0; + } + } +} + +void +check_send_occ_msg_dowork (struct context *c) +{ + bool doit = false; + + c->c2.buf = c->c2.buffers->aux_buf; + ASSERT (buf_init (&c->c2.buf, FRAME_HEADROOM (&c->c2.frame))); + ASSERT (buf_safe (&c->c2.buf, MAX_RW_SIZE_TUN (&c->c2.frame))); + ASSERT (buf_write (&c->c2.buf, occ_magic, OCC_STRING_SIZE)); + + switch (c->c2.occ_op) + { + case OCC_REQUEST: + if (!buf_write_u8 (&c->c2.buf, OCC_REQUEST)) + break; + dmsg (D_PACKET_CONTENT, "SENT OCC_REQUEST"); + doit = true; + break; + + case OCC_REPLY: + if (!c->c2.options_string_local) + break; + if (!buf_write_u8 (&c->c2.buf, OCC_REPLY)) + break; + if (!buf_write (&c->c2.buf, c->c2.options_string_local, + strlen (c->c2.options_string_local) + 1)) + break; + dmsg (D_PACKET_CONTENT, "SENT OCC_REPLY"); + doit = true; + break; + + case OCC_MTU_REQUEST: + if (!buf_write_u8 (&c->c2.buf, OCC_MTU_REQUEST)) + break; + dmsg (D_PACKET_CONTENT, "SENT OCC_MTU_REQUEST"); + doit = true; + break; + + case OCC_MTU_REPLY: + if (!buf_write_u8 (&c->c2.buf, OCC_MTU_REPLY)) + break; + if (!buf_write_u16 (&c->c2.buf, c->c2.max_recv_size_local)) + break; + if (!buf_write_u16 (&c->c2.buf, c->c2.max_send_size_local)) + break; + dmsg (D_PACKET_CONTENT, "SENT OCC_MTU_REPLY"); + doit = true; + break; + + case OCC_MTU_LOAD_REQUEST: + if (!buf_write_u8 (&c->c2.buf, OCC_MTU_LOAD_REQUEST)) + break; + if (!buf_write_u16 (&c->c2.buf, c->c2.occ_mtu_load_size)) + break; + dmsg (D_PACKET_CONTENT, "SENT OCC_MTU_LOAD_REQUEST"); + doit = true; + break; + + case OCC_MTU_LOAD: + { + int need_to_add; + + if (!buf_write_u8 (&c->c2.buf, OCC_MTU_LOAD)) + break; + need_to_add = min_int (c->c2.occ_mtu_load_size, EXPANDED_SIZE (&c->c2.frame)) + - OCC_STRING_SIZE + - sizeof (uint8_t) + - EXTRA_FRAME (&c->c2.frame); + + while (need_to_add > 0) + { + /* + * Fill the load test packet with pseudo-random bytes. + */ + if (!buf_write_u8 (&c->c2.buf, get_random () & 0xFF)) + break; + --need_to_add; + } + dmsg (D_PACKET_CONTENT, "SENT OCC_MTU_LOAD min_int(%d-%d-%d-%d,%d) size=%d", + c->c2.occ_mtu_load_size, + OCC_STRING_SIZE, + (int) sizeof (uint8_t), + EXTRA_FRAME (&c->c2.frame), + MAX_RW_SIZE_TUN (&c->c2.frame), + BLEN (&c->c2.buf)); + doit = true; + } + break; + + case OCC_EXIT: + if (!buf_write_u8 (&c->c2.buf, OCC_EXIT)) + break; + dmsg (D_PACKET_CONTENT, "SENT OCC_EXIT"); + doit = true; + break; + } + + if (doit) + { + /* + * We will treat the packet like any other outgoing packet, + * compress, encrypt, sign, etc. + */ + encrypt_sign (c, true); + } + + c->c2.occ_op = -1; +} + +void +process_received_occ_msg (struct context *c) +{ + ASSERT (buf_advance (&c->c2.buf, OCC_STRING_SIZE)); + switch (buf_read_u8 (&c->c2.buf)) + { + case OCC_REQUEST: + dmsg (D_PACKET_CONTENT, "RECEIVED OCC_REQUEST"); + c->c2.occ_op = OCC_REPLY; + break; + + case OCC_MTU_REQUEST: + dmsg (D_PACKET_CONTENT, "RECEIVED OCC_MTU_REQUEST"); + c->c2.occ_op = OCC_MTU_REPLY; + break; + + case OCC_MTU_LOAD_REQUEST: + dmsg (D_PACKET_CONTENT, "RECEIVED OCC_MTU_LOAD_REQUEST"); + c->c2.occ_mtu_load_size = buf_read_u16 (&c->c2.buf); + if (c->c2.occ_mtu_load_size >= 0) + c->c2.occ_op = OCC_MTU_LOAD; + break; + + case OCC_REPLY: + dmsg (D_PACKET_CONTENT, "RECEIVED OCC_REPLY"); + if (c->options.occ && !TLS_MODE (c) && c->c2.options_string_remote) + { + if (!options_cmp_equal_safe ((char *) BPTR (&c->c2.buf), + c->c2.options_string_remote, + c->c2.buf.len)) + { + options_warning_safe ((char *) BPTR (&c->c2.buf), + c->c2.options_string_remote, + c->c2.buf.len); + } + } + event_timeout_clear (&c->c2.occ_interval); + break; + + case OCC_MTU_REPLY: + dmsg (D_PACKET_CONTENT, "RECEIVED OCC_MTU_REPLY"); + c->c2.max_recv_size_remote = buf_read_u16 (&c->c2.buf); + c->c2.max_send_size_remote = buf_read_u16 (&c->c2.buf); + if (c->options.mtu_test + && c->c2.max_recv_size_remote > 0 + && c->c2.max_send_size_remote > 0) + { + msg (M_INFO, "NOTE: Empirical MTU test completed [Tried,Actual] local->remote=[%d,%d] remote->local=[%d,%d]", + c->c2.max_send_size_local, + c->c2.max_recv_size_remote, + c->c2.max_send_size_remote, + c->c2.max_recv_size_local); + if (!c->options.ce.fragment + && (proto_is_dgram(c->options.ce.proto)) + && c->c2.max_send_size_local > TUN_MTU_MIN + && (c->c2.max_recv_size_remote < c->c2.max_send_size_local + || c->c2.max_recv_size_local < c->c2.max_send_size_remote)) + msg (M_INFO, "NOTE: This connection is unable to accomodate a UDP packet size of %d. Consider using --fragment or --mssfix options as a workaround.", + c->c2.max_send_size_local); + } + event_timeout_clear (&c->c2.occ_mtu_load_test_interval); + break; + + case OCC_EXIT: + dmsg (D_PACKET_CONTENT, "RECEIVED OCC_EXIT"); + c->sig->signal_received = SIGTERM; + c->sig->signal_text = "remote-exit"; + break; + } + c->c2.buf.len = 0; /* don't pass packet on */ +} + +#else +static void dummy(void) {} +#endif |