aboutsummaryrefslogtreecommitdiff
path: root/src/main.rs
blob: f1da0cb944e1eef84c62178534757fcd7e73344c (plain) (blame)
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
use std::fs::{File, OpenOptions};

use tokio::sync::mpsc;

use rsdsl_ip_config::DsConfig;
use rsdsl_netlinklib::Connection;
use rsdsl_pppoe3::{Client, Error, Result};
use serde::{Deserialize, Serialize};
use sysinfo::{ProcessExt, Signal, System, SystemExt};

const INTERFACE: &str = "carrier0";

#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
struct Config {
    username: String,
    password: String,
}

#[tokio::main]
async fn main() -> Result<()> {
    let conn = Connection::new().await?;

    println!("[info] wait for {}", INTERFACE);
    conn.link_wait_up(INTERFACE.into()).await?;
    println!("[info] startup");

    let mut config_file = File::open("/data/pppoe.conf")?;
    let config: Config = serde_json::from_reader(&mut config_file)?;

    let mut ds_config: DsConfig = {
        let mut ds_config_file = OpenOptions::new()
            .read(true)
            .write(true)
            .create(true)
            .truncate(false)
            .open(rsdsl_ip_config::LOCATION_LAST)?;

        serde_json::from_reader(&mut ds_config_file).unwrap_or_else(|_| {
            println!("[info] no valid ds config to reuse");
            DsConfig::default()
        })
    };
    let mut ds_config_last = ds_config;

    let (v4_tx, mut v4_rx) = mpsc::unbounded_channel();
    let (v6_tx, mut v6_rx) = mpsc::unbounded_channel();

    let client = Client::new(
        INTERFACE.into(),
        config.username,
        config.password,
        ds_config.v4.map(|v4| v4.addr),
        ds_config
            .v6
            .map(|v6| (u128::from(v6.laddr) & u128::from(u64::MAX)) as u64),
    )?;

    let mut join_handle = tokio::spawn(client.run(v4_tx.clone(), v6_tx.clone()));

    loop {
        tokio::select! {
            result = v4_rx.recv() => {
                ds_config.v4 = result.ok_or(Error::V4ChannelClosed)?;

                let mut ds_config_file = File::create(rsdsl_ip_config::LOCATION)?;
                serde_json::to_writer_pretty(&mut ds_config_file, &ds_config)?;

                if ds_config.v4.is_some() {
                    ds_config_last.v4 = ds_config.v4;

                    let mut ds_config_last_file = File::create(rsdsl_ip_config::LOCATION_LAST)?;
                    serde_json::to_writer_pretty(&mut ds_config_last_file, &ds_config_last)?;
                }

                inform();

                if let Some(v4) = ds_config.v4 {
                    println!("[info] <> ipv4: addr={}, dns1={}, dns2={}", v4.addr, v4.dns1, v4.dns2);
                } else {
                    println!("[info] <> ipv4: n/a");
                }
            }
            result = v6_rx.recv() => {
                ds_config.v6 = result.ok_or(Error::V6ChannelClosed)?;

                let mut ds_config_file = File::create(rsdsl_ip_config::LOCATION)?;
                serde_json::to_writer_pretty(&mut ds_config_file, &ds_config)?;

                if ds_config.v6.is_some() {
                    ds_config_last.v6 = ds_config.v6;

                    let mut ds_config_last_file = File::create(rsdsl_ip_config::LOCATION_LAST)?;
                    serde_json::to_writer_pretty(&mut ds_config_last_file, &ds_config_last)?;
                }

                inform();

                if let Some(v6) = ds_config.v6 {
                    println!("[info] <> ipv6: laddr={}, raddr={}", v6.laddr, v6.raddr);
                } else {
                    println!("[info] <> ipv6: n/a");
                }
            }
            result = &mut join_handle => {
                result??;

                println!("[info] <> exiting");
                return Ok(());
            }
        }
    }
}

/// Informs netlinkd of IPv4/IPv6 configuration changes.
fn inform() {
    for netlinkd in System::new_all().processes_by_exact_name("rsdsl_netlinkd") {
        netlinkd.kill_with(Signal::User1);
    }
}