ll_neighbors/
lib.rs

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 mut h = HashMap::new();
75    //h.insert(
76    //    IpAddr::V6("::1".parse().unwrap()),
77    //    LlAddr("00:28:f8:6f:b1:c0".to_string()),
78    //);
79    //return Ok(h);
80    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    //return Ok(Some(LlAddr("00:28:f8:6f:b1:c0".to_string())));
102    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}