netwatcher is a cross-platform Rust library for enumerating network interfaces and their IP addresses, featuring the ability to watch for changes to those interfaces efficiently. It uses platform-specific methods to detect when interface changes have occurred instead of polling, which means that you find out about changes more quickly and there is no CPU or wakeup overhead when nothing is happening.
Sync and async APIs are available, with no extra dependencies for sync users. If you are using tokio, enable feature tokio. For async-io, enable async-io. Other reactors may be used by implementing the appropriate traits.
| Platform | Min Version | List | Watch | Notes |
|---|---|---|---|---|
| Windows | - | ✅ | ✅ | |
| Mac | - | ✅ | ✅ | Callback watch creates background thread. |
| Linux | - | ✅ | ✅ | Callback watch creates background thread. |
| iOS | - | ✅ | ✅ | Callback watch creates background thread. |
| Android | 5.0 | ✅ | ✅ | Watch requires extra setup. See Android Setup instructions below. |
| BSD | - | ✅ | ✅ | FreeBSD 15.0 is tested in CI. |
Use list_interfaces.
// Returns a HashMap from ifindex (a `u32`) to an `Interface` struct.
let interfaces = netwatcher::list_interfaces().unwrap();
for i in interfaces.values() {
println!("interface {} has {} IPs", i.name, i.ips.len());
}Choose one of the three options:
- Sync callback:
watch_interfaces_with_callback - Sync blocking:
watch_interfaces_blocking - Async:
watch_interfaces_async::<T>
Deliver change notifications to a callback.
let handle = netwatcher::watch_interfaces_with_callback(|update| {
// All watch types will fire immediately with initial interface state
println!("Is initial update: {}", update.is_initial);
println!("Current interface map: {:#?}", update.interfaces);
// Interfaces may appear or disappear entirely.
for ifindex in &update.diff.added {
println!("ifindex {} was added", ifindex);
}
for ifindex in &update.diff.removed {
println!("ifindex {} was removed", ifindex);
}
// Existing interfaces may gain or lose IPs.
for (ifindex, diff) in &update.diff.modified {
let interface = &update.interfaces[ifindex];
for addr in &diff.addrs_added {
println!("{} gained {}/{}", interface.name, addr.ip, addr.prefix_len);
}
for addr in &diff.addrs_removed {
println!("{} lost {}/{}", interface.name, addr.ip, addr.prefix_len);
}
}
})
.unwrap();
// Keep `handle` alive as long as you want callbacks.
// ...
drop(handle);Park the current thread until a change notification is available.
let mut watch = netwatcher::watch_interfaces_blocking().unwrap();
loop {
let update = watch.changed();
println!("Initial update: {}", update.is_initial);
println!("Current interface map: {:#?}", update.interfaces);
}.await interface changes. This requires a small amount of integration with your async runtime. You will probably want to enable a crate feature such as tokio or async-io to use the provided adapter.
use netwatcher::async_adapter::Tokio;
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
runtime.block_on(async {
let mut watch = netwatcher::watch_interfaces_async::<Tokio>().unwrap();
loop {
let update = watch.changed().await;
println!("Initial update: {}", update.is_initial);
println!("Current interface map: {:#?}", update.interfaces);
}
});Ensure the app module which is going to end up running netwatcher has these permissions:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />You will also need to make sure that netwatcher gets access to the Android app's Context. There is built-in support for the ndk-context crate. What this means is that if you're using certain frameworks for building all-Rust Android apps then it will be able to pick up the context automatically. In other situations, the Rust code in your app will have to call netwatcher::set_android_context (example code).
There is a test app included in the repo that provides a full example. MainActivity.kt is an activity with some methods defined in Rust. app-native/src/lib.rs provides the native implementations of those methods. This includes an example of calling set_android_context, and using the netwatcher library to watch for interface changes, passing the results back to the Java GUI.
MIT