You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Our Go code that creates the tailnet connection and services the VPN packets needs to communicate with OS-native code that can do things like
Log to the native system log
Update the status tray with peer status
Configure the network settings (IP and DNS)
Since Go and native code will have different memory layouts and synchronization primitives, we will use a bidirectional stream to communicate between Go and the native code, even if Go and native code coexist in the same process.
We define a new internal protocol to facilitate this communication. This protocol uses Protobufs for message serialization, but does not use dRPC like our other Protobuf-based APIs since dRPC is not available for the languages we’d like to use for native OS development (e.g. C#, Swift).
The protocol runs over a single bidirectional stream between the native code (called the Manager) and the Go code (called the Tunnel). We keep everything simple and send messages serially on the stream (rather than rely on a multiplexing library like yamux, which is only implemented in Go). This means that RPC responses might get behind other, unrelated messages, but we expect this control protocol to be very light in traffic, and response times are generally not crucial.
On opening the stream, each side sends a single plaintext header of the form codervpn <major.minor> <role>\n where <major.minor> is the protocol version and <role> is “manager” or “tunnel”. After reading and verifying the other side’s header, they each proceed to send a series of messages encoded as:
4-byte unsigned integer, big-endian: length
<length> bytes of Protobuf encoded message, either TunnelMessage or ManagerMessage according to their role, as follows:
// RPC allows a very simple unary request/response RPC mechanism. The requester// generates a unique msg_id which it sets on the request, the responder sets// response_to that msg_id on the response messagemessageRPC {
uint64msg_id=1;
uint64response_to=2;
}
// ManagerMessage is a message from the manager (to the tunnel).messageManagerMessage {
RPCrpc=1;
oneofmsg {
GetPeerUpdateget_peer_update=2;
NetworkSettingsResponsenetwork_settings=3;
StartRequeststart=4;
StopRequeststop=5;
}
}
// TunnelMessage is a message from the tunnel (to the manager).messageTunnelMessage {
RPCrpc=1;
oneofmsg {
Loglog=2;
PeerUpdatepeer_update=3;
NetworkSettingsRequestnetwork_settings=4;
StartResponsestart=5;
StopResponsestop=6;
}
}
// Log is a log message generated by the tunnel. The manager should log it to// the system log. It is one-way tunnel -> manager with no response.messageLog {
enumLevel {
UNSPECIFIED=0;
INFO=1;
WARN=2;
ERROR=3;
CRITICAL=4;
FATAL=5;
}
Levellevel=1;
stringmessage=2;
repeatedstringlogger_names=3;
messageField {
stringname=1;
stringvalue=2;
}
repeatedFieldfields=4;
}
// GetPeerUpdate asks for a PeerUpdate with a full set of data.messageGetPeerUpdate {}
// PeerUpdate is an update about workspaces and agents connected via the tunnel.// It is generated in response to GetPeerUpdate (which dumps the full set). It is// also generated on any changes (not in response to any request).messagePeerUpdate {
repeatedWorkspaceupserted_workspaces=1;
repeatedAgentupserted_agents=2;
repeatedWorkspacedeleted_workspaces=3;
repeatedAgentdeleted_agents=4;
}
messageWorkspace {
bytesid=1; // UUIDstringname=2;
enumStatus {
UNKNOWN=0;
PENDING=1;
STARTING=2;
RUNNING=3;
STOPPING=4;
STOPPED=5;
FAILED=6;
CANCELING=7;
CANCELED=8;
DELETING=9;
DELETED=10;
}
Statusstatus=3;
}
messageAgent {
bytesid=1; // UUIDstringname=2;
bytesworkspace_id=3; // UUIDstringfqdn=4;
repeatedstringip_addrs=5;
// last_handshake is the primary indicator of whether we are connected to a// peer. Zero value or anything longer than 5 minutes ago means there is a// problem.google.protobuf.Timestamplast_handshake=6;
}
// NetworkSettingsRequest is based on// https://developer.apple.com/documentation/networkextension/nepackettunnelnetworksettings// for macOS. It is a request/response message with response// NetworkSettingsResponsemessageNetworkSettingsRequest {
uint32tunnel_overhead_bytes=1;
uint32mtu=2;
messageDNSSettings {
repeatedstringservers=1;
repeatedstringsearch_domains=2;
// domain_name is the primary domain name of the tunnelstringdomain_name=3;
repeatedstringmatch_domains=4;
// match_domains_no_search specifies if the domains in the matchDomains// list should not be appended to the resolver’s list of search domains.boolmatch_domains_no_search=5;
}
DNSSettingsdns_settings=3;
stringtunnel_remote_address=4;
messageIPv4Settings {
repeatedstringaddrs=1;
repeatedstringsubnet_masks=2;
// router is the next-hop router in dotted-decimal formatstringrouter=3;
messageIPv4Route {
stringdestination=1;
stringmask=2;
// router is the next-hop router in dotted-decimal formatstringrouter=3;
}
repeatedIPv4Routeincluded_routes=4;
repeatedIPv4Routeexcluded_routes=5;
}
IPv4Settingsipv4_settings=5;
messageIPv6Settings {
repeatedstringaddrs=1;
repeateduint32prefix_lengths=2;
messageIPv6Route {
stringdestination=1;
uint32prefix_length=2;
// router is the address of the next-hopstringrouter=3;
}
repeatedIPv6Routeincluded_routes=3;
repeatedIPv6Routeexcluded_routes=4;
}
IPv6Settingsipv6_settings=6;
}
// NetworkSettingsResponse is the response from the manager to the tunnel for a// NetworkSettingsRequestmessageNetworkSettingsResponse {
boolsuccess=1;
stringerror_message=2;
}
// StartRequest is a request from the manager to start the tunnel. The tunnel// replies with a StartResponse.messageStartRequest {
int32tunnel_file_descriptor=1;
stringcoder_url=2;
stringapi_token=3;
}
messageStartResponse {
boolsuccess=1;
stringerror_message=2;
}
// StopRequest is a request from the manager to stop the tunnel. The tunnel// replies with a StopResponse.messageStopRequest {}
// StopResponse is a response to stopping the tunnel. After sending this response,// the tunnel closes its side of the bidirectional stream for writing.messageStopResponse {
boolsuccess=1;
stringerror_message=2;
}
The text was updated successfully, but these errors were encountered:
Our Go code that creates the tailnet connection and services the VPN packets needs to communicate with OS-native code that can do things like
Since Go and native code will have different memory layouts and synchronization primitives, we will use a bidirectional stream to communicate between Go and the native code, even if Go and native code coexist in the same process.
We define a new internal protocol to facilitate this communication. This protocol uses Protobufs for message serialization, but does not use dRPC like our other Protobuf-based APIs since dRPC is not available for the languages we’d like to use for native OS development (e.g. C#, Swift).
The protocol runs over a single bidirectional stream between the native code (called the Manager) and the Go code (called the Tunnel). We keep everything simple and send messages serially on the stream (rather than rely on a multiplexing library like yamux, which is only implemented in Go). This means that RPC responses might get behind other, unrelated messages, but we expect this control protocol to be very light in traffic, and response times are generally not crucial.
On opening the stream, each side sends a single plaintext header of the form
codervpn <major.minor> <role>\n
where <major.minor> is the protocol version and <role> is “manager” or “tunnel”. After reading and verifying the other side’s header, they each proceed to send a series of messages encoded as:TunnelMessage
orManagerMessage
according to their role, as follows:The text was updated successfully, but these errors were encountered: