dialinfoclass refactor, cleaning up network class detection

This commit is contained in:
John Smith 2022-04-23 22:08:02 -04:00
parent 99dc4e16f9
commit 1156159748
12 changed files with 435 additions and 210 deletions

View File

@ -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
}

View File

@ -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(())
}
}

View File

@ -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())),
},
}

View File

@ -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))?;

View File

@ -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
}

View 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,
}
}

View 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 })
}

View File

@ -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::*;

View File

@ -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,
})
}

View File

@ -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();

View File

@ -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

View File

@ -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
}
})
}
}