more DoS prevention: add packet_limit option to apply a hard limit to downstream...
authorbodea <bodea>
Mon, 10 Jan 2005 07:08:12 +0000 (07:08 +0000)
committerbodea <bodea>
Mon, 10 Jan 2005 07:08:12 +0000 (07:08 +0000)
Changes
Docs/manual.html
Docs/startup-config.5
cli.c
l2tpns.c
l2tpns.h
l2tpns.spec

diff --git a/Changes b/Changes
index 75d052f..c5bd865 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,3 +1,7 @@
+* Mon Jan 10 2005 Brendan O'Dea <bod@optusnet.com.au> 2.0.15
+- More DoS prevention:  add packet_limit option to apply a hard limit
+  to downstream packets per session.
+
 * Mon Dec 20 2004 Brendan O'Dea <bod@optusnet.com.au> 2.0.14
 - Throttle outgoing LASTSEEN packets to at most one per second for a
   given seq#.
index b95858b..8dd2532 100644 (file)
@@ -307,6 +307,13 @@ Keep all pages mapped by the l2tpns process in memory.
 Maximum number of host unreachable ICMP packets to send per second.
 </LI>
 
+<LI><B>packet_limit</B> (int><BR>
+Maximum number of packets of downstream traffic to be handled each
+tenth of a second per session.  If zero, no limit is applied (default: 
+0).  Intended as a DoS prevention mechanism and not a general
+throttling control (packets are dropped, not queued).
+</LI>
+
 <LI><B>cluster_address</B> (ip address)<BR>
 Multicast cluster address (default:  239.192.13.13).  See the section
 on <A HREF="#Clustering">Clustering</A> for more information.
index 3ced03f..9fe9178 100644 (file)
@@ -2,7 +2,7 @@
 .de Id
 .ds Dt \\$4 \\$5
 ..
-.Id $Id: startup-config.5,v 1.3 2004/11/29 06:29:28 bodea Exp $
+.Id $Id: startup-config.5,v 1.3.2.1 2005/01/10 07:08:36 bodea Exp $
 .TH STARTUP-CONFIG 5 "\*(Dt" L2TPNS "File Formats and Conventions"
 .SH NAME
 startup\-config \- configuration file for l2tpns
@@ -160,6 +160,12 @@ process in memory.
 .B icmp_rate
 Maximum number of host unreachable ICMP packets to send per second.
 .TP
+.B packet_limit
+Maximum number of packets of downstream traffic to be handled each
+tenth of a second per session.  If zero, no limit is applied (default: 
+0).  Intended as a DoS prevention mechanism and not a general
+throttling control (packets are dropped, not queued).
+.TP
 .B cluster_address
 Multicast cluster address (default: 239.192.13.13).
 .TP
diff --git a/cli.c b/cli.c
index e90b2a6..cde1649 100644 (file)
--- a/cli.c
+++ b/cli.c
@@ -2,7 +2,7 @@
 // vim: sw=8 ts=8
 
 char const *cvs_name = "$Name:  $";
-char const *cvs_id_cli = "$Id: cli.c,v 1.44 2004/12/18 01:20:05 bodea Exp $";
+char const *cvs_id_cli = "$Id: cli.c,v 1.43.2.1 2005/01/10 07:08:12 bodea Exp $";
 
 #include <stdio.h>
 #include <stdarg.h>
@@ -98,7 +98,6 @@ static int cmd_set(struct cli_def *cli, char *command, char **argv, int argc);
 static int cmd_load_plugin(struct cli_def *cli, char *command, char **argv, int argc);
 static int cmd_remove_plugin(struct cli_def *cli, char *command, char **argv, int argc);
 static int cmd_uptime(struct cli_def *cli, char *command, char **argv, int argc);
-
 static int regular_stuff(struct cli_def *cli);
 static void parsemac(char *string, char mac[6]);
 
@@ -646,23 +645,24 @@ static int cmd_show_counters(struct cli_def *cli, char *command, char **argv, in
        if (CLI_HELP_REQUESTED)
                return CLI_HELP_NO_ARGS;
 
-       cli_print(cli, "%-10s %-8s %-10s %-8s", "Ethernet", "Bytes", "Packets", "Errors");
-       cli_print(cli, "%-10s %8u %8u %8u", "RX",
+       cli_print(cli, "%-10s %10s %8s %8s %8s", "Ethernet", "Bytes", "Packets", "Errors", "Dropped");
+       cli_print(cli, "%-10s %10u %8u %8u %8u", "RX",
                        GET_STAT(tun_rx_bytes),
                        GET_STAT(tun_rx_packets),
-                       GET_STAT(tun_rx_errors));
-       cli_print(cli, "%-10s %8u %8u %8u", "TX",
+                       GET_STAT(tun_rx_errors),
+                       GET_STAT(tun_rx_dropped));
+       cli_print(cli, "%-10s %10u %8u %8u", "TX",
                        GET_STAT(tun_tx_bytes),
                        GET_STAT(tun_tx_packets),
                        GET_STAT(tun_tx_errors));
        cli_print(cli, "");
 
-       cli_print(cli, "%-10s %-8s %-10s %-8s %-8s", "Tunnel", "Bytes", "Packets", "Errors", "Retries");
-       cli_print(cli, "%-10s %8u %8u %8u", "RX",
+       cli_print(cli, "%-10s %10s %8s %8s %8s", "Tunnel", "Bytes", "Packets", "Errors", "Retries");
+       cli_print(cli, "%-10s %10u %8u %8u", "RX",
                        GET_STAT(tunnel_rx_bytes),
                        GET_STAT(tunnel_rx_packets),
                        GET_STAT(tunnel_rx_errors));
-       cli_print(cli, "%-10s %8u %8u %8u %8u", "TX",
+       cli_print(cli, "%-10s %10u %8u %8u %8u", "TX",
                        GET_STAT(tunnel_tx_bytes),
                        GET_STAT(tunnel_tx_packets),
                        GET_STAT(tunnel_tx_errors),
@@ -890,7 +890,6 @@ 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;
-       char ipv6addr[INET6_ADDRSTRLEN];
 
        if (CLI_HELP_REQUESTED)
                return CLI_HELP_NO_ARGS;
@@ -901,27 +900,25 @@ static int cmd_show_run(struct cli_def *cli, char *command, char **argv, int arg
        {
                void *value = ((void *)config) + config_values[i].offset;
                if (config_values[i].type == STRING)
-                       cli_print(cli, "set %s \"%.*s\"", config_values[i].key, config_values[i].size, (char *) value);
-               else if (config_values[i].type == IPv4)
-                       cli_print(cli, "set %s %s", config_values[i].key, fmtaddr(*(in_addr_t *) value, 0));
-               else if (config_values[i].type == IPv6)
-                       cli_print(cli, "set %s %s", config_values[i].key, inet_ntop(AF_INET6, value, ipv6addr, INET6_ADDRSTRLEN));
+                       cli_print(cli, "set %s \"%.*s\"", config_values[i].key, config_values[i].size, (char *)value);
+               else if (config_values[i].type == IP)
+                       cli_print(cli, "set %s %s", config_values[i].key, fmtaddr(*(unsigned *)value, 0));
                else if (config_values[i].type == SHORT)
-                       cli_print(cli, "set %s %hu", config_values[i].key, *(short *) value);
+                       cli_print(cli, "set %s %hu", config_values[i].key, *(short *)value);
                else if (config_values[i].type == BOOL)
-                       cli_print(cli, "set %s %s", config_values[i].key, (*(int *) value) ? "yes" : "no");
+                       cli_print(cli, "set %s %s", config_values[i].key, (*(int *)value) ? "yes" : "no");
                else if (config_values[i].type == INT)
-                       cli_print(cli, "set %s %d", config_values[i].key, *(int *) value);
+                       cli_print(cli, "set %s %d", config_values[i].key, *(int *)value);
                else if (config_values[i].type == UNSIGNED_LONG)
-                       cli_print(cli, "set %s %lu", config_values[i].key, *(unsigned long *) value);
+                       cli_print(cli, "set %s %lu", config_values[i].key, *(unsigned long *)value);
                else if (config_values[i].type == MAC)
                        cli_print(cli, "set %s %02x%02x.%02x%02x.%02x%02x", config_values[i].key,
-                                       *(unsigned short *) (value + 0),
-                                       *(unsigned short *) (value + 1),
-                                       *(unsigned short *) (value + 2),
-                                       *(unsigned short *) (value + 3),
-                                       *(unsigned short *) (value + 4),
-                                       *(unsigned short *) (value + 5));
+                                       *(unsigned short *)(value + 0),
+                                       *(unsigned short *)(value + 1),
+                                       *(unsigned short *)(value + 2),
+                                       *(unsigned short *)(value + 3),
+                                       *(unsigned short *)(value + 4),
+                                       *(unsigned short *)(value + 5));
        }
 
        cli_print(cli, "# Plugins");
@@ -1830,31 +1827,28 @@ static int cmd_set(struct cli_def *cli, char *command, char **argv, int argc)
                        switch (config_values[i].type)
                        {
                        case STRING:
-                               strncpy((char *) value, argv[1], config_values[i].size - 1);
+                               strncpy((char *)value, argv[1], config_values[i].size - 1);
                                break;
                        case INT:
-                               *(int *) value = atoi(argv[1]);
+                               *(int *)value = atoi(argv[1]);
                                break;
                        case UNSIGNED_LONG:
-                               *(unsigned long *) value = atol(argv[1]);
+                               *(unsigned long *)value = atol(argv[1]);
                                break;
                        case SHORT:
-                               *(short *) value = atoi(argv[1]);
-                               break;
-                       case IPv4:
-                               *(in_addr_t *) value = inet_addr(argv[1]);
+                               *(short *)value = atoi(argv[1]);
                                break;
-                       case IPv6:
-                               inet_pton(AF_INET6, argv[1], value);
+                       case IP:
+                               *(unsigned *)value = inet_addr(argv[1]);
                                break;
                        case MAC:
                                parsemac(argv[1], (char *)value);
                                break;
                        case BOOL:
                                if (strcasecmp(argv[1], "yes") == 0 || strcasecmp(argv[1], "true") == 0 || strcasecmp(argv[1], "1") == 0)
-                                       *(int *) value = 1;
+                                       *(int *)value = 1;
                                else
-                                       *(int *) value = 0;
+                                       *(int *)value = 0;
                                break;
                        default:
                                cli_print(cli, "Unknown variable type");
index 0503ba2..8f6e7bf 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
 
-char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.74 2004/12/18 01:20:05 bodea Exp $";
+char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.73.2.1 2005/01/10 07:08:13 bodea Exp $";
 
 #include <arpa/inet.h>
 #include <assert.h>
@@ -95,17 +95,17 @@ config_descriptt config_values[] = {
        CONFIG("log_file", log_filename, STRING),
        CONFIG("pid_file", pid_file, STRING),
        CONFIG("l2tp_secret", l2tpsecret, STRING),
-       CONFIG("primary_dns", default_dns1, IPv4),
-       CONFIG("secondary_dns", default_dns2, IPv4),
+       CONFIG("primary_dns", default_dns1, IP),
+       CONFIG("secondary_dns", default_dns2, IP),
        CONFIG("save_state", save_state, BOOL),
-       CONFIG("primary_radius", radiusserver[0], IPv4),
-       CONFIG("secondary_radius", radiusserver[1], IPv4),
+       CONFIG("primary_radius", radiusserver[0], IP),
+       CONFIG("secondary_radius", radiusserver[1], IP),
        CONFIG("primary_radius_port", radiusport[0], SHORT),
        CONFIG("secondary_radius_port", radiusport[1], SHORT),
        CONFIG("radius_accounting", radius_accounting, BOOL),
        CONFIG("radius_secret", radiussecret, STRING),
-       CONFIG("bind_address", bind_address, IPv4),
-       CONFIG("peer_address", peer_address, IPv4),
+       CONFIG("bind_address", bind_address, IP),
+       CONFIG("peer_address", peer_address, IP),
        CONFIG("send_garp", send_garp, BOOL),
        CONFIG("throttle_speed", rl_rate, UNSIGNED_LONG),
        CONFIG("throttle_buckets", num_tbfs, INT),
@@ -117,7 +117,8 @@ config_descriptt config_values[] = {
        CONFIG("scheduler_fifo", scheduler_fifo, BOOL),
        CONFIG("lock_pages", lock_pages, BOOL),
        CONFIG("icmp_rate", icmp_rate, INT),
-       CONFIG("cluster_address", cluster_address, IPv4),
+       CONFIG("packet_limit", max_packets, INT),
+       CONFIG("cluster_address", cluster_address, IP),
        CONFIG("cluster_interface", cluster_interface, STRING),
        CONFIG("cluster_hb_interval", cluster_hb_interval, INT),
        CONFIG("cluster_hb_timeout", cluster_hb_timeout, INT),
@@ -711,7 +712,7 @@ static void processipout(uint8_t * buf, int len)
        tunnelidt t;
        in_addr_t ip;
 
-       char * data = buf;      // Keep a copy of the originals.
+       char *data = buf;       // Keep a copy of the originals.
        int size = len;
 
        uint8_t b[MAXETHER + 20];
@@ -721,13 +722,13 @@ static void processipout(uint8_t * buf, int len)
        if (len < MIN_IP_SIZE)
        {
                LOG(1, 0, 0, "Short IP, %d bytes\n", len);
-               STAT(tunnel_tx_errors);
+               STAT(tun_rx_errors);
                return;
        }
        if (len >= MAXETHER)
        {
                LOG(1, 0, 0, "Oversize IP packet %d bytes\n", len);
-               STAT(tunnel_tx_errors);
+               STAT(tun_rx_errors);
                return;
        }
 
@@ -765,6 +766,43 @@ static void processipout(uint8_t * buf, int len)
        t = session[s].tunnel;
        sp = &session[s];
 
+       // DoS prevention: enforce a maximum number of packets per 0.1s for a session
+       if (config->max_packets > 0)
+       {
+               if (sess_count[s].last_packet_out == TIME)
+               {
+                       int max = config->max_packets;
+
+                       // All packets for throttled sessions are handled by the
+                       // master, so further limit by using the throttle rate.
+                       // A bit of a kludge, since throttle rate is in kbps,
+                       // but should still be generous given our average DSL
+                       // packet size is 200 bytes: a limit of 28kbps equates
+                       // to around 180 packets per second.
+                       if (!config->cluster_iam_master && sp->throttle_out && sp->throttle_out < max)
+                               max = sp->throttle_out;
+
+                       if (++sess_count[s].packets_out > max)
+                       {
+                               sess_count[s].packets_dropped++;
+                               return;
+                       }
+               }
+               else
+               {
+                       if (sess_count[s].packets_dropped)
+                       {
+                               INC_STAT(tun_rx_dropped, sess_count[s].packets_dropped);
+                               LOG(2, s, t, "Possible DoS attack on %s (%s); dropped %u packets.",
+                                       fmtaddr(ip, 0), sp->user, sess_count[s].packets_dropped);
+                       }
+
+                       sess_count[s].last_packet_out = TIME;
+                       sess_count[s].packets_out = 1;
+                       sess_count[s].packets_dropped = 0;
+               }
+       }
+
        // run access-list if any
        if (session[s].filter_out && !ip_filter(buf, len, session[s].filter_out - 1))
                return;
@@ -1993,7 +2031,7 @@ static void processtun(uint8_t * buf, int len)
                return;
        }
 
-       if (*(uint16_t *) (buf + 2) == htons(PKTIP)) // IPv4
+       if (*(uint16_t *) (buf + 2) == htons(PKTIP)) // IP
                processipout(buf, len);
        // Else discard.
 }
index 8c3e4e6..87ed953 100644 (file)
--- a/l2tpns.h
+++ b/l2tpns.h
@@ -1,5 +1,5 @@
 // L2TPNS Global Stuff
-// $Id: l2tpns.h,v 1.49.2.1 2005/01/06 01:39:23 bodea Exp $
+// $Id: l2tpns.h,v 1.49.2.2 2005/01/10 07:08:14 bodea Exp $
 
 #ifndef __L2TPNS_H__
 #define __L2TPNS_H__
@@ -15,7 +15,7 @@
 #include <sys/types.h>
 #include <libcli.h>
 
-#define VERSION        "2.0.14"
+#define VERSION        "2.0.15"
 
 // Limits
 #define MAXTUNNEL      500             // could be up to 65535
@@ -211,8 +211,14 @@ sessiont;
 
 typedef struct
 {
+       // byte counters
        uint32_t cin;
        uint32_t cout;
+
+       // DoS prevention
+       clockt last_packet_out;
+       uint32_t packets_out;
+       uint32_t packets_dropped;
 } sessioncountt;
 
 #define        SESSIONPFC      1       // PFC negotiated flags
@@ -316,6 +322,7 @@ struct Tstats
     uint32_t   tun_tx_bytes;
     uint32_t   tun_rx_errors;
     uint32_t   tun_tx_errors;
+    uint32_t   tun_rx_dropped;
 
     uint32_t   tunnel_rx_packets;
     uint32_t   tunnel_tx_packets;
@@ -446,7 +453,8 @@ typedef struct
        int             next_tbf;                       // Next HTB id available to use
        int             scheduler_fifo;                 // If the system has multiple CPUs, use FIFO scheduling policy for this process.
        int             lock_pages;                     // Lock pages into memory.
-       int             icmp_rate;                      // Max number of ICMP unreachable per second to send>
+       int             icmp_rate;                      // Max number of ICMP unreachable per second to send
+       int             max_packets;                    // DoS prevention: per session limit of packets/0.1s
 
        in_addr_t       cluster_address;                // Multicast address of cluster.
                                                        // Send to this address to have everyone hear.
index b3af7ad..b73eb21 100644 (file)
@@ -1,6 +1,6 @@
 Summary: A high-speed clustered L2TP LNS
 Name: l2tpns
-Version: 2.0.14
+Version: 2.0.15
 Release: 1
 Copyright: GPL
 Group: System Environment/Daemons
@@ -43,5 +43,5 @@ rm -rf %{buildroot}
 %attr(644,root,root) /usr/share/man/man[58]/*
 
 %changelog
-* Mon Dec 20 2004 Brendan O'Dea <bod@optusnet.com.au> 2.0.14-1
-- 2.0.14 release, see /usr/share/doc/l2tpns-2.0.14/Changes
+* Mon Jan 10 2005 Brendan O'Dea <bod@optusnet.com.au> 2.0.15-1
+- 2.0.15 release, see /usr/share/doc/l2tpns-2.0.15/Changes