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 ICMPv6 to ICMP sockets #205

Closed
wants to merge 1 commit into from

Conversation

progval
Copy link
Contributor

@progval progval commented May 9, 2018

Hi,

I started adding support for ICMPv6 to ICMP sockets (see #99 and #103).

Do you have some feedback on my changes (attached to this PR) before I implement it further?

Thanks!

src/wire/mod.rs Outdated
TimeExceeded as IcmpTimeExceeded,
ParamProblem as IcmpParamProblem,
Packet as IcmpPacket,
Repr as IcmpRepr};
Copy link
Contributor

Choose a reason for hiding this comment

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

Are you planning to somehow unify the ICMPv4 and ICMPv6 Message, DstUnreachable, TimeExceeded and ParamProblem enums? This doesn't seem like the right way to go to me. Why is this necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed, it isn't

@whitequark
Copy link
Contributor

Other than the comment above, looks good!

fn process_icmpv4<'frame>(&self, _sockets: &mut SocketSet, ip_repr: IpRepr,
ip_payload: &'frame [u8]) -> Result<Packet<'frame>>
#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
fn process_icmp<'frame>(&self, _sockets: &mut SocketSet, ip_repr: IpRepr,
Copy link
Collaborator

Choose a reason for hiding this comment

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

ICMPv6 packets will be processed by process_icmpv6. It has a different IP protocol number.

@@ -174,6 +176,13 @@ pub use self::icmpv6::{Message as Icmpv6Message,
ParamProblem as Icmpv6ParamProblem,
Packet as Icmpv6Packet,
Repr as Icmpv6Repr};
#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same

src/wire/mod.rs Outdated
@@ -95,6 +95,8 @@ mod ipv6fragment;
mod icmpv4;
#[cfg(feature = "proto-ipv6")]
mod icmpv6;
#[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
Copy link
Collaborator

Choose a reason for hiding this comment

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

Rely on the icmpv6 module for proto-ipv6.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What do you mean?

Copy link
Collaborator

Choose a reason for hiding this comment

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

The comment by @whitequark here was kind of what I was getting at and is a much clearer. If we get an ICMPv6 response we shouldn't be using the definitions in wire::icmpv4.

Note: my assumption was that wire::icmp was the same thing as wire::icmpv4. If your intent was to make wire::icmp a module that encapsulates ICMPv4 and ICMPv4 (similar to wire::ip for IPv4 and IPv6), then your current approach might work.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm, except that it should be any() and not all()

Copy link
Collaborator

Choose a reason for hiding this comment

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

True.

@whitequark
Copy link
Contributor

OK, now that I see icmp.rs, I think this is definitely not the right approach. IcmpRepr makes sense to have, but the other enums aren't used anywhere and it's not clear to me that they are clearly useful as defined.

@progval
Copy link
Contributor Author

progval commented May 9, 2018

Yeah, I started by implementing all the enums before looking at the existing code. I'll remove them

@progval
Copy link
Contributor Author

progval commented May 9, 2018

I'm getting three test failures:

---- socket::icmp::test::test_accepts_udp_v6 stdout ----
	thread 'socket::icmp::test::test_accepts_udp_v6' panicked at 'assertion failed: `(left == right)`
  left: `Err(Truncated)`,
 right: `Ok(())`', src/socket/icmp.rs:745:9

---- socket::icmp::test::test_send_dispatch_v6 stdout ----
	thread 'socket::icmp::test::test_send_dispatch_v6' panicked at 'assertion failed: `(left == right)`
  left: `Err(Checksum)`,
 right: `Err(Unaddressable)`', src/socket/icmp.rs:499:9

---- socket::icmp::test::test_set_hop_limit_v6 stdout ----
	thread 'socket::icmp::test::test_set_hop_limit_v6' panicked at 'assertion failed: `(left == right)`
  left: `Err(Checksum)`,
 right: `Ok(())`', src/socket/icmp.rs:552:9

My guess is that the two latter are because the checksum is never computed (which is not an issue for ICMPv4 tests, because ICMPv4 checksums can be blank).

I tried solving them, with no success. Any idea?

@whitequark
Copy link
Contributor

---- socket::icmp::test::test_accepts_udp_v6 stdout ----
thread 'socket::icmp::test::test_accepts_udp_v6' panicked at 'assertion failed: (left == right)
left: Err(Truncated),
right: Ok(())', src/socket/icmp.rs:745:9

Do you know why this results in a Truncated error?

@progval
Copy link
Contributor Author

progval commented May 9, 2018

No. I added a println! in front of every initialization of Truncated I could find in ICMP- and IPv6-related files, none of them were called.

@whitequark
Copy link
Contributor

Sorry, I don't have time to investigate this right now. Maybe @dlrobertson can help you?

Copy link
Collaborator

@dlrobertson dlrobertson left a comment

Choose a reason for hiding this comment

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

Do you know why this results in a Truncated error?

I think it is returning Truncated here I'll have to spend some quality time in gdb to confirm.

@@ -828,11 +833,11 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {

#[cfg(feature = "proto-ipv4")]
fn process_icmpv4<'frame>(&self, _sockets: &mut SocketSet, ip_repr: IpRepr,
ip_payload: &'frame [u8]) -> Result<Packet<'frame>>
ip_payload: &'frame [u8]) -> Result<Packet<'frame>>
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: whitespace

{
let icmp_packet = Icmpv4Packet::new_checked(ip_payload)?;
let checksum_caps = self.device_capabilities.checksum.clone();
let icmp_repr = Icmpv4Repr::parse(&icmp_packet, &checksum_caps)?;
let icmp_repr = Icmpv4Repr::parse(&icmp_packet, &checksum_caps)?.into();
Copy link
Collaborator

Choose a reason for hiding this comment

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

The new IcmpRepr really shouldn't be used in the process_icmpv4 or process_icmpv6 functions. AFAIK it is really only needed for getting data from the socket to the interface, but I don't think it is needed for parsing data from the outside world.

Copy link
Contributor Author

@progval progval May 9, 2018

Choose a reason for hiding this comment

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

It's needed for IcmpSocket::accept and IcmpSocket::process

Copy link
Collaborator

Choose a reason for hiding this comment

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

yeah this is more or less a nit. It might be cleaner to add a line like let socket_repr = IcmpRepr::from(icmp_repr) then use socket_repr for the IcmpSocket functions and then you can still match icmp_repr and you don't have the extra IcmpRepr::Ipv4(...), but this is just a nit.

Copy link
Collaborator

Choose a reason for hiding this comment

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

You also need to add similar code to process_icmpv6 to ensure any ICMPv6 sockets actually receive data.

IpAddress::Ipv6(ipv6_addr) => {
let packet = Icmpv6Packet::new(&*packet_buf);
let src_addr = Ipv6Address::default();
let repr = Icmpv6Repr::parse(&src_addr.into(), &ipv6_addr.into(), &packet, &ChecksumCapabilities::default())?;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should we expect users to fill in the checksum? Or should we just parse it with ChecksumCapabilities::ignored()?

Copy link
Contributor

Choose a reason for hiding this comment

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

We shouldn't expect the users to fill in checksum.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't really understand what ChecksumCapabilities mean (should the user of smoltcp verify them? or smoltcp itself? or the hardware?), but using ChecksumCapabilities::ignored fixed two of the issues, thanks!

Copy link
Collaborator

Choose a reason for hiding this comment

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

It is essentially used to determine if the parse/emit function should verify/fill the checksum. It is typically used in the context of a device. For example, a device that we can offload IPv4 checksumming on transmit to would have a ChecksumCapabilities structure with ipv4 equal to TX. However, in this case we don't want to evaluate the checksum so using ChecksumCapabilities::ignored makes that happen.

@progval
Copy link
Contributor Author

progval commented May 9, 2018

Ha, I found why it's truncated, it's because the buffer allocated on the first line of test_accepts_udp_v6 only supports 46-bytes packets.

Thanks for suggesting gdb, that's how I found the issue.

src/wire/icmp.rs Outdated
}
}
};
( $class:ident < $typearg:tt > ) => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is the only matching arm that is used right? Or am I missing something?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, now it is. I think I'll remove the macro altogether, as there is only one class left.

@progval progval changed the title Start adding support for ICMPv6 to ICMP sockets Add support for ICMPv6 to ICMP sockets May 9, 2018
@progval
Copy link
Contributor Author

progval commented May 9, 2018

(I just squashed some commits to make the git history cleaner)

Looks like everything is working now, thanks for your help!

Err(_) => false,
}
}
#[cfg(feature = "proto-ipv6")]
Copy link
Contributor

Choose a reason for hiding this comment

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

I feel like these two cases ought to be unified, but not sure if this can be done cleanly.

Copy link
Contributor Author

@progval progval May 9, 2018

Choose a reason for hiding this comment

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

I could use a function to factorize the code, but I'm not sure it improves readability

Copy link
Contributor

Choose a reason for hiding this comment

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

I was thinking of something like

match x {
  #[cfg(foo)] Y | #[cfg(bar)] Z => t
}

but I don't know if it works.

Copy link
Contributor Author

@progval progval May 9, 2018

Choose a reason for hiding this comment

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

It doesn't

@whitequark
Copy link
Contributor

Looks good! Please squash all commits. After that, r=me.

@whitequark
Copy link
Contributor

@m-labs-homu r+

@m-labs-homu
Copy link

📌 Commit 6eb23fa has been approved by whitequark

@m-labs-homu
Copy link

⌛ Testing commit 6eb23fa with merge ce2fbb7...

@m-labs-homu
Copy link

☀️ Test successful - status-travis
Approved by: whitequark
Pushing ce2fbb7 to master...

@dlrobertson
Copy link
Collaborator

@progval thanks for working on this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants