finish incorporating ipv6 patches
authorBrendan O'Dea <bod@optus.net>
Tue, 25 Jan 2005 04:19:04 +0000 (04:19 +0000)
committerBrendan O'Dea <bod@optus.net>
Tue, 25 Jan 2005 04:19:04 +0000 (04:19 +0000)
Changes
cli.c
icmp.c
l2tpns.c
l2tpns.h
l2tpns.spec
ppp.c
radius.c
util.c

diff --git a/Changes b/Changes
index 1432656..56247c0 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,5 +1,5 @@
-* Thu Jan 13 2005 Brendan O'Dea <bod@optusnet.com.au> 2.1.0
-- Add IPv6 support from Jonathan McDowell (work in progress).
+* Tue Jan 25 2005 Brendan O'Dea <bod@optusnet.com.au> 2.1.0
+- Add IPv6 support from Jonathan McDowell.
 - Add CHAP support from Jordan Hrycaj (work in progress).
 - Sanity check that cluster_send_session is not called from a child
   process.
diff --git a/cli.c b/cli.c
index c2e54be..dace437 100644 (file)
--- a/cli.c
+++ b/cli.c
@@ -2,7 +2,7 @@
 // vim: sw=8 ts=8
 
 char const *cvs_name = "$Name:  $";
-char const *cvs_id_cli = "$Id: cli.c,v 1.51 2005-01-13 08:26:25 bodea Exp $";
+char const *cvs_id_cli = "$Id: cli.c,v 1.52 2005-01-25 04:19:05 bodea Exp $";
 
 #include <stdio.h>
 #include <stdarg.h>
@@ -478,7 +478,7 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int
        }
 
        // Show Summary
-       cli_print(cli, "%5s %4s %-32s %-15s %s %s %s %10s %10s %10s %4s %-15s %s",
+       cli_print(cli, "%5s %4s %-32s %-15s %s %s %s %s %10s %10s %10s %4s %-15s %s",
                        "SID",
                        "TID",
                        "Username",
@@ -486,6 +486,7 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int
                        "I",
                        "T",
                        "G",
+                       "6",
                        "opened",
                        "downloaded",
                        "uploaded",
@@ -496,7 +497,7 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int
        for (i = 1; i < MAXSESSION; i++)
        {
                if (!session[i].opened) continue;
-               cli_print(cli, "%5d %4d %-32s %-15s %s %s %s %10u %10lu %10lu %4u %-15s %s",
+               cli_print(cli, "%5d %4d %-32s %-15s %s %s %s %s %10u %10lu %10lu %4u %-15s %s",
                                i,
                                session[i].tunnel,
                                session[i].user[0] ? session[i].user : "*",
@@ -504,6 +505,7 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int
                                (session[i].snoop_ip && session[i].snoop_port) ? "Y" : "N",
                                (session[i].throttle_in || session[i].throttle_out) ? "Y" : "N",
                                (session[i].walled_garden) ? "Y" : "N",
+                               (session[i].flags & SF_IPV6CP_ACKED) ? "Y" : "N",
                                abs(time_now - (unsigned long)session[i].opened),
                                (unsigned long)session[i].total_cout,
                                (unsigned long)session[i].total_cin,
@@ -700,18 +702,22 @@ static int cmd_show_counters(struct cli_def *cli, char *command, char **argv, in
        cli_print(cli, "-----------------------------------------");
        cli_print(cli, "%-30s%u", "call_processtun",            GET_STAT(call_processtun));
        cli_print(cli, "%-30s%u", "call_processipout",          GET_STAT(call_processipout));
+       cli_print(cli, "%-30s%u", "call_processipv6out",        GET_STAT(call_processipv6out));
        cli_print(cli, "%-30s%u", "call_processudp",            GET_STAT(call_processudp));
        cli_print(cli, "%-30s%u", "call_processpap",            GET_STAT(call_processpap));
        cli_print(cli, "%-30s%u", "call_processchap",           GET_STAT(call_processchap));
        cli_print(cli, "%-30s%u", "call_processlcp",            GET_STAT(call_processlcp));
        cli_print(cli, "%-30s%u", "call_processipcp",           GET_STAT(call_processipcp));
+       cli_print(cli, "%-30s%u", "call_processipv6cp",         GET_STAT(call_processipv6cp));
        cli_print(cli, "%-30s%u", "call_processipin",           GET_STAT(call_processipin));
+       cli_print(cli, "%-30s%u", "call_processipv6in",         GET_STAT(call_processipv6in));
        cli_print(cli, "%-30s%u", "call_processccp",            GET_STAT(call_processccp));
        cli_print(cli, "%-30s%u", "call_processrad",            GET_STAT(call_processrad));
        cli_print(cli, "%-30s%u", "call_sendarp",               GET_STAT(call_sendarp));
        cli_print(cli, "%-30s%u", "call_sendipcp",              GET_STAT(call_sendipcp));
        cli_print(cli, "%-30s%u", "call_sendchap",              GET_STAT(call_sendchap));
        cli_print(cli, "%-30s%u", "call_sessionbyip",           GET_STAT(call_sessionbyip));
+       cli_print(cli, "%-30s%u", "call_sessionbyipv6",         GET_STAT(call_sessionbyipv6));
        cli_print(cli, "%-30s%u", "call_sessionbyuser",         GET_STAT(call_sessionbyuser));
        cli_print(cli, "%-30s%u", "call_tunnelsend",            GET_STAT(call_tunnelsend));
        cli_print(cli, "%-30s%u", "call_tunnelkill",            GET_STAT(call_tunnelkill));
diff --git a/icmp.c b/icmp.c
index a920553..f6c3e0d 100644 (file)
--- a/icmp.c
+++ b/icmp.c
@@ -1,6 +1,6 @@
 // L2TPNS: icmp
 
-char const *cvs_id_icmp = "$Id: icmp.c,v 1.6 2004-12-16 08:49:53 bodea Exp $";
+char const *cvs_id_icmp = "$Id: icmp.c,v 1.7 2005-01-25 04:19:05 bodea Exp $";
 
 #include <arpa/inet.h>
 #include <netdb.h>
@@ -8,6 +8,7 @@ char const *cvs_id_icmp = "$Id: icmp.c,v 1.6 2004-12-16 08:49:53 bodea Exp $";
 #include <asm/types.h>
 #include <linux/ip.h>
 #include <linux/icmp.h>
+#include <netinet/icmp6.h>
 #include <stdio.h>
 #include <sys/socket.h>
 #include <unistd.h>
@@ -19,6 +20,14 @@ char const *cvs_id_icmp = "$Id: icmp.c,v 1.6 2004-12-16 08:49:53 bodea Exp $";
 
 static uint16_t _checksum(unsigned char *addr, int count);
 
+struct ipv6_pseudo_hdr {
+       struct in6_addr src;
+       struct in6_addr dest;
+       uint32_t ulp_length;
+       uint32_t zero    : 24;
+       uint32_t nexthdr :  8;
+};
+
 void host_unreachable(in_addr_t destination, uint16_t id, in_addr_t source, char *packet, int packet_len)
 {
        char buf[128] = {0};
@@ -89,3 +98,77 @@ static uint16_t _checksum(unsigned char *addr, int count)
 
        return htons((uint16_t) sum);
 }
+
+void send_ipv6_ra(tunnelidt t, sessionidt s, struct in6_addr *ip)
+{
+       struct nd_opt_prefix_info *pinfo;
+       struct ipv6_pseudo_hdr *phdr;
+       uint8_t b[MAXETHER + 20];
+       uint8_t c[MAXETHER + 20];
+       int l;
+       uint8_t *o;
+
+       LOG(3, s, t, "Sending IPv6 RA\n");
+               
+       memset(b, 0, sizeof(b));
+       o = makeppp(b, sizeof(b), 0, 0, t, s, PPPIPV6);
+
+       if (!o)
+       {
+               LOG(3, s, t, "failed to send IPv6 RA\n");
+               return;
+       }
+
+       *o = 0x60;                      // IPv6
+       *(o+1) = 0;
+       *(o+5) = 48;                    // Length of payload (not header)
+       *(o+6) = 58;                    // icmp6 is next
+       *(o+7) = 255;                   // Hop limit
+       memset(o+8, 0, 16);             // source = FE80::1
+       *(o+8) = 0xFE;
+       *(o+9) = 0x80;
+       *(o+23) = 1;
+       if (ip != NULL)
+               memcpy(o+24, ip, 16);   // dest = ip
+       else
+       {
+               // FF02::1 - all hosts
+               *(o+24) = 0xFF;
+               *(o+25) = 2;
+               *(o+39) = 1;
+       }
+       *(o+40) = 134;                  // RA message
+       *(o+41) = 0;                    // Code
+       *(o+42) = *(o+43) = 0;          // Checksum
+       *(o+44) = 64;                   // Hop count
+       *(o+45) = 0;                    // Flags
+       *(o+46) = *(o+47) = 255;        // Lifetime
+       *(uint32_t *)(o+48) = 0;        // Reachable time
+       *(uint32_t *)(o+52) = 0;        // Retrans timer
+       pinfo = (struct nd_opt_prefix_info *)(o+56);
+       pinfo->nd_opt_pi_type           = ND_OPT_PREFIX_INFORMATION;
+       pinfo->nd_opt_pi_len            = 4;
+       pinfo->nd_opt_pi_prefix_len     = 64; // prefix length
+       pinfo->nd_opt_pi_flags_reserved = ND_OPT_PI_FLAG_ONLINK;
+       pinfo->nd_opt_pi_flags_reserved |= ND_OPT_PI_FLAG_AUTO;
+       pinfo->nd_opt_pi_valid_time     = htonl(2592000);
+       pinfo->nd_opt_pi_preferred_time = htonl(604800);
+       pinfo->nd_opt_pi_reserved2      = 0;
+       pinfo->nd_opt_pi_prefix         = config->ipv6_prefix;
+       l = sizeof(*pinfo) + 56;
+
+       memset(c, 0, sizeof(c));
+       phdr = (struct ipv6_pseudo_hdr *) c;
+       memcpy(&phdr->src, o+8, 16);
+       memcpy(&phdr->dest, o+24, 16);
+       phdr->ulp_length = htonl(l - 40);
+       phdr->nexthdr = IPPROTO_ICMPV6;
+
+       memcpy(c + sizeof(*phdr), o + 40, l - 40);
+
+       // Checksum is over the icmp6 payload plus the pseudo header
+       *(uint16_t *)(o+42) = _checksum(c, l - 40 + sizeof(*phdr));
+
+       tunnelsend(b, l + (o-b), t); // send it...
+       return;
+}
index 2073160..778dd24 100644 (file)
--- 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.79 2005-01-13 08:42:52 bodea Exp $";
+char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.80 2005-01-25 04:19:05 bodea Exp $";
 
 #include <arpa/inet.h>
 #include <assert.h>
@@ -19,6 +19,7 @@ char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.79 2005-01-13 08:42:52 bodea Exp
 #include <sys/mman.h>
 #include <netdb.h>
 #include <netinet/in.h>
+#include <netinet/ip6.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -62,9 +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).
@@ -75,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;
@@ -125,6 +132,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("ipv6_prefix", ipv6_prefix, IPv6),
        { NULL, 0, 0, 0 },
 };
 
@@ -163,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);
@@ -421,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;
@@ -471,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
@@ -537,6 +632,32 @@ 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);
@@ -548,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.
@@ -587,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.
 //
@@ -908,6 +1084,115 @@ static void processipout(uint8_t * buf, int len)
        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..
+}
+
 //
 // Helper routine for the TBF filters.
 // Used to send queued data in to the user!
@@ -1215,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.
@@ -1276,6 +1565,30 @@ 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
@@ -2037,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;
@@ -2060,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);
@@ -2088,6 +2436,10 @@ static void processtun(uint8_t * buf, int len)
 
        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.
 }
 
@@ -3897,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);
@@ -4019,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]))
        {
index e0be6b4..b1b6d86 100644 (file)
--- a/l2tpns.h
+++ b/l2tpns.h
@@ -1,5 +1,5 @@
 // L2TPNS Global Stuff
-// $Id: l2tpns.h,v 1.53 2005-01-13 07:57:39 bodea Exp $
+// $Id: l2tpns.h,v 1.54 2005-01-25 04:19:05 bodea Exp $
 
 #ifndef __L2TPNS_H__
 #define __L2TPNS_H__
@@ -213,7 +213,9 @@ typedef struct
        uint16_t sid;                   // near end session id.
        uint8_t filter_in;              // input filter index (to ip_filters[N-1]; 0 if none)
        uint8_t filter_out;             // output filter index
-       char reserved[18];              // Space to expand structure without changing HB_VERSION
+       struct in6_addr ipv6route;      // Static IPv6 route
+       uint8_t ipv6prefixlen;          // IPv6 route prefix length
+       char reserved[1];               // Space to expand structure without changing HB_VERSION
 }
 sessiont;
 
@@ -222,6 +224,7 @@ sessiont;
 #define SF_CCP_ACKED   4       // CCP negotiated
 #define SF_IPV6CP_ACKED        8       // IPv6 negotiated
 #define SF_IPV6_NACKED 16      // IPv6 rejected
+#define SF_IPV6_ROUTED 32      // advertised v6 route
 
 #define AUTHPAP                1       // allow PAP
 #define AUTHCHAP       2       // allow CHAP
@@ -377,11 +380,14 @@ struct Tstats
 #ifdef STATISTICS
     uint32_t   call_processtun;
     uint32_t   call_processipout;
+    uint32_t   call_processipv6out;
     uint32_t   call_processudp;
     uint32_t   call_sessionbyip;
+    uint32_t   call_sessionbyipv6;
     uint32_t   call_sessionbyuser;
     uint32_t   call_sendarp;
     uint32_t   call_sendipcp;
+    uint32_t   call_processipv6cp;
     uint32_t   call_tunnelsend;
     uint32_t   call_sessionkill;
     uint32_t   call_sessionshutdown;
@@ -396,6 +402,7 @@ struct Tstats
     uint32_t   call_processlcp;
     uint32_t   call_processipcp;
     uint32_t   call_processipin;
+    uint32_t   call_processipv6in;
     uint32_t   call_processccp;
     uint32_t   call_sendchap;
     uint32_t   call_processrad;
@@ -500,6 +507,8 @@ typedef struct
        int             cluster_hb_timeout;             // How many missed heartbeats trigger an election.
        uint64_t        cluster_table_version;          // # state changes processed by cluster
 
+       struct in6_addr ipv6_prefix;                    // Our IPv6 network pool.
+
 #ifdef BGP
 #define BGP_NUM_PEERS  2
        uint16_t as_number;
@@ -582,7 +591,9 @@ void processpap(tunnelidt t, sessionidt s, uint8_t *p, uint16_t l);
 void processchap(tunnelidt t, sessionidt s, uint8_t *p, uint16_t l);
 void processlcp(tunnelidt t, sessionidt s, uint8_t *p, uint16_t l);
 void processipcp(tunnelidt t, sessionidt s, uint8_t *p, uint16_t l);
+void processipv6cp(tunnelidt t, sessionidt s, uint8_t *p, uint16_t l);
 void processipin(tunnelidt t, sessionidt s, uint8_t *p, uint16_t l);
+void processipv6in(tunnelidt t, sessionidt s, uint8_t *p, uint16_t l);
 void processccp(tunnelidt t, sessionidt s, uint8_t *p, uint16_t l);
 void sendchap(tunnelidt t, sessionidt s);
 uint8_t *makeppp(uint8_t *b, int size, uint8_t *p, int l, tunnelidt t, sessionidt s, uint16_t mtype);
@@ -601,7 +612,10 @@ void radiusclear(uint16_t r, sessionidt s);
 
 // l2tpns.c
 clockt backoff(uint8_t try);
+void send_ipv6_ra(tunnelidt t, sessionidt s, struct in6_addr *ip);
+void route6set(sessionidt s, struct in6_addr ip, int prefixlen, int add);
 sessionidt sessionbyip(in_addr_t ip);
+sessionidt sessionbyipv6(struct in6_addr ip);
 sessionidt sessionbyuser(char *username);
 void random_data(uint8_t *buf, int len);
 void sessionshutdown(sessionidt s, char *reason);
index e6b12de..ea7a929 100644 (file)
@@ -43,5 +43,5 @@ rm -rf %{buildroot}
 %attr(644,root,root) /usr/share/man/man[58]/*
 
 %changelog
-* Thu Jan 13 2005 Brendan O'Dea <bod@optusnet.com.au> 2.1.0-1
+* Tue Jan 25 2005 Brendan O'Dea <bod@optusnet.com.au> 2.1.0-1
 - 2.1.0 release, see /usr/share/doc/l2tpns-2.1.0/Changes
diff --git a/ppp.c b/ppp.c
index 8e1e844..cdd8a94 100644 (file)
--- a/ppp.c
+++ b/ppp.c
@@ -1,6 +1,6 @@
 // L2TPNS PPP Stuff
 
-char const *cvs_id_ppp = "$Id: ppp.c,v 1.41 2005-01-13 07:57:39 bodea Exp $";
+char const *cvs_id_ppp = "$Id: ppp.c,v 1.42 2005-01-25 04:19:06 bodea Exp $";
 
 #include <stdio.h>
 #include <string.h>
@@ -139,9 +139,6 @@ void processchap(tunnelidt t, sessionidt s, uint8_t *p, uint16_t l)
        if (!r)
        {
                LOG(1, s, t, "Unexpected CHAP message\n");
-
-// FIXME: Need to drop the session here.
-
                STAT(tunnel_rx_errors);
                return;
        }
@@ -492,6 +489,20 @@ void processlcp(tunnelidt t, sessionidt s, uint8_t *p, uint16_t l)
        {
                sessionshutdown(s, "Connection closed.");
        }
+       else if (*p == ProtocolRej)
+       {
+               if (*(uint16_t *) (p+4) == htons(PPPIPV6CP))
+               {
+                       LOG(3, s, t, "IPv6 rejected\n");
+                       session[s].flags |= SF_IPV6_NACKED;
+               }
+               else
+               {
+                       LOG(1, s, t, "Unexpected LCP protocol reject 0x%X\n",
+                               ntohs(*(uint16_t *) (p+4)));
+                       STAT(tunnel_rx_errors);
+               }
+       }
        else if (*p == EchoReq)
        {
                LOG(5, s, t, "LCP: Received EchoReq.  Sending EchoReply\n");
@@ -685,6 +696,128 @@ void processipcp(tunnelidt t, sessionidt s, uint8_t *p, uint16_t l)
        }
 }
 
+// Process IPV6CP messages
+void processipv6cp(tunnelidt t, sessionidt s, uint8_t *p, uint16_t l)
+{
+
+       CSTAT(processipv6cp);
+
+       LOG_HEX(5, "IPV6CP", p, l);
+       if (l < 4)
+       {
+               LOG(1, s, t, "Short IPV6CP %d bytes\n", l);
+               STAT(tunnel_rx_errors);
+               return ;
+       }
+       if (*p == ConfigAck)
+       {
+               // happy with our IPV6CP
+               session[s].flags |= SF_IPV6CP_ACKED;
+
+               LOG(3, s, t, "IPV6CP Acked, IPv6 is now active\n");
+               // Add a routed block if configured.
+               if (session[s].ipv6prefixlen)
+               {
+                       route6set(s, session[s].ipv6route, session[s].ipv6prefixlen, 1);
+                       session[s].flags |= SF_IPV6_ROUTED;
+               }
+
+               // Send an initial RA (TODO: Should we send these regularly?)
+               send_ipv6_ra(t, s, NULL);
+               return;
+       }
+       if (*p != ConfigReq)
+       {
+               LOG(1, s, t, "Unexpected IPV6CP code %d\n", *p);
+               STAT(tunnel_rx_errors);
+               return;
+       }
+
+       LOG(4, s, t, "IPV6CP ConfigReq received\n");
+       if (ntohs(*(uint16_t *) (p + 2)) > l)
+       {
+               LOG(1, s, t, "Length mismatch IPV6CP %d/%d\n", ntohs(*(uint16_t *) (p + 2)), l);
+               STAT(tunnel_rx_errors);
+               return ;
+       }
+       if (!session[s].ip)
+       {
+               LOG(3, s, t, "Waiting on radius reply\n");
+               return;                 // have to wait on RADIUS reply
+       }
+       // form a config reply quoting the IP in the session
+       {
+               uint8_t b[MAXCONTROL];
+               uint8_t *i,
+               *q;
+
+               l = ntohs(*(uint16_t *) (p + 2)); // We must use length from IPV6CP len field
+               q = p + 4;
+               i = p + l;
+               while (q < i && q[1])
+               {
+                       if (*q != 1)
+                               break;
+                       q += q[1];
+               }
+               if (q < i)
+               {
+                       // reject
+                       uint16_t n = 4;
+                       i = p + l;
+                       if (!(q = makeppp(b, sizeof(b), p, l, t, s, PPPIPV6CP)))
+                       {
+                               LOG(2, s, t, "Failed to send IPV6CP ConfigRej\n");
+                               return;
+                       }
+                       *q = ConfigRej;
+                       p += 4;
+                       while (p < i && p[1])
+                       {
+                               if (*p != 1)
+                               {
+                                       LOG(2, s, t, "IPV6CP reject %d\n", *p);
+                                       memcpy(q + n, p, p[1]);
+                                       n += p[1];
+                               }
+                               p += p[1];
+                       }
+                       *(uint16_t *) (q + 2) = htons(n);
+                       LOG(4, s, t, "Sending ConfigRej\n");
+                       tunnelsend(b, n + (q - b), t); // send it
+               }
+               else
+               {
+                       LOG(4, s, t, "Sending ConfigAck\n");
+                       *p = ConfigAck;
+                       i = findppp(p, 1);              // IP address
+                       if (!i || i[1] != 10)
+                       {
+                               LOG(1, s, t, "No IP in IPV6CP request\n");
+                               STAT(tunnel_rx_errors);
+                               return ;
+                       }
+                       if ((*(uint32_t *) (i + 2) != htonl(session[s].ip)) || 
+                                       (*(uint32_t *) (i + 6) != 0))
+                       {
+                               *(uint32_t *) (i + 2) = htonl(session[s].ip);
+                               *(uint32_t *) (i + 6) = 0;
+                               *p = ConfigNak;
+                               LOG(4, s, t,
+                                       " No, a ConfigNak, client is "
+                                       "requesting IP - sending %s\n",
+                                       fmtaddr(htonl(session[s].ip), 0));
+                       }
+                       if (!(q = makeppp(b, sizeof(b), p, l, t, s, PPPIPV6CP)))
+                       {
+                               LOG(2, s, t, " Failed to send IPV6CP packet.\n");
+                               return;
+                       }
+                       tunnelsend(b, l + (q - b), t); // send it
+               }
+       }
+}
+
 // process IP packet received
 //
 // This MUST be called with at least 4 byte behind 'p'.
@@ -759,6 +892,92 @@ void processipin(tunnelidt t, sessionidt s, uint8_t *p, uint16_t l)
        INC_STAT(tun_tx_bytes, l - 4);
 }
 
+// process IPv6 packet received
+//
+// This MUST be called with at least 4 byte behind 'p'.
+// (i.e. this routine writes to p[-4]).
+void processipv6in(tunnelidt t, sessionidt s, uint8_t *p, uint16_t l)
+{
+       struct in6_addr ip;
+       in_addr_t ipv4;
+
+       CSTAT(processipv6in);
+
+       LOG_HEX(5, "IPv6", p, l);
+
+       ip = *(struct in6_addr *) (p + 8);
+       ipv4 = ntohl(*(uint32_t *)(p + 16));
+
+       if (l > MAXETHER)
+       {
+               LOG(1, s, t, "IP packet too long %d\n", l);
+               STAT(tunnel_rx_errors);
+               return ;
+       }
+
+       // no spoof
+       if (ipv4 != session[s].ip && memcmp(&config->ipv6_prefix, &ip, 8) && sessionbyipv6(ip) != s)
+       {
+               char str[INET6_ADDRSTRLEN];
+               LOG(5, s, t, "Dropping packet with spoofed IP %s\n",
+                               inet_ntop(AF_INET6, &ip, str, INET6_ADDRSTRLEN));
+               return;
+       }
+
+       // Check if it's a Router Solicition message.
+       if (*(p + 6) == 58 && *(p + 7) == 255 && *(p + 24) == 0xFF && *(p + 25) == 2 &&
+                       *(uint32_t *)(p + 26) == 0 && *(uint32_t *)(p + 30) == 0 &&
+                       *(uint32_t *)(p + 34) == 0 &&
+                       *(p + 38) == 0 && *(p + 39) == 2 && *(p + 40) == 133) {
+               LOG(3, s, t, "Got IPv6 RS\n");
+               send_ipv6_ra(t, s, &ip);
+               return;
+       }
+
+       // Add on the tun header
+       p -= 4;
+       *(uint32_t *)p = htonl(PKTIPV6);
+       l += 4;
+
+       // Are we throttled and a slave?
+       if (session[s].tbf_in && !config->cluster_iam_master) {
+               // Pass it to the master for handling.
+               master_throttle_packet(session[s].tbf_in, p, l);
+               return;
+       }
+
+       // Are we throttled and a master?? actually handle the throttled
+       // packets.
+       if (session[s].tbf_in && config->cluster_iam_master) {
+               tbf_queue_packet(session[s].tbf_in, p, l);
+               return;
+       }
+
+       // send to ethernet
+       if (tun_write(p, l) < 0)
+       {
+               STAT(tun_tx_errors);
+               LOG(0, s, t, "Error writing %d bytes to TUN device: %s" " (tunfd=%d, p=%p)\n",
+                       l, strerror(errno), tunfd, p);
+       }
+
+       if (session[s].snoop_ip && session[s].snoop_port)
+       {
+               // Snooping this session
+               snoop_send_packet(p, l, session[s].snoop_ip, session[s].snoop_port);
+       }
+
+       session[s].cin += l - 4;
+       session[s].total_cin += l - 4;
+       sess_local[s].cin += l - 4;
+
+       session[s].pin++;
+       eth_tx += l - 4;
+
+       STAT(tun_tx_packets);
+       INC_STAT(tun_tx_bytes, l - 4);
+}
+
 //
 // Helper routine for the TBF filters.
 // Used to send queued data in from the user.
index 571fded..d1d5894 100644 (file)
--- a/radius.c
+++ b/radius.c
@@ -1,6 +1,6 @@
 // L2TPNS Radius Stuff
 
-char const *cvs_id_radius = "$Id: radius.c,v 1.22 2005-01-05 14:35:01 bodea Exp $";
+char const *cvs_id_radius = "$Id: radius.c,v 1.23 2005-01-25 04:19:06 bodea Exp $";
 
 #include <time.h>
 #include <stdio.h>
@@ -619,12 +619,38 @@ void processrad(uint8_t *buf, int len, char socket_index)
                                                } while (newp);
                                                free(avpair);
                                        }
+                                       else if (*p == 99)
+                                       {
+                                               // Framed-IPv6-Route
+                                               struct in6_addr r6;
+                                               int prefixlen;
+                                               uint8_t *n = p + 2;
+                                               uint8_t *e = p + p[1];
+                                               uint8_t *m = strchr(n, '/');
+
+                                               *m++ = 0;
+                                               inet_pton(AF_INET6, n, &r6);
+
+                                               prefixlen = 0;
+                                               while (m < e && isdigit(*m)) {
+                                                       prefixlen = prefixlen * 10 + *m++ - '0';
+                                               }
+
+                                               if (prefixlen)
+                                               {
+                                                       LOG(3, s, session[s].tunnel,
+                                                               "   Radius reply contains route for %s/%d\n",
+                                                               n, prefixlen);
+                                                       session[s].ipv6route = r6;
+                                                       session[s].ipv6prefixlen = prefixlen;
+                                               }
+                                       }
                                }
                        }
                        else if (r_code == AccessReject)
                        {
                                LOG(2, s, session[s].tunnel, "   Authentication denied for %s\n", session[s].user);
-//FIXME: We should tear down the session here!
+                               sessionshutdown(s, "Authentication denied");
                                break;
                        }
 
diff --git a/util.c b/util.c
index 3db4edd..205fe03 100644 (file)
--- a/util.c
+++ b/util.c
@@ -1,6 +1,6 @@
 /* Misc util functions */
 
-char const *cvs_id_util = "$Id: util.c,v 1.9 2004-12-20 07:23:53 bodea Exp $";
+char const *cvs_id_util = "$Id: util.c,v 1.10 2005-01-25 04:19:07 bodea Exp $";
 
 #include <unistd.h>
 #include <errno.h>
@@ -40,7 +40,7 @@ void *shared_malloc(unsigned int size)
 }
 
 extern int forked;
-extern int udpfd, controlfd, tunfd, snoopfd, ifrfd, cluster_sockfd;
+extern int udpfd, controlfd, tunfd, snoopfd, ifrfd, ifr6fd, cluster_sockfd;
 extern int *radfds;
 
 pid_t fork_and_close()
@@ -78,6 +78,7 @@ pid_t fork_and_close()
        if (controlfd != -1)      close(controlfd);
        if (snoopfd != -1)        close(snoopfd);
        if (ifrfd != -1)          close(ifrfd);
+       if (ifr6fd != -1)         close(ifr6fd);
        if (cluster_sockfd != -1) close(cluster_sockfd);
        if (clifd != -1)          close(clifd);