From 0076e8479f11f3a34aebfc44059fdbaee0854c18 Mon Sep 17 00:00:00 2001
From: Ethan <39577870+ethanndickson@users.noreply.github.com>
Date: Fri, 6 Jun 2025 14:18:57 +1000
Subject: [PATCH] chore(vpn): send ping results over tunnel (#18200)
Closes #17982.
The purpose of this PR is to expose network latency via the API used by Coder Desktop.
This PR has the tunnel ping all known agents every 5 seconds, in order to produce an instance of:
```proto
message LastPing {
// latency is the RTT of the ping to the agent.
google.protobuf.Duration latency = 1;
// did_p2p indicates whether the ping was sent P2P, or over DERP.
bool did_p2p = 2;
// preferred_derp is the human readable name of the preferred DERP region,
// or the region used for the last ping, if it was sent over DERP.
string preferred_derp = 3;
// preferred_derp_latency is the last known latency to the preferred DERP
// region. Unset if the region does not appear in the DERP map.
optional google.protobuf.Duration preferred_derp_latency = 4;
}
```
The contents of this message are stored and included on all subsequent upsertions of the agent.
Note that we upsert existing agents every 5 seconds to update the `last_handshake` value.
On the desktop apps, this message will be used to produce a tooltip similar to that of the VS Code extension:
(wording not final)
Unlike the VS Code extension, we omit:
- The Latency of *all* available DERP regions. It seems not ideal to send a copy of this entire map for every online agent, and it certainly doesn't make sense for it to be on the `Agent` or `LastPing` message.
If we do want to expose this info on Coder Desktop, we should consider how best to do so; maybe we want to include it on a more generic `Netcheck` message.
- The current throughput (Bytes up/down). This is out of scope of the linked issue, and is non-trivial to implement. I'm also not sure of the value given the frequency we're doing these status updates (every 5 seconds).
If we want to expose it, it'll be in a separate PR.
---
cli/ssh.go | 45 +--
coderd/database/db2sdk/db2sdk.go | 8 -
coderd/util/maps/maps.go | 8 +
tailnet/derpmap.go | 46 +++
tailnet/derpmap_test.go | 109 ++++++
vpn/client.go | 10 +
vpn/speaker_internal_test.go | 9 +-
vpn/tunnel.go | 139 ++++++-
vpn/tunnel_internal_test.go | 277 +++++++++++++-
vpn/version.go | 9 +-
vpn/vpn.pb.go | 615 +++++++++++++++++++------------
vpn/vpn.proto | 16 +
12 files changed, 973 insertions(+), 318 deletions(-)
diff --git a/cli/ssh.go b/cli/ssh.go
index 51f53e10bc..4adbf12ccc 100644
--- a/cli/ssh.go
+++ b/cli/ssh.go
@@ -16,7 +16,6 @@ import (
"path/filepath"
"regexp"
"slices"
- "strconv"
"strings"
"sync"
"time"
@@ -31,7 +30,6 @@ import (
"golang.org/x/term"
"golang.org/x/xerrors"
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
- "tailscale.com/tailcfg"
"tailscale.com/types/netlogtype"
"cdr.dev/slog"
@@ -40,11 +38,13 @@ import (
"github.com/coder/coder/v2/cli/cliui"
"github.com/coder/coder/v2/cli/cliutil"
"github.com/coder/coder/v2/coderd/autobuild/notify"
+ "github.com/coder/coder/v2/coderd/util/maps"
"github.com/coder/coder/v2/coderd/util/ptr"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/codersdk/workspacesdk"
"github.com/coder/coder/v2/cryptorand"
"github.com/coder/coder/v2/pty"
+ "github.com/coder/coder/v2/tailnet"
"github.com/coder/quartz"
"github.com/coder/retry"
"github.com/coder/serpent"
@@ -1456,28 +1456,6 @@ func collectNetworkStats(ctx context.Context, agentConn *workspacesdk.AgentConn,
}
node := agentConn.Node()
derpMap := agentConn.DERPMap()
- derpLatency := map[string]float64{}
-
- // Convert DERP region IDs to friendly names for display in the UI.
- for rawRegion, latency := range node.DERPLatency {
- regionParts := strings.SplitN(rawRegion, "-", 2)
- regionID, err := strconv.Atoi(regionParts[0])
- if err != nil {
- continue
- }
- region, found := derpMap.Regions[regionID]
- if !found {
- // It's possible that a workspace agent is using an old DERPMap
- // and reports regions that do not exist. If that's the case,
- // report the region as unknown!
- region = &tailcfg.DERPRegion{
- RegionID: regionID,
- RegionName: fmt.Sprintf("Unnamed %d", regionID),
- }
- }
- // Convert the microseconds to milliseconds.
- derpLatency[region.RegionName] = latency * 1000
- }
totalRx := uint64(0)
totalTx := uint64(0)
@@ -1491,27 +1469,20 @@ func collectNetworkStats(ctx context.Context, agentConn *workspacesdk.AgentConn,
uploadSecs := float64(totalTx) / dur.Seconds()
downloadSecs := float64(totalRx) / dur.Seconds()
- // Sometimes the preferred DERP doesn't match the one we're actually
- // connected with. Perhaps because the agent prefers a different DERP and
- // we're using that server instead.
- preferredDerpID := node.PreferredDERP
- if pingResult.DERPRegionID != 0 {
- preferredDerpID = pingResult.DERPRegionID
- }
- preferredDerp, ok := derpMap.Regions[preferredDerpID]
- preferredDerpName := fmt.Sprintf("Unnamed %d", preferredDerpID)
- if ok {
- preferredDerpName = preferredDerp.RegionName
- }
+ preferredDerpName := tailnet.ExtractPreferredDERPName(pingResult, node, derpMap)
+ derpLatency := tailnet.ExtractDERPLatency(node, derpMap)
if _, ok := derpLatency[preferredDerpName]; !ok {
derpLatency[preferredDerpName] = 0
}
+ derpLatencyMs := maps.Map(derpLatency, func(dur time.Duration) float64 {
+ return float64(dur) / float64(time.Millisecond)
+ })
return &sshNetworkStats{
P2P: p2p,
Latency: float64(latency.Microseconds()) / 1000,
PreferredDERP: preferredDerpName,
- DERPLatency: derpLatency,
+ DERPLatency: derpLatencyMs,
UploadBytesSec: int64(uploadSecs),
DownloadBytesSec: int64(downloadSecs),
}, nil
diff --git a/coderd/database/db2sdk/db2sdk.go b/coderd/database/db2sdk/db2sdk.go
index 40b1423a0f..9978aa0bca 100644
--- a/coderd/database/db2sdk/db2sdk.go
+++ b/coderd/database/db2sdk/db2sdk.go
@@ -47,14 +47,6 @@ func ListLazy[F any, T any](convert func(F) T) func(list []F) []T {
}
}
-func Map[K comparable, F any, T any](params map[K]F, convert func(F) T) map[K]T {
- into := make(map[K]T)
- for k, item := range params {
- into[k] = convert(item)
- }
- return into
-}
-
type ExternalAuthMeta struct {
Authenticated bool
ValidateError string
diff --git a/coderd/util/maps/maps.go b/coderd/util/maps/maps.go
index 8aaa6669cb..6a858bf3f7 100644
--- a/coderd/util/maps/maps.go
+++ b/coderd/util/maps/maps.go
@@ -6,6 +6,14 @@ import (
"golang.org/x/exp/constraints"
)
+func Map[K comparable, F any, T any](params map[K]F, convert func(F) T) map[K]T {
+ into := make(map[K]T)
+ for k, item := range params {
+ into[k] = convert(item)
+ }
+ return into
+}
+
// Subset returns true if all the keys of a are present
// in b and have the same values.
// If the corresponding value of a[k] is the zero value in
diff --git a/tailnet/derpmap.go b/tailnet/derpmap.go
index e2722c1ff9..6f284dad05 100644
--- a/tailnet/derpmap.go
+++ b/tailnet/derpmap.go
@@ -8,8 +8,11 @@ import (
"net/http"
"os"
"strconv"
+ "strings"
+ "time"
"golang.org/x/xerrors"
+ "tailscale.com/ipn/ipnstate"
"tailscale.com/tailcfg"
)
@@ -152,6 +155,49 @@ regionLoop:
return derpMap, nil
}
+func ExtractPreferredDERPName(pingResult *ipnstate.PingResult, node *Node, derpMap *tailcfg.DERPMap) string {
+ // Sometimes the preferred DERP doesn't match the one we're actually
+ // connected with. Perhaps because the agent prefers a different DERP and
+ // we're using that server instead.
+ preferredDerpID := node.PreferredDERP
+ if pingResult.DERPRegionID != 0 {
+ preferredDerpID = pingResult.DERPRegionID
+ }
+ preferredDerp, ok := derpMap.Regions[preferredDerpID]
+ preferredDerpName := fmt.Sprintf("Unnamed %d", preferredDerpID)
+ if ok {
+ preferredDerpName = preferredDerp.RegionName
+ }
+
+ return preferredDerpName
+}
+
+// ExtractDERPLatency extracts a map of derp region names to their latencies
+func ExtractDERPLatency(node *Node, derpMap *tailcfg.DERPMap) map[string]time.Duration {
+ latencyMs := make(map[string]time.Duration)
+
+ // Convert DERP region IDs to friendly names for display in the UI.
+ for rawRegion, latency := range node.DERPLatency {
+ regionParts := strings.SplitN(rawRegion, "-", 2)
+ regionID, err := strconv.Atoi(regionParts[0])
+ if err != nil {
+ continue
+ }
+ region, found := derpMap.Regions[regionID]
+ if !found {
+ // It's possible that a workspace agent is using an old DERPMap
+ // and reports regions that do not exist. If that's the case,
+ // report the region as unknown!
+ region = &tailcfg.DERPRegion{
+ RegionID: regionID,
+ RegionName: fmt.Sprintf("Unnamed %d", regionID),
+ }
+ }
+ latencyMs[region.RegionName] = time.Duration(latency * float64(time.Second))
+ }
+ return latencyMs
+}
+
// CompareDERPMaps returns true if the given DERPMaps are equivalent. Ordering
// of slices is ignored.
//
diff --git a/tailnet/derpmap_test.go b/tailnet/derpmap_test.go
index a91969bfec..c723437cad 100644
--- a/tailnet/derpmap_test.go
+++ b/tailnet/derpmap_test.go
@@ -10,6 +10,7 @@ import (
"testing"
"github.com/stretchr/testify/require"
+ "tailscale.com/ipn/ipnstate"
"tailscale.com/tailcfg"
"github.com/coder/coder/v2/tailnet"
@@ -162,3 +163,111 @@ func TestNewDERPMap(t *testing.T) {
require.ErrorContains(t, err, "DERP map has no DERP nodes")
})
}
+
+func TestExtractDERPLatency(t *testing.T) {
+ t.Parallel()
+
+ derpMap := &tailcfg.DERPMap{
+ Regions: map[int]*tailcfg.DERPRegion{
+ 1: {
+ RegionID: 1,
+ RegionName: "Region One",
+ Nodes: []*tailcfg.DERPNode{
+ {Name: "node1", RegionID: 1},
+ },
+ },
+ 2: {
+ RegionID: 2,
+ RegionName: "Region Two",
+ Nodes: []*tailcfg.DERPNode{
+ {Name: "node2", RegionID: 2},
+ },
+ },
+ },
+ }
+
+ t.Run("Basic", func(t *testing.T) {
+ t.Parallel()
+ node := &tailnet.Node{
+ DERPLatency: map[string]float64{
+ "1-node1": 0.05,
+ "2-node2": 0.1,
+ },
+ }
+ latencyMs := tailnet.ExtractDERPLatency(node, derpMap)
+ require.EqualValues(t, 50, latencyMs["Region One"].Milliseconds())
+ require.EqualValues(t, 100, latencyMs["Region Two"].Milliseconds())
+ require.Len(t, latencyMs, 2)
+ })
+
+ t.Run("UnknownRegion", func(t *testing.T) {
+ t.Parallel()
+ node := &tailnet.Node{
+ DERPLatency: map[string]float64{
+ "999-node999": 0.2,
+ },
+ }
+ latencyMs := tailnet.ExtractDERPLatency(node, derpMap)
+ require.EqualValues(t, 200, latencyMs["Unnamed 999"].Milliseconds())
+ require.Len(t, latencyMs, 1)
+ })
+
+ t.Run("InvalidRegionFormat", func(t *testing.T) {
+ t.Parallel()
+ node := &tailnet.Node{
+ DERPLatency: map[string]float64{
+ "invalid": 0.3,
+ "1-node1": 0.05,
+ "abc-node": 0.15,
+ },
+ }
+ latencyMs := tailnet.ExtractDERPLatency(node, derpMap)
+ require.EqualValues(t, 50, latencyMs["Region One"].Milliseconds())
+ require.Len(t, latencyMs, 1)
+ require.NotContains(t, latencyMs, "invalid")
+ require.NotContains(t, latencyMs, "abc-node")
+ })
+
+ t.Run("EmptyInput", func(t *testing.T) {
+ t.Parallel()
+ node := &tailnet.Node{
+ DERPLatency: map[string]float64{},
+ }
+ latencyMs := tailnet.ExtractDERPLatency(node, derpMap)
+ require.Empty(t, latencyMs)
+ })
+}
+
+func TestExtractPreferredDERPName(t *testing.T) {
+ t.Parallel()
+ derpMap := &tailcfg.DERPMap{
+ Regions: map[int]*tailcfg.DERPRegion{
+ 1: {RegionName: "New York"},
+ 2: {RegionName: "London"},
+ },
+ }
+
+ t.Run("UsesPingRegion", func(t *testing.T) {
+ t.Parallel()
+ pingResult := &ipnstate.PingResult{DERPRegionID: 2}
+ node := &tailnet.Node{PreferredDERP: 1}
+ result := tailnet.ExtractPreferredDERPName(pingResult, node, derpMap)
+ require.Equal(t, "London", result)
+ })
+
+ t.Run("UsesNodePreferred", func(t *testing.T) {
+ t.Parallel()
+ pingResult := &ipnstate.PingResult{DERPRegionID: 0}
+ node := &tailnet.Node{PreferredDERP: 1}
+ result := tailnet.ExtractPreferredDERPName(pingResult, node, derpMap)
+ require.Equal(t, "New York", result)
+ })
+
+ t.Run("UnknownRegion", func(t *testing.T) {
+ t.Parallel()
+ pingResult := &ipnstate.PingResult{DERPRegionID: 99}
+ node := &tailnet.Node{PreferredDERP: 1}
+ result := tailnet.ExtractPreferredDERPName(pingResult, node, derpMap)
+ require.Equal(t, "Unnamed 99", result)
+ })
+}
diff --git a/vpn/client.go b/vpn/client.go
index da066bbcd6..e3f3e767fc 100644
--- a/vpn/client.go
+++ b/vpn/client.go
@@ -5,11 +5,14 @@ import (
"net/http"
"net/netip"
"net/url"
+ "time"
"golang.org/x/xerrors"
+ "tailscale.com/ipn/ipnstate"
"tailscale.com/net/dns"
"tailscale.com/net/netmon"
+ "tailscale.com/tailcfg"
"tailscale.com/wgengine/router"
"github.com/google/uuid"
@@ -27,6 +30,9 @@ import (
type Conn interface {
CurrentWorkspaceState() (tailnet.WorkspaceUpdate, error)
GetPeerDiagnostics(peerID uuid.UUID) tailnet.PeerDiagnostics
+ Ping(ctx context.Context, agentID uuid.UUID) (time.Duration, bool, *ipnstate.PingResult, error)
+ Node() *tailnet.Node
+ DERPMap() *tailcfg.DERPMap
Close() error
}
@@ -38,6 +44,10 @@ type vpnConn struct {
updatesCtrl *tailnet.TunnelAllWorkspaceUpdatesController
}
+func (c *vpnConn) Ping(ctx context.Context, agentID uuid.UUID) (time.Duration, bool, *ipnstate.PingResult, error) {
+ return c.Conn.Ping(ctx, tailnet.TailscaleServicePrefix.AddrFromUUID(agentID))
+}
+
func (c *vpnConn) CurrentWorkspaceState() (tailnet.WorkspaceUpdate, error) {
return c.updatesCtrl.CurrentState()
}
diff --git a/vpn/speaker_internal_test.go b/vpn/speaker_internal_test.go
index 2f3d131093..433868851a 100644
--- a/vpn/speaker_internal_test.go
+++ b/vpn/speaker_internal_test.go
@@ -23,6 +23,8 @@ func TestMain(m *testing.M) {
goleak.VerifyTestMain(m, testutil.GoleakOptions...)
}
+const expectedHandshake = "codervpn tunnel 1.2\n"
+
// TestSpeaker_RawPeer tests the speaker with a peer that we simulate by directly making reads and
// writes to the other end of the pipe. There should be at least one test that does this, rather
// than use 2 speakers so that we don't have a bug where we don't adhere to the stated protocol, but
@@ -48,8 +50,6 @@ func TestSpeaker_RawPeer(t *testing.T) {
errCh <- err
}()
- expectedHandshake := "codervpn tunnel 1.1\n"
-
b := make([]byte, 256)
n, err := mp.Read(b)
require.NoError(t, err)
@@ -157,8 +157,6 @@ func TestSpeaker_OversizeHandshake(t *testing.T) {
errCh <- err
}()
- expectedHandshake := "codervpn tunnel 1.1\n"
-
b := make([]byte, 256)
n, err := mp.Read(b)
require.NoError(t, err)
@@ -210,7 +208,6 @@ func TestSpeaker_HandshakeInvalid(t *testing.T) {
_, err = mp.Write([]byte(tc.handshake))
require.NoError(t, err)
- expectedHandshake := "codervpn tunnel 1.1\n"
b := make([]byte, 256)
n, err := mp.Read(b)
require.NoError(t, err)
@@ -248,8 +245,6 @@ func TestSpeaker_CorruptMessage(t *testing.T) {
errCh <- err
}()
- expectedHandshake := "codervpn tunnel 1.1\n"
-
b := make([]byte, 256)
n, err := mp.Read(b)
require.NoError(t, err)
diff --git a/vpn/tunnel.go b/vpn/tunnel.go
index 6c71aecaa0..aa1d0e32ef 100644
--- a/vpn/tunnel.go
+++ b/vpn/tunnel.go
@@ -19,6 +19,7 @@ import (
"github.com/google/uuid"
"github.com/tailscale/wireguard-go/tun"
"golang.org/x/xerrors"
+ "google.golang.org/protobuf/types/known/durationpb"
"google.golang.org/protobuf/types/known/timestamppb"
"tailscale.com/net/dns"
"tailscale.com/net/netmon"
@@ -32,9 +33,9 @@ import (
"github.com/coder/coder/v2/tailnet"
)
-// netStatusInterval is the interval at which the tunnel sends network status updates to the manager.
-// This is currently only used to keep `last_handshake` up to date.
-const netStatusInterval = 10 * time.Second
+// netStatusInterval is the interval at which the tunnel records latencies,
+// and sends network status updates to the manager.
+const netStatusInterval = 5 * time.Second
type Tunnel struct {
speaker[*TunnelMessage, *ManagerMessage, ManagerMessage]
@@ -86,8 +87,9 @@ func NewTunnel(
ctx: uCtx,
cancel: uCancel,
netLoopDone: make(chan struct{}),
+ logger: logger,
uSendCh: s.sendCh,
- agents: map[uuid.UUID]tailnet.Agent{},
+ agents: map[uuid.UUID]agentWithPing{},
workspaces: map[uuid.UUID]tailnet.Workspace{},
clock: quartz.NewReal(),
},
@@ -344,10 +346,12 @@ type updater struct {
cancel context.CancelFunc
netLoopDone chan struct{}
+ logger slog.Logger
+
mu sync.Mutex
uSendCh chan<- *TunnelMessage
// agents contains the agents that are currently connected to the tunnel.
- agents map[uuid.UUID]tailnet.Agent
+ agents map[uuid.UUID]agentWithPing
// workspaces contains the workspaces to which agents are currently connected via the tunnel.
workspaces map[uuid.UUID]tailnet.Workspace
conn Conn
@@ -355,6 +359,26 @@ type updater struct {
clock quartz.Clock
}
+type agentWithPing struct {
+ tailnet.Agent
+ // non-nil if a successful ping has been made
+ lastPing *lastPing
+}
+
+func (a *agentWithPing) Clone() *agentWithPing {
+ return &agentWithPing{
+ Agent: a.Agent.Clone(),
+ lastPing: a.lastPing,
+ }
+}
+
+type lastPing struct {
+ pingDur time.Duration
+ didP2p bool
+ preferredDerp string
+ preferredDerpLatency *time.Duration
+}
+
// Update pushes a workspace update to the manager
func (u *updater) Update(update tailnet.WorkspaceUpdate) error {
u.mu.Lock()
@@ -412,10 +436,21 @@ func (u *updater) createPeerUpdateLocked(update tailnet.WorkspaceUpdate) *PeerUp
DeletedAgents: make([]*Agent, len(update.DeletedAgents)),
}
+ var upsertedAgentsWithPing []*agentWithPing
+
// save the workspace update to the tunnel's state, such that it can
// be used to populate automated peer updates.
for _, agent := range update.UpsertedAgents {
- u.agents[agent.ID] = agent.Clone()
+ var lastPing *lastPing
+ if existing, ok := u.agents[agent.ID]; ok {
+ lastPing = existing.lastPing
+ }
+ upsertedAgent := agentWithPing{
+ Agent: agent.Clone(),
+ lastPing: lastPing,
+ }
+ u.agents[agent.ID] = upsertedAgent
+ upsertedAgentsWithPing = append(upsertedAgentsWithPing, &upsertedAgent)
}
for _, agent := range update.DeletedAgents {
delete(u.agents, agent.ID)
@@ -435,7 +470,7 @@ func (u *updater) createPeerUpdateLocked(update tailnet.WorkspaceUpdate) *PeerUp
}
}
- upsertedAgents := u.convertAgentsLocked(update.UpsertedAgents)
+ upsertedAgents := u.convertAgentsLocked(upsertedAgentsWithPing)
out.UpsertedAgents = upsertedAgents
for i, ws := range update.DeletedWorkspaces {
out.DeletedWorkspaces[i] = &Workspace{
@@ -466,7 +501,7 @@ func (u *updater) createPeerUpdateLocked(update tailnet.WorkspaceUpdate) *PeerUp
// convertAgentsLocked takes a list of `tailnet.Agent` and converts them to proto agents.
// If there is an active connection, the last handshake time is populated.
-func (u *updater) convertAgentsLocked(agents []*tailnet.Agent) []*Agent {
+func (u *updater) convertAgentsLocked(agents []*agentWithPing) []*Agent {
out := make([]*Agent, 0, len(agents))
for _, agent := range agents {
@@ -477,12 +512,26 @@ func (u *updater) convertAgentsLocked(agents []*tailnet.Agent) []*Agent {
sort.Slice(fqdn, func(i, j int) bool {
return len(fqdn[i]) < len(fqdn[j])
})
+ var lastPing *LastPing
+ if agent.lastPing != nil {
+ var preferredDerpLatency *durationpb.Duration
+ if agent.lastPing.preferredDerpLatency != nil {
+ preferredDerpLatency = durationpb.New(*agent.lastPing.preferredDerpLatency)
+ }
+ lastPing = &LastPing{
+ Latency: durationpb.New(agent.lastPing.pingDur),
+ DidP2P: agent.lastPing.didP2p,
+ PreferredDerp: agent.lastPing.preferredDerp,
+ PreferredDerpLatency: preferredDerpLatency,
+ }
+ }
protoAgent := &Agent{
Id: tailnet.UUIDToByteSlice(agent.ID),
Name: agent.Name,
WorkspaceId: tailnet.UUIDToByteSlice(agent.WorkspaceID),
Fqdn: fqdn,
IpAddrs: hostsToIPStrings(agent.Hosts),
+ LastPing: lastPing,
}
if u.conn != nil {
diags := u.conn.GetPeerDiagnostics(agent.ID)
@@ -514,8 +563,8 @@ func (u *updater) stop() error {
return nil
}
err := u.conn.Close()
- u.conn = nil
u.cancel()
+ u.conn = nil
return err
}
@@ -525,7 +574,7 @@ func (u *updater) sendAgentUpdate() {
u.mu.Lock()
defer u.mu.Unlock()
- agents := make([]*tailnet.Agent, 0, len(u.agents))
+ agents := make([]*agentWithPing, 0, len(u.agents))
for _, agent := range u.agents {
agents = append(agents, &agent)
}
@@ -558,17 +607,85 @@ func (u *updater) netStatusLoop() {
case <-u.ctx.Done():
return
case <-ticker.C:
+ u.recordLatencies()
u.sendAgentUpdate()
}
}
}
+func (u *updater) recordLatencies() {
+ var agentsIDsToPing []uuid.UUID
+ u.mu.Lock()
+ for _, agent := range u.agents {
+ agentsIDsToPing = append(agentsIDsToPing, agent.ID)
+ }
+ conn := u.conn
+ u.mu.Unlock()
+
+ if conn == nil {
+ u.logger.Debug(u.ctx, "skipping pings as tunnel is not connected")
+ return
+ }
+
+ go func() {
+ // We need a waitgroup to cancel the context after all pings are done.
+ var wg sync.WaitGroup
+ pingCtx, cancelFunc := context.WithTimeout(u.ctx, netStatusInterval)
+ defer cancelFunc()
+ for _, agentID := range agentsIDsToPing {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+
+ pingDur, didP2p, pingResult, err := conn.Ping(pingCtx, agentID)
+ if err != nil {
+ u.logger.Warn(u.ctx, "failed to ping agent", slog.F("agent_id", agentID), slog.Error(err))
+ return
+ }
+
+ // We fetch the Node and DERPMap after each ping, as it may have
+ // changed.
+ node := conn.Node()
+ derpMap := conn.DERPMap()
+ if node == nil || derpMap == nil {
+ u.logger.Warn(u.ctx, "failed to get DERP map or node after ping")
+ return
+ }
+ derpLatencies := tailnet.ExtractDERPLatency(node, derpMap)
+ preferredDerp := tailnet.ExtractPreferredDERPName(pingResult, node, derpMap)
+ var preferredDerpLatency *time.Duration
+ if derpLatency, ok := derpLatencies[preferredDerp]; ok {
+ preferredDerpLatency = &derpLatency
+ } else {
+ u.logger.Debug(u.ctx, "preferred DERP not found in DERP latency map", slog.F("preferred_derp", preferredDerp))
+ }
+
+ // Write back results
+ u.mu.Lock()
+ defer u.mu.Unlock()
+ if agent, ok := u.agents[agentID]; ok {
+ agent.lastPing = &lastPing{
+ pingDur: pingDur,
+ didP2p: didP2p,
+ preferredDerp: preferredDerp,
+ preferredDerpLatency: preferredDerpLatency,
+ }
+ u.agents[agentID] = agent
+ } else {
+ u.logger.Debug(u.ctx, "ignoring ping result for unknown agent", slog.F("agent_id", agentID))
+ }
+ }()
+ }
+ wg.Wait()
+ }()
+}
+
// processSnapshotUpdate handles the logic when a full state update is received.
// When the tunnel is live, we only receive diffs, but the first packet on any given
// reconnect to the tailnet API is a full state.
// Without this logic we weren't processing deletes for any workspaces or agents deleted
// while the client was disconnected while the computer was asleep.
-func processSnapshotUpdate(update *tailnet.WorkspaceUpdate, agents map[uuid.UUID]tailnet.Agent, workspaces map[uuid.UUID]tailnet.Workspace) {
+func processSnapshotUpdate(update *tailnet.WorkspaceUpdate, agents map[uuid.UUID]agentWithPing, workspaces map[uuid.UUID]tailnet.Workspace) {
// ignoredWorkspaces is initially populated with the workspaces that are
// in the current update. Later on we populate it with the deleted workspaces too
// so that we don't send duplicate updates. Same applies to ignoredAgents.
diff --git a/vpn/tunnel_internal_test.go b/vpn/tunnel_internal_test.go
index 15eb9cf569..5c4e6ec03d 100644
--- a/vpn/tunnel_internal_test.go
+++ b/vpn/tunnel_internal_test.go
@@ -15,10 +15,13 @@ import (
"github.com/google/uuid"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/types/known/timestamppb"
+ "tailscale.com/ipn/ipnstate"
+ "tailscale.com/tailcfg"
"tailscale.com/util/dnsname"
"github.com/coder/quartz"
+ maputil "github.com/coder/coder/v2/coderd/util/maps"
"github.com/coder/coder/v2/tailnet"
"github.com/coder/coder/v2/tailnet/proto"
"github.com/coder/coder/v2/testutil"
@@ -57,15 +60,59 @@ func newFakeConn(state tailnet.WorkspaceUpdate, hsTime time.Time) *fakeConn {
}
}
+func (f *fakeConn) withManualPings() *fakeConn {
+ f.returnPing = make(chan struct{})
+ return f
+}
+
type fakeConn struct {
- state tailnet.WorkspaceUpdate
- hsTime time.Time
- closed chan struct{}
- doClose sync.Once
+ state tailnet.WorkspaceUpdate
+ returnPing chan struct{}
+ hsTime time.Time
+ closed chan struct{}
+ doClose sync.Once
+}
+
+func (*fakeConn) DERPMap() *tailcfg.DERPMap {
+ return &tailcfg.DERPMap{
+ Regions: map[int]*tailcfg.DERPRegion{
+ 999: {
+ RegionID: 999,
+ RegionCode: "zzz",
+ RegionName: "Coder Region",
+ },
+ },
+ }
+}
+
+func (*fakeConn) Node() *tailnet.Node {
+ return &tailnet.Node{
+ PreferredDERP: 999,
+ DERPLatency: map[string]float64{
+ "999": 0.1,
+ },
+ }
}
var _ Conn = (*fakeConn)(nil)
+func (f *fakeConn) Ping(ctx context.Context, agentID uuid.UUID) (time.Duration, bool, *ipnstate.PingResult, error) {
+ if f.returnPing == nil {
+ return time.Millisecond * 100, true, &ipnstate.PingResult{
+ DERPRegionID: 999,
+ }, nil
+ }
+
+ select {
+ case <-ctx.Done():
+ return 0, false, nil, ctx.Err()
+ case <-f.returnPing:
+ return time.Millisecond * 100, true, &ipnstate.PingResult{
+ DERPRegionID: 999,
+ }, nil
+ }
+}
+
func (f *fakeConn) CurrentWorkspaceState() (tailnet.WorkspaceUpdate, error) {
return f.state, nil
}
@@ -292,7 +339,7 @@ func TestUpdater_createPeerUpdate(t *testing.T) {
updater := updater{
ctx: ctx,
netLoopDone: make(chan struct{}),
- agents: map[uuid.UUID]tailnet.Agent{},
+ agents: map[uuid.UUID]agentWithPing{},
workspaces: map[uuid.UUID]tailnet.Workspace{},
conn: newFakeConn(tailnet.WorkspaceUpdate{}, hsTime),
}
@@ -430,6 +477,22 @@ func TestTunnel_sendAgentUpdate(t *testing.T) {
require.Equal(t, hsTime, req.msg.GetPeerUpdate().UpsertedAgents[0].LastHandshake.AsTime())
}
+ // Latency is gathered in the background, so it'll eventually be sent
+ testutil.Eventually(ctx, t, func(ctx context.Context) bool {
+ mClock.AdvanceNext()
+ req = testutil.TryReceive(ctx, t, mgr.requests)
+ if len(req.msg.GetPeerUpdate().UpsertedAgents) == 0 {
+ return false
+ }
+ if req.msg.GetPeerUpdate().UpsertedAgents[0].LastPing == nil {
+ return false
+ }
+ if req.msg.GetPeerUpdate().UpsertedAgents[0].LastPing.Latency.AsDuration().Milliseconds() != 100 {
+ return false
+ }
+ return req.msg.GetPeerUpdate().UpsertedAgents[0].LastPing.PreferredDerp == "Coder Region"
+ }, testutil.IntervalFast)
+
// Upsert a new agent
err = tun.Update(tailnet.WorkspaceUpdate{
UpsertedWorkspaces: []*tailnet.Workspace{},
@@ -459,6 +522,10 @@ func TestTunnel_sendAgentUpdate(t *testing.T) {
require.Equal(t, aID1[:], req.msg.GetPeerUpdate().UpsertedAgents[0].Id)
require.Equal(t, hsTime, req.msg.GetPeerUpdate().UpsertedAgents[0].LastHandshake.AsTime())
+ // The latency of the first agent is still set
+ require.NotNil(t, req.msg.GetPeerUpdate().UpsertedAgents[0].LastPing)
+ require.EqualValues(t, 100, req.msg.GetPeerUpdate().UpsertedAgents[0].LastPing.Latency.AsDuration().Milliseconds())
+
require.Equal(t, aID2[:], req.msg.GetPeerUpdate().UpsertedAgents[1].Id)
require.Equal(t, hsTime, req.msg.GetPeerUpdate().UpsertedAgents[1].LastHandshake.AsTime())
@@ -486,6 +553,22 @@ func TestTunnel_sendAgentUpdate(t *testing.T) {
require.Len(t, req.msg.GetPeerUpdate().UpsertedAgents, 1)
require.Equal(t, aID2[:], req.msg.GetPeerUpdate().UpsertedAgents[0].Id)
require.Equal(t, hsTime, req.msg.GetPeerUpdate().UpsertedAgents[0].LastHandshake.AsTime())
+
+ // Eventually the second agent's latency is set
+ testutil.Eventually(ctx, t, func(ctx context.Context) bool {
+ mClock.AdvanceNext()
+ req = testutil.TryReceive(ctx, t, mgr.requests)
+ if len(req.msg.GetPeerUpdate().UpsertedAgents) == 0 {
+ return false
+ }
+ if req.msg.GetPeerUpdate().UpsertedAgents[0].LastPing == nil {
+ return false
+ }
+ if req.msg.GetPeerUpdate().UpsertedAgents[0].LastPing.Latency.AsDuration().Milliseconds() != 100 {
+ return false
+ }
+ return req.msg.GetPeerUpdate().UpsertedAgents[0].LastPing.PreferredDerp == "Coder Region"
+ }, testutil.IntervalFast)
}
func TestTunnel_sendAgentUpdateReconnect(t *testing.T) {
@@ -693,6 +776,178 @@ func TestTunnel_sendAgentUpdateWorkspaceReconnect(t *testing.T) {
require.Equal(t, wID1[:], peerUpdate.DeletedWorkspaces[0].Id)
}
+func TestTunnel_slowPing(t *testing.T) {
+ t.Parallel()
+
+ ctx := testutil.Context(t, testutil.WaitShort)
+
+ mClock := quartz.NewMock(t)
+
+ wID1 := uuid.UUID{1}
+ aID1 := uuid.UUID{2}
+ hsTime := time.Now().Add(-time.Minute).UTC()
+
+ client := newFakeClient(ctx, t)
+ conn := newFakeConn(tailnet.WorkspaceUpdate{}, hsTime).withManualPings()
+
+ tun, mgr := setupTunnel(t, ctx, client, mClock)
+ errCh := make(chan error, 1)
+ var resp *TunnelMessage
+ go func() {
+ r, err := mgr.unaryRPC(ctx, &ManagerMessage{
+ Msg: &ManagerMessage_Start{
+ Start: &StartRequest{
+ TunnelFileDescriptor: 2,
+ CoderUrl: "https://coder.example.com",
+ ApiToken: "fakeToken",
+ },
+ },
+ })
+ resp = r
+ errCh <- err
+ }()
+ testutil.RequireSend(ctx, t, client.ch, conn)
+ err := testutil.TryReceive(ctx, t, errCh)
+ require.NoError(t, err)
+ _, ok := resp.Msg.(*TunnelMessage_Start)
+ require.True(t, ok)
+
+ // Inform the tunnel of the initial state
+ err = tun.Update(tailnet.WorkspaceUpdate{
+ UpsertedWorkspaces: []*tailnet.Workspace{
+ {
+ ID: wID1, Name: "w1", Status: proto.Workspace_STARTING,
+ },
+ },
+ UpsertedAgents: []*tailnet.Agent{
+ {
+ ID: aID1,
+ Name: "agent1",
+ WorkspaceID: wID1,
+ Hosts: map[dnsname.FQDN][]netip.Addr{
+ "agent1.coder.": {netip.MustParseAddr("fd60:627a:a42b:0101::")},
+ },
+ },
+ },
+ })
+ require.NoError(t, err)
+ req := testutil.TryReceive(ctx, t, mgr.requests)
+ require.Nil(t, req.msg.Rpc)
+ require.NotNil(t, req.msg.GetPeerUpdate())
+ require.Len(t, req.msg.GetPeerUpdate().UpsertedAgents, 1)
+ require.Equal(t, aID1[:], req.msg.GetPeerUpdate().UpsertedAgents[0].Id)
+
+ // We can't check that it *never* pings, so the best we can do is
+ // check it doesn't ping even with 5 goroutines attempting to,
+ // and that updates are received as normal
+ for range 5 {
+ mClock.AdvanceNext()
+ require.Nil(t, req.msg.GetPeerUpdate().UpsertedAgents[0].LastPing)
+ }
+
+ // Provided that it hasn't been 5 seconds since the last AdvanceNext call,
+ // there'll be a ping in-flight that will return with this message
+ testutil.RequireSend(ctx, t, conn.returnPing, struct{}{})
+ // Which will mean we'll eventually receive a PeerUpdate with the ping
+ testutil.Eventually(ctx, t, func(ctx context.Context) bool {
+ mClock.AdvanceNext()
+ req = testutil.TryReceive(ctx, t, mgr.requests)
+ if len(req.msg.GetPeerUpdate().UpsertedAgents) == 0 {
+ return false
+ }
+ if req.msg.GetPeerUpdate().UpsertedAgents[0].LastPing == nil {
+ return false
+ }
+ if req.msg.GetPeerUpdate().UpsertedAgents[0].LastPing.Latency.AsDuration().Milliseconds() != 100 {
+ return false
+ }
+ return req.msg.GetPeerUpdate().UpsertedAgents[0].LastPing.PreferredDerp == "Coder Region"
+ }, testutil.IntervalFast)
+}
+
+func TestTunnel_stopMidPing(t *testing.T) {
+ t.Parallel()
+
+ ctx := testutil.Context(t, testutil.WaitShort)
+
+ mClock := quartz.NewMock(t)
+
+ wID1 := uuid.UUID{1}
+ aID1 := uuid.UUID{2}
+ hsTime := time.Now().Add(-time.Minute).UTC()
+
+ client := newFakeClient(ctx, t)
+ conn := newFakeConn(tailnet.WorkspaceUpdate{}, hsTime).withManualPings()
+
+ tun, mgr := setupTunnel(t, ctx, client, mClock)
+ errCh := make(chan error, 1)
+ var resp *TunnelMessage
+ go func() {
+ r, err := mgr.unaryRPC(ctx, &ManagerMessage{
+ Msg: &ManagerMessage_Start{
+ Start: &StartRequest{
+ TunnelFileDescriptor: 2,
+ CoderUrl: "https://coder.example.com",
+ ApiToken: "fakeToken",
+ },
+ },
+ })
+ resp = r
+ errCh <- err
+ }()
+ testutil.RequireSend(ctx, t, client.ch, conn)
+ err := testutil.TryReceive(ctx, t, errCh)
+ require.NoError(t, err)
+ _, ok := resp.Msg.(*TunnelMessage_Start)
+ require.True(t, ok)
+
+ // Inform the tunnel of the initial state
+ err = tun.Update(tailnet.WorkspaceUpdate{
+ UpsertedWorkspaces: []*tailnet.Workspace{
+ {
+ ID: wID1, Name: "w1", Status: proto.Workspace_STARTING,
+ },
+ },
+ UpsertedAgents: []*tailnet.Agent{
+ {
+ ID: aID1,
+ Name: "agent1",
+ WorkspaceID: wID1,
+ Hosts: map[dnsname.FQDN][]netip.Addr{
+ "agent1.coder.": {netip.MustParseAddr("fd60:627a:a42b:0101::")},
+ },
+ },
+ },
+ })
+ require.NoError(t, err)
+ req := testutil.TryReceive(ctx, t, mgr.requests)
+ require.Nil(t, req.msg.Rpc)
+ require.NotNil(t, req.msg.GetPeerUpdate())
+ require.Len(t, req.msg.GetPeerUpdate().UpsertedAgents, 1)
+ require.Equal(t, aID1[:], req.msg.GetPeerUpdate().UpsertedAgents[0].Id)
+
+ // We'll have some pings in flight when we stop
+ for range 5 {
+ mClock.AdvanceNext()
+ req = testutil.TryReceive(ctx, t, mgr.requests)
+ require.Nil(t, req.msg.GetPeerUpdate().UpsertedAgents[0].LastPing)
+ }
+
+ // Stop the tunnel
+ go func() {
+ r, err := mgr.unaryRPC(ctx, &ManagerMessage{
+ Msg: &ManagerMessage_Stop{},
+ })
+ resp = r
+ errCh <- err
+ }()
+ testutil.TryReceive(ctx, t, conn.closed)
+ err = testutil.TryReceive(ctx, t, errCh)
+ require.NoError(t, err)
+ _, ok = resp.Msg.(*TunnelMessage_Stop)
+ require.True(t, ok)
+}
+
//nolint:revive // t takes precedence
func setupTunnel(t *testing.T, ctx context.Context, client *fakeClient, mClock *quartz.Mock) (*Tunnel, *speaker[*ManagerMessage, *TunnelMessage, TunnelMessage]) {
mp, tp := net.Pipe()
@@ -902,11 +1157,13 @@ func TestProcessFreshState(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
- agentsCopy := make(map[uuid.UUID]tailnet.Agent)
- maps.Copy(agentsCopy, tt.initialAgents)
-
- workspaceCopy := make(map[uuid.UUID]tailnet.Workspace)
- maps.Copy(workspaceCopy, tt.initialWorkspaces)
+ agentsCopy := maputil.Map(tt.initialAgents, func(a tailnet.Agent) agentWithPing {
+ return agentWithPing{
+ Agent: a.Clone(),
+ lastPing: nil,
+ }
+ })
+ workspaceCopy := maps.Clone(tt.initialWorkspaces)
processSnapshotUpdate(tt.update, agentsCopy, workspaceCopy)
diff --git a/vpn/version.go b/vpn/version.go
index 91aac9175f..2bf815e903 100644
--- a/vpn/version.go
+++ b/vpn/version.go
@@ -16,7 +16,14 @@ var CurrentSupportedVersions = RPCVersionList{
// - device_id: Coder Desktop device ID
// - device_os: Coder Desktop OS information
// - coder_desktop_version: Coder Desktop version
- {Major: 1, Minor: 1},
+ // 1.2 adds network related information to Agent:
+ // - last_ping:
+ // - latency: RTT of the most recently sent ping
+ // - did_p2p: Whether the last ping was sent over P2P
+ // - preferred_derp: The server that DERP relayed connections are
+ // using, if they're not using P2P.
+ // - preferred_derp_latency: The latency to the preferred DERP
+ {Major: 1, Minor: 2},
},
}
diff --git a/vpn/vpn.pb.go b/vpn/vpn.pb.go
index c89d3e51e6..bc5829d763 100644
--- a/vpn/vpn.pb.go
+++ b/vpn/vpn.pb.go
@@ -9,6 +9,7 @@ package vpn
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ durationpb "google.golang.org/protobuf/types/known/durationpb"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
reflect "reflect"
sync "sync"
@@ -205,7 +206,7 @@ func (x Status_Lifecycle) Number() protoreflect.EnumNumber {
// Deprecated: Use Status_Lifecycle.Descriptor instead.
func (Status_Lifecycle) EnumDescriptor() ([]byte, []int) {
- return file_vpn_vpn_proto_rawDescGZIP(), []int{17, 0}
+ return file_vpn_vpn_proto_rawDescGZIP(), []int{18, 0}
}
// RPC allows a very simple unary request/response RPC mechanism. The requester generates a unique
@@ -986,6 +987,8 @@ type Agent struct {
// 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.
LastHandshake *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=last_handshake,json=lastHandshake,proto3" json:"last_handshake,omitempty"`
+ // If unset, a successful ping has not yet been made.
+ LastPing *LastPing `protobuf:"bytes,7,opt,name=last_ping,json=lastPing,proto3,oneof" json:"last_ping,omitempty"`
}
func (x *Agent) Reset() {
@@ -1062,6 +1065,90 @@ func (x *Agent) GetLastHandshake() *timestamppb.Timestamp {
return nil
}
+func (x *Agent) GetLastPing() *LastPing {
+ if x != nil {
+ return x.LastPing
+ }
+ return nil
+}
+
+type LastPing struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // latency is the RTT of the ping to the agent.
+ Latency *durationpb.Duration `protobuf:"bytes,1,opt,name=latency,proto3" json:"latency,omitempty"`
+ // did_p2p indicates whether the ping was sent P2P, or over DERP.
+ DidP2P bool `protobuf:"varint,2,opt,name=did_p2p,json=didP2p,proto3" json:"did_p2p,omitempty"`
+ // preferred_derp is the human readable name of the preferred DERP region,
+ // or the region used for the last ping, if it was sent over DERP.
+ PreferredDerp string `protobuf:"bytes,3,opt,name=preferred_derp,json=preferredDerp,proto3" json:"preferred_derp,omitempty"`
+ // preferred_derp_latency is the last known latency to the preferred DERP
+ // region. Unset if the region does not appear in the DERP map.
+ PreferredDerpLatency *durationpb.Duration `protobuf:"bytes,4,opt,name=preferred_derp_latency,json=preferredDerpLatency,proto3,oneof" json:"preferred_derp_latency,omitempty"`
+}
+
+func (x *LastPing) Reset() {
+ *x = LastPing{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_vpn_vpn_proto_msgTypes[10]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *LastPing) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*LastPing) ProtoMessage() {}
+
+func (x *LastPing) ProtoReflect() protoreflect.Message {
+ mi := &file_vpn_vpn_proto_msgTypes[10]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use LastPing.ProtoReflect.Descriptor instead.
+func (*LastPing) Descriptor() ([]byte, []int) {
+ return file_vpn_vpn_proto_rawDescGZIP(), []int{10}
+}
+
+func (x *LastPing) GetLatency() *durationpb.Duration {
+ if x != nil {
+ return x.Latency
+ }
+ return nil
+}
+
+func (x *LastPing) GetDidP2P() bool {
+ if x != nil {
+ return x.DidP2P
+ }
+ return false
+}
+
+func (x *LastPing) GetPreferredDerp() string {
+ if x != nil {
+ return x.PreferredDerp
+ }
+ return ""
+}
+
+func (x *LastPing) GetPreferredDerpLatency() *durationpb.Duration {
+ if x != nil {
+ return x.PreferredDerpLatency
+ }
+ return nil
+}
+
// NetworkSettingsRequest is based on
// https://developer.apple.com/documentation/networkextension/nepackettunnelnetworksettings for
// macOS. It is a request/response message with response NetworkSettingsResponse
@@ -1081,7 +1168,7 @@ type NetworkSettingsRequest struct {
func (x *NetworkSettingsRequest) Reset() {
*x = NetworkSettingsRequest{}
if protoimpl.UnsafeEnabled {
- mi := &file_vpn_vpn_proto_msgTypes[10]
+ mi := &file_vpn_vpn_proto_msgTypes[11]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1094,7 +1181,7 @@ func (x *NetworkSettingsRequest) String() string {
func (*NetworkSettingsRequest) ProtoMessage() {}
func (x *NetworkSettingsRequest) ProtoReflect() protoreflect.Message {
- mi := &file_vpn_vpn_proto_msgTypes[10]
+ mi := &file_vpn_vpn_proto_msgTypes[11]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1107,7 +1194,7 @@ func (x *NetworkSettingsRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use NetworkSettingsRequest.ProtoReflect.Descriptor instead.
func (*NetworkSettingsRequest) Descriptor() ([]byte, []int) {
- return file_vpn_vpn_proto_rawDescGZIP(), []int{10}
+ return file_vpn_vpn_proto_rawDescGZIP(), []int{11}
}
func (x *NetworkSettingsRequest) GetTunnelOverheadBytes() uint32 {
@@ -1166,7 +1253,7 @@ type NetworkSettingsResponse struct {
func (x *NetworkSettingsResponse) Reset() {
*x = NetworkSettingsResponse{}
if protoimpl.UnsafeEnabled {
- mi := &file_vpn_vpn_proto_msgTypes[11]
+ mi := &file_vpn_vpn_proto_msgTypes[12]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1179,7 +1266,7 @@ func (x *NetworkSettingsResponse) String() string {
func (*NetworkSettingsResponse) ProtoMessage() {}
func (x *NetworkSettingsResponse) ProtoReflect() protoreflect.Message {
- mi := &file_vpn_vpn_proto_msgTypes[11]
+ mi := &file_vpn_vpn_proto_msgTypes[12]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1192,7 +1279,7 @@ func (x *NetworkSettingsResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use NetworkSettingsResponse.ProtoReflect.Descriptor instead.
func (*NetworkSettingsResponse) Descriptor() ([]byte, []int) {
- return file_vpn_vpn_proto_rawDescGZIP(), []int{11}
+ return file_vpn_vpn_proto_rawDescGZIP(), []int{12}
}
func (x *NetworkSettingsResponse) GetSuccess() bool {
@@ -1231,7 +1318,7 @@ type StartRequest struct {
func (x *StartRequest) Reset() {
*x = StartRequest{}
if protoimpl.UnsafeEnabled {
- mi := &file_vpn_vpn_proto_msgTypes[12]
+ mi := &file_vpn_vpn_proto_msgTypes[13]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1244,7 +1331,7 @@ func (x *StartRequest) String() string {
func (*StartRequest) ProtoMessage() {}
func (x *StartRequest) ProtoReflect() protoreflect.Message {
- mi := &file_vpn_vpn_proto_msgTypes[12]
+ mi := &file_vpn_vpn_proto_msgTypes[13]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1257,7 +1344,7 @@ func (x *StartRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use StartRequest.ProtoReflect.Descriptor instead.
func (*StartRequest) Descriptor() ([]byte, []int) {
- return file_vpn_vpn_proto_rawDescGZIP(), []int{12}
+ return file_vpn_vpn_proto_rawDescGZIP(), []int{13}
}
func (x *StartRequest) GetTunnelFileDescriptor() int32 {
@@ -1321,7 +1408,7 @@ type StartResponse struct {
func (x *StartResponse) Reset() {
*x = StartResponse{}
if protoimpl.UnsafeEnabled {
- mi := &file_vpn_vpn_proto_msgTypes[13]
+ mi := &file_vpn_vpn_proto_msgTypes[14]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1334,7 +1421,7 @@ func (x *StartResponse) String() string {
func (*StartResponse) ProtoMessage() {}
func (x *StartResponse) ProtoReflect() protoreflect.Message {
- mi := &file_vpn_vpn_proto_msgTypes[13]
+ mi := &file_vpn_vpn_proto_msgTypes[14]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1347,7 +1434,7 @@ func (x *StartResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use StartResponse.ProtoReflect.Descriptor instead.
func (*StartResponse) Descriptor() ([]byte, []int) {
- return file_vpn_vpn_proto_rawDescGZIP(), []int{13}
+ return file_vpn_vpn_proto_rawDescGZIP(), []int{14}
}
func (x *StartResponse) GetSuccess() bool {
@@ -1375,7 +1462,7 @@ type StopRequest struct {
func (x *StopRequest) Reset() {
*x = StopRequest{}
if protoimpl.UnsafeEnabled {
- mi := &file_vpn_vpn_proto_msgTypes[14]
+ mi := &file_vpn_vpn_proto_msgTypes[15]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1388,7 +1475,7 @@ func (x *StopRequest) String() string {
func (*StopRequest) ProtoMessage() {}
func (x *StopRequest) ProtoReflect() protoreflect.Message {
- mi := &file_vpn_vpn_proto_msgTypes[14]
+ mi := &file_vpn_vpn_proto_msgTypes[15]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1401,7 +1488,7 @@ func (x *StopRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use StopRequest.ProtoReflect.Descriptor instead.
func (*StopRequest) Descriptor() ([]byte, []int) {
- return file_vpn_vpn_proto_rawDescGZIP(), []int{14}
+ return file_vpn_vpn_proto_rawDescGZIP(), []int{15}
}
// StopResponse is a response to stopping the tunnel. After sending this response, the tunnel closes
@@ -1418,7 +1505,7 @@ type StopResponse struct {
func (x *StopResponse) Reset() {
*x = StopResponse{}
if protoimpl.UnsafeEnabled {
- mi := &file_vpn_vpn_proto_msgTypes[15]
+ mi := &file_vpn_vpn_proto_msgTypes[16]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1431,7 +1518,7 @@ func (x *StopResponse) String() string {
func (*StopResponse) ProtoMessage() {}
func (x *StopResponse) ProtoReflect() protoreflect.Message {
- mi := &file_vpn_vpn_proto_msgTypes[15]
+ mi := &file_vpn_vpn_proto_msgTypes[16]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1444,7 +1531,7 @@ func (x *StopResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use StopResponse.ProtoReflect.Descriptor instead.
func (*StopResponse) Descriptor() ([]byte, []int) {
- return file_vpn_vpn_proto_rawDescGZIP(), []int{15}
+ return file_vpn_vpn_proto_rawDescGZIP(), []int{16}
}
func (x *StopResponse) GetSuccess() bool {
@@ -1472,7 +1559,7 @@ type StatusRequest struct {
func (x *StatusRequest) Reset() {
*x = StatusRequest{}
if protoimpl.UnsafeEnabled {
- mi := &file_vpn_vpn_proto_msgTypes[16]
+ mi := &file_vpn_vpn_proto_msgTypes[17]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1485,7 +1572,7 @@ func (x *StatusRequest) String() string {
func (*StatusRequest) ProtoMessage() {}
func (x *StatusRequest) ProtoReflect() protoreflect.Message {
- mi := &file_vpn_vpn_proto_msgTypes[16]
+ mi := &file_vpn_vpn_proto_msgTypes[17]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1498,7 +1585,7 @@ func (x *StatusRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use StatusRequest.ProtoReflect.Descriptor instead.
func (*StatusRequest) Descriptor() ([]byte, []int) {
- return file_vpn_vpn_proto_rawDescGZIP(), []int{16}
+ return file_vpn_vpn_proto_rawDescGZIP(), []int{17}
}
// Status is sent in response to a StatusRequest or broadcasted to all clients
@@ -1519,7 +1606,7 @@ type Status struct {
func (x *Status) Reset() {
*x = Status{}
if protoimpl.UnsafeEnabled {
- mi := &file_vpn_vpn_proto_msgTypes[17]
+ mi := &file_vpn_vpn_proto_msgTypes[18]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1532,7 +1619,7 @@ func (x *Status) String() string {
func (*Status) ProtoMessage() {}
func (x *Status) ProtoReflect() protoreflect.Message {
- mi := &file_vpn_vpn_proto_msgTypes[17]
+ mi := &file_vpn_vpn_proto_msgTypes[18]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1545,7 +1632,7 @@ func (x *Status) ProtoReflect() protoreflect.Message {
// Deprecated: Use Status.ProtoReflect.Descriptor instead.
func (*Status) Descriptor() ([]byte, []int) {
- return file_vpn_vpn_proto_rawDescGZIP(), []int{17}
+ return file_vpn_vpn_proto_rawDescGZIP(), []int{18}
}
func (x *Status) GetLifecycle() Status_Lifecycle {
@@ -1581,7 +1668,7 @@ type Log_Field struct {
func (x *Log_Field) Reset() {
*x = Log_Field{}
if protoimpl.UnsafeEnabled {
- mi := &file_vpn_vpn_proto_msgTypes[18]
+ mi := &file_vpn_vpn_proto_msgTypes[19]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1594,7 +1681,7 @@ func (x *Log_Field) String() string {
func (*Log_Field) ProtoMessage() {}
func (x *Log_Field) ProtoReflect() protoreflect.Message {
- mi := &file_vpn_vpn_proto_msgTypes[18]
+ mi := &file_vpn_vpn_proto_msgTypes[19]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1642,7 +1729,7 @@ type NetworkSettingsRequest_DNSSettings struct {
func (x *NetworkSettingsRequest_DNSSettings) Reset() {
*x = NetworkSettingsRequest_DNSSettings{}
if protoimpl.UnsafeEnabled {
- mi := &file_vpn_vpn_proto_msgTypes[19]
+ mi := &file_vpn_vpn_proto_msgTypes[20]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1655,7 +1742,7 @@ func (x *NetworkSettingsRequest_DNSSettings) String() string {
func (*NetworkSettingsRequest_DNSSettings) ProtoMessage() {}
func (x *NetworkSettingsRequest_DNSSettings) ProtoReflect() protoreflect.Message {
- mi := &file_vpn_vpn_proto_msgTypes[19]
+ mi := &file_vpn_vpn_proto_msgTypes[20]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1668,7 +1755,7 @@ func (x *NetworkSettingsRequest_DNSSettings) ProtoReflect() protoreflect.Message
// Deprecated: Use NetworkSettingsRequest_DNSSettings.ProtoReflect.Descriptor instead.
func (*NetworkSettingsRequest_DNSSettings) Descriptor() ([]byte, []int) {
- return file_vpn_vpn_proto_rawDescGZIP(), []int{10, 0}
+ return file_vpn_vpn_proto_rawDescGZIP(), []int{11, 0}
}
func (x *NetworkSettingsRequest_DNSSettings) GetServers() []string {
@@ -1722,7 +1809,7 @@ type NetworkSettingsRequest_IPv4Settings struct {
func (x *NetworkSettingsRequest_IPv4Settings) Reset() {
*x = NetworkSettingsRequest_IPv4Settings{}
if protoimpl.UnsafeEnabled {
- mi := &file_vpn_vpn_proto_msgTypes[20]
+ mi := &file_vpn_vpn_proto_msgTypes[21]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1735,7 +1822,7 @@ func (x *NetworkSettingsRequest_IPv4Settings) String() string {
func (*NetworkSettingsRequest_IPv4Settings) ProtoMessage() {}
func (x *NetworkSettingsRequest_IPv4Settings) ProtoReflect() protoreflect.Message {
- mi := &file_vpn_vpn_proto_msgTypes[20]
+ mi := &file_vpn_vpn_proto_msgTypes[21]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1748,7 +1835,7 @@ func (x *NetworkSettingsRequest_IPv4Settings) ProtoReflect() protoreflect.Messag
// Deprecated: Use NetworkSettingsRequest_IPv4Settings.ProtoReflect.Descriptor instead.
func (*NetworkSettingsRequest_IPv4Settings) Descriptor() ([]byte, []int) {
- return file_vpn_vpn_proto_rawDescGZIP(), []int{10, 1}
+ return file_vpn_vpn_proto_rawDescGZIP(), []int{11, 1}
}
func (x *NetworkSettingsRequest_IPv4Settings) GetAddrs() []string {
@@ -1800,7 +1887,7 @@ type NetworkSettingsRequest_IPv6Settings struct {
func (x *NetworkSettingsRequest_IPv6Settings) Reset() {
*x = NetworkSettingsRequest_IPv6Settings{}
if protoimpl.UnsafeEnabled {
- mi := &file_vpn_vpn_proto_msgTypes[21]
+ mi := &file_vpn_vpn_proto_msgTypes[22]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1813,7 +1900,7 @@ func (x *NetworkSettingsRequest_IPv6Settings) String() string {
func (*NetworkSettingsRequest_IPv6Settings) ProtoMessage() {}
func (x *NetworkSettingsRequest_IPv6Settings) ProtoReflect() protoreflect.Message {
- mi := &file_vpn_vpn_proto_msgTypes[21]
+ mi := &file_vpn_vpn_proto_msgTypes[22]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1826,7 +1913,7 @@ func (x *NetworkSettingsRequest_IPv6Settings) ProtoReflect() protoreflect.Messag
// Deprecated: Use NetworkSettingsRequest_IPv6Settings.ProtoReflect.Descriptor instead.
func (*NetworkSettingsRequest_IPv6Settings) Descriptor() ([]byte, []int) {
- return file_vpn_vpn_proto_rawDescGZIP(), []int{10, 2}
+ return file_vpn_vpn_proto_rawDescGZIP(), []int{11, 2}
}
func (x *NetworkSettingsRequest_IPv6Settings) GetAddrs() []string {
@@ -1871,7 +1958,7 @@ type NetworkSettingsRequest_IPv4Settings_IPv4Route struct {
func (x *NetworkSettingsRequest_IPv4Settings_IPv4Route) Reset() {
*x = NetworkSettingsRequest_IPv4Settings_IPv4Route{}
if protoimpl.UnsafeEnabled {
- mi := &file_vpn_vpn_proto_msgTypes[22]
+ mi := &file_vpn_vpn_proto_msgTypes[23]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1884,7 +1971,7 @@ func (x *NetworkSettingsRequest_IPv4Settings_IPv4Route) String() string {
func (*NetworkSettingsRequest_IPv4Settings_IPv4Route) ProtoMessage() {}
func (x *NetworkSettingsRequest_IPv4Settings_IPv4Route) ProtoReflect() protoreflect.Message {
- mi := &file_vpn_vpn_proto_msgTypes[22]
+ mi := &file_vpn_vpn_proto_msgTypes[23]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1897,7 +1984,7 @@ func (x *NetworkSettingsRequest_IPv4Settings_IPv4Route) ProtoReflect() protorefl
// Deprecated: Use NetworkSettingsRequest_IPv4Settings_IPv4Route.ProtoReflect.Descriptor instead.
func (*NetworkSettingsRequest_IPv4Settings_IPv4Route) Descriptor() ([]byte, []int) {
- return file_vpn_vpn_proto_rawDescGZIP(), []int{10, 1, 0}
+ return file_vpn_vpn_proto_rawDescGZIP(), []int{11, 1, 0}
}
func (x *NetworkSettingsRequest_IPv4Settings_IPv4Route) GetDestination() string {
@@ -1935,7 +2022,7 @@ type NetworkSettingsRequest_IPv6Settings_IPv6Route struct {
func (x *NetworkSettingsRequest_IPv6Settings_IPv6Route) Reset() {
*x = NetworkSettingsRequest_IPv6Settings_IPv6Route{}
if protoimpl.UnsafeEnabled {
- mi := &file_vpn_vpn_proto_msgTypes[23]
+ mi := &file_vpn_vpn_proto_msgTypes[24]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1948,7 +2035,7 @@ func (x *NetworkSettingsRequest_IPv6Settings_IPv6Route) String() string {
func (*NetworkSettingsRequest_IPv6Settings_IPv6Route) ProtoMessage() {}
func (x *NetworkSettingsRequest_IPv6Settings_IPv6Route) ProtoReflect() protoreflect.Message {
- mi := &file_vpn_vpn_proto_msgTypes[23]
+ mi := &file_vpn_vpn_proto_msgTypes[24]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1961,7 +2048,7 @@ func (x *NetworkSettingsRequest_IPv6Settings_IPv6Route) ProtoReflect() protorefl
// Deprecated: Use NetworkSettingsRequest_IPv6Settings_IPv6Route.ProtoReflect.Descriptor instead.
func (*NetworkSettingsRequest_IPv6Settings_IPv6Route) Descriptor() ([]byte, []int) {
- return file_vpn_vpn_proto_rawDescGZIP(), []int{10, 2, 0}
+ return file_vpn_vpn_proto_rawDescGZIP(), []int{11, 2, 0}
}
func (x *NetworkSettingsRequest_IPv6Settings_IPv6Route) GetDestination() string {
@@ -1998,7 +2085,7 @@ type StartRequest_Header struct {
func (x *StartRequest_Header) Reset() {
*x = StartRequest_Header{}
if protoimpl.UnsafeEnabled {
- mi := &file_vpn_vpn_proto_msgTypes[24]
+ mi := &file_vpn_vpn_proto_msgTypes[25]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2011,7 +2098,7 @@ func (x *StartRequest_Header) String() string {
func (*StartRequest_Header) ProtoMessage() {}
func (x *StartRequest_Header) ProtoReflect() protoreflect.Message {
- mi := &file_vpn_vpn_proto_msgTypes[24]
+ mi := &file_vpn_vpn_proto_msgTypes[25]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2024,7 +2111,7 @@ func (x *StartRequest_Header) ProtoReflect() protoreflect.Message {
// Deprecated: Use StartRequest_Header.ProtoReflect.Descriptor instead.
func (*StartRequest_Header) Descriptor() ([]byte, []int) {
- return file_vpn_vpn_proto_rawDescGZIP(), []int{12, 0}
+ return file_vpn_vpn_proto_rawDescGZIP(), []int{13, 0}
}
func (x *StartRequest_Header) GetName() string {
@@ -2047,6 +2134,8 @@ var file_vpn_vpn_proto_rawDesc = []byte{
0x0a, 0x0d, 0x76, 0x70, 0x6e, 0x2f, 0x76, 0x70, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
0x03, 0x76, 0x70, 0x6e, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72,
+ 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x3d, 0x0a, 0x03, 0x52, 0x50, 0x43, 0x12, 0x15, 0x0a, 0x06,
0x6d, 0x73, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x6d, 0x73,
0x67, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f,
@@ -2158,7 +2247,7 @@ var file_vpn_vpn_proto_rawDesc = []byte{
0x49, 0x4c, 0x45, 0x44, 0x10, 0x06, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c,
0x49, 0x4e, 0x47, 0x10, 0x07, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x45,
0x44, 0x10, 0x08, 0x12, 0x0c, 0x0a, 0x08, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x49, 0x4e, 0x47, 0x10,
- 0x09, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x44, 0x10, 0x0a, 0x22, 0xc0,
+ 0x09, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x44, 0x10, 0x0a, 0x22, 0xff,
0x01, 0x0a, 0x05, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c,
@@ -2171,148 +2260,167 @@ var file_vpn_vpn_proto_rawDesc = []byte{
0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
0x6d, 0x70, 0x52, 0x0d, 0x6c, 0x61, 0x73, 0x74, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b,
- 0x65, 0x22, 0xb5, 0x0a, 0x0a, 0x16, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x74,
- 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x32, 0x0a, 0x15,
- 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x68, 0x65, 0x61, 0x64, 0x5f,
- 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x13, 0x74, 0x75, 0x6e,
- 0x6e, 0x65, 0x6c, 0x4f, 0x76, 0x65, 0x72, 0x68, 0x65, 0x61, 0x64, 0x42, 0x79, 0x74, 0x65, 0x73,
- 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x74, 0x75, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6d,
- 0x74, 0x75, 0x12, 0x4a, 0x0a, 0x0c, 0x64, 0x6e, 0x73, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e,
- 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x76, 0x70, 0x6e, 0x2e, 0x4e,
+ 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x70, 0x69, 0x6e, 0x67, 0x18, 0x07,
+ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x76, 0x70, 0x6e, 0x2e, 0x4c, 0x61, 0x73, 0x74, 0x50,
+ 0x69, 0x6e, 0x67, 0x48, 0x00, 0x52, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x50, 0x69, 0x6e, 0x67, 0x88,
+ 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x70, 0x69, 0x6e, 0x67,
+ 0x22, 0xf0, 0x01, 0x0a, 0x08, 0x4c, 0x61, 0x73, 0x74, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x33, 0x0a,
+ 0x07, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19,
+ 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
+ 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x6c, 0x61, 0x74, 0x65, 0x6e,
+ 0x63, 0x79, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x69, 0x64, 0x5f, 0x70, 0x32, 0x70, 0x18, 0x02, 0x20,
+ 0x01, 0x28, 0x08, 0x52, 0x06, 0x64, 0x69, 0x64, 0x50, 0x32, 0x70, 0x12, 0x25, 0x0a, 0x0e, 0x70,
+ 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x5f, 0x64, 0x65, 0x72, 0x70, 0x18, 0x03, 0x20,
+ 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x44, 0x65,
+ 0x72, 0x70, 0x12, 0x54, 0x0a, 0x16, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x5f,
+ 0x64, 0x65, 0x72, 0x70, 0x5f, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01,
+ 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+ 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52,
+ 0x14, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x44, 0x65, 0x72, 0x70, 0x4c, 0x61,
+ 0x74, 0x65, 0x6e, 0x63, 0x79, 0x88, 0x01, 0x01, 0x42, 0x19, 0x0a, 0x17, 0x5f, 0x70, 0x72, 0x65,
+ 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x5f, 0x64, 0x65, 0x72, 0x70, 0x5f, 0x6c, 0x61, 0x74, 0x65,
+ 0x6e, 0x63, 0x79, 0x22, 0xb5, 0x0a, 0x0a, 0x16, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53,
+ 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x32,
+ 0x0a, 0x15, 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x68, 0x65, 0x61,
+ 0x64, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x13, 0x74,
+ 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x4f, 0x76, 0x65, 0x72, 0x68, 0x65, 0x61, 0x64, 0x42, 0x79, 0x74,
+ 0x65, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x74, 0x75, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52,
+ 0x03, 0x6d, 0x74, 0x75, 0x12, 0x4a, 0x0a, 0x0c, 0x64, 0x6e, 0x73, 0x5f, 0x73, 0x65, 0x74, 0x74,
+ 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x76, 0x70, 0x6e,
+ 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73,
+ 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x44, 0x4e, 0x53, 0x53, 0x65, 0x74, 0x74, 0x69,
+ 0x6e, 0x67, 0x73, 0x52, 0x0b, 0x64, 0x6e, 0x73, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73,
+ 0x12, 0x32, 0x0a, 0x15, 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x72, 0x65, 0x6d, 0x6f, 0x74,
+ 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
+ 0x13, 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x64, 0x64,
+ 0x72, 0x65, 0x73, 0x73, 0x12, 0x4d, 0x0a, 0x0d, 0x69, 0x70, 0x76, 0x34, 0x5f, 0x73, 0x65, 0x74,
+ 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x76, 0x70,
+ 0x6e, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67,
+ 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x50, 0x76, 0x34, 0x53, 0x65, 0x74,
+ 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x0c, 0x69, 0x70, 0x76, 0x34, 0x53, 0x65, 0x74, 0x74, 0x69,
+ 0x6e, 0x67, 0x73, 0x12, 0x4d, 0x0a, 0x0d, 0x69, 0x70, 0x76, 0x36, 0x5f, 0x73, 0x65, 0x74, 0x74,
+ 0x69, 0x6e, 0x67, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x76, 0x70, 0x6e,
+ 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73,
+ 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x50, 0x76, 0x36, 0x53, 0x65, 0x74, 0x74,
+ 0x69, 0x6e, 0x67, 0x73, 0x52, 0x0c, 0x69, 0x70, 0x76, 0x36, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e,
+ 0x67, 0x73, 0x1a, 0xcb, 0x01, 0x0a, 0x0b, 0x44, 0x4e, 0x53, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e,
+ 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20,
+ 0x03, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x25, 0x0a, 0x0e,
+ 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x02,
+ 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x44, 0x6f, 0x6d, 0x61,
+ 0x69, 0x6e, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x6e, 0x61,
+ 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
+ 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x64, 0x6f,
+ 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x6d, 0x61, 0x74,
+ 0x63, 0x68, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x35, 0x0a, 0x17, 0x6d, 0x61, 0x74,
+ 0x63, 0x68, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x5f, 0x6e, 0x6f, 0x5f, 0x73, 0x65,
+ 0x61, 0x72, 0x63, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x6d, 0x61, 0x74, 0x63,
+ 0x68, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x4e, 0x6f, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68,
+ 0x1a, 0xf4, 0x02, 0x0a, 0x0c, 0x49, 0x50, 0x76, 0x34, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67,
+ 0x73, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x64, 0x64, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09,
+ 0x52, 0x05, 0x61, 0x64, 0x64, 0x72, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x6e, 0x65,
+ 0x74, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x73,
+ 0x75, 0x62, 0x6e, 0x65, 0x74, 0x4d, 0x61, 0x73, 0x6b, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x6f,
+ 0x75, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74,
+ 0x65, 0x72, 0x12, 0x5b, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x5f, 0x72,
+ 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x76, 0x70,
+ 0x6e, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67,
+ 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x50, 0x76, 0x34, 0x53, 0x65, 0x74,
+ 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x49, 0x50, 0x76, 0x34, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52,
+ 0x0e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12,
+ 0x5b, 0x0a, 0x0f, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x5f, 0x72, 0x6f, 0x75, 0x74,
+ 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x76, 0x70, 0x6e, 0x2e, 0x4e,
0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65,
- 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x44, 0x4e, 0x53, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67,
- 0x73, 0x52, 0x0b, 0x64, 0x6e, 0x73, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x32,
- 0x0a, 0x15, 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f,
- 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x74,
- 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65,
- 0x73, 0x73, 0x12, 0x4d, 0x0a, 0x0d, 0x69, 0x70, 0x76, 0x34, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69,
- 0x6e, 0x67, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x76, 0x70, 0x6e, 0x2e,
- 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52,
- 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x50, 0x76, 0x34, 0x53, 0x65, 0x74, 0x74, 0x69,
- 0x6e, 0x67, 0x73, 0x52, 0x0c, 0x69, 0x70, 0x76, 0x34, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67,
- 0x73, 0x12, 0x4d, 0x0a, 0x0d, 0x69, 0x70, 0x76, 0x36, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e,
- 0x67, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x76, 0x70, 0x6e, 0x2e, 0x4e,
+ 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x50, 0x76, 0x34, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e,
+ 0x67, 0x73, 0x2e, 0x49, 0x50, 0x76, 0x34, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x0e, 0x65, 0x78,
+ 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x1a, 0x59, 0x0a, 0x09,
+ 0x49, 0x50, 0x76, 0x34, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73,
+ 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b,
+ 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6d,
+ 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, 0x61, 0x73, 0x6b, 0x12,
+ 0x16, 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
+ 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x1a, 0xf1, 0x02, 0x0a, 0x0c, 0x49, 0x50, 0x76, 0x36,
+ 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x64, 0x64, 0x72,
+ 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x61, 0x64, 0x64, 0x72, 0x73, 0x12, 0x25,
+ 0x0a, 0x0e, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x73,
+ 0x18, 0x02, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0d, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x4c, 0x65,
+ 0x6e, 0x67, 0x74, 0x68, 0x73, 0x12, 0x5b, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65,
+ 0x64, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32,
+ 0x2e, 0x76, 0x70, 0x6e, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x74, 0x74,
+ 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x50, 0x76, 0x36,
+ 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x49, 0x50, 0x76, 0x36, 0x52, 0x6f, 0x75,
+ 0x74, 0x65, 0x52, 0x0e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x74,
+ 0x65, 0x73, 0x12, 0x5b, 0x0a, 0x0f, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x5f, 0x72,
+ 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x76, 0x70,
+ 0x6e, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67,
+ 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x50, 0x76, 0x36, 0x53, 0x65, 0x74,
+ 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x49, 0x50, 0x76, 0x36, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52,
+ 0x0e, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x1a,
+ 0x6a, 0x0a, 0x09, 0x49, 0x50, 0x76, 0x36, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x0b,
+ 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23,
+ 0x0a, 0x0d, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18,
+ 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x4c, 0x65, 0x6e,
+ 0x67, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20,
+ 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x22, 0x58, 0x0a, 0x17, 0x4e,
0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65,
- 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x50, 0x76, 0x36, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e,
- 0x67, 0x73, 0x52, 0x0c, 0x69, 0x70, 0x76, 0x36, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73,
- 0x1a, 0xcb, 0x01, 0x0a, 0x0b, 0x44, 0x4e, 0x53, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73,
- 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
- 0x09, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x65,
- 0x61, 0x72, 0x63, 0x68, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03,
- 0x28, 0x09, 0x52, 0x0d, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
- 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
- 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4e, 0x61,
- 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x64, 0x6f, 0x6d, 0x61,
- 0x69, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x6d, 0x61, 0x74, 0x63, 0x68,
- 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x35, 0x0a, 0x17, 0x6d, 0x61, 0x74, 0x63, 0x68,
- 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x5f, 0x6e, 0x6f, 0x5f, 0x73, 0x65, 0x61, 0x72,
- 0x63, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x44,
- 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x4e, 0x6f, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x1a, 0xf4,
- 0x02, 0x0a, 0x0c, 0x49, 0x50, 0x76, 0x34, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12,
- 0x14, 0x0a, 0x05, 0x61, 0x64, 0x64, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05,
- 0x61, 0x64, 0x64, 0x72, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x5f,
- 0x6d, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x75, 0x62,
- 0x6e, 0x65, 0x74, 0x4d, 0x61, 0x73, 0x6b, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x74,
- 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72,
- 0x12, 0x5b, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x5f, 0x72, 0x6f, 0x75,
- 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x76, 0x70, 0x6e, 0x2e,
- 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52,
- 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x50, 0x76, 0x34, 0x53, 0x65, 0x74, 0x74, 0x69,
- 0x6e, 0x67, 0x73, 0x2e, 0x49, 0x50, 0x76, 0x34, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x0e, 0x69,
- 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x5b, 0x0a,
- 0x0f, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73,
- 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x76, 0x70, 0x6e, 0x2e, 0x4e, 0x65, 0x74,
- 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75,
- 0x65, 0x73, 0x74, 0x2e, 0x49, 0x50, 0x76, 0x34, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73,
- 0x2e, 0x49, 0x50, 0x76, 0x34, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x0e, 0x65, 0x78, 0x63, 0x6c,
- 0x75, 0x64, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x1a, 0x59, 0x0a, 0x09, 0x49, 0x50,
- 0x76, 0x34, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69,
- 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65,
- 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x61, 0x73,
- 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, 0x61, 0x73, 0x6b, 0x12, 0x16, 0x0a,
- 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72,
- 0x6f, 0x75, 0x74, 0x65, 0x72, 0x1a, 0xf1, 0x02, 0x0a, 0x0c, 0x49, 0x50, 0x76, 0x36, 0x53, 0x65,
- 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x64, 0x64, 0x72, 0x73, 0x18,
- 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x61, 0x64, 0x64, 0x72, 0x73, 0x12, 0x25, 0x0a, 0x0e,
- 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x73, 0x18, 0x02,
- 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0d, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x4c, 0x65, 0x6e, 0x67,
- 0x74, 0x68, 0x73, 0x12, 0x5b, 0x0a, 0x0f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x5f,
- 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x76,
- 0x70, 0x6e, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e,
- 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x50, 0x76, 0x36, 0x53, 0x65,
- 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x2e, 0x49, 0x50, 0x76, 0x36, 0x52, 0x6f, 0x75, 0x74, 0x65,
- 0x52, 0x0e, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73,
- 0x12, 0x5b, 0x0a, 0x0f, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x5f, 0x72, 0x6f, 0x75,
- 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x76, 0x70, 0x6e, 0x2e,
- 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52,
- 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x49, 0x50, 0x76, 0x36, 0x53, 0x65, 0x74, 0x74, 0x69,
- 0x6e, 0x67, 0x73, 0x2e, 0x49, 0x50, 0x76, 0x36, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x0e, 0x65,
- 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x1a, 0x6a, 0x0a,
- 0x09, 0x49, 0x50, 0x76, 0x36, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65,
- 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
- 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d,
- 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x02, 0x20,
- 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x4c, 0x65, 0x6e, 0x67, 0x74,
- 0x68, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28,
- 0x09, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x22, 0x58, 0x0a, 0x17, 0x4e, 0x65, 0x74,
- 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70,
- 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18,
- 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x23,
- 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18,
- 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73,
- 0x61, 0x67, 0x65, 0x22, 0xd4, 0x02, 0x0a, 0x0c, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x71,
- 0x75, 0x65, 0x73, 0x74, 0x12, 0x34, 0x0a, 0x16, 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x66,
- 0x69, 0x6c, 0x65, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x18, 0x01,
- 0x20, 0x01, 0x28, 0x05, 0x52, 0x14, 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x69, 0x6c, 0x65,
- 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6f,
- 0x64, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63,
- 0x6f, 0x64, 0x65, 0x72, 0x55, 0x72, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x70, 0x69, 0x5f, 0x74,
- 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x70, 0x69, 0x54,
- 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x32, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18,
- 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x76, 0x70, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x72,
- 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52,
- 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69,
- 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x76,
- 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f,
- 0x6f, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65,
- 0x4f, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x64, 0x65, 0x73, 0x6b,
- 0x74, 0x6f, 0x70, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28,
- 0x09, 0x52, 0x13, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x44, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x56,
- 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x1a, 0x32, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72,
- 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
- 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
- 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x4e, 0x0a, 0x0d, 0x53, 0x74,
- 0x61, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73,
+ 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73,
+ 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73,
+ 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
+ 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65,
+ 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xd4, 0x02, 0x0a, 0x0c, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52,
+ 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x34, 0x0a, 0x16, 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c,
+ 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x14, 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x46, 0x69,
+ 0x6c, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x1b, 0x0a, 0x09,
+ 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
+ 0x08, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x55, 0x72, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x70, 0x69,
+ 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x70,
+ 0x69, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x32, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72,
+ 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x76, 0x70, 0x6e, 0x2e, 0x53, 0x74,
+ 0x61, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65,
+ 0x72, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65,
+ 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64,
+ 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63,
+ 0x65, 0x5f, 0x6f, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69,
+ 0x63, 0x65, 0x4f, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x64, 0x65,
+ 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20,
+ 0x01, 0x28, 0x09, 0x52, 0x13, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x44, 0x65, 0x73, 0x6b, 0x74, 0x6f,
+ 0x70, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x1a, 0x32, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x64,
+ 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
+ 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x4e, 0x0a, 0x0d,
+ 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a,
+ 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07,
+ 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72,
+ 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c,
+ 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x0d, 0x0a, 0x0b,
+ 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x4d, 0x0a, 0x0c, 0x53,
+ 0x74, 0x6f, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73,
0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75,
0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d,
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72,
- 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x0d, 0x0a, 0x0b, 0x53, 0x74,
- 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x4d, 0x0a, 0x0c, 0x53, 0x74, 0x6f,
- 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63,
- 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63,
- 0x65, 0x73, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73,
- 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f,
- 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x0f, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x74,
- 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xe4, 0x01, 0x0a, 0x06, 0x53, 0x74,
- 0x61, 0x74, 0x75, 0x73, 0x12, 0x33, 0x0a, 0x09, 0x6c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c,
- 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x76, 0x70, 0x6e, 0x2e, 0x53, 0x74,
- 0x61, 0x74, 0x75, 0x73, 0x2e, 0x4c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x52, 0x09,
- 0x6c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72,
- 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
- 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x30,
- 0x0a, 0x0b, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20,
- 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x70, 0x6e, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x55, 0x70,
- 0x64, 0x61, 0x74, 0x65, 0x52, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
- 0x22, 0x4e, 0x0a, 0x09, 0x4c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x12, 0x0b, 0x0a,
- 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x54,
- 0x41, 0x52, 0x54, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x54, 0x41, 0x52,
- 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x54, 0x4f, 0x50, 0x50, 0x49, 0x4e,
- 0x47, 0x10, 0x03, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x54, 0x4f, 0x50, 0x50, 0x45, 0x44, 0x10, 0x04,
- 0x42, 0x39, 0x5a, 0x1d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63,
- 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x76, 0x32, 0x2f, 0x76, 0x70,
- 0x6e, 0xaa, 0x02, 0x17, 0x43, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x44, 0x65, 0x73, 0x6b, 0x74, 0x6f,
- 0x70, 0x2e, 0x56, 0x70, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f,
- 0x74, 0x6f, 0x33,
+ 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x0f, 0x0a, 0x0d, 0x53, 0x74,
+ 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xe4, 0x01, 0x0a, 0x06,
+ 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x33, 0x0a, 0x09, 0x6c, 0x69, 0x66, 0x65, 0x63, 0x79,
+ 0x63, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x76, 0x70, 0x6e, 0x2e,
+ 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x4c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65,
+ 0x52, 0x09, 0x6c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x65,
+ 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01,
+ 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
+ 0x12, 0x30, 0x0a, 0x0b, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18,
+ 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x76, 0x70, 0x6e, 0x2e, 0x50, 0x65, 0x65, 0x72,
+ 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61,
+ 0x74, 0x65, 0x22, 0x4e, 0x0a, 0x09, 0x4c, 0x69, 0x66, 0x65, 0x63, 0x79, 0x63, 0x6c, 0x65, 0x12,
+ 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08,
+ 0x53, 0x54, 0x41, 0x52, 0x54, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x54,
+ 0x41, 0x52, 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x54, 0x4f, 0x50, 0x50,
+ 0x49, 0x4e, 0x47, 0x10, 0x03, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x54, 0x4f, 0x50, 0x50, 0x45, 0x44,
+ 0x10, 0x04, 0x42, 0x39, 0x5a, 0x1d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x76, 0x32, 0x2f,
+ 0x76, 0x70, 0x6e, 0xaa, 0x02, 0x17, 0x43, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x44, 0x65, 0x73, 0x6b,
+ 0x74, 0x6f, 0x70, 0x2e, 0x56, 0x70, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -2328,7 +2436,7 @@ func file_vpn_vpn_proto_rawDescGZIP() []byte {
}
var file_vpn_vpn_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
-var file_vpn_vpn_proto_msgTypes = make([]protoimpl.MessageInfo, 25)
+var file_vpn_vpn_proto_msgTypes = make([]protoimpl.MessageInfo, 26)
var file_vpn_vpn_proto_goTypes = []interface{}{
(Log_Level)(0), // 0: vpn.Log.Level
(Workspace_Status)(0), // 1: vpn.Workspace.Status
@@ -2343,66 +2451,71 @@ var file_vpn_vpn_proto_goTypes = []interface{}{
(*PeerUpdate)(nil), // 10: vpn.PeerUpdate
(*Workspace)(nil), // 11: vpn.Workspace
(*Agent)(nil), // 12: vpn.Agent
- (*NetworkSettingsRequest)(nil), // 13: vpn.NetworkSettingsRequest
- (*NetworkSettingsResponse)(nil), // 14: vpn.NetworkSettingsResponse
- (*StartRequest)(nil), // 15: vpn.StartRequest
- (*StartResponse)(nil), // 16: vpn.StartResponse
- (*StopRequest)(nil), // 17: vpn.StopRequest
- (*StopResponse)(nil), // 18: vpn.StopResponse
- (*StatusRequest)(nil), // 19: vpn.StatusRequest
- (*Status)(nil), // 20: vpn.Status
- (*Log_Field)(nil), // 21: vpn.Log.Field
- (*NetworkSettingsRequest_DNSSettings)(nil), // 22: vpn.NetworkSettingsRequest.DNSSettings
- (*NetworkSettingsRequest_IPv4Settings)(nil), // 23: vpn.NetworkSettingsRequest.IPv4Settings
- (*NetworkSettingsRequest_IPv6Settings)(nil), // 24: vpn.NetworkSettingsRequest.IPv6Settings
- (*NetworkSettingsRequest_IPv4Settings_IPv4Route)(nil), // 25: vpn.NetworkSettingsRequest.IPv4Settings.IPv4Route
- (*NetworkSettingsRequest_IPv6Settings_IPv6Route)(nil), // 26: vpn.NetworkSettingsRequest.IPv6Settings.IPv6Route
- (*StartRequest_Header)(nil), // 27: vpn.StartRequest.Header
- (*timestamppb.Timestamp)(nil), // 28: google.protobuf.Timestamp
+ (*LastPing)(nil), // 13: vpn.LastPing
+ (*NetworkSettingsRequest)(nil), // 14: vpn.NetworkSettingsRequest
+ (*NetworkSettingsResponse)(nil), // 15: vpn.NetworkSettingsResponse
+ (*StartRequest)(nil), // 16: vpn.StartRequest
+ (*StartResponse)(nil), // 17: vpn.StartResponse
+ (*StopRequest)(nil), // 18: vpn.StopRequest
+ (*StopResponse)(nil), // 19: vpn.StopResponse
+ (*StatusRequest)(nil), // 20: vpn.StatusRequest
+ (*Status)(nil), // 21: vpn.Status
+ (*Log_Field)(nil), // 22: vpn.Log.Field
+ (*NetworkSettingsRequest_DNSSettings)(nil), // 23: vpn.NetworkSettingsRequest.DNSSettings
+ (*NetworkSettingsRequest_IPv4Settings)(nil), // 24: vpn.NetworkSettingsRequest.IPv4Settings
+ (*NetworkSettingsRequest_IPv6Settings)(nil), // 25: vpn.NetworkSettingsRequest.IPv6Settings
+ (*NetworkSettingsRequest_IPv4Settings_IPv4Route)(nil), // 26: vpn.NetworkSettingsRequest.IPv4Settings.IPv4Route
+ (*NetworkSettingsRequest_IPv6Settings_IPv6Route)(nil), // 27: vpn.NetworkSettingsRequest.IPv6Settings.IPv6Route
+ (*StartRequest_Header)(nil), // 28: vpn.StartRequest.Header
+ (*timestamppb.Timestamp)(nil), // 29: google.protobuf.Timestamp
+ (*durationpb.Duration)(nil), // 30: google.protobuf.Duration
}
var file_vpn_vpn_proto_depIdxs = []int32{
3, // 0: vpn.ManagerMessage.rpc:type_name -> vpn.RPC
9, // 1: vpn.ManagerMessage.get_peer_update:type_name -> vpn.GetPeerUpdate
- 14, // 2: vpn.ManagerMessage.network_settings:type_name -> vpn.NetworkSettingsResponse
- 15, // 3: vpn.ManagerMessage.start:type_name -> vpn.StartRequest
- 17, // 4: vpn.ManagerMessage.stop:type_name -> vpn.StopRequest
+ 15, // 2: vpn.ManagerMessage.network_settings:type_name -> vpn.NetworkSettingsResponse
+ 16, // 3: vpn.ManagerMessage.start:type_name -> vpn.StartRequest
+ 18, // 4: vpn.ManagerMessage.stop:type_name -> vpn.StopRequest
3, // 5: vpn.TunnelMessage.rpc:type_name -> vpn.RPC
8, // 6: vpn.TunnelMessage.log:type_name -> vpn.Log
10, // 7: vpn.TunnelMessage.peer_update:type_name -> vpn.PeerUpdate
- 13, // 8: vpn.TunnelMessage.network_settings:type_name -> vpn.NetworkSettingsRequest
- 16, // 9: vpn.TunnelMessage.start:type_name -> vpn.StartResponse
- 18, // 10: vpn.TunnelMessage.stop:type_name -> vpn.StopResponse
+ 14, // 8: vpn.TunnelMessage.network_settings:type_name -> vpn.NetworkSettingsRequest
+ 17, // 9: vpn.TunnelMessage.start:type_name -> vpn.StartResponse
+ 19, // 10: vpn.TunnelMessage.stop:type_name -> vpn.StopResponse
3, // 11: vpn.ClientMessage.rpc:type_name -> vpn.RPC
- 15, // 12: vpn.ClientMessage.start:type_name -> vpn.StartRequest
- 17, // 13: vpn.ClientMessage.stop:type_name -> vpn.StopRequest
- 19, // 14: vpn.ClientMessage.status:type_name -> vpn.StatusRequest
+ 16, // 12: vpn.ClientMessage.start:type_name -> vpn.StartRequest
+ 18, // 13: vpn.ClientMessage.stop:type_name -> vpn.StopRequest
+ 20, // 14: vpn.ClientMessage.status:type_name -> vpn.StatusRequest
3, // 15: vpn.ServiceMessage.rpc:type_name -> vpn.RPC
- 16, // 16: vpn.ServiceMessage.start:type_name -> vpn.StartResponse
- 18, // 17: vpn.ServiceMessage.stop:type_name -> vpn.StopResponse
- 20, // 18: vpn.ServiceMessage.status:type_name -> vpn.Status
+ 17, // 16: vpn.ServiceMessage.start:type_name -> vpn.StartResponse
+ 19, // 17: vpn.ServiceMessage.stop:type_name -> vpn.StopResponse
+ 21, // 18: vpn.ServiceMessage.status:type_name -> vpn.Status
0, // 19: vpn.Log.level:type_name -> vpn.Log.Level
- 21, // 20: vpn.Log.fields:type_name -> vpn.Log.Field
+ 22, // 20: vpn.Log.fields:type_name -> vpn.Log.Field
11, // 21: vpn.PeerUpdate.upserted_workspaces:type_name -> vpn.Workspace
12, // 22: vpn.PeerUpdate.upserted_agents:type_name -> vpn.Agent
11, // 23: vpn.PeerUpdate.deleted_workspaces:type_name -> vpn.Workspace
12, // 24: vpn.PeerUpdate.deleted_agents:type_name -> vpn.Agent
1, // 25: vpn.Workspace.status:type_name -> vpn.Workspace.Status
- 28, // 26: vpn.Agent.last_handshake:type_name -> google.protobuf.Timestamp
- 22, // 27: vpn.NetworkSettingsRequest.dns_settings:type_name -> vpn.NetworkSettingsRequest.DNSSettings
- 23, // 28: vpn.NetworkSettingsRequest.ipv4_settings:type_name -> vpn.NetworkSettingsRequest.IPv4Settings
- 24, // 29: vpn.NetworkSettingsRequest.ipv6_settings:type_name -> vpn.NetworkSettingsRequest.IPv6Settings
- 27, // 30: vpn.StartRequest.headers:type_name -> vpn.StartRequest.Header
- 2, // 31: vpn.Status.lifecycle:type_name -> vpn.Status.Lifecycle
- 10, // 32: vpn.Status.peer_update:type_name -> vpn.PeerUpdate
- 25, // 33: vpn.NetworkSettingsRequest.IPv4Settings.included_routes:type_name -> vpn.NetworkSettingsRequest.IPv4Settings.IPv4Route
- 25, // 34: vpn.NetworkSettingsRequest.IPv4Settings.excluded_routes:type_name -> vpn.NetworkSettingsRequest.IPv4Settings.IPv4Route
- 26, // 35: vpn.NetworkSettingsRequest.IPv6Settings.included_routes:type_name -> vpn.NetworkSettingsRequest.IPv6Settings.IPv6Route
- 26, // 36: vpn.NetworkSettingsRequest.IPv6Settings.excluded_routes:type_name -> vpn.NetworkSettingsRequest.IPv6Settings.IPv6Route
- 37, // [37:37] is the sub-list for method output_type
- 37, // [37:37] is the sub-list for method input_type
- 37, // [37:37] is the sub-list for extension type_name
- 37, // [37:37] is the sub-list for extension extendee
- 0, // [0:37] is the sub-list for field type_name
+ 29, // 26: vpn.Agent.last_handshake:type_name -> google.protobuf.Timestamp
+ 13, // 27: vpn.Agent.last_ping:type_name -> vpn.LastPing
+ 30, // 28: vpn.LastPing.latency:type_name -> google.protobuf.Duration
+ 30, // 29: vpn.LastPing.preferred_derp_latency:type_name -> google.protobuf.Duration
+ 23, // 30: vpn.NetworkSettingsRequest.dns_settings:type_name -> vpn.NetworkSettingsRequest.DNSSettings
+ 24, // 31: vpn.NetworkSettingsRequest.ipv4_settings:type_name -> vpn.NetworkSettingsRequest.IPv4Settings
+ 25, // 32: vpn.NetworkSettingsRequest.ipv6_settings:type_name -> vpn.NetworkSettingsRequest.IPv6Settings
+ 28, // 33: vpn.StartRequest.headers:type_name -> vpn.StartRequest.Header
+ 2, // 34: vpn.Status.lifecycle:type_name -> vpn.Status.Lifecycle
+ 10, // 35: vpn.Status.peer_update:type_name -> vpn.PeerUpdate
+ 26, // 36: vpn.NetworkSettingsRequest.IPv4Settings.included_routes:type_name -> vpn.NetworkSettingsRequest.IPv4Settings.IPv4Route
+ 26, // 37: vpn.NetworkSettingsRequest.IPv4Settings.excluded_routes:type_name -> vpn.NetworkSettingsRequest.IPv4Settings.IPv4Route
+ 27, // 38: vpn.NetworkSettingsRequest.IPv6Settings.included_routes:type_name -> vpn.NetworkSettingsRequest.IPv6Settings.IPv6Route
+ 27, // 39: vpn.NetworkSettingsRequest.IPv6Settings.excluded_routes:type_name -> vpn.NetworkSettingsRequest.IPv6Settings.IPv6Route
+ 40, // [40:40] is the sub-list for method output_type
+ 40, // [40:40] is the sub-list for method input_type
+ 40, // [40:40] is the sub-list for extension type_name
+ 40, // [40:40] is the sub-list for extension extendee
+ 0, // [0:40] is the sub-list for field type_name
}
func init() { file_vpn_vpn_proto_init() }
@@ -2532,7 +2645,7 @@ func file_vpn_vpn_proto_init() {
}
}
file_vpn_vpn_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*NetworkSettingsRequest); i {
+ switch v := v.(*LastPing); i {
case 0:
return &v.state
case 1:
@@ -2544,7 +2657,7 @@ func file_vpn_vpn_proto_init() {
}
}
file_vpn_vpn_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*NetworkSettingsResponse); i {
+ switch v := v.(*NetworkSettingsRequest); i {
case 0:
return &v.state
case 1:
@@ -2556,7 +2669,7 @@ func file_vpn_vpn_proto_init() {
}
}
file_vpn_vpn_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*StartRequest); i {
+ switch v := v.(*NetworkSettingsResponse); i {
case 0:
return &v.state
case 1:
@@ -2568,7 +2681,7 @@ func file_vpn_vpn_proto_init() {
}
}
file_vpn_vpn_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*StartResponse); i {
+ switch v := v.(*StartRequest); i {
case 0:
return &v.state
case 1:
@@ -2580,7 +2693,7 @@ func file_vpn_vpn_proto_init() {
}
}
file_vpn_vpn_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*StopRequest); i {
+ switch v := v.(*StartResponse); i {
case 0:
return &v.state
case 1:
@@ -2592,7 +2705,7 @@ func file_vpn_vpn_proto_init() {
}
}
file_vpn_vpn_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*StopResponse); i {
+ switch v := v.(*StopRequest); i {
case 0:
return &v.state
case 1:
@@ -2604,7 +2717,7 @@ func file_vpn_vpn_proto_init() {
}
}
file_vpn_vpn_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*StatusRequest); i {
+ switch v := v.(*StopResponse); i {
case 0:
return &v.state
case 1:
@@ -2616,7 +2729,7 @@ func file_vpn_vpn_proto_init() {
}
}
file_vpn_vpn_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*Status); i {
+ switch v := v.(*StatusRequest); i {
case 0:
return &v.state
case 1:
@@ -2628,7 +2741,7 @@ func file_vpn_vpn_proto_init() {
}
}
file_vpn_vpn_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*Log_Field); i {
+ switch v := v.(*Status); i {
case 0:
return &v.state
case 1:
@@ -2640,7 +2753,7 @@ func file_vpn_vpn_proto_init() {
}
}
file_vpn_vpn_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*NetworkSettingsRequest_DNSSettings); i {
+ switch v := v.(*Log_Field); i {
case 0:
return &v.state
case 1:
@@ -2652,7 +2765,7 @@ func file_vpn_vpn_proto_init() {
}
}
file_vpn_vpn_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*NetworkSettingsRequest_IPv4Settings); i {
+ switch v := v.(*NetworkSettingsRequest_DNSSettings); i {
case 0:
return &v.state
case 1:
@@ -2664,7 +2777,7 @@ func file_vpn_vpn_proto_init() {
}
}
file_vpn_vpn_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*NetworkSettingsRequest_IPv6Settings); i {
+ switch v := v.(*NetworkSettingsRequest_IPv4Settings); i {
case 0:
return &v.state
case 1:
@@ -2676,7 +2789,7 @@ func file_vpn_vpn_proto_init() {
}
}
file_vpn_vpn_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*NetworkSettingsRequest_IPv4Settings_IPv4Route); i {
+ switch v := v.(*NetworkSettingsRequest_IPv6Settings); i {
case 0:
return &v.state
case 1:
@@ -2688,7 +2801,7 @@ func file_vpn_vpn_proto_init() {
}
}
file_vpn_vpn_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*NetworkSettingsRequest_IPv6Settings_IPv6Route); i {
+ switch v := v.(*NetworkSettingsRequest_IPv4Settings_IPv4Route); i {
case 0:
return &v.state
case 1:
@@ -2700,6 +2813,18 @@ func file_vpn_vpn_proto_init() {
}
}
file_vpn_vpn_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*NetworkSettingsRequest_IPv6Settings_IPv6Route); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_vpn_vpn_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*StartRequest_Header); i {
case 0:
return &v.state
@@ -2735,13 +2860,15 @@ func file_vpn_vpn_proto_init() {
(*ServiceMessage_Stop)(nil),
(*ServiceMessage_Status)(nil),
}
+ file_vpn_vpn_proto_msgTypes[9].OneofWrappers = []interface{}{}
+ file_vpn_vpn_proto_msgTypes[10].OneofWrappers = []interface{}{}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_vpn_vpn_proto_rawDesc,
NumEnums: 3,
- NumMessages: 25,
+ NumMessages: 26,
NumExtensions: 0,
NumServices: 0,
},
diff --git a/vpn/vpn.proto b/vpn/vpn.proto
index 963098c60a..44383fa80e 100644
--- a/vpn/vpn.proto
+++ b/vpn/vpn.proto
@@ -3,6 +3,7 @@ option go_package = "github.com/coder/coder/v2/vpn";
option csharp_namespace = "Coder.Desktop.Vpn.Proto";
import "google/protobuf/timestamp.proto";
+import "google/protobuf/duration.proto";
package vpn;
@@ -130,6 +131,21 @@ message Agent {
// 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.Timestamp last_handshake = 6;
+ // If unset, a successful ping has not yet been made.
+ optional LastPing last_ping = 7;
+}
+
+message LastPing {
+ // latency is the RTT of the ping to the agent.
+ google.protobuf.Duration latency = 1;
+ // did_p2p indicates whether the ping was sent P2P, or over DERP.
+ bool did_p2p = 2;
+ // preferred_derp is the human readable name of the preferred DERP region,
+ // or the region used for the last ping, if it was sent over DERP.
+ string preferred_derp = 3;
+ // preferred_derp_latency is the last known latency to the preferred DERP
+ // region. Unset if the region does not appear in the DERP map.
+ optional google.protobuf.Duration preferred_derp_latency = 4;
}
// NetworkSettingsRequest is based on