Home | History | Annotate | Download | only in py
      1 # Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 import fcntl
      6 import os
      7 import struct
      8 import socket
      9 
     10 from lansim import pyiftun
     11 from lansim import tools
     12 
     13 
     14 # Export some constants used by callers to pass to the |mode| argument while
     15 # create a TunTap() object.
     16 from lansim.pyiftun import IFF_TAP, IFF_TUN
     17 
     18 
     19 class TunTapError(Exception):
     20     """TunTap specific error."""
     21 
     22 
     23 ETHERNET_HEADER_SIZE = 18
     24 
     25 
     26 STRUCT_IFREQ_FMT = {
     27     "ifr_flags": "h", # short ifr_flags
     28     "ifr_mtu": "i", # int ifr_flags
     29     "ifr_addr": "HH12s", # struct sockaddr_in ifr_addr
     30     "ifr_hwaddr": "H14s", # struct sockaddr ifru_hwaddr
     31 }
     32 
     33 
     34 IFNAMSIZ_FMT = str(pyiftun.IFNAMSIZ) + 's'
     35 
     36 
     37 def pack_struct_ifreq(if_name, argname, *args):
     38     """Packs a binary string representing a struct ifreq.
     39 
     40     The struct ifreq is used to call ioctl() on network devices. The argument
     41     type and size depends on the operation performed and is represented as the
     42     union of different types. This function packs the struct according to the
     43     provided |argname| which defines the type of |arg|. See netdevice(7) for a
     44     list of possible arguments and ioctl() commands.
     45 
     46     @param if_name: The interface name.
     47     @param argname: The name of the member used for the union in struct ifreq.
     48     @param args: The values used to pack the requested |argname|.
     49     @raises ValueError: if |argname| isn't a supported union's member name.
     50     """
     51     if argname not in STRUCT_IFREQ_FMT:
     52       raise ValueError()
     53     return struct.pack(IFNAMSIZ_FMT + STRUCT_IFREQ_FMT[argname], if_name, *args)
     54 
     55 
     56 def unpack_struct_ifreq(data, argname):
     57     """Returns a tuple with the interpreted contents of the a struct ifreq.
     58 
     59     The result returned from a ioctl() on network devices has the same format
     60     than the passed struct ifreq request. This function decodes this result into
     61     a python tuple, which depends on the |argname| passed to
     62 
     63     @param data: The packed representation of the struct ifreq.
     64     @param argname: The name of the member used for the union in struct ifreq.
     65     @raises ValueError: if |argname| isn't a supported union's member name.
     66     """
     67     if argname not in STRUCT_IFREQ_FMT:
     68       raise ValueError()
     69     return struct.unpack(IFNAMSIZ_FMT + STRUCT_IFREQ_FMT[argname], data)
     70 
     71 
     72 class TunTap(object):
     73     """TUN/TAP network interface manipulation class."""
     74 
     75 
     76     DEFAULT_DEV_NAME = {
     77         IFF_TUN: "tun%d",
     78         IFF_TAP: "tap%d",
     79     }
     80 
     81 
     82     def __init__(self, mode=pyiftun.IFF_TUN, name=None, tundev='/dev/net/tun'):
     83         """Creates or re-opens a TUN/TAP interface.
     84 
     85         @param mode: This argument is passed to the TUNSETIFF ioctl() to create
     86         the interface. It says whether the interface created is a TAP (IFF_TAP)
     87         or TUN (IFF_TUN) interface and some related constant flags found on
     88         pyiftun.IFF_*.
     89         @param name: The name of the created interface. If the name ends in '%d'
     90         that value will be replaced by the kernel with a given number, otherwise
     91         the name will be appended with '%d'.
     92         @param tundev: The path to the kerner interface to the tun driver which
     93         defaults to the standard '/dev/net/tun' if not specified.
     94         """
     95         tun_type = mode & pyiftun.TUN_TYPE_MASK
     96         if tun_type not in self.DEFAULT_DEV_NAME:
     97             raise TunTapError("mode (%r) not supported" % mode)
     98 
     99         self.mode = mode
    100 
    101         # The interface name can have a "%d" that the kernel will replace with
    102         # a number.
    103         if name is None:
    104             name = self.DEFAULT_DEV_NAME[tun_type]
    105         elif not name.endswith('%d'):
    106             name += "%d"
    107 
    108         # Create the TUN/TAP interface.
    109         fd = os.open(tundev, os.O_RDWR)
    110         self._fd = fd
    111 
    112         ifs = fcntl.ioctl(fd, pyiftun.TUNSETIFF,
    113             pack_struct_ifreq(name, 'ifr_flags', mode))
    114         ifs_name, ifs_mode = struct.unpack(IFNAMSIZ_FMT + "H", ifs)
    115         self.name = ifs_name.rstrip('\0')
    116 
    117         # Socket used for ioctl() operations over the network device.
    118         self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    119 
    120         self.mtu = self._get_mtu()
    121 
    122 
    123     def __del__(self):
    124         if hasattr(self, '_fd'):
    125             os.close(self._fd)
    126 
    127 
    128     def _get_mtu(self):
    129         ifs = fcntl.ioctl(self._sock, pyiftun.SIOCGIFMTU,
    130             pack_struct_ifreq(self.name, 'ifr_mtu', 0))
    131         ifr_name, ifr_mtu = unpack_struct_ifreq(ifs, 'ifr_mtu')
    132         return ifr_mtu
    133 
    134 
    135     def _get_flags(self):
    136         ifs = fcntl.ioctl(self._sock, pyiftun.SIOCGIFFLAGS,
    137             pack_struct_ifreq(self.name, 'ifr_flags', 0))
    138         ifr_name, ifr_flags = unpack_struct_ifreq(ifs, 'ifr_flags')
    139         return ifr_flags
    140 
    141 
    142     def _set_flags(self, flags):
    143         ifs = fcntl.ioctl(self._sock, pyiftun.SIOCSIFFLAGS,
    144             pack_struct_ifreq(self.name, 'ifr_flags', flags))
    145         ifr_name, ifr_flags = unpack_struct_ifreq(ifs, 'ifr_flags')
    146         return ifr_flags
    147 
    148 
    149     def get_addr(self):
    150         """Return the address of the interface.
    151 
    152         @param string addr: The IPv4 address for the interface.
    153         """
    154         ifs = fcntl.ioctl(self._sock, pyiftun.SIOCGIFADDR,
    155             pack_struct_ifreq(self.name, 'ifr_addr', socket.AF_INET, 0, ''))
    156         ifr_name, ifr_family, ifr_type, ifr_addr = unpack_struct_ifreq(
    157                 ifs, 'ifr_addr')
    158         if ifr_type != 0:
    159             return None
    160         # ifr_addr contains up to 12 bytes (see STRUCT_IFREQ_FMT).
    161         return socket.inet_ntoa(ifr_addr[:4])
    162 
    163 
    164     def set_addr(self, addr, mask=None):
    165         """Sets the address and network mask of the interface.
    166 
    167         @param string addr: The IPv4 address for the interface.
    168         """
    169         str_addr = socket.inet_aton(addr)
    170         ifs = fcntl.ioctl(self._sock, pyiftun.SIOCSIFADDR,
    171             pack_struct_ifreq(self.name, 'ifr_addr',
    172                 socket.AF_INET, 0, str_addr))
    173 
    174         if mask != None:
    175           net_mask = (1 << 32) - (1 << (32 - mask))
    176           str_mask = struct.pack('!I', net_mask)
    177           ifs = fcntl.ioctl(self._sock, pyiftun.SIOCSIFNETMASK,
    178               pack_struct_ifreq(self.name, 'ifr_addr',
    179                   socket.AF_INET, 0, str_mask))
    180 
    181 
    182     """The interface IPv4 address in plain text as in '192.168.0.1'."""
    183     addr = property(get_addr, set_addr)
    184 
    185 
    186     def get_hwaddr(self):
    187         ifs = fcntl.ioctl(self._sock, pyiftun.SIOCGIFHWADDR,
    188             pack_struct_ifreq(self.name, 'ifr_hwaddr', 0, ''))
    189         ifr_name, ifr_family, ifr_hwaddr = unpack_struct_ifreq(
    190             ifs, 'ifr_hwaddr')
    191         return (ifr_family, tools.inet_ntohw(ifr_hwaddr[:6]))
    192 
    193 
    194     def set_hwaddr(self, hwaddr):
    195         """Sets the hardware ethernet address of the interface.
    196 
    197         The interface needs to be down in order to set this hardware (MAC)
    198         address.
    199 
    200         @param string hwaddr: The address in hex format: 'aa:bb:cc:DD:EE:FF'.
    201         """
    202         ifs = fcntl.ioctl(self._sock, pyiftun.SIOCSIFHWADDR,
    203             pack_struct_ifreq(self.name, 'ifr_hwaddr', 1, # 1 for Ethernet
    204                               tools.inet_hwton(hwaddr)))
    205         ifr_name, ifr_family, ifr_hwaddr = unpack_struct_ifreq(
    206             ifs, 'ifr_hwaddr')
    207         return (ifr_family, tools.inet_ntohw(ifr_hwaddr[:6]))
    208 
    209 
    210     """The interface Ethernet address as in '00:11:22:AA:BB:CC'."""
    211     hwaddr = property(get_hwaddr, set_hwaddr)
    212 
    213 
    214     def up(self):
    215         """Brings up the interface."""
    216         self._set_flags(self._get_flags() | pyiftun.IFF_UP)
    217 
    218 
    219     def down(self):
    220         """Brings down the interface."""
    221         self._set_flags(self._get_flags() & ~pyiftun.IFF_UP)
    222 
    223 
    224     def is_up(self):
    225         """Returns whether the interface is up."""
    226         return (self._get_flags() & pyiftun.IFF_UP) != 0
    227 
    228 
    229     def read(self):
    230         """Reads a 'sent' frame from the interface.
    231 
    232         The frame format depends on the interface type: Ethernet frame for TAP
    233         interfaces and IP frame for TUN interfaces. This function blocks until
    234         a new frame is available.
    235 
    236         @return string: A single frame sent to the interface.
    237         """
    238         return os.read(self._fd, self.mtu + ETHERNET_HEADER_SIZE)
    239 
    240 
    241     def write(self, data):
    242         """Write a 'received' frame from the interface.
    243 
    244         The frame format depends on the interface type: Ethernet frame for TAP
    245         interfaces and IP frame for TUN interfaces. This function does not
    246         block.
    247 
    248         @param data: A single frame received from the interface.
    249         """
    250         os.write(self._fd, data)
    251 
    252 
    253     def fileno(self):
    254         """Returns a file descriptor suitable to be used with select()."""
    255         return self._fd
    256