• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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) Guillaume Valadon <guillaume@valadon.net>
5
6"""
7Scapy *BSD native support - core
8"""
9
10
11import fcntl
12import os
13import socket
14import struct
15
16from scapy.arch.bpf.consts import BIOCSETF, BIOCSETIF
17from scapy.arch.common import compile_filter
18from scapy.config import conf
19from scapy.consts import LINUX
20from scapy.error import Scapy_Exception
21from scapy.interfaces import (
22    InterfaceProvider,
23    NetworkInterface,
24    _GlobInterfaceType,
25)
26
27# re-export
28from scapy.arch.bpf.pfroute import (  # noqa F403
29    read_routes,
30    read_routes6,
31    _get_if_list,
32)
33from scapy.arch.common import get_if_raw_addr, read_nameservers  # noqa: F401
34
35# Typing
36from typing import (
37    Dict,
38    List,
39    Tuple,
40)
41
42if LINUX:
43    raise OSError("BPF conflicts with Linux")
44
45# BPF specific functions
46
47
48def get_dev_bpf():
49    # type: () -> Tuple[int, int]
50    """Returns an opened BPF file object"""
51
52    # Get the first available BPF handle
53    for bpf in range(256):
54        try:
55            fd = os.open("/dev/bpf%i" % bpf, os.O_RDWR)
56            return (fd, bpf)
57        except OSError as ex:
58            if ex.errno == 13:  # Permission denied
59                raise Scapy_Exception(
60                    (
61                        "Permission denied: could not open /dev/bpf%i. "
62                        "Make sure to be running Scapy as root ! (sudo)"
63                    )
64                    % bpf
65                )
66            continue
67
68    raise Scapy_Exception("No /dev/bpf handle is available !")
69
70
71def attach_filter(fd, bpf_filter, iface):
72    # type: (int, str, _GlobInterfaceType) -> None
73    """Attach a BPF filter to the BPF file descriptor"""
74    bp = compile_filter(bpf_filter, iface)
75    # Assign the BPF program to the interface
76    ret = fcntl.ioctl(fd, BIOCSETF, bp)
77    if ret < 0:
78        raise Scapy_Exception("Can't attach the BPF filter !")
79
80
81def in6_getifaddr():
82    # type: () -> List[Tuple[str, int, str]]
83    """
84    Returns a list of 3-tuples of the form (addr, scope, iface) where
85    'addr' is the address of scope 'scope' associated to the interface
86    'iface'.
87
88    This is the list of all addresses of all interfaces available on
89    the system.
90    """
91    ifaces = _get_if_list()
92    return [
93        (ip["address"], ip["scope"], iface["name"])
94        for iface in ifaces.values()
95        for ip in iface["ips"]
96        if ip["af_family"] == socket.AF_INET6
97    ]
98
99
100# Interface provider
101
102
103class BPFInterfaceProvider(InterfaceProvider):
104    name = "BPF"
105
106    def _is_valid(self, dev):
107        # type: (NetworkInterface) -> bool
108        if not dev.flags & 0x1:  # not IFF_UP
109            return False
110        # Get a BPF handle
111        try:
112            fd = get_dev_bpf()[0]
113        except Scapy_Exception:
114            return True  # Can't check if available (non sudo?)
115        if fd is None:
116            raise Scapy_Exception("No /dev/bpf are available !")
117        # Check if the interface can be used
118        try:
119            fcntl.ioctl(fd, BIOCSETIF, struct.pack("16s16x", dev.network_name.encode()))
120        except IOError:
121            return False
122        else:
123            return True
124        finally:
125            # Close the file descriptor
126            os.close(fd)
127
128    def load(self):
129        # type: () -> Dict[str, NetworkInterface]
130        data = {}
131        for iface in _get_if_list().values():
132            if_data = iface.copy()
133            if_data.update(
134                {
135                    "network_name": iface["name"],
136                    "description": iface["name"],
137                    "ips": [x["address"] for x in iface["ips"]],
138                }
139            )
140            data[iface["name"]] = NetworkInterface(self, if_data)
141        return data
142
143
144conf.ifaces.register_provider(BPFInterfaceProvider)
145