#!/usr/bin/env python3 import os import subprocess import argparse def get_physical_interfaces(): """ Retrieve a list of physical network interfaces on the system. This function checks the `/sys/class/net/` directory to identify physical network interfaces. It determines if an interface is physical by verifying the presence of a symbolic link to a `device` directory. Returns: list: A list of strings, where each string is the name of a physical network interface. """ interfaces_path = '/sys/class/net/' physical_interfaces = [] for interface in os.listdir(interfaces_path): if not os.path.islink(os.path.join(interfaces_path, interface, 'device')): continue physical_interfaces.append(interface) return physical_interfaces def get_virtual_interfaces(): """ Retrieves a list of virtual network interfaces on the system. This function scans the network interfaces available in the '/sys/class/net/' directory and filters out physical interfaces and the loopback interface ('lo'). It identifies virtual interfaces by checking if the 'device' path is not a symbolic link. Returns: list: A list of virtual network interface names as strings. """ interfaces_path = '/sys/class/net/' virtual_interfaces = [] for interface in os.listdir(interfaces_path): if os.path.islink(os.path.join(interfaces_path, interface, 'device')): continue if interface == 'lo': continue virtual_interfaces.append(interface) return virtual_interfaces def display_interface_details(show_all=False): """ Display details of network interfaces, including their IP address, subnet mask, and MAC address, in a formatted table. Args: show_all (bool): If True, includes both physical and virtual interfaces in the output. Defaults to False, which only displays physical interfaces. Output: Prints a table with the following columns: - Interface: The name of the network interface. - IP Address: The IP address assigned to the interface. - Subnet: The subnet mask associated with the IP address. - MAC Address: The MAC address of the interface. Notes: - The function uses the `ip` command to fetch interface details. - If an interface does not have an IP address, "No IP" is displayed in the IP Address column. - If an error occurs while fetching details for an interface, an error message is printed for that interface. """ interfaces = get_physical_interfaces() if show_all: interfaces.extend(get_virtual_interfaces()) interfaces.sort() # Define column widths based on expected maximum content length col_widths = { 'interface': 15, # Increased to accommodate longer interface names 'ip': 18, # Increased for longer IP addresses 'subnet': 10, 'mac': 18 } # Print header with proper formatting print(f"{'Interface':<{col_widths['interface']}} {'IP Address':<{col_widths['ip']}} {'Subnet':<{col_widths['subnet']}} {'MAC Address':<{col_widths['mac']}}") print("-" * (col_widths['interface'] + col_widths['ip'] + col_widths['subnet'] + col_widths['mac'])) for interface in interfaces: try: result = subprocess.run(['ip', '-br', 'addr', 'show', interface], capture_output=True, text=True, check=True) if result.returncode == 0: parts = result.stdout.strip().split() if len(parts) >= 3: name = parts[0] mac = parts[1] ip_with_mask = parts[2] # Split IP/mask if '/' in ip_with_mask: ip = ip_with_mask.split('/')[0] subnet = ip_with_mask.split('/')[1] else: ip = ip_with_mask subnet = "" print(f"{name:<{col_widths['interface']}} {ip:<{col_widths['ip']}} {subnet:<{col_widths['subnet']}} {mac:<{col_widths['mac']}}") else: print(f"{interface:<{col_widths['interface']}} {'No IP':<{col_widths['ip']}} {'':<{col_widths['subnet']}} {parts[1] if len(parts) > 1 else 'N/A':<{col_widths['mac']}}") except Exception as e: print(f"Error fetching details for {interface}: {e}") if __name__ == "__main__": parser = argparse.ArgumentParser(description='Display network interface information') parser.add_argument('-a', '--all', action='store_true', help='Show all interfaces including virtual ones like Tailscale and WireGuard') args = parser.parse_args() display_interface_details(args.all)