643 lines
21 KiB
Python
643 lines
21 KiB
Python
|
# This file is part of Scapy
|
||
|
# See http://www.secdev.org/projects/scapy for more information
|
||
|
# Copyright (C) Philippe Biondi <phil@secdev.org>
|
||
|
# This program is published under a GPLv2 license
|
||
|
|
||
|
"""
|
||
|
Linux specific functions.
|
||
|
"""
|
||
|
|
||
|
from __future__ import absolute_import
|
||
|
|
||
|
|
||
|
import array
|
||
|
from fcntl import ioctl
|
||
|
import os
|
||
|
from select import select
|
||
|
import socket
|
||
|
import struct
|
||
|
import time
|
||
|
import re
|
||
|
|
||
|
import subprocess
|
||
|
|
||
|
from scapy.compat import raw, plain_str
|
||
|
from scapy.consts import LINUX
|
||
|
import scapy.utils
|
||
|
import scapy.utils6
|
||
|
from scapy.packet import Packet, Padding
|
||
|
from scapy.config import conf
|
||
|
from scapy.data import MTU, ETH_P_ALL, SOL_PACKET, SO_ATTACH_FILTER, \
|
||
|
SO_TIMESTAMPNS
|
||
|
from scapy.supersocket import SuperSocket
|
||
|
from scapy.error import warning, Scapy_Exception, \
|
||
|
ScapyInvalidPlatformException, log_runtime
|
||
|
from scapy.arch.common import get_if, compile_filter
|
||
|
import scapy.modules.six as six
|
||
|
from scapy.modules.six.moves import range
|
||
|
|
||
|
from scapy.arch.common import get_if_raw_hwaddr # noqa: F401
|
||
|
|
||
|
# From bits/ioctls.h
|
||
|
SIOCGIFHWADDR = 0x8927 # Get hardware address
|
||
|
SIOCGIFADDR = 0x8915 # get PA address
|
||
|
SIOCGIFNETMASK = 0x891b # get network PA mask
|
||
|
SIOCGIFNAME = 0x8910 # get iface name
|
||
|
SIOCSIFLINK = 0x8911 # set iface channel
|
||
|
SIOCGIFCONF = 0x8912 # get iface list
|
||
|
SIOCGIFFLAGS = 0x8913 # get flags
|
||
|
SIOCSIFFLAGS = 0x8914 # set flags
|
||
|
SIOCGIFINDEX = 0x8933 # name -> if_index mapping
|
||
|
SIOCGIFCOUNT = 0x8938 # get number of devices
|
||
|
SIOCGSTAMP = 0x8906 # get packet timestamp (as a timeval)
|
||
|
|
||
|
# From if.h
|
||
|
IFF_UP = 0x1 # Interface is up.
|
||
|
IFF_BROADCAST = 0x2 # Broadcast address valid.
|
||
|
IFF_DEBUG = 0x4 # Turn on debugging.
|
||
|
IFF_LOOPBACK = 0x8 # Is a loopback net.
|
||
|
IFF_POINTOPOINT = 0x10 # Interface is point-to-point link.
|
||
|
IFF_NOTRAILERS = 0x20 # Avoid use of trailers.
|
||
|
IFF_RUNNING = 0x40 # Resources allocated.
|
||
|
IFF_NOARP = 0x80 # No address resolution protocol.
|
||
|
IFF_PROMISC = 0x100 # Receive all packets.
|
||
|
|
||
|
# From netpacket/packet.h
|
||
|
PACKET_ADD_MEMBERSHIP = 1
|
||
|
PACKET_DROP_MEMBERSHIP = 2
|
||
|
PACKET_RECV_OUTPUT = 3
|
||
|
PACKET_RX_RING = 5
|
||
|
PACKET_STATISTICS = 6
|
||
|
PACKET_MR_MULTICAST = 0
|
||
|
PACKET_MR_PROMISC = 1
|
||
|
PACKET_MR_ALLMULTI = 2
|
||
|
|
||
|
# From net/route.h
|
||
|
RTF_UP = 0x0001 # Route usable
|
||
|
RTF_REJECT = 0x0200
|
||
|
|
||
|
# From if_packet.h
|
||
|
PACKET_HOST = 0 # To us
|
||
|
PACKET_BROADCAST = 1 # To all
|
||
|
PACKET_MULTICAST = 2 # To group
|
||
|
PACKET_OTHERHOST = 3 # To someone else
|
||
|
PACKET_OUTGOING = 4 # Outgoing of any type
|
||
|
PACKET_LOOPBACK = 5 # MC/BRD frame looped back
|
||
|
PACKET_USER = 6 # To user space
|
||
|
PACKET_KERNEL = 7 # To kernel space
|
||
|
PACKET_AUXDATA = 8
|
||
|
PACKET_FASTROUTE = 6 # Fastrouted frame
|
||
|
# Unused, PACKET_FASTROUTE and PACKET_LOOPBACK are invisible to user space
|
||
|
|
||
|
# Utils
|
||
|
|
||
|
|
||
|
def get_if_raw_addr(iff):
|
||
|
try:
|
||
|
return get_if(iff, SIOCGIFADDR)[20:24]
|
||
|
except IOError:
|
||
|
return b"\0\0\0\0"
|
||
|
|
||
|
|
||
|
def get_if_list():
|
||
|
try:
|
||
|
f = open("/proc/net/dev", "rb")
|
||
|
except IOError:
|
||
|
try:
|
||
|
f.close()
|
||
|
except Exception:
|
||
|
pass
|
||
|
warning("Can't open /proc/net/dev !")
|
||
|
return []
|
||
|
lst = []
|
||
|
f.readline()
|
||
|
f.readline()
|
||
|
for line in f:
|
||
|
line = plain_str(line)
|
||
|
lst.append(line.split(":")[0].strip())
|
||
|
f.close()
|
||
|
return lst
|
||
|
|
||
|
|
||
|
def get_working_if():
|
||
|
"""
|
||
|
Return the name of the first network interfcace that is up.
|
||
|
"""
|
||
|
for i in get_if_list():
|
||
|
if i == conf.loopback_name:
|
||
|
continue
|
||
|
ifflags = struct.unpack("16xH14x", get_if(i, SIOCGIFFLAGS))[0]
|
||
|
if ifflags & IFF_UP:
|
||
|
return i
|
||
|
return conf.loopback_name
|
||
|
|
||
|
|
||
|
def attach_filter(sock, bpf_filter, iface):
|
||
|
"""
|
||
|
Compile bpf filter and attach it to a socket
|
||
|
|
||
|
:param sock: the python socket
|
||
|
:param bpf_filter: the bpf string filter to compile
|
||
|
:param iface: the interface used to compile
|
||
|
"""
|
||
|
bp = compile_filter(bpf_filter, iface)
|
||
|
sock.setsockopt(socket.SOL_SOCKET, SO_ATTACH_FILTER, bp)
|
||
|
|
||
|
|
||
|
def set_promisc(s, iff, val=1):
|
||
|
mreq = struct.pack("IHH8s", get_if_index(iff), PACKET_MR_PROMISC, 0, b"")
|
||
|
if val:
|
||
|
cmd = PACKET_ADD_MEMBERSHIP
|
||
|
else:
|
||
|
cmd = PACKET_DROP_MEMBERSHIP
|
||
|
s.setsockopt(SOL_PACKET, cmd, mreq)
|
||
|
|
||
|
|
||
|
def get_alias_address(iface_name, ip_mask, gw_str, metric):
|
||
|
"""
|
||
|
Get the correct source IP address of an interface alias
|
||
|
"""
|
||
|
|
||
|
# Detect the architecture
|
||
|
if scapy.consts.IS_64BITS:
|
||
|
offset, name_len = 16, 40
|
||
|
else:
|
||
|
offset, name_len = 32, 32
|
||
|
|
||
|
# Retrieve interfaces structures
|
||
|
sck = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||
|
names = array.array('B', b'\0' * 4096)
|
||
|
ifreq = ioctl(sck.fileno(), SIOCGIFCONF,
|
||
|
struct.pack("iL", len(names), names.buffer_info()[0]))
|
||
|
|
||
|
# Extract interfaces names
|
||
|
out = struct.unpack("iL", ifreq)[0]
|
||
|
names = names.tobytes() if six.PY3 else names.tostring()
|
||
|
names = [names[i:i + offset].split(b'\0', 1)[0] for i in range(0, out, name_len)] # noqa: E501
|
||
|
|
||
|
# Look for the IP address
|
||
|
for ifname in names:
|
||
|
# Only look for a matching interface name
|
||
|
if not ifname.decode("utf8").startswith(iface_name):
|
||
|
continue
|
||
|
|
||
|
# Retrieve and convert addresses
|
||
|
ifreq = ioctl(sck, SIOCGIFADDR, struct.pack("16s16x", ifname))
|
||
|
ifaddr = struct.unpack(">I", ifreq[20:24])[0]
|
||
|
ifreq = ioctl(sck, SIOCGIFNETMASK, struct.pack("16s16x", ifname))
|
||
|
msk = struct.unpack(">I", ifreq[20:24])[0]
|
||
|
|
||
|
# Get the full interface name
|
||
|
ifname = plain_str(ifname)
|
||
|
if ':' in ifname:
|
||
|
ifname = ifname[:ifname.index(':')]
|
||
|
else:
|
||
|
continue
|
||
|
|
||
|
# Check if the source address is included in the network
|
||
|
if (ifaddr & msk) == ip_mask:
|
||
|
sck.close()
|
||
|
return (ifaddr & msk, msk, gw_str, ifname,
|
||
|
scapy.utils.ltoa(ifaddr), metric)
|
||
|
|
||
|
sck.close()
|
||
|
return
|
||
|
|
||
|
|
||
|
def read_routes():
|
||
|
try:
|
||
|
f = open("/proc/net/route", "rb")
|
||
|
except IOError:
|
||
|
warning("Can't open /proc/net/route !")
|
||
|
return []
|
||
|
routes = []
|
||
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||
|
try:
|
||
|
ifreq = ioctl(s, SIOCGIFADDR, struct.pack("16s16x", conf.loopback_name.encode("utf8"))) # noqa: E501
|
||
|
addrfamily = struct.unpack("h", ifreq[16:18])[0]
|
||
|
if addrfamily == socket.AF_INET:
|
||
|
ifreq2 = ioctl(s, SIOCGIFNETMASK, struct.pack("16s16x", conf.loopback_name.encode("utf8"))) # noqa: E501
|
||
|
msk = socket.ntohl(struct.unpack("I", ifreq2[20:24])[0])
|
||
|
dst = socket.ntohl(struct.unpack("I", ifreq[20:24])[0]) & msk
|
||
|
ifaddr = scapy.utils.inet_ntoa(ifreq[20:24])
|
||
|
routes.append((dst, msk, "0.0.0.0", conf.loopback_name, ifaddr, 1)) # noqa: E501
|
||
|
else:
|
||
|
warning("Interface %s: unknown address family (%i)" % (conf.loopback_name, addrfamily)) # noqa: E501
|
||
|
except IOError as err:
|
||
|
if err.errno == 99:
|
||
|
warning("Interface %s: no address assigned" % conf.loopback_name) # noqa: E501
|
||
|
else:
|
||
|
warning("Interface %s: failed to get address config (%s)" % (conf.loopback_name, str(err))) # noqa: E501
|
||
|
|
||
|
for line in f.readlines()[1:]:
|
||
|
line = plain_str(line)
|
||
|
iff, dst, gw, flags, _, _, metric, msk, _, _, _ = line.split()
|
||
|
flags = int(flags, 16)
|
||
|
if flags & RTF_UP == 0:
|
||
|
continue
|
||
|
if flags & RTF_REJECT:
|
||
|
continue
|
||
|
try:
|
||
|
ifreq = ioctl(s, SIOCGIFADDR, struct.pack("16s16x", iff.encode("utf8"))) # noqa: E501
|
||
|
except IOError: # interface is present in routing tables but does not have any assigned IP # noqa: E501
|
||
|
ifaddr = "0.0.0.0"
|
||
|
ifaddr_int = 0
|
||
|
else:
|
||
|
addrfamily = struct.unpack("h", ifreq[16:18])[0]
|
||
|
if addrfamily == socket.AF_INET:
|
||
|
ifaddr = scapy.utils.inet_ntoa(ifreq[20:24])
|
||
|
ifaddr_int = struct.unpack("!I", ifreq[20:24])[0]
|
||
|
else:
|
||
|
warning("Interface %s: unknown address family (%i)", iff, addrfamily) # noqa: E501
|
||
|
continue
|
||
|
|
||
|
# Attempt to detect an interface alias based on addresses inconsistencies # noqa: E501
|
||
|
dst_int = socket.htonl(int(dst, 16)) & 0xffffffff
|
||
|
msk_int = socket.htonl(int(msk, 16)) & 0xffffffff
|
||
|
gw_str = scapy.utils.inet_ntoa(struct.pack("I", int(gw, 16)))
|
||
|
metric = int(metric)
|
||
|
|
||
|
if ifaddr_int & msk_int != dst_int:
|
||
|
tmp_route = get_alias_address(iff, dst_int, gw_str, metric)
|
||
|
if tmp_route:
|
||
|
routes.append(tmp_route)
|
||
|
else:
|
||
|
routes.append((dst_int, msk_int, gw_str, iff, ifaddr, metric))
|
||
|
|
||
|
else:
|
||
|
routes.append((dst_int, msk_int, gw_str, iff, ifaddr, metric))
|
||
|
|
||
|
f.close()
|
||
|
s.close()
|
||
|
return routes
|
||
|
|
||
|
############
|
||
|
# IPv6 #
|
||
|
############
|
||
|
|
||
|
|
||
|
def in6_getifaddr():
|
||
|
"""
|
||
|
Returns a list of 3-tuples of the form (addr, scope, iface) where
|
||
|
'addr' is the address of scope 'scope' associated to the interface
|
||
|
'iface'.
|
||
|
|
||
|
This is the list of all addresses of all interfaces available on
|
||
|
the system.
|
||
|
"""
|
||
|
ret = []
|
||
|
try:
|
||
|
fdesc = open("/proc/net/if_inet6", "rb")
|
||
|
except IOError:
|
||
|
return ret
|
||
|
for line in fdesc:
|
||
|
# addr, index, plen, scope, flags, ifname
|
||
|
tmp = plain_str(line).split()
|
||
|
addr = scapy.utils6.in6_ptop(
|
||
|
b':'.join(
|
||
|
struct.unpack('4s4s4s4s4s4s4s4s', tmp[0].encode())
|
||
|
).decode()
|
||
|
)
|
||
|
# (addr, scope, iface)
|
||
|
ret.append((addr, int(tmp[3], 16), tmp[5]))
|
||
|
fdesc.close()
|
||
|
return ret
|
||
|
|
||
|
|
||
|
def read_routes6():
|
||
|
try:
|
||
|
f = open("/proc/net/ipv6_route", "rb")
|
||
|
except IOError:
|
||
|
return []
|
||
|
# 1. destination network
|
||
|
# 2. destination prefix length
|
||
|
# 3. source network displayed
|
||
|
# 4. source prefix length
|
||
|
# 5. next hop
|
||
|
# 6. metric
|
||
|
# 7. reference counter (?!?)
|
||
|
# 8. use counter (?!?)
|
||
|
# 9. flags
|
||
|
# 10. device name
|
||
|
routes = []
|
||
|
|
||
|
def proc2r(p):
|
||
|
ret = struct.unpack('4s4s4s4s4s4s4s4s', p)
|
||
|
ret = b':'.join(ret).decode()
|
||
|
return scapy.utils6.in6_ptop(ret)
|
||
|
|
||
|
lifaddr = in6_getifaddr()
|
||
|
for line in f.readlines():
|
||
|
d, dp, _, _, nh, metric, rc, us, fl, dev = line.split()
|
||
|
metric = int(metric, 16)
|
||
|
fl = int(fl, 16)
|
||
|
dev = plain_str(dev)
|
||
|
|
||
|
if fl & RTF_UP == 0:
|
||
|
continue
|
||
|
if fl & RTF_REJECT:
|
||
|
continue
|
||
|
|
||
|
d = proc2r(d)
|
||
|
dp = int(dp, 16)
|
||
|
nh = proc2r(nh)
|
||
|
|
||
|
cset = [] # candidate set (possible source addresses)
|
||
|
if dev == conf.loopback_name:
|
||
|
if d == '::':
|
||
|
continue
|
||
|
cset = ['::1']
|
||
|
else:
|
||
|
devaddrs = (x for x in lifaddr if x[2] == dev)
|
||
|
cset = scapy.utils6.construct_source_candidate_set(d, dp, devaddrs)
|
||
|
|
||
|
if len(cset) != 0:
|
||
|
routes.append((d, dp, nh, dev, cset, metric))
|
||
|
f.close()
|
||
|
return routes
|
||
|
|
||
|
|
||
|
def get_if_index(iff):
|
||
|
return int(struct.unpack("I", get_if(iff, SIOCGIFINDEX)[16:20])[0])
|
||
|
|
||
|
|
||
|
if os.uname()[4] in ['x86_64', 'aarch64']:
|
||
|
def get_last_packet_timestamp(sock):
|
||
|
ts = ioctl(sock, SIOCGSTAMP, "1234567890123456")
|
||
|
s, us = struct.unpack("QQ", ts)
|
||
|
return s + us / 1000000.0
|
||
|
else:
|
||
|
def get_last_packet_timestamp(sock):
|
||
|
ts = ioctl(sock, SIOCGSTAMP, "12345678")
|
||
|
s, us = struct.unpack("II", ts)
|
||
|
return s + us / 1000000.0
|
||
|
|
||
|
|
||
|
def _flush_fd(fd):
|
||
|
if hasattr(fd, 'fileno'):
|
||
|
fd = fd.fileno()
|
||
|
while True:
|
||
|
r, w, e = select([fd], [], [], 0)
|
||
|
if r:
|
||
|
os.read(fd, MTU)
|
||
|
else:
|
||
|
break
|
||
|
|
||
|
|
||
|
def get_iface_mode(iface):
|
||
|
"""Return the interface mode.
|
||
|
params:
|
||
|
- iface: the iwconfig interface
|
||
|
"""
|
||
|
p = subprocess.Popen(["iwconfig", iface], stdout=subprocess.PIPE,
|
||
|
stderr=subprocess.STDOUT)
|
||
|
output, err = p.communicate()
|
||
|
match = re.search(br"mode:([a-zA-Z]*)", output.lower())
|
||
|
if match:
|
||
|
return plain_str(match.group(1))
|
||
|
return "unknown"
|
||
|
|
||
|
|
||
|
def set_iface_monitor(iface, monitor):
|
||
|
"""Sets the monitor mode (or remove it) from an interface.
|
||
|
params:
|
||
|
- iface: the iwconfig interface
|
||
|
- monitor: True if the interface should be set in monitor mode,
|
||
|
False if it should be in managed mode
|
||
|
"""
|
||
|
mode = get_iface_mode(iface)
|
||
|
if mode == "unknown":
|
||
|
warning("Could not parse iwconfig !")
|
||
|
current_monitor = mode == "monitor"
|
||
|
if monitor == current_monitor:
|
||
|
# Already correct
|
||
|
return True
|
||
|
s_mode = "monitor" if monitor else "managed"
|
||
|
|
||
|
def _check_call(commands):
|
||
|
p = subprocess.Popen(commands,
|
||
|
stderr=subprocess.PIPE,
|
||
|
stdout=subprocess.PIPE)
|
||
|
stdout, stderr = p.communicate()
|
||
|
if p.returncode != 0:
|
||
|
warning("%s failed !" % " ".join(commands))
|
||
|
return False
|
||
|
return True
|
||
|
if not _check_call(["ifconfig", iface, "down"]):
|
||
|
return False
|
||
|
if not _check_call(["iwconfig", iface, "mode", s_mode]):
|
||
|
return False
|
||
|
if not _check_call(["ifconfig", iface, "up"]):
|
||
|
return False
|
||
|
return True
|
||
|
|
||
|
|
||
|
class L2Socket(SuperSocket):
|
||
|
desc = "read/write packets at layer 2 using Linux PF_PACKET sockets"
|
||
|
|
||
|
def __init__(self, iface=None, type=ETH_P_ALL, promisc=None, filter=None,
|
||
|
nofilter=0, monitor=None):
|
||
|
self.iface = conf.iface if iface is None else iface
|
||
|
self.type = type
|
||
|
self.promisc = conf.sniff_promisc if promisc is None else promisc
|
||
|
if monitor is not None:
|
||
|
warning(
|
||
|
"The monitor argument is ineffective on native linux sockets."
|
||
|
" Use set_iface_monitor instead."
|
||
|
)
|
||
|
self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) # noqa: E501
|
||
|
if not nofilter:
|
||
|
if conf.except_filter:
|
||
|
if filter:
|
||
|
filter = "(%s) and not (%s)" % (filter, conf.except_filter)
|
||
|
else:
|
||
|
filter = "not (%s)" % conf.except_filter
|
||
|
if filter is not None:
|
||
|
try:
|
||
|
attach_filter(self.ins, filter, iface)
|
||
|
except ImportError as ex:
|
||
|
warning("Cannot set filter: %s" % ex)
|
||
|
if self.promisc:
|
||
|
set_promisc(self.ins, self.iface)
|
||
|
self.ins.bind((self.iface, type))
|
||
|
_flush_fd(self.ins)
|
||
|
self.ins.setsockopt(
|
||
|
socket.SOL_SOCKET,
|
||
|
socket.SO_RCVBUF,
|
||
|
conf.bufsize
|
||
|
)
|
||
|
if not six.PY2:
|
||
|
# Receive Auxiliary Data (VLAN tags)
|
||
|
try:
|
||
|
self.ins.setsockopt(SOL_PACKET, PACKET_AUXDATA, 1)
|
||
|
self.ins.setsockopt(
|
||
|
socket.SOL_SOCKET,
|
||
|
SO_TIMESTAMPNS,
|
||
|
1
|
||
|
)
|
||
|
self.auxdata_available = True
|
||
|
except OSError:
|
||
|
# Note: Auxiliary Data is only supported since
|
||
|
# Linux 2.6.21
|
||
|
msg = "Your Linux Kernel does not support Auxiliary Data!"
|
||
|
log_runtime.info(msg)
|
||
|
if isinstance(self, L2ListenSocket):
|
||
|
self.outs = None
|
||
|
else:
|
||
|
self.outs = self.ins
|
||
|
self.outs.setsockopt(
|
||
|
socket.SOL_SOCKET,
|
||
|
socket.SO_SNDBUF,
|
||
|
conf.bufsize
|
||
|
)
|
||
|
sa_ll = self.ins.getsockname()
|
||
|
if sa_ll[3] in conf.l2types:
|
||
|
self.LL = conf.l2types[sa_ll[3]]
|
||
|
self.lvl = 2
|
||
|
elif sa_ll[1] in conf.l3types:
|
||
|
self.LL = conf.l3types[sa_ll[1]]
|
||
|
self.lvl = 3
|
||
|
else:
|
||
|
self.LL = conf.default_l2
|
||
|
self.lvl = 2
|
||
|
warning("Unable to guess type (interface=%s protocol=%#x family=%i). Using %s", sa_ll[0], sa_ll[1], sa_ll[3], self.LL.name) # noqa: E501
|
||
|
|
||
|
def close(self):
|
||
|
if self.closed:
|
||
|
return
|
||
|
try:
|
||
|
if self.promisc and self.ins:
|
||
|
set_promisc(self.ins, self.iface, 0)
|
||
|
except (AttributeError, OSError):
|
||
|
pass
|
||
|
SuperSocket.close(self)
|
||
|
|
||
|
def recv_raw(self, x=MTU):
|
||
|
"""Receives a packet, then returns a tuple containing (cls, pkt_data, time)""" # noqa: E501
|
||
|
pkt, sa_ll, ts = self._recv_raw(self.ins, x)
|
||
|
if self.outs and sa_ll[2] == socket.PACKET_OUTGOING:
|
||
|
return None, None, None
|
||
|
if ts is None:
|
||
|
ts = get_last_packet_timestamp(self.ins)
|
||
|
return self.LL, pkt, ts
|
||
|
|
||
|
def send(self, x):
|
||
|
try:
|
||
|
return SuperSocket.send(self, x)
|
||
|
except socket.error as msg:
|
||
|
if msg.errno == 22 and len(x) < conf.min_pkt_size:
|
||
|
padding = b"\x00" * (conf.min_pkt_size - len(x))
|
||
|
if isinstance(x, Packet):
|
||
|
return SuperSocket.send(self, x / Padding(load=padding))
|
||
|
else:
|
||
|
return SuperSocket.send(self, raw(x) + padding)
|
||
|
raise
|
||
|
|
||
|
|
||
|
class L2ListenSocket(L2Socket):
|
||
|
desc = "read packets at layer 2 using Linux PF_PACKET sockets. Also receives the packets going OUT" # noqa: E501
|
||
|
|
||
|
def send(self, x):
|
||
|
raise Scapy_Exception("Can't send anything with L2ListenSocket")
|
||
|
|
||
|
|
||
|
class L3PacketSocket(L2Socket):
|
||
|
desc = "read/write packets at layer 3 using Linux PF_PACKET sockets"
|
||
|
|
||
|
def recv(self, x=MTU):
|
||
|
pkt = SuperSocket.recv(self, x)
|
||
|
if pkt and self.lvl == 2:
|
||
|
pkt.payload.time = pkt.time
|
||
|
return pkt.payload
|
||
|
return pkt
|
||
|
|
||
|
def send(self, x):
|
||
|
iff = x.route()[0]
|
||
|
if iff is None:
|
||
|
iff = conf.iface
|
||
|
sdto = (iff, self.type)
|
||
|
self.outs.bind(sdto)
|
||
|
sn = self.outs.getsockname()
|
||
|
ll = lambda x: x
|
||
|
type_x = type(x)
|
||
|
if type_x in conf.l3types:
|
||
|
sdto = (iff, conf.l3types[type_x])
|
||
|
if sn[3] in conf.l2types:
|
||
|
ll = lambda x: conf.l2types[sn[3]]() / x
|
||
|
if self.lvl == 3 and type_x != self.LL:
|
||
|
warning("Incompatible L3 types detected using %s instead of %s !",
|
||
|
type_x, self.LL)
|
||
|
self.LL = type_x
|
||
|
sx = raw(ll(x))
|
||
|
x.sent_time = time.time()
|
||
|
try:
|
||
|
self.outs.sendto(sx, sdto)
|
||
|
except socket.error as msg:
|
||
|
if msg.errno == 22 and len(sx) < conf.min_pkt_size:
|
||
|
self.outs.send(sx + b"\x00" * (conf.min_pkt_size - len(sx)))
|
||
|
elif conf.auto_fragment and msg.errno == 90:
|
||
|
for p in x.fragment():
|
||
|
self.outs.sendto(raw(ll(p)), sdto)
|
||
|
else:
|
||
|
raise
|
||
|
|
||
|
|
||
|
class VEthPair(object):
|
||
|
"""
|
||
|
encapsulates a virtual Ethernet interface pair
|
||
|
"""
|
||
|
|
||
|
def __init__(self, iface_name, peer_name):
|
||
|
|
||
|
if not LINUX:
|
||
|
# ToDo: do we need a kernel version check here?
|
||
|
raise ScapyInvalidPlatformException(
|
||
|
'Virtual Ethernet interface pair only available on Linux'
|
||
|
)
|
||
|
|
||
|
self.ifaces = [iface_name, peer_name]
|
||
|
|
||
|
def iface(self):
|
||
|
return self.ifaces[0]
|
||
|
|
||
|
def peer(self):
|
||
|
return self.ifaces[1]
|
||
|
|
||
|
def setup(self):
|
||
|
"""
|
||
|
create veth pair links
|
||
|
:raises subprocess.CalledProcessError if operation fails
|
||
|
"""
|
||
|
subprocess.check_call(['ip', 'link', 'add', self.ifaces[0], 'type', 'veth', 'peer', 'name', self.ifaces[1]]) # noqa: E501
|
||
|
|
||
|
def destroy(self):
|
||
|
"""
|
||
|
remove veth pair links
|
||
|
:raises subprocess.CalledProcessError if operation fails
|
||
|
"""
|
||
|
subprocess.check_call(['ip', 'link', 'del', self.ifaces[0]])
|
||
|
|
||
|
def up(self):
|
||
|
"""
|
||
|
set veth pair links up
|
||
|
:raises subprocess.CalledProcessError if operation fails
|
||
|
"""
|
||
|
for idx in [0, 1]:
|
||
|
subprocess.check_call(["ip", "link", "set", self.ifaces[idx], "up"]) # noqa: E501
|
||
|
|
||
|
def down(self):
|
||
|
"""
|
||
|
set veth pair links down
|
||
|
:raises subprocess.CalledProcessError if operation fails
|
||
|
"""
|
||
|
for idx in [0, 1]:
|
||
|
subprocess.check_call(["ip", "link", "set", self.ifaces[idx], "down"]) # noqa: E501
|
||
|
|
||
|
def __enter__(self):
|
||
|
self.setup()
|
||
|
self.up()
|
||
|
return self
|
||
|
|
||
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||
|
self.destroy()
|