- Revise CCP, send ConfigReq once only.
authorbodea <bodea>
Sat, 27 Nov 2004 05:19:53 +0000 (05:19 +0000)
committerbodea <bodea>
Sat, 27 Nov 2004 05:19:53 +0000 (05:19 +0000)
- Check control serial before clearing window, prevents looping tunnel
  setup in some instances.
- Add configuration syntax for adding named access lists (work in progress).

Changes
Docs/manual.html
Docs/startup-config.5
cli.c
l2tpns.c
l2tpns.h
l2tpns.spec
ppp.c
radius.c

diff --git a/Changes b/Changes
index 59dde80..1d2bdcd 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,8 +1,11 @@
-* 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.
 - 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).
 
 * Sat Nov 20 2004 Brendan O'Dea <bod@optusnet.com.au> 2.0.8
 - Ignore gateway address in Framed-Route (from Jonathan McDowell).
index 08d9408..13748a9 100644 (file)
@@ -52,7 +52,8 @@ H3 {
     <LI><A HREF="#Interception">Interception</A></LI>
     <LI><A HREF="#Authentication">Authentication</A></LI>
     <LI><A HREF="#Plugins">Plugins</A></LI>
     <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><A HREF="#Clustering">Clustering</A></LI>
     <LI><A HREF="#Routing">Routing</A></LI>
     <LI><A HREF="#Performance">Performance</A></LI>
@@ -200,29 +201,34 @@ software upgrade.
 
 <LI><B>primary_radius</B> (ip address)
 <LI><B>secondary_radius</B> (ip address)<BR>
 
 <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>
 </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
 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>
 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>
 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>
 </LI>
 
 <LI><B>bind_address</B> (ip address)<BR>
@@ -338,6 +344,40 @@ Where <I>peer</I> specifies the BGP neighbour as either a hostname or
 IP address, <I>as</I> is the remote AS number and <I>keepalive</I>,
 <I>hold</I> are the timer values in seconds.
 
 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
 <H3 ID="users">users</H3>
 
 Usernames and passwords for the command-line interface are stored in
@@ -500,19 +540,19 @@ IP Address      Used  Session User
 </LI>
 
 <LI><B>show radius</B><BR>
 </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>
 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>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>
        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>
        discarded after 3 retries.</TD></TR>
 </TABLE>
 <P>
@@ -553,7 +593,7 @@ current session for that username will be forwarded to the given
 host/port.  Specify <EM>no snoop username</EM> to disable interception
 for the session.<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>
 response for the user.  See <A HREF="#Interception">Interception</A>.
 <P>
 </LI>
@@ -564,7 +604,7 @@ session.  Specify <EM>no throttle username</EM> to disable throttling
 for the current session.<P>
 
 If you want throttling to be permanent, you will have to modify the
 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>
 
 <P>
 </LI>
 
@@ -660,7 +700,7 @@ desire.  You must first enable the global setting <EM>throttle_speed</EM>
 before this will be activated.<P>
 
 If you wish a session to be throttled permanently, you should set the
 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
 will be handled by the <EM>autothrottle</EM> module.<P>
 
 Otherwise, you can enable and disable throttling an active session using
@@ -684,7 +724,7 @@ and <EM>no snoop username</EM> CLI commands.  These will enable interception
 immediately.<P>
 
 If you wish the user to be intercepted whenever they reconnect, you will
 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>
 
 <B>Cisco-Avpair="intercept=yes"</B>.  For this feature to be enabled,
 you need to have the <EM>autosnoop</EM> module loaded.<P>
 
@@ -694,11 +734,11 @@ Whenever a session connects, it is not fully set up until authentication is
 completed.  The remote end must send a PPP CHAP or PPP PAP authentication
 request to l2tpns.<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
 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>
 
 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>
 
@@ -708,7 +748,7 @@ walled garden module is loaded, in which case the user still receives the
 PPP AUTHACK, but their session is flagged as being a garden'd user, and they
 should not receive any service.<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
 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
@@ -758,7 +798,7 @@ supplied structure:
 <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>
 <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.
                received, but before it has been processed by the
                code.  This will allow you to modify the response in
                some way.
@@ -775,7 +815,7 @@ supplied structure:
                </TD>
        </TR>
        <TR VALIGN=TOP BGCOLOR=WHITE><TD><B>post_auth</B></TD>
                </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.
                received, and the basic checks have been performed.  This
                is what the garden module uses to force authentication
                to be accepted.
@@ -855,7 +895,7 @@ supplied structure:
                </TD>
        </TR>
        <TR VALIGN=TOP BGCOLOR=WHITE><TD><B>radius_response</B></TD>
                </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.
                Cisco-Avpair value.  The value is split up into
                <EM>key=value</EM> pairs, and each is processed through all
                modules.
@@ -901,7 +941,7 @@ Walled Garden is implemented so that you can provide perhaps limited service
 to sessions that incorrectly authenticate.<P>
 
 Whenever a session provides incorrect authentication, and the
 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
 (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
@@ -926,6 +966,14 @@ command:
 iptables -t nat -L garden -nvx
 </PRE>
 
 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
 <H2 ID="Clustering">Clustering</H2>
 
 An l2tpns cluster consists of of one* or more servers configured with
index 35527f3..0c13c8a 100644 (file)
@@ -2,7 +2,7 @@
 .de Id
 .ds Dt \\$4 \\$5
 ..
 .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
 .TH STARTUP-CONFIG 5 "\*(Dt" L2TPNS "File Formats and Conventions"
 .SH NAME
 startup\-config \- configuration file for l2tpns
@@ -199,5 +199,111 @@ is the remote AS number and
 .IR keepalive ,
 .I hold
 are the timer values in seconds.
 .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)
 .SH SEE ALSO
 .BR l2tpns (8)
diff --git a/cli.c b/cli.c
index 435c4cf..0fca55f 100644 (file)
--- a/cli.c
+++ b/cli.c
@@ -2,7 +2,7 @@
 // vim: sw=8 ts=8
 
 char const *cvs_name = "$Name:  $";
 // 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>
 
 #include <stdio.h>
 #include <stdarg.h>
@@ -38,14 +38,15 @@ extern radiust *radius;
 extern ippoolt *ip_address_pool;
 extern struct Tstats *_statistics;
 static struct cli_def *cli = NULL;
 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;
 #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 char *debug_levels[] = {
        "CRIT",
@@ -110,6 +111,14 @@ static int cmd_no_suspend_bgp(struct cli_def *cli, char *command, char **argv, i
 static int cmd_restart_bgp(struct cli_def *cli, char *command, char **argv, int argc);
 #endif /* BGP */
 
 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;
 void init_cli(char *hostname)
 {
        FILE *f;
@@ -203,6 +212,16 @@ void init_cli(char *hostname)
 
        cli_register_command(cli, NULL, "set", cmd_set, PRIVILEGE_PRIVILEGED, MODE_CONFIG, "Set a configuration variable");
 
 
        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);
 
        // Enable regular processing
        cli_regular(cli, regular_stuff);
 
@@ -267,7 +286,7 @@ void cli_do(int sockfd)
                require_auth = addr.sin_addr.s_addr != inet_addr("127.0.0.1");
        }
        else
                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)
        {
 
        if (require_auth)
        {
@@ -852,6 +871,8 @@ static int cmd_write_memory(struct cli_def *cli, char *command, char **argv, int
        return CLI_OK;
 }
 
        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;
 static int cmd_show_run(struct cli_def *cli, char *command, char **argv, int argc)
 {
        int i;
@@ -898,17 +919,17 @@ static int cmd_show_run(struct cli_def *cli, char *command, char **argv, int arg
 #ifdef BGP
        if (config->as_number)
        {
 #ifdef BGP
        if (config->as_number)
        {
-               int k;
+               int k;
                int h;
 
                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, "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;
 
                        k = config->neighbour[i].keepalive;
                        h = config->neighbour[i].hold;
@@ -929,6 +950,22 @@ static int cmd_show_run(struct cli_def *cli, char *command, char **argv, int arg
        }
 #endif
 
        }
 #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;
 }
        cli_print(cli, "# end");
        return CLI_OK;
 }
@@ -1877,7 +1914,7 @@ static int cmd_router_bgp(struct cli_def *cli, char *command, char **argv, int a
        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;
 {
        int i;
        int new = -1;
@@ -1893,7 +1930,7 @@ static int find_bgp_neighbour(char *name)
 
        for (i = 0; i < BGP_NUM_PEERS; i++)
        {
 
        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;
                {
                        if (new == -1) new = i;
                        continue;
@@ -1940,12 +1977,12 @@ static int cmd_router_bgp_neighbour(struct cli_def *cli, char *command, char **a
                                NULL);
 
                default:
                                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_arg_help(cli, 0, "<1-65535>", "Keepalive time", NULL);
 
                                if (argc == 4)
@@ -1977,9 +2014,9 @@ static int cmd_router_bgp_neighbour(struct cli_def *cli, char *command, char **a
                return CLI_OK;
        }
 
                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");
                if (as < 0 || as > 65535)
                {
                        cli_print(cli, "Invalid autonomous system number");
@@ -1997,7 +2034,7 @@ static int cmd_router_bgp_neighbour(struct cli_def *cli, char *command, char **a
                return CLI_OK;
        }
 
                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;
        {
                cli_print(cli, "Invalid arguments");
                return CLI_OK;
@@ -2222,6 +2259,568 @@ static int cmd_restart_bgp(struct cli_def *cli, char *command, char **argv, int
 }
 #endif /* BGP*/
 
 }
 #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])
 {
 // Convert a string in the form of abcd.ef12.3456 into char[6]
 void parsemac(char *string, char mac[6])
 {
index de3929d..54b4667 100644 (file)
--- 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
 
 // 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>
 
 #include <arpa/inet.h>
 #include <assert.h>
@@ -54,7 +54,7 @@ char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.56 2004/11/25 02:49:18 bodea Exp
 #endif /* BGP */
 
 // Globals
 #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
 int tunfd = -1;                        // tun interface file handle. (network device)
 int udpfd = -1;                        // UDP file handle
 int controlfd = -1;            // Control signal handle
@@ -88,9 +88,9 @@ linked_list *loaded_plugins;
 linked_list *plugins[MAX_PLUGIN_TYPES];
 
 #define membersize(STRUCT, MEMBER) sizeof(((STRUCT *)0)->MEMBER)
 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),
        CONFIG("debug", debug, INT),
        CONFIG("log_file", log_filename, STRING),
        CONFIG("pid_file", pid_file, STRING),
@@ -146,6 +146,7 @@ sessiont *session = NULL;           // Array of session structures.
 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.
 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
 static controlt *controlfree = 0;
 struct Tstats *_statistics = NULL;
 #ifdef RINGBUFFER
@@ -942,8 +943,10 @@ static void controladd(controlt * c, tunnelidt t, sessionidt s)
                tunnel[t].controle->next = c;
        else
                tunnel[t].controls = c;
                tunnel[t].controle->next = c;
        else
                tunnel[t].controls = c;
+
        tunnel[t].controle = c;
        tunnel[t].controlc++;
        tunnel[t].controle = c;
        tunnel[t].controlc++;
+
        // send now if space in window
        if (tunnel[t].controlc <= tunnel[t].window)
        {
        // send now if space in window
        if (tunnel[t].controlc <= tunnel[t].window)
        {
@@ -1306,10 +1309,9 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr)
                        STAT(tunnel_rx_errors);
                        return;
                }
                        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;
 
                {
                        int i;
 
@@ -1323,10 +1325,15 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr)
                                        tunnel[i].port != ntohs(addr->sin_port) )
                                        continue;
                                t = 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;
                        }
                }
 
                                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()))
                if (!t)
                {
                        if (!(t = new_tunnel()))
@@ -1339,8 +1346,28 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr)
                        tunnel[t].ip = ntohl(*(ipt *) & addr->sin_addr);
                        tunnel[t].port = ntohs(addr->sin_port);
                        tunnel[t].window = 4; // default window
                        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);
                        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
                }
 
                // This is used to time out old tunnels
@@ -1350,7 +1377,7 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr)
                {
                        int skip = tunnel[t].window; // track how many in-window packets are still in queue
                                // some to clear maybe?
                {
                        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;
                        {
                                controlt *c = tunnel[t].controls;
                                tunnel[t].controls = c->next;
@@ -1361,22 +1388,6 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr)
                                tunnel[t].try = 0; // we have progress
                        }
 
                                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;
                        // receiver advance (do here so quoted correctly in any sends below)
                        if (l) tunnel[t].nr = (ns + 1);
                        if (skip < 0) skip = 0;
@@ -2480,12 +2491,12 @@ static void initdata(int optdebug, char *optconfig)
                LOG(0, 0, 0, 0, "Error doing malloc for _statistics: %s\n", strerror(errno));
                exit(1);
        }
                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);
        }
        {
                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;
        time(&config->start_time);
        strncpy(config->config_file, optconfig, strlen(optconfig));
        config->debug = optdebug;
@@ -2521,6 +2532,13 @@ static void initdata(int optdebug, char *optconfig)
                exit(1);
        }
 
                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))))
        {
 #ifdef RINGBUFFER
        if (!(ringbuffer = shared_malloc(sizeof(struct Tringbuffer))))
        {
@@ -2572,11 +2590,11 @@ static void initdata(int optdebug, char *optconfig)
        _statistics->start_time = _statistics->last_reset = time(NULL);
 
 #ifdef BGP
        _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 */
 }
 
 #endif /* BGP */
 }
 
index ccb55ef..eb575a3 100644 (file)
--- a/l2tpns.h
+++ b/l2tpns.h
@@ -1,5 +1,5 @@
 // L2TPNS Global Stuff
 // 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__
 
 #ifndef __L2TPNS_H__
 #define __L2TPNS_H__
@@ -142,7 +142,7 @@ struct cli_tunnel_actions {
 #define DUMP_MAGIC "L2TPNS#" VERSION "#"
 
 // structures
 #define DUMP_MAGIC "L2TPNS#" VERSION "#"
 
 // structures
-typedef struct routes           // route
+typedef struct                 // route
 {
        ipt ip;
        ipt mask;
 {
        ipt ip;
        ipt mask;
@@ -157,7 +157,7 @@ typedef struct controls         // control message
 }
 controlt;
 
 }
 controlt;
 
-typedef struct sessions
+typedef struct
 {
        sessionidt next;                // next session in linked list
        sessionidt far;                 // far end session ID
 {
        sessionidt next;                // next session in linked list
        sessionidt far;                 // far end session ID
@@ -199,7 +199,9 @@ typedef struct sessions
        ipt snoop_ip;                   // Interception destination IP
        u16 snoop_port;                 // Interception destination port
        u16 sid;                        // near 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;
 
 }
 sessiont;
 
@@ -207,7 +209,8 @@ sessiont;
 #define SF_LCP_ACKED   2       // LCP negotiated
 #define SF_CCP_ACKED   4       // CCP negotiated
 
 #define SF_LCP_ACKED   2       // LCP negotiated
 #define SF_CCP_ACKED   4       // CCP negotiated
 
-typedef struct {
+typedef struct
+{
        u32     cin;
        u32     cout;
 } sessioncountt;
        u32     cin;
        u32     cout;
 } sessioncountt;
@@ -216,7 +219,7 @@ typedef struct {
 #define        SESSIONACFC     2       // ACFC negotiated flags
 
 // 168 bytes per tunnel
 #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
 {
        tunnelidt far;          // far end tunnel ID
        ipt ip;                 // Ip for far end
@@ -239,7 +242,7 @@ typedef struct tunnels
 tunnelt;
 
 // 180 bytes per radius session
 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
 {
        sessionidt session;     // which session this applies to
        hasht auth;             // request authenticator
@@ -393,7 +396,7 @@ struct Tstats
 #define SET_STAT(x, y)
 #endif
 
 #define SET_STAT(x, y)
 #endif
 
-struct configt
+typedef struct
 {
        int             debug;                          // debugging level
        time_t          start_time;                     // time when l2tpns was started
 {
        int             debug;                          // debugging level
        time_t          start_time;                     // time when l2tpns was started
@@ -470,16 +473,65 @@ struct configt
                int     hold;
        }               neighbour[BGP_NUM_PEERS];
 #endif
                int     hold;
        }               neighbour[BGP_NUM_PEERS];
 #endif
-};
+} configt;
 
 enum config_typet { INT, STRING, UNSIGNED_LONG, SHORT, BOOL, IP, MAC };
 
 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;
 {
        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);
 
 // arp.c
 void sendarp(int ifr_idx, const unsigned char* mac, ipt ip);
@@ -570,7 +622,7 @@ if (count++ < max) { \
 }
 
 
 }
 
 
-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;
 extern time_t basetime;                // Time when this process started.
 extern time_t time_now;                // Seconds since EPOCH.
 extern u32 last_id;
index be9b52a..1a39885 100644 (file)
@@ -43,5 +43,5 @@ rm -rf %{buildroot}
 %attr(644,root,root) /usr/share/man/man[58]/*
 
 %changelog
 %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
 - 2.0.9 release, see /usr/share/doc/l2tpns-2.0.9/Changes
diff --git a/ppp.c b/ppp.c
index d6db8d8..9cd9948 100644 (file)
--- a/ppp.c
+++ b/ppp.c
@@ -1,6 +1,6 @@
 // L2TPNS PPP Stuff
 
 // 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>
 
 #include <stdio.h>
 #include <string.h>
@@ -21,7 +21,7 @@ extern int tunfd;
 extern char hostname[];
 extern u32 eth_tx;
 extern time_t time_now;
 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);
 
 
 static void initccp(tunnelidt t, sessionidt s);
 
index ed8bc54..33f8bcb 100644 (file)
--- a/radius.c
+++ b/radius.c
@@ -1,6 +1,6 @@
 // L2TPNS Radius Stuff
 
 // 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>
 
 #include <time.h>
 #include <stdio.h>
@@ -22,7 +22,7 @@ extern radiust *radius;
 extern sessiont *session;
 extern tunnelt *tunnel;
 extern u32 sessionid;
 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)
 extern int *radfds;
 
 static const char *radius_state(int state)