1use serde::Deserialize;
2use serde_json;
3use serde_json::error::Error as JsonError;
4
5use std::collections::HashMap;
6use std::io::Error as IoError;
7use std::net::IpAddr;
8use std::process::Command;
9use std::str::Utf8Error;
10
11#[derive(Clone, Deserialize, Debug, Eq, Hash, PartialEq)]
12pub struct LlAddr(String);
13impl LlAddr {
14 pub fn from_string(mut s: String) -> Self {
15 s = s.to_lowercase();
16 LlAddr(s)
17 }
18 pub fn as_str(&self) -> &str {
19 self.0.as_str()
20 }
21}
22
23#[derive(Deserialize, Debug)]
24struct Neighbor {
25 dst: IpAddr,
26 dev: String,
27 lladdr: Option<LlAddr>,
28 state: Vec<State>,
29}
30#[derive(Deserialize, Debug)]
31#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
32enum State {
33 Delay,
34 Failed,
35 Incomplete,
36 Reachable,
37 Stale,
38}
39
40#[derive(Debug)]
41pub enum LookupError {
42 IoError(IoError),
43 Utf8Error(Utf8Error),
44 JsonError(JsonError),
45}
46impl From<IoError> for LookupError {
47 fn from(e: IoError) -> LookupError {
48 LookupError::IoError(e)
49 }
50}
51impl From<Utf8Error> for LookupError {
52 fn from(e: Utf8Error) -> LookupError {
53 LookupError::Utf8Error(e)
54 }
55}
56impl From<JsonError> for LookupError {
57 fn from(e: JsonError) -> LookupError {
58 LookupError::JsonError(e)
59 }
60}
61
62#[cfg(target_os = "linux")]
63fn neighbors_raw() -> Result<Vec<Neighbor>, LookupError> {
64 let json = Command::new("ip")
65 .arg("--json")
66 .arg("neighbor")
67 .stderr(std::process::Stdio::inherit())
68 .output()?
69 .stdout;
70 Ok(serde_json::from_str(std::str::from_utf8(json.as_ref())?)?)
71}
72#[cfg(target_os = "linux")]
73pub fn neighbors() -> Result<HashMap<IpAddr, LlAddr>, LookupError> {
74 let neighbors: Vec<Neighbor> = neighbors_raw()?;
81 let neighbors_map = neighbors
82 .into_iter()
83 .filter_map(|n| match n {
84 Neighbor {
85 lladdr: Some(l), ..
86 } => Some((n.dst, l)),
87 Neighbor { lladdr: None, .. } => None,
88 })
89 .collect();
90 Ok(neighbors_map)
91}
92#[cfg(target_os = "linux")]
93pub fn lookup(ip_addr: IpAddr) -> Result<Option<LlAddr>, LookupError> {
94 let ip_addr = normalize_ip_addr(ip_addr);
95 let neighbors: Vec<Neighbor> = neighbors_raw()?;
96 let lladdr = neighbors
97 .into_iter()
98 .filter(|n| n.dst == ip_addr)
99 .filter_map(|n| n.lladdr)
100 .nth(0);
101 Ok(lladdr)
103}
104
105fn normalize_ip_addr(ip_addr: IpAddr) -> IpAddr {
106 match ip_addr {
107 IpAddr::V4(_) => ip_addr,
108 IpAddr::V6(v6) => match v6.to_ipv4() {
109 Some(converted_v4) => IpAddr::V4(converted_v4),
110 None => ip_addr,
111 },
112 }
113}
114
115#[cfg(test)]
116#[cfg(target_os = "linux")]
117mod tests {
118 use super::*;
119
120 #[test]
121 fn test_neighbors() {
122 assert!(neighbors().unwrap().keys().count() > 0);
123 }
124
125 #[test]
126 fn test_lookup() {
127 for (ip, lladdr) in neighbors().unwrap().into_iter() {
128 assert_eq!(lookup(ip).unwrap().unwrap(), lladdr);
129 }
130 }
131}