X-Git-Url: http://git.sameswireless.fr/l2tpns.git/blobdiff_plain/9fd9a6fba13ac55698990ecc7bcf293e817ff86e..845bb1f376be7666923e4ce0798f3801ea84b910:/l2tpns.c diff --git a/l2tpns.c b/l2tpns.c index 3d789a5..a544134 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.164 2006-04-27 14:37:27 bodea Exp $"; +char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.173 2009-12-08 14:49:28 bodea Exp $"; #include #include @@ -74,6 +74,9 @@ static int tunidx; // ifr_ifindex of tun device static int syslog_log = 0; // are we logging to syslog static FILE *log_stream = 0; // file handle for direct logging (i.e. direct into file, not via syslog). uint32_t last_id = 0; // Unique ID for radius accounting +// Guest change +char guest_users[10][32]; // Array of guest users +int guest_accounts_num = 0; // Number of guest users // calculated from config->l2tp_mtu uint16_t MRU = 0; // PPP MRU @@ -82,9 +85,13 @@ 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 -static void *ip_hash[256]; // Mapping from IP address to session structures. +union iphash { + sessionidt sess; + union iphash *idx; +} ip_hash[256]; // Mapping from IP address to session structures. + struct ipv6radix { - int sess; + sessionidt sess; struct ipv6radix *branch; } ipv6_hash[256]; // Mapping from IPv6 address to session structures. @@ -126,7 +133,10 @@ 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("kill_timedout_sessions", kill_timedout_sessions, BOOL), CONFIG("guest_account", guest_user, STRING), CONFIG("bind_address", bind_address, IPv4), CONFIG("peer_address", peer_address, IPv4), @@ -187,9 +197,9 @@ struct Tstats *_statistics = NULL; struct Tringbuffer *ringbuffer = NULL; #endif -static void cache_ipmap(in_addr_t ip, int s); +static void cache_ipmap(in_addr_t ip, sessionidt s); static void uncache_ipmap(in_addr_t ip); -static void cache_ipv6map(struct in6_addr ip, int prefixlen, int s); +static void cache_ipv6map(struct in6_addr ip, int prefixlen, sessionidt s); static void free_ip_address(sessionidt s); static void dump_acct_info(int all); static void sighup_handler(int sig); @@ -625,7 +635,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); @@ -638,7 +648,7 @@ static void initudp(void) 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) + if (bind(controlfd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { LOG(0, 0, 0, "Error in control bind: %s\n", strerror(errno)); exit(1); @@ -651,7 +661,7 @@ static void initudp(void) 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) + if (bind(daefd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { LOG(0, 0, 0, "Error in DAE bind: %s\n", strerror(errno)); exit(1); @@ -676,19 +686,19 @@ static void initudp(void) // IP address. // -static int lookup_ipmap(in_addr_t ip) +static sessionidt lookup_ipmap(in_addr_t ip) { uint8_t *a = (uint8_t *) &ip; - uint8_t **d = (uint8_t **) ip_hash; + union iphash *h = ip_hash; - if (!(d = (uint8_t **) d[(size_t) *a++])) return 0; - if (!(d = (uint8_t **) d[(size_t) *a++])) return 0; - if (!(d = (uint8_t **) d[(size_t) *a++])) return 0; + if (!(h = h[*a++].idx)) return 0; + if (!(h = h[*a++].idx)) return 0; + if (!(h = h[*a++].idx)) return 0; - return (int) (intptr_t) d[(size_t) *a]; + return h[*a].sess; } -static int lookup_ipv6map(struct in6_addr ip) +static sessionidt lookup_ipv6map(struct in6_addr ip) { struct ipv6radix *curnode; int i; @@ -716,18 +726,18 @@ static int lookup_ipv6map(struct in6_addr ip) sessionidt sessionbyip(in_addr_t ip) { - int s = lookup_ipmap(ip); + sessionidt s = lookup_ipmap(ip); CSTAT(sessionbyip); if (s > 0 && s < MAXSESSION && session[s].opened) - return (sessionidt) s; + return s; return 0; } sessionidt sessionbyipv6(struct in6_addr ip) { - int s; + sessionidt s; CSTAT(sessionbyipv6); if (!memcmp(&config->ipv6_prefix, &ip, 8) || @@ -753,25 +763,22 @@ sessionidt sessionbyipv6(struct in6_addr ip) // // (It's actually cached in network order) // -static void cache_ipmap(in_addr_t ip, int s) +static void cache_ipmap(in_addr_t ip, sessionidt s) { in_addr_t nip = htonl(ip); // MUST be in network order. I.e. MSB must in be ((char *) (&ip))[0] uint8_t *a = (uint8_t *) &nip; - uint8_t **d = (uint8_t **) ip_hash; + union iphash *h = ip_hash; int i; for (i = 0; i < 3; i++) { - if (!d[(size_t) a[i]]) - { - if (!(d[(size_t) a[i]] = calloc(256, sizeof(void *)))) - return; - } + if (!(h[a[i]].idx || (h[a[i]].idx = calloc(256, sizeof(union iphash))))) + return; - d = (uint8_t **) d[(size_t) a[i]]; + h = h[a[i]].idx; } - d[(size_t) a[3]] = (uint8_t *) (intptr_t) s; + h[a[3]].sess = s; if (s > 0) LOG(4, s, session[s].tunnel, "Caching ip address %s\n", fmtaddr(nip, 0)); @@ -786,7 +793,7 @@ static void uncache_ipmap(in_addr_t ip) cache_ipmap(ip, 0); // Assign it to the NULL session. } -static void cache_ipv6map(struct in6_addr ip, int prefixlen, int s) +static void cache_ipv6map(struct in6_addr ip, int prefixlen, sessionidt s) { int i; int bytes; @@ -827,7 +834,7 @@ static void cache_ipv6map(struct in6_addr ip, int prefixlen, int s) // int cmd_show_ipcache(struct cli_def *cli, char *command, char **argv, int argc) { - char **d = (char **) ip_hash, **e, **f, **g; + union iphash *d = ip_hash, *e, *f, *g; int i, j, k, l; int count = 0; @@ -838,24 +845,28 @@ int cmd_show_ipcache(struct cli_def *cli, char *command, char **argv, int argc) for (i = 0; i < 256; ++i) { - if (!d[i]) + if (!d[i].idx) continue; - e = (char **) d[i]; + + e = d[i].idx; for (j = 0; j < 256; ++j) { - if (!e[j]) + if (!e[j].idx) continue; - f = (char **) e[j]; + + f = e[j].idx; for (k = 0; k < 256; ++k) { - if (!f[k]) + if (!f[k].idx) continue; - g = (char **)f[k]; + + g = f[k].idx; for (l = 0; l < 256; ++l) { - if (!g[l]) + if (!g[l].sess) continue; - cli_print(cli, "%7d %d.%d.%d.%d", (int) (intptr_t) g[l], i, j, k, l); + + cli_print(cli, "%7d %d.%d.%d.%d", g[l].sess, i, j, k, l); ++count; } } @@ -1063,43 +1074,63 @@ void processmpframe(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l, uint8_t e 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 %d is closing. Don't process PPP packets\n", s); - return; // closing session, PPP not processed - } - session[s].last_packet = 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 %d is closing. Don't process PPP packets\n", s); - return; // closing session, PPP not processed - } + 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 %d 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 %d 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 if (proto == PPPIPCP) + { + session[s].last_packet = session[s].last_data = time_now; + processipcp(s, t, p, l); + } + else if (proto == PPPCCP) + { + session[s].last_packet = session[s].last_data = time_now; + processccp(s, t, p, l); + } + else + { + LOG(2, s, t, "MPPP: Unsupported MP protocol 0x%04X received\n",proto); + } +} - session[s].last_packet = time_now; - processipv6in(s, t, p, l); - } - else - { - LOG(2, s, t, "MPPP: Unsupported MP protocol 0x%04X received\n",proto); - } +static void update_session_out_stat(sessionidt s, sessiont *sp, int len) +{ + increment_counter(&sp->cout, &sp->cout_wrap, len); // byte count + sp->cout_delta += len; + sp->pout++; + sp->last_data = time_now; + + sess_local[s].cout += len; // To send to master.. + sess_local[s].pout++; } // process outgoing (to tunnel) IP @@ -1114,8 +1145,7 @@ static void processipout(uint8_t *buf, int len) uint8_t *data = buf; // Keep a copy of the originals. int size = len; - uint8_t b1[MAXETHER + 20]; - uint8_t b2[MAXETHER + 20]; + uint8_t fragbuf[MAXETHER + 20]; CSTAT(processipout); @@ -1164,7 +1194,14 @@ static void processipout(uint8_t *buf, int len) } return; } + t = session[s].tunnel; + if (len > session[s].mru || (session[s].mrru && len > session[s].mrru)) + { + LOG(3, s, t, "Packet size more than session MRU\n"); + return; + } + sp = &session[s]; // DoS prevention: enforce a maximum number of packets per 0.1s for a session @@ -1236,55 +1273,80 @@ static void processipout(uint8_t *buf, int len) } // Add on L2TP header - { - 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: %d\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: %d\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... - } - } + { + bundleidt bid = 0; + if(session[s].bundle != 0 && 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]; + t = session[s].tunnel; + sp = &session[s]; + LOG(4, s, t, "MPPP: (1)Session number becomes: %d\n", s); + if(len > MINFRAGLEN) + { + // Partition the packet to "bundle[b].num_of_links" fragments + bundlet *b = &bundle[bid]; + uint32_t num_of_links = b->num_of_links; + uint32_t fraglen = len / num_of_links; + fraglen = (fraglen > session[s].mru ? session[s].mru : fraglen); + uint32_t last_fraglen = fraglen + len % num_of_links; + last_fraglen = (last_fraglen > session[s].mru ? len % num_of_links : last_fraglen); + uint32_t remain = len; + + // send the first packet + uint8_t *p = makeppp(fragbuf, sizeof(fragbuf), buf, fraglen, s, t, PPPIP, 0, bid, MP_BEGIN); + if (!p) return; + tunnelsend(fragbuf, fraglen + (p-fragbuf), t); // send it... + // statistics + update_session_out_stat(s, sp, fraglen); + remain -= fraglen; + while (remain > last_fraglen) + { + s = b->members[b->current_ses = ++b->current_ses % num_of_links]; + t = session[s].tunnel; + sp = &session[s]; + LOG(4, s, t, "MPPP: (2)Session number becomes: %d\n", s); + p = makeppp(fragbuf, sizeof(fragbuf), buf+(len - remain), fraglen, s, t, PPPIP, 0, bid, 0); + if (!p) return; + tunnelsend(fragbuf, fraglen + (p-fragbuf), t); // send it... + update_session_out_stat(s, sp, fraglen); + remain -= fraglen; + } + // send the last fragment + s = b->members[b->current_ses = ++b->current_ses % num_of_links]; + t = session[s].tunnel; + sp = &session[s]; + LOG(4, s, t, "MPPP: (2)Session number becomes: %d\n", s); + p = makeppp(fragbuf, sizeof(fragbuf), buf+(len - remain), remain, s, t, PPPIP, 0, bid, MP_END); + if (!p) return; + tunnelsend(fragbuf, remain + (p-fragbuf), t); // send it... + update_session_out_stat(s, sp, remain); + if (remain != last_fraglen) + LOG(3, s, t, "PROCESSIPOUT ERROR REMAIN != LAST_FRAGLEN, %d != %d\n", remain, last_fraglen); + } + else { + // Send it as one frame + uint8_t *p = makeppp(fragbuf, sizeof(fragbuf), buf, len, s, t, PPPIP, 0, bid, MP_BOTH_BITS); + if (!p) return; + tunnelsend(fragbuf, len + (p-fragbuf), t); // send it... + LOG(4, s, t, "MPPP: packet sent as one frame\n"); + update_session_out_stat(s, sp, len); + } + } + else + { + uint8_t *p = makeppp(fragbuf, sizeof(fragbuf), buf, len, s, t, PPPIP, 0, 0, 0); + if (!p) return; + tunnelsend(fragbuf, len + (p-fragbuf), t); // send it... + update_session_out_stat(s, sp, len); + } + } // Snooping this session, send it to intercept box if (sp->snoop_ip && sp->snoop_port) snoop_send_packet(buf, len, sp->snoop_ip, sp->snoop_port); - increment_counter(&sp->cout, &sp->cout_wrap, len); // byte count - sp->cout_delta += len; - sp->pout++; udp_tx += len; - - sess_local[s].cout += len; // To send to master.. - sess_local[s].pout++; } // process outgoing (to tunnel) IPv6 @@ -1359,10 +1421,11 @@ static void processipv6out(uint8_t * buf, int len) { 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: %d\n", s); + 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? @@ -1650,7 +1713,9 @@ void filter_session(sessionidt s, int filter_in, int filter_out) void sessionshutdown(sessionidt s, char const *reason, int cdn_result, int cdn_error, int term_cause) { int walled_garden = session[s].walled_garden; - + bundleidt b = session[s].bundle; + //delete routes only for last session in bundle (in case of MPPP) + int del_routes = !b || (bundle[b].num_of_links == 1); CSTAT(sessionshutdown); @@ -1663,7 +1728,7 @@ void sessionshutdown(sessionidt s, char const *reason, int cdn_result, int cdn_e 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); } @@ -1699,21 +1764,52 @@ void sessionshutdown(sessionidt s, char const *reason, int cdn_result, int cdn_e (session[s].route[r].ip & session[s].route[r].mask)) routed++; - routeset(s, session[s].route[r].ip, session[s].route[r].mask, 0, 0); + if (del_routes) routeset(s, session[s].route[r].ip, session[s].route[r].mask, 0, 0); session[s].route[r].ip = 0; } if (session[s].ip_pool_index == -1) // static ip { - if (!routed) routeset(s, session[s].ip, 0, 0, 0); + if (!routed && del_routes) routeset(s, session[s].ip, 0, 0, 0); session[s].ip = 0; } else free_ip_address(s); // unroute IPv6, if setup - if (session[s].ppp.ipv6cp == Opened && session[s].ipv6prefixlen) + if (session[s].ppp.ipv6cp == Opened && session[s].ipv6prefixlen && del_routes) route6set(s, session[s].ipv6route, session[s].ipv6prefixlen, 0); + + if (b) + { + // This session was part of a bundle + bundle[b].num_of_links--; + LOG(3, s, 0, "MPPP: Dropping member link: %d from bundle %d\n",s,b); + if(bundle[b].num_of_links == 0) + { + bundleclear(b); + LOG(3, s, 0, "MPPP: Kill bundle: %d (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) @@ -1999,7 +2061,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; } @@ -2096,7 +2158,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 @@ -2113,7 +2175,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); } @@ -2127,7 +2189,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? @@ -2180,9 +2242,10 @@ 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). + // 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)."; @@ -2214,7 +2277,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 @@ -2279,7 +2342,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) @@ -2287,37 +2350,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); @@ -2351,13 +2430,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 { @@ -2372,17 +2451,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 @@ -2436,7 +2515,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 @@ -2456,7 +2535,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 @@ -2502,6 +2581,8 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) "(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 @@ -2572,7 +2653,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) 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 @@ -2642,8 +2723,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 @@ -2705,7 +2786,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); @@ -2799,11 +2880,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); @@ -2816,11 +2897,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); @@ -2833,11 +2914,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); @@ -3023,37 +3104,6 @@ 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) { @@ -3195,6 +3245,45 @@ static void regular_cleanups(double period) s_actions++; } + // Drop sessions who have reached session_timeout seconds + if (session[s].session_timeout) + { + bundleidt bid = session[s].bundle; + if (bid) + { + if (time_now - bundle[bid].last_check >= 1) + { + bundle[bid].online_time += (time_now - bundle[bid].last_check) * bundle[bid].num_of_links; + bundle[bid].last_check = time_now; + if (bundle[bid].online_time >= session[s].session_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 (time_now - session[s].opened >= session[s].session_timeout) + { + sessionshutdown(s, "Session timeout", CDN_ADMIN_DISC, TERM_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)) { @@ -3219,7 +3308,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); @@ -3273,7 +3362,8 @@ static void regular_cleanups(double period) if (config->radius_accounting && config->radius_interim > 0 && session[s].ip && !session[s].walled_garden && !sess_local[s].radius // RADIUS already in progress - && time_now - sess_local[s].last_interim >= config->radius_interim) + && time_now - sess_local[s].last_interim >= config->radius_interim + && session[s].flags & SESSION_STARTED) { int rad = radiusnew(s); if (!rad) @@ -3362,7 +3452,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; @@ -3384,7 +3474,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; @@ -3872,6 +3962,7 @@ static void initdata(int optdebug, char *optconfig) config->ppp_restart_time = 3; config->ppp_max_configure = 10; config->ppp_max_failure = 5; + config->kill_timedout_sessions = 1; strcpy(config->random_device, RANDOMDEVICE); log_stream = stderr; @@ -4107,7 +4198,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. @@ -4116,7 +4207,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; @@ -4264,7 +4355,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)); @@ -4399,7 +4490,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; @@ -4698,6 +4789,40 @@ static void update_config() } } + // Guest change + guest_accounts_num = 0; + char *p2 = config->guest_user; + while (p2 && *p2) + { + char *s = strpbrk(p2, " \t,"); + if (s) + { + *s++ = 0; + while (*s == ' ' || *s == '\t') + s++; + + if (!*s) + s = 0; + } + + strcpy(guest_users[guest_accounts_num], p2); + LOG(1, 0, 0, "Guest account[%d]: %s\n", guest_accounts_num, guest_users[guest_accounts_num]); + guest_accounts_num++; + p2 = s; + } + // Rebuild the guest_user array + strcpy(config->guest_user, ""); + int ui = 0; + for (ui=0; uiguest_user, guest_users[ui]); + if (uiguest_user, ","); + } + } + + memcpy(config->old_plugins, config->plugins, sizeof(config->plugins)); if (!config->multi_read_count) config->multi_read_count = 10; if (!config->cluster_address) config->cluster_address = inet_addr(DEFAULT_MCAST_ADDR); @@ -4776,6 +4901,20 @@ int sessionsetup(sessionidt s, tunnelidt t) LOG(3, s, t, "Doing session setup for session\n"); + // Join a bundle if the MRRU option is accepted + if(session[s].mrru > 0 && session[s].bundle == 0) + { + LOG(3, s, t, "This session can be part of multilink bundle\n"); + if (join_bundle(s) > 0) + cluster_send_bundle(session[s].bundle); + else + { + LOG(0, s, t, "MPPP: Mismaching mssf option with other sessions in bundle\n"); + sessionshutdown(s, "Mismaching mssf option.", CDN_NONE, TERM_SERVICE_UNAVAILABLE); + return 0; + } + } + if (!session[s].ip) { assign_ip_address(s); @@ -4793,14 +4932,6 @@ 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 @@ -4811,25 +4942,29 @@ int sessionsetup(sessionidt s, tunnelidt t) { if (i == s) continue; if (!session[s].opened) 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; if (ip == session[i].ip) { sessionkill(i, "Duplicate IP address"); continue; } - if (config->allow_duplicate_users) - continue; - - if (session[s].walled_garden || session[i].walled_garden) - continue; - - // 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; + if (config->allow_duplicate_users) continue; + if (session[s].walled_garden || session[i].walled_garden) continue; + // Guest change + int found = 0; + int gu; + for (gu = 0; gu < guest_accounts_num; gu++) + { + if (!strcasecmp(user, guest_users[gu])) + { + found = 1; + break; + } + } + if (found) continue; // Drop the new session in case of duplicate sessionss, not the old one. if (!strcasecmp(user, session[i].user)) @@ -4837,6 +4972,8 @@ int sessionsetup(sessionidt s, tunnelidt t) } } + // no need to set a route for the same IP address of the bundle + if (!session[s].bundle || (bundle[session[s].bundle].num_of_links == 1)) { int routed = 0; @@ -4876,7 +5013,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), @@ -4979,13 +5116,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; } @@ -5369,7 +5506,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; @@ -5439,7 +5576,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;