how to resolve ip route in ruby - ruby

Say a multi-horned PC has two network interface with IP address 192.168.1.100/24 and 192.168.2.100/24. If any other PC send a multicast message to any of them, I need to send a response back, but in the UDP data part of the response, I need to tell that PC my proper IP address which they can reach.
Say 192.168.1.123 send that multicast message, I can use ip route get to 192.168.1.123 to determine which one is the proper interface to use(which is 192.168.1.100), but I don't know how to do this in ruby code.
To invoke external process then parse the stdout is not what I want, cause it's too slow for a busy network, and to make it cross-platform is not that easy.
Currently I mimic the route resolving with these codes:
localifs = Socket.ip_address_list.keep_if { |addr| addr.ipv4? and not(addr.ipv4_loopback?) }
raise Error, "There is no alive ipv4 interface on local machine" if localifs.empty?
src_addr_i = IPAddr.new(src_addr).to_i
localifs = localifs.sort do |x,y|
(IPAddr.new(x.ip_address).to_i ^ src_addr_i) <=> (IPAddr.new(y.ip_address).to_i ^ src_addr_i)
end
host = localifs.first.ip_address

Related

Obtain local IP address on receiving UDP

I would like to obtain local IP address on receiving UDP packets.
packetConn, _ := net.ListenPacket("udp", ":12345")
n, remoteAddr, _ := packetConn.ReadFrom(buf)
My server has secondary IP addresses and I would like to know to which address each client sends UDP packets.
packetConn.ReadFrom can get remote IP address, but I would like to get the local IP address of the UDP.
I tried packetConn.LocalAddr(), but this returns only local port number like :12345.
How can I get local IP address of UDP packet?
LocalAddr effectively returns the address value passed to ListenPacket. If this is not specific enough for you, you'll need to create multiple listeners, one for each IP address on your server.

How to pick up IP packets and inject on different VM and interface

I have been trying to solve these 2 problems, but without success.
I wonder if it's possible to remove specific packets from an interface with Gopacket or is it just for listening on the wire? For example when I send a UDP packet to a wrong port and then with Gopacket I correct it, it will send 2 packets, 1 to the wrong port and 1 to the correct one. Is there a way to discard/drop the wrong packet with Gopacket?
What I am trying to do, is to pick up all packets that are sent by a client over IP and then encapsulate each packet as a payload in another protocol X and send to the remote host which will receive on protocol X, get the payload and send it on its interface to reach the server over IP again. (IP (Client) -> Protocol X (Sniffer 1) -> Protocol X (Sniffer 2) -> IP (Server))
I have verified that the packet which Sniffer 1 picks up from the Client's interface is the same which arrives at Sniffer 2, but the problem is when Sniffer 2 injects it on the Server's interface. I can't see that packet with tcpdump or any other tool. The packet is injected with this command:
if handle, err := pcap.OpenLive("enp0s8", 1600, true, 100); err != nil {
panic(err)
} else {
err = handle.WritePacketData(packet.Data())
}
If the Protocol X part is avoided, then the server will receive messages from client, but with Protocol X it is not possible.
Thanks in advance!
According to the Documentation
Package pcap allows users of gopacket to read packets off the wire or
from pcap files.
To discard packages, you will need to be able to intercept them. Depending on how generic you want to solve this problem, you probably need to hook into the kernel. I recommend looking into iptables and netfilters.
I found some VPN that are written in go, maybe look into how they are built, as you want to do something similar (tunnelling of packets).

Boost::asio UDP Broadcast with ephemeral port

I'm having trouble with udp broadcast transactions under boost::asio, related to the following code snippet. Since I'm trying to broadcast in this instance, so deviceIP = "255.255.255.255". devicePort is a specified management port for my device. I want to use an ephemeral local port, so I would prefer if at all possible not to have to socket.bind() after the connection, and the code supports this for unicast by setting localPort = 0.
boost::asio::ip::address_v4 targetIP = boost::asio::ip::address_v4::from_string(deviceIP);
m_targetEndPoint = boost::asio::ip::udp::endpoint(targetIP, devicePort);
m_ioServicePtr = boost::shared_ptr<boost::asio::io_service>(new boost::asio::io_service);
m_socketPtr = boost::shared_ptr<boost::asio::ip::udp::socket>(new boost::asio::ip::udp::socket(*m_ioServicePtr));
m_socketPtr->open(m_targetEndPoint.protocol());
m_socketPtr->set_option(boost::asio::socket_base::broadcast(true));
// If no local port is specified, default parameter is 0
// If local port is specified, bind to that port.
if(localPort != 0)
{
boost::asio::ip::udp::endpoint localEndpoint(boost::asio::ip::address_v4::any(), localPort);
m_socketPtr->bind(localEndpoint);
}
if(m_forceConnect)
m_socketPtr->connect(m_targetEndPoint);
this->AsyncReceive(); // Register Asynch Recieve callback and buffer
m_socketThread = boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&MyNetworkBase::RunSocketThread, this))); // Start thread running io_service process
No matter what I do in terms of the following settings, the transmit is working fine, and I can use Wireshark to see the response packets coming back from the device as expected. These response packets are also broadcasts, as the device may be on a different subnet to the pc searching for it.
The issues are extremely strange to my mind, but are as follows:
If I specify the local port and set m_forceConnect=false, everything works fine, and my recieve callback fires appropriately.
If I set m_forceConnect = true in the constructor, but pass in a local port of 0, the transmit works fine, but my receive callback never fires. I would assume this is because the 'target' (m_targetEndpoint) is 255.255.255.255, and since the device has a real IP, the response packet gets filtered out.
(what I actually want) If m_forceConnect = false (and data is transmitted using a send_to call), and local port = 0, therefore taking an ephemeral port, my RX callback immediately fires with an error code 10022, which I believe is an "Invalid Argument" socket error.
Can anyone suggest why I can't use the connection in this manner (not explicitly bound and not explicitly connected)? I obviously don't want to use socket.connect() in this case, as I want to respond to anything I receive. I also don't want to use a predefined port, as I want the user to be able to construct multiple copies of this object without port conflicts.
As some people may have noticed, the overall aim of this is to use the same network-interface base-class to handle both the unicast and broadcast cases. Obviously for the unicast version, I can perfectly happily m_socket->connect() as I know the device's IP, and I receive the responses since they're from the connected IP address, therefore I set m_forceConnect = true, and it all just works.
As all my transmits use send_to, I have also tried to socket.connect(endpoint(ip::addressv4::any(), devicePort), but I get a 'The requested address is not valid in its context' exception when I try it.
I've tried a pretty serious hack:
boost::asio::ip::udp::endpoint localEndpoint(boost::asio::ip::address_v4::any(), m_socketPtr->local_endpoint().port());
m_socketPtr->bind(localEndpoint);
where I extract the initial ephemeral port number and attempt to bind to it, but funnily enough that throws an Invalid Argument exception when I try and bind.
OK, I found a solution to this issue. Under linux it's not necessary, but under windows I discovered that if you are neither binding nor connecting, you must have transmitted something before you make the call to asynch_recieve_from(), the call to which is included within my this->asynch_receive() method.
My solution, make a dummy transmission of an empty string immediately before making the asynch_receive call under windows, so the modified code becomes:
m_socketPtr->set_option(boost::asio::socket_base::broadcast(true));
// If no local port is specified, default parameter is 0
// If local port is specified, bind to that port.
if(localPort != 0)
{
boost::asio::ip::udp::endpoint localEndpoint(boost::asio::ip::address_v4::any(), localPort);
m_socketPtr->bind(localEndpoint);
}
if(m_forceConnect)
m_socketPtr->connect(m_targetEndPoint);
// A dummy TX is required for the socket to acquire the local port properly under windoze
// Transmitting an empty string works fine for this, but the TX must take place BEFORE the first call to Asynch_receive_from(...)
#ifdef WIN32
m_socketPtr->send_to(boost::asio::buffer("", 0), m_targetEndPoint);
#endif
this->AsyncReceive(); // Register Asynch Recieve callback and buffer
m_socketThread = boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&MyNetworkBase::RunSocketThread, this)));
It's a bit of a hack in my book, but it is a lot better than implementing all the requirements to defer the call to the asynch recieve until after the first transmission.

Sending UDP Packet to NTP to get a packet back (in ruby)

What I need to do:
Send a packet to pool.ntp.org (and I am assuming I get a packet back automatically).
The following just does not work for me. I have no solid idea of what I am doing, so for now I would be satisfied if I could reach the address and get the packet back. The code below just hangs. Any and all help would be appreciated.
require 'socket'
sock = UDPSocket.new
sock.connect("pool.ntp.org", 123)
sock.recvfrom(10)
The documentation on UDPSocket states:
connect(host, port)
Connects udpsocket to host:port.
This makes possible to send without destination address.
That means that you may use send(mesg, flags) form of the send, nothing more. You need to send a request message to the NTP server to get a reply
You can see the NTPv4 protocol specification here: RFC 5905

how do I get the IP of incoming ICMP due to UDP-send to dead client in Ruby?

so.. I'm doing a small multiplayer game with blocking UDP and IO.select. To my problem.. (In the server) reading from a UDP socket (packet, sender = #socket.recvfrom(1000)) which have just sent a packet to a dead client results in a ICMP unreachable (and exception Errno::ECONNRESET in ruby). The problem is that I can't find any way whatsoever to extract the IP of that ICMP.. so I can clean out that dead client.
Anyone know how to achieve this?
thanks
You'll need to call recvmsg for the socket, and pass MSG_ERRQUEUE as the flag.
The original destination address of the datagram that caused the error is supplied via msg_name.
It's worth noting that the source IP address of the ICMP packet will not always be the same address as your client. Any router that handles packets for this connection could be the source, and the payload of the ICMP packet would contain the IP header + the first 8 bytes of the packet it relates to.

Resources