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