Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 25 additions & 8 deletions scapy/arch/bpf/supersocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -390,14 +390,31 @@ def send(self, pkt):
self.assigned_interface = iff

# Build the frame
if self.guessed_cls == Loopback:
# bpf(4) man page (from macOS, but also for BSD):
# "A packet can be sent out on the network by writing to a bpf
# file descriptor. [...] Currently only writes to Ethernets and
# SLIP links are supported"
#
# Headers are only mentioned for reads, not writes. tuntaposx's tun
# device reports as a "loopback" device, but it does IP.
#
# LINKTYPE_NULL / DLT_NULL (Loopback) is a special case. From the
# bpf(4) man page (from macOS/Darwin, but also for BSD):
#
# "A packet can be sent out on the network by writing to a bpf file
# descriptor. [...] Currently only writes to Ethernets and SLIP links
# are supported."
#
# Headers are only mentioned for reads, not writes, and it has the
# name "NULL" and id=0.
#
# The _correct_ behaviour appears to be that one should add a BSD
# Loopback header to every sent packet. This is needed by FreeBSD's
# if_lo, and Darwin's if_lo & if_utun.
#
# tuntaposx appears to have interpreted "NULL" as "no headers".
# Thankfully its interfaces have a different name (tunX) to Darwin's
# if_utun interfaces (utunX).
#
# There might be other drivers which make the same mistake as
# tuntaposx, but these are typically provided with VPN software, and
# Apple are breaking these kexts in a future version of macOS... so
# the problem will eventually go away. They already don't work on Macs
# with Apple Silicon (M1).
if DARWIN and iff.startswith('tun') and self.guessed_cls == Loopback:
frame = raw(pkt)
else:
frame = raw(self.guessed_cls() / pkt)
Expand Down
20 changes: 20 additions & 0 deletions test/bpf.uts
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,23 @@ s.send(IP(dst="8.8.8.8")/ICMP())
s = L3bpfSocket()
s.assigned_interface = conf.loopback_name
s.send(IP(dst="8.8.8.8")/ICMP())

= L3bpfSocket - send and sniff on loopback
~ needs_root

localhost_ip = conf.ifaces[conf.loopback_name].ips[4][0]

def cb():
# Send a ping to the loopback IP.
s = L3bpfSocket(iface=conf.loopback_name)
s.send(IP(dst=localhost_ip)/ICMP(seq=1001))

t = AsyncSniffer(iface=conf.loopback_name, started_callback=cb)
t.start()
time.sleep(1)
t.stop()
t.join(timeout=1)

# We expect to see our packet and kernel's response.
len(t.results.filter(lambda p: (
IP in p and ICMP in p and (p[IP].src == localhost_ip or p[IP].dst == localhost_ip) and p[ICMP].seq == 1001))) == 2