tun.c cleanup and support for setting multiple addresses on FreeBSD
diff --git a/README b/README
index aa60138..3d55259 100644
--- a/README
+++ b/README
@@ -208,6 +208,22 @@
 for details on autotools.
 
 
+Checking out from CVS
+---------------------
+
+To download the latest source code from anonymous CVS:
+
+cvs -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/ggsn login
+cvs -z3 -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/ggsn co openggsn
+
+Or to download from developer CVS:
+
+export CVS_RSH=ssh
+cvs -z3 -d:ext:developername@cvs.sourceforge.net:/cvsroot/ggsn co openggsn
+
+Both the above sets of commands creates a new directory called openggsn.
+
+
 Compilation and installation
 ----------------------------
 
diff --git a/ggsn/tun.c b/ggsn/tun.c
index a19fd5b..208cca6 100644
--- a/ggsn/tun.c
+++ b/ggsn/tun.c
@@ -45,20 +45,25 @@
 #include <errno.h>
 #include <net/route.h>
 
-#ifdef __linux__
+#if defined(__linux__)
 #include <linux/if.h>
 #include <linux/if_tun.h>
 #include <linux/netlink.h>
 #include <linux/rtnetlink.h>
+
+#elif defined (__FreeBSD__)
+#include <net/if.h>
+#include <net/if_tun.h>
+
 #elif defined (__sun__)
 #include <stropts.h>
 #include <sys/sockio.h>
 #include <net/if.h>
 #include <net/if_tun.h>
 /*#include "sun_if_tun.h"*/
-#elif defined (__FreeBSD__)
-#include <net/if.h>
-#include <net/if_tun.h>
+
+#else
+#error  "Unknown platform!"
 #endif
 
 
@@ -66,7 +71,8 @@
 #include "syserr.h"
 
 
-#ifdef __linux__
+#if defined(__linux__)
+
 int tun_nlattr(struct nlmsghdr *n, int nsize, int type, void *d, int dlen)
 {
   int len = RTA_LENGTH(dlen);
@@ -231,7 +237,7 @@
 		struct in_addr *dstaddr,
 		struct in_addr *netmask) {
 
-#ifdef __linux__
+#if defined(__linux__)
   struct {
     struct nlmsghdr 	n;
     struct ifaddrmsg 	i;
@@ -242,20 +248,14 @@
   int addr_len;
   int fd;
   int status;
-
+  
   struct sockaddr_nl nladdr;
   struct iovec iov;
   struct msghdr msg;
-#endif
 
   if (!this->addrs) /* Use ioctl for first addr to make ping work */
     return tun_setaddr(this, addr, dstaddr, netmask);
 
-#ifndef __linux__
-  sys_err(LOG_ERR, __FILE__, __LINE__, errno,
-	  "Setting multiple addresses only possible on linux");
-  return -1;
-#else
   memset(&req, 0, sizeof(req));
   req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
   req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
@@ -331,11 +331,66 @@
 
   status = sendmsg(fd, &msg, 0); /* TODO Error check */
 
-  tun_sifflags(this, IFF_UP | IFF_RUNNING);
+  tun_sifflags(this, IFF_UP | IFF_RUNNING); /* TODO */
   close(fd);
   this->addrs++;
   return 0;
+
+#elif defined (__FreeBSD__)
+
+  int fd;
+  struct ifaliasreq      areq;
+
+  /* TODO: Is this needed on FreeBSD? */
+  if (!this->addrs) /* Use ioctl for first addr to make ping work */
+    return tun_setaddr(this, addr, dstaddr, netmask);
+
+  memset(&areq, 0, sizeof(areq));
+
+  /* Set up interface name */
+  strncpy(areq.ifra_name, this->devname, IFNAMSIZ);
+  ifr.ifr_name[IFNAMSIZ-1] = 0; /* Make sure to terminate */
+
+  ((struct sockaddr_in) areq.ifra_addr).sin_family = AF_INET;
+  ((struct sockaddr_in) areq.ifra_addr).sin_len = sizeof(areq.ifra_addr);
+  ((struct sockaddr_in) areq.ifra_addr).sin_addr.s_addr = addr->s_addr;
+
+  ((struct sockaddr_in) areq.ifra_mask).sin_family = AF_INET;
+  ((struct sockaddr_in) areq.ifra_mask).sin_len    = sizeof(areq.ifra_mask);
+  ((struct sockaddr_in) areq.ifra_mask).sin_addr.s_addr = netmask->s_addr;
+
+
+  /* Create a channel to the NET kernel. */
+  if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+    sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+	    "socket() failed");
+    return -1;
+  }
+  
+  if (ioctl(fd, SIOCAIFADDR, (void *) &areq) < 0) {
+    sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+	    "ioctl(SIOCAIFADDR) failed");
+    close(fd);
+    return -1;
+  }
+
+  close(fd);
+  this->addrs++;
+  return 0;
+
+#elif defined (__sun__)
+  
+  if (!this->addrs) /* Use ioctl for first addr to make ping work */
+    return tun_setaddr(this, addr, dstaddr, netmask);
+  
+  sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+	  "Setting multiple addresses not possible on Solaris");
+  return -1;
+
+#else
+#error  "Unknown platform!"
 #endif
+  
 }
 
 
@@ -350,15 +405,17 @@
   memset (&ifr, '\0', sizeof (ifr));
   ifr.ifr_addr.sa_family = AF_INET;
   ifr.ifr_dstaddr.sa_family = AF_INET;
-#ifdef __linux__
+
+#if defined(__linux__)
   ifr.ifr_netmask.sa_family = AF_INET;
-#endif
-#ifdef __FreeBSD__
+
+#elif defined(__FreeBSD__)
   ((struct sockaddr_in *) &ifr.ifr_addr)->sin_len = 
     sizeof (struct sockaddr_in);
   ((struct sockaddr_in *) &ifr.ifr_dstaddr)->sin_len = 
     sizeof (struct sockaddr_in);
 #endif
+
   strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
   ifr.ifr_name[IFNAMSIZ-1] = 0; /* Make sure to terminate */
 
@@ -400,17 +457,21 @@
 
   if (netmask) { /* Set the netmask */
     this->netmask.s_addr = netmask->s_addr;
-#if defined(__sun__)
-    /* TODO: This should probably be ifr_addr */
-    ((struct sockaddr_in *) &ifr.ifr_dstaddr)->sin_addr.s_addr = 
+#if defined(__linux__)
+    ((struct sockaddr_in *) &ifr.ifr_netmask)->sin_addr.s_addr = 
       netmask->s_addr;
+
 #elif defined(__FreeBSD__)
     ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr = 
       netmask->s_addr;
-#else
-    ((struct sockaddr_in *) &ifr.ifr_netmask)->sin_addr.s_addr = 
+
+#elif defined(__sun__)
+    ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr = 
       netmask->s_addr;
+#else
+#error  "Unknown platform!"
 #endif
+
     if (ioctl(fd, SIOCSIFNETMASK, (void *) &ifr) < 0) {
       sys_err(LOG_ERR, __FILE__, __LINE__, errno,
 	      "ioctl(SIOCSIFNETMASK) failed");
@@ -418,13 +479,15 @@
       return -1;
     }
   }
-
+  
   close(fd);
   this->addrs++;
-
+  
   /* On linux the route to the interface is set automatically
      on FreeBSD we have to do this manually */
 
+  /* TODO: How does it work on Solaris? */
+
 #if defined(__FreeBSD__)
   tun_sifflags(this, IFF_UP | IFF_RUNNING); /* TODO */
   return tun_addroute(this, addr, addr, netmask);
@@ -442,7 +505,8 @@
 
 
   /* TODO: Learn how to set routing table on sun  */
-#ifdef __linux__
+
+#if defined(__linux__)
 
   struct rtentry r;
   int fd;
@@ -471,6 +535,7 @@
     return -1;
   }
   close(fd);
+  return 0;
 
 #elif defined(__FreeBSD__)
 
@@ -520,24 +585,35 @@
    return -1;
  }
  close(fd);
+ return 0;
 
+#elif defined(__sun__)
+ sys_err(LOG_WARNING, __FILE__, __LINE__, errno,
+	 "Could not set up routing on Solaris. Please add route manually.");
+ return 0;
+ 
+#else
+#error  "Unknown platform!"
 #endif
 
-  return 0;
 }
 
-
 int tun_new(struct tun_t **tun)
 {
 
-#ifdef __sun__
-  int if_fd, ppa = -1;
-  static int ip_fd = 0;
+#if defined(__linux__)
+  struct ifreq ifr;
+
 #elif defined(__FreeBSD__)
   char devname[IFNAMSIZ+5]; /* "/dev/" + ifname */
   int devnum;
+
+#elif defined(__sun__)
+  int if_fd, ppa = -1;
+  static int ip_fd = 0;
+
 #else
-  struct ifreq ifr;
+#error  "Unknown platform!"
 #endif
   
   if (!(*tun = calloc(1, sizeof(struct tun_t)))) {
@@ -547,14 +623,14 @@
   
   (*tun)->cb_ind = NULL;
   (*tun)->addrs = 0;
-
-#ifdef __linux__
+  
+#if defined(__linux__)
   /* Open the actual tun device */
   if (((*tun)->fd  = open("/dev/net/tun", O_RDWR)) < 0) {
     sys_err(LOG_ERR, __FILE__, __LINE__, errno, "open() failed");
     return -1;
   }
-
+  
   /* Set device flags. For some weird reason this is also the method
      used to obtain the network interface name */
   memset(&ifr, 0, sizeof(ifr));
@@ -564,15 +640,32 @@
     close((*tun)->fd);
     return -1;
   } 
-
+  
   strncpy((*tun)->devname, ifr.ifr_name, IFNAMSIZ);
   (*tun)->devname[IFNAMSIZ] = 0;
-
+  
   ioctl((*tun)->fd, TUNSETNOCSUM, 1); /* Disable checksums */
+  return 0;
+  
+#elif defined(__FreeBSD__)
 
-#endif
+  /* Find suitable device */
+  for (devnum = 0; devnum < 255; devnum++) { /* TODO 255 */ 
+    snprintf(devname, sizeof(devname), "/dev/tun%d", devnum);
+    devname[sizeof(devname)] = 0;
+    if (((*tun)->fd = open(devname, O_RDWR)) >= 0) break;
+    if (errno != EBUSY) break;
+  } 
+  if ((*tun)->fd < 0) {
+    sys_err(LOG_ERR, __FILE__, __LINE__, errno, "Can't find tunnel device");
+    return -1;
+  }
 
-#ifdef __sun__
+  snprintf((*tun)->devname, sizeof((*tun)->devname), "tun%d", devnum);
+  (*tun)->devname[sizeof((*tun)->devname)] = 0;
+  return 0;
+
+#elif defined(__sun__)
 
   if( (ip_fd = open("/dev/ip", O_RDWR, 0)) < 0){
     sys_err(LOG_ERR, __FILE__, __LINE__, errno, "Can't open /dev/ip");
@@ -615,28 +708,12 @@
   
   snprintf((*tun)->devname, sizeof((*tun)->devname), "tun%d", ppa);
   (*tun)->devname[sizeof((*tun)->devname)] = 0;
-  
-#endif
- 
-#ifdef __FreeBSD__
-  /* Find suitable device */
-  for (devnum = 0; devnum < 255; devnum++) { /* TODO 255 */ 
-    snprintf(devname, sizeof(devname), "/dev/tun%d", devnum);
-    devname[sizeof(devname)] = 0;
-    if (((*tun)->fd = open(devname, O_RDWR)) >= 0) break;
-    if (errno != EBUSY) break;
-  } 
-  if ((*tun)->fd < 0) {
-    sys_err(LOG_ERR, __FILE__, __LINE__, errno, "Can't find tunnel device");
-    return -1;
-  }
-
-  snprintf((*tun)->devname, sizeof((*tun)->devname), "tun%d", devnum);
-  (*tun)->devname[sizeof((*tun)->devname)] = 0;
-
-#endif
-
   return 0;
+
+#else
+#error  "Unknown platform!"
+#endif
+
 }
 
 int tun_free(struct tun_t *tun)
diff --git a/sgsnemu/tun.c b/sgsnemu/tun.c
index a19fd5b..208cca6 100644
--- a/sgsnemu/tun.c
+++ b/sgsnemu/tun.c
@@ -45,20 +45,25 @@
 #include <errno.h>
 #include <net/route.h>
 
-#ifdef __linux__
+#if defined(__linux__)
 #include <linux/if.h>
 #include <linux/if_tun.h>
 #include <linux/netlink.h>
 #include <linux/rtnetlink.h>
+
+#elif defined (__FreeBSD__)
+#include <net/if.h>
+#include <net/if_tun.h>
+
 #elif defined (__sun__)
 #include <stropts.h>
 #include <sys/sockio.h>
 #include <net/if.h>
 #include <net/if_tun.h>
 /*#include "sun_if_tun.h"*/
-#elif defined (__FreeBSD__)
-#include <net/if.h>
-#include <net/if_tun.h>
+
+#else
+#error  "Unknown platform!"
 #endif
 
 
@@ -66,7 +71,8 @@
 #include "syserr.h"
 
 
-#ifdef __linux__
+#if defined(__linux__)
+
 int tun_nlattr(struct nlmsghdr *n, int nsize, int type, void *d, int dlen)
 {
   int len = RTA_LENGTH(dlen);
@@ -231,7 +237,7 @@
 		struct in_addr *dstaddr,
 		struct in_addr *netmask) {
 
-#ifdef __linux__
+#if defined(__linux__)
   struct {
     struct nlmsghdr 	n;
     struct ifaddrmsg 	i;
@@ -242,20 +248,14 @@
   int addr_len;
   int fd;
   int status;
-
+  
   struct sockaddr_nl nladdr;
   struct iovec iov;
   struct msghdr msg;
-#endif
 
   if (!this->addrs) /* Use ioctl for first addr to make ping work */
     return tun_setaddr(this, addr, dstaddr, netmask);
 
-#ifndef __linux__
-  sys_err(LOG_ERR, __FILE__, __LINE__, errno,
-	  "Setting multiple addresses only possible on linux");
-  return -1;
-#else
   memset(&req, 0, sizeof(req));
   req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
   req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
@@ -331,11 +331,66 @@
 
   status = sendmsg(fd, &msg, 0); /* TODO Error check */
 
-  tun_sifflags(this, IFF_UP | IFF_RUNNING);
+  tun_sifflags(this, IFF_UP | IFF_RUNNING); /* TODO */
   close(fd);
   this->addrs++;
   return 0;
+
+#elif defined (__FreeBSD__)
+
+  int fd;
+  struct ifaliasreq      areq;
+
+  /* TODO: Is this needed on FreeBSD? */
+  if (!this->addrs) /* Use ioctl for first addr to make ping work */
+    return tun_setaddr(this, addr, dstaddr, netmask);
+
+  memset(&areq, 0, sizeof(areq));
+
+  /* Set up interface name */
+  strncpy(areq.ifra_name, this->devname, IFNAMSIZ);
+  ifr.ifr_name[IFNAMSIZ-1] = 0; /* Make sure to terminate */
+
+  ((struct sockaddr_in) areq.ifra_addr).sin_family = AF_INET;
+  ((struct sockaddr_in) areq.ifra_addr).sin_len = sizeof(areq.ifra_addr);
+  ((struct sockaddr_in) areq.ifra_addr).sin_addr.s_addr = addr->s_addr;
+
+  ((struct sockaddr_in) areq.ifra_mask).sin_family = AF_INET;
+  ((struct sockaddr_in) areq.ifra_mask).sin_len    = sizeof(areq.ifra_mask);
+  ((struct sockaddr_in) areq.ifra_mask).sin_addr.s_addr = netmask->s_addr;
+
+
+  /* Create a channel to the NET kernel. */
+  if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+    sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+	    "socket() failed");
+    return -1;
+  }
+  
+  if (ioctl(fd, SIOCAIFADDR, (void *) &areq) < 0) {
+    sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+	    "ioctl(SIOCAIFADDR) failed");
+    close(fd);
+    return -1;
+  }
+
+  close(fd);
+  this->addrs++;
+  return 0;
+
+#elif defined (__sun__)
+  
+  if (!this->addrs) /* Use ioctl for first addr to make ping work */
+    return tun_setaddr(this, addr, dstaddr, netmask);
+  
+  sys_err(LOG_ERR, __FILE__, __LINE__, errno,
+	  "Setting multiple addresses not possible on Solaris");
+  return -1;
+
+#else
+#error  "Unknown platform!"
 #endif
+  
 }
 
 
@@ -350,15 +405,17 @@
   memset (&ifr, '\0', sizeof (ifr));
   ifr.ifr_addr.sa_family = AF_INET;
   ifr.ifr_dstaddr.sa_family = AF_INET;
-#ifdef __linux__
+
+#if defined(__linux__)
   ifr.ifr_netmask.sa_family = AF_INET;
-#endif
-#ifdef __FreeBSD__
+
+#elif defined(__FreeBSD__)
   ((struct sockaddr_in *) &ifr.ifr_addr)->sin_len = 
     sizeof (struct sockaddr_in);
   ((struct sockaddr_in *) &ifr.ifr_dstaddr)->sin_len = 
     sizeof (struct sockaddr_in);
 #endif
+
   strncpy(ifr.ifr_name, this->devname, IFNAMSIZ);
   ifr.ifr_name[IFNAMSIZ-1] = 0; /* Make sure to terminate */
 
@@ -400,17 +457,21 @@
 
   if (netmask) { /* Set the netmask */
     this->netmask.s_addr = netmask->s_addr;
-#if defined(__sun__)
-    /* TODO: This should probably be ifr_addr */
-    ((struct sockaddr_in *) &ifr.ifr_dstaddr)->sin_addr.s_addr = 
+#if defined(__linux__)
+    ((struct sockaddr_in *) &ifr.ifr_netmask)->sin_addr.s_addr = 
       netmask->s_addr;
+
 #elif defined(__FreeBSD__)
     ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr = 
       netmask->s_addr;
-#else
-    ((struct sockaddr_in *) &ifr.ifr_netmask)->sin_addr.s_addr = 
+
+#elif defined(__sun__)
+    ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr = 
       netmask->s_addr;
+#else
+#error  "Unknown platform!"
 #endif
+
     if (ioctl(fd, SIOCSIFNETMASK, (void *) &ifr) < 0) {
       sys_err(LOG_ERR, __FILE__, __LINE__, errno,
 	      "ioctl(SIOCSIFNETMASK) failed");
@@ -418,13 +479,15 @@
       return -1;
     }
   }
-
+  
   close(fd);
   this->addrs++;
-
+  
   /* On linux the route to the interface is set automatically
      on FreeBSD we have to do this manually */
 
+  /* TODO: How does it work on Solaris? */
+
 #if defined(__FreeBSD__)
   tun_sifflags(this, IFF_UP | IFF_RUNNING); /* TODO */
   return tun_addroute(this, addr, addr, netmask);
@@ -442,7 +505,8 @@
 
 
   /* TODO: Learn how to set routing table on sun  */
-#ifdef __linux__
+
+#if defined(__linux__)
 
   struct rtentry r;
   int fd;
@@ -471,6 +535,7 @@
     return -1;
   }
   close(fd);
+  return 0;
 
 #elif defined(__FreeBSD__)
 
@@ -520,24 +585,35 @@
    return -1;
  }
  close(fd);
+ return 0;
 
+#elif defined(__sun__)
+ sys_err(LOG_WARNING, __FILE__, __LINE__, errno,
+	 "Could not set up routing on Solaris. Please add route manually.");
+ return 0;
+ 
+#else
+#error  "Unknown platform!"
 #endif
 
-  return 0;
 }
 
-
 int tun_new(struct tun_t **tun)
 {
 
-#ifdef __sun__
-  int if_fd, ppa = -1;
-  static int ip_fd = 0;
+#if defined(__linux__)
+  struct ifreq ifr;
+
 #elif defined(__FreeBSD__)
   char devname[IFNAMSIZ+5]; /* "/dev/" + ifname */
   int devnum;
+
+#elif defined(__sun__)
+  int if_fd, ppa = -1;
+  static int ip_fd = 0;
+
 #else
-  struct ifreq ifr;
+#error  "Unknown platform!"
 #endif
   
   if (!(*tun = calloc(1, sizeof(struct tun_t)))) {
@@ -547,14 +623,14 @@
   
   (*tun)->cb_ind = NULL;
   (*tun)->addrs = 0;
-
-#ifdef __linux__
+  
+#if defined(__linux__)
   /* Open the actual tun device */
   if (((*tun)->fd  = open("/dev/net/tun", O_RDWR)) < 0) {
     sys_err(LOG_ERR, __FILE__, __LINE__, errno, "open() failed");
     return -1;
   }
-
+  
   /* Set device flags. For some weird reason this is also the method
      used to obtain the network interface name */
   memset(&ifr, 0, sizeof(ifr));
@@ -564,15 +640,32 @@
     close((*tun)->fd);
     return -1;
   } 
-
+  
   strncpy((*tun)->devname, ifr.ifr_name, IFNAMSIZ);
   (*tun)->devname[IFNAMSIZ] = 0;
-
+  
   ioctl((*tun)->fd, TUNSETNOCSUM, 1); /* Disable checksums */
+  return 0;
+  
+#elif defined(__FreeBSD__)
 
-#endif
+  /* Find suitable device */
+  for (devnum = 0; devnum < 255; devnum++) { /* TODO 255 */ 
+    snprintf(devname, sizeof(devname), "/dev/tun%d", devnum);
+    devname[sizeof(devname)] = 0;
+    if (((*tun)->fd = open(devname, O_RDWR)) >= 0) break;
+    if (errno != EBUSY) break;
+  } 
+  if ((*tun)->fd < 0) {
+    sys_err(LOG_ERR, __FILE__, __LINE__, errno, "Can't find tunnel device");
+    return -1;
+  }
 
-#ifdef __sun__
+  snprintf((*tun)->devname, sizeof((*tun)->devname), "tun%d", devnum);
+  (*tun)->devname[sizeof((*tun)->devname)] = 0;
+  return 0;
+
+#elif defined(__sun__)
 
   if( (ip_fd = open("/dev/ip", O_RDWR, 0)) < 0){
     sys_err(LOG_ERR, __FILE__, __LINE__, errno, "Can't open /dev/ip");
@@ -615,28 +708,12 @@
   
   snprintf((*tun)->devname, sizeof((*tun)->devname), "tun%d", ppa);
   (*tun)->devname[sizeof((*tun)->devname)] = 0;
-  
-#endif
- 
-#ifdef __FreeBSD__
-  /* Find suitable device */
-  for (devnum = 0; devnum < 255; devnum++) { /* TODO 255 */ 
-    snprintf(devname, sizeof(devname), "/dev/tun%d", devnum);
-    devname[sizeof(devname)] = 0;
-    if (((*tun)->fd = open(devname, O_RDWR)) >= 0) break;
-    if (errno != EBUSY) break;
-  } 
-  if ((*tun)->fd < 0) {
-    sys_err(LOG_ERR, __FILE__, __LINE__, errno, "Can't find tunnel device");
-    return -1;
-  }
-
-  snprintf((*tun)->devname, sizeof((*tun)->devname), "tun%d", devnum);
-  (*tun)->devname[sizeof((*tun)->devname)] = 0;
-
-#endif
-
   return 0;
+
+#else
+#error  "Unknown platform!"
+#endif
+
 }
 
 int tun_free(struct tun_t *tun)