- Add startup-config(5) manpage [FIXME].

- Revise nsctl to allow arbitrary strings/args to be passed to plugins.
This commit is contained in:
Brendan O'Dea 2004-11-17 08:23:34 +00:00
parent 5d4d6bb059
commit 5e01d2924d
17 changed files with 796 additions and 415 deletions

10
Changes
View file

@ -1,12 +1,12 @@
* ?
- Add manpages from Jonathan McDowell
- Remove reference to old -a command line argument
* Tue Nov 16 2004 Brendan O'Dea <bod@optusnet.com.au> 2.0.8
* Wed Nov 17 2004 Brendan O'Dea <bod@optusnet.com.au> 2.0.8
- Ignore gateway address in Framed-Route (from Jonathan McDowell).
- Call sessionshutdown() when a tunnel is dropped rather than
sessionkill() to ensure that RADIUS stop records are sent.
- Cleanup: make a bunch of global functions/variables static.
- Remove reference to old -a command line argument.
- Add l2tpns(8) and nsctl(8) manpages from Jonathan McDowell.
- Add startup-config(5) manpage [FIXME].
- Revise nsctl to allow arbitrary strings/args to be passed to plugins.
* Mon Nov 15 2004 Brendan O'Dea <bod@optusnet.com.au> 2.0.7
- Fix socket creation in host_unreachable() (thanks to Bjørn Augestad)

64
Docs/l2tpns.8 Normal file
View file

@ -0,0 +1,64 @@
.\" -*- nroff -*-
.de Id
.ds Dt \\$4 \\$5
..
.Id $Id: l2tpns.8,v 1.1 2004-11-17 08:23:35 bodea Exp $
.TH L2TPNS 8 "\*(Dt" L2TPNS "System Management Commands"
.SH NAME
l2tpns \- L2TP LNS daemon
.SH SYNOPSIS
.B l2tpns
.RB [ \-d ]
.RB [ \-v ]
.RB [ \-c
.IR file ]
.RB [ \-h
.IR hostname ]
.SH DESCRIPTION
This manual page documents briefly the
.B l2tpns
command.
.PP
.B l2tpns
is an L2TP LNS daemon that doesn't require kernel support for PPP or
L2TP, nor any kernel patches. For more information on L2TP see RFC
2661.
.PP
Once running,
.B l2tpns
may be controlled by telnetting to port 23 on the machine running the
daemon and with the
.B nsctl
utility.
.SH OPTIONS
.TP
.B \-d
Detach from terminal and fork into the background. By default l2tpns
will stay in the foreground.
.TP
.B \-v
Increase verbosity for debugging. Can be used multiple times.
.TP
.BI "\-c " file
Specify configuration file.
.TP
.BI "\-h " hostname
Force hostname to
.IR hostname .
.SH FILES
.TP
.I /etc/l2tpns/startup-config
The default configuration file.
.TP
.I /etc/l2tpns/ip_pool
IP address pool configuration.
.TP
.I /etc/l2tpns/users
Username/password configuration for access to admin interface.
.SH SEE ALSO
.BR startup-config (5),
and
.BR nsctl (8)
.SH AUTHOR
This manual page was written by Jonathan McDowell <noodles@earth.li>,
for the Debian GNU/Linux system (but may be used by others).

67
Docs/nsctl.8 Normal file
View file

@ -0,0 +1,67 @@
.\" -*- nroff -*-
.de Id
.ds Dt \\$4 \\$5
..
.Id $Id: nsctl.8,v 1.1 2004-11-17 08:23:35 bodea Exp $
.TH NSCTL 8 "\*(Dt" L2TPNS "System Management Commands"
.SH NAME
nsctl \- Issue commands to l2tpns plugins
.SH SYNOPSIS
.B nsctl
.RB [ \-d ]
.RB [ \-h
.IR host [: port ]]
.RB [ \-t
.IR timeout ]
.I command
.RI [ arg " ...]"
.SH DESCRIPTION
.B nsctl
is part of the
.B l2tpns
package. It allows the system administrator to send manage plugin
features of a running l2tpns process.
.SH OPTIONS
.TP
.B \-d
Enable debugging output.
.TP
.B \-h \fIhost\fR[:\fIport\fR]
The host running
.B l2tpns
that should receive the message. By default the message is sent to
UDP port 1702 on
.BR localhost .
.TP
.B \-t \fItimeout\fR
Timeout in seconds to wait for a response from the server.
.SH COMMANDS
The first argument specifies the command to send to
.B l2tpns .
The following commands are as defined:
.TP
.BI "load_plugin " plugin
Load the named
.IR plugin .
.TP
.BI "unload_plugin " plugin
Unload the named
.IR plugin .
.TP
.B help
Each loaded plugin is queried for what commands it supports and the
synopsis for each is output.
.PP
Any other value of
.I command
(and
.I args
if any)
are sent to
.B l2tpns
as-is, to be passed to each plugin in turn (and possibly acted upon).
.SH SEE ALSO
.BR l2tpns (8)
.SH AUTHOR
This manual page was written by Jonathan McDowell <noodles@the.earth.li>,
for the Debian GNU/Linux system (but may be used by others).

View file

@ -2,7 +2,8 @@ DESTDIR =
bindir = /usr/sbin
etcdir = /etc/l2tpns
libdir = /usr/lib/l2tpns
mandir = /usr/share/man/man8
man5dir = /usr/share/man/man5
man8dir = /usr/share/man/man8
statedir = /var/lib/l2tpns
DEFINES =
@ -84,9 +85,14 @@ bounce: test/bounce.o
install: all
$(INSTALL) -m 0755 l2tpns $(DESTDIR)$(bindir)/l2tpns
$(INSTALL) -m 0644 l2tpns.8 $(DESTDIR)$(mandir)/l2tpns.8
$(INSTALL) -m 0755 nsctl $(DESTDIR)$(bindir)/nsctl
$(INSTALL) -m 0644 nsctl.8 $(DESTDIR)$(mandir)/nsctl.8
$(INSTALL) -m 0644 Docs/startup-config.5 $(DESTDIR)$(man5dir)/startup-config.5
$(INSTALL) -m 0644 Docs/l2tpns.8 $(DESTDIR)$(man8dir)/l2tpns.8
$(INSTALL) -m 0644 Docs/nsctl.8 $(DESTDIR)$(man8dir)/nsctl.8
gzip $(DESTDIR)$(man5dir)/*.5 $(DESTDIR)$(man8dir)/*.8
@if [ -f $(DESTDIR)$(etcdir)/startup-config ]; then \
echo '***' Installing default config files in $(DESTDIR)$(etcdir) as .defaults; \
suffix=.default; \
@ -115,7 +121,7 @@ bgp.o: bgp.c l2tpns.h bgp.h util.h
cli.o: cli.c l2tpns.h util.h cluster.h tbf.h ll.h bgp.h
cluster.o: cluster.c l2tpns.h cluster.h util.h tbf.h bgp.h
constants.o: constants.c constants.h
control.o: control.c control.h
control.o: control.c l2tpns.h control.h
icmp.o: icmp.c l2tpns.h
l2tpns.o: l2tpns.c md5.h l2tpns.h cluster.h plugin.h ll.h constants.h \
control.h util.h tbf.h bgp.h

View file

@ -4,9 +4,9 @@
/* set up intercept based on RADIUS reply */
char const *cvs_id = "$Id: autosnoop.c,v 1.7 2004-11-09 08:05:02 bodea Exp $";
char const *cvs_id = "$Id: autosnoop.c,v 1.8 2004-11-17 08:23:34 bodea Exp $";
int __plugin_api_version = PLUGIN_API_VERSION;
int plugin_api_version = PLUGIN_API_VERSION;
struct pluginfuncs *p;
int plugin_radius_response(struct param_radius_response *data)

View file

@ -4,9 +4,9 @@
/* set up throttling based on RADIUS reply */
char const *cvs_id = "$Id: autothrottle.c,v 1.8 2004-11-09 08:05:02 bodea Exp $";
char const *cvs_id = "$Id: autothrottle.c,v 1.9 2004-11-17 08:23:34 bodea Exp $";
int __plugin_api_version = PLUGIN_API_VERSION;
int plugin_api_version = PLUGIN_API_VERSION;
struct pluginfuncs *p;
#define THROTTLE_KEY "lcp:interface-config"

189
control.c
View file

@ -1,74 +1,163 @@
// L2TPNS: control
char const *cvs_id_control = "$Id: control.c,v 1.2 2004-06-28 02:43:13 fred_nerk Exp $";
char const *cvs_id_control = "$Id: control.c,v 1.3 2004-11-17 08:23:34 bodea Exp $";
#include <stdio.h>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <malloc.h>
#include <netdb.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include "l2tpns.h"
#include "control.h"
int new_packet(short type, char *packet)
int pack_control(char *data, int len, u8 type, int argc, char *argv[])
{
int id = (time(NULL) ^ (rand() * 1024*1024));
struct nsctl_packet pkt;
struct nsctl_args arg;
char *p = pkt.argv;
int sz = (p - (char *) &pkt);
*(short *)(packet + 0) = ntohs(0x9012);
*(short *)(packet + 2) = ntohs(type);
*(int *)(packet + 6) = ntohl(id);
if (len > sizeof(pkt))
len = sizeof(pkt);
return 10;
if (argc > 0xff)
argc = 0xff; // paranoia
pkt.magic = ntohs(NSCTL_MAGIC);
pkt.type = type;
pkt.argc = argc;
while (argc-- > 0)
{
char *a = *argv++;
int s = strlen(a);
if (s > sizeof(arg.value))
s = sizeof(arg.value); // silently truncate
arg.len = s;
s += sizeof(arg.len);
if (sz + s > len)
return -1; // overflow
if (arg.len)
memcpy(arg.value, a, arg.len);
memcpy(p, &arg, s);
sz += s;
p += s;
}
int send_packet(int sockfd, int dest_ip, int dest_port, char *packet, int len)
{
struct sockaddr_in addr;
/*
* terminate: this is both a sanity check and additionally
* ensures that there's a spare byte in the packet to null
* terminate the last argument when unpacking (see unpack_control)
*/
if (sz + sizeof(arg.len) > len)
return -1; // overflow
*(short *)(packet + 4) = ntohs(len);
arg.len = 0xff;
memcpy(p, &arg.len, sizeof(arg.len));
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
*(int*)&addr.sin_addr = htonl(dest_ip);
addr.sin_port = htons(dest_port);
if (sendto(sockfd, packet, len, 0, (void *) &addr, sizeof(addr)) < 0)
{
perror("sendto");
return 0;
}
return 1;
sz += sizeof(arg.len);
memcpy(data, &pkt, sz);
return sz;
}
int read_packet(int sockfd, char *packet)
int unpack_control(struct nsctl *control, char *data, int len)
{
struct sockaddr_in addr;
int alen = sizeof(addr);
memset(&addr, 0, sizeof(addr));
return recvfrom(sockfd, packet, 1400, 0, (void *) &addr, &alen);
struct nsctl_packet pkt;
char *p = pkt.argv;
int sz = (p - (char *) &pkt);
int i;
if (len < sz)
return NSCTL_ERR_SHORT;
if (len > sizeof(pkt))
return NSCTL_ERR_LONG;
memcpy(&pkt, data, len);
if (ntohs(pkt.magic) != NSCTL_MAGIC)
return NSCTL_ERR_MAGIC;
switch (pkt.type)
{
case NSCTL_REQ_LOAD:
case NSCTL_REQ_UNLOAD:
case NSCTL_REQ_HELP:
case NSCTL_REQ_CONTROL:
case NSCTL_RES_OK:
case NSCTL_RES_ERR:
control->type = pkt.type;
break;
default:
return NSCTL_ERR_TYPE;
}
void dump_packet(char *packet, FILE *stream)
control->argc = pkt.argc;
for (i = 0; i <= control->argc; i++)
{
if (htons(*(short *)(packet + 0)) != 0x9012)
unsigned s;
if (len < sz + 1)
return NSCTL_ERR_SHORT;
s = (u8) *p;
*p++ = 0; // null terminate previous arg
sz++;
if (i < control->argc)
{
fprintf(stream, "Invalid packet identifier %x\n", htons(*(short *)(packet + 0)));
return;
if (len < sz + s)
return NSCTL_ERR_SHORT;
control->argv[i] = p;
p += s;
sz += s;
}
else
{
/* check for terminator */
if (s != 0xff)
return NSCTL_ERR_SHORT;
}
}
if (sz != len)
return NSCTL_ERR_LONG; // trailing cr*p
return control->type;
}
void dump_control(struct nsctl *control, FILE *stream)
{
char *type = "*unknown*";
if (!stream)
stream = stdout;
switch (control->type)
{
case NSCTL_REQ_LOAD: type = "NSCTL_REQ_LOAD"; break;
case NSCTL_REQ_UNLOAD: type = "NSCTL_REQ_UNLOAD"; break;
case NSCTL_REQ_HELP: type = "NSCTL_REQ_HELP"; break;
case NSCTL_REQ_CONTROL: type = "NSCTL_REQ_CONTROL"; break;
case NSCTL_RES_OK: type = "NSCTL_RES_OK"; break;
case NSCTL_RES_ERR: type = "NSCTL_RES_ERR"; break;
}
fprintf(stream, "Control packet:\n");
fprintf(stream, " Type: %d\n", htons(*(short *)(packet + 2)));
fprintf(stream, " Length: %d\n", htons(*(short *)(packet + 4)));
fprintf(stream, " Identifier: %x\n", htonl(*(int *)(packet + 6)));
fprintf(stream, "\n");
fprintf(stream, " Type: %d (%s)\n", (int) control->type, type);
fprintf(stream, " Args: %d", (int) control->argc);
if (control->argc)
{
int i;
fprintf(stream, " (\"");
for (i = 0; i < control->argc; i++)
fprintf(stream, "%s%s", i ? "\", \"" : "", control->argv[i]);
fprintf(stream, "\")");
}
fprintf(stream, "\n\n");
}

View file

@ -1,18 +1,54 @@
#ifndef __CONTROL_H__
#define __CONTROL_H__
#define PKT_RESP_OK 1
#define PKT_RESP_ERROR 2
#define NSCTL_PORT 1702
#define NSCTL_MAGIC 0x9013
#define PKT_LOAD_PLUGIN 5
#define PKT_UNLOAD_PLUGIN 6
/* builtin commands */
#define NSCTL_REQUEST (1 << 4)
#define NSCTL_REQ_LOAD (NSCTL_REQUEST | 1)
#define NSCTL_REQ_UNLOAD (NSCTL_REQUEST | 2)
#define NSCTL_REQ_HELP (NSCTL_REQUEST | 3)
#define PKT_GARDEN 1000
#define PKT_UNGARDEN 1001
/* general control message, passed to plugins */
#define NSCTL_REQ_CONTROL (NSCTL_REQUEST | 4)
int new_packet(short type, char *packet);
int send_packet(int sockfd, int dest_ip, int dest_port, char *packet, int len);
void dump_packet(char *packet, FILE *stream);
int read_packet(int sockfd, char *packet);
/* response messages */
#define NSCTL_RESPONSE (1 << 5)
#define NSCTL_RES_OK (NSCTL_RESPONSE | 1)
#define NSCTL_RES_ERR (NSCTL_RESPONSE | 2)
/* unpack errors */
#define NSCTL_ERR_SHORT -1 // short packet
#define NSCTL_ERR_LONG -2 // packet exceeds max, or trailing cr*p
#define NSCTL_ERR_MAGIC -3 // invalid magic number
#define NSCTL_ERR_TYPE -4 // unrecognised type
#define NSCTL_MAX_PKT_SZ 4096
struct nsctl_packet {
u16 magic;
u8 type;
u8 argc;
char argv[NSCTL_MAX_PKT_SZ - 4];
} __attribute__ ((packed));
#define NSCTL_MAX_ARG_SZ 512
struct nsctl_args {
u8 len;
char value[NSCTL_MAX_ARG_SZ - 1];
} __attribute__ ((packed));
/* parsed packet */
struct nsctl {
u8 type;
u8 argc;
char *argv[0xff];
};
int pack_control(char *data, int len, u8 type, int argc, char *argv[]);
int unpack_control(struct nsctl *packet, char *data, int len);
void dump_control(struct nsctl *control, FILE *stream);
#endif /* __CONTROL_H__ */

View file

@ -9,9 +9,9 @@
/* walled garden */
char const *cvs_id = "$Id: garden.c,v 1.12 2004-11-09 08:05:02 bodea Exp $";
char const *cvs_id = "$Id: garden.c,v 1.13 2004-11-17 08:23:34 bodea Exp $";
int __plugin_api_version = PLUGIN_API_VERSION;
int plugin_api_version = PLUGIN_API_VERSION;
static struct pluginfuncs *p = 0;
static int iam_master = 0; // We're all slaves! Slaves I tell you!
@ -75,46 +75,62 @@ int plugin_kill_session(struct param_new_session *data)
return PLUGIN_RET_OK;
}
char *plugin_control_help[] = {
" garden USER|SID Put user into the walled garden",
" ungarden USER|SID Release user",
0
};
int plugin_control(struct param_control *data)
{
sessiont *s;
sessionidt session;
sessiont *s = 0;
int flag;
char *end;
if (data->argc < 1 || (strcmp(data->argv[0], "garden") && strcmp(data->argv[0], "ungarden")))
return PLUGIN_RET_OK; // not for us
flag = data->argv[0][0] == 'g';
if (!iam_master) // All garden processing happens on the master.
return PLUGIN_RET_OK;
if (data->type != PKT_GARDEN && data->type != PKT_UNGARDEN)
return PLUGIN_RET_OK;
if (!data->data && data->data_length)
return PLUGIN_RET_OK;
session = atoi((char*)(data->data));
if (!session)
return PLUGIN_RET_OK;
data->send_response = 1;
s = p->get_session_by_id(session);
if (!s || !s->ip)
{
char *errormsg = "Session not connected";
*(short *)(data->response + 2) = ntohs(PKT_RESP_ERROR);
sprintf((data->response + data->response_length), "%s", errormsg);
data->response_length += strlen(errormsg) + 1;
p->log(3, 0, 0, 0, "Unknown session %d\n", session);
data->response = NSCTL_RES_ERR;
data->additional = "must be run on the cluster master";
return PLUGIN_RET_STOP;
}
*(short *)(data->response + 2) = ntohs(PKT_RESP_OK);
if (!(garden_session(s, (data->type == PKT_GARDEN))))
if (data->argc != 2)
{
char *errormsg = "User not connected";
*(short *)(data->response + 2) = ntohs(PKT_RESP_ERROR);
sprintf((data->response + data->response_length), "%s", errormsg);
data->response_length += strlen(errormsg) + 1;
data->response = NSCTL_RES_ERR;
data->additional = "one argument required: username or session id";
return PLUGIN_RET_STOP;
}
if (!(session = strtol(data->argv[0], &end, 10)) || *end)
session = p->get_session_by_username(data->argv[0]);
if (session)
s = p->get_session_by_id(session);
if (!s || !s->ip)
{
data->response = NSCTL_RES_ERR;
data->additional = "session not found";
return PLUGIN_RET_STOP;
}
if (s->walled_garden == flag)
{
data->response = NSCTL_RES_ERR;
data->additional = flag ? "already in walled garden" : "not in walled garden";
return PLUGIN_RET_STOP;
}
garden_session(s, flag);
data->response = NSCTL_RES_OK;
data->additional = 0;
return PLUGIN_RET_STOP;
}
@ -136,7 +152,10 @@ int plugin_become_master(void)
int plugin_new_session_master(sessiont * s)
{
if (s->walled_garden)
{
s->walled_garden = 0;
garden_session(s, 1);
}
return PLUGIN_RET_OK;
}

View file

@ -1,68 +0,0 @@
.\" Hey, EMACS: -*- nroff -*-
.\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1)
.TH L2TPNS 8 "November 16, 2004"
.\" Please adjust this date whenever revising the manpage.
.\"
.\" Some roff macros, for reference:
.\" .nh disable hyphenation
.\" .hy enable hyphenation
.\" .ad l left justify
.\" .ad b justify to both left and right margins
.\" .nf disable filling
.\" .fi enable filling
.\" .br insert line break
.\" .sp <n> insert n+1 empty lines
.\" for manpage-specific macros, see man(7)
.SH NAME
l2tpns \- L2TP LNS daemon
.SH SYNOPSIS
.B l2tpns
[ \fB-d\fR ] [ \fB-c\fR \fI<file>\fR ] [ \fB-h\fR \fI<hostname>\fR ] [ \fB-a\fR \fI<address>\fR ] [ \fB-v\fR ]
.br
.SH DESCRIPTION
This manual page documents briefly the
.B l2tpns
command.
.PP
.\" TeX users may be more comfortable with the \fB<whatever>\fP and
.\" \fI<whatever>\fP escape sequences to invode bold face and italics,
.\" respectively.
\fBl2tpns\fP is an L2TP LNS daemon that doesn't require kernel support
for PPP or L2TP, nor any kernel patches. For more information on L2TP see
RFC 2661.
.PP
Once running l2tpns can be controlled by telnetting to port 23 on the
machine running the daemon.
.SH OPTIONS
.TP
.B \-d
Detach from terminal and fork into the background. By default l2tpns
will stay in the foreground.
.TP
.B \-c <file>
Specify config file.
.TP
.B \-h <hostname>
Force hostname to <hostname>.
.TP
.B \-v
Increase verbosity for debugging. Can be used multiple times.
.br
.SH FILES
.TP
\fB\fI/etc/l2tpns/startup-config\fR
The default configuration file.
.TP
\fB\fI/etc/l2tpns/ip_pool\fR
IP address pool configuration.
.TP
\fB\fI/etc/l2tpns/users\fR
Username/password configuration for access to admin interface.
.SH SEE ALSO
\fInsctl\fR(8)
.SH AUTHOR
This manual page was written by Jonathan McDowell <noodles@earth.li>,
for the Debian GNU/Linux system (but may be used by others).

202
l2tpns.c
View file

@ -4,7 +4,7 @@
// Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced
// vim: sw=8 ts=8
char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.50 2004-11-16 21:54:46 fred_nerk Exp $";
char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.51 2004-11-17 08:23:34 bodea Exp $";
#include <arpa/inet.h>
#include <assert.h>
@ -167,10 +167,10 @@ static void build_chap_response(char *challenge, u8 id, u16 challenge_length, ch
static void update_config(void);
static void read_config_file(void);
static void initplugins(void);
static void add_plugin(char *plugin_name);
static void remove_plugin(char *plugin_name);
static int add_plugin(char *plugin_name);
static int remove_plugin(char *plugin_name);
static void plugins_done(void);
static void processcontrol(u8 * buf, int len, struct sockaddr_in *addr);
static void processcontrol(u8 * buf, int len, struct sockaddr_in *addr, int alen);
static tunnelidt new_tunnel(void);
static int unhide_avp(u8 *avp, tunnelidt t, sessionidt s, u16 length);
@ -434,8 +434,8 @@ static void initudp(void)
// Control
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(1702);
controlfd = socket(AF_INET, SOCK_DGRAM, 17);
addr.sin_port = htons(NSCTL_PORT);
controlfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
setsockopt(controlfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
if (bind(controlfd, (void *) &addr, sizeof(addr)) < 0)
{
@ -2324,7 +2324,7 @@ static void mainloop(void)
}
if (FD_ISSET(controlfd, &r))
processcontrol(buf, recvfrom(controlfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr);
processcontrol(buf, recvfrom(controlfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr, alen);
if (FD_ISSET(clifd, &r))
{
@ -3751,7 +3751,7 @@ static void *getconfig(char *key, enum config_typet type)
return 0;
}
static void add_plugin(char *plugin_name)
static int add_plugin(char *plugin_name)
{
static struct pluginfuncs funcs = {
_log,
@ -3773,22 +3773,22 @@ static void add_plugin(char *plugin_name)
if (!p)
{
LOG(1, 0, 0, 0, " Plugin load failed: %s\n", dlerror());
return;
return -1;
}
if (ll_contains(loaded_plugins, p))
{
dlclose(p);
return;
return 0; // already loaded
}
{
int *v = dlsym(p, "__plugin_api_version");
int *v = dlsym(p, "plugin_api_version");
if (!v || *v != PLUGIN_API_VERSION)
{
LOG(1, 0, 0, 0, " Plugin load failed: API version mismatch: %s\n", dlerror());
dlclose(p);
return;
return -1;
}
}
@ -3798,7 +3798,7 @@ static void add_plugin(char *plugin_name)
{
LOG(1, 0, 0, 0, " Plugin load failed: plugin_init() returned FALSE: %s\n", dlerror());
dlclose(p);
return;
return -1;
}
}
@ -3815,6 +3815,7 @@ static void add_plugin(char *plugin_name)
}
LOG(2, 0, 0, 0, " Loaded plugin %s\n", plugin_name);
return 1;
}
static void run_plugin_done(void *plugin)
@ -3825,14 +3826,17 @@ static void run_plugin_done(void *plugin)
donefunc();
}
static void remove_plugin(char *plugin_name)
static int remove_plugin(char *plugin_name)
{
void *p = open_plugin(plugin_name, 0);
int i;
int loaded = 0;
if (!p)
return;
return -1;
if (ll_contains(loaded_plugins, p))
{
int i;
for (i = 0; i < max_plugin_functions; i++)
{
void *x;
@ -3840,14 +3844,14 @@ static void remove_plugin(char *plugin_name)
ll_delete(plugins[i], x);
}
if (ll_contains(loaded_plugins, p))
{
ll_delete(loaded_plugins, p);
run_plugin_done(p);
loaded = 1;
}
dlclose(p);
LOG(2, 0, 0, 0, "Removed plugin %s\n", plugin_name);
return loaded;
}
int run_plugins(int plugin_type, void *data)
@ -3875,39 +3879,149 @@ static void plugins_done()
run_plugin_done(p);
}
static void processcontrol(u8 * buf, int len, struct sockaddr_in *addr)
static void processcontrol(u8 * buf, int len, struct sockaddr_in *addr, int alen)
{
char *resp;
int l;
struct param_control param = { buf, len, ntohl(addr->sin_addr.s_addr), ntohs(addr->sin_port), NULL, 0, 0 };
struct nsctl request;
struct nsctl response;
int type = unpack_control(&request, buf, len);
int r;
void *p;
if (log_stream && config->debug >= 4)
{
LOG(4, ntohl(addr->sin_addr.s_addr), 0, 0, "Received ");
dump_packet(buf, log_stream);
}
resp = calloc(1400, 1);
l = new_packet(PKT_RESP_ERROR, resp);
*(int *)(resp + 6) = *(int *)(buf + 6);
param.type = ntohs(*(short *)(buf + 2));
param.id = ntohl(*(int *)(buf + 6));
param.data_length = ntohs(*(short *)(buf + 4)) - 10;
param.data = (param.data_length > 0) ? (char *)(buf + 10) : NULL;
param.response = resp;
param.response_length = l;
run_plugins(PLUGIN_CONTROL, &param);
if (param.send_response)
if (type < 0)
{
send_packet(controlfd, ntohl(addr->sin_addr.s_addr), ntohs(addr->sin_port), param.response, param.response_length);
LOG(4, ntohl(addr->sin_addr.s_addr), 0, 0, "Sent Control packet response\n");
LOG(4, ntohl(addr->sin_addr.s_addr), 0, 0, "Bogus control message (%d)\n", type);
}
else
{
LOG(4, ntohl(addr->sin_addr.s_addr), 0, 0, "Received ");
dump_control(&request, log_stream);
}
}
free(resp);
switch (type)
{
case NSCTL_REQ_LOAD:
if (request.argc != 1)
{
response.type = NSCTL_RES_ERR;
response.argc = 1;
response.argv[0] = "name of plugin required";
}
else if ((r = add_plugin(request.argv[0])) < 1)
{
response.type = NSCTL_RES_ERR;
response.argc = 1;
response.argv[0] = !r
? "plugin already loaded"
: "error loading plugin";
}
else
{
response.type = NSCTL_RES_OK;
response.argc = 0;
}
break;
case NSCTL_REQ_UNLOAD:
if (request.argc != 1)
{
response.type = NSCTL_RES_ERR;
response.argc = 1;
response.argv[0] = "name of plugin required";
}
else if ((r = remove_plugin(request.argv[0])) < 1)
{
response.type = NSCTL_RES_ERR;
response.argc = 1;
response.argv[0] = !r
? "plugin not loaded"
: "plugin not found";
}
else
{
response.type = NSCTL_RES_OK;
response.argc = 0;
}
break;
case NSCTL_REQ_HELP:
response.type = NSCTL_RES_OK;
response.argc = 0;
ll_reset(loaded_plugins);
while ((p = ll_next(loaded_plugins)))
{
char **help = dlsym(p, "plugin_control_help");
while (response.argc < 0xff && help && *help)
response.argv[response.argc++] = *help++;
}
break;
case NSCTL_REQ_CONTROL:
{
struct param_control param = { request.argc, request.argv, 0, NULL };
if (!run_plugins(PLUGIN_CONTROL, &param))
{
response.type = NSCTL_RES_ERR;
response.argc = 1;
response.argv[0] = param.additional
? param.additional
: "error returned by plugin";
}
else if (!(param.response & NSCTL_RESPONSE))
{
response.type = NSCTL_RES_ERR;
response.argc = 1;
response.argv[0] = param.response
? "unrecognised response value from plugin"
: "unhandled action";
}
else
{
response.type = param.response;
response.argc = 0;
if (param.additional)
{
response.argc = 1;
response.argv[0] = param.additional;
}
}
}
break;
default:
response.type = NSCTL_RES_ERR;
response.argc = 1;
response.argv[0] = "error unpacking control packet";
}
buf = calloc(NSCTL_MAX_PKT_SZ, 1);
if (!buf)
{
LOG(2, ntohl(addr->sin_addr.s_addr), 0, 0, "Failed to allocate nsctl response\n");
return;
}
r = pack_control(buf, NSCTL_MAX_PKT_SZ, response.type, response.argc, response.argv);
if (r > 0)
{
sendto(controlfd, buf, r, 0, (const struct sockaddr *) addr, alen);
if (log_stream && config->debug >= 4)
{
LOG(4, ntohl(addr->sin_addr.s_addr), 0, 0, "Sent ");
dump_control(&response, log_stream);
}
}
else
LOG(2, ntohl(addr->sin_addr.s_addr), 0, 0, "Failed to pack nsctl response (%d)\n", r);
free(buf);
}
static tunnelidt new_tunnel()

View file

@ -39,7 +39,8 @@ rm -rf %{buildroot}
%config(noreplace) /etc/l2tpns/ip_pool
%attr(700,root,root) /usr/sbin/l2tpns
%attr(755,root,root) /usr/lib/l2tpns
%attr(644,root,root) /usr/share/man/man[58]/*
%changelog
* Mon Nov 15 2004 Brendan O'Dea <bod@optusnet.com.au> 2.0.8-1
* Wed Nov 17 2004 Brendan O'Dea <bod@optusnet.com.au> 2.0.8-1
- 2.0.8 release, see /usr/share/doc/l2tpns-2.0.8/Changes

41
nsctl.8
View file

@ -1,41 +0,0 @@
.\" Hey, EMACS: -*- nroff -*-
.\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1)
.TH NSCTL 8 "November 16, 2004"
.\" Please adjust this date whenever revising the manpage.
.\"
.\" Some roff macros, for reference:
.\" .nh disable hyphenation
.\" .hy enable hyphenation
.\" .ad l left justify
.\" .ad b justify to both left and right margins
.\" .nf disable filling
.\" .fi enable filling
.\" .br insert line break
.\" .sp <n> insert n+1 empty lines
.\" for manpage-specific macros, see man(7)
.SH NAME
nsctl \- Issue commands to l2tpns plugins
.SH SYNOPSIS
.B nsctl \fI<host>\fP \fI<command>\fP [ \fIargs...\fP ]
.SH DESCRIPTION
nsctl is part of the l2tpns package. It allows the system administrator
to allow messages to be passed to l2tpns plugins.
.SH OPTIONS
.TP
.B <host>
The host running l2tpns that should receive the message.
.TP
.B <command>
The command to send. Currently one of \fIload_plugin\fP,
\fIunload_plugin\fP, \fIgarden\fP or \fIungarden\fP.
\fIgarden\fP/\fIungarden\fP enable or disable the walled garden plugin
for a particular user; they take a single argument which is the session
id to affect.
.SH SEE ALSO
.BR l2tpns(8)
.SH AUTHOR
This manual page was written by Jonathan McDowell <noodles@the.earth.li>,
for the Debian GNU/Linux system (but may be used by others).

317
nsctl.c
View file

@ -1,138 +1,239 @@
/* l2tpns plugin control */
#include <stdio.h>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <malloc.h>
#include <netdb.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <signal.h>
#include "l2tpns.h"
#include "control.h"
struct { char *command; int pkt_type; int params; } commands[] = {
{ "load_plugin", PKT_LOAD_PLUGIN, 1 },
{ "unload_plugin", PKT_UNLOAD_PLUGIN, 1 },
{ "garden", PKT_GARDEN, 1 },
{ "ungarden", PKT_UNGARDEN, 1 },
struct {
char *command;
char *usage;
int action;
} builtins[] = {
{ "load_plugin", " PLUGIN Load named plugin", NSCTL_REQ_LOAD },
{ "unload_plugin", " PLUGIN Unload named plugin", NSCTL_REQ_UNLOAD },
{ "help", " List available commands", NSCTL_REQ_HELP },
{ 0 }
};
char *dest_host = NULL;
unsigned int dest_port = 1702;
int udpfd;
static int debug = 0;
static int timeout = 2; // 2 seconds
static char *me;
#define USAGE() fprintf(stderr, "Usage: %s [-d] [-h HOST[:PORT]] [-t TIMEOUT] COMMAND [ARG ...]\n", me)
static struct nsctl *request(char *host, int port, int type, int argc, char *argv[]);
int main(int argc, char *argv[])
{
int len = 0;
int dest_ip = 0;
int pkt_type = 0;
char *packet = NULL;
int req_type = 0;
char *host = 0;
int port;
int i;
char *p;
struct nsctl *res;
setbuf(stdout, NULL);
if ((p = strrchr((me = argv[0]), '/')))
me = p + 1;
if (argc < 3)
opterr = 0;
while ((i = getopt(argc, argv, "dh:t:")) != -1)
switch (i)
{
printf("Usage: %s <host> <command> [args...]\n", argv[0]);
return 1;
}
dest_host = strdup(argv[1]);
{
// Init socket
int on = 1;
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(1703);
udpfd = socket(AF_INET, SOCK_DGRAM, 17);
setsockopt(udpfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
if (bind(udpfd, (void *) &addr, sizeof(addr)) < 0)
{
perror("bind");
return(1);
}
}
{
struct hostent *h = gethostbyname(dest_host);
if (h) dest_ip = ntohl(*(unsigned int *)h->h_addr);
if (!dest_ip) dest_ip = ntohl(inet_addr(dest_host));
if (!dest_ip)
{
printf("Can't resolve \"%s\"\n", dest_host);
return 0;
}
}
if (!(packet = calloc(1400, 1)))
{
perror("calloc");
return(1);
}
srand(time(NULL));
// Deal with command & params
for (i = 0; i < (sizeof(commands) / sizeof(commands[0])); i++)
{
if (strcasecmp(commands[i].command, argv[2]) == 0)
{
int p;
pkt_type = commands[i].pkt_type;
len = new_packet(pkt_type, packet);
if (argc < (commands[i].params + 3))
{
printf("Not enough parameters for %s\n", argv[2]);
return 1;
}
for (p = 0; p < commands[i].params; p++)
{
strncpy((packet + len), argv[p + 3], 1400 - len - 1);
len += strlen(argv[p + 3]) + 1;
}
case 'd':
debug++;
break;
}
}
if (!pkt_type)
{
printf("Unknown command\n");
return 1;
case 'h':
host = optarg;
break;
case 't':
timeout = atoi(optarg);
break;
default:
USAGE();
return EXIT_FAILURE;
}
send_packet(udpfd, dest_ip, dest_port, packet, len);
argc -= optind;
argv += optind;
if (argc < 1 || !argv[0][0])
{
int n;
fd_set r;
struct timeval timeout;
USAGE();
return EXIT_FAILURE;
}
FD_ZERO(&r);
FD_SET(udpfd, &r);
timeout.tv_sec = 1;
timeout.tv_usec = 0;
if (!host)
host = "127.0.0.1";
n = select(udpfd + 1, &r, 0, 0, &timeout);
if (n <= 0)
if ((p = strchr(host, ':')))
{
printf("Timeout waiting for packet\n");
port = atoi(p + 1);
if (!port)
{
fprintf(stderr, "%s: invalid port `%s'\n", me, p + 1);
return EXIT_FAILURE;
}
*p = 0;
}
else
{
port = NSCTL_PORT;
}
for (i = 0; !req_type && builtins[i].command; i++)
if (!strcmp(argv[0], builtins[i].command))
req_type = builtins[i].action;
if (req_type == NSCTL_REQ_HELP)
{
printf("Available commands:\n");
for (i = 0; builtins[i].command; i++)
printf(" %s%s\n", builtins[i].command, builtins[i].usage);
}
if (req_type)
{
argc--;
argv++;
}
else
{
req_type = NSCTL_REQ_CONTROL;
}
if ((res = request(host, port, req_type, argc, argv)))
{
FILE *stream = stderr;
int status = EXIT_FAILURE;
if (res->type == NSCTL_RES_OK)
{
stream = stdout;
status = EXIT_SUCCESS;
}
for (i = 0; i < res->argc; i++)
fprintf(stream, "%s\n", res->argv[i]);
return status;
}
return EXIT_FAILURE;
}
static void sigalrm_handler(int sig) { }
static struct nsctl *request(char *host, int port, int type, int argc, char *argv[])
{
static struct nsctl res;
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
struct hostent *h = gethostbyname(host);
int fd;
char buf[NSCTL_MAX_PKT_SZ];
int sz;
char *err;
if (!h || h->h_addrtype != AF_INET)
{
fprintf(stderr, "%s: invalid host `%s'\n", me, host);
return 0;
}
}
if ((len = read_packet(udpfd, packet)))
if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
{
printf("Received ");
dump_packet(packet, stdout);
fprintf(stderr, "%s: can't create udp socket (%s)\n", me, strerror(errno));
return 0;
}
memset(&peer, 0, len);
peer.sin_family = AF_INET;
peer.sin_port = htons(port);
memcpy(&peer.sin_addr.s_addr, h->h_addr, sizeof(peer.sin_addr.s_addr));
if (connect(fd, (struct sockaddr *) &peer, sizeof(peer)) < 0)
{
fprintf(stderr, "%s: udp connect failed (%s)\n", me, strerror(errno));
return 0;
}
if ((sz = pack_control(buf, sizeof(buf), type, argc, argv)) < 0)
{
fprintf(stderr, "%s: error packing request\n", me);
return 0;
}
if (debug)
{
struct nsctl req;
if (unpack_control(&req, buf, sz) == type)
{
fprintf(stderr, "Sending ");
dump_control(&req, stderr);
}
}
if (send(fd, buf, sz, 0) < 0)
{
fprintf(stderr, "%s: error sending request (%s)\n", me, strerror(errno));
return 0;
}
/* set timer */
if (timeout)
{
struct sigaction alrm;
alrm.sa_handler = sigalrm_handler;
sigemptyset(&alrm.sa_mask);
alrm.sa_flags = 0;
sigaction(SIGALRM, &alrm, 0);
alarm(timeout);
}
sz = recv(fd, buf, sizeof(buf), 0);
alarm(0);
if (sz < 0)
{
fprintf(stderr, "%s: error receiving response (%s)\n", me,
errno == EINTR ? "timed out" : strerror(errno));
return 0;
}
if ((type = unpack_control(&res, buf, sz)) > 0 && type & NSCTL_RESPONSE)
{
if (debug)
{
fprintf(stderr, "Received ");
dump_control(&res, stderr);
}
return &res;
}
err = "unknown error";
switch (type)
{
case NSCTL_ERR_SHORT: err = "short packet"; break;
case NSCTL_ERR_LONG: err = "extra data"; break;
case NSCTL_ERR_MAGIC: err = "bad magic"; break;
case NSCTL_ERR_TYPE: err = "invalid type"; break;
}
fprintf(stderr, "%s: %s\n", me, err);
return 0;
}

View file

@ -1,7 +1,7 @@
#ifndef __PLUGIN_H__
#define __PLUGIN_H__
#define PLUGIN_API_VERSION 2
#define PLUGIN_API_VERSION 3
#define MAX_PLUGIN_TYPES 30
enum
@ -79,17 +79,10 @@ struct param_timer
struct param_control
{
char *buf;
int l;
unsigned int source_ip;
unsigned short source_port;
char *response;
int response_length;
int send_response;
short type;
int id;
char *data;
int data_length;
int argc;
char **argv;
int response;
char *additional;
};
struct param_new_session

View file

@ -4,9 +4,9 @@
/* fudge up session rx speed if not set */
char const *cvs_id = "$Id: setrxspeed.c,v 1.2 2004-11-09 08:05:03 bodea Exp $";
char const *cvs_id = "$Id: setrxspeed.c,v 1.3 2004-11-17 08:23:35 bodea Exp $";
int __plugin_api_version = PLUGIN_API_VERSION;
int plugin_api_version = PLUGIN_API_VERSION;
static struct pluginfuncs *p = 0;
int plugin_post_auth(struct param_post_auth *data)

View file

@ -4,9 +4,9 @@
/* strip domain part of username before sending RADIUS requests */
char const *cvs_id = "$Id: stripdomain.c,v 1.5 2004-11-09 08:05:03 bodea Exp $";
char const *cvs_id = "$Id: stripdomain.c,v 1.6 2004-11-17 08:23:35 bodea Exp $";
int __plugin_api_version = PLUGIN_API_VERSION;
int plugin_api_version = PLUGIN_API_VERSION;
static struct pluginfuncs *p = 0;
int plugin_pre_auth(struct param_pre_auth *data)