Skip to content

Commit cb01001

Browse files
committed
feat: adds gateway detection
1 parent b4be4f2 commit cb01001

29 files changed

+309
-189
lines changed

Cargo.lock

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cli/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ ignored = ["prettytable-rs"]
2222

2323
[dependencies]
2424
clap = { version = "^4.5", features = ["derive"] }
25-
pnet = "^0.35"
2625
prettytable-rs = "^0.10"
2726
color-eyre = "0.6.5"
2827
log = "^0.4"

cli/README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -244,9 +244,9 @@ sudo r-lancli --debug
244244
+---------------+----------+-------------------+------------------------+
245245
| IP | HOSTNAME | MAC | VENDOR |
246246
+---------------+----------+-------------------+------------------------+
247-
| 192.168.1.1 | router | aa:bb:cc:dd:ee:ff | Netgear |
248-
| 192.168.1.100 | laptop | 11:22:33:44:55:66 | Apple, Inc. |
249-
| 192.168.1.150 | | 99:88:77:66:55:44 | Samsung Electronics |
247+
| 192.168.1.1 [GTWY] | router | aa:bb:cc:dd:ee:ff | Netgear |
248+
| 192.168.1.100 | laptop | 11:22:33:44:55:66 | Apple, Inc. |
249+
| 192.168.1.150 | | 99:88:77:66:55:44 | Samsung Electronics |
250250
+---------------+----------+-------------------+------------------------+
251251
```
252252

@@ -256,8 +256,8 @@ sudo r-lancli --debug
256256
+---------------+----------+-------------------+------------------------+-------------+
257257
| IP | HOSTNAME | MAC | VENDOR | OPEN_PORTS |
258258
+---------------+----------+-------------------+------------------------+-------------+
259-
| 192.168.1.1 | router | aa:bb:cc:dd:ee:ff | Netgear | 22, 80, 443 |
260-
| 192.168.1.100 | laptop | 11:22:33:44:55:66 | Apple, Inc. | 22, 5900 |
259+
| 192.168.1.1 [GTWY] | router | aa:bb:cc:dd:ee:ff | Netgear | 22, 80, 443 |
260+
| 192.168.1.100 | laptop | 11:22:33:44:55:66 | Apple, Inc. | 22, 5900 |
261261
+---------------+----------+-------------------+------------------------+-------------+
262262
```
263263

@@ -271,6 +271,7 @@ sudo r-lancli --debug
271271
"mac": "aa:bb:cc:dd:ee:ff",
272272
"vendor": "Netgear",
273273
"is_current_host": false,
274+
"is_gateway": true,
274275
"open_ports": [
275276
{ "id": 22, "service": "ssh" },
276277
{ "id": 80, "service": "http" },

cli/src/main.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use core::time;
1717
use itertools::Itertools;
1818
use r_lanlib::{
1919
error::Result as LibResult,
20-
network::{self, NetworkInterface},
20+
network::{self, NetworkInterface, get_default_gateway},
2121
packet,
2222
scanners::{
2323
Device, IDLE_TIMEOUT, ScanMessage, Scanner, arp_scanner::ARPScanner,
@@ -179,6 +179,8 @@ fn print_arp(args: &Args, devices: &Vec<Device>) -> Result<()> {
179179
for d in devices.iter() {
180180
let ip_field = if d.is_current_host {
181181
format!("{} [YOU]", d.ip)
182+
} else if d.is_gateway {
183+
format!("{} [GTWY]", d.ip)
182184
} else {
183185
d.ip.to_string()
184186
};
@@ -265,6 +267,8 @@ fn print_syn(
265267
for d in devices {
266268
let ip_field = if d.is_current_host {
267269
format!("{} [YOU]", d.ip)
270+
} else if d.is_gateway {
271+
format!("{} [GTWY]", d.ip)
268272
} else {
269273
d.ip.to_string()
270274
};
@@ -321,12 +325,8 @@ fn main() -> Result<()> {
321325
}
322326

323327
let interface = match &args.interface {
324-
Some(name) => network::get_interface(name).ok_or_else(|| {
325-
eyre!("Could not find network interface: {}", name)
326-
})?,
327-
None => network::get_default_interface().ok_or_else(|| {
328-
eyre!("Could not detect default network interface")
329-
})?,
328+
Some(name) => network::get_interface(name)?,
329+
None => network::get_default_interface()?,
330330
};
331331

332332
args.interface = Some(interface.name.clone());
@@ -346,6 +346,7 @@ fn main() -> Result<()> {
346346
let arp = ARPScanner::builder()
347347
.interface(Arc::clone(&interface))
348348
.wire(wire.clone())
349+
.gateway(get_default_gateway())
349350
.targets(
350351
IPTargets::new(args.targets.clone())
351352
.map_err(|e| eyre!("Invalid IP targets: {}", e))?,

cli/src/main_tests.rs

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use mockall::mock;
22
use mpsc::channel;
3-
use pnet::util::MacAddr;
43
use r_lanlib::{
54
error::Result,
65
scanners::{Port, PortSet, Scanner},
@@ -86,11 +85,8 @@ fn prints_arp_table_results() {
8685
let device = Device {
8786
hostname: "hostname".to_string(),
8887
ip: Ipv4Addr::new(192, 168, 1, 1),
89-
is_current_host: false,
90-
mac: MacAddr::default(),
9188
vendor: "vendor".to_string(),
92-
open_ports: PortSet::new(),
93-
latency_ms: None,
89+
..Device::default()
9490
};
9591

9692
print_arp(&args, &vec![device]).unwrap();
@@ -115,11 +111,8 @@ fn prints_arp_json_results() {
115111
let device = Device {
116112
hostname: "hostname".to_string(),
117113
ip: Ipv4Addr::new(192, 168, 1, 1),
118-
is_current_host: false,
119-
mac: MacAddr::default(),
120114
vendor: "vendor".to_string(),
121-
open_ports: PortSet::new(),
122-
latency_ms: None,
115+
..Device::default()
123116
};
124117

125118
print_arp(&args, &vec![device]).unwrap();
@@ -152,11 +145,9 @@ fn prints_syn_table_results() {
152145
let device = Device {
153146
hostname: "hostname".to_string(),
154147
ip: Ipv4Addr::new(192, 168, 1, 1),
155-
is_current_host: false,
156-
mac: MacAddr::default(),
157148
vendor: "vendor".to_string(),
158149
open_ports,
159-
latency_ms: None,
150+
..Device::default()
160151
};
161152

162153
let devices = HashMap::from([(device.ip, device)]);
@@ -190,11 +181,9 @@ fn prints_syn_json_results() {
190181
let device = Device {
191182
hostname: "hostname".to_string(),
192183
ip: Ipv4Addr::new(192, 168, 1, 1),
193-
is_current_host: false,
194-
mac: MacAddr::default(),
195184
vendor: "vendor".to_string(),
196185
open_ports,
197-
latency_ms: None,
186+
..Device::default()
198187
};
199188

200189
let devices = HashMap::from([(device.ip, device)]);
@@ -210,11 +199,8 @@ fn performs_arp_scan() {
210199
let device = Device {
211200
hostname: "hostname".to_string(),
212201
ip: Ipv4Addr::new(192, 168, 1, 1),
213-
is_current_host: false,
214-
mac: MacAddr::default(),
215202
vendor: "vendor".to_string(),
216-
open_ports: PortSet::new(),
217-
latency_ms: None,
203+
..Device::default()
218204
};
219205

220206
let device_clone = device.clone();
@@ -255,11 +241,9 @@ fn performs_syn_scan() {
255241
let device = Device {
256242
hostname: "hostname".to_string(),
257243
ip: Ipv4Addr::new(192, 168, 1, 1),
258-
is_current_host: false,
259-
mac: MacAddr::default(),
260244
vendor: "vendor".to_string(),
261245
open_ports: ports,
262-
latency_ms: None,
246+
..Device::default()
263247
};
264248

265249
let device_clone = device.clone();

lib/README.md

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,9 @@ let devices = vec![
106106
mac: MacAddr::new(0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff),
107107
vendor: "".to_string(),
108108
is_current_host: false,
109+
is_gateway: false,
109110
open_ports: PortSet::new(),
111+
latency_ms: None,
110112
}
111113
];
112114

@@ -180,9 +182,18 @@ let handle = scanner.scan().expect("Failed to start scan");
180182

181183
Provides helpers for selecting network interfaces:
182184

183-
- `get_default_interface()` - Get the default network interface
184-
- `get_interface(name)` - Get a specific interface by name
185-
- `get_available_port()` - Find an available port for scanning
185+
- `get_default_interface()` - Get the default network interface, returns `Result<NetworkInterface>`
186+
- `get_interface(name)` - Get a specific interface by name, returns `Result<NetworkInterface>`
187+
- `get_available_port()` - Find an available port for scanning, returns `Result<u16>`
188+
189+
#### `routing`
190+
191+
Provides OS-level routing table inspection:
192+
193+
- `get_default_gateway()` - Detect the default gateway IP address by parsing
194+
the system routing table (`netstat -rn` on macOS, `ip route show` on Linux).
195+
Returns `Option<Ipv4Addr>``None` if the gateway cannot be determined or
196+
the platform is unsupported.
186197

187198
#### `packet`
188199

@@ -219,7 +230,9 @@ pub struct Device {
219230
pub mac: MacAddr,
220231
pub vendor: String,
221232
pub is_current_host: bool,
233+
pub is_gateway: bool,
222234
pub open_ports: PortSet,
235+
pub latency_ms: Option<u128>,
223236
}
224237
```
225238

lib/examples/arp-scanner.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ use std::{
55
};
66

77
use r_lanlib::{
8-
network, packet,
8+
network::{self, get_default_gateway},
9+
packet,
910
scanners::{Device, ScanMessage, Scanner, arp_scanner::ARPScanner},
1011
targets::ips::IPTargets,
1112
};
@@ -38,6 +39,7 @@ fn main() {
3839
let scanner = ARPScanner::builder()
3940
.interface(interface)
4041
.wire(wire)
42+
.gateway(get_default_gateway())
4143
.targets(ip_targets)
4244
.source_port(source_port)
4345
.include_vendor(vendor)

lib/examples/syn-scanner.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ fn main() {
3636
mac: MacAddr::new(0x00, 0x00, 0x00, 0x00, 0x00, 0x01),
3737
vendor: "".to_string(),
3838
is_current_host: false,
39+
is_gateway: false,
3940
open_ports: PortSet::new(),
4041
latency_ms: None,
4142
},
@@ -45,6 +46,7 @@ fn main() {
4546
mac: MacAddr::new(0x00, 0x00, 0x00, 0x00, 0x00, 0x02),
4647
vendor: "".to_string(),
4748
is_current_host: false,
49+
is_gateway: false,
4850
open_ports: PortSet::new(),
4951
latency_ms: None,
5052
},
@@ -54,6 +56,7 @@ fn main() {
5456
mac: MacAddr::new(0x00, 0x00, 0x00, 0x00, 0x00, 0x03),
5557
vendor: "".to_string(),
5658
is_current_host: false,
59+
is_gateway: false,
5760
open_ports: PortSet::new(),
5861
latency_ms: None,
5962
},

lib/src/error.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ pub enum RLanLibError {
7777
#[error("failed to build heartbeat packet: {_0}")]
7878
HeartbeatPacketBuild(#[from] HeartbeatPacketBuilderError),
7979

80+
/// Errors generated accessing device interfaces
81+
#[error("network interface error: {_0}")]
82+
NetworkInterface(String),
83+
8084
/// Wrapping errors related to scanning
8185
#[error("scanning error: {error} - ip: {:#?}, port: {:#?}", ip, port)]
8286
Scan {

0 commit comments

Comments
 (0)