src/openvpn/pool.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.
  *
58716979
  *  Copyright (C) 2002-2017 OpenVPN Technologies, 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.
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program (see the file COPYING included with this
  *  distribution); if not, write to the Free Software Foundation, Inc.,
  *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
c110b289
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #elif defined(_MSC_VER)
 #include "config-msvc.h"
 #endif
 
6fbf66fa
 #include "syshead.h"
 
 #include "pool.h"
 #include "buffer.h"
 #include "error.h"
 #include "socket.h"
 #include "otime.h"
 
 #include "memdbg.h"
 
 #if P2MP
 
 static void
81d882d5
 ifconfig_pool_entry_free(struct ifconfig_pool_entry *ipe, bool hard)
6fbf66fa
 {
81d882d5
     ipe->in_use = false;
     if (hard && ipe->common_name)
6fbf66fa
     {
81d882d5
         free(ipe->common_name);
         ipe->common_name = NULL;
     }
     if (hard)
     {
         ipe->last_release = 0;
     }
     else
     {
         ipe->last_release = now;
6fbf66fa
     }
 }
 
 static int
81d882d5
 ifconfig_pool_find(struct ifconfig_pool *pool, const char *common_name)
6fbf66fa
 {
81d882d5
     int i;
     time_t earliest_release = 0;
     int previous_usage = -1;
     int new_usage = -1;
6fbf66fa
 
81d882d5
     for (i = 0; i < pool->size; ++i)
6fbf66fa
     {
81d882d5
         struct ifconfig_pool_entry *ipe = &pool->list[i];
         if (!ipe->in_use)
         {
             /*
              * If duplicate_cn mode, take first available IP address
              */
             if (pool->duplicate_cn)
             {
                 new_usage = i;
                 break;
             }
 
             /*
              * Keep track of the unused IP address entry which
              * was released earliest.
              */
             if ((new_usage == -1 || ipe->last_release < earliest_release) && !ipe->fixed)
             {
                 earliest_release = ipe->last_release;
                 new_usage = i;
             }
 
             /*
              * Keep track of a possible allocation to us
              * from an earlier session.
              */
             if (previous_usage < 0
                 && common_name
                 && ipe->common_name
                 && !strcmp(common_name, ipe->common_name))
             {
                 previous_usage = i;
             }
 
         }
6fbf66fa
     }
 
81d882d5
     if (previous_usage >= 0)
     {
         return previous_usage;
     }
6fbf66fa
 
81d882d5
     if (new_usage >= 0)
     {
         return new_usage;
     }
6fbf66fa
 
81d882d5
     return -1;
6fbf66fa
 }
 
92bbb061
 /*
  * Verify start/end range
  */
 bool
81d882d5
 ifconfig_pool_verify_range(const int msglevel, const in_addr_t start, const in_addr_t end)
92bbb061
 {
81d882d5
     struct gc_arena gc = gc_new();
     bool ret = true;
92bbb061
 
81d882d5
     if (start > end)
92bbb061
     {
81d882d5
         msg(msglevel, "--ifconfig-pool start IP [%s] is greater than end IP [%s]",
             print_in_addr_t(start, 0, &gc),
             print_in_addr_t(end, 0, &gc));
         ret = false;
92bbb061
     }
81d882d5
     if (end - start >= IFCONFIG_POOL_MAX)
92bbb061
     {
81d882d5
         msg(msglevel, "--ifconfig-pool address range is too large [%s -> %s].  Current maximum is %d addresses, as defined by IFCONFIG_POOL_MAX variable.",
             print_in_addr_t(start, 0, &gc),
             print_in_addr_t(end, 0, &gc),
             IFCONFIG_POOL_MAX);
         ret = false;
92bbb061
     }
81d882d5
     gc_free(&gc);
     return ret;
92bbb061
 }
6fbf66fa
 
 struct ifconfig_pool *
81d882d5
 ifconfig_pool_init(int type, in_addr_t start, in_addr_t end,
                    const bool duplicate_cn,
                    const bool ipv6_pool, const struct in6_addr ipv6_base,
                    const int ipv6_netbits )
6fbf66fa
 {
81d882d5
     struct gc_arena gc = gc_new();
     struct ifconfig_pool *pool = NULL;
6fbf66fa
 
81d882d5
     ASSERT(start <= end && end - start < IFCONFIG_POOL_MAX);
     ALLOC_OBJ_CLEAR(pool, struct ifconfig_pool);
6fbf66fa
 
81d882d5
     pool->type = type;
     pool->duplicate_cn = duplicate_cn;
6fbf66fa
 
81d882d5
     switch (type)
6fbf66fa
     {
81d882d5
         case IFCONFIG_POOL_30NET:
             pool->base = start & ~3;
             pool->size = (((end | 3) + 1) - pool->base) >> 2;
             break;
 
         case IFCONFIG_POOL_INDIV:
             pool->base = start;
             pool->size = end - start + 1;
             break;
 
         default:
             ASSERT(0);
6fbf66fa
     }
 
81d882d5
     /* IPv6 pools are always "INDIV" type */
     pool->ipv6 = ipv6_pool;
512cda46
 
81d882d5
     if (pool->ipv6)
512cda46
     {
81d882d5
         pool->base_ipv6 = ipv6_base;
         pool->size_ipv6 = ipv6_netbits>96 ? ( 1<<(128-ipv6_netbits) )
                           : IFCONFIG_POOL_MAX;
 
         msg( D_IFCONFIG_POOL, "IFCONFIG POOL IPv6: (IPv4) size=%d, size_ipv6=%d, netbits=%d, base_ipv6=%s",
              pool->size, pool->size_ipv6, ipv6_netbits,
              print_in6_addr( pool->base_ipv6, 0, &gc ));
 
         /* the current code is very simple and assumes that the IPv6
          * pool is at least as big as the IPv4 pool, and we don't need
          * to do separate math etc. for IPv6
          */
         ASSERT( pool->size < pool->size_ipv6 );
512cda46
     }
 
81d882d5
     ALLOC_ARRAY_CLEAR(pool->list, struct ifconfig_pool_entry, pool->size);
6fbf66fa
 
81d882d5
     msg(D_IFCONFIG_POOL, "IFCONFIG POOL: base=%s size=%d, ipv6=%d",
         print_in_addr_t(pool->base, 0, &gc),
         pool->size, pool->ipv6 );
6fbf66fa
 
81d882d5
     gc_free(&gc);
     return pool;
6fbf66fa
 }
 
 void
81d882d5
 ifconfig_pool_free(struct ifconfig_pool *pool)
6fbf66fa
 {
81d882d5
     if (pool)
6fbf66fa
     {
81d882d5
         int i;
         for (i = 0; i < pool->size; ++i)
4cd4899e
         {
81d882d5
             ifconfig_pool_entry_free(&pool->list[i], true);
4cd4899e
         }
81d882d5
         free(pool->list);
         free(pool);
6fbf66fa
     }
 }
 
 ifconfig_pool_handle
81d882d5
 ifconfig_pool_acquire(struct ifconfig_pool *pool, in_addr_t *local, in_addr_t *remote, struct in6_addr *remote_ipv6, const char *common_name)
6fbf66fa
 {
81d882d5
     int i;
6fbf66fa
 
81d882d5
     i = ifconfig_pool_find(pool, common_name);
     if (i >= 0)
6fbf66fa
     {
81d882d5
         struct ifconfig_pool_entry *ipe = &pool->list[i];
         ASSERT(!ipe->in_use);
         ifconfig_pool_entry_free(ipe, true);
         ipe->in_use = true;
         if (common_name)
         {
             ipe->common_name = string_alloc(common_name, NULL);
         }
 
         switch (pool->type)
         {
             case IFCONFIG_POOL_30NET:
             {
                 in_addr_t b = pool->base + (i << 2);
                 *local = b + 1;
                 *remote = b + 2;
                 break;
             }
 
             case IFCONFIG_POOL_INDIV:
             {
                 in_addr_t b = pool->base + i;
                 *local = 0;
                 *remote = b;
                 break;
             }
 
             default:
                 ASSERT(0);
         }
 
         /* IPv6 pools are always INDIV (--linear) */
         if (pool->ipv6 && remote_ipv6)
         {
             *remote_ipv6 = add_in6_addr( pool->base_ipv6, i );
         }
6fbf66fa
     }
81d882d5
     return i;
6fbf66fa
 }
 
 bool
81d882d5
 ifconfig_pool_release(struct ifconfig_pool *pool, ifconfig_pool_handle hand, const bool hard)
6fbf66fa
 {
81d882d5
     bool ret = false;
     if (pool && hand >= 0 && hand < pool->size)
6fbf66fa
     {
81d882d5
         ifconfig_pool_entry_free(&pool->list[hand], hard);
         ret = true;
6fbf66fa
     }
81d882d5
     return ret;
6fbf66fa
 }
 
 /*
  * private access functions
  */
 
 static ifconfig_pool_handle
81d882d5
 ifconfig_pool_ip_base_to_handle(const struct ifconfig_pool *pool, const in_addr_t addr)
6fbf66fa
 {
81d882d5
     ifconfig_pool_handle ret = -1;
6fbf66fa
 
81d882d5
     switch (pool->type)
6fbf66fa
     {
81d882d5
         case IFCONFIG_POOL_30NET:
         {
             ret = (addr - pool->base) >> 2;
             break;
         }
 
         case IFCONFIG_POOL_INDIV:
         {
             ret = (addr - pool->base);
             break;
         }
 
         default:
             ASSERT(0);
6fbf66fa
     }
 
81d882d5
     if (ret < 0 || ret >= pool->size)
     {
         ret = -1;
     }
6fbf66fa
 
81d882d5
     return ret;
6fbf66fa
 }
 
 static in_addr_t
81d882d5
 ifconfig_pool_handle_to_ip_base(const struct ifconfig_pool *pool, ifconfig_pool_handle hand)
6fbf66fa
 {
81d882d5
     in_addr_t ret = 0;
6fbf66fa
 
81d882d5
     if (hand >= 0 && hand < pool->size)
6fbf66fa
     {
81d882d5
         switch (pool->type)
         {
             case IFCONFIG_POOL_30NET:
             {
                 ret = pool->base + (hand << 2);
                 break;
             }
 
             case IFCONFIG_POOL_INDIV:
             {
                 ret = pool->base + hand;
                 break;
             }
 
             default:
                 ASSERT(0);
         }
6fbf66fa
     }
 
81d882d5
     return ret;
6fbf66fa
 }
 
512cda46
 static struct in6_addr
81d882d5
 ifconfig_pool_handle_to_ipv6_base(const struct ifconfig_pool *pool, ifconfig_pool_handle hand)
512cda46
 {
81d882d5
     struct in6_addr ret = in6addr_any;
512cda46
 
81d882d5
     /* IPv6 pools are always INDIV (--linear) */
     if (hand >= 0 && hand < pool->size_ipv6)
512cda46
     {
81d882d5
         ret = add_in6_addr( pool->base_ipv6, hand );
512cda46
     }
81d882d5
     return ret;
512cda46
 }
 
6fbf66fa
 static void
81d882d5
 ifconfig_pool_set(struct ifconfig_pool *pool, const char *cn, const in_addr_t addr, const bool fixed)
6fbf66fa
 {
81d882d5
     ifconfig_pool_handle h = ifconfig_pool_ip_base_to_handle(pool, addr);
     if (h >= 0)
6fbf66fa
     {
81d882d5
         struct ifconfig_pool_entry *e = &pool->list[h];
         ifconfig_pool_entry_free(e, true);
         e->in_use = false;
         e->common_name = string_alloc(cn, NULL);
         e->last_release = now;
         e->fixed = fixed;
6fbf66fa
     }
 }
 
 static void
81d882d5
 ifconfig_pool_list(const struct ifconfig_pool *pool, struct status_output *out)
6fbf66fa
 {
81d882d5
     if (pool && out)
6fbf66fa
     {
81d882d5
         struct gc_arena gc = gc_new();
         int i;
 
         for (i = 0; i < pool->size; ++i)
         {
             const struct ifconfig_pool_entry *e = &pool->list[i];
             if (e->common_name)
             {
                 const in_addr_t ip = ifconfig_pool_handle_to_ip_base(pool, i);
                 if (pool->ipv6)
                 {
                     struct in6_addr ip6 = ifconfig_pool_handle_to_ipv6_base(pool, i);
                     status_printf(out, "%s,%s,%s",
                                   e->common_name,
                                   print_in_addr_t(ip, 0, &gc),
                                   print_in6_addr(ip6, 0, &gc));
                 }
                 else
                 {
                     status_printf(out, "%s,%s",
                                   e->common_name,
                                   print_in_addr_t(ip, 0, &gc));
                 }
             }
         }
         gc_free(&gc);
6fbf66fa
     }
 }
 
 static void
81d882d5
 ifconfig_pool_msg(const struct ifconfig_pool *pool, int msglevel)
6fbf66fa
 {
81d882d5
     struct status_output *so = status_open(NULL, 0, msglevel, NULL, 0);
     ASSERT(so);
     status_printf(so, "IFCONFIG POOL LIST");
     ifconfig_pool_list(pool, so);
     status_close(so);
6fbf66fa
 }
 
 /*
  * Deal with reading/writing the ifconfig pool database to a file
  */
 
 struct ifconfig_pool_persist *
81d882d5
 ifconfig_pool_persist_init(const char *filename, int refresh_freq)
6fbf66fa
 {
81d882d5
     struct ifconfig_pool_persist *ret;
6fbf66fa
 
81d882d5
     ASSERT(filename);
6fbf66fa
 
81d882d5
     ALLOC_OBJ_CLEAR(ret, struct ifconfig_pool_persist);
     if (refresh_freq > 0)
6fbf66fa
     {
81d882d5
         ret->fixed = false;
         ret->file = status_open(filename, refresh_freq, -1, NULL, STATUS_OUTPUT_READ|STATUS_OUTPUT_WRITE);
6fbf66fa
     }
81d882d5
     else
6fbf66fa
     {
81d882d5
         ret->fixed = true;
         ret->file = status_open(filename, 0, -1, NULL, STATUS_OUTPUT_READ);
6fbf66fa
     }
81d882d5
     return ret;
6fbf66fa
 }
 
 void
81d882d5
 ifconfig_pool_persist_close(struct ifconfig_pool_persist *persist)
6fbf66fa
 {
81d882d5
     if (persist)
6fbf66fa
     {
81d882d5
         if (persist->file)
         {
             status_close(persist->file);
         }
         free(persist);
6fbf66fa
     }
 }
 
 bool
81d882d5
 ifconfig_pool_write_trigger(struct ifconfig_pool_persist *persist)
6fbf66fa
 {
81d882d5
     if (persist->file)
     {
         return status_trigger(persist->file);
     }
     else
     {
         return false;
     }
6fbf66fa
 }
 
 void
81d882d5
 ifconfig_pool_read(struct ifconfig_pool_persist *persist, struct ifconfig_pool *pool)
6fbf66fa
 {
81d882d5
     const int buf_size = 128;
6fbf66fa
 
81d882d5
     update_time();
     if (persist && persist->file && pool)
6fbf66fa
     {
81d882d5
         struct gc_arena gc = gc_new();
         struct buffer in = alloc_buf_gc(256, &gc);
         char *cn_buf;
         char *ip_buf;
         int line = 0;
 
         ALLOC_ARRAY_CLEAR_GC(cn_buf, char, buf_size, &gc);
         ALLOC_ARRAY_CLEAR_GC(ip_buf, char, buf_size, &gc);
 
         while (true)
         {
             ASSERT(buf_init(&in, 0));
             if (!status_read(persist->file, &in))
             {
                 break;
             }
             ++line;
             if (BLEN(&in))
             {
                 int c = *BSTR(&in);
                 if (c == '#' || c == ';')
                 {
                     continue;
                 }
                 msg( M_INFO, "ifconfig_pool_read(), in='%s', TODO: IPv6",
                      BSTR(&in) );
 
                 if (buf_parse(&in, ',', cn_buf, buf_size)
                     && buf_parse(&in, ',', ip_buf, buf_size))
                 {
                     bool succeeded;
                     const in_addr_t addr = getaddr(GETADDR_HOST_ORDER, ip_buf, 0, &succeeded, NULL);
                     if (succeeded)
                     {
                         msg( M_INFO, "succeeded -> ifconfig_pool_set()");
                         ifconfig_pool_set(pool, cn_buf, addr, persist->fixed);
                     }
                 }
             }
         }
 
         ifconfig_pool_msg(pool, D_IFCONFIG_POOL);
 
         gc_free(&gc);
6fbf66fa
     }
 }
 
 void
81d882d5
 ifconfig_pool_write(struct ifconfig_pool_persist *persist, const struct ifconfig_pool *pool)
6fbf66fa
 {
81d882d5
     if (persist && persist->file && (status_rw_flags(persist->file) & STATUS_OUTPUT_WRITE) && pool)
6fbf66fa
     {
81d882d5
         status_reset(persist->file);
         ifconfig_pool_list(pool, persist->file);
         status_flush(persist->file);
6fbf66fa
     }
 }
 
 /*
  * TESTING ONLY
  */
 
 #ifdef IFCONFIG_POOL_TEST
 
 #define DUP_CN
 
 void
81d882d5
 ifconfig_pool_test(in_addr_t start, in_addr_t end)
6fbf66fa
 {
81d882d5
     struct gc_arena gc = gc_new();
     struct ifconfig_pool *p = ifconfig_pool_init(IFCONFIG_POOL_30NET, start, end);
     /*struct ifconfig_pool *p = ifconfig_pool_init (IFCONFIG_POOL_INDIV, start, end);*/
     ifconfig_pool_handle array[256];
     int i;
6fbf66fa
 
81d882d5
     CLEAR(array);
6fbf66fa
 
81d882d5
     msg(M_INFO | M_NOPREFIX, "************ 1");
     for (i = 0; i < (int) SIZE(array); ++i)
6fbf66fa
     {
81d882d5
         char *cn;
         ifconfig_pool_handle h;
         in_addr_t local, remote;
         char buf[256];
         openvpn_snprintf(buf, sizeof(buf), "common-name-%d", i);
6fbf66fa
 #ifdef DUP_CN
81d882d5
         cn = NULL;
6fbf66fa
 #else
81d882d5
         cn = buf;
6fbf66fa
 #endif
81d882d5
         h = ifconfig_pool_acquire(p, &local, &remote, NULL, cn);
         if (h < 0)
         {
             break;
         }
         msg(M_INFO | M_NOPREFIX, "IFCONFIG_POOL TEST pass 1: l=%s r=%s cn=%s",
             print_in_addr_t(local, 0, &gc),
             print_in_addr_t(remote, 0, &gc),
             cn);
         array[i] = h;
 
6fbf66fa
     }
 
81d882d5
     msg(M_INFO | M_NOPREFIX, "************* 2");
     for (i = (int) SIZE(array) / 16; i < (int) SIZE(array) / 8; ++i)
6fbf66fa
     {
81d882d5
         msg(M_INFO, "Attempt to release %d cn=%s", array[i], p->list[i].common_name);
         if (!ifconfig_pool_release(p, array[i]))
         {
             break;
         }
         msg(M_INFO, "Succeeded");
6fbf66fa
     }
 
81d882d5
     CLEAR(array);
6fbf66fa
 
81d882d5
     msg(M_INFO | M_NOPREFIX, "**************** 3");
     for (i = 0; i < (int) SIZE(array); ++i)
6fbf66fa
     {
81d882d5
         char *cn;
         ifconfig_pool_handle h;
         in_addr_t local, remote;
         char buf[256];
         snprintf(buf, sizeof(buf), "common-name-%d", i+24);
6fbf66fa
 #ifdef DUP_CN
81d882d5
         cn = NULL;
6fbf66fa
 #else
81d882d5
         cn = buf;
6fbf66fa
 #endif
81d882d5
         h = ifconfig_pool_acquire(p, &local, &remote, NULL, cn);
         if (h < 0)
         {
             break;
         }
         msg(M_INFO | M_NOPREFIX, "IFCONFIG_POOL TEST pass 3: l=%s r=%s cn=%s",
             print_in_addr_t(local, 0, &gc),
             print_in_addr_t(remote, 0, &gc),
             cn);
         array[i] = h;
 
6fbf66fa
     }
 
81d882d5
     ifconfig_pool_free(p);
     gc_free(&gc);
6fbf66fa
 }
 
81d882d5
 #endif /* ifdef IFCONFIG_POOL_TEST */
6fbf66fa
 
81d882d5
 #endif /* if P2MP */