X-Git-Url: http://git.sameswireless.fr/l2tpns.git/blobdiff_plain/2d66b44732405bb788287bed726de637a20df68f..57a847d9a36386aefb1d61bdb40399d9e8f5b463:/l2tpns.c diff --git a/l2tpns.c b/l2tpns.c index ab9aded..b4828c3 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.147 2005-11-14 08:38:02 bodea Exp $"; +char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.162 2006-04-23 23:18:31 bodea Exp $"; #include #include @@ -96,9 +96,9 @@ uint32_t eth_tx = 0; static uint32_t ip_pool_size = 1; // Size of the pool of addresses used for dynamic address allocation. time_t time_now = 0; // Current time in seconds since epoch. static char time_now_string[64] = {0}; // Current time as a string. -int time_changed = 0; // time_now changed +static int time_changed = 0; // time_now changed char main_quit = 0; // True if we're in the process of exiting. -char main_reload = 0; // Re-load pending +static char main_reload = 0; // Re-load pending linked_list *loaded_plugins; linked_list *plugins[MAX_PLUGIN_TYPES]; @@ -200,7 +200,7 @@ static void initplugins(void); static int add_plugin(char *plugin_name); static int remove_plugin(char *plugin_name); static void plugins_done(void); -static void processcontrol(uint8_t *buf, int len, struct sockaddr_in *addr, int alen); +static void processcontrol(uint8_t *buf, int len, struct sockaddr_in *addr, int alen, struct in_addr *local); static tunnelidt new_tunnel(void); static void unhide_value(uint8_t *value, size_t len, uint16_t type, uint8_t *vector, size_t vec_len); @@ -554,6 +554,13 @@ static void inittun(void) LOG(0, 0, 0, "Error setting tun queue length: %s\n", strerror(errno)); exit(1); } + /* set MTU to modem MRU */ + ifr.ifr_mtu = MRU; + if (ioctl(ifrfd, SIOCSIFMTU, (void *) &ifr) < 0) + { + LOG(0, 0, 0, "Error setting tun MTU: %s\n", strerror(errno)); + exit(1); + } ifr.ifr_flags = IFF_UP; if (ioctl(ifrfd, SIOCSIFFLAGS, (void *) &ifr) < 0) { @@ -627,6 +634,7 @@ static void initudp(void) addr.sin_port = htons(NSCTL_PORT); controlfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); setsockopt(controlfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + setsockopt(controlfd, SOL_IP, IP_PKTINFO, &on, sizeof(on)); // recvfromto if (bind(controlfd, (void *) &addr, sizeof(addr)) < 0) { LOG(0, 0, 0, "Error in control bind: %s\n", strerror(errno)); @@ -639,6 +647,7 @@ static void initudp(void) addr.sin_port = htons(config->radius_dae_port); daefd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); setsockopt(daefd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + setsockopt(daefd, SOL_IP, IP_PKTINFO, &on, sizeof(on)); // recvfromto if (bind(daefd, (void *) &addr, sizeof(addr)) < 0) { LOG(0, 0, 0, "Error in DAE bind: %s\n", strerror(errno)); @@ -933,18 +942,14 @@ void tunnelsend(uint8_t * buf, uint16_t l, tunnelidt t) if (!t) { - static int backtrace_count = 0; LOG(0, 0, t, "tunnelsend called with 0 as tunnel id\n"); STAT(tunnel_tx_errors); - log_backtrace(backtrace_count, 5) return; } if (!tunnel[t].ip) { - static int backtrace_count = 0; LOG(1, 0, t, "Error sending data out tunnel: no remote endpoint (tunnel not set up)\n"); - log_backtrace(backtrace_count, 5) STAT(tunnel_tx_errors); return; } @@ -1043,7 +1048,7 @@ void adjust_tcp_mss(sessionidt s, tunnelidt t, uint8_t *buf, int len, uint8_t *t sum = orig + (~MSS & 0xffff); sum += ntohs(*(uint16_t *) (tcp + 16)); sum = (sum & 0xffff) + (sum >> 16); - *(uint16_t *) (tcp + 16) = htons(sum); + *(uint16_t *) (tcp + 16) = htons(sum + (sum >> 16)); } // process outgoing (to tunnel) IP @@ -1170,7 +1175,8 @@ static void processipout(uint8_t *buf, int len) master_throttle_packet(sp->tbf_out, data, size); return; } - else if (sp->walled_garden && !config->cluster_iam_master) + + if (sp->walled_garden && !config->cluster_iam_master) { // We are walled-gardening this master_garden_packet(s, data, size); @@ -1553,7 +1559,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; @@ -1581,7 +1587,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"); @@ -1621,18 +1631,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 @@ -1738,7 +1748,7 @@ void sessionkill(sessionidt s, char *reason) } 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 @@ -1803,7 +1813,7 @@ static void tunnelshutdown(tunnelidt t, char *reason, int result, int error, cha // close session for (s = 1; s <= config->cluster_highest_sessionid ; ++s) if (session[s].tunnel == t) - sessionshutdown(s, reason, 0, 0); + sessionshutdown(s, reason, CDN_NONE, TERM_ADMIN_RESET); tunnel[t].state = TUNNELDIE; tunnel[t].die = TIME + 700; // Clean up in 70 seconds @@ -2042,6 +2052,12 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) int error = 0; char *msg = 0; + // default disconnect cause/message on receipt + // of CDN (set to more specific value from + // attribute 46 if present below). + int disc_cause = TERM_NAS_REQUEST; + char const *disc_reason = "Closed (Received CDN)."; + // process AVPs while (l && !(fatal & 0x80)) // 0x80 = mandatory AVP { @@ -2049,6 +2065,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"); @@ -2185,17 +2202,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)); @@ -2350,6 +2363,84 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) memcpy(session[s].random_vector, b, n); session[s].random_vector_length = n; break; + case 46: // ppp disconnect cause + if (n >= 5) + { + uint16_t code = ntohs(*(uint16_t *) b); + uint16_t proto = ntohs(*(uint16_t *) (b + 2)); + uint8_t dir = *(b + 4); + + LOG(4, s, t, " PPP disconnect cause " + "(code=%u, proto=%04X, dir=%u, msg=\"%.*s\")\n", + code, proto, dir, n - 5, b + 5); + + switch (code) + { + case 1: // admin disconnect + disc_cause = TERM_ADMIN_RESET; + disc_reason = "Administrative disconnect"; + break; + case 3: // lcp terminate + if (dir != 2) break; // 1=peer (LNS), 2=local (LAC) + disc_cause = TERM_USER_REQUEST; + disc_reason = "Normal disconnection"; + break; + case 4: // compulsory encryption unavailable + if (dir != 1) break; // 1=refused by peer, 2=local + disc_cause = TERM_USER_ERROR; + disc_reason = "Compulsory encryption refused"; + break; + case 5: // lcp: fsm timeout + disc_cause = TERM_PORT_ERROR; + disc_reason = "LCP: FSM timeout"; + break; + case 6: // lcp: no recognisable lcp packets received + disc_cause = TERM_PORT_ERROR; + disc_reason = "LCP: no recognisable LCP packets"; + break; + case 7: // lcp: magic-no error (possibly looped back) + disc_cause = TERM_PORT_ERROR; + disc_reason = "LCP: magic-no error (possible loop)"; + break; + case 8: // lcp: echo request timeout + disc_cause = TERM_PORT_ERROR; + disc_reason = "LCP: echo request timeout"; + break; + case 13: // auth: fsm timeout + disc_cause = TERM_SERVICE_UNAVAILABLE; + disc_reason = "Authentication: FSM timeout"; + break; + case 15: // auth: unacceptable auth protocol + disc_cause = TERM_SERVICE_UNAVAILABLE; + disc_reason = "Unacceptable authentication protocol"; + break; + case 16: // auth: authentication failed + disc_cause = TERM_SERVICE_UNAVAILABLE; + disc_reason = "Authentication failed"; + break; + case 17: // ncp: fsm timeout + disc_cause = TERM_SERVICE_UNAVAILABLE; + disc_reason = "NCP: FSM timeout"; + break; + case 18: // ncp: no ncps available + disc_cause = TERM_SERVICE_UNAVAILABLE; + disc_reason = "NCP: no NCPs available"; + break; + case 19: // ncp: failure to converge on acceptable address + disc_cause = TERM_SERVICE_UNAVAILABLE; + disc_reason = (dir == 1) + ? "NCP: too many Configure-Naks received from peer" + : "NCP: too many Configure-Naks sent to peer"; + break; + case 20: // ncp: user not permitted to use any address + disc_cause = TERM_SERVICE_UNAVAILABLE; + disc_reason = (dir == 1) + ? "NCP: local link address not acceptable to peer" + : "NCP: remote link address not acceptable"; + break; + } + } + break; default: { static char e[] = "unknown AVP 0xXXXX"; @@ -2459,7 +2550,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) if (amagic == 0) amagic = time_now; session[s].magic = amagic; // set magic number session[s].flags = aflags; // set flags received - session[s].mru = PPPMTU; // default + session[s].mru = PPPoE_MRU; // default controlnull(t); // ack // start LCP @@ -2471,7 +2562,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) 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"); @@ -2607,29 +2698,9 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr) } else if (session[s].ppp.lcp == Opened) { - uint8_t buf[MAXETHER]; - uint8_t *q; - int mru = session[s].mru; - if (mru > sizeof(buf)) mru = sizeof(buf); - - l += 6; - if (l > mru) l = mru; - - q = makeppp(buf, sizeof(buf), 0, 0, s, t, PPPLCP); - if (!q) return; - - *q = ProtocolRej; - *(q + 1) = ++sess_local[s].lcp_ident; - *(uint16_t *)(q + 2) = htons(l); - *(uint16_t *)(q + 4) = htons(proto); - memcpy(q + 6, p, l - 6); - - if (proto == PPPIPV6CP) - LOG(3, s, t, "LCP: send ProtocolRej (IPV6CP: not configured)\n"); - else - LOG(2, s, t, "LCP: sent ProtocolRej (0x%04X: unsupported)\n", proto); - - tunnelsend(buf, l + (q - buf), t); + session[s].last_packet = time_now; + if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port); return; } + protoreject(s, t, p, l, proto); } else { @@ -2821,7 +2892,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); } @@ -2850,7 +2921,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); } @@ -2916,7 +2987,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; @@ -2934,7 +3005,7 @@ static void regular_cleanups(double period) *q = EchoReq; *(uint8_t *)(q + 1) = (time_now % 255); // ID *(uint16_t *)(q + 2) = htons(8); // Length - *(uint32_t *)(q + 4) = 0; // Magic Number (not supported) + *(uint32_t *)(q + 4) = session[s].ppp.lcp == Opened ? htonl(session[s].magic) : 0; // Magic Number LOG(4, s, session[s].tunnel, "No data in %d seconds, sending LCP ECHO\n", (int)(time_now - session[s].last_packet)); @@ -2952,7 +3023,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++; } @@ -3184,9 +3255,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++]; @@ -3264,6 +3338,7 @@ static void mainloop(void) if (n) { struct sockaddr_in addr; + struct in_addr local; socklen_t alen; int c, s; int udp_ready = 0; @@ -3280,6 +3355,7 @@ static void mainloop(void) for (c = n, i = 0; i < c; i++) { struct event_data *d = events[i].data.ptr; + switch (d->type) { case FD_TYPE_CLI: // CLI connections @@ -3306,20 +3382,30 @@ static void mainloop(void) case FD_TYPE_CONTROL: // nsctl commands alen = sizeof(addr); - processcontrol(buf, recvfrom(controlfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr, alen); + s = recvfromto(controlfd, buf, sizeof(buf), MSG_WAITALL, (struct sockaddr *) &addr, &alen, &local); + if (s > 0) processcontrol(buf, s, &addr, alen, &local); n--; break; case FD_TYPE_DAE: // DAE requests alen = sizeof(addr); - processdae(buf, recvfrom(daefd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr, alen); + s = recvfromto(daefd, buf, sizeof(buf), MSG_WAITALL, (struct sockaddr *) &addr, &alen, &local); + if (s > 0) processdae(buf, s, &addr, alen, &local); n--; break; case FD_TYPE_RADIUS: // RADIUS response - s = recv(radfds[d->index], buf, sizeof(buf), 0); + alen = sizeof(addr); + s = recvfrom(radfds[d->index], buf, sizeof(buf), MSG_WAITALL, (struct sockaddr *) &addr, &alen); if (s >= 0 && config->cluster_iam_master) - processrad(buf, s, d->index); + { + if (addr.sin_addr.s_addr == config->radiusserver[0] || + addr.sin_addr.s_addr == config->radiusserver[1]) + processrad(buf, s, d->index); + else + LOG(3, 0, 0, "Dropping RADIUS packet from unknown source %s\n", + fmtaddr(addr.sin_addr.s_addr, 0)); + } n--; break; @@ -3965,7 +4051,7 @@ static void initippool() else { // It's a single ip address - add_to_ip_pool(inet_addr(pool), 0); + add_to_ip_pool(ntohl(inet_addr(pool)), 0); } } fclose(f); @@ -4311,12 +4397,15 @@ static void update_config() #define L2TP_HDRS (20+8+6+4) // L2TP data encaptulation: ip + udp + l2tp (data) + ppp (inc hdlc) #define TCP_HDRS (20+20) // TCP encapsulation: ip + tcp - if (config->l2tp_mtu <= 0) config->l2tp_mtu = PPPMTU; + if (config->l2tp_mtu <= 0) config->l2tp_mtu = 1500; // ethernet default else if (config->l2tp_mtu < MINMTU) config->l2tp_mtu = MINMTU; else if (config->l2tp_mtu > MAXMTU) config->l2tp_mtu = MAXMTU; // reset MRU/MSS globals MRU = config->l2tp_mtu - L2TP_HDRS; + if (MRU > PPPoE_MRU) + MRU = PPPoE_MRU; + MSS = MRU - TCP_HDRS; // Update radius @@ -4501,7 +4590,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", @@ -4888,7 +4977,7 @@ static void plugins_done() run_plugin_done(p); } -static void processcontrol(uint8_t *buf, int len, struct sockaddr_in *addr, int alen) +static void processcontrol(uint8_t *buf, int len, struct sockaddr_in *addr, int alen, struct in_addr *local) { struct nsctl request; struct nsctl response; @@ -5046,7 +5135,7 @@ static void processcontrol(uint8_t *buf, int len, struct sockaddr_in *addr, int r = pack_control(buf, NSCTL_MAX_PKT_SZ, response.type, response.argc, response.argv); if (r > 0) { - sendto(controlfd, buf, r, 0, (const struct sockaddr *) addr, alen); + sendtofrom(controlfd, buf, r, 0, (const struct sockaddr *) addr, alen, local); if (log_stream && config->debug >= 4) { LOG(4, 0, 0, "Sent [%s] ", fmtaddr(addr->sin_addr.s_addr, 0)); @@ -5350,10 +5439,9 @@ int ip_filter(uint8_t *buf, int len, uint8_t filter) if (frag_offset) { - // non-fragmented deny rules are skipped if containing L4 matches - if (!rule->frag && - (rule->src_ports.op || rule->dst_ports.op || rule->tcp_flag_op) && - rule->action == FILTER_ACTION_DENY) + // layer 4 deny rules are skipped + if (rule->action == FILTER_ACTION_DENY && + (rule->src_ports.op || rule->dst_ports.op || rule->tcp_flag_op)) continue; } else