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

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

1418 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 

6""" 

7IPv4 (Internet Protocol v4). 

8""" 

9 

10import time 

11import struct 

12import re 

13import random 

14import select 

15import socket 

16from collections import defaultdict 

17 

18from scapy.utils import checksum, do_graph, incremental_label, \ 

19 linehexdump, strxor, whois, colgen 

20from scapy.ansmachine import AnsweringMachine 

21from scapy.base_classes import Gen, Net, _ScopedIP 

22from scapy.data import ETH_P_IP, ETH_P_ALL, DLT_RAW, DLT_RAW_ALT, DLT_IPV4, \ 

23 IP_PROTOS, TCP_SERVICES, UDP_SERVICES 

24from scapy.layers.l2 import ( 

25 CookedLinux, 

26 Dot3, 

27 Ether, 

28 GRE, 

29 Loopback, 

30 SNAP, 

31 arpcachepoison, 

32 getmacbyip, 

33) 

34from scapy.compat import raw, chb, orb, bytes_encode, Optional 

35from scapy.config import conf 

36from scapy.fields import ( 

37 BitEnumField, 

38 BitField, 

39 ByteEnumField, 

40 ByteField, 

41 ConditionalField, 

42 DestField, 

43 Emph, 

44 FieldLenField, 

45 FieldListField, 

46 FlagsField, 

47 IPField, 

48 IP6Field, 

49 IntField, 

50 MayEnd, 

51 MultiEnumField, 

52 MultipleTypeField, 

53 PacketField, 

54 PacketListField, 

55 ShortEnumField, 

56 ShortField, 

57 SourceIPField, 

58 StrField, 

59 StrFixedLenField, 

60 StrLenField, 

61 TrailerField, 

62 XByteField, 

63 XShortField, 

64) 

65from scapy.packet import Packet, bind_layers, bind_bottom_up, NoPayload 

66from scapy.volatile import RandShort, RandInt, RandBin, RandNum, VolatileValue 

67from scapy.sendrecv import sr, sr1 

68from scapy.plist import _PacketList, PacketList, SndRcvList 

69from scapy.automaton import Automaton, ATMT 

70from scapy.error import log_runtime, warning 

71from scapy.pton_ntop import inet_pton 

72 

73import scapy.as_resolvers 

74 

75#################### 

76# IP Tools class # 

77#################### 

78 

79 

80class IPTools(object): 

81 """Add more powers to a class with an "src" attribute.""" 

82 __slots__ = [] 

83 

84 def whois(self): 

85 """whois the source and print the output""" 

86 print(whois(self.src).decode("utf8", "ignore")) 

87 

88 def _ttl(self): 

89 """Returns ttl or hlim, depending on the IP version""" 

90 return self.hlim if isinstance(self, scapy.layers.inet6.IPv6) else self.ttl # noqa: E501 

91 

92 def ottl(self): 

93 t = sorted([32, 64, 128, 255] + [self._ttl()]) 

94 return t[t.index(self._ttl()) + 1] 

95 

96 def hops(self): 

97 return self.ottl() - self._ttl() 

98 

99 

100_ip_options_names = {0: "end_of_list", 

101 1: "nop", 

102 2: "security", 

103 3: "loose_source_route", 

104 4: "timestamp", 

105 5: "extended_security", 

106 6: "commercial_security", 

107 7: "record_route", 

108 8: "stream_id", 

109 9: "strict_source_route", 

110 10: "experimental_measurement", 

111 11: "mtu_probe", 

112 12: "mtu_reply", 

113 13: "flow_control", 

114 14: "access_control", 

115 15: "encode", 

116 16: "imi_traffic_descriptor", 

117 17: "extended_IP", 

118 18: "traceroute", 

119 19: "address_extension", 

120 20: "router_alert", 

121 21: "selective_directed_broadcast_mode", 

122 23: "dynamic_packet_state", 

123 24: "upstream_multicast_packet", 

124 25: "quick_start", 

125 30: "rfc4727_experiment", 

126 } 

127 

128 

129class _IPOption_HDR(Packet): 

130 fields_desc = [BitField("copy_flag", 0, 1), 

131 BitEnumField("optclass", 0, 2, {0: "control", 2: "debug"}), 

132 BitEnumField("option", 0, 5, _ip_options_names)] 

133 

134 

135class IPOption(Packet): 

136 name = "IP Option" 

137 fields_desc = [_IPOption_HDR, 

138 FieldLenField("length", None, fmt="B", # Only option 0 and 1 have no length and value # noqa: E501 

139 length_of="value", adjust=lambda pkt, l:l + 2), # noqa: E501 

140 StrLenField("value", "", length_from=lambda pkt:pkt.length - 2)] # noqa: E501 

141 

142 def extract_padding(self, p): 

143 return b"", p 

144 

145 registered_ip_options = {} 

146 

147 @classmethod 

148 def register_variant(cls): 

149 cls.registered_ip_options[cls.option.default] = cls 

150 

151 @classmethod 

152 def dispatch_hook(cls, pkt=None, *args, **kargs): 

153 if pkt: 

154 opt = orb(pkt[0]) & 0x1f 

155 if opt in cls.registered_ip_options: 

156 return cls.registered_ip_options[opt] 

157 return cls 

158 

159 

160class IPOption_EOL(IPOption): 

161 name = "IP Option End of Options List" 

162 option = 0 

163 fields_desc = [_IPOption_HDR] 

164 

165 

166class IPOption_NOP(IPOption): 

167 name = "IP Option No Operation" 

168 option = 1 

169 fields_desc = [_IPOption_HDR] 

170 

171 

172class IPOption_Security(IPOption): 

173 name = "IP Option Security" 

174 copy_flag = 1 

175 option = 2 

176 fields_desc = [_IPOption_HDR, 

177 ByteField("length", 11), 

178 ShortField("security", 0), 

179 ShortField("compartment", 0), 

180 ShortField("handling_restrictions", 0), 

181 StrFixedLenField("transmission_control_code", "xxx", 3), 

182 ] 

183 

184 

185class IPOption_RR(IPOption): 

186 name = "IP Option Record Route" 

187 option = 7 

188 fields_desc = [_IPOption_HDR, 

189 FieldLenField("length", None, fmt="B", 

190 length_of="routers", adjust=lambda pkt, l:l + 3), # noqa: E501 

191 ByteField("pointer", 4), # 4 is first IP 

192 FieldListField("routers", [], IPField("", "0.0.0.0"), 

193 length_from=lambda pkt:pkt.length - 3) 

194 ] 

195 

196 def get_current_router(self): 

197 return self.routers[self.pointer // 4 - 1] 

198 

199 

200class IPOption_LSRR(IPOption_RR): 

201 name = "IP Option Loose Source and Record Route" 

202 copy_flag = 1 

203 option = 3 

204 

205 

206class IPOption_SSRR(IPOption_RR): 

207 name = "IP Option Strict Source and Record Route" 

208 copy_flag = 1 

209 option = 9 

210 

211 

212class IPOption_Stream_Id(IPOption): 

213 name = "IP Option Stream ID" 

214 copy_flag = 1 

215 option = 8 

216 fields_desc = [_IPOption_HDR, 

217 ByteField("length", 4), 

218 ShortField("security", 0), ] 

219 

220 

221class IPOption_MTU_Probe(IPOption): 

222 name = "IP Option MTU Probe" 

223 option = 11 

224 fields_desc = [_IPOption_HDR, 

225 ByteField("length", 4), 

226 ShortField("mtu", 0), ] 

227 

228 

229class IPOption_MTU_Reply(IPOption_MTU_Probe): 

230 name = "IP Option MTU Reply" 

231 option = 12 

232 

233 

234class IPOption_Traceroute(IPOption): 

235 name = "IP Option Traceroute" 

236 option = 18 

237 fields_desc = [_IPOption_HDR, 

238 ByteField("length", 12), 

239 ShortField("id", 0), 

240 ShortField("outbound_hops", 0), 

241 ShortField("return_hops", 0), 

242 IPField("originator_ip", "0.0.0.0")] 

243 

244 

245class IPOption_Timestamp(IPOption): 

246 name = "IP Option Timestamp" 

247 optclass = 2 

248 option = 4 

249 fields_desc = [_IPOption_HDR, 

250 ByteField("length", None), 

251 ByteField("pointer", 9), 

252 BitField("oflw", 0, 4), 

253 BitEnumField("flg", 1, 4, 

254 {0: "timestamp_only", 

255 1: "timestamp_and_ip_addr", 

256 3: "prespecified_ip_addr"}), 

257 ConditionalField(IPField("internet_address", "0.0.0.0"), 

258 lambda pkt: pkt.flg != 0), 

259 IntField('timestamp', 0)] 

260 

261 def post_build(self, p, pay): 

262 if self.length is None: 

263 p = p[:1] + struct.pack("!B", len(p)) + p[2:] 

264 return p + pay 

265 

266 

267class IPOption_Address_Extension(IPOption): 

268 name = "IP Option Address Extension" 

269 copy_flag = 1 

270 option = 19 

271 fields_desc = [_IPOption_HDR, 

272 ByteField("length", 10), 

273 IPField("src_ext", "0.0.0.0"), 

274 IPField("dst_ext", "0.0.0.0")] 

275 

276 

277class IPOption_Router_Alert(IPOption): 

278 name = "IP Option Router Alert" 

279 copy_flag = 1 

280 option = 20 

281 fields_desc = [_IPOption_HDR, 

282 ByteField("length", 4), 

283 ShortEnumField("alert", 0, {0: "router_shall_examine_packet"}), ] # noqa: E501 

284 

285 

286class IPOption_SDBM(IPOption): 

287 name = "IP Option Selective Directed Broadcast Mode" 

288 copy_flag = 1 

289 option = 21 

290 fields_desc = [_IPOption_HDR, 

291 FieldLenField("length", None, fmt="B", 

292 length_of="addresses", adjust=lambda pkt, l:l + 2), # noqa: E501 

293 FieldListField("addresses", [], IPField("", "0.0.0.0"), 

294 length_from=lambda pkt:pkt.length - 2) 

295 ] 

296 

297 

298TCPOptions = ( 

299 {0: ("EOL", None), 

300 1: ("NOP", None), 

301 2: ("MSS", "!H"), 

302 3: ("WScale", "!B"), 

303 4: ("SAckOK", None), 

304 5: ("SAck", "!"), 

305 8: ("Timestamp", "!II"), 

306 14: ("AltChkSum", "!BH"), 

307 15: ("AltChkSumOpt", None), 

308 19: ("MD5", "16s"), 

309 25: ("Mood", "!p"), 

310 28: ("UTO", "!H"), 

311 29: ("AO", None), 

312 34: ("TFO", "!II"), 

313 # RFC 3692 

314 # 253: ("Experiment", "!HHHH"), 

315 # 254: ("Experiment", "!HHHH"), 

316 }, 

317 {"EOL": 0, 

318 "NOP": 1, 

319 "MSS": 2, 

320 "WScale": 3, 

321 "SAckOK": 4, 

322 "SAck": 5, 

323 "Timestamp": 8, 

324 "AltChkSum": 14, 

325 "AltChkSumOpt": 15, 

326 "MD5": 19, 

327 "Mood": 25, 

328 "UTO": 28, 

329 "AO": 29, 

330 "TFO": 34, 

331 }) 

332 

333 

334class TCPAOValue(Packet): 

335 """Value of TCP-AO option""" 

336 fields_desc = [ 

337 ByteField("keyid", None), 

338 ByteField("rnextkeyid", None), 

339 StrLenField("mac", "", length_from=lambda p:len(p.original) - 2), 

340 ] 

341 

342 

343def get_tcpao(tcphdr): 

344 # type: (TCP) -> Optional[TCPAOValue] 

345 """Get the TCP-AO option from the header""" 

346 for optid, optval in tcphdr.options: 

347 if optid == 'AO': 

348 return optval 

349 return None 

350 

351 

352class RandTCPOptions(VolatileValue): 

353 def __init__(self, size=None): 

354 if size is None: 

355 size = RandNum(1, 5) 

356 self.size = size 

357 

358 def _fix(self): 

359 # Pseudo-Random amount of options 

360 # Random ("NAME", fmt) 

361 rand_patterns = [ 

362 random.choice(list( 

363 (opt, fmt) for opt, fmt in TCPOptions[0].values() 

364 if opt != 'EOL' 

365 )) 

366 for _ in range(self.size) 

367 ] 

368 rand_vals = [] 

369 for oname, fmt in rand_patterns: 

370 if fmt is None: 

371 rand_vals.append((oname, b'')) 

372 else: 

373 # Process the fmt arguments 1 by 1 

374 structs = re.findall(r"!?([bBhHiIlLqQfdpP]|\d+[spx])", fmt) 

375 rval = [] 

376 for stru in structs: 

377 stru = "!" + stru 

378 if "s" in stru or "p" in stru: # str / chr 

379 v = bytes(RandBin(struct.calcsize(stru))) 

380 else: # int 

381 _size = struct.calcsize(stru) 

382 v = random.randint(0, 2 ** (8 * _size) - 1) 

383 rval.append(v) 

384 rand_vals.append((oname, tuple(rval))) 

385 return rand_vals 

386 

387 def __bytes__(self): 

388 return TCPOptionsField.i2m(None, None, self._fix()) 

389 

390 

391class TCPOptionsField(StrField): 

392 islist = 1 

393 

394 def getfield(self, pkt, s): 

395 opsz = (pkt.dataofs - 5) * 4 

396 if opsz < 0: 

397 log_runtime.info( 

398 "bad dataofs (%i). Assuming dataofs=5", pkt.dataofs 

399 ) 

400 opsz = 0 

401 return s[opsz:], self.m2i(pkt, s[:opsz]) 

402 

403 def m2i(self, pkt, x): 

404 opt = [] 

405 while x: 

406 onum = orb(x[0]) 

407 if onum == 0: 

408 opt.append(("EOL", None)) 

409 break 

410 if onum == 1: 

411 opt.append(("NOP", None)) 

412 x = x[1:] 

413 continue 

414 try: 

415 olen = orb(x[1]) 

416 except IndexError: 

417 olen = 0 

418 if olen < 2: 

419 log_runtime.info( 

420 "Malformed TCP option (announced length is %i)", olen 

421 ) 

422 olen = 2 

423 oval = x[2:olen] 

424 if onum in TCPOptions[0]: 

425 oname, ofmt = TCPOptions[0][onum] 

426 if onum == 5: # SAck 

427 ofmt += "%iI" % (len(oval) // 4) 

428 if onum == 29: # AO 

429 oval = TCPAOValue(oval) 

430 if ofmt and struct.calcsize(ofmt) == len(oval): 

431 oval = struct.unpack(ofmt, oval) 

432 if len(oval) == 1: 

433 oval = oval[0] 

434 opt.append((oname, oval)) 

435 else: 

436 opt.append((onum, oval)) 

437 x = x[olen:] 

438 return opt 

439 

440 def i2h(self, pkt, x): 

441 if not x: 

442 return [] 

443 return x 

444 

445 def i2m(self, pkt, x): 

446 opt = b"" 

447 for oname, oval in x: 

448 # We check for a (0, b'') or (1, b'') option first 

449 oname = {0: "EOL", 1: "NOP"}.get(oname, oname) 

450 if isinstance(oname, str): 

451 if oname == "NOP": 

452 opt += b"\x01" 

453 continue 

454 elif oname == "EOL": 

455 opt += b"\x00" 

456 continue 

457 elif oname in TCPOptions[1]: 

458 onum = TCPOptions[1][oname] 

459 ofmt = TCPOptions[0][onum][1] 

460 if onum == 5: # SAck 

461 ofmt += "%iI" % len(oval) 

462 _test_isinstance = not isinstance(oval, (bytes, str)) 

463 if ofmt is not None and (_test_isinstance or "s" in ofmt): 

464 if not isinstance(oval, tuple): 

465 oval = (oval,) 

466 oval = struct.pack(ofmt, *oval) 

467 if onum == 29: # AO 

468 oval = bytes(oval) 

469 else: 

470 warning("Option [%s] unknown. Skipped.", oname) 

471 continue 

472 else: 

473 onum = oname 

474 if not isinstance(onum, int): 

475 warning("Invalid option number [%i]" % onum) 

476 continue 

477 if not isinstance(oval, (bytes, str)): 

478 warning("Option [%i] is not bytes." % onum) 

479 continue 

480 if isinstance(oval, str): 

481 oval = bytes_encode(oval) 

482 opt += chb(onum) + chb(2 + len(oval)) + oval 

483 return opt + b"\x00" * (3 - ((len(opt) + 3) % 4)) # Padding 

484 

485 def randval(self): 

486 return RandTCPOptions() 

487 

488 

489class ICMPTimeStampField(IntField): 

490 re_hmsm = re.compile("([0-2]?[0-9])[Hh:](([0-5]?[0-9])([Mm:]([0-5]?[0-9])([sS:.]([0-9]{0,3}))?)?)?$") # noqa: E501 

491 

492 def i2repr(self, pkt, val): 

493 if val is None: 

494 return "--" 

495 else: 

496 sec, milli = divmod(val, 1000) 

497 min, sec = divmod(sec, 60) 

498 hour, min = divmod(min, 60) 

499 return "%d:%d:%d.%d" % (hour, min, sec, int(milli)) 

500 

501 def any2i(self, pkt, val): 

502 if isinstance(val, str): 

503 hmsms = self.re_hmsm.match(val) 

504 if hmsms: 

505 h, _, m, _, s, _, ms = hmsms.groups() 

506 ms = int(((ms or "") + "000")[:3]) 

507 val = ((int(h) * 60 + int(m or 0)) * 60 + int(s or 0)) * 1000 + ms # noqa: E501 

508 else: 

509 val = 0 

510 elif val is None: 

511 val = int((time.time() % (24 * 60 * 60)) * 1000) 

512 return val 

513 

514 

515class DestIPField(IPField, DestField): 

516 bindings = {} 

517 

518 def __init__(self, name, default): 

519 IPField.__init__(self, name, None) 

520 DestField.__init__(self, name, default) 

521 

522 def i2m(self, pkt, x): 

523 if x is None: 

524 x = self.dst_from_pkt(pkt) 

525 return IPField.i2m(self, pkt, x) 

526 

527 def i2h(self, pkt, x): 

528 if x is None: 

529 x = self.dst_from_pkt(pkt) 

530 return IPField.i2h(self, pkt, x) 

531 

532 

533class IP(Packet, IPTools): 

534 name = "IP" 

535 fields_desc = [BitField("version", 4, 4), 

536 BitField("ihl", None, 4), 

537 XByteField("tos", 0), 

538 ShortField("len", None), 

539 ShortField("id", 1), 

540 FlagsField("flags", 0, 3, ["MF", "DF", "evil"]), 

541 BitField("frag", 0, 13), 

542 ByteField("ttl", 64), 

543 ByteEnumField("proto", 0, IP_PROTOS), 

544 XShortField("chksum", None), 

545 # IPField("src", "127.0.0.1"), 

546 Emph(SourceIPField("src")), 

547 Emph(DestIPField("dst", "127.0.0.1")), 

548 PacketListField("options", [], IPOption, length_from=lambda p:p.ihl * 4 - 20)] # noqa: E501 

549 

550 def post_build(self, p, pay): 

551 ihl = self.ihl 

552 p += b"\0" * ((-len(p)) % 4) # pad IP options if needed 

553 if ihl is None: 

554 ihl = len(p) // 4 

555 p = chb(((self.version & 0xf) << 4) | ihl & 0x0f) + p[1:] 

556 if self.len is None: 

557 tmp_len = len(p) + len(pay) 

558 p = p[:2] + struct.pack("!H", tmp_len) + p[4:] 

559 if self.chksum is None: 

560 ck = checksum(p) 

561 p = p[:10] + chb(ck >> 8) + chb(ck & 0xff) + p[12:] 

562 return p + pay 

563 

564 def extract_padding(self, s): 

565 tmp_len = self.len - (self.ihl << 2) 

566 if tmp_len < 0: 

567 return s, b"" 

568 return s[:tmp_len], s[tmp_len:] 

569 

570 def route(self): 

571 dst = self.dst 

572 scope = None 

573 if isinstance(dst, (Net, _ScopedIP)): 

574 scope = dst.scope 

575 if isinstance(dst, (Gen, list)): 

576 dst = next(iter(dst)) 

577 if conf.route is None: 

578 # unused import, only to initialize conf.route 

579 import scapy.route # noqa: F401 

580 return conf.route.route(dst, dev=scope) 

581 

582 def hashret(self): 

583 if ((self.proto == socket.IPPROTO_ICMP) and 

584 (isinstance(self.payload, ICMP)) and 

585 (self.payload.type in [3, 4, 5, 11, 12])): 

586 return self.payload.payload.hashret() 

587 if not conf.checkIPinIP and self.proto in [4, 41]: # IP, IPv6 

588 return self.payload.hashret() 

589 if self.dst == "224.0.0.251": # mDNS 

590 return struct.pack("B", self.proto) + self.payload.hashret() 

591 if conf.checkIPsrc and conf.checkIPaddr: 

592 return (strxor(inet_pton(socket.AF_INET, self.src), 

593 inet_pton(socket.AF_INET, self.dst)) + 

594 struct.pack("B", self.proto) + self.payload.hashret()) 

595 return struct.pack("B", self.proto) + self.payload.hashret() 

596 

597 def answers(self, other): 

598 if not conf.checkIPinIP: # skip IP in IP and IPv6 in IP 

599 if self.proto in [4, 41]: 

600 return self.payload.answers(other) 

601 if isinstance(other, IP) and other.proto in [4, 41]: 

602 return self.answers(other.payload) 

603 if conf.ipv6_enabled \ 

604 and isinstance(other, scapy.layers.inet6.IPv6) \ 

605 and other.nh in [4, 41]: 

606 return self.answers(other.payload) 

607 if not isinstance(other, IP): 

608 return 0 

609 if conf.checkIPaddr: 

610 if other.dst == "224.0.0.251" and self.dst == "224.0.0.251": # mDNS # noqa: E501 

611 return self.payload.answers(other.payload) 

612 elif (self.dst != other.src): 

613 return 0 

614 if ((self.proto == socket.IPPROTO_ICMP) and 

615 (isinstance(self.payload, ICMP)) and 

616 (self.payload.type in [3, 4, 5, 11, 12])): 

617 # ICMP error message 

618 return self.payload.payload.answers(other) 

619 

620 else: 

621 if ((conf.checkIPaddr and (self.src != other.dst)) or 

622 (self.proto != other.proto)): 

623 return 0 

624 return self.payload.answers(other.payload) 

625 

626 def mysummary(self): 

627 s = self.sprintf("%IP.src% > %IP.dst% %IP.proto%") 

628 if self.frag: 

629 s += " frag:%i" % self.frag 

630 return s 

631 

632 def fragment(self, fragsize=1480): 

633 """Fragment IP datagrams""" 

634 return fragment(self, fragsize=fragsize) 

635 

636 

637def in4_pseudoheader(proto, u, plen): 

638 # type: (int, IP, int) -> bytes 

639 """IPv4 Pseudo Header as defined in RFC793 as bytes 

640 

641 :param proto: value of upper layer protocol 

642 :param u: IP layer instance 

643 :param plen: the length of the upper layer and payload 

644 """ 

645 u = u.copy() 

646 if u.len is not None: 

647 if u.ihl is None: 

648 olen = sum(len(x) for x in u.options) 

649 ihl = 5 + olen // 4 + (1 if olen % 4 else 0) 

650 else: 

651 ihl = u.ihl 

652 ln = max(u.len - 4 * ihl, 0) 

653 else: 

654 ln = plen 

655 

656 # Filter out IPOption_LSRR and IPOption_SSRR 

657 sr_options = [opt for opt in u.options if isinstance(opt, IPOption_LSRR) or 

658 isinstance(opt, IPOption_SSRR)] 

659 len_sr_options = len(sr_options) 

660 if len_sr_options == 1 and len(sr_options[0].routers): 

661 # The checksum must be computed using the final 

662 # destination address 

663 u.dst = sr_options[0].routers[-1] 

664 elif len_sr_options > 1: 

665 message = "Found %d Source Routing Options! " 

666 message += "Falling back to IP.dst for checksum computation." 

667 warning(message, len_sr_options) 

668 

669 return struct.pack("!4s4sHH", 

670 inet_pton(socket.AF_INET, u.src), 

671 inet_pton(socket.AF_INET, u.dst), 

672 proto, 

673 ln) 

674 

675 

676def in4_chksum(proto, u, p): 

677 # type: (int, IP, bytes) -> int 

678 """IPv4 Pseudo Header checksum as defined in RFC793 

679 

680 :param proto: value of upper layer protocol 

681 :param u: upper layer instance 

682 :param p: the payload of the upper layer provided as a string 

683 """ 

684 if not isinstance(u, IP): 

685 warning("No IP underlayer to compute checksum. Leaving null.") 

686 return 0 

687 psdhdr = in4_pseudoheader(proto, u, len(p)) 

688 return checksum(psdhdr + p) 

689 

690 

691def _is_ipv6_layer(p): 

692 # type: (Packet) -> bytes 

693 return (isinstance(p, scapy.layers.inet6.IPv6) or 

694 isinstance(p, scapy.layers.inet6._IPv6ExtHdr)) 

695 

696 

697def tcp_pseudoheader(tcp): 

698 # type: (TCP) -> bytes 

699 """Pseudoheader of a TCP packet as bytes 

700 

701 Requires underlayer to be either IP or IPv6 

702 """ 

703 if isinstance(tcp.underlayer, IP): 

704 plen = len(bytes(tcp)) 

705 return in4_pseudoheader(socket.IPPROTO_TCP, tcp.underlayer, plen) 

706 elif conf.ipv6_enabled and _is_ipv6_layer(tcp.underlayer): 

707 plen = len(bytes(tcp)) 

708 return raw(scapy.layers.inet6.in6_pseudoheader( 

709 socket.IPPROTO_TCP, tcp.underlayer, plen)) 

710 else: 

711 raise ValueError("TCP packet does not have IP or IPv6 underlayer") 

712 

713 

714def calc_tcp_md5_hash(tcp, key): 

715 # type: (TCP, bytes) -> bytes 

716 """Calculate TCP-MD5 hash from packet and return a 16-byte string""" 

717 import hashlib 

718 

719 h = hashlib.md5() # nosec 

720 tcp_bytes = bytes(tcp) 

721 h.update(tcp_pseudoheader(tcp)) 

722 h.update(tcp_bytes[:16]) 

723 h.update(b"\x00\x00") 

724 h.update(tcp_bytes[18:]) 

725 h.update(key) 

726 

727 return h.digest() 

728 

729 

730def sign_tcp_md5(tcp, key): 

731 # type: (TCP, bytes) -> None 

732 """Append TCP-MD5 signature to tcp packet""" 

733 sig = calc_tcp_md5_hash(tcp, key) 

734 tcp.options = tcp.options + [('MD5', sig)] 

735 

736 

737class TCP(Packet): 

738 name = "TCP" 

739 fields_desc = [ShortEnumField("sport", 20, TCP_SERVICES), 

740 ShortEnumField("dport", 80, TCP_SERVICES), 

741 IntField("seq", 0), 

742 IntField("ack", 0), 

743 BitField("dataofs", None, 4), 

744 BitField("reserved", 0, 3), 

745 FlagsField("flags", 0x2, 9, "FSRPAUECN"), 

746 ShortField("window", 8192), 

747 XShortField("chksum", None), 

748 ShortField("urgptr", 0), 

749 TCPOptionsField("options", "")] 

750 

751 def post_build(self, p, pay): 

752 p += pay 

753 dataofs = self.dataofs 

754 if dataofs is None: 

755 opt_len = len(self.get_field("options").i2m(self, self.options)) 

756 dataofs = 5 + ((opt_len + 3) // 4) 

757 dataofs = (dataofs << 4) | orb(p[12]) & 0x0f 

758 p = p[:12] + chb(dataofs & 0xff) + p[13:] 

759 if self.chksum is None: 

760 if isinstance(self.underlayer, IP): 

761 ck = in4_chksum(socket.IPPROTO_TCP, self.underlayer, p) 

762 p = p[:16] + struct.pack("!H", ck) + p[18:] 

763 elif conf.ipv6_enabled and isinstance(self.underlayer, scapy.layers.inet6.IPv6) or isinstance(self.underlayer, scapy.layers.inet6._IPv6ExtHdr): # noqa: E501 

764 ck = scapy.layers.inet6.in6_chksum(socket.IPPROTO_TCP, self.underlayer, p) # noqa: E501 

765 p = p[:16] + struct.pack("!H", ck) + p[18:] 

766 else: 

767 log_runtime.info( 

768 "No IP underlayer to compute checksum. Leaving null." 

769 ) 

770 return p 

771 

772 def hashret(self): 

773 if conf.checkIPsrc: 

774 return struct.pack("H", self.sport ^ self.dport) + self.payload.hashret() # noqa: E501 

775 else: 

776 return self.payload.hashret() 

777 

778 def answers(self, other): 

779 if not isinstance(other, TCP): 

780 return 0 

781 # RST packets don't get answers 

782 if other.flags.R: 

783 return 0 

784 # We do not support the four-way handshakes with the SYN+ACK 

785 # answer split in two packets (one ACK and one SYN): in that 

786 # case the ACK will be seen as an answer, but not the SYN. 

787 if self.flags.S: 

788 # SYN packets without ACK are not answers 

789 if not self.flags.A: 

790 return 0 

791 # SYN+ACK packets answer SYN packets 

792 if not other.flags.S: 

793 return 0 

794 if conf.checkIPsrc: 

795 if not ((self.sport == other.dport) and 

796 (self.dport == other.sport)): 

797 return 0 

798 # Do not check ack value for SYN packets without ACK 

799 if not (other.flags.S and not other.flags.A) \ 

800 and abs(other.ack - self.seq) > 2: 

801 return 0 

802 # Do not check ack value for RST packets without ACK 

803 if self.flags.R and not self.flags.A: 

804 return 1 

805 if abs(other.seq - self.ack) > 2 + len(other.payload): 

806 return 0 

807 return 1 

808 

809 def mysummary(self): 

810 if isinstance(self.underlayer, IP): 

811 return self.underlayer.sprintf("TCP %IP.src%:%TCP.sport% > %IP.dst%:%TCP.dport% %TCP.flags%") # noqa: E501 

812 elif conf.ipv6_enabled and isinstance(self.underlayer, scapy.layers.inet6.IPv6): # noqa: E501 

813 return self.underlayer.sprintf("TCP %IPv6.src%:%TCP.sport% > %IPv6.dst%:%TCP.dport% %TCP.flags%") # noqa: E501 

814 else: 

815 return self.sprintf("TCP %TCP.sport% > %TCP.dport% %TCP.flags%") 

816 

817 

818class UDP(Packet): 

819 name = "UDP" 

820 fields_desc = [ShortEnumField("sport", 53, UDP_SERVICES), 

821 ShortEnumField("dport", 53, UDP_SERVICES), 

822 ShortField("len", None), 

823 XShortField("chksum", None), ] 

824 

825 def post_build(self, p, pay): 

826 p += pay 

827 tmp_len = self.len 

828 if tmp_len is None: 

829 tmp_len = len(p) 

830 p = p[:4] + struct.pack("!H", tmp_len) + p[6:] 

831 if self.chksum is None: 

832 if isinstance(self.underlayer, IP): 

833 ck = in4_chksum(socket.IPPROTO_UDP, self.underlayer, p) 

834 # According to RFC768 if the result checksum is 0, it should be set to 0xFFFF # noqa: E501 

835 if ck == 0: 

836 ck = 0xFFFF 

837 p = p[:6] + struct.pack("!H", ck) + p[8:] 

838 elif isinstance(self.underlayer, scapy.layers.inet6.IPv6) or isinstance(self.underlayer, scapy.layers.inet6._IPv6ExtHdr): # noqa: E501 

839 ck = scapy.layers.inet6.in6_chksum(socket.IPPROTO_UDP, self.underlayer, p) # noqa: E501 

840 # According to RFC2460 if the result checksum is 0, it should be set to 0xFFFF # noqa: E501 

841 if ck == 0: 

842 ck = 0xFFFF 

843 p = p[:6] + struct.pack("!H", ck) + p[8:] 

844 else: 

845 log_runtime.info( 

846 "No IP underlayer to compute checksum. Leaving null." 

847 ) 

848 return p 

849 

850 def extract_padding(self, s): 

851 tmp_len = self.len - 8 

852 return s[:tmp_len], s[tmp_len:] 

853 

854 def hashret(self): 

855 return self.payload.hashret() 

856 

857 def answers(self, other): 

858 if not isinstance(other, UDP): 

859 return 0 

860 if conf.checkIPsrc: 

861 if self.dport != other.sport: 

862 return 0 

863 return self.payload.answers(other.payload) 

864 

865 def mysummary(self): 

866 if isinstance(self.underlayer, IP): 

867 return self.underlayer.sprintf("UDP %IP.src%:%UDP.sport% > %IP.dst%:%UDP.dport%") # noqa: E501 

868 elif isinstance(self.underlayer, scapy.layers.inet6.IPv6): 

869 return self.underlayer.sprintf("UDP %IPv6.src%:%UDP.sport% > %IPv6.dst%:%UDP.dport%") # noqa: E501 

870 else: 

871 return self.sprintf("UDP %UDP.sport% > %UDP.dport%") 

872 

873 

874# RFC 4884 ICMP extensions 

875_ICMP_classnums = { 

876 # https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml#icmp-parameters-ext-classes 

877 1: "MPLS", 

878 2: "Interface Information", 

879 3: "Interface Identification", 

880 4: "Extended Information", 

881} 

882 

883 

884class ICMPExtension_Object(Packet): 

885 name = "ICMP Extension Object" 

886 show_indent = 0 

887 fields_desc = [ 

888 ShortField("len", None), 

889 ByteEnumField("classnum", 0, _ICMP_classnums), 

890 ByteField("classtype", 0), 

891 ] 

892 

893 def post_build(self, p, pay): 

894 if self.len is None: 

895 tmp_len = len(p) + len(pay) 

896 p = struct.pack("!H", tmp_len) + p[2:] 

897 return p + pay 

898 

899 registered_icmp_exts = {} 

900 

901 @classmethod 

902 def register_variant(cls): 

903 cls.registered_icmp_exts[cls.classnum.default] = cls 

904 

905 @classmethod 

906 def dispatch_hook(cls, _pkt=None, *args, **kargs): 

907 if _pkt and len(_pkt) >= 4: 

908 classnum = _pkt[2] 

909 if classnum in cls.registered_icmp_exts: 

910 return cls.registered_icmp_exts[classnum] 

911 return cls 

912 

913 

914class ICMPExtension_InterfaceInformation(ICMPExtension_Object): 

915 name = "ICMP Extension Object - Interface Information Object (RFC5837)" 

916 

917 fields_desc = [ 

918 ShortField("len", None), 

919 ByteEnumField("classnum", 2, _ICMP_classnums), 

920 BitField("classtype", 0, 2), 

921 BitField("reserved", 0, 2), 

922 BitField("has_ifindex", 0, 1), 

923 BitField("has_ipaddr", 0, 1), 

924 BitField("has_ifname", 0, 1), 

925 BitField("has_mtu", 0, 1), 

926 ConditionalField(IntField("ifindex", None), lambda pkt: pkt.has_ifindex == 1), 

927 ConditionalField(ShortField("afi", None), lambda pkt: pkt.has_ipaddr == 1), 

928 ConditionalField(ShortField("reserved2", 0), lambda pkt: pkt.has_ipaddr == 1), 

929 ConditionalField(IPField("ip4", None), lambda pkt: pkt.afi == 1), 

930 ConditionalField(IP6Field("ip6", None), lambda pkt: pkt.afi == 2), 

931 ConditionalField( 

932 FieldLenField("ifname_len", None, fmt="B", length_of="ifname"), 

933 lambda pkt: pkt.has_ifname == 1, 

934 ), 

935 ConditionalField( 

936 StrLenField("ifname", None, length_from=lambda pkt: pkt.ifname_len), 

937 lambda pkt: pkt.has_ifname == 1, 

938 ), 

939 ConditionalField(IntField("mtu", None), lambda pkt: pkt.has_mtu == 1), 

940 ] 

941 

942 def self_build(self, **kwargs): 

943 if self.afi is None: 

944 if self.ip4 is not None: 

945 self.afi = 1 

946 elif self.ip6 is not None: 

947 self.afi = 2 

948 return ICMPExtension_Object.self_build(self, **kwargs) 

949 

950 

951class ICMPExtension_Header(Packet): 

952 r""" 

953 ICMP Extension per RFC4884. 

954 

955 Example:: 

956 

957 pkt = IP(dst="127.0.0.1", src="127.0.0.1") / ICMP( 

958 type="time-exceeded", 

959 code="ttl-zero-during-transit", 

960 ext=ICMPExtension_Header() / ICMPExtension_InterfaceInformation( 

961 has_ifindex=1, 

962 has_ipaddr=1, 

963 has_ifname=1, 

964 ip4="10.10.10.10", 

965 ifname="hey", 

966 ) 

967 ) / IPerror(src="12.4.4.4", dst="12.1.1.1") / \ 

968 UDPerror(sport=42315, dport=33440) / \ 

969 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 

970 """ 

971 

972 name = "ICMP Extension Header (RFC4884)" 

973 show_indent = 0 

974 fields_desc = [ 

975 BitField("version", 2, 4), 

976 BitField("reserved", 0, 12), 

977 XShortField("chksum", None), 

978 ] 

979 

980 _min_ieo_len = len(ICMPExtension_Object()) 

981 

982 def post_build(self, p, pay): 

983 p += pay 

984 if self.chksum is None: 

985 ck = checksum(p) 

986 p = p[:2] + chb(ck >> 8) + chb(ck & 0xFF) + p[4:] 

987 return p 

988 

989 def guess_payload_class(self, payload): 

990 if len(payload) < self._min_ieo_len: 

991 return Packet.guess_payload_class(self, payload) 

992 return ICMPExtension_Object 

993 

994 

995class _ICMPExtensionField(TrailerField): 

996 # We use a TrailerField for building only. Dissection is normal. 

997 

998 def __init__(self): 

999 super(_ICMPExtensionField, self).__init__( 

1000 PacketField( 

1001 "ext", 

1002 None, 

1003 ICMPExtension_Header, 

1004 ), 

1005 ) 

1006 

1007 def getfield(self, pkt, s): 

1008 # RFC4884 section 5.2 says if the ICMP packet length 

1009 # is >144 then ICMP extensions start at byte 137. 

1010 if len(pkt.original) < 144: 

1011 return s, None 

1012 offset = 136 + len(s) - len(pkt.original) 

1013 data = s[offset:] 

1014 # Validate checksum 

1015 if checksum(data) == data[3:5]: 

1016 return s, None # failed 

1017 # Dissect 

1018 return s[:offset], ICMPExtension_Header(data) 

1019 

1020 def addfield(self, pkt, s, val): 

1021 if val is None: 

1022 return s 

1023 data = bytes(val) 

1024 # Calc how much padding we need, not how much we deserve 

1025 pad = 136 - len(pkt.payload) - len(s) 

1026 if pad < 0: 

1027 warning("ICMPExtension_Header is after the 136th octet of ICMP.") 

1028 return data 

1029 return super(_ICMPExtensionField, self).addfield(pkt, s, b"\x00" * pad + data) 

1030 

1031 

1032class _ICMPExtensionPadField(TrailerField): 

1033 def __init__(self): 

1034 super(_ICMPExtensionPadField, self).__init__( 

1035 StrFixedLenField("extpad", "", length=0) 

1036 ) 

1037 

1038 def i2repr(self, pkt, s): 

1039 if s and s == b"\x00" * len(s): 

1040 return "b'' (%s octets)" % len(s) 

1041 return self.fld.i2repr(pkt, s) 

1042 

1043 

1044def _ICMP_extpad_post_dissection(self, pkt): 

1045 # If we have padding, put it in 'extpad' for re-build 

1046 if pkt.ext: 

1047 pad = pkt.lastlayer() 

1048 if isinstance(pad, conf.padding_layer): 

1049 pad.underlayer.remove_payload() 

1050 pkt.extpad = pad.load 

1051 

1052 

1053icmptypes = {0: "echo-reply", 

1054 3: "dest-unreach", 

1055 4: "source-quench", 

1056 5: "redirect", 

1057 8: "echo-request", 

1058 9: "router-advertisement", 

1059 10: "router-solicitation", 

1060 11: "time-exceeded", 

1061 12: "parameter-problem", 

1062 13: "timestamp-request", 

1063 14: "timestamp-reply", 

1064 15: "information-request", 

1065 16: "information-response", 

1066 17: "address-mask-request", 

1067 18: "address-mask-reply", 

1068 30: "traceroute", 

1069 31: "datagram-conversion-error", 

1070 32: "mobile-host-redirect", 

1071 33: "ipv6-where-are-you", 

1072 34: "ipv6-i-am-here", 

1073 35: "mobile-registration-request", 

1074 36: "mobile-registration-reply", 

1075 37: "domain-name-request", 

1076 38: "domain-name-reply", 

1077 39: "skip", 

1078 40: "photuris"} 

1079 

1080 

1081icmpcodes = {3: {0: "network-unreachable", 

1082 1: "host-unreachable", 

1083 2: "protocol-unreachable", 

1084 3: "port-unreachable", 

1085 4: "fragmentation-needed", 

1086 5: "source-route-failed", 

1087 6: "network-unknown", 

1088 7: "host-unknown", 

1089 9: "network-prohibited", 

1090 10: "host-prohibited", 

1091 11: "TOS-network-unreachable", 

1092 12: "TOS-host-unreachable", 

1093 13: "communication-prohibited", 

1094 14: "host-precedence-violation", 

1095 15: "precedence-cutoff", }, 

1096 5: {0: "network-redirect", 

1097 1: "host-redirect", 

1098 2: "TOS-network-redirect", 

1099 3: "TOS-host-redirect", }, 

1100 11: {0: "ttl-zero-during-transit", 

1101 1: "ttl-zero-during-reassembly", }, 

1102 12: {0: "ip-header-bad", 

1103 1: "required-option-missing", }, 

1104 40: {0: "bad-spi", 

1105 1: "authentication-failed", 

1106 2: "decompression-failed", 

1107 3: "decryption-failed", 

1108 4: "need-authentification", 

1109 5: "need-authorization", }, } 

1110 

1111 

1112_icmp_answers = [ 

1113 (8, 0), 

1114 (13, 14), 

1115 (15, 16), 

1116 (17, 18), 

1117 (33, 34), 

1118 (35, 36), 

1119 (37, 38), 

1120] 

1121 

1122icmp_id_seq_types = [0, 8, 13, 14, 15, 16, 17, 18, 37, 38] 

1123 

1124 

1125class ICMP(Packet): 

1126 name = "ICMP" 

1127 fields_desc = [ 

1128 ByteEnumField("type", 8, icmptypes), 

1129 MultiEnumField("code", 0, icmpcodes, 

1130 depends_on=lambda pkt:pkt.type, fmt="B"), 

1131 XShortField("chksum", None), 

1132 ConditionalField( 

1133 XShortField("id", 0), 

1134 lambda pkt: pkt.type in icmp_id_seq_types 

1135 ), 

1136 ConditionalField( 

1137 XShortField("seq", 0), 

1138 lambda pkt: pkt.type in icmp_id_seq_types 

1139 ), 

1140 ConditionalField( 

1141 # Timestamp only (RFC792) 

1142 ICMPTimeStampField("ts_ori", None), 

1143 lambda pkt: pkt.type in [13, 14] 

1144 ), 

1145 ConditionalField( 

1146 # Timestamp only (RFC792) 

1147 ICMPTimeStampField("ts_rx", None), 

1148 lambda pkt: pkt.type in [13, 14] 

1149 ), 

1150 ConditionalField( 

1151 # Timestamp only (RFC792) 

1152 ICMPTimeStampField("ts_tx", None), 

1153 lambda pkt: pkt.type in [13, 14] 

1154 ), 

1155 ConditionalField( 

1156 # Redirect only (RFC792) 

1157 IPField("gw", "0.0.0.0"), 

1158 lambda pkt: pkt.type == 5 

1159 ), 

1160 ConditionalField( 

1161 # Parameter problem only (RFC792) 

1162 ByteField("ptr", 0), 

1163 lambda pkt: pkt.type == 12 

1164 ), 

1165 ConditionalField( 

1166 ByteField("reserved", 0), 

1167 lambda pkt: pkt.type in [3, 11] 

1168 ), 

1169 ConditionalField( 

1170 ByteField("length", 0), 

1171 lambda pkt: pkt.type in [3, 11, 12] 

1172 ), 

1173 ConditionalField( 

1174 IPField("addr_mask", "0.0.0.0"), 

1175 lambda pkt: pkt.type in [17, 18] 

1176 ), 

1177 ConditionalField( 

1178 ShortField("nexthopmtu", 0), 

1179 lambda pkt: pkt.type == 3 

1180 ), 

1181 MultipleTypeField( 

1182 [ 

1183 (ShortField("unused", 0), 

1184 lambda pkt:pkt.type in [11, 12]), 

1185 (IntField("unused", 0), 

1186 lambda pkt:pkt.type not in [0, 3, 5, 8, 11, 12, 

1187 13, 14, 15, 16, 17, 

1188 18]) 

1189 ], 

1190 StrFixedLenField("unused", "", length=0), 

1191 ), 

1192 # RFC4884 ICMP extension 

1193 ConditionalField( 

1194 _ICMPExtensionPadField(), 

1195 lambda pkt: pkt.type in [3, 11, 12], 

1196 ), 

1197 ConditionalField( 

1198 _ICMPExtensionField(), 

1199 lambda pkt: pkt.type in [3, 11, 12], 

1200 ), 

1201 ] 

1202 

1203 # To handle extpad 

1204 post_dissection = _ICMP_extpad_post_dissection 

1205 

1206 def post_build(self, p, pay): 

1207 p += pay 

1208 if self.chksum is None: 

1209 ck = checksum(p) 

1210 p = p[:2] + chb(ck >> 8) + chb(ck & 0xff) + p[4:] 

1211 return p 

1212 

1213 def hashret(self): 

1214 if self.type in icmp_id_seq_types: 

1215 return struct.pack("HH", self.id, self.seq) + self.payload.hashret() # noqa: E501 

1216 return self.payload.hashret() 

1217 

1218 def answers(self, other): 

1219 if not isinstance(other, ICMP): 

1220 return 0 

1221 if ((other.type, self.type) in _icmp_answers and 

1222 self.id == other.id and 

1223 self.seq == other.seq): 

1224 return 1 

1225 return 0 

1226 

1227 def guess_payload_class(self, payload): 

1228 if self.type in [3, 4, 5, 11, 12]: 

1229 return IPerror 

1230 else: 

1231 return None 

1232 

1233 def mysummary(self): 

1234 extra = "" 

1235 if self.ext: 

1236 extra = self.ext.payload.sprintf(" ext:%classnum%") 

1237 if isinstance(self.underlayer, IP): 

1238 return self.underlayer.sprintf( 

1239 "ICMP %IP.src% > %IP.dst% %ICMP.type% %ICMP.code%" 

1240 ) + extra 

1241 else: 

1242 return self.sprintf("ICMP %ICMP.type% %ICMP.code%") + extra 

1243 

1244 

1245# IP / TCP / UDP error packets 

1246 

1247class IPerror(IP): 

1248 name = "IP in ICMP" 

1249 

1250 def answers(self, other): 

1251 if not isinstance(other, IP): 

1252 return 0 

1253 

1254 # Check if IP addresses match 

1255 test_IPsrc = not conf.checkIPsrc or self.src == other.src 

1256 test_IPdst = self.dst == other.dst 

1257 

1258 # Check if IP ids match 

1259 test_IPid = not conf.checkIPID or self.id == other.id 

1260 test_IPid |= conf.checkIPID and self.id == socket.htons(other.id) 

1261 

1262 # Check if IP protocols match 

1263 test_IPproto = self.proto == other.proto 

1264 

1265 if not (test_IPsrc and test_IPdst and test_IPid and test_IPproto): 

1266 return 0 

1267 

1268 return self.payload.answers(other.payload) 

1269 

1270 def mysummary(self): 

1271 return Packet.mysummary(self) 

1272 

1273 

1274class TCPerror(TCP): 

1275 name = "TCP in ICMP" 

1276 fields_desc = ( 

1277 TCP.fields_desc[:2] + 

1278 # MayEnd after the 8 first octets. 

1279 [MayEnd(TCP.fields_desc[2])] + 

1280 TCP.fields_desc[3:] 

1281 ) 

1282 

1283 def answers(self, other): 

1284 if not isinstance(other, TCP): 

1285 return 0 

1286 if conf.checkIPsrc: 

1287 if not ((self.sport == other.sport) and 

1288 (self.dport == other.dport)): 

1289 return 0 

1290 if conf.check_TCPerror_seqack: 

1291 if self.seq is not None: 

1292 if self.seq != other.seq: 

1293 return 0 

1294 if self.ack is not None: 

1295 if self.ack != other.ack: 

1296 return 0 

1297 return 1 

1298 

1299 def mysummary(self): 

1300 return Packet.mysummary(self) 

1301 

1302 

1303class UDPerror(UDP): 

1304 name = "UDP in ICMP" 

1305 

1306 def answers(self, other): 

1307 if not isinstance(other, UDP): 

1308 return 0 

1309 if conf.checkIPsrc: 

1310 if not ((self.sport == other.sport) and 

1311 (self.dport == other.dport)): 

1312 return 0 

1313 return 1 

1314 

1315 def mysummary(self): 

1316 return Packet.mysummary(self) 

1317 

1318 

1319class ICMPerror(ICMP): 

1320 name = "ICMP in ICMP" 

1321 

1322 def answers(self, other): 

1323 if not isinstance(other, ICMP): 

1324 return 0 

1325 if not ((self.type == other.type) and 

1326 (self.code == other.code)): 

1327 return 0 

1328 if self.code in [0, 8, 13, 14, 17, 18]: 

1329 if (self.id == other.id and 

1330 self.seq == other.seq): 

1331 return 1 

1332 else: 

1333 return 0 

1334 else: 

1335 return 1 

1336 

1337 def mysummary(self): 

1338 return Packet.mysummary(self) 

1339 

1340 

1341bind_layers(Ether, IP, type=2048) 

1342bind_layers(CookedLinux, IP, proto=2048) 

1343bind_layers(GRE, IP, proto=2048) 

1344bind_layers(SNAP, IP, code=2048) 

1345bind_bottom_up(Loopback, IP, type=0) 

1346bind_layers(Loopback, IP, type=socket.AF_INET) 

1347bind_layers(IPerror, IPerror, frag=0, proto=4) 

1348bind_layers(IPerror, ICMPerror, frag=0, proto=1) 

1349bind_layers(IPerror, TCPerror, frag=0, proto=6) 

1350bind_layers(IPerror, UDPerror, frag=0, proto=17) 

1351bind_layers(IP, IP, frag=0, proto=4) 

1352bind_layers(IP, ICMP, frag=0, proto=1) 

1353bind_layers(IP, TCP, frag=0, proto=6) 

1354bind_layers(IP, UDP, frag=0, proto=17) 

1355bind_layers(IP, GRE, frag=0, proto=47) 

1356bind_layers(UDP, GRE, dport=4754) 

1357 

1358conf.l2types.register(DLT_RAW, IP) 

1359conf.l2types.register_num2layer(DLT_RAW_ALT, IP) 

1360conf.l2types.register(DLT_IPV4, IP) 

1361 

1362conf.l3types.register(ETH_P_IP, IP) 

1363conf.l3types.register_num2layer(ETH_P_ALL, IP) 

1364 

1365 

1366def inet_register_l3(l2, l3): 

1367 """ 

1368 Resolves the default L2 destination address when IP is used. 

1369 """ 

1370 return getmacbyip(l3.dst) 

1371 

1372 

1373conf.neighbor.register_l3(Ether, IP, inet_register_l3) 

1374conf.neighbor.register_l3(Dot3, IP, inet_register_l3) 

1375 

1376 

1377################### 

1378# Fragmentation # 

1379################### 

1380 

1381@conf.commands.register 

1382def fragment(pkt, fragsize=1480): 

1383 """Fragment a big IP datagram""" 

1384 if fragsize < 8: 

1385 warning("fragsize cannot be lower than 8") 

1386 fragsize = max(fragsize, 8) 

1387 lastfragsz = fragsize 

1388 fragsize -= fragsize % 8 

1389 lst = [] 

1390 for p in pkt: 

1391 s = raw(p[IP].payload) 

1392 nb = (len(s) - lastfragsz + fragsize - 1) // fragsize + 1 

1393 for i in range(nb): 

1394 q = p.copy() 

1395 del q[IP].payload 

1396 del q[IP].chksum 

1397 del q[IP].len 

1398 if i != nb - 1: 

1399 q[IP].flags |= 1 

1400 fragend = (i + 1) * fragsize 

1401 else: 

1402 fragend = i * fragsize + lastfragsz 

1403 q[IP].frag += i * fragsize // 8 

1404 r = conf.raw_layer(load=s[i * fragsize:fragend]) 

1405 r.overload_fields = p[IP].payload.overload_fields.copy() 

1406 q.add_payload(r) 

1407 lst.append(q) 

1408 return lst 

1409 

1410 

1411@conf.commands.register 

1412def overlap_frag(p, overlap, fragsize=8, overlap_fragsize=None): 

1413 """Build overlapping fragments to bypass NIPS 

1414 

1415p: the original packet 

1416overlap: the overlapping data 

1417fragsize: the fragment size of the packet 

1418overlap_fragsize: the fragment size of the overlapping packet""" 

1419 

1420 if overlap_fragsize is None: 

1421 overlap_fragsize = fragsize 

1422 q = p.copy() 

1423 del q[IP].payload 

1424 q[IP].add_payload(overlap) 

1425 

1426 qfrag = fragment(q, overlap_fragsize) 

1427 qfrag[-1][IP].flags |= 1 

1428 return qfrag + fragment(p, fragsize) 

1429 

1430 

1431class BadFragments(ValueError): 

1432 def __init__(self, *args, **kwargs): 

1433 self.frags = kwargs.pop("frags", None) 

1434 super(BadFragments, self).__init__(*args, **kwargs) 

1435 

1436 

1437def _defrag_iter_and_check_offsets(frags): 

1438 """ 

1439 Internal generator used in _defrag_ip_pkt 

1440 """ 

1441 offset = 0 

1442 for pkt, o, length in frags: 

1443 if offset != o: 

1444 if offset > o: 

1445 op = ">" 

1446 else: 

1447 op = "<" 

1448 warning("Fragment overlap (%i %s %i) on %r" % (offset, op, o, pkt)) 

1449 raise BadFragments 

1450 offset += length 

1451 yield bytes(pkt[IP].payload) 

1452 

1453 

1454def _defrag_ip_pkt(pkt, frags): 

1455 """ 

1456 Defragment a single IP packet. 

1457 

1458 :param pkt: the new pkt 

1459 :param frags: a defaultdict(list) used for storage 

1460 :return: a tuple (fragmented, defragmented_value) 

1461 """ 

1462 ip = pkt[IP] 

1463 if pkt.frag != 0 or ip.flags.MF: 

1464 # fragmented ! 

1465 uid = (ip.id, ip.src, ip.dst, ip.proto) 

1466 if ip.len is None or ip.ihl is None: 

1467 fraglen = len(ip.payload) 

1468 else: 

1469 fraglen = ip.len - (ip.ihl << 2) 

1470 # (pkt, frag offset, frag len) 

1471 frags[uid].append((pkt, ip.frag << 3, fraglen)) 

1472 if not ip.flags.MF: # no more fragments = last fragment 

1473 curfrags = sorted(frags[uid], key=lambda x: x[1]) # sort by offset 

1474 try: 

1475 data = b"".join(_defrag_iter_and_check_offsets(curfrags)) 

1476 except ValueError: 

1477 # bad fragment 

1478 badfrags = frags[uid] 

1479 del frags[uid] 

1480 raise BadFragments(frags=badfrags) 

1481 # re-build initial packet without fragmentation 

1482 p = curfrags[0][0].copy() 

1483 pay_class = p[IP].payload.__class__ 

1484 p[IP].flags.MF = False 

1485 p[IP].remove_payload() 

1486 p[IP].len = None 

1487 p[IP].chksum = None 

1488 # append defragmented payload 

1489 p /= pay_class(data) 

1490 # cleanup 

1491 del frags[uid] 

1492 return True, p 

1493 return True, None 

1494 return False, pkt 

1495 

1496 

1497def _defrag_logic(plist, complete=False): 

1498 """ 

1499 Internal function used to defragment a list of packets. 

1500 It contains the logic behind the defrag() and defragment() functions 

1501 """ 

1502 frags = defaultdict(list) 

1503 final = [] 

1504 notfrag = [] 

1505 badfrag = [] 

1506 # Defrag 

1507 for i, pkt in enumerate(plist): 

1508 if IP not in pkt: 

1509 # no IP layer 

1510 if complete: 

1511 final.append(pkt) 

1512 continue 

1513 try: 

1514 fragmented, defragmented_value = _defrag_ip_pkt( 

1515 pkt, 

1516 frags, 

1517 ) 

1518 except BadFragments as ex: 

1519 if complete: 

1520 final.extend(ex.frags) 

1521 else: 

1522 badfrag.extend(ex.frags) 

1523 continue 

1524 if complete and defragmented_value: 

1525 final.append(defragmented_value) 

1526 elif defragmented_value: 

1527 if fragmented: 

1528 final.append(defragmented_value) 

1529 else: 

1530 notfrag.append(defragmented_value) 

1531 # Return 

1532 if complete: 

1533 if hasattr(plist, "listname"): 

1534 name = "Defragmented %s" % plist.listname 

1535 else: 

1536 name = "Defragmented" 

1537 return PacketList(final, name=name) 

1538 else: 

1539 return PacketList(notfrag), PacketList(final), PacketList(badfrag) 

1540 

1541 

1542@conf.commands.register 

1543def defrag(plist): 

1544 """defrag(plist) -> ([not fragmented], [defragmented], 

1545 [ [bad fragments], [bad fragments], ... ])""" 

1546 return _defrag_logic(plist, complete=False) 

1547 

1548 

1549@conf.commands.register 

1550def defragment(plist): 

1551 """defragment(plist) -> plist defragmented as much as possible """ 

1552 return _defrag_logic(plist, complete=True) 

1553 

1554 

1555# Add timeskew_graph() method to PacketList 

1556def _packetlist_timeskew_graph(self, ip, **kargs): 

1557 """Tries to graph the timeskew between the timestamps and real time for a given ip""" # noqa: E501 

1558 # Defer imports of matplotlib until its needed 

1559 # because it has a heavy dep chain 

1560 from scapy.libs.matplot import ( 

1561 plt, 

1562 MATPLOTLIB_INLINED, 

1563 MATPLOTLIB_DEFAULT_PLOT_KARGS 

1564 ) 

1565 

1566 # Filter TCP segments which source address is 'ip' 

1567 tmp = (self._elt2pkt(x) for x in self.res) 

1568 b = (x for x in tmp if IP in x and x[IP].src == ip and TCP in x) 

1569 

1570 # Build a list of tuples (creation_time, replied_timestamp) 

1571 c = [] 

1572 tsf = ICMPTimeStampField("", None) 

1573 for p in b: 

1574 opts = p.getlayer(TCP).options 

1575 for o in opts: 

1576 if o[0] == "Timestamp": 

1577 c.append((p.time, tsf.any2i("", o[1][0]))) 

1578 

1579 # Stop if the list is empty 

1580 if not c: 

1581 warning("No timestamps found in packet list") 

1582 return [] 

1583 

1584 # Prepare the data that will be plotted 

1585 first_creation_time = c[0][0] 

1586 first_replied_timestamp = c[0][1] 

1587 

1588 def _wrap_data(ts_tuple, wrap_seconds=2000): 

1589 """Wrap the list of tuples.""" 

1590 

1591 ct, rt = ts_tuple # (creation_time, replied_timestamp) 

1592 X = ct % wrap_seconds 

1593 Y = ((ct - first_creation_time) - ((rt - first_replied_timestamp) / 1000.0)) # noqa: E501 

1594 

1595 return X, Y 

1596 

1597 data = [_wrap_data(e) for e in c] 

1598 

1599 # Mimic the default gnuplot output 

1600 if kargs == {}: 

1601 kargs = MATPLOTLIB_DEFAULT_PLOT_KARGS 

1602 lines = plt.plot(data, **kargs) 

1603 

1604 # Call show() if matplotlib is not inlined 

1605 if not MATPLOTLIB_INLINED: 

1606 plt.show() 

1607 

1608 return lines 

1609 

1610 

1611_PacketList.timeskew_graph = _packetlist_timeskew_graph 

1612 

1613 

1614# Create a new packet list 

1615class TracerouteResult(SndRcvList): 

1616 __slots__ = ["graphdef", "graphpadding", "graphASres", "padding", "hloc", 

1617 "nloc"] 

1618 

1619 def __init__(self, res=None, name="Traceroute", stats=None): 

1620 SndRcvList.__init__(self, res, name, stats) 

1621 self.graphdef = None 

1622 self.graphASres = None 

1623 self.padding = 0 

1624 self.hloc = None 

1625 self.nloc = None 

1626 

1627 def show(self): 

1628 return self.make_table(lambda s, r: (s.sprintf("%IP.dst%:{TCP:tcp%ir,TCP.dport%}{UDP:udp%ir,UDP.dport%}{ICMP:ICMP}"), # noqa: E501 

1629 s.ttl, 

1630 r.sprintf("%-15s,IP.src% {TCP:%TCP.flags%}{ICMP:%ir,ICMP.type%}"))) # noqa: E501 

1631 

1632 def get_trace(self): 

1633 trace = {} 

1634 for s, r in self.res: 

1635 if IP not in s: 

1636 continue 

1637 d = s[IP].dst 

1638 if d not in trace: 

1639 trace[d] = {} 

1640 trace[d][s[IP].ttl] = r[IP].src, ICMP not in r 

1641 for k in trace.values(): 

1642 try: 

1643 m = min(x for x, y in k.items() if y[1]) 

1644 except ValueError: 

1645 continue 

1646 for li in list(k): # use list(): k is modified in the loop 

1647 if li > m: 

1648 del k[li] 

1649 return trace 

1650 

1651 def trace3D(self, join=True): 

1652 """Give a 3D representation of the traceroute. 

1653 right button: rotate the scene 

1654 middle button: zoom 

1655 shift-left button: move the scene 

1656 left button on a ball: toggle IP displaying 

1657 double-click button on a ball: scan ports 21,22,23,25,80 and 443 and display the result""" # noqa: E501 

1658 # When not ran from a notebook, vpython pooly closes itself 

1659 # using os._exit once finished. We pack it into a Process 

1660 import multiprocessing 

1661 p = multiprocessing.Process(target=self.trace3D_notebook) 

1662 p.start() 

1663 if join: 

1664 p.join() 

1665 

1666 def trace3D_notebook(self): 

1667 """Same than trace3D, used when ran from Jupyter notebooks""" 

1668 trace = self.get_trace() 

1669 import vpython 

1670 

1671 class IPsphere(vpython.sphere): 

1672 def __init__(self, ip, **kargs): 

1673 vpython.sphere.__init__(self, **kargs) 

1674 self.ip = ip 

1675 self.label = None 

1676 self.setlabel(self.ip) 

1677 self.last_clicked = None 

1678 self.full = False 

1679 self.savcolor = vpython.vec(*self.color.value) 

1680 

1681 def fullinfos(self): 

1682 self.full = True 

1683 self.color = vpython.vec(1, 0, 0) 

1684 a, b = sr(IP(dst=self.ip) / TCP(dport=[21, 22, 23, 25, 80, 443], flags="S"), timeout=2, verbose=0) # noqa: E501 

1685 if len(a) == 0: 

1686 txt = "%s:\nno results" % self.ip 

1687 else: 

1688 txt = "%s:\n" % self.ip 

1689 for s, r in a: 

1690 txt += r.sprintf("{TCP:%IP.src%:%TCP.sport% %TCP.flags%}{TCPerror:%IPerror.dst%:%TCPerror.dport% %IP.src% %ir,ICMP.type%}\n") # noqa: E501 

1691 self.setlabel(txt, visible=1) 

1692 

1693 def unfull(self): 

1694 self.color = self.savcolor 

1695 self.full = False 

1696 self.setlabel(self.ip) 

1697 

1698 def setlabel(self, txt, visible=None): 

1699 if self.label is not None: 

1700 if visible is None: 

1701 visible = self.label.visible 

1702 self.label.visible = 0 

1703 elif visible is None: 

1704 visible = 0 

1705 self.label = vpython.label(text=txt, pos=self.pos, space=self.radius, xoffset=10, yoffset=20, visible=visible) # noqa: E501 

1706 

1707 def check_double_click(self): 

1708 try: 

1709 if self.full or not self.label.visible: 

1710 return False 

1711 if self.last_clicked is not None: 

1712 return (time.time() - self.last_clicked) < 0.5 

1713 return False 

1714 finally: 

1715 self.last_clicked = time.time() 

1716 

1717 def action(self): 

1718 self.label.visible ^= 1 

1719 if self.full: 

1720 self.unfull() 

1721 

1722 vpython.scene = vpython.canvas() 

1723 vpython.scene.title = "<center><u><b>%s</b></u></center>" % self.listname # noqa: E501 

1724 vpython.scene.append_to_caption( 

1725 re.sub( 

1726 r'\%(.*)\%', 

1727 r'<span style="color: red">\1</span>', 

1728 re.sub( 

1729 r'\`(.*)\`', 

1730 r'<span style="color: #3399ff">\1</span>', 

1731 """<u><b>Commands:</b></u> 

1732%Click% to toggle information about a node. 

1733%Double click% to perform a quick web scan on this node. 

1734<u><b>Camera usage:</b></u> 

1735`Right button drag or Ctrl-drag` to rotate "camera" to view scene. 

1736`Shift-drag` to move the object around. 

1737`Middle button or Alt-drag` to drag up or down to zoom in or out. 

1738 On a two-button mouse, `middle is wheel or left + right`. 

1739Touch screen: pinch/extend to zoom, swipe or two-finger rotate.""" 

1740 ) 

1741 ) 

1742 ) 

1743 vpython.scene.exit = True 

1744 rings = {} 

1745 tr3d = {} 

1746 for i in trace: 

1747 tr = trace[i] 

1748 tr3d[i] = [] 

1749 for t in range(1, max(tr) + 1): 

1750 if t not in rings: 

1751 rings[t] = [] 

1752 if t in tr: 

1753 if tr[t] not in rings[t]: 

1754 rings[t].append(tr[t]) 

1755 tr3d[i].append(rings[t].index(tr[t])) 

1756 else: 

1757 rings[t].append(("unk", -1)) 

1758 tr3d[i].append(len(rings[t]) - 1) 

1759 

1760 for t in rings: 

1761 r = rings[t] 

1762 tmp_len = len(r) 

1763 for i in range(tmp_len): 

1764 if r[i][1] == -1: 

1765 col = vpython.vec(0.75, 0.75, 0.75) 

1766 elif r[i][1]: 

1767 col = vpython.color.green 

1768 else: 

1769 col = vpython.color.blue 

1770 

1771 s = IPsphere(pos=vpython.vec((tmp_len - 1) * vpython.cos(2 * i * vpython.pi / tmp_len), (tmp_len - 1) * vpython.sin(2 * i * vpython.pi / tmp_len), 2 * t), # noqa: E501 

1772 ip=r[i][0], 

1773 color=col) 

1774 for trlst in tr3d.values(): 

1775 if t <= len(trlst): 

1776 if trlst[t - 1] == i: 

1777 trlst[t - 1] = s 

1778 forecol = colgen(0.625, 0.4375, 0.25, 0.125) 

1779 for trlst in tr3d.values(): 

1780 col = vpython.vec(*next(forecol)) 

1781 start = vpython.vec(0, 0, 0) 

1782 for ip in trlst: 

1783 vpython.cylinder(pos=start, axis=ip.pos - start, color=col, radius=0.2) # noqa: E501 

1784 start = ip.pos 

1785 

1786 vpython.rate(50) 

1787 

1788 # Keys handling 

1789 # TODO: there is currently no way of closing vpython correctly 

1790 # https://github.com/BruceSherwood/vpython-jupyter/issues/36 

1791 # def keyboard_press(ev): 

1792 # k = ev.key 

1793 # if k == "esc" or k == "q": 

1794 # pass # TODO: close 

1795 # 

1796 # vpython.scene.bind('keydown', keyboard_press) 

1797 

1798 # Mouse handling 

1799 def mouse_click(ev): 

1800 if ev.press == "left": 

1801 o = vpython.scene.mouse.pick 

1802 if o and isinstance(o, IPsphere): 

1803 if o.check_double_click(): 

1804 if o.ip == "unk": 

1805 return 

1806 o.fullinfos() 

1807 else: 

1808 o.action() 

1809 

1810 vpython.scene.bind('mousedown', mouse_click) 

1811 

1812 def world_trace(self): 

1813 """Display traceroute results on a world map.""" 

1814 

1815 # Check that the geoip2 module can be imported 

1816 # Doc: http://geoip2.readthedocs.io/en/latest/ 

1817 from scapy.libs.matplot import plt, MATPLOTLIB, MATPLOTLIB_INLINED 

1818 

1819 try: 

1820 # GeoIP2 modules need to be imported as below 

1821 import geoip2.database 

1822 import geoip2.errors 

1823 except ImportError: 

1824 log_runtime.error( 

1825 "Cannot import geoip2. Won't be able to plot the world." 

1826 ) 

1827 return [] 

1828 # Check availability of database 

1829 if not conf.geoip_city: 

1830 log_runtime.error( 

1831 "Cannot import the geolite2 CITY database.\n" 

1832 "Download it from http://dev.maxmind.com/geoip/geoip2/geolite2/" # noqa: E501 

1833 " then set its path to conf.geoip_city" 

1834 ) 

1835 return [] 

1836 # Check availability of plotting devices 

1837 try: 

1838 import cartopy.crs as ccrs 

1839 except ImportError: 

1840 log_runtime.error( 

1841 "Cannot import cartopy.\n" 

1842 "More infos on http://scitools.org.uk/cartopy/docs/latest/installing.html" # noqa: E501 

1843 ) 

1844 return [] 

1845 if not MATPLOTLIB: 

1846 log_runtime.error( 

1847 "Matplotlib is not installed. Won't be able to plot the world." 

1848 ) 

1849 return [] 

1850 

1851 # Open & read the GeoListIP2 database 

1852 try: 

1853 db = geoip2.database.Reader(conf.geoip_city) 

1854 except Exception: 

1855 log_runtime.error( 

1856 "Cannot open geoip2 database at %s", 

1857 conf.geoip_city 

1858 ) 

1859 return [] 

1860 

1861 # Regroup results per trace 

1862 ips = {} 

1863 rt = {} 

1864 ports_done = {} 

1865 for s, r in self.res: 

1866 ips[r.src] = None 

1867 if s.haslayer(TCP) or s.haslayer(UDP): 

1868 trace_id = (s.src, s.dst, s.proto, s.dport) 

1869 elif s.haslayer(ICMP): 

1870 trace_id = (s.src, s.dst, s.proto, s.type) 

1871 else: 

1872 trace_id = (s.src, s.dst, s.proto, 0) 

1873 trace = rt.get(trace_id, {}) 

1874 if not r.haslayer(ICMP) or r.type != 11: 

1875 if trace_id in ports_done: 

1876 continue 

1877 ports_done[trace_id] = None 

1878 trace[s.ttl] = r.src 

1879 rt[trace_id] = trace 

1880 

1881 # Get the addresses locations 

1882 trt = {} 

1883 for trace_id in rt: 

1884 trace = rt[trace_id] 

1885 loctrace = [] 

1886 for i in range(max(trace)): 

1887 ip = trace.get(i, None) 

1888 if ip is None: 

1889 continue 

1890 # Fetch database 

1891 try: 

1892 sresult = db.city(ip) 

1893 except geoip2.errors.AddressNotFoundError: 

1894 continue 

1895 loctrace.append((sresult.location.longitude, sresult.location.latitude)) # noqa: E501 

1896 if loctrace: 

1897 trt[trace_id] = loctrace 

1898 

1899 # Load the map renderer 

1900 plt.figure(num='Scapy') 

1901 ax = plt.axes(projection=ccrs.PlateCarree()) 

1902 # Draw countries 

1903 ax.coastlines() 

1904 ax.stock_img() 

1905 # Set normal size 

1906 ax.set_global() 

1907 # Add title 

1908 plt.title("Scapy traceroute results") 

1909 

1910 from matplotlib.collections import LineCollection 

1911 from matplotlib import colors as mcolors 

1912 colors_cycle = iter(mcolors.BASE_COLORS) 

1913 lines = [] 

1914 

1915 # Split traceroute measurement 

1916 for key, trc in trt.items(): 

1917 # Get next color 

1918 color = next(colors_cycle) 

1919 # Gather mesurments data 

1920 data_lines = [(trc[i], trc[i + 1]) for i in range(len(trc) - 1)] 

1921 # Create line collection 

1922 line_col = LineCollection(data_lines, linewidths=2, 

1923 label=key[1], 

1924 color=color) 

1925 lines.append(line_col) 

1926 ax.add_collection(line_col) 

1927 # Create map points 

1928 lines.extend([ax.plot(*x, marker='.', color=color) for x in trc]) 

1929 

1930 # Generate legend 

1931 ax.legend() 

1932 

1933 # Call show() if matplotlib is not inlined 

1934 if not MATPLOTLIB_INLINED: 

1935 plt.show() 

1936 

1937 # Clean 

1938 ax.remove() 

1939 

1940 # Return the drawn lines 

1941 return lines 

1942 

1943 def make_graph(self, ASres=None, padding=0): 

1944 self.graphASres = ASres 

1945 self.graphpadding = padding 

1946 ips = {} 

1947 rt = {} 

1948 ports = {} 

1949 ports_done = {} 

1950 for s, r in self.res: 

1951 r = r.getlayer(IP) or (conf.ipv6_enabled and r[scapy.layers.inet6.IPv6]) or r # noqa: E501 

1952 s = s.getlayer(IP) or (conf.ipv6_enabled and s[scapy.layers.inet6.IPv6]) or s # noqa: E501 

1953 ips[r.src] = None 

1954 if TCP in s: 

1955 trace_id = (s.src, s.dst, 6, s.dport) 

1956 elif UDP in s: 

1957 trace_id = (s.src, s.dst, 17, s.dport) 

1958 elif ICMP in s: 

1959 trace_id = (s.src, s.dst, 1, s.type) 

1960 else: 

1961 trace_id = (s.src, s.dst, s.proto, 0) 

1962 trace = rt.get(trace_id, {}) 

1963 ttl = conf.ipv6_enabled and scapy.layers.inet6.IPv6 in s and s.hlim or s.ttl # noqa: E501 

1964 if not (ICMP in r and r[ICMP].type == 11) and not (conf.ipv6_enabled and scapy.layers.inet6.IPv6 in r and scapy.layers.inet6.ICMPv6TimeExceeded in r): # noqa: E501 

1965 if trace_id in ports_done: 

1966 continue 

1967 ports_done[trace_id] = None 

1968 p = ports.get(r.src, []) 

1969 if TCP in r: 

1970 p.append(r.sprintf("<T%ir,TCP.sport%> %TCP.sport% %TCP.flags%")) # noqa: E501 

1971 trace[ttl] = r.sprintf('"%r,src%":T%ir,TCP.sport%') 

1972 elif UDP in r: 

1973 p.append(r.sprintf("<U%ir,UDP.sport%> %UDP.sport%")) 

1974 trace[ttl] = r.sprintf('"%r,src%":U%ir,UDP.sport%') 

1975 elif ICMP in r: 

1976 p.append(r.sprintf("<I%ir,ICMP.type%> ICMP %ICMP.type%")) 

1977 trace[ttl] = r.sprintf('"%r,src%":I%ir,ICMP.type%') 

1978 else: 

1979 p.append(r.sprintf("{IP:<P%ir,proto%> IP %proto%}{IPv6:<P%ir,nh%> IPv6 %nh%}")) # noqa: E501 

1980 trace[ttl] = r.sprintf('"%r,src%":{IP:P%ir,proto%}{IPv6:P%ir,nh%}') # noqa: E501 

1981 ports[r.src] = p 

1982 else: 

1983 trace[ttl] = r.sprintf('"%r,src%"') 

1984 rt[trace_id] = trace 

1985 

1986 # Fill holes with unk%i nodes 

1987 unknown_label = incremental_label("unk%i") 

1988 blackholes = [] 

1989 bhip = {} 

1990 for rtk in rt: 

1991 trace = rt[rtk] 

1992 max_trace = max(trace) 

1993 for n in range(min(trace), max_trace): 

1994 if n not in trace: 

1995 trace[n] = next(unknown_label) 

1996 if rtk not in ports_done: 

1997 if rtk[2] == 1: # ICMP 

1998 bh = "%s %i/icmp" % (rtk[1], rtk[3]) 

1999 elif rtk[2] == 6: # TCP 

2000 bh = "%s %i/tcp" % (rtk[1], rtk[3]) 

2001 elif rtk[2] == 17: # UDP 

2002 bh = '%s %i/udp' % (rtk[1], rtk[3]) 

2003 else: 

2004 bh = '%s %i/proto' % (rtk[1], rtk[2]) 

2005 ips[bh] = None 

2006 bhip[rtk[1]] = bh 

2007 bh = '"%s"' % bh 

2008 trace[max_trace + 1] = bh 

2009 blackholes.append(bh) 

2010 

2011 # Find AS numbers 

2012 ASN_query_list = set(x.rsplit(" ", 1)[0] for x in ips) 

2013 if ASres is None: 

2014 ASNlist = [] 

2015 else: 

2016 ASNlist = ASres.resolve(*ASN_query_list) 

2017 

2018 ASNs = {} 

2019 ASDs = {} 

2020 for ip, asn, desc, in ASNlist: 

2021 if asn is None: 

2022 continue 

2023 iplist = ASNs.get(asn, []) 

2024 if ip in bhip: 

2025 if ip in ports: 

2026 iplist.append(ip) 

2027 iplist.append(bhip[ip]) 

2028 else: 

2029 iplist.append(ip) 

2030 ASNs[asn] = iplist 

2031 ASDs[asn] = desc 

2032 

2033 backcolorlist = colgen("60", "86", "ba", "ff") 

2034 forecolorlist = colgen("a0", "70", "40", "20") 

2035 

2036 s = "digraph trace {\n" 

2037 

2038 s += "\n\tnode [shape=ellipse,color=black,style=solid];\n\n" 

2039 

2040 s += "\n#ASN clustering\n" 

2041 for asn in ASNs: 

2042 s += '\tsubgraph cluster_%s {\n' % asn 

2043 col = next(backcolorlist) 

2044 s += '\t\tcolor="#%s%s%s";' % col 

2045 s += '\t\tnode [fillcolor="#%s%s%s",style=filled];' % col 

2046 s += '\t\tfontsize = 10;' 

2047 s += '\t\tlabel = "%s\\n[%s]"\n' % (asn, ASDs[asn]) 

2048 for ip in ASNs[asn]: 

2049 

2050 s += '\t\t"%s";\n' % ip 

2051 s += "\t}\n" 

2052 

2053 s += "#endpoints\n" 

2054 for p in ports: 

2055 s += '\t"%s" [shape=record,color=black,fillcolor=green,style=filled,label="%s|%s"];\n' % (p, p, "|".join(ports[p])) # noqa: E501 

2056 

2057 s += "\n#Blackholes\n" 

2058 for bh in blackholes: 

2059 s += '\t%s [shape=octagon,color=black,fillcolor=red,style=filled];\n' % bh # noqa: E501 

2060 

2061 if padding: 

2062 s += "\n#Padding\n" 

2063 pad = {} 

2064 for snd, rcv in self.res: 

2065 if rcv.src not in ports and rcv.haslayer(conf.padding_layer): 

2066 p = rcv.getlayer(conf.padding_layer).load 

2067 if p != b"\x00" * len(p): 

2068 pad[rcv.src] = None 

2069 for rcv in pad: 

2070 s += '\t"%s" [shape=triangle,color=black,fillcolor=red,style=filled];\n' % rcv # noqa: E501 

2071 

2072 s += "\n\tnode [shape=ellipse,color=black,style=solid];\n\n" 

2073 

2074 for rtk in rt: 

2075 s += "#---[%s\n" % repr(rtk) 

2076 s += '\t\tedge [color="#%s%s%s"];\n' % next(forecolorlist) 

2077 trace = rt[rtk] 

2078 maxtrace = max(trace) 

2079 for n in range(min(trace), maxtrace): 

2080 s += '\t%s ->\n' % trace[n] 

2081 s += '\t%s;\n' % trace[maxtrace] 

2082 

2083 s += "}\n" 

2084 self.graphdef = s 

2085 

2086 def graph(self, ASres=conf.AS_resolver, padding=0, **kargs): 

2087 """x.graph(ASres=conf.AS_resolver, other args): 

2088 ASres=None : no AS resolver => no clustering 

2089 ASres=AS_resolver() : default whois AS resolver (riswhois.ripe.net) 

2090 ASres=AS_resolver_cymru(): use whois.cymru.com whois database 

2091 ASres=AS_resolver(server="whois.ra.net") 

2092 type: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option # noqa: E501 

2093 target: filename or redirect. Defaults pipe to Imagemagick's display program # noqa: E501 

2094 prog: which graphviz program to use""" 

2095 if (self.graphdef is None or 

2096 self.graphASres != ASres or 

2097 self.graphpadding != padding): 

2098 self.make_graph(ASres, padding) 

2099 

2100 return do_graph(self.graphdef, **kargs) 

2101 

2102 

2103@conf.commands.register 

2104def traceroute(target, dport=80, minttl=1, maxttl=30, sport=RandShort(), l4=None, filter=None, timeout=2, verbose=None, **kargs): # noqa: E501 

2105 """Instant TCP traceroute 

2106 

2107 :param target: hostnames or IP addresses 

2108 :param dport: TCP destination port (default is 80) 

2109 :param minttl: minimum TTL (default is 1) 

2110 :param maxttl: maximum TTL (default is 30) 

2111 :param sport: TCP source port (default is random) 

2112 :param l4: use a Scapy packet instead of TCP 

2113 :param filter: BPF filter applied to received packets 

2114 :param timeout: time to wait for answers (default is 2s) 

2115 :param verbose: detailed output 

2116 :return: an TracerouteResult, and a list of unanswered packets""" 

2117 if verbose is None: 

2118 verbose = conf.verb 

2119 if filter is None: 

2120 # we only consider ICMP error packets and TCP packets with at 

2121 # least the ACK flag set *and* either the SYN or the RST flag 

2122 # set 

2123 filter = "(icmp and (icmp[0]=3 or icmp[0]=4 or icmp[0]=5 or icmp[0]=11 or icmp[0]=12)) or (tcp and (tcp[13] & 0x16 > 0x10))" # noqa: E501 

2124 if l4 is None: 

2125 a, b = sr(IP(dst=target, id=RandShort(), ttl=(minttl, maxttl)) / TCP(seq=RandInt(), sport=sport, dport=dport), # noqa: E501 

2126 timeout=timeout, filter=filter, verbose=verbose, **kargs) 

2127 else: 

2128 # this should always work 

2129 filter = "ip" 

2130 a, b = sr(IP(dst=target, id=RandShort(), ttl=(minttl, maxttl)) / l4, 

2131 timeout=timeout, filter=filter, verbose=verbose, **kargs) 

2132 

2133 a = TracerouteResult(a.res) 

2134 if verbose: 

2135 a.show() 

2136 return a, b 

2137 

2138 

2139@conf.commands.register 

2140def traceroute_map(ips, **kargs): 

2141 """Util function to call traceroute on multiple targets, then 

2142 show the different paths on a map. 

2143 

2144 :param ips: a list of IPs on which traceroute will be called 

2145 :param kargs: (optional) kwargs, passed to traceroute 

2146 """ 

2147 kargs.setdefault("verbose", 0) 

2148 return traceroute(ips)[0].world_trace() 

2149 

2150############################# 

2151# Simple TCP client stack # 

2152############################# 

2153 

2154 

2155class TCP_client(Automaton): 

2156 """ 

2157 Creates a TCP Client Automaton. 

2158 This automaton will handle TCP 3-way handshake. 

2159 

2160 Usage: the easiest usage is to use it as a SuperSocket. 

2161 >>> a = TCP_client.tcplink(HTTP, "www.google.com", 80) 

2162 >>> a.send(HTTPRequest()) 

2163 >>> a.recv() 

2164 

2165 :param ip: the ip to connect to 

2166 :param port: 

2167 :param src: (optional) use another source IP 

2168 """ 

2169 

2170 def parse_args(self, ip, port, srcip=None, **kargs): 

2171 from scapy.sessions import TCPSession 

2172 self.dst = str(Net(ip)) 

2173 self.dport = port 

2174 self.sport = random.randrange(0, 2**16) 

2175 self.l4 = IP(dst=ip, src=srcip) / TCP( 

2176 sport=self.sport, dport=self.dport, 

2177 flags=0, seq=random.randrange(0, 2**32) 

2178 ) 

2179 self.src = self.l4.src 

2180 self.sack = self.l4[TCP].ack 

2181 self.rel_seq = None 

2182 self.rcvbuf = TCPSession() 

2183 bpf = "host %s and host %s and port %i and port %i" % (self.src, 

2184 self.dst, 

2185 self.sport, 

2186 self.dport) 

2187 Automaton.parse_args(self, filter=bpf, **kargs) 

2188 

2189 def _transmit_packet(self, pkt): 

2190 """Transmits a packet from TCPSession to the SuperSocket""" 

2191 self.oi.tcp.send(raw(pkt[TCP].payload)) 

2192 

2193 def master_filter(self, pkt): 

2194 return (IP in pkt and 

2195 pkt[IP].src == self.dst and 

2196 pkt[IP].dst == self.src and 

2197 TCP in pkt and 

2198 pkt[TCP].sport == self.dport and 

2199 pkt[TCP].dport == self.sport and 

2200 self.l4[TCP].seq >= pkt[TCP].ack and # XXX: seq/ack 2^32 wrap up # noqa: E501 

2201 ((self.l4[TCP].ack == 0) or (self.sack <= pkt[TCP].seq <= self.l4[TCP].ack + pkt[TCP].window))) # noqa: E501 

2202 

2203 @ATMT.state(initial=1) 

2204 def START(self): 

2205 pass 

2206 

2207 @ATMT.state() 

2208 def SYN_SENT(self): 

2209 pass 

2210 

2211 @ATMT.state() 

2212 def ESTABLISHED(self): 

2213 pass 

2214 

2215 @ATMT.state() 

2216 def LAST_ACK(self): 

2217 pass 

2218 

2219 @ATMT.state(final=1) 

2220 def CLOSED(self): 

2221 pass 

2222 

2223 @ATMT.state(stop=1) 

2224 def STOP(self): 

2225 pass 

2226 

2227 @ATMT.state() 

2228 def STOP_SENT_FIN_ACK(self): 

2229 pass 

2230 

2231 @ATMT.condition(START) 

2232 def connect(self): 

2233 raise self.SYN_SENT() 

2234 

2235 @ATMT.action(connect) 

2236 def send_syn(self): 

2237 self.l4[TCP].flags = "S" 

2238 self.send(self.l4) 

2239 self.l4[TCP].seq += 1 

2240 

2241 @ATMT.receive_condition(SYN_SENT) 

2242 def synack_received(self, pkt): 

2243 if pkt[TCP].flags.SA: 

2244 raise self.ESTABLISHED().action_parameters(pkt) 

2245 

2246 @ATMT.action(synack_received) 

2247 def send_ack_of_synack(self, pkt): 

2248 self.l4[TCP].ack = pkt[TCP].seq + 1 

2249 self.l4[TCP].flags = "A" 

2250 self.send(self.l4) 

2251 

2252 @ATMT.receive_condition(ESTABLISHED) 

2253 def incoming_data_received(self, pkt): 

2254 if not isinstance(pkt[TCP].payload, (NoPayload, conf.padding_layer)): 

2255 raise self.ESTABLISHED().action_parameters(pkt) 

2256 

2257 @ATMT.action(incoming_data_received) 

2258 def receive_data(self, pkt): 

2259 data = raw(pkt[TCP].payload) 

2260 if data and self.l4[TCP].ack == pkt[TCP].seq: 

2261 self.sack = self.l4[TCP].ack 

2262 self.l4[TCP].ack += len(data) 

2263 self.l4[TCP].flags = "A" 

2264 # Answer with an Ack 

2265 self.send(self.l4) 

2266 # Process data - will be sent to the SuperSocket through this 

2267 pkt = self.rcvbuf.process(pkt) 

2268 if pkt: 

2269 self._transmit_packet(pkt) 

2270 

2271 @ATMT.ioevent(ESTABLISHED, name="tcp", as_supersocket="tcplink") 

2272 def outgoing_data_received(self, fd): 

2273 raise self.ESTABLISHED().action_parameters(fd.recv()) 

2274 

2275 @ATMT.action(outgoing_data_received) 

2276 def send_data(self, d): 

2277 self.l4[TCP].flags = "PA" 

2278 self.send(self.l4 / d) 

2279 self.l4[TCP].seq += len(d) 

2280 

2281 @ATMT.receive_condition(ESTABLISHED) 

2282 def reset_received(self, pkt): 

2283 if pkt[TCP].flags.R: 

2284 raise self.CLOSED() 

2285 

2286 @ATMT.receive_condition(ESTABLISHED) 

2287 def fin_received(self, pkt): 

2288 if pkt[TCP].flags.F: 

2289 raise self.LAST_ACK().action_parameters(pkt) 

2290 

2291 @ATMT.action(fin_received) 

2292 def send_finack(self, pkt): 

2293 self.l4[TCP].flags = "FA" 

2294 self.l4[TCP].ack = pkt[TCP].seq + 1 

2295 self.send(self.l4) 

2296 self.l4[TCP].seq += 1 

2297 

2298 @ATMT.receive_condition(LAST_ACK) 

2299 def ack_of_fin_received(self, pkt): 

2300 if pkt[TCP].flags.A: 

2301 raise self.CLOSED() 

2302 

2303 @ATMT.condition(STOP) 

2304 def stop_requested(self): 

2305 raise self.STOP_SENT_FIN_ACK() 

2306 

2307 @ATMT.action(stop_requested) 

2308 def stop_send_finack(self): 

2309 self.l4[TCP].flags = "FA" 

2310 self.send(self.l4) 

2311 self.l4[TCP].seq += 1 

2312 

2313 @ATMT.receive_condition(STOP_SENT_FIN_ACK) 

2314 def stop_fin_received(self, pkt): 

2315 if pkt[TCP].flags.F: 

2316 raise self.CLOSED().action_parameters(pkt) 

2317 

2318 @ATMT.action(stop_fin_received) 

2319 def stop_send_ack(self, pkt): 

2320 self.l4[TCP].flags = "A" 

2321 self.l4[TCP].ack = pkt[TCP].seq + 1 

2322 self.send(self.l4) 

2323 

2324 @ATMT.timeout(SYN_SENT, 1) 

2325 def syn_ack_timeout(self): 

2326 raise self.CLOSED() 

2327 

2328 @ATMT.timeout(STOP_SENT_FIN_ACK, 1) 

2329 def stop_ack_timeout(self): 

2330 raise self.CLOSED() 

2331 

2332 

2333##################### 

2334# Reporting stuff # 

2335##################### 

2336 

2337 

2338@conf.commands.register 

2339def report_ports(target, ports): 

2340 """portscan a target and output a LaTeX table 

2341report_ports(target, ports) -> string""" 

2342 ans, unans = sr(IP(dst=target) / TCP(dport=ports), timeout=5) 

2343 rep = "\\begin{tabular}{|r|l|l|}\n\\hline\n" 

2344 for s, r in ans: 

2345 if not r.haslayer(ICMP): 

2346 if r.payload.flags == 0x12: 

2347 rep += r.sprintf("%TCP.sport% & open & SA \\\\\n") 

2348 rep += "\\hline\n" 

2349 for s, r in ans: 

2350 if r.haslayer(ICMP): 

2351 rep += r.sprintf("%TCPerror.dport% & closed & ICMP type %ICMP.type%/%ICMP.code% from %IP.src% \\\\\n") # noqa: E501 

2352 elif r.payload.flags != 0x12: 

2353 rep += r.sprintf("%TCP.sport% & closed & TCP %TCP.flags% \\\\\n") 

2354 rep += "\\hline\n" 

2355 for i in unans: 

2356 rep += i.sprintf("%TCP.dport% & ? & unanswered \\\\\n") 

2357 rep += "\\hline\n\\end{tabular}\n" 

2358 return rep 

2359 

2360 

2361@conf.commands.register 

2362def IPID_count(lst, funcID=lambda x: x[1].id, funcpres=lambda x: x[1].summary()): # noqa: E501 

2363 """Identify IP id values classes in a list of packets 

2364 

2365lst: a list of packets 

2366funcID: a function that returns IP id values 

2367funcpres: a function used to summarize packets""" 

2368 idlst = [funcID(e) for e in lst] 

2369 idlst.sort() 

2370 classes = [idlst[0]] 

2371 classes += [t[1] for t in zip(idlst[:-1], idlst[1:]) if abs(t[0] - t[1]) > 50] # noqa: E501 

2372 lst = [(funcID(x), funcpres(x)) for x in lst] 

2373 lst.sort() 

2374 print("Probably %i classes: %s" % (len(classes), classes)) 

2375 for id, pr in lst: 

2376 print("%5i" % id, pr) 

2377 

2378 

2379@conf.commands.register 

2380def fragleak(target, sport=123, dport=123, timeout=0.2, onlyasc=0, count=None): 

2381 load = "XXXXYYYYYYYYYY" 

2382 pkt = IP(dst=target, id=RandShort(), options=b"\x00" * 40, flags=1) 

2383 pkt /= UDP(sport=sport, dport=sport) / load 

2384 s = conf.L3socket() 

2385 intr = 0 

2386 found = {} 

2387 try: 

2388 while count is None or count: 

2389 if count is not None and isinstance(count, int): 

2390 count -= 1 

2391 try: 

2392 if not intr: 

2393 s.send(pkt) 

2394 sin = select.select([s], [], [], timeout)[0] 

2395 if not sin: 

2396 continue 

2397 ans = s.recv(1600) 

2398 if not isinstance(ans, IP): # TODO: IPv6 

2399 continue 

2400 if not isinstance(ans.payload, ICMP): 

2401 continue 

2402 if not isinstance(ans.payload.payload, IPerror): 

2403 continue 

2404 if ans.payload.payload.dst != target: 

2405 continue 

2406 if ans.src != target: 

2407 print("leak from", ans.src) 

2408 if not ans.haslayer(conf.padding_layer): 

2409 continue 

2410 leak = ans.getlayer(conf.padding_layer).load 

2411 if leak not in found: 

2412 found[leak] = None 

2413 linehexdump(leak, onlyasc=onlyasc) 

2414 except KeyboardInterrupt: 

2415 if intr: 

2416 raise 

2417 intr = 1 

2418 except KeyboardInterrupt: 

2419 pass 

2420 

2421 

2422@conf.commands.register 

2423def fragleak2(target, timeout=0.4, onlyasc=0, count=None): 

2424 found = {} 

2425 try: 

2426 while count is None or count: 

2427 if count is not None and isinstance(count, int): 

2428 count -= 1 

2429 

2430 pkt = IP(dst=target, options=b"\x00" * 40, proto=200) 

2431 pkt /= "XXXXYYYYYYYYYYYY" 

2432 p = sr1(pkt, timeout=timeout, verbose=0) 

2433 if not p: 

2434 continue 

2435 if conf.padding_layer in p: 

2436 leak = p[conf.padding_layer].load 

2437 if leak not in found: 

2438 found[leak] = None 

2439 linehexdump(leak, onlyasc=onlyasc) 

2440 except Exception: 

2441 pass 

2442 

2443 

2444@conf.commands.register 

2445class connect_from_ip: 

2446 """ 

2447 Open a TCP socket to a host:port while spoofing another IP. 

2448 

2449 :param host: the host to connect to 

2450 :param port: the port to connect to 

2451 :param srcip: the IP to spoof. the cache of the gateway will 

2452 be poisonned with this IP. 

2453 :param poison: (optional, default True) ARP poison the gateway (or next hop), 

2454 so that it answers us (only one packet). 

2455 :param timeout: (optional) the socket timeout. 

2456 

2457 Example - Connect to 192.168.0.1:80 spoofing 192.168.0.2:: 

2458 

2459 from scapy.layers.http import HTTP, HTTPRequest 

2460 client = connect_from_ip("192.168.0.1", 80, "192.168.0.2") 

2461 sock = SSLStreamSocket(client.sock, HTTP) 

2462 resp = sock.sr1(HTTP() / HTTPRequest(Path="/")) 

2463 

2464 Example - Connect to 192.168.0.1:443 with TLS wrapping spoofing 192.168.0.2:: 

2465 

2466 import ssl 

2467 from scapy.layers.http import HTTP, HTTPRequest 

2468 context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) 

2469 context.check_hostname = False 

2470 context.verify_mode = ssl.CERT_NONE 

2471 client = connect_from_ip("192.168.0.1", 443, "192.168.0.2") 

2472 sock = context.wrap_socket(client.sock) 

2473 sock = SSLStreamSocket(client.sock, HTTP) 

2474 resp = sock.sr1(HTTP() / HTTPRequest(Path="/")) 

2475 """ 

2476 

2477 def __init__(self, host, port, srcip, poison=True, timeout=1, debug=0): 

2478 host = str(Net(host)) 

2479 if poison: 

2480 # poison the next hop 

2481 gateway = conf.route.route(host)[2] 

2482 if gateway == "0.0.0.0": 

2483 # on lan 

2484 gateway = host 

2485 getmacbyip(gateway) # cache real gateway before poisoning 

2486 arpcachepoison(gateway, srcip, count=1, interval=0, verbose=0) 

2487 # create a socket pair 

2488 self._sock, self.sock = socket.socketpair() 

2489 self.sock.settimeout(timeout) 

2490 self.client = TCP_client( 

2491 host, port, 

2492 srcip=srcip, 

2493 external_fd={"tcp": self._sock}, 

2494 debug=debug, 

2495 ) 

2496 # start the TCP_client 

2497 self.client.runbg() 

2498 

2499 def close(self): 

2500 self.client.stop() 

2501 self.client.destroy() 

2502 self.sock.close() 

2503 self._sock.close() 

2504 

2505 

2506class ICMPEcho_am(AnsweringMachine): 

2507 """Responds to ICMP Echo-Requests (ping)""" 

2508 function_name = "icmpechod" 

2509 

2510 def is_request(self, req): 

2511 if req.haslayer(ICMP): 

2512 icmp_req = req.getlayer(ICMP) 

2513 if icmp_req.type == 8: # echo-request 

2514 return True 

2515 

2516 return False 

2517 

2518 def print_reply(self, req, reply): 

2519 print("Replying %s to %s" % (reply[IP].dst, req[IP].dst)) 

2520 

2521 def make_reply(self, req): 

2522 reply = req.copy() 

2523 reply[ICMP].type = 0 # echo-reply 

2524 # Force re-generation of the checksum 

2525 reply[ICMP].chksum = None 

2526 if req.haslayer(IP): 

2527 reply[IP].src, reply[IP].dst = req[IP].dst, req[IP].src 

2528 reply[IP].chksum = None 

2529 if req.haslayer(Ether): 

2530 reply[Ether].src, reply[Ether].dst = ( 

2531 None if req[Ether].dst == "ff:ff:ff:ff:ff:ff" else req[Ether].dst, 

2532 req[Ether].src, 

2533 ) 

2534 return reply 

2535 

2536 

2537conf.stats_classic_protocols += [TCP, UDP, ICMP] 

2538conf.stats_dot11_protocols += [TCP, UDP, ICMP] 

2539 

2540if conf.ipv6_enabled: 

2541 import scapy.layers.inet6