From patchwork Wed Apr 25 09:57:21 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Antonio Quartulli X-Patchwork-Id: 328 Return-Path: Delivered-To: patchwork@openvpn.net Delivered-To: patchwork@openvpn.net Received: from director11.mail.ord1d.rsapps.net ([172.27.255.9]) by backend30.mail.ord1d.rsapps.net (Dovecot) with LMTP id jyr/Akfe4FrySwAAIUCqbw for ; Wed, 25 Apr 2018 16:00:07 -0400 Received: from proxy9.mail.iad3a.rsapps.net ([172.27.255.9]) by director11.mail.ord1d.rsapps.net (Dovecot) with LMTP id 6aDnAEfe4FpmOAAAvGGmqA ; Wed, 25 Apr 2018 16:00:07 -0400 Received: from smtp5.gate.iad3a ([172.27.255.9]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) by proxy9.mail.iad3a.rsapps.net with LMTP id QDWLBEfe4FprFgAAGuSQww ; Wed, 25 Apr 2018 16:00:07 -0400 X-Spam-Threshold: 95 X-Spam-Score: 0 X-Spam-Flag: NO X-Virus-Scanned: OK X-Orig-To: openvpnslackdevel@openvpn.net X-Originating-Ip: [216.105.38.7] Authentication-Results: smtp5.gate.iad3a.rsapps.net; iprev=pass policy.iprev="216.105.38.7"; spf=pass smtp.mailfrom="openvpn-devel-bounces@lists.sourceforge.net" smtp.helo="lists.sourceforge.net"; dkim=fail (signature verification failed) header.d=sourceforge.net; dkim=fail (signature verification failed) header.d=sf.net; dmarc=none (p=nil; dis=none) header.from=unstable.cc X-Suspicious-Flag: YES X-Classification-ID: 3f91fb6a-48c3-11e8-94c3-525400de824c-1-1 Received: from [216.105.38.7] ([216.105.38.7:27937] helo=lists.sourceforge.net) by smtp5.gate.iad3a.rsapps.net (envelope-from ) (ecelerity 4.2.1.56364 r(Core:4.2.1.14)) with ESMTPS (cipher=DHE-RSA-AES256-GCM-SHA384) id 5D/95-16262-64ED0EA5; Wed, 25 Apr 2018 16:00:06 -0400 Received: from [127.0.0.1] (helo=sfs-ml-4.v29.lw.sourceforge.com) by sfs-ml-4.v29.lw.sourceforge.com with esmtp (Exim 4.90_1) (envelope-from ) id 1fBQZH-0008Sn-Rz; Wed, 25 Apr 2018 19:59:07 +0000 Received: from [172.30.20.202] (helo=mx.sourceforge.net) by sfs-ml-4.v29.lw.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.90_1) (envelope-from ) id 1fBQZH-0008SY-6W for openvpn-devel@lists.sourceforge.net; Wed, 25 Apr 2018 19:59:07 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sourceforge.net; s=x; h=References:In-Reply-To:Message-Id:Date:Subject:Cc: To:From:Sender:Reply-To:MIME-Version:Content-Type:Content-Transfer-Encoding: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=SofSjai1r/yUWSa5boXgKbCjiaJzE8WKH/RvkBd29mU=; b=bt/ph1Pdd5hFZIG+tLT96p61gg NEd752Nl+0t4ULPqSY32kNdNS3wn8FnIWRvTFB895noNjIzt4kuHeMdo8Rhz/HR3P5DRpOCZXBmk2 jeDJ3upAABsdyipHHuPQcLqfMMqDLN2qCnfICQ+7sRLo02JNs6jr3XSjwOLsIAcHP0co=; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sf.net; s=x ; h=References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To :MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=SofSjai1r/yUWSa5boXgKbCjiaJzE8WKH/RvkBd29mU=; b=I4osvWh+8RVsX25r85dS3PEUvu 2JTn59LgA+Ube/yXgK+z4wUoRsk6w5XYYTzED2pZvRuJza6yhJUcJ8Bveg0BKErbnunMQ89bG9i0t qK/LO1GaSxFcI8+zWDecHKMsxe/IqxBlZJoSnFLAcIYyRCSWhMbMOq88tth2uantjO/Y=; Received: from s2.neomailbox.net ([5.148.176.60]) by sfi-mx-1.v28.lw.sourceforge.com with esmtps (TLSv1.2:DHE-RSA-AES256-GCM-SHA384:256) (Exim 4.90_1) id 1fBQZD-009L9U-MB for openvpn-devel@lists.sourceforge.net; Wed, 25 Apr 2018 19:59:07 +0000 From: Antonio Quartulli To: openvpn-devel@lists.sourceforge.net Date: Thu, 26 Apr 2018 03:57:21 +0800 Message-Id: <20180425195722.20744-8-a@unstable.cc> In-Reply-To: <20180425195722.20744-1-a@unstable.cc> References: <20180425195722.20744-1-a@unstable.cc> X-Spam-Report: Spam Filtering performed by mx.sourceforge.net. See http://spamassassin.org/tag/ for more details. -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [5.148.176.60 listed in list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record X-Headers-End: 1fBQZD-009L9U-MB Subject: [Openvpn-devel] [RFC 7/8] allow user to specify 'local' multiple times in config files X-BeenThere: openvpn-devel@lists.sourceforge.net X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Antonio Quartulli MIME-Version: 1.0 Errors-To: openvpn-devel-bounces@lists.sourceforge.net X-getmail-retrieved-from-mailbox: Inbox It is now possible to specify 'local' multiple times in a server config to let it listen on multiple sockets (address:port) of the same protocol. Signed-off-by: Antonio Quartulli --- doc/openvpn.8 | 10 ++- src/openvpn/init.c | 9 +-- src/openvpn/options.c | 162 ++++++++++++++++++++++++++++++++++-------- src/openvpn/options.h | 15 +++- src/openvpn/socket.c | 18 +++-- 5 files changed, 174 insertions(+), 40 deletions(-) diff --git a/doc/openvpn.8 b/doc/openvpn.8 index 4114f408..658bda23 100644 --- a/doc/openvpn.8 +++ b/doc/openvpn.8 @@ -201,10 +201,16 @@ a new mode ("server") which implements a multi\-client server capability. .\"********************************************************* .TP -.B \-\-local host -Local host name or IP address for bind. +.B \-\-local host|* [port] +Local host name or IP address and port for bind. If specified, OpenVPN will bind to this address only. If unspecified, OpenVPN will bind to all interfaces. +Multiple occurrencies of this option are allowed on +a server, where it will listen on all the configured +ip:port couples. 'ip' can also be '*', where it will +just be treated as 'any host'. +To listen on IPv4 or IPv6 only, it is possible to use +0.0.0.0 or :: respectivaly. .\"********************************************************* .TP .B \-\-remote host [port] [proto] diff --git a/src/openvpn/init.c b/src/openvpn/init.c index a2b474c8..41bc5094 100644 --- a/src/openvpn/init.c +++ b/src/openvpn/init.c @@ -627,7 +627,7 @@ context_init_1(struct context *c) init_connection_list(c); - c->c1.link_sockets_num = 1; + c->c1.link_sockets_num = c->options.ce.local_list->len; do_link_socket_addr_new(c); @@ -3248,8 +3248,8 @@ do_init_socket_1(struct context *c, const int mode) { /* init each socket with its specific port */ link_socket_init_phase1(c->c2.link_sockets[i], - c->options.ce.local, - c->options.ce.local_port, + c->options.ce.local_list->array[i]->local, + c->options.ce.local_list->array[i]->port, c->options.ce.remote, c->options.ce.remote_port, c->c1.dns_cache, @@ -3263,7 +3263,7 @@ do_init_socket_1(struct context *c, const int mode) #ifdef ENABLE_DEBUG c->options.gremlin, #endif - c->options.ce.bind_local, + c->options.ce.local_list->array[i]->bind_local, c->options.ce.remote_float, c->options.inetd, &c->c1.link_socket_addrs[i], @@ -4422,6 +4422,7 @@ inherit_context_child(struct context *dest, if (dest->mode == CM_CHILD_UDP) { ASSERT(!dest->c2.link_sockets); + ASSERT(dest->options.ce.local_list); /* inherit buffers */ dest->c2.buffers = src->c2.buffers; diff --git a/src/openvpn/options.c b/src/openvpn/options.c index 426057ab..884a8f35 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -118,7 +118,13 @@ static const char usage_message[] = "--version : Show copyright and version information.\n" "\n" "Tunnel Options:\n" - "--local host : Local host name or ip address. Implies --bind.\n" + "--local host|* [port] : Local host name or ip address and port. '*' can be used\n" + " as hostname and means 'any host' (openvpn will listen on\n" + " what is returned by the OS). Implies --bind.\n" + " 0.0.0.0 or :: can be used to specifically open a socket\n" + " listening on any IPv4 or IPv6 address respectively.\n" + " The user can specify multiple --local entries to have\n" + " a server listen on multiple sockets at the same time.\n" "--remote host [port] : Remote host name or ip address.\n" "--remote-random : If multiple --remote options specified, choose one randomly.\n" "--remote-random-hostname : Add a random string to remote DNS name.\n" @@ -963,8 +969,9 @@ setenv_connection_entry(struct env_set *es, const int i) { setenv_str_i(es, "proto", proto2ascii(e->proto, e->af, false), i); - setenv_str_i(es, "local", e->local, i); - setenv_str_i(es, "local_port", e->local_port, i); + /* expected to befor single socket contexts only */ + setenv_str_i(es, "local", e->local_list->array[0]->local, i); + setenv_str_i(es, "local_port", e->local_list->array[0]->port, i); setenv_str_i(es, "remote", e->remote, i); setenv_str_i(es, "remote_port", e->remote_port, i); @@ -1472,8 +1479,12 @@ static void show_connection_entry(const struct connection_entry *o) { msg(D_SHOW_PARMS, " proto = %s", proto2ascii(o->proto, o->af, false)); - SHOW_STR(local); - SHOW_STR(local_port); + msg(D_SHOW_PARMS, " Local Sockets:"); + for (int i = 0; i < o->local_list->len; i++) + { + msg(D_SHOW_PARMS, " [%s]:%s", o->local_list->array[i]->local, + o->local_list->array[i]->port); + } SHOW_STR(remote); SHOW_STR(remote_port); SHOW_BOOL(remote_float); @@ -1908,6 +1919,37 @@ options_postprocess_http_proxy_override(struct options *o) #endif /* ifdef ENABLE_MANAGEMENT */ +static struct local_list * +alloc_local_list_if_undef(struct connection_entry *ce, struct gc_arena *gc) +{ + if (!ce->local_list) + { + ALLOC_OBJ_CLEAR_GC(ce->local_list, struct local_list, gc); + } + return ce->local_list; +} + +static struct local_entry * +alloc_local_entry(struct connection_entry *ce, const int msglevel, + struct gc_arena *gc) +{ + struct local_list *l = alloc_local_list_if_undef(ce, gc); + struct local_entry *e; + + if (l->len >= CONNECTION_LIST_SIZE) + { + msg(msglevel, "Maximum number of 'local' options (%d) exceeded", + CONNECTION_LIST_SIZE); + + return NULL; + } + + ALLOC_OBJ_CLEAR_GC(e, struct local_entry, gc); + l->array[l->len++] = e; + + return e; +} + static struct connection_list * alloc_connection_list_if_undef(struct options *options) { @@ -2053,11 +2095,19 @@ options_postprocess_verify_ce(const struct options *options, const struct connec msg(M_USAGE, "only one of --daemon or --inetd may be specified"); } - if (options->inetd && (ce->local || ce->remote)) + if (options->inetd && (ce->local_list->len > 1 + || ce->local_list->array[0]->local + || strcmp(ce->local_list->array[0]->local, "*") + || ce->remote)) { msg(M_USAGE, "--local or --remote cannot be used with --inetd"); } + if (ce->remote && ce->local_list->len > 1) + { + msg(M_USAGE, "multiple --local do not make sense in Client mode"); + } + if (options->inetd && ce->proto == PROTO_TCP_CLIENT) { msg(M_USAGE, "--proto tcp-client cannot be used with --inetd"); @@ -2109,25 +2159,12 @@ options_postprocess_verify_ce(const struct options *options, const struct connec * Sanity check on --local, --remote, and --ifconfig */ - if (proto_is_net(ce->proto) - && string_defined_equal(ce->local, ce->remote) - && string_defined_equal(ce->local_port, ce->remote_port)) - { - msg(M_USAGE, "--remote and --local addresses are the same"); - } - if (string_defined_equal(ce->remote, options->ifconfig_local) || string_defined_equal(ce->remote, options->ifconfig_remote_netmask)) { msg(M_USAGE, "--local and --remote addresses must be distinct from --ifconfig addresses"); } - if (string_defined_equal(ce->local, options->ifconfig_local) - || string_defined_equal(ce->local, options->ifconfig_remote_netmask)) - { - msg(M_USAGE, "--local addresses must be distinct from --ifconfig addresses"); - } - if (string_defined_equal(options->ifconfig_local, options->ifconfig_remote_netmask)) { msg(M_USAGE, "local and remote/netmask --ifconfig addresses must be different"); @@ -2138,11 +2175,6 @@ options_postprocess_verify_ce(const struct options *options, const struct connec msg(M_USAGE, "--bind and --nobind can't be used together"); } - if (ce->local && !ce->bind_local) - { - msg(M_USAGE, "--local and --nobind don't make sense when used together"); - } - if (ce->local_port_defined && !ce->bind_local) { msg(M_USAGE, "--lport and --nobind don't make sense when used together"); @@ -2153,6 +2185,29 @@ options_postprocess_verify_ce(const struct options *options, const struct connec msg(M_USAGE, "--nobind doesn't make sense unless used with --remote"); } + for (int i = 0; i < ce->local_list->len; i++) + { + struct local_entry *le = ce->local_list->array[i]; + + if (proto_is_net(ce->proto) + && string_defined_equal(le->local, ce->remote) + && string_defined_equal(le->port, ce->remote_port)) + { + msg(M_USAGE, "--remote and a --local addresses are the same"); + } + + if (string_defined_equal(le->local, options->ifconfig_local) + || string_defined_equal(le->local, options->ifconfig_remote_netmask)) + { + msg(M_USAGE, "--local addresses must be distinct from --ifconfig addresses"); + } + + if (le->local && !ce->bind_local) + { + msg(M_USAGE, "--local and --nobind don't make sense when used together"); + } + } + /* * Check for consistency of management options */ @@ -2817,12 +2872,12 @@ options_postprocess_mutate_ce(struct options *o, struct connection_entry *ce) } #endif - if (ce->proto == PROTO_TCP_CLIENT && !ce->local && !ce->local_port_defined && !ce->bind_defined) + if (ce->proto == PROTO_TCP_CLIENT && !ce->local_list && !ce->local_port_defined && !ce->bind_defined) { ce->bind_local = false; } - if (ce->proto == PROTO_UDP && ce->socks_proxy_server && !ce->local && !ce->local_port_defined && !ce->bind_defined) + if (ce->proto == PROTO_UDP && ce->socks_proxy_server && !ce->local_list && !ce->local_port_defined && !ce->bind_defined) { ce->bind_local = false; } @@ -2868,7 +2923,16 @@ options_postprocess_mutate_ce(struct options *o, struct connection_entry *ce) ce->tun_mtu_extra = TAP_MTU_EXTRA_DEFAULT; } } +} +static void +options_postprocess_mutate_le(struct options *o, struct local_entry *le) +{ + /* use the global port if none is specified */ + if (!le->port) + { + le->port = o->ce.local_port; + } } #ifdef _WIN32 @@ -3019,6 +3083,29 @@ options_postprocess_mutate(struct options *o) options_postprocess_mutate_ce(o, o->connection_list->array[i]); } + if (o->ce.local_list) + { + for (i = 0; i < o->ce.local_list->len; i++) + { + options_postprocess_mutate_le(o, o->ce.local_list->array[i]); + } + } + else + { + /* if no 'local' directive was specified, convert the global port + * setting to a listen entry */ + struct local_entry *e = alloc_local_entry(&o->ce, M_USAGE, &o->gc); + ASSERT(e); + e->port = o->ce.local_port; + e->bind_local = o->ce.bind_local; + } + + /* use the same listen list for every outgoing connection */ + for (i = 0; i < o->connection_list->len; ++i) + { + o->connection_list->array[i]->local_list = o->ce.local_list; + } + if (o->tls_server) { /* Check that DH file is specified, or explicitly disabled */ @@ -5267,10 +5354,29 @@ add_option(struct options *options, VERIFY_PERMISSION(OPT_P_UP); options->ifconfig_nowarn = true; } - else if (streq(p[0], "local") && p[1] && !p[2]) + else if (streq(p[0], "local") && p[1] && !p[3]) { + struct local_entry *e; + VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_CONNECTION); - options->ce.local = p[1]; + + e = alloc_local_entry(&options->ce, M_USAGE, &options->gc); + ASSERT(e); + + /* '*' is treated as 'ask the system to get some socket', + * therefore force binding on a particular address only when + * actually specified. */ + if (strcmp(p[1], "*") != 0) + { + e->local = p[1]; + e->bind_local = true; + } + + if (p[2]) + { + e->port = p[2]; + e->bind_local = true; + } } else if (streq(p[0], "remote-random") && !p[1]) { diff --git a/src/openvpn/options.h b/src/openvpn/options.h index f7d0145a..fb8c32fd 100644 --- a/src/openvpn/options.h +++ b/src/openvpn/options.h @@ -83,14 +83,21 @@ struct options_pre_pull #error "At least one of OpenSSL or mbed TLS needs to be defined." #endif +struct local_entry +{ + const char *local; + const char *port; + bool bind_local; +}; + struct connection_entry { + struct local_list *local_list; int proto; sa_family_t af; const char *local_port; bool local_port_defined; const char *remote_port; - const char *local; const char *remote; bool remote_float; bool bind_defined; @@ -142,6 +149,12 @@ struct remote_entry #define CONNECTION_LIST_SIZE 64 +struct local_list +{ + int len; + struct local_entry *array[CONNECTION_LIST_SIZE]; +}; + struct connection_list { int len; diff --git a/src/openvpn/socket.c b/src/openvpn/socket.c index 79fbc6a8..9d961347 100644 --- a/src/openvpn/socket.c +++ b/src/openvpn/socket.c @@ -232,7 +232,7 @@ do_preresolve_host(struct context *c, void do_preresolve(struct context *c) { - int i; + int i, j; struct connection_list *l = c->options.connection_list; const unsigned int preresolve_flags = GETADDR_RESOLVE |GETADDR_UPDATE_MANAGEMENT_STATE @@ -305,11 +305,19 @@ do_preresolve(struct context *c) } } - if (ce->bind_local) + flags |= GETADDR_PASSIVE; + flags &= ~GETADDR_RANDOMIZE; + + for (j = 0; j < ce->local_list->len; j++) { - flags |= GETADDR_PASSIVE; - flags &= ~GETADDR_RANDOMIZE; - status = do_preresolve_host(c, ce->local, ce->local_port, ce->af, flags); + struct local_entry *le = ce->local_list->array[j]; + + if (!le->local) + { + continue; + } + + status = do_preresolve_host(c, le->local, le->port, ce->af, flags); if (status != 0) { goto err;