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

1423 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.consts import OPENBSD 

23from scapy.data import ( 

24 ETH_P_IP, 

25 ETH_P_ALL, 

26 DLT_RAW, 

27 DLT_RAW_ALT, 

28 DLT_IPV4, 

29 IP_PROTOS, 

30 TCP_SERVICES, 

31 UDP_SERVICES, 

32) 

33from scapy.layers.l2 import ( 

34 CookedLinux, 

35 Dot3, 

36 Ether, 

37 GRE, 

38 Loopback, 

39 SNAP, 

40 arpcachepoison, 

41 getmacbyip, 

42) 

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

44from scapy.config import conf 

45from scapy.fields import ( 

46 BitEnumField, 

47 BitField, 

48 ByteEnumField, 

49 ByteField, 

50 ConditionalField, 

51 DestField, 

52 Emph, 

53 FieldLenField, 

54 FieldListField, 

55 FlagsField, 

56 IPField, 

57 IP6Field, 

58 IntField, 

59 MayEnd, 

60 MultiEnumField, 

61 MultipleTypeField, 

62 PacketField, 

63 PacketListField, 

64 ShortEnumField, 

65 ShortField, 

66 SourceIPField, 

67 StrField, 

68 StrFixedLenField, 

69 StrLenField, 

70 TrailerField, 

71 XByteField, 

72 XShortField, 

73) 

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

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

76from scapy.sendrecv import sr, sr1 

77from scapy.plist import _PacketList, PacketList, SndRcvList 

78from scapy.automaton import Automaton, ATMT 

79from scapy.error import log_runtime, warning 

80from scapy.pton_ntop import inet_pton 

81 

82import scapy.as_resolvers 

83 

84#################### 

85# IP Tools class # 

86#################### 

87 

88 

89class IPTools(object): 

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

91 __slots__ = [] 

92 

93 def whois(self): 

94 """whois the source and print the output""" 

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

96 

97 def _ttl(self): 

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

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

100 

101 def ottl(self): 

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

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

104 

105 def hops(self): 

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

107 

108 

109_ip_options_names = {0: "end_of_list", 

110 1: "nop", 

111 2: "security", 

112 3: "loose_source_route", 

113 4: "timestamp", 

114 5: "extended_security", 

115 6: "commercial_security", 

116 7: "record_route", 

117 8: "stream_id", 

118 9: "strict_source_route", 

119 10: "experimental_measurement", 

120 11: "mtu_probe", 

121 12: "mtu_reply", 

122 13: "flow_control", 

123 14: "access_control", 

124 15: "encode", 

125 16: "imi_traffic_descriptor", 

126 17: "extended_IP", 

127 18: "traceroute", 

128 19: "address_extension", 

129 20: "router_alert", 

130 21: "selective_directed_broadcast_mode", 

131 23: "dynamic_packet_state", 

132 24: "upstream_multicast_packet", 

133 25: "quick_start", 

134 30: "rfc4727_experiment", 

135 } 

136 

137 

138class _IPOption_HDR(Packet): 

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

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

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

142 

143 

144class IPOption(Packet): 

145 name = "IP Option" 

146 fields_desc = [_IPOption_HDR, 

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

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

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

150 

151 def extract_padding(self, p): 

152 return b"", p 

153 

154 registered_ip_options = {} 

155 

156 @classmethod 

157 def register_variant(cls): 

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

159 

160 @classmethod 

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

162 if pkt: 

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

164 if opt in cls.registered_ip_options: 

165 return cls.registered_ip_options[opt] 

166 return cls 

167 

168 

169class IPOption_EOL(IPOption): 

170 name = "IP Option End of Options List" 

171 option = 0 

172 fields_desc = [_IPOption_HDR] 

173 

174 

175class IPOption_NOP(IPOption): 

176 name = "IP Option No Operation" 

177 option = 1 

178 fields_desc = [_IPOption_HDR] 

179 

180 

181class IPOption_Security(IPOption): 

182 name = "IP Option Security" 

183 copy_flag = 1 

184 option = 2 

185 fields_desc = [_IPOption_HDR, 

186 ByteField("length", 11), 

187 ShortField("security", 0), 

188 ShortField("compartment", 0), 

189 ShortField("handling_restrictions", 0), 

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

191 ] 

192 

193 

194class IPOption_RR(IPOption): 

195 name = "IP Option Record Route" 

196 option = 7 

197 fields_desc = [_IPOption_HDR, 

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

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

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

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

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

203 ] 

204 

205 def get_current_router(self): 

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

207 

208 

209class IPOption_LSRR(IPOption_RR): 

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

211 copy_flag = 1 

212 option = 3 

213 

214 

215class IPOption_SSRR(IPOption_RR): 

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

217 copy_flag = 1 

218 option = 9 

219 

220 

221class IPOption_Stream_Id(IPOption): 

222 name = "IP Option Stream ID" 

223 copy_flag = 1 

224 option = 8 

225 fields_desc = [_IPOption_HDR, 

226 ByteField("length", 4), 

227 ShortField("security", 0), ] 

228 

229 

230class IPOption_MTU_Probe(IPOption): 

231 name = "IP Option MTU Probe" 

232 option = 11 

233 fields_desc = [_IPOption_HDR, 

234 ByteField("length", 4), 

235 ShortField("mtu", 0), ] 

236 

237 

238class IPOption_MTU_Reply(IPOption_MTU_Probe): 

239 name = "IP Option MTU Reply" 

240 option = 12 

241 

242 

243class IPOption_Traceroute(IPOption): 

244 name = "IP Option Traceroute" 

245 option = 18 

246 fields_desc = [_IPOption_HDR, 

247 ByteField("length", 12), 

248 ShortField("id", 0), 

249 ShortField("outbound_hops", 0), 

250 ShortField("return_hops", 0), 

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

252 

253 

254class IPOption_Timestamp(IPOption): 

255 name = "IP Option Timestamp" 

256 optclass = 2 

257 option = 4 

258 fields_desc = [_IPOption_HDR, 

259 ByteField("length", None), 

260 ByteField("pointer", 9), 

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

262 BitEnumField("flg", 1, 4, 

263 {0: "timestamp_only", 

264 1: "timestamp_and_ip_addr", 

265 3: "prespecified_ip_addr"}), 

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

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

268 IntField('timestamp', 0)] 

269 

270 def post_build(self, p, pay): 

271 if self.length is None: 

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

273 return p + pay 

274 

275 

276class IPOption_Address_Extension(IPOption): 

277 name = "IP Option Address Extension" 

278 copy_flag = 1 

279 option = 19 

280 fields_desc = [_IPOption_HDR, 

281 ByteField("length", 10), 

282 IPField("src_ext", "0.0.0.0"), 

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

284 

285 

286class IPOption_Router_Alert(IPOption): 

287 name = "IP Option Router Alert" 

288 copy_flag = 1 

289 option = 20 

290 fields_desc = [_IPOption_HDR, 

291 ByteField("length", 4), 

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

293 

294 

295class IPOption_SDBM(IPOption): 

296 name = "IP Option Selective Directed Broadcast Mode" 

297 copy_flag = 1 

298 option = 21 

299 fields_desc = [_IPOption_HDR, 

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

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

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

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

304 ] 

305 

306 

307TCPOptions = ( 

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

309 1: ("NOP", None), 

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

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

312 4: ("SAckOK", None), 

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

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

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

316 15: ("AltChkSumOpt", None), 

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

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

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

320 29: ("AO", None), 

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

322 # RFC 3692 

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

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

325 }, 

326 {"EOL": 0, 

327 "NOP": 1, 

328 "MSS": 2, 

329 "WScale": 3, 

330 "SAckOK": 4, 

331 "SAck": 5, 

332 "Timestamp": 8, 

333 "AltChkSum": 14, 

334 "AltChkSumOpt": 15, 

335 "MD5": 19, 

336 "Mood": 25, 

337 "UTO": 28, 

338 "AO": 29, 

339 "TFO": 34, 

340 }) 

341 

342 

343class TCPAOValue(Packet): 

344 """Value of TCP-AO option""" 

345 fields_desc = [ 

346 ByteField("keyid", None), 

347 ByteField("rnextkeyid", None), 

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

349 ] 

350 

351 

352def get_tcpao(tcphdr): 

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

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

355 for optid, optval in tcphdr.options: 

356 if optid == 'AO': 

357 return optval 

358 return None 

359 

360 

361class RandTCPOptions(VolatileValue): 

362 def __init__(self, size=None): 

363 if size is None: 

364 size = RandNum(1, 5) 

365 self.size = size 

366 

367 def _fix(self): 

368 # Pseudo-Random amount of options 

369 # Random ("NAME", fmt) 

370 rand_patterns = [ 

371 random.choice(list( 

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

373 if opt != 'EOL' 

374 )) 

375 for _ in range(self.size) 

376 ] 

377 rand_vals = [] 

378 for oname, fmt in rand_patterns: 

379 if fmt is None: 

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

381 else: 

382 # Process the fmt arguments 1 by 1 

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

384 rval = [] 

385 for stru in structs: 

386 stru = "!" + stru 

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

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

389 else: # int 

390 _size = struct.calcsize(stru) 

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

392 rval.append(v) 

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

394 return rand_vals 

395 

396 def __bytes__(self): 

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

398 

399 

400class TCPOptionsField(StrField): 

401 islist = 1 

402 

403 def getfield(self, pkt, s): 

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

405 if opsz < 0: 

406 log_runtime.info( 

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

408 ) 

409 opsz = 0 

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

411 

412 def m2i(self, pkt, x): 

413 opt = [] 

414 while x: 

415 onum = orb(x[0]) 

416 if onum == 0: 

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

418 break 

419 if onum == 1: 

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

421 x = x[1:] 

422 continue 

423 try: 

424 olen = orb(x[1]) 

425 except IndexError: 

426 olen = 0 

427 if olen < 2: 

428 log_runtime.info( 

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

430 ) 

431 olen = 2 

432 oval = x[2:olen] 

433 if onum in TCPOptions[0]: 

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

435 if onum == 5: # SAck 

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

437 if onum == 29: # AO 

438 oval = TCPAOValue(oval) 

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

440 oval = struct.unpack(ofmt, oval) 

441 if len(oval) == 1: 

442 oval = oval[0] 

443 opt.append((oname, oval)) 

444 else: 

445 opt.append((onum, oval)) 

446 x = x[olen:] 

447 return opt 

448 

449 def i2h(self, pkt, x): 

450 if not x: 

451 return [] 

452 return x 

453 

454 def i2m(self, pkt, x): 

455 opt = b"" 

456 for oname, oval in x: 

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

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

459 if isinstance(oname, str): 

460 if oname == "NOP": 

461 opt += b"\x01" 

462 continue 

463 elif oname == "EOL": 

464 opt += b"\x00" 

465 continue 

466 elif oname in TCPOptions[1]: 

467 onum = TCPOptions[1][oname] 

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

469 if onum == 5: # SAck 

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

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

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

473 if not isinstance(oval, tuple): 

474 oval = (oval,) 

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

476 if onum == 29: # AO 

477 oval = bytes(oval) 

478 else: 

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

480 continue 

481 else: 

482 onum = oname 

483 if not isinstance(onum, int): 

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

485 continue 

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

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

488 continue 

489 if isinstance(oval, str): 

490 oval = bytes_encode(oval) 

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

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

493 

494 def randval(self): 

495 return RandTCPOptions() 

496 

497 

498class ICMPTimeStampField(IntField): 

499 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 

500 

501 def i2repr(self, pkt, val): 

502 if val is None: 

503 return "--" 

504 else: 

505 sec, milli = divmod(val, 1000) 

506 min, sec = divmod(sec, 60) 

507 hour, min = divmod(min, 60) 

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

509 

510 def any2i(self, pkt, val): 

511 if isinstance(val, str): 

512 hmsms = self.re_hmsm.match(val) 

513 if hmsms: 

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

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

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

517 else: 

518 val = 0 

519 elif val is None: 

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

521 return val 

522 

523 

524class DestIPField(IPField, DestField): 

525 bindings = {} 

526 

527 def __init__(self, name, default): 

528 IPField.__init__(self, name, None) 

529 DestField.__init__(self, name, default) 

530 

531 def i2m(self, pkt, x): 

532 if x is None: 

533 x = self.dst_from_pkt(pkt) 

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

535 

536 def i2h(self, pkt, x): 

537 if x is None: 

538 x = self.dst_from_pkt(pkt) 

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

540 

541 

542class IP(Packet, IPTools): 

543 name = "IP" 

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

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

546 XByteField("tos", 0), 

547 ShortField("len", None), 

548 ShortField("id", 1), 

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

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

551 ByteField("ttl", 64), 

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

553 XShortField("chksum", None), 

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

555 Emph(SourceIPField("src")), 

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

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

558 

559 def post_build(self, p, pay): 

560 ihl = self.ihl 

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

562 if ihl is None: 

563 ihl = len(p) // 4 

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

565 if self.len is None: 

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

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

568 if self.chksum is None: 

569 ck = checksum(p) 

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

571 return p + pay 

572 

573 def extract_padding(self, s): 

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

575 if tmp_len < 0: 

576 return s, b"" 

577 return s[:tmp_len], s[tmp_len:] 

578 

579 def route(self): 

580 dst = self.dst 

581 scope = None 

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

583 scope = dst.scope 

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

585 dst = next(iter(dst)) 

586 if conf.route is None: 

587 # unused import, only to initialize conf.route 

588 import scapy.route # noqa: F401 

589 if not isinstance(dst, (str, bytes, int)): 

590 dst = str(dst) 

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

592 

593 def hashret(self): 

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

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

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

597 return self.payload.payload.hashret() 

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

599 return self.payload.hashret() 

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

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

602 if conf.checkIPsrc and conf.checkIPaddr: 

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

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

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

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

607 

608 def answers(self, other): 

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

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

611 return self.payload.answers(other) 

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

613 return self.answers(other.payload) 

614 if conf.ipv6_enabled \ 

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

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

617 return self.answers(other.payload) 

618 if not isinstance(other, IP): 

619 return 0 

620 if conf.checkIPaddr: 

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

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

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

624 return 0 

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

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

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

628 # ICMP error message 

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

630 

631 else: 

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

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

634 return 0 

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

636 

637 def mysummary(self): 

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

639 if self.frag: 

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

641 return s 

642 

643 def fragment(self, fragsize=1480): 

644 """Fragment IP datagrams""" 

645 return fragment(self, fragsize=fragsize) 

646 

647 

648def in4_pseudoheader(proto, u, plen): 

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

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

651 

652 :param proto: value of upper layer protocol 

653 :param u: IP layer instance 

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

655 """ 

656 u = u.copy() 

657 if u.len is not None: 

658 if u.ihl is None: 

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

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

661 else: 

662 ihl = u.ihl 

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

664 else: 

665 ln = plen 

666 

667 # Filter out IPOption_LSRR and IPOption_SSRR 

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

669 isinstance(opt, IPOption_SSRR)] 

670 len_sr_options = len(sr_options) 

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

672 # The checksum must be computed using the final 

673 # destination address 

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

675 elif len_sr_options > 1: 

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

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

678 warning(message, len_sr_options) 

679 

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

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

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

683 proto, 

684 ln) 

685 

686 

687def in4_chksum(proto, u, p): 

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

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

690 

691 :param proto: value of upper layer protocol 

692 :param u: upper layer instance 

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

694 """ 

695 if not isinstance(u, IP): 

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

697 return 0 

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

699 return checksum(psdhdr + p) 

700 

701 

702def _is_ipv6_layer(p): 

703 # type: (Packet) -> bytes 

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

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

706 

707 

708def tcp_pseudoheader(tcp): 

709 # type: (TCP) -> bytes 

710 """Pseudoheader of a TCP packet as bytes 

711 

712 Requires underlayer to be either IP or IPv6 

713 """ 

714 if isinstance(tcp.underlayer, IP): 

715 plen = len(bytes(tcp)) 

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

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

718 plen = len(bytes(tcp)) 

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

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

721 else: 

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

723 

724 

725def calc_tcp_md5_hash(tcp, key): 

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

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

728 import hashlib 

729 

730 h = hashlib.md5() # nosec 

731 tcp_bytes = bytes(tcp) 

732 h.update(tcp_pseudoheader(tcp)) 

733 h.update(tcp_bytes[:16]) 

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

735 h.update(tcp_bytes[18:]) 

736 h.update(key) 

737 

738 return h.digest() 

739 

740 

741def sign_tcp_md5(tcp, key): 

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

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

744 sig = calc_tcp_md5_hash(tcp, key) 

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

746 

747 

748class TCP(Packet): 

749 name = "TCP" 

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

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

752 IntField("seq", 0), 

753 IntField("ack", 0), 

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

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

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

757 ShortField("window", 8192), 

758 XShortField("chksum", None), 

759 ShortField("urgptr", 0), 

760 TCPOptionsField("options", "")] 

761 

762 def post_build(self, p, pay): 

763 p += pay 

764 dataofs = self.dataofs 

765 if dataofs is None: 

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

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

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

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

770 if self.chksum is None: 

771 if isinstance(self.underlayer, IP): 

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

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

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

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

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

777 else: 

778 log_runtime.info( 

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

780 ) 

781 return p 

782 

783 def hashret(self): 

784 if conf.checkIPsrc: 

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

786 else: 

787 return self.payload.hashret() 

788 

789 def answers(self, other): 

790 if not isinstance(other, TCP): 

791 return 0 

792 # RST packets don't get answers 

793 if other.flags.R: 

794 return 0 

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

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

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

798 if self.flags.S: 

799 # SYN packets without ACK are not answers 

800 if not self.flags.A: 

801 return 0 

802 # SYN+ACK packets answer SYN packets 

803 if not other.flags.S: 

804 return 0 

805 if conf.checkIPsrc: 

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

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

808 return 0 

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

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

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

812 return 0 

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

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

815 return 1 

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

817 return 0 

818 return 1 

819 

820 def mysummary(self): 

821 if isinstance(self.underlayer, IP): 

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

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

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

825 else: 

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

827 

828 

829class UDP(Packet): 

830 name = "UDP" 

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

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

833 ShortField("len", None), 

834 XShortField("chksum", None), ] 

835 

836 def post_build(self, p, pay): 

837 p += pay 

838 tmp_len = self.len 

839 if tmp_len is None: 

840 tmp_len = len(p) 

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

842 if self.chksum is None: 

843 if isinstance(self.underlayer, IP): 

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

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

846 if ck == 0: 

847 ck = 0xFFFF 

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

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

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

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

852 if ck == 0: 

853 ck = 0xFFFF 

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

855 else: 

856 log_runtime.info( 

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

858 ) 

859 return p 

860 

861 def extract_padding(self, s): 

862 tmp_len = self.len - 8 

863 return s[:tmp_len], s[tmp_len:] 

864 

865 def hashret(self): 

866 return self.payload.hashret() 

867 

868 def answers(self, other): 

869 if not isinstance(other, UDP): 

870 return 0 

871 if conf.checkIPsrc: 

872 if self.dport != other.sport: 

873 return 0 

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

875 

876 def mysummary(self): 

877 if isinstance(self.underlayer, IP): 

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

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

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

881 else: 

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

883 

884 

885# RFC 4884 ICMP extensions 

886_ICMP_classnums = { 

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

888 1: "MPLS", 

889 2: "Interface Information", 

890 3: "Interface Identification", 

891 4: "Extended Information", 

892} 

893 

894 

895class ICMPExtension_Object(Packet): 

896 name = "ICMP Extension Object" 

897 show_indent = 0 

898 fields_desc = [ 

899 ShortField("len", None), 

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

901 ByteField("classtype", 0), 

902 ] 

903 

904 def post_build(self, p, pay): 

905 if self.len is None: 

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

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

908 return p + pay 

909 

910 registered_icmp_exts = {} 

911 

912 @classmethod 

913 def register_variant(cls): 

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

915 

916 @classmethod 

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

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

919 classnum = _pkt[2] 

920 if classnum in cls.registered_icmp_exts: 

921 return cls.registered_icmp_exts[classnum] 

922 return cls 

923 

924 

925class ICMPExtension_InterfaceInformation(ICMPExtension_Object): 

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

927 

928 fields_desc = [ 

929 ShortField("len", None), 

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

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

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

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

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

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

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

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

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

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

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

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

942 ConditionalField( 

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

944 lambda pkt: pkt.has_ifname == 1, 

945 ), 

946 ConditionalField( 

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

948 lambda pkt: pkt.has_ifname == 1, 

949 ), 

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

951 ] 

952 

953 def self_build(self, **kwargs): 

954 if self.afi is None: 

955 if self.ip4 is not None: 

956 self.afi = 1 

957 elif self.ip6 is not None: 

958 self.afi = 2 

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

960 

961 

962class ICMPExtension_Header(Packet): 

963 r""" 

964 ICMP Extension per RFC4884. 

965 

966 Example:: 

967 

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

969 type="time-exceeded", 

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

971 ext=ICMPExtension_Header() / ICMPExtension_InterfaceInformation( 

972 has_ifindex=1, 

973 has_ipaddr=1, 

974 has_ifname=1, 

975 ip4="10.10.10.10", 

976 ifname="hey", 

977 ) 

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

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

980 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 

981 """ 

982 

983 name = "ICMP Extension Header (RFC4884)" 

984 show_indent = 0 

985 fields_desc = [ 

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

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

988 XShortField("chksum", None), 

989 ] 

990 

991 _min_ieo_len = len(ICMPExtension_Object()) 

992 

993 def post_build(self, p, pay): 

994 p += pay 

995 if self.chksum is None: 

996 ck = checksum(p) 

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

998 return p 

999 

1000 def guess_payload_class(self, payload): 

1001 if len(payload) < self._min_ieo_len: 

1002 return Packet.guess_payload_class(self, payload) 

1003 return ICMPExtension_Object 

1004 

1005 

1006class _ICMPExtensionField(TrailerField): 

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

1008 

1009 def __init__(self): 

1010 super(_ICMPExtensionField, self).__init__( 

1011 PacketField( 

1012 "ext", 

1013 None, 

1014 ICMPExtension_Header, 

1015 ), 

1016 ) 

1017 

1018 def getfield(self, pkt, s): 

1019 # RFC4884 section 5.2 says if the ICMP packet length 

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

1021 if len(pkt.original) < 144: 

1022 return s, None 

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

1024 data = s[offset:] 

1025 # Validate checksum 

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

1027 return s, None # failed 

1028 # Dissect 

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

1030 

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

1032 if val is None: 

1033 return s 

1034 data = bytes(val) 

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

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

1037 if pad < 0: 

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

1039 return data 

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

1041 

1042 

1043class _ICMPExtensionPadField(TrailerField): 

1044 def __init__(self): 

1045 super(_ICMPExtensionPadField, self).__init__( 

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

1047 ) 

1048 

1049 def i2repr(self, pkt, s): 

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

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

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

1053 

1054 

1055def _ICMP_extpad_post_dissection(self, pkt): 

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

1057 if pkt.ext: 

1058 pad = pkt.lastlayer() 

1059 if isinstance(pad, conf.padding_layer): 

1060 pad.underlayer.remove_payload() 

1061 pkt.extpad = pad.load 

1062 

1063 

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

1065 3: "dest-unreach", 

1066 4: "source-quench", 

1067 5: "redirect", 

1068 8: "echo-request", 

1069 9: "router-advertisement", 

1070 10: "router-solicitation", 

1071 11: "time-exceeded", 

1072 12: "parameter-problem", 

1073 13: "timestamp-request", 

1074 14: "timestamp-reply", 

1075 15: "information-request", 

1076 16: "information-response", 

1077 17: "address-mask-request", 

1078 18: "address-mask-reply", 

1079 30: "traceroute", 

1080 31: "datagram-conversion-error", 

1081 32: "mobile-host-redirect", 

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

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

1084 35: "mobile-registration-request", 

1085 36: "mobile-registration-reply", 

1086 37: "domain-name-request", 

1087 38: "domain-name-reply", 

1088 39: "skip", 

1089 40: "photuris"} 

1090 

1091 

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

1093 1: "host-unreachable", 

1094 2: "protocol-unreachable", 

1095 3: "port-unreachable", 

1096 4: "fragmentation-needed", 

1097 5: "source-route-failed", 

1098 6: "network-unknown", 

1099 7: "host-unknown", 

1100 9: "network-prohibited", 

1101 10: "host-prohibited", 

1102 11: "TOS-network-unreachable", 

1103 12: "TOS-host-unreachable", 

1104 13: "communication-prohibited", 

1105 14: "host-precedence-violation", 

1106 15: "precedence-cutoff", }, 

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

1108 1: "host-redirect", 

1109 2: "TOS-network-redirect", 

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

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

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

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

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

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

1116 1: "authentication-failed", 

1117 2: "decompression-failed", 

1118 3: "decryption-failed", 

1119 4: "need-authentification", 

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

1121 

1122 

1123_icmp_answers = [ 

1124 (8, 0), 

1125 (13, 14), 

1126 (15, 16), 

1127 (17, 18), 

1128 (33, 34), 

1129 (35, 36), 

1130 (37, 38), 

1131] 

1132 

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

1134 

1135 

1136class ICMP(Packet): 

1137 name = "ICMP" 

1138 fields_desc = [ 

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

1140 MultiEnumField("code", 0, icmpcodes, 

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

1142 XShortField("chksum", None), 

1143 ConditionalField( 

1144 XShortField("id", 0), 

1145 lambda pkt: pkt.type in icmp_id_seq_types 

1146 ), 

1147 ConditionalField( 

1148 XShortField("seq", 0), 

1149 lambda pkt: pkt.type in icmp_id_seq_types 

1150 ), 

1151 ConditionalField( 

1152 # Timestamp only (RFC792) 

1153 ICMPTimeStampField("ts_ori", None), 

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

1155 ), 

1156 ConditionalField( 

1157 # Timestamp only (RFC792) 

1158 ICMPTimeStampField("ts_rx", None), 

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

1160 ), 

1161 ConditionalField( 

1162 # Timestamp only (RFC792) 

1163 ICMPTimeStampField("ts_tx", None), 

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

1165 ), 

1166 ConditionalField( 

1167 # Redirect only (RFC792) 

1168 IPField("gw", "0.0.0.0"), 

1169 lambda pkt: pkt.type == 5 

1170 ), 

1171 ConditionalField( 

1172 # Parameter problem only (RFC792) 

1173 ByteField("ptr", 0), 

1174 lambda pkt: pkt.type == 12 

1175 ), 

1176 ConditionalField( 

1177 ByteField("reserved", 0), 

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

1179 ), 

1180 ConditionalField( 

1181 ByteField("length", 0), 

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

1183 ), 

1184 ConditionalField( 

1185 IPField("addr_mask", "0.0.0.0"), 

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

1187 ), 

1188 ConditionalField( 

1189 ShortField("nexthopmtu", 0), 

1190 lambda pkt: pkt.type == 3 

1191 ), 

1192 MultipleTypeField( 

1193 [ 

1194 (ShortField("unused", 0), 

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

1196 (IntField("unused", 0), 

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

1198 13, 14, 15, 16, 17, 

1199 18]) 

1200 ], 

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

1202 ), 

1203 # RFC4884 ICMP extension 

1204 ConditionalField( 

1205 _ICMPExtensionPadField(), 

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

1207 ), 

1208 ConditionalField( 

1209 _ICMPExtensionField(), 

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

1211 ), 

1212 ] 

1213 

1214 # To handle extpad 

1215 post_dissection = _ICMP_extpad_post_dissection 

1216 

1217 def post_build(self, p, pay): 

1218 p += pay 

1219 if self.chksum is None: 

1220 ck = checksum(p) 

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

1222 return p 

1223 

1224 def hashret(self): 

1225 if self.type in icmp_id_seq_types: 

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

1227 return self.payload.hashret() 

1228 

1229 def answers(self, other): 

1230 if not isinstance(other, ICMP): 

1231 return 0 

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

1233 self.id == other.id and 

1234 self.seq == other.seq): 

1235 return 1 

1236 return 0 

1237 

1238 def guess_payload_class(self, payload): 

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

1240 return IPerror 

1241 else: 

1242 return None 

1243 

1244 def mysummary(self): 

1245 extra = "" 

1246 if self.ext: 

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

1248 if isinstance(self.underlayer, IP): 

1249 return self.underlayer.sprintf( 

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

1251 ) + extra 

1252 else: 

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

1254 

1255 

1256# IP / TCP / UDP error packets 

1257 

1258class IPerror(IP): 

1259 name = "IP in ICMP" 

1260 

1261 def answers(self, other): 

1262 if not isinstance(other, IP): 

1263 return 0 

1264 

1265 # Check if IP addresses match 

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

1267 test_IPdst = self.dst == other.dst 

1268 

1269 # Check if IP ids match 

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

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

1272 

1273 # Check if IP protocols match 

1274 test_IPproto = self.proto == other.proto 

1275 

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

1277 return 0 

1278 

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

1280 

1281 def mysummary(self): 

1282 return Packet.mysummary(self) 

1283 

1284 

1285class TCPerror(TCP): 

1286 name = "TCP in ICMP" 

1287 fields_desc = ( 

1288 TCP.fields_desc[:2] + 

1289 # MayEnd after the 8 first octets. 

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

1291 TCP.fields_desc[3:] 

1292 ) 

1293 

1294 def answers(self, other): 

1295 if not isinstance(other, TCP): 

1296 return 0 

1297 if conf.checkIPsrc: 

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

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

1300 return 0 

1301 if conf.check_TCPerror_seqack: 

1302 if self.seq is not None: 

1303 if self.seq != other.seq: 

1304 return 0 

1305 if self.ack is not None: 

1306 if self.ack != other.ack: 

1307 return 0 

1308 return 1 

1309 

1310 def mysummary(self): 

1311 return Packet.mysummary(self) 

1312 

1313 

1314class UDPerror(UDP): 

1315 name = "UDP in ICMP" 

1316 

1317 def answers(self, other): 

1318 if not isinstance(other, UDP): 

1319 return 0 

1320 if conf.checkIPsrc: 

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

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

1323 return 0 

1324 return 1 

1325 

1326 def mysummary(self): 

1327 return Packet.mysummary(self) 

1328 

1329 

1330class ICMPerror(ICMP): 

1331 name = "ICMP in ICMP" 

1332 

1333 def answers(self, other): 

1334 if not isinstance(other, ICMP): 

1335 return 0 

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

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

1338 return 0 

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

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

1341 self.seq == other.seq): 

1342 return 1 

1343 else: 

1344 return 0 

1345 else: 

1346 return 1 

1347 

1348 def mysummary(self): 

1349 return Packet.mysummary(self) 

1350 

1351 

1352bind_layers(Ether, IP, type=2048) 

1353bind_layers(CookedLinux, IP, proto=2048) 

1354bind_layers(GRE, IP, proto=2048) 

1355bind_layers(SNAP, IP, code=2048) 

1356bind_bottom_up(Loopback, IP, type=0) 

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

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

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

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

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

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

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

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

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

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

1367bind_layers(UDP, GRE, dport=4754) 

1368 

1369conf.l2types.register(DLT_RAW, IP) 

1370conf.l2types.register_num2layer(DLT_RAW_ALT, IP) 

1371conf.l2types.register(DLT_IPV4, IP) 

1372if OPENBSD: 

1373 conf.l2types.register_num2layer(228, IP) 

1374 

1375conf.l3types.register(ETH_P_IP, IP) 

1376conf.l3types.register_num2layer(ETH_P_ALL, IP) 

1377 

1378 

1379def inet_register_l3(l2, l3): 

1380 """ 

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

1382 """ 

1383 return getmacbyip(l3.dst) 

1384 

1385 

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

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

1388 

1389 

1390################### 

1391# Fragmentation # 

1392################### 

1393 

1394@conf.commands.register 

1395def fragment(pkt, fragsize=1480): 

1396 """Fragment a big IP datagram""" 

1397 if fragsize < 8: 

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

1399 fragsize = max(fragsize, 8) 

1400 lastfragsz = fragsize 

1401 fragsize -= fragsize % 8 

1402 lst = [] 

1403 for p in pkt: 

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

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

1406 for i in range(nb): 

1407 q = p.copy() 

1408 del q[IP].payload 

1409 del q[IP].chksum 

1410 del q[IP].len 

1411 if i != nb - 1: 

1412 q[IP].flags |= 1 

1413 fragend = (i + 1) * fragsize 

1414 else: 

1415 fragend = i * fragsize + lastfragsz 

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

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

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

1419 q.add_payload(r) 

1420 lst.append(q) 

1421 return lst 

1422 

1423 

1424@conf.commands.register 

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

1426 """Build overlapping fragments to bypass NIPS 

1427 

1428p: the original packet 

1429overlap: the overlapping data 

1430fragsize: the fragment size of the packet 

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

1432 

1433 if overlap_fragsize is None: 

1434 overlap_fragsize = fragsize 

1435 q = p.copy() 

1436 del q[IP].payload 

1437 q[IP].add_payload(overlap) 

1438 

1439 qfrag = fragment(q, overlap_fragsize) 

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

1441 return qfrag + fragment(p, fragsize) 

1442 

1443 

1444class BadFragments(ValueError): 

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

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

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

1448 

1449 

1450def _defrag_iter_and_check_offsets(frags): 

1451 """ 

1452 Internal generator used in _defrag_ip_pkt 

1453 """ 

1454 offset = 0 

1455 for pkt, o, length in frags: 

1456 if offset != o: 

1457 if offset > o: 

1458 op = ">" 

1459 else: 

1460 op = "<" 

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

1462 raise BadFragments 

1463 offset += length 

1464 yield bytes(pkt[IP].payload) 

1465 

1466 

1467def _defrag_ip_pkt(pkt, frags): 

1468 """ 

1469 Defragment a single IP packet. 

1470 

1471 :param pkt: the new pkt 

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

1473 :return: a tuple (fragmented, defragmented_value) 

1474 """ 

1475 ip = pkt[IP] 

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

1477 # fragmented ! 

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

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

1480 fraglen = len(ip.payload) 

1481 else: 

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

1483 # (pkt, frag offset, frag len) 

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

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

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

1487 try: 

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

1489 except ValueError: 

1490 # bad fragment 

1491 badfrags = frags[uid] 

1492 del frags[uid] 

1493 raise BadFragments(frags=badfrags) 

1494 # re-build initial packet without fragmentation 

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

1496 pay_class = p[IP].payload.__class__ 

1497 p[IP].flags.MF = False 

1498 p[IP].remove_payload() 

1499 p[IP].len = None 

1500 p[IP].chksum = None 

1501 # append defragmented payload 

1502 p /= pay_class(data) 

1503 # cleanup 

1504 del frags[uid] 

1505 return True, p 

1506 return True, None 

1507 return False, pkt 

1508 

1509 

1510def _defrag_logic(plist, complete=False): 

1511 """ 

1512 Internal function used to defragment a list of packets. 

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

1514 """ 

1515 frags = defaultdict(list) 

1516 final = [] 

1517 notfrag = [] 

1518 badfrag = [] 

1519 # Defrag 

1520 for i, pkt in enumerate(plist): 

1521 if IP not in pkt: 

1522 # no IP layer 

1523 if complete: 

1524 final.append(pkt) 

1525 continue 

1526 try: 

1527 fragmented, defragmented_value = _defrag_ip_pkt( 

1528 pkt, 

1529 frags, 

1530 ) 

1531 except BadFragments as ex: 

1532 if complete: 

1533 final.extend(ex.frags) 

1534 else: 

1535 badfrag.extend(ex.frags) 

1536 continue 

1537 if complete and defragmented_value: 

1538 final.append(defragmented_value) 

1539 elif defragmented_value: 

1540 if fragmented: 

1541 final.append(defragmented_value) 

1542 else: 

1543 notfrag.append(defragmented_value) 

1544 # Return 

1545 if complete: 

1546 if hasattr(plist, "listname"): 

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

1548 else: 

1549 name = "Defragmented" 

1550 return PacketList(final, name=name) 

1551 else: 

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

1553 

1554 

1555@conf.commands.register 

1556def defrag(plist): 

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

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

1559 return _defrag_logic(plist, complete=False) 

1560 

1561 

1562@conf.commands.register 

1563def defragment(plist): 

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

1565 return _defrag_logic(plist, complete=True) 

1566 

1567 

1568# Add timeskew_graph() method to PacketList 

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

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

1571 # Defer imports of matplotlib until its needed 

1572 # because it has a heavy dep chain 

1573 from scapy.libs.matplot import ( 

1574 plt, 

1575 MATPLOTLIB_INLINED, 

1576 MATPLOTLIB_DEFAULT_PLOT_KARGS 

1577 ) 

1578 

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

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

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

1582 

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

1584 c = [] 

1585 tsf = ICMPTimeStampField("", None) 

1586 for p in b: 

1587 opts = p.getlayer(TCP).options 

1588 for o in opts: 

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

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

1591 

1592 # Stop if the list is empty 

1593 if not c: 

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

1595 return [] 

1596 

1597 # Prepare the data that will be plotted 

1598 first_creation_time = c[0][0] 

1599 first_replied_timestamp = c[0][1] 

1600 

1601 def _wrap_data(ts_tuple, wrap_seconds=2000): 

1602 """Wrap the list of tuples.""" 

1603 

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

1605 X = ct % wrap_seconds 

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

1607 

1608 return X, Y 

1609 

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

1611 

1612 # Mimic the default gnuplot output 

1613 if kargs == {}: 

1614 kargs = MATPLOTLIB_DEFAULT_PLOT_KARGS 

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

1616 

1617 # Call show() if matplotlib is not inlined 

1618 if not MATPLOTLIB_INLINED: 

1619 plt.show() 

1620 

1621 return lines 

1622 

1623 

1624_PacketList.timeskew_graph = _packetlist_timeskew_graph 

1625 

1626 

1627# Create a new packet list 

1628class TracerouteResult(SndRcvList): 

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

1630 "nloc"] 

1631 

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

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

1634 self.graphdef = None 

1635 self.graphASres = None 

1636 self.padding = 0 

1637 self.hloc = None 

1638 self.nloc = None 

1639 

1640 def show(self): 

1641 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 

1642 s.ttl, 

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

1644 

1645 def get_trace(self): 

1646 trace = {} 

1647 for s, r in self.res: 

1648 if IP not in s: 

1649 continue 

1650 d = s[IP].dst 

1651 if d not in trace: 

1652 trace[d] = {} 

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

1654 for k in trace.values(): 

1655 try: 

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

1657 except ValueError: 

1658 continue 

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

1660 if li > m: 

1661 del k[li] 

1662 return trace 

1663 

1664 def trace3D(self, join=True): 

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

1666 right button: rotate the scene 

1667 middle button: zoom 

1668 shift-left button: move the scene 

1669 left button on a ball: toggle IP displaying 

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

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

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

1673 import multiprocessing 

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

1675 p.start() 

1676 if join: 

1677 p.join() 

1678 

1679 def trace3D_notebook(self): 

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

1681 trace = self.get_trace() 

1682 import vpython 

1683 

1684 class IPsphere(vpython.sphere): 

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

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

1687 self.ip = ip 

1688 self.label = None 

1689 self.setlabel(self.ip) 

1690 self.last_clicked = None 

1691 self.full = False 

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

1693 

1694 def fullinfos(self): 

1695 self.full = True 

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

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

1698 if len(a) == 0: 

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

1700 else: 

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

1702 for s, r in a: 

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

1704 self.setlabel(txt, visible=1) 

1705 

1706 def unfull(self): 

1707 self.color = self.savcolor 

1708 self.full = False 

1709 self.setlabel(self.ip) 

1710 

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

1712 if self.label is not None: 

1713 if visible is None: 

1714 visible = self.label.visible 

1715 self.label.visible = 0 

1716 elif visible is None: 

1717 visible = 0 

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

1719 

1720 def check_double_click(self): 

1721 try: 

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

1723 return False 

1724 if self.last_clicked is not None: 

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

1726 return False 

1727 finally: 

1728 self.last_clicked = time.time() 

1729 

1730 def action(self): 

1731 self.label.visible ^= 1 

1732 if self.full: 

1733 self.unfull() 

1734 

1735 vpython.scene = vpython.canvas() 

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

1737 vpython.scene.append_to_caption( 

1738 re.sub( 

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

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

1741 re.sub( 

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

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

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

1745%Click% to toggle information about a node. 

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

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

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

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

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

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

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

1753 ) 

1754 ) 

1755 ) 

1756 vpython.scene.exit = True 

1757 rings = {} 

1758 tr3d = {} 

1759 for i in trace: 

1760 tr = trace[i] 

1761 tr3d[i] = [] 

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

1763 if t not in rings: 

1764 rings[t] = [] 

1765 if t in tr: 

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

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

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

1769 else: 

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

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

1772 

1773 for t in rings: 

1774 r = rings[t] 

1775 tmp_len = len(r) 

1776 for i in range(tmp_len): 

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

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

1779 elif r[i][1]: 

1780 col = vpython.color.green 

1781 else: 

1782 col = vpython.color.blue 

1783 

1784 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 

1785 ip=r[i][0], 

1786 color=col) 

1787 for trlst in tr3d.values(): 

1788 if t <= len(trlst): 

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

1790 trlst[t - 1] = s 

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

1792 for trlst in tr3d.values(): 

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

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

1795 for ip in trlst: 

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

1797 start = ip.pos 

1798 

1799 vpython.rate(50) 

1800 

1801 # Keys handling 

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

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

1804 # def keyboard_press(ev): 

1805 # k = ev.key 

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

1807 # pass # TODO: close 

1808 # 

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

1810 

1811 # Mouse handling 

1812 def mouse_click(ev): 

1813 if ev.press == "left": 

1814 o = vpython.scene.mouse.pick 

1815 if o and isinstance(o, IPsphere): 

1816 if o.check_double_click(): 

1817 if o.ip == "unk": 

1818 return 

1819 o.fullinfos() 

1820 else: 

1821 o.action() 

1822 

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

1824 

1825 def world_trace(self): 

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

1827 

1828 # Check that the geoip2 module can be imported 

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

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

1831 

1832 try: 

1833 # GeoIP2 modules need to be imported as below 

1834 import geoip2.database 

1835 import geoip2.errors 

1836 except ImportError: 

1837 log_runtime.error( 

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

1839 ) 

1840 return [] 

1841 # Check availability of database 

1842 if not conf.geoip_city: 

1843 log_runtime.error( 

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

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

1846 " then set its path to conf.geoip_city" 

1847 ) 

1848 return [] 

1849 # Check availability of plotting devices 

1850 try: 

1851 import cartopy.crs as ccrs 

1852 except ImportError: 

1853 log_runtime.error( 

1854 "Cannot import cartopy.\n" 

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

1856 ) 

1857 return [] 

1858 if not MATPLOTLIB: 

1859 log_runtime.error( 

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

1861 ) 

1862 return [] 

1863 

1864 # Open & read the GeoListIP2 database 

1865 try: 

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

1867 except Exception: 

1868 log_runtime.error( 

1869 "Cannot open geoip2 database at %s", 

1870 conf.geoip_city 

1871 ) 

1872 return [] 

1873 

1874 # Regroup results per trace 

1875 ips = {} 

1876 rt = {} 

1877 ports_done = {} 

1878 for s, r in self.res: 

1879 ips[r.src] = None 

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

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

1882 elif s.haslayer(ICMP): 

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

1884 else: 

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

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

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

1888 if trace_id in ports_done: 

1889 continue 

1890 ports_done[trace_id] = None 

1891 trace[s.ttl] = r.src 

1892 rt[trace_id] = trace 

1893 

1894 # Get the addresses locations 

1895 trt = {} 

1896 for trace_id in rt: 

1897 trace = rt[trace_id] 

1898 loctrace = [] 

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

1900 ip = trace.get(i, None) 

1901 if ip is None: 

1902 continue 

1903 # Fetch database 

1904 try: 

1905 sresult = db.city(ip) 

1906 except geoip2.errors.AddressNotFoundError: 

1907 continue 

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

1909 if loctrace: 

1910 trt[trace_id] = loctrace 

1911 

1912 # Load the map renderer 

1913 plt.figure(num='Scapy') 

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

1915 # Draw countries 

1916 ax.coastlines() 

1917 ax.stock_img() 

1918 # Set normal size 

1919 ax.set_global() 

1920 # Add title 

1921 plt.title("Scapy traceroute results") 

1922 

1923 from matplotlib.collections import LineCollection 

1924 from matplotlib import colors as mcolors 

1925 colors_cycle = iter(mcolors.BASE_COLORS) 

1926 lines = [] 

1927 

1928 # Split traceroute measurement 

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

1930 # Get next color 

1931 color = next(colors_cycle) 

1932 # Gather mesurments data 

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

1934 # Create line collection 

1935 line_col = LineCollection(data_lines, linewidths=2, 

1936 label=key[1], 

1937 color=color) 

1938 lines.append(line_col) 

1939 ax.add_collection(line_col) 

1940 # Create map points 

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

1942 

1943 # Generate legend 

1944 ax.legend() 

1945 

1946 # Call show() if matplotlib is not inlined 

1947 if not MATPLOTLIB_INLINED: 

1948 plt.show() 

1949 

1950 # Clean 

1951 ax.remove() 

1952 

1953 # Return the drawn lines 

1954 return lines 

1955 

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

1957 self.graphASres = ASres 

1958 self.graphpadding = padding 

1959 ips = {} 

1960 rt = {} 

1961 ports = {} 

1962 ports_done = {} 

1963 for s, r in self.res: 

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

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

1966 ips[r.src] = None 

1967 if TCP in s: 

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

1969 elif UDP in s: 

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

1971 elif ICMP in s: 

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

1973 else: 

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

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

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

1977 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 

1978 if trace_id in ports_done: 

1979 continue 

1980 ports_done[trace_id] = None 

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

1982 if TCP in r: 

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

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

1985 elif UDP in r: 

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

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

1988 elif ICMP in r: 

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

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

1991 else: 

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

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

1994 ports[r.src] = p 

1995 else: 

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

1997 rt[trace_id] = trace 

1998 

1999 # Fill holes with unk%i nodes 

2000 unknown_label = incremental_label("unk%i") 

2001 blackholes = [] 

2002 bhip = {} 

2003 for rtk in rt: 

2004 trace = rt[rtk] 

2005 max_trace = max(trace) 

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

2007 if n not in trace: 

2008 trace[n] = next(unknown_label) 

2009 if rtk not in ports_done: 

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

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

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

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

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

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

2016 else: 

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

2018 ips[bh] = None 

2019 bhip[rtk[1]] = bh 

2020 bh = '"%s"' % bh 

2021 trace[max_trace + 1] = bh 

2022 blackholes.append(bh) 

2023 

2024 # Find AS numbers 

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

2026 if ASres is None: 

2027 ASNlist = [] 

2028 else: 

2029 ASNlist = ASres.resolve(*ASN_query_list) 

2030 

2031 ASNs = {} 

2032 ASDs = {} 

2033 for ip, asn, desc, in ASNlist: 

2034 if asn is None: 

2035 continue 

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

2037 if ip in bhip: 

2038 if ip in ports: 

2039 iplist.append(ip) 

2040 iplist.append(bhip[ip]) 

2041 else: 

2042 iplist.append(ip) 

2043 ASNs[asn] = iplist 

2044 ASDs[asn] = desc 

2045 

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

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

2048 

2049 s = "digraph trace {\n" 

2050 

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

2052 

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

2054 for asn in ASNs: 

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

2056 col = next(backcolorlist) 

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

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

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

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

2061 for ip in ASNs[asn]: 

2062 

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

2064 s += "\t}\n" 

2065 

2066 s += "#endpoints\n" 

2067 for p in ports: 

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

2069 

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

2071 for bh in blackholes: 

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

2073 

2074 if padding: 

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

2076 pad = {} 

2077 for snd, rcv in self.res: 

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

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

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

2081 pad[rcv.src] = None 

2082 for rcv in pad: 

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

2084 

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

2086 

2087 for rtk in rt: 

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

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

2090 trace = rt[rtk] 

2091 maxtrace = max(trace) 

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

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

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

2095 

2096 s += "}\n" 

2097 self.graphdef = s 

2098 

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

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

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

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

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

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

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

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

2107 prog: which graphviz program to use""" 

2108 if (self.graphdef is None or 

2109 self.graphASres != ASres or 

2110 self.graphpadding != padding): 

2111 self.make_graph(ASres, padding) 

2112 

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

2114 

2115 

2116@conf.commands.register 

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

2118 """Instant TCP traceroute 

2119 

2120 :param target: hostnames or IP addresses 

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

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

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

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

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

2126 :param filter: BPF filter applied to received packets 

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

2128 :param verbose: detailed output 

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

2130 if verbose is None: 

2131 verbose = conf.verb 

2132 if filter is None: 

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

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

2135 # set 

2136 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 

2137 if l4 is None: 

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

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

2140 else: 

2141 # this should always work 

2142 filter = "ip" 

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

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

2145 

2146 a = TracerouteResult(a.res) 

2147 if verbose: 

2148 a.show() 

2149 return a, b 

2150 

2151 

2152@conf.commands.register 

2153def traceroute_map(ips, **kargs): 

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

2155 show the different paths on a map. 

2156 

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

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

2159 """ 

2160 kargs.setdefault("verbose", 0) 

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

2162 

2163############################# 

2164# Simple TCP client stack # 

2165############################# 

2166 

2167 

2168class TCP_client(Automaton): 

2169 """ 

2170 Creates a TCP Client Automaton. 

2171 This automaton will handle TCP 3-way handshake. 

2172 

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

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

2175 >>> a.send(HTTPRequest()) 

2176 >>> a.recv() 

2177 

2178 :param ip: the ip to connect to 

2179 :param port: 

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

2181 :param sport: (optional) the TCP source port (default: random) 

2182 :param seq: (optional) initial TCP sequence number (default: random) 

2183 """ 

2184 

2185 def parse_args(self, ip, port, srcip=None, sport=None, seq=None, ack=0, **kargs): 

2186 from scapy.sessions import TCPSession 

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

2188 self.dport = port 

2189 self.sport = sport if sport is not None else random.randrange(0, 2**16) 

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

2191 sport=self.sport, dport=self.dport, 

2192 flags=0, 

2193 seq=seq if seq is not None else random.randrange(0, 2**32), 

2194 ack=ack, 

2195 ) 

2196 self.src = self.l4.src 

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

2198 self.rel_seq = None 

2199 self.rcvbuf = TCPSession() 

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

2201 self.dst, 

2202 self.sport, 

2203 self.dport) 

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

2205 

2206 def _transmit_packet(self, pkt): 

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

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

2209 

2210 def master_filter(self, pkt): 

2211 return (IP in pkt and 

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

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

2214 TCP in pkt and 

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

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

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

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

2219 

2220 @ATMT.state(initial=1) 

2221 def START(self): 

2222 pass 

2223 

2224 @ATMT.state() 

2225 def SYN_SENT(self): 

2226 pass 

2227 

2228 @ATMT.state() 

2229 def ESTABLISHED(self): 

2230 pass 

2231 

2232 @ATMT.state() 

2233 def LAST_ACK(self): 

2234 pass 

2235 

2236 @ATMT.state(final=1) 

2237 def CLOSED(self): 

2238 pass 

2239 

2240 @ATMT.state(stop=1) 

2241 def STOP(self): 

2242 pass 

2243 

2244 @ATMT.state() 

2245 def STOP_SENT_FIN_ACK(self): 

2246 pass 

2247 

2248 @ATMT.condition(START) 

2249 def connect(self): 

2250 raise self.SYN_SENT() 

2251 

2252 @ATMT.action(connect) 

2253 def send_syn(self): 

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

2255 self.send(self.l4) 

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

2257 

2258 @ATMT.receive_condition(SYN_SENT) 

2259 def synack_received(self, pkt): 

2260 if pkt[TCP].flags.SA: 

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

2262 

2263 @ATMT.action(synack_received) 

2264 def send_ack_of_synack(self, pkt): 

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

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

2267 self.send(self.l4) 

2268 

2269 @ATMT.receive_condition(ESTABLISHED) 

2270 def incoming_data_received(self, pkt): 

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

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

2273 

2274 @ATMT.action(incoming_data_received) 

2275 def receive_data(self, pkt): 

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

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

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

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

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

2281 # Answer with an Ack 

2282 self.send(self.l4) 

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

2284 pkt = self.rcvbuf.process(pkt) 

2285 if pkt: 

2286 self._transmit_packet(pkt) 

2287 

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

2289 def outgoing_data_received(self, fd): 

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

2291 

2292 @ATMT.action(outgoing_data_received) 

2293 def send_data(self, d): 

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

2295 self.send(self.l4 / d) 

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

2297 

2298 @ATMT.receive_condition(ESTABLISHED) 

2299 def reset_received(self, pkt): 

2300 if pkt[TCP].flags.R: 

2301 raise self.CLOSED() 

2302 

2303 @ATMT.receive_condition(ESTABLISHED) 

2304 def fin_received(self, pkt): 

2305 if pkt[TCP].flags.F: 

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

2307 

2308 @ATMT.action(fin_received) 

2309 def send_finack(self, pkt): 

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

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

2312 self.send(self.l4) 

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

2314 

2315 @ATMT.receive_condition(LAST_ACK) 

2316 def ack_of_fin_received(self, pkt): 

2317 if pkt[TCP].flags.A: 

2318 raise self.CLOSED() 

2319 

2320 @ATMT.condition(STOP) 

2321 def stop_requested(self): 

2322 raise self.STOP_SENT_FIN_ACK() 

2323 

2324 @ATMT.action(stop_requested) 

2325 def stop_send_finack(self): 

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

2327 self.send(self.l4) 

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

2329 

2330 @ATMT.receive_condition(STOP_SENT_FIN_ACK) 

2331 def stop_fin_received(self, pkt): 

2332 if pkt[TCP].flags.F: 

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

2334 

2335 @ATMT.action(stop_fin_received) 

2336 def stop_send_ack(self, pkt): 

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

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

2339 self.send(self.l4) 

2340 

2341 @ATMT.timeout(SYN_SENT, 1) 

2342 def syn_ack_timeout(self): 

2343 raise self.CLOSED() 

2344 

2345 @ATMT.timeout(STOP_SENT_FIN_ACK, 1) 

2346 def stop_ack_timeout(self): 

2347 raise self.CLOSED() 

2348 

2349 

2350##################### 

2351# Reporting stuff # 

2352##################### 

2353 

2354 

2355@conf.commands.register 

2356def report_ports(target, ports): 

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

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

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

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

2361 for s, r in ans: 

2362 if not r.haslayer(ICMP): 

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

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

2365 rep += "\\hline\n" 

2366 for s, r in ans: 

2367 if r.haslayer(ICMP): 

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

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

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

2371 rep += "\\hline\n" 

2372 for i in unans: 

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

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

2375 return rep 

2376 

2377 

2378@conf.commands.register 

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

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

2381 

2382lst: a list of packets 

2383funcID: a function that returns IP id values 

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

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

2386 idlst.sort() 

2387 classes = [idlst[0]] 

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

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

2390 lst.sort() 

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

2392 for id, pr in lst: 

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

2394 

2395 

2396@conf.commands.register 

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

2398 load = "XXXXYYYYYYYYYY" 

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

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

2401 s = conf.L3socket() 

2402 intr = 0 

2403 found = {} 

2404 try: 

2405 while count is None or count: 

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

2407 count -= 1 

2408 try: 

2409 if not intr: 

2410 s.send(pkt) 

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

2412 if not sin: 

2413 continue 

2414 ans = s.recv(1600) 

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

2416 continue 

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

2418 continue 

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

2420 continue 

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

2422 continue 

2423 if ans.src != target: 

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

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

2426 continue 

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

2428 if leak not in found: 

2429 found[leak] = None 

2430 linehexdump(leak, onlyasc=onlyasc) 

2431 except KeyboardInterrupt: 

2432 if intr: 

2433 raise 

2434 intr = 1 

2435 except KeyboardInterrupt: 

2436 pass 

2437 

2438 

2439@conf.commands.register 

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

2441 found = {} 

2442 try: 

2443 while count is None or count: 

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

2445 count -= 1 

2446 

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

2448 pkt /= "XXXXYYYYYYYYYYYY" 

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

2450 if not p: 

2451 continue 

2452 if conf.padding_layer in p: 

2453 leak = p[conf.padding_layer].load 

2454 if leak not in found: 

2455 found[leak] = None 

2456 linehexdump(leak, onlyasc=onlyasc) 

2457 except Exception: 

2458 pass 

2459 

2460 

2461@conf.commands.register 

2462class connect_from_ip: 

2463 """ 

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

2465 

2466 :param host: the host to connect to 

2467 :param port: the port to connect to 

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

2469 be poisonned with this IP. 

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

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

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

2473 

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

2475 

2476 from scapy.layers.http import HTTP, HTTPRequest 

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

2478 sock = SSLStreamSocket(client.sock, HTTP) 

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

2480 

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

2482 

2483 import ssl 

2484 from scapy.layers.http import HTTP, HTTPRequest 

2485 context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) 

2486 context.check_hostname = False 

2487 context.verify_mode = ssl.CERT_NONE 

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

2489 sock = context.wrap_socket(client.sock) 

2490 sock = SSLStreamSocket(client.sock, HTTP) 

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

2492 """ 

2493 

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

2495 host = str(Net(host)) 

2496 if poison: 

2497 # poison the next hop 

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

2499 if gateway == "0.0.0.0": 

2500 # on lan 

2501 gateway = host 

2502 getmacbyip(gateway) # cache real gateway before poisoning 

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

2504 # create a socket pair 

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

2506 self.sock.settimeout(timeout) 

2507 self.client = TCP_client( 

2508 host, port, 

2509 srcip=srcip, 

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

2511 debug=debug, 

2512 ) 

2513 # start the TCP_client 

2514 self.client.runbg() 

2515 

2516 def close(self): 

2517 self.client.stop() 

2518 self.client.destroy() 

2519 self.sock.close() 

2520 self._sock.close() 

2521 

2522 

2523class ICMPEcho_am(AnsweringMachine): 

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

2525 function_name = "icmpechod" 

2526 

2527 def is_request(self, req): 

2528 if req.haslayer(ICMP): 

2529 icmp_req = req.getlayer(ICMP) 

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

2531 return True 

2532 

2533 return False 

2534 

2535 def print_reply(self, req, reply): 

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

2537 

2538 def make_reply(self, req): 

2539 reply = req.copy() 

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

2541 # Force re-generation of the checksum 

2542 reply[ICMP].chksum = None 

2543 if req.haslayer(IP): 

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

2545 reply[IP].chksum = None 

2546 if req.haslayer(Ether): 

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

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

2549 req[Ether].src, 

2550 ) 

2551 return reply 

2552 

2553 

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

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

2556 

2557if conf.ipv6_enabled: 

2558 import scapy.layers.inet6