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 support for IP_RECVERR #1161

Closed
wants to merge 6 commits into from
Closed

Add support for IP_RECVERR #1161

wants to merge 6 commits into from

Conversation

mcpherrinm
Copy link

This pull request adds support for setting the IP_RECVERR sockopt, and then decoding the resulting messages from recvmsg. My intent is to use this to implement a traceroute program (And so need ICMP TTL exceeded messages), though IP_RECVERR is useful in general when writing UDP network programs. It is, unfortunately, a linux-specific API.

There's no tests here, but I've been trying it out with this bit of code:
mcpherrinm/traceroute@8a92b6d

SockExtendedErr is the struct type returned as a Cmsg from recvmsg. It (like the C linux/errqueue.h which defines it) uses u8 and u32. I think we'll want something more user-friendly here, so I'd say this PR is still a work-in-progress.

I am new to the nix codebase (and am not very experienced with Rust), so would appreciate early feedback.

Setting these options enables receiving errors, such as
ICMP errors from the network, via recvmsg with MSG_ERRQUEUE
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.

It's a good start, but it needs tests, a CHANGELOG entry, and a usage example.

And once we start a review in Nix, please don't force-push until the end when you're ready to squash.

#[cfg(any(target_os = "android", target_os = "linux"))]
#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SockExtendedErr {
Copy link
Member

Choose a reason for hiding this comment

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

Is this struct defined anywhere in the C headers? If so, you should add its definition to the libc crate, and reference that here. Nix shouldn't be in the business of repeating the headers.

Copy link
Author

Choose a reason for hiding this comment

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

It's defined in https://elixir.bootlin.com/linux/v5.4.1/source/include/uapi/linux/errqueue.h

I read the libc crate's RFC https://github.com/rust-lang/rfcs/blob/master/text/1291-promote-libc.md and it wasn't clear to me if this struct belonged there or not.

I am happy to open a PR there first and define it there.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, please submit a PR to libc.

@mcpherrinm
Copy link
Author

Yes, won't force-push anymore. Just wanted to quickly fix a few cfg errors that caused the freebsd CI to fail.

@mcpherrinm
Copy link
Author

I have opened a PR against libc @ rust-lang/libc#1613

I will revise this pull request to use the struct definition from there once reviewed and merged.

Thank you for your time so far.

This uses a patched libc and should not be merged until libc merges.
@asomers
Copy link
Member

asomers commented Dec 15, 2019

Looks like this PR is still waiting on rust-lang/libc#1616

ControlMessageOwned::Ipv4RecvErr(err, addr)
},
#[cfg(any(target_os = "android", target_os = "linux"))]
(libc::IPPROTO_IPV6, libc::IP_RECVERR) => {
Copy link

Choose a reason for hiding this comment

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

It should be IPV6_RECVERR (tested that on Linux).

Copy link

@WGH- WGH- left a comment

Choose a reason for hiding this comment

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

I think this also needs some proper way to calculate space for cmsg.

I looked through some C examples, and they seem to just allocate excessive buffer space instead of calculating properly. I was unable to find a "proper" example yet.

Should it be CMSG_SPACE(sizeof(struct sock_extended_err) + max(sizeof(struct sockaddr_in), sizeof(struct sockaddr_in6)))?

(libc::IPPROTO_IPV6, libc::IP_RECVERR) => {
let ee = p as *const libc::sock_extended_err;
let err = ptr::read_unaligned(ee);
let addr = ptr::read_unaligned(libc::SO_EE_OFFENDER(ee) as *const sockaddr_in6);
Copy link

Choose a reason for hiding this comment

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

man 2 recvmsg

For local errors, no address is passed (this can be checked with the cmsg_len member of the cmsghdr).

Given that, I think addr should become an Option, and len should be handled appropriately.

@WGH-
Copy link

WGH- commented Feb 13, 2020

Anyway, I tested the code with my toy TCP SYN scanner, and it works fine (except that IPV6_RECVERR thing that I fixed myself).

@WGH-
Copy link

WGH- commented Mar 1, 2020

libc-0.2.67 has been released recently, which includes the missing bits, so it's no longer necessary to use a patch.

iff --git a/Cargo.toml b/Cargo.toml
index 29f0fca..8fb953e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -16,14 +16,11 @@ exclude     = [
 ]
 
 [dependencies]
-libc = { version = "0.2.60", features = [ "extra_traits" ] }
+libc = { version = "0.2.67", features = [ "extra_traits" ] }
 bitflags = "1.0"
 cfg-if = "0.1.2"
 void = "1.0.2"
 
-[patch.crates-io]
-libc = { git = "https://github.com/mcpherrinm/libc" }
-
 [target.'cfg(target_os = "dragonfly")'.build-dependencies]
 cc = "1"

@mcpherrinm
Copy link
Author

Thanks @WGH-. I'll update this PR soon.

@asomers
Copy link
Member

asomers commented Mar 8, 2020

Looks like you need to raise the minimum version of libc

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.

This PR still needs documentation, a usage example, tests (those two can be the same), and a CHANGELOG entry. And it will need to be rebased before merging.

@mcpherrinm
Copy link
Author

Thanks. Sorry this is taking so long, but I have little free time for side project programming right now.

cemeyer added a commit to cemeyer/nix that referenced this pull request Sep 6, 2021
Make the address field of the RecvErr tuples optional, as
local-originating errors do not have an address attached to the control
message.  Use the associated cmsg_len to determine if the address is
present when copying the raw cmsg into a ControlMessageOwned.
bors bot added a commit that referenced this pull request Sep 7, 2021
1514: Add support for IP_RECVERR r=asomers a=cemeyer

This pull request adds support for setting the `IP_RECVERR` (and corresponding `IPV6_RECVERR`) sockopt, and then decoding the resulting control messages from `recvmsg()`.  It is a Linux-specific API.

This PR extends #1161 by:
* Making the address associated with a `IpvXRecvErr` control message optional (`recvmsg` documentation claims it is not provided for some kinds of error), per `@WGH-.`
* Adding basic tests for both IPv4 and IPv6 (blat a UDP packet at a hopefully-unoccupied port on localhost and observe ICMP port unreachable).
* Adding a Changelog entry.

I added some trivial doc comments on the `ControlMessageOwned` variants, but I'm not sure exactly what documentation you had in mind earlier on #1161.

Co-authored-by: Conrad Meyer <cem@FreeBSD.org>
@cemeyer
Copy link
Contributor

cemeyer commented Sep 7, 2021

We can probably close this one now. Thanks!

@asomers
Copy link
Member

asomers commented Sep 7, 2021

Yep.

@asomers asomers closed this Sep 7, 2021
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

4 participants