X-Git-Url: http://git.sameswireless.fr/l2tpns.git/blobdiff_plain/a840035219b9552241dc071c0b6afc54e3a64b5f..e49d6736b53034705fc2f54a36e0965070accd69:/l2tpns.c?ds=inline diff --git a/l2tpns.c b/l2tpns.c index 27a5821..0c6c1c0 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.157 2006-04-05 01:45:57 bodea Exp $"; +char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.169 2006-07-01 12:40:17 bodea Exp $"; #include #include @@ -126,14 +126,16 @@ config_descriptt config_values[] = { CONFIG("radius_secret", radiussecret, STRING), CONFIG("radius_authtypes", radius_authtypes_s, STRING), CONFIG("radius_dae_port", radius_dae_port, SHORT), + CONFIG("radius_bind_min", radius_bind_min, SHORT), + CONFIG("radius_bind_max", radius_bind_max, 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), @@ -174,6 +176,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. @@ -203,6 +207,7 @@ static void plugins_done(void); 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 @@ -622,7 +627,7 @@ static void initudp(void) int flags = fcntl(udpfd, F_GETFL, 0); fcntl(udpfd, F_SETFL, flags | O_NONBLOCK); } - if (bind(udpfd, (void *) &addr, sizeof(addr)) < 0) + if (bind(udpfd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { LOG(0, 0, 0, "Error in UDP bind: %s\n", strerror(errno)); exit(1); @@ -634,7 +639,8 @@ 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)); - if (bind(controlfd, (void *) &addr, sizeof(addr)) < 0) + setsockopt(controlfd, SOL_IP, IP_PKTINFO, &on, sizeof(on)); // recvfromto + if (bind(controlfd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { LOG(0, 0, 0, "Error in control bind: %s\n", strerror(errno)); exit(1); @@ -646,7 +652,8 @@ 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)); - if (bind(daefd, (void *) &addr, sizeof(addr)) < 0) + setsockopt(daefd, SOL_IP, IP_PKTINFO, &on, sizeof(on)); // recvfromto + if (bind(daefd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { LOG(0, 0, 0, "Error in DAE bind: %s\n", strerror(errno)); exit(1); @@ -1049,6 +1056,56 @@ void adjust_tcp_mss(sessionidt s, tunnelidt t, uint8_t *buf, int len, uint8_t *t *(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) @@ -1061,7 +1118,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); @@ -1112,6 +1170,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) @@ -1181,13 +1240,43 @@ static void processipout(uint8_t *buf, int len) 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 @@ -1271,8 +1360,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? @@ -1296,7 +1392,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... } @@ -1348,7 +1444,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... } @@ -1557,7 +1653,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; @@ -1573,7 +1669,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); } @@ -1585,7 +1681,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"); @@ -1625,18 +1725,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 @@ -1673,7 +1773,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; @@ -1697,7 +1797,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; @@ -1729,6 +1829,7 @@ static void sessionclear(sessionidt s) // kill a session now void sessionkill(sessionidt s, char *reason) { + bundleidt b; CSTAT(sessionkill); @@ -1737,16 +1838,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 @@ -1865,7 +2005,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; } @@ -1962,7 +2102,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 @@ -1979,7 +2119,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); } @@ -1993,7 +2133,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? @@ -2046,6 +2186,13 @@ 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 1 (result code) or 46 + // (disconnect cause) if present below. + int disc_cause_set = 0; + int disc_cause = TERM_NAS_REQUEST; + char const *disc_reason = "Closed (Received CDN)."; + // process AVPs while (l && !(fatal & 0x80)) // 0x80 = mandatory AVP { @@ -2053,6 +2200,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"); @@ -2073,7 +2221,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 @@ -2138,7 +2286,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) @@ -2146,37 +2294,53 @@ 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 { uint16_t rescode = ntohs(*(uint16_t *) b); - const char* resdesc = "(unknown)"; + char const *resdesc = "(unknown)"; + char const *errdesc = NULL; + int cause = 0; + if (message == 4) { /* StopCCN */ resdesc = l2tp_stopccn_result_code(rescode); + cause = TERM_LOST_SERVICE; } else if (message == 14) { /* CDN */ resdesc = l2tp_cdn_result_code(rescode); + if (rescode == 1) + cause = TERM_LOST_CARRIER; + else + cause = TERM_ADMIN_RESET; } - 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)); + errdesc = l2tp_error_code(errcode); + LOG(4, s, t, " Error Code %u: %s\n", errcode, errdesc); } if (n > 4) LOG(4, s, t, " Error String: %.*s\n", n-4, b+4); + if (cause && disc_cause_set < mtype) // take cause from attrib 46 in preference + { + disc_cause_set = mtype; + disc_reason = errdesc ? errdesc : resdesc; + disc_cause = cause; + } + break; } break; 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); @@ -2189,17 +2353,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)); @@ -2214,13 +2374,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 { @@ -2235,17 +2395,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 @@ -2299,7 +2459,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 @@ -2319,7 +2479,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 @@ -2354,10 +2514,90 @@ 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); + + disc_cause_set = mtype; + + 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 @@ -2427,8 +2667,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 @@ -2469,13 +2709,18 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) // start LCP sess_local[s].lcp_authtype = config->radius_authprefer; 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"); @@ -2485,7 +2730,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); @@ -2579,11 +2824,11 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) { 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); @@ -2592,15 +2837,32 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) processipin(s, t, p, l); } + else if (proto == PPPMP) + { + 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) + { + 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); @@ -2786,6 +3048,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) { @@ -2805,7 +3098,7 @@ static void regular_cleanups(double period) } 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); } @@ -2834,7 +3127,7 @@ static void regular_cleanups(double period) } 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); } @@ -2900,7 +3193,7 @@ 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; @@ -2912,7 +3205,7 @@ static void regular_cleanups(double period) { 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; @@ -2927,6 +3220,24 @@ static void regular_cleanups(double period) 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 if ((a = cli_session_actions[s].action)) { @@ -2936,7 +3247,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++; } @@ -2951,7 +3262,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); @@ -3094,7 +3405,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; @@ -3116,7 +3427,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; @@ -3168,9 +3479,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++]; @@ -3299,8 +3613,8 @@ static void mainloop(void) case FD_TYPE_DAE: // DAE requests alen = sizeof(addr); - s = recvfrom(daefd, buf, sizeof(buf), MSG_WAITALL, (struct sockaddr *) &addr, &alen); - if (s > 0) processdae(buf, s, &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; @@ -3624,6 +3938,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)); @@ -3670,6 +3994,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); @@ -3687,6 +4012,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 @@ -3821,7 +4150,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. @@ -3830,7 +4159,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; @@ -3978,7 +4307,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)); @@ -4113,7 +4442,7 @@ int main(int argc, char *argv[]) init_tbf(config->num_tbfs); LOG(0, 0, 0, "L2TPNS version " VERSION "\n"); - LOG(0, 0, 0, "Copyright (c) 2003, 2004, 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; @@ -4185,10 +4514,6 @@ int main(int argc, char *argv[]) LOG(0, 0, 0, "Can't lock pages: %s\n", strerror(errno)); } - // Drop privileges here - if (config->target_uid > 0 && geteuid() == 0) - setuid(config->target_uid); - mainloop(); /* remove plugins (so cleanup code gets run) */ @@ -4500,7 +4825,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", @@ -4511,6 +4836,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 @@ -4527,8 +4860,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"); } @@ -4573,7 +4919,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), @@ -4676,13 +5022,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; } @@ -5066,7 +5412,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; @@ -5136,7 +5482,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;