File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / coova-chilli / src / tun.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 22:48:25 2012 UTC (12 years, 10 months ago) by misho
Branches: coova-chilli, MAIN
CVS tags: v1_0_12, HEAD
coova-chilli

/* 
 * Tunnel Interface Functions.
 * Copyright (C) 2002, 2003, 2004 Mondru AB.
 * Copyright (c) 2006-2007 David Bird <david@coova.com>
 * 
 * The contents of this file may be used under the terms of the GNU
 * General Public License Version 2, provided that the above copyright
 * notice and this permission notice is included in all copies or
 * substantial portions of the software.
 * 
 */

/*
 *  A tunnel is the back-haul link which chilli sends traffic. Typically,
 *  this is a single tun/tap interface allowing chilli to simply pass on
 *  packets to the kernel for processing (iptables) and routing. Without the
 *  tun/tap interface, chilli must decide for itself how to route traffic,
 *  maintaining a socket into each back-haul interface. One or more tunnels
 *  are required.
 *
 */

#include "system.h"
#include "tun.h"
#include "ippool.h"
#include "radius.h"
#include "radius_wispr.h"
#include "radius_chillispot.h"
#include "redir.h"
#include "syserr.h"
#include "dhcp.h"
#include "cmdline.h"
#include "chilli.h"
#include "options.h"
#include "net.h"

#define inaddr(x)    (((struct sockaddr_in *)&ifr->x)->sin_addr)
#define inaddr2(p,x) (((struct sockaddr_in *)&(p)->x)->sin_addr)

int tun_discover(struct tun_t *this) {
  net_interface netif;
  struct ifconf ic;
  int fd, len, i;

  if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    log_err(errno, "socket() failed");
    return -1;
  }

  ic.ifc_buf=0;
  ic.ifc_len=0;

  if (ioctl(fd, SIOCGIFCONF, &ic) < 0) {
    log_err(errno, "ioctl(SIOCGIFCONF)");
    close(fd);
    return -1;
  }

  ic.ifc_buf = calloc((size_t)ic.ifc_len, 1);
  if (ioctl(fd, SIOCGIFCONF, &ic) < 0) {
    log_err(errno, "ioctl(SIOCGIFCONF)");
    close(fd);
    return -1;
  }
    
  len = (ic.ifc_len/sizeof(struct ifreq));

  for (i=0; i<len; ++i) {
    struct ifreq *ifr = (struct ifreq *)&ic.ifc_req[i];
    memset(&netif, 0, sizeof(netif));


    /* device name and address */
    strncpy(netif.devname, ifr->ifr_name, sizeof(netif.devname));
    netif.address = inaddr(ifr_addr);

    log_dbg("Interface: %s", ifr->ifr_name);
    log_dbg("\tIP Address:\t%s", inet_ntoa(inaddr(ifr_addr)));


    /* netmask */
    if (-1 < ioctl(fd, SIOCGIFNETMASK, (caddr_t)ifr)) {

      netif.netmask = inaddr(ifr_addr);
      log_dbg("\tNetmask:\t%s", inet_ntoa(inaddr(ifr_addr)));

    } else log_err(errno, "ioctl(SIOCGIFNETMASK)");


    /* hardware address */
#ifdef SIOCGIFHWADDR
    if (-1 < ioctl(fd, SIOCGIFHWADDR, (caddr_t)ifr)) {
      switch (ifr->ifr_hwaddr.sa_family) {
      case  ARPHRD_NETROM:  
      case  ARPHRD_ETHER:  
      case  ARPHRD_PPP:
      case  ARPHRD_EETHER:  
      case  ARPHRD_IEEE802: 
	{
	  unsigned char *u = (unsigned char *)&ifr->ifr_addr.sa_data;

	  memcpy(netif.hwaddr, u, 6);

	  log_dbg("\tHW Address:\t%2.2X-%2.2X-%2.2X-%2.2X-%2.2X-%2.2x",
		  u[0], u[1], u[2], u[3], u[4], u[5]);
	}
	break;
      }
    } else log_err(errno, "ioctl(SIOCGIFHWADDR)");
#else
#ifdef SIOCGENADDR
    if (-1 < ioctl(fd, SIOCGENADDR, (caddr_t)ifr)) {
      unsigned char *u = (unsigned char *)&ifr->ifr_enaddr;

      memcpy(netif.hwaddr, u, 6);

      log_dbg("\tHW Address:\t%2.2X-%2.2X-%2.2X-%2.2X-%2.2X-%2.2x",
		  u[0], u[1], u[2], u[3], u[4], u[5]);
    } else log_err(errno, "ioctl(SIOCGENADDR)");
#else
#warning Do not know how to find interface hardware address
#endif /* SIOCGENADDR */
#endif /* SIOCGIFHWADDR */


    /* flags */
    if (-1 < ioctl(fd, SIOCGIFFLAGS, (caddr_t)ifr)) {

      netif.devflags = ifr->ifr_flags;

    } else log_err(errno, "ioctl(SIOCGIFFLAGS)");


    /* point-to-point gateway */
    if (netif.devflags & IFF_POINTOPOINT) {
      if (-1 < ioctl(fd, SIOCGIFDSTADDR, (caddr_t)ifr)) {

	netif.gateway = inaddr(ifr_addr);
	log_dbg("\tPoint-to-Point:\t%s", inet_ntoa(inaddr(ifr_dstaddr)));

      } else log_err(errno, "ioctl(SIOCGIFDSTADDR)");
    }


    /* broadcast address */
    if (netif.devflags & IFF_BROADCAST) {
      if (-1 < ioctl(fd, SIOCGIFBRDADDR, (caddr_t)ifr)) {
	
	netif.broadcast = inaddr(ifr_addr);
	log_dbg("\tBroadcast:\t%s", inet_ntoa(inaddr(ifr_addr)));
	
      } else log_err(errno, "ioctl(SIOCGIFBRDADDR)");
    }


    /* mtu */
    if (-1 < ioctl(fd, SIOCGIFMTU, (caddr_t)ifr)) {
      
      netif.mtu = ifr->ifr_mtu;
      log_dbg("\tMTU:      \t%u",  ifr->ifr_mtu);
      
    } else log_err(errno, "ioctl(SIOCGIFMTU)");
    

    /* if (0 == ioctl(fd, SIOCGIFMETRIC, ifr)) */

    if (netif.address.s_addr == htonl(INADDR_LOOPBACK) ||
        netif.address.s_addr == INADDR_ANY ||
        netif.address.s_addr == INADDR_NONE)
      continue;

    else {
      net_interface *newif = tun_nextif(tun);
      if (newif) {
	memcpy(newif, &netif, sizeof(netif));
	net_open(newif);

	if (!strcmp(options.routeif, netif.devname))
	  tun->routeidx = newif->idx;
      } else {
	log_dbg("no room for interface %s", netif.devname);
      }
    }
  }

  close(fd);
  return 0;
}

net_interface * tun_nextif(struct tun_t *tun) {
  net_interface *netif;

  if (tun->_interface_count == TUN_MAX_INTERFACES)
    return 0;

  netif = &tun->_interfaces[tun->_interface_count];
  netif->idx = tun->_interface_count;

  tun->_interface_count++;

  return netif;
}

int tun_name2idx(struct tun_t *tun, char *name) {
  int i;

  for (i=0; i<tun->_interface_count; i++) 
    if (!strcmp(name, tun->_interfaces[i].devname))
      return i;

  return 0; /* tun/tap index */
}

#if defined(__linux__)

int tun_nlattr(struct nlmsghdr *n, int nsize, int type, void *d, size_t dlen) {
  size_t len = RTA_LENGTH(dlen);
  size_t alen = NLMSG_ALIGN(n->nlmsg_len);
  struct rtattr *rta = (struct rtattr*) (((void*)n) + alen);
  if (alen + len > nsize)
    return -1;
  rta->rta_len = len;
  rta->rta_type = type;
  memcpy(RTA_DATA(rta), d, dlen);
  n->nlmsg_len = alen + len;
  return 0;
}

int tun_gifindex(struct tun_t *this, int *index) {
  struct ifreq ifr;
  int fd;

  memset (&ifr, '\0', sizeof (ifr));
  ifr.ifr_addr.sa_family = AF_INET;
  ifr.ifr_dstaddr.sa_family = AF_INET;
  ifr.ifr_netmask.sa_family = AF_INET;
  strncpy(ifr.ifr_name, tuntap(this).devname, IFNAMSIZ);
  ifr.ifr_name[IFNAMSIZ-1] = 0; /* Make sure to terminate */
  if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    log_err(errno, "socket() failed");
  }
  if (ioctl(fd, SIOCGIFINDEX, &ifr)) {
    log_err(errno,"ioctl() failed");
    close(fd);
    return -1;
  }
  close(fd);
  *index = ifr.ifr_ifindex;
  return 0;
}
#endif

int tun_addaddr(struct tun_t *this, struct in_addr *addr,
		struct in_addr *dstaddr, struct in_addr *netmask) {

#if defined(__linux__)
  struct {
    struct nlmsghdr 	n;
    struct ifaddrmsg 	i;
    char buf[TUN_NLBUFSIZE];
  } req;
  
  struct sockaddr_nl local;
  size_t addr_len;
  int fd;
  int status;
  
  struct sockaddr_nl nladdr;
  struct iovec iov;
  struct msghdr msg;

  if (!this->addrs) /* Use ioctl for first addr to make ping work */
    return tun_setaddr(this, addr, dstaddr, netmask);

  memset(&req, 0, sizeof(req));
  req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
  req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
  req.n.nlmsg_type = RTM_NEWADDR;
  req.i.ifa_family = AF_INET;
  req.i.ifa_prefixlen = 32; /* 32 FOR IPv4 */
  req.i.ifa_flags = 0;
  req.i.ifa_scope = RT_SCOPE_HOST; /* TODO or 0 */

  if (tun_gifindex(this, &req.i.ifa_index)) {
    log_err(errno,"tun_gifindex() failed");
    return -1;
  }

  tun_nlattr(&req.n, sizeof(req), IFA_ADDRESS, addr, sizeof(addr));
  tun_nlattr(&req.n, sizeof(req), IFA_LOCAL, dstaddr, sizeof(dstaddr));

  if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
    log_err(errno,"socket() failed");
    return -1;
  }

  memset(&local, 0, sizeof(local));
  local.nl_family = AF_NETLINK;
  local.nl_groups = 0;
  
  if (bind(fd, (struct sockaddr*)&local, sizeof(local)) < 0) {
    log_err(errno, "bind() failed");
    close(fd);
    return -1;
  }

  addr_len = sizeof(local);
  if (getsockname(fd, (struct sockaddr*)&local, (socklen_t *) &addr_len) < 0) {
    log_err(errno, "getsockname() failed");
    close(fd);
    return -1;
  }

  if (addr_len != sizeof(local)) {
    log_err(0, "Wrong address length %d", addr_len);
    close(fd);
    return -1;
  }

  if (local.nl_family != AF_NETLINK) {
    log_err(0, "Wrong address family %d", local.nl_family);
    close(fd);
    return -1;
  }
  
  iov.iov_base = (void*)&req.n;
  iov.iov_len = req.n.nlmsg_len;

  msg.msg_name = (void*)&nladdr;
  msg.msg_namelen = sizeof(nladdr),
  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;
  msg.msg_control = NULL;
  msg.msg_controllen = 0;
  msg.msg_flags = 0;

  memset(&nladdr, 0, sizeof(nladdr));
  nladdr.nl_family = AF_NETLINK;
  nladdr.nl_pid = 0;
  nladdr.nl_groups = 0;

  req.n.nlmsg_seq = 0;
  req.n.nlmsg_flags |= NLM_F_ACK;

  status = sendmsg(fd, &msg, 0); 

  dev_set_flags(tuntap(this).devname, IFF_UP | IFF_RUNNING); 

  close(fd);
  this->addrs++;

  return 0;

#elif defined (__FreeBSD__) || defined (__APPLE__) || defined (__OpenBSD__) || defined (__NetBSD__)

  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); /* TODO dstaddr */

  memset(&areq, 0, sizeof(areq));

  /* Set up interface name */
  strncpy(areq.ifra_name, tuntap(this).devname, IFNAMSIZ);
  areq.ifra_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;

  /* For some reason FreeBSD uses ifra_broadcast for specifying dstaddr */
  ((struct sockaddr_in*) &areq.ifra_broadaddr)->sin_family = AF_INET;
  ((struct sockaddr_in*) &areq.ifra_broadaddr)->sin_len = sizeof(areq.ifra_broadaddr);
  ((struct sockaddr_in*) &areq.ifra_broadaddr)->sin_addr.s_addr = dstaddr->s_addr;

  /* Create a channel to the NET kernel. */
  if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    log_err(errno,
	    "socket() failed");
    return -1;
  }
  
  if (ioctl(fd, SIOCAIFADDR, (void *) &areq) < 0) {
    log_err(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);
  
  log_err(errno, "Setting multiple addresses not possible on Solaris");
  return -1;

#else
#error  "Unknown platform!"
#endif
  
}


int tun_setaddr(struct tun_t *this, struct in_addr *addr, struct in_addr *dstaddr, struct in_addr *netmask) {
  net_set_address(&tuntap(this), addr, dstaddr, netmask);
  
#if defined(__FreeBSD__) || defined (__APPLE__) || defined (__OpenBSD__) || defined (__NetBSD__)
  net_add_route(dstaddr, addr, netmask);
  this->routes = 1;
#endif

  return 0;
}

static int tuntap_interface(struct _net_interface *netif) {
#if defined(__linux__)
  struct ifreq ifr;

#elif defined(__FreeBSD__) || defined (__APPLE__) || defined (__OpenBSD__) || defined (__NetBSD__)
  char devname[IFNAMSIZ+5]; /* "/dev/" + ifname */
  int devnum;
  struct ifaliasreq areq;
  int fd;

#elif defined(__sun__)
  int if_fd, ppa = -1;
  static int ip_fd = 0;
  int muxid;
  struct ifreq ifr;

#else
#error  "Unknown platform!"
#endif


#if defined(__linux__)
  /* Open the actual tun device */
  if ((netif->fd  = open("/dev/net/tun", O_RDWR)) < 0) {
    log_err(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));
  ifr.ifr_flags = (options.usetap ? IFF_TAP : IFF_TUN) | IFF_NO_PI; /* Tun device, no packet info */
#if defined(IFF_ONE_QUEUE) && defined(SIOCSIFTXQLEN)
  ifr.ifr_flags |= IFF_ONE_QUEUE;
#endif

  if (options.tundev && *options.tundev && 
      strcmp(options.tundev, "tap") && strcmp(options.tundev, "tun"))
    strncpy(ifr.ifr_name, options.tundev, IFNAMSIZ);

  if (ioctl(netif->fd, TUNSETIFF, (void *) &ifr) < 0) {
    log_err(errno, "ioctl() failed");
    close(netif->fd);
    return -1;
  } 
  
#if defined(IFF_ONE_QUEUE) && defined(SIOCSIFTXQLEN)
  {
    struct ifreq nifr;
    int nfd;
    memset(&nifr, 0, sizeof(nifr));
    if ((nfd = socket (AF_INET, SOCK_DGRAM, 0)) >= 0) {
      strncpy(nifr.ifr_name, ifr.ifr_name, IFNAMSIZ);
      nifr.ifr_qlen = options.txqlen;

      if (ioctl(nfd, SIOCSIFTXQLEN, (void *) &nifr) >= 0) 
	log_info("TX queue length set to %d", options.txqlen);
      else 
	log_err(errno, "Cannot set tx queue length on %s", ifr.ifr_name);

      close (nfd);
    } else {
      log_err(errno, "Cannot open socket on %s", ifr.ifr_name);
    }
  }
#endif
  
  strncpy(netif->devname, ifr.ifr_name, IFNAMSIZ);
  netif->devname[IFNAMSIZ-1] = 0;
  
  ioctl(netif->fd, TUNSETNOCSUM, 1); /* Disable checksums */

  /* Get the MAC address of our tap interface */
  if (options.usetap) {
    int fd;
    netif->flags |= NET_ETHHDR;
    if ((fd = socket (AF_INET, SOCK_DGRAM, 0)) >= 0) {
      memset(&ifr, 0, sizeof(ifr));
      strncpy(ifr.ifr_name, netif->devname, IFNAMSIZ);
      if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
	log_err(errno, "ioctl(d=%d, request=%d) failed", fd, SIOCGIFHWADDR);
      }
      memcpy(netif->hwaddr, ifr.ifr_hwaddr.sa_data, PKT_ETH_ALEN);
      log_dbg("tap-mac: %s %.2X-%.2X-%.2X-%.2X-%.2X-%.2X", ifr.ifr_name,
	      netif->hwaddr[0],netif->hwaddr[1],netif->hwaddr[2],
	      netif->hwaddr[3],netif->hwaddr[4],netif->hwaddr[5]);
      close(fd);
    }
  }

  return 0;
  
#elif defined(__FreeBSD__) || defined (__APPLE__) || defined (__OpenBSD__) || defined (__NetBSD__)

  /* Find suitable device */
  for (devnum = 0; devnum < 255; devnum++) { /* TODO 255 */ 
    snprintf(devname, sizeof(devname), "/dev/tun%d", devnum);
    devname[sizeof(devname)] = 0;
    if ((netif->fd = open(devname, O_RDWR)) >= 0) break;
    if (errno != EBUSY) break;
  } 
  if (netif->fd < 0) {
    log_err(errno, "Can't find tunnel device");
    return -1;
  }

  snprintf(netif->devname, sizeof(netif->devname), "tun%d", devnum);
  netif->devname[sizeof(netif->devname)-1] = 0;

  /* The tun device we found might have "old" IP addresses allocated */
  /* We need to delete those. This problem is not present on Linux */

  memset(&areq, 0, sizeof(areq));

  /* Set up interface name */
  strncpy(areq.ifra_name, netif->devname, IFNAMSIZ);
  areq.ifra_name[IFNAMSIZ-1] = 0; /* Make sure to terminate */

  /* Create a channel to the NET kernel. */
  if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    log_err(errno,
	    "socket() failed");
    return -1;
  }
  
  /* Delete any IP addresses until SIOCDIFADDR fails */
  while (ioctl(fd, SIOCDIFADDR, (void *) &areq) != -1);

  close(fd);
  return 0;

#elif defined(__sun__)

  if( (ip_fd = open("/dev/udp", O_RDWR, 0)) < 0){
    log_err(errno, "Can't open /dev/udp");
    return -1;
  }
  
  if( (netif->fd = open("/dev/tun", O_RDWR, 0)) < 0){
    log_err(errno, "Can't open /dev/tun");
    return -1;
  }
  
  /* Assign a new PPA and get its unit number. */
  if( (ppa = ioctl(netif->fd, TUNNEWPPA, -1)) < 0){
    log_err(errno, "Can't assign new interface");
    return -1;
  }
  
  if( (if_fd = open("/dev/tun", O_RDWR, 0)) < 0){
    log_err(errno, "Can't open /dev/tun (2)");
    return -1;
  }
  if(ioctl(if_fd, I_PUSH, "ip") < 0){
    log_err(errno, "Can't push IP module");
    return -1;
  }
  
  /* Assign ppa according to the unit number returned by tun device */
  if(ioctl(if_fd, IF_UNITSEL, (char *)&ppa) < 0){
    log_err(errno, "Can't set PPA %d", ppa);
    return -1;
  }

  /* Link the two streams */
  if ((muxid = ioctl(ip_fd, I_LINK, if_fd)) < 0) {
    log_err(errno, "Can't link TUN device to IP");
    return -1;
  }

  close (if_fd);
  
  snprintf(netif->devname, sizeof(netif->devname), "tun%d", ppa);
  netif->devname[sizeof(netif->devname)-1] = 0;

  memset(&ifr, 0, sizeof(ifr));
  strcpy(ifr.ifr_name, netif->devname);
  ifr.ifr_ip_muxid = muxid;
  
  if (ioctl(ip_fd, SIOCSIFMUXID, &ifr) < 0) {
    ioctl(ip_fd, I_PUNLINK, muxid);
    log_err(errno, "Can't set multiplexor id");
    return -1;
  }
  
  /*  if (fcntl (fd, F_SETFL, O_NONBLOCK) < 0)
      msg (M_ERR, "Set file descriptor to non-blocking failed"); */

  return 0;

#else
#error  "Unknown platform!"
#endif

}

int tun_new(struct tun_t **ptun) {
  struct tun_t *tun;

  if (!(tun = *ptun = calloc(1, sizeof(struct tun_t)))) {
    log_err(errno, "calloc() failed");
    return EOF;
  }

  tuntap_interface(tun_nextif(tun));

  if (options.routeif) {
    tun_discover(tun);
  }

  return 0;
}

int tun_free(struct tun_t *tun) {

  if (tun->routes) {
#warning fix this
    /*XXX: todo! net_delete_route(&tuntap(tun)); */
  }

  tun_close(tun);

  /* TODO: For solaris we need to unlink streams */

  free(tun);
  return 0;
}

int tun_set_cb_ind(struct tun_t *this, 
		   int (*cb_ind) (struct tun_t *tun, void *pack, size_t len, int idx)) {
  this->cb_ind = cb_ind;
  return 0;
}

int tun_decaps(struct tun_t *this, int idx) {

#if defined(__linux__) || defined (__FreeBSD__) || defined (__APPLE__) || defined (__OpenBSD__) || defined (__NetBSD__)
  unsigned char buffer[PACKET_MAX];
  ssize_t status;

  if ((status = net_read(&tun(this, idx), buffer, sizeof(buffer))) <= 0) {
    log_err(errno, "read() failed");
    return -1;
  }

  if (this->debug)  
    log_dbg("tun_decaps(%d) %s",status,tun(tun,idx).devname);

  if (0) { /* if we wanted to do nat'ing, it would could be done here */
    struct in_addr a;
    struct pkt_iphdr_t *iph = (struct pkt_iphdr_t *)buffer;
    inet_aton("10.1.0.1", &a);
    iph->daddr = a.s_addr;
    chksum(iph);
  }
  
   if (this->cb_ind)
#if defined (__OpenBSD__)
    /* tun interface adds 4 bytes to front of packet under OpenBSD */
     return this->cb_ind(this, buffer+4, status, idx);
#else
     return this->cb_ind(this, buffer, status, idx);
#endif

  return 0;

#elif defined (__sun__)
  unsigned char buffer[PACKET_MAX];
  struct strbuf sbuf;
  int f = 0;
  
  sbuf.maxlen = PACKET_MAX;      
  sbuf.buf = buffer;
  if (getmsg(tun(this, idx).fd, NULL, &sbuf, &f) < 0) {
    log_err(errno, "getmsg() failed");
    return -1;
  }

  if (this->cb_ind)
    return this->cb_ind(this, &packet, sbuf.len);

  return 0;
  
#endif
}

static uint32_t dnatip[1024];
static uint16_t dnatport[1024];

int tun_encaps(struct tun_t *tun, void *pack, size_t len, int idx) {

  if (tun(tun, idx).flags & NET_ETHHDR) {

    struct pkt_ethhdr_t *ethh = (struct pkt_ethhdr_t *)pack;
    /*memcpy(ethh->src, tun->tap_hwaddr, PKT_ETH_ALEN); */

    /** XXX **/
    if (1) {
      ethh->src[0]=0x00;
      ethh->src[1]=0x17;
      ethh->src[2]=0x3f;
      ethh->src[3]=0x99;
      ethh->src[4]=0xf4;
      ethh->src[5]=0x46;

      ethh->dst[0]=0x00;
      ethh->dst[1]=0x14;
      ethh->dst[2]=0xBF;
      ethh->dst[3]=0xE2;
      ethh->dst[4]=0xC1;
      ethh->dst[5]=0x75;
    }

    log_dbg("writing to tun/tap src=%.2x:%.2x:%.2x:%.2x:%.2x:%.2x dst=%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
	    ethh->src[0],ethh->src[1],ethh->src[2],ethh->src[3],ethh->src[4],ethh->src[5],
	    ethh->dst[0],ethh->dst[1],ethh->dst[2],ethh->dst[3],ethh->dst[4],ethh->dst[5]);
  } else {
    pack += PKT_ETH_HLEN;
    len  -= PKT_ETH_HLEN;
  }

  if (tun->debug) 
    log_dbg("tun_encaps(%d) %s",len,tun(tun,idx).devname);

#if defined (__OpenBSD__)

  unsigned char buffer[PACKET_MAX+4];

  /* Can we user writev here to be more efficient??? */
  *((uint32_t *)(&buffer))=htonl(AF_INET);
  memcpy(&buffer[4], pack, PACKET_MAX);

  return net_write(&tun(tun, idx), buffer, len+4);

#elif defined(__linux__) || defined (__FreeBSD__) || defined (__APPLE__) || defined (__NetBSD__)

  if (0) { /* if we wanted to do nat'ing we could do it here */
    struct in_addr a;
    struct pkt_iphdr_t *iph = (struct pkt_iphdr_t *)pack;
    inet_aton("172.30.97.2", &a);
    iph->saddr = a.s_addr;
    chksum(iph);
  }

  return net_write(&tun(tun, idx), pack, len);

#elif defined (__sun__)

  struct strbuf sbuf;
  sbuf.len = len;      
  sbuf.buf = pack;
  return putmsg(tun(tun, idx).fd, NULL, &sbuf, 0);

#endif
}

int tun_runscript(struct tun_t *tun, char* script) {
  char saddr[TUN_ADDRSIZE];
  char smask[TUN_ADDRSIZE];
  char b[TUN_ADDRSIZE];
  struct in_addr net;
  int status;

  net.s_addr = tuntap(tun).address.s_addr & tuntap(tun).netmask.s_addr;

  if ((status = fork()) < 0) {
    log_err(errno, "fork() returned -1!");
    return 0;
  }
  
  if (status > 0) { /* Parent */
    return 0;
  }
  
/*
#ifdef HAVE_CLEARENV
  if (clearenv() != 0) {
    log_err(errno,
	    "clearenv() did not return 0!");
    exit(0);
  }
#endif
*/
  
  if (setenv("DEV", tuntap(tun).devname, 1) != 0) {
    log_err(errno, "setenv() did not return 0!");
    exit(0);
  }

  strncpy(saddr, inet_ntoa(tuntap(tun).address), sizeof(saddr));
  saddr[sizeof(saddr)-1] = 0;
  if (setenv("ADDR", saddr, 1 ) != 0) {
    log_err(errno, "setenv() did not return 0!");
    exit(0);
  }

  strncpy(smask, inet_ntoa(tuntap(tun).netmask), sizeof(smask));
  smask[sizeof(smask)-1] = 0;
  if (setenv("MASK", smask, 1) != 0) {
    log_err(errno, "setenv() did not return 0!");
    exit(0);
  }

  strncpy(b, inet_ntoa(net), sizeof(b));
  b[sizeof(b)-1] = 0;
  if (setenv("NET", b, 1 ) != 0) {
    log_err(errno, "setenv() did not return 0!");
    exit(0);
  }

  snprintf(b, sizeof(b), "%d", options.uamport);
  if (setenv("UAMPORT", b, 1 ) != 0) {
    log_err(errno, "setenv() did not return 0!");
    exit(0);
  }

  snprintf(b, sizeof(b), "%d", options.uamuiport);
  if (setenv("UAMUIPORT", b, 1 ) != 0) {
    log_err(errno, "setenv() did not return 0!");
    exit(0);
  }

  if (setenv("DHCPIF", options.dhcpif ? options.dhcpif : "", 1 ) != 0) {
    log_err(errno, "setenv() did not return 0!");
    exit(0);
  }

  if (execl(script, script, tuntap(tun).devname, saddr, smask, (char *) 0) != 0) {
    log_err(errno, "execl() did not return 0!");
    exit(0);
  }
  
  exit(0);
}


/* Currently unused 
int tun_addroute2(struct tun_t *this,
		  struct in_addr *dst,
		  struct in_addr *gateway,
		  struct in_addr *mask) {
  
  struct {
    struct nlmsghdr 	n;
    struct rtmsg 	r;
    char buf[TUN_NLBUFSIZE];
  } req;
  
  struct sockaddr_nl local;
  int addr_len;
  int fd;
  int status;
  struct sockaddr_nl nladdr;
  struct iovec iov;
  struct msghdr msg;

  memset(&req, 0, sizeof(req));
  req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
  req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
  req.n.nlmsg_type = RTM_NEWROUTE;
  req.r.rtm_family = AF_INET;
  req.r.rtm_table  = RT_TABLE_MAIN;
  req.r.rtm_protocol = RTPROT_BOOT;
  req.r.rtm_scope  = RT_SCOPE_UNIVERSE;
  req.r.rtm_type  = RTN_UNICAST;
  tun_nlattr(&req.n, sizeof(req), RTA_DST, dst, 4);
  tun_nlattr(&req.n, sizeof(req), RTA_GATEWAY, gateway, 4);
  
  if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) {
    sys_err(LOG_ERR, __FILE__, __LINE__, errno,
	    "socket() failed");
    return -1;
  }

  memset(&local, 0, sizeof(local));
  local.nl_family = AF_NETLINK;
  local.nl_groups = 0;
  
  if (bind(fd, (struct sockaddr*)&local, sizeof(local)) < 0) {
    sys_err(LOG_ERR, __FILE__, __LINE__, errno,
	    "bind() failed");
    close(fd);
    return -1;
  }

  addr_len = sizeof(local);
  if (getsockname(fd, (struct sockaddr*)&local, &addr_len) < 0) {
    sys_err(LOG_ERR, __FILE__, __LINE__, errno,
	    "getsockname() failed");
    close(fd);
    return -1;
  }

  if (addr_len != sizeof(local)) {
    sys_err(LOG_ERR, __FILE__, __LINE__, 0,
	    "Wrong address length %d", addr_len);
    close(fd);
    return -1;
  }

  if (local.nl_family != AF_NETLINK) {
    sys_err(LOG_ERR, __FILE__, __LINE__, 0,
	    "Wrong address family %d", local.nl_family);
    close(fd);
    return -1;
  }
  
  iov.iov_base = (void*)&req.n;
  iov.iov_len = req.n.nlmsg_len;

  msg.msg_name = (void*)&nladdr;
  msg.msg_namelen = sizeof(nladdr),
  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;
  msg.msg_control = NULL;
  msg.msg_controllen = 0;
  msg.msg_flags = 0;

  memset(&nladdr, 0, sizeof(nladdr));
  nladdr.nl_family = AF_NETLINK;
  nladdr.nl_pid = 0;
  nladdr.nl_groups = 0;

  req.n.nlmsg_seq = 0;
  req.n.nlmsg_flags |= NLM_F_ACK;

  status = sendmsg(fd, &msg, 0);  * TODO: Error check *
  close(fd);
  return 0;
}
*/


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>