Use a Virtual Interface

Virtual Interface

We assume that you have an OpenLBR connected to the IPv6 cloud. If not, go back to OpenTunneling.

Goal

This page deals with installing a virtual interface which will put the mote connected into the OpenLBR onto the IPv6 cloud.

TUN, a Linux command for creating Virtual Network Interfaces

The OpenLBR is connected to the Internet through an Ethernet cable. We will now create a network interface for the mote side. The OpenWSN stack understands IPv6, enabling direct communication between the motes and the Internet. The mote presents itself as a serial port to Debian; we will create a virtual network interface coupled with a Python script to redirect between this interface and the serial port.

A virtual network interface is a software network interface provided by the operating system to user space programs. Instead of having packets sent across a physical network, they are captured by the user space program. The user space program may do various tasks with the captured packets, the most common being virtual private networking (VPN), but others are possible.

We have the user space program do the 6LoWPAN header compression/decompression and forwarding packets to/from the serial port to which the mote is attached. This converts our mote into a network interface at the OpenLBR, enabling communication between the motes and the Internet by editing the kernel routing tables.

Creating and configuring a TUN interface

The tun kernel module needs to be loaded. The second line forces the module to be loaded automatically at boot time.

# echo tun >> /etc/modules
# modprobe tun

The following Python script creates and configure the TUN interface. Whatever is sent through that interface (i.e. below the IP protocol) will be printed in hexadecimal form on the OpenADR. The TUN interface disappears when the program ends.

#!python
import os, sys, struct, signal, binascii
from time import sleep
from fcntl import ioctl

IPV6PREFIX = '2001:470:1f05:98e'
IFF_TUN    = 0x0001
TUNSETIFF  = 0x400454ca

# create virtual interface
f = os.open("/dev/net/tun", os.O_RDWR)
ifs = ioctl(f, TUNSETIFF, struct.pack("16sH", "tun%d", IFF_TUN))
ifname = ifs[:16].strip("\x00")

# configure IPv6 address
v = os.system('ifconfig ' + ifname + ' inet6 add ' + IPV6PREFIX + '::1/64')
v = os.system('ifconfig ' + ifname + ' inet6 add fe80::1/64')
v = os.system('ifconfig ' + ifname + ' up')

# set route
os.system('route -A inet6 add ' + IPV6PREFIX + '::/64 dev ' + ifname)

# enable IPv6 forwarding
os.system('echo 1 > /proc/sys/net/ipv6/conf/all/forwarding')

print('\ncreated following virtual interface:')
os.system('ifconfig ' + ifname)

try:
   while(True):
      line = os.read(f,1500) #packets are at most 1500 bytes long (Ethernet MTU)
      line = line[4:]        #IPv6 header starts at byte 4
      output_wireshark(line)
      #print 'received '+str(len(line))+' bytes: '+binascii.hexlify(line)
except KeyboardInterrupt:
   print "Stopped by user."

When running the program, you should read on the OpenLBR:

wsngateway:~# python tun_test.py

created following virtual interface:
tun0      Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
          inet6 addr: 2001:470:1f05:98e::1/64 Scope:Global
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:500
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

On the Internet Host, issue the following command:

ping -6 2001:470:1f05:98e::1

It should be successful, but you shouldn't see anything on the OpenLBR. This is what we expect: the Debian stack answers the ICMPv6 echo request (the command is successful on the Internet host) without ever needing to encapsulate the packet at layer 2 (we don't see anything on the OpenADR).

On the Internet Host, issue the following command:

ping -6 2001:470:1f05:98e::2

This time, we are issuing a ping towards an IP address within the 2001:470:1f05:98e/64 prefix space, but not the tun interface of the OpenLBR. As a result, the IMCPv6 echo request is routed to the tun0 interface, encapsulated and sent 'over the wire'. Because it's a software interface, the packet is captures and displayed by the Python script. You can hence read on the OpenLBR:

received 80 bytes: 6000000000283a762607f14004001176c46626995ef1d477200104701f05098e000000000000000280003b89000101426162636465666768696a6b6c6d6e6f7071727374757677616263646566676869
received 80 bytes: 6000000000283a762607f14004001176c46626995ef1d477200104701f05098e000000000000000280003b88000101436162636465666768696a6b6c6d6e6f7071727374757677616263646566676869
received 80 bytes: 6000000000283a762607f14004001176c46626995ef1d477200104701f05098e000000000000000280003b87000101446162636465666768696a6b6c6d6e6f7071727374757677616263646566676869
received 80 bytes: 6000000000283a762607f14004001176c46626995ef1d477200104701f05098e000000000000000280003b86000101456162636465666768696a6b6c6d6e6f7071727374757677616263646566676869

These are the 4 ping attempts Windows performs. They are not successful because our Python script does not answer echo requests with echo replies.

Using Wireshark to interpret the bytes

The captured bytes above are hard to interpret. They should be composed for an IPv6 header, followed by an ICMPv6 header, and some ICMPv6 payload. We use Wireshark to interpret the content. For that, we need to first output the bytes in the right format. Add the following lines to the Python script:

#!python
def openhex(num,length):
   output = ''
   for i in range(len((hex(num))[2:]),length):
      output += '0'
   output += (hex(num))[2:]+' '
   return output

def output_wireshark(line):
   num_bytes_per_line = 16
   index=0
   for line_index in range(len(line)/num_bytes_per_line+1):
      chars = ''
      sys.stdout.write(openhex(index,6))
      while index<(line_index+1)*num_bytes_per_line and index<len(line):
         if ord(line[index])>32 and ord(line[index])<127:
            chars += line[index]
         else:
            chars += '.'
         sys.stdout.write(openhex(ord(line[index]),2))
         index += 1
      for i in range(index,(line_index+1)*num_bytes_per_line):
         sys.stdout.write('   ')
      sys.stdout.write(chars+'\n')

And replace the line

print 'received '+str(len(line))+' bytes: '+binascii.hexlify(line)

by

output_wireshark(line)

Repeat the last ping from the Internet host, you should now read on the OpenLBR:

000000 60 00 00 00 00 28 3a 76 26 07 f1 40 04 00 11 76 `....(:v&..@...v
000010 98 4f d7 1b fe f7 23 53 20 01 04 70 1f 05 09 8e .O....#S...p....
000020 00 00 00 00 00 00 00 02 80 00 c9 78 00 01 00 05 ...........x....
000030 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 abcdefghijklmnop
000040 71 72 73 74 75 76 77 61 62 63 64 65 66 67 68 69 qrstuvwabcdefghi
000050

Save this into a text file. The following step is to use the text2pcap utility to turn this text file into a pcap file. text2pcap comes will any distribution of Wireshark, just make sure your PATH environment variable points to the directory where that _text2pcap' lives.

Type in the following command:

C:\Users\Thomas\Desktop>text2pcap -l 12 poipoi.txt poipoi.pcap
Input from: poipoi.txt
Output to: poipoi.pcap
Wrote packet of 80 bytes at 0
Read 1 potential packet, wrote 1 packet

The option -l 12 indicates to Wireshark a raw IP packet, i.e. no MAC headers. Double click on the poipoi.pcap, Wireshark should open, indicating that this is an ICMPv6 packet.

You can find more information about text2pcap at http://www.wireshark.org/docs/man\-pages/text2pcap.html.

If everything works, go on to configure radvd.