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

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

100 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 

13 

14import socket 

15import time 

16from fcntl import ioctl 

17 

18from scapy.compat import raw, bytes_encode 

19from scapy.config import conf 

20from scapy.consts import BIG_ENDIAN, BSD, LINUX 

21from scapy.data import ETHER_TYPES, MTU 

22from scapy.error import warning, log_runtime 

23from scapy.fields import Field, FlagsField, StrFixedLenField, XShortEnumField 

24from scapy.interfaces import network_name 

25from scapy.layers.inet import IP 

26from scapy.layers.inet6 import IPv46, IPv6 

27from scapy.layers.l2 import Ether 

28from scapy.packet import Packet 

29from scapy.supersocket import SimpleSocket 

30 

31 

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

33LINUX_TUNSETIFF = 0x400454ca 

34LINUX_IFF_TUN = 0x0001 

35LINUX_IFF_TAP = 0x0002 

36LINUX_IFF_NO_PI = 0x1000 

37LINUX_IFNAMSIZ = 16 

38 

39 

40class NativeShortField(Field): 

41 def __init__(self, name, default): 

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

43 

44 

45class TunPacketInfo(Packet): 

46 aliastypes = [Ether] 

47 

48 

49class LinuxTunIfReq(Packet): 

50 """ 

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

52 Linux ``struct ifreq``. 

53 

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

55 """ 

56 fields_desc = [ 

57 # union ifr_ifrn 

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

59 # union ifr_ifru 

60 NativeShortField("ifru_flags", 0), 

61 ] 

62 

63 

64class LinuxTunPacketInfo(TunPacketInfo): 

65 """ 

66 Base for TUN packets. 

67 

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

69 """ 

70 fields_desc = [ 

71 # This is native byte order 

72 FlagsField("flags", 0, 

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

74 ["TUN_VNET_HDR"] + 

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

76 # This is always network byte order 

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

78 ] 

79 

80 

81class TunTapInterface(SimpleSocket): 

82 """ 

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

84 

85 This implements kernel interfaces for tun and tap devices. 

86 

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

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

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

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

91 name. 

92 :type mode_tun: bool 

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

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

95 operating systems and tunnel types don't include 

96 this sort of data. 

97 :type strip_packet_info: bool 

98 

99 FreeBSD references: 

100 

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

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

103 

104 Linux references: 

105 

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

107 

108 """ 

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

110 

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

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

113 self.iface = bytes_encode( 

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

115 ) 

116 

117 self.mode_tun = mode_tun 

118 if self.mode_tun is None: 

119 if self.iface.startswith(b"tun"): 

120 self.mode_tun = True 

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

122 self.mode_tun = False 

123 else: 

124 raise ValueError( 

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

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

127 

128 self.strip_packet_info = bool(strip_packet_info) 

129 

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

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

132 # remove leading bytes when strip_packet_info=True. 

133 self.mtu_overhead = 0 

134 

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

136 # which version. 

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

138 

139 if LINUX: 

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

141 

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

143 # to use auto-detection of IP version. 

144 if self.mode_tun: 

145 self.kernel_packet_class = LinuxTunPacketInfo 

146 self.mtu_overhead = 4 # len(LinuxTunPacketInfo) 

147 else: 

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

149 self.strip_packet_info = True 

150 

151 if len(self.iface) > LINUX_IFNAMSIZ: 

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

153 "truncating!" % (LINUX_IFNAMSIZ,)) 

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

155 

156 elif BSD: # also DARWIN 

157 if not (self.iface.startswith(b"tap") or 

158 self.iface.startswith(b"tun")): 

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

160 "`tap` on BSD and Darwin") 

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

162 if not self.strip_packet_info: 

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

164 "packet info!") 

165 self.strip_packet_info = True 

166 else: 

167 raise NotImplementedError("TunTapInterface is not supported on " 

168 "this platform!") 

169 

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

171 

172 if LINUX: 

173 if self.mode_tun: 

174 flags = LINUX_IFF_TUN 

175 else: 

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

177 # the kernel sends the wrong information! 

178 # 

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

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

181 # 

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

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

184 # IFF_NO_PI. 

185 flags = LINUX_IFF_TAP | LINUX_IFF_NO_PI 

186 

187 tsetiff = raw(LinuxTunIfReq( 

188 ifrn_name=self.iface, 

189 ifru_flags=flags)) 

190 

191 ioctl(sock, LINUX_TUNSETIFF, tsetiff) 

192 

193 self.closed = False 

194 self.default_read_size = default_read_size 

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

196 

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

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

199 conf.L2listen, conf.L2socket or conf.L3socket. 

200 

201 """ 

202 return self 

203 

204 def recv_raw(self, x=None): 

205 if x is None: 

206 x = self.default_read_size 

207 

208 x += self.mtu_overhead 

209 

210 dat = self.ins.read(x) 

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

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

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

214 # decode of the payload data. 

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

216 

217 # Return the payload data only 

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

219 else: 

220 return r 

221 

222 def send(self, x): 

223 # type: (Packet) -> int 

224 if hasattr(x, "sent_time"): 

225 x.sent_time = time.time() 

226 

227 if self.kernel_packet_class == IPv46: 

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

229 # packets normally if we got IP or IPv6. 

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

231 x = IP() / x 

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

233 x = self.kernel_packet_class() / x 

234 

235 sx = raw(x) 

236 

237 try: 

238 r = self.outs.write(sx) 

239 self.outs.flush() 

240 return r 

241 except socket.error: 

242 log_runtime.error("%s send", 

243 self.__class__.__name__, exc_info=True)