From: bodea Date: Mon, 10 Jan 2005 07:08:12 +0000 (+0000) Subject: more DoS prevention: add packet_limit option to apply a hard limit to downstream... X-Git-Tag: release_2_0_15~8 X-Git-Url: http://git.sameswireless.fr/l2tpns.git/commitdiff_plain/c3e841988ea3dfc005a414de54d252d2fe0b071c more DoS prevention: add packet_limit option to apply a hard limit to downstream packets per session --- diff --git a/Changes b/Changes index 75d052f..c5bd865 100644 --- a/Changes +++ b/Changes @@ -1,3 +1,7 @@ +* Mon Jan 10 2005 Brendan O'Dea 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 2.0.14 - Throttle outgoing LASTSEEN packets to at most one per second for a given seq#. diff --git a/Docs/manual.html b/Docs/manual.html index b95858b..8dd2532 100644 --- a/Docs/manual.html +++ b/Docs/manual.html @@ -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. +
  • packet_limit (int>
    +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). +
  • +
  • cluster_address (ip address)
    Multicast cluster address (default: 239.192.13.13). See the section on Clustering for more information. diff --git a/Docs/startup-config.5 b/Docs/startup-config.5 index 3ced03f..9fe9178 100644 --- a/Docs/startup-config.5 +++ b/Docs/startup-config.5 @@ -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 --- 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 #include @@ -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"); diff --git a/l2tpns.c b/l2tpns.c index 0503ba2..8f6e7bf 100644 --- 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 #include @@ -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. } diff --git a/l2tpns.h b/l2tpns.h index 8c3e4e6..87ed953 100644 --- 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 #include -#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. diff --git a/l2tpns.spec b/l2tpns.spec index b3af7ad..b73eb21 100644 --- a/l2tpns.spec +++ b/l2tpns.spec @@ -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 2.0.14-1 -- 2.0.14 release, see /usr/share/doc/l2tpns-2.0.14/Changes +* Mon Jan 10 2005 Brendan O'Dea 2.0.15-1 +- 2.0.15 release, see /usr/share/doc/l2tpns-2.0.15/Changes