From 9c5438da65bba198bba2a2a127f3f6448dcb56c5 Mon Sep 17 00:00:00 2001
From: Jacek Kowalski <Jacek@jacekk.info>
Date: Sun, 03 May 2026 14:12:59 +0000
Subject: [PATCH] NetworkDriver: fix return type annotation in create_interface

---
 lib/NetworkDriver.py |  130 +++++++++++++++++++++++++++----------------
 1 files changed, 81 insertions(+), 49 deletions(-)

diff --git a/lib/NetworkDriver.py b/lib/NetworkDriver.py
index 6e42b70..306172d 100644
--- a/lib/NetworkDriver.py
+++ b/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 {}
 
 

--
Gitblit v1.10.0