+* 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#.
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.
.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
.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
// 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>
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]);
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),
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;
{
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");
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");
// 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>
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),
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),
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];
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;
}
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;
return;
}
- if (*(uint16_t *) (buf + 2) == htons(PKTIP)) // IPv4
+ if (*(uint16_t *) (buf + 2) == htons(PKTIP)) // IP
processipout(buf, len);
// Else discard.
}
// 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__
#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
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
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;
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.
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
%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