-
Notifications
You must be signed in to change notification settings - Fork 12.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
From<u32> for Ipv4Addr is not performed in network byte order #48819
Comments
IMO My understanding of network order / big endian conversion is that it behaves exactly like that, while what you propose would be called host/native order. But perhaps the documentation could be written more clearly so it's easier to understand that its the representation of |
Perhaps it is best to see how Linux C libraries interpret network byte order. Typically IPv4 addresses are stored as u32 and are always in network byte order. Same is true for UDP and TCP port numbers. Lets experiment with a simple C program: int main(void) { inet_aton("1.2.3.4", &ia); ia.s_addr=0x0d0c0b0a; return 0; Unsurprisingly this works per network byte order interpretation I have described: Notice how on Intel based server bytes are swaped. Let's look manual page to see what these functions are supposed to do:
…
Notice manual page specifying that network byte order is used. If you use host byte order for IP addresses (or port numbers) on Intel based Linux machine, it will fail miserably. I encountered this problem in Rust library while trying to integrate a Rust based software to software that stores network byte order IP addresses in u32s. So I am quite convinced that conversion is incorrect and the evidence the above test program shows clearly indicates that at minimum your interpretation of network byte order for u32 is different from what is commonly accepted. You may also want to check how IP packets looks when captured with Wireshark. IP addresses shown in hex dump are in network byte order (if you interpret four octets as u32). Notice that bytes are not in the order Intel processor would store them in host byte order. |
The mindset to use is that This means that the same integer always maps to the same bytes, regardless of your host endianness, making it easy to write platform independent code (e.g. Unfortunately the C programmers of old did not follow that mindset, re-interpret casting between structs and on-wire representations with abandon, producing some ugly APIs in the process. So if you have to interact with such a badly designed API, you need to produce integers that can be re-interpreted to a specific representation. This can be done with One interesting question is if Rust guarantees that an |
It is perfectly good specification to do the conversion from host byte order. I would however suggest fixing the documentation to indicate that the conversion is performed in host byte order. Current documentation is highly misleading, especially in light of established practices (i.e. sockets) on what u32 network byte order presentation of IPv4 address is. Use of [u8; 4] is indeed unambiguous, but much of the C-based IPv4 handling code does rely on u32:s with network byte order (probably due to fact that that is how kernel and C library handles them). I do agree that on Rust code proper typing should be done, and indeed the conversion to Ipv4Addr is the first thing Rust code does for inputs. Hence this bug report. I don't see how I could pass Ipv4Addr conveniently in FFI however. |
The specification is written with the intended meaning of "This converts an integer to its network representation", but it's a bit ambiguous since it can also be interpreted as "This interprets the integer as being in network representation".
If As long as there is no guarantee, you'll have to resort to In How to teach endian Robert Graham describes pretty much the same approach. The part most relevant to our discussion here is:
|
So seems we can't change the underling behavior here since that could be a very subtle breaking change. Regarding changing the documentation here, what would be the best way to word this? Something like this?
note to self, here's a WIP branch: https://github.com/rust-lang/rust/compare/master...frewsxcv:frewsxcv-network-order?expand=1 |
Documentation change seems the easiest avenue. For u32->Ipv4Addr conversion, documentation would of course have to be: It might be a good idea to add a warning note along the lines of: As conversion is performed in host byte order, this primitive must not be used to convert in_addr (in_addr.s_addr) to IP address. A suitable doc-test might be a good clarification also, although I am not sure how to come up with a short elegant test. A long version would be: #[cfg(target_endian = "big")] |
opened a pr #49418 |
…rsions. Opened primarily to address rust-lang#48819.
I'm not too happy with the tests. The test should make it clear that any code which simply uses Something like:
I also want to note that |
Clarify network byte order conversions for integer / IP address conversions. Opened primarily to address #48819. Also added a few other conversion docs/examples.
the root issue here seems to have been addressed by #49418 if you have ideas test improvements, might be worth opening a pull request or separate issue for that |
Documentation for u32 -> Ipv4Addr conversion (https://doc.rust-lang.org/1.21.0/std/net/struct.Ipv4Addr.html) and recent bug discussion (#40118) claim that conversion is performed in network byte order. This is clearly not the cases.
For example IP address 10.11.12.13 in IP header would be hex bytes 0x0a 0x0b 0x0c 0x0d and Inter CPU (little endian) would interpret this value as u32 0x0d0c0b0a. Conversion to Ipv4Addr results with current library implementation IP address 13.12.11.10.
The problem is clear from the fact that implementation (https://doc.rust-lang.org/1.21.0/src/std/net/ip.rs.html#745-747) does not contain conditional code based on endianness and operates using shift operations. Hence it can only work on a processor that is natively in network byte order. Current implementation is:
Conversion without conditional code would only be possible if code would cast u32 to u8[4] (not sure if that is possible in Rust... on C that would work), not using arithmetic operations on u32 where host byte order comes into play.
A suitable fix might be along the lines of (I am assuming Rust compiler optimises htonl away.. if not, more optimal approach would just be to make conversion trait itself conditional on host byte order):
Fixing the issue in code (instead of just fixing the documentation) does have down side. If any code writes IP addresses in hex in host byte order (that is a convenient way to do it), fixing this breaks such code.
The text was updated successfully, but these errors were encountered: