- 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 @@
* ? * Wed Nov 17 2004 Brendan O'Dea <bod@optusnet.com.au> 2.0.8
- 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
- Ignore gateway address in Framed-Route (from Jonathan McDowell). - Ignore gateway address in Framed-Route (from Jonathan McDowell).
- Call sessionshutdown() when a tunnel is dropped rather than - Call sessionshutdown() when a tunnel is dropped rather than
sessionkill() to ensure that RADIUS stop records are sent. sessionkill() to ensure that RADIUS stop records are sent.
- Cleanup: make a bunch of global functions/variables static. - 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 * 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) - 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 bindir = /usr/sbin
etcdir = /etc/l2tpns etcdir = /etc/l2tpns
libdir = /usr/lib/l2tpns libdir = /usr/lib/l2tpns
mandir = /usr/share/man/man8 man5dir = /usr/share/man/man5
man8dir = /usr/share/man/man8
statedir = /var/lib/l2tpns statedir = /var/lib/l2tpns
DEFINES = DEFINES =
@ -84,9 +85,14 @@ bounce: test/bounce.o
install: all install: all
$(INSTALL) -m 0755 l2tpns $(DESTDIR)$(bindir)/l2tpns $(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 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 \ @if [ -f $(DESTDIR)$(etcdir)/startup-config ]; then \
echo '***' Installing default config files in $(DESTDIR)$(etcdir) as .defaults; \ echo '***' Installing default config files in $(DESTDIR)$(etcdir) as .defaults; \
suffix=.default; \ 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 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 cluster.o: cluster.c l2tpns.h cluster.h util.h tbf.h bgp.h
constants.o: constants.c constants.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 icmp.o: icmp.c l2tpns.h
l2tpns.o: l2tpns.c md5.h l2tpns.h cluster.h plugin.h ll.h constants.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 control.h util.h tbf.h bgp.h

View file

@ -4,9 +4,9 @@
/* set up intercept based on RADIUS reply */ /* 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; struct pluginfuncs *p;
int plugin_radius_response(struct param_radius_response *data) int plugin_radius_response(struct param_radius_response *data)

View file

@ -4,9 +4,9 @@
/* set up throttling based on RADIUS reply */ /* 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; struct pluginfuncs *p;
#define THROTTLE_KEY "lcp:interface-config" #define THROTTLE_KEY "lcp:interface-config"

195
control.c
View file

@ -1,74 +1,163 @@
// L2TPNS: control // 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 <string.h>
#include <sys/time.h> #include "l2tpns.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 "control.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); if (len > sizeof(pkt))
*(short *)(packet + 2) = ntohs(type); len = sizeof(pkt);
*(int *)(packet + 6) = ntohl(id);
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;
}
/*
* 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;
*(short *)(packet + 4) = ntohs(len); if (len < sz)
return NSCTL_ERR_SHORT;
memset(&addr, 0, sizeof(addr)); if (len > sizeof(pkt))
addr.sin_family = AF_INET; return NSCTL_ERR_LONG;
*(int*)&addr.sin_addr = htonl(dest_ip);
addr.sin_port = htons(dest_port); memcpy(&pkt, data, len);
if (sendto(sockfd, packet, len, 0, (void *) &addr, sizeof(addr)) < 0) 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;
}
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)
{ {
perror("sendto"); if (len < sz + s)
return 0; return NSCTL_ERR_SHORT;
control->argv[i] = p;
p += s;
sz += s;
} }
return 1; else
}
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);
}
void dump_packet(char *packet, FILE *stream)
{
if (htons(*(short *)(packet + 0)) != 0x9012)
{ {
fprintf(stream, "Invalid packet identifier %x\n", htons(*(short *)(packet + 0))); /* check for terminator */
return; if (s != 0xff)
return NSCTL_ERR_SHORT;
} }
fprintf(stream, "Control packet:\n"); }
fprintf(stream, " Type: %d\n", htons(*(short *)(packet + 2)));
fprintf(stream, " Length: %d\n", htons(*(short *)(packet + 4))); if (sz != len)
fprintf(stream, " Identifier: %x\n", htonl(*(int *)(packet + 6))); return NSCTL_ERR_LONG; // trailing cr*p
fprintf(stream, "\n");
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 (%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__ #ifndef __CONTROL_H__
#define __CONTROL_H__ #define __CONTROL_H__
#define PKT_RESP_OK 1 #define NSCTL_PORT 1702
#define PKT_RESP_ERROR 2 #define NSCTL_MAGIC 0x9013
#define PKT_LOAD_PLUGIN 5 /* builtin commands */
#define PKT_UNLOAD_PLUGIN 6 #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 /* general control message, passed to plugins */
#define PKT_UNGARDEN 1001 #define NSCTL_REQ_CONTROL (NSCTL_REQUEST | 4)
int new_packet(short type, char *packet); /* response messages */
int send_packet(int sockfd, int dest_ip, int dest_port, char *packet, int len); #define NSCTL_RESPONSE (1 << 5)
void dump_packet(char *packet, FILE *stream); #define NSCTL_RES_OK (NSCTL_RESPONSE | 1)
int read_packet(int sockfd, char *packet); #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__ */ #endif /* __CONTROL_H__ */

View file

@ -9,9 +9,9 @@
/* walled garden */ /* 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 struct pluginfuncs *p = 0;
static int iam_master = 0; // We're all slaves! Slaves I tell you! 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; 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) int plugin_control(struct param_control *data)
{ {
sessiont *s;
sessionidt session; 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. 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"; data->response = NSCTL_RES_ERR;
*(short *)(data->response + 2) = ntohs(PKT_RESP_ERROR); data->additional = "must be run on the cluster master";
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);
return PLUGIN_RET_STOP; 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"; data->response = NSCTL_RES_ERR;
*(short *)(data->response + 2) = ntohs(PKT_RESP_ERROR); data->additional = "one argument required: username or session id";
sprintf((data->response + data->response_length), "%s", errormsg); return PLUGIN_RET_STOP;
data->response_length += strlen(errormsg) + 1;
} }
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; return PLUGIN_RET_STOP;
} }
@ -136,7 +152,10 @@ int plugin_become_master(void)
int plugin_new_session_master(sessiont * s) int plugin_new_session_master(sessiont * s)
{ {
if (s->walled_garden) if (s->walled_garden)
{
s->walled_garden = 0;
garden_session(s, 1); garden_session(s, 1);
}
return PLUGIN_RET_OK; 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).

256
l2tpns.c
View file

@ -4,7 +4,7 @@
// Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced // Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced
// vim: sw=8 ts=8 // 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 <arpa/inet.h>
#include <assert.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 update_config(void);
static void read_config_file(void); static void read_config_file(void);
static void initplugins(void); static void initplugins(void);
static void add_plugin(char *plugin_name); static int add_plugin(char *plugin_name);
static void remove_plugin(char *plugin_name); static int remove_plugin(char *plugin_name);
static void plugins_done(void); 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 tunnelidt new_tunnel(void);
static int unhide_avp(u8 *avp, tunnelidt t, sessionidt s, u16 length); static int unhide_avp(u8 *avp, tunnelidt t, sessionidt s, u16 length);
@ -434,8 +434,8 @@ static void initudp(void)
// Control // Control
memset(&addr, 0, sizeof(addr)); memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET; addr.sin_family = AF_INET;
addr.sin_port = htons(1702); addr.sin_port = htons(NSCTL_PORT);
controlfd = socket(AF_INET, SOCK_DGRAM, 17); controlfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
setsockopt(controlfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); setsockopt(controlfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
if (bind(controlfd, (void *) &addr, sizeof(addr)) < 0) if (bind(controlfd, (void *) &addr, sizeof(addr)) < 0)
{ {
@ -2324,7 +2324,7 @@ static void mainloop(void)
} }
if (FD_ISSET(controlfd, &r)) 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)) if (FD_ISSET(clifd, &r))
{ {
@ -2934,31 +2934,31 @@ int main(int argc, char *argv[])
{ {
switch (i) switch (i)
{ {
case 'd': case 'd':
if (fork()) exit(0); if (fork()) exit(0);
setsid(); setsid();
freopen("/dev/null", "r", stdin); freopen("/dev/null", "r", stdin);
freopen("/dev/null", "w", stdout); freopen("/dev/null", "w", stdout);
freopen("/dev/null", "w", stderr); freopen("/dev/null", "w", stderr);
break; break;
case 'v': case 'v':
optdebug++; optdebug++;
break; break;
case 'c': case 'c':
optconfig = optarg; optconfig = optarg;
break; break;
case 'h': case 'h':
snprintf(hostname, sizeof(hostname), "%s", optarg); snprintf(hostname, sizeof(hostname), "%s", optarg);
break; break;
default: default:
printf("Args are:\n" printf("Args are:\n"
"\t-d\t\tDetach from terminal\n" "\t-d\t\tDetach from terminal\n"
"\t-c <file>\tConfig file\n" "\t-c <file>\tConfig file\n"
"\t-h <hostname>\tForce hostname\n" "\t-h <hostname>\tForce hostname\n"
"\t-v\t\tDebug\n"); "\t-v\t\tDebug\n");
return (0); return (0);
break; break;
} }
} }
@ -3751,7 +3751,7 @@ static void *getconfig(char *key, enum config_typet type)
return 0; return 0;
} }
static void add_plugin(char *plugin_name) static int add_plugin(char *plugin_name)
{ {
static struct pluginfuncs funcs = { static struct pluginfuncs funcs = {
_log, _log,
@ -3773,22 +3773,22 @@ static void add_plugin(char *plugin_name)
if (!p) if (!p)
{ {
LOG(1, 0, 0, 0, " Plugin load failed: %s\n", dlerror()); LOG(1, 0, 0, 0, " Plugin load failed: %s\n", dlerror());
return; return -1;
} }
if (ll_contains(loaded_plugins, p)) if (ll_contains(loaded_plugins, p))
{ {
dlclose(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) if (!v || *v != PLUGIN_API_VERSION)
{ {
LOG(1, 0, 0, 0, " Plugin load failed: API version mismatch: %s\n", dlerror()); LOG(1, 0, 0, 0, " Plugin load failed: API version mismatch: %s\n", dlerror());
dlclose(p); 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()); LOG(1, 0, 0, 0, " Plugin load failed: plugin_init() returned FALSE: %s\n", dlerror());
dlclose(p); 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); LOG(2, 0, 0, 0, " Loaded plugin %s\n", plugin_name);
return 1;
} }
static void run_plugin_done(void *plugin) static void run_plugin_done(void *plugin)
@ -3825,29 +3826,32 @@ static void run_plugin_done(void *plugin)
donefunc(); donefunc();
} }
static void remove_plugin(char *plugin_name) static int remove_plugin(char *plugin_name)
{ {
void *p = open_plugin(plugin_name, 0); void *p = open_plugin(plugin_name, 0);
int i; int loaded = 0;
if (!p) if (!p)
return; return -1;
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);
}
if (ll_contains(loaded_plugins, p)) 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); ll_delete(loaded_plugins, p);
run_plugin_done(p); run_plugin_done(p);
loaded = 1;
} }
dlclose(p); dlclose(p);
LOG(2, 0, 0, 0, "Removed plugin %s\n", plugin_name); LOG(2, 0, 0, 0, "Removed plugin %s\n", plugin_name);
return loaded;
} }
int run_plugins(int plugin_type, void *data) int run_plugins(int plugin_type, void *data)
@ -3875,39 +3879,149 @@ static void plugins_done()
run_plugin_done(p); 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; struct nsctl request;
int l; struct nsctl response;
struct param_control param = { buf, len, ntohl(addr->sin_addr.s_addr), ntohs(addr->sin_port), NULL, 0, 0 }; int type = unpack_control(&request, buf, len);
int r;
void *p;
if (log_stream && config->debug >= 4) if (log_stream && config->debug >= 4)
{ {
LOG(4, ntohl(addr->sin_addr.s_addr), 0, 0, "Received "); if (type < 0)
dump_packet(buf, log_stream); {
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); switch (type)
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)
{ {
send_packet(controlfd, ntohl(addr->sin_addr.s_addr), ntohs(addr->sin_port), param.response, param.response_length); case NSCTL_REQ_LOAD:
LOG(4, ntohl(addr->sin_addr.s_addr), 0, 0, "Sent Control packet response\n"); 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";
} }
free(resp); 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() static tunnelidt new_tunnel()

View file

@ -39,7 +39,8 @@ rm -rf %{buildroot}
%config(noreplace) /etc/l2tpns/ip_pool %config(noreplace) /etc/l2tpns/ip_pool
%attr(700,root,root) /usr/sbin/l2tpns %attr(700,root,root) /usr/sbin/l2tpns
%attr(755,root,root) /usr/lib/l2tpns %attr(755,root,root) /usr/lib/l2tpns
%attr(644,root,root) /usr/share/man/man[58]/*
%changelog %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 - 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).

325
nsctl.c
View file

@ -1,138 +1,239 @@
/* l2tpns plugin control */
#include <stdio.h> #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 <stdlib.h>
#include <unistd.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" #include "control.h"
struct { char *command; int pkt_type; int params; } commands[] = { struct {
{ "load_plugin", PKT_LOAD_PLUGIN, 1 }, char *command;
{ "unload_plugin", PKT_UNLOAD_PLUGIN, 1 }, char *usage;
{ "garden", PKT_GARDEN, 1 }, int action;
{ "ungarden", PKT_UNGARDEN, 1 }, } 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; static int debug = 0;
int udpfd; 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 main(int argc, char *argv[])
{ {
int len = 0; int req_type = 0;
int dest_ip = 0; char *host = 0;
int pkt_type = 0; int port;
char *packet = NULL; int i;
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]); case 'd':
return 1; debug++;
break;
case 'h':
host = optarg;
break;
case 't':
timeout = atoi(optarg);
break;
default:
USAGE();
return EXIT_FAILURE;
} }
dest_host = strdup(argv[1]); argc -= optind;
argv += optind;
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)
{ {
// Init socket fprintf(stderr, "%s: invalid port `%s'\n", me, p + 1);
int on = 1; return EXIT_FAILURE;
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);
}
} }
*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)
{ {
struct hostent *h = gethostbyname(dest_host); stream = stdout;
if (h) dest_ip = ntohl(*(unsigned int *)h->h_addr); status = EXIT_SUCCESS;
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))) for (i = 0; i < res->argc; i++)
{ fprintf(stream, "%s\n", res->argv[i]);
perror("calloc");
return(1);
}
srand(time(NULL)); return status;
}
// Deal with command & params return EXIT_FAILURE;
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;
}
send_packet(udpfd, dest_ip, dest_port, packet, len);
{
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;
}
}
if ((len = read_packet(udpfd, packet)))
{
printf("Received ");
dump_packet(packet, stdout);
}
return 0;
} }
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)
{
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__ #ifndef __PLUGIN_H__
#define __PLUGIN_H__ #define __PLUGIN_H__
#define PLUGIN_API_VERSION 2 #define PLUGIN_API_VERSION 3
#define MAX_PLUGIN_TYPES 30 #define MAX_PLUGIN_TYPES 30
enum enum
@ -79,17 +79,10 @@ struct param_timer
struct param_control struct param_control
{ {
char *buf; int argc;
int l; char **argv;
unsigned int source_ip; int response;
unsigned short source_port; char *additional;
char *response;
int response_length;
int send_response;
short type;
int id;
char *data;
int data_length;
}; };
struct param_new_session struct param_new_session

View file

@ -4,9 +4,9 @@
/* fudge up session rx speed if not set */ /* 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; static struct pluginfuncs *p = 0;
int plugin_post_auth(struct param_post_auth *data) int plugin_post_auth(struct param_post_auth *data)

View file

@ -4,9 +4,9 @@
/* strip domain part of username before sending RADIUS requests */ /* 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; static struct pluginfuncs *p = 0;
int plugin_pre_auth(struct param_pre_auth *data) int plugin_pre_auth(struct param_pre_auth *data)