Why am I not receiving UDP Packets sent to my own IP? - ruby

I've written this ruby program to send a UDP Packet to port 16800:
require 'socket'
sock = UDPSocket.new
sock.bind("", 47123)
sock.send("Hello!", 0, "192.168.0.100", 16800)
sock.close
where 192.168.0.100 is my own ip address. However, the packet doesn't show in Wireshark and my UDP server listening on port 16800 on the same PC (running Linux) isn't receiving anything.
When I slightly rewrite the program to
require 'socket'
sock = UDPSocket.new
sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, true)
sock.bind("", 47123)
sock.send("Hello!", 0, "<broadcast>", 16800)
sock.close
the packet does show up in Wireshark, but my server still isn't receiving anything.
Am I missing something obvious?
EDIT:
The server code, for completeness
require 'socket'
sock = UDPSocket.new
sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, true)
sock.bind(nil, 16800)
while true
packet = sock.recvfrom(1024)
puts packet
end
However, this was copied somewhere from the internet, and while editing it for stackoverflow, i found out that the server always receives packets sent to 127.0.0.1 but when I change nil to "", the server suddenly also receives the packets sent above. Is this a bug in the ruby socket implementation?

I just used your very code and the problem is you're not binding to a specific IP address. I don't know about Linux but on OS X there's an issue where sometimes the default will attach to an unused IP6 address rather than the interface you were expecting.
Changing "" to 192.168.1.255 on the server, and including "192.168.1.255" on the bind in the client made all this work for me :)

UDPSocket.BIND takes a host and a port. Try changing the nil to your IP address. From http://coderrr.wordpress.com/2008/05/28/get-your-local-ip-address/; use
require 'socket'
def local_ip
orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true # turn off reverse DNS resolution temporarily
UDPSocket.open do |s|
s.connect '64.233.187.99', 1
s.addr.last
end
ensure
Socket.do_not_reverse_lookup = orig
end
to get your IP

Have you tried sending to "localhost" or "127.0.0.1"? This will send directly to the local computer and will help diagnose the problem further.

Kind of speculating here, but you might want to consider something like this - when you use the computer's own IP, the packets aren't visible on the physical link b/c the tcp/ip stack doesn't have to push them that far to get them where they are going, it can turn them around internally to the computer at one of the higher layers in that seven-layer stack model (the 'transport' layer, perhaps?)

If such a connection is supposed to work, and you see the packages using Wireshark, then you should also check the software firewall on your machine. If it blocks traffic to the UDP port you want to use you can still see the package using Wireshark!

Related

Ruby - UDP - How to spoof IP and Port Number

I've been messing about with some sample client and server code for sending and receiving UDP packets, here's what I'm doing for the send
require 'socket'
s = UDPSocket.new
s.send("hello", 0, 'localhost', 1234)
Is it possible to somehow use a fake IP address and port number when sending? haven't come across anything online with any examples of how to do so.
You need to bind the socket before calling send:
require 'socket'
s = UDPSocket.new
s.bind('128.100.8.6', 1253)
s.send(...)
Keep in mind that your upstream is in no way responsible for delivering "spoofed" packets and that many providers will dump these if they're not originating in their network.

Windows/Python UDP Broadcast Blocked No Firewall

OS: Windows 10
I use an Ethernet switch to send UDP packets to two other systems (connected directly to that same switch) simultaneously via Python 3.4.4. The same code works on two other dev/testing PC's so I know it's not the Python code, but for some reason it doesn't work on the PC that I want the system to be utilized by.
When I use Wireshark to view the UDP traffic at 169.254.255.255 (the target IP for sending the UDP packets to), nothing appears. However, sending packets to 169.X.X.1 works. On the other hand, packets sent to 169.X.X.255 are sent, but I receive time-to-live exceeded messages in return. I am restricted to that target IP, so changing the IP is not a solution. I also have it sending on port 6000 (arbitrary), I have tried changing the port number to no avail. Also won't let me send to 169.254.255.1
I have the firewalls turned off.
Thanks for your help.
same problem here,
solution is bind socket to adapter which is sending multicast
example:
msg = \
'M-SEARCH * HTTP/1.1\r\n' \
'HOST:239.255.255.250:1900\r\n' \
'ST:upnp:rootdevice\r\n' \
'MX:2\r\n' \
'MAN:"ssdp:discover"\r\n' \
'USER-AGENT:Python\r\n\r\n'
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
s.bind(('ip of interface',1901))
s.settimeout(10)
print msg,len(msg)
print s.sendto(msg, ('239.255.255.250', 1900) )
resp, (addr, port) = s.recvfrom(1024)
print resp
s.close()
solution from this post Can't send multicast over non-default NIC
The strange thing about my problem, was that this exact code worked on the computer in question (and two development computers) previously, but wasn't working at the time that I posted this question.
Wireshark wasn't leading me to my answer (only showing me that the UDP packets were not sent), so I decided to ping the IP via the command prompt. I received one of two errors (destination host unreachable, or request timed out). These errors led me to adding my desired target IP (169.254.255.255) to the ARP cache, which my problem was solved.
I'd like to thank you for suggesting a possible solution.

Multicast packet seen in Wireshark, but not received by user program (Windows and Linux)

I'm trying to detect a network connected device using multicast from a Ruby program. I've sniffed the network traffic from the official detection program and found that the computer sends out a packet to 225.0.0.37:12345 and the device responds on 225.0.0.38:12346. The device is connected to a local network consisting of a Netgear switch, a Windows computer and a Linux computer. I've made my program to send out an identical packet as the official program does.
The problem I have is that the response is never received, neither by my program nor the official detection program. Using Wireshark I can clearly see the device responding without fail and the packet clearly makes it to my Linux computer which I run Wireshark on, but neither the official program running on the Windows computer, nor my own Ruby program running on the Linux computer gets the response.
Both my computers have two network adapters, one connected to the same local switch and one connected to another switch which is then connected up to a large LAN with internet access. I've tried using a different brand switch between the computers, but it made no difference.
I've searched far and wide on Internet and haven't found anything to solve my issue. I appreciate any help I can get.
Here are the relevant packets captured with tcpdump, the first is sent from my program and the second is the response from the device:
13:30:25.773019 IP 192.168.0.1.12345 > 225.0.0.37.12345: UDP, length 15
13:30:25.773770 IP 192.168.0.125.39129 > 225.0.0.38.12346: UDP, length 228
Here is a (simplified) snippet from my program:
MULTICAST_SEND_ADDRESS = '225.0.0.37'
MULTICAST_SEND_PORT = 12345
MULTICAST_RESPONSE_ADDRESS = '225.0.0.38'
MULTICAST_RESPONSE_PORT = 12346
BIND_ADDRESS = '0.0.0.0'
# Transmit packet on all adapters - this part works fine
packet = [ID_STRING].pack("Z*")
addresses = Socket.ip_address_list
addresses.each do |addr|
next unless addr.pfamily == Socket::PF_INET
socket = UDPSocket.new
socket.bind(addr.ip_address, MULTICAST_SEND_PORT)
socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, true)
socket.send(packet, 0, MULTICAST_SEND_ADDRESS, MULTICAST_SEND_PORT)
socket.close
end
# Receive packet - this should work, but doesn't
membership = IPAddr.new(MULTICAST_SEND_ADDRESS).hton + IPAddr.new(BIND_ADDRESS).hton
listen_socket = UDPSocket.new
listen_socket.setsockopt(Socket::IPPROTO_IP, Socket::IP_ADD_MEMBERSHIP, membership)
listen_socket.bind(BIND_ADDRESS, MULTICAST_RESPONSE_PORT)
packet, addr = listen_socket.recvfrom(1024)
# We never get to this line since there's nothing received
puts packet
I figured out the problem in the end. In spite of what all the code examples I've seen say, it seems you can't bind to address '0.0.0.0' to get all interfaces on a multihomed machine. I don't know if this used to work and has been changed or all examples I've found have only been tested on singlehomed machines, but when I bound the socket to the actual ip of the correct interface it started working.

How to get other computer to connect to my localhost server?

I recently started learning Ruby Sockets and decided to research the topic. I came across the ruby-doc which had some example code that ran smoothly:
This is the example code for the server:
require 'socket'
server = TCPServer.new 2000 # Server bound to port 2000
loop do
client = server.accept # Wait for a client to connect
client.puts "Hello !"
client.puts "Time is #{Time.now}"
client.close
end
And the example code for the client:
require 'socket'
s = TCPSocket.new 'localhost', 2000
while line = s.gets # Read lines from socket
puts line # and print them
end
s.close # close socket when done
So this ran well but I was wondering how I would get the client to connect if it is running from a different computer. So I attempted to replace the "'localhost'" in the client code with my public IP address courtesy of whatismyip.com, however, when I tried running the new client code on a different computer I merely got a timeout error. I even attempted running the new client code on the same machine running the server but still I got a timeout error.
Does anyone know how I can get this to work properly?
Any help would be much appreciated!
Greg Hewgill helped me figure this out:
My first problem was that I was using the wrong address. Greg suggested I check my actual address through the cmd command "ipconfig". The command gave me the actual address that the server was being hosted on. Through this I changed the "'localhost'" in the client code and changed it to the actual IP address. Upon running, I received an error that stated that the server had actively refused the connection. This was fixed by also changing the 'localhost' in the server code to the IP address of the server's machine.
Thank you Greg for the help!

ruby cannot assign requested address

I had already created a qt program that listens on a specific port on my server. And it works fine. Now I want to create a simple ruby program to do the same. Right now I just have a simple test server using netcat which establishes a network socket and accepts UDP data (this is Ubuntu server by the way):
$ sudo nc -l 1720
Now I am just trying to listen on the port in Ruby:
# network.rb
require 'socket'
socket = UDPSocket.new
socket.bind('64.xxx.xx.xxx', 1720)
This right away raises this exception:
network.rb:4:in `bind': Cannot assign requested address - bind(2) (Errno::EADDRNOTAVAIL)
WHy is it saying the address is not available? All that is there is a netcat socket. The goal is that I will have UDP data coming in from GPRS devices to that port, and then I will have ruby sitting on my ubuntu server listening for that data, then decoding it, and storing it into a postgresql database.
You are making a server or client?
you used nc, so i guess you are making client.
server is bind
client is connect:
c = UDPSocket.new
c.connect("127.0.0.1", 1111)
"address is not available" usually as the port is used.
or you can ping the address fisrt to check if the address can be reached
I was getting the same error by running:
rails s -b 10.0.0.61
It turns out that my local IP wasn't 10.0.0.61 and this was causing the error.

Resources