Receive broadcast packets from IP 255.255.255.255 in ruby - ruby

I am currently struggling with receiving broadcast packets from the IP address 255.255.255.255 in ruby.
In general my network configuration has two different vlans:
vlan10: ip 10.10.10.1 netmask 255.255.0.0 => broadcast address 10.10.255.255
vlan20: ip 10.0.0.1 netmask 255.0.0.0 => broadcast address 10.255.255.255
As receiver I use the following test code to dispay the incoming packets:
require 'socket'
addr = ['0.0.0.0', 3020]
BasicSocket.do_not_reverse_lookup = true
# Create socket and bind to address
UDPSock = UDPSocket.new
UDPSock.bind(addr[0], addr[1])
while true
data, addr = UDPSock.recvfrom(1024)
puts "From addr: '%s', msg: '%s'" % [addr[0], data]
end
UDPSock.close
Receiving packets from 10.255.255.255 and 10.10.255.255 works fine.
Packets sent to IP 255.255.255.255 are not received.
Do I need to set additional properties to make ruby receive "limited broadcast" packets?
I hope somebody can help. I am really lost.
Thanks, Uwe

Thanks for your help. During checking the router configuration I noticed that I had a wrong vlan configuration that result in this behavior.

Related

How can a socket connect when bound to a specific ethernet interface that's also being used by VPN / utun interface?

I'm trying to write a function that can connect to a server using a specific network interface so that it's consistently routed through that interface's gateway. This is on a macOS system that has one or more VPN connections.
Here's a proof-of-concept test function I've written:
void connectionTest(const char *hostname, int portNumber, const char *interface) {
struct hostent *serverHostname = gethostbyname(hostname);
if (serverHostname == NULL) {
printf("error: no such host\n");
return;
}
int socketDesc = socket(AF_INET, SOCK_STREAM, 0);
int interfaceIndex = if_nametoindex(interface);
if (interfaceIndex == 0) {
printf("Error: no such interface\n");
close(socketDesc);
return;
}
// Set the socket to specifically use the specified interface:
setsockopt(socketDesc, IPPROTO_IP, IP_BOUND_IF, &interfaceIndex, sizeof(interfaceIndex));
struct sockaddr_in servAddr;
bzero((char *)&servAddr, sizeof(servAddr));
servAddr.sin_family = AF_INET;
bcopy((char *)serverHostname->h_addr, (char *)&servAddr.sin_addr.s_addr, serverHostname->h_length);
servAddr.sin_port = htons(portNumber);
if (connect(socketDesc, (struct sockaddr *)&servAddr, sizeof(servAddr)) < 0) {
printf("connect failed, errno: %d", errno);
close(socketDesc);
return;
}
printf("connection succeeded\n");
close(socketDesc);
}
This function will successfully connect so long as the interface is one of the utun interfaces created by the VPNs, or a physical interface that is not used by the VPNs. But if I try to use the physical interface that is used by the VPNs, the function fails with errno 51: Network is unreachable.
For a more specific example, consider a system with the following network interfaces:
en0: Ethernet connection
en1: Wi-Fi connection
utun10: VPN connection 1, connected via en0
utun11: VPN connection 2, also connected via en0
If I call my function with something like:
connectionTest("api.ipify.org", 80, "en1");
connectionTest("api.ipify.org", 80, "utun10");
connectionTest("api.ipify.org", 80, "utun11");
... it will succeed. However, this is what produces the "network unreachable" error:
connectionTest("api.ipify.org", 80, "en0");
Is there some way to have the function work in the case of en0? (Preferably without changing the system's routing table just for this one connection?)
Edit:
It looks like the system doesn't know how to route packets through en0 when the VPN is up, unless it has a non-default route for en0.
I tried using the route command to check which route in the table would be used for a specific interface, and I get the following:
$ route get -ifscope en0 1.1.1.1
route: writing to routing socket: not in table
Only -ifscope en0 produces that error. However, the route table indicates there is a default route for en0. Here is the routing table when only ethernet and the VPN are connected (so no Wi-Fi or second VPN):
$ netstat -rn
Routing tables
Internet:
Destination Gateway Flags Refs Use Netif Expire
0/1 10.16.0.1 UGSc 165 0 utun10
default 192.168.20.1 UGSc 0 0 en0
10.16/16 10.16.0.8 UGSc 3 0 utun10
10.16.0.8 10.16.0.8 UH 2 0 utun10
127 127.0.0.1 UCS 0 0 lo0
127.0.0.1 127.0.0.1 UH 7 7108160 lo0
128.0/1 10.16.0.1 UGSc 40 0 utun10
169.254 link#8 UCS 1 0 en0 !
192.168.20 link#8 UCS 9 0 en0 !
192.168.20.1/32 link#8 UCS 2 0 en0 !
224.0.0/4 link#22 UmCS 0 0 utun10
224.0.0/4 link#8 UmCSI 1 0 en0 !
224.0.0.251 1:0:5e:0:0:fb UHmLWI 0 0 en0
255.255.255.255/32 link#22 UCS 0 0 utun10
255.255.255.255/32 link#8 UCSI 0 0 en0 !
There's clearly a default route listed for en0 pointing to its gateway, 192.168.20.1. Why isn't the packet being routed? If I create a static route for 1.1.1.1/32 or even 1/8 it will work. But so long as en0 only has a default route, it won't work. It's like the default route has been disabled somehow.
Edit 2:
If I add a new route to the table using:
$ route add -ifscope en0 0/0 192.168.20.1
so that the routing table now includes the following entry:
Destination Gateway Flags Refs Use Netif Expire
default 192.168.20.1 UGScI 1 0 en0
alongside all of the above entries, so there are now two default entries, then the connection works. Why is it necessary for there to be an interface-specific default route in order for this to work?
Once you added the routing table to your question, your problem became obvious.
It is the routing table that determines to which gateway a packet is sent. The routing table tells the sending host to which gateway the packet is sent. It does that by comparing the destination address to the routes in the routing table. The most-specific (longest match) route is used. A default route is the least-specific (shortest match) route, and it is used as the route of last resort when there are no more-specific routes in the routing table.
Based on the routing table you provided, any packet with a destination address from 1.0.0.0 to 126.255.255.255 (0.0.0.0/8 and 127.0.0.0/8 are exceptions as unusable ranges) will match the 0/1 routing table entry rather than the default route (0/0), and any packet with a destination address from 128.0.0.0 to 223.255.255.255 (224.0.0.0/4 is multicast, and 240.0.0.0/4 is unusable) will match the 128/1 routing table entry rather than the default route (0/0), because the route length of 1 is more specific than the default route length of 0. That means any packets destined to an address in those ranges (combined, all addresses destined for a different network) will be sent to the gateway (10.16.0.1) referenced by the routing table entries for the 0/1 and 128/1 routes.
To solve your problem, you need to remove the 0/1 and 128/1 routing table entries and replace them with one or more entries that are restricted to the networks which the tunnel can reach. With that, the entries not matching the tunnel route(s) or other more specific routing table entries will use the default route.

Parse TCP Packet and return response PHP or Ruby

I am trying to gather information from a TCP connection made to my web server to assist in our troubleshooting efforts, similar to http://speedguide.net/analyzer.php tool.
We have a simple PHP server script test page that users connect that returns their private IP to an AJAX call waiting for the response.
I would like to either build on that or prefer using Ruby. I played with the PacketFu lib and get all the information I think I need, however, I'm having trouble with the recipe to combine it all:
listen on port x,
parse the packet
respond back to client.
Using Ruby's TCPServer I can easily handle 1 and 3. With Packetfu, 2.
I've coded with PHP in the past but only HTML-based, no sockets. And I'm not really all that familiar with Ruby sockets either.
Though the packet stream and client.accept don't seem to play nice. The packets aren't always IP or TCP meeting the Packetfu::Packet.is_ip? or is_tcp?.
Could someone point me in the right direction or give me some practical example of how I might combine the two, or adjust my thinking on how I would accomplish this task?
This is the Playground code:
require 'socket'
require 'json'
require 'packetfu'
iface = ARGV[0] || "eno1"
server = TCPServer.open(31337)
cap = PacketFu::Capture.new(:iface => iface, :start => true, :promisc => true)
loop {
cap.stream.each do |p|
pkt = PacketFu::Packet.parse(p)
if pkt.is_ip? || pkt.is_tcp?
if pkt.tcp_dport == 31337
print "Source Addr: #{pkt.ip_saddr}\n"
print "Source Port: #{pkt.tcp_src}\n"
print "Destination Addr: #{pkt.ip_daddr}\n"
print "Destination Port: #{pkt.tcp_dport}\n"
print "TCP Options: #{pkt.tcp_options.inspect}\n"
print "TCP Win: #{pkt.tcp_win}\n"
print "TCP SYN?: #{pkt.tcp_flags.syn}\n"
print "TCP ACK?: #{pkt.tcp_flags.ack}\n"
print "TCP FLAGS ALL: #{pkt.tcp_flags.inspect}\n"
print "TTL: #{pkt.ip_ttl}\n"
print "IP FRAG: #{pkt.ip_frag}\n"
end
end
client = server.accept # Wait for a client to connect
h = { ipaddress: client.peeraddr[2] }
client.puts h.to_json
client.close
end
}
This is the output:
Source Addr: 172.20.0.15
Source Port: 41165
Destination Addr: 172.20.0.10
Destination Port: 31337
TCP Options: "NOP,NOP,TS:216432150;57946250"
TCP Win: 229
TCP SYN?: 0
TCP ACK?: 1
TCP FLAGS ALL: #<struct PacketFu::TcpFlags urg=0, ack=1, psh=0, rst=0, syn=0, fin=0>
TTL: 61
IP FRAG: 16384
This is the browser response:
{"ipaddress":"172.20.0.15"}

Doesn't get Access_accept packet from freeradius server

1.From client:
root#amsys-LIFEBOOK-AH502:/home/amsys# radtest -t chap usr password 127.0.0.1 0 testing123
This is how,the way i sended a packet access-request packet from the client (here,loop back only).
2.From server.
the server responds to client as shown as below:
Ready to process requests.
Ignoring request to auth address * port 1812 as server default from unknown client 127.0.0.1 port 34962 proto udp
3.server to client
Sending Access-Request of id 67 from 0.0.0.0 port 47852 to 127.0.0.1 port 1812
User-Name = 'usr'
User-Password = 'password'
NAS-IP-Address = 127.0.1.1
NAS-Port = 0
Message-Authenticator = 0x00
radclient: no response from server for ID 67 socket 3
if anybody would aware about this thing,please give your prompt response and pleased me.thanking you.!

Ruby, get incoming address from UDP message

I have a UDP server that binds to all addresses on a system, I would like to know what ip address the message was addressed to. Any ideas how to do this?
Here is my example code:
sock = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, 0)
sock.bind(Addrinfo.udp('', 2400))
while(true)
sockset = IO.select([sock])
sockset[0].each do |sock|
data = sock.recvfrom(1024)
puts "data: " + data.inspect
end
end
sock.close
This will produce something like:
data: ["test message\n", #<Addrinfo: 172.16.5.110:41949 UDP>]
Am I able to set a socket option, or something, to return the local IP?
Just a note, this needs to work for IPv6 too. Thanks in advance, Dave.
UNIX Network Programming has this to say about this very subject:
With a UDP socket, however, the destination IP address can only be obtained by setting the IP_RECVDSTADDR socket option for IPv4 or the IPV6_PKTINFO socket option for IPv6 and then calling recvmsg instead of recvfrom.
Ruby’s socket library has recvmsg which is a bit easier to use than the underlying C function, but still needs a bit of work to get the info needed. The destination IP address is included in the array of ancillary data returned from recvmsg. Here’s a version of your code adapted to use recvmsg and get the destination address for IPv4:
require 'socket'
require 'ipaddr'
sock = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, 0)
# Set the required socket option:
sock.setsockopt :IPPROTO_IP, :IP_RECVDSTADDR, true
sock.bind(Addrinfo.udp('0.0.0.0', 2400))
while(true)
sockset = IO.select([sock])
sockset[0].each do |sock|
mesg, sender, _, *anc_data = sock.recvmsg
# Find the relevant data and extract it into a string
dest = IPAddr.ntop(anc_data.find {|d| d.cmsg_is?(:IP, :RECVDSTADDR)}.data)
puts "Data: #{mesg}, Sender: #{sender.ip_address}, Destination: #{dest}"
end
end
And here is a version for IPv6. There is also a RECVPKTINFO socket option, which I think may have superseded PKTINFO – depending on your system you may need to use that instead.
require 'socket'
sock = Socket.new(Socket::AF_INET6, Socket::SOCK_DGRAM, 0)
# Set the socket option for IP6:
sock.setsockopt :IPPROTO_IPV6, :IPV6_PKTINFO, true
sock.bind(Addrinfo.udp('0::0', 2400))
while(true)
sockset = IO.select([sock])
sockset[0].each do |sock|
mesg, sender, _, *anc_data = sock.recvmsg
# Find and extract the destination address
dest = anc_data.find {|d| d.cmsg_is?(:IPV6, :PKTINFO)}.ipv6_pktinfo_addr
puts "Data: #{mesg}, Sender: #{sender.ip_address}, Destination: #{dest.ip_address}"
end
end
Ruby also provides a Socket.udp_server_loop method, which yields the message and a UDPSource object to the block you provide, and this source object has a local_address field. Looking at the source this appears to check the PKTINFO data like I do above to get the destination address for IPv6 requests, but not for IPv4. This method binds to all available IP addresses individually, and just uses the address of the incoming interface for IP4 requests, which may not be accurate for a weak end system model. However it might be simpler for you to use Socket.udp_server_loop.

IPv6 with perl on windows not working

cross-post http://perlmonks.org/index.pl?node_id=984750
(Possible duplicate of perl windows IPv6 )
I tried following sample example from : https://metacpan.org/module/IO::Socket::IP
use IO::Socket::IP -register;
my $sock = IO::Socket->new(
Domain => PF_INET6,
LocalHost => "::1",
Listen => 1,
) or die "Cannot create socket - $#\n";
print "Created a socket of type " . ref($sock) . "\n";
It is giving output as :
Cannot create socket - no address associated with nodename
I am using ActiveState perl 5.14.2 and have built IO::Socket::IP module on it.
Following is the ping result:
c:\>ping ::1
Pinging ::1 with 32 bytes of data:
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Reply from ::1: time<1ms
Ping statistics for ::1:
Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 0ms, Maximum = 0ms, Average = 0ms
If I use the IPv4 style loopback address 127.0.0.1, the above code works well.
I am wondering what I am missing.
Update:
I just cleaned the perl setup and path, and freshly installed perl 5.14.2 from
http://www.activestate.com/activeperl/downloads
and then I tried following simple code:
use strict;
use warnings;
use Socket qw(getaddrinfo SOCK_STREAM AI_PASSIVE );
my ( $err, #res ) = getaddrinfo( "::", 8086, {
socktype => SOCK_STREAM,
flags => AI_PASSIVE,
} );
die $err if $err;
it ended with following error:
no address associated with nodename at c:\IPv6.pl line 10.
But with 127.0.0.1 it returns proper value.
I am using windows 2008 R2 box, a same run on my another windows box also fails.
I just tried to trace this call in Socket.pm, and found that a "fake_getaddrinfo" is getting called instead of the real getaddrinfo. It seems the XSLoader was either not able to find/load getaddrinfo from Socket.dll or Socket.dll didn't at all have the getaddrinfo.
What could be the reason?
A similar code below using Socket6 works properly on the same setup:
use Socket;
use Socket6;
#res = getaddrinfo('::', 8086, AF_UNSPEC, SOCK_STREAM);
while(scalar(#res)>=5){
($family, $socktype, $proto, $saddr, $canonname, #res) = #res;
($host, $port) = getnameinfo($saddr, NI_NUMERICHOST | NI_NUMERICSERV);
print ("\nhost= $host port = $port");
socket(Socket_Handle, $family, $socktype, $proto) || next;
bind(Socket_Handle,$saddr ) || die "bind: $!";
listen(Socket_Handle, 5) || die "listen: $!";
($host, $port) = getnameinfo($saddr, NI_NUMERICHOST | NI_NUMERICSERV);
print ("\nReady for connections \nhost= $host port = $port");
$paddr = accept(Client, Socket_Handle);
}
So I can't even blame the setup or the system dlls. Is there an issue with perl's built-in IPv6 support for windows' activestate build?
As stated by vinsworldcom on perlmonks.org, to make use of IPv6 sockets you do need to have the Socket6 module installed. As soon as you'd install this via cpan, the code snippet will work fine.

Resources