Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Purpose of sendto address for C raw socket?

I'm sending some ping packets via 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 I write to the socket (IP_HDRINCL is implied).

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

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

However, I have noticed a few things:

  • The sin_family is AF_INET - which was already specified when the socket was created.
  • The sin_port is naturally unused (ports are not a concept for IP).
  • It doesn't matter what address I use, so long as it is an external address (the IP packet specifies 8.8.8.8 and the sin_addr specifies 1.1.1.1).

It seems none of the extra fields in sendto are actually used to great extent. So, is there a technical reason why I have to use sendto instead of send or is it just an oversight in the API?

like image 208
William Avatar asked Sep 25 '16 03:09

William


People also ask

What is Sendto function in C?

The sendto function is normally used on a connectionless socket to send a datagram to a specific peer socket identified by the to parameter. Even if the connectionless socket has been previously connected to a specific address, the to parameter overrides the destination address for that particular datagram only.

What this function sendto () does?

The sendto() function sends data on the socket with descriptor socket. The sendto() call applies to either connected or unconnected sockets. The socket descriptor. The pointer to the buffer containing the message to transmit.

What is Sendto?

Description. The system calls send(), sendto(), and sendmsg() are used to transmit a message to another socket. The send() call may be used only when the socket is in a connected state (so that the intended recipient is known). The only difference between send() and write(2) is the presence of flags.

What is the purpose of raw socket?

The raw socket interface provides direct access to lower layer protocols, such as the Internet Protocol (IP) and Internet Control Message Protocol (ICMP or ICMPv6). You can use raw sockets to test new protocol implementations.


2 Answers

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

It fails, because the send() function can only be used on connected sockets (as stated here). Usually you would use send() for TCP communication (connection-oriented) and sendto() can be used to send UDP datagrams (connectionless).

Since you want to send "ping" packets, or more correctly ICMP datagrams, which are clearly connectionless, you have to use the sendto() function.

It seems none of the extra fields in sendto are actually used to great extent. So, is there a technical reason why I have to use sendto instead of send or is it just an oversight in the API?

Short answer:

When you are not allowed to use send(), then there is only one option left, called sendto().

Long answer:

It is not just an oversight in the API. If you want to send a UDP datagram by using an ordinary socket (e.g. SOCK_DGRAM), sendto() needs the information about the destination address and port, which you provided in the struct sockaddr_in, right? The kernel will insert that information into the resulting IP header, since the struct sockaddr_in is the only place where you specified who the receiver will be. Or in other words: in this case the kernel has to take the destination info from your struct as you don't provide an additional IP header.

Because sendto() is not only used for UDP but also raw sockets, it has to be a more or less "generic" function which can cover all the different use cases, even when some parameters like the port number are not relevant/used in the end.

For instance, by using IPPROTO_RAW (which automatically implies IP_HDRINCL), you show your intention that you want to create the IP header on your own. Thus the last two arguments of sendto() are actually redundant information, because they're already included in the data buffer you pass to sendto() as the second argument. Note that, even when 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 could also change the last argument in your socket() function from IPPROTO_RAW to IPPROTO_ICMP and let the kernel create the IP header for you, so you have one thing less to worry about. Now you can easily see how the two sendto()-parameters *dest_addr and addrlen become significant again because it's the only place where you provide a destination address.

The language and APIs are very old and have grown over time. Some APIs can look weird from todays perspective but you can't change the old interfaces without breaking a huge amount of existing code. Sometimes you just have to get used to things that were defined/designed many years or decades ago.

Hope that answers your question.

like image 55
reflective_mind Avatar answered Sep 29 '22 05:09

reflective_mind


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

From the man page:

the send() call may be used only when the socket is in a connected state (so that the intended recipient is known).

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

like image 30
iceeggocean Avatar answered Sep 29 '22 05:09

iceeggocean