Commit 8d3dcc3b authored by Dimitris Aragiorgis's avatar Dimitris Aragiorgis
Browse files

Fix bug that causes nfdhcpd to freeze

nfdhcpd opens a socket during init. socket.send() blocks in case
SO_SNDBUF is full. This might happen when packages are pushed to
buffer but never consumed (e.g. VM is shuting down).

To fix this we use non-blocking send with MSG_DONTWAIT and catch
the error when the resource is not available.

In order to empty the socket buffer we close the socket and re-open it.
To this end we need CAP_NET_RAW capability otherwise operation
(socket.socket()) is not permitted.

Add various logging messages (during client creation, opening a
socket, etc.)
Signed-off-by: default avatarDimitris Aragiorgis <>
parent 0cca7143
......@@ -199,11 +199,12 @@ def parse_binding_file(path):
eui64 = get_value(line)
client = Client(tap=tap, mac=mac, ip=ip,
hostname=hostname, indev=indev, subnet=subnet,
gateway=gateway, subnet6=subnet6, gateway6=gateway6, eui64=eui64 )
return client
return Client(tap=tap, mac=mac, ip=ip, hostname=hostname,
indev=indev, subnet=subnet, gateway=gateway,
subnet6=subnet6, gateway6=gateway6, eui64=eui64 )
except ValueError:
logging.warning("Cannot add client for host %s and IP %s on tap %s",
hostname, ip, tap)
return None
......@@ -255,8 +256,9 @@ class Subnet(object):
if isinstance(net, str):
try: = IPy.IP(net)
raise Exception
except ValueError, e:
logging.warning("IPy error: %s", e)
raise e
else: = net = gw
......@@ -356,9 +358,7 @@ class VMNetProxy(object): # pylint: disable=R0902
#self.ifaces = {}
#self.v6nets = {}
self.nfq = {}
self.l2socket = socket.socket(socket.AF_PACKET,
self.l2socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 0)
self.l2socket = self._socket()
# Inotify setup
self.wm = pyinotify.WatchManager()
......@@ -380,6 +380,15 @@ class VMNetProxy(object): # pylint: disable=R0902
self._setup_nfqueue(ns_queue_num, AF_INET6, self.ns_response)
self.ipv6_enabled = True
def _socket(self):"Opening L2 socket")
s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, ETH_P_ALL)
s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 0)
except socket.error, e:
logging.warning("Cannot open socket %s", e)
return s
def _cleanup(self):
""" Free all resources for a graceful exit
......@@ -414,13 +423,22 @@ class VMNetProxy(object): # pylint: disable=R0902
""" Send a raw packet using a layer-2 socket
logging.debug("Sending raw packet %s", data)
if isinstance(data, BasePacket):
data = str(data)
logging.debug("Sending raw packet %r", data)
self.l2socket.bind((dev, ETH_P_ALL))
count = self.l2socket.send(data)
count = self.l2socket.send(data, socket.MSG_DONTWAIT)
except socket.error, e:
logging.warn("Send with MSG_DONTWAIT failed: %s", str(e))
self.l2socket = self._socket()
raise e
ldata = len(data)
logging.debug("Sent %d bytes to device %s", count, dev)
if count != ldata:
logging.warn("Truncated send on %s (%d/%d bytes sent)",
dev, count, ldata)
......@@ -1017,10 +1035,11 @@ if __name__ == "__main__":
# Keep only the capabilities we need
# CAP_NET_ADMIN: we need to send nfqueue packet verdicts to a netlinkgroup
# CAP_NET_RAW: we need to reopen socket in case the buffer gets full
capng.CAP_NET_ADMIN | capng.CAP_NET_RAW)
capng.capng_change_id(uid.pw_uid, uid.pw_gid,
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment