]> git.ipfire.org Git - thirdparty/openvpn.git/commitdiff
Merge remote branch SVN 2.1 into the git tree
authorDavid Sommerseth <davids@redhat.com>
Sun, 24 Jul 2011 23:44:27 +0000 (01:44 +0200)
committerDavid Sommerseth <davids@redhat.com>
Fri, 19 Aug 2011 06:46:43 +0000 (08:46 +0200)
Hopefully the last SVN merge we need to do, as these merges are getting
more and more difficult.  Most of the files had minor changes, but due to
the CRLF unification patch (commit 6b2883a637fe73492) we got an increased
number of conflicts.  In addition inclusion of IPv6 support makes the
creates a lot of merge issues in route.c and socket.c

This merge also reverts commit 7c18c6353904f8c6e7 which merged
add_bypass_address() into add_host_route_if_nonlocal().  However the SVN
tree began to use add_bypass_address() another place, where at first glance
it did not be appropriate to use add_host_route_if_nonlocal().

This merge has gone through a 'make check' without any errors, but have
not been tested more thoroughly yet.

Conflicts:
ChangeLog
INSTALL
INSTALL-win32.txt
Makefile.am
acinclude.m4
base64.c
buffer.c
buffer.h
common.h
configure.ac
contrib/pull-resolv-conf/client.down
contrib/pull-resolv-conf/client.up
crypto.c
cryptoapi.c
easy-rsa/2.0/Makefile
easy-rsa/2.0/README
easy-rsa/2.0/build-ca
easy-rsa/2.0/build-dh
easy-rsa/2.0/build-inter
easy-rsa/2.0/build-key
easy-rsa/2.0/build-key-pass
easy-rsa/2.0/build-key-pkcs12
easy-rsa/2.0/build-key-server
easy-rsa/2.0/build-req
easy-rsa/2.0/build-req-pass
easy-rsa/2.0/clean-all
easy-rsa/2.0/inherit-inter
easy-rsa/2.0/list-crl
easy-rsa/2.0/pkitool
easy-rsa/2.0/revoke-full
easy-rsa/2.0/sign-req
easy-rsa/2.0/vars
easy-rsa/2.0/whichopensslcnf
easy-rsa/Windows/build-ca-pass.bat
easy-rsa/Windows/build-key-pass.bat
easy-rsa/Windows/build-key-server-pass.bat
easy-rsa/Windows/init-config.bat
easy-rsa/Windows/vars.bat.sample
error.c
error.h
forward.c
helper.c
httpdigest.c
httpdigest.h
ieproxy.c
init.c
init.h
install-win32/Makefile.am
install-win32/makeopenvpn
install-win32/openssl/openssl097.patch
install-win32/openssl/openssl098.patch
install-win32/openvpn.nsi
list.c
list.h
manage.c
manage.h
management/management-notes.txt
mbuf.c
mbuf.h
misc.c
misc.h
mroute.c
mroute.h
msvc/autodefs.h.in
msvc/config.py
msvc/msvc.mak
mtcp.c
mudp.c
multi.c
multi.h
occ.c
openvpn-plugin.h
openvpn.8
openvpn.h
options.c
options.h
otime.c
otime.h
perf.c
pf.c
ping.c
pkcs11.c
plugin.c
plugin.h
plugin/auth-pam/README
plugin/auth-pam/auth-pam.c
pool.c
pool.h
proto.h
proxy.c
ps.c
push.c
reliable.c
route.c
route.h
sample-config-files/firewall.sh
sample-scripts/bridge-start
sample-scripts/bridge-stop
sample-scripts/openvpn.init
sample-scripts/verify-cn
schedule.c
schedule.h
service-win32/openvpnserv.c
sig.c
socket.c
socket.h
socks.c
socks.h
ssl.c
ssl.h
status.c
syshead.h
tap-win32/SOURCES.in
tap-win32/common.h
tap-win32/proto.h
tap-win32/tapdrvr.c
tap-win32/types.h
tun.c
tun.h
version.m4
win/autodefs.h.in
win/build.py
win/build_all.py
win/build_ddk.py
win/build_exe.py
win/config.py
win/config_all.py
win/config_tap.py
win/config_ti.py
win/js.py
win/make_dist.py
win/msvc.mak.in
win/settings.in
win/show.py
win/sign.py
win/tap_span.py
win/wb.py
win32.c
win32.h

Signed-off-by: David Sommerseth <davids@redhat.com>
Reviewed-by: Gert Doering <gert@greenie.muc.de>
Reviewed-by: James Yonan <james@openvpn.net>
Reviewed-by: Adriaan de Jong <dejong@fox-it.com>
17 files changed:
1  2 
base64.c
forward.c
init.c
manage.c
management/management-notes.txt
misc.c
misc.h
mtcp.c
openvpn.8
options.c
options.h
route.c
route.h
ssl.c
ssl.h
syshead.h
tun.c

diff --cc base64.c
index 26ca7d711d09fad9f5ddd6e7004c55f587c312fc,045b04331ad62a7cb64de3d99e07a0d95c12162b..95ccffcde9b61d7034834f3b762a845edbb95876
+++ b/base64.c
  
  static char base64_chars[] = 
      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
--
+ /*
+  * base64 encode input data of length size to malloced
+  * buffer which is returned as *str.  Returns string
+  * length of *str.
+  */
  int 
  base64_encode(const void *data, int size, char **str)
  {
@@@ -115,7 -120,12 +119,11 @@@ token_decode(const char *token
        return DECODE_ERROR;
      return (marker << 24) | val;
  }
--
+ /*
+  * Decode base64 str, outputting data to buffer
+  * at data of length size.  Return length of
+  * decoded data written or -1 on error or overflow.
+  */
  int
  base64_decode(const char *str, void *data, int size)
  {
diff --cc forward.c
Simple merge
diff --cc init.c
index 4a16fba44776037b269e78089cb2fea15526549f,0e567e6e38e3870a45daf3a9d8d70f5c81864379..b7c09db1979a4393fb8ddb90c89c2e22ade8b7c4
--- 1/init.c
--- 2/init.c
+++ b/init.c
@@@ -1238,10 -1279,10 +1325,10 @@@ do_route (const struct options *options
          const struct plugin_list *plugins,
          struct env_set *es)
  {
 -  if (!options->route_noexec && route_list)
 +  if (!options->route_noexec && ( route_list || route_ipv6_list ) )
      {
 -      add_routes (route_list, tt, ROUTE_OPTION_FLAGS (options), es);
 +      add_routes (route_list, route_ipv6_list, tt, ROUTE_OPTION_FLAGS (options), es);
-       setenv_int (es, "redirect_gateway", route_list->did_redirect_default_gateway);
+       setenv_int (es, "redirect_gateway", route_did_redirect_default_gateway(route_list));
      }
  #ifdef ENABLE_MANAGEMENT
    if (management)
diff --cc manage.c
Simple merge
index 1f4cbd0193116ed4bc72a4ceb108080da42399ab,90643eafc5a324c007689800ea01758933ba4e5e..785eb88188edae7f22a8f9ff63648cffa1275dbc
@@@ -687,6 -687,38 +687,38 @@@ the 10.0.0.0/8 netblock is allowed: 10.
  may not interact with external IP addresses using an "unknown"
  protocol (i.e. one that is not IPv4 or ARP).
  
 -COMMAND -- remote  (OpenVPN 2.1.5 or higher)
++COMMAND -- remote  (OpenVPN AS 2.1.5/OpenVPN 2.3 or higher)
+ --------------------------------------------
+ Provide remote host/port in response to a >REMOTE notification
+ (client only).  Requires that the --management-query-remote
+ directive is used.
+   remote ACTION [HOST PORT]
+ The "remote" command should only be given in response to a >REMOTE
+ notification.  For example, the following >REMOTE notification
+ indicates that the client config file would ordinarily connect
+ to vpn.example.com port 1194 (UDP):
+   >REMOTE:vpn.example.com,1194,udp
+ Now, suppose we want to override the host and port, connecting
+ instead to vpn.otherexample.com port 1234.  After receiving
+ the above notification, use this command:
+   remote MOD vpn.otherexample.com 1234
+ To accept the same host and port as the client would ordinarily
+ have connected to, use this command:
+   remote ACCEPT
+ To skip the current connection entry and advance to the next one,
+ use this command:
+   remote SKIP
  OUTPUT FORMAT
  -------------
  
diff --cc misc.c
Simple merge
diff --cc misc.h
Simple merge
diff --cc mtcp.c
Simple merge
diff --cc openvpn.8
index 71187379e1bfe41100e2d53e18f60be548f7d30e,87058699bc97eb0d1f683d0b208102eb31d77f7e..dc7646428105f66942d9e4ceddb000b029451e3a
+++ b/openvpn.8
@@@ -1113,9 -1092,9 +1113,9 @@@ for debugging info showing the transfor
  addresses in packets.
  .\"*********************************************************
  .TP
 -.B --redirect-gateway flags...
 +.B \-\-redirect-gateway flags...
(Experimental) Automatically execute routing commands to cause all outgoing IP traffic
- to be redirected over the VPN.
+ Automatically execute routing commands to cause all outgoing IP traffic
+ to be redirected over the VPN.  This is a client-side option.
  
  This option performs three steps:
  
@@@ -1154,7 -1133,12 +1154,12 @@@ flag will cause ste
  .B 1
  above to be omitted.
  
 -.B autolocal --
++.B autolocal \-\-
+ Try to automatically determine whether to enable
+ .B local
+ flag above.
 -.B def1 --
 +.B def1 \-\-
  Use this flag to override
  the default gateway by using 0.0.0.0/1 and 128.0.0.0/1
  rather than 0.0.0.0/0.  This has the benefit of overriding
@@@ -1172,21 -1156,19 +1177,25 @@@ bypasses the tunne
  (Available on Windows clients, may not be available
  on non-Windows clients).
  
- Using the def1 flag is highly recommended.
- .\"*********************************************************
- .TP
- .B \-\-redirect-private [flags]
- Like \-\-redirect-gateway, but omit actually changing the default
- gateway.  Useful when pushing private subnets.
 -.B block-local --
++.B block-local \-\-
+ Block access to local LAN when the tunnel is active, except for
+ the LAN gateway itself.  This is accomplished by routing the local
+ LAN (except for the LAN gateway address) into the tunnel.
  .\"*********************************************************
  .TP
 -.B --link-mtu n
 +.B \-\-link-mtu n
  Sets an upper bound on the size of UDP packets which are sent
  between OpenVPN peers.  It's best not to set this parameter unless
  you know what you're doing.
  .\"*********************************************************
 -.B --tun-mtu n
++.\"*********************************************************
+ .TP
++.B \-\-redirect-private [flags]
++Like \-\-redirect-gateway, but omit actually changing the default
++gateway.  Useful when pushing private subnets.
++.\"*********************************************************
 +.TP
 +.B \-\-tun-mtu n
  Take the TUN device MTU to be
  .B n
  and derive the link MTU
@@@ -2406,7 -2370,13 +2415,13 @@@ for inputs which ordinarily would have 
  console.
  .\"*********************************************************
  .TP
 -.B --management-query-remote
++.B \-\-management-query-remote
+ Allow management interface to override
 -.B --remote
++.B \-\-remote
+ directives (client-only).
+ .\"*********************************************************
+ .TP
 -.B --management-forget-disconnect
 +.B \-\-management-forget-disconnect
  Make OpenVPN forget passwords when management session
  disconnects.
  
@@@ -3425,7 -3376,22 +3440,22 @@@ Note that while this option cannot be p
  from the management interface.
  .\"*********************************************************
  .TP
 -.B --server-poll-timeout n
+ .B \-\-static\-challenge t e
+ Enable static challenge/response protocol using challenge text
+ .B t,
+ with
+ echo flag given by
+ .B e
+ (0|1).
+ The echo flag indicates whether or not the user's response
+ to the challenge should be echoed.
+ See management\-notes.txt in the OpenVPN distribution for a
+ description of the OpenVPN challenge/response protocol.
+ .\"*********************************************************
+ .TP
 +.B \-\-server-poll-timeout n
  when polling possible remote servers to connect to
  in a round-robin fashion, spend no more than
  .B n
diff --cc options.c
Simple merge
diff --cc options.h
Simple merge
diff --cc route.c
index 96596cd3164662a9154db036ebc28800c3c4baf2,6046873a75fe1016f1ce1c875988cce4a6737efb..7843686ab3eb579ed1d105669d164a67d9092377
+++ b/route.c
@@@ -39,7 -38,7 +39,8 @@@
  
  #include "memdbg.h"
  
- static void delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es);
+ static void delete_route (struct route *r, const struct tuntap *tt, unsigned int flags, const struct route_gateway_info *rgi, const struct env_set *es);
++
  static void get_bypass_addresses (struct route_bypass *rb, const unsigned int flags);
  
  #ifdef ENABLE_DEBUG
@@@ -437,22 -357,13 +456,22 @@@ clear_route_list (struct route_list *rl
    rl->capacity = capacity;
  }
  
 +void
 +clear_route_ipv6_list (struct route_ipv6_list *rl6)
 +{
 +  const int capacity = rl6->capacity;
 +  const size_t rl6_size = array_mult_safe (sizeof(struct route_ipv6), capacity, sizeof(struct route_ipv6_list));
 +  memset(rl6, 0, rl6_size);
 +  rl6->capacity = capacity;
 +}
 +
  void
- route_list_add_default_gateway (struct route_list *rl,
-                               struct env_set *es,
-                               const in_addr_t addr)
+ route_list_add_vpn_gateway (struct route_list *rl,
+                           struct env_set *es,
+                           const in_addr_t addr)
  {
    rl->spec.remote_endpoint = addr;
-   rl->spec.remote_endpoint_defined = true;
+   rl->spec.flags |= RTSA_REMOTE_ENDPOINT;
    setenv_route_addr (es, "vpn_gateway", rl->spec.remote_endpoint, -1);
  }
  
@@@ -754,23 -667,14 +841,24 @@@ redirect_default_route_to_vpn (struct r
          if (!local)
            {
              /* route remote host to original default gateway */
 -            add_route3 (rl->spec.remote_host,
 -                        ~0,
 -                        rl->rgi.gateway.addr,
 -                        tt,
 -                        flags,
 -                        &rl->rgi,
 -                        es);
 -            rl->iflags |= RL_DID_LOCAL;
 +#ifdef USE_PF_INET6
 +            /* if remote_host is not ipv4 (ie: ipv6), just skip
 +             * adding this special /32 route */
 +            if (rl->spec.remote_host != IPV4_INVALID_ADDR) {
 +#endif
 +              add_route3 (rl->spec.remote_host,
 +                          ~0,
-                           rl->spec.net_gateway,
++                          rl->rgi.gateway.addr,
 +                          tt,
 +                          flags,
++                          &rl->rgi,
 +                          es);
-               rl->did_local = true;
++              rl->iflags |= RL_DID_LOCAL;
 +#ifdef USE_PF_INET6
 +            } else {
 +              dmsg (D_ROUTE, "ROUTE remote_host protocol differs from tunneled");
 +            }
 +#endif
            }
  
          /* route DHCP/DNS server traffic through original default gateway */
@@@ -887,13 -800,10 +984,10 @@@ undo_redirect_default_route_to_vpn (str
  }
  
  void
- add_routes (struct route_list *rl, struct route_ipv6_list *rl6,
-           const struct tuntap *tt, unsigned int flags, const struct env_set *es)
 -add_routes (struct route_list *rl, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
++add_routes (struct route_list *rl, struct route_ipv6_list *rl6, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
  {
-   if (rl) 
-       redirect_default_route_to_vpn (rl, tt, flags, es);
-   if (rl && !rl->routes_added)
+   redirect_default_route_to_vpn (rl, tt, flags, es);
+   if (!(rl->iflags & RL_ROUTES_ADDED))
      {
        int i;
  
          struct route *r = &rl->routes[i];
          check_subnet_conflict (r->network, r->netmask, "route");
          if (flags & ROUTE_DELETE_FIRST)
-           delete_route (r, tt, flags, es);
-         add_route (r, tt, flags, es);
+           delete_route (r, tt, flags, &rl->rgi, es);
+         add_route (r, tt, flags, &rl->rgi, es);
        }
-       rl->routes_added = true;
+       rl->iflags |= RL_ROUTES_ADDED;
      }
 +  if (rl6 && !rl6->routes_added)
 +    {
 +      int i;
 +
 +      for (i = 0; i < rl6->n; ++i)
 +      {
 +        struct route_ipv6 *r = &rl6->routes_ipv6[i];
 +        if (flags & ROUTE_DELETE_FIRST)
 +          delete_route_ipv6 (r, tt, flags, es);
 +        add_route_ipv6 (r, tt, flags, es);
 +      }
 +      rl6->routes_added = true;
 +    }
  }
  
  void
 -delete_routes (struct route_list *rl, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
 +delete_routes (struct route_list *rl, struct route_ipv6_list *rl6,
 +             const struct tuntap *tt, unsigned int flags, const struct env_set *es)
  {
-   if (rl && rl->routes_added)
+   if (rl->iflags & RL_ROUTES_ADDED)
      {
        int i;
        for (i = rl->n - 1; i >= 0; --i)
        {
 -        struct route *r = &rl->routes[i];
 +        const struct route *r = &rl->routes[i];
-         delete_route (r, tt, flags, es);
+         delete_route (r, tt, flags, &rl->rgi, es);
        }
-       rl->routes_added = false;
+       rl->iflags &= ~RL_ROUTES_ADDED;
      }
 -  undo_redirect_default_route_to_vpn (rl, tt, flags, es);
  
-   if ( rl )
-     {
-       undo_redirect_default_route_to_vpn (rl, tt, flags, es);
-       clear_route_list (rl);
-     }
 -  clear_route_list (rl);
++   undo_redirect_default_route_to_vpn (rl, tt, flags, es);
++   clear_route_list (rl);
 +
 +  if ( rl6 && rl6->routes_added )
 +    {
 +      int i;
 +      for (i = rl6->n - 1; i >= 0; --i)
 +      {
 +        const struct route_ipv6 *r6 = &rl6->routes_ipv6[i];
 +        delete_route_ipv6 (r6, tt, flags, es);
 +      }
 +      rl6->routes_added = false;
 +    }
 +
 +  if ( rl6 )
 +    {
 +      clear_route_ipv6_list (rl6);
 +    }
  }
  
  #ifdef ENABLE_DEBUG
@@@ -1052,36 -953,65 +1167,93 @@@ setenv_routes (struct env_set *es, cons
      setenv_route (es, &rl->routes[i], i + 1);
  }
  
 - *   route add -net 10.10.0.1 netmask 255.255.255.255 dev eth0 
 +static void
 +setenv_route_ipv6 (struct env_set *es, const struct route_ipv6 *r6, int i)
 +{
 +  struct gc_arena gc = gc_new ();
 +  if (r6->defined)
 +    {
 +      struct buffer name1 = alloc_buf_gc( 256, &gc );
 +      struct buffer val = alloc_buf_gc( 256, &gc );
 +      struct buffer name2 = alloc_buf_gc( 256, &gc );
 +
 +      buf_printf( &name1, "route_ipv6_network_%d", i );
 +      buf_printf( &val, "%s/%d", print_in6_addr( r6->network, 0, &gc ),
 +                               r6->netbits );
 +      setenv_str( es, BSTR(&name1), BSTR(&val) );
 +
 +      buf_printf( &name2, "route_ipv6_gateway_%d", i );
 +      setenv_str( es, BSTR(&name2), print_in6_addr( r6->gateway, 0, &gc ));
 +    }
 +  gc_free (&gc);
 +}
 +void
 +setenv_routes_ipv6 (struct env_set *es, const struct route_ipv6_list *rl6)
 +{
 +  int i;
 +  for (i = 0; i < rl6->n; ++i)
 +    setenv_route_ipv6 (es, &rl6->routes_ipv6[i], i + 1);
 +}
 +
+ /*
+  * local_route() determines whether the gateway of a provided host
+  * route is on the same interface that owns the default gateway.
+  * It uses the data structure
+  * returned by get_default_gateway() (struct route_gateway_info)
+  * to determine this.  If the route is local, LR_MATCH is returned.
+  * When adding routes into the kernel, if LR_MATCH is defined for
+  * a given route, the route should explicitly reference the default
+  * gateway interface as the route destination.  For example, here
+  * is an example on Linux that uses LR_MATCH:
+  *
++ *   route add -net 10.10.0.1 netmask 255.255.255.255 dev eth0
+  *
+  * This capability is needed by the "default-gateway block-local"
+  * directive, to allow client access to the local subnet to be
+  * blocked but still allow access to the local default gateway.
+  */
+ /* local_route() return values */
+ #define LR_NOMATCH 0 /* route is not local */
+ #define LR_MATCH   1 /* route is local */
+ #define LR_ERROR   2 /* caller should abort adding route */
+ static int
+ local_route (in_addr_t network,
+            in_addr_t netmask,
+            in_addr_t gateway,
+            const struct route_gateway_info *rgi)
+ {
+   /* set LR_MATCH on local host routes */
+   const int rgi_needed = (RGI_ADDR_DEFINED|RGI_NETMASK_DEFINED|RGI_IFACE_DEFINED);
+   if (rgi
+       && (rgi->flags & rgi_needed) == rgi_needed
+       && gateway == rgi->gateway.addr
+       && netmask == 0xFFFFFFFF)
+     {
+       if (((network ^  rgi->gateway.addr) & rgi->gateway.netmask) == 0)
+       return LR_MATCH;
+       else
+       {
+         /* examine additional subnets on gateway interface */
+         size_t i;
+         for (i = 0; i < rgi->n_addrs; ++i)
+           {
+             const struct route_gateway_address *gwa = &rgi->addrs[i];
+             if (((network ^ gwa->addr) & gwa->netmask) == 0)
+               return LR_MATCH;
+           }
+       }
+     }
+     return LR_NOMATCH;
+ }
  void
- add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
+ add_route (struct route *r,
+          const struct tuntap *tt,
+          unsigned int flags,
+          const struct route_gateway_info *rgi, /* may be NULL */
+          const struct env_set *es)
  {
    struct gc_arena gc;
    struct argv argv;
      argv_printf_cat (&argv, "-rtt %d", r->metric);
  #endif
  
-   argv_printf_cat (&argv, "-net %s %s %s",
-               network,
-               gateway,
-               netmask);
+   if (rgi && is_local_route == LR_MATCH)
+     {
 -      /* Mac OS X route syntax for LR_MATCH: 
++      /* Mac OS X route syntax for LR_MATCH:
+        route add -cloning -net 10.10.0.1 -netmask 255.255.255.255 -interface en0 */
+       argv_printf_cat (&argv, "-cloning -net %s -netmask %s -interface %s",
+                      network,
+                      netmask,
+                      rgi->iface);
+     }
+   else
+     {
+       argv_printf_cat (&argv, "-net %s %s %s",
+                      network,
+                      gateway,
+                      netmask);
+     }
  
    argv_msg (D_ROUTE, &argv);
    status = openvpn_execve_check (&argv, es, 0, "ERROR: OS X route add command failed");
    gc_free (&gc);
  }
  
 +
 +static const char * 
 +print_in6_addr_netbits_only( struct in6_addr network_copy, int netbits, 
 +                             struct gc_arena * gc)
 +{
 +  /* clear host bit parts of route 
 +   * (needed if routes are specified improperly, or if we need to 
 +   * explicitely setup/clear the "connected" network routes on some OSes)
 +   */
 +  int byte = 15;
 +  int bits_to_clear = 128 - netbits;
 +
 +  while( byte >= 0 && bits_to_clear > 0 )
 +    {
 +      if ( bits_to_clear >= 8 )
 +      { network_copy.s6_addr[byte--] = 0; bits_to_clear -= 8; }
 +      else
 +      { network_copy.s6_addr[byte--] &= (~0 << bits_to_clear); bits_to_clear = 0; }
 +    }
 +
 +  return print_in6_addr( network_copy, 0, gc);
 +}
 +
 +void
 +add_route_ipv6 (struct route_ipv6 *r6, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
 +{
 +  struct gc_arena gc;
 +  struct argv argv;
 +
 +  const char *network;
 +  const char *gateway;
 +  bool status = false;
 +  const char *device = tt->actual_name;
 +
 +  if (!r6->defined)
 +    return;
 +
 +  gc_init (&gc);
 +  argv_init (&argv);
 +
 +  network = print_in6_addr_netbits_only( r6->network, r6->netbits, &gc);
 +  gateway = print_in6_addr( r6->gateway, 0, &gc);
 +
 +  if ( !tt->ipv6 )
 +    {
 +      msg( M_INFO, "add_route_ipv6(): not adding %s/%d, no IPv6 on if %s",
 +                  network, r6->netbits, device );
 +      return;
 +    }
 +
 +  msg( M_INFO, "add_route_ipv6(%s/%d -> %s metric %d) dev %s",
 +              network, r6->netbits, gateway, r6->metric, device );
 +
 +  /*
 +   * Filter out routes which are essentially no-ops
 +   * (not currently done for IPv6)
 +   */
 +
 +#if defined(TARGET_LINUX)
 +#ifdef CONFIG_FEATURE_IPROUTE
 +  argv_printf (&argv, "%s -6 route add %s/%d dev %s",
 +            iproute_path,
 +            network,
 +            r6->netbits,
 +            device);
 +  if (r6->metric_defined)
 +    argv_printf_cat (&argv, " metric %d", r6->metric);
 +
 +#else
 +  argv_printf (&argv, "%s -A inet6 add %s/%d dev %s",
 +              ROUTE_PATH,
 +            network,
 +            r6->netbits,
 +            device);
 +  if (r6->metric_defined)
 +    argv_printf_cat (&argv, " metric %d", r6->metric);
 +#endif  /*CONFIG_FEATURE_IPROUTE*/
 +  argv_msg (D_ROUTE, &argv);
 +  status = openvpn_execve_check (&argv, es, 0, "ERROR: Linux route -6/-A inet6 add command failed");
 +
 +#elif defined (WIN32)
 +
 +  /* netsh interface ipv6 add route 2001:db8::/32 MyTunDevice */
 +  argv_printf (&argv, "%s%sc interface ipv6 add route %s/%d %s",
 +             get_win_sys_path(),
 +             NETSH_PATH_SUFFIX,
 +             network,
 +             r6->netbits,
 +             device);
 +
 +  /* next-hop depends on TUN or TAP mode:
 +   * - in TAP mode, we use the "real" next-hop
 +   * - in TUN mode we use a special-case link-local address that the tapdrvr
 +   *   knows about and will answer ND (neighbor discovery) packets for
 +   */
 +  if ( tt->type == DEV_TYPE_TUN )
 +      argv_printf_cat( &argv, " %s", "fe80::8" );
 +  else
 +      argv_printf_cat( &argv, " %s", gateway );
 +
 +#if 0
 +  if (r->metric_defined)
 +    argv_printf_cat (&argv, " METRIC %d", r->metric);
 +#endif
 +
 +  /* in some versions of Windows, routes are persistent across reboots by
 +   * default, unless "store=active" is set (pointed out by Tony Lim, thanks)
 +   */
 +  argv_printf_cat( &argv, " store=active" );
 +
 +  argv_msg (D_ROUTE, &argv);
 +
 +  netcmd_semaphore_lock ();
 +  status = openvpn_execve_check (&argv, es, 0, "ERROR: Windows route add ipv6 command failed");
 +  netcmd_semaphore_release ();
 +
 +#elif defined (TARGET_SOLARIS)
 +
 +  /* example: route add -inet6 2001:db8::/32 somegateway 0 */
 +
 +  /* for some weird reason, this does not work for me unless I set
 +   * "metric 0" - otherwise, the routes will be nicely installed, but
 +   * packets will just disappear somewhere.  So we use "0" now...
 +   */
 +
 +  argv_printf (&argv, "%s add -inet6 %s/%d %s 0",
 +              ROUTE_PATH,
 +              network,
 +              r6->netbits,
 +              gateway );
 +
 +  argv_msg (D_ROUTE, &argv);
 +  status = openvpn_execve_check (&argv, es, 0, "ERROR: Solaris route add -inet6 command failed");
 +
 +#elif defined(TARGET_FREEBSD) || defined(TARGET_DRAGONFLY)
 +
 +  argv_printf (&argv, "%s add -inet6 %s/%d -iface %s",
 +              ROUTE_PATH,
 +              network,
 +              r6->netbits,
 +              device );
 +
 +  argv_msg (D_ROUTE, &argv);
 +  status = openvpn_execve_check (&argv, es, 0, "ERROR: *BSD route add -inet6 command failed");
 +
 +#elif defined(TARGET_DARWIN) 
 +
 +  argv_printf (&argv, "%s add -inet6 %s -prefixlen %d -iface %s",
 +              ROUTE_PATH,
 +              network, r6->netbits, device );
 +
 +  argv_msg (D_ROUTE, &argv);
 +  status = openvpn_execve_check (&argv, es, 0, "ERROR: MacOS X route add -inet6 command failed");
 +
 +#elif defined(TARGET_OPENBSD)
 +
 +  argv_printf (&argv, "%s add -inet6 %s -prefixlen %d %s",
 +              ROUTE_PATH,
 +              network, r6->netbits, gateway );
 +
 +  argv_msg (D_ROUTE, &argv);
 +  status = openvpn_execve_check (&argv, es, 0, "ERROR: OpenBSD route add -inet6 command failed");
 +
 +#elif defined(TARGET_NETBSD)
 +
 +  argv_printf (&argv, "%s add -inet6 %s/%d %s",
 +              ROUTE_PATH,
 +              network, r6->netbits, gateway );
 +
 +  argv_msg (D_ROUTE, &argv);
 +  status = openvpn_execve_check (&argv, es, 0, "ERROR: NetBSD route add -inet6 command failed");
 +
 +#else
 +  msg (M_FATAL, "Sorry, but I don't know how to do 'route ipv6' commands on this operating system.  Try putting your routes in a --route-up script");
 +#endif
 +
 +  r6->defined = status;
 +  argv_reset (&argv);
 +  gc_free (&gc);
 +}
 +
  static void
- delete_route (const struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es)
+ delete_route (struct route *r,
+             const struct tuntap *tt,
+             unsigned int flags,
+             const struct route_gateway_info *rgi,
+             const struct env_set *es)
  {
    struct gc_arena gc;
    struct argv argv;
@@@ -2096,75 -1774,136 +2333,136 @@@ show_routes (int msglev
  
  #elif defined(TARGET_LINUX)
  
- bool
- get_default_gateway (in_addr_t *gateway, in_addr_t *netmask)
+ void
+ get_default_gateway (struct route_gateway_info *rgi)
  {
    struct gc_arena gc = gc_new ();
-   bool ret = false;
-   FILE *fp = fopen ("/proc/net/route", "r");
-   if (fp)
-     {
-       char line[256];
-       int count = 0;
-       int best_count = 0;
-       unsigned int lowest_metric = ~0;
-       in_addr_t best_gw = 0;
-       while (fgets (line, sizeof (line), fp) != NULL)
+   int sd = -1;
+   CLEAR(*rgi);
+   /* get default gateway IP addr */
+   {
+     FILE *fp = fopen ("/proc/net/route", "r");
+     if (fp)
+       {
+       char line[256];
+       int count = 0;
+       unsigned int lowest_metric = ~0;
+       in_addr_t best_gw = 0;
+       while (fgets (line, sizeof (line), fp) != NULL)
+         {
+           if (count)
+             {
+               unsigned int net_x = 0;
+               unsigned int mask_x = 0;
+               unsigned int gw_x = 0;
+               unsigned int metric = 0;
+               unsigned int flags = 0;
+               const int np = sscanf (line, "%*s\t%x\t%x\t%x\t%*s\t%*s\t%d\t%x",
+                                      &net_x,
+                                      &gw_x,
+                                      &flags,
+                                      &metric,
+                                      &mask_x);
+               if (np == 5 && (flags & IFF_UP))
+                 {
+                   const in_addr_t net = ntohl (net_x);
+                   const in_addr_t mask = ntohl (mask_x);
+                   const in_addr_t gw = ntohl (gw_x);
+                   if (!net && !mask && metric < lowest_metric)
+                     {
+                       best_gw = gw;
+                       lowest_metric = metric;
+                     }
+                 }
+             }
+           ++count;
+         }
+       fclose (fp);
+       if (best_gw)
+         {
+           rgi->gateway.addr = best_gw;
+           rgi->flags |= RGI_ADDR_DEFINED;
+         }
+       }
+   }
+   /* scan adapter list */
+   if (rgi->flags & RGI_ADDR_DEFINED)
+     {
+       struct ifreq *ifr, *ifend;
+       in_addr_t addr, netmask;
+       struct ifreq ifreq;
+       struct ifconf ifc;
+       struct ifreq ifs[20]; /* Maximum number of interfaces to scan */
+       if ((sd = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
        {
-         if (count)
-           {
-             unsigned int net_x = 0;
-             unsigned int mask_x = 0;
-             unsigned int gw_x = 0;
-             unsigned int metric = 0;
-             const int np = sscanf (line, "%*s\t%x\t%x\t%*s\t%*s\t%*s\t%d\t%x",
-                                    &net_x,
-                                    &gw_x,
-                                    &metric,
-                                    &mask_x);
-             if (np == 4)
-               {
-                 const in_addr_t net = ntohl (net_x);
-                 const in_addr_t mask = ntohl (mask_x);
-                 const in_addr_t gw = ntohl (gw_x);
-                 dmsg (D_ROUTE_DEBUG, "GDG: route[%d] %s/%s/%s m=%u",
-                       count,
-                       print_in_addr_t ((in_addr_t) net, 0, &gc),
-                       print_in_addr_t ((in_addr_t) mask, 0, &gc),
-                       print_in_addr_t ((in_addr_t) gw, 0, &gc),
-                       metric);
-                 if (!net && !mask && metric < lowest_metric)
-                   {
-                     best_gw = gw;
-                     lowest_metric = metric;
-                     best_count = count;
-                   }
-               }
-           }
-         ++count;
+         msg (M_WARN, "GDG: socket() failed");
+         goto done;
+       }
+       ifc.ifc_len = sizeof (ifs);
+       ifc.ifc_req = ifs;
+       if (ioctl (sd, SIOCGIFCONF, &ifc) < 0)
+       {
+         msg (M_WARN, "GDG: ioctl(SIOCGIFCONF) failed");
+         goto done;
        }
-       fclose (fp);
  
-       if (best_gw)
+       /* scan through interface list */
+       ifend = ifs + (ifc.ifc_len / sizeof (struct ifreq));
+       for (ifr = ifc.ifc_req; ifr < ifend; ifr++)
        {
-         *gateway = best_gw;
-         if (netmask)
+         if (ifr->ifr_addr.sa_family == AF_INET)
            {
-             *netmask = 0xFFFFFF00; /* FIXME -- get the real netmask of the adapter containing the default gateway */
+             /* get interface addr */
+             addr = ntohl(((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr);
+             /* get interface name */
+             strncpynt (ifreq.ifr_name, ifr->ifr_name, sizeof (ifreq.ifr_name));
+             /* check that the interface is up, and not point-to-point or loopback */
+             if (ioctl (sd, SIOCGIFFLAGS, &ifreq) < 0)
+               continue;
+             if (!(ifreq.ifr_flags & IFF_UP))
+               continue;
+             /* get interface netmask */
+             if (ioctl (sd, SIOCGIFNETMASK, &ifreq) < 0)
+               continue;
+             netmask = ntohl(((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr.s_addr);
+             /* check that interface matches default route */
+             if (((rgi->gateway.addr ^ addr) & netmask) != 0)
+               continue;
+             /* save iface name and netmask */
+             strncpynt (rgi->iface, ifreq.ifr_name, sizeof(rgi->iface));
+             rgi->gateway.netmask = netmask;
+             rgi->flags |= (RGI_IFACE_DEFINED|RGI_NETMASK_DEFINED);
 -      
++
+             /* now get the hardware address. */
+             memset (&ifreq.ifr_hwaddr, 0, sizeof (struct sockaddr));
+             if (ioctl (sd, SIOCGIFHWADDR, &ifreq) < 0)
+               {
+                 msg (M_WARN, "GDG: SIOCGIFHWADDR(%s) failed", ifreq.ifr_name);
+                 goto done;
+               }
+             memcpy (rgi->hwaddr, &ifreq.ifr_hwaddr.sa_data, 6);
+             rgi->flags |= RGI_HWADDR_DEFINED;
+             break;
            }
-         ret = true;
        }
-       dmsg (D_ROUTE_DEBUG, "GDG: best=%s[%d] lm=%u",
-           print_in_addr_t ((in_addr_t) best_gw, 0, &gc),
-           best_count,
-           (unsigned int)lowest_metric);
      }
  
+  done:
+   if (sd >= 0)
+     close (sd);
    gc_free (&gc);
-   return ret;
  }
  
  #elif defined(TARGET_FREEBSD)||defined(TARGET_DRAGONFLY)
@@@ -2390,85 -2130,148 +2689,148 @@@ get_default_gateway (struct route_gatew
  
    rtm.rtm_msglen = l = cp - (char *)&m_rtmsg;
  
-   s = socket(PF_ROUTE, SOCK_RAW, 0);
-   if (write(s, (char *)&m_rtmsg, l) < 0)
+   /* transact with routing socket */
+   sockfd = socket(PF_ROUTE, SOCK_RAW, 0);
+   if (sockfd < 0)
      {
-       msg (M_WARN, "ROUTE: problem writing to routing socket");
-       gc_free (&gc);
-       close(s);
-       return false;
+       msg (M_WARN, "GDG: socket #1 failed");
+       goto done;
+     }
+   if (write(sockfd, (char *)&m_rtmsg, l) < 0)
+     {
+       msg (M_WARN, "GDG: problem writing to routing socket");
+       goto done;
      }
    do {
-     l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
+     l = read(sockfd, (char *)&m_rtmsg, sizeof(m_rtmsg));
 -  } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));                   
 +  } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
-                         
-   close(s);
+   close(sockfd);
+   sockfd = -1;
  
+   /* extract return data from routing socket */
    rtm_aux = &rtm;
    cp = ((char *)(rtm_aux + 1));
-   if (rtm_aux->rtm_addrs) {
-     for (i = 1; i; i <<= 1)
-       {
-       if (i & rtm_aux->rtm_addrs)
-         {
-           sa = (struct sockaddr *)cp;
-           if (i == RTA_GATEWAY )
-             gate = sa;
-           else if (i == RTA_IFP)
-             ifp = sa;
-           ADVANCE(cp, sa);
-         }
-       }
-   }
+   if (rtm_aux->rtm_addrs)
+     {
+       for (i = 1; i; i <<= 1)
+       {
+         if (i & rtm_aux->rtm_addrs)
+           {
+             sa = (struct sockaddr *)cp;
+             if (i == RTA_GATEWAY )
+               gate = sa;
+             else if (i == RTA_IFP)
+               ifp = sa;
+             ADVANCE(cp, sa);
+           }
+       }
+     }
    else
+     goto done;
+   /* get gateway addr and interface name */
+   if (gate != NULL )
      {
-       gc_free (&gc);
-       return false;
+       /* get default gateway addr */
+       rgi->gateway.addr = ntohl(((struct sockaddr_in *)gate)->sin_addr.s_addr);
+       if (rgi->gateway.addr)
+         rgi->flags |= RGI_ADDR_DEFINED;
+       if (ifp)
+       {
+         /* get interface name */
+         const struct sockaddr_dl *adl = (struct sockaddr_dl *) ifp;
+         int len = adl->sdl_nlen;
+         if (adl->sdl_nlen && adl->sdl_nlen < sizeof(rgi->iface))
+           {
+             memcpy (rgi->iface, adl->sdl_data, adl->sdl_nlen);
+             rgi->iface[adl->sdl_nlen] = '\0';
+             rgi->flags |= RGI_IFACE_DEFINED;
+           }
+       }
      }
  
+   /* get netmask of interface that owns default gateway */
+   if (rgi->flags & RGI_IFACE_DEFINED) {
+     struct ifreq ifr;
  
-   if (gate != NULL )
+     sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+     if (sockfd < 0)
+       {
+       msg (M_WARN, "GDG: socket #2 failed");
+       goto done;
+       }
+     CLEAR(ifr);
+     ifr.ifr_addr.sa_family = AF_INET;
+     strncpynt(ifr.ifr_name, rgi->iface, IFNAMSIZ);
+     if (ioctl(sockfd, SIOCGIFNETMASK, (char *)&ifr) < 0)
+       {
+       msg (M_WARN, "GDG: ioctl #1 failed");
+       goto done;
+       }
+     close(sockfd);
+     sockfd = -1;
+     rgi->gateway.netmask = ntohl(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr);
+     rgi->flags |= RGI_NETMASK_DEFINED;
+   }
+   /* try to read MAC addr associated with interface that owns default gateway */
+   if (rgi->flags & RGI_IFACE_DEFINED)
      {
-       *ret = ntohl(((struct sockaddr_in *)gate)->sin_addr.s_addr);
- #if 0
-       msg (M_INFO, "gw %s",
-          print_in_addr_t ((in_addr_t) *ret, 0, &gc));
- #endif
+       struct ifconf ifc;
+       struct ifreq *ifr;
+       const int bufsize = 4096;
+       char *buffer;
  
-       if (netmask)
+       buffer = (char *) gc_malloc (bufsize, true, &gc);
+       sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+       if (sockfd < 0)
        {
-         *netmask = 0xFFFFFF00; // FIXME -- get the real netmask of the adapter containing the default gateway
+         msg (M_WARN, "GDG: socket #3 failed");
+         goto done;
        }
  
-       if (ifp && ifname)
+       ifc.ifc_len = bufsize;
+       ifc.ifc_buf = buffer;
+       if (ioctl(sockfd, SIOCGIFCONF, (char *)&ifc) < 0)
        {
-         struct sockaddr_dl *adl = (struct sockaddr_dl *) ifp;
-         char *name = malloc(adl->sdl_nlen+1);
-         check_malloc_return(name);
-         memcpy(name, adl->sdl_data, adl->sdl_nlen);
-         name[adl->sdl_nlen] = '\0';
-         *ifname = name;
+         msg (M_WARN, "GDG: ioctl #2 failed");
+         goto done;
        }
+       close(sockfd);
+       sockfd = -1;
  
-       gc_free (&gc);
-       return true;
-     }
-   else
-     {
-       gc_free (&gc);
-       return false;
+       for (cp = buffer; cp <= buffer + ifc.ifc_len - sizeof(struct ifreq); )
+       {
+         ifr = (struct ifreq *)cp;
+         const size_t len = sizeof(ifr->ifr_name) + max(sizeof(ifr->ifr_addr), ifr->ifr_addr.sa_len);
+         if (!ifr->ifr_addr.sa_family)
+           break;
+         if (!strncmp(ifr->ifr_name, rgi->iface, IFNAMSIZ))
+           {
+             if (ifr->ifr_addr.sa_family == AF_LINK)
+               {
+                 struct sockaddr_dl *sdl = (struct sockaddr_dl *)&ifr->ifr_addr;
+                 memcpy(rgi->hwaddr, LLADDR(sdl), 6);
+                 rgi->flags |= RGI_HWADDR_DEFINED;
+               }
+           }
+         cp += len;
+       }
      }
- }
  
- bool
get_default_gateway (in_addr_t *ret, in_addr_t *netmask)
- {
-   return get_default_gateway_ex(ret, netmask, NULL);
+  done:
  if (sockfd >= 0)
+     close(sockfd);
+   gc_free (&gc);
  }
  
+ #undef max
  #elif defined(TARGET_OPENBSD) || defined(TARGET_NETBSD)
  
  #include <sys/types.h>
diff --cc route.h
index 6a7704f09c50c09c8ec6922cfb0f55e80dd76a7d,b25bb8839f28ec128ed5250e0db6f50741cc77b9..f900d3e0ad8878a0306436d0e51c04b7a99810bf
+++ b/route.h
@@@ -92,21 -96,11 +96,24 @@@ struct route_option_list 
    struct route_option routes[EMPTY_ARRAY_SIZE];
  };
  
 +struct route_ipv6_option {
 +  const char *prefix;         /* e.g. "2001:db8:1::/64" */
 +  const char *gateway;                /* e.g. "2001:db8:0::2" */
 +  const char *metric;         /* e.g. "5" */
 +};
 +
 +struct route_ipv6_option_list {
 +  unsigned int flags;
 +  int capacity;
 +  int n;
 +  struct route_ipv6_option routes_ipv6[EMPTY_ARRAY_SIZE];
 +};
 +
  struct route {
-   bool defined;
+ # define RT_DEFINED        (1<<0)
+ # define RT_ADDED          (1<<1)
+ # define RT_METRIC_DEFINED (1<<2)
+   unsigned int flags;
    const struct route_option *option;
    in_addr_t network;
    in_addr_t netmask;
    int metric;
  };
  
- struct route_list {
-   bool routes_added;
-   struct route_special_addr spec;
-   unsigned int flags;
-   bool did_redirect_default_gateway;
-   bool did_local;
-   int capacity;
-   int n;
-   struct route routes[EMPTY_ARRAY_SIZE];
- };
 +struct route_ipv6 {
 +  bool defined;
 +  const struct route_ipv6_option *option;
 +  struct in6_addr network;
 +  unsigned int netbits;
 +  struct in6_addr gateway;
 +  bool metric_defined;
 +  int metric;
 +};
 +
 +struct route_ipv6_list {
 +  bool routes_added;
 +  unsigned int flags;
 +  int default_metric;
 +  bool default_metric_defined;
 +  struct in6_addr remote_endpoint_ipv6;
 +  bool remote_endpoint_defined;
 +  bool did_redirect_default_gateway;                  /* TODO (?) */
 +  bool did_local;                                     /* TODO (?) */
 +  int capacity;
 +  int n;
 +  struct route_ipv6 routes_ipv6[EMPTY_ARRAY_SIZE];
 +};
 +
 +
+ struct route_gateway_address {
+   in_addr_t addr;
+   in_addr_t netmask;
+ };
+ struct route_gateway_info {
+ # define RGI_ADDR_DEFINED     (1<<0) /* set if gateway.addr defined */
+ # define RGI_NETMASK_DEFINED  (1<<1) /* set if gateway.netmask defined */
+ # define RGI_HWADDR_DEFINED   (1<<2) /* set if hwaddr is defined */
+ # define RGI_IFACE_DEFINED    (1<<3) /* set if iface is defined */
+ # define RGI_OVERFLOW         (1<<4) /* set if more interface addresses than will fit in addrs */
+   unsigned int flags;
+   /* gateway interface */
+ # ifdef WIN32
+   DWORD adapter_index;  /* interface or ~0 if undefined */
+ #else
+   char iface[16]; /* interface name (null terminated), may be empty */
+ #endif
+   /* gateway interface hardware address */
+   uint8_t hwaddr[6];
+   /* gateway/router address */
+   struct route_gateway_address gateway;
+   /* address/netmask pairs bound to interface */
+ # define RGI_N_ADDRESSES 8
+   int n_addrs; /* len of addrs, may be 0 */
+   struct route_gateway_address addrs[RGI_N_ADDRESSES]; /* local addresses attached to iface */
+ };
+ struct route_list {
+ # define RL_DID_REDIRECT_DEFAULT_GATEWAY (1<<0)
+ # define RL_DID_LOCAL                    (1<<1)
+ # define RL_ROUTES_ADDED                 (1<<2)
+   unsigned int iflags;
+   struct route_special_addr spec;
+   struct route_gateway_info rgi;
+   unsigned int flags;     /* RG_x flags */
+   int capacity;
+   int n;
+   struct route routes[EMPTY_ARRAY_SIZE];
+ };
  #if P2MP
  /* internal OpenVPN route */
  struct iroute {
@@@ -167,17 -164,17 +208,23 @@@ struct iroute_ipv6 
  #endif
  
  struct route_option_list *new_route_option_list (const int max_routes, struct gc_arena *a);
 +struct route_ipv6_option_list *new_route_ipv6_option_list (const int max_routes, struct gc_arena *a);
++
  struct route_option_list *clone_route_option_list (const struct route_option_list *src, struct gc_arena *a);
  void copy_route_option_list (struct route_option_list *dest, const struct route_option_list *src);
  
  struct route_list *new_route_list (const int max_routes, struct gc_arena *a);
- void add_route (struct route *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es);
 +struct route_ipv6_list *new_route_ipv6_list (const int max_routes, struct gc_arena *a);
 +
 +void add_route_ipv6 (struct route_ipv6 *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es);
 +void delete_route_ipv6 (const struct route_ipv6 *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es);
  
+ void add_route (struct route *r,
+               const struct tuntap *tt,
+               unsigned int flags,
+               const struct route_gateway_info *rgi,
+               const struct env_set *es);
  void add_route_to_option_list (struct route_option_list *l,
                               const char *network,
                               const char *netmask,
@@@ -196,18 -188,11 +243,18 @@@ bool init_route_list (struct route_lis
                      in_addr_t remote_host,
                      struct env_set *es);
  
- void route_list_add_default_gateway (struct route_list *rl,
-                                    struct env_set *es,
-                                    const in_addr_t addr);
 +bool init_route_ipv6_list (struct route_ipv6_list *rl6,
 +                    const struct route_ipv6_option_list *opt6,
 +                    const char *remote_endpoint,
 +                    int default_metric,
 +                    struct env_set *es);
 +
+ void route_list_add_vpn_gateway (struct route_list *rl,
+                                struct env_set *es,
+                                const in_addr_t addr);
  
  void add_routes (struct route_list *rl,
 +               struct route_ipv6_list *rl6,
                 const struct tuntap *tt,
                 unsigned int flags,
                 const struct env_set *es);
@@@ -219,11 -203,11 +266,14 @@@ void delete_routes (struct route_list *
                    const struct env_set *es);
  
  void setenv_routes (struct env_set *es, const struct route_list *rl);
 +void setenv_routes_ipv6 (struct env_set *es, const struct route_ipv6_list *rl6);
 +
++
  bool is_special_addr (const char *addr_str);
  
- bool get_default_gateway (in_addr_t *ip, in_addr_t *netmask);
+ void get_default_gateway (struct route_gateway_info *rgi);
+ void print_default_gateway(const int msglevel, const struct route_gateway_info *rgi);
  
  /*
   * Test if addr is reachable via a local interface (return ILA_LOCAL),
diff --cc ssl.c
index a0493ff978309957fa48879d1d9f3cfdbd6d3dc8,d039f5592017a2f607e314be5484bb233fa08a94..11b780115aaa018c6bf18083629cbdbf1d0656bf
--- 1/ssl.c
--- 2/ssl.c
+++ b/ssl.c
@@@ -302,10 -300,28 +303,28 @@@ auth_user_pass_setup (const char *auth_
      {
  #if AUTO_USERID
        get_user_pass_auto_userid (&auth_user_pass, auth_file);
- #elif defined(ENABLE_CLIENT_CR)
-       get_user_pass_cr (&auth_user_pass, auth_file, UP_TYPE_AUTH, GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE, auth_challenge);
  #else
-       get_user_pass (&auth_user_pass, auth_file, UP_TYPE_AUTH, GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE);
+ # ifdef ENABLE_CLIENT_CR
+       if (auth_challenge) /* dynamic challenge/response */
 -      get_user_pass_cr (&auth_user_pass,
 -                        auth_file,
 -                        UP_TYPE_AUTH,
 -                        GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE|GET_USER_PASS_DYNAMIC_CHALLENGE,
 -                        auth_challenge);
++       get_user_pass_cr (&auth_user_pass,
++                         auth_file,
++                         UP_TYPE_AUTH,
++                         GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE|GET_USER_PASS_DYNAMIC_CHALLENGE,
++                         auth_challenge);
+       else if (sci) /* static challenge response */
 -      {
 -        int flags = GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE|GET_USER_PASS_STATIC_CHALLENGE;
 -        if (sci->flags & SC_ECHO)
 -          flags |= GET_USER_PASS_STATIC_CHALLENGE_ECHO;
 -        get_user_pass_cr (&auth_user_pass,
 -                          auth_file,
 -                          UP_TYPE_AUTH,
 -                          flags,
 -                          sci->challenge_text);
 -      }
++       {
++         int flags = GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE|GET_USER_PASS_STATIC_CHALLENGE;
++         if (sci->flags & SC_ECHO)
++           flags |= GET_USER_PASS_STATIC_CHALLENGE_ECHO;
++         get_user_pass_cr (&auth_user_pass,
++                           auth_file,
++                           UP_TYPE_AUTH,
++                           flags,
++                           sci->challenge_text);
++       }
+       else
+ # endif
 -      get_user_pass (&auth_user_pass, auth_file, UP_TYPE_AUTH, GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE);
++       get_user_pass (&auth_user_pass, auth_file, UP_TYPE_AUTH, GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE);
  #endif
      }
  }
diff --cc ssl.h
Simple merge
diff --cc syshead.h
Simple merge
diff --cc tun.c
index aa7f66710857b9cac8a724b0da22d3f09b03aeef,c775be6341425efdf8a01f47a76e995576552994..1d3f8bca4b34d384c3c5e6051bfaeb7ecf9c2cc7
--- 1/tun.c
--- 2/tun.c
+++ b/tun.c
@@@ -276,12 -300,13 +278,13 @@@ voi
  warn_on_use_of_common_subnets (void)
  {
    struct gc_arena gc = gc_new ();
-   in_addr_t lan_gw = 0;
-   in_addr_t lan_netmask = 0;
+   struct route_gateway_info rgi;
+   const int needed = (RGI_ADDR_DEFINED|RGI_NETMASK_DEFINED);
  
-   if (get_default_gateway (&lan_gw, &lan_netmask))
+   get_default_gateway (&rgi);
+   if ((rgi.flags & needed) == needed)
      {
-       const in_addr_t lan_network = lan_gw & lan_netmask; 
 -      const in_addr_t lan_network = rgi.gateway.addr & rgi.gateway.netmask; 
++      const in_addr_t lan_network = rgi.gateway.addr & rgi.gateway.netmask;
        if (lan_network == 0xC0A80000 || lan_network == 0xC0A80100)
        msg (M_WARN, "NOTE: your local LAN uses the extremely common subnet address 192.168.0.x or 192.168.1.x.  Be aware that this might create routing conflicts if you connect to the VPN server from public locations such as internet cafes that use the same subnet.");
      }