Use session[s].opened consistently when checking for in-use sessions (rather than...
[l2tpns.git] / l2tpns.c
index 341cf74..56e1377 100644 (file)
--- a/l2tpns.c
+++ b/l2tpns.c
@@ -1,10 +1,10 @@
 // L2TP Network Server
 // Adrian Kennard 2002
-// Copyright (c) 2003, 2004 Optus Internet Engineering
+// Copyright (c) 2003, 2004, 2005 Optus Internet Engineering
 // 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.72 2004/12/16 23:40:31 bodea Exp $";
+char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.73.2.4 2005/01/13 08:43:19 bodea Exp $";
 
 #include <arpa/inet.h>
 #include <assert.h>
@@ -117,6 +117,7 @@ config_descriptt config_values[] = {
        CONFIG("scheduler_fifo", scheduler_fifo, BOOL),
        CONFIG("lock_pages", lock_pages, BOOL),
        CONFIG("icmp_rate", icmp_rate, INT),
+       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),
@@ -147,7 +148,7 @@ static sessionidt shut_acct_n = 0;
 
 tunnelt *tunnel = NULL;                        // Array of tunnel structures.
 sessiont *session = NULL;              // Array of session structures.
-sessioncountt *sess_count = NULL;      // Array of partial per-session traffic counters.
+sessionlocalt *sess_local = NULL;      // Array of local per-session 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.
@@ -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,45 @@ 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_local[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_local[s].packets_out > max)
+                       {
+                               sess_local[s].packets_dropped++;
+                               return;
+                       }
+               }
+               else
+               {
+                       if (sess_local[s].packets_dropped)
+                       {
+                               INC_STAT(tun_rx_dropped, sess_local[s].packets_dropped);
+                               LOG(3, s, t, "Dropped %u/%u packets to %s for %suser %s\n",
+                                       sess_local[s].packets_dropped, sess_local[s].packets_out,
+                                       fmtaddr(ip, 0), sp->throttle_out ? "throttled " : "",
+                                       sp->user);
+                       }
+
+                       sess_local[s].last_packet_out = TIME;
+                       sess_local[s].packets_out = 1;
+                       sess_local[s].packets_dropped = 0;
+               }
+       }
+
        // run access-list if any
        if (session[s].filter_out && !ip_filter(buf, len, session[s].filter_out - 1))
                return;
@@ -802,7 +842,7 @@ static void processipout(uint8_t * buf, int len)
        sp->total_cout += len; // byte count
        sp->pout++;
        udp_tx += len;
-       sess_count[s].cout += len;      // To send to master..
+       sess_local[s].cout += len;      // To send to master..
 }
 
 //
@@ -852,7 +892,7 @@ static void send_ipout(sessionidt s, uint8_t *buf, int len)
        sp->total_cout += len; // byte count
        sp->pout++;
        udp_tx += len;
-       sess_count[s].cout += len;      // To send to master..
+       sess_local[s].cout += len;      // To send to master..
 }
 
 // add an AVP (16 bit)
@@ -2367,14 +2407,11 @@ static void mainloop(void)
                        int tun_pkts = 0;
                        int cluster_pkts = 0;
 
-                       INC_STAT(select_ready, n);
-
                        // nsctl commands
                        if (FD_ISSET(controlfd, &r))
                        {
                                alen = sizeof(addr);
                                processcontrol(buf, recvfrom(controlfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr, alen);
-                               STAT(select_processed);
                                n--;
                        }
 
@@ -2386,7 +2423,6 @@ static void mainloop(void)
                                        if (FD_ISSET(radfds[i], &r))
                                        {
                                                processrad(buf, recv(radfds[i], buf, sizeof(buf), 0), i);
-                                               STAT(select_processed);
                                                n--;
                                        }
                                }
@@ -2406,21 +2442,17 @@ static void mainloop(void)
                                else
                                        LOG(0, 0, 0, "accept error: %s\n", strerror(errno));
 
-                               STAT(select_processed);
                                n--;
                        }
 
 #ifdef BGP
                        for (i = 0; i < BGP_NUM_PEERS; i++)
                        {
-                               int isr = bgp_set[i] ? !!FD_ISSET(bgp_peers[i].sock, &r) : 0;
-                               int isw = bgp_set[i] ? !!FD_ISSET(bgp_peers[i].sock, &w) : 0;
+                               int isr = bgp_set[i] ? FD_ISSET(bgp_peers[i].sock, &r) : 0;
+                               int isw = bgp_set[i] ? FD_ISSET(bgp_peers[i].sock, &w) : 0;
                                bgp_process(&bgp_peers[i], isr, isw);
-                               if (isr || isw)
-                               {
-                                       INC_STAT(select_processed, isr + isw);
-                                       n -= (isr + isw);
-                               }
+                               if (isr) n--;
+                               if (isw) n--;
                        }
 #endif /* BGP */
 
@@ -2433,7 +2465,6 @@ static void mainloop(void)
                                        if ((s = recvfrom(udpfd, buf, sizeof(buf), 0, (void *) &addr, &alen)) > 0)
                                        {
                                                processudp(buf, s, &addr);
-                                               STAT(select_processed);
                                                udp_pkts++;
                                        }
                                        else
@@ -2449,7 +2480,6 @@ static void mainloop(void)
                                        if ((s = read(tunfd, buf, sizeof(buf))) > 0)
                                        {
                                                processtun(buf, s);
-                                               STAT(select_processed);
                                                tun_pkts++;
                                        }
                                        else
@@ -2466,7 +2496,6 @@ static void mainloop(void)
                                        if ((s = recvfrom(cluster_sockfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen)) > 0)
                                        {
                                                processcluster(buf, s, addr.sin_addr.s_addr);
-                                               STAT(select_processed);
                                                cluster_pkts++;
                                        }
                                        else
@@ -2477,9 +2506,16 @@ static void mainloop(void)
                                }
                        }
 
+                       if (udp_pkts > 1 || tun_pkts > 1 || cluster_pkts > 1)
+                               STAT(multi_read_used);
+
                        if (c >= config->multi_read_count)
+                       {
                                LOG(3, 0, 0, "Reached multi_read_count (%d); processed %d udp, %d tun and %d cluster packets\n",
                                        config->multi_read_count, udp_pkts, tun_pkts, cluster_pkts);
+
+                               STAT(multi_read_exceeded);
+                       }
                }
 
                        // Runs on every machine (master and slaves).
@@ -2641,9 +2677,9 @@ static void initdata(int optdebug, char *optconfig)
                exit(1);
        }
 
-       if (!(sess_count = shared_malloc(sizeof(sessioncountt) * MAXSESSION)))
+       if (!(sess_local = shared_malloc(sizeof(sessionlocalt) * MAXSESSION)))
        {
-               LOG(0, 0, 0, "Error doing malloc for sessions_count: %s\n", strerror(errno));
+               LOG(0, 0, 0, "Error doing malloc for sess_local: %s\n", strerror(errno));
                exit(1);
        }
 
@@ -3132,7 +3168,7 @@ int main(int argc, char *argv[])
        init_tbf(config->num_tbfs);
 
        LOG(0, 0, 0, "L2TPNS version " VERSION "\n");
-       LOG(0, 0, 0, "Copyright (c) 2003, 2004 Optus Internet Engineering\n");
+       LOG(0, 0, 0, "Copyright (c) 2003, 2004, 2005 Optus Internet Engineering\n");
        LOG(0, 0, 0, "Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced\n");
        {
                struct rlimit rlim;