diff options
author | HimbeerserverDE <himbeerserverde@gmail.com> | 2023-10-14 16:35:53 +0200 |
---|---|---|
committer | HimbeerserverDE <himbeerserverde@gmail.com> | 2023-10-14 16:35:53 +0200 |
commit | 784bfebab14ddcef46c01c04287a418c732cb3fb (patch) | |
tree | bcd2b67649034fb1a1cb5f9206ba5268dd1a2eb8 /src | |
parent | 45f3d50078569c68a73d651cf11a3e5f2e1dcb64 (diff) |
initialize state from disk on startup
Diffstat (limited to 'src')
-rw-r--r-- | src/error.rs | 2 | ||||
-rw-r--r-- | src/main.rs | 489 |
2 files changed, 31 insertions, 460 deletions
diff --git a/src/error.rs b/src/error.rs index b61846a..d8ddd4b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -32,8 +32,6 @@ pub enum Error { DhcprotoDecode(#[from] dhcproto::error::DecodeError), #[error("dhcproto encode: {0}")] DhcprotoEncode(#[from] dhcproto::error::EncodeError), - #[error("rsdsl_netlinkd: {0}")] - RsdslNetlinkd(#[from] rsdsl_netlinkd::error::Error), #[error("serde_json: {0}")] SerdeJson(#[from] serde_json::Error), } diff --git a/src/main.rs b/src/main.rs index 78f7a51..f7b2b77 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,7 @@ +#![allow(unused)] + use std::ffi::CString; -use std::fs::File; +use std::fs::{self, File}; use std::mem::MaybeUninit; use std::net::{Ipv6Addr, SocketAddr, SocketAddrV6}; use std::os::fd::AsRawFd; @@ -8,53 +10,56 @@ use std::process; use std::str::FromStr; use std::sync::{Arc, Mutex}; use std::thread; -use std::time::{Duration, Instant}; +use std::time::{Duration, Instant, SystemTime}; use dhcproto::v6::{duid::Duid, DhcpOption, IAPrefix, Message, MessageType, OptionCode, IAPD, ORO}; use dhcproto::{Decodable, Decoder, Encodable, Encoder, Name}; use rsdsl_dhcp6::util::setsockopt; use rsdsl_dhcp6::{Error, Result}; use rsdsl_ip_config::DsConfig; -use rsdsl_netlinkd::link; use rsdsl_pd_config::PdConfig; use socket2::{Domain, SockAddr, Socket, Type}; use trust_dns_proto::serialize::binary::BinDecodable; const BUFSIZE: usize = 1500; +const DUID_LOCATION: &str = "/data/dhcp6.duid"; #[derive(Clone, Debug, Eq, PartialEq)] -enum State { - Solicit(Vec<u8>), - Request(Vec<u8>, Vec<u8>, [u8; 3], IAPD, usize), - Active(Vec<u8>, Vec<u8>, IAPD, Instant, u32), - Renew(Vec<u8>, Vec<u8>, IAPD, usize), +struct Dhcp6 { + duid: Duid, + lease: Option<PdConfig>, } -impl Default for State { - fn default() -> Self { - Self::Solicit( - Duid::uuid(&rand::random::<u128>().to_be_bytes()) - .as_ref() - .to_vec(), - ) +impl Dhcp6 { + fn load_from_disk() -> Result<Self> { + let mut lease_file = File::open(rsdsl_pd_config::LOCATION)?; + + Ok(Self { + duid: load_or_generate_duid()?, + lease: load_lease_optional(), + }) } } -fn main() -> Result<()> { - let mut file = File::open(rsdsl_ip_config::LOCATION)?; - let dsconfig: DsConfig = serde_json::from_reader(&mut file)?; - - if dsconfig.v6.is_none() { - println!("ignore incapable ppp0"); +fn load_or_generate_duid() -> Result<Duid> { + match fs::read("/data/dhcp6.duid") { + Ok(duid) => Ok(duid.into()), + Err(_) => { + let duid = Duid::uuid(&rand::random::<u128>().to_be_bytes()); + fs::write("/data/dhcp6.duid", &duid)?; - loop { - thread::sleep(Duration::MAX); + Ok(duid) } } +} - println!("init ppp0"); +fn load_lease_optional() -> Option<PdConfig> { + let mut file = File::open(rsdsl_pd_config::LOCATION).ok()?; + serde_json::from_reader(&mut file).ok() +} - let state = Arc::new(Mutex::new(State::default())); +fn main() -> Result<()> { + let dhcp6 = Dhcp6::load_from_disk()?; let sock = Socket::new(Domain::IPV6, Type::DGRAM, None)?; @@ -81,20 +86,6 @@ fn main() -> Result<()> { let address = SocketAddr::from_str("[::]:546")?; sock.bind(&address.into())?; - let sock2 = sock.try_clone()?; - let state2 = state.clone(); - thread::spawn(move || loop { - match tick(&sock2, state2.clone()) { - Ok(_) => {} - Err(e) => { - println!("can't tick on ppp0: {}", e); - process::exit(1); - } - } - - thread::sleep(Duration::from_secs(3)); - }); - loop { let mut buf = [MaybeUninit::new(0); BUFSIZE]; let (n, remote) = sock.recv_from(&mut buf)?; @@ -105,409 +96,6 @@ fn main() -> Result<()> { .collect::<Vec<u8>>(); let remote = remote.as_socket_ipv6().unwrap(); - - match handle_response(&sock, buf, remote, state.clone()) { - Ok(_) => {} - Err(e) => println!("can't handle pkt from {} on ppp0: {}", remote, e), - } - } -} - -fn handle_response( - sock: &Socket, - buf: &[u8], - remote: SocketAddrV6, - state: Arc<Mutex<State>>, -) -> Result<()> { - let mut state = state.lock().expect("state mutex is poisoned"); - let msg = Message::decode(&mut Decoder::new(buf))?; - - let expected_client_id = match *state { - State::Solicit(ref expected_client_id) => expected_client_id, - State::Request(ref expected_client_id, ..) => expected_client_id, - State::Active(ref expected_client_id, ..) => expected_client_id, - State::Renew(ref expected_client_id, ..) => expected_client_id, - }; - - let client_id = match msg - .opts() - .get(OptionCode::ClientId) - .ok_or(Error::NoClientId)? - { - DhcpOption::ClientId(client_id) => client_id, - _ => unreachable!(), - }; - - if client_id != expected_client_id { - println!(" <- [{}] invalid client id", remote); - return Ok(()); - } - - match msg.msg_type() { - MessageType::Advertise => { - let opts = msg.opts(); - - let server_id = match opts.get(OptionCode::ServerId).ok_or(Error::NoServerId)? { - DhcpOption::ServerId(server_id) => server_id, - _ => unreachable!(), - }; - - let aftr = opts.get(OptionCode::AftrName).map(|v| match v { - DhcpOption::Unknown(unk) => { - Name::from_bytes(unk.data()).expect("invalid aftr name format") - } - _ => unreachable!(), - }); - - let ia_pd = match opts.get(OptionCode::IAPD).ok_or(Error::NoIAPD)? { - DhcpOption::IAPD(ia_pd) => ia_pd, - _ => unreachable!(), - }; - - let ia_prefix = match ia_pd - .opts - .get(OptionCode::IAPrefix) - .ok_or(Error::NoIAPrefix)? - { - DhcpOption::IAPrefix(ia_prefix) => ia_prefix, - _ => unreachable!(), - }; - - let dnss = match opts - .get(OptionCode::DomainNameServers) - .ok_or(Error::NoDns)? - { - DhcpOption::DomainNameServers(dnss) => dnss, - _ => unreachable!(), - }; - - if dnss.len() < 2 { - return Err(Error::TooFewDns(dnss.len())); - } - - match *state { - State::Solicit(ref client_id) => { - let mut request = Message::new_with_id(MessageType::Request, msg.xid()); - let opts = request.opts_mut(); - - opts.insert(DhcpOption::ClientId(client_id.clone())); - opts.insert(DhcpOption::ServerId(server_id.clone())); - opts.insert(DhcpOption::IAPD(ia_pd.clone())); - opts.insert(DhcpOption::ORO(ORO { - opts: vec![OptionCode::AftrName, OptionCode::DomainNameServers], - })); - - let mut request_buf = Vec::new(); - request.encode(&mut Encoder::new(&mut request_buf))?; - - send_to_exact(sock, &request_buf, &remote.into())?; - - println!( - " <- [{}] advertise pd {}/{} valid {} pref {}, dns1 {}, dns2 {}, aftr {}", - remote, - ia_prefix.prefix_ip, - ia_prefix.prefix_len, - ia_prefix.valid_lifetime, - ia_prefix.preferred_lifetime, - dnss[0], - dnss[1], - aftr.map(|v| v.to_utf8()).unwrap_or("unset".into()) - ); - println!( - " -> [{}] request 0/{} pd {}, dns, aftr", - remote, MAX_ATTEMPTS, ia_pd.id - ); - - *state = State::Request( - client_id.clone(), - server_id.clone(), - msg.xid(), - ia_pd.clone(), - 1, - ); - } - _ => println!(" <- [{}] unexpected advertise", remote), - } - } - MessageType::Reply => { - let opts = msg.opts(); - - let rapid_commit = opts.get(OptionCode::RapidCommit).is_some(); - - let server_id = match opts.get(OptionCode::ServerId).ok_or(Error::NoServerId)? { - DhcpOption::ServerId(server_id) => server_id, - _ => unreachable!(), - }; - - let aftr = opts.get(OptionCode::AftrName).map(|v| match v { - DhcpOption::Unknown(unk) => { - Name::from_bytes(unk.data()).expect("invalid aftr name format") - } - _ => unreachable!(), - }); - - let ia_pd = match opts.get(OptionCode::IAPD).ok_or(Error::NoIAPD)? { - DhcpOption::IAPD(ia_pd) => ia_pd, - _ => unreachable!(), - }; - - let ia_prefix = match ia_pd - .opts - .get(OptionCode::IAPrefix) - .ok_or(Error::NoIAPrefix)? - { - DhcpOption::IAPrefix(ia_prefix) => ia_prefix, - _ => unreachable!(), - }; - - let dnss = match opts - .get(OptionCode::DomainNameServers) - .ok_or(Error::NoDns)? - { - DhcpOption::DomainNameServers(dnss) => dnss, - _ => unreachable!(), - }; - - if dnss.len() < 2 { - return Err(Error::TooFewDns(dnss.len())); - } - - match *state { - State::Solicit(ref client_id) => { - let aftr = aftr.map(|v| v.to_utf8()); - - if !rapid_commit { - println!(" <- [{}] unexpected reply rapid commit, pd {}/{} valid {} pref {}, dns1 {}, dns2 {}, aftr {}", remote, ia_prefix.prefix_ip, ia_prefix.prefix_len, ia_prefix.valid_lifetime, ia_prefix.preferred_lifetime, dnss[0], dnss[1], aftr.clone().unwrap_or("unset".into())); - return Ok(()); - } - - println!(" <- [{}] reply rapid commit, pd {}/{} valid {} pref {}, dns1 {}, dns2 {}, aftr {}", remote, ia_prefix.prefix_ip, ia_prefix.prefix_len, ia_prefix.valid_lifetime, ia_prefix.preferred_lifetime, dnss[0], dnss[1], aftr.clone().unwrap_or("unset".into())); - *state = State::Active( - client_id.clone(), - server_id.clone(), - ia_pd.clone(), - Instant::now(), - ia_pd.t1, - ); - - update_pdconfig(ia_prefix, dnss, &aftr); - } - State::Request(ref client_id, ref expected_server_id, ..) => { - if server_id != expected_server_id { - println!(" <- [{}] reply from invalid server id", remote); - return Ok(()); - } - - let aftr = aftr.map(|v| v.to_utf8()); - - println!( - " <- [{}] reply pd {}/{} valid {} pref {}, dns1 {}, dns2 {}, aftr {}", - remote, - ia_prefix.prefix_ip, - ia_prefix.prefix_len, - ia_prefix.valid_lifetime, - ia_prefix.preferred_lifetime, - dnss[0], - dnss[1], - aftr.clone().unwrap_or("unset".into()) - ); - *state = State::Active( - client_id.clone(), - server_id.clone(), - ia_pd.clone(), - Instant::now(), - ia_pd.t1, - ); - - update_pdconfig(ia_prefix, dnss, &aftr); - } - State::Renew(ref client_id, ref expected_server_id, ..) => { - if server_id != expected_server_id { - println!(" <- [{}] reply renew from invalid server id", remote); - return Ok(()); - } - - let aftr = aftr.map(|v| v.to_utf8()); - - println!( - " <- [{}] reply renew pd {}, dns1 {}, dns2 {}, aftr {}", - remote, - ia_pd.id, - dnss[0], - dnss[1], - aftr.clone().unwrap_or("unset".into()) - ); - *state = State::Active( - client_id.clone(), - server_id.clone(), - ia_pd.clone(), - Instant::now(), - ia_pd.t1, - ); - } - _ => println!(" <- [{}] unexpected reply", remote), - } - } - MessageType::Decline => { - let client_id = match *state { - State::Solicit(ref client_id) => client_id, - State::Request(ref client_id, ..) => client_id, - State::Active(ref client_id, ..) => client_id, - State::Renew(ref client_id, ..) => client_id, - }; - - let expected_server_id = match *state { - State::Solicit(..) => None, - State::Request(_, ref expected_server_id, ..) => Some(expected_server_id), - State::Active(_, ref expected_server_id, ..) => Some(expected_server_id), - State::Renew(_, ref expected_server_id, ..) => Some(expected_server_id), - }; - - if let Some(expected_server_id) = expected_server_id { - let server_id = match msg - .opts() - .get(OptionCode::ServerId) - .ok_or(Error::NoServerId)? - { - DhcpOption::ServerId(server_id) => server_id, - _ => unreachable!(), - }; - - if server_id != expected_server_id { - println!(" <- [{}] decline from invalid server id", remote); - return Ok(()); - } - } - - *state = State::Solicit(client_id.clone()); - println!(" <- [{}] decline", remote); - } - _ => println!(" <- [{}] invalid message type {:?}", remote, msg.msg_type()), - } - - Ok(()) -} - -fn tick(sock: &Socket, state: Arc<Mutex<State>>) -> Result<()> { - let dst: SocketAddrV6 = "[ff02::1:2]:547".parse()?; - - let mut state = state.lock().expect("state mutex is poisoned"); - match *state { - State::Solicit(ref client_id) => { - let mut solicit = Message::new(MessageType::Solicit); - let opts = solicit.opts_mut(); - - opts.insert(DhcpOption::ClientId(client_id.clone())); - opts.insert(DhcpOption::RapidCommit); - opts.insert(DhcpOption::IAPD(IAPD { - id: 1, - t1: 0, - t2: 0, - opts: Default::default(), - })); - opts.insert(DhcpOption::ORO(ORO { - opts: vec![OptionCode::AftrName, OptionCode::DomainNameServers], - })); - - let mut solicit_buf = Vec::new(); - solicit.encode(&mut Encoder::new(&mut solicit_buf))?; - - send_to_exact(sock, &solicit_buf, &dst.into())?; - - println!(" -> [{}] solicit rapid commit, pd 1, dns, aftr", dst); - Ok(()) - } - State::Request(ref client_id, ref server_id, xid, ref ia_pd, n) => { - if n >= MAX_ATTEMPTS { - *state = State::Solicit(client_id.clone()); - - println!("<-> request retransmission maximum exceeded"); - return Ok(()); - } - - let mut request = Message::new_with_id(MessageType::Request, xid); - let opts = request.opts_mut(); - - opts.insert(DhcpOption::ClientId(client_id.clone())); - opts.insert(DhcpOption::ServerId(server_id.clone())); - opts.insert(DhcpOption::IAPD(ia_pd.clone())); - opts.insert(DhcpOption::ORO(ORO { - opts: vec![OptionCode::AftrName, OptionCode::DomainNameServers], - })); - - let mut request_buf = Vec::new(); - request.encode(&mut Encoder::new(&mut request_buf))?; - - send_to_exact(sock, &request_buf, &dst.into())?; - - println!( - " -> [{}] request {}/{} pd {}, dns, aftr", - dst, n, MAX_ATTEMPTS, ia_pd.id - ); - - *state = State::Request( - client_id.clone(), - server_id.clone(), - xid, - ia_pd.clone(), - n + 1, - ); - Ok(()) - } - State::Active(ref client_id, ref server_id, ref ia_pd, recv, t1) => { - // Subtraction accounts for delay causey by loop interval. - if Instant::now().duration_since(recv).as_secs() >= (t1 - 3).into() { - let mut expired_pd = ia_pd.clone(); - let expired_prefix = match expired_pd - .opts - .get_mut(OptionCode::IAPrefix) - .ok_or(Error::NoIAPrefix)? - { - DhcpOption::IAPrefix(expired_prefix) => expired_prefix, - _ => unreachable!(), - }; - - expired_pd.t1 = 0; - expired_pd.t2 = 0; - expired_prefix.preferred_lifetime = 0; - expired_prefix.valid_lifetime = 0; - - *state = State::Renew(client_id.clone(), server_id.clone(), expired_pd, 0); - } - - Ok(()) - } - State::Renew(ref client_id, ref server_id, ref ia_pd, n) => { - if n >= MAX_ATTEMPTS { - *state = State::Solicit(client_id.clone()); - - println!("<-> renew retransmission maximum exceeded"); - return Ok(()); - } - - let mut renew = Message::new(MessageType::Renew); - let opts = renew.opts_mut(); - - opts.insert(DhcpOption::ClientId(client_id.clone())); - opts.insert(DhcpOption::ServerId(server_id.clone())); - opts.insert(DhcpOption::IAPD(ia_pd.clone())); - opts.insert(DhcpOption::ORO(ORO { - opts: vec![OptionCode::AftrName, OptionCode::DomainNameServers], - })); - - let mut renew_buf = Vec::new(); - renew.encode(&mut Encoder::new(&mut renew_buf))?; - - send_to_exact(sock, &renew_buf, &dst.into())?; - - println!( - " -> [{}] renew {}/{} pd {}, dns, aftr", - dst, n, MAX_ATTEMPTS, ia_pd.id - ); - - *state = State::Renew(client_id.clone(), server_id.clone(), ia_pd.clone(), n + 1); - Ok(()) - } } } @@ -520,19 +108,9 @@ fn send_to_exact(sock: &Socket, buf: &[u8], dst: &SockAddr) -> Result<()> { } } -fn update_pdconfig(ia_prefix: &IAPrefix, dnss: &[Ipv6Addr], aftr: &Option<String>) { - match write_pdconfig(ia_prefix, dnss, aftr) { - Ok(_) => println!("<-> write pd config to {}", rsdsl_pd_config::LOCATION), - Err(e) => println!( - "<-> can't write pd config to {}: {}", - rsdsl_pd_config::LOCATION, - e - ), - } -} - fn write_pdconfig(ia_prefix: &IAPrefix, dnss: &[Ipv6Addr], aftr: &Option<String>) -> Result<()> { let pdconfig = PdConfig { + timestamp: SystemTime::now(), prefix: ia_prefix.prefix_ip, len: ia_prefix.prefix_len, validlft: ia_prefix.valid_lifetime, @@ -547,8 +125,3 @@ fn write_pdconfig(ia_prefix: &IAPrefix, dnss: &[Ipv6Addr], aftr: &Option<String> Ok(()) } - -fn restart(state: Arc<Mutex<State>>) { - *state.lock().expect("state mutex is poisoned") = State::default(); - println!("reinitialize reconnected pppoe"); -} |