imsnif / bandwhich

Terminal bandwidth utilization tool
MIT License
10.14k stars 298 forks source link

Fails to listen to network interface on Catalina #31

Closed leonaves closed 4 years ago

leonaves commented 4 years ago

On Catalina (10.15.2):

โžœ sudo what
Password:
Error: Failed to listen on network interface: Device not configured (os error 6)
hobofan commented 4 years ago

I think this is might be related to SIP, which is on by default. See https://apple.stackexchange.com/questions/208478/how-do-i-disable-system-integrity-protection-sip-aka-rootless-on-macos-os-x

ztj commented 4 years ago

It seems to be picking an unused/inactive interface by default. You can directly tell it which interface to use with the -i parameter. However, when doing this in my attempts so far, it displays no traffic use despite there definitely being traffic.

To be clear, it does show the UI and there is no longer the mentioned error if, for example, I run sudo what en0 on my system where en0 is the name of the interface that is my top priority default ethernet port. The app just claims 0 bytes traffic and has empty boxes.

atulatri commented 4 years ago

I am seeing same issue on Mojave Version 10.14.5

jbpratt commented 4 years ago

I am also seeing no traffic displayed. (Mojave Version 10.14.6)

IvRRimum commented 4 years ago

Same

Vengarioth commented 4 years ago

chances are there's a tool called what already installed on osx. use sudo ~/.cargo/bin/what -i en0 to be sure to use the right tool

Maximophone commented 4 years ago

Same problem here. I tried all network interfaces with the "-i" argument, all of them show no traffic, except en5 which still outputs the "device not configured" error

Maaxion commented 4 years ago

Mojave 10.14.6, doesn't run without sudo. With sudo, it starts but does not display any data.

xenio commented 4 years ago

Same empty screen for me on High Sierra. Using sudo or not same empty screen. I am on hackintosh so no SIP for me at all. Listening for en0 does not improve. :(

grishy commented 4 years ago

I think I found why an error occurs without specifying an interface. We get all the interfaces and loop around them. If some interface does not active, then we return an error. Therefore, even one non-working interface breaks everything.

https://github.com/imsnif/what/blob/c9b9025577d0e1f831c9ebb054a5ac00b7a89796/src/os/shared.rs#L94-L97 And... https://github.com/imsnif/what/blob/b150bf3ef49d38b8839c19cd711fb91cbdc20f38/src/os/shared.rs#L33-L43

In get_datalink_channel we get an error. Please check me out, I started learning Rust a couple of days ago :-)

imsnif commented 4 years ago

@Grishy - this looks like the issue and you describe it perfectly. :) Would you be interested in working on a PR to fix it? I'd be happy to provide guidance.

The no-traffic issue could, as @hobofan mentioned, be related to not being able to run lsof properly (which is how we get process information on MacOS). We can discuss that issue further here: https://github.com/imsnif/what/issues/34 - would be happy to hear input for all who experience it.

grishy commented 4 years ago

@imsnif That would be great! But I'm afraid you will be a little disappointed ๐Ÿ˜… I have not done PR yet and have not programmed in Rust (just debug your code 3 min). But I will try. I have little experience with the network (from Golang) and I have a macOS :)

imsnif commented 4 years ago

@Grishy - I promise not to be disappointed, and since there is a workaround for this issue (using the -i flag), there is no urgency. Take as much time as you need, and ping me here or privately through email for any help whatsoever.

ebroto commented 4 years ago

@Grishy, just some hint in case it helps :)

Something that would work would be to make get_datalink_channel return

Result<Option<Box<dyn DataLinkReceiver>>, failure::Error>

(note the Box being wrapped in Option) and returning a None in case there is an error listening. Then in the first snippet you linked you can filter the Nones. Take a look into filter_map and flatten.

EDIT: Thinking a bit more about it, maybe we can even get rid of the Result if we don't want to fail here. We should also check at some point that the vector of interfaces is not empty.

grishy commented 4 years ago

I have now set up the development environment and was able to start the project normally in debug mode ๐ŸŽ‰ At the moment I do not understand what the word dyn is responsible for... Do I understand correctly that we need an additional Option because we need to return something with error. We cannot return something like:

    return nil, failure::bail!("Unknown interface type");

because we do not have in Rust null pointers. It's just that I'm looking at examples now and everything is much simpler here) https://doc.rust-lang.org/stable/rust-by-example/error/iter_result.html

Also, the compiler itself can recognize

match datalink::channel(interface, config) {
    Ok(Ethernet(_tx, rx)) => Ok(rx),
// ...

like

Result<Box<dyn DataLinkReceiver>, failure::Error>

Or are we somehow helping him?

ritchiea commented 4 years ago

I'm having the same issue on Mojave 10.14.6.

Neither sudo what nor sudo what -i en0 show any network activity.

grishy commented 4 years ago

@rtulip You have no error as in the issue header when starting without specifying an interface?

ebroto commented 4 years ago

@Grishy dyn is a way to make explicit that DataLinkReceiver is a trait object. This would be similar to an interface in golang. As you say the compiler understands Ok(rx) as the Ok variant of the Result, and rx is the aforementioned trait object; because the size of the trait object is not known (it could be any type implementing DataLinkReceiver) we need a way to refer to it indirectly, a pointer, and in this case the actual object implementing DataLinkReceiver is in the heap (this is the box).

About a possible solution, we want to avoid bailing out because that would turn the Result into the Err variant, and when we hit this question mark: https://github.com/imsnif/what/blob/a935397e66c96d989e7b81157ee603b0a58664d2/src/os/shared.rs#L97 we will return early if we find an error (collect is turning a Vec<Result<T, U>> into a Result<Vec<T>, U> under the hood).

So one think we can do would be to make get_datalink_channel return an Option<Box<dyn DataLinkReceiver>> (without the result) so we can end up with a Vec<Option<T>> and turn that into a Vec<T>, checking at some point that the vector is not empty. Maybe @imsnif has another idea but I think we could check that in try_main (main.rs).

Let me know if this helps!

grishy commented 4 years ago

@ebroto Thanks! I got to the point where the code after my changes at least compiles ๐ŸŽ‰ x2

    let network_frames = network_interfaces
        .iter()
        .map(|iface| get_datalink_channel(iface))
        .filter_map(Result::ok)
        .collect::<Vec<_>>();

Itโ€™s bad that we drown out the errors, although it is not clear what to do with them (output to the console?) Maybe use partition() and display? With Option, we also lose errors.

ebroto commented 4 years ago

Cool!

IMO it's OK in this case to ignore the errors as it means that the interfaces are not usable for us. We should error out though in case there are no available interfaces.

grishy commented 4 years ago

@ebroto ok, add check on len == 0 and create PR? I just started looking for how to get connections in macOS.

ebroto commented 4 years ago

Sure, open the PR and we can discuss it there :)

grishy commented 4 years ago

open https://github.com/imsnif/bandwhich/pull/49

virtualritz commented 4 years ago

+1 On macOS 10.15.2:

> bandwhich -i en0
Error: Failed to listen on network interface: No such file or directory (os error 2)
> sudo bandwhich -i en0

Does run but doesnโ€™t display any data.

imsnif commented 4 years ago

The interface error has been fixed in 0.7.0. The no-traffic error has been partially fixed (we have a workaround that should work in most cases), and are working on a permanent fix for all cases - it's dependent on an external dependency. To follow the progress of that other issue, see: https://github.com/imsnif/bandwhich/issues/51

EDIT: Almost forgot to mention - thanks @Grishy for the fix!!

virtualritz commented 4 years ago

I can't confirm. I run bandwhich 0.7.0 on Catalina 10.15.2 and the symptoms are exactly the same as before.

Without sudo I get the Failed to find any network interface to listen on. error. With sudo I get an empty interface (no traffic displayed).

imsnif commented 4 years ago

Hey @virtualritz, you can follow the progress of the empty interface issue here: https://github.com/imsnif/bandwhich/issues/51

virtualritz commented 4 years ago

@imsnif: ok, re. the empty interface. But what about the Failed to find any network interface to listen on. error? Is bandwhich meant to only work with root privileges?

alexcroox commented 4 years ago

I have the version just released on homebrew. It needs sudo to run, doesn't seem to need you to specify the interface either, but sudo is definitely needed to prevent the Failed to find any network interface to listen on error.

imsnif commented 4 years ago

Yeah, I'm afraid sudo is definitely required in order to run. OSes don't like giving permissions to sniff network packets without it (and rightfully so :) ).

On the other hand, we just put an error that should tell you to use sudo if you did not. I'm sorry it didn't show in your case! Maybe it doesn't work on a mac?

Would one of you be willing to open an issue for this? You should be told to use 'sudo'.

virtualritz commented 4 years ago

I did some digging and indeed the code in os/shared.rs that deals with this doesn't trigger on macOS because the ErrorKind returned when ran w/o sudo on Catalina is actually ErrorKind::NotFound, not ErrorKind::PermissionDenied.

virtualritz commented 4 years ago

This quick fix in os/shared.rs works for me on Catalina to get the Try running with sudo. error:

if (ErrorKind::PermissionDenied == iface_error.kind())
    || (cfg!(target_os = "macos") && (ErrorKind::NotFound == iface_error.kind()))
imsnif commented 4 years ago

Thanks @virtualritz - I'm opening a new issue for this. Feel free to pick it up!