src/openvpn/helper.c
6fbf66fa
 /*
  *  OpenVPN -- An application to securely tunnel IP networks
  *             over a single TCP/UDP port, with support for SSL/TLS-based
  *             session authentication and key exchange,
  *             packet encryption, packet authentication, and
  *             packet compression.
  *
49979459
  *  Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
6fbf66fa
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2
  *  as published by the Free Software Foundation.
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
caa54ac3
  *  You should have received a copy of the GNU General Public License along
  *  with this program; if not, write to the Free Software Foundation, Inc.,
  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
6fbf66fa
  */
 
c110b289
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #elif defined(_MSC_VER)
 #include "config-msvc.h"
 #endif
 
6fbf66fa
 #include "syshead.h"
 
 #include "forward.h"
 #include "helper.h"
 #include "pool.h"
 #include "push.h"
 
 #include "memdbg.h"
 
 #if P2MP_SERVER
 
 static const char *
81d882d5
 print_netmask(int netbits, struct gc_arena *gc)
6fbf66fa
 {
81d882d5
     struct buffer out = alloc_buf_gc(128, gc);
     const in_addr_t netmask = netbits_to_netmask(netbits);
6fbf66fa
 
81d882d5
     buf_printf(&out, "%s (/%d)", print_in_addr_t(netmask, 0, gc), netbits);
6fbf66fa
 
81d882d5
     return BSTR(&out);
6fbf66fa
 }
 
 static const char *
81d882d5
 print_opt_route_gateway(const in_addr_t route_gateway, struct gc_arena *gc)
6fbf66fa
 {
81d882d5
     struct buffer out = alloc_buf_gc(128, gc);
     ASSERT(route_gateway);
     buf_printf(&out, "route-gateway %s", print_in_addr_t(route_gateway, 0, gc));
     return BSTR(&out);
6fbf66fa
 }
 
 static const char *
81d882d5
 print_opt_route_gateway_dhcp(struct gc_arena *gc)
03731db3
 {
81d882d5
     struct buffer out = alloc_buf_gc(32, gc);
     buf_printf(&out, "route-gateway dhcp");
     return BSTR(&out);
03731db3
 }
 
 static const char *
81d882d5
 print_opt_route(const in_addr_t network, const in_addr_t netmask, struct gc_arena *gc)
6fbf66fa
 {
81d882d5
     struct buffer out = alloc_buf_gc(128, gc);
     ASSERT(network);
 
     if (netmask)
     {
         buf_printf(&out, "route %s %s",
                    print_in_addr_t(network, 0, gc),
                    print_in_addr_t(netmask, 0, gc));
     }
     else
     {
         buf_printf(&out, "route %s",
                    print_in_addr_t(network, 0, gc));
     }
 
     return BSTR(&out);
6fbf66fa
 }
 
 static const char *
81d882d5
 print_opt_topology(const int topology, struct gc_arena *gc)
3c7f2f55
 {
81d882d5
     struct buffer out = alloc_buf_gc(128, gc);
3c7f2f55
 
81d882d5
     buf_printf(&out, "topology %s", print_topology(topology));
3c7f2f55
 
81d882d5
     return BSTR(&out);
3c7f2f55
 }
 
 static const char *
81d882d5
 print_str_int(const char *str, const int i, struct gc_arena *gc)
6fbf66fa
 {
81d882d5
     struct buffer out = alloc_buf_gc(128, gc);
     buf_printf(&out, "%s %d", str, i);
     return BSTR(&out);
6fbf66fa
 }
 
ae3b3746
 static const char *
81d882d5
 print_str(const char *str, struct gc_arena *gc)
ae3b3746
 {
81d882d5
     struct buffer out = alloc_buf_gc(128, gc);
     buf_printf(&out, "%s", str);
     return BSTR(&out);
ae3b3746
 }
 
6fbf66fa
 static void
81d882d5
 helper_add_route(const in_addr_t network, const in_addr_t netmask, struct options *o)
6fbf66fa
 {
81d882d5
     rol_check_alloc(o);
     add_route_to_option_list(o->routes,
                              print_in_addr_t(network, 0, &o->gc),
                              print_in_addr_t(netmask, 0, &o->gc),
                              NULL,
                              NULL);
6fbf66fa
 }
 
 static void
81d882d5
 verify_common_subnet(const char *opt, const in_addr_t a, const in_addr_t b, const in_addr_t subnet)
6fbf66fa
 {
81d882d5
     struct gc_arena gc = gc_new();
     if ((a & subnet) != (b & subnet))
     {
         msg(M_USAGE, "%s IP addresses %s and %s are not in the same %s subnet",
             opt,
             print_in_addr_t(a, 0, &gc),
             print_in_addr_t(b, 0, &gc),
             print_in_addr_t(subnet, 0, &gc));
     }
     gc_free(&gc);
6fbf66fa
 }
 
81d882d5
 #endif /* if P2MP_SERVER */
6fbf66fa
 
 /*
  * Process server, server-bridge, and client helper
  * directives after the parameters themselves have been
  * parsed and placed in struct options.
  */
 void
81d882d5
 helper_client_server(struct options *o)
6fbf66fa
 {
81d882d5
     struct gc_arena gc = gc_new();
6fbf66fa
 
 #if P2MP
 #if P2MP_SERVER
512cda46
 
afb6e85d
 /*
81d882d5
  * Get tun/tap/null device type
  */
     const int dev = dev_type_enum(o->dev, o->dev_type);
     const int topology = o->topology;
 
     /*
      *
      * HELPER DIRECTIVE for IPv6
      *
      * server-ipv6 2001:db8::/64
      *
      * EXPANDS TO:
      *
      * tun-ipv6
      * push "tun-ipv6"
      * ifconfig-ipv6 2001:db8::1 2001:db8::2
      * if !nopool:
      *   ifconfig-ipv6-pool 2001:db8::1000/64
      *
      */
     if (o->server_ipv6_defined)
     {
         if (!o->server_defined)
         {
             msg(M_USAGE, "--server-ipv6 must be used together with --server");
         }
         if (o->server_flags & SF_NOPOOL)
         {
             msg( M_USAGE, "--server-ipv6 is incompatible with 'nopool' option" );
         }
         if (o->ifconfig_ipv6_pool_defined)
         {
             msg( M_USAGE, "--server-ipv6 already defines an ifconfig-ipv6-pool, so you can't also specify --ifconfig-pool explicitly");
         }
512cda46
 
         /* local ifconfig is "base address + 1" and "+2" */
81d882d5
         o->ifconfig_ipv6_local =
             print_in6_addr( add_in6_addr( o->server_network_ipv6, 1), 0, &o->gc );
         o->ifconfig_ipv6_remote =
             print_in6_addr( add_in6_addr( o->server_network_ipv6, 2), 0, &o->gc );
         o->ifconfig_ipv6_netbits = o->server_netbits_ipv6;
 
         /* pool starts at "base address + 0x1000" - leave enough room */
         ASSERT( o->server_netbits_ipv6 <= 112 );        /* want 16 bits */
 
         o->ifconfig_ipv6_pool_defined = true;
         o->ifconfig_ipv6_pool_base =
             add_in6_addr( o->server_network_ipv6, 0x1000 );
         o->ifconfig_ipv6_pool_netbits = o->server_netbits_ipv6;
 
         push_option( o, "tun-ipv6", M_USAGE );
     }
 
     /*
      *
      * HELPER DIRECTIVE:
      *
      * server 10.8.0.0 255.255.255.0
      *
      * EXPANDS TO:
      *
      * mode server
      * tls-server
      * push "topology [topology]"
      *
      * if tun AND (topology == net30 OR topology == p2p):
      *   ifconfig 10.8.0.1 10.8.0.2
      *   if !nopool:
      *     ifconfig-pool 10.8.0.4 10.8.0.251
      *   route 10.8.0.0 255.255.255.0
      *   if client-to-client:
      *     push "route 10.8.0.0 255.255.255.0"
      *   else if topology == net30:
      *     push "route 10.8.0.1"
      *
      * if tap OR (tun AND topology == subnet):
      *   ifconfig 10.8.0.1 255.255.255.0
      *   if !nopool:
      *     ifconfig-pool 10.8.0.2 10.8.0.253 255.255.255.0
      *   push "route-gateway 10.8.0.1"
      *   if route-gateway unset:
      *     route-gateway 10.8.0.2
      */
 
     if (o->server_defined)
6fbf66fa
     {
81d882d5
         int netbits = -2;
         bool status = false;
 
         if (o->client)
         {
             msg(M_USAGE, "--server and --client cannot be used together");
         }
 
         if (o->server_bridge_defined || o->server_bridge_proxy_dhcp)
         {
             msg(M_USAGE, "--server and --server-bridge cannot be used together");
         }
 
         if (o->shared_secret_file)
         {
             msg(M_USAGE, "--server and --secret cannot be used together (you must use SSL/TLS keys)");
         }
 
         if (!(o->server_flags & SF_NOPOOL) && o->ifconfig_pool_defined)
         {
             msg(M_USAGE, "--server already defines an ifconfig-pool, so you can't also specify --ifconfig-pool explicitly");
         }
 
         if (!(dev == DEV_TYPE_TAP || dev == DEV_TYPE_TUN))
         {
             msg(M_USAGE, "--server directive only makes sense with --dev tun or --dev tap");
         }
 
         status = netmask_to_netbits(o->server_network, o->server_netmask, &netbits);
         if (!status)
         {
             msg(M_USAGE, "--server directive network/netmask combination is invalid");
         }
 
         if (netbits < 0)
         {
             msg(M_USAGE, "--server directive netmask is invalid");
         }
 
         if (netbits < IFCONFIG_POOL_MIN_NETBITS)
         {
             msg(M_USAGE, "--server directive netmask allows for too many host addresses (subnet must be %s or higher)",
                 print_netmask(IFCONFIG_POOL_MIN_NETBITS, &gc));
         }
 
         if (dev == DEV_TYPE_TUN)
         {
             int pool_end_reserve = 4;
 
             if (netbits > 29)
             {
                 msg(M_USAGE, "--server directive when used with --dev tun must define a subnet of %s or lower",
                     print_netmask(29, &gc));
             }
 
             if (netbits == 29)
             {
                 pool_end_reserve = 0;
             }
 
             o->mode = MODE_SERVER;
             o->tls_server = true;
 
             if (topology == TOP_NET30 || topology == TOP_P2P)
             {
                 o->ifconfig_local = print_in_addr_t(o->server_network + 1, 0, &o->gc);
                 o->ifconfig_remote_netmask = print_in_addr_t(o->server_network + 2, 0, &o->gc);
 
                 if (!(o->server_flags & SF_NOPOOL))
                 {
                     o->ifconfig_pool_defined = true;
                     o->ifconfig_pool_start = o->server_network + 4;
                     o->ifconfig_pool_end = (o->server_network | ~o->server_netmask) - pool_end_reserve;
                     ifconfig_pool_verify_range(M_USAGE, o->ifconfig_pool_start, o->ifconfig_pool_end);
                 }
 
                 helper_add_route(o->server_network, o->server_netmask, o);
                 if (o->enable_c2c)
                 {
                     push_option(o, print_opt_route(o->server_network, o->server_netmask, &o->gc), M_USAGE);
                 }
                 else if (topology == TOP_NET30)
                 {
                     push_option(o, print_opt_route(o->server_network + 1, 0, &o->gc), M_USAGE);
                 }
             }
             else if (topology == TOP_SUBNET)
             {
                 o->ifconfig_local = print_in_addr_t(o->server_network + 1, 0, &o->gc);
                 o->ifconfig_remote_netmask = print_in_addr_t(o->server_netmask, 0, &o->gc);
 
                 if (!(o->server_flags & SF_NOPOOL))
                 {
                     o->ifconfig_pool_defined = true;
                     o->ifconfig_pool_start = o->server_network + 2;
                     o->ifconfig_pool_end = (o->server_network | ~o->server_netmask) - 2;
                     ifconfig_pool_verify_range(M_USAGE, o->ifconfig_pool_start, o->ifconfig_pool_end);
                 }
                 o->ifconfig_pool_netmask = o->server_netmask;
 
                 push_option(o, print_opt_route_gateway(o->server_network + 1, &o->gc), M_USAGE);
                 if (!o->route_default_gateway)
                 {
                     o->route_default_gateway = print_in_addr_t(o->server_network + 2, 0, &o->gc);
                 }
             }
             else
             {
                 ASSERT(0);
             }
 
             push_option(o, print_opt_topology(topology, &o->gc), M_USAGE);
         }
         else if (dev == DEV_TYPE_TAP)
         {
             if (netbits > 30)
             {
                 msg(M_USAGE, "--server directive when used with --dev tap must define a subnet of %s or lower",
                     print_netmask(30, &gc));
             }
 
             o->mode = MODE_SERVER;
             o->tls_server = true;
             o->ifconfig_local = print_in_addr_t(o->server_network + 1, 0, &o->gc);
             o->ifconfig_remote_netmask = print_in_addr_t(o->server_netmask, 0, &o->gc);
 
             if (!(o->server_flags & SF_NOPOOL))
             {
                 o->ifconfig_pool_defined = true;
                 o->ifconfig_pool_start = o->server_network + 2;
                 o->ifconfig_pool_end = (o->server_network | ~o->server_netmask) - 1;
                 ifconfig_pool_verify_range(M_USAGE, o->ifconfig_pool_start, o->ifconfig_pool_end);
             }
             o->ifconfig_pool_netmask = o->server_netmask;
 
             push_option(o, print_opt_route_gateway(o->server_network + 1, &o->gc), M_USAGE);
         }
         else
         {
             ASSERT(0);
         }
 
         /* set push-ifconfig-constraint directive */
         if ((dev == DEV_TYPE_TAP || topology == TOP_SUBNET))
         {
             o->push_ifconfig_constraint_defined = true;
             o->push_ifconfig_constraint_network = o->server_network;
             o->push_ifconfig_constraint_netmask = o->server_netmask;
         }
6fbf66fa
     }
 
81d882d5
     /*
      * HELPER DIRECTIVE:
      *
      * server-bridge 10.8.0.4 255.255.255.0 10.8.0.128 10.8.0.254
      *
      * EXPANDS TO:
      *
      * mode server
      * tls-server
      *
      * ifconfig-pool 10.8.0.128 10.8.0.254 255.255.255.0
      * push "route-gateway 10.8.0.4"
      *
      * OR
      *
      * server-bridge
      *
      * EXPANDS TO:
      *
      * mode server
      * tls-server
      *
      * if !nogw:
      *   push "route-gateway dhcp"
      */
     else if (o->server_bridge_defined | o->server_bridge_proxy_dhcp)
6fbf66fa
     {
81d882d5
         if (o->client)
         {
             msg(M_USAGE, "--server-bridge and --client cannot be used together");
         }
 
         if (!(o->server_flags & SF_NOPOOL) && o->ifconfig_pool_defined)
         {
             msg(M_USAGE, "--server-bridge already defines an ifconfig-pool, so you can't also specify --ifconfig-pool explicitly");
         }
 
         if (o->shared_secret_file)
         {
             msg(M_USAGE, "--server-bridge and --secret cannot be used together (you must use SSL/TLS keys)");
         }
 
         if (dev != DEV_TYPE_TAP)
         {
             msg(M_USAGE, "--server-bridge directive only makes sense with --dev tap");
         }
 
         if (o->server_bridge_defined)
         {
             verify_common_subnet("--server-bridge", o->server_bridge_ip, o->server_bridge_pool_start, o->server_bridge_netmask);
             verify_common_subnet("--server-bridge", o->server_bridge_pool_start, o->server_bridge_pool_end, o->server_bridge_netmask);
             verify_common_subnet("--server-bridge", o->server_bridge_ip, o->server_bridge_pool_end, o->server_bridge_netmask);
         }
 
         o->mode = MODE_SERVER;
         o->tls_server = true;
 
         if (o->server_bridge_defined)
         {
             o->ifconfig_pool_defined = true;
             o->ifconfig_pool_start = o->server_bridge_pool_start;
             o->ifconfig_pool_end = o->server_bridge_pool_end;
             ifconfig_pool_verify_range(M_USAGE, o->ifconfig_pool_start, o->ifconfig_pool_end);
             o->ifconfig_pool_netmask = o->server_bridge_netmask;
             push_option(o, print_opt_route_gateway(o->server_bridge_ip, &o->gc), M_USAGE);
         }
         else if (o->server_bridge_proxy_dhcp && !(o->server_flags & SF_NO_PUSH_ROUTE_GATEWAY))
         {
             push_option(o, print_opt_route_gateway_dhcp(&o->gc), M_USAGE);
         }
6fbf66fa
     }
81d882d5
     else
6fbf66fa
 #endif /* P2MP_SERVER */
 
81d882d5
     /*
      * HELPER DIRECTIVE:
      *
      * client
      *
      * EXPANDS TO:
      *
      * pull
      * tls-client
      */
     if (o->client)
6fbf66fa
     {
81d882d5
         if (o->key_method != 2)
         {
             msg(M_USAGE, "--client requires --key-method 2");
         }
6fbf66fa
 
81d882d5
         o->pull = true;
         o->tls_client = true;
6fbf66fa
     }
 
 #endif /* P2MP */
 
81d882d5
     gc_free(&gc);
6fbf66fa
 }
 
 /*
  *
  * HELPER DIRECTIVE:
  *
  * keepalive 10 60
  *
  * EXPANDS TO:
  *
  * if mode server:
  *   ping 10
  *   ping-restart 120
  *   push "ping 10"
  *   push "ping-restart 60"
  * else
  *   ping 10
  *   ping-restart 60
  */
 void
81d882d5
 helper_keepalive(struct options *o)
6fbf66fa
 {
81d882d5
     if (o->keepalive_ping || o->keepalive_timeout)
6fbf66fa
     {
81d882d5
         /*
          * Sanity checks.
          */
         if (o->keepalive_ping <= 0 || o->keepalive_timeout <= 0)
         {
             msg(M_USAGE, "--keepalive parameters must be > 0");
         }
         if (o->keepalive_ping * 2 > o->keepalive_timeout)
         {
             msg(M_USAGE, "the second parameter to --keepalive (restart timeout=%d) must be at least twice the value of the first parameter (ping interval=%d).  A ratio of 1:5 or 1:6 would be even better.  Recommended setting is --keepalive 10 60.",
                 o->keepalive_timeout,
                 o->keepalive_ping);
         }
         if (o->ping_send_timeout || o->ping_rec_timeout)
         {
             msg(M_USAGE, "--keepalive conflicts with --ping, --ping-exit, or --ping-restart.  If you use --keepalive, you don't need any of the other --ping directives.");
         }
 
         /*
          * Expand.
          */
         if (o->mode == MODE_POINT_TO_POINT)
         {
             o->ping_rec_timeout_action = PING_RESTART;
             o->ping_send_timeout = o->keepalive_ping;
             o->ping_rec_timeout = o->keepalive_timeout;
         }
6fbf66fa
 #if P2MP_SERVER
81d882d5
         else if (o->mode == MODE_SERVER)
         {
             o->ping_rec_timeout_action = PING_RESTART;
             o->ping_send_timeout = o->keepalive_ping;
             o->ping_rec_timeout = o->keepalive_timeout * 2;
             push_option(o, print_str_int("ping", o->keepalive_ping, &o->gc), M_USAGE);
             push_option(o, print_str_int("ping-restart", o->keepalive_timeout, &o->gc), M_USAGE);
         }
6fbf66fa
 #endif
81d882d5
         else
         {
             ASSERT(0);
         }
6fbf66fa
     }
 }
ae3b3746
 
 /*
  *
  * HELPER DIRECTIVE:
  *
  * tcp-nodelay
  *
  * EXPANDS TO:
  *
  * if mode server:
  *   socket-flags TCP_NODELAY
  *   push "socket-flags TCP_NODELAY"
  */
 void
81d882d5
 helper_tcp_nodelay(struct options *o)
ae3b3746
 {
 #if P2MP_SERVER
81d882d5
     if (o->server_flags & SF_TCP_NODELAY_HELPER)
ae3b3746
     {
81d882d5
         if (o->mode == MODE_SERVER)
         {
             o->sockflags |= SF_TCP_NODELAY;
             push_option(o, print_str("socket-flags TCP_NODELAY", &o->gc), M_USAGE);
         }
         else
         {
             o->sockflags |= SF_TCP_NODELAY;
         }
ae3b3746
     }
 #endif
 }