Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/scapy/sendrecv.py: 14%
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
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
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>
6"""
7Functions to send and receive packets.
8"""
10import itertools
11from threading import Thread, Event
12import os
13import re
14import socket
15import subprocess
16import time
18from scapy.compat import plain_str
19from scapy.data import ETH_P_ALL
20from scapy.config import conf
21from scapy.error import warning
22from scapy.interfaces import (
23 network_name,
24 resolve_iface,
25 NetworkInterface,
26)
27from scapy.packet import Packet
28from scapy.pton_ntop import inet_pton
29from scapy.utils import get_temp_file, tcpdump, wrpcap, \
30 ContextManagerSubprocess, PcapReader, EDecimal
31from scapy.plist import (
32 PacketList,
33 QueryAnswer,
34 SndRcvList,
35)
36from scapy.error import log_runtime, log_interactive, Scapy_Exception
37from scapy.base_classes import Gen, SetGen
38from scapy.sessions import DefaultSession
39from scapy.supersocket import SuperSocket, IterSocket
41# Typing imports
42from typing import (
43 Any,
44 Callable,
45 Dict,
46 Iterator,
47 List,
48 Optional,
49 Tuple,
50 Type,
51 Union,
52 cast
53)
54from scapy.interfaces import _GlobInterfaceType
55from scapy.plist import _PacketIterable
57if conf.route is None:
58 # unused import, only to initialize conf.route and conf.iface*
59 import scapy.route # noqa: F401
61#################
62# Debug class #
63#################
66class debug:
67 recv = PacketList([], "Received")
68 sent = PacketList([], "Sent")
69 match = SndRcvList([], "Matched")
70 crashed_on = None # type: Optional[Tuple[Type[Packet], bytes]]
73####################
74# Send / Receive #
75####################
77_DOC_SNDRCV_PARAMS = """
78 :param pks: SuperSocket instance to send/receive packets
79 :param pkt: the packet to send
80 :param timeout: how much time to wait after the last packet has been sent
81 :param inter: delay between two packets during sending
82 :param verbose: set verbosity level
83 :param chainCC: if True, KeyboardInterrupts will be forwarded
84 :param retry: if positive, how many times to resend unanswered packets
85 if negative, how many times to retry when no more packets
86 are answered
87 :param multi: whether to accept multiple answers for the same stimulus
88 :param rcv_pks: if set, will be used instead of pks to receive packets.
89 packets will still be sent through pks
90 :param prebuild: pre-build the packets before starting to send them.
91 Automatically enabled when a generator is passed as the packet
92 :param _flood:
93 :param threaded: if True, packets are sent in a thread and received in another.
94 defaults to False.
95 :param session: a flow decoder used to handle stream of packets
96 :param chainEX: if True, exceptions during send will be forwarded
97 :param stop_filter: Python function applied to each packet to determine if
98 we have to stop the capture after this packet.
99 """
102_GlobSessionType = Union[Type[DefaultSession], DefaultSession]
105class SndRcvHandler(object):
106 """
107 Util to send/receive packets, used by sr*().
108 Do not use directly.
110 This matches the requests and answers.
112 Notes::
113 - threaded: if you're planning to send/receive many packets, it's likely
114 a good idea to use threaded mode.
115 - DEVS: store the outgoing timestamp right BEFORE sending the packet
116 to avoid races that could result in negative latency. We aren't Stadia
117 """
118 def __init__(self,
119 pks, # type: SuperSocket
120 pkt, # type: _PacketIterable
121 timeout=None, # type: Optional[int]
122 inter=0, # type: int
123 verbose=None, # type: Optional[int]
124 chainCC=False, # type: bool
125 retry=0, # type: int
126 multi=False, # type: bool
127 rcv_pks=None, # type: Optional[SuperSocket]
128 prebuild=False, # type: bool
129 _flood=None, # type: Optional[_FloodGenerator]
130 threaded=False, # type: bool
131 session=None, # type: Optional[_GlobSessionType]
132 chainEX=False, # type: bool
133 stop_filter=None # type: Optional[Callable[[Packet], bool]]
134 ):
135 # type: (...) -> None
136 # Instantiate all arguments
137 if verbose is None:
138 verbose = conf.verb
139 if conf.debug_match:
140 debug.recv = PacketList([], "Received")
141 debug.sent = PacketList([], "Sent")
142 debug.match = SndRcvList([], "Matched")
143 self.nbrecv = 0
144 self.ans = [] # type: List[QueryAnswer]
145 self.pks = pks
146 self.rcv_pks = rcv_pks or pks
147 self.inter = inter
148 self.verbose = verbose
149 self.chainCC = chainCC
150 self.multi = multi
151 self.timeout = timeout
152 self.session = session
153 self.chainEX = chainEX
154 self.stop_filter = stop_filter
155 self._send_done = False
156 self.notans = 0
157 self.noans = 0
158 self._flood = _flood
159 self.threaded = threaded
160 self.breakout = False
161 # Instantiate packet holders
162 if prebuild and not self._flood:
163 self.tobesent = list(pkt) # type: _PacketIterable
164 else:
165 self.tobesent = pkt
167 if retry < 0:
168 autostop = retry = -retry
169 else:
170 autostop = 0
172 if timeout is not None and timeout < 0:
173 self.timeout = None
175 while retry >= 0:
176 self.hsent = {} # type: Dict[bytes, List[Packet]]
178 if threaded or self._flood:
179 # Send packets in thread.
180 snd_thread = Thread(
181 target=self._sndrcv_snd
182 )
183 snd_thread.daemon = True
185 # Start routine with callback
186 interrupted = None
187 try:
188 self._sndrcv_rcv(snd_thread.start)
189 except KeyboardInterrupt as ex:
190 interrupted = ex
192 self.breakout = True
194 # Ended. Let's close gracefully
195 if self._flood:
196 # Flood: stop send thread
197 self._flood.stop()
198 snd_thread.join()
200 if interrupted and self.chainCC:
201 raise interrupted
202 else:
203 # Send packets, then receive.
204 try:
205 self._sndrcv_rcv(self._sndrcv_snd)
206 except KeyboardInterrupt:
207 if self.chainCC:
208 raise
210 if multi:
211 remain = [
212 p for p in itertools.chain(*self.hsent.values())
213 if not hasattr(p, '_answered')
214 ]
215 else:
216 remain = list(itertools.chain(*self.hsent.values()))
218 if autostop and len(remain) > 0 and \
219 len(remain) != len(self.tobesent):
220 retry = autostop
222 self.tobesent = remain
223 if len(self.tobesent) == 0:
224 break
225 retry -= 1
227 if conf.debug_match:
228 debug.sent = PacketList(remain[:], "Sent")
229 debug.match = SndRcvList(self.ans[:])
231 # Clean the ans list to delete the field _answered
232 if multi:
233 for snd, _ in self.ans:
234 if hasattr(snd, '_answered'):
235 del snd._answered
237 if verbose:
238 print(
239 "\nReceived %i packets, got %i answers, "
240 "remaining %i packets" % (
241 self.nbrecv + len(self.ans), len(self.ans),
242 max(0, self.notans - self.noans)
243 )
244 )
246 self.ans_result = SndRcvList(self.ans)
247 self.unans_result = PacketList(remain, "Unanswered")
249 def results(self):
250 # type: () -> Tuple[SndRcvList, PacketList]
251 return self.ans_result, self.unans_result
253 def _sndrcv_snd(self):
254 # type: () -> None
255 """Function used in the sending thread of sndrcv()"""
256 i = 0
257 p = None
258 try:
259 if self.verbose:
260 print("Begin emission:")
261 for p in self.tobesent:
262 # Populate the dictionary of _sndrcv_rcv
263 # _sndrcv_rcv won't miss the answer of a packet that
264 # has not been sent
265 self.hsent.setdefault(p.hashret(), []).append(p)
266 # Send packet
267 self.pks.send(p)
268 if self.inter:
269 time.sleep(self.inter)
270 if self.breakout:
271 break
272 i += 1
273 if self.verbose:
274 print("Finished sending %i packets." % i)
275 except SystemExit:
276 pass
277 except Exception:
278 if self.chainEX:
279 raise
280 else:
281 log_runtime.exception("--- Error sending packets")
282 finally:
283 try:
284 cast(Packet, self.tobesent).sent_time = \
285 cast(Packet, p).sent_time
286 except AttributeError:
287 pass
288 if self._flood:
289 self.notans = self._flood.iterlen
290 elif not self._send_done:
291 self.notans = i
292 self._send_done = True
293 # In threaded mode, timeout.
294 if self.threaded and self.timeout is not None and not self.breakout:
295 t = time.monotonic() + self.timeout
296 while time.monotonic() < t:
297 if self.breakout:
298 break
299 time.sleep(0.1)
300 if self.sniffer and self.sniffer.running:
301 self.sniffer.stop()
303 def _process_packet(self, r):
304 # type: (Packet) -> None
305 """Internal function used to process each packet."""
306 if r is None:
307 return
308 ok = False
309 h = r.hashret()
310 if h in self.hsent:
311 hlst = self.hsent[h]
312 for i, sentpkt in enumerate(hlst):
313 if r.answers(sentpkt):
314 self.ans.append(QueryAnswer(sentpkt, r))
315 if self.verbose > 1:
316 os.write(1, b"*")
317 ok = True
318 if not self.multi:
319 del hlst[i]
320 self.noans += 1
321 else:
322 if not hasattr(sentpkt, '_answered'):
323 self.noans += 1
324 sentpkt._answered = 1
325 break
326 if self._send_done and self.noans >= self.notans and not self.multi:
327 if self.sniffer and self.sniffer.running:
328 self.sniffer.stop(join=False)
329 if not ok:
330 if self.verbose > 1:
331 os.write(1, b".")
332 self.nbrecv += 1
333 if conf.debug_match:
334 debug.recv.append(r)
336 def _sndrcv_rcv(self, callback):
337 # type: (Callable[[], None]) -> None
338 """Function used to receive packets and check their hashret"""
339 # This is blocking.
340 self.sniffer = None # type: Optional[AsyncSniffer]
341 self.sniffer = AsyncSniffer()
342 self.sniffer._run(
343 prn=self._process_packet,
344 timeout=None if self.threaded else self.timeout,
345 store=False,
346 opened_socket=self.rcv_pks,
347 session=self.session,
348 stop_filter=self.stop_filter,
349 started_callback=callback,
350 chainCC=True,
351 )
354def sndrcv(*args, **kwargs):
355 # type: (*Any, **Any) -> Tuple[SndRcvList, PacketList]
356 """Scapy raw function to send a packet and receive its answer.
357 WARNING: This is an internal function. Using sr/srp/sr1/srp is
358 more appropriate in many cases.
359 """
360 sndrcver = SndRcvHandler(*args, **kwargs)
361 return sndrcver.results()
364def __gen_send(s, # type: SuperSocket
365 x, # type: _PacketIterable
366 inter=0, # type: int
367 loop=0, # type: int
368 count=None, # type: Optional[int]
369 verbose=None, # type: Optional[int]
370 realtime=False, # type: bool
371 return_packets=False, # type: bool
372 *args, # type: Any
373 **kargs # type: Any
374 ):
375 # type: (...) -> Optional[PacketList]
376 """
377 An internal function used by send/sendp to actually send the packets,
378 implement the send logic...
380 It will take care of iterating through the different packets
381 """
382 if isinstance(x, str):
383 x = conf.raw_layer(load=x)
384 if not isinstance(x, Gen):
385 x = SetGen(x)
386 if verbose is None:
387 verbose = conf.verb
388 n = 0
389 if count is not None:
390 loop = -count
391 elif not loop:
392 loop = -1
393 sent_packets = PacketList() if return_packets else None
394 p = None
395 try:
396 while loop:
397 dt0 = None
398 for p in x:
399 if realtime:
400 ct = time.time()
401 if dt0:
402 st = dt0 + float(p.time) - ct
403 if st > 0:
404 time.sleep(st)
405 else:
406 dt0 = ct - float(p.time)
407 s.send(p)
408 if sent_packets is not None:
409 sent_packets.append(p)
410 n += 1
411 if verbose:
412 os.write(1, b".")
413 time.sleep(inter)
414 if loop < 0:
415 loop += 1
416 except KeyboardInterrupt:
417 pass
418 finally:
419 try:
420 cast(Packet, x).sent_time = cast(Packet, p).sent_time
421 except AttributeError:
422 pass
423 if verbose:
424 print("\nSent %i packets." % n)
425 return sent_packets
428def _send(x, # type: _PacketIterable
429 _func, # type: Callable[[NetworkInterface], Type[SuperSocket]]
430 inter=0, # type: int
431 loop=0, # type: int
432 iface=None, # type: Optional[_GlobInterfaceType]
433 count=None, # type: Optional[int]
434 verbose=None, # type: Optional[int]
435 realtime=False, # type: bool
436 return_packets=False, # type: bool
437 socket=None, # type: Optional[SuperSocket]
438 **kargs # type: Any
439 ):
440 # type: (...) -> Optional[PacketList]
441 """Internal function used by send and sendp"""
442 need_closing = socket is None
443 iface = resolve_iface(iface or conf.iface)
444 socket = socket or _func(iface)(iface=iface, **kargs)
445 results = __gen_send(socket, x, inter=inter, loop=loop,
446 count=count, verbose=verbose,
447 realtime=realtime, return_packets=return_packets)
448 if need_closing:
449 socket.close()
450 return results
453@conf.commands.register
454def send(x, # type: _PacketIterable
455 iface=None, # type: Optional[_GlobInterfaceType]
456 **kargs # type: Any
457 ):
458 # type: (...) -> Optional[PacketList]
459 """
460 Send packets at layer 3
462 :param x: the packets
463 :param inter: time (in s) between two packets (default 0)
464 :param loop: send packet indefinitely (default 0)
465 :param count: number of packets to send (default None=1)
466 :param verbose: verbose mode (default None=conf.verb)
467 :param realtime: check that a packet was sent before sending the next one
468 :param return_packets: return the sent packets
469 :param socket: the socket to use (default is conf.L3socket(kargs))
470 :param iface: the interface to send the packets on
471 :param monitor: (not on linux) send in monitor mode
472 :returns: None
473 """
474 iface, ipv6 = _interface_selection(iface, x)
475 return _send(
476 x,
477 lambda iface: iface.l3socket(ipv6),
478 iface=iface,
479 **kargs
480 )
483@conf.commands.register
484def sendp(x, # type: _PacketIterable
485 iface=None, # type: Optional[_GlobInterfaceType]
486 iface_hint=None, # type: Optional[str]
487 socket=None, # type: Optional[SuperSocket]
488 **kargs # type: Any
489 ):
490 # type: (...) -> Optional[PacketList]
491 """
492 Send packets at layer 2
494 :param x: the packets
495 :param inter: time (in s) between two packets (default 0)
496 :param loop: send packet indefinitely (default 0)
497 :param count: number of packets to send (default None=1)
498 :param verbose: verbose mode (default None=conf.verb)
499 :param realtime: check that a packet was sent before sending the next one
500 :param return_packets: return the sent packets
501 :param socket: the socket to use (default is conf.L3socket(kargs))
502 :param iface: the interface to send the packets on
503 :param monitor: (not on linux) send in monitor mode
504 :returns: None
505 """
506 if iface is None and iface_hint is not None and socket is None:
507 iface = conf.route.route(iface_hint)[0]
508 return _send(
509 x,
510 lambda iface: iface.l2socket(),
511 iface=iface,
512 socket=socket,
513 **kargs
514 )
517@conf.commands.register
518def sendpfast(x: _PacketIterable,
519 pps: Optional[float] = None,
520 mbps: Optional[float] = None,
521 realtime: bool = False,
522 count: Optional[int] = None,
523 loop: int = 0,
524 file_cache: bool = False,
525 iface: Optional[_GlobInterfaceType] = None,
526 replay_args: Optional[List[str]] = None,
527 parse_results: bool = False,
528 ):
529 # type: (...) -> Optional[Dict[str, Any]]
530 """Send packets at layer 2 using tcpreplay for performance
532 :param pps: packets per second
533 :param mbps: MBits per second
534 :param realtime: use packet's timestamp, bending time with real-time value
535 :param loop: send the packet indefinitely (default 0)
536 :param count: number of packets to send (default None=1)
537 :param file_cache: cache packets in RAM instead of reading from
538 disk at each iteration
539 :param iface: output interface
540 :param replay_args: List of additional tcpreplay args (List[str])
541 :param parse_results: Return a dictionary of information
542 outputted by tcpreplay (default=False)
543 :returns: stdout, stderr, command used
544 """
545 if iface is None:
546 iface = conf.iface
547 argv = [conf.prog.tcpreplay, "--intf1=%s" % network_name(iface)]
548 if pps is not None:
549 argv.append("--pps=%f" % pps)
550 elif mbps is not None:
551 argv.append("--mbps=%f" % mbps)
552 elif realtime is not None:
553 argv.append("--multiplier=%f" % realtime)
554 else:
555 argv.append("--topspeed")
557 if count:
558 assert not loop, "Can't use loop and count at the same time in sendpfast"
559 argv.append("--loop=%i" % count)
560 elif loop:
561 argv.append("--loop=0")
562 if file_cache:
563 argv.append("--preload-pcap")
565 # Check for any additional args we didn't cover.
566 if replay_args is not None:
567 argv.extend(replay_args)
569 f = get_temp_file()
570 argv.append(f)
571 wrpcap(f, x)
572 results = None
573 with ContextManagerSubprocess(conf.prog.tcpreplay):
574 try:
575 cmd = subprocess.Popen(argv, stdout=subprocess.PIPE,
576 stderr=subprocess.PIPE)
577 except KeyboardInterrupt:
578 log_interactive.info("Interrupted by user")
579 except Exception:
580 os.unlink(f)
581 raise
582 else:
583 stdout, stderr = cmd.communicate()
584 if stderr:
585 log_runtime.warning(stderr.decode())
586 if parse_results:
587 results = _parse_tcpreplay_result(stdout, stderr, argv)
588 elif conf.verb > 2:
589 log_runtime.info(stdout.decode())
590 if os.path.exists(f):
591 os.unlink(f)
592 return results
595def _parse_tcpreplay_result(stdout_b, stderr_b, argv):
596 # type: (bytes, bytes, List[str]) -> Dict[str, Any]
597 """
598 Parse the output of tcpreplay and modify the results_dict to populate output information. # noqa: E501
599 Tested with tcpreplay v3.4.4
600 Tested with tcpreplay v4.1.2
601 :param stdout: stdout of tcpreplay subprocess call
602 :param stderr: stderr of tcpreplay subprocess call
603 :param argv: the command used in the subprocess call
604 :return: dictionary containing the results
605 """
606 try:
607 results = {}
608 stdout = plain_str(stdout_b).lower()
609 stderr = plain_str(stderr_b).strip().split("\n")
610 elements = {
611 "actual": (int, int, float),
612 "rated": (float, float, float),
613 "flows": (int, float, int, int),
614 "attempted": (int,),
615 "successful": (int,),
616 "failed": (int,),
617 "truncated": (int,),
618 "retried packets (eno": (int,),
619 "retried packets (eag": (int,),
620 }
621 multi = {
622 "actual": ("packets", "bytes", "time"),
623 "rated": ("bps", "mbps", "pps"),
624 "flows": ("flows", "fps", "flow_packets", "non_flow"),
625 "retried packets (eno": ("retried_enobufs",),
626 "retried packets (eag": ("retried_eagain",),
627 }
628 float_reg = r"([0-9]*\.[0-9]+|[0-9]+)"
629 int_reg = r"([0-9]+)"
630 any_reg = r"[^0-9]*"
631 r_types = {int: int_reg, float: float_reg}
632 for line in stdout.split("\n"):
633 line = line.strip()
634 for elt, _types in elements.items():
635 if line.startswith(elt):
636 regex = any_reg.join([r_types[x] for x in _types])
637 matches = re.search(regex, line)
638 for i, typ in enumerate(_types):
639 name = multi.get(elt, [elt])[i]
640 if matches:
641 results[name] = typ(matches.group(i + 1))
642 results["command"] = " ".join(argv)
643 results["warnings"] = stderr[:-1]
644 return results
645 except Exception as parse_exception:
646 if not conf.interactive:
647 raise
648 log_runtime.error("Error parsing output: %s", parse_exception)
649 return {}
652def _interface_selection(iface, # type: Optional[_GlobInterfaceType]
653 packet # type: _PacketIterable
654 ):
655 # type: (...) -> Tuple[NetworkInterface, bool]
656 """
657 Select the network interface according to the layer 3 destination
658 """
659 _iff, src, _ = next(packet.__iter__()).route()
660 ipv6 = False
661 if src:
662 try:
663 inet_pton(socket.AF_INET6, src)
664 ipv6 = True
665 except (ValueError, OSError):
666 pass
667 if iface is None:
668 try:
669 iff = resolve_iface(_iff or conf.iface)
670 except AttributeError:
671 iff = None
672 return iff or conf.iface, ipv6
674 return resolve_iface(iface), ipv6
677@conf.commands.register
678def sr(x, # type: _PacketIterable
679 promisc=None, # type: Optional[bool]
680 filter=None, # type: Optional[str]
681 iface=None, # type: Optional[_GlobInterfaceType]
682 nofilter=0, # type: int
683 *args, # type: Any
684 **kargs # type: Any
685 ):
686 # type: (...) -> Tuple[SndRcvList, PacketList]
687 """
688 Send and receive packets at layer 3
689 """
690 iface, ipv6 = _interface_selection(iface, x)
691 s = iface.l3socket(ipv6)(
692 promisc=promisc, filter=filter,
693 iface=iface, nofilter=nofilter,
694 )
695 result = sndrcv(s, x, *args, **kargs)
696 s.close()
697 return result
700@conf.commands.register
701def sr1(*args, **kargs):
702 # type: (*Any, **Any) -> Optional[Packet]
703 """
704 Send packets at layer 3 and return only the first answer
705 """
706 ans, _ = sr(*args, **kargs)
707 if ans:
708 return cast(Packet, ans[0][1])
709 return None
712@conf.commands.register
713def srp(x, # type: _PacketIterable
714 promisc=None, # type: Optional[bool]
715 iface=None, # type: Optional[_GlobInterfaceType]
716 iface_hint=None, # type: Optional[str]
717 filter=None, # type: Optional[str]
718 nofilter=0, # type: int
719 type=ETH_P_ALL, # type: int
720 *args, # type: Any
721 **kargs # type: Any
722 ):
723 # type: (...) -> Tuple[SndRcvList, PacketList]
724 """
725 Send and receive packets at layer 2
726 """
727 if iface is None and iface_hint is not None:
728 iface = conf.route.route(iface_hint)[0]
729 iface = resolve_iface(iface or conf.iface)
730 s = iface.l2socket()(promisc=promisc, iface=iface,
731 filter=filter, nofilter=nofilter, type=type)
732 result = sndrcv(s, x, *args, **kargs)
733 s.close()
734 return result
737@conf.commands.register
738def srp1(*args, **kargs):
739 # type: (*Any, **Any) -> Optional[Packet]
740 """
741 Send and receive packets at layer 2 and return only the first answer
742 """
743 ans, _ = srp(*args, **kargs)
744 if len(ans) > 0:
745 return cast(Packet, ans[0][1])
746 return None
749# Append doc
750for sr_func in [srp, srp1, sr, sr1]:
751 if sr_func.__doc__ is not None:
752 sr_func.__doc__ += _DOC_SNDRCV_PARAMS
755# SEND/RECV LOOP METHODS
758def __sr_loop(srfunc, # type: Callable[..., Tuple[SndRcvList, PacketList]]
759 pkts, # type: _PacketIterable
760 prn=lambda x: x[1].summary(), # type: Optional[Callable[[QueryAnswer], Any]] # noqa: E501
761 prnfail=lambda x: x.summary(), # type: Optional[Callable[[Packet], Any]]
762 inter=1, # type: int
763 timeout=None, # type: Optional[int]
764 count=None, # type: Optional[int]
765 verbose=None, # type: Optional[int]
766 store=1, # type: int
767 *args, # type: Any
768 **kargs # type: Any
769 ):
770 # type: (...) -> Tuple[SndRcvList, PacketList]
771 n = 0
772 r = 0
773 ct = conf.color_theme
774 if verbose is None:
775 verbose = conf.verb
776 parity = 0
777 ans = [] # type: List[QueryAnswer]
778 unans = [] # type: List[Packet]
779 if timeout is None:
780 timeout = min(2 * inter, 5)
781 try:
782 while True:
783 parity ^= 1
784 col = [ct.even, ct.odd][parity]
785 if count is not None:
786 if count == 0:
787 break
788 count -= 1
789 start = time.monotonic()
790 if verbose > 1:
791 print("\rsend...\r", end=' ')
792 res = srfunc(pkts, timeout=timeout, verbose=0, chainCC=True, *args, **kargs) # noqa: E501
793 n += len(res[0]) + len(res[1])
794 r += len(res[0])
795 if verbose > 1 and prn and len(res[0]) > 0:
796 msg = "RECV %i:" % len(res[0])
797 print("\r" + ct.success(msg), end=' ')
798 for rcv in res[0]:
799 print(col(prn(rcv)))
800 print(" " * len(msg), end=' ')
801 if verbose > 1 and prnfail and len(res[1]) > 0:
802 msg = "fail %i:" % len(res[1])
803 print("\r" + ct.fail(msg), end=' ')
804 for fail in res[1]:
805 print(col(prnfail(fail)))
806 print(" " * len(msg), end=' ')
807 if verbose > 1 and not (prn or prnfail):
808 print("recv:%i fail:%i" % tuple(
809 map(len, res[:2]) # type: ignore
810 ))
811 if verbose == 1:
812 if res[0]:
813 os.write(1, b"*")
814 if res[1]:
815 os.write(1, b".")
816 if store:
817 ans += res[0]
818 unans += res[1]
819 end = time.monotonic()
820 if end - start < inter:
821 time.sleep(inter + start - end)
822 except KeyboardInterrupt:
823 pass
825 if verbose and n > 0:
826 print(ct.normal("\nSent %i packets, received %i packets. %3.1f%% hits." % (n, r, 100.0 * r / n))) # noqa: E501
827 return SndRcvList(ans), PacketList(unans)
830@conf.commands.register
831def srloop(pkts, # type: _PacketIterable
832 *args, # type: Any
833 **kargs # type: Any
834 ):
835 # type: (...) -> Tuple[SndRcvList, PacketList]
836 """
837 Send a packet at layer 3 in loop and print the answer each time
838 srloop(pkts, [prn], [inter], [count], ...) --> None
839 """
840 return __sr_loop(sr, pkts, *args, **kargs)
843@conf.commands.register
844def srploop(pkts, # type: _PacketIterable
845 *args, # type: Any
846 **kargs # type: Any
847 ):
848 # type: (...) -> Tuple[SndRcvList, PacketList]
849 """
850 Send a packet at layer 2 in loop and print the answer each time
851 srloop(pkts, [prn], [inter], [count], ...) --> None
852 """
853 return __sr_loop(srp, pkts, *args, **kargs)
855# SEND/RECV FLOOD METHODS
858class _FloodGenerator(object):
859 def __init__(self, tobesent, maxretries):
860 # type: (_PacketIterable, Optional[int]) -> None
861 self.tobesent = tobesent
862 self.maxretries = maxretries
863 self.stopevent = Event()
864 self.iterlen = 0
866 def __iter__(self):
867 # type: () -> Iterator[Packet]
868 i = 0
869 while True:
870 i += 1
871 j = 0
872 if self.maxretries and i >= self.maxretries:
873 return
874 for p in self.tobesent:
875 if self.stopevent.is_set():
876 return
877 j += 1
878 yield p
879 if self.iterlen == 0:
880 self.iterlen = j
882 @property
883 def sent_time(self):
884 # type: () -> Union[EDecimal, float, None]
885 return cast(Packet, self.tobesent).sent_time
887 @sent_time.setter
888 def sent_time(self, val):
889 # type: (Union[EDecimal, float, None]) -> None
890 cast(Packet, self.tobesent).sent_time = val
892 def stop(self):
893 # type: () -> None
894 self.stopevent.set()
897def sndrcvflood(pks, # type: SuperSocket
898 pkt, # type: _PacketIterable
899 inter=0, # type: int
900 maxretries=None, # type: Optional[int]
901 verbose=None, # type: Optional[int]
902 chainCC=False, # type: bool
903 timeout=None # type: Optional[int]
904 ):
905 # type: (...) -> Tuple[SndRcvList, PacketList]
906 """sndrcv equivalent for flooding."""
908 flood_gen = _FloodGenerator(pkt, maxretries)
909 return sndrcv(
910 pks, flood_gen,
911 inter=inter, verbose=verbose,
912 chainCC=chainCC, timeout=timeout,
913 _flood=flood_gen
914 )
917@conf.commands.register
918def srflood(x, # type: _PacketIterable
919 promisc=None, # type: Optional[bool]
920 filter=None, # type: Optional[str]
921 iface=None, # type: Optional[_GlobInterfaceType]
922 nofilter=None, # type: Optional[bool]
923 *args, # type: Any
924 **kargs # type: Any
925 ):
926 # type: (...) -> Tuple[SndRcvList, PacketList]
927 """Flood and receive packets at layer 3
929 :param prn: function applied to packets received
930 :param unique: only consider packets whose print
931 :param nofilter: put 1 to avoid use of BPF filters
932 :param filter: provide a BPF filter
933 :param iface: listen answers only on the given interface
934 """
935 iface, ipv6 = _interface_selection(iface, x)
936 s = iface.l3socket(ipv6)(
937 promisc=promisc, filter=filter,
938 iface=iface, nofilter=nofilter,
939 )
940 r = sndrcvflood(s, x, *args, **kargs)
941 s.close()
942 return r
945@conf.commands.register
946def sr1flood(x, # type: _PacketIterable
947 promisc=None, # type: Optional[bool]
948 filter=None, # type: Optional[str]
949 iface=None, # type: Optional[_GlobInterfaceType]
950 nofilter=0, # type: int
951 *args, # type: Any
952 **kargs # type: Any
953 ):
954 # type: (...) -> Optional[Packet]
955 """Flood and receive packets at layer 3 and return only the first answer
957 :param prn: function applied to packets received
958 :param verbose: set verbosity level
959 :param nofilter: put 1 to avoid use of BPF filters
960 :param filter: provide a BPF filter
961 :param iface: listen answers only on the given interface
962 """
963 iface, ipv6 = _interface_selection(iface, x)
964 s = iface.l3socket(ipv6)(
965 promisc=promisc, filter=filter,
966 nofilter=nofilter, iface=iface,
967 )
968 ans, _ = sndrcvflood(s, x, *args, **kargs)
969 s.close()
970 if len(ans) > 0:
971 return cast(Packet, ans[0][1])
972 return None
975@conf.commands.register
976def srpflood(x, # type: _PacketIterable
977 promisc=None, # type: Optional[bool]
978 filter=None, # type: Optional[str]
979 iface=None, # type: Optional[_GlobInterfaceType]
980 iface_hint=None, # type: Optional[str]
981 nofilter=None, # type: Optional[bool]
982 *args, # type: Any
983 **kargs # type: Any
984 ):
985 # type: (...) -> Tuple[SndRcvList, PacketList]
986 """Flood and receive packets at layer 2
988 :param prn: function applied to packets received
989 :param unique: only consider packets whose print
990 :param nofilter: put 1 to avoid use of BPF filters
991 :param filter: provide a BPF filter
992 :param iface: listen answers only on the given interface
993 """
994 if iface is None and iface_hint is not None:
995 iface = conf.route.route(iface_hint)[0]
996 iface = resolve_iface(iface or conf.iface)
997 s = iface.l2socket()(promisc=promisc, filter=filter, iface=iface, nofilter=nofilter) # noqa: E501
998 r = sndrcvflood(s, x, *args, **kargs)
999 s.close()
1000 return r
1003@conf.commands.register
1004def srp1flood(x, # type: _PacketIterable
1005 promisc=None, # type: Optional[bool]
1006 filter=None, # type: Optional[str]
1007 iface=None, # type: Optional[_GlobInterfaceType]
1008 nofilter=0, # type: int
1009 *args, # type: Any
1010 **kargs # type: Any
1011 ):
1012 # type: (...) -> Optional[Packet]
1013 """Flood and receive packets at layer 2 and return only the first answer
1015 :param prn: function applied to packets received
1016 :param verbose: set verbosity level
1017 :param nofilter: put 1 to avoid use of BPF filters
1018 :param filter: provide a BPF filter
1019 :param iface: listen answers only on the given interface
1020 """
1021 iface = resolve_iface(iface or conf.iface)
1022 s = iface.l2socket()(promisc=promisc, filter=filter, nofilter=nofilter, iface=iface) # noqa: E501
1023 ans, _ = sndrcvflood(s, x, *args, **kargs)
1024 s.close()
1025 if len(ans) > 0:
1026 return cast(Packet, ans[0][1])
1027 return None
1029# SNIFF METHODS
1032class AsyncSniffer(object):
1033 """
1034 Sniff packets and return a list of packets.
1036 Args:
1037 count: number of packets to capture. 0 means infinity.
1038 store: whether to store sniffed packets or discard them
1039 prn: function to apply to each packet. If something is returned, it
1040 is displayed.
1041 --Ex: prn = lambda x: x.summary()
1042 session: a session = a flow decoder used to handle stream of packets.
1043 --Ex: session=TCPSession
1044 See below for more details.
1045 filter: BPF filter to apply.
1046 lfilter: Python function applied to each packet to determine if
1047 further action may be done.
1048 --Ex: lfilter = lambda x: x.haslayer(Padding)
1049 offline: PCAP file (or list of PCAP files) to read packets from,
1050 instead of sniffing them
1051 quiet: when set to True, the process stderr is discarded
1052 (default: False).
1053 timeout: stop sniffing after a given time (default: None).
1054 L2socket: use the provided L2socket (default: use conf.L2listen).
1055 opened_socket: provide an object (or a list of objects) ready to use
1056 .recv() on.
1057 stop_filter: Python function applied to each packet to determine if
1058 we have to stop the capture after this packet.
1059 --Ex: stop_filter = lambda x: x.haslayer(TCP)
1060 iface: interface or list of interfaces (default: None for sniffing
1061 on the default interface).
1062 monitor: use monitor mode. May not be available on all OS
1063 started_callback: called as soon as the sniffer starts sniffing
1064 (default: None).
1066 The iface, offline and opened_socket parameters can be either an
1067 element, a list of elements, or a dict object mapping an element to a
1068 label (see examples below).
1070 For more information about the session argument, see
1071 https://scapy.rtfd.io/en/latest/usage.html#advanced-sniffing-sniffing-sessions
1073 Examples: synchronous
1074 >>> sniff(filter="arp")
1075 >>> sniff(filter="tcp",
1076 ... session=IPSession, # defragment on-the-flow
1077 ... prn=lambda x: x.summary())
1078 >>> sniff(lfilter=lambda pkt: ARP in pkt)
1079 >>> sniff(iface="eth0", prn=Packet.summary)
1080 >>> sniff(iface=["eth0", "mon0"],
1081 ... prn=lambda pkt: "%s: %s" % (pkt.sniffed_on,
1082 ... pkt.summary()))
1083 >>> sniff(iface={"eth0": "Ethernet", "mon0": "Wifi"},
1084 ... prn=lambda pkt: "%s: %s" % (pkt.sniffed_on,
1085 ... pkt.summary()))
1087 Examples: asynchronous
1088 >>> t = AsyncSniffer(iface="enp0s3")
1089 >>> t.start()
1090 >>> time.sleep(1)
1091 >>> print("nice weather today")
1092 >>> t.stop()
1093 """
1095 def __init__(self, *args, **kwargs):
1096 # type: (*Any, **Any) -> None
1097 # Store keyword arguments
1098 self.args = args
1099 self.kwargs = kwargs
1100 self.running = False
1101 self.thread = None # type: Optional[Thread]
1102 self.results = None # type: Optional[PacketList]
1103 self.exception = None # type: Optional[Exception]
1105 def _setup_thread(self):
1106 # type: () -> None
1107 def _run_catch(self=self, *args, **kwargs):
1108 # type: (Any, *Any, **Any) -> None
1109 try:
1110 self._run(*args, **kwargs)
1111 except Exception as ex:
1112 self.exception = ex
1113 # Prepare sniffing thread
1114 self.thread = Thread(
1115 target=_run_catch,
1116 args=self.args,
1117 kwargs=self.kwargs,
1118 name="AsyncSniffer"
1119 )
1120 self.thread.daemon = True
1122 def _run(self,
1123 count=0, # type: int
1124 store=True, # type: bool
1125 offline=None, # type: Any
1126 quiet=False, # type: bool
1127 prn=None, # type: Optional[Callable[[Packet], Any]]
1128 lfilter=None, # type: Optional[Callable[[Packet], bool]]
1129 L2socket=None, # type: Optional[Type[SuperSocket]]
1130 timeout=None, # type: Optional[int]
1131 opened_socket=None, # type: Optional[SuperSocket]
1132 stop_filter=None, # type: Optional[Callable[[Packet], bool]]
1133 iface=None, # type: Optional[_GlobInterfaceType]
1134 started_callback=None, # type: Optional[Callable[[], Any]]
1135 session=None, # type: Optional[_GlobSessionType]
1136 chainCC=False, # type: bool
1137 **karg # type: Any
1138 ):
1139 # type: (...) -> None
1140 self.running = True
1141 self.count = 0
1142 lst = []
1143 # Start main thread
1144 # instantiate session
1145 if not isinstance(session, DefaultSession):
1146 session = session or DefaultSession
1147 session = session()
1148 # sniff_sockets follows: {socket: label}
1149 sniff_sockets = {} # type: Dict[SuperSocket, _GlobInterfaceType]
1150 if opened_socket is not None:
1151 if isinstance(opened_socket, list):
1152 sniff_sockets.update(
1153 (s, "socket%d" % i)
1154 for i, s in enumerate(opened_socket)
1155 )
1156 elif isinstance(opened_socket, dict):
1157 sniff_sockets.update(
1158 (s, label)
1159 for s, label in opened_socket.items()
1160 )
1161 else:
1162 sniff_sockets[opened_socket] = "socket0"
1163 if offline is not None:
1164 flt = karg.get('filter')
1166 if isinstance(offline, str):
1167 # Single file
1168 offline = [offline]
1169 if isinstance(offline, list) and \
1170 all(isinstance(elt, str) for elt in offline):
1171 # List of files
1172 sniff_sockets.update((PcapReader( # type: ignore
1173 fname if flt is None else
1174 tcpdump(fname,
1175 args=["-w", "-"],
1176 flt=flt,
1177 getfd=True,
1178 quiet=quiet)
1179 ), fname) for fname in offline)
1180 elif isinstance(offline, dict):
1181 # Dict of files
1182 sniff_sockets.update((PcapReader( # type: ignore
1183 fname if flt is None else
1184 tcpdump(fname,
1185 args=["-w", "-"],
1186 flt=flt,
1187 getfd=True,
1188 quiet=quiet)
1189 ), label) for fname, label in offline.items())
1190 elif isinstance(offline, (Packet, PacketList, list)):
1191 # Iterables (list of packets, PacketList..)
1192 offline = IterSocket(offline)
1193 sniff_sockets[offline if flt is None else PcapReader(
1194 tcpdump(offline,
1195 args=["-w", "-"],
1196 flt=flt,
1197 getfd=True,
1198 quiet=quiet)
1199 )] = offline
1200 else:
1201 # Other (file descriptors...)
1202 sniff_sockets[PcapReader( # type: ignore
1203 offline if flt is None else
1204 tcpdump(offline,
1205 args=["-w", "-"],
1206 flt=flt,
1207 getfd=True,
1208 quiet=quiet)
1209 )] = offline
1210 if not sniff_sockets or iface is not None:
1211 # The _RL2 function resolves the L2socket of an iface
1212 _RL2 = lambda i: L2socket or resolve_iface(i).l2listen() # type: Callable[[_GlobInterfaceType], Callable[..., SuperSocket]] # noqa: E501
1213 if isinstance(iface, list):
1214 sniff_sockets.update(
1215 (_RL2(ifname)(type=ETH_P_ALL, iface=ifname, **karg),
1216 ifname)
1217 for ifname in iface
1218 )
1219 elif isinstance(iface, dict):
1220 sniff_sockets.update(
1221 (_RL2(ifname)(type=ETH_P_ALL, iface=ifname, **karg),
1222 iflabel)
1223 for ifname, iflabel in iface.items()
1224 )
1225 else:
1226 iface = iface or conf.iface
1227 sniff_sockets[_RL2(iface)(type=ETH_P_ALL, iface=iface,
1228 **karg)] = iface
1230 # Get select information from the sockets
1231 _main_socket = next(iter(sniff_sockets))
1232 select_func = _main_socket.select
1233 nonblocking_socket = getattr(_main_socket, "nonblocking_socket", False)
1234 # We check that all sockets use the same select(), or raise a warning
1235 if not all(select_func == sock.select for sock in sniff_sockets):
1236 warning("Warning: inconsistent socket types ! "
1237 "The used select function "
1238 "will be the one of the first socket")
1240 close_pipe = None # type: Optional[ObjectPipe[None]]
1241 if not nonblocking_socket:
1242 # select is blocking: Add special control socket
1243 from scapy.automaton import ObjectPipe
1244 close_pipe = ObjectPipe[None]("control_socket")
1245 sniff_sockets[close_pipe] = "control_socket" # type: ignore
1247 def stop_cb():
1248 # type: () -> None
1249 if self.running and close_pipe:
1250 close_pipe.send(None)
1251 self.continue_sniff = False
1252 self.stop_cb = stop_cb
1253 else:
1254 # select is non blocking
1255 def stop_cb():
1256 # type: () -> None
1257 self.continue_sniff = False
1258 self.stop_cb = stop_cb
1260 try:
1261 if started_callback:
1262 started_callback()
1263 self.continue_sniff = True
1265 # Start timeout
1266 if timeout is not None:
1267 stoptime = time.monotonic() + timeout
1268 remain = None
1270 while sniff_sockets and self.continue_sniff:
1271 if timeout is not None:
1272 remain = stoptime - time.monotonic()
1273 if remain <= 0:
1274 break
1275 sockets = select_func(list(sniff_sockets.keys()), remain)
1276 dead_sockets = []
1277 for s in sockets:
1278 if s is close_pipe: # type: ignore
1279 break
1280 # The session object is passed the socket to call recv() on,
1281 # and may perform additional processing (ip defrag, etc.)
1282 try:
1283 packets = session.recv(s)
1284 # A session can return multiple objects
1285 for p in packets:
1286 if lfilter and not lfilter(p):
1287 continue
1288 p.sniffed_on = sniff_sockets[s]
1289 # post-processing
1290 self.count += 1
1291 if store:
1292 lst.append(p)
1293 if prn:
1294 result = prn(p)
1295 if result is not None:
1296 print(result)
1297 # check
1298 if (stop_filter and stop_filter(p)) or \
1299 (0 < count <= self.count):
1300 self.continue_sniff = False
1301 break
1302 except EOFError:
1303 # End of stream
1304 try:
1305 s.close()
1306 except Exception:
1307 pass
1308 dead_sockets.append(s)
1309 continue
1310 except Exception as ex:
1311 msg = " It was closed."
1312 try:
1313 # Make sure it's closed
1314 s.close()
1315 except Exception as ex2:
1316 msg = " close() failed with '%s'" % ex2
1317 warning(
1318 "Socket %s failed with '%s'." % (s, ex) + msg
1319 )
1320 dead_sockets.append(s)
1321 if conf.debug_dissector >= 2:
1322 raise
1323 continue
1324 # Removed dead sockets
1325 for s in dead_sockets:
1326 del sniff_sockets[s]
1327 if len(sniff_sockets) == 1 and \
1328 close_pipe in sniff_sockets: # type: ignore
1329 # Only the close_pipe left
1330 del sniff_sockets[close_pipe] # type: ignore
1331 except KeyboardInterrupt:
1332 if chainCC:
1333 raise
1334 self.running = False
1335 if opened_socket is None:
1336 for s in sniff_sockets:
1337 s.close()
1338 elif close_pipe:
1339 close_pipe.close()
1340 self.results = PacketList(lst, "Sniffed")
1342 def start(self):
1343 # type: () -> None
1344 """Starts AsyncSniffer in async mode"""
1345 self._setup_thread()
1346 if self.thread:
1347 self.thread.start()
1349 def stop(self, join=True):
1350 # type: (bool) -> Optional[PacketList]
1351 """Stops AsyncSniffer if not in async mode"""
1352 if self.running:
1353 try:
1354 self.stop_cb()
1355 except AttributeError:
1356 raise Scapy_Exception(
1357 "Unsupported (offline or unsupported socket)"
1358 )
1359 if join:
1360 self.join()
1361 return self.results
1362 return None
1363 else:
1364 raise Scapy_Exception("Not running ! (check .running attr)")
1366 def join(self, *args, **kwargs):
1367 # type: (*Any, **Any) -> None
1368 if self.thread:
1369 self.thread.join(*args, **kwargs)
1370 if self.exception is not None:
1371 raise self.exception
1374@conf.commands.register
1375def sniff(*args, **kwargs):
1376 # type: (*Any, **Any) -> PacketList
1377 sniffer = AsyncSniffer()
1378 sniffer._run(*args, **kwargs)
1379 return cast(PacketList, sniffer.results)
1382sniff.__doc__ = AsyncSniffer.__doc__
1385@conf.commands.register
1386def bridge_and_sniff(if1, # type: _GlobInterfaceType
1387 if2, # type: _GlobInterfaceType
1388 xfrm12=None, # type: Optional[Callable[[Packet], Union[Packet, bool]]] # noqa: E501
1389 xfrm21=None, # type: Optional[Callable[[Packet], Union[Packet, bool]]] # noqa: E501
1390 prn=None, # type: Optional[Callable[[Packet], Any]]
1391 L2socket=None, # type: Optional[Type[SuperSocket]]
1392 *args, # type: Any
1393 **kargs # type: Any
1394 ):
1395 # type: (...) -> PacketList
1396 """Forward traffic between interfaces if1 and if2, sniff and return
1397 the exchanged packets.
1399 :param if1: the interfaces to use (interface names or opened sockets).
1400 :param if2:
1401 :param xfrm12: a function to call when forwarding a packet from if1 to
1402 if2. If it returns True, the packet is forwarded as it. If it
1403 returns False or None, the packet is discarded. If it returns a
1404 packet, this packet is forwarded instead of the original packet
1405 one.
1406 :param xfrm21: same as xfrm12 for packets forwarded from if2 to if1.
1408 The other arguments are the same than for the function sniff(),
1409 except for offline, opened_socket and iface that are ignored.
1410 See help(sniff) for more.
1411 """
1412 for arg in ['opened_socket', 'offline', 'iface']:
1413 if arg in kargs:
1414 log_runtime.warning("Argument %s cannot be used in "
1415 "bridge_and_sniff() -- ignoring it.", arg)
1416 del kargs[arg]
1418 def _init_socket(iface, # type: _GlobInterfaceType
1419 count, # type: int
1420 L2socket=L2socket # type: Optional[Type[SuperSocket]]
1421 ):
1422 # type: (...) -> Tuple[SuperSocket, _GlobInterfaceType]
1423 if isinstance(iface, SuperSocket):
1424 return iface, "iface%d" % count
1425 else:
1426 if not L2socket:
1427 iface = resolve_iface(iface or conf.iface)
1428 L2socket = iface.l2socket()
1429 return L2socket(iface=iface), iface
1430 sckt1, if1 = _init_socket(if1, 1)
1431 sckt2, if2 = _init_socket(if2, 2)
1432 peers = {if1: sckt2, if2: sckt1}
1433 xfrms = {}
1434 if xfrm12 is not None:
1435 xfrms[if1] = xfrm12
1436 if xfrm21 is not None:
1437 xfrms[if2] = xfrm21
1439 def prn_send(pkt):
1440 # type: (Packet) -> None
1441 try:
1442 sendsock = peers[pkt.sniffed_on or ""]
1443 except KeyError:
1444 return
1445 if pkt.sniffed_on in xfrms:
1446 try:
1447 _newpkt = xfrms[pkt.sniffed_on](pkt)
1448 except Exception:
1449 log_runtime.warning(
1450 'Exception in transformation function for packet [%s] '
1451 'received on %s -- dropping',
1452 pkt.summary(), pkt.sniffed_on, exc_info=True
1453 )
1454 return
1455 else:
1456 if isinstance(_newpkt, bool):
1457 if not _newpkt:
1458 return
1459 newpkt = pkt
1460 else:
1461 newpkt = _newpkt
1462 else:
1463 newpkt = pkt
1464 try:
1465 sendsock.send(newpkt)
1466 except Exception:
1467 log_runtime.warning('Cannot forward packet [%s] received on %s',
1468 pkt.summary(), pkt.sniffed_on, exc_info=True)
1469 if prn is None:
1470 prn = prn_send
1471 else:
1472 prn_orig = prn
1474 def prn(pkt):
1475 # type: (Packet) -> Any
1476 prn_send(pkt)
1477 return prn_orig(pkt)
1479 return sniff(opened_socket={sckt1: if1, sckt2: if2}, prn=prn,
1480 *args, **kargs)
1483@conf.commands.register
1484def tshark(*args, **kargs):
1485 # type: (Any, Any) -> None
1486 """Sniff packets and print them calling pkt.summary().
1487 This tries to replicate what text-wireshark (tshark) would look like"""
1489 if 'iface' in kargs:
1490 iface = kargs.get('iface')
1491 elif 'opened_socket' in kargs:
1492 iface = cast(SuperSocket, kargs.get('opened_socket')).iface
1493 else:
1494 iface = conf.iface
1495 print("Capturing on '%s'" % iface)
1497 # This should be a nonlocal variable, using a mutable object
1498 # for Python 2 compatibility
1499 i = [0]
1501 def _cb(pkt):
1502 # type: (Packet) -> None
1503 print("%5d\t%s" % (i[0], pkt.summary()))
1504 i[0] += 1
1506 sniff(prn=_cb, store=False, *args, **kargs)
1507 print("\n%d packet%s captured" % (i[0], 's' if i[0] > 1 else ''))