[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

6.3 Implementation

Perhaps the most unusual part of the Emu and Tap device implementation relates to the requirement for executing some of the code with super-user permissions. Rather than force the user to execute the entire simulation as root, we provide a small “creator” program that runs as root and does any required high-permission sockets work.

We do a similar thing for both the Emu and the Tap devices. The high-level view is that the CreateSocket method creates a local interprocess (Unix) socket, forks, and executes the small creation program. The small program, which runs as suid root, creates a raw socket and sends back the raw socket file descriptor over the Unix socket that is passed to it as a parameter. The raw socket is passed as a control message (sometimes called ancillary data) of type SCM_RIGHTS.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

6.3.1 Emu Net Device

The Emu net device uses the ns-3 threading and multithreaded real-time scheduler extensions. The interesting work in the Emu device is done when the net device is started (EmuNetDevice::StartDevice ()). An attribute (“Start”) provides a simulation time at which to spin up the net device. At this specified time (which defaults to t=0), the socket creation function is called and executes as described above. You may also specify a time at which to stop the device using the “Stop” attribute.

Once the (promiscuous mode) socket is created, we bind it to an interface name also provided as an attribute (“DeviceName”) that is stored internally as m_deviceName:

  struct ifreq ifr;
  bzero (&ifr, sizeof(ifr));
  strncpy ((char *)ifr.ifr_name, m_deviceName.c_str (), IFNAMSIZ);

  int32_t rc = ioctl (m_sock, SIOCGIFINDEX, &ifr);

  struct sockaddr_ll ll;
  bzero (&ll, sizeof(ll));

  ll.sll_family = AF_PACKET;
  ll.sll_ifindex = m_sll_ifindex;
  ll.sll_protocol = htons(ETH_P_ALL);

  rc = bind (m_sock, (struct sockaddr *)&ll, sizeof (ll));

After the promiscuous raw socket is set up, a separate thread is spawned to do reads from that socket and the link state is set to Up.

  m_readThread = Create<SystemThread> (
    MakeCallback (&EmuNetDevice::ReadThread, this));
  m_readThread->Start ();

  NotifyLinkUp ();

The EmuNetDevice::ReadThread function basically just sits in an infinite loop reading from the promiscuous mode raw socket and scheduling packet receptions using the real-time simulator extensions.

  for (;;)
    {
      ...

      len = recvfrom (m_sock, buf, bufferSize, 0, (struct sockaddr *)&addr, 
        &addrSize);

      ...

      DynamicCast<RealtimeSimulatorImpl> (Simulator::GetImplementation ())->
        ScheduleRealtimeNow (
          MakeEvent (&EmuNetDevice::ForwardUp, this, buf, len));

      ...
    }

The line starting with our templated DynamicCast function probably deserves a comment. It gains access to the simulator implementation object using the Simulator::GetImplementation method and then casts to the real-time simulator implementation to use the real-time schedule method ScheduleRealtimeNow. This function will cause a handler for the newly received packet to be scheduled for execution at the current real time clock value. This will, in turn cause the simulation clock to be advanced to that real time value when the scheduled event (EmuNetDevice::ForwardUp) is fired.

The ForwardUp function operates as most other similar ns-3 net device methods do. The packet is first filtered based on the destination address. In the case of the Emu device, the MAC destination address will be the address of the Emu device and not the hardware address of the real device. Headers are then stripped off and the trace hooks are hit. Finally, the packet is passed up the ns-3 protocol stack using the receive callback function of the net device.

Sending a packet is equally straightforward as shown below. The first thing we do is to add the ethernet header and trailer to the ns-3 Packet we are sending. The source address corresponds to the address of the Emu device and not the underlying native device MAC address. This is where the MAC address spoofing is done. The trailer is added and we enqueue and dequeue the packet from the net device queue to hit the trace hooks.

  header.SetSource (source);
  header.SetDestination (destination);
  header.SetLengthType (packet->GetSize ());
  packet->AddHeader (header);

  EthernetTrailer trailer;
  trailer.CalcFcs (packet);
  packet->AddTrailer (trailer);

  m_queue->Enqueue (packet);
  packet = m_queue->Dequeue ();

  struct sockaddr_ll ll;
  bzero (&ll, sizeof (ll));

  ll.sll_family = AF_PACKET;
  ll.sll_ifindex = m_sll_ifindex;
  ll.sll_protocol = htons(ETH_P_ALL);

  rc = sendto (m_sock, packet->PeekData (), packet->GetSize (), 0, 
    reinterpret_cast<struct sockaddr *> (&ll), sizeof (ll));

Finally, we simply send the packet to the raw socket which puts it out on the real network.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

6.3.2 Tap Net Device

The Tap Net Device is scheduled for inclusion in ns-3.4 at the writing of this section. We will include details as soon as the Tap device is merged.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]

This document was generated on December, 19 2008 using texi2html 1.78.