From 2f1966a098674de9ec706745209854bb564e6bed Mon Sep 17 00:00:00 2001 From: Ishan Jain Date: Tue, 7 Nov 2023 03:33:39 +0530 Subject: [PATCH] Added readme, the whole project --- .gitignore | 1 + Cargo.lock | 538 +++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 10 + README.md | 37 ++++ src/hosts.rs | 119 +++++++++++ src/main.rs | 31 +++ src/neighbour.rs | 76 +++++++ 7 files changed, 812 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 src/hosts.rs create mode 100644 src/main.rs create mode 100644 src/neighbour.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..7a98e93 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,538 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "futures" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" + +[[package]] +name = "futures-executor" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" + +[[package]] +name = "futures-macro" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" + +[[package]] +name = "futures-task" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" + +[[package]] +name = "futures-util" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + +[[package]] +name = "hermit-abi" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" + +[[package]] +name = "libc" +version = "0.2.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "netlink-packet-core" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72724faf704479d67b388da142b186f916188505e7e0b26719019c525882eda4" +dependencies = [ + "anyhow", + "byteorder", + "netlink-packet-utils", +] + +[[package]] +name = "netlink-packet-route" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053998cea5a306971f88580d0829e90f270f940befd7cf928da179d4187a5a66" +dependencies = [ + "anyhow", + "bitflags", + "byteorder", + "libc", + "netlink-packet-core", + "netlink-packet-utils", +] + +[[package]] +name = "netlink-packet-utils" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" +dependencies = [ + "anyhow", + "byteorder", + "paste", + "thiserror", +] + +[[package]] +name = "netlink-proto" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "842c6770fc4bb33dd902f41829c61ef872b8e38de1405aa0b938b27b8fba12c3" +dependencies = [ + "bytes", + "futures", + "log", + "netlink-packet-core", + "netlink-sys", + "thiserror", + "tokio", +] + +[[package]] +name = "netlink-sys" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6471bf08e7ac0135876a9581bf3217ef0333c191c128d34878079f42ee150411" +dependencies = [ + "bytes", + "futures", + "libc", + "log", + "tokio", +] + +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags", + "cfg-if", + "libc", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "proc-macro2" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rdns-ipv6" +version = "0.1.0" +dependencies = [ + "futures", + "netlink-packet-route", + "rtnetlink", + "tokio", +] + +[[package]] +name = "rtnetlink" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a552eb82d19f38c3beed3f786bd23aa434ceb9ac43ab44419ca6d67a7e186c0" +dependencies = [ + "futures", + "log", + "netlink-packet-core", + "netlink-packet-route", + "netlink-packet-utils", + "netlink-proto", + "netlink-sys", + "nix", + "thiserror", + "tokio", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "syn" +version = "2.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio" +version = "1.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..d08941c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "rdns-ipv6" +version = "0.1.0" +edition = "2021" + +[dependencies] +futures = "0.3.29" +netlink-packet-route = "0.17.1" +rtnetlink = "0.13.1" +tokio = { version = "1.33.0", features = ["macros", "rt-multi-thread"] } diff --git a/README.md b/README.md new file mode 100644 index 0000000..59691a5 --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +# rdns-ipv6 + +In dual stack networks, This project can help in rdns resolution of IPv6 addresses by reusing hostname from DHCPv4 leases for that client. + +It works like this, + +1. Read IPv6 neighbours on the machine. +2. Identify hostname for this IPv6 client by trying to find a IPv4 lease for this MAC address +3. Update `/etc/hosts` with a list of IPv4 and IPv6 records. + +There should be `unbound` or some other DNS resolver running on the machine to read `/etc/hosts` and respond to PTR queries and queries in your local search domain. +This program does not handle any aspect related to that. + + +This program exits after modifying `/etc/hosts`. It can be used with systemd to run at some interval. + +``` +# /etc/systemd/system/rdns-ipv6.timer +[Unit] +Description=rdns-ipv6 + +[Timer] +OnUnitActiveSec=10s +OnBootSec=10s + +[Install] +WantedBy=timers.target + + +#/etc/systemd/system/rdns-ipv6.Service +[Unit] +Description=RDNS for IPv6 + +[Service] +Type=oneshot +ExecStart=/config/rdns-ipv6 +``` diff --git a/src/hosts.rs b/src/hosts.rs new file mode 100644 index 0000000..0b56057 --- /dev/null +++ b/src/hosts.rs @@ -0,0 +1,119 @@ +use crate::neighbour::Record as NeighbourRecord; +use std::{ + fs::{File, OpenOptions}, + io::{Read, Write}, + net::{IpAddr, Ipv6Addr}, +}; + +pub struct HostsFile { + host_file_path: String, + records: Vec, +} + +#[derive(Debug)] +struct Record { + address: IpAddr, + hostname: String, +} + +pub const HOST_FILE_PATH: &str = "/etc/hosts"; + +impl HostsFile { + pub fn new(path: &str) -> Result { + let mut file = File::open(path).map_err(|e| e.to_string())?; + let mut buf = vec![]; + + file.read_to_end(&mut buf).map_err(|e| e.to_string())?; + + Ok(Self { + records: HostsFile::parse(&buf)?, + host_file_path: path.to_string(), + }) + } + + fn parse(data: &[u8]) -> Result, String> { + let mut output = vec![]; + for line in data.split(|&x| x == b'\n') { + let s = String::from_utf8_lossy(line); + + let s = s.trim(); + if s.starts_with('#') { + continue; + } + + if let Some((address, hostname)) = s.split_once(' ') { + output.push(Record { + address: address.parse::().map_err(|e| e.to_string())?, + hostname: hostname.to_string(), + }); + } + } + + Ok(output) + } + + pub fn retain_ipv4_only(&mut self) { + self.records.retain(|x| { + x.address.is_ipv4() + || ["fe00::0", "ff00::0", "ff02::1", "ff02::2"] + .map(|x| x.parse::().unwrap()) + .map(IpAddr::V6) + .contains(&x.address) + }); + } + + pub fn add_ipv6_clients( + &mut self, + ipv6_neigh: Vec, + ipv4_neigh: Vec, + ) { + for neighbour in ipv6_neigh { + // Lookup address from ipv4 neigh + let ipv4_neighbour = if let Some(v) = ipv4_neigh.iter().find(|x| x.lla == neighbour.lla) + { + v + } else { + continue; + }; + + let hostname = if let Some(v) = self + .records + .iter() + .find(|x| x.address == ipv4_neighbour.address) + { + v.hostname.clone() + } else { + continue; + }; + + self.records.push(Record { + address: neighbour.address, + hostname, + }); + } + } + + pub fn flush(self) -> Result<(), String> { + let mut output = vec![]; + + for record in self.records { + output.push(format!("{} {}", record.address, record.hostname)); + } + + let mut f = OpenOptions::new() + .read(false) + .write(true) + .create(true) + .append(false) + .truncate(true) + .open(self.host_file_path) + .map_err(|e| e.to_string())?; + + f.write_all(output.join("\n").as_bytes()) + .map_err(|e| e.to_string())?; + + println!("{}", output.join("\n")); + + f.flush().map_err(|e| e.to_string()) + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..d1bf601 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,31 @@ +#![allow(clippy::erasing_op)] +#![allow(clippy::identity_op)] +#![allow(clippy::eq_op)] + +use crate::hosts::HostsFile; +use rtnetlink::{new_connection, IpVersion}; + +mod hosts; +mod neighbour; + +#[tokio::main] +async fn main() -> Result<(), String> { + run().await.expect("error occured"); + + Ok(()) +} + +async fn run() -> Result<(), String> { + let (conn, handle, _) = new_connection().unwrap(); + + tokio::spawn(conn); + + let ipv4_neigh = neighbour::fetch_reachable_neighbours(handle.clone(), IpVersion::V4).await?; + let ipv6_neigh = neighbour::fetch_reachable_neighbours(handle, IpVersion::V6).await?; + + let mut hosts = HostsFile::new(hosts::HOST_FILE_PATH)?; + hosts.retain_ipv4_only(); + hosts.add_ipv6_clients(ipv6_neigh, ipv4_neigh); + + hosts.flush() +} diff --git a/src/neighbour.rs b/src/neighbour.rs new file mode 100644 index 0000000..d48b95b --- /dev/null +++ b/src/neighbour.rs @@ -0,0 +1,76 @@ +use futures::TryStreamExt; +use netlink_packet_route::rtnl::neighbour::nlas::Nla; +use rtnetlink::{Handle, IpVersion, NeighbourHandle}; +use std::net::{IpAddr, Ipv6Addr}; + +#[derive(Debug)] +pub struct Record { + pub lla: u64, + pub address: IpAddr, +} + +impl Default for Record { + fn default() -> Self { + Self { + lla: 0, + address: IpAddr::from([0, 0, 0, 0]), + } + } +} + +pub async fn fetch_reachable_neighbours( + handle: Handle, + version: IpVersion, +) -> Result, String> { + let mut output = vec![]; + let nh = NeighbourHandle::new(handle); + let mut resp = nh.get().set_family(version.clone()).execute(); + + while let Some(resp) = resp.try_next().await.map_err(|e| e.to_string())? { + let record: Record = resp.nlas.into_iter().fold(Default::default(), |mut r, x| { + match x { + Nla::LinkLocalAddress(d) if !d.is_empty() => r.lla = to_u64(&d), + Nla::Destination(d) => { + r.address = match version { + IpVersion::V4 => IpAddr::from([d[0], d[1], d[2], d[3]]), + IpVersion::V6 => IpAddr::V6(Ipv6Addr::from(to_u128(&d))), + }; + } + + _ => (), + }; + r + }); + + if record.lla != 0 { + output.push(record); + } + } + + Ok(output) +} + +#[inline] +const fn to_u64(v: &[u8]) -> u64 { + let mut out = 0; + + out |= (v[5] as u64) << ((5 - 5) * 8); + out |= (v[4] as u64) << ((5 - 4) * 8); + out |= (v[3] as u64) << ((5 - 3) * 8); + out |= (v[2] as u64) << ((5 - 2) * 8); + out |= (v[1] as u64) << ((5 - 1) * 8); + out |= (v[0] as u64) << ((5 - 0) * 8); + + out +} + +fn to_u128(v: &[u8]) -> u128 { + let mut out = 0; + let l = v.len() - 1; + + for (i, x) in v.iter().enumerate() { + out |= (*x as u128) << ((l - i) * 8); + } + + out +}