"""Network utility functions for interface detection.""" import ipaddress import logging import socket import struct logger = logging.getLogger(__name__) # Interfaces to exclude from selection EXCLUDED_INTERFACE_PREFIXES = ("lo", "docker", "br-", "veth", "virbr") def get_network_interfaces() -> list[dict]: """Get all network interfaces with their IPs and subnets. Returns: List of dicts with name, ip, netmask, subnet, broadcast """ interfaces = [] try: import fcntl for iface in socket.if_nameindex(): name = iface[1] # Skip excluded interfaces if any(name.startswith(prefix) for prefix in EXCLUDED_INTERFACE_PREFIXES): continue try: s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Get IP address ip_bytes = fcntl.ioctl( s.fileno(), 0x8915, # SIOCGIFADDR struct.pack("256s", name[:15].encode()), )[20:24] ip = socket.inet_ntoa(ip_bytes) # Get netmask netmask_bytes = fcntl.ioctl( s.fileno(), 0x891B, # SIOCGIFNETMASK struct.pack("256s", name[:15].encode()), )[20:24] netmask = socket.inet_ntoa(netmask_bytes) # Calculate subnet network = ipaddress.IPv4Network(f"{ip}/{netmask}", strict=False) interfaces.append( { "name": name, "ip": ip, "netmask": netmask, "subnet": str(network), } ) s.close() except OSError: # Interface doesn't have an IP or other error pass except Exception as e: logger.debug("Error getting info for interface %s: %s", name, e) except ImportError: # fcntl not available (Windows) logger.warning("fcntl not available, interface detection limited") except Exception as e: logger.error("Error enumerating interfaces: %s", e) return interfaces def find_interface_for_ip(target_ip: str) -> dict | None: """Find which interface is on the same subnet as the target IP. Args: target_ip: IP address to find the matching interface for Returns: Interface dict or None if not found """ try: target = ipaddress.IPv4Address(target_ip) except ValueError: logger.error("Invalid target IP: %s", target_ip) return None interfaces = get_network_interfaces() for iface in interfaces: try: network = ipaddress.IPv4Network(iface["subnet"], strict=False) if target in network: logger.debug("Found interface %s (%s) for target %s", iface["name"], iface["ip"], target_ip) return iface except ValueError: continue logger.warning("No interface found for target IP %s", target_ip) return None def get_other_interfaces(exclude_ip: str) -> list[dict]: """Get all interfaces except the one with the given IP. Args: exclude_ip: IP address of interface to exclude Returns: List of interface dicts """ interfaces = get_network_interfaces() return [iface for iface in interfaces if iface["ip"] != exclude_ip]