X-Git-Url: http://git.sameswireless.fr/l2tpns.git/blobdiff_plain/777899ba6972243236778b1078ee019a929617a4..ca4801db5521d0dc9f7c7f854f62f27c55420558:/l2tpns.c?ds=sidebyside diff --git a/l2tpns.c b/l2tpns.c index 813f2c7..a592ff6 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -4,7 +4,7 @@ // Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced // vim: sw=8 ts=8 -char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.83 2005/02/09 00:45:34 bodea Exp $"; +char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.105 2005/05/26 12:17:30 bodea Exp $"; #include #include @@ -68,11 +68,10 @@ static int rand_fd = -1; // Random data source time_t basetime = 0; // base clock char hostname[1000] = ""; // us. static int tunidx; // ifr_ifindex of tun device -static uint32_t sessionid = 0; // session id for radius accounting static int syslog_log = 0; // are we logging to syslog -static FILE *log_stream = NULL; // file handle for direct logging (i.e. direct into file, not via syslog). +static FILE *log_stream = 0; // file handle for direct logging (i.e. direct into file, not via syslog). extern int cluster_sockfd; // Intra-cluster communications socket. -uint32_t last_id = 0; // Last used PPP SID. Can I kill this?? -- mo +uint32_t last_id = 0; // Unique ID for radius accounting 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 @@ -111,6 +110,7 @@ config_descriptt config_values[] = { CONFIG("primary_radius_port", radiusport[0], SHORT), CONFIG("secondary_radius_port", radiusport[1], SHORT), CONFIG("radius_accounting", radius_accounting, BOOL), + CONFIG("radius_interim", radius_interim, INT), CONFIG("radius_secret", radiussecret, STRING), CONFIG("radius_authtypes", radius_authtypes_s, STRING), CONFIG("bind_address", bind_address, IPv4), @@ -131,6 +131,7 @@ config_descriptt config_values[] = { CONFIG("cluster_interface", cluster_interface, STRING), CONFIG("cluster_hb_interval", cluster_hb_interval, INT), CONFIG("cluster_hb_timeout", cluster_hb_timeout, INT), + CONFIG("cluster_master_min_adv", cluster_master_min_adv, INT), CONFIG("ipv6_prefix", ipv6_prefix, IPv6), { NULL, 0, 0, 0 }, }; @@ -187,7 +188,7 @@ 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 tunnelidt new_tunnel(void); -static int unhide_avp(uint8_t *avp, tunnelidt t, sessionidt s, uint16_t length); +static void unhide_value(uint8_t *value, size_t len, uint16_t type, uint8_t *vector, size_t vec_len); // return internal time (10ths since process startup) static clockt now(void) @@ -660,7 +661,7 @@ sessionidt sessionbyip(in_addr_t ip) int s = lookup_ipmap(ip); CSTAT(sessionbyip); - if (s > 0 && s < MAXSESSION && session[s].tunnel) + if (s > 0 && s < MAXSESSION && session[s].opened) return (sessionidt) s; return 0; @@ -679,7 +680,7 @@ sessionidt sessionbyipv6(struct in6_addr ip) s = lookup_ipv6map(ip); } - if (s > 0 && s < MAXSESSION && session[s].tunnel) + if (s > 0 && s < MAXSESSION && session[s].opened) return s; return 0; @@ -815,8 +816,11 @@ sessionidt sessionbyuser(char *username) int s; CSTAT(sessionbyuser); - for (s = 1; s < MAXSESSION ; ++s) + for (s = 1; s <= config->cluster_highest_sessionid ; ++s) { + if (!session[s].opened) + continue; + if (session[s].walled_garden) continue; // Skip walled garden users. @@ -858,17 +862,16 @@ void send_garp(in_addr_t ip) sendarp(ifr.ifr_ifindex, mac, ip); } -// Find session by username, 0 for not found static sessiont *sessiontbysessionidt(sessionidt s) { - if (!s || s > MAXSESSION) return NULL; + if (!s || s >= MAXSESSION) return NULL; return &session[s]; } static sessionidt sessionidtbysessiont(sessiont *s) { sessionidt val = s-session; - if (s < session || val > MAXSESSION) return 0; + if (s < session || val >= MAXSESSION) return 0; return val; } @@ -1262,7 +1265,7 @@ static void control32(controlt * c, uint16_t avp, uint32_t val, uint8_t m) c->length += 10; } -// add an AVP (32 bit) +// add an AVP (string) static void controls(controlt * c, uint16_t avp, char *val, uint8_t m) { uint16_t l = ((m ? 0x8000 : 0) + strlen(val) + 6); @@ -1321,11 +1324,11 @@ static void controlnull(tunnelidt t) } // add a control message to a tunnel, and send if within window -static void controladd(controlt * c, tunnelidt t, sessionidt s) +static void controladd(controlt * c, tunnelidt t, sessionidt far) { *(uint16_t *) (c->buf + 2) = htons(c->length); // length *(uint16_t *) (c->buf + 4) = htons(tunnel[t].far); // tunnel - *(uint16_t *) (c->buf + 6) = htons(s ? session[s].far : 0); // session + *(uint16_t *) (c->buf + 6) = htons(far); // session *(uint16_t *) (c->buf + 8) = htons(tunnel[t].ns); // sequence tunnel[t].ns++; // advance sequence // link in message in to queue @@ -1357,7 +1360,7 @@ static void controladd(controlt * c, tunnelidt t, sessionidt s) // void throttle_session(sessionidt s, int rate_in, int rate_out) { - if (!session[s].tunnel) + if (!session[s].opened) return; // No-one home. if (!*session[s].user) @@ -1395,7 +1398,7 @@ void throttle_session(sessionidt s, int rate_in, int rate_out) // add/remove filters from session (-1 = no change) static void filter_session(sessionidt s, int filter_in, int filter_out) { - if (!session[s].tunnel) + if (!session[s].opened) return; // No-one home. if (!*session[s].user) @@ -1431,16 +1434,16 @@ static void filter_session(sessionidt s, int filter_in, int filter_out) } // start tidy shutdown of session -void sessionshutdown(sessionidt s, char *reason) +void sessionshutdown(sessionidt s, char *reason, int result, int error) { int walled_garden = session[s].walled_garden; CSTAT(sessionshutdown); - if (!session[s].tunnel) + if (!session[s].opened) { - LOG(3, s, session[s].tunnel, "Called sessionshutdown on a session with no tunnel.\n"); + LOG(3, s, session[s].tunnel, "Called sessionshutdown on an unopened session.\n"); return; // not a live session } @@ -1451,25 +1454,21 @@ void sessionshutdown(sessionidt s, char *reason) run_plugins(PLUGIN_KILL_SESSION, &data); } - if (session[s].opened && !walled_garden && !session[s].die) + if (session[s].ip && !walled_garden && !session[s].die) { // RADIUS Stop message - uint16_t r = session[s].radius; + uint16_t r = sess_local[s].radius; if (!r) + r = radiusnew(s); + + if (r) { - if (!(r = radiusnew(s))) - { - LOG(1, s, session[s].tunnel, "No free RADIUS sessions for Stop message\n"); - STAT(radius_overflow); - } - else - { - random_data(radius[r].auth, sizeof(radius[r].auth)); - } + // stop, if not already trying + if (radius[r].state != RADIUSSTOP) + radiussend(r, RADIUSSTOP); } - - if (r && radius[r].state != RADIUSSTOP) - radiussend(r, RADIUSSTOP); // stop, if not already trying + else + LOG(1, s, session[s].tunnel, "No free RADIUS sessions for Stop message\n"); // Save counters to dump to accounting file if (*config->accounting_dir && shut_acct_n < sizeof(shut_acct) / sizeof(*shut_acct)) @@ -1506,15 +1505,25 @@ void sessionshutdown(sessionidt s, char *reason) if (session[s].throttle_in || session[s].throttle_out) // Unthrottle if throttled. throttle_session(s, 0, 0); + if (result) { // Send CDN controlt *c = controlnew(14); // sending CDN - control16(c, 1, 3, 1); // result code (admin reasons - TBA make error, general error, add message + if (error) + { + char buf[4]; + *(uint16_t *) buf = htons(result); + *(uint16_t *) (buf+2) = htons(error); + controlb(c, 1, buf, 4, 1); + } + else + control16(c, 1, result, 1); + control16(c, 14, s, 1); // assigned session (our end) - controladd(c, session[s].tunnel, s); // send the message + controladd(c, session[s].tunnel, session[s].far); // send the message } if (!session[s].die) - session[s].die = now() + 150; // Clean up in 15 seconds + session[s].die = TIME + 150; // Clean up in 15 seconds // update filter refcounts if (session[s].filter_in) ip_filters[session[s].filter_in - 1].used--; @@ -1526,7 +1535,7 @@ void sessionshutdown(sessionidt s, char *reason) void sendipcp(tunnelidt t, sessionidt s) { uint8_t buf[MAXCONTROL]; - uint16_t r = session[s].radius; + uint16_t r = sess_local[s].radius; uint8_t *q; CSTAT(sendipcp); @@ -1534,6 +1543,12 @@ void sendipcp(tunnelidt t, sessionidt s) if (!r) r = radiusnew(s); + if (!r) + { + sessionshutdown(s, "No free RADIUS sessions for IPCP", 3, 0); + return; + } + if (radius[r].state != RADIUSIPCP) { radius[r].state = RADIUSIPCP; @@ -1544,7 +1559,7 @@ void sendipcp(tunnelidt t, sessionidt s) if (radius[r].try > 10) { radiusclear(r, s); // Clear radius session. - sessionshutdown(s, "No reply on IPCP"); + sessionshutdown(s, "No reply to IPCP.", 3, 0); return; } @@ -1552,7 +1567,7 @@ void sendipcp(tunnelidt t, sessionidt s) if (!q) return; *q = ConfigReq; - q[1] = r << RADIUS_SHIFT; // ID, dont care, we only send one type of request + q[1] = r >> RADIUS_SHIFT; // ID, dont care, we only send one type of request *(uint16_t *) (q + 2) = htons(10); q[4] = 3; q[5] = 6; @@ -1574,7 +1589,7 @@ void sendipcp(tunnelidt t, sessionidt s) if (!q) return; *q = ConfigReq; - q[1] = r << RADIUS_SHIFT; // ID, don't care, we + q[1] = r >> RADIUS_SHIFT; // ID, don't care, we // only send one type // of request *(uint16_t *) (q + 2) = htons(14); @@ -1588,24 +1603,39 @@ void sendipcp(tunnelidt t, sessionidt s) } } +static void sessionclear(sessionidt s) +{ + memset(&session[s], 0, sizeof(session[s])); + memset(&sess_local[s], 0, sizeof(sess_local[s])); + memset(&cli_session_actions[s], 0, sizeof(cli_session_actions[s])); + + session[s].tunnel = T_FREE; // Mark it as free. + session[s].next = sessionfree; + sessionfree = s; +} + // kill a session now -static void sessionkill(sessionidt s, char *reason) +void sessionkill(sessionidt s, char *reason) { CSTAT(sessionkill); - session[s].die = now(); - sessionshutdown(s, reason); // close radius/routes, etc. - if (session[s].radius) - radiusclear(session[s].radius, s); // cant send clean accounting data, session is killed + if (!session[s].opened) // not alive + return; - LOG(2, s, session[s].tunnel, "Kill session %d (%s): %s\n", s, session[s].user, 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); + return; + } - memset(&session[s], 0, sizeof(session[s])); - session[s].tunnel = T_FREE; // Mark it as free. - session[s].next = sessionfree; - sessionfree = s; - cli_session_actions[s].action = 0; + session[s].die = TIME; + sessionshutdown(s, reason, 3, 0); // 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); + sessionclear(s); cluster_send_session(s); } @@ -1636,19 +1666,19 @@ static void tunnelkill(tunnelidt t, char *reason) controlfree = c; } // kill sessions - for (s = 1; s < MAXSESSION; s++) + for (s = 1; s <= config->cluster_highest_sessionid ; ++s) if (session[s].tunnel == t) sessionkill(s, reason); // free tunnel tunnelclear(t); LOG(1, 0, t, "Kill tunnel %d: %s\n", t, reason); - cli_tunnel_actions[s].action = 0; + cli_tunnel_actions[t].action = 0; cluster_send_tunnel(t); } // shut down a tunnel cleanly -static void tunnelshutdown(tunnelidt t, char *reason) +static void tunnelshutdown(tunnelidt t, char *reason, int result, int error, char *msg) { sessionidt s; @@ -1663,19 +1693,40 @@ static void tunnelshutdown(tunnelidt t, char *reason) LOG(1, 0, t, "Shutting down tunnel %d (%s)\n", t, reason); // close session - for (s = 1; s < MAXSESSION; s++) + for (s = 1; s <= config->cluster_highest_sessionid ; ++s) if (session[s].tunnel == t) - sessionshutdown(s, reason); + sessionshutdown(s, reason, 3, 0); tunnel[t].state = TUNNELDIE; - tunnel[t].die = now() + 700; // Clean up in 70 seconds + tunnel[t].die = TIME + 700; // Clean up in 70 seconds cluster_send_tunnel(t); // TBA - should we wait for sessions to stop? - { // Send StopCCN - controlt *c = controlnew(4); // sending StopCCN - control16(c, 1, 1, 1); // result code (admin reasons - TBA make error, general error, add message) - control16(c, 9, t, 1); // assigned tunnel (our end) - controladd(c, t, 0); // send the message + if (result) + { + controlt *c = controlnew(4); // sending StopCCN + if (error) + { + char buf[64]; + int l = 4; + *(uint16_t *) buf = htons(result); + *(uint16_t *) (buf+2) = htons(error); + if (msg) + { + int m = strlen(msg); + if (m + 4 > sizeof(buf)) + m = sizeof(buf) - 4; + + memcpy(buf+4, msg, m); + l += m; + } + + controlb(c, 1, buf, l, 1); + } + else + control16(c, 1, result, 1); + + control16(c, 9, t, 1); // assigned tunnel (our end) + controladd(c, t, 0); // send the message } } @@ -1750,13 +1801,12 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) { // control uint16_t message = 0xFFFF; // message type uint8_t fatal = 0; - uint8_t mandatorymessage = 0; - uint8_t chap = 0; // if CHAP being used + uint8_t mandatory = 0; + uint8_t authtype = 0; // proxy auth type uint16_t asession = 0; // assigned session uint32_t amagic = 0; // magic number uint8_t aflags = 0; // flags from last LCF uint16_t version = 0x0100; // protocol version (we handle 0.0 as well and send that back just in case) - int requestchap = 0; // do we request PAP instead of original CHAP request? char called[MAXTEL] = ""; // called number char calling[MAXTEL] = ""; // calling number @@ -1766,7 +1816,10 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) return; } - if ((*buf & 0xCA) != 0xC8) + // control messages must have bits 0x80|0x40|0x08 + // (type, length and sequence) set, and bits 0x02|0x01 + // (offset and priority) clear + if ((*buf & 0xCB) != 0xC8) { LOG(1, s, t, "Bad control header %02X\n", *buf); STAT(tunnel_rx_errors); @@ -1876,71 +1929,111 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) } if (l) { // if not a null message + int result = 0; + int error = 0; + char *msg = 0; + // process AVPs - while (l && !(fatal & 0x80)) + while (l && !(fatal & 0x80)) // 0x80 = mandatory AVP { uint16_t n = (ntohs(*(uint16_t *) p) & 0x3FF); uint8_t *b = p; uint8_t flags = *p; uint16_t mtype; - p += n; // next - if (l < n) + if (n > l) { LOG(1, s, t, "Invalid length in AVP\n"); STAT(tunnel_rx_errors); - fatal = flags; return; } + p += n; // next l -= n; + if (flags & 0x3C) // reserved bits, should be clear + { + LOG(1, s, t, "Unrecognised AVP flags %02X\n", *b); + fatal = flags; + result = 2; // general error + error = 3; // reserved field non-zero + msg = 0; + continue; // next + } + b += 2; + if (*(uint16_t *) (b)) + { + LOG(2, s, t, "Unknown AVP vendor %d\n", ntohs(*(uint16_t *) (b))); + fatal = flags; + result = 2; // general error + error = 6; // generic vendor-specific error + msg = "unsupported vendor-specific"; + continue; // next + } + b += 2; + mtype = ntohs(*(uint16_t *) (b)); + b += 2; + n -= 6; + if (flags & 0x40) { + uint16_t orig_len; + // handle hidden AVPs if (!*config->l2tpsecret) { LOG(1, s, t, "Hidden AVP requested, but no L2TP secret.\n"); fatal = flags; + result = 2; // general error + error = 6; // generic vendor-specific error + msg = "secret not specified"; continue; } if (!session[s].random_vector_length) { LOG(1, s, t, "Hidden AVP requested, but no random vector.\n"); fatal = flags; + result = 2; // general error + error = 6; // generic + msg = "no random vector"; continue; } + if (n < 8) + { + LOG(2, s, t, "Short hidden AVP.\n"); + fatal = flags; + result = 2; // general error + error = 2; // length is wrong + msg = 0; + continue; + } + LOG(4, s, t, "Hidden AVP\n"); + // Unhide the AVP - n = unhide_avp(b, t, s, n); - if (n == 0) + unhide_value(b, n, mtype, session[s].random_vector, session[s].random_vector_length); + + orig_len = ntohs(*(uint16_t *) b); + if (orig_len > n + 2) { + LOG(1, s, t, "Original length %d too long in hidden AVP of length %d; wrong secret?\n", + orig_len, n); + fatal = flags; + result = 2; // general error + error = 2; // length is wrong + msg = 0; continue; } + + b += 2; + n = orig_len; } - if (*b & 0x3C) - { - LOG(1, s, t, "Unrecognised AVP flags %02X\n", *b); - fatal = flags; - continue; // next - } - b += 2; - if (*(uint16_t *) (b)) - { - LOG(2, s, t, "Unknown AVP vendor %d\n", ntohs(*(uint16_t *) (b))); - fatal = flags; - continue; // next - } - b += 2; - mtype = ntohs(*(uint16_t *) (b)); - b += 2; - n -= 6; LOG(4, s, t, " AVP %d (%s) len %d\n", mtype, avp_name(mtype), n); switch (mtype) { case 0: // message type message = ntohs(*(uint16_t *) b); + mandatory = flags & 0x80; LOG(4, s, t, " Message type = %d (%s)\n", *b, l2tp_message_type(message)); - mandatorymessage = flags; break; case 1: // result code { @@ -1975,6 +2068,9 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) { // allow 0.0 and 1.0 LOG(1, s, t, " Bad protocol version %04X\n", version); fatal = flags; + result = 5; // unspported protocol version + error = 0x0100; // supported version + msg = 0; continue; // next } } @@ -1993,14 +2089,14 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) // LOG(4, s, t, "Firmware revision\n"); break; case 7: // host name - memset(tunnel[t].hostname, 0, 128); - memcpy(tunnel[t].hostname, b, (n >= 127) ? 127 : n); + memset(tunnel[t].hostname, 0, sizeof(tunnel[t].hostname)); + memcpy(tunnel[t].hostname, b, (n < sizeof(tunnel[t].hostname)) ? n : sizeof(tunnel[t].hostname) - 1); LOG(4, s, t, " Tunnel hostname = \"%s\"\n", tunnel[t].hostname); // TBA - to send to RADIUS break; case 8: // vendor name memset(tunnel[t].vendor, 0, sizeof(tunnel[t].vendor)); - memcpy(tunnel[t].vendor, b, (n >= sizeof(tunnel[t].vendor) - 1) ? sizeof(tunnel[t].vendor) - 1 : n); + memcpy(tunnel[t].vendor, b, (n < sizeof(tunnel[t].vendor)) ? n : sizeof(tunnel[t].vendor) - 1); LOG(4, s, t, " Vendor name = \"%s\"\n", tunnel[t].vendor); break; case 9: // assigned tunnel @@ -2040,13 +2136,13 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) // TBA break; case 21: // called number - memset(called, 0, MAXTEL); - memcpy(called, b, (n >= MAXTEL) ? (MAXTEL-1) : n); + memset(called, 0, sizeof(called)); + memcpy(called, b, (n < sizeof(called)) ? n : sizeof(called) - 1); LOG(4, s, t, " Called <%s>\n", called); break; case 22: // calling number - memset(calling, 0, MAXTEL); - memcpy(calling, b, (n >= MAXTEL) ? (MAXTEL-1) : n); + memset(calling, 0, sizeof(calling)); + memcpy(calling, b, (n < sizeof(calling)) ? n : sizeof(calling) - 1); LOG(4, s, t, " Calling <%s>\n", calling); break; case 23: // subtype @@ -2059,8 +2155,9 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) else { // AS5300s send connect speed as a string - char tmp[30] = {0}; - memcpy(tmp, b, (n >= 30) ? 30 : n); + char tmp[30]; + memset(tmp, 0, sizeof(tmp)); + memcpy(tmp, b, (n < sizeof(tmp)) ? n : sizeof(tmp) - 1); session[s].tx_connect_speed = atol(tmp); } LOG(4, s, t, " TX connect speed <%u>\n", session[s].tx_connect_speed); @@ -2073,8 +2170,9 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) else { // AS5300s send connect speed as a string - char tmp[30] = {0}; - memcpy(tmp, b, (n >= 30) ? 30 : n); + char tmp[30]; + memset(tmp, 0, sizeof(tmp)); + memcpy(tmp, b, (n < sizeof(tmp)) ? n : sizeof(tmp) - 1); session[s].rx_connect_speed = atol(tmp); } LOG(4, s, t, " RX connect speed <%u>\n", session[s].rx_connect_speed); @@ -2089,47 +2187,51 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) { uint16_t atype = ntohs(*(uint16_t *)b); LOG(4, s, t, " Proxy Auth Type %d (%s)\n", atype, auth_type(atype)); - requestchap = (atype == 2); + if (atype == 2) + authtype = AUTHCHAP; + else if (atype == 3) + authtype = AUTHPAP; + break; } case 30: // Proxy Authentication Name { - char authname[64] = {0}; - memcpy(authname, b, (n > 63) ? 63 : n); + char authname[64]; + memset(authname, 0, sizeof(authname)); + memcpy(authname, b, (n < sizeof(authname)) ? n : sizeof(authname) - 1); LOG(4, s, t, " Proxy Auth Name (%s)\n", authname); break; } case 31: // Proxy Authentication Challenge { - memcpy(radius[session[s].radius].auth, b, 16); LOG(4, s, t, " Proxy Auth Challenge\n"); + if (sess_local[s].radius) + memcpy(radius[sess_local[s].radius].auth, b, 16); break; } case 32: // Proxy Authentication ID { uint16_t authid = ntohs(*(uint16_t *)(b)); LOG(4, s, t, " Proxy Auth ID (%d)\n", authid); - if (session[s].radius) - radius[session[s].radius].id = authid; + if (sess_local[s].radius) + radius[sess_local[s].radius].id = authid; break; } case 33: // Proxy Authentication Response - { - char authresp[64] = {0}; - memcpy(authresp, b, (n > 63) ? 63 : n); - LOG(4, s, t, " Proxy Auth Response\n"); - break; - } - case 27: // last send lcp + LOG(4, s, t, " Proxy Auth Response\n"); + break; + case 27: // last sent lcp { // find magic number uint8_t *p = b, *e = p + n; while (p + 1 < e && p[1] && p + p[1] <= e) { if (*p == 5 && p[1] == 6) // Magic-Number amagic = ntohl(*(uint32_t *) (p + 2)); - else if (*p == 3 && p[1] == 5 && *(uint16_t *) (p + 2) == htons(PPPCHAP) && p[4] == 5) // Authentication-Protocol - chap = 1; + else if (*p == 3 && p[1] == 4 && *(uint16_t *) (p + 2) == htons(PPPPAP)) // Authentication-Protocol (PAP) + authtype = AUTHPAP; + else if (*p == 3 && p[1] == 5 && *(uint16_t *) (p + 2) == htons(PPPCHAP) && p[4] == 5) // Authentication-Protocol (CHAP) + authtype = AUTHCHAP; else if (*p == 7) // Protocol-Field-Compression aflags |= SESSIONPFC; else if (*p == 8) // Address-and-Control-Field-Compression @@ -2151,14 +2253,20 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) session[s].random_vector_length = n; break; default: - LOG(2, s, t, " Unknown AVP type %d\n", mtype); - fatal = flags; - continue; // next + { + static char e[] = "unknown AVP 0xXXXX"; + LOG(2, s, t, " Unknown AVP type %d\n", mtype); + fatal = flags; + result = 2; // general error + error = 8; // unknown mandatory AVP + sprintf((msg = e) + 14, "%04x", mtype); + continue; // next + } } } // process message if (fatal & 0x80) - tunnelshutdown(t, "Unknown Mandatory AVP"); + tunnelshutdown(t, "Invalid mandatory AVP", result, error, msg); else switch (message) { @@ -2170,7 +2278,7 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) controls(c, 7, tunnel[t].hostname, 1); // host name (TBA) if (chapresponse) controlb(c, 13, chapresponse, 16, 1); // Challenge response control16(c, 9, t, 1); // assigned tunnel - controladd(c, t, s); // send the resply + controladd(c, t, 0); // send the resply } tunnel[t].state = TUNNELOPENING; break; @@ -2183,8 +2291,7 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) break; case 4: // StopCCN controlnull(t); // ack - tunnelshutdown(t, "Stopped"); // Shut down cleanly - tunnelkill(t, "Stopped"); // Immediately force everything dead + tunnelshutdown(t, "Stopped", 0, 0, 0); // Shut down cleanly break; case 6: // HELLO controlnull(t); // simply ACK @@ -2199,15 +2306,9 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) // TBA break; case 10: // ICRQ - if (!sessionfree) - { - STAT(session_overflow); - tunnelshutdown(t, "No free sessions"); - } - else + if (sessionfree) { uint16_t r; - controlt *c; s = sessionfree; sessionfree = session[s].next; @@ -2217,31 +2318,40 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) config->cluster_highest_sessionid = s; // make a RADIUS session - if (!(r = radiusnew(s))) + if ((r = radiusnew(s))) { - LOG(1, s, t, "No free RADIUS sessions for ICRQ\n"); - sessionkill(s, "no free RADIUS sesions"); - return; + controlt *c = controlnew(11); // sending ICRP + session[s].opened = time_now; + session[s].tunnel = t; + session[s].far = asession; + session[s].last_packet = time_now; + LOG(3, s, t, "New session (%d/%d)\n", tunnel[t].far, session[s].far); + control16(c, 14, s, 1); // assigned session + controladd(c, t, asession); // send the reply + + strncpy(radius[r].calling, calling, sizeof(radius[r].calling) - 1); + strncpy(session[s].called, called, sizeof(session[s].called) - 1); + strncpy(session[s].calling, calling, sizeof(session[s].calling) - 1); + STAT(session_created); + break; } - c = controlnew(11); // sending ICRP - session[s].id = sessionid++; - session[s].opened = time(NULL); - session[s].tunnel = t; - session[s].far = asession; - session[s].last_packet = time_now; - LOG(3, s, t, "New session (%d/%d)\n", tunnel[t].far, session[s].far); - control16(c, 14, s, 1); // assigned session - controladd(c, t, s); // send the reply - - // Generate a random challenge - random_data(radius[r].auth, sizeof(radius[r].auth)); - strncpy(radius[r].calling, calling, sizeof(radius[r].calling) - 1); - strncpy(session[s].called, called, sizeof(session[s].called) - 1); - strncpy(session[s].calling, calling, sizeof(session[s].calling) - 1); - STAT(session_created); + + LOG(1, s, t, "No free RADIUS sessions for ICRQ\n"); + sessionclear(s); } - break; + else + { + STAT(session_overflow); + LOG(1, 0, t, "No free sessions\n"); + } + + { + controlt *c = controlnew(14); // CDN + control16(c, 1, 4, 1); // temporary lack of resources + controladd(c, session[s].tunnel, asession); // send the message + } + return; case 11: // ICRP // TBA break; @@ -2251,21 +2361,24 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) session[s].l2tp_flags = aflags; // set flags received LOG(3, s, t, "Magic %X Flags %X\n", amagic, aflags); controlnull(t); // ack - // In CHAP state, request PAP instead - if (requestchap) - initlcp(t, s); + // proxy authentication type is not supported + if (!(config->radius_authtypes & authtype)) + authtype = config->radius_authprefer; + + // start LCP + sendlcp(t, s, authtype); break; case 14: // CDN controlnull(t); // ack - sessionshutdown(s, "Closed (Received CDN)"); + sessionshutdown(s, "Closed (Received CDN).", 0, 0); break; case 0xFFFF: LOG(1, s, t, "Missing message type\n"); break; default: STAT(tunnel_rx_errors); - if (mandatorymessage & 0x80) - tunnelshutdown(t, "Unknown message"); + if (mandatory) + tunnelshutdown(t, "Unknown message type", 2, 6, "unknown message type"); else LOG(1, s, t, "Unknown message type %d\n", message); break; @@ -2306,7 +2419,7 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) l -= 2; } - if (s && !session[s].tunnel) // Is something wrong?? + if (s && !session[s].opened) // Is something wrong?? { if (!config->cluster_iam_master) { @@ -2316,9 +2429,7 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) } - LOG(1, s, t, "UDP packet contains session %d but no session[%d].tunnel " - "exists (LAC said tunnel = %d). Dropping packet.\n", s, s, t); - + LOG(1, s, t, "UDP packet contains session which is not opened. Dropping packet.\n"); STAT(tunnel_rx_errors); return; } @@ -2512,7 +2623,7 @@ static int regular_cleanups(void) if (a & CLI_TUN_KILL) { LOG(2, 0, t, "Dropping tunnel by CLI\n"); - tunnelshutdown(t, "Requested by administrator"); + tunnelshutdown(t, "Requested by administrator", 1, 0, 0); } } @@ -2525,34 +2636,37 @@ static int regular_cleanups(void) if (s > config->cluster_highest_sessionid) s = 1; - if (!session[s].tunnel) // Session isn't in use + if (!session[s].opened) // Session isn't in use continue; - if (!session[s].die && session[s].ip && !(session[s].flags & SF_IPCP_ACKED)) + // check for expired sessions + if (session[s].die) { - // IPCP has not completed yet. Resend - LOG(3, s, session[s].tunnel, "No ACK for initial IPCP ConfigReq... resending\n"); - sendipcp(session[s].tunnel, s); + if (session[s].die <= TIME) + { + sessionkill(s, "Expired"); + if (++count >= MAX_ACTIONS) break; + } + continue; } - // check for expired sessions - if (session[s].die && session[s].die <= TIME) + if (session[s].ip && !(session[s].flags & SF_IPCP_ACKED)) { - sessionkill(s, "Expired"); - if (++count >= MAX_ACTIONS) break; - continue; + // IPCP has not completed yet. Resend + LOG(3, s, session[s].tunnel, "No ACK for initial IPCP ConfigReq... resending\n"); + sendipcp(session[s].tunnel, s); } // 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"); + sessionshutdown(s, "No response to LCP ECHO requests.", 3, 0); STAT(session_timeout); if (++count >= MAX_ACTIONS) break; continue; } - // No data in IDLE_TIMEOUT seconds, send LCP ECHO + // No data in ECHO_TIMEOUT seconds, send LCP ECHO if (session[s].user[0] && (time_now - session[s].last_packet >= ECHO_TIMEOUT)) { uint8_t b[MAXCONTROL] = {0}; @@ -2580,7 +2694,7 @@ static int regular_cleanups(void) if (a & CLI_SESS_KILL) { LOG(2, s, session[s].tunnel, "Dropping session by CLI\n"); - sessionshutdown(s, "Requested by administrator"); + sessionshutdown(s, "Requested by administrator.", 3, 0); a = 0; // dead, no need to check for other actions } @@ -2639,6 +2753,29 @@ static int regular_cleanups(void) if (++count >= MAX_ACTIONS) break; } + + // RADIUS interim accounting + 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) + { + if (!(r = radiusnew(s))) + { + LOG(1, s, session[s].tunnel, "No free RADIUS sessions for Interim message\n"); + STAT(radius_overflow); + continue; + } + + LOG(3, s, session[s].tunnel, "Sending RADIUS Interim for %s (%u)\n", + session[s].user, session[s].unique_id); + + radiussend(r, RADIUSINTERIM); + sess_local[s].last_interim = time_now; + + if (++count >= MAX_ACTIONS) + break; + } } if (*config->accounting_dir) @@ -3051,24 +3188,37 @@ static void initdata(int optdebug, char *optconfig) { int i; - if (!(_statistics = shared_malloc(sizeof(struct Tstats)))) - { - LOG(0, 0, 0, "Error doing malloc for _statistics: %s\n", strerror(errno)); - exit(1); - } if (!(config = shared_malloc(sizeof(configt)))) { - LOG(0, 0, 0, "Error doing malloc for configuration: %s\n", strerror(errno)); + fprintf(stderr, "Error doing malloc for configuration: %s\n", strerror(errno)); exit(1); } + memset(config, 0, sizeof(configt)); time(&config->start_time); strncpy(config->config_file, optconfig, strlen(optconfig)); config->debug = optdebug; config->num_tbfs = MAXTBFS; config->rl_rate = 28; // 28kbps + config->cluster_master_min_adv = 1; strcpy(config->random_device, RANDOMDEVICE); + log_stream = stderr; + +#ifdef RINGBUFFER + if (!(ringbuffer = shared_malloc(sizeof(struct Tringbuffer)))) + { + LOG(0, 0, 0, "Error doing malloc for ringbuffer: %s\n", strerror(errno)); + exit(1); + } + memset(ringbuffer, 0, sizeof(struct Tringbuffer)); +#endif + + if (!(_statistics = shared_malloc(sizeof(struct Tstats)))) + { + LOG(0, 0, 0, "Error doing malloc for _statistics: %s\n", strerror(errno)); + exit(1); + } if (!(tunnel = shared_malloc(sizeof(tunnelt) * MAXTUNNEL))) { LOG(0, 0, 0, "Error doing malloc for tunnels: %s\n", strerror(errno)); @@ -3105,15 +3255,6 @@ static void initdata(int optdebug, char *optconfig) } memset(ip_filters, 0, sizeof(ip_filtert) * MAXFILTER); -#ifdef RINGBUFFER - if (!(ringbuffer = shared_malloc(sizeof(struct Tringbuffer)))) - { - LOG(0, 0, 0, "Error doing malloc for ringbuffer: %s\n", strerror(errno)); - exit(1); - } - memset(ringbuffer, 0, sizeof(struct Tringbuffer)); -#endif - if (!(cli_session_actions = shared_malloc(sizeof(struct cli_session_actions) * MAXSESSION))) { LOG(0, 0, 0, "Error doing malloc for cli session actions: %s\n", strerror(errno)); @@ -3269,8 +3410,9 @@ void rebuild_address_pool(void) for (i = 0; i < MAXSESSION; ++i) { int ipid; - if (!session[i].ip || !session[i].tunnel) + if (!(session[i].opened && session[i].ip)) continue; + ipid = - lookup_ipmap(htonl(session[i].ip)); if (session[i].ip_pool_index < 0) @@ -3680,9 +3822,11 @@ int main(int argc, char *argv[]) static void sighup_handler(int sig) { - if (log_stream && log_stream != stderr) + if (log_stream) { - fclose(log_stream); + if (log_stream != stderr) + fclose(log_stream); + log_stream = NULL; } @@ -3744,7 +3888,7 @@ static void sigquit_handler(int sig) for (i = 1; i < MAXTUNNEL; i++) { if (tunnel[i].ip || tunnel[i].state) - tunnelshutdown(i, "L2TPNS Closing"); + tunnelshutdown(i, "L2TPNS Closing", 6, 0, 0); } } @@ -3804,7 +3948,9 @@ static void update_config() syslog_log = 0; if (log_stream) { - fclose(log_stream); + if (log_stream != stderr) + fclose(log_stream); + log_stream = NULL; } @@ -3852,7 +3998,7 @@ static void update_config() // test twice, In case someone works with // a secondary radius server without defining // a primary one, this will work even then. - if (i>0 && !config->radiusport[i]) + if (i > 0 && !config->radiusport[i]) config->radiusport[i] = config->radiusport[i-1]; if (!config->radiusport[i]) config->radiusport[i] = RADPORT; @@ -3861,12 +4007,12 @@ static void update_config() if (!config->numradiusservers) LOG(0, 0, 0, "No RADIUS servers defined!\n"); - config->num_radfds = 2 << RADIUS_SHIFT; + config->num_radfds = 1 << RADIUS_SHIFT; // parse radius_authtypes_s config->radius_authtypes = config->radius_authprefer = 0; p = config->radius_authtypes_s; - while (*p) + while (p && *p) { char *s = strpbrk(p, " \t,"); int type = 0; @@ -3886,11 +4032,13 @@ static void update_config() else if (!strncasecmp("pap", p, strlen(p))) type = AUTHPAP; else - LOG(0, 0, 0, "Invalid RADIUS authentication type \"%s\"", p); + LOG(0, 0, 0, "Invalid RADIUS authentication type \"%s\"\n", p); config->radius_authtypes |= type; if (!config->radius_authprefer) config->radius_authprefer = type; + + p = s; } if (!config->radius_authtypes) @@ -4016,13 +4164,13 @@ int sessionsetup(tunnelidt t, sessionidt s) LOG(3, s, t, "Doing session setup for session\n"); - if (!session[s].ip || session[s].ip == 0xFFFFFFFE) + if (!session[s].ip) { assign_ip_address(s); if (!session[s].ip) { LOG(0, s, t, " No IP allocated. The IP address pool is FULL!\n"); - sessionshutdown(s, "No IP addresses available"); + sessionshutdown(s, "No IP addresses available.", 2, 7); return 0; } LOG(3, s, t, " No IP allocated. Assigned %s from pool\n", @@ -4290,6 +4438,7 @@ static int add_plugin(char *plugin_name) radiusnew, radiussend, getconfig, + sessionshutdown, sessionkill, throttle_session, cluster_send_session, @@ -4619,7 +4768,7 @@ void become_master(void) { for (s = 1; s <= config->cluster_highest_sessionid ; ++s) { - if (!session[s].tunnel) // Not an in-use session. + if (!session[s].opened) // Not an in-use session. continue; run_plugins(PLUGIN_NEW_SESSION_MASTER, &session[s]); @@ -4651,7 +4800,7 @@ int cmd_show_hist_idle(struct cli_def *cli, char *command, char **argv, int argc for (s = 1; s <= config->cluster_highest_sessionid ; ++s) { int idle; - if (!session[s].tunnel) + if (!session[s].opened) continue; idle = time_now - session[s].last_packet; @@ -4689,7 +4838,7 @@ int cmd_show_hist_open(struct cli_def *cli, char *command, char **argv, int argc for (s = 1; s <= config->cluster_highest_sessionid ; ++s) { int open = 0, d; - if (!session[s].tunnel) + if (!session[s].opened) continue; d = time_now - session[s].opened; @@ -4716,75 +4865,45 @@ int cmd_show_hist_open(struct cli_def *cli, char *command, char **argv, int argc /* Unhide an avp. * - * This unencodes the AVP using the L2TP CHAP secret and the - * previously stored random vector. It replaces the hidden data with - * the cleartext data and returns the length of the cleartext data - * (including the AVP "header" of 6 bytes). - * - * Based on code from rp-l2tpd by Roaring Penguin Software Inc. + * This unencodes the AVP using the L2TP secret and the previously + * stored random vector. It overwrites the hidden data with the + * unhidden AVP subformat. */ -static int unhide_avp(uint8_t *avp, tunnelidt t, sessionidt s, uint16_t length) +static void unhide_value(uint8_t *value, size_t len, uint16_t type, uint8_t *vector, size_t vec_len) { MD5_CTX ctx; - uint8_t *cursor; uint8_t digest[16]; - uint8_t working_vector[16]; - uint16_t hidden_length; - uint8_t type[2]; - size_t done, todo; - uint8_t *output; - - // Find the AVP type. - type[0] = *(avp + 4); - type[1] = *(avp + 5); - - // Line up with the hidden data - cursor = output = avp + 6; + uint8_t *last; + size_t d = 0; // Compute initial pad MD5Init(&ctx); - MD5Update(&ctx, type, 2); + MD5Update(&ctx, (uint8_t) (type >> 8) & 0xff, 1); + MD5Update(&ctx, (uint8_t) type & 0xff, 1); MD5Update(&ctx, config->l2tpsecret, strlen(config->l2tpsecret)); - MD5Update(&ctx, session[s].random_vector, session[s].random_vector_length); + MD5Update(&ctx, vector, vec_len); MD5Final(digest, &ctx); - // Get hidden length - hidden_length = ((uint16_t) (digest[0] ^ cursor[0])) * 256 + (uint16_t) (digest[1] ^ cursor[1]); - - // Keep these for later use - working_vector[0] = *cursor; - working_vector[1] = *(cursor + 1); - cursor += 2; - - if (hidden_length > length - 8) - { - LOG(1, s, t, "Hidden length %d too long in AVP of length %d\n", (int) hidden_length, (int) length); - return 0; - } + // pointer to last decoded 16 octets + last = value; - /* Decrypt remainder */ - done = 2; - todo = hidden_length; - while (todo) + while (len > 0) { - working_vector[done] = *cursor; - *output = digest[done] ^ *cursor; - ++output; - ++cursor; - --todo; - ++done; - if (done == 16 && todo) + // calculate a new pad based on the last decoded block + if (d >= sizeof(digest)) { - // Compute new digest - done = 0; MD5Init(&ctx); MD5Update(&ctx, config->l2tpsecret, strlen(config->l2tpsecret)); - MD5Update(&ctx, &working_vector, 16); + MD5Update(&ctx, last, sizeof(digest)); MD5Final(digest, &ctx); + + d = 0; + last = value; } - } - return hidden_length + 6; + *value++ ^= digest[d++]; + len--; + } } static int ip_filter_port(ip_filter_portt *p, uint16_t port)