mirror of
https://github.com/OpenVPN/openvpn.git
synced 2024-11-23 17:53:49 +08:00
564a21094e
git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@5599 e7ae566f-a301-0410-adde-c780ea21d3b5
527 lines
12 KiB
C
527 lines
12 KiB
C
/*
|
|
* 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.
|
|
*
|
|
* Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
|
|
*
|
|
* 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
|
|
*/
|
|
|
|
#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
|
|
ifconfig_pool_entry_free (struct ifconfig_pool_entry *ipe, bool hard)
|
|
{
|
|
ipe->in_use = false;
|
|
if (hard && ipe->common_name)
|
|
{
|
|
free (ipe->common_name);
|
|
ipe->common_name = NULL;
|
|
}
|
|
if (hard)
|
|
ipe->last_release = 0;
|
|
else
|
|
ipe->last_release = now;
|
|
}
|
|
|
|
static int
|
|
ifconfig_pool_find (struct ifconfig_pool *pool, const char *common_name)
|
|
{
|
|
int i;
|
|
time_t earliest_release = 0;
|
|
int previous_usage = -1;
|
|
int new_usage = -1;
|
|
|
|
for (i = 0; i < pool->size; ++i)
|
|
{
|
|
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;
|
|
|
|
}
|
|
}
|
|
|
|
if (previous_usage >= 0)
|
|
return previous_usage;
|
|
|
|
if (new_usage >= 0)
|
|
return new_usage;
|
|
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Verify start/end range
|
|
*/
|
|
bool
|
|
ifconfig_pool_verify_range (const int msglevel, const in_addr_t start, const in_addr_t end)
|
|
{
|
|
struct gc_arena gc = gc_new ();
|
|
bool ret = true;
|
|
|
|
if (start > end)
|
|
{
|
|
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;
|
|
}
|
|
if (end - start >= IFCONFIG_POOL_MAX)
|
|
{
|
|
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;
|
|
}
|
|
gc_free (&gc);
|
|
return ret;
|
|
}
|
|
|
|
struct ifconfig_pool *
|
|
ifconfig_pool_init (int type, in_addr_t start, in_addr_t end, const bool duplicate_cn)
|
|
{
|
|
struct gc_arena gc = gc_new ();
|
|
struct ifconfig_pool *pool = NULL;
|
|
|
|
ASSERT (start <= end && end - start < IFCONFIG_POOL_MAX);
|
|
ALLOC_OBJ_CLEAR (pool, struct ifconfig_pool);
|
|
|
|
pool->type = type;
|
|
pool->duplicate_cn = duplicate_cn;
|
|
|
|
switch (type)
|
|
{
|
|
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);
|
|
}
|
|
|
|
ALLOC_ARRAY_CLEAR (pool->list, struct ifconfig_pool_entry, pool->size);
|
|
|
|
msg (D_IFCONFIG_POOL, "IFCONFIG POOL: base=%s size=%d",
|
|
print_in_addr_t (pool->base, 0, &gc),
|
|
pool->size);
|
|
|
|
gc_free (&gc);
|
|
return pool;
|
|
}
|
|
|
|
void
|
|
ifconfig_pool_free (struct ifconfig_pool *pool)
|
|
{
|
|
if (pool)
|
|
{
|
|
int i;
|
|
for (i = 0; i < pool->size; ++i)
|
|
ifconfig_pool_entry_free (&pool->list[i], true);
|
|
free (pool->list);
|
|
free (pool);
|
|
}
|
|
}
|
|
|
|
ifconfig_pool_handle
|
|
ifconfig_pool_acquire (struct ifconfig_pool *pool, in_addr_t *local, in_addr_t *remote, const char *common_name)
|
|
{
|
|
int i;
|
|
|
|
i = ifconfig_pool_find (pool, common_name);
|
|
if (i >= 0)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
|
|
bool
|
|
ifconfig_pool_release (struct ifconfig_pool* pool, ifconfig_pool_handle hand, const bool hard)
|
|
{
|
|
bool ret = false;
|
|
if (pool && hand >= 0 && hand < pool->size)
|
|
{
|
|
ifconfig_pool_entry_free (&pool->list[hand], hard);
|
|
ret = true;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* private access functions
|
|
*/
|
|
|
|
static ifconfig_pool_handle
|
|
ifconfig_pool_ip_base_to_handle (const struct ifconfig_pool* pool, const in_addr_t addr)
|
|
{
|
|
ifconfig_pool_handle ret = -1;
|
|
|
|
switch (pool->type)
|
|
{
|
|
case IFCONFIG_POOL_30NET:
|
|
{
|
|
ret = (addr - pool->base) >> 2;
|
|
break;
|
|
}
|
|
case IFCONFIG_POOL_INDIV:
|
|
{
|
|
ret = (addr - pool->base);
|
|
break;
|
|
}
|
|
default:
|
|
ASSERT (0);
|
|
}
|
|
|
|
if (ret < 0 || ret >= pool->size)
|
|
ret = -1;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static in_addr_t
|
|
ifconfig_pool_handle_to_ip_base (const struct ifconfig_pool* pool, ifconfig_pool_handle hand)
|
|
{
|
|
in_addr_t ret = 0;
|
|
|
|
if (hand >= 0 && hand < pool->size)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
ifconfig_pool_set (struct ifconfig_pool* pool, const char *cn, const in_addr_t addr, const bool fixed)
|
|
{
|
|
ifconfig_pool_handle h = ifconfig_pool_ip_base_to_handle (pool, addr);
|
|
if (h >= 0)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
static void
|
|
ifconfig_pool_list (const struct ifconfig_pool* pool, struct status_output *out)
|
|
{
|
|
if (pool && out)
|
|
{
|
|
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);
|
|
status_printf (out, "%s,%s",
|
|
e->common_name,
|
|
print_in_addr_t (ip, 0, &gc));
|
|
}
|
|
}
|
|
gc_free (&gc);
|
|
}
|
|
}
|
|
|
|
static void
|
|
ifconfig_pool_msg (const struct ifconfig_pool* pool, int msglevel)
|
|
{
|
|
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);
|
|
}
|
|
|
|
/*
|
|
* Deal with reading/writing the ifconfig pool database to a file
|
|
*/
|
|
|
|
struct ifconfig_pool_persist *
|
|
ifconfig_pool_persist_init (const char *filename, int refresh_freq)
|
|
{
|
|
struct ifconfig_pool_persist *ret;
|
|
|
|
ASSERT (filename);
|
|
|
|
ALLOC_OBJ_CLEAR (ret, struct ifconfig_pool_persist);
|
|
if (refresh_freq > 0)
|
|
{
|
|
ret->fixed = false;
|
|
ret->file = status_open (filename, refresh_freq, -1, NULL, STATUS_OUTPUT_READ|STATUS_OUTPUT_WRITE);
|
|
}
|
|
else
|
|
{
|
|
ret->fixed = true;
|
|
ret->file = status_open (filename, 0, -1, NULL, STATUS_OUTPUT_READ);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
ifconfig_pool_persist_close (struct ifconfig_pool_persist *persist)
|
|
{
|
|
if (persist)
|
|
{
|
|
if (persist->file)
|
|
status_close (persist->file);
|
|
free (persist);
|
|
}
|
|
}
|
|
|
|
bool
|
|
ifconfig_pool_write_trigger (struct ifconfig_pool_persist *persist)
|
|
{
|
|
if (persist->file)
|
|
return status_trigger (persist->file);
|
|
else
|
|
return false;
|
|
}
|
|
|
|
void
|
|
ifconfig_pool_read (struct ifconfig_pool_persist *persist, struct ifconfig_pool *pool)
|
|
{
|
|
const int buf_size = 128;
|
|
|
|
update_time ();
|
|
if (persist && persist->file && pool)
|
|
{
|
|
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;
|
|
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)
|
|
{
|
|
ifconfig_pool_set (pool, cn_buf, addr, persist->fixed);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ifconfig_pool_msg (pool, D_IFCONFIG_POOL);
|
|
|
|
gc_free (&gc);
|
|
}
|
|
}
|
|
|
|
void
|
|
ifconfig_pool_write (struct ifconfig_pool_persist *persist, const struct ifconfig_pool *pool)
|
|
{
|
|
if (persist && persist->file && (status_rw_flags (persist->file) & STATUS_OUTPUT_WRITE) && pool)
|
|
{
|
|
status_reset (persist->file);
|
|
ifconfig_pool_list (pool, persist->file);
|
|
status_flush (persist->file);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* TESTING ONLY
|
|
*/
|
|
|
|
#ifdef IFCONFIG_POOL_TEST
|
|
|
|
#define DUP_CN
|
|
|
|
void
|
|
ifconfig_pool_test (in_addr_t start, in_addr_t end)
|
|
{
|
|
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;
|
|
|
|
CLEAR (array);
|
|
|
|
msg (M_INFO | M_NOPREFIX, "************ 1");
|
|
for (i = 0; i < (int) SIZE (array); ++i)
|
|
{
|
|
char *cn;
|
|
ifconfig_pool_handle h;
|
|
in_addr_t local, remote;
|
|
char buf[256];
|
|
openvpn_snprintf (buf, sizeof(buf), "common-name-%d", i);
|
|
#ifdef DUP_CN
|
|
cn = NULL;
|
|
#else
|
|
cn = buf;
|
|
#endif
|
|
h = ifconfig_pool_acquire (p, &local, &remote, 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;
|
|
|
|
}
|
|
|
|
msg (M_INFO | M_NOPREFIX, "************* 2");
|
|
for (i = (int) SIZE (array) / 16; i < (int) SIZE (array) / 8; ++i)
|
|
{
|
|
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");
|
|
}
|
|
|
|
CLEAR (array);
|
|
|
|
msg (M_INFO | M_NOPREFIX, "**************** 3");
|
|
for (i = 0; i < (int) SIZE (array); ++i)
|
|
{
|
|
char *cn;
|
|
ifconfig_pool_handle h;
|
|
in_addr_t local, remote;
|
|
char buf[256];
|
|
snprintf (buf, sizeof(buf), "common-name-%d", i+24);
|
|
#ifdef DUP_CN
|
|
cn = NULL;
|
|
#else
|
|
cn = buf;
|
|
#endif
|
|
h = ifconfig_pool_acquire (p, &local, &remote, 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;
|
|
|
|
}
|
|
|
|
ifconfig_pool_free (p);
|
|
gc_free (&gc);
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|