dialinfoclass refactor, cleaning up network class detection
This commit is contained in:
parent
99dc4e16f9
commit
1156159748
@ -179,16 +179,24 @@ struct OperationInfoQ {
|
||||
nodeStatus @0 :NodeStatus; # node status update about the infoq sender
|
||||
}
|
||||
|
||||
|
||||
enum NetworkClass {
|
||||
server @0; # S = Device with public IP and no UDP firewall
|
||||
mapped @1; # M = Device with portmap behind any NAT
|
||||
fullConeNAT @2; # F = Device without portmap behind full-cone NAT
|
||||
addressRestrictedNAT @3; # A = Device without portmap behind address-only restricted NAT
|
||||
portRestrictedNAT @4; # P = Device without portmap behind address-and-port restricted NAT
|
||||
outboundOnly @5; # O = Outbound only
|
||||
webApp @6; # W = PWA
|
||||
invalid @7; # I = Invalid
|
||||
inboundCapable @0; # I = Inbound capable without relay, may require signal
|
||||
outboundOnly @1; # O = Outbound only, inbound relay required except with reverse connect signal
|
||||
webApp @2; # W = PWA, outbound relay is required in most cases
|
||||
}
|
||||
|
||||
enum DialInfoClass {
|
||||
direct @0; # D = Directly reachable with public IP and no firewall, with statically configured port
|
||||
mapped @1; # M = Directly reachable with via portmap behind any NAT or firewalled with dynamically negotiated port
|
||||
fullConeNAT @2; # F = Directly reachable device without portmap behind full-cone NAT
|
||||
blocked @3; # B = Inbound blocked at firewall but may hole punch with public address
|
||||
addressRestrictedNAT @4; # A = Device without portmap behind address-only restricted NAT
|
||||
portRestrictedNAT @5; # P = Device without portmap behind address-and-port restricted NAT
|
||||
}
|
||||
|
||||
struct DialInfoDetail {
|
||||
dialInfo @0; :DialInfo;
|
||||
class @1; :DialInfoClass;
|
||||
}
|
||||
|
||||
struct NodeStatus {
|
||||
@ -208,8 +216,8 @@ struct ProtocolSet {
|
||||
|
||||
struct NodeInfo {
|
||||
networkClass @0 :NetworkClass; # network class of this node
|
||||
outboundProtocols @1 :ProtocolSet; # protocols that can go outbound
|
||||
dialInfoList @2 :List(DialInfo); # inbound dial info for this node
|
||||
outboundProtocols @1 :ProtocolSet; # protocols that can go outbound
|
||||
dialInfoDetailList @2 :List(DialInfoDetail); # inbound dial info details for this node
|
||||
relayPeerInfo @3 :PeerInfo; # (optional) relay peer info for this node
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,34 @@ use crate::intf::*;
|
||||
use crate::routing_table::*;
|
||||
use crate::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct DiscoveryContext {
|
||||
routing_table: RoutingTable,
|
||||
external_ipv4: Option<Ipv4Addr>,
|
||||
external_ipv6: Option<Ipv6Addr>,
|
||||
network_class: Option<NetworkClass>,
|
||||
}
|
||||
|
||||
impl DiscoveryContext {
|
||||
pub fn new(routing_table: RoutingTable) -> Self {
|
||||
Self {
|
||||
routing_table,
|
||||
external_ipv4: None,
|
||||
external_ipv6: None,
|
||||
network_class: None,
|
||||
}
|
||||
}
|
||||
pub fn upgrade_network_class(&mut self, network_class: NetworkClass) {
|
||||
if let Some(old_nc) = self.network_class {
|
||||
if network_class < old_nc {
|
||||
self.network_class = Some(network_class);
|
||||
}
|
||||
} else {
|
||||
self.network_class = Some(network_class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Network {
|
||||
// Ask for a public address check from a particular noderef
|
||||
async fn request_public_address(&self, node_ref: NodeRef) -> Option<SocketAddress> {
|
||||
@ -100,7 +128,12 @@ impl Network {
|
||||
None
|
||||
}
|
||||
|
||||
pub async fn update_udpv4_dialinfo(&self) -> Result<(), String> {
|
||||
xxx split this routine up into helper routines that can be used by different protocols too.
|
||||
|
||||
pub async fn update_udpv4_dialinfo(
|
||||
&self,
|
||||
context: &mut DiscoveryContext,
|
||||
) -> Result<(), String> {
|
||||
log_net!("looking for udpv4 public dial info");
|
||||
let routing_table = self.routing_table();
|
||||
|
||||
@ -130,24 +163,41 @@ impl Network {
|
||||
// If our local interface list contains external1 then there is no NAT in place
|
||||
if intf_addrs.contains(&external1) {
|
||||
// No NAT
|
||||
// Do a validate_dial_info on the external address from a routed node
|
||||
// Do a validate_dial_info on the external address from a redirected node
|
||||
if self
|
||||
.validate_dial_info(node_b.clone(), external1_dial_info.clone(), true, false)
|
||||
.await
|
||||
{
|
||||
// Add public dial info with Server network class
|
||||
routing_table.register_public_dial_info(
|
||||
// Add public dial info with Direct dialinfo class
|
||||
routing_table.register_dial_info(
|
||||
RoutingDomain::PublicInternet,
|
||||
external1_dial_info,
|
||||
DialInfoOrigin::Discovered,
|
||||
Some(NetworkClass::Server),
|
||||
DialInfoClass::Direct,
|
||||
);
|
||||
|
||||
// No more retries
|
||||
break;
|
||||
} else {
|
||||
// UDP firewall?
|
||||
log_net!("UDP static public dial info not reachable. UDP firewall may be blocking inbound to {:?} for {:?}",external1_dial_info, node_b);
|
||||
}
|
||||
// Attempt a UDP port mapping via all available and enabled mechanisms
|
||||
else if let Some(external_mapped) = self
|
||||
.try_port_mapping(&intf_addrs, ProtocolType::UDP, AddressType::IPV4)
|
||||
.await
|
||||
{
|
||||
// Got a port mapping, let's use it
|
||||
let external_mapped_dial_info = DialInfo::udp(external_mapped);
|
||||
routing_table.register_dial_info(
|
||||
RoutingDomain::PublicInternet,
|
||||
external_mapped_dial_info,
|
||||
DialInfoClass::Mapped,
|
||||
);
|
||||
} else {
|
||||
// Add public dial info with Blocked dialinfo class
|
||||
routing_table.register_dial_info(
|
||||
RoutingDomain::PublicInternet,
|
||||
external1_dial_info,
|
||||
DialInfoClass::Blocked,
|
||||
);
|
||||
}
|
||||
context.upgrade_network_class(NetworkClass::InboundCapable);
|
||||
// No more retries
|
||||
break;
|
||||
} else {
|
||||
// There is -some NAT-
|
||||
// Attempt a UDP port mapping via all available and enabled mechanisms
|
||||
@ -157,11 +207,12 @@ impl Network {
|
||||
{
|
||||
// Got a port mapping, let's use it
|
||||
let external_mapped_dial_info = DialInfo::udp(external_mapped);
|
||||
routing_table.register_public_dial_info(
|
||||
routing_table.register_dial_info(
|
||||
RoutingDomain::PublicInternet,
|
||||
external_mapped_dial_info,
|
||||
DialInfoOrigin::Mapped,
|
||||
Some(NetworkClass::Mapped),
|
||||
DialInfoClass::Mapped,
|
||||
);
|
||||
context.upgrade_network_class(NetworkClass::InboundCapable);
|
||||
|
||||
// No more retries
|
||||
break;
|
||||
@ -180,11 +231,12 @@ impl Network {
|
||||
{
|
||||
// Yes, another machine can use the dial info directly, so Full Cone
|
||||
// Add public dial info with full cone NAT network class
|
||||
routing_table.register_public_dial_info(
|
||||
routing_table.register_dial_info(
|
||||
RoutingDomain::PublicInternet,
|
||||
external1_dial_info,
|
||||
DialInfoOrigin::Discovered,
|
||||
Some(NetworkClass::FullConeNAT),
|
||||
DialInfoClass::FullConeNAT,
|
||||
);
|
||||
context.upgrade_network_class(NetworkClass::InboundCapable);
|
||||
|
||||
// No more retries
|
||||
break;
|
||||
@ -209,7 +261,7 @@ impl Network {
|
||||
// If we have two different external addresses, then this is a symmetric NAT
|
||||
if external2 != external1 {
|
||||
// Symmetric NAT is outbound only, no public dial info will work
|
||||
self.inner.lock().network_class = Some(NetworkClass::OutboundOnly);
|
||||
context.upgrade_network_class(NetworkClass::OutboundOnly);
|
||||
|
||||
// No more retries
|
||||
break;
|
||||
@ -230,19 +282,20 @@ impl Network {
|
||||
.await
|
||||
{
|
||||
// Got a reply from a non-default port, which means we're only address restricted
|
||||
routing_table.register_public_dial_info(
|
||||
routing_table.register_dial_info(
|
||||
RoutingDomain::PublicInternet,
|
||||
external1_dial_info,
|
||||
DialInfoOrigin::Discovered,
|
||||
Some(NetworkClass::AddressRestrictedNAT),
|
||||
DialInfoClass::AddressRestrictedNAT,
|
||||
);
|
||||
} else {
|
||||
// Didn't get a reply from a non-default port, which means we are also port restricted
|
||||
routing_table.register_public_dial_info(
|
||||
routing_table.register_dial_info(
|
||||
RoutingDomain::PublicInternet,
|
||||
external1_dial_info,
|
||||
DialInfoOrigin::Discovered,
|
||||
Some(NetworkClass::PortRestrictedNAT),
|
||||
DialInfoClass::PortRestrictedNAT,
|
||||
);
|
||||
}
|
||||
context.upgrade_network_class(NetworkClass::InboundCapable);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -255,18 +308,49 @@ impl Network {
|
||||
}
|
||||
}
|
||||
|
||||
// xxx should verify hole punch capable somehow and switch to outbound-only if hole punch can't work
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn update_tcpv4_dialinfo(&self) -> Result<(), String> {
|
||||
pub async fn update_tcpv4_dialinfo(
|
||||
&self,
|
||||
context: &mut DiscoveryContext,
|
||||
) -> Result<(), String> {
|
||||
log_net!("looking for tcpv4 public dial info");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn update_wsv4_dialinfo(&self, context: &mut DiscoveryContext) -> Result<(), String> {
|
||||
log_net!("looking for wsv4 public dial info");
|
||||
// xxx
|
||||
//Err("unimplemented".to_owned())
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn update_wsv4_dialinfo(&self) -> Result<(), String> {
|
||||
log_net!("looking for wsv4 public dial info");
|
||||
pub async fn update_udpv6_dialinfo(
|
||||
&self,
|
||||
context: &mut DiscoveryContext,
|
||||
) -> Result<(), String> {
|
||||
log_net!("looking for udpv6 public dial info");
|
||||
// xxx
|
||||
//Err("unimplemented".to_owned())
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn update_tcpv6_dialinfo(
|
||||
&self,
|
||||
context: &mut DiscoveryContext,
|
||||
) -> Result<(), String> {
|
||||
log_net!("looking for tcpv6 public dial info");
|
||||
// xxx
|
||||
//Err("unimplemented".to_owned())
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn update_wsv6_dialinfo(&self, context: &mut DiscoveryContext) -> Result<(), String> {
|
||||
log_net!("looking for wsv6 public dial info");
|
||||
// xxx
|
||||
//Err("unimplemented".to_owned())
|
||||
Ok(())
|
||||
@ -282,18 +366,25 @@ impl Network {
|
||||
.clone()
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut context = DiscoveryContext::default();
|
||||
|
||||
if protocol_config.inbound.contains(ProtocolType::UDP) {
|
||||
self.update_udpv4_dialinfo().await?;
|
||||
self.update_udpv4_dialinfo(&mut context).await?;
|
||||
self.update_udpv6_dialinfo(&mut context).await?;
|
||||
}
|
||||
|
||||
if protocol_config.inbound.contains(ProtocolType::TCP) {
|
||||
self.update_tcpv4_dialinfo().await?;
|
||||
self.update_tcpv4_dialinfo(&mut context).await?;
|
||||
self.update_tcpv6_dialinfo(&mut context).await?;
|
||||
}
|
||||
|
||||
if protocol_config.inbound.contains(ProtocolType::WS) {
|
||||
self.update_wsv4_dialinfo().await?;
|
||||
self.update_wsv4_dialinfo(&mut context).await?;
|
||||
self.update_wsv6_dialinfo(&mut context).await?;
|
||||
}
|
||||
|
||||
self.inner.lock().network_class = context.network_class;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,9 @@ impl RoutingTable {
|
||||
// does it have matching public dial info?
|
||||
entry
|
||||
.node_info()
|
||||
.first_filtered_dial_info(|di| di.matches_filter(&dial_info_filter1))
|
||||
.first_filtered_dial_info_detail(|did| {
|
||||
did.matches_filter(&dial_info_filter1)
|
||||
})
|
||||
.is_some()
|
||||
},
|
||||
)),
|
||||
@ -55,11 +57,7 @@ impl RoutingTable {
|
||||
node_info: NodeInfo {
|
||||
network_class: netman.get_network_class().unwrap_or(NetworkClass::Invalid),
|
||||
outbound_protocols: netman.get_protocol_config().unwrap_or_default().outbound,
|
||||
dial_info_list: self
|
||||
.dial_info_details(RoutingDomain::PublicInternet)
|
||||
.iter()
|
||||
.map(|did| did.dial_info.clone())
|
||||
.collect(),
|
||||
dial_info_detail_list: self.dial_info_details(RoutingDomain::PublicInternet),
|
||||
relay_peer_info: relay_node.map(|rn| Box::new(rn.peer_info())),
|
||||
},
|
||||
}
|
||||
|
@ -22,13 +22,6 @@ pub use stats_accounting::*;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq)]
|
||||
pub enum DialInfoOrigin {
|
||||
Static,
|
||||
Discovered,
|
||||
Mapped,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq)]
|
||||
pub enum RoutingDomain {
|
||||
PublicInternet,
|
||||
@ -40,19 +33,6 @@ pub struct RoutingDomainDetail {
|
||||
dial_info_details: Vec<DialInfoDetail>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq)]
|
||||
pub struct DialInfoDetail {
|
||||
pub dial_info: DialInfo,
|
||||
pub origin: DialInfoOrigin,
|
||||
pub timestamp: u64,
|
||||
}
|
||||
|
||||
impl MatchesDialInfoFilter for DialInfoDetail {
|
||||
fn matches_filter(&self, filter: &DialInfoFilter) -> bool {
|
||||
self.dial_info.matches_filter(filter)
|
||||
}
|
||||
}
|
||||
|
||||
struct RoutingTableInner {
|
||||
network_manager: NetworkManager,
|
||||
node_id: DHTKey,
|
||||
@ -223,28 +203,40 @@ impl RoutingTable {
|
||||
|
||||
pub fn all_filtered_dial_info_details(
|
||||
&self,
|
||||
domain: RoutingDomain,
|
||||
domain: Option<RoutingDomain>,
|
||||
filter: &DialInfoFilter,
|
||||
) -> Vec<DialInfoDetail> {
|
||||
let inner = self.inner.lock();
|
||||
Self::with_routing_domain(&*inner, domain, |rd| {
|
||||
let mut ret = Vec::new();
|
||||
for did in rd.dial_info_details {
|
||||
if did.matches_filter(filter) {
|
||||
ret.push(did.clone());
|
||||
let mut ret = Vec::new();
|
||||
|
||||
if domain == None || domain == Some(RoutingDomain::Local) {
|
||||
Self::with_routing_domain(&*inner, RoutingDomain::Local, |rd| {
|
||||
for did in rd.dial_info_details {
|
||||
if did.matches_filter(filter) {
|
||||
ret.push(did.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
ret
|
||||
})
|
||||
});
|
||||
}
|
||||
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) {
|
||||
ret.push(did.clone());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
ret.remove_duplicates();
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn register_dial_info(
|
||||
&self,
|
||||
domain: RoutingDomain,
|
||||
dial_info: DialInfo,
|
||||
origin: DialInfoOrigin,
|
||||
class: DialInfoClass,
|
||||
) {
|
||||
let timestamp = get_timestamp();
|
||||
let enable_local_peer_scope = {
|
||||
let config = self.network_manager().config();
|
||||
let c = config.get();
|
||||
@ -267,8 +259,7 @@ impl RoutingTable {
|
||||
Self::with_routing_domain_mut(&mut *inner, domain, |rd| {
|
||||
rd.dial_info_details.push(DialInfoDetail {
|
||||
dial_info: dial_info.clone(),
|
||||
origin,
|
||||
timestamp,
|
||||
class,
|
||||
});
|
||||
});
|
||||
|
||||
@ -285,7 +276,7 @@ impl RoutingTable {
|
||||
}
|
||||
.to_string(),
|
||||
);
|
||||
debug!(" Origin: {:?}", origin);
|
||||
debug!(" Class: {:?}", class);
|
||||
}
|
||||
|
||||
pub fn clear_dial_info_details(&self, domain: RoutingDomain) {
|
||||
@ -611,7 +602,7 @@ impl RoutingTable {
|
||||
log_rtab!("--- bootstrap_task");
|
||||
|
||||
// Map all bootstrap entries to a single key with multiple dialinfo
|
||||
let mut bsmap: BTreeMap<DHTKey, Vec<DialInfo>> = BTreeMap::new();
|
||||
let mut bsmap: BTreeMap<DHTKey, Vec<DialInfoDetail>> = BTreeMap::new();
|
||||
for b in bootstrap {
|
||||
let ndis = NodeDialInfo::from_str(b.as_str())
|
||||
.map_err(map_to_string)
|
||||
@ -620,7 +611,10 @@ impl RoutingTable {
|
||||
bsmap
|
||||
.entry(node_id)
|
||||
.or_insert_with(Vec::new)
|
||||
.push(ndis.dial_info);
|
||||
.push(DialInfoDetail {
|
||||
dial_info: ndis.dial_info,
|
||||
class: DialInfoClass::Direct,
|
||||
});
|
||||
}
|
||||
log_rtab!(" bootstrap list: {:?}", bsmap);
|
||||
|
||||
@ -634,8 +628,8 @@ impl RoutingTable {
|
||||
NodeInfo {
|
||||
network_class: NetworkClass::Server, // Bootstraps are always full servers
|
||||
outbound_protocols: ProtocolSet::empty(), // Bootstraps do not participate in relaying and will not make outbound requests
|
||||
dial_info_list: v, // Dial info is as specified in the bootstrap list
|
||||
relay_peer_info: None, // Bootstraps never require a relay themselves
|
||||
dial_info_detail_list: v, // Dial info is as specified in the bootstrap list
|
||||
relay_peer_info: None, // Bootstraps never require a relay themselves
|
||||
},
|
||||
)
|
||||
.map_err(logthru_rtab!("Couldn't add bootstrap node: {}", k))?;
|
||||
|
@ -90,30 +90,42 @@ impl NodeRef {
|
||||
nr
|
||||
})
|
||||
}
|
||||
pub fn first_filtered_dial_info(&self) -> Option<DialInfo> {
|
||||
pub fn first_filtered_dial_info_detail(
|
||||
&self,
|
||||
routing_domain: Option<RoutingDomain>,
|
||||
) -> Option<DialInfoDetail> {
|
||||
self.operate(|e| {
|
||||
if matches!(
|
||||
self.filter.map(|f| f.peer_scope).unwrap_or(PeerScope::All),
|
||||
PeerScope::All | PeerScope::Local
|
||||
) {
|
||||
e.local_node_info().first_filtered_dial_info(|di| {
|
||||
if let Some(filter) = self.filter {
|
||||
di.matches_filter(&filter)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
if (routing_domain == None || routing_domain == Some(RoutingDomain::LocalNetwork))
|
||||
&& matches!(
|
||||
self.filter.map(|f| f.peer_scope).unwrap_or(PeerScope::All),
|
||||
PeerScope::All | PeerScope::Local
|
||||
)
|
||||
{
|
||||
e.local_node_info()
|
||||
.first_filtered_dial_info(|di| {
|
||||
if let Some(filter) = self.filter {
|
||||
di.matches_filter(&filter)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
.map(|di| DialInfoDetail {
|
||||
class: DialInfoClass::Direct,
|
||||
dial_info: di,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
.or_else(|| {
|
||||
if matches!(
|
||||
self.filter.map(|f| f.peer_scope).unwrap_or(PeerScope::All),
|
||||
PeerScope::All | PeerScope::Global
|
||||
) {
|
||||
e.node_info().first_filtered_dial_info(|di| {
|
||||
if (routing_domain == None || routing_domain == Some(RoutingDomain::PublicInternet))
|
||||
&& matches!(
|
||||
self.filter.map(|f| f.peer_scope).unwrap_or(PeerScope::All),
|
||||
PeerScope::All | PeerScope::Global
|
||||
)
|
||||
{
|
||||
e.node_info().first_filtered_dial_info_detail(|did| {
|
||||
if let Some(filter) = self.filter {
|
||||
di.matches_filter(&filter)
|
||||
did.matches_filter(&filter)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
@ -125,34 +137,47 @@ impl NodeRef {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn all_filtered_dial_info<F>(&self) -> Vec<DialInfo> {
|
||||
pub fn all_filtered_dial_info_details<F>(
|
||||
&self,
|
||||
routing_domain: Option<RoutingDomain>,
|
||||
) -> Vec<DialInfoDetail> {
|
||||
let mut out = Vec::new();
|
||||
self.operate(|e| {
|
||||
if matches!(
|
||||
self.filter.map(|f| f.peer_scope).unwrap_or(PeerScope::All),
|
||||
PeerScope::All | PeerScope::Global
|
||||
) {
|
||||
out.append(&mut e.node_info().all_filtered_dial_info(|di| {
|
||||
if (routing_domain == None || routing_domain == Some(RoutingDomain::LocalNetwork))
|
||||
&& matches!(
|
||||
self.filter.map(|f| f.peer_scope).unwrap_or(PeerScope::All),
|
||||
PeerScope::All | PeerScope::Local
|
||||
)
|
||||
{
|
||||
for di in e.local_node_info().all_filtered_dial_info(|di| {
|
||||
if let Some(filter) = self.filter {
|
||||
di.matches_filter(&filter)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}))
|
||||
}) {
|
||||
out.push(DialInfoDetail {
|
||||
class: DialInfoClass::Direct,
|
||||
dial_info: di,
|
||||
});
|
||||
}
|
||||
}
|
||||
if matches!(
|
||||
self.filter.map(|f| f.peer_scope).unwrap_or(PeerScope::All),
|
||||
PeerScope::All | PeerScope::Local
|
||||
) {
|
||||
out.append(&mut e.local_node_info().all_filtered_dial_info(|di| {
|
||||
if (routing_domain == None || routing_domain == Some(RoutingDomain::PublicInternet))
|
||||
&& matches!(
|
||||
self.filter.map(|f| f.peer_scope).unwrap_or(PeerScope::All),
|
||||
PeerScope::All | PeerScope::Global
|
||||
)
|
||||
{
|
||||
out.append(&mut e.node_info().all_filtered_dial_info_details(|did| {
|
||||
if let Some(filter) = self.filter {
|
||||
di.matches_filter(&filter)
|
||||
did.matches_filter(&filter)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}))
|
||||
}
|
||||
});
|
||||
out.remove_duplicates();
|
||||
out
|
||||
}
|
||||
|
||||
|
23
veilid-core/src/rpc_processor/coders/dial_info_class.rs
Normal file
23
veilid-core/src/rpc_processor/coders/dial_info_class.rs
Normal file
@ -0,0 +1,23 @@
|
||||
use crate::*;
|
||||
|
||||
pub fn encode_dial_info_class(dial_info_class: DialInfoClass) -> veilid_capnp::DialInfoClass {
|
||||
match dial_info_class {
|
||||
DialInfoClass::Direct => veilid_capnp::DialInfoClass::Direct,
|
||||
DialInfoClass::Mapped => veilid_capnp::DialInfoClass::Mapped,
|
||||
DialInfoClass::FullConeNAT => veilid_capnp::DialInfoClass::FullConeNAT,
|
||||
DialInfoClass::Blocked => veilid_capnp::DialInfoClass::Blocked,
|
||||
DialInfoClass::AddressRestrictedNAT => veilid_capnp::DialInfoClass::AddressRestrictedNAT,
|
||||
DialInfoClass::PortRestrictedNAT => veilid_capnp::DialInfoClass::PortRestrictedNAT,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decode_dial_info_class(dial_info_class: veilid_capnp::DialInfoClass) -> DialInfoClass {
|
||||
match dial_info_class {
|
||||
veilid_capnp::DialInfoClass::Direct => DialInfoClass::Direct,
|
||||
veilid_capnp::DialInfoClass::Mapped => DialInfoClass::Mapped,
|
||||
veilid_capnp::DialInfoClass::FullConeNAT => DialInfoClass::FullConeNAT,
|
||||
veilid_capnp::DialInfoClass::Blocked => DialInfoClass::Blocked,
|
||||
veilid_capnp::DialInfoClass::AddressRestrictedNAT => DialInfoClass::AddressRestrictedNAT,
|
||||
veilid_capnp::DialInfoClass::PortRestrictedNAT => DialInfoClass::PortRestrictedNAT,
|
||||
}
|
||||
}
|
33
veilid-core/src/rpc_processor/coders/dial_info_detail.rs
Normal file
33
veilid-core/src/rpc_processor/coders/dial_info_detail.rs
Normal file
@ -0,0 +1,33 @@
|
||||
use crate::*;
|
||||
use rpc_processor::*;
|
||||
|
||||
pub fn encode_dial_info_detail(
|
||||
dial_info_detail: &DialInfoDetail,
|
||||
builder: &mut veilid_capnp::dial_info_detail::Builder,
|
||||
) -> Result<(), RPCError> {
|
||||
let mut di_builder = builder.reborrow().init_dial_info();
|
||||
encode_dial_info(&node_info.dial_info, &mut di_builder)?;
|
||||
|
||||
builder.set_class(encode_dial_info_class(dial_info_detail.class));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn decode_dial_info_detail(
|
||||
reader: &veilid_capnp::dial_info_detail::Reader,
|
||||
) -> Result<DialInfoDetail, RPCError> {
|
||||
let dial_info = decode_dial_info(
|
||||
&reader
|
||||
.reborrow()
|
||||
.get_dial_info()
|
||||
.map_err(map_error_capnp_error!())?,
|
||||
)?;
|
||||
|
||||
let dial_info_class = decode_dial_info_class(
|
||||
reader
|
||||
.reborrow()
|
||||
.get_class()
|
||||
.map_err(map_error_capnp_notinschema!())?,
|
||||
);
|
||||
|
||||
Ok(DialInfoDetail { dial_info, class })
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
mod address;
|
||||
mod dial_info;
|
||||
mod dial_info_class;
|
||||
mod dial_info_detail;
|
||||
mod network_class;
|
||||
mod node_dial_info;
|
||||
mod node_info;
|
||||
@ -15,6 +17,8 @@ mod socket_address;
|
||||
|
||||
pub use address::*;
|
||||
pub use dial_info::*;
|
||||
pub use dial_info_class::*;
|
||||
pub use dial_info_detail::*;
|
||||
pub use network_class::*;
|
||||
pub use node_dial_info::*;
|
||||
pub use node_info::*;
|
||||
|
@ -10,17 +10,19 @@ pub fn encode_node_info(
|
||||
let mut ps_builder = builder.reborrow().init_outbound_protocols();
|
||||
encode_protocol_set(&node_info.outbound_protocols, &mut ps_builder)?;
|
||||
|
||||
let mut dil_builder = builder.reborrow().init_dial_info_list(
|
||||
let mut didl_builder = builder.reborrow().init_dial_info_detail_list(
|
||||
node_info
|
||||
.dial_info_list
|
||||
.dial_info_detail_list
|
||||
.len()
|
||||
.try_into()
|
||||
.map_err(map_error_protocol!("too many dial infos in node info"))?,
|
||||
.map_err(map_error_protocol!(
|
||||
"too many dial info details in node info"
|
||||
))?,
|
||||
);
|
||||
|
||||
for idx in 0..node_info.dial_info_list.len() {
|
||||
let mut di_builder = dil_builder.reborrow().get(idx as u32);
|
||||
encode_dial_info(&node_info.dial_info_list[idx], &mut di_builder)?;
|
||||
for idx in 0..node_info.dial_info_detail_list.len() {
|
||||
let mut did_builder = didl_builder.reborrow().get(idx as u32);
|
||||
encode_dial_info_detail(&node_info.dial_info_detail_list[idx], &mut did_builder)?;
|
||||
}
|
||||
|
||||
if let Some(rpi) = &node_info.relay_peer_info {
|
||||
@ -49,18 +51,18 @@ pub fn decode_node_info(
|
||||
.map_err(map_error_capnp_error!())?,
|
||||
)?;
|
||||
|
||||
let dil_reader = reader
|
||||
let didl_reader = reader
|
||||
.reborrow()
|
||||
.get_dial_info_list()
|
||||
.get_dial_info_detail_list()
|
||||
.map_err(map_error_capnp_error!())?;
|
||||
let mut dial_info_list = Vec::<DialInfo>::with_capacity(
|
||||
dil_reader
|
||||
let mut dial_info_detail_list = Vec::<DialInfo>::with_capacity(
|
||||
didl_reader
|
||||
.len()
|
||||
.try_into()
|
||||
.map_err(map_error_protocol!("too many dial infos"))?,
|
||||
.map_err(map_error_protocol!("too many dial info details"))?,
|
||||
);
|
||||
for di in dil_reader.iter() {
|
||||
dial_info_list.push(decode_dial_info(&di)?)
|
||||
dial_info_detail_list.push(decode_dial_info_detail(&di)?)
|
||||
}
|
||||
|
||||
let relay_peer_info = if allow_relay_peer_info {
|
||||
@ -82,7 +84,7 @@ pub fn decode_node_info(
|
||||
Ok(NodeInfo {
|
||||
network_class,
|
||||
outbound_protocols,
|
||||
dial_info_list,
|
||||
dial_info_detail_list,
|
||||
relay_peer_info,
|
||||
})
|
||||
}
|
||||
|
@ -1349,12 +1349,16 @@ impl RPCProcessor {
|
||||
}
|
||||
|
||||
// Gets a 'RespondTo::Sender' that contains either our dial info,
|
||||
// or None if the peer has seen our dial info before
|
||||
pub fn get_respond_to_sender(&self, peer: NodeRef) -> RespondTo {
|
||||
if peer.has_seen_our_node_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 {
|
||||
let our_node_info = self.routing_table().get_own_peer_info().node_info;
|
||||
if peer.has_seen_our_node_info()
|
||||
|| matches!(our_node_info.network_class, NetworkClass::Invalid)
|
||||
{
|
||||
RespondTo::Sender(None)
|
||||
} else {
|
||||
RespondTo::Sender(Some(self.routing_table().get_own_peer_info().node_info))
|
||||
RespondTo::Sender(Some(our_node_info))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1366,7 +1370,7 @@ impl RPCProcessor {
|
||||
let mut question = info_q_msg.init_root::<veilid_capnp::operation::Builder>();
|
||||
question.set_op_id(self.get_next_op_id());
|
||||
let mut respond_to = question.reborrow().init_respond_to();
|
||||
self.get_respond_to_sender(peer.clone())
|
||||
self.make_respond_to_sender(peer.clone())
|
||||
.encode(&mut respond_to)?;
|
||||
let detail = question.reborrow().init_detail();
|
||||
let mut iqb = detail.init_info_q();
|
||||
|
@ -236,78 +236,56 @@ pub struct SenderInfo {
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
|
||||
pub enum NetworkClass {
|
||||
Server = 0, // S = Device with public IP and no UDP firewall
|
||||
Mapped = 1, // M = Device with portmap behind any NAT
|
||||
FullConeNAT = 2, // F = Device without portmap behind full-cone NAT
|
||||
AddressRestrictedNAT = 3, // A = Device without portmap behind address-only restricted NAT
|
||||
PortRestrictedNAT = 4, // P = Device without portmap behind address-and-port restricted NAT
|
||||
OutboundOnly = 5, // O = Outbound only
|
||||
WebApp = 6, // W = PWA
|
||||
Invalid = 7, // I = Invalid network class, unreachable or can not send packets
|
||||
pub enum DialInfoClass {
|
||||
Direct = 0, // D = Directly reachable with public IP and no firewall, with statically configured port
|
||||
Mapped = 1, // M = Directly reachable with via portmap behind any NAT or firewalled with dynamically negotiated port
|
||||
FullConeNAT = 2, // F = Directly reachable device without portmap behind full-cone NAT
|
||||
Blocked = 3, // B = Inbound blocked at firewall but may hole punch with public address
|
||||
AddressRestrictedNAT = 4, // A = Device without portmap behind address-only restricted NAT
|
||||
PortRestrictedNAT = 5, // P = Device without portmap behind address-and-port restricted NAT
|
||||
}
|
||||
|
||||
impl NetworkClass {
|
||||
// Can the node receive inbound requests without a relay?
|
||||
pub fn inbound_capable(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::Server
|
||||
| Self::Mapped
|
||||
| Self::FullConeNAT
|
||||
| Self::AddressRestrictedNAT
|
||||
| Self::PortRestrictedNAT
|
||||
)
|
||||
}
|
||||
|
||||
// Should an outbound relay be kept available?
|
||||
pub fn outbound_wants_relay(&self) -> bool {
|
||||
matches!(self, Self::WebApp)
|
||||
}
|
||||
|
||||
impl DialInfoClass {
|
||||
// Is a signal required to do an inbound hole-punch?
|
||||
pub fn inbound_requires_signal(&self) -> bool {
|
||||
matches!(self, Self::AddressRestrictedNAT | Self::PortRestrictedNAT)
|
||||
}
|
||||
|
||||
// Is some relay required either for signal or inbound relay or outbound relay?
|
||||
pub fn needs_relay(&self) -> bool {
|
||||
pub fn requires_signal(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::AddressRestrictedNAT
|
||||
| Self::PortRestrictedNAT
|
||||
| Self::OutboundOnly
|
||||
| Self::WebApp
|
||||
Self::Blocked | Self::AddressRestrictedNAT | Self::PortRestrictedNAT
|
||||
)
|
||||
}
|
||||
|
||||
// Must keepalive be used to preserve the public dialinfo in use?
|
||||
// Keepalive can be to either a
|
||||
pub fn dialinfo_requires_keepalive(&self) -> bool {
|
||||
// Does a relay node need to be allocated for this dial info?
|
||||
// For full cone NAT, the relay itself may not be used but the keepalive sent to it
|
||||
// is required to keep the NAT mapping valid in the router state table
|
||||
pub fn requires_relay(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::FullConeNAT
|
||||
| Self::Blocked
|
||||
| Self::AddressRestrictedNAT
|
||||
| Self::PortRestrictedNAT
|
||||
| Self::OutboundOnly
|
||||
| Self::WebApp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Can this node assist with signalling? Yes but only if it doesn't require signalling, itself.
|
||||
pub fn can_signal(&self) -> bool {
|
||||
self.inbound_capable() && !self.inbound_requires_signal()
|
||||
}
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq, Serialize, Deserialize)]
|
||||
pub struct DialInfoDetail {
|
||||
pub dial_info: DialInfo,
|
||||
pub class: DialInfoClass,
|
||||
}
|
||||
|
||||
// Can this node relay be an inbound relay?
|
||||
pub fn can_inbound_relay(&self) -> bool {
|
||||
matches!(self, Self::Server | Self::Mapped | Self::FullConeNAT)
|
||||
impl MatchesDialInfoFilter for DialInfoDetail {
|
||||
fn matches_filter(&self, filter: &DialInfoFilter) -> bool {
|
||||
self.dial_info.matches_filter(filter)
|
||||
}
|
||||
}
|
||||
|
||||
// Is this node capable of validating dial info
|
||||
pub fn can_validate_dial_info(&self) -> bool {
|
||||
matches!(self, Self::Server | Self::Mapped | Self::FullConeNAT)
|
||||
}
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
|
||||
pub enum NetworkClass {
|
||||
InboundCapable = 0, // I = Inbound capable without relay, may require signal
|
||||
OutboundOnly = 1, // O = Outbound only, inbound relay required except with reverse connect signal
|
||||
WebApp = 2, // W = PWA, outbound relay is required in most cases
|
||||
Invalid = 3, // X = Invalid network class, we don't know how to reach this node
|
||||
}
|
||||
|
||||
impl Default for NetworkClass {
|
||||
@ -316,6 +294,13 @@ impl Default for NetworkClass {
|
||||
}
|
||||
}
|
||||
|
||||
impl NetworkClass {
|
||||
// Should an outbound relay be kept available?
|
||||
pub fn outbound_wants_relay(&self) -> bool {
|
||||
matches!(self, Self::WebApp)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct NodeStatus {
|
||||
pub will_route: bool,
|
||||
@ -329,39 +314,39 @@ pub struct NodeStatus {
|
||||
pub struct NodeInfo {
|
||||
pub network_class: NetworkClass,
|
||||
pub outbound_protocols: ProtocolSet,
|
||||
pub dial_info_list: Vec<DialInfo>,
|
||||
pub dial_info_detail_list: Vec<DialInfoDetail>,
|
||||
pub relay_peer_info: Option<Box<PeerInfo>>,
|
||||
}
|
||||
|
||||
impl NodeInfo {
|
||||
pub fn first_filtered_dial_info<F>(&self, filter: F) -> Option<DialInfo>
|
||||
pub fn first_filtered_dial_info_detail<F>(&self, filter: F) -> Option<DialInfoDetail>
|
||||
where
|
||||
F: Fn(&DialInfo) -> bool,
|
||||
F: Fn(&DialInfoDetail) -> bool,
|
||||
{
|
||||
for di in &self.dial_info_list {
|
||||
if filter(di) {
|
||||
return Some(di.clone());
|
||||
for did in &self.dial_info_detail_list {
|
||||
if filter(&did) {
|
||||
return Some(did.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn all_filtered_dial_info<F>(&self, filter: F) -> Vec<DialInfo>
|
||||
pub fn all_filtered_dial_info_details<F>(&self, filter: F) -> Vec<DialInfoDetail>
|
||||
where
|
||||
F: Fn(&DialInfo) -> bool,
|
||||
F: Fn(&DialInfoDetail) -> bool,
|
||||
{
|
||||
let mut dial_info_list = Vec::new();
|
||||
let mut dial_info_detail_list = Vec::new();
|
||||
|
||||
for di in &self.dial_info_list {
|
||||
if filter(di) {
|
||||
dial_info_list.push(di.clone());
|
||||
for did in &self.dial_info_detail_list {
|
||||
if filter(&did) {
|
||||
dial_info_detail_list.push(did.clone());
|
||||
}
|
||||
}
|
||||
dial_info_list
|
||||
dial_info_detail_list
|
||||
}
|
||||
|
||||
pub fn has_any_dial_info(&self) -> bool {
|
||||
!self.dial_info_list.is_empty()
|
||||
!self.dial_info_detail_list.is_empty()
|
||||
|| !self
|
||||
.relay_peer_info
|
||||
.as_ref()
|
||||
@ -370,7 +355,55 @@ impl NodeInfo {
|
||||
}
|
||||
|
||||
pub fn has_direct_dial_info(&self) -> bool {
|
||||
!self.dial_info_list.is_empty()
|
||||
!self.dial_info_detail_list.is_empty()
|
||||
}
|
||||
|
||||
// Is some relay required either for signal or inbound relay or outbound relay?
|
||||
pub fn requires_relay(&self) -> bool {
|
||||
match self.network_class {
|
||||
NetworkClass::InboundCapable => {
|
||||
for did in &self.dial_info_detail_list {
|
||||
if did.class.requires_relay() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
NetworkClass::OutboundOnly => {
|
||||
return true;
|
||||
}
|
||||
NetworkClass::WebApp => {
|
||||
return true;
|
||||
}
|
||||
NetworkClass::Invalid => {}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
// Can this node assist with signalling? Yes but only if it doesn't require signalling, itself.
|
||||
pub fn can_signal(&self) -> bool {
|
||||
// Must be inbound capable
|
||||
if !matches!(self.network_class, NetworkClass::InboundCapable) {
|
||||
return false;
|
||||
}
|
||||
// Do any of our dial info require signalling? if so, we can't offer signalling
|
||||
for did in &self.dial_info_detail_list {
|
||||
if did.class.requires_signal() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
// Can this node relay be an inbound relay?
|
||||
pub fn can_inbound_relay(&self) -> bool {
|
||||
// For now this is the same
|
||||
self.can_signal()
|
||||
}
|
||||
|
||||
// Is this node capable of validating dial info
|
||||
pub fn can_validate_dial_info(&self) -> bool {
|
||||
// For now this is the same
|
||||
self.can_signal()
|
||||
}
|
||||
}
|
||||
|
||||
@ -674,13 +707,6 @@ pub struct DialInfoWSS {
|
||||
pub request: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Serialize, Deserialize)]
|
||||
#[serde(tag = "kind")]
|
||||
pub enum DialInfoClass {
|
||||
Direct,
|
||||
Relay,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Serialize, Deserialize)]
|
||||
#[serde(tag = "kind")]
|
||||
// The derived ordering here is the order of preference, lower is preferred for connections
|
||||
|
@ -168,3 +168,20 @@ pub fn listen_address_to_socket_addrs(listen_address: &str) -> Result<Vec<Socket
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub trait Dedup<T: PartialEq + Clone> {
|
||||
fn remove_duplicates(&mut self);
|
||||
}
|
||||
|
||||
impl<T: PartialEq + Clone> Dedup<T> for Vec<T> {
|
||||
fn remove_duplicates(&mut self) {
|
||||
let mut already_seen = Vec::new();
|
||||
self.retain(|item| match already_seen.contains(item) {
|
||||
true => false,
|
||||
_ => {
|
||||
already_seen.push(item.clone());
|
||||
true
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user