pub struct Node {Show 23 fields
pub id: Id,
pub stable_id: StableId,
pub hostname: String,
pub user_id: UserId,
pub tailnet: Option<String>,
pub tags: Vec<String>,
pub tailnet_address: TailnetAddress,
pub node_key: NodePublicKey,
pub node_key_expiry: 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: NodeCapMap,
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: IdThe node’s id.
stable_id: StableIdThe node’s stable id.
hostname: StringThis node’s hostname.
user_id: UserIdThe 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.
The tags assigned to this node.
tailnet_address: TailnetAddressThe address of the node in the tailnet.
node_key: NodePublicKeyThe node’s NodePublicKey.
node_key_expiry: Option<DateTime<Utc>>The node key’s expiration.
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: CapabilityVersionThis 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: NodeCapMapThis 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: boolWhether 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: boolWhether 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: boolWhether 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
impl Node
Sourcepub fn fqdn(&self, trailing_dot: bool) -> String
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.
Sourcepub fn key_expired(&self, now: DateTime<Utc>) -> bool
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.
Sourcepub fn key_expiry(&self) -> Option<DateTime<Utc>>
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.
Sourcepub fn is_peer_relay(&self) -> bool
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.
Sourcepub fn key_expiry_unix(&self) -> Option<i64>
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.
Sourcepub fn key_expired_at_unix(&self, now_unix_secs: i64) -> bool
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.
Sourcepub fn fqdn_opt(&self, trailing_dot: bool) -> Option<String>
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.
Sourcepub fn matches_name(&self, name: &str) -> bool
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.
Sourcepub fn is_subnet_route(&self, route: &IpNet) -> bool
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.
Sourcepub fn routes_to_install<'a>(
&'a self,
accept_routes: bool,
exit_node: Option<&StableId>,
) -> impl Iterator<Item = &'a IpNet> + 'a
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.
Sourcepub fn peerapi_doh_url(&self) -> Option<String>
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::cap ≥ PEER_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.
Sourcepub fn peerapi_doh_addr(&self) -> Option<SocketAddr>
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.
Sourcepub fn peerapi_addr(&self) -> Option<SocketAddr>
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.
Sourcepub fn has_node_attr(&self, cap: &str) -> bool
pub fn has_node_attr(&self, cap: &str) -> bool
Report whether the cap map contains cap as a key (Go NodeCapMap.Contains / HasCap).
Sourcepub fn can_funnel(&self) -> bool
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.
Sourcepub fn check_funnel_port(&self, wanted_port: u16) -> bool
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.
Sourcepub fn is_service_host(&self) -> bool
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.
Sourcepub fn service_addresses_for(&self, service: &str) -> &[IpAddr]
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).
Sourcepub fn service_addresses(&self) -> Vec<IpAddr>
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§
impl Eq for Node
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> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.