| /* |
| * 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-2018 OpenVPN 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; if not, write to the Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 accommodate 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 /* ifdef ENABLE_OCC */ |
| static void |
| dummy(void) |
| { |
| } |
| #endif /* ifdef ENABLE_OCC */ |