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

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

1414 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 

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", "dst")), 

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 if isinstance(dst, Gen): 

573 dst = next(iter(dst)) 

574 if conf.route is None: 

575 # unused import, only to initialize conf.route 

576 import scapy.route # noqa: F401 

577 return conf.route.route(dst) 

578 

579 def hashret(self): 

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

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

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

583 return self.payload.payload.hashret() 

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

585 return self.payload.hashret() 

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

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

588 if conf.checkIPsrc and conf.checkIPaddr: 

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

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

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

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

593 

594 def answers(self, other): 

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

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

597 return self.payload.answers(other) 

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

599 return self.answers(other.payload) 

600 if conf.ipv6_enabled \ 

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

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

603 return self.answers(other.payload) 

604 if not isinstance(other, IP): 

605 return 0 

606 if conf.checkIPaddr: 

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

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

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

610 return 0 

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

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

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

614 # ICMP error message 

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

616 

617 else: 

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

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

620 return 0 

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

622 

623 def mysummary(self): 

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

625 if self.frag: 

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

627 return s 

628 

629 def fragment(self, fragsize=1480): 

630 """Fragment IP datagrams""" 

631 return fragment(self, fragsize=fragsize) 

632 

633 

634def in4_pseudoheader(proto, u, plen): 

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

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

637 

638 :param proto: value of upper layer protocol 

639 :param u: IP layer instance 

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

641 """ 

642 u = u.copy() 

643 if u.len is not None: 

644 if u.ihl is None: 

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

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

647 else: 

648 ihl = u.ihl 

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

650 else: 

651 ln = plen 

652 

653 # Filter out IPOption_LSRR and IPOption_SSRR 

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

655 isinstance(opt, IPOption_SSRR)] 

656 len_sr_options = len(sr_options) 

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

658 # The checksum must be computed using the final 

659 # destination address 

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

661 elif len_sr_options > 1: 

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

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

664 warning(message, len_sr_options) 

665 

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

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

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

669 proto, 

670 ln) 

671 

672 

673def in4_chksum(proto, u, p): 

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

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

676 

677 :param proto: value of upper layer protocol 

678 :param u: upper layer instance 

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

680 """ 

681 if not isinstance(u, IP): 

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

683 return 0 

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

685 return checksum(psdhdr + p) 

686 

687 

688def _is_ipv6_layer(p): 

689 # type: (Packet) -> bytes 

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

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

692 

693 

694def tcp_pseudoheader(tcp): 

695 # type: (TCP) -> bytes 

696 """Pseudoheader of a TCP packet as bytes 

697 

698 Requires underlayer to be either IP or IPv6 

699 """ 

700 if isinstance(tcp.underlayer, IP): 

701 plen = len(bytes(tcp)) 

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

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

704 plen = len(bytes(tcp)) 

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

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

707 else: 

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

709 

710 

711def calc_tcp_md5_hash(tcp, key): 

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

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

714 import hashlib 

715 

716 h = hashlib.md5() # nosec 

717 tcp_bytes = bytes(tcp) 

718 h.update(tcp_pseudoheader(tcp)) 

719 h.update(tcp_bytes[:16]) 

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

721 h.update(tcp_bytes[18:]) 

722 h.update(key) 

723 

724 return h.digest() 

725 

726 

727def sign_tcp_md5(tcp, key): 

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

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

730 sig = calc_tcp_md5_hash(tcp, key) 

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

732 

733 

734class TCP(Packet): 

735 name = "TCP" 

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

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

738 IntField("seq", 0), 

739 IntField("ack", 0), 

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

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

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

743 ShortField("window", 8192), 

744 XShortField("chksum", None), 

745 ShortField("urgptr", 0), 

746 TCPOptionsField("options", "")] 

747 

748 def post_build(self, p, pay): 

749 p += pay 

750 dataofs = self.dataofs 

751 if dataofs is None: 

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

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

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

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

756 if self.chksum is None: 

757 if isinstance(self.underlayer, IP): 

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

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

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

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

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

763 else: 

764 log_runtime.info( 

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

766 ) 

767 return p 

768 

769 def hashret(self): 

770 if conf.checkIPsrc: 

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

772 else: 

773 return self.payload.hashret() 

774 

775 def answers(self, other): 

776 if not isinstance(other, TCP): 

777 return 0 

778 # RST packets don't get answers 

779 if other.flags.R: 

780 return 0 

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

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

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

784 if self.flags.S: 

785 # SYN packets without ACK are not answers 

786 if not self.flags.A: 

787 return 0 

788 # SYN+ACK packets answer SYN packets 

789 if not other.flags.S: 

790 return 0 

791 if conf.checkIPsrc: 

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

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

794 return 0 

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

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

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

798 return 0 

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

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

801 return 1 

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

803 return 0 

804 return 1 

805 

806 def mysummary(self): 

807 if isinstance(self.underlayer, IP): 

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

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

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

811 else: 

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

813 

814 

815class UDP(Packet): 

816 name = "UDP" 

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

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

819 ShortField("len", None), 

820 XShortField("chksum", None), ] 

821 

822 def post_build(self, p, pay): 

823 p += pay 

824 tmp_len = self.len 

825 if tmp_len is None: 

826 tmp_len = len(p) 

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

828 if self.chksum is None: 

829 if isinstance(self.underlayer, IP): 

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

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

832 if ck == 0: 

833 ck = 0xFFFF 

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

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

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

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

838 if ck == 0: 

839 ck = 0xFFFF 

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

841 else: 

842 log_runtime.info( 

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

844 ) 

845 return p 

846 

847 def extract_padding(self, s): 

848 tmp_len = self.len - 8 

849 return s[:tmp_len], s[tmp_len:] 

850 

851 def hashret(self): 

852 return self.payload.hashret() 

853 

854 def answers(self, other): 

855 if not isinstance(other, UDP): 

856 return 0 

857 if conf.checkIPsrc: 

858 if self.dport != other.sport: 

859 return 0 

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

861 

862 def mysummary(self): 

863 if isinstance(self.underlayer, IP): 

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

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

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

867 else: 

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

869 

870 

871# RFC 4884 ICMP extensions 

872_ICMP_classnums = { 

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

874 1: "MPLS", 

875 2: "Interface Information", 

876 3: "Interface Identification", 

877 4: "Extended Information", 

878} 

879 

880 

881class ICMPExtension_Object(Packet): 

882 name = "ICMP Extension Object" 

883 show_indent = 0 

884 fields_desc = [ 

885 ShortField("len", None), 

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

887 ByteField("classtype", 0), 

888 ] 

889 

890 def post_build(self, p, pay): 

891 if self.len is None: 

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

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

894 return p + pay 

895 

896 registered_icmp_exts = {} 

897 

898 @classmethod 

899 def register_variant(cls): 

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

901 

902 @classmethod 

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

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

905 classnum = _pkt[2] 

906 if classnum in cls.registered_icmp_exts: 

907 return cls.registered_icmp_exts[classnum] 

908 return cls 

909 

910 

911class ICMPExtension_InterfaceInformation(ICMPExtension_Object): 

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

913 

914 fields_desc = [ 

915 ShortField("len", None), 

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

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

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

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

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

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

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

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

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

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

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

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

928 ConditionalField( 

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

930 lambda pkt: pkt.has_ifname == 1, 

931 ), 

932 ConditionalField( 

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

934 lambda pkt: pkt.has_ifname == 1, 

935 ), 

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

937 ] 

938 

939 def self_build(self, **kwargs): 

940 if self.afi is None: 

941 if self.ip4 is not None: 

942 self.afi = 1 

943 elif self.ip6 is not None: 

944 self.afi = 2 

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

946 

947 

948class ICMPExtension_Header(Packet): 

949 r""" 

950 ICMP Extension per RFC4884. 

951 

952 Example:: 

953 

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

955 type="time-exceeded", 

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

957 ext=ICMPExtension_Header() / ICMPExtension_InterfaceInformation( 

958 has_ifindex=1, 

959 has_ipaddr=1, 

960 has_ifname=1, 

961 ip4="10.10.10.10", 

962 ifname="hey", 

963 ) 

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

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

966 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 

967 """ 

968 

969 name = "ICMP Extension Header (RFC4884)" 

970 show_indent = 0 

971 fields_desc = [ 

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

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

974 XShortField("chksum", None), 

975 ] 

976 

977 _min_ieo_len = len(ICMPExtension_Object()) 

978 

979 def post_build(self, p, pay): 

980 p += pay 

981 if self.chksum is None: 

982 ck = checksum(p) 

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

984 return p 

985 

986 def guess_payload_class(self, payload): 

987 if len(payload) < self._min_ieo_len: 

988 return Packet.guess_payload_class(self, payload) 

989 return ICMPExtension_Object 

990 

991 

992class _ICMPExtensionField(TrailerField): 

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

994 

995 def __init__(self): 

996 super(_ICMPExtensionField, self).__init__( 

997 PacketField( 

998 "ext", 

999 None, 

1000 ICMPExtension_Header, 

1001 ), 

1002 ) 

1003 

1004 def getfield(self, pkt, s): 

1005 # RFC4884 section 5.2 says if the ICMP packet length 

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

1007 if len(pkt.original) < 144: 

1008 return s, None 

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

1010 data = s[offset:] 

1011 # Validate checksum 

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

1013 return s, None # failed 

1014 # Dissect 

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

1016 

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

1018 if val is None: 

1019 return s 

1020 data = bytes(val) 

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

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

1023 if pad < 0: 

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

1025 return data 

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

1027 

1028 

1029class _ICMPExtensionPadField(TrailerField): 

1030 def __init__(self): 

1031 super(_ICMPExtensionPadField, self).__init__( 

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

1033 ) 

1034 

1035 def i2repr(self, pkt, s): 

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

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

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

1039 

1040 

1041def _ICMP_extpad_post_dissection(self, pkt): 

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

1043 if pkt.ext: 

1044 pad = pkt.lastlayer() 

1045 if isinstance(pad, conf.padding_layer): 

1046 pad.underlayer.remove_payload() 

1047 pkt.extpad = pad.load 

1048 

1049 

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

1051 3: "dest-unreach", 

1052 4: "source-quench", 

1053 5: "redirect", 

1054 8: "echo-request", 

1055 9: "router-advertisement", 

1056 10: "router-solicitation", 

1057 11: "time-exceeded", 

1058 12: "parameter-problem", 

1059 13: "timestamp-request", 

1060 14: "timestamp-reply", 

1061 15: "information-request", 

1062 16: "information-response", 

1063 17: "address-mask-request", 

1064 18: "address-mask-reply", 

1065 30: "traceroute", 

1066 31: "datagram-conversion-error", 

1067 32: "mobile-host-redirect", 

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

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

1070 35: "mobile-registration-request", 

1071 36: "mobile-registration-reply", 

1072 37: "domain-name-request", 

1073 38: "domain-name-reply", 

1074 39: "skip", 

1075 40: "photuris"} 

1076 

1077 

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

1079 1: "host-unreachable", 

1080 2: "protocol-unreachable", 

1081 3: "port-unreachable", 

1082 4: "fragmentation-needed", 

1083 5: "source-route-failed", 

1084 6: "network-unknown", 

1085 7: "host-unknown", 

1086 9: "network-prohibited", 

1087 10: "host-prohibited", 

1088 11: "TOS-network-unreachable", 

1089 12: "TOS-host-unreachable", 

1090 13: "communication-prohibited", 

1091 14: "host-precedence-violation", 

1092 15: "precedence-cutoff", }, 

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

1094 1: "host-redirect", 

1095 2: "TOS-network-redirect", 

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

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

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

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

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

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

1102 1: "authentication-failed", 

1103 2: "decompression-failed", 

1104 3: "decryption-failed", 

1105 4: "need-authentification", 

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

1107 

1108 

1109_icmp_answers = [ 

1110 (8, 0), 

1111 (13, 14), 

1112 (15, 16), 

1113 (17, 18), 

1114 (33, 34), 

1115 (35, 36), 

1116 (37, 38), 

1117] 

1118 

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

1120 

1121 

1122class ICMP(Packet): 

1123 name = "ICMP" 

1124 fields_desc = [ 

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

1126 MultiEnumField("code", 0, icmpcodes, 

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

1128 XShortField("chksum", None), 

1129 ConditionalField( 

1130 XShortField("id", 0), 

1131 lambda pkt: pkt.type in icmp_id_seq_types 

1132 ), 

1133 ConditionalField( 

1134 XShortField("seq", 0), 

1135 lambda pkt: pkt.type in icmp_id_seq_types 

1136 ), 

1137 ConditionalField( 

1138 # Timestamp only (RFC792) 

1139 ICMPTimeStampField("ts_ori", None), 

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

1141 ), 

1142 ConditionalField( 

1143 # Timestamp only (RFC792) 

1144 ICMPTimeStampField("ts_rx", None), 

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

1146 ), 

1147 ConditionalField( 

1148 # Timestamp only (RFC792) 

1149 ICMPTimeStampField("ts_tx", None), 

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

1151 ), 

1152 ConditionalField( 

1153 # Redirect only (RFC792) 

1154 IPField("gw", "0.0.0.0"), 

1155 lambda pkt: pkt.type == 5 

1156 ), 

1157 ConditionalField( 

1158 # Parameter problem only (RFC792) 

1159 ByteField("ptr", 0), 

1160 lambda pkt: pkt.type == 12 

1161 ), 

1162 ConditionalField( 

1163 ByteField("reserved", 0), 

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

1165 ), 

1166 ConditionalField( 

1167 ByteField("length", 0), 

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

1169 ), 

1170 ConditionalField( 

1171 IPField("addr_mask", "0.0.0.0"), 

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

1173 ), 

1174 ConditionalField( 

1175 ShortField("nexthopmtu", 0), 

1176 lambda pkt: pkt.type == 3 

1177 ), 

1178 MultipleTypeField( 

1179 [ 

1180 (ShortField("unused", 0), 

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

1182 (IntField("unused", 0), 

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

1184 13, 14, 15, 16, 17, 

1185 18]) 

1186 ], 

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

1188 ), 

1189 # RFC4884 ICMP extension 

1190 ConditionalField( 

1191 _ICMPExtensionPadField(), 

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

1193 ), 

1194 ConditionalField( 

1195 _ICMPExtensionField(), 

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

1197 ), 

1198 ] 

1199 

1200 # To handle extpad 

1201 post_dissection = _ICMP_extpad_post_dissection 

1202 

1203 def post_build(self, p, pay): 

1204 p += pay 

1205 if self.chksum is None: 

1206 ck = checksum(p) 

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

1208 return p 

1209 

1210 def hashret(self): 

1211 if self.type in icmp_id_seq_types: 

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

1213 return self.payload.hashret() 

1214 

1215 def answers(self, other): 

1216 if not isinstance(other, ICMP): 

1217 return 0 

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

1219 self.id == other.id and 

1220 self.seq == other.seq): 

1221 return 1 

1222 return 0 

1223 

1224 def guess_payload_class(self, payload): 

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

1226 return IPerror 

1227 else: 

1228 return None 

1229 

1230 def mysummary(self): 

1231 extra = "" 

1232 if self.ext: 

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

1234 if isinstance(self.underlayer, IP): 

1235 return self.underlayer.sprintf( 

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

1237 ) + extra 

1238 else: 

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

1240 

1241 

1242# IP / TCP / UDP error packets 

1243 

1244class IPerror(IP): 

1245 name = "IP in ICMP" 

1246 

1247 def answers(self, other): 

1248 if not isinstance(other, IP): 

1249 return 0 

1250 

1251 # Check if IP addresses match 

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

1253 test_IPdst = self.dst == other.dst 

1254 

1255 # Check if IP ids match 

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

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

1258 

1259 # Check if IP protocols match 

1260 test_IPproto = self.proto == other.proto 

1261 

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

1263 return 0 

1264 

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

1266 

1267 def mysummary(self): 

1268 return Packet.mysummary(self) 

1269 

1270 

1271class TCPerror(TCP): 

1272 name = "TCP in ICMP" 

1273 fields_desc = ( 

1274 TCP.fields_desc[:2] + 

1275 # MayEnd after the 8 first octets. 

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

1277 TCP.fields_desc[3:] 

1278 ) 

1279 

1280 def answers(self, other): 

1281 if not isinstance(other, TCP): 

1282 return 0 

1283 if conf.checkIPsrc: 

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

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

1286 return 0 

1287 if conf.check_TCPerror_seqack: 

1288 if self.seq is not None: 

1289 if self.seq != other.seq: 

1290 return 0 

1291 if self.ack is not None: 

1292 if self.ack != other.ack: 

1293 return 0 

1294 return 1 

1295 

1296 def mysummary(self): 

1297 return Packet.mysummary(self) 

1298 

1299 

1300class UDPerror(UDP): 

1301 name = "UDP in ICMP" 

1302 

1303 def answers(self, other): 

1304 if not isinstance(other, UDP): 

1305 return 0 

1306 if conf.checkIPsrc: 

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

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

1309 return 0 

1310 return 1 

1311 

1312 def mysummary(self): 

1313 return Packet.mysummary(self) 

1314 

1315 

1316class ICMPerror(ICMP): 

1317 name = "ICMP in ICMP" 

1318 

1319 def answers(self, other): 

1320 if not isinstance(other, ICMP): 

1321 return 0 

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

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

1324 return 0 

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

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

1327 self.seq == other.seq): 

1328 return 1 

1329 else: 

1330 return 0 

1331 else: 

1332 return 1 

1333 

1334 def mysummary(self): 

1335 return Packet.mysummary(self) 

1336 

1337 

1338bind_layers(Ether, IP, type=2048) 

1339bind_layers(CookedLinux, IP, proto=2048) 

1340bind_layers(GRE, IP, proto=2048) 

1341bind_layers(SNAP, IP, code=2048) 

1342bind_bottom_up(Loopback, IP, type=0) 

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

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

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

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

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

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

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

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

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

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

1353bind_layers(UDP, GRE, dport=4754) 

1354 

1355conf.l2types.register(DLT_RAW, IP) 

1356conf.l2types.register_num2layer(DLT_RAW_ALT, IP) 

1357conf.l2types.register(DLT_IPV4, IP) 

1358 

1359conf.l3types.register(ETH_P_IP, IP) 

1360conf.l3types.register_num2layer(ETH_P_ALL, IP) 

1361 

1362 

1363def inet_register_l3(l2, l3): 

1364 """ 

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

1366 """ 

1367 return getmacbyip(l3.dst) 

1368 

1369 

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

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

1372 

1373 

1374################### 

1375# Fragmentation # 

1376################### 

1377 

1378@conf.commands.register 

1379def fragment(pkt, fragsize=1480): 

1380 """Fragment a big IP datagram""" 

1381 if fragsize < 8: 

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

1383 fragsize = max(fragsize, 8) 

1384 lastfragsz = fragsize 

1385 fragsize -= fragsize % 8 

1386 lst = [] 

1387 for p in pkt: 

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

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

1390 for i in range(nb): 

1391 q = p.copy() 

1392 del q[IP].payload 

1393 del q[IP].chksum 

1394 del q[IP].len 

1395 if i != nb - 1: 

1396 q[IP].flags |= 1 

1397 fragend = (i + 1) * fragsize 

1398 else: 

1399 fragend = i * fragsize + lastfragsz 

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

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

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

1403 q.add_payload(r) 

1404 lst.append(q) 

1405 return lst 

1406 

1407 

1408@conf.commands.register 

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

1410 """Build overlapping fragments to bypass NIPS 

1411 

1412p: the original packet 

1413overlap: the overlapping data 

1414fragsize: the fragment size of the packet 

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

1416 

1417 if overlap_fragsize is None: 

1418 overlap_fragsize = fragsize 

1419 q = p.copy() 

1420 del q[IP].payload 

1421 q[IP].add_payload(overlap) 

1422 

1423 qfrag = fragment(q, overlap_fragsize) 

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

1425 return qfrag + fragment(p, fragsize) 

1426 

1427 

1428class BadFragments(ValueError): 

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

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

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

1432 

1433 

1434def _defrag_iter_and_check_offsets(frags): 

1435 """ 

1436 Internal generator used in _defrag_ip_pkt 

1437 """ 

1438 offset = 0 

1439 for pkt, o, length in frags: 

1440 if offset != o: 

1441 if offset > o: 

1442 op = ">" 

1443 else: 

1444 op = "<" 

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

1446 raise BadFragments 

1447 offset += length 

1448 yield bytes(pkt[IP].payload) 

1449 

1450 

1451def _defrag_ip_pkt(pkt, frags): 

1452 """ 

1453 Defragment a single IP packet. 

1454 

1455 :param pkt: the new pkt 

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

1457 :return: a tuple (fragmented, defragmented_value) 

1458 """ 

1459 ip = pkt[IP] 

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

1461 # fragmented ! 

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

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

1464 fraglen = len(ip.payload) 

1465 else: 

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

1467 # (pkt, frag offset, frag len) 

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

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

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

1471 try: 

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

1473 except ValueError: 

1474 # bad fragment 

1475 badfrags = frags[uid] 

1476 del frags[uid] 

1477 raise BadFragments(frags=badfrags) 

1478 # re-build initial packet without fragmentation 

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

1480 pay_class = p[IP].payload.__class__ 

1481 p[IP].flags.MF = False 

1482 p[IP].remove_payload() 

1483 p[IP].len = None 

1484 p[IP].chksum = None 

1485 # append defragmented payload 

1486 p /= pay_class(data) 

1487 # cleanup 

1488 del frags[uid] 

1489 return True, p 

1490 return True, None 

1491 return False, pkt 

1492 

1493 

1494def _defrag_logic(plist, complete=False): 

1495 """ 

1496 Internal function used to defragment a list of packets. 

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

1498 """ 

1499 frags = defaultdict(list) 

1500 final = [] 

1501 notfrag = [] 

1502 badfrag = [] 

1503 # Defrag 

1504 for i, pkt in enumerate(plist): 

1505 if IP not in pkt: 

1506 # no IP layer 

1507 if complete: 

1508 final.append(pkt) 

1509 continue 

1510 try: 

1511 fragmented, defragmented_value = _defrag_ip_pkt( 

1512 pkt, 

1513 frags, 

1514 ) 

1515 except BadFragments as ex: 

1516 if complete: 

1517 final.extend(ex.frags) 

1518 else: 

1519 badfrag.extend(ex.frags) 

1520 continue 

1521 if complete and defragmented_value: 

1522 final.append(defragmented_value) 

1523 elif defragmented_value: 

1524 if fragmented: 

1525 final.append(defragmented_value) 

1526 else: 

1527 notfrag.append(defragmented_value) 

1528 # Return 

1529 if complete: 

1530 if hasattr(plist, "listname"): 

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

1532 else: 

1533 name = "Defragmented" 

1534 return PacketList(final, name=name) 

1535 else: 

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

1537 

1538 

1539@conf.commands.register 

1540def defrag(plist): 

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

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

1543 return _defrag_logic(plist, complete=False) 

1544 

1545 

1546@conf.commands.register 

1547def defragment(plist): 

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

1549 return _defrag_logic(plist, complete=True) 

1550 

1551 

1552# Add timeskew_graph() method to PacketList 

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

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

1555 # Defer imports of matplotlib until its needed 

1556 # because it has a heavy dep chain 

1557 from scapy.libs.matplot import ( 

1558 plt, 

1559 MATPLOTLIB_INLINED, 

1560 MATPLOTLIB_DEFAULT_PLOT_KARGS 

1561 ) 

1562 

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

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

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

1566 

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

1568 c = [] 

1569 tsf = ICMPTimeStampField("", None) 

1570 for p in b: 

1571 opts = p.getlayer(TCP).options 

1572 for o in opts: 

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

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

1575 

1576 # Stop if the list is empty 

1577 if not c: 

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

1579 return [] 

1580 

1581 # Prepare the data that will be plotted 

1582 first_creation_time = c[0][0] 

1583 first_replied_timestamp = c[0][1] 

1584 

1585 def _wrap_data(ts_tuple, wrap_seconds=2000): 

1586 """Wrap the list of tuples.""" 

1587 

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

1589 X = ct % wrap_seconds 

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

1591 

1592 return X, Y 

1593 

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

1595 

1596 # Mimic the default gnuplot output 

1597 if kargs == {}: 

1598 kargs = MATPLOTLIB_DEFAULT_PLOT_KARGS 

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

1600 

1601 # Call show() if matplotlib is not inlined 

1602 if not MATPLOTLIB_INLINED: 

1603 plt.show() 

1604 

1605 return lines 

1606 

1607 

1608_PacketList.timeskew_graph = _packetlist_timeskew_graph 

1609 

1610 

1611# Create a new packet list 

1612class TracerouteResult(SndRcvList): 

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

1614 "nloc"] 

1615 

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

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

1618 self.graphdef = None 

1619 self.graphASres = None 

1620 self.padding = 0 

1621 self.hloc = None 

1622 self.nloc = None 

1623 

1624 def show(self): 

1625 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 

1626 s.ttl, 

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

1628 

1629 def get_trace(self): 

1630 trace = {} 

1631 for s, r in self.res: 

1632 if IP not in s: 

1633 continue 

1634 d = s[IP].dst 

1635 if d not in trace: 

1636 trace[d] = {} 

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

1638 for k in trace.values(): 

1639 try: 

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

1641 except ValueError: 

1642 continue 

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

1644 if li > m: 

1645 del k[li] 

1646 return trace 

1647 

1648 def trace3D(self, join=True): 

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

1650 right button: rotate the scene 

1651 middle button: zoom 

1652 shift-left button: move the scene 

1653 left button on a ball: toggle IP displaying 

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

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

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

1657 import multiprocessing 

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

1659 p.start() 

1660 if join: 

1661 p.join() 

1662 

1663 def trace3D_notebook(self): 

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

1665 trace = self.get_trace() 

1666 import vpython 

1667 

1668 class IPsphere(vpython.sphere): 

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

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

1671 self.ip = ip 

1672 self.label = None 

1673 self.setlabel(self.ip) 

1674 self.last_clicked = None 

1675 self.full = False 

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

1677 

1678 def fullinfos(self): 

1679 self.full = True 

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

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

1682 if len(a) == 0: 

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

1684 else: 

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

1686 for s, r in a: 

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

1688 self.setlabel(txt, visible=1) 

1689 

1690 def unfull(self): 

1691 self.color = self.savcolor 

1692 self.full = False 

1693 self.setlabel(self.ip) 

1694 

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

1696 if self.label is not None: 

1697 if visible is None: 

1698 visible = self.label.visible 

1699 self.label.visible = 0 

1700 elif visible is None: 

1701 visible = 0 

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

1703 

1704 def check_double_click(self): 

1705 try: 

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

1707 return False 

1708 if self.last_clicked is not None: 

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

1710 return False 

1711 finally: 

1712 self.last_clicked = time.time() 

1713 

1714 def action(self): 

1715 self.label.visible ^= 1 

1716 if self.full: 

1717 self.unfull() 

1718 

1719 vpython.scene = vpython.canvas() 

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

1721 vpython.scene.append_to_caption( 

1722 re.sub( 

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

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

1725 re.sub( 

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

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

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

1729%Click% to toggle information about a node. 

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

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

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

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

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

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

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

1737 ) 

1738 ) 

1739 ) 

1740 vpython.scene.exit = True 

1741 rings = {} 

1742 tr3d = {} 

1743 for i in trace: 

1744 tr = trace[i] 

1745 tr3d[i] = [] 

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

1747 if t not in rings: 

1748 rings[t] = [] 

1749 if t in tr: 

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

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

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

1753 else: 

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

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

1756 

1757 for t in rings: 

1758 r = rings[t] 

1759 tmp_len = len(r) 

1760 for i in range(tmp_len): 

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

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

1763 elif r[i][1]: 

1764 col = vpython.color.green 

1765 else: 

1766 col = vpython.color.blue 

1767 

1768 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 

1769 ip=r[i][0], 

1770 color=col) 

1771 for trlst in tr3d.values(): 

1772 if t <= len(trlst): 

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

1774 trlst[t - 1] = s 

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

1776 for trlst in tr3d.values(): 

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

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

1779 for ip in trlst: 

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

1781 start = ip.pos 

1782 

1783 vpython.rate(50) 

1784 

1785 # Keys handling 

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

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

1788 # def keyboard_press(ev): 

1789 # k = ev.key 

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

1791 # pass # TODO: close 

1792 # 

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

1794 

1795 # Mouse handling 

1796 def mouse_click(ev): 

1797 if ev.press == "left": 

1798 o = vpython.scene.mouse.pick 

1799 if o and isinstance(o, IPsphere): 

1800 if o.check_double_click(): 

1801 if o.ip == "unk": 

1802 return 

1803 o.fullinfos() 

1804 else: 

1805 o.action() 

1806 

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

1808 

1809 def world_trace(self): 

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

1811 

1812 # Check that the geoip2 module can be imported 

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

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

1815 

1816 try: 

1817 # GeoIP2 modules need to be imported as below 

1818 import geoip2.database 

1819 import geoip2.errors 

1820 except ImportError: 

1821 log_runtime.error( 

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

1823 ) 

1824 return [] 

1825 # Check availability of database 

1826 if not conf.geoip_city: 

1827 log_runtime.error( 

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

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

1830 " then set its path to conf.geoip_city" 

1831 ) 

1832 return [] 

1833 # Check availability of plotting devices 

1834 try: 

1835 import cartopy.crs as ccrs 

1836 except ImportError: 

1837 log_runtime.error( 

1838 "Cannot import cartopy.\n" 

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

1840 ) 

1841 return [] 

1842 if not MATPLOTLIB: 

1843 log_runtime.error( 

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

1845 ) 

1846 return [] 

1847 

1848 # Open & read the GeoListIP2 database 

1849 try: 

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

1851 except Exception: 

1852 log_runtime.error( 

1853 "Cannot open geoip2 database at %s", 

1854 conf.geoip_city 

1855 ) 

1856 return [] 

1857 

1858 # Regroup results per trace 

1859 ips = {} 

1860 rt = {} 

1861 ports_done = {} 

1862 for s, r in self.res: 

1863 ips[r.src] = None 

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

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

1866 elif s.haslayer(ICMP): 

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

1868 else: 

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

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

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

1872 if trace_id in ports_done: 

1873 continue 

1874 ports_done[trace_id] = None 

1875 trace[s.ttl] = r.src 

1876 rt[trace_id] = trace 

1877 

1878 # Get the addresses locations 

1879 trt = {} 

1880 for trace_id in rt: 

1881 trace = rt[trace_id] 

1882 loctrace = [] 

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

1884 ip = trace.get(i, None) 

1885 if ip is None: 

1886 continue 

1887 # Fetch database 

1888 try: 

1889 sresult = db.city(ip) 

1890 except geoip2.errors.AddressNotFoundError: 

1891 continue 

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

1893 if loctrace: 

1894 trt[trace_id] = loctrace 

1895 

1896 # Load the map renderer 

1897 plt.figure(num='Scapy') 

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

1899 # Draw countries 

1900 ax.coastlines() 

1901 ax.stock_img() 

1902 # Set normal size 

1903 ax.set_global() 

1904 # Add title 

1905 plt.title("Scapy traceroute results") 

1906 

1907 from matplotlib.collections import LineCollection 

1908 from matplotlib import colors as mcolors 

1909 colors_cycle = iter(mcolors.BASE_COLORS) 

1910 lines = [] 

1911 

1912 # Split traceroute measurement 

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

1914 # Get next color 

1915 color = next(colors_cycle) 

1916 # Gather mesurments data 

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

1918 # Create line collection 

1919 line_col = LineCollection(data_lines, linewidths=2, 

1920 label=key[1], 

1921 color=color) 

1922 lines.append(line_col) 

1923 ax.add_collection(line_col) 

1924 # Create map points 

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

1926 

1927 # Generate legend 

1928 ax.legend() 

1929 

1930 # Call show() if matplotlib is not inlined 

1931 if not MATPLOTLIB_INLINED: 

1932 plt.show() 

1933 

1934 # Clean 

1935 ax.remove() 

1936 

1937 # Return the drawn lines 

1938 return lines 

1939 

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

1941 self.graphASres = ASres 

1942 self.graphpadding = padding 

1943 ips = {} 

1944 rt = {} 

1945 ports = {} 

1946 ports_done = {} 

1947 for s, r in self.res: 

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

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

1950 ips[r.src] = None 

1951 if TCP in s: 

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

1953 elif UDP in s: 

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

1955 elif ICMP in s: 

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

1957 else: 

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

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

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

1961 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 

1962 if trace_id in ports_done: 

1963 continue 

1964 ports_done[trace_id] = None 

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

1966 if TCP in r: 

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

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

1969 elif UDP in r: 

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

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

1972 elif ICMP in r: 

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

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

1975 else: 

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

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

1978 ports[r.src] = p 

1979 else: 

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

1981 rt[trace_id] = trace 

1982 

1983 # Fill holes with unk%i nodes 

1984 unknown_label = incremental_label("unk%i") 

1985 blackholes = [] 

1986 bhip = {} 

1987 for rtk in rt: 

1988 trace = rt[rtk] 

1989 max_trace = max(trace) 

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

1991 if n not in trace: 

1992 trace[n] = next(unknown_label) 

1993 if rtk not in ports_done: 

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

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

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

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

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

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

2000 else: 

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

2002 ips[bh] = None 

2003 bhip[rtk[1]] = bh 

2004 bh = '"%s"' % bh 

2005 trace[max_trace + 1] = bh 

2006 blackholes.append(bh) 

2007 

2008 # Find AS numbers 

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

2010 if ASres is None: 

2011 ASNlist = [] 

2012 else: 

2013 ASNlist = ASres.resolve(*ASN_query_list) 

2014 

2015 ASNs = {} 

2016 ASDs = {} 

2017 for ip, asn, desc, in ASNlist: 

2018 if asn is None: 

2019 continue 

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

2021 if ip in bhip: 

2022 if ip in ports: 

2023 iplist.append(ip) 

2024 iplist.append(bhip[ip]) 

2025 else: 

2026 iplist.append(ip) 

2027 ASNs[asn] = iplist 

2028 ASDs[asn] = desc 

2029 

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

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

2032 

2033 s = "digraph trace {\n" 

2034 

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

2036 

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

2038 for asn in ASNs: 

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

2040 col = next(backcolorlist) 

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

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

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

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

2045 for ip in ASNs[asn]: 

2046 

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

2048 s += "\t}\n" 

2049 

2050 s += "#endpoints\n" 

2051 for p in ports: 

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

2053 

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

2055 for bh in blackholes: 

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

2057 

2058 if padding: 

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

2060 pad = {} 

2061 for snd, rcv in self.res: 

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

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

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

2065 pad[rcv.src] = None 

2066 for rcv in pad: 

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

2068 

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

2070 

2071 for rtk in rt: 

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

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

2074 trace = rt[rtk] 

2075 maxtrace = max(trace) 

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

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

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

2079 

2080 s += "}\n" 

2081 self.graphdef = s 

2082 

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

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

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

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

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

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

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

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

2091 prog: which graphviz program to use""" 

2092 if (self.graphdef is None or 

2093 self.graphASres != ASres or 

2094 self.graphpadding != padding): 

2095 self.make_graph(ASres, padding) 

2096 

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

2098 

2099 

2100@conf.commands.register 

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

2102 """Instant TCP traceroute 

2103 

2104 :param target: hostnames or IP addresses 

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

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

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

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

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

2110 :param filter: BPF filter applied to received packets 

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

2112 :param verbose: detailed output 

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

2114 if verbose is None: 

2115 verbose = conf.verb 

2116 if filter is None: 

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

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

2119 # set 

2120 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 

2121 if l4 is None: 

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

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

2124 else: 

2125 # this should always work 

2126 filter = "ip" 

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

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

2129 

2130 a = TracerouteResult(a.res) 

2131 if verbose: 

2132 a.show() 

2133 return a, b 

2134 

2135 

2136@conf.commands.register 

2137def traceroute_map(ips, **kargs): 

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

2139 show the different paths on a map. 

2140 

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

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

2143 """ 

2144 kargs.setdefault("verbose", 0) 

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

2146 

2147############################# 

2148# Simple TCP client stack # 

2149############################# 

2150 

2151 

2152class TCP_client(Automaton): 

2153 """ 

2154 Creates a TCP Client Automaton. 

2155 This automaton will handle TCP 3-way handshake. 

2156 

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

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

2159 >>> a.send(HTTPRequest()) 

2160 >>> a.recv() 

2161 

2162 :param ip: the ip to connect to 

2163 :param port: 

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

2165 """ 

2166 

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

2168 from scapy.sessions import TCPSession 

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

2170 self.dport = port 

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

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

2173 sport=self.sport, dport=self.dport, 

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

2175 ) 

2176 self.src = self.l4.src 

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

2178 self.rel_seq = None 

2179 self.rcvbuf = TCPSession() 

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

2181 self.dst, 

2182 self.sport, 

2183 self.dport) 

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

2185 

2186 def _transmit_packet(self, pkt): 

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

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

2189 

2190 def master_filter(self, pkt): 

2191 return (IP in pkt and 

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

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

2194 TCP in pkt and 

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

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

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

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

2199 

2200 @ATMT.state(initial=1) 

2201 def START(self): 

2202 pass 

2203 

2204 @ATMT.state() 

2205 def SYN_SENT(self): 

2206 pass 

2207 

2208 @ATMT.state() 

2209 def ESTABLISHED(self): 

2210 pass 

2211 

2212 @ATMT.state() 

2213 def LAST_ACK(self): 

2214 pass 

2215 

2216 @ATMT.state(final=1) 

2217 def CLOSED(self): 

2218 pass 

2219 

2220 @ATMT.state(stop=1) 

2221 def STOP(self): 

2222 pass 

2223 

2224 @ATMT.state() 

2225 def STOP_SENT_FIN_ACK(self): 

2226 pass 

2227 

2228 @ATMT.condition(START) 

2229 def connect(self): 

2230 raise self.SYN_SENT() 

2231 

2232 @ATMT.action(connect) 

2233 def send_syn(self): 

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

2235 self.send(self.l4) 

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

2237 

2238 @ATMT.receive_condition(SYN_SENT) 

2239 def synack_received(self, pkt): 

2240 if pkt[TCP].flags.SA: 

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

2242 

2243 @ATMT.action(synack_received) 

2244 def send_ack_of_synack(self, pkt): 

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

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

2247 self.send(self.l4) 

2248 

2249 @ATMT.receive_condition(ESTABLISHED) 

2250 def incoming_data_received(self, pkt): 

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

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

2253 

2254 @ATMT.action(incoming_data_received) 

2255 def receive_data(self, pkt): 

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

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

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

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

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

2261 # Answer with an Ack 

2262 self.send(self.l4) 

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

2264 pkt = self.rcvbuf.process(pkt) 

2265 if pkt: 

2266 self._transmit_packet(pkt) 

2267 

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

2269 def outgoing_data_received(self, fd): 

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

2271 

2272 @ATMT.action(outgoing_data_received) 

2273 def send_data(self, d): 

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

2275 self.send(self.l4 / d) 

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

2277 

2278 @ATMT.receive_condition(ESTABLISHED) 

2279 def reset_received(self, pkt): 

2280 if pkt[TCP].flags.R: 

2281 raise self.CLOSED() 

2282 

2283 @ATMT.receive_condition(ESTABLISHED) 

2284 def fin_received(self, pkt): 

2285 if pkt[TCP].flags.F: 

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

2287 

2288 @ATMT.action(fin_received) 

2289 def send_finack(self, pkt): 

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

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

2292 self.send(self.l4) 

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

2294 

2295 @ATMT.receive_condition(LAST_ACK) 

2296 def ack_of_fin_received(self, pkt): 

2297 if pkt[TCP].flags.A: 

2298 raise self.CLOSED() 

2299 

2300 @ATMT.condition(STOP) 

2301 def stop_requested(self): 

2302 raise self.STOP_SENT_FIN_ACK() 

2303 

2304 @ATMT.action(stop_requested) 

2305 def stop_send_finack(self): 

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

2307 self.send(self.l4) 

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

2309 

2310 @ATMT.receive_condition(STOP_SENT_FIN_ACK) 

2311 def stop_fin_received(self, pkt): 

2312 if pkt[TCP].flags.F: 

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

2314 

2315 @ATMT.action(stop_fin_received) 

2316 def stop_send_ack(self, pkt): 

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

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

2319 self.send(self.l4) 

2320 

2321 @ATMT.timeout(SYN_SENT, 1) 

2322 def syn_ack_timeout(self): 

2323 raise self.CLOSED() 

2324 

2325 @ATMT.timeout(STOP_SENT_FIN_ACK, 1) 

2326 def stop_ack_timeout(self): 

2327 raise self.CLOSED() 

2328 

2329 

2330##################### 

2331# Reporting stuff # 

2332##################### 

2333 

2334 

2335@conf.commands.register 

2336def report_ports(target, ports): 

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

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

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

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

2341 for s, r in ans: 

2342 if not r.haslayer(ICMP): 

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

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

2345 rep += "\\hline\n" 

2346 for s, r in ans: 

2347 if r.haslayer(ICMP): 

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

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

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

2351 rep += "\\hline\n" 

2352 for i in unans: 

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

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

2355 return rep 

2356 

2357 

2358@conf.commands.register 

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

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

2361 

2362lst: a list of packets 

2363funcID: a function that returns IP id values 

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

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

2366 idlst.sort() 

2367 classes = [idlst[0]] 

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

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

2370 lst.sort() 

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

2372 for id, pr in lst: 

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

2374 

2375 

2376@conf.commands.register 

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

2378 load = "XXXXYYYYYYYYYY" 

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

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

2381 s = conf.L3socket() 

2382 intr = 0 

2383 found = {} 

2384 try: 

2385 while count is None or count: 

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

2387 count -= 1 

2388 try: 

2389 if not intr: 

2390 s.send(pkt) 

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

2392 if not sin: 

2393 continue 

2394 ans = s.recv(1600) 

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

2396 continue 

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

2398 continue 

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

2400 continue 

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

2402 continue 

2403 if ans.src != target: 

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

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

2406 continue 

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

2408 if leak not in found: 

2409 found[leak] = None 

2410 linehexdump(leak, onlyasc=onlyasc) 

2411 except KeyboardInterrupt: 

2412 if intr: 

2413 raise 

2414 intr = 1 

2415 except KeyboardInterrupt: 

2416 pass 

2417 

2418 

2419@conf.commands.register 

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

2421 found = {} 

2422 try: 

2423 while count is None or count: 

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

2425 count -= 1 

2426 

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

2428 pkt /= "XXXXYYYYYYYYYYYY" 

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

2430 if not p: 

2431 continue 

2432 if conf.padding_layer in p: 

2433 leak = p[conf.padding_layer].load 

2434 if leak not in found: 

2435 found[leak] = None 

2436 linehexdump(leak, onlyasc=onlyasc) 

2437 except Exception: 

2438 pass 

2439 

2440 

2441@conf.commands.register 

2442class connect_from_ip: 

2443 """ 

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

2445 

2446 :param host: the host to connect to 

2447 :param port: the port to connect to 

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

2449 be poisonned with this IP. 

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

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

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

2453 

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

2455 

2456 from scapy.layers.http import HTTP, HTTPRequest 

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

2458 sock = SSLStreamSocket(client.sock, HTTP) 

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

2460 

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

2462 

2463 import ssl 

2464 from scapy.layers.http import HTTP, HTTPRequest 

2465 context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) 

2466 context.check_hostname = False 

2467 context.verify_mode = ssl.CERT_NONE 

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

2469 sock = context.wrap_socket(client.sock) 

2470 sock = SSLStreamSocket(client.sock, HTTP) 

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

2472 """ 

2473 

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

2475 host = str(Net(host)) 

2476 if poison: 

2477 # poison the next hop 

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

2479 if gateway == "0.0.0.0": 

2480 # on lan 

2481 gateway = host 

2482 getmacbyip(gateway) # cache real gateway before poisoning 

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

2484 # create a socket pair 

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

2486 self.sock.settimeout(timeout) 

2487 self.client = TCP_client( 

2488 host, port, 

2489 srcip=srcip, 

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

2491 debug=debug, 

2492 ) 

2493 # start the TCP_client 

2494 self.client.runbg() 

2495 

2496 def close(self): 

2497 self.client.stop() 

2498 self.client.destroy() 

2499 self.sock.close() 

2500 self._sock.close() 

2501 

2502 

2503class ICMPEcho_am(AnsweringMachine): 

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

2505 function_name = "icmpechod" 

2506 

2507 def is_request(self, req): 

2508 if req.haslayer(ICMP): 

2509 icmp_req = req.getlayer(ICMP) 

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

2511 return True 

2512 

2513 return False 

2514 

2515 def print_reply(self, req, reply): 

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

2517 

2518 def make_reply(self, req): 

2519 reply = req.copy() 

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

2521 # Force re-generation of the checksum 

2522 reply[ICMP].chksum = None 

2523 if req.haslayer(IP): 

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

2525 reply[IP].chksum = None 

2526 if req.haslayer(Ether): 

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

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

2529 req[Ether].src, 

2530 ) 

2531 return reply 

2532 

2533 

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

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

2536 

2537if conf.ipv6_enabled: 

2538 import scapy.layers.inet6