8 from pyroute2 import IPDB, WireGuard
10 from wg_pubkey_to_llv6 import pubkey_to_lladdr
13 def fix_addr(addr_str):
14 """This is awful. It fixes wrongly-formatted IPv6 addresses that come back from pyroute2.
15 All 16 bytes are returned, with a ':' between each.
16 e.g., fe:80:00:00:.../128 instead of fe80::.../128"""
18 # IPv4 is rendered fine.
21 # Rebuild IPv6 address in proper form.
22 addr, mask = addr_str.split('/')
23 addr = str(ipaddress.IPv6Address(b''.join(bytes.fromhex(x) for x in addr.split(':'))))
24 return '/'.join([addr, mask])
27 def peer_to_spec(peer):
28 """Partially reconstruct the peer definition. We only care about the
29 public key [identity] and the allowed ipaddresses [to be updated]."""
31 'public_key': peer.WGPEER_A_PUBLIC_KEY['value'],
32 'allowed_ips': [fix_addr(x['addr']) for x in peer.WGPEER_A_ALLOWEDIPS['value']],
36 def add_llv6_addresses_to_interface(ifname):
42 wg_iface = wg.info(ifname)[0]
44 # Calculate interface IPv6 link-local address.
45 dev_pk = wg_iface.WGDEVICE_A_PUBLIC_KEY['value']
46 dev_lla = pubkey_to_lladdr(dev_pk)
47 addresses.add(dev_lla)
48 address_map[dev_pk] = dev_lla
50 # Calculate peer IPv6 link-local addresses.
51 peers = [peer_to_spec(peer) for peer in wg_iface.WGDEVICE_A_PEERS.value]
53 peer_pk = peer['public_key']
54 peer_lla = pubkey_to_lladdr(peer_pk)
55 if peer_lla in addresses:
56 collisions.add(peer_lla)
57 addresses.add(peer_lla)
58 address_map[peer_pk] = peer_lla
60 # Did we find collisions?
62 print('Peer link local address collisions:')
63 for pk, lla in addresses.items:
66 if dev_lla in collisions:
67 raise Exception('Our link local address collides with a peer. Giving up.')
69 print('Proceeding with link-local assignments, skipping colliding peers.')
71 # Add IPv6 link-local address and subnet to interface.
73 iface = ip.interfaces[ifname]
74 iface.add_ip(str(dev_lla)+'/64')
77 # Add IPv6 link-local addresses to peers without address collisions.
79 peer_pk = peer['public_key']
80 peer_lla = str(address_map[peer_pk])+'/128'
81 if peer_lla not in peer['allowed_ips'] and peer_lla not in collisions:
82 peer['allowed_ips'].append(peer_lla)
83 wg.set(ifname, peer=peer)
87 ifname = os.getenv('IFACE')
94 print('No interface specified; quitting.')
96 add_llv6_addresses_to_interface(ifname)
99 if __name__ == '__main__':