Assign sendto address to C raw socket?

I am sending some ping packets through a raw socket in C on my linux machine.

int sock_fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); 

This means that I specify the IP packet header when writing to the socket ( IP_HDRINCL implied).

Writing to the socket with send fails, telling me that I need to specify an address.

If I use sendto then it works. For sendto I have to specify the sockaddr_in structure, which includes the fields sin_family , sin_port and sin_addr .

However, I noticed a few things:

  • sin_family - AF_INET - which was already specified when creating the socket.
  • sin_port naturally not used (ports are not a concept for IP).
  • It does not matter which address I use if it is an external address (the IP packet indicates 8.8.8.8, and sin_addr indicates 1.1.1.1).

It seems that no additional fields in sendto actually used to a large extent. So, is there a technical reason why I should use sendto instead of send or is it just oversight in the API?

+5
source share
2 answers

Writing to the socket with send fails, telling me that I need to specify an address.

It does not work because the send() function can only be used for connected sockets (as indicated here ). Usually you use send() to communicate over TCP (connection-oriented) and sendto() can be used to send UDP datagrams (without establishing a connection).

Since you want to send ping packets, or rather ICMP datagrams that are not explicitly installed, you must use the sendto() function.

It seems that no additional fields in sendto actually used to a large extent. So, is there a technical reason why I should use sendto instead of send or is it just oversight in the API?

Short answer:

If you are not allowed to use send() , then only one option remains, called sendto() .

Long answer:

This is not just oversight in the API. If you want to send a UDP datagram using a regular socket (for example, SOCK_DGRAM ), sendto() needs information about the destination address and port that you specified in struct sockaddr_in , right? The kernel inserts this information into the resulting IP header, as struct sockaddr_in is the only place you specify who will be the recipient. Or in other words: in this case, the kernel should get the destination information from your structure, since you are not providing an additional IP header.

Since sendto() used not only for UDP, but also for raw sockets, it should be a more or less "universal" function that can cover all the different use cases, even if some parameters, such as the port number, are not related to / used at the end.

For example, using IPPROTO_RAW (which automatically implies IP_HDRINCL ), you indicate that you want to create an IP header yourself. So the last two arguments to sendto() are actually redundant information, as they are already included in the data buffer that you pass to sendto() as the second argument. Note that even if you use IP_HDRINCL with your raw socket, the kernel will fill in the source address and checksum of your IP datagram if you set the corresponding fields to 0 .

If you want to write your own ping program, you can also change the last argument in your socket() function from IPPROTO_RAW to IPPROTO_ICMP and let the kernel create an IP header for you, so you have one less thing to worry about. Now you can easily see how the two sendto() parameters *dest_addr and addrlen become significant again, because this is the only place you specify the destination address.

The language and APIs are very old and have grown over time. Some APIs may look strange from today's point of view, but you cannot change old interfaces without breaking a huge amount of existing code. Sometimes you just need to get used to things that were defined / developed many years or decades ago.

Hope that answers your question.

+6
source

The send() call is used when the sockets are in the TCP connection state SOCK_STREAM .

On the man page:

the send () call can only be used when the socket is plugged in (so that the intended receiver is known).

Since your application obviously does not connect to another socket, we cannot expect send() to work.

+1
source

All Articles