- Revise nsctl to allow arbitrary strings/args to be passed to plugins.
-* ?
-- 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)
--- /dev/null
+.\" -*- 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).
--- /dev/null
+.\" -*- 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).
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 =
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; \
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
/* 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)
/* 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"
// L2TPNS: control
-char const *cvs_id_control = "$Id: control.c,v 1.2 2004/06/28 02:43:13 fred_nerk Exp $";
-
-#include <stdio.h>
-#include <arpa/inet.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <malloc.h>
-#include <netdb.h>
+char const *cvs_id_control = "$Id: control.c,v 1.3 2004/11/17 08:23:34 bodea Exp $";
+
#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);
+
+ if (len > sizeof(pkt))
+ len = sizeof(pkt);
+
+ 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
- *(short *)(packet + 0) = ntohs(0x9012);
- *(short *)(packet + 2) = ntohs(type);
- *(int *)(packet + 6) = ntohl(id);
+ if (arg.len)
+ memcpy(arg.value, a, arg.len);
- return 10;
+ memcpy(p, &arg, s);
+ sz += s;
+ p += s;
+ }
+
+ /*
+ * 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
+
+ arg.len = 0xff;
+ memcpy(p, &arg.len, sizeof(arg.len));
+
+ sz += sizeof(arg.len);
+ memcpy(data, &pkt, sz);
+
+ return sz;
}
-int send_packet(int sockfd, int dest_ip, int dest_port, char *packet, int len)
+int unpack_control(struct nsctl *control, char *data, int len)
{
- struct sockaddr_in addr;
+ 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;
- *(short *)(packet + 4) = ntohs(len);
+ default:
+ return NSCTL_ERR_TYPE;
+ }
- 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)
+ control->argc = pkt.argc;
+ for (i = 0; i <= control->argc; i++)
+ {
+ unsigned s;
+
+ if (len < sz + 1)
+ return NSCTL_ERR_SHORT;
+
+ s = (u8) *p;
+ *p++ = 0; // null terminate previous arg
+ sz++;
+
+ if (i < control->argc)
+ {
+ if (len < sz + s)
+ return NSCTL_ERR_SHORT;
+
+ control->argv[i] = p;
+ p += s;
+ sz += s;
+ }
+ else
{
- perror("sendto");
- return 0;
+ /* check for terminator */
+ if (s != 0xff)
+ return NSCTL_ERR_SHORT;
}
- return 1;
-}
+ }
-int read_packet(int sockfd, char *packet)
-{
- struct sockaddr_in addr;
- int alen = sizeof(addr);
- memset(&addr, 0, sizeof(addr));
- return recvfrom(sockfd, packet, 1400, 0, (void *) &addr, &alen);
+ if (sz != len)
+ return NSCTL_ERR_LONG; // trailing cr*p
+
+ return control->type;
}
-void dump_packet(char *packet, FILE *stream)
+void dump_control(struct nsctl *control, FILE *stream)
{
- if (htons(*(short *)(packet + 0)) != 0x9012)
- {
- fprintf(stream, "Invalid packet identifier %x\n", htons(*(short *)(packet + 0)));
- return;
- }
- 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");
-}
+ 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 (%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");
+}
#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__ */
/* 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!
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;
+ {
+ data->response = NSCTL_RES_ERR;
+ data->additional = "must be run on the cluster master";
+ return PLUGIN_RET_STOP;
+ }
- if (data->type != PKT_GARDEN && data->type != PKT_UNGARDEN)
- return PLUGIN_RET_OK;
+ if (data->argc != 2)
+ {
+ data->response = NSCTL_RES_ERR;
+ data->additional = "one argument required: username or session id";
+ return PLUGIN_RET_STOP;
+ }
- if (!data->data && data->data_length)
- return PLUGIN_RET_OK;
+ if (!(session = strtol(data->argv[0], &end, 10)) || *end)
+ session = p->get_session_by_username(data->argv[0]);
- session = atoi((char*)(data->data));
- if (!session)
- return PLUGIN_RET_OK;
+ if (session)
+ s = p->get_session_by_id(session);
- 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 = "session not found";
return PLUGIN_RET_STOP;
}
- *(short *)(data->response + 2) = ntohs(PKT_RESP_OK);
- if (!(garden_session(s, (data->type == PKT_GARDEN))))
+ if (s->walled_garden == flag)
{
- 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 = 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;
}
int plugin_new_session_master(sessiont * s)
{
if (s->walled_garden)
+ {
+ s->walled_garden = 0;
garden_session(s, 1);
+ }
return PLUGIN_RET_OK;
}
+++ /dev/null
-.\" 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).
-
// 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>
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);
// 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)
{
}
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))
{
{
switch (i)
{
- case 'd':
- if (fork()) exit(0);
- setsid();
- freopen("/dev/null", "r", stdin);
- freopen("/dev/null", "w", stdout);
- freopen("/dev/null", "w", stderr);
- break;
- case 'v':
- optdebug++;
- break;
- case 'c':
- optconfig = optarg;
- break;
- case 'h':
- snprintf(hostname, sizeof(hostname), "%s", optarg);
- break;
- default:
- printf("Args are:\n"
- "\t-d\t\tDetach from terminal\n"
- "\t-c <file>\tConfig file\n"
- "\t-h <hostname>\tForce hostname\n"
- "\t-v\t\tDebug\n");
-
- return (0);
- break;
+ case 'd':
+ if (fork()) exit(0);
+ setsid();
+ freopen("/dev/null", "r", stdin);
+ freopen("/dev/null", "w", stdout);
+ freopen("/dev/null", "w", stderr);
+ break;
+ case 'v':
+ optdebug++;
+ break;
+ case 'c':
+ optconfig = optarg;
+ break;
+ case 'h':
+ snprintf(hostname, sizeof(hostname), "%s", optarg);
+ break;
+ default:
+ printf("Args are:\n"
+ "\t-d\t\tDetach from terminal\n"
+ "\t-c <file>\tConfig file\n"
+ "\t-h <hostname>\tForce hostname\n"
+ "\t-v\t\tDebug\n");
+
+ return (0);
+ break;
}
}
return 0;
}
-static void add_plugin(char *plugin_name)
+static int add_plugin(char *plugin_name)
{
static struct pluginfuncs funcs = {
_log,
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;
}
}
{
LOG(1, 0, 0, 0, " Plugin load failed: plugin_init() returned FALSE: %s\n", dlerror());
dlclose(p);
- return;
+ return -1;
}
}
}
LOG(2, 0, 0, 0, " Loaded plugin %s\n", plugin_name);
+ return 1;
}
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;
-
- for (i = 0; i < max_plugin_functions; i++)
- {
- void *x;
- if (plugin_functions[i] && (x = dlsym(p, plugin_functions[i])))
- ll_delete(plugins[i], x);
- }
+ return -1;
if (ll_contains(loaded_plugins, p))
{
+ int i;
+ for (i = 0; i < max_plugin_functions; i++)
+ {
+ void *x;
+ if (plugin_functions[i] && (x = dlsym(p, plugin_functions[i])))
+ ll_delete(plugins[i], x);
+ }
+
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)
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);
+ if (type < 0)
+ {
+ 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);
+ }
}
- resp = calloc(1400, 1);
- l = new_packet(PKT_RESP_ERROR, resp);
- *(int *)(resp + 6) = *(int *)(buf + 6);
+ 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, ¶m))
+ {
+ 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;
+ }
+ }
+ }
- 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;
+ break;
- run_plugins(PLUGIN_CONTROL, ¶m);
+ default:
+ response.type = NSCTL_RES_ERR;
+ response.argc = 1;
+ response.argv[0] = "error unpacking control packet";
+ }
- if (param.send_response)
+ buf = calloc(NSCTL_MAX_PKT_SZ, 1);
+ if (!buf)
{
- 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(2, ntohl(addr->sin_addr.s_addr), 0, 0, "Failed to allocate nsctl response\n");
+ return;
}
- free(resp);
+ 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()
%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
+++ /dev/null
-.\" 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).
-
+/* l2tpns plugin control */
+
#include <stdio.h>
-#include <arpa/inet.h>
+#include <stdlib.h>
+#include <unistd.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 <netdb.h>
#include <signal.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <time.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 i;
+ int req_type = 0;
+ char *host = 0;
+ int port;
+ int i;
+ char *p;
+ struct nsctl *res;
+
+ if ((p = strrchr((me = argv[0]), '/')))
+ me = p + 1;
+
+ opterr = 0;
+ while ((i = getopt(argc, argv, "dh:t:")) != -1)
+ switch (i)
+ {
+ case 'd':
+ debug++;
+ break;
- setbuf(stdout, NULL);
+ case 'h':
+ host = optarg;
+ break;
- if (argc < 3)
- {
- printf("Usage: %s <host> <command> [args...]\n", argv[0]);
- return 1;
+ case 't':
+ timeout = atoi(optarg);
+ break;
+
+ default:
+ USAGE();
+ return EXIT_FAILURE;
}
- dest_host = strdup(argv[1]);
+ argc -= optind;
+ argv += optind;
- {
- // 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);
- }
- }
+ if (argc < 1 || !argv[0][0])
+ {
+ USAGE();
+ return EXIT_FAILURE;
+ }
+
+ if (!host)
+ host = "127.0.0.1";
+ if ((p = strchr(host, ':')))
+ {
+ port = atoi(p + 1);
+ if (!port)
{
- 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;
- }
+ fprintf(stderr, "%s: invalid port `%s'\n", me, p + 1);
+ return EXIT_FAILURE;
}
- if (!(packet = calloc(1400, 1)))
+ *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)
{
- perror("calloc");
- return(1);
+ stream = stdout;
+ status = EXIT_SUCCESS;
}
- srand(time(NULL));
+ for (i = 0; i < res->argc; i++)
+ fprintf(stream, "%s\n", res->argv[i]);
- // 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;
- }
- break;
- }
- }
- if (!pkt_type)
- {
- printf("Unknown command\n");
- return 1;
- }
+ return status;
+ }
+
+ return EXIT_FAILURE;
+}
- send_packet(udpfd, dest_ip, dest_port, packet, len);
+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 ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
+ {
+ 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)
{
- int n;
- fd_set r;
- struct timeval timeout;
-
- FD_ZERO(&r);
- FD_SET(udpfd, &r);
- timeout.tv_sec = 1;
- timeout.tv_usec = 0;
-
- n = select(udpfd + 1, &r, 0, 0, &timeout);
- if (n <= 0)
- {
- printf("Timeout waiting for packet\n");
- return 0;
- }
+ fprintf(stderr, "Sending ");
+ dump_control(&req, stderr);
}
- if ((len = read_packet(udpfd, packet)))
+ }
+
+ 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)
{
- printf("Received ");
- dump_packet(packet, stdout);
+ fprintf(stderr, "Received ");
+ dump_control(&res, stderr);
}
- return 0;
-}
+ 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;
+}
#ifndef __PLUGIN_H__
#define __PLUGIN_H__
-#define PLUGIN_API_VERSION 2
+#define PLUGIN_API_VERSION 3
#define MAX_PLUGIN_TYPES 30
enum
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
/* 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)
/* 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)