Description
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:
#[stable(feature = "ip_u32", since = "1.1.0")]
impl From<u32> for Ipv4Addr {
/// It performs the conversion in network order (big-endian).
fn from(ip: u32) -> Ipv4Addr {
Ipv4Addr::new((ip >> 24) as u8, (ip >> 16) as u8, (ip >> 8) as u8, ip as u8)
}
}
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):
#[cfg(target_endian = "little")]
fn htonl(val : u32) -> u32 {
let o3 = (val >> 24) as u8;
let o2 = (val >> 16) as u8;
let o1 = (val >> 8) as u8;
let o0 = val as u8;
((o0 as u32) << 24 | (o1 as u32) << 16 | (o2 as u32) << 8 | (o3 as u32))
}
#[cfg(target_endian = "big")]
fn htonl(val : u32) -> u32 {
val
}
#[cfg(target_endian = "little")]
#[test]
fn test_htonl() {
assert_eq!(0x12345678, htonl(0x78563412));
}
impl From<u32> for Ipv4Addr {
/// It performs the conversion in network order (big-endian).
fn from(ip_hb: u32) -> Ipv4Addr {
let ip = htonl(ip_hb);
Ipv4Addr::new((ip >> 24) as u8, (ip >> 16) as u8, (ip >> 8) as u8, ip as u8)
}
}
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.