493 lines
13 KiB
C
493 lines
13 KiB
C
|
/*
|
||
|
* conffile.c -- config file parser
|
||
|
*
|
||
|
* part of TAYGA <http://www.litech.org/tayga/>
|
||
|
* Copyright (C) 2010 Nathan Lutchansky <lutchann@litech.org>
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation; either version 2 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* 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.
|
||
|
*/
|
||
|
|
||
|
#include <tayga.h>
|
||
|
|
||
|
struct config *gcfg;
|
||
|
|
||
|
static int parse_prefix(int af, const char *src, void *prefix, int *prefix_len)
|
||
|
{
|
||
|
char *p, *end;
|
||
|
long int a;
|
||
|
int r;
|
||
|
|
||
|
p = strchr(src, '/');
|
||
|
if (!p)
|
||
|
return -1;
|
||
|
*p = 0;
|
||
|
a = strtol(p + 1, &end, 10);
|
||
|
r = *end || !inet_pton(af, src, prefix);
|
||
|
*p = '/';
|
||
|
if (r)
|
||
|
return -1;
|
||
|
if (a < 0 || a > (af == AF_INET6 ? 128 : 32))
|
||
|
return -1;
|
||
|
|
||
|
*prefix_len = a;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static struct map_static *alloc_map_static(int ln)
|
||
|
{
|
||
|
struct map_static *m;
|
||
|
|
||
|
m = (struct map_static *)malloc(sizeof(struct map_static));
|
||
|
if (!m) {
|
||
|
slog(LOG_CRIT, "Unable to allocate config memory\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
memset(m, 0, sizeof(struct map_static));
|
||
|
m->map4.type = MAP_TYPE_STATIC;
|
||
|
m->map4.prefix_len = 32;
|
||
|
calc_ip4_mask(&m->map4.mask, NULL, 32);
|
||
|
INIT_LIST_HEAD(&m->map4.list);
|
||
|
m->map6.type = MAP_TYPE_STATIC;
|
||
|
m->map6.prefix_len = 128;
|
||
|
calc_ip6_mask(&m->map6.mask, NULL, 128);
|
||
|
INIT_LIST_HEAD(&m->map6.list);
|
||
|
m->conffile_lineno = ln;
|
||
|
return m;
|
||
|
}
|
||
|
|
||
|
static void abort_on_conflict4(char *msg, int ln, struct map4 *old)
|
||
|
{
|
||
|
char oldaddr[INET_ADDRSTRLEN];
|
||
|
char oldline[128] = "";
|
||
|
struct map_static *s;
|
||
|
|
||
|
if (old->type == MAP_TYPE_STATIC || old->type == MAP_TYPE_RFC6052) {
|
||
|
s = container_of(old, struct map_static, map4);
|
||
|
if (s->conffile_lineno)
|
||
|
sprintf(oldline, " from line %d", s->conffile_lineno);
|
||
|
}
|
||
|
|
||
|
inet_ntop(AF_INET, &old->addr, oldaddr, sizeof(oldaddr));
|
||
|
if (ln)
|
||
|
slog(LOG_CRIT, "%s on line %d conflicts with earlier "
|
||
|
"definition of %s/%d%s\n", msg, ln,
|
||
|
oldaddr, old->prefix_len, oldline);
|
||
|
else
|
||
|
slog(LOG_CRIT, "%s conflicts with earlier "
|
||
|
"definition of %s/%d%s\n", msg,
|
||
|
oldaddr, old->prefix_len, oldline);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
static void abort_on_conflict6(char *msg, int ln, struct map6 *old)
|
||
|
{
|
||
|
char oldaddr[INET6_ADDRSTRLEN];
|
||
|
char oldline[128] = "";
|
||
|
struct map_static *s;
|
||
|
|
||
|
if (old->type == MAP_TYPE_STATIC || old->type == MAP_TYPE_RFC6052) {
|
||
|
s = container_of(old, struct map_static, map6);
|
||
|
if (s->conffile_lineno)
|
||
|
sprintf(oldline, " from line %d", s->conffile_lineno);
|
||
|
}
|
||
|
|
||
|
inet_ntop(AF_INET6, &old->addr, oldaddr, sizeof(oldaddr));
|
||
|
if (ln)
|
||
|
slog(LOG_CRIT, "%s on line %d overlaps with earlier "
|
||
|
"definition of %s/%d%s\n", msg, ln,
|
||
|
oldaddr, old->prefix_len, oldline);
|
||
|
else
|
||
|
slog(LOG_CRIT, "%s overlaps with earlier "
|
||
|
"definition of %s/%d%s\n", msg,
|
||
|
oldaddr, old->prefix_len, oldline);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
static void config_ipv4_addr(int ln, int arg_count, char **args)
|
||
|
{
|
||
|
if (gcfg->local_addr4.s_addr) {
|
||
|
slog(LOG_CRIT, "Error: duplicate ipv4-addr directive on "
|
||
|
"line %d\n", ln);
|
||
|
exit(1);
|
||
|
}
|
||
|
if (!inet_pton(AF_INET, args[0], &gcfg->local_addr4)) {
|
||
|
slog(LOG_CRIT, "Expected an IPv4 address but found \"%s\" on "
|
||
|
"line %d\n", args[0], ln);
|
||
|
exit(1);
|
||
|
}
|
||
|
if (validate_ip4_addr(&gcfg->local_addr4) < 0) {
|
||
|
slog(LOG_CRIT, "Cannot use reserved address %s in ipv4-addr "
|
||
|
"directive, aborting...\n", args[0]);
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void config_ipv6_addr(int ln, int arg_count, char **args)
|
||
|
{
|
||
|
if (gcfg->local_addr6.s6_addr[0]) {
|
||
|
slog(LOG_CRIT, "Error: duplicate ipv6-addr directive on line "
|
||
|
"%d\n", ln);
|
||
|
exit(1);
|
||
|
}
|
||
|
if (!inet_pton(AF_INET6, args[0], &gcfg->local_addr6)) {
|
||
|
slog(LOG_CRIT, "Expected an IPv6 address but found \"%s\" on "
|
||
|
"line %d\n", args[0], ln);
|
||
|
exit(1);
|
||
|
}
|
||
|
if (validate_ip6_addr(&gcfg->local_addr6) < 0) {
|
||
|
slog(LOG_CRIT, "Cannot use reserved address %s in ipv6-addr "
|
||
|
"directive, aborting...\n", args[0]);
|
||
|
exit(1);
|
||
|
}
|
||
|
if (gcfg->local_addr6.s6_addr32[0] == WKPF) {
|
||
|
slog(LOG_CRIT, "Error: ipv6-addr directive cannot contain an "
|
||
|
"address in the Well-Known Prefix "
|
||
|
"(64:ff9b::/96)\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void config_prefix(int ln, int arg_count, char **args)
|
||
|
{
|
||
|
struct map_static *m;
|
||
|
struct map6 *m6;
|
||
|
|
||
|
m = alloc_map_static(ln);
|
||
|
m->map4.prefix_len = 0;
|
||
|
m->map4.mask.s_addr = 0;
|
||
|
m->map4.type = MAP_TYPE_RFC6052;
|
||
|
m->map6.type = MAP_TYPE_RFC6052;
|
||
|
m6 = &m->map6;
|
||
|
|
||
|
if (parse_prefix(AF_INET6, args[0], &m6->addr, &m6->prefix_len) ||
|
||
|
calc_ip6_mask(&m6->mask, &m6->addr, m6->prefix_len)) {
|
||
|
slog(LOG_CRIT, "Expected an IPv6 prefix but found \"%s\" on "
|
||
|
"line %d\n", args[0], ln);
|
||
|
exit(1);
|
||
|
}
|
||
|
if (validate_ip6_addr(&m6->addr) < 0) {
|
||
|
slog(LOG_CRIT, "Cannot use reserved address %s in prefix "
|
||
|
"directive, aborting...\n", args[0]);
|
||
|
exit(1);
|
||
|
}
|
||
|
if (m6->prefix_len != 32 && m6->prefix_len != 40 &&
|
||
|
m6->prefix_len != 48 && m6->prefix_len != 56 &&
|
||
|
m6->prefix_len != 64 && m6->prefix_len != 96) {
|
||
|
slog(LOG_CRIT, "NAT prefix length must be 32, 40, 48, 56, 64 "
|
||
|
"or 96 only, aborting...\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
if (insert_map4(&m->map4, NULL) < 0) {
|
||
|
slog(LOG_CRIT, "Error: duplicate prefix directive on line %d\n",
|
||
|
ln);
|
||
|
exit(1);
|
||
|
}
|
||
|
if (insert_map6(&m->map6, &m6) < 0)
|
||
|
abort_on_conflict6("Error: NAT64 prefix", ln, m6);
|
||
|
}
|
||
|
|
||
|
static void config_tun_device(int ln, int arg_count, char **args)
|
||
|
{
|
||
|
if (gcfg->tundev[0]) {
|
||
|
slog(LOG_CRIT, "Error: duplicate tun-device directive on line "
|
||
|
"%d\n", ln);
|
||
|
exit(1);
|
||
|
}
|
||
|
if (strlen(args[0]) + 1 > sizeof(gcfg->tundev)) {
|
||
|
slog(LOG_CRIT, "Device name \"%s\" is invalid on line %d\n",
|
||
|
args[0], ln);
|
||
|
exit(1);
|
||
|
}
|
||
|
strcpy(gcfg->tundev, args[0]);
|
||
|
}
|
||
|
|
||
|
static void config_map(int ln, int arg_count, char **args)
|
||
|
{
|
||
|
struct map_static *m;
|
||
|
struct map4 *m4;
|
||
|
struct map6 *m6;
|
||
|
|
||
|
m = alloc_map_static(ln);
|
||
|
|
||
|
if (!inet_pton(AF_INET, args[0], &m->map4.addr)) {
|
||
|
slog(LOG_CRIT, "Expected an IPv4 address but found \"%s\" on "
|
||
|
"line %d\n", args[0], ln);
|
||
|
exit(1);
|
||
|
}
|
||
|
if (!inet_pton(AF_INET6, args[1], &m->map6.addr)) {
|
||
|
slog(LOG_CRIT, "Expected an IPv6 address but found \"%s\" on "
|
||
|
"line %d\n", args[1], ln);
|
||
|
exit(1);
|
||
|
}
|
||
|
if (validate_ip4_addr(&m->map4.addr) < 0) {
|
||
|
slog(LOG_CRIT, "Cannot use reserved address %s in map "
|
||
|
"directive, aborting...\n", args[0]);
|
||
|
exit(1);
|
||
|
}
|
||
|
if (validate_ip6_addr(&m->map6.addr) < 0) {
|
||
|
slog(LOG_CRIT, "Cannot use reserved address %s in map "
|
||
|
"directive, aborting...\n", args[1]);
|
||
|
exit(1);
|
||
|
}
|
||
|
if (m->map6.addr.s6_addr32[0] == WKPF) {
|
||
|
slog(LOG_CRIT, "Cannot create single-host maps inside "
|
||
|
"64:ff9b::/96, aborting...\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
if (insert_map4(&m->map4, &m4) < 0)
|
||
|
abort_on_conflict4("Error: IPv4 address in map directive",
|
||
|
ln, m4);
|
||
|
if (insert_map6(&m->map6, &m6) < 0)
|
||
|
abort_on_conflict6("Error: IPv6 address in map directive",
|
||
|
ln, m6);
|
||
|
}
|
||
|
|
||
|
static void config_dynamic_pool(int ln, int arg_count, char **args)
|
||
|
{
|
||
|
struct dynamic_pool *pool;
|
||
|
struct map4 *m4;
|
||
|
|
||
|
if (gcfg->dynamic_pool) {
|
||
|
slog(LOG_CRIT, "Error: duplicate dynamic-pool directive on "
|
||
|
"line %d\n", ln);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
pool = (struct dynamic_pool *)malloc(sizeof(struct dynamic_pool));
|
||
|
if (!pool) {
|
||
|
slog(LOG_CRIT, "Unable to allocate config memory\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
memset(pool, 0, sizeof(struct dynamic_pool));
|
||
|
INIT_LIST_HEAD(&pool->mapped_list);
|
||
|
INIT_LIST_HEAD(&pool->dormant_list);
|
||
|
INIT_LIST_HEAD(&pool->free_list);
|
||
|
|
||
|
m4 = &pool->map4;
|
||
|
m4->type = MAP_TYPE_DYNAMIC_POOL;
|
||
|
INIT_LIST_HEAD(&m4->list);
|
||
|
|
||
|
if (parse_prefix(AF_INET, args[0], &m4->addr, &m4->prefix_len) ||
|
||
|
calc_ip4_mask(&m4->mask, &m4->addr, m4->prefix_len)) {
|
||
|
slog(LOG_CRIT, "Expected an IPv4 prefix but found \"%s\" on "
|
||
|
"line %d\n", args[0], ln);
|
||
|
exit(1);
|
||
|
}
|
||
|
if (validate_ip4_addr(&m4->addr) < 0) {
|
||
|
slog(LOG_CRIT, "Cannot use reserved address %s in dynamic-pool "
|
||
|
"directive, aborting...\n", args[0]);
|
||
|
exit(1);
|
||
|
}
|
||
|
if (m4->prefix_len > 31) {
|
||
|
slog(LOG_CRIT, "Cannot use a prefix longer than /31 in "
|
||
|
"dynamic-pool directive, aborting...\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
if (insert_map4(&pool->map4, &m4) < 0)
|
||
|
abort_on_conflict4("Error: IPv4 prefix in dynamic-pool "
|
||
|
"directive", ln, m4);
|
||
|
|
||
|
pool->free_head.addr = ntohl(m4->addr.s_addr);
|
||
|
pool->free_head.count = (1 << (32 - m4->prefix_len)) - 1;
|
||
|
INIT_LIST_HEAD(&pool->free_head.list);
|
||
|
list_add(&pool->free_head.list, &pool->free_list);
|
||
|
|
||
|
gcfg->dynamic_pool = pool;
|
||
|
}
|
||
|
|
||
|
static void config_data_dir(int ln, int arg_count, char **args)
|
||
|
{
|
||
|
if (gcfg->data_dir[0]) {
|
||
|
slog(LOG_CRIT, "Error: duplicate data-dir directive on line "
|
||
|
"%d\n", ln);
|
||
|
exit(1);
|
||
|
}
|
||
|
if (args[0][0] != '/') {
|
||
|
slog(LOG_CRIT, "Error: data-dir must be an absolute path\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
strcpy(gcfg->data_dir, args[0]);
|
||
|
}
|
||
|
|
||
|
static void config_strict_fh(int ln, int arg_count, char **args)
|
||
|
{
|
||
|
if (!strcasecmp(args[0], "true") || !strcasecmp(args[0], "on") ||
|
||
|
!strcasecmp(args[0], "1")) {
|
||
|
gcfg->lazy_frag_hdr = 0;
|
||
|
} else if (!strcasecmp(args[0], "false") ||
|
||
|
!strcasecmp(args[0], "off") ||
|
||
|
!strcasecmp(args[0], "0")) {
|
||
|
gcfg->lazy_frag_hdr = 1;
|
||
|
} else {
|
||
|
slog(LOG_CRIT, "Error: invalid value for strict-frag-hdr\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
struct {
|
||
|
char *name;
|
||
|
void (*config_func)(int ln, int arg_count, char **args);
|
||
|
int need_args;
|
||
|
} config_directives[] = {
|
||
|
{ "ipv4-addr", config_ipv4_addr, 1 },
|
||
|
{ "ipv6-addr", config_ipv6_addr, 1 },
|
||
|
{ "prefix", config_prefix, 1 },
|
||
|
{ "tun-device", config_tun_device, 1 },
|
||
|
{ "map", config_map, 2 },
|
||
|
{ "dynamic-pool", config_dynamic_pool, 1 },
|
||
|
{ "data-dir", config_data_dir, 1 },
|
||
|
{ "strict-frag-hdr", config_strict_fh, 1 },
|
||
|
{ NULL, NULL, 0 }
|
||
|
};
|
||
|
|
||
|
void read_config(char *conffile)
|
||
|
{
|
||
|
FILE *in;
|
||
|
int ln = 0;
|
||
|
char line[512];
|
||
|
char addrbuf[128];
|
||
|
char *c, *tokptr;
|
||
|
#define MAX_ARGS 10
|
||
|
char *args[MAX_ARGS];
|
||
|
int arg_count;
|
||
|
int i;
|
||
|
struct map_static *m;
|
||
|
struct map4 *m4;
|
||
|
struct map6 *m6;
|
||
|
|
||
|
gcfg = (struct config *)malloc(sizeof(struct config));
|
||
|
if (!gcfg)
|
||
|
goto malloc_fail;
|
||
|
memset(gcfg, 0, sizeof(struct config));
|
||
|
gcfg->recv_buf_size = 65536 + sizeof(struct tun_pi);
|
||
|
INIT_LIST_HEAD(&gcfg->map4_list);
|
||
|
INIT_LIST_HEAD(&gcfg->map6_list);
|
||
|
gcfg->dyn_min_lease = 7200 + 4 * 60; /* just over two hours */
|
||
|
gcfg->dyn_max_lease = 14 * 86400;
|
||
|
gcfg->max_commit_delay = gcfg->dyn_max_lease / 4;
|
||
|
gcfg->hash_bits = 7;
|
||
|
gcfg->cache_size = 8192;
|
||
|
gcfg->allow_ident_gen = 1;
|
||
|
gcfg->ipv6_offlink_mtu = 1280;
|
||
|
gcfg->lazy_frag_hdr = 1;
|
||
|
INIT_LIST_HEAD(&gcfg->cache_pool);
|
||
|
INIT_LIST_HEAD(&gcfg->cache_active);
|
||
|
|
||
|
in = fopen(conffile, "r");
|
||
|
if (!in) {
|
||
|
slog(LOG_CRIT, "unable to open %s, aborting: %s\n", conffile,
|
||
|
strerror(errno));
|
||
|
exit(1);
|
||
|
}
|
||
|
while (fgets(line, sizeof(line), in)) {
|
||
|
++ln;
|
||
|
if (strlen(line) + 1 == sizeof(line)) {
|
||
|
slog(LOG_CRIT, "Line %d of %s is too long, "
|
||
|
"aborting...\n", ln, conffile);
|
||
|
exit(1);
|
||
|
}
|
||
|
arg_count = 0;
|
||
|
for (;;) {
|
||
|
c = strtok_r(arg_count ? NULL : line, DELIM, &tokptr);
|
||
|
if (!c || *c == '#')
|
||
|
break;
|
||
|
if (arg_count == MAX_ARGS) {
|
||
|
slog(LOG_CRIT, "Line %d of %s is too long, "
|
||
|
"aborting...\n", ln, conffile);
|
||
|
exit(1);
|
||
|
}
|
||
|
args[arg_count++] = c;
|
||
|
}
|
||
|
if (arg_count == 0)
|
||
|
continue;
|
||
|
for (i = 0; config_directives[i].name; ++i)
|
||
|
if (!strcasecmp(args[0], config_directives[i].name))
|
||
|
break;
|
||
|
if (!config_directives[i].name) {
|
||
|
slog(LOG_CRIT, "Unknown directive \"%s\" on line %d of "
|
||
|
"%s, aborting...\n", args[0],
|
||
|
ln, conffile);
|
||
|
exit(1);
|
||
|
}
|
||
|
--arg_count;
|
||
|
if (config_directives[i].need_args >= 0 &&
|
||
|
arg_count != config_directives[i].need_args) {
|
||
|
slog(LOG_CRIT, "Incorrect number of arguments on "
|
||
|
"line %d\n", ln);
|
||
|
exit(1);
|
||
|
}
|
||
|
config_directives[i].config_func(ln, arg_count, &args[1]);
|
||
|
}
|
||
|
fclose(in);
|
||
|
|
||
|
if (list_empty(&gcfg->map6_list)) {
|
||
|
slog(LOG_CRIT, "Error: no translation maps or NAT64 prefix "
|
||
|
"configured\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
m4 = list_entry(gcfg->map4_list.next, struct map4, list);
|
||
|
m6 = list_entry(gcfg->map6_list.next, struct map6, list);
|
||
|
|
||
|
if (m4->type == MAP_TYPE_RFC6052 && m6->type == MAP_TYPE_RFC6052 &&
|
||
|
!gcfg->allow_ident_gen)
|
||
|
gcfg->cache_size = 0;
|
||
|
|
||
|
if (!gcfg->local_addr4.s_addr) {
|
||
|
slog(LOG_CRIT, "Error: no ipv4-addr directive found\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
m = alloc_map_static(0);
|
||
|
m->map4.addr = gcfg->local_addr4;
|
||
|
if (insert_map4(&m->map4, &m4) < 0)
|
||
|
abort_on_conflict4("Error: ipv4-addr", 0, m4);
|
||
|
|
||
|
if (gcfg->local_addr6.s6_addr32[0]) {
|
||
|
m->map6.addr = gcfg->local_addr6;
|
||
|
if (insert_map6(&m->map6, &m6) < 0) {
|
||
|
if (m6->type == MAP_TYPE_RFC6052) {
|
||
|
inet_ntop(AF_INET6, &m6->addr,
|
||
|
addrbuf, sizeof(addrbuf));
|
||
|
slog(LOG_CRIT, "Error: ipv6-addr cannot reside "
|
||
|
"within configured prefix "
|
||
|
"%s/%d\n", addrbuf,
|
||
|
m6->prefix_len);
|
||
|
exit(1);
|
||
|
} else {
|
||
|
abort_on_conflict6("Error: ipv6-addr", 0, m6);
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
m6 = list_entry(gcfg->map6_list.prev, struct map6, list);
|
||
|
if (m6->type != MAP_TYPE_RFC6052) {
|
||
|
slog(LOG_CRIT, "Error: ipv6-addr directive must be "
|
||
|
"specified if no NAT64 prefix is "
|
||
|
"configured\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
if (append_to_prefix(&gcfg->local_addr6, &gcfg->local_addr4,
|
||
|
&m6->addr, m6->prefix_len)) {
|
||
|
slog(LOG_CRIT, "Error: ipv6-addr directive must be "
|
||
|
"specified if prefix is 64:ff9b::/96 "
|
||
|
"and ipv4-addr is a non-global "
|
||
|
"(RFC 1918) address\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
m->map6.addr = gcfg->local_addr6;
|
||
|
}
|
||
|
return;
|
||
|
|
||
|
malloc_fail:
|
||
|
slog(LOG_CRIT, "Unable to allocate config memory\n");
|
||
|
exit(1);
|
||
|
}
|