--- /dev/null
+diff -ur openvpn-2.0_rc16/configure.ac openvpn-2.0_rc16MH/configure.ac
+--- openvpn-2.0_rc16/configure.ac 2005-02-20 11:46:15.000000000 -0700
++++ openvpn-2.0_rc16MH/configure.ac 2005-02-26 00:21:45.000000000 -0700
+@@ -89,6 +89,12 @@
+ [FRAGMENT="yes"]
+ )
+
++AC_ARG_ENABLE(multihome,
++ [ --disable-multihome Disable multi-homed UDP server support (--multihome)],
++ [MULTIHOME="$enableval"],
++ [MULTIHOME="yes"]
++)
++
+ AC_ARG_ENABLE(debug,
+ [ --disable-debug Disable debugging support (disable gremlin and verb 7+ messages)],
+ [DEBUG="$enableval"],
+@@ -326,6 +332,11 @@
+ [AC_DEFINE(HAVE_CMSGHDR, 1, [struct cmsghdr needed for extended socket error support])],
+ [],
+ [#include "syshead.h"])
++AC_CHECK_TYPE(
++ [struct in_pktinfo],
++ [AC_DEFINE(HAVE_IN_PKTINFO, 1, [struct in_pktinfo needed for IP_PKTINFO support])],
++ [],
++ [#include "syshead.h"])
+
+ AC_CHECK_SIZEOF(unsigned int)
+ AC_CHECK_SIZEOF(unsigned long)
+@@ -351,7 +362,7 @@
+ getpass strerror syslog openlog mlockall getgrnam setgid dnl
+ setgroups stat flock readv writev setsockopt getsockopt dnl
+ setsid chdir gettimeofday putenv getpeername unlink dnl
+- poll chsize ftruncate)
++ poll chsize ftruncate sendmsg recvmsg)
+ AC_CACHE_SAVE
+
+ dnl Required library functions
+@@ -568,6 +579,11 @@
+ AC_DEFINE(ENABLE_HTTP_PROXY, 1, [Enable HTTP proxy support])
+ fi
+
++dnl compile --multihome option
++if test "$MULTIHOME" = "yes"; then
++ AC_DEFINE(ENABLE_MULTIHOME, 1, [Enable multi-homed UDP server capability])
++fi
++
+ dnl enable debugging
+ if test "$DEBUG" = "yes"; then
+ AC_DEFINE(ENABLE_DEBUG, 1, [Enable debugging support])
+diff -ur openvpn-2.0_rc16/forward.c openvpn-2.0_rc16MH/forward.c
+--- openvpn-2.0_rc16/forward.c 2005-02-05 01:55:10.000000000 -0700
++++ openvpn-2.0_rc16MH/forward.c 2005-02-25 20:52:18.000000000 -0700
+@@ -563,12 +563,12 @@
+
+ static inline void
+ socks_preprocess_outgoing_link (struct context *c,
+- struct sockaddr_in **to_addr,
++ struct link_socket_actual **to_addr,
+ int *size_delta)
+ {
+ if (c->c2.link_socket->socks_proxy && c->c2.link_socket->info.proto == PROTO_UDPv4)
+ {
+- *size_delta += socks_process_outgoing_udp (&c->c2.to_link, &c->c2.to_link_addr);
++ *size_delta += socks_process_outgoing_udp (&c->c2.to_link, c->c2.to_link_addr);
+ *to_addr = &c->c2.link_socket->socks_relay;
+ }
+ }
+@@ -607,7 +607,11 @@
+
+ c->c2.buf = c->c2.buffers->read_link_buf;
+ ASSERT (buf_init (&c->c2.buf, FRAME_HEADROOM_ADJ (&c->c2.frame, FRAME_HEADROOM_MARKER_READ_LINK)));
+- 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.from);
+
+ if (socket_connection_reset (c->c2.link_socket, status))
+ {
+@@ -677,7 +681,7 @@
+ 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_link_socket_actual (&c->c2.from, &gc),
+ PROTO_DUMP (&c->c2.buf, &gc));
+
+ /*
+@@ -947,7 +951,7 @@
+ * packet to remote over the TCP/UDP port.
+ */
+ int size = 0;
+- ASSERT (addr_defined (&c->c2.to_link_addr));
++ ASSERT (link_socket_actual_defined (c->c2.to_link_addr));
+
+ #ifdef ENABLE_DEBUG
+ /* In gremlin-test mode, we may choose to drop this packet */
+@@ -982,12 +986,12 @@
+ msg (D_LINK_RW, "%s WRITE [%d] to %s: %s",
+ proto2ascii (c->c2.link_socket->info.proto, true),
+ BLEN (&c->c2.to_link),
+- print_sockaddr (&c->c2.to_link_addr, &gc),
++ print_link_socket_actual (c->c2.to_link_addr, &gc),
+ PROTO_DUMP (&c->c2.to_link, &gc));
+
+ /* Packet send complexified by possible Socks5 usage */
+ {
+- struct sockaddr_in *to_addr = &c->c2.to_link_addr;
++ struct link_socket_actual *to_addr = c->c2.to_link_addr;
+ #ifdef ENABLE_SOCKS
+ int size_delta = 0;
+ #endif
+@@ -997,7 +1001,9 @@
+ socks_preprocess_outgoing_link (c, &to_addr, &size_delta);
+ #endif
+ /* 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);
+
+ #ifdef ENABLE_SOCKS
+ /* Undo effect of prepend */
+@@ -1021,7 +1027,7 @@
+ if (size != BLEN (&c->c2.to_link))
+ msg (D_LINK_ERRORS,
+ "TCP/UDP packet was truncated/expanded on write to %s (tried=%d,actual=%d)",
+- print_sockaddr (&c->c2.to_link_addr, &gc),
++ print_link_socket_actual (c->c2.to_link_addr, &gc),
+ BLEN (&c->c2.to_link),
+ size);
+ }
+@@ -1030,7 +1036,7 @@
+ {
+ if (c->c2.to_link.len > 0)
+ msg (D_LINK_ERRORS, "TCP/UDP packet too large on write to %s (tried=%d,max=%d)",
+- print_sockaddr (&c->c2.to_link_addr, &gc),
++ print_link_socket_actual (c->c2.to_link_addr, &gc),
+ c->c2.to_link.len,
+ EXPANDED_SIZE (&c->c2.frame));
+ }
+diff -ur openvpn-2.0_rc16/init.c openvpn-2.0_rc16MH/init.c
+--- openvpn-2.0_rc16/init.c 2005-02-17 13:36:08.000000000 -0700
++++ openvpn-2.0_rc16MH/init.c 2005-02-25 21:12:42.000000000 -0700
+@@ -1651,6 +1651,13 @@
+ static void
+ do_init_socket_1 (struct context *c, int mode)
+ {
++ unsigned int flags = 0;
++
++#if ENABLE_IP_PKTINFO
++ if (c->options.multihome)
++ flags |= SF_USE_IP_PKTINFO;
++#endif
++
+ link_socket_init_phase1 (c->c2.link_socket,
+ c->options.local,
+ c->c1.remote_list,
+@@ -1677,7 +1684,8 @@
+ c->options.connect_retry_seconds,
+ c->options.mtu_discover_type,
+ c->options.rcvbuf,
+- c->options.sndbuf);
++ c->options.sndbuf,
++ flags);
+ }
+
+ /*
+diff -ur openvpn-2.0_rc16/manage.c openvpn-2.0_rc16MH/manage.c
+--- openvpn-2.0_rc16/manage.c 2005-01-14 21:04:11.000000000 -0700
++++ openvpn-2.0_rc16MH/manage.c 2005-02-24 20:13:31.000000000 -0700
+@@ -746,13 +746,16 @@
+ man_accept (struct management *man)
+ {
+ struct gc_arena gc = gc_new ();
++ struct link_socket_actual act;
+
+ /*
+ * Accept the TCP client.
+ */
+- man->connection.sd_cli = socket_do_accept (man->connection.sd_top, &man->connection.remote, false);
++ man->connection.sd_cli = socket_do_accept (man->connection.sd_top, &act, false);
+ if (socket_defined (man->connection.sd_cli))
+ {
++ man->connection.remote = act.dest;
++
+ if (socket_defined (man->connection.sd_top))
+ {
+ #ifdef WIN32
+@@ -1121,9 +1124,9 @@
+ /*
+ * Initialize socket address
+ */
+- ms->local.sin_family = AF_INET;
+- ms->local.sin_addr.s_addr = 0;
+- ms->local.sin_port = htons (port);
++ ms->local.sa.sin_family = AF_INET;
++ ms->local.sa.sin_addr.s_addr = 0;
++ ms->local.sa.sin_port = htons (port);
+
+ /*
+ * Run management over tunnel, or
+@@ -1135,7 +1138,7 @@
+ }
+ else
+ {
+- ms->local.sin_addr.s_addr = getaddr
++ ms->local.sa.sin_addr.s_addr = getaddr
+ (GETADDR_RESOLVE|GETADDR_WARN_ON_SIGNAL|GETADDR_FATAL, addr, 0, NULL, NULL);
+ }
+
+@@ -1382,7 +1385,7 @@
+ && man->connection.state == MS_INITIAL)
+ {
+ /* listen on our local TUN/TAP IP address */
+- man->settings.local.sin_addr.s_addr = htonl (tun_local_ip);
++ man->settings.local.sa.sin_addr.s_addr = htonl (tun_local_ip);
+ man_connection_init (man);
+ }
+
+diff -ur openvpn-2.0_rc16/manage.h openvpn-2.0_rc16MH/manage.h
+--- openvpn-2.0_rc16/manage.h 2005-01-09 17:46:29.000000000 -0700
++++ openvpn-2.0_rc16MH/manage.h 2005-02-24 20:01:50.000000000 -0700
+@@ -186,7 +186,7 @@
+
+ struct man_settings {
+ bool defined;
+- struct sockaddr_in local;
++ struct openvpn_sockaddr local;
+ bool up_query_passwords;
+ bool management_over_tunnel;
+ struct user_pass up;
+@@ -213,7 +213,7 @@
+
+ socket_descriptor_t sd_top;
+ socket_descriptor_t sd_cli;
+- struct sockaddr_in remote;
++ struct openvpn_sockaddr remote;
+
+ #ifdef WIN32
+ struct net_event_win32 ne32;
+diff -ur openvpn-2.0_rc16/mroute.c openvpn-2.0_rc16MH/mroute.c
+--- openvpn-2.0_rc16/mroute.c 2005-01-09 17:46:41.000000000 -0700
++++ openvpn-2.0_rc16MH/mroute.c 2005-02-24 20:20:57.000000000 -0700
+@@ -169,28 +169,29 @@
+ }
+
+ /*
+- * Translate a struct sockaddr_in (saddr)
++ * Translate a struct openvpn_sockaddr (osaddr)
+ * to a struct mroute_addr (addr).
+ */
+-bool
+-mroute_extract_sockaddr_in (struct mroute_addr *addr, const struct sockaddr_in *saddr, bool use_port)
++bool mroute_extract_openvpn_sockaddr (struct mroute_addr *addr,
++ const struct openvpn_sockaddr *osaddr,
++ bool use_port)
+ {
+- if (saddr->sin_family == AF_INET)
++ if (osaddr->sa.sin_family == AF_INET)
+ {
+ if (use_port)
+ {
+ addr->type = MR_ADDR_IPV4 | MR_WITH_PORT;
+ addr->netbits = 0;
+ addr->len = 6;
+- memcpy (addr->addr, &saddr->sin_addr.s_addr, 4);
+- memcpy (addr->addr + 4, &saddr->sin_port, 2);
++ memcpy (addr->addr, &osaddr->sa.sin_addr.s_addr, 4);
++ memcpy (addr->addr + 4, &osaddr->sa.sin_port, 2);
+ }
+ else
+ {
+ addr->type = MR_ADDR_IPV4;
+ addr->netbits = 0;
+ addr->len = 4;
+- memcpy (addr->addr, &saddr->sin_addr.s_addr, 4);
++ memcpy (addr->addr, &osaddr->sa.sin_addr.s_addr, 4);
+ }
+ return true;
+ }
+diff -ur openvpn-2.0_rc16/mroute.h openvpn-2.0_rc16MH/mroute.h
+--- openvpn-2.0_rc16/mroute.h 2005-01-09 17:46:41.000000000 -0700
++++ openvpn-2.0_rc16MH/mroute.h 2005-02-24 20:19:05.000000000 -0700
+@@ -95,9 +95,11 @@
+ struct buffer *buf,
+ int tunnel_type);
+
+-bool mroute_extract_sockaddr_in (struct mroute_addr *addr,
+- const struct sockaddr_in *saddr,
+- bool use_port);
++struct openvpn_sockaddr;
++
++bool mroute_extract_openvpn_sockaddr (struct mroute_addr *addr,
++ const struct openvpn_sockaddr *osaddr,
++ bool use_port);
+
+ bool mroute_learnable_address (const struct mroute_addr *addr);
+
+diff -ur openvpn-2.0_rc16/mtcp.c openvpn-2.0_rc16MH/mtcp.c
+--- openvpn-2.0_rc16/mtcp.c 2005-02-05 01:55:09.000000000 -0700
++++ openvpn-2.0_rc16MH/mtcp.c 2005-02-24 20:18:27.000000000 -0700
+@@ -160,7 +160,7 @@
+ ASSERT (mi->context.c2.link_socket);
+ ASSERT (mi->context.c2.link_socket->info.lsa);
+ ASSERT (mi->context.c2.link_socket->mode == LS_MODE_TCP_ACCEPT_FROM);
+- if (!mroute_extract_sockaddr_in (&mi->real, &mi->context.c2.link_socket->info.lsa->actual, true))
++ if (!mroute_extract_openvpn_sockaddr (&mi->real, &mi->context.c2.link_socket->info.lsa->actual.dest, true))
+ {
+ msg (D_MULTI_ERRORS, "MULTI TCP: TCP client address is undefined");
+ return false;
+diff -ur openvpn-2.0_rc16/mudp.c openvpn-2.0_rc16MH/mudp.c
+--- openvpn-2.0_rc16/mudp.c 2005-02-05 01:55:09.000000000 -0700
++++ openvpn-2.0_rc16MH/mudp.c 2005-02-24 20:23:14.000000000 -0700
+@@ -52,7 +52,7 @@
+ struct multi_instance *mi = NULL;
+ struct hash *hash = m->hash;
+
+- if (mroute_extract_sockaddr_in (&real, &m->top.c2.from, true))
++ if (mroute_extract_openvpn_sockaddr (&real, &m->top.c2.from.dest, true))
+ {
+ struct hash_element *he;
+ const uint32_t hv = hash_value (hash, &real);
+diff -ur openvpn-2.0_rc16/multi.c openvpn-2.0_rc16MH/multi.c
+--- openvpn-2.0_rc16/multi.c 2005-01-18 22:23:17.000000000 -0700
++++ openvpn-2.0_rc16MH/multi.c 2005-02-24 20:27:42.000000000 -0700
+@@ -939,13 +939,13 @@
+ in_addr_t a,
+ int netbits) /* -1 if host route, otherwise # of network bits in address */
+ {
+- struct sockaddr_in remote_si;
++ struct openvpn_sockaddr remote_si;
+ struct mroute_addr addr;
+
+ CLEAR (remote_si);
+- remote_si.sin_family = AF_INET;
+- remote_si.sin_addr.s_addr = htonl (a);
+- ASSERT (mroute_extract_sockaddr_in (&addr, &remote_si, false));
++ remote_si.sa.sin_family = AF_INET;
++ remote_si.sa.sin_addr.s_addr = htonl (a);
++ ASSERT (mroute_extract_openvpn_sockaddr (&addr, &remote_si, false));
+
+ if (netbits >= 0)
+ {
+@@ -2009,15 +2009,15 @@
+ struct multi_context *m = (struct multi_context *) arg;
+ struct hash_iterator hi;
+ struct hash_element *he;
+- struct sockaddr_in saddr;
++ struct openvpn_sockaddr saddr;
+ struct mroute_addr maddr;
+ int count = 0;
+
+ CLEAR (saddr);
+- saddr.sin_family = AF_INET;
+- saddr.sin_addr.s_addr = htonl (addr);
+- saddr.sin_port = htons (port);
+- if (mroute_extract_sockaddr_in (&maddr, &saddr, true))
++ saddr.sa.sin_family = AF_INET;
++ saddr.sa.sin_addr.s_addr = htonl (addr);
++ saddr.sa.sin_port = htons (port);
++ if (mroute_extract_openvpn_sockaddr (&maddr, &saddr, true))
+ {
+ hash_iterator_init (m->iter, &hi, true);
+ while ((he = hash_iterator_next (&hi)))
+diff -ur openvpn-2.0_rc16/openvpn.h openvpn-2.0_rc16MH/openvpn.h
+--- openvpn-2.0_rc16/openvpn.h 2005-01-09 17:46:29.000000000 -0700
++++ openvpn-2.0_rc16MH/openvpn.h 2005-02-24 18:53:27.000000000 -0700
+@@ -210,8 +210,8 @@
+ struct link_socket_info *link_socket_info;
+ 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 link_socket_actual *to_link_addr; /* IP address of remote */
++ struct link_socket_actual from; /* address of incoming datagram */
+
+ /* MTU frame parameters */
+ struct frame frame;
+diff -ur openvpn-2.0_rc16/options.c openvpn-2.0_rc16MH/options.c
+--- openvpn-2.0_rc16/options.c 2005-02-10 19:18:08.000000000 -0700
++++ openvpn-2.0_rc16MH/options.c 2005-02-25 21:11:12.000000000 -0700
+@@ -172,6 +172,9 @@
+ "--ping-timer-rem: Run the --ping-exit/--ping-restart timer only if we have a\n"
+ " remote address.\n"
+ "--ping n : Ping remote once every n seconds over TCP/UDP port.\n"
++#if ENABLE_IP_PKTINFO
++ "--multihome : Configure a multi-homed UDP server.\n"
++#endif
+ "--fast-io : (experimental) Optimize TUN/TAP/UDP writes.\n"
+ #ifdef ENABLE_OCC
+ "--explicit-exit-notify n : (experimental) on exit, send exit signal to remote.\n"
+@@ -1034,6 +1037,10 @@
+ SHOW_INT (rcvbuf);
+ SHOW_INT (sndbuf);
+
++#if ENABLE_IP_PKTINFO
++ SHOW_BOOL (multihome);
++#endif
++
+ #ifdef ENABLE_HTTP_PROXY
+ if (o->http_proxy_options)
+ show_http_proxy_options (o->http_proxy_options);
+@@ -2997,6 +3004,13 @@
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->mlock = true;
+ }
++#if ENABLE_IP_PKTINFO
++ else if (streq (p[0], "multihome"))
++ {
++ VERIFY_PERMISSION (OPT_P_GENERAL);
++ options->multihome = true;
++ }
++#endif
+ else if (streq (p[0], "verb") && p[1])
+ {
+ ++i;
+diff -ur openvpn-2.0_rc16/options.h openvpn-2.0_rc16MH/options.h
+--- openvpn-2.0_rc16/options.h 2005-02-10 16:37:25.000000000 -0700
++++ openvpn-2.0_rc16MH/options.h 2005-02-25 21:11:13.000000000 -0700
+@@ -223,6 +223,10 @@
+ /* optimize TUN/TAP/UDP writes */
+ bool fast_io;
+
++#if ENABLE_IP_PKTINFO
++ bool multihome;
++#endif
++
+ #ifdef USE_LZO
+ bool comp_lzo;
+ bool comp_lzo_adaptive;
+diff -ur openvpn-2.0_rc16/ping-inline.h openvpn-2.0_rc16MH/ping-inline.h
+--- openvpn-2.0_rc16/ping-inline.h 2005-01-09 17:46:41.000000000 -0700
++++ openvpn-2.0_rc16MH/ping-inline.h 2005-02-24 19:56:53.000000000 -0700
+@@ -38,7 +38,7 @@
+ && event_timeout_trigger (&c->c2.ping_rec_interval,
+ &c->c2.timeval,
+ (!c->options.ping_timer_remote
+- || addr_defined (&c->c1.link_socket_addr.actual))
++ || link_socket_actual_defined (&c->c1.link_socket_addr.actual))
+ ? ETT_DEFAULT : 15))
+ check_ping_restart_dowork (c);
+ }
+Only in openvpn-2.0_rc16MH/plugin: common
+diff -ur openvpn-2.0_rc16/socket.c openvpn-2.0_rc16MH/socket.c
+--- openvpn-2.0_rc16/socket.c 2005-02-05 01:42:13.000000000 -0700
++++ openvpn-2.0_rc16MH/socket.c 2005-02-25 21:41:34.000000000 -0700
+@@ -237,7 +237,7 @@
+
+ static void
+ update_remote (const char* host,
+- struct sockaddr_in *addr,
++ struct openvpn_sockaddr *addr,
+ bool *changed)
+ {
+ if (host && addr)
+@@ -248,9 +248,9 @@
+ 1,
+ NULL,
+ NULL);
+- if (new_addr && addr->sin_addr.s_addr != new_addr)
++ if (new_addr && addr->sa.sin_addr.s_addr != new_addr)
+ {
+- addr->sin_addr.s_addr = new_addr;
++ addr->sa.sin_addr.s_addr = new_addr;
+ *changed = true;
+ }
+ }
+@@ -440,12 +440,19 @@
+ }
+
+ static socket_descriptor_t
+-create_socket_udp (void)
++create_socket_udp (const unsigned int flags)
+ {
+ socket_descriptor_t sd;
+
+ if ((sd = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
+ msg (M_SOCKERR, "UDP: Cannot create UDP socket");
++#if ENABLE_IP_PKTINFO
++ else if (flags & SF_USE_IP_PKTINFO)
++ {
++ int pad = 1;
++ setsockopt (sd, SOL_IP, IP_PKTINFO, (void*)&pad, sizeof(pad));
++ }
++#endif
+ return sd;
+ }
+
+@@ -455,7 +462,7 @@
+ /* create socket */
+ if (sock->info.proto == PROTO_UDPv4)
+ {
+- sock->sd = create_socket_udp ();
++ sock->sd = create_socket_udp (sock->socket_flags);
+
+ #ifdef ENABLE_SOCKS
+ if (sock->socks_proxy)
+@@ -479,7 +486,7 @@
+
+ static void
+ socket_do_listen (socket_descriptor_t sd,
+- const struct sockaddr_in *local,
++ const struct openvpn_sockaddr *local,
+ bool do_listen,
+ bool do_set_nonblock)
+ {
+@@ -501,16 +508,18 @@
+
+ socket_descriptor_t
+ socket_do_accept (socket_descriptor_t sd,
+- struct sockaddr_in *remote,
++ struct link_socket_actual *act,
+ const bool nowait)
+ {
+- socklen_t remote_len = sizeof (*remote);
++ socklen_t remote_len = sizeof (act->dest.sa);
+ socket_descriptor_t new_sd = SOCKET_UNDEFINED;
+
++ CLEAR (*act);
++
+ #ifdef HAVE_GETPEERNAME
+ if (nowait)
+ {
+- new_sd = getpeername (sd, (struct sockaddr *) remote, &remote_len);
++ new_sd = getpeername (sd, (struct sockaddr *) &act->dest.sa, &remote_len);
+
+ if (!socket_defined (new_sd))
+ msg (D_LINK_ERRORS | M_ERRNO_SOCK, "TCP: getpeername() failed");
+@@ -523,14 +532,14 @@
+ #endif
+ else
+ {
+- new_sd = accept (sd, (struct sockaddr *) remote, &remote_len);
++ new_sd = accept (sd, (struct sockaddr *) &act->dest.sa, &remote_len);
+ }
+
+ if (!socket_defined (new_sd))
+ {
+ msg (D_LINK_ERRORS | M_ERRNO_SOCK, "TCP: accept(%d) failed", sd);
+ }
+- else if (remote_len != sizeof (*remote))
++ else if (remote_len != sizeof (act->dest.sa))
+ {
+ msg (D_LINK_ERRORS, "TCP: Received strange incoming connection with unknown address length=%d", remote_len);
+ openvpn_close_socket (new_sd);
+@@ -540,28 +549,30 @@
+ }
+
+ static void
+-tcp_connection_established (const struct sockaddr_in *remote)
++tcp_connection_established (const struct link_socket_actual *act)
+ {
+ struct gc_arena gc = gc_new ();
+ msg (M_INFO, "TCP connection established with %s",
+- print_sockaddr (remote, &gc));
++ print_link_socket_actual (act, &gc));
+ gc_free (&gc);
+ }
+
+ static int
+ socket_listen_accept (socket_descriptor_t sd,
+- struct sockaddr_in *remote,
++ struct link_socket_actual *act,
+ const char *remote_dynamic,
+ bool *remote_changed,
+- const struct sockaddr_in *local,
++ const struct openvpn_sockaddr *local,
+ bool do_listen,
+ bool nowait,
+ volatile int *signal_received)
+ {
+ struct gc_arena gc = gc_new ();
+- struct sockaddr_in remote_verify = *remote;
++ //struct openvpn_sockaddr *remote = &act->dest;
++ struct openvpn_sockaddr remote_verify = act->dest;
+ int new_sd = SOCKET_UNDEFINED;
+
++ CLEAR (*act);
+ socket_do_listen (sd, local, do_listen, true);
+
+ while (true)
+@@ -590,17 +601,17 @@
+ if (status <= 0)
+ continue;
+
+- new_sd = socket_do_accept (sd, remote, nowait);
++ new_sd = socket_do_accept (sd, act, nowait);
+
+ if (socket_defined (new_sd))
+ {
+ update_remote (remote_dynamic, &remote_verify, remote_changed);
+ if (addr_defined (&remote_verify)
+- && !addr_match (&remote_verify, remote))
++ && !addr_match (&remote_verify, &act->dest))
+ {
+ msg (M_WARN,
+ "TCP NOTE: Rejected connection attempt from %s due to --remote setting",
+- print_sockaddr (remote, &gc));
++ print_link_socket_actual (act, &gc));
+ if (openvpn_close_socket (new_sd))
+ msg (M_SOCKERR, "TCP: close socket failed (new_sd)");
+ }
+@@ -613,7 +624,7 @@
+ if (!nowait && openvpn_close_socket (sd))
+ msg (M_SOCKERR, "TCP: close socket failed (sd)");
+
+- tcp_connection_established (remote);
++ tcp_connection_established (act);
+
+ gc_free (&gc);
+ return new_sd;
+@@ -621,7 +632,7 @@
+
+ static void
+ socket_connect (socket_descriptor_t *sd,
+- struct sockaddr_in *remote,
++ struct openvpn_sockaddr *remote,
+ struct remote_list *remote_list,
+ const char *remote_dynamic,
+ bool *remote_changed,
+@@ -634,8 +645,8 @@
+ print_sockaddr (remote, &gc));
+ while (true)
+ {
+- const int status = connect (*sd, (struct sockaddr *) remote,
+- sizeof (*remote));
++ const int status = connect (*sd, (struct sockaddr *) &remote->sa,
++ sizeof (remote->sa));
+
+ get_signal (signal_received);
+ if (*signal_received)
+@@ -656,7 +667,7 @@
+ {
+ remote_list_next (remote_list);
+ remote_dynamic = remote_list_host (remote_list);
+- remote->sin_port = htons (remote_list_port (remote_list));
++ remote->sa.sin_port = htons (remote_list_port (remote_list));
+ *remote_changed = true;
+ }
+
+@@ -716,22 +727,22 @@
+ /* resolve local address if undefined */
+ if (!addr_defined (&sock->info.lsa->local))
+ {
+- sock->info.lsa->local.sin_family = AF_INET;
+- sock->info.lsa->local.sin_addr.s_addr =
++ sock->info.lsa->local.sa.sin_family = AF_INET;
++ sock->info.lsa->local.sa.sin_addr.s_addr =
+ (sock->local_host ? getaddr (GETADDR_RESOLVE | GETADDR_WARN_ON_SIGNAL | GETADDR_FATAL,
+ sock->local_host,
+ 0,
+ NULL,
+ NULL)
+ : htonl (INADDR_ANY));
+- sock->info.lsa->local.sin_port = htons (sock->local_port);
++ sock->info.lsa->local.sa.sin_port = htons (sock->local_port);
+ }
+
+ /* bind to local address/port */
+ if (sock->bind_local)
+ {
+- if (bind (sock->sd, (struct sockaddr *) &sock->info.lsa->local,
+- sizeof (sock->info.lsa->local)))
++ if (bind (sock->sd, (struct sockaddr *) &sock->info.lsa->local.sa,
++ sizeof (sock->info.lsa->local.sa)))
+ {
+ const int errnum = openvpn_errno_socket ();
+ msg (M_FATAL, "TCP/UDP: Socket bind failed on local address %s: %s",
+@@ -755,8 +766,8 @@
+ /* resolve remote address if undefined */
+ if (!addr_defined (&sock->info.lsa->remote))
+ {
+- sock->info.lsa->remote.sin_family = AF_INET;
+- sock->info.lsa->remote.sin_addr.s_addr = 0;
++ sock->info.lsa->remote.sa.sin_family = AF_INET;
++ sock->info.lsa->remote.sa.sin_addr.s_addr = 0;
+
+ if (sock->remote_host)
+ {
+@@ -794,7 +805,7 @@
+ ASSERT (0);
+ }
+
+- sock->info.lsa->remote.sin_addr.s_addr = getaddr (
++ sock->info.lsa->remote.sa.sin_addr.s_addr = getaddr (
+ flags,
+ sock->remote_host,
+ retry,
+@@ -821,19 +832,22 @@
+ }
+ }
+
+- sock->info.lsa->remote.sin_port = htons (sock->remote_port);
++ sock->info.lsa->remote.sa.sin_port = htons (sock->remote_port);
+ }
+
+ /* should we re-use previous active remote address? */
+- if (addr_defined (&sock->info.lsa->actual))
++ if (link_socket_actual_defined (&sock->info.lsa->actual))
+ {
+ msg (M_INFO, "TCP/UDP: Preserving recently used remote address: %s",
+- print_sockaddr (&sock->info.lsa->actual, &gc));
++ print_link_socket_actual (&sock->info.lsa->actual, &gc));
+ if (remote_dynamic)
+ *remote_dynamic = NULL;
+ }
+ else
+- sock->info.lsa->actual = sock->info.lsa->remote;
++ {
++ CLEAR (sock->info.lsa->actual);
++ sock->info.lsa->actual.dest = sock->info.lsa->remote;
++ }
+
+ /* remember that we finished */
+ sock->did_resolve_remote = true;
+@@ -884,7 +898,8 @@
+ int connect_retry_seconds,
+ int mtu_discover_type,
+ int rcvbuf,
+- int sndbuf)
++ int sndbuf,
++ const unsigned int socket_flags)
+ {
+ const char *remote_host;
+ int remote_port;
+@@ -920,6 +935,8 @@
+ sock->socket_buffer_sizes.rcvbuf = rcvbuf;
+ sock->socket_buffer_sizes.sndbuf = sndbuf;
+
++ sock->socket_flags = socket_flags;
++
+ sock->info.proto = proto;
+ sock->info.remote_float = remote_float;
+ sock->info.lsa = lsa;
+@@ -1097,7 +1114,7 @@
+ else if (sock->info.proto == PROTO_TCPv4_CLIENT)
+ {
+ socket_connect (&sock->sd,
+- &sock->info.lsa->actual,
++ &sock->info.lsa->actual.dest,
+ sock->remote_list,
+ remote_dynamic,
+ &remote_changed,
+@@ -1135,7 +1152,7 @@
+ else if (sock->info.proto == PROTO_UDPv4 && sock->socks_proxy)
+ {
+ socket_connect (&sock->ctrl_sd,
+- &sock->info.lsa->actual,
++ &sock->info.lsa->actual.dest,
+ NULL,
+ remote_dynamic,
+ &remote_changed,
+@@ -1147,7 +1164,8 @@
+
+ establish_socks_proxy_udpassoc (sock->socks_proxy,
+ sock->ctrl_sd,
+- sock->sd, &sock->socks_relay,
++ sock->sd,
++ &sock->socks_relay.dest,
+ signal_received);
+
+ if (*signal_received)
+@@ -1156,8 +1174,9 @@
+ sock->remote_host = sock->proxy_dest_host;
+ sock->remote_port = sock->proxy_dest_port;
+ sock->did_resolve_remote = false;
+- sock->info.lsa->actual.sin_addr.s_addr = 0;
+- sock->info.lsa->remote.sin_addr.s_addr = 0;
++
++ sock->info.lsa->actual.dest.sa.sin_addr.s_addr = 0;
++ sock->info.lsa->remote.sa.sin_addr.s_addr = 0;
+
+ resolve_remote (sock, 1, NULL, signal_received);
+
+@@ -1172,7 +1191,7 @@
+ if (remote_changed)
+ {
+ msg (M_INFO, "TCP/UDP: Dynamic remote address changed during TCP connection establishment");
+- sock->info.lsa->remote.sin_addr.s_addr = sock->info.lsa->actual.sin_addr.s_addr;
++ sock->info.lsa->remote.sa.sin_addr.s_addr = sock->info.lsa->actual.dest.sa.sin_addr.s_addr;
+ }
+ }
+
+@@ -1206,12 +1225,15 @@
+ msg (M_INFO, "%s link local%s: %s",
+ proto2ascii (sock->info.proto, true),
+ (sock->bind_local ? " (bound)" : ""),
+- print_sockaddr_ex (&sock->info.lsa->local, sock->bind_local, ":", &gc));
++ print_sockaddr_ex (&sock->info.lsa->local, ":", sock->bind_local ? PS_SHOW_PORT : 0, &gc));
+
+ /* print active remote address */
+ msg (M_INFO, "%s link remote: %s",
+ proto2ascii (sock->info.proto, true),
+- print_sockaddr_ex (&sock->info.lsa->actual, addr_defined (&sock->info.lsa->actual), ":", &gc));
++ print_link_socket_actual_ex (&sock->info.lsa->actual,
++ ":",
++ PS_SHOW_PORT_IF_DEFINED,
++ &gc));
+
+ done:
+ gc_free (&gc);
+@@ -1276,19 +1298,19 @@
+ void
+ setenv_trusted (struct env_set *es, const struct link_socket_info *info)
+ {
+- setenv_sockaddr (es, "trusted", &info->lsa->actual, SA_IP_PORT);
++ setenv_link_socket_actual (es, "trusted", &info->lsa->actual, SA_IP_PORT);
+ }
+
+ void
+ link_socket_connection_initiated (const struct buffer *buf,
+ struct link_socket_info *info,
+- const struct sockaddr_in *addr,
++ const struct link_socket_actual *act,
+ const char *common_name,
+ struct env_set *es)
+ {
+ struct gc_arena gc = gc_new ();
+
+- info->lsa->actual = *addr; /* Note: skip this line for --force-dest */
++ info->lsa->actual = *act; /* Note: skip this line for --force-dest */
+ setenv_trusted (es, info);
+ info->connection_established = true;
+
+@@ -1297,7 +1319,7 @@
+ struct buffer out = alloc_buf_gc (256, &gc);
+ if (common_name)
+ buf_printf (&out, "[%s] ", common_name);
+- buf_printf (&out, "Peer Connection Initiated with %s", print_sockaddr (&info->lsa->actual, &gc));
++ buf_printf (&out, "Peer Connection Initiated with %s", print_link_socket_actual (&info->lsa->actual, &gc));
+ msg (M_INFO, "%s", BSTR (&out));
+ }
+
+@@ -1307,7 +1329,7 @@
+ /* Process --ipchange plugin */
+ if (plugin_defined (info->plugins, OPENVPN_PLUGIN_IPCHANGE))
+ {
+- const char *addr_ascii = print_sockaddr_ex (&info->lsa->actual, true, " ", &gc);
++ const char *addr_ascii = print_sockaddr_ex (&info->lsa->actual.dest, " ", PS_SHOW_PORT, &gc);
+ if (plugin_call (info->plugins, OPENVPN_PLUGIN_IPCHANGE, addr_ascii, es))
+ msg (M_WARN, "WARNING: ipchange plugin call failed");
+ }
+@@ -1319,7 +1341,7 @@
+ setenv_str (es, "script_type", "ipchange");
+ buf_printf (&out, "%s %s",
+ info->ipchange_command,
+- print_sockaddr_ex (&info->lsa->actual, true, " ", &gc));
++ print_sockaddr_ex (&info->lsa->actual.dest, " ", PS_SHOW_PORT, &gc));
+ system_check (BSTR (&out), es, S_SCRIPT, "ip-change command failed");
+ }
+
+@@ -1329,14 +1351,14 @@
+ void
+ link_socket_bad_incoming_addr (struct buffer *buf,
+ const struct link_socket_info *info,
+- const struct sockaddr_in *from_addr)
++ const struct link_socket_actual *from_addr)
+ {
+ struct gc_arena gc = gc_new ();
+
+ msg (D_LINK_ERRORS,
+ "TCP/UDP: Incoming packet rejected from %s[%d], expected peer address: %s (allow this incoming source address/port by removing --remote or adding --float)",
+- print_sockaddr (from_addr, &gc),
+- (int)from_addr->sin_family,
++ print_link_socket_actual (from_addr, &gc),
++ (int)from_addr->dest.sa.sin_family,
+ print_sockaddr (&info->lsa->remote, &gc));
+ buf->len = 0;
+
+@@ -1354,10 +1376,10 @@
+ {
+ const struct link_socket_addr *lsa = info->lsa;
+
+- if (addr_defined (&lsa->actual))
+- return ntohl (lsa->actual.sin_addr.s_addr);
++ if (link_socket_actual_defined (&lsa->actual))
++ return ntohl (lsa->actual.dest.sa.sin_addr.s_addr);
+ else if (addr_defined (&lsa->remote))
+- return ntohl (lsa->remote.sin_addr.s_addr);
++ return ntohl (lsa->remote.sa.sin_addr.s_addr);
+ else
+ return 0;
+ }
+@@ -1550,29 +1572,69 @@
+ */
+
+ const char *
+-print_sockaddr (const struct sockaddr_in *addr, struct gc_arena *gc)
++print_sockaddr (const struct openvpn_sockaddr *addr, struct gc_arena *gc)
+ {
+- return print_sockaddr_ex(addr, true, ":", gc);
++ return print_sockaddr_ex (addr, ":", PS_SHOW_PORT, gc);
+ }
+
+ const char *
+-print_sockaddr_ex (const struct sockaddr_in *addr, bool do_port, const char* separator, struct gc_arena *gc)
++print_sockaddr_ex (const struct openvpn_sockaddr *addr,
++ const char* separator,
++ const unsigned int flags,
++ struct gc_arena *gc)
+ {
+- struct buffer out = alloc_buf_gc (64, gc);
+- const int port = ntohs (addr->sin_port);
++ if (addr)
++ {
++ struct buffer out = alloc_buf_gc (64, gc);
++ const int port = ntohs (addr->sa.sin_port);
+
+- mutex_lock_static (L_INET_NTOA);
+- buf_printf (&out, "%s", (addr_defined (addr) ? inet_ntoa (addr->sin_addr) : "[undef]"));
+- mutex_unlock_static (L_INET_NTOA);
++ mutex_lock_static (L_INET_NTOA);
++ buf_printf (&out, "%s", (addr_defined (addr) ? inet_ntoa (addr->sa.sin_addr) : "[undef]"));
++ mutex_unlock_static (L_INET_NTOA);
+
+- if (do_port && port)
+- {
+- if (separator)
+- buf_printf (&out, "%s", separator);
++ if (((flags & PS_SHOW_PORT) || (addr_defined (addr) && (flags & PS_SHOW_PORT_IF_DEFINED)))
++ && port)
++ {
++ if (separator)
++ buf_printf (&out, "%s", separator);
+
+- buf_printf (&out, "%d", port);
++ buf_printf (&out, "%d", port);
++ }
++ return BSTR (&out);
+ }
+- return BSTR (&out);
++ else
++ return "[NULL]";
++}
++
++const char *
++print_link_socket_actual (const struct link_socket_actual *act, struct gc_arena *gc)
++{
++ return print_link_socket_actual_ex (act, ":", PS_SHOW_PORT|PS_SHOW_PKTINFO, gc);
++}
++
++const char *
++print_link_socket_actual_ex (const struct link_socket_actual *act,
++ const char *separator,
++ const unsigned int flags,
++ struct gc_arena *gc)
++{
++ if (act)
++ {
++ struct buffer out = alloc_buf_gc (128, gc);
++ buf_printf (&out, "%s", print_sockaddr_ex (&act->dest, separator, flags, gc));
++#if ENABLE_IP_PKTINFO
++ if ((flags & PS_SHOW_PKTINFO) && act->pi.ipi_spec_dst.s_addr)
++ {
++ struct openvpn_sockaddr sa;
++ CLEAR (sa);
++ sa.sa.sin_addr = act->pi.ipi_spec_dst;
++ buf_printf (&out, " (via %s)", print_sockaddr_ex (&sa, separator, 0, gc));
++ }
++#endif
++ return BSTR (&out);
++ }
++ else
++ return "[NULL]";
+ }
+
+ /*
+@@ -1599,7 +1661,7 @@
+
+ /* set environmental variables for ip/port in *addr */
+ void
+-setenv_sockaddr (struct env_set *es, const char *name_prefix, const struct sockaddr_in *addr, const bool flags)
++setenv_sockaddr (struct env_set *es, const char *name_prefix, const struct openvpn_sockaddr *addr, const bool flags)
+ {
+ char name_buf[256];
+
+@@ -1609,13 +1671,13 @@
+ openvpn_snprintf (name_buf, sizeof (name_buf), "%s", name_prefix);
+
+ mutex_lock_static (L_INET_NTOA);
+- setenv_str (es, name_buf, inet_ntoa (addr->sin_addr));
++ setenv_str (es, name_buf, inet_ntoa (addr->sa.sin_addr));
+ mutex_unlock_static (L_INET_NTOA);
+
+- if ((flags & SA_IP_PORT) && addr->sin_port)
++ if ((flags & SA_IP_PORT) && addr->sa.sin_port)
+ {
+ openvpn_snprintf (name_buf, sizeof (name_buf), "%s_port", name_prefix);
+- setenv_int (es, name_buf, ntohs (addr->sin_port));
++ setenv_int (es, name_buf, ntohs (addr->sa.sin_port));
+ }
+ }
+
+@@ -1624,13 +1686,22 @@
+ {
+ if (addr || !(flags & SA_SET_IF_NONZERO))
+ {
+- struct sockaddr_in si;
++ struct openvpn_sockaddr si;
+ CLEAR (si);
+- si.sin_addr.s_addr = htonl (addr);
++ si.sa.sin_addr.s_addr = htonl (addr);
+ setenv_sockaddr (es, name_prefix, &si, flags);
+ }
+ }
+
++void
++setenv_link_socket_actual (struct env_set *es,
++ const char *name_prefix,
++ const struct link_socket_actual *act,
++ const bool flags)
++{
++ setenv_sockaddr (es, name_prefix, &act->dest, flags);
++}
++
+ /*
+ * Convert protocol names between index and ascii form.
+ */
+@@ -1760,19 +1831,72 @@
+
+ #ifndef WIN32
+
++#if ENABLE_IP_PKTINFO
++
++struct openvpn_pktinfo
++{
++ struct cmsghdr cmsghdr;
++ struct in_pktinfo in_pktinfo;
++};
++
++static socklen_t
++link_socket_read_udp_posix_recvmsg (struct link_socket *sock,
++ struct buffer *buf,
++ int maxsize,
++ struct link_socket_actual *from)
++{
++ struct iovec iov;
++ struct openvpn_pktinfo opi;
++ struct msghdr mesg;
++ socklen_t fromlen = sizeof (from->dest.sa);
++
++ iov.iov_base = BPTR (buf);
++ iov.iov_len = maxsize;
++ mesg.msg_iov = &iov;
++ mesg.msg_iovlen = 1;
++ mesg.msg_name = &from->dest.sa;
++ mesg.msg_namelen = fromlen;
++ mesg.msg_control = &opi;
++ mesg.msg_controllen = sizeof (opi);
++ 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 (opi))
++ {
++ struct in_pktinfo *pkti = (struct in_pktinfo *) CMSG_DATA (cmsg);
++ from->pi.ipi_ifindex = pkti->ipi_ifindex;
++ from->pi.ipi_spec_dst = pkti->ipi_spec_dst;
++ }
++ }
++ return fromlen;
++}
++#endif
++
+ int
+ link_socket_read_udp_posix (struct link_socket *sock,
+ struct buffer *buf,
+ int maxsize,
+- struct sockaddr_in *from)
++ struct link_socket_actual *from)
+ {
+- socklen_t fromlen = sizeof (*from);
+- CLEAR (*from);
++ socklen_t fromlen = sizeof (from->dest.sa);
++ from->dest.sa.sin_addr.s_addr = 0;
+ ASSERT (buf_safe (buf, maxsize));
+- buf->len = recvfrom (sock->sd, BPTR (buf), maxsize, 0,
+- (struct sockaddr *) from, &fromlen);
+- if (fromlen != sizeof (*from))
+- bad_address_length (fromlen, sizeof (*from));
++#if ENABLE_IP_PKTINFO
++ if (sock->socket_flags & SF_USE_IP_PKTINFO)
++ fromlen = link_socket_read_udp_posix_recvmsg (sock, buf, maxsize, from);
++ else
++#endif
++ buf->len = recvfrom (sock->sd, BPTR (buf), maxsize, 0,
++ (struct sockaddr *) &from->dest.sa, &fromlen);
++ if (fromlen != sizeof (from->dest.sa))
++ bad_address_length (fromlen, sizeof (from->dest.sa));
+ return buf->len;
+ }
+
+@@ -1785,7 +1909,7 @@
+ int
+ link_socket_write_tcp (struct link_socket *sock,
+ struct buffer *buf,
+- struct sockaddr_in *to)
++ struct link_socket_actual *to)
+ {
+ packet_size_type len = BLEN (buf);
+ dmsg (D_STREAM_DEBUG, "STREAM: WRITE %d offset=%d", (int)len, buf->offset);
+@@ -1799,6 +1923,41 @@
+ #endif
+ }
+
++#if ENABLE_IP_PKTINFO
++
++int
++link_socket_write_udp_posix_sendmsg (struct link_socket *sock,
++ struct buffer *buf,
++ struct link_socket_actual *to)
++{
++ struct iovec iov;
++ struct msghdr mesg;
++ struct cmsghdr *cmsg;
++ struct in_pktinfo *pkti;
++ struct openvpn_pktinfo opi;
++
++ iov.iov_base = BPTR (buf);
++ iov.iov_len = BLEN (buf);
++ mesg.msg_iov = &iov;
++ mesg.msg_iovlen = 1;
++ mesg.msg_name = &to->dest.sa;
++ mesg.msg_namelen = sizeof (to->dest.sa);
++ mesg.msg_control = &opi;
++ mesg.msg_controllen = sizeof (opi);
++ mesg.msg_flags = 0;
++ cmsg = CMSG_FIRSTHDR (&mesg);
++ cmsg->cmsg_len = sizeof (opi);
++ cmsg->cmsg_level = SOL_IP;
++ cmsg->cmsg_type = IP_PKTINFO;
++ pkti = (struct in_pktinfo *) CMSG_DATA (cmsg);
++ pkti->ipi_ifindex = to->pi.ipi_ifindex;
++ pkti->ipi_spec_dst = to->pi.ipi_spec_dst;
++ pkti->ipi_addr.s_addr = 0;
++ return sendmsg (sock->sd, &mesg, 0);
++}
++
++#endif
++
+ /*
+ * Win32 overlapped socket I/O functions.
+ */
+@@ -1913,7 +2072,7 @@
+ }
+
+ int
+-socket_send_queue (struct link_socket *sock, struct buffer *buf, const struct sockaddr_in *to)
++socket_send_queue (struct link_socket *sock, struct buffer *buf, const struct link_socket_actual *to)
+ {
+ if (sock->writes.iostate == IOSTATE_INITIAL)
+ {
+@@ -1937,7 +2096,7 @@
+ {
+ /* set destination address for UDP writes */
+ sock->writes.addr_defined = true;
+- sock->writes.addr = *to;
++ sock->writes.addr = to->dest.sa;
+ sock->writes.addrlen = sizeof (sock->writes.addr);
+
+ status = WSASendTo(
+@@ -2013,11 +2172,10 @@
+ }
+
+ int
+-socket_finalize (
+- SOCKET s,
++socket_finalize (SOCKET s,
+ struct overlapped_io *io,
+ struct buffer *buf,
+- struct sockaddr_in *from)
++ struct link_socket_actual *from)
+ {
+ int ret = -1;
+ BOOL status;
+@@ -2094,10 +2252,10 @@
+ {
+ if (io->addrlen != sizeof (io->addr))
+ bad_address_length (io->addrlen, sizeof (io->addr));
+- *from = io->addr;
++ from->dest.sa = io->addr;
+ }
+ else
+- CLEAR (*from);
++ CLEAR (from->dest.sa);
+ }
+
+ if (buf)
+diff -ur openvpn-2.0_rc16/socket.h openvpn-2.0_rc16MH/socket.h
+--- openvpn-2.0_rc16/socket.h 2005-02-05 01:36:31.000000000 -0700
++++ openvpn-2.0_rc16MH/socket.h 2005-02-25 21:13:53.000000000 -0700
+@@ -78,12 +78,29 @@
+ /* convert a packet_size_type from network to host order */
+ #define ntohps(x) ntohs(x)
+
++/* OpenVPN sockaddr struct */
++struct openvpn_sockaddr
++{
++ int dummy; // JYFIXME
++ struct sockaddr_in sa;
++};
++
++/* actual address of remote, based on source address of received packets */
++struct link_socket_actual
++{
++ int dummy; // JYFIXME
++ struct openvpn_sockaddr dest;
++#if ENABLE_IP_PKTINFO
++ struct in_pktinfo pi;
++#endif
++};
++
+ /* IP addresses which are persistant across SIGUSR1s */
+ 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 openvpn_sockaddr local;
++ struct openvpn_sockaddr remote; /* initial remote */
++ struct link_socket_actual actual; /* reply to this address */
+ };
+
+ struct link_socket_info
+@@ -186,6 +203,9 @@
+ struct buffer stream_buf_data;
+ bool stream_reset;
+
++# define SF_USE_IP_PKTINFO (1<<0)
++ unsigned int socket_flags;
++
+ #ifdef ENABLE_HTTP_PROXY
+ /* HTTP proxy */
+ struct http_proxy_info *http_proxy;
+@@ -194,7 +214,7 @@
+ #ifdef ENABLE_SOCKS
+ /* Socks proxy */
+ struct socks_proxy_info *socks_proxy;
+- struct sockaddr_in socks_relay; /* Socks UDP relay address */
++ struct link_socket_actual socks_relay; /* Socks UDP relay address */
+ #endif
+
+ #if defined(ENABLE_HTTP_PROXY) || defined(ENABLE_SOCKS)
+@@ -230,13 +250,13 @@
+
+ int socket_send_queue (struct link_socket *sock,
+ struct buffer *buf,
+- const struct sockaddr_in *to);
++ const struct link_socket_actual *to);
+
+ int socket_finalize (
+ SOCKET s,
+ struct overlapped_io *io,
+ struct buffer *buf,
+- struct sockaddr_in *from);
++ struct link_socket_actual *from);
+
+ #else
+
+@@ -277,29 +297,41 @@
+ int connect_retry_seconds,
+ int mtu_discover_type,
+ int rcvbuf,
+- int sndbuf);
++ int sndbuf,
++ const unsigned int socket_flags);
+
+ void link_socket_init_phase2 (struct link_socket *sock,
+ const struct frame *frame,
+ volatile int *signal_received);
+
+-void link_socket_post_fork (const struct link_socket *sock,
+- const struct sockaddr_in *remote);
+-
+ void socket_adjust_frame_parameters (struct frame *frame, int proto);
+
+ void frame_adjust_path_mtu (struct frame *frame, int pmtu, int proto);
+
+ void link_socket_close (struct link_socket *sock);
+
+-const char *print_sockaddr_ex (const struct sockaddr_in *addr,
+- bool do_port,
++#define PS_SHOW_PORT_IF_DEFINED (1<<0)
++#define PS_SHOW_PORT (1<<1)
++#define PS_SHOW_PKTINFO (1<<2)
++
++const char *print_sockaddr_ex (const struct openvpn_sockaddr *addr,
+ const char* separator,
++ const unsigned int flags,
+ struct gc_arena *gc);
+
+-const char *print_sockaddr (const struct sockaddr_in *addr,
++
++const char *print_sockaddr (const struct openvpn_sockaddr *addr,
+ struct gc_arena *gc);
+
++const char *print_link_socket_actual_ex (const struct link_socket_actual *act,
++ const char* separator,
++ const unsigned int flags,
++ struct gc_arena *gc);
++
++const char *print_link_socket_actual (const struct link_socket_actual *act,
++ struct gc_arena *gc);
++
++
+ #define IA_EMPTY_IF_UNDEF (1<<0)
+ #define IA_NET_ORDER (1<<1)
+ const char *print_in_addr_t (in_addr_t addr, unsigned int flags, struct gc_arena *gc);
+@@ -308,7 +340,7 @@
+ #define SA_SET_IF_NONZERO (1<<1)
+ void setenv_sockaddr (struct env_set *es,
+ const char *name_prefix,
+- const struct sockaddr_in *addr,
++ const struct openvpn_sockaddr *addr,
+ const bool flags);
+
+ void setenv_in_addr_t (struct env_set *es,
+@@ -316,19 +348,24 @@
+ in_addr_t addr,
+ const bool flags);
+
++void setenv_link_socket_actual (struct env_set *es,
++ const char *name_prefix,
++ const struct link_socket_actual *act,
++ const bool flags);
++
+ void bad_address_length (int actual, int expected);
+
+ in_addr_t link_socket_current_remote (const struct link_socket_info *info);
+
+ void link_socket_connection_initiated (const struct buffer *buf,
+ struct link_socket_info *info,
+- const struct sockaddr_in *addr,
++ const struct link_socket_actual *addr,
+ const char *common_name,
+ struct env_set *es);
+
+ void link_socket_bad_incoming_addr (struct buffer *buf,
+ const struct link_socket_info *info,
+- const struct sockaddr_in *from_addr);
++ const struct link_socket_actual *from_addr);
+
+ void link_socket_bad_outgoing_addr (void);
+
+@@ -349,7 +386,7 @@
+ socket_descriptor_t create_socket_tcp (void);
+
+ socket_descriptor_t socket_do_accept (socket_descriptor_t sd,
+- struct sockaddr_in *remote,
++ struct link_socket_actual *act,
+ const bool nowait);
+
+ /*
+@@ -440,33 +477,39 @@
+ }
+
+ static inline bool
+-addr_defined (const struct sockaddr_in *addr)
++addr_defined (const struct openvpn_sockaddr *addr)
+ {
+- return addr->sin_addr.s_addr != 0;
++ return addr->sa.sin_addr.s_addr != 0;
+ }
+
+ static inline bool
+-addr_match (const struct sockaddr_in *a1, const struct sockaddr_in *a2)
++link_socket_actual_defined (const struct link_socket_actual *act)
+ {
+- return a1->sin_addr.s_addr == a2->sin_addr.s_addr;
++ return act && addr_defined (&act->dest);
++}
++
++static inline bool
++addr_match (const struct openvpn_sockaddr *a1, const struct openvpn_sockaddr *a2)
++{
++ return a1->sa.sin_addr.s_addr == a2->sa.sin_addr.s_addr;
+ }
+
+ static inline in_addr_t
+-addr_host (const struct sockaddr_in *s)
++addr_host (const struct openvpn_sockaddr *s)
+ {
+- return ntohl (s->sin_addr.s_addr);
++ return ntohl (s->sa.sin_addr.s_addr);
+ }
+
+ static inline bool
+-addr_port_match (const struct sockaddr_in *a1, const struct sockaddr_in *a2)
++addr_port_match (const struct openvpn_sockaddr *a1, const struct openvpn_sockaddr *a2)
+ {
+- return a1->sin_addr.s_addr == a2->sin_addr.s_addr
+- && a1->sin_port == a2->sin_port;
++ return a1->sa.sin_addr.s_addr == a2->sa.sin_addr.s_addr
++ && a1->sa.sin_port == a2->sa.sin_port;
+ }
+
+ static inline bool
+-addr_match_proto (const struct sockaddr_in *a1,
+- const struct sockaddr_in *a2,
++addr_match_proto (const struct openvpn_sockaddr *a1,
++ const struct openvpn_sockaddr *a2,
+ const int proto)
+ {
+ return link_socket_proto_connection_oriented (proto)
+@@ -475,6 +518,12 @@
+ }
+
+ static inline bool
++link_socket_actual_match (const struct link_socket_actual *a1, const struct link_socket_actual *a2)
++{
++ return addr_port_match (&a1->dest, &a2->dest);
++}
++
++static inline bool
+ socket_connection_reset (const struct link_socket *sock, int status)
+ {
+ if (link_socket_connection_oriented (sock))
+@@ -497,17 +546,17 @@
+ static inline bool
+ link_socket_verify_incoming_addr (struct buffer *buf,
+ const struct link_socket_info *info,
+- const struct sockaddr_in *from_addr)
++ const struct link_socket_actual *from_addr)
+ {
+ if (buf->len > 0)
+ {
+- if (from_addr->sin_family != AF_INET)
++ if (from_addr->dest.sa.sin_family != AF_INET)
+ return false;
+- if (!addr_defined (from_addr))
++ if (!link_socket_actual_defined (from_addr))
+ return false;
+ if (info->remote_float || !addr_defined (&info->lsa->remote))
+ return true;
+- if (addr_match_proto (from_addr, &info->lsa->remote, info->proto))
++ if (addr_match_proto (&from_addr->dest, &info->lsa->remote, info->proto))
+ return true;
+ }
+ return false;
+@@ -516,21 +565,18 @@
+ static inline void
+ link_socket_get_outgoing_addr (struct buffer *buf,
+ const struct link_socket_info *info,
+- struct sockaddr_in *addr)
++ struct link_socket_actual **act)
+ {
+ if (buf->len > 0)
+ {
+ struct link_socket_addr *lsa = info->lsa;
+- if (addr_defined (&lsa->actual))
+- {
+- 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;
+- }
++ if (link_socket_actual_defined (&lsa->actual))
++ *act = &lsa->actual;
+ else
+ {
+ link_socket_bad_outgoing_addr ();
+ buf->len = 0;
++ *act = NULL;
+ }
+ }
+ }
+@@ -538,7 +584,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 link_socket_actual *act,
+ const char *common_name,
+ struct env_set *es)
+ {
+@@ -548,14 +594,14 @@
+ if (
+ /* new or changed address? */
+ (!info->connection_established
+- || !addr_match_proto (addr, &lsa->actual, info->proto))
++ || !addr_match_proto (&act->dest, &lsa->actual.dest, info->proto))
+ /* address undef or address == remote or --float */
+ && (info->remote_float
+ || !addr_defined (&lsa->remote)
+- || addr_match_proto (addr, &lsa->remote, info->proto))
++ || addr_match_proto (&act->dest, &lsa->remote, info->proto))
+ )
+ {
+- link_socket_connection_initiated (buf, info, addr, common_name, es);
++ link_socket_connection_initiated (buf, info, act, common_name, es);
+ }
+ }
+ }
+@@ -592,7 +638,7 @@
+ static inline int
+ link_socket_read_udp_win32 (struct link_socket *sock,
+ struct buffer *buf,
+- struct sockaddr_in *from)
++ struct link_socket_actual *from)
+ {
+ return socket_finalize (sock->sd, &sock->reads, buf, from);
+ }
+@@ -602,7 +648,7 @@
+ int link_socket_read_udp_posix (struct link_socket *sock,
+ struct buffer *buf,
+ int maxsize,
+- struct sockaddr_in *from);
++ struct link_socket_actual *from);
+
+ #endif
+
+@@ -611,7 +657,7 @@
+ link_socket_read (struct link_socket *sock,
+ struct buffer *buf,
+ int maxsize,
+- struct sockaddr_in *from)
++ struct link_socket_actual *from)
+ {
+ if (sock->info.proto == PROTO_UDPv4)
+ {
+@@ -627,7 +673,7 @@
+ else if (sock->info.proto == PROTO_TCPv4_SERVER || sock->info.proto == PROTO_TCPv4_CLIENT)
+ {
+ /* from address was returned by accept */
+- *from = sock->info.lsa->actual;
++ from->dest.sa = sock->info.lsa->actual.dest.sa;
+ return link_socket_read_tcp (sock, buf);
+ }
+ else
+@@ -643,14 +689,14 @@
+
+ int link_socket_write_tcp (struct link_socket *sock,
+ struct buffer *buf,
+- struct sockaddr_in *to);
++ struct link_socket_actual *to);
+
+ #ifdef WIN32
+
+ static inline int
+ link_socket_write_win32 (struct link_socket *sock,
+ struct buffer *buf,
+- struct sockaddr_in *to)
++ struct link_socket_actual *to)
+ {
+ int err = 0;
+ int status = 0;
+@@ -675,17 +721,26 @@
+ static inline int
+ link_socket_write_udp_posix (struct link_socket *sock,
+ struct buffer *buf,
+- struct sockaddr_in *to)
++ struct link_socket_actual *to)
+ {
+- return sendto (sock->sd, BPTR (buf), BLEN (buf), 0,
+- (struct sockaddr *) to,
+- (socklen_t) sizeof (*to));
++#if ENABLE_IP_PKTINFO
++ int link_socket_write_udp_posix_sendmsg (struct link_socket *sock,
++ struct buffer *buf,
++ struct link_socket_actual *to);
++
++ if (sock->socket_flags & SF_USE_IP_PKTINFO)
++ return link_socket_write_udp_posix_sendmsg (sock, buf, to);
++ else
++#endif
++ return sendto (sock->sd, BPTR (buf), BLEN (buf), 0,
++ (struct sockaddr *) &to->dest.sa,
++ (socklen_t) sizeof (to->dest.sa));
+ }
+
+ static inline int
+ link_socket_write_tcp_posix (struct link_socket *sock,
+ struct buffer *buf,
+- struct sockaddr_in *to)
++ struct link_socket_actual *to)
+ {
+ return send (sock->sd, BPTR (buf), BLEN (buf), MSG_NOSIGNAL);
+ }
+@@ -695,7 +750,7 @@
+ static inline int
+ link_socket_write_udp (struct link_socket *sock,
+ struct buffer *buf,
+- struct sockaddr_in *to)
++ struct link_socket_actual *to)
+ {
+ #ifdef WIN32
+ return link_socket_write_win32 (sock, buf, to);
+@@ -708,7 +763,7 @@
+ static inline int
+ link_socket_write (struct link_socket *sock,
+ struct buffer *buf,
+- struct sockaddr_in *to)
++ struct link_socket_actual *to)
+ {
+ if (sock->info.proto == PROTO_UDPv4)
+ {
+diff -ur openvpn-2.0_rc16/socks.c openvpn-2.0_rc16MH/socks.c
+--- openvpn-2.0_rc16/socks.c 2005-01-09 17:46:29.000000000 -0700
++++ openvpn-2.0_rc16MH/socks.c 2005-02-24 22:52:32.000000000 -0700
+@@ -149,7 +149,8 @@
+ }
+
+ static bool
+-recv_socks_reply (socket_descriptor_t sd, struct sockaddr_in *addr,
++recv_socks_reply (socket_descriptor_t sd,
++ struct openvpn_sockaddr *addr,
+ volatile int *signal_received)
+ {
+ char atyp = '\0';
+@@ -160,9 +161,9 @@
+
+ if (addr != NULL)
+ {
+- addr->sin_family = AF_INET;
+- addr->sin_addr.s_addr = htonl (INADDR_ANY);
+- addr->sin_port = htons (0);
++ addr->sa.sin_family = AF_INET;
++ addr->sa.sin_addr.s_addr = htonl (INADDR_ANY);
++ addr->sa.sin_port = htons (0);
+ }
+
+ while (len < 4 + alen + 2)
+@@ -249,8 +250,8 @@
+ /* ATYP == 1 (IP V4 address) */
+ if (atyp == '\x01' && addr != NULL)
+ {
+- memcpy (&addr->sin_addr, buf + 4, sizeof (addr->sin_addr));
+- memcpy (&addr->sin_port, buf + 8, sizeof (addr->sin_port));
++ memcpy (&addr->sa.sin_addr, buf + 4, sizeof (addr->sa.sin_addr));
++ memcpy (&addr->sa.sin_port, buf + 8, sizeof (addr->sa.sin_port));
+ }
+
+
+@@ -311,7 +312,7 @@
+ establish_socks_proxy_udpassoc (struct socks_proxy_info *p,
+ socket_descriptor_t ctrl_sd, /* already open to proxy */
+ socket_descriptor_t udp_sd,
+- struct sockaddr_in *relay_addr,
++ struct openvpn_sockaddr *relay_addr,
+ volatile int *signal_received)
+ {
+ if (!socks_handshake (ctrl_sd, signal_received))
+@@ -353,7 +354,7 @@
+ */
+ void
+ socks_process_incoming_udp (struct buffer *buf,
+- struct sockaddr_in *from)
++ struct link_socket_actual *from)
+ {
+ int atyp;
+
+@@ -368,8 +369,8 @@
+ if (atyp != 1) /* ATYP == 1 (IP V4) */
+ goto error;
+
+- buf_read (buf, &from->sin_addr, sizeof (from->sin_addr));
+- buf_read (buf, &from->sin_port, sizeof (from->sin_port));
++ buf_read (buf, &from->dest.sa.sin_addr, sizeof (from->dest.sa.sin_addr));
++ buf_read (buf, &from->dest.sa.sin_port, sizeof (from->dest.sa.sin_port));
+
+ return;
+
+@@ -386,7 +387,7 @@
+ */
+ int
+ socks_process_outgoing_udp (struct buffer *buf,
+- struct sockaddr_in *to)
++ const struct link_socket_actual *to)
+ {
+ /*
+ * Get a 10 byte subset buffer prepended to buf --
+@@ -401,8 +402,8 @@
+ buf_write_u16 (&head, 0); /* RSV = 0 */
+ buf_write_u8 (&head, 0); /* FRAG = 0 */
+ buf_write_u8 (&head, '\x01'); /* ATYP = 1 (IP V4) */
+- buf_write (&head, &to->sin_addr, sizeof (to->sin_addr));
+- buf_write (&head, &to->sin_port, sizeof (to->sin_port));
++ buf_write (&head, &to->dest.sa.sin_addr, sizeof (to->dest.sa.sin_addr));
++ buf_write (&head, &to->dest.sa.sin_port, sizeof (to->dest.sa.sin_port));
+
+ return 10;
+ }
+diff -ur openvpn-2.0_rc16/socks.h openvpn-2.0_rc16MH/socks.h
+--- openvpn-2.0_rc16/socks.h 2005-01-09 17:46:29.000000000 -0700
++++ openvpn-2.0_rc16MH/socks.h 2005-02-24 22:52:11.000000000 -0700
+@@ -35,6 +35,9 @@
+
+ #include "buffer.h"
+
++struct openvpn_sockaddr;
++struct link_socket_actual;
++
+ struct socks_proxy_info {
+ bool defined;
+ bool retry;
+@@ -59,14 +62,14 @@
+ void establish_socks_proxy_udpassoc (struct socks_proxy_info *p,
+ socket_descriptor_t ctrl_sd, /* already open to proxy */
+ socket_descriptor_t udp_sd,
+- struct sockaddr_in *relay_addr,
++ struct openvpn_sockaddr *relay_addr,
+ volatile int *signal_received);
+
+ void socks_process_incoming_udp (struct buffer *buf,
+- struct sockaddr_in *from);
++ struct link_socket_actual *from);
+
+ int socks_process_outgoing_udp (struct buffer *buf,
+- struct sockaddr_in *to);
++ const struct link_socket_actual *to);
+
+ #endif
+ #endif
+diff -ur openvpn-2.0_rc16/ssl.c openvpn-2.0_rc16MH/ssl.c
+--- openvpn-2.0_rc16/ssl.c 2005-02-15 21:12:55.000000000 -0700
++++ openvpn-2.0_rc16MH/ssl.c 2005-02-25 14:47:45.000000000 -0700
+@@ -362,7 +362,7 @@
+ static void
+ setenv_untrusted (struct tls_session *session)
+ {
+- setenv_sockaddr (session->opt->es, "untrusted", &session->untrusted_sockaddr, SA_IP_PORT);
++ setenv_link_socket_actual (session->opt->es, "untrusted", &session->untrusted_addr, SA_IP_PORT);
+ }
+
+ static void
+@@ -1784,7 +1784,7 @@
+ write_control_auth (struct tls_session *session,
+ struct key_state *ks,
+ struct buffer *buf,
+- struct sockaddr_in *to_link_addr,
++ struct link_socket_actual **to_link_addr,
+ int opcode,
+ int max_ack,
+ bool prepend_ack)
+@@ -1792,7 +1792,7 @@
+ uint8_t *header;
+ struct buffer null = clear_buf ();
+
+- ASSERT (addr_defined (&ks->remote_addr));
++ ASSERT (link_socket_actual_defined (&ks->remote_addr));
+ ASSERT (reliable_ack_write
+ (ks->rec_ack, buf, &ks->session_id_remote, max_ack, prepend_ack));
+ ASSERT (session_id_write_prepend (&session->session_id, buf));
+@@ -1804,7 +1804,7 @@
+ openvpn_encrypt (buf, null, &session->tls_auth, NULL);
+ ASSERT (swap_hmac (buf, &session->tls_auth, false));
+ }
+- *to_link_addr = ks->remote_addr;
++ *to_link_addr = &ks->remote_addr;
+ }
+
+ /*
+@@ -1813,7 +1813,7 @@
+ static bool
+ read_control_auth (struct buffer *buf,
+ const struct crypto_options *co,
+- const struct sockaddr_in *from)
++ const struct link_socket_actual *from)
+ {
+ struct gc_arena gc = gc_new ();
+
+@@ -1826,7 +1826,7 @@
+ {
+ msg (D_TLS_ERRORS,
+ "TLS Error: cannot locate HMAC in incoming packet from %s",
+- print_sockaddr (from, &gc));
++ print_link_socket_actual (from, &gc));
+ gc_free (&gc);
+ return false;
+ }
+@@ -1838,7 +1838,7 @@
+ {
+ msg (D_TLS_ERRORS,
+ "TLS Error: incoming packet authentication failed from %s",
+- print_sockaddr (from, &gc));
++ print_link_socket_actual (from, &gc));
+ gc_free (&gc);
+ return false;
+ }
+@@ -2720,7 +2720,7 @@
+ tls_process (struct tls_multi *multi,
+ struct tls_session *session,
+ struct buffer *to_link,
+- struct sockaddr_in *to_link_addr,
++ struct link_socket_actual **to_link_addr,
+ struct link_socket_info *to_link_socket_info,
+ interval_t *wakeup)
+ {
+@@ -3113,7 +3113,7 @@
+ bool
+ tls_multi_process (struct tls_multi *multi,
+ struct buffer *to_link,
+- struct sockaddr_in *to_link_addr,
++ struct link_socket_actual **to_link_addr,
+ struct link_socket_info *to_link_socket_info,
+ interval_t *wakeup)
+ {
+@@ -3137,7 +3137,7 @@
+
+ /* set initial remote address */
+ if (i == TM_ACTIVE && ks->state == S_INITIAL &&
+- addr_defined (&to_link_socket_info->lsa->actual))
++ link_socket_actual_defined (&to_link_socket_info->lsa->actual))
+ ks->remote_addr = to_link_socket_info->lsa->actual;
+
+ dmsg (D_TLS_DEBUG,
+@@ -3146,17 +3146,30 @@
+ state_name (ks->state),
+ session_id_print (&session->session_id, &gc),
+ session_id_print (&ks->session_id_remote, &gc),
+- print_sockaddr (&ks->remote_addr, &gc));
++ print_link_socket_actual (&ks->remote_addr, &gc));
+
+- if (ks->state >= S_INITIAL && addr_defined (&ks->remote_addr))
++ if (ks->state >= S_INITIAL && link_socket_actual_defined (&ks->remote_addr))
+ {
++ struct link_socket_actual *tla = NULL;
++
+ update_time ();
+
+- if (tls_process (multi, session, to_link, to_link_addr,
++ if (tls_process (multi, session, to_link, &tla,
+ to_link_socket_info, wakeup))
+ active = true;
+
+ /*
++ * If tls_process produced an outgoing packet,
++ * return the link_socket_actual object (which
++ * contains the outgoing address).
++ */
++ if (tla)
++ {
++ multi->to_link_addr = *tla;
++ *to_link_addr = &multi->to_link_addr;
++ }
++
++ /*
+ * If tls_process hits an error:
+ * (1) If the session has an unexpired lame duck key, preserve it.
+ * (2) Reinitialize the session.
+@@ -3275,7 +3288,7 @@
+
+ bool
+ tls_pre_decrypt (struct tls_multi *multi,
+- struct sockaddr_in *from,
++ const struct link_socket_actual *from,
+ struct buffer *buf,
+ struct crypto_options *opt)
+ {
+@@ -3317,7 +3330,7 @@
+ if (DECRYPT_KEY_ENABLED (multi, ks)
+ && key_id == ks->key_id
+ && ks->authenticated
+- && addr_port_match(from, &ks->remote_addr))
++ && link_socket_actual_match (from, &ks->remote_addr))
+ {
+ /* return appropriate data channel decrypt key in opt */
+ opt->key_ctx_bi = &ks->key;
+@@ -3330,7 +3343,7 @@
+ ks->n_bytes += buf->len;
+ dmsg (D_TLS_DEBUG,
+ "TLS: data channel, key_id=%d, IP=%s",
+- key_id, print_sockaddr (from, &gc));
++ key_id, print_link_socket_actual (from, &gc));
+ gc_free (&gc);
+ return ret;
+ }
+@@ -3343,14 +3356,14 @@
+ key_id,
+ ks->key_id,
+ ks->authenticated,
+- addr_port_match (from, &ks->remote_addr));
++ link_socket_actual_match (from, &ks->remote_addr));
+ }
+ #endif
+ }
+
+ msg (D_TLS_ERRORS,
+ "TLS Error: local/remote TLS keys are out of sync: %s [%d]",
+- print_sockaddr (from, &gc), key_id);
++ print_link_socket_actual (from, &gc), key_id);
+ goto error;
+ }
+ else /* control channel packet */
+@@ -3364,7 +3377,7 @@
+ {
+ msg (D_TLS_ERRORS,
+ "TLS Error: unknown opcode received from %s op=%d",
+- print_sockaddr (from, &gc), op);
++ print_link_socket_actual (from, &gc), op);
+ goto error;
+ }
+
+@@ -3379,7 +3392,7 @@
+ {
+ msg (D_TLS_ERRORS,
+ "TLS Error: client->client or server->server connection attempted from %s",
+- print_sockaddr (from, &gc));
++ print_link_socket_actual (from, &gc));
+ goto error;
+ }
+ }
+@@ -3388,7 +3401,7 @@
+ * Authenticate Packet
+ */
+ dmsg (D_TLS_DEBUG, "TLS: control channel, op=%s, IP=%s",
+- packet_opcode_name (op), print_sockaddr (from, &gc));
++ packet_opcode_name (op), print_link_socket_actual (from, &gc));
+
+ /* get remote session-id */
+ {
+@@ -3398,7 +3411,7 @@
+ {
+ msg (D_TLS_ERRORS,
+ "TLS Error: session-id not found in packet from %s",
+- print_sockaddr (from, &gc));
++ print_link_socket_actual (from, &gc));
+ goto error;
+ }
+ }
+@@ -3415,9 +3428,9 @@
+ state_name (ks->state),
+ session_id_print (&session->session_id, &gc),
+ session_id_print (&sid, &gc),
+- print_sockaddr (from, &gc),
++ print_link_socket_actual (from, &gc),
+ session_id_print (&ks->session_id_remote, &gc),
+- print_sockaddr (&ks->remote_addr, &gc));
++ print_link_socket_actual (&ks->remote_addr, &gc));
+
+ if (session_id_equal (&ks->session_id_remote, &sid))
+ /* found a match */
+@@ -3462,7 +3475,7 @@
+ {
+ msg (D_TLS_ERRORS,
+ "TLS Error: Cannot accept new session request from %s due to --single-session [1]",
+- print_sockaddr (from, &gc));
++ print_link_socket_actual (from, &gc));
+ goto error;
+ }
+
+@@ -3478,13 +3491,13 @@
+
+ msg (D_TLS_DEBUG_LOW,
+ "TLS: Initial packet from %s, sid=%s",
+- print_sockaddr (from, &gc),
++ print_link_socket_actual (from, &gc),
+ session_id_print (&sid, &gc));
+
+ do_burst = true;
+ new_link = true;
+ i = TM_ACTIVE;
+- session->untrusted_sockaddr = *from;
++ session->untrusted_addr = *from;
+ }
+ }
+
+@@ -3504,7 +3517,7 @@
+ {
+ msg (D_TLS_ERRORS,
+ "TLS Error: Cannot accept new session request from %s due to --single-session [2]",
+- print_sockaddr (from, &gc));
++ print_link_socket_actual (from, &gc));
+ goto error;
+ }
+
+@@ -3527,11 +3540,11 @@
+ */
+ msg (D_TLS_DEBUG_LOW,
+ "TLS: new session incoming connection from %s",
+- print_sockaddr (from, &gc));
++ print_link_socket_actual (from, &gc));
+
+ new_link = true;
+ i = TM_UNTRUSTED;
+- session->untrusted_sockaddr = *from;
++ session->untrusted_addr = *from;
+ }
+ else
+ {
+@@ -3545,7 +3558,7 @@
+ {
+ msg (D_TLS_ERRORS,
+ "TLS Error: Unroutable control packet received from %s (si=%d op=%s)",
+- print_sockaddr (from, &gc),
++ print_link_socket_actual (from, &gc),
+ i,
+ packet_opcode_name (op));
+ goto error;
+@@ -3554,10 +3567,10 @@
+ /*
+ * Verify remote IP address
+ */
+- if (!new_link && !addr_port_match (&ks->remote_addr, from))
++ if (!new_link && !link_socket_actual_match (&ks->remote_addr, from))
+ {
+ msg (D_TLS_ERRORS, "TLS Error: Received control packet from unexpected IP addr: %s",
+- print_sockaddr (from, &gc));
++ print_link_socket_actual (from, &gc));
+ goto error;
+ }
+
+@@ -3619,11 +3632,11 @@
+ ks->remote_addr = *from;
+ ++multi->n_sessions;
+ }
+- else if (!addr_port_match (&ks->remote_addr, from))
++ else if (!link_socket_actual_match (&ks->remote_addr, from))
+ {
+ msg (D_TLS_ERRORS,
+ "TLS Error: Existing session control channel packet from unknown IP address: %s",
+- print_sockaddr (from, &gc));
++ print_link_socket_actual (from, &gc));
+ goto error;
+ }
+
+@@ -3720,8 +3733,9 @@
+ */
+ bool
+ tls_pre_decrypt_lite (const struct tls_auth_standalone *tas,
+- const struct sockaddr_in *from,
++ const struct link_socket_actual *from,
+ const struct buffer *buf)
++
+ {
+ struct gc_arena gc = gc_new ();
+ bool ret = false;
+@@ -3748,7 +3762,7 @@
+ */
+ dmsg (D_TLS_STATE_ERRORS,
+ "TLS State Error: No TLS state for client %s, opcode=%d",
+- print_sockaddr (from, &gc),
++ print_link_socket_actual (from, &gc),
+ op);
+ goto error;
+ }
+@@ -3758,7 +3772,7 @@
+ dmsg (D_TLS_STATE_ERRORS,
+ "TLS State Error: Unknown key ID (%d) received from %s -- 0 was expected",
+ key_id,
+- print_sockaddr (from, &gc));
++ print_link_socket_actual (from, &gc));
+ goto error;
+ }
+
+@@ -3767,7 +3781,7 @@
+ dmsg (D_TLS_STATE_ERRORS,
+ "TLS State Error: Large packet (size %d) received from %s -- a packet no larger than %d bytes was expected",
+ buf->len,
+- print_sockaddr (from, &gc),
++ print_link_socket_actual (from, &gc),
+ EXPANDED_SIZE_DYNAMIC (&tas->frame));
+ goto error;
+ }
+diff -ur openvpn-2.0_rc16/ssl.h openvpn-2.0_rc16MH/ssl.h
+--- openvpn-2.0_rc16/ssl.h 2005-01-18 22:59:20.000000000 -0700
++++ openvpn-2.0_rc16MH/ssl.h 2005-02-25 14:47:49.000000000 -0700
+@@ -345,8 +345,8 @@
+ time_t must_die; /* this object is destroyed at this time */
+
+ 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 session_id session_id_remote; /* peer's random session ID */
++ struct link_socket_actual remote_addr; /* peer's IP addr */
+ struct packet_id packet_id; /* for data channel, to prevent replay attacks */
+
+ struct key_ctx_bi key; /* data channel keys for encrypt/decrypt/hmac */
+@@ -489,7 +489,7 @@
+ bool verified; /* true if peer certificate was verified against CA */
+
+ /* not-yet-authenticated incoming client */
+- struct sockaddr_in untrusted_sockaddr;
++ struct link_socket_actual untrusted_addr;
+
+ struct key_state key[KS_SIZE];
+ };
+@@ -536,6 +536,12 @@
+ struct key_state *save_ks; /* temporary pointer used between pre/post routines */
+
+ /*
++ * Used to return outgoing address from
++ * tls_multi_process.
++ */
++ struct link_socket_actual to_link_addr;
++
++ /*
+ * Number of sessions negotiated thus far.
+ */
+ int n_sessions;
+@@ -591,19 +597,19 @@
+
+ bool tls_multi_process (struct tls_multi *multi,
+ struct buffer *to_link,
+- struct sockaddr_in *to_link_addr,
++ struct link_socket_actual **to_link_addr,
+ struct link_socket_info *to_link_socket_info,
+ interval_t *wakeup);
+
+ void tls_multi_free (struct tls_multi *multi, bool clear);
+
+ bool tls_pre_decrypt (struct tls_multi *multi,
+- struct sockaddr_in *from,
++ const struct link_socket_actual *from,
+ 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 link_socket_actual *from,
+ const struct buffer *buf);
+
+ void tls_pre_encrypt (struct tls_multi *multi,
+diff -ur openvpn-2.0_rc16/syshead.h openvpn-2.0_rc16MH/syshead.h
+--- openvpn-2.0_rc16/syshead.h 2005-01-09 17:46:27.000000000 -0700
++++ openvpn-2.0_rc16MH/syshead.h 2005-02-25 21:23:50.000000000 -0700
+@@ -291,6 +291,15 @@
+ #endif
+
+ /*
++ * Does this platform support linux-style IP_PKTINFO?
++ */
++#if defined(ENABLE_MULTIHOME) && defined(HAVE_IN_PKTINFO) && defined(IP_PKTINFO) && defined(HAVE_MSGHDR) && defined(HAVE_CMSGHDR) && defined(HAVE_IOVEC) && defined(CMSG_FIRSTHDR) && defined(CMSG_NXTHDR) && defined(HAVE_RECVMSG) && defined(HAVE_SENDMSG)
++#define ENABLE_IP_PKTINFO 1
++#else
++#define ENABLE_IP_PKTINFO 0
++#endif
++
++/*
+ * Disable ESEC
+ */
+ #if 0