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; +} pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy