diff options
author | james <james@e7ae566f-a301-0410-adde-c780ea21d3b5> | 2005-09-26 05:28:27 +0000 |
---|---|---|
committer | james <james@e7ae566f-a301-0410-adde-c780ea21d3b5> | 2005-09-26 05:28:27 +0000 |
commit | 6fbf66fad3367b24fd6743bcd50254902fd9c8d5 (patch) | |
tree | 9802876e3771744eead18917bb47ff6e90ac39f5 /forward.c | |
download | openvpn-6fbf66fad3367b24fd6743bcd50254902fd9c8d5.tar.xz |
This is the start of the BETA21 branch.
It includes the --topology feature, and
TAP-Win32 driver changes to allow
non-admin access.
git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@580 e7ae566f-a301-0410-adde-c780ea21d3b5
Diffstat (limited to 'forward.c')
-rw-r--r-- | forward.c | 1402 |
1 files changed, 1402 insertions, 0 deletions
diff --git a/forward.c b/forward.c new file mode 100644 index 0000000..4c0aff8 --- /dev/null +++ b/forward.c @@ -0,0 +1,1402 @@ +/* + * 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-2005 OpenVPN Solutions LLC <info@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 WIN32 +#include "config-win32.h" +#else +#include "config.h" +#endif + +#include "syshead.h" + +#include "forward.h" +#include "init.h" +#include "push.h" +#include "gremlin.h" +#include "mss.h" +#include "event.h" + +#include "memdbg.h" + +#include "forward-inline.h" +#include "occ-inline.h" +#include "ping-inline.h" + +/* show event wait debugging info */ + +#ifdef ENABLE_DEBUG + +const char * +wait_status_string (struct context *c, struct gc_arena *gc) +{ + struct buffer out = alloc_buf_gc (64, gc); + buf_printf (&out, "I/O WAIT %s|%s|%s|%s %s", + tun_stat (c->c1.tuntap, EVENT_READ, gc), + tun_stat (c->c1.tuntap, EVENT_WRITE, gc), + socket_stat (c->c2.link_socket, EVENT_READ, gc), + socket_stat (c->c2.link_socket, EVENT_WRITE, gc), + tv_string (&c->c2.timeval, gc)); + return BSTR (&out); +} + +void +show_wait_status (struct context *c) +{ + struct gc_arena gc = gc_new (); + dmsg (D_EVENT_WAIT, "%s", wait_status_string (c, &gc)); + gc_free (&gc); +} + +#endif + +/* + * In TLS mode, let TLS level respond to any control-channel + * packets which were received, or prepare any packets for + * transmission. + * + * tmp_int is purely an optimization that allows us to call + * tls_multi_process less frequently when there's not much + * traffic on the control-channel. + * + */ +#if defined(USE_CRYPTO) && defined(USE_SSL) +void +check_tls_dowork (struct context *c) +{ + interval_t wakeup = BIG_TIMEOUT; + + if (interval_test (&c->c2.tmp_int)) + { + if (tls_multi_process + (c->c2.tls_multi, &c->c2.to_link, &c->c2.to_link_addr, + get_link_socket_info (c), &wakeup)) + { + update_time (); + interval_action (&c->c2.tmp_int); + } + + interval_future_trigger (&c->c2.tmp_int, wakeup); + } + + interval_schedule_wakeup (&c->c2.tmp_int, &wakeup); + + if (wakeup) + context_reschedule_sec (c, wakeup); +} +#endif + +#if defined(USE_CRYPTO) && defined(USE_SSL) + +void +check_tls_errors_co (struct context *c) +{ + msg (D_STREAM_ERRORS, "Fatal TLS error (check_tls_errors_co), restarting"); + c->sig->signal_received = c->c2.tls_exit_signal; /* SOFT-SIGUSR1 -- TLS error */ + c->sig->signal_text = "tls-error"; +} + +void +check_tls_errors_nco (struct context *c) +{ + c->sig->signal_received = c->c2.tls_exit_signal; /* SOFT-SIGUSR1 -- TLS error */ + c->sig->signal_text = "tls-error"; +} + +#endif + +#if P2MP + +/* + * Handle incoming configuration + * messages on the control channel. + */ +void +check_incoming_control_channel_dowork (struct context *c) +{ + const int len = tls_test_payload_len (c->c2.tls_multi); + if (len) + { + struct gc_arena gc = gc_new (); + struct buffer buf = alloc_buf_gc (len, &gc); + if (tls_rec_payload (c->c2.tls_multi, &buf)) + { + /* force null termination of message */ + buf_null_terminate (&buf); + + /* enforce character class restrictions */ + string_mod (BSTR (&buf), CC_PRINT, CC_CRLF, 0); + + if (buf_string_match_head_str (&buf, "AUTH_FAILED")) + receive_auth_failed (c, &buf); + else if (buf_string_match_head_str (&buf, "PUSH_")) + incoming_push_message (c, &buf); + else + msg (D_PUSH_ERRORS, "WARNING: Received unknown control message: %s", BSTR (&buf)); + } + else + { + msg (D_PUSH_ERRORS, "WARNING: Receive control message failed"); + } + + gc_free (&gc); + } +} + +/* + * Periodically resend PUSH_REQUEST until PUSH message received + */ +void +check_push_request_dowork (struct context *c) +{ + send_push_request (c); +} + +#endif + +/* + * Things that need to happen immediately after connection initiation should go here. + */ +void +check_connection_established_dowork (struct context *c) +{ + if (event_timeout_trigger (&c->c2.wait_for_connect, &c->c2.timeval, ETT_DEFAULT)) + { + if (CONNECTION_ESTABLISHED (c)) + { +#if P2MP + /* if --pull was specified, send a push request to server */ + if (c->c2.tls_multi && c->options.pull) + { +#ifdef ENABLE_MANAGEMENT + if (management) + { + management_set_state (management, + OPENVPN_STATE_GET_CONFIG, + NULL, + 0); + } +#endif + send_push_request (c); + + /* if no reply, try again in 5 sec */ + event_timeout_init (&c->c2.push_request_interval, 5, now); + reset_coarse_timers (c); + } + else +#endif + { + do_up (c, false, 0); + } + + event_timeout_clear (&c->c2.wait_for_connect); + } + } +} + +/* + * Send a string to remote over the TLS control channel. + * Used for push/pull messages, passing username/password, + * etc. + */ +bool +send_control_channel_string (struct context *c, const char *str, int msglevel) +{ +#if defined(USE_CRYPTO) && defined(USE_SSL) + + if (c->c2.tls_multi) { + bool stat; + + /* buffered cleartext write onto TLS control channel */ + stat = tls_send_payload (c->c2.tls_multi, (uint8_t*) str, strlen (str) + 1); + + /* reschedule tls_multi_process */ + interval_action (&c->c2.tmp_int); + context_immediate_reschedule (c); /* ZERO-TIMEOUT */ + + msg (msglevel, "SENT CONTROL [%s]: '%s' (status=%d)", + tls_common_name (c->c2.tls_multi, false), + str, + (int) stat); + + return stat; + } +#endif + return true; +} + +/* + * Add routes. + */ + +static void +check_add_routes_action (struct context *c, const bool errors) +{ + do_route (&c->options, c->c1.route_list, c->c1.tuntap, c->c1.plugins, c->c2.es); + update_time (); + event_timeout_clear (&c->c2.route_wakeup); + event_timeout_clear (&c->c2.route_wakeup_expire); + initialization_sequence_completed (c, errors ? ISC_ERRORS : 0); /* client/p2p --route-delay was defined */ +} + +void +check_add_routes_dowork (struct context *c) +{ + if (test_routes (c->c1.route_list, c->c1.tuntap)) + { + check_add_routes_action (c, false); + } + else if (event_timeout_trigger (&c->c2.route_wakeup_expire, &c->c2.timeval, ETT_DEFAULT)) + { + check_add_routes_action (c, true); + } + else + { + msg (D_ROUTE, "Route: Waiting for TUN/TAP interface to come up..."); + if (c->c2.route_wakeup.n != 1) + event_timeout_init (&c->c2.route_wakeup, 1, now); + } +} + +/* + * Should we exit due to inactivity timeout? + */ +void +check_inactivity_timeout_dowork (struct context *c) +{ + msg (M_INFO, "Inactivity timeout (--inactive), exiting"); + c->sig->signal_received = SIGTERM; + c->sig->signal_text = "inactive"; +} + +#if P2MP + +/* + * Schedule a SIGTERM n_seconds from now. + */ +void +schedule_exit (struct context *c, const int n_seconds) +{ + update_time (); + reset_coarse_timers (c); + event_timeout_init (&c->c2.scheduled_exit, n_seconds, now); + msg (D_SCHED_EXIT, "Delayed exit in %d seconds", n_seconds); +} + +/* + * Scheduled exit? + */ +void +check_scheduled_exit_dowork (struct context *c) +{ + c->sig->signal_received = SIGTERM; + c->sig->signal_text = "delayed-exit"; +} + +#endif + +/* + * Should we write timer-triggered status file. + */ +void +check_status_file_dowork (struct context *c) +{ + if (c->c1.status_output) + print_status (c, c->c1.status_output); +} + +#ifdef ENABLE_FRAGMENT +/* + * Should we deliver a datagram fragment to remote? + */ +void +check_fragment_dowork (struct context *c) +{ + struct link_socket_info *lsi = get_link_socket_info (c); + + /* OS MTU Hint? */ + if (lsi->mtu_changed && c->c2.ipv4_tun) + { + frame_adjust_path_mtu (&c->c2.frame_fragment, c->c2.link_socket->mtu, + c->options.proto); + lsi->mtu_changed = false; + } + + if (fragment_outgoing_defined (c->c2.fragment)) + { + if (!c->c2.to_link.len) + { + /* encrypt a fragment for output to TCP/UDP port */ + ASSERT (fragment_ready_to_send (c->c2.fragment, &c->c2.buf, &c->c2.frame_fragment)); + encrypt_sign (c, false); + } + } + + fragment_housekeeping (c->c2.fragment, &c->c2.frame_fragment, &c->c2.timeval); +} +#endif + +/* + * Buffer reallocation, for use with null encryption. + */ +static inline void +buffer_turnover (const uint8_t *orig_buf, struct buffer *dest_stub, struct buffer *src_stub, struct buffer *storage) +{ + if (orig_buf == src_stub->data && src_stub->data != storage->data) + { + buf_assign (storage, src_stub); + *dest_stub = *storage; + } + else + { + *dest_stub = *src_stub; + } +} + +/* + * Compress, fragment, encrypt and HMAC-sign an outgoing packet. + * Input: c->c2.buf + * Output: c->c2.to_link + */ +void +encrypt_sign (struct context *c, bool comp_frag) +{ + struct context_buffers *b = c->c2.buffers; + const uint8_t *orig_buf = c->c2.buf.data; + +#if P2MP_SERVER + /* + * Drop non-TLS outgoing packet if client-connect script/plugin + * has not yet succeeded. + */ + if (c->c2.context_auth != CAS_SUCCEEDED) + c->c2.buf.len = 0; +#endif + + if (comp_frag) + { +#ifdef USE_LZO + /* Compress the packet. */ + if (c->options.comp_lzo) + lzo_compress (&c->c2.buf, b->lzo_compress_buf, &c->c2.lzo_compwork, &c->c2.frame); +#endif +#ifdef ENABLE_FRAGMENT + if (c->c2.fragment) + fragment_outgoing (c->c2.fragment, &c->c2.buf, &c->c2.frame_fragment); +#endif + } + +#ifdef USE_CRYPTO +#ifdef USE_SSL + /* + * If TLS mode, get the key we will use to encrypt + * the packet. + */ + if (c->c2.tls_multi) + { + /*tls_mutex_lock (c->c2.tls_multi);*/ + tls_pre_encrypt (c->c2.tls_multi, &c->c2.buf, &c->c2.crypto_options); + } +#endif + + /* + * Encrypt the packet and write an optional + * HMAC signature. + */ + openvpn_encrypt (&c->c2.buf, b->encrypt_buf, &c->c2.crypto_options, &c->c2.frame); +#endif + /* + * Get the address we will be sending the packet to. + */ + link_socket_get_outgoing_addr (&c->c2.buf, get_link_socket_info (c), + &c->c2.to_link_addr); +#ifdef USE_CRYPTO +#ifdef USE_SSL + /* + * In TLS mode, prepend the appropriate one-byte opcode + * to the packet which identifies it as a data channel + * packet and gives the low-permutation version of + * the key-id to the recipient so it knows which + * decrypt key to use. + */ + if (c->c2.tls_multi) + { + tls_post_encrypt (c->c2.tls_multi, &c->c2.buf); + /*tls_mutex_unlock (c->c2.tls_multi);*/ + } +#endif +#endif + + /* if null encryption, copy result to read_tun_buf */ + buffer_turnover (orig_buf, &c->c2.to_link, &c->c2.buf, &b->read_tun_buf); +} + +/* + * Coarse timers work to 1 second resolution. + */ +static void +process_coarse_timers (struct context *c) +{ +#ifdef USE_CRYPTO + /* flush current packet-id to file once per 60 + seconds if --replay-persist was specified */ + check_packet_id_persist_flush (c); +#endif + + /* should we update status file? */ + check_status_file (c); + + /* process connection establishment items */ + check_connection_established (c); + +#if P2MP + /* see if we should send a push_request in response to --pull */ + check_push_request (c); +#endif + + /* process --route options */ + check_add_routes (c); + + /* possibly exit due to --inactive */ + check_inactivity_timeout (c); + if (c->sig->signal_received) + return; + + /* restart if ping not received */ + check_ping_restart (c); + if (c->sig->signal_received) + return; + +#if P2MP + check_scheduled_exit (c); + if (c->sig->signal_received) + return; +#endif + +#ifdef ENABLE_OCC + /* Should we send an OCC_REQUEST message? */ + check_send_occ_req (c); + + /* Should we send an MTU load test? */ + check_send_occ_load_test (c); + + /* Should we send an OCC_EXIT message to remote? */ + if (c->c2.explicit_exit_notification_time_wait) + process_explicit_exit_notification_timer_wakeup (c); +#endif + + /* Should we ping the remote? */ + check_ping_send (c); +} + +static void +check_coarse_timers_dowork (struct context *c) +{ + const struct timeval save = c->c2.timeval; + c->c2.timeval.tv_sec = BIG_TIMEOUT; + c->c2.timeval.tv_usec = 0; + process_coarse_timers (c); + c->c2.coarse_timer_wakeup = now + c->c2.timeval.tv_sec; + + dmsg (D_INTERVAL, "TIMER: coarse timer wakeup %d seconds", (int) c->c2.timeval.tv_sec); + + /* Is the coarse timeout NOT the earliest one? */ + if (c->c2.timeval.tv_sec > save.tv_sec) + c->c2.timeval = save; +} + +static inline void +check_coarse_timers (struct context *c) +{ + const time_t local_now = now; + if (local_now >= c->c2.coarse_timer_wakeup) + check_coarse_timers_dowork (c); + else + context_reschedule_sec (c, c->c2.coarse_timer_wakeup - local_now); +} + +static void +check_timeout_random_component_dowork (struct context *c) +{ + const int update_interval = 10; /* seconds */ + c->c2.update_timeout_random_component = now + update_interval; + c->c2.timeout_random_component.tv_usec = (time_t) get_random () & 0x0003FFFF; + c->c2.timeout_random_component.tv_sec = 0; + + dmsg (D_INTERVAL, "RANDOM USEC=%d", (int) c->c2.timeout_random_component.tv_usec); +} + +static inline void +check_timeout_random_component (struct context *c) +{ + if (now >= c->c2.update_timeout_random_component) + check_timeout_random_component_dowork (c); + if (c->c2.timeval.tv_sec >= 1) + tv_add (&c->c2.timeval, &c->c2.timeout_random_component); +} + +#ifdef ENABLE_SOCKS + +/* + * Handle addition and removal of the 10-byte Socks5 header + * in UDP packets. + */ + +static inline void +socks_postprocess_incoming_link (struct context *c) +{ + if (c->c2.link_socket->socks_proxy && c->c2.link_socket->info.proto == PROTO_UDPv4) + socks_process_incoming_udp (&c->c2.buf, &c->c2.from); +} + +static inline void +socks_preprocess_outgoing_link (struct context *c, + struct sockaddr_in **to_addr, + int *size_delta) +{ + if (c->c2.link_socket->socks_proxy && c->c2.link_socket->info.proto == PROTO_UDPv4) + { + *size_delta += socks_process_outgoing_udp (&c->c2.to_link, &c->c2.to_link_addr); + *to_addr = &c->c2.link_socket->socks_relay; + } +} + +/* undo effect of socks_preprocess_outgoing_link */ +static inline void +link_socket_write_post_size_adjust (int *size, + int size_delta, + struct buffer *buf) +{ + if (size_delta > 0 && *size > size_delta) + { + *size -= size_delta; + if (!buf_advance (buf, size_delta)) + *size = 0; + } +} +#endif + +/* + * Output: c->c2.buf + */ + +void +read_incoming_link (struct context *c) +{ + /* + * Set up for recvfrom call to read datagram + * sent to our TCP/UDP port. + */ + int status; + + perf_push (PERF_READ_IN_LINK); + + ASSERT (!c->c2.to_tun.len); + + c->c2.buf = c->c2.buffers->read_link_buf; + ASSERT (buf_init (&c->c2.buf, FRAME_HEADROOM_ADJ (&c->c2.frame, FRAME_HEADROOM_MARKER_READ_LINK))); + status = link_socket_read (c->c2.link_socket, &c->c2.buf, MAX_RW_SIZE_LINK (&c->c2.frame), &c->c2.from); + + if (socket_connection_reset (c->c2.link_socket, status)) + { + /* received a disconnect from a connection-oriented protocol */ + if (c->options.inetd) + { + c->sig->signal_received = SIGTERM; + msg (D_STREAM_ERRORS, "Connection reset, inetd/xinetd exit [%d]", status); + } + else + { + c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- TCP connection reset */ + msg (D_STREAM_ERRORS, "Connection reset, restarting [%d]", status); + } + c->sig->signal_text = "connection-reset"; + perf_pop (); + return; + } + + /* check recvfrom status */ + check_status (status, "read", c->c2.link_socket, NULL); + +#ifdef ENABLE_SOCKS + /* Remove socks header if applicable */ + socks_postprocess_incoming_link (c); +#endif + + perf_pop (); +} + +/* + * Input: c->c2.buf + * Output: c->c2.to_tun + */ + +void +process_incoming_link (struct context *c) +{ + struct gc_arena gc = gc_new (); + bool decrypt_status; + struct link_socket_info *lsi = get_link_socket_info (c); + const uint8_t *orig_buf = c->c2.buf.data; + + perf_push (PERF_PROC_IN_LINK); + + if (c->c2.buf.len > 0) + { + c->c2.link_read_bytes += c->c2.buf.len; + c->c2.original_recv_size = c->c2.buf.len; + } + else + c->c2.original_recv_size = 0; + +#ifdef ENABLE_DEBUG + /* take action to corrupt packet if we are in gremlin test mode */ + if (c->options.gremlin) { + if (!ask_gremlin (c->options.gremlin)) + c->c2.buf.len = 0; + corrupt_gremlin (&c->c2.buf, c->options.gremlin); + } +#endif + + /* log incoming packet */ +#ifdef LOG_RW + if (c->c2.log_rw) + fprintf (stderr, "R"); +#endif + msg (D_LINK_RW, "%s READ [%d] from %s: %s", + proto2ascii (lsi->proto, true), + BLEN (&c->c2.buf), + print_sockaddr (&c->c2.from, &gc), + PROTO_DUMP (&c->c2.buf, &gc)); + + /* + * Good, non-zero length packet received. + * Commence multi-stage processing of packet, + * such as authenticate, decrypt, decompress. + * If any stage fails, it sets buf.len to 0 or -1, + * telling downstream stages to ignore the packet. + */ + if (c->c2.buf.len > 0) + { + if (!link_socket_verify_incoming_addr (&c->c2.buf, lsi, &c->c2.from)) + link_socket_bad_incoming_addr (&c->c2.buf, lsi, &c->c2.from); + +#ifdef USE_CRYPTO +#ifdef USE_SSL + if (c->c2.tls_multi) + { + /* + * If tls_pre_decrypt returns true, it means the incoming + * packet was a good TLS control channel packet. If so, TLS code + * will deal with the packet and set buf.len to 0 so downstream + * stages ignore it. + * + * If the packet is a data channel packet, tls_pre_decrypt + * will load crypto_options with the correct encryption key + * and return false. + */ + /*tls_mutex_lock (c->c2.tls_multi);*/ + if (tls_pre_decrypt (c->c2.tls_multi, &c->c2.from, &c->c2.buf, &c->c2.crypto_options)) + { + interval_action (&c->c2.tmp_int); + + /* reset packet received timer if TLS packet */ + if (c->options.ping_rec_timeout) + event_timeout_reset (&c->c2.ping_rec_interval); + } + } +#if P2MP_SERVER + /* + * Drop non-TLS packet if client-connect script/plugin has not + * yet succeeded. + */ + if (c->c2.context_auth != CAS_SUCCEEDED) + c->c2.buf.len = 0; +#endif +#endif /* USE_SSL */ + + /* authenticate and decrypt the incoming packet */ + decrypt_status = openvpn_decrypt (&c->c2.buf, c->c2.buffers->decrypt_buf, &c->c2.crypto_options, &c->c2.frame); + +#ifdef USE_SSL + if (c->c2.tls_multi) + { + /*tls_mutex_unlock (c->c2.tls_multi);*/ + } +#endif + + if (!decrypt_status && link_socket_connection_oriented (c->c2.link_socket)) + { + /* decryption errors are fatal in TCP mode */ + c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- decryption error in TCP mode */ + c->sig->signal_text = "decryption-error"; + msg (D_STREAM_ERRORS, "Fatal decryption error (process_incoming_link), restarting"); + goto done; + } + +#endif /* USE_CRYPTO */ + +#ifdef ENABLE_FRAGMENT + if (c->c2.fragment) + fragment_incoming (c->c2.fragment, &c->c2.buf, &c->c2.frame_fragment); +#endif + +#ifdef USE_LZO + /* decompress the incoming packet */ + if (c->options.comp_lzo) + lzo_decompress (&c->c2.buf, c->c2.buffers->lzo_decompress_buf, &c->c2.lzo_compwork, &c->c2.frame); +#endif + /* + * Set our "official" outgoing address, since + * if buf.len is non-zero, we know the packet + * authenticated. In TLS mode we do nothing + * because TLS mode takes care of source address + * authentication. + * + * Also, update the persisted version of our packet-id. + */ + if (!TLS_MODE (c)) + link_socket_set_outgoing_addr (&c->c2.buf, lsi, &c->c2.from, NULL, c->c2.es); + + /* reset packet received timer */ + if (c->options.ping_rec_timeout && c->c2.buf.len > 0) + event_timeout_reset (&c->c2.ping_rec_interval); + + /* increment authenticated receive byte count */ + if (c->c2.buf.len > 0) + { + c->c2.link_read_bytes_auth += c->c2.buf.len; + c->c2.max_recv_size_local = max_int (c->c2.original_recv_size, c->c2.max_recv_size_local); + } + + /* Did we just receive an openvpn ping packet? */ + if (is_ping_msg (&c->c2.buf)) + { + dmsg (D_PACKET_CONTENT, "RECEIVED PING PACKET"); + c->c2.buf.len = 0; /* drop packet */ + } + +#ifdef ENABLE_OCC + /* Did we just receive an OCC packet? */ + if (is_occ_msg (&c->c2.buf)) + process_received_occ_msg (c); +#endif + + buffer_turnover (orig_buf, &c->c2.to_tun, &c->c2.buf, &c->c2.buffers->read_link_buf); + + /* to_tun defined + unopened tuntap can cause deadlock */ + if (!tuntap_defined (c->c1.tuntap)) + c->c2.to_tun.len = 0; + } + else + { + buf_reset (&c->c2.to_tun); + } + done: + perf_pop (); + gc_free (&gc); +} + +/* + * Output: c->c2.buf + */ + +void +read_incoming_tun (struct context *c) +{ + perf_push (PERF_READ_IN_TUN); + + /* + * Setup for read() call on TUN/TAP device. + */ + ASSERT (!c->c2.to_link.len); + + c->c2.buf = c->c2.buffers->read_tun_buf; +#ifdef TUN_PASS_BUFFER + read_tun_buffered (c->c1.tuntap, &c->c2.buf, MAX_RW_SIZE_TUN (&c->c2.frame)); +#else + ASSERT (buf_init (&c->c2.buf, FRAME_HEADROOM (&c->c2.frame))); + ASSERT (buf_safe (&c->c2.buf, MAX_RW_SIZE_TUN (&c->c2.frame))); + c->c2.buf.len = read_tun (c->c1.tuntap, BPTR (&c->c2.buf), MAX_RW_SIZE_TUN (&c->c2.frame)); +#endif + + /* Was TUN/TAP interface stopped? */ + if (tuntap_stop (c->c2.buf.len)) + { + c->sig->signal_received = SIGTERM; + c->sig->signal_text = "tun-stop"; + msg (M_INFO, "TUN/TAP interface has been stopped, exiting"); + perf_pop (); + return; + } + + /* Check the status return from read() */ + check_status (c->c2.buf.len, "read from TUN/TAP", NULL, c->c1.tuntap); + + perf_pop (); +} + +/* + * Input: c->c2.buf + * Output: c->c2.to_link + */ + +void +process_incoming_tun (struct context *c) +{ + struct gc_arena gc = gc_new (); + + perf_push (PERF_PROC_IN_TUN); + + if (c->c2.buf.len > 0) + c->c2.tun_read_bytes += c->c2.buf.len; + +#ifdef LOG_RW + if (c->c2.log_rw) + fprintf (stderr, "r"); +#endif + + /* Show packet content */ + dmsg (D_TUN_RW, "TUN READ [%d]: %s md5=%s", + BLEN (&c->c2.buf), + format_hex (BPTR (&c->c2.buf), BLEN (&c->c2.buf), 80, &gc), + MD5SUM (BPTR (&c->c2.buf), BLEN (&c->c2.buf), &gc)); + + if (c->c2.buf.len > 0) + { + /* + * The --passtos and --mssfix options require + * us to examine the IPv4 header. + */ + process_ipv4_header (c, PIPV4_PASSTOS|PIPV4_MSSFIX, &c->c2.buf); + encrypt_sign (c, true); + } + else + { + buf_reset (&c->c2.to_link); + } + perf_pop (); + gc_free (&gc); +} + +void +process_ipv4_header (struct context *c, unsigned int flags, struct buffer *buf) +{ + if (!c->options.mssfix) + flags &= ~PIPV4_MSSFIX; +#if PASSTOS_CAPABILITY + if (!c->options.passtos) + flags &= ~PIPV4_PASSTOS; +#endif + + if (buf->len > 0) + { + /* + * The --passtos and --mssfix options require + * us to examine the IPv4 header. + */ +#if PASSTOS_CAPABILITY + if (flags & (PIPV4_PASSTOS|PIPV4_MSSFIX)) +#else + if (flags & PIPV4_MSSFIX) +#endif + { + struct buffer ipbuf = *buf; + if (is_ipv4 (TUNNEL_TYPE (c->c1.tuntap), &ipbuf)) + { +#if PASSTOS_CAPABILITY + /* extract TOS from IP header */ + if (flags & PIPV4_PASSTOS) + link_socket_extract_tos (c->c2.link_socket, &ipbuf); +#endif + + /* possibly alter the TCP MSS */ + if (flags & PIPV4_MSSFIX) + mss_fixup (&ipbuf, MTU_TO_MSS (TUN_MTU_SIZE_DYNAMIC (&c->c2.frame))); + } + } + } +} + +/* + * Input: c->c2.to_link + */ + +void +process_outgoing_link (struct context *c) +{ + struct gc_arena gc = gc_new (); + + perf_push (PERF_PROC_OUT_LINK); + + if (c->c2.to_link.len > 0 && c->c2.to_link.len <= EXPANDED_SIZE (&c->c2.frame)) + { + /* + * Setup for call to send/sendto which will send + * packet to remote over the TCP/UDP port. + */ + int size = 0; + ASSERT (addr_defined (&c->c2.to_link_addr)); + +#ifdef ENABLE_DEBUG + /* In gremlin-test mode, we may choose to drop this packet */ + if (!c->options.gremlin || ask_gremlin (c->options.gremlin)) +#endif + { + /* + * Let the traffic shaper know how many bytes + * we wrote. + */ +#ifdef HAVE_GETTIMEOFDAY + if (c->options.shaper) + shaper_wrote_bytes (&c->c2.shaper, BLEN (&c->c2.to_link) + + datagram_overhead (c->options.proto)); +#endif + /* + * Let the pinger know that we sent a packet. + */ + if (c->options.ping_send_timeout) + event_timeout_reset (&c->c2.ping_send_interval); + +#if PASSTOS_CAPABILITY + /* Set TOS */ + link_socket_set_tos (c->c2.link_socket); +#endif + + /* Log packet send */ +#ifdef LOG_RW + if (c->c2.log_rw) + fprintf (stderr, "W"); +#endif + msg (D_LINK_RW, "%s WRITE [%d] to %s: %s", + proto2ascii (c->c2.link_socket->info.proto, true), + BLEN (&c->c2.to_link), + print_sockaddr (&c->c2.to_link_addr, &gc), + PROTO_DUMP (&c->c2.to_link, &gc)); + + /* Packet send complexified by possible Socks5 usage */ + { + struct sockaddr_in *to_addr = &c->c2.to_link_addr; +#ifdef ENABLE_SOCKS + int size_delta = 0; +#endif + +#ifdef ENABLE_SOCKS + /* If Socks5 over UDP, prepend header */ + socks_preprocess_outgoing_link (c, &to_addr, &size_delta); +#endif + /* Send packet */ + size = link_socket_write (c->c2.link_socket, &c->c2.to_link, to_addr); + +#ifdef ENABLE_SOCKS + /* Undo effect of prepend */ + link_socket_write_post_size_adjust (&size, size_delta, &c->c2.to_link); +#endif + } + + if (size > 0) + { + c->c2.max_send_size_local = max_int (size, c->c2.max_send_size_local); + c->c2.link_write_bytes += size; + } + } + + /* Check return status */ + check_status (size, "write", c->c2.link_socket, NULL); + + if (size > 0) + { + /* Did we write a different size packet than we intended? */ + if (size != BLEN (&c->c2.to_link)) + msg (D_LINK_ERRORS, + "TCP/UDP packet was truncated/expanded on write to %s (tried=%d,actual=%d)", + print_sockaddr (&c->c2.to_link_addr, &gc), + BLEN (&c->c2.to_link), + size); + } + } + else + { + if (c->c2.to_link.len > 0) + msg (D_LINK_ERRORS, "TCP/UDP packet too large on write to %s (tried=%d,max=%d)", + print_sockaddr (&c->c2.to_link_addr, &gc), + c->c2.to_link.len, + EXPANDED_SIZE (&c->c2.frame)); + } + + buf_reset (&c->c2.to_link); + + perf_pop (); + gc_free (&gc); +} + +/* + * Input: c->c2.to_tun + */ + +void +process_outgoing_tun (struct context *c) +{ + struct gc_arena gc = gc_new (); + + perf_push (PERF_PROC_OUT_TUN); + + /* + * Set up for write() call to TUN/TAP + * device. + */ + ASSERT (c->c2.to_tun.len > 0); + + /* + * The --mssfix option requires + * us to examine the IPv4 header. + */ + process_ipv4_header (c, PIPV4_MSSFIX, &c->c2.to_tun); + + if (c->c2.to_tun.len <= MAX_RW_SIZE_TUN (&c->c2.frame)) + { + /* + * Write to TUN/TAP device. + */ + int size; + +#ifdef LOG_RW + if (c->c2.log_rw) + fprintf (stderr, "w"); +#endif + dmsg (D_TUN_RW, "TUN WRITE [%d]: %s md5=%s", + BLEN (&c->c2.to_tun), + format_hex (BPTR (&c->c2.to_tun), BLEN (&c->c2.to_tun), 80, &gc), + MD5SUM (BPTR (&c->c2.to_tun), BLEN (&c->c2.to_tun), &gc)); + +#ifdef TUN_PASS_BUFFER + size = write_tun_buffered (c->c1.tuntap, &c->c2.to_tun); +#else + size = write_tun (c->c1.tuntap, BPTR (&c->c2.to_tun), BLEN (&c->c2.to_tun)); +#endif + + if (size > 0) + c->c2.tun_write_bytes += size; + check_status (size, "write to TUN/TAP", NULL, c->c1.tuntap); + + /* check written packet size */ + if (size > 0) + { + /* Did we write a different size packet than we intended? */ + if (size != BLEN (&c->c2.to_tun)) + msg (D_LINK_ERRORS, + "TUN/TAP packet was destructively fragmented on write to %s (tried=%d,actual=%d)", + c->c1.tuntap->actual_name, + BLEN (&c->c2.to_tun), + size); + } + } + else + { + /* + * This should never happen, probably indicates some kind + * of MTU mismatch. + */ + msg (D_LINK_ERRORS, "tun packet too large on write (tried=%d,max=%d)", + c->c2.to_tun.len, + MAX_RW_SIZE_TUN (&c->c2.frame)); + } + + /* + * Putting the --inactive timeout reset here, ensures that we will timeout + * if the remote goes away, even if we are trying to send data to the + * remote and failing. + */ + register_activity (c); + + buf_reset (&c->c2.to_tun); + + perf_pop (); + gc_free (&gc); +} + +void +pre_select (struct context *c) +{ + /* make sure current time (now) is updated on function entry */ + + /* + * Start with an effectively infinite timeout, then let it + * reduce to a timeout that reflects the component which + * needs the earliest service. + */ + c->c2.timeval.tv_sec = BIG_TIMEOUT; + c->c2.timeval.tv_usec = 0; + +#if defined(WIN32) + if (check_debug_level (D_TAP_WIN32_DEBUG)) + { + c->c2.timeval.tv_sec = 1; + if (tuntap_defined (c->c1.tuntap)) + tun_show_debug (c->c1.tuntap); + } +#endif + + /* check coarse timers? */ + check_coarse_timers (c); + if (c->sig->signal_received) + return; + + /* Does TLS need service? */ + check_tls (c); + + /* In certain cases, TLS errors will require a restart */ + check_tls_errors (c); + if (c->sig->signal_received) + return; + + /* check for incoming configuration info on the control channel */ + check_incoming_control_channel (c); + +#ifdef ENABLE_OCC + /* Should we send an OCC message? */ + check_send_occ_msg (c); +#endif + +#ifdef ENABLE_FRAGMENT + /* Should we deliver a datagram fragment to remote? */ + check_fragment (c); +#endif + + /* Update random component of timeout */ + check_timeout_random_component (c); +} + +/* + * Wait for I/O events. Used for both TCP & UDP sockets + * in point-to-point mode and for UDP sockets in + * point-to-multipoint mode. + */ + +void +io_wait_dowork (struct context *c, const unsigned int flags) +{ + unsigned int socket = 0; + unsigned int tuntap = 0; + struct event_set_return esr[4]; + + /* These shifts all depend on EVENT_READ and EVENT_WRITE */ + static const int socket_shift = 0; /* depends on SOCKET_READ and SOCKET_WRITE */ + static const int tun_shift = 2; /* depends on TUN_READ and TUN_WRITE */ + static const int err_shift = 4; /* depends on ES_ERROR */ +#ifdef ENABLE_MANAGEMENT + static const int management_shift = 6; /* depends on MANAGEMENT_READ and MANAGEMENT_WRITE */ +#endif + + /* + * Decide what kind of events we want to wait for. + */ + event_reset (c->c2.event_set); + + /* + * On win32 we use the keyboard or an event object as a source + * of asynchronous signals. + */ + if (flags & IOW_WAIT_SIGNAL) + wait_signal (c->c2.event_set, (void*)&err_shift); + + /* + * If outgoing data (for TCP/UDP port) pending, wait for ready-to-send + * status from TCP/UDP port. Otherwise, wait for incoming data on + * TUN/TAP device. + */ + if (flags & IOW_TO_LINK) + { + if (flags & IOW_SHAPER) + { + /* + * If sending this packet would put us over our traffic shaping + * quota, don't send -- instead compute the delay we must wait + * until it will be OK to send the packet. + */ +#ifdef HAVE_GETTIMEOFDAY + int delay = 0; + + /* set traffic shaping delay in microseconds */ + if (c->options.shaper) + delay = max_int (delay, shaper_delay (&c->c2.shaper)); + + if (delay < 1000) + { + socket |= EVENT_WRITE; + } + else + { + shaper_soonest_event (&c->c2.timeval, delay); + } +#else /* HAVE_GETTIMEOFDAY */ + socket |= EVENT_WRITE; +#endif /* HAVE_GETTIMEOFDAY */ + } + else + { + socket |= EVENT_WRITE; + } + } + else if (!((flags & IOW_FRAG) && TO_LINK_FRAG (c))) + { + if (flags & IOW_READ_TUN) + tuntap |= EVENT_READ; + } + + /* + * If outgoing data (for TUN/TAP device) pending, wait for ready-to-send status + * from device. Otherwise, wait for incoming data on TCP/UDP port. + */ + if (flags & IOW_TO_TUN) + { + tuntap |= EVENT_WRITE; + } + else + { + if (flags & IOW_READ_LINK) + socket |= EVENT_READ; + } + + /* + * outgoing bcast buffer waiting to be sent? + */ + if (flags & IOW_MBUF) + socket |= EVENT_WRITE; + + /* + * Force wait on TUN input, even if also waiting on TCP/UDP output + */ + if (flags & IOW_READ_TUN_FORCE) + tuntap |= EVENT_READ; + + /* + * Configure event wait based on socket, tuntap flags. + */ + socket_set (c->c2.link_socket, c->c2.event_set, socket, (void*)&socket_shift, NULL); + tun_set (c->c1.tuntap, c->c2.event_set, tuntap, (void*)&tun_shift, NULL); + +#ifdef ENABLE_MANAGEMENT + if (management) + management_socket_set (management, c->c2.event_set, (void*)&management_shift, NULL); +#endif + + /* + * Possible scenarios: + * (1) tcp/udp port has data available to read + * (2) tcp/udp port is ready to accept more data to write + * (3) tun dev has data available to read + * (4) tun dev is ready to accept more data to write + * (5) we received a signal (handler sets signal_received) + * (6) timeout (tv) expired + */ + + c->c2.event_set_status = ES_ERROR; + + if (!c->sig->signal_received) + { + if (!(flags & IOW_CHECK_RESIDUAL) || !socket_read_residual (c->c2.link_socket)) + { + int status; + +#ifdef ENABLE_DEBUG + if (check_debug_level (D_EVENT_WAIT)) + show_wait_status (c); +#endif + + /* + * Wait for something to happen. + */ + status = event_wait (c->c2.event_set, &c->c2.timeval, esr, SIZE(esr)); + + check_status (status, "event_wait", NULL, NULL); + + if (status > 0) + { + int i; + c->c2.event_set_status = 0; + for (i = 0; i < status; ++i) + { + const struct event_set_return *e = &esr[i]; + c->c2.event_set_status |= ((e->rwflags & 3) << *((int*)e->arg)); + } + } + else if (status == 0) + { + c->c2.event_set_status = ES_TIMEOUT; + } + } + else + { + c->c2.event_set_status = SOCKET_READ; + } + } + + /* 'now' should always be a reasonably up-to-date timestamp */ + update_time (); + + /* set signal_received if a signal was received */ + if (c->c2.event_set_status & ES_ERROR) + get_signal (&c->sig->signal_received); + + dmsg (D_EVENT_WAIT, "I/O WAIT status=0x%04x", c->c2.event_set_status); +} + +void +process_io (struct context *c) +{ + const unsigned int status = c->c2.event_set_status; + +#ifdef ENABLE_MANAGEMENT + if (status & (MANAGEMENT_READ|MANAGEMENT_WRITE)) + { + ASSERT (management); + management_io (management); + } +#endif + + /* TCP/UDP port ready to accept write */ + if (status & SOCKET_WRITE) + { + process_outgoing_link (c); + } + /* TUN device ready to accept write */ + else if (status & TUN_WRITE) + { + process_outgoing_tun (c); + } + /* Incoming data on TCP/UDP port */ + else if (status & SOCKET_READ) + { + read_incoming_link (c); + if (!IS_SIG (c)) + process_incoming_link (c); + } + /* Incoming data on TUN device */ + else if (status & TUN_READ) + { + read_incoming_tun (c); + if (!IS_SIG (c)) + process_incoming_tun (c); + } +} |