mirror of
https://github.com/devplayer0/docker-net-dhcp
synced 2024-11-16 21:28:01 +00:00
Fix container DHCP without gateway
This commit is contained in:
parent
9887cfab30
commit
ab8336a5cb
@ -3,6 +3,7 @@ import ipaddress
|
||||
import logging
|
||||
import atexit
|
||||
import socket
|
||||
import time
|
||||
import threading
|
||||
|
||||
import pyroute2
|
||||
@ -61,12 +62,31 @@ def endpoint_container_iface(n, e):
|
||||
if info['EndpointID'] == e:
|
||||
container = client.containers.get(cid)
|
||||
netns = f'/proc/{container.attrs["State"]["Pid"]}/ns/net'
|
||||
ndb.sources.add(netns=netns)
|
||||
|
||||
already = False
|
||||
for source in ndb.sources:
|
||||
if source == netns:
|
||||
already = True
|
||||
break
|
||||
if not already:
|
||||
ndb.sources.add(netns=netns)
|
||||
|
||||
for i in ndb.interfaces:
|
||||
if i['address'] == info['MacAddress']:
|
||||
return i
|
||||
break
|
||||
return None
|
||||
def await_endpoint_container_iface(n, e, timeout=5):
|
||||
start = time.time()
|
||||
iface = None
|
||||
while time.time() - start < timeout:
|
||||
try:
|
||||
iface = endpoint_container_iface(n, e)
|
||||
except docker.errors.NotFound:
|
||||
time.sleep(0.5)
|
||||
if not iface:
|
||||
raise NetDhcpError('Timed out waiting for container to become availabile')
|
||||
return iface
|
||||
|
||||
@app.route('/NetworkDriver.GetCapabilities', methods=['POST'])
|
||||
def net_get_capabilities():
|
||||
@ -202,6 +222,7 @@ def delete_endpoint():
|
||||
@app.route('/NetworkDriver.Join', methods=['POST'])
|
||||
def join():
|
||||
req = request.get_json(force=True)
|
||||
network = req['NetworkID']
|
||||
endpoint = req['EndpointID']
|
||||
|
||||
bridge = net_bridge(req['NetworkID'])
|
||||
@ -220,7 +241,7 @@ def join():
|
||||
res['Gateway'] = str(gateway)
|
||||
del gateway_hints[endpoint]
|
||||
|
||||
ipv6 = ipv6_enabled(req['NetworkID'])
|
||||
ipv6 = ipv6_enabled(network)
|
||||
for route in bridge.routes:
|
||||
if route['type'] != rtypes['RTN_UNICAST'] or \
|
||||
(route['family'] == socket.AF_INET6 and not ipv6):
|
||||
@ -242,24 +263,34 @@ def join():
|
||||
'NextHop': route['gateway']
|
||||
})
|
||||
|
||||
container_dhcp_clients[endpoint] = ContainerDHCPManager(network, endpoint)
|
||||
return jsonify(res)
|
||||
|
||||
@app.route('/NetworkDriver.Leave', methods=['POST'])
|
||||
def leave():
|
||||
req = request.get_json(force=True)
|
||||
endpoint = req['EndpointID']
|
||||
|
||||
if endpoint in container_dhcp_clients:
|
||||
container_dhcp_clients[endpoint].stop()
|
||||
del container_dhcp_clients[endpoint]
|
||||
|
||||
return jsonify({})
|
||||
|
||||
# Trying to grab the container's attributes (to get the network namespace)
|
||||
# will deadlock, so we must defer starting the DHCP client
|
||||
# will deadlock (since Docker is waiting on us), so we must defer starting
|
||||
# the DHCP client
|
||||
class ContainerDHCPManager:
|
||||
def __init__(self, network, endpoint):
|
||||
self.network = network
|
||||
self.endpoint = endpoint
|
||||
self.ipv6 = ipv6_enabled(network)
|
||||
|
||||
self.dhcp = None
|
||||
self._thread = threading.Thread(target=self.run)
|
||||
self._thread.start()
|
||||
|
||||
def _on_event(self, dhcp, event_type, _args):
|
||||
def _on_event(self, dhcp, event_type, _event):
|
||||
if event_type != udhcpc.EventType.RENEW:
|
||||
return
|
||||
|
||||
@ -281,36 +312,20 @@ class ContainerDHCPManager:
|
||||
.commit())
|
||||
|
||||
def run(self):
|
||||
iface = endpoint_container_iface(self.network, self.endpoint)
|
||||
self.dhcp = udhcpc.DHCPClient(iface)
|
||||
logger.info('Starting DHCP client on %s in container namespace %s', iface['ifname'], \
|
||||
self.dhcp.netns)
|
||||
try:
|
||||
iface = await_endpoint_container_iface(self.network, self.endpoint)
|
||||
self.dhcp = udhcpc.DHCPClient(iface, event_listener=self._on_event)
|
||||
logger.info('Starting DHCP client on %s in container namespace %s', iface['ifname'], \
|
||||
self.dhcp.netns)
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
|
||||
def stop(self):
|
||||
if not self.dhcp:
|
||||
return
|
||||
|
||||
logger.info('Shutting down DHCP client on %s in container namespace %s', \
|
||||
self.dhcp.iface['ifname'], self.dhcp.netns)
|
||||
self.dhcp.finish(timeout=1)
|
||||
ndb.sources.remove(self.dhcp.netns)
|
||||
self._thread.join()
|
||||
|
||||
# ProgramExternalActivity is supposed to be used for port forwarding etc.,
|
||||
# but we can use it to start the DHCP client in the container's network namespace
|
||||
# since the interface will have been moved inside at this point.
|
||||
@app.route('/NetworkDriver.ProgramExternalConnectivity', methods=['POST'])
|
||||
def start_container_dhcp():
|
||||
req = request.get_json(force=True)
|
||||
endpoint = req['EndpointID']
|
||||
container_dhcp_clients[endpoint] = ContainerDHCPManager(req['NetworkID'], endpoint)
|
||||
|
||||
return jsonify({})
|
||||
|
||||
@app.route('/NetworkDriver.RevokeExternalConnectivity', methods=['POST'])
|
||||
def stop_container_dhcp():
|
||||
req = request.get_json(force=True)
|
||||
endpoint = req['EndpointID']
|
||||
|
||||
if endpoint in container_dhcp_clients:
|
||||
container_dhcp_clients[endpoint].stop()
|
||||
del container_dhcp_clients[endpoint]
|
||||
|
||||
return jsonify({})
|
||||
|
Loading…
Reference in New Issue
Block a user