Skip to main content

NodeInfo

Struct NodeInfo 

Source
pub struct NodeInfo {
Show 25 fields pub id: i64, pub stable_id: StableId, pub hostname: String, pub user_id: i64, pub tailnet: Option<String>, pub tags: Vec<String>, pub tailnet_address: TailnetAddress, pub node_key: NodePublicKey, pub node_key_expiry: Option<DateTime<Utc>>, pub online: Option<bool>, pub last_seen: Option<DateTime<Utc>>, pub key_signature: Vec<u8>, pub machine_key: Option<MachinePublicKey>, pub disco_key: Option<DiscoPublicKey>, pub accepted_routes: Vec<IpNet>, pub underlay_addresses: Vec<SocketAddr>, pub derp_region: Option<RegionId>, pub cap: CapabilityVersion, pub cap_map: BTreeMap<String, Vec<String>>, pub peerapi_port: Option<u16>, pub peerapi_dns_proxy: bool, pub is_wireguard_only: bool, pub exit_node_dns_resolvers: Vec<Resolver>, pub peer_relay: bool, pub service_vips: BTreeMap<String, Vec<IpAddr>>,
}
Expand description

A node in a tailnet.

Fields§

§id: i64

The node’s id.

§stable_id: StableId

The node’s stable id.

§hostname: String

This node’s hostname.

§user_id: i64

The integer id of the user that owns this node (Node.User in Go). 0 when control sends no owner (e.g. tagged/ACL nodes have no human owner). Join against the netmap’s UserProfiles table (accumulated by the runtime’s peer tracker) to resolve a login/display name — see the runtime WhoIs lookup.

§tailnet: Option<String>

The tailnet this node belongs to.

§tags: Vec<String>

The tags assigned to this node.

§tailnet_address: TailnetAddress

The address of the node in the tailnet.

§node_key: NodePublicKey

The node’s NodePublicKey.

§node_key_expiry: Option<DateTime<Utc>>

The node key’s expiration.

§online: Option<bool>

Whether control reports this node currently connected to the coordination server (tailcfg.Node.Online, a tri-state *bool). None = unknown / no permission to know / never been online — do not collapse to false (that would fabricate an offline status control never asserted). Updated by full nodes AND by the delta channels (a PeerChange::online, or the MapResponse.online_change map).

§last_seen: Option<DateTime<Utc>>

When control last saw this node online (tailcfg.Node.LastSeen). Per Go, only meaningful while online is not Some(true) (“not updated when Online is true”). None = unknown / never online.

§key_signature: Vec<u8>

Marshalled TKA node-key signature (tailcfg.Node.KeySignature); empty when control sends none. Verified against a TKA Authority at the peer-trust chokepoint WHEN tailnet-lock enforcement is active.

§machine_key: Option<MachinePublicKey>

The node’s MachinePublicKey, if known.

§disco_key: Option<DiscoPublicKey>

The node’s DiscoPublicKey, if known.

§accepted_routes: Vec<IpNet>

The routes this node accepts traffic for.

§underlay_addresses: Vec<SocketAddr>

The underlay addresses this node is reachable on (Endpoints in Go).

§derp_region: Option<RegionId>

The DERP region for this node, if known.

§cap: CapabilityVersion

This node’s advertised capability version (Node.Cap in Go). Old control servers may not send it, in which case it defaults to CapabilityVersion::default. Used to gate features that require a minimum peer capability, e.g. exit-node DNS proxying (peerCanProxyDNS).

§cap_map: BTreeMap<String, Vec<String>>

This node’s capability map (Node.CapMap in Go). Keys are capability names/URLs; values are the raw JSON argument blobs (often empty). Threaded from the wire (ts_control_serde::Node::cap_map) as an owned copy. Used to gate node-level features such as Funnel ingress (Node::can_funnel, Node::check_funnel_port).

§peerapi_port: Option<u16>

The peerAPI port this node advertises over IPv4 (peerapi4 service), if any.

Derived from HostInfo.Services. None means the peer advertises no IPv4 peerAPI, so it cannot be reached for peerAPI DoH (DNS-over-HTTPS) exit-node delegation.

§peerapi_dns_proxy: bool

Whether this peer advertises the peerapi-dns-proxy service (Go PeerAPIDNSProxy), indicating it will proxy DNS lookups for other nodes when used as an exit node.

§is_wireguard_only: bool

Whether this is a non-Tailscale WireGuard-only peer (IsWireGuardOnly in Go). Such peers cannot run a peerAPI DoH server, so exit-node DNS for them comes from Node::exit_node_dns_resolvers instead.

§exit_node_dns_resolvers: Vec<Resolver>

DNS resolvers to use when this WireGuard-only peer is selected as an exit node (ExitNodeDNSResolvers in Go). Only meaningful when Node::is_wireguard_only is set. Encrypted-transport resolvers are dropped (see Resolver::from_serde).

§peer_relay: bool

Whether this node advertises itself as a peer relay (Go Hostinfo.PeerRelay): it runs a UDP relay server other peers can allocate relay endpoints on. This fork is a relay client only and never sets this for itself; it is parsed off peers so a relay candidate can be recognized. Actually using a relay path (the Geneve data path + allocation handshake) is not yet implemented — see the crate docs.

§service_vips: BTreeMap<String, Vec<IpAddr>>

Per-service virtual IP addresses of the Tailscale VIP services this node hosts, keyed by svc:<label> service name. Parsed from the service-host (ts_control_serde::NODE_ATTR_SERVICE_HOST) node-capability value (tailcfg.ServiceIPMappings). These VIPs are control-assigned and also injected into the node’s AllowedIPs; the application netstack must accept packets for them so a Device::listen_service-bound listener can answer. Empty when the node hosts no VIP services (the common case). Per-service IP lists are deduplicated, source order otherwise preserved. Use Node::service_addresses for the flattened set (netstack accept list) and Node::service_addresses_for for a specific service’s VIPs.

Implementations§

Source§

impl Node

Source

pub fn fqdn(&self, trailing_dot: bool) -> String

The fully-qualified domain name of the node.

This is a string of the form $HOST.$TAILNET_DOMAIN.. For tailnets controlled by Tailscale’s control plane, this usually means $HOST.tail1234.ts.net.

The trailing_dot parameter specifies whether to include the trailing dot in the fqdn. This is included by the definition of FQDN, and is the way the Go codebase formats this field, but the parameter is included to allow turning it off for use in contexts that expect it to be absent.

Source

pub fn key_expired(&self, now: DateTime<Utc>) -> bool

Whether this node’s key has expired as of now, mirroring Go’s netmap.NetworkMap.SelfKeyExpiry + the !expiry.IsZero() && expiry.Before(now) check in ipnlocal. A node with no expiry (Node::node_key_expiry is None, the Go “zero value = does not expire”) is never expired.

Like Go, this fork is reactive: it reports expiry rather than auto-rotating in the background (Go transitions to NeedsLogin on expiry and re-registers via stored auth-key or interactive login). A caller observing true should re-register (crate::tokio::register) — supplying RegisterRequest::old_node_key (the prior key) and a fresh node_key when rotating the key, or the same key to merely refresh.

Source

pub fn key_expiry(&self) -> Option<DateTime<Utc>>

The instant this node’s key expires (Node.KeyExpiry in Go), or None if it never expires. A caller can schedule a re-evaluation/re-auth at this time.

Source

pub fn is_peer_relay(&self) -> bool

Whether this node advertises itself as a peer relay (Go Hostinfo.PeerRelay): it runs a UDP relay server other peers may allocate relay endpoints on. Recognizing a relay candidate; actually traversing a relay path is not yet implemented in this fork.

Source

pub fn key_expiry_unix(&self) -> Option<i64>

The key-expiry instant as Unix seconds, or None if the key never expires. Provided for callers (e.g. the root crate) that don’t depend on chrono.

Source

pub fn key_expired_at_unix(&self, now_unix_secs: i64) -> bool

Whether the key has expired as of now_unix_secs (Unix seconds). Equivalent to key_expired for chrono-free callers. A key with no expiry is never expired.

Source

pub fn fqdn_opt(&self, trailing_dot: bool) -> Option<String>

The fully-qualified domain name of the node, only returning Some if the tailnet component is present.

See Node::fqdn.

Source

pub fn matches_name(&self, name: &str) -> bool

Report whether this node matches the given name.

name is checked for equality with both this node’s bare hostname and its fqdn. A trailing . may be present. Matching is case-insensitive (DNS names are case-insensitive), so this agrees with the canonicalized MagicDNS-name index used for peer lookups.

Source

pub fn is_subnet_route(&self, route: &IpNet) -> bool

Report whether route is an advertised subnet route (as opposed to one of this node’s own tailnet addresses).

Mirrors cidrIsSubnet in the Go client (wgengine/wgcfg/nmcfg/nmcfg.go). A route is not a subnet route (i.e. it’s a self-address) when it is a single host IP that is either a Tailscale-assigned IP or exactly one of this node’s TailnetAddress addresses. Everything else — multi-IP CIDRs, and single IPs outside the Tailscale ranges — is a subnet route.

The default route (0.0.0.0/0 / ::/0) is treated as a subnet route here; exit-node handling is a separate concern.

Source

pub fn routes_to_install<'a>( &'a self, accept_routes: bool, exit_node: Option<&StableId>, ) -> impl Iterator<Item = &'a IpNet> + 'a

The routes that should be installed for this peer, given whether this node accepts advertised subnet routes (--accept-routes / RouteAll in the Go client) and which peer (if any) is the selected exit node (--exit-node / ExitNodeID in the Go client).

This node’s own addresses (the peer’s /32 and /128) are always installed so the peer itself stays reachable. Larger advertised subnet routes are only installed when accept_routes is set; otherwise they are dropped (fail-closed). The same filtered set governs both outbound routing to the peer and inbound source validation, exactly as WireGuard cryptokey routing couples them in the Go client.

The default route (0.0.0.0/0 / ::/0) is installed only for the peer whose StableId equals exit_node, mirroring nmcfg.go’s if allowedIP.Bits()==0 && peer.StableID()!=exitNode { skip }. Exit-node use is gated behind this separate, explicit preference (ExitNodeID, not RouteAll): conflating the two would let enabling subnet-route acceptance silently route every packet through any peer advertising a default route — unacceptable for a fail-closed privacy posture. When exit_node is None (the default) no peer ever receives a /0, so internet-bound traffic has no overlay route and is dropped by the userspace netstack (fail-closed, no leak). Longest-prefix-match means a peer selected as the exit node still loses more-specific destinations to other peers; only residual default-route traffic egresses through it.

Source

pub fn peerapi_doh_url(&self) -> Option<String>

The base URL of this peer’s IPv4 peerAPI DoH endpoint for exit-node DNS proxying, if it can proxy DNS. Returns e.g. http://100.64.0.5:8080/dns-query.

Mirrors Go peerAPIBase(...)+"/dns-query" gated by exitNodeCanProxyDNS: a peer can proxy DNS when it advertises an IPv4 peerAPI port and either advertises the explicit peerapi-dns-proxy service or is new enough (Node::capPEER_CAN_PROXY_DNS). A WireGuard-only peer never runs a peerAPI, so it returns None here (its exit-node DNS comes from Node::exit_node_dns_resolvers instead).

IPv4-only by deliberate design: the tailnet dataplane in this fork binds IPv4 only, so we never form a peerAPI URL on the peer’s IPv6 address.

Source

pub fn peerapi_doh_addr(&self) -> Option<SocketAddr>

The IPv4 socket address (<tailnet-ipv4>:<peerapi-port>) of this peer’s peerAPI DoH endpoint for exit-node DNS proxying, if it can proxy DNS. Same gate as Node::peerapi_doh_url; this is the form the DoH client dials (over the overlay netstack) when delegating recursive resolution to a selected exit node. SocketAddr’s Display is ip:port, so peerapi_doh_url formats to http://<ip>:<port>/dns-query over this.

Source

pub fn peerapi_addr(&self) -> Option<SocketAddr>

The IPv4 peerAPI socket address (<tailnet-ipv4>:<peerapi4-port>) of this node, if it advertises an IPv4 peerAPI. Unlike Node::peerapi_doh_addr, this is not gated on the DNS-proxy capability: it is the general base for any peerAPI request to this node (e.g. a Taildrop PUT /v0/put/<name> upload), mirroring Go’s peerAPIBase/peerAPIPorts.

IPv4-only by this fork’s deliberate design (the tailnet dataplane binds IPv4 only, so we never form a peerAPI URL on the peer’s IPv6 address). Returns None for a WireGuard-only peer (which runs no peerAPI) or a peer advertising no IPv4 peerAPI port.

Source

pub fn has_node_attr(&self, cap: &str) -> bool

Report whether the cap map contains cap as a key (Go NodeCapMap.Contains / HasCap).

Source

pub fn can_funnel(&self) -> bool

Report whether this node is permitted to host Tailscale Funnel ingress.

Mirrors Go ipn.NodeCanFunnel: the node must advertise BOTH CapabilityHTTPS ("https") AND NodeAttrFunnel ("funnel") in its cap map. Fail-closed: a missing cap denies.

Source

pub fn can_share_files(&self) -> bool

Report whether this node may send Taildrop files — i.e. the admin has enabled file sharing for the tailnet (Go self.CapMap().Contains(CapabilityFileSharing)). Applied to the self node as the node-level gate in FileTargets; fail-closed when the cap is absent.

Source

pub fn is_file_sharing_target(&self) -> bool

Report whether this peer is an explicit Taildrop send target via ACL caps (Go PeerHasCap(p, PeerCapabilityFileSharingTarget)) — the cross-owner path that lets a peer owned by a different user still be a valid target.

Source

pub fn check_funnel_port(&self, wanted_port: u16) -> bool

Report whether wanted_port is allowed for Funnel on this node.

Mirrors Go ipn.CheckFunnelPort: scan the cap-map keys for one prefixed by Node::CAP_FUNNEL_PORTS, URL-parse that key, read its ports query parameter, and match wanted_port against the comma-separated list of single ports and first-last ranges. The port list lives in the key, never the value. Fail-closed: no matching cap, an empty or unparseable ports query, or a key whose non-query part isn’t exactly the funnel-ports URL all deny.

Source

pub fn is_service_host(&self) -> bool

Report whether this node is permitted to host Tailscale VIP services.

Mirrors the Go grant model: possession of the service-host (ts_control_serde::NODE_ATTR_SERVICE_HOST) node-capability and at least one assigned VIP address. Go additionally requires the host to be tagged (ErrUntaggedServiceHost); that tag gate is enforced at Device::listen_service using Node::tags. Fail-closed: no cap or no assigned VIP denies.

Source

pub fn service_addresses_for(&self, service: &str) -> &[IpAddr]

The control-assigned VIP addresses for one named service (svc:<label>), or an empty slice if this node does not host that service. This is the exact per-service mapping (so a multi-service co-host binds the right VIP for each service).

Source

pub fn service_addresses(&self) -> Vec<IpAddr>

The flattened, deduplicated set of every VIP address this node hosts across all services. Used to widen the netstack’s accepted-address set so any hosted-service listener is reachable. Per-service binding uses Node::service_addresses_for instead.

Trait Implementations§

Source§

impl Clone for Node

Source§

fn clone(&self) -> Node

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Node

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Eq for Node

Source§

impl From<&Node<'_>> for Node

Source§

fn from(value: &Node<'_>) -> Node

Converts to this type from the input type.
Source§

impl Hash for Node

Source§

fn hash<__H>(&self, state: &mut __H)
where __H: Hasher,

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl PartialEq for Node

Source§

fn eq(&self, other: &Node) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl StructuralPartialEq for Node

Auto Trait Implementations§

§

impl Freeze for Node

§

impl RefUnwindSafe for Node

§

impl Send for Node

§

impl Sync for Node

§

impl Unpin for Node

§

impl UnsafeUnpin for Node

§

impl UnwindSafe for Node

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Converts Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Converts Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
Source§

fn as_any(&self) -> &(dyn Any + 'static)

Converts &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
Source§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Converts &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> DowncastSend for T
where T: Any + Send,

Source§

fn into_any_send(self: Box<T>) -> Box<dyn Any + Send>

Converts Box<Trait> (where Trait: DowncastSend) to Box<dyn Any + Send>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

Source§

fn into_any_sync(self: Box<T>) -> Box<dyn Any + Sync + Send>

Converts Box<Trait> (where Trait: DowncastSync) to Box<dyn Any + Send + Sync>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Sync + Send>

Converts Arc<Trait> (where Trait: DowncastSync) to Arc<Any>, which can then be downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<A, T> DynMessage<A> for T
where A: Actor + Message<T>, T: Send + 'static,

Source§

fn handle_dyn<'a>( self: Box<T>, state: &'a mut A, actor_ref: ActorRef<A>, tx: Option<Sender<Result<Box<dyn Any + Send>, SendError<Box<dyn Any + Send>, Box<dyn Any + Send>>>>>, stop: &'a mut bool, ) -> Pin<Box<dyn Future<Output = Result<(), Box<dyn ReplyError>>> + Send + 'a>>

Handles the dyn message with the provided actor state, ref, and reply sender.
Source§

fn as_any(self: Box<T>) -> Box<dyn Any>

Casts the type to a Box<dyn Any>.
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<T> ErasedDestructor for T
where T: 'static,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> FromRef<T> for T
where T: Clone,

Source§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
Source§

impl<A, B, T> HttpServerConnExec<A, B> for T
where B: Body,

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> ReplyError for T
where T: Debug + Send + 'static,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more