#!/usr/bin/env python
"""XXX doc"""

import netifaces
import netaddr
from socket import AF_INET6
from math import log2, ceil
from os.path import exists

import sys
import subprocess


def main():
    tap = sys.argv[1]
    n   = int(sys.argv[2])
    assert n > 0, n

    # determine tap's address, network and owner
    owner = readfile(sysnet(tap, 'owner')) .strip()
    addr  = None
    net   = None
    prefixlen = None
    for iaddr in netifaces.ifaddresses(tap)[AF_INET6]:
        a = iaddr['addr']
        if '%' in a: # link-local
            a = a.split('%')[0]
            a = netaddr.IPAddress(a)
            assert a.is_link_local(), a
            continue

        if addr is not None:
            raise RuntimeError('%s: multiple addresses:  %s and %s' % (tap, addr, a))

        addr = netaddr.IPAddress(a)
        netmask, plen = iaddr['netmask'].split('/')
        prefixlen = int(plen)
        net = netaddr.IPNetwork('%s/%d' % (a, prefixlen))

    if addr is None:
        raise RuntimeError('%s: no non link-local addresses' % tap)

    # normalize network
    # ex 2401:5180:0:66:a7ff:ffff:ffff:ffff/71  ->  2401:5180:0:66:a600::/71
    net = net.cidr

    print('%s: split %s by %d' % (tap, net, n))

    # see how much prefix bits we need to take to divide by n
    n += 1  # also leaving first range for the original tap
    ptake = ceil(log2(n))

    for i, subnet in enumerate(net.subnet(prefixlen + ptake)):
        if i == 0:
            print('preserve         %s' % subnet)
            continue    # leave this range for original tap
        subtap = '%s-%d' % (tap, i)
        print('-> %s   %s' % (subtap, subnet))
        if exists(sysnet(subtap)):
            run('ip', 'link', 'del', subtap)
        run('ip', 'tuntap', 'add', 'dev', subtap, 'mode', 'tap', 'user', owner)
        run('ip', 'link',   'set', subtap, 'up')
        run('ip', 'addr',   'add', str(subnet), 'dev', subtap, 'noprefixroute')
        run('ip', 'route',  'add', str(subnet[1]), 'dev', subtap)
        run('ip', 'route',  'add', str(subnet), 'dev', subtap, 'via', str(subnet[1]))


# sysnet returns path on /sys corresponding to given interface.
def sysnet(ifname, subpath=None):
    path = '/sys/devices/virtual/net/%s' % ifname
    if subpath is not None:
        path += '/'+subpath
    return path

def run(*argv):
    print(' # %s' % ' '.join(argv))
    subprocess.check_call(argv)

def readfile(path):
    with open(path) as f:
        return f.read()


if __name__ == '__main__':
    main()