X-Git-Url: http://git.sameswireless.fr/l2tpns.git/blobdiff_plain/6c4ac1aa6a6659816080995e649cc640a9290066..30b64bb9f99720d8886057caeb519f2524f1b4cf:/l2tpns.c diff --git a/l2tpns.c b/l2tpns.c index a02be1d..5641cd6 100644 --- 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.73 2004/12/17 00:28:00 bodea Exp $"; +char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.73.2.7 2005/05/06 06:35:18 bodea Exp $"; #include #include @@ -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. @@ -478,7 +479,7 @@ sessionidt sessionbyip(in_addr_t ip) int s = lookup_ipmap(ip); CSTAT(call_sessionbyip); - if (s > 0 && s < MAXSESSION && session[s].tunnel) + if (s > 0 && s < MAXSESSION && session[s].opened) return (sessionidt) s; return 0; @@ -578,8 +579,11 @@ sessionidt sessionbyuser(char *username) int s; CSTAT(call_sessionbyuser); - for (s = 1; s < MAXSESSION ; ++s) + for (s = 1; s <= config->cluster_highest_sessionid ; ++s) { + if (!session[s].opened) + continue; + if (session[s].walled_garden) continue; // Skip walled garden users. @@ -621,17 +625,16 @@ void send_garp(in_addr_t ip) sendarp(ifr.ifr_ifindex, mac, ip); } -// Find session by username, 0 for not found static sessiont *sessiontbysessionidt(sessionidt s) { - if (!s || s > MAXSESSION) return NULL; + if (!s || s >= MAXSESSION) return NULL; return &session[s]; } static sessionidt sessionidtbysessiont(sessiont *s) { sessionidt val = s-session; - if (s < session || val > MAXSESSION) return 0; + if (s < session || val >= MAXSESSION) return 0; return val; } @@ -711,7 +714,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 +724,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 +768,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 +844,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 +894,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) @@ -972,7 +1014,7 @@ static void controladd(controlt * c, tunnelidt t, sessionidt s) // void throttle_session(sessionidt s, int rate_in, int rate_out) { - if (!session[s].tunnel) + if (!session[s].opened) return; // No-one home. if (!*session[s].user) @@ -1010,7 +1052,7 @@ void throttle_session(sessionidt s, int rate_in, int rate_out) // add/remove filters from session (-1 = no change) void filter_session(sessionidt s, int filter_in, int filter_out) { - if (!session[s].tunnel) + if (!session[s].opened) return; // No-one home. if (!*session[s].user) @@ -1053,9 +1095,9 @@ void sessionshutdown(sessionidt s, char *reason) CSTAT(call_sessionshutdown); - if (!session[s].tunnel) + if (!session[s].opened) { - LOG(3, s, session[s].tunnel, "Called sessionshutdown on a session with no tunnel.\n"); + LOG(3, s, session[s].tunnel, "Called sessionshutdown on an unopened session.\n"); return; // not a live session } @@ -1066,7 +1108,7 @@ void sessionshutdown(sessionidt s, char *reason) run_plugins(PLUGIN_KILL_SESSION, &data); } - if (session[s].opened && !walled_garden && !session[s].die) + if (session[s].ip && !walled_garden && !session[s].die) { // RADIUS Stop message uint16_t r = session[s].radius; @@ -1127,7 +1169,7 @@ void sessionshutdown(sessionidt s, char *reason) } if (!session[s].die) - session[s].die = now() + 150; // Clean up in 15 seconds + session[s].die = TIME + 150; // Clean up in 15 seconds // update filter refcounts if (session[s].filter_in) ip_filters[session[s].filter_in - 1].used--; @@ -1178,12 +1220,21 @@ void sendipcp(tunnelidt t, sessionidt s) } // kill a session now -static void sessionkill(sessionidt s, char *reason) +void sessionkill(sessionidt s, char *reason) { CSTAT(call_sessionkill); - session[s].die = now(); + if (!session[s].opened) // not alive + return; + + if (session[s].next) + { + LOG(0, s, session[s].tunnel, "Tried to kill a session with next pointer set (%d)\n", session[s].next); + return; + } + + session[s].die = TIME; sessionshutdown(s, reason); // close radius/routes, etc. if (session[s].radius) radiusclear(session[s].radius, s); // cant send clean accounting data, session is killed @@ -1225,7 +1276,7 @@ static void tunnelkill(tunnelidt t, char *reason) controlfree = c; } // kill sessions - for (s = 1; s < MAXSESSION; s++) + for (s = 1; s <= config->cluster_highest_sessionid ; ++s) if (session[s].tunnel == t) sessionkill(s, reason); @@ -1252,12 +1303,12 @@ static void tunnelshutdown(tunnelidt t, char *reason) LOG(1, 0, t, "Shutting down tunnel %d (%s)\n", t, reason); // close session - for (s = 1; s < MAXSESSION; s++) + for (s = 1; s <= config->cluster_highest_sessionid ; ++s) if (session[s].tunnel == t) sessionshutdown(s, reason); tunnel[t].state = TUNNELDIE; - tunnel[t].die = now() + 700; // Clean up in 70 seconds + tunnel[t].die = TIME + 700; // Clean up in 70 seconds cluster_send_tunnel(t); // TBA - should we wait for sessions to stop? { // Send StopCCN @@ -1796,7 +1847,8 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) if (!sessionfree) { STAT(session_overflow); - tunnelshutdown(t, "No free sessions"); + LOG(1, 0, t, "No free sessions\n"); + return; } else { @@ -1820,7 +1872,7 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) c = controlnew(11); // sending ICRP session[s].id = sessionid++; - session[s].opened = time(NULL); + session[s].opened = time_now; session[s].tunnel = t; session[s].far = asession; session[s].last_packet = time_now; @@ -1903,7 +1955,7 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) l -= 2; } - if (s && !session[s].tunnel) // Is something wrong?? + if (s && !session[s].opened) // Is something wrong?? { if (!config->cluster_iam_master) { @@ -1912,10 +1964,7 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) return; } - - LOG(1, s, t, "UDP packet contains session %d but no session[%d].tunnel " - "exists (LAC said tunnel = %d). Dropping packet.\n", s, s, t); - + LOG(1, s, t, "UDP packet contains session which is not opened. Dropping packet.\n"); STAT(tunnel_rx_errors); return; } @@ -2083,7 +2132,7 @@ static int regular_cleanups(void) if (s > config->cluster_highest_sessionid) s = 1; - if (!session[s].tunnel) // Session isn't in use + if (!session[s].opened) // Session isn't in use continue; if (!session[s].die && session[s].ip && !(session[s].flags & SF_IPCP_ACKED)) @@ -2637,9 +2686,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); } @@ -2691,7 +2740,7 @@ memset(ip_filters, 0, sizeof(ip_filtert) * MAXFILTER); memset(ip_address_pool, 0, sizeof(ippoolt) * MAXIPPOOL); // Put all the sessions on the free list marked as undefined. - for (i = 1; i < MAXSESSION - 1; i++) + for (i = 1; i < MAXSESSION; i++) { session[i].next = i + 1; session[i].tunnel = T_UNDEF; // mark it as not filled in. @@ -2700,7 +2749,7 @@ memset(ip_filters, 0, sizeof(ip_filtert) * MAXFILTER); sessionfree = 1; // Mark all the tunnels as undefined (waiting to be filled in by a download). - for (i = 1; i < MAXTUNNEL- 1; i++) + for (i = 1; i < MAXTUNNEL; i++) tunnel[i].state = TUNNELUNDEF; // mark it as not filled in. if (!*hostname) @@ -2826,7 +2875,7 @@ void rebuild_address_pool(void) for (i = 0; i < MAXSESSION; ++i) { int ipid; - if (!session[i].ip || !session[i].tunnel) + if (!(session[i].opened && session[i].ip)) continue; ipid = - lookup_ipmap(htonl(session[i].ip)); @@ -3128,7 +3177,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; @@ -3699,7 +3748,7 @@ int sessionsetup(tunnelidt t, sessionidt s) LOG(3, s, t, "Doing session setup for session\n"); - if (!session[s].ip || session[s].ip == 0xFFFFFFFE) + if (!session[s].ip) { assign_ip_address(s); if (!session[s].ip) @@ -4297,7 +4346,7 @@ void become_master(void) { for (s = 1; s <= config->cluster_highest_sessionid ; ++s) { - if (!session[s].tunnel) // Not an in-use session. + if (!session[s].opened) // Not an in-use session. continue; run_plugins(PLUGIN_NEW_SESSION_MASTER, &session[s]); @@ -4329,7 +4378,7 @@ int cmd_show_hist_idle(struct cli_def *cli, char *command, char **argv, int argc for (s = 1; s <= config->cluster_highest_sessionid ; ++s) { int idle; - if (!session[s].tunnel) + if (!session[s].opened) continue; idle = time_now - session[s].last_packet; @@ -4367,7 +4416,7 @@ int cmd_show_hist_open(struct cli_def *cli, char *command, char **argv, int argc for (s = 1; s <= config->cluster_highest_sessionid ; ++s) { int open = 0, d; - if (!session[s].tunnel) + if (!session[s].opened) continue; d = time_now - session[s].opened;