src/openvpn/occ.c
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"
 
 #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[] = {
81d882d5
     0x28, 0x7f, 0x34, 0x6b, 0xd4, 0xef, 0x7a, 0x81,
     0x2d, 0x56, 0xb8, 0xd3, 0xaf, 0xc5, 0x45, 0x9c
6fbf66fa
 };
 
 static const struct mtu_load_test mtu_load_test_sequence[] = {
 
81d882d5
     {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}
6fbf66fa
 };
 
 void
81d882d5
 check_send_occ_req_dowork(struct context *c)
6fbf66fa
 {
81d882d5
     if (++c->c2.occ_n_tries >= OCC_N_TRIES)
6fbf66fa
     {
81d882d5
         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);
6fbf66fa
     }
81d882d5
     else
6fbf66fa
     {
81d882d5
         c->c2.occ_op = OCC_REQUEST;
6fbf66fa
 
81d882d5
         /*
          * If we don't hear back from peer, send another
          * OCC_REQUEST in OCC_INTERVAL_SECONDS.
          */
         event_timeout_reset(&c->c2.occ_interval);
6fbf66fa
     }
 }
 
 void
81d882d5
 check_send_occ_load_test_dowork(struct context *c)
6fbf66fa
 {
81d882d5
     if (CONNECTION_ESTABLISHED(c))
6fbf66fa
     {
81d882d5
         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;
         }
6fbf66fa
     }
 }
 
 void
81d882d5
 check_send_occ_msg_dowork(struct context *c)
6fbf66fa
 {
81d882d5
     bool doit = false;
6fbf66fa
 
81d882d5
     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));
6fbf66fa
 
81d882d5
     switch (c->c2.occ_op)
6fbf66fa
     {
81d882d5
         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;
6fbf66fa
     }
 
81d882d5
     if (doit)
6fbf66fa
     {
81d882d5
         /*
          * We will treat the packet like any other outgoing packet,
          * compress, encrypt, sign, etc.
          */
         encrypt_sign(c, true);
6fbf66fa
     }
 
81d882d5
     c->c2.occ_op = -1;
6fbf66fa
 }
 
 void
81d882d5
 process_received_occ_msg(struct context *c)
6fbf66fa
 {
81d882d5
     ASSERT(buf_advance(&c->c2.buf, OCC_STRING_SIZE));
     switch (buf_read_u8(&c->c2.buf))
6fbf66fa
     {
81d882d5
         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;
6fbf66fa
     }
81d882d5
     c->c2.buf.len = 0; /* don't pass packet on */
6fbf66fa
 }
 
81d882d5
 #else  /* ifdef ENABLE_OCC */
 static void
4cd4899e
 dummy(void)
 {
81d882d5
 }
 #endif /* ifdef ENABLE_OCC */