-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
WIP: introduce event_reporter module
- Loading branch information
Showing
3 changed files
with
168 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
use std::collections::HashMap; | ||
use super::{Entities, Error, RequestedState, Result}; | ||
use std::sync::Arc; | ||
use ipnet::{IpNet, Ipv4Net, Ipv6Net}; | ||
use telio_model::config::{RelayState, Server as RelayEvent}; | ||
use telio_model::event::Set; | ||
use telio_model::{event::Event, features::Features, mesh::Node, PublicKey}; | ||
use telio_model::constants::{RESERVED_IPV4_IPS, RESERVED_IPV6_IPS}; | ||
use telio_relay::DerpRelay; | ||
use telio_task::io::mc_chan::Tx; | ||
use telio_wg::uapi::Peer; | ||
use telio_wg::{DynamicWg, WireGuard}; | ||
|
||
pub struct EventReporter { | ||
event_publisher: Tx<Box<Event>>, | ||
last_reported_derp_state: Option<RelayEvent>, | ||
last_reported_node_state: HashMap<PublicKey, Node>, | ||
} | ||
|
||
impl EventReporter { | ||
pub fn new(event_publisher: Tx<Box<Event>>) -> Self { | ||
return Self { | ||
event_publisher, | ||
last_reported_derp_state: Default::default(), | ||
last_reported_node_state: Default::default(), | ||
}; | ||
} | ||
|
||
/// Report any state changes to the libtelio library users | ||
/// | ||
/// This function will mainly take care of the following uses cases: | ||
/// * Report event for any state change of any VPN/Meshnet Node. VPN nodes may retrieve their | ||
/// statuses from telio-pq as well as telio-wg as sources of truth for current state. | ||
/// * Report DERP connection status changes. | ||
/// * Notify apps about non-recoverable issues, for example in case WireGuard starts spewing | ||
/// errors over UAPI. | ||
/// | ||
/// The list above may get extended in the future. | ||
/// | ||
/// This module depends on external monitoring of state changes, it will not monitor for it | ||
/// itself. Hence the handle_state_change function should get called whenever any state change | ||
/// occurs. | ||
/// | ||
/// Note here we are *not* handling panic's reporting. | ||
pub async fn report_events( | ||
&mut self, | ||
_requested_state: &RequestedState, | ||
entities: &Entities, | ||
_features: &Features, | ||
) -> Result { | ||
self.report_derp_events(entities.meshnet.left().map(|e| &e.derp)) | ||
.await?; | ||
self.report_node_events(&entities.wireguard_interface).await?; | ||
Ok(()) | ||
} | ||
|
||
pub async fn report_derp_events(&mut self, relay: Option<&Arc<DerpRelay>>) -> Result { | ||
// Meshnet may be disabled, in which case relay will be None | ||
// Otherwise we may have no server selected | ||
// Or we may be attemting to connect to some server | ||
// Or we may be connected to some server | ||
let current_relay_state = match relay { | ||
Some(r) => r.get_connected_server().await, | ||
_ => None, | ||
}; | ||
|
||
// Evaluate if anything has changed and build an even if necessary | ||
let event = match ( | ||
self.last_reported_derp_state.clone(), | ||
current_relay_state.clone(), | ||
) { | ||
// Nothing Changed | ||
(None, None) => return Ok(()), | ||
(Some(a), Some(b)) if a == b => return Ok(()), | ||
|
||
// Either freshly connected or connection info has changed | ||
(None, Some(current_state)) | (Some(_), Some(current_state)) => { | ||
Event::builder::<RelayEvent>() | ||
.set(current_state) | ||
.build() | ||
.ok_or(Error::EventBuildingError)? | ||
} | ||
|
||
// Disconnected | ||
(Some(last_reported_derp_state), None) => { | ||
let relay_event = RelayEvent { | ||
conn_state: RelayState::Disconnected, | ||
..last_reported_derp_state | ||
}; | ||
Event::builder::<RelayEvent>() | ||
.set(relay_event) | ||
.build() | ||
.ok_or(Error::EventBuildingError)? | ||
} | ||
}; | ||
|
||
// Fire event | ||
self.event_publisher.send(Box::new(event))?; | ||
|
||
// Make sure, that we know what has been reported in the future iterations | ||
self.last_reported_derp_state = current_relay_state; | ||
Ok(()) | ||
} | ||
|
||
pub async fn report_node_events(&mut self, wireguard: &Arc<DynamicWg>) -> Result { | ||
// Retreive relevant WireGuard peers | ||
let wg_peers: HashMap<PublicKey, Peer> = wireguard.get_interface().await? | ||
.peers | ||
.into_iter() | ||
.filter(|(_, peer)| Self::should_report_peer(&peer)) | ||
.collect(); | ||
|
||
//TODO: acquire some sort of representation of telio-pq current state | ||
|
||
//TODO: For each node: | ||
// compare against reported state | ||
//TODO: report any differences | ||
//TODO: save reported state | ||
Ok(()) | ||
} | ||
|
||
fn should_report_peer(peer: &Peer) -> bool { | ||
// Note: We have some internal virtual peers (like DNS/starcast and similar) which we | ||
// would like to leave unreported as per agreement with integrators, peers which are | ||
// not explicitly requested by them - should not trigger events. | ||
// Current convention is that such peers have allowed ips set to reserved range | ||
// defined in telio_model::constants. | ||
// It would be nicer to have explicit indication of the fact that peer is internal, | ||
// but until it is introduced - we will have to rely on allowed ips, and reserved | ||
// ranges | ||
// There is a case worth mentioning, that for VPN and Exit Node peers we have a range | ||
// of 0.0.0.0/0 in allowedIps, which is not contained by reserved range. But at the | ||
// same time such peers should be reported, so everything is in-line | ||
peer.allowed_ips.iter() | ||
.all(|net| match &net { | ||
IpNet::V4(ipv4net) => Ipv4Net::from(RESERVED_IPV4_IPS).contains(ipv4net), | ||
IpNet::V6(ipv6net) => Ipv6Net::from(RESERVED_IPV6_IPS).contains(ipv6net) | ||
}) | ||
} | ||
} |