vcpe/l2tp/xl2tpd/file.c

1626 lines
43 KiB
C
Raw Normal View History

2022-08-09 03:06:08 +00:00
/*
* Layer Two Tunnelling Protocol Daemon
* Copyright (C) 1998 Adtran, Inc.
* Copyright (C) 2002 Jeff McAdams
*
* Mark Spencer
*
* This software is distributed under the terms
* of the GPL, which you should have received
* along with this source.
*
* File format handling
*
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <netdb.h>
#include <netinet/in.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "l2tp.h"
struct lns *lnslist;
struct lac *laclist;
struct lns *deflns;
struct lac *deflac;
struct global gconfig;
char filerr[STRLEN];
int parse_config (FILE *);
struct keyword words[];
int init_config ()
{
FILE *f;
int returnedValue;
gconfig.port = UDP_LISTEN_PORT;
gconfig.sarefnum = IP_IPSEC_REFINFO; /* default use the latest we know */
gconfig.ipsecsaref = 0; /* default off - requires patched KLIPS kernel module */
gconfig.forceuserspace = 0; /* default off - allow kernel decap of data packets */
gconfig.listenaddr = htonl(INADDR_ANY); /* Default is to bind (listen) to all interfaces */
gconfig.debug_avp = 0;
gconfig.debug_network = 0;
gconfig.packet_dump = 0;
gconfig.debug_tunnel = 0;
gconfig.debug_state = 0;
gconfig.max_retries = DEFAULT_MAX_RETRIES;
gconfig.cap_backoff = 0;
lnslist = NULL;
laclist = NULL;
deflac = (struct lac *) calloc (1, sizeof (struct lac));
f = fopen (gconfig.configfile, "r");
if (!f)
{
f = fopen (gconfig.altconfigfile, "r");
if (f)
{
l2tp_log (LOG_WARNING, "%s: Using old style config files %s and %s\n",
__FUNCTION__, gconfig.altconfigfile, gconfig.altauthfile);
strncpy (gconfig.authfile, gconfig.altauthfile,
sizeof (gconfig.authfile));
}
else
{
l2tp_log (LOG_CRIT, "%s: Unable to open config file %s or %s\n",
__FUNCTION__, gconfig.configfile, gconfig.altconfigfile);
return -1;
}
}
returnedValue = parse_config (f);
fclose (f);
return (returnedValue);
}
struct lns *new_lns ()
{
struct lns *tmp;
tmp = (struct lns *) calloc (1, sizeof (struct lns));
if (!tmp)
{
l2tp_log (LOG_CRIT, "%s: Unable to allocate memory for new LNS\n",
__FUNCTION__);
return NULL;
}
tmp->next = NULL;
tmp->exclusive = 0;
tmp->localaddr = 0;
tmp->tun_rws = DEFAULT_RWS_SIZE;
tmp->call_rws = DEFAULT_RWS_SIZE;
tmp->rxspeed = DEFAULT_RX_BPS;
tmp->txspeed = DEFAULT_TX_BPS;
tmp->hbit = 0;
tmp->lbit = 0;
tmp->authpeer = 0;
tmp->authself = -1;
tmp->authname[0] = 0;
tmp->peername[0] = 0;
tmp->hostname[0] = 0;
tmp->entname[0] = 0;
tmp->range = NULL;
tmp->localrange = NULL;
tmp->assign_ip = 1; /* default to 'yes' */
tmp->lacs = NULL;
tmp->passwdauth = 0;
tmp->pap_require = 0;
tmp->pap_refuse = 0;
tmp->chap_require = 0;
tmp->chap_refuse = 0;
tmp->idle = 0;
tmp->pridns = 0;
tmp->secdns = 0;
tmp->priwins = 0;
tmp->secwins = 0;
tmp->proxyarp = 0;
tmp->proxyauth = 0;
tmp->challenge = 0;
tmp->debug = 0;
tmp->pass_peer = 0;
tmp->pppoptfile[0] = 0;
tmp->t = NULL;
return tmp;
}
struct lac *new_lac ()
{
struct lac *tmp;
tmp = (struct lac *) calloc (1, sizeof (struct lac));
if (!tmp)
{
l2tp_log (LOG_CRIT, "%s: Unable to allocate memory for lac entry!\n",
__FUNCTION__);
return NULL;
}
tmp->next = NULL;
tmp->rsched = NULL;
tmp->localaddr = 0;
tmp->remoteaddr = 0;
tmp->lns = 0;
tmp->tun_rws = DEFAULT_RWS_SIZE;
tmp->call_rws = DEFAULT_RWS_SIZE;
tmp->hbit = 0;
tmp->lbit = 0;
tmp->authpeer = 0;
tmp->authself = -1;
tmp->authname[0] = 0;
tmp->peername[0] = 0;
tmp->hostname[0] = 0;
tmp->entname[0] = 0;
tmp->pap_require = 0;
tmp->pap_refuse = 0;
tmp->chap_require = 0;
tmp->chap_refuse = 0;
tmp->t = NULL;
tmp->redial = 0;
tmp->rtries = 0;
tmp->rmax = 0;
tmp->challenge = 0;
tmp->autodial = 0;
tmp->rtimeout = 30;
tmp->active = 0;
tmp->debug = 0;
tmp->pass_peer = 0;
tmp->pppoptfile[0] = 0;
tmp->defaultroute = 0;
return tmp;
}
int yesno (char *value)
{
if (!strcasecmp (value, "yes") || !strcasecmp (value, "y") ||
!strcasecmp (value, "true"))
return 1;
else if (!strcasecmp (value, "no") || !strcasecmp (value, "n") ||
!strcasecmp (value, "false"))
return 0;
else
return -1;
}
int set_boolean (char *word, char *value, int *ptr)
{
int val;
#ifdef DEBUG_FILE
l2tp_log (LOG_DEBUG, "set_%s: %s flag to '%s'\n", word, word, value);
#endif /* ; */
if ((val = yesno (value)) < 0)
{
snprintf (filerr, sizeof (filerr), "%s must be 'yes' or 'no'\n",
word);
return -1;
}
*ptr = val;
return 0;
}
int set_int (char *word, char *value, int *ptr)
{
int val;
#ifdef DEBUG_FILE
l2tp_log (LOG_DEBUG, "set_%s: %s flag to '%s'\n", word, word, value);
#endif /* ; */
if ((val = atoi (value)) < 0)
{
snprintf (filerr, sizeof (filerr), "%s must be a number\n", word);
return -1;
}
*ptr = val;
return 0;
}
int set_string (char *word, char *value, char *ptr, int len)
{
UNUSED(word);
#ifdef DEBUG_FILE
l2tp_log (LOG_DEBUG, "set_%s: %s flag to '%s'\n", word, word, value);
#endif /* ; */
strncpy (ptr, value, len);
return 0;
}
int set_port (char *word, char *value, int context, void *item)
{
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_GLOBAL:
#ifdef DEBUG_FILE
l2tp_log (LOG_DEBUG, "set_maxretries: Setting global max retries to %s\n",
value);
#endif
set_int (word, value, &(((struct global *) item)->port));
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_rtimeout (char *word, char *value, int context, void *item)
{
if (atoi (value) < 1)
{
snprintf (filerr, sizeof (filerr),
"rtimeout value must be at least 1\n");
return -1;
}
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_LAC:
#ifdef DEBUG_FILE
l2tp_log (LOG_DEBUG, "set_rtimeout: Setting redial timeout to %s\n",
value);
#endif
set_int (word, value, &(((struct lac *) item)->rtimeout));
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_rws (char *word, char *value, int context, void *item)
{
if (atoi (value) < -1)
{
snprintf (filerr, sizeof (filerr),
"receive window size must be at least -1\n");
return -1;
}
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_LAC:
if (word[0] == 'c')
set_int (word, value, &(((struct lac *) item)->call_rws));
if (word[0] == 't')
{
set_int (word, value, &(((struct lac *) item)->tun_rws));
if (((struct lac *) item)->tun_rws < 1)
{
snprintf (filerr, sizeof (filerr),
"receive window size for tunnels must be at least 1\n");
return -1;
}
}
break;
case CONTEXT_LNS:
if (word[0] == 'c')
set_int (word, value, &(((struct lns *) item)->call_rws));
if (word[0] == 't')
{
set_int (word, value, &(((struct lns *) item)->tun_rws));
if (((struct lns *) item)->tun_rws < 1)
{
snprintf (filerr, sizeof (filerr),
"receive window size for tunnels must be at least 1\n");
return -1;
}
}
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_speed (char *word, char *value, int context, void *item)
{
if (atoi (value) < 1 )
{
snprintf (filerr, sizeof (filerr),
"bps must be greater than zero\n");
return -1;
}
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_LAC:
if (word[0] == 't')
set_int (word, value, &(((struct lac *) item)->txspeed));
else if (word[0] == 'r')
set_int (word, value, &(((struct lac *) item)->rxspeed));
else
{
set_int (word, value, &(((struct lac *) item)->rxspeed));
set_int (word, value, &(((struct lac *) item)->txspeed));
}
break;
case CONTEXT_LNS:
if (word[0] == 't')
set_int (word, value, &(((struct lns *) item)->txspeed));
else if (word[0] == 'r')
set_int (word, value, &(((struct lns *) item)->rxspeed));
else
{
set_int (word, value, &(((struct lns *) item)->rxspeed));
set_int (word, value, &(((struct lns *) item)->txspeed));
}
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_maxretries (char *word, char *value, int context, void *item)
{
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_GLOBAL:
#ifdef DEBUG_FILE
l2tp_log (LOG_DEBUG, "set_port: Setting global max retries to %s\n",
value);
#endif
set_int (word, value, &(((struct global *) item)->max_retries));
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_capbackoff (char *word, char *value, int context, void *item)
{
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_GLOBAL:
#ifdef DEBUG_FILE
l2tp_log (LOG_DEBUG, "set_capbackoff: Setting global cap backoff to %s\n",
value);
#endif
set_int (word, value, &(((struct global *) item)->cap_backoff));
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_rmax (char *word, char *value, int context, void *item)
{
if (atoi (value) < 1)
{
snprintf (filerr, sizeof (filerr), "rmax value must be at least 1\n");
return -1;
}
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_LAC:
#ifdef DEBUG_FILE
l2tp_log (LOG_DEBUG, "set_rmax: Setting max redials to %s\n", value);
#endif
set_int (word, value, &(((struct lac *) item)->rmax));
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_authfile (char *word, char *value, int context, void *item)
{
if (!strlen (value))
{
snprintf (filerr, sizeof (filerr),
"no filename specified for authentication\n");
return -1;
}
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_GLOBAL:
#ifdef DEBUG_FILE
l2tp_log (LOG_DEBUG, "set_authfile: Setting global auth file to '%s'\n",
value);
#endif /* ; */
strncpy (((struct global *) item)->authfile, value,
sizeof (((struct global *)item)->authfile));
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_autodial (char *word, char *value, int context, void *item)
{
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_LAC:
if (set_boolean (word, value, &(((struct lac *) item)->autodial)))
return -1;
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_flow (char *word, char *value, int context, void *item)
{
int v = -1;
set_boolean (word, value, &v);
if (v < 0)
return -1;
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_LAC:
if (v)
{
if (((struct lac *) item)->call_rws < 0)
((struct lac *) item)->call_rws = 0;
}
else
{
((struct lac *) item)->call_rws = -1;
}
break;
case CONTEXT_LNS:
if (v)
{
if (((struct lns *) item)->call_rws < 0)
((struct lns *) item)->call_rws = 0;
}
else
{
((struct lns *) item)->call_rws = -1;
}
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_defaultroute (char *word, char *value, int context, void *item)
{
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_LAC:
if (set_boolean (word, value, &(((struct lac *) item)->defaultroute)))
return -1;
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_authname (char *word, char *value, int context, void *item)
{
struct lac *l = (struct lac *) item;
struct lns *n = (struct lns *) item;
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_LNS:
if (set_string (word, value, n->authname, sizeof (n->authname)))
return -1;
break;
case CONTEXT_LAC:
if (set_string (word, value, l->authname, sizeof (l->authname)))
return -1;
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_hostname (char *word, char *value, int context, void *item)
{
struct lac *l = (struct lac *) item;
struct lns *n = (struct lns *) item;
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_LNS:
if (set_string (word, value, n->hostname, sizeof (n->hostname)))
return -1;
break;
case CONTEXT_LAC:
if (set_string (word, value, l->hostname, sizeof (l->hostname)))
return -1;
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_passwdauth (char *word, char *value, int context, void *item)
{
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_LNS:
if (set_boolean (word, value, &(((struct lns *) item)->passwdauth)))
return -1;
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_hbit (char *word, char *value, int context, void *item)
{
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_LAC:
if (set_boolean (word, value, &(((struct lac *) item)->hbit)))
return -1;
break;
case CONTEXT_LNS:
if (set_boolean (word, value, &(((struct lns *) item)->hbit)))
return -1;
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_challenge (char *word, char *value, int context, void *item)
{
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_LAC:
if (set_boolean (word, value, &(((struct lac *) item)->challenge)))
return -1;
break;
case CONTEXT_LNS:
if (set_boolean (word, value, &(((struct lns *) item)->challenge)))
return -1;
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_lbit (char *word, char *value, int context, void *item)
{
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_LAC:
if (set_boolean (word, value, &(((struct lac *) item)->lbit)))
return -1;
break;
case CONTEXT_LNS:
if (set_boolean (word, value, &(((struct lns *) item)->lbit)))
return -1;
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_debug (char *word, char *value, int context, void *item)
{
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_LAC:
if (set_boolean (word, value, &(((struct lac *) item)->debug)))
return -1;
break;
case CONTEXT_LNS:
if (set_boolean (word, value, &(((struct lns *) item)->debug)))
return -1;
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_pass_peer (char *word, char *value, int context, void *item)
{
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_LAC:
if (set_boolean (word, value, &(((struct lac *) item)->pass_peer)))
return -1;
break;
case CONTEXT_LNS:
if (set_boolean (word, value, &(((struct lns *) item)->pass_peer)))
return -1;
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_pppoptfile (char *word, char *value, int context, void *item)
{
struct lac *l = (struct lac *) item;
struct lns *n = (struct lns *) item;
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_LNS:
if (set_string (word, value, n->pppoptfile, sizeof (n->pppoptfile)))
return -1;
break;
case CONTEXT_LAC:
if (set_string (word, value, l->pppoptfile, sizeof (l->pppoptfile)))
return -1;
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_papchap (char *word, char *value, int context, void *item)
{
int result;
char *c;
struct lac *l = (struct lac *) item;
struct lns *n = (struct lns *) item;
if (set_boolean (word, value, &result))
return -1;
c = strchr (word, ' ');
c++;
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_LAC:
if (c[0] == 'p') /* PAP */
if (word[2] == 'f')
l->pap_refuse = result;
else
l->pap_require = result;
else if (c[0] == 'a') /* Authentication */
if (word[2] == 'f')
l->authself = !result;
else
l->authpeer = result;
else /* CHAP */ if (word[2] == 'f')
l->chap_refuse = result;
else
l->chap_require = result;
break;
case CONTEXT_LNS:
if (c[0] == 'p') /* PAP */
if (word[2] == 'f')
n->pap_refuse = result;
else
n->pap_require = result;
else if (c[0] == 'a') /* Authentication */
if (word[2] == 'f')
n->authself = !result;
else
n->authpeer = result;
else /* CHAP */ if (word[2] == 'f')
n->chap_refuse = result;
else
n->chap_require = result;
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_redial (char *word, char *value, int context, void *item)
{
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_LAC:
if (set_boolean (word, value, &(((struct lac *) item)->redial)))
return -1;
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_accesscontrol (char *word, char *value, int context, void *item)
{
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_GLOBAL:
if (set_boolean
(word, value, &(((struct global *) item)->accesscontrol)))
return -1;
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_userspace (char *word, char *value, int context, void *item)
{
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_GLOBAL:
if (set_boolean
(word, value, &(((struct global *) item)->forceuserspace)))
return -1;
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_debugavp (char *word, char *value, int context, void *item)
{
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_GLOBAL:
if (set_boolean
(word, value, &(((struct global *) item)->debug_avp)))
return -1;
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_debugnetwork (char *word, char *value, int context, void *item)
{
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_GLOBAL:
if (set_boolean
(word, value, &(((struct global *) item)->debug_network)))
return -1;
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_debugpacket (char *word, char *value, int context, void *item)
{
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_GLOBAL:
if (set_boolean
(word, value, &(((struct global *) item)->packet_dump)))
return -1;
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_debugtunnel (char *word, char *value, int context, void *item)
{
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_GLOBAL:
if (set_boolean
(word, value, &(((struct global *) item)->debug_tunnel)))
return -1;
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_debugstate (char *word, char *value, int context, void *item)
{
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_GLOBAL:
if (set_boolean
(word, value, &(((struct global *) item)->debug_state)))
return -1;
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_assignip (char *word, char *value, int context, void *item)
{
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_LNS:
if (set_boolean (word, value, &(((struct lns *) item)->assign_ip)))
return -1;
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
struct iprange *set_range (char *word, char *value, struct iprange *in)
{
char *c, *d = NULL, *e = NULL;
struct iprange *ipr, *p;
struct hostent *hp;
int count = 0;
c = strchr (value, '-');
if (c)
{
d = c + 1;
*c = 0;
while ((c >= value) && (*c < 33))
*(c--) = 0;
while (*d && (*d < 33))
d++;
}
if (!strlen (value) || (c && !strlen (d)))
{
snprintf (filerr, sizeof (filerr),
"format is '%s <host or ip> - <host or ip>'\n", word);
return NULL;
}
ipr = malloc (sizeof (struct iprange));
ipr->next = NULL;
hp = gethostbyname (value);
if (!hp)
{
snprintf (filerr, sizeof (filerr), "Unknown host %s\n", value);
free (ipr);
return NULL;
}
bcopy (hp->h_addr, &ipr->start, sizeof (unsigned int));
if (c)
{
char ip_hi[16];
e = d;
while(*e != '\0') {
if (*e++ == '.')
count++;
}
if (count < 3) {
strcpy(ip_hi, value);
for (e = ip_hi + sizeof(ip_hi); e >= ip_hi; e--) {
if (*e == '.') count--;
if (count < 0) {
e++;
break;
}
}
/* Copy the last field + null terminator */
if ((size_t)(ip_hi + sizeof(ip_hi)-e) > strlen(d)) {
strcpy(e, d);
d = ip_hi;
}
}
hp = gethostbyname (d);
if (!hp)
{
snprintf (filerr, sizeof (filerr), "Unknown host %s\n", d);
free (ipr);
return NULL;
}
bcopy (hp->h_addr, &ipr->end, sizeof (unsigned int));
}
else
ipr->end = ipr->start;
if (ntohl (ipr->start) > ntohl (ipr->end))
{
snprintf (filerr, sizeof (filerr), "start is greater than end!\n");
free (ipr);
return NULL;
}
if (word[0] == 'n')
ipr->sense = SENSE_DENY;
else
ipr->sense = SENSE_ALLOW;
p = in;
if (p)
{
while (p->next)
p = p->next;
p->next = ipr;
return in;
}
else
return ipr;
}
int set_iprange (char *word, char *value, int context, void *item)
{
struct lns *lns = (struct lns *) item;
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_LNS:
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
lns->range = set_range (word, value, lns->range);
if (!lns->range)
return -1;
#ifdef DEBUG_FILE
l2tp_log (LOG_DEBUG, "range start = %x, end = %x, sense=%ud\n",
ntohl (lns->range->start), ntohl (lns->range->end), lns->range->sense);
#endif
return 0;
}
int set_localiprange (char *word, char *value, int context, void *item)
{
struct lns *lns = (struct lns *) item;
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_LNS:
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
if (lns->localaddr) {
snprintf (filerr, sizeof (filerr), "'local ip range' and 'local ip' are mutually exclusive\n");
return -1;
}
lns->localrange = set_range (word, value, lns->localrange);
if (!lns->localrange)
return -1;
#ifdef DEBUG_FILE
l2tp_log (LOG_DEBUG, "range start = %x, end = %x, sense=%ud\n",
ntohl (lns->range->start), ntohl (lns->range->end), lns->range->sense);
#endif
return 0;
}
int set_lac (char *word, char *value, int context, void *item)
{
struct lns *lns = (struct lns *) item;
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_LNS:
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
lns->lacs = set_range (word, value, lns->lacs);
if (!lns->lacs)
return -1;
#ifdef DEBUG_FILE
l2tp_log (LOG_DEBUG, "lac start = %x, end = %x, sense=%ud\n",
ntohl (lns->lacs->start), ntohl (lns->lacs->end), lns->lacs->sense);
#endif
return 0;
}
int set_exclusive (char *word, char *value, int context, void *item)
{
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_LNS:
if (set_boolean (word, value, &(((struct lns *) item)->exclusive)))
return -1;
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_ip (char *word, char *value, unsigned int *addr)
{
UNUSED(word);
struct hostent *hp;
hp = gethostbyname (value);
if (!hp)
{
snprintf (filerr, sizeof (filerr), "%s: host '%s' not found\n",
__FUNCTION__, value);
return -1;
}
bcopy (hp->h_addr, addr, sizeof (unsigned int));
return 0;
}
int set_listenaddr (char *word, char *value, int context, void *item)
{
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_GLOBAL:
#ifdef DEBUG_FILE
l2tp_log (LOG_DEBUG, "set_listenaddr: Setting listen address to %s\n",
value);
#endif
if (set_ip (word, value, &(((struct global *) item)->listenaddr)))
return -1;
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_localaddr (char *word, char *value, int context, void *item)
{
struct lac *l;
struct lns *n;
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_LAC:
l = (struct lac *) item;
return set_ip (word, value, &(l->localaddr));
case CONTEXT_LNS:
n = (struct lns *) item;
if (n->localrange) {
snprintf (filerr, sizeof (filerr),
"'local ip range' and 'local ip' are mutually exclusive\n");
return -1;
}
return set_ip (word, value, &(n->localaddr));
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_remoteaddr (char *word, char *value, int context, void *item)
{
struct lac *l;
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_LAC:
l = (struct lac *) item;
return set_ip (word, value, &(l->remoteaddr));
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_lns (char *word, char *value, int context, void *item)
{
#if 0
struct hostent *hp;
#endif
struct lac *l;
struct host *ipr, *pos;
char *d;
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_LAC:
#ifdef DEBUG_FILE
l2tp_log (LOG_DEBUG, "set_lns: setting LNS to '%s'\n", value);
#endif
l = (struct lac *) item;
d = strchr (value, ':');
if (d)
{
d[0] = 0;
d++;
}
#if 0
// why would you want to lookup hostnames at this time?
hp = gethostbyname (value);
if (!hp)
{
snprintf (filerr, sizeof (filerr), "no such host '%s'\n", value);
return -1;
}
#endif
ipr = malloc (sizeof (struct host));
ipr->next = NULL;
pos = l->lns;
if (!pos)
{
l->lns = ipr;
}
else
{
while (pos->next)
pos = pos->next;
pos->next = ipr;
}
strncpy (ipr->hostname, value, sizeof (ipr->hostname));
if (d)
ipr->port = atoi (d);
else
ipr->port = UDP_LISTEN_PORT;
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_rand_sys ()
{
l2tp_log(LOG_WARNING, "The \"rand()\" function call is not a very good source"
"of randomness\n");
rand_source = RAND_SYS;
return 0;
}
int set_ipsec_saref (char *word, char *value, int context, void *item)
{
struct global *g = ((struct global *) item);
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_GLOBAL:
if (set_boolean
(word, value, &(g->ipsecsaref)))
return -1;
if(g->ipsecsaref) {
l2tp_log(LOG_INFO, "Enabling IPsec SAref processing for L2TP transport mode SAs\n");
}
if(g->forceuserspace != 1) {
l2tp_log(LOG_WARNING, "IPsec SAref does not work with L2TP kernel mode yet, enabling force userspace=yes\n");
g->forceuserspace = 1;
}
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n",
word);
return -1;
}
return 0;
}
int set_saref_num (char *word, char *value, int context, void *item)
{
switch (context & ~CONTEXT_DEFAULT)
{
case CONTEXT_GLOBAL:
l2tp_log (LOG_INFO, "Setting SAref IP_IPSEC_REFINFO number to %s\n", value);
set_int (word, value, &(((struct global *) item)->sarefnum));
break;
default:
snprintf (filerr, sizeof (filerr), "'%s' not valid in this context\n", word);
return -1;
}
return 0;
}
int set_rand_dev ()
{
rand_source = RAND_DEV;
return 0;
}
int set_rand_egd (char *value)
{
UNUSED(value);
l2tp_log(LOG_WARNING, "%s: not yet implemented!\n", __FUNCTION__);
rand_source = RAND_EGD;
return -1;
}
int set_rand_source (char *word, char *value, int context, void *item)
{
UNUSED(item);
time_t seconds;
/*
* We're going to go ahead and seed the rand() function with srand()
* because even if we set the randomness source to dev or egd, they
* can fall back to sys if they fail, so we want to make sure we at
* least have *some* semblance of randomness available from the
* rand() function
*/
/*
* This is a sucky random number seed...just the result from the
* time() call...but...the user requested to use the rand()
* function, which is a pretty sucky source of randomness
* regardless...at least we can get a almost sorta decent seed. If
* you have any better suggestions for creating a seed...lemme know
* :/
*/
seconds = time(NULL);
srand(seconds);
if (context != CONTEXT_GLOBAL)
{
l2tp_log(LOG_WARNING, "%s: %s not valid in context %d\n",
__FUNCTION__, word, context);
return -1;
}
/* WORKING HERE */
if (strlen(value) == 0)
{
snprintf(filerr, sizeof (filerr), "no randomness source specified\n");
return -1;
}
if (strncmp(value, "egd", 3) == 0)
{
return set_rand_egd(value);
}
else if (strncmp(value, "dev", 3) == 0)
{
return set_rand_dev();
}
else if (strncmp(value, "sys", 3) == 0)
{
return set_rand_sys();
}
else
{
l2tp_log(LOG_WARNING, "%s: %s is not a valid randomness source\n",
__FUNCTION__, value);
return -1;
}
}
int parse_config (FILE * f)
{
/* Read in the configuration file handed to us */
/* FIXME: I should check for incompatible options */
int context = 0;
char buf[1024];
char *s, *d, *t;
int linenum = 0;
int def = 0;
int in_comment = 0;
int has_lf;
void *data = NULL;
struct lns *tl;
struct lac *tc;
while (!feof (f))
{
if (NULL == fgets (buf, sizeof (buf), f))
{
/* Error or EOL */
break;
}
/* Watch for continuation comments. */
has_lf = buf[strlen(buf) - 1] == '\n';
if (in_comment)
{
in_comment = !has_lf;
continue;
}
linenum++;
s = buf;
/* Strip comments */
while (*s && *s != ';')
s++;
if (*s == ';' && !has_lf)
in_comment = 1;
*s = 0;
s = buf;
if (!strlen (buf))
continue;
while ((*s < 33) && *s)
s++; /* Skip over beginning white space */
t = s + strlen (s);
while ((t >= s) && (*t < 33))
*(t--) = 0; /* Ditch trailing white space */
if (!strlen (s))
continue;
if (s[0] == '[')
{
/* We've got a context description */
if (!(t = strchr (s, ']')))
{
l2tp_log (LOG_CRIT, "parse_config: line %d: No closing bracket\n",
linenum);
return -1;
}
t[0] = 0;
s++;
if ((d = strchr (s, ' ')))
{
/* There's a parameter */
d[0] = 0;
d++;
}
if (d && !strcasecmp (d, "default"))
def = CONTEXT_DEFAULT;
else
def = 0;
if (!strcasecmp (s, "global"))
{
context = CONTEXT_GLOBAL;
#ifdef DEBUG_FILE
l2tp_log (LOG_DEBUG,
"parse_config: global context descriptor %s\n",
d ? d : "");
#endif
data = &gconfig;
}
else if (!strcasecmp (s, "lns"))
{
context = CONTEXT_LNS;
if (def)
{
if (!deflns)
{
deflns = new_lns ();
strncpy (deflns->entname, "default",
sizeof (deflns->entname));
}
data = deflns;
continue;
}
data = NULL;
tl = lnslist;
if (d)
{
while (tl)
{
if (!strcasecmp (d, tl->entname))
break;
tl = tl->next;
}
if (tl)
data = tl;
}
if (!data)
{
data = new_lns ();
if (!data)
return -1;
((struct lns *) data)->next = lnslist;
lnslist = (struct lns *) data;
}
if (d)
strncpy (((struct lns *) data)->entname,
d, sizeof (((struct lns *) data)->entname));
#ifdef DEBUG_FILE
l2tp_log (LOG_DEBUG, "parse_config: lns context descriptor %s\n",
d ? d : "");
#endif
}
else if (!strcasecmp (s, "lac"))
{
context = CONTEXT_LAC;
if (def)
{
if (!deflac)
{
deflac = new_lac ();
strncpy (deflac->entname, "default",
sizeof (deflac->entname));
}
data = deflac;
continue;
}
data = NULL;
tc = laclist;
if (d)
{
while (tc)
{
if (!strcasecmp (d, tc->entname))
break;
tc = tc->next;
}
if (tc)
data = tc;
}
if (!data)
{
data = new_lac ();
if (!data)
return -1;
((struct lac *) data)->next = laclist;
laclist = (struct lac *) data;
}
if (d)
strncpy (((struct lac *) data)->entname,
d, sizeof (((struct lac *) data)->entname));
#ifdef DEBUG_FILE
l2tp_log (LOG_DEBUG, "parse_config: lac context descriptor %s\n",
d ? d : "");
#endif
}
else
{
l2tp_log (LOG_WARNING,
"parse_config: line %d: unknown context '%s'\n", linenum,
s);
return -1;
}
}
else
{
if (!context)
{
l2tp_log (LOG_WARNING,
"parse_config: line %d: data '%s' occurs with no context\n",
linenum, s);
return -1;
}
if (!(t = strchr (s, '=')))
{
l2tp_log (LOG_WARNING, "parse_config: line %d: line too long or no '=' in data\n",
linenum);
return -1;
}
d = t;
d--;
t++;
while ((d >= s) && (*d < 33))
d--;
d++;
*d = 0;
while (*t && (*t < 33))
t++;
#ifdef DEBUG_FILE
l2tp_log (LOG_DEBUG, "parse_config: field is %s, value is %s\n", s, t);
#endif
/* Okay, bit twiddling is done. Let's handle this */
switch (parse_one_option (s, t, context | def, data))
{
case -1:
l2tp_log (LOG_WARNING, "parse_config: line %d: %s", linenum,
filerr);
return -1;
case -2:
l2tp_log (LOG_CRIT, "parse_config: line %d: Unknown field '%s'\n",
linenum, s);
return -1;
}
}
}
return 0;
}
int parse_one_option(char *word, char *value, int context, void *item)
{
struct keyword *kw;
for (kw = words; kw->keyword; kw++)
{
if (!strcasecmp (word, kw->keyword))
{
if (kw->handler (word, value, context, item))
{
return -1;
}
break;
}
}
if (!kw->keyword)
{
return -2;
}
return 0;
}
struct keyword words[] = {
{"listen-addr", &set_listenaddr},
{"port", &set_port},
{"saref refinfo", &set_saref_num},
{"rand source", &set_rand_source},
{"auth file", &set_authfile},
{"exclusive", &set_exclusive},
{"autodial", &set_autodial},
{"redial", &set_redial},
{"redial timeout", &set_rtimeout},
{"lns", &set_lns},
{"max redials", &set_rmax},
{"access control", &set_accesscontrol},
{"force userspace", &set_userspace},
{"ip range", &set_iprange},
{"no ip range", &set_iprange},
{"debug avp", &set_debugavp},
{"debug network", &set_debugnetwork},
{"debug packet", &set_debugpacket},
{"debug tunnel", &set_debugtunnel},
{"debug state", &set_debugstate},
{"ipsec saref", &set_ipsec_saref},
{"lac", &set_lac},
{"no lac", &set_lac},
{"assign ip", &set_assignip},
{"local ip", &set_localaddr},
{"local ip range", &set_localiprange},
{"remote ip", &set_remoteaddr},
{"defaultroute", &set_defaultroute},
{"length bit", &set_lbit},
{"hidden bit", &set_hbit},
{"require pap", &set_papchap},
{"require chap", &set_papchap},
{"require authentication", &set_papchap},
{"require auth", &set_papchap},
{"refuse pap", &set_papchap},
{"refuse chap", &set_papchap},
{"refuse authentication", &set_papchap},
{"refuse auth", &set_papchap},
{"unix authentication", &set_passwdauth},
{"unix auth", &set_passwdauth},
{"name", &set_authname},
{"hostname", &set_hostname},
{"ppp debug", &set_debug},
{"pass peer", &set_pass_peer},
{"pppoptfile", &set_pppoptfile},
{"call rws", &set_rws},
{"tunnel rws", &set_rws},
{"flow bit", &set_flow},
{"challenge", &set_challenge},
{"tx bps", &set_speed},
{"rx bps", &set_speed},
{"bps", &set_speed},
{"max retries" , &set_maxretries},
{"cap backoff" , &set_capbackoff},
{NULL, NULL}
};