pyveth - veth driver for Docker Engine written in Python
Jacek Kowalski
yesterday 5687c6c38c48c23d03073b45e065aeb9b36873ae
lib/NetworkDriver.py
@@ -15,11 +15,63 @@
    return ''.join([random.choice(chars) for _ in range(size)])
def create_interface(endpoint, network) -> tuple[str, str]:
    ifname0 = 'veth{}'.format(genid())
    ifname1 = 'veth{}'.format(genid())
    with pyroute2.IPRoute() as ip:
        ip.link('add', ifname=ifname0, peer=ifname1, kind='veth')
        idx = ip.link_lookup(ifname=ifname0)[0]
        if endpoint.Interface.MacAddress:
            ip.link('set', index=idx, address=endpoint.Interface.MacAddress)
        ip.link('set', index=idx, state='up')
        if endpoint.Interface.Address:
            addr = ipaddress.ip_interface(endpoint.Interface.Address)
            ip.addr('add', index=idx, address=addr.ip.compressed, mask=addr.network.prefixlen)
        if endpoint.Interface.AddressIPv6:
            addr = ipaddress.ip_interface(endpoint.Interface.AddressIPv6)
            ip.addr('add', index=idx, address=addr.ip.compressed, mask=addr.network.prefixlen)
        endpoint.Interface.Name = ifname0
        idx = ip.link_lookup(ifname=ifname1)[0]
        ip.link('set', index=idx, state='up')
        if 'parent' in network.Options:
            id_parent = ip.link_lookup(ifname=network.Options['parent'])[0]
            ip.link("set", index=idx, master=id_parent)
        endpoint.Interface.Peer = ifname1
    return ifname0, ifname1
def delete_interface(interface):
    try:
        with pyroute2.IPRoute() as ip:
            idx = ip.link_lookup(ifname=interface)[0]
            ip.link("delete", index=idx)
    except:
        pass
@app.route('/NetworkDriver.GetCapabilities', methods=['POST'])
def GetCapabilities():
    return {
        'Scope': 'local',
        'ConnectivityScope': 'global',
        'GwAllocChecker': True,
    }
@app.route('/NetworkDriver.GwAllocCheck', methods=['POST'])
def GwAllocCheck():
    request = GwAllocCheckEntity(**flask.request.get_json(force=True))
    skip_ipv4 = skip_ipv6 = request.Options.get('com.docker.network.generic', {}).get('nogw') == '1'
    if request.Options.get('com.docker.network.generic', {}).get('nogw4') == '1':
        skip_ipv4 = True
    if request.Options.get('com.docker.network.generic', {}).get('nogw6') == '1':
        skip_ipv6 = True
    return {
        'SkipIPv4': skip_ipv4,
        'SkipIPv6': skip_ipv6,
    }
@@ -33,20 +85,22 @@
    except KeyError:
        pass
    networks[network.NetworkID] = network
    networks_sync()
    return {}
@app.route('/NetworkDriver.DeleteNetwork', methods=['POST'])
def DeleteNetwork():
    network = NetworkDeleteEntity(**flask.request.get_json(force=True))
    del networks[network.NetworkID]
    if network.NetworkID in networks:
        del networks[network.NetworkID]
        networks_sync()
    return {}
@app.route('/NetworkDriver.CreateEndpoint', methods=['POST'])
def CreateEndpoint():
    endpoint = EndpointCreateEntity(**flask.request.get_json(force=True))
    endpoints['{}-{}'.format(endpoint.NetworkID, endpoint.EndpointID)] = endpoint
    return {
        'Interface': {
@@ -66,19 +120,9 @@
@app.route('/NetworkDriver.DeleteEndpoint', methods=['POST'])
def DeleteEndpoint():
    entity = EndpointDeleteEntity(**flask.request.get_json(force=True))
    endpoint = endpoints['{}-{}'.format(entity.NetworkID, entity.EndpointID)]
    try:
        with pyroute2.IPRoute() as ip:
            iface = ip.link_lookup(ifname=endpoint.Interface.Peer)[0]
            if iface:
                ip.link('del', index=iface)
        del endpoint.Interface.Name
        del endpoint.Interface.Peer
    except AttributeError:
        pass
    del endpoints['{}-{}'.format(endpoint.NetworkID, endpoint.EndpointID)]
    endpoint_id = '{}-{}'.format(entity.NetworkID, entity.EndpointID)
    if endpoint_id in endpoints:
        del endpoints['{}-{}'.format(entity.NetworkID, entity.EndpointID)]
    return {}
@@ -88,28 +132,10 @@
    network = networks[join.NetworkID]
    endpoint = endpoints['{}-{}'.format(join.NetworkID, join.EndpointID)]
    ifname0 = 'veth{}'.format(genid())
    ifname1 = 'veth{}'.format(genid())
    with pyroute2.IPRoute() as ip:
        ip.link('add', ifname=ifname0, peer=ifname1, kind='veth')
        idx = ip.link_lookup(ifname=ifname0)[0]
        if endpoint.Interface.MacAddress:
            ip.link('set', index=idx, address=endpoint.Interface.MacAddress)
        ip.link('set', index=idx, state='up')
        if endpoint.Interface.Address:
            addr = ipaddress.ip_interface(endpoint.Interface.Address)
            ip.addr('add', index=idx, address=addr.ip.compressed, mask=addr.network.prefixlen)
        if endpoint.Interface.AddressIPv6:
            addr = ipaddress.ip_interface(endpoint.Interface.AddressIPv6)
            ip.addr('add', index=idx, address=addr.ip.compressed, mask=addr.network.prefixlen)
        endpoint.Interface.Name = ifname0
    interface, interface_external = create_interface(endpoint, network)
        idx = ip.link_lookup(ifname=ifname1)[0]
        ip.link('set', index=idx, state='up')
        if 'parent' in network.Options:
            id_parent = ip.link_lookup(ifname=network.Options['parent'])[0]
            print(ip.link("set", index=idx, master=id_parent))
        endpoint.Interface.Peer = ifname1
    endpoint.internal_interface_name = interface
    endpoint.external_interface_name = interface_external
    gw4 = None
    for net4 in network.IPv4:
@@ -128,32 +154,38 @@
    result = {
        'InterfaceName': {
            'SrcName': ifname0,
            'SrcName': interface,
            'DstPrefix': 'eth',
        },
        'StaticRoutes': [],
    }
    if gw4 is not None:
        result['Gateway'] = gw4.ip.compressed
    if gw6 is not None:
        result['GatewayIPv6'] = gw6.ip.compressed
    gw4 = endpoint.Options.get("gw4", network.Options.get("gw4", None))
    if gw4 is not None:
        result['StaticRoutes'].append({
            'Destination': gw4 + '/32',
            'RouteType': 1,
        })
        result['Gateway'] = gw4
    gw6 = endpoint.Options.get("gw6", network.Options.get("gw6", None))
    if gw6 is not None:
        result['StaticRoutes'].append({
            'Destination': gw6 + '/128',
            'RouteType': 1,
        })
        result['GatewayIPv6'] = gw6
    return result
@app.route('/NetworkDriver.Leave', methods=['POST'])
def Leave():
    leave = LeaveEntity(**flask.request.get_json(force=True))
    endpoint = endpoints['{}-{}'.format(leave.NetworkID, leave.EndpointID)]
    try:
        with pyroute2.IPRoute() as ip:
            iface = ip.link_lookup(ifname=endpoint.Interface.Peer)[0]
            if iface:
                ip.link('del', index=iface)
        del endpoint.Interface.Name
        del endpoint.Interface.Peer
    except AttributeError:
        pass
    endpoint = endpoints.get('{}-{}'.format(leave.NetworkID, leave.EndpointID), None)
    if endpoint is not None and endpoint.external_interface_name:
        delete_interface(endpoint.external_interface_name)
    return {}