Skip to contentSkip to conversation

Set source address for UDP packets from multihomed server

PATCH In review Neil Brown 3 participants 3 messages Jul 26, 2004 – Jul 27, 2004

lore
Share & tools
lore

Neil Brown

author
#0 lore raw

PATCH 1/1 Set source address for UDP packets from multihomed server

Hi.
This is the first of 4 patches that I would like to offer for openvpn.
They are against 2.0_beta7.

This first one is a refinement of a patch I posted earlier which, at that time, didn't work.
The purpose of the patch is to use the IP_PKTINFO socket option to get and set the local address for UDP packets being sent or received. This means that if you have a multihomed server, then packets sent to it will always come from the address that they corresponding packets were sent to. This is needed to pass cleanly through a NAT.

In replaces "struct sockaddr_in from" in "struct context_2" with
struct sockaddr_in incoming_from, incoming_to
and records the address provided by IP_PKTINFO in incoming_to.

It adds "struct sockaddr_in actuallocal" alongside "struct sockaddr_in actual" in "struct lock_socket_addr", for storing the local enpoint if a udp "connection" in this structure.

It adds "struct sockaddr_in local_addr" alongside "remote_addr" in "struct key_state" for a similar purpose.

And it makes sure these fields are set and used appropriately.

The main reason that it didn't work before was that in multi_process_incoming_link, when the packet and address are transferred from the "mt" structure into the per-session context structure, I wasn't copying the incoming_to address as well. I do now.

One part of the change that I am not completely happy with is
+ /* alway set localaddr address */
+ info->lsa->actuallocal = *localaddr;
in link_socket_set_outgoing_addr.
The current code only changes the outgoing address if it has changed, and it does that inside link_socket_connection_initiated. However it is possible for the localaddress to change without the remote address changing.
e.g. If I stop and restart my openvpn instance on my notebook, and it gets a different IP address via DNS lookup. In this case it will appear to be a continuation of the same session, but will need to use a different local address. I think it is OK to change the localaddress on every packet, but I am open to be convinced otherwise, or that the code could be cleaner.

Anyway, here is the patch. I hope it can be included in an upcoming beta of openvpn.

Thanks,
NeilBrown

============================================

### Diffstat output

 ./forward.c |   23 ++++++++++++--------
 ./multi.c   |   10 +++++---
 ./openvpn.h |    5 +++-
 ./socket.c  |   46 ++++++++++++++++++++++++++++++++++++++++-
 ./socket.h  |   67 ++++++++++++++++++++++++++++++++++++++++++++++++++----------
 ./ssl.c     |   25 +++++++++++++++++-----
 ./ssl.h     |    4 +++
 7 files changed, 148 insertions(+), 32 deletions(-)
forward.c renamed from forward.c~current~ +14 -9
diff ./forward.c~current~ ./forward.c
@@ -87,6 +87,7 @@ check_tls_dowork (struct context *c)
     {
       if (tls_multi_process
 	  (c->c2.tls_multi, &c->c2.to_link, &c->c2.to_link_addr,
+	   &c->c2.from_link_addr,
 	   get_link_socket_info (c), &wakeup))
 	{
 	  update_time ();
@@ -342,7 +343,7 @@ encrypt_sign (struct context *c, bool co
    * Get the address we will be sending the packet to.
    */
   link_socket_get_outgoing_addr (&c->c2.buf, get_link_socket_info (c),
-				 &c->c2.to_link_addr);
+				 &c->c2.to_link_addr, &c->c2.from_link_addr);
 #ifdef USE_CRYPTO
 #ifdef USE_SSL
   /*
@@ -463,7 +464,7 @@ static inline void
 socks_postprocess_incoming_link (struct context *c)
 {
   if (c->c2.link_socket->socks_proxy && c->c2.link_socket->info.proto == PROTO_UDPv4)
-    socks_process_incoming_udp (&c->c2.buf, &c->c2.from);
+    socks_process_incoming_udp (&c->c2.buf, &c->c2.incoming_from);
 }
 
 static inline void
@@ -505,7 +506,8 @@ read_incoming_link (struct context *c)
 
   c->c2.buf = c->c2.buffers->read_link_buf;
   ASSERT (buf_init (&c->c2.buf, FRAME_HEADROOM (&c->c2.frame)));
-  status = link_socket_read (c->c2.link_socket, &c->c2.buf, MAX_RW_SIZE_LINK (&c->c2.frame), &c->c2.from);
+  status = link_socket_read (c->c2.link_socket, &c->c2.buf, MAX_RW_SIZE_LINK (&c->c2.frame),
+			     &c->c2.incoming_from, &c->c2.incoming_to);
 
   if (socket_connection_reset (c->c2.link_socket, status))
     {
@@ -561,7 +563,7 @@ process_incoming_link (struct context *c
   msg (D_LINK_RW, "%s READ [%d] from %s: %s",
        proto2ascii (lsi->proto, true),
        BLEN (&c->c2.buf),
-       print_sockaddr (&c->c2.from, &gc),
+       print_sockaddr (&c->c2.incoming_from, &gc),
        PROTO_DUMP (&c->c2.buf, &gc));
 
   /*
@@ -573,8 +575,8 @@ process_incoming_link (struct context *c
    */
   if (c->c2.buf.len > 0)
     {
-      if (!link_socket_verify_incoming_addr (&c->c2.buf, lsi, &c->c2.from))
-	link_socket_bad_incoming_addr (&c->c2.buf, lsi, &c->c2.from);
+      if (!link_socket_verify_incoming_addr (&c->c2.buf, lsi, &c->c2.incoming_from))
+	link_socket_bad_incoming_addr (&c->c2.buf, lsi, &c->c2.incoming_from);
 
 #ifdef USE_CRYPTO
 #ifdef USE_SSL
@@ -591,7 +593,9 @@ process_incoming_link (struct context *c
 	   * and return false.
 	   */
 	  //tls_mutex_lock (c->c2.tls_multi);
-	  if (tls_pre_decrypt (c->c2.tls_multi, &c->c2.from, &c->c2.buf, &c->c2.crypto_options))
+	  if (tls_pre_decrypt (c->c2.tls_multi,
+			       &c->c2.incoming_from, &c->c2.incoming_to,
+			       &c->c2.buf, &c->c2.crypto_options))
 	    {
 	      interval_action (&c->c2.tmp_int);
 
@@ -641,7 +645,7 @@ process_incoming_link (struct context *c
        * Also, update the persisted version of our packet-id.
        */
       if (!TLS_MODE)
-	link_socket_set_outgoing_addr (&c->c2.buf, lsi, &c->c2.from, NULL);
+	link_socket_set_outgoing_addr (&c->c2.buf, lsi, &c->c2.incoming_from, &c->c2.incoming_to, NULL);
 
       /* reset packet received timer */
       if (c->options.ping_rec_timeout && c->c2.buf.len > 0)
@@ -826,7 +830,8 @@ process_outgoing_link (struct context *c
 	    socks_preprocess_outgoing_link (c, &to_addr, &size_delta);
 
 	    /* Send packet */
-	    size = link_socket_write (c->c2.link_socket, &c->c2.to_link, to_addr);
+	    size = link_socket_write (c->c2.link_socket, &c->c2.to_link, to_addr,
+				      &c->c2.from_link_addr);
 
 	    /* Undo effect of prepend */
 	    link_socket_write_post_size_adjust (&size, size_delta, &c->c2.to_link);
multi.c renamed from multi.c~current~ +6 -4
diff ./multi.c~current~ ./multi.c
@@ -544,7 +544,7 @@ multi_get_create_instance (struct multi_
   struct multi_instance *mi = NULL;
   struct hash *hash = mt->multi->hash;
 
-  if (mroute_extract_sockaddr_in (&real, &mt->top.c2.from, true))
+  if (mroute_extract_sockaddr_in (&real, &mt->top.c2.incoming_from, true))
     {
       struct hash_element *he;
       const uint32_t hv = hash_value (hash, &real);
@@ -560,7 +560,8 @@ multi_get_create_instance (struct multi_
       else
 	{
 	  if (!mt->top.c2.tls_auth_standalone
-	      || tls_pre_decrypt_lite (mt->top.c2.tls_auth_standalone, &mt->top.c2.from, &mt->top.c2.buf))
+	      || tls_pre_decrypt_lite (mt->top.c2.tls_auth_standalone,
+				       &mt->top.c2.incoming_from, &mt->top.c2.incoming_to, &mt->top.c2.buf))
 	    {
 	      if (frequency_limit_event_allowed (mt->multi->new_connection_limiter))
 		{
@@ -1330,8 +1331,9 @@ multi_process_incoming_link (struct mult
       /* transfer packet pointer from top-level context buffer to instance */
       c->c2.buf = mt->top.c2.buf;
 
-      /* transfer from-addr from top-level context buffer to instance */
-      c->c2.from = mt->top.c2.from;
+      /* transfer from-addr and to-addr from top-level context buffer to instance */
+      c->c2.incoming_from = mt->top.c2.incoming_from;
+      c->c2.incoming_to = mt->top.c2.incoming_to;
 
       /* decrypt in instance context */
       process_incoming_link (c);
openvpn.h renamed from openvpn.h~current~ +4 -1
diff ./openvpn.h~current~ ./openvpn.h
@@ -167,7 +167,10 @@ struct context_2
   const struct link_socket *accept_from; /* possibly do accept() on a parent link_socket */
 
   struct sockaddr_in to_link_addr;	 /* IP address of remote */
-  struct sockaddr_in from;               /* address of incoming datagram */
+  struct sockaddr_in from_link_addr;	 /* Address to send messages from */
+
+  struct sockaddr_in incoming_from;      /* address of incoming datagram */
+  struct sockaddr_in incoming_to;	 /* address incoming datagram was to */
 
   /* MTU frame parameters */
   struct frame frame;
socket.c renamed from socket.c~current~ +45 -1

76 lines not on this page · raw message

socket.h renamed from socket.h~current~ +56 -11

149 lines not on this page · raw message

ssl.c renamed from ssl.c~current~ +19 -6
diff ./ssl.c~current~ ./ssl.c
@@ -1494,6 +1494,7 @@ write_control_auth (struct tls_session *
 		    struct key_state *ks,
 		    struct buffer *buf,
 		    struct sockaddr_in *to_link_addr,
+		    struct sockaddr_in *from_link_addr,
 		    int opcode,
 		    int max_ack,
 		    bool prepend_ack)
@@ -1514,6 +1515,7 @@ write_control_auth (struct tls_session *
       ASSERT (swap_hmac (buf, &session->tls_auth, false));
     }
   *to_link_addr = ks->remote_addr;
+  *from_link_addr = ks->local_addr;
 }
 
 /*
@@ -1916,6 +1918,7 @@ key_state_soft_reset (struct tls_session
   key_state_init (session, ks);
   ks->session_id_remote = ks_lame->session_id_remote;
   ks->remote_addr = ks_lame->remote_addr;
+  ks->local_addr = ks_lame->local_addr;
 }
 
 /*
@@ -1932,6 +1935,7 @@ tls_process (struct tls_multi *multi,
 	     struct tls_session *session,
 	     struct buffer *to_link,
 	     struct sockaddr_in *to_link_addr,
+	     struct sockaddr_in *from_link_addr,
 	     struct link_socket_info *to_link_socket_info,
 	     interval_t *wakeup)
 {
@@ -2052,7 +2056,8 @@ tls_process (struct tls_multi *multi,
 		  INCR_SUCCESS;
 
 		  /* Set outgoing address for data channel packets */
-		  link_socket_set_outgoing_addr (NULL, to_link_socket_info, &ks->remote_addr, session->common_name);
+		  link_socket_set_outgoing_addr (NULL, to_link_socket_info, &ks->remote_addr,
+						 &ks->local_addr, session->common_name);
 
 #ifdef MEASURE_TLS_HANDSHAKE_STATS
 		  show_tls_performance_stats();
@@ -2072,7 +2077,8 @@ tls_process (struct tls_multi *multi,
 	      b = *buf;
 	      INCR_SENT;
 
-	      write_control_auth (session, ks, &b, to_link_addr, opcode,
+	      write_control_auth (session, ks, &b, to_link_addr, 
+				  from_link_addr, opcode,
 				  CONTROL_SEND_ACK_MAX, true);
 	      *to_link = b;
 	      active = true;
@@ -2087,7 +2093,8 @@ tls_process (struct tls_multi *multi,
 	    {
 	      buf = &ks->ack_write_buf;
 	      ASSERT (buf_init (buf, FRAME_HEADROOM (&multi->opt.frame)));
-	      write_control_auth (session, ks, buf, to_link_addr, P_ACK_V1,
+	      write_control_auth (session, ks, buf, to_link_addr, from_link_addr,
+				  P_ACK_V1,
 				  RELIABLE_ACK_SIZE, false);
 	      *to_link = *buf;
 	      active = true;
@@ -2391,7 +2398,7 @@ tls_process (struct tls_multi *multi,
     {
       buf = &ks->ack_write_buf;
       ASSERT (buf_init (buf, FRAME_HEADROOM (&multi->opt.frame)));
-      write_control_auth (session, ks, buf, to_link_addr, P_ACK_V1,
+      write_control_auth (session, ks, buf, to_link_addr, from_link_addr, P_ACK_V1,
 			  RELIABLE_ACK_SIZE, false);
       *to_link = *buf;
       active = true;
@@ -2453,6 +2460,7 @@ bool
 tls_multi_process (struct tls_multi *multi,
 		   struct buffer *to_link,
 		   struct sockaddr_in *to_link_addr,
+		   struct sockaddr_in *from_link_addr,
 		   struct link_socket_info *to_link_socket_info,
 		   interval_t *wakeup)
 {
@@ -2473,8 +2481,10 @@ tls_multi_process (struct tls_multi *mul
 
       /* set initial remote address */
       if (i == TM_ACTIVE && ks->state == S_INITIAL &&
-	  addr_defined (&to_link_socket_info->lsa->actual))
+	  addr_defined (&to_link_socket_info->lsa->actual)) {
 	ks->remote_addr = to_link_socket_info->lsa->actual;
+	ks->local_addr = to_link_socket_info->lsa->actuallocal;
+      }
 
       msg (D_TLS_DEBUG,
 	   "TLS: tls_multi_process: i=%d state=%s, mysid=%s, stored-sid=%s, stored-ip=%s",
@@ -2488,7 +2498,7 @@ tls_multi_process (struct tls_multi *mul
 	{
 	  update_time ();
 
-	  if (tls_process (multi, session, to_link, to_link_addr,
+	  if (tls_process (multi, session, to_link, to_link_addr, from_link_addr,
 			   to_link_socket_info, wakeup))
 	    active = true;
 
@@ -2617,6 +2627,7 @@ tls_rec_payload (struct tls_multi *multi
 bool
 tls_pre_decrypt (struct tls_multi *multi,
 		 struct sockaddr_in *from,
+		 struct sockaddr_in *to,
 		 struct buffer *buf,
 		 struct crypto_options *opt)
 {
@@ -2919,6 +2930,7 @@ tls_pre_decrypt (struct tls_multi *multi
 	      {
 		ks->session_id_remote = sid;
 		ks->remote_addr = *from;
+		ks->local_addr = *to;
 		++multi->n_sessions;
 	      }
 	    else if (!addr_port_match (&ks->remote_addr, from))
@@ -3023,6 +3035,7 @@ tls_pre_decrypt (struct tls_multi *multi
 bool
 tls_pre_decrypt_lite (const struct tls_auth_standalone *tas,
 		      const struct sockaddr_in *from,
+		      const struct sockaddr_in *to,
 		      const struct buffer *buf)
 {
   struct gc_arena gc = gc_new ();
ssl.h renamed from ssl.h~current~ +4 -0
diff ./ssl.h~current~ ./ssl.h
@@ -236,6 +236,7 @@ struct key_state
   int initial_opcode;		/* our initial P_ opcode */
   struct session_id session_id_remote; /* peer's random session ID */
   struct sockaddr_in remote_addr;      /* peer's IP addr */
+  struct sockaddr_in local_addr;       /* address peer expects reply from */
   struct packet_id packet_id;	       /* for data channel, to prevent replay attacks */
 
   struct key_ctx_bi key;	       /* data channel keys for encrypt/decrypt/hmac */
@@ -456,6 +457,7 @@ void tls_multi_init_set_options(struct t
 bool tls_multi_process (struct tls_multi *multi,
 			struct buffer *to_link,
 			struct sockaddr_in *to_link_addr,
+			struct sockaddr_in *from_link_addr,
 			struct link_socket_info *to_link_socket_info,
 			interval_t *wakeup);
 
@@ -463,11 +465,13 @@ void tls_multi_free (struct tls_multi *m
 
 bool tls_pre_decrypt (struct tls_multi *multi,
 		      struct sockaddr_in *from,
+		      struct sockaddr_in *to,
 		      struct buffer *buf,
 		      struct crypto_options *opt);
 
 bool tls_pre_decrypt_lite (const struct tls_auth_standalone *tas,
 			   const struct sockaddr_in *from,
+			   const struct sockaddr_in *to,
 			   const struct buffer *buf);
 
 void tls_pre_encrypt (struct tls_multi *multi,
#1 lore raw

Neil,

Thanks for the patches... here are some comments:

* You've obviously put a lot of effort into understanding the code to write this patch. But did you consider any alternative approaches, such as starting multiple OpenVPN instances and binding each to a separate local interface?

* A lot of function prototypes get changed so that both from and to sockaddr_in pointers must be passed. Do you think it might make more sense to make a new struct that represents a bidirectional socketaddr_in and embed from and to inside of it? I'm mostly thinking about this from the angle of easing future maintenance. As much as is possible, I would like to see this patch live in it's own location (probably a separate source file) and require a minimal amount of modification of existing code.

* It would be nice to see the #ifdef IP_PKTINFO code (which appears to be non-portable) isolated somewhere and bracketed inside of an internal API which would make it easy for different cases to be implemented for different OSes.

* The change to link_socket_set_outgoing_addr is probably okay, though remember that this is in the main line event loop, so efficiency is important here. Most of link_socket_set_outgoing_addr is structured around the fact that a bunch of special things need to happen if the remote address changes, but what similar kinds of things need to happen when the local address changes?

James

Quoted 87 lines · Neil Brown

Hi.
This is the first of 4 patches that I would like to offer for openvpn.
They are against 2.0_beta7.

This first one is a refinement of a patch I posted earlier which, at that time, didn't work.
The purpose of the patch is to use the IP_PKTINFO socket option to get and set the local address for UDP packets being sent or received. This means that if you have a multihomed server, then packets sent to it will always come from the address that they corresponding packets were sent to. This is needed to pass cleanly through a NAT.

In replaces "struct sockaddr_in from" in "struct context_2" with
struct sockaddr_in incoming_from, incoming_to
and records the address provided by IP_PKTINFO in incoming_to.

It adds "struct sockaddr_in actuallocal" alongside "struct sockaddr_in actual" in "struct lock_socket_addr", for storing the local enpoint if a udp "connection" in this structure.

It adds "struct sockaddr_in local_addr" alongside "remote_addr" in "struct key_state" for a similar purpose.

And it makes sure these fields are set and used appropriately.

The main reason that it didn't work before was that in multi_process_incoming_link, when the packet and address are transferred from the "mt" structure into the per-session context structure, I wasn't copying the incoming_to address as well. I do now.

One part of the change that I am not completely happy with is

socket.h renamed from socket.h~current~ +2 -0
+      /* alway set localaddr address */
+      info->lsa->actuallocal = *localaddr;

in link_socket_set_outgoing_addr.
The current code only changes the outgoing address if it has changed, and it does that inside link_socket_connection_initiated. However it is possible for the localaddress to change without the remote address changing.
e.g. If I stop and restart my openvpn instance on my notebook, and it gets a different IP address via DNS lookup. In this case it will appear to be a continuation of the same session, but will need to use a different local address. I think it is OK to change the localaddress on every packet, but I am open to be convinced otherwise, or that the code could be cleaner.

Anyway, here is the patch. I hope it can be included in an upcoming beta of openvpn.

Thanks,
NeilBrown

============================================

### Diffstat output

 ./forward.c |   23 ++++++++++++--------
 ./multi.c   |   10 +++++---
 ./openvpn.h |    5 +++-
 ./socket.c  |   46 ++++++++++++++++++++++++++++++++++++++++-
 ./socket.h  |   67 ++++++++++++++++++++++++++++++++++++++++++++++++++----------
 ./ssl.c     |   25 +++++++++++++++++-----
 ./ssl.h     |    4 +++
 7 files changed, 148 insertions(+), 32 deletions(-)

diff ./forward.c~current~ ./forward.c

forward.c renamed from forward.c~current~ +2 -1
@@ -87,6 +87,7 @@ check_tls_dowork (struct context *c)
     {
       if (tls_multi_process
 	  (c->c2.tls_multi, &c->c2.to_link, &c->c2.to_link_addr,
+	   &c->c2.from_link_addr,
 	   get_link_socket_info (c), &wakeup))
 	{
 	  update_time ();
@@ -342,7 +343,7 @@ encrypt_sign (struct context *c, bool co
    * Get the address we will be sending the packet to.
    */
   link_socket_get_outgoing_addr (&c->c2.buf, get_link_socket_info (c),
-				 &c->c2.to_link_addr);
+				 &c->c2.to_link_addr, &c->c2.from_link_addr);
 #ifdef USE_CRYPTO
 #ifdef USE_SSL
   /*

forward.c static inline void · line 466

Quoted 3 lines
forward.c renamed from forward.c~current~ +0 -0
@@ -463,7 +464,7 @@ static inline void
 socks_postprocess_incoming_link (struct context *c)
 {
   if (c->c2.link_socket->socks_proxy && c->c2.link_socket->info.proto ==

PROTO_UDPv4)

forward.c read_incoming_link (struct context *c) · line 508

Quoted 10 lines
forward.c renamed from forward.c~current~ +1 -2
-    socks_process_incoming_udp (&c->c2.buf, &c->c2.from);
+    socks_process_incoming_udp (&c->c2.buf, &c->c2.incoming_from);
 }
 
 static inline void
@@ -505,7 +506,8 @@ read_incoming_link (struct context *c)
 
   c->c2.buf = c->c2.buffers->read_link_buf;
   ASSERT (buf_init (&c->c2.buf, FRAME_HEADROOM (&c->c2.frame)));
-  status = link_socket_read (c->c2.link_socket, &c->c2.buf,

MAX_RW_SIZE_LINK (&c->c2.frame), &c->c2.from);

forward.c read_incoming_link (struct context *c) · line 509

Quoted 1 line
forward.c renamed from forward.c~current~ +1 -0
+  status = link_socket_read (c->c2.link_socket, &c->c2.buf,

MAX_RW_SIZE_LINK (&c->c2.frame),

Quoted 14 lines
forward.c renamed from forward.c~current~ +2 -1
+			     &c->c2.incoming_from, &c->c2.incoming_to);
 
   if (socket_connection_reset (c->c2.link_socket, status))
     {
@@ -561,7 +563,7 @@ process_incoming_link (struct context *c
   msg (D_LINK_RW, "%s READ [%d] from %s: %s",
        proto2ascii (lsi->proto, true),
        BLEN (&c->c2.buf),
-       print_sockaddr (&c->c2.from, &gc),
+       print_sockaddr (&c->c2.incoming_from, &gc),
        PROTO_DUMP (&c->c2.buf, &gc));
 
   /*

forward.c process_incoming_link (struct context *c · line 578

Quoted 6 lines
forward.c renamed from forward.c~current~ +1 -2
@@ -573,8 +575,8 @@ process_incoming_link (struct context *c
    */
   if (c->c2.buf.len > 0)
     {
-      if (!link_socket_verify_incoming_addr (&c->c2.buf, lsi, &c->c2.from))
-	link_socket_bad_incoming_addr (&c->c2.buf, lsi, &c->c2.from);
+      if (!link_socket_verify_incoming_addr (&c->c2.buf, lsi,

&c->c2.incoming_from))

forward.c process_incoming_link (struct context *c · line 594

Quoted 9 lines
forward.c renamed from forward.c~current~ +1 -1
+	link_socket_bad_incoming_addr (&c->c2.buf, lsi, &c->c2.incoming_from);
 
 #ifdef USE_CRYPTO
 #ifdef USE_SSL
@@ -591,7 +593,9 @@ process_incoming_link (struct context *c
 	   * and return false.
 	   */
 	  //tls_mutex_lock (c->c2.tls_multi);
-	  if (tls_pre_decrypt (c->c2.tls_multi, &c->c2.from, &c->c2.buf,

&c->c2.crypto_options))

forward.c process_incoming_link (struct context *c · line 648

Quoted 12 lines
forward.c renamed from forward.c~current~ +4 -1
+	  if (tls_pre_decrypt (c->c2.tls_multi,
+			       &c->c2.incoming_from, &c->c2.incoming_to,
+			       &c->c2.buf, &c->c2.crypto_options))
 	    {
 	      interval_action (&c->c2.tmp_int);
 
@@ -641,7 +645,7 @@ process_incoming_link (struct context *c
        * Also, update the persisted version of our packet-id.
        */
       if (!TLS_MODE)
-	link_socket_set_outgoing_addr (&c->c2.buf, lsi, &c->c2.from, NULL);
+	link_socket_set_outgoing_addr (&c->c2.buf, lsi, &c->c2.incoming_from,

&c->c2.incoming_to, NULL);

Quoted 27 lines
forward.c renamed from forward.c~current~ +2 -1
       /* reset packet received timer */
       if (c->options.ping_rec_timeout && c->c2.buf.len > 0)
@@ -826,7 +830,8 @@ process_outgoing_link (struct context *c
 	    socks_preprocess_outgoing_link (c, &to_addr, &size_delta);
 
 	    /* Send packet */
-	    size = link_socket_write (c->c2.link_socket, &c->c2.to_link, to_addr);
+	    size = link_socket_write (c->c2.link_socket, &c->c2.to_link, to_addr,
+				      &c->c2.from_link_addr);
 
 	    /* Undo effect of prepend */
 	    link_socket_write_post_size_adjust (&size, size_delta, &c->c2.to_link);

diff ./multi.c~current~ ./multi.c

multi.c renamed from multi.c~current~ +1 -1
@@ -544,7 +544,7 @@ multi_get_create_instance (struct multi_
   struct multi_instance *mi = NULL;
   struct hash *hash = mt->multi->hash;
 
-  if (mroute_extract_sockaddr_in (&real, &mt->top.c2.from, true))
+  if (mroute_extract_sockaddr_in (&real, &mt->top.c2.incoming_from, true))
     {
       struct hash_element *he;
       const uint32_t hv = hash_value (hash, &real);

multi.c multi_get_create_instance (struct multi_ · line 563

Quoted 4 lines
multi.c renamed from multi.c~current~ +0 -1
@@ -560,7 +560,8 @@ multi_get_create_instance (struct multi_
       else
 	{
 	  if (!mt->top.c2.tls_auth_standalone
-	      || tls_pre_decrypt_lite (mt->top.c2.tls_auth_standalone,

&mt->top.c2.from, &mt->top.c2.buf))

multi.c multi_get_create_instance (struct multi_ · line 564

Quoted 2 lines
multi.c renamed from multi.c~current~ +2 -0
+	      || tls_pre_decrypt_lite (mt->top.c2.tls_auth_standalone,
+				       &mt->top.c2.incoming_from, &mt->top.c2.incoming_to,

&mt->top.c2.buf))

multi.c multi_process_incoming_link (struct mult · line 1334

Quoted 10 lines
multi.c renamed from multi.c~current~ +1 -2
 	    {
 	      if (frequency_limit_event_allowed (mt->multi->new_connection_limiter))
 		{
@@ -1330,8 +1331,9 @@ multi_process_incoming_link (struct mult
       /* transfer packet pointer from top-level context buffer to instance */
       c->c2.buf = mt->top.c2.buf;
 
-      /* transfer from-addr from top-level context buffer to instance */
-      c->c2.from = mt->top.c2.from;
+      /* transfer from-addr and to-addr from top-level context buffer to

instance */

openvpn.h struct context_2 · line 167

Quoted 11 lines
multi.c renamed from multi.c~current~ +2 -0
+      c->c2.incoming_from = mt->top.c2.incoming_from;
+      c->c2.incoming_to = mt->top.c2.incoming_to;
 
       /* decrypt in instance context */
       process_incoming_link (c);

diff ./openvpn.h~current~ ./openvpn.h

openvpn.h renamed from openvpn.h~current~ +0 -0
@@ -167,7 +167,10 @@ struct context_2
   const struct link_socket *accept_from; /* possibly do accept() on a

parent link_socket */

Quoted 247 lines
openvpn.h renamed from openvpn.h~current~ +4 -1
   struct sockaddr_in to_link_addr;	 /* IP address of remote */
-  struct sockaddr_in from;               /* address of incoming datagram */
+  struct sockaddr_in from_link_addr;	 /* Address to send messages from */
+
+  struct sockaddr_in incoming_from;      /* address of incoming datagram */
+  struct sockaddr_in incoming_to;	 /* address incoming datagram was to */
 
   /* MTU frame parameters */
   struct frame frame;
 

diff ./socket.c~current~ ./socket.c

socket.c renamed from socket.c~current~ +45 -1
@@ -392,6 +392,12 @@ create_socket_udp (void)
 
   if ((sd = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
     msg (M_SOCKERR, "UDP: Cannot create UDP socket");
+#ifdef IP_PKTINFO
+  else {
+	  int pad = 1;
+	  setsockopt(sd, SOL_IP, IP_PKTINFO, (void*)&pad, sizeof(pad));
+  }
+#endif
   return sd;
 }
 
@@ -1135,6 +1141,7 @@ void
 link_socket_connection_initiated (const struct buffer *buf,
 				  struct link_socket_info *info,
 				  const struct sockaddr_in *addr,
+				  const struct sockaddr_in *localaddr,
 				  const char *common_name)
 {
   struct gc_arena gc = gc_new ();
@@ -1143,6 +1150,7 @@ link_socket_connection_initiated (const 
   mutex_lock_static (L_SCRIPT);
 
   info->lsa->actual = *addr; /* Note: skip this line for --force-dest */
+  info->lsa->actuallocal = *localaddr;
   setenv_trusted (info);
   info->connection_established = true;
 
@@ -1582,13 +1590,49 @@ int
 link_socket_read_udp_posix (struct link_socket *sock,
 			    struct buffer *buf,
 			    int maxsize,
-			    struct sockaddr_in *from)
+			    struct sockaddr_in *from,
+			    struct sockaddr_in *to)
 {
   socklen_t fromlen = sizeof (*from);
   CLEAR (*from);
+  CLEAR (*to);
   ASSERT (buf_safe (buf, maxsize));
+#ifdef IP_PKTINFO
+  {
+    struct iovec iov;
+    char b[sizeof(struct cmsghdr)+sizeof(struct in_pktinfo)];
+    struct msghdr mesg;
+    iov.iov_base = BPTR (buf);
+    iov.iov_len = maxsize;
+    mesg.msg_iov = &iov;
+    mesg.msg_iovlen = 1;
+    mesg.msg_name = from;
+    mesg.msg_namelen = fromlen;
+    mesg.msg_control = b;
+    mesg.msg_controllen = sizeof(b);
+    CLEAR(*to);
+    buf->len = recvmsg (sock->sd, &mesg, 0);
+    if (buf->len >= 0) 
+     {
+       struct cmsghdr *cmsg;
+       fromlen = mesg.msg_namelen;
+       cmsg = CMSG_FIRSTHDR (&mesg);
+       if (cmsg != NULL
+	   && CMSG_NXTHDR (&mesg, cmsg) == NULL
+	   && cmsg->cmsg_level == SOL_IP 
+	   && cmsg->cmsg_type == IP_PKTINFO
+	   && cmsg->cmsg_len >= (sizeof (struct cmsghdr)
+				 + sizeof (struct in_pktinfo))) 
+        {
+	       struct in_pktinfo *pkti = (struct in_pktinfo *) CMSG_DATA (cmsg);
+	       to->sin_addr = pkti->ipi_addr;
+        }
+      }
+  }
+#else
   buf->len = recvfrom (sock->sd, BPTR (buf), maxsize, 0,
 		       (struct sockaddr *) from, &fromlen);
+#endif
   if (fromlen != sizeof (*from))
     bad_address_length (fromlen, sizeof (*from));
   return buf->len;

diff ./socket.h~current~ ./socket.h

socket.h renamed from socket.h~current~ +53 -9
@@ -70,6 +70,7 @@ struct link_socket_addr
   struct sockaddr_in local;
   struct sockaddr_in remote; /* initial remote */
   struct sockaddr_in actual; /* remote may change due to --float */
+  struct sockaddr_in actuallocal; /* make sure we reply from  right address */
 };
 
 struct link_socket_info
@@ -273,6 +274,7 @@ in_addr_t link_socket_current_remote (co
 void link_socket_connection_initiated (const struct buffer *buf,
 				       struct link_socket_info *info,
 				       const struct sockaddr_in *addr,
+				       const struct sockaddr_in *localaddr,
 				       const char *common_name);
 
 void link_socket_bad_incoming_addr (struct buffer *buf,
@@ -443,7 +445,8 @@ link_socket_verify_incoming_addr (struct
 static inline void
 link_socket_get_outgoing_addr (struct buffer *buf,
 			      const struct link_socket_info *info,
-			      struct sockaddr_in *addr)
+			      struct sockaddr_in *addr,
+			      struct sockaddr_in *fromaddr)
 {
   if (buf->len > 0)
     {
@@ -453,6 +456,10 @@ link_socket_get_outgoing_addr (struct bu
 	  addr->sin_family = lsa->actual.sin_family;
 	  addr->sin_addr.s_addr = lsa->actual.sin_addr.s_addr;
 	  addr->sin_port = lsa->actual.sin_port;
+
+	  fromaddr->sin_family = lsa->actuallocal.sin_family;
+	  fromaddr->sin_addr.s_addr = lsa->actuallocal.sin_addr.s_addr;
+	  fromaddr->sin_port = lsa->actuallocal.sin_port;
 	}
       else
 	{
@@ -466,6 +473,7 @@ static inline void
 link_socket_set_outgoing_addr (const struct buffer *buf,
 			       struct link_socket_info *info,
 			       const struct sockaddr_in *addr,
+			       const struct sockaddr_in *localaddr,
 			       const char *common_name)
 {
   if (!buf || buf->len > 0)
@@ -481,8 +489,10 @@ link_socket_set_outgoing_addr (const str
 	      || addr_match_proto (addr, &lsa->remote, info->proto))
 	  )
 	{
-	  link_socket_connection_initiated (buf, info, addr, common_name);
+	  link_socket_connection_initiated (buf, info, addr, localaddr, common_name);
 	}
+      /* alway set localaddr address */
+      info->lsa->actuallocal = *localaddr;
     }
 }
 
@@ -528,7 +538,8 @@ link_socket_read_udp_win32 (struct link_
 int link_socket_read_udp_posix (struct link_socket *sock,
 				struct buffer *buf,
 				int maxsize,
-				struct sockaddr_in *from);
+				struct sockaddr_in *from,
+				struct sockaddr_in *to);
 
 #endif
 
@@ -537,16 +548,17 @@ static inline int
 link_socket_read (struct link_socket *sock,
 		  struct buffer *buf,
 		  int maxsize,
-		  struct sockaddr_in *from)
+		  struct sockaddr_in *from,
+		  struct sockaddr_in *to)
 {
   if (sock->info.proto == PROTO_UDPv4)
     {
       int res;
-
+      CLEAR (*to);
 #ifdef WIN32
       res = link_socket_read_udp_win32 (sock, buf, from);
 #else
-      res = link_socket_read_udp_posix (sock, buf, maxsize, from);
+      res = link_socket_read_udp_posix (sock, buf, maxsize, from, to);
 #endif
       return res;
     }
@@ -601,8 +613,39 @@ link_socket_write_win32 (struct link_soc
 static inline int
 link_socket_write_udp_posix (struct link_socket *sock,
 			     struct buffer *buf,
-			     struct sockaddr_in *to)
+			     struct sockaddr_in *to,
+			     struct sockaddr_in *from)
 {
+#ifdef IP_PKTINFO
+ if (addr_defined(from)) 
+   {
+     struct iovec iov;
+     struct msghdr mesg;
+     struct cmsghdr *cmsg;
+     struct in_pktinfo *pkti;
+
+     char b[sizeof(struct cmsghdr)+sizeof(struct in_pktinfo)];
+     iov.iov_base = BPTR (buf);
+     iov.iov_len = BLEN (buf);
+     mesg.msg_iov = &iov;
+     mesg.msg_iovlen = 1;
+     mesg.msg_name = to;
+     mesg.msg_namelen = sizeof(*to);
+     mesg.msg_control = b;
+     mesg.msg_controllen = sizeof(b);
+     mesg.msg_flags = 0;
+     cmsg = CMSG_FIRSTHDR(&mesg);
+     cmsg->cmsg_len = sizeof(b);
+     cmsg->cmsg_level = SOL_IP;
+     cmsg->cmsg_type = IP_PKTINFO;
+     pkti = (struct in_pktinfo *)CMSG_DATA(cmsg);
+     pkti->ipi_ifindex = 0;
+     pkti->ipi_spec_dst = from->sin_addr;
+     pkti->ipi_addr = from->sin_addr;
+
+     return sendmsg (sock->sd, &mesg, 0);
+   }
+#endif
   return sendto (sock->sd, BPTR (buf), BLEN (buf), 0,
 		 (struct sockaddr *) to,
 		 (socklen_t) sizeof (*to));
@@ -621,12 +664,13 @@ link_socket_write_tcp_posix (struct link
 static inline int
 link_socket_write_udp (struct link_socket *sock,
 		       struct buffer *buf,
-		       struct sockaddr_in *to)
+		       struct sockaddr_in *to,
+		       struct sockaddr_in *from)
 {
 #ifdef WIN32
   return link_socket_write_win32 (sock, buf, to);
 #else
-  return link_socket_write_udp_posix (sock, buf, to);
+  return link_socket_write_udp_posix (sock, buf, to, from);
 #endif
 }
 
@@ -634,11 +678,12 @@ link_socket_write_udp (struct link_socke
 static inline int
 link_socket_write (struct link_socket *sock,
 		   struct buffer *buf,

socket.h link_socket_write_udp (struct link_socke · line 688

Quoted 10 lines
socket.h renamed from socket.h~current~ +3 -2
-		   struct sockaddr_in *to)
+		   struct sockaddr_in *to,
+		   struct sockaddr_in *from)
 {
   if (sock->info.proto == PROTO_UDPv4)
     {
-      return link_socket_write_udp (sock, buf, to);
+      return link_socket_write_udp (sock, buf, to, from);
     }
   else if (sock->info.proto == PROTO_TCPv4_SERVER || sock->info.proto ==

PROTO_TCPv4_CLIENT)

Quoted 38 lines
socket.h renamed from socket.h~current~ +0 -0
     {

diff ./ssl.c~current~ ./ssl.c

ssl.c renamed from ssl.c~current~ +4 -0
@@ -1494,6 +1494,7 @@ write_control_auth (struct tls_session *
 		    struct key_state *ks,
 		    struct buffer *buf,
 		    struct sockaddr_in *to_link_addr,
+		    struct sockaddr_in *from_link_addr,
 		    int opcode,
 		    int max_ack,
 		    bool prepend_ack)
@@ -1514,6 +1515,7 @@ write_control_auth (struct tls_session *
       ASSERT (swap_hmac (buf, &session->tls_auth, false));
     }
   *to_link_addr = ks->remote_addr;
+  *from_link_addr = ks->local_addr;
 }
 
 /*
@@ -1916,6 +1918,7 @@ key_state_soft_reset (struct tls_session
   key_state_init (session, ks);
   ks->session_id_remote = ks_lame->session_id_remote;
   ks->remote_addr = ks_lame->remote_addr;
+  ks->local_addr = ks_lame->local_addr;
 }
 
 /*
@@ -1932,6 +1935,7 @@ tls_process (struct tls_multi *multi,
 	     struct tls_session *session,
 	     struct buffer *to_link,
 	     struct sockaddr_in *to_link_addr,
+	     struct sockaddr_in *from_link_addr,
 	     struct link_socket_info *to_link_socket_info,
 	     interval_t *wakeup)
 {

ssl.c tls_process (struct tls_multi *multi, · line 2055

Quoted 4 lines
ssl.c renamed from ssl.c~current~ +0 -1
@@ -2052,7 +2056,8 @@ tls_process (struct tls_multi *multi,
 		  INCR_SUCCESS;
 
 		  /* Set outgoing address for data channel packets */
-		  link_socket_set_outgoing_addr (NULL, to_link_socket_info,

&ks->remote_addr, session->common_name);

Quoted 26 lines
ssl.c renamed from ssl.c~current~ +6 -2
+		  link_socket_set_outgoing_addr (NULL, to_link_socket_info, &ks->remote_addr,
+						 &ks->local_addr, session->common_name);
 
 #ifdef MEASURE_TLS_HANDSHAKE_STATS
 		  show_tls_performance_stats();
@@ -2072,7 +2077,8 @@ tls_process (struct tls_multi *multi,
 	      b = *buf;
 	      INCR_SENT;
 
-	      write_control_auth (session, ks, &b, to_link_addr, opcode,
+	      write_control_auth (session, ks, &b, to_link_addr, 
+				  from_link_addr, opcode,
 				  CONTROL_SEND_ACK_MAX, true);
 	      *to_link = b;
 	      active = true;
@@ -2087,7 +2093,8 @@ tls_process (struct tls_multi *multi,
 	    {
 	      buf = &ks->ack_write_buf;
 	      ASSERT (buf_init (buf, FRAME_HEADROOM (&multi->opt.frame)));
-	      write_control_auth (session, ks, buf, to_link_addr, P_ACK_V1,
+	      write_control_auth (session, ks, buf, to_link_addr, from_link_addr,
+				  P_ACK_V1,
 				  RELIABLE_ACK_SIZE, false);
 	      *to_link = *buf;
 	      active = true;

ssl.c tls_process (struct tls_multi *multi, · line 2401

Quoted 5 lines
ssl.c renamed from ssl.c~current~ +1 -1
@@ -2391,7 +2398,7 @@ tls_process (struct tls_multi *multi,
     {
       buf = &ks->ack_write_buf;
       ASSERT (buf_init (buf, FRAME_HEADROOM (&multi->opt.frame)));
-      write_control_auth (session, ks, buf, to_link_addr, P_ACK_V1,
+      write_control_auth (session, ks, buf, to_link_addr, from_link_addr,

P_ACK_V1,

Quoted 13 lines
ssl.c renamed from ssl.c~current~ +1 -0
 			  RELIABLE_ACK_SIZE, false);
       *to_link = *buf;
       active = true;
@@ -2453,6 +2460,7 @@ bool
 tls_multi_process (struct tls_multi *multi,
 		   struct buffer *to_link,
 		   struct sockaddr_in *to_link_addr,
+		   struct sockaddr_in *from_link_addr,
 		   struct link_socket_info *to_link_socket_info,
 		   interval_t *wakeup)
 {
@@ -2473,8 +2481,10 @@ tls_multi_process (struct tls_multi *mul
 

ssl.c tls_multi_process (struct tls_multi *mul · line 2490

Quoted 10 lines
ssl.c renamed from ssl.c~current~ +3 -1
       /* set initial remote address */
       if (i == TM_ACTIVE && ks->state == S_INITIAL &&
-	  addr_defined (&to_link_socket_info->lsa->actual))
+	  addr_defined (&to_link_socket_info->lsa->actual)) {
 	ks->remote_addr = to_link_socket_info->lsa->actual;
+	ks->local_addr = to_link_socket_info->lsa->actuallocal;
+      }
 
       msg (D_TLS_DEBUG,
 	   "TLS: tls_multi_process: i=%d state=%s, mysid=%s, stored-sid=%s,

stored-ip=%s",

Quoted 38 lines
ssl.c renamed from ssl.c~current~ +4 -1
@@ -2488,7 +2498,7 @@ tls_multi_process (struct tls_multi *mul
 	{
 	  update_time ();
 
-	  if (tls_process (multi, session, to_link, to_link_addr,
+	  if (tls_process (multi, session, to_link, to_link_addr, from_link_addr,
 			   to_link_socket_info, wakeup))
 	    active = true;
 
@@ -2617,6 +2627,7 @@ tls_rec_payload (struct tls_multi *multi
 bool
 tls_pre_decrypt (struct tls_multi *multi,
 		 struct sockaddr_in *from,
+		 struct sockaddr_in *to,
 		 struct buffer *buf,
 		 struct crypto_options *opt)
 {
@@ -2919,6 +2930,7 @@ tls_pre_decrypt (struct tls_multi *multi
 	      {
 		ks->session_id_remote = sid;
 		ks->remote_addr = *from;
+		ks->local_addr = *to;
 		++multi->n_sessions;
 	      }
 	    else if (!addr_port_match (&ks->remote_addr, from))
@@ -3023,6 +3035,7 @@ tls_pre_decrypt (struct tls_multi *multi
 bool
 tls_pre_decrypt_lite (const struct tls_auth_standalone *tas,
 		      const struct sockaddr_in *from,
+		      const struct sockaddr_in *to,
 		      const struct buffer *buf)
 {
   struct gc_arena gc = gc_new ();

diff ./ssl.h~current~ ./ssl.h

ssl.h struct key_state · line 240

Quoted 5 lines
ssl.h renamed from ssl.h~current~ +1 -0
@@ -236,6 +236,7 @@ struct key_state
   int initial_opcode;		/* our initial P_ opcode */
   struct session_id session_id_remote; /* peer's random session ID */
   struct sockaddr_in remote_addr;      /* peer's IP addr */
+  struct sockaddr_in local_addr;       /* address peer expects reply from */
   struct packet_id packet_id;	       /* for data channel, to prevent replay

attacks */

ssl.h struct key_state · line 241

Quoted 2 lines
ssl.h renamed from ssl.h~current~ +0 -0
   struct key_ctx_bi key;	       /* data channel keys for

encrypt/decrypt/hmac */

Quoted 24 lines
ssl.h renamed from ssl.h~current~ +3 -0
@@ -456,6 +457,7 @@ void tls_multi_init_set_options(struct t
 bool tls_multi_process (struct tls_multi *multi,
 			struct buffer *to_link,
 			struct sockaddr_in *to_link_addr,
+			struct sockaddr_in *from_link_addr,
 			struct link_socket_info *to_link_socket_info,
 			interval_t *wakeup);
 
@@ -463,11 +465,13 @@ void tls_multi_free (struct tls_multi *m
 
 bool tls_pre_decrypt (struct tls_multi *multi,
 		      struct sockaddr_in *from,
+		      struct sockaddr_in *to,
 		      struct buffer *buf,
 		      struct crypto_options *opt);
 
 bool tls_pre_decrypt_lite (const struct tls_auth_standalone *tas,
 			   const struct sockaddr_in *from,
+			   const struct sockaddr_in *to,
 			   const struct buffer *buf);
 
 void tls_pre_encrypt (struct tls_multi *multi,
#2 lore raw
Quoted 8 lines · James Yonan

Neil,

Thanks for the patches... here are some comments:

* You've obviously put a lot of effort into understanding the code to write this patch. But did you consider any alternative approaches, such as starting multiple OpenVPN instances and binding each to a separate local interface?

This will fail for dynamic ip addresses and dynamic interfaces. Also it will incur memory and context switch time penalties. Just imagine a host with 100 ip addresses.
--
vda