Skip to content
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

Add SetSockOpt definitions related to Linux Kernel TLS. #2175

Merged
merged 1 commit into from Jan 3, 2024

Conversation

Arnavion
Copy link
Contributor

@Arnavion Arnavion commented Nov 3, 2023

  1. kTLS is implemented as a TCP ULP, so add a SetSockOpt impl for it.

  2. kTLS parameters are pushed to the kernel using setsockopt(SOL_TLS),
    so add SetSockOpt impls for them.

Other test fixes:

  1. Change setsockopt tests to bind to port 0 so that
    they bind to an unbound port and don't affect each other.

  2. Fix skip! macro used to skip tests to generate its own block so that
    it can be used as an expr.


What does this PR do

Implement additional definitions needed to set up kTLS.

Checklist:

  • I have read CONTRIBUTING.md
  • I have written necessary tests and rustdoc comments
  • A change log has been added if this PR modifies nix's API

@Arnavion
Copy link
Contributor Author

Arnavion commented Nov 3, 2023

The Linux CI did fail with EADDRINUSE. I did notice when I was writing the test that all the setsockopt tests that did bind were using the same port, and I wondered how it was that they never stepped on each other. I guess it was just luck that nobody hit it before.

Should I change my test and all the others to use unique ports?

Copy link
Member

@asomers asomers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you should change the port number. Also, would it be possible to add some docs? It isn't obvious how to use this. And is there any overlap with PR #2065 ?

@Arnavion
Copy link
Contributor Author

Arnavion commented Nov 4, 2023

Yes, you should change the port number.

Will do.

Also, would it be possible to add some docs? It isn't obvious how to use this.

The documentation is https://docs.kernel.org/networking/tls.html . I'll add comments explaining which setsockopt call each of the structs maps to.

And is there any overlap with PR #2065 ?

No. The steps of using kTLS are:

  1. Set up TCP connection.

  2. Do TLS handshake however you want, say using rustls or openssl.

  3. Enable the TLS ULP on the TCP socket. (setsockopt(TcpUlp, b"tls\0") from this PR.)

  4. Import the TLS secrets for sending and receiving to the TCP socket. (setsockopt(TcpTlsTx, ...) and setsockopt(TcpTlsRx, ...) from this PR.)

  5. Read and write to the TCP socket using regular socket syscalls. Reading should use recvmsg because the kernel will send cmsg's with level == libc::SOL_TLS and type == libc::TLS_GET_RECORD_TYPE whenever there is a non-application data / post-handshake record. (The recvmsg changes in Parse SOL_TLS control message, closes #2064 #2065 )

@asomers
Copy link
Member

asomers commented Nov 4, 2023

There are a bunch of tests that don't pass under cross. If you're sure that this test failure is cross-induced, you could skip the test in CI by adding #[cfg_attr(qemu, ignore)].

@Arnavion
Copy link
Contributor Author

Arnavion commented Nov 4, 2023

There are a bunch of tests that don't pass under cross. If you're sure that this test failure is cross-induced, you could skip the test in CI by adding #[cfg_attr(qemu, ignore)].

Yes, that is what I had been battling with. The last commit I pushed finally fixes them.

Cargo.toml Outdated Show resolved Hide resolved
@SteveLauC
Copy link
Member

Hi @Arnavion, we now use the libc dependency from git, rebasing your branch should make things work

@Arnavion
Copy link
Contributor Author

Arnavion commented Dec 4, 2023

Done, and verified with my test program.

/// ```ignore,rust
/// setsockopt(sock, TcpUlp::default(), b"tls\0");
/// ```
#[cfg(target_os = "linux")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TCP_ULP and SOL_TCP symbols are defined on Android as well as Linux. So here and elsewhere, you should do

Suggested change
#[cfg(target_os = "linux")]
#[cfg(linux_android)]

Those two symbols are also defined on Fuchsia, but stuff like tls12_crypto_info_aes_gcm_128 isn't, so I guess you should leave Fuchsia out.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TLS ULP is useless without being able to do TLS_TX and TLS_RX, and I only added all this in libc for linux. Someone who uses Android should extend it to Android.

Copy link
Contributor Author

@Arnavion Arnavion Dec 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, but you're right that there's no harm in enabling other ULPs at least. Done.

Comment on lines 679 to 691
// Some architectures running under cross don't support `setsockopt(SOL_TCP, TCP_ULP)`
// because the cross image is based on Ubuntu 16.04 which predates TCP ULP support
// (it was added in kernel v4.13 released in 2017). For these architectures,
// the `setsockopt(SOL_TCP, TCP_ULP, "tls", sizeof("tls"))` call succeeds
// but the subsequent `setsockopt(SOL_TLS, TLS_TX, ...)` call fails with `ENOPROTOOPT`.
// It's as if the first `setsockopt` call enabled some other option, not `TCP_ULP`.
// For example, `strace` says:
//
// [pid 813] setsockopt(4, SOL_TCP, 0x1f /* TCP_??? */, [7564404], 4) = 0
//
// It's not clear why `setsockopt(SOL_TCP, TCP_ULP)` succeeds if the container image libc doesn't support it,
// but in any case we can't run the test on such an architecture, so skip it.
Err(nix::Error::ENOPROTOOPT) => skip!("TCP ULP are not supported."),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be simpler to delete this part, and just add #[cfg_attr(qemu, ignore)] to the top of the function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

/// ... and the `nix` equivalent is:
///
/// ```ignore,rust
/// setsockopt(sock, TcpUlp::default(), b"tls\0");
Copy link
Member

@SteveLauC SteveLauC Dec 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am curious, in the C interface, does this "tls" argument always have the type C string (having a NUL at the end) or it can be some random bytes

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The value is the name of the ULP. You can look at the ones you have available with cat /proc/sys/net/ipv4/tcp_available_ulp.

The string does not need to be NUL-terminated. This is leftover from when I was trying to make the CI work and I thought the lack of NUL terminator might be the reason armv7 was failing. I confirmed from the kernel code that it only copies the user-provided string up to the smaller of user-provided length and 15 bytes (TCP_ULP_NAME_MAX - 1) and adds its own NUL terminator, and it also works without the trailing NUL in my test program.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the trailing NUL from this example and from the test.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I confirmed from the kernel code that it only copies the user-provided string up to the smaller of user-provided length and 15 bytes (TCP_ULP_NAME_MAX - 1) and adds its own NUL terminator.

Thanks for digging into the kernel code, do you think it would be great if we could have a comment in the code describing this behavior?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for doing it, and you don't need to squash commits as we will do squash merge.

Copy link
Member

@SteveLauC SteveLauC left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, but maybe @asomers also wants to finish his review

@Arnavion
Copy link
Contributor Author

@asomers @SteveLauC Ping.

@SteveLauC
Copy link
Member

Normally, asomers will maintain Nix on Friday/weekend

1. kTLS is implemented as a TCP ULP, so add a `SetSockOpt` impl for it.

2. kTLS parameters are pushed to the kernel using `setsockopt(SOL_TLS)`,
   so add `SetSockOpt` impls for them.

Other test fixes:

1. Change `setsockopt` tests to bind to port 0 so that
   they bind to an unbound port and don't affect each other.

2. Fix `skip!` macro used to skip tests to generate its own block so that
   it can be used as an expr.
@Arnavion
Copy link
Contributor Author

Arnavion commented Jan 2, 2024

@SteveLauC Ping. This has been ready to merge for two weeks now, and I'm tired of fixing all the merge conflicts from the other newer PRs that keep getting merged :)

This is also blocking more fixes I want to make to the GetSockOpt / SetSockOpt traits.

@SteveLauC SteveLauC added this pull request to the merge queue Jan 3, 2024
@SteveLauC
Copy link
Member

Understand, let's merge this!

Merged via the queue into nix-rust:master with commit 57c611c Jan 3, 2024
35 checks passed
@Arnavion Arnavion deleted the ktls branch January 3, 2024 01:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants