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