aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHimbeerserverDE <himbeerserverde@gmail.com>2023-05-07 20:01:34 +0200
committerHimbeerserverDE <himbeerserverde@gmail.com>2023-05-07 20:01:34 +0200
commit1c39727c4ac81120828afafe28b750f32b981bf8 (patch)
treeec3f5ee61c22314180bb5259e30d5361d6f4687d
parent5c69c25fca3ef40bafea6840ab7c2cb554894ee2 (diff)
hand out addresses from the prefixes assigned to the corresponding interface
-rw-r--r--Cargo.lock21
-rw-r--r--Cargo.toml3
-rw-r--r--src/main.rs124
3 files changed, 124 insertions, 24 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 15cdb74..3e8a68a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -212,6 +212,12 @@ dependencies = [
]
[[package]]
+name = "ipnet"
+version = "2.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f"
+
+[[package]]
name = "itoa"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -244,6 +250,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edc207893e85c5d6be840e969b496b53d94cec8be2d501b214f50daa97fa8024"
[[package]]
+name = "linkaddrs"
+version = "0.1.0"
+source = "git+https://github.com/HimbeerserverDE/linkaddrs.git#412f8341bc1631acdae5ded45e53a23d93477e63"
+dependencies = [
+ "futures",
+ "ipnet",
+ "netlink-packet-route",
+ "rtnetlink",
+ "tokio",
+]
+
+[[package]]
name = "log"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -513,9 +531,12 @@ dependencies = [
name = "rsdsl_radvd"
version = "0.1.0"
dependencies = [
+ "ipnet",
+ "linkaddrs",
"pnet_packet",
"rsdsl_netlinkd",
"socket2 0.5.2",
+ "thiserror",
]
[[package]]
diff --git a/Cargo.toml b/Cargo.toml
index 6390dea..5455f81 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,6 +4,9 @@ version = "0.1.0"
edition = "2021"
[dependencies]
+ipnet = "2.7.1"
+linkaddrs = { git = "https://github.com/HimbeerserverDE/linkaddrs.git", version = "0.1.0" }
pnet_packet = "0.33.0"
rsdsl_netlinkd = { git = "https://github.com/rsdsl/netlinkd.git", version = "0.3.0" }
socket2 = { version = "0.5.2", features = ["all"] }
+thiserror = "1.0"
diff --git a/src/main.rs b/src/main.rs
index c199dce..29a7199 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,18 +1,47 @@
use std::io::{self, Read};
use std::net::{Ipv6Addr, SocketAddrV6};
+use std::thread;
+use std::time::Duration;
+use ipnet::Ipv6Net;
use pnet_packet::icmpv6::ndp::{MutableRouterAdvertPacket, NdpOption, NdpOptionType, RouterAdvert};
use pnet_packet::icmpv6::{Icmpv6Code, Icmpv6Type};
-use rsdsl_netlinkd::error::Result;
use rsdsl_netlinkd::link;
use socket2::{Domain, Protocol, Socket, Type};
+use thiserror::Error;
+
+#[derive(Debug, Error)]
+enum Error {
+ #[error("io: {0}")]
+ Io(#[from] io::Error),
+ #[error("linkaddrs: {0}")]
+ LinkAddrs(#[from] linkaddrs::Error),
+ #[error("rsdsl_netlinkd: {0}")]
+ RsdslNetlinkd(#[from] rsdsl_netlinkd::error::Error),
+}
+
+type Result<T> = std::result::Result<T, Error>;
fn main() -> Result<()> {
+ for i in 1..=4 {
+ let vlan_id = i * 10;
+ let vlan_name = format!("eth0.{}", vlan_id);
+
+ thread::spawn(move || match run(vlan_name.clone()) {
+ Ok(_) => {}
+ Err(e) => println!("[radvd] can't init {}: {}", vlan_name, e),
+ });
+ }
+
run("eth0".into())?;
Ok(())
}
fn run(link: String) -> Result<()> {
+ println!("[radvd] wait for {}", link);
+ link::wait_up(link.clone())?;
+ thread::sleep(Duration::from_secs(1));
+
println!("[radvd] init {}", link);
let ifi = link::index(link.clone())?;
@@ -22,7 +51,21 @@ fn run(link: String) -> Result<()> {
sock.join_multicast_v6(&Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 2), ifi)?;
sock.set_multicast_hops_v6(255)?;
- send_ra_multicast(&sock, &link, ifi)?;
+ // Periodically send NDP RAs so SLAAC addresses don't expire.
+ // The interval is five minutes shorter than the preferred lifetime.
+ let sock2 = sock.try_clone()?;
+ let link2 = link.clone();
+ thread::spawn(move || loop {
+ match send_ra_multicast(&sock2, &link2, ifi) {
+ Ok(_) => {}
+ Err(e) => println!(
+ "[radvd] warning: can't send ra multicast on {}: {}",
+ link2, e
+ ),
+ }
+
+ thread::sleep(Duration::from_secs(1200));
+ });
let mut buf = [0; 1500];
loop {
@@ -32,13 +75,47 @@ fn run(link: String) -> Result<()> {
// Router Solicitation
if buf[0] == 133 {
println!("[radvd] recv nd-rs on {}", link);
- send_ra_multicast(&sock, &link, ifi)?;
+
+ match send_ra_multicast(&sock, &link, ifi) {
+ Ok(_) => {}
+ Err(e) => println!(
+ "[radvd] warning: can't send ra multicast on {}: {}",
+ link, e
+ ),
+ }
}
}
}
-fn send_ra_multicast(sock: &Socket, link: &str, ifi: u32) -> io::Result<()> {
+fn send_ra_multicast(sock: &Socket, link: &str, ifi: u32) -> Result<()> {
let all_nodes = SocketAddrV6::new(Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 1), 0, 0, ifi).into();
+ let global = Ipv6Net::new(Ipv6Addr::new(0x2000, 0, 0, 0, 0, 0, 0, 0), 3).unwrap();
+
+ let mut ndp_opts = Vec::new();
+ let mut prefs = Vec::new();
+
+ for prefix in linkaddrs::ipv6_addresses(link.to_owned())?
+ .into_iter()
+ .filter(|addr| global.contains(addr))
+ {
+ let mut prefix_data = [
+ 64, // Prefix Length, always /64
+ 0xc0, // Flags: On-Link + SLAAC
+ 0, 0, 0x07, 0x08, // Valid Lifetime: 1800s
+ 0, 0, 0x05, 0xdc, // Preferred Lifetime: 1500s
+ 0, 0, 0, 0, // Reserved
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Prefix (inserted later)
+ ];
+ prefix_data[14..].copy_from_slice(&prefix.trunc().addr().octets());
+
+ prefs.push(prefix);
+
+ ndp_opts.push(NdpOption {
+ option_type: NdpOptionType::new(3),
+ length: 4,
+ data: prefix_data.to_vec(),
+ });
+ }
let adv = RouterAdvert {
icmpv6_type: Icmpv6Type::new(134),
@@ -49,33 +126,32 @@ fn send_ra_multicast(sock: &Socket, link: &str, ifi: u32) -> io::Result<()> {
lifetime: 1800,
reachable_time: 0,
retrans_time: 0,
- options: vec![
- NdpOption {
- option_type: NdpOptionType::new(3),
- length: 4,
- data: vec![
- 64, 0b11000000, 0, 0, 0, 30, 0, 0, 0, 20, 0, 0, 0, 0, 0x20, 0x01, 0xab, 0xab,
- 0xab, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- ],
- },
- /*NdpOption {
- option_type: NdpOptionType::new(25),
- length: 3,
- data: vec![
- 0, 0, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 1,
- ],
- },*/
- ],
+ options: ndp_opts.clone(),
+ /*NdpOption {
+ option_type: NdpOptionType::new(25),
+ length: 3,
+ data: vec![
+ 0, 0, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1,
+ ],
+ },*/
payload: vec![],
};
- let mut buf = [0; 16 + 32];
+ let mut buf = Vec::new();
+ buf.resize(16 + (32 * ndp_opts.len()), 0);
+
let mut pkt = MutableRouterAdvertPacket::new(&mut buf).unwrap();
pkt.populate(&adv);
sock.send_to(&buf, &all_nodes)?;
- println!("[radvd] send multicast nd-ra ::/64 on {}", link);
+ let prefixes = prefs
+ .into_iter()
+ .map(|prefix| format!("{}", prefix))
+ .reduce(|acc, prefix| acc + &prefix)
+ .unwrap_or(String::from("::/64"));
+
+ println!("[radvd] send multicast nd-ra {} on {}", prefixes, link);
Ok(())
}