Skip to contentSkip to conversation

server: support listening on multiple ports/IPs

RFC New Antonio Quartulli 1 participant 9 messages Apr 25, 2018

lore
Share & tools
lore
#0 lore raw

cover server: support listening on multiple ports/IPs

This patchset allows a server to listen on multiple sockets concurrently.

Practically this means that an admin can configure his/her server to listen on multiple IPs and/or multiple ports at the same time.

The new behaviour can be configured by using an extension of the current "--local" option.
In particular, the new directive syntax is "--local IP [port]" and it can appear multiple times in the same server config. Each occurrence represents a single socket.

"IP" can be an IPv4, IPv6, an hostname or *. Multiple "--local" directives can combine all of the above.

The special case "*" will mimic what currently happens on a server when no "--local" is specified, but in this case it gives a chance to specify a port.

The special addresses 0.0.0.0 and :: are also allowed and they can be used to explicitly bind only IPv4 o v6.

If no port is specified, the value coming from "lport" is used (1194 by default).

At the moment the protocol cannot be customized and the one specified by the "--proto" directive will always be used.
Future development will aim at making the protocol part of the "--local" directive and finally have multi protocol support on the same instance.

This patchset is an RFC because it requires feedback and testing, especially when used in client mode with all kind of weird options.

It currently passes "make check" and our "internal buildbot" tests.

Interested users can also get this code branch from:
https://github.com/ordex/openvpn/tree/multiport

Cheers,

Antonio Quartulli (8):
event/multi: add event_arg object to make event handling more generic pass link_socket object to i/o functions
io_work: convert shift argument to uintptr_t
io_work: pass event_arg object to event handler in case of socket
event
allow tcp/udp server to listen on multiple ports at the same time if a local IPv6 address is provided, socket must be v6-only allow user to specify 'local' multiple times in config files override ai_family if 'local' numeric address was specified

 doc/openvpn.8                |  10 +-
 src/openvpn/event.h          |  22 ++++
 src/openvpn/forward-inline.h |  15 ++-
 src/openvpn/forward.c        | 137 +++++++++++++++---------
 src/openvpn/forward.h        |  35 ++++---
 src/openvpn/init.c           | 198 +++++++++++++++++++++++------------
 src/openvpn/init.h           |   3 +-
 src/openvpn/mtcp.c           | 113 ++++++++++++--------
 src/openvpn/mudp.c           |  19 +++-
 src/openvpn/mudp.h           |   6 +-
 src/openvpn/multi.c          |  27 +++--
 src/openvpn/multi.h          |  15 ++-
 src/openvpn/openvpn.c        |   2 +-
 src/openvpn/openvpn.h        |   9 +-
 src/openvpn/options.c        | 162 +++++++++++++++++++++++-----
 src/openvpn/options.h        |  15 ++-
 src/openvpn/ping-inline.h    |   2 +-
 src/openvpn/socket.c         |  55 ++++++++--
 src/openvpn/socket.h         |   8 +-
 19 files changed, 602 insertions(+), 251 deletions(-)
Signature · 1 line
2.17.0
#1 lore raw

RFC 1/8 event/multi: add event_arg object to make event handling more generic

In order to prepare the event handling code to deal with multiple listening sockets, we have to make sure that it is possible to distinguish which of these sockets have been pocked by an incoming connection request.

To achieve that, this patch changes the object being passed as event handler argument, from a "partly integer evaluated variable" to a full struct with a proper type attribute.

This struct will allow the code to carry around the particular listening socket where the connection is being established.

This change affects the TCP server code path only as UDP servers use only one socket to handle all clients i/o.

---
 src/openvpn/event.h  | 14 ++++++++++
 src/openvpn/mtcp.c   | 62 +++++++++++++++++++++++++-------------------
 src/openvpn/multi.c  |  3 +++
 src/openvpn/multi.h  |  1 +
 src/openvpn/socket.c |  2 ++
 src/openvpn/socket.h |  2 ++
 6 files changed, 58 insertions(+), 26 deletions(-)
src/openvpn/event.h +14 -0
index 4af6371e..33ff8890 100644
@@ -85,6 +85,20 @@ struct event_set
     struct event_set_functions func;
 };
 
+typedef enum {
+    EVENT_ARG_MULTI_INSTANCE = 0,
+    EVENT_ARG_LINK_SOCKET,
+} event_arg_t;
+
+/* generic event argument object to pass to event_ctl() */
+struct event_arg
+{
+    event_arg_t type;
+    union {
+        struct multi_instance *mi; /* if type = EVENT_ARG_MULTI_INSTANCE */
+    } u;
+};
+
 /*
  * maxevents on input:  desired max number of event_t descriptors
  *                      simultaneously set with event_ctl
src/openvpn/mtcp.c +36 -26

96 lines not on this page · raw message

src/openvpn/multi.c +3 -0
index 3da8c110..f877c060 100644
@@ -841,6 +841,9 @@ multi_create_instance(struct multi_context *m, const struct mroute_addr *real)
         goto err;
     }
 
+    mi->ev_arg.type = EVENT_ARG_MULTI_INSTANCE;
+    mi->ev_arg.u.mi = mi;
+
     perf_pop();
     gc_free(&gc);
     return mi;
src/openvpn/multi.h +1 -0
index d7e5c298..c52445ed 100644
@@ -75,6 +75,7 @@ struct deferred_signal_schedule_entry
  */
 struct multi_instance {
     struct schedule_entry se;  /* this must be the first element of the structure */
+    struct event_arg ev_arg;
     struct gc_arena gc;
     bool defined;
     bool halt;
src/openvpn/socket.c +2 -0
index 211e7441..e0620f09 100644
@@ -1712,6 +1712,8 @@ link_socket_new(void)
     ALLOC_OBJ_CLEAR(sock, struct link_socket);
     sock->sd = SOCKET_UNDEFINED;
     sock->ctrl_sd = SOCKET_UNDEFINED;
+    sock->ev_arg.type = EVENT_ARG_LINK_SOCKET;
+
     return sock;
 }
 
src/openvpn/socket.h +2 -0
index 479d1150..46000246 100644
@@ -165,6 +165,8 @@ struct link_socket
 {
     struct link_socket_info info;
 
+    struct event_arg ev_arg;
+
     socket_descriptor_t sd;
     socket_descriptor_t ctrl_sd; /* only used for UDP over Socks */
 
Signature · 1 line
2.17.0
#2 lore raw

RFC 2/8 pass link_socket object to i/o functions

In reply to Antonio Quartulli

In order to prepare the code to work with distinct sockets, it is essential that i/o functions do not operate on any hard-coded socket object (i.e. c->c2.link_socket).

This patch changes all the low-level i/o functionis to work with a socket specified as argument rather than a fixed one.

---
 src/openvpn/event.h   |  1 +
 src/openvpn/forward.c | 49 ++++++++++++++++++++++---------------------
 src/openvpn/forward.h | 17 ++++++++-------
 src/openvpn/mtcp.c    |  2 +-
 src/openvpn/mudp.c    |  2 +-
 src/openvpn/multi.h   |  2 +-
 src/openvpn/openvpn.c |  2 +-
 src/openvpn/socket.c  |  1 +
 8 files changed, 41 insertions(+), 35 deletions(-)
src/openvpn/event.h +1 -0
index 33ff8890..be199ad9 100644
@@ -96,6 +96,7 @@ struct event_arg
     event_arg_t type;
     union {
         struct multi_instance *mi; /* if type = EVENT_ARG_MULTI_INSTANCE */
+        struct link_socket *ls; /* if type = EVENT_ARG_LINK_SOCKET */
     } u;
 };
 
src/openvpn/forward.c +25 -24

154 lines not on this page · raw message

src/openvpn/forward.h +10 -7
index 924cc5e2..16639f75 100644
@@ -63,7 +63,8 @@
 
 void pre_select(struct context *c);
 
-void process_io(struct context *c);
+void process_io(struct context *ci, struct link_socket *ls);
+
 
 const char *wait_status_string(struct context *c, struct gc_arena *gc);
 
@@ -124,10 +125,11 @@ int get_server_poll_remaining_time(struct event_timeout *server_poll_timeout);
  * context associated with the appropriate VPN tunnel for which data is
  * available to be read.
  *
- * @param c - The context structure which contains the external
- *     network socket from which to read incoming packets.
+ * @param c    The context structure which contains the external
+ *             network socket from which to read incoming packets.
+ * @param ls   The socket where the packet can be read from.
  */
-void read_incoming_link(struct context *c);
+void read_incoming_link(struct context *c, struct link_socket *ls);
 
 /**
  * Starts processing a packet read from the external network interface.
@@ -193,10 +195,11 @@ void process_incoming_link_part2(struct context *c, struct link_socket_info *lsi
  *
  * If an error occurs, it is logged and the packet is dropped.
  *
- * @param c - The context structure of the VPN tunnel associated with the
- *     packet.
+ * @param c   The context structure of the VPN tunnel associated with the
+ *            packet.
+ * @param ls  The socket to be used to send the packet.
  */
-void process_outgoing_link(struct context *c);
+void process_outgoing_link(struct context *c, struct link_socket *ls);
 
 
 /**************************************************************************/
src/openvpn/mtcp.c +1 -1
index 234c077c..51f339d0 100644
@@ -470,7 +470,7 @@ multi_tcp_dispatch(struct multi_context *m, struct multi_instance *mi, const int
             ASSERT(mi);
             ASSERT(mi->context.c2.link_socket);
             set_prefix(mi);
-            read_incoming_link(&mi->context);
+            read_incoming_link(&mi->context, mi->context.c2.link_socket);
             clear_prefix();
             if (!IS_SIG(&mi->context))
             {
src/openvpn/mudp.c +1 -1
index b3690ab7..80efefb3 100644
@@ -227,7 +227,7 @@ multi_process_io_udp(struct multi_context *m)
     /* Incoming data on UDP port */
     else if (status & SOCKET_READ)
     {
-        read_incoming_link(&m->top);
+        read_incoming_link(&m->top, m->top.c2.link_socket);
         if (!IS_SIG(&m->top))
         {
             multi_process_incoming_link(m, NULL, mpp_flags);
src/openvpn/multi.h +1 -1
index c52445ed..5fb8402f 100644
@@ -637,7 +637,7 @@ multi_process_outgoing_link_dowork(struct multi_context *m, struct multi_instanc
 {
     bool ret = true;
     set_prefix(mi);
-    process_outgoing_link(&mi->context);
+    process_outgoing_link(&mi->context, mi->context.c2.link_socket);
     ret = multi_process_post(m, mi, mpp_flags);
     clear_prefix();
     return ret;
src/openvpn/openvpn.c +1 -1
index b9e914a8..b52063a2 100644
@@ -115,7 +115,7 @@ tunnel_point_to_point(struct context *c)
         }
 
         /* process the I/O which triggered select */
-        process_io(c);
+        process_io(c, c->c2.link_socket);
         P2P_CHECK_SIG();
 
         perf_pop();
src/openvpn/socket.c +1 -0
index e0620f09..564948a2 100644
@@ -1713,6 +1713,7 @@ link_socket_new(void)
     sock->sd = SOCKET_UNDEFINED;
     sock->ctrl_sd = SOCKET_UNDEFINED;
     sock->ev_arg.type = EVENT_ARG_LINK_SOCKET;
+    sock->ev_arg.u.ls = sock;
 
     return sock;
 }
Signature · 1 line
2.17.0
#3 lore raw

RFC 3/8 io_work: convert shift argument to uintptr_t

In reply to Antonio Quartulli

Instead of passing the shift argument as pointer, pass directly its integer value. This will allow the code to distinguish a shift value from a real object pointer, like we already do in multi_tcp_process_io().

This change will allow us later to pass an event_arg object as event handler argument instead of a simple integer value.

---
 src/openvpn/forward.c | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)
src/openvpn/forward.c +10 -10
index c5ddf385..a4e4d98a 100644
@@ -1607,11 +1607,11 @@ io_wait_dowork(struct context *c, const unsigned int flags)
     struct event_set_return esr[4];
 
     /* These shifts all depend on EVENT_READ and EVENT_WRITE */
-    static int socket_shift = 0;   /* depends on SOCKET_READ and SOCKET_WRITE */
-    static int tun_shift = 2;      /* depends on TUN_READ and TUN_WRITE */
-    static int err_shift = 4;      /* depends on ES_ERROR */
+    static uintptr_t socket_shift = 0;   /* depends on SOCKET_READ and SOCKET_WRITE */
+    static uintptr_t tun_shift = 2;      /* depends on TUN_READ and TUN_WRITE */
+    static uintptr_t err_shift = 4;      /* depends on ES_ERROR */
 #ifdef ENABLE_MANAGEMENT
-    static int management_shift = 6; /* depends on MANAGEMENT_READ and MANAGEMENT_WRITE */
+    static uintptr_t management_shift = 6; /* depends on MANAGEMENT_READ and MANAGEMENT_WRITE */
 #endif
 #ifdef ENABLE_ASYNC_PUSH
     static int file_shift = 8;     /* listening inotify events */
@@ -1628,7 +1628,7 @@ io_wait_dowork(struct context *c, const unsigned int flags)
      */
     if (flags & IOW_WAIT_SIGNAL)
     {
-        wait_signal(c->c2.event_set, (void *)&err_shift);
+        wait_signal(c->c2.event_set, (void *)err_shift);
     }
 
     /*
@@ -1714,13 +1714,13 @@ io_wait_dowork(struct context *c, const unsigned int flags)
     /*
      * Configure event wait based on socket, tuntap flags.
      */
-    socket_set(c->c2.link_socket, c->c2.event_set, socket, (void *)&socket_shift, NULL);
-    tun_set(c->c1.tuntap, c->c2.event_set, tuntap, (void *)&tun_shift, NULL);
+    socket_set(c->c2.link_socket, c->c2.event_set, socket, (void *)socket_shift, NULL);
+    tun_set(c->c1.tuntap, c->c2.event_set, tuntap, (void *)tun_shift, NULL);
 
 #ifdef ENABLE_MANAGEMENT
     if (management)
     {
-        management_socket_set(management, c->c2.event_set, (void *)&management_shift, NULL);
+        management_socket_set(management, c->c2.event_set, (void *)management_shift, NULL);
     }
 #endif
 
@@ -1728,7 +1728,7 @@ io_wait_dowork(struct context *c, const unsigned int flags)
     /* arm inotify watcher */
     if (c->options.mode == MODE_SERVER)
     {
-        event_ctl(c->c2.event_set, c->c2.inotify_fd, EVENT_READ, (void *)&file_shift);
+        event_ctl(c->c2.event_set, c->c2.inotify_fd, EVENT_READ, (void *)file_shift);
     }
 #endif
 
@@ -1771,7 +1771,7 @@ io_wait_dowork(struct context *c, const unsigned int flags)
                 for (i = 0; i < status; ++i)
                 {
                     const struct event_set_return *e = &esr[i];
-                    c->c2.event_set_status |= ((e->rwflags & 3) << *((int *)e->arg));
+                    c->c2.event_set_status |= ((e->rwflags & 3) << (uintptr_t)e->arg);
                 }
             }
             else if (status == 0)
Signature · 1 line
2.17.0
#4 lore raw

RFC 4/8 io_work: pass event_arg object to event handler in case of socket event

In reply to Antonio Quartulli

In order to allow the code to work with multiple listening sockets it is essential to allow the generic multi_io event handler to distinguish between the various socket objects.

This can be achieved by passing an event_arg object that contains a pointer to the link_socket.

This code path is used on clients as well as UDP servers.

---
 src/openvpn/event.h   |  6 ++++++
 src/openvpn/forward.c | 25 +++++++++++++++++++++++--
 src/openvpn/mtcp.c    |  4 +---
 3 files changed, 30 insertions(+), 5 deletions(-)
src/openvpn/event.h +6 -0
index be199ad9..fd0ede04 100644
@@ -41,6 +41,12 @@
 #define EVENT_METHOD_US_TIMEOUT   (1<<0)
 #define EVENT_METHOD_FAST         (1<<1)
 
+/*
+ * The following constant is used as boundary between integer value
+ * and real addresses when passing arguments to event handlers as (void *)
+ */
+#define MULTI_N           ((void *)16) /* upper bound on MTCP_x */
+
 #ifdef _WIN32
 
 typedef const struct rw_handle *event_t;
src/openvpn/forward.c +23 -2
index a4e4d98a..1b5dad9b 100644
@@ -1714,7 +1714,8 @@ io_wait_dowork(struct context *c, const unsigned int flags)
     /*
      * Configure event wait based on socket, tuntap flags.
      */
-    socket_set(c->c2.link_socket, c->c2.event_set, socket, (void *)socket_shift, NULL);
+    socket_set(c->c2.link_socket, c->c2.event_set, socket,
+               &c->c2.link_socket->ev_arg, NULL);
     tun_set(c->c1.tuntap, c->c2.event_set, tuntap, (void *)tun_shift, NULL);
 
 #ifdef ENABLE_MANAGEMENT
@@ -1771,7 +1772,27 @@ io_wait_dowork(struct context *c, const unsigned int flags)
                 for (i = 0; i < status; ++i)
                 {
                     const struct event_set_return *e = &esr[i];
-                    c->c2.event_set_status |= ((e->rwflags & 3) << (uintptr_t)e->arg);
+                    uintptr_t shift;
+
+                    if (e->arg >= MULTI_N)
+                    {
+                        struct event_arg *ev_arg = (struct event_arg *)e->arg;
+                        if (ev_arg->type != EVENT_ARG_LINK_SOCKET)
+                        {
+                            c->c2.event_set_status = ES_ERROR;
+                            msg(D_LINK_ERRORS,
+                                "io_work: non socket event delivered");
+                            return;
+                        }
+
+                        shift = socket_shift;
+                    }
+                    else
+                    {
+                        shift = (uintptr_t)e->arg;
+                    }
+
+                    c->c2.event_set_status |= ((e->rwflags & 3) << shift);
                 }
             }
             else if (status == 0)
src/openvpn/mtcp.c +1 -3
index 51f339d0..0be97ae4 100644
@@ -68,8 +68,6 @@
 #define MTCP_FILE_CLOSE_WRITE ((void *)5)
 #endif
 
-#define MTCP_N           ((void *)16) /* upper bound on MTCP_x */
-
 struct ta_iow_flags
 {
     unsigned int flags;
@@ -682,7 +680,7 @@ multi_tcp_process_io(struct multi_context *m)
         struct event_arg *ev_arg = (struct event_arg *)e->arg;
 
         /* incoming data for instance or listening socket? */
-        if (e->arg >= MTCP_N)
+        if (e->arg >= MULTI_N)
         {
             switch (ev_arg->type)
             {
Signature · 1 line
2.17.0
#5 lore raw

RFC 5/8 allow tcp/udp server to listen on multiple ports at the same time

In reply to Antonio Quartulli
---
 src/openvpn/event.h          |   1 +
 src/openvpn/forward-inline.h |  15 ++-
 src/openvpn/forward.c        |  51 ++++++---
 src/openvpn/forward.h        |  18 ++--
 src/openvpn/init.c           | 197 +++++++++++++++++++++++------------
 src/openvpn/init.h           |   3 +-
 src/openvpn/mtcp.c           |  57 ++++++----
 src/openvpn/mudp.c           |  19 +++-
 src/openvpn/mudp.h           |   6 +-
 src/openvpn/multi.c          |  24 +++--
 src/openvpn/multi.h          |  14 ++-
 src/openvpn/openvpn.c        |   2 +-
 src/openvpn/openvpn.h        |   9 +-
 src/openvpn/ping-inline.h    |   2 +-
 src/openvpn/socket.c         |  13 +++
 src/openvpn/socket.h         |   6 +-
 16 files changed, 289 insertions(+), 148 deletions(-)
src/openvpn/event.h +1 -0
index fd0ede04..2b9c1b58 100644
@@ -100,6 +100,7 @@ typedef enum {
 struct event_arg
 {
     event_arg_t type;
+    bool pending; /* in UDP/P2P mode marks sockets waiting for processing */
     union {
         struct multi_instance *mi; /* if type = EVENT_ARG_MULTI_INSTANCE */
         struct link_socket *ls; /* if type = EVENT_ARG_LINK_SOCKET */
src/openvpn/forward-inline.h +11 -4
index feb0ba70..29751a1b 100644
@@ -55,7 +55,8 @@ check_tls_errors(struct context *c)
 
     if (c->c2.tls_multi && c->c2.tls_exit_signal)
     {
-        if (link_socket_connection_oriented(c->c2.link_socket))
+        /* all sockets are of the same type, so just check the first one */
+        if (link_socket_connection_oriented(c->c2.link_sockets[0]))
         {
             if (c->c2.tls_multi->n_soft_errors)
             {
@@ -254,16 +255,21 @@ context_reschedule_sec(struct context *c, int sec)
     }
 }
 
+/*
+ * This function is invoked only on single-link contexts
+ * (i.e. p2p or child contexts), therefore we can explicitly
+ * work on the first socket
+ */
 static inline struct link_socket_info *
 get_link_socket_info(struct context *c)
 {
-    if (c->c2.link_socket_info)
+    if (c->c2.link_socket_infos && c->c2.link_socket_infos[0])
     {
-        return c->c2.link_socket_info;
+        return c->c2.link_socket_infos[0];
     }
     else
     {
-        return &c->c2.link_socket->info;
+        return &c->c2.link_sockets[0]->info;
     }
 }
 
@@ -330,6 +336,7 @@ io_wait(struct context *c, const unsigned int flags)
     }
 }
 
+/* invoked only on single-link instances */
 #define CONNECTION_ESTABLISHED(c) (get_link_socket_info(c)->connection_established)
 
 #endif /* EVENT_INLINE_H */
src/openvpn/forward.c +35 -16

150 lines not on this page · raw message

src/openvpn/forward.h +11 -7
index 16639f75..45f8cc59 100644
@@ -227,10 +227,12 @@ void read_incoming_tun(struct context *c);
  *
  * If an error occurs, it is logged and the packet is dropped.
  *
- * @param c - The context structure of the VPN tunnel associated with the
- *     packet.
+ * @param c       The context structure of the VPN tunnel associated with
+ *                the packet.
+ * @param out_ls  Socket that will be used to send out the packet.
+ *
  */
-void process_incoming_tun(struct context *c);
+void process_incoming_tun(struct context *c, struct link_socket *out_ls);
 
 
 /**
@@ -242,10 +244,11 @@ void process_incoming_tun(struct context *c);
  *
  * If an error occurs, it is logged and the packet is dropped.
  *
- * @param c - The context structure of the VPN tunnel associated with
- *     the packet.
+ * @param c      The context structure of the VPN tunnel associated
+ *               with the packet.
+ * @param in_ls  Socket where the packet was received.
  */
-void process_outgoing_tun(struct context *c);
+void process_outgoing_tun(struct context *c, struct link_socket *in_ls);
 
 
 /**************************************************************************/
@@ -258,7 +261,8 @@ bool send_control_channel_string(struct context *c, const char *str, int msgleve
 #define PIPV4_EXTRACT_DHCP_ROUTER (1<<3)
 #define PIPV4_CLIENT_NAT      (1<<4)
 
-void process_ip_header(struct context *c, unsigned int flags, struct buffer *buf);
+void process_ip_header(struct context *c, unsigned int flags, struct buffer *buf,
+                       struct link_socket *ls);
 
 #if P2MP
 void schedule_exit(struct context *c, const int n_seconds, const int signal);
src/openvpn/init.c +130 -67

343 lines not on this page · raw message

src/openvpn/init.h +2 -1
index c8ebe768..590dd81b 100644
@@ -97,7 +97,8 @@ void reset_coarse_timers(struct context *c);
 bool do_deferred_options(struct context *c, const unsigned int found);
 
 void inherit_context_child(struct context *dest,
-                           const struct context *src);
+                           const struct context *src,
+                           struct link_socket *ls);
 
 void inherit_context_top(struct context *dest,
                          const struct context *src);
src/openvpn/mtcp.c +36 -21

117 lines not on this page · raw message

src/openvpn/mudp.c +14 -5
index 80efefb3..bfe24c01 100644
@@ -48,7 +48,8 @@
  */
 
 struct multi_instance *
-multi_get_create_instance_udp(struct multi_context *m, bool *floated)
+multi_get_create_instance_udp(struct multi_context *m, bool *floated,
+                              struct link_socket *ls)
 {
     struct gc_arena gc = gc_new();
     struct mroute_addr real;
@@ -102,7 +103,7 @@ multi_get_create_instance_udp(struct multi_context *m, bool *floated)
             {
                 if (frequency_limit_event_allowed(m->new_connection_limiter))
                 {
-                    mi = multi_create_instance(m, &real);
+                    mi = multi_create_instance(m, &real, ls);
                     if (mi)
                     {
                         int i;
@@ -227,10 +228,18 @@ multi_process_io_udp(struct multi_context *m)
     /* Incoming data on UDP port */
     else if (status & SOCKET_READ)
     {
-        read_incoming_link(&m->top, m->top.c2.link_socket);
-        if (!IS_SIG(&m->top))
+        int i;
+        for (i = 0; i < m->top.c1.link_sockets_num; i++)
         {
-            multi_process_incoming_link(m, NULL, mpp_flags);
+            if (!m->top.c2.link_sockets[i]->ev_arg.pending)
+                continue;
+
+            read_incoming_link(&m->top, m->top.c2.link_sockets[i]);
+            if (!IS_SIG(&m->top))
+            {
+                multi_process_incoming_link(m, NULL, mpp_flags,
+                                            m->top.c2.link_sockets[i]);
+            }
         }
     }
     /* Incoming data on TUN device */
src/openvpn/mudp.h +4 -2
index 7e311519..3ab442ba 100644
@@ -58,13 +58,15 @@ void tunnel_server_udp(struct context *top);
  * it.  If no entry exists, this function handles its creation, and if
  * successful, returns the newly created instance.
  *
- * @param m            - The single multi_context structure.
+ * @param m           - The single multi_context structure.
+ * @param ls          - Listening socket where this instance is connecting to
  *
  * @return A pointer to a multi_instance if one already existed for the
  *     packet's source address or if one was a newly created successfully.
  *      NULL if one did not yet exist and a new one was not created.
  */
-struct multi_instance *multi_get_create_instance_udp(struct multi_context *m, bool *floated);
+struct multi_instance *multi_get_create_instance_udp(struct multi_context *m, bool *floated,
+                                                     struct link_socket *ls);
 
 #endif
 #endif
src/openvpn/multi.c +14 -10
index f877c060..a6534c16 100644
@@ -765,7 +765,8 @@ multi_uninit(struct multi_context *m)
  * Create a client instance object for a newly connected client.
  */
 struct multi_instance *
-multi_create_instance(struct multi_context *m, const struct mroute_addr *real)
+multi_create_instance(struct multi_context *m, const struct mroute_addr *real,
+                      struct link_socket *ls)
 {
     struct gc_arena gc = gc_new();
     struct multi_instance *mi;
@@ -789,7 +790,7 @@ multi_create_instance(struct multi_context *m, const struct mroute_addr *real)
     }
 
     mi->did_open_context = true;
-    inherit_context_child(&mi->context, &m->top);
+    inherit_context_child(&mi->context, &m->top, ls);
     if (IS_SIG(&mi->context))
     {
         goto err;
@@ -2409,7 +2410,8 @@ multi_process_post(struct multi_context *m, struct multi_instance *mi, const uns
 }
 
 void
-multi_process_float(struct multi_context *m, struct multi_instance *mi)
+multi_process_float(struct multi_context *m, struct multi_instance *mi,
+                    struct link_socket *ls)
 {
     struct mroute_addr real;
     struct hash *hash = m->hash;
@@ -2465,8 +2467,8 @@ multi_process_float(struct multi_context *m, struct multi_instance *mi)
     mi->context.c2.to_link_addr = &mi->context.c2.from;
 
     /* inherit parent link_socket and link_socket_info */
-    mi->context.c2.link_socket = m->top.c2.link_socket;
-    mi->context.c2.link_socket_info->lsa->actual = m->top.c2.from;
+    mi->context.c2.link_sockets[0] = ls;
+    mi->context.c2.link_socket_infos[0]->lsa->actual = m->top.c2.from;
 
     tls_update_remote_addr(mi->context.c2.tls_multi, &mi->context.c2.from);
 
@@ -2486,7 +2488,8 @@ done:
  * i.e. client -> server direction.
  */
 bool
-multi_process_incoming_link(struct multi_context *m, struct multi_instance *instance, const unsigned int mpp_flags)
+multi_process_incoming_link(struct multi_context *m, struct multi_instance *instance, const unsigned int mpp_flags,
+                            struct link_socket *ls)
 {
     struct gc_arena gc = gc_new();
 
@@ -2507,7 +2510,7 @@ multi_process_incoming_link(struct multi_context *m, struct multi_instance *inst
 #ifdef MULTI_DEBUG_EVENT_LOOP
         printf("TCP/UDP -> TUN [%d]\n", BLEN(&m->top.c2.buf));
 #endif
-        multi_set_pending(m, multi_get_create_instance_udp(m, &floated));
+        multi_set_pending(m, multi_get_create_instance_udp(m, &floated, ls));
     }
     else
     {
@@ -2547,7 +2550,7 @@ multi_process_incoming_link(struct multi_context *m, struct multi_instance *inst
             {
                 if (floated)
                 {
-                    multi_process_float(m, m->pending);
+                    multi_process_float(m, m->pending, ls);
                 }
 
                 process_incoming_link_part2(c, lsi, orig_buf);
@@ -2816,7 +2819,7 @@ multi_process_incoming_tun(struct multi_context *m, const unsigned int mpp_flags
                     }
 
                     /* encrypt in instance context */
-                    process_incoming_tun(c);
+                    process_incoming_tun(c, c->c2.link_sockets[0]);
 
                     /* postprocess and set wakeup */
                     ret = multi_process_post(m, m->pending, mpp_flags);
@@ -2849,7 +2852,8 @@ multi_get_queue(struct mbuf_set *ms)
         {
             pip_flags |= PIP_MSSFIX;
         }
-        process_ip_header(&item.instance->context, pip_flags, &item.instance->context.c2.buf);
+        process_ip_header(&item.instance->context, pip_flags, &item.instance->context.c2.buf,
+                          item.instance->context.c2.link_sockets[0]);
         encrypt_sign(&item.instance->context, true);
         mbuf_free_buf(item.buffer);
 
src/openvpn/multi.h +9 -5
index 5fb8402f..a7bb0852 100644
@@ -238,7 +238,8 @@ void multi_top_init(struct multi_context *m, const struct context *top);
 
 void multi_top_free(struct multi_context *m);
 
-struct multi_instance *multi_create_instance(struct multi_context *m, const struct mroute_addr *real);
+struct multi_instance *multi_create_instance(struct multi_context *m, const struct mroute_addr *real,
+                                             struct link_socket *ls);
 
 void multi_close_instance(struct multi_context *m, struct multi_instance *mi, bool shutdown);
 
@@ -252,7 +253,8 @@ bool multi_process_timeout(struct multi_context *m, const unsigned int mpp_flags
  * existing peer. Updates multi_instance with new address,
  * updates hashtables in multi_context.
  */
-void multi_process_float(struct multi_context *m, struct multi_instance *mi);
+void multi_process_float(struct multi_context *m, struct multi_instance *mi,
+                         struct link_socket *ls);
 
 #define MPP_PRE_SELECT             (1<<0)
 #define MPP_CONDITIONAL_PRE_SELECT (1<<1)
@@ -307,8 +309,10 @@ bool multi_process_post(struct multi_context *m, struct multi_instance *mi, cons
  *                       when using TCP transport. Otherwise NULL, as is
  *                       the case when using UDP transport.
  * @param mpp_flags    - Fast I/O optimization flags.
+ * @param ls           - Socket where the packet was received.
  */
-bool multi_process_incoming_link(struct multi_context *m, struct multi_instance *instance, const unsigned int mpp_flags);
+bool multi_process_incoming_link(struct multi_context *m, struct multi_instance *instance, const unsigned int mpp_flags,
+                                 struct link_socket *ls);
 
 
 /**
@@ -624,7 +628,7 @@ multi_process_outgoing_tun(struct multi_context *m, const unsigned int mpp_flags
            mi->context.c2.to_tun.len);
 #endif
     set_prefix(mi);
-    process_outgoing_tun(&mi->context);
+    process_outgoing_tun(&mi->context, mi->context.c2.link_sockets[0]);
     ret = multi_process_post(m, mi, mpp_flags);
     clear_prefix();
     return ret;
@@ -637,7 +641,7 @@ multi_process_outgoing_link_dowork(struct multi_context *m, struct multi_instanc
 {
     bool ret = true;
     set_prefix(mi);
-    process_outgoing_link(&mi->context, mi->context.c2.link_socket);
+    process_outgoing_link(&mi->context, mi->context.c2.link_sockets[0]);
     ret = multi_process_post(m, mi, mpp_flags);
     clear_prefix();
     return ret;
src/openvpn/openvpn.c +1 -1
index b52063a2..4cd6ecf1 100644
@@ -115,7 +115,7 @@ tunnel_point_to_point(struct context *c)
         }
 
         /* process the I/O which triggered select */
-        process_io(c, c->c2.link_socket);
+        process_io(c, c->c2.link_sockets[0]);
         P2P_CHECK_SIG();
 
         perf_pop();
src/openvpn/openvpn.h +6 -3
index c5c767c0..17509d75 100644
@@ -149,7 +149,8 @@ struct context_0
  */
 struct context_1
 {
-    struct link_socket_addr link_socket_addr;
+    int link_sockets_num;
+    struct link_socket_addr *link_socket_addrs;
     /**< Local and remote addresses on the
      *   external network. */
 
@@ -247,9 +248,11 @@ struct context_2
 
     unsigned int event_set_status;
 
-    struct link_socket *link_socket;     /* socket used for TCP/UDP connection to remote */
+    struct link_socket **link_sockets;
+    struct link_socket_info **link_socket_infos;
+
     bool link_socket_owned;
-    struct link_socket_info *link_socket_info;
+
     const struct link_socket *accept_from; /* possibly do accept() on a parent link_socket */
 
     struct link_socket_actual *to_link_addr;    /* IP address of remote */
src/openvpn/ping-inline.h +1 -1
index 1a5c8bc3..d5f5d0d6 100644
@@ -37,7 +37,7 @@ check_ping_restart(struct context *c)
         && event_timeout_trigger(&c->c2.ping_rec_interval,
                                  &c->c2.timeval,
                                  (!c->options.ping_timer_remote
-                                  || link_socket_actual_defined(&c->c1.link_socket_addr.actual))
+                                  || link_socket_actual_defined(&c->c1.link_socket_addrs[0].actual))
                                  ? ETT_DEFAULT : 15))
     {
         check_ping_restart_dowork(c);
src/openvpn/socket.c +13 -0
index 564948a2..84d828e6 100644
@@ -54,6 +54,19 @@ const int proto_overhead[] = { /* indexed by PROTO_x */
     IPv6_TCP_HEADER_SIZE,
 };
 
+bool
+sockets_read_residual(const struct context *c)
+{
+    int i;
+
+    for (i = 0; i < c->c1.link_sockets_num; i++)
+    {
+        if (c->c2.link_sockets[i]->stream_buf.residual_fully_formed)
+            return true;
+    }
+    return false;
+}
+
 /*
  * Convert sockflags/getaddr_flags into getaddr_flags
  */
src/openvpn/socket.h +1 -5
index 46000246..c80a230d 100644
@@ -1220,11 +1220,7 @@ link_socket_set_tos(struct link_socket *ls)
  * Socket I/O wait functions
  */
 
-static inline bool
-socket_read_residual(const struct link_socket *s)
-{
-    return s && s->stream_buf.residual_fully_formed;
-}
+bool sockets_read_residual(const struct context *c);
 
 static inline event_t
 socket_event_handle(const struct link_socket *s)
Signature · 1 line
2.17.0
#6 lore raw

RFC 6/8 if a local IPv6 address is provided, socket must be v6-only

In reply to Antonio Quartulli
---
 src/openvpn/socket.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)
src/openvpn/socket.c +8 -2
index 84d828e6..79fbc6a8 100644
@@ -988,9 +988,15 @@ bind_local(struct link_socket *sock, const sa_family_t ai_family)
         }
         else
         {
+            bool v6only = sock->info.bind_ipv6_only;
+
+            /* force binding IPv6-only if an address was specified
+             * an it is a IPv6 */
+            if (sock->local_host && ai_family == AF_INET6)
+                v6only = true;
+
             socket_bind(sock->sd, sock->info.lsa->bind_local,
-                        ai_family,
-                        "TCP/UDP", sock->info.bind_ipv6_only);
+                        ai_family, "TCP/UDP", v6only);
         }
     }
 }
Signature · 1 line
2.17.0
#7 lore raw

RFC 7/8 allow user to specify 'local' multiple times in config files

In reply to Antonio Quartulli

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.

---
 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(-)
doc/openvpn.8 +8 -2
index 4114f408..658bda23 100644
@@ -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]
src/openvpn/init.c +5 -4
index a2b474c8..41bc5094 100644
@@ -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;
src/openvpn/options.c +134 -28

251 lines not on this page · raw message

src/openvpn/options.h +14 -1
index f7d0145a..fb8c32fd 100644
@@ -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;
src/openvpn/socket.c +13 -5
index 79fbc6a8..9d961347 100644
@@ -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;
Signature · 1 line
2.17.0
#8 lore raw

RFC 8/8 override ai_family if 'local' numeric address was specified

In reply to Antonio Quartulli

This change ensures that when a numeric IP address is specified as argument to a 'local' directive, its ai_family overrides the one extracted from the 'proto' config option.

---
 src/openvpn/socket.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)
src/openvpn/socket.c +9 -2
index 9d961347..3dd44b83 100644
@@ -378,9 +378,8 @@ openvpn_getaddrinfo(unsigned int flags,
         signal_received = &sigrec;
     }
 
-    /* try numeric ipv6 addr first */
+    /* try numeric ip addr first */
     CLEAR(hints);
-    hints.ai_family = ai_family;
     hints.ai_flags = AI_NUMERICHOST;
 
     if (flags & GETADDR_PASSIVE)
@@ -408,6 +407,10 @@ openvpn_getaddrinfo(unsigned int flags,
         const char *fmt;
         int level = 0;
 
+        /* this is not a numeric IP, therefore force resolution using the
+         * provided ai_family */
+        hints.ai_family = ai_family;
+
         if (hostname && (flags & GETADDR_RANDOMIZE))
         {
             hostname = hostname_randomize(hostname, &gc);
@@ -1599,6 +1602,10 @@ resolve_bind_local(struct link_socket *sock, const sa_family_t af)
                 sock->local_host, sock->local_port,
                 gai_strerror(status));
         }
+
+        /* the resolved 'local entry' might have a different family than what
+         * was globally configured */
+        sock->info.af = sock->info.lsa->bind_local->ai_family;
     }
 
     gc_free(&gc);
Signature · 1 line
2.17.0