#!/bin/python

from ctypes import (CDLL, CFUNCTYPE, c_char, c_char_p, c_int, c_void_p, c_size_t, get_errno, sizeof, c_uint32)
import time
import random
import ctypes
import errno
import os

NETLINK_ROUTE = 0
LIBNL = CDLL('libnl-3.so', use_errno=True)

c = CDLL("libc.so.6")
c.__errno_location.restype = ctypes.POINTER(ctypes.c_int)

def get_errno():
    return c.__errno_location().contents.value;

_nl_connect = CFUNCTYPE(c_int, c_void_p, c_int)(('nl_connect', LIBNL))
_nl_socket_alloc = CFUNCTYPE(c_void_p)(('nl_socket_alloc', LIBNL))
_nl_socket_set_local_port = CFUNCTYPE(None, c_void_p, c_uint32)(('nl_socket_set_local_port', LIBNL))
_nl_socket_get_local_port = CFUNCTYPE(c_uint32, c_void_p)(('nl_socket_get_local_port', LIBNL))
_nl_geterror = CFUNCTYPE(c_char_p, c_int)(('nl_geterror', LIBNL))
_nl_socket_free = CFUNCTYPE(None, c_void_p)(('nl_socket_free', LIBNL))

def s_nl_socket_get_local_port(port):
    p2 = port >> 22
    return "%d (%d)" % (p2, port)


parallel_handles = []
test_handles = []

for i in range(0,1023):
    handle = _nl_socket_alloc()
    port = _nl_socket_get_local_port(handle)
    #_nl_socket_set_local_port (handle, port);
    print("create handle[%d]: %r - %s" % (i, handle, s_nl_socket_get_local_port(port)))

    err = _nl_connect(handle, NETLINK_ROUTE)
    if err:
        n = get_errno()
        print("connect handle[%d] failed: %d - %d - %s" % (i, err, n, os.strerror(n)))
        raise IOError(-err, _nl_geterror(err))
    parallel_handles.append(handle)

for handle in parallel_handles:
    _nl_socket_set_local_port(handle, _nl_socket_get_local_port(handle))


for i in range(0,1030):
    handle = _nl_socket_alloc()
    print("test handle[%d]: %r" % (i, handle))

    err = _nl_connect(handle, NETLINK_ROUTE)
    if err:
        print("test handle[%d]: connect handle failed: %d, port=%s" % (i, err, s_nl_socket_get_local_port(_nl_socket_get_local_port(handle))))
        if not parallel_handles:
            print("no more handles to release. Release test handles...")
            parallel_handles = test_handles;

        h = parallel_handles.pop(random.randrange(len(parallel_handles)))
        ppp = _nl_socket_get_local_port(h)
        print("test handle[%d]: release handle %d with port %s" % (i, h, s_nl_socket_get_local_port(ppp)))
        _nl_socket_free(h)

        _nl_socket_set_local_port(handle, 0);
        err = _nl_connect(handle, NETLINK_ROUTE)
        if err:
            raise IOError(-err, _nl_geterror(err))
    print("test handle[%d]: %r - connected to port %s" % (i, handle, s_nl_socket_get_local_port(_nl_socket_get_local_port(handle))))
    test_handles.append(handle)


