Merge branch 'dev' into 'main'
dev merge See merge request veilid/veilid!8
This commit is contained in:
commit
ae6ff1b512
11
Cargo.lock
generated
11
Cargo.lock
generated
@ -1209,6 +1209,15 @@ dependencies = [
|
|||||||
"xi-unicode",
|
"xi-unicode",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cursive_table_view"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8935dd87d19c54b7506b245bc988a7b4e65b1058e1d0d64c0ad9b3188e48060"
|
||||||
|
dependencies = [
|
||||||
|
"cursive_core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "curve25519-dalek"
|
name = "curve25519-dalek"
|
||||||
version = "3.2.1"
|
version = "3.2.1"
|
||||||
@ -5087,6 +5096,7 @@ dependencies = [
|
|||||||
"cursive",
|
"cursive",
|
||||||
"cursive-flexi-logger-view",
|
"cursive-flexi-logger-view",
|
||||||
"cursive_buffered_backend",
|
"cursive_buffered_backend",
|
||||||
|
"cursive_table_view",
|
||||||
"directories",
|
"directories",
|
||||||
"flexi_logger",
|
"flexi_logger",
|
||||||
"futures",
|
"futures",
|
||||||
@ -5155,6 +5165,7 @@ dependencies = [
|
|||||||
"nix 0.25.0",
|
"nix 0.25.0",
|
||||||
"no-std-net",
|
"no-std-net",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"owning_ref",
|
||||||
"owo-colors",
|
"owo-colors",
|
||||||
"parking_lot 0.12.1",
|
"parking_lot 0.12.1",
|
||||||
"rand 0.7.3",
|
"rand 0.7.3",
|
||||||
|
@ -3,5 +3,4 @@ core:
|
|||||||
network:
|
network:
|
||||||
dht:
|
dht:
|
||||||
min_peer_count: 1
|
min_peer_count: 1
|
||||||
enable_local_peer_scope: true
|
|
||||||
bootstrap: []
|
bootstrap: []
|
||||||
|
@ -23,9 +23,9 @@ tokio-util = { version = "^0", features = ["compat"], optional = true}
|
|||||||
async-tungstenite = { version = "^0.8" }
|
async-tungstenite = { version = "^0.8" }
|
||||||
cursive-flexi-logger-view = { path = "../external/cursive-flexi-logger-view" }
|
cursive-flexi-logger-view = { path = "../external/cursive-flexi-logger-view" }
|
||||||
cursive_buffered_backend = { path = "../external/cursive_buffered_backend" }
|
cursive_buffered_backend = { path = "../external/cursive_buffered_backend" }
|
||||||
# cursive-multiplex = "0.4.0"
|
# cursive-multiplex = "0.6.0"
|
||||||
# cursive_tree_view = "0.6.0"
|
# cursive_tree_view = "0.6.0"
|
||||||
# cursive_table_view = "0.12.0"
|
cursive_table_view = "0.14.0"
|
||||||
# cursive-tabs = "0.5.0"
|
# cursive-tabs = "0.5.0"
|
||||||
clap = "^3"
|
clap = "^3"
|
||||||
directories = "^4"
|
directories = "^4"
|
||||||
|
@ -8,7 +8,7 @@ use std::net::SocketAddr;
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
use veilid_core::xx::{Eventual, EventualCommon};
|
use veilid_core::xx::{Eventual, EventualCommon};
|
||||||
use veilid_core::VeilidConfigLogLevel;
|
use veilid_core::*;
|
||||||
|
|
||||||
pub fn convert_loglevel(s: &str) -> Result<VeilidConfigLogLevel, String> {
|
pub fn convert_loglevel(s: &str) -> Result<VeilidConfigLogLevel, String> {
|
||||||
match s.to_ascii_lowercase().as_str() {
|
match s.to_ascii_lowercase().as_str() {
|
||||||
@ -111,7 +111,8 @@ attach - attach the server to the Veilid network
|
|||||||
detach - detach the server from the Veilid network
|
detach - detach the server from the Veilid network
|
||||||
debug - send a debugging command to the Veilid server
|
debug - send a debugging command to the Veilid server
|
||||||
change_log_level - change the log level for a tracing layer
|
change_log_level - change the log level for a tracing layer
|
||||||
"#,
|
"#
|
||||||
|
.to_owned(),
|
||||||
);
|
);
|
||||||
let ui = self.ui();
|
let ui = self.ui();
|
||||||
ui.send_callback(callback);
|
ui.send_callback(callback);
|
||||||
@ -202,7 +203,7 @@ change_log_level - change the log level for a tracing layer
|
|||||||
let log_level = match convert_loglevel(&rest.unwrap_or_default()) {
|
let log_level = match convert_loglevel(&rest.unwrap_or_default()) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("failed to change log level: {}", e);
|
ui.add_node_event(format!("Failed to change log level: {}", e));
|
||||||
ui.send_callback(callback);
|
ui.send_callback(callback);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -210,12 +211,14 @@ change_log_level - change the log level for a tracing layer
|
|||||||
|
|
||||||
match capi.server_change_log_level(layer, log_level).await {
|
match capi.server_change_log_level(layer, log_level).await {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
info!("Log level changed");
|
ui.display_string_dialog("Success", "Log level changed", callback);
|
||||||
ui.send_callback(callback);
|
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Server command 'change_log_level' failed: {}", e);
|
ui.display_string_dialog(
|
||||||
ui.send_callback(callback);
|
"Server command 'change_log_level' failed",
|
||||||
|
e.to_string(),
|
||||||
|
callback,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -320,14 +323,25 @@ change_log_level - change the log level for a tracing layer
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_network_status(&mut self, network: veilid_core::VeilidStateNetwork) {
|
pub fn update_network_status(&mut self, network: veilid_core::VeilidStateNetwork) {
|
||||||
self.inner_mut()
|
self.inner_mut().ui.set_network_status(
|
||||||
.ui
|
network.started,
|
||||||
.set_network_status(network.started, network.bps_down, network.bps_up);
|
network.bps_down,
|
||||||
|
network.bps_up,
|
||||||
|
network.peers,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_log(&mut self, log: veilid_core::VeilidStateLog) {
|
pub fn update_log(&mut self, log: veilid_core::VeilidStateLog) {
|
||||||
let message = format!("{}: {}", log.log_level, log.message);
|
self.inner().ui.add_node_event(format!(
|
||||||
self.inner().ui.add_node_event(&message);
|
"{}: {}{}",
|
||||||
|
log.log_level,
|
||||||
|
log.message,
|
||||||
|
if let Some(bt) = log.backtrace {
|
||||||
|
format!("\nBacktrace:\n{}", bt)
|
||||||
|
} else {
|
||||||
|
"".to_owned()
|
||||||
|
}
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_shutdown(&mut self) {
|
pub fn update_shutdown(&mut self) {
|
||||||
|
@ -12,6 +12,7 @@ use tools::*;
|
|||||||
|
|
||||||
mod client_api_connection;
|
mod client_api_connection;
|
||||||
mod command_processor;
|
mod command_processor;
|
||||||
|
mod peers_table_view;
|
||||||
mod settings;
|
mod settings;
|
||||||
mod tools;
|
mod tools;
|
||||||
mod ui;
|
mod ui;
|
||||||
|
99
veilid-cli/src/peers_table_view.rs
Normal file
99
veilid-cli/src/peers_table_view.rs
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
use super::*;
|
||||||
|
use cursive_table_view::*;
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
use veilid_core::PeerTableData;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub enum PeerTableColumn {
|
||||||
|
NodeId,
|
||||||
|
Address,
|
||||||
|
LatencyAvg,
|
||||||
|
TransferDownAvg,
|
||||||
|
TransferUpAvg,
|
||||||
|
}
|
||||||
|
|
||||||
|
// impl PeerTableColumn {
|
||||||
|
// fn as_str(&self) -> &str {
|
||||||
|
// match self {
|
||||||
|
// PeerTableColumn::NodeId => "Node Id",
|
||||||
|
// PeerTableColumn::Address => "Address",
|
||||||
|
// PeerTableColumn::LatencyAvg => "Latency",
|
||||||
|
// PeerTableColumn::TransferDownAvg => "Down",
|
||||||
|
// PeerTableColumn::TransferUpAvg => "Up",
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
fn format_ts(ts: u64) -> String {
|
||||||
|
let secs = timestamp_to_secs(ts);
|
||||||
|
if secs >= 1.0 {
|
||||||
|
format!("{:.2}s", timestamp_to_secs(ts))
|
||||||
|
} else {
|
||||||
|
format!("{:.2}ms", timestamp_to_secs(ts) * 1000.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_bps(bps: u64) -> String {
|
||||||
|
if bps >= 1024u64 * 1024u64 * 1024u64 {
|
||||||
|
format!("{:.2}GB/s", (bps / (1024u64 * 1024u64)) as f64 / 1024.0)
|
||||||
|
} else if bps >= 1024u64 * 1024u64 {
|
||||||
|
format!("{:.2}MB/s", (bps / 1024u64) as f64 / 1024.0)
|
||||||
|
} else if bps >= 1024u64 {
|
||||||
|
format!("{:.2}KB/s", bps as f64 / 1024.0)
|
||||||
|
} else {
|
||||||
|
format!("{:.2}B/s", bps as f64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TableViewItem<PeerTableColumn> for PeerTableData {
|
||||||
|
fn to_column(&self, column: PeerTableColumn) -> String {
|
||||||
|
match column {
|
||||||
|
PeerTableColumn::NodeId => self.node_id.encode(),
|
||||||
|
PeerTableColumn::Address => format!(
|
||||||
|
"{:?}:{}",
|
||||||
|
self.peer_address.protocol_type(),
|
||||||
|
self.peer_address.to_socket_addr()
|
||||||
|
),
|
||||||
|
PeerTableColumn::LatencyAvg => format!(
|
||||||
|
"{}",
|
||||||
|
self.peer_stats
|
||||||
|
.latency
|
||||||
|
.as_ref()
|
||||||
|
.map(|l| format_ts(l.average))
|
||||||
|
.unwrap_or("---".to_owned())
|
||||||
|
),
|
||||||
|
PeerTableColumn::TransferDownAvg => format_bps(self.peer_stats.transfer.down.average),
|
||||||
|
PeerTableColumn::TransferUpAvg => format_bps(self.peer_stats.transfer.up.average),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cmp(&self, other: &Self, column: PeerTableColumn) -> Ordering
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
match column {
|
||||||
|
PeerTableColumn::NodeId => self.node_id.cmp(&other.node_id),
|
||||||
|
PeerTableColumn::Address => self.to_column(column).cmp(&other.to_column(column)),
|
||||||
|
PeerTableColumn::LatencyAvg => self
|
||||||
|
.peer_stats
|
||||||
|
.latency
|
||||||
|
.as_ref()
|
||||||
|
.map(|l| l.average)
|
||||||
|
.cmp(&other.peer_stats.latency.as_ref().map(|l| l.average)),
|
||||||
|
PeerTableColumn::TransferDownAvg => self
|
||||||
|
.peer_stats
|
||||||
|
.transfer
|
||||||
|
.down
|
||||||
|
.average
|
||||||
|
.cmp(&other.peer_stats.transfer.down.average),
|
||||||
|
PeerTableColumn::TransferUpAvg => self
|
||||||
|
.peer_stats
|
||||||
|
.transfer
|
||||||
|
.up
|
||||||
|
.average
|
||||||
|
.cmp(&other.peer_stats.transfer.up.average),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type PeersTableView = TableView<PeerTableData, PeerTableColumn>;
|
@ -1,4 +1,5 @@
|
|||||||
use crate::command_processor::*;
|
use crate::command_processor::*;
|
||||||
|
use crate::peers_table_view::*;
|
||||||
use crate::settings::Settings;
|
use crate::settings::Settings;
|
||||||
use crossbeam_channel::Sender;
|
use crossbeam_channel::Sender;
|
||||||
use cursive::align::*;
|
use cursive::align::*;
|
||||||
@ -10,6 +11,7 @@ use cursive::views::*;
|
|||||||
use cursive::Cursive;
|
use cursive::Cursive;
|
||||||
use cursive::CursiveRunnable;
|
use cursive::CursiveRunnable;
|
||||||
use cursive_flexi_logger_view::{CursiveLogWriter, FlexiLoggerView};
|
use cursive_flexi_logger_view::{CursiveLogWriter, FlexiLoggerView};
|
||||||
|
//use cursive_multiplex::*;
|
||||||
use log::*;
|
use log::*;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::{HashMap, VecDeque};
|
use std::collections::{HashMap, VecDeque};
|
||||||
@ -20,7 +22,7 @@ use veilid_core::*;
|
|||||||
//////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////
|
||||||
///
|
///
|
||||||
struct Dirty<T> {
|
struct Dirty<T> {
|
||||||
pub value: T,
|
value: T,
|
||||||
dirty: bool,
|
dirty: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,6 +54,7 @@ struct UIState {
|
|||||||
network_started: Dirty<bool>,
|
network_started: Dirty<bool>,
|
||||||
network_down_up: Dirty<(f32, f32)>,
|
network_down_up: Dirty<(f32, f32)>,
|
||||||
connection_state: Dirty<ConnectionState>,
|
connection_state: Dirty<ConnectionState>,
|
||||||
|
peers_state: Dirty<Vec<PeerTableData>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UIState {
|
impl UIState {
|
||||||
@ -61,6 +64,7 @@ impl UIState {
|
|||||||
network_started: Dirty::new(false),
|
network_started: Dirty::new(false),
|
||||||
network_down_up: Dirty::new((0.0, 0.0)),
|
network_down_up: Dirty::new((0.0, 0.0)),
|
||||||
connection_state: Dirty::new(ConnectionState::Disconnected),
|
connection_state: Dirty::new(ConnectionState::Disconnected),
|
||||||
|
peers_state: Dirty::new(Vec::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -219,6 +223,9 @@ impl UI {
|
|||||||
fn status_bar(s: &mut Cursive) -> ViewRef<TextView> {
|
fn status_bar(s: &mut Cursive) -> ViewRef<TextView> {
|
||||||
s.find_name("status-bar").unwrap()
|
s.find_name("status-bar").unwrap()
|
||||||
}
|
}
|
||||||
|
fn peers(s: &mut Cursive) -> ViewRef<PeersTableView> {
|
||||||
|
s.find_name("peers").unwrap()
|
||||||
|
}
|
||||||
fn render_attachment_state<'a>(inner: &mut UIInner) -> &'a str {
|
fn render_attachment_state<'a>(inner: &mut UIInner) -> &'a str {
|
||||||
match inner.ui_state.attachment_state.get() {
|
match inner.ui_state.attachment_state.get() {
|
||||||
AttachmentState::Detached => " Detached [----]",
|
AttachmentState::Detached => " Detached [----]",
|
||||||
@ -607,15 +614,23 @@ impl UI {
|
|||||||
statusbar.set_content(status);
|
statusbar.set_content(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn refresh_peers(s: &mut Cursive) {
|
||||||
|
let mut peers = UI::peers(s);
|
||||||
|
let inner = Self::inner_mut(s);
|
||||||
|
peers.set_items_stable(inner.ui_state.peers_state.get().clone());
|
||||||
|
}
|
||||||
|
|
||||||
fn update_cb(s: &mut Cursive) {
|
fn update_cb(s: &mut Cursive) {
|
||||||
let mut inner = Self::inner_mut(s);
|
let mut inner = Self::inner_mut(s);
|
||||||
|
|
||||||
let mut refresh_statusbar = false;
|
let mut refresh_statusbar = false;
|
||||||
let mut refresh_button_attach = false;
|
let mut refresh_button_attach = false;
|
||||||
let mut refresh_connection_dialog = false;
|
let mut refresh_connection_dialog = false;
|
||||||
|
let mut refresh_peers = false;
|
||||||
if inner.ui_state.attachment_state.take_dirty() {
|
if inner.ui_state.attachment_state.take_dirty() {
|
||||||
refresh_statusbar = true;
|
refresh_statusbar = true;
|
||||||
refresh_button_attach = true;
|
refresh_button_attach = true;
|
||||||
|
refresh_peers = true;
|
||||||
}
|
}
|
||||||
if inner.ui_state.network_started.take_dirty() {
|
if inner.ui_state.network_started.take_dirty() {
|
||||||
refresh_statusbar = true;
|
refresh_statusbar = true;
|
||||||
@ -627,6 +642,10 @@ impl UI {
|
|||||||
refresh_statusbar = true;
|
refresh_statusbar = true;
|
||||||
refresh_button_attach = true;
|
refresh_button_attach = true;
|
||||||
refresh_connection_dialog = true;
|
refresh_connection_dialog = true;
|
||||||
|
refresh_peers = true;
|
||||||
|
}
|
||||||
|
if inner.ui_state.peers_state.take_dirty() {
|
||||||
|
refresh_peers = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
drop(inner);
|
drop(inner);
|
||||||
@ -640,6 +659,9 @@ impl UI {
|
|||||||
if refresh_connection_dialog {
|
if refresh_connection_dialog {
|
||||||
Self::refresh_connection_dialog(s);
|
Self::refresh_connection_dialog(s);
|
||||||
}
|
}
|
||||||
|
if refresh_peers {
|
||||||
|
Self::refresh_peers(s);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
@ -686,30 +708,48 @@ impl UI {
|
|||||||
siv.set_user_data(this.inner.clone());
|
siv.set_user_data(this.inner.clone());
|
||||||
|
|
||||||
// Create layouts
|
// Create layouts
|
||||||
let mut mainlayout = LinearLayout::vertical().with_name("main-layout");
|
|
||||||
mainlayout.get_mut().add_child(
|
let node_events_view = Panel::new(
|
||||||
Panel::new(
|
|
||||||
FlexiLoggerView::new_scrollable()
|
FlexiLoggerView::new_scrollable()
|
||||||
.with_name("node-events")
|
.with_name("node-events")
|
||||||
.full_screen(),
|
.full_screen(),
|
||||||
)
|
)
|
||||||
.title_position(HAlign::Left)
|
.title_position(HAlign::Left)
|
||||||
.title("Node Events"),
|
.title("Node Events");
|
||||||
);
|
|
||||||
mainlayout.get_mut().add_child(
|
let peers_table_view = PeersTableView::new()
|
||||||
Panel::new(ScrollView::new(
|
.column(PeerTableColumn::NodeId, "Node Id", |c| c.width(43))
|
||||||
TextView::new("Peer Table")
|
.column(PeerTableColumn::Address, "Address", |c| c)
|
||||||
|
.column(PeerTableColumn::LatencyAvg, "Ping", |c| c.width(8))
|
||||||
|
.column(PeerTableColumn::TransferDownAvg, "Down", |c| c.width(8))
|
||||||
|
.column(PeerTableColumn::TransferUpAvg, "Up", |c| c.width(8))
|
||||||
.with_name("peers")
|
.with_name("peers")
|
||||||
.fixed_height(8)
|
.full_width()
|
||||||
.scrollable(),
|
.min_height(8);
|
||||||
))
|
|
||||||
.title_position(HAlign::Left)
|
// attempt at using Mux. Mux has bugs, like resizing problems.
|
||||||
.title("Peers"),
|
// let mut mux = Mux::new();
|
||||||
);
|
// let node_node_events_view = mux
|
||||||
|
// .add_below(node_events_view, mux.root().build().unwrap())
|
||||||
|
// .unwrap();
|
||||||
|
// let node_peers_table_view = mux
|
||||||
|
// .add_below(peers_table_view, node_node_events_view)
|
||||||
|
// .unwrap();
|
||||||
|
// mux.set_container_split_ratio(node_peers_table_view, 0.75)
|
||||||
|
// .unwrap();
|
||||||
|
// let mut mainlayout = LinearLayout::vertical();
|
||||||
|
// mainlayout.add_child(mux);
|
||||||
|
|
||||||
|
// Back to fixed layout
|
||||||
|
let mut mainlayout = LinearLayout::vertical();
|
||||||
|
mainlayout.add_child(node_events_view);
|
||||||
|
mainlayout.add_child(peers_table_view);
|
||||||
|
// ^^^ fixed layout
|
||||||
|
|
||||||
let mut command = StyledString::new();
|
let mut command = StyledString::new();
|
||||||
command.append_styled("Command> ", ColorStyle::title_primary());
|
command.append_styled("Command> ", ColorStyle::title_primary());
|
||||||
//
|
//
|
||||||
mainlayout.get_mut().add_child(
|
mainlayout.add_child(
|
||||||
LinearLayout::horizontal()
|
LinearLayout::horizontal()
|
||||||
.child(TextView::new(command))
|
.child(TextView::new(command))
|
||||||
.child(
|
.child(
|
||||||
@ -738,7 +778,7 @@ impl UI {
|
|||||||
ColorStyle::highlight_inactive(),
|
ColorStyle::highlight_inactive(),
|
||||||
);
|
);
|
||||||
|
|
||||||
mainlayout.get_mut().add_child(
|
mainlayout.add_child(
|
||||||
LinearLayout::horizontal()
|
LinearLayout::horizontal()
|
||||||
.color(Some(ColorStyle::highlight_inactive()))
|
.color(Some(ColorStyle::highlight_inactive()))
|
||||||
.child(
|
.child(
|
||||||
@ -776,13 +816,20 @@ impl UI {
|
|||||||
inner.ui_state.attachment_state.set(state);
|
inner.ui_state.attachment_state.set(state);
|
||||||
let _ = inner.cb_sink.send(Box::new(UI::update_cb));
|
let _ = inner.cb_sink.send(Box::new(UI::update_cb));
|
||||||
}
|
}
|
||||||
pub fn set_network_status(&mut self, started: bool, bps_down: u64, bps_up: u64) {
|
pub fn set_network_status(
|
||||||
|
&mut self,
|
||||||
|
started: bool,
|
||||||
|
bps_down: u64,
|
||||||
|
bps_up: u64,
|
||||||
|
peers: Vec<PeerTableData>,
|
||||||
|
) {
|
||||||
let mut inner = self.inner.borrow_mut();
|
let mut inner = self.inner.borrow_mut();
|
||||||
inner.ui_state.network_started.set(started);
|
inner.ui_state.network_started.set(started);
|
||||||
inner.ui_state.network_down_up.set((
|
inner.ui_state.network_down_up.set((
|
||||||
((bps_down as f64) / 1000.0f64) as f32,
|
((bps_down as f64) / 1000.0f64) as f32,
|
||||||
((bps_up as f64) / 1000.0f64) as f32,
|
((bps_up as f64) / 1000.0f64) as f32,
|
||||||
));
|
));
|
||||||
|
inner.ui_state.peers_state.set(peers);
|
||||||
let _ = inner.cb_sink.send(Box::new(UI::update_cb));
|
let _ = inner.cb_sink.send(Box::new(UI::update_cb));
|
||||||
}
|
}
|
||||||
pub fn set_connection_state(&mut self, state: ConnectionState) {
|
pub fn set_connection_state(&mut self, state: ConnectionState) {
|
||||||
@ -790,7 +837,8 @@ impl UI {
|
|||||||
inner.ui_state.connection_state.set(state);
|
inner.ui_state.connection_state.set(state);
|
||||||
let _ = inner.cb_sink.send(Box::new(UI::update_cb));
|
let _ = inner.cb_sink.send(Box::new(UI::update_cb));
|
||||||
}
|
}
|
||||||
pub fn add_node_event(&self, event: &str) {
|
|
||||||
|
pub fn add_node_event(&self, event: String) {
|
||||||
let inner = self.inner.borrow();
|
let inner = self.inner.borrow();
|
||||||
let color = *inner.log_colors.get(&Level::Info).unwrap();
|
let color = *inner.log_colors.get(&Level::Info).unwrap();
|
||||||
for line in event.lines() {
|
for line in event.lines() {
|
||||||
|
@ -15,8 +15,8 @@ rt-async-std = [ "async-std", "async-std-resolver", "async_executors/async_std",
|
|||||||
rt-tokio = [ "tokio", "tokio-util", "tokio-stream", "trust-dns-resolver/tokio-runtime", "async_executors/tokio_tp", "async_executors/tokio_io", "async_executors/tokio_timer", "rtnetlink?/tokio_socket" ]
|
rt-tokio = [ "tokio", "tokio-util", "tokio-stream", "trust-dns-resolver/tokio-runtime", "async_executors/tokio_tp", "async_executors/tokio_io", "async_executors/tokio_timer", "rtnetlink?/tokio_socket" ]
|
||||||
|
|
||||||
android_tests = []
|
android_tests = []
|
||||||
ios_tests = [ "simplelog", "backtrace" ]
|
ios_tests = [ "simplelog" ]
|
||||||
tracking = [ "backtrace" ]
|
tracking = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tracing = { version = "^0", features = ["log", "attributes"] }
|
tracing = { version = "^0", features = ["log", "attributes"] }
|
||||||
@ -41,9 +41,10 @@ lazy_static = "^1"
|
|||||||
directories = "^4"
|
directories = "^4"
|
||||||
once_cell = "^1"
|
once_cell = "^1"
|
||||||
json = "^0"
|
json = "^0"
|
||||||
|
owning_ref = "^0"
|
||||||
flume = { version = "^0", features = ["async"] }
|
flume = { version = "^0", features = ["async"] }
|
||||||
enumset = { version= "^1", features = ["serde"] }
|
enumset = { version= "^1", features = ["serde"] }
|
||||||
backtrace = { version = "^0", optional = true }
|
backtrace = { version = "^0" }
|
||||||
owo-colors = "^3"
|
owo-colors = "^3"
|
||||||
stop-token = { version = "^0", default-features = false }
|
stop-token = { version = "^0", default-features = false }
|
||||||
ed25519-dalek = { version = "^1", default_features = false, features = ["alloc", "u64_backend"] }
|
ed25519-dalek = { version = "^1", default_features = false, features = ["alloc", "u64_backend"] }
|
||||||
@ -134,7 +135,6 @@ jni-sys = "^0"
|
|||||||
ndk = { version = "^0", features = ["trace"] }
|
ndk = { version = "^0", features = ["trace"] }
|
||||||
ndk-glue = { version = "^0", features = ["logger"] }
|
ndk-glue = { version = "^0", features = ["logger"] }
|
||||||
tracing-android = { version = "^0" }
|
tracing-android = { version = "^0" }
|
||||||
backtrace = { version = "^0" }
|
|
||||||
|
|
||||||
# Dependenices for all Unix (Linux, Android, MacOS, iOS)
|
# Dependenices for all Unix (Linux, Android, MacOS, iOS)
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
|
@ -175,10 +175,6 @@ struct ValueData {
|
|||||||
# Operations
|
# Operations
|
||||||
##############################
|
##############################
|
||||||
|
|
||||||
struct OperationStatusQ {
|
|
||||||
nodeStatus @0 :NodeStatus; # node status update about the statusq sender
|
|
||||||
}
|
|
||||||
|
|
||||||
enum NetworkClass {
|
enum NetworkClass {
|
||||||
inboundCapable @0; # I = Inbound capable without relay, may require signal
|
inboundCapable @0; # I = Inbound capable without relay, may require signal
|
||||||
outboundOnly @1; # O = Outbound only, inbound relay required except with reverse connect signal
|
outboundOnly @1; # O = Outbound only, inbound relay required except with reverse connect signal
|
||||||
@ -199,7 +195,7 @@ struct DialInfoDetail {
|
|||||||
class @1 :DialInfoClass;
|
class @1 :DialInfoClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct NodeStatus {
|
struct PublicInternetNodeStatus {
|
||||||
willRoute @0 :Bool;
|
willRoute @0 :Bool;
|
||||||
willTunnel @1 :Bool;
|
willTunnel @1 :Bool;
|
||||||
willSignal @2 :Bool;
|
willSignal @2 :Bool;
|
||||||
@ -207,6 +203,18 @@ struct NodeStatus {
|
|||||||
willValidateDialInfo @4 :Bool;
|
willValidateDialInfo @4 :Bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct LocalNetworkNodeStatus {
|
||||||
|
willRelay @0 :Bool;
|
||||||
|
willValidateDialInfo @1 :Bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NodeStatus {
|
||||||
|
union {
|
||||||
|
publicInternet @0 :PublicInternetNodeStatus;
|
||||||
|
localNetwork @1 :LocalNetworkNodeStatus;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct ProtocolTypeSet {
|
struct ProtocolTypeSet {
|
||||||
udp @0 :Bool;
|
udp @0 :Bool;
|
||||||
tcp @1 :Bool;
|
tcp @1 :Bool;
|
||||||
@ -239,6 +247,21 @@ struct SenderInfo {
|
|||||||
socketAddress @0 :SocketAddress; # socket address was available for peer
|
socketAddress @0 :SocketAddress; # socket address was available for peer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct PeerInfo {
|
||||||
|
nodeId @0 :NodeID; # node id for 'closer peer'
|
||||||
|
signedNodeInfo @1 :SignedNodeInfo; # signed node info for 'closer peer'
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RoutedOperation {
|
||||||
|
signatures @0 :List(Signature); # signatures from nodes that have handled the private route
|
||||||
|
nonce @1 :Nonce; # nonce Xmsg
|
||||||
|
data @2 :Data; # Operation encrypted with ENC(Xmsg,DH(PKapr,SKbsr))
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OperationStatusQ {
|
||||||
|
nodeStatus @0 :NodeStatus; # node status update about the statusq sender
|
||||||
|
}
|
||||||
|
|
||||||
struct OperationStatusA {
|
struct OperationStatusA {
|
||||||
nodeStatus @0 :NodeStatus; # returned node status
|
nodeStatus @0 :NodeStatus; # returned node status
|
||||||
senderInfo @1 :SenderInfo; # info about StatusQ sender from the perspective of the replier
|
senderInfo @1 :SenderInfo; # info about StatusQ sender from the perspective of the replier
|
||||||
@ -258,21 +281,10 @@ struct OperationFindNodeQ {
|
|||||||
nodeId @0 :NodeID; # node id to locate
|
nodeId @0 :NodeID; # node id to locate
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PeerInfo {
|
|
||||||
nodeId @0 :NodeID; # node id for 'closer peer'
|
|
||||||
signedNodeInfo @1 :SignedNodeInfo; # signed node info for 'closer peer'
|
|
||||||
}
|
|
||||||
|
|
||||||
struct OperationFindNodeA {
|
struct OperationFindNodeA {
|
||||||
peers @0 :List(PeerInfo); # returned 'closer peer' information
|
peers @0 :List(PeerInfo); # returned 'closer peer' information
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RoutedOperation {
|
|
||||||
signatures @0 :List(Signature); # signatures from nodes that have handled the private route
|
|
||||||
nonce @1 :Nonce; # nonce Xmsg
|
|
||||||
data @2 :Data; # Operation encrypted with ENC(Xmsg,DH(PKapr,SKbsr))
|
|
||||||
}
|
|
||||||
|
|
||||||
struct OperationRoute {
|
struct OperationRoute {
|
||||||
safetyRoute @0 :SafetyRoute; # Where this should go
|
safetyRoute @0 :SafetyRoute; # Where this should go
|
||||||
operation @1 :RoutedOperation; # The operation to be routed
|
operation @1 :RoutedOperation; # The operation to be routed
|
||||||
@ -419,26 +431,25 @@ struct OperationCancelTunnelA {
|
|||||||
# Things that want an answer
|
# Things that want an answer
|
||||||
struct Question {
|
struct Question {
|
||||||
respondTo :union {
|
respondTo :union {
|
||||||
sender @0 :Void; # sender without node info
|
sender @0 :Void; # sender
|
||||||
senderWithInfo @1 :SignedNodeInfo; # some envelope-sender signed node info to be used for reply
|
privateRoute @1 :PrivateRoute; # embedded private route to be used for reply
|
||||||
privateRoute @2 :PrivateRoute; # embedded private route to be used for reply
|
|
||||||
}
|
}
|
||||||
detail :union {
|
detail :union {
|
||||||
# Direct operations
|
# Direct operations
|
||||||
statusQ @3 :OperationStatusQ;
|
statusQ @2 :OperationStatusQ;
|
||||||
findNodeQ @4 :OperationFindNodeQ;
|
findNodeQ @3 :OperationFindNodeQ;
|
||||||
|
|
||||||
# Routable operations
|
# Routable operations
|
||||||
getValueQ @5 :OperationGetValueQ;
|
getValueQ @4 :OperationGetValueQ;
|
||||||
setValueQ @6 :OperationSetValueQ;
|
setValueQ @5 :OperationSetValueQ;
|
||||||
watchValueQ @7 :OperationWatchValueQ;
|
watchValueQ @6 :OperationWatchValueQ;
|
||||||
supplyBlockQ @8 :OperationSupplyBlockQ;
|
supplyBlockQ @7 :OperationSupplyBlockQ;
|
||||||
findBlockQ @9 :OperationFindBlockQ;
|
findBlockQ @8 :OperationFindBlockQ;
|
||||||
|
|
||||||
# Tunnel operations
|
# Tunnel operations
|
||||||
startTunnelQ @10 :OperationStartTunnelQ;
|
startTunnelQ @9 :OperationStartTunnelQ;
|
||||||
completeTunnelQ @11 :OperationCompleteTunnelQ;
|
completeTunnelQ @10 :OperationCompleteTunnelQ;
|
||||||
cancelTunnelQ @12 :OperationCancelTunnelQ;
|
cancelTunnelQ @11 :OperationCancelTunnelQ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -480,10 +491,10 @@ struct Answer {
|
|||||||
|
|
||||||
struct Operation {
|
struct Operation {
|
||||||
opId @0 :UInt64; # Random RPC ID. Must be random to foil reply forgery attacks.
|
opId @0 :UInt64; # Random RPC ID. Must be random to foil reply forgery attacks.
|
||||||
|
senderNodeInfo @1 :SignedNodeInfo; # (optional) SignedNodeInfo for the sender to be cached by the receiver.
|
||||||
kind :union {
|
kind :union {
|
||||||
question @1 :Question;
|
question @2 :Question;
|
||||||
statement @2 :Statement;
|
statement @3 :Statement;
|
||||||
answer @3 :Answer;
|
answer @4 :Answer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,18 @@ impl<S: Subscriber + for<'a> registry::LookupSpan<'a>> Layer<S> for ApiTracingLa
|
|||||||
|
|
||||||
let message = format!("{} {}", origin, recorder);
|
let message = format!("{} {}", origin, recorder);
|
||||||
|
|
||||||
(inner.update_callback)(VeilidUpdate::Log(VeilidStateLog { log_level, message }))
|
let backtrace = if log_level <= VeilidLogLevel::Error {
|
||||||
|
let bt = backtrace::Backtrace::new();
|
||||||
|
Some(format!("{:?}", bt))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
(inner.update_callback)(VeilidUpdate::Log(VeilidStateLog {
|
||||||
|
log_level,
|
||||||
|
message,
|
||||||
|
backtrace,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ use super::*;
|
|||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ConnectionHandle {
|
pub struct ConnectionHandle {
|
||||||
|
id: u64,
|
||||||
descriptor: ConnectionDescriptor,
|
descriptor: ConnectionDescriptor,
|
||||||
channel: flume::Sender<Vec<u8>>,
|
channel: flume::Sender<Vec<u8>>,
|
||||||
}
|
}
|
||||||
@ -13,13 +14,22 @@ pub enum ConnectionHandleSendResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ConnectionHandle {
|
impl ConnectionHandle {
|
||||||
pub(super) fn new(descriptor: ConnectionDescriptor, channel: flume::Sender<Vec<u8>>) -> Self {
|
pub(super) fn new(
|
||||||
|
id: u64,
|
||||||
|
descriptor: ConnectionDescriptor,
|
||||||
|
channel: flume::Sender<Vec<u8>>,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
id,
|
||||||
descriptor,
|
descriptor,
|
||||||
channel,
|
channel,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn connection_id(&self) -> u64 {
|
||||||
|
self.id
|
||||||
|
}
|
||||||
|
|
||||||
pub fn connection_descriptor(&self) -> ConnectionDescriptor {
|
pub fn connection_descriptor(&self) -> ConnectionDescriptor {
|
||||||
self.descriptor.clone()
|
self.descriptor.clone()
|
||||||
}
|
}
|
||||||
|
@ -11,12 +11,11 @@ use stop_token::future::FutureExt;
|
|||||||
enum ConnectionManagerEvent {
|
enum ConnectionManagerEvent {
|
||||||
Accepted(ProtocolNetworkConnection),
|
Accepted(ProtocolNetworkConnection),
|
||||||
Dead(NetworkConnection),
|
Dead(NetworkConnection),
|
||||||
Finished(ConnectionDescriptor),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct ConnectionManagerInner {
|
struct ConnectionManagerInner {
|
||||||
connection_table: ConnectionTable,
|
next_id: NetworkConnectionId,
|
||||||
sender: flume::Sender<ConnectionManagerEvent>,
|
sender: flume::Sender<ConnectionManagerEvent>,
|
||||||
async_processor_jh: Option<MustJoinHandle<()>>,
|
async_processor_jh: Option<MustJoinHandle<()>>,
|
||||||
stop_source: Option<StopSource>,
|
stop_source: Option<StopSource>,
|
||||||
@ -24,6 +23,9 @@ struct ConnectionManagerInner {
|
|||||||
|
|
||||||
struct ConnectionManagerArc {
|
struct ConnectionManagerArc {
|
||||||
network_manager: NetworkManager,
|
network_manager: NetworkManager,
|
||||||
|
connection_initial_timeout_ms: u32,
|
||||||
|
connection_inactivity_timeout_ms: u32,
|
||||||
|
connection_table: ConnectionTable,
|
||||||
inner: Mutex<Option<ConnectionManagerInner>>,
|
inner: Mutex<Option<ConnectionManagerInner>>,
|
||||||
}
|
}
|
||||||
impl core::fmt::Debug for ConnectionManagerArc {
|
impl core::fmt::Debug for ConnectionManagerArc {
|
||||||
@ -41,21 +43,32 @@ pub struct ConnectionManager {
|
|||||||
|
|
||||||
impl ConnectionManager {
|
impl ConnectionManager {
|
||||||
fn new_inner(
|
fn new_inner(
|
||||||
config: VeilidConfig,
|
|
||||||
stop_source: StopSource,
|
stop_source: StopSource,
|
||||||
sender: flume::Sender<ConnectionManagerEvent>,
|
sender: flume::Sender<ConnectionManagerEvent>,
|
||||||
async_processor_jh: MustJoinHandle<()>,
|
async_processor_jh: MustJoinHandle<()>,
|
||||||
) -> ConnectionManagerInner {
|
) -> ConnectionManagerInner {
|
||||||
ConnectionManagerInner {
|
ConnectionManagerInner {
|
||||||
|
next_id: 0,
|
||||||
stop_source: Some(stop_source),
|
stop_source: Some(stop_source),
|
||||||
sender: sender,
|
sender: sender,
|
||||||
async_processor_jh: Some(async_processor_jh),
|
async_processor_jh: Some(async_processor_jh),
|
||||||
connection_table: ConnectionTable::new(config),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn new_arc(network_manager: NetworkManager) -> ConnectionManagerArc {
|
fn new_arc(network_manager: NetworkManager) -> ConnectionManagerArc {
|
||||||
|
let config = network_manager.config();
|
||||||
|
let (connection_initial_timeout_ms, connection_inactivity_timeout_ms) = {
|
||||||
|
let c = config.get();
|
||||||
|
(
|
||||||
|
c.network.connection_initial_timeout_ms,
|
||||||
|
c.network.connection_inactivity_timeout_ms,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
ConnectionManagerArc {
|
ConnectionManagerArc {
|
||||||
network_manager,
|
network_manager,
|
||||||
|
connection_initial_timeout_ms,
|
||||||
|
connection_inactivity_timeout_ms,
|
||||||
|
connection_table: ConnectionTable::new(config),
|
||||||
inner: Mutex::new(None),
|
inner: Mutex::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -69,6 +82,14 @@ impl ConnectionManager {
|
|||||||
self.arc.network_manager.clone()
|
self.arc.network_manager.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn connection_initial_timeout_ms(&self) -> u32 {
|
||||||
|
self.arc.connection_initial_timeout_ms
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn connection_inactivity_timeout_ms(&self) -> u32 {
|
||||||
|
self.arc.connection_inactivity_timeout_ms
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn startup(&self) {
|
pub async fn startup(&self) {
|
||||||
trace!("startup connection manager");
|
trace!("startup connection manager");
|
||||||
let mut inner = self.arc.inner.lock();
|
let mut inner = self.arc.inner.lock();
|
||||||
@ -86,12 +107,7 @@ impl ConnectionManager {
|
|||||||
let async_processor = spawn(self.clone().async_processor(stop_source.token(), receiver));
|
let async_processor = spawn(self.clone().async_processor(stop_source.token(), receiver));
|
||||||
|
|
||||||
// Store in the inner object
|
// Store in the inner object
|
||||||
*inner = Some(Self::new_inner(
|
*inner = Some(Self::new_inner(stop_source, sender, async_processor));
|
||||||
self.network_manager().config(),
|
|
||||||
stop_source,
|
|
||||||
sender,
|
|
||||||
async_processor,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn shutdown(&self) {
|
pub async fn shutdown(&self) {
|
||||||
@ -117,25 +133,10 @@ impl ConnectionManager {
|
|||||||
async_processor_jh.await;
|
async_processor_jh.await;
|
||||||
// Wait for the connections to complete
|
// Wait for the connections to complete
|
||||||
debug!("waiting for connection handlers to complete");
|
debug!("waiting for connection handlers to complete");
|
||||||
inner.connection_table.join().await;
|
self.arc.connection_table.join().await;
|
||||||
debug!("finished connection manager shutdown");
|
debug!("finished connection manager shutdown");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a network connection if one already is established
|
|
||||||
pub async fn get_connection(
|
|
||||||
&self,
|
|
||||||
descriptor: ConnectionDescriptor,
|
|
||||||
) -> Option<ConnectionHandle> {
|
|
||||||
let mut inner = self.arc.inner.lock();
|
|
||||||
let inner = match &mut *inner {
|
|
||||||
Some(v) => v,
|
|
||||||
None => {
|
|
||||||
panic!("not started");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
inner.connection_table.get_connection(descriptor)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal routine to register new connection atomically.
|
// Internal routine to register new connection atomically.
|
||||||
// Registers connection in the connection table for later access
|
// Registers connection in the connection table for later access
|
||||||
// and spawns a message processing loop for the connection
|
// and spawns a message processing loop for the connection
|
||||||
@ -144,7 +145,14 @@ impl ConnectionManager {
|
|||||||
inner: &mut ConnectionManagerInner,
|
inner: &mut ConnectionManagerInner,
|
||||||
prot_conn: ProtocolNetworkConnection,
|
prot_conn: ProtocolNetworkConnection,
|
||||||
) -> EyreResult<NetworkResult<ConnectionHandle>> {
|
) -> EyreResult<NetworkResult<ConnectionHandle>> {
|
||||||
log_net!("on_new_protocol_network_connection: {:?}", prot_conn);
|
// Get next connection id to use
|
||||||
|
let id = inner.next_id;
|
||||||
|
inner.next_id += 1;
|
||||||
|
log_net!(
|
||||||
|
"on_new_protocol_network_connection: id={} prot_conn={:?}",
|
||||||
|
id,
|
||||||
|
prot_conn
|
||||||
|
);
|
||||||
|
|
||||||
// Wrap with NetworkConnection object to start the connection processing loop
|
// Wrap with NetworkConnection object to start the connection processing loop
|
||||||
let stop_token = match &inner.stop_source {
|
let stop_token = match &inner.stop_source {
|
||||||
@ -152,71 +160,112 @@ impl ConnectionManager {
|
|||||||
None => bail!("not creating connection because we are stopping"),
|
None => bail!("not creating connection because we are stopping"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let conn = NetworkConnection::from_protocol(self.clone(), stop_token, prot_conn);
|
let conn = NetworkConnection::from_protocol(self.clone(), stop_token, prot_conn, id);
|
||||||
let handle = conn.get_handle();
|
let handle = conn.get_handle();
|
||||||
// Add to the connection table
|
// Add to the connection table
|
||||||
match inner.connection_table.add_connection(conn) {
|
match self.arc.connection_table.add_connection(conn) {
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
// Connection added
|
// Connection added
|
||||||
}
|
}
|
||||||
Ok(Some(conn)) => {
|
Ok(Some(conn)) => {
|
||||||
// Connection added and a different one LRU'd out
|
// Connection added and a different one LRU'd out
|
||||||
|
// Send it to be terminated
|
||||||
let _ = inner.sender.send(ConnectionManagerEvent::Dead(conn));
|
let _ = inner.sender.send(ConnectionManagerEvent::Dead(conn));
|
||||||
}
|
}
|
||||||
Err(ConnectionTableAddError::AddressFilter(conn, e)) => {
|
Err(ConnectionTableAddError::AddressFilter(conn, e)) => {
|
||||||
// Connection filtered
|
// Connection filtered
|
||||||
let desc = conn.connection_descriptor();
|
let desc = conn.connection_descriptor();
|
||||||
let _ = inner.sender.send(ConnectionManagerEvent::Dead(conn));
|
let _ = inner.sender.send(ConnectionManagerEvent::Dead(conn));
|
||||||
return Err(eyre!("connection filtered: {:?} ({})", desc, e));
|
return Ok(NetworkResult::no_connection_other(format!(
|
||||||
|
"connection filtered: {:?} ({})",
|
||||||
|
desc, e
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
Err(ConnectionTableAddError::AlreadyExists(conn)) => {
|
Err(ConnectionTableAddError::AlreadyExists(conn)) => {
|
||||||
// Connection already exists
|
// Connection already exists
|
||||||
let desc = conn.connection_descriptor();
|
let desc = conn.connection_descriptor();
|
||||||
let _ = inner.sender.send(ConnectionManagerEvent::Dead(conn));
|
let _ = inner.sender.send(ConnectionManagerEvent::Dead(conn));
|
||||||
return Err(eyre!("connection already exists: {:?}", desc));
|
return Ok(NetworkResult::no_connection_other(format!(
|
||||||
|
"connection already exists: {:?}",
|
||||||
|
desc
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(NetworkResult::Value(handle))
|
Ok(NetworkResult::Value(handle))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns a network connection if one already is established
|
||||||
|
pub fn get_connection(&self, descriptor: ConnectionDescriptor) -> Option<ConnectionHandle> {
|
||||||
|
self.arc
|
||||||
|
.connection_table
|
||||||
|
.get_connection_by_descriptor(descriptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Terminate any connections that would collide with a new connection
|
||||||
|
// using different protocols to the same remote address and port. Used to ensure
|
||||||
|
// that we can switch quickly between TCP and WS if necessary to the same node
|
||||||
|
// Returns true if we killed off colliding connections
|
||||||
|
async fn kill_off_colliding_connections(&self, dial_info: &DialInfo) -> bool {
|
||||||
|
let protocol_type = dial_info.protocol_type();
|
||||||
|
let socket_address = dial_info.socket_address();
|
||||||
|
|
||||||
|
let killed = self.arc.connection_table.drain_filter(|prior_descriptor| {
|
||||||
|
// If the protocol types aren't the same, then this is a candidate to be killed off
|
||||||
|
// If they are the same, then we would just return the exact same connection from get_or_create_connection()
|
||||||
|
if prior_descriptor.protocol_type() == protocol_type {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// If the prior remote is not the same address, then we're not going to collide
|
||||||
|
if *prior_descriptor.remote().socket_address() != socket_address {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_net!(debug
|
||||||
|
">< Terminating connection prior_descriptor={:?}",
|
||||||
|
prior_descriptor
|
||||||
|
);
|
||||||
|
true
|
||||||
|
});
|
||||||
|
// Wait for the killed connections to end their recv loops
|
||||||
|
let did_kill = !killed.is_empty();
|
||||||
|
for k in killed {
|
||||||
|
k.await;
|
||||||
|
}
|
||||||
|
did_kill
|
||||||
|
}
|
||||||
|
|
||||||
// Called when we want to create a new connection or get the current one that already exists
|
// Called when we want to create a new connection or get the current one that already exists
|
||||||
// This will kill off any connections that are in conflict with the new connection to be made
|
// This will kill off any connections that are in conflict with the new connection to be made
|
||||||
// in order to make room for the new connection in the system's connection table
|
// in order to make room for the new connection in the system's connection table
|
||||||
|
// This routine needs to be atomic, or connections may exist in the table that are not established
|
||||||
pub async fn get_or_create_connection(
|
pub async fn get_or_create_connection(
|
||||||
&self,
|
&self,
|
||||||
local_addr: Option<SocketAddr>,
|
local_addr: Option<SocketAddr>,
|
||||||
dial_info: DialInfo,
|
dial_info: DialInfo,
|
||||||
) -> EyreResult<NetworkResult<ConnectionHandle>> {
|
) -> EyreResult<NetworkResult<ConnectionHandle>> {
|
||||||
let killed = {
|
|
||||||
let mut inner = self.arc.inner.lock();
|
|
||||||
let inner = match &mut *inner {
|
|
||||||
Some(v) => v,
|
|
||||||
None => {
|
|
||||||
panic!("not started");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
log_net!(
|
log_net!(
|
||||||
"== get_or_create_connection local_addr={:?} dial_info={:?}",
|
"== get_or_create_connection local_addr={:?} dial_info={:?}",
|
||||||
local_addr.green(),
|
local_addr.green(),
|
||||||
dial_info.green()
|
dial_info.green()
|
||||||
);
|
);
|
||||||
|
|
||||||
let peer_address = dial_info.to_peer_address();
|
// Kill off any possibly conflicting connections
|
||||||
|
let did_kill = self.kill_off_colliding_connections(&dial_info).await;
|
||||||
|
let mut retry_count = if did_kill { 2 } else { 0 };
|
||||||
|
|
||||||
// Make a connection to the address
|
// Make a connection descriptor for this dialinfo
|
||||||
// reject connections to addresses with an unknown or unsupported peer scope
|
let peer_address = dial_info.to_peer_address();
|
||||||
let descriptor = match local_addr {
|
let descriptor = match local_addr {
|
||||||
Some(la) => {
|
Some(la) => {
|
||||||
ConnectionDescriptor::new(peer_address, SocketAddress::from_socket_addr(la))
|
ConnectionDescriptor::new(peer_address, SocketAddress::from_socket_addr(la))
|
||||||
}
|
}
|
||||||
None => ConnectionDescriptor::new_no_local(peer_address),
|
None => ConnectionDescriptor::new_no_local(peer_address),
|
||||||
}?;
|
};
|
||||||
|
|
||||||
// If any connection to this remote exists that has the same protocol, return it
|
// If any connection to this remote exists that has the same protocol, return it
|
||||||
// Any connection will do, we don't have to match the local address
|
// Any connection will do, we don't have to match the local address
|
||||||
|
if let Some(conn) = self
|
||||||
if let Some(conn) = inner
|
.arc
|
||||||
.connection_table
|
.connection_table
|
||||||
.get_last_connection_by_remote(descriptor.remote())
|
.get_last_connection_by_remote(descriptor.remote())
|
||||||
{
|
{
|
||||||
@ -229,66 +278,14 @@ impl ConnectionManager {
|
|||||||
return Ok(NetworkResult::Value(conn));
|
return Ok(NetworkResult::Value(conn));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drop any other protocols connections to this remote that have the same local addr
|
|
||||||
// otherwise this connection won't succeed due to binding
|
|
||||||
let mut killed = Vec::<NetworkConnection>::new();
|
|
||||||
if let Some(local_addr) = local_addr {
|
|
||||||
if local_addr.port() != 0 {
|
|
||||||
for pt in [ProtocolType::TCP, ProtocolType::WS, ProtocolType::WSS] {
|
|
||||||
let pa = PeerAddress::new(descriptor.remote_address().clone(), pt);
|
|
||||||
for prior_descriptor in inner
|
|
||||||
.connection_table
|
|
||||||
.get_connection_descriptors_by_remote(pa)
|
|
||||||
{
|
|
||||||
let mut kill = false;
|
|
||||||
// See if the local address would collide
|
|
||||||
if let Some(prior_local) = prior_descriptor.local() {
|
|
||||||
if (local_addr.ip().is_unspecified()
|
|
||||||
|| prior_local.to_ip_addr().is_unspecified()
|
|
||||||
|| (local_addr.ip() == prior_local.to_ip_addr()))
|
|
||||||
&& prior_local.port() == local_addr.port()
|
|
||||||
{
|
|
||||||
kill = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if kill {
|
|
||||||
log_net!(debug
|
|
||||||
">< Terminating connection prior_descriptor={:?}",
|
|
||||||
prior_descriptor
|
|
||||||
);
|
|
||||||
let mut conn = inner
|
|
||||||
.connection_table
|
|
||||||
.remove_connection(prior_descriptor)
|
|
||||||
.expect("connection not in table");
|
|
||||||
|
|
||||||
conn.close();
|
|
||||||
|
|
||||||
killed.push(conn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
killed
|
|
||||||
};
|
|
||||||
|
|
||||||
// Wait for the killed connections to end their recv loops
|
|
||||||
let mut retry_count = if !killed.is_empty() { 2 } else { 0 };
|
|
||||||
for k in killed {
|
|
||||||
k.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get connection timeout
|
|
||||||
let timeout_ms = {
|
|
||||||
let config = self.network_manager().config();
|
|
||||||
let c = config.get();
|
|
||||||
c.network.connection_initial_timeout_ms
|
|
||||||
};
|
|
||||||
|
|
||||||
// Attempt new connection
|
// Attempt new connection
|
||||||
let conn = network_result_try!(loop {
|
let prot_conn = network_result_try!(loop {
|
||||||
let result_net_res =
|
let result_net_res = ProtocolNetworkConnection::connect(
|
||||||
ProtocolNetworkConnection::connect(local_addr, &dial_info, timeout_ms).await;
|
local_addr,
|
||||||
|
&dial_info,
|
||||||
|
self.arc.connection_initial_timeout_ms,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
match result_net_res {
|
match result_net_res {
|
||||||
Ok(net_res) => {
|
Ok(net_res) => {
|
||||||
if net_res.is_value() || retry_count == 0 {
|
if net_res.is_value() || retry_count == 0 {
|
||||||
@ -314,7 +311,8 @@ impl ConnectionManager {
|
|||||||
bail!("shutting down");
|
bail!("shutting down");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.on_new_protocol_network_connection(inner, conn)
|
|
||||||
|
self.on_new_protocol_network_connection(inner, prot_conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -347,27 +345,6 @@ impl ConnectionManager {
|
|||||||
conn.close();
|
conn.close();
|
||||||
conn.await;
|
conn.await;
|
||||||
}
|
}
|
||||||
ConnectionManagerEvent::Finished(desc) => {
|
|
||||||
let conn = {
|
|
||||||
let mut inner_lock = self.arc.inner.lock();
|
|
||||||
match &mut *inner_lock {
|
|
||||||
Some(inner) => {
|
|
||||||
// Remove the connection and wait for the connection loop to terminate
|
|
||||||
if let Ok(conn) = inner.connection_table.remove_connection(desc) {
|
|
||||||
// Must close and wait to ensure things join
|
|
||||||
Some(conn)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if let Some(mut conn) = conn {
|
|
||||||
conn.close();
|
|
||||||
conn.await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -377,7 +354,7 @@ impl ConnectionManager {
|
|||||||
#[cfg_attr(target_os = "wasm32", allow(dead_code))]
|
#[cfg_attr(target_os = "wasm32", allow(dead_code))]
|
||||||
pub(super) async fn on_accepted_protocol_network_connection(
|
pub(super) async fn on_accepted_protocol_network_connection(
|
||||||
&self,
|
&self,
|
||||||
conn: ProtocolNetworkConnection,
|
protocol_connection: ProtocolNetworkConnection,
|
||||||
) -> EyreResult<()> {
|
) -> EyreResult<()> {
|
||||||
// Get channel sender
|
// Get channel sender
|
||||||
let sender = {
|
let sender = {
|
||||||
@ -394,14 +371,14 @@ impl ConnectionManager {
|
|||||||
|
|
||||||
// Inform the processor of the event
|
// Inform the processor of the event
|
||||||
let _ = sender
|
let _ = sender
|
||||||
.send_async(ConnectionManagerEvent::Accepted(conn))
|
.send_async(ConnectionManagerEvent::Accepted(protocol_connection))
|
||||||
.await;
|
.await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Callback from network connection receive loop when it exits
|
// Callback from network connection receive loop when it exits
|
||||||
// cleans up the entry in the connection table
|
// cleans up the entry in the connection table
|
||||||
pub(super) async fn report_connection_finished(&self, descriptor: ConnectionDescriptor) {
|
pub(super) async fn report_connection_finished(&self, connection_id: u64) {
|
||||||
// Get channel sender
|
// Get channel sender
|
||||||
let sender = {
|
let sender = {
|
||||||
let mut inner = self.arc.inner.lock();
|
let mut inner = self.arc.inner.lock();
|
||||||
@ -415,9 +392,15 @@ impl ConnectionManager {
|
|||||||
inner.sender.clone()
|
inner.sender.clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Remove the connection
|
||||||
|
let conn = self
|
||||||
|
.arc
|
||||||
|
.connection_table
|
||||||
|
.remove_connection_by_id(connection_id);
|
||||||
|
|
||||||
// Inform the processor of the event
|
// Inform the processor of the event
|
||||||
let _ = sender
|
if let Some(conn) = conn {
|
||||||
.send_async(ConnectionManagerEvent::Finished(descriptor))
|
let _ = sender.send_async(ConnectionManagerEvent::Dead(conn)).await;
|
||||||
.await;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use alloc::collections::btree_map::Entry;
|
|
||||||
use futures_util::StreamExt;
|
use futures_util::StreamExt;
|
||||||
use hashlink::LruCache;
|
use hashlink::LruCache;
|
||||||
|
|
||||||
@ -21,36 +20,21 @@ impl ConnectionTableAddError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
#[derive(ThisError, Debug)]
|
|
||||||
pub enum ConnectionTableRemoveError {
|
|
||||||
#[error("Connection not in table")]
|
|
||||||
NotInTable,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ConnectionTableRemoveError {
|
|
||||||
pub fn not_in_table() -> Self {
|
|
||||||
ConnectionTableRemoveError::NotInTable
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ConnectionTable {
|
pub struct ConnectionTableInner {
|
||||||
max_connections: Vec<usize>,
|
max_connections: Vec<usize>,
|
||||||
conn_by_descriptor: Vec<LruCache<ConnectionDescriptor, NetworkConnection>>,
|
conn_by_id: Vec<LruCache<NetworkConnectionId, NetworkConnection>>,
|
||||||
descriptors_by_remote: BTreeMap<PeerAddress, Vec<ConnectionDescriptor>>,
|
protocol_index_by_id: BTreeMap<NetworkConnectionId, usize>,
|
||||||
|
id_by_descriptor: BTreeMap<ConnectionDescriptor, NetworkConnectionId>,
|
||||||
|
ids_by_remote: BTreeMap<PeerAddress, Vec<NetworkConnectionId>>,
|
||||||
address_filter: ConnectionLimits,
|
address_filter: ConnectionLimits,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn protocol_to_index(protocol: ProtocolType) -> usize {
|
#[derive(Debug)]
|
||||||
match protocol {
|
pub struct ConnectionTable {
|
||||||
ProtocolType::TCP => 0,
|
inner: Arc<Mutex<ConnectionTableInner>>,
|
||||||
ProtocolType::WS => 1,
|
|
||||||
ProtocolType::WSS => 2,
|
|
||||||
ProtocolType::UDP => panic!("not a connection-oriented protocol"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConnectionTable {
|
impl ConnectionTable {
|
||||||
@ -64,154 +48,217 @@ impl ConnectionTable {
|
|||||||
]
|
]
|
||||||
};
|
};
|
||||||
Self {
|
Self {
|
||||||
|
inner: Arc::new(Mutex::new(ConnectionTableInner {
|
||||||
max_connections,
|
max_connections,
|
||||||
conn_by_descriptor: vec![
|
conn_by_id: vec![
|
||||||
LruCache::new_unbounded(),
|
LruCache::new_unbounded(),
|
||||||
LruCache::new_unbounded(),
|
LruCache::new_unbounded(),
|
||||||
LruCache::new_unbounded(),
|
LruCache::new_unbounded(),
|
||||||
],
|
],
|
||||||
descriptors_by_remote: BTreeMap::new(),
|
protocol_index_by_id: BTreeMap::new(),
|
||||||
|
id_by_descriptor: BTreeMap::new(),
|
||||||
|
ids_by_remote: BTreeMap::new(),
|
||||||
address_filter: ConnectionLimits::new(config),
|
address_filter: ConnectionLimits::new(config),
|
||||||
|
})),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn join(&mut self) {
|
fn protocol_to_index(protocol: ProtocolType) -> usize {
|
||||||
let mut unord = FuturesUnordered::new();
|
match protocol {
|
||||||
for table in &mut self.conn_by_descriptor {
|
ProtocolType::TCP => 0,
|
||||||
|
ProtocolType::WS => 1,
|
||||||
|
ProtocolType::WSS => 2,
|
||||||
|
ProtocolType::UDP => panic!("not a connection-oriented protocol"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn join(&self) {
|
||||||
|
let mut unord = {
|
||||||
|
let mut inner = self.inner.lock();
|
||||||
|
let unord = FuturesUnordered::new();
|
||||||
|
for table in &mut inner.conn_by_id {
|
||||||
for (_, v) in table.drain() {
|
for (_, v) in table.drain() {
|
||||||
trace!("connection table join: {:?}", v);
|
trace!("connection table join: {:?}", v);
|
||||||
unord.push(v);
|
unord.push(v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
inner.id_by_descriptor.clear();
|
||||||
|
inner.ids_by_remote.clear();
|
||||||
|
unord
|
||||||
|
};
|
||||||
|
|
||||||
while unord.next().await.is_some() {}
|
while unord.next().await.is_some() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_connection(
|
pub fn add_connection(
|
||||||
&mut self,
|
&self,
|
||||||
conn: NetworkConnection,
|
network_connection: NetworkConnection,
|
||||||
) -> Result<Option<NetworkConnection>, ConnectionTableAddError> {
|
) -> Result<Option<NetworkConnection>, ConnectionTableAddError> {
|
||||||
let descriptor = conn.connection_descriptor();
|
// Get indices for network connection table
|
||||||
let ip_addr = descriptor.remote_address().to_ip_addr();
|
let id = network_connection.connection_id();
|
||||||
|
let descriptor = network_connection.connection_descriptor();
|
||||||
|
let protocol_index = Self::protocol_to_index(descriptor.protocol_type());
|
||||||
|
let remote = descriptor.remote();
|
||||||
|
|
||||||
let index = protocol_to_index(descriptor.protocol_type());
|
let mut inner = self.inner.lock();
|
||||||
if self.conn_by_descriptor[index].contains_key(&descriptor) {
|
|
||||||
return Err(ConnectionTableAddError::already_exists(conn));
|
// Two connections to the same descriptor should be rejected (soft rejection)
|
||||||
|
if inner.id_by_descriptor.contains_key(&descriptor) {
|
||||||
|
return Err(ConnectionTableAddError::already_exists(network_connection));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity checking this implementation (hard fails that would invalidate the representation)
|
||||||
|
if inner.conn_by_id[protocol_index].contains_key(&id) {
|
||||||
|
panic!("duplicate connection id: {:#?}", network_connection);
|
||||||
|
}
|
||||||
|
if inner.protocol_index_by_id.get(&id).is_some() {
|
||||||
|
panic!("duplicate id to protocol index: {:#?}", network_connection);
|
||||||
|
}
|
||||||
|
if let Some(ids) = inner.ids_by_remote.get(&descriptor.remote()) {
|
||||||
|
if ids.contains(&id) {
|
||||||
|
panic!("duplicate id by remote: {:#?}", network_connection);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter by ip for connection limits
|
// Filter by ip for connection limits
|
||||||
match self.address_filter.add(ip_addr) {
|
let ip_addr = descriptor.remote_address().to_ip_addr();
|
||||||
|
match inner.address_filter.add(ip_addr) {
|
||||||
Ok(()) => {}
|
Ok(()) => {}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// send connection to get cleaned up cleanly
|
// Return the connection in the error to be disposed of
|
||||||
return Err(ConnectionTableAddError::address_filter(conn, e));
|
return Err(ConnectionTableAddError::address_filter(
|
||||||
|
network_connection,
|
||||||
|
e,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add the connection to the table
|
// Add the connection to the table
|
||||||
let res = self.conn_by_descriptor[index].insert(descriptor.clone(), conn);
|
let res = inner.conn_by_id[protocol_index].insert(id, network_connection);
|
||||||
assert!(res.is_none());
|
assert!(res.is_none());
|
||||||
|
|
||||||
// if we have reached the maximum number of connections per protocol type
|
// if we have reached the maximum number of connections per protocol type
|
||||||
// then drop the least recently used connection
|
// then drop the least recently used connection
|
||||||
let mut out_conn = None;
|
let mut out_conn = None;
|
||||||
if self.conn_by_descriptor[index].len() > self.max_connections[index] {
|
if inner.conn_by_id[protocol_index].len() > inner.max_connections[protocol_index] {
|
||||||
if let Some((lruk, lru_conn)) = self.conn_by_descriptor[index].remove_lru() {
|
if let Some((lruk, lru_conn)) = inner.conn_by_id[protocol_index].remove_lru() {
|
||||||
debug!("connection lru out: {:?}", lruk);
|
debug!("connection lru out: {:?}", lru_conn);
|
||||||
out_conn = Some(lru_conn);
|
out_conn = Some(lru_conn);
|
||||||
self.remove_connection_records(lruk);
|
Self::remove_connection_records(&mut *inner, lruk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// add connection records
|
// add connection records
|
||||||
let descriptors = self
|
inner.protocol_index_by_id.insert(id, protocol_index);
|
||||||
.descriptors_by_remote
|
inner.id_by_descriptor.insert(descriptor, id);
|
||||||
.entry(descriptor.remote())
|
inner.ids_by_remote.entry(remote).or_default().push(id);
|
||||||
.or_default();
|
|
||||||
|
|
||||||
descriptors.push(descriptor);
|
|
||||||
|
|
||||||
Ok(out_conn)
|
Ok(out_conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_connection(&mut self, descriptor: ConnectionDescriptor) -> Option<ConnectionHandle> {
|
pub fn get_connection_by_id(&self, id: NetworkConnectionId) -> Option<ConnectionHandle> {
|
||||||
let index = protocol_to_index(descriptor.protocol_type());
|
let mut inner = self.inner.lock();
|
||||||
let out = self.conn_by_descriptor[index].get(&descriptor);
|
let protocol_index = *inner.protocol_index_by_id.get(&id)?;
|
||||||
out.map(|c| c.get_handle())
|
let out = inner.conn_by_id[protocol_index].get(&id).unwrap();
|
||||||
|
Some(out.get_handle())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_last_connection_by_remote(
|
pub fn get_connection_by_descriptor(
|
||||||
&mut self,
|
&self,
|
||||||
remote: PeerAddress,
|
descriptor: ConnectionDescriptor,
|
||||||
) -> Option<ConnectionHandle> {
|
) -> Option<ConnectionHandle> {
|
||||||
let descriptor = self
|
let mut inner = self.inner.lock();
|
||||||
.descriptors_by_remote
|
|
||||||
.get(&remote)
|
let id = *inner.id_by_descriptor.get(&descriptor)?;
|
||||||
.map(|v| v[(v.len() - 1)].clone());
|
let protocol_index = Self::protocol_to_index(descriptor.protocol_type());
|
||||||
if let Some(descriptor) = descriptor {
|
let out = inner.conn_by_id[protocol_index].get(&id).unwrap();
|
||||||
// lru bump
|
Some(out.get_handle())
|
||||||
let index = protocol_to_index(descriptor.protocol_type());
|
|
||||||
let handle = self.conn_by_descriptor[index]
|
|
||||||
.get(&descriptor)
|
|
||||||
.map(|c| c.get_handle());
|
|
||||||
handle
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_connection_descriptors_by_remote(
|
pub fn get_last_connection_by_remote(&self, remote: PeerAddress) -> Option<ConnectionHandle> {
|
||||||
&mut self,
|
let mut inner = self.inner.lock();
|
||||||
remote: PeerAddress,
|
|
||||||
) -> Vec<ConnectionDescriptor> {
|
let id = inner.ids_by_remote.get(&remote).map(|v| v[(v.len() - 1)])?;
|
||||||
self.descriptors_by_remote
|
let protocol_index = Self::protocol_to_index(remote.protocol_type());
|
||||||
|
let out = inner.conn_by_id[protocol_index].get(&id).unwrap();
|
||||||
|
Some(out.get_handle())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn _get_connection_ids_by_remote(&self, remote: PeerAddress) -> Vec<NetworkConnectionId> {
|
||||||
|
let inner = self.inner.lock();
|
||||||
|
inner
|
||||||
|
.ids_by_remote
|
||||||
.get(&remote)
|
.get(&remote)
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn drain_filter<F>(&self, mut filter: F) -> Vec<NetworkConnection>
|
||||||
|
where
|
||||||
|
F: FnMut(ConnectionDescriptor) -> bool,
|
||||||
|
{
|
||||||
|
let mut inner = self.inner.lock();
|
||||||
|
let mut filtered_ids = Vec::new();
|
||||||
|
for cbi in &mut inner.conn_by_id {
|
||||||
|
for (id, conn) in cbi {
|
||||||
|
if filter(conn.connection_descriptor()) {
|
||||||
|
filtered_ids.push(*id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut filtered_connections = Vec::new();
|
||||||
|
for id in filtered_ids {
|
||||||
|
let conn = Self::remove_connection_records(&mut *inner, id);
|
||||||
|
filtered_connections.push(conn)
|
||||||
|
}
|
||||||
|
filtered_connections
|
||||||
|
}
|
||||||
|
|
||||||
pub fn connection_count(&self) -> usize {
|
pub fn connection_count(&self) -> usize {
|
||||||
self.conn_by_descriptor.iter().fold(0, |b, c| b + c.len())
|
let inner = self.inner.lock();
|
||||||
|
inner.conn_by_id.iter().fold(0, |acc, c| acc + c.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_connection_records(&mut self, descriptor: ConnectionDescriptor) {
|
fn remove_connection_records(
|
||||||
let ip_addr = descriptor.remote_address().to_ip_addr();
|
inner: &mut ConnectionTableInner,
|
||||||
|
id: NetworkConnectionId,
|
||||||
// conns_by_remote
|
) -> NetworkConnection {
|
||||||
match self.descriptors_by_remote.entry(descriptor.remote()) {
|
// protocol_index_by_id
|
||||||
Entry::Vacant(_) => {
|
let protocol_index = inner.protocol_index_by_id.remove(&id).unwrap();
|
||||||
panic!("inconsistency in connection table")
|
// conn_by_id
|
||||||
|
let conn = inner.conn_by_id[protocol_index].remove(&id).unwrap();
|
||||||
|
// id_by_descriptor
|
||||||
|
let descriptor = conn.connection_descriptor();
|
||||||
|
inner.id_by_descriptor.remove(&descriptor).unwrap();
|
||||||
|
// ids_by_remote
|
||||||
|
let remote = descriptor.remote();
|
||||||
|
let ids = inner.ids_by_remote.get_mut(&remote).unwrap();
|
||||||
|
for (n, elem) in ids.iter().enumerate() {
|
||||||
|
if *elem == id {
|
||||||
|
ids.remove(n);
|
||||||
|
if ids.is_empty() {
|
||||||
|
inner.ids_by_remote.remove(&remote).unwrap();
|
||||||
}
|
}
|
||||||
Entry::Occupied(mut o) => {
|
|
||||||
let v = o.get_mut();
|
|
||||||
|
|
||||||
// Remove one matching connection from the list
|
|
||||||
for (n, elem) in v.iter().enumerate() {
|
|
||||||
if *elem == descriptor {
|
|
||||||
v.remove(n);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// No connections left for this remote, remove the entry from conns_by_remote
|
// address_filter
|
||||||
if v.is_empty() {
|
let ip_addr = remote.to_socket_addr().ip();
|
||||||
o.remove_entry();
|
inner
|
||||||
}
|
.address_filter
|
||||||
}
|
|
||||||
}
|
|
||||||
self.address_filter
|
|
||||||
.remove(ip_addr)
|
.remove(ip_addr)
|
||||||
.expect("Inconsistency in connection table");
|
.expect("Inconsistency in connection table");
|
||||||
|
conn
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_connection(
|
pub fn remove_connection_by_id(&self, id: NetworkConnectionId) -> Option<NetworkConnection> {
|
||||||
&mut self,
|
let mut inner = self.inner.lock();
|
||||||
descriptor: ConnectionDescriptor,
|
|
||||||
) -> Result<NetworkConnection, ConnectionTableRemoveError> {
|
|
||||||
let index = protocol_to_index(descriptor.protocol_type());
|
|
||||||
let conn = self.conn_by_descriptor[index]
|
|
||||||
.remove(&descriptor)
|
|
||||||
.ok_or_else(|| ConnectionTableRemoveError::not_in_table())?;
|
|
||||||
|
|
||||||
self.remove_connection_records(descriptor);
|
let protocol_index = *inner.protocol_index_by_id.get(&id)?;
|
||||||
Ok(conn)
|
if !inner.conn_by_id[protocol_index].contains_key(&id) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let conn = Self::remove_connection_records(&mut *inner, id);
|
||||||
|
Some(conn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -34,32 +34,56 @@ pub const PEEK_DETECT_LEN: usize = 64;
|
|||||||
/////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
struct NetworkInner {
|
struct NetworkInner {
|
||||||
|
/// true if the low-level network is running
|
||||||
network_started: bool,
|
network_started: bool,
|
||||||
|
/// set if the network needs to be restarted due to a low level configuration change
|
||||||
|
/// such as dhcp release or change of address or interfaces being added or removed
|
||||||
network_needs_restart: bool,
|
network_needs_restart: bool,
|
||||||
|
/// the calculated protocol configuration for inbound/outbound protocols
|
||||||
protocol_config: Option<ProtocolConfig>,
|
protocol_config: Option<ProtocolConfig>,
|
||||||
|
/// set of statically configured protocols with public dialinfo
|
||||||
static_public_dialinfo: ProtocolTypeSet,
|
static_public_dialinfo: ProtocolTypeSet,
|
||||||
network_class: Option<NetworkClass>,
|
/// network class per routing domain
|
||||||
|
network_class: [Option<NetworkClass>; RoutingDomain::count()],
|
||||||
|
/// join handles for all the low level network background tasks
|
||||||
join_handles: Vec<MustJoinHandle<()>>,
|
join_handles: Vec<MustJoinHandle<()>>,
|
||||||
|
/// stop source for shutting down the low level network background tasks
|
||||||
stop_source: Option<StopSource>,
|
stop_source: Option<StopSource>,
|
||||||
|
/// port we are binding raw udp listen to
|
||||||
udp_port: u16,
|
udp_port: u16,
|
||||||
|
/// port we are binding raw tcp listen to
|
||||||
tcp_port: u16,
|
tcp_port: u16,
|
||||||
|
/// port we are binding websocket listen to
|
||||||
ws_port: u16,
|
ws_port: u16,
|
||||||
|
/// port we are binding secure websocket listen to
|
||||||
wss_port: u16,
|
wss_port: u16,
|
||||||
|
/// does our network have ipv4 on any network?
|
||||||
enable_ipv4: bool,
|
enable_ipv4: bool,
|
||||||
|
/// does our network have ipv6 on the global internet?
|
||||||
enable_ipv6_global: bool,
|
enable_ipv6_global: bool,
|
||||||
|
/// does our network have ipv6 on the local network?
|
||||||
enable_ipv6_local: bool,
|
enable_ipv6_local: bool,
|
||||||
// public dial info check
|
/// set if we need to calculate our public dial info again
|
||||||
needs_public_dial_info_check: bool,
|
needs_public_dial_info_check: bool,
|
||||||
|
/// set during the actual execution of the public dial info check to ensure we don't do it more than once
|
||||||
doing_public_dial_info_check: bool,
|
doing_public_dial_info_check: bool,
|
||||||
|
/// the punishment closure to enax
|
||||||
public_dial_info_check_punishment: Option<Box<dyn FnOnce() + Send + 'static>>,
|
public_dial_info_check_punishment: Option<Box<dyn FnOnce() + Send + 'static>>,
|
||||||
// udp
|
/// udp socket record for bound-first sockets, which are used to guarantee a port is available before
|
||||||
|
/// creating a 'reuseport' socket there. we don't want to pick ports that other programs are using
|
||||||
bound_first_udp: BTreeMap<u16, Option<(socket2::Socket, socket2::Socket)>>,
|
bound_first_udp: BTreeMap<u16, Option<(socket2::Socket, socket2::Socket)>>,
|
||||||
|
/// mapping of protocol handlers to accept messages from a set of bound socket addresses
|
||||||
inbound_udp_protocol_handlers: BTreeMap<SocketAddr, RawUdpProtocolHandler>,
|
inbound_udp_protocol_handlers: BTreeMap<SocketAddr, RawUdpProtocolHandler>,
|
||||||
|
/// outbound udp protocol handler for udpv4
|
||||||
outbound_udpv4_protocol_handler: Option<RawUdpProtocolHandler>,
|
outbound_udpv4_protocol_handler: Option<RawUdpProtocolHandler>,
|
||||||
|
/// outbound udp protocol handler for udpv6
|
||||||
outbound_udpv6_protocol_handler: Option<RawUdpProtocolHandler>,
|
outbound_udpv6_protocol_handler: Option<RawUdpProtocolHandler>,
|
||||||
//tcp
|
/// tcp socket record for bound-first sockets, which are used to guarantee a port is available before
|
||||||
|
/// creating a 'reuseport' socket there. we don't want to pick ports that other programs are using
|
||||||
bound_first_tcp: BTreeMap<u16, Option<(socket2::Socket, socket2::Socket)>>,
|
bound_first_tcp: BTreeMap<u16, Option<(socket2::Socket, socket2::Socket)>>,
|
||||||
|
/// TLS handling socket controller
|
||||||
tls_acceptor: Option<TlsAcceptor>,
|
tls_acceptor: Option<TlsAcceptor>,
|
||||||
|
/// Multiplexer record for protocols on low level TCP sockets
|
||||||
listener_states: BTreeMap<SocketAddr, Arc<RwLock<ListenerState>>>,
|
listener_states: BTreeMap<SocketAddr, Arc<RwLock<ListenerState>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +122,7 @@ impl Network {
|
|||||||
public_dial_info_check_punishment: None,
|
public_dial_info_check_punishment: None,
|
||||||
protocol_config: None,
|
protocol_config: None,
|
||||||
static_public_dialinfo: ProtocolTypeSet::empty(),
|
static_public_dialinfo: ProtocolTypeSet::empty(),
|
||||||
network_class: None,
|
network_class: [None, None],
|
||||||
join_handles: Vec::new(),
|
join_handles: Vec::new(),
|
||||||
stop_source: None,
|
stop_source: None,
|
||||||
udp_port: 0u16,
|
udp_port: 0u16,
|
||||||
@ -521,7 +545,7 @@ impl Network {
|
|||||||
// Handle connection-oriented protocols
|
// Handle connection-oriented protocols
|
||||||
|
|
||||||
// Try to send to the exact existing connection if one exists
|
// Try to send to the exact existing connection if one exists
|
||||||
if let Some(conn) = self.connection_manager().get_connection(descriptor).await {
|
if let Some(conn) = self.connection_manager().get_connection(descriptor) {
|
||||||
// connection exists, send over it
|
// connection exists, send over it
|
||||||
match conn.send_async(data).await {
|
match conn.send_async(data).await {
|
||||||
ConnectionHandleSendResult::Sent => {
|
ConnectionHandleSendResult::Sent => {
|
||||||
@ -603,6 +627,31 @@ impl Network {
|
|||||||
// initialize interfaces
|
// initialize interfaces
|
||||||
self.unlocked_inner.interfaces.refresh().await?;
|
self.unlocked_inner.interfaces.refresh().await?;
|
||||||
|
|
||||||
|
// build the set of networks we should consider for the 'LocalNetwork' routing domain
|
||||||
|
let mut local_networks: HashSet<(IpAddr, IpAddr)> = HashSet::new();
|
||||||
|
self.unlocked_inner
|
||||||
|
.interfaces
|
||||||
|
.with_interfaces(|interfaces| {
|
||||||
|
trace!("interfaces: {:#?}", interfaces);
|
||||||
|
|
||||||
|
for (_name, intf) in interfaces {
|
||||||
|
// Skip networks that we should never encounter
|
||||||
|
if intf.is_loopback() || !intf.is_running() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Add network to local networks table
|
||||||
|
for addr in &intf.addrs {
|
||||||
|
let netmask = addr.if_addr().netmask();
|
||||||
|
let network_ip = ipaddr_apply_netmask(addr.if_addr().ip(), netmask);
|
||||||
|
local_networks.insert((network_ip, netmask));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let local_networks: Vec<(IpAddr, IpAddr)> = local_networks.into_iter().collect();
|
||||||
|
self.unlocked_inner
|
||||||
|
.routing_table
|
||||||
|
.configure_local_network_routing_domain(local_networks);
|
||||||
|
|
||||||
// determine if we have ipv4/ipv6 addresses
|
// determine if we have ipv4/ipv6 addresses
|
||||||
{
|
{
|
||||||
let mut inner = self.inner.lock();
|
let mut inner = self.inner.lock();
|
||||||
@ -687,18 +736,32 @@ impl Network {
|
|||||||
protocol_config
|
protocol_config
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Start editing routing table
|
||||||
|
let mut editor_public_internet = self
|
||||||
|
.unlocked_inner
|
||||||
|
.routing_table
|
||||||
|
.edit_routing_domain(RoutingDomain::PublicInternet);
|
||||||
|
let mut editor_local_network = self
|
||||||
|
.unlocked_inner
|
||||||
|
.routing_table
|
||||||
|
.edit_routing_domain(RoutingDomain::LocalNetwork);
|
||||||
|
|
||||||
// start listeners
|
// start listeners
|
||||||
if protocol_config.inbound.contains(ProtocolType::UDP) {
|
if protocol_config.inbound.contains(ProtocolType::UDP) {
|
||||||
self.start_udp_listeners().await?;
|
self.start_udp_listeners(&mut editor_public_internet, &mut editor_local_network)
|
||||||
|
.await?;
|
||||||
}
|
}
|
||||||
if protocol_config.inbound.contains(ProtocolType::WS) {
|
if protocol_config.inbound.contains(ProtocolType::WS) {
|
||||||
self.start_ws_listeners().await?;
|
self.start_ws_listeners(&mut editor_public_internet, &mut editor_local_network)
|
||||||
|
.await?;
|
||||||
}
|
}
|
||||||
if protocol_config.inbound.contains(ProtocolType::WSS) {
|
if protocol_config.inbound.contains(ProtocolType::WSS) {
|
||||||
self.start_wss_listeners().await?;
|
self.start_wss_listeners(&mut editor_public_internet, &mut editor_local_network)
|
||||||
|
.await?;
|
||||||
}
|
}
|
||||||
if protocol_config.inbound.contains(ProtocolType::TCP) {
|
if protocol_config.inbound.contains(ProtocolType::TCP) {
|
||||||
self.start_tcp_listeners().await?;
|
self.start_tcp_listeners(&mut editor_public_internet, &mut editor_local_network)
|
||||||
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// release caches of available listener ports
|
// release caches of available listener ports
|
||||||
@ -715,13 +778,18 @@ impl Network {
|
|||||||
if !detect_address_changes {
|
if !detect_address_changes {
|
||||||
let mut inner = self.inner.lock();
|
let mut inner = self.inner.lock();
|
||||||
if !inner.static_public_dialinfo.is_empty() {
|
if !inner.static_public_dialinfo.is_empty() {
|
||||||
inner.network_class = Some(NetworkClass::InboundCapable);
|
inner.network_class[RoutingDomain::PublicInternet as usize] =
|
||||||
|
Some(NetworkClass::InboundCapable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("network started");
|
info!("network started");
|
||||||
self.inner.lock().network_started = true;
|
self.inner.lock().network_started = true;
|
||||||
|
|
||||||
|
// commit routing table edits
|
||||||
|
editor_public_internet.commit().await;
|
||||||
|
editor_local_network.commit().await;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -766,9 +834,16 @@ impl Network {
|
|||||||
while unord.next().await.is_some() {}
|
while unord.next().await.is_some() {}
|
||||||
|
|
||||||
debug!("clearing dial info");
|
debug!("clearing dial info");
|
||||||
// Drop all dial info
|
|
||||||
routing_table.clear_dial_info_details(RoutingDomain::PublicInternet);
|
let mut editor = routing_table.edit_routing_domain(RoutingDomain::PublicInternet);
|
||||||
routing_table.clear_dial_info_details(RoutingDomain::LocalNetwork);
|
editor.disable_node_info_updates();
|
||||||
|
editor.clear_dial_info_details();
|
||||||
|
editor.commit().await;
|
||||||
|
|
||||||
|
let mut editor = routing_table.edit_routing_domain(RoutingDomain::LocalNetwork);
|
||||||
|
editor.disable_node_info_updates();
|
||||||
|
editor.clear_dial_info_details();
|
||||||
|
editor.commit().await;
|
||||||
|
|
||||||
// Reset state including network class
|
// Reset state including network class
|
||||||
*self.inner.lock() = Self::new_inner();
|
*self.inner.lock() = Self::new_inner();
|
||||||
@ -796,9 +871,9 @@ impl Network {
|
|||||||
inner.doing_public_dial_info_check
|
inner.doing_public_dial_info_check
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_network_class(&self) -> Option<NetworkClass> {
|
pub fn get_network_class(&self, routing_domain: RoutingDomain) -> Option<NetworkClass> {
|
||||||
let inner = self.inner.lock();
|
let inner = self.inner.lock();
|
||||||
inner.network_class
|
inner.network_class[routing_domain as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////
|
//////////////////////////////////////////
|
||||||
@ -861,9 +936,13 @@ impl Network {
|
|||||||
|
|
||||||
// If we need to figure out our network class, tick the task for it
|
// If we need to figure out our network class, tick the task for it
|
||||||
if detect_address_changes {
|
if detect_address_changes {
|
||||||
let network_class = self.get_network_class().unwrap_or(NetworkClass::Invalid);
|
let public_internet_network_class = self
|
||||||
|
.get_network_class(RoutingDomain::PublicInternet)
|
||||||
|
.unwrap_or(NetworkClass::Invalid);
|
||||||
let needs_public_dial_info_check = self.needs_public_dial_info_check();
|
let needs_public_dial_info_check = self.needs_public_dial_info_check();
|
||||||
if network_class == NetworkClass::Invalid || needs_public_dial_info_check {
|
if public_internet_network_class == NetworkClass::Invalid
|
||||||
|
|| needs_public_dial_info_check
|
||||||
|
{
|
||||||
let routing_table = self.routing_table();
|
let routing_table = self.routing_table();
|
||||||
let rth = routing_table.get_routing_table_health();
|
let rth = routing_table.get_routing_table_health();
|
||||||
|
|
||||||
|
@ -118,20 +118,24 @@ impl DiscoveryContext {
|
|||||||
|
|
||||||
// Build an filter that matches our protocol and address type
|
// Build an filter that matches our protocol and address type
|
||||||
// and excludes relays so we can get an accurate external address
|
// and excludes relays so we can get an accurate external address
|
||||||
let dial_info_filter = DialInfoFilter::global()
|
let dial_info_filter = DialInfoFilter::all()
|
||||||
.with_protocol_type(protocol_type)
|
.with_protocol_type(protocol_type)
|
||||||
.with_address_type(address_type);
|
.with_address_type(address_type);
|
||||||
let inbound_dial_info_entry_filter =
|
let inbound_dial_info_entry_filter = RoutingTable::make_inbound_dial_info_entry_filter(
|
||||||
RoutingTable::make_inbound_dial_info_entry_filter(dial_info_filter.clone());
|
RoutingDomain::PublicInternet,
|
||||||
|
dial_info_filter.clone(),
|
||||||
|
);
|
||||||
let disallow_relays_filter = move |e: &BucketEntryInner| {
|
let disallow_relays_filter = move |e: &BucketEntryInner| {
|
||||||
if let Some(n) = e.node_info() {
|
if let Some(n) = e.node_info(RoutingDomain::PublicInternet) {
|
||||||
n.relay_peer_info.is_none()
|
n.relay_peer_info.is_none()
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let filter =
|
let filter = RoutingTable::combine_entry_filters(
|
||||||
RoutingTable::combine_filters(inbound_dial_info_entry_filter, disallow_relays_filter);
|
inbound_dial_info_entry_filter,
|
||||||
|
disallow_relays_filter,
|
||||||
|
);
|
||||||
|
|
||||||
// Find public nodes matching this filter
|
// Find public nodes matching this filter
|
||||||
let peers = self
|
let peers = self
|
||||||
@ -153,7 +157,11 @@ impl DiscoveryContext {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
peer.set_filter(Some(dial_info_filter.clone()));
|
peer.set_filter(Some(
|
||||||
|
NodeRefFilter::new()
|
||||||
|
.with_routing_domain(RoutingDomain::PublicInternet)
|
||||||
|
.with_dial_info_filter(dial_info_filter.clone()),
|
||||||
|
));
|
||||||
if let Some(sa) = self.request_public_address(peer.clone()).await {
|
if let Some(sa) = self.request_public_address(peer.clone()).await {
|
||||||
return Some((sa, peer));
|
return Some((sa, peer));
|
||||||
}
|
}
|
||||||
@ -169,7 +177,7 @@ impl DiscoveryContext {
|
|||||||
protocol_type: ProtocolType,
|
protocol_type: ProtocolType,
|
||||||
address_type: AddressType,
|
address_type: AddressType,
|
||||||
) -> Vec<SocketAddress> {
|
) -> Vec<SocketAddress> {
|
||||||
let filter = DialInfoFilter::local()
|
let filter = DialInfoFilter::all()
|
||||||
.with_protocol_type(protocol_type)
|
.with_protocol_type(protocol_type)
|
||||||
.with_address_type(address_type);
|
.with_address_type(address_type);
|
||||||
self.routing_table
|
self.routing_table
|
||||||
@ -340,20 +348,22 @@ impl DiscoveryContext {
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Attempt a port mapping via all available and enabled mechanisms
|
||||||
|
// Try this before the direct mapping in the event that we are restarting
|
||||||
|
// and may not have recorded a mapping created the last time
|
||||||
|
if let Some(external_mapped_dial_info) = self.try_port_mapping().await {
|
||||||
|
// Got a port mapping, let's use it
|
||||||
|
self.set_detected_public_dial_info(external_mapped_dial_info, DialInfoClass::Mapped);
|
||||||
|
self.set_detected_network_class(NetworkClass::InboundCapable);
|
||||||
|
}
|
||||||
// Do a validate_dial_info on the external address from a redirected node
|
// Do a validate_dial_info on the external address from a redirected node
|
||||||
if self
|
else if self
|
||||||
.validate_dial_info(node_1.clone(), external_1_dial_info.clone(), true)
|
.validate_dial_info(node_1.clone(), external_1_dial_info.clone(), true)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
// Add public dial info with Direct dialinfo class
|
// Add public dial info with Direct dialinfo class
|
||||||
self.set_detected_public_dial_info(external_1_dial_info, DialInfoClass::Direct);
|
self.set_detected_public_dial_info(external_1_dial_info, DialInfoClass::Direct);
|
||||||
self.set_detected_network_class(NetworkClass::InboundCapable);
|
self.set_detected_network_class(NetworkClass::InboundCapable);
|
||||||
}
|
|
||||||
// Attempt a port mapping via all available and enabled mechanisms
|
|
||||||
else if let Some(external_mapped_dial_info) = self.try_port_mapping().await {
|
|
||||||
// Got a port mapping, let's use it
|
|
||||||
self.set_detected_public_dial_info(external_mapped_dial_info, DialInfoClass::Mapped);
|
|
||||||
self.set_detected_network_class(NetworkClass::InboundCapable);
|
|
||||||
} else {
|
} else {
|
||||||
// Add public dial info with Blocked dialinfo class
|
// Add public dial info with Blocked dialinfo class
|
||||||
self.set_detected_public_dial_info(external_1_dial_info, DialInfoClass::Blocked);
|
self.set_detected_public_dial_info(external_1_dial_info, DialInfoClass::Blocked);
|
||||||
@ -376,8 +386,19 @@ impl DiscoveryContext {
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Attempt a port mapping via all available and enabled mechanisms
|
||||||
|
// Try this before the direct mapping in the event that we are restarting
|
||||||
|
// and may not have recorded a mapping created the last time
|
||||||
|
if let Some(external_mapped_dial_info) = self.try_port_mapping().await {
|
||||||
|
// Got a port mapping, let's use it
|
||||||
|
self.set_detected_public_dial_info(external_mapped_dial_info, DialInfoClass::Mapped);
|
||||||
|
self.set_detected_network_class(NetworkClass::InboundCapable);
|
||||||
|
|
||||||
|
// No more retries
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
// Do a validate_dial_info on the external address from a redirected node
|
// Do a validate_dial_info on the external address from a redirected node
|
||||||
if self
|
else if self
|
||||||
.validate_dial_info(node_1.clone(), external_1_dial_info.clone(), true)
|
.validate_dial_info(node_1.clone(), external_1_dial_info.clone(), true)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
@ -386,17 +407,9 @@ impl DiscoveryContext {
|
|||||||
self.set_detected_network_class(NetworkClass::InboundCapable);
|
self.set_detected_network_class(NetworkClass::InboundCapable);
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
// Attempt a port mapping via all available and enabled mechanisms
|
|
||||||
else if let Some(external_mapped_dial_info) = self.try_port_mapping().await {
|
|
||||||
// Got a port mapping, let's use it
|
|
||||||
self.set_detected_public_dial_info(external_mapped_dial_info, DialInfoClass::Mapped);
|
|
||||||
self.set_detected_network_class(NetworkClass::InboundCapable);
|
|
||||||
|
|
||||||
// No more retries
|
// Port mapping was not possible, and things aren't accessible directly.
|
||||||
return Ok(true);
|
// Let's see what kind of NAT we have
|
||||||
}
|
|
||||||
|
|
||||||
// Port mapping was not possible, let's see what kind of NAT we have
|
|
||||||
|
|
||||||
// Does a redirected dial info validation from a different address and a random port find us?
|
// Does a redirected dial info validation from a different address and a random port find us?
|
||||||
if self
|
if self
|
||||||
@ -583,7 +596,8 @@ impl Network {
|
|||||||
let (protocol_config, existing_network_class, tcp_same_port) = {
|
let (protocol_config, existing_network_class, tcp_same_port) = {
|
||||||
let inner = self.inner.lock();
|
let inner = self.inner.lock();
|
||||||
let protocol_config = inner.protocol_config.unwrap_or_default();
|
let protocol_config = inner.protocol_config.unwrap_or_default();
|
||||||
let existing_network_class = inner.network_class;
|
let existing_network_class =
|
||||||
|
inner.network_class[RoutingDomain::PublicInternet as usize];
|
||||||
let tcp_same_port = if protocol_config.inbound.contains(ProtocolType::TCP)
|
let tcp_same_port = if protocol_config.inbound.contains(ProtocolType::TCP)
|
||||||
&& protocol_config.inbound.contains(ProtocolType::WS)
|
&& protocol_config.inbound.contains(ProtocolType::WS)
|
||||||
{
|
{
|
||||||
@ -594,7 +608,6 @@ impl Network {
|
|||||||
(protocol_config, existing_network_class, tcp_same_port)
|
(protocol_config, existing_network_class, tcp_same_port)
|
||||||
};
|
};
|
||||||
let routing_table = self.routing_table();
|
let routing_table = self.routing_table();
|
||||||
let network_manager = self.network_manager();
|
|
||||||
|
|
||||||
// Process all protocol and address combinations
|
// Process all protocol and address combinations
|
||||||
let mut futures = FuturesUnordered::new();
|
let mut futures = FuturesUnordered::new();
|
||||||
@ -757,11 +770,12 @@ impl Network {
|
|||||||
// If a network class could be determined
|
// If a network class could be determined
|
||||||
// see about updating our public dial info
|
// see about updating our public dial info
|
||||||
let mut changed = false;
|
let mut changed = false;
|
||||||
|
let mut editor = routing_table.edit_routing_domain(RoutingDomain::PublicInternet);
|
||||||
if new_network_class.is_some() {
|
if new_network_class.is_some() {
|
||||||
// Get existing public dial info
|
// Get existing public dial info
|
||||||
let existing_public_dial_info: HashSet<DialInfoDetail> = routing_table
|
let existing_public_dial_info: HashSet<DialInfoDetail> = routing_table
|
||||||
.all_filtered_dial_info_details(
|
.all_filtered_dial_info_details(
|
||||||
Some(RoutingDomain::PublicInternet),
|
RoutingDomain::PublicInternet.into(),
|
||||||
&DialInfoFilter::all(),
|
&DialInfoFilter::all(),
|
||||||
)
|
)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -800,13 +814,9 @@ impl Network {
|
|||||||
// Is the public dial info different?
|
// Is the public dial info different?
|
||||||
if existing_public_dial_info != new_public_dial_info {
|
if existing_public_dial_info != new_public_dial_info {
|
||||||
// If so, clear existing public dial info and re-register the new public dial info
|
// If so, clear existing public dial info and re-register the new public dial info
|
||||||
routing_table.clear_dial_info_details(RoutingDomain::PublicInternet);
|
editor.clear_dial_info_details();
|
||||||
for did in new_public_dial_info {
|
for did in new_public_dial_info {
|
||||||
if let Err(e) = routing_table.register_dial_info(
|
if let Err(e) = editor.register_dial_info(did.dial_info, did.class) {
|
||||||
RoutingDomain::PublicInternet,
|
|
||||||
did.dial_info,
|
|
||||||
did.class,
|
|
||||||
) {
|
|
||||||
log_net!(error "Failed to register detected public dial info: {}", e);
|
log_net!(error "Failed to register detected public dial info: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -815,14 +825,15 @@ impl Network {
|
|||||||
|
|
||||||
// Is the network class different?
|
// Is the network class different?
|
||||||
if existing_network_class != new_network_class {
|
if existing_network_class != new_network_class {
|
||||||
self.inner.lock().network_class = new_network_class;
|
self.inner.lock().network_class[RoutingDomain::PublicInternet as usize] =
|
||||||
|
new_network_class;
|
||||||
changed = true;
|
changed = true;
|
||||||
log_net!(debug "network class changed to {:?}", new_network_class);
|
log_net!(debug "PublicInternet network class changed to {:?}", new_network_class);
|
||||||
}
|
}
|
||||||
} else if existing_network_class.is_some() {
|
} else if existing_network_class.is_some() {
|
||||||
// Network class could not be determined
|
// Network class could not be determined
|
||||||
routing_table.clear_dial_info_details(RoutingDomain::PublicInternet);
|
editor.clear_dial_info_details();
|
||||||
self.inner.lock().network_class = None;
|
self.inner.lock().network_class[RoutingDomain::PublicInternet as usize] = None;
|
||||||
changed = true;
|
changed = true;
|
||||||
log_net!(debug "network class cleared");
|
log_net!(debug "network class cleared");
|
||||||
}
|
}
|
||||||
@ -834,7 +845,7 @@ impl Network {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Send updates to everyone
|
// Send updates to everyone
|
||||||
network_manager.send_node_info_updates(true).await;
|
editor.commit().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -149,8 +149,7 @@ impl RawTcpProtocolHandler {
|
|||||||
);
|
);
|
||||||
let local_address = self.inner.lock().local_address;
|
let local_address = self.inner.lock().local_address;
|
||||||
let conn = ProtocolNetworkConnection::RawTcp(RawTcpNetworkConnection::new(
|
let conn = ProtocolNetworkConnection::RawTcp(RawTcpNetworkConnection::new(
|
||||||
ConnectionDescriptor::new(peer_addr, SocketAddress::from_socket_addr(local_address))
|
ConnectionDescriptor::new(peer_addr, SocketAddress::from_socket_addr(local_address)),
|
||||||
.map_err(|e| io::Error::new(io::ErrorKind::AddrNotAvailable, e))?,
|
|
||||||
ps,
|
ps,
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -190,8 +189,7 @@ impl RawTcpProtocolHandler {
|
|||||||
ProtocolType::TCP,
|
ProtocolType::TCP,
|
||||||
),
|
),
|
||||||
SocketAddress::from_socket_addr(actual_local_address),
|
SocketAddress::from_socket_addr(actual_local_address),
|
||||||
)
|
),
|
||||||
.map_err(|e| io::Error::new(io::ErrorKind::AddrNotAvailable, e))?,
|
|
||||||
ps,
|
ps,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -25,16 +25,10 @@ impl RawUdpProtocolHandler {
|
|||||||
ProtocolType::UDP,
|
ProtocolType::UDP,
|
||||||
);
|
);
|
||||||
let local_socket_addr = self.socket.local_addr()?;
|
let local_socket_addr = self.socket.local_addr()?;
|
||||||
let descriptor = match ConnectionDescriptor::new(
|
let descriptor = ConnectionDescriptor::new(
|
||||||
peer_addr,
|
peer_addr,
|
||||||
SocketAddress::from_socket_addr(local_socket_addr),
|
SocketAddress::from_socket_addr(local_socket_addr),
|
||||||
) {
|
);
|
||||||
Ok(d) => d,
|
|
||||||
Err(_) => {
|
|
||||||
log_net!(debug "{}({}) at {}@{}:{}: {:?}", "Invalid peer scope".green(), "received message from invalid peer scope", file!(), line!(), column!(), peer_addr);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
break (size, descriptor);
|
break (size, descriptor);
|
||||||
};
|
};
|
||||||
@ -62,8 +56,7 @@ impl RawUdpProtocolHandler {
|
|||||||
let descriptor = ConnectionDescriptor::new(
|
let descriptor = ConnectionDescriptor::new(
|
||||||
peer_addr,
|
peer_addr,
|
||||||
SocketAddress::from_socket_addr(local_socket_addr),
|
SocketAddress::from_socket_addr(local_socket_addr),
|
||||||
)
|
);
|
||||||
.map_err(|e| io::Error::new(io::ErrorKind::AddrNotAvailable, e))?;
|
|
||||||
|
|
||||||
let len = network_result_try!(self
|
let len = network_result_try!(self
|
||||||
.socket
|
.socket
|
||||||
|
@ -212,8 +212,7 @@ impl WebsocketProtocolHandler {
|
|||||||
ConnectionDescriptor::new(
|
ConnectionDescriptor::new(
|
||||||
peer_addr,
|
peer_addr,
|
||||||
SocketAddress::from_socket_addr(self.arc.local_address),
|
SocketAddress::from_socket_addr(self.arc.local_address),
|
||||||
)
|
),
|
||||||
.map_err(|e| io::Error::new(io::ErrorKind::AddrNotAvailable, e))?,
|
|
||||||
ws_stream,
|
ws_stream,
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -268,8 +267,7 @@ impl WebsocketProtocolHandler {
|
|||||||
let descriptor = ConnectionDescriptor::new(
|
let descriptor = ConnectionDescriptor::new(
|
||||||
dial_info.to_peer_address(),
|
dial_info.to_peer_address(),
|
||||||
SocketAddress::from_socket_addr(actual_local_addr),
|
SocketAddress::from_socket_addr(actual_local_addr),
|
||||||
)
|
);
|
||||||
.map_err(|e| io::Error::new(io::ErrorKind::AddrNotAvailable, e))?;
|
|
||||||
|
|
||||||
// Negotiate TLS if this is WSS
|
// Negotiate TLS if this is WSS
|
||||||
if tls {
|
if tls {
|
||||||
|
@ -250,15 +250,18 @@ impl Network {
|
|||||||
|
|
||||||
/////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
pub(super) async fn start_udp_listeners(&self) -> EyreResult<()> {
|
pub(super) async fn start_udp_listeners(
|
||||||
|
&self,
|
||||||
|
editor_public_internet: &mut RoutingDomainEditor,
|
||||||
|
editor_local_network: &mut RoutingDomainEditor,
|
||||||
|
) -> EyreResult<()> {
|
||||||
trace!("starting udp listeners");
|
trace!("starting udp listeners");
|
||||||
let routing_table = self.routing_table();
|
let routing_table = self.routing_table();
|
||||||
let (listen_address, public_address, enable_local_peer_scope, detect_address_changes) = {
|
let (listen_address, public_address, detect_address_changes) = {
|
||||||
let c = self.config.get();
|
let c = self.config.get();
|
||||||
(
|
(
|
||||||
c.network.protocol.udp.listen_address.clone(),
|
c.network.protocol.udp.listen_address.clone(),
|
||||||
c.network.protocol.udp.public_address.clone(),
|
c.network.protocol.udp.public_address.clone(),
|
||||||
c.network.enable_local_peer_scope,
|
|
||||||
c.network.detect_address_changes,
|
c.network.detect_address_changes,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
@ -288,26 +291,18 @@ impl Network {
|
|||||||
|
|
||||||
// Register local dial info
|
// Register local dial info
|
||||||
for di in &local_dial_info_list {
|
for di in &local_dial_info_list {
|
||||||
// If the local interface address is global, or we are enabling local peer scope
|
// If the local interface address is global, then register global dial info
|
||||||
// register global dial info if no public address is specified
|
// if no other public address is specified
|
||||||
if !detect_address_changes
|
if !detect_address_changes
|
||||||
&& public_address.is_none()
|
&& public_address.is_none()
|
||||||
&& (di.is_global() || enable_local_peer_scope)
|
&& routing_table.ensure_dial_info_is_valid(RoutingDomain::PublicInternet, &di)
|
||||||
{
|
{
|
||||||
routing_table.register_dial_info(
|
editor_public_internet.register_dial_info(di.clone(), DialInfoClass::Direct)?;
|
||||||
RoutingDomain::PublicInternet,
|
|
||||||
di.clone(),
|
|
||||||
DialInfoClass::Direct,
|
|
||||||
)?;
|
|
||||||
static_public = true;
|
static_public = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register interface dial info as well since the address is on the local interface
|
// Register interface dial info as well since the address is on the local interface
|
||||||
routing_table.register_dial_info(
|
editor_local_network.register_dial_info(di.clone(), DialInfoClass::Direct)?;
|
||||||
RoutingDomain::LocalNetwork,
|
|
||||||
di.clone(),
|
|
||||||
DialInfoClass::Direct,
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add static public dialinfo if it's configured
|
// Add static public dialinfo if it's configured
|
||||||
@ -323,11 +318,8 @@ impl Network {
|
|||||||
|
|
||||||
// Register the public address
|
// Register the public address
|
||||||
if !detect_address_changes {
|
if !detect_address_changes {
|
||||||
routing_table.register_dial_info(
|
editor_public_internet
|
||||||
RoutingDomain::PublicInternet,
|
.register_dial_info(pdi.clone(), DialInfoClass::Direct)?;
|
||||||
pdi.clone(),
|
|
||||||
DialInfoClass::Direct,
|
|
||||||
)?;
|
|
||||||
static_public = true;
|
static_public = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,8 +334,7 @@ impl Network {
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
if !local_dial_info_list.contains(&pdi) && is_interface_address {
|
if !local_dial_info_list.contains(&pdi) && is_interface_address {
|
||||||
routing_table.register_dial_info(
|
editor_local_network.register_dial_info(
|
||||||
RoutingDomain::LocalNetwork,
|
|
||||||
DialInfo::udp_from_socketaddr(pdi_addr),
|
DialInfo::udp_from_socketaddr(pdi_addr),
|
||||||
DialInfoClass::Direct,
|
DialInfoClass::Direct,
|
||||||
)?;
|
)?;
|
||||||
@ -362,16 +353,19 @@ impl Network {
|
|||||||
self.create_udp_listener_tasks().await
|
self.create_udp_listener_tasks().await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn start_ws_listeners(&self) -> EyreResult<()> {
|
pub(super) async fn start_ws_listeners(
|
||||||
|
&self,
|
||||||
|
editor_public_internet: &mut RoutingDomainEditor,
|
||||||
|
editor_local_network: &mut RoutingDomainEditor,
|
||||||
|
) -> EyreResult<()> {
|
||||||
trace!("starting ws listeners");
|
trace!("starting ws listeners");
|
||||||
let routing_table = self.routing_table();
|
let routing_table = self.routing_table();
|
||||||
let (listen_address, url, path, enable_local_peer_scope, detect_address_changes) = {
|
let (listen_address, url, path, detect_address_changes) = {
|
||||||
let c = self.config.get();
|
let c = self.config.get();
|
||||||
(
|
(
|
||||||
c.network.protocol.ws.listen_address.clone(),
|
c.network.protocol.ws.listen_address.clone(),
|
||||||
c.network.protocol.ws.url.clone(),
|
c.network.protocol.ws.url.clone(),
|
||||||
c.network.protocol.ws.path.clone(),
|
c.network.protocol.ws.path.clone(),
|
||||||
c.network.enable_local_peer_scope,
|
|
||||||
c.network.detect_address_changes,
|
c.network.detect_address_changes,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
@ -420,11 +414,8 @@ impl Network {
|
|||||||
.wrap_err("try_ws failed")?;
|
.wrap_err("try_ws failed")?;
|
||||||
|
|
||||||
if !detect_address_changes {
|
if !detect_address_changes {
|
||||||
routing_table.register_dial_info(
|
editor_public_internet
|
||||||
RoutingDomain::PublicInternet,
|
.register_dial_info(pdi.clone(), DialInfoClass::Direct)?;
|
||||||
pdi.clone(),
|
|
||||||
DialInfoClass::Direct,
|
|
||||||
)?;
|
|
||||||
static_public = true;
|
static_public = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -432,11 +423,7 @@ impl Network {
|
|||||||
if !registered_addresses.contains(&gsa.ip())
|
if !registered_addresses.contains(&gsa.ip())
|
||||||
&& self.is_usable_interface_address(gsa.ip())
|
&& self.is_usable_interface_address(gsa.ip())
|
||||||
{
|
{
|
||||||
routing_table.register_dial_info(
|
editor_local_network.register_dial_info(pdi, DialInfoClass::Direct)?;
|
||||||
RoutingDomain::LocalNetwork,
|
|
||||||
pdi,
|
|
||||||
DialInfoClass::Direct,
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
registered_addresses.insert(gsa.ip());
|
registered_addresses.insert(gsa.ip());
|
||||||
@ -454,23 +441,16 @@ impl Network {
|
|||||||
|
|
||||||
if !detect_address_changes
|
if !detect_address_changes
|
||||||
&& url.is_none()
|
&& url.is_none()
|
||||||
&& (socket_address.address().is_global() || enable_local_peer_scope)
|
&& routing_table.ensure_dial_info_is_valid(RoutingDomain::PublicInternet, &local_di)
|
||||||
{
|
{
|
||||||
// Register public dial info
|
// Register public dial info
|
||||||
routing_table.register_dial_info(
|
editor_public_internet
|
||||||
RoutingDomain::PublicInternet,
|
.register_dial_info(local_di.clone(), DialInfoClass::Direct)?;
|
||||||
local_di.clone(),
|
|
||||||
DialInfoClass::Direct,
|
|
||||||
)?;
|
|
||||||
static_public = true;
|
static_public = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register local dial info
|
// Register local dial info
|
||||||
routing_table.register_dial_info(
|
editor_local_network.register_dial_info(local_di, DialInfoClass::Direct)?;
|
||||||
RoutingDomain::LocalNetwork,
|
|
||||||
local_di,
|
|
||||||
DialInfoClass::Direct,
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if static_public {
|
if static_public {
|
||||||
@ -483,10 +463,13 @@ impl Network {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn start_wss_listeners(&self) -> EyreResult<()> {
|
pub(super) async fn start_wss_listeners(
|
||||||
|
&self,
|
||||||
|
editor_public_internet: &mut RoutingDomainEditor,
|
||||||
|
editor_local_network: &mut RoutingDomainEditor,
|
||||||
|
) -> EyreResult<()> {
|
||||||
trace!("starting wss listeners");
|
trace!("starting wss listeners");
|
||||||
|
|
||||||
let routing_table = self.routing_table();
|
|
||||||
let (listen_address, url, detect_address_changes) = {
|
let (listen_address, url, detect_address_changes) = {
|
||||||
let c = self.config.get();
|
let c = self.config.get();
|
||||||
(
|
(
|
||||||
@ -545,11 +528,8 @@ impl Network {
|
|||||||
.wrap_err("try_wss failed")?;
|
.wrap_err("try_wss failed")?;
|
||||||
|
|
||||||
if !detect_address_changes {
|
if !detect_address_changes {
|
||||||
routing_table.register_dial_info(
|
editor_public_internet
|
||||||
RoutingDomain::PublicInternet,
|
.register_dial_info(pdi.clone(), DialInfoClass::Direct)?;
|
||||||
pdi.clone(),
|
|
||||||
DialInfoClass::Direct,
|
|
||||||
)?;
|
|
||||||
static_public = true;
|
static_public = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -557,11 +537,7 @@ impl Network {
|
|||||||
if !registered_addresses.contains(&gsa.ip())
|
if !registered_addresses.contains(&gsa.ip())
|
||||||
&& self.is_usable_interface_address(gsa.ip())
|
&& self.is_usable_interface_address(gsa.ip())
|
||||||
{
|
{
|
||||||
routing_table.register_dial_info(
|
editor_local_network.register_dial_info(pdi, DialInfoClass::Direct)?;
|
||||||
RoutingDomain::LocalNetwork,
|
|
||||||
pdi,
|
|
||||||
DialInfoClass::Direct,
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
registered_addresses.insert(gsa.ip());
|
registered_addresses.insert(gsa.ip());
|
||||||
@ -580,16 +556,19 @@ impl Network {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn start_tcp_listeners(&self) -> EyreResult<()> {
|
pub(super) async fn start_tcp_listeners(
|
||||||
|
&self,
|
||||||
|
editor_public_internet: &mut RoutingDomainEditor,
|
||||||
|
editor_local_network: &mut RoutingDomainEditor,
|
||||||
|
) -> EyreResult<()> {
|
||||||
trace!("starting tcp listeners");
|
trace!("starting tcp listeners");
|
||||||
|
|
||||||
let routing_table = self.routing_table();
|
let routing_table = self.routing_table();
|
||||||
let (listen_address, public_address, enable_local_peer_scope, detect_address_changes) = {
|
let (listen_address, public_address, detect_address_changes) = {
|
||||||
let c = self.config.get();
|
let c = self.config.get();
|
||||||
(
|
(
|
||||||
c.network.protocol.tcp.listen_address.clone(),
|
c.network.protocol.tcp.listen_address.clone(),
|
||||||
c.network.protocol.tcp.public_address.clone(),
|
c.network.protocol.tcp.public_address.clone(),
|
||||||
c.network.enable_local_peer_scope,
|
|
||||||
c.network.detect_address_changes,
|
c.network.detect_address_changes,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
@ -625,21 +604,13 @@ impl Network {
|
|||||||
// Register global dial info if no public address is specified
|
// Register global dial info if no public address is specified
|
||||||
if !detect_address_changes
|
if !detect_address_changes
|
||||||
&& public_address.is_none()
|
&& public_address.is_none()
|
||||||
&& (di.is_global() || enable_local_peer_scope)
|
&& routing_table.ensure_dial_info_is_valid(RoutingDomain::PublicInternet, &di)
|
||||||
{
|
{
|
||||||
routing_table.register_dial_info(
|
editor_public_internet.register_dial_info(di.clone(), DialInfoClass::Direct)?;
|
||||||
RoutingDomain::PublicInternet,
|
|
||||||
di.clone(),
|
|
||||||
DialInfoClass::Direct,
|
|
||||||
)?;
|
|
||||||
static_public = true;
|
static_public = true;
|
||||||
}
|
}
|
||||||
// Register interface dial info
|
// Register interface dial info
|
||||||
routing_table.register_dial_info(
|
editor_local_network.register_dial_info(di.clone(), DialInfoClass::Direct)?;
|
||||||
RoutingDomain::LocalNetwork,
|
|
||||||
di.clone(),
|
|
||||||
DialInfoClass::Direct,
|
|
||||||
)?;
|
|
||||||
registered_addresses.insert(socket_address.to_ip_addr());
|
registered_addresses.insert(socket_address.to_ip_addr());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -659,21 +630,14 @@ impl Network {
|
|||||||
let pdi = DialInfo::tcp_from_socketaddr(pdi_addr);
|
let pdi = DialInfo::tcp_from_socketaddr(pdi_addr);
|
||||||
|
|
||||||
if !detect_address_changes {
|
if !detect_address_changes {
|
||||||
routing_table.register_dial_info(
|
editor_public_internet
|
||||||
RoutingDomain::PublicInternet,
|
.register_dial_info(pdi.clone(), DialInfoClass::Direct)?;
|
||||||
pdi.clone(),
|
|
||||||
DialInfoClass::Direct,
|
|
||||||
)?;
|
|
||||||
static_public = true;
|
static_public = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// See if this public address is also a local interface address
|
// See if this public address is also a local interface address
|
||||||
if self.is_usable_interface_address(pdi_addr.ip()) {
|
if self.is_usable_interface_address(pdi_addr.ip()) {
|
||||||
routing_table.register_dial_info(
|
editor_local_network.register_dial_info(pdi, DialInfoClass::Direct)?;
|
||||||
RoutingDomain::LocalNetwork,
|
|
||||||
pdi,
|
|
||||||
DialInfoClass::Direct,
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use futures_util::{FutureExt, StreamExt};
|
use futures_util::{FutureExt, StreamExt};
|
||||||
use std::io;
|
use std::{io, sync::Arc};
|
||||||
use stop_token::prelude::*;
|
use stop_token::prelude::*;
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
@ -81,8 +81,12 @@ pub struct NetworkConnectionStats {
|
|||||||
last_message_recv_time: Option<u64>,
|
last_message_recv_time: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub type NetworkConnectionId = u64;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct NetworkConnection {
|
pub struct NetworkConnection {
|
||||||
|
connection_id: NetworkConnectionId,
|
||||||
descriptor: ConnectionDescriptor,
|
descriptor: ConnectionDescriptor,
|
||||||
processor: Option<MustJoinHandle<()>>,
|
processor: Option<MustJoinHandle<()>>,
|
||||||
established_time: u64,
|
established_time: u64,
|
||||||
@ -92,11 +96,12 @@ pub struct NetworkConnection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl NetworkConnection {
|
impl NetworkConnection {
|
||||||
pub(super) fn dummy(descriptor: ConnectionDescriptor) -> Self {
|
pub(super) fn dummy(id: NetworkConnectionId, descriptor: ConnectionDescriptor) -> Self {
|
||||||
// Create handle for sending (dummy is immediately disconnected)
|
// Create handle for sending (dummy is immediately disconnected)
|
||||||
let (sender, _receiver) = flume::bounded(intf::get_concurrency() as usize);
|
let (sender, _receiver) = flume::bounded(intf::get_concurrency() as usize);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
connection_id: id,
|
||||||
descriptor,
|
descriptor,
|
||||||
processor: None,
|
processor: None,
|
||||||
established_time: intf::get_timestamp(),
|
established_time: intf::get_timestamp(),
|
||||||
@ -113,14 +118,10 @@ impl NetworkConnection {
|
|||||||
connection_manager: ConnectionManager,
|
connection_manager: ConnectionManager,
|
||||||
manager_stop_token: StopToken,
|
manager_stop_token: StopToken,
|
||||||
protocol_connection: ProtocolNetworkConnection,
|
protocol_connection: ProtocolNetworkConnection,
|
||||||
|
connection_id: NetworkConnectionId,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
// Get timeout
|
// Get timeout
|
||||||
let network_manager = connection_manager.network_manager();
|
let network_manager = connection_manager.network_manager();
|
||||||
let inactivity_timeout = network_manager
|
|
||||||
.config()
|
|
||||||
.get()
|
|
||||||
.network
|
|
||||||
.connection_inactivity_timeout_ms;
|
|
||||||
|
|
||||||
// Get descriptor
|
// Get descriptor
|
||||||
let descriptor = protocol_connection.descriptor();
|
let descriptor = protocol_connection.descriptor();
|
||||||
@ -142,15 +143,16 @@ impl NetworkConnection {
|
|||||||
connection_manager,
|
connection_manager,
|
||||||
local_stop_token,
|
local_stop_token,
|
||||||
manager_stop_token,
|
manager_stop_token,
|
||||||
|
connection_id,
|
||||||
descriptor.clone(),
|
descriptor.clone(),
|
||||||
receiver,
|
receiver,
|
||||||
protocol_connection,
|
protocol_connection,
|
||||||
inactivity_timeout,
|
|
||||||
stats.clone(),
|
stats.clone(),
|
||||||
));
|
));
|
||||||
|
|
||||||
// Return the connection
|
// Return the connection
|
||||||
Self {
|
Self {
|
||||||
|
connection_id,
|
||||||
descriptor,
|
descriptor,
|
||||||
processor: Some(processor),
|
processor: Some(processor),
|
||||||
established_time: intf::get_timestamp(),
|
established_time: intf::get_timestamp(),
|
||||||
@ -160,12 +162,16 @@ impl NetworkConnection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn connection_id(&self) -> NetworkConnectionId {
|
||||||
|
self.connection_id
|
||||||
|
}
|
||||||
|
|
||||||
pub fn connection_descriptor(&self) -> ConnectionDescriptor {
|
pub fn connection_descriptor(&self) -> ConnectionDescriptor {
|
||||||
self.descriptor.clone()
|
self.descriptor.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_handle(&self) -> ConnectionHandle {
|
pub fn get_handle(&self) -> ConnectionHandle {
|
||||||
ConnectionHandle::new(self.descriptor.clone(), self.sender.clone())
|
ConnectionHandle::new(self.connection_id, self.descriptor.clone(), self.sender.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close(&mut self) {
|
pub fn close(&mut self) {
|
||||||
@ -215,15 +221,15 @@ impl NetworkConnection {
|
|||||||
connection_manager: ConnectionManager,
|
connection_manager: ConnectionManager,
|
||||||
local_stop_token: StopToken,
|
local_stop_token: StopToken,
|
||||||
manager_stop_token: StopToken,
|
manager_stop_token: StopToken,
|
||||||
|
connection_id: NetworkConnectionId,
|
||||||
descriptor: ConnectionDescriptor,
|
descriptor: ConnectionDescriptor,
|
||||||
receiver: flume::Receiver<Vec<u8>>,
|
receiver: flume::Receiver<Vec<u8>>,
|
||||||
protocol_connection: ProtocolNetworkConnection,
|
protocol_connection: ProtocolNetworkConnection,
|
||||||
connection_inactivity_timeout_ms: u32,
|
|
||||||
stats: Arc<Mutex<NetworkConnectionStats>>,
|
stats: Arc<Mutex<NetworkConnectionStats>>,
|
||||||
) -> SendPinBoxFuture<()> {
|
) -> SendPinBoxFuture<()> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
log_net!(
|
log_net!(
|
||||||
"== Starting process_connection loop for {:?}",
|
"== Starting process_connection loop for id={}, {:?}", connection_id,
|
||||||
descriptor.green()
|
descriptor.green()
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -235,7 +241,7 @@ impl NetworkConnection {
|
|||||||
// Push mutable timer so we can reset it
|
// Push mutable timer so we can reset it
|
||||||
// Normally we would use an io::timeout here, but WASM won't support that, so we use a mutable sleep future
|
// Normally we would use an io::timeout here, but WASM won't support that, so we use a mutable sleep future
|
||||||
let new_timer = || {
|
let new_timer = || {
|
||||||
intf::sleep(connection_inactivity_timeout_ms).then(|_| async {
|
intf::sleep(connection_manager.connection_inactivity_timeout_ms()).then(|_| async {
|
||||||
// timeout
|
// timeout
|
||||||
log_net!("== Connection timeout on {:?}", descriptor.green());
|
log_net!("== Connection timeout on {:?}", descriptor.green());
|
||||||
RecvLoopAction::Timeout
|
RecvLoopAction::Timeout
|
||||||
@ -317,7 +323,7 @@ impl NetworkConnection {
|
|||||||
.timeout_at(local_stop_token.clone())
|
.timeout_at(local_stop_token.clone())
|
||||||
.timeout_at(manager_stop_token.clone())
|
.timeout_at(manager_stop_token.clone())
|
||||||
.await
|
.await
|
||||||
.and_then(std::convert::identity) // flatten
|
.and_then(std::convert::identity) // flatten stoptoken timeouts
|
||||||
{
|
{
|
||||||
Ok(Some(RecvLoopAction::Send)) => {
|
Ok(Some(RecvLoopAction::Send)) => {
|
||||||
// Don't reset inactivity timer if we're only sending
|
// Don't reset inactivity timer if we're only sending
|
||||||
@ -350,7 +356,7 @@ impl NetworkConnection {
|
|||||||
|
|
||||||
// Let the connection manager know the receive loop exited
|
// Let the connection manager know the receive loop exited
|
||||||
connection_manager
|
connection_manager
|
||||||
.report_connection_finished(descriptor)
|
.report_connection_finished(connection_id)
|
||||||
.await;
|
.await;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -181,15 +181,22 @@ impl NetworkManager {
|
|||||||
let routing_table = self.routing_table();
|
let routing_table = self.routing_table();
|
||||||
|
|
||||||
for bootstrap_di in bootstrap_dialinfos {
|
for bootstrap_di in bootstrap_dialinfos {
|
||||||
|
log_net!(debug "direct bootstrap with: {}", bootstrap_di);
|
||||||
|
|
||||||
let peer_info = self.boot_request(bootstrap_di).await?;
|
let peer_info = self.boot_request(bootstrap_di).await?;
|
||||||
|
|
||||||
|
log_net!(debug " direct bootstrap peerinfo: {:?}", peer_info);
|
||||||
|
|
||||||
// Got peer info, let's add it to the routing table
|
// Got peer info, let's add it to the routing table
|
||||||
for pi in peer_info {
|
for pi in peer_info {
|
||||||
let k = pi.node_id.key;
|
let k = pi.node_id.key;
|
||||||
// Register the node
|
// Register the node
|
||||||
if let Some(nr) =
|
if let Some(nr) = routing_table.register_node_with_signed_node_info(
|
||||||
routing_table.register_node_with_signed_node_info(k, pi.signed_node_info, false)
|
RoutingDomain::PublicInternet,
|
||||||
{
|
k,
|
||||||
|
pi.signed_node_info,
|
||||||
|
false,
|
||||||
|
) {
|
||||||
// Add this our futures to process in parallel
|
// Add this our futures to process in parallel
|
||||||
let routing_table = routing_table.clone();
|
let routing_table = routing_table.clone();
|
||||||
unord.push(
|
unord.push(
|
||||||
@ -278,6 +285,7 @@ impl NetworkManager {
|
|||||||
|
|
||||||
// Make invalid signed node info (no signature)
|
// Make invalid signed node info (no signature)
|
||||||
if let Some(nr) = routing_table.register_node_with_signed_node_info(
|
if let Some(nr) = routing_table.register_node_with_signed_node_info(
|
||||||
|
RoutingDomain::PublicInternet,
|
||||||
k,
|
k,
|
||||||
SignedNodeInfo::with_no_signature(NodeInfo {
|
SignedNodeInfo::with_no_signature(NodeInfo {
|
||||||
network_class: NetworkClass::InboundCapable, // Bootstraps are always inbound capable
|
network_class: NetworkClass::InboundCapable, // Bootstraps are always inbound capable
|
||||||
@ -298,7 +306,7 @@ impl NetworkManager {
|
|||||||
let _ = routing_table.find_target(nr.clone()).await;
|
let _ = routing_table.find_target(nr.clone()).await;
|
||||||
|
|
||||||
// Ensure we got the signed peer info
|
// Ensure we got the signed peer info
|
||||||
if !nr.operate(|e| e.has_valid_signed_node_info()) {
|
if !nr.signed_node_info_has_valid_signature(RoutingDomain::PublicInternet) {
|
||||||
log_net!(warn
|
log_net!(warn
|
||||||
"bootstrap at {:?} did not return valid signed node info",
|
"bootstrap at {:?} did not return valid signed node info",
|
||||||
nr
|
nr
|
||||||
@ -320,28 +328,37 @@ impl NetworkManager {
|
|||||||
// Ping each node in the routing table if they need to be pinged
|
// Ping each node in the routing table if they need to be pinged
|
||||||
// to determine their reliability
|
// to determine their reliability
|
||||||
#[instrument(level = "trace", skip(self), err)]
|
#[instrument(level = "trace", skip(self), err)]
|
||||||
pub(super) async fn ping_validator_task_routine(
|
fn ping_validator_public_internet(
|
||||||
self,
|
&self,
|
||||||
stop_token: StopToken,
|
|
||||||
_last_ts: u64,
|
|
||||||
cur_ts: u64,
|
cur_ts: u64,
|
||||||
|
unord: &mut FuturesUnordered<
|
||||||
|
SendPinBoxFuture<Result<NetworkResult<Answer<SenderInfo>>, RPCError>>,
|
||||||
|
>,
|
||||||
) -> EyreResult<()> {
|
) -> EyreResult<()> {
|
||||||
let rpc = self.rpc_processor();
|
let rpc = self.rpc_processor();
|
||||||
let routing_table = self.routing_table();
|
let routing_table = self.routing_table();
|
||||||
|
|
||||||
let relay_node_id = self.relay_node().map(|nr| nr.node_id());
|
// Get all nodes needing pings in the PublicInternet routing domain
|
||||||
let dids = routing_table.all_filtered_dial_info_details(
|
let node_refs = routing_table.get_nodes_needing_ping(RoutingDomain::PublicInternet, cur_ts);
|
||||||
Some(RoutingDomain::PublicInternet),
|
|
||||||
&DialInfoFilter::global(),
|
|
||||||
);
|
|
||||||
let mut unord = FuturesUnordered::new();
|
|
||||||
|
|
||||||
let node_refs = routing_table.get_nodes_needing_ping(cur_ts, relay_node_id);
|
// Look up any NAT mappings we may need to try to preserve with keepalives
|
||||||
let mut mapped_port_info = routing_table.get_mapped_port_info();
|
let mut mapped_port_info = routing_table.get_mapped_port_info();
|
||||||
|
|
||||||
|
// Get the PublicInternet relay if we are using one
|
||||||
|
let opt_relay_nr = routing_table.relay_node(RoutingDomain::PublicInternet);
|
||||||
|
let opt_relay_id = opt_relay_nr.map(|nr| nr.node_id());
|
||||||
|
|
||||||
|
// Get our publicinternet dial info
|
||||||
|
let dids = routing_table.all_filtered_dial_info_details(
|
||||||
|
RoutingDomain::PublicInternet.into(),
|
||||||
|
&DialInfoFilter::all(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// For all nodes needing pings, figure out how many and over what protocols
|
||||||
for nr in node_refs {
|
for nr in node_refs {
|
||||||
let rpc = rpc.clone();
|
// If this is a relay, let's check for NAT keepalives
|
||||||
if Some(nr.node_id()) == relay_node_id {
|
let mut did_pings = false;
|
||||||
|
if Some(nr.node_id()) == opt_relay_id {
|
||||||
// Relay nodes get pinged over all protocols we have inbound dialinfo for
|
// Relay nodes get pinged over all protocols we have inbound dialinfo for
|
||||||
// This is so we can preserve the inbound NAT mappings at our router
|
// This is so we can preserve the inbound NAT mappings at our router
|
||||||
for did in &dids {
|
for did in &dids {
|
||||||
@ -361,19 +378,72 @@ impl NetworkManager {
|
|||||||
};
|
};
|
||||||
if needs_ping {
|
if needs_ping {
|
||||||
let rpc = rpc.clone();
|
let rpc = rpc.clone();
|
||||||
let dif = did.dial_info.make_filter(true);
|
let dif = did.dial_info.make_filter();
|
||||||
let nr_filtered = nr.filtered_clone(dif);
|
let nr_filtered =
|
||||||
|
nr.filtered_clone(NodeRefFilter::new().with_dial_info_filter(dif));
|
||||||
log_net!("--> Keepalive ping to {:?}", nr_filtered);
|
log_net!("--> Keepalive ping to {:?}", nr_filtered);
|
||||||
unord.push(async move { rpc.rpc_call_status(nr_filtered).await }.boxed());
|
unord.push(async move { rpc.rpc_call_status(nr_filtered).await }.boxed());
|
||||||
|
did_pings = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
// Just do a single ping with the best protocol for all the other nodes
|
// Just do a single ping with the best protocol for all the other nodes,
|
||||||
|
// ensuring that we at least ping a relay with -something- even if we didnt have
|
||||||
|
// any mapped ports to preserve
|
||||||
|
if !did_pings {
|
||||||
|
let rpc = rpc.clone();
|
||||||
unord.push(async move { rpc.rpc_call_status(nr).await }.boxed());
|
unord.push(async move { rpc.rpc_call_status(nr).await }.boxed());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for futures to complete
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ping each node in the LocalNetwork routing domain if they
|
||||||
|
// need to be pinged to determine their reliability
|
||||||
|
#[instrument(level = "trace", skip(self), err)]
|
||||||
|
fn ping_validator_local_network(
|
||||||
|
&self,
|
||||||
|
cur_ts: u64,
|
||||||
|
unord: &mut FuturesUnordered<
|
||||||
|
SendPinBoxFuture<Result<NetworkResult<Answer<SenderInfo>>, RPCError>>,
|
||||||
|
>,
|
||||||
|
) -> EyreResult<()> {
|
||||||
|
let rpc = self.rpc_processor();
|
||||||
|
let routing_table = self.routing_table();
|
||||||
|
|
||||||
|
// Get all nodes needing pings in the LocalNetwork routing domain
|
||||||
|
let node_refs = routing_table.get_nodes_needing_ping(RoutingDomain::LocalNetwork, cur_ts);
|
||||||
|
|
||||||
|
// For all nodes needing pings, figure out how many and over what protocols
|
||||||
|
for nr in node_refs {
|
||||||
|
let rpc = rpc.clone();
|
||||||
|
|
||||||
|
// Just do a single ping with the best protocol for all the nodes
|
||||||
|
unord.push(async move { rpc.rpc_call_status(nr).await }.boxed());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ping each node in the routing table if they need to be pinged
|
||||||
|
// to determine their reliability
|
||||||
|
#[instrument(level = "trace", skip(self), err)]
|
||||||
|
pub(super) async fn ping_validator_task_routine(
|
||||||
|
self,
|
||||||
|
stop_token: StopToken,
|
||||||
|
_last_ts: u64,
|
||||||
|
cur_ts: u64,
|
||||||
|
) -> EyreResult<()> {
|
||||||
|
let mut unord = FuturesUnordered::new();
|
||||||
|
|
||||||
|
// PublicInternet
|
||||||
|
self.ping_validator_public_internet(cur_ts, &mut unord)?;
|
||||||
|
|
||||||
|
// LocalNetwork
|
||||||
|
self.ping_validator_local_network(cur_ts, &mut unord)?;
|
||||||
|
|
||||||
|
// Wait for ping futures to complete in parallel
|
||||||
while let Ok(Some(_)) = unord.next().timeout_at(stop_token.clone()).await {}
|
while let Ok(Some(_)) = unord.next().timeout_at(stop_token.clone()).await {}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -381,25 +451,39 @@ impl NetworkManager {
|
|||||||
|
|
||||||
// Ask our remaining peers to give us more peers before we go
|
// Ask our remaining peers to give us more peers before we go
|
||||||
// back to the bootstrap servers to keep us from bothering them too much
|
// back to the bootstrap servers to keep us from bothering them too much
|
||||||
|
// This only adds PublicInternet routing domain peers. The discovery
|
||||||
|
// mechanism for LocalNetwork suffices for locating all the local network
|
||||||
|
// peers that are available. This, however, may query other LocalNetwork
|
||||||
|
// nodes for their PublicInternet peers, which is a very fast way to get
|
||||||
|
// a new node online.
|
||||||
#[instrument(level = "trace", skip(self), err)]
|
#[instrument(level = "trace", skip(self), err)]
|
||||||
pub(super) async fn peer_minimum_refresh_task_routine(
|
pub(super) async fn peer_minimum_refresh_task_routine(
|
||||||
self,
|
self,
|
||||||
stop_token: StopToken,
|
stop_token: StopToken,
|
||||||
) -> EyreResult<()> {
|
) -> EyreResult<()> {
|
||||||
let routing_table = self.routing_table();
|
let routing_table = self.routing_table();
|
||||||
let cur_ts = intf::get_timestamp();
|
let mut ord = FuturesOrdered::new();
|
||||||
|
let min_peer_count = {
|
||||||
|
let c = self.config.get();
|
||||||
|
c.network.dht.min_peer_count as usize
|
||||||
|
};
|
||||||
|
|
||||||
// get list of all peers we know about, even the unreliable ones, and ask them to find nodes close to our node too
|
// For the PublicInternet routing domain, get list of all peers we know about
|
||||||
let noderefs = routing_table.get_all_nodes(cur_ts);
|
// even the unreliable ones, and ask them to find nodes close to our node too
|
||||||
|
let noderefs = routing_table.find_fastest_nodes(
|
||||||
// do peer minimum search concurrently
|
min_peer_count,
|
||||||
let mut unord = FuturesUnordered::new();
|
|_k, _v| true,
|
||||||
|
|k: DHTKey, v: Option<Arc<BucketEntry>>| {
|
||||||
|
NodeRef::new(routing_table.clone(), k, v.unwrap().clone(), None)
|
||||||
|
},
|
||||||
|
);
|
||||||
for nr in noderefs {
|
for nr in noderefs {
|
||||||
log_net!("--- peer minimum search with {:?}", nr);
|
|
||||||
let routing_table = routing_table.clone();
|
let routing_table = routing_table.clone();
|
||||||
unord.push(async move { routing_table.reverse_find_node(nr, false).await });
|
ord.push_back(async move { routing_table.reverse_find_node(nr, false).await });
|
||||||
}
|
}
|
||||||
while let Ok(Some(_)) = unord.next().timeout_at(stop_token.clone()).await {}
|
|
||||||
|
// do peer minimum search in order from fastest to slowest
|
||||||
|
while let Ok(Some(_)) = ord.next().timeout_at(stop_token.clone()).await {}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -414,30 +498,29 @@ impl NetworkManager {
|
|||||||
) -> EyreResult<()> {
|
) -> EyreResult<()> {
|
||||||
// Get our node's current node info and network class and do the right thing
|
// Get our node's current node info and network class and do the right thing
|
||||||
let routing_table = self.routing_table();
|
let routing_table = self.routing_table();
|
||||||
let node_info = routing_table.get_own_node_info();
|
let node_info = routing_table.get_own_node_info(RoutingDomain::PublicInternet);
|
||||||
let network_class = self.get_network_class();
|
let network_class = self.get_network_class(RoutingDomain::PublicInternet);
|
||||||
let mut node_info_changed = false;
|
|
||||||
|
// Get routing domain editor
|
||||||
|
let mut editor = routing_table.edit_routing_domain(RoutingDomain::PublicInternet);
|
||||||
|
|
||||||
// Do we know our network class yet?
|
// Do we know our network class yet?
|
||||||
if let Some(network_class) = network_class {
|
if let Some(network_class) = network_class {
|
||||||
// If we already have a relay, see if it is dead, or if we don't need it any more
|
// If we already have a relay, see if it is dead, or if we don't need it any more
|
||||||
let has_relay = {
|
let has_relay = {
|
||||||
let mut inner = self.inner.lock();
|
if let Some(relay_node) = routing_table.relay_node(RoutingDomain::PublicInternet) {
|
||||||
if let Some(relay_node) = inner.relay_node.clone() {
|
let state = relay_node.state(cur_ts);
|
||||||
let state = relay_node.operate(|e| e.state(cur_ts));
|
|
||||||
// Relay node is dead or no longer needed
|
// Relay node is dead or no longer needed
|
||||||
if matches!(state, BucketEntryState::Dead) {
|
if matches!(state, BucketEntryState::Dead) {
|
||||||
info!("Relay node died, dropping relay {}", relay_node);
|
info!("Relay node died, dropping relay {}", relay_node);
|
||||||
inner.relay_node = None;
|
editor.clear_relay_node();
|
||||||
node_info_changed = true;
|
|
||||||
false
|
false
|
||||||
} else if !node_info.requires_relay() {
|
} else if !node_info.requires_relay() {
|
||||||
info!(
|
info!(
|
||||||
"Relay node no longer required, dropping relay {}",
|
"Relay node no longer required, dropping relay {}",
|
||||||
relay_node
|
relay_node
|
||||||
);
|
);
|
||||||
inner.relay_node = None;
|
editor.clear_relay_node();
|
||||||
node_info_changed = true;
|
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
@ -453,36 +536,32 @@ impl NetworkManager {
|
|||||||
if network_class.outbound_wants_relay() {
|
if network_class.outbound_wants_relay() {
|
||||||
// The outbound relay is the host of the PWA
|
// The outbound relay is the host of the PWA
|
||||||
if let Some(outbound_relay_peerinfo) = intf::get_outbound_relay_peer().await {
|
if let Some(outbound_relay_peerinfo) = intf::get_outbound_relay_peer().await {
|
||||||
let mut inner = self.inner.lock();
|
|
||||||
|
|
||||||
// Register new outbound relay
|
// Register new outbound relay
|
||||||
if let Some(nr) = routing_table.register_node_with_signed_node_info(
|
if let Some(nr) = routing_table.register_node_with_signed_node_info(
|
||||||
|
RoutingDomain::PublicInternet,
|
||||||
outbound_relay_peerinfo.node_id.key,
|
outbound_relay_peerinfo.node_id.key,
|
||||||
outbound_relay_peerinfo.signed_node_info,
|
outbound_relay_peerinfo.signed_node_info,
|
||||||
false,
|
false,
|
||||||
) {
|
) {
|
||||||
info!("Outbound relay node selected: {}", nr);
|
info!("Outbound relay node selected: {}", nr);
|
||||||
inner.relay_node = Some(nr);
|
editor.set_relay_node(nr);
|
||||||
node_info_changed = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Otherwise we must need an inbound relay
|
// Otherwise we must need an inbound relay
|
||||||
} else {
|
} else {
|
||||||
// Find a node in our routing table that is an acceptable inbound relay
|
// Find a node in our routing table that is an acceptable inbound relay
|
||||||
if let Some(nr) = routing_table.find_inbound_relay(cur_ts) {
|
if let Some(nr) =
|
||||||
let mut inner = self.inner.lock();
|
routing_table.find_inbound_relay(RoutingDomain::PublicInternet, cur_ts)
|
||||||
|
{
|
||||||
info!("Inbound relay node selected: {}", nr);
|
info!("Inbound relay node selected: {}", nr);
|
||||||
inner.relay_node = Some(nr);
|
editor.set_relay_node(nr);
|
||||||
node_info_changed = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-send our node info if we selected a relay
|
// Commit the changes
|
||||||
if node_info_changed {
|
editor.commit().await;
|
||||||
self.send_node_info_updates(true).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -7,13 +7,12 @@ use crate::*;
|
|||||||
pub async fn test_add_get_remove() {
|
pub async fn test_add_get_remove() {
|
||||||
let config = get_config();
|
let config = get_config();
|
||||||
|
|
||||||
let mut table = ConnectionTable::new(config);
|
let table = ConnectionTable::new(config);
|
||||||
|
|
||||||
let a1 = ConnectionDescriptor::new_no_local(PeerAddress::new(
|
let a1 = ConnectionDescriptor::new_no_local(PeerAddress::new(
|
||||||
SocketAddress::new(Address::IPV4(Ipv4Addr::new(192, 168, 0, 1)), 8080),
|
SocketAddress::new(Address::IPV4(Ipv4Addr::new(192, 168, 0, 1)), 8080),
|
||||||
ProtocolType::TCP,
|
ProtocolType::TCP,
|
||||||
))
|
));
|
||||||
.unwrap();
|
|
||||||
let a2 = a1;
|
let a2 = a1;
|
||||||
let a3 = ConnectionDescriptor::new(
|
let a3 = ConnectionDescriptor::new(
|
||||||
PeerAddress::new(
|
PeerAddress::new(
|
||||||
@ -26,8 +25,7 @@ pub async fn test_add_get_remove() {
|
|||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
))),
|
))),
|
||||||
)
|
);
|
||||||
.unwrap();
|
|
||||||
let a4 = ConnectionDescriptor::new(
|
let a4 = ConnectionDescriptor::new(
|
||||||
PeerAddress::new(
|
PeerAddress::new(
|
||||||
SocketAddress::new(Address::IPV6(Ipv6Addr::new(192, 0, 0, 0, 0, 0, 0, 1)), 8090),
|
SocketAddress::new(Address::IPV6(Ipv6Addr::new(192, 0, 0, 0, 0, 0, 0, 1)), 8090),
|
||||||
@ -39,8 +37,7 @@ pub async fn test_add_get_remove() {
|
|||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
))),
|
))),
|
||||||
)
|
);
|
||||||
.unwrap();
|
|
||||||
let a5 = ConnectionDescriptor::new(
|
let a5 = ConnectionDescriptor::new(
|
||||||
PeerAddress::new(
|
PeerAddress::new(
|
||||||
SocketAddress::new(Address::IPV6(Ipv6Addr::new(192, 0, 0, 0, 0, 0, 0, 1)), 8090),
|
SocketAddress::new(Address::IPV6(Ipv6Addr::new(192, 0, 0, 0, 0, 0, 0, 1)), 8090),
|
||||||
@ -52,79 +49,72 @@ pub async fn test_add_get_remove() {
|
|||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
))),
|
))),
|
||||||
)
|
);
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let c1 = NetworkConnection::dummy(a1);
|
let c1 = NetworkConnection::dummy(1, a1);
|
||||||
let c1h = c1.get_handle();
|
let c1h = c1.get_handle();
|
||||||
let c2 = NetworkConnection::dummy(a2);
|
let c2 = NetworkConnection::dummy(2, a2);
|
||||||
//let c2h = c2.get_handle();
|
let c3 = NetworkConnection::dummy(3, a3);
|
||||||
let c3 = NetworkConnection::dummy(a3);
|
let c4 = NetworkConnection::dummy(4, a4);
|
||||||
//let c3h = c3.get_handle();
|
let c5 = NetworkConnection::dummy(5, a5);
|
||||||
let c4 = NetworkConnection::dummy(a4);
|
|
||||||
//let c4h = c4.get_handle();
|
|
||||||
let c5 = NetworkConnection::dummy(a5);
|
|
||||||
//let c5h = c5.get_handle();
|
|
||||||
|
|
||||||
assert_eq!(a1, c2.connection_descriptor());
|
assert_eq!(a1, c2.connection_descriptor());
|
||||||
assert_ne!(a3, c4.connection_descriptor());
|
assert_ne!(a3, c4.connection_descriptor());
|
||||||
assert_ne!(a4, c5.connection_descriptor());
|
assert_ne!(a4, c5.connection_descriptor());
|
||||||
|
|
||||||
assert_eq!(table.connection_count(), 0);
|
assert_eq!(table.connection_count(), 0);
|
||||||
assert_eq!(table.get_connection(a1), None);
|
assert_eq!(table.get_connection_by_descriptor(a1), None);
|
||||||
table.add_connection(c1).unwrap();
|
table.add_connection(c1).unwrap();
|
||||||
|
|
||||||
assert_eq!(table.connection_count(), 1);
|
assert_eq!(table.connection_count(), 1);
|
||||||
assert_err!(table.remove_connection(a3));
|
assert!(table.remove_connection_by_id(4).is_none());
|
||||||
assert_err!(table.remove_connection(a4));
|
assert!(table.remove_connection_by_id(5).is_none());
|
||||||
assert_eq!(table.connection_count(), 1);
|
assert_eq!(table.connection_count(), 1);
|
||||||
assert_eq!(table.get_connection(a1), Some(c1h.clone()));
|
assert_eq!(table.get_connection_by_descriptor(a1), Some(c1h.clone()));
|
||||||
assert_eq!(table.get_connection(a1), Some(c1h.clone()));
|
assert_eq!(table.get_connection_by_descriptor(a1), Some(c1h.clone()));
|
||||||
assert_eq!(table.connection_count(), 1);
|
assert_eq!(table.connection_count(), 1);
|
||||||
assert_err!(table.add_connection(c2));
|
assert_err!(table.add_connection(c2));
|
||||||
assert_eq!(table.connection_count(), 1);
|
assert_eq!(table.connection_count(), 1);
|
||||||
assert_eq!(table.get_connection(a1), Some(c1h.clone()));
|
assert_eq!(table.get_connection_by_descriptor(a1), Some(c1h.clone()));
|
||||||
assert_eq!(table.get_connection(a1), Some(c1h.clone()));
|
assert_eq!(table.get_connection_by_descriptor(a1), Some(c1h.clone()));
|
||||||
assert_eq!(table.connection_count(), 1);
|
assert_eq!(table.connection_count(), 1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
table
|
table
|
||||||
.remove_connection(a2)
|
.remove_connection_by_id(1)
|
||||||
.map(|c| c.connection_descriptor())
|
.map(|c| c.connection_descriptor())
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
a1
|
a1
|
||||||
);
|
);
|
||||||
assert_eq!(table.connection_count(), 0);
|
assert_eq!(table.connection_count(), 0);
|
||||||
assert_err!(table.remove_connection(a2));
|
assert!(table.remove_connection_by_id(2).is_none());
|
||||||
assert_eq!(table.connection_count(), 0);
|
assert_eq!(table.connection_count(), 0);
|
||||||
assert_eq!(table.get_connection(a2), None);
|
assert_eq!(table.get_connection_by_descriptor(a2), None);
|
||||||
assert_eq!(table.get_connection(a1), None);
|
assert_eq!(table.get_connection_by_descriptor(a1), None);
|
||||||
assert_eq!(table.connection_count(), 0);
|
assert_eq!(table.connection_count(), 0);
|
||||||
let c1 = NetworkConnection::dummy(a1);
|
let c1 = NetworkConnection::dummy(6, a1);
|
||||||
//let c1h = c1.get_handle();
|
|
||||||
table.add_connection(c1).unwrap();
|
table.add_connection(c1).unwrap();
|
||||||
let c2 = NetworkConnection::dummy(a2);
|
let c2 = NetworkConnection::dummy(7, a2);
|
||||||
//let c2h = c2.get_handle();
|
|
||||||
assert_err!(table.add_connection(c2));
|
assert_err!(table.add_connection(c2));
|
||||||
table.add_connection(c3).unwrap();
|
table.add_connection(c3).unwrap();
|
||||||
table.add_connection(c4).unwrap();
|
table.add_connection(c4).unwrap();
|
||||||
assert_eq!(table.connection_count(), 3);
|
assert_eq!(table.connection_count(), 3);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
table
|
table
|
||||||
.remove_connection(a2)
|
.remove_connection_by_id(6)
|
||||||
.map(|c| c.connection_descriptor())
|
.map(|c| c.connection_descriptor())
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
a2
|
a2
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
table
|
table
|
||||||
.remove_connection(a3)
|
.remove_connection_by_id(3)
|
||||||
.map(|c| c.connection_descriptor())
|
.map(|c| c.connection_descriptor())
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
a3
|
a3
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
table
|
table
|
||||||
.remove_connection(a4)
|
.remove_connection_by_id(4)
|
||||||
.map(|c| c.connection_descriptor())
|
.map(|c| c.connection_descriptor())
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
a4
|
a4
|
||||||
|
@ -160,7 +160,7 @@ impl Network {
|
|||||||
// Handle connection-oriented protocols
|
// Handle connection-oriented protocols
|
||||||
|
|
||||||
// Try to send to the exact existing connection if one exists
|
// Try to send to the exact existing connection if one exists
|
||||||
if let Some(conn) = self.connection_manager().get_connection(descriptor).await {
|
if let Some(conn) = self.connection_manager().get_connection(descriptor) {
|
||||||
// connection exists, send over it
|
// connection exists, send over it
|
||||||
match conn.send_async(data).await {
|
match conn.send_async(data).await {
|
||||||
ConnectionHandleSendResult::Sent => {
|
ConnectionHandleSendResult::Sent => {
|
||||||
@ -292,11 +292,15 @@ impl Network {
|
|||||||
|
|
||||||
//////////////////////////////////////////
|
//////////////////////////////////////////
|
||||||
|
|
||||||
pub fn set_needs_public_dial_info_check(&self) {
|
pub fn set_needs_public_dial_info_check(&self, _punishment: Option<Box<dyn FnOnce() + Send + 'static>>) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_network_class(&self) -> Option<NetworkClass> {
|
pub fn doing_public_dial_info_check(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_network_class(&self, _routing_domain: RoutingDomain) -> Option<NetworkClass> {
|
||||||
// xxx eventually detect tor browser?
|
// xxx eventually detect tor browser?
|
||||||
return if self.inner.lock().network_started {
|
return if self.inner.lock().network_started {
|
||||||
Some(NetworkClass::WebApp)
|
Some(NetworkClass::WebApp)
|
||||||
|
@ -134,8 +134,7 @@ impl WebsocketProtocolHandler {
|
|||||||
|
|
||||||
// Make our connection descriptor
|
// Make our connection descriptor
|
||||||
let wnc = WebsocketNetworkConnection::new(
|
let wnc = WebsocketNetworkConnection::new(
|
||||||
ConnectionDescriptor::new_no_local(dial_info.to_peer_address())
|
ConnectionDescriptor::new_no_local(dial_info.to_peer_address()),
|
||||||
.map_err(|e| io::Error::new(io::ErrorKind::AddrNotAvailable, e))?,
|
|
||||||
wsmeta,
|
wsmeta,
|
||||||
wsio,
|
wsio,
|
||||||
);
|
);
|
||||||
|
@ -39,16 +39,37 @@ pub enum BucketEntryState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
|
||||||
struct LastConnectionKey(PeerScope, ProtocolType, AddressType);
|
struct LastConnectionKey(ProtocolType, AddressType);
|
||||||
|
|
||||||
|
/// Bucket entry information specific to the LocalNetwork RoutingDomain
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct BucketEntryPublicInternet {
|
||||||
|
/// The PublicInternet node info
|
||||||
|
signed_node_info: Option<Box<SignedNodeInfo>>,
|
||||||
|
/// If this node has seen our publicinternet node info
|
||||||
|
seen_our_node_info: bool,
|
||||||
|
/// Last known node status
|
||||||
|
node_status: Option<PublicInternetNodeStatus>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bucket entry information specific to the LocalNetwork RoutingDomain
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct BucketEntryLocalNetwork {
|
||||||
|
/// The LocalNetwork node info
|
||||||
|
signed_node_info: Option<Box<SignedNodeInfo>>,
|
||||||
|
/// If this node has seen our localnetwork node info
|
||||||
|
seen_our_node_info: bool,
|
||||||
|
/// Last known node status
|
||||||
|
node_status: Option<LocalNetworkNodeStatus>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct BucketEntryInner {
|
pub struct BucketEntryInner {
|
||||||
min_max_version: Option<(u8, u8)>,
|
min_max_version: Option<(u8, u8)>,
|
||||||
seen_our_node_info: bool,
|
|
||||||
updated_since_last_network_change: bool,
|
updated_since_last_network_change: bool,
|
||||||
last_connections: BTreeMap<LastConnectionKey, (ConnectionDescriptor, u64)>,
|
last_connections: BTreeMap<LastConnectionKey, (ConnectionDescriptor, u64)>,
|
||||||
opt_signed_node_info: Option<SignedNodeInfo>,
|
public_internet: BucketEntryPublicInternet,
|
||||||
opt_local_node_info: Option<LocalNodeInfo>,
|
local_network: BucketEntryLocalNetwork,
|
||||||
peer_stats: PeerStats,
|
peer_stats: PeerStats,
|
||||||
latency_stats_accounting: LatencyStatsAccounting,
|
latency_stats_accounting: LatencyStatsAccounting,
|
||||||
transfer_stats_accounting: TransferStatsAccounting,
|
transfer_stats_accounting: TransferStatsAccounting,
|
||||||
@ -115,29 +136,39 @@ impl BucketEntryInner {
|
|||||||
move |e1, e2| Self::cmp_fastest_reliable(cur_ts, e1, e2)
|
move |e1, e2| Self::cmp_fastest_reliable(cur_ts, e1, e2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn clear_signed_node_info(&mut self, routing_domain: RoutingDomain) {
|
||||||
|
// Get the correct signed_node_info for the chosen routing domain
|
||||||
|
let opt_current_sni = match routing_domain {
|
||||||
|
RoutingDomain::LocalNetwork => &mut self.local_network.signed_node_info,
|
||||||
|
RoutingDomain::PublicInternet => &mut self.public_internet.signed_node_info,
|
||||||
|
};
|
||||||
|
*opt_current_sni = None;
|
||||||
|
}
|
||||||
|
|
||||||
// Retuns true if the node info changed
|
// Retuns true if the node info changed
|
||||||
pub fn update_signed_node_info(
|
pub fn update_signed_node_info(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
routing_domain: RoutingDomain,
|
||||||
signed_node_info: SignedNodeInfo,
|
signed_node_info: SignedNodeInfo,
|
||||||
allow_invalid_signature: bool,
|
|
||||||
) {
|
) {
|
||||||
// Don't allow invalid signatures unless we are explicitly allowing it
|
// Get the correct signed_node_info for the chosen routing domain
|
||||||
if !allow_invalid_signature && !signed_node_info.signature.valid {
|
let opt_current_sni = match routing_domain {
|
||||||
log_rtab!(debug "Invalid signature on signed node info: {:?}", signed_node_info);
|
RoutingDomain::LocalNetwork => &mut self.local_network.signed_node_info,
|
||||||
return;
|
RoutingDomain::PublicInternet => &mut self.public_internet.signed_node_info,
|
||||||
}
|
};
|
||||||
|
|
||||||
// See if we have an existing signed_node_info to update or not
|
// See if we have an existing signed_node_info to update or not
|
||||||
if let Some(current_sni) = &self.opt_signed_node_info {
|
if let Some(current_sni) = opt_current_sni {
|
||||||
// If the timestamp hasn't changed or is less, ignore this update
|
// If the timestamp hasn't changed or is less, ignore this update
|
||||||
if signed_node_info.timestamp <= current_sni.timestamp {
|
if signed_node_info.timestamp <= current_sni.timestamp {
|
||||||
// If we received a node update with the same timestamp
|
// If we received a node update with the same timestamp
|
||||||
// we can make this node live again, but only if our network hasn't changed
|
// we can make this node live again, but only if our network has recently changed
|
||||||
|
// which may make nodes that were unreachable now reachable with the same dialinfo
|
||||||
if !self.updated_since_last_network_change
|
if !self.updated_since_last_network_change
|
||||||
&& signed_node_info.timestamp == current_sni.timestamp
|
&& signed_node_info.timestamp == current_sni.timestamp
|
||||||
{
|
{
|
||||||
// No need to update the signednodeinfo though since the timestamp is the same
|
// No need to update the signednodeinfo though since the timestamp is the same
|
||||||
// Just return true so we can make the node not dead
|
// Touch the node and let it try to live again
|
||||||
self.updated_since_last_network_change = true;
|
self.updated_since_last_network_change = true;
|
||||||
self.touch_last_seen(intf::get_timestamp());
|
self.touch_last_seen(intf::get_timestamp());
|
||||||
}
|
}
|
||||||
@ -152,48 +183,70 @@ impl BucketEntryInner {
|
|||||||
));
|
));
|
||||||
|
|
||||||
// Update the signed node info
|
// Update the signed node info
|
||||||
self.opt_signed_node_info = Some(signed_node_info);
|
*opt_current_sni = Some(Box::new(signed_node_info));
|
||||||
self.updated_since_last_network_change = true;
|
self.updated_since_last_network_change = true;
|
||||||
self.touch_last_seen(intf::get_timestamp());
|
self.touch_last_seen(intf::get_timestamp());
|
||||||
}
|
}
|
||||||
pub fn update_local_node_info(&mut self, local_node_info: LocalNodeInfo) {
|
|
||||||
self.opt_local_node_info = Some(local_node_info)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn has_node_info(&self) -> bool {
|
pub fn has_node_info(&self, routing_domain_set: RoutingDomainSet) -> bool {
|
||||||
self.opt_signed_node_info.is_some()
|
for routing_domain in routing_domain_set {
|
||||||
|
// Get the correct signed_node_info for the chosen routing domain
|
||||||
|
let opt_current_sni = match routing_domain {
|
||||||
|
RoutingDomain::LocalNetwork => &self.local_network.signed_node_info,
|
||||||
|
RoutingDomain::PublicInternet => &self.public_internet.signed_node_info,
|
||||||
|
};
|
||||||
|
if opt_current_sni.is_some() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_valid_signed_node_info(&self) -> bool {
|
|
||||||
if let Some(sni) = &self.opt_signed_node_info {
|
|
||||||
sni.is_valid()
|
|
||||||
} else {
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn node_info(&self, routing_domain: RoutingDomain) -> Option<&NodeInfo> {
|
||||||
|
let opt_current_sni = match routing_domain {
|
||||||
|
RoutingDomain::LocalNetwork => &self.local_network.signed_node_info,
|
||||||
|
RoutingDomain::PublicInternet => &self.public_internet.signed_node_info,
|
||||||
|
};
|
||||||
|
opt_current_sni.as_ref().map(|s| &s.node_info)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_local_node_info(&self) -> bool {
|
pub fn signed_node_info(&self, routing_domain: RoutingDomain) -> Option<&SignedNodeInfo> {
|
||||||
self.opt_local_node_info.is_some()
|
let opt_current_sni = match routing_domain {
|
||||||
|
RoutingDomain::LocalNetwork => &self.local_network.signed_node_info,
|
||||||
|
RoutingDomain::PublicInternet => &self.public_internet.signed_node_info,
|
||||||
|
};
|
||||||
|
opt_current_sni.as_ref().map(|s| s.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn node_info(&self) -> Option<NodeInfo> {
|
pub fn make_peer_info(&self, key: DHTKey, routing_domain: RoutingDomain) -> Option<PeerInfo> {
|
||||||
self.opt_signed_node_info
|
let opt_current_sni = match routing_domain {
|
||||||
.as_ref()
|
RoutingDomain::LocalNetwork => &self.local_network.signed_node_info,
|
||||||
.map(|s| s.node_info.clone())
|
RoutingDomain::PublicInternet => &self.public_internet.signed_node_info,
|
||||||
}
|
};
|
||||||
pub fn local_node_info(&self) -> Option<LocalNodeInfo> {
|
opt_current_sni.as_ref().map(|s| PeerInfo {
|
||||||
self.opt_local_node_info.clone()
|
|
||||||
}
|
|
||||||
pub fn peer_info(&self, key: DHTKey) -> Option<PeerInfo> {
|
|
||||||
self.opt_signed_node_info.as_ref().map(|s| PeerInfo {
|
|
||||||
node_id: NodeId::new(key),
|
node_id: NodeId::new(key),
|
||||||
signed_node_info: s.clone(),
|
signed_node_info: *s.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn descriptor_to_key(last_connection: ConnectionDescriptor) -> LastConnectionKey {
|
pub fn best_routing_domain(
|
||||||
|
&self,
|
||||||
|
routing_domain_set: RoutingDomainSet,
|
||||||
|
) -> Option<RoutingDomain> {
|
||||||
|
for routing_domain in routing_domain_set {
|
||||||
|
let opt_current_sni = match routing_domain {
|
||||||
|
RoutingDomain::LocalNetwork => &self.local_network.signed_node_info,
|
||||||
|
RoutingDomain::PublicInternet => &self.public_internet.signed_node_info,
|
||||||
|
};
|
||||||
|
if opt_current_sni.is_some() {
|
||||||
|
return Some(routing_domain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn descriptor_to_key(&self, last_connection: ConnectionDescriptor) -> LastConnectionKey {
|
||||||
LastConnectionKey(
|
LastConnectionKey(
|
||||||
last_connection.peer_scope(),
|
|
||||||
last_connection.protocol_type(),
|
last_connection.protocol_type(),
|
||||||
last_connection.address_type(),
|
last_connection.address_type(),
|
||||||
)
|
)
|
||||||
@ -201,7 +254,7 @@ impl BucketEntryInner {
|
|||||||
|
|
||||||
// Stores a connection descriptor in this entry's table of last connections
|
// Stores a connection descriptor in this entry's table of last connections
|
||||||
pub fn set_last_connection(&mut self, last_connection: ConnectionDescriptor, timestamp: u64) {
|
pub fn set_last_connection(&mut self, last_connection: ConnectionDescriptor, timestamp: u64) {
|
||||||
let key = Self::descriptor_to_key(last_connection);
|
let key = self.descriptor_to_key(last_connection);
|
||||||
self.last_connections
|
self.last_connections
|
||||||
.insert(key, (last_connection, timestamp));
|
.insert(key, (last_connection, timestamp));
|
||||||
}
|
}
|
||||||
@ -211,23 +264,30 @@ impl BucketEntryInner {
|
|||||||
self.last_connections.clear();
|
self.last_connections.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the best 'last connection' that matches a set of protocol types and address types
|
// Gets the best 'last connection' that matches a set of routing domain, protocol types and address types
|
||||||
pub fn last_connection(
|
pub(super) fn last_connection(
|
||||||
&self,
|
&self,
|
||||||
dial_info_filter: Option<DialInfoFilter>,
|
routing_table_inner: &RoutingTableInner,
|
||||||
|
node_ref_filter: Option<NodeRefFilter>,
|
||||||
) -> Option<(ConnectionDescriptor, u64)> {
|
) -> Option<(ConnectionDescriptor, u64)> {
|
||||||
// Iterate peer scopes and protocol types and address type in order to ensure we pick the preferred protocols if all else is the same
|
// Iterate peer scopes and protocol types and address type in order to ensure we pick the preferred protocols if all else is the same
|
||||||
let dif = dial_info_filter.unwrap_or_default();
|
let nrf = node_ref_filter.unwrap_or_default();
|
||||||
for ps in dif.peer_scope_set {
|
for pt in nrf.dial_info_filter.protocol_type_set {
|
||||||
for pt in dif.protocol_type_set {
|
for at in nrf.dial_info_filter.address_type_set {
|
||||||
for at in dif.address_type_set {
|
let key = LastConnectionKey(pt, at);
|
||||||
let key = LastConnectionKey(ps, pt, at);
|
|
||||||
if let Some(v) = self.last_connections.get(&key) {
|
if let Some(v) = self.last_connections.get(&key) {
|
||||||
|
// Verify this connection could be in the filtered routing domain
|
||||||
|
let address = v.0.remote_address().address();
|
||||||
|
if let Some(rd) =
|
||||||
|
RoutingTable::routing_domain_for_address_inner(routing_table_inner, address)
|
||||||
|
{
|
||||||
|
if nrf.routing_domain_set.contains(rd) {
|
||||||
return Some(*v);
|
return Some(*v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
pub fn set_min_max_version(&mut self, min_max_version: (u8, u8)) {
|
pub fn set_min_max_version(&mut self, min_max_version: (u8, u8)) {
|
||||||
@ -253,15 +313,46 @@ impl BucketEntryInner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_node_status(&mut self, status: NodeStatus) {
|
pub fn update_node_status(&mut self, status: NodeStatus) {
|
||||||
self.peer_stats.status = Some(status);
|
match status {
|
||||||
|
NodeStatus::LocalNetwork(ln) => {
|
||||||
|
self.local_network.node_status = Some(ln);
|
||||||
|
}
|
||||||
|
NodeStatus::PublicInternet(pi) => {
|
||||||
|
self.public_internet.node_status = Some(pi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn node_status(&self, routing_domain: RoutingDomain) -> Option<NodeStatus> {
|
||||||
|
match routing_domain {
|
||||||
|
RoutingDomain::LocalNetwork => self
|
||||||
|
.local_network
|
||||||
|
.node_status
|
||||||
|
.as_ref()
|
||||||
|
.map(|ln| NodeStatus::LocalNetwork(ln.clone())),
|
||||||
|
RoutingDomain::PublicInternet => self
|
||||||
|
.public_internet
|
||||||
|
.node_status
|
||||||
|
.as_ref()
|
||||||
|
.map(|pi| NodeStatus::PublicInternet(pi.clone())),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_seen_our_node_info(&mut self, seen: bool) {
|
pub fn set_seen_our_node_info(&mut self, routing_domain: RoutingDomain, seen: bool) {
|
||||||
self.seen_our_node_info = seen;
|
match routing_domain {
|
||||||
|
RoutingDomain::LocalNetwork => {
|
||||||
|
self.local_network.seen_our_node_info = seen;
|
||||||
|
}
|
||||||
|
RoutingDomain::PublicInternet => {
|
||||||
|
self.public_internet.seen_our_node_info = seen;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_seen_our_node_info(&self) -> bool {
|
pub fn has_seen_our_node_info(&self, routing_domain: RoutingDomain) -> bool {
|
||||||
self.seen_our_node_info
|
match routing_domain {
|
||||||
|
RoutingDomain::LocalNetwork => self.local_network.seen_our_node_info,
|
||||||
|
RoutingDomain::PublicInternet => self.public_internet.seen_our_node_info,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_updated_since_last_network_change(&mut self, updated: bool) {
|
pub fn set_updated_since_last_network_change(&mut self, updated: bool) {
|
||||||
@ -337,21 +428,15 @@ impl BucketEntryInner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if this node needs a ping right now to validate it is still reachable
|
// Check if this node needs a ping right now to validate it is still reachable
|
||||||
pub(super) fn needs_ping(
|
pub(super) fn needs_ping(&self, cur_ts: u64, needs_keepalive: bool) -> bool {
|
||||||
&self,
|
|
||||||
node_id: &DHTKey,
|
|
||||||
cur_ts: u64,
|
|
||||||
relay_node_id: Option<DHTKey>,
|
|
||||||
) -> bool {
|
|
||||||
// See which ping pattern we are to use
|
// See which ping pattern we are to use
|
||||||
let state = self.state(cur_ts);
|
let state = self.state(cur_ts);
|
||||||
|
|
||||||
// If this entry is our relay node, then we should ping it regularly to keep our association alive
|
// If this entry needs a keepalive (like a relay node),
|
||||||
if let Some(relay_node_id) = relay_node_id {
|
// then we should ping it regularly to keep our association alive
|
||||||
if relay_node_id == *node_id {
|
if needs_keepalive {
|
||||||
return self.needs_constant_ping(cur_ts, KEEPALIVE_PING_INTERVAL_SECS as u64);
|
return self.needs_constant_ping(cur_ts, KEEPALIVE_PING_INTERVAL_SECS as u64);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
match state {
|
match state {
|
||||||
BucketEntryState::Reliable => {
|
BucketEntryState::Reliable => {
|
||||||
@ -494,17 +579,23 @@ impl BucketEntry {
|
|||||||
ref_count: AtomicU32::new(0),
|
ref_count: AtomicU32::new(0),
|
||||||
inner: RwLock::new(BucketEntryInner {
|
inner: RwLock::new(BucketEntryInner {
|
||||||
min_max_version: None,
|
min_max_version: None,
|
||||||
seen_our_node_info: false,
|
|
||||||
updated_since_last_network_change: false,
|
updated_since_last_network_change: false,
|
||||||
last_connections: BTreeMap::new(),
|
last_connections: BTreeMap::new(),
|
||||||
opt_signed_node_info: None,
|
local_network: BucketEntryLocalNetwork {
|
||||||
opt_local_node_info: None,
|
seen_our_node_info: false,
|
||||||
|
signed_node_info: None,
|
||||||
|
node_status: None,
|
||||||
|
},
|
||||||
|
public_internet: BucketEntryPublicInternet {
|
||||||
|
seen_our_node_info: false,
|
||||||
|
signed_node_info: None,
|
||||||
|
node_status: None,
|
||||||
|
},
|
||||||
peer_stats: PeerStats {
|
peer_stats: PeerStats {
|
||||||
time_added: now,
|
time_added: now,
|
||||||
rpc_stats: RPCStats::default(),
|
rpc_stats: RPCStats::default(),
|
||||||
latency: None,
|
latency: None,
|
||||||
transfer: TransferStatsDownUp::default(),
|
transfer: TransferStatsDownUp::default(),
|
||||||
status: None,
|
|
||||||
},
|
},
|
||||||
latency_stats_accounting: LatencyStatsAccounting::new(),
|
latency_stats_accounting: LatencyStatsAccounting::new(),
|
||||||
transfer_stats_accounting: TransferStatsAccounting::new(),
|
transfer_stats_accounting: TransferStatsAccounting::new(),
|
||||||
@ -516,7 +607,7 @@ impl BucketEntry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with<F, R>(&self, f: F) -> R
|
pub(super) fn with<F, R>(&self, f: F) -> R
|
||||||
where
|
where
|
||||||
F: FnOnce(&BucketEntryInner) -> R,
|
F: FnOnce(&BucketEntryInner) -> R,
|
||||||
{
|
{
|
||||||
@ -524,7 +615,7 @@ impl BucketEntry {
|
|||||||
f(&*inner)
|
f(&*inner)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_mut<F, R>(&self, f: F) -> R
|
pub(super) fn with_mut<F, R>(&self, f: F) -> R
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut BucketEntryInner) -> R,
|
F: FnOnce(&mut BucketEntryInner) -> R,
|
||||||
{
|
{
|
||||||
@ -547,7 +638,7 @@ impl Drop for BucketEntry {
|
|||||||
|
|
||||||
panic!(
|
panic!(
|
||||||
"bucket entry dropped with non-zero refcount: {:#?}",
|
"bucket entry dropped with non-zero refcount: {:#?}",
|
||||||
self.inner.read().node_info()
|
&*self.inner.read()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ impl RoutingTable {
|
|||||||
let inner = self.inner.read();
|
let inner = self.inner.read();
|
||||||
out += "Routing Table Info:\n";
|
out += "Routing Table Info:\n";
|
||||||
|
|
||||||
out += &format!(" Node Id: {}\n", inner.node_id.encode());
|
out += &format!(" Node Id: {}\n", self.unlocked_inner.node_id.encode());
|
||||||
out += &format!(
|
out += &format!(
|
||||||
" Self Latency Stats Accounting: {:#?}\n\n",
|
" Self Latency Stats Accounting: {:#?}\n\n",
|
||||||
inner.self_latency_stats_accounting
|
inner.self_latency_stats_accounting
|
||||||
@ -85,8 +85,17 @@ impl RoutingTable {
|
|||||||
out += &format!(" {:>2}: {:?}\n", n, gdi);
|
out += &format!(" {:>2}: {:?}\n", n, gdi);
|
||||||
}
|
}
|
||||||
|
|
||||||
out += "Own PeerInfo:\n";
|
out += "LocalNetwork PeerInfo:\n";
|
||||||
out += &format!(" {:#?}\n", self.get_own_peer_info());
|
out += &format!(
|
||||||
|
" {:#?}\n",
|
||||||
|
self.get_own_peer_info(RoutingDomain::LocalNetwork)
|
||||||
|
);
|
||||||
|
|
||||||
|
out += "PublicInternet PeerInfo:\n";
|
||||||
|
out += &format!(
|
||||||
|
" {:#?}\n",
|
||||||
|
self.get_own_peer_info(RoutingDomain::PublicInternet)
|
||||||
|
);
|
||||||
|
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
@ -142,7 +151,7 @@ impl RoutingTable {
|
|||||||
let mut out = String::new();
|
let mut out = String::new();
|
||||||
out += &format!("Entry {:?}:\n", node_id);
|
out += &format!("Entry {:?}:\n", node_id);
|
||||||
if let Some(nr) = self.lookup_node_ref(node_id) {
|
if let Some(nr) = self.lookup_node_ref(node_id) {
|
||||||
out += &nr.operate(|e| format!("{:#?}\n", e));
|
out += &nr.operate(|_rt, e| format!("{:#?}\n", e));
|
||||||
} else {
|
} else {
|
||||||
out += "Entry not found\n";
|
out += "Entry not found\n";
|
||||||
}
|
}
|
||||||
|
@ -15,38 +15,47 @@ pub struct MappedPortInfo {
|
|||||||
impl RoutingTable {
|
impl RoutingTable {
|
||||||
// Makes a filter that finds nodes with a matching inbound dialinfo
|
// Makes a filter that finds nodes with a matching inbound dialinfo
|
||||||
pub fn make_inbound_dial_info_entry_filter(
|
pub fn make_inbound_dial_info_entry_filter(
|
||||||
|
routing_domain: RoutingDomain,
|
||||||
dial_info_filter: DialInfoFilter,
|
dial_info_filter: DialInfoFilter,
|
||||||
) -> impl FnMut(&BucketEntryInner) -> bool {
|
) -> impl FnMut(&BucketEntryInner) -> bool {
|
||||||
// does it have matching public dial info?
|
// does it have matching public dial info?
|
||||||
move |e| {
|
move |e| {
|
||||||
e.node_info()
|
if let Some(ni) = e.node_info(routing_domain) {
|
||||||
.map(|n| {
|
if ni
|
||||||
n.first_filtered_dial_info_detail(|did| did.matches_filter(&dial_info_filter))
|
.first_filtered_dial_info_detail(|did| did.matches_filter(&dial_info_filter))
|
||||||
.is_some()
|
.is_some()
|
||||||
})
|
{
|
||||||
.unwrap_or(false)
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Makes a filter that finds nodes capable of dialing a particular outbound dialinfo
|
// Makes a filter that finds nodes capable of dialing a particular outbound dialinfo
|
||||||
pub fn make_outbound_dial_info_entry_filter(
|
pub fn make_outbound_dial_info_entry_filter(
|
||||||
|
routing_domain: RoutingDomain,
|
||||||
dial_info: DialInfo,
|
dial_info: DialInfo,
|
||||||
) -> impl FnMut(&BucketEntryInner) -> bool {
|
) -> impl FnMut(&BucketEntryInner) -> bool {
|
||||||
// does the node's outbound capabilities match the dialinfo?
|
// does the node's outbound capabilities match the dialinfo?
|
||||||
move |e| {
|
move |e| {
|
||||||
e.node_info()
|
if let Some(ni) = e.node_info(routing_domain) {
|
||||||
.map(|n| {
|
let dif = DialInfoFilter::all()
|
||||||
let mut dif = DialInfoFilter::all();
|
.with_protocol_type_set(ni.outbound_protocols)
|
||||||
dif = dif.with_protocol_type_set(n.outbound_protocols);
|
.with_address_type_set(ni.address_types);
|
||||||
dif = dif.with_address_type_set(n.address_types);
|
if dial_info.matches_filter(&dif) {
|
||||||
dial_info.matches_filter(&dif)
|
return true;
|
||||||
})
|
}
|
||||||
.unwrap_or(false)
|
}
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a filter that wraps another filter
|
// Make a filter that wraps another filter
|
||||||
pub fn combine_filters<F, G>(mut f1: F, mut f2: G) -> impl FnMut(&BucketEntryInner) -> bool
|
pub fn combine_entry_filters<F, G>(
|
||||||
|
mut f1: F,
|
||||||
|
mut f2: G,
|
||||||
|
) -> impl FnMut(&BucketEntryInner) -> bool
|
||||||
where
|
where
|
||||||
F: FnMut(&BucketEntryInner) -> bool,
|
F: FnMut(&BucketEntryInner) -> bool,
|
||||||
G: FnMut(&BucketEntryInner) -> bool,
|
G: FnMut(&BucketEntryInner) -> bool,
|
||||||
@ -75,18 +84,21 @@ impl RoutingTable {
|
|||||||
// count
|
// count
|
||||||
node_count,
|
node_count,
|
||||||
// filter
|
// filter
|
||||||
Some(move |_k: DHTKey, v: Option<Arc<BucketEntry>>| {
|
|_k: DHTKey, v: Option<Arc<BucketEntry>>| {
|
||||||
let entry = v.unwrap();
|
let entry = v.unwrap();
|
||||||
entry.with(|e| {
|
entry.with(|e| {
|
||||||
// skip nodes on our local network here
|
// skip nodes on local network
|
||||||
if e.local_node_info().is_some() {
|
if e.node_info(RoutingDomain::LocalNetwork).is_some() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// skip nodes not on public internet
|
||||||
|
if e.node_info(RoutingDomain::PublicInternet).is_none() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// skip nodes that dont match entry filter
|
// skip nodes that dont match entry filter
|
||||||
entry_filter(e)
|
entry_filter(e)
|
||||||
})
|
})
|
||||||
}),
|
},
|
||||||
// transform
|
// transform
|
||||||
|k: DHTKey, v: Option<Arc<BucketEntry>>| {
|
|k: DHTKey, v: Option<Arc<BucketEntry>>| {
|
||||||
NodeRef::new(self.clone(), k, v.unwrap().clone(), None)
|
NodeRef::new(self.clone(), k, v.unwrap().clone(), None)
|
||||||
@ -109,19 +121,18 @@ impl RoutingTable {
|
|||||||
// count
|
// count
|
||||||
protocol_types.len() * 2 * max_per_type,
|
protocol_types.len() * 2 * max_per_type,
|
||||||
// filter
|
// filter
|
||||||
Some(move |_k: DHTKey, v: Option<Arc<BucketEntry>>| {
|
move |_k: DHTKey, v: Option<Arc<BucketEntry>>| {
|
||||||
let entry = v.unwrap();
|
let entry = v.unwrap();
|
||||||
entry.with(|e| {
|
entry.with(|e| {
|
||||||
// skip nodes on our local network here
|
// skip nodes on our local network here
|
||||||
if e.local_node_info().is_some() {
|
if e.has_node_info(RoutingDomain::LocalNetwork.into()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// does it have some dial info we need?
|
// does it have some dial info we need?
|
||||||
let filter = |n: NodeInfo| {
|
let filter = |n: &NodeInfo| {
|
||||||
let mut keep = false;
|
let mut keep = false;
|
||||||
for did in n.dial_info_detail_list {
|
for did in &n.dial_info_detail_list {
|
||||||
if did.dial_info.is_global() {
|
|
||||||
if matches!(did.dial_info.address_type(), AddressType::IPV4) {
|
if matches!(did.dial_info.address_type(), AddressType::IPV4) {
|
||||||
for (n, protocol_type) in protocol_types.iter().enumerate() {
|
for (n, protocol_type) in protocol_types.iter().enumerate() {
|
||||||
if nodes_proto_v4[n] < max_per_type
|
if nodes_proto_v4[n] < max_per_type
|
||||||
@ -131,8 +142,7 @@ impl RoutingTable {
|
|||||||
keep = true;
|
keep = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if matches!(did.dial_info.address_type(), AddressType::IPV6)
|
} else if matches!(did.dial_info.address_type(), AddressType::IPV6) {
|
||||||
{
|
|
||||||
for (n, protocol_type) in protocol_types.iter().enumerate() {
|
for (n, protocol_type) in protocol_types.iter().enumerate() {
|
||||||
if nodes_proto_v6[n] < max_per_type
|
if nodes_proto_v6[n] < max_per_type
|
||||||
&& did.dial_info.protocol_type() == *protocol_type
|
&& did.dial_info.protocol_type() == *protocol_type
|
||||||
@ -143,13 +153,14 @@ impl RoutingTable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
keep
|
keep
|
||||||
};
|
};
|
||||||
|
|
||||||
e.node_info().map(filter).unwrap_or(false)
|
e.node_info(RoutingDomain::PublicInternet)
|
||||||
|
.map(filter)
|
||||||
|
.unwrap_or(false)
|
||||||
})
|
})
|
||||||
}),
|
},
|
||||||
// transform
|
// transform
|
||||||
|k: DHTKey, v: Option<Arc<BucketEntry>>| {
|
|k: DHTKey, v: Option<Arc<BucketEntry>>| {
|
||||||
NodeRef::new(self.clone(), k, v.unwrap().clone(), None)
|
NodeRef::new(self.clone(), k, v.unwrap().clone(), None)
|
||||||
@ -157,50 +168,30 @@ impl RoutingTable {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get our own node's peer info (public node info) so we can share it with other nodes
|
|
||||||
pub fn get_own_peer_info(&self) -> PeerInfo {
|
|
||||||
PeerInfo::new(NodeId::new(self.node_id()), self.get_own_signed_node_info())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_own_signed_node_info(&self) -> SignedNodeInfo {
|
|
||||||
let node_id = NodeId::new(self.node_id());
|
|
||||||
let secret = self.node_id_secret();
|
|
||||||
SignedNodeInfo::with_secret(self.get_own_node_info(), node_id, &secret).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_own_node_info(&self) -> NodeInfo {
|
|
||||||
let netman = self.network_manager();
|
|
||||||
let relay_node = netman.relay_node();
|
|
||||||
let pc = netman.get_protocol_config();
|
|
||||||
NodeInfo {
|
|
||||||
network_class: netman.get_network_class().unwrap_or(NetworkClass::Invalid),
|
|
||||||
outbound_protocols: pc.outbound,
|
|
||||||
address_types: pc.family_global,
|
|
||||||
min_version: MIN_VERSION,
|
|
||||||
max_version: MAX_VERSION,
|
|
||||||
dial_info_detail_list: self.dial_info_details(RoutingDomain::PublicInternet),
|
|
||||||
relay_peer_info: relay_node.and_then(|rn| rn.peer_info().map(Box::new)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn filter_has_valid_signed_node_info(
|
pub fn filter_has_valid_signed_node_info(
|
||||||
|
&self,
|
||||||
|
routing_domain: RoutingDomain,
|
||||||
v: Option<Arc<BucketEntry>>,
|
v: Option<Arc<BucketEntry>>,
|
||||||
own_peer_info_is_valid: bool,
|
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match v {
|
match v {
|
||||||
None => own_peer_info_is_valid,
|
None => self.has_valid_own_node_info(routing_domain),
|
||||||
Some(entry) => entry.with(|e| e.has_valid_signed_node_info()),
|
Some(entry) => entry.with(|e| {
|
||||||
|
e.signed_node_info(routing_domain.into())
|
||||||
|
.map(|sni| sni.has_valid_signature())
|
||||||
|
.unwrap_or(false)
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn transform_to_peer_info(
|
pub fn transform_to_peer_info(
|
||||||
|
&self,
|
||||||
|
routing_domain: RoutingDomain,
|
||||||
k: DHTKey,
|
k: DHTKey,
|
||||||
v: Option<Arc<BucketEntry>>,
|
v: Option<Arc<BucketEntry>>,
|
||||||
own_peer_info: &PeerInfo,
|
|
||||||
) -> PeerInfo {
|
) -> PeerInfo {
|
||||||
match v {
|
match v {
|
||||||
None => own_peer_info.clone(),
|
None => self.get_own_peer_info(routing_domain),
|
||||||
Some(entry) => entry.with(|e| e.peer_info(k).unwrap()),
|
Some(entry) => entry.with(|e| e.make_peer_info(k, routing_domain).unwrap()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,7 +212,7 @@ impl RoutingTable {
|
|||||||
T: FnMut(DHTKey, Option<Arc<BucketEntry>>) -> O,
|
T: FnMut(DHTKey, Option<Arc<BucketEntry>>) -> O,
|
||||||
{
|
{
|
||||||
let inner = self.inner.read();
|
let inner = self.inner.read();
|
||||||
let self_node_id = inner.node_id;
|
let self_node_id = self.unlocked_inner.node_id;
|
||||||
|
|
||||||
// collect all the nodes for sorting
|
// collect all the nodes for sorting
|
||||||
let mut nodes =
|
let mut nodes =
|
||||||
@ -258,7 +249,7 @@ impl RoutingTable {
|
|||||||
pub fn find_fastest_nodes<T, F, O>(
|
pub fn find_fastest_nodes<T, F, O>(
|
||||||
&self,
|
&self,
|
||||||
node_count: usize,
|
node_count: usize,
|
||||||
mut filter: Option<F>,
|
mut filter: F,
|
||||||
transform: T,
|
transform: T,
|
||||||
) -> Vec<O>
|
) -> Vec<O>
|
||||||
where
|
where
|
||||||
@ -276,7 +267,7 @@ impl RoutingTable {
|
|||||||
if entry.with(|e| e.state(cur_ts) == BucketEntryState::Dead) {
|
if entry.with(|e| e.state(cur_ts) == BucketEntryState::Dead) {
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
filter.as_mut().map(|f| f(k, v)).unwrap_or(true)
|
filter(k, v)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// always filter out self peer, as it is irrelevant to the 'fastest nodes' search
|
// always filter out self peer, as it is irrelevant to the 'fastest nodes' search
|
||||||
@ -340,23 +331,23 @@ impl RoutingTable {
|
|||||||
pub fn find_closest_nodes<F, T, O>(
|
pub fn find_closest_nodes<F, T, O>(
|
||||||
&self,
|
&self,
|
||||||
node_id: DHTKey,
|
node_id: DHTKey,
|
||||||
mut filter: Option<F>,
|
filter: F,
|
||||||
mut transform: T,
|
mut transform: T,
|
||||||
) -> Vec<O>
|
) -> Vec<O>
|
||||||
where
|
where
|
||||||
T: FnMut(DHTKey, Option<Arc<BucketEntry>>) -> O,
|
|
||||||
F: FnMut(DHTKey, Option<Arc<BucketEntry>>) -> bool,
|
F: FnMut(DHTKey, Option<Arc<BucketEntry>>) -> bool,
|
||||||
|
T: FnMut(DHTKey, Option<Arc<BucketEntry>>) -> O,
|
||||||
{
|
{
|
||||||
let cur_ts = intf::get_timestamp();
|
let cur_ts = intf::get_timestamp();
|
||||||
let node_count = {
|
let node_count = {
|
||||||
let c = self.config.get();
|
let c = self.unlocked_inner.config.get();
|
||||||
c.network.dht.max_find_node_count as usize
|
c.network.dht.max_find_node_count as usize
|
||||||
};
|
};
|
||||||
let out = self.find_peers_with_sort_and_filter(
|
let out = self.find_peers_with_sort_and_filter(
|
||||||
node_count,
|
node_count,
|
||||||
cur_ts,
|
cur_ts,
|
||||||
// filter
|
// filter
|
||||||
|k, v| filter.as_mut().map(|f| f(k, v)).unwrap_or(true),
|
filter,
|
||||||
// sort
|
// sort
|
||||||
|(a_key, a_entry), (b_key, b_entry)| {
|
|(a_key, a_entry), (b_key, b_entry)| {
|
||||||
// same nodes are always the same
|
// same nodes are always the same
|
||||||
@ -402,7 +393,7 @@ impl RoutingTable {
|
|||||||
let mut protocol_to_port =
|
let mut protocol_to_port =
|
||||||
BTreeMap::<(ProtocolType, AddressType), (LowLevelProtocolType, u16)>::new();
|
BTreeMap::<(ProtocolType, AddressType), (LowLevelProtocolType, u16)>::new();
|
||||||
let our_dids = self.all_filtered_dial_info_details(
|
let our_dids = self.all_filtered_dial_info_details(
|
||||||
Some(RoutingDomain::PublicInternet),
|
RoutingDomain::PublicInternet.into(),
|
||||||
&DialInfoFilter::all(),
|
&DialInfoFilter::all(),
|
||||||
);
|
);
|
||||||
for did in our_dids {
|
for did in our_dids {
|
||||||
@ -425,7 +416,7 @@ impl RoutingTable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_relay_node_filter(&self) -> impl Fn(&BucketEntryInner) -> bool {
|
fn make_public_internet_relay_node_filter(&self) -> impl Fn(&BucketEntryInner) -> bool {
|
||||||
// Get all our outbound protocol/address types
|
// Get all our outbound protocol/address types
|
||||||
let outbound_dif = self
|
let outbound_dif = self
|
||||||
.network_manager()
|
.network_manager()
|
||||||
@ -433,12 +424,8 @@ impl RoutingTable {
|
|||||||
let mapped_port_info = self.get_mapped_port_info();
|
let mapped_port_info = self.get_mapped_port_info();
|
||||||
|
|
||||||
move |e: &BucketEntryInner| {
|
move |e: &BucketEntryInner| {
|
||||||
// Ensure this node is not on our local network
|
// Ensure this node is not on the local network
|
||||||
let has_local_dial_info = e
|
if e.has_node_info(RoutingDomain::LocalNetwork.into()) {
|
||||||
.local_node_info()
|
|
||||||
.map(|l| l.has_dial_info())
|
|
||||||
.unwrap_or(false);
|
|
||||||
if has_local_dial_info {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -447,7 +434,7 @@ impl RoutingTable {
|
|||||||
let mut low_level_protocol_ports = mapped_port_info.low_level_protocol_ports.clone();
|
let mut low_level_protocol_ports = mapped_port_info.low_level_protocol_ports.clone();
|
||||||
|
|
||||||
let can_serve_as_relay = e
|
let can_serve_as_relay = e
|
||||||
.node_info()
|
.node_info(RoutingDomain::PublicInternet)
|
||||||
.map(|n| {
|
.map(|n| {
|
||||||
let dids =
|
let dids =
|
||||||
n.all_filtered_dial_info_details(|did| did.matches_filter(&outbound_dif));
|
n.all_filtered_dial_info_details(|did| did.matches_filter(&outbound_dif));
|
||||||
@ -471,9 +458,18 @@ impl RoutingTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "trace", skip(self), ret)]
|
#[instrument(level = "trace", skip(self), ret)]
|
||||||
pub fn find_inbound_relay(&self, cur_ts: u64) -> Option<NodeRef> {
|
pub fn find_inbound_relay(
|
||||||
|
&self,
|
||||||
|
routing_domain: RoutingDomain,
|
||||||
|
cur_ts: u64,
|
||||||
|
) -> Option<NodeRef> {
|
||||||
// Get relay filter function
|
// Get relay filter function
|
||||||
let relay_node_filter = self.make_relay_node_filter();
|
let relay_node_filter = match routing_domain {
|
||||||
|
RoutingDomain::PublicInternet => self.make_public_internet_relay_node_filter(),
|
||||||
|
RoutingDomain::LocalNetwork => {
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Go through all entries and find fastest entry that matches filter function
|
// Go through all entries and find fastest entry that matches filter function
|
||||||
let inner = self.inner.read();
|
let inner = self.inner.read();
|
||||||
@ -485,9 +481,9 @@ impl RoutingTable {
|
|||||||
let v2 = v.clone();
|
let v2 = v.clone();
|
||||||
v.with(|e| {
|
v.with(|e| {
|
||||||
// Ensure we have the node's status
|
// Ensure we have the node's status
|
||||||
if let Some(node_status) = e.peer_stats().status.clone() {
|
if let Some(node_status) = e.node_status(routing_domain) {
|
||||||
// Ensure the node will relay
|
// Ensure the node will relay
|
||||||
if node_status.will_relay {
|
if node_status.will_relay() {
|
||||||
// Compare against previous candidate
|
// Compare against previous candidate
|
||||||
if let Some(best_inbound_relay) = best_inbound_relay.as_mut() {
|
if let Some(best_inbound_relay) = best_inbound_relay.as_mut() {
|
||||||
// Less is faster
|
// Less is faster
|
||||||
@ -534,6 +530,7 @@ impl RoutingTable {
|
|||||||
|
|
||||||
// register the node if it's new
|
// register the node if it's new
|
||||||
if let Some(nr) = self.register_node_with_signed_node_info(
|
if let Some(nr) = self.register_node_with_signed_node_info(
|
||||||
|
RoutingDomain::PublicInternet,
|
||||||
p.node_id.key,
|
p.node_id.key,
|
||||||
p.signed_node_info.clone(),
|
p.signed_node_info.clone(),
|
||||||
false,
|
false,
|
||||||
@ -555,12 +552,7 @@ impl RoutingTable {
|
|||||||
let res = network_result_try!(
|
let res = network_result_try!(
|
||||||
rpc_processor
|
rpc_processor
|
||||||
.clone()
|
.clone()
|
||||||
.rpc_call_find_node(
|
.rpc_call_find_node(Destination::direct(node_ref), node_id)
|
||||||
Destination::Direct(node_ref.clone()),
|
|
||||||
node_id,
|
|
||||||
None,
|
|
||||||
rpc_processor.make_respond_to_sender(node_ref.clone()),
|
|
||||||
)
|
|
||||||
.await?
|
.await?
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -3,6 +3,8 @@ mod bucket_entry;
|
|||||||
mod debug;
|
mod debug;
|
||||||
mod find_nodes;
|
mod find_nodes;
|
||||||
mod node_ref;
|
mod node_ref;
|
||||||
|
mod routing_domain_editor;
|
||||||
|
mod routing_domains;
|
||||||
mod stats_accounting;
|
mod stats_accounting;
|
||||||
mod tasks;
|
mod tasks;
|
||||||
|
|
||||||
@ -15,78 +17,98 @@ use bucket::*;
|
|||||||
pub use bucket_entry::*;
|
pub use bucket_entry::*;
|
||||||
pub use debug::*;
|
pub use debug::*;
|
||||||
pub use find_nodes::*;
|
pub use find_nodes::*;
|
||||||
|
use hashlink::LruCache;
|
||||||
pub use node_ref::*;
|
pub use node_ref::*;
|
||||||
|
pub use routing_domain_editor::*;
|
||||||
|
pub use routing_domains::*;
|
||||||
pub use stats_accounting::*;
|
pub use stats_accounting::*;
|
||||||
|
|
||||||
|
const RECENT_PEERS_TABLE_SIZE: usize = 64;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum RoutingDomain {
|
pub struct RecentPeersEntry {
|
||||||
PublicInternet,
|
pub last_connection: ConnectionDescriptor,
|
||||||
LocalNetwork,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct RoutingDomainDetail {
|
|
||||||
dial_info_details: Vec<DialInfoDetail>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// RoutingTable rwlock-internal data
|
||||||
struct RoutingTableInner {
|
struct RoutingTableInner {
|
||||||
network_manager: NetworkManager,
|
/// Routing table buckets that hold entries
|
||||||
node_id: DHTKey, // The current node's public DHT key
|
buckets: Vec<Bucket>,
|
||||||
node_id_secret: DHTKeySecret, // The current node's DHT key secret
|
/// A fast counter for the number of entries in the table, total
|
||||||
|
bucket_entry_count: usize,
|
||||||
buckets: Vec<Bucket>, // Routing table buckets that hold entries
|
/// The public internet routing domain
|
||||||
kick_queue: BTreeSet<usize>, // Buckets to kick on our next kick task
|
public_internet_routing_domain: PublicInternetRoutingDomainDetail,
|
||||||
bucket_entry_count: usize, // A fast counter for the number of entries in the table, total
|
/// The dial info we use on the local network
|
||||||
|
local_network_routing_domain: LocalInternetRoutingDomainDetail,
|
||||||
public_internet_routing_domain: RoutingDomainDetail, // The dial info we use on the public internet
|
/// Interim accounting mechanism for this node's RPC latency to any other node
|
||||||
local_network_routing_domain: RoutingDomainDetail, // The dial info we use on the local network
|
self_latency_stats_accounting: LatencyStatsAccounting,
|
||||||
|
/// Interim accounting mechanism for the total bandwidth to/from this node
|
||||||
self_latency_stats_accounting: LatencyStatsAccounting, // Interim accounting mechanism for this node's RPC latency to any other node
|
self_transfer_stats_accounting: TransferStatsAccounting,
|
||||||
self_transfer_stats_accounting: TransferStatsAccounting, // Interim accounting mechanism for the total bandwidth to/from this node
|
/// Statistics about the total bandwidth to/from this node
|
||||||
self_transfer_stats: TransferStatsDownUp, // Statistics about the total bandwidth to/from this node
|
self_transfer_stats: TransferStatsDownUp,
|
||||||
|
/// Peers we have recently communicated with
|
||||||
|
recent_peers: LruCache<DHTKey, RecentPeersEntry>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct RoutingTableHealth {
|
pub struct RoutingTableHealth {
|
||||||
|
/// Number of reliable (responsive) entries in the routing table
|
||||||
pub reliable_entry_count: usize,
|
pub reliable_entry_count: usize,
|
||||||
|
/// Number of unreliable (occasionally unresponsive) entries in the routing table
|
||||||
pub unreliable_entry_count: usize,
|
pub unreliable_entry_count: usize,
|
||||||
|
/// Number of dead (always unresponsive) entries in the routing table
|
||||||
pub dead_entry_count: usize,
|
pub dead_entry_count: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RoutingTableUnlockedInner {
|
struct RoutingTableUnlockedInner {
|
||||||
// Background processes
|
// Accessors
|
||||||
|
config: VeilidConfig,
|
||||||
|
network_manager: NetworkManager,
|
||||||
|
|
||||||
|
/// The current node's public DHT key
|
||||||
|
node_id: DHTKey,
|
||||||
|
/// The current node's DHT key secret
|
||||||
|
node_id_secret: DHTKeySecret,
|
||||||
|
/// Buckets to kick on our next kick task
|
||||||
|
kick_queue: Mutex<BTreeSet<usize>>,
|
||||||
|
/// Background process for computing statistics
|
||||||
rolling_transfers_task: TickTask<EyreReport>,
|
rolling_transfers_task: TickTask<EyreReport>,
|
||||||
|
/// Backgroup process to purge dead routing table entries when necessary
|
||||||
kick_buckets_task: TickTask<EyreReport>,
|
kick_buckets_task: TickTask<EyreReport>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RoutingTable {
|
pub struct RoutingTable {
|
||||||
config: VeilidConfig,
|
|
||||||
inner: Arc<RwLock<RoutingTableInner>>,
|
inner: Arc<RwLock<RoutingTableInner>>,
|
||||||
unlocked_inner: Arc<RoutingTableUnlockedInner>,
|
unlocked_inner: Arc<RoutingTableUnlockedInner>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RoutingTable {
|
impl RoutingTable {
|
||||||
fn new_inner(network_manager: NetworkManager) -> RoutingTableInner {
|
fn new_inner() -> RoutingTableInner {
|
||||||
RoutingTableInner {
|
RoutingTableInner {
|
||||||
network_manager,
|
|
||||||
node_id: DHTKey::default(),
|
|
||||||
node_id_secret: DHTKeySecret::default(),
|
|
||||||
buckets: Vec::new(),
|
buckets: Vec::new(),
|
||||||
kick_queue: BTreeSet::default(),
|
public_internet_routing_domain: PublicInternetRoutingDomainDetail::default(),
|
||||||
public_internet_routing_domain: RoutingDomainDetail::default(),
|
local_network_routing_domain: LocalInternetRoutingDomainDetail::default(),
|
||||||
local_network_routing_domain: RoutingDomainDetail::default(),
|
|
||||||
bucket_entry_count: 0,
|
bucket_entry_count: 0,
|
||||||
self_latency_stats_accounting: LatencyStatsAccounting::new(),
|
self_latency_stats_accounting: LatencyStatsAccounting::new(),
|
||||||
self_transfer_stats_accounting: TransferStatsAccounting::new(),
|
self_transfer_stats_accounting: TransferStatsAccounting::new(),
|
||||||
self_transfer_stats: TransferStatsDownUp::default(),
|
self_transfer_stats: TransferStatsDownUp::default(),
|
||||||
|
recent_peers: LruCache::new(RECENT_PEERS_TABLE_SIZE),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn new_unlocked_inner(_config: VeilidConfig) -> RoutingTableUnlockedInner {
|
fn new_unlocked_inner(
|
||||||
//let c = config.get();
|
config: VeilidConfig,
|
||||||
|
network_manager: NetworkManager,
|
||||||
|
) -> RoutingTableUnlockedInner {
|
||||||
|
let c = config.get();
|
||||||
RoutingTableUnlockedInner {
|
RoutingTableUnlockedInner {
|
||||||
|
config: config.clone(),
|
||||||
|
network_manager,
|
||||||
|
node_id: c.network.node_id,
|
||||||
|
node_id_secret: c.network.node_id_secret,
|
||||||
|
kick_queue: Mutex::new(BTreeSet::default()),
|
||||||
rolling_transfers_task: TickTask::new(ROLLING_TRANSFERS_INTERVAL_SECS),
|
rolling_transfers_task: TickTask::new(ROLLING_TRANSFERS_INTERVAL_SECS),
|
||||||
kick_buckets_task: TickTask::new(1),
|
kick_buckets_task: TickTask::new(1),
|
||||||
}
|
}
|
||||||
@ -94,9 +116,8 @@ impl RoutingTable {
|
|||||||
pub fn new(network_manager: NetworkManager) -> Self {
|
pub fn new(network_manager: NetworkManager) -> Self {
|
||||||
let config = network_manager.config();
|
let config = network_manager.config();
|
||||||
let this = Self {
|
let this = Self {
|
||||||
config: config.clone(),
|
inner: Arc::new(RwLock::new(Self::new_inner())),
|
||||||
inner: Arc::new(RwLock::new(Self::new_inner(network_manager))),
|
unlocked_inner: Arc::new(Self::new_unlocked_inner(config, network_manager)),
|
||||||
unlocked_inner: Arc::new(Self::new_unlocked_inner(config)),
|
|
||||||
};
|
};
|
||||||
// Set rolling transfers tick task
|
// Set rolling transfers tick task
|
||||||
{
|
{
|
||||||
@ -121,23 +142,42 @@ impl RoutingTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn network_manager(&self) -> NetworkManager {
|
pub fn network_manager(&self) -> NetworkManager {
|
||||||
self.inner.read().network_manager.clone()
|
self.unlocked_inner.network_manager.clone()
|
||||||
}
|
}
|
||||||
pub fn rpc_processor(&self) -> RPCProcessor {
|
pub fn rpc_processor(&self) -> RPCProcessor {
|
||||||
self.network_manager().rpc_processor()
|
self.network_manager().rpc_processor()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn node_id(&self) -> DHTKey {
|
pub fn node_id(&self) -> DHTKey {
|
||||||
self.inner.read().node_id
|
self.unlocked_inner.node_id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn node_id_secret(&self) -> DHTKeySecret {
|
pub fn node_id_secret(&self) -> DHTKeySecret {
|
||||||
self.inner.read().node_id_secret
|
self.unlocked_inner.node_id_secret
|
||||||
|
}
|
||||||
|
|
||||||
|
fn routing_domain_for_address_inner(
|
||||||
|
inner: &RoutingTableInner,
|
||||||
|
address: Address,
|
||||||
|
) -> Option<RoutingDomain> {
|
||||||
|
for rd in RoutingDomain::all() {
|
||||||
|
let can_contain =
|
||||||
|
Self::with_routing_domain(inner, rd, |rdd| rdd.can_contain_address(address));
|
||||||
|
if can_contain {
|
||||||
|
return Some(rd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn routing_domain_for_address(&self, address: Address) -> Option<RoutingDomain> {
|
||||||
|
let inner = self.inner.read();
|
||||||
|
Self::routing_domain_for_address_inner(&*inner, address)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_routing_domain<F, R>(inner: &RoutingTableInner, domain: RoutingDomain, f: F) -> R
|
fn with_routing_domain<F, R>(inner: &RoutingTableInner, domain: RoutingDomain, f: F) -> R
|
||||||
where
|
where
|
||||||
F: FnOnce(&RoutingDomainDetail) -> R,
|
F: FnOnce(&dyn RoutingDomainDetail) -> R,
|
||||||
{
|
{
|
||||||
match domain {
|
match domain {
|
||||||
RoutingDomain::PublicInternet => f(&inner.public_internet_routing_domain),
|
RoutingDomain::PublicInternet => f(&inner.public_internet_routing_domain),
|
||||||
@ -151,7 +191,7 @@ impl RoutingTable {
|
|||||||
f: F,
|
f: F,
|
||||||
) -> R
|
) -> R
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut RoutingDomainDetail) -> R,
|
F: FnOnce(&mut dyn RoutingDomainDetail) -> R,
|
||||||
{
|
{
|
||||||
match domain {
|
match domain {
|
||||||
RoutingDomain::PublicInternet => f(&mut inner.public_internet_routing_domain),
|
RoutingDomain::PublicInternet => f(&mut inner.public_internet_routing_domain),
|
||||||
@ -159,71 +199,53 @@ impl RoutingTable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn relay_node(&self, domain: RoutingDomain) -> Option<NodeRef> {
|
||||||
|
let inner = self.inner.read();
|
||||||
|
Self::with_routing_domain(&*inner, domain, |rd| rd.relay_node())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn has_dial_info(&self, domain: RoutingDomain) -> bool {
|
pub fn has_dial_info(&self, domain: RoutingDomain) -> bool {
|
||||||
let inner = self.inner.read();
|
let inner = self.inner.read();
|
||||||
Self::with_routing_domain(&*inner, domain, |rd| !rd.dial_info_details.is_empty())
|
Self::with_routing_domain(&*inner, domain, |rd| !rd.dial_info_details().is_empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dial_info_details(&self, domain: RoutingDomain) -> Vec<DialInfoDetail> {
|
pub fn dial_info_details(&self, domain: RoutingDomain) -> Vec<DialInfoDetail> {
|
||||||
let inner = self.inner.read();
|
let inner = self.inner.read();
|
||||||
Self::with_routing_domain(&*inner, domain, |rd| rd.dial_info_details.clone())
|
Self::with_routing_domain(&*inner, domain, |rd| rd.dial_info_details().clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn first_filtered_dial_info_detail(
|
pub fn first_filtered_dial_info_detail(
|
||||||
&self,
|
&self,
|
||||||
domain: Option<RoutingDomain>,
|
routing_domain_set: RoutingDomainSet,
|
||||||
filter: &DialInfoFilter,
|
filter: &DialInfoFilter,
|
||||||
) -> Option<DialInfoDetail> {
|
) -> Option<DialInfoDetail> {
|
||||||
let inner = self.inner.read();
|
let inner = self.inner.read();
|
||||||
// Prefer local network first if it isn't filtered out
|
for routing_domain in routing_domain_set {
|
||||||
if domain == None || domain == Some(RoutingDomain::LocalNetwork) {
|
let did = Self::with_routing_domain(&*inner, routing_domain, |rd| {
|
||||||
Self::with_routing_domain(&*inner, RoutingDomain::LocalNetwork, |rd| {
|
for did in rd.dial_info_details() {
|
||||||
for did in &rd.dial_info_details {
|
|
||||||
if did.matches_filter(filter) {
|
if did.matches_filter(filter) {
|
||||||
return Some(did.clone());
|
return Some(did.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
})
|
});
|
||||||
} else {
|
if did.is_some() {
|
||||||
None
|
return did;
|
||||||
}
|
|
||||||
.or_else(|| {
|
|
||||||
if domain == None || domain == Some(RoutingDomain::PublicInternet) {
|
|
||||||
Self::with_routing_domain(&*inner, RoutingDomain::PublicInternet, |rd| {
|
|
||||||
for did in &rd.dial_info_details {
|
|
||||||
if did.matches_filter(filter) {
|
|
||||||
return Some(did.clone());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn all_filtered_dial_info_details(
|
pub fn all_filtered_dial_info_details(
|
||||||
&self,
|
&self,
|
||||||
domain: Option<RoutingDomain>,
|
routing_domain_set: RoutingDomainSet,
|
||||||
filter: &DialInfoFilter,
|
filter: &DialInfoFilter,
|
||||||
) -> Vec<DialInfoDetail> {
|
) -> Vec<DialInfoDetail> {
|
||||||
let inner = self.inner.read();
|
let inner = self.inner.read();
|
||||||
let mut ret = Vec::new();
|
let mut ret = Vec::new();
|
||||||
|
for routing_domain in routing_domain_set {
|
||||||
if domain == None || domain == Some(RoutingDomain::LocalNetwork) {
|
Self::with_routing_domain(&*inner, routing_domain, |rd| {
|
||||||
Self::with_routing_domain(&*inner, RoutingDomain::LocalNetwork, |rd| {
|
for did in rd.dial_info_details() {
|
||||||
for did in &rd.dial_info_details {
|
|
||||||
if did.matches_filter(filter) {
|
|
||||||
ret.push(did.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if domain == None || domain == Some(RoutingDomain::PublicInternet) {
|
|
||||||
Self::with_routing_domain(&*inner, RoutingDomain::PublicInternet, |rd| {
|
|
||||||
for did in &rd.dial_info_details {
|
|
||||||
if did.matches_filter(filter) {
|
if did.matches_filter(filter) {
|
||||||
ret.push(did.clone());
|
ret.push(did.clone());
|
||||||
}
|
}
|
||||||
@ -235,17 +257,13 @@ impl RoutingTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn ensure_dial_info_is_valid(&self, domain: RoutingDomain, dial_info: &DialInfo) -> bool {
|
pub fn ensure_dial_info_is_valid(&self, domain: RoutingDomain, dial_info: &DialInfo) -> bool {
|
||||||
let enable_local_peer_scope = {
|
let address = dial_info.socket_address().address();
|
||||||
let config = self.network_manager().config();
|
let inner = self.inner.read();
|
||||||
let c = config.get();
|
let can_contain_address =
|
||||||
c.network.enable_local_peer_scope
|
Self::with_routing_domain(&*inner, domain, |rd| rd.can_contain_address(address));
|
||||||
};
|
|
||||||
|
|
||||||
if !enable_local_peer_scope
|
if !can_contain_address {
|
||||||
&& matches!(domain, RoutingDomain::PublicInternet)
|
log_rtab!(debug "can not add dial info to this routing domain");
|
||||||
&& dial_info.is_local()
|
|
||||||
{
|
|
||||||
log_rtab!(debug "shouldn't be registering local addresses as public");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if !dial_info.is_valid() {
|
if !dial_info.is_valid() {
|
||||||
@ -258,59 +276,47 @@ impl RoutingTable {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "debug", skip(self), err)]
|
pub fn node_info_is_valid_in_routing_domain(
|
||||||
pub fn register_dial_info(
|
|
||||||
&self,
|
&self,
|
||||||
domain: RoutingDomain,
|
routing_domain: RoutingDomain,
|
||||||
dial_info: DialInfo,
|
node_info: &NodeInfo,
|
||||||
class: DialInfoClass,
|
) -> bool {
|
||||||
) -> EyreResult<()> {
|
// Should not be passing around nodeinfo with an invalid network class
|
||||||
if !self.ensure_dial_info_is_valid(domain, &dial_info) {
|
if matches!(node_info.network_class, NetworkClass::Invalid) {
|
||||||
return Err(eyre!("dial info is not valid"));
|
return false;
|
||||||
|
}
|
||||||
|
// Ensure all of the dial info works in this routing domain
|
||||||
|
for did in &node_info.dial_info_detail_list {
|
||||||
|
if !self.ensure_dial_info_is_valid(routing_domain, &did.dial_info) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Ensure the relay is also valid in this routing domain if it is provided
|
||||||
|
if let Some(relay_peer_info) = node_info.relay_peer_info.as_ref() {
|
||||||
|
let relay_ni = &relay_peer_info.signed_node_info.node_info;
|
||||||
|
if !self.node_info_is_valid_in_routing_domain(routing_domain, relay_ni) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut inner = self.inner.write();
|
#[instrument(level = "debug", skip(self))]
|
||||||
Self::with_routing_domain_mut(&mut *inner, domain, |rd| {
|
pub fn edit_routing_domain(&self, domain: RoutingDomain) -> RoutingDomainEditor {
|
||||||
rd.dial_info_details.push(DialInfoDetail {
|
RoutingDomainEditor::new(self.clone(), domain)
|
||||||
dial_info: dial_info.clone(),
|
|
||||||
class,
|
|
||||||
});
|
|
||||||
rd.dial_info_details.sort();
|
|
||||||
});
|
|
||||||
|
|
||||||
let domain_str = match domain {
|
|
||||||
RoutingDomain::PublicInternet => "Public",
|
|
||||||
RoutingDomain::LocalNetwork => "Local",
|
|
||||||
};
|
|
||||||
info!(
|
|
||||||
"{} Dial Info: {}",
|
|
||||||
domain_str,
|
|
||||||
NodeDialInfo {
|
|
||||||
node_id: NodeId::new(inner.node_id),
|
|
||||||
dial_info
|
|
||||||
}
|
|
||||||
.to_string(),
|
|
||||||
);
|
|
||||||
debug!(" Class: {:?}", class);
|
|
||||||
|
|
||||||
// Public dial info changed, go through all nodes and reset their 'seen our node info' bit
|
|
||||||
if matches!(domain, RoutingDomain::PublicInternet) {
|
|
||||||
Self::reset_all_seen_our_node_info(&*inner);
|
|
||||||
Self::reset_all_updated_since_last_network_change(&*inner);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
fn reset_all_seen_our_node_info(inner: &mut RoutingTableInner, routing_domain: RoutingDomain) {
|
||||||
}
|
|
||||||
|
|
||||||
fn reset_all_seen_our_node_info(inner: &RoutingTableInner) {
|
|
||||||
let cur_ts = intf::get_timestamp();
|
let cur_ts = intf::get_timestamp();
|
||||||
Self::with_entries(&*inner, cur_ts, BucketEntryState::Dead, |_, v| {
|
Self::with_entries(&*inner, cur_ts, BucketEntryState::Dead, |_, v| {
|
||||||
v.with_mut(|e| e.set_seen_our_node_info(false));
|
v.with_mut(|e| {
|
||||||
|
e.set_seen_our_node_info(routing_domain, false);
|
||||||
|
});
|
||||||
Option::<()>::None
|
Option::<()>::None
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset_all_updated_since_last_network_change(inner: &RoutingTableInner) {
|
fn reset_all_updated_since_last_network_change(inner: &mut RoutingTableInner) {
|
||||||
let cur_ts = intf::get_timestamp();
|
let cur_ts = intf::get_timestamp();
|
||||||
Self::with_entries(&*inner, cur_ts, BucketEntryState::Dead, |_, v| {
|
Self::with_entries(&*inner, cur_ts, BucketEntryState::Dead, |_, v| {
|
||||||
v.with_mut(|e| e.set_updated_since_last_network_change(false));
|
v.with_mut(|e| e.set_updated_since_last_network_change(false));
|
||||||
@ -318,18 +324,44 @@ impl RoutingTable {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_dial_info_details(&self, domain: RoutingDomain) {
|
pub fn get_own_peer_info(&self, routing_domain: RoutingDomain) -> PeerInfo {
|
||||||
trace!("clearing dial info domain: {:?}", domain);
|
PeerInfo::new(
|
||||||
|
NodeId::new(self.node_id()),
|
||||||
let mut inner = self.inner.write();
|
self.get_own_signed_node_info(routing_domain),
|
||||||
Self::with_routing_domain_mut(&mut *inner, domain, |rd| {
|
)
|
||||||
rd.dial_info_details.clear();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Public dial info changed, go through all nodes and reset their 'seen our node info' bit
|
|
||||||
if matches!(domain, RoutingDomain::PublicInternet) {
|
|
||||||
Self::reset_all_seen_our_node_info(&*inner);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_own_signed_node_info(&self, routing_domain: RoutingDomain) -> SignedNodeInfo {
|
||||||
|
let node_id = NodeId::new(self.node_id());
|
||||||
|
let secret = self.node_id_secret();
|
||||||
|
SignedNodeInfo::with_secret(self.get_own_node_info(routing_domain), node_id, &secret)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_own_node_info(&self, routing_domain: RoutingDomain) -> NodeInfo {
|
||||||
|
let netman = self.network_manager();
|
||||||
|
let relay_node = self.relay_node(routing_domain);
|
||||||
|
let pc = netman.get_protocol_config();
|
||||||
|
NodeInfo {
|
||||||
|
network_class: netman
|
||||||
|
.get_network_class(routing_domain)
|
||||||
|
.unwrap_or(NetworkClass::Invalid),
|
||||||
|
outbound_protocols: pc.outbound,
|
||||||
|
address_types: pc.family_global,
|
||||||
|
min_version: MIN_VERSION,
|
||||||
|
max_version: MAX_VERSION,
|
||||||
|
dial_info_detail_list: self.dial_info_details(routing_domain),
|
||||||
|
relay_peer_info: relay_node
|
||||||
|
.and_then(|rn| rn.make_peer_info(routing_domain).map(Box::new)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_valid_own_node_info(&self, routing_domain: RoutingDomain) -> bool {
|
||||||
|
let netman = self.network_manager();
|
||||||
|
let nc = netman
|
||||||
|
.get_network_class(routing_domain)
|
||||||
|
.unwrap_or(NetworkClass::Invalid);
|
||||||
|
!matches!(nc, NetworkClass::Invalid)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bucket_depth(index: usize) -> usize {
|
fn bucket_depth(index: usize) -> usize {
|
||||||
@ -357,11 +389,6 @@ impl RoutingTable {
|
|||||||
inner.buckets.push(bucket);
|
inner.buckets.push(bucket);
|
||||||
}
|
}
|
||||||
|
|
||||||
// make local copy of node id for easy access
|
|
||||||
let c = self.config.get();
|
|
||||||
inner.node_id = c.network.node_id;
|
|
||||||
inner.node_id_secret = c.network.node_id_secret;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -378,11 +405,33 @@ impl RoutingTable {
|
|||||||
error!("kick_buckets_task not stopped: {}", e);
|
error!("kick_buckets_task not stopped: {}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
*self.inner.write() = Self::new_inner(self.network_manager());
|
*self.inner.write() = Self::new_inner();
|
||||||
|
|
||||||
debug!("finished routing table terminate");
|
debug!("finished routing table terminate");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn configure_local_network_routing_domain(&self, local_networks: Vec<(IpAddr, IpAddr)>) {
|
||||||
|
log_net!(debug "configure_local_network_routing_domain: {:#?}", local_networks);
|
||||||
|
|
||||||
|
let mut inner = self.inner.write();
|
||||||
|
let changed = inner
|
||||||
|
.local_network_routing_domain
|
||||||
|
.set_local_networks(local_networks);
|
||||||
|
|
||||||
|
// If the local network topology has changed, nuke the existing local node info and let new local discovery happen
|
||||||
|
if changed {
|
||||||
|
let cur_ts = intf::get_timestamp();
|
||||||
|
Self::with_entries(&*inner, cur_ts, BucketEntryState::Dead, |_rti, e| {
|
||||||
|
e.with_mut(|e| {
|
||||||
|
e.clear_signed_node_info(RoutingDomain::LocalNetwork);
|
||||||
|
e.set_seen_our_node_info(RoutingDomain::LocalNetwork, false);
|
||||||
|
e.set_updated_since_last_network_change(false);
|
||||||
|
});
|
||||||
|
Option::<()>::None
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Attempt to empty the routing table
|
// Attempt to empty the routing table
|
||||||
// should only be performed when there are no node_refs (detached)
|
// should only be performed when there are no node_refs (detached)
|
||||||
pub fn purge_buckets(&self) {
|
pub fn purge_buckets(&self) {
|
||||||
@ -440,22 +489,34 @@ impl RoutingTable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_bucket_index(inner: &RoutingTableInner, node_id: DHTKey) -> usize {
|
fn find_bucket_index(&self, node_id: DHTKey) -> usize {
|
||||||
distance(&node_id, &inner.node_id)
|
distance(&node_id, &self.unlocked_inner.node_id)
|
||||||
.first_nonzero_bit()
|
.first_nonzero_bit()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_entry_count(&self, min_state: BucketEntryState) -> usize {
|
pub fn get_entry_count(
|
||||||
|
&self,
|
||||||
|
routing_domain_set: RoutingDomainSet,
|
||||||
|
min_state: BucketEntryState,
|
||||||
|
) -> usize {
|
||||||
let inner = self.inner.read();
|
let inner = self.inner.read();
|
||||||
Self::get_entry_count_inner(&*inner, min_state)
|
Self::get_entry_count_inner(&*inner, routing_domain_set, min_state)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_entry_count_inner(inner: &RoutingTableInner, min_state: BucketEntryState) -> usize {
|
fn get_entry_count_inner(
|
||||||
|
inner: &RoutingTableInner,
|
||||||
|
routing_domain_set: RoutingDomainSet,
|
||||||
|
min_state: BucketEntryState,
|
||||||
|
) -> usize {
|
||||||
let mut count = 0usize;
|
let mut count = 0usize;
|
||||||
let cur_ts = intf::get_timestamp();
|
let cur_ts = intf::get_timestamp();
|
||||||
Self::with_entries(inner, cur_ts, min_state, |_, _| {
|
Self::with_entries(inner, cur_ts, min_state, |_, e| {
|
||||||
|
if e.with(|e| e.best_routing_domain(routing_domain_set))
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
count += 1;
|
count += 1;
|
||||||
|
}
|
||||||
Option::<()>::None
|
Option::<()>::None
|
||||||
});
|
});
|
||||||
count
|
count
|
||||||
@ -479,13 +540,23 @@ impl RoutingTable {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_nodes_needing_updates(&self, cur_ts: u64, all: bool) -> Vec<NodeRef> {
|
pub fn get_nodes_needing_updates(
|
||||||
|
&self,
|
||||||
|
routing_domain: RoutingDomain,
|
||||||
|
cur_ts: u64,
|
||||||
|
all: bool,
|
||||||
|
) -> Vec<NodeRef> {
|
||||||
let inner = self.inner.read();
|
let inner = self.inner.read();
|
||||||
let mut node_refs = Vec::<NodeRef>::with_capacity(inner.bucket_entry_count);
|
let mut node_refs = Vec::<NodeRef>::with_capacity(inner.bucket_entry_count);
|
||||||
Self::with_entries(&*inner, cur_ts, BucketEntryState::Unreliable, |k, v| {
|
Self::with_entries(&*inner, cur_ts, BucketEntryState::Unreliable, |k, v| {
|
||||||
// Only update nodes that haven't seen our node info yet
|
// Only update nodes that haven't seen our node info yet
|
||||||
if all || !v.with(|e| e.has_seen_our_node_info()) {
|
if all || !v.with(|e| e.has_seen_our_node_info(routing_domain)) {
|
||||||
node_refs.push(NodeRef::new(self.clone(), k, v, None));
|
node_refs.push(NodeRef::new(
|
||||||
|
self.clone(),
|
||||||
|
k,
|
||||||
|
v,
|
||||||
|
Some(NodeRefFilter::new().with_routing_domain(routing_domain)),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
Option::<()>::None
|
Option::<()>::None
|
||||||
});
|
});
|
||||||
@ -494,15 +565,29 @@ impl RoutingTable {
|
|||||||
|
|
||||||
pub fn get_nodes_needing_ping(
|
pub fn get_nodes_needing_ping(
|
||||||
&self,
|
&self,
|
||||||
|
routing_domain: RoutingDomain,
|
||||||
cur_ts: u64,
|
cur_ts: u64,
|
||||||
relay_node_id: Option<DHTKey>,
|
|
||||||
) -> Vec<NodeRef> {
|
) -> Vec<NodeRef> {
|
||||||
let inner = self.inner.read();
|
let inner = self.inner.read();
|
||||||
|
|
||||||
|
// Collect relay nodes
|
||||||
|
let opt_relay_id = Self::with_routing_domain(&*inner, routing_domain, |rd| {
|
||||||
|
rd.relay_node().map(|rn| rn.node_id())
|
||||||
|
});
|
||||||
|
|
||||||
|
// Collect all entries that are 'needs_ping' and have some node info making them reachable somehow
|
||||||
let mut node_refs = Vec::<NodeRef>::with_capacity(inner.bucket_entry_count);
|
let mut node_refs = Vec::<NodeRef>::with_capacity(inner.bucket_entry_count);
|
||||||
Self::with_entries(&*inner, cur_ts, BucketEntryState::Unreliable, |k, v| {
|
Self::with_entries(&*inner, cur_ts, BucketEntryState::Unreliable, |k, v| {
|
||||||
// Only update nodes that haven't seen our node info yet
|
if v.with(|e| {
|
||||||
if v.with(|e| e.needs_ping(&k, cur_ts, relay_node_id)) {
|
e.has_node_info(routing_domain.into())
|
||||||
node_refs.push(NodeRef::new(self.clone(), k, v, None));
|
&& e.needs_ping(cur_ts, opt_relay_id == Some(k))
|
||||||
|
}) {
|
||||||
|
node_refs.push(NodeRef::new(
|
||||||
|
self.clone(),
|
||||||
|
k,
|
||||||
|
v,
|
||||||
|
Some(NodeRefFilter::new().with_routing_domain(routing_domain)),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
Option::<()>::None
|
Option::<()>::None
|
||||||
});
|
});
|
||||||
@ -520,9 +605,8 @@ impl RoutingTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn queue_bucket_kick(&self, node_id: DHTKey) {
|
fn queue_bucket_kick(&self, node_id: DHTKey) {
|
||||||
let mut inner = self.inner.write();
|
let idx = self.find_bucket_index(node_id);
|
||||||
let idx = Self::find_bucket_index(&*inner, node_id);
|
self.unlocked_inner.kick_queue.lock().insert(idx);
|
||||||
inner.kick_queue.insert(idx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a node reference, possibly creating a bucket entry
|
// Create a node reference, possibly creating a bucket entry
|
||||||
@ -542,7 +626,7 @@ impl RoutingTable {
|
|||||||
let mut inner = self.inner.write();
|
let mut inner = self.inner.write();
|
||||||
|
|
||||||
// Look up existing entry
|
// Look up existing entry
|
||||||
let idx = Self::find_bucket_index(&*inner, node_id);
|
let idx = self.find_bucket_index(node_id);
|
||||||
let noderef = {
|
let noderef = {
|
||||||
let bucket = &inner.buckets[idx];
|
let bucket = &inner.buckets[idx];
|
||||||
let entry = bucket.entry(&node_id);
|
let entry = bucket.entry(&node_id);
|
||||||
@ -563,8 +647,8 @@ impl RoutingTable {
|
|||||||
entry.with_mut(update_func);
|
entry.with_mut(update_func);
|
||||||
|
|
||||||
// Kick the bucket
|
// Kick the bucket
|
||||||
inner.kick_queue.insert(idx);
|
self.unlocked_inner.kick_queue.lock().insert(idx);
|
||||||
log_rtab!(debug "Routing table now has {} nodes, {} live", cnt, Self::get_entry_count_inner(&mut *inner, BucketEntryState::Unreliable));
|
log_rtab!(debug "Routing table now has {} nodes, {} live", cnt, Self::get_entry_count_inner(&mut *inner, RoutingDomainSet::all(), BucketEntryState::Unreliable));
|
||||||
|
|
||||||
nr
|
nr
|
||||||
}
|
}
|
||||||
@ -584,12 +668,12 @@ impl RoutingTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn lookup_node_ref(&self, node_id: DHTKey) -> Option<NodeRef> {
|
pub fn lookup_node_ref(&self, node_id: DHTKey) -> Option<NodeRef> {
|
||||||
let inner = self.inner.read();
|
if node_id == self.unlocked_inner.node_id {
|
||||||
if node_id == inner.node_id {
|
|
||||||
log_rtab!(debug "can't look up own node id in routing table");
|
log_rtab!(debug "can't look up own node id in routing table");
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let idx = Self::find_bucket_index(&*inner, node_id);
|
let idx = self.find_bucket_index(node_id);
|
||||||
|
let inner = self.inner.read();
|
||||||
let bucket = &inner.buckets[idx];
|
let bucket = &inner.buckets[idx];
|
||||||
bucket
|
bucket
|
||||||
.entry(&node_id)
|
.entry(&node_id)
|
||||||
@ -597,13 +681,17 @@ impl RoutingTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Shortcut function to add a node to our routing table if it doesn't exist
|
// Shortcut function to add a node to our routing table if it doesn't exist
|
||||||
// and add the dial info we have for it, since that's pretty common
|
// and add the dial info we have for it. Returns a noderef filtered to
|
||||||
|
// the routing domain in which this node was registered for convenience.
|
||||||
pub fn register_node_with_signed_node_info(
|
pub fn register_node_with_signed_node_info(
|
||||||
&self,
|
&self,
|
||||||
|
routing_domain: RoutingDomain,
|
||||||
node_id: DHTKey,
|
node_id: DHTKey,
|
||||||
signed_node_info: SignedNodeInfo,
|
signed_node_info: SignedNodeInfo,
|
||||||
allow_invalid_signature: bool,
|
allow_invalid: bool,
|
||||||
) -> Option<NodeRef> {
|
) -> Option<NodeRef> {
|
||||||
|
//log_rtab!("register_node_with_signed_node_info: routing_domain: {:?}, node_id: {:?}, signed_node_info: {:?}, allow_invalid: {:?}", routing_domain, node_id, signed_node_info, allow_invalid );
|
||||||
|
|
||||||
// validate signed node info is not something malicious
|
// validate signed node info is not something malicious
|
||||||
if node_id == self.node_id() {
|
if node_id == self.node_id() {
|
||||||
log_rtab!(debug "can't register own node id in routing table");
|
log_rtab!(debug "can't register own node id in routing table");
|
||||||
@ -615,9 +703,29 @@ impl RoutingTable {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !allow_invalid {
|
||||||
|
// verify signature
|
||||||
|
if !signed_node_info.has_valid_signature() {
|
||||||
|
log_rtab!(debug "signed node info for {} has invalid signature", node_id);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
// verify signed node info is valid in this routing domain
|
||||||
|
if !self
|
||||||
|
.node_info_is_valid_in_routing_domain(routing_domain, &signed_node_info.node_info)
|
||||||
|
{
|
||||||
|
log_rtab!(debug "signed node info for {} not valid in the {:?} routing domain", node_id, routing_domain);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.create_node_ref(node_id, |e| {
|
self.create_node_ref(node_id, |e| {
|
||||||
e.update_signed_node_info(signed_node_info, allow_invalid_signature);
|
e.update_signed_node_info(routing_domain, signed_node_info);
|
||||||
|
})
|
||||||
|
.map(|mut nr| {
|
||||||
|
nr.set_filter(Some(
|
||||||
|
NodeRefFilter::new().with_routing_domain(routing_domain),
|
||||||
|
));
|
||||||
|
nr
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -629,13 +737,15 @@ impl RoutingTable {
|
|||||||
descriptor: ConnectionDescriptor,
|
descriptor: ConnectionDescriptor,
|
||||||
timestamp: u64,
|
timestamp: u64,
|
||||||
) -> Option<NodeRef> {
|
) -> Option<NodeRef> {
|
||||||
self.create_node_ref(node_id, |e| {
|
let out = self.create_node_ref(node_id, |e| {
|
||||||
// set the most recent node address for connection finding and udp replies
|
|
||||||
e.set_last_connection(descriptor, timestamp);
|
|
||||||
|
|
||||||
// this node is live because it literally just connected to us
|
// this node is live because it literally just connected to us
|
||||||
e.touch_last_seen(timestamp);
|
e.touch_last_seen(timestamp);
|
||||||
})
|
});
|
||||||
|
if let Some(nr) = &out {
|
||||||
|
// set the most recent node address for connection finding and udp replies
|
||||||
|
nr.set_last_connection(descriptor, timestamp);
|
||||||
|
}
|
||||||
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ticks about once per second
|
// Ticks about once per second
|
||||||
@ -645,7 +755,7 @@ impl RoutingTable {
|
|||||||
self.unlocked_inner.rolling_transfers_task.tick().await?;
|
self.unlocked_inner.rolling_transfers_task.tick().await?;
|
||||||
|
|
||||||
// Kick buckets task
|
// Kick buckets task
|
||||||
let kick_bucket_queue_count = { self.inner.read().kick_queue.len() };
|
let kick_bucket_queue_count = self.unlocked_inner.kick_queue.lock().len();
|
||||||
if kick_bucket_queue_count > 0 {
|
if kick_bucket_queue_count > 0 {
|
||||||
self.unlocked_inner.kick_buckets_task.tick().await?;
|
self.unlocked_inner.kick_buckets_task.tick().await?;
|
||||||
}
|
}
|
||||||
@ -653,64 +763,6 @@ impl RoutingTable {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
// Stats Accounting
|
|
||||||
pub fn stats_question_sent(
|
|
||||||
&self,
|
|
||||||
node_ref: NodeRef,
|
|
||||||
ts: u64,
|
|
||||||
bytes: u64,
|
|
||||||
expects_answer: bool,
|
|
||||||
) {
|
|
||||||
self.inner
|
|
||||||
.write()
|
|
||||||
.self_transfer_stats_accounting
|
|
||||||
.add_up(bytes);
|
|
||||||
node_ref.operate_mut(|e| {
|
|
||||||
e.question_sent(ts, bytes, expects_answer);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
pub fn stats_question_rcvd(&self, node_ref: NodeRef, ts: u64, bytes: u64) {
|
|
||||||
self.inner
|
|
||||||
.write()
|
|
||||||
.self_transfer_stats_accounting
|
|
||||||
.add_down(bytes);
|
|
||||||
node_ref.operate_mut(|e| {
|
|
||||||
e.question_rcvd(ts, bytes);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
pub fn stats_answer_sent(&self, node_ref: NodeRef, bytes: u64) {
|
|
||||||
self.inner
|
|
||||||
.write()
|
|
||||||
.self_transfer_stats_accounting
|
|
||||||
.add_up(bytes);
|
|
||||||
node_ref.operate_mut(|e| {
|
|
||||||
e.answer_sent(bytes);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
pub fn stats_answer_rcvd(&self, node_ref: NodeRef, send_ts: u64, recv_ts: u64, bytes: u64) {
|
|
||||||
{
|
|
||||||
let mut inner = self.inner.write();
|
|
||||||
inner.self_transfer_stats_accounting.add_down(bytes);
|
|
||||||
inner
|
|
||||||
.self_latency_stats_accounting
|
|
||||||
.record_latency(recv_ts - send_ts);
|
|
||||||
}
|
|
||||||
node_ref.operate_mut(|e| {
|
|
||||||
e.answer_rcvd(send_ts, recv_ts, bytes);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
pub fn stats_question_lost(&self, node_ref: NodeRef) {
|
|
||||||
node_ref.operate_mut(|e| {
|
|
||||||
e.question_lost();
|
|
||||||
})
|
|
||||||
}
|
|
||||||
pub fn stats_failed_to_send(&self, node_ref: NodeRef, ts: u64, expects_answer: bool) {
|
|
||||||
node_ref.operate_mut(|e| {
|
|
||||||
e.failed_to_send(ts, expects_answer);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// Routing Table Health Metrics
|
// Routing Table Health Metrics
|
||||||
|
|
||||||
@ -735,4 +787,50 @@ impl RoutingTable {
|
|||||||
}
|
}
|
||||||
health
|
health
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_recent_peers(&self) -> Vec<(DHTKey, RecentPeersEntry)> {
|
||||||
|
let mut recent_peers = Vec::new();
|
||||||
|
let mut dead_peers = Vec::new();
|
||||||
|
let mut out = Vec::new();
|
||||||
|
|
||||||
|
// collect all recent peers
|
||||||
|
{
|
||||||
|
let inner = self.inner.read();
|
||||||
|
for (k, _v) in &inner.recent_peers {
|
||||||
|
recent_peers.push(*k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// look up each node and make sure the connection is still live
|
||||||
|
// (uses same logic as send_data, ensuring last_connection works for UDP)
|
||||||
|
for e in &recent_peers {
|
||||||
|
let mut dead = true;
|
||||||
|
if let Some(nr) = self.lookup_node_ref(*e) {
|
||||||
|
if let Some(last_connection) = nr.last_connection() {
|
||||||
|
out.push((*e, RecentPeersEntry { last_connection }));
|
||||||
|
dead = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if dead {
|
||||||
|
dead_peers.push(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// purge dead recent peers
|
||||||
|
{
|
||||||
|
let mut inner = self.inner.write();
|
||||||
|
for d in dead_peers {
|
||||||
|
inner.recent_peers.remove(d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn touch_recent_peer(&self, node_id: DHTKey, last_connection: ConnectionDescriptor) {
|
||||||
|
let mut inner = self.inner.write();
|
||||||
|
inner
|
||||||
|
.recent_peers
|
||||||
|
.insert(node_id, RecentPeersEntry { last_connection });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,71 @@ use alloc::fmt;
|
|||||||
// We should ping them with some frequency and 30 seconds is typical timeout
|
// We should ping them with some frequency and 30 seconds is typical timeout
|
||||||
const CONNECTIONLESS_TIMEOUT_SECS: u32 = 29;
|
const CONNECTIONLESS_TIMEOUT_SECS: u32 = 29;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct NodeRefFilter {
|
||||||
|
pub routing_domain_set: RoutingDomainSet,
|
||||||
|
pub dial_info_filter: DialInfoFilter,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for NodeRefFilter {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NodeRefFilter {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
routing_domain_set: RoutingDomainSet::all(),
|
||||||
|
dial_info_filter: DialInfoFilter::all(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_routing_domain(mut self, routing_domain: RoutingDomain) -> Self {
|
||||||
|
self.routing_domain_set = routing_domain.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn with_routing_domain_set(mut self, routing_domain_set: RoutingDomainSet) -> Self {
|
||||||
|
self.routing_domain_set = routing_domain_set;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn with_dial_info_filter(mut self, dial_info_filter: DialInfoFilter) -> Self {
|
||||||
|
self.dial_info_filter = dial_info_filter;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn with_protocol_type(mut self, protocol_type: ProtocolType) -> Self {
|
||||||
|
self.dial_info_filter = self.dial_info_filter.with_protocol_type(protocol_type);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn with_protocol_type_set(mut self, protocol_set: ProtocolTypeSet) -> Self {
|
||||||
|
self.dial_info_filter = self.dial_info_filter.with_protocol_type_set(protocol_set);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn with_address_type(mut self, address_type: AddressType) -> Self {
|
||||||
|
self.dial_info_filter = self.dial_info_filter.with_address_type(address_type);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn with_address_type_set(mut self, address_set: AddressTypeSet) -> Self {
|
||||||
|
self.dial_info_filter = self.dial_info_filter.with_address_type_set(address_set);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn filtered(mut self, other_filter: &NodeRefFilter) -> Self {
|
||||||
|
self.routing_domain_set &= other_filter.routing_domain_set;
|
||||||
|
self.dial_info_filter = self
|
||||||
|
.dial_info_filter
|
||||||
|
.filtered(&other_filter.dial_info_filter);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn is_dead(&self) -> bool {
|
||||||
|
self.dial_info_filter.is_dead() || self.routing_domain_set.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct NodeRef {
|
pub struct NodeRef {
|
||||||
routing_table: RoutingTable,
|
routing_table: RoutingTable,
|
||||||
node_id: DHTKey,
|
node_id: DHTKey,
|
||||||
entry: Arc<BucketEntry>,
|
entry: Arc<BucketEntry>,
|
||||||
filter: Option<DialInfoFilter>,
|
filter: Option<NodeRefFilter>,
|
||||||
#[cfg(feature = "tracking")]
|
#[cfg(feature = "tracking")]
|
||||||
track_id: usize,
|
track_id: usize,
|
||||||
}
|
}
|
||||||
@ -20,7 +80,7 @@ impl NodeRef {
|
|||||||
routing_table: RoutingTable,
|
routing_table: RoutingTable,
|
||||||
node_id: DHTKey,
|
node_id: DHTKey,
|
||||||
entry: Arc<BucketEntry>,
|
entry: Arc<BucketEntry>,
|
||||||
filter: Option<DialInfoFilter>,
|
filter: Option<NodeRefFilter>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
entry.ref_count.fetch_add(1u32, Ordering::Relaxed);
|
entry.ref_count.fetch_add(1u32, Ordering::Relaxed);
|
||||||
|
|
||||||
@ -34,31 +94,47 @@ impl NodeRef {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn node_id(&self) -> DHTKey {
|
// Operate on entry accessors
|
||||||
self.node_id
|
|
||||||
|
pub(super) fn operate<T, F>(&self, f: F) -> T
|
||||||
|
where
|
||||||
|
F: FnOnce(&RoutingTableInner, &BucketEntryInner) -> T,
|
||||||
|
{
|
||||||
|
let inner = &*self.routing_table.inner.read();
|
||||||
|
self.entry.with(|e| f(inner, e))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn filter_ref(&self) -> Option<&DialInfoFilter> {
|
pub(super) fn operate_mut<T, F>(&self, f: F) -> T
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut RoutingTableInner, &mut BucketEntryInner) -> T,
|
||||||
|
{
|
||||||
|
let inner = &mut *self.routing_table.inner.write();
|
||||||
|
self.entry.with_mut(|e| f(inner, e))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filtering
|
||||||
|
|
||||||
|
pub fn filter_ref(&self) -> Option<&NodeRefFilter> {
|
||||||
self.filter.as_ref()
|
self.filter.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn take_filter(&mut self) -> Option<DialInfoFilter> {
|
pub fn take_filter(&mut self) -> Option<NodeRefFilter> {
|
||||||
self.filter.take()
|
self.filter.take()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_filter(&mut self, filter: Option<DialInfoFilter>) {
|
pub fn set_filter(&mut self, filter: Option<NodeRefFilter>) {
|
||||||
self.filter = filter
|
self.filter = filter
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn merge_filter(&mut self, filter: DialInfoFilter) {
|
pub fn merge_filter(&mut self, filter: NodeRefFilter) {
|
||||||
if let Some(self_filter) = self.filter.take() {
|
if let Some(self_filter) = self.filter.take() {
|
||||||
self.filter = Some(self_filter.filtered(filter));
|
self.filter = Some(self_filter.filtered(&filter));
|
||||||
} else {
|
} else {
|
||||||
self.filter = Some(filter);
|
self.filter = Some(filter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn filtered_clone(&self, filter: DialInfoFilter) -> Self {
|
pub fn filtered_clone(&self, filter: NodeRefFilter) -> Self {
|
||||||
let mut out = self.clone();
|
let mut out = self.clone();
|
||||||
out.merge_filter(filter);
|
out.merge_filter(filter);
|
||||||
out
|
out
|
||||||
@ -72,70 +148,103 @@ impl NodeRef {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if some protocols can still pass the filter and false if no protocols remain
|
pub fn routing_domain_set(&self) -> RoutingDomainSet {
|
||||||
// pub fn filter_protocols(&mut self, protocol_set: ProtocolSet) -> bool {
|
self.filter
|
||||||
// if protocol_set != ProtocolSet::all() {
|
.as_ref()
|
||||||
// let mut dif = self.filter.clone().unwrap_or_default();
|
.map(|f| f.routing_domain_set)
|
||||||
// dif.protocol_set &= protocol_set;
|
.unwrap_or(RoutingDomainSet::all())
|
||||||
// self.filter = Some(dif);
|
|
||||||
// }
|
|
||||||
// self.filter
|
|
||||||
// .as_ref()
|
|
||||||
// .map(|f| !f.protocol_set.is_empty())
|
|
||||||
// .unwrap_or(true)
|
|
||||||
// }
|
|
||||||
|
|
||||||
pub fn operate<T, F>(&self, f: F) -> T
|
|
||||||
where
|
|
||||||
F: FnOnce(&BucketEntryInner) -> T,
|
|
||||||
{
|
|
||||||
self.entry.with(f)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn operate_mut<T, F>(&self, f: F) -> T
|
pub fn dial_info_filter(&self) -> DialInfoFilter {
|
||||||
where
|
self.filter
|
||||||
F: FnOnce(&mut BucketEntryInner) -> T,
|
.as_ref()
|
||||||
{
|
.map(|f| f.dial_info_filter.clone())
|
||||||
self.entry.with_mut(f)
|
.unwrap_or(DialInfoFilter::all())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn peer_info(&self) -> Option<PeerInfo> {
|
pub fn best_routing_domain(&self) -> Option<RoutingDomain> {
|
||||||
self.operate(|e| e.peer_info(self.node_id()))
|
self.operate(|_rti, e| {
|
||||||
|
e.best_routing_domain(
|
||||||
|
self.filter
|
||||||
|
.as_ref()
|
||||||
|
.map(|f| f.routing_domain_set)
|
||||||
|
.unwrap_or(RoutingDomainSet::all()),
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
pub fn has_seen_our_node_info(&self) -> bool {
|
|
||||||
self.operate(|e| e.has_seen_our_node_info())
|
// Accessors
|
||||||
|
pub fn routing_table(&self) -> RoutingTable {
|
||||||
|
self.routing_table.clone()
|
||||||
}
|
}
|
||||||
pub fn set_seen_our_node_info(&self) {
|
pub fn node_id(&self) -> DHTKey {
|
||||||
self.operate_mut(|e| e.set_seen_our_node_info(true));
|
self.node_id
|
||||||
}
|
}
|
||||||
pub fn has_updated_since_last_network_change(&self) -> bool {
|
pub fn has_updated_since_last_network_change(&self) -> bool {
|
||||||
self.operate(|e| e.has_updated_since_last_network_change())
|
self.operate(|_rti, e| e.has_updated_since_last_network_change())
|
||||||
}
|
}
|
||||||
pub fn set_updated_since_last_network_change(&self) {
|
pub fn set_updated_since_last_network_change(&self) {
|
||||||
self.operate_mut(|e| e.set_updated_since_last_network_change(true));
|
self.operate_mut(|_rti, e| e.set_updated_since_last_network_change(true));
|
||||||
}
|
}
|
||||||
pub fn network_class(&self) -> Option<NetworkClass> {
|
pub fn update_node_status(&self, node_status: NodeStatus) {
|
||||||
self.operate(|e| e.node_info().map(|n| n.network_class))
|
self.operate_mut(|_rti, e| {
|
||||||
|
e.update_node_status(node_status);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
pub fn outbound_protocols(&self) -> Option<ProtocolTypeSet> {
|
pub fn min_max_version(&self) -> Option<(u8, u8)> {
|
||||||
self.operate(|e| e.node_info().map(|n| n.outbound_protocols))
|
self.operate(|_rti, e| e.min_max_version())
|
||||||
}
|
}
|
||||||
pub fn address_types(&self) -> Option<AddressTypeSet> {
|
pub fn set_min_max_version(&self, min_max_version: (u8, u8)) {
|
||||||
self.operate(|e| e.node_info().map(|n| n.address_types))
|
self.operate_mut(|_rti, e| e.set_min_max_version(min_max_version))
|
||||||
}
|
}
|
||||||
pub fn node_info_outbound_filter(&self) -> DialInfoFilter {
|
pub fn state(&self, cur_ts: u64) -> BucketEntryState {
|
||||||
|
self.operate(|_rti, e| e.state(cur_ts))
|
||||||
|
}
|
||||||
|
pub fn peer_stats(&self) -> PeerStats {
|
||||||
|
self.operate(|_rti, e| e.peer_stats().clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Per-RoutingDomain accessors
|
||||||
|
pub fn make_peer_info(&self, routing_domain: RoutingDomain) -> Option<PeerInfo> {
|
||||||
|
self.operate(|_rti, e| e.make_peer_info(self.node_id(), routing_domain))
|
||||||
|
}
|
||||||
|
pub fn signed_node_info_has_valid_signature(&self, routing_domain: RoutingDomain) -> bool {
|
||||||
|
self.operate(|_rti, e| {
|
||||||
|
e.signed_node_info(routing_domain)
|
||||||
|
.map(|sni| sni.has_valid_signature())
|
||||||
|
.unwrap_or(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn has_seen_our_node_info(&self, routing_domain: RoutingDomain) -> bool {
|
||||||
|
self.operate(|_rti, e| e.has_seen_our_node_info(routing_domain))
|
||||||
|
}
|
||||||
|
pub fn set_seen_our_node_info(&self, routing_domain: RoutingDomain) {
|
||||||
|
self.operate_mut(|_rti, e| e.set_seen_our_node_info(routing_domain, true));
|
||||||
|
}
|
||||||
|
pub fn network_class(&self, routing_domain: RoutingDomain) -> Option<NetworkClass> {
|
||||||
|
self.operate(|_rt, e| e.node_info(routing_domain).map(|n| n.network_class))
|
||||||
|
}
|
||||||
|
pub fn outbound_protocols(&self, routing_domain: RoutingDomain) -> Option<ProtocolTypeSet> {
|
||||||
|
self.operate(|_rt, e| e.node_info(routing_domain).map(|n| n.outbound_protocols))
|
||||||
|
}
|
||||||
|
pub fn address_types(&self, routing_domain: RoutingDomain) -> Option<AddressTypeSet> {
|
||||||
|
self.operate(|_rt, e| e.node_info(routing_domain).map(|n| n.address_types))
|
||||||
|
}
|
||||||
|
pub fn node_info_outbound_filter(&self, routing_domain: RoutingDomain) -> DialInfoFilter {
|
||||||
let mut dif = DialInfoFilter::all();
|
let mut dif = DialInfoFilter::all();
|
||||||
if let Some(outbound_protocols) = self.outbound_protocols() {
|
if let Some(outbound_protocols) = self.outbound_protocols(routing_domain) {
|
||||||
dif = dif.with_protocol_type_set(outbound_protocols);
|
dif = dif.with_protocol_type_set(outbound_protocols);
|
||||||
}
|
}
|
||||||
if let Some(address_types) = self.address_types() {
|
if let Some(address_types) = self.address_types(routing_domain) {
|
||||||
dif = dif.with_address_type_set(address_types);
|
dif = dif.with_address_type_set(address_types);
|
||||||
}
|
}
|
||||||
dif
|
dif
|
||||||
}
|
}
|
||||||
|
pub fn relay(&self, routing_domain: RoutingDomain) -> Option<NodeRef> {
|
||||||
pub fn relay(&self) -> Option<NodeRef> {
|
let target_rpi = self.operate(|_rti, e| {
|
||||||
let target_rpi = self.operate(|e| e.node_info().map(|n| n.relay_peer_info))?;
|
e.node_info(routing_domain)
|
||||||
|
.map(|n| n.relay_peer_info.as_ref().map(|pi| pi.as_ref().clone()))
|
||||||
|
})?;
|
||||||
target_rpi.and_then(|t| {
|
target_rpi.and_then(|t| {
|
||||||
// If relay is ourselves, then return None, because we can't relay through ourselves
|
// If relay is ourselves, then return None, because we can't relay through ourselves
|
||||||
// and to contact this node we should have had an existing inbound connection
|
// and to contact this node we should have had an existing inbound connection
|
||||||
@ -144,87 +253,45 @@ impl NodeRef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Register relay node and return noderef
|
// Register relay node and return noderef
|
||||||
self.routing_table
|
self.routing_table.register_node_with_signed_node_info(
|
||||||
.register_node_with_signed_node_info(t.node_id.key, t.signed_node_info, false)
|
routing_domain,
|
||||||
.map(|mut nr| {
|
t.node_id.key,
|
||||||
nr.set_filter(self.filter_ref().cloned());
|
t.signed_node_info,
|
||||||
nr
|
false,
|
||||||
})
|
)
|
||||||
})
|
|
||||||
}
|
|
||||||
pub fn first_filtered_dial_info_detail(
|
|
||||||
&self,
|
|
||||||
routing_domain: Option<RoutingDomain>,
|
|
||||||
) -> Option<DialInfoDetail> {
|
|
||||||
self.operate(|e| {
|
|
||||||
// Prefer local dial info first unless it is filtered out
|
|
||||||
if routing_domain == None || routing_domain == Some(RoutingDomain::LocalNetwork) {
|
|
||||||
e.local_node_info().and_then(|l| {
|
|
||||||
l.first_filtered_dial_info(|di| {
|
|
||||||
if let Some(filter) = self.filter.as_ref() {
|
|
||||||
di.matches_filter(filter)
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.map(|di| DialInfoDetail {
|
|
||||||
class: DialInfoClass::Direct,
|
|
||||||
dial_info: di,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
.or_else(|| {
|
|
||||||
if routing_domain == None || routing_domain == Some(RoutingDomain::PublicInternet) {
|
|
||||||
e.node_info().and_then(|n| {
|
|
||||||
n.first_filtered_dial_info_detail(|did| {
|
|
||||||
if let Some(filter) = self.filter.as_ref() {
|
|
||||||
did.matches_filter(filter)
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn all_filtered_dial_info_details<F>(
|
// Filtered accessors
|
||||||
&self,
|
pub fn first_filtered_dial_info_detail(&self) -> Option<DialInfoDetail> {
|
||||||
routing_domain: Option<RoutingDomain>,
|
let routing_domain_set = self.routing_domain_set();
|
||||||
) -> Vec<DialInfoDetail> {
|
let dial_info_filter = self.dial_info_filter();
|
||||||
|
|
||||||
|
self.operate(|_rt, e| {
|
||||||
|
for routing_domain in routing_domain_set {
|
||||||
|
if let Some(ni) = e.node_info(routing_domain) {
|
||||||
|
let filter = |did: &DialInfoDetail| did.matches_filter(&dial_info_filter);
|
||||||
|
if let Some(did) = ni.first_filtered_dial_info_detail(filter) {
|
||||||
|
return Some(did);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn all_filtered_dial_info_details<F>(&self) -> Vec<DialInfoDetail> {
|
||||||
|
let routing_domain_set = self.routing_domain_set();
|
||||||
|
let dial_info_filter = self.dial_info_filter();
|
||||||
|
|
||||||
let mut out = Vec::new();
|
let mut out = Vec::new();
|
||||||
self.operate(|e| {
|
self.operate(|_rt, e| {
|
||||||
// Prefer local dial info first unless it is filtered out
|
for routing_domain in routing_domain_set {
|
||||||
if routing_domain == None || routing_domain == Some(RoutingDomain::LocalNetwork) {
|
if let Some(ni) = e.node_info(routing_domain) {
|
||||||
if let Some(lni) = e.local_node_info() {
|
let filter = |did: &DialInfoDetail| did.matches_filter(&dial_info_filter);
|
||||||
for di in lni.all_filtered_dial_info(|di| {
|
if let Some(did) = ni.first_filtered_dial_info_detail(filter) {
|
||||||
if let Some(filter) = self.filter.as_ref() {
|
out.push(did);
|
||||||
di.matches_filter(filter)
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
}) {
|
|
||||||
out.push(DialInfoDetail {
|
|
||||||
class: DialInfoClass::Direct,
|
|
||||||
dial_info: di,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if routing_domain == None || routing_domain == Some(RoutingDomain::PublicInternet) {
|
|
||||||
if let Some(ni) = e.node_info() {
|
|
||||||
out.append(&mut ni.all_filtered_dial_info_details(|did| {
|
|
||||||
if let Some(filter) = self.filter.as_ref() {
|
|
||||||
did.matches_filter(filter)
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -232,16 +299,16 @@ impl NodeRef {
|
|||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn last_connection(&self) -> Option<ConnectionDescriptor> {
|
pub fn last_connection(&self) -> Option<ConnectionDescriptor> {
|
||||||
// Get the last connection and the last time we saw anything with this connection
|
// Get the last connection and the last time we saw anything with this connection
|
||||||
let (last_connection, last_seen) =
|
let (last_connection, last_seen) =
|
||||||
self.operate(|e| e.last_connection(self.filter.clone()))?;
|
self.operate(|rti, e| e.last_connection(rti, self.filter.clone()))?;
|
||||||
|
|
||||||
// Should we check the connection table?
|
// Should we check the connection table?
|
||||||
if last_connection.protocol_type().is_connection_oriented() {
|
if last_connection.protocol_type().is_connection_oriented() {
|
||||||
// Look the connection up in the connection manager and see if it's still there
|
// Look the connection up in the connection manager and see if it's still there
|
||||||
let connection_manager = self.routing_table.network_manager().connection_manager();
|
let connection_manager = self.routing_table.network_manager().connection_manager();
|
||||||
connection_manager.get_connection(last_connection).await?;
|
connection_manager.get_connection(last_connection)?;
|
||||||
} else {
|
} else {
|
||||||
// If this is not connection oriented, then we check our last seen time
|
// If this is not connection oriented, then we check our last seen time
|
||||||
// to see if this mapping has expired (beyond our timeout)
|
// to see if this mapping has expired (beyond our timeout)
|
||||||
@ -254,21 +321,62 @@ impl NodeRef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_last_connections(&self) {
|
pub fn clear_last_connections(&self) {
|
||||||
self.operate_mut(|e| e.clear_last_connections())
|
self.operate_mut(|_rti, e| e.clear_last_connections())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_last_connection(&self, connection_descriptor: ConnectionDescriptor, ts: u64) {
|
pub fn set_last_connection(&self, connection_descriptor: ConnectionDescriptor, ts: u64) {
|
||||||
self.operate_mut(|e| e.set_last_connection(connection_descriptor, ts))
|
self.operate_mut(|_rti, e| e.set_last_connection(connection_descriptor, ts));
|
||||||
|
self.routing_table
|
||||||
|
.touch_recent_peer(self.node_id(), connection_descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_any_dial_info(&self) -> bool {
|
pub fn has_any_dial_info(&self) -> bool {
|
||||||
self.operate(|e| {
|
self.operate(|_rti, e| {
|
||||||
e.node_info()
|
for rtd in RoutingDomain::all() {
|
||||||
.map(|n| n.has_any_dial_info())
|
if let Some(ni) = e.node_info(rtd) {
|
||||||
.unwrap_or(false)
|
if ni.has_any_dial_info() {
|
||||||
|| e.local_node_info()
|
return true;
|
||||||
.map(|l| l.has_dial_info())
|
}
|
||||||
.unwrap_or(false)
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stats_question_sent(&self, ts: u64, bytes: u64, expects_answer: bool) {
|
||||||
|
self.operate_mut(|rti, e| {
|
||||||
|
rti.self_transfer_stats_accounting.add_up(bytes);
|
||||||
|
e.question_sent(ts, bytes, expects_answer);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn stats_question_rcvd(&self, ts: u64, bytes: u64) {
|
||||||
|
self.operate_mut(|rti, e| {
|
||||||
|
rti.self_transfer_stats_accounting.add_down(bytes);
|
||||||
|
e.question_rcvd(ts, bytes);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn stats_answer_sent(&self, bytes: u64) {
|
||||||
|
self.operate_mut(|rti, e| {
|
||||||
|
rti.self_transfer_stats_accounting.add_up(bytes);
|
||||||
|
e.answer_sent(bytes);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn stats_answer_rcvd(&self, send_ts: u64, recv_ts: u64, bytes: u64) {
|
||||||
|
self.operate_mut(|rti, e| {
|
||||||
|
rti.self_transfer_stats_accounting.add_down(bytes);
|
||||||
|
rti.self_latency_stats_accounting
|
||||||
|
.record_latency(recv_ts - send_ts);
|
||||||
|
e.answer_rcvd(send_ts, recv_ts, bytes);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn stats_question_lost(&self) {
|
||||||
|
self.operate_mut(|_rti, e| {
|
||||||
|
e.question_lost();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn stats_failed_to_send(&self, ts: u64, expects_answer: bool) {
|
||||||
|
self.operate_mut(|_rti, e| {
|
||||||
|
e.failed_to_send(ts, expects_answer);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
130
veilid-core/src/routing_table/routing_domain_editor.rs
Normal file
130
veilid-core/src/routing_table/routing_domain_editor.rs
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
enum RoutingDomainChange {
|
||||||
|
ClearDialInfoDetails,
|
||||||
|
ClearRelayNode,
|
||||||
|
SetRelayNode { relay_node: NodeRef },
|
||||||
|
AddDialInfoDetail { dial_info_detail: DialInfoDetail },
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RoutingDomainEditor {
|
||||||
|
routing_table: RoutingTable,
|
||||||
|
routing_domain: RoutingDomain,
|
||||||
|
changes: Vec<RoutingDomainChange>,
|
||||||
|
send_node_info_updates: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RoutingDomainEditor {
|
||||||
|
pub(super) fn new(routing_table: RoutingTable, routing_domain: RoutingDomain) -> Self {
|
||||||
|
Self {
|
||||||
|
routing_table,
|
||||||
|
routing_domain,
|
||||||
|
changes: Vec::new(),
|
||||||
|
send_node_info_updates: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[instrument(level = "debug", skip(self))]
|
||||||
|
pub fn disable_node_info_updates(&mut self) {
|
||||||
|
self.send_node_info_updates = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "debug", skip(self))]
|
||||||
|
pub fn clear_dial_info_details(&mut self) {
|
||||||
|
self.changes.push(RoutingDomainChange::ClearDialInfoDetails);
|
||||||
|
}
|
||||||
|
#[instrument(level = "debug", skip(self))]
|
||||||
|
pub fn clear_relay_node(&mut self) {
|
||||||
|
self.changes.push(RoutingDomainChange::ClearRelayNode);
|
||||||
|
}
|
||||||
|
#[instrument(level = "debug", skip(self))]
|
||||||
|
pub fn set_relay_node(&mut self, relay_node: NodeRef) {
|
||||||
|
self.changes
|
||||||
|
.push(RoutingDomainChange::SetRelayNode { relay_node })
|
||||||
|
}
|
||||||
|
#[instrument(level = "debug", skip(self), err)]
|
||||||
|
pub fn register_dial_info(
|
||||||
|
&mut self,
|
||||||
|
dial_info: DialInfo,
|
||||||
|
class: DialInfoClass,
|
||||||
|
) -> EyreResult<()> {
|
||||||
|
if !self
|
||||||
|
.routing_table
|
||||||
|
.ensure_dial_info_is_valid(self.routing_domain, &dial_info)
|
||||||
|
{
|
||||||
|
return Err(eyre!(
|
||||||
|
"dial info '{}' is not valid in routing domain '{:?}'",
|
||||||
|
dial_info,
|
||||||
|
self.routing_domain
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.changes.push(RoutingDomainChange::AddDialInfoDetail {
|
||||||
|
dial_info_detail: DialInfoDetail {
|
||||||
|
dial_info: dial_info.clone(),
|
||||||
|
class,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "debug", skip(self))]
|
||||||
|
pub async fn commit(self) {
|
||||||
|
let mut changed = false;
|
||||||
|
{
|
||||||
|
let node_id = self.routing_table.node_id();
|
||||||
|
|
||||||
|
let mut inner = self.routing_table.inner.write();
|
||||||
|
let inner = &mut *inner;
|
||||||
|
RoutingTable::with_routing_domain_mut(inner, self.routing_domain, |detail| {
|
||||||
|
for change in self.changes {
|
||||||
|
match change {
|
||||||
|
RoutingDomainChange::ClearDialInfoDetails => {
|
||||||
|
debug!("[{:?}] cleared dial info details", self.routing_domain);
|
||||||
|
detail.clear_dial_info_details();
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
RoutingDomainChange::ClearRelayNode => {
|
||||||
|
debug!("[{:?}] cleared relay node", self.routing_domain);
|
||||||
|
detail.set_relay_node(None);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
RoutingDomainChange::SetRelayNode { relay_node } => {
|
||||||
|
debug!("[{:?}] set relay node: {}", self.routing_domain, relay_node);
|
||||||
|
detail.set_relay_node(Some(relay_node));
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
RoutingDomainChange::AddDialInfoDetail { dial_info_detail } => {
|
||||||
|
debug!(
|
||||||
|
"[{:?}] add dial info detail: {:?}",
|
||||||
|
self.routing_domain, dial_info_detail
|
||||||
|
);
|
||||||
|
detail.add_dial_info_detail(dial_info_detail.clone());
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"{:?} Dial Info: {}",
|
||||||
|
self.routing_domain,
|
||||||
|
NodeDialInfo {
|
||||||
|
node_id: NodeId::new(node_id),
|
||||||
|
dial_info: dial_info_detail.dial_info
|
||||||
|
}
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if changed {
|
||||||
|
RoutingTable::reset_all_seen_our_node_info(inner, self.routing_domain);
|
||||||
|
RoutingTable::reset_all_updated_since_last_network_change(inner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if changed && self.send_node_info_updates {
|
||||||
|
let network_manager = self.routing_table.unlocked_inner.network_manager.clone();
|
||||||
|
network_manager
|
||||||
|
.send_node_info_updates(self.routing_domain, true)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
98
veilid-core/src/routing_table/routing_domains.rs
Normal file
98
veilid-core/src/routing_table/routing_domains.rs
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// General trait for all routing domains
|
||||||
|
pub trait RoutingDomainDetail {
|
||||||
|
fn can_contain_address(&self, address: Address) -> bool;
|
||||||
|
fn relay_node(&self) -> Option<NodeRef>;
|
||||||
|
fn set_relay_node(&mut self, opt_relay_node: Option<NodeRef>);
|
||||||
|
fn dial_info_details(&self) -> &Vec<DialInfoDetail>;
|
||||||
|
fn clear_dial_info_details(&mut self);
|
||||||
|
fn add_dial_info_detail(&mut self, did: DialInfoDetail);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Public Internet routing domain internals
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct PublicInternetRoutingDomainDetail {
|
||||||
|
/// An optional node we relay through for this domain
|
||||||
|
relay_node: Option<NodeRef>,
|
||||||
|
/// The dial infos on this domain we can be reached by
|
||||||
|
dial_info_details: Vec<DialInfoDetail>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RoutingDomainDetail for PublicInternetRoutingDomainDetail {
|
||||||
|
fn can_contain_address(&self, address: Address) -> bool {
|
||||||
|
address.is_global()
|
||||||
|
}
|
||||||
|
fn relay_node(&self) -> Option<NodeRef> {
|
||||||
|
self.relay_node.clone()
|
||||||
|
}
|
||||||
|
fn set_relay_node(&mut self, opt_relay_node: Option<NodeRef>) {
|
||||||
|
self.relay_node = opt_relay_node.map(|nr| {
|
||||||
|
nr.filtered_clone(
|
||||||
|
NodeRefFilter::new().with_routing_domain(RoutingDomain::PublicInternet),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn dial_info_details(&self) -> &Vec<DialInfoDetail> {
|
||||||
|
&self.dial_info_details
|
||||||
|
}
|
||||||
|
fn clear_dial_info_details(&mut self) {
|
||||||
|
self.dial_info_details.clear();
|
||||||
|
}
|
||||||
|
fn add_dial_info_detail(&mut self, did: DialInfoDetail) {
|
||||||
|
self.dial_info_details.push(did);
|
||||||
|
self.dial_info_details.sort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Local Network routing domain internals
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct LocalInternetRoutingDomainDetail {
|
||||||
|
/// An optional node we relay through for this domain
|
||||||
|
relay_node: Option<NodeRef>,
|
||||||
|
/// The dial infos on this domain we can be reached by
|
||||||
|
dial_info_details: Vec<DialInfoDetail>,
|
||||||
|
/// The local networks this domain will communicate with
|
||||||
|
local_networks: Vec<(IpAddr, IpAddr)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LocalInternetRoutingDomainDetail {
|
||||||
|
pub fn set_local_networks(&mut self, mut local_networks: Vec<(IpAddr, IpAddr)>) -> bool {
|
||||||
|
local_networks.sort();
|
||||||
|
if local_networks == self.local_networks {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
self.local_networks = local_networks;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RoutingDomainDetail for LocalInternetRoutingDomainDetail {
|
||||||
|
fn can_contain_address(&self, address: Address) -> bool {
|
||||||
|
let ip = address.to_ip_addr();
|
||||||
|
for localnet in &self.local_networks {
|
||||||
|
if ipaddr_in_network(ip, localnet.0, localnet.1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
fn relay_node(&self) -> Option<NodeRef> {
|
||||||
|
self.relay_node.clone()
|
||||||
|
}
|
||||||
|
fn set_relay_node(&mut self, opt_relay_node: Option<NodeRef>) {
|
||||||
|
self.relay_node = opt_relay_node.map(|nr| {
|
||||||
|
nr.filtered_clone(NodeRefFilter::new().with_routing_domain(RoutingDomain::LocalNetwork))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
fn dial_info_details(&self) -> &Vec<DialInfoDetail> {
|
||||||
|
&self.dial_info_details
|
||||||
|
}
|
||||||
|
fn clear_dial_info_details(&mut self) {
|
||||||
|
self.dial_info_details.clear();
|
||||||
|
}
|
||||||
|
fn add_dial_info_detail(&mut self, did: DialInfoDetail) {
|
||||||
|
self.dial_info_details.push(did);
|
||||||
|
self.dial_info_details.sort();
|
||||||
|
}
|
||||||
|
}
|
@ -37,9 +37,10 @@ impl RoutingTable {
|
|||||||
_last_ts: u64,
|
_last_ts: u64,
|
||||||
cur_ts: u64,
|
cur_ts: u64,
|
||||||
) -> EyreResult<()> {
|
) -> EyreResult<()> {
|
||||||
|
let kick_queue: Vec<usize> = core::mem::take(&mut *self.unlocked_inner.kick_queue.lock())
|
||||||
|
.into_iter()
|
||||||
|
.collect();
|
||||||
let mut inner = self.inner.write();
|
let mut inner = self.inner.write();
|
||||||
let kick_queue: Vec<usize> = inner.kick_queue.iter().map(|v| *v).collect();
|
|
||||||
inner.kick_queue.clear();
|
|
||||||
for idx in kick_queue {
|
for idx in kick_queue {
|
||||||
Self::kick_bucket(&mut *inner, idx)
|
Self::kick_bucket(&mut *inner, idx)
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
use crate::*;
|
use crate::*;
|
||||||
use rpc_processor::*;
|
use rpc_processor::*;
|
||||||
|
|
||||||
pub fn encode_node_status(
|
pub fn encode_public_internet_node_status(
|
||||||
node_status: &NodeStatus,
|
public_internet_node_status: &PublicInternetNodeStatus,
|
||||||
builder: &mut veilid_capnp::node_status::Builder,
|
builder: &mut veilid_capnp::public_internet_node_status::Builder,
|
||||||
) -> Result<(), RPCError> {
|
) -> Result<(), RPCError> {
|
||||||
builder.set_will_route(node_status.will_route);
|
builder.set_will_route(public_internet_node_status.will_route);
|
||||||
builder.set_will_tunnel(node_status.will_tunnel);
|
builder.set_will_tunnel(public_internet_node_status.will_tunnel);
|
||||||
builder.set_will_signal(node_status.will_signal);
|
builder.set_will_signal(public_internet_node_status.will_signal);
|
||||||
builder.set_will_relay(node_status.will_relay);
|
builder.set_will_relay(public_internet_node_status.will_relay);
|
||||||
builder.set_will_validate_dial_info(node_status.will_validate_dial_info);
|
builder.set_will_validate_dial_info(public_internet_node_status.will_validate_dial_info);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decode_node_status(
|
pub fn decode_public_internet_node_status(
|
||||||
reader: &veilid_capnp::node_status::Reader,
|
reader: &veilid_capnp::public_internet_node_status::Reader,
|
||||||
) -> Result<NodeStatus, RPCError> {
|
) -> Result<PublicInternetNodeStatus, RPCError> {
|
||||||
Ok(NodeStatus {
|
Ok(PublicInternetNodeStatus {
|
||||||
will_route: reader.reborrow().get_will_route(),
|
will_route: reader.reborrow().get_will_route(),
|
||||||
will_tunnel: reader.reborrow().get_will_tunnel(),
|
will_tunnel: reader.reborrow().get_will_tunnel(),
|
||||||
will_signal: reader.reborrow().get_will_signal(),
|
will_signal: reader.reborrow().get_will_signal(),
|
||||||
@ -25,3 +25,60 @@ pub fn decode_node_status(
|
|||||||
will_validate_dial_info: reader.reborrow().get_will_validate_dial_info(),
|
will_validate_dial_info: reader.reborrow().get_will_validate_dial_info(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn encode_local_network_node_status(
|
||||||
|
local_network_node_status: &LocalNetworkNodeStatus,
|
||||||
|
builder: &mut veilid_capnp::local_network_node_status::Builder,
|
||||||
|
) -> Result<(), RPCError> {
|
||||||
|
builder.set_will_relay(local_network_node_status.will_relay);
|
||||||
|
builder.set_will_validate_dial_info(local_network_node_status.will_validate_dial_info);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decode_local_network_node_status(
|
||||||
|
reader: &veilid_capnp::local_network_node_status::Reader,
|
||||||
|
) -> Result<LocalNetworkNodeStatus, RPCError> {
|
||||||
|
Ok(LocalNetworkNodeStatus {
|
||||||
|
will_relay: reader.reborrow().get_will_relay(),
|
||||||
|
will_validate_dial_info: reader.reborrow().get_will_validate_dial_info(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode_node_status(
|
||||||
|
node_status: &NodeStatus,
|
||||||
|
builder: &mut veilid_capnp::node_status::Builder,
|
||||||
|
) -> Result<(), RPCError> {
|
||||||
|
match node_status {
|
||||||
|
NodeStatus::PublicInternet(ns) => {
|
||||||
|
let mut pi_builder = builder.reborrow().init_public_internet();
|
||||||
|
encode_public_internet_node_status(&ns, &mut pi_builder)
|
||||||
|
}
|
||||||
|
NodeStatus::LocalNetwork(ns) => {
|
||||||
|
let mut ln_builder = builder.reborrow().init_local_network();
|
||||||
|
encode_local_network_node_status(&ns, &mut ln_builder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decode_node_status(
|
||||||
|
reader: &veilid_capnp::node_status::Reader,
|
||||||
|
) -> Result<NodeStatus, RPCError> {
|
||||||
|
Ok(
|
||||||
|
match reader
|
||||||
|
.which()
|
||||||
|
.map_err(RPCError::map_internal("invalid node status"))?
|
||||||
|
{
|
||||||
|
veilid_capnp::node_status::PublicInternet(pi) => {
|
||||||
|
let r = pi.map_err(RPCError::protocol)?;
|
||||||
|
let pins = decode_public_internet_node_status(&r)?;
|
||||||
|
NodeStatus::PublicInternet(pins)
|
||||||
|
}
|
||||||
|
veilid_capnp::node_status::LocalNetwork(ln) => {
|
||||||
|
let r = ln.map_err(RPCError::protocol)?;
|
||||||
|
let lnns = decode_local_network_node_status(&r)?;
|
||||||
|
NodeStatus::LocalNetwork(lnns)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -25,7 +25,7 @@ impl RPCOperationKind {
|
|||||||
let out = match which_reader {
|
let out = match which_reader {
|
||||||
veilid_capnp::operation::kind::Which::Question(r) => {
|
veilid_capnp::operation::kind::Which::Question(r) => {
|
||||||
let q_reader = r.map_err(RPCError::protocol)?;
|
let q_reader = r.map_err(RPCError::protocol)?;
|
||||||
let out = RPCQuestion::decode(&q_reader, sender_node_id)?;
|
let out = RPCQuestion::decode(&q_reader)?;
|
||||||
RPCOperationKind::Question(out)
|
RPCOperationKind::Question(out)
|
||||||
}
|
}
|
||||||
veilid_capnp::operation::kind::Which::Statement(r) => {
|
veilid_capnp::operation::kind::Which::Statement(r) => {
|
||||||
@ -58,26 +58,37 @@ impl RPCOperationKind {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct RPCOperation {
|
pub struct RPCOperation {
|
||||||
op_id: u64,
|
op_id: u64,
|
||||||
|
sender_node_info: Option<SignedNodeInfo>,
|
||||||
kind: RPCOperationKind,
|
kind: RPCOperationKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RPCOperation {
|
impl RPCOperation {
|
||||||
pub fn new_question(question: RPCQuestion) -> Self {
|
pub fn new_question(question: RPCQuestion, sender_node_info: Option<SignedNodeInfo>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
op_id: intf::get_random_u64(),
|
op_id: intf::get_random_u64(),
|
||||||
|
sender_node_info,
|
||||||
kind: RPCOperationKind::Question(question),
|
kind: RPCOperationKind::Question(question),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn new_statement(statement: RPCStatement) -> Self {
|
pub fn new_statement(
|
||||||
|
statement: RPCStatement,
|
||||||
|
sender_node_info: Option<SignedNodeInfo>,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
op_id: intf::get_random_u64(),
|
op_id: intf::get_random_u64(),
|
||||||
|
sender_node_info,
|
||||||
kind: RPCOperationKind::Statement(statement),
|
kind: RPCOperationKind::Statement(statement),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_answer(request: &RPCOperation, answer: RPCAnswer) -> Self {
|
pub fn new_answer(
|
||||||
|
request: &RPCOperation,
|
||||||
|
answer: RPCAnswer,
|
||||||
|
sender_node_info: Option<SignedNodeInfo>,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
op_id: request.op_id,
|
op_id: request.op_id,
|
||||||
|
sender_node_info,
|
||||||
kind: RPCOperationKind::Answer(answer),
|
kind: RPCOperationKind::Answer(answer),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,6 +97,10 @@ impl RPCOperation {
|
|||||||
self.op_id
|
self.op_id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn sender_node_info(&self) -> Option<&SignedNodeInfo> {
|
||||||
|
self.sender_node_info.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn kind(&self) -> &RPCOperationKind {
|
pub fn kind(&self) -> &RPCOperationKind {
|
||||||
&self.kind
|
&self.kind
|
||||||
}
|
}
|
||||||
@ -100,14 +115,32 @@ impl RPCOperation {
|
|||||||
) -> Result<Self, RPCError> {
|
) -> Result<Self, RPCError> {
|
||||||
let op_id = operation_reader.get_op_id();
|
let op_id = operation_reader.get_op_id();
|
||||||
|
|
||||||
|
let sender_node_info = if operation_reader.has_sender_node_info() {
|
||||||
|
let sni_reader = operation_reader
|
||||||
|
.get_sender_node_info()
|
||||||
|
.map_err(RPCError::protocol)?;
|
||||||
|
let sni = decode_signed_node_info(&sni_reader, sender_node_id, true)?;
|
||||||
|
Some(sni)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let kind_reader = operation_reader.get_kind();
|
let kind_reader = operation_reader.get_kind();
|
||||||
let kind = RPCOperationKind::decode(&kind_reader, sender_node_id)?;
|
let kind = RPCOperationKind::decode(&kind_reader, sender_node_id)?;
|
||||||
|
|
||||||
Ok(RPCOperation { op_id, kind })
|
Ok(RPCOperation {
|
||||||
|
op_id,
|
||||||
|
sender_node_info,
|
||||||
|
kind,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn encode(&self, builder: &mut veilid_capnp::operation::Builder) -> Result<(), RPCError> {
|
pub fn encode(&self, builder: &mut veilid_capnp::operation::Builder) -> Result<(), RPCError> {
|
||||||
builder.set_op_id(self.op_id);
|
builder.set_op_id(self.op_id);
|
||||||
|
if let Some(sender_info) = &self.sender_node_info {
|
||||||
|
let mut si_builder = builder.reborrow().init_sender_node_info();
|
||||||
|
encode_signed_node_info(&sender_info, &mut si_builder)?;
|
||||||
|
}
|
||||||
let mut k_builder = builder.reborrow().init_kind();
|
let mut k_builder = builder.reborrow().init_kind();
|
||||||
self.kind.encode(&mut k_builder)?;
|
self.kind.encode(&mut k_builder)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -18,21 +18,12 @@ impl RPCQuestion {
|
|||||||
pub fn detail(&self) -> &RPCQuestionDetail {
|
pub fn detail(&self) -> &RPCQuestionDetail {
|
||||||
&self.detail
|
&self.detail
|
||||||
}
|
}
|
||||||
// pub fn into_detail(self) -> RPCQuestionDetail {
|
|
||||||
// self.detail
|
|
||||||
// }
|
|
||||||
// pub fn into_respond_to_detail(self) -> (RespondTo, RPCQuestionDetail) {
|
|
||||||
// (self.respond_to, self.detail)
|
|
||||||
// }
|
|
||||||
pub fn desc(&self) -> &'static str {
|
pub fn desc(&self) -> &'static str {
|
||||||
self.detail.desc()
|
self.detail.desc()
|
||||||
}
|
}
|
||||||
pub fn decode(
|
pub fn decode(reader: &veilid_capnp::question::Reader) -> Result<RPCQuestion, RPCError> {
|
||||||
reader: &veilid_capnp::question::Reader,
|
|
||||||
sender_node_id: &DHTKey,
|
|
||||||
) -> Result<RPCQuestion, RPCError> {
|
|
||||||
let rt_reader = reader.get_respond_to();
|
let rt_reader = reader.get_respond_to();
|
||||||
let respond_to = RespondTo::decode(&rt_reader, sender_node_id)?;
|
let respond_to = RespondTo::decode(&rt_reader)?;
|
||||||
let d_reader = reader.get_detail();
|
let d_reader = reader.get_detail();
|
||||||
let detail = RPCQuestionDetail::decode(&d_reader)?;
|
let detail = RPCQuestionDetail::decode(&d_reader)?;
|
||||||
Ok(RPCQuestion { respond_to, detail })
|
Ok(RPCQuestion { respond_to, detail })
|
||||||
|
@ -3,7 +3,7 @@ use rpc_processor::*;
|
|||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum RespondTo {
|
pub enum RespondTo {
|
||||||
Sender(Option<SignedNodeInfo>),
|
Sender,
|
||||||
PrivateRoute(PrivateRoute),
|
PrivateRoute(PrivateRoute),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -13,11 +13,7 @@ impl RespondTo {
|
|||||||
builder: &mut veilid_capnp::question::respond_to::Builder,
|
builder: &mut veilid_capnp::question::respond_to::Builder,
|
||||||
) -> Result<(), RPCError> {
|
) -> Result<(), RPCError> {
|
||||||
match self {
|
match self {
|
||||||
Self::Sender(Some(sni)) => {
|
Self::Sender => {
|
||||||
let mut sni_builder = builder.reborrow().init_sender_with_info();
|
|
||||||
encode_signed_node_info(sni, &mut sni_builder)?;
|
|
||||||
}
|
|
||||||
Self::Sender(None) => {
|
|
||||||
builder.reborrow().set_sender(());
|
builder.reborrow().set_sender(());
|
||||||
}
|
}
|
||||||
Self::PrivateRoute(pr) => {
|
Self::PrivateRoute(pr) => {
|
||||||
@ -28,17 +24,9 @@ impl RespondTo {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decode(
|
pub fn decode(reader: &veilid_capnp::question::respond_to::Reader) -> Result<Self, RPCError> {
|
||||||
reader: &veilid_capnp::question::respond_to::Reader,
|
|
||||||
sender_node_id: &DHTKey,
|
|
||||||
) -> Result<Self, RPCError> {
|
|
||||||
let respond_to = match reader.which().map_err(RPCError::protocol)? {
|
let respond_to = match reader.which().map_err(RPCError::protocol)? {
|
||||||
veilid_capnp::question::respond_to::Sender(()) => RespondTo::Sender(None),
|
veilid_capnp::question::respond_to::Sender(()) => RespondTo::Sender,
|
||||||
veilid_capnp::question::respond_to::SenderWithInfo(sender_ni_reader) => {
|
|
||||||
let sender_ni_reader = sender_ni_reader.map_err(RPCError::protocol)?;
|
|
||||||
let sni = decode_signed_node_info(&sender_ni_reader, sender_node_id, true)?;
|
|
||||||
RespondTo::Sender(Some(sni))
|
|
||||||
}
|
|
||||||
veilid_capnp::question::respond_to::PrivateRoute(pr_reader) => {
|
veilid_capnp::question::respond_to::PrivateRoute(pr_reader) => {
|
||||||
let pr_reader = pr_reader.map_err(RPCError::protocol)?;
|
let pr_reader = pr_reader.map_err(RPCError::protocol)?;
|
||||||
let pr = decode_private_route(&pr_reader)?;
|
let pr = decode_private_route(&pr_reader)?;
|
||||||
|
188
veilid-core/src/rpc_processor/destination.rs
Normal file
188
veilid-core/src/rpc_processor/destination.rs
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Where to send an RPC message
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Destination {
|
||||||
|
/// Send to node directly
|
||||||
|
Direct {
|
||||||
|
/// The node to send to
|
||||||
|
target: NodeRef,
|
||||||
|
/// An optional safety route specification to send from for sender privacy
|
||||||
|
safety_route_spec: Option<Arc<SafetyRouteSpec>>,
|
||||||
|
},
|
||||||
|
/// Send to node for relay purposes
|
||||||
|
Relay {
|
||||||
|
/// The relay to send to
|
||||||
|
relay: NodeRef,
|
||||||
|
/// The final destination the relay should send to
|
||||||
|
target: DHTKey,
|
||||||
|
/// An optional safety route specification to send from for sender privacy
|
||||||
|
safety_route_spec: Option<Arc<SafetyRouteSpec>>,
|
||||||
|
},
|
||||||
|
/// Send to private route (privateroute)
|
||||||
|
PrivateRoute {
|
||||||
|
/// A private route to send to
|
||||||
|
private_route: PrivateRoute,
|
||||||
|
/// An optional safety route specification to send from for sender privacy
|
||||||
|
safety_route_spec: Option<Arc<SafetyRouteSpec>>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Destination {
|
||||||
|
pub fn direct(target: NodeRef) -> Self {
|
||||||
|
Self::Direct {
|
||||||
|
target,
|
||||||
|
safety_route_spec: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn relay(relay: NodeRef, target: DHTKey) -> Self {
|
||||||
|
Self::Relay {
|
||||||
|
relay,
|
||||||
|
target,
|
||||||
|
safety_route_spec: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn private_route(private_route: PrivateRoute) -> Self {
|
||||||
|
Self::PrivateRoute {
|
||||||
|
private_route,
|
||||||
|
safety_route_spec: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// pub fn target_id(&self) -> DHTKey {
|
||||||
|
// match self {
|
||||||
|
// Destination::Direct {
|
||||||
|
// target,
|
||||||
|
// safety_route_spec,
|
||||||
|
// } => target.node_id(),
|
||||||
|
// Destination::Relay {
|
||||||
|
// relay,
|
||||||
|
// target,
|
||||||
|
// safety_route_spec,
|
||||||
|
// } => *target,
|
||||||
|
// Destination::PrivateRoute {
|
||||||
|
// private_route,
|
||||||
|
// safety_route_spec,
|
||||||
|
// } => {}
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn best_routing_domain(&self) -> RoutingDomain {
|
||||||
|
// match self {
|
||||||
|
// Destination::Direct {
|
||||||
|
// target,
|
||||||
|
// safety_route_spec,
|
||||||
|
// } => {
|
||||||
|
// if safety_route_spec.is_some() {
|
||||||
|
// RoutingDomain::PublicInternet
|
||||||
|
// } else {
|
||||||
|
// target
|
||||||
|
// .best_routing_domain()
|
||||||
|
// .unwrap_or(RoutingDomain::PublicInternet)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// Destination::Relay {
|
||||||
|
// relay,
|
||||||
|
// target,
|
||||||
|
// safety_route_spec,
|
||||||
|
// } => {
|
||||||
|
// if safety_route_spec.is_some() {
|
||||||
|
// RoutingDomain::PublicInternet
|
||||||
|
// } else {
|
||||||
|
// relay
|
||||||
|
// .best_routing_domain()
|
||||||
|
// .unwrap_or(RoutingDomain::PublicInternet)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// Destination::PrivateRoute {
|
||||||
|
// private_route: _,
|
||||||
|
// safety_route_spec: _,
|
||||||
|
// } => RoutingDomain::PublicInternet,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
pub fn safety_route_spec(&self) -> Option<Arc<SafetyRouteSpec>> {
|
||||||
|
match self {
|
||||||
|
Destination::Direct {
|
||||||
|
target: _,
|
||||||
|
safety_route_spec,
|
||||||
|
} => safety_route_spec.clone(),
|
||||||
|
Destination::Relay {
|
||||||
|
relay: _,
|
||||||
|
target: _,
|
||||||
|
safety_route_spec,
|
||||||
|
} => safety_route_spec.clone(),
|
||||||
|
Destination::PrivateRoute {
|
||||||
|
private_route: _,
|
||||||
|
safety_route_spec,
|
||||||
|
} => safety_route_spec.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn with_safety_route_spec(self, safety_route_spec: Arc<SafetyRouteSpec>) -> Self {
|
||||||
|
match self {
|
||||||
|
Destination::Direct {
|
||||||
|
target,
|
||||||
|
safety_route_spec: _,
|
||||||
|
} => Self::Direct {
|
||||||
|
target,
|
||||||
|
safety_route_spec: Some(safety_route_spec),
|
||||||
|
},
|
||||||
|
Destination::Relay {
|
||||||
|
relay,
|
||||||
|
target,
|
||||||
|
safety_route_spec: _,
|
||||||
|
} => Self::Relay {
|
||||||
|
relay,
|
||||||
|
target,
|
||||||
|
safety_route_spec: Some(safety_route_spec),
|
||||||
|
},
|
||||||
|
Destination::PrivateRoute {
|
||||||
|
private_route,
|
||||||
|
safety_route_spec: _,
|
||||||
|
} => Self::PrivateRoute {
|
||||||
|
private_route,
|
||||||
|
safety_route_spec: Some(safety_route_spec),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Destination {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Destination::Direct {
|
||||||
|
target,
|
||||||
|
safety_route_spec,
|
||||||
|
} => {
|
||||||
|
let sr = safety_route_spec
|
||||||
|
.as_ref()
|
||||||
|
.map(|_sr| "+SR".to_owned())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
write!(f, "{:?}{}", target, sr)
|
||||||
|
}
|
||||||
|
Destination::Relay {
|
||||||
|
relay,
|
||||||
|
target,
|
||||||
|
safety_route_spec,
|
||||||
|
} => {
|
||||||
|
let sr = safety_route_spec
|
||||||
|
.as_ref()
|
||||||
|
.map(|_sr| "+SR".to_owned())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
write!(f, "{:?}@{:?}{}", target.encode(), relay, sr)
|
||||||
|
}
|
||||||
|
Destination::PrivateRoute {
|
||||||
|
private_route,
|
||||||
|
safety_route_spec,
|
||||||
|
} => {
|
||||||
|
let sr = safety_route_spec
|
||||||
|
.as_ref()
|
||||||
|
.map(|_sr| "+SR".to_owned())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
write!(f, "{}{}", private_route, sr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
mod coders;
|
mod coders;
|
||||||
|
mod destination;
|
||||||
mod private_route;
|
mod private_route;
|
||||||
mod rpc_cancel_tunnel;
|
mod rpc_cancel_tunnel;
|
||||||
mod rpc_complete_tunnel;
|
mod rpc_complete_tunnel;
|
||||||
@ -18,6 +19,7 @@ mod rpc_validate_dial_info;
|
|||||||
mod rpc_value_changed;
|
mod rpc_value_changed;
|
||||||
mod rpc_watch_value;
|
mod rpc_watch_value;
|
||||||
|
|
||||||
|
pub use destination::*;
|
||||||
pub use private_route::*;
|
pub use private_route::*;
|
||||||
pub use rpc_error::*;
|
pub use rpc_error::*;
|
||||||
|
|
||||||
@ -36,33 +38,6 @@ use stop_token::future::FutureExt;
|
|||||||
|
|
||||||
type OperationId = u64;
|
type OperationId = u64;
|
||||||
|
|
||||||
/// Where to send an RPC message
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum Destination {
|
|
||||||
/// Send to node (target noderef)
|
|
||||||
Direct(NodeRef),
|
|
||||||
/// Send to node for relay purposes (relay noderef, target nodeid)
|
|
||||||
Relay(NodeRef, DHTKey),
|
|
||||||
/// Send to private route (privateroute)
|
|
||||||
PrivateRoute(PrivateRoute),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Destination {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Destination::Direct(nr) => {
|
|
||||||
write!(f, "{:?}", nr)
|
|
||||||
}
|
|
||||||
Destination::Relay(nr, key) => {
|
|
||||||
write!(f, "{:?}@{:?}", key.encode(), nr)
|
|
||||||
}
|
|
||||||
Destination::PrivateRoute(pr) => {
|
|
||||||
write!(f, "{}", pr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The decoded header of an RPC message
|
/// The decoded header of an RPC message
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct RPCMessageHeader {
|
struct RPCMessageHeader {
|
||||||
@ -76,6 +51,8 @@ struct RPCMessageHeader {
|
|||||||
peer_noderef: NodeRef,
|
peer_noderef: NodeRef,
|
||||||
/// The connection from the peer sent the message (not the original sender)
|
/// The connection from the peer sent the message (not the original sender)
|
||||||
connection_descriptor: ConnectionDescriptor,
|
connection_descriptor: ConnectionDescriptor,
|
||||||
|
/// The routing domain the message was sent through
|
||||||
|
routing_domain: RoutingDomain,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -177,7 +154,6 @@ pub struct RPCProcessorInner {
|
|||||||
pub struct RPCProcessor {
|
pub struct RPCProcessor {
|
||||||
crypto: Crypto,
|
crypto: Crypto,
|
||||||
config: VeilidConfig,
|
config: VeilidConfig,
|
||||||
enable_local_peer_scope: bool,
|
|
||||||
inner: Arc<Mutex<RPCProcessorInner>>,
|
inner: Arc<Mutex<RPCProcessorInner>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,11 +176,6 @@ impl RPCProcessor {
|
|||||||
Self {
|
Self {
|
||||||
crypto: network_manager.crypto(),
|
crypto: network_manager.crypto(),
|
||||||
config: network_manager.config(),
|
config: network_manager.config(),
|
||||||
enable_local_peer_scope: network_manager
|
|
||||||
.config()
|
|
||||||
.get()
|
|
||||||
.network
|
|
||||||
.enable_local_peer_scope,
|
|
||||||
inner: Arc::new(Mutex::new(Self::new_inner(network_manager))),
|
inner: Arc::new(Mutex::new(Self::new_inner(network_manager))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -227,28 +198,10 @@ impl RPCProcessor {
|
|||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
fn filter_peer_scope(&self, node_info: &NodeInfo) -> bool {
|
/// Determine if a NodeInfo can be placed into the specified routing domain
|
||||||
// if local peer scope is enabled, then don't reject any peer info
|
fn filter_node_info(&self, routing_domain: RoutingDomain, node_info: &NodeInfo) -> bool {
|
||||||
if self.enable_local_peer_scope {
|
let routing_table = self.routing_table();
|
||||||
return true;
|
routing_table.node_info_is_valid_in_routing_domain(routing_domain, &node_info)
|
||||||
}
|
|
||||||
|
|
||||||
// reject attempts to include non-public addresses in results
|
|
||||||
for did in &node_info.dial_info_detail_list {
|
|
||||||
if !did.dial_info.is_global() {
|
|
||||||
// non-public address causes rejection
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(rpi) = &node_info.relay_peer_info {
|
|
||||||
for did in &rpi.signed_node_info.node_info.dial_info_detail_list {
|
|
||||||
if !did.dial_info.is_global() {
|
|
||||||
// non-public address causes rejection
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
@ -370,7 +323,7 @@ impl RPCProcessor {
|
|||||||
.await
|
.await
|
||||||
.into_timeout_or();
|
.into_timeout_or();
|
||||||
Ok(res.map(|res| {
|
Ok(res.map(|res| {
|
||||||
let (span_id, rpcreader) = res.take_value().unwrap();
|
let (_span_id, rpcreader) = res.take_value().unwrap();
|
||||||
let end_ts = intf::get_timestamp();
|
let end_ts = intf::get_timestamp();
|
||||||
|
|
||||||
// fixme: causes crashes? "Missing otel data span extensions"??
|
// fixme: causes crashes? "Missing otel data span extensions"??
|
||||||
@ -390,17 +343,12 @@ impl RPCProcessor {
|
|||||||
Err(_) | Ok(TimeoutOr::Timeout) => {
|
Err(_) | Ok(TimeoutOr::Timeout) => {
|
||||||
self.cancel_op_id_waiter(waitable_reply.op_id);
|
self.cancel_op_id_waiter(waitable_reply.op_id);
|
||||||
|
|
||||||
self.routing_table()
|
waitable_reply.node_ref.stats_question_lost();
|
||||||
.stats_question_lost(waitable_reply.node_ref.clone());
|
|
||||||
}
|
}
|
||||||
Ok(TimeoutOr::Value((rpcreader, _))) => {
|
Ok(TimeoutOr::Value((rpcreader, _))) => {
|
||||||
// Note that the remote node definitely received this node info since we got a reply
|
|
||||||
waitable_reply.node_ref.set_seen_our_node_info();
|
|
||||||
|
|
||||||
// Reply received
|
// Reply received
|
||||||
let recv_ts = intf::get_timestamp();
|
let recv_ts = intf::get_timestamp();
|
||||||
self.routing_table().stats_answer_rcvd(
|
waitable_reply.node_ref.stats_answer_rcvd(
|
||||||
waitable_reply.node_ref,
|
|
||||||
waitable_reply.send_ts,
|
waitable_reply.send_ts,
|
||||||
recv_ts,
|
recv_ts,
|
||||||
rpcreader.header.body_len,
|
rpcreader.header.body_len,
|
||||||
@ -411,34 +359,14 @@ impl RPCProcessor {
|
|||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a 'RespondTo::Sender' that contains either our dial info,
|
|
||||||
/// or None if the peer has seen our dial info before or our node info is not yet valid
|
|
||||||
/// because of an unknown network class
|
|
||||||
pub fn make_respond_to_sender(&self, peer: NodeRef) -> RespondTo {
|
|
||||||
if peer.has_seen_our_node_info()
|
|
||||||
|| matches!(
|
|
||||||
self.network_manager()
|
|
||||||
.get_network_class()
|
|
||||||
.unwrap_or(NetworkClass::Invalid),
|
|
||||||
NetworkClass::Invalid
|
|
||||||
)
|
|
||||||
{
|
|
||||||
RespondTo::Sender(None)
|
|
||||||
} else {
|
|
||||||
let our_sni = self.routing_table().get_own_signed_node_info();
|
|
||||||
RespondTo::Sender(Some(our_sni))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Produce a byte buffer that represents the wire encoding of the entire
|
/// Produce a byte buffer that represents the wire encoding of the entire
|
||||||
/// unencrypted envelope body for a RPC message. This incorporates
|
/// unencrypted envelope body for a RPC message. This incorporates
|
||||||
/// wrapping a private and/or safety route if they are specified.
|
/// wrapping a private and/or safety route if they are specified.
|
||||||
#[instrument(level = "debug", skip(self, operation, safety_route_spec), err)]
|
#[instrument(level = "debug", skip(self, operation), err)]
|
||||||
fn render_operation(
|
fn render_operation(
|
||||||
&self,
|
&self,
|
||||||
dest: Destination,
|
dest: Destination,
|
||||||
operation: &RPCOperation,
|
operation: &RPCOperation,
|
||||||
safety_route_spec: Option<&SafetyRouteSpec>,
|
|
||||||
) -> Result<RenderedOperation, RPCError> {
|
) -> Result<RenderedOperation, RPCError> {
|
||||||
let out_node_id; // Envelope Node Id
|
let out_node_id; // Envelope Node Id
|
||||||
let mut out_node_ref: Option<NodeRef> = None; // Node to send envelope to
|
let mut out_node_ref: Option<NodeRef> = None; // Node to send envelope to
|
||||||
@ -456,12 +384,25 @@ impl RPCProcessor {
|
|||||||
|
|
||||||
// To where are we sending the request
|
// To where are we sending the request
|
||||||
match dest {
|
match dest {
|
||||||
Destination::Direct(ref node_ref) | Destination::Relay(ref node_ref, _) => {
|
Destination::Direct {
|
||||||
|
target: ref node_ref,
|
||||||
|
ref safety_route_spec,
|
||||||
|
}
|
||||||
|
| Destination::Relay {
|
||||||
|
relay: ref node_ref,
|
||||||
|
target: _,
|
||||||
|
ref safety_route_spec,
|
||||||
|
} => {
|
||||||
// Send to a node without a private route
|
// Send to a node without a private route
|
||||||
// --------------------------------------
|
// --------------------------------------
|
||||||
|
|
||||||
// Get the actual destination node id accounting for relays
|
// Get the actual destination node id accounting for relays
|
||||||
let (node_ref, node_id) = if let Destination::Relay(_, dht_key) = dest {
|
let (node_ref, node_id) = if let Destination::Relay {
|
||||||
|
relay: _,
|
||||||
|
target: ref dht_key,
|
||||||
|
safety_route_spec: _,
|
||||||
|
} = dest
|
||||||
|
{
|
||||||
(node_ref.clone(), dht_key.clone())
|
(node_ref.clone(), dht_key.clone())
|
||||||
} else {
|
} else {
|
||||||
let node_id = node_ref.node_id();
|
let node_id = node_ref.node_id();
|
||||||
@ -469,7 +410,7 @@ impl RPCProcessor {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Handle the existence of safety route
|
// Handle the existence of safety route
|
||||||
match safety_route_spec {
|
match safety_route_spec.as_ref() {
|
||||||
None => {
|
None => {
|
||||||
// If no safety route is being used, and we're not sending to a private
|
// If no safety route is being used, and we're not sending to a private
|
||||||
// route, we can use a direct envelope instead of routing
|
// route, we can use a direct envelope instead of routing
|
||||||
@ -493,12 +434,16 @@ impl RPCProcessor {
|
|||||||
.dial_info
|
.dial_info
|
||||||
.node_id
|
.node_id
|
||||||
.key;
|
.key;
|
||||||
out_message = self.wrap_with_route(Some(sr), private_route, message_vec)?;
|
out_message =
|
||||||
|
self.wrap_with_route(Some(sr.clone()), private_route, message_vec)?;
|
||||||
out_hop_count = 1 + sr.hops.len();
|
out_hop_count = 1 + sr.hops.len();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Destination::PrivateRoute(private_route) => {
|
Destination::PrivateRoute {
|
||||||
|
private_route,
|
||||||
|
safety_route_spec,
|
||||||
|
} => {
|
||||||
// Send to private route
|
// Send to private route
|
||||||
// ---------------------
|
// ---------------------
|
||||||
// Reply with 'route' operation
|
// Reply with 'route' operation
|
||||||
@ -544,16 +489,65 @@ impl RPCProcessor {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get signed node info to package with RPC messages to improve
|
||||||
|
// routing table caching when it is okay to do so
|
||||||
|
// This is only done in the PublicInternet routing domain because
|
||||||
|
// as far as we can tell this is the only domain that will really benefit
|
||||||
|
fn get_sender_signed_node_info(&self, dest: &Destination) -> Option<SignedNodeInfo> {
|
||||||
|
// Don't do this if the sender is to remain private
|
||||||
|
if dest.safety_route_spec().is_some() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
// Don't do this if our own signed node info isn't valid yet
|
||||||
|
let routing_table = self.routing_table();
|
||||||
|
if !routing_table.has_valid_own_node_info(RoutingDomain::PublicInternet) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
match dest {
|
||||||
|
Destination::Direct {
|
||||||
|
target,
|
||||||
|
safety_route_spec: _,
|
||||||
|
} => {
|
||||||
|
// If the target has seen our node info already don't do this
|
||||||
|
if target.has_seen_our_node_info(RoutingDomain::PublicInternet) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(routing_table.get_own_signed_node_info(RoutingDomain::PublicInternet))
|
||||||
|
}
|
||||||
|
Destination::Relay {
|
||||||
|
relay: _,
|
||||||
|
target,
|
||||||
|
safety_route_spec: _,
|
||||||
|
} => {
|
||||||
|
if let Some(target) = routing_table.lookup_node_ref(*target) {
|
||||||
|
if target.has_seen_our_node_info(RoutingDomain::PublicInternet) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(routing_table.get_own_signed_node_info(RoutingDomain::PublicInternet))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Destination::PrivateRoute {
|
||||||
|
private_route: _,
|
||||||
|
safety_route_spec: _,
|
||||||
|
} => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Issue a question over the network, possibly using an anonymized route
|
// Issue a question over the network, possibly using an anonymized route
|
||||||
#[instrument(level = "debug", skip(self, question, safety_route_spec), err)]
|
#[instrument(level = "debug", skip(self, question), err)]
|
||||||
async fn question(
|
async fn question(
|
||||||
&self,
|
&self,
|
||||||
dest: Destination,
|
dest: Destination,
|
||||||
question: RPCQuestion,
|
question: RPCQuestion,
|
||||||
safety_route_spec: Option<&SafetyRouteSpec>,
|
|
||||||
) -> Result<NetworkResult<WaitableReply>, RPCError> {
|
) -> Result<NetworkResult<WaitableReply>, RPCError> {
|
||||||
|
// Get sender info if we should send that
|
||||||
|
let opt_sender_info = self.get_sender_signed_node_info(&dest);
|
||||||
|
|
||||||
// Wrap question in operation
|
// Wrap question in operation
|
||||||
let operation = RPCOperation::new_question(question);
|
let operation = RPCOperation::new_question(question, opt_sender_info);
|
||||||
let op_id = operation.op_id();
|
let op_id = operation.op_id();
|
||||||
|
|
||||||
// Log rpc send
|
// Log rpc send
|
||||||
@ -565,7 +559,7 @@ impl RPCProcessor {
|
|||||||
node_id,
|
node_id,
|
||||||
node_ref,
|
node_ref,
|
||||||
hop_count,
|
hop_count,
|
||||||
} = self.render_operation(dest, &operation, safety_route_spec)?;
|
} = self.render_operation(dest, &operation)?;
|
||||||
|
|
||||||
// If we need to resolve the first hop, do it
|
// If we need to resolve the first hop, do it
|
||||||
let node_ref = match node_ref {
|
let node_ref = match node_ref {
|
||||||
@ -595,20 +589,19 @@ impl RPCProcessor {
|
|||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
// If we're returning an error, clean up
|
// If we're returning an error, clean up
|
||||||
self.cancel_op_id_waiter(op_id);
|
self.cancel_op_id_waiter(op_id);
|
||||||
self.routing_table()
|
node_ref
|
||||||
.stats_failed_to_send(node_ref.clone(), send_ts, true);
|
.stats_failed_to_send(send_ts, true);
|
||||||
RPCError::network(e) })?
|
RPCError::network(e)
|
||||||
=> {
|
})? => {
|
||||||
// If we couldn't send we're still cleaning up
|
// If we couldn't send we're still cleaning up
|
||||||
self.cancel_op_id_waiter(op_id);
|
self.cancel_op_id_waiter(op_id);
|
||||||
self.routing_table()
|
node_ref
|
||||||
.stats_failed_to_send(node_ref, send_ts, true);
|
.stats_failed_to_send(send_ts, true);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Successfully sent
|
// Successfully sent
|
||||||
self.routing_table()
|
node_ref.stats_question_sent(send_ts, bytes, true);
|
||||||
.stats_question_sent(node_ref.clone(), send_ts, bytes, true);
|
|
||||||
|
|
||||||
// Pass back waitable reply completion
|
// Pass back waitable reply completion
|
||||||
Ok(NetworkResult::value(WaitableReply {
|
Ok(NetworkResult::value(WaitableReply {
|
||||||
@ -622,15 +615,17 @@ impl RPCProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Issue a statement over the network, possibly using an anonymized route
|
// Issue a statement over the network, possibly using an anonymized route
|
||||||
#[instrument(level = "debug", skip(self, statement, safety_route_spec), err)]
|
#[instrument(level = "debug", skip(self, statement), err)]
|
||||||
async fn statement(
|
async fn statement(
|
||||||
&self,
|
&self,
|
||||||
dest: Destination,
|
dest: Destination,
|
||||||
statement: RPCStatement,
|
statement: RPCStatement,
|
||||||
safety_route_spec: Option<&SafetyRouteSpec>,
|
|
||||||
) -> Result<NetworkResult<()>, RPCError> {
|
) -> Result<NetworkResult<()>, RPCError> {
|
||||||
|
// Get sender info if we should send that
|
||||||
|
let opt_sender_info = self.get_sender_signed_node_info(&dest);
|
||||||
|
|
||||||
// Wrap statement in operation
|
// Wrap statement in operation
|
||||||
let operation = RPCOperation::new_statement(statement);
|
let operation = RPCOperation::new_statement(statement, opt_sender_info);
|
||||||
|
|
||||||
// Log rpc send
|
// Log rpc send
|
||||||
debug!(target: "rpc_message", dir = "send", kind = "statement", op_id = operation.op_id(), desc = operation.kind().desc(), ?dest);
|
debug!(target: "rpc_message", dir = "send", kind = "statement", op_id = operation.op_id(), desc = operation.kind().desc(), ?dest);
|
||||||
@ -641,7 +636,7 @@ impl RPCProcessor {
|
|||||||
node_id,
|
node_id,
|
||||||
node_ref,
|
node_ref,
|
||||||
hop_count: _,
|
hop_count: _,
|
||||||
} = self.render_operation(dest, &operation, safety_route_spec)?;
|
} = self.render_operation(dest, &operation)?;
|
||||||
|
|
||||||
// If we need to resolve the first hop, do it
|
// If we need to resolve the first hop, do it
|
||||||
let node_ref = match node_ref {
|
let node_ref = match node_ref {
|
||||||
@ -663,18 +658,18 @@ impl RPCProcessor {
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
// If we're returning an error, clean up
|
// If we're returning an error, clean up
|
||||||
self.routing_table()
|
node_ref
|
||||||
.stats_failed_to_send(node_ref.clone(), send_ts, true);
|
.stats_failed_to_send(send_ts, true);
|
||||||
RPCError::network(e) })? => {
|
RPCError::network(e)
|
||||||
|
})? => {
|
||||||
// If we couldn't send we're still cleaning up
|
// If we couldn't send we're still cleaning up
|
||||||
self.routing_table()
|
node_ref
|
||||||
.stats_failed_to_send(node_ref, send_ts, true);
|
.stats_failed_to_send(send_ts, true);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Successfully sent
|
// Successfully sent
|
||||||
self.routing_table()
|
node_ref.stats_question_sent(send_ts, bytes, true);
|
||||||
.stats_question_sent(node_ref.clone(), send_ts, bytes, true);
|
|
||||||
|
|
||||||
Ok(NetworkResult::value(()))
|
Ok(NetworkResult::value(()))
|
||||||
}
|
}
|
||||||
@ -691,7 +686,7 @@ impl RPCProcessor {
|
|||||||
|
|
||||||
// To where should we respond?
|
// To where should we respond?
|
||||||
match respond_to {
|
match respond_to {
|
||||||
RespondTo::Sender(_) => {
|
RespondTo::Sender => {
|
||||||
// Reply directly to the request's source
|
// Reply directly to the request's source
|
||||||
let sender_id = request.header.envelope.get_sender_id();
|
let sender_id = request.header.envelope.get_sender_id();
|
||||||
|
|
||||||
@ -701,30 +696,32 @@ impl RPCProcessor {
|
|||||||
// If the sender_id is that of the peer, then this is a direct reply
|
// If the sender_id is that of the peer, then this is a direct reply
|
||||||
// else it is a relayed reply through the peer
|
// else it is a relayed reply through the peer
|
||||||
if peer_noderef.node_id() == sender_id {
|
if peer_noderef.node_id() == sender_id {
|
||||||
Destination::Direct(peer_noderef)
|
Destination::direct(peer_noderef)
|
||||||
} else {
|
} else {
|
||||||
Destination::Relay(peer_noderef, sender_id)
|
Destination::relay(peer_noderef, sender_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RespondTo::PrivateRoute(pr) => Destination::PrivateRoute(pr.clone()),
|
RespondTo::PrivateRoute(pr) => Destination::private_route(pr.clone()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Issue a reply over the network, possibly using an anonymized route
|
// Issue a reply over the network, possibly using an anonymized route
|
||||||
// The request must want a response, or this routine fails
|
// The request must want a response, or this routine fails
|
||||||
#[instrument(level = "debug", skip(self, request, answer, safety_route_spec), err)]
|
#[instrument(level = "debug", skip(self, request, answer), err)]
|
||||||
async fn answer(
|
async fn answer(
|
||||||
&self,
|
&self,
|
||||||
request: RPCMessage,
|
request: RPCMessage,
|
||||||
answer: RPCAnswer,
|
answer: RPCAnswer,
|
||||||
safety_route_spec: Option<&SafetyRouteSpec>,
|
|
||||||
) -> Result<NetworkResult<()>, RPCError> {
|
) -> Result<NetworkResult<()>, RPCError> {
|
||||||
// Wrap answer in operation
|
|
||||||
let operation = RPCOperation::new_answer(&request.operation, answer);
|
|
||||||
|
|
||||||
// Extract destination from respond_to
|
// Extract destination from respond_to
|
||||||
let dest = self.get_respond_to_destination(&request);
|
let dest = self.get_respond_to_destination(&request);
|
||||||
|
|
||||||
|
// Get sender info if we should send that
|
||||||
|
let opt_sender_info = self.get_sender_signed_node_info(&dest);
|
||||||
|
|
||||||
|
// Wrap answer in operation
|
||||||
|
let operation = RPCOperation::new_answer(&request.operation, answer, opt_sender_info);
|
||||||
|
|
||||||
// Log rpc send
|
// Log rpc send
|
||||||
debug!(target: "rpc_message", dir = "send", kind = "answer", op_id = operation.op_id(), desc = operation.kind().desc(), ?dest);
|
debug!(target: "rpc_message", dir = "send", kind = "answer", op_id = operation.op_id(), desc = operation.kind().desc(), ?dest);
|
||||||
|
|
||||||
@ -734,7 +731,7 @@ impl RPCProcessor {
|
|||||||
node_id,
|
node_id,
|
||||||
node_ref,
|
node_ref,
|
||||||
hop_count: _,
|
hop_count: _,
|
||||||
} = self.render_operation(dest, &operation, safety_route_spec)?;
|
} = self.render_operation(dest, &operation)?;
|
||||||
|
|
||||||
// If we need to resolve the first hop, do it
|
// If we need to resolve the first hop, do it
|
||||||
let node_ref = match node_ref {
|
let node_ref = match node_ref {
|
||||||
@ -755,17 +752,18 @@ impl RPCProcessor {
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
// If we're returning an error, clean up
|
// If we're returning an error, clean up
|
||||||
self.routing_table()
|
node_ref
|
||||||
.stats_failed_to_send(node_ref.clone(), send_ts, true);
|
.stats_failed_to_send(send_ts, true);
|
||||||
RPCError::network(e) })? => {
|
RPCError::network(e)
|
||||||
|
})? => {
|
||||||
// If we couldn't send we're still cleaning up
|
// If we couldn't send we're still cleaning up
|
||||||
self.routing_table()
|
node_ref
|
||||||
.stats_failed_to_send(node_ref.clone(), send_ts, false);
|
.stats_failed_to_send(send_ts, false);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Reply successfully sent
|
// Reply successfully sent
|
||||||
self.routing_table().stats_answer_sent(node_ref, bytes);
|
node_ref.stats_answer_sent(bytes);
|
||||||
|
|
||||||
Ok(NetworkResult::value(()))
|
Ok(NetworkResult::value(()))
|
||||||
}
|
}
|
||||||
@ -776,6 +774,9 @@ impl RPCProcessor {
|
|||||||
&self,
|
&self,
|
||||||
encoded_msg: RPCMessageEncoded,
|
encoded_msg: RPCMessageEncoded,
|
||||||
) -> Result<(), RPCError> {
|
) -> Result<(), RPCError> {
|
||||||
|
// Get the routing domain this message came over
|
||||||
|
let routing_domain = encoded_msg.header.routing_domain;
|
||||||
|
|
||||||
// Decode the operation
|
// Decode the operation
|
||||||
let sender_node_id = encoded_msg.header.envelope.get_sender_id();
|
let sender_node_id = encoded_msg.header.envelope.get_sender_id();
|
||||||
|
|
||||||
@ -789,34 +790,34 @@ impl RPCProcessor {
|
|||||||
RPCOperation::decode(&op_reader, &sender_node_id)?
|
RPCOperation::decode(&op_reader, &sender_node_id)?
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get the sender noderef, incorporating and 'sender node info' we have from a question
|
// Get the sender noderef, incorporating and 'sender node info'
|
||||||
let mut opt_sender_nr: Option<NodeRef> = None;
|
let mut opt_sender_nr: Option<NodeRef> = None;
|
||||||
match operation.kind() {
|
if let Some(sender_node_info) = operation.sender_node_info() {
|
||||||
RPCOperationKind::Question(q) => {
|
|
||||||
match q.respond_to() {
|
|
||||||
RespondTo::Sender(Some(sender_ni)) => {
|
|
||||||
// Sender NodeInfo was specified, update our routing table with it
|
// Sender NodeInfo was specified, update our routing table with it
|
||||||
if !self.filter_peer_scope(&sender_ni.node_info) {
|
if !self.filter_node_info(RoutingDomain::PublicInternet, &sender_node_info.node_info) {
|
||||||
return Err(RPCError::invalid_format(
|
return Err(RPCError::invalid_format(
|
||||||
"respond_to_sender_signed_node_info has invalid peer scope",
|
"sender signednodeinfo has invalid peer scope",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
opt_sender_nr = self.routing_table().register_node_with_signed_node_info(
|
opt_sender_nr = self.routing_table().register_node_with_signed_node_info(
|
||||||
|
routing_domain,
|
||||||
sender_node_id,
|
sender_node_id,
|
||||||
sender_ni.clone(),
|
sender_node_info.clone(),
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
};
|
|
||||||
if opt_sender_nr.is_none() {
|
|
||||||
// look up sender node, in case it's different than our peer due to relaying
|
// look up sender node, in case it's different than our peer due to relaying
|
||||||
|
if opt_sender_nr.is_none() {
|
||||||
opt_sender_nr = self.routing_table().lookup_node_ref(sender_node_id)
|
opt_sender_nr = self.routing_table().lookup_node_ref(sender_node_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mark this sender as having seen our node info over this routing domain
|
||||||
|
// because it managed to reach us over that routing domain
|
||||||
|
if let Some(sender_nr) = &opt_sender_nr {
|
||||||
|
sender_nr.set_seen_our_node_info(routing_domain);
|
||||||
|
}
|
||||||
|
|
||||||
// Make the RPC message
|
// Make the RPC message
|
||||||
let msg = RPCMessage {
|
let msg = RPCMessage {
|
||||||
header: encoded_msg.header,
|
header: encoded_msg.header,
|
||||||
@ -828,21 +829,13 @@ impl RPCProcessor {
|
|||||||
let kind = match msg.operation.kind() {
|
let kind = match msg.operation.kind() {
|
||||||
RPCOperationKind::Question(_) => {
|
RPCOperationKind::Question(_) => {
|
||||||
if let Some(sender_nr) = msg.opt_sender_nr.clone() {
|
if let Some(sender_nr) = msg.opt_sender_nr.clone() {
|
||||||
self.routing_table().stats_question_rcvd(
|
sender_nr.stats_question_rcvd(msg.header.timestamp, msg.header.body_len);
|
||||||
sender_nr,
|
|
||||||
msg.header.timestamp,
|
|
||||||
msg.header.body_len,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
"question"
|
"question"
|
||||||
}
|
}
|
||||||
RPCOperationKind::Statement(_) => {
|
RPCOperationKind::Statement(_) => {
|
||||||
if let Some(sender_nr) = msg.opt_sender_nr.clone() {
|
if let Some(sender_nr) = msg.opt_sender_nr.clone() {
|
||||||
self.routing_table().stats_question_rcvd(
|
sender_nr.stats_question_rcvd(msg.header.timestamp, msg.header.body_len);
|
||||||
sender_nr,
|
|
||||||
msg.header.timestamp,
|
|
||||||
msg.header.body_len,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
"statement"
|
"statement"
|
||||||
}
|
}
|
||||||
@ -900,7 +893,7 @@ impl RPCProcessor {
|
|||||||
stop_token: StopToken,
|
stop_token: StopToken,
|
||||||
receiver: flume::Receiver<(Option<Id>, RPCMessageEncoded)>,
|
receiver: flume::Receiver<(Option<Id>, RPCMessageEncoded)>,
|
||||||
) {
|
) {
|
||||||
while let Ok(Ok((span_id, msg))) =
|
while let Ok(Ok((_span_id, msg))) =
|
||||||
receiver.recv_async().timeout_at(stop_token.clone()).await
|
receiver.recv_async().timeout_at(stop_token.clone()).await
|
||||||
{
|
{
|
||||||
let rpc_worker_span = span!(parent: None, Level::TRACE, "rpc_worker");
|
let rpc_worker_span = span!(parent: None, Level::TRACE, "rpc_worker");
|
||||||
@ -1001,6 +994,7 @@ impl RPCProcessor {
|
|||||||
body: Vec<u8>,
|
body: Vec<u8>,
|
||||||
peer_noderef: NodeRef,
|
peer_noderef: NodeRef,
|
||||||
connection_descriptor: ConnectionDescriptor,
|
connection_descriptor: ConnectionDescriptor,
|
||||||
|
routing_domain: RoutingDomain,
|
||||||
) -> EyreResult<()> {
|
) -> EyreResult<()> {
|
||||||
let msg = RPCMessageEncoded {
|
let msg = RPCMessageEncoded {
|
||||||
header: RPCMessageHeader {
|
header: RPCMessageHeader {
|
||||||
@ -1009,6 +1003,7 @@ impl RPCProcessor {
|
|||||||
body_len: body.len() as u64,
|
body_len: body.len() as u64,
|
||||||
peer_noderef,
|
peer_noderef,
|
||||||
connection_descriptor,
|
connection_descriptor,
|
||||||
|
routing_domain,
|
||||||
},
|
},
|
||||||
data: RPCMessageData { contents: body },
|
data: RPCMessageData { contents: body },
|
||||||
};
|
};
|
||||||
|
53
veilid-core/src/rpc_processor/origin.rs
Normal file
53
veilid-core/src/rpc_processor/origin.rs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Origin {
|
||||||
|
Sender,
|
||||||
|
PrivateRoute(PrivateRoute),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Origin {
|
||||||
|
pub fn sender() -> Self {
|
||||||
|
Self::Sender
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn private_route(private_route: PrivateRoute) -> Self {
|
||||||
|
Self::PrivateRoute(private_route)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_respond_to(self, destination: &Destination) -> Result<RespondTo, RPCError> {
|
||||||
|
match self {
|
||||||
|
Self::Sender => {
|
||||||
|
let peer = match destination {
|
||||||
|
Destination::Direct {
|
||||||
|
target,
|
||||||
|
safety_route_spec,
|
||||||
|
} => todo!(),
|
||||||
|
Destination::Relay {
|
||||||
|
relay,
|
||||||
|
target,
|
||||||
|
safety_route_spec,
|
||||||
|
} => todo!(),
|
||||||
|
Destination::PrivateRoute {
|
||||||
|
private_route,
|
||||||
|
safety_route_spec,
|
||||||
|
} => todo!(),
|
||||||
|
};
|
||||||
|
let routing_table = peer.routing_table();
|
||||||
|
let routing_domain = peer.best_routing_domain();
|
||||||
|
// Send some signed node info along with the question if this node needs to be replied to
|
||||||
|
if routing_table.has_valid_own_node_info()
|
||||||
|
&& !peer.has_seen_our_node_info(routing_domain)
|
||||||
|
{
|
||||||
|
let our_sni = self
|
||||||
|
.routing_table()
|
||||||
|
.get_own_signed_node_info(routing_domain);
|
||||||
|
RespondTo::Sender(Some(our_sni))
|
||||||
|
} else {
|
||||||
|
RespondTo::Sender(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::PrivateRoute(pr) => RespondTo::PrivateRoute(pr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,7 @@ impl RPCProcessor {
|
|||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
fn compile_safety_route(
|
fn compile_safety_route(
|
||||||
&self,
|
&self,
|
||||||
safety_route_spec: &SafetyRouteSpec,
|
safety_route_spec: Arc<SafetyRouteSpec>,
|
||||||
private_route: PrivateRoute,
|
private_route: PrivateRoute,
|
||||||
) -> Result<SafetyRoute, RPCError> {
|
) -> Result<SafetyRoute, RPCError> {
|
||||||
// Ensure the total hop count isn't too long for our config
|
// Ensure the total hop count isn't too long for our config
|
||||||
@ -111,15 +111,15 @@ impl RPCProcessor {
|
|||||||
// Wrap an operation inside a route
|
// Wrap an operation inside a route
|
||||||
pub(super) fn wrap_with_route(
|
pub(super) fn wrap_with_route(
|
||||||
&self,
|
&self,
|
||||||
safety_route_spec: Option<&SafetyRouteSpec>,
|
safety_route_spec: Option<Arc<SafetyRouteSpec>>,
|
||||||
private_route: PrivateRoute,
|
private_route: PrivateRoute,
|
||||||
message_data: Vec<u8>,
|
message_data: Vec<u8>,
|
||||||
) -> Result<Vec<u8>, RPCError> {
|
) -> Result<Vec<u8>, RPCError> {
|
||||||
// Encrypt routed operation
|
// Encrypt routed operation
|
||||||
// Xmsg + ENC(Xmsg, DH(PKapr, SKbsr))
|
// Xmsg + ENC(Xmsg, DH(PKapr, SKbsr))
|
||||||
let nonce = Crypto::get_random_nonce();
|
let nonce = Crypto::get_random_nonce();
|
||||||
let stub_safety_route_spec = SafetyRouteSpec::new();
|
let safety_route_spec =
|
||||||
let safety_route_spec = safety_route_spec.unwrap_or(&stub_safety_route_spec);
|
safety_route_spec.unwrap_or_else(|| Arc::new(SafetyRouteSpec::new()));
|
||||||
let dh_secret = self
|
let dh_secret = self
|
||||||
.crypto
|
.crypto
|
||||||
.cached_dh(&private_route.public_key, &safety_route_spec.secret_key)
|
.cached_dh(&private_route.public_key, &safety_route_spec.secret_key)
|
||||||
@ -139,7 +139,7 @@ impl RPCProcessor {
|
|||||||
operation,
|
operation,
|
||||||
};
|
};
|
||||||
let operation =
|
let operation =
|
||||||
RPCOperation::new_statement(RPCStatement::new(RPCStatementDetail::Route(route)));
|
RPCOperation::new_statement(RPCStatement::new(RPCStatementDetail::Route(route)), None);
|
||||||
|
|
||||||
// Convert message to bytes and return it
|
// Convert message to bytes and return it
|
||||||
let mut route_msg = ::capnp::message::Builder::new_default();
|
let mut route_msg = ::capnp::message::Builder::new_default();
|
||||||
|
@ -8,15 +8,13 @@ impl RPCProcessor {
|
|||||||
self,
|
self,
|
||||||
dest: Destination,
|
dest: Destination,
|
||||||
key: DHTKey,
|
key: DHTKey,
|
||||||
safety_route: Option<&SafetyRouteSpec>,
|
|
||||||
respond_to: RespondTo,
|
|
||||||
) -> Result<NetworkResult<Answer<Vec<PeerInfo>>>, RPCError> {
|
) -> Result<NetworkResult<Answer<Vec<PeerInfo>>>, RPCError> {
|
||||||
let find_node_q = RPCOperationFindNodeQ { node_id: key };
|
let find_node_q_detail =
|
||||||
let question = RPCQuestion::new(respond_to, RPCQuestionDetail::FindNodeQ(find_node_q));
|
RPCQuestionDetail::FindNodeQ(RPCOperationFindNodeQ { node_id: key });
|
||||||
|
let find_node_q = RPCQuestion::new(RespondTo::Sender, find_node_q_detail);
|
||||||
|
|
||||||
// Send the find_node request
|
// Send the find_node request
|
||||||
let waitable_reply =
|
let waitable_reply = network_result_try!(self.question(dest, find_node_q).await?);
|
||||||
network_result_try!(self.question(dest, question, safety_route).await?);
|
|
||||||
|
|
||||||
// Wait for reply
|
// Wait for reply
|
||||||
let (msg, latency) = match self.wait_for_reply(waitable_reply).await? {
|
let (msg, latency) = match self.wait_for_reply(waitable_reply).await? {
|
||||||
@ -35,7 +33,10 @@ impl RPCProcessor {
|
|||||||
|
|
||||||
// Verify peers are in the correct peer scope
|
// Verify peers are in the correct peer scope
|
||||||
for peer_info in &find_node_a.peers {
|
for peer_info in &find_node_a.peers {
|
||||||
if !self.filter_peer_scope(&peer_info.signed_node_info.node_info) {
|
if !self.filter_node_info(
|
||||||
|
RoutingDomain::PublicInternet,
|
||||||
|
&peer_info.signed_node_info.node_info,
|
||||||
|
) {
|
||||||
return Err(RPCError::invalid_format(
|
return Err(RPCError::invalid_format(
|
||||||
"find_node response has invalid peer scope",
|
"find_node response has invalid peer scope",
|
||||||
));
|
));
|
||||||
@ -61,19 +62,16 @@ impl RPCProcessor {
|
|||||||
|
|
||||||
// add node information for the requesting node to our routing table
|
// add node information for the requesting node to our routing table
|
||||||
let routing_table = self.routing_table();
|
let routing_table = self.routing_table();
|
||||||
|
let rt2 = routing_table.clone();
|
||||||
|
let rt3 = routing_table.clone();
|
||||||
|
|
||||||
// find N nodes closest to the target node in our routing table
|
// find N nodes closest to the target node in our routing table
|
||||||
let own_peer_info = routing_table.get_own_peer_info();
|
|
||||||
let own_peer_info_is_valid = own_peer_info.signed_node_info.is_valid();
|
|
||||||
|
|
||||||
let closest_nodes = routing_table.find_closest_nodes(
|
let closest_nodes = routing_table.find_closest_nodes(
|
||||||
find_node_q.node_id,
|
find_node_q.node_id,
|
||||||
// filter
|
// filter
|
||||||
Some(move |_k, v| {
|
move |_k, v| rt2.filter_has_valid_signed_node_info(RoutingDomain::PublicInternet, v),
|
||||||
RoutingTable::filter_has_valid_signed_node_info(v, own_peer_info_is_valid)
|
|
||||||
}),
|
|
||||||
// transform
|
// transform
|
||||||
move |k, v| RoutingTable::transform_to_peer_info(k, v, &own_peer_info),
|
move |k, v| rt3.transform_to_peer_info(RoutingDomain::PublicInternet, k, v),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Make status answer
|
// Make status answer
|
||||||
@ -83,11 +81,7 @@ impl RPCProcessor {
|
|||||||
|
|
||||||
// Send status answer
|
// Send status answer
|
||||||
let res = self
|
let res = self
|
||||||
.answer(
|
.answer(msg, RPCAnswer::new(RPCAnswerDetail::FindNodeA(find_node_a)))
|
||||||
msg,
|
|
||||||
RPCAnswer::new(RPCAnswerDetail::FindNodeA(find_node_a)),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await?;
|
.await?;
|
||||||
tracing::Span::current().record("res", &tracing::field::display(res));
|
tracing::Span::current().record("res", &tracing::field::display(res));
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -2,19 +2,29 @@ use super::*;
|
|||||||
|
|
||||||
impl RPCProcessor {
|
impl RPCProcessor {
|
||||||
// Sends a our node info to another node
|
// Sends a our node info to another node
|
||||||
// Can be sent via all methods including relays and routes
|
|
||||||
#[instrument(level = "trace", skip(self), ret, err)]
|
#[instrument(level = "trace", skip(self), ret, err)]
|
||||||
pub async fn rpc_call_node_info_update(
|
pub async fn rpc_call_node_info_update(
|
||||||
self,
|
self,
|
||||||
dest: Destination,
|
target: NodeRef,
|
||||||
safety_route: Option<&SafetyRouteSpec>,
|
routing_domain: RoutingDomain,
|
||||||
) -> Result<NetworkResult<()>, RPCError> {
|
) -> Result<NetworkResult<()>, RPCError> {
|
||||||
let signed_node_info = self.routing_table().get_own_signed_node_info();
|
// Get the signed node info for the desired routing domain to send update with
|
||||||
|
let signed_node_info = self
|
||||||
|
.routing_table()
|
||||||
|
.get_own_signed_node_info(routing_domain);
|
||||||
let node_info_update = RPCOperationNodeInfoUpdate { signed_node_info };
|
let node_info_update = RPCOperationNodeInfoUpdate { signed_node_info };
|
||||||
let statement = RPCStatement::new(RPCStatementDetail::NodeInfoUpdate(node_info_update));
|
let statement = RPCStatement::new(RPCStatementDetail::NodeInfoUpdate(node_info_update));
|
||||||
|
|
||||||
// Send the node_info_update request
|
// Send the node_info_update request to the specific routing domain requested
|
||||||
network_result_try!(self.statement(dest, statement, safety_route).await?);
|
network_result_try!(
|
||||||
|
self.statement(
|
||||||
|
Destination::direct(
|
||||||
|
target.filtered_clone(NodeRefFilter::new().with_routing_domain(routing_domain))
|
||||||
|
),
|
||||||
|
statement,
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
);
|
||||||
|
|
||||||
Ok(NetworkResult::value(()))
|
Ok(NetworkResult::value(()))
|
||||||
}
|
}
|
||||||
@ -22,6 +32,7 @@ impl RPCProcessor {
|
|||||||
#[instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), err)]
|
#[instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id), err)]
|
||||||
pub(crate) async fn process_node_info_update(&self, msg: RPCMessage) -> Result<(), RPCError> {
|
pub(crate) async fn process_node_info_update(&self, msg: RPCMessage) -> Result<(), RPCError> {
|
||||||
let sender_node_id = msg.header.envelope.get_sender_id();
|
let sender_node_id = msg.header.envelope.get_sender_id();
|
||||||
|
let routing_domain = msg.header.routing_domain;
|
||||||
|
|
||||||
// Get the statement
|
// Get the statement
|
||||||
let node_info_update = match msg.operation.into_kind() {
|
let node_info_update = match msg.operation.into_kind() {
|
||||||
@ -33,14 +44,13 @@ impl RPCProcessor {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Update our routing table with signed node info
|
// Update our routing table with signed node info
|
||||||
if !self.filter_peer_scope(&node_info_update.signed_node_info.node_info) {
|
if !self.filter_node_info(routing_domain, &node_info_update.signed_node_info.node_info) {
|
||||||
log_rpc!(debug
|
log_rpc!(debug "node info doesn't belong in {:?} routing domain: {}", routing_domain, sender_node_id);
|
||||||
"node_info_update has invalid peer scope from {}", sender_node_id
|
|
||||||
);
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.routing_table().register_node_with_signed_node_info(
|
self.routing_table().register_node_with_signed_node_info(
|
||||||
|
routing_domain,
|
||||||
sender_node_id,
|
sender_node_id,
|
||||||
node_info_update.signed_node_info,
|
node_info_update.signed_node_info,
|
||||||
false,
|
false,
|
||||||
|
@ -7,7 +7,6 @@ impl RPCProcessor {
|
|||||||
pub async fn rpc_call_return_receipt<D: AsRef<[u8]>>(
|
pub async fn rpc_call_return_receipt<D: AsRef<[u8]>>(
|
||||||
self,
|
self,
|
||||||
dest: Destination,
|
dest: Destination,
|
||||||
safety_route: Option<&SafetyRouteSpec>,
|
|
||||||
receipt: D,
|
receipt: D,
|
||||||
) -> Result<NetworkResult<()>, RPCError> {
|
) -> Result<NetworkResult<()>, RPCError> {
|
||||||
let receipt = receipt.as_ref().to_vec();
|
let receipt = receipt.as_ref().to_vec();
|
||||||
@ -16,7 +15,7 @@ impl RPCProcessor {
|
|||||||
let statement = RPCStatement::new(RPCStatementDetail::ReturnReceipt(return_receipt));
|
let statement = RPCStatement::new(RPCStatementDetail::ReturnReceipt(return_receipt));
|
||||||
|
|
||||||
// Send the return_receipt request
|
// Send the return_receipt request
|
||||||
network_result_try!(self.statement(dest, statement, safety_route).await?);
|
network_result_try!(self.statement(dest, statement).await?);
|
||||||
|
|
||||||
Ok(NetworkResult::value(()))
|
Ok(NetworkResult::value(()))
|
||||||
}
|
}
|
||||||
|
@ -7,15 +7,13 @@ impl RPCProcessor {
|
|||||||
pub async fn rpc_call_signal(
|
pub async fn rpc_call_signal(
|
||||||
self,
|
self,
|
||||||
dest: Destination,
|
dest: Destination,
|
||||||
safety_route: Option<&SafetyRouteSpec>,
|
|
||||||
signal_info: SignalInfo,
|
signal_info: SignalInfo,
|
||||||
) -> Result<NetworkResult<()>, RPCError> {
|
) -> Result<NetworkResult<()>, RPCError> {
|
||||||
//let signed_node_info = self.routing_table().get_own_signed_node_info();
|
|
||||||
let signal = RPCOperationSignal { signal_info };
|
let signal = RPCOperationSignal { signal_info };
|
||||||
let statement = RPCStatement::new(RPCStatementDetail::Signal(signal));
|
let statement = RPCStatement::new(RPCStatementDetail::Signal(signal));
|
||||||
|
|
||||||
// Send the signal request
|
// Send the signal request
|
||||||
network_result_try!(self.statement(dest, statement, safety_route).await?);
|
network_result_try!(self.statement(dest, statement).await?);
|
||||||
|
|
||||||
Ok(NetworkResult::value(()))
|
Ok(NetworkResult::value(()))
|
||||||
}
|
}
|
||||||
|
@ -8,14 +8,21 @@ impl RPCProcessor {
|
|||||||
self,
|
self,
|
||||||
peer: NodeRef,
|
peer: NodeRef,
|
||||||
) -> Result<NetworkResult<Answer<SenderInfo>>, RPCError> {
|
) -> Result<NetworkResult<Answer<SenderInfo>>, RPCError> {
|
||||||
let node_status = self.network_manager().generate_node_status();
|
let routing_domain = match peer.best_routing_domain() {
|
||||||
|
Some(rd) => rd,
|
||||||
|
None => {
|
||||||
|
return Ok(NetworkResult::no_connection_other(
|
||||||
|
"no routing domain for peer",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let node_status = self.network_manager().generate_node_status(routing_domain);
|
||||||
let status_q = RPCOperationStatusQ { node_status };
|
let status_q = RPCOperationStatusQ { node_status };
|
||||||
let respond_to = self.make_respond_to_sender(peer.clone());
|
let question = RPCQuestion::new(RespondTo::Sender, RPCQuestionDetail::StatusQ(status_q));
|
||||||
let question = RPCQuestion::new(respond_to, RPCQuestionDetail::StatusQ(status_q));
|
|
||||||
|
|
||||||
// Send the info request
|
// Send the info request
|
||||||
let waitable_reply = network_result_try!(
|
let waitable_reply = network_result_try!(
|
||||||
self.question(Destination::Direct(peer.clone()), question, None)
|
self.question(Destination::direct(peer.clone()), question)
|
||||||
.await?
|
.await?
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -37,28 +44,48 @@ impl RPCProcessor {
|
|||||||
_ => return Err(RPCError::invalid_format("not an answer")),
|
_ => return Err(RPCError::invalid_format("not an answer")),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Ensure the returned node status is the kind for the routing domain we asked for
|
||||||
|
match routing_domain {
|
||||||
|
RoutingDomain::PublicInternet => {
|
||||||
|
if !matches!(status_a.node_status, NodeStatus::PublicInternet(_)) {
|
||||||
|
return Ok(NetworkResult::invalid_message(
|
||||||
|
"node status doesn't match PublicInternet routing domain",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RoutingDomain::LocalNetwork => {
|
||||||
|
if !matches!(status_a.node_status, NodeStatus::LocalNetwork(_)) {
|
||||||
|
return Ok(NetworkResult::invalid_message(
|
||||||
|
"node status doesn't match LocalNetwork routing domain",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update latest node status in routing table
|
// Update latest node status in routing table
|
||||||
peer.operate_mut(|e| {
|
peer.update_node_status(status_a.node_status);
|
||||||
e.update_node_status(status_a.node_status.clone());
|
|
||||||
});
|
|
||||||
|
|
||||||
// Report sender_info IP addresses to network manager
|
// Report sender_info IP addresses to network manager
|
||||||
|
// Don't need to validate these addresses for the current routing domain
|
||||||
|
// the address itself is irrelevant, and the remote node can lie anyway
|
||||||
if let Some(socket_address) = status_a.sender_info.socket_address {
|
if let Some(socket_address) = status_a.sender_info.socket_address {
|
||||||
match send_data_kind {
|
match send_data_kind {
|
||||||
SendDataKind::Direct(connection_descriptor) => {
|
SendDataKind::Direct(connection_descriptor) => match routing_domain {
|
||||||
match connection_descriptor.peer_scope() {
|
RoutingDomain::PublicInternet => self
|
||||||
PeerScope::Global => self.network_manager().report_global_socket_address(
|
.network_manager()
|
||||||
|
.report_public_internet_socket_address(
|
||||||
socket_address,
|
socket_address,
|
||||||
connection_descriptor,
|
connection_descriptor,
|
||||||
peer,
|
peer,
|
||||||
),
|
),
|
||||||
PeerScope::Local => self.network_manager().report_local_socket_address(
|
RoutingDomain::LocalNetwork => {
|
||||||
|
self.network_manager().report_local_network_socket_address(
|
||||||
socket_address,
|
socket_address,
|
||||||
connection_descriptor,
|
connection_descriptor,
|
||||||
peer,
|
peer,
|
||||||
),
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
SendDataKind::Indirect => {
|
SendDataKind::Indirect => {
|
||||||
// Do nothing in this case, as the socket address returned here would be for any node other than ours
|
// Do nothing in this case, as the socket address returned here would be for any node other than ours
|
||||||
}
|
}
|
||||||
@ -77,6 +104,7 @@ impl RPCProcessor {
|
|||||||
#[instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id, res), err)]
|
#[instrument(level = "trace", skip(self, msg), fields(msg.operation.op_id, res), err)]
|
||||||
pub(crate) async fn process_status_q(&self, msg: RPCMessage) -> Result<(), RPCError> {
|
pub(crate) async fn process_status_q(&self, msg: RPCMessage) -> Result<(), RPCError> {
|
||||||
let connection_descriptor = msg.header.connection_descriptor;
|
let connection_descriptor = msg.header.connection_descriptor;
|
||||||
|
let routing_domain = msg.header.routing_domain;
|
||||||
|
|
||||||
// Get the question
|
// Get the question
|
||||||
let status_q = match msg.operation.kind() {
|
let status_q = match msg.operation.kind() {
|
||||||
@ -87,16 +115,30 @@ impl RPCProcessor {
|
|||||||
_ => panic!("not a question"),
|
_ => panic!("not a question"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Ensure the node status from the question is the kind for the routing domain we received the request in
|
||||||
|
match routing_domain {
|
||||||
|
RoutingDomain::PublicInternet => {
|
||||||
|
if !matches!(status_q.node_status, NodeStatus::PublicInternet(_)) {
|
||||||
|
log_rpc!(debug "node status doesn't match PublicInternet routing domain");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RoutingDomain::LocalNetwork => {
|
||||||
|
if !matches!(status_q.node_status, NodeStatus::LocalNetwork(_)) {
|
||||||
|
log_rpc!(debug "node status doesn't match LocalNetwork routing domain");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// update node status for the requesting node to our routing table
|
// update node status for the requesting node to our routing table
|
||||||
if let Some(sender_nr) = msg.opt_sender_nr.clone() {
|
if let Some(sender_nr) = msg.opt_sender_nr.clone() {
|
||||||
// Update latest node status in routing table for the statusq sender
|
// Update latest node status in routing table for the statusq sender
|
||||||
sender_nr.operate_mut(|e| {
|
sender_nr.update_node_status(status_q.node_status.clone());
|
||||||
e.update_node_status(status_q.node_status.clone());
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make status answer
|
// Make status answer
|
||||||
let node_status = self.network_manager().generate_node_status();
|
let node_status = self.network_manager().generate_node_status(routing_domain);
|
||||||
|
|
||||||
// Get the peer address in the returned sender info
|
// Get the peer address in the returned sender info
|
||||||
let sender_info = SenderInfo {
|
let sender_info = SenderInfo {
|
||||||
@ -110,11 +152,7 @@ impl RPCProcessor {
|
|||||||
|
|
||||||
// Send status answer
|
// Send status answer
|
||||||
let res = self
|
let res = self
|
||||||
.answer(
|
.answer(msg, RPCAnswer::new(RPCAnswerDetail::StatusA(status_a)))
|
||||||
msg,
|
|
||||||
RPCAnswer::new(RPCAnswerDetail::StatusA(status_a)),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await?;
|
.await?;
|
||||||
tracing::Span::current().record("res", &tracing::field::display(res));
|
tracing::Span::current().record("res", &tracing::field::display(res));
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -32,7 +32,7 @@ impl RPCProcessor {
|
|||||||
|
|
||||||
// Send the validate_dial_info request
|
// Send the validate_dial_info request
|
||||||
// This can only be sent directly, as relays can not validate dial info
|
// This can only be sent directly, as relays can not validate dial info
|
||||||
network_result_value_or_log!(debug self.statement(Destination::Direct(peer), statement, None)
|
network_result_value_or_log!(debug self.statement(Destination::direct(peer), statement)
|
||||||
.await? => {
|
.await? => {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
@ -81,6 +81,7 @@ impl RPCProcessor {
|
|||||||
// an ipv6 address
|
// an ipv6 address
|
||||||
let routing_table = self.routing_table();
|
let routing_table = self.routing_table();
|
||||||
let sender_id = msg.header.envelope.get_sender_id();
|
let sender_id = msg.header.envelope.get_sender_id();
|
||||||
|
let routing_domain = msg.header.routing_domain;
|
||||||
let node_count = {
|
let node_count = {
|
||||||
let c = self.config.get();
|
let c = self.config.get();
|
||||||
c.network.dht.max_find_node_count as usize
|
c.network.dht.max_find_node_count as usize
|
||||||
@ -88,15 +89,18 @@ impl RPCProcessor {
|
|||||||
|
|
||||||
// Filter on nodes that can validate dial info, and can reach a specific dial info
|
// Filter on nodes that can validate dial info, and can reach a specific dial info
|
||||||
let outbound_dial_info_entry_filter =
|
let outbound_dial_info_entry_filter =
|
||||||
RoutingTable::make_outbound_dial_info_entry_filter(dial_info.clone());
|
RoutingTable::make_outbound_dial_info_entry_filter(
|
||||||
|
routing_domain,
|
||||||
|
dial_info.clone(),
|
||||||
|
);
|
||||||
let will_validate_dial_info_filter = |e: &BucketEntryInner| {
|
let will_validate_dial_info_filter = |e: &BucketEntryInner| {
|
||||||
if let Some(status) = &e.peer_stats().status {
|
if let Some(status) = &e.node_status(routing_domain) {
|
||||||
status.will_validate_dial_info
|
status.will_validate_dial_info()
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let filter = RoutingTable::combine_filters(
|
let filter = RoutingTable::combine_entry_filters(
|
||||||
outbound_dial_info_entry_filter,
|
outbound_dial_info_entry_filter,
|
||||||
will_validate_dial_info_filter,
|
will_validate_dial_info_filter,
|
||||||
);
|
);
|
||||||
@ -126,7 +130,7 @@ impl RPCProcessor {
|
|||||||
|
|
||||||
// Send the validate_dial_info request
|
// Send the validate_dial_info request
|
||||||
// This can only be sent directly, as relays can not validate dial info
|
// This can only be sent directly, as relays can not validate dial info
|
||||||
network_result_value_or_log!(debug self.statement(Destination::Direct(peer), statement, None)
|
network_result_value_or_log!(debug self.statement(Destination::direct(peer), statement)
|
||||||
.await? => {
|
.await? => {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
@ -224,7 +224,6 @@ fn config_callback(key: String) -> ConfigCallbackReturn {
|
|||||||
"network.upnp" => Ok(Box::new(false)),
|
"network.upnp" => Ok(Box::new(false)),
|
||||||
"network.natpmp" => Ok(Box::new(false)),
|
"network.natpmp" => Ok(Box::new(false)),
|
||||||
"network.detect_address_changes" => Ok(Box::new(true)),
|
"network.detect_address_changes" => Ok(Box::new(true)),
|
||||||
"network.enable_local_peer_scope" => Ok(Box::new(false)),
|
|
||||||
"network.restricted_nat_retries" => Ok(Box::new(3u32)),
|
"network.restricted_nat_retries" => Ok(Box::new(3u32)),
|
||||||
"network.tls.certificate_path" => Ok(Box::new(get_certfile_path())),
|
"network.tls.certificate_path" => Ok(Box::new(get_certfile_path())),
|
||||||
"network.tls.private_key_path" => Ok(Box::new(get_keyfile_path())),
|
"network.tls.private_key_path" => Ok(Box::new(get_keyfile_path())),
|
||||||
@ -354,7 +353,6 @@ pub async fn test_config() {
|
|||||||
assert_eq!(inner.network.upnp, false);
|
assert_eq!(inner.network.upnp, false);
|
||||||
assert_eq!(inner.network.natpmp, false);
|
assert_eq!(inner.network.natpmp, false);
|
||||||
assert_eq!(inner.network.detect_address_changes, true);
|
assert_eq!(inner.network.detect_address_changes, true);
|
||||||
assert_eq!(inner.network.enable_local_peer_scope, false);
|
|
||||||
assert_eq!(inner.network.restricted_nat_retries, 3u32);
|
assert_eq!(inner.network.restricted_nat_retries, 3u32);
|
||||||
assert_eq!(inner.network.tls.certificate_path, get_certfile_path());
|
assert_eq!(inner.network.tls.certificate_path, get_certfile_path());
|
||||||
assert_eq!(inner.network.tls.private_key_path, get_keyfile_path());
|
assert_eq!(inner.network.tls.private_key_path, get_keyfile_path());
|
||||||
|
@ -45,6 +45,16 @@ fn get_address_type(text: &str) -> Option<AddressType> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn get_routing_domain(text: &str) -> Option<RoutingDomain> {
|
||||||
|
let lctext = text.to_ascii_lowercase();
|
||||||
|
if "publicinternet".starts_with(&lctext) {
|
||||||
|
Some(RoutingDomain::PublicInternet)
|
||||||
|
} else if "localnetwork".starts_with(&lctext) {
|
||||||
|
Some(RoutingDomain::LocalNetwork)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn get_debug_argument<T, G: FnOnce(&str) -> Option<T>>(
|
fn get_debug_argument<T, G: FnOnce(&str) -> Option<T>>(
|
||||||
value: &str,
|
value: &str,
|
||||||
@ -294,25 +304,41 @@ impl VeilidAPI {
|
|||||||
None => return Ok("Node id not found in routing table".to_owned()),
|
None => return Ok("Node id not found in routing table".to_owned()),
|
||||||
};
|
};
|
||||||
|
|
||||||
if args.len() >= 2 {
|
let mut ai = 1;
|
||||||
let pt = get_debug_argument_at(
|
let mut routing_domain = None;
|
||||||
|
while ai < args.len() {
|
||||||
|
if let Ok(pt) = get_debug_argument_at(
|
||||||
&args,
|
&args,
|
||||||
1,
|
ai,
|
||||||
"debug_contact",
|
"debug_contact",
|
||||||
"protocol_type",
|
"protocol_type",
|
||||||
get_protocol_type,
|
get_protocol_type,
|
||||||
)?;
|
) {
|
||||||
nr.merge_filter(DialInfoFilter::all().with_protocol_type(pt));
|
nr.merge_filter(NodeRefFilter::new().with_protocol_type(pt));
|
||||||
if args.len() >= 3 {
|
} else if let Ok(at) =
|
||||||
let at = get_debug_argument_at(
|
get_debug_argument_at(&args, ai, "debug_contact", "address_type", get_address_type)
|
||||||
|
{
|
||||||
|
nr.merge_filter(NodeRefFilter::new().with_address_type(at));
|
||||||
|
} else if let Ok(rd) = get_debug_argument_at(
|
||||||
&args,
|
&args,
|
||||||
2,
|
ai,
|
||||||
"debug_contact",
|
"debug_contact",
|
||||||
"address_type",
|
"routing_domain",
|
||||||
get_address_type,
|
get_routing_domain,
|
||||||
)?;
|
) {
|
||||||
nr.merge_filter(DialInfoFilter::all().with_address_type(at));
|
if routing_domain.is_none() {
|
||||||
|
routing_domain = Some(rd);
|
||||||
|
} else {
|
||||||
|
return Ok("Multiple routing domains specified".to_owned());
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return Ok(format!("Invalid argument specified: {}", args[ai]));
|
||||||
|
}
|
||||||
|
ai += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(routing_domain) = routing_domain {
|
||||||
|
nr.merge_filter(NodeRefFilter::new().with_routing_domain(routing_domain))
|
||||||
}
|
}
|
||||||
|
|
||||||
let cm = network_manager.get_contact_method(nr);
|
let cm = network_manager.get_contact_method(nr);
|
||||||
@ -321,33 +347,51 @@ impl VeilidAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn debug_ping(&self, args: String) -> Result<String, VeilidAPIError> {
|
async fn debug_ping(&self, args: String) -> Result<String, VeilidAPIError> {
|
||||||
|
let netman = self.network_manager()?;
|
||||||
|
let routing_table = netman.routing_table();
|
||||||
|
let rpc = netman.rpc_processor();
|
||||||
|
|
||||||
let args: Vec<String> = args.split_whitespace().map(|s| s.to_owned()).collect();
|
let args: Vec<String> = args.split_whitespace().map(|s| s.to_owned()).collect();
|
||||||
|
|
||||||
let node_id = get_debug_argument_at(&args, 0, "debug_ping", "node_id", get_dht_key)?;
|
let node_id = get_debug_argument_at(&args, 0, "debug_ping", "node_id", get_dht_key)?;
|
||||||
|
|
||||||
let routing_table = self.network_manager()?.routing_table();
|
|
||||||
let mut nr = match routing_table.lookup_node_ref(node_id) {
|
let mut nr = match routing_table.lookup_node_ref(node_id) {
|
||||||
Some(nr) => nr,
|
Some(nr) => nr,
|
||||||
None => return Ok("Node id not found in routing table".to_owned()),
|
None => return Ok("Node id not found in routing table".to_owned()),
|
||||||
};
|
};
|
||||||
|
|
||||||
if args.len() >= 2 {
|
let mut ai = 1;
|
||||||
let pt =
|
let mut routing_domain = None;
|
||||||
get_debug_argument_at(&args, 1, "debug_ping", "protocol_type", get_protocol_type)?;
|
while ai < args.len() {
|
||||||
nr.merge_filter(DialInfoFilter::all().with_protocol_type(pt));
|
if let Ok(pt) =
|
||||||
if args.len() >= 3 {
|
get_debug_argument_at(&args, ai, "debug_ping", "protocol_type", get_protocol_type)
|
||||||
let at = get_debug_argument_at(
|
{
|
||||||
|
nr.merge_filter(NodeRefFilter::new().with_protocol_type(pt));
|
||||||
|
} else if let Ok(at) =
|
||||||
|
get_debug_argument_at(&args, ai, "debug_ping", "address_type", get_address_type)
|
||||||
|
{
|
||||||
|
nr.merge_filter(NodeRefFilter::new().with_address_type(at));
|
||||||
|
} else if let Ok(rd) = get_debug_argument_at(
|
||||||
&args,
|
&args,
|
||||||
2,
|
ai,
|
||||||
"debug_ping",
|
"debug_ping",
|
||||||
"address_type",
|
"routing_domain",
|
||||||
get_address_type,
|
get_routing_domain,
|
||||||
)?;
|
) {
|
||||||
nr.merge_filter(DialInfoFilter::all().with_address_type(at));
|
if routing_domain.is_none() {
|
||||||
|
routing_domain = Some(rd);
|
||||||
|
} else {
|
||||||
|
return Ok("Multiple routing domains specified".to_owned());
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return Ok(format!("Invalid argument specified: {}", args[ai]));
|
||||||
|
}
|
||||||
|
ai += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
let rpc = self.network_manager()?.rpc_processor();
|
if let Some(routing_domain) = routing_domain {
|
||||||
|
nr.merge_filter(NodeRefFilter::new().with_routing_domain(routing_domain))
|
||||||
|
}
|
||||||
|
|
||||||
// Dump routing table entry
|
// Dump routing table entry
|
||||||
let out = match rpc
|
let out = match rpc
|
||||||
@ -383,7 +427,7 @@ impl VeilidAPI {
|
|||||||
attach
|
attach
|
||||||
detach
|
detach
|
||||||
restart network
|
restart network
|
||||||
ping <node_id> [protocol_type [address_type]]
|
ping <node_id> [protocol_type][address_type][routing_domain]
|
||||||
contact <node_id> [protocol_type [address_type]]
|
contact <node_id> [protocol_type [address_type]]
|
||||||
"#
|
"#
|
||||||
.to_owned())
|
.to_owned())
|
||||||
|
@ -215,22 +215,33 @@ impl fmt::Display for VeilidLogLevel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub struct VeilidStateLog {
|
pub struct VeilidStateLog {
|
||||||
pub log_level: VeilidLogLevel,
|
pub log_level: VeilidLogLevel,
|
||||||
pub message: String,
|
pub message: String,
|
||||||
|
pub backtrace: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub struct VeilidStateAttachment {
|
pub struct VeilidStateAttachment {
|
||||||
pub state: AttachmentState,
|
pub state: AttachmentState,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
|
pub struct PeerTableData {
|
||||||
|
pub node_id: DHTKey,
|
||||||
|
pub peer_address: PeerAddress,
|
||||||
|
pub peer_stats: PeerStats,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub struct VeilidStateNetwork {
|
pub struct VeilidStateNetwork {
|
||||||
pub started: bool,
|
pub started: bool,
|
||||||
|
#[serde(with = "json_as_string")]
|
||||||
pub bps_down: u64,
|
pub bps_down: u64,
|
||||||
|
#[serde(with = "json_as_string")]
|
||||||
pub bps_up: u64,
|
pub bps_up: u64,
|
||||||
|
pub peers: Vec<PeerTableData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
@ -390,8 +401,12 @@ impl NetworkClass {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// RoutingDomain-specific status for each node
|
||||||
|
/// is returned by the StatusA call
|
||||||
|
|
||||||
|
/// PublicInternet RoutingDomain Status
|
||||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||||
pub struct NodeStatus {
|
pub struct PublicInternetNodeStatus {
|
||||||
pub will_route: bool,
|
pub will_route: bool,
|
||||||
pub will_tunnel: bool,
|
pub will_tunnel: bool,
|
||||||
pub will_signal: bool,
|
pub will_signal: bool,
|
||||||
@ -399,6 +414,51 @@ pub struct NodeStatus {
|
|||||||
pub will_validate_dial_info: bool,
|
pub will_validate_dial_info: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||||
|
pub struct LocalNetworkNodeStatus {
|
||||||
|
pub will_relay: bool,
|
||||||
|
pub will_validate_dial_info: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub enum NodeStatus {
|
||||||
|
PublicInternet(PublicInternetNodeStatus),
|
||||||
|
LocalNetwork(LocalNetworkNodeStatus),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NodeStatus {
|
||||||
|
pub fn will_route(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
NodeStatus::PublicInternet(pi) => pi.will_route,
|
||||||
|
NodeStatus::LocalNetwork(_) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn will_tunnel(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
NodeStatus::PublicInternet(pi) => pi.will_tunnel,
|
||||||
|
NodeStatus::LocalNetwork(_) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn will_signal(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
NodeStatus::PublicInternet(pi) => pi.will_signal,
|
||||||
|
NodeStatus::LocalNetwork(_) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn will_relay(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
NodeStatus::PublicInternet(pi) => pi.will_relay,
|
||||||
|
NodeStatus::LocalNetwork(ln) => ln.will_relay,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn will_validate_dial_info(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
NodeStatus::PublicInternet(pi) => pi.will_validate_dial_info,
|
||||||
|
NodeStatus::LocalNetwork(ln) => ln.will_validate_dial_info,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct NodeInfo {
|
pub struct NodeInfo {
|
||||||
pub network_class: NetworkClass,
|
pub network_class: NetworkClass,
|
||||||
@ -411,9 +471,6 @@ pub struct NodeInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl NodeInfo {
|
impl NodeInfo {
|
||||||
pub fn is_valid(&self) -> bool {
|
|
||||||
!matches!(self.network_class, NetworkClass::Invalid)
|
|
||||||
}
|
|
||||||
pub fn first_filtered_dial_info_detail<F>(&self, filter: F) -> Option<DialInfoDetail>
|
pub fn first_filtered_dial_info_detail<F>(&self, filter: F) -> Option<DialInfoDetail>
|
||||||
where
|
where
|
||||||
F: Fn(&DialInfoDetail) -> bool,
|
F: Fn(&DialInfoDetail) -> bool,
|
||||||
@ -502,43 +559,6 @@ impl NodeInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub struct LocalNodeInfo {
|
|
||||||
pub dial_info_list: Vec<DialInfo>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LocalNodeInfo {
|
|
||||||
pub fn first_filtered_dial_info<F>(&self, filter: F) -> Option<DialInfo>
|
|
||||||
where
|
|
||||||
F: Fn(&DialInfo) -> bool,
|
|
||||||
{
|
|
||||||
for di in &self.dial_info_list {
|
|
||||||
if filter(di) {
|
|
||||||
return Some(di.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn all_filtered_dial_info<F>(&self, filter: F) -> Vec<DialInfo>
|
|
||||||
where
|
|
||||||
F: Fn(&DialInfo) -> bool,
|
|
||||||
{
|
|
||||||
let mut dial_info_list = Vec::new();
|
|
||||||
|
|
||||||
for di in &self.dial_info_list {
|
|
||||||
if filter(di) {
|
|
||||||
dial_info_list.push(di.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dial_info_list
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn has_dial_info(&self) -> bool {
|
|
||||||
!self.dial_info_list.is_empty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::derive_hash_xor_eq)]
|
#[allow(clippy::derive_hash_xor_eq)]
|
||||||
#[derive(Debug, PartialOrd, Ord, Hash, Serialize, Deserialize, EnumSetType)]
|
#[derive(Debug, PartialOrd, Ord, Hash, Serialize, Deserialize, EnumSetType)]
|
||||||
// Keep member order appropriate for sorting < preference
|
// Keep member order appropriate for sorting < preference
|
||||||
@ -590,13 +610,23 @@ pub enum AddressType {
|
|||||||
}
|
}
|
||||||
pub type AddressTypeSet = EnumSet<AddressType>;
|
pub type AddressTypeSet = EnumSet<AddressType>;
|
||||||
|
|
||||||
|
// Routing domain here is listed in order of preference, keep in order
|
||||||
#[allow(clippy::derive_hash_xor_eq)]
|
#[allow(clippy::derive_hash_xor_eq)]
|
||||||
#[derive(Debug, Ord, PartialOrd, Hash, Serialize, Deserialize, EnumSetType)]
|
#[derive(Debug, Ord, PartialOrd, Hash, Serialize, Deserialize, EnumSetType)]
|
||||||
pub enum PeerScope {
|
pub enum RoutingDomain {
|
||||||
Global,
|
LocalNetwork = 0,
|
||||||
Local,
|
PublicInternet = 1,
|
||||||
}
|
}
|
||||||
pub type PeerScopeSet = EnumSet<PeerScope>;
|
impl RoutingDomain {
|
||||||
|
pub const fn count() -> usize {
|
||||||
|
2
|
||||||
|
}
|
||||||
|
pub const fn all() -> [RoutingDomain; RoutingDomain::count()] {
|
||||||
|
// Routing domain here is listed in order of preference, keep in order
|
||||||
|
[RoutingDomain::LocalNetwork, RoutingDomain::PublicInternet]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub type RoutingDomainSet = EnumSet<RoutingDomain>;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash, Serialize, Deserialize)]
|
||||||
pub enum Address {
|
pub enum Address {
|
||||||
@ -687,6 +717,15 @@ impl Address {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Address {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Address::IPV4(v4) => write!(f, "{}", v4),
|
||||||
|
Address::IPV6(v6) => write!(f, "{}", v6),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FromStr for Address {
|
impl FromStr for Address {
|
||||||
type Err = VeilidAPIError;
|
type Err = VeilidAPIError;
|
||||||
fn from_str(host: &str) -> Result<Address, VeilidAPIError> {
|
fn from_str(host: &str) -> Result<Address, VeilidAPIError> {
|
||||||
@ -763,7 +802,6 @@ impl FromStr for SocketAddress {
|
|||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct DialInfoFilter {
|
pub struct DialInfoFilter {
|
||||||
pub peer_scope_set: PeerScopeSet,
|
|
||||||
pub protocol_type_set: ProtocolTypeSet,
|
pub protocol_type_set: ProtocolTypeSet,
|
||||||
pub address_type_set: AddressTypeSet,
|
pub address_type_set: AddressTypeSet,
|
||||||
}
|
}
|
||||||
@ -771,7 +809,6 @@ pub struct DialInfoFilter {
|
|||||||
impl Default for DialInfoFilter {
|
impl Default for DialInfoFilter {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
peer_scope_set: PeerScopeSet::all(),
|
|
||||||
protocol_type_set: ProtocolTypeSet::all(),
|
protocol_type_set: ProtocolTypeSet::all(),
|
||||||
address_type_set: AddressTypeSet::all(),
|
address_type_set: AddressTypeSet::all(),
|
||||||
}
|
}
|
||||||
@ -781,28 +818,6 @@ impl Default for DialInfoFilter {
|
|||||||
impl DialInfoFilter {
|
impl DialInfoFilter {
|
||||||
pub fn all() -> Self {
|
pub fn all() -> Self {
|
||||||
Self {
|
Self {
|
||||||
peer_scope_set: PeerScopeSet::all(),
|
|
||||||
protocol_type_set: ProtocolTypeSet::all(),
|
|
||||||
address_type_set: AddressTypeSet::all(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn global() -> Self {
|
|
||||||
Self {
|
|
||||||
peer_scope_set: PeerScopeSet::only(PeerScope::Global),
|
|
||||||
protocol_type_set: ProtocolTypeSet::all(),
|
|
||||||
address_type_set: AddressTypeSet::all(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn local() -> Self {
|
|
||||||
Self {
|
|
||||||
peer_scope_set: PeerScopeSet::only(PeerScope::Local),
|
|
||||||
protocol_type_set: ProtocolTypeSet::all(),
|
|
||||||
address_type_set: AddressTypeSet::all(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn scoped(peer_scope: PeerScope) -> Self {
|
|
||||||
Self {
|
|
||||||
peer_scope_set: PeerScopeSet::only(peer_scope),
|
|
||||||
protocol_type_set: ProtocolTypeSet::all(),
|
protocol_type_set: ProtocolTypeSet::all(),
|
||||||
address_type_set: AddressTypeSet::all(),
|
address_type_set: AddressTypeSet::all(),
|
||||||
}
|
}
|
||||||
@ -823,30 +838,28 @@ impl DialInfoFilter {
|
|||||||
self.address_type_set = address_set;
|
self.address_type_set = address_set;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
pub fn filtered(mut self, other_dif: DialInfoFilter) -> Self {
|
pub fn filtered(mut self, other_dif: &DialInfoFilter) -> Self {
|
||||||
self.peer_scope_set &= other_dif.peer_scope_set;
|
|
||||||
self.protocol_type_set &= other_dif.protocol_type_set;
|
self.protocol_type_set &= other_dif.protocol_type_set;
|
||||||
self.address_type_set &= other_dif.address_type_set;
|
self.address_type_set &= other_dif.address_type_set;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
pub fn is_dead(&self) -> bool {
|
pub fn is_dead(&self) -> bool {
|
||||||
self.peer_scope_set.is_empty()
|
self.protocol_type_set.is_empty() || self.address_type_set.is_empty()
|
||||||
|| self.protocol_type_set.is_empty()
|
|
||||||
|| self.address_type_set.is_empty()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for DialInfoFilter {
|
impl fmt::Debug for DialInfoFilter {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
let mut out = String::new();
|
let mut out = String::new();
|
||||||
if self.peer_scope_set != PeerScopeSet::all() {
|
|
||||||
out += &format!("+{:?}", self.peer_scope_set);
|
|
||||||
}
|
|
||||||
if self.protocol_type_set != ProtocolTypeSet::all() {
|
if self.protocol_type_set != ProtocolTypeSet::all() {
|
||||||
out += &format!("+{:?}", self.protocol_type_set);
|
out += &format!("+{:?}", self.protocol_type_set);
|
||||||
|
} else {
|
||||||
|
out += "*";
|
||||||
}
|
}
|
||||||
if self.address_type_set != AddressTypeSet::all() {
|
if self.address_type_set != AddressTypeSet::all() {
|
||||||
out += &format!("+{:?}", self.address_type_set);
|
out += &format!("+{:?}", self.address_type_set);
|
||||||
|
} else {
|
||||||
|
out += "*";
|
||||||
}
|
}
|
||||||
write!(f, "[{}]", out)
|
write!(f, "[{}]", out)
|
||||||
}
|
}
|
||||||
@ -1104,6 +1117,14 @@ impl DialInfo {
|
|||||||
pub fn address_type(&self) -> AddressType {
|
pub fn address_type(&self) -> AddressType {
|
||||||
self.socket_address().address_type()
|
self.socket_address().address_type()
|
||||||
}
|
}
|
||||||
|
pub fn address(&self) -> Address {
|
||||||
|
match self {
|
||||||
|
Self::UDP(di) => di.socket_address.address,
|
||||||
|
Self::TCP(di) => di.socket_address.address,
|
||||||
|
Self::WS(di) => di.socket_address.address,
|
||||||
|
Self::WSS(di) => di.socket_address.address,
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn socket_address(&self) -> SocketAddress {
|
pub fn socket_address(&self) -> SocketAddress {
|
||||||
match self {
|
match self {
|
||||||
Self::UDP(di) => di.socket_address,
|
Self::UDP(di) => di.socket_address,
|
||||||
@ -1160,49 +1181,15 @@ impl DialInfo {
|
|||||||
Self::WSS(di) => Some(format!("wss://{}", di.request)),
|
Self::WSS(di) => Some(format!("wss://{}", di.request)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn is_global(&self) -> bool {
|
|
||||||
self.socket_address().address().is_global()
|
|
||||||
}
|
|
||||||
pub fn is_local(&self) -> bool {
|
|
||||||
self.socket_address().address().is_local()
|
|
||||||
}
|
|
||||||
pub fn is_valid(&self) -> bool {
|
pub fn is_valid(&self) -> bool {
|
||||||
let socket_address = self.socket_address();
|
let socket_address = self.socket_address();
|
||||||
let address = socket_address.address();
|
let address = socket_address.address();
|
||||||
let port = socket_address.port();
|
let port = socket_address.port();
|
||||||
(address.is_global() || address.is_local()) && port > 0
|
(address.is_global() || address.is_local()) && port > 0
|
||||||
}
|
}
|
||||||
pub fn peer_scope(&self) -> Option<PeerScope> {
|
|
||||||
let addr = self.socket_address().address();
|
|
||||||
if addr.is_global() {
|
|
||||||
return Some(PeerScope::Global);
|
|
||||||
}
|
|
||||||
if addr.is_local() {
|
|
||||||
return Some(PeerScope::Local);
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
pub fn matches_peer_scope(&self, scope: PeerScopeSet) -> bool {
|
|
||||||
if let Some(ps) = self.peer_scope() {
|
|
||||||
scope.contains(ps)
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn make_filter(&self, scoped: bool) -> DialInfoFilter {
|
pub fn make_filter(&self) -> DialInfoFilter {
|
||||||
DialInfoFilter {
|
DialInfoFilter {
|
||||||
peer_scope_set: if scoped {
|
|
||||||
if self.is_global() {
|
|
||||||
PeerScopeSet::only(PeerScope::Global)
|
|
||||||
} else if self.is_local() {
|
|
||||||
PeerScopeSet::only(PeerScope::Local)
|
|
||||||
} else {
|
|
||||||
PeerScopeSet::empty()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
PeerScopeSet::all()
|
|
||||||
},
|
|
||||||
protocol_type_set: ProtocolTypeSet::only(self.protocol_type()),
|
protocol_type_set: ProtocolTypeSet::only(self.protocol_type()),
|
||||||
address_type_set: AddressTypeSet::only(self.address_type()),
|
address_type_set: AddressTypeSet::only(self.address_type()),
|
||||||
}
|
}
|
||||||
@ -1389,9 +1376,6 @@ impl DialInfo {
|
|||||||
|
|
||||||
impl MatchesDialInfoFilter for DialInfo {
|
impl MatchesDialInfoFilter for DialInfo {
|
||||||
fn matches_filter(&self, filter: &DialInfoFilter) -> bool {
|
fn matches_filter(&self, filter: &DialInfoFilter) -> bool {
|
||||||
if !self.matches_peer_scope(filter.peer_scope_set) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if !filter.protocol_type_set.contains(self.protocol_type()) {
|
if !filter.protocol_type_set.contains(self.protocol_type()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1464,8 +1448,8 @@ impl SignedNodeInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_valid(&self) -> bool {
|
pub fn has_valid_signature(&self) -> bool {
|
||||||
self.signature.valid && self.node_info.is_valid()
|
self.signature.valid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1486,8 +1470,9 @@ impl PeerInfo {
|
|||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
|
||||||
pub struct PeerAddress {
|
pub struct PeerAddress {
|
||||||
socket_address: SocketAddress,
|
|
||||||
protocol_type: ProtocolType,
|
protocol_type: ProtocolType,
|
||||||
|
#[serde(with = "json_as_string")]
|
||||||
|
socket_address: SocketAddress,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PeerAddress {
|
impl PeerAddress {
|
||||||
@ -1522,40 +1507,17 @@ pub struct ConnectionDescriptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ConnectionDescriptor {
|
impl ConnectionDescriptor {
|
||||||
fn validate_peer_scope(remote: PeerAddress) -> Result<(), VeilidAPIError> {
|
pub fn new(remote: PeerAddress, local: SocketAddress) -> Self {
|
||||||
// Verify address is in one of our peer scopes we care about
|
Self {
|
||||||
let addr = remote.socket_address.address();
|
|
||||||
|
|
||||||
// Allow WASM to have unresolved addresses, for bootstraps
|
|
||||||
cfg_if::cfg_if! {
|
|
||||||
if #[cfg(target_arch = "wasm32")] {
|
|
||||||
if addr.is_unspecified() {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !addr.is_global() && !addr.is_local() {
|
|
||||||
return Err(VeilidAPIError::generic(format!(
|
|
||||||
"not a valid peer scope: {:?}",
|
|
||||||
addr
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new(remote: PeerAddress, local: SocketAddress) -> Result<Self, VeilidAPIError> {
|
|
||||||
Self::validate_peer_scope(remote)?;
|
|
||||||
Ok(Self {
|
|
||||||
remote,
|
remote,
|
||||||
local: Some(local),
|
local: Some(local),
|
||||||
})
|
|
||||||
}
|
}
|
||||||
pub fn new_no_local(remote: PeerAddress) -> Result<Self, VeilidAPIError> {
|
}
|
||||||
Self::validate_peer_scope(remote)?;
|
pub fn new_no_local(remote: PeerAddress) -> Self {
|
||||||
Ok(Self {
|
Self {
|
||||||
remote,
|
remote,
|
||||||
local: None,
|
local: None,
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
pub fn remote(&self) -> PeerAddress {
|
pub fn remote(&self) -> PeerAddress {
|
||||||
self.remote
|
self.remote
|
||||||
@ -1572,36 +1534,15 @@ impl ConnectionDescriptor {
|
|||||||
pub fn address_type(&self) -> AddressType {
|
pub fn address_type(&self) -> AddressType {
|
||||||
self.remote.address_type()
|
self.remote.address_type()
|
||||||
}
|
}
|
||||||
pub fn peer_scope(&self) -> PeerScope {
|
|
||||||
let addr = self.remote.socket_address.address();
|
|
||||||
// Allow WASM to have unresolved addresses, for bootstraps
|
|
||||||
cfg_if::cfg_if! {
|
|
||||||
if #[cfg(target_arch = "wasm32")] {
|
|
||||||
if addr.is_unspecified() {
|
|
||||||
return PeerScope::Global;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if addr.is_global() {
|
|
||||||
return PeerScope::Global;
|
|
||||||
}
|
|
||||||
PeerScope::Local
|
|
||||||
}
|
|
||||||
pub fn make_dial_info_filter(&self) -> DialInfoFilter {
|
pub fn make_dial_info_filter(&self) -> DialInfoFilter {
|
||||||
DialInfoFilter::scoped(self.peer_scope())
|
DialInfoFilter::all()
|
||||||
.with_protocol_type(self.protocol_type())
|
.with_protocol_type(self.protocol_type())
|
||||||
.with_address_type(self.address_type())
|
.with_address_type(self.address_type())
|
||||||
}
|
}
|
||||||
pub fn matches_peer_scope(&self, scope: PeerScopeSet) -> bool {
|
|
||||||
scope.contains(self.peer_scope())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MatchesDialInfoFilter for ConnectionDescriptor {
|
impl MatchesDialInfoFilter for ConnectionDescriptor {
|
||||||
fn matches_filter(&self, filter: &DialInfoFilter) -> bool {
|
fn matches_filter(&self, filter: &DialInfoFilter) -> bool {
|
||||||
if !self.matches_peer_scope(filter.peer_scope_set) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if !filter.protocol_type_set.contains(self.protocol_type()) {
|
if !filter.protocol_type_set.contains(self.protocol_type()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1649,46 +1590,56 @@ impl FromStr for NodeDialInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub struct LatencyStats {
|
pub struct LatencyStats {
|
||||||
|
#[serde(with = "json_as_string")]
|
||||||
pub fastest: u64, // fastest latency in the ROLLING_LATENCIES_SIZE last latencies
|
pub fastest: u64, // fastest latency in the ROLLING_LATENCIES_SIZE last latencies
|
||||||
|
#[serde(with = "json_as_string")]
|
||||||
pub average: u64, // average latency over the ROLLING_LATENCIES_SIZE last latencies
|
pub average: u64, // average latency over the ROLLING_LATENCIES_SIZE last latencies
|
||||||
|
#[serde(with = "json_as_string")]
|
||||||
pub slowest: u64, // slowest latency in the ROLLING_LATENCIES_SIZE last latencies
|
pub slowest: u64, // slowest latency in the ROLLING_LATENCIES_SIZE last latencies
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub struct TransferStats {
|
pub struct TransferStats {
|
||||||
|
#[serde(with = "json_as_string")]
|
||||||
pub total: u64, // total amount transferred ever
|
pub total: u64, // total amount transferred ever
|
||||||
|
#[serde(with = "json_as_string")]
|
||||||
pub maximum: u64, // maximum rate over the ROLLING_TRANSFERS_SIZE last amounts
|
pub maximum: u64, // maximum rate over the ROLLING_TRANSFERS_SIZE last amounts
|
||||||
|
#[serde(with = "json_as_string")]
|
||||||
pub average: u64, // average rate over the ROLLING_TRANSFERS_SIZE last amounts
|
pub average: u64, // average rate over the ROLLING_TRANSFERS_SIZE last amounts
|
||||||
|
#[serde(with = "json_as_string")]
|
||||||
pub minimum: u64, // minimum rate over the ROLLING_TRANSFERS_SIZE last amounts
|
pub minimum: u64, // minimum rate over the ROLLING_TRANSFERS_SIZE last amounts
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub struct TransferStatsDownUp {
|
pub struct TransferStatsDownUp {
|
||||||
pub down: TransferStats,
|
pub down: TransferStats,
|
||||||
pub up: TransferStats,
|
pub up: TransferStats,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub struct RPCStats {
|
pub struct RPCStats {
|
||||||
pub messages_sent: u32, // number of rpcs that have been sent in the total_time range
|
pub messages_sent: u32, // number of rpcs that have been sent in the total_time range
|
||||||
pub messages_rcvd: u32, // number of rpcs that have been received in the total_time range
|
pub messages_rcvd: u32, // number of rpcs that have been received in the total_time range
|
||||||
pub questions_in_flight: u32, // number of questions issued that have yet to be answered
|
pub questions_in_flight: u32, // number of questions issued that have yet to be answered
|
||||||
|
#[serde(with = "opt_json_as_string")]
|
||||||
pub last_question: Option<u64>, // when the peer was last questioned (either successfully or not) and we wanted an answer
|
pub last_question: Option<u64>, // when the peer was last questioned (either successfully or not) and we wanted an answer
|
||||||
|
#[serde(with = "opt_json_as_string")]
|
||||||
pub last_seen_ts: Option<u64>, // when the peer was last seen for any reason, including when we first attempted to reach out to it
|
pub last_seen_ts: Option<u64>, // when the peer was last seen for any reason, including when we first attempted to reach out to it
|
||||||
|
#[serde(with = "opt_json_as_string")]
|
||||||
pub first_consecutive_seen_ts: Option<u64>, // the timestamp of the first consecutive proof-of-life for this node (an answer or received question)
|
pub first_consecutive_seen_ts: Option<u64>, // the timestamp of the first consecutive proof-of-life for this node (an answer or received question)
|
||||||
pub recent_lost_answers: u32, // number of answers that have been lost since we lost reliability
|
pub recent_lost_answers: u32, // number of answers that have been lost since we lost reliability
|
||||||
pub failed_to_send: u32, // number of messages that have failed to send since we last successfully sent one
|
pub failed_to_send: u32, // number of messages that have failed to send since we last successfully sent one
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
pub struct PeerStats {
|
pub struct PeerStats {
|
||||||
|
#[serde(with = "json_as_string")]
|
||||||
pub time_added: u64, // when the peer was added to the routing table
|
pub time_added: u64, // when the peer was added to the routing table
|
||||||
pub rpc_stats: RPCStats, // information about RPCs
|
pub rpc_stats: RPCStats, // information about RPCs
|
||||||
pub latency: Option<LatencyStats>, // latencies for communications with the peer
|
pub latency: Option<LatencyStats>, // latencies for communications with the peer
|
||||||
pub transfer: TransferStatsDownUp, // Stats for communications with the peer
|
pub transfer: TransferStatsDownUp, // Stats for communications with the peer
|
||||||
pub status: Option<NodeStatus>, // Last known node status
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type ValueChangeCallback =
|
pub type ValueChangeCallback =
|
||||||
|
@ -40,3 +40,59 @@ pub fn serialize_json<T: Serialize + Debug>(val: T) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod json_as_string {
|
||||||
|
use std::fmt::Display;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use serde::{de, Deserialize, Deserializer, Serializer};
|
||||||
|
|
||||||
|
pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
T: Display,
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
serializer.collect_str(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
|
||||||
|
where
|
||||||
|
T: FromStr,
|
||||||
|
T::Err: Display,
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
String::deserialize(deserializer)?
|
||||||
|
.parse()
|
||||||
|
.map_err(de::Error::custom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod opt_json_as_string {
|
||||||
|
use std::fmt::Display;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use serde::{de, Deserialize, Deserializer, Serializer};
|
||||||
|
|
||||||
|
pub fn serialize<T, S>(value: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
T: Display,
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
match value {
|
||||||
|
Some(v) => serializer.collect_str(v),
|
||||||
|
None => serializer.serialize_none(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
|
||||||
|
where
|
||||||
|
T: FromStr,
|
||||||
|
T::Err: Display,
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
match Option::<String>::deserialize(deserializer)? {
|
||||||
|
None => Ok(None),
|
||||||
|
Some(v) => Ok(Some(v.parse::<T>().map_err(de::Error::custom)?)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -226,7 +226,6 @@ pub struct VeilidConfigNetwork {
|
|||||||
pub upnp: bool,
|
pub upnp: bool,
|
||||||
pub natpmp: bool,
|
pub natpmp: bool,
|
||||||
pub detect_address_changes: bool,
|
pub detect_address_changes: bool,
|
||||||
pub enable_local_peer_scope: bool,
|
|
||||||
pub restricted_nat_retries: u32,
|
pub restricted_nat_retries: u32,
|
||||||
pub tls: VeilidConfigTLS,
|
pub tls: VeilidConfigTLS,
|
||||||
pub application: VeilidConfigApplication,
|
pub application: VeilidConfigApplication,
|
||||||
@ -448,7 +447,6 @@ impl VeilidConfig {
|
|||||||
get_config!(inner.network.upnp);
|
get_config!(inner.network.upnp);
|
||||||
get_config!(inner.network.natpmp);
|
get_config!(inner.network.natpmp);
|
||||||
get_config!(inner.network.detect_address_changes);
|
get_config!(inner.network.detect_address_changes);
|
||||||
get_config!(inner.network.enable_local_peer_scope);
|
|
||||||
get_config!(inner.network.restricted_nat_retries);
|
get_config!(inner.network.restricted_nat_retries);
|
||||||
get_config!(inner.network.tls.certificate_path);
|
get_config!(inner.network.tls.certificate_path);
|
||||||
get_config!(inner.network.tls.private_key_path);
|
get_config!(inner.network.tls.private_key_path);
|
||||||
|
@ -215,3 +215,54 @@ pub fn ip_to_ipblock(ip6_prefix_size: usize, addr: IpAddr) -> IpAddr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ipaddr_apply_netmask(addr: IpAddr, netmask: IpAddr) -> IpAddr {
|
||||||
|
match addr {
|
||||||
|
IpAddr::V4(v4) => {
|
||||||
|
let v4mask = match netmask {
|
||||||
|
IpAddr::V4(v4mask) => v4mask,
|
||||||
|
IpAddr::V6(_) => {
|
||||||
|
panic!("netmask doesn't match ipv4 address");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let v4 = v4.octets();
|
||||||
|
let v4mask = v4mask.octets();
|
||||||
|
IpAddr::V4(Ipv4Addr::new(
|
||||||
|
v4[0] & v4mask[0],
|
||||||
|
v4[1] & v4mask[1],
|
||||||
|
v4[2] & v4mask[2],
|
||||||
|
v4[3] & v4mask[3],
|
||||||
|
))
|
||||||
|
}
|
||||||
|
IpAddr::V6(v6) => {
|
||||||
|
let v6mask = match netmask {
|
||||||
|
IpAddr::V4(_) => {
|
||||||
|
panic!("netmask doesn't match ipv6 address");
|
||||||
|
}
|
||||||
|
IpAddr::V6(v6mask) => v6mask,
|
||||||
|
};
|
||||||
|
let v6 = v6.segments();
|
||||||
|
let v6mask = v6mask.segments();
|
||||||
|
IpAddr::V6(Ipv6Addr::new(
|
||||||
|
v6[0] & v6mask[0],
|
||||||
|
v6[1] & v6mask[1],
|
||||||
|
v6[2] & v6mask[2],
|
||||||
|
v6[3] & v6mask[3],
|
||||||
|
v6[4] & v6mask[4],
|
||||||
|
v6[5] & v6mask[5],
|
||||||
|
v6[6] & v6mask[6],
|
||||||
|
v6[7] & v6mask[7],
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ipaddr_in_network(addr: IpAddr, netaddr: IpAddr, netmask: IpAddr) -> bool {
|
||||||
|
if addr.is_ipv4() && !netaddr.is_ipv4() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if addr.is_ipv6() && !netaddr.is_ipv6() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ipaddr_apply_netmask(netaddr, netmask) == ipaddr_apply_netmask(addr, netmask)
|
||||||
|
}
|
||||||
|
@ -56,6 +56,12 @@ android {
|
|||||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||||
signingConfig signingConfigs.debug
|
signingConfig signingConfigs.debug
|
||||||
}
|
}
|
||||||
|
debug {
|
||||||
|
packagingOptions {
|
||||||
|
jniLibs.useLegacyPackaging = true
|
||||||
|
jniLibs.keepDebugSymbols += '**/libveilid_flutter.so'
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ buildscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:4.1.0'
|
classpath 'com.android.tools.build:gradle:7.2.0'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
|||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
|
||||||
|
@ -85,7 +85,6 @@ Future<VeilidConfig> getDefaultVeilidConfig() async {
|
|||||||
upnp: true,
|
upnp: true,
|
||||||
natpmp: true,
|
natpmp: true,
|
||||||
detectAddressChanges: true,
|
detectAddressChanges: true,
|
||||||
enableLocalPeerScope: false,
|
|
||||||
restrictedNatRetries: 0,
|
restrictedNatRetries: 0,
|
||||||
tls: VeilidConfigTLS(
|
tls: VeilidConfigTLS(
|
||||||
certificatePath: "",
|
certificatePath: "",
|
||||||
|
@ -3,7 +3,6 @@ import 'dart:typed_data';
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:flutter/foundation.dart' show kIsWeb;
|
import 'package:flutter/foundation.dart' show kIsWeb;
|
||||||
import 'package:veilid/veilid.dart';
|
import 'package:veilid/veilid.dart';
|
||||||
import 'package:flutter_loggy/flutter_loggy.dart';
|
import 'package:flutter_loggy/flutter_loggy.dart';
|
||||||
@ -12,7 +11,7 @@ import 'package:loggy/loggy.dart';
|
|||||||
import 'config.dart';
|
import 'config.dart';
|
||||||
|
|
||||||
// Loggy tools
|
// Loggy tools
|
||||||
const LogLevel traceLevel = LogLevel('trace', 1);
|
const LogLevel traceLevel = LogLevel('Trace', 1);
|
||||||
|
|
||||||
class ConsolePrinter extends LoggyPrinter {
|
class ConsolePrinter extends LoggyPrinter {
|
||||||
ConsolePrinter(this.childPrinter) : super();
|
ConsolePrinter(this.childPrinter) : super();
|
||||||
@ -162,21 +161,30 @@ class _MyAppState extends State<MyApp> with UiLoggy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> processUpdateLog(VeilidUpdateLog update) async {
|
Future<void> processUpdateLog(VeilidUpdateLog update) async {
|
||||||
|
StackTrace? stackTrace;
|
||||||
|
Object? error;
|
||||||
|
final backtrace = update.backtrace;
|
||||||
|
if (backtrace != null) {
|
||||||
|
stackTrace =
|
||||||
|
StackTrace.fromString("$backtrace\n${StackTrace.current.toString()}");
|
||||||
|
error = 'embedded stack trace for ${update.logLevel} ${update.message}';
|
||||||
|
}
|
||||||
|
|
||||||
switch (update.logLevel) {
|
switch (update.logLevel) {
|
||||||
case VeilidLogLevel.error:
|
case VeilidLogLevel.error:
|
||||||
loggy.error(update.message);
|
loggy.error(update.message, error, stackTrace);
|
||||||
break;
|
break;
|
||||||
case VeilidLogLevel.warn:
|
case VeilidLogLevel.warn:
|
||||||
loggy.warning(update.message);
|
loggy.warning(update.message, error, stackTrace);
|
||||||
break;
|
break;
|
||||||
case VeilidLogLevel.info:
|
case VeilidLogLevel.info:
|
||||||
loggy.info(update.message);
|
loggy.info(update.message, error, stackTrace);
|
||||||
break;
|
break;
|
||||||
case VeilidLogLevel.debug:
|
case VeilidLogLevel.debug:
|
||||||
loggy.debug(update.message);
|
loggy.debug(update.message, error, stackTrace);
|
||||||
break;
|
break;
|
||||||
case VeilidLogLevel.trace:
|
case VeilidLogLevel.trace:
|
||||||
loggy.trace(update.message);
|
loggy.trace(update.message, error, stackTrace);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -188,7 +196,7 @@ class _MyAppState extends State<MyApp> with UiLoggy {
|
|||||||
if (update is VeilidUpdateLog) {
|
if (update is VeilidUpdateLog) {
|
||||||
await processUpdateLog(update);
|
await processUpdateLog(update);
|
||||||
} else {
|
} else {
|
||||||
loggy.trace("Update: " + update.json.toString());
|
loggy.trace("Update: ${update.json}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -218,12 +226,14 @@ class _MyAppState extends State<MyApp> with UiLoggy {
|
|||||||
onPressed: _updateStream != null
|
onPressed: _updateStream != null
|
||||||
? null
|
? null
|
||||||
: () async {
|
: () async {
|
||||||
var updateStream = Veilid.instance.startupVeilidCore(
|
var updateStream = await Veilid.instance
|
||||||
|
.startupVeilidCore(
|
||||||
await getDefaultVeilidConfig());
|
await getDefaultVeilidConfig());
|
||||||
setState(() {
|
setState(() {
|
||||||
_updateStream = updateStream;
|
_updateStream = updateStream;
|
||||||
_updateProcessor = processUpdates();
|
_updateProcessor = processUpdates();
|
||||||
});
|
});
|
||||||
|
await Veilid.instance.attach();
|
||||||
},
|
},
|
||||||
child: const Text('Startup'),
|
child: const Text('Startup'),
|
||||||
),
|
),
|
||||||
|
@ -19,7 +19,7 @@ EXTERNAL SOURCES:
|
|||||||
:path: Flutter/ephemeral/.symlinks/plugins/veilid/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/veilid/macos
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
FlutterMacOS: 57701585bf7de1b3fc2bb61f6378d73bbdea8424
|
FlutterMacOS: ae6af50a8ea7d6103d888583d46bd8328a7e9811
|
||||||
path_provider_macos: 3c0c3b4b0d4a76d2bf989a913c2de869c5641a19
|
path_provider_macos: 3c0c3b4b0d4a76d2bf989a913c2de869c5641a19
|
||||||
veilid: 6bed3adec63fd8708a2ace498e0e17941c9fc32b
|
veilid: 6bed3adec63fd8708a2ace498e0e17941c9fc32b
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ packages:
|
|||||||
name: async
|
name: async
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.8.2"
|
version: "2.9.0"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -28,21 +28,14 @@ packages:
|
|||||||
name: characters
|
name: characters
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.2.1"
|
||||||
charcode:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: charcode
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.3.1"
|
|
||||||
clock:
|
clock:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: clock
|
name: clock
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.1"
|
||||||
collection:
|
collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -63,21 +56,21 @@ packages:
|
|||||||
name: fake_async
|
name: fake_async
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.3.1"
|
||||||
ffi:
|
ffi:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: ffi
|
name: ffi
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.0"
|
version: "2.0.1"
|
||||||
file:
|
file:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: file
|
name: file
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.2"
|
version: "6.1.4"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -134,30 +127,30 @@ packages:
|
|||||||
name: matcher
|
name: matcher
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.11"
|
version: "0.12.12"
|
||||||
material_color_utilities:
|
material_color_utilities:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: material_color_utilities
|
name: material_color_utilities
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.4"
|
version: "0.1.5"
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.7.0"
|
version: "1.8.0"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: path
|
name: path
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.1"
|
version: "1.8.2"
|
||||||
path_provider:
|
path_provider:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: path_provider
|
name: path_provider
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
@ -169,14 +162,14 @@ packages:
|
|||||||
name: path_provider_android
|
name: path_provider_android
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.14"
|
version: "2.0.20"
|
||||||
path_provider_ios:
|
path_provider_ios:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_ios
|
name: path_provider_ios
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.10"
|
version: "2.0.11"
|
||||||
path_provider_linux:
|
path_provider_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -204,7 +197,7 @@ packages:
|
|||||||
name: path_provider_windows
|
name: path_provider_windows
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.1.3"
|
||||||
platform:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -232,7 +225,7 @@ packages:
|
|||||||
name: rxdart
|
name: rxdart
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.27.4"
|
version: "0.27.5"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -244,7 +237,7 @@ packages:
|
|||||||
name: source_span
|
name: source_span
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.2"
|
version: "1.9.0"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -265,21 +258,21 @@ packages:
|
|||||||
name: string_scanner
|
name: string_scanner
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.1"
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: term_glyph
|
name: term_glyph
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.2.1"
|
||||||
test_api:
|
test_api:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.9"
|
version: "0.4.12"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -300,14 +293,14 @@ packages:
|
|||||||
name: win32
|
name: win32
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.7.0"
|
version: "3.0.0"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: xdg_directories
|
name: xdg_directories
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.0+1"
|
version: "0.2.0+2"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.17.0 <3.0.0"
|
dart: ">=2.17.0 <3.0.0"
|
||||||
flutter: ">=3.0.0"
|
flutter: ">=3.0.0"
|
||||||
|
@ -34,6 +34,8 @@ dependencies:
|
|||||||
cupertino_icons: ^1.0.2
|
cupertino_icons: ^1.0.2
|
||||||
loggy: ^2.0.1+1
|
loggy: ^2.0.1+1
|
||||||
flutter_loggy: ^2.0.1
|
flutter_loggy: ^2.0.1
|
||||||
|
path_provider: ^2.0.11
|
||||||
|
path: ^1.8.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
@ -741,7 +741,6 @@ class VeilidConfigNetwork {
|
|||||||
bool upnp;
|
bool upnp;
|
||||||
bool natpmp;
|
bool natpmp;
|
||||||
bool detectAddressChanges;
|
bool detectAddressChanges;
|
||||||
bool enableLocalPeerScope;
|
|
||||||
int restrictedNatRetries;
|
int restrictedNatRetries;
|
||||||
VeilidConfigTLS tls;
|
VeilidConfigTLS tls;
|
||||||
VeilidConfigApplication application;
|
VeilidConfigApplication application;
|
||||||
@ -767,7 +766,6 @@ class VeilidConfigNetwork {
|
|||||||
required this.upnp,
|
required this.upnp,
|
||||||
required this.natpmp,
|
required this.natpmp,
|
||||||
required this.detectAddressChanges,
|
required this.detectAddressChanges,
|
||||||
required this.enableLocalPeerScope,
|
|
||||||
required this.restrictedNatRetries,
|
required this.restrictedNatRetries,
|
||||||
required this.tls,
|
required this.tls,
|
||||||
required this.application,
|
required this.application,
|
||||||
@ -795,7 +793,6 @@ class VeilidConfigNetwork {
|
|||||||
'upnp': upnp,
|
'upnp': upnp,
|
||||||
'natpmp': natpmp,
|
'natpmp': natpmp,
|
||||||
'detect_address_changes': detectAddressChanges,
|
'detect_address_changes': detectAddressChanges,
|
||||||
'enable_local_peer_scope': enableLocalPeerScope,
|
|
||||||
'restricted_nat_retries': restrictedNatRetries,
|
'restricted_nat_retries': restrictedNatRetries,
|
||||||
'tls': tls.json,
|
'tls': tls.json,
|
||||||
'application': application.json,
|
'application': application.json,
|
||||||
@ -826,7 +823,6 @@ class VeilidConfigNetwork {
|
|||||||
upnp = json['upnp'],
|
upnp = json['upnp'],
|
||||||
natpmp = json['natpmp'],
|
natpmp = json['natpmp'],
|
||||||
detectAddressChanges = json['detect_address_changes'],
|
detectAddressChanges = json['detect_address_changes'],
|
||||||
enableLocalPeerScope = json['enable_local_peer_scope'],
|
|
||||||
restrictedNatRetries = json['restricted_nat_retries'],
|
restrictedNatRetries = json['restricted_nat_retries'],
|
||||||
tls = VeilidConfigTLS.fromJson(json['tls']),
|
tls = VeilidConfigTLS.fromJson(json['tls']),
|
||||||
application = VeilidConfigApplication.fromJson(json['application']),
|
application = VeilidConfigApplication.fromJson(json['application']),
|
||||||
@ -991,6 +987,243 @@ class VeilidConfig {
|
|||||||
network = VeilidConfigNetwork.fromJson(json['network']);
|
network = VeilidConfigNetwork.fromJson(json['network']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////
|
||||||
|
|
||||||
|
class LatencyStats {
|
||||||
|
BigInt fastest;
|
||||||
|
BigInt average;
|
||||||
|
BigInt slowest;
|
||||||
|
|
||||||
|
LatencyStats({
|
||||||
|
required this.fastest,
|
||||||
|
required this.average,
|
||||||
|
required this.slowest,
|
||||||
|
});
|
||||||
|
|
||||||
|
Map<String, dynamic> get json {
|
||||||
|
return {
|
||||||
|
'fastest': fastest.toString(),
|
||||||
|
'average': average.toString(),
|
||||||
|
'slowest': slowest.toString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
LatencyStats.fromJson(Map<String, dynamic> json)
|
||||||
|
: fastest = BigInt.parse(json['fastest']),
|
||||||
|
average = BigInt.parse(json['average']),
|
||||||
|
slowest = BigInt.parse(json['slowest']);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////
|
||||||
|
|
||||||
|
class TransferStats {
|
||||||
|
BigInt total;
|
||||||
|
BigInt maximum;
|
||||||
|
BigInt average;
|
||||||
|
BigInt minimum;
|
||||||
|
|
||||||
|
TransferStats({
|
||||||
|
required this.total,
|
||||||
|
required this.maximum,
|
||||||
|
required this.average,
|
||||||
|
required this.minimum,
|
||||||
|
});
|
||||||
|
|
||||||
|
Map<String, dynamic> get json {
|
||||||
|
return {
|
||||||
|
'total': total.toString(),
|
||||||
|
'maximum': maximum.toString(),
|
||||||
|
'average': average.toString(),
|
||||||
|
'minimum': minimum.toString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
TransferStats.fromJson(Map<String, dynamic> json)
|
||||||
|
: total = BigInt.parse(json['total']),
|
||||||
|
maximum = BigInt.parse(json['maximum']),
|
||||||
|
average = BigInt.parse(json['average']),
|
||||||
|
minimum = BigInt.parse(json['minimum']);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////
|
||||||
|
|
||||||
|
class TransferStatsDownUp {
|
||||||
|
TransferStats down;
|
||||||
|
TransferStats up;
|
||||||
|
|
||||||
|
TransferStatsDownUp({
|
||||||
|
required this.down,
|
||||||
|
required this.up,
|
||||||
|
});
|
||||||
|
|
||||||
|
Map<String, dynamic> get json {
|
||||||
|
return {
|
||||||
|
'down': down.json,
|
||||||
|
'up': up.json,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
TransferStatsDownUp.fromJson(Map<String, dynamic> json)
|
||||||
|
: down = TransferStats.fromJson(json['down']),
|
||||||
|
up = TransferStats.fromJson(json['up']);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////
|
||||||
|
|
||||||
|
class RPCStats {
|
||||||
|
int messagesSent;
|
||||||
|
int messagesRcvd;
|
||||||
|
int questionsInFlight;
|
||||||
|
BigInt? lastQuestion;
|
||||||
|
BigInt? lastSeenTs;
|
||||||
|
BigInt? firstConsecutiveSeenTs;
|
||||||
|
int recentLostAnswers;
|
||||||
|
int failedToSend;
|
||||||
|
|
||||||
|
RPCStats({
|
||||||
|
required this.messagesSent,
|
||||||
|
required this.messagesRcvd,
|
||||||
|
required this.questionsInFlight,
|
||||||
|
required this.lastQuestion,
|
||||||
|
required this.lastSeenTs,
|
||||||
|
required this.firstConsecutiveSeenTs,
|
||||||
|
required this.recentLostAnswers,
|
||||||
|
required this.failedToSend,
|
||||||
|
});
|
||||||
|
|
||||||
|
Map<String, dynamic> get json {
|
||||||
|
return {
|
||||||
|
'messages_sent': messagesSent,
|
||||||
|
'messages_rcvd': messagesRcvd,
|
||||||
|
'questions_in_flight': questionsInFlight,
|
||||||
|
'last_question': lastQuestion?.toString(),
|
||||||
|
'last_seen_ts': lastSeenTs?.toString(),
|
||||||
|
'first_consecutive_seen_ts': firstConsecutiveSeenTs?.toString(),
|
||||||
|
'recent_lost_answers': recentLostAnswers,
|
||||||
|
'failed_to_send': failedToSend,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
RPCStats.fromJson(Map<String, dynamic> json)
|
||||||
|
: messagesSent = json['messages_sent'],
|
||||||
|
messagesRcvd = json['messages_rcvd'],
|
||||||
|
questionsInFlight = json['questions_in_flight'],
|
||||||
|
lastQuestion = json['last_question'] != null
|
||||||
|
? BigInt.parse(json['last_question'])
|
||||||
|
: null,
|
||||||
|
lastSeenTs = json['last_seen_ts'] != null
|
||||||
|
? BigInt.parse(json['last_seen_ts'])
|
||||||
|
: null,
|
||||||
|
firstConsecutiveSeenTs = json['first_consecutive_seen_ts'] != null
|
||||||
|
? BigInt.parse(json['first_consecutive_seen_ts'])
|
||||||
|
: null,
|
||||||
|
recentLostAnswers = json['recent_lost_answers'],
|
||||||
|
failedToSend = json['failed_to_send'];
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////
|
||||||
|
|
||||||
|
class PeerStats {
|
||||||
|
BigInt timeAdded;
|
||||||
|
RPCStats rpcStats;
|
||||||
|
LatencyStats? latency;
|
||||||
|
TransferStatsDownUp transfer;
|
||||||
|
|
||||||
|
PeerStats({
|
||||||
|
required this.timeAdded,
|
||||||
|
required this.rpcStats,
|
||||||
|
required this.latency,
|
||||||
|
required this.transfer,
|
||||||
|
});
|
||||||
|
|
||||||
|
Map<String, dynamic> get json {
|
||||||
|
return {
|
||||||
|
'time_added': timeAdded.toString(),
|
||||||
|
'rpc_stats': rpcStats.json,
|
||||||
|
'latency': latency?.json,
|
||||||
|
'transfer': transfer.json,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
PeerStats.fromJson(Map<String, dynamic> json)
|
||||||
|
: timeAdded = BigInt.parse(json['time_added']),
|
||||||
|
rpcStats = RPCStats.fromJson(json['rpc_stats']),
|
||||||
|
latency = json['latency'] != null
|
||||||
|
? LatencyStats.fromJson(json['latency'])
|
||||||
|
: null,
|
||||||
|
transfer = TransferStatsDownUp.fromJson(json['transfer']);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////
|
||||||
|
|
||||||
|
class PeerTableData {
|
||||||
|
String nodeId;
|
||||||
|
PeerAddress peerAddress;
|
||||||
|
PeerStats peerStats;
|
||||||
|
|
||||||
|
PeerTableData({
|
||||||
|
required this.nodeId,
|
||||||
|
required this.peerAddress,
|
||||||
|
required this.peerStats,
|
||||||
|
});
|
||||||
|
|
||||||
|
Map<String, dynamic> get json {
|
||||||
|
return {
|
||||||
|
'node_id': nodeId,
|
||||||
|
'peer_address': peerAddress.json,
|
||||||
|
'peer_stats': peerStats.json,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
PeerTableData.fromJson(Map<String, dynamic> json)
|
||||||
|
: nodeId = json['node_id'],
|
||||||
|
peerAddress = PeerAddress.fromJson(json['peer_address']),
|
||||||
|
peerStats = PeerStats.fromJson(json['peer_stats']);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////
|
||||||
|
/// AttachmentState
|
||||||
|
|
||||||
|
enum ProtocolType {
|
||||||
|
udp,
|
||||||
|
tcp,
|
||||||
|
ws,
|
||||||
|
wss,
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ProtocolTypeExt on ProtocolType {
|
||||||
|
String get json {
|
||||||
|
return name.toUpperCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProtocolType protocolTypeFromJson(String j) {
|
||||||
|
return ProtocolType.values.byName(j.toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////
|
||||||
|
|
||||||
|
class PeerAddress {
|
||||||
|
ProtocolType protocolType;
|
||||||
|
String socketAddress;
|
||||||
|
|
||||||
|
PeerAddress({
|
||||||
|
required this.protocolType,
|
||||||
|
required this.socketAddress,
|
||||||
|
});
|
||||||
|
|
||||||
|
Map<String, dynamic> get json {
|
||||||
|
return {
|
||||||
|
'protocol_type': protocolType.json,
|
||||||
|
'socket_address': socketAddress,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
PeerAddress.fromJson(Map<String, dynamic> json)
|
||||||
|
: protocolType = protocolTypeFromJson(json['protocol_type']),
|
||||||
|
socketAddress = json['socket_address'];
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////
|
//////////////////////////////////////
|
||||||
/// VeilidUpdate
|
/// VeilidUpdate
|
||||||
|
|
||||||
@ -1000,16 +1233,18 @@ abstract class VeilidUpdate {
|
|||||||
case "Log":
|
case "Log":
|
||||||
{
|
{
|
||||||
return VeilidUpdateLog(
|
return VeilidUpdateLog(
|
||||||
veilidLogLevelFromJson(json["log_level"]), json["message"]);
|
logLevel: veilidLogLevelFromJson(json["log_level"]),
|
||||||
|
message: json["message"],
|
||||||
|
backtrace: json["backtrace"]);
|
||||||
}
|
}
|
||||||
case "Attachment":
|
case "Attachment":
|
||||||
{
|
{
|
||||||
return VeilidUpdateAttachment(attachmentStateFromJson(json["state"]));
|
return VeilidUpdateAttachment(
|
||||||
|
state: VeilidStateAttachment.fromJson(json));
|
||||||
}
|
}
|
||||||
case "Network":
|
case "Network":
|
||||||
{
|
{
|
||||||
return VeilidUpdateNetwork(
|
return VeilidUpdateNetwork(state: VeilidStateNetwork.fromJson(json));
|
||||||
json["started"], json["bps_up"], json["bps_down"]);
|
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
@ -1024,8 +1259,13 @@ abstract class VeilidUpdate {
|
|||||||
class VeilidUpdateLog implements VeilidUpdate {
|
class VeilidUpdateLog implements VeilidUpdate {
|
||||||
final VeilidLogLevel logLevel;
|
final VeilidLogLevel logLevel;
|
||||||
final String message;
|
final String message;
|
||||||
|
final String? backtrace;
|
||||||
//
|
//
|
||||||
VeilidUpdateLog(this.logLevel, this.message);
|
VeilidUpdateLog({
|
||||||
|
required this.logLevel,
|
||||||
|
required this.message,
|
||||||
|
required this.backtrace,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> get json {
|
Map<String, dynamic> get json {
|
||||||
@ -1033,39 +1273,34 @@ class VeilidUpdateLog implements VeilidUpdate {
|
|||||||
'kind': "Log",
|
'kind': "Log",
|
||||||
'log_level': logLevel.json,
|
'log_level': logLevel.json,
|
||||||
'message': message,
|
'message': message,
|
||||||
|
'backtrace': backtrace
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class VeilidUpdateAttachment implements VeilidUpdate {
|
class VeilidUpdateAttachment implements VeilidUpdate {
|
||||||
final AttachmentState state;
|
final VeilidStateAttachment state;
|
||||||
//
|
//
|
||||||
VeilidUpdateAttachment(this.state);
|
VeilidUpdateAttachment({required this.state});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> get json {
|
Map<String, dynamic> get json {
|
||||||
return {
|
var jsonRep = state.json;
|
||||||
'kind': "Attachment",
|
jsonRep['kind'] = "Attachment";
|
||||||
'state': state.json,
|
return jsonRep;
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class VeilidUpdateNetwork implements VeilidUpdate {
|
class VeilidUpdateNetwork implements VeilidUpdate {
|
||||||
final bool started;
|
final VeilidStateNetwork state;
|
||||||
final int bpsDown;
|
|
||||||
final int bpsUp;
|
|
||||||
//
|
//
|
||||||
VeilidUpdateNetwork(this.started, this.bpsDown, this.bpsUp);
|
VeilidUpdateNetwork({required this.state});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> get json {
|
Map<String, dynamic> get json {
|
||||||
return {
|
var jsonRep = state.json;
|
||||||
'kind': "Network",
|
jsonRep['kind'] = "Network";
|
||||||
'started': started,
|
return jsonRep;
|
||||||
'bps_down': bpsDown,
|
|
||||||
'bps_up': bpsUp
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1079,6 +1314,12 @@ class VeilidStateAttachment {
|
|||||||
|
|
||||||
VeilidStateAttachment.fromJson(Map<String, dynamic> json)
|
VeilidStateAttachment.fromJson(Map<String, dynamic> json)
|
||||||
: state = attachmentStateFromJson(json['state']);
|
: state = attachmentStateFromJson(json['state']);
|
||||||
|
|
||||||
|
Map<String, dynamic> get json {
|
||||||
|
return {
|
||||||
|
'state': state.json,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////
|
//////////////////////////////////////
|
||||||
@ -1086,11 +1327,31 @@ class VeilidStateAttachment {
|
|||||||
|
|
||||||
class VeilidStateNetwork {
|
class VeilidStateNetwork {
|
||||||
final bool started;
|
final bool started;
|
||||||
|
final BigInt bpsDown;
|
||||||
|
final BigInt bpsUp;
|
||||||
|
final List<PeerTableData> peers;
|
||||||
|
|
||||||
VeilidStateNetwork(this.started);
|
VeilidStateNetwork(
|
||||||
|
{required this.started,
|
||||||
|
required this.bpsDown,
|
||||||
|
required this.bpsUp,
|
||||||
|
required this.peers});
|
||||||
|
|
||||||
VeilidStateNetwork.fromJson(Map<String, dynamic> json)
|
VeilidStateNetwork.fromJson(Map<String, dynamic> json)
|
||||||
: started = json['started'];
|
: started = json['started'],
|
||||||
|
bpsDown = BigInt.parse(json['bps_down']),
|
||||||
|
bpsUp = BigInt.parse(json['bps_up']),
|
||||||
|
peers = List<PeerTableData>.from(
|
||||||
|
json['peers'].map((j) => PeerTableData.fromJson(j)));
|
||||||
|
|
||||||
|
Map<String, dynamic> get json {
|
||||||
|
return {
|
||||||
|
'started': started,
|
||||||
|
'bps_down': bpsDown.toString(),
|
||||||
|
'bps_up': bpsUp.toString(),
|
||||||
|
'peers': peers.map((p) => p.json).toList(),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////
|
//////////////////////////////////////
|
||||||
@ -1100,11 +1361,13 @@ class VeilidState {
|
|||||||
final VeilidStateAttachment attachment;
|
final VeilidStateAttachment attachment;
|
||||||
final VeilidStateNetwork network;
|
final VeilidStateNetwork network;
|
||||||
|
|
||||||
VeilidState(this.attachment, this.network);
|
|
||||||
|
|
||||||
VeilidState.fromJson(Map<String, dynamic> json)
|
VeilidState.fromJson(Map<String, dynamic> json)
|
||||||
: attachment = VeilidStateAttachment.fromJson(json['attachment']),
|
: attachment = VeilidStateAttachment.fromJson(json['attachment']),
|
||||||
network = VeilidStateNetwork.fromJson(json['network']);
|
network = VeilidStateNetwork.fromJson(json['network']);
|
||||||
|
|
||||||
|
Map<String, dynamic> get json {
|
||||||
|
return {'attachment': attachment.json, 'network': network.json};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////
|
//////////////////////////////////////
|
||||||
@ -1303,8 +1566,10 @@ abstract class Veilid {
|
|||||||
|
|
||||||
void initializeVeilidCore(Map<String, dynamic> platformConfigJson);
|
void initializeVeilidCore(Map<String, dynamic> platformConfigJson);
|
||||||
void changeLogLevel(String layer, VeilidConfigLogLevel logLevel);
|
void changeLogLevel(String layer, VeilidConfigLogLevel logLevel);
|
||||||
Stream<VeilidUpdate> startupVeilidCore(VeilidConfig config);
|
Future<Stream<VeilidUpdate>> startupVeilidCore(VeilidConfig config);
|
||||||
Future<VeilidState> getVeilidState();
|
Future<VeilidState> getVeilidState();
|
||||||
|
Future<void> attach();
|
||||||
|
Future<void> detach();
|
||||||
Future<void> shutdownVeilidCore();
|
Future<void> shutdownVeilidCore();
|
||||||
Future<String> debug(String command);
|
Future<String> debug(String command);
|
||||||
String veilidVersionString();
|
String veilidVersionString();
|
||||||
|
@ -36,11 +36,17 @@ typedef _InitializeVeilidCoreDart = void Function(Pointer<Utf8>);
|
|||||||
typedef _ChangeLogLevelC = Void Function(Pointer<Utf8>, Pointer<Utf8>);
|
typedef _ChangeLogLevelC = Void Function(Pointer<Utf8>, Pointer<Utf8>);
|
||||||
typedef _ChangeLogLevelDart = void Function(Pointer<Utf8>, Pointer<Utf8>);
|
typedef _ChangeLogLevelDart = void Function(Pointer<Utf8>, Pointer<Utf8>);
|
||||||
// fn startup_veilid_core(port: i64, config: FfiStr)
|
// fn startup_veilid_core(port: i64, config: FfiStr)
|
||||||
typedef _StartupVeilidCoreC = Void Function(Int64, Pointer<Utf8>);
|
typedef _StartupVeilidCoreC = Void Function(Int64, Int64, Pointer<Utf8>);
|
||||||
typedef _StartupVeilidCoreDart = void Function(int, Pointer<Utf8>);
|
typedef _StartupVeilidCoreDart = void Function(int, int, Pointer<Utf8>);
|
||||||
// fn get_veilid_state(port: i64)
|
// fn get_veilid_state(port: i64)
|
||||||
typedef _GetVeilidStateC = Void Function(Int64);
|
typedef _GetVeilidStateC = Void Function(Int64);
|
||||||
typedef _GetVeilidStateDart = void Function(int);
|
typedef _GetVeilidStateDart = void Function(int);
|
||||||
|
// fn attach(port: i64)
|
||||||
|
typedef _AttachC = Void Function(Int64);
|
||||||
|
typedef _AttachDart = void Function(int);
|
||||||
|
// fn detach(port: i64)
|
||||||
|
typedef _DetachC = Void Function(Int64);
|
||||||
|
typedef _DetachDart = void Function(int);
|
||||||
// fn debug(port: i64, log_level: FfiStr)
|
// fn debug(port: i64, log_level: FfiStr)
|
||||||
typedef _DebugC = Void Function(Int64, Pointer<Utf8>);
|
typedef _DebugC = Void Function(Int64, Pointer<Utf8>);
|
||||||
typedef _DebugDart = void Function(int, Pointer<Utf8>);
|
typedef _DebugDart = void Function(int, Pointer<Utf8>);
|
||||||
@ -193,6 +199,51 @@ Future<void> processFutureVoid(Future<dynamic> future) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Stream<T>> processFutureStream<T>(
|
||||||
|
Stream<T> returnStream, Future<dynamic> future) {
|
||||||
|
return future.then((value) {
|
||||||
|
final list = value as List<dynamic>;
|
||||||
|
switch (list[0] as int) {
|
||||||
|
case messageOk:
|
||||||
|
{
|
||||||
|
if (list[1] != null) {
|
||||||
|
throw VeilidAPIExceptionInternal(
|
||||||
|
"Unexpected MESSAGE_OK value '${list[1]}' where null expected");
|
||||||
|
}
|
||||||
|
return returnStream;
|
||||||
|
}
|
||||||
|
case messageErr:
|
||||||
|
{
|
||||||
|
throw VeilidAPIExceptionInternal("Internal API Error: ${list[1]}");
|
||||||
|
}
|
||||||
|
case messageOkJson:
|
||||||
|
{
|
||||||
|
var ret = jsonDecode(list[1] as String);
|
||||||
|
if (ret != null) {
|
||||||
|
throw VeilidAPIExceptionInternal(
|
||||||
|
"Unexpected MESSAGE_OK_JSON value '$ret' where null expected");
|
||||||
|
}
|
||||||
|
return returnStream;
|
||||||
|
}
|
||||||
|
case messageErrJson:
|
||||||
|
{
|
||||||
|
throw VeilidAPIException.fromJson(jsonDecode(list[1] as String));
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
throw VeilidAPIExceptionInternal(
|
||||||
|
"Unexpected async return message type: ${list[0]}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catchError((e) {
|
||||||
|
// Wrap all other errors in VeilidAPIExceptionInternal
|
||||||
|
throw VeilidAPIExceptionInternal(e.toString());
|
||||||
|
}, test: (e) {
|
||||||
|
// Pass errors that are already VeilidAPIException through without wrapping
|
||||||
|
return e is! VeilidAPIException;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Stream<T> processStreamJson<T>(
|
Stream<T> processStreamJson<T>(
|
||||||
T Function(Map<String, dynamic>) jsonConstructor, ReceivePort port) async* {
|
T Function(Map<String, dynamic>) jsonConstructor, ReceivePort port) async* {
|
||||||
try {
|
try {
|
||||||
@ -249,6 +300,8 @@ class VeilidFFI implements Veilid {
|
|||||||
final _ChangeLogLevelDart _changeLogLevel;
|
final _ChangeLogLevelDart _changeLogLevel;
|
||||||
final _StartupVeilidCoreDart _startupVeilidCore;
|
final _StartupVeilidCoreDart _startupVeilidCore;
|
||||||
final _GetVeilidStateDart _getVeilidState;
|
final _GetVeilidStateDart _getVeilidState;
|
||||||
|
final _AttachDart _attach;
|
||||||
|
final _DetachDart _detach;
|
||||||
final _ShutdownVeilidCoreDart _shutdownVeilidCore;
|
final _ShutdownVeilidCoreDart _shutdownVeilidCore;
|
||||||
final _DebugDart _debug;
|
final _DebugDart _debug;
|
||||||
final _VeilidVersionStringDart _veilidVersionString;
|
final _VeilidVersionStringDart _veilidVersionString;
|
||||||
@ -269,6 +322,8 @@ class VeilidFFI implements Veilid {
|
|||||||
_getVeilidState =
|
_getVeilidState =
|
||||||
dylib.lookupFunction<_GetVeilidStateC, _GetVeilidStateDart>(
|
dylib.lookupFunction<_GetVeilidStateC, _GetVeilidStateDart>(
|
||||||
'get_veilid_state'),
|
'get_veilid_state'),
|
||||||
|
_attach = dylib.lookupFunction<_AttachC, _AttachDart>('attach'),
|
||||||
|
_detach = dylib.lookupFunction<_DetachC, _DetachDart>('detach'),
|
||||||
_shutdownVeilidCore =
|
_shutdownVeilidCore =
|
||||||
dylib.lookupFunction<_ShutdownVeilidCoreC, _ShutdownVeilidCoreDart>(
|
dylib.lookupFunction<_ShutdownVeilidCoreC, _ShutdownVeilidCoreDart>(
|
||||||
'shutdown_veilid_core'),
|
'shutdown_veilid_core'),
|
||||||
@ -308,15 +363,20 @@ class VeilidFFI implements Veilid {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Stream<VeilidUpdate> startupVeilidCore(VeilidConfig config) {
|
Future<Stream<VeilidUpdate>> startupVeilidCore(VeilidConfig config) {
|
||||||
var nativeConfig =
|
var nativeConfig =
|
||||||
jsonEncode(config.json, toEncodable: veilidApiToEncodable)
|
jsonEncode(config.json, toEncodable: veilidApiToEncodable)
|
||||||
.toNativeUtf8();
|
.toNativeUtf8();
|
||||||
|
final recvStreamPort = ReceivePort("veilid_api_stream");
|
||||||
|
final sendStreamPort = recvStreamPort.sendPort;
|
||||||
final recvPort = ReceivePort("startup_veilid_core");
|
final recvPort = ReceivePort("startup_veilid_core");
|
||||||
final sendPort = recvPort.sendPort;
|
final sendPort = recvPort.sendPort;
|
||||||
_startupVeilidCore(sendPort.nativePort, nativeConfig);
|
_startupVeilidCore(
|
||||||
|
sendPort.nativePort, sendStreamPort.nativePort, nativeConfig);
|
||||||
malloc.free(nativeConfig);
|
malloc.free(nativeConfig);
|
||||||
return processStreamJson(VeilidUpdate.fromJson, recvPort);
|
return processFutureStream(
|
||||||
|
processStreamJson(VeilidUpdate.fromJson, recvStreamPort),
|
||||||
|
recvPort.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -327,6 +387,22 @@ class VeilidFFI implements Veilid {
|
|||||||
return processFutureJson(VeilidState.fromJson, recvPort.first);
|
return processFutureJson(VeilidState.fromJson, recvPort.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> attach() async {
|
||||||
|
final recvPort = ReceivePort("attach");
|
||||||
|
final sendPort = recvPort.sendPort;
|
||||||
|
_attach(sendPort.nativePort);
|
||||||
|
return processFutureVoid(recvPort.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> detach() async {
|
||||||
|
final recvPort = ReceivePort("detach");
|
||||||
|
final sendPort = recvPort.sendPort;
|
||||||
|
_detach(sendPort.nativePort);
|
||||||
|
return processFutureVoid(recvPort.first);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> shutdownVeilidCore() async {
|
Future<void> shutdownVeilidCore() async {
|
||||||
final recvPort = ReceivePort("shutdown_veilid_core");
|
final recvPort = ReceivePort("shutdown_veilid_core");
|
||||||
|
@ -35,7 +35,7 @@ class VeilidJS implements Veilid {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Stream<VeilidUpdate> startupVeilidCore(VeilidConfig config) async* {
|
Future<Stream<VeilidUpdate>> startupVeilidCore(VeilidConfig config) async {
|
||||||
var streamController = StreamController<VeilidUpdate>();
|
var streamController = StreamController<VeilidUpdate>();
|
||||||
updateCallback(String update) {
|
updateCallback(String update) {
|
||||||
var updateJson = jsonDecode(update);
|
var updateJson = jsonDecode(update);
|
||||||
@ -51,7 +51,8 @@ class VeilidJS implements Veilid {
|
|||||||
js.allowInterop(updateCallback),
|
js.allowInterop(updateCallback),
|
||||||
jsonEncode(config.json, toEncodable: veilidApiToEncodable)
|
jsonEncode(config.json, toEncodable: veilidApiToEncodable)
|
||||||
]));
|
]));
|
||||||
yield* streamController.stream;
|
|
||||||
|
return streamController.stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -60,6 +61,16 @@ class VeilidJS implements Veilid {
|
|||||||
js_util.callMethod(wasm, "get_veilid_state", []))));
|
js_util.callMethod(wasm, "get_veilid_state", []))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> attach() async {
|
||||||
|
return _wrapApiPromise(js_util.callMethod(wasm, "attach", []));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> detach() async {
|
||||||
|
return _wrapApiPromise(js_util.callMethod(wasm, "detach", []));
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> shutdownVeilidCore() {
|
Future<void> shutdownVeilidCore() {
|
||||||
return _wrapApiPromise(
|
return _wrapApiPromise(
|
||||||
|
@ -48,19 +48,6 @@ define_string_destructor!(free_string);
|
|||||||
type APIResult<T> = Result<T, veilid_core::VeilidAPIError>;
|
type APIResult<T> = Result<T, veilid_core::VeilidAPIError>;
|
||||||
const APIRESULT_VOID: APIResult<()> = APIResult::Ok(());
|
const APIRESULT_VOID: APIResult<()> = APIResult::Ok(());
|
||||||
|
|
||||||
// Stream abort macro for simplified error handling
|
|
||||||
macro_rules! check_err_json {
|
|
||||||
($stream:expr, $ex:expr) => {
|
|
||||||
match $ex {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(e) => {
|
|
||||||
$stream.abort_json(e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////////////
|
/////////////////////////////////////////
|
||||||
// FFI-specific cofnig
|
// FFI-specific cofnig
|
||||||
|
|
||||||
@ -253,25 +240,24 @@ pub extern "C" fn change_log_level(layer: FfiStr, log_level: FfiStr) {
|
|||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
#[instrument]
|
#[instrument]
|
||||||
pub extern "C" fn startup_veilid_core(port: i64, config: FfiStr) {
|
pub extern "C" fn startup_veilid_core(port: i64, stream_port: i64, config: FfiStr) {
|
||||||
let config = config.into_opt_string();
|
let config = config.into_opt_string();
|
||||||
let stream = DartIsolateStream::new(port);
|
let stream = DartIsolateStream::new(stream_port);
|
||||||
spawn(async move {
|
DartIsolateWrapper::new(port).spawn_result_json(async move {
|
||||||
let config_json = match config {
|
let config_json = match config {
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
None => {
|
None => {
|
||||||
stream.abort_json(veilid_core::VeilidAPIError::MissingArgument {
|
let err = veilid_core::VeilidAPIError::MissingArgument {
|
||||||
context: "startup_veilid_core".to_owned(),
|
context: "startup_veilid_core".to_owned(),
|
||||||
argument: "config".to_owned(),
|
argument: "config".to_owned(),
|
||||||
});
|
};
|
||||||
return;
|
return APIResult::Err(err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut api_lock = VEILID_API.lock().await;
|
let mut api_lock = VEILID_API.lock().await;
|
||||||
if api_lock.is_some() {
|
if api_lock.is_some() {
|
||||||
stream.abort_json(veilid_core::VeilidAPIError::AlreadyInitialized);
|
return APIResult::Err(veilid_core::VeilidAPIError::AlreadyInitialized);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let sink = stream.clone();
|
let sink = stream.clone();
|
||||||
@ -287,9 +273,10 @@ pub extern "C" fn startup_veilid_core(port: i64, config: FfiStr) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let res = veilid_core::api_startup_json(update_callback, config_json).await;
|
let veilid_api = veilid_core::api_startup_json(update_callback, config_json).await?;
|
||||||
let veilid_api = check_err_json!(stream, res);
|
|
||||||
*api_lock = Some(veilid_api);
|
*api_lock = Some(veilid_api);
|
||||||
|
|
||||||
|
APIRESULT_VOID
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,6 +289,24 @@ pub extern "C" fn get_veilid_state(port: i64) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn attach(port: i64) {
|
||||||
|
DartIsolateWrapper::new(port).spawn_result_json(async move {
|
||||||
|
let veilid_api = get_veilid_api().await?;
|
||||||
|
veilid_api.attach().await?;
|
||||||
|
APIRESULT_VOID
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn detach(port: i64) {
|
||||||
|
DartIsolateWrapper::new(port).spawn_result_json(async move {
|
||||||
|
let veilid_api = get_veilid_api().await?;
|
||||||
|
veilid_api.detach().await?;
|
||||||
|
APIRESULT_VOID
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
#[instrument]
|
#[instrument]
|
||||||
pub extern "C" fn shutdown_veilid_core(port: i64) {
|
pub extern "C" fn shutdown_veilid_core(port: i64) {
|
||||||
|
@ -20,7 +20,7 @@ const MESSAGE_ERR_JSON: i32 = 3;
|
|||||||
//const MESSAGE_STREAM_ITEM: i32 = 4;
|
//const MESSAGE_STREAM_ITEM: i32 = 4;
|
||||||
const MESSAGE_STREAM_ITEM_JSON: i32 = 5;
|
const MESSAGE_STREAM_ITEM_JSON: i32 = 5;
|
||||||
//const MESSAGE_STREAM_ABORT: i32 = 6;
|
//const MESSAGE_STREAM_ABORT: i32 = 6;
|
||||||
const MESSAGE_STREAM_ABORT_JSON: i32 = 7;
|
//const MESSAGE_STREAM_ABORT_JSON: i32 = 7;
|
||||||
const MESSAGE_STREAM_CLOSE: i32 = 8;
|
const MESSAGE_STREAM_CLOSE: i32 = 8;
|
||||||
|
|
||||||
impl DartIsolateWrapper {
|
impl DartIsolateWrapper {
|
||||||
@ -148,17 +148,17 @@ impl DartIsolateStream {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
pub fn abort_json<E: Serialize + Debug>(self, error: E) -> bool {
|
// pub fn abort_json<E: Serialize + Debug>(self, error: E) -> bool {
|
||||||
let mut inner = self.inner.lock();
|
// let mut inner = self.inner.lock();
|
||||||
if let Some(isolate) = inner.isolate.take() {
|
// if let Some(isolate) = inner.isolate.take() {
|
||||||
isolate.post(vec![
|
// isolate.post(vec![
|
||||||
MESSAGE_STREAM_ABORT_JSON.into_dart(),
|
// MESSAGE_STREAM_ABORT_JSON.into_dart(),
|
||||||
veilid_core::serialize_json(error).into_dart(),
|
// veilid_core::serialize_json(error).into_dart(),
|
||||||
])
|
// ])
|
||||||
} else {
|
// } else {
|
||||||
false
|
// false
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn close(self) -> bool {
|
pub fn close(self) -> bool {
|
||||||
let mut inner = self.inner.lock();
|
let mut inner = self.inner.lock();
|
||||||
|
@ -130,11 +130,7 @@ fn do_clap_matches(default_config_path: &OsStr) -> Result<clap::ArgMatches, clap
|
|||||||
.value_name("BOOTSTRAP_NODE_LIST")
|
.value_name("BOOTSTRAP_NODE_LIST")
|
||||||
.help("Specify a list of bootstrap node dialinfos to use"),
|
.help("Specify a list of bootstrap node dialinfos to use"),
|
||||||
)
|
)
|
||||||
.arg(
|
;
|
||||||
Arg::new("local")
|
|
||||||
.long("local")
|
|
||||||
.help("Enable local peer scope")
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
let matches = matches.arg(
|
let matches = matches.arg(
|
||||||
@ -218,9 +214,6 @@ pub fn process_command_line() -> EyreResult<(Settings, ArgMatches)> {
|
|||||||
if matches.is_present("attach") {
|
if matches.is_present("attach") {
|
||||||
settingsrw.auto_attach = !matches!(matches.value_of("attach"), Some("true"));
|
settingsrw.auto_attach = !matches!(matches.value_of("attach"), Some("true"));
|
||||||
}
|
}
|
||||||
if matches.is_present("local") {
|
|
||||||
settingsrw.core.network.enable_local_peer_scope = true;
|
|
||||||
}
|
|
||||||
if matches.occurrences_of("delete-protected-store") != 0 {
|
if matches.occurrences_of("delete-protected-store") != 0 {
|
||||||
settingsrw.core.protected_store.delete = true;
|
settingsrw.core.protected_store.delete = true;
|
||||||
}
|
}
|
||||||
|
@ -99,7 +99,6 @@ core:
|
|||||||
upnp: true
|
upnp: true
|
||||||
natpmp: false
|
natpmp: false
|
||||||
detect_address_changes: true
|
detect_address_changes: true
|
||||||
enable_local_peer_scope: false
|
|
||||||
restricted_nat_retries: 0
|
restricted_nat_retries: 0
|
||||||
tls:
|
tls:
|
||||||
certificate_path: '%CERTIFICATE_PATH%'
|
certificate_path: '%CERTIFICATE_PATH%'
|
||||||
@ -589,7 +588,6 @@ pub struct Network {
|
|||||||
pub upnp: bool,
|
pub upnp: bool,
|
||||||
pub natpmp: bool,
|
pub natpmp: bool,
|
||||||
pub detect_address_changes: bool,
|
pub detect_address_changes: bool,
|
||||||
pub enable_local_peer_scope: bool,
|
|
||||||
pub restricted_nat_retries: u32,
|
pub restricted_nat_retries: u32,
|
||||||
pub tls: Tls,
|
pub tls: Tls,
|
||||||
pub application: Application,
|
pub application: Application,
|
||||||
@ -986,7 +984,6 @@ impl Settings {
|
|||||||
set_config_value!(inner.core.network.upnp, value);
|
set_config_value!(inner.core.network.upnp, value);
|
||||||
set_config_value!(inner.core.network.natpmp, value);
|
set_config_value!(inner.core.network.natpmp, value);
|
||||||
set_config_value!(inner.core.network.detect_address_changes, value);
|
set_config_value!(inner.core.network.detect_address_changes, value);
|
||||||
set_config_value!(inner.core.network.enable_local_peer_scope, value);
|
|
||||||
set_config_value!(inner.core.network.restricted_nat_retries, value);
|
set_config_value!(inner.core.network.restricted_nat_retries, value);
|
||||||
set_config_value!(inner.core.network.tls.certificate_path, value);
|
set_config_value!(inner.core.network.tls.certificate_path, value);
|
||||||
set_config_value!(inner.core.network.tls.private_key_path, value);
|
set_config_value!(inner.core.network.tls.private_key_path, value);
|
||||||
@ -1187,9 +1184,6 @@ impl Settings {
|
|||||||
"network.detect_address_changes" => {
|
"network.detect_address_changes" => {
|
||||||
Ok(Box::new(inner.core.network.detect_address_changes))
|
Ok(Box::new(inner.core.network.detect_address_changes))
|
||||||
}
|
}
|
||||||
"network.enable_local_peer_scope" => {
|
|
||||||
Ok(Box::new(inner.core.network.enable_local_peer_scope))
|
|
||||||
}
|
|
||||||
"network.restricted_nat_retries" => {
|
"network.restricted_nat_retries" => {
|
||||||
Ok(Box::new(inner.core.network.restricted_nat_retries))
|
Ok(Box::new(inner.core.network.restricted_nat_retries))
|
||||||
}
|
}
|
||||||
@ -1513,7 +1507,6 @@ mod tests {
|
|||||||
assert_eq!(s.core.network.upnp, true);
|
assert_eq!(s.core.network.upnp, true);
|
||||||
assert_eq!(s.core.network.natpmp, false);
|
assert_eq!(s.core.network.natpmp, false);
|
||||||
assert_eq!(s.core.network.detect_address_changes, true);
|
assert_eq!(s.core.network.detect_address_changes, true);
|
||||||
assert_eq!(s.core.network.enable_local_peer_scope, false);
|
|
||||||
assert_eq!(s.core.network.restricted_nat_retries, 0u32);
|
assert_eq!(s.core.network.restricted_nat_retries, 0u32);
|
||||||
//
|
//
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -232,6 +232,24 @@ pub fn get_veilid_state() -> Promise {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen()]
|
||||||
|
pub fn attach() -> Promise {
|
||||||
|
wrap_api_future(async move {
|
||||||
|
let veilid_api = get_veilid_api()?;
|
||||||
|
veilid_api.attach().await?;
|
||||||
|
APIRESULT_UNDEFINED
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen()]
|
||||||
|
pub fn detach() -> Promise {
|
||||||
|
wrap_api_future(async move {
|
||||||
|
let veilid_api = get_veilid_api()?;
|
||||||
|
veilid_api.detach().await?;
|
||||||
|
APIRESULT_UNDEFINED
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[wasm_bindgen()]
|
#[wasm_bindgen()]
|
||||||
pub fn shutdown_veilid_core() -> Promise {
|
pub fn shutdown_veilid_core() -> Promise {
|
||||||
wrap_api_future(async move {
|
wrap_api_future(async move {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user