From 48e91e41e3222807eea0e605b39dc6062f7729ea Mon Sep 17 00:00:00 2001 From: Spike Curtis Date: Wed, 4 Sep 2024 16:42:27 +0400 Subject: [PATCH] feat: add CoderVPN protocol definition & implementaion --- Makefile | 8 + vpn/serdes.go | 135 +++ vpn/speaker.go | 374 ++++++ vpn/speaker_internal_test.go | 456 +++++++ vpn/tunnel.go | 229 ++++ vpn/vpn.pb.go | 2155 ++++++++++++++++++++++++++++++++++ vpn/vpn.proto | 197 ++++ 7 files changed, 3554 insertions(+) create mode 100644 vpn/serdes.go create mode 100644 vpn/speaker.go create mode 100644 vpn/speaker_internal_test.go create mode 100644 vpn/tunnel.go create mode 100644 vpn/vpn.pb.go create mode 100644 vpn/vpn.proto diff --git a/Makefile b/Makefile index 0b2f14a8d3429..0765346500975 100644 --- a/Makefile +++ b/Makefile @@ -488,6 +488,7 @@ gen: \ agent/proto/agent.pb.go \ provisionersdk/proto/provisioner.pb.go \ provisionerd/proto/provisionerd.pb.go \ + vpn/vpn.proto \ coderd/database/dump.sql \ $(DB_GEN_FILES) \ site/src/api/typesGenerated.ts \ @@ -517,6 +518,7 @@ gen/mark-fresh: agent/proto/agent.pb.go \ provisionersdk/proto/provisioner.pb.go \ provisionerd/proto/provisionerd.pb.go \ + vpn/vpn.proto \ coderd/database/dump.sql \ $(DB_GEN_FILES) \ site/src/api/typesGenerated.ts \ @@ -600,6 +602,12 @@ provisionerd/proto/provisionerd.pb.go: provisionerd/proto/provisionerd.proto --go-drpc_opt=paths=source_relative \ ./provisionerd/proto/provisionerd.proto +vpn/vpn.pb.go: vpn/vpn.proto + protoc \ + --go_out=. \ + --go_opt=paths=source_relative \ + ./vpn/vpn.proto + site/src/api/typesGenerated.ts: $(wildcard scripts/apitypings/*) $(shell find ./codersdk $(FIND_EXCLUSIONS) -type f -name '*.go') go run ./scripts/apitypings/ > $@ ./scripts/pnpm_install.sh diff --git a/vpn/serdes.go b/vpn/serdes.go new file mode 100644 index 0000000000000..0be9247150289 --- /dev/null +++ b/vpn/serdes.go @@ -0,0 +1,135 @@ +package vpn + +import ( + "context" + "encoding/binary" + "io" + "sync" + + "google.golang.org/protobuf/proto" + + "cdr.dev/slog" +) + +// MaxLength is the largest possible CoderVPN Protocol message size. This is set +// so that a misbehaving peer can't cause us to allocate a huge amount of memory. +const MaxLength = 0x1000000 // 16MiB + +// serdes SERializes and DESerializes protobuf messages to and from the conn. +type serdes[S rpcMessage, R receivableRPCMessage[RR], RR any] struct { + ctx context.Context + logger slog.Logger + conn io.ReadWriteCloser + sendCh <-chan S + recvCh chan<- R + closeOnce sync.Once + wg sync.WaitGroup +} + +func (s *serdes[_, R, RR]) recvLoop() { + s.logger.Debug(s.ctx, "starting recvLoop") + defer s.closeIdempotent() + defer close(s.recvCh) + for { + var length uint32 + if err := binary.Read(s.conn, binary.BigEndian, &length); err != nil { + s.logger.Debug(s.ctx, "failed to read length", slog.Error(err)) + return + } + if length > MaxLength { + s.logger.Critical(s.ctx, "message length exceeds max", + slog.F("length", length)) + return + } + s.logger.Debug(s.ctx, "about to read message", slog.F("length", length)) + mb := make([]byte, length) + if n, err := io.ReadFull(s.conn, mb); err != nil { + s.logger.Debug(s.ctx, "failed to read message", + slog.Error(err), + slog.F("num_bytes_read", n)) + return + } + msg := R(new(RR)) + if err := proto.Unmarshal(mb, msg); err != nil { + s.logger.Critical(s.ctx, "failed to unmarshal message", slog.Error(err)) + return + } + select { + case s.recvCh <- msg: + s.logger.Debug(s.ctx, "passed received message to speaker") + case <-s.ctx.Done(): + s.logger.Debug(s.ctx, "recvLoop canceled", slog.Error(s.ctx.Err())) + } + } +} + +func (s *serdes[S, _, _]) sendLoop() { + s.logger.Debug(s.ctx, "starting sendLoop") + defer s.closeIdempotent() + for { + select { + case <-s.ctx.Done(): + s.logger.Debug(s.ctx, "sendLoop canceled", slog.Error(s.ctx.Err())) + return + case msg, ok := <-s.sendCh: + if !ok { + s.logger.Debug(s.ctx, "sendCh closed") + return + } + mb, err := proto.Marshal(msg) + if err != nil { + s.logger.Critical(s.ctx, "failed to marshal message", slog.Error(err)) + return + } + if err := binary.Write(s.conn, binary.BigEndian, uint32(len(mb))); err != nil { + s.logger.Debug(s.ctx, "failed to write length", slog.Error(err)) + return + } + if _, err := s.conn.Write(mb); err != nil { + s.logger.Debug(s.ctx, "failed to write message", slog.Error(err)) + return + } + } + } +} + +func (s *serdes[_, _, _]) closeIdempotent() { + s.closeOnce.Do(func() { + if err := s.conn.Close(); err != nil { + s.logger.Error(s.ctx, "failed to close connection", slog.Error(err)) + } else { + s.logger.Info(s.ctx, "closed connection") + } + }) +} + +func (s *serdes[_, _, _]) Close() error { + s.closeIdempotent() + s.wg.Wait() + return nil +} + +func (s *serdes[_, _, _]) start() { + s.wg.Add(2) + go func() { + defer s.wg.Done() + s.recvLoop() + }() + go func() { + defer s.wg.Done() + s.sendLoop() + }() +} + +func newSerdes[S rpcMessage, R receivableRPCMessage[RR], RR any]( + ctx context.Context, logger slog.Logger, conn io.ReadWriteCloser, + sendCh <-chan S, recvCh chan<- R, +) *serdes[S, R, RR] { + return &serdes[S, R, RR]{ + ctx: ctx, + logger: logger.Named("serdes"), + conn: conn, + sendCh: sendCh, + recvCh: recvCh, + } +} diff --git a/vpn/speaker.go b/vpn/speaker.go new file mode 100644 index 0000000000000..bb6fa9659b6c2 --- /dev/null +++ b/vpn/speaker.go @@ -0,0 +1,374 @@ +package vpn + +import ( + "context" + "fmt" + "io" + "strings" + "sync" + + "golang.org/x/xerrors" + "google.golang.org/protobuf/proto" + + "cdr.dev/slog" + "github.com/coder/coder/v2/apiversion" +) + +type SpeakerRole string + +type rpcMessage interface { + proto.Message + GetRpc() *RPC + // EnsureRPC isn't autogenerated, but we'll manually add it for RPC types so that the speaker + // can allocate the RPC. + EnsureRPC() *RPC +} + +func (t *TunnelMessage) EnsureRPC() *RPC { + if t.Rpc == nil { + t.Rpc = &RPC{} + } + return t.Rpc +} + +func (m *ManagerMessage) EnsureRPC() *RPC { + if m.Rpc == nil { + m.Rpc = &RPC{} + } + return m.Rpc +} + +// receivableRPCMessage is an rpcMessage that we can receive, and unmarshal, using generics, from a +// byte stream. proto.Unmarshal requires us to have already allocated the memory for the message +// type we are unmarshalling. All our message types are pointers like *TunnelMessage, so to +// allocate, the compiler needs to know: +// +// a) that the type is a pointer type +// b) what type it is pointing to +// +// So, this generic interface requires that the message is a pointer to the type RR. Then, we pass +// both the receivableRPCMessage and RR as type constraints, so that we'll have access to the +// underlying type when it comes time to allocate it. It's a bit messy, but the alternative is +// reflection, which has its own challenges in understandability. +type receivableRPCMessage[RR any] interface { + rpcMessage + *RR +} + +const ( + SpeakerRoleManager SpeakerRole = "manager" + SpeakerRoleTunnel SpeakerRole = "tunnel" +) + +const ( + CurrentMajor = 1 + CurrentMinor = 0 +) + +var CurrentVersion = apiversion.New(CurrentMajor, CurrentMinor) + +// speaker is an implementation of the CoderVPN protocol. It handles unary RPCs and their responses, +// as well as the low-level serialization & deserialization to the ReadWriteCloser (rwc). +// +// ┌────────┐ sendCh +// ◄─────│ ◄────────────────────────────────────────────────────────────────── ◄┐ +// │ │ ▲ rpc requests +// rwc │ serdes │ │ │ sendReply() +// │ │ ┌───────────────────┐ ┌──────┼──────┐ +// ──────► ┼────────► recvFromSerdes() │ rpc │rpc handling │ │ +// └────────┘ recvCh │ ┼────────────► ◄──── unaryRPC() +// │ │ responses │ │ │ +// │ │ │ │ +// │ │ └─────────────┘ ┌ ─ ─│─ ─ ─ ─ ─ ─ ─ ┐ +// │ ┼──────────────────────────────────────────► request handling +// └───────────────────┘ requests (outside speaker) +// └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ +// +// speaker is implemented as a generic type that accepts the type of message we send (S), the type we receive (R), and +// the underlying type that R points to (RR). The speaker is intended to be wrapped by another, non-generic type for +// the role (manager or tunnel). E.g. Tunnel from this package. +// +// The serdes handles SERialiazation and DESerialization of the low level message types. The wrapping type may send +// non-RPC messages (that is messages that don't expect an explicit reply) by sending on the sendCh. +// +// Unary RPCs are handled by the unaryRPC() function, which handles sending the message and waiting for the response. +// +// recvFromSerdes() reads all incoming messages from the serdes. If they are RPC responses, it dispatches them to the +// waiting unaryRPC() function call, if any. If they are RPC requests or non-RPC messages, it wraps them in a request +// struct and sends them over the requests chan. The manager/tunnel role type must read from this chan and handle +// the requests. If they are RPC types, it should call sendReply() on the request with the reply message. +type speaker[S rpcMessage, R receivableRPCMessage[RR], RR any] struct { + serdes *serdes[S, R, RR] + requests chan *request[S, R] + logger slog.Logger + nextMsgID uint64 + + ctx context.Context + cancel context.CancelFunc + + sendCh chan<- S + recvCh <-chan R + recvLoopDone chan struct{} + + mu sync.Mutex + responseChans map[uint64]chan R +} + +// newSpeaker creates a new protocol speaker. +func newSpeaker[S rpcMessage, R receivableRPCMessage[RR], RR any]( + ctx context.Context, logger slog.Logger, conn io.ReadWriteCloser, + me, them SpeakerRole, +) ( + *speaker[S, R, RR], error, +) { + ctx, cancel := context.WithCancel(ctx) + if err := handshake(ctx, conn, logger, me, them); err != nil { + cancel() + return nil, xerrors.Errorf("handshake failed: %w", err) + } + sendCh := make(chan S) + recvCh := make(chan R) + s := &speaker[S, R, RR]{ + serdes: newSerdes(ctx, logger, conn, sendCh, recvCh), + logger: logger, + requests: make(chan *request[S, R]), + responseChans: make(map[uint64]chan R), + nextMsgID: 1, + ctx: ctx, + cancel: cancel, + sendCh: sendCh, + recvCh: recvCh, + recvLoopDone: make(chan struct{}), + } + return s, nil +} + +// start starts the serialzation/deserialization. It's important this happens +// after any assignments of the speaker to its owning Tunnel or Manager, since +// the mutex is copied and that is not threadsafe. +// nolint: revive +func (s *speaker[_, _, _]) start() { + s.serdes.start() + go s.recvFromSerdes() +} + +func (s *speaker[S, R, _]) recvFromSerdes() { + defer close(s.recvLoopDone) + defer close(s.requests) + for { + select { + case <-s.ctx.Done(): + s.logger.Debug(s.ctx, "recvFromSerdes context done while waiting for proto", slog.Error(s.ctx.Err())) + return + case msg, ok := <-s.recvCh: + if !ok { + s.logger.Debug(s.ctx, "recvCh is closed") + return + } + rpc := msg.GetRpc() + if rpc != nil && rpc.ResponseTo != 0 { + // this is a unary response + s.tryToDeliverResponse(msg) + continue + } + req := &request[S, R]{ + ctx: s.ctx, + msg: msg, + replyCh: s.sendCh, + } + select { + case <-s.ctx.Done(): + s.logger.Debug(s.ctx, "recvFromSerdes context done while waiting for request handler", slog.Error(s.ctx.Err())) + return + case s.requests <- req: + } + } + } +} + +// nolint: revive +func (s *speaker[_, _, _]) Close() error { + s.cancel() + err := s.serdes.Close() + return err +} + +// unaryRPC sends a request/response style RPC over the protocol, waits for the response, then +// returns the response +func (s *speaker[S, R, _]) unaryRPC(ctx context.Context, req S) (resp R, err error) { + rpc := req.EnsureRPC() + msgID, respCh := s.newRPC() + rpc.MsgId = msgID + logger := s.logger.With(slog.F("msg_id", msgID)) + select { + case <-ctx.Done(): + return resp, ctx.Err() + case <-s.ctx.Done(): + return resp, xerrors.Errorf("vpn protocol closed: %w", s.ctx.Err()) + case <-s.recvLoopDone: + logger.Debug(s.ctx, "recvLoopDone while sending request") + return resp, io.ErrUnexpectedEOF + case s.sendCh <- req: + logger.Debug(s.ctx, "sent rpc request", slog.F("req", req)) + } + select { + case <-ctx.Done(): + s.rmResponseChan(msgID) + return resp, ctx.Err() + case <-s.ctx.Done(): + s.rmResponseChan(msgID) + return resp, xerrors.Errorf("vpn protocol closed: %w", s.ctx.Err()) + case <-s.recvLoopDone: + logger.Debug(s.ctx, "recvLoopDone while waiting for response") + return resp, io.ErrUnexpectedEOF + case resp = <-respCh: + logger.Debug(s.ctx, "got response", slog.F("resp", resp)) + return resp, nil + } +} + +func (s *speaker[_, R, _]) newRPC() (uint64, chan R) { + s.mu.Lock() + defer s.mu.Unlock() + msgID := s.nextMsgID + s.nextMsgID++ + c := make(chan R, 1) + s.responseChans[msgID] = c + return msgID, c +} + +func (s *speaker[_, _, _]) rmResponseChan(msgID uint64) { + s.mu.Lock() + defer s.mu.Unlock() + delete(s.responseChans, msgID) +} + +func (s *speaker[_, R, _]) tryToDeliverResponse(resp R) { + msgID := resp.GetRpc().GetResponseTo() + s.mu.Lock() + defer s.mu.Unlock() + c, ok := s.responseChans[msgID] + if ok { + c <- resp + // Remove the channel since we delivered a response. This ensures that each response channel + // gets _at most_ one response. Since the channels are buffered with size 1, send will + // never block. + delete(s.responseChans, msgID) + } +} + +// handshake performs the initial CoderVPN protocol handshake over the given conn +func handshake( + ctx context.Context, conn io.ReadWriteCloser, logger slog.Logger, me, them SpeakerRole, +) error { + // read and write simultaneously to avoid deadlocking if the conn is not buffered + errCh := make(chan error, 2) + go func() { + ours := headerString(CurrentVersion, me) + _, err := conn.Write([]byte(ours)) + logger.Debug(ctx, "wrote out header") + if err != nil { + err = xerrors.Errorf("write header: %w", err) + } + errCh <- err + }() + headerCh := make(chan string, 1) + go func() { + // we can't use bufio.Scanner here because we need to ensure we don't read beyond the + // first newline. So, we'll read one byte at a time. It's inefficient, but the initial + // header is only a few characters, so we'll keep this code simple. + buf := make([]byte, 256) + have := 0 + for { + _, err := conn.Read(buf[have : have+1]) + if err != nil { + errCh <- xerrors.Errorf("read header: %w", err) + return + } + if buf[have] == '\n' { + logger.Debug(ctx, "got newline header delimiter") + // use have (not have+1) since we don't want the delimiter for verification. + headerCh <- string(buf[:have]) + return + } + have++ + if have >= len(buf) { + errCh <- xerrors.Errorf("header malformed or too large: %s", string(buf)) + return + } + } + }() + + writeOK := false + theirHeader := "" + readOK := false + for !(readOK && writeOK) { + select { + case <-ctx.Done(): + _ = conn.Close() // ensure our read/write goroutines get a chance to clean up + return ctx.Err() + case err := <-errCh: + if err == nil { + // write goroutine sends nil when completing successfully. + logger.Debug(ctx, "write ok") + writeOK = true + continue + } + _ = conn.Close() + return err + case theirHeader = <-headerCh: + logger.Debug(ctx, "read ok") + readOK = true + } + } + logger.Debug(ctx, "handshake read/write complete", slog.F("their_header", theirHeader)) + err := validateHeader(theirHeader, them) + if err != nil { + return xerrors.Errorf("validate header (%s): %w", theirHeader, err) + } + return nil +} + +const headerPreamble = "codervpn" + +func headerString(version *apiversion.APIVersion, role SpeakerRole) string { + return fmt.Sprintf("%s %s %s\n", headerPreamble, version.String(), role) +} + +func validateHeader(header string, expectedRole SpeakerRole) error { + parts := strings.Split(header, " ") + if len(parts) != 3 { + return xerrors.New("wrong number of parts") + } + if parts[0] != headerPreamble { + return xerrors.New("invalid preamble") + } + if err := CurrentVersion.Validate(parts[1]); err != nil { + return xerrors.Errorf("version: %w", err) + } + if parts[2] != string(expectedRole) { + return xerrors.New("unexpected role") + } + return nil +} + +type request[S rpcMessage, R rpcMessage] struct { + ctx context.Context + msg R + replyCh chan<- S +} + +func (r *request[S, _]) sendReply(reply S) error { + rrpc := reply.EnsureRPC() + mrpc := r.msg.GetRpc() + if mrpc == nil { + return xerrors.Errorf("message didn't want a reply") + } + rrpc.ResponseTo = mrpc.MsgId + select { + case <-r.ctx.Done(): + return r.ctx.Err() + case r.replyCh <- reply: + } + return nil +} diff --git a/vpn/speaker_internal_test.go b/vpn/speaker_internal_test.go new file mode 100644 index 0000000000000..c43f33c4a62bc --- /dev/null +++ b/vpn/speaker_internal_test.go @@ -0,0 +1,456 @@ +package vpn + +import ( + "context" + "encoding/binary" + "io" + "net" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/require" + "go.uber.org/goleak" + "google.golang.org/protobuf/proto" + + "cdr.dev/slog" + "cdr.dev/slog/sloggers/slogtest" + "github.com/coder/coder/v2/testutil" +) + +func TestMain(m *testing.M) { + goleak.VerifyTestMain(m) +} + +// 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 +// both sides have the bug and can still communicate. +func TestSpeaker_RawPeer(t *testing.T) { + t.Parallel() + mp, tp := net.Pipe() + defer mp.Close() + defer tp.Close() + ctx := testutil.Context(t, testutil.WaitShort) + // We're going to use deadlines for this test so that we don't hang the main test thread if + // the speaker misbehaves. + err := mp.SetReadDeadline(time.Now().Add(testutil.WaitShort)) + require.NoError(t, err) + err = mp.SetWriteDeadline(time.Now().Add(testutil.WaitShort)) + require.NoError(t, err) + logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug) + var tun *speaker[*TunnelMessage, *ManagerMessage, ManagerMessage] + errCh := make(chan error, 1) + go func() { + s, err := newSpeaker[*TunnelMessage, *ManagerMessage](ctx, logger, tp, SpeakerRoleTunnel, SpeakerRoleManager) + tun = s + errCh <- err + }() + + expectedHandshake := "codervpn 1.0 tunnel\n" + + b := make([]byte, 256) + n, err := mp.Read(b) + require.NoError(t, err) + require.Equal(t, expectedHandshake, string(b[:n])) + + _, err = mp.Write([]byte("codervpn 1.0 manager\n")) + require.NoError(t, err) + + err = testutil.RequireRecvCtx(ctx, t, errCh) + require.NoError(t, err) + tun.start() + + // send a message and verify it follows protocol for encoding + testutil.RequireSendCtx(ctx, t, tun.sendCh, &TunnelMessage{ + Msg: &TunnelMessage_Start{ + Start: &StartResponse{}, + }, + }) + + var msgLen uint32 + err = binary.Read(mp, binary.BigEndian, &msgLen) + require.NoError(t, err) + msgBuf := make([]byte, msgLen) + n, err = mp.Read(msgBuf) + require.NoError(t, err) + require.Equal(t, msgLen, uint32(n)) + msg := new(TunnelMessage) + err = proto.Unmarshal(msgBuf, msg) + require.NoError(t, err) + _, ok := msg.Msg.(*TunnelMessage_Start) + require.True(t, ok) + + // Should close the pipe on close of the speaker. + err = tun.Close() + require.NoError(t, err) + _, err = mp.Read(b) + require.ErrorIs(t, err, io.EOF) +} + +func TestSpeaker_HandshakeRWFailure(t *testing.T) { + t.Parallel() + mp, tp := net.Pipe() + // immediately close the pipe, so we'll get read & write failures on handshake + _ = mp.Close() + ctx := testutil.Context(t, testutil.WaitShort) + logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug) + + var tun *speaker[*TunnelMessage, *ManagerMessage, ManagerMessage] + errCh := make(chan error, 1) + go func() { + s, err := newSpeaker[*TunnelMessage, *ManagerMessage]( + ctx, logger.Named("tun"), tp, SpeakerRoleTunnel, SpeakerRoleManager, + ) + tun = s + errCh <- err + }() + err := testutil.RequireRecvCtx(ctx, t, errCh) + require.ErrorContains(t, err, "handshake failed") + require.Nil(t, tun) +} + +func TestSpeaker_HandshakeCtxDone(t *testing.T) { + t.Parallel() + mp, tp := net.Pipe() + defer mp.Close() + defer tp.Close() + testCtx := testutil.Context(t, testutil.WaitShort) + ctx, cancel := context.WithCancel(testCtx) + logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug) + + var tun *speaker[*TunnelMessage, *ManagerMessage, ManagerMessage] + errCh := make(chan error, 1) + go func() { + s, err := newSpeaker[*TunnelMessage, *ManagerMessage]( + ctx, logger.Named("tun"), tp, SpeakerRoleTunnel, SpeakerRoleManager, + ) + tun = s + errCh <- err + }() + cancel() + err := testutil.RequireRecvCtx(testCtx, t, errCh) + require.ErrorContains(t, err, "handshake failed") + require.Nil(t, tun) +} + +func TestSpeaker_OversizeHandshake(t *testing.T) { + t.Parallel() + mp, tp := net.Pipe() + defer mp.Close() + defer tp.Close() + ctx := testutil.Context(t, testutil.WaitShort) + // We're going to use deadlines for this test so that we don't hang the main test thread if + // the speaker misbehaves. + err := mp.SetReadDeadline(time.Now().Add(testutil.WaitShort)) + require.NoError(t, err) + err = mp.SetWriteDeadline(time.Now().Add(testutil.WaitShort)) + require.NoError(t, err) + logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug) + var tun *speaker[*TunnelMessage, *ManagerMessage, ManagerMessage] + errCh := make(chan error, 1) + go func() { + s, err := newSpeaker[*TunnelMessage, *ManagerMessage](ctx, logger, tp, SpeakerRoleTunnel, SpeakerRoleManager) + tun = s + errCh <- err + }() + + expectedHandshake := "codervpn 1.0 tunnel\n" + + b := make([]byte, 256) + n, err := mp.Read(b) + require.NoError(t, err) + require.Equal(t, expectedHandshake, string(b[:n])) + + badHandshake := strings.Repeat("bad", 256) + _, err = mp.Write([]byte(badHandshake)) + require.Error(t, err) // other side closes when we write too much + + err = testutil.RequireRecvCtx(ctx, t, errCh) + require.ErrorContains(t, err, "handshake failed") + require.Nil(t, tun) +} + +func TestSpeaker_HandshakeInvalid(t *testing.T) { + t.Parallel() + // nolint: paralleltest // no longer need to reinitialize loop vars in go 1.22 + for _, tc := range []struct { + name, handshake string + }{ + {name: "preamble", handshake: "ssh 1.0 manager\n"}, + {name: "2components", handshake: "ssh manager\n"}, + {name: "newversion", handshake: "codervpn 1.1 manager\n"}, + {name: "oldversion", handshake: "codervpn 0.1 manager\n"}, + {name: "unknown_role", handshake: "codervpn 1.0 supervisor\n"}, + {name: "unexpected_role", handshake: "codervpn 1.0 tunnel\n"}, + } { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + mp, tp := net.Pipe() + defer mp.Close() + defer tp.Close() + ctx := testutil.Context(t, testutil.WaitShort) + // We're going to use deadlines for this test so that we don't hang the main test thread if + // the speaker misbehaves. + err := mp.SetReadDeadline(time.Now().Add(testutil.WaitShort)) + require.NoError(t, err) + err = mp.SetWriteDeadline(time.Now().Add(testutil.WaitShort)) + require.NoError(t, err) + logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug) + var tun *speaker[*TunnelMessage, *ManagerMessage, ManagerMessage] + errCh := make(chan error, 1) + go func() { + s, err := newSpeaker[*TunnelMessage, *ManagerMessage](ctx, logger, tp, SpeakerRoleTunnel, SpeakerRoleManager) + tun = s + errCh <- err + }() + + _, err = mp.Write([]byte(tc.handshake)) + require.NoError(t, err) + + expectedHandshake := "codervpn 1.0 tunnel\n" + b := make([]byte, 256) + n, err := mp.Read(b) + require.NoError(t, err) + require.Equal(t, expectedHandshake, string(b[:n])) + + err = testutil.RequireRecvCtx(ctx, t, errCh) + require.ErrorContains(t, err, "validate header") + require.Nil(t, tun) + }) + } +} + +// 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 +// both sides have the bug and can still communicate. +func TestSpeaker_CorruptMessage(t *testing.T) { + t.Parallel() + mp, tp := net.Pipe() + defer mp.Close() + defer tp.Close() + ctx := testutil.Context(t, testutil.WaitShort) + // We're going to use deadlines for this test so that we don't hang the main test thread if + // the speaker misbehaves. + err := mp.SetReadDeadline(time.Now().Add(testutil.WaitShort)) + require.NoError(t, err) + err = mp.SetWriteDeadline(time.Now().Add(testutil.WaitShort)) + require.NoError(t, err) + logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug) + var tun *speaker[*TunnelMessage, *ManagerMessage, ManagerMessage] + errCh := make(chan error, 1) + go func() { + s, err := newSpeaker[*TunnelMessage, *ManagerMessage](ctx, logger, tp, SpeakerRoleTunnel, SpeakerRoleManager) + tun = s + errCh <- err + }() + + expectedHandshake := "codervpn 1.0 tunnel\n" + + b := make([]byte, 256) + n, err := mp.Read(b) + require.NoError(t, err) + require.Equal(t, expectedHandshake, string(b[:n])) + + _, err = mp.Write([]byte("codervpn 1.0 manager\n")) + require.NoError(t, err) + + err = testutil.RequireRecvCtx(ctx, t, errCh) + require.NoError(t, err) + tun.start() + + err = binary.Write(mp, binary.BigEndian, uint32(10)) + require.NoError(t, err) + n, err = mp.Write([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) + require.NoError(t, err) + require.EqualValues(t, 10, n) + + // it should hang up on us if we write nonsense + _, err = mp.Read(b) + require.ErrorIs(t, err, io.EOF) +} + +func TestSpeaker_unaryRPC_mainline(t *testing.T) { + t.Parallel() + ctx, tun, mgr := setupSpeakers(t) + + errCh := make(chan error, 1) + var resp *TunnelMessage + go func() { + r, err := mgr.unaryRPC(ctx, &ManagerMessage{ + Msg: &ManagerMessage_Start{ + Start: &StartRequest{ + CoderUrl: "https://coder.example.com", + }, + }, + }) + resp = r + errCh <- err + }() + req := testutil.RequireRecvCtx(ctx, t, tun.requests) + require.NotEqualValues(t, 0, req.msg.GetRpc().GetMsgId()) + require.Equal(t, "https://coder.example.com", req.msg.GetStart().GetCoderUrl()) + err := req.sendReply(&TunnelMessage{ + Msg: &TunnelMessage_Start{ + Start: &StartResponse{}, + }, + }) + require.NoError(t, err) + err = testutil.RequireRecvCtx(ctx, t, errCh) + require.NoError(t, err) + _, ok := resp.Msg.(*TunnelMessage_Start) + require.True(t, ok) + + // closing the manager should close the tun.requests channel + err = mgr.Close() + require.NoError(t, err) + select { + case _, ok := <-tun.requests: + require.False(t, ok) + case <-ctx.Done(): + t.Fatal("timed out waiting for requests to close") + } +} + +func TestSpeaker_unaryRPC_canceled(t *testing.T) { + t.Parallel() + testCtx, tun, mgr := setupSpeakers(t) + + ctx, cancel := context.WithCancel(testCtx) + defer cancel() + errCh := make(chan error, 1) + var resp *TunnelMessage + go func() { + r, err := mgr.unaryRPC(ctx, &ManagerMessage{ + Msg: &ManagerMessage_Start{ + Start: &StartRequest{ + CoderUrl: "https://coder.example.com", + }, + }, + }) + resp = r + errCh <- err + }() + req := testutil.RequireRecvCtx(testCtx, t, tun.requests) + require.NotEqualValues(t, 0, req.msg.GetRpc().GetMsgId()) + require.Equal(t, "https://coder.example.com", req.msg.GetStart().GetCoderUrl()) + + cancel() + err := testutil.RequireRecvCtx(testCtx, t, errCh) + require.ErrorIs(t, err, context.Canceled) + require.Nil(t, resp) + + err = req.sendReply(&TunnelMessage{ + Msg: &TunnelMessage_Start{ + Start: &StartResponse{}, + }, + }) + require.NoError(t, err) +} + +func TestSpeaker_unaryRPC_hung_up(t *testing.T) { + t.Parallel() + testCtx, tun, mgr := setupSpeakers(t) + + ctx, cancel := context.WithCancel(testCtx) + defer cancel() + errCh := make(chan error, 1) + var resp *TunnelMessage + go func() { + r, err := mgr.unaryRPC(ctx, &ManagerMessage{ + Msg: &ManagerMessage_Start{ + Start: &StartRequest{ + CoderUrl: "https://coder.example.com", + }, + }, + }) + resp = r + errCh <- err + }() + req := testutil.RequireRecvCtx(testCtx, t, tun.requests) + require.NotEqualValues(t, 0, req.msg.GetRpc().GetMsgId()) + require.Equal(t, "https://coder.example.com", req.msg.GetStart().GetCoderUrl()) + + // When: Tunnel closes instead of replying. + err := tun.Close() + require.NoError(t, err) + // Then: we should get an error on the RPC. + err = testutil.RequireRecvCtx(testCtx, t, errCh) + require.ErrorIs(t, err, io.ErrUnexpectedEOF) + require.Nil(t, resp) +} + +func TestSpeaker_unaryRPC_sendLoop(t *testing.T) { + t.Parallel() + testCtx, tun, mgr := setupSpeakers(t) + + ctx, cancel := context.WithCancel(testCtx) + defer cancel() + + // When: Tunnel closes before we send the RPC + err := tun.Close() + require.NoError(t, err) + + // When: serdes sendloop is closed + // Send a message from the manager. This closes the manager serdes sendloop, since it will error + // when writing the message to the (closed) pipe. + testutil.RequireSendCtx(ctx, t, mgr.sendCh, &ManagerMessage{ + Msg: &ManagerMessage_GetPeerUpdate{}, + }) + + // When: we send an RPC + errCh := make(chan error, 1) + var resp *TunnelMessage + go func() { + r, err := mgr.unaryRPC(ctx, &ManagerMessage{ + Msg: &ManagerMessage_Start{ + Start: &StartRequest{ + CoderUrl: "https://coder.example.com", + }, + }, + }) + resp = r + errCh <- err + }() + + // Then: we should get an error on the RPC. + err = testutil.RequireRecvCtx(testCtx, t, errCh) + require.ErrorIs(t, err, io.ErrUnexpectedEOF) + require.Nil(t, resp) +} + +func setupSpeakers(t *testing.T) ( + context.Context, *speaker[*TunnelMessage, *ManagerMessage, ManagerMessage], *speaker[*ManagerMessage, *TunnelMessage, TunnelMessage], +) { + mp, tp := net.Pipe() + t.Cleanup(func() { _ = mp.Close() }) + t.Cleanup(func() { _ = tp.Close() }) + ctx := testutil.Context(t, testutil.WaitShort) + logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug) + + var tun *speaker[*TunnelMessage, *ManagerMessage, ManagerMessage] + var mgr *speaker[*ManagerMessage, *TunnelMessage, TunnelMessage] + errCh := make(chan error, 2) + go func() { + s, err := newSpeaker[*TunnelMessage, *ManagerMessage]( + ctx, logger.Named("tun"), tp, SpeakerRoleTunnel, SpeakerRoleManager, + ) + tun = s + errCh <- err + }() + go func() { + s, err := newSpeaker[*ManagerMessage, *TunnelMessage]( + ctx, logger.Named("mgr"), mp, SpeakerRoleManager, SpeakerRoleTunnel, + ) + mgr = s + errCh <- err + }() + err := testutil.RequireRecvCtx(ctx, t, errCh) + require.NoError(t, err) + err = testutil.RequireRecvCtx(ctx, t, errCh) + require.NoError(t, err) + tun.start() + mgr.start() + return ctx, tun, mgr +} diff --git a/vpn/tunnel.go b/vpn/tunnel.go new file mode 100644 index 0000000000000..f077f8eb1a442 --- /dev/null +++ b/vpn/tunnel.go @@ -0,0 +1,229 @@ +package vpn + +import ( + "context" + "database/sql/driver" + "encoding/json" + "fmt" + "io" + "reflect" + "strconv" + "sync" + "unicode" + + "golang.org/x/xerrors" + + "cdr.dev/slog" +) + +type Tunnel struct { + speaker[*TunnelMessage, *ManagerMessage, ManagerMessage] + ctx context.Context + logger slog.Logger + requestLoopDone chan struct{} + + logMu sync.Mutex + logs []*TunnelMessage +} + +func NewTunnel( + ctx context.Context, logger slog.Logger, conn io.ReadWriteCloser, +) (*Tunnel, error) { + logger = logger.Named("vpn") + s, err := newSpeaker[*TunnelMessage, *ManagerMessage]( + ctx, logger, conn, SpeakerRoleTunnel, SpeakerRoleManager) + if err != nil { + return nil, err + } + t := &Tunnel{ + // nolint: govet // safe to copy the locks here because we haven't started the speaker + speaker: *(s), + ctx: ctx, + logger: logger, + requestLoopDone: make(chan struct{}), + } + t.speaker.start() + go t.requestLoop() + return t, nil +} + +func (t *Tunnel) requestLoop() { + defer close(t.requestLoopDone) + for req := range t.speaker.requests { + if req.msg.Rpc != nil && req.msg.Rpc.MsgId != 0 { + resp := t.handleRPC(req.msg, req.msg.Rpc.MsgId) + if err := req.sendReply(resp); err != nil { + t.logger.Debug(t.ctx, "failed to send RPC reply", slog.Error(err)) + } + continue + } + // Not a unary RPC. We don't know of any message types that are neither a response nor a + // unary RPC from the Manager. This shouldn't ever happen because we checked the protocol + // version during the handshake. + t.logger.Critical(t.ctx, "unknown request", slog.F("msg", req.msg)) + } +} + +// handleRPC handles unary RPCs from the manager. +func (t *Tunnel) handleRPC(req *ManagerMessage, msgID uint64) *TunnelMessage { + resp := &TunnelMessage{} + resp.Rpc = &RPC{ResponseTo: msgID} + switch msg := req.GetMsg().(type) { + case *ManagerMessage_GetPeerUpdate: + // TODO: actually get the peer updates + resp.Msg = &TunnelMessage_PeerUpdate{ + PeerUpdate: &PeerUpdate{ + UpsertedWorkspaces: nil, + UpsertedAgents: nil, + }, + } + return resp + case *ManagerMessage_Start: + startReq := msg.Start + t.logger.Info(t.ctx, "starting CoderVPN tunnel", + slog.F("url", startReq.CoderUrl), + slog.F("tunnel_fd", startReq.TunnelFileDescriptor), + ) + // TODO: actually start the tunnel + resp.Msg = &TunnelMessage_Start{ + Start: &StartResponse{ + Success: true, + }, + } + return resp + case *ManagerMessage_Stop: + t.logger.Info(t.ctx, "stopping CoderVPN tunnel") + // TODO: actually stop the tunnel + resp.Msg = &TunnelMessage_Stop{ + Stop: &StopResponse{ + Success: true, + }, + } + err := t.speaker.Close() + if err != nil { + t.logger.Error(t.ctx, "failed to close speaker", slog.Error(err)) + } else { + t.logger.Info(t.ctx, "coderVPN tunnel stopped") + } + return resp + default: + t.logger.Warn(t.ctx, "unhandled manager request", slog.F("request", msg)) + return resp + } +} + +// ApplyNetworkSettings sends a request to the manager to apply the given network settings +func (t *Tunnel) ApplyNetworkSettings(ctx context.Context, ns *NetworkSettingsRequest) error { + msg, err := t.speaker.unaryRPC(ctx, &TunnelMessage{ + Msg: &TunnelMessage_NetworkSettings{ + NetworkSettings: ns, + }, + }) + if err != nil { + return xerrors.Errorf("rpc failure: %w", err) + } + resp := msg.GetNetworkSettings() + if !resp.Success { + return xerrors.Errorf("network settings failed: %s", resp.ErrorMessage) + } + return nil +} + +var _ slog.Sink = &Tunnel{} + +func (t *Tunnel) LogEntry(_ context.Context, e slog.SinkEntry) { + t.logMu.Lock() + defer t.logMu.Unlock() + t.logs = append(t.logs, &TunnelMessage{ + Msg: &TunnelMessage_Log{ + Log: sinkEntryToPb(e), + }, + }) +} + +func (t *Tunnel) Sync() { + t.logMu.Lock() + logs := t.logs + t.logs = nil + t.logMu.Unlock() + for _, msg := range logs { + select { + case <-t.ctx.Done(): + return + case t.sendCh <- msg: + } + } +} + +func sinkEntryToPb(e slog.SinkEntry) *Log { + l := &Log{ + Level: Log_Level(e.Level), + Message: e.Message, + LoggerNames: e.LoggerNames, + } + for _, field := range e.Fields { + l.Fields = append(l.Fields, &Log_Field{ + Name: field.Name, + Value: formatValue(field.Value), + }) + } + return l +} + +// the following are taken from sloghuman: + +func formatValue(v interface{}) string { + if vr, ok := v.(driver.Valuer); ok { + var err error + v, err = vr.Value() + if err != nil { + return fmt.Sprintf("error calling Value: %v", err) + } + } + if v == nil { + return "" + } + typ := reflect.TypeOf(v) + switch typ.Kind() { + case reflect.Struct, reflect.Map: + byt, err := json.Marshal(v) + if err != nil { + panic(err) + } + return string(byt) + case reflect.Slice: + // Byte slices are optimistically readable. + if typ.Elem().Kind() == reflect.Uint8 { + return fmt.Sprintf("%q", v) + } + fallthrough + default: + return quote(fmt.Sprintf("%+v", v)) + } +} + +// quotes quotes a string so that it is suitable +// as a key for a map or in general some output that +// cannot span multiple lines or have weird characters. +func quote(key string) string { + // strconv.Quote does not quote an empty string so we need this. + if key == "" { + return `""` + } + + var hasSpace bool + for _, r := range key { + if unicode.IsSpace(r) { + hasSpace = true + break + } + } + quoted := strconv.Quote(key) + // If the key doesn't need to be quoted, don't quote it. + // We do not use strconv.CanBackquote because it doesn't + // account tabs. + if !hasSpace && quoted[1:len(quoted)-1] == key { + return key + } + return quoted +} diff --git a/vpn/vpn.pb.go b/vpn/vpn.pb.go new file mode 100644 index 0000000000000..a99a53a5bd1fe --- /dev/null +++ b/vpn/vpn.pb.go @@ -0,0 +1,2155 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.30.0 +// protoc v4.23.3 +// source: vpn/vpn.proto + +package vpn + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Log_Level int32 + +const ( + // these are designed to match slog levels + Log_DEBUG Log_Level = 0 + Log_INFO Log_Level = 1 + Log_WARN Log_Level = 2 + Log_ERROR Log_Level = 3 + Log_CRITICAL Log_Level = 4 + Log_FATAL Log_Level = 5 +) + +// Enum value maps for Log_Level. +var ( + Log_Level_name = map[int32]string{ + 0: "DEBUG", + 1: "INFO", + 2: "WARN", + 3: "ERROR", + 4: "CRITICAL", + 5: "FATAL", + } + Log_Level_value = map[string]int32{ + "DEBUG": 0, + "INFO": 1, + "WARN": 2, + "ERROR": 3, + "CRITICAL": 4, + "FATAL": 5, + } +) + +func (x Log_Level) Enum() *Log_Level { + p := new(Log_Level) + *p = x + return p +} + +func (x Log_Level) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Log_Level) Descriptor() protoreflect.EnumDescriptor { + return file_vpn_vpn_proto_enumTypes[0].Descriptor() +} + +func (Log_Level) Type() protoreflect.EnumType { + return &file_vpn_vpn_proto_enumTypes[0] +} + +func (x Log_Level) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Log_Level.Descriptor instead. +func (Log_Level) EnumDescriptor() ([]byte, []int) { + return file_vpn_vpn_proto_rawDescGZIP(), []int{3, 0} +} + +type Workspace_Status int32 + +const ( + Workspace_UNKNOWN Workspace_Status = 0 + Workspace_PENDING Workspace_Status = 1 + Workspace_STARTING Workspace_Status = 2 + Workspace_RUNNING Workspace_Status = 3 + Workspace_STOPPING Workspace_Status = 4 + Workspace_STOPPED Workspace_Status = 5 + Workspace_FAILED Workspace_Status = 6 + Workspace_CANCELING Workspace_Status = 7 + Workspace_CANCELED Workspace_Status = 8 + Workspace_DELETING Workspace_Status = 9 + Workspace_DELETED Workspace_Status = 10 +) + +// Enum value maps for Workspace_Status. +var ( + Workspace_Status_name = map[int32]string{ + 0: "UNKNOWN", + 1: "PENDING", + 2: "STARTING", + 3: "RUNNING", + 4: "STOPPING", + 5: "STOPPED", + 6: "FAILED", + 7: "CANCELING", + 8: "CANCELED", + 9: "DELETING", + 10: "DELETED", + } + Workspace_Status_value = map[string]int32{ + "UNKNOWN": 0, + "PENDING": 1, + "STARTING": 2, + "RUNNING": 3, + "STOPPING": 4, + "STOPPED": 5, + "FAILED": 6, + "CANCELING": 7, + "CANCELED": 8, + "DELETING": 9, + "DELETED": 10, + } +) + +func (x Workspace_Status) Enum() *Workspace_Status { + p := new(Workspace_Status) + *p = x + return p +} + +func (x Workspace_Status) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Workspace_Status) Descriptor() protoreflect.EnumDescriptor { + return file_vpn_vpn_proto_enumTypes[1].Descriptor() +} + +func (Workspace_Status) Type() protoreflect.EnumType { + return &file_vpn_vpn_proto_enumTypes[1] +} + +func (x Workspace_Status) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Workspace_Status.Descriptor instead. +func (Workspace_Status) EnumDescriptor() ([]byte, []int) { + return file_vpn_vpn_proto_rawDescGZIP(), []int{6, 0} +} + +// RPC allows a very simple unary request/response RPC mechanism. The requester generates a unique +// msg_id which it sets on the request, the responder sets response_to that msg_id on the response +// message +type RPC struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + MsgId uint64 `protobuf:"varint,1,opt,name=msg_id,json=msgId,proto3" json:"msg_id,omitempty"` + ResponseTo uint64 `protobuf:"varint,2,opt,name=response_to,json=responseTo,proto3" json:"response_to,omitempty"` +} + +func (x *RPC) Reset() { + *x = RPC{} + if protoimpl.UnsafeEnabled { + mi := &file_vpn_vpn_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RPC) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RPC) ProtoMessage() {} + +func (x *RPC) ProtoReflect() protoreflect.Message { + mi := &file_vpn_vpn_proto_msgTypes[0] + 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 RPC.ProtoReflect.Descriptor instead. +func (*RPC) Descriptor() ([]byte, []int) { + return file_vpn_vpn_proto_rawDescGZIP(), []int{0} +} + +func (x *RPC) GetMsgId() uint64 { + if x != nil { + return x.MsgId + } + return 0 +} + +func (x *RPC) GetResponseTo() uint64 { + if x != nil { + return x.ResponseTo + } + return 0 +} + +// ManagerMessage is a message from the manager (to the tunnel). +type ManagerMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Rpc *RPC `protobuf:"bytes,1,opt,name=rpc,proto3" json:"rpc,omitempty"` + // Types that are assignable to Msg: + // + // *ManagerMessage_GetPeerUpdate + // *ManagerMessage_NetworkSettings + // *ManagerMessage_Start + // *ManagerMessage_Stop + Msg isManagerMessage_Msg `protobuf_oneof:"msg"` +} + +func (x *ManagerMessage) Reset() { + *x = ManagerMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_vpn_vpn_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ManagerMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ManagerMessage) ProtoMessage() {} + +func (x *ManagerMessage) ProtoReflect() protoreflect.Message { + mi := &file_vpn_vpn_proto_msgTypes[1] + 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 ManagerMessage.ProtoReflect.Descriptor instead. +func (*ManagerMessage) Descriptor() ([]byte, []int) { + return file_vpn_vpn_proto_rawDescGZIP(), []int{1} +} + +func (x *ManagerMessage) GetRpc() *RPC { + if x != nil { + return x.Rpc + } + return nil +} + +func (m *ManagerMessage) GetMsg() isManagerMessage_Msg { + if m != nil { + return m.Msg + } + return nil +} + +func (x *ManagerMessage) GetGetPeerUpdate() *GetPeerUpdate { + if x, ok := x.GetMsg().(*ManagerMessage_GetPeerUpdate); ok { + return x.GetPeerUpdate + } + return nil +} + +func (x *ManagerMessage) GetNetworkSettings() *NetworkSettingsResponse { + if x, ok := x.GetMsg().(*ManagerMessage_NetworkSettings); ok { + return x.NetworkSettings + } + return nil +} + +func (x *ManagerMessage) GetStart() *StartRequest { + if x, ok := x.GetMsg().(*ManagerMessage_Start); ok { + return x.Start + } + return nil +} + +func (x *ManagerMessage) GetStop() *StopRequest { + if x, ok := x.GetMsg().(*ManagerMessage_Stop); ok { + return x.Stop + } + return nil +} + +type isManagerMessage_Msg interface { + isManagerMessage_Msg() +} + +type ManagerMessage_GetPeerUpdate struct { + GetPeerUpdate *GetPeerUpdate `protobuf:"bytes,2,opt,name=get_peer_update,json=getPeerUpdate,proto3,oneof"` +} + +type ManagerMessage_NetworkSettings struct { + NetworkSettings *NetworkSettingsResponse `protobuf:"bytes,3,opt,name=network_settings,json=networkSettings,proto3,oneof"` +} + +type ManagerMessage_Start struct { + Start *StartRequest `protobuf:"bytes,4,opt,name=start,proto3,oneof"` +} + +type ManagerMessage_Stop struct { + Stop *StopRequest `protobuf:"bytes,5,opt,name=stop,proto3,oneof"` +} + +func (*ManagerMessage_GetPeerUpdate) isManagerMessage_Msg() {} + +func (*ManagerMessage_NetworkSettings) isManagerMessage_Msg() {} + +func (*ManagerMessage_Start) isManagerMessage_Msg() {} + +func (*ManagerMessage_Stop) isManagerMessage_Msg() {} + +// TunnelMessage is a message from the tunnel (to the manager). +type TunnelMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Rpc *RPC `protobuf:"bytes,1,opt,name=rpc,proto3" json:"rpc,omitempty"` + // Types that are assignable to Msg: + // + // *TunnelMessage_Log + // *TunnelMessage_PeerUpdate + // *TunnelMessage_NetworkSettings + // *TunnelMessage_Start + // *TunnelMessage_Stop + Msg isTunnelMessage_Msg `protobuf_oneof:"msg"` +} + +func (x *TunnelMessage) Reset() { + *x = TunnelMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_vpn_vpn_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TunnelMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TunnelMessage) ProtoMessage() {} + +func (x *TunnelMessage) ProtoReflect() protoreflect.Message { + mi := &file_vpn_vpn_proto_msgTypes[2] + 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 TunnelMessage.ProtoReflect.Descriptor instead. +func (*TunnelMessage) Descriptor() ([]byte, []int) { + return file_vpn_vpn_proto_rawDescGZIP(), []int{2} +} + +func (x *TunnelMessage) GetRpc() *RPC { + if x != nil { + return x.Rpc + } + return nil +} + +func (m *TunnelMessage) GetMsg() isTunnelMessage_Msg { + if m != nil { + return m.Msg + } + return nil +} + +func (x *TunnelMessage) GetLog() *Log { + if x, ok := x.GetMsg().(*TunnelMessage_Log); ok { + return x.Log + } + return nil +} + +func (x *TunnelMessage) GetPeerUpdate() *PeerUpdate { + if x, ok := x.GetMsg().(*TunnelMessage_PeerUpdate); ok { + return x.PeerUpdate + } + return nil +} + +func (x *TunnelMessage) GetNetworkSettings() *NetworkSettingsRequest { + if x, ok := x.GetMsg().(*TunnelMessage_NetworkSettings); ok { + return x.NetworkSettings + } + return nil +} + +func (x *TunnelMessage) GetStart() *StartResponse { + if x, ok := x.GetMsg().(*TunnelMessage_Start); ok { + return x.Start + } + return nil +} + +func (x *TunnelMessage) GetStop() *StopResponse { + if x, ok := x.GetMsg().(*TunnelMessage_Stop); ok { + return x.Stop + } + return nil +} + +type isTunnelMessage_Msg interface { + isTunnelMessage_Msg() +} + +type TunnelMessage_Log struct { + Log *Log `protobuf:"bytes,2,opt,name=log,proto3,oneof"` +} + +type TunnelMessage_PeerUpdate struct { + PeerUpdate *PeerUpdate `protobuf:"bytes,3,opt,name=peer_update,json=peerUpdate,proto3,oneof"` +} + +type TunnelMessage_NetworkSettings struct { + NetworkSettings *NetworkSettingsRequest `protobuf:"bytes,4,opt,name=network_settings,json=networkSettings,proto3,oneof"` +} + +type TunnelMessage_Start struct { + Start *StartResponse `protobuf:"bytes,5,opt,name=start,proto3,oneof"` +} + +type TunnelMessage_Stop struct { + Stop *StopResponse `protobuf:"bytes,6,opt,name=stop,proto3,oneof"` +} + +func (*TunnelMessage_Log) isTunnelMessage_Msg() {} + +func (*TunnelMessage_PeerUpdate) isTunnelMessage_Msg() {} + +func (*TunnelMessage_NetworkSettings) isTunnelMessage_Msg() {} + +func (*TunnelMessage_Start) isTunnelMessage_Msg() {} + +func (*TunnelMessage_Stop) isTunnelMessage_Msg() {} + +// Log is a log message generated by the tunnel. The manager should log it to the system log. It is +// one-way tunnel -> manager with no response. +type Log struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Level Log_Level `protobuf:"varint,1,opt,name=level,proto3,enum=vpn.Log_Level" json:"level,omitempty"` + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` + LoggerNames []string `protobuf:"bytes,3,rep,name=logger_names,json=loggerNames,proto3" json:"logger_names,omitempty"` + Fields []*Log_Field `protobuf:"bytes,4,rep,name=fields,proto3" json:"fields,omitempty"` +} + +func (x *Log) Reset() { + *x = Log{} + if protoimpl.UnsafeEnabled { + mi := &file_vpn_vpn_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Log) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Log) ProtoMessage() {} + +func (x *Log) ProtoReflect() protoreflect.Message { + mi := &file_vpn_vpn_proto_msgTypes[3] + 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 Log.ProtoReflect.Descriptor instead. +func (*Log) Descriptor() ([]byte, []int) { + return file_vpn_vpn_proto_rawDescGZIP(), []int{3} +} + +func (x *Log) GetLevel() Log_Level { + if x != nil { + return x.Level + } + return Log_DEBUG +} + +func (x *Log) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +func (x *Log) GetLoggerNames() []string { + if x != nil { + return x.LoggerNames + } + return nil +} + +func (x *Log) GetFields() []*Log_Field { + if x != nil { + return x.Fields + } + return nil +} + +// GetPeerUpdate asks for a PeerUpdate with a full set of data. +type GetPeerUpdate struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *GetPeerUpdate) Reset() { + *x = GetPeerUpdate{} + if protoimpl.UnsafeEnabled { + mi := &file_vpn_vpn_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetPeerUpdate) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetPeerUpdate) ProtoMessage() {} + +func (x *GetPeerUpdate) ProtoReflect() protoreflect.Message { + mi := &file_vpn_vpn_proto_msgTypes[4] + 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 GetPeerUpdate.ProtoReflect.Descriptor instead. +func (*GetPeerUpdate) Descriptor() ([]byte, []int) { + return file_vpn_vpn_proto_rawDescGZIP(), []int{4} +} + +// PeerUpdate is an update about workspaces and agents connected via the tunnel. It is generated in +// response to GetPeerUpdate (which dumps the full set). It is also generated on any changes (not in +// response to any request). +type PeerUpdate struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + UpsertedWorkspaces []*Workspace `protobuf:"bytes,1,rep,name=upserted_workspaces,json=upsertedWorkspaces,proto3" json:"upserted_workspaces,omitempty"` + UpsertedAgents []*Agent `protobuf:"bytes,2,rep,name=upserted_agents,json=upsertedAgents,proto3" json:"upserted_agents,omitempty"` + DeletedWorkspaces []*Workspace `protobuf:"bytes,3,rep,name=deleted_workspaces,json=deletedWorkspaces,proto3" json:"deleted_workspaces,omitempty"` + DeletedAgents []*Agent `protobuf:"bytes,4,rep,name=deleted_agents,json=deletedAgents,proto3" json:"deleted_agents,omitempty"` +} + +func (x *PeerUpdate) Reset() { + *x = PeerUpdate{} + if protoimpl.UnsafeEnabled { + mi := &file_vpn_vpn_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PeerUpdate) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PeerUpdate) ProtoMessage() {} + +func (x *PeerUpdate) ProtoReflect() protoreflect.Message { + mi := &file_vpn_vpn_proto_msgTypes[5] + 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 PeerUpdate.ProtoReflect.Descriptor instead. +func (*PeerUpdate) Descriptor() ([]byte, []int) { + return file_vpn_vpn_proto_rawDescGZIP(), []int{5} +} + +func (x *PeerUpdate) GetUpsertedWorkspaces() []*Workspace { + if x != nil { + return x.UpsertedWorkspaces + } + return nil +} + +func (x *PeerUpdate) GetUpsertedAgents() []*Agent { + if x != nil { + return x.UpsertedAgents + } + return nil +} + +func (x *PeerUpdate) GetDeletedWorkspaces() []*Workspace { + if x != nil { + return x.DeletedWorkspaces + } + return nil +} + +func (x *PeerUpdate) GetDeletedAgents() []*Agent { + if x != nil { + return x.DeletedAgents + } + return nil +} + +type Workspace struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id []byte `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // UUID + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Status Workspace_Status `protobuf:"varint,3,opt,name=status,proto3,enum=vpn.Workspace_Status" json:"status,omitempty"` +} + +func (x *Workspace) Reset() { + *x = Workspace{} + if protoimpl.UnsafeEnabled { + mi := &file_vpn_vpn_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Workspace) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Workspace) ProtoMessage() {} + +func (x *Workspace) ProtoReflect() protoreflect.Message { + mi := &file_vpn_vpn_proto_msgTypes[6] + 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 Workspace.ProtoReflect.Descriptor instead. +func (*Workspace) Descriptor() ([]byte, []int) { + return file_vpn_vpn_proto_rawDescGZIP(), []int{6} +} + +func (x *Workspace) GetId() []byte { + if x != nil { + return x.Id + } + return nil +} + +func (x *Workspace) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Workspace) GetStatus() Workspace_Status { + if x != nil { + return x.Status + } + return Workspace_UNKNOWN +} + +type Agent struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id []byte `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // UUID + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + WorkspaceId []byte `protobuf:"bytes,3,opt,name=workspace_id,json=workspaceId,proto3" json:"workspace_id,omitempty"` // UUID + Fqdn string `protobuf:"bytes,4,opt,name=fqdn,proto3" json:"fqdn,omitempty"` + IpAddrs []string `protobuf:"bytes,5,rep,name=ip_addrs,json=ipAddrs,proto3" json:"ip_addrs,omitempty"` + // 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"` +} + +func (x *Agent) Reset() { + *x = Agent{} + if protoimpl.UnsafeEnabled { + mi := &file_vpn_vpn_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Agent) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Agent) ProtoMessage() {} + +func (x *Agent) ProtoReflect() protoreflect.Message { + mi := &file_vpn_vpn_proto_msgTypes[7] + 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 Agent.ProtoReflect.Descriptor instead. +func (*Agent) Descriptor() ([]byte, []int) { + return file_vpn_vpn_proto_rawDescGZIP(), []int{7} +} + +func (x *Agent) GetId() []byte { + if x != nil { + return x.Id + } + return nil +} + +func (x *Agent) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Agent) GetWorkspaceId() []byte { + if x != nil { + return x.WorkspaceId + } + return nil +} + +func (x *Agent) GetFqdn() string { + if x != nil { + return x.Fqdn + } + return "" +} + +func (x *Agent) GetIpAddrs() []string { + if x != nil { + return x.IpAddrs + } + return nil +} + +func (x *Agent) GetLastHandshake() *timestamppb.Timestamp { + if x != nil { + return x.LastHandshake + } + return nil +} + +// NetworkSettingsRequest is based on +// https://developer.apple.com/documentation/networkextension/nepackettunnelnetworksettings for +// macOS. It is a request/response message with response NetworkSettingsResponse +type NetworkSettingsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TunnelOverheadBytes uint32 `protobuf:"varint,1,opt,name=tunnel_overhead_bytes,json=tunnelOverheadBytes,proto3" json:"tunnel_overhead_bytes,omitempty"` + Mtu uint32 `protobuf:"varint,2,opt,name=mtu,proto3" json:"mtu,omitempty"` + DnsSettings *NetworkSettingsRequest_DNSSettings `protobuf:"bytes,3,opt,name=dns_settings,json=dnsSettings,proto3" json:"dns_settings,omitempty"` + TunnelRemoteAddress string `protobuf:"bytes,4,opt,name=tunnel_remote_address,json=tunnelRemoteAddress,proto3" json:"tunnel_remote_address,omitempty"` + Ipv4Settings *NetworkSettingsRequest_IPv4Settings `protobuf:"bytes,5,opt,name=ipv4_settings,json=ipv4Settings,proto3" json:"ipv4_settings,omitempty"` + Ipv6Settings *NetworkSettingsRequest_IPv6Settings `protobuf:"bytes,6,opt,name=ipv6_settings,json=ipv6Settings,proto3" json:"ipv6_settings,omitempty"` +} + +func (x *NetworkSettingsRequest) Reset() { + *x = NetworkSettingsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_vpn_vpn_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NetworkSettingsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NetworkSettingsRequest) ProtoMessage() {} + +func (x *NetworkSettingsRequest) ProtoReflect() protoreflect.Message { + mi := &file_vpn_vpn_proto_msgTypes[8] + 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 NetworkSettingsRequest.ProtoReflect.Descriptor instead. +func (*NetworkSettingsRequest) Descriptor() ([]byte, []int) { + return file_vpn_vpn_proto_rawDescGZIP(), []int{8} +} + +func (x *NetworkSettingsRequest) GetTunnelOverheadBytes() uint32 { + if x != nil { + return x.TunnelOverheadBytes + } + return 0 +} + +func (x *NetworkSettingsRequest) GetMtu() uint32 { + if x != nil { + return x.Mtu + } + return 0 +} + +func (x *NetworkSettingsRequest) GetDnsSettings() *NetworkSettingsRequest_DNSSettings { + if x != nil { + return x.DnsSettings + } + return nil +} + +func (x *NetworkSettingsRequest) GetTunnelRemoteAddress() string { + if x != nil { + return x.TunnelRemoteAddress + } + return "" +} + +func (x *NetworkSettingsRequest) GetIpv4Settings() *NetworkSettingsRequest_IPv4Settings { + if x != nil { + return x.Ipv4Settings + } + return nil +} + +func (x *NetworkSettingsRequest) GetIpv6Settings() *NetworkSettingsRequest_IPv6Settings { + if x != nil { + return x.Ipv6Settings + } + return nil +} + +// NetworkSettingsResponse is the response from the manager to the tunnel for a +// NetworkSettingsRequest +type NetworkSettingsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + ErrorMessage string `protobuf:"bytes,2,opt,name=error_message,json=errorMessage,proto3" json:"error_message,omitempty"` +} + +func (x *NetworkSettingsResponse) Reset() { + *x = NetworkSettingsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_vpn_vpn_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NetworkSettingsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NetworkSettingsResponse) ProtoMessage() {} + +func (x *NetworkSettingsResponse) ProtoReflect() protoreflect.Message { + mi := &file_vpn_vpn_proto_msgTypes[9] + 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 NetworkSettingsResponse.ProtoReflect.Descriptor instead. +func (*NetworkSettingsResponse) Descriptor() ([]byte, []int) { + return file_vpn_vpn_proto_rawDescGZIP(), []int{9} +} + +func (x *NetworkSettingsResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +func (x *NetworkSettingsResponse) GetErrorMessage() string { + if x != nil { + return x.ErrorMessage + } + return "" +} + +// StartRequest is a request from the manager to start the tunnel. The tunnel replies with a +// StartResponse. +type StartRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TunnelFileDescriptor int32 `protobuf:"varint,1,opt,name=tunnel_file_descriptor,json=tunnelFileDescriptor,proto3" json:"tunnel_file_descriptor,omitempty"` + CoderUrl string `protobuf:"bytes,2,opt,name=coder_url,json=coderUrl,proto3" json:"coder_url,omitempty"` + ApiToken string `protobuf:"bytes,3,opt,name=api_token,json=apiToken,proto3" json:"api_token,omitempty"` +} + +func (x *StartRequest) Reset() { + *x = StartRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_vpn_vpn_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StartRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StartRequest) ProtoMessage() {} + +func (x *StartRequest) 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 StartRequest.ProtoReflect.Descriptor instead. +func (*StartRequest) Descriptor() ([]byte, []int) { + return file_vpn_vpn_proto_rawDescGZIP(), []int{10} +} + +func (x *StartRequest) GetTunnelFileDescriptor() int32 { + if x != nil { + return x.TunnelFileDescriptor + } + return 0 +} + +func (x *StartRequest) GetCoderUrl() string { + if x != nil { + return x.CoderUrl + } + return "" +} + +func (x *StartRequest) GetApiToken() string { + if x != nil { + return x.ApiToken + } + return "" +} + +type StartResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + ErrorMessage string `protobuf:"bytes,2,opt,name=error_message,json=errorMessage,proto3" json:"error_message,omitempty"` +} + +func (x *StartResponse) Reset() { + *x = StartResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_vpn_vpn_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StartResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StartResponse) ProtoMessage() {} + +func (x *StartResponse) ProtoReflect() protoreflect.Message { + mi := &file_vpn_vpn_proto_msgTypes[11] + 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 StartResponse.ProtoReflect.Descriptor instead. +func (*StartResponse) Descriptor() ([]byte, []int) { + return file_vpn_vpn_proto_rawDescGZIP(), []int{11} +} + +func (x *StartResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +func (x *StartResponse) GetErrorMessage() string { + if x != nil { + return x.ErrorMessage + } + return "" +} + +// StopRequest is a request from the manager to stop the tunnel. The tunnel replies with a +// StopResponse. +type StopRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *StopRequest) Reset() { + *x = StopRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_vpn_vpn_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StopRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StopRequest) ProtoMessage() {} + +func (x *StopRequest) ProtoReflect() protoreflect.Message { + mi := &file_vpn_vpn_proto_msgTypes[12] + 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 StopRequest.ProtoReflect.Descriptor instead. +func (*StopRequest) Descriptor() ([]byte, []int) { + return file_vpn_vpn_proto_rawDescGZIP(), []int{12} +} + +// StopResponse is a response to stopping the tunnel. After sending this response, the tunnel closes +// its side of the bidirectional stream for writing. +type StopResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + ErrorMessage string `protobuf:"bytes,2,opt,name=error_message,json=errorMessage,proto3" json:"error_message,omitempty"` +} + +func (x *StopResponse) Reset() { + *x = StopResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_vpn_vpn_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StopResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StopResponse) ProtoMessage() {} + +func (x *StopResponse) ProtoReflect() protoreflect.Message { + mi := &file_vpn_vpn_proto_msgTypes[13] + 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 StopResponse.ProtoReflect.Descriptor instead. +func (*StopResponse) Descriptor() ([]byte, []int) { + return file_vpn_vpn_proto_rawDescGZIP(), []int{13} +} + +func (x *StopResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +func (x *StopResponse) GetErrorMessage() string { + if x != nil { + return x.ErrorMessage + } + return "" +} + +type Log_Field struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *Log_Field) Reset() { + *x = Log_Field{} + if protoimpl.UnsafeEnabled { + mi := &file_vpn_vpn_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Log_Field) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Log_Field) ProtoMessage() {} + +func (x *Log_Field) ProtoReflect() protoreflect.Message { + mi := &file_vpn_vpn_proto_msgTypes[14] + 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 Log_Field.ProtoReflect.Descriptor instead. +func (*Log_Field) Descriptor() ([]byte, []int) { + return file_vpn_vpn_proto_rawDescGZIP(), []int{3, 0} +} + +func (x *Log_Field) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Log_Field) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +type NetworkSettingsRequest_DNSSettings struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Servers []string `protobuf:"bytes,1,rep,name=servers,proto3" json:"servers,omitempty"` + SearchDomains []string `protobuf:"bytes,2,rep,name=search_domains,json=searchDomains,proto3" json:"search_domains,omitempty"` + // domain_name is the primary domain name of the tunnel + DomainName string `protobuf:"bytes,3,opt,name=domain_name,json=domainName,proto3" json:"domain_name,omitempty"` + MatchDomains []string `protobuf:"bytes,4,rep,name=match_domains,json=matchDomains,proto3" json:"match_domains,omitempty"` + // match_domains_no_search specifies if the domains in the matchDomains list should not be + // appended to the resolver’s list of search domains. + MatchDomainsNoSearch bool `protobuf:"varint,5,opt,name=match_domains_no_search,json=matchDomainsNoSearch,proto3" json:"match_domains_no_search,omitempty"` +} + +func (x *NetworkSettingsRequest_DNSSettings) Reset() { + *x = NetworkSettingsRequest_DNSSettings{} + if protoimpl.UnsafeEnabled { + mi := &file_vpn_vpn_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NetworkSettingsRequest_DNSSettings) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NetworkSettingsRequest_DNSSettings) ProtoMessage() {} + +func (x *NetworkSettingsRequest_DNSSettings) ProtoReflect() protoreflect.Message { + mi := &file_vpn_vpn_proto_msgTypes[15] + 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 NetworkSettingsRequest_DNSSettings.ProtoReflect.Descriptor instead. +func (*NetworkSettingsRequest_DNSSettings) Descriptor() ([]byte, []int) { + return file_vpn_vpn_proto_rawDescGZIP(), []int{8, 0} +} + +func (x *NetworkSettingsRequest_DNSSettings) GetServers() []string { + if x != nil { + return x.Servers + } + return nil +} + +func (x *NetworkSettingsRequest_DNSSettings) GetSearchDomains() []string { + if x != nil { + return x.SearchDomains + } + return nil +} + +func (x *NetworkSettingsRequest_DNSSettings) GetDomainName() string { + if x != nil { + return x.DomainName + } + return "" +} + +func (x *NetworkSettingsRequest_DNSSettings) GetMatchDomains() []string { + if x != nil { + return x.MatchDomains + } + return nil +} + +func (x *NetworkSettingsRequest_DNSSettings) GetMatchDomainsNoSearch() bool { + if x != nil { + return x.MatchDomainsNoSearch + } + return false +} + +type NetworkSettingsRequest_IPv4Settings struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Addrs []string `protobuf:"bytes,1,rep,name=addrs,proto3" json:"addrs,omitempty"` + SubnetMasks []string `protobuf:"bytes,2,rep,name=subnet_masks,json=subnetMasks,proto3" json:"subnet_masks,omitempty"` + // router is the next-hop router in dotted-decimal format + Router string `protobuf:"bytes,3,opt,name=router,proto3" json:"router,omitempty"` + IncludedRoutes []*NetworkSettingsRequest_IPv4Settings_IPv4Route `protobuf:"bytes,4,rep,name=included_routes,json=includedRoutes,proto3" json:"included_routes,omitempty"` + ExcludedRoutes []*NetworkSettingsRequest_IPv4Settings_IPv4Route `protobuf:"bytes,5,rep,name=excluded_routes,json=excludedRoutes,proto3" json:"excluded_routes,omitempty"` +} + +func (x *NetworkSettingsRequest_IPv4Settings) Reset() { + *x = NetworkSettingsRequest_IPv4Settings{} + if protoimpl.UnsafeEnabled { + mi := &file_vpn_vpn_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NetworkSettingsRequest_IPv4Settings) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NetworkSettingsRequest_IPv4Settings) ProtoMessage() {} + +func (x *NetworkSettingsRequest_IPv4Settings) ProtoReflect() protoreflect.Message { + mi := &file_vpn_vpn_proto_msgTypes[16] + 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 NetworkSettingsRequest_IPv4Settings.ProtoReflect.Descriptor instead. +func (*NetworkSettingsRequest_IPv4Settings) Descriptor() ([]byte, []int) { + return file_vpn_vpn_proto_rawDescGZIP(), []int{8, 1} +} + +func (x *NetworkSettingsRequest_IPv4Settings) GetAddrs() []string { + if x != nil { + return x.Addrs + } + return nil +} + +func (x *NetworkSettingsRequest_IPv4Settings) GetSubnetMasks() []string { + if x != nil { + return x.SubnetMasks + } + return nil +} + +func (x *NetworkSettingsRequest_IPv4Settings) GetRouter() string { + if x != nil { + return x.Router + } + return "" +} + +func (x *NetworkSettingsRequest_IPv4Settings) GetIncludedRoutes() []*NetworkSettingsRequest_IPv4Settings_IPv4Route { + if x != nil { + return x.IncludedRoutes + } + return nil +} + +func (x *NetworkSettingsRequest_IPv4Settings) GetExcludedRoutes() []*NetworkSettingsRequest_IPv4Settings_IPv4Route { + if x != nil { + return x.ExcludedRoutes + } + return nil +} + +type NetworkSettingsRequest_IPv6Settings struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Addrs []string `protobuf:"bytes,1,rep,name=addrs,proto3" json:"addrs,omitempty"` + PrefixLengths []uint32 `protobuf:"varint,2,rep,packed,name=prefix_lengths,json=prefixLengths,proto3" json:"prefix_lengths,omitempty"` + IncludedRoutes []*NetworkSettingsRequest_IPv6Settings_IPv6Route `protobuf:"bytes,3,rep,name=included_routes,json=includedRoutes,proto3" json:"included_routes,omitempty"` + ExcludedRoutes []*NetworkSettingsRequest_IPv6Settings_IPv6Route `protobuf:"bytes,4,rep,name=excluded_routes,json=excludedRoutes,proto3" json:"excluded_routes,omitempty"` +} + +func (x *NetworkSettingsRequest_IPv6Settings) Reset() { + *x = NetworkSettingsRequest_IPv6Settings{} + if protoimpl.UnsafeEnabled { + mi := &file_vpn_vpn_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NetworkSettingsRequest_IPv6Settings) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NetworkSettingsRequest_IPv6Settings) ProtoMessage() {} + +func (x *NetworkSettingsRequest_IPv6Settings) ProtoReflect() protoreflect.Message { + mi := &file_vpn_vpn_proto_msgTypes[17] + 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 NetworkSettingsRequest_IPv6Settings.ProtoReflect.Descriptor instead. +func (*NetworkSettingsRequest_IPv6Settings) Descriptor() ([]byte, []int) { + return file_vpn_vpn_proto_rawDescGZIP(), []int{8, 2} +} + +func (x *NetworkSettingsRequest_IPv6Settings) GetAddrs() []string { + if x != nil { + return x.Addrs + } + return nil +} + +func (x *NetworkSettingsRequest_IPv6Settings) GetPrefixLengths() []uint32 { + if x != nil { + return x.PrefixLengths + } + return nil +} + +func (x *NetworkSettingsRequest_IPv6Settings) GetIncludedRoutes() []*NetworkSettingsRequest_IPv6Settings_IPv6Route { + if x != nil { + return x.IncludedRoutes + } + return nil +} + +func (x *NetworkSettingsRequest_IPv6Settings) GetExcludedRoutes() []*NetworkSettingsRequest_IPv6Settings_IPv6Route { + if x != nil { + return x.ExcludedRoutes + } + return nil +} + +type NetworkSettingsRequest_IPv4Settings_IPv4Route struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Destination string `protobuf:"bytes,1,opt,name=destination,proto3" json:"destination,omitempty"` + Mask string `protobuf:"bytes,2,opt,name=mask,proto3" json:"mask,omitempty"` + // router is the next-hop router in dotted-decimal format + Router string `protobuf:"bytes,3,opt,name=router,proto3" json:"router,omitempty"` +} + +func (x *NetworkSettingsRequest_IPv4Settings_IPv4Route) Reset() { + *x = NetworkSettingsRequest_IPv4Settings_IPv4Route{} + if protoimpl.UnsafeEnabled { + mi := &file_vpn_vpn_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NetworkSettingsRequest_IPv4Settings_IPv4Route) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NetworkSettingsRequest_IPv4Settings_IPv4Route) ProtoMessage() {} + +func (x *NetworkSettingsRequest_IPv4Settings_IPv4Route) ProtoReflect() protoreflect.Message { + mi := &file_vpn_vpn_proto_msgTypes[18] + 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 NetworkSettingsRequest_IPv4Settings_IPv4Route.ProtoReflect.Descriptor instead. +func (*NetworkSettingsRequest_IPv4Settings_IPv4Route) Descriptor() ([]byte, []int) { + return file_vpn_vpn_proto_rawDescGZIP(), []int{8, 1, 0} +} + +func (x *NetworkSettingsRequest_IPv4Settings_IPv4Route) GetDestination() string { + if x != nil { + return x.Destination + } + return "" +} + +func (x *NetworkSettingsRequest_IPv4Settings_IPv4Route) GetMask() string { + if x != nil { + return x.Mask + } + return "" +} + +func (x *NetworkSettingsRequest_IPv4Settings_IPv4Route) GetRouter() string { + if x != nil { + return x.Router + } + return "" +} + +type NetworkSettingsRequest_IPv6Settings_IPv6Route struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Destination string `protobuf:"bytes,1,opt,name=destination,proto3" json:"destination,omitempty"` + PrefixLength uint32 `protobuf:"varint,2,opt,name=prefix_length,json=prefixLength,proto3" json:"prefix_length,omitempty"` + // router is the address of the next-hop + Router string `protobuf:"bytes,3,opt,name=router,proto3" json:"router,omitempty"` +} + +func (x *NetworkSettingsRequest_IPv6Settings_IPv6Route) Reset() { + *x = NetworkSettingsRequest_IPv6Settings_IPv6Route{} + if protoimpl.UnsafeEnabled { + mi := &file_vpn_vpn_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NetworkSettingsRequest_IPv6Settings_IPv6Route) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NetworkSettingsRequest_IPv6Settings_IPv6Route) ProtoMessage() {} + +func (x *NetworkSettingsRequest_IPv6Settings_IPv6Route) ProtoReflect() protoreflect.Message { + mi := &file_vpn_vpn_proto_msgTypes[19] + 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 NetworkSettingsRequest_IPv6Settings_IPv6Route.ProtoReflect.Descriptor instead. +func (*NetworkSettingsRequest_IPv6Settings_IPv6Route) Descriptor() ([]byte, []int) { + return file_vpn_vpn_proto_rawDescGZIP(), []int{8, 2, 0} +} + +func (x *NetworkSettingsRequest_IPv6Settings_IPv6Route) GetDestination() string { + if x != nil { + return x.Destination + } + return "" +} + +func (x *NetworkSettingsRequest_IPv6Settings_IPv6Route) GetPrefixLength() uint32 { + if x != nil { + return x.PrefixLength + } + return 0 +} + +func (x *NetworkSettingsRequest_IPv6Settings_IPv6Route) GetRouter() string { + if x != nil { + return x.Router + } + return "" +} + +var File_vpn_vpn_proto protoreflect.FileDescriptor + +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, 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, + 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x54, 0x6f, 0x22, 0x8f, 0x02, 0x0a, 0x0e, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1a, 0x0a, 0x03, 0x72, 0x70, 0x63, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x76, 0x70, 0x6e, 0x2e, 0x52, 0x50, 0x43, 0x52, 0x03, + 0x72, 0x70, 0x63, 0x12, 0x3c, 0x0a, 0x0f, 0x67, 0x65, 0x74, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x5f, + 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x76, + 0x70, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x65, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x48, 0x00, 0x52, 0x0d, 0x67, 0x65, 0x74, 0x50, 0x65, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x12, 0x49, 0x0a, 0x10, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x73, 0x65, 0x74, + 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x76, 0x70, + 0x6e, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x0f, 0x6e, 0x65, 0x74, + 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x29, 0x0a, 0x05, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x76, 0x70, + 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, + 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x26, 0x0a, 0x04, 0x73, 0x74, 0x6f, 0x70, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x76, 0x70, 0x6e, 0x2e, 0x53, 0x74, 0x6f, 0x70, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x04, 0x73, 0x74, 0x6f, 0x70, 0x42, + 0x05, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x22, 0xa3, 0x02, 0x0a, 0x0d, 0x54, 0x75, 0x6e, 0x6e, 0x65, + 0x6c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1a, 0x0a, 0x03, 0x72, 0x70, 0x63, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x76, 0x70, 0x6e, 0x2e, 0x52, 0x50, 0x43, 0x52, + 0x03, 0x72, 0x70, 0x63, 0x12, 0x1c, 0x0a, 0x03, 0x6c, 0x6f, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x08, 0x2e, 0x76, 0x70, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x48, 0x00, 0x52, 0x03, 0x6c, + 0x6f, 0x67, 0x12, 0x32, 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, 0x48, 0x00, 0x52, 0x0a, 0x70, 0x65, 0x65, 0x72, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x48, 0x0a, 0x10, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, + 0x6b, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1b, 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, 0x48, 0x00, 0x52, + 0x0f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, + 0x12, 0x2a, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x12, 0x2e, 0x76, 0x70, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x27, 0x0a, 0x04, + 0x73, 0x74, 0x6f, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x76, 0x70, 0x6e, + 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, + 0x04, 0x73, 0x74, 0x6f, 0x70, 0x42, 0x05, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x22, 0x8f, 0x02, 0x0a, + 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x24, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x76, 0x70, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x2e, 0x4c, 0x65, + 0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x72, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x6c, 0x6f, 0x67, 0x67, + 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, + 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x76, 0x70, 0x6e, 0x2e, 0x4c, 0x6f, + 0x67, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x1a, + 0x31, 0x0a, 0x05, 0x46, 0x69, 0x65, 0x6c, 0x64, 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, 0x4a, 0x0a, 0x05, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, 0x0a, 0x05, 0x44, + 0x45, 0x42, 0x55, 0x47, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x01, + 0x12, 0x08, 0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, + 0x52, 0x4f, 0x52, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x52, 0x49, 0x54, 0x49, 0x43, 0x41, + 0x4c, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x46, 0x41, 0x54, 0x41, 0x4c, 0x10, 0x05, 0x22, 0x0f, + 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x50, 0x65, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x22, + 0xf4, 0x01, 0x0a, 0x0a, 0x50, 0x65, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x3f, + 0x0a, 0x13, 0x75, 0x70, 0x73, 0x65, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x76, 0x70, + 0x6e, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x52, 0x12, 0x75, 0x70, 0x73, + 0x65, 0x72, 0x74, 0x65, 0x64, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, + 0x33, 0x0a, 0x0f, 0x75, 0x70, 0x73, 0x65, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x67, 0x65, 0x6e, + 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x76, 0x70, 0x6e, 0x2e, 0x41, + 0x67, 0x65, 0x6e, 0x74, 0x52, 0x0e, 0x75, 0x70, 0x73, 0x65, 0x72, 0x74, 0x65, 0x64, 0x41, 0x67, + 0x65, 0x6e, 0x74, 0x73, 0x12, 0x3d, 0x0a, 0x12, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x5f, + 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x0e, 0x2e, 0x76, 0x70, 0x6e, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x52, 0x11, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x73, 0x12, 0x31, 0x0a, 0x0e, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x61, + 0x67, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x76, 0x70, + 0x6e, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x0d, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, + 0x41, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x22, 0xfd, 0x01, 0x0a, 0x09, 0x57, 0x6f, 0x72, 0x6b, 0x73, + 0x70, 0x61, 0x63, 0x65, 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, 0x2d, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x76, 0x70, 0x6e, 0x2e, 0x57, + 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x9c, 0x01, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, + 0x0b, 0x0a, 0x07, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, + 0x53, 0x54, 0x41, 0x52, 0x54, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x55, + 0x4e, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x54, 0x4f, 0x50, 0x50, + 0x49, 0x4e, 0x47, 0x10, 0x04, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x54, 0x4f, 0x50, 0x50, 0x45, 0x44, + 0x10, 0x05, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x41, 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, 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, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x77, 0x6f, 0x72, 0x6b, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x69, + 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x69, + 0x70, 0x41, 0x64, 0x64, 0x72, 0x73, 0x12, 0x41, 0x0a, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x68, + 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 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, 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, 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, 0x7e, 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, 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, 0x42, 0x1f, 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, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, +} + +var ( + file_vpn_vpn_proto_rawDescOnce sync.Once + file_vpn_vpn_proto_rawDescData = file_vpn_vpn_proto_rawDesc +) + +func file_vpn_vpn_proto_rawDescGZIP() []byte { + file_vpn_vpn_proto_rawDescOnce.Do(func() { + file_vpn_vpn_proto_rawDescData = protoimpl.X.CompressGZIP(file_vpn_vpn_proto_rawDescData) + }) + return file_vpn_vpn_proto_rawDescData +} + +var file_vpn_vpn_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_vpn_vpn_proto_msgTypes = make([]protoimpl.MessageInfo, 20) +var file_vpn_vpn_proto_goTypes = []interface{}{ + (Log_Level)(0), // 0: vpn.Log.Level + (Workspace_Status)(0), // 1: vpn.Workspace.Status + (*RPC)(nil), // 2: vpn.RPC + (*ManagerMessage)(nil), // 3: vpn.ManagerMessage + (*TunnelMessage)(nil), // 4: vpn.TunnelMessage + (*Log)(nil), // 5: vpn.Log + (*GetPeerUpdate)(nil), // 6: vpn.GetPeerUpdate + (*PeerUpdate)(nil), // 7: vpn.PeerUpdate + (*Workspace)(nil), // 8: vpn.Workspace + (*Agent)(nil), // 9: vpn.Agent + (*NetworkSettingsRequest)(nil), // 10: vpn.NetworkSettingsRequest + (*NetworkSettingsResponse)(nil), // 11: vpn.NetworkSettingsResponse + (*StartRequest)(nil), // 12: vpn.StartRequest + (*StartResponse)(nil), // 13: vpn.StartResponse + (*StopRequest)(nil), // 14: vpn.StopRequest + (*StopResponse)(nil), // 15: vpn.StopResponse + (*Log_Field)(nil), // 16: vpn.Log.Field + (*NetworkSettingsRequest_DNSSettings)(nil), // 17: vpn.NetworkSettingsRequest.DNSSettings + (*NetworkSettingsRequest_IPv4Settings)(nil), // 18: vpn.NetworkSettingsRequest.IPv4Settings + (*NetworkSettingsRequest_IPv6Settings)(nil), // 19: vpn.NetworkSettingsRequest.IPv6Settings + (*NetworkSettingsRequest_IPv4Settings_IPv4Route)(nil), // 20: vpn.NetworkSettingsRequest.IPv4Settings.IPv4Route + (*NetworkSettingsRequest_IPv6Settings_IPv6Route)(nil), // 21: vpn.NetworkSettingsRequest.IPv6Settings.IPv6Route + (*timestamppb.Timestamp)(nil), // 22: google.protobuf.Timestamp +} +var file_vpn_vpn_proto_depIdxs = []int32{ + 2, // 0: vpn.ManagerMessage.rpc:type_name -> vpn.RPC + 6, // 1: vpn.ManagerMessage.get_peer_update:type_name -> vpn.GetPeerUpdate + 11, // 2: vpn.ManagerMessage.network_settings:type_name -> vpn.NetworkSettingsResponse + 12, // 3: vpn.ManagerMessage.start:type_name -> vpn.StartRequest + 14, // 4: vpn.ManagerMessage.stop:type_name -> vpn.StopRequest + 2, // 5: vpn.TunnelMessage.rpc:type_name -> vpn.RPC + 5, // 6: vpn.TunnelMessage.log:type_name -> vpn.Log + 7, // 7: vpn.TunnelMessage.peer_update:type_name -> vpn.PeerUpdate + 10, // 8: vpn.TunnelMessage.network_settings:type_name -> vpn.NetworkSettingsRequest + 13, // 9: vpn.TunnelMessage.start:type_name -> vpn.StartResponse + 15, // 10: vpn.TunnelMessage.stop:type_name -> vpn.StopResponse + 0, // 11: vpn.Log.level:type_name -> vpn.Log.Level + 16, // 12: vpn.Log.fields:type_name -> vpn.Log.Field + 8, // 13: vpn.PeerUpdate.upserted_workspaces:type_name -> vpn.Workspace + 9, // 14: vpn.PeerUpdate.upserted_agents:type_name -> vpn.Agent + 8, // 15: vpn.PeerUpdate.deleted_workspaces:type_name -> vpn.Workspace + 9, // 16: vpn.PeerUpdate.deleted_agents:type_name -> vpn.Agent + 1, // 17: vpn.Workspace.status:type_name -> vpn.Workspace.Status + 22, // 18: vpn.Agent.last_handshake:type_name -> google.protobuf.Timestamp + 17, // 19: vpn.NetworkSettingsRequest.dns_settings:type_name -> vpn.NetworkSettingsRequest.DNSSettings + 18, // 20: vpn.NetworkSettingsRequest.ipv4_settings:type_name -> vpn.NetworkSettingsRequest.IPv4Settings + 19, // 21: vpn.NetworkSettingsRequest.ipv6_settings:type_name -> vpn.NetworkSettingsRequest.IPv6Settings + 20, // 22: vpn.NetworkSettingsRequest.IPv4Settings.included_routes:type_name -> vpn.NetworkSettingsRequest.IPv4Settings.IPv4Route + 20, // 23: vpn.NetworkSettingsRequest.IPv4Settings.excluded_routes:type_name -> vpn.NetworkSettingsRequest.IPv4Settings.IPv4Route + 21, // 24: vpn.NetworkSettingsRequest.IPv6Settings.included_routes:type_name -> vpn.NetworkSettingsRequest.IPv6Settings.IPv6Route + 21, // 25: vpn.NetworkSettingsRequest.IPv6Settings.excluded_routes:type_name -> vpn.NetworkSettingsRequest.IPv6Settings.IPv6Route + 26, // [26:26] is the sub-list for method output_type + 26, // [26:26] is the sub-list for method input_type + 26, // [26:26] is the sub-list for extension type_name + 26, // [26:26] is the sub-list for extension extendee + 0, // [0:26] is the sub-list for field type_name +} + +func init() { file_vpn_vpn_proto_init() } +func file_vpn_vpn_proto_init() { + if File_vpn_vpn_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_vpn_vpn_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RPC); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vpn_vpn_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ManagerMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vpn_vpn_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TunnelMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vpn_vpn_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Log); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vpn_vpn_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetPeerUpdate); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vpn_vpn_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PeerUpdate); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vpn_vpn_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Workspace); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vpn_vpn_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Agent); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vpn_vpn_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NetworkSettingsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vpn_vpn_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NetworkSettingsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vpn_vpn_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StartRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vpn_vpn_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StartResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vpn_vpn_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StopRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vpn_vpn_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StopResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vpn_vpn_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Log_Field); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vpn_vpn_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NetworkSettingsRequest_DNSSettings); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vpn_vpn_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NetworkSettingsRequest_IPv4Settings); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vpn_vpn_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NetworkSettingsRequest_IPv6Settings); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vpn_vpn_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NetworkSettingsRequest_IPv4Settings_IPv4Route); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_vpn_vpn_proto_msgTypes[19].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[1].OneofWrappers = []interface{}{ + (*ManagerMessage_GetPeerUpdate)(nil), + (*ManagerMessage_NetworkSettings)(nil), + (*ManagerMessage_Start)(nil), + (*ManagerMessage_Stop)(nil), + } + file_vpn_vpn_proto_msgTypes[2].OneofWrappers = []interface{}{ + (*TunnelMessage_Log)(nil), + (*TunnelMessage_PeerUpdate)(nil), + (*TunnelMessage_NetworkSettings)(nil), + (*TunnelMessage_Start)(nil), + (*TunnelMessage_Stop)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_vpn_vpn_proto_rawDesc, + NumEnums: 2, + NumMessages: 20, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_vpn_vpn_proto_goTypes, + DependencyIndexes: file_vpn_vpn_proto_depIdxs, + EnumInfos: file_vpn_vpn_proto_enumTypes, + MessageInfos: file_vpn_vpn_proto_msgTypes, + }.Build() + File_vpn_vpn_proto = out.File + file_vpn_vpn_proto_rawDesc = nil + file_vpn_vpn_proto_goTypes = nil + file_vpn_vpn_proto_depIdxs = nil +} diff --git a/vpn/vpn.proto b/vpn/vpn.proto new file mode 100644 index 0000000000000..3ed47cf968a22 --- /dev/null +++ b/vpn/vpn.proto @@ -0,0 +1,197 @@ +syntax = "proto3"; +option go_package = "github.com/coder/coder/v2/vpn"; + +import "google/protobuf/timestamp.proto"; + +package vpn; + +// The CoderVPN protocol operates over a bidirectional stream between a "manager" and a "tunnel." +// The manager is part of the Coder Desktop application and written in OS native code. It handles +// configuring the VPN and displaying status to the end user. The tunnel is written in Go and +// handles operating the actual tunnel, including reading and writing packets, & communicating with +// the Coder server control plane. + + +// RPC allows a very simple unary request/response RPC mechanism. The requester generates a unique +// msg_id which it sets on the request, the responder sets response_to that msg_id on the response +// message +message RPC { + uint64 msg_id = 1; + uint64 response_to = 2; +} + +// ManagerMessage is a message from the manager (to the tunnel). +message ManagerMessage { + RPC rpc = 1; + oneof msg { + GetPeerUpdate get_peer_update = 2; + NetworkSettingsResponse network_settings = 3; + StartRequest start = 4; + StopRequest stop = 5; + } +} + +// TunnelMessage is a message from the tunnel (to the manager). +message TunnelMessage { + RPC rpc = 1; + oneof msg { + Log log = 2; + PeerUpdate peer_update = 3; + NetworkSettingsRequest network_settings = 4; + StartResponse start = 5; + StopResponse stop = 6; + } +} + +// Log is a log message generated by the tunnel. The manager should log it to the system log. It is +// one-way tunnel -> manager with no response. +message Log { + enum Level { + // these are designed to match slog levels + DEBUG = 0; + INFO = 1; + WARN = 2; + ERROR = 3; + CRITICAL = 4; + FATAL = 5; + } + Level level = 1; + + string message = 2; + repeated string logger_names = 3; + + message Field { + string name = 1; + string value = 2; + } + repeated Field fields = 4; +} + +// GetPeerUpdate asks for a PeerUpdate with a full set of data. +message GetPeerUpdate {} + +// PeerUpdate is an update about workspaces and agents connected via the tunnel. It is generated in +// response to GetPeerUpdate (which dumps the full set). It is also generated on any changes (not in +// response to any request). +message PeerUpdate { + repeated Workspace upserted_workspaces = 1; + repeated Agent upserted_agents = 2; + repeated Workspace deleted_workspaces = 3; + repeated Agent deleted_agents = 4; +} + +message Workspace { + bytes id = 1; // UUID + string name = 2; + + enum Status { + UNKNOWN = 0; + PENDING = 1; + STARTING = 2; + RUNNING = 3; + STOPPING = 4; + STOPPED = 5; + FAILED = 6; + CANCELING = 7; + CANCELED = 8; + DELETING = 9; + DELETED = 10; + } + Status status = 3; +} + +message Agent { + bytes id = 1; // UUID + string name = 2; + bytes workspace_id = 3; // UUID + string fqdn = 4; + repeated string ip_addrs = 5; + // last_handshake is the primary indicator of whether we are connected to a peer. Zero value or + // anything longer than 5 minutes ago means there is a problem. + google.protobuf.Timestamp last_handshake = 6; +} + +// NetworkSettingsRequest is based on +// https://developer.apple.com/documentation/networkextension/nepackettunnelnetworksettings for +// macOS. It is a request/response message with response NetworkSettingsResponse +message NetworkSettingsRequest { + uint32 tunnel_overhead_bytes = 1; + uint32 mtu = 2; + + message DNSSettings { + repeated string servers = 1; + repeated string search_domains = 2; + // domain_name is the primary domain name of the tunnel + string domain_name = 3; + repeated string match_domains = 4; + // match_domains_no_search specifies if the domains in the matchDomains list should not be + // appended to the resolver’s list of search domains. + bool match_domains_no_search = 5; + } + DNSSettings dns_settings = 3; + + string tunnel_remote_address = 4; + + message IPv4Settings { + repeated string addrs = 1; + repeated string subnet_masks = 2; + // router is the next-hop router in dotted-decimal format + string router = 3; + + message IPv4Route { + string destination = 1; + string mask = 2; + // router is the next-hop router in dotted-decimal format + string router = 3; + } + repeated IPv4Route included_routes = 4; + repeated IPv4Route excluded_routes = 5; + } + IPv4Settings ipv4_settings = 5; + + message IPv6Settings { + repeated string addrs = 1; + repeated uint32 prefix_lengths = 2; + + message IPv6Route { + string destination = 1; + uint32 prefix_length = 2; + // router is the address of the next-hop + string router = 3; + } + repeated IPv6Route included_routes = 3; + repeated IPv6Route excluded_routes = 4; + } + IPv6Settings ipv6_settings = 6; +} + +// NetworkSettingsResponse is the response from the manager to the tunnel for a +// NetworkSettingsRequest +message NetworkSettingsResponse { + bool success = 1; + string error_message = 2; +} + +// StartRequest is a request from the manager to start the tunnel. The tunnel replies with a +// StartResponse. +message StartRequest { + int32 tunnel_file_descriptor = 1; + string coder_url = 2; + string api_token = 3; +} + +message StartResponse { + bool success = 1; + string error_message = 2; +} + +// StopRequest is a request from the manager to stop the tunnel. The tunnel replies with a +// StopResponse. +message StopRequest {} + +// StopResponse is a response to stopping the tunnel. After sending this response, the tunnel closes +// its side of the bidirectional stream for writing. +message StopResponse { + bool success = 1; + string error_message = 2; +}