src/fd-net-device/helper/tap-creator.cc
changeset 9271 85e753b6676c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/fd-net-device/helper/tap-creator.cc	Wed Mar 27 02:14:57 2013 +0100
@@ -0,0 +1,426 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2009 University of Washington
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <iostream>
+#include <iomanip>
+#include <sstream>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <net/if.h>
+#include <linux/if_tun.h>
+#include <net/route.h>
+#include <netinet/in.h>
+
+#include "creator-utils.h"
+
+#define TAP_MAGIC 95549
+
+//
+// Lots of the following helper code taken from corresponding functions in src/node.
+//
+#define ASCII_DOT (0x2e)
+#define ASCII_ZERO (0x30)
+#define ASCII_a (0x41)
+#define ASCII_z (0x5a)
+#define ASCII_A (0x61)
+#define ASCII_Z (0x7a)
+#define ASCII_COLON (0x3a)
+#define ASCII_ZERO (0x30)
+
+using namespace ns3;
+
+struct in6_ifreq {
+  struct in6_addr ifr6_addr;
+  uint32_t        ifr6_prefixlen;
+  int32_t         ifr6_ifindex;
+};
+
+char
+AsciiToLowCase (char c)
+{
+  if (c >= ASCII_a && c <= ASCII_z)
+    {
+      return c;
+    }
+  else if (c >= ASCII_A && c <= ASCII_Z)
+    {
+      return c + (ASCII_a - ASCII_A);
+    }
+  else
+    {
+      return c;
+    }
+}
+
+void
+AsciiToMac48 (const char *str, uint8_t addr[6])
+{
+  int i = 0;
+  while (*str != 0 && i < 6)
+    {
+      uint8_t byte = 0;
+      while (*str != ASCII_COLON && *str != 0)
+        {
+          byte <<= 4;
+          char low = AsciiToLowCase (*str);
+          if (low >= ASCII_a)
+            {
+              byte |= low - ASCII_a + 10;
+            }
+          else
+            {
+              byte |= low - ASCII_ZERO;
+            }
+          str++;
+        }
+      addr[i] = byte;
+      i++;
+      if (*str == 0)
+        {
+          break;
+        }
+      str++;
+    }
+}
+
+void
+SetIpv4 (const char *deviceName, const char *ip, const char *netmask)
+{
+  struct ifreq ifr;
+  struct sockaddr_in *sin;
+
+  int sock = socket(AF_INET, SOCK_DGRAM, 0);
+
+  //
+  // Set the IP address of the new interface/device.
+  //
+  memset(&ifr,  0, sizeof(struct ifreq));
+  strncpy(ifr.ifr_name, deviceName, IFNAMSIZ);
+
+  sin = (struct sockaddr_in*) &ifr.ifr_addr;
+  inet_pton(AF_INET, ip, &sin->sin_addr);
+  ifr.ifr_addr.sa_family = AF_INET;
+
+  ABORT_IF (ioctl (sock, SIOCSIFADDR, &ifr) == -1, 
+          "Could not set IP address", true);
+
+  LOG ("Set device IP address to " << ip);
+
+  //
+  // Set the net mask of the new interface/device
+  //
+  memset(&ifr,  0, sizeof(struct ifreq));
+  strncpy(ifr.ifr_name, deviceName, IFNAMSIZ);
+
+  sin = (struct sockaddr_in*) &ifr.ifr_netmask;
+  inet_pton(AF_INET, netmask, &sin->sin_addr);
+  ifr.ifr_addr.sa_family = AF_INET;
+
+  ABORT_IF (ioctl (sock, SIOCSIFNETMASK, &ifr) == -1, 
+          "Could not set net mask", true);
+
+  LOG ("Set device Net Mask to " << netmask);
+  close(sock);
+}
+
+void 
+SetIpv6 (const char* deviceName, const char *ip, int netprefix)
+{
+  struct ifreq ifr;
+  struct sockaddr_in6 sin;
+  struct in6_ifreq ifr6;
+
+  int sock = socket(AF_INET6, SOCK_DGRAM, 0);
+  memset(&ifr,  0, sizeof(struct ifreq));
+  strncpy(ifr.ifr_name, deviceName, IFNAMSIZ);
+
+  ABORT_IF (ioctl (sock, SIOGIFINDEX, &ifr) == -1, 
+          "Could not get interface index", true);
+
+  LOG ("Set device IP v6 address to " << ip);
+
+  memset(&sin, 0, sizeof(struct sockaddr_in6));
+  sin.sin6_family = AF_INET6;
+  inet_pton(AF_INET6, ip, (void *) &sin.sin6_addr);
+  
+  memset(&ifr6, 0, sizeof(in6_ifreq));
+  memcpy((char *) &ifr6.ifr6_addr, (char *) &sin.sin6_addr, sizeof(struct in6_addr));
+
+  ifr6.ifr6_ifindex = ifr.ifr_ifindex;
+  ifr6.ifr6_prefixlen = netprefix;
+
+  //
+  // Set the IP address of the new interface/device.
+  //
+  ABORT_IF (ioctl (sock, SIOCSIFADDR, &ifr6) == -1, 
+          "Could not set IP v6 address", true);
+
+  LOG ("Set device IP v6 address to " << ip);
+  close (sock);
+
+}
+
+void 
+SetMacAddress (int fd, const char* mac)
+{
+  struct ifreq ifr;
+  memset(&ifr,  0, sizeof(struct ifreq));
+
+  ifr.ifr_hwaddr.sa_family = 1; // this is ARPHRD_ETHER from if_arp.h
+  AsciiToMac48 (mac, (uint8_t*)ifr.ifr_hwaddr.sa_data);
+  ABORT_IF (ioctl (fd, SIOCSIFHWADDR, &ifr) == -1, "Could not set MAC address", true);
+  LOG ("Set device MAC address to " << mac);
+}
+
+void
+SetUp (char *deviceName)
+{
+  struct ifreq ifr;
+
+  int sock = socket(AF_INET, SOCK_DGRAM, 0);
+
+  memset(&ifr,  0, sizeof(struct ifreq));
+  strncpy(ifr.ifr_name, deviceName, IFNAMSIZ);
+
+  ABORT_IF (ioctl (sock, SIOCGIFFLAGS, &ifr) == -1, 
+          "Could not get flags for interface", true);
+  ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
+
+  ABORT_IF (ioctl (sock, SIOCSIFFLAGS, &ifr) == -1, 
+          "Could not bring interface up", true);
+
+  LOG ("Device is up");
+  close (sock);
+}
+
+int
+CreateTap (char *deviceName, const char *mac, const int ifftap, const int iffpi,
+        const char *ip4, const char *netmask, const char *ip6, const int netprefix)
+{
+
+  //
+  // Creation and management of Tap devices is done via the tun device
+  //
+  int fd = open ("/dev/net/tun", O_RDWR);
+  ABORT_IF (fd == -1, "Could not open /dev/net/tun", true);
+
+  //
+  // Set flags for device type and PI header.
+  //
+  struct ifreq ifr;
+
+  memset(&ifr,  0, sizeof(struct ifreq));
+
+  ifr.ifr_flags = (ifftap ? IFF_TAP : IFF_TUN);
+  if (!iffpi)
+    {
+       ifr.ifr_flags |= IFF_NO_PI;
+    }
+
+  //
+  // If device name is not specified, the kernel chooses one.
+  //
+  if (*deviceName) 
+    {
+       strncpy(ifr.ifr_name, deviceName, IFNAMSIZ);
+    }
+
+  
+  ABORT_IF (ioctl (fd, TUNSETIFF, (void *) &ifr) == -1, 
+          "Could not allocate tap device", true);
+
+  LOG ("Allocated TAP device " << deviceName);
+
+  //
+  // Set the hardware (MAC) address of the new device
+  //
+  if (ifftap)
+    {
+      SetMacAddress(fd, mac);
+    }
+
+  //
+  // Set the IP address and netmask of the new interface/device.
+  //
+  if (ip4)
+    {
+      SetIpv4(deviceName, ip4, netmask);
+    }
+
+  if (ip6)
+    {
+      SetIpv6(deviceName, ip6, netprefix);
+    }
+
+  //
+  // Bring the interface up.
+  //
+  SetUp(deviceName);
+
+  return fd;
+}
+
+
+int
+main (int argc, char *argv[])
+{
+  int c;
+  char *dev = NULL;
+  char *ip4 = NULL;
+  char *ip6 = NULL;
+  char *mac = NULL;
+  char *netmask = NULL;
+  char *path = NULL;
+  int tap = false;
+  int pi = false;
+  int prefix = NULL;
+
+  while ((c = getopt (argc, argv, "vd:i:m:n:I:P:thp:")) != -1)
+    {
+      switch (c)
+        {
+        case 'd':
+          dev = optarg;           // name of the new tap device
+          break;
+        case 'i':
+          ip4 = optarg;            // ip v4 address of the new device
+          break;
+        case 'I':
+          ip6 = optarg;            // ip v6 address of the new device
+          break;
+        case 'm':
+          mac = optarg;           // mac address of the new device
+          break;
+        case 'n':
+          netmask = optarg;       // ip v4 net mask for the new device
+          break;
+        case 'P':
+          prefix = atoi(optarg);         // ip v6 prefix for the new device
+          break;
+        case 't':
+          tap = true;             // mode for the device (TAP or TUN)
+          break;
+        case 'h':
+          pi = true;              // set the IFF_NO_PI flag
+          break;
+        case 'p':
+          path = optarg;          // path back to the tap bridge
+          break;
+        case 'v':
+          gVerbose = true;
+          break;
+        }
+    }
+
+  //
+  // We have got to be able to coordinate the name of the tap device we are
+  // going to create and or open with the device that an external Linux host
+  // will use.  If this name is provided we use it.  If not we let the system
+  // create the device for us.  This name is given in dev
+  //
+  LOG ("Provided Device Name is \"" << dev << "\"");
+
+  //
+  // We have got to be able to assign an IP address to the tap device we are
+  // allocating.  This address is allocated in the simulation and assigned to
+  // the tap bridge.  This address is given in ip.
+  //
+  ABORT_IF (ip4 == NULL && ip6 == NULL, "IP Address is a required argument", 0);
+  if (ip4)
+    {
+      ABORT_IF (netmask == NULL, "Net mask is a required argument", 0);
+      LOG ("Provided IP v4 Address is \"" << ip4 << "\"");
+      LOG ("Provided IP v4 Net Mask is \"" << netmask << "\"");
+    }
+  if (ip6)
+    {
+      ABORT_IF (prefix == NULL, "Prefix is a required argument", 0);
+      LOG ("Provided IP v6 Address is \"" << ip6 << "\"");
+      LOG ("Provided IP v6 Prefix is \"" << prefix << "\"");
+    }
+
+  //
+  // We have got to be able to assign a Mac address to the tap device we are
+  // allocating.  This address is allocated in the simulation and assigned to
+  // the bridged device.  This allows packets addressed to the bridged device
+  // to appear in the Linux host as if they were received there.
+  //
+  ABORT_IF (mac == NULL, "MAC Address is a required argument", 0);
+  LOG ("Provided MAC Address is \"" << mac << "\"");
+
+  //
+  // We have got to know whether or not to create the TAP.
+  //
+  if (tap)
+    {
+      LOG ("Provided device Mode is TAP");
+    }
+  else
+    {
+      LOG ("Provided device Mode is TUN");
+    }
+
+  //
+  // IFF_NO_PI flag.
+  //
+  if (pi)
+    {
+      LOG ("IFF_NO_PI flag set. Packet Information will be present in the traffic");
+    }
+
+  //
+  // This program is spawned by a tap bridge running in a simulation.  It
+  // wants to create a socket as described below.  We are going to do the
+  // work here since we're running suid root.  Once we create the socket,
+  // we have to send it back to the tap bridge.  We do that over a Unix
+  // (local interprocess) socket.  The tap bridge created a socket to
+  // listen for our response on, and it is expected to have encoded the address
+  // information as a string and to have passed that string as an argument to
+  // us.  We see it here as the "path" string.  We can't do anything useful
+  // unless we have that string.
+  //
+  ABORT_IF (path == NULL, "path is a required argument", 0);
+  LOG ("Provided path is \"" << path << "\"");
+
+  //
+  // The whole reason for all of the hoops we went through to call out to this
+  // program will pay off here.  We created this program to run as suid root
+  // in order to keep the main simulation program from having to be run with
+  // root privileges.  We need root privileges to be able to futz with the
+  // Tap device underlying all of this.  So all of these hoops are to allow
+  // us to execute the following code:
+  //
+  LOG ("Creating Tap");
+  int sock = CreateTap (dev, mac, tap, pi, ip4, netmask, ip6, prefix);
+  ABORT_IF (sock == -1, "main(): Unable to create tap socket", 1);
+
+  //
+  // Send the socket back to the tap net device so it can go about its business
+  //
+  SendSocket (path, sock, TAP_MAGIC);
+
+  return 0;
+}