summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Sommerseth <davids@redhat.com>2011-07-25 01:44:27 +0200
committerDavid Sommerseth <davids@redhat.com>2011-08-19 08:46:43 +0200
commit576dc96ca1ef1badb651e05ac694f07c91e02518 (patch)
treef1d71d43a93853a6d44f34e6e38238ada1a9eb8b
parentb7e0d372e3aeb07d129642473d274d7d590eea1a (diff)
parent9a105405a32cd3d7c4abafdb91e85494687392e9 (diff)
downloadopenvpn-576dc96ca1ef1badb651e05ac694f07c91e02518.tar.gz
openvpn-576dc96ca1ef1badb651e05ac694f07c91e02518.tar.xz
openvpn-576dc96ca1ef1badb651e05ac694f07c91e02518.zip
Merge remote branch SVN 2.1 into the git tree
Hopefully the last SVN merge we need to do, as these merges are getting more and more difficult. Most of the files had minor changes, but due to the CRLF unification patch (commit 6b2883a637fe73492) we got an increased number of conflicts. In addition inclusion of IPv6 support makes the creates a lot of merge issues in route.c and socket.c This merge also reverts commit 7c18c6353904f8c6e7 which merged add_bypass_address() into add_host_route_if_nonlocal(). However the SVN tree began to use add_bypass_address() another place, where at first glance it did not be appropriate to use add_host_route_if_nonlocal(). This merge has gone through a 'make check' without any errors, but have not been tested more thoroughly yet. Conflicts: ChangeLog INSTALL INSTALL-win32.txt Makefile.am acinclude.m4 base64.c buffer.c buffer.h common.h configure.ac contrib/pull-resolv-conf/client.down contrib/pull-resolv-conf/client.up crypto.c cryptoapi.c easy-rsa/2.0/Makefile easy-rsa/2.0/README easy-rsa/2.0/build-ca easy-rsa/2.0/build-dh easy-rsa/2.0/build-inter easy-rsa/2.0/build-key easy-rsa/2.0/build-key-pass easy-rsa/2.0/build-key-pkcs12 easy-rsa/2.0/build-key-server easy-rsa/2.0/build-req easy-rsa/2.0/build-req-pass easy-rsa/2.0/clean-all easy-rsa/2.0/inherit-inter easy-rsa/2.0/list-crl easy-rsa/2.0/pkitool easy-rsa/2.0/revoke-full easy-rsa/2.0/sign-req easy-rsa/2.0/vars easy-rsa/2.0/whichopensslcnf easy-rsa/Windows/build-ca-pass.bat easy-rsa/Windows/build-key-pass.bat easy-rsa/Windows/build-key-server-pass.bat easy-rsa/Windows/init-config.bat easy-rsa/Windows/vars.bat.sample error.c error.h forward.c helper.c httpdigest.c httpdigest.h ieproxy.c init.c init.h install-win32/Makefile.am install-win32/makeopenvpn install-win32/openssl/openssl097.patch install-win32/openssl/openssl098.patch install-win32/openvpn.nsi list.c list.h manage.c manage.h management/management-notes.txt mbuf.c mbuf.h misc.c misc.h mroute.c mroute.h msvc/autodefs.h.in msvc/config.py msvc/msvc.mak mtcp.c mudp.c multi.c multi.h occ.c openvpn-plugin.h openvpn.8 openvpn.h options.c options.h otime.c otime.h perf.c pf.c ping.c pkcs11.c plugin.c plugin.h plugin/auth-pam/README plugin/auth-pam/auth-pam.c pool.c pool.h proto.h proxy.c ps.c push.c reliable.c route.c route.h sample-config-files/firewall.sh sample-scripts/bridge-start sample-scripts/bridge-stop sample-scripts/openvpn.init sample-scripts/verify-cn schedule.c schedule.h service-win32/openvpnserv.c sig.c socket.c socket.h socks.c socks.h ssl.c ssl.h status.c syshead.h tap-win32/SOURCES.in tap-win32/common.h tap-win32/proto.h tap-win32/tapdrvr.c tap-win32/types.h tun.c tun.h version.m4 win/autodefs.h.in win/build.py win/build_all.py win/build_ddk.py win/build_exe.py win/config.py win/config_all.py win/config_tap.py win/config_ti.py win/js.py win/make_dist.py win/msvc.mak.in win/settings.in win/show.py win/sign.py win/tap_span.py win/wb.py win32.c win32.h Signed-off-by: David Sommerseth <davids@redhat.com> Reviewed-by: Gert Doering <gert@greenie.muc.de> Reviewed-by: James Yonan <james@openvpn.net> Reviewed-by: Adriaan de Jong <dejong@fox-it.com>
-rw-r--r--base64.c12
-rw-r--r--forward.c4
-rw-r--r--init.c128
-rw-r--r--manage.c88
-rw-r--r--manage.h22
-rw-r--r--management/management-notes.txt142
-rw-r--r--misc.c72
-rw-r--r--misc.h15
-rw-r--r--mtcp.c5
-rw-r--r--openvpn.847
-rw-r--r--options.c34
-rw-r--r--options.h25
-rw-r--r--route.c1293
-rw-r--r--route.h116
-rw-r--r--ssl.c41
-rw-r--r--ssl.h6
-rw-r--r--syshead.h11
-rw-r--r--tun.c13
18 files changed, 1366 insertions, 708 deletions
diff --git a/base64.c b/base64.c
index 26ca7d7..95ccffc 100644
--- a/base64.c
+++ b/base64.c
@@ -41,7 +41,11 @@
static char base64_chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
+/*
+ * base64 encode input data of length size to malloced
+ * buffer which is returned as *str. Returns string
+ * length of *str.
+ */
int
base64_encode(const void *data, int size, char **str)
{
@@ -115,7 +119,11 @@ token_decode(const char *token)
return DECODE_ERROR;
return (marker << 24) | val;
}
-
+/*
+ * Decode base64 str, outputting data to buffer
+ * at data of length size. Return length of
+ * decoded data written or -1 on error or overflow.
+ */
int
base64_decode(const char *str, void *data, int size)
{
diff --git a/forward.c b/forward.c
index b43c1c0..4592cfc 100644
--- a/forward.c
+++ b/forward.c
@@ -1006,7 +1006,7 @@ process_ipv4_header (struct context *c, unsigned int flags, struct buffer *buf)
if (!c->options.passtos)
flags &= ~PIPV4_PASSTOS;
#endif
- if (!c->options.route_gateway_via_dhcp || !route_list_default_gateway_needed (c->c1.route_list))
+ if (!c->options.route_gateway_via_dhcp || !route_list_vpn_gateway_needed (c->c1.route_list))
flags &= ~PIPV4_EXTRACT_DHCP_ROUTER;
if (buf->len > 0)
@@ -1047,7 +1047,7 @@ process_ipv4_header (struct context *c, unsigned int flags, struct buffer *buf)
{
const in_addr_t dhcp_router = dhcp_extract_router_msg (&ipbuf);
if (dhcp_router)
- route_list_add_default_gateway (c->c1.route_list, c->c2.es, dhcp_router);
+ route_list_add_vpn_gateway (c->c1.route_list, c->c2.es, dhcp_router);
}
}
}
diff --git a/init.c b/init.c
index 4a16fba..b7c09db 100644
--- a/init.c
+++ b/init.c
@@ -199,6 +199,90 @@ management_callback_http_proxy_fallback_cmd (void *arg, const char *server, cons
#endif
+#if MANAGEMENT_QUERY_REMOTE
+
+static bool
+management_callback_remote_cmd (void *arg, const char **p)
+{
+ struct context *c = (struct context *) arg;
+ struct connection_entry *ce = &c->options.ce;
+ int ret = false;
+ if (p[1] && ((ce->flags>>CE_MAN_QUERY_REMOTE_SHIFT)&CE_MAN_QUERY_REMOTE_MASK) == CE_MAN_QUERY_REMOTE_QUERY)
+ {
+ int flags = 0;
+ if (!strcmp(p[1], "ACCEPT"))
+ {
+ flags = CE_MAN_QUERY_REMOTE_ACCEPT;
+ ret = true;
+ }
+ else if (!strcmp(p[1], "SKIP"))
+ {
+ flags = CE_MAN_QUERY_REMOTE_SKIP;
+ ret = true;
+ }
+ else if (!strcmp(p[1], "MOD") && p[2] && p[3])
+ {
+ const int port = atoi(p[3]);
+ if (strlen(p[2]) < RH_HOST_LEN && legal_ipv4_port(port))
+ {
+ struct remote_host_store *rhs = c->options.rh_store;
+ if (!rhs)
+ {
+ ALLOC_OBJ_CLEAR_GC (rhs, struct remote_host_store, &c->options.gc);
+ c->options.rh_store = rhs;
+ }
+ strncpynt(rhs->host, p[2], RH_HOST_LEN);
+ ce->remote = rhs->host;
+ ce->remote_port = port;
+ flags = CE_MAN_QUERY_REMOTE_MOD;
+ ret = true;
+ }
+ }
+ if (ret)
+ {
+ ce->flags &= ~(CE_MAN_QUERY_REMOTE_MASK<<CE_MAN_QUERY_REMOTE_SHIFT);
+ ce->flags |= ((flags&CE_MAN_QUERY_REMOTE_MASK)<<CE_MAN_QUERY_REMOTE_SHIFT);
+ }
+ }
+ return ret;
+}
+
+static bool
+ce_management_query_remote (struct context *c, const char *remote_ip_hint)
+{
+ struct gc_arena gc = gc_new ();
+ volatile struct connection_entry *ce = &c->options.ce;
+ int ret = true;
+ update_time();
+ if (management)
+ {
+ struct buffer out = alloc_buf_gc (256, &gc);
+ buf_printf (&out, ">REMOTE:%s,%d,%s", np(ce->remote), ce->remote_port, proto2ascii(ce->proto, false));
+ management_notify_generic(management, BSTR (&out));
+ ce->flags &= ~(CE_MAN_QUERY_REMOTE_MASK<<CE_MAN_QUERY_REMOTE_SHIFT);
+ ce->flags |= (CE_MAN_QUERY_REMOTE_QUERY<<CE_MAN_QUERY_REMOTE_SHIFT);
+ while (((ce->flags>>CE_MAN_QUERY_REMOTE_SHIFT) & CE_MAN_QUERY_REMOTE_MASK) == CE_MAN_QUERY_REMOTE_QUERY)
+ {
+ management_event_loop_n_seconds (management, 1);
+ if (IS_SIG (c))
+ {
+ ret = false;
+ break;
+ }
+ }
+ }
+ {
+ const int flags = ((ce->flags>>CE_MAN_QUERY_REMOTE_SHIFT) & CE_MAN_QUERY_REMOTE_MASK);
+ if (flags == CE_MAN_QUERY_REMOTE_ACCEPT && remote_ip_hint)
+ ce->remote = remote_ip_hint;
+ ret = (flags != CE_MAN_QUERY_REMOTE_SKIP);
+ }
+ gc_free (&gc);
+ return ret;
+}
+
+#endif
+
/*
* Initialize and possibly randomize connection list.
*/
@@ -313,6 +397,15 @@ next_connection_entry (struct context *c)
c->options.ce = *ce;
+#if MANAGEMENT_QUERY_REMOTE
+ if (ce_defined && management && management_query_remote_enabled(management))
+ {
+ /* allow management interface to override connection entry details */
+ ce_defined = ce_management_query_remote(c, remote_ip_hint);
+ if (IS_SIG (c))
+ break;
+ } else
+#endif
if (remote_ip_hint)
c->options.ce.remote = remote_ip_hint;
@@ -343,7 +436,13 @@ init_query_passwords (struct context *c)
#if P2MP
/* Auth user/pass input */
if (c->options.auth_user_pass_file)
- auth_user_pass_setup (c->options.auth_user_pass_file);
+ {
+#ifdef ENABLE_CLIENT_CR
+ auth_user_pass_setup (c->options.auth_user_pass_file, &c->options.sc_info);
+#else
+ auth_user_pass_setup (c->options.auth_user_pass_file, NULL);
+#endif
+ }
#endif
}
@@ -598,21 +697,9 @@ init_static (void)
#ifdef TEST_GET_DEFAULT_GATEWAY
{
- struct gc_arena gc = gc_new ();
- in_addr_t addr;
- char macaddr[6];
-
- if (get_default_gateway(&addr, NULL))
- msg (M_INFO, "GW %s", print_in_addr_t(addr, 0, &gc));
- else
- msg (M_INFO, "GDG ERROR");
-
- if (get_default_gateway_mac_addr(macaddr))
- msg (M_INFO, "MAC %s", format_hex_ex (macaddr, 6, 0, 1, ":", &gc));
- else
- msg (M_INFO, "GDGMA ERROR");
-
- gc_free (&gc);
+ struct route_gateway_info rgi;
+ get_default_gateway(&rgi);
+ print_default_gateway(M_INFO, &rgi);
return false;
}
#endif
@@ -1241,7 +1328,7 @@ do_route (const struct options *options,
if (!options->route_noexec && ( route_list || route_ipv6_list ) )
{
add_routes (route_list, route_ipv6_list, tt, ROUTE_OPTION_FLAGS (options), es);
- setenv_int (es, "redirect_gateway", route_list->did_redirect_default_gateway);
+ setenv_int (es, "redirect_gateway", route_did_redirect_default_gateway(route_list));
}
#ifdef ENABLE_MANAGEMENT
if (management)
@@ -2154,6 +2241,10 @@ do_init_crypto_tls (struct context *c, const unsigned int flags)
to.x509_track = options->x509_track;
#endif
+#ifdef ENABLE_CLIENT_CR
+ to.sci = &options->sc_info;
+#endif
+
/* TLS handshake authentication (--tls-auth) */
if (options->tls_auth_file)
{
@@ -3046,6 +3137,9 @@ init_management_callback_p2p (struct context *c)
#if HTTP_PROXY_FALLBACK
cb.http_proxy_fallback_cmd = management_callback_http_proxy_fallback_cmd;
#endif
+#if MANAGEMENT_QUERY_REMOTE
+ cb.remote_cmd = management_callback_remote_cmd;
+#endif
management_set_callback (management, &cb);
}
#endif
diff --git a/manage.c b/manage.c
index ab425e7..7a888e8 100644
--- a/manage.c
+++ b/manage.c
@@ -86,6 +86,9 @@ man_help ()
msg (M_CLIENT, " where action is reply string.");
msg (M_CLIENT, "net : (Windows only) Show network info and routing table.");
msg (M_CLIENT, "password type p : Enter password p for a queried OpenVPN password.");
+#if MANAGEMENT_QUERY_REMOTE
+ msg (M_CLIENT, "remote type [host port] : Override remote directive, type=ACCEPT|MOD|SKIP.");
+#endif
msg (M_CLIENT, "pid : Show process ID of the current OpenVPN process.");
#ifdef ENABLE_PKCS11
msg (M_CLIENT, "pkcs11-id-count : Get number of available PKCS#11 identities.");
@@ -606,25 +609,19 @@ man_up_finalize (struct management *man)
{
switch (man->connection.up_query_mode)
{
- case UP_QUERY_DISABLED:
- man->connection.up_query.defined = false;
- break;
case UP_QUERY_USER_PASS:
- if (strlen (man->connection.up_query.username) && strlen (man->connection.up_query.password))
- man->connection.up_query.defined = true;
- break;
+ if (!strlen (man->connection.up_query.username))
+ break;
+ /* fall through */
case UP_QUERY_PASS:
- if (strlen (man->connection.up_query.password))
- man->connection.up_query.defined = true;
- break;
case UP_QUERY_NEED_OK:
- if (strlen (man->connection.up_query.password))
- man->connection.up_query.defined = true;
- break;
case UP_QUERY_NEED_STR:
if (strlen (man->connection.up_query.password))
man->connection.up_query.defined = true;
break;
+ case UP_QUERY_DISABLED:
+ man->connection.up_query.defined = false;
+ break;
default:
ASSERT (0);
}
@@ -665,16 +662,17 @@ man_query_user_pass (struct management *man,
static void
man_query_username (struct management *man, const char *type, const char *string)
{
- const bool needed = (man->connection.up_query_mode == UP_QUERY_USER_PASS && man->connection.up_query_type);
+ const bool needed = ((man->connection.up_query_mode == UP_QUERY_USER_PASS
+ ) && man->connection.up_query_type);
man_query_user_pass (man, type, string, needed, "username", man->connection.up_query.username, USER_PASS_LEN);
}
static void
man_query_password (struct management *man, const char *type, const char *string)
{
- const bool needed = ((man->connection.up_query_mode == UP_QUERY_USER_PASS
- || man->connection.up_query_mode == UP_QUERY_PASS)
- && man->connection.up_query_type);
+ const bool needed = ((man->connection.up_query_mode == UP_QUERY_PASS
+ || man->connection.up_query_mode == UP_QUERY_USER_PASS
+ ) && man->connection.up_query_type);
if (!string[0]) /* allow blank passwords to be passed through using the blank_up tag */
string = blank_up;
man_query_user_pass (man, type, string, needed, "password", man->connection.up_query.password, USER_PASS_LEN);
@@ -1090,6 +1088,31 @@ man_http_proxy_fallback (struct management *man, const char *server, const char
#endif
+#if MANAGEMENT_QUERY_REMOTE
+
+static void
+man_remote (struct management *man, const char **p)
+{
+ if (man->persist.callback.remote_cmd)
+ {
+ const bool status = (*man->persist.callback.remote_cmd)(man->persist.callback.arg, p);
+ if (status)
+ {
+ msg (M_CLIENT, "SUCCESS: remote command succeeded");
+ }
+ else
+ {
+ msg (M_CLIENT, "ERROR: remote command failed");
+ }
+ }
+ else
+ {
+ msg (M_CLIENT, "ERROR: The remote command is not supported by the current daemon mode");
+ }
+}
+
+#endif
+
static void
man_dispatch_command (struct management *man, struct status_output *so, const char **p, const int nparms)
{
@@ -1319,6 +1342,13 @@ man_dispatch_command (struct management *man, struct status_output *so, const ch
man_http_proxy_fallback (man, NULL, NULL, NULL);
}
#endif
+#if MANAGEMENT_QUERY_REMOTE
+ else if (streq (p[0], "remote"))
+ {
+ if (man_need (man, p, 1, MN_AT_LEAST))
+ man_remote (man, p);
+ }
+#endif
#if 1
else if (streq (p[0], "test"))
{
@@ -2339,6 +2369,12 @@ management_notify(struct management *man, const char *severity, const char *type
msg (M_CLIENT, ">NOTIFY:%s,%s,%s", severity, type, text);
}
+void
+management_notify_generic (struct management *man, const char *str)
+{
+ msg (M_CLIENT, "%s", str);
+}
+
#ifdef MANAGEMENT_DEF_AUTH
static bool
@@ -2843,7 +2879,8 @@ bool
management_query_user_pass (struct management *man,
struct user_pass *up,
const char *type,
- const unsigned int flags)
+ const unsigned int flags,
+ const char *static_challenge)
{
struct gc_arena gc = gc_new ();
bool ret = false;
@@ -2856,7 +2893,9 @@ management_query_user_pass (struct management *man,
const char *alert_type = NULL;
const char *prefix = NULL;
unsigned int up_query_mode = 0;
-
+#ifdef ENABLE_CLIENT_CR
+ const char *sc = NULL;
+#endif
ret = true;
man->persist.standalone_disabled = false; /* This is so M_CLIENT messages will be correctly passed through msg() */
man->persist.special_state_msg = NULL;
@@ -2886,6 +2925,10 @@ management_query_user_pass (struct management *man,
up_query_mode = UP_QUERY_USER_PASS;
prefix = "PASSWORD";
alert_type = "username/password";
+#ifdef ENABLE_CLIENT_CR
+ if (static_challenge)
+ sc = static_challenge;
+#endif
}
buf_printf (&alert_msg, ">%s:Need '%s' %s",
prefix,
@@ -2895,6 +2938,13 @@ management_query_user_pass (struct management *man,
if (flags & (GET_USER_PASS_NEED_OK | GET_USER_PASS_NEED_STR))
buf_printf (&alert_msg, " MSG:%s", up->username);
+#ifdef ENABLE_CLIENT_CR
+ if (sc)
+ buf_printf (&alert_msg, " SC:%d,%s",
+ BOOL_CAST(flags & GET_USER_PASS_STATIC_CHALLENGE_ECHO),
+ sc);
+#endif
+
man_wait_for_client_connection (man, &signal_received, 0, MWCC_PASSWORD_WAIT);
if (signal_received)
ret = false;
@@ -2908,7 +2958,7 @@ management_query_user_pass (struct management *man,
man->connection.up_query_mode = up_query_mode;
man->connection.up_query_type = type;
- /* run command processing event loop until we get our username/password */
+ /* run command processing event loop until we get our username/password/response */
do
{
man_standalone_event_loop (man, &signal_received, 0);
diff --git a/manage.h b/manage.h
index c6ce31e..f681f8d 100644
--- a/manage.h
+++ b/manage.h
@@ -174,6 +174,9 @@ struct management_callback
#if HTTP_PROXY_FALLBACK
bool (*http_proxy_fallback_cmd) (void *arg, const char *server, const char *port, const char *flags);
#endif
+#if MANAGEMENT_QUERY_REMOTE
+ bool (*remote_cmd) (void *arg, const char **p);
+#endif
};
/*
@@ -333,6 +336,9 @@ struct management *management_init (void);
# define MF_EXTERNAL_KEY (1<<9)
#endif
#define MF_UP_DOWN (1<<10)
+#if MANAGEMENT_QUERY_REMOTE
+#define MF_QUERY_REMOTE (1<<11)
+#endif
bool management_open (struct management *man,
const char *addr,
@@ -365,7 +371,11 @@ void management_set_callback (struct management *man,
void management_clear_callback (struct management *man);
-bool management_query_user_pass (struct management *man, struct user_pass *up, const char *type, const unsigned int flags);
+bool management_query_user_pass (struct management *man,
+ struct user_pass *up,
+ const char *type,
+ const unsigned int flags,
+ const char *static_challenge);
bool management_should_daemonize (struct management *man);
bool management_would_hold (struct management *man);
@@ -377,6 +387,8 @@ void management_up_down(struct management *man, const char *updown, const struct
void management_notify(struct management *man, const char *severity, const char *type, const char *text);
+void management_notify_generic (struct management *man, const char *str);
+
#ifdef MANAGEMENT_DEF_AUTH
void management_notify_client_needing_auth (struct management *management,
const unsigned int auth_id,
@@ -415,6 +427,14 @@ management_query_user_pass_enabled (const struct management *man)
return BOOL_CAST(man->settings.flags & MF_QUERY_PASSWORDS);
}
+#if MANAGEMENT_QUERY_REMOTE
+static inline bool
+management_query_remote_enabled (const struct management *man)
+{
+ return BOOL_CAST(man->settings.flags & MF_QUERY_REMOTE);
+}
+#endif
+
#ifdef MANAGEMENT_PF
static inline bool
management_enable_pf (const struct management *man)
diff --git a/management/management-notes.txt b/management/management-notes.txt
index 1f4cbd0..785eb88 100644
--- a/management/management-notes.txt
+++ b/management/management-notes.txt
@@ -687,6 +687,38 @@ the 10.0.0.0/8 netblock is allowed: 10.10.0.1. Also, the client
may not interact with external IP addresses using an "unknown"
protocol (i.e. one that is not IPv4 or ARP).
+COMMAND -- remote (OpenVPN AS 2.1.5/OpenVPN 2.3 or higher)
+--------------------------------------------
+
+Provide remote host/port in response to a >REMOTE notification
+(client only). Requires that the --management-query-remote
+directive is used.
+
+ remote ACTION [HOST PORT]
+
+The "remote" command should only be given in response to a >REMOTE
+notification. For example, the following >REMOTE notification
+indicates that the client config file would ordinarily connect
+to vpn.example.com port 1194 (UDP):
+
+ >REMOTE:vpn.example.com,1194,udp
+
+Now, suppose we want to override the host and port, connecting
+instead to vpn.otherexample.com port 1234. After receiving
+the above notification, use this command:
+
+ remote MOD vpn.otherexample.com 1234
+
+To accept the same host and port as the client would ordinarily
+have connected to, use this command:
+
+ remote ACCEPT
+
+To skip the current connection entry and advance to the next one,
+use this command:
+
+ remote SKIP
+
OUTPUT FORMAT
-------------
@@ -836,3 +868,113 @@ mappings, when not in single quotations:
interpret it as enclosing a parameter.
\[SPACE] Pass a literal space or tab character, don't
interpret it as a parameter delimiter.
+
+Challenge/Response Protocol
+---------------------------
+
+The OpenVPN Challenge/Response Protocol allows an OpenVPN server to
+generate challenge questions that are shown to the user, and to see
+the user's responses to those challenges. Based on the responses, the
+server can allow or deny access.
+
+In this way, the OpenVPN Challenge/Response Protocol can be used
+to implement multi-factor authentication. Two different
+variations on the challenge/response protocol are supported: the
+"Dynamic" and "Static" protocols.
+
+The basic idea of Challenge/Response is that the user must enter an
+additional piece of information, in addition to the username and
+password, to successfully authenticate. Normally, this information
+is used to prove that the user posesses a certain key-like device
+such as cryptographic token or a particular mobile phone.
+
+Dynamic protocol:
+
+The OpenVPN dynamic challenge/response protocol works by returning
+a specially formatted error message after initial successful
+authentication. This error message contains the challenge question,
+and is formatted as such:
+
+ CRV1:<flags>:<state_id>:<username_base64>:<challenge_text>
+
+flags: a series of optional, comma-separated flags:
+ E : echo the response when the user types it
+ R : a response is required
+
+state_id: an opaque string that should be returned to the server
+ along with the response.
+
+username_base64 : the username formatted as base64
+
+challenge_text : the challenge text to be shown to the user
+
+Example challenge:
+
+ CRV1:R,E:Om01u7Fh4LrGBS7uh0SWmzwabUiGiW6l:Y3Ix:Please enter token PIN
+
+After showing the challenge_text and getting a response from the user
+(if R flag is specified), the client should submit the following
+auth creds back to the OpenVPN server:
+
+Username: [username decoded from username_base64]
+Password: CRV1::<state_id>::<response_text>
+
+Where state_id is taken from the challenge request and response_text
+is what the user entered in response to the challenge_text.
+If the R flag is not present, response_text may be the empty
+string.
+
+Example response (suppose the user enters "8675309" for the token PIN):
+
+ Username: cr1 ("Y3Ix" base64 decoded)
+ Password: CRV1::Om01u7Fh4LrGBS7uh0SWmzwabUiGiW6l::8675309
+
+Static protocol:
+
+The static protocol differs from the dynamic protocol in that the
+challenge question and response field is given to the user in the
+initial username/password dialog, and the username, password, and
+response are delivered back to the server in a single transaction.
+
+The "static-challenge" directive is used to give the challenge text
+to OpenVPN and indicate whether or not the response should be echoed.
+
+When the "static-challenge" directive is used, the management
+interface will respond as such when credentials are needed:
+
+ >PASSWORD:Need 'Auth' username/password SC:<ECHO>,<TEXT>
+
+ ECHO: "1" if response should be echoed, "0" to not echo
+ TEXT: challenge text that should be shown to the user to
+ facilitate their response
+
+For example:
+
+ >PASSWORD:Need 'Auth' username/password SC:1,Please enter token PIN
+
+The above notification indicates that OpenVPN needs a --auth-user-pass
+password plus a response to a static challenge ("Please enter token PIN").
+The "1" after the "SC:" indicates that the response should be echoed.
+
+The management interface client in this case should add the static
+challenge text to the auth dialog followed by a field for the user to
+enter a response. Then the client should pack the password and response
+together into an encoded password:
+
+ username "Auth" foo
+ password "Auth" "SCRV1:<BASE64_PASSWORD>:<BASE64_RESPONSE>"
+
+For example, if the user entered "bar" as the password and 8675309
+as the PIN, the following management interface commands should be
+issued:
+
+ username "Auth" foo
+ password "Auth" "SCRV1:Zm9v:ODY3NTMwOQ=="
+
+Client-side support for challenge/response protocol:
+
+Currently, the Access Server client and standalone OpenVPN
+client support both static and dynamic challenge/response
+protocols. However, any OpenVPN client UI that drives OpenVPN
+via the management interface needs to add explicit support
+for the challenge/response protocol.
diff --git a/misc.c b/misc.c
index 2d6c6f3..136e4ce 100644
--- a/misc.c
+++ b/misc.c
@@ -1403,10 +1403,16 @@ get_user_pass_cr (struct user_pass *up,
&& ((auth_file && streq (auth_file, "management")) || (from_stdin && (flags & GET_USER_PASS_MANAGEMENT)))
&& management_query_user_pass_enabled (management))
{
+ const char *sc = NULL;
+
if (flags & GET_USER_PASS_PREVIOUS_CREDS_FAILED)
management_auth_failure (management, prefix, "previous auth credentials failed");
- if (!management_query_user_pass (management, up, prefix, flags))
+#ifdef ENABLE_CLIENT_CR
+ if (auth_challenge && (flags & GET_USER_PASS_STATIC_CHALLENGE))
+ sc = auth_challenge;
+#endif
+ if (!management_query_user_pass (management, up, prefix, flags, sc))
{
if ((flags & GET_USER_PASS_NOFATAL) != 0)
return false;
@@ -1438,7 +1444,7 @@ get_user_pass_cr (struct user_pass *up,
else if (from_stdin)
{
#ifdef ENABLE_CLIENT_CR
- if (auth_challenge)
+ if (auth_challenge && (flags & GET_USER_PASS_DYNAMIC_CHALLENGE))
{
struct auth_challenge_info *ac = get_auth_challenge (auth_challenge, &gc);
if (ac)
@@ -1447,7 +1453,7 @@ get_user_pass_cr (struct user_pass *up,
struct buffer packed_resp;
buf_set_write (&packed_resp, (uint8_t*)up->password, USER_PASS_LEN);
- msg (M_INFO, "CHALLENGE: %s", ac->challenge_text);
+ msg (M_INFO|M_NOPREFIX, "CHALLENGE: %s", ac->challenge_text);
if (!get_console_input ("Response:", BOOL_CAST(ac->flags&CR_ECHO), response, USER_PASS_LEN))
msg (M_FATAL, "ERROR: could not read challenge response from stdin");
strncpynt (up->username, ac->user, USER_PASS_LEN);
@@ -1477,6 +1483,28 @@ get_user_pass_cr (struct user_pass *up,
if (!get_console_input (BSTR (&pass_prompt), false, up->password, USER_PASS_LEN))
msg (M_FATAL, "ERROR: could not not read %s password from stdin", prefix);
+
+#ifdef ENABLE_CLIENT_CR
+ if (auth_challenge && (flags & GET_USER_PASS_STATIC_CHALLENGE))
+ {
+ char *response = (char *) gc_malloc (USER_PASS_LEN, false, &gc);
+ struct buffer packed_resp;
+ char *pw64=NULL, *resp64=NULL;
+
+ msg (M_INFO|M_NOPREFIX, "CHALLENGE: %s", auth_challenge);
+ if (!get_console_input ("Response:", BOOL_CAST(flags & GET_USER_PASS_STATIC_CHALLENGE_ECHO), response, USER_PASS_LEN))
+ msg (M_FATAL, "ERROR: could not read static challenge response from stdin");
+ if (base64_encode(up->password, strlen(up->password), &pw64) == -1
+ || base64_encode(response, strlen(response), &resp64) == -1)
+ msg (M_FATAL, "ERROR: could not base64-encode password/static_response");
+ buf_set_write (&packed_resp, (uint8_t*)up->password, USER_PASS_LEN);
+ buf_printf (&packed_resp, "SCRV1:%s:%s", pw64, resp64);
+ string_clear(pw64);
+ free(pw64);
+ string_clear(resp64);
+ free(resp64);
+ }
+#endif
}
}
else
@@ -1544,42 +1572,8 @@ get_user_pass_cr (struct user_pass *up,
#ifdef ENABLE_CLIENT_CR
/*
- * Parse a challenge message returned along with AUTH_FAILED.
- * The message is formatted as such:
- *
- * CRV1:<flags>:<state_id>:<username_base64>:<challenge_text>
- *
- * flags: a series of optional, comma-separated flags:
- * E : echo the response when the user types it
- * R : a response is required
- *
- * state_id: an opaque string that should be returned to the server
- * along with the response.
- *
- * username_base64 : the username formatted as base64
- *
- * challenge_text : the challenge text to be shown to the user
- *
- * Example challenge:
- *
- * CRV1:R,E:Om01u7Fh4LrGBS7uh0SWmzwabUiGiW6l:Y3Ix:Please enter token PIN
- *
- * After showing the challenge_text and getting a response from the user
- * (if R flag is specified), the client should submit the following
- * auth creds back to the OpenVPN server:
- *
- * Username: [username decoded from username_base64]
- * Password: CRV1::<state_id>::<response_text>
- *
- * Where state_id is taken from the challenge request and response_text
- * is what the user entered in response to the challenge_text.
- * If the R flag is not present, response_text may be the empty
- * string.
- *
- * Example response (suppose the user enters "8675309" for the token PIN):
- *
- * Username: cr1 ("Y3Ix" base64 decoded)
- * Password: CRV1::Om01u7Fh4LrGBS7uh0SWmzwabUiGiW6l::8675309
+ * See management/management-notes.txt for more info on the
+ * the dynamic challenge/response protocol implemented here.
*/
struct auth_challenge_info *
get_auth_challenge (const char *auth_challenge, struct gc_arena *gc)
diff --git a/misc.h b/misc.h
index 7681c00..b2e6f91 100644
--- a/misc.h
+++ b/misc.h
@@ -278,8 +278,19 @@ struct auth_challenge_info {
struct auth_challenge_info *get_auth_challenge (const char *auth_challenge, struct gc_arena *gc);
+/*
+ * Challenge response info on client as pushed by server.
+ */
+struct static_challenge_info {
+# define SC_ECHO (1<<0) /* echo response when typed by user */
+ unsigned int flags;
+
+ const char *challenge_text;
+};
+
#else
struct auth_challenge_info {};
+struct static_challenge_info {};
#endif
bool get_console_input (const char *prompt, const bool echo, char *input, const int capacity);
@@ -295,6 +306,10 @@ bool get_console_input (const char *prompt, const bool echo, char *input, const
#define GET_USER_PASS_NEED_STR (1<<5)
#define GET_USER_PASS_PREVIOUS_CREDS_FAILED (1<<6)
+#define GET_USER_PASS_DYNAMIC_CHALLENGE (1<<7) /* CRV1 protocol -- dynamic challenge */
+#define GET_USER_PASS_STATIC_CHALLENGE (1<<8) /* SCRV1 protocol -- static challenge */
+#define GET_USER_PASS_STATIC_CHALLENGE_ECHO (1<<9) /* SCRV1 protocol -- echo response */
+
bool get_user_pass_cr (struct user_pass *up,
const char *auth_file,
const char *prefix,
diff --git a/mtcp.c b/mtcp.c
index ade2cfb..314aa44 100644
--- a/mtcp.c
+++ b/mtcp.c
@@ -150,11 +150,6 @@ multi_tcp_instance_specific_init (struct multi_context *m, struct multi_instance
ASSERT (mi->context.c2.link_socket);
ASSERT (mi->context.c2.link_socket->info.lsa);
ASSERT (mi->context.c2.link_socket->mode == LS_MODE_TCP_ACCEPT_FROM);
- ASSERT (mi->context.c2.link_socket->info.lsa->actual.dest.addr.sa.sa_family == AF_INET
-#ifdef USE_PF_INET6
- || mi->context.c2.link_socket->info.lsa->actual.dest.addr.sa.sa_family == AF_INET6
-#endif
- );
if (!mroute_extract_openvpn_sockaddr (&mi->real, &mi->context.c2.link_socket->info.lsa->actual.dest, true))
{
msg (D_MULTI_ERRORS, "MULTI TCP: TCP client address is undefined");
diff --git a/openvpn.8 b/openvpn.8
index 7118737..dc76464 100644
--- a/openvpn.8
+++ b/openvpn.8
@@ -1114,8 +1114,8 @@ addresses in packets.
.\"*********************************************************
.TP
.B \-\-redirect-gateway flags...
-(Experimental) Automatically execute routing commands to cause all outgoing IP traffic
-to be redirected over the VPN.
+Automatically execute routing commands to cause all outgoing IP traffic
+to be redirected over the VPN. This is a client-side option.
This option performs three steps:
@@ -1154,6 +1154,11 @@ flag will cause step
.B 1
above to be omitted.
+.B autolocal \-\-
+Try to automatically determine whether to enable
+.B local
+flag above.
+
.B def1 \-\-
Use this flag to override
the default gateway by using 0.0.0.0/1 and 128.0.0.0/1
@@ -1172,12 +1177,10 @@ bypasses the tunnel
(Available on Windows clients, may not be available
on non-Windows clients).
-Using the def1 flag is highly recommended.
-.\"*********************************************************
-.TP
-.B \-\-redirect-private [flags]
-Like \-\-redirect-gateway, but omit actually changing the default
-gateway. Useful when pushing private subnets.
+.B block-local \-\-
+Block access to local LAN when the tunnel is active, except for
+the LAN gateway itself. This is accomplished by routing the local
+LAN (except for the LAN gateway address) into the tunnel.
.\"*********************************************************
.TP
.B \-\-link-mtu n
@@ -1185,6 +1188,12 @@ Sets an upper bound on the size of UDP packets which are sent
between OpenVPN peers. It's best not to set this parameter unless
you know what you're doing.
.\"*********************************************************
+.\"*********************************************************
+.TP
+.B \-\-redirect-private [flags]
+Like \-\-redirect-gateway, but omit actually changing the default
+gateway. Useful when pushing private subnets.
+.\"*********************************************************
.TP
.B \-\-tun-mtu n
Take the TUN device MTU to be
@@ -2406,6 +2415,12 @@ for inputs which ordinarily would have been queried from the
console.
.\"*********************************************************
.TP
+.B \-\-management-query-remote
+Allow management interface to override
+.B \-\-remote
+directives (client-only).
+.\"*********************************************************
+.TP
.B \-\-management-forget-disconnect
Make OpenVPN forget passwords when management session
disconnects.
@@ -3425,6 +3440,21 @@ Note that while this option cannot be pushed, it can be controlled
from the management interface.
.\"*********************************************************
.TP
+.B \-\-static\-challenge t e
+Enable static challenge/response protocol using challenge text
+.B t,
+with
+echo flag given by
+.B e
+(0|1).
+
+The echo flag indicates whether or not the user's response
+to the challenge should be echoed.
+
+See management\-notes.txt in the OpenVPN distribution for a
+description of the OpenVPN challenge/response protocol.
+.\"*********************************************************
+.TP
.B \-\-server-poll-timeout n
when polling possible remote servers to connect to
in a round-robin fashion, spend no more than
@@ -5532,6 +5562,7 @@ script being run. It can be one of the following:
.B client-connect, client-disconnect,
or
.B learn-address.
+Set prior to execution of any script.
.\"*********************************************************
.TP
.B signal
diff --git a/options.c b/options.c
index 77e7c7f..9695475 100644
--- a/options.c
+++ b/options.c
@@ -358,6 +358,9 @@ static const char usage_message[] =
" ip/port rather than listen as a TCP server.\n"
"--management-query-passwords : Query management channel for private key\n"
" and auth-user-pass passwords.\n"
+#if MANAGEMENT_QUERY_REMOTE
+ "--management-query-remote : Query management channel for --remote directive.\n"
+#endif
"--management-hold : Start " PACKAGE_NAME " in a hibernating state, until a client\n"
" of the management interface explicitly starts it.\n"
"--management-signal : Issue SIGUSR1 when management disconnect event occurs.\n"
@@ -469,6 +472,8 @@ static const char usage_message[] =
" when connecting to a '--mode server' remote host.\n"
"--auth-retry t : How to handle auth failures. Set t to\n"
" none (default), interact, or nointeract.\n"
+ "--static-challenge t e : Enable static challenge/response protocol using\n"
+ " challenge text t, with e indicating echo flag (0|1)\n"
"--server-poll-timeout n : when polling possible remote servers to connect to\n"
" in a round-robin fashion, spend no more than n seconds\n"
" waiting for a response before trying the next server.\n"
@@ -701,6 +706,9 @@ static const char usage_message[] =
"--show-pkcs11-ids provider [cert_private] : Show PKCS#11 available ids.\n"
" --verb option can be added *BEFORE* this.\n"
#endif /* ENABLE_PKCS11 */
+ "\n"
+ "General Standalone Options:\n"
+ "--show-gateway : Show info about default gateway.\n"
;
#endif /* !ENABLE_SMALL */
@@ -3845,6 +3853,14 @@ add_option (struct options *options,
read_config_file (options, p[1], level, file, line, msglevel, permission_mask, option_types_found, es);
}
+ else if (streq (p[0], "show-gateway"))
+ {
+ struct route_gateway_info rgi;
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ get_default_gateway(&rgi);
+ print_default_gateway(M_INFO, &rgi);
+ openvpn_exit (OPENVPN_EXIT_STATUS_GOOD); /* exit point */
+ }
#if 0
else if (streq (p[0], "foreign-option") && p[1])
{
@@ -3928,6 +3944,13 @@ add_option (struct options *options,
VERIFY_PERMISSION (OPT_P_GENERAL);
options->management_flags |= MF_QUERY_PASSWORDS;
}
+#if MANAGEMENT_QUERY_REMOTE
+ else if (streq (p[0], "management-query-remote"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->management_flags |= MF_QUERY_REMOTE;
+ }
+#endif
else if (streq (p[0], "management-hold"))
{
VERIFY_PERMISSION (OPT_P_GENERAL);
@@ -5031,6 +5054,8 @@ add_option (struct options *options,
options->routes->flags |= RG_BYPASS_DHCP;
else if (streq (p[j], "bypass-dns"))
options->routes->flags |= RG_BYPASS_DNS;
+ else if (streq (p[j], "block-local"))
+ options->routes->flags |= RG_BLOCK_LOCAL;
else
{
msg (msglevel, "unknown --%s flag: %s", p[0], p[j]);
@@ -5611,6 +5636,15 @@ add_option (struct options *options,
VERIFY_PERMISSION (OPT_P_GENERAL);
auth_retry_set (msglevel, p[1]);
}
+#ifdef ENABLE_CLIENT_CR
+ else if (streq (p[0], "static-challenge") && p[1] && p[2])
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->sc_info.challenge_text = p[1];
+ if (atoi(p[2]))
+ options->sc_info.flags |= SC_ECHO;
+ }
+#endif
#endif
#ifdef WIN32
else if (streq (p[0], "win-sys") && p[1])
diff --git a/options.h b/options.h
index 8a51502..e723b66 100644
--- a/options.h
+++ b/options.h
@@ -110,7 +110,15 @@ struct connection_entry
# define CE_HTTP_PROXY_FALLBACK (1<<1)
time_t ce_http_proxy_fallback_timestamp; /* time when fallback http_proxy_options was last updated */
#endif
-
+#if MANAGEMENT_QUERY_REMOTE
+# define CE_MAN_QUERY_REMOTE_UNDEF 0
+# define CE_MAN_QUERY_REMOTE_QUERY 1
+# define CE_MAN_QUERY_REMOTE_ACCEPT 2
+# define CE_MAN_QUERY_REMOTE_MOD 3
+# define CE_MAN_QUERY_REMOTE_SKIP 4
+# define CE_MAN_QUERY_REMOTE_MASK (0x07)
+# define CE_MAN_QUERY_REMOTE_SHIFT (2)
+#endif
unsigned int flags;
};
@@ -150,6 +158,14 @@ struct hpo_store
};
#endif
+#if MANAGEMENT_QUERY_REMOTE
+struct remote_host_store
+{
+# define RH_HOST_LEN 80
+ char host[RH_HOST_LEN];
+};
+#endif
+
/* Command line options */
struct options
{
@@ -202,6 +218,10 @@ struct options
struct hpo_store *hpo_store; /* used to store dynamic proxy info given by management interface */
#endif
+#if MANAGEMENT_QUERY_REMOTE
+ struct remote_host_store *rh_store;
+#endif
+
bool remote_random;
const char *ipchange;
const char *dev;
@@ -457,6 +477,9 @@ struct options
int scheduled_exit_interval;
+#ifdef ENABLE_CLIENT_CR
+ struct static_challenge_info sc_info;
+#endif
#endif
#ifdef USE_CRYPTO
diff --git a/route.c b/route.c
index 96596cd..7843686 100644
--- a/route.c
+++ b/route.c
@@ -39,7 +39,8 @@
#include "memdbg.h"
-static void delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es);
+static void delete_route (struct route *r, const struct tuntap *tt, unsigned int flags, const struct route_gateway_info *rgi, const struct env_set *es);
+
static void get_bypass_addresses (struct route_bypass *rb, const unsigned int flags);
#ifdef ENABLE_DEBUG
@@ -60,6 +61,26 @@ print_bypass_addresses (const struct route_bypass *rb)
#endif
+static bool
+add_bypass_address (struct route_bypass *rb, const in_addr_t a)
+{
+ int i;
+ for (i = 0; i < rb->n_bypass; ++i)
+ {
+ if (a == rb->bypass[i]) /* avoid duplicates */
+ return true;
+ }
+ if (rb->n_bypass < N_ROUTE_BYPASS)
+ {
+ rb->bypass[rb->n_bypass++] = a;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
struct route_option_list *
new_route_option_list (const int max_routes, struct gc_arena *a)
{
@@ -123,7 +144,7 @@ route_string (const struct route *r, struct gc_arena *gc)
print_in_addr_t (r->netmask, 0, gc),
print_in_addr_t (r->gateway, 0, gc)
);
- if (r->metric_defined)
+ if (r->flags & RT_METRIC_DEFINED)
buf_printf (&out, " metric %d", r->metric);
return BSTR (&out);
}
@@ -152,7 +173,7 @@ setenv_route_addr (struct env_set *es, const char *key, const in_addr_t addr, in
}
static bool
-get_special_addr (const struct route_special_addr *spec,
+get_special_addr (const struct route_list *rl,
const char *string,
in_addr_t *out,
bool *status)
@@ -161,10 +182,10 @@ get_special_addr (const struct route_special_addr *spec,
*status = true;
if (!strcmp (string, "vpn_gateway"))
{
- if (spec)
+ if (rl)
{
- if (spec->remote_endpoint_defined)
- *out = spec->remote_endpoint;
+ if (rl->spec.flags & RTSA_REMOTE_ENDPOINT)
+ *out = rl->spec.remote_endpoint;
else
{
msg (M_INFO, PACKAGE_NAME " ROUTE: vpn_gateway undefined");
@@ -176,10 +197,10 @@ get_special_addr (const struct route_special_addr *spec,
}
else if (!strcmp (string, "net_gateway"))
{
- if (spec)
+ if (rl)
{
- if (spec->net_gateway_defined)
- *out = spec->net_gateway;
+ if (rl->rgi.flags & RGI_ADDR_DEFINED)
+ *out = rl->rgi.gateway.addr;
else
{
msg (M_INFO, PACKAGE_NAME " ROUTE: net_gateway undefined -- unable to get default gateway from system");
@@ -191,10 +212,10 @@ get_special_addr (const struct route_special_addr *spec,
}
else if (!strcmp (string, "remote_host"))
{
- if (spec)
+ if (rl)
{
- if (spec->remote_host_defined)
- *out = spec->remote_host;
+ if (rl->spec.flags & RTSA_REMOTE_HOST)
+ *out = rl->spec.remote_host;
else
{
msg (M_INFO, PACKAGE_NAME " ROUTE: remote_host undefined");
@@ -220,13 +241,13 @@ static bool
init_route (struct route *r,
struct resolve_list *network_list,
const struct route_option *ro,
- const struct route_special_addr *spec)
+ const struct route_list *rl)
{
const in_addr_t default_netmask = ~0;
bool status;
+ CLEAR (*r);
r->option = ro;
- r->defined = false;
/* network */
@@ -235,7 +256,7 @@ init_route (struct route *r,
goto fail;
}
- if (!get_special_addr (spec, ro->network, &r->network, &status))
+ if (!get_special_addr (rl, ro->network, &r->network, &status))
{
r->network = getaddr_multi (
GETADDR_RESOLVE
@@ -272,7 +293,7 @@ init_route (struct route *r,
if (is_route_parm_defined (ro->gateway))
{
- if (!get_special_addr (spec, ro->gateway, &r->gateway, &status))
+ if (!get_special_addr (rl, ro->gateway, &r->gateway, &status))
{
r->gateway = getaddr (
GETADDR_RESOLVE
@@ -288,8 +309,8 @@ init_route (struct route *r,
}
else
{
- if (spec->remote_endpoint_defined)
- r->gateway = spec->remote_endpoint;
+ if (rl->spec.flags & RTSA_REMOTE_ENDPOINT)
+ r->gateway = rl->spec.remote_endpoint;
else
{
msg (M_WARN, PACKAGE_NAME " ROUTE: " PACKAGE_NAME " needs a gateway parameter for a --route option and no default was specified by either --route-gateway or --ifconfig options");
@@ -299,7 +320,6 @@ init_route (struct route *r,
/* metric */
- r->metric_defined = false;
r->metric = 0;
if (is_route_parm_defined (ro->metric))
{
@@ -311,22 +331,21 @@ init_route (struct route *r,
ro->metric);
goto fail;
}
- r->metric_defined = true;
+ r->flags |= RT_METRIC_DEFINED;
}
- else if (spec->default_metric_defined)
+ else if (rl->spec.flags & RTSA_DEFAULT_METRIC)
{
- r->metric = spec->default_metric;
- r->metric_defined = true;
+ r->metric = rl->spec.default_metric;
+ r->flags |= RT_METRIC_DEFINED;
}
- r->defined = true;
+ r->flags |= RT_DEFINED;
return true;
fail:
msg (M_WARN, PACKAGE_NAME " ROUTE: failed to parse/resolve route for host/network: %s",
ro->network);
- r->defined = false;
return false;
}
@@ -447,15 +466,70 @@ clear_route_ipv6_list (struct route_ipv6_list *rl6)
}
void
-route_list_add_default_gateway (struct route_list *rl,
- struct env_set *es,
- const in_addr_t addr)
+route_list_add_vpn_gateway (struct route_list *rl,
+ struct env_set *es,
+ const in_addr_t addr)
{
rl->spec.remote_endpoint = addr;
- rl->spec.remote_endpoint_defined = true;
+ rl->spec.flags |= RTSA_REMOTE_ENDPOINT;
setenv_route_addr (es, "vpn_gateway", rl->spec.remote_endpoint, -1);
}
+static void
+add_block_local_item (struct route_list *rl,
+ const struct route_gateway_address *gateway,
+ in_addr_t target)
+{
+ const int rgi_needed = (RGI_ADDR_DEFINED|RGI_NETMASK_DEFINED);
+ if ((rl->rgi.flags & rgi_needed) == rgi_needed
+ && rl->rgi.gateway.netmask < 0xFFFFFFFF
+ && (rl->n)+2 <= rl->capacity)
+ {
+ struct route r;
+ unsigned int l2;
+
+ /* split a route into two smaller blocking routes, and direct them to target */
+ CLEAR(r);
+ r.flags = RT_DEFINED;
+ r.gateway = target;
+ l2 = ((~gateway->netmask)+1)>>1;
+ r.netmask = ~(l2-1);
+ r.network = gateway->addr & r.netmask;
+ rl->routes[rl->n++] = r;
+ r.network += l2;
+ rl->routes[rl->n++] = r;
+ }
+}
+
+static void
+add_block_local (struct route_list *rl)
+{
+ const int rgi_needed = (RGI_ADDR_DEFINED|RGI_NETMASK_DEFINED);
+ if ((rl->flags & RG_BLOCK_LOCAL)
+ && (rl->rgi.flags & rgi_needed) == rgi_needed
+ && (rl->spec.flags & RTSA_REMOTE_ENDPOINT)
+ && rl->spec.remote_host_local != TLA_LOCAL)
+ {
+ size_t i;
+
+ /* add bypass for gateway addr */
+ add_bypass_address (&rl->spec.bypass, rl->rgi.gateway.addr);
+
+ /* block access to local subnet */
+ add_block_local_item (rl, &rl->rgi.gateway, rl->spec.remote_endpoint);
+
+ /* process additional subnets on gateway interface */
+ for (i = 0; i < rl->rgi.n_addrs; ++i)
+ {
+ const struct route_gateway_address *gwa = &rl->rgi.addrs[i];
+ /* omit the add/subnet in &rl->rgi which we processed above */
+ if (!((rl->rgi.gateway.addr & rl->rgi.gateway.netmask) == (gwa->addr & gwa->netmask)
+ && rl->rgi.gateway.netmask == gwa->netmask))
+ add_block_local_item (rl, gwa, rl->spec.remote_endpoint);
+ }
+ }
+}
+
bool
init_route_list (struct route_list *rl,
const struct route_option_list *opt,
@@ -474,48 +548,47 @@ init_route_list (struct route_list *rl,
if (remote_host)
{
rl->spec.remote_host = remote_host;
- rl->spec.remote_host_defined = true;
+ rl->spec.flags |= RTSA_REMOTE_HOST;
}
if (default_metric)
{
rl->spec.default_metric = default_metric;
- rl->spec.default_metric_defined = true;
+ rl->spec.flags |= RTSA_DEFAULT_METRIC;
}
- rl->spec.net_gateway_defined = get_default_gateway (&rl->spec.net_gateway, NULL);
- if (rl->spec.net_gateway_defined)
+ get_default_gateway (&rl->rgi);
+ if (rl->rgi.flags & RGI_ADDR_DEFINED)
{
- setenv_route_addr (es, "net_gateway", rl->spec.net_gateway, -1);
- dmsg (D_ROUTE, "ROUTE default_gateway=%s", print_in_addr_t (rl->spec.net_gateway, 0, &gc));
+ setenv_route_addr (es, "net_gateway", rl->rgi.gateway.addr, -1);
+#ifdef ENABLE_DEBUG
+ print_default_gateway (D_ROUTE, &rl->rgi);
+#endif
}
else
{
dmsg (D_ROUTE, "ROUTE: default_gateway=UNDEF");
}
- if (rl->flags & RG_ENABLE)
- {
- get_bypass_addresses (&rl->spec.bypass, rl->flags);
-#ifdef ENABLE_DEBUG
- print_bypass_addresses (&rl->spec.bypass);
-#endif
- }
+ if (rl->spec.flags & RTSA_REMOTE_HOST)
+ rl->spec.remote_host_local = test_local_addr (remote_host, &rl->rgi);
if (is_route_parm_defined (remote_endpoint))
{
+ bool defined = false;
rl->spec.remote_endpoint = getaddr (
GETADDR_RESOLVE
| GETADDR_HOST_ORDER
| GETADDR_WARN_ON_SIGNAL,
remote_endpoint,
0,
- &rl->spec.remote_endpoint_defined,
+ &defined,
NULL);
- if (rl->spec.remote_endpoint_defined)
+ if (defined)
{
setenv_route_addr (es, "vpn_gateway", rl->spec.remote_endpoint, -1);
+ rl->spec.flags |= RTSA_REMOTE_ENDPOINT;
}
else
{
@@ -524,12 +597,20 @@ init_route_list (struct route_list *rl,
ret = false;
}
}
- else
- rl->spec.remote_endpoint_defined = false;
+
+ if (rl->flags & RG_ENABLE)
+ {
+ add_block_local (rl);
+ get_bypass_addresses (&rl->spec.bypass, rl->flags);
+#ifdef ENABLE_DEBUG
+ print_bypass_addresses (&rl->spec.bypass);
+#endif
+ }
/* parse the routes from opt to rl */
{
- int i, j = 0;
+ int i = 0;
+ int j = rl->n;
bool warned = false;
for (i = 0; i < opt->n; ++i)
{
@@ -542,7 +623,7 @@ init_route_list (struct route_list *rl,
if (!init_route (&r,
&netlist,
&opt->routes[i],
- &rl->spec))
+ rl))
ret = false;
else
{
@@ -648,15 +729,16 @@ add_route3 (in_addr_t network,
in_addr_t gateway,
const struct tuntap *tt,
unsigned int flags,
+ const struct route_gateway_info *rgi,
const struct env_set *es)
{
struct route r;
CLEAR (r);
- r.defined = true;
+ r.flags = RT_DEFINED;
r.network = network;
r.netmask = netmask;
r.gateway = gateway;
- add_route (&r, tt, flags, es);
+ add_route (&r, tt, flags, rgi, es);
}
static void
@@ -665,15 +747,16 @@ del_route3 (in_addr_t network,
in_addr_t gateway,
const struct tuntap *tt,
unsigned int flags,
+ const struct route_gateway_info *rgi,
const struct env_set *es)
{
struct route r;
CLEAR (r);
- r.defined = true;
+ r.flags = RT_DEFINED|RT_ADDED;
r.network = network;
r.netmask = netmask;
r.gateway = gateway;
- delete_route (&r, tt, flags, es);
+ delete_route (&r, tt, flags, rgi, es);
}
static void
@@ -681,17 +764,19 @@ add_bypass_routes (struct route_bypass *rb,
in_addr_t gateway,
const struct tuntap *tt,
unsigned int flags,
+ const struct route_gateway_info *rgi,
const struct env_set *es)
{
int i;
for (i = 0; i < rb->n_bypass; ++i)
{
- if (rb->bypass[i] != gateway)
+ if (rb->bypass[i])
add_route3 (rb->bypass[i],
~0,
gateway,
tt,
flags,
+ rgi,
es);
}
}
@@ -701,17 +786,19 @@ del_bypass_routes (struct route_bypass *rb,
in_addr_t gateway,
const struct tuntap *tt,
unsigned int flags,
+ const struct route_gateway_info *rgi,
const struct env_set *es)
{
int i;
for (i = 0; i < rb->n_bypass; ++i)
{
- if (rb->bypass[i] != gateway)
+ if (rb->bypass[i])
del_route3 (rb->bypass[i],
~0,
gateway,
tt,
flags,
+ rgi,
es);
}
}
@@ -723,15 +810,15 @@ redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, u
if (rl->flags & RG_ENABLE)
{
- if (!rl->spec.remote_endpoint_defined)
+ if (!(rl->spec.flags & RTSA_REMOTE_ENDPOINT))
{
msg (M_WARN, "%s VPN gateway parameter (--route-gateway or --ifconfig) is missing", err);
}
- else if (!rl->spec.net_gateway_defined)
+ else if (!(rl->rgi.flags & RGI_ADDR_DEFINED))
{
msg (M_WARN, "%s Cannot read current default gateway from system", err);
}
- else if (!rl->spec.remote_host_defined)
+ else if (!(rl->spec.flags & RTSA_REMOTE_HOST))
{
msg (M_WARN, "%s Cannot obtain current remote host address", err);
}
@@ -739,7 +826,7 @@ redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, u
{
bool local = BOOL_CAST(rl->flags & RG_LOCAL);
if (rl->flags & RG_AUTO_LOCAL) {
- const int tla = test_local_addr (rl->spec.remote_host);
+ const int tla = rl->spec.remote_host_local;
if (tla == TLA_NONLOCAL)
{
dmsg (D_ROUTE, "ROUTE remote_host is NOT LOCAL");
@@ -761,11 +848,12 @@ redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, u
#endif
add_route3 (rl->spec.remote_host,
~0,
- rl->spec.net_gateway,
+ rl->rgi.gateway.addr,
tt,
flags,
+ &rl->rgi,
es);
- rl->did_local = true;
+ rl->iflags |= RL_DID_LOCAL;
#ifdef USE_PF_INET6
} else {
dmsg (D_ROUTE, "ROUTE remote_host protocol differs from tunneled");
@@ -774,7 +862,7 @@ redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, u
}
/* route DHCP/DNS server traffic through original default gateway */
- add_bypass_routes (&rl->spec.bypass, rl->spec.net_gateway, tt, flags, es);
+ add_bypass_routes (&rl->spec.bypass, rl->rgi.gateway.addr, tt, flags, &rl->rgi, es);
if (rl->flags & RG_REROUTE_GW)
{
@@ -786,6 +874,7 @@ redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, u
rl->spec.remote_endpoint,
tt,
flags,
+ &rl->rgi,
es);
/* add new default route (2nd component) */
@@ -794,6 +883,7 @@ redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, u
rl->spec.remote_endpoint,
tt,
flags,
+ &rl->rgi,
es);
}
else
@@ -801,9 +891,10 @@ redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, u
/* delete default route */
del_route3 (0,
0,
- rl->spec.net_gateway,
+ rl->rgi.gateway.addr,
tt,
flags,
+ &rl->rgi,
es);
/* add new default route */
@@ -812,12 +903,13 @@ redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, u
rl->spec.remote_endpoint,
tt,
flags,
+ &rl->rgi,
es);
}
}
/* set a flag so we can undo later */
- rl->did_redirect_default_gateway = true;
+ rl->iflags |= RL_DID_REDIRECT_DEFAULT_GATEWAY;
}
}
}
@@ -825,22 +917,23 @@ redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, u
static void
undo_redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
{
- if (rl->did_redirect_default_gateway)
+ if (rl->iflags & RL_DID_REDIRECT_DEFAULT_GATEWAY)
{
/* delete remote host route */
- if (rl->did_local)
+ if (rl->iflags & RL_DID_LOCAL)
{
del_route3 (rl->spec.remote_host,
~0,
- rl->spec.net_gateway,
+ rl->rgi.gateway.addr,
tt,
flags,
+ &rl->rgi,
es);
- rl->did_local = false;
+ rl->iflags &= ~RL_DID_LOCAL;
}
/* delete special DHCP/DNS bypass route */
- del_bypass_routes (&rl->spec.bypass, rl->spec.net_gateway, tt, flags, es);
+ del_bypass_routes (&rl->spec.bypass, rl->rgi.gateway.addr, tt, flags, &rl->rgi, es);
if (rl->flags & RG_REROUTE_GW)
{
@@ -852,6 +945,7 @@ undo_redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *
rl->spec.remote_endpoint,
tt,
flags,
+ &rl->rgi,
es);
/* delete default route (2nd component) */
@@ -860,6 +954,7 @@ undo_redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *
rl->spec.remote_endpoint,
tt,
flags,
+ &rl->rgi,
es);
}
else
@@ -870,30 +965,29 @@ undo_redirect_default_route_to_vpn (struct route_list *rl, const struct tuntap *
rl->spec.remote_endpoint,
tt,
flags,
+ &rl->rgi,
es);
/* restore original default route */
add_route3 (0,
0,
- rl->spec.net_gateway,
+ rl->rgi.gateway.addr,
tt,
flags,
+ &rl->rgi,
es);
}
}
- rl->did_redirect_default_gateway = false;
+ rl->iflags &= ~RL_DID_REDIRECT_DEFAULT_GATEWAY;
}
}
void
-add_routes (struct route_list *rl, struct route_ipv6_list *rl6,
- const struct tuntap *tt, unsigned int flags, const struct env_set *es)
+add_routes (struct route_list *rl, struct route_ipv6_list *rl6, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
{
- if (rl)
- redirect_default_route_to_vpn (rl, tt, flags, es);
-
- if (rl && !rl->routes_added)
+ redirect_default_route_to_vpn (rl, tt, flags, es);
+ if (!(rl->iflags & RL_ROUTES_ADDED))
{
int i;
@@ -913,12 +1007,11 @@ add_routes (struct route_list *rl, struct route_ipv6_list *rl6,
struct route *r = &rl->routes[i];
check_subnet_conflict (r->network, r->netmask, "route");
if (flags & ROUTE_DELETE_FIRST)
- delete_route (r, tt, flags, es);
- add_route (r, tt, flags, es);
+ delete_route (r, tt, flags, &rl->rgi, es);
+ add_route (r, tt, flags, &rl->rgi, es);
}
- rl->routes_added = true;
+ rl->iflags |= RL_ROUTES_ADDED;
}
-
if (rl6 && !rl6->routes_added)
{
int i;
@@ -938,22 +1031,19 @@ void
delete_routes (struct route_list *rl, struct route_ipv6_list *rl6,
const struct tuntap *tt, unsigned int flags, const struct env_set *es)
{
- if (rl && rl->routes_added)
+ if (rl->iflags & RL_ROUTES_ADDED)
{
int i;
for (i = rl->n - 1; i >= 0; --i)
{
const struct route *r = &rl->routes[i];
- delete_route (r, tt, flags, es);
+ delete_route (r, tt, flags, &rl->rgi, es);
}
- rl->routes_added = false;
+ rl->iflags &= ~RL_ROUTES_ADDED;
}
- if ( rl )
- {
- undo_redirect_default_route_to_vpn (rl, tt, flags, es);
- clear_route_list (rl);
- }
+ undo_redirect_default_route_to_vpn (rl, tt, flags, es);
+ clear_route_list (rl);
if ( rl6 && rl6->routes_added )
{
@@ -1005,13 +1095,38 @@ print_route_options (const struct route_option_list *rol,
print_route_option (&rol->routes[i], level);
}
+void
+print_default_gateway(const int msglevel, const struct route_gateway_info *rgi)
+{
+ struct gc_arena gc = gc_new ();
+ if (rgi->flags & RGI_ADDR_DEFINED)
+ {
+ struct buffer out = alloc_buf_gc (256, &gc);
+ buf_printf (&out, "ROUTE_GATEWAY");
+ buf_printf (&out, " %s", print_in_addr_t (rgi->gateway.addr, 0, &gc));
+ if (rgi->flags & RGI_NETMASK_DEFINED)
+ buf_printf (&out, "/%s", print_in_addr_t (rgi->gateway.netmask, 0, &gc));
+#ifdef WIN32
+ if (rgi->flags & RGI_IFACE_DEFINED)
+ buf_printf (&out, " I=%u", (unsigned int)rgi->adapter_index);
+#else
+ if (rgi->flags & RGI_IFACE_DEFINED)
+ buf_printf (&out, " IFACE=%s", rgi->iface);
+#endif
+ if (rgi->flags & RGI_HWADDR_DEFINED)
+ buf_printf (&out, " HWADDR=%s", format_hex_ex (rgi->hwaddr, 6, 0, 1, ":", &gc));
+ msg (msglevel, "%s", BSTR (&out));
+ }
+ gc_free (&gc);
+}
+
#endif
static void
print_route (const struct route *r, int level)
{
struct gc_arena gc = gc_new ();
- if (r->defined)
+ if (r->flags & RT_DEFINED)
msg (level, "%s", route_string (r, &gc));
gc_free (&gc);
}
@@ -1028,13 +1143,13 @@ static void
setenv_route (struct env_set *es, const struct route *r, int i)
{
struct gc_arena gc = gc_new ();
- if (r->defined)
+ if (r->flags & RT_DEFINED)
{
setenv_route_addr (es, "network", r->network, i);
setenv_route_addr (es, "netmask", r->netmask, i);
setenv_route_addr (es, "gateway", r->gateway, i);
- if (r->metric_defined)
+ if (r->flags & RT_METRIC_DEFINED)
{
struct buffer name = alloc_buf_gc (256, &gc);
buf_printf (&name, "route_metric_%d", i);
@@ -1080,8 +1195,65 @@ setenv_routes_ipv6 (struct env_set *es, const struct route_ipv6_list *rl6)
setenv_route_ipv6 (es, &rl6->routes_ipv6[i], i + 1);
}
+/*
+ * local_route() determines whether the gateway of a provided host
+ * route is on the same interface that owns the default gateway.
+ * It uses the data structure
+ * returned by get_default_gateway() (struct route_gateway_info)
+ * to determine this. If the route is local, LR_MATCH is returned.
+ * When adding routes into the kernel, if LR_MATCH is defined for
+ * a given route, the route should explicitly reference the default
+ * gateway interface as the route destination. For example, here
+ * is an example on Linux that uses LR_MATCH:
+ *
+ * route add -net 10.10.0.1 netmask 255.255.255.255 dev eth0
+ *
+ * This capability is needed by the "default-gateway block-local"
+ * directive, to allow client access to the local subnet to be
+ * blocked but still allow access to the local default gateway.
+ */
+
+/* local_route() return values */
+#define LR_NOMATCH 0 /* route is not local */
+#define LR_MATCH 1 /* route is local */
+#define LR_ERROR 2 /* caller should abort adding route */
+
+static int
+local_route (in_addr_t network,
+ in_addr_t netmask,
+ in_addr_t gateway,
+ const struct route_gateway_info *rgi)
+{
+ /* set LR_MATCH on local host routes */
+ const int rgi_needed = (RGI_ADDR_DEFINED|RGI_NETMASK_DEFINED|RGI_IFACE_DEFINED);
+ if (rgi
+ && (rgi->flags & rgi_needed) == rgi_needed
+ && gateway == rgi->gateway.addr
+ && netmask == 0xFFFFFFFF)
+ {
+ if (((network ^ rgi->gateway.addr) & rgi->gateway.netmask) == 0)
+ return LR_MATCH;
+ else
+ {
+ /* examine additional subnets on gateway interface */
+ size_t i;
+ for (i = 0; i < rgi->n_addrs; ++i)
+ {
+ const struct route_gateway_address *gwa = &rgi->addrs[i];
+ if (((network ^ gwa->addr) & gwa->netmask) == 0)
+ return LR_MATCH;
+ }
+ }
+ }
+ return LR_NOMATCH;
+}
+
void
-add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
+add_route (struct route *r,
+ const struct tuntap *tt,
+ unsigned int flags,
+ const struct route_gateway_info *rgi, /* may be NULL */
+ const struct env_set *es)
{
struct gc_arena gc;
struct argv argv;
@@ -1089,8 +1261,9 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
const char *netmask;
const char *gateway;
bool status = false;
+ int is_local_route;
- if (!r->defined)
+ if (!(r->flags & RT_DEFINED))
return;
gc_init (&gc);
@@ -1100,78 +1273,84 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
netmask = print_in_addr_t (r->netmask, 0, &gc);
gateway = print_in_addr_t (r->gateway, 0, &gc);
- /*
- * Filter out routes which are essentially no-ops
- */
- if (r->network == r->gateway && r->netmask == 0xFFFFFFFF)
- {
- msg (M_INFO, PACKAGE_NAME " ROUTE: omitted no-op route: %s/%s -> %s",
- network, netmask, gateway);
- goto done;
- }
+ is_local_route = local_route(r->network, r->netmask, r->gateway, rgi);
+ if (is_local_route == LR_ERROR)
+ goto done;
#if defined(TARGET_LINUX)
#ifdef CONFIG_FEATURE_IPROUTE
+ /* FIXME -- add LR_MATCH support for CONFIG_FEATURE_IPROUTE */
argv_printf (&argv, "%s route add %s/%d via %s",
iproute_path,
network,
count_netmask_bits(netmask),
gateway);
- if (r->metric_defined)
+ if (r->flags & RT_METRIC_DEFINED)
argv_printf_cat (&argv, "metric %d", r->metric);
#else
- argv_printf (&argv, "%s add -net %s netmask %s gw %s",
- ROUTE_PATH,
- network,
- netmask,
- gateway);
- if (r->metric_defined)
+ argv_printf (&argv, "%s add -net %s netmask %s",
+ ROUTE_PATH,
+ network,
+ netmask);
+ if (r->flags & RT_METRIC_DEFINED)
argv_printf_cat (&argv, "metric %d", r->metric);
+ if (rgi && is_local_route == LR_MATCH)
+ argv_printf_cat (&argv, "dev %s", rgi->iface);
+ else
+ argv_printf_cat (&argv, "gw %s", gateway);
+
#endif /*CONFIG_FEATURE_IPROUTE*/
argv_msg (D_ROUTE, &argv);
status = openvpn_execve_check (&argv, es, 0, "ERROR: Linux route add command failed");
#elif defined (WIN32)
+ {
+ DWORD ai = ~0;
+ argv_printf (&argv, "%s%sc ADD %s MASK %s %s",
+ get_win_sys_path(),
+ WIN_ROUTE_PATH_SUFFIX,
+ network,
+ netmask,
+ gateway);
+ if (r->flags & RT_METRIC_DEFINED)
+ argv_printf_cat (&argv, "METRIC %d", r->metric);
+ if (rgi && is_local_route == LR_MATCH)
+ {
+ ai = rgi->adapter_index;
+ argv_printf_cat (&argv, "IF %u", (unsigned int)ai);
+ }
- argv_printf (&argv, "%s%sc ADD %s MASK %s %s",
- get_win_sys_path(),
- WIN_ROUTE_PATH_SUFFIX,
- network,
- netmask,
- gateway);
- if (r->metric_defined)
- argv_printf_cat (&argv, "METRIC %d", r->metric);
-
- argv_msg (D_ROUTE, &argv);
+ argv_msg (D_ROUTE, &argv);
- if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_IPAPI)
- {
- status = add_route_ipapi (r, tt);
- msg (D_ROUTE, "Route addition via IPAPI %s", status ? "succeeded" : "failed");
- }
- else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_EXE)
- {
- netcmd_semaphore_lock ();
- status = openvpn_execve_check (&argv, es, 0, "ERROR: Windows route add command failed");
- netcmd_semaphore_release ();
- }
- else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_ADAPTIVE)
- {
- status = add_route_ipapi (r, tt);
- msg (D_ROUTE, "Route addition via IPAPI %s [adaptive]", status ? "succeeded" : "failed");
- if (!status)
- {
- msg (D_ROUTE, "Route addition fallback to route.exe");
- netcmd_semaphore_lock ();
- status = openvpn_execve_check (&argv, es, 0, "ERROR: Windows route add command failed [adaptive]");
- netcmd_semaphore_release ();
- }
- }
- else
- {
- ASSERT (0);
- }
+ if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_IPAPI)
+ {
+ status = add_route_ipapi (r, tt, ai);
+ msg (D_ROUTE, "Route addition via IPAPI %s", status ? "succeeded" : "failed");
+ }
+ else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_EXE)
+ {
+ netcmd_semaphore_lock ();
+ status = openvpn_execve_check (&argv, es, 0, "ERROR: Windows route add command failed");
+ netcmd_semaphore_release ();
+ }
+ else if ((flags & ROUTE_METHOD_MASK) == ROUTE_METHOD_ADAPTIVE)
+ {
+ status = add_route_ipapi (r, tt, ai);
+ msg (D_ROUTE, "Route addition via IPAPI %s [adaptive]", status ? "succeeded" : "failed");
+ if (!status)
+ {
+ msg (D_ROUTE, "Route addition fallback to route.exe");
+ netcmd_semaphore_lock ();
+ status = openvpn_execve_check (&argv, es, 0, "ERROR: Windows route add command failed [adaptive]");
+ netcmd_semaphore_release ();
+ }
+ }
+ else
+ {
+ ASSERT (0);
+ }
+ }
#elif defined (TARGET_SOLARIS)
@@ -1180,13 +1359,17 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
argv_printf (&argv, "%s add",
ROUTE_PATH);
+#if 0
+ if (r->flags & RT_METRIC_DEFINED)
+ argv_printf_cat (&argv, "-rtt %d", r->metric);
+#endif
+
argv_printf_cat (&argv, "%s -netmask %s %s",
network,
netmask,
gateway);
- if (r->metric_defined)
- argv_printf_cat (&argv, "%d", r->metric);
+ /* FIXME -- add LR_MATCH support for Solaris */
argv_msg (D_ROUTE, &argv);
status = openvpn_execve_check (&argv, es, 0, "ERROR: Solaris route add command failed");
@@ -1197,7 +1380,7 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
ROUTE_PATH);
#if 0
- if (r->metric_defined)
+ if (r->flags & RT_METRIC_DEFINED)
argv_printf_cat (&argv, "-rtt %d", r->metric);
#endif
@@ -1206,6 +1389,8 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
gateway,
netmask);
+ /* FIXME -- add LR_MATCH support for FreeBSD */
+
argv_msg (D_ROUTE, &argv);
status = openvpn_execve_check (&argv, es, 0, "ERROR: FreeBSD route add command failed");
@@ -1215,7 +1400,7 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
ROUTE_PATH);
#if 0
- if (r->metric_defined)
+ if (r->flags & RT_METRIC_DEFINED)
argv_printf_cat (&argv, "-rtt %d", r->metric);
#endif
@@ -1224,6 +1409,8 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
gateway,
netmask);
+ /* FIXME -- add LR_MATCH support for Dragonfly */
+
argv_msg (D_ROUTE, &argv);
status = openvpn_execve_check (&argv, es, 0, "ERROR: DragonFly route add command failed");
@@ -1233,14 +1420,26 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
ROUTE_PATH);
#if 0
- if (r->metric_defined)
+ if (r->flags & RT_METRIC_DEFINED)
argv_printf_cat (&argv, "-rtt %d", r->metric);
#endif
- argv_printf_cat (&argv, "-net %s %s %s",
- network,
- gateway,
- netmask);
+ if (rgi && is_local_route == LR_MATCH)
+ {
+ /* Mac OS X route syntax for LR_MATCH:
+ route add -cloning -net 10.10.0.1 -netmask 255.255.255.255 -interface en0 */
+ argv_printf_cat (&argv, "-cloning -net %s -netmask %s -interface %s",
+ network,
+ netmask,
+ rgi->iface);
+ }
+ else
+ {
+ argv_printf_cat (&argv, "-net %s %s %s",
+ network,
+ gateway,
+ netmask);
+ }
argv_msg (D_ROUTE, &argv);
status = openvpn_execve_check (&argv, es, 0, "ERROR: OS X route add command failed");
@@ -1251,7 +1450,7 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
ROUTE_PATH);
#if 0
- if (r->metric_defined)
+ if (r->flags & RT_METRIC_DEFINED)
argv_printf_cat (&argv, "-rtt %d", r->metric);
#endif
@@ -1260,6 +1459,8 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
gateway,
netmask);
+ /* FIXME -- add LR_MATCH support for OpenBSD/NetBSD */
+
argv_msg (D_ROUTE, &argv);
status = openvpn_execve_check (&argv, es, 0, "ERROR: OpenBSD/NetBSD route add command failed");
@@ -1268,7 +1469,10 @@ add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const s
#endif
done:
- r->defined = status;
+ if (status)
+ r->flags |= RT_ADDED;
+ else
+ r->flags &= ~RT_ADDED;
argv_reset (&argv);
gc_free (&gc);
}
@@ -1455,15 +1659,20 @@ add_route_ipv6 (struct route_ipv6 *r6, const struct tuntap *tt, unsigned int fla
}
static void
-delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
+delete_route (struct route *r,
+ const struct tuntap *tt,
+ unsigned int flags,
+ const struct route_gateway_info *rgi,
+ const struct env_set *es)
{
struct gc_arena gc;
struct argv argv;
const char *network;
const char *netmask;
const char *gateway;
+ int is_local_route;
- if (!r->defined)
+ if ((r->flags & (RT_DEFINED|RT_ADDED)) != (RT_DEFINED|RT_ADDED))
return;
gc_init (&gc);
@@ -1473,6 +1682,10 @@ delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags
netmask = print_in_addr_t (r->netmask, 0, &gc);
gateway = print_in_addr_t (r->gateway, 0, &gc);
+ is_local_route = local_route(r->network, r->netmask, r->gateway, rgi);
+ if (is_local_route == LR_ERROR)
+ goto done;
+
#if defined(TARGET_LINUX)
#ifdef CONFIG_FEATURE_IPROUTE
argv_printf (&argv, "%s route del %s/%d",
@@ -1480,13 +1693,12 @@ delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags
network,
count_netmask_bits(netmask));
#else
-
argv_printf (&argv, "%s del -net %s netmask %s",
- ROUTE_PATH,
- network,
- netmask);
+ ROUTE_PATH,
+ network,
+ netmask);
#endif /*CONFIG_FEATURE_IPROUTE*/
- if (r->metric_defined)
+ if (r->flags & RT_METRIC_DEFINED)
argv_printf_cat (&argv, "metric %d", r->metric);
argv_msg (D_ROUTE, &argv);
openvpn_execve_check (&argv, es, 0, "ERROR: Linux route delete command failed");
@@ -1565,11 +1777,22 @@ delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags
#elif defined(TARGET_DARWIN)
- argv_printf (&argv, "%s delete -net %s %s %s",
- ROUTE_PATH,
- network,
- gateway,
- netmask);
+ if (rgi && is_local_route == LR_MATCH)
+ {
+ argv_printf (&argv, "%s delete -cloning -net %s -netmask %s -interface %s",
+ ROUTE_PATH,
+ network,
+ netmask,
+ rgi->iface);
+ }
+ else
+ {
+ argv_printf (&argv, "%s delete -net %s %s %s",
+ ROUTE_PATH,
+ network,
+ gateway,
+ netmask);
+ }
argv_msg (D_ROUTE, &argv);
openvpn_execve_check (&argv, es, 0, "ERROR: OS X route delete command failed");
@@ -1589,6 +1812,8 @@ delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags
msg (M_FATAL, "Sorry, but I don't know how to do 'route' commands on this operating system. Try putting your routes in a --route-up script");
#endif
+ done:
+ r->flags &= ~RT_ADDED;
argv_reset (&argv);
gc_free (&gc);
}
@@ -1816,7 +2041,7 @@ test_routes (const struct route_list *rl, const struct tuntap *tt)
for (i = 0; i < rl->n; ++i)
test_route_helper (&ret, &count, &good, &ambig, adapters, rl->routes[i].gateway);
- if ((rl->flags & RG_ENABLE) && rl->spec.remote_endpoint_defined)
+ if ((rl->flags & RG_ENABLE) && (rl->spec.flags & RTSA_REMOTE_ENDPOINT))
test_route_helper (&ret, &count, &good, &ambig, adapters, rl->spec.remote_endpoint);
}
}
@@ -1874,29 +2099,41 @@ get_default_gateway_row (const MIB_IPFORWARDTABLE *routes)
return ret;
}
-bool
-get_default_gateway (in_addr_t *gw, in_addr_t *netmask)
+void
+get_default_gateway (struct route_gateway_info *rgi)
{
struct gc_arena gc = gc_new ();
- bool ret_bool = false;
const IP_ADAPTER_INFO *adapters = get_adapter_info_list (&gc);
const MIB_IPFORWARDTABLE *routes = get_windows_routing_table (&gc);
const MIB_IPFORWARDROW *row = get_default_gateway_row (routes);
+ DWORD a_index;
+ const IP_ADAPTER_INFO *ai;
+
+ CLEAR(*rgi);
if (row)
{
- *gw = ntohl (row->dwForwardNextHop);
- if (netmask)
+ rgi->gateway.addr = ntohl (row->dwForwardNextHop);
+ if (rgi->gateway.addr)
{
- if (adapter_index_of_ip (adapters, *gw, NULL, netmask) == ~0)
- *netmask = ~0;
+ rgi->flags |= RGI_ADDR_DEFINED;
+ a_index = adapter_index_of_ip (adapters, rgi->gateway.addr, NULL, &rgi->gateway.netmask);
+ if (a_index != ~0)
+ {
+ rgi->adapter_index = a_index;
+ rgi->flags |= (RGI_IFACE_DEFINED|RGI_NETMASK_DEFINED);
+ ai = get_adapter (adapters, a_index);
+ if (ai)
+ {
+ memcpy (rgi->hwaddr, ai->Address, 6);
+ rgi->flags |= RGI_HWADDR_DEFINED;
+ }
+ }
}
- ret_bool = true;
}
gc_free (&gc);
- return ret_bool;
}
static DWORD
@@ -1945,12 +2182,12 @@ windows_route_find_if_index (const struct route *r, const struct tuntap *tt)
}
bool
-add_route_ipapi (const struct route *r, const struct tuntap *tt)
+add_route_ipapi (const struct route *r, const struct tuntap *tt, DWORD adapter_index)
{
struct gc_arena gc = gc_new ();
bool ret = false;
DWORD status;
- const DWORD if_index = windows_route_find_if_index (r, tt);
+ const DWORD if_index = (adapter_index == ~0) ? windows_route_find_if_index (r, tt) : adapter_index;
if (if_index != ~0)
{
@@ -1965,7 +2202,7 @@ add_route_ipapi (const struct route *r, const struct tuntap *tt)
fr.dwForwardProto = 3; /* PROTO_IP_NETMGMT */
fr.dwForwardAge = 0;
fr.dwForwardNextHopAS = 0;
- fr.dwForwardMetric1 = r->metric_defined ? r->metric : 1;
+ fr.dwForwardMetric1 = (r->flags & RT_METRIC_DEFINED) ? r->metric : 1;
fr.dwForwardMetric2 = ~0;
fr.dwForwardMetric3 = ~0;
fr.dwForwardMetric4 = ~0;
@@ -2096,75 +2333,136 @@ show_routes (int msglev)
#elif defined(TARGET_LINUX)
-bool
-get_default_gateway (in_addr_t *gateway, in_addr_t *netmask)
+void
+get_default_gateway (struct route_gateway_info *rgi)
{
struct gc_arena gc = gc_new ();
- bool ret = false;
- FILE *fp = fopen ("/proc/net/route", "r");
- if (fp)
- {
- char line[256];
- int count = 0;
- int best_count = 0;
- unsigned int lowest_metric = ~0;
- in_addr_t best_gw = 0;
- while (fgets (line, sizeof (line), fp) != NULL)
+ int sd = -1;
+
+ CLEAR(*rgi);
+
+ /* get default gateway IP addr */
+ {
+ FILE *fp = fopen ("/proc/net/route", "r");
+ if (fp)
+ {
+ char line[256];
+ int count = 0;
+ unsigned int lowest_metric = ~0;
+ in_addr_t best_gw = 0;
+ while (fgets (line, sizeof (line), fp) != NULL)
+ {
+ if (count)
+ {
+ unsigned int net_x = 0;
+ unsigned int mask_x = 0;
+ unsigned int gw_x = 0;
+ unsigned int metric = 0;
+ unsigned int flags = 0;
+ const int np = sscanf (line, "%*s\t%x\t%x\t%x\t%*s\t%*s\t%d\t%x",
+ &net_x,
+ &gw_x,
+ &flags,
+ &metric,
+ &mask_x);
+ if (np == 5 && (flags & IFF_UP))
+ {
+ const in_addr_t net = ntohl (net_x);
+ const in_addr_t mask = ntohl (mask_x);
+ const in_addr_t gw = ntohl (gw_x);
+
+ if (!net && !mask && metric < lowest_metric)
+ {
+ best_gw = gw;
+ lowest_metric = metric;
+ }
+ }
+ }
+ ++count;
+ }
+ fclose (fp);
+
+ if (best_gw)
+ {
+ rgi->gateway.addr = best_gw;
+ rgi->flags |= RGI_ADDR_DEFINED;
+ }
+ }
+ }
+
+ /* scan adapter list */
+ if (rgi->flags & RGI_ADDR_DEFINED)
+ {
+ struct ifreq *ifr, *ifend;
+ in_addr_t addr, netmask;
+ struct ifreq ifreq;
+ struct ifconf ifc;
+ struct ifreq ifs[20]; /* Maximum number of interfaces to scan */
+
+ if ((sd = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
{
- if (count)
- {
- unsigned int net_x = 0;
- unsigned int mask_x = 0;
- unsigned int gw_x = 0;
- unsigned int metric = 0;
- const int np = sscanf (line, "%*s\t%x\t%x\t%*s\t%*s\t%*s\t%d\t%x",
- &net_x,
- &gw_x,
- &metric,
- &mask_x);
- if (np == 4)
- {
- const in_addr_t net = ntohl (net_x);
- const in_addr_t mask = ntohl (mask_x);
- const in_addr_t gw = ntohl (gw_x);
-
- dmsg (D_ROUTE_DEBUG, "GDG: route[%d] %s/%s/%s m=%u",
- count,
- print_in_addr_t ((in_addr_t) net, 0, &gc),
- print_in_addr_t ((in_addr_t) mask, 0, &gc),
- print_in_addr_t ((in_addr_t) gw, 0, &gc),
- metric);
-
- if (!net && !mask && metric < lowest_metric)
- {
- best_gw = gw;
- lowest_metric = metric;
- best_count = count;
- }
- }
- }
- ++count;
+ msg (M_WARN, "GDG: socket() failed");
+ goto done;
+ }
+ ifc.ifc_len = sizeof (ifs);
+ ifc.ifc_req = ifs;
+ if (ioctl (sd, SIOCGIFCONF, &ifc) < 0)
+ {
+ msg (M_WARN, "GDG: ioctl(SIOCGIFCONF) failed");
+ goto done;
}
- fclose (fp);
- if (best_gw)
+ /* scan through interface list */
+ ifend = ifs + (ifc.ifc_len / sizeof (struct ifreq));
+ for (ifr = ifc.ifc_req; ifr < ifend; ifr++)
{
- *gateway = best_gw;
- if (netmask)
+ if (ifr->ifr_addr.sa_family == AF_INET)
{
- *netmask = 0xFFFFFF00; /* FIXME -- get the real netmask of the adapter containing the default gateway */
+ /* get interface addr */
+ addr = ntohl(((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr);
+
+ /* get interface name */
+ strncpynt (ifreq.ifr_name, ifr->ifr_name, sizeof (ifreq.ifr_name));
+
+ /* check that the interface is up, and not point-to-point or loopback */
+ if (ioctl (sd, SIOCGIFFLAGS, &ifreq) < 0)
+ continue;
+ if (!(ifreq.ifr_flags & IFF_UP))
+ continue;
+
+ /* get interface netmask */
+ if (ioctl (sd, SIOCGIFNETMASK, &ifreq) < 0)
+ continue;
+ netmask = ntohl(((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr.s_addr);
+
+ /* check that interface matches default route */
+ if (((rgi->gateway.addr ^ addr) & netmask) != 0)
+ continue;
+
+ /* save iface name and netmask */
+ strncpynt (rgi->iface, ifreq.ifr_name, sizeof(rgi->iface));
+ rgi->gateway.netmask = netmask;
+ rgi->flags |= (RGI_IFACE_DEFINED|RGI_NETMASK_DEFINED);
+
+ /* now get the hardware address. */
+ memset (&ifreq.ifr_hwaddr, 0, sizeof (struct sockaddr));
+ if (ioctl (sd, SIOCGIFHWADDR, &ifreq) < 0)
+ {
+ msg (M_WARN, "GDG: SIOCGIFHWADDR(%s) failed", ifreq.ifr_name);
+ goto done;
+ }
+ memcpy (rgi->hwaddr, &ifreq.ifr_hwaddr.sa_data, 6);
+ rgi->flags |= RGI_HWADDR_DEFINED;
+
+ break;
}
- ret = true;
}
-
- dmsg (D_ROUTE_DEBUG, "GDG: best=%s[%d] lm=%u",
- print_in_addr_t ((in_addr_t) best_gw, 0, &gc),
- best_count,
- (unsigned int)lowest_metric);
}
+ done:
+ if (sd >= 0)
+ close (sd);
gc_free (&gc);
- return ret;
}
#elif defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY)
@@ -2228,8 +2526,11 @@ struct {
#define ROUNDUP(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
-bool
-get_default_gateway (in_addr_t *ret, in_addr_t *netmask)
+/*
+ * FIXME -- add support for netmask, hwaddr, and iface
+ */
+void
+get_default_gateway (struct route_gateway_info *rgi)
{
struct gc_arena gc = gc_new ();
int s, seq, l, pid, rtm_addrs, i;
@@ -2247,6 +2548,8 @@ get_default_gateway (in_addr_t *ret, in_addr_t *netmask)
#define rtm m_rtmsg.m_rtm
+ CLEAR(*rgi);
+
pid = getpid();
seq = 0;
rtm_addrs = RTA_DST | RTA_NETMASK;
@@ -2308,24 +2611,14 @@ get_default_gateway (in_addr_t *ret, in_addr_t *netmask)
if (gate != NULL )
{
- *ret = ntohl(((struct sockaddr_in *)gate)->sin_addr.s_addr);
-#if 0
- msg (M_INFO, "gw %s",
- print_in_addr_t ((in_addr_t) *ret, 0, &gc));
-#endif
-
- if (netmask)
- {
- *netmask = 0xFFFFFF00; // FIXME -- get the real netmask of the adapter containing the default gateway
- }
+ rgi->gateway.addr = ntohl(((struct sockaddr_in *)gate)->sin_addr.s_addr);
+ rgi->flags |= RGI_ADDR_DEFINED;
gc_free (&gc);
- return true;
}
else
{
gc_free (&gc);
- return false;
}
}
@@ -2345,26 +2638,32 @@ struct rtmsg {
#define ROUNDUP(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof(uint32_t) - 1))) : sizeof(uint32_t))
-static bool
-get_default_gateway_ex (in_addr_t *ret, in_addr_t *netmask, char **ifname)
+#define NEXTADDR(w, u) \
+ if (rtm_addrs & (w)) {\
+ l = ROUNDUP(u.sa_len); memmove(cp, &(u), l); cp += l;\
+ }
+
+#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+
+#define max(a,b) ((a) > (b) ? (a) : (b))
+
+void
+get_default_gateway (struct route_gateway_info *rgi)
{
struct gc_arena gc = gc_new ();
struct rtmsg m_rtmsg;
- int s, seq, l, pid, rtm_addrs, i;
+ int sockfd = -1;
+ int seq, l, pid, rtm_addrs, i;
struct sockaddr so_dst, so_mask;
char *cp = m_rtmsg.m_space;
struct sockaddr *gate = NULL, *ifp = NULL, *sa;
struct rt_msghdr *rtm_aux;
-#define NEXTADDR(w, u) \
- if (rtm_addrs & (w)) {\
- l = ROUNDUP(u.sa_len); memmove(cp, &(u), l); cp += l;\
- }
-
-#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+# define rtm m_rtmsg.m_rtm
-#define rtm m_rtmsg.m_rtm
+ CLEAR(*rgi);
+ /* setup data to send to routing socket */
pid = getpid();
seq = 0;
rtm_addrs = RTA_DST | RTA_NETMASK | RTA_IFP;
@@ -2390,85 +2689,148 @@ get_default_gateway_ex (in_addr_t *ret, in_addr_t *netmask, char **ifname)
rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;
- s = socket(PF_ROUTE, SOCK_RAW, 0);
-
- if (write(s, (char *)&m_rtmsg, l) < 0)
+ /* transact with routing socket */
+ sockfd = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (sockfd < 0)
{
- msg (M_WARN, "ROUTE: problem writing to routing socket");
- gc_free (&gc);
- close(s);
- return false;
+ msg (M_WARN, "GDG: socket #1 failed");
+ goto done;
+ }
+ if (write(sockfd, (char *)&m_rtmsg, l) < 0)
+ {
+ msg (M_WARN, "GDG: problem writing to routing socket");
+ goto done;
}
-
do {
- l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
+ l = read(sockfd, (char *)&m_rtmsg, sizeof(m_rtmsg));
} while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
-
- close(s);
+ close(sockfd);
+ sockfd = -1;
+ /* extract return data from routing socket */
rtm_aux = &rtm;
-
cp = ((char *)(rtm_aux + 1));
- if (rtm_aux->rtm_addrs) {
- for (i = 1; i; i <<= 1)
- {
- if (i & rtm_aux->rtm_addrs)
- {
- sa = (struct sockaddr *)cp;
- if (i == RTA_GATEWAY )
- gate = sa;
- else if (i == RTA_IFP)
- ifp = sa;
- ADVANCE(cp, sa);
- }
- }
- }
+ if (rtm_aux->rtm_addrs)
+ {
+ for (i = 1; i; i <<= 1)
+ {
+ if (i & rtm_aux->rtm_addrs)
+ {
+ sa = (struct sockaddr *)cp;
+ if (i == RTA_GATEWAY )
+ gate = sa;
+ else if (i == RTA_IFP)
+ ifp = sa;
+ ADVANCE(cp, sa);
+ }
+ }
+ }
else
+ goto done;
+
+ /* get gateway addr and interface name */
+ if (gate != NULL )
{
- gc_free (&gc);
- return false;
+ /* get default gateway addr */
+ rgi->gateway.addr = ntohl(((struct sockaddr_in *)gate)->sin_addr.s_addr);
+ if (rgi->gateway.addr)
+ rgi->flags |= RGI_ADDR_DEFINED;
+
+ if (ifp)
+ {
+ /* get interface name */
+ const struct sockaddr_dl *adl = (struct sockaddr_dl *) ifp;
+ int len = adl->sdl_nlen;
+ if (adl->sdl_nlen && adl->sdl_nlen < sizeof(rgi->iface))
+ {
+ memcpy (rgi->iface, adl->sdl_data, adl->sdl_nlen);
+ rgi->iface[adl->sdl_nlen] = '\0';
+ rgi->flags |= RGI_IFACE_DEFINED;
+ }
+ }
}
+ /* get netmask of interface that owns default gateway */
+ if (rgi->flags & RGI_IFACE_DEFINED) {
+ struct ifreq ifr;
- if (gate != NULL )
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd < 0)
+ {
+ msg (M_WARN, "GDG: socket #2 failed");
+ goto done;
+ }
+
+ CLEAR(ifr);
+ ifr.ifr_addr.sa_family = AF_INET;
+ strncpynt(ifr.ifr_name, rgi->iface, IFNAMSIZ);
+
+ if (ioctl(sockfd, SIOCGIFNETMASK, (char *)&ifr) < 0)
+ {
+ msg (M_WARN, "GDG: ioctl #1 failed");
+ goto done;
+ }
+ close(sockfd);
+ sockfd = -1;
+
+ rgi->gateway.netmask = ntohl(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr);
+ rgi->flags |= RGI_NETMASK_DEFINED;
+ }
+
+ /* try to read MAC addr associated with interface that owns default gateway */
+ if (rgi->flags & RGI_IFACE_DEFINED)
{
- *ret = ntohl(((struct sockaddr_in *)gate)->sin_addr.s_addr);
-#if 0
- msg (M_INFO, "gw %s",
- print_in_addr_t ((in_addr_t) *ret, 0, &gc));
-#endif
+ struct ifconf ifc;
+ struct ifreq *ifr;
+ const int bufsize = 4096;
+ char *buffer;
- if (netmask)
+ buffer = (char *) gc_malloc (bufsize, true, &gc);
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd < 0)
{
- *netmask = 0xFFFFFF00; // FIXME -- get the real netmask of the adapter containing the default gateway
+ msg (M_WARN, "GDG: socket #3 failed");
+ goto done;
}
- if (ifp && ifname)
+ ifc.ifc_len = bufsize;
+ ifc.ifc_buf = buffer;
+
+ if (ioctl(sockfd, SIOCGIFCONF, (char *)&ifc) < 0)
{
- struct sockaddr_dl *adl = (struct sockaddr_dl *) ifp;
- char *name = malloc(adl->sdl_nlen+1);
- check_malloc_return(name);
- memcpy(name, adl->sdl_data, adl->sdl_nlen);
- name[adl->sdl_nlen] = '\0';
- *ifname = name;
+ msg (M_WARN, "GDG: ioctl #2 failed");
+ goto done;
}
+ close(sockfd);
+ sockfd = -1;
- gc_free (&gc);
- return true;
- }
- else
- {
- gc_free (&gc);
- return false;
+ for (cp = buffer; cp <= buffer + ifc.ifc_len - sizeof(struct ifreq); )
+ {
+ ifr = (struct ifreq *)cp;
+ const size_t len = sizeof(ifr->ifr_name) + max(sizeof(ifr->ifr_addr), ifr->ifr_addr.sa_len);
+ if (!ifr->ifr_addr.sa_family)
+ break;
+ if (!strncmp(ifr->ifr_name, rgi->iface, IFNAMSIZ))
+ {
+ if (ifr->ifr_addr.sa_family == AF_LINK)
+ {
+ struct sockaddr_dl *sdl = (struct sockaddr_dl *)&ifr->ifr_addr;
+ memcpy(rgi->hwaddr, LLADDR(sdl), 6);
+ rgi->flags |= RGI_HWADDR_DEFINED;
+ }
+ }
+ cp += len;
+ }
}
-}
-bool
-get_default_gateway (in_addr_t *ret, in_addr_t *netmask)
-{
- return get_default_gateway_ex(ret, netmask, NULL);
+ done:
+ if (sockfd >= 0)
+ close(sockfd);
+ gc_free (&gc);
}
+#undef max
+
#elif defined(TARGET_OPENBSD) || defined(TARGET_NETBSD)
#include <sys/types.h>
@@ -2529,8 +2891,11 @@ struct {
#define ROUNDUP(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
-bool
-get_default_gateway (in_addr_t *ret, in_addr_t *netmask)
+/*
+ * FIXME -- add support for netmask, hwaddr, and iface
+ */
+void
+get_default_gateway (struct route_gateway_info *rgi)
{
struct gc_arena gc = gc_new ();
int s, seq, l, rtm_addrs, i;
@@ -2549,6 +2914,8 @@ get_default_gateway (in_addr_t *ret, in_addr_t *netmask)
#define rtm m_rtmsg.m_rtm
+ CLEAR(*rgi);
+
pid = getpid();
seq = 0;
rtm_addrs = RTA_DST | RTA_NETMASK;
@@ -2610,33 +2977,47 @@ get_default_gateway (in_addr_t *ret, in_addr_t *netmask)
if (gate != NULL )
{
- *ret = ntohl(((struct sockaddr_in *)gate)->sin_addr.s_addr);
-#if 0
- msg (M_INFO, "gw %s",
- print_in_addr_t ((in_addr_t) *ret, 0, &gc));
-#endif
-
- if (netmask)
- {
- *netmask = 0xFFFFFF00; // FIXME -- get the real netmask of the adapter containing the default gateway
- }
+ rgi->gateway.addr = ntohl(((struct sockaddr_in *)gate)->sin_addr.s_addr);
+ rgi->flags |= RGI_ADDR_DEFINED;
gc_free (&gc);
- return true;
}
else
{
gc_free (&gc);
- return false;
}
}
#else
-bool
-get_default_gateway (in_addr_t *ret, in_addr_t *netmask) /* PLATFORM-SPECIFIC */
+/*
+ * This is a platform-specific method that returns data about
+ * the current default gateway. Return data is placed into
+ * a struct route_gateway_info object provided by caller. The
+ * implementation should CLEAR the structure before adding
+ * data to it.
+ *
+ * Data returned includes:
+ * 1. default gateway address (rgi->gateway.addr)
+ * 2. netmask of interface that owns default gateway
+ * (rgi->gateway.netmask)
+ * 3. hardware address (i.e. MAC address) of interface that owns
+ * default gateway (rgi->hwaddr)
+ * 4. interface name (or adapter index on Windows) that owns default
+ * gateway (rgi->iface or rgi->adapter_index)
+ * 5. an array of additional address/netmask pairs defined by
+ * interface that owns default gateway (rgi->addrs with length
+ * given in rgi->n_addrs)
+ *
+ * The flags RGI_x_DEFINED may be used to indicate which of the data
+ * members were successfully returned (set in rgi->flags). All of
+ * the data members are optional, however certain OpenVPN functionality
+ * may be disabled by missing items.
+ */
+void
+get_default_gateway (struct route_gateway_info *rgi)
{
- return false;
+ CLEAR(*rgi);
}
#endif
@@ -2676,18 +3057,8 @@ netmask_to_netbits (const in_addr_t network, const in_addr_t netmask, int *netbi
static void
add_host_route_if_nonlocal (struct route_bypass *rb, const in_addr_t addr)
{
- if (test_local_addr(addr) == TLA_NONLOCAL && addr != 0 && addr != ~0) {
- int i;
- for (i = 0; i < rb->n_bypass; ++i)
- {
- if (addr == rb->bypass[i]) /* avoid duplicates */
- return;
- }
- if (rb->n_bypass < N_ROUTE_BYPASS)
- {
- rb->bypass[rb->n_bypass++] = addr;
- }
- }
+ if (test_local_addr(addr, NULL) == TLA_NONLOCAL && addr != 0 && addr != ~0)
+ add_bypass_address (rb, addr);
}
static void
@@ -2746,222 +3117,6 @@ get_bypass_addresses (struct route_bypass *rb, const unsigned int flags) /* PLA
#endif
-#if AUTO_USERID || defined(ENABLE_PUSH_PEER_INFO)
-
-#if defined(TARGET_LINUX)
-
-bool
-get_default_gateway_mac_addr (unsigned char *macaddr)
-{
- struct ifreq *ifr, *ifend;
- in_addr_t ina, mask;
- struct ifreq ifreq;
- struct ifconf ifc;
- struct ifreq ifs[20]; // Maximum number of interfaces to scan
- int sd = -1;
- in_addr_t gwip = 0;
- bool ret = false;
-
- if (!get_default_gateway (&gwip, NULL))
- {
- msg (M_WARN, "GDGMA: get_default_gateway failed");
- goto err;
- }
-
- if ((sd = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
- {
- msg (M_WARN, "GDGMA: socket() failed");
- goto err;
- }
-
- ifc.ifc_len = sizeof (ifs);
- ifc.ifc_req = ifs;
- if (ioctl (sd, SIOCGIFCONF, &ifc) < 0)
- {
- msg (M_WARN, "GDGMA: ioctl(SIOCGIFCONF) failed");
- goto err;
- }
-
- /* scan through interface list */
- ifend = ifs + (ifc.ifc_len / sizeof (struct ifreq));
- for (ifr = ifc.ifc_req; ifr < ifend; ifr++)
- {
- if (ifr->ifr_addr.sa_family == AF_INET)
- {
- ina = ntohl(((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr);
- strncpynt (ifreq.ifr_name, ifr->ifr_name, sizeof (ifreq.ifr_name));
-
- dmsg (D_AUTO_USERID, "GDGMA: %s", ifreq.ifr_name);
-
- /* check that the interface is up, and not point-to-point or loopback */
- if (ioctl (sd, SIOCGIFFLAGS, &ifreq) < 0)
- {
- dmsg (D_AUTO_USERID, "GDGMA: SIOCGIFFLAGS(%s) failed", ifreq.ifr_name);
- continue;
- }
-
- if ((ifreq.ifr_flags & (IFF_UP|IFF_LOOPBACK)) != IFF_UP)
- {
- dmsg (D_AUTO_USERID, "GDGMA: interface %s is down or loopback", ifreq.ifr_name);
- continue;
- }
-
- /* get interface netmask and check for correct subnet */
- if (ioctl (sd, SIOCGIFNETMASK, &ifreq) < 0)
- {
- dmsg (D_AUTO_USERID, "GDGMA: SIOCGIFNETMASK(%s) failed", ifreq.ifr_name);
- continue;
- }
-
- mask = ntohl(((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr.s_addr);
- if (((gwip ^ ina) & mask) != 0)
- {
- dmsg (D_AUTO_USERID, "GDGMA: gwip=0x%08x ina=0x%08x mask=0x%08x",
- (unsigned int)gwip,
- (unsigned int)ina,
- (unsigned int)mask);
- continue;
- }
- break;
- }
- }
- if (ifr >= ifend)
- {
- msg (M_WARN, "GDGMA: couldn't find gw interface");
- goto err;
- }
-
- /* now get the hardware address. */
- memset (&ifreq.ifr_hwaddr, 0, sizeof (struct sockaddr));
- if (ioctl (sd, SIOCGIFHWADDR, &ifreq) < 0)
- {
- msg (M_WARN, "GDGMA: SIOCGIFHWADDR(%s) failed", ifreq.ifr_name);
- goto err;
- }
-
- memcpy (macaddr, &ifreq.ifr_hwaddr.sa_data, 6);
- ret = true;
-
- err:
- if (sd >= 0)
- close (sd);
- return ret;
-}
-
-#elif defined(WIN32)
-
-bool
-get_default_gateway_mac_addr (unsigned char *macaddr)
-{
- struct gc_arena gc = gc_new ();
- const IP_ADAPTER_INFO *adapters = get_adapter_info_list (&gc);
- in_addr_t gwip = 0;
- DWORD a_index;
- const IP_ADAPTER_INFO *ai;
-
- if (!get_default_gateway (&gwip, NULL))
- {
- msg (M_WARN, "GDGMA: get_default_gateway failed");
- goto err;
- }
-
- a_index = adapter_index_of_ip (adapters, gwip, NULL, NULL);
- ai = get_adapter (adapters, a_index);
-
- if (!ai)
- {
- msg (M_WARN, "GDGMA: couldn't find gw interface");
- goto err;
- }
-
- memcpy (macaddr, ai->Address, 6);
-
- gc_free (&gc);
- return true;
-
- err:
- gc_free (&gc);
- return false;
-}
-
-#elif defined(TARGET_DARWIN)
-
-bool
-get_default_gateway_mac_addr (unsigned char *macaddr)
-{
-# define max(a,b) ((a) > (b) ? (a) : (b))
- struct gc_arena gc = gc_new ();
- struct ifconf ifc;
- struct ifreq *ifr;
- char *buffer, *cp;
- bool status = false;
- in_addr_t gw = 0;
- char *ifname = NULL;
- int sockfd = -1;
- const int bufsize = 4096;
-
- if (!get_default_gateway_ex (&gw, NULL, &ifname)) /* get interface name of default gateway */
- {
- msg (M_WARN, "GDGMA: get_default_gateway_ex failed");
- goto done;
- }
-
- if (!ifname)
- {
- msg (M_WARN, "GDGMA: cannot get default gateway ifname");
- goto done;
- }
-
- buffer = (char *) gc_malloc (bufsize, true, &gc);
-
- sockfd = socket(AF_INET, SOCK_DGRAM, 0);
- if (sockfd < 0)
- {
- msg (M_WARN, "GDGMA: socket failed");
- goto done;
- }
-
- ifc.ifc_len = bufsize;
- ifc.ifc_buf = buffer;
-
- if (ioctl(sockfd, SIOCGIFCONF, (char *)&ifc) < 0)
- {
- msg (M_WARN, "GDGMA: ioctl failed");
- goto done;
- }
-
- for (cp = buffer; cp <= buffer + bufsize - sizeof(struct ifreq); )
- {
- ifr = (struct ifreq *)cp;
- if (ifr->ifr_addr.sa_family == AF_LINK && !strncmp(ifr->ifr_name, ifname, IFNAMSIZ))
- {
- struct sockaddr_dl *sdl = (struct sockaddr_dl *)&ifr->ifr_addr;
- memcpy(macaddr, LLADDR(sdl), 6);
- status = true;
- }
- cp += sizeof(ifr->ifr_name) + max(sizeof(ifr->ifr_addr), ifr->ifr_addr.sa_len);
- }
-
- done:
- if (sockfd >= 0)
- close (sockfd);
- free (ifname);
- gc_free (&gc);
- return status;
-# undef max
-}
-
-#else
-
-bool
-get_default_gateway_mac_addr (unsigned char *macaddr) /* PLATFORM-SPECIFIC */
-{
- return false;
-}
-
-#endif
-#endif /* AUTO_USERID */
-
/*
* Test if addr is reachable via a local interface (return ILA_LOCAL),
* or if it needs to be routed via the default gateway (return
@@ -2974,7 +3129,7 @@ get_default_gateway_mac_addr (unsigned char *macaddr) /* PLATFORM-SPECIFIC */
#if defined(WIN32)
int
-test_local_addr (const in_addr_t addr)
+test_local_addr (const in_addr_t addr, const struct route_gateway_info *rgi)
{
struct gc_arena gc = gc_new ();
const in_addr_t nonlocal_netmask = 0x80000000L; /* routes with netmask <= to this are considered non-local */
@@ -3004,10 +3159,16 @@ test_local_addr (const in_addr_t addr)
#else
-
int
-test_local_addr (const in_addr_t addr) /* PLATFORM-SPECIFIC */
+test_local_addr (const in_addr_t addr, const struct route_gateway_info *rgi) /* PLATFORM-SPECIFIC */
{
+ if (rgi)
+ {
+ if (local_route (addr, 0xFFFFFFFF, rgi->gateway.addr, rgi))
+ return TLA_LOCAL;
+ else
+ return TLA_NONLOCAL;
+ }
return TLA_NOT_IMPLEMENTED;
}
diff --git a/route.h b/route.h
index 6a7704f..f900d3e 100644
--- a/route.h
+++ b/route.h
@@ -29,6 +29,7 @@
#ifndef ROUTE_H
#define ROUTE_H
+#include "basic.h"
#include "tun.h"
#include "misc.h"
@@ -58,15 +59,17 @@ struct route_bypass
struct route_special_addr
{
+ /* bits indicating which members below are defined */
+# define RTSA_REMOTE_ENDPOINT (1<<0)
+# define RTSA_REMOTE_HOST (1<<1)
+# define RTSA_DEFAULT_METRIC (1<<2)
+ unsigned int flags;
+
in_addr_t remote_endpoint;
- bool remote_endpoint_defined;
- in_addr_t net_gateway;
- bool net_gateway_defined;
in_addr_t remote_host;
- bool remote_host_defined;
+ int remote_host_local; /* TLA_x value */
struct route_bypass bypass;
int default_metric;
- bool default_metric_defined;
};
struct route_option {
@@ -84,9 +87,10 @@ struct route_option {
#define RG_BYPASS_DNS (1<<4)
#define RG_REROUTE_GW (1<<5)
#define RG_AUTO_LOCAL (1<<6)
+#define RG_BLOCK_LOCAL (1<<7)
struct route_option_list {
- unsigned int flags;
+ unsigned int flags; /* RG_x flags */
int capacity;
int n;
struct route_option routes[EMPTY_ARRAY_SIZE];
@@ -106,26 +110,17 @@ struct route_ipv6_option_list {
};
struct route {
- bool defined;
+# define RT_DEFINED (1<<0)
+# define RT_ADDED (1<<1)
+# define RT_METRIC_DEFINED (1<<2)
+ unsigned int flags;
const struct route_option *option;
in_addr_t network;
in_addr_t netmask;
in_addr_t gateway;
- bool metric_defined;
int metric;
};
-struct route_list {
- bool routes_added;
- struct route_special_addr spec;
- unsigned int flags;
- bool did_redirect_default_gateway;
- bool did_local;
- int capacity;
- int n;
- struct route routes[EMPTY_ARRAY_SIZE];
-};
-
struct route_ipv6 {
bool defined;
const struct route_ipv6_option *option;
@@ -151,6 +146,52 @@ struct route_ipv6_list {
};
+struct route_gateway_address {
+ in_addr_t addr;
+ in_addr_t netmask;
+};
+
+struct route_gateway_info {
+# define RGI_ADDR_DEFINED (1<<0) /* set if gateway.addr defined */
+# define RGI_NETMASK_DEFINED (1<<1) /* set if gateway.netmask defined */
+# define RGI_HWADDR_DEFINED (1<<2) /* set if hwaddr is defined */
+# define RGI_IFACE_DEFINED (1<<3) /* set if iface is defined */
+# define RGI_OVERFLOW (1<<4) /* set if more interface addresses than will fit in addrs */
+ unsigned int flags;
+
+ /* gateway interface */
+# ifdef WIN32
+ DWORD adapter_index; /* interface or ~0 if undefined */
+#else
+ char iface[16]; /* interface name (null terminated), may be empty */
+#endif
+
+ /* gateway interface hardware address */
+ uint8_t hwaddr[6];
+
+ /* gateway/router address */
+ struct route_gateway_address gateway;
+
+ /* address/netmask pairs bound to interface */
+# define RGI_N_ADDRESSES 8
+ int n_addrs; /* len of addrs, may be 0 */
+ struct route_gateway_address addrs[RGI_N_ADDRESSES]; /* local addresses attached to iface */
+};
+
+struct route_list {
+# define RL_DID_REDIRECT_DEFAULT_GATEWAY (1<<0)
+# define RL_DID_LOCAL (1<<1)
+# define RL_ROUTES_ADDED (1<<2)
+ unsigned int iflags;
+
+ struct route_special_addr spec;
+ struct route_gateway_info rgi;
+ unsigned int flags; /* RG_x flags */
+ int capacity;
+ int n;
+ struct route routes[EMPTY_ARRAY_SIZE];
+};
+
#if P2MP
/* internal OpenVPN route */
struct iroute {
@@ -168,16 +209,22 @@ struct iroute_ipv6 {
struct route_option_list *new_route_option_list (const int max_routes, struct gc_arena *a);
struct route_ipv6_option_list *new_route_ipv6_option_list (const int max_routes, struct gc_arena *a);
+
struct route_option_list *clone_route_option_list (const struct route_option_list *src, struct gc_arena *a);
void copy_route_option_list (struct route_option_list *dest, const struct route_option_list *src);
struct route_list *new_route_list (const int max_routes, struct gc_arena *a);
struct route_ipv6_list *new_route_ipv6_list (const int max_routes, struct gc_arena *a);
-void add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es);
void add_route_ipv6 (struct route_ipv6 *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es);
void delete_route_ipv6 (const struct route_ipv6 *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es);
+void add_route (struct route *r,
+ const struct tuntap *tt,
+ unsigned int flags,
+ const struct route_gateway_info *rgi,
+ const struct env_set *es);
+
void add_route_to_option_list (struct route_option_list *l,
const char *network,
const char *netmask,
@@ -202,9 +249,9 @@ bool init_route_ipv6_list (struct route_ipv6_list *rl6,
int default_metric,
struct env_set *es);
-void route_list_add_default_gateway (struct route_list *rl,
- struct env_set *es,
- const in_addr_t addr);
+void route_list_add_vpn_gateway (struct route_list *rl,
+ struct env_set *es,
+ const in_addr_t addr);
void add_routes (struct route_list *rl,
struct route_ipv6_list *rl6,
@@ -221,9 +268,12 @@ void delete_routes (struct route_list *rl,
void setenv_routes (struct env_set *es, const struct route_list *rl);
void setenv_routes_ipv6 (struct env_set *es, const struct route_ipv6_list *rl6);
+
+
bool is_special_addr (const char *addr_str);
-bool get_default_gateway (in_addr_t *ip, in_addr_t *netmask);
+void get_default_gateway (struct route_gateway_info *rgi);
+void print_default_gateway(const int msglevel, const struct route_gateway_info *rgi);
/*
* Test if addr is reachable via a local interface (return ILA_LOCAL),
@@ -234,11 +284,7 @@ bool get_default_gateway (in_addr_t *ip, in_addr_t *netmask);
#define TLA_NOT_IMPLEMENTED 0
#define TLA_NONLOCAL 1
#define TLA_LOCAL 2
-int test_local_addr (const in_addr_t addr);
-
-#if AUTO_USERID || defined(ENABLE_PUSH_PEER_INFO)
-bool get_default_gateway_mac_addr (unsigned char *macaddr);
-#endif
+int test_local_addr (const in_addr_t addr, const struct route_gateway_info *rgi);
#ifdef ENABLE_DEBUG
void print_route_options (const struct route_option_list *rol,
@@ -251,7 +297,7 @@ void print_routes (const struct route_list *rl, int level);
void show_routes (int msglev);
bool test_routes (const struct route_list *rl, const struct tuntap *tt);
-bool add_route_ipapi (const struct route *r, const struct tuntap *tt);
+bool add_route_ipapi (const struct route *r, const struct tuntap *tt, DWORD adapter_index);
bool del_route_ipapi (const struct route *r, const struct tuntap *tt);
#else
@@ -271,12 +317,18 @@ netbits_to_netmask (const int netbits)
}
static inline bool
-route_list_default_gateway_needed (const struct route_list *rl)
+route_list_vpn_gateway_needed (const struct route_list *rl)
{
if (!rl)
return false;
else
- return !rl->spec.remote_endpoint_defined;
+ return !(rl->spec.flags & RTSA_REMOTE_ENDPOINT);
+}
+
+static inline int
+route_did_redirect_default_gateway(const struct route_list *rl)
+{
+ return BOOL_CAST(rl->iflags & RL_DID_REDIRECT_DEFAULT_GATEWAY);
}
#endif
diff --git a/ssl.c b/ssl.c
index a0493ff..11b7801 100644
--- a/ssl.c
+++ b/ssl.c
@@ -52,6 +52,7 @@
#include "pkcs11.h"
#include "list.h"
#include "base64.h"
+#include "route.h"
#ifdef WIN32
#include "cryptoapi.h"
@@ -295,17 +296,35 @@ static char *auth_challenge; /* GLOBAL */
#endif
void
-auth_user_pass_setup (const char *auth_file)
+auth_user_pass_setup (const char *auth_file, const struct static_challenge_info *sci)
{
auth_user_pass_enabled = true;
if (!auth_user_pass.defined)
{
#if AUTO_USERID
get_user_pass_auto_userid (&auth_user_pass, auth_file);
-#elif defined(ENABLE_CLIENT_CR)
- get_user_pass_cr (&auth_user_pass, auth_file, UP_TYPE_AUTH, GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE, auth_challenge);
#else
- get_user_pass (&auth_user_pass, auth_file, UP_TYPE_AUTH, GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE);
+# ifdef ENABLE_CLIENT_CR
+ if (auth_challenge) /* dynamic challenge/response */
+ get_user_pass_cr (&auth_user_pass,
+ auth_file,
+ UP_TYPE_AUTH,
+ GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE|GET_USER_PASS_DYNAMIC_CHALLENGE,
+ auth_challenge);
+ else if (sci) /* static challenge response */
+ {
+ int flags = GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE|GET_USER_PASS_STATIC_CHALLENGE;
+ if (sci->flags & SC_ECHO)
+ flags |= GET_USER_PASS_STATIC_CHALLENGE_ECHO;
+ get_user_pass_cr (&auth_user_pass,
+ auth_file,
+ UP_TYPE_AUTH,
+ flags,
+ sci->challenge_text);
+ }
+ else
+# endif
+ get_user_pass (&auth_user_pass, auth_file, UP_TYPE_AUTH, GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE);
#endif
}
}
@@ -4033,10 +4052,10 @@ push_peer_info(struct buffer *buf, struct tls_session *session)
/* push mac addr */
{
- bool get_default_gateway_mac_addr (unsigned char *macaddr);
- uint8_t macaddr[6];
- get_default_gateway_mac_addr (macaddr);
- buf_printf (&out, "IV_HWADDR=%s\n", format_hex_ex (macaddr, 6, 0, 1, ":", &gc));
+ struct route_gateway_info rgi;
+ get_default_gateway (&rgi);
+ if (rgi.flags & RGI_HWADDR_DEFINED)
+ buf_printf (&out, "IV_HWADDR=%s\n", format_hex_ex (rgi.hwaddr, 6, 0, 1, ":", &gc));
}
/* push LZO status */
@@ -4097,7 +4116,11 @@ key_method_2_write (struct buffer *buf, struct tls_session *session)
/* write username/password if specified */
if (auth_user_pass_enabled)
{
- auth_user_pass_setup (NULL);
+#ifdef ENABLE_CLIENT_CR
+ auth_user_pass_setup (NULL, session->opt->sci);
+#else
+ auth_user_pass_setup (NULL, NULL);
+#endif
if (!write_string (buf, auth_user_pass.username, -1))
goto error;
if (!write_string (buf, auth_user_pass.password, -1))
diff --git a/ssl.h b/ssl.h
index 790a57e..f736327 100644
--- a/ssl.h
+++ b/ssl.h
@@ -519,6 +519,10 @@ struct tls_options
const struct x509_track *x509_track;
#endif
+#ifdef ENABLE_CLIENT_CR
+ const struct static_challenge_info *sci;
+#endif
+
/* --gremlin bits */
int gremlin;
};
@@ -723,7 +727,7 @@ void get_highest_preference_tls_cipher (char *buf, int size);
void pem_password_setup (const char *auth_file);
int pem_password_callback (char *buf, int size, int rwflag, void *u);
-void auth_user_pass_setup (const char *auth_file);
+void auth_user_pass_setup (const char *auth_file, const struct static_challenge_info *sc_info);
void ssl_set_auth_nocache (void);
void ssl_set_auth_token (const char *token);
void ssl_purge_auth (const bool auth_user_pass_only);
diff --git a/syshead.h b/syshead.h
index 82b94eb..a1af7a6 100644
--- a/syshead.h
+++ b/syshead.h
@@ -652,6 +652,15 @@ socket_defined (const socket_descriptor_t sd)
#endif
/*
+ * Should we include --management-query-remote functionality
+ */
+#if defined(ENABLE_CONNECTION) && defined(ENABLE_MANAGEMENT)
+#define MANAGEMENT_QUERY_REMOTE 1
+#else
+#define MANAGEMENT_QUERY_REMOTE 0
+#endif
+
+/*
* Reduce sensitivity to system clock instability
* and backtracks.
*/
@@ -674,7 +683,7 @@ socket_defined (const socket_descriptor_t sd)
#endif
/*
- * Do we support challenge/response authentication, as a console-based client?
+ * Do we support challenge/response authentication as client?
*/
#define ENABLE_CLIENT_CR
diff --git a/tun.c b/tun.c
index aa7f667..1d3f8bc 100644
--- a/tun.c
+++ b/tun.c
@@ -248,11 +248,12 @@ check_subnet_conflict (const in_addr_t ip,
const in_addr_t netmask,
const char *prefix)
{
+#if 0 /* too many false positives */
struct gc_arena gc = gc_new ();
in_addr_t lan_gw = 0;
in_addr_t lan_netmask = 0;
- if (get_default_gateway (&lan_gw, &lan_netmask))
+ if (get_default_gateway (&lan_gw, &lan_netmask) && lan_netmask)
{
const in_addr_t lan_network = lan_gw & lan_netmask;
const in_addr_t network = ip & netmask;
@@ -270,18 +271,20 @@ check_subnet_conflict (const in_addr_t ip,
}
}
gc_free (&gc);
+#endif
}
void
warn_on_use_of_common_subnets (void)
{
struct gc_arena gc = gc_new ();
- in_addr_t lan_gw = 0;
- in_addr_t lan_netmask = 0;
+ struct route_gateway_info rgi;
+ const int needed = (RGI_ADDR_DEFINED|RGI_NETMASK_DEFINED);
- if (get_default_gateway (&lan_gw, &lan_netmask))
+ get_default_gateway (&rgi);
+ if ((rgi.flags & needed) == needed)
{
- const in_addr_t lan_network = lan_gw & lan_netmask;
+ const in_addr_t lan_network = rgi.gateway.addr & rgi.gateway.netmask;
if (lan_network == 0xC0A80000 || lan_network == 0xC0A80100)
msg (M_WARN, "NOTE: your local LAN uses the extremely common subnet address 192.168.0.x or 192.168.1.x. Be aware that this might create routing conflicts if you connect to the VPN server from public locations such as internet cafes that use the same subnet.");
}