Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 390ce43

Browse files
committed
rel: v0.2.0
* Add looking up single mac address
1 parent 4ccdc2a commit 390ce43

4 files changed

Lines changed: 87 additions & 3 deletions

File tree

Cargo.lock

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

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "arpbox"
3-
version = "0.1.0"
3+
version = "0.2.0"
44
edition = "2024"
55
authors = ["wcampbell"]
66
description = "Enrich ARP data with device information from NetBox"

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,12 @@ This uses the `Bearer nbt_<key>.<token>` authorization header.
4141

4242
### Run
4343

44-
Pipe arp-scan into arpbox.
44+
Look up a single MAC address:
45+
```console
46+
$ arpbox 00:11:22:33:44:55
47+
```
48+
49+
Or pipe arp-scan into arpbox:
4550
```console
4651
$ arp-scan --localnet | arpbox
4752
```

src/main.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,70 @@ fn lookup_mac(
9494
result
9595
}
9696

97+
fn lookup_mac_verbose(
98+
client: &reqwest::blocking::Client,
99+
config: &Config,
100+
mac: &str,
101+
) -> Result<Option<String>, String> {
102+
let url = format!(
103+
"{}/api/dcim/interfaces/?mac_address={}",
104+
config.netbox_url.trim_end_matches('/'),
105+
mac
106+
);
107+
108+
let auth = match &config.netbox_key {
109+
Some(key) => format!("Bearer nbt_{}.{}", key, config.netbox_token),
110+
None => format!("Token {}", config.netbox_token),
111+
};
112+
113+
let resp = client
114+
.get(&url)
115+
.header("Authorization", &auth)
116+
.header("Accept", "application/json")
117+
.send()
118+
.map_err(|e| format!("request failed: {}", e))?;
119+
120+
if !resp.status().is_success() {
121+
return Err(format!("HTTP {}: {}", resp.status(), resp.status().canonical_reason().unwrap_or("unknown")));
122+
}
123+
124+
let body: NetBoxResponse = resp
125+
.json()
126+
.map_err(|e| format!("failed to parse response: {}", e))?;
127+
128+
let iface = match body.results.into_iter().next() {
129+
Some(i) => i,
130+
None => return Ok(None),
131+
};
132+
133+
let device_url = match &iface.device {
134+
Some(d) => &d.url,
135+
None => return Ok(None),
136+
};
137+
138+
let resp = client
139+
.get(device_url)
140+
.header("Authorization", &auth)
141+
.header("Accept", "application/json")
142+
.send()
143+
.map_err(|e| format!("device request failed: {}", e))?;
144+
145+
if !resp.status().is_success() {
146+
return Err(format!("device HTTP {}: {}", resp.status(), resp.status().canonical_reason().unwrap_or("unknown")));
147+
}
148+
149+
let device: NetBoxDeviceFull = resp
150+
.json()
151+
.map_err(|e| format!("failed to parse device response: {}", e))?;
152+
153+
let name = match device.asset_tag {
154+
Some(tag) => format!("{}:{}:{}", device.name, tag, iface.name),
155+
None => format!("{}:{}", device.name, iface.name),
156+
};
157+
158+
Ok(Some(name))
159+
}
160+
97161
fn main() {
98162
let config = match load_config() {
99163
Ok(c) => c,
@@ -104,6 +168,21 @@ fn main() {
104168
};
105169

106170
let client = reqwest::blocking::Client::new();
171+
172+
let args: Vec<String> = std::env::args().collect();
173+
if args.len() == 2 {
174+
let mac = &args[1];
175+
match lookup_mac_verbose(&client, &config, mac) {
176+
Ok(Some(device)) => println!("(netbox://{})", device),
177+
Ok(None) => println!("No device found for {}", mac),
178+
Err(e) => {
179+
eprintln!("Error: {}", e);
180+
std::process::exit(1);
181+
}
182+
}
183+
return;
184+
}
185+
107186
let mut cache: HashMap<String, Option<String>> = HashMap::new();
108187
let stdin = io::stdin();
109188

0 commit comments

Comments
 (0)