diff --git a/src/devices/emutap/host-tap-net-device.cc b/src/devices/emutap/host-tap-net-device.cc new file mode 100644 index 000000000..c83000b1b --- /dev/null +++ b/src/devices/emutap/host-tap-net-device.cc @@ -0,0 +1,194 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2008 INRIA + * + * 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 + * + * Author: Mathieu Lacage + */ +#include "host-tap-net-device.h" +#include "ns3/node.h" +#include "ns3/tap-channel.h" +#include "ns3/log.h" + +NS_LOG_COMPONENT_DEFINE ("HostTapNetDevice"); + +namespace ns3 { + +TypeId +HostTapNetDevice::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::HostTapNetDevice") + .SetParent () + .AddConstructor () + ; + return tid; +} + +HostTapNetDevice::HostTapNetDevice () + : m_node (0), + m_mtu (0xffff), + m_name (""), + m_ifIndex (0) +{ + NS_LOG_FUNCTION (this); +} + +void +HostTapNetDevice::SetChannel (Ptr channel) +{ + m_channel = channel; + m_channel->SetHostDevice (this); +} + +void +HostTapNetDevice::SetAddress (Mac48Address address) +{ + m_address = address; +} + +Mac48Address +HostTapNetDevice::GetMacAddress (void) const +{ + return m_address; +} + +void +HostTapNetDevice::SetName(const std::string name) +{ + m_name = name; +} +std::string +HostTapNetDevice::GetName(void) const +{ + return m_name; +} +void +HostTapNetDevice::SetIfIndex(const uint32_t index) +{ + m_ifIndex = index; +} +uint32_t +HostTapNetDevice::GetIfIndex(void) const +{ + return m_ifIndex; +} +Ptr +HostTapNetDevice::GetChannel (void) const +{ + return m_channel; +} +Address +HostTapNetDevice::GetAddress (void) const +{ + return m_address; +} +bool +HostTapNetDevice::SetMtu (const uint16_t mtu) +{ + m_mtu = mtu; + return true; +} +uint16_t +HostTapNetDevice::GetMtu (void) const +{ + return m_mtu; +} +bool +HostTapNetDevice::IsLinkUp (void) const +{ + return true; +} +void +HostTapNetDevice::SetLinkChangeCallback (Callback callback) +{} +bool +HostTapNetDevice::IsBroadcast (void) const +{ + return true; +} +Address +HostTapNetDevice::GetBroadcast (void) const +{ + return Mac48Address ("ff:ff:ff:ff:ff:ff"); +} +bool +HostTapNetDevice::IsMulticast (void) const +{ + return true; +} +Address +HostTapNetDevice::GetMulticast (void) const +{ + return Mac48Address::GetMulticastPrefix (); +} +Address +HostTapNetDevice::MakeMulticastAddress (Ipv4Address multicastGroup) const +{ + return Mac48Address::GetMulticast (multicastGroup); +} +bool +HostTapNetDevice::IsPointToPoint (void) const +{ + return false; +} +bool +HostTapNetDevice::Send(Ptr packet, const Address& dest, uint16_t protocolNumber) +{ + return false; +} +bool +HostTapNetDevice::SendFrom(Ptr packet, const Address& source, const Address& dest, uint16_t protocolNumber) +{ + return false; +} +Ptr +HostTapNetDevice::GetNode (void) const +{ + return m_node; +} +void +HostTapNetDevice::SetNode (Ptr node) +{ + m_node = node; +} +bool +HostTapNetDevice::NeedsArp (void) const +{ + return false; +} +void +HostTapNetDevice::SetReceiveCallback (NetDevice::ReceiveCallback cb) +{} + +void +HostTapNetDevice::DoDispose (void) +{ + NS_LOG_FUNCTION (this); + m_node = 0; + m_channel = 0; + NetDevice::DoDispose (); +} + +void +HostTapNetDevice::SetPromiscReceiveCallback (PromiscReceiveCallback cb) +{} + +bool +HostTapNetDevice::SupportsSendFrom (void) const +{ + return false; +} + +} // namespace ns3 diff --git a/src/devices/emutap/host-tap-net-device.h b/src/devices/emutap/host-tap-net-device.h new file mode 100644 index 000000000..91b4b7ee2 --- /dev/null +++ b/src/devices/emutap/host-tap-net-device.h @@ -0,0 +1,90 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2008 INRIA + * + * 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 + * + * Author: Mathieu Lacage + */ +#ifndef HOST_TAP_NET_DEVICE_H +#define HOST_TAP_NET_DEVICE_H + +#include "ns3/net-device.h" +#include "ns3/mac48-address.h" +#include "ns3/traced-callback.h" +#include +#include + +namespace ns3 { + +class Node; +class TapChannel; + + +/** + * \ingroup netdevice + * + * \brief a NetDevice to get packets to and from a host tap device. + */ +class HostTapNetDevice : public NetDevice +{ +public: + static TypeId GetTypeId (void); + HostTapNetDevice (); + + void SetAddress (Mac48Address address); + void SetChannel (Ptr channel); + + Mac48Address GetMacAddress (void) const; + + // inherited from NetDevice base class. + virtual void SetName(const std::string name); + virtual std::string GetName(void) const; + virtual void SetIfIndex(const uint32_t index); + virtual uint32_t GetIfIndex(void) const; + virtual Ptr GetChannel (void) const; + virtual Address GetAddress (void) const; + virtual bool SetMtu (const uint16_t mtu); + virtual uint16_t GetMtu (void) const; + virtual bool IsLinkUp (void) const; + virtual void SetLinkChangeCallback (Callback callback); + virtual bool IsBroadcast (void) const; + virtual Address GetBroadcast (void) const; + virtual bool IsMulticast (void) const; + virtual Address GetMulticast (void) const; + virtual Address MakeMulticastAddress (Ipv4Address multicastGroup) const; + virtual bool IsPointToPoint (void) const; + virtual bool Send(Ptr packet, const Address& dest, uint16_t protocolNumber); + virtual bool SendFrom(Ptr packet, const Address& source, const Address& dest, uint16_t protocolNumber); + virtual Ptr GetNode (void) const; + virtual void SetNode (Ptr node); + virtual bool NeedsArp (void) const; + virtual void SetReceiveCallback (NetDevice::ReceiveCallback cb); + virtual void SetPromiscReceiveCallback (PromiscReceiveCallback cb); + virtual bool SupportsSendFrom (void) const; + +protected: + virtual void DoDispose (void); +private: + Ptr m_node; + uint16_t m_mtu; + std::string m_name; + uint32_t m_ifIndex; + Mac48Address m_address; + Ptr m_channel; +}; + +} // namespace ns3 + +#endif /* HOST_TAP_NET_DEVICE_H */ diff --git a/src/devices/emutap/tap-channel.cc b/src/devices/emutap/tap-channel.cc new file mode 100644 index 000000000..d07944fb1 --- /dev/null +++ b/src/devices/emutap/tap-channel.cc @@ -0,0 +1,61 @@ +#include "tap-channel.h" +#include "host-tap-net-device.h" +#include "tap-net-device.h" + +namespace ns3 { + + +TapChannel::TapChannel () + : m_hostDevice (0), + m_device (0) +{} + +void +TapChannel::DoDispose (void) +{ + m_device = 0; + m_hostDevice = 0; + Channel::DoDispose (); +} + +void +TapChannel::SetDevice (Ptr device) +{ + m_device = device; +} + +void +TapChannel::SetHostDevice (Ptr device) +{ + m_hostDevice = device; +} + +Ptr +TapChannel::GetHostDevice (void) const +{ + return m_hostDevice; +} + +uint32_t +TapChannel::GetNDevices (void) const +{ + return 2; +} +Ptr +TapChannel::GetDevice (uint32_t i) const +{ + if (i == 0) + { + return m_device; + } + else if (i == 1) + { + return m_hostDevice; + } + else + { + return 0; + } +} + +} // namespace ns3 diff --git a/src/devices/emutap/tap-channel.h b/src/devices/emutap/tap-channel.h new file mode 100644 index 000000000..fcf529c6c --- /dev/null +++ b/src/devices/emutap/tap-channel.h @@ -0,0 +1,36 @@ +#ifndef TAP_CHANNEL_H +#define TAP_CHANNEL_H + +#include "ns3/channel.h" + +namespace ns3 { + +class HostTapNetDevice; +class TapNetDevice; +class NetDevice; + + +class TapChannel : public Channel +{ +public: + TapChannel (); + + void SetHostDevice (Ptr device); + void SetDevice (Ptr device); + + Ptr GetHostDevice (void) const; + + // overriden from Channel base class + virtual uint32_t GetNDevices (void) const; + virtual Ptr GetDevice (uint32_t i) const; + +private: + virtual void DoDispose (void); + + Ptr m_hostDevice; + Ptr m_device; +}; + +} // namespace ns3 + +#endif /* TAP_CHANNEL_H */ diff --git a/src/devices/emutap/tap-manager-client.cc b/src/devices/emutap/tap-manager-client.cc new file mode 100644 index 000000000..24c624faf --- /dev/null +++ b/src/devices/emutap/tap-manager-client.cc @@ -0,0 +1,180 @@ +#include "tap-manager-client.h" +#include "ns3/log.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +NS_LOG_COMPONENT_DEFINE("TapManagerClient"); + +#define TAP_MANAGER "ns3-tap-manager" + +namespace ns3 { + +static std::string +EncodeAsString (struct sockaddr_un un, int len) +{ + uint8_t *buffer = (uint8_t *)&un; + std::ostringstream oss; + oss.setf (std::ios::hex, std::ios::basefield); + oss.fill('0'); + for (uint8_t i = 0; i < len; i++) + { + oss << ":" << std::setw (2) << (uint32_t)buffer[i]; + } + return oss.str (); +} + +bool +TapManagerClient::Exists (std::string filename) const +{ + struct stat st; + int retval = ::stat (filename.c_str (), &st); + return retval == 0; +} + +std::string +TapManagerClient::FindManager (void) const +{ + std::list locations; + locations.push_back ("./src/devices/tap"); + locations.push_back ("./build/debug/src/devices/tap"); + locations.push_back ("./build/optimized/src/devices/tap"); + for (std::list::const_iterator i = locations.begin (); i != locations.end (); ++i) + { + if (Exists (*i + "/" + TAP_MANAGER)) + { + return *i + "/" + TAP_MANAGER; + } + } + NS_FATAL_ERROR ("Could not find manager"); + return ""; // quiet compiler +} + +int +TapManagerClient::AllocateTap (Mac48Address host, Ipv4Address ad, Ipv4Mask mask, Ipv4Address gateway) +{ + NS_LOG_FUNCTION (host << ad << mask << gateway); + // create a socket to get information back from the tap manager. + int sock = socket (PF_UNIX, SOCK_DGRAM, 0); + if (sock == -1) + { + NS_FATAL_ERROR ("Socket creation, errno=" << strerror (errno)); + } + + struct sockaddr_un un; + memset (&un, 0, sizeof (un)); + un.sun_family = AF_UNIX; + int status = bind (sock, (struct sockaddr*)&un, sizeof (sa_family_t)); // let the kernel allocate an endpoint for us. + if (status == -1) + { + NS_FATAL_ERROR ("Could not bind: errno=" << strerror (errno)); + } + socklen_t len = sizeof (un); + status = getsockname (sock, (struct sockaddr*)&un, &len); + if (status == -1) + { + NS_FATAL_ERROR ("Could not get socket address: errno=" << strerror (errno)); + } + NS_LOG_DEBUG ("Allocated enpoint=" << EncodeAsString (un, len) << ", len=" << len); + + pid_t pid = ::fork (); + if (pid == 0) + { + // child. + NS_LOG_DEBUG ("Child"); + + std::ostringstream oss; + oss << "--path=" << EncodeAsString (un, len); + std::string pathArg = oss.str (); + oss.str (""); + oss << "--mac-addr=" << host; + std::string hostArg = oss.str (); + oss.str (""); + oss << "--ip-addr=" << ad; + std::string ipArg = oss.str (); + oss.str (""); + oss << "--ip-gw=" << gateway; + std::string ipGw = oss.str (); + oss.str (""); + oss << "--ip-netmask=" << mask; + std::string ipMask = oss.str (); + oss.str (""); + status = ::execl (FindManager ().c_str (), + TAP_MANAGER, + pathArg.c_str (), + hostArg.c_str (), + ipArg.c_str (), + ipGw.c_str (), + ipMask.c_str (), + (char *)NULL); + if (status == -1) + { + NS_LOG_ERROR ("Cannot execl tap-manager, errno=" << ::strerror (errno)); + } + ::_exit (-1); + } + else + { + // parent + NS_LOG_DEBUG ("Parent"); + int st; + pid_t waited = waitpid (pid, &st, 0); + if (waited == -1) + { + NS_FATAL_ERROR ("Cannot wait for tap-manager, errno=" << strerror (errno)); + } + NS_ASSERT (pid == waited); + if (!WIFEXITED (st)) + { + // tap manager did not complete successfully + NS_FATAL_ERROR ("tap-manager did not exit correctly"); + } + else if (WEXITSTATUS (st) != 0) + { + NS_FATAL_ERROR ("tap-manager did not complete successfully, err=" << WEXITSTATUS (st)); + } + // the tap fd should be available on our unix socket now. + size_t msg_size = sizeof(int); + char control[CMSG_SPACE(msg_size)]; + struct cmsghdr *cmsg; + uint8_t buffer; + struct iovec iov; + iov.iov_base = &buffer; + iov.iov_len = 1; + struct msghdr msg; + msg.msg_name = 0; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = control; + msg.msg_controllen = sizeof (control); + msg.msg_flags = 0; + ssize_t bytesRead = recvmsg (sock, &msg, 0); + if (bytesRead != 1) + { + NS_FATAL_ERROR ("Did not get byte from tap-manager"); + } + NS_LOG_ERROR ("read bytes=" << bytesRead); + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) + { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_RIGHTS) + { + int *fd = (int*)CMSG_DATA (cmsg); + NS_LOG_ERROR ("got tap fd=" << *fd); + return *fd; + } + } + NS_FATAL_ERROR ("Did not get SCM_RIGHTS from tap-manager"); + } + return -1; +} + +} // namespace ns3 diff --git a/src/devices/emutap/tap-manager-client.h b/src/devices/emutap/tap-manager-client.h new file mode 100644 index 000000000..eb5ce96d8 --- /dev/null +++ b/src/devices/emutap/tap-manager-client.h @@ -0,0 +1,21 @@ +#ifndef TAP_MANAGER_CLIENT_H +#define TAP_MANAGER_CLIENT_H + +#include "ns3/mac48-address.h" +#include "ns3/ipv4-address.h" +#include + +namespace ns3 { + +class TapManagerClient +{ +public: + int AllocateTap (Mac48Address host, Ipv4Address ad, Ipv4Mask mask, Ipv4Address gateway); +private: + std::string FindManager (void) const; + bool Exists (std::string filename) const; +}; + +} // namespace ns3 + +#endif /* TAP_MANAGER_CLIENT_H */ diff --git a/src/devices/emutap/tap-manager.cc b/src/devices/emutap/tap-manager.cc new file mode 100644 index 000000000..a68ec9fc7 --- /dev/null +++ b/src/devices/emutap/tap-manager.cc @@ -0,0 +1,339 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define noENABLE_LOG + +#define EXIT_ERROR(x, err) \ + std::cout << __FILE__ << ":" << __LINE__ << ": Unrecoverable Error: " << x; \ + std::cout << " errno=" << strerror (errno) << std::endl; \ + exit (err) +#ifdef ENABLE_LOG +#define LOG(x) \ + std::cout << x << std::endl; +#else +#define LOG(x) +#endif + + +#define CHECK_ARG(el,var) \ + { \ + char start[] = "--" el "="; \ + if (strncmp (*argv, start, strlen (start)) == 0) \ + { \ + var = *argv + strlen (start); \ + LOG ("--" << el << "=" << var); \ + } \ + } + +#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) +static 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; + } +} +static uint32_t +AsciiToIpv4 (const char *address) +{ + uint32_t host = 0; + while (true) { + uint8_t byte = 0; + while (*address != ASCII_DOT && + *address != 0) { + byte *= 10; + byte += *address - ASCII_ZERO; + address++; + } + host <<= 8; + host |= byte; + if (*address == 0) { + break; + } + address++; + } + return host; +} + +static 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++; + } +} + +static void +SetInetAddress (sockaddr *ad, uint32_t networkOrder) +{ + struct sockaddr_in *sin = (struct sockaddr_in*)ad; + sin->sin_family = AF_INET; + sin->sin_port = 0; // unused + sin->sin_addr.s_addr = htonl (networkOrder); +} + + +static int +CreateTap (const char *mac_addr, const char *ip, + const char *gw, const char *netmask) +{ + // opening the tun device usually requires root privs + int tap = open ("/dev/net/tun", O_RDWR); + if (tap == -1) + { + EXIT_ERROR ("Could not open /dev/net/tun", 1); + } + // now, crate a tap device. + struct ifreq ifr; + // make sure that the tap device will not send us the tun_pi header. + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + ifr.ifr_name[0] = 0; // allow the kernel to pick a device name. + int status = ioctl (tap, TUNSETIFF, (void *) &ifr); + if (status == -1) + { + EXIT_ERROR ("Could not allocate a tap device", 2); + } + std::string tapDeviceName = (char *)ifr.ifr_name; + LOG ("Allocated TAP device=" << tapDeviceName); + + // set its hardware address to something we know will be unique within the simulation + ifr.ifr_hwaddr.sa_family = 1; // this is ARPHRD_ETHER from if_arp.h + AsciiToMac48 (mac_addr, (uint8_t*)ifr.ifr_hwaddr.sa_data); + status = ioctl (tap, SIOCSIFHWADDR, &ifr); + if (status == -1) + { + EXIT_ERROR ("Could not set hardware address=" << mac_addr << " for=" << (char *)ifr.ifr_name, 3); + } + LOG ("device=" << (char *)ifr.ifr_name << " addr=" << mac_addr); + + + // The ip address must be set using an AF_INET socket. + int fd = socket (AF_INET, SOCK_DGRAM, 0); + + // set interface up. + status = ioctl (fd, SIOCGIFFLAGS, &ifr); + if (status == -1) + { + EXIT_ERROR ("Could not get flags for interface=" << (char *)ifr.ifr_name, 4); + } + ifr.ifr_flags |= IFF_UP | IFF_RUNNING; + status = ioctl (fd, SIOCSIFFLAGS, &ifr); + if (status == -1) + { + EXIT_ERROR ("Could not bring interface " << (char *)ifr.ifr_name << " up", 5); + } + LOG ("device=" << (char *)ifr.ifr_name << " is up"); + + + // set its ip address. + SetInetAddress (&ifr.ifr_addr, AsciiToIpv4 (ip)); + status = ioctl (fd, SIOCSIFADDR, &ifr); + if (status == -1) + { + EXIT_ERROR ("Could not set ip address=" << ip << " for=" << (char *)ifr.ifr_name, 6); + } + LOG ("device=" << (char *)ifr.ifr_name << " addr=" << ip); + + // set its ip mask to be /32 + SetInetAddress (&ifr.ifr_netmask, 0xffffffff); + status = ioctl (fd, SIOCSIFNETMASK, &ifr); + if (status == -1) + { + EXIT_ERROR ("Could not set ip mask=" << netmask << " for=" << (char *)ifr.ifr_name, 7); + } + LOG ("device=" << (char *)ifr.ifr_name << " mask=" << netmask); + + // add routing entry for gateway. + struct rtentry rt; + SetInetAddress (&rt.rt_dst, AsciiToIpv4 (gw)); + SetInetAddress (&rt.rt_genmask, 0xffffffff); + rt.rt_flags = RTF_UP; + rt.rt_metric = 2; + rt.rt_dev = (char*)tapDeviceName.c_str (); + status = ioctl (fd, SIOCADDRT, &rt); + if (status == -1) + { + EXIT_ERROR ("Could not add routing table entry", 8); + } + LOG ("added routing table entry for gw."); + + // add routing entry for subnet through gateway. + uint32_t network = AsciiToIpv4 (ip) & AsciiToIpv4 (netmask); + SetInetAddress (&rt.rt_dst, network); + SetInetAddress (&rt.rt_gateway, AsciiToIpv4 (gw)); + SetInetAddress (&rt.rt_genmask, AsciiToIpv4 (netmask)); + rt.rt_flags = RTF_UP | RTF_GATEWAY; + rt.rt_metric = 2; + rt.rt_dev = (char*)tapDeviceName.c_str (); + status = ioctl (fd, SIOCADDRT, &rt); + if (status == -1) + { + EXIT_ERROR ("Could not add routing table entry", 9); + } + LOG ("added routing table entry for subnet."); + + return tap; +} + +static struct sockaddr_un +DecodeFromString (const char *path, socklen_t *len) +{ + sockaddr_un un; + uint8_t *buffer = (uint8_t *)&un; + std::istringstream iss; + iss.str (path); + uint8_t n = 0; + while (!iss.bad () && !iss.eof () && !iss.fail ()) + { + char c; + iss.read (&c, 1); + uint32_t tmp; + iss >> std::hex >> tmp; + //LOG (std::hex << tmp); + buffer[n] = tmp; + n++; + } + *len = n; + return un; +} + +static void +SendFd (const char *path, int fd) +{ + // send back configuration to caller. + int sock = socket (PF_UNIX, SOCK_DGRAM, 0); + if (sock == -1) + { + EXIT_ERROR ("Socket creation", 10); + } + LOG ("Socket Created"); + + socklen_t local_len; + struct sockaddr_un local = DecodeFromString (path, &local_len); + LOG ("len=" << local_len); + int status = connect (sock, (struct sockaddr*)&local, local_len); + if (status == -1) + { + EXIT_ERROR ("Could not connect to caller", 11); + } + LOG ("Socket Connected"); + + // we send a single byte whose content is meaningless. + // we also return as ancillary data the tap file descriptor + struct cmsghdr *cmsg; + size_t msg_size = sizeof(int); + char control[CMSG_SPACE(msg_size)]; + struct iovec iov; + struct msghdr msg; + char buffer = 0; + iov.iov_base = &buffer; + iov.iov_len = 1; + msg.msg_name = 0; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = control; + msg.msg_controllen = sizeof (control); + msg.msg_flags = 0; + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(msg_size); + msg.msg_controllen = cmsg->cmsg_len; + + int *fdptr = (int*) (CMSG_DATA(cmsg)); + *fdptr = fd; + + ssize_t len = sendmsg(sock, &msg, 0); + if (len == -1) + { + EXIT_ERROR ("Could not send SCM_RIGHTS", 12); + } + LOG ("Sent SCM_RIGHTS"); +} + + + + +int main (int argc, char *argv[]) +{ + char *path = 0; + char *mac_addr = 0; + char *ip_addr = 0; + char *ip_gw = 0; + char *ip_netmask = 0; + char *stop = 0; + argv++; + argc--; + while (argc > 0) + { + CHECK_ARG("path", path); + CHECK_ARG("mac-addr", mac_addr); + CHECK_ARG("ip-addr", ip_addr); + CHECK_ARG("ip-gw", ip_gw); + CHECK_ARG("ip-netmask", ip_netmask); + CHECK_ARG("stop", stop); + argv++; + argc--; + } + + int tap = CreateTap (mac_addr, ip_addr, ip_gw, ip_netmask); + + if (stop) + { + while (1) {} + } + + SendFd (path, tap); + + return 0; +} diff --git a/src/devices/emutap/tap-net-device.cc b/src/devices/emutap/tap-net-device.cc new file mode 100644 index 000000000..85f189dc5 --- /dev/null +++ b/src/devices/emutap/tap-net-device.cc @@ -0,0 +1,323 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2008 INRIA + * + * 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 + * + * Author: Mathieu Lacage + */ +#include "tap-net-device.h" +#include "tap-manager-client.h" +#include "ns3/node.h" +#include "ns3/channel.h" +#include "ns3/packet.h" +#include "ns3/log.h" +#include "ns3/system-thread.h" +#include "ns3/realtime-simulator-impl.h" +#include "ns3/make-event.h" +#include "ns3/simulator.h" +#include "ns3/ethernet-header.h" +#include "ns3/trace-source-accessor.h" +#include "host-tap-net-device.h" +#include "tap-channel.h" +#include +#include + +NS_LOG_COMPONENT_DEFINE ("TapNetDevice"); + +namespace ns3 { + +TypeId +TapNetDevice::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::TapNetDevice") + .SetParent () + .AddConstructor () + .AddTraceSource ("Rx", "A packet has been received", + MakeTraceSourceAccessor (&TapNetDevice::m_rxTrace)) + .AddTraceSource ("Tx", "A packet has been sent", + MakeTraceSourceAccessor (&TapNetDevice::m_txTrace)) + .AddTraceSource ("Drop", "A packet has been dropped", + MakeTraceSourceAccessor (&TapNetDevice::m_dropTrace)) + ; + return tid; +} + +TapNetDevice::TapNetDevice () + : m_node (0), + m_mtu (0xffff), + m_name (""), + m_ifIndex (0), + m_tap (-1) +{ + NS_LOG_FUNCTION (this); +} + +void +TapNetDevice::SetChannel (Ptr channel) +{ + m_channel = channel; + m_channel->SetDevice (this); +} + +void +TapNetDevice::SetupHost (Ipv4Address ad, Ipv4Mask mask, Ipv4Address gateway) +{ + NS_LOG_FUNCTION (this << ad << mask << gateway); + NS_ASSERT (m_tap == -1); + + Mac48Address hostMacAddress = m_channel->GetHostDevice ()->GetMacAddress (); + + TapManagerClient manager; + m_tap = manager.AllocateTap (hostMacAddress, ad, mask, gateway); + + m_thread = Create (MakeCallback (&TapNetDevice::ReadThread, this)); + m_thread->Start (); +} + + +void +TapNetDevice::ReadThread (void) +{ + NS_LOG_FUNCTION (this); + + while (1) + { + uint8_t *buffer = (uint8_t *)malloc (0xffff); + ssize_t bytesRead = read (m_tap, buffer, 0xffff); + if (bytesRead == -1) + { + if (errno == EBADF || errno == EINTR) + { + // the device was closed from under us by ::DoDispose + return; + } + NS_FATAL_ERROR ("Error reading from tap device: errno=" << strerror (errno)); + } + // Note: we purposedly don't use a smart pointer to manage this packet + // because the want to hand over ownership of this packet to the ForwardUp + // method. + DynamicCast (Simulator::GetImplementation ())-> + ScheduleRealtimeNow (MakeEvent (&TapNetDevice::ForwardUp, this, buffer, (uint32_t)bytesRead)); + } +} + +void +TapNetDevice::ForwardUp (uint8_t *buffer, uint32_t size) +{ + NS_LOG_FUNCTION (this << buffer << size); + + // swallow packet reference in smart pointer. + Ptr packet = Create (buffer, size); + free (buffer); + Ptr copy = packet->Copy (); + + EthernetHeader header = EthernetHeader (false); + packet->RemoveHeader (header); + + uint16_t protocol = header.GetLengthType (); + Mac48Address to = header.GetDestination (); + Mac48Address from = header.GetSource (); + + NetDevice::PacketType packetType; + if (to == m_address) + { + packetType = NetDevice::PACKET_HOST; + } + else if (to.IsBroadcast ()) + { + packetType = NetDevice::PACKET_HOST; + } + else if (to.IsMulticast ()) + { + packetType = NetDevice::PACKET_MULTICAST; + } + else + { + packetType = NetDevice::PACKET_OTHERHOST; + } + m_rxTrace (copy, from, to); + if (packetType != NetDevice::PACKET_OTHERHOST) + { + m_rxCallback (this, packet, protocol, from); + } + if (!m_promiscCallback.IsNull ()) + { + m_promiscCallback (this, packet, protocol, from, to, packetType); + } +} + +void +TapNetDevice::SetAddress (Mac48Address address) +{ + m_address = address; +} + +void +TapNetDevice::SetName(const std::string name) +{ + m_name = name; +} +std::string +TapNetDevice::GetName(void) const +{ + return m_name; +} +void +TapNetDevice::SetIfIndex(const uint32_t index) +{ + m_ifIndex = index; +} +uint32_t +TapNetDevice::GetIfIndex(void) const +{ + return m_ifIndex; +} +Ptr +TapNetDevice::GetChannel (void) const +{ + return m_channel; +} +Address +TapNetDevice::GetAddress (void) const +{ + return m_address; +} +bool +TapNetDevice::SetMtu (const uint16_t mtu) +{ + m_mtu = mtu; + return true; +} +uint16_t +TapNetDevice::GetMtu (void) const +{ + return m_mtu; +} +bool +TapNetDevice::IsLinkUp (void) const +{ + return true; +} +void +TapNetDevice::SetLinkChangeCallback (Callback callback) +{} +bool +TapNetDevice::IsBroadcast (void) const +{ + return true; +} +Address +TapNetDevice::GetBroadcast (void) const +{ + return Mac48Address ("ff:ff:ff:ff:ff:ff"); +} +bool +TapNetDevice::IsMulticast (void) const +{ + return true; +} +Address +TapNetDevice::GetMulticast (void) const +{ + return Mac48Address::GetMulticastPrefix (); +} +Address +TapNetDevice::MakeMulticastAddress (Ipv4Address multicastGroup) const +{ + return Mac48Address::GetMulticast (multicastGroup); +} +bool +TapNetDevice::IsPointToPoint (void) const +{ + return false; +} +bool +TapNetDevice::Send(Ptr packet, const Address& dest, uint16_t protocolNumber) +{ + NS_LOG_FUNCTION (this << packet << dest << protocolNumber); + return SendFrom (packet, m_address, dest, protocolNumber); +} +bool +TapNetDevice::SendFrom(Ptr packet, const Address& source, const Address& dest, uint16_t protocolNumber) +{ + NS_LOG_FUNCTION (this << packet << source << dest << protocolNumber); + Mac48Address to = Mac48Address::ConvertFrom (dest); + Mac48Address from = Mac48Address::ConvertFrom (source); + + EthernetHeader header = EthernetHeader (false); + header.SetSource (from); + header.SetDestination (to); + header.SetLengthType (protocolNumber); + packet->AddHeader (header); + + ssize_t written = write (m_tap, packet->PeekData (), packet->GetSize ()); + if (written == -1 || written != (ssize_t)packet->GetSize ()) + { + m_dropTrace (packet, from, to); + return false; + } + + m_txTrace (packet, from, to); + + return true; +} + +Ptr +TapNetDevice::GetNode (void) const +{ + return m_node; +} +void +TapNetDevice::SetNode (Ptr node) +{ + m_node = node; +} +bool +TapNetDevice::NeedsArp (void) const +{ + return true; +} +void +TapNetDevice::SetReceiveCallback (NetDevice::ReceiveCallback cb) +{ + m_rxCallback = cb; +} + +void +TapNetDevice::DoDispose (void) +{ + NS_LOG_FUNCTION (this); + close (m_tap); + m_thread->Join (); + m_thread = 0; + m_node = 0; + m_channel = 0; + NetDevice::DoDispose (); +} + + +void +TapNetDevice::SetPromiscReceiveCallback (PromiscReceiveCallback cb) +{ + m_promiscCallback = cb; +} + +bool +TapNetDevice::SupportsSendFrom (void) const +{ + return true; +} + +} // namespace ns3 diff --git a/src/devices/emutap/tap-net-device.h b/src/devices/emutap/tap-net-device.h new file mode 100644 index 000000000..d0f493b92 --- /dev/null +++ b/src/devices/emutap/tap-net-device.h @@ -0,0 +1,103 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2008 INRIA + * + * 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 + * + * Author: Mathieu Lacage + */ +#ifndef TAP_NET_DEVICE_H +#define TAP_NET_DEVICE_H + +#include "ns3/net-device.h" +#include "ns3/mac48-address.h" +#include "ns3/traced-callback.h" +#include +#include + +namespace ns3 { + +class Node; +class SystemThread; +class TapChannel; + +/** + * \ingroup netdevice + * + * \brief a NetDevice to get packets to and from a host tap device. + */ +class TapNetDevice : public NetDevice +{ +public: + static TypeId GetTypeId (void); + TapNetDevice (); + + void SetAddress (Mac48Address address); + + void SetChannel (Ptr channel); + + void SetupHost (Ipv4Address ad, Ipv4Mask mask, Ipv4Address gateway); + + // inherited from NetDevice base class. + virtual void SetName(const std::string name); + virtual std::string GetName(void) const; + virtual void SetIfIndex(const uint32_t index); + virtual uint32_t GetIfIndex(void) const; + virtual Ptr GetChannel (void) const; + virtual Address GetAddress (void) const; + virtual bool SetMtu (const uint16_t mtu); + virtual uint16_t GetMtu (void) const; + virtual bool IsLinkUp (void) const; + virtual void SetLinkChangeCallback (Callback callback); + virtual bool IsBroadcast (void) const; + virtual Address GetBroadcast (void) const; + virtual bool IsMulticast (void) const; + virtual Address GetMulticast (void) const; + virtual Address MakeMulticastAddress (Ipv4Address multicastGroup) const; + virtual bool IsPointToPoint (void) const; + virtual bool Send(Ptr packet, const Address& dest, uint16_t protocolNumber); + virtual bool SendFrom(Ptr packet, const Address& source, const Address& dest, uint16_t protocolNumber); + virtual Ptr GetNode (void) const; + virtual void SetNode (Ptr node); + virtual bool NeedsArp (void) const; + virtual void SetReceiveCallback (NetDevice::ReceiveCallback cb); + virtual void SetPromiscReceiveCallback (PromiscReceiveCallback cb); + virtual bool SupportsSendFrom (void) const; + +protected: + virtual void DoDispose (void); +private: + void Receive (Ptr packet, uint16_t protocol, + Mac48Address to, Mac48Address from); + void ForwardUp (uint8_t *buffer, uint32_t size); + void ReadThread (void); + + NetDevice::ReceiveCallback m_rxCallback; + NetDevice::PromiscReceiveCallback m_promiscCallback; + Ptr m_node; + uint16_t m_mtu; + std::string m_name; + uint32_t m_ifIndex; + Mac48Address m_address; + int m_tap; + Ptr m_thread; + TracedCallback,Mac48Address,Mac48Address> m_rxTrace; + TracedCallback,Mac48Address,Mac48Address> m_txTrace; + TracedCallback,Mac48Address,Mac48Address> m_dropTrace; + Ptr m_channel; +}; + +} // namespace ns3 + +#endif /* TAP_NET_DEVICE_H */ diff --git a/src/devices/emutap/wscript b/src/devices/emutap/wscript new file mode 100644 index 000000000..11bdb3a3c --- /dev/null +++ b/src/devices/emutap/wscript @@ -0,0 +1,26 @@ +## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- + + +def build(bld): + obj = bld.create_suid_program('ns3-tap-manager') + obj.source = [ + 'tap-manager.cc', + ] + + module = bld.create_ns3_module('emutap', ['node']) + module.uselib = 'CAP' + module.source = [ + 'tap-net-device.cc', + 'tap-manager-client.cc', + 'tap-channel.cc', + 'host-tap-net-device.cc', + ] + headers = bld.create_obj('ns3header') + headers.module = 'emutap' + headers.source = [ + 'tap-net-device.h', + 'host-tap-net-device.h', + 'tap-channel.h', + ] + + diff --git a/src/wscript b/src/wscript index bc8c87689..5cf64f53f 100644 --- a/src/wscript +++ b/src/wscript @@ -19,6 +19,8 @@ all_modules = ( 'internet-stack', 'devices/point-to-point', 'devices/csma', + 'devices/bridge', + 'devices/emutap', 'applications/onoff', 'applications/packet-sink', 'applications/udp-echo', @@ -27,7 +29,6 @@ all_modules = ( 'mobility', 'devices/wifi', 'helper', - 'devices/bridge', 'contrib/stats', ) diff --git a/wscript b/wscript index 8eb8c50b5..b78b2e812 100644 --- a/wscript +++ b/wscript @@ -12,6 +12,7 @@ import pproc as subprocess import Params import Object import ccroot +import Task Params.g_autoconfig = 1 @@ -166,6 +167,10 @@ def set_options(opt): help=('For regression testing, only run/generate the indicated regression tests, ' 'specified as a comma separated list of test names'), dest='regression_tests', type="string") + opt.add_option('--disable-sudo', + help=('Do not attempt to use sudo to setup suid bits on ns3 executables.'), + dest='disable_sudo', action='store_true', + default=False) # options provided in a script in a subdirectory named "src" opt.sub_options('src') @@ -275,6 +280,9 @@ def configure(conf): # we cannot run regression tests without diff conf.find_program('diff', var='DIFF') + # for suid bits + conf.find_program('sudo', var='SUDO') + # we cannot pull regression traces without mercurial conf.find_program('hg', var='MERCURIAL') @@ -288,6 +296,41 @@ def configure(conf): print "%-30s: %s" % (caption, status) +class SuidBuildTask(Task.TaskBase): + """task that makes a binary Suid + """ + def __init__(self, bld, program): + self.m_display = 'build-suid' + self.prio = 1000 # build after the rest of ns-3 + self.__program = program + self.__env = bld.env () + super(SuidBuildTask, self).__init__() + + def run(self): + try: + program_obj = _find_program(self.__program.target, self.__env) + except ValueError, ex: + Params.fatal(str(ex)) + + try: + program_node = program_obj.path.find_build(ccroot.get_target_name(program_obj)) + except AttributeError: + Params.fatal("%s does not appear to be a program" % (program_name,)) + + filename = program_node.abspath(self.__env) + os.system ('sudo chown root ' + filename) + os.system ('sudo chmod u+s ' + filename) + +def create_suid_program(bld, name): + program = bld.create_obj('cpp', 'program') + program.is_ns3_program = True + program.module_deps = list() + program.name = name + program.target = name + if bld.env ()['SUDO'] and not Params.g_options.disable_sudo: + SuidBuildTask (bld, program) + return program + def create_ns3_program(bld, name, dependencies=('simulator',)): program = bld.create_obj('cpp', 'program') program.is_ns3_program = True @@ -340,6 +383,7 @@ def build(bld): Params.g_cwd_launch = Params.g_build.m_curdirnode.abspath() bld.create_ns3_program = types.MethodType(create_ns3_program, bld) + bld.create_suid_program = types.MethodType(create_suid_program, bld) variant_name = bld.env_of_name('default')['NS3_ACTIVE_VARIANT'] variant_env = bld.env_of_name(variant_name) bld.m_allenvs['default'] = variant_env # switch to the active variant