Skip to content

Commit 8466c2c

Browse files
ordexcron2
authored andcommitted
allow user to specify 'local' multiple times in config files
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. Change-Id: I4d1c96662c5a8c750d883e3b20adde09529e2764 Signed-off-by: Antonio Quartulli <a@unstable.cc> Signed-off-by: Gianmarco De Gregori <gianmarco@mandelbit.com> Acked-by: Gert Doering <gert@greenie.muc.de> Message-Id: <20250123155111.23371-1-gert@greenie.muc.de> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg30544.html Signed-off-by: Gert Doering <gert@greenie.muc.de>
1 parent 5cea4bc commit 8466c2c

File tree

5 files changed

+183
-45
lines changed

5 files changed

+183
-45
lines changed

doc/man-sections/link-options.rst

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,13 +106,22 @@ the local and the remote host.
106106
is not reliable. It is recommended to set tun-mtu with enough headroom
107107
instead.
108108

109-
--local host
110-
Local host name or IP address for bind. If specified, OpenVPN will bind
111-
to this address only. If unspecified, OpenVPN will bind to all
112-
interfaces.
109+
--local host|* [port]
110+
Local host name or IP address and port for bind. If specified, OpenVPN will bind
111+
to this address. If unspecified, OpenVPN will bind to all interfaces.
112+
'*' can be used as hostname and means 'any host' (OpenVPN will listen on what
113+
is returned by the OS).
114+
On a client, or in point-to-point mode, this can only be specified once (1 socket).
115+
116+
On an OpenVPN setup running as ``--server``, this can be specified multiple times
117+
to open multiple listening sockets on different addresses and/or different ports.
118+
In order to specify multiple listen ports without specifying an address, use '*'
119+
to signal "use what the operating system gives you as default", for
120+
"all IPv4 addresses" use "0.0.0.0", for "all IPv6 addresses" use '::'.
121+
``--local`` implies ``--bind``.
113122

114123
--lport port
115-
Set local TCP/UDP port number or name. Cannot be used together with
124+
Set default TCP/UDP port number. Cannot be used together with
116125
``--nobind`` option.
117126

118127
--mark value

src/openvpn/init.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -751,7 +751,7 @@ context_init_1(struct context *c)
751751

752752
init_connection_list(c);
753753

754-
c->c1.link_sockets_num = 1;
754+
c->c1.link_sockets_num = c->options.ce.local_list->len;
755755

756756
do_link_socket_addr_new(c);
757757

@@ -4978,6 +4978,7 @@ inherit_context_child(struct context *dest,
49784978
if (dest->mode == CM_CHILD_UDP)
49794979
{
49804980
ASSERT(!dest->c2.link_sockets);
4981+
ASSERT(dest->options.ce.local_list);
49814982

49824983
/* inherit buffers */
49834984
dest->c2.buffers = src->c2.buffers;

src/openvpn/options.c

Lines changed: 135 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,17 @@ static const char usage_message[] =
124124
"--version : Show copyright and version information.\n"
125125
"\n"
126126
"Tunnel Options:\n"
127-
"--local host : Local host name or ip address. Implies --bind.\n"
127+
"--local host|* [port]: Local host name or IP address and port for bind.\n"
128+
" If specified, OpenVPN will bindto this address. If unspecified,\n"
129+
" OpenVPN will bind to all interfaces. '*' can be used as hostname\n"
130+
" and means 'any host' (OpenVPN will listen on what is returned by the OS).\n"
131+
" On a client, or in point-to-point mode, this can only be specified once (1 socket).\n"
132+
" On an OpenVPN setup running as ``--server``, this can be specified multiple times\n"
133+
" to open multiple listening sockets on different addresses and/or different ports.\n"
134+
" In order to specify multiple listen ports without specifying an address, use '*'\n"
135+
" to signal 'use what the operating system gives you as default', for\n"
136+
" 'all IPv4 addresses' use '0.0.0.0', for 'all IPv6 addresses' use '::'.\n"
137+
" ``--local`` implies ``--bind``.\n"
128138
"--remote host [port] : Remote host name or ip address.\n"
129139
"--remote-random : If multiple --remote options specified, choose one randomly.\n"
130140
"--remote-random-hostname : Add a random string to remote DNS name.\n"
@@ -988,8 +998,9 @@ setenv_connection_entry(struct env_set *es,
988998
const int i)
989999
{
9901000
setenv_str_i(es, "proto", proto2ascii(e->proto, e->af, false), i);
991-
setenv_str_i(es, "local", e->local, i);
992-
setenv_str_i(es, "local_port", e->local_port, i);
1001+
/* expected to be for single socket contexts only */
1002+
setenv_str_i(es, "local", e->local_list->array[0]->local, i);
1003+
setenv_str_i(es, "local_port", e->local_list->array[0]->port, i);
9931004
setenv_str_i(es, "remote", e->remote, i);
9941005
setenv_str_i(es, "remote_port", e->remote_port, i);
9951006

@@ -1713,8 +1724,12 @@ static void
17131724
show_connection_entry(const struct connection_entry *o)
17141725
{
17151726
msg(D_SHOW_PARMS, " proto = %s", proto2ascii(o->proto, o->af, false));
1716-
SHOW_STR(local);
1717-
SHOW_STR(local_port);
1727+
msg(D_SHOW_PARMS, " Local Sockets:");
1728+
for (int i = 0; i < o->local_list->len; i++)
1729+
{
1730+
msg(D_SHOW_PARMS, " [%s]:%s", o->local_list->array[i]->local,
1731+
o->local_list->array[i]->port);
1732+
}
17181733
SHOW_STR(remote);
17191734
SHOW_STR(remote_port);
17201735
SHOW_BOOL(remote_float);
@@ -2162,6 +2177,37 @@ options_postprocess_http_proxy_override(struct options *o)
21622177

21632178
#endif /* ifdef ENABLE_MANAGEMENT */
21642179

2180+
static struct local_list *
2181+
alloc_local_list_if_undef(struct connection_entry *ce, struct gc_arena *gc)
2182+
{
2183+
if (!ce->local_list)
2184+
{
2185+
ALLOC_OBJ_CLEAR_GC(ce->local_list, struct local_list, gc);
2186+
}
2187+
return ce->local_list;
2188+
}
2189+
2190+
static struct local_entry *
2191+
alloc_local_entry(struct connection_entry *ce, const int msglevel,
2192+
struct gc_arena *gc)
2193+
{
2194+
struct local_list *l = alloc_local_list_if_undef(ce, gc);
2195+
struct local_entry *e;
2196+
2197+
if (l->len >= CONNECTION_LIST_SIZE)
2198+
{
2199+
msg(msglevel, "Maximum number of 'local' options (%d) exceeded",
2200+
CONNECTION_LIST_SIZE);
2201+
2202+
return NULL;
2203+
}
2204+
2205+
ALLOC_OBJ_CLEAR_GC(e, struct local_entry, gc);
2206+
l->array[l->len++] = e;
2207+
2208+
return e;
2209+
}
2210+
21652211
static struct connection_list *
21662212
alloc_connection_list_if_undef(struct options *options)
21672213
{
@@ -2354,6 +2400,15 @@ options_postprocess_verify_ce(const struct options *options,
23542400
"--proto tcp-server or --proto tcp-client");
23552401
}
23562402

2403+
/*
2404+
* Sanity check on Client mode
2405+
*/
2406+
2407+
if (options->mode != MODE_SERVER && ce->local_list->len > 1)
2408+
{
2409+
msg(M_USAGE, "multiple --local statements only allowed in --server mode");
2410+
}
2411+
23572412
if (options->lladdr && dev != DEV_TYPE_TAP)
23582413
{
23592414
msg(M_USAGE, "--lladdr can only be used in --dev tap mode");
@@ -2379,13 +2434,6 @@ options_postprocess_verify_ce(const struct options *options,
23792434
* Sanity check on --local, --remote, and --ifconfig
23802435
*/
23812436

2382-
if (proto_is_net(ce->proto)
2383-
&& string_defined_equal(ce->local, ce->remote)
2384-
&& string_defined_equal(ce->local_port, ce->remote_port))
2385-
{
2386-
msg(M_USAGE, "--remote and --local addresses are the same");
2387-
}
2388-
23892437
if (string_defined_equal(ce->remote, options->ifconfig_local)
23902438
|| string_defined_equal(ce->remote, options->ifconfig_remote_netmask))
23912439
{
@@ -2394,13 +2442,6 @@ options_postprocess_verify_ce(const struct options *options,
23942442
"addresses");
23952443
}
23962444

2397-
if (string_defined_equal(ce->local, options->ifconfig_local)
2398-
|| string_defined_equal(ce->local, options->ifconfig_remote_netmask))
2399-
{
2400-
msg(M_USAGE,
2401-
"--local addresses must be distinct from --ifconfig addresses");
2402-
}
2403-
24042445
if (string_defined_equal(options->ifconfig_local,
24052446
options->ifconfig_remote_netmask))
24062447
{
@@ -2413,12 +2454,6 @@ options_postprocess_verify_ce(const struct options *options,
24132454
msg(M_USAGE, "--bind and --nobind can't be used together");
24142455
}
24152456

2416-
if (ce->local && !ce->bind_local)
2417-
{
2418-
msg(M_USAGE,
2419-
"--local and --nobind don't make sense when used together");
2420-
}
2421-
24222457
if (ce->local_port_defined && !ce->bind_local)
24232458
{
24242459
msg(M_USAGE,
@@ -2430,6 +2465,29 @@ options_postprocess_verify_ce(const struct options *options,
24302465
msg(M_USAGE, "--nobind doesn't make sense unless used with --remote");
24312466
}
24322467

2468+
for (int i = 0; i < ce->local_list->len; i++)
2469+
{
2470+
struct local_entry *le = ce->local_list->array[i];
2471+
2472+
if (proto_is_net(ce->proto)
2473+
&& string_defined_equal(le->local, ce->remote)
2474+
&& string_defined_equal(le->port, ce->remote_port))
2475+
{
2476+
msg(M_USAGE, "--remote and one of the --local addresses are the same");
2477+
}
2478+
2479+
if (string_defined_equal(le->local, options->ifconfig_local)
2480+
|| string_defined_equal(le->local, options->ifconfig_remote_netmask))
2481+
{
2482+
msg(M_USAGE, "--local addresses must be distinct from --ifconfig addresses");
2483+
}
2484+
2485+
if (le->local && !ce->bind_local)
2486+
{
2487+
msg(M_USAGE, "--local and --nobind don't make sense when used together");
2488+
}
2489+
}
2490+
24332491
/*
24342492
* Check for consistency of management options
24352493
*/
@@ -3128,7 +3186,7 @@ options_postprocess_mutate_ce(struct options *o, struct connection_entry *ce)
31283186
}
31293187

31303188
/* an option is present that requires local bind to enabled */
3131-
bool need_bind = ce->local || ce->local_port_defined || ce->bind_defined;
3189+
bool need_bind = ce->local_port_defined || ce->bind_defined || ce->local_list;
31323190

31333191
/* socks proxy is enabled */
31343192
bool uses_socks = ce->proto == PROTO_UDP && ce->socks_proxy_server;
@@ -3264,6 +3322,16 @@ options_postprocess_mutate_ce(struct options *o, struct connection_entry *ce)
32643322
}
32653323
}
32663324

3325+
static void
3326+
options_postprocess_mutate_le(struct connection_entry ce, struct local_entry *le)
3327+
{
3328+
/* use the global port if none is specified */
3329+
if (!le->port)
3330+
{
3331+
le->port = ce.local_port;
3332+
}
3333+
}
3334+
32673335
#ifdef _WIN32
32683336
/* If iservice is in use, we need def1 method for redirect-gateway */
32693337
static void
@@ -3705,6 +3773,28 @@ options_postprocess_mutate(struct options *o, struct env_set *es)
37053773
options_postprocess_mutate_ce(o, o->connection_list->array[i]);
37063774
}
37073775

3776+
if (o->ce.local_list)
3777+
{
3778+
for (i = 0; i < o->ce.local_list->len; i++)
3779+
{
3780+
options_postprocess_mutate_le(o->ce, o->ce.local_list->array[i]);
3781+
}
3782+
}
3783+
else
3784+
{
3785+
/* if no 'local' directive was specified, convert the global port
3786+
* setting to a listen entry */
3787+
struct local_entry *e = alloc_local_entry(&o->ce, M_USAGE, &o->gc);
3788+
ASSERT(e);
3789+
e->port = o->ce.local_port;
3790+
}
3791+
3792+
/* use the same listen list for every outgoing connection */
3793+
for (i = 0; i < o->connection_list->len; ++i)
3794+
{
3795+
o->connection_list->array[i]->local_list = o->ce.local_list;
3796+
}
3797+
37083798
if (o->tls_server)
37093799
{
37103800
/* Check that DH file is specified, or explicitly disabled */
@@ -6100,10 +6190,27 @@ add_option(struct options *options,
61006190
VERIFY_PERMISSION(OPT_P_UP);
61016191
options->ifconfig_nowarn = true;
61026192
}
6103-
else if (streq(p[0], "local") && p[1] && !p[2])
6193+
else if (streq(p[0], "local") && p[1] && !p[3])
61046194
{
6195+
struct local_entry *e;
6196+
61056197
VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_CONNECTION);
6106-
options->ce.local = p[1];
6198+
6199+
e = alloc_local_entry(&options->ce, M_USAGE, &options->gc);
6200+
ASSERT(e);
6201+
6202+
/* '*' is treated as 'ask the system to get some socket',
6203+
* therefore force binding on a particular address only when
6204+
* actually specified. */
6205+
if (strcmp(p[1], "*") != 0)
6206+
{
6207+
e->local = p[1];
6208+
}
6209+
6210+
if (p[2])
6211+
{
6212+
e->port = p[2];
6213+
}
61076214
}
61086215
else if (streq(p[0], "remote-random") && !p[1])
61096216
{

src/openvpn/options.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,14 +94,20 @@ struct options_pre_connect
9494
#error "At least one of OpenSSL or mbed TLS needs to be defined."
9595
#endif
9696

97+
struct local_entry
98+
{
99+
const char *local;
100+
const char *port;
101+
};
102+
97103
struct connection_entry
98104
{
105+
struct local_list *local_list;
99106
int proto;
100107
sa_family_t af;
101108
const char *local_port;
102109
bool local_port_defined;
103110
const char *remote_port;
104-
const char *local;
105111
const char *remote;
106112
bool remote_float;
107113
bool bind_defined;
@@ -179,6 +185,12 @@ struct remote_entry
179185

180186
#define CONNECTION_LIST_SIZE 64
181187

188+
struct local_list
189+
{
190+
int len;
191+
struct local_entry *array[CONNECTION_LIST_SIZE];
192+
};
193+
182194
struct connection_list
183195
{
184196
int capacity;

src/openvpn/socket.c

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -342,21 +342,20 @@ do_preresolve_host(struct context *c,
342342
void
343343
do_preresolve(struct context *c)
344344
{
345-
int i;
346345
struct connection_list *l = c->options.connection_list;
347346
const unsigned int preresolve_flags = GETADDR_RESOLVE
348347
|GETADDR_UPDATE_MANAGEMENT_STATE
349348
|GETADDR_MENTION_RESOLVE_RETRY
350349
|GETADDR_FATAL;
351350

352351

353-
for (i = 0; i < l->len; ++i)
352+
for (int i = 0; i < l->len; ++i)
354353
{
355354
int status;
356355
const char *remote;
357356
int flags = preresolve_flags;
358357

359-
struct connection_entry *ce = c->options.connection_list->array[i];
358+
struct connection_entry *ce = l->array[i];
360359

361360
if (proto_is_dgram(ce->proto))
362361
{
@@ -420,13 +419,23 @@ do_preresolve(struct context *c)
420419
{
421420
flags |= GETADDR_PASSIVE;
422421
flags &= ~GETADDR_RANDOMIZE;
423-
status = do_preresolve_host(c, ce->local, ce->local_port,
424-
ce->af, flags);
425-
if (status != 0)
422+
423+
for (int j = 0; j < ce->local_list->len; j++)
426424
{
427-
goto err;
428-
}
425+
struct local_entry *le = ce->local_list->array[j];
429426

427+
if (!le->local)
428+
{
429+
continue;
430+
}
431+
432+
status = do_preresolve_host(c, le->local, le->port, ce->af, flags);
433+
if (status != 0)
434+
{
435+
goto err;
436+
}
437+
438+
}
430439
}
431440

432441
}
@@ -1874,8 +1883,8 @@ link_socket_init_phase1(struct context *c, int sock_index, int mode)
18741883
const char *remote_host = o->ce.remote;
18751884
const char *remote_port = o->ce.remote_port;
18761885

1877-
sock->local_host = o->ce.local;
1878-
sock->local_port = o->ce.local_port;
1886+
sock->local_host = o->ce.local_list->array[sock_index]->local;
1887+
sock->local_port = o->ce.local_list->array[sock_index]->port;
18791888
sock->remote_host = remote_host;
18801889
sock->remote_port = remote_port;
18811890
sock->dns_cache = c->c1.dns_cache;

0 commit comments

Comments
 (0)