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

swarm: disallow dialing of 0.0.0.0 #4460

Open
marten-seemann opened this issue Sep 6, 2023 · 11 comments
Open

swarm: disallow dialing of 0.0.0.0 #4460

marten-seemann opened this issue Sep 6, 2023 · 11 comments
Labels
difficulty:easy help wanted priority:important The changes needed are critical for libp2p, or are blocking another project

Comments

@marten-seemann
Copy link

marten-seemann commented Sep 6, 2023

We just discovered a bug in go-libp2p where our existing logic that prevents self-dials doesn't work.

It turns out that on Linux, it is valid to dial 0.0.0.0: https://en.wikipedia.org/wiki/0.0.0.0#Operating_system_specific_uses. The kernel will then dial localhost.

We should detect that and disallow the dial.

@thomaseizinger
Copy link
Contributor

Thanks for the ping!

The way we handle 0.0.0.0 is that we watch the local system for interface changes and listen on the specific interfaces as they come up, see

if local_addr.ip().is_unspecified() {
return ListenStream::<T>::new(
id,
listener,
Some(T::new_if_watcher()?),
self.port_reuse.clone(),
);
}
for the specific line.

Under the hood, this uses https://github.com/mxinden/if-watch/.

@marten-seemann
Copy link
Author

The issue is not about the listener side, it's about dialing 0.0.0.0.

@thomaseizinger
Copy link
Contributor

It's probably a good idea to filter out 0.0.0.0 advertisements.

My point was towards this in that we never advertise 0.0.0.0 because we translate them to actual addresses of interfaces first.

But I guess what you are saying is that we should still deny a dial for 0.0.0.0 and filter them from identify, etc.

@marten-seemann
Copy link
Author

Right. You shouldn't advertise it, but you need to correctly handle all kinds of nonsense that other peers might try to tell you. 0.0.0.0 is one example of such nonsense that caught us by surprise in go-libp2p.

@marten-seemann
Copy link
Author

go-libp2p fix: libp2p/go-libp2p#2560

@thomaseizinger thomaseizinger changed the title check what happens when a peer advertises 0.0.0.0 swarm: disallow dialing of 0.0.0.0 Sep 11, 2023
@thomaseizinger thomaseizinger added difficulty:easy help wanted priority:important The changes needed are critical for libp2p, or are blocking another project labels Sep 11, 2023
@vnermolaev
Copy link
Contributor

vnermolaev commented Jan 19, 2024

Hi @thomaseizinger

I want to address this; my initial thought goes as follows.

Immediately after https://github.com/libp2p/rust-libp2p/blob/master/swarm/src/lib.rs#L438

I want to add the following filtering:

let dial_address_errors = dial_opts
            .get_addresses()
            .iter()
            .map(|address| {
               // Collect errors per address.
                address
                    .iter()
                    .filter_map(|protocol| match protocol {
                        Protocol::Ip4(ip) if ip.is_unspecified() => Some(AddressError::Ip4(ip)),
                        Protocol::Ip6(ip) if ip.is_unspecified() => Some(AddressError::Ip6(ip)),
                        _ => None,
                    })
                    .collect::<Vec<_>>()
            })
            .flatten()
            .collect::<Vec<_>>();

        if !dial_address_errors.is_empty() {
            return DialError::AddressConfiguration(dial_address_errors);
        }

This filtering shall collect all errors in all address configurations and report them as a single error.
Could you consider this?

@thomaseizinger
Copy link
Contributor

Filtering the addresses sounds good to me. We need to do it as late as possible, after we gathered the addresses from the behaviours. We should also add a log statement to inform users that we are dropping one of they addresses they provided.

Overall, this may result in a NoAddresses error which might leave people puzzling if they supply one but don't realise it is 0.0.0.0.

This will need a test too!

@vnermolaev
Copy link
Contributor

I planned to introduce a new error variant in DialError and return all erroneous addresses in that variant. The presented code snippet does exactly that, albeit skips the introduction of the error variants.

Would you prefer to

  • filter and drop incorrect addresses with an appropriate log statement or
  • collect incorrect addresses and report them to the developer to handle?

Let me know, and I will be able to make a PR.

@thomaseizinger
Copy link
Contributor

Adding a new variant will be a breaking change so I'd rather not do that for such a corner case.

I think filtering is fine. Most users will never hit this so adding an extra variant for that is unnecessary complexity :)

@vnermolaev
Copy link
Contributor

vnermolaev commented Jan 23, 2024

Hey @thomaseizinger
I've implementing filtering and then got to testing and I found this test multiple_addresses_err.
The test lists addresses that should produce connection errors

        let addresses = HashSet::from([
            multiaddr![Ip4([0, 0, 0, 0]), Tcp(rand::random::<u16>())],
            multiaddr![Ip4([0, 0, 0, 0]), Tcp(rand::random::<u16>())],
            multiaddr![Ip4([0, 0, 0, 0]), Tcp(rand::random::<u16>())],
            multiaddr![Udp(rand::random::<u16>())],
            multiaddr![Udp(rand::random::<u16>())],
            multiaddr![Udp(rand::random::<u16>())],
            multiaddr![Udp(rand::random::<u16>())],
            multiaddr![Udp(rand::random::<u16>())],
        ]);

All these addresses require to produce a SwarmEvent::OutgoingConnectionError which seems to cover the original issue, doesn't it?
The test is easily extandable to ip6 ::1 and 127.0.0.1.

@thomaseizinger
Copy link
Contributor

We just need to change these addresses to something else then! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
difficulty:easy help wanted priority:important The changes needed are critical for libp2p, or are blocking another project
Projects
None yet
Development

No branches or pull requests

3 participants