-* Thu Nov 25 2004 Brendan O'Dea <bod@optusnet.com.au> 2.0.9
+* Sat Nov 27 2004 Brendan O'Dea <bod@optusnet.com.au> 2.0.9
- Revise CCP, send ConfigReq once only.
- Don't copy the old buffer into Config{Nak,Rej} LCP responses (oops);
add length checks when appending.
- Have makeppp print a backtrace on overflow.
+- Check control serial before clearing window, prevents looping tunnel
+ setup in some instances.
+- Add configuration syntax for adding named access lists (work in progress).
* Sat Nov 20 2004 Brendan O'Dea <bod@optusnet.com.au> 2.0.8
- Ignore gateway address in Framed-Route (from Jonathan McDowell).
<LI><A HREF="#Interception">Interception</A></LI>
<LI><A HREF="#Authentication">Authentication</A></LI>
<LI><A HREF="#Plugins">Plugins</A></LI>
- <LI><A HREF="#Walled Garden">Walled Garden</A></LI>
+ <LI><A HREF="#WalledGarden">Walled Garden</A></LI>
+ <LI><A HREF="#Filtering">Filtering</A></LI>
<LI><A HREF="#Clustering">Clustering</A></LI>
<LI><A HREF="#Routing">Routing</A></LI>
<LI><A HREF="#Performance">Performance</A></LI>
<LI><B>primary_radius</B> (ip address)
<LI><B>secondary_radius</B> (ip address)<BR>
-Sets the radius servers used for both authentication and accounting.
-If the primary server does not respond, then the secondary radius
-server will be tried.
+Sets the RADIUS servers used for both authentication and accounting.
+If the primary server does not respond, then the secondary RADIUS
+server will be tried.<br>
+<strong>Note:</strong> in addition to the source IP address and
+identifier, the RADIUS server <strong>must</strong> include the source
+port when detecting duplicates to supress (in order to cope with a
+large number of sessions comming on-line simultaneously l2tpns uses a
+set of udp sockets, each with a seperate identifier).
</LI>
<LI><B>primary_radius_port</B> (short)
<LI><B>secondary_radius_port</B> (short)<BR>
-Sets the authentication ports for the primary and secondary radius
+Sets the authentication ports for the primary and secondary RADIUS
servers. The accounting port is one more than the authentication
-port. If no radius ports are given, the authentication port defaults
+port. If no RADIUS ports are given, the authentication port defaults
to 1645, and the accounting port to 1646.
</LI>
<LI><B>radius_accounting</B> (boolean)<BR>
-If set to true, then radius accounting packets will be sent. This
+If set to true, then RADIUS accounting packets will be sent. This
means that a Start record will be sent when the session is
successfully authenticated, and a Stop record will be sent when the
session is closed.
</LI>
<LI><B>radius_secret</B> (string)<BR>
-This secret will be used in all radius queries. If this is not set then
-radius queries will fail.
+This secret will be used in all RADIUS queries. If this is not set then
+RADIUS queries will fail.
</LI>
<LI><B>bind_address</B> (ip address)<BR>
IP address, <I>as</I> is the remote AS number and <I>keepalive</I>,
<I>hold</I> are the timer values in seconds.
+<P>Named access-lists are configured using one of the commands:
+<DL>
+ <DD><B>ip access-list standard</B> <I>name</I>
+ <DD><B>ip access-list extended</B> <I>name</I>
+</DL>
+
+<P>Subsequent lines prefixed with <B>permit</B> or <B>deny</B>
+define the body of the access-list. Standard access-list syntax:
+<DL>
+ <DD>{<B>permit</B>|<B>deny</B>}
+ {<I>host</I>|<I>source source-wildcard</I>|<B>any</B>}
+ [{<I>host</I>|<I>destination destination-wildcard</I>|<B>any</B>}]
+</DL>
+
+Extended access-lists:
+
+<DL>
+ <DD>{<B>permit</B>|<B>deny</B>} <B>ip</B>
+ {<I>host</I>|<I>source source-wildcard</I>|<B>any</B>}
+ {<I>host</I>|<I>destination destination-wildcard</I>|<B>any</B>}
+ <DD>{<B>permit</B>|<B>deny</B>} <B>udp</B>
+ {<I>host</I>|<I>source source-wildcard</I>|<B>any</B>}
+ [{<B>eq</B>|<B>neq</B>|<B>gt</B>|<B>lt</B>} <I>port</I>|<B>range</B> <I>from</I> <I>to</I>]
+ {<I>host</I>|<I>destination destination-wildcard</I>|<B>any</B>}
+ [{<B>eq</B>|<B>neq</B>|<B>gt</B>|<B>lt</B>} <I>port</I>|<B>range</B> <I>from</I> <I>to</I>]
+ <DD>{<B>permit</B>|<B>deny</B>} <B>tcp</B>
+ {<I>host</I>|<I>source source-wildcard</I>|<B>any</B>}
+ [{<B>eq</B>|<B>neq</B>|<B>gt</B>|<B>lt</B>} <I>port</I>|<B>range</B> <I>from</I> <I>to</I>]
+ {<I>host</I>|<I>destination destination-wildcard</I>|<B>any</B>}
+ [{<B>eq</B>|<B>neq</B>|<B>gt</B>|<B>lt</B>} <I>port</I>|<B>range</B> <I>from</I> <I>to</I>]
+ [{<B>established</B>|{<B>match-any</B>|<B>match-all</B>}
+ {<B>+</B>|<B>-</B>}{<B>fin</B>|<B>syn</B>|<B>rst</B>|<B>psh</B>|<B>ack</B>|<B>urg</B>} ...]
+</DL>
+
<H3 ID="users">users</H3>
Usernames and passwords for the command-line interface are stored in
</LI>
<LI><B>show radius</B><BR>
-Show a summary of the in-use radius sessions. This list should not be very
-long, as radius sessions should be cleaned up as soon as they are used. The
+Show a summary of the in-use RADIUS sessions. This list should not be very
+long, as RADIUS sessions should be cleaned up as soon as they are used. The
columns listed are:
<TABLE>
- <TR><TD><B>Radius</B></TD><TD>The ID of the radius request. This is
- sent in the packet to the radius server for identification.</TD></TR>
+ <TR><TD><B>Radius</B></TD><TD>The ID of the RADIUS request. This is
+ sent in the packet to the RADIUS server for identification.</TD></TR>
<TR><TD><B>State</B></TD><TD>The state of the request - WAIT, CHAP,
AUTH, IPCP, START, STOP, NULL.</TD></TR>
- <TR><TD><B>Session</B></TD><TD>The session ID that this radius
+ <TR><TD><B>Session</B></TD><TD>The session ID that this RADIUS
request is associated with</TD></TR>
<TR><TD><B>Retry</B></TD><TD>If a response does not appear to the
request, it will retry at this time. This is a unix timestamp.</TD></TR>
- <TR><TD><B>Try</B></TD><TD>Retry count. The radius request is
+ <TR><TD><B>Try</B></TD><TD>Retry count. The RADIUS request is
discarded after 3 retries.</TD></TR>
</TABLE>
<P>
host/port. Specify <EM>no snoop username</EM> to disable interception
for the session.<P>
-If you want interception to be permanent, you will have to modify the radius
+If you want interception to be permanent, you will have to modify the RADIUS
response for the user. See <A HREF="#Interception">Interception</A>.
<P>
</LI>
for the current session.<P>
If you want throttling to be permanent, you will have to modify the
-radius response for the user. See <A HREF="#THrottling">Throttling</A>.
+RADIUS response for the user. See <A HREF="#Throttling">Throttling</A>.
<P>
</LI>
before this will be activated.<P>
If you wish a session to be throttled permanently, you should set the
-Vendor-Specific radius value <B>Cisco-Avpair="throttle=yes"</B>, which
+Vendor-Specific RADIUS value <B>Cisco-Avpair="throttle=yes"</B>, which
will be handled by the <EM>autothrottle</EM> module.<P>
Otherwise, you can enable and disable throttling an active session using
immediately.<P>
If you wish the user to be intercepted whenever they reconnect, you will
-need to modify the radius response to include the Vendor-Specific value
+need to modify the RADIUS response to include the Vendor-Specific value
<B>Cisco-Avpair="intercept=yes"</B>. For this feature to be enabled,
you need to have the <EM>autosnoop</EM> module loaded.<P>
completed. The remote end must send a PPP CHAP or PPP PAP authentication
request to l2tpns.<P>
-This request is sent to the radius server, which will hopefully respond with
+This request is sent to the RADIUS server, which will hopefully respond with
Auth-Accept or Auth-Reject.<P>
If Auth-Accept is received, the session is set up and an IP address is
-assigned. The radius server can include a Framed-IP-Address field in the
+assigned. The RADIUS server can include a Framed-IP-Address field in the
reply, and that address will be assigned to the client. It can also include
specific DNS servers, and a Framed-Route if that is required.<P>
PPP AUTHACK, but their session is flagged as being a garden'd user, and they
should not receive any service.<P>
-The radius reply can also contain a Vendor-Specific attribute called
+The RADIUS reply can also contain a Vendor-Specific attribute called
Cisco-Avpair. This field is a freeform text field that most Cisco
devices understand to contain configuration instructions for the session. In
the case of l2tpns it is expected to be of the form
<TABLE CELLSPACING=1 CELLPADDING=3>
<TR BGCOLOR=LIGHTGREEN><TH><B>Event</B></TH><TH><B>Description</B></TH><TH><B>Parameters</B></TH></TR>
<TR VALIGN=TOP BGCOLOR=WHITE><TD><B>pre_auth</B></TD>
- <TD>This is called after a radius response has been
+ <TD>This is called after a RADIUS response has been
received, but before it has been processed by the
code. This will allow you to modify the response in
some way.
</TD>
</TR>
<TR VALIGN=TOP BGCOLOR=WHITE><TD><B>post_auth</B></TD>
- <TD>This is called after a radius response has been
+ <TD>This is called after a RADIUS response has been
received, and the basic checks have been performed. This
is what the garden module uses to force authentication
to be accepted.
</TD>
</TR>
<TR VALIGN=TOP BGCOLOR=WHITE><TD><B>radius_response</B></TD>
- <TD>This is called whenever a radius response includes a
+ <TD>This is called whenever a RADIUS response includes a
Cisco-Avpair value. The value is split up into
<EM>key=value</EM> pairs, and each is processed through all
modules.
to sessions that incorrectly authenticate.<P>
Whenever a session provides incorrect authentication, and the
-radius server responds with Auth-Reject, the walled garden module
+RADIUS server responds with Auth-Reject, the walled garden module
(if loaded) will force authentication to succeed, but set the flag
<EM>garden</EM> in the session structure, and adds an iptables rule to
the <B>garden_users</B> chain to force all packets for the session's IP
iptables -t nat -L garden -nvx
</PRE>
+<H2 ID="Filtering">Filtering</H2>
+
+Sessions may be filtered by specifying <B>Filter-Id</B> attributes in
+the RADIUS reply. <I>filter</I>.<B>in</B> specifies that the named
+access-list <I>filter</I> should be applied to traffic from the
+customer, <I>filter</I>.<B>out</B> specifies a list for traffic to the
+customer.
+
<H2 ID="Clustering">Clustering</H2>
An l2tpns cluster consists of of one* or more servers configured with
.de Id
.ds Dt \\$4 \\$5
..
-.Id $Id: startup-config.5,v 1.1 2004/11/17 15:08:19 bodea Exp $
+.Id $Id: startup-config.5,v 1.2 2004/11/27 05:19:54 bodea Exp $
.TH STARTUP-CONFIG 5 "\*(Dt" L2TPNS "File Formats and Conventions"
.SH NAME
startup\-config \- configuration file for l2tpns
.IR keepalive ,
.I hold
are the timer values in seconds.
+.SS NAMED ACCESS LISTS
+Named access lists may be defined with either of
+.IP
+.BI "ip access\-list standard " name
+.br
+.BI "ip access\-list extended " name
+.PP
+Subsequent lines starting with
+.B permit
+or
+.B deny
+define the body of the access\-list.
+.PP
+.B Standard Access Lists
+.RS 4n
+Standard access lists are defined with:
+.IP
+.RB { permit | deny }
+.IR source " [" dest ]
+.PP
+Where
+.I source
+and
+.I dest
+specify IP matches using one of:
+.IP
+.I address
+.I wildard
+.br
+.B host
+.I address
+.br
+.B any
+.PP
+.I address
+and
+.I wildard
+are in dotted-quad notation, bits in the
+.I wildard
+indicate which address bits in
+.I address
+are relevant to the match (0 = exact match; 1 = don't care).
+.PP
+The shorthand
+.RB ' host
+.IR address '
+is equivalent to
+.RI ' address
+.BR 0.0.0.0 ';
+.RB ' any '
+to
+.RB ' 0.0.0.0
+.BR 255.255.255.255 '.
+.RE
+.PP
+.B Extended Access Lists
+.RS 4n
+Extended access lists are defined with:
+.IP
+.RB { permit | deny }
+.I proto
+.IR source " [" ports "] " dest " [" ports "] [" flags ]
+.PP
+Where
+.I proto
+is one of
+.BR ip ,
+.B tcp
+or
+.BR udp ,
+and
+.I source
+and
+.I dest
+are as described above for standard lists.
+.PP
+For
+.B tcp
+and
+.B udp
+matches, source and destination may be optionally followed by a
+.I ports
+specification:
+.IP
+.RB { eq | neq | gt | lt }
+.I port
+.br
+.B
+range
+.I from to
+.PP
+.B tcp
+matches may also specify
+.I flags
+to match against tcp header flags:
+.IP
+.RB { match\-any | match\-all }
+.RB { + | - }{ fin | syn | rst | psh | ack | urg }
+\&...
+.br
+.B established
+.PP
+.RB ' established '
+is shorthand for
+.RB ' "match-any +ack +rst -syn" '.
+.RE
.SH SEE ALSO
.BR l2tpns (8)
// vim: sw=8 ts=8
char const *cvs_name = "$Name: $";
-char const *cvs_id_cli = "$Id: cli.c,v 1.28 2004/11/16 07:54:32 bodea Exp $";
+char const *cvs_id_cli = "$Id: cli.c,v 1.29 2004/11/27 05:19:53 bodea Exp $";
#include <stdio.h>
#include <stdarg.h>
extern ippoolt *ip_address_pool;
extern struct Tstats *_statistics;
static struct cli_def *cli = NULL;
-extern struct configt *config;
-extern struct config_descriptt config_values[];
+extern configt *config;
+extern config_descriptt config_values[];
#ifdef RINGBUFFER
extern struct Tringbuffer *ringbuffer;
#endif
extern struct cli_session_actions *cli_session_actions;
extern struct cli_tunnel_actions *cli_tunnel_actions;
extern tbft *filter_list;
+extern ip_filtert *ip_filters;
static char *debug_levels[] = {
"CRIT",
static int cmd_restart_bgp(struct cli_def *cli, char *command, char **argv, int argc);
#endif /* BGP */
+#define MODE_CONFIG_NACL 9
+static int cmd_ip_access_list(struct cli_def *cli, char *command, char **argv, int argc);
+static int cmd_no_ip_access_list(struct cli_def *cli, char *command, char **argv, int argc);
+static int cmd_ip_access_list_rule(struct cli_def *cli, char *command, char **argv, int argc);
+
+/* match if b is a substr of a */
+#define MATCH(a,b) (!strncmp((a), (b), strlen(b)))
+
void init_cli(char *hostname)
{
FILE *f;
cli_register_command(cli, NULL, "set", cmd_set, PRIVILEGE_PRIVILEGED, MODE_CONFIG, "Set a configuration variable");
+ c = cli_register_command(cli, NULL, "ip", NULL, PRIVILEGE_PRIVILEGED, MODE_CONFIG, NULL);
+ cli_register_command(cli, c, "access-list", cmd_ip_access_list, PRIVILEGE_PRIVILEGED, MODE_CONFIG, "Add named access-list");
+
+ cli_register_command(cli, NULL, "permit", cmd_ip_access_list_rule, PRIVILEGE_PRIVILEGED, MODE_CONFIG_NACL, "Permit rule");
+ cli_register_command(cli, NULL, "deny", cmd_ip_access_list_rule, PRIVILEGE_PRIVILEGED, MODE_CONFIG_NACL, "Deny rule");
+
+ c = cli_register_command(cli, NULL, "no", NULL, PRIVILEGE_UNPRIVILEGED, MODE_CONFIG, NULL);
+ c2 = cli_register_command(cli, c, "ip", NULL, PRIVILEGE_PRIVILEGED, MODE_CONFIG, NULL);
+ cli_register_command(cli, c2, "access-list", cmd_no_ip_access_list, PRIVILEGE_PRIVILEGED, MODE_CONFIG, "Remove named access-list");
+
// Enable regular processing
cli_regular(cli, regular_stuff);
require_auth = addr.sin_addr.s_addr != inet_addr("127.0.0.1");
}
else
- LOG(0, 0, 0, 0, "getpeername() failed on cli socket. Requiring authentication: %s\n", strerror(errno));
+ LOG(0, 0, 0, 0, "getpeername() failed on cli socket. Requiring authentication: %s\n", strerror(errno));
if (require_auth)
{
return CLI_OK;
}
+static char const *show_access_list_rule(int extended, ip_filter_rulet *rule);
+
static int cmd_show_run(struct cli_def *cli, char *command, char **argv, int argc)
{
int i;
#ifdef BGP
if (config->as_number)
{
- int k;
+ int k;
int h;
- cli_print(cli, "# BGP");
+ cli_print(cli, "# BGP");
cli_print(cli, "router bgp %u", config->as_number);
for (i = 0; i < BGP_NUM_PEERS; i++)
{
if (!config->neighbour[i].name[0])
continue;
- cli_print(cli, " neighbour %s remote-as %u", config->neighbour[i].name, config->neighbour[i].as);
+ cli_print(cli, " neighbour %s remote-as %u", config->neighbour[i].name, config->neighbour[i].as);
k = config->neighbour[i].keepalive;
h = config->neighbour[i].hold;
}
#endif
+ cli_print(cli, "# Filters");
+ for (i = 0; i < MAXFILTER; i++)
+ {
+ ip_filter_rulet *rules;
+ if (!*ip_filters[i].name)
+ continue;
+
+ cli_print(cli, "ip access-list %s %s",
+ ip_filters[i].extended ? "extended" : "standard",
+ ip_filters[i].name);
+
+ rules = ip_filters[i].rules;
+ while (rules->action)
+ cli_print(cli, "%s", show_access_list_rule(ip_filters[i].extended, rules++));
+ }
+
cli_print(cli, "# end");
return CLI_OK;
}
return CLI_OK;
}
-static int find_bgp_neighbour(char *name)
+static int find_bgp_neighbour(char const *name)
{
int i;
int new = -1;
for (i = 0; i < BGP_NUM_PEERS; i++)
{
- if (!config->neighbour[i].name[0])
+ if (!config->neighbour[i].name[0])
{
if (new == -1) new = i;
continue;
NULL);
default:
- if (!strncmp("remote-as", argv[1], strlen(argv[1])))
- return cli_arg_help(cli, argv[2][1], "<1-65535>", "Autonomous system number", NULL);
+ if (MATCH("remote-as", argv[1]))
+ return cli_arg_help(cli, argv[2][1], "<1-65535>", "Autonomous system number", NULL);
- if (!strncmp("timers", argv[1], strlen(argv[1])))
+ if (MATCH("timers", argv[1]))
{
- if (argc == 3)
+ if (argc == 3)
return cli_arg_help(cli, 0, "<1-65535>", "Keepalive time", NULL);
if (argc == 4)
return CLI_OK;
}
- if (!strncmp("remote-as", argv[1], strlen(argv[1])))
+ if (MATCH("remote-as", argv[1]))
{
- int as = atoi(argv[2]);
+ int as = atoi(argv[2]);
if (as < 0 || as > 65535)
{
cli_print(cli, "Invalid autonomous system number");
return CLI_OK;
}
- if (argc != 4 || strncmp("timers", argv[1], strlen(argv[1])))
+ if (argc != 4 || !MATCH("timers", argv[1]))
{
cli_print(cli, "Invalid arguments");
return CLI_OK;
}
#endif /* BGP*/
+static int filt;
+static int find_access_list(char const *name)
+{
+ int i;
+
+ for (i = 0; i < MAXFILTER; i++)
+ if (!(*ip_filters[i].name && strcmp(ip_filters[i].name, name)))
+ return i;
+
+ return -1;
+}
+
+static int access_list(struct cli_def *cli, char **argv, int argc, int add)
+{
+ int extended;
+
+ if (CLI_HELP_REQUESTED)
+ {
+ switch (argc)
+ {
+ case 1:
+ return cli_arg_help(cli, 0,
+ "standard", "Standard syntax",
+ "extended", "Extended syntax",
+ NULL);
+
+ case 2:
+ return cli_arg_help(cli, argv[1][1],
+ "NAME", "Access-list name",
+ NULL);
+
+ default:
+ if (argc == 3 && !argv[2][1])
+ return cli_arg_help(cli, 1, NULL);
+
+ return CLI_OK;
+ }
+ }
+
+ if (argc != 2)
+ {
+ cli_print(cli, "Specify access-list type and name");
+ return CLI_OK;
+ }
+
+ if (MATCH("standard", argv[0]))
+ extended = 0;
+ else if (MATCH("extended", argv[0]))
+ extended = 1;
+ else
+ {
+ cli_print(cli, "Invalid access-list type");
+ return CLI_OK;
+ }
+
+ if (strlen(argv[1]) > sizeof(ip_filters[0].name) - 1 ||
+ strspn(argv[1], "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_") != strlen(argv[1]))
+ {
+ cli_print(cli, "Invalid access-list name");
+ return CLI_OK;
+ }
+
+ filt = find_access_list(argv[1]);
+ if (add)
+ {
+ if (filt < 0)
+ {
+ cli_print(cli, "Too many access-lists");
+ return CLI_OK;
+ }
+
+ // racy
+ if (!*ip_filters[filt].name)
+ {
+ memset(&ip_filters[filt], 0, sizeof(ip_filters[filt]));
+ strcpy(ip_filters[filt].name, argv[1]);
+ ip_filters[filt].extended = extended;
+ }
+ else if (ip_filters[filt].extended != extended)
+ {
+ cli_print(cli, "Access-list is %s",
+ ip_filters[filt].extended ? "extended" : "standard");
+
+ return CLI_OK;
+ }
+
+ cli_set_configmode(cli, MODE_CONFIG_NACL, extended ? "ext-nacl" : "std-nacl");
+ return CLI_OK;
+ }
+
+ if (filt < 0 || !*ip_filters[filt].name)
+ {
+ cli_print(cli, "Access-list not defined");
+ return CLI_OK;
+ }
+
+ // racy
+ if (ip_filters[filt].used)
+ {
+ cli_print(cli, "Access-list in use");
+ return CLI_OK;
+ }
+
+ memset(&ip_filters[filt], 0, sizeof(ip_filters[filt]));
+ return CLI_OK;
+}
+
+static int cmd_ip_access_list(struct cli_def *cli, char *command, char **argv, int argc)
+{
+ return access_list(cli, argv, argc, 1);
+}
+
+static int cmd_no_ip_access_list(struct cli_def *cli, char *command, char **argv, int argc)
+{
+ return access_list(cli, argv, argc, 0);
+}
+
+static int show_ip_wild(char *buf, ipt ip, ipt wild)
+{
+ int i;
+ if (ip == INADDR_ANY && wild == INADDR_BROADCAST)
+ return sprintf(buf, " any");
+
+ if (wild == INADDR_ANY)
+ return sprintf(buf, " host %s", inet_toa(ip));
+
+ i = sprintf(buf, " %s", inet_toa(ip));
+ return i + sprintf(buf + i, " %s", inet_toa(wild));
+}
+
+static int show_ports(char *buf, ip_filter_portt *ports)
+{
+ switch (ports->op)
+ {
+ case FILTER_PORT_OP_EQ: return sprintf(buf, " eq %u", ports->port);
+ case FILTER_PORT_OP_NEQ: return sprintf(buf, " neq %u", ports->port);
+ case FILTER_PORT_OP_GT: return sprintf(buf, " gt %u", ports->port);
+ case FILTER_PORT_OP_LT: return sprintf(buf, " lt %u", ports->port);
+ case FILTER_PORT_OP_RANGE: return sprintf(buf, " range %u %u", ports->port, ports->port2);
+ }
+
+ return 0;
+}
+
+static char const *show_access_list_rule(int extended, ip_filter_rulet *rule)
+{
+ static char buf[256];
+ char *p = buf;
+
+ p += sprintf(p, " %s", rule->action == FILTER_ACTION_PERMIT ? "permit" : "deny");
+ if (extended)
+ {
+ struct protoent *proto = getprotobynumber(rule->proto);
+ p += sprintf(p, " %s", proto ? proto->p_name : "ERR");
+ }
+
+ p += show_ip_wild(p, rule->src_ip, rule->src_wild);
+ if (!extended)
+ return buf;
+
+ if (rule->proto == IPPROTO_TCP || rule->proto == IPPROTO_UDP)
+ p += show_ports(p, &rule->src_ports);
+
+ p += show_ip_wild(p, rule->dst_ip, rule->dst_wild);
+ if (rule->proto == IPPROTO_TCP || rule->proto == IPPROTO_UDP)
+ p += show_ports(p, &rule->dst_ports);
+
+ if (rule->proto == IPPROTO_TCP && (rule->tcp_sflags || rule->tcp_cflags))
+ {
+ if (rule->tcp_flag_op == FILTER_FLAG_OP_ANY &&
+ rule->tcp_sflags == (TCP_FLAG_ACK|TCP_FLAG_FIN) &&
+ rule->tcp_cflags == TCP_FLAG_SYN)
+ {
+ p += sprintf(p, " established");
+ }
+ else
+ {
+ p += sprintf(p, " match-%s", rule->tcp_flag_op == FILTER_FLAG_OP_ALL ? "all" : "any");
+ if (rule->tcp_sflags & TCP_FLAG_FIN) p += sprintf(p, " +fin");
+ if (rule->tcp_cflags & TCP_FLAG_FIN) p += sprintf(p, " -fin");
+ if (rule->tcp_sflags & TCP_FLAG_SYN) p += sprintf(p, " +syn");
+ if (rule->tcp_cflags & TCP_FLAG_SYN) p += sprintf(p, " -syn");
+ if (rule->tcp_sflags & TCP_FLAG_RST) p += sprintf(p, " +rst");
+ if (rule->tcp_cflags & TCP_FLAG_RST) p += sprintf(p, " -rst");
+ if (rule->tcp_sflags & TCP_FLAG_PSH) p += sprintf(p, " +psh");
+ if (rule->tcp_cflags & TCP_FLAG_PSH) p += sprintf(p, " -psh");
+ if (rule->tcp_sflags & TCP_FLAG_ACK) p += sprintf(p, " +ack");
+ if (rule->tcp_cflags & TCP_FLAG_ACK) p += sprintf(p, " -ack");
+ if (rule->tcp_sflags & TCP_FLAG_URG) p += sprintf(p, " +urg");
+ if (rule->tcp_cflags & TCP_FLAG_URG) p += sprintf(p, " -urg");
+ }
+ }
+
+ return buf;
+}
+
+ip_filter_rulet *access_list_rule_ext(struct cli_def *cli, char *command, char **argv, int argc)
+{
+ static ip_filter_rulet rule;
+ struct in_addr addr;
+ int i;
+ int a;
+
+ if (CLI_HELP_REQUESTED)
+ {
+ if (argc == 1)
+ {
+ cli_arg_help(cli, 0,
+ "ip", "Match IP packets",
+ "tcp", "Match TCP packets",
+ "udp", "Match UDP packets",
+ NULL);
+
+ return NULL;
+ }
+
+ // *sigh*, too darned complex
+ cli_arg_help(cli, 0, "RULE", "SOURCE [PORTS] DEST [PORTS] FLAGS", NULL);
+ return NULL;
+ }
+
+ if (argc < 3)
+ {
+ cli_print(cli, "Specify rule details");
+ return NULL;
+ }
+
+ memset(&rule, 0, sizeof(rule));
+ rule.action = (command[0] == 'p')
+ ? FILTER_ACTION_PERMIT
+ : FILTER_ACTION_DENY;
+
+ if (MATCH("ip", argv[0]))
+ rule.proto = IPPROTO_IP;
+ else if (MATCH("udp", argv[0]))
+ rule.proto = IPPROTO_UDP;
+ else if (MATCH("tcp", argv[0]))
+ rule.proto = IPPROTO_TCP;
+ else
+ {
+ cli_print(cli, "Invalid protocol \"%s\"", argv[0]);
+ return NULL;
+ }
+
+ for (a = 1, i = 0; i < 2; i++)
+ {
+ ipt *ip;
+ ipt *wild;
+ ip_filter_portt *port;
+
+ if (i == 0)
+ {
+ ip = &rule.src_ip;
+ wild = &rule.src_wild;
+ port = &rule.src_ports;
+ }
+ else
+ {
+ ip = &rule.dst_ip;
+ wild = &rule.dst_wild;
+ port = &rule.dst_ports;
+ if (a >= argc)
+ {
+ cli_print(cli, "Specify destination");
+ return NULL;
+ }
+ }
+
+ if (MATCH("any", argv[a]))
+ {
+ *ip = INADDR_ANY;
+ *wild = INADDR_BROADCAST;
+ a++;
+ }
+ else if (MATCH("host", argv[a]))
+ {
+ if (++a >= argc)
+ {
+ cli_print(cli, "Specify host ip address");
+ return NULL;
+ }
+
+ if (!inet_aton(argv[a], &addr))
+ {
+ cli_print(cli, "Cannot parse IP \"%s\"", argv[a]);
+ return NULL;
+ }
+
+ *ip = addr.s_addr;
+ *wild = INADDR_ANY;
+ a++;
+ }
+ else
+ {
+ if (++a >= argc)
+ {
+ cli_print(cli, "Specify %s ip address and wildcard", i ? "destination" : "source");
+ return NULL;
+ }
+
+ if (!inet_aton(argv[a], &addr))
+ {
+ cli_print(cli, "Cannot parse IP \"%s\"", argv[a]);
+ return NULL;
+ }
+
+ *ip = addr.s_addr;
+
+ if (!inet_aton(argv[++a], &addr))
+ {
+ cli_print(cli, "Cannot parse IP \"%s\"", argv[a]);
+ return NULL;
+ }
+
+ *wild = addr.s_addr;
+ }
+
+ if (rule.proto == IPPROTO_IP || a >= argc)
+ continue;
+
+ port->op = 0;
+ if (MATCH("eq", argv[a]))
+ port->op = FILTER_PORT_OP_EQ;
+ else if (MATCH("neq", argv[a]))
+ port->op = FILTER_PORT_OP_NEQ;
+ else if (MATCH("gt", argv[a]))
+ port->op = FILTER_PORT_OP_GT;
+ else if (MATCH("lt", argv[a]))
+ port->op = FILTER_PORT_OP_LT;
+ else if (MATCH("range", argv[a]))
+ port->op = FILTER_PORT_OP_RANGE;
+
+ if (!port->op)
+ continue;
+
+ if (++a >= argc)
+ {
+ cli_print(cli, "Specify port");
+ return NULL;
+ }
+
+ if (!(port->port = atoi(argv[a])))
+ {
+ cli_print(cli, "Invalid port \"%s\"", argv[a]);
+ return NULL;
+ }
+
+ a++;
+ if (port->op != FILTER_PORT_OP_RANGE)
+ continue;
+
+ if (a >= argc)
+ {
+ cli_print(cli, "Specify port");
+ return NULL;
+ }
+
+ if (!(port->port2 = atoi(argv[a])) || port->port2 < port->port)
+ {
+ cli_print(cli, "Invalid port \"%s\"", argv[a]);
+ return NULL;
+ }
+
+ a++;
+ }
+
+ if (rule.proto == IPPROTO_TCP && a < argc)
+ {
+ if (MATCH("established", argv[a]))
+ {
+ rule.tcp_flag_op = FILTER_FLAG_OP_ANY;
+ rule.tcp_sflags = (TCP_FLAG_ACK|TCP_FLAG_FIN);
+ rule.tcp_cflags = TCP_FLAG_SYN;
+ a++;
+ }
+ else if (!strcmp(argv[a], "match-any") || !strcmp(argv[a], "match-an") ||
+ !strcmp(argv[a], "match-all") || !strcmp(argv[a], "match-al"))
+ {
+ rule.tcp_flag_op = argv[a][7] == 'n'
+ ? FILTER_FLAG_OP_ANY
+ : FILTER_FLAG_OP_ALL;
+
+ if (++a >= argc)
+ {
+ cli_print(cli, "Specify tcp flags");
+ return NULL;
+ }
+
+ while (a < argc && (argv[a][0] == '+' || argv[a][0] == '-'))
+ {
+ u8 *f;
+
+ f = (argv[a][0] == '+') ? &rule.tcp_sflags : &rule.tcp_cflags;
+
+ if (MATCH("fin", &argv[a][1])) *f |= TCP_FLAG_FIN;
+ else if (MATCH("syn", &argv[a][1])) *f |= TCP_FLAG_SYN;
+ else if (MATCH("rst", &argv[a][1])) *f |= TCP_FLAG_RST;
+ else if (MATCH("psh", &argv[a][1])) *f |= TCP_FLAG_PSH;
+ else if (MATCH("ack", &argv[a][1])) *f |= TCP_FLAG_ACK;
+ else if (MATCH("urg", &argv[a][1])) *f |= TCP_FLAG_URG;
+ else
+ {
+ cli_print(cli, "Invalid tcp flag \"%s\"", argv[a]);
+ return NULL;
+ }
+
+ a++;
+ }
+ }
+ }
+
+ if (a < argc)
+ {
+ cli_print(cli, "Invalid flag \"%s\"", argv[a]);
+ return NULL;
+ }
+
+ return &rule;
+}
+
+ip_filter_rulet *access_list_rule_std(struct cli_def *cli, char *command, char **argv, int argc)
+{
+ static ip_filter_rulet rule;
+ struct in_addr addr;
+
+ if (CLI_HELP_REQUESTED)
+ {
+ if (argc == 1)
+ {
+ cli_arg_help(cli, argv[0][1],
+ "A.B.C.D", "Source address",
+ "any", "Any source address",
+ "host", "Source host",
+ NULL);
+
+ return NULL;
+ }
+
+ if (MATCH("any", argv[0]))
+ {
+ if (argc == 2 && !argv[1][1])
+ cli_arg_help(cli, 1, NULL);
+ }
+ else if (MATCH("host", argv[0]))
+ {
+ if (argc == 2)
+ {
+ cli_arg_help(cli, argv[1][1],
+ "A.B.C.D", "Host address",
+ NULL);
+ }
+ else if (argc == 3 && !argv[2][1])
+ cli_arg_help(cli, 1, NULL);
+ }
+ else
+ {
+ if (argc == 2)
+ {
+ cli_arg_help(cli, 1,
+ "A.B.C.D", "Wildcard bits",
+ NULL);
+ }
+ else if (argc == 3 && !argv[2][1])
+ cli_arg_help(cli, 1, NULL);
+ }
+
+ return NULL;
+ }
+
+ if (argc < 1)
+ {
+ cli_print(cli, "Specify rule details");
+ return NULL;
+ }
+
+ memset(&rule, 0, sizeof(rule));
+ rule.action = (command[0] == 'p')
+ ? FILTER_ACTION_PERMIT
+ : FILTER_ACTION_DENY;
+
+ rule.proto = IPPROTO_IP;
+ if (MATCH("any", argv[0]))
+ {
+ rule.src_ip = INADDR_ANY;
+ rule.src_wild = INADDR_BROADCAST;
+ }
+ else if (MATCH("host", argv[0]))
+ {
+ if (argc != 2)
+ {
+ cli_print(cli, "Specify host ip address");
+ return NULL;
+ }
+
+ if (!inet_aton(argv[1], &addr))
+ {
+ cli_print(cli, "Cannot parse IP \"%s\"", argv[1]);
+ return NULL;
+ }
+
+ rule.src_ip = addr.s_addr;
+ rule.src_wild = INADDR_ANY;
+ }
+ else
+ {
+ if (argc > 2)
+ {
+ cli_print(cli, "Specify source ip address and wildcard");
+ return NULL;
+ }
+
+ if (!inet_aton(argv[0], &addr))
+ {
+ cli_print(cli, "Cannot parse IP \"%s\"", argv[0]);
+ return NULL;
+ }
+
+ rule.src_ip = addr.s_addr;
+
+ if (argc > 1)
+ {
+ if (!inet_aton(argv[1], &addr))
+ {
+ cli_print(cli, "Cannot parse IP \"%s\"", argv[1]);
+ return NULL;
+ }
+
+ rule.src_wild = addr.s_addr;
+ }
+ else
+ rule.src_wild = INADDR_ANY;
+ }
+
+ return &rule;
+}
+
+static int cmd_ip_access_list_rule(struct cli_def *cli, char *command, char **argv, int argc)
+{
+ int i;
+ ip_filter_rulet *rule = ip_filters[filt].extended
+ ? access_list_rule_ext(cli, command, argv, argc)
+ : access_list_rule_std(cli, command, argv, argc);
+
+ if (!rule)
+ return CLI_OK;
+
+ for (i = 0; i < MAXFILTER_RULES - 1; i++) // -1: list always terminated by empty rule
+ {
+ if (!ip_filters[filt].rules[i].action)
+ {
+ memcpy(&ip_filters[filt].rules[i], rule, sizeof(*rule));
+ return CLI_OK;
+ }
+
+ if (!memcmp(&ip_filters[filt].rules[i], rule, sizeof(*rule)))
+ return CLI_OK;
+ }
+
+ cli_print(cli, "Too many rules");
+ return CLI_OK;
+}
+
// Convert a string in the form of abcd.ef12.3456 into char[6]
void parsemac(char *string, char mac[6])
{
// 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.56 2004/11/25 02:49:18 bodea Exp $";
+char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.57 2004/11/27 05:19:53 bodea Exp $";
#include <arpa/inet.h>
#include <assert.h>
#endif /* BGP */
// Globals
-struct configt *config = NULL; // all configuration
+configt *config = NULL; // all configuration
int tunfd = -1; // tun interface file handle. (network device)
int udpfd = -1; // UDP file handle
int controlfd = -1; // Control signal handle
linked_list *plugins[MAX_PLUGIN_TYPES];
#define membersize(STRUCT, MEMBER) sizeof(((STRUCT *)0)->MEMBER)
-#define CONFIG(NAME, MEMBER, TYPE) { NAME, offsetof(struct configt, MEMBER), membersize(struct configt, MEMBER), TYPE }
+#define CONFIG(NAME, MEMBER, TYPE) { NAME, offsetof(configt, MEMBER), membersize(configt, MEMBER), TYPE }
-struct config_descriptt config_values[] = {
+config_descriptt config_values[] = {
CONFIG("debug", debug, INT),
CONFIG("log_file", log_filename, STRING),
CONFIG("pid_file", pid_file, STRING),
sessioncountt *sess_count = NULL; // Array of partial per-session traffic counters.
radiust *radius = NULL; // Array of radius structures.
ippoolt *ip_address_pool = NULL; // Array of dynamic IP addresses.
+ip_filtert *ip_filters = NULL; // Array of named filters.
static controlt *controlfree = 0;
struct Tstats *_statistics = NULL;
#ifdef RINGBUFFER
tunnel[t].controle->next = c;
else
tunnel[t].controls = c;
+
tunnel[t].controle = c;
tunnel[t].controlc++;
+
// send now if space in window
if (tunnel[t].controlc <= tunnel[t].window)
{
STAT(tunnel_rx_errors);
return;
}
- LOG(3, ntohl(addr->sin_addr.s_addr), s, t, "Control message (%d bytes): (unacked %d) l-ns %d l-nr %d r-ns %d r-nr %d\n",
- l, tunnel[t].controlc, tunnel[t].ns, tunnel[t].nr, ns, nr);
- // if no tunnel specified, assign one
- if (!t)
+
+ // check for duplicate tunnel open message
+ if (!t && ns == 0)
{
int i;
tunnel[i].port != ntohs(addr->sin_port) )
continue;
t = i;
+ LOG(3, ntohl(addr->sin_addr.s_addr), s, t, "Duplicate SCCRQ?\n");
break;
}
}
+ LOG(3, ntohl(addr->sin_addr.s_addr), s, t, "Control message (%d bytes): (unacked %d) l-ns %d l-nr %d r-ns %d r-nr %d\n",
+ l, tunnel[t].controlc, tunnel[t].ns, tunnel[t].nr, ns, nr);
+
+ // if no tunnel specified, assign one
if (!t)
{
if (!(t = new_tunnel()))
tunnel[t].ip = ntohl(*(ipt *) & addr->sin_addr);
tunnel[t].port = ntohs(addr->sin_port);
tunnel[t].window = 4; // default window
- LOG(1, ntohl(addr->sin_addr.s_addr), 0, t, " New tunnel from %u.%u.%u.%u/%u ID %d\n", tunnel[t].ip >> 24, tunnel[t].ip >> 16 & 255, tunnel[t].ip >> 8 & 255, tunnel[t].ip & 255, tunnel[t].port, t);
STAT(tunnel_created);
+ LOG(1, ntohl(addr->sin_addr.s_addr), 0, t, " New tunnel from %u.%u.%u.%u/%u ID %d\n",
+ tunnel[t].ip >> 24, tunnel[t].ip >> 16 & 255,
+ tunnel[t].ip >> 8 & 255, tunnel[t].ip & 255, tunnel[t].port, t);
+ }
+
+ // If the 'ns' just received is not the 'nr' we're
+ // expecting, just send an ack and drop it.
+ //
+ // if 'ns' is less, then we got a retransmitted packet.
+ // if 'ns' is greater than missed a packet. Either way
+ // we should ignore it.
+ if (ns != tunnel[t].nr)
+ {
+ // is this the sequence we were expecting?
+ STAT(tunnel_rx_errors);
+ LOG(1, ntohl(addr->sin_addr.s_addr), 0, t, " Out of sequence tunnel %d, (%d is not the expected %d)\n",
+ t, ns, tunnel[t].nr);
+
+ if (l) // Is this not a ZLB?
+ controlnull(t);
+ return;
}
// This is used to time out old tunnels
{
int skip = tunnel[t].window; // track how many in-window packets are still in queue
// some to clear maybe?
- while (tunnel[t].controlc && (((tunnel[t].ns - tunnel[t].controlc) - nr) & 0x8000))
+ while (tunnel[t].controlc > 0 && (((tunnel[t].ns - tunnel[t].controlc) - nr) & 0x8000))
{
controlt *c = tunnel[t].controls;
tunnel[t].controls = c->next;
tunnel[t].try = 0; // we have progress
}
- // If the 'ns' just received is not the 'nr' we're
- // expecting, just send an ack and drop it.
- //
- // if 'ns' is less, then we got a retransmitted packet.
- // if 'ns' is greater than missed a packet. Either way
- // we should ignore it.
- if (ns != tunnel[t].nr)
- {
- // is this the sequence we were expecting?
- LOG(1, ntohl(addr->sin_addr.s_addr), 0, t, " Out of sequence tunnel %d, (%d is not the expected %d)\n", t, ns, tunnel[t].nr);
- STAT(tunnel_rx_errors);
-
- if (l) // Is this not a ZLB?
- controlnull(t);
- return;
- }
// receiver advance (do here so quoted correctly in any sends below)
if (l) tunnel[t].nr = (ns + 1);
if (skip < 0) skip = 0;
LOG(0, 0, 0, 0, "Error doing malloc for _statistics: %s\n", strerror(errno));
exit(1);
}
- if (!(config = shared_malloc(sizeof(struct configt))))
+ if (!(config = shared_malloc(sizeof(configt))))
{
LOG(0, 0, 0, 0, "Error doing malloc for configuration: %s\n", strerror(errno));
exit(1);
}
- memset(config, 0, sizeof(struct configt));
+ memset(config, 0, sizeof(configt));
time(&config->start_time);
strncpy(config->config_file, optconfig, strlen(optconfig));
config->debug = optdebug;
exit(1);
}
+if (!(ip_filters = shared_malloc(sizeof(ip_filtert) * MAXFILTER)))
+{
+ LOG(0, 0, 0, 0, "Error doing malloc for ip_filters: %s\n", strerror(errno));
+ exit(1);
+}
+memset(ip_filters, 0, sizeof(ip_filtert) * MAXFILTER);
+
#ifdef RINGBUFFER
if (!(ringbuffer = shared_malloc(sizeof(struct Tringbuffer))))
{
_statistics->start_time = _statistics->last_reset = time(NULL);
#ifdef BGP
- if (!(bgp_peers = shared_malloc(sizeof(struct bgp_peer) * BGP_NUM_PEERS)))
- {
- LOG(0, 0, 0, 0, "Error doing malloc for bgp: %s\n", strerror(errno));
- exit(1);
- }
+ if (!(bgp_peers = shared_malloc(sizeof(struct bgp_peer) * BGP_NUM_PEERS)))
+ {
+ LOG(0, 0, 0, 0, "Error doing malloc for bgp: %s\n", strerror(errno));
+ exit(1);
+ }
#endif /* BGP */
}
// L2TPNS Global Stuff
-// $Id: l2tpns.h,v 1.37 2004/11/25 12:41:35 bodea Exp $
+// $Id: l2tpns.h,v 1.38 2004/11/27 05:19:53 bodea Exp $
#ifndef __L2TPNS_H__
#define __L2TPNS_H__
#define DUMP_MAGIC "L2TPNS#" VERSION "#"
// structures
-typedef struct routes // route
+typedef struct // route
{
ipt ip;
ipt mask;
}
controlt;
-typedef struct sessions
+typedef struct
{
sessionidt next; // next session in linked list
sessionidt far; // far end session ID
ipt snoop_ip; // Interception destination IP
u16 snoop_port; // Interception destination port
u16 sid; // near end session id.
- char reserved[20]; // Space to expand structure without changing HB_VERSION
+ u8 filter_in; // input filter index (to ip_filters[N-1]; 0 if none)
+ u8 filter_out; // output filter index
+ char reserved[18]; // Space to expand structure without changing HB_VERSION
}
sessiont;
#define SF_LCP_ACKED 2 // LCP negotiated
#define SF_CCP_ACKED 4 // CCP negotiated
-typedef struct {
+typedef struct
+{
u32 cin;
u32 cout;
} sessioncountt;
#define SESSIONACFC 2 // ACFC negotiated flags
// 168 bytes per tunnel
-typedef struct tunnels
+typedef struct
{
tunnelidt far; // far end tunnel ID
ipt ip; // Ip for far end
tunnelt;
// 180 bytes per radius session
-typedef struct radiuss // outstanding RADIUS requests
+typedef struct // outstanding RADIUS requests
{
sessionidt session; // which session this applies to
hasht auth; // request authenticator
#define SET_STAT(x, y)
#endif
-struct configt
+typedef struct
{
int debug; // debugging level
time_t start_time; // time when l2tpns was started
int hold;
} neighbour[BGP_NUM_PEERS];
#endif
-};
+} configt;
enum config_typet { INT, STRING, UNSIGNED_LONG, SHORT, BOOL, IP, MAC };
-struct config_descriptt
+typedef struct
{
char *key;
int offset;
int size;
enum config_typet type;
-};
+} config_descriptt;
+
+typedef struct
+{
+ u8 op; // operation
+#define FILTER_PORT_OP_NONE 0 // all ports match
+#define FILTER_PORT_OP_EQ 1
+#define FILTER_PORT_OP_NEQ 2
+#define FILTER_PORT_OP_GT 3
+#define FILTER_PORT_OP_LT 4
+#define FILTER_PORT_OP_RANGE 5
+ portt port;
+ portt port2; // for range
+} ip_filter_portt;
+
+typedef struct
+{
+ int action; // permit/deny
+#define FILTER_ACTION_DENY 1
+#define FILTER_ACTION_PERMIT 2
+ int proto; // protocol: IPPROTO_* (netinet/in.h)
+ ipt src_ip; // source ip
+ ipt src_wild;
+ ip_filter_portt src_ports;
+ ipt dst_ip; // dest ip
+ ipt dst_wild;
+ ip_filter_portt dst_ports;
+ u8 tcp_flag_op; // match type: any, all
+#define FILTER_FLAG_OP_ANY 0
+#define FILTER_FLAG_OP_ALL 1
+ u8 tcp_sflags; // flags set
+ u8 tcp_cflags; // flags clear
+} ip_filter_rulet;
+
+#define TCP_FLAG_FIN 0x01
+#define TCP_FLAG_SYN 0x02
+#define TCP_FLAG_RST 0x04
+#define TCP_FLAG_PSH 0x08
+#define TCP_FLAG_ACK 0x10
+#define TCP_FLAG_URG 0x20
+
+#define MAXFILTER 32
+#define MAXFILTER_RULES 32
+typedef struct
+{
+ char name[32]; // ACL name
+ int extended; // type: 0 = standard, 1 = extended
+ ip_filter_rulet rules[MAXFILTER_RULES];
+ int used; // session ref count
+} ip_filtert;
// arp.c
void sendarp(int ifr_idx, const unsigned char* mac, ipt ip);
}
-extern struct configt *config;
+extern configt *config;
extern time_t basetime; // Time when this process started.
extern time_t time_now; // Seconds since EPOCH.
extern u32 last_id;
%attr(644,root,root) /usr/share/man/man[58]/*
%changelog
-* Thu Nov 25 2004 Brendan O'Dea <bod@optusnet.com.au> 2.0.9-1
+* Sat Nov 27 2004 Brendan O'Dea <bod@optusnet.com.au> 2.0.9-1
- 2.0.9 release, see /usr/share/doc/l2tpns-2.0.9/Changes
// L2TPNS PPP Stuff
-char const *cvs_id_ppp = "$Id: ppp.c,v 1.30 2004/11/25 12:46:48 bodea Exp $";
+char const *cvs_id_ppp = "$Id: ppp.c,v 1.31 2004/11/27 05:19:53 bodea Exp $";
#include <stdio.h>
#include <string.h>
extern char hostname[];
extern u32 eth_tx;
extern time_t time_now;
-extern struct configt *config;
+extern configt *config;
static void initccp(tunnelidt t, sessionidt s);
// L2TPNS Radius Stuff
-char const *cvs_id_radius = "$Id: radius.c,v 1.13 2004/11/25 02:45:27 bodea Exp $";
+char const *cvs_id_radius = "$Id: radius.c,v 1.14 2004/11/27 05:19:53 bodea Exp $";
#include <time.h>
#include <stdio.h>
extern sessiont *session;
extern tunnelt *tunnel;
extern u32 sessionid;
-extern struct configt *config;
+extern configt *config;
extern int *radfds;
static const char *radius_state(int state)