1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
use std::fs::File;
use std::io;
use std::net::{Ipv6Addr, SocketAddr};
use std::thread;
use std::time::Duration;
use hickory_resolver::config::{NameServerConfig, Protocol, ResolverConfig, ResolverOpts};
use hickory_resolver::Resolver;
use ipnet::Ipv6Net;
use rsdsl_netlinklib::tunnel::IpIp6;
use rsdsl_pd_config::PdConfig;
use signal_hook::{consts::SIGUSR1, iterator::Signals};
use sysinfo::{ProcessExt, Signal, System, SystemExt};
use thiserror::Error;
const MAX_ATTEMPTS: usize = 3;
const BACKOFF: u64 = 900;
#[derive(Debug, Error)]
pub enum Error {
#[error("no address associated with aftr name")]
NoDnsRecord,
#[error("not enough ipv6 subnets")]
NotEnoughIpv6Subnets,
#[error("io error: {0}")]
Io(#[from] io::Error),
#[error("invalid prefix length: {0}")]
IpnetPrefixLen(#[from] ipnet::PrefixLenError),
#[error("netlinklib error: {0}")]
Netlinklib(#[from] rsdsl_netlinklib::Error),
#[error("serde_json error: {0}")]
SerdeJson(#[from] serde_json::Error),
#[error("hickory_resolver resolve error: {0}")]
HickoryResolverResolve(#[from] hickory_resolver::error::ResolveError),
}
pub type Result<T> = std::result::Result<T, Error>;
fn main() -> Result<()> {
println!("[info] init");
let mut tnl = None;
let mut signals = Signals::new([SIGUSR1])?;
for _ in signals.forever() {
logic(&mut tnl)?;
}
unreachable!()
}
fn logic(tnl: &mut Option<IpIp6>) -> Result<()> {
*tnl = None; // Delete old tunnel.
let mut file = File::open(rsdsl_pd_config::LOCATION)?;
let pdconfig: PdConfig = serde_json::from_reader(&mut file)?;
if let Some(ref aftr) = pdconfig.aftr {
let local = local_address(&pdconfig)?;
let remote = multitry_resolve6(&pdconfig, aftr)?;
*tnl = Some(IpIp6::new(
"dslite0".to_string(),
"ppp0".to_string(),
local,
remote,
)?);
for netlinkd in System::new_all().processes_by_exact_name("rsdsl_netlinkd") {
netlinkd.kill_with(Signal::User1);
}
println!("[info] init ds-lite tunnel {} <=> {}", local, remote);
} else {
println!("[info] no aftr");
}
Ok(())
}
fn local_address(pdconfig: &PdConfig) -> Result<Ipv6Addr> {
let prefix = Ipv6Net::new(pdconfig.prefix, pdconfig.len)?.trunc();
let mut subnets = prefix.subnets(64)?;
let addr = next_ifid1(&mut subnets)?;
Ok(addr)
}
fn next_ifid1<T: Iterator<Item = Ipv6Net>>(subnets: &mut T) -> Result<Ipv6Addr> {
Ok((u128::from(subnets.next().ok_or(Error::NotEnoughIpv6Subnets)?.addr()) + 1).into())
}
fn resolve6(pdconfig: &PdConfig, fqdn: &str) -> Result<Ipv6Addr> {
let mut cfg = ResolverConfig::new();
cfg.add_name_server(NameServerConfig::new(
SocketAddr::new(pdconfig.dns1.into(), 53),
Protocol::Udp,
));
cfg.add_name_server(NameServerConfig::new(
SocketAddr::new(pdconfig.dns2.into(), 53),
Protocol::Udp,
));
let resolver = Resolver::new(cfg, ResolverOpts::default())?;
let response = resolver.ipv6_lookup(fqdn)?;
let addr = response.iter().next().ok_or(Error::NoDnsRecord)?;
Ok(**addr)
}
fn multitry_resolve6(pdconfig: &PdConfig, fqdn: &str) -> Result<Ipv6Addr> {
for i in 0..MAX_ATTEMPTS {
match resolve6(pdconfig, fqdn) {
Ok(v) => return Ok(v),
Err(e) => {
if i >= MAX_ATTEMPTS - 1 {
return Err(e);
} else {
println!(
"[warn] resolve aftr {}: {} (attempt {}/{})",
fqdn, e, i, MAX_ATTEMPTS
)
}
}
}
thread::sleep(Duration::from_secs(BACKOFF));
}
unreachable!()
}
|