use std::fs; use std::net::IpAddr; use std::path::Path; use std::str::FromStr; use anyhow::{Context, Result, anyhow}; use crate::data_model::resources::as_resources::Asn; use crate::data_model::resources::ip_resources::{IPAddress, IPAddressPrefix}; use crate::rtr::payload::{Aspa, Payload, RouteOrigin, RouterKey, Ski}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct ParsedVrp { pub prefix_addr: IpAddr, pub prefix_len: u8, pub max_len: u8, pub asn: u32, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct ParsedAspa { pub customer_asn: u32, pub provider_asns: Vec, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct ParsedRouterKey { pub ski: [u8; 20], pub asn: u32, pub spki: Vec, } /// 从文本文件中加载 VRP,并转换成 RTR Payload::RouteOrigin。 /// /// 文件格式: /// /// ```text /// # prefix,max_len,asn /// 10.0.0.0/24,24,65001 /// 10.0.1.0/24,24,65002 /// 2001:db8::/32,48,65003 /// ``` pub fn load_vrps_from_file(path: impl AsRef) -> Result> { let path = path.as_ref(); let content = fs::read_to_string(path) .with_context(|| format!("failed to read VRP file: {}", path.display()))?; let mut payloads = Vec::new(); for (idx, raw_line) in content.lines().enumerate() { let line_no = idx + 1; let line = raw_line.trim(); if line.is_empty() || line.starts_with('#') { continue; } let vrp = parse_vrp_line(line) .with_context(|| format!("invalid VRP line {}: {}", line_no, raw_line))?; payloads.push(Payload::RouteOrigin(build_route_origin(vrp)?)); } Ok(payloads) } pub fn load_aspas_from_file(path: impl AsRef) -> Result> { let path = path.as_ref(); let content = fs::read_to_string(path) .with_context(|| format!("failed to read ASPA file: {}", path.display()))?; let mut payloads = Vec::new(); for (idx, raw_line) in content.lines().enumerate() { let line_no = idx + 1; let line = raw_line.trim(); if line.is_empty() || line.starts_with('#') { continue; } let aspa = parse_aspa_line(line) .with_context(|| format!("invalid ASPA line {}: {}", line_no, raw_line))?; payloads.push(Payload::Aspa(build_aspa(aspa)?)); } Ok(payloads) } pub fn load_router_keys_from_file(path: impl AsRef) -> Result> { let path = path.as_ref(); let content = fs::read_to_string(path) .with_context(|| format!("failed to read Router Key file: {}", path.display()))?; let mut payloads = Vec::new(); for (idx, raw_line) in content.lines().enumerate() { let line_no = idx + 1; let line = raw_line.trim(); if line.is_empty() || line.starts_with('#') { continue; } let router_key = parse_router_key_line(line) .with_context(|| format!("invalid Router Key line {}: {}", line_no, raw_line))?; payloads.push(Payload::RouterKey(build_router_key(router_key)?)); } Ok(payloads) } /// 解析单行 VRP。 /// /// 格式: /// `prefix/prefix_len,max_len,asn` /// /// 例如: /// `10.0.0.0/24,24,65001` pub fn parse_vrp_line(line: &str) -> Result { let parts: Vec<_> = line.split(',').map(|s| s.trim()).collect(); if parts.len() != 3 { return Err(anyhow!( "expected format: /,," )); } let prefix_part = parts[0]; let max_len = u8::from_str(parts[1]).with_context(|| format!("invalid max_len: {}", parts[1]))?; let asn = u32::from_str(parts[2]).with_context(|| format!("invalid asn: {}", parts[2]))?; let (addr_str, prefix_len_str) = prefix_part .split_once('/') .ok_or_else(|| anyhow!("prefix must be in CIDR form, e.g. 10.0.0.0/24"))?; let prefix_addr = IpAddr::from_str(addr_str.trim()) .with_context(|| format!("invalid IP address: {}", addr_str))?; let prefix_len = u8::from_str(prefix_len_str.trim()) .with_context(|| format!("invalid prefix length: {}", prefix_len_str))?; validate_vrp(prefix_addr, prefix_len, max_len)?; Ok(ParsedVrp { prefix_addr, prefix_len, max_len, asn, }) } pub fn parse_aspa_line(line: &str) -> Result { let parts: Vec<_> = line.split(',').map(|s| s.trim()).collect(); if parts.len() != 2 { return Err(anyhow!( "expected format: , [provider_asn ...]" )); } let customer_asn = u32::from_str(parts[0]).with_context(|| format!("invalid customer_asn: {}", parts[0]))?; let provider_asns = parts[1] .split_whitespace() .map(|provider| { u32::from_str(provider).with_context(|| format!("invalid provider_asn: {}", provider)) }) .collect::>>()?; validate_aspa(customer_asn, &provider_asns)?; Ok(ParsedAspa { customer_asn, provider_asns, }) } pub fn parse_router_key_line(line: &str) -> Result { let parts: Vec<_> = line.split(',').map(|s| s.trim()).collect(); if parts.len() != 3 { return Err(anyhow!("expected format: ,,")); } let ski_vec = decode_hex(parts[0]).with_context(|| format!("invalid SKI hex: {}", parts[0]))?; if ski_vec.len() != 20 { return Err(anyhow!("SKI must be exactly 20 bytes")); } let mut ski = [0u8; 20]; ski.copy_from_slice(&ski_vec); let asn = u32::from_str(parts[1]).with_context(|| format!("invalid asn: {}", parts[1]))?; let spki = decode_hex(parts[2]).with_context(|| format!("invalid SPKI hex: {}", parts[2]))?; validate_router_key(asn, &spki)?; Ok(ParsedRouterKey { ski, asn, spki }) } fn validate_vrp(prefix_addr: IpAddr, prefix_len: u8, max_len: u8) -> Result<()> { match prefix_addr { IpAddr::V4(_) => { if prefix_len > 32 { return Err(anyhow!("IPv4 prefix length must be <= 32")); } if max_len > 32 { return Err(anyhow!("IPv4 max_len must be <= 32")); } if max_len < prefix_len { return Err(anyhow!("IPv4 max_len must be >= prefix length")); } } IpAddr::V6(_) => { if prefix_len > 128 { return Err(anyhow!("IPv6 prefix length must be <= 128")); } if max_len > 128 { return Err(anyhow!("IPv6 max_len must be <= 128")); } if max_len < prefix_len { return Err(anyhow!("IPv6 max_len must be >= prefix length")); } } } Ok(()) } fn validate_aspa(customer_asn: u32, provider_asns: &[u32]) -> Result<()> { if customer_asn == 0 { return Err(anyhow!("customer_asn must not be AS0")); } if provider_asns.is_empty() { return Err(anyhow!("provider list must not be empty")); } if provider_asns.iter().any(|asn| *asn == 0) && !(provider_asns.len() == 1 && provider_asns[0] == 0) { return Err(anyhow!( "provider list containing AS0 must be exactly [0]" )); } Ok(()) } fn validate_router_key(asn: u32, spki: &[u8]) -> Result<()> { crate::rtr::payload::RouterKey::new(Ski::default(), Asn::from(asn), spki.to_vec()) .validate() .map_err(|err| anyhow!(err.to_string()))?; Ok(()) } pub fn build_route_origin(vrp: ParsedVrp) -> Result { let address = match vrp.prefix_addr { IpAddr::V4(addr) => IPAddress::from_ipv4(addr), IpAddr::V6(addr) => IPAddress::from_ipv6(addr), }; let prefix = IPAddressPrefix::new(address, vrp.prefix_len); let asn = Asn::from(vrp.asn); Ok(RouteOrigin::new(prefix, vrp.max_len, asn)) } pub fn build_aspa(aspa: ParsedAspa) -> Result { let customer_asn = Asn::from(aspa.customer_asn); let provider_asns = aspa .provider_asns .into_iter() .map(Asn::from) .collect::>(); let aspa = Aspa::new(customer_asn, provider_asns); aspa.validate_announcement()?; Ok(aspa) } pub fn build_router_key(router_key: ParsedRouterKey) -> Result { let asn = Asn::from(router_key.asn); let ski = Ski::from_bytes(router_key.ski); let router_key = RouterKey::new(ski, asn, router_key.spki); Ok(router_key) } fn decode_hex(input: &str) -> Result> { let trimmed = input.trim(); if trimmed.len() % 2 != 0 { return Err(anyhow!("hex string must have even length")); } (0..trimmed.len()) .step_by(2) .map(|idx| { u8::from_str_radix(&trimmed[idx..idx + 2], 16) .map_err(|err| anyhow!("invalid hex at byte {}: {}", idx / 2, err)) }) .collect() }