Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/scapy/layers/tuntap.py: 38%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

120 statements  

1# SPDX-License-Identifier: GPL-2.0-only 

2# This file is part of Scapy 

3# See https://scapy.net/ for more information 

4# Copyright (C) Philippe Biondi <phil@secdev.org> 

5# Copyright (C) Michael Farrell <micolous+git@gmail.com> 

6 

7""" 

8Implementation of TUN/TAP interfaces. 

9 

10These allow Scapy to act as the remote side of a virtual network interface. 

11""" 

12 

13import socket 

14import time 

15from fcntl import ioctl 

16 

17from scapy.compat import bytes_encode, raw 

18from scapy.config import conf 

19from scapy.consts import BIG_ENDIAN, BSD, DARWIN, LINUX 

20from scapy.data import ETHER_TYPES, MTU 

21from scapy.error import log_runtime, warning 

22from scapy.fields import ( 

23 BitField, 

24 Field, 

25 FlagsField, 

26 IntField, 

27 StrFixedLenField, 

28 XShortEnumField, 

29) 

30from scapy.interfaces import network_name 

31from scapy.layers.inet import IP 

32from scapy.layers.inet6 import IPv6, IPv46 

33from scapy.layers.l2 import Ether 

34from scapy.packet import Packet, bind_layers 

35from scapy.supersocket import SimpleSocket 

36 

37# Linux-specific defines (/usr/include/linux/if_tun.h) 

38LINUX_TUNSETIFF = 0x400454ca 

39LINUX_IFF_TUN = 0x0001 

40LINUX_IFF_TAP = 0x0002 

41LINUX_IFF_NO_PI = 0x1000 

42LINUX_IFNAMSIZ = 16 

43 

44# Darwin-specific defines (net/if_utun.h and sys/kern_control.h) 

45DARWIN_CTLIOCGINFO = 0xc0644e03 

46DARWIN_UTUN_CONTROL_NAME = b"com.apple.net.utun_control" 

47DARWIN_MAX_KCTL_NAME = 96 

48 

49 

50class NativeShortField(Field): 

51 def __init__(self, name, default): 

52 Field.__init__(self, name, default, "@H") 

53 

54 

55class TunPacketInfo(Packet): 

56 aliastypes = [Ether] 

57 

58 

59class LinuxTunIfReq(Packet): 

60 """ 

61 Structure to request a specific device name for a tun/tap 

62 Linux ``struct ifreq``. 

63 

64 See linux/if.h (struct ifreq) and tuntap.txt for reference. 

65 """ 

66 fields_desc = [ 

67 # union ifr_ifrn 

68 StrFixedLenField("ifrn_name", b"", 16), 

69 # union ifr_ifru 

70 NativeShortField("ifru_flags", 0), 

71 ] 

72 

73 

74class DarwinUtunIfReq(Packet): 

75 """ 

76 Structure for issuing Darwin ioctl commands (``struct ctl_info``). 

77 

78 See net/if_utun.h and sys/kern_control.h for reference. 

79 """ 

80 fields_desc = [ 

81 BitField("ctl_id", 0, -32), 

82 StrFixedLenField("ctl_name", DARWIN_UTUN_CONTROL_NAME, DARWIN_MAX_KCTL_NAME) 

83 ] 

84 

85 

86class LinuxTunPacketInfo(TunPacketInfo): 

87 """ 

88 Base for TUN packets. 

89 

90 See linux/if_tun.h (struct tun_pi) for reference. 

91 """ 

92 fields_desc = [ 

93 # This is native byte order 

94 FlagsField("flags", 0, 

95 (lambda _: 16 if BIG_ENDIAN else -16), 

96 ["TUN_VNET_HDR"] + 

97 ["reserved%d" % x for x in range(1, 16)]), 

98 # This is always network byte order 

99 XShortEnumField("type", 0x9000, ETHER_TYPES), 

100 ] 

101 

102 

103class DarwinUtunPacketInfo(Packet): 

104 fields_desc = [ 

105 IntField("addr_family", socket.AF_INET) 

106 ] 

107 

108 

109class TunTapInterface(SimpleSocket): 

110 """ 

111 A socket to act as the host's peer of a tun / tap interface. 

112 

113 This implements kernel interfaces for tun and tap devices. 

114 

115 :param iface: The name of the interface to use, eg: 'tun0' 

116 :param mode_tun: If True, create as TUN interface (layer 3). 

117 If False, creates a TAP interface (layer 2). 

118 If not supplied, attempts to detect from the ``iface`` 

119 name. 

120 :type mode_tun: bool 

121 :param strip_packet_info: If True (default), strips any TunPacketInfo from 

122 the packet. If False, leaves it in tact. Some 

123 operating systems and tunnel types don't include 

124 this sort of data. 

125 :type strip_packet_info: bool 

126 

127 FreeBSD references: 

128 

129 * tap(4): https://www.freebsd.org/cgi/man.cgi?query=tap&sektion=4 

130 * tun(4): https://www.freebsd.org/cgi/man.cgi?query=tun&sektion=4 

131 

132 Linux references: 

133 

134 * https://www.kernel.org/doc/Documentation/networking/tuntap.txt 

135 

136 """ 

137 desc = "Act as the host's peer of a tun / tap interface" 

138 

139 def __init__(self, iface=None, mode_tun=None, default_read_size=MTU, 

140 strip_packet_info=True, *args, **kwargs): 

141 self.iface = bytes_encode( 

142 network_name(conf.iface) if iface is None else iface 

143 ) 

144 

145 self.mode_tun = mode_tun 

146 if self.mode_tun is None: 

147 if self.iface.startswith(b"tun") or self.iface.startswith(b"utun"): 

148 self.mode_tun = True 

149 elif self.iface.startswith(b"tap"): 

150 self.mode_tun = False 

151 else: 

152 raise ValueError( 

153 "Could not determine interface type for %r; set " 

154 "`mode_tun` explicitly." % (self.iface,)) 

155 

156 self.strip_packet_info = bool(strip_packet_info) 

157 

158 # This is non-zero when there is some kernel-specific packet info. 

159 # We add this to any MTU value passed to recv(), and use it to 

160 # remove leading bytes when strip_packet_info=True. 

161 self.mtu_overhead = 0 

162 

163 # The TUN packet specification sends raw IP at us, and doesn't specify 

164 # which version. 

165 self.kernel_packet_class = IPv46 if self.mode_tun else Ether 

166 

167 if LINUX: 

168 devname = b"/dev/net/tun" 

169 

170 # Having an EtherType always helps on Linux, then we don't need 

171 # to use auto-detection of IP version. 

172 if self.mode_tun: 

173 self.kernel_packet_class = LinuxTunPacketInfo 

174 self.mtu_overhead = 4 # len(LinuxTunPacketInfo) 

175 else: 

176 warning("tap devices on Linux do not include packet info!") 

177 self.strip_packet_info = True 

178 

179 if len(self.iface) > LINUX_IFNAMSIZ: 

180 warning("Linux interface names are limited to %d bytes, " 

181 "truncating!" % (LINUX_IFNAMSIZ,)) 

182 self.iface = self.iface[:LINUX_IFNAMSIZ] 

183 sock = open(devname, "r+b", buffering=0) 

184 elif BSD: # also DARWIN 

185 if self.iface.startswith(b"utun"): # allowed for Darwin 

186 if not DARWIN: 

187 raise ValueError('`utun` iface prefix is only allowed for Darwin') 

188 self.kernel_packet_class = DarwinUtunPacketInfo 

189 self.mtu_overhead = 4 

190 interface_num = int(self.iface[4:]) 

191 

192 utun_socket = socket.socket( 

193 socket.PF_SYSTEM, socket.SOCK_DGRAM, socket.SYSPROTO_CONTROL) 

194 ctl_info = ioctl(utun_socket, DARWIN_CTLIOCGINFO, 

195 raw(DarwinUtunIfReq())) 

196 utun_socket.connect( 

197 (DarwinUtunIfReq(ctl_info).getfieldval("ctl_id"), interface_num + 1) 

198 ) 

199 

200 sock = utun_socket.makefile(mode="rwb", buffering=0) 

201 elif self.iface.startswith(b"tap") or self.iface.startswith(b"tun"): 

202 devname = b"/dev/" + self.iface 

203 if not self.strip_packet_info: 

204 warning("tun/tap devices on BSD and Darwin never include " 

205 "packet info!") 

206 self.strip_packet_info = True 

207 sock = open(devname, "r+b", buffering=0) 

208 else: 

209 raise ValueError("Interface names must start with `tun` or " 

210 "`tap` on BSD and Darwin or `utun` on Darwin") 

211 else: 

212 raise NotImplementedError("TunTapInterface is not supported on " 

213 "this platform!") 

214 

215 if LINUX: 

216 if self.mode_tun: 

217 flags = LINUX_IFF_TUN 

218 else: 

219 # Linux can send us LinuxTunPacketInfo for TAP interfaces, but 

220 # the kernel sends the wrong information! 

221 # 

222 # Instead of type=1 (Ether), it sends that of the payload 

223 # (eg: 0x800 for IPv4 or 0x86dd for IPv6). 

224 # 

225 # tap interfaces always send Ether frames, which include a 

226 # type parameter for the IPv4/v6/etc. payload, so we set 

227 # IFF_NO_PI. 

228 flags = LINUX_IFF_TAP | LINUX_IFF_NO_PI 

229 

230 tsetiff = raw(LinuxTunIfReq( 

231 ifrn_name=self.iface, 

232 ifru_flags=flags)) 

233 

234 ioctl(sock, LINUX_TUNSETIFF, tsetiff) 

235 

236 self.closed = False 

237 self.default_read_size = default_read_size 

238 super(TunTapInterface, self).__init__(sock) 

239 

240 def __call__(self, *arg, **karg): 

241 """Needed when using an instantiated TunTapInterface object for 

242 conf.L2listen, conf.L2socket or conf.L3socket. 

243 

244 """ 

245 return self 

246 

247 def recv_raw(self, x=None): 

248 if x is None: 

249 x = self.default_read_size 

250 

251 x += self.mtu_overhead 

252 

253 dat = self.ins.read(x) 

254 r = self.kernel_packet_class, dat, time.time() 

255 if self.mtu_overhead > 0 and self.strip_packet_info: 

256 # Get the packed class of the payload, without triggering a full 

257 # decode of the payload data. 

258 cls = r[0](r[1][:self.mtu_overhead]).guess_payload_class(b'') 

259 

260 # Return the payload data only 

261 return cls, r[1][self.mtu_overhead:], r[2] 

262 else: 

263 return r 

264 

265 def send(self, x): 

266 # type: (Packet) -> int 

267 if hasattr(x, "sent_time"): 

268 x.sent_time = time.time() 

269 

270 if self.kernel_packet_class == IPv46: 

271 # IPv46 is an auto-detection wrapper; we should just push through 

272 # packets normally if we got IP or IPv6. 

273 if not isinstance(x, (IP, IPv6)): 

274 x = IP() / x 

275 elif not isinstance(x, self.kernel_packet_class): 

276 x = self.kernel_packet_class() / x 

277 

278 sx = raw(x) 

279 

280 try: 

281 r = self.outs.write(sx) 

282 self.outs.flush() 

283 return r 

284 except socket.error: 

285 log_runtime.error("%s send", 

286 self.__class__.__name__, exc_info=True) 

287 

288 

289# Bindings # 

290bind_layers(DarwinUtunPacketInfo, IP, addr_family=socket.AF_INET) 

291bind_layers(DarwinUtunPacketInfo, IPv6, addr_family=socket.AF_INET6)