X-Git-Url: http://git.sameswireless.fr/l2tpns.git/blobdiff_plain/2b05f31dc724539454f67ea09fb2bd04e5a51e40..65270c4bd287b5a27c567ff1ebb595738d466b4d:/l2tpns.c?ds=sidebyside diff --git a/l2tpns.c b/l2tpns.c index 384e7ca..0795866 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -1,10 +1,10 @@ // L2TP Network Server // Adrian Kennard 2002 -// Copyright (c) 2003, 2004, 2005 Optus Internet Engineering +// Copyright (c) 2003, 2004, 2005, 2006 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.128 2005-09-02 23:59:56 bodea Exp $"; +char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.167 2006-06-11 12:46:18 bodea Exp $"; #include #include @@ -75,6 +75,10 @@ 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). uint32_t last_id = 0; // Unique ID for radius accounting +// calculated from config->l2tp_mtu +uint16_t MRU = 0; // PPP MRU +uint16_t MSS = 0; // TCP MSS + struct cli_session_actions *cli_session_actions = NULL; // Pending session changes requested by CLI struct cli_tunnel_actions *cli_tunnel_actions = NULL; // Pending tunnel changes required by CLI @@ -92,7 +96,9 @@ 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 int time_changed = 0; // time_now changed char main_quit = 0; // True if we're in the process of exiting. +static char main_reload = 0; // Re-load pending linked_list *loaded_plugins; linked_list *plugins[MAX_PLUGIN_TYPES]; @@ -104,7 +110,8 @@ config_descriptt config_values[] = { CONFIG("log_file", log_filename, STRING), CONFIG("pid_file", pid_file, STRING), CONFIG("random_device", random_device, STRING), - CONFIG("l2tp_secret", l2tpsecret, STRING), + CONFIG("l2tp_secret", l2tp_secret, STRING), + CONFIG("l2tp_mtu", l2tp_mtu, INT), CONFIG("ppp_restart_time", ppp_restart_time, INT), CONFIG("ppp_max_configure", ppp_max_configure, INT), CONFIG("ppp_max_failure", ppp_max_failure, INT), @@ -120,13 +127,13 @@ config_descriptt config_values[] = { CONFIG("radius_authtypes", radius_authtypes_s, STRING), CONFIG("radius_dae_port", radius_dae_port, SHORT), CONFIG("allow_duplicate_users", allow_duplicate_users, BOOL), + CONFIG("guest_account", guest_user, STRING), CONFIG("bind_address", bind_address, IPv4), CONFIG("peer_address", peer_address, IPv4), CONFIG("send_garp", send_garp, BOOL), CONFIG("throttle_speed", rl_rate, UNSIGNED_LONG), CONFIG("throttle_buckets", num_tbfs, INT), CONFIG("accounting_dir", accounting_dir, STRING), - CONFIG("setuid", target_uid, INT), CONFIG("dump_speed", dump_speed, BOOL), CONFIG("multi_read_count", multi_read_count, INT), CONFIG("scheduler_fifo", scheduler_fifo, BOOL), @@ -155,6 +162,7 @@ static char *plugin_functions[] = { "plugin_control", "plugin_radius_response", "plugin_radius_reset", + "plugin_radius_account", "plugin_become_master", "plugin_new_session_master", }; @@ -166,6 +174,8 @@ static sessiont shut_acct[8192]; static sessionidt shut_acct_n = 0; tunnelt *tunnel = NULL; // Array of tunnel structures. +bundlet *bundle = NULL; // Array of bundle structures. +fragmentationt *frag = NULL; // Array of fragmentation structures. sessiont *session = NULL; // Array of session structures. sessionlocalt *sess_local = NULL; // Array of local per-session counters. radiust *radius = NULL; // Array of radius structures. @@ -183,7 +193,6 @@ static void cache_ipv6map(struct in6_addr ip, int prefixlen, int s); 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 shutdown_handler(int sig); static void sigchild_handler(int sig); static void build_chap_response(uint8_t *challenge, uint8_t id, uint16_t challenge_length, uint8_t **challenge_response); @@ -193,9 +202,10 @@ static void initplugins(void); static int add_plugin(char *plugin_name); static int remove_plugin(char *plugin_name); static void plugins_done(void); -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 in_addr *local); 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); +static void bundleclear(bundleidt b); // on slaves, alow BGP to withdraw cleanly before exiting #define QUIT_DELAY 5 @@ -205,11 +215,17 @@ static void unhide_value(uint8_t *value, size_t len, uint16_t type, uint8_t *vec #define QUIT_SHUTDOWN 2 // SIGQUIT: shutdown sessions/tunnels, reject new connections // return internal time (10ths since process startup), set f if given +// as a side-effect sets time_now, and time_changed static clockt now(double *f) { struct timeval t; gettimeofday(&t, 0); if (f) *f = t.tv_sec + t.tv_usec / 1000000.0; + if (t.tv_sec != time_now) + { + time_now = t.tv_sec; + time_changed++; + } return (t.tv_sec - basetime) * 10 + t.tv_usec / 100000 + 1; } @@ -541,6 +557,13 @@ static void inittun(void) LOG(0, 0, 0, "Error setting tun queue length: %s\n", strerror(errno)); exit(1); } + /* set MTU to modem MRU */ + ifr.ifr_mtu = MRU; + if (ioctl(ifrfd, SIOCSIFMTU, (void *) &ifr) < 0) + { + LOG(0, 0, 0, "Error setting tun MTU: %s\n", strerror(errno)); + exit(1); + } ifr.ifr_flags = IFF_UP; if (ioctl(ifrfd, SIOCSIFFLAGS, (void *) &ifr) < 0) { @@ -614,6 +637,7 @@ static void initudp(void) addr.sin_port = htons(NSCTL_PORT); controlfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); setsockopt(controlfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + setsockopt(controlfd, SOL_IP, IP_PKTINFO, &on, sizeof(on)); // recvfromto if (bind(controlfd, (void *) &addr, sizeof(addr)) < 0) { LOG(0, 0, 0, "Error in control bind: %s\n", strerror(errno)); @@ -626,6 +650,7 @@ static void initudp(void) 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)); + setsockopt(daefd, SOL_IP, IP_PKTINFO, &on, sizeof(on)); // recvfromto if (bind(daefd, (void *) &addr, sizeof(addr)) < 0) { LOG(0, 0, 0, "Error in DAE bind: %s\n", strerror(errno)); @@ -920,18 +945,14 @@ void tunnelsend(uint8_t * buf, uint16_t l, tunnelidt t) if (!t) { - static int backtrace_count = 0; LOG(0, 0, t, "tunnelsend called with 0 as tunnel id\n"); STAT(tunnel_tx_errors); - log_backtrace(backtrace_count, 5) return; } if (!tunnel[t].ip) { - static int backtrace_count = 0; LOG(1, 0, t, "Error sending data out tunnel: no remote endpoint (tunnel not set up)\n"); - log_backtrace(backtrace_count, 5) STAT(tunnel_tx_errors); return; } @@ -949,7 +970,7 @@ void tunnelsend(uint8_t * buf, uint16_t l, tunnelidt t) { tunnel[t].last = time_now; // control message sent tunnel[t].retry = backoff(tunnel[t].try); // when to resend - if (tunnel[t].try > 1) + if (tunnel[t].try) { STAT(tunnel_retries); LOG(3, 0, t, "Control message resend try %d\n", tunnel[t].try); @@ -978,6 +999,111 @@ int tun_write(uint8_t * data, int size) return write(tunfd, data, size); } +// adjust tcp mss to avoid fragmentation (called only for tcp packets with syn set) +void adjust_tcp_mss(sessionidt s, tunnelidt t, uint8_t *buf, int len, uint8_t *tcp) +{ + int d = (tcp[12] >> 4) * 4; + uint8_t *mss = 0; + uint8_t *opts; + uint8_t *data; + uint16_t orig; + uint32_t sum; + + if ((tcp[13] & 0x3f) & ~(TCP_FLAG_SYN|TCP_FLAG_ACK)) // only want SYN and SYN,ACK + return; + + if (tcp + d > buf + len) // short? + return; + + opts = tcp + 20; + data = tcp + d; + + while (opts < data) + { + if (*opts == 2 && opts[1] == 4) // mss option (2), length 4 + { + mss = opts + 2; + if (mss + 2 > data) return; // short? + break; + } + + if (*opts == 0) return; // end of options + if (*opts == 1 || !opts[1]) // no op (one byte), or no length (prevent loop) + opts++; + else + opts += opts[1]; // skip over option + } + + if (!mss) return; // not found + orig = ntohs(*(uint16_t *) mss); + + if (orig <= MSS) return; // mss OK + + LOG(5, s, t, "TCP: %s:%u -> %s:%u SYN%s: adjusted mss from %u to %u\n", + fmtaddr(*(in_addr_t *) (buf + 12), 0), ntohs(*(uint16_t *) tcp), + fmtaddr(*(in_addr_t *) (buf + 16), 1), ntohs(*(uint16_t *) (tcp + 2)), + (tcp[13] & TCP_FLAG_ACK) ? ",ACK" : "", orig, MSS); + + // set mss + *(int16_t *) mss = htons(MSS); + + // adjust checksum (see rfc1141) + sum = orig + (~MSS & 0xffff); + sum += ntohs(*(uint16_t *) (tcp + 16)); + sum = (sum & 0xffff) + (sum >> 16); + *(uint16_t *) (tcp + 16) = htons(sum + (sum >> 16)); +} + +void processmpframe(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l, uint8_t extra) +{ + uint16_t proto; + if (extra) { + // Skip the four extra bytes + p += 4; + l -= 4; + } + + // Process this frame + if (*p & 1) + { + proto = *p++; + l--; + } + else + { + proto = ntohs(*(uint16_t *) p); + p += 2; + l -= 2; + } + + if (proto == PPPIP) + { + if (session[s].die) + { + LOG(4, s, t, "MPPP: Session %u is closing. Don't process PPP packets\n", s); + return; // closing session, PPP not processed + } + + session[s].last_packet = session[s].last_data = time_now; + processipin(s, t, p, l); + } + else if (proto == PPPIPV6 && config->ipv6_prefix.s6_addr[0]) + { + if (session[s].die) + { + LOG(4, s, t, "MPPP: Session %u is closing. Don't process PPP packets\n", s); + return; // closing session, PPP not processed + } + + session[s].last_packet = session[s].last_data = time_now; + processipv6in(s, t, p, l); + } + else + { + LOG(2, s, t, "MPPP: Unsupported MP protocol 0x%04X received\n",proto); + } +} + // process outgoing (to tunnel) IP // static void processipout(uint8_t *buf, int len) @@ -990,7 +1116,8 @@ static void processipout(uint8_t *buf, int len) uint8_t *data = buf; // Keep a copy of the originals. int size = len; - uint8_t b[MAXETHER + 20]; + uint8_t b1[MAXETHER + 20]; + uint8_t b2[MAXETHER + 20]; CSTAT(processipout); @@ -1041,6 +1168,7 @@ static void processipout(uint8_t *buf, int len) } t = session[s].tunnel; sp = &session[s]; + sp->last_data = time_now; // DoS prevention: enforce a maximum number of packets per 0.1s for a session if (config->max_packets > 0) @@ -1085,6 +1213,14 @@ static void processipout(uint8_t *buf, int len) if (session[s].filter_out && !ip_filter(buf, len, session[s].filter_out - 1)) return; + // adjust MSS on SYN and SYN,ACK packets with options + if ((ntohs(*(uint16_t *) (buf + 6)) & 0x1fff) == 0 && buf[9] == IPPROTO_TCP) // first tcp fragment + { + int ihl = (buf[0] & 0xf) * 4; // length of IP header + if (len >= ihl + 20 && (buf[ihl + 13] & TCP_FLAG_SYN) && ((buf[ihl + 12] >> 4) > 5)) + adjust_tcp_mss(s, t, buf, len, buf + ihl); + } + if (sp->tbf_out) { // Are we throttling this session? @@ -1094,20 +1230,51 @@ static void processipout(uint8_t *buf, int len) master_throttle_packet(sp->tbf_out, data, size); return; } - else if (sp->walled_garden && !config->cluster_iam_master) + + if (sp->walled_garden && !config->cluster_iam_master) { // We are walled-gardening this master_garden_packet(s, data, size); return; } - LOG(5, s, t, "Ethernet -> Tunnel (%d bytes)\n", len); - // Add on L2TP header { - uint8_t *p = makeppp(b, sizeof(b), buf, len, s, t, PPPIP); - if (!p) return; - tunnelsend(b, len + (p-b), t); // send it... + bundleidt bid = 0; + if (session[s].bundle && bundle[session[s].bundle].num_of_links > 1) + { + bid = session[s].bundle; + s = bundle[bid].members[bundle[bid].current_ses = ++bundle[bid].current_ses % bundle[bid].num_of_links]; + LOG(4, s, t, "MPPP: (1)Session number becomes: %u\n", s); + if (len > 256) + { + // Partition the packet to 2 fragments + uint32_t frag1len = len / 2; + uint32_t frag2len = len - frag1len; + uint8_t *p = makeppp(b1, sizeof(b1), buf, frag1len, s, t, PPPIP, 0, bid, MP_BEGIN); + uint8_t *q; + + if (!p) return; + tunnelsend(b1, frag1len + (p-b1), t); // send it... + s = bundle[bid].members[bundle[bid].current_ses = ++bundle[bid].current_ses % bundle[bid].num_of_links]; + LOG(4, s, t, "MPPP: (2)Session number becomes: %u\n", s); + q = makeppp(b2, sizeof(b2), buf+frag1len, frag2len, s, t, PPPIP, 0, bid, MP_END); + if (!q) return; + tunnelsend(b2, frag2len + (q-b2), t); // send it... + } + else { + // Send it as one frame + uint8_t *p = makeppp(b1, sizeof(b1), buf, len, s, t, PPPIP, 0, bid, MP_BOTH_BITS); + if (!p) return; + tunnelsend(b1, len + (p-b1), t); // send it... + } + } + else + { + uint8_t *p = makeppp(b1, sizeof(b1), buf, len, s, t, PPPIP, 0, 0, 0); + if (!p) return; + tunnelsend(b1, len + (p-b1), t); // send it... + } } // Snooping this session, send it to intercept box @@ -1191,8 +1358,15 @@ static void processipv6out(uint8_t * buf, int len) } return; } + if (session[s].bundle && bundle[session[s].bundle].num_of_links > 1) + { + bundleidt bid = session[s].bundle; + s = bundle[bid].members[bundle[bid].current_ses = ++bundle[bid].current_ses % bundle[bid].num_of_links]; + LOG(3, s, session[s].tunnel, "MPPP: Session number becomes: %u\n", s); + } t = session[s].tunnel; sp = &session[s]; + sp->last_data = time_now; // FIXME: add DoS prevention/filters? @@ -1216,7 +1390,7 @@ static void processipv6out(uint8_t * buf, int len) // Add on L2TP header { - uint8_t *p = makeppp(b, sizeof(b), buf, len, s, t, PPPIPV6); + uint8_t *p = makeppp(b, sizeof(b), buf, len, s, t, PPPIPV6, 0, 0, 0); if (!p) return; tunnelsend(b, len + (p-b), t); // send it... } @@ -1268,7 +1442,7 @@ static void send_ipout(sessionidt s, uint8_t *buf, int len) // Add on L2TP header { - uint8_t *p = makeppp(b, sizeof(b), buf, len, s, t, PPPIP); + uint8_t *p = makeppp(b, sizeof(b), buf, len, s, t, PPPIP, 0, 0, 0); if (!p) return; tunnelsend(b, len + (p-b), t); // send it... } @@ -1477,7 +1651,7 @@ void filter_session(sessionidt s, int filter_in, int filter_out) } // start tidy shutdown of session -void sessionshutdown(sessionidt s, char *reason, int result, int error) +void sessionshutdown(sessionidt s, char const *reason, int cdn_result, int cdn_error, int term_cause) { int walled_garden = session[s].walled_garden; @@ -1493,7 +1667,7 @@ void sessionshutdown(sessionidt s, char *reason, int result, int error) if (!session[s].die) { struct param_kill_session data = { &tunnel[session[s].tunnel], &session[s] }; - LOG(2, s, session[s].tunnel, "Shutting down session %d: %s\n", s, reason); + LOG(2, s, session[s].tunnel, "Shutting down session %u: %s\n", s, reason); run_plugins(PLUGIN_KILL_SESSION, &data); } @@ -1505,7 +1679,11 @@ void sessionshutdown(sessionidt s, char *reason, int result, int error) { // stop, if not already trying if (radius[r].state != RADIUSSTOP) + { + radius[r].term_cause = term_cause; + radius[r].term_msg = reason; radiussend(r, RADIUSSTOP); + } } else LOG(1, s, session[s].tunnel, "No free RADIUS sessions for Stop message\n"); @@ -1545,18 +1723,18 @@ void sessionshutdown(sessionidt s, char *reason, int result, int error) if (session[s].throttle_in || session[s].throttle_out) // Unthrottle if throttled. throttle_session(s, 0, 0); - if (result) + if (cdn_result) { // Send CDN controlt *c = controlnew(14); // sending CDN - if (error) + if (cdn_error) { uint8_t buf[4]; - *(uint16_t *) buf = htons(result); - *(uint16_t *) (buf+2) = htons(error); + *(uint16_t *) buf = htons(cdn_result); + *(uint16_t *) (buf+2) = htons(cdn_error); controlb(c, 1, buf, 4, 1); } else - control16(c, 1, result, 1); + control16(c, 1, cdn_result, 1); control16(c, 14, s, 1); // assigned session (our end) controladd(c, session[s].far, session[s].tunnel); // send the message @@ -1593,7 +1771,7 @@ void sendipcp(sessionidt s, tunnelidt t) session[s].unique_id = last_id; } - q = makeppp(buf, sizeof(buf), 0, 0, s, t, PPPIPCP); + q = makeppp(buf, sizeof(buf), 0, 0, s, t, PPPIPCP, 0, 0, 0); if (!q) return; *q = ConfigReq; @@ -1606,6 +1784,7 @@ void sendipcp(sessionidt s, tunnelidt t) my_address; // send my IP tunnelsend(buf, 10 + (q - buf), t); // send it + restart_timer(s, ipcp); } void sendipv6cp(sessionidt s, tunnelidt t) @@ -1616,7 +1795,7 @@ void sendipv6cp(sessionidt s, tunnelidt t) CSTAT(sendipv6cp); LOG(3, s, t, "IPV6CP: send ConfigReq\n"); - q = makeppp(buf, sizeof(buf), 0, 0, s, t, PPPIPV6CP); + q = makeppp(buf, sizeof(buf), 0, 0, s, t, PPPIPV6CP, 0, 0, 0); if (!q) return; *q = ConfigReq; @@ -1631,6 +1810,7 @@ void sendipv6cp(sessionidt s, tunnelidt t) q[13] = 1; tunnelsend(buf, 14 + (q - buf), t); // send it + restart_timer(s, ipv6cp); } static void sessionclear(sessionidt s) @@ -1647,6 +1827,7 @@ static void sessionclear(sessionidt s) // kill a session now void sessionkill(sessionidt s, char *reason) { + bundleidt b; CSTAT(sessionkill); @@ -1655,16 +1836,48 @@ void sessionkill(sessionidt s, char *reason) if (session[s].next) { - LOG(0, s, session[s].tunnel, "Tried to kill a session with next pointer set (%d)\n", session[s].next); + LOG(0, s, session[s].tunnel, "Tried to kill a session with next pointer set (%u)\n", session[s].next); return; } session[s].die = TIME; - sessionshutdown(s, reason, 3, 0); // close radius/routes, etc. + sessionshutdown(s, reason, CDN_ADMIN_DISC, TERM_ADMIN_RESET); // close radius/routes, etc. if (sess_local[s].radius) radiusclear(sess_local[s].radius, s); // cant send clean accounting data, session is killed - LOG(2, s, session[s].tunnel, "Kill session %d (%s): %s\n", s, session[s].user, reason); + LOG(2, s, session[s].tunnel, "Kill session %u (%s): %s\n", s, session[s].user, reason); + if ((b = session[s].bundle)) + { + // This session was part of a bundle + bundle[b].num_of_links--; + LOG(3, s, 0, "MPPP: Dropping member link: %u from bundle %u\n", s, b); + if (bundle[b].num_of_links == 0) + { + bundleclear(b); + LOG(3, s, 0, "MPPP: Kill bundle: %u (No remaing member links)\n", b); + } + else + { + // Adjust the members array to accomodate the new change + uint8_t mem_num = 0; + // It should be here num_of_links instead of num_of_links-1 (previous instruction "num_of_links--") + if (bundle[b].members[bundle[b].num_of_links] != s) + { + uint8_t ml; + for (ml = 0; mlcluster_highest_sessionid ; ++s) if (session[s].tunnel == t) - sessionshutdown(s, reason, 0, 0); + sessionshutdown(s, reason, CDN_NONE, TERM_ADMIN_RESET); tunnel[t].state = TUNNELDIE; tunnel[t].die = TIME + 700; // Clean up in 70 seconds @@ -1783,7 +2003,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) } if ((buf[1] & 0x0F) != 2) { - LOG(1, 0, 0, "Bad L2TP ver %d\n", (buf[1] & 0x0F) != 2); + LOG(1, 0, 0, "Bad L2TP ver %d\n", buf[1] & 0x0F); STAT(tunnel_rx_errors); return; } @@ -1827,6 +2047,11 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) return; } l -= (p - buf); + + // used to time out old tunnels + if (t && tunnel[t].state == TUNNELOPEN) + tunnel[t].lastrec = time_now; + if (*buf & 0x80) { // control uint16_t message = 0xFFFF; // message type @@ -1875,7 +2100,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) } } - LOG(3, s, t, "Control message (%d bytes): (unacked %d) l-ns %d l-nr %d r-ns %d r-nr %d\n", + LOG(3, s, t, "Control message (%d bytes): (unacked %d) l-ns %u l-nr %u r-ns %u r-nr %u\n", l, tunnel[t].controlc, tunnel[t].ns, tunnel[t].nr, ns, nr); // if no tunnel specified, assign one @@ -1892,7 +2117,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) tunnel[t].port = ntohs(addr->sin_port); tunnel[t].window = 4; // default window STAT(tunnel_created); - LOG(1, 0, t, " New tunnel from %s:%u ID %d\n", + LOG(1, 0, t, " New tunnel from %s:%u ID %u\n", fmtaddr(htonl(tunnel[t].ip), 0), tunnel[t].port, t); } @@ -1906,7 +2131,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) { // is this the sequence we were expecting? STAT(tunnel_rx_errors); - LOG(1, 0, t, " Out of sequence tunnel %d, (%d is not the expected %d)\n", + LOG(1, 0, t, " Out of sequence tunnel %u, (%u is not the expected %u)\n", t, ns, tunnel[t].nr); if (l) // Is this not a ZLB? @@ -1914,9 +2139,6 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) return; } - // This is used to time out old tunnels - tunnel[t].lastrec = time_now; - // check sequence of this message { int skip = tunnel[t].window; // track how many in-window packets are still in queue @@ -1962,6 +2184,12 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) int error = 0; char *msg = 0; + // default disconnect cause/message on receipt + // of CDN (set to more specific value from + // attribute 46 if present below). + int disc_cause = TERM_NAS_REQUEST; + char const *disc_reason = "Closed (Received CDN)."; + // process AVPs while (l && !(fatal & 0x80)) // 0x80 = mandatory AVP { @@ -1969,6 +2197,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) uint8_t *b = p; uint8_t flags = *p; uint16_t mtype; + if (n > l) { LOG(1, s, t, "Invalid length in AVP\n"); @@ -1989,7 +2218,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) b += 2; if (*(uint16_t *) (b)) { - LOG(2, s, t, "Unknown AVP vendor %d\n", ntohs(*(uint16_t *) (b))); + LOG(2, s, t, "Unknown AVP vendor %u\n", ntohs(*(uint16_t *) (b))); fatal = flags; result = 2; // general error error = 6; // generic vendor-specific error @@ -2006,7 +2235,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) uint16_t orig_len; // handle hidden AVPs - if (!*config->l2tpsecret) + if (!*config->l2tp_secret) { LOG(1, s, t, "Hidden AVP requested, but no L2TP secret.\n"); fatal = flags; @@ -2054,7 +2283,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) n = orig_len; } - LOG(4, s, t, " AVP %d (%s) len %d%s%s\n", mtype, l2tp_avp_name(mtype), n, + LOG(4, s, t, " AVP %u (%s) len %d%s%s\n", mtype, l2tp_avp_name(mtype), n, flags & 0x40 ? ", hidden" : "", flags & 0x80 ? ", mandatory" : ""); switch (mtype) @@ -2062,7 +2291,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) case 0: // message type message = ntohs(*(uint16_t *) b); mandatory = flags & 0x80; - LOG(4, s, t, " Message type = %d (%s)\n", *b, l2tp_code(message)); + LOG(4, s, t, " Message type = %u (%s)\n", *b, l2tp_code(message)); break; case 1: // result code { @@ -2077,11 +2306,11 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) resdesc = l2tp_cdn_result_code(rescode); } - LOG(4, s, t, " Result Code %d: %s\n", rescode, resdesc); + LOG(4, s, t, " Result Code %u: %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, l2tp_error_code(errcode)); + LOG(4, s, t, " Error Code %u: %s\n", errcode, l2tp_error_code(errcode)); } if (n > 4) LOG(4, s, t, " Error String: %.*s\n", n-4, b+4); @@ -2092,7 +2321,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) case 2: // protocol version { version = ntohs(*(uint16_t *) (b)); - LOG(4, s, t, " Protocol version = %d\n", version); + LOG(4, s, t, " Protocol version = %u\n", version); if (version && version != 0x0100) { // allow 0.0 and 1.0 LOG(1, s, t, " Bad protocol version %04X\n", version); @@ -2105,17 +2334,13 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) } break; case 3: // framing capabilities -// LOG(4, s, t, "Framing capabilities\n"); break; case 4: // bearer capabilities -// LOG(4, s, t, "Bearer capabilities\n"); break; case 5: // tie breaker // We never open tunnels, so we don't care about tie breakers -// LOG(4, s, t, "Tie breaker\n"); continue; case 6: // firmware revision -// LOG(4, s, t, "Firmware revision\n"); break; case 7: // host name memset(tunnel[t].hostname, 0, sizeof(tunnel[t].hostname)); @@ -2130,13 +2355,13 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) break; case 9: // assigned tunnel tunnel[t].far = ntohs(*(uint16_t *) (b)); - LOG(4, s, t, " Remote tunnel id = %d\n", tunnel[t].far); + LOG(4, s, t, " Remote tunnel id = %u\n", tunnel[t].far); break; case 10: // rx window tunnel[t].window = ntohs(*(uint16_t *) (b)); if (!tunnel[t].window) tunnel[t].window = 1; // window of 0 is silly - LOG(4, s, t, " rx window = %d\n", tunnel[t].window); + LOG(4, s, t, " rx window = %u\n", tunnel[t].window); break; case 11: // Challenge { @@ -2151,17 +2376,17 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) case 14: // assigned session asession = session[s].far = ntohs(*(uint16_t *) (b)); - LOG(4, s, t, " assigned session = %d\n", asession); + LOG(4, s, t, " assigned session = %u\n", asession); break; case 15: // call serial number - LOG(4, s, t, " call serial number = %d\n", ntohl(*(uint32_t *)b)); + LOG(4, s, t, " call serial number = %u\n", ntohl(*(uint32_t *)b)); break; case 18: // bearer type - LOG(4, s, t, " bearer type = %d\n", ntohl(*(uint32_t *)b)); + LOG(4, s, t, " bearer type = %u\n", ntohl(*(uint32_t *)b)); // TBA - for RADIUS break; case 19: // framing type - LOG(4, s, t, " framing type = %d\n", ntohl(*(uint32_t *)b)); + LOG(4, s, t, " framing type = %u\n", ntohl(*(uint32_t *)b)); // TBA break; case 21: // called number @@ -2215,7 +2440,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, ppp_auth_type(atype)); + LOG(4, s, t, " Proxy Auth Type %u (%s)\n", atype, ppp_auth_type(atype)); break; } case 30: // Proxy Authentication Name @@ -2235,7 +2460,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) case 32: // Proxy Authentication ID { uint16_t authid = ntohs(*(uint16_t *)(b)); - LOG(4, s, t, " Proxy Auth ID (%d)\n", authid); + LOG(4, s, t, " Proxy Auth ID (%u)\n", authid); break; } case 33: // Proxy Authentication Response @@ -2249,9 +2474,9 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) if (*p == 5 && p[1] == 6) // Magic-Number amagic = ntohl(*(uint32_t *) (p + 2)); else if (*p == 7) // Protocol-Field-Compression - aflags |= SESSIONPFC; + aflags |= SESSION_PFC; else if (*p == 8) // Address-and-Control-Field-Compression - aflags |= SESSIONACFC; + aflags |= SESSION_ACFC; p += p[1]; } } @@ -2270,10 +2495,88 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) memcpy(session[s].random_vector, b, n); session[s].random_vector_length = n; break; + case 46: // ppp disconnect cause + if (n >= 5) + { + uint16_t code = ntohs(*(uint16_t *) b); + uint16_t proto = ntohs(*(uint16_t *) (b + 2)); + uint8_t dir = *(b + 4); + + LOG(4, s, t, " PPP disconnect cause " + "(code=%u, proto=%04X, dir=%u, msg=\"%.*s\")\n", + code, proto, dir, n - 5, b + 5); + + switch (code) + { + case 1: // admin disconnect + disc_cause = TERM_ADMIN_RESET; + disc_reason = "Administrative disconnect"; + break; + case 3: // lcp terminate + if (dir != 2) break; // 1=peer (LNS), 2=local (LAC) + disc_cause = TERM_USER_REQUEST; + disc_reason = "Normal disconnection"; + break; + case 4: // compulsory encryption unavailable + if (dir != 1) break; // 1=refused by peer, 2=local + disc_cause = TERM_USER_ERROR; + disc_reason = "Compulsory encryption refused"; + break; + case 5: // lcp: fsm timeout + disc_cause = TERM_PORT_ERROR; + disc_reason = "LCP: FSM timeout"; + break; + case 6: // lcp: no recognisable lcp packets received + disc_cause = TERM_PORT_ERROR; + disc_reason = "LCP: no recognisable LCP packets"; + break; + case 7: // lcp: magic-no error (possibly looped back) + disc_cause = TERM_PORT_ERROR; + disc_reason = "LCP: magic-no error (possible loop)"; + break; + case 8: // lcp: echo request timeout + disc_cause = TERM_PORT_ERROR; + disc_reason = "LCP: echo request timeout"; + break; + case 13: // auth: fsm timeout + disc_cause = TERM_SERVICE_UNAVAILABLE; + disc_reason = "Authentication: FSM timeout"; + break; + case 15: // auth: unacceptable auth protocol + disc_cause = TERM_SERVICE_UNAVAILABLE; + disc_reason = "Unacceptable authentication protocol"; + break; + case 16: // auth: authentication failed + disc_cause = TERM_SERVICE_UNAVAILABLE; + disc_reason = "Authentication failed"; + break; + case 17: // ncp: fsm timeout + disc_cause = TERM_SERVICE_UNAVAILABLE; + disc_reason = "NCP: FSM timeout"; + break; + case 18: // ncp: no ncps available + disc_cause = TERM_SERVICE_UNAVAILABLE; + disc_reason = "NCP: no NCPs available"; + break; + case 19: // ncp: failure to converge on acceptable address + disc_cause = TERM_SERVICE_UNAVAILABLE; + disc_reason = (dir == 1) + ? "NCP: too many Configure-Naks received from peer" + : "NCP: too many Configure-Naks sent to peer"; + break; + case 20: // ncp: user not permitted to use any address + disc_cause = TERM_SERVICE_UNAVAILABLE; + disc_reason = (dir == 1) + ? "NCP: local link address not acceptable to peer" + : "NCP: remote link address not acceptable"; + break; + } + } + break; default: { static char e[] = "unknown AVP 0xXXXX"; - LOG(2, s, t, " Unknown AVP type %d\n", mtype); + LOG(2, s, t, " Unknown AVP type %u\n", mtype); fatal = flags; result = 2; // general error error = 8; // unknown mandatory AVP @@ -2343,8 +2646,8 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) 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); + session[s].last_packet = session[s].last_data = time_now; + LOG(3, s, t, "New session (%u/%u)\n", tunnel[t].far, session[s].far); control16(c, 14, s, 1); // assigned session controladd(c, asession, t); // send the reply @@ -2378,22 +2681,25 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) case 12: // ICCN if (amagic == 0) amagic = time_now; session[s].magic = amagic; // set magic number - session[s].l2tp_flags = aflags; // set flags received - session[s].mru = DEFAULT_MRU; + session[s].flags = aflags; // set flags received + session[s].mru = PPPoE_MRU; // default controlnull(t); // ack // start LCP - 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 = config->radius_authprefer; - session[s].ppp.lcp = RequestSent; - sendlcp(s, t); + sess_local[s].ppp_mru = MRU; + + // Set multilink options before sending initial LCP packet + sess_local[s].mp_mrru = 1614; + sess_local[s].mp_epdis = config->bind_address ? config->bind_address : my_address; + sendlcp(s, t); + change_state(s, lcp, RequestSent); break; + case 14: // CDN controlnull(t); // ack - sessionshutdown(s, "Closed (Received CDN).", 0, 0); + sessionshutdown(s, disc_reason, CDN_NONE, disc_cause); break; case 0xFFFF: LOG(1, s, t, "Missing message type\n"); @@ -2403,7 +2709,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) if (mandatory) tunnelshutdown(t, "Unknown message type", 2, 6, "unknown message type"); else - LOG(1, s, t, "Unknown message type %d\n", message); + LOG(1, s, t, "Unknown message type %u\n", message); break; } if (chapresponse) free(chapresponse); @@ -2416,7 +2722,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) } else { // data - uint16_t prot; + uint16_t proto; LOG_HEX(5, "Receive Tunnel Data", p, l); if (l > 2 && p[0] == 0xFF && p[1] == 0x03) @@ -2432,12 +2738,12 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) } if (*p & 1) { - prot = *p++; + proto = *p++; l--; } else { - prot = ntohs(*(uint16_t *) p); + proto = ntohs(*(uint16_t *) p); p += 2; l -= 2; } @@ -2457,51 +2763,51 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) return; } - if (prot == PPPPAP) + if (proto == PPPPAP) { 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; } processpap(s, t, p, l); } - else if (prot == PPPCHAP) + else if (proto == PPPCHAP) { 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; } processchap(s, t, p, l); } - else if (prot == PPPLCP) + else if (proto == PPPLCP) { 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; } processlcp(s, t, p, l); } - else if (prot == PPPIPCP) + else if (proto == PPPIPCP) { 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; } processipcp(s, t, p, l); } - else if (prot == PPPIPV6CP) + else if (proto == PPPIPV6CP && config->ipv6_prefix.s6_addr[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(s, t, p, l); } - else if (prot == PPPCCP) + else if (proto == PPPCCP) { 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; } processccp(s, t, p, l); } - else if (prot == PPPIP) + else if (proto == PPPIP) { if (session[s].die) { - LOG(4, s, t, "Session %d is closing. Don't process PPP packets\n", s); + LOG(4, s, t, "Session %u is closing. Don't process PPP packets\n", s); return; // closing session, PPP not processed } - session[s].last_packet = time_now; + session[s].last_packet = session[s].last_data = time_now; if (session[s].walled_garden && !config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port); @@ -2510,20 +2816,32 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) processipin(s, t, p, l); } - else if (prot == PPPIPV6) + else if (proto == PPPMP) { - if (!config->ipv6_prefix.s6_addr[0]) + if (session[s].die) + { + LOG(4, s, t, "Session %u is closing. Don't process PPP packets\n", s); + return; // closing session, PPP not processed + } + + session[s].last_packet = session[s].last_data = time_now; + if (session[s].walled_garden && !config->cluster_iam_master) { - LOG(1, s, t, "IPv6 not configured; yet received IPv6 packet. Ignoring.\n"); + master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port); return; } + + processmpin(s, t, p, l); + } + else if (proto == PPPIPV6 && config->ipv6_prefix.s6_addr[0]) + { if (session[s].die) { - LOG(4, s, t, "Session %d is closing. Don't process PPP packets\n", s); + LOG(4, s, t, "Session %u is closing. Don't process PPP packets\n", s); return; // closing session, PPP not processed } - session[s].last_packet = time_now; + session[s].last_packet = session[s].last_data = time_now; if (session[s].walled_garden && !config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port); @@ -2532,10 +2850,16 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) processipv6in(s, t, p, l); } + else if (session[s].ppp.lcp == Opened) + { + 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; } + protoreject(s, t, p, l, proto); + } else { - STAT(tunnel_rx_errors); - LOG(1, s, t, "Unknown PPP protocol %04X\n", prot); + LOG(2, s, t, "Unknown PPP protocol 0x%04X received in LCP %s state\n", + proto, ppp_state(session[s].ppp.lcp)); } } } @@ -2646,7 +2970,7 @@ static void regular_cleanups(double period) } } // Send hello - if (tunnel[t].state == TUNNELOPEN && (time_now - tunnel[t].lastrec) > 60) + if (tunnel[t].state == TUNNELOPEN && !tunnel[t].controlc && (time_now - tunnel[t].lastrec) > 60) { controlt *c = controlnew(6); // sending HELLO controladd(c, 0, t); // send the message @@ -2703,6 +3027,37 @@ static void regular_cleanups(double period) continue; } + // check for timed out sessions + if (session[s].timeout) + { + bundleidt bid = session[s].bundle; + if (bid) + { + clockt curr_time = time_now; + if (curr_time - bundle[bid].last_check >= 1) + { + bundle[bid].online_time += (curr_time-bundle[bid].last_check)*bundle[bid].num_of_links; + bundle[bid].last_check = curr_time; + if (bundle[bid].online_time >= session[s].timeout) + { + int ses; + for (ses = bundle[bid].num_of_links - 1; ses >= 0; ses--) + { + sessionshutdown(bundle[bid].members[ses], "Session timeout", CDN_ADMIN_DISC, TERM_SESSION_TIMEOUT); + s_actions++; + continue; + } + } + } + } + else if (session[s].timeout <= time_now - session[s].opened) + { + sessionshutdown(s, "Session timeout", CDN_ADMIN_DISC, TERM_SESSION_TIMEOUT); + s_actions++; + continue; + } + } + // PPP timeouts if (sess_local[s].lcp.restart <= time_now) { @@ -2717,14 +3072,12 @@ static void regular_cleanups(double period) 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(s, session[s].tunnel); change_state(s, lcp, next_state); } else { - sessionshutdown(s, "No response to LCP ConfigReq.", 3, 0); + sessionshutdown(s, "No response to LCP ConfigReq.", CDN_ADMIN_DISC, TERM_LOST_SERVICE); STAT(session_timeout); } @@ -2748,14 +3101,12 @@ static void regular_cleanups(double period) 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(s, session[s].tunnel); change_state(s, ipcp, next_state); } else { - sessionshutdown(s, "No response to IPCP ConfigReq.", 3, 0); + sessionshutdown(s, "No response to IPCP ConfigReq.", CDN_ADMIN_DISC, TERM_LOST_SERVICE); STAT(session_timeout); } @@ -2779,8 +3130,6 @@ static void regular_cleanups(double period) 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(s, session[s].tunnel); change_state(s, ipv6cp, next_state); } @@ -2807,8 +3156,6 @@ static void regular_cleanups(double period) 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(s, session[s].tunnel); change_state(s, ccp, next_state); } @@ -2825,29 +3172,49 @@ static void regular_cleanups(double period) // Drop sessions who have not responded within IDLE_TIMEOUT seconds if (session[s].last_packet && (time_now - session[s].last_packet >= IDLE_TIMEOUT)) { - sessionshutdown(s, "No response to LCP ECHO requests.", 3, 0); + sessionshutdown(s, "No response to LCP ECHO requests.", CDN_ADMIN_DISC, TERM_LOST_SERVICE); STAT(session_timeout); s_actions++; continue; } // No data in ECHO_TIMEOUT seconds, send LCP ECHO - if (session[s].ppp.phase >= Establish && (time_now - session[s].last_packet >= ECHO_TIMEOUT)) + if (session[s].ppp.phase >= Establish && (time_now - session[s].last_packet >= ECHO_TIMEOUT) && + (time_now - sess_local[s].last_echo >= ECHO_TIMEOUT)) { uint8_t b[MAXETHER]; - uint8_t *q = makeppp(b, sizeof(b), 0, 0, s, session[s].tunnel, PPPLCP); + uint8_t *q = makeppp(b, sizeof(b), 0, 0, s, session[s].tunnel, PPPLCP, 1, 0, 0); if (!q) continue; *q = EchoReq; *(uint8_t *)(q + 1) = (time_now % 255); // ID *(uint16_t *)(q + 2) = htons(8); // Length - *(uint32_t *)(q + 4) = 0; // Magic Number (not supported) + *(uint32_t *)(q + 4) = session[s].ppp.lcp == Opened ? htonl(session[s].magic) : 0; // Magic Number 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 + sess_local[s].last_echo = time_now; + s_actions++; + } + + // Drop sessions who have reached session_timeout seconds + if (session[s].session_timeout && (time_now - session[s].opened >= session[s].session_timeout)) + { + sessionshutdown(s, "Session Timeout Reached", CDN_ADMIN_DISC, TERM_SESSION_TIMEOUT); + STAT(session_timeout); + s_actions++; + continue; + } + + // Drop sessions who have reached idle_timeout seconds + if (session[s].last_data && session[s].idle_timeout && (time_now - session[s].last_data >= session[s].idle_timeout)) + { + sessionshutdown(s, "Idle Timeout Reached", CDN_ADMIN_DISC, TERM_IDLE_TIMEOUT); + STAT(session_timeout); s_actions++; + continue; } // Check for actions requested from the CLI @@ -2859,7 +3226,7 @@ static void regular_cleanups(double period) if (a & CLI_SESS_KILL) { LOG(2, s, session[s].tunnel, "Dropping session by CLI\n"); - sessionshutdown(s, "Requested by administrator.", 3, 0); + sessionshutdown(s, "Requested by administrator.", CDN_ADMIN_DISC, TERM_ADMIN_RESET); a = 0; // dead, no need to check for other actions s_actions++; } @@ -2874,7 +3241,7 @@ static void regular_cleanups(double period) } else if (a & CLI_SESS_SNOOP) { - LOG(2, s, session[s].tunnel, "Snooping session by CLI (to %s:%d)\n", + LOG(2, s, session[s].tunnel, "Snooping session by CLI (to %s:%u)\n", fmtaddr(cli_session_actions[s].snoop_ip, 0), cli_session_actions[s].snoop_port); @@ -3017,7 +3384,7 @@ static int still_busy(void) if (last_talked != TIME) { - LOG(2, 0, 0, "Tunnel %d still has un-acked control messages.\n", i); + LOG(2, 0, 0, "Tunnel %u still has un-acked control messages.\n", i); last_talked = TIME; } return 1; @@ -3039,7 +3406,7 @@ static int still_busy(void) if (last_talked != TIME) { - LOG(2, 0, 0, "Radius session %d is still busy (sid %d)\n", i, radius[i].session); + LOG(2, 0, 0, "Radius session %u is still busy (sid %u)\n", i, radius[i].session); last_talked = TIME; } return 1; @@ -3091,9 +3458,12 @@ static void mainloop(void) e.events = EPOLLIN; i = 0; - d[i].type = FD_TYPE_CLI; - e.data.ptr = &d[i++]; - epoll_ctl(epollfd, EPOLL_CTL_ADD, clifd, &e); + if (clifd >= 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++]; @@ -3136,9 +3506,17 @@ static void mainloop(void) int more = 0; int n; + + if (main_reload) + { + main_reload = 0; + read_config_file(); + config->reload_config++; + } + if (config->reload_config) { - // Update the config state based on config settings + config->reload_config = 0; update_config(); } @@ -3163,6 +3541,7 @@ static void mainloop(void) if (n) { struct sockaddr_in addr; + struct in_addr local; socklen_t alen; int c, s; int udp_ready = 0; @@ -3179,6 +3558,7 @@ static void mainloop(void) for (c = n, i = 0; i < c; i++) { struct event_data *d = events[i].data.ptr; + switch (d->type) { case FD_TYPE_CLI: // CLI connections @@ -3205,20 +3585,30 @@ static void mainloop(void) case FD_TYPE_CONTROL: // nsctl commands alen = sizeof(addr); - processcontrol(buf, recvfrom(controlfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr, alen); + s = recvfromto(controlfd, buf, sizeof(buf), MSG_WAITALL, (struct sockaddr *) &addr, &alen, &local); + if (s > 0) processcontrol(buf, s, &addr, alen, &local); 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); + s = recvfromto(daefd, buf, sizeof(buf), MSG_WAITALL, (struct sockaddr *) &addr, &alen, &local); + if (s > 0) processdae(buf, s, &addr, alen, &local); n--; break; case FD_TYPE_RADIUS: // RADIUS response - s = recv(radfds[d->index], buf, sizeof(buf), 0); + alen = sizeof(addr); + s = recvfrom(radfds[d->index], buf, sizeof(buf), MSG_WAITALL, (struct sockaddr *) &addr, &alen); if (s >= 0 && config->cluster_iam_master) - processrad(buf, s, d->index); + { + if (addr.sin_addr.s_addr == config->radiusserver[0] || + addr.sin_addr.s_addr == config->radiusserver[1]) + processrad(buf, s, d->index); + else + LOG(3, 0, 0, "Dropping RADIUS packet from unknown source %s\n", + fmtaddr(addr.sin_addr.s_addr, 0)); + } n--; break; @@ -3302,6 +3692,35 @@ static void mainloop(void) } } + if (time_changed) + { + double Mbps = 1024.0 * 1024.0 / 8 * time_changed; + + // Log current traffic stats + snprintf(config->bandwidth, sizeof(config->bandwidth), + "UDP-ETH:%1.0f/%1.0f ETH-UDP:%1.0f/%1.0f TOTAL:%0.1f IN:%u OUT:%u", + (udp_rx / Mbps), (eth_tx / Mbps), (eth_rx / Mbps), (udp_tx / Mbps), + ((udp_tx + udp_rx + eth_tx + eth_rx) / Mbps), + udp_rx_pkt / time_changed, eth_rx_pkt / time_changed); + + udp_tx = udp_rx = 0; + udp_rx_pkt = eth_rx_pkt = 0; + eth_tx = eth_rx = 0; + time_changed = 0; + + if (config->dump_speed) + printf("%s\n", config->bandwidth); + + // Update the internal time counter + strftime(time_now_string, sizeof(time_now_string), "%Y-%m-%d %H:%M:%S", localtime(&time_now)); + + { + // Run timer hooks + struct param_timer p = { time_now }; + run_plugins(PLUGIN_TIMER, &p); + } + } + // Runs on every machine (master and slaves). if (next_cluster_ping <= TIME) { @@ -3498,6 +3917,16 @@ static void initdata(int optdebug, char *optconfig) LOG(0, 0, 0, "Error doing malloc for tunnels: %s\n", strerror(errno)); exit(1); } + if (!(bundle = shared_malloc(sizeof(bundlet) * MAXBUNDLE))) + { + LOG(0, 0, 0, "Error doing malloc for bundles: %s\n", strerror(errno)); + exit(1); + } + if (!(frag = shared_malloc(sizeof(fragmentationt) * MAXBUNDLE))) + { + LOG(0, 0, 0, "Error doing malloc for fragmentations: %s\n", strerror(errno)); + exit(1); + } if (!(session = shared_malloc(sizeof(sessiont) * MAXSESSION))) { LOG(0, 0, 0, "Error doing malloc for sessions: %s\n", strerror(errno)); @@ -3544,6 +3973,7 @@ static void initdata(int optdebug, char *optconfig) memset(cli_tunnel_actions, 0, sizeof(struct cli_tunnel_actions) * MAXSESSION); memset(tunnel, 0, sizeof(tunnelt) * MAXTUNNEL); + memset(bundle, 0, sizeof(bundlet) * MAXBUNDLE); memset(session, 0, sizeof(sessiont) * MAXSESSION); memset(radius, 0, sizeof(radiust) * MAXRADIUS); memset(ip_address_pool, 0, sizeof(ippoolt) * MAXIPPOOL); @@ -3561,6 +3991,10 @@ static void initdata(int optdebug, char *optconfig) for (i = 1; i < MAXTUNNEL; i++) tunnel[i].state = TUNNELUNDEF; // mark it as not filled in. + for (i = 1; i < MAXBUNDLE; i++) { + bundle[i].state = BUNDLEUNDEF; + } + if (!*hostname) { // Grab my hostname unless it's been specified @@ -3695,7 +4129,7 @@ void rebuild_address_pool(void) if (ipid < 1) // Not found in the pool either? good. continue; - LOG(0, i, 0, "Session %d has an IP address (%s) that was marked static, but is in the pool (%d)!\n", + LOG(0, i, 0, "Session %u has an IP address (%s) that was marked static, but is in the pool (%d)!\n", i, fmtaddr(session[i].ip, 0), ipid); // Fall through and process it as part of the pool. @@ -3704,7 +4138,7 @@ void rebuild_address_pool(void) if (ipid > MAXIPPOOL || ipid < 0) { - LOG(0, i, 0, "Session %d has a pool IP that's not found in the pool! (%d)\n", i, ipid); + LOG(0, i, 0, "Session %u has a pool IP that's not found in the pool! (%d)\n", i, ipid); ipid = -1; session[i].ip_pool_index = ipid; continue; @@ -3835,7 +4269,7 @@ static void initippool() else { // It's a single ip address - add_to_ip_pool(inet_addr(pool), 0); + add_to_ip_pool(ntohl(inet_addr(pool)), 0); } } fclose(f); @@ -3852,7 +4286,7 @@ void snoop_send_packet(uint8_t *packet, uint16_t size, in_addr_t destination, ui snoop_addr.sin_addr.s_addr = destination; snoop_addr.sin_port = ntohs(port); - LOG(5, 0, 0, "Snooping %d byte packet to %s:%d\n", size, + LOG(5, 0, 0, "Snooping %d byte packet to %s:%u\n", size, fmtaddr(snoop_addr.sin_addr.s_addr, 0), htons(snoop_addr.sin_port)); @@ -3977,18 +4411,17 @@ int main(int argc, char *argv[]) // Start the timer routine off time(&time_now); strftime(time_now_string, sizeof(time_now_string), "%Y-%m-%d %H:%M:%S", localtime(&time_now)); - signal(SIGALRM, sigalrm_handler); - siginterrupt(SIGALRM, 0); initplugins(); initdata(optdebug, optconfig); init_cli(hostname); read_config_file(); + update_config(); init_tbf(config->num_tbfs); LOG(0, 0, 0, "L2TPNS version " VERSION "\n"); - LOG(0, 0, 0, "Copyright (c) 2003, 2004, 2005 Optus Internet Engineering\n"); + LOG(0, 0, 0, "Copyright (c) 2003, 2004, 2005, 2006 Optus Internet Engineering\n"); LOG(0, 0, 0, "Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced\n"); { struct rlimit rlim; @@ -4060,12 +4493,6 @@ int main(int argc, char *argv[]) LOG(0, 0, 0, "Can't lock pages: %s\n", strerror(errno)); } - alarm(1); - - // Drop privileges here - if (config->target_uid > 0 && geteuid() == 0) - setuid(config->target_uid); - mainloop(); /* remove plugins (so cleanup code gets run) */ @@ -4083,53 +4510,11 @@ int main(int argc, char *argv[]) static void sighup_handler(int sig) { - if (log_stream) - { - if (log_stream != stderr) - fclose(log_stream); - - log_stream = NULL; - } - - read_config_file(); -} - -static void sigalrm_handler(int sig) -{ - // Log current traffic stats - - snprintf(config->bandwidth, sizeof(config->bandwidth), - "UDP-ETH:%1.0f/%1.0f ETH-UDP:%1.0f/%1.0f TOTAL:%0.1f IN:%u OUT:%u", - (udp_rx / 1024.0 / 1024.0 * 8), - (eth_tx / 1024.0 / 1024.0 * 8), - (eth_rx / 1024.0 / 1024.0 * 8), - (udp_tx / 1024.0 / 1024.0 * 8), - ((udp_tx + udp_rx + eth_tx + eth_rx) / 1024.0 / 1024.0 * 8), - udp_rx_pkt, eth_rx_pkt); - - udp_tx = udp_rx = 0; - udp_rx_pkt = eth_rx_pkt = 0; - eth_tx = eth_rx = 0; - - if (config->dump_speed) - printf("%s\n", config->bandwidth); - - // Update the internal time counter - time(&time_now); - strftime(time_now_string, sizeof(time_now_string), "%Y-%m-%d %H:%M:%S", localtime(&time_now)); - alarm(1); - - { - // Run timer hooks - struct param_timer p = { time_now }; - run_plugins(PLUGIN_TIMER, &p); - } - + main_reload++; } static void shutdown_handler(int sig) { - LOG(1, 0, 0, "Shutting down\n"); main_quit = (sig == SIGQUIT) ? QUIT_SHUTDOWN : QUIT_FAILOVER; } @@ -4144,7 +4529,7 @@ static void build_chap_response(uint8_t *challenge, uint8_t id, uint16_t challen MD5_CTX ctx; *challenge_response = NULL; - if (!*config->l2tpsecret) + if (!*config->l2tp_secret) { LOG(0, 0, 0, "LNS requested CHAP authentication, but no l2tp secret is defined\n"); return; @@ -4156,7 +4541,7 @@ static void build_chap_response(uint8_t *challenge, uint8_t id, uint16_t challen MD5_Init(&ctx); MD5_Update(&ctx, &id, 1); - MD5_Update(&ctx, config->l2tpsecret, strlen(config->l2tpsecret)); + MD5_Update(&ctx, config->l2tp_secret, strlen(config->l2tp_secret)); MD5_Update(&ctx, challenge, challenge_length); MD5_Final(*challenge_response, &ctx); @@ -4223,6 +4608,20 @@ static void update_config() setbuf(log_stream, NULL); } +#define L2TP_HDRS (20+8+6+4) // L2TP data encaptulation: ip + udp + l2tp (data) + ppp (inc hdlc) +#define TCP_HDRS (20+20) // TCP encapsulation: ip + tcp + + if (config->l2tp_mtu <= 0) config->l2tp_mtu = 1500; // ethernet default + else if (config->l2tp_mtu < MINMTU) config->l2tp_mtu = MINMTU; + else if (config->l2tp_mtu > MAXMTU) config->l2tp_mtu = MAXMTU; + + // reset MRU/MSS globals + MRU = config->l2tp_mtu - L2TP_HDRS; + if (MRU > PPPoE_MRU) + MRU = PPPoE_MRU; + + MSS = MRU - TCP_HDRS; + // Update radius config->numradiusservers = 0; for (i = 0; i < MAXRADSERVER; i++) @@ -4368,8 +4767,6 @@ static void update_config() LOG(0, 0, 0, "Can't write to PID file %s: %s\n", config->pid_file, strerror(errno)); } } - - config->reload_config = 0; } static void read_config_file() @@ -4387,7 +4784,6 @@ static void read_config_file() cli_do_file(f); LOG(3, 0, 0, "Done reading config file\n"); fclose(f); - update_config(); } int sessionsetup(sessionidt s, tunnelidt t) @@ -4408,7 +4804,7 @@ int sessionsetup(sessionidt s, tunnelidt t) 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); // try another + sessionshutdown(s, "No IP addresses available.", CDN_TRY_ANOTHER, TERM_SERVICE_UNAVAILABLE); return 0; } LOG(3, s, t, " No IP allocated. Assigned %s from pool\n", @@ -4419,6 +4815,14 @@ int sessionsetup(sessionidt s, tunnelidt t) // Make sure this is right session[s].tunnel = t; + // Join a bundle if the MRRU option is accepted + if (session[s].mrru > 0 && !session[s].bundle) + { + LOG(3, s, t, "This session can be part of multilink bundle\n"); + if (join_bundle(s)) + cluster_send_bundle(session[s].bundle); + } + // zap old sessions with same IP and/or username // Don't kill gardened sessions - doing so leads to a DoS // from someone who doesn't need to know the password @@ -4435,8 +4839,21 @@ int sessionsetup(sessionidt s, tunnelidt t) continue; } - if (config->allow_duplicate_users) continue; - if (session[s].walled_garden || session[i].walled_garden) continue; + if (config->allow_duplicate_users) + continue; + + if (session[s].walled_garden || session[i].walled_garden) + continue; + + // Allow duplicate sessions for guest account. + if (*config->guest_user && !strcasecmp(user, config->guest_user)) + continue; + + // Allow duplicate sessions for multilink ones of the same bundle. + if (session[s].bundle && session[i].bundle && session[s].bundle == session[i].bundle) + continue; + + // Drop the new session in case of duplicate sessionss, not the old one. if (!strcasecmp(user, session[i].user)) sessionkill(i, "Duplicate session for users"); } @@ -4481,7 +4898,7 @@ int sessionsetup(sessionidt s, tunnelidt t) if (session[s].throttle_in || session[s].throttle_out) throttle_session(s, session[s].throttle_in, session[s].throttle_out); - session[s].last_packet = time_now; + session[s].last_packet = session[s].last_data = time_now; LOG(2, s, t, "Login by %s at %s from %s (%s)\n", session[s].user, fmtaddr(htonl(session[s].ip), 0), @@ -4584,13 +5001,13 @@ int load_session(sessionidt s, sessiont *new) // check filters if (new->filter_in && (new->filter_in > MAXFILTER || !ip_filters[new->filter_in - 1].name[0])) { - LOG(2, s, session[s].tunnel, "Dropping invalid input filter %d\n", (int) new->filter_in); + LOG(2, s, session[s].tunnel, "Dropping invalid input filter %u\n", (int) new->filter_in); new->filter_in = 0; } if (new->filter_out && (new->filter_out > MAXFILTER || !ip_filters[new->filter_out - 1].name[0])) { - LOG(2, s, session[s].tunnel, "Dropping invalid output filter %d\n", (int) new->filter_out); + LOG(2, s, session[s].tunnel, "Dropping invalid output filter %u\n", (int) new->filter_out); new->filter_out = 0; } @@ -4795,7 +5212,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 in_addr *local) { struct nsctl request; struct nsctl response; @@ -4953,7 +5370,7 @@ static void processcontrol(uint8_t *buf, int len, struct sockaddr_in *addr, int r = pack_control(buf, NSCTL_MAX_PKT_SZ, response.type, response.argc, response.argv); if (r > 0) { - sendto(controlfd, buf, r, 0, (const struct sockaddr *) addr, alen); + sendtofrom(controlfd, buf, r, 0, (const struct sockaddr *) addr, alen, local); if (log_stream && config->debug >= 4) { LOG(4, 0, 0, "Sent [%s] ", fmtaddr(addr->sin_addr.s_addr, 0)); @@ -4974,7 +5391,7 @@ static tunnelidt new_tunnel() { if (tunnel[i].state == TUNNELFREE) { - LOG(4, 0, i, "Assigning tunnel ID %d\n", i); + LOG(4, 0, i, "Assigning tunnel ID %u\n", i); if (i > config->cluster_highest_tunnelid) config->cluster_highest_tunnelid = i; return i; @@ -5044,7 +5461,7 @@ int cmd_show_hist_idle(struct cli_def *cli, char *command, char **argv, int argc if (!session[s].opened) continue; - idle = time_now - session[s].last_packet; + idle = time_now - session[s].last_data; idle /= 5 ; // In multiples of 5 seconds. if (idle < 0) idle = 0; @@ -5121,7 +5538,7 @@ static void unhide_value(uint8_t *value, size_t len, uint16_t type, uint8_t *vec // Compute initial pad MD5_Init(&ctx); MD5_Update(&ctx, (unsigned char *) &m, 2); - MD5_Update(&ctx, config->l2tpsecret, strlen(config->l2tpsecret)); + MD5_Update(&ctx, config->l2tp_secret, strlen(config->l2tp_secret)); MD5_Update(&ctx, vector, vec_len); MD5_Final(digest, &ctx); @@ -5134,7 +5551,7 @@ static void unhide_value(uint8_t *value, size_t len, uint16_t type, uint8_t *vec if (d >= sizeof(digest)) { MD5_Init(&ctx); - MD5_Update(&ctx, config->l2tpsecret, strlen(config->l2tpsecret)); + MD5_Update(&ctx, config->l2tp_secret, strlen(config->l2tp_secret)); MD5_Update(&ctx, last, sizeof(digest)); MD5_Final(digest, &ctx); @@ -5257,7 +5674,9 @@ int ip_filter(uint8_t *buf, int len, uint8_t filter) if (frag_offset) { - if (!rule->frag || rule->action == FILTER_ACTION_DENY) + // layer 4 deny rules are skipped + if (rule->action == FILTER_ACTION_DENY && + (rule->src_ports.op || rule->dst_ports.op || rule->tcp_flag_op)) continue; } else