From 9f3c9ce503a1f983ee8fffeb6f1eaf571b976ca0 Mon Sep 17 00:00:00 2001
From: Jacek Kowalski <Jacek@jacekk.info>
Date: Fri, 13 Mar 2026 09:19:04 +0000
Subject: [PATCH] Move gw4/gw6 options to network level (with optional overrides)
---
lib/NetworkDriver.py | 130 +++++++++++++++++++++++++++----------------
1 files changed, 81 insertions(+), 49 deletions(-)
diff --git a/lib/NetworkDriver.py b/lib/NetworkDriver.py
index 6e42b70..478414b 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) -> 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