Live data from Hacker News

Rootless Pings in Rust

bou.ke

51–60 of 87 posts

Re: Rootless Pings in Rust

#51
post #48
post #25

> It turns out you can create a UDP socket with a protocol flag, which allows you to send the ping rootless This is wrong, despite the Rust library in question's naming convention. You're not creating a UDP socket. You're creating an IP (AF_INET), datagram socket (SOCK_DGRAM), using protocol ICMP (IPPROTO_ICMP). The issue is that the rust library apparently conflates datagram and UDP, when they're not the same thing.…

Could you please explain me the difference? As UDP is the "User Datagram Protocol" when I read about datagrams I always think about UDP and though it was just a different way of saying the same thing. Maybe "datagram" is supposed to be the packet itself, but you're still sending it via UDP, right?

Think of it as a protocol for sending custom ("user") datagrams. There are other datagram protocols too.

The kernel API defaults to UDP when you pass 0 as the protocol for a datagram socket, which may be the source of the confusion.

Re: Rootless Pings in Rust

#52
post #44

Earlier quoted context omitted.

This is in the standard library; it's not a learning project. And it also isn't even incorrect - see erk__'s comment. Rust is an excellent language and fully capable of production use.

It's not, it's the `socket2` library. The standard sockets don't allow (ab)using actual `UdpSocket`s as a different kind of datagram socket.

We're talking about UdpSocket: https://doc.rust-lang.org/stable/std/net/struct.UdpSocket.ht...

Re: Rootless Pings in Rust

#53
post #48
post #25

> It turns out you can create a UDP socket with a protocol flag, which allows you to send the ping rootless This is wrong, despite the Rust library in question's naming convention. You're not creating a UDP socket. You're creating an IP (AF_INET), datagram socket (SOCK_DGRAM), using protocol ICMP (IPPROTO_ICMP). The issue is that the rust library apparently conflates datagram and UDP, when they're not the same thing.…

Could you please explain me the difference? As UDP is the "User Datagram Protocol" when I read about datagrams I always think about UDP and though it was just a different way of saying the same thing. Maybe "datagram" is supposed to be the packet itself, but you're still sending it via UDP, right?

As the other commenter pointed out, UDP is transport protocol, not a packet level protocol.

Think of it like this:

Ethernet sends data frames to a MAC address. It has a sender and a receiver address. See here for structure: https://en.wikipedia.org/wiki/Ethernet_frame

Internet Protocols (v6 and v4) send packets via Ethernet (or WiFi or Bluetooth or anything else) from an IP address to an IP address. For structure see https://en.wikipedia.org/wiki/IPv6_packet or if for some reason you still need the legacy version see https://en.wikipedia.org/wiki/IPv4#Packet_structure (aside but notice how much complexity was removed from the legacy version). Notably, IP does not have any mechanism for reliability. It is essentially writing your address and a destination address on a brick and tossing over your fence to the neighbor’s yard and asking them to pass it along. If your neighbor isn’t home your brick is not moving along.

TCP and UDP send streams and datagrams respectively and use the concept of application ports. A TCP stream is what it sounds like: a continuous stream of bytes with no length or predefined stopping point. TCP takes your stream and chunks it into IP packets, the size of which is determined by the lowest Ethernet (or whatever data link protocol) data frame size. Typically this is 1500 but don’t forget to account for header sizes so useful payload size is smaller. TCP is complex because it guarantees that your stream will eventually be delivered in the exact order in which it was sent. Eventually here could mean at t = infinity. UDP simply has source and destination port numbers in its header (which follows the IP header in the data frame), and guarantees nothing: not ordering not guaranteed delivery, etc. If an IP packet is a brick with two house addresses, a UDP datagram is a brick with two house addresses and an ATTN: application X added. An address represents an computer (this is very approximate in the world where any machine can have N addresses and run M VMs or containers which themselves can have O addresses), and a port represents a specific process on that computer.

ICMP does not use ports. ICMP is meant for essentially network and host telemetry so you are still sending and receiving only at an IP address level. But it has a number of message types. You can see them here: https://en.wikipedia.org/wiki/ICMPv6. Note that ping and pong are just two of the types. Others are actually responsible for things like communicating what raw IP cannot. For example Packet Too Large type tells you that an IP packet you tried to send was hitting a part of its path where the datagram size did not allow it to fit and it’s used for IP MTU path discovery (you keep sending different size packets to find what is the largest that will go through).

There are other protocols that run directly on top of IP (6in4 for example, or SCTP). Most are way less popular than the three mentioned above. Some use datagrams (discrete “bricks” of data), some use streams (endless “tapes” of data), which is the difference in protocol family: datagrams vs stream. You can also go a level deeper and just craft raw IP packets directly but for that you typically must be the root user since you can for example send a packet with the source port set to 22 even though you are not the ssh daemon.

Since ICMP has no concept of a port, when you send a ping to a remote host and it returns a ping to you, how does your kernel know to hand the response to your process and not some other one? In the ICMP header there is an ICMP identifier (often the process PID) and when the reply comes back it has the same identifier (but with source and destination IPs swapped and type updated to echo reply). This is what the kernel uses to find the process to which it will deliver the ICMP packet.

I hope this clears up some of this.

Re: Rootless Pings in Rust

#54
post #25

> It turns out you can create a UDP socket with a protocol flag, which allows you to send the ping rootless This is wrong, despite the Rust library in question's naming convention. You're not creating a UDP socket. You're creating an IP (AF_INET), datagram socket (SOCK_DGRAM), using protocol ICMP (IPPROTO_ICMP). The issue is that the rust library apparently conflates datagram and UDP, when they're not the same thing.…

> The issue is that the rust library apparently conflates datagram and UDP, when they're not the same thing.

It comes down to these two lines (using full items paths for clarity):

    let socket = socket2::Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::ICMPV4))?;
    let socket: std::net::UdpSocket = socket.into();
The latter is using this impl: https://docs.rs/socket2/0.6.1/socket2/struct.Socket.html#imp...

Basically the `socket2` crate lets you convert the fd it produces into a `UdpSocket`. It doesn't verify it really is a UDP socket first; that's up to you. If you do it blindly, you can get something with the wrong name, but it's probably harmless. (At the very least, it doesn't violate memory safety guarantees, which is what Rust code tends to be very strict about.)

`UdpSocket` itself has a `From` impl that similarly doesn't check it really is a UDP socket; you could convert the `socket2::Socket` to an `OwnedFd` then that to a `UdpSocket`. https://doc.rust-lang.org/stable/std/net/struct.UdpSocket.ht... https://docs.rs/socket2/0.6.1/socket2/struct.Socket.html#imp...

Re: Rootless Pings in Rust

#55
post #48
post #25

> It turns out you can create a UDP socket with a protocol flag, which allows you to send the ping rootless This is wrong, despite the Rust library in question's naming convention. You're not creating a UDP socket. You're creating an IP (AF_INET), datagram socket (SOCK_DGRAM), using protocol ICMP (IPPROTO_ICMP). The issue is that the rust library apparently conflates datagram and UDP, when they're not the same thing.…

Could you please explain me the difference? As UDP is the "User Datagram Protocol" when I read about datagrams I always think about UDP and though it was just a different way of saying the same thing. Maybe "datagram" is supposed to be the packet itself, but you're still sending it via UDP, right?

When I read about animals I always think about elephants and though it was just a different way of saying the same thing.

Re: Rootless Pings in Rust

#56
post #27

Earlier quoted context omitted.

ICMP is just different protocol from UDP. There's field "Protocol" in IP packet. 0x01 = ICMP, 0x06 = TCP, 0x11 = UDP. I think that this article gets terminology wrong. It's not UDP socket that gets created here, but Datagram socket. Seems to be bad API naming in Rust library.

> It's not UDP socket that gets created here, but Datagram socket A datagram socket is a UDP socket, though. That's what the D stands for.

Not every cola is Coca-Cola, even though "Cola" stands for cola.

Re: Rootless Pings in Rust

#57
post #39

Earlier quoted context omitted.

No? Why would you think a datagram socket is UDP?

What a reasonable question to be asked today.

Let me rephrase GP into (I hope) a more useful analogy. — actually, here’s the whole analogous exchange:

“A rectangle is an equal-sided rectangle (i.e. “square”) though. That’s what the R stands for.”

“No? Why would you think a rectangle is a square?”

Just as not all rectangles are squares (squares are a specific subset of rectangles), not all datagram protocols are UDP (UDP is just one particular datagram protocol).

Re: Rootless Pings in Rust

#58
post #44

Earlier quoted context omitted.

It's not, it's the `socket2` library. The standard sockets don't allow (ab)using actual `UdpSocket`s as a different kind of datagram socket.

We're talking about UdpSocket: https://doc.rust-lang.org/stable/std/net/struct.UdpSocket.ht...

Yes, I know, but the point is that the standard UdpSocket is correctly named as it doesn’t represent any other datagram socket. Uh, we’re pribably in agreement here actually.

Re: Rootless Pings in Rust

#59
post #25

> It turns out you can create a UDP socket with a protocol flag, which allows you to send the ping rootless This is wrong, despite the Rust library in question's naming convention. You're not creating a UDP socket. You're creating an IP (AF_INET), datagram socket (SOCK_DGRAM), using protocol ICMP (IPPROTO_ICMP). The issue is that the rust library apparently conflates datagram and UDP, when they're not the same thing.…

> The issue is that the rust library apparently conflates datagram and UDP, when they're not the same thing. It comes down to these two lines (using full items paths for clarity): let socket = socket2::Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::ICMPV4))?; let socket: std::net::UdpSocket = socket.into(); The latter is using this impl: https://docs.rs/socket2/0.6.1/socket2/struct.Socket.html#imp... Basically…

It may be memory safe but it's not using the type system to represent the domain very well.

One could imagine a more type-friendly design in which we could write that first line as follows:

    let socket: Socket = Socket::new()?;
Now, the specifics of socket types will be statically checked.

Edit: I realized that the issue here is actually the conversion, and that UdpSocket on its own is actually a type-safe representation of a UDP socket, not a general datagram socket. But the fact that this dubiously-safe conversion is possible and even useful suggests that an improved design is possible. For example, a method like UdpSocket's `set_broadcast` can't work with a socket like the above, and from a type safety perspective, it shouldn't be possible to call it on such a socket.

Re: Rootless Pings in Rust

#60
post #58

Earlier quoted context omitted.

We're talking about UdpSocket: https://doc.rust-lang.org/stable/std/net/struct.UdpSocket.ht...

Yes, I know, but the point is that the standard UdpSocket is correctly named as it doesn’t represent any other datagram socket. Uh, we’re pribably in agreement here actually.

Yeah exactly! :-D
Post reply on HN