More or less like what SLAAC is doing, but without relying on the router advertisment.
pip install netifacespython ./generate-ipv6.py "eth0" "2001:db8:abcd"| import netifaces as ni | |
| import re | |
| import argparse | |
| import ipaddress | |
| def get_mac_address(interface): | |
| try: | |
| interface_details = ni.ifaddresses(interface) | |
| mac_address = interface_details[ni.AF_LINK][0]['addr'] | |
| return mac_address | |
| except (ValueError, KeyError): | |
| raise ValueError(f"No MAC address found for interface {interface}") | |
| def mac_to_eui64(mac, ipv6_network): | |
| # Remove any non-hexadecimal characters from MAC | |
| mac = re.sub(r'[^0-9a-fA-F]', '', mac) | |
| mac_bytes = bytes.fromhex(mac) | |
| # Flip the universal/local bit of the MAC address | |
| mac_bytes = bytearray(mac_bytes) | |
| mac_bytes[0] ^= 0b00000010 | |
| # Insert ff:fe in the middle of the MAC to form EUI-64 | |
| eui64 = mac_bytes[:3] + b'\xff\xfe' + mac_bytes[3:] | |
| eui64 = int.from_bytes(eui64, byteorder='big') | |
| # Calculate full IPv6 address within the network | |
| full_ipv6_address = ipaddress.IPv6Address(ipv6_network.network_address + eui64) | |
| return full_ipv6_address | |
| def main(): | |
| parser = argparse.ArgumentParser(description="Generate an IPv6 address from a network device MAC address and an IPv6 prefix with a network mask.") | |
| parser.add_argument("interface_name", type=str, help="Network interface name") | |
| parser.add_argument("ipv6_network", type=str, help="IPv6 network with prefix, e.g., xxxx:xxxx:xxxx::/xx") | |
| args = parser.parse_args() | |
| try: | |
| ipv6_network = ipaddress.IPv6Network(args.ipv6_network, strict=False) | |
| mac_address = get_mac_address(args.interface_name) | |
| ipv6_address = mac_to_eui64(mac_address, ipv6_network) | |
| print(f"Generated IPv6 Address: {ipv6_address} from MAC Address: {mac_address}") | |
| except ValueError as e: | |
| print(e) | |
| except ipaddress.AddressValueError as ave: | |
| print("Error: Invalid IPv6 network format.") | |
| if __name__ == "__main__": | |
| main() |