"""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(f"Error getting info for interface {name}: {e}") except ImportError: # fcntl not available (Windows) logger.warning("fcntl not available, interface detection limited") except Exception as e: logger.error(f"Error enumerating interfaces: {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(f"Invalid target IP: {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(f"Found interface {iface['name']} ({iface['ip']}) for target {target_ip}") return iface except ValueError: continue logger.warning(f"No interface found for target IP {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]