X-Git-Url: http://git.sameswireless.fr/l2tpns.git/blobdiff_plain/847b4c6b1d2e074003411aa706b76bc667518b34..895d679e8ea7f092d6710dfa16bea8aec211d881:/l2tpns.c?ds=sidebyside diff --git a/l2tpns.c b/l2tpns.c index 2fa9b12..8204503 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.104 2005-05-16 04:51:16 bodea Exp $"; +char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.115 2005-07-31 10:04:10 bodea Exp $"; #include #include @@ -38,9 +38,9 @@ char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.104 2005-05-16 04:51:16 bodea Exp #include #include #include +#include #include -#include "md5.h" #include "l2tpns.h" #include "cluster.h" #include "plugin.h" @@ -52,7 +52,7 @@ char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.104 2005-05-16 04:51:16 bodea Exp #ifdef BGP #include "bgp.h" -#endif /* BGP */ +#endif // Globals configt *config = NULL; // all configuration @@ -60,17 +60,19 @@ int tunfd = -1; // tun interface file handle. (network device) int udpfd = -1; // UDP file handle int controlfd = -1; // Control signal handle int clifd = -1; // Socket listening for CLI connections. +int daefd = -1; // Socket listening for DAE connections. int snoopfd = -1; // UDP file handle for sending out intercept data int *radfds = NULL; // RADIUS requests file handles int ifrfd = -1; // File descriptor for routing, etc int ifr6fd = -1; // File descriptor for IPv6 routing, etc -static int rand_fd = -1; // Random data source +int rand_fd = -1; // Random data source +int cluster_sockfd = -1; // Intra-cluster communications socket. +int epollfd = -1; // event polling time_t basetime = 0; // base clock char hostname[1000] = ""; // us. static int tunidx; // ifr_ifindex of tun device static int syslog_log = 0; // are we logging to syslog static FILE *log_stream = 0; // file handle for direct logging (i.e. direct into file, not via syslog). -extern int cluster_sockfd; // Intra-cluster communications socket. uint32_t last_id = 0; // Unique ID for radius accounting struct cli_session_actions *cli_session_actions = NULL; // Pending session changes requested by CLI @@ -90,7 +92,7 @@ uint32_t eth_tx = 0; static uint32_t ip_pool_size = 1; // Size of the pool of addresses used for dynamic address allocation. time_t time_now = 0; // Current time in seconds since epoch. static char time_now_string[64] = {0}; // Current time as a string. -static char main_quit = 0; // True if we're in the process of exiting. +char main_quit = 0; // True if we're in the process of exiting. linked_list *loaded_plugins; linked_list *plugins[MAX_PLUGIN_TYPES]; @@ -103,6 +105,9 @@ config_descriptt config_values[] = { CONFIG("pid_file", pid_file, STRING), CONFIG("random_device", random_device, STRING), CONFIG("l2tp_secret", l2tpsecret, STRING), + CONFIG("ppp_restart_time", ppp_restart_time, INT), + CONFIG("ppp_max_configure", ppp_max_configure, INT), + CONFIG("ppp_max_failure", ppp_max_failure, INT), CONFIG("primary_dns", default_dns1, IPv4), CONFIG("secondary_dns", default_dns2, IPv4), CONFIG("primary_radius", radiusserver[0], IPv4), @@ -113,6 +118,8 @@ config_descriptt config_values[] = { CONFIG("radius_interim", radius_interim, INT), CONFIG("radius_secret", radiussecret, STRING), CONFIG("radius_authtypes", radius_authtypes_s, STRING), + CONFIG("radius_dae_port", radius_dae_port, SHORT), + CONFIG("allow_duplicate_users", allow_duplicate_users, BOOL), CONFIG("bind_address", bind_address, IPv4), CONFIG("peer_address", peer_address, IPv4), CONFIG("send_garp", send_garp, BOOL), @@ -121,7 +128,6 @@ config_descriptt config_values[] = { CONFIG("accounting_dir", accounting_dir, STRING), CONFIG("setuid", target_uid, INT), CONFIG("dump_speed", dump_speed, BOOL), - CONFIG("cleanup_interval", cleanup_interval, INT), CONFIG("multi_read_count", multi_read_count, INT), CONFIG("scheduler_fifo", scheduler_fifo, BOOL), CONFIG("lock_pages", lock_pages, BOOL), @@ -131,6 +137,7 @@ config_descriptt config_values[] = { CONFIG("cluster_interface", cluster_interface, STRING), CONFIG("cluster_hb_interval", cluster_hb_interval, INT), CONFIG("cluster_hb_timeout", cluster_hb_timeout, INT), + CONFIG("cluster_master_min_adv", cluster_master_min_adv, INT), CONFIG("ipv6_prefix", ipv6_prefix, IPv6), { NULL, 0, 0, 0 }, }; @@ -146,6 +153,7 @@ static char *plugin_functions[] = { "plugin_kill_session", "plugin_control", "plugin_radius_response", + "plugin_radius_reset", "plugin_become_master", "plugin_new_session_master", }; @@ -161,7 +169,7 @@ sessiont *session = NULL; // Array of session structures. 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. +ip_filtert *ip_filters = NULL; // Array of named filters. static controlt *controlfree = 0; struct Tstats *_statistics = NULL; #ifdef RINGBUFFER @@ -175,10 +183,9 @@ static void free_ip_address(sessionidt s); static void dump_acct_info(int all); static void sighup_handler(int sig); static void sigalrm_handler(int sig); -static void sigterm_handler(int sig); -static void sigquit_handler(int sig); +static void shutdown_handler(int sig); static void sigchild_handler(int sig); -static void build_chap_response(char *challenge, uint8_t id, uint16_t challenge_length, char **challenge_response); +static void build_chap_response(uint8_t *challenge, uint8_t id, uint16_t challenge_length, uint8_t **challenge_response); static void update_config(void); static void read_config_file(void); static void initplugins(void); @@ -189,11 +196,19 @@ static void processcontrol(uint8_t *buf, int len, struct sockaddr_in *addr, int static tunnelidt new_tunnel(void); static void unhide_value(uint8_t *value, size_t len, uint16_t type, uint8_t *vector, size_t vec_len); -// return internal time (10ths since process startup) -static clockt now(void) +// on slaves, alow BGP to withdraw cleanly before exiting +#define QUIT_DELAY 5 + +// quit actions (master) +#define QUIT_FAILOVER 1 // SIGTERM: exit when all control messages have been acked (for cluster failover) +#define QUIT_SHUTDOWN 2 // SIGQUIT: shutdown sessions/tunnels, reject new connections + +// return internal time (10ths since process startup), set f if given +static clockt now(double *f) { struct timeval t; gettimeofday(&t, 0); + if (f) *f = t.tv_sec + t.tv_usec / 1000000.0; return (t.tv_sec - basetime) * 10 + t.tv_usec / 100000 + 1; } @@ -203,7 +218,7 @@ static clockt now(void) clockt backoff(uint8_t try) { if (try > 5) try = 5; // max backoff - return now() + 10 * (1 << try); + return now(NULL) + 10 * (1 << try); } @@ -246,10 +261,10 @@ void _log(int level, sessionidt s, tunnelidt t, const char *format, ...) va_end(ap); } -void _log_hex(int level, const char *title, const char *data, int maxsize) +void _log_hex(int level, const char *title, const uint8_t *data, int maxsize) { int i, j; - const uint8_t *d = (const uint8_t *) data; + const uint8_t *d = data; if (config->debug < level) return; @@ -297,6 +312,16 @@ void _log_hex(int level, const char *title, const char *data, int maxsize) } } +// update a counter, accumulating 2^32 wraps +void increment_counter(uint32_t *counter, uint32_t *wrap, uint32_t delta) +{ + uint32_t new = *counter + delta; + if (new < *counter) + (*wrap)++; + + *counter = new; +} + // initialise the random generator static void initrandom(char *source) { @@ -307,7 +332,8 @@ static void initrandom(char *source) return; // close previous source, if any - if (rand_fd >= 0) close(rand_fd); + if (rand_fd >= 0) + close(rand_fd); rand_fd = -1; @@ -324,13 +350,6 @@ static void initrandom(char *source) path, strerror(errno)); } } - - // no source: seed prng - { - unsigned seed = time_now ^ getpid(); - LOG(4, 0, 0, "Seeding the pseudo random generator: %u\n", seed); - srand(seed); - } } // fill buffer with random data @@ -351,7 +370,7 @@ void random_data(uint8_t *buf, int len) strerror(errno)); // fall back to rand() - initrandom(0); + initrandom(NULL); } n = 0; @@ -535,7 +554,7 @@ static void inittun(void) tunidx = ifr.ifr_ifindex; // Only setup IPv6 on the tun device if we have a configured prefix - if (config->ipv6_prefix.s6_addr[0] > 0) { + if (config->ipv6_prefix.s6_addr[0]) { ifr6fd = socket(PF_INET6, SOCK_DGRAM, 0); // Link local address is FE80::1 @@ -565,7 +584,7 @@ static void inittun(void) } } -// set up UDP port +// set up UDP ports static void initudp(void) { int on = 1; @@ -587,7 +606,6 @@ static void initudp(void) LOG(0, 0, 0, "Error in UDP bind: %s\n", strerror(errno)); exit(1); } - snoopfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); // Control memset(&addr, 0, sizeof(addr)); @@ -600,6 +618,21 @@ static void initudp(void) LOG(0, 0, 0, "Error in control bind: %s\n", strerror(errno)); exit(1); } + + // Dynamic Authorization Extensions to RADIUS + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(config->radius_dae_port); + daefd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + setsockopt(daefd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (bind(daefd, (void *) &addr, sizeof(addr)) < 0) + { + LOG(0, 0, 0, "Error in DAE bind: %s\n", strerror(errno)); + exit(1); + } + + // Intercept + snoopfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); } // @@ -672,8 +705,11 @@ sessionidt sessionbyipv6(struct in6_addr ip) CSTAT(sessionbyipv6); if (!memcmp(&config->ipv6_prefix, &ip, 8) || - (ip.s6_addr[0] == 0xFE && ip.s6_addr[1] == 0x80 && - (ip.s6_addr16[1] == ip.s6_addr16[2] == ip.s6_addr16[3] == 0))) { + (ip.s6_addr[0] == 0xFE && + ip.s6_addr[1] == 0x80 && + ip.s6_addr16[1] == 0 && + ip.s6_addr16[2] == 0 && + ip.s6_addr16[3] == 0)) { s = lookup_ipmap(*(in_addr_t *) &ip.s6_addr[8]); } else { s = lookup_ipv6map(ip); @@ -943,14 +979,14 @@ int tun_write(uint8_t * data, int size) // process outgoing (to tunnel) IP // -static void processipout(uint8_t * buf, int len) +static void processipout(uint8_t *buf, int len) { sessionidt s; sessiont *sp; tunnelidt t; in_addr_t ip; - char *data = buf; // Keep a copy of the originals. + uint8_t *data = buf; // Keep a copy of the originals. int size = len; uint8_t b[MAXETHER + 20]; @@ -997,7 +1033,8 @@ static void processipout(uint8_t * buf, int len) if (rate++ < config->icmp_rate) // Only send a max of icmp_rate per second. { LOG(4, 0, 0, "IP: Sending ICMP host unreachable to %s\n", fmtaddr(*(in_addr_t *)(buf + 12), 0)); - host_unreachable(*(in_addr_t *)(buf + 12), *(uint16_t *)(buf + 4), ip, buf, (len < 64) ? 64 : len); + host_unreachable(*(in_addr_t *)(buf + 12), *(uint16_t *)(buf + 4), + config->bind_address ? config->bind_address : my_address, buf, len); } return; } @@ -1076,11 +1113,13 @@ static void processipout(uint8_t * buf, int len) if (sp->snoop_ip && sp->snoop_port) snoop_send_packet(buf, len, sp->snoop_ip, sp->snoop_port); - sp->cout += len; // byte count - sp->total_cout += len; // byte count + increment_counter(&sp->cout, &sp->cout_wrap, len); // byte count + sp->cout_delta += len; sp->pout++; udp_tx += len; + sess_local[s].cout += len; // To send to master.. + sess_local[s].pout++; } // process outgoing (to tunnel) IPv6 @@ -1093,7 +1132,7 @@ static void processipv6out(uint8_t * buf, int len) in_addr_t ip; struct in6_addr ip6; - char *data = buf; // Keep a copy of the originals. + uint8_t *data = buf; // Keep a copy of the originals. int size = len; uint8_t b[MAXETHER + 20]; @@ -1185,11 +1224,13 @@ static void processipv6out(uint8_t * buf, int len) if (sp->snoop_ip && sp->snoop_port) snoop_send_packet(buf, len, sp->snoop_ip, sp->snoop_port); - sp->cout += len; // byte count - sp->total_cout += len; // byte count + increment_counter(&sp->cout, &sp->cout_wrap, len); // byte count + sp->cout_delta += len; sp->pout++; udp_tx += len; + sess_local[s].cout += len; // To send to master.. + sess_local[s].pout++; } // @@ -1235,11 +1276,13 @@ static void send_ipout(sessionidt s, uint8_t *buf, int len) if (sp->snoop_ip && sp->snoop_port) snoop_send_packet(buf, len, sp->snoop_ip, sp->snoop_port); - sp->cout += len; // byte count - sp->total_cout += len; // byte count + increment_counter(&sp->cout, &sp->cout_wrap, len); // byte count + sp->cout_delta += len; sp->pout++; udp_tx += len; + sess_local[s].cout += len; // To send to master.. + sess_local[s].pout++; } // add an AVP (16 bit) @@ -1276,7 +1319,7 @@ static void controls(controlt * c, uint16_t avp, char *val, uint8_t m) } // add a binary AVP -static void controlb(controlt * c, uint16_t avp, char *val, unsigned int len, uint8_t m) +static void controlb(controlt * c, uint16_t avp, uint8_t *val, unsigned int len, uint8_t m) { uint16_t l = ((m ? 0x8000 : 0) + len + 6); *(uint16_t *) (c->buf + c->length + 0) = htons(l); @@ -1395,7 +1438,7 @@ void throttle_session(sessionidt s, int rate_in, int rate_out) } // add/remove filters from session (-1 = no change) -static void filter_session(sessionidt s, int filter_in, int filter_out) +void filter_session(sessionidt s, int filter_in, int filter_out) { if (!session[s].opened) return; // No-one home. @@ -1456,10 +1499,7 @@ void sessionshutdown(sessionidt s, char *reason, int result, int error) if (session[s].ip && !walled_garden && !session[s].die) { // RADIUS Stop message - uint16_t r = sess_local[s].radius; - if (!r) - r = radiusnew(s); - + uint16_t r = radiusnew(s); if (r) { // stop, if not already trying @@ -1497,7 +1537,7 @@ void sessionshutdown(sessionidt s, char *reason, int result, int error) free_ip_address(s); // unroute IPv6, if setup - if (session[s].flags & SF_IPV6_ROUTED) + if (session[s].ppp.ipv6cp == Opened && session[s].ipv6prefixlen) route6set(s, session[s].ipv6route, session[s].ipv6prefixlen, 0); } @@ -1509,7 +1549,7 @@ void sessionshutdown(sessionidt s, char *reason, int result, int error) controlt *c = controlnew(14); // sending CDN if (error) { - char buf[4]; + uint8_t buf[4]; *(uint16_t *) buf = htons(result); *(uint16_t *) (buf+2) = htons(error); controlb(c, 1, buf, 4, 1); @@ -1528,78 +1568,66 @@ void sessionshutdown(sessionidt s, char *reason, int result, int error) if (session[s].filter_in) ip_filters[session[s].filter_in - 1].used--; if (session[s].filter_out) ip_filters[session[s].filter_out - 1].used--; + // clear PPP state + memset(&session[s].ppp, 0, sizeof(session[s].ppp)); + sess_local[s].lcp.restart = 0; + sess_local[s].ipcp.restart = 0; + sess_local[s].ipv6cp.restart = 0; + sess_local[s].ccp.restart = 0; + cluster_send_session(s); } void sendipcp(tunnelidt t, sessionidt s) { uint8_t buf[MAXCONTROL]; - uint16_t r = sess_local[s].radius; uint8_t *q; CSTAT(sendipcp); - if (!r) - r = radiusnew(s); - - if (!r) - { - sessionshutdown(s, "No free RADIUS sessions for IPCP", 3, 0); - return; - } - - if (radius[r].state != RADIUSIPCP) - { - radius[r].state = RADIUSIPCP; - radius[r].try = 0; - } - - radius[r].retry = backoff(radius[r].try++); - if (radius[r].try > 10) + if (!session[s].unique_id) { - radiusclear(r, s); // Clear radius session. - sessionshutdown(s, "No reply to IPCP.", 3, 0); - return; + if (!++last_id) ++last_id; // skip zero + session[s].unique_id = last_id; } q = makeppp(buf,sizeof(buf), 0, 0, t, s, PPPIPCP); if (!q) return; *q = ConfigReq; - q[1] = r >> RADIUS_SHIFT; // ID, dont care, we only send one type of request - *(uint16_t *) (q + 2) = htons(10); - q[4] = 3; - q[5] = 6; + q[1] = session[s].unique_id & 0xf; // ID, dont care, we only send one type of request + *(uint16_t *) (q + 2) = htons(10); // packet length + q[4] = 3; // ip address option + q[5] = 6; // option length *(in_addr_t *) (q + 6) = config->peer_address ? config->peer_address : config->bind_address ? config->bind_address : my_address; // send my IP tunnelsend(buf, 10 + (q - buf), t); // send it - session[s].flags &= ~SF_IPCP_ACKED; // Clear flag. - - // If we have an IPv6 prefix length configured, assume we should - // try to negotiate an IPv6 session as well. Unless we've had a - // (N)ACK for IPV6CP. - if (config->ipv6_prefix.s6_addr[0] > 0 && - !(session[s].flags & SF_IPV6CP_ACKED) && - !(session[s].flags & SF_IPV6_NACKED)) - { - q = makeppp(buf,sizeof(buf), 0, 0, t, s, PPPIPV6CP); - if (!q) return; - - *q = ConfigReq; - q[1] = r >> RADIUS_SHIFT; // ID, don't care, we - // only send one type - // of request - *(uint16_t *) (q + 2) = htons(14); - q[4] = 1; - q[5] = 10; - *(uint32_t *) (q + 6) = 0; // We'll be prefix::1 - *(uint32_t *) (q + 10) = 0; - q[13] = 1; - - tunnelsend(buf, 14 + (q - buf), t); // send it - } +} + +void sendipv6cp(tunnelidt t, sessionidt s) +{ + uint8_t buf[MAXCONTROL]; + uint8_t *q; + + CSTAT(sendipv6cp); + + q = makeppp(buf,sizeof(buf), 0, 0, t, s, PPPIPV6CP); + if (!q) return; + + *q = ConfigReq; + q[1] = session[s].unique_id & 0xf; // ID, don't care, we + // only send one type + // of request + *(uint16_t *) (q + 2) = htons(14); + q[4] = 1; // interface identifier option + q[5] = 10; // option length + *(uint32_t *) (q + 6) = 0; // We'll be prefix::1 + *(uint32_t *) (q + 10) = 0; + q[13] = 1; + + tunnelsend(buf, 14 + (q - buf), t); // send it } static void sessionclear(sessionidt s) @@ -1694,7 +1722,7 @@ static void tunnelshutdown(tunnelidt t, char *reason, int result, int error, cha // close session for (s = 1; s <= config->cluster_highest_sessionid ; ++s) if (session[s].tunnel == t) - sessionshutdown(s, reason, 3, 0); + sessionshutdown(s, reason, 0, 0); tunnel[t].state = TUNNELDIE; tunnel[t].die = TIME + 700; // Clean up in 70 seconds @@ -1705,7 +1733,7 @@ static void tunnelshutdown(tunnelidt t, char *reason, int result, int error, cha controlt *c = controlnew(4); // sending StopCCN if (error) { - char buf[64]; + uint8_t buf[64]; int l = 4; *(uint16_t *) buf = htons(result); *(uint16_t *) (buf+2) = htons(error); @@ -1730,9 +1758,9 @@ static void tunnelshutdown(tunnelidt t, char *reason, int result, int error, cha } // read and process packet on tunnel (UDP) -void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) +void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) { - char *chapresponse = NULL; + uint8_t *chapresponse = NULL; uint16_t l = len, t = 0, s = 0, ns = 0, nr = 0; uint8_t *p = buf + 2; @@ -2004,8 +2032,6 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) continue; } - LOG(4, s, t, "Hidden AVP\n"); - // Unhide the AVP unhide_value(b, n, mtype, session[s].random_vector, session[s].random_vector_length); @@ -2026,13 +2052,15 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) n = orig_len; } - LOG(4, s, t, " AVP %d (%s) len %d\n", mtype, avp_name(mtype), n); + LOG(4, s, t, " AVP %d (%s) len %d%s%s\n", mtype, l2tp_avp_name(mtype), n, + flags & 0x40 ? ", hidden" : "", flags & 0x80 ? ", mandatory" : ""); + switch (mtype) { case 0: // message type message = ntohs(*(uint16_t *) b); mandatory = flags & 0x80; - LOG(4, s, t, " Message type = %d (%s)\n", *b, l2tp_message_type(message)); + LOG(4, s, t, " Message type = %d (%s)\n", *b, l2tp_code(message)); break; case 1: // result code { @@ -2040,18 +2068,18 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) const char* resdesc = "(unknown)"; if (message == 4) { /* StopCCN */ - resdesc = stopccn_result_code(rescode); + resdesc = l2tp_stopccn_result_code(rescode); } else if (message == 14) { /* CDN */ - resdesc = cdn_result_code(rescode); + resdesc = l2tp_cdn_result_code(rescode); } LOG(4, s, t, " Result Code %d: %s\n", rescode, resdesc); if (n >= 4) { uint16_t errcode = ntohs(*(uint16_t *)(b + 2)); - LOG(4, s, t, " Error Code %d: %s\n", errcode, error_code(errcode)); + LOG(4, s, t, " Error Code %d: %s\n", errcode, l2tp_error_code(errcode)); } if (n > 4) LOG(4, s, t, " Error String: %.*s\n", n-4, b+4); @@ -2185,7 +2213,7 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) case 29: // Proxy Authentication Type { uint16_t atype = ntohs(*(uint16_t *)b); - LOG(4, s, t, " Proxy Auth Type %d (%s)\n", atype, auth_type(atype)); + LOG(4, s, t, " Proxy Auth Type %d (%s)\n", atype, ppp_auth_type(atype)); if (atype == 2) authtype = AUTHCHAP; else if (atype == 3) @@ -2248,6 +2276,8 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) case 36: // Random Vector LOG(4, s, t, " Random Vector received. Enabled AVP Hiding.\n"); memset(session[s].random_vector, 0, sizeof(session[s].random_vector)); + if (n > sizeof(session[s].random_vector)) + n = sizeof(session[s].random_vector); memcpy(session[s].random_vector, b, n); session[s].random_vector_length = n; break; @@ -2270,6 +2300,8 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) switch (message) { case 1: // SCCRQ - Start Control Connection Request + tunnel[t].state = TUNNELOPENING; + if (main_quit != QUIT_SHUTDOWN) { controlt *c = controlnew(2); // sending SCCRP control16(c, 2, version, 1); // protocol version @@ -2279,7 +2311,10 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) control16(c, 9, t, 1); // assigned tunnel controladd(c, t, 0); // send the resply } - tunnel[t].state = TUNNELOPENING; + else + { + tunnelshutdown(t, "Shutting down", 6, 0, 0); + } break; case 2: // SCCRP tunnel[t].state = TUNNELOPEN; @@ -2305,9 +2340,9 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) // TBA break; case 10: // ICRQ - if (sessionfree) + if (sessionfree && main_quit != QUIT_SHUTDOWN) { - uint16_t r; + controlt *c = controlnew(11); // ICRP s = sessionfree; sessionfree = session[s].next; @@ -2316,39 +2351,36 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) if (s > config->cluster_highest_sessionid) config->cluster_highest_sessionid = s; - // make a RADIUS session - if ((r = radiusnew(s))) - { - controlt *c = controlnew(11); // sending ICRP - session[s].opened = time_now; - session[s].tunnel = t; - session[s].far = asession; - session[s].last_packet = time_now; - LOG(3, s, t, "New session (%d/%d)\n", tunnel[t].far, session[s].far); - control16(c, 14, s, 1); // assigned session - controladd(c, t, asession); // send the reply - - strncpy(radius[r].calling, calling, sizeof(radius[r].calling) - 1); - strncpy(session[s].called, called, sizeof(session[s].called) - 1); - strncpy(session[s].calling, calling, sizeof(session[s].calling) - 1); - STAT(session_created); - break; - } + session[s].opened = time_now; + session[s].tunnel = t; + session[s].far = asession; + session[s].last_packet = time_now; + LOG(3, s, t, "New session (%d/%d)\n", tunnel[t].far, session[s].far); + control16(c, 14, s, 1); // assigned session + controladd(c, t, asession); // send the reply + strncpy(session[s].called, called, sizeof(session[s].called) - 1); + strncpy(session[s].calling, calling, sizeof(session[s].calling) - 1); - LOG(1, s, t, "No free RADIUS sessions for ICRQ\n"); - sessionclear(s); - } - else - { - STAT(session_overflow); - LOG(1, 0, t, "No free sessions\n"); + session[s].ppp.phase = Establish; + session[s].ppp.lcp = Starting; + + STAT(session_created); + break; } { controlt *c = controlnew(14); // CDN - control16(c, 1, 4, 1); // temporary lack of resources - controladd(c, session[s].tunnel, asession); // send the message + if (!sessionfree) + { + STAT(session_overflow); + LOG(1, 0, t, "No free sessions\n"); + control16(c, 1, 4, 0); // temporary lack of resources + } + else + control16(c, 1, 2, 7); // shutting down, try another + + controladd(c, t, asession); // send the message } return; case 11: // ICRP @@ -2360,12 +2392,19 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) session[s].l2tp_flags = aflags; // set flags received LOG(3, s, t, "Magic %X Flags %X\n", amagic, aflags); controlnull(t); // ack + // proxy authentication type is not supported if (!(config->radius_authtypes & authtype)) authtype = config->radius_authprefer; // start LCP sendlcp(t, s, authtype); + sess_local[s].lcp.restart = time_now + config->ppp_restart_time; + sess_local[s].lcp.conf_sent = 1; + sess_local[s].lcp.nak_sent = 0; + sess_local[s].lcp_authtype = authtype; + session[s].ppp.lcp = RequestSent; + break; case 14: // CDN controlnull(t); // ack @@ -2459,16 +2498,9 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) } else if (prot == PPPIPV6CP) { - if (config->ipv6_prefix.s6_addr[0] > 0) - { - session[s].last_packet = time_now; - if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port); return; } - processipv6cp(t, s, p, l); - } - else - { - LOG(1, s, t, "IPv6 not configured; ignoring IPv6CP\n"); - } + session[s].last_packet = time_now; + if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port); return; } + processipv6cp(t, s, p, l); } else if (prot == PPPCCP) { @@ -2495,7 +2527,7 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) } else if (prot == PPPIPV6) { - if (!config->ipv6_prefix.s6_addr[0] > 0) + if (!config->ipv6_prefix.s6_addr[0]) { LOG(1, s, t, "IPv6 not configured; yet received IPv6 packet. Ignoring.\n"); return; @@ -2544,48 +2576,67 @@ static void processtun(uint8_t * buf, int len) if (*(uint16_t *) (buf + 2) == htons(PKTIP)) // IPv4 processipout(buf, len); else if (*(uint16_t *) (buf + 2) == htons(PKTIPV6) // IPV6 - && config->ipv6_prefix.s6_addr[0] > 0) + && config->ipv6_prefix.s6_addr[0]) processipv6out(buf, len); // Else discard. } -// -// Maximum number of actions to complete. -// This is to avoid sending out too many packets -// at once. -#define MAX_ACTIONS 500 - -static int regular_cleanups(void) +// Handle retries, timeouts. Runs every 1/10th sec, want to ensure +// that we look at the whole of the tunnel, radius and session tables +// every second +static void regular_cleanups(double period) { - static sessionidt s = 0; // Next session to check for actions on. - tunnelidt t; - int count=0,i; - uint16_t r; - static clockt next_acct = 0; - static clockt next_shut_acct = 0; + // Next tunnel, radius and session to check for actions on. + static tunnelidt t = 0; + static int r = 0; + static sessionidt s = 0; + + int t_actions = 0; + int r_actions = 0; + int s_actions = 0; + + int t_slice; + int r_slice; + int s_slice; + + int i; int a; - LOG(3, 0, 0, "Begin regular cleanup\n"); + // divide up tables into slices based on the last run + t_slice = config->cluster_highest_tunnelid * period; + r_slice = (MAXRADIUS - 1) * period; + s_slice = config->cluster_highest_sessionid * period; - for (r = 1; r < MAXRADIUS; r++) - { - if (!radius[r].state) - continue; - if (radius[r].retry) - { - if (radius[r].retry <= TIME) - radiusretry(r); - } else - radius[r].retry = backoff(radius[r].try+1); // Is this really needed? --mo - } - for (t = 1; t <= config->cluster_highest_tunnelid; t++) + if (t_slice < 1) + t_slice = 1; + else if (t_slice > config->cluster_highest_tunnelid) + t_slice = config->cluster_highest_tunnelid; + + if (r_slice < 1) + r_slice = 1; + else if (r_slice > (MAXRADIUS - 1)) + r_slice = MAXRADIUS - 1; + + if (s_slice < 1) + s_slice = 1; + else if (s_slice > config->cluster_highest_sessionid) + s_slice = config->cluster_highest_sessionid; + + LOG(4, 0, 0, "Begin regular cleanup (last %f seconds ago)\n", period); + + for (i = 0; i < t_slice; i++) { + t++; + if (t > config->cluster_highest_tunnelid) + t = 1; + // check for expired tunnels if (tunnel[t].die && tunnel[t].die <= TIME) { STAT(tunnel_timeout); tunnelkill(t, "Expired"); + t_actions++; continue; } // check for message resend @@ -2605,6 +2656,8 @@ static int regular_cleanups(void) tunnelsend(c->buf, c->length, t); c = c->next; } + + t_actions++; } } // Send hello @@ -2613,6 +2666,7 @@ static int regular_cleanups(void) controlt *c = controlnew(6); // sending HELLO controladd(c, t, 0); // send the message LOG(3, 0, t, "Sending HELLO message\n"); + t_actions++; } // Check for tunnel changes requested from the CLI @@ -2623,13 +2677,28 @@ static int regular_cleanups(void) { LOG(2, 0, t, "Dropping tunnel by CLI\n"); tunnelshutdown(t, "Requested by administrator", 1, 0, 0); + t_actions++; } } + } + + for (i = 0; i < r_slice; i++) + { + r++; + if (r >= MAXRADIUS) + r = 1; + if (!radius[r].state) + continue; + + if (radius[r].retry <= TIME) + { + radiusretry(r); + r_actions++; + } } - count = 0; - for (i = 1; i <= config->cluster_highest_sessionid; i++) + for (i = 0; i < s_slice; i++) { s++; if (s > config->cluster_highest_sessionid) @@ -2644,16 +2713,128 @@ static int regular_cleanups(void) if (session[s].die <= TIME) { sessionkill(s, "Expired"); - if (++count >= MAX_ACTIONS) break; + s_actions++; } continue; } - if (session[s].ip && !(session[s].flags & SF_IPCP_ACKED)) + // PPP timeouts + if (sess_local[s].lcp.restart >= time_now) + { + int next_state = session[s].ppp.lcp; + switch (session[s].ppp.lcp) + { + case RequestSent: + case AckReceived: + next_state = RequestSent; + + case AckSent: + if (sess_local[s].lcp.conf_sent < config->ppp_max_configure) + { + LOG(3, s, session[s].tunnel, "No ACK for LCP ConfigReq... resending\n"); + sess_local[s].lcp.restart = time_now + config->ppp_restart_time; + sess_local[s].lcp.conf_sent++; + sendlcp(t, s, sess_local[s].lcp_authtype); + change_state(s, lcp, next_state); + } + else + { + sessionshutdown(s, "No response to LCP ConfigReq.", 3, 0); + STAT(session_timeout); + } + + s_actions++; + } + + if (session[s].die) + continue; + } + + if (sess_local[s].ipcp.restart >= time_now) + { + int next_state = session[s].ppp.ipcp; + switch (session[s].ppp.ipcp) + { + case RequestSent: + case AckReceived: + next_state = RequestSent; + + case AckSent: + if (sess_local[s].ipcp.conf_sent < config->ppp_max_configure) + { + LOG(3, s, session[s].tunnel, "No ACK for IPCP ConfigReq... resending\n"); + sess_local[s].ipcp.restart = time_now + config->ppp_restart_time; + sess_local[s].ipcp.conf_sent++; + sendipcp(t, s); + change_state(s, ipcp, next_state); + } + else + { + sessionshutdown(s, "No response to IPCP ConfigReq.", 3, 0); + STAT(session_timeout); + } + + s_actions++; + } + + if (session[s].die) + continue; + } + + if (sess_local[s].ipv6cp.restart >= time_now) { - // IPCP has not completed yet. Resend - LOG(3, s, session[s].tunnel, "No ACK for initial IPCP ConfigReq... resending\n"); - sendipcp(session[s].tunnel, s); + int next_state = session[s].ppp.ipv6cp; + switch (session[s].ppp.ipv6cp) + { + case RequestSent: + case AckReceived: + next_state = RequestSent; + + case AckSent: + if (sess_local[s].ipv6cp.conf_sent < config->ppp_max_configure) + { + LOG(3, s, session[s].tunnel, "No ACK for IPV6CP ConfigReq... resending\n"); + sess_local[s].ipv6cp.restart = time_now + config->ppp_restart_time; + sess_local[s].ipv6cp.conf_sent++; + sendipv6cp(t, s); + change_state(s, ipv6cp, next_state); + } + else + { + LOG(3, s, session[s].tunnel, "No ACK for IPV6CP ConfigReq\n"); + change_state(s, ipv6cp, Stopped); + } + + s_actions++; + } + } + + if (sess_local[s].ccp.restart >= time_now) + { + int next_state = session[s].ppp.ccp; + switch (session[s].ppp.ccp) + { + case RequestSent: + case AckReceived: + next_state = RequestSent; + + case AckSent: + if (sess_local[s].ccp.conf_sent < config->ppp_max_configure) + { + LOG(3, s, session[s].tunnel, "No ACK for CCP ConfigReq... resending\n"); + sess_local[s].ccp.restart = time_now + config->ppp_restart_time; + sess_local[s].ccp.conf_sent++; + sendccp(t, s); + change_state(s, ccp, next_state); + } + else + { + LOG(3, s, session[s].tunnel, "No ACK for CCP ConfigReq\n"); + change_state(s, ccp, Stopped); + } + + s_actions++; + } } // Drop sessions who have not responded within IDLE_TIMEOUT seconds @@ -2661,12 +2842,12 @@ static int regular_cleanups(void) { sessionshutdown(s, "No response to LCP ECHO requests.", 3, 0); STAT(session_timeout); - if (++count >= MAX_ACTIONS) break; + s_actions++; continue; } // No data in ECHO_TIMEOUT seconds, send LCP ECHO - if (session[s].user[0] && (time_now - session[s].last_packet >= ECHO_TIMEOUT)) + if (session[s].ppp.phase >= Establish && (time_now - session[s].last_packet >= ECHO_TIMEOUT)) { uint8_t b[MAXCONTROL] = {0}; @@ -2681,7 +2862,7 @@ static int regular_cleanups(void) LOG(4, s, session[s].tunnel, "No data in %d seconds, sending LCP ECHO\n", (int)(time_now - session[s].last_packet)); tunnelsend(b, 24, session[s].tunnel); // send it - if (++count >= MAX_ACTIONS) break; + s_actions++; } // Check for actions requested from the CLI @@ -2695,6 +2876,7 @@ static int regular_cleanups(void) LOG(2, s, session[s].tunnel, "Dropping session by CLI\n"); sessionshutdown(s, "Requested by administrator.", 3, 0); a = 0; // dead, no need to check for other actions + s_actions++; } if (a & CLI_SESS_NOSNOOP) @@ -2702,6 +2884,7 @@ static int regular_cleanups(void) LOG(2, s, session[s].tunnel, "Unsnooping session by CLI\n"); session[s].snoop_ip = 0; session[s].snoop_port = 0; + s_actions++; send++; } else if (a & CLI_SESS_SNOOP) @@ -2712,6 +2895,7 @@ static int regular_cleanups(void) session[s].snoop_ip = cli_session_actions[s].snoop_ip; session[s].snoop_port = cli_session_actions[s].snoop_port; + s_actions++; send++; } @@ -2719,6 +2903,7 @@ static int regular_cleanups(void) { LOG(2, s, session[s].tunnel, "Un-throttling session by CLI\n"); throttle_session(s, 0, 0); + s_actions++; send++; } else if (a & CLI_SESS_THROTTLE) @@ -2728,6 +2913,7 @@ static int regular_cleanups(void) cli_session_actions[s].throttle_out); throttle_session(s, cli_session_actions[s].throttle_in, cli_session_actions[s].throttle_out); + s_actions++; send++; } @@ -2735,6 +2921,7 @@ static int regular_cleanups(void) { LOG(2, s, session[s].tunnel, "Un-filtering session by CLI\n"); filter_session(s, 0, 0); + s_actions++; send++; } else if (a & CLI_SESS_FILTER) @@ -2744,13 +2931,12 @@ static int regular_cleanups(void) cli_session_actions[s].filter_out); filter_session(s, cli_session_actions[s].filter_in, cli_session_actions[s].filter_out); + s_actions++; send++; } if (send) cluster_send_session(s); - - if (++count >= MAX_ACTIONS) break; } // RADIUS interim accounting @@ -2759,7 +2945,8 @@ static int regular_cleanups(void) && !sess_local[s].radius // RADIUS already in progress && time_now - sess_local[s].last_interim >= config->radius_interim) { - if (!(r = radiusnew(s))) + int rad = radiusnew(s); + if (!rad) { LOG(1, s, session[s].tunnel, "No free RADIUS sessions for Interim message\n"); STAT(radius_overflow); @@ -2769,40 +2956,16 @@ static int regular_cleanups(void) LOG(3, s, session[s].tunnel, "Sending RADIUS Interim for %s (%u)\n", session[s].user, session[s].unique_id); - radiussend(r, RADIUSINTERIM); + radiussend(rad, RADIUSINTERIM); sess_local[s].last_interim = time_now; - - if (++count >= MAX_ACTIONS) - break; + s_actions++; } } - if (*config->accounting_dir) - { - if (next_acct <= TIME) - { - // Dump accounting data - next_acct = TIME + ACCT_TIME; - next_shut_acct = TIME + ACCT_SHUT_TIME; - dump_acct_info(1); - } - else if (next_shut_acct <= TIME) - { - // Dump accounting data for shutdown sessions - next_shut_acct = TIME + ACCT_SHUT_TIME; - if (shut_acct_n) - dump_acct_info(0); - } - } - - if (count >= MAX_ACTIONS) - return 1; // Didn't finish! - - LOG(3, 0, 0, "End regular cleanup (%d actions), next in %d seconds\n", count, config->cleanup_interval); - return 0; + LOG(4, 0, 0, "End regular cleanup: checked %d/%d/%d tunnels/radius/sessions; %d/%d/%d actions\n", + t_slice, r_slice, s_slice, t_actions, r_actions, s_actions); } - // // Are we in the middle of a tunnel update, or radius // requests?? @@ -2812,6 +2975,53 @@ static int still_busy(void) int i; static clockt last_talked = 0; static clockt start_busy_wait = 0; + + if (!config->cluster_iam_master) + { +#ifdef BGP + static time_t stopped_bgp = 0; + if (bgp_configured) + { + if (!stopped_bgp) + { + LOG(1, 0, 0, "Shutting down in %d seconds, stopping BGP...\n", QUIT_DELAY); + + for (i = 0; i < BGP_NUM_PEERS; i++) + if (bgp_peers[i].state == Established) + bgp_stop(&bgp_peers[i]); + + stopped_bgp = time_now; + + // we don't want to become master + cluster_send_ping(0); + + return 1; + } + + if (time_now < (stopped_bgp + QUIT_DELAY)) + return 1; + } +#endif /* BGP */ + + return 0; + } + + if (main_quit == QUIT_SHUTDOWN) + { + static int dropped = 0; + if (!dropped) + { + int i; + + LOG(1, 0, 0, "Dropping sessions and tunnels\n"); + for (i = 1; i < MAXTUNNEL; i++) + if (tunnel[i].ip || tunnel[i].state) + tunnelshutdown(i, "L2TPNS Closing", 6, 0, 0); + + dropped = 1; + } + } + if (start_busy_wait == 0) start_busy_wait = TIME; @@ -2853,80 +3063,108 @@ static int still_busy(void) return 0; } -static fd_set readset; -static int readset_n = 0; +#ifdef HAVE_EPOLL +# include +#else +# define FAKE_EPOLL_IMPLEMENTATION /* include the functions */ +# include "fake_epoll.h" +#endif + +// the base set of fds polled: cli, cluster, tun, udp, control, dae +#define BASE_FDS 6 + +// additional polled fds +#ifdef BGP +# define EXTRA_FDS BGP_NUM_PEERS +#else +# define EXTRA_FDS 0 +#endif // main loop - gets packets on tun or udp and processes them static void mainloop(void) { int i; uint8_t buf[65536]; - struct timeval to; clockt next_cluster_ping = 0; // send initial ping immediately - time_t next_clean = time_now + config->cleanup_interval; - - LOG(4, 0, 0, "Beginning of main loop. udpfd=%d, tunfd=%d, cluster_sockfd=%d, controlfd=%d\n", - udpfd, tunfd, cluster_sockfd, controlfd); - - FD_ZERO(&readset); - FD_SET(udpfd, &readset); - FD_SET(tunfd, &readset); - FD_SET(controlfd, &readset); - FD_SET(clifd, &readset); - if (cluster_sockfd) FD_SET(cluster_sockfd, &readset); - readset_n = udpfd; - if (tunfd > readset_n) readset_n = tunfd; - if (controlfd > readset_n) readset_n = controlfd; - if (clifd > readset_n) readset_n = clifd; - if (cluster_sockfd > readset_n) readset_n = cluster_sockfd; + struct epoll_event events[BASE_FDS + RADIUS_FDS + EXTRA_FDS]; + int maxevent = sizeof(events)/sizeof(*events); - while (!main_quit || still_busy()) + if ((epollfd = epoll_create(maxevent)) < 0) { - fd_set r; - int n = readset_n; + LOG(0, 0, 0, "epoll_create failed: %s\n", strerror(errno)); + exit(1); + } + + LOG(4, 0, 0, "Beginning of main loop. clifd=%d, cluster_sockfd=%d, tunfd=%d, udpfd=%d, controlfd=%d, daefd=%d\n", + clifd, cluster_sockfd, tunfd, udpfd, controlfd, daefd); + + /* setup our fds to poll for input */ + { + static struct event_data d[BASE_FDS]; + struct epoll_event e; + + e.events = EPOLLIN; + i = 0; + + d[i].type = FD_TYPE_CLI; + e.data.ptr = &d[i++]; + epoll_ctl(epollfd, EPOLL_CTL_ADD, clifd, &e); + + d[i].type = FD_TYPE_CLUSTER; + e.data.ptr = &d[i++]; + epoll_ctl(epollfd, EPOLL_CTL_ADD, cluster_sockfd, &e); + + d[i].type = FD_TYPE_TUN; + e.data.ptr = &d[i++]; + epoll_ctl(epollfd, EPOLL_CTL_ADD, tunfd, &e); + + d[i].type = FD_TYPE_UDP; + e.data.ptr = &d[i++]; + epoll_ctl(epollfd, EPOLL_CTL_ADD, udpfd, &e); + + d[i].type = FD_TYPE_CONTROL; + e.data.ptr = &d[i++]; + epoll_ctl(epollfd, EPOLL_CTL_ADD, controlfd, &e); + + d[i].type = FD_TYPE_DAE; + e.data.ptr = &d[i++]; + epoll_ctl(epollfd, EPOLL_CTL_ADD, daefd, &e); + } + #ifdef BGP - fd_set w; - int bgp_set[BGP_NUM_PEERS]; + signal(SIGPIPE, SIG_IGN); + bgp_setup(config->as_number); + if (config->bind_address) + bgp_add_route(config->bind_address, 0xffffffff); + + for (i = 0; i < BGP_NUM_PEERS; i++) + { + if (config->neighbour[i].name[0]) + bgp_start(&bgp_peers[i], config->neighbour[i].name, + config->neighbour[i].as, config->neighbour[i].keepalive, + config->neighbour[i].hold, 0); /* 0 = routing disabled */ + } #endif /* BGP */ + while (!main_quit || still_busy()) + { + int more = 0; + int n; + if (config->reload_config) { // Update the config state based on config settings update_config(); } - memcpy(&r, &readset, sizeof(fd_set)); - to.tv_sec = 0; - to.tv_usec = 100000; // 1/10th of a second. - #ifdef BGP - FD_ZERO(&w); - for (i = 0; i < BGP_NUM_PEERS; i++) - { - bgp_set[i] = bgp_select_state(&bgp_peers[i]); - if (bgp_set[i] & 1) - { - FD_SET(bgp_peers[i].sock, &r); - if (bgp_peers[i].sock > n) - n = bgp_peers[i].sock; - } - - if (bgp_set[i] & 2) - { - FD_SET(bgp_peers[i].sock, &w); - if (bgp_peers[i].sock > n) - n = bgp_peers[i].sock; - } - } - - n = select(n + 1, &r, &w, 0, &to); -#else /* BGP */ - n = select(n + 1, &r, 0, 0, &to); + bgp_set_poll(); #endif /* BGP */ + n = epoll_wait(epollfd, events, maxevent, 100); // timeout 100ms (1/10th sec) STAT(select_called); - TIME = now(); + TIME = now(NULL); if (n < 0) { if (errno == EINTR || @@ -2934,70 +3172,92 @@ static void mainloop(void) continue; LOG(0, 0, 0, "Error returned from select(): %s\n", strerror(errno)); - main_quit++; - break; + break; // exit } - else if (n) + + if (n) { struct sockaddr_in addr; - int alen, c, s; + socklen_t alen; + int c, s; + int udp_ready = 0; + int tun_ready = 0; + int cluster_ready = 0; int udp_pkts = 0; int tun_pkts = 0; int cluster_pkts = 0; +#ifdef BGP + uint32_t bgp_events[BGP_NUM_PEERS]; + memset(bgp_events, 0, sizeof(bgp_events)); +#endif /* BGP */ - // nsctl commands - if (FD_ISSET(controlfd, &r)) - { - alen = sizeof(addr); - processcontrol(buf, recvfrom(controlfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr, alen); - n--; - } - - // RADIUS responses - if (config->cluster_iam_master) + for (c = n, i = 0; i < c; i++) { - for (i = 0; i < config->num_radfds; i++) + struct event_data *d = events[i].data.ptr; + switch (d->type) { - if (FD_ISSET(radfds[i], &r)) + case FD_TYPE_CLI: // CLI connections + { + int cli; + + alen = sizeof(addr); + if ((cli = accept(clifd, (struct sockaddr *)&addr, &alen)) >= 0) { - processrad(buf, recv(radfds[i], buf, sizeof(buf), 0), i); - n--; + cli_do(cli); + close(cli); } - } - } + else + LOG(0, 0, 0, "accept error: %s\n", strerror(errno)); - // CLI connections - if (FD_ISSET(clifd, &r)) - { - int cli; - - alen = sizeof(addr); - if ((cli = accept(clifd, (struct sockaddr *)&addr, &alen)) >= 0) - { - cli_do(cli); - close(cli); + n--; + break; } - else - LOG(0, 0, 0, "accept error: %s\n", strerror(errno)); - n--; - } + // these are handled below, with multiple interleaved reads + case FD_TYPE_CLUSTER: cluster_ready++; break; + case FD_TYPE_TUN: tun_ready++; break; + case FD_TYPE_UDP: udp_ready++; break; + + case FD_TYPE_CONTROL: // nsctl commands + alen = sizeof(addr); + processcontrol(buf, recvfrom(controlfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr, alen); + n--; + break; + + case FD_TYPE_DAE: // DAE requests + alen = sizeof(addr); + processdae(buf, recvfrom(daefd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr, alen); + n--; + break; + + case FD_TYPE_RADIUS: // RADIUS response + s = recv(radfds[d->index], buf, sizeof(buf), 0); + if (s >= 0 && config->cluster_iam_master) + processrad(buf, s, d->index); + + n--; + break; #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; - bgp_process(&bgp_peers[i], isr, isw); - if (isr) n--; - if (isw) n--; + case FD_TYPE_BGP: + bgp_events[d->index] = events[i].events; + n--; + break; +#endif /* BGP */ + + default: + LOG(0, 0, 0, "Unexpected fd type returned from epoll_wait: %d\n", d->type); + } } + +#ifdef BGP + bgp_process(bgp_events); #endif /* BGP */ for (c = 0; n && c < config->multi_read_count; c++) { // L2TP - if (FD_ISSET(udpfd, &r)) + if (udp_ready) { alen = sizeof(addr); if ((s = recvfrom(udpfd, buf, sizeof(buf), 0, (void *) &addr, &alen)) > 0) @@ -3007,13 +3267,13 @@ static void mainloop(void) } else { - FD_CLR(udpfd, &r); + udp_ready = 0; n--; } } // incoming IP - if (FD_ISSET(tunfd, &r)) + if (tun_ready) { if ((s = read(tunfd, buf, sizeof(buf))) > 0) { @@ -3022,13 +3282,13 @@ static void mainloop(void) } else { - FD_CLR(tunfd, &r); + tun_ready = 0; n--; } } // cluster - if (FD_ISSET(cluster_sockfd, &r)) + if (cluster_ready) { alen = sizeof(addr); if ((s = recvfrom(cluster_sockfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen)) > 0) @@ -3038,7 +3298,7 @@ static void mainloop(void) } else { - FD_CLR(cluster_sockfd, &r); + cluster_ready = 0; n--; } } @@ -3053,11 +3313,12 @@ static void mainloop(void) config->multi_read_count, udp_pkts, tun_pkts, cluster_pkts); STAT(multi_read_exceeded); + more++; } } // Runs on every machine (master and slaves). - if (cluster_sockfd && next_cluster_ping <= TIME) + if (next_cluster_ping <= TIME) { // Check to see which of the cluster is still alive.. @@ -3075,9 +3336,11 @@ static void mainloop(void) next_cluster_ping = TIME + config->cluster_hb_interval; } + if (!config->cluster_iam_master) + continue; + // Run token bucket filtering queue.. // Only run it every 1/10th of a second. - // Runs on all machines both master and slave. { static clockt last_run = 0; if (last_run != TIME) @@ -3087,20 +3350,42 @@ static void mainloop(void) } } - /* Handle timeouts. Make sure that this gets run anyway, even if there was - * something to read, else under load this will never actually run.... - * - */ - if (config->cluster_iam_master && next_clean <= time_now) + // Handle timeouts, retries etc. { - if (regular_cleanups()) + static double last_clean = 0; + double this_clean; + double diff; + + TIME = now(&this_clean); + diff = this_clean - last_clean; + + // Run during idle time (after we've handled + // all incoming packets) or every 1/10th sec + if (!more || diff > 0.1) { - // Did it finish? - next_clean = time_now + 1 ; // Didn't finish. Check quickly. + regular_cleanups(diff); + last_clean = this_clean; } - else + } + + if (*config->accounting_dir) + { + static clockt next_acct = 0; + static clockt next_shut_acct = 0; + + if (next_acct <= TIME) + { + // Dump accounting data + next_acct = TIME + ACCT_TIME; + next_shut_acct = TIME + ACCT_SHUT_TIME; + dump_acct_info(1); + } + else if (next_shut_acct <= TIME) { - next_clean = time_now + config->cleanup_interval; // Did. Move to next interval. + // Dump accounting data for shutdown sessions + next_shut_acct = TIME + ACCT_SHUT_TIME; + if (shut_acct_n) + dump_acct_info(0); } } } @@ -3115,6 +3400,7 @@ static void mainloop(void) // // Important!!! We MUST not process any packets past this point! + LOG(1, 0, 0, "Shutdown complete\n"); } static void stripdomain(char *host) @@ -3199,6 +3485,10 @@ static void initdata(int optdebug, char *optconfig) config->debug = optdebug; config->num_tbfs = MAXTBFS; config->rl_rate = 28; // 28kbps + config->cluster_master_min_adv = 1; + config->ppp_restart_time = 3; + config->ppp_max_configure = 10; + config->ppp_max_failure = 5; strcpy(config->random_device, RANDOMDEVICE); log_stream = stderr; @@ -3566,7 +3856,7 @@ static void initippool() LOG(1, 0, 0, "IP address pool is %d addresses\n", ip_pool_size - 1); } -void snoop_send_packet(char *packet, uint16_t size, in_addr_t destination, uint16_t port) +void snoop_send_packet(uint8_t *packet, uint16_t size, in_addr_t destination, uint16_t port) { struct sockaddr_in snoop_addr = {0}; if (!destination || !port || snoopfd <= 0 || size <= 0 || !packet) @@ -3588,7 +3878,7 @@ void snoop_send_packet(char *packet, uint16_t size, in_addr_t destination, uint1 static int dump_session(FILE **f, sessiont *s) { - if (!s->opened || !s->ip || !(s->cin || s->cout) || !*s->user || s->walled_garden) + if (!s->opened || !s->ip || !(s->cin_delta || s->cout_delta) || !*s->user || s->walled_garden) return 1; if (!*f) @@ -3609,10 +3899,12 @@ static int dump_session(FILE **f, sessiont *s) LOG(3, 0, 0, "Dumping accounting information to %s\n", filename); fprintf(*f, "# dslwatch.pl dump file V1.01\n" "# host: %s\n" + "# endpoint: %s\n" "# time: %ld\n" "# uptime: %ld\n" "# format: username ip qos uptxoctets downrxoctets\n", hostname, + fmtaddr(config->bind_address ? config->bind_address : my_address, 0), now, now - basetime); } @@ -3622,11 +3914,10 @@ static int dump_session(FILE **f, sessiont *s) s->user, // username fmtaddr(htonl(s->ip), 0), // ip (s->throttle_in || s->throttle_out) ? 2 : 1, // qos - (uint32_t) s->cin, // uptxoctets - (uint32_t) s->cout); // downrxoctets + (uint32_t) s->cin_delta, // uptxoctets + (uint32_t) s->cout_delta); // downrxoctets - s->pin = s->cin = 0; - s->pout = s->cout = 0; + s->cin_delta = s->cout_delta = 0; return 1; } @@ -3754,19 +4045,6 @@ int main(int argc, char *argv[]) if (cluster_init() < 0) exit(1); -#ifdef BGP - signal(SIGPIPE, SIG_IGN); - bgp_setup(config->as_number); - bgp_add_route(config->bind_address, 0xffffffff); - for (i = 0; i < BGP_NUM_PEERS; i++) - { - if (config->neighbour[i].name[0]) - bgp_start(&bgp_peers[i], config->neighbour[i].name, - config->neighbour[i].as, config->neighbour[i].keepalive, - config->neighbour[i].hold, 0); /* 0 = routing disabled */ - } -#endif /* BGP */ - inittun(); LOG(1, 0, 0, "Set up on interface %s\n", config->tundevice); @@ -3774,11 +4052,18 @@ int main(int argc, char *argv[]) initrad(); initippool(); - signal(SIGHUP, sighup_handler); - signal(SIGTERM, sigterm_handler); - signal(SIGINT, sigterm_handler); - signal(SIGQUIT, sigquit_handler); + // seed prng + { + unsigned seed = time_now ^ getpid(); + LOG(4, 0, 0, "Seeding the pseudo random generator: %u\n", seed); + srand(seed); + } + + signal(SIGHUP, sighup_handler); signal(SIGCHLD, sigchild_handler); + signal(SIGTERM, shutdown_handler); + signal(SIGINT, shutdown_handler); + signal(SIGQUIT, shutdown_handler); // Prevent us from getting paged out if (config->lock_pages) @@ -3797,14 +4082,6 @@ int main(int argc, char *argv[]) mainloop(); -#ifdef BGP - /* try to shut BGP down cleanly; with luck the sockets will be - writable since we're out of the select */ - for (i = 0; i < BGP_NUM_PEERS; i++) - if (bgp_peers[i].state == Established) - bgp_stop(&bgp_peers[i]); -#endif /* BGP */ - /* remove plugins (so cleanup code gets run) */ plugins_done(); @@ -3864,33 +4141,10 @@ static void sigalrm_handler(int sig) } -static void sigterm_handler(int sig) +static void shutdown_handler(int sig) { - LOG(1, 0, 0, "Shutting down cleanly\n"); - main_quit++; -} - -static void sigquit_handler(int sig) -{ - int i; - - LOG(1, 0, 0, "Shutting down without saving sessions\n"); - - if (config->cluster_iam_master) - { - for (i = 1; i < MAXSESSION; i++) - { - if (session[i].opened) - sessionkill(i, "L2TPNS Closing"); - } - for (i = 1; i < MAXTUNNEL; i++) - { - if (tunnel[i].ip || tunnel[i].state) - tunnelshutdown(i, "L2TPNS Closing", 6, 0, 0); - } - } - - main_quit++; + LOG(1, 0, 0, "Shutting down\n"); + main_quit = (sig == SIGQUIT) ? QUIT_SHUTDOWN : QUIT_FAILOVER; } static void sigchild_handler(int sig) @@ -3899,7 +4153,7 @@ static void sigchild_handler(int sig) ; } -static void build_chap_response(char *challenge, uint8_t id, uint16_t challenge_length, char **challenge_response) +static void build_chap_response(uint8_t *challenge, uint8_t id, uint16_t challenge_length, uint8_t **challenge_response) { MD5_CTX ctx; *challenge_response = NULL; @@ -3912,13 +4166,13 @@ static void build_chap_response(char *challenge, uint8_t id, uint16_t challenge_ LOG(4, 0, 0, " Building challenge response for CHAP request\n"); - *challenge_response = (char *)calloc(17, 1); + *challenge_response = calloc(17, 1); - MD5Init(&ctx); - MD5Update(&ctx, &id, 1); - MD5Update(&ctx, config->l2tpsecret, strlen(config->l2tpsecret)); - MD5Update(&ctx, challenge, challenge_length); - MD5Final(*challenge_response, &ctx); + MD5_Init(&ctx); + MD5_Update(&ctx, &id, 1); + MD5_Update(&ctx, config->l2tpsecret, strlen(config->l2tpsecret)); + MD5_Update(&ctx, challenge, challenge_length); + MD5_Final(*challenge_response, &ctx); return; } @@ -4005,8 +4259,6 @@ static void update_config() if (!config->numradiusservers) LOG(0, 0, 0, "No RADIUS servers defined!\n"); - config->num_radfds = 1 << RADIUS_SHIFT; - // parse radius_authtypes_s config->radius_authtypes = config->radius_authprefer = 0; p = config->radius_authtypes_s; @@ -4059,6 +4311,9 @@ static void update_config() strcat(config->radius_authtypes_s, ", pap"); } + if (!config->radius_dae_port) + config->radius_dae_port = DAEPORT; + // re-initialise the random number source initrandom(config->random_device); @@ -4081,7 +4336,6 @@ static void update_config() } memcpy(config->old_plugins, config->plugins, sizeof(config->plugins)); - if (!config->cleanup_interval) config->cleanup_interval = 10; if (!config->multi_read_count) config->multi_read_count = 10; if (!config->cluster_address) config->cluster_address = inet_addr(DEFAULT_MCAST_ADDR); if (!*config->cluster_interface) @@ -4168,7 +4422,7 @@ int sessionsetup(tunnelidt t, sessionidt s) if (!session[s].ip) { LOG(0, s, t, " No IP allocated. The IP address pool is FULL!\n"); - sessionshutdown(s, "No IP addresses available.", 2, 7); + sessionshutdown(s, "No IP addresses available.", 2, 7); // try another return 0; } LOG(3, s, t, " No IP allocated. Assigned %s from pool\n", @@ -4188,8 +4442,16 @@ int sessionsetup(tunnelidt t, sessionidt s) for (i = 1; i <= config->cluster_highest_sessionid; i++) { if (i == s) continue; - if (ip == session[i].ip) sessionkill(i, "Duplicate IP address"); - if (!session[s].walled_garden && !session[i].walled_garden && strcasecmp(user, session[i].user) == 0) + if (!session[s].opened) continue; + if (ip == session[i].ip) + { + sessionkill(i, "Duplicate IP address"); + continue; + } + + if (config->allow_duplicate_users) continue; + if (session[s].walled_garden || session[i].walled_garden) continue; + if (!strcasecmp(user, session[i].user)) sessionkill(i, "Duplicate session for users"); } } @@ -4220,13 +4482,8 @@ int sessionsetup(tunnelidt t, sessionidt s) cache_ipmap(session[s].ip, s); } - if (!session[s].unique_id) - { - // did this session just finish radius? - LOG(3, s, t, "Sending initial IPCP to client\n"); - sendipcp(t, s); - session[s].unique_id = ++last_id; - } + sess_local[s].lcp_authtype = 0; // RADIUS authentication complete + lcp_open(t, s); // transition to Network phase and send initial IPCP // Run the plugin's against this new session. { @@ -4335,7 +4592,7 @@ int load_session(sessionidt s, sessiont *new) } // check v6 routing - if (new->flags & SF_IPV6_ROUTED && !(session[s].flags & SF_IPV6_ROUTED)) + if (new->ipv6prefixlen && new->ppp.ipv6cp == Opened && session[s].ppp.ipv6cp != Opened) route6set(s, new->ipv6route, new->ipv6prefixlen, 1); // check filters @@ -4367,12 +4624,6 @@ int load_session(sessionidt s, sessiont *new) // for walking the sessions to forward byte counts to the master. config->cluster_highest_sessionid = s; - // TEMP: old session struct used a uint32_t to define the throttle - // speed for both up/down, new uses a uint16_t for each. Deal with - // sessions from an old master for migration. - if (new->throttle_out == 0 && new->tbf_out) - new->throttle_out = new->throttle_in; - memcpy(&session[s], new, sizeof(session[s])); // Copy over.. // Do fixups into address pool. @@ -4558,7 +4809,7 @@ static void plugins_done() run_plugin_done(p); } -static void processcontrol(uint8_t * buf, int len, struct sockaddr_in *addr, int alen) +static void processcontrol(uint8_t *buf, int len, struct sockaddr_in *addr, int alen) { struct nsctl request; struct nsctl response; @@ -4757,6 +5008,9 @@ static tunnelidt new_tunnel() void become_master(void) { int s, i; + static struct event_data d[RADIUS_FDS]; + struct epoll_event e; + run_plugins(PLUGIN_BECOME_MASTER, NULL); // running a bunch of iptables commands is slow and can cause @@ -4775,11 +5029,14 @@ void become_master(void) } // add radius fds - for (i = 0; i < config->num_radfds; i++) + e.events = EPOLLIN; + for (i = 0; i < RADIUS_FDS; i++) { - FD_SET(radfds[i], &readset); - if (radfds[i] > readset_n) - readset_n = radfds[i]; + d[i].type = FD_TYPE_RADIUS; + d[i].index = i; + e.data.ptr = &d[i]; + + epoll_ctl(epollfd, EPOLL_CTL_ADD, radfds[i], &e); } } @@ -4873,14 +5130,14 @@ static void unhide_value(uint8_t *value, size_t len, uint16_t type, uint8_t *vec uint8_t digest[16]; uint8_t *last; size_t d = 0; + uint16_t m = htons(type); // Compute initial pad - MD5Init(&ctx); - MD5Update(&ctx, (uint8_t) (type >> 8) & 0xff, 1); - MD5Update(&ctx, (uint8_t) type & 0xff, 1); - MD5Update(&ctx, config->l2tpsecret, strlen(config->l2tpsecret)); - MD5Update(&ctx, vector, vec_len); - MD5Final(digest, &ctx); + MD5_Init(&ctx); + MD5_Update(&ctx, (unsigned char *) &m, 2); + MD5_Update(&ctx, config->l2tpsecret, strlen(config->l2tpsecret)); + MD5_Update(&ctx, vector, vec_len); + MD5_Final(digest, &ctx); // pointer to last decoded 16 octets last = value; @@ -4890,10 +5147,10 @@ static void unhide_value(uint8_t *value, size_t len, uint16_t type, uint8_t *vec // calculate a new pad based on the last decoded block if (d >= sizeof(digest)) { - MD5Init(&ctx); - MD5Update(&ctx, config->l2tpsecret, strlen(config->l2tpsecret)); - MD5Update(&ctx, last, sizeof(digest)); - MD5Final(digest, &ctx); + MD5_Init(&ctx); + MD5_Update(&ctx, config->l2tpsecret, strlen(config->l2tpsecret)); + MD5_Update(&ctx, last, sizeof(digest)); + MD5_Final(digest, &ctx); d = 0; last = value; @@ -4904,6 +5161,31 @@ static void unhide_value(uint8_t *value, size_t len, uint16_t type, uint8_t *vec } } +int find_filter(char const *name, size_t len) +{ + int free = -1; + int i; + + for (i = 0; i < MAXFILTER; i++) + { + if (!*ip_filters[i].name) + { + if (free < 0) + free = i; + + continue; + } + + if (strlen(ip_filters[i].name) != len) + continue; + + if (!strncmp(ip_filters[i].name, name, len)) + return i; + } + + return free; +} + static int ip_filter_port(ip_filter_portt *p, uint16_t port) { switch (p->op)