From cac6e7698b7beea11b807384dd00aaa0bed3c5b4 Mon Sep 17 00:00:00 2001 From: nick Date: Tue, 15 Apr 2014 15:10:39 +0100 Subject: [PATCH] Fix a multiple-sockets-in-one-process issue. "pid" is not "process id" in netlink, but rather, "port id". If you bind to a sockaddr == 0 then Linux automatically assigns your socket a port id - which happens to be the same as the process ID for the first one concurrently open. For the second and subsequent concurrently-open sockets, binding 0 (as most users of this library will do) gets you back a random high-numbered port id. This change preserves the existing use case (one port open in the process, binding to 0) while fixing multiple-ports-open-in-the-same-process, socket-is- passed-in-and-pid-is-not-specified, and specific-pid-is-requested-but-could- not-bind-to-it. We're probably still not thread-safe - the seq handling looks dodgy - but at least now we can use multiple sockets in separate threads and have them all work. Using the same socket from multiple threads is a slightly niche use case, and it's tempting to say "don't do this" instead... --- lib/linux/netlink/nlsocket.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/linux/netlink/nlsocket.rb b/lib/linux/netlink/nlsocket.rb index 5f6ba45..8cdf9ea 100644 --- a/lib/linux/netlink/nlsocket.rb +++ b/lib/linux/netlink/nlsocket.rb @@ -58,14 +58,16 @@ module Netlink # :timeout => N (seconds, default to DEFAULT_TIMEOUT. Pass nil for no timeout) # :junk_handler => lambda { ... } for unexpected packets def initialize(opt) - @socket ||= opt[:socket] || ::Socket.new( + @socket = opt[:socket] || ::Socket.new( Socket::AF_NETLINK, Socket::SOCK_DGRAM, opt[:protocol] || (raise "Missing :protocol") ) @socket.bind(NLSocket.sockaddr(opt)) unless opt[:socket] @seq = opt[:seq] || Time.now.to_i - @pid = opt[:pid] || $$ + + @pid = @socket.getsockname.unpack(SOCKADDR_PACK)[2] + @timeout = opt.has_key?(:timeout) ? opt[:timeout] : DEFAULT_TIMEOUT if opt.has_key?(:junk_handler) @junk_handler = opt[:junk_handler] @@ -75,7 +77,7 @@ module Netlink } end end - + # Close the Netlink socket def close @socket.close @@ -85,7 +87,7 @@ module Netlink def next_seq @seq = (@seq + 1) & 0xffffffff end - + # Add a header and send a single message over the socket. # type:: the message type code # msg:: the message to send (without header) @@ -203,7 +205,7 @@ module Netlink end end end - + # Receive one datagram from kernel. Validates the sender, and returns # the raw binary message. Raises an exception on timeout or if the # kernel closes the socket.