From 5e01d2924d4eec8915e93a2cae01217ae25ab4dc Mon Sep 17 00:00:00 2001 From: Brendan O'Dea Date: Wed, 17 Nov 2004 08:23:34 +0000 Subject: [PATCH] - Add startup-config(5) manpage [FIXME]. - Revise nsctl to allow arbitrary strings/args to be passed to plugins. --- Changes | 10 +- Docs/l2tpns.8 | 64 ++++++++++ Docs/nsctl.8 | 67 +++++++++++ Makefile | 14 ++- autosnoop.c | 4 +- autothrottle.c | 4 +- control.c | 195 +++++++++++++++++++++--------- control.h | 56 +++++++-- garden.c | 69 +++++++---- l2tpns.8 | 68 ----------- l2tpns.c | 252 ++++++++++++++++++++++++++++----------- l2tpns.spec | 3 +- nsctl.8 | 41 ------- nsctl.c | 313 ++++++++++++++++++++++++++++++++----------------- plugin.h | 17 +-- setrxspeed.c | 4 +- stripdomain.c | 4 +- 17 files changed, 783 insertions(+), 402 deletions(-) create mode 100644 Docs/l2tpns.8 create mode 100644 Docs/nsctl.8 delete mode 100644 l2tpns.8 delete mode 100644 nsctl.8 diff --git a/Changes b/Changes index e64d7af..dcc3e40 100644 --- a/Changes +++ b/Changes @@ -1,12 +1,12 @@ -* ? -- Add manpages from Jonathan McDowell -- Remove reference to old -a command line argument - -* Tue Nov 16 2004 Brendan O'Dea 2.0.8 +* Wed Nov 17 2004 Brendan O'Dea 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 2.0.7 - Fix socket creation in host_unreachable() (thanks to Bjørn Augestad) diff --git a/Docs/l2tpns.8 b/Docs/l2tpns.8 new file mode 100644 index 0000000..537fa08 --- /dev/null +++ b/Docs/l2tpns.8 @@ -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 , +for the Debian GNU/Linux system (but may be used by others). diff --git a/Docs/nsctl.8 b/Docs/nsctl.8 new file mode 100644 index 0000000..ec2d7df --- /dev/null +++ b/Docs/nsctl.8 @@ -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 , +for the Debian GNU/Linux system (but may be used by others). diff --git a/Makefile b/Makefile index cc3429c..3c203c1 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/autosnoop.c b/autosnoop.c index 910a569..beb8f8d 100644 --- a/autosnoop.c +++ b/autosnoop.c @@ -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) diff --git a/autothrottle.c b/autothrottle.c index 9980011..9973a4f 100644 --- a/autothrottle.c +++ b/autothrottle.c @@ -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" diff --git a/control.c b/control.c index db86e2d..45657d8 100644 --- a/control.c +++ b/control.c @@ -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 $"; - -#include -#include -#include -#include -#include -#include +char const *cvs_id_control = "$Id: control.c,v 1.3 2004-11-17 08:23:34 bodea Exp $"; + #include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#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"); +} diff --git a/control.h b/control.h index 60e0d03..8dd159c 100644 --- a/control.h +++ b/control.h @@ -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__ */ diff --git a/garden.c b/garden.c index 92320a7..ba228cd 100644 --- a/garden.c +++ b/garden.c @@ -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; + { + 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; } @@ -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; } diff --git a/l2tpns.8 b/l2tpns.8 deleted file mode 100644 index 1f6c687..0000000 --- a/l2tpns.8 +++ /dev/null @@ -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 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\fR ] [ \fB-h\fR \fI\fR ] [ \fB-a\fR \fI
\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\fP and -.\" \fI\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 -Specify config file. -.TP -.B \-h -Force hostname to . -.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 , -for the Debian GNU/Linux system (but may be used by others). - diff --git a/l2tpns.c b/l2tpns.c index a6bff01..facecab 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -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 #include @@ -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)) { @@ -2934,31 +2934,31 @@ int main(int argc, char *argv[]) { 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 \tConfig file\n" - "\t-h \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 \tConfig file\n" + "\t-h \tForce hostname\n" + "\t-v\t\tDebug\n"); + + return (0); + break; } } @@ -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,29 +3826,32 @@ 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) @@ -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); + 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() diff --git a/l2tpns.spec b/l2tpns.spec index 79bf046..d6c8f56 100644 --- a/l2tpns.spec +++ b/l2tpns.spec @@ -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 2.0.8-1 +* Wed Nov 17 2004 Brendan O'Dea 2.0.8-1 - 2.0.8 release, see /usr/share/doc/l2tpns-2.0.8/Changes diff --git a/nsctl.8 b/nsctl.8 deleted file mode 100644 index 9d79f56..0000000 --- a/nsctl.8 +++ /dev/null @@ -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 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\fP \fI\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 -The host running l2tpns that should receive the message. -.TP -.B -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 , -for the Debian GNU/Linux system (but may be used by others). - diff --git a/nsctl.c b/nsctl.c index aa6a300..2ad6a7a 100644 --- a/nsctl.c +++ b/nsctl.c @@ -1,138 +1,239 @@ +/* l2tpns plugin control */ + #include -#include +#include +#include #include -#include -#include -#include #include -#include -#include -#include -#include +#include #include -#include -#include -#include -#include + +#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 [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; +} diff --git a/plugin.h b/plugin.h index be92047..b751a1d 100644 --- a/plugin.h +++ b/plugin.h @@ -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 diff --git a/setrxspeed.c b/setrxspeed.c index 9d20915..9842e78 100644 --- a/setrxspeed.c +++ b/setrxspeed.c @@ -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) diff --git a/stripdomain.c b/stripdomain.c index 1c315c8..1013b7b 100644 --- a/stripdomain.c +++ b/stripdomain.c @@ -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) -- 2.20.1