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 5import fcntl 6import os 7import struct 8import socket 9 10from lansim import pyiftun 11from lansim import tools 12 13 14# Export some constants used by callers to pass to the |mode| argument while 15# create a TunTap() object. 16from lansim.pyiftun import IFF_TAP, IFF_TUN 17 18 19class TunTapError(Exception): 20 """TunTap specific error.""" 21 22 23ETHERNET_HEADER_SIZE = 18 24 25 26STRUCT_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 34IFNAMSIZ_FMT = str(pyiftun.IFNAMSIZ) + 's' 35 36 37def 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 56def 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 72class 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