X-Git-Url: http://git.sameswireless.fr/l2tpns.git/blobdiff_plain/0ab682e58005f0847fb4931cced6f2612d7321e2..b5d3f182cd565ae0a4e523957664a11055cad3d4:/l2tpns.c diff --git a/l2tpns.c b/l2tpns.c index e7ac01c..778dd24 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -1,10 +1,10 @@ // L2TP Network Server // Adrian Kennard 2002 -// Copyright (c) 2003, 2004 Optus Internet Engineering +// Copyright (c) 2003, 2004, 2005 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.72 2004-12-16 23:40:31 bodea Exp $"; +char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.80 2005-01-25 04:19:05 bodea Exp $"; #include #include @@ -19,6 +19,7 @@ char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.72 2004-12-16 23:40:31 bodea Exp #include #include #include +#include #include #include #include @@ -62,8 +63,11 @@ int clifd = -1; // Socket listening for CLI connections. int snoopfd = -1; // UDP file handle for sending out intercept data int *radfds = NULL; // RADIUS requests file handles int ifrfd = -1; // File descriptor for routing, etc +int ifr6fd = -1; // File descriptor for IPv6 routing, etc +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). @@ -74,6 +78,10 @@ struct cli_session_actions *cli_session_actions = NULL; // Pending session chang 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. +struct ipv6radix { + int sess; + struct ipv6radix *branch; +} ipv6_hash[256]; // Mapping from IPv6 address to session structures. // Traffic counters. static uint32_t udp_rx = 0, udp_rx_pkt = 0, udp_tx = 0; @@ -94,18 +102,20 @@ config_descriptt config_values[] = { CONFIG("debug", debug, INT), CONFIG("log_file", log_filename, STRING), CONFIG("pid_file", pid_file, STRING), + CONFIG("random_device", random_device, STRING), CONFIG("l2tp_secret", l2tpsecret, STRING), - CONFIG("primary_dns", default_dns1, IP), - CONFIG("secondary_dns", default_dns2, IP), + CONFIG("primary_dns", default_dns1, IPv4), + CONFIG("secondary_dns", default_dns2, IPv4), CONFIG("save_state", save_state, BOOL), - CONFIG("primary_radius", radiusserver[0], IP), - CONFIG("secondary_radius", radiusserver[1], IP), + CONFIG("primary_radius", radiusserver[0], IPv4), + CONFIG("secondary_radius", radiusserver[1], IPv4), CONFIG("primary_radius_port", radiusport[0], SHORT), CONFIG("secondary_radius_port", radiusport[1], SHORT), CONFIG("radius_accounting", radius_accounting, BOOL), CONFIG("radius_secret", radiussecret, STRING), - CONFIG("bind_address", bind_address, IP), - CONFIG("peer_address", peer_address, IP), + CONFIG("radius_authtypes", radius_authtypes_s, STRING), + CONFIG("bind_address", bind_address, IPv4), + CONFIG("peer_address", peer_address, IPv4), CONFIG("send_garp", send_garp, BOOL), CONFIG("throttle_speed", rl_rate, UNSIGNED_LONG), CONFIG("throttle_buckets", num_tbfs, INT), @@ -117,10 +127,12 @@ config_descriptt config_values[] = { CONFIG("scheduler_fifo", scheduler_fifo, BOOL), CONFIG("lock_pages", lock_pages, BOOL), CONFIG("icmp_rate", icmp_rate, INT), - CONFIG("cluster_address", cluster_address, IP), + CONFIG("packet_limit", max_packets, INT), + CONFIG("cluster_address", cluster_address, IPv4), CONFIG("cluster_interface", cluster_interface, STRING), CONFIG("cluster_hb_interval", cluster_hb_interval, INT), CONFIG("cluster_hb_timeout", cluster_hb_timeout, INT), + CONFIG("ipv6_prefix", ipv6_prefix, IPv6), { NULL, 0, 0, 0 }, }; @@ -147,7 +159,7 @@ static sessionidt shut_acct_n = 0; tunnelt *tunnel = NULL; // Array of tunnel structures. sessiont *session = NULL; // Array of session structures. -sessioncountt *sess_count = NULL; // Array of partial per-session traffic counters. +sessionlocalt *sess_local = NULL; // Array of local per-session counters. radiust *radius = NULL; // Array of radius structures. ippoolt *ip_address_pool = NULL; // Array of dynamic IP addresses. ip_filtert *ip_filters = NULL; // Array of named filters. @@ -159,6 +171,7 @@ struct Tringbuffer *ringbuffer = NULL; static void cache_ipmap(in_addr_t ip, int s); static void uncache_ipmap(in_addr_t ip); +static void cache_ipv6map(struct in6_addr ip, int prefixlen, int s); static void free_ip_address(sessionidt s); static void dump_acct_info(int all); static void sighup_handler(int sig); @@ -203,7 +216,6 @@ clockt backoff(uint8_t try) void _log(int level, sessionidt s, tunnelidt t, const char *format, ...) { static char message[65536] = {0}; - static char message2[65536] = {0}; va_list ap; #ifdef RINGBUFFER @@ -227,18 +239,13 @@ void _log(int level, sessionidt s, tunnelidt t, const char *format, ...) if (config->debug < level) return; va_start(ap, format); + vsnprintf(message, sizeof(message), format, ap); + if (log_stream) - { - vsnprintf(message2, 65535, format, ap); - snprintf(message, 65535, "%s %02d/%02d %s", time_now_string, t, s, message2); - fprintf(log_stream, "%s", message); - } + fprintf(log_stream, "%s %02d/%02d %s", time_now_string, t, s, message); else if (syslog_log) - { - vsnprintf(message2, 65535, format, ap); - snprintf(message, 65535, "%02d/%02d %s", t, s, message2); - syslog(level + 2, message); // We don't need LOG_EMERG or LOG_ALERT - } + syslog(level + 2, "%02d/%02d %s", t, s, message); // We don't need LOG_EMERG or LOG_ALERT + va_end(ap); } @@ -293,6 +300,72 @@ void _log_hex(int level, const char *title, const char *data, int maxsize) } } +// initialise the random generator +static void initrandom(char *source) +{ + static char path[sizeof(config->random_device)] = "*undefined*"; + + // reinitialise only if we are forced to do so or if the config has changed + if (source && !strncmp(path, source, sizeof(path))) + return; + + // close previous source, if any + if (rand_fd >= 0) close(rand_fd); + + rand_fd = -1; + + if (source) + { + // register changes + snprintf(path, sizeof(path), "%s", source); + + if (*path == '/') + { + rand_fd = open(path, O_RDONLY|O_NONBLOCK); + if (rand_fd < 0) + LOG(0, 0, 0, "Error opening the random device %s: %s\n", + path, strerror(errno)); + } + } + + // no source: seed prng + { + unsigned seed = time_now ^ getpid(); + LOG(4, 0, 0, "Seeding the pseudo random generator: %u\n", seed); + srand(seed); + } +} + +// fill buffer with random data +void random_data(uint8_t *buf, int len) +{ + int n = 0; + + CSTAT(random_data); + if (rand_fd >= 0) + { + n = read(rand_fd, buf, len); + if (n >= len) return; + if (n < 0) + { + if (errno != EAGAIN) + { + LOG(0, 0, 0, "Error reading from random source: %s\n", + strerror(errno)); + + // fall back to rand() + initrandom(0); + } + + n = 0; + } + } + + // append missing data + while (n < len) + // not using the low order bits from the prng stream + buf[n++] = (rand() >> 4) & 0xff; +} // Add a route // @@ -357,11 +430,61 @@ static void routeset(sessionidt s, in_addr_t ip, in_addr_t mask, in_addr_t gw, i } } +void route6set(sessionidt s, struct in6_addr ip, int prefixlen, int add) +{ + struct in6_rtmsg rt; + char ipv6addr[INET6_ADDRSTRLEN]; + + if (ifr6fd < 0) + { + LOG(0, 0, 0, "Asked to set IPv6 route, but IPv6 not setup.\n"); + return; + } + + memset(&rt, 0, sizeof(rt)); + + memcpy(&rt.rtmsg_dst, &ip, sizeof(struct in6_addr)); + rt.rtmsg_dst_len = prefixlen; + rt.rtmsg_metric = 1; + rt.rtmsg_flags = RTF_UP; + rt.rtmsg_ifindex = tunidx; + + LOG(1, 0, 0, "Route %s %s/%d\n", + add ? "add" : "del", + inet_ntop(AF_INET6, &ip, ipv6addr, INET6_ADDRSTRLEN), + prefixlen); + + if (ioctl(ifr6fd, add ? SIOCADDRT : SIOCDELRT, (void *) &rt) < 0) + LOG(0, 0, 0, "route6set() error in ioctl: %s\n", + strerror(errno)); + + // FIXME: need to add BGP routing (RFC2858) + + if (s) + { + if (!add) // Are we deleting a route? + s = 0; // Caching the session as '0' is the same as uncaching. + + cache_ipv6map(ip, prefixlen, s); + } + + return; +} + +// defined in linux/ipv6.h, but tricky to include from user-space +// TODO: move routing to use netlink rather than ioctl +struct in6_ifreq { + struct in6_addr ifr6_addr; + __u32 ifr6_prefixlen; + unsigned int ifr6_ifindex; +}; + // // Set up TUN interface static void inittun(void) { struct ifreq ifr; + struct in6_ifreq ifr6; struct sockaddr_in sin = {0}; memset(&ifr, 0, sizeof(ifr)); ifr.ifr_flags = IFF_TUN; @@ -407,6 +530,42 @@ static void inittun(void) LOG(0, 0, 0, "Error setting tun flags: %s\n", strerror(errno)); exit(1); } + if (ioctl(ifrfd, SIOCGIFINDEX, (void *) &ifr) < 0) + { + LOG(0, 0, 0, "Error getting tun ifindex: %s\n", strerror(errno)); + exit(1); + } + tunidx = ifr.ifr_ifindex; + + // Only setup IPv6 on the tun device if we have a configured prefix + if (config->ipv6_prefix.s6_addr[0] > 0) { + ifr6fd = socket(PF_INET6, SOCK_DGRAM, 0); + + // Link local address is FE80::1 + memset(&ifr6.ifr6_addr, 0, sizeof(ifr6.ifr6_addr)); + ifr6.ifr6_addr.s6_addr[0] = 0xFE; + ifr6.ifr6_addr.s6_addr[1] = 0x80; + ifr6.ifr6_addr.s6_addr[15] = 1; + ifr6.ifr6_prefixlen = 64; + ifr6.ifr6_ifindex = ifr.ifr_ifindex; + if (ioctl(ifr6fd, SIOCSIFADDR, (void *) &ifr6) < 0) + { + LOG(0, 0, 0, "Error setting tun IPv6 link local address:" + " %s\n", strerror(errno)); + } + + // Global address is prefix::1 + memset(&ifr6.ifr6_addr, 0, sizeof(ifr6.ifr6_addr)); + ifr6.ifr6_addr = config->ipv6_prefix; + ifr6.ifr6_addr.s6_addr[15] = 1; + ifr6.ifr6_prefixlen = 64; + ifr6.ifr6_ifindex = ifr.ifr_ifindex; + if (ioctl(ifr6fd, SIOCSIFADDR, (void *) &ifr6) < 0) + { + LOG(0, 0, 0, "Error setting tun IPv6 global address: %s\n", + strerror(errno)); + } + } } // set up UDP port @@ -473,10 +632,36 @@ static int lookup_ipmap(in_addr_t ip) return (int) (intptr_t) d[(size_t) *a]; } +int lookup_ipv6map(struct in6_addr ip) +{ + struct ipv6radix *curnode; + int i; + int s; + char ipv6addr[INET6_ADDRSTRLEN]; + + curnode = &ipv6_hash[ip.s6_addr[0]]; + i = 1; + s = curnode->sess; + + while (s == 0 && i < 15 && curnode->branch != NULL) + { + curnode = &curnode->branch[ip.s6_addr[i]]; + s = curnode->sess; + i++; + } + + LOG(4, s, session[s].tunnel, "Looking up address %s and got %d\n", + inet_ntop(AF_INET6, &ip, ipv6addr, + INET6_ADDRSTRLEN), + s); + + return s; +} + sessionidt sessionbyip(in_addr_t ip) { int s = lookup_ipmap(ip); - CSTAT(call_sessionbyip); + CSTAT(sessionbyip); if (s > 0 && s < MAXSESSION && session[s].tunnel) return (sessionidt) s; @@ -484,6 +669,25 @@ sessionidt sessionbyip(in_addr_t ip) return 0; } +sessionidt sessionbyipv6(struct in6_addr ip) +{ + int s; + CSTAT(sessionbyipv6); + + if (!memcmp(&config->ipv6_prefix, &ip, 8) || + (ip.s6_addr[0] == 0xFE && ip.s6_addr[1] == 0x80 && + (ip.s6_addr16[1] == ip.s6_addr16[2] == ip.s6_addr16[3] == 0))) { + s = lookup_ipmap(*(in_addr_t *) &ip.s6_addr[8]); + } else { + s = lookup_ipv6map(ip); + } + + if (s > 0 && s < MAXSESSION && session[s].tunnel) + return s; + + return 0; +} + // // Take an IP address in HOST byte order and // add it to the sessionid by IP cache. @@ -523,6 +727,42 @@ 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) +{ + int i; + int bytes; + struct ipv6radix *curnode; + char ipv6addr[INET6_ADDRSTRLEN]; + + curnode = &ipv6_hash[ip.s6_addr[0]]; + + bytes = prefixlen >> 3; + i = 1; + while (i < bytes) { + if (curnode->branch == NULL) + { + if (!(curnode->branch = calloc(256, + sizeof (struct ipv6radix)))) + return; + } + curnode = &curnode->branch[ip.s6_addr[i]]; + i++; + } + + curnode->sess = s; + + if (s > 0) + LOG(4, s, session[s].tunnel, "Caching ip address %s/%d\n", + inet_ntop(AF_INET6, &ip, ipv6addr, + INET6_ADDRSTRLEN), + prefixlen); + else if (s == 0) + LOG(4, 0, 0, "Un-caching ip address %s/%d\n", + inet_ntop(AF_INET6, &ip, ipv6addr, + INET6_ADDRSTRLEN), + prefixlen); +} + // // CLI list to dump current ipcache. // @@ -576,7 +816,7 @@ int cmd_show_ipcache(struct cli_def *cli, char *command, char **argv, int argc) sessionidt sessionbyuser(char *username) { int s; - CSTAT(call_sessionbyuser); + CSTAT(sessionbyuser); for (s = 1; s < MAXSESSION ; ++s) { @@ -640,7 +880,7 @@ void tunnelsend(uint8_t * buf, uint16_t l, tunnelidt t) { struct sockaddr_in addr; - CSTAT(call_tunnelsend); + CSTAT(tunnelsend); if (!t) { @@ -711,23 +951,23 @@ static void processipout(uint8_t * buf, int len) tunnelidt t; in_addr_t ip; - char * data = buf; // Keep a copy of the originals. + char *data = buf; // Keep a copy of the originals. int size = len; uint8_t b[MAXETHER + 20]; - CSTAT(call_processipout); + CSTAT(processipout); if (len < MIN_IP_SIZE) { LOG(1, 0, 0, "Short IP, %d bytes\n", len); - STAT(tunnel_tx_errors); + STAT(tun_rx_errors); return; } if (len >= MAXETHER) { LOG(1, 0, 0, "Oversize IP packet %d bytes\n", len); - STAT(tunnel_tx_errors); + STAT(tun_rx_errors); return; } @@ -765,6 +1005,45 @@ static void processipout(uint8_t * buf, int len) t = session[s].tunnel; sp = &session[s]; + // DoS prevention: enforce a maximum number of packets per 0.1s for a session + if (config->max_packets > 0) + { + if (sess_local[s].last_packet_out == TIME) + { + int max = config->max_packets; + + // All packets for throttled sessions are handled by the + // master, so further limit by using the throttle rate. + // A bit of a kludge, since throttle rate is in kbps, + // but should still be generous given our average DSL + // packet size is 200 bytes: a limit of 28kbps equates + // to around 180 packets per second. + if (!config->cluster_iam_master && sp->throttle_out && sp->throttle_out < max) + max = sp->throttle_out; + + if (++sess_local[s].packets_out > max) + { + sess_local[s].packets_dropped++; + return; + } + } + else + { + if (sess_local[s].packets_dropped) + { + INC_STAT(tun_rx_dropped, sess_local[s].packets_dropped); + LOG(3, s, t, "Dropped %u/%u packets to %s for %suser %s\n", + sess_local[s].packets_dropped, sess_local[s].packets_out, + fmtaddr(ip, 0), sp->throttle_out ? "throttled " : "", + sp->user); + } + + sess_local[s].last_packet_out = TIME; + sess_local[s].packets_out = 1; + sess_local[s].packets_dropped = 0; + } + } + // run access-list if any if (session[s].filter_out && !ip_filter(buf, len, session[s].filter_out - 1)) return; @@ -802,7 +1081,116 @@ static void processipout(uint8_t * buf, int len) sp->total_cout += len; // byte count sp->pout++; udp_tx += len; - sess_count[s].cout += len; // To send to master.. + sess_local[s].cout += len; // To send to master.. +} + +// process outgoing (to tunnel) IPv6 +// +void processipv6out(uint8_t * buf, int len) +{ + sessionidt s; + sessiont *sp; + tunnelidt t; + in_addr_t ip; + struct in6_addr ip6; + + char *data = buf; // Keep a copy of the originals. + int size = len; + + uint8_t b[MAXETHER + 20]; + + CSTAT(processipv6out); + + if (len < MIN_IP_SIZE) + { + LOG(1, 0, 0, "Short IPv6, %d bytes\n", len); + STAT(tunnel_tx_errors); + return; + } + if (len >= MAXETHER) + { + LOG(1, 0, 0, "Oversize IPv6 packet %d bytes\n", len); + STAT(tunnel_tx_errors); + return; + } + + // Skip the tun header + buf += 4; + len -= 4; + + // Got an IP header now + if (*(uint8_t *)(buf) >> 4 != 6) + { + LOG(1, 0, 0, "IP: Don't understand anything except IPv6\n"); + return; + } + + ip6 = *(struct in6_addr *)(buf+24); + s = sessionbyipv6(ip6); + + if (s == 0) + { + ip = *(uint32_t *)(buf + 32); + s = sessionbyip(ip); + } + + if (s == 0) + { + // Is this a packet for a session that doesn't exist? + static int rate = 0; // Number of ICMP packets we've sent this second. + static int last = 0; // Last time we reset the ICMP packet counter 'rate'. + + if (last != time_now) + { + last = time_now; + rate = 0; + } + + if (rate++ < config->icmp_rate) // Only send a max of icmp_rate per second. + { + // FIXME: Should send icmp6 host unreachable + } + return; + } + t = session[s].tunnel; + sp = &session[s]; + + // FIXME: add DoS prevention/filters? + + if (sp->tbf_out) + { + // Are we throttling this session? + if (config->cluster_iam_master) + tbf_queue_packet(sp->tbf_out, data, size); + else + master_throttle_packet(sp->tbf_out, data, size); + return; + } + else if (sp->walled_garden && !config->cluster_iam_master) + { + // We are walled-gardening this + master_garden_packet(s, data, size); + return; + } + + LOG(5, s, t, "Ethernet -> Tunnel (%d bytes)\n", len); + + // Add on L2TP header + { + uint8_t *p = makeppp(b, sizeof(b), buf, len, t, s, PPPIPV6); + if (!p) return; + tunnelsend(b, len + (p-b), t); // send it... + } + + // 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); + + sp->cout += len; // byte count + sp->total_cout += len; // byte count + sp->pout++; + udp_tx += len; + sess_local[s].cout += len; // To send to master.. } // @@ -852,7 +1240,7 @@ static void send_ipout(sessionidt s, uint8_t *buf, int len) sp->total_cout += len; // byte count sp->pout++; udp_tx += len; - sess_count[s].cout += len; // To send to master.. + sess_local[s].cout += len; // To send to master.. } // add an AVP (16 bit) @@ -1051,7 +1439,7 @@ void sessionshutdown(sessionidt s, char *reason) int walled_garden = session[s].walled_garden; - CSTAT(call_sessionshutdown); + CSTAT(sessionshutdown); if (!session[s].tunnel) { @@ -1079,9 +1467,7 @@ void sessionshutdown(sessionidt s, char *reason) } else { - int n; - for (n = 0; n < 15; n++) - radius[r].auth[n] = rand(); + random_data(radius[r].auth, sizeof(radius[r].auth)); } } @@ -1114,6 +1500,10 @@ void sessionshutdown(sessionidt s, char *reason) } else free_ip_address(s); + + // unroute IPv6, if setup + if (session[s].flags & SF_IPV6_ROUTED) + route6set(s, session[s].ipv6route, session[s].ipv6prefixlen, 0); } if (session[s].throttle_in || session[s].throttle_out) // Unthrottle if throttled. @@ -1142,7 +1532,7 @@ void sendipcp(tunnelidt t, sessionidt s) uint16_t r = session[s].radius; uint8_t *q; - CSTAT(call_sendipcp); + CSTAT(sendipcp); if (!r) r = radiusnew(s); @@ -1175,13 +1565,37 @@ void sendipcp(tunnelidt t, sessionidt s) tunnelsend(buf, 10 + (q - buf), t); // send it session[s].flags &= ~SF_IPCP_ACKED; // Clear flag. + + // If we have an IPv6 prefix length configured, assume we should + // try to negotiate an IPv6 session as well. Unless we've had a + // (N)ACK for IPV6CP. + if (config->ipv6_prefix.s6_addr[0] > 0 && + !(session[s].flags & SF_IPV6CP_ACKED) && + !(session[s].flags & SF_IPV6_NACKED)) + { + q = makeppp(buf,sizeof(buf), 0, 0, t, s, PPPIPV6CP); + if (!q) return; + + *q = ConfigReq; + q[1] = r << RADIUS_SHIFT; // ID, don't care, we + // only send one type + // of request + *(uint16_t *) (q + 2) = htons(14); + q[4] = 1; + q[5] = 10; + *(uint32_t *) (q + 6) = 0; // We'll be prefix::1 + *(uint32_t *) (q + 10) = 0; + q[13] = 1; + + tunnelsend(buf, 14 + (q - buf), t); // send it + } } // kill a session now static void sessionkill(sessionidt s, char *reason) { - CSTAT(call_sessionkill); + CSTAT(sessionkill); session[s].die = now(); sessionshutdown(s, reason); // close radius/routes, etc. @@ -1211,7 +1625,7 @@ static void tunnelkill(tunnelidt t, char *reason) sessionidt s; controlt *c; - CSTAT(call_tunnelkill); + CSTAT(tunnelkill); tunnel[t].state = TUNNELDIE; @@ -1241,7 +1655,7 @@ static void tunnelshutdown(tunnelidt t, char *reason) { sessionidt s; - CSTAT(call_tunnelshutdown); + CSTAT(tunnelshutdown); if (!tunnel[t].last || !tunnel[t].far || tunnel[t].state == TUNNELFREE) { @@ -1276,7 +1690,7 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) uint8_t *p = buf + 2; - CSTAT(call_processudp); + CSTAT(processudp); udp_rx += len; udp_rx_pkt++; @@ -1523,12 +1937,12 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) b += 2; n -= 6; - LOG(4, s, t, " AVP %d (%s) len %d\n", mtype, avpnames[mtype], n); + 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); - LOG(4, s, t, " Message type = %d (%s)\n", *b, l2tp_message_types[message]); + LOG(4, s, t, " Message type = %d (%s)\n", *b, l2tp_message_type(message)); mandatorymessage = flags; break; case 1: // result code @@ -1537,23 +1951,18 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) const char* resdesc = "(unknown)"; if (message == 4) { /* StopCCN */ - if (rescode <= MAX_STOPCCN_RESULT_CODE) - resdesc = stopccn_result_codes[rescode]; + resdesc = stopccn_result_code(rescode); } else if (message == 14) { /* CDN */ - if (rescode <= MAX_CDN_RESULT_CODE) - resdesc = cdn_result_codes[rescode]; + resdesc = cdn_result_code(rescode); } LOG(4, s, t, " Result Code %d: %s\n", rescode, resdesc); if (n >= 4) { uint16_t errcode = ntohs(*(uint16_t *)(b + 2)); - const char* errdesc = "(unknown)"; - if (errcode <= MAX_ERROR_CODE) - errdesc = error_codes[errcode]; - LOG(4, s, t, " Error Code %d: %s\n", errcode, errdesc); + LOG(4, s, t, " Error Code %d: %s\n", errcode, error_code(errcode)); } if (n > 4) LOG(4, s, t, " Error String: %.*s\n", n-4, b+4); @@ -1681,9 +2090,9 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) } case 29: // Proxy Authentication Type { - uint16_t authtype = ntohs(*(uint16_t *)b); - LOG(4, s, t, " Proxy Auth Type %d (%s)\n", authtype, authtypes[authtype]); - requestchap = (authtype == 2); + uint16_t atype = ntohs(*(uint16_t *)b); + LOG(4, s, t, " Proxy Auth Type %d (%s)\n", atype, auth_type(atype)); + requestchap = (atype == 2); break; } case 30: // Proxy Authentication Name @@ -1827,12 +2236,9 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) 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 - int n; - for (n = 0; n < 15; n++) - radius[r].auth[n] = rand(); - } + + // 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); @@ -1944,6 +2350,19 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port); return; } processipcp(t, s, p, l); } + else if (prot == PPPIPV6CP) + { + if (config->ipv6_prefix.s6_addr[0] > 0) + { + session[s].last_packet = time_now; + if (!config->cluster_iam_master) { master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port); return; } + processipv6cp(t, s, p, l); + } + else + { + LOG(1, s, t, "IPv6 not configured; ignoring IPv6CP\n"); + } + } else if (prot == PPPCCP) { session[s].last_packet = time_now; @@ -1967,6 +2386,28 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) processipin(t, s, p, l); } + else if (prot == PPPIPV6) + { + if (!config->ipv6_prefix.s6_addr[0] > 0) + { + LOG(1, s, t, "IPv6 not configured; yet received IPv6 packet. Ignoring.\n"); + return; + } + if (session[s].die) + { + LOG(4, s, t, "Session %d is closing. Don't process PPP packets\n", s); + return; // closing session, PPP not processed + } + + session[s].last_packet = time_now; + if (session[s].walled_garden && !config->cluster_iam_master) + { + master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port); + return; + } + + processipv6in(t, s, p, l); + } else { STAT(tunnel_rx_errors); @@ -1982,7 +2423,7 @@ static void processtun(uint8_t * buf, int len) STAT(tun_rx_packets); INC_STAT(tun_rx_bytes, len); - CSTAT(call_processtun); + CSTAT(processtun); eth_rx_pkt++; eth_rx += len; @@ -1993,8 +2434,12 @@ static void processtun(uint8_t * buf, int len) return; } - if (*(uint16_t *) (buf + 2) == htons(PKTIP)) // IP + if (*(uint16_t *) (buf + 2) == htons(PKTIP)) // IPv4 processipout(buf, len); + else if (*(uint16_t *) (buf + 2) == htons(PKTIPV6) // IPV6 + && config->ipv6_prefix.s6_addr[0] > 0) + processipv6out(buf, len); + // Else discard. } @@ -2367,14 +2812,11 @@ static void mainloop(void) int tun_pkts = 0; int cluster_pkts = 0; - INC_STAT(select_ready, n); - // nsctl commands if (FD_ISSET(controlfd, &r)) { alen = sizeof(addr); processcontrol(buf, recvfrom(controlfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr, alen); - STAT(select_processed); n--; } @@ -2386,7 +2828,6 @@ static void mainloop(void) if (FD_ISSET(radfds[i], &r)) { processrad(buf, recv(radfds[i], buf, sizeof(buf), 0), i); - STAT(select_processed); n--; } } @@ -2406,21 +2847,17 @@ static void mainloop(void) else LOG(0, 0, 0, "accept error: %s\n", strerror(errno)); - STAT(select_processed); n--; } #ifdef BGP for (i = 0; i < BGP_NUM_PEERS; i++) { - int isr = bgp_set[i] ? !!FD_ISSET(bgp_peers[i].sock, &r) : 0; - int isw = bgp_set[i] ? !!FD_ISSET(bgp_peers[i].sock, &w) : 0; + int isr = bgp_set[i] ? FD_ISSET(bgp_peers[i].sock, &r) : 0; + int isw = bgp_set[i] ? FD_ISSET(bgp_peers[i].sock, &w) : 0; bgp_process(&bgp_peers[i], isr, isw); - if (isr || isw) - { - INC_STAT(select_processed, isr + isw); - n -= (isr + isw); - } + if (isr) n--; + if (isw) n--; } #endif /* BGP */ @@ -2433,7 +2870,6 @@ static void mainloop(void) if ((s = recvfrom(udpfd, buf, sizeof(buf), 0, (void *) &addr, &alen)) > 0) { processudp(buf, s, &addr); - STAT(select_processed); udp_pkts++; } else @@ -2449,7 +2885,6 @@ static void mainloop(void) if ((s = read(tunfd, buf, sizeof(buf))) > 0) { processtun(buf, s); - STAT(select_processed); tun_pkts++; } else @@ -2466,7 +2901,6 @@ static void mainloop(void) if ((s = recvfrom(cluster_sockfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen)) > 0) { processcluster(buf, s, addr.sin_addr.s_addr); - STAT(select_processed); cluster_pkts++; } else @@ -2477,9 +2911,16 @@ static void mainloop(void) } } + if (udp_pkts > 1 || tun_pkts > 1 || cluster_pkts > 1) + STAT(multi_read_used); + if (c >= config->multi_read_count) + { LOG(3, 0, 0, "Reached multi_read_count (%d); processed %d udp, %d tun and %d cluster packets\n", config->multi_read_count, udp_pkts, tun_pkts, cluster_pkts); + + STAT(multi_read_exceeded); + } } // Runs on every machine (master and slaves). @@ -2629,6 +3070,7 @@ static void initdata(int optdebug, char *optconfig) config->debug = optdebug; config->num_tbfs = MAXTBFS; config->rl_rate = 28; // 28kbps + strcpy(config->random_device, RANDOMDEVICE); if (!(tunnel = shared_malloc(sizeof(tunnelt) * MAXTUNNEL))) { @@ -2641,9 +3083,9 @@ static void initdata(int optdebug, char *optconfig) exit(1); } - if (!(sess_count = shared_malloc(sizeof(sessioncountt) * MAXSESSION))) + if (!(sess_local = shared_malloc(sizeof(sessionlocalt) * MAXSESSION))) { - LOG(0, 0, 0, "Error doing malloc for sessions_count: %s\n", strerror(errno)); + LOG(0, 0, 0, "Error doing malloc for sess_local: %s\n", strerror(errno)); exit(1); } @@ -2659,12 +3101,12 @@ static void initdata(int optdebug, char *optconfig) exit(1); } -if (!(ip_filters = shared_malloc(sizeof(ip_filtert) * MAXFILTER))) -{ - LOG(0, 0, 0, "Error doing malloc for ip_filters: %s\n", strerror(errno)); - exit(1); -} -memset(ip_filters, 0, sizeof(ip_filtert) * MAXFILTER); + if (!(ip_filters = shared_malloc(sizeof(ip_filtert) * MAXFILTER))) + { + LOG(0, 0, 0, "Error doing malloc for ip_filters: %s\n", strerror(errno)); + exit(1); + } + memset(ip_filters, 0, sizeof(ip_filtert) * MAXFILTER); #ifdef RINGBUFFER if (!(ringbuffer = shared_malloc(sizeof(struct Tringbuffer)))) @@ -2734,7 +3176,7 @@ static int assign_ip_address(sessionidt s) char reuse = 0; - CSTAT(call_assign_ip_address); + CSTAT(assign_ip_address); for (i = 1; i < ip_pool_size; i++) { @@ -2787,7 +3229,7 @@ static void free_ip_address(sessionidt s) int i = session[s].ip_pool_index; - CSTAT(call_free_ip_address); + CSTAT(free_ip_address); if (!session[s].ip) return; // what the? @@ -3058,7 +3500,7 @@ static void dump_acct_info(int all) FILE *f = NULL; - CSTAT(call_dump_acct_info); + CSTAT(dump_acct_info); if (shut_acct_n) { @@ -3132,7 +3574,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 Optus Internet Engineering\n"); + LOG(0, 0, 0, "Copyright (c) 2003, 2004, 2005 Optus Internet Engineering\n"); LOG(0, 0, 0, "Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced\n"); { struct rlimit rlim; @@ -3537,6 +3979,7 @@ static int facility_value(char *name) static void update_config() { int i; + char *p; static int timeout = 0; static int interval = 0; @@ -3548,6 +3991,7 @@ static void update_config() fclose(log_stream); log_stream = NULL; } + if (*config->log_filename) { if (strstr(config->log_filename, "syslog:") == config->log_filename) @@ -3579,7 +4023,6 @@ static void update_config() setbuf(log_stream, NULL); } - // Update radius config->numradiusservers = 0; for (i = 0; i < MAXRADSERVER; i++) @@ -3604,6 +4047,59 @@ static void update_config() config->num_radfds = 2 << RADIUS_SHIFT; + // parse radius_authtypes_s + config->radius_authtypes = config->radius_authprefer = 0; + p = config->radius_authtypes_s; + while (*p) + { + char *s = strpbrk(p, " \t,"); + int type = 0; + + if (s) + { + *s++ = 0; + while (*s == ' ' || *s == '\t') + s++; + + if (!*s) + s = 0; + } + + if (!strncasecmp("chap", p, strlen(p))) + type = AUTHCHAP; + else if (!strncasecmp("pap", p, strlen(p))) + type = AUTHPAP; + else + LOG(0, 0, 0, "Invalid RADIUS authentication type \"%s\"", p); + + config->radius_authtypes |= type; + if (!config->radius_authprefer) + config->radius_authprefer = type; + } + + if (!config->radius_authtypes) + { + LOG(0, 0, 0, "Defaulting to PAP authentication\n"); + config->radius_authtypes = config->radius_authprefer = AUTHPAP; + } + + // normalise radius_authtypes_s + if (config->radius_authprefer == AUTHPAP) + { + strcpy(config->radius_authtypes_s, "pap"); + if (config->radius_authtypes & AUTHCHAP) + strcat(config->radius_authtypes_s, ", chap"); + } + else + { + strcpy(config->radius_authtypes_s, "chap"); + if (config->radius_authtypes & AUTHPAP) + strcat(config->radius_authtypes_s, ", pap"); + } + + // re-initialise the random number source + initrandom(config->random_device); + // Update plugins for (i = 0; i < MAXPLUGINS; i++) { @@ -3621,6 +4117,7 @@ static void update_config() remove_plugin(config->old_plugins[i]); } } + memcpy(config->old_plugins, config->plugins, sizeof(config->plugins)); if (!config->cleanup_interval) config->cleanup_interval = 10; if (!config->multi_read_count) config->multi_read_count = 10; @@ -3699,7 +4196,7 @@ int sessionsetup(tunnelidt t, sessionidt s) sessionidt i; int r; - CSTAT(call_sessionsetup); + CSTAT(sessionsetup); LOG(3, s, t, "Doing session setup for session\n"); @@ -3752,6 +4249,7 @@ int sessionsetup(tunnelidt t, sessionidt s) // convered by a Framed-Route. Anything else is part // of the IP address pool and is already routed, it // just needs to be added to the IP cache. + // IPv6 route setup is done in ppp.c, when IPV6CP is acked. if (session[s].ip_pool_index == -1) // static ip { if (!routed) routeset(s, session[s].ip, 0, 0, 1); @@ -3874,6 +4372,10 @@ int load_session(sessionidt s, sessiont *new) } } + // check v6 routing + if (new->flags & SF_IPV6_ROUTED && !(session[s].flags & SF_IPV6_ROUTED)) + route6set(s, new->ipv6route, new->ipv6prefixlen, 1); + // check filters if (new->filter_in && (new->filter_in > MAXFILTER || !ip_filters[new->filter_in - 1].name[0])) {