How to tell which interface the socket received the message from? - networking

If a socket is bound to IN6ADDR_ANY or INADDR_ANY and you use a call such as recvfrom() to receive messages on the socket. Is there a way to find out which interface the message came from?
In the case of IPv6 link-scope messages, I was hoping that the from argument of recvfrom() would have the scope_id field initialized to the interface Id. Unfortunately it is set to 0 in my test program.
Anybody know of a way to find out this information?

dwc is right, IPV6_PKTINFO will work for IPv6 on Linux.
Moreover, IP_PKTINFO will work for IPv4 — you can see details in manpage ip(7)

I've constructed an example that extracts the source, destination and interface addresses. For brevity, no error checking is provided. See this duplicate: Get destination address of a received UDP packet.
// sock is bound AF_INET socket, usually SOCK_DGRAM
// include struct in_pktinfo in the message "ancilliary" control data
setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt));
// the control data is dumped here
char cmbuf[0x100];
// the remote/source sockaddr is put here
struct sockaddr_in peeraddr;
// if you want access to the data you need to init the msg_iovec fields
struct msghdr mh = {
.msg_name = &peeraddr,
.msg_namelen = sizeof(peeraddr),
.msg_control = cmbuf,
.msg_controllen = sizeof(cmbuf),
};
recvmsg(sock, &mh, 0);
for ( // iterate through all the control headers
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&mh);
cmsg != NULL;
cmsg = CMSG_NXTHDR(&mh, cmsg))
{
// ignore the control headers that don't match what we want
if (cmsg->cmsg_level != IPPROTO_IP ||
cmsg->cmsg_type != IP_PKTINFO)
{
continue;
}
struct in_pktinfo *pi = CMSG_DATA(cmsg);
// at this point, peeraddr is the source sockaddr
// pi->ipi_spec_dst is the destination in_addr
// pi->ipi_addr is the receiving interface in_addr
}

Apart from binding to each interface, I'm not aware of a way with IPv4, per se.
IPv6 has added the IPV6_PKTINFO socket option to address this shortcoming. With that option in effect, a struct in6_pktinfo will be returned as ancillary data.

Its been a while since I've been doing C/C++ TCP/IP coding but as far as I remember on every message (or derived socket) you can get into the IP headers information. These headers should include the receiving address which will be the IP of the interface you are asking about.

Outside of opening a separate socket on each interface as Glomek suggested, the only way I know to do this definitively on Windows is to use a raw socket, e.g.,
SOCKET s = socket(AF_INET, SOCK_RAW, IPPROTO_IP);
Each receive from this socket will be an IP packet, which contains both the source and destination addresses. The program I work on requires me to put the socket in promiscuous mode using the SIO_RCVALL option. Doing this means I get every IP packet the interface "sees" on the network. To extract packets expressly for my application requires me to filter the data using the addresses and ports in the IP and TCP/UDP headers. Obviously, that's probably more overhead than you're interested in. I only mention it to say this - I've never used a raw socket without putting it in promiscuous mode. So I'm not sure if you can bind it to INADDR_ANY and just use it as a regular socket from that point forward or not. It would seem to me that you can; I've just never tried it.
EDIT: Read this article for limitations regarding raw sockets on Windows. This biggest hurdle I faced on my project was that one has to be a member of the Administrators group to open a raw socket on Windows 2000 and later.

Related

Ada GNAT.Sockets send on multiple ethernet adaptors?

I have a machine with 4 Ethernet Interfaces (ensf1s1, ensf1s2, ensf1s3, ensf1f4) and using GNAT.Sockets I need to be able to send/recieve data over each interface.
The code I am using is
Create_Socket(SendFrom1, Family_Inet, Socket_Datagram);
Create_Socket(SendFrom2, Family_Inet, Socket_Datagram);
...
Bind_Socket(SendFrom1, SendFrom1Address);
Bind_Socket(SendFrom2, SendFrom2Address);
...
Channel1 := Stream(SendFrom1, SendToAddress1);
Channel2 := Stream(SendFrom2, SendToAddress2);
...
With IP addresses configured as 192.168.1.(101/102/103/104) I am getting all messages sent over a single interface with the correctly specified Source and Destination IPs in the packet.
I read in another question that having multiple NICs on the same subnet could cause a problem to some OS's so I changed to 192.168.1.101, 192.168.2.102 etc with a Subnet mask of 255.255.0.0. Using the same code with Addresses corrected this only sent data intended for the interface which previously sent all messages but nothing on the other 3.
Have I missed something in my Socket configuration to ensure a Socket is binded to the adaptor with the SendFromAddress specified? The OS is RHEL 7 if that's relevant.
Your question is related to how sockets are working.
If you bind your socket to a specific address, you will receive packets only for that destination address.
To receive packets from any of your four interfaces, you may bind to the INADDR_ANY address. You will do this as follows:
Address : GNAT.Sockets.Sock_Addr_Type;
SendFromAll : GNAT.Sockets.Socket_Type;
...
Address.Port := 0; -- Or whatever fixed port you like
Address.Addr := GNAT.Sockets.Any_Inet_Addr;
GNAT.Sockets.Bind_Socket (SendFromAll, Address);
Using this implementation, the SendFromAll socket will receive data from any interface. With Receive_Socket, you can get the sender address. Then when you send data back to the client using the SendFromAll socket, the system will pick an interface depending on the destination address and the network routing tables. On Linux, it will depend on the routing policy (ip rule) and on the routing tables (ip route).
Client : GNAT.Sockets.Sock_Addr_Type;
Buffer : Ada.Streams.Stream_Element_Array (0 .. 8192);
Last : Ada.Streams.Stream_Element_Offset;
....
GNAT.Sockets.Receive_Socket (SendFromAll, Buffer, Last, Client);
GNAT.Sockets.Send_Socket (SendFromAll, Buffer (Buffer'First .. Last), Last, Client);
Now if you really need to bind a socket to an interface, you must get the IP address of that interface. If you have several interfaces, you have to get their own IP addresses. There is no easy way with GNAT.Sockets to do this. You can use the Get_Host_By_Name function but you must setup different names for each interface (otherwise you'll get the same IP for each socket).
Another way which is not possible with GNAT.Sockets is to use the SO_BINDTODEVICE socket option. You bind the socket to the interface name (you don't need to get the IP).
What may happen to you is that you are using the same IP address for each Bind_Socket call.

does dev_queue_xmit depends on dst_entry in skb

I am working on a network module and need to send out packet to specific destinations (think as software router).
I want to send a sk_buff to some IP address, passing it to dev_queue_xmit().
However, I am feeling complex with dst_entry concept.
Can dev_queue_xmit send a skb, without a valid dst_entry, when the skb has necessary link layer info ?
After some testing, I eventually concluded that skb need not have a valid dst_entry for successfully transmitting a packet. All that it needs is a valid buffer, with skb->data pointing to correct mac_header location.

Recover port from Boost Asio udp::endpoint

I am programming a server and client program to communicate between a windows PC using the Boost libraries and a Linux ARM beagleboard using the asio stand alone libraries. I have for a while had successful UDP communication between the two devices but now I want to recover the port from the endpoint the server discovers when the client connects. The way the client connects is via query:
udp::resolver resolver(io_service);
udp::resolver::query query_tx(udp::v4(), hostIP, "43210");
udp::endpoint receiver_endpoint_tx = *resolver.resolve(query_tx);
where host IP is a string and this works fine. Upon debugging though I notice that when i check the value returned by:
receiver_endpoint_tx.port()
This returns 51880. Now don't jump the guns and yell out network byte order and host byte order. I AM AWARE. The strange part is that this number 51880 sometimes is a different number and when i check what the server has stored in its endpoint it is a completely different number: 21743. Now I know I must be doing something wrong with the byte orders but i tried:
//unsigned long port_long = boost::asio::detail::socket_ops::host_to_network_long(receiver_endpoint_tx.port());
//unsigned long port_short = boost::asio::detail::socket_ops::host_to_network_short(receiver_endpoint_tx.port());
And they do not give me back my original port: 43210. Neither does network to host. So what am i missing and how can I on both ends recover my 43210 port? Obviously it must be there somewhere because they are successfully communicating.
Thanks in advance, sorry if noob mistake :)
Fistly, UDP is connectionless, there is no connection.
I'm not sure if I understand you correctly, but it sound too me like you want to bind to specific port numbers. If you want the client to send a packet from port x to port y on the server, and the server should respond from port y to port x, then you need to bind the sockets to the desired ports. Alternatively you can use the constructor to bind. Not doing so will result in the OS using ephemeral ports.
Further, to get the remote endpoint that a packet was received from the async_receive_from takes the sender_endpoint reference parameter. When the read handler is called, you can retrieve host and port from it.

Building DHCP packet ( DHCPDISCOVER ) , packet structure

I'm trying to build DHCP packet in then send it via UDP ( "0xff.0xff.0xff.0xff", 67 port ).
I have sucessfully connected to my DHCP server, but I have problems with first packet structure ( DHCPDISCOVER ) as I see, but I have built it from RFC && Wiki, and have checked all fields/size in bytes of these fields.
Here is the code in C# ( don't argue , this code is only for testing purpose to check the work of DHCP protocal and structures of packet ): http://pastebin.com/9NXuHyrw
I have initialized the body of discover dhcp-packet in class and you can check the struct of it ( size, right fields ).
So, what's wrong?
Thanks,
Best Regards
Using a BinaryFormatter will not give you what you are expecting. It will produce a sequence of bytes in an unspecified format. The only guarantee you have about that sequence of bytes is that you can deserialize it into an object that resembles the original. It is highly likely it doesn't look at all like a DHCP packet.
To get an array of bytes with the correct packet structure you will have to use a BinaryWriter over a MemoryStream and write each field manually.

Qt Network Broadcasting Question: To get sender IP

There is a simple example about network broadcasting in QtSDK. It is easy to send and receive broadcast information. And in the receiver side, I want to know whose broadcast packet I just received. I try QUdpSocket.peerName() in readyRead signal callback function,but I get empty string. Any clue?
You can retrieve the sender's IP address when reading the packet with QUdpSocket::readDatagram(). You need to pass to readDatagram() a pointer to a QHostAddress in which to store the address:
QHostAddress senderAddress;
yourSocket->readDatagram(&data, size, &senderAddress);
// senderAddress now represents the sender address
You can get the address as a QString or integer from senderAddress if you need to, see the documentation for QHostAddress.
If you want a host name, you can use QHostInfo to do a reverse lookup (but you are not guaranteed to get a name).
From the docs:
Returns the name of the peer as specified by connectToHost(), or an empty QString if connectToHost() has not been called.
So, if you've called connectToHost() you should get a result. You might also try peerAddress(). Unless you always connect via a name, I wouldn't expect that you'll always be able to do a reverse lookup and get a name.

Resources