quinn-rs / quinn

Async-friendly QUIC implementation in Rust
Apache License 2.0
3.76k stars 380 forks source link

Unable to bind IPv6 socket on cross aarch64-linux-android #1568

Closed dignifiedquire closed 1 year ago

dignifiedquire commented 1 year ago

Currently running through some tests, and it looks like IPv6 is broken on my cross builds. I checked the flags and the offender is the IPV6_DONTFRAG, which results in

set_socket_option(&socket, libc::IPPROTO_IPV6, libc::IPV6_DONTFRAG, 1) = Err(
    Os {
        code: 92,
        kind: Uncategorized,
        message: "Protocol not available",
    },
)
Ralith commented 1 year ago

Thanks for the report! What kernel is this?

dignifiedquire commented 1 year ago

I am not entirely sure: https://github.com/cross-rs/cross#supported-targets is the relevant docs. I believe this executes in Qemu.

Ralith commented 1 year ago

Do you know what the minimum kernel version you actually intend to support is? We could just look up the status of IPV6_DONTFRAG on whatever that is.

If indeed a kernel you need to support lacks that socket option, quinn_udp::may_fragment() should be modified to return true in that environment (perhaps by attempting it on a dummy socket) and we should skip the call. As it looks like you've already discovered, explicitly binding an IPv4 address in that environment is a good workaround.

Arqu commented 1 year ago

An overview of that environment:

- Env: github actions runner
- OS: Ubuntu 22.04.02
- Kernel: 5.15.0-1037-azure
- Cargo/Rust: 1.69.0
- Lowest android kernel: 3.14.0
- Tested android kernel: 4.15 (I think)

Since this is like 5 layers deep with cross (docker) + qemu (android) + whatever android version combo it's a bit hard to track all of it down, but the min kernel version for the android stuff we want to run is 3.14.0. However this is a lot more complicated since there are no kernel version guarantees for any given android version running on a device, mostly just rule of thumb based on what the "core release" included.

Ralith commented 1 year ago

Strange, IPV6_DONTFRAG seems to be supported since at least 3.0: https://elixir.bootlin.com/linux/v3.0/source/net/ipv6/ipv6_sockglue.c#L802

What address was the socket that gave you that error bound to?

dignifiedquire commented 1 year ago

What address was the socket that gave you that error bound to?

https://doc.rust-lang.org/nightly/std/net/struct.Ipv6Addr.html#associatedconstant.LOCALHOST

dignifiedquire commented 1 year ago

I also tested setting all the flags except DONTFRAG and then it works without any issues.

Ralith commented 1 year ago

Thanks. I'm very confused by why that would fail, but I'd be happy to merge a mitigation as outlined above. If anyone's feeling especially motivated, it would be interesting to step through the kernel to work out where exactly that error comes from.

flub commented 1 year ago

While I wrote the feature detection in https://github.com/flub/quinn/tree/flub/detect-ipv6-dontfrag I don't think this is helpful. While testing that out on cross aarch64-linux-android it seems this platform just doesn't support IPv6 at all, regardless of socket options it fails by the time you try and bind with AddrNotAvailable.

So I suggest this can be closed safely (I seem to not have permissions to do so).

djc commented 1 year ago

Thanks for the effort!

Ralith commented 1 year ago

Weird, I thought we bound before setting any socket options? Anyway, thanks for investigating.

flub commented 1 year ago

Weird, I thought we bound before setting any socket options? Anyway, thanks for investigating.

We are doing some unusual things where we make our own AsyncUdpSocket which is backed by 3 different real sockets to enable us to do holepunching. So we're to blame ourselves for the weird order things happen in.