449 lines
9.4 KiB
C
449 lines
9.4 KiB
C
/* $NetBSD: conffile.c,v 1.13 2020/04/22 23:53:27 joerg Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2010 The NetBSD Foundation, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
* by Mihai Chelaru <kefren@NetBSD.org>
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/mman.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include <arpa/inet.h>
|
|
#include <netinet/in.h>
|
|
|
|
#include <ctype.h>
|
|
#include <fcntl.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "conffile.h"
|
|
#include "ldp_errors.h"
|
|
|
|
#define NextCommand(x) strsep(&x, " ")
|
|
#define LINEMAXSIZE 1024
|
|
|
|
struct coifs_head coifs_head;
|
|
struct conei_head conei_head;
|
|
|
|
char *mapped, *nextline;
|
|
size_t mapsize;
|
|
|
|
extern int ldp_hello_time, ldp_keepalive_time, ldp_holddown_time, command_port,
|
|
min_label, max_label, no_default_route, loop_detection;
|
|
struct in_addr conf_ldp_id;
|
|
|
|
static int conf_dispatch(char*);
|
|
static char * conf_getlinelimit(void);
|
|
static int checkeol(char*);
|
|
static int Fhellotime(char*);
|
|
static int Fport(char*);
|
|
static int Fholddown(char*);
|
|
static int Fkeepalive(char*);
|
|
static int Fmaxlabel(char*);
|
|
static int Fminlabel(char*);
|
|
static int Fldpid(char*);
|
|
static int Fneighbour(char*);
|
|
static int Gneighbour(struct conf_neighbour *, char *);
|
|
static int Fnodefault(char*);
|
|
static int Floopdetection(char*);
|
|
static int Finterface(char*);
|
|
static int Ginterface(struct conf_interface *, char *);
|
|
static int Ipassive(struct conf_interface *, char *);
|
|
static int Itaddr(struct conf_interface *, char *);
|
|
|
|
struct conf_func {
|
|
char com[64];
|
|
int (* func)(char *);
|
|
};
|
|
|
|
struct intf_func {
|
|
char com[64];
|
|
int (* func)(struct conf_interface *, char *);
|
|
};
|
|
|
|
struct conf_func main_commands[] = {
|
|
{ "hello-time", Fhellotime },
|
|
{ "keepalive-time", Fkeepalive },
|
|
{ "holddown-time", Fholddown },
|
|
{ "command-port", Fport },
|
|
{ "min-label", Fminlabel },
|
|
{ "max-label", Fmaxlabel },
|
|
{ "ldp-id", Fldpid },
|
|
{ "neighbor", Fneighbour },
|
|
{ "neighbour", Fneighbour },
|
|
{ "no-default-route", Fnodefault },
|
|
{ "loop-detection", Floopdetection },
|
|
{ "interface", Finterface },
|
|
{ "", NULL },
|
|
};
|
|
|
|
struct intf_func intf_commands[] = {
|
|
{ "passive", Ipassive },
|
|
{ "transport-address", Itaddr },
|
|
{ "", NULL },
|
|
};
|
|
|
|
static int parseline;
|
|
|
|
/*
|
|
* Parses config file
|
|
*/
|
|
int
|
|
conf_parsefile(const char *fname)
|
|
{
|
|
char line[LINEMAXSIZE+1];
|
|
struct stat fs;
|
|
|
|
SLIST_INIT(&conei_head);
|
|
SLIST_INIT(&coifs_head);
|
|
conf_ldp_id.s_addr = 0;
|
|
|
|
int confh = open(fname, O_RDONLY, 0);
|
|
|
|
if (confh == -1 || fstat(confh, &fs) == -1 ||
|
|
(mapped = mmap(NULL, fs.st_size, PROT_READ, MAP_SHARED, confh, 0))
|
|
== MAP_FAILED) {
|
|
if (confh != -1)
|
|
close(confh);
|
|
return E_CONF_IO;
|
|
}
|
|
|
|
mapsize = fs.st_size;
|
|
nextline = mapped;
|
|
for (parseline = 1; ; parseline++) {
|
|
char *prev = nextline;
|
|
if ((nextline = conf_getlinelimit()) == NULL)
|
|
break;
|
|
while (isspace((int)*prev) != 0 && prev < nextline)
|
|
prev++;
|
|
if (nextline - prev < 2)
|
|
continue;
|
|
else if (nextline - prev > LINEMAXSIZE)
|
|
goto parerr;
|
|
memcpy(line, prev, nextline - prev);
|
|
if (line[0] == '#')
|
|
continue;
|
|
else
|
|
line[nextline - prev] = '\0';
|
|
if (conf_dispatch(line) != 0)
|
|
goto parerr;
|
|
}
|
|
munmap(mapped, mapsize);
|
|
close(confh);
|
|
return 0;
|
|
parerr:
|
|
munmap(mapped, mapsize);
|
|
close(confh);
|
|
return parseline;
|
|
}
|
|
|
|
char *
|
|
conf_getlinelimit(void)
|
|
{
|
|
char *p = nextline;
|
|
|
|
if (nextline < mapped || (size_t)(nextline - mapped) >= mapsize)
|
|
return NULL;
|
|
|
|
for (p = nextline; *p != '\n' && (size_t)(p - mapped) < mapsize; p++);
|
|
return p + 1;
|
|
}
|
|
|
|
/*
|
|
* Looks for a matching command on a line
|
|
*/
|
|
int
|
|
conf_dispatch(char *line)
|
|
{
|
|
int i, last_match = -1, matched = 0;
|
|
char *command, *nline = line;
|
|
|
|
if (strlen(line) == 0 || line[0] == '#')
|
|
return E_CONF_OK;
|
|
command = NextCommand(nline);
|
|
for (i = 0; main_commands[i].func != NULL; i++)
|
|
if (strncasecmp(main_commands[i].com, command,
|
|
strlen(main_commands[i].com)) == 0) {
|
|
matched++;
|
|
last_match = i;
|
|
}
|
|
if (matched == 0)
|
|
return E_CONF_NOMATCH;
|
|
else if (matched > 1)
|
|
return E_CONF_AMBIGUOUS;
|
|
|
|
if (nline == NULL || checkeol(nline) != 0)
|
|
return E_CONF_PARAM;
|
|
return main_commands[last_match].func(nline);
|
|
}
|
|
|
|
/*
|
|
* Checks if a line is terminated or else if it contains
|
|
* a start block bracket. If it's semicolon terminated
|
|
* then trim it.
|
|
*/
|
|
int
|
|
checkeol(char *line)
|
|
{
|
|
size_t len = strlen(line);
|
|
if (len > 0 && line[len - 1] == '\n') {
|
|
line[len - 1] = '\0';
|
|
len--;
|
|
}
|
|
if (len > 0 && line[len - 1] == ';') {
|
|
line[len - 1] = '\0';
|
|
return 0;
|
|
}
|
|
for (size_t i = 0; i < len; i++)
|
|
if (line[i] == '{')
|
|
return 0;
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Sets hello time
|
|
*/
|
|
int
|
|
Fhellotime(char *line)
|
|
{
|
|
int ht = atoi(line);
|
|
if (ht <= 0)
|
|
return E_CONF_PARAM;
|
|
ldp_hello_time = ht;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Sets command port
|
|
*/
|
|
int
|
|
Fport(char *line)
|
|
{
|
|
int cp = atoi(line);
|
|
if (cp <= 0 || cp > 65535)
|
|
return E_CONF_PARAM;
|
|
command_port = cp;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Sets neighbour keepalive
|
|
*/
|
|
int
|
|
Fkeepalive(char *line)
|
|
{
|
|
int kt = atoi(line);
|
|
if (kt <= 0)
|
|
return E_CONF_PARAM;
|
|
ldp_keepalive_time = kt;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Sets neighbour holddown timer
|
|
*/
|
|
int
|
|
Fholddown(char *line)
|
|
{
|
|
int hdt = atoi(line);
|
|
if (hdt <= 0)
|
|
return E_CONF_PARAM;
|
|
ldp_holddown_time = hdt;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
Fminlabel(char *line)
|
|
{
|
|
int ml = atoi(line);
|
|
if (ml <= 0)
|
|
return E_CONF_PARAM;
|
|
min_label = ml;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
Fmaxlabel(char *line)
|
|
{
|
|
int ml = atoi(line);
|
|
if (ml <= 0)
|
|
return E_CONF_PARAM;
|
|
max_label = ml;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
Fldpid(char *line)
|
|
{
|
|
if (inet_pton(AF_INET, line, &conf_ldp_id) != 1)
|
|
return E_CONF_PARAM;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
Fneighbour(char *line)
|
|
{
|
|
char *peer;
|
|
struct conf_neighbour *nei;
|
|
struct in_addr ad;
|
|
char buf[LINEMAXSIZE];
|
|
|
|
peer = NextCommand(line);
|
|
if (inet_pton(AF_INET, peer, &ad) != 1)
|
|
return E_CONF_PARAM;
|
|
|
|
nei = calloc(1, sizeof(*nei));
|
|
if (nei == NULL)
|
|
return E_CONF_MEM;
|
|
nei->address.s_addr = ad.s_addr;
|
|
SLIST_INSERT_HEAD(&conei_head, nei, neilist);
|
|
|
|
for ( ; ; ) {
|
|
char *prev = nextline;
|
|
parseline++;
|
|
nextline = conf_getlinelimit();
|
|
if (nextline == NULL || (size_t)(nextline - prev) > LINEMAXSIZE)
|
|
return -1;
|
|
while (isspace((int)*prev) != 0 && prev < nextline)
|
|
prev++;
|
|
memcpy(buf, prev, nextline - prev);
|
|
if (nextline - prev < 2 || buf[0] == '#')
|
|
continue;
|
|
else if (buf[0] == '}')
|
|
break;
|
|
else
|
|
buf[nextline - prev] = '\0';
|
|
if (Gneighbour(nei, buf) == -1)
|
|
return -1;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* neighbour { } sub-commands
|
|
*/
|
|
int
|
|
Gneighbour(struct conf_neighbour *nei, char *line)
|
|
{
|
|
if (strncasecmp("authenticate", line, 12) == 0) {
|
|
nei->authenticate = 1;
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
Fnodefault(char *line)
|
|
{
|
|
int nd = atoi(line);
|
|
if (nd < 0)
|
|
return E_CONF_PARAM;
|
|
no_default_route = nd;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
Floopdetection(char *line)
|
|
{
|
|
int loopd = atoi(line);
|
|
if (loopd < 0)
|
|
return E_CONF_PARAM;
|
|
loop_detection = loopd;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Interface sub-commands
|
|
*/
|
|
int
|
|
Finterface(char *line)
|
|
{
|
|
char *ifname;
|
|
struct conf_interface *conf_if;
|
|
char buf[LINEMAXSIZE];
|
|
|
|
if ((ifname = NextCommand(line)) == NULL)
|
|
return -1;
|
|
if ((conf_if = calloc(1, sizeof(*conf_if))) == NULL)
|
|
return -1;
|
|
|
|
strlcpy(conf_if->if_name, ifname, IF_NAMESIZE);
|
|
SLIST_INSERT_HEAD(&coifs_head, conf_if, iflist);
|
|
|
|
for ( ; ; ) {
|
|
char *prev = nextline;
|
|
parseline++;
|
|
nextline = conf_getlinelimit();
|
|
if (nextline == NULL || (size_t)(nextline - prev) > LINEMAXSIZE)
|
|
return -1;
|
|
while (isspace((int)*prev) != 0 && prev < nextline)
|
|
prev++;
|
|
memcpy(buf, prev, nextline - prev);
|
|
if (nextline - prev < 2 || buf[0] == '#')
|
|
continue;
|
|
else if (buf[0] == '}')
|
|
break;
|
|
else
|
|
buf[nextline - prev] = '\0';
|
|
if (Ginterface(conf_if, buf) == -1)
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
Ginterface(struct conf_interface *conf_if, char *buf)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; intf_commands[i].func != NULL; i++)
|
|
if (strncasecmp(buf, intf_commands[i].com,
|
|
strlen(intf_commands[i].com)) == 0)
|
|
return intf_commands[i].func(conf_if, buf +
|
|
strlen(intf_commands[i].com) + 1);
|
|
/* command not found */
|
|
return -1;
|
|
}
|
|
|
|
/* sets transport address */
|
|
int
|
|
Itaddr(struct conf_interface *conf_if, char *buf)
|
|
{
|
|
if (inet_pton(AF_INET, buf, &conf_if->tr_addr) != 1)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
/* sets passive-interface on */
|
|
int
|
|
Ipassive(struct conf_interface *conf_if, char *buf)
|
|
{
|
|
conf_if->passive = 1;
|
|
return 0;
|
|
}
|