mirror of
https://github.com/netbirdio/netbird.git
synced 2025-06-20 17:58:02 +02:00
[client] Feature/lazy connection (#3379)
With the lazy connection feature, the peer will connect to target peers on-demand. The trigger can be any IP traffic. This feature can be enabled with the NB_ENABLE_EXPERIMENTAL_LAZY_CONN environment variable. When the engine receives a network map, it binds a free UDP port for every remote peer, and the system configures WireGuard endpoints for these ports. When traffic appears on a UDP socket, the system removes this listener and starts the peer connection procedure immediately. Key changes Fix slow netbird status -d command Move from engine.go file to conn_mgr.go the peer connection related code Refactor the iface interface usage and moved interface file next to the engine code Add new command line flag and UI option to enable feature The peer.Conn struct is reusable after it has been closed. Change connection states Connection states Idle: The peer is not attempting to establish a connection. This typically means it's in a lazy state or the remote peer is expired. Connecting: The peer is actively trying to establish a connection. This occurs when the peer has entered an active state and is continuously attempting to reach the remote peer. Connected: A successful peer-to-peer connection has been established and communication is active.
This commit is contained in:
parent
4785f23fc4
commit
daa8380df9
@ -26,22 +26,23 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
externalIPMapFlag = "external-ip-map"
|
externalIPMapFlag = "external-ip-map"
|
||||||
dnsResolverAddress = "dns-resolver-address"
|
dnsResolverAddress = "dns-resolver-address"
|
||||||
enableRosenpassFlag = "enable-rosenpass"
|
enableRosenpassFlag = "enable-rosenpass"
|
||||||
rosenpassPermissiveFlag = "rosenpass-permissive"
|
rosenpassPermissiveFlag = "rosenpass-permissive"
|
||||||
preSharedKeyFlag = "preshared-key"
|
preSharedKeyFlag = "preshared-key"
|
||||||
interfaceNameFlag = "interface-name"
|
interfaceNameFlag = "interface-name"
|
||||||
wireguardPortFlag = "wireguard-port"
|
wireguardPortFlag = "wireguard-port"
|
||||||
networkMonitorFlag = "network-monitor"
|
networkMonitorFlag = "network-monitor"
|
||||||
disableAutoConnectFlag = "disable-auto-connect"
|
disableAutoConnectFlag = "disable-auto-connect"
|
||||||
serverSSHAllowedFlag = "allow-server-ssh"
|
serverSSHAllowedFlag = "allow-server-ssh"
|
||||||
extraIFaceBlackListFlag = "extra-iface-blacklist"
|
extraIFaceBlackListFlag = "extra-iface-blacklist"
|
||||||
dnsRouteIntervalFlag = "dns-router-interval"
|
dnsRouteIntervalFlag = "dns-router-interval"
|
||||||
systemInfoFlag = "system-info"
|
systemInfoFlag = "system-info"
|
||||||
blockLANAccessFlag = "block-lan-access"
|
blockLANAccessFlag = "block-lan-access"
|
||||||
uploadBundle = "upload-bundle"
|
enableLazyConnectionFlag = "enable-lazy-connection"
|
||||||
uploadBundleURL = "upload-bundle-url"
|
uploadBundle = "upload-bundle"
|
||||||
|
uploadBundleURL = "upload-bundle-url"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -80,6 +81,7 @@ var (
|
|||||||
blockLANAccess bool
|
blockLANAccess bool
|
||||||
debugUploadBundle bool
|
debugUploadBundle bool
|
||||||
debugUploadBundleURL string
|
debugUploadBundleURL string
|
||||||
|
lazyConnEnabled bool
|
||||||
|
|
||||||
rootCmd = &cobra.Command{
|
rootCmd = &cobra.Command{
|
||||||
Use: "netbird",
|
Use: "netbird",
|
||||||
@ -184,6 +186,7 @@ func init() {
|
|||||||
upCmd.PersistentFlags().BoolVar(&rosenpassPermissive, rosenpassPermissiveFlag, false, "[Experimental] Enable Rosenpass in permissive mode to allow this peer to accept WireGuard connections without requiring Rosenpass functionality from peers that do not have Rosenpass enabled.")
|
upCmd.PersistentFlags().BoolVar(&rosenpassPermissive, rosenpassPermissiveFlag, false, "[Experimental] Enable Rosenpass in permissive mode to allow this peer to accept WireGuard connections without requiring Rosenpass functionality from peers that do not have Rosenpass enabled.")
|
||||||
upCmd.PersistentFlags().BoolVar(&serverSSHAllowed, serverSSHAllowedFlag, false, "Allow SSH server on peer. If enabled, the SSH server will be permitted")
|
upCmd.PersistentFlags().BoolVar(&serverSSHAllowed, serverSSHAllowedFlag, false, "Allow SSH server on peer. If enabled, the SSH server will be permitted")
|
||||||
upCmd.PersistentFlags().BoolVar(&autoConnectDisabled, disableAutoConnectFlag, false, "Disables auto-connect feature. If enabled, then the client won't connect automatically when the service starts.")
|
upCmd.PersistentFlags().BoolVar(&autoConnectDisabled, disableAutoConnectFlag, false, "Disables auto-connect feature. If enabled, then the client won't connect automatically when the service starts.")
|
||||||
|
upCmd.PersistentFlags().BoolVar(&lazyConnEnabled, enableLazyConnectionFlag, false, "[Experimental] Enable the lazy connection feature. If enabled, the client will establish connections on-demand.")
|
||||||
|
|
||||||
debugCmd.PersistentFlags().BoolVarP(&debugSystemInfoFlag, systemInfoFlag, "S", true, "Adds system information to the debug bundle")
|
debugCmd.PersistentFlags().BoolVarP(&debugSystemInfoFlag, systemInfoFlag, "S", true, "Adds system information to the debug bundle")
|
||||||
debugCmd.PersistentFlags().BoolVarP(&debugUploadBundle, uploadBundle, "U", false, fmt.Sprintf("Uploads the debug bundle to a server from URL defined by %s", uploadBundleURL))
|
debugCmd.PersistentFlags().BoolVarP(&debugUploadBundle, uploadBundle, "U", false, fmt.Sprintf("Uploads the debug bundle to a server from URL defined by %s", uploadBundleURL))
|
||||||
|
@ -44,7 +44,7 @@ func init() {
|
|||||||
statusCmd.MarkFlagsMutuallyExclusive("detail", "json", "yaml", "ipv4")
|
statusCmd.MarkFlagsMutuallyExclusive("detail", "json", "yaml", "ipv4")
|
||||||
statusCmd.PersistentFlags().StringSliceVar(&ipsFilter, "filter-by-ips", []string{}, "filters the detailed output by a list of one or more IPs, e.g., --filter-by-ips 100.64.0.100,100.64.0.200")
|
statusCmd.PersistentFlags().StringSliceVar(&ipsFilter, "filter-by-ips", []string{}, "filters the detailed output by a list of one or more IPs, e.g., --filter-by-ips 100.64.0.100,100.64.0.200")
|
||||||
statusCmd.PersistentFlags().StringSliceVar(&prefixNamesFilter, "filter-by-names", []string{}, "filters the detailed output by a list of one or more peer FQDN or hostnames, e.g., --filter-by-names peer-a,peer-b.netbird.cloud")
|
statusCmd.PersistentFlags().StringSliceVar(&prefixNamesFilter, "filter-by-names", []string{}, "filters the detailed output by a list of one or more peer FQDN or hostnames, e.g., --filter-by-names peer-a,peer-b.netbird.cloud")
|
||||||
statusCmd.PersistentFlags().StringVar(&statusFilter, "filter-by-status", "", "filters the detailed output by connection status(connected|disconnected), e.g., --filter-by-status connected")
|
statusCmd.PersistentFlags().StringVar(&statusFilter, "filter-by-status", "", "filters the detailed output by connection status(idle|connecting|connected), e.g., --filter-by-status connected")
|
||||||
}
|
}
|
||||||
|
|
||||||
func statusFunc(cmd *cobra.Command, args []string) error {
|
func statusFunc(cmd *cobra.Command, args []string) error {
|
||||||
@ -127,12 +127,12 @@ func getStatus(ctx context.Context) (*proto.StatusResponse, error) {
|
|||||||
|
|
||||||
func parseFilters() error {
|
func parseFilters() error {
|
||||||
switch strings.ToLower(statusFilter) {
|
switch strings.ToLower(statusFilter) {
|
||||||
case "", "disconnected", "connected":
|
case "", "idle", "connecting", "connected":
|
||||||
if strings.ToLower(statusFilter) != "" {
|
if strings.ToLower(statusFilter) != "" {
|
||||||
enableDetailFlagWhenFilterFlag()
|
enableDetailFlagWhenFilterFlag()
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("wrong status filter, should be one of connected|disconnected, got: %s", statusFilter)
|
return fmt.Errorf("wrong status filter, should be one of connected|connecting|idle, got: %s", statusFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(ipsFilter) > 0 {
|
if len(ipsFilter) > 0 {
|
||||||
|
@ -194,6 +194,10 @@ func runInForegroundMode(ctx context.Context, cmd *cobra.Command) error {
|
|||||||
ic.BlockLANAccess = &blockLANAccess
|
ic.BlockLANAccess = &blockLANAccess
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cmd.Flag(enableLazyConnectionFlag).Changed {
|
||||||
|
ic.LazyConnectionEnabled = &lazyConnEnabled
|
||||||
|
}
|
||||||
|
|
||||||
providedSetupKey, err := getSetupKey()
|
providedSetupKey, err := getSetupKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -332,6 +336,10 @@ func runInDaemonMode(ctx context.Context, cmd *cobra.Command) error {
|
|||||||
loginRequest.BlockLanAccess = &blockLANAccess
|
loginRequest.BlockLanAccess = &blockLANAccess
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cmd.Flag(enableLazyConnectionFlag).Changed {
|
||||||
|
loginRequest.LazyConnectionEnabled = &lazyConnEnabled
|
||||||
|
}
|
||||||
|
|
||||||
var loginErr error
|
var loginErr error
|
||||||
|
|
||||||
var loginResp *proto.LoginResponse
|
var loginResp *proto.LoginResponse
|
||||||
|
@ -201,14 +201,30 @@ func (c *KernelConfigurer) configure(config wgtypes.Config) error {
|
|||||||
func (c *KernelConfigurer) Close() {
|
func (c *KernelConfigurer) Close() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *KernelConfigurer) GetStats(peerKey string) (WGStats, error) {
|
func (c *KernelConfigurer) GetStats() (map[string]WGStats, error) {
|
||||||
peer, err := c.getPeer(c.deviceName, peerKey)
|
stats := make(map[string]WGStats)
|
||||||
|
wg, err := wgctrl.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return WGStats{}, fmt.Errorf("get wireguard stats: %w", err)
|
return nil, fmt.Errorf("wgctl: %w", err)
|
||||||
}
|
}
|
||||||
return WGStats{
|
defer func() {
|
||||||
LastHandshake: peer.LastHandshakeTime,
|
err = wg.Close()
|
||||||
TxBytes: peer.TransmitBytes,
|
if err != nil {
|
||||||
RxBytes: peer.ReceiveBytes,
|
log.Errorf("Got error while closing wgctl: %v", err)
|
||||||
}, nil
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
wgDevice, err := wg.Device(c.deviceName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("get device %s: %w", c.deviceName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, peer := range wgDevice.Peers {
|
||||||
|
stats[peer.PublicKey.String()] = WGStats{
|
||||||
|
LastHandshake: peer.LastHandshakeTime,
|
||||||
|
TxBytes: peer.TransmitBytes,
|
||||||
|
RxBytes: peer.ReceiveBytes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stats, nil
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package configurer
|
package configurer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
@ -17,6 +18,13 @@ import (
|
|||||||
nbnet "github.com/netbirdio/netbird/util/net"
|
nbnet "github.com/netbirdio/netbird/util/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ipcKeyLastHandshakeTimeSec = "last_handshake_time_sec"
|
||||||
|
ipcKeyLastHandshakeTimeNsec = "last_handshake_time_nsec"
|
||||||
|
ipcKeyTxBytes = "tx_bytes"
|
||||||
|
ipcKeyRxBytes = "rx_bytes"
|
||||||
|
)
|
||||||
|
|
||||||
var ErrAllowedIPNotFound = fmt.Errorf("allowed IP not found")
|
var ErrAllowedIPNotFound = fmt.Errorf("allowed IP not found")
|
||||||
|
|
||||||
type WGUSPConfigurer struct {
|
type WGUSPConfigurer struct {
|
||||||
@ -217,91 +225,75 @@ func (t *WGUSPConfigurer) Close() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *WGUSPConfigurer) GetStats(peerKey string) (WGStats, error) {
|
func (t *WGUSPConfigurer) GetStats() (map[string]WGStats, error) {
|
||||||
ipc, err := t.device.IpcGet()
|
ipc, err := t.device.IpcGet()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return WGStats{}, fmt.Errorf("ipc get: %w", err)
|
return nil, fmt.Errorf("ipc get: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
stats, err := findPeerInfo(ipc, peerKey, []string{
|
return parseTransfers(ipc)
|
||||||
"last_handshake_time_sec",
|
|
||||||
"last_handshake_time_nsec",
|
|
||||||
"tx_bytes",
|
|
||||||
"rx_bytes",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return WGStats{}, fmt.Errorf("find peer info: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sec, err := strconv.ParseInt(stats["last_handshake_time_sec"], 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return WGStats{}, fmt.Errorf("parse handshake sec: %w", err)
|
|
||||||
}
|
|
||||||
nsec, err := strconv.ParseInt(stats["last_handshake_time_nsec"], 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return WGStats{}, fmt.Errorf("parse handshake nsec: %w", err)
|
|
||||||
}
|
|
||||||
txBytes, err := strconv.ParseInt(stats["tx_bytes"], 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return WGStats{}, fmt.Errorf("parse tx_bytes: %w", err)
|
|
||||||
}
|
|
||||||
rxBytes, err := strconv.ParseInt(stats["rx_bytes"], 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return WGStats{}, fmt.Errorf("parse rx_bytes: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return WGStats{
|
|
||||||
LastHandshake: time.Unix(sec, nsec),
|
|
||||||
TxBytes: txBytes,
|
|
||||||
RxBytes: rxBytes,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func findPeerInfo(ipcInput string, peerKey string, searchConfigKeys []string) (map[string]string, error) {
|
func parseTransfers(ipc string) (map[string]WGStats, error) {
|
||||||
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
|
stats := make(map[string]WGStats)
|
||||||
if err != nil {
|
var (
|
||||||
return nil, fmt.Errorf("parse key: %w", err)
|
currentKey string
|
||||||
}
|
currentStats WGStats
|
||||||
|
hasPeer bool
|
||||||
hexKey := hex.EncodeToString(peerKeyParsed[:])
|
)
|
||||||
|
lines := strings.Split(ipc, "\n")
|
||||||
lines := strings.Split(ipcInput, "\n")
|
|
||||||
|
|
||||||
configFound := map[string]string{}
|
|
||||||
foundPeer := false
|
|
||||||
for _, line := range lines {
|
for _, line := range lines {
|
||||||
line = strings.TrimSpace(line)
|
line = strings.TrimSpace(line)
|
||||||
|
|
||||||
// If we're within the details of the found peer and encounter another public key,
|
// If we're within the details of the found peer and encounter another public key,
|
||||||
// this means we're starting another peer's details. So, stop.
|
// this means we're starting another peer's details. So, stop.
|
||||||
if strings.HasPrefix(line, "public_key=") && foundPeer {
|
if strings.HasPrefix(line, "public_key=") {
|
||||||
break
|
peerID := strings.TrimPrefix(line, "public_key=")
|
||||||
}
|
h, err := hex.DecodeString(peerID)
|
||||||
|
if err != nil {
|
||||||
// Identify the peer with the specific public key
|
return nil, fmt.Errorf("decode peerID: %w", err)
|
||||||
if line == fmt.Sprintf("public_key=%s", hexKey) {
|
|
||||||
foundPeer = true
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, key := range searchConfigKeys {
|
|
||||||
if foundPeer && strings.HasPrefix(line, key+"=") {
|
|
||||||
v := strings.SplitN(line, "=", 2)
|
|
||||||
configFound[v[0]] = v[1]
|
|
||||||
}
|
}
|
||||||
|
currentKey = base64.StdEncoding.EncodeToString(h)
|
||||||
|
currentStats = WGStats{} // Reset stats for the new peer
|
||||||
|
hasPeer = true
|
||||||
|
stats[currentKey] = currentStats
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasPeer {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
key := strings.SplitN(line, "=", 2)
|
||||||
|
if len(key) != 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch key[0] {
|
||||||
|
case ipcKeyLastHandshakeTimeSec:
|
||||||
|
hs, err := toLastHandshake(key[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
currentStats.LastHandshake = hs
|
||||||
|
stats[currentKey] = currentStats
|
||||||
|
case ipcKeyRxBytes:
|
||||||
|
rxBytes, err := toBytes(key[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parse rx_bytes: %w", err)
|
||||||
|
}
|
||||||
|
currentStats.RxBytes = rxBytes
|
||||||
|
stats[currentKey] = currentStats
|
||||||
|
case ipcKeyTxBytes:
|
||||||
|
TxBytes, err := toBytes(key[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parse tx_bytes: %w", err)
|
||||||
|
}
|
||||||
|
currentStats.TxBytes = TxBytes
|
||||||
|
stats[currentKey] = currentStats
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: use multierr
|
return stats, nil
|
||||||
for _, key := range searchConfigKeys {
|
|
||||||
if _, ok := configFound[key]; !ok {
|
|
||||||
return configFound, fmt.Errorf("config key not found: %s", key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !foundPeer {
|
|
||||||
return nil, fmt.Errorf("%w: %s", ErrPeerNotFound, peerKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
return configFound, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func toWgUserspaceString(wgCfg wgtypes.Config) string {
|
func toWgUserspaceString(wgCfg wgtypes.Config) string {
|
||||||
@ -355,6 +347,18 @@ func toWgUserspaceString(wgCfg wgtypes.Config) string {
|
|||||||
return sb.String()
|
return sb.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func toLastHandshake(stringVar string) (time.Time, error) {
|
||||||
|
sec, err := strconv.ParseInt(stringVar, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return time.Time{}, fmt.Errorf("parse handshake sec: %w", err)
|
||||||
|
}
|
||||||
|
return time.Unix(sec, 0), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func toBytes(s string) (int64, error) {
|
||||||
|
return strconv.ParseInt(s, 10, 64)
|
||||||
|
}
|
||||||
|
|
||||||
func getFwmark() int {
|
func getFwmark() int {
|
||||||
if nbnet.AdvancedRouting() {
|
if nbnet.AdvancedRouting() {
|
||||||
return nbnet.ControlPlaneMark
|
return nbnet.ControlPlaneMark
|
||||||
|
@ -2,10 +2,8 @@ package configurer
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
)
|
)
|
||||||
@ -34,58 +32,35 @@ errno=0
|
|||||||
|
|
||||||
`
|
`
|
||||||
|
|
||||||
func Test_findPeerInfo(t *testing.T) {
|
func Test_parseTransfers(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
peerKey string
|
peerKey string
|
||||||
searchKeys []string
|
want WGStats
|
||||||
want map[string]string
|
|
||||||
wantErr bool
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "single",
|
name: "single",
|
||||||
peerKey: "58402e695ba1772b1cc9309755f043251ea77fdcf10fbe63989ceb7e19321376",
|
peerKey: "b85996fecc9c7f1fc6d2572a76eda11d59bcd20be8e543b15ce4bd85a8e75a33",
|
||||||
searchKeys: []string{"tx_bytes"},
|
want: WGStats{
|
||||||
want: map[string]string{
|
TxBytes: 0,
|
||||||
"tx_bytes": "38333",
|
RxBytes: 0,
|
||||||
},
|
},
|
||||||
wantErr: false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "multiple",
|
name: "multiple",
|
||||||
peerKey: "58402e695ba1772b1cc9309755f043251ea77fdcf10fbe63989ceb7e19321376",
|
peerKey: "58402e695ba1772b1cc9309755f043251ea77fdcf10fbe63989ceb7e19321376",
|
||||||
searchKeys: []string{"tx_bytes", "rx_bytes"},
|
want: WGStats{
|
||||||
want: map[string]string{
|
TxBytes: 38333,
|
||||||
"tx_bytes": "38333",
|
RxBytes: 2224,
|
||||||
"rx_bytes": "2224",
|
|
||||||
},
|
},
|
||||||
wantErr: false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "lastpeer",
|
name: "lastpeer",
|
||||||
peerKey: "662e14fd594556f522604703340351258903b64f35553763f19426ab2a515c58",
|
peerKey: "662e14fd594556f522604703340351258903b64f35553763f19426ab2a515c58",
|
||||||
searchKeys: []string{"tx_bytes", "rx_bytes"},
|
want: WGStats{
|
||||||
want: map[string]string{
|
TxBytes: 1212111,
|
||||||
"tx_bytes": "1212111",
|
RxBytes: 1929999999,
|
||||||
"rx_bytes": "1929999999",
|
|
||||||
},
|
},
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "peer not found",
|
|
||||||
peerKey: "1111111111111111111111111111111111111111111111111111111111111111",
|
|
||||||
searchKeys: nil,
|
|
||||||
want: nil,
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "key not found",
|
|
||||||
peerKey: "662e14fd594556f522604703340351258903b64f35553763f19426ab2a515c58",
|
|
||||||
searchKeys: []string{"tx_bytes", "unknown_key"},
|
|
||||||
want: map[string]string{
|
|
||||||
"tx_bytes": "1212111",
|
|
||||||
},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
@ -96,9 +71,19 @@ func Test_findPeerInfo(t *testing.T) {
|
|||||||
key, err := wgtypes.NewKey(res)
|
key, err := wgtypes.NewKey(res)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
got, err := findPeerInfo(ipcFixture, key.String(), tt.searchKeys)
|
stats, err := parseTransfers(ipcFixture)
|
||||||
assert.Equalf(t, tt.wantErr, err != nil, fmt.Sprintf("findPeerInfo(%v, %v, %v)", ipcFixture, key.String(), tt.searchKeys))
|
if err != nil {
|
||||||
assert.Equalf(t, tt.want, got, "findPeerInfo(%v, %v, %v)", ipcFixture, key.String(), tt.searchKeys)
|
require.NoError(t, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
stat, ok := stats[key.String()]
|
||||||
|
if !ok {
|
||||||
|
require.True(t, ok)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
require.Equal(t, tt.want, stat)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,5 +16,5 @@ type WGConfigurer interface {
|
|||||||
AddAllowedIP(peerKey string, allowedIP string) error
|
AddAllowedIP(peerKey string, allowedIP string) error
|
||||||
RemoveAllowedIP(peerKey string, allowedIP string) error
|
RemoveAllowedIP(peerKey string, allowedIP string) error
|
||||||
Close()
|
Close()
|
||||||
GetStats(peerKey string) (configurer.WGStats, error)
|
GetStats() (map[string]configurer.WGStats, error)
|
||||||
}
|
}
|
||||||
|
@ -212,9 +212,9 @@ func (w *WGIface) GetWGDevice() *wgdevice.Device {
|
|||||||
return w.tun.Device()
|
return w.tun.Device()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStats returns the last handshake time, rx and tx bytes for the given peer
|
// GetStats returns the last handshake time, rx and tx bytes
|
||||||
func (w *WGIface) GetStats(peerKey string) (configurer.WGStats, error) {
|
func (w *WGIface) GetStats() (map[string]configurer.WGStats, error) {
|
||||||
return w.configurer.GetStats(peerKey)
|
return w.configurer.GetStats()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WGIface) waitUntilRemoved() error {
|
func (w *WGIface) waitUntilRemoved() error {
|
||||||
|
@ -74,6 +74,8 @@ type ConfigInput struct {
|
|||||||
DisableNotifications *bool
|
DisableNotifications *bool
|
||||||
|
|
||||||
DNSLabels domain.List
|
DNSLabels domain.List
|
||||||
|
|
||||||
|
LazyConnectionEnabled *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config Configuration type
|
// Config Configuration type
|
||||||
@ -138,6 +140,8 @@ type Config struct {
|
|||||||
ClientCertKeyPath string
|
ClientCertKeyPath string
|
||||||
|
|
||||||
ClientCertKeyPair *tls.Certificate `json:"-"`
|
ClientCertKeyPair *tls.Certificate `json:"-"`
|
||||||
|
|
||||||
|
LazyConnectionEnabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadConfig read config file and return with Config. If it is not exists create a new with default values
|
// ReadConfig read config file and return with Config. If it is not exists create a new with default values
|
||||||
@ -524,6 +528,12 @@ func (config *Config) apply(input ConfigInput) (updated bool, err error) {
|
|||||||
updated = true
|
updated = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if input.LazyConnectionEnabled != nil && *input.LazyConnectionEnabled != config.LazyConnectionEnabled {
|
||||||
|
log.Infof("switching lazy connection to %t", *input.LazyConnectionEnabled)
|
||||||
|
config.LazyConnectionEnabled = *input.LazyConnectionEnabled
|
||||||
|
updated = true
|
||||||
|
}
|
||||||
|
|
||||||
return updated, nil
|
return updated, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
303
client/internal/conn_mgr.go
Normal file
303
client/internal/conn_mgr.go
Normal file
@ -0,0 +1,303 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/internal/lazyconn"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/lazyconn/manager"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/peer"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/peer/dispatcher"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/peerstore"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConnMgr coordinates both lazy connections (established on-demand) and permanent peer connections.
|
||||||
|
//
|
||||||
|
// The connection manager is responsible for:
|
||||||
|
// - Managing lazy connections via the lazyConnManager
|
||||||
|
// - Maintaining a list of excluded peers that should always have permanent connections
|
||||||
|
// - Handling connection establishment based on peer signaling
|
||||||
|
//
|
||||||
|
// The implementation is not thread-safe; it is protected by engine.syncMsgMux.
|
||||||
|
type ConnMgr struct {
|
||||||
|
peerStore *peerstore.Store
|
||||||
|
statusRecorder *peer.Status
|
||||||
|
iface lazyconn.WGIface
|
||||||
|
dispatcher *dispatcher.ConnectionDispatcher
|
||||||
|
enabledLocally bool
|
||||||
|
|
||||||
|
lazyConnMgr *manager.Manager
|
||||||
|
|
||||||
|
wg sync.WaitGroup
|
||||||
|
ctx context.Context
|
||||||
|
ctxCancel context.CancelFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnMgr(engineConfig *EngineConfig, statusRecorder *peer.Status, peerStore *peerstore.Store, iface lazyconn.WGIface, dispatcher *dispatcher.ConnectionDispatcher) *ConnMgr {
|
||||||
|
e := &ConnMgr{
|
||||||
|
peerStore: peerStore,
|
||||||
|
statusRecorder: statusRecorder,
|
||||||
|
iface: iface,
|
||||||
|
dispatcher: dispatcher,
|
||||||
|
}
|
||||||
|
if engineConfig.LazyConnectionEnabled || lazyconn.IsLazyConnEnabledByEnv() {
|
||||||
|
e.enabledLocally = true
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start initializes the connection manager and starts the lazy connection manager if enabled by env var or cmd line option.
|
||||||
|
func (e *ConnMgr) Start(ctx context.Context) {
|
||||||
|
if e.lazyConnMgr != nil {
|
||||||
|
log.Errorf("lazy connection manager is already started")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !e.enabledLocally {
|
||||||
|
log.Infof("lazy connection manager is disabled")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
e.initLazyManager(ctx)
|
||||||
|
e.statusRecorder.UpdateLazyConnection(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatedRemoteFeatureFlag is called when the remote feature flag is updated.
|
||||||
|
// If enabled, it initializes the lazy connection manager and start it. Do not need to call Start() again.
|
||||||
|
// If disabled, then it closes the lazy connection manager and open the connections to all peers.
|
||||||
|
func (e *ConnMgr) UpdatedRemoteFeatureFlag(ctx context.Context, enabled bool) error {
|
||||||
|
// do not disable lazy connection manager if it was enabled by env var
|
||||||
|
if e.enabledLocally {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if enabled {
|
||||||
|
// if the lazy connection manager is already started, do not start it again
|
||||||
|
if e.lazyConnMgr != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("lazy connection manager is enabled by management feature flag")
|
||||||
|
e.initLazyManager(ctx)
|
||||||
|
e.statusRecorder.UpdateLazyConnection(true)
|
||||||
|
return e.addPeersToLazyConnManager(ctx)
|
||||||
|
} else {
|
||||||
|
if e.lazyConnMgr == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
log.Infof("lazy connection manager is disabled by management feature flag")
|
||||||
|
e.closeManager(ctx)
|
||||||
|
e.statusRecorder.UpdateLazyConnection(false)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetExcludeList sets the list of peer IDs that should always have permanent connections.
|
||||||
|
func (e *ConnMgr) SetExcludeList(peerIDs []string) {
|
||||||
|
if e.lazyConnMgr == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
excludedPeers := make([]lazyconn.PeerConfig, 0, len(peerIDs))
|
||||||
|
|
||||||
|
for _, peerID := range peerIDs {
|
||||||
|
var peerConn *peer.Conn
|
||||||
|
var exists bool
|
||||||
|
if peerConn, exists = e.peerStore.PeerConn(peerID); !exists {
|
||||||
|
log.Warnf("failed to find peer conn for peerID: %s", peerID)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
lazyPeerCfg := lazyconn.PeerConfig{
|
||||||
|
PublicKey: peerID,
|
||||||
|
AllowedIPs: peerConn.WgConfig().AllowedIps,
|
||||||
|
PeerConnID: peerConn.ConnID(),
|
||||||
|
Log: peerConn.Log,
|
||||||
|
}
|
||||||
|
excludedPeers = append(excludedPeers, lazyPeerCfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
added := e.lazyConnMgr.ExcludePeer(e.ctx, excludedPeers)
|
||||||
|
for _, peerID := range added {
|
||||||
|
var peerConn *peer.Conn
|
||||||
|
var exists bool
|
||||||
|
if peerConn, exists = e.peerStore.PeerConn(peerID); !exists {
|
||||||
|
// if the peer not exist in the store, it means that the engine will call the AddPeerConn in next step
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
peerConn.Log.Infof("peer has been added to lazy connection exclude list, opening permanent connection")
|
||||||
|
if err := peerConn.Open(e.ctx); err != nil {
|
||||||
|
peerConn.Log.Errorf("failed to open connection: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ConnMgr) AddPeerConn(ctx context.Context, peerKey string, conn *peer.Conn) (exists bool) {
|
||||||
|
if success := e.peerStore.AddPeerConn(peerKey, conn); !success {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !e.isStartedWithLazyMgr() {
|
||||||
|
if err := conn.Open(ctx); err != nil {
|
||||||
|
conn.Log.Errorf("failed to open connection: %v", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !lazyconn.IsSupported(conn.AgentVersionString()) {
|
||||||
|
conn.Log.Warnf("peer does not support lazy connection (%s), open permanent connection", conn.AgentVersionString())
|
||||||
|
if err := conn.Open(ctx); err != nil {
|
||||||
|
conn.Log.Errorf("failed to open connection: %v", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lazyPeerCfg := lazyconn.PeerConfig{
|
||||||
|
PublicKey: peerKey,
|
||||||
|
AllowedIPs: conn.WgConfig().AllowedIps,
|
||||||
|
PeerConnID: conn.ConnID(),
|
||||||
|
Log: conn.Log,
|
||||||
|
}
|
||||||
|
excluded, err := e.lazyConnMgr.AddPeer(lazyPeerCfg)
|
||||||
|
if err != nil {
|
||||||
|
conn.Log.Errorf("failed to add peer to lazyconn manager: %v", err)
|
||||||
|
if err := conn.Open(ctx); err != nil {
|
||||||
|
conn.Log.Errorf("failed to open connection: %v", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if excluded {
|
||||||
|
conn.Log.Infof("peer is on lazy conn manager exclude list, opening connection")
|
||||||
|
if err := conn.Open(ctx); err != nil {
|
||||||
|
conn.Log.Errorf("failed to open connection: %v", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.Log.Infof("peer added to lazy conn manager")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ConnMgr) RemovePeerConn(peerKey string) {
|
||||||
|
conn, ok := e.peerStore.Remove(peerKey)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
if !e.isStartedWithLazyMgr() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
e.lazyConnMgr.RemovePeer(peerKey)
|
||||||
|
conn.Log.Infof("removed peer from lazy conn manager")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ConnMgr) OnSignalMsg(ctx context.Context, peerKey string) (*peer.Conn, bool) {
|
||||||
|
conn, ok := e.peerStore.PeerConn(peerKey)
|
||||||
|
if !ok {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !e.isStartedWithLazyMgr() {
|
||||||
|
return conn, true
|
||||||
|
}
|
||||||
|
|
||||||
|
if found := e.lazyConnMgr.ActivatePeer(ctx, peerKey); found {
|
||||||
|
conn.Log.Infof("activated peer from inactive state")
|
||||||
|
if err := conn.Open(e.ctx); err != nil {
|
||||||
|
conn.Log.Errorf("failed to open connection: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return conn, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ConnMgr) Close() {
|
||||||
|
if !e.isStartedWithLazyMgr() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
e.ctxCancel()
|
||||||
|
e.wg.Wait()
|
||||||
|
e.lazyConnMgr = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ConnMgr) initLazyManager(parentCtx context.Context) {
|
||||||
|
cfg := manager.Config{
|
||||||
|
InactivityThreshold: inactivityThresholdEnv(),
|
||||||
|
}
|
||||||
|
e.lazyConnMgr = manager.NewManager(cfg, e.peerStore, e.iface, e.dispatcher)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(parentCtx)
|
||||||
|
e.ctx = ctx
|
||||||
|
e.ctxCancel = cancel
|
||||||
|
|
||||||
|
e.wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer e.wg.Done()
|
||||||
|
e.lazyConnMgr.Start(ctx)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ConnMgr) addPeersToLazyConnManager(ctx context.Context) error {
|
||||||
|
peers := e.peerStore.PeersPubKey()
|
||||||
|
lazyPeerCfgs := make([]lazyconn.PeerConfig, 0, len(peers))
|
||||||
|
for _, peerID := range peers {
|
||||||
|
var peerConn *peer.Conn
|
||||||
|
var exists bool
|
||||||
|
if peerConn, exists = e.peerStore.PeerConn(peerID); !exists {
|
||||||
|
log.Warnf("failed to find peer conn for peerID: %s", peerID)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
lazyPeerCfg := lazyconn.PeerConfig{
|
||||||
|
PublicKey: peerID,
|
||||||
|
AllowedIPs: peerConn.WgConfig().AllowedIps,
|
||||||
|
PeerConnID: peerConn.ConnID(),
|
||||||
|
Log: peerConn.Log,
|
||||||
|
}
|
||||||
|
lazyPeerCfgs = append(lazyPeerCfgs, lazyPeerCfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.lazyConnMgr.AddActivePeers(ctx, lazyPeerCfgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ConnMgr) closeManager(ctx context.Context) {
|
||||||
|
if e.lazyConnMgr == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
e.ctxCancel()
|
||||||
|
e.wg.Wait()
|
||||||
|
e.lazyConnMgr = nil
|
||||||
|
|
||||||
|
for _, peerID := range e.peerStore.PeersPubKey() {
|
||||||
|
e.peerStore.PeerConnOpen(ctx, peerID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ConnMgr) isStartedWithLazyMgr() bool {
|
||||||
|
return e.lazyConnMgr != nil && e.ctxCancel != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func inactivityThresholdEnv() *time.Duration {
|
||||||
|
envValue := os.Getenv(lazyconn.EnvInactivityThreshold)
|
||||||
|
if envValue == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedMinutes, err := strconv.Atoi(envValue)
|
||||||
|
if err != nil || parsedMinutes <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
d := time.Duration(parsedMinutes) * time.Minute
|
||||||
|
return &d
|
||||||
|
}
|
@ -440,7 +440,8 @@ func createEngineConfig(key wgtypes.Key, config *Config, peerConfig *mgmProto.Pe
|
|||||||
DisableDNS: config.DisableDNS,
|
DisableDNS: config.DisableDNS,
|
||||||
DisableFirewall: config.DisableFirewall,
|
DisableFirewall: config.DisableFirewall,
|
||||||
|
|
||||||
BlockLANAccess: config.BlockLANAccess,
|
BlockLANAccess: config.BlockLANAccess,
|
||||||
|
LazyConnectionEnabled: config.LazyConnectionEnabled,
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.PreSharedKey != "" {
|
if config.PreSharedKey != "" {
|
||||||
@ -481,7 +482,7 @@ func connectToSignal(ctx context.Context, wtConfig *mgmProto.NetbirdConfig, ourP
|
|||||||
return signalClient, nil
|
return signalClient, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// loginToManagement creates Management Services client, establishes a connection, logs-in and gets a global Netbird config (signal, turn, stun hosts, etc)
|
// loginToManagement creates Management ServiceDependencies client, establishes a connection, logs-in and gets a global Netbird config (signal, turn, stun hosts, etc)
|
||||||
func loginToManagement(ctx context.Context, client mgm.Client, pubSSHKey []byte, config *Config) (*mgmProto.LoginResponse, error) {
|
func loginToManagement(ctx context.Context, client mgm.Client, pubSSHKey []byte, config *Config) (*mgmProto.LoginResponse, error) {
|
||||||
|
|
||||||
serverPublicKey, err := client.GetServerPublicKey()
|
serverPublicKey, err := client.GetServerPublicKey()
|
||||||
|
@ -376,6 +376,7 @@ func (g *BundleGenerator) addCommonConfigFields(configContent *strings.Builder)
|
|||||||
configContent.WriteString(fmt.Sprintf("DisableFirewall: %v\n", g.internalConfig.DisableFirewall))
|
configContent.WriteString(fmt.Sprintf("DisableFirewall: %v\n", g.internalConfig.DisableFirewall))
|
||||||
|
|
||||||
configContent.WriteString(fmt.Sprintf("BlockLANAccess: %v\n", g.internalConfig.BlockLANAccess))
|
configContent.WriteString(fmt.Sprintf("BlockLANAccess: %v\n", g.internalConfig.BlockLANAccess))
|
||||||
|
configContent.WriteString(fmt.Sprintf("LazyConnectionEnabled: %v\n", g.internalConfig.LazyConnectionEnabled))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *BundleGenerator) addProf() (err error) {
|
func (g *BundleGenerator) addProf() (err error) {
|
||||||
|
@ -5,7 +5,6 @@ package dns
|
|||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/iface/configurer"
|
|
||||||
"github.com/netbirdio/netbird/client/iface/device"
|
"github.com/netbirdio/netbird/client/iface/device"
|
||||||
"github.com/netbirdio/netbird/client/iface/wgaddr"
|
"github.com/netbirdio/netbird/client/iface/wgaddr"
|
||||||
)
|
)
|
||||||
@ -18,5 +17,4 @@ type WGIface interface {
|
|||||||
IsUserspaceBind() bool
|
IsUserspaceBind() bool
|
||||||
GetFilter() device.PacketFilter
|
GetFilter() device.PacketFilter
|
||||||
GetDevice() *device.FilteredDevice
|
GetDevice() *device.FilteredDevice
|
||||||
GetStats(peerKey string) (configurer.WGStats, error)
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/netbirdio/netbird/client/iface/configurer"
|
|
||||||
"github.com/netbirdio/netbird/client/iface/device"
|
"github.com/netbirdio/netbird/client/iface/device"
|
||||||
"github.com/netbirdio/netbird/client/iface/wgaddr"
|
"github.com/netbirdio/netbird/client/iface/wgaddr"
|
||||||
)
|
)
|
||||||
@ -13,6 +12,5 @@ type WGIface interface {
|
|||||||
IsUserspaceBind() bool
|
IsUserspaceBind() bool
|
||||||
GetFilter() device.PacketFilter
|
GetFilter() device.PacketFilter
|
||||||
GetDevice() *device.FilteredDevice
|
GetDevice() *device.FilteredDevice
|
||||||
GetStats(peerKey string) (configurer.WGStats, error)
|
|
||||||
GetInterfaceGUIDString() (string, error)
|
GetInterfaceGUIDString() (string, error)
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ import (
|
|||||||
nftypes "github.com/netbirdio/netbird/client/internal/netflow/types"
|
nftypes "github.com/netbirdio/netbird/client/internal/netflow/types"
|
||||||
"github.com/netbirdio/netbird/client/internal/networkmonitor"
|
"github.com/netbirdio/netbird/client/internal/networkmonitor"
|
||||||
"github.com/netbirdio/netbird/client/internal/peer"
|
"github.com/netbirdio/netbird/client/internal/peer"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/peer/dispatcher"
|
||||||
"github.com/netbirdio/netbird/client/internal/peer/guard"
|
"github.com/netbirdio/netbird/client/internal/peer/guard"
|
||||||
icemaker "github.com/netbirdio/netbird/client/internal/peer/ice"
|
icemaker "github.com/netbirdio/netbird/client/internal/peer/ice"
|
||||||
"github.com/netbirdio/netbird/client/internal/peerstore"
|
"github.com/netbirdio/netbird/client/internal/peerstore"
|
||||||
@ -122,6 +123,8 @@ type EngineConfig struct {
|
|||||||
DisableFirewall bool
|
DisableFirewall bool
|
||||||
|
|
||||||
BlockLANAccess bool
|
BlockLANAccess bool
|
||||||
|
|
||||||
|
LazyConnectionEnabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Engine is a mechanism responsible for reacting on Signal and Management stream events and managing connections to the remote peers.
|
// Engine is a mechanism responsible for reacting on Signal and Management stream events and managing connections to the remote peers.
|
||||||
@ -134,6 +137,8 @@ type Engine struct {
|
|||||||
// peerConns is a map that holds all the peers that are known to this peer
|
// peerConns is a map that holds all the peers that are known to this peer
|
||||||
peerStore *peerstore.Store
|
peerStore *peerstore.Store
|
||||||
|
|
||||||
|
connMgr *ConnMgr
|
||||||
|
|
||||||
beforePeerHook nbnet.AddHookFunc
|
beforePeerHook nbnet.AddHookFunc
|
||||||
afterPeerHook nbnet.RemoveHookFunc
|
afterPeerHook nbnet.RemoveHookFunc
|
||||||
|
|
||||||
@ -170,7 +175,8 @@ type Engine struct {
|
|||||||
sshServerFunc func(hostKeyPEM []byte, addr string) (nbssh.Server, error)
|
sshServerFunc func(hostKeyPEM []byte, addr string) (nbssh.Server, error)
|
||||||
sshServer nbssh.Server
|
sshServer nbssh.Server
|
||||||
|
|
||||||
statusRecorder *peer.Status
|
statusRecorder *peer.Status
|
||||||
|
peerConnDispatcher *dispatcher.ConnectionDispatcher
|
||||||
|
|
||||||
firewall firewallManager.Manager
|
firewall firewallManager.Manager
|
||||||
routeManager routemanager.Manager
|
routeManager routemanager.Manager
|
||||||
@ -262,6 +268,10 @@ func (e *Engine) Stop() error {
|
|||||||
e.syncMsgMux.Lock()
|
e.syncMsgMux.Lock()
|
||||||
defer e.syncMsgMux.Unlock()
|
defer e.syncMsgMux.Unlock()
|
||||||
|
|
||||||
|
if e.connMgr != nil {
|
||||||
|
e.connMgr.Close()
|
||||||
|
}
|
||||||
|
|
||||||
// stopping network monitor first to avoid starting the engine again
|
// stopping network monitor first to avoid starting the engine again
|
||||||
if e.networkMonitor != nil {
|
if e.networkMonitor != nil {
|
||||||
e.networkMonitor.Stop()
|
e.networkMonitor.Stop()
|
||||||
@ -297,8 +307,7 @@ func (e *Engine) Stop() error {
|
|||||||
e.statusRecorder.UpdateDNSStates([]peer.NSGroupState{})
|
e.statusRecorder.UpdateDNSStates([]peer.NSGroupState{})
|
||||||
e.statusRecorder.UpdateRelayStates([]relay.ProbeResult{})
|
e.statusRecorder.UpdateRelayStates([]relay.ProbeResult{})
|
||||||
|
|
||||||
err := e.removeAllPeers()
|
if err := e.removeAllPeers(); err != nil {
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to remove all peers: %s", err)
|
return fmt.Errorf("failed to remove all peers: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -405,8 +414,7 @@ func (e *Engine) Start() error {
|
|||||||
|
|
||||||
e.routeManager.SetRouteChangeListener(e.mobileDep.NetworkChangeListener)
|
e.routeManager.SetRouteChangeListener(e.mobileDep.NetworkChangeListener)
|
||||||
|
|
||||||
err = e.wgInterfaceCreate()
|
if err = e.wgInterfaceCreate(); err != nil {
|
||||||
if err != nil {
|
|
||||||
log.Errorf("failed creating tunnel interface %s: [%s]", e.config.WgIfaceName, err.Error())
|
log.Errorf("failed creating tunnel interface %s: [%s]", e.config.WgIfaceName, err.Error())
|
||||||
e.close()
|
e.close()
|
||||||
return fmt.Errorf("create wg interface: %w", err)
|
return fmt.Errorf("create wg interface: %w", err)
|
||||||
@ -442,6 +450,11 @@ func (e *Engine) Start() error {
|
|||||||
NATExternalIPs: e.parseNATExternalIPMappings(),
|
NATExternalIPs: e.parseNATExternalIPMappings(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
e.peerConnDispatcher = dispatcher.NewConnectionDispatcher()
|
||||||
|
|
||||||
|
e.connMgr = NewConnMgr(e.config, e.statusRecorder, e.peerStore, wgIface, e.peerConnDispatcher)
|
||||||
|
e.connMgr.Start(e.ctx)
|
||||||
|
|
||||||
e.srWatcher = guard.NewSRWatcher(e.signal, e.relayManager, e.mobileDep.IFaceDiscover, iceCfg)
|
e.srWatcher = guard.NewSRWatcher(e.signal, e.relayManager, e.mobileDep.IFaceDiscover, iceCfg)
|
||||||
e.srWatcher.Start()
|
e.srWatcher.Start()
|
||||||
|
|
||||||
@ -450,7 +463,6 @@ func (e *Engine) Start() error {
|
|||||||
|
|
||||||
// starting network monitor at the very last to avoid disruptions
|
// starting network monitor at the very last to avoid disruptions
|
||||||
e.startNetworkMonitor()
|
e.startNetworkMonitor()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -550,6 +562,16 @@ func (e *Engine) modifyPeers(peersUpdate []*mgmProto.RemotePeerConfig) error {
|
|||||||
var modified []*mgmProto.RemotePeerConfig
|
var modified []*mgmProto.RemotePeerConfig
|
||||||
for _, p := range peersUpdate {
|
for _, p := range peersUpdate {
|
||||||
peerPubKey := p.GetWgPubKey()
|
peerPubKey := p.GetWgPubKey()
|
||||||
|
currentPeer, ok := e.peerStore.PeerConn(peerPubKey)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if currentPeer.AgentVersionString() != p.AgentVersion {
|
||||||
|
modified = append(modified, p)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
allowedIPs, ok := e.peerStore.AllowedIPs(peerPubKey)
|
allowedIPs, ok := e.peerStore.AllowedIPs(peerPubKey)
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
@ -559,8 +581,7 @@ func (e *Engine) modifyPeers(peersUpdate []*mgmProto.RemotePeerConfig) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
err := e.statusRecorder.UpdatePeerFQDN(peerPubKey, p.GetFqdn())
|
if err := e.statusRecorder.UpdatePeerFQDN(peerPubKey, p.GetFqdn()); err != nil {
|
||||||
if err != nil {
|
|
||||||
log.Warnf("error updating peer's %s fqdn in the status recorder, got error: %v", peerPubKey, err)
|
log.Warnf("error updating peer's %s fqdn in the status recorder, got error: %v", peerPubKey, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -621,16 +642,11 @@ func (e *Engine) removePeer(peerKey string) error {
|
|||||||
e.sshServer.RemoveAuthorizedKey(peerKey)
|
e.sshServer.RemoveAuthorizedKey(peerKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
e.connMgr.RemovePeerConn(peerKey)
|
||||||
err := e.statusRecorder.RemovePeer(peerKey)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("received error when removing peer %s from status recorder: %v", peerKey, err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
conn, exists := e.peerStore.Remove(peerKey)
|
err := e.statusRecorder.RemovePeer(peerKey)
|
||||||
if exists {
|
if err != nil {
|
||||||
conn.Close()
|
log.Warnf("received error when removing peer %s from status recorder: %v", peerKey, err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -952,6 +968,10 @@ func (e *Engine) updateNetworkMap(networkMap *mgmProto.NetworkMap) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := e.connMgr.UpdatedRemoteFeatureFlag(e.ctx, networkMap.GetPeerConfig().GetLazyConnectionEnabled()); err != nil {
|
||||||
|
log.Errorf("failed to update lazy connection feature flag: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
if e.firewall != nil {
|
if e.firewall != nil {
|
||||||
if localipfw, ok := e.firewall.(localIpUpdater); ok {
|
if localipfw, ok := e.firewall.(localIpUpdater); ok {
|
||||||
if err := localipfw.UpdateLocalIPs(); err != nil {
|
if err := localipfw.UpdateLocalIPs(); err != nil {
|
||||||
@ -976,7 +996,8 @@ func (e *Engine) updateNetworkMap(networkMap *mgmProto.NetworkMap) error {
|
|||||||
e.updateDNSForwarder(dnsRouteFeatureFlag, fwdEntries)
|
e.updateDNSForwarder(dnsRouteFeatureFlag, fwdEntries)
|
||||||
|
|
||||||
// Ingress forward rules
|
// Ingress forward rules
|
||||||
if err := e.updateForwardRules(networkMap.GetForwardingRules()); err != nil {
|
forwardingRules, err := e.updateForwardRules(networkMap.GetForwardingRules())
|
||||||
|
if err != nil {
|
||||||
log.Errorf("failed to update forward rules, err: %v", err)
|
log.Errorf("failed to update forward rules, err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1022,6 +1043,10 @@ func (e *Engine) updateNetworkMap(networkMap *mgmProto.NetworkMap) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// must set the exclude list after the peers are added. Without it the manager can not figure out the peers parameters from the store
|
||||||
|
excludedLazyPeers := e.toExcludedLazyPeers(routes, forwardingRules, networkMap.GetRemotePeers())
|
||||||
|
e.connMgr.SetExcludeList(excludedLazyPeers)
|
||||||
|
|
||||||
protoDNSConfig := networkMap.GetDNSConfig()
|
protoDNSConfig := networkMap.GetDNSConfig()
|
||||||
if protoDNSConfig == nil {
|
if protoDNSConfig == nil {
|
||||||
protoDNSConfig = &mgmProto.DNSConfig{}
|
protoDNSConfig = &mgmProto.DNSConfig{}
|
||||||
@ -1155,7 +1180,7 @@ func (e *Engine) updateOfflinePeers(offlinePeers []*mgmProto.RemotePeerConfig) {
|
|||||||
IP: strings.Join(offlinePeer.GetAllowedIps(), ","),
|
IP: strings.Join(offlinePeer.GetAllowedIps(), ","),
|
||||||
PubKey: offlinePeer.GetWgPubKey(),
|
PubKey: offlinePeer.GetWgPubKey(),
|
||||||
FQDN: offlinePeer.GetFqdn(),
|
FQDN: offlinePeer.GetFqdn(),
|
||||||
ConnStatus: peer.StatusDisconnected,
|
ConnStatus: peer.StatusIdle,
|
||||||
ConnStatusUpdate: time.Now(),
|
ConnStatusUpdate: time.Now(),
|
||||||
Mux: new(sync.RWMutex),
|
Mux: new(sync.RWMutex),
|
||||||
}
|
}
|
||||||
@ -1191,12 +1216,17 @@ func (e *Engine) addNewPeer(peerConfig *mgmProto.RemotePeerConfig) error {
|
|||||||
peerIPs = append(peerIPs, allowedNetIP)
|
peerIPs = append(peerIPs, allowedNetIP)
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := e.createPeerConn(peerKey, peerIPs)
|
conn, err := e.createPeerConn(peerKey, peerIPs, peerConfig.AgentVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("create peer connection: %w", err)
|
return fmt.Errorf("create peer connection: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ok := e.peerStore.AddPeerConn(peerKey, conn); !ok {
|
err = e.statusRecorder.AddPeer(peerKey, peerConfig.Fqdn, peerIPs[0].Addr().String())
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("error adding peer %s to status recorder, got error: %v", peerKey, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if exists := e.connMgr.AddPeerConn(e.ctx, peerKey, conn); exists {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
return fmt.Errorf("peer already exists: %s", peerKey)
|
return fmt.Errorf("peer already exists: %s", peerKey)
|
||||||
}
|
}
|
||||||
@ -1205,17 +1235,10 @@ func (e *Engine) addNewPeer(peerConfig *mgmProto.RemotePeerConfig) error {
|
|||||||
conn.AddBeforeAddPeerHook(e.beforePeerHook)
|
conn.AddBeforeAddPeerHook(e.beforePeerHook)
|
||||||
conn.AddAfterRemovePeerHook(e.afterPeerHook)
|
conn.AddAfterRemovePeerHook(e.afterPeerHook)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = e.statusRecorder.AddPeer(peerKey, peerConfig.Fqdn)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("error adding peer %s to status recorder, got error: %v", peerKey, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
conn.Open()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Engine) createPeerConn(pubKey string, allowedIPs []netip.Prefix) (*peer.Conn, error) {
|
func (e *Engine) createPeerConn(pubKey string, allowedIPs []netip.Prefix, agentVersion string) (*peer.Conn, error) {
|
||||||
log.Debugf("creating peer connection %s", pubKey)
|
log.Debugf("creating peer connection %s", pubKey)
|
||||||
|
|
||||||
wgConfig := peer.WgConfig{
|
wgConfig := peer.WgConfig{
|
||||||
@ -1229,11 +1252,12 @@ func (e *Engine) createPeerConn(pubKey string, allowedIPs []netip.Prefix) (*peer
|
|||||||
// randomize connection timeout
|
// randomize connection timeout
|
||||||
timeout := time.Duration(rand.Intn(PeerConnectionTimeoutMax-PeerConnectionTimeoutMin)+PeerConnectionTimeoutMin) * time.Millisecond
|
timeout := time.Duration(rand.Intn(PeerConnectionTimeoutMax-PeerConnectionTimeoutMin)+PeerConnectionTimeoutMin) * time.Millisecond
|
||||||
config := peer.ConnConfig{
|
config := peer.ConnConfig{
|
||||||
Key: pubKey,
|
Key: pubKey,
|
||||||
LocalKey: e.config.WgPrivateKey.PublicKey().String(),
|
LocalKey: e.config.WgPrivateKey.PublicKey().String(),
|
||||||
Timeout: timeout,
|
AgentVersion: agentVersion,
|
||||||
WgConfig: wgConfig,
|
Timeout: timeout,
|
||||||
LocalWgPort: e.config.WgPort,
|
WgConfig: wgConfig,
|
||||||
|
LocalWgPort: e.config.WgPort,
|
||||||
RosenpassConfig: peer.RosenpassConfig{
|
RosenpassConfig: peer.RosenpassConfig{
|
||||||
PubKey: e.getRosenpassPubKey(),
|
PubKey: e.getRosenpassPubKey(),
|
||||||
Addr: e.getRosenpassAddr(),
|
Addr: e.getRosenpassAddr(),
|
||||||
@ -1249,7 +1273,16 @@ func (e *Engine) createPeerConn(pubKey string, allowedIPs []netip.Prefix) (*peer
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
peerConn, err := peer.NewConn(e.ctx, config, e.statusRecorder, e.signaler, e.mobileDep.IFaceDiscover, e.relayManager, e.srWatcher, e.connSemaphore)
|
serviceDependencies := peer.ServiceDependencies{
|
||||||
|
StatusRecorder: e.statusRecorder,
|
||||||
|
Signaler: e.signaler,
|
||||||
|
IFaceDiscover: e.mobileDep.IFaceDiscover,
|
||||||
|
RelayManager: e.relayManager,
|
||||||
|
SrWatcher: e.srWatcher,
|
||||||
|
Semaphore: e.connSemaphore,
|
||||||
|
PeerConnDispatcher: e.peerConnDispatcher,
|
||||||
|
}
|
||||||
|
peerConn, err := peer.NewConn(config, serviceDependencies)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -1270,7 +1303,7 @@ func (e *Engine) receiveSignalEvents() {
|
|||||||
e.syncMsgMux.Lock()
|
e.syncMsgMux.Lock()
|
||||||
defer e.syncMsgMux.Unlock()
|
defer e.syncMsgMux.Unlock()
|
||||||
|
|
||||||
conn, ok := e.peerStore.PeerConn(msg.Key)
|
conn, ok := e.connMgr.OnSignalMsg(e.ctx, msg.Key)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("wrongly addressed message %s", msg.Key)
|
return fmt.Errorf("wrongly addressed message %s", msg.Key)
|
||||||
}
|
}
|
||||||
@ -1578,13 +1611,39 @@ func (e *Engine) getRosenpassAddr() string {
|
|||||||
// RunHealthProbes executes health checks for Signal, Management, Relay and WireGuard services
|
// RunHealthProbes executes health checks for Signal, Management, Relay and WireGuard services
|
||||||
// and updates the status recorder with the latest states.
|
// and updates the status recorder with the latest states.
|
||||||
func (e *Engine) RunHealthProbes() bool {
|
func (e *Engine) RunHealthProbes() bool {
|
||||||
|
e.syncMsgMux.Lock()
|
||||||
|
|
||||||
signalHealthy := e.signal.IsHealthy()
|
signalHealthy := e.signal.IsHealthy()
|
||||||
log.Debugf("signal health check: healthy=%t", signalHealthy)
|
log.Debugf("signal health check: healthy=%t", signalHealthy)
|
||||||
|
|
||||||
managementHealthy := e.mgmClient.IsHealthy()
|
managementHealthy := e.mgmClient.IsHealthy()
|
||||||
log.Debugf("management health check: healthy=%t", managementHealthy)
|
log.Debugf("management health check: healthy=%t", managementHealthy)
|
||||||
|
|
||||||
results := append(e.probeSTUNs(), e.probeTURNs()...)
|
stuns := slices.Clone(e.STUNs)
|
||||||
|
turns := slices.Clone(e.TURNs)
|
||||||
|
|
||||||
|
if e.wgInterface != nil {
|
||||||
|
stats, err := e.wgInterface.GetStats()
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("failed to get wireguard stats: %v", err)
|
||||||
|
e.syncMsgMux.Unlock()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, key := range e.peerStore.PeersPubKey() {
|
||||||
|
// wgStats could be zero value, in which case we just reset the stats
|
||||||
|
wgStats, ok := stats[key]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := e.statusRecorder.UpdateWireGuardPeerState(key, wgStats); err != nil {
|
||||||
|
log.Debugf("failed to update wg stats for peer %s: %s", key, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
e.syncMsgMux.Unlock()
|
||||||
|
|
||||||
|
results := e.probeICE(stuns, turns)
|
||||||
e.statusRecorder.UpdateRelayStates(results)
|
e.statusRecorder.UpdateRelayStates(results)
|
||||||
|
|
||||||
relayHealthy := true
|
relayHealthy := true
|
||||||
@ -1596,37 +1655,16 @@ func (e *Engine) RunHealthProbes() bool {
|
|||||||
}
|
}
|
||||||
log.Debugf("relay health check: healthy=%t", relayHealthy)
|
log.Debugf("relay health check: healthy=%t", relayHealthy)
|
||||||
|
|
||||||
for _, key := range e.peerStore.PeersPubKey() {
|
|
||||||
wgStats, err := e.wgInterface.GetStats(key)
|
|
||||||
if err != nil {
|
|
||||||
log.Debugf("failed to get wg stats for peer %s: %s", key, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// wgStats could be zero value, in which case we just reset the stats
|
|
||||||
if err := e.statusRecorder.UpdateWireGuardPeerState(key, wgStats); err != nil {
|
|
||||||
log.Debugf("failed to update wg stats for peer %s: %s", key, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
allHealthy := signalHealthy && managementHealthy && relayHealthy
|
allHealthy := signalHealthy && managementHealthy && relayHealthy
|
||||||
log.Debugf("all health checks completed: healthy=%t", allHealthy)
|
log.Debugf("all health checks completed: healthy=%t", allHealthy)
|
||||||
return allHealthy
|
return allHealthy
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Engine) probeSTUNs() []relay.ProbeResult {
|
func (e *Engine) probeICE(stuns, turns []*stun.URI) []relay.ProbeResult {
|
||||||
e.syncMsgMux.Lock()
|
return append(
|
||||||
stuns := slices.Clone(e.STUNs)
|
relay.ProbeAll(e.ctx, relay.ProbeSTUN, stuns),
|
||||||
e.syncMsgMux.Unlock()
|
relay.ProbeAll(e.ctx, relay.ProbeSTUN, turns)...,
|
||||||
|
)
|
||||||
return relay.ProbeAll(e.ctx, relay.ProbeSTUN, stuns)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Engine) probeTURNs() []relay.ProbeResult {
|
|
||||||
e.syncMsgMux.Lock()
|
|
||||||
turns := slices.Clone(e.TURNs)
|
|
||||||
e.syncMsgMux.Unlock()
|
|
||||||
|
|
||||||
return relay.ProbeAll(e.ctx, relay.ProbeTURN, turns)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// restartEngine restarts the engine by cancelling the client context
|
// restartEngine restarts the engine by cancelling the client context
|
||||||
@ -1813,21 +1851,21 @@ func (e *Engine) Address() (netip.Addr, error) {
|
|||||||
return ip.Unmap(), nil
|
return ip.Unmap(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Engine) updateForwardRules(rules []*mgmProto.ForwardingRule) error {
|
func (e *Engine) updateForwardRules(rules []*mgmProto.ForwardingRule) ([]firewallManager.ForwardRule, error) {
|
||||||
if e.firewall == nil {
|
if e.firewall == nil {
|
||||||
log.Warn("firewall is disabled, not updating forwarding rules")
|
log.Warn("firewall is disabled, not updating forwarding rules")
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(rules) == 0 {
|
if len(rules) == 0 {
|
||||||
if e.ingressGatewayMgr == nil {
|
if e.ingressGatewayMgr == nil {
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err := e.ingressGatewayMgr.Close()
|
err := e.ingressGatewayMgr.Close()
|
||||||
e.ingressGatewayMgr = nil
|
e.ingressGatewayMgr = nil
|
||||||
e.statusRecorder.SetIngressGwMgr(nil)
|
e.statusRecorder.SetIngressGwMgr(nil)
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.ingressGatewayMgr == nil {
|
if e.ingressGatewayMgr == nil {
|
||||||
@ -1878,7 +1916,33 @@ func (e *Engine) updateForwardRules(rules []*mgmProto.ForwardingRule) error {
|
|||||||
log.Errorf("failed to update forwarding rules: %v", err)
|
log.Errorf("failed to update forwarding rules: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nberrors.FormatErrorOrNil(merr)
|
return forwardingRules, nberrors.FormatErrorOrNil(merr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Engine) toExcludedLazyPeers(routes []*route.Route, rules []firewallManager.ForwardRule, peers []*mgmProto.RemotePeerConfig) []string {
|
||||||
|
excludedPeers := make([]string, 0)
|
||||||
|
for _, r := range routes {
|
||||||
|
if r.Peer == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Infof("exclude router peer from lazy connection: %s", r.Peer)
|
||||||
|
excludedPeers = append(excludedPeers, r.Peer)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, r := range rules {
|
||||||
|
ip := r.TranslatedAddress
|
||||||
|
for _, p := range peers {
|
||||||
|
for _, allowedIP := range p.GetAllowedIps() {
|
||||||
|
if allowedIP != ip.String() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Infof("exclude forwarder peer from lazy connection: %s", p.GetWgPubKey())
|
||||||
|
excludedPeers = append(excludedPeers, p.GetWgPubKey())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return excludedPeers
|
||||||
}
|
}
|
||||||
|
|
||||||
// isChecksEqual checks if two slices of checks are equal.
|
// isChecksEqual checks if two slices of checks are equal.
|
||||||
|
@ -28,8 +28,6 @@ import (
|
|||||||
|
|
||||||
"github.com/netbirdio/management-integrations/integrations"
|
"github.com/netbirdio/management-integrations/integrations"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/management/server/types"
|
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/iface"
|
"github.com/netbirdio/netbird/client/iface"
|
||||||
"github.com/netbirdio/netbird/client/iface/bind"
|
"github.com/netbirdio/netbird/client/iface/bind"
|
||||||
"github.com/netbirdio/netbird/client/iface/configurer"
|
"github.com/netbirdio/netbird/client/iface/configurer"
|
||||||
@ -38,6 +36,7 @@ import (
|
|||||||
"github.com/netbirdio/netbird/client/iface/wgproxy"
|
"github.com/netbirdio/netbird/client/iface/wgproxy"
|
||||||
"github.com/netbirdio/netbird/client/internal/dns"
|
"github.com/netbirdio/netbird/client/internal/dns"
|
||||||
"github.com/netbirdio/netbird/client/internal/peer"
|
"github.com/netbirdio/netbird/client/internal/peer"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/peer/dispatcher"
|
||||||
"github.com/netbirdio/netbird/client/internal/peer/guard"
|
"github.com/netbirdio/netbird/client/internal/peer/guard"
|
||||||
icemaker "github.com/netbirdio/netbird/client/internal/peer/ice"
|
icemaker "github.com/netbirdio/netbird/client/internal/peer/ice"
|
||||||
"github.com/netbirdio/netbird/client/internal/routemanager"
|
"github.com/netbirdio/netbird/client/internal/routemanager"
|
||||||
@ -53,6 +52,7 @@ import (
|
|||||||
"github.com/netbirdio/netbird/management/server/settings"
|
"github.com/netbirdio/netbird/management/server/settings"
|
||||||
"github.com/netbirdio/netbird/management/server/store"
|
"github.com/netbirdio/netbird/management/server/store"
|
||||||
"github.com/netbirdio/netbird/management/server/telemetry"
|
"github.com/netbirdio/netbird/management/server/telemetry"
|
||||||
|
"github.com/netbirdio/netbird/management/server/types"
|
||||||
relayClient "github.com/netbirdio/netbird/relay/client"
|
relayClient "github.com/netbirdio/netbird/relay/client"
|
||||||
"github.com/netbirdio/netbird/route"
|
"github.com/netbirdio/netbird/route"
|
||||||
signal "github.com/netbirdio/netbird/signal/client"
|
signal "github.com/netbirdio/netbird/signal/client"
|
||||||
@ -93,7 +93,7 @@ type MockWGIface struct {
|
|||||||
GetFilterFunc func() device.PacketFilter
|
GetFilterFunc func() device.PacketFilter
|
||||||
GetDeviceFunc func() *device.FilteredDevice
|
GetDeviceFunc func() *device.FilteredDevice
|
||||||
GetWGDeviceFunc func() *wgdevice.Device
|
GetWGDeviceFunc func() *wgdevice.Device
|
||||||
GetStatsFunc func(peerKey string) (configurer.WGStats, error)
|
GetStatsFunc func() (map[string]configurer.WGStats, error)
|
||||||
GetInterfaceGUIDStringFunc func() (string, error)
|
GetInterfaceGUIDStringFunc func() (string, error)
|
||||||
GetProxyFunc func() wgproxy.Proxy
|
GetProxyFunc func() wgproxy.Proxy
|
||||||
GetNetFunc func() *netstack.Net
|
GetNetFunc func() *netstack.Net
|
||||||
@ -171,8 +171,8 @@ func (m *MockWGIface) GetWGDevice() *wgdevice.Device {
|
|||||||
return m.GetWGDeviceFunc()
|
return m.GetWGDeviceFunc()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockWGIface) GetStats(peerKey string) (configurer.WGStats, error) {
|
func (m *MockWGIface) GetStats() (map[string]configurer.WGStats, error) {
|
||||||
return m.GetStatsFunc(peerKey)
|
return m.GetStatsFunc()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockWGIface) GetProxy() wgproxy.Proxy {
|
func (m *MockWGIface) GetProxy() wgproxy.Proxy {
|
||||||
@ -378,6 +378,9 @@ func TestEngine_UpdateNetworkMap(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
UpdatePeerFunc: func(peerKey string, allowedIps []netip.Prefix, keepAlive time.Duration, endpoint *net.UDPAddr, preSharedKey *wgtypes.Key) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
engine.wgInterface = wgIface
|
engine.wgInterface = wgIface
|
||||||
engine.routeManager = routemanager.NewManager(routemanager.ManagerConfig{
|
engine.routeManager = routemanager.NewManager(routemanager.ManagerConfig{
|
||||||
@ -400,6 +403,8 @@ func TestEngine_UpdateNetworkMap(t *testing.T) {
|
|||||||
engine.udpMux = bind.NewUniversalUDPMuxDefault(bind.UniversalUDPMuxParams{UDPConn: conn})
|
engine.udpMux = bind.NewUniversalUDPMuxDefault(bind.UniversalUDPMuxParams{UDPConn: conn})
|
||||||
engine.ctx = ctx
|
engine.ctx = ctx
|
||||||
engine.srWatcher = guard.NewSRWatcher(nil, nil, nil, icemaker.Config{})
|
engine.srWatcher = guard.NewSRWatcher(nil, nil, nil, icemaker.Config{})
|
||||||
|
engine.connMgr = NewConnMgr(engine.config, engine.statusRecorder, engine.peerStore, wgIface, dispatcher.NewConnectionDispatcher())
|
||||||
|
engine.connMgr.Start(ctx)
|
||||||
|
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
name string
|
name string
|
||||||
@ -770,6 +775,8 @@ func TestEngine_UpdateNetworkMapWithRoutes(t *testing.T) {
|
|||||||
|
|
||||||
engine.routeManager = mockRouteManager
|
engine.routeManager = mockRouteManager
|
||||||
engine.dnsServer = &dns.MockServer{}
|
engine.dnsServer = &dns.MockServer{}
|
||||||
|
engine.connMgr = NewConnMgr(engine.config, engine.statusRecorder, engine.peerStore, engine.wgInterface, dispatcher.NewConnectionDispatcher())
|
||||||
|
engine.connMgr.Start(ctx)
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
exitErr := engine.Stop()
|
exitErr := engine.Stop()
|
||||||
@ -966,6 +973,8 @@ func TestEngine_UpdateNetworkMapWithDNSUpdate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
engine.dnsServer = mockDNSServer
|
engine.dnsServer = mockDNSServer
|
||||||
|
engine.connMgr = NewConnMgr(engine.config, engine.statusRecorder, engine.peerStore, engine.wgInterface, dispatcher.NewConnectionDispatcher())
|
||||||
|
engine.connMgr.Start(ctx)
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
exitErr := engine.Stop()
|
exitErr := engine.Stop()
|
||||||
@ -1476,7 +1485,7 @@ func getConnectedPeers(e *Engine) int {
|
|||||||
i := 0
|
i := 0
|
||||||
for _, id := range e.peerStore.PeersPubKey() {
|
for _, id := range e.peerStore.PeersPubKey() {
|
||||||
conn, _ := e.peerStore.PeerConn(id)
|
conn, _ := e.peerStore.PeerConn(id)
|
||||||
if conn.Status() == peer.StatusConnected {
|
if conn.IsConnected() {
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,6 @@ type wgIfaceBase interface {
|
|||||||
GetFilter() device.PacketFilter
|
GetFilter() device.PacketFilter
|
||||||
GetDevice() *device.FilteredDevice
|
GetDevice() *device.FilteredDevice
|
||||||
GetWGDevice() *wgdevice.Device
|
GetWGDevice() *wgdevice.Device
|
||||||
GetStats(peerKey string) (configurer.WGStats, error)
|
GetStats() (map[string]configurer.WGStats, error)
|
||||||
GetNet() *netstack.Net
|
GetNet() *netstack.Net
|
||||||
}
|
}
|
||||||
|
9
client/internal/lazyconn/activity/listen_ip.go
Normal file
9
client/internal/lazyconn/activity/listen_ip.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
//go:build !linux || android
|
||||||
|
|
||||||
|
package activity
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
var (
|
||||||
|
listenIP = net.IP{127, 0, 0, 1}
|
||||||
|
)
|
10
client/internal/lazyconn/activity/listen_ip_linux.go
Normal file
10
client/internal/lazyconn/activity/listen_ip_linux.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
//go:build !android
|
||||||
|
|
||||||
|
package activity
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
var (
|
||||||
|
// use this ip to avoid eBPF proxy congestion
|
||||||
|
listenIP = net.IP{127, 0, 1, 1}
|
||||||
|
)
|
106
client/internal/lazyconn/activity/listener.go
Normal file
106
client/internal/lazyconn/activity/listener.go
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
package activity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/internal/lazyconn"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Listener it is not a thread safe implementation, do not call Close before ReadPackets. It will cause blocking
|
||||||
|
type Listener struct {
|
||||||
|
wgIface lazyconn.WGIface
|
||||||
|
peerCfg lazyconn.PeerConfig
|
||||||
|
conn *net.UDPConn
|
||||||
|
endpoint *net.UDPAddr
|
||||||
|
done sync.Mutex
|
||||||
|
|
||||||
|
isClosed atomic.Bool // use to avoid error log when closing the listener
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewListener(wgIface lazyconn.WGIface, cfg lazyconn.PeerConfig) (*Listener, error) {
|
||||||
|
d := &Listener{
|
||||||
|
wgIface: wgIface,
|
||||||
|
peerCfg: cfg,
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := d.newConn()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to creating activity listener: %v", err)
|
||||||
|
}
|
||||||
|
d.conn = conn
|
||||||
|
d.endpoint = conn.LocalAddr().(*net.UDPAddr)
|
||||||
|
|
||||||
|
if err := d.createEndpoint(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
d.done.Lock()
|
||||||
|
cfg.Log.Infof("created activity listener: %s", conn.LocalAddr().(*net.UDPAddr).String())
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Listener) ReadPackets() {
|
||||||
|
for {
|
||||||
|
n, remoteAddr, err := d.conn.ReadFromUDP(make([]byte, 1))
|
||||||
|
if err != nil {
|
||||||
|
if d.isClosed.Load() {
|
||||||
|
d.peerCfg.Log.Debugf("exit from activity listener")
|
||||||
|
} else {
|
||||||
|
d.peerCfg.Log.Errorf("failed to read from activity listener: %s", err)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < 1 {
|
||||||
|
d.peerCfg.Log.Warnf("received %d bytes from %s, too short", n, remoteAddr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.removeEndpoint(); err != nil {
|
||||||
|
d.peerCfg.Log.Errorf("failed to remove endpoint: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = d.conn.Close() // do not care err because some cases it will return "use of closed network connection"
|
||||||
|
d.done.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Listener) Close() {
|
||||||
|
d.peerCfg.Log.Infof("closing listener: %s", d.conn.LocalAddr().String())
|
||||||
|
d.isClosed.Store(true)
|
||||||
|
|
||||||
|
if err := d.conn.Close(); err != nil {
|
||||||
|
d.peerCfg.Log.Errorf("failed to close UDP listener: %s", err)
|
||||||
|
}
|
||||||
|
d.done.Lock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Listener) removeEndpoint() error {
|
||||||
|
d.peerCfg.Log.Debugf("removing lazy endpoint: %s", d.endpoint.String())
|
||||||
|
return d.wgIface.RemovePeer(d.peerCfg.PublicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Listener) createEndpoint() error {
|
||||||
|
d.peerCfg.Log.Debugf("creating lazy endpoint: %s", d.endpoint.String())
|
||||||
|
return d.wgIface.UpdatePeer(d.peerCfg.PublicKey, d.peerCfg.AllowedIPs, 0, d.endpoint, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Listener) newConn() (*net.UDPConn, error) {
|
||||||
|
addr := &net.UDPAddr{
|
||||||
|
Port: 0,
|
||||||
|
IP: listenIP,
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := net.ListenUDP("udp", addr)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to create activity listener on %s: %s", addr, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
41
client/internal/lazyconn/activity/listener_test.go
Normal file
41
client/internal/lazyconn/activity/listener_test.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package activity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/internal/lazyconn"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewListener(t *testing.T) {
|
||||||
|
peer := &MocPeer{
|
||||||
|
PeerID: "examplePublicKey1",
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := lazyconn.PeerConfig{
|
||||||
|
PublicKey: peer.PeerID,
|
||||||
|
PeerConnID: peer.ConnID(),
|
||||||
|
Log: log.WithField("peer", "examplePublicKey1"),
|
||||||
|
}
|
||||||
|
|
||||||
|
l, err := NewListener(MocWGIface{}, cfg)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create listener: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
chanClosed := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
defer close(chanClosed)
|
||||||
|
l.ReadPackets()
|
||||||
|
}()
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
l.Close()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-chanClosed:
|
||||||
|
case <-time.After(time.Second):
|
||||||
|
}
|
||||||
|
}
|
95
client/internal/lazyconn/activity/manager.go
Normal file
95
client/internal/lazyconn/activity/manager.go
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
package activity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/internal/lazyconn"
|
||||||
|
peerid "github.com/netbirdio/netbird/client/internal/peer/id"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Manager struct {
|
||||||
|
OnActivityChan chan peerid.ConnID
|
||||||
|
|
||||||
|
wgIface lazyconn.WGIface
|
||||||
|
|
||||||
|
peers map[peerid.ConnID]*Listener
|
||||||
|
done chan struct{}
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewManager(wgIface lazyconn.WGIface) *Manager {
|
||||||
|
m := &Manager{
|
||||||
|
OnActivityChan: make(chan peerid.ConnID, 1),
|
||||||
|
wgIface: wgIface,
|
||||||
|
peers: make(map[peerid.ConnID]*Listener),
|
||||||
|
done: make(chan struct{}),
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) MonitorPeerActivity(peerCfg lazyconn.PeerConfig) error {
|
||||||
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
|
||||||
|
if _, ok := m.peers[peerCfg.PeerConnID]; ok {
|
||||||
|
log.Warnf("activity listener already exists for: %s", peerCfg.PublicKey)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
listener, err := NewListener(m.wgIface, peerCfg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m.peers[peerCfg.PeerConnID] = listener
|
||||||
|
|
||||||
|
go m.waitForTraffic(listener, peerCfg.PeerConnID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) RemovePeer(log *log.Entry, peerConnID peerid.ConnID) {
|
||||||
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
|
||||||
|
listener, ok := m.peers[peerConnID]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Debugf("removing activity listener")
|
||||||
|
delete(m.peers, peerConnID)
|
||||||
|
listener.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) Close() {
|
||||||
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
|
||||||
|
close(m.done)
|
||||||
|
for peerID, listener := range m.peers {
|
||||||
|
delete(m.peers, peerID)
|
||||||
|
listener.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) waitForTraffic(listener *Listener, peerConnID peerid.ConnID) {
|
||||||
|
listener.ReadPackets()
|
||||||
|
|
||||||
|
m.mu.Lock()
|
||||||
|
if _, ok := m.peers[peerConnID]; !ok {
|
||||||
|
m.mu.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
delete(m.peers, peerConnID)
|
||||||
|
m.mu.Unlock()
|
||||||
|
|
||||||
|
m.notify(peerConnID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) notify(peerConnID peerid.ConnID) {
|
||||||
|
select {
|
||||||
|
case <-m.done:
|
||||||
|
case m.OnActivityChan <- peerConnID:
|
||||||
|
}
|
||||||
|
}
|
162
client/internal/lazyconn/activity/manager_test.go
Normal file
162
client/internal/lazyconn/activity/manager_test.go
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
package activity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/internal/lazyconn"
|
||||||
|
peerid "github.com/netbirdio/netbird/client/internal/peer/id"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MocPeer struct {
|
||||||
|
PeerID string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MocPeer) ConnID() peerid.ConnID {
|
||||||
|
return peerid.ConnID(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
type MocWGIface struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MocWGIface) RemovePeer(string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MocWGIface) UpdatePeer(string, []netip.Prefix, time.Duration, *net.UDPAddr, *wgtypes.Key) error {
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestManager_MonitorPeerActivity(t *testing.T) {
|
||||||
|
mocWgInterface := &MocWGIface{}
|
||||||
|
|
||||||
|
peer1 := &MocPeer{
|
||||||
|
PeerID: "examplePublicKey1",
|
||||||
|
}
|
||||||
|
mgr := NewManager(mocWgInterface)
|
||||||
|
defer mgr.Close()
|
||||||
|
peerCfg1 := lazyconn.PeerConfig{
|
||||||
|
PublicKey: peer1.PeerID,
|
||||||
|
PeerConnID: peer1.ConnID(),
|
||||||
|
Log: log.WithField("peer", "examplePublicKey1"),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mgr.MonitorPeerActivity(peerCfg1); err != nil {
|
||||||
|
t.Fatalf("failed to monitor peer activity: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := trigger(mgr.peers[peerCfg1.PeerConnID].conn.LocalAddr().String()); err != nil {
|
||||||
|
t.Fatalf("failed to trigger activity: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case peerConnID := <-mgr.OnActivityChan:
|
||||||
|
if peerConnID != peerCfg1.PeerConnID {
|
||||||
|
t.Fatalf("unexpected peerConnID: %v", peerConnID)
|
||||||
|
}
|
||||||
|
case <-time.After(1 * time.Second):
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestManager_RemovePeerActivity(t *testing.T) {
|
||||||
|
mocWgInterface := &MocWGIface{}
|
||||||
|
|
||||||
|
peer1 := &MocPeer{
|
||||||
|
PeerID: "examplePublicKey1",
|
||||||
|
}
|
||||||
|
mgr := NewManager(mocWgInterface)
|
||||||
|
defer mgr.Close()
|
||||||
|
|
||||||
|
peerCfg1 := lazyconn.PeerConfig{
|
||||||
|
PublicKey: peer1.PeerID,
|
||||||
|
PeerConnID: peer1.ConnID(),
|
||||||
|
Log: log.WithField("peer", "examplePublicKey1"),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mgr.MonitorPeerActivity(peerCfg1); err != nil {
|
||||||
|
t.Fatalf("failed to monitor peer activity: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
addr := mgr.peers[peerCfg1.PeerConnID].conn.LocalAddr().String()
|
||||||
|
|
||||||
|
mgr.RemovePeer(peerCfg1.Log, peerCfg1.PeerConnID)
|
||||||
|
|
||||||
|
if err := trigger(addr); err != nil {
|
||||||
|
t.Fatalf("failed to trigger activity: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-mgr.OnActivityChan:
|
||||||
|
t.Fatal("should not have active activity")
|
||||||
|
case <-time.After(1 * time.Second):
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestManager_MultiPeerActivity(t *testing.T) {
|
||||||
|
mocWgInterface := &MocWGIface{}
|
||||||
|
|
||||||
|
peer1 := &MocPeer{
|
||||||
|
PeerID: "examplePublicKey1",
|
||||||
|
}
|
||||||
|
mgr := NewManager(mocWgInterface)
|
||||||
|
defer mgr.Close()
|
||||||
|
|
||||||
|
peerCfg1 := lazyconn.PeerConfig{
|
||||||
|
PublicKey: peer1.PeerID,
|
||||||
|
PeerConnID: peer1.ConnID(),
|
||||||
|
Log: log.WithField("peer", "examplePublicKey1"),
|
||||||
|
}
|
||||||
|
|
||||||
|
peer2 := &MocPeer{}
|
||||||
|
peerCfg2 := lazyconn.PeerConfig{
|
||||||
|
PublicKey: peer2.PeerID,
|
||||||
|
PeerConnID: peer2.ConnID(),
|
||||||
|
Log: log.WithField("peer", "examplePublicKey2"),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mgr.MonitorPeerActivity(peerCfg1); err != nil {
|
||||||
|
t.Fatalf("failed to monitor peer activity: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mgr.MonitorPeerActivity(peerCfg2); err != nil {
|
||||||
|
t.Fatalf("failed to monitor peer activity: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := trigger(mgr.peers[peerCfg1.PeerConnID].conn.LocalAddr().String()); err != nil {
|
||||||
|
t.Fatalf("failed to trigger activity: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := trigger(mgr.peers[peerCfg2.PeerConnID].conn.LocalAddr().String()); err != nil {
|
||||||
|
t.Fatalf("failed to trigger activity: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
select {
|
||||||
|
case <-mgr.OnActivityChan:
|
||||||
|
case <-time.After(1 * time.Second):
|
||||||
|
t.Fatal("timed out waiting for activity")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func trigger(addr string) error {
|
||||||
|
// Create a connection to the destination UDP address and port
|
||||||
|
conn, err := net.Dial("udp", addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
// Write the bytes to the UDP connection
|
||||||
|
_, err = conn.Write([]byte{0x01, 0x02, 0x03, 0x04, 0x05})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
32
client/internal/lazyconn/doc.go
Normal file
32
client/internal/lazyconn/doc.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
Package lazyconn provides mechanisms for managing lazy connections, which activate on demand to optimize resource usage and establish connections efficiently.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The package includes a `Manager` component responsible for:
|
||||||
|
- Managing lazy connections activated on-demand
|
||||||
|
- Managing inactivity monitors for lazy connections (based on peer disconnection events)
|
||||||
|
- Maintaining a list of excluded peers that should always have permanent connections
|
||||||
|
- Handling remote peer connection initiatives based on peer signaling
|
||||||
|
|
||||||
|
## Thread-Safe Operations
|
||||||
|
|
||||||
|
The `Manager` ensures thread safety across multiple operations, categorized by caller:
|
||||||
|
|
||||||
|
- **Engine (single goroutine)**:
|
||||||
|
- `AddPeer`: Adds a peer to the connection manager.
|
||||||
|
- `RemovePeer`: Removes a peer from the connection manager.
|
||||||
|
- `ActivatePeer`: Activates a lazy connection for a peer. This come from Signal client
|
||||||
|
- `ExcludePeer`: Marks peers for a permanent connection. Like router peers and other peers that should always have a connection.
|
||||||
|
|
||||||
|
- **Connection Dispatcher (any peer routine)**:
|
||||||
|
- `onPeerConnected`: Suspend the inactivity monitor for an active peer connection.
|
||||||
|
- `onPeerDisconnected`: Starts the inactivity monitor for a disconnected peer.
|
||||||
|
|
||||||
|
- **Activity Manager**:
|
||||||
|
- `onPeerActivity`: Run peer.Open(context).
|
||||||
|
|
||||||
|
- **Inactivity Monitor**:
|
||||||
|
- `onPeerInactivityTimedOut`: Close peer connection and restart activity monitor.
|
||||||
|
*/
|
||||||
|
package lazyconn
|
26
client/internal/lazyconn/env.go
Normal file
26
client/internal/lazyconn/env.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package lazyconn
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
EnvEnableLazyConn = "NB_ENABLE_EXPERIMENTAL_LAZY_CONN"
|
||||||
|
EnvInactivityThreshold = "NB_LAZY_CONN_INACTIVITY_THRESHOLD"
|
||||||
|
)
|
||||||
|
|
||||||
|
func IsLazyConnEnabledByEnv() bool {
|
||||||
|
val := os.Getenv(EnvEnableLazyConn)
|
||||||
|
if val == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
enabled, err := strconv.ParseBool(val)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("failed to parse %s: %v", EnvEnableLazyConn, err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return enabled
|
||||||
|
}
|
70
client/internal/lazyconn/inactivity/inactivity.go
Normal file
70
client/internal/lazyconn/inactivity/inactivity.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package inactivity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
peer "github.com/netbirdio/netbird/client/internal/peer/id"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
DefaultInactivityThreshold = 60 * time.Minute // idle after 1 hour inactivity
|
||||||
|
MinimumInactivityThreshold = 3 * time.Minute
|
||||||
|
)
|
||||||
|
|
||||||
|
type Monitor struct {
|
||||||
|
id peer.ConnID
|
||||||
|
timer *time.Timer
|
||||||
|
cancel context.CancelFunc
|
||||||
|
inactivityThreshold time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInactivityMonitor(peerID peer.ConnID, threshold time.Duration) *Monitor {
|
||||||
|
i := &Monitor{
|
||||||
|
id: peerID,
|
||||||
|
timer: time.NewTimer(0),
|
||||||
|
inactivityThreshold: threshold,
|
||||||
|
}
|
||||||
|
i.timer.Stop()
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Monitor) Start(ctx context.Context, timeoutChan chan peer.ConnID) {
|
||||||
|
i.timer.Reset(i.inactivityThreshold)
|
||||||
|
defer i.timer.Stop()
|
||||||
|
|
||||||
|
ctx, i.cancel = context.WithCancel(ctx)
|
||||||
|
defer func() {
|
||||||
|
defer i.cancel()
|
||||||
|
select {
|
||||||
|
case <-i.timer.C:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-i.timer.C:
|
||||||
|
select {
|
||||||
|
case timeoutChan <- i.id:
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Monitor) Stop() {
|
||||||
|
if i.cancel == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
i.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Monitor) PauseTimer() {
|
||||||
|
i.timer.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Monitor) ResetTimer() {
|
||||||
|
i.timer.Reset(i.inactivityThreshold)
|
||||||
|
}
|
156
client/internal/lazyconn/inactivity/inactivity_test.go
Normal file
156
client/internal/lazyconn/inactivity/inactivity_test.go
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
package inactivity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
peerid "github.com/netbirdio/netbird/client/internal/peer/id"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MocPeer struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MocPeer) ConnID() peerid.ConnID {
|
||||||
|
return peerid.ConnID(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInactivityMonitor(t *testing.T) {
|
||||||
|
tCtx, testTimeoutCancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||||
|
defer testTimeoutCancel()
|
||||||
|
|
||||||
|
p := &MocPeer{}
|
||||||
|
im := NewInactivityMonitor(p.ConnID(), time.Second*2)
|
||||||
|
|
||||||
|
timeoutChan := make(chan peerid.ConnID)
|
||||||
|
|
||||||
|
exitChan := make(chan struct{})
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer close(exitChan)
|
||||||
|
im.Start(tCtx, timeoutChan)
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-timeoutChan:
|
||||||
|
case <-tCtx.Done():
|
||||||
|
t.Fatal("timeout")
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-exitChan:
|
||||||
|
case <-tCtx.Done():
|
||||||
|
t.Fatal("timeout")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReuseInactivityMonitor(t *testing.T) {
|
||||||
|
p := &MocPeer{}
|
||||||
|
im := NewInactivityMonitor(p.ConnID(), time.Second*2)
|
||||||
|
|
||||||
|
timeoutChan := make(chan peerid.ConnID)
|
||||||
|
|
||||||
|
for i := 2; i > 0; i-- {
|
||||||
|
exitChan := make(chan struct{})
|
||||||
|
|
||||||
|
testTimeoutCtx, testTimeoutCancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer close(exitChan)
|
||||||
|
im.Start(testTimeoutCtx, timeoutChan)
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-timeoutChan:
|
||||||
|
case <-testTimeoutCtx.Done():
|
||||||
|
t.Fatal("timeout")
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-exitChan:
|
||||||
|
case <-testTimeoutCtx.Done():
|
||||||
|
t.Fatal("timeout")
|
||||||
|
}
|
||||||
|
testTimeoutCancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStopInactivityMonitor(t *testing.T) {
|
||||||
|
tCtx, testTimeoutCancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||||
|
defer testTimeoutCancel()
|
||||||
|
|
||||||
|
p := &MocPeer{}
|
||||||
|
im := NewInactivityMonitor(p.ConnID(), DefaultInactivityThreshold)
|
||||||
|
|
||||||
|
timeoutChan := make(chan peerid.ConnID)
|
||||||
|
|
||||||
|
exitChan := make(chan struct{})
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer close(exitChan)
|
||||||
|
im.Start(tCtx, timeoutChan)
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
time.Sleep(3 * time.Second)
|
||||||
|
im.Stop()
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-timeoutChan:
|
||||||
|
t.Fatal("unexpected timeout")
|
||||||
|
case <-exitChan:
|
||||||
|
case <-tCtx.Done():
|
||||||
|
t.Fatal("timeout")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPauseInactivityMonitor(t *testing.T) {
|
||||||
|
tCtx, testTimeoutCancel := context.WithTimeout(context.Background(), time.Second*10)
|
||||||
|
defer testTimeoutCancel()
|
||||||
|
|
||||||
|
p := &MocPeer{}
|
||||||
|
trashHold := time.Second * 3
|
||||||
|
im := NewInactivityMonitor(p.ConnID(), trashHold)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
timeoutChan := make(chan peerid.ConnID)
|
||||||
|
|
||||||
|
exitChan := make(chan struct{})
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer close(exitChan)
|
||||||
|
im.Start(ctx, timeoutChan)
|
||||||
|
}()
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second) // grant time to start the monitor
|
||||||
|
im.PauseTimer()
|
||||||
|
|
||||||
|
// check to do not receive timeout
|
||||||
|
thresholdCtx, thresholdCancel := context.WithTimeout(context.Background(), trashHold+time.Second)
|
||||||
|
defer thresholdCancel()
|
||||||
|
select {
|
||||||
|
case <-exitChan:
|
||||||
|
t.Fatal("unexpected exit")
|
||||||
|
case <-timeoutChan:
|
||||||
|
t.Fatal("unexpected timeout")
|
||||||
|
case <-thresholdCtx.Done():
|
||||||
|
// test ok
|
||||||
|
case <-tCtx.Done():
|
||||||
|
t.Fatal("test timed out")
|
||||||
|
}
|
||||||
|
|
||||||
|
// test reset timer
|
||||||
|
im.ResetTimer()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-tCtx.Done():
|
||||||
|
t.Fatal("test timed out")
|
||||||
|
case <-exitChan:
|
||||||
|
t.Fatal("unexpected exit")
|
||||||
|
case <-timeoutChan:
|
||||||
|
// expected timeout
|
||||||
|
}
|
||||||
|
}
|
404
client/internal/lazyconn/manager/manager.go
Normal file
404
client/internal/lazyconn/manager/manager.go
Normal file
@ -0,0 +1,404 @@
|
|||||||
|
package manager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/internal/lazyconn"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/lazyconn/activity"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/lazyconn/inactivity"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/peer/dispatcher"
|
||||||
|
peerid "github.com/netbirdio/netbird/client/internal/peer/id"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/peerstore"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
watcherActivity watcherType = iota
|
||||||
|
watcherInactivity
|
||||||
|
)
|
||||||
|
|
||||||
|
type watcherType int
|
||||||
|
|
||||||
|
type managedPeer struct {
|
||||||
|
peerCfg *lazyconn.PeerConfig
|
||||||
|
expectedWatcher watcherType
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
InactivityThreshold *time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manager manages lazy connections
|
||||||
|
// It is responsible for:
|
||||||
|
// - Managing lazy connections activated on-demand
|
||||||
|
// - Managing inactivity monitors for lazy connections (based on peer disconnection events)
|
||||||
|
// - Maintaining a list of excluded peers that should always have permanent connections
|
||||||
|
// - Handling connection establishment based on peer signaling
|
||||||
|
type Manager struct {
|
||||||
|
peerStore *peerstore.Store
|
||||||
|
connStateDispatcher *dispatcher.ConnectionDispatcher
|
||||||
|
inactivityThreshold time.Duration
|
||||||
|
|
||||||
|
connStateListener *dispatcher.ConnectionListener
|
||||||
|
managedPeers map[string]*lazyconn.PeerConfig
|
||||||
|
managedPeersByConnID map[peerid.ConnID]*managedPeer
|
||||||
|
excludes map[string]lazyconn.PeerConfig
|
||||||
|
managedPeersMu sync.Mutex
|
||||||
|
|
||||||
|
activityManager *activity.Manager
|
||||||
|
inactivityMonitors map[peerid.ConnID]*inactivity.Monitor
|
||||||
|
|
||||||
|
cancel context.CancelFunc
|
||||||
|
onInactive chan peerid.ConnID
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewManager(config Config, peerStore *peerstore.Store, wgIface lazyconn.WGIface, connStateDispatcher *dispatcher.ConnectionDispatcher) *Manager {
|
||||||
|
log.Infof("setup lazy connection service")
|
||||||
|
m := &Manager{
|
||||||
|
peerStore: peerStore,
|
||||||
|
connStateDispatcher: connStateDispatcher,
|
||||||
|
inactivityThreshold: inactivity.DefaultInactivityThreshold,
|
||||||
|
managedPeers: make(map[string]*lazyconn.PeerConfig),
|
||||||
|
managedPeersByConnID: make(map[peerid.ConnID]*managedPeer),
|
||||||
|
excludes: make(map[string]lazyconn.PeerConfig),
|
||||||
|
activityManager: activity.NewManager(wgIface),
|
||||||
|
inactivityMonitors: make(map[peerid.ConnID]*inactivity.Monitor),
|
||||||
|
onInactive: make(chan peerid.ConnID),
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.InactivityThreshold != nil {
|
||||||
|
if *config.InactivityThreshold >= inactivity.MinimumInactivityThreshold {
|
||||||
|
m.inactivityThreshold = *config.InactivityThreshold
|
||||||
|
} else {
|
||||||
|
log.Warnf("inactivity threshold is too low, using %v", m.inactivityThreshold)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m.connStateListener = &dispatcher.ConnectionListener{
|
||||||
|
OnConnected: m.onPeerConnected,
|
||||||
|
OnDisconnected: m.onPeerDisconnected,
|
||||||
|
}
|
||||||
|
|
||||||
|
connStateDispatcher.AddListener(m.connStateListener)
|
||||||
|
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start starts the manager and listens for peer activity and inactivity events
|
||||||
|
func (m *Manager) Start(ctx context.Context) {
|
||||||
|
defer m.close()
|
||||||
|
|
||||||
|
ctx, m.cancel = context.WithCancel(ctx)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case peerConnID := <-m.activityManager.OnActivityChan:
|
||||||
|
m.onPeerActivity(ctx, peerConnID)
|
||||||
|
case peerConnID := <-m.onInactive:
|
||||||
|
m.onPeerInactivityTimedOut(peerConnID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExcludePeer marks peers for a permanent connection
|
||||||
|
// It removes peers from the managed list if they are added to the exclude list
|
||||||
|
// Adds them back to the managed list and start the inactivity listener if they are removed from the exclude list. In
|
||||||
|
// this case, we suppose that the connection status is connected or connecting.
|
||||||
|
// If the peer is not exists yet in the managed list then the responsibility is the upper layer to call the AddPeer function
|
||||||
|
func (m *Manager) ExcludePeer(ctx context.Context, peerConfigs []lazyconn.PeerConfig) []string {
|
||||||
|
m.managedPeersMu.Lock()
|
||||||
|
defer m.managedPeersMu.Unlock()
|
||||||
|
|
||||||
|
added := make([]string, 0)
|
||||||
|
excludes := make(map[string]lazyconn.PeerConfig, len(peerConfigs))
|
||||||
|
|
||||||
|
for _, peerCfg := range peerConfigs {
|
||||||
|
log.Infof("update excluded lazy connection list with peer: %s", peerCfg.PublicKey)
|
||||||
|
excludes[peerCfg.PublicKey] = peerCfg
|
||||||
|
}
|
||||||
|
|
||||||
|
// if a peer is newly added to the exclude list, remove from the managed peers list
|
||||||
|
for pubKey, peerCfg := range excludes {
|
||||||
|
if _, wasExcluded := m.excludes[pubKey]; wasExcluded {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
added = append(added, pubKey)
|
||||||
|
peerCfg.Log.Infof("peer newly added to lazy connection exclude list")
|
||||||
|
m.removePeer(pubKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// if a peer has been removed from exclude list then it should be added to the managed peers
|
||||||
|
for pubKey, peerCfg := range m.excludes {
|
||||||
|
if _, stillExcluded := excludes[pubKey]; stillExcluded {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
peerCfg.Log.Infof("peer removed from lazy connection exclude list")
|
||||||
|
|
||||||
|
if err := m.addActivePeer(ctx, peerCfg); err != nil {
|
||||||
|
log.Errorf("failed to add peer to lazy connection manager: %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m.excludes = excludes
|
||||||
|
return added
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) AddPeer(peerCfg lazyconn.PeerConfig) (bool, error) {
|
||||||
|
m.managedPeersMu.Lock()
|
||||||
|
defer m.managedPeersMu.Unlock()
|
||||||
|
|
||||||
|
peerCfg.Log.Debugf("adding peer to lazy connection manager")
|
||||||
|
|
||||||
|
_, exists := m.excludes[peerCfg.PublicKey]
|
||||||
|
if exists {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := m.managedPeers[peerCfg.PublicKey]; ok {
|
||||||
|
peerCfg.Log.Warnf("peer already managed")
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.activityManager.MonitorPeerActivity(peerCfg); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
im := inactivity.NewInactivityMonitor(peerCfg.PeerConnID, m.inactivityThreshold)
|
||||||
|
m.inactivityMonitors[peerCfg.PeerConnID] = im
|
||||||
|
|
||||||
|
m.managedPeers[peerCfg.PublicKey] = &peerCfg
|
||||||
|
m.managedPeersByConnID[peerCfg.PeerConnID] = &managedPeer{
|
||||||
|
peerCfg: &peerCfg,
|
||||||
|
expectedWatcher: watcherActivity,
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddActivePeers adds a list of peers to the lazy connection manager
|
||||||
|
// suppose these peers was in connected or in connecting states
|
||||||
|
func (m *Manager) AddActivePeers(ctx context.Context, peerCfg []lazyconn.PeerConfig) error {
|
||||||
|
m.managedPeersMu.Lock()
|
||||||
|
defer m.managedPeersMu.Unlock()
|
||||||
|
|
||||||
|
for _, cfg := range peerCfg {
|
||||||
|
if _, ok := m.managedPeers[cfg.PublicKey]; ok {
|
||||||
|
cfg.Log.Errorf("peer already managed")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.addActivePeer(ctx, cfg); err != nil {
|
||||||
|
cfg.Log.Errorf("failed to add peer to lazy connection manager: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) RemovePeer(peerID string) {
|
||||||
|
m.managedPeersMu.Lock()
|
||||||
|
defer m.managedPeersMu.Unlock()
|
||||||
|
|
||||||
|
m.removePeer(peerID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ActivatePeer activates a peer connection when a signal message is received
|
||||||
|
func (m *Manager) ActivatePeer(ctx context.Context, peerID string) (found bool) {
|
||||||
|
m.managedPeersMu.Lock()
|
||||||
|
defer m.managedPeersMu.Unlock()
|
||||||
|
|
||||||
|
cfg, ok := m.managedPeers[peerID]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
mp, ok := m.managedPeersByConnID[cfg.PeerConnID]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// signal messages coming continuously after success activation, with this avoid the multiple activation
|
||||||
|
if mp.expectedWatcher == watcherInactivity {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
mp.expectedWatcher = watcherInactivity
|
||||||
|
|
||||||
|
m.activityManager.RemovePeer(cfg.Log, cfg.PeerConnID)
|
||||||
|
|
||||||
|
im, ok := m.inactivityMonitors[cfg.PeerConnID]
|
||||||
|
if !ok {
|
||||||
|
cfg.Log.Errorf("inactivity monitor not found for peer")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
mp.peerCfg.Log.Infof("starting inactivity monitor")
|
||||||
|
go im.Start(ctx, m.onInactive)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) addActivePeer(ctx context.Context, peerCfg lazyconn.PeerConfig) error {
|
||||||
|
if _, ok := m.managedPeers[peerCfg.PublicKey]; ok {
|
||||||
|
peerCfg.Log.Warnf("peer already managed")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
im := inactivity.NewInactivityMonitor(peerCfg.PeerConnID, m.inactivityThreshold)
|
||||||
|
m.inactivityMonitors[peerCfg.PeerConnID] = im
|
||||||
|
|
||||||
|
m.managedPeers[peerCfg.PublicKey] = &peerCfg
|
||||||
|
m.managedPeersByConnID[peerCfg.PeerConnID] = &managedPeer{
|
||||||
|
peerCfg: &peerCfg,
|
||||||
|
expectedWatcher: watcherInactivity,
|
||||||
|
}
|
||||||
|
|
||||||
|
peerCfg.Log.Infof("starting inactivity monitor on peer that has been removed from exclude list")
|
||||||
|
go im.Start(ctx, m.onInactive)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) removePeer(peerID string) {
|
||||||
|
cfg, ok := m.managedPeers[peerID]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.Log.Infof("removing lazy peer")
|
||||||
|
|
||||||
|
if im, ok := m.inactivityMonitors[cfg.PeerConnID]; ok {
|
||||||
|
im.Stop()
|
||||||
|
delete(m.inactivityMonitors, cfg.PeerConnID)
|
||||||
|
cfg.Log.Debugf("inactivity monitor stopped")
|
||||||
|
}
|
||||||
|
|
||||||
|
m.activityManager.RemovePeer(cfg.Log, cfg.PeerConnID)
|
||||||
|
delete(m.managedPeers, peerID)
|
||||||
|
delete(m.managedPeersByConnID, cfg.PeerConnID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) close() {
|
||||||
|
m.managedPeersMu.Lock()
|
||||||
|
defer m.managedPeersMu.Unlock()
|
||||||
|
|
||||||
|
m.cancel()
|
||||||
|
|
||||||
|
m.connStateDispatcher.RemoveListener(m.connStateListener)
|
||||||
|
m.activityManager.Close()
|
||||||
|
for _, iw := range m.inactivityMonitors {
|
||||||
|
iw.Stop()
|
||||||
|
}
|
||||||
|
m.inactivityMonitors = make(map[peerid.ConnID]*inactivity.Monitor)
|
||||||
|
m.managedPeers = make(map[string]*lazyconn.PeerConfig)
|
||||||
|
m.managedPeersByConnID = make(map[peerid.ConnID]*managedPeer)
|
||||||
|
log.Infof("lazy connection manager closed")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) onPeerActivity(ctx context.Context, peerConnID peerid.ConnID) {
|
||||||
|
m.managedPeersMu.Lock()
|
||||||
|
defer m.managedPeersMu.Unlock()
|
||||||
|
|
||||||
|
mp, ok := m.managedPeersByConnID[peerConnID]
|
||||||
|
if !ok {
|
||||||
|
log.Errorf("peer not found by conn id: %v", peerConnID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if mp.expectedWatcher != watcherActivity {
|
||||||
|
mp.peerCfg.Log.Warnf("ignore activity event")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mp.peerCfg.Log.Infof("detected peer activity")
|
||||||
|
|
||||||
|
mp.expectedWatcher = watcherInactivity
|
||||||
|
|
||||||
|
mp.peerCfg.Log.Infof("starting inactivity monitor")
|
||||||
|
go m.inactivityMonitors[peerConnID].Start(ctx, m.onInactive)
|
||||||
|
|
||||||
|
m.peerStore.PeerConnOpen(ctx, mp.peerCfg.PublicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) onPeerInactivityTimedOut(peerConnID peerid.ConnID) {
|
||||||
|
m.managedPeersMu.Lock()
|
||||||
|
defer m.managedPeersMu.Unlock()
|
||||||
|
|
||||||
|
mp, ok := m.managedPeersByConnID[peerConnID]
|
||||||
|
if !ok {
|
||||||
|
log.Errorf("peer not found by id: %v", peerConnID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if mp.expectedWatcher != watcherInactivity {
|
||||||
|
mp.peerCfg.Log.Warnf("ignore inactivity event")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mp.peerCfg.Log.Infof("connection timed out")
|
||||||
|
|
||||||
|
// this is blocking operation, potentially can be optimized
|
||||||
|
m.peerStore.PeerConnClose(mp.peerCfg.PublicKey)
|
||||||
|
|
||||||
|
mp.peerCfg.Log.Infof("start activity monitor")
|
||||||
|
|
||||||
|
mp.expectedWatcher = watcherActivity
|
||||||
|
|
||||||
|
// just in case free up
|
||||||
|
m.inactivityMonitors[peerConnID].PauseTimer()
|
||||||
|
|
||||||
|
if err := m.activityManager.MonitorPeerActivity(*mp.peerCfg); err != nil {
|
||||||
|
mp.peerCfg.Log.Errorf("failed to create activity monitor: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) onPeerConnected(peerConnID peerid.ConnID) {
|
||||||
|
m.managedPeersMu.Lock()
|
||||||
|
defer m.managedPeersMu.Unlock()
|
||||||
|
|
||||||
|
mp, ok := m.managedPeersByConnID[peerConnID]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if mp.expectedWatcher != watcherInactivity {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
iw, ok := m.inactivityMonitors[mp.peerCfg.PeerConnID]
|
||||||
|
if !ok {
|
||||||
|
mp.peerCfg.Log.Errorf("inactivity monitor not found for peer")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mp.peerCfg.Log.Infof("peer connected, pausing inactivity monitor while connection is not disconnected")
|
||||||
|
iw.PauseTimer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) onPeerDisconnected(peerConnID peerid.ConnID) {
|
||||||
|
m.managedPeersMu.Lock()
|
||||||
|
defer m.managedPeersMu.Unlock()
|
||||||
|
|
||||||
|
mp, ok := m.managedPeersByConnID[peerConnID]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if mp.expectedWatcher != watcherInactivity {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
iw, ok := m.inactivityMonitors[mp.peerCfg.PeerConnID]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mp.peerCfg.Log.Infof("reset inactivity monitor timer")
|
||||||
|
iw.ResetTimer()
|
||||||
|
}
|
16
client/internal/lazyconn/peercfg.go
Normal file
16
client/internal/lazyconn/peercfg.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package lazyconn
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/netip"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/internal/peer/id"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PeerConfig struct {
|
||||||
|
PublicKey string
|
||||||
|
AllowedIPs []netip.Prefix
|
||||||
|
PeerConnID id.ConnID
|
||||||
|
Log *log.Entry
|
||||||
|
}
|
41
client/internal/lazyconn/support.go
Normal file
41
client/internal/lazyconn/support.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package lazyconn
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-version"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
minVersion = version.Must(version.NewVersion("0.45.0"))
|
||||||
|
)
|
||||||
|
|
||||||
|
func IsSupported(agentVersion string) bool {
|
||||||
|
if agentVersion == "development" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// filter out versions like this: a6c5960, a7d5c522, d47be154
|
||||||
|
if !strings.Contains(agentVersion, ".") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
normalizedVersion := normalizeVersion(agentVersion)
|
||||||
|
inputVer, err := version.NewVersion(normalizedVersion)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return inputVer.GreaterThanOrEqual(minVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeVersion(version string) string {
|
||||||
|
// Remove prefixes like 'v' or 'a'
|
||||||
|
if len(version) > 0 && (version[0] == 'v' || version[0] == 'a') {
|
||||||
|
version = version[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove any suffixes like '-dirty', '-dev', '-SNAPSHOT', etc.
|
||||||
|
parts := strings.Split(version, "-")
|
||||||
|
return parts[0]
|
||||||
|
}
|
31
client/internal/lazyconn/support_test.go
Normal file
31
client/internal/lazyconn/support_test.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package lazyconn
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestIsSupported(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
version string
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{"development", true},
|
||||||
|
{"0.45.0", true},
|
||||||
|
{"v0.45.0", true},
|
||||||
|
{"0.45.1", true},
|
||||||
|
{"0.45.1-SNAPSHOT-559e6731", true},
|
||||||
|
{"v0.45.1-dev", true},
|
||||||
|
{"a7d5c522", false},
|
||||||
|
{"0.9.6", false},
|
||||||
|
{"0.9.6-SNAPSHOT", false},
|
||||||
|
{"0.9.6-SNAPSHOT-2033650", false},
|
||||||
|
{"meta_wt_version", false},
|
||||||
|
{"v0.31.1-dev", false},
|
||||||
|
{"", false},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.version, func(t *testing.T) {
|
||||||
|
if got := IsSupported(tt.version); got != tt.want {
|
||||||
|
t.Errorf("IsSupported() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
14
client/internal/lazyconn/wgiface.go
Normal file
14
client/internal/lazyconn/wgiface.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package lazyconn
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WGIface interface {
|
||||||
|
RemovePeer(peerKey string) error
|
||||||
|
UpdatePeer(peerKey string, allowedIps []netip.Prefix, keepAlive time.Duration, endpoint *net.UDPAddr, preSharedKey *wgtypes.Key) error
|
||||||
|
}
|
@ -17,8 +17,12 @@ import (
|
|||||||
|
|
||||||
"github.com/netbirdio/netbird/client/iface/configurer"
|
"github.com/netbirdio/netbird/client/iface/configurer"
|
||||||
"github.com/netbirdio/netbird/client/iface/wgproxy"
|
"github.com/netbirdio/netbird/client/iface/wgproxy"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/peer/conntype"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/peer/dispatcher"
|
||||||
"github.com/netbirdio/netbird/client/internal/peer/guard"
|
"github.com/netbirdio/netbird/client/internal/peer/guard"
|
||||||
icemaker "github.com/netbirdio/netbird/client/internal/peer/ice"
|
icemaker "github.com/netbirdio/netbird/client/internal/peer/ice"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/peer/id"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/peer/worker"
|
||||||
"github.com/netbirdio/netbird/client/internal/stdnet"
|
"github.com/netbirdio/netbird/client/internal/stdnet"
|
||||||
relayClient "github.com/netbirdio/netbird/relay/client"
|
relayClient "github.com/netbirdio/netbird/relay/client"
|
||||||
"github.com/netbirdio/netbird/route"
|
"github.com/netbirdio/netbird/route"
|
||||||
@ -26,32 +30,20 @@ import (
|
|||||||
semaphoregroup "github.com/netbirdio/netbird/util/semaphore-group"
|
semaphoregroup "github.com/netbirdio/netbird/util/semaphore-group"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConnPriority int
|
|
||||||
|
|
||||||
func (cp ConnPriority) String() string {
|
|
||||||
switch cp {
|
|
||||||
case connPriorityNone:
|
|
||||||
return "None"
|
|
||||||
case connPriorityRelay:
|
|
||||||
return "PriorityRelay"
|
|
||||||
case connPriorityICETurn:
|
|
||||||
return "PriorityICETurn"
|
|
||||||
case connPriorityICEP2P:
|
|
||||||
return "PriorityICEP2P"
|
|
||||||
default:
|
|
||||||
return fmt.Sprintf("ConnPriority(%d)", cp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultWgKeepAlive = 25 * time.Second
|
defaultWgKeepAlive = 25 * time.Second
|
||||||
|
|
||||||
connPriorityNone ConnPriority = 0
|
|
||||||
connPriorityRelay ConnPriority = 1
|
|
||||||
connPriorityICETurn ConnPriority = 2
|
|
||||||
connPriorityICEP2P ConnPriority = 3
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ServiceDependencies struct {
|
||||||
|
StatusRecorder *Status
|
||||||
|
Signaler *Signaler
|
||||||
|
IFaceDiscover stdnet.ExternalIFaceDiscover
|
||||||
|
RelayManager *relayClient.Manager
|
||||||
|
SrWatcher *guard.SRWatcher
|
||||||
|
Semaphore *semaphoregroup.SemaphoreGroup
|
||||||
|
PeerConnDispatcher *dispatcher.ConnectionDispatcher
|
||||||
|
}
|
||||||
|
|
||||||
type WgConfig struct {
|
type WgConfig struct {
|
||||||
WgListenPort int
|
WgListenPort int
|
||||||
RemoteKey string
|
RemoteKey string
|
||||||
@ -76,6 +68,8 @@ type ConnConfig struct {
|
|||||||
// LocalKey is a public key of a local peer
|
// LocalKey is a public key of a local peer
|
||||||
LocalKey string
|
LocalKey string
|
||||||
|
|
||||||
|
AgentVersion string
|
||||||
|
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
|
|
||||||
WgConfig WgConfig
|
WgConfig WgConfig
|
||||||
@ -89,22 +83,23 @@ type ConnConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Conn struct {
|
type Conn struct {
|
||||||
log *log.Entry
|
Log *log.Entry
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
ctxCancel context.CancelFunc
|
ctxCancel context.CancelFunc
|
||||||
config ConnConfig
|
config ConnConfig
|
||||||
statusRecorder *Status
|
statusRecorder *Status
|
||||||
signaler *Signaler
|
signaler *Signaler
|
||||||
|
iFaceDiscover stdnet.ExternalIFaceDiscover
|
||||||
relayManager *relayClient.Manager
|
relayManager *relayClient.Manager
|
||||||
handshaker *Handshaker
|
srWatcher *guard.SRWatcher
|
||||||
|
|
||||||
onConnected func(remoteWireGuardKey string, remoteRosenpassPubKey []byte, wireGuardIP string, remoteRosenpassAddr string)
|
onConnected func(remoteWireGuardKey string, remoteRosenpassPubKey []byte, wireGuardIP string, remoteRosenpassAddr string)
|
||||||
onDisconnected func(remotePeer string)
|
onDisconnected func(remotePeer string)
|
||||||
|
|
||||||
statusRelay *AtomicConnStatus
|
statusRelay *worker.AtomicWorkerStatus
|
||||||
statusICE *AtomicConnStatus
|
statusICE *worker.AtomicWorkerStatus
|
||||||
currentConnPriority ConnPriority
|
currentConnPriority conntype.ConnPriority
|
||||||
opened bool // this flag is used to prevent close in case of not opened connection
|
opened bool // this flag is used to prevent close in case of not opened connection
|
||||||
|
|
||||||
workerICE *WorkerICE
|
workerICE *WorkerICE
|
||||||
@ -120,9 +115,12 @@ type Conn struct {
|
|||||||
|
|
||||||
wgProxyICE wgproxy.Proxy
|
wgProxyICE wgproxy.Proxy
|
||||||
wgProxyRelay wgproxy.Proxy
|
wgProxyRelay wgproxy.Proxy
|
||||||
|
handshaker *Handshaker
|
||||||
|
|
||||||
guard *guard.Guard
|
guard *guard.Guard
|
||||||
semaphore *semaphoregroup.SemaphoreGroup
|
semaphore *semaphoregroup.SemaphoreGroup
|
||||||
|
peerConnDispatcher *dispatcher.ConnectionDispatcher
|
||||||
|
wg sync.WaitGroup
|
||||||
|
|
||||||
// debug purpose
|
// debug purpose
|
||||||
dumpState *stateDump
|
dumpState *stateDump
|
||||||
@ -130,91 +128,101 @@ type Conn struct {
|
|||||||
|
|
||||||
// NewConn creates a new not opened Conn to the remote peer.
|
// NewConn creates a new not opened Conn to the remote peer.
|
||||||
// To establish a connection run Conn.Open
|
// To establish a connection run Conn.Open
|
||||||
func NewConn(engineCtx context.Context, config ConnConfig, statusRecorder *Status, signaler *Signaler, iFaceDiscover stdnet.ExternalIFaceDiscover, relayManager *relayClient.Manager, srWatcher *guard.SRWatcher, semaphore *semaphoregroup.SemaphoreGroup) (*Conn, error) {
|
func NewConn(config ConnConfig, services ServiceDependencies) (*Conn, error) {
|
||||||
if len(config.WgConfig.AllowedIps) == 0 {
|
if len(config.WgConfig.AllowedIps) == 0 {
|
||||||
return nil, fmt.Errorf("allowed IPs is empty")
|
return nil, fmt.Errorf("allowed IPs is empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, ctxCancel := context.WithCancel(engineCtx)
|
|
||||||
connLog := log.WithField("peer", config.Key)
|
connLog := log.WithField("peer", config.Key)
|
||||||
|
|
||||||
var conn = &Conn{
|
var conn = &Conn{
|
||||||
log: connLog,
|
Log: connLog,
|
||||||
ctx: ctx,
|
config: config,
|
||||||
ctxCancel: ctxCancel,
|
statusRecorder: services.StatusRecorder,
|
||||||
config: config,
|
signaler: services.Signaler,
|
||||||
statusRecorder: statusRecorder,
|
iFaceDiscover: services.IFaceDiscover,
|
||||||
signaler: signaler,
|
relayManager: services.RelayManager,
|
||||||
relayManager: relayManager,
|
srWatcher: services.SrWatcher,
|
||||||
statusRelay: NewAtomicConnStatus(),
|
semaphore: services.Semaphore,
|
||||||
statusICE: NewAtomicConnStatus(),
|
peerConnDispatcher: services.PeerConnDispatcher,
|
||||||
semaphore: semaphore,
|
statusRelay: worker.NewAtomicStatus(),
|
||||||
dumpState: newStateDump(config.Key, connLog, statusRecorder),
|
statusICE: worker.NewAtomicStatus(),
|
||||||
|
dumpState: newStateDump(config.Key, connLog, services.StatusRecorder),
|
||||||
}
|
}
|
||||||
|
|
||||||
ctrl := isController(config)
|
|
||||||
conn.workerRelay = NewWorkerRelay(connLog, ctrl, config, conn, relayManager, conn.dumpState)
|
|
||||||
|
|
||||||
relayIsSupportedLocally := conn.workerRelay.RelayIsSupportedLocally()
|
|
||||||
workerICE, err := NewWorkerICE(ctx, connLog, config, conn, signaler, iFaceDiscover, statusRecorder, relayIsSupportedLocally)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
conn.workerICE = workerICE
|
|
||||||
|
|
||||||
conn.handshaker = NewHandshaker(ctx, connLog, config, signaler, conn.workerICE, conn.workerRelay)
|
|
||||||
|
|
||||||
conn.handshaker.AddOnNewOfferListener(conn.workerRelay.OnNewOffer)
|
|
||||||
if os.Getenv("NB_FORCE_RELAY") != "true" {
|
|
||||||
conn.handshaker.AddOnNewOfferListener(conn.workerICE.OnNewOffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
conn.guard = guard.NewGuard(connLog, ctrl, conn.isConnectedOnAllWay, config.Timeout, srWatcher)
|
|
||||||
|
|
||||||
go conn.handshaker.Listen()
|
|
||||||
|
|
||||||
go conn.dumpState.Start(ctx)
|
|
||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open opens connection to the remote peer
|
// Open opens connection to the remote peer
|
||||||
// It will try to establish a connection using ICE and in parallel with relay. The higher priority connection type will
|
// It will try to establish a connection using ICE and in parallel with relay. The higher priority connection type will
|
||||||
// be used.
|
// be used.
|
||||||
func (conn *Conn) Open() {
|
func (conn *Conn) Open(engineCtx context.Context) error {
|
||||||
conn.semaphore.Add(conn.ctx)
|
conn.semaphore.Add(engineCtx)
|
||||||
conn.log.Debugf("open connection to peer")
|
|
||||||
|
|
||||||
conn.mu.Lock()
|
conn.mu.Lock()
|
||||||
defer conn.mu.Unlock()
|
defer conn.mu.Unlock()
|
||||||
conn.opened = true
|
|
||||||
|
if conn.opened {
|
||||||
|
conn.semaphore.Done(engineCtx)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.ctx, conn.ctxCancel = context.WithCancel(engineCtx)
|
||||||
|
|
||||||
|
conn.workerRelay = NewWorkerRelay(conn.Log, isController(conn.config), conn.config, conn, conn.relayManager, conn.dumpState)
|
||||||
|
|
||||||
|
relayIsSupportedLocally := conn.workerRelay.RelayIsSupportedLocally()
|
||||||
|
workerICE, err := NewWorkerICE(conn.ctx, conn.Log, conn.config, conn, conn.signaler, conn.iFaceDiscover, conn.statusRecorder, relayIsSupportedLocally)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
conn.workerICE = workerICE
|
||||||
|
|
||||||
|
conn.handshaker = NewHandshaker(conn.Log, conn.config, conn.signaler, conn.workerICE, conn.workerRelay)
|
||||||
|
|
||||||
|
conn.handshaker.AddOnNewOfferListener(conn.workerRelay.OnNewOffer)
|
||||||
|
if os.Getenv("NB_FORCE_RELAY") != "true" {
|
||||||
|
conn.handshaker.AddOnNewOfferListener(conn.workerICE.OnNewOffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.guard = guard.NewGuard(conn.Log, conn.isConnectedOnAllWay, conn.config.Timeout, conn.srWatcher)
|
||||||
|
|
||||||
|
conn.wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer conn.wg.Done()
|
||||||
|
conn.handshaker.Listen(conn.ctx)
|
||||||
|
}()
|
||||||
|
go conn.dumpState.Start(conn.ctx)
|
||||||
|
|
||||||
peerState := State{
|
peerState := State{
|
||||||
PubKey: conn.config.Key,
|
PubKey: conn.config.Key,
|
||||||
IP: conn.config.WgConfig.AllowedIps[0].Addr().String(),
|
|
||||||
ConnStatusUpdate: time.Now(),
|
ConnStatusUpdate: time.Now(),
|
||||||
ConnStatus: StatusDisconnected,
|
ConnStatus: StatusConnecting,
|
||||||
Mux: new(sync.RWMutex),
|
Mux: new(sync.RWMutex),
|
||||||
}
|
}
|
||||||
err := conn.statusRecorder.UpdatePeerState(peerState)
|
if err := conn.statusRecorder.UpdatePeerState(peerState); err != nil {
|
||||||
if err != nil {
|
conn.Log.Warnf("error while updating the state err: %v", err)
|
||||||
conn.log.Warnf("error while updating the state err: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
go conn.startHandshakeAndReconnect(conn.ctx)
|
conn.wg.Add(1)
|
||||||
}
|
go func() {
|
||||||
|
defer conn.wg.Done()
|
||||||
|
conn.waitInitialRandomSleepTime(conn.ctx)
|
||||||
|
conn.semaphore.Done(conn.ctx)
|
||||||
|
|
||||||
func (conn *Conn) startHandshakeAndReconnect(ctx context.Context) {
|
conn.dumpState.SendOffer()
|
||||||
defer conn.semaphore.Done(conn.ctx)
|
if err := conn.handshaker.sendOffer(); err != nil {
|
||||||
conn.waitInitialRandomSleepTime(ctx)
|
conn.Log.Errorf("failed to send initial offer: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
conn.dumpState.SendOffer()
|
conn.wg.Add(1)
|
||||||
err := conn.handshaker.sendOffer()
|
go func() {
|
||||||
if err != nil {
|
conn.guard.Start(conn.ctx, conn.onGuardEvent)
|
||||||
conn.log.Errorf("failed to send initial offer: %v", err)
|
conn.wg.Done()
|
||||||
}
|
}()
|
||||||
|
}()
|
||||||
go conn.guard.Start(ctx)
|
conn.opened = true
|
||||||
go conn.listenGuardEvent(ctx)
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes this peer Conn issuing a close event to the Conn closeCh
|
// Close closes this peer Conn issuing a close event to the Conn closeCh
|
||||||
@ -223,14 +231,14 @@ func (conn *Conn) Close() {
|
|||||||
defer conn.wgWatcherWg.Wait()
|
defer conn.wgWatcherWg.Wait()
|
||||||
defer conn.mu.Unlock()
|
defer conn.mu.Unlock()
|
||||||
|
|
||||||
conn.log.Infof("close peer connection")
|
|
||||||
conn.ctxCancel()
|
|
||||||
|
|
||||||
if !conn.opened {
|
if !conn.opened {
|
||||||
conn.log.Debugf("ignore close connection to peer")
|
conn.Log.Debugf("ignore close connection to peer")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
conn.Log.Infof("close peer connection")
|
||||||
|
conn.ctxCancel()
|
||||||
|
|
||||||
conn.workerRelay.DisableWgWatcher()
|
conn.workerRelay.DisableWgWatcher()
|
||||||
conn.workerRelay.CloseConn()
|
conn.workerRelay.CloseConn()
|
||||||
conn.workerICE.Close()
|
conn.workerICE.Close()
|
||||||
@ -238,7 +246,7 @@ func (conn *Conn) Close() {
|
|||||||
if conn.wgProxyRelay != nil {
|
if conn.wgProxyRelay != nil {
|
||||||
err := conn.wgProxyRelay.CloseConn()
|
err := conn.wgProxyRelay.CloseConn()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
conn.log.Errorf("failed to close wg proxy for relay: %v", err)
|
conn.Log.Errorf("failed to close wg proxy for relay: %v", err)
|
||||||
}
|
}
|
||||||
conn.wgProxyRelay = nil
|
conn.wgProxyRelay = nil
|
||||||
}
|
}
|
||||||
@ -246,13 +254,13 @@ func (conn *Conn) Close() {
|
|||||||
if conn.wgProxyICE != nil {
|
if conn.wgProxyICE != nil {
|
||||||
err := conn.wgProxyICE.CloseConn()
|
err := conn.wgProxyICE.CloseConn()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
conn.log.Errorf("failed to close wg proxy for ice: %v", err)
|
conn.Log.Errorf("failed to close wg proxy for ice: %v", err)
|
||||||
}
|
}
|
||||||
conn.wgProxyICE = nil
|
conn.wgProxyICE = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := conn.removeWgPeer(); err != nil {
|
if err := conn.removeWgPeer(); err != nil {
|
||||||
conn.log.Errorf("failed to remove wg endpoint: %v", err)
|
conn.Log.Errorf("failed to remove wg endpoint: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
conn.freeUpConnID()
|
conn.freeUpConnID()
|
||||||
@ -262,14 +270,16 @@ func (conn *Conn) Close() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
conn.setStatusToDisconnected()
|
conn.setStatusToDisconnected()
|
||||||
conn.log.Infof("peer connection has been closed")
|
conn.opened = false
|
||||||
|
conn.wg.Wait()
|
||||||
|
conn.Log.Infof("peer connection closed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnRemoteAnswer handles an offer from the remote peer and returns true if the message was accepted, false otherwise
|
// OnRemoteAnswer handles an offer from the remote peer and returns true if the message was accepted, false otherwise
|
||||||
// doesn't block, discards the message if connection wasn't ready
|
// doesn't block, discards the message if connection wasn't ready
|
||||||
func (conn *Conn) OnRemoteAnswer(answer OfferAnswer) bool {
|
func (conn *Conn) OnRemoteAnswer(answer OfferAnswer) bool {
|
||||||
conn.dumpState.RemoteAnswer()
|
conn.dumpState.RemoteAnswer()
|
||||||
conn.log.Infof("OnRemoteAnswer, priority: %s, status ICE: %s, status relay: %s", conn.currentConnPriority, conn.statusICE, conn.statusRelay)
|
conn.Log.Infof("OnRemoteAnswer, priority: %s, status ICE: %s, status relay: %s", conn.currentConnPriority, conn.statusICE, conn.statusRelay)
|
||||||
return conn.handshaker.OnRemoteAnswer(answer)
|
return conn.handshaker.OnRemoteAnswer(answer)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,7 +308,7 @@ func (conn *Conn) SetOnDisconnected(handler func(remotePeer string)) {
|
|||||||
|
|
||||||
func (conn *Conn) OnRemoteOffer(offer OfferAnswer) bool {
|
func (conn *Conn) OnRemoteOffer(offer OfferAnswer) bool {
|
||||||
conn.dumpState.RemoteOffer()
|
conn.dumpState.RemoteOffer()
|
||||||
conn.log.Infof("OnRemoteOffer, on status ICE: %s, status Relay: %s", conn.statusICE, conn.statusRelay)
|
conn.Log.Infof("OnRemoteOffer, on status ICE: %s, status Relay: %s", conn.statusICE, conn.statusRelay)
|
||||||
return conn.handshaker.OnRemoteOffer(offer)
|
return conn.handshaker.OnRemoteOffer(offer)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,19 +317,24 @@ func (conn *Conn) WgConfig() WgConfig {
|
|||||||
return conn.config.WgConfig
|
return conn.config.WgConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status returns current status of the Conn
|
// IsConnected unit tests only
|
||||||
func (conn *Conn) Status() ConnStatus {
|
// refactor unit test to use status recorder use refactor status recorded to manage connection status in peer.Conn
|
||||||
|
func (conn *Conn) IsConnected() bool {
|
||||||
conn.mu.Lock()
|
conn.mu.Lock()
|
||||||
defer conn.mu.Unlock()
|
defer conn.mu.Unlock()
|
||||||
return conn.evalStatus()
|
return conn.currentConnPriority != conntype.None
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn *Conn) GetKey() string {
|
func (conn *Conn) GetKey() string {
|
||||||
return conn.config.Key
|
return conn.config.Key
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (conn *Conn) ConnID() id.ConnID {
|
||||||
|
return id.ConnID(conn)
|
||||||
|
}
|
||||||
|
|
||||||
// configureConnection starts proxying traffic from/to local Wireguard and sets connection status to StatusConnected
|
// configureConnection starts proxying traffic from/to local Wireguard and sets connection status to StatusConnected
|
||||||
func (conn *Conn) onICEConnectionIsReady(priority ConnPriority, iceConnInfo ICEConnInfo) {
|
func (conn *Conn) onICEConnectionIsReady(priority conntype.ConnPriority, iceConnInfo ICEConnInfo) {
|
||||||
conn.mu.Lock()
|
conn.mu.Lock()
|
||||||
defer conn.mu.Unlock()
|
defer conn.mu.Unlock()
|
||||||
|
|
||||||
@ -327,21 +342,21 @@ func (conn *Conn) onICEConnectionIsReady(priority ConnPriority, iceConnInfo ICEC
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if remoteConnNil(conn.log, iceConnInfo.RemoteConn) {
|
if remoteConnNil(conn.Log, iceConnInfo.RemoteConn) {
|
||||||
conn.log.Errorf("remote ICE connection is nil")
|
conn.Log.Errorf("remote ICE connection is nil")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// this never should happen, because Relay is the lower priority and ICE always close the deprecated connection before upgrade
|
// this never should happen, because Relay is the lower priority and ICE always close the deprecated connection before upgrade
|
||||||
// todo consider to remove this check
|
// todo consider to remove this check
|
||||||
if conn.currentConnPriority > priority {
|
if conn.currentConnPriority > priority {
|
||||||
conn.log.Infof("current connection priority (%s) is higher than the new one (%s), do not upgrade connection", conn.currentConnPriority, priority)
|
conn.Log.Infof("current connection priority (%s) is higher than the new one (%s), do not upgrade connection", conn.currentConnPriority, priority)
|
||||||
conn.statusICE.Set(StatusConnected)
|
conn.statusICE.SetConnected()
|
||||||
conn.updateIceState(iceConnInfo)
|
conn.updateIceState(iceConnInfo)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
conn.log.Infof("set ICE to active connection")
|
conn.Log.Infof("set ICE to active connection")
|
||||||
conn.dumpState.P2PConnected()
|
conn.dumpState.P2PConnected()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -353,7 +368,7 @@ func (conn *Conn) onICEConnectionIsReady(priority ConnPriority, iceConnInfo ICEC
|
|||||||
conn.dumpState.NewLocalProxy()
|
conn.dumpState.NewLocalProxy()
|
||||||
wgProxy, err = conn.newProxy(iceConnInfo.RemoteConn)
|
wgProxy, err = conn.newProxy(iceConnInfo.RemoteConn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
conn.log.Errorf("failed to add turn net.Conn to local proxy: %v", err)
|
conn.Log.Errorf("failed to add turn net.Conn to local proxy: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ep = wgProxy.EndpointAddr()
|
ep = wgProxy.EndpointAddr()
|
||||||
@ -369,7 +384,7 @@ func (conn *Conn) onICEConnectionIsReady(priority ConnPriority, iceConnInfo ICEC
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := conn.runBeforeAddPeerHooks(ep.IP); err != nil {
|
if err := conn.runBeforeAddPeerHooks(ep.IP); err != nil {
|
||||||
conn.log.Errorf("Before add peer hook failed: %v", err)
|
conn.Log.Errorf("Before add peer hook failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
conn.workerRelay.DisableWgWatcher()
|
conn.workerRelay.DisableWgWatcher()
|
||||||
@ -388,10 +403,16 @@ func (conn *Conn) onICEConnectionIsReady(priority ConnPriority, iceConnInfo ICEC
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
wgConfigWorkaround()
|
wgConfigWorkaround()
|
||||||
|
|
||||||
|
oldState := conn.currentConnPriority
|
||||||
conn.currentConnPriority = priority
|
conn.currentConnPriority = priority
|
||||||
conn.statusICE.Set(StatusConnected)
|
conn.statusICE.SetConnected()
|
||||||
conn.updateIceState(iceConnInfo)
|
conn.updateIceState(iceConnInfo)
|
||||||
conn.doOnConnected(iceConnInfo.RosenpassPubKey, iceConnInfo.RosenpassAddr)
|
conn.doOnConnected(iceConnInfo.RosenpassPubKey, iceConnInfo.RosenpassAddr)
|
||||||
|
|
||||||
|
if oldState == conntype.None {
|
||||||
|
conn.peerConnDispatcher.NotifyConnected(conn.ConnID())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn *Conn) onICEStateDisconnected() {
|
func (conn *Conn) onICEStateDisconnected() {
|
||||||
@ -402,22 +423,22 @@ func (conn *Conn) onICEStateDisconnected() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
conn.log.Tracef("ICE connection state changed to disconnected")
|
conn.Log.Tracef("ICE connection state changed to disconnected")
|
||||||
|
|
||||||
if conn.wgProxyICE != nil {
|
if conn.wgProxyICE != nil {
|
||||||
if err := conn.wgProxyICE.CloseConn(); err != nil {
|
if err := conn.wgProxyICE.CloseConn(); err != nil {
|
||||||
conn.log.Warnf("failed to close deprecated wg proxy conn: %v", err)
|
conn.Log.Warnf("failed to close deprecated wg proxy conn: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// switch back to relay connection
|
// switch back to relay connection
|
||||||
if conn.isReadyToUpgrade() {
|
if conn.isReadyToUpgrade() {
|
||||||
conn.log.Infof("ICE disconnected, set Relay to active connection")
|
conn.Log.Infof("ICE disconnected, set Relay to active connection")
|
||||||
conn.dumpState.SwitchToRelay()
|
conn.dumpState.SwitchToRelay()
|
||||||
conn.wgProxyRelay.Work()
|
conn.wgProxyRelay.Work()
|
||||||
|
|
||||||
if err := conn.configureWGEndpoint(conn.wgProxyRelay.EndpointAddr(), conn.rosenpassRemoteKey); err != nil {
|
if err := conn.configureWGEndpoint(conn.wgProxyRelay.EndpointAddr(), conn.rosenpassRemoteKey); err != nil {
|
||||||
conn.log.Errorf("failed to switch to relay conn: %v", err)
|
conn.Log.Errorf("failed to switch to relay conn: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
conn.wgWatcherWg.Add(1)
|
conn.wgWatcherWg.Add(1)
|
||||||
@ -425,17 +446,18 @@ func (conn *Conn) onICEStateDisconnected() {
|
|||||||
defer conn.wgWatcherWg.Done()
|
defer conn.wgWatcherWg.Done()
|
||||||
conn.workerRelay.EnableWgWatcher(conn.ctx)
|
conn.workerRelay.EnableWgWatcher(conn.ctx)
|
||||||
}()
|
}()
|
||||||
conn.currentConnPriority = connPriorityRelay
|
conn.currentConnPriority = conntype.Relay
|
||||||
} else {
|
} else {
|
||||||
conn.log.Infof("ICE disconnected, do not switch to Relay. Reset priority to: %s", connPriorityNone.String())
|
conn.Log.Infof("ICE disconnected, do not switch to Relay. Reset priority to: %s", conntype.None.String())
|
||||||
conn.currentConnPriority = connPriorityNone
|
conn.currentConnPriority = conntype.None
|
||||||
|
conn.peerConnDispatcher.NotifyDisconnected(conn.ConnID())
|
||||||
}
|
}
|
||||||
|
|
||||||
changed := conn.statusICE.Get() != StatusDisconnected
|
changed := conn.statusICE.Get() != worker.StatusDisconnected
|
||||||
if changed {
|
if changed {
|
||||||
conn.guard.SetICEConnDisconnected()
|
conn.guard.SetICEConnDisconnected()
|
||||||
}
|
}
|
||||||
conn.statusICE.Set(StatusDisconnected)
|
conn.statusICE.SetDisconnected()
|
||||||
|
|
||||||
peerState := State{
|
peerState := State{
|
||||||
PubKey: conn.config.Key,
|
PubKey: conn.config.Key,
|
||||||
@ -446,7 +468,7 @@ func (conn *Conn) onICEStateDisconnected() {
|
|||||||
|
|
||||||
err := conn.statusRecorder.UpdatePeerICEStateToDisconnected(peerState)
|
err := conn.statusRecorder.UpdatePeerICEStateToDisconnected(peerState)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
conn.log.Warnf("unable to set peer's state to disconnected ice, got error: %v", err)
|
conn.Log.Warnf("unable to set peer's state to disconnected ice, got error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -456,41 +478,41 @@ func (conn *Conn) onRelayConnectionIsReady(rci RelayConnInfo) {
|
|||||||
|
|
||||||
if conn.ctx.Err() != nil {
|
if conn.ctx.Err() != nil {
|
||||||
if err := rci.relayedConn.Close(); err != nil {
|
if err := rci.relayedConn.Close(); err != nil {
|
||||||
conn.log.Warnf("failed to close unnecessary relayed connection: %v", err)
|
conn.Log.Warnf("failed to close unnecessary relayed connection: %v", err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
conn.dumpState.RelayConnected()
|
conn.dumpState.RelayConnected()
|
||||||
conn.log.Debugf("Relay connection has been established, setup the WireGuard")
|
conn.Log.Debugf("Relay connection has been established, setup the WireGuard")
|
||||||
|
|
||||||
wgProxy, err := conn.newProxy(rci.relayedConn)
|
wgProxy, err := conn.newProxy(rci.relayedConn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
conn.log.Errorf("failed to add relayed net.Conn to local proxy: %v", err)
|
conn.Log.Errorf("failed to add relayed net.Conn to local proxy: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
conn.dumpState.NewLocalProxy()
|
conn.dumpState.NewLocalProxy()
|
||||||
|
|
||||||
conn.log.Infof("created new wgProxy for relay connection: %s", wgProxy.EndpointAddr().String())
|
conn.Log.Infof("created new wgProxy for relay connection: %s", wgProxy.EndpointAddr().String())
|
||||||
|
|
||||||
if conn.isICEActive() {
|
if conn.isICEActive() {
|
||||||
conn.log.Infof("do not switch to relay because current priority is: %s", conn.currentConnPriority.String())
|
conn.Log.Debugf("do not switch to relay because current priority is: %s", conn.currentConnPriority.String())
|
||||||
conn.setRelayedProxy(wgProxy)
|
conn.setRelayedProxy(wgProxy)
|
||||||
conn.statusRelay.Set(StatusConnected)
|
conn.statusRelay.SetConnected()
|
||||||
conn.updateRelayStatus(rci.relayedConn.RemoteAddr().String(), rci.rosenpassPubKey)
|
conn.updateRelayStatus(rci.relayedConn.RemoteAddr().String(), rci.rosenpassPubKey)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := conn.runBeforeAddPeerHooks(wgProxy.EndpointAddr().IP); err != nil {
|
if err := conn.runBeforeAddPeerHooks(wgProxy.EndpointAddr().IP); err != nil {
|
||||||
conn.log.Errorf("Before add peer hook failed: %v", err)
|
conn.Log.Errorf("Before add peer hook failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
wgProxy.Work()
|
wgProxy.Work()
|
||||||
if err := conn.configureWGEndpoint(wgProxy.EndpointAddr(), rci.rosenpassPubKey); err != nil {
|
if err := conn.configureWGEndpoint(wgProxy.EndpointAddr(), rci.rosenpassPubKey); err != nil {
|
||||||
if err := wgProxy.CloseConn(); err != nil {
|
if err := wgProxy.CloseConn(); err != nil {
|
||||||
conn.log.Warnf("Failed to close relay connection: %v", err)
|
conn.Log.Warnf("Failed to close relay connection: %v", err)
|
||||||
}
|
}
|
||||||
conn.log.Errorf("Failed to update WireGuard peer configuration: %v", err)
|
conn.Log.Errorf("Failed to update WireGuard peer configuration: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -502,12 +524,13 @@ func (conn *Conn) onRelayConnectionIsReady(rci RelayConnInfo) {
|
|||||||
|
|
||||||
wgConfigWorkaround()
|
wgConfigWorkaround()
|
||||||
conn.rosenpassRemoteKey = rci.rosenpassPubKey
|
conn.rosenpassRemoteKey = rci.rosenpassPubKey
|
||||||
conn.currentConnPriority = connPriorityRelay
|
conn.currentConnPriority = conntype.Relay
|
||||||
conn.statusRelay.Set(StatusConnected)
|
conn.statusRelay.SetConnected()
|
||||||
conn.setRelayedProxy(wgProxy)
|
conn.setRelayedProxy(wgProxy)
|
||||||
conn.updateRelayStatus(rci.relayedConn.RemoteAddr().String(), rci.rosenpassPubKey)
|
conn.updateRelayStatus(rci.relayedConn.RemoteAddr().String(), rci.rosenpassPubKey)
|
||||||
conn.log.Infof("start to communicate with peer via relay")
|
conn.Log.Infof("start to communicate with peer via relay")
|
||||||
conn.doOnConnected(rci.rosenpassPubKey, rci.rosenpassAddr)
|
conn.doOnConnected(rci.rosenpassPubKey, rci.rosenpassAddr)
|
||||||
|
conn.peerConnDispatcher.NotifyConnected(conn.ConnID())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn *Conn) onRelayDisconnected() {
|
func (conn *Conn) onRelayDisconnected() {
|
||||||
@ -518,14 +541,15 @@ func (conn *Conn) onRelayDisconnected() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
conn.log.Infof("relay connection is disconnected")
|
conn.Log.Debugf("relay connection is disconnected")
|
||||||
|
|
||||||
if conn.currentConnPriority == connPriorityRelay {
|
if conn.currentConnPriority == conntype.Relay {
|
||||||
conn.log.Infof("clean up WireGuard config")
|
conn.Log.Debugf("clean up WireGuard config")
|
||||||
if err := conn.removeWgPeer(); err != nil {
|
if err := conn.removeWgPeer(); err != nil {
|
||||||
conn.log.Errorf("failed to remove wg endpoint: %v", err)
|
conn.Log.Errorf("failed to remove wg endpoint: %v", err)
|
||||||
}
|
}
|
||||||
conn.currentConnPriority = connPriorityNone
|
conn.currentConnPriority = conntype.None
|
||||||
|
conn.peerConnDispatcher.NotifyDisconnected(conn.ConnID())
|
||||||
}
|
}
|
||||||
|
|
||||||
if conn.wgProxyRelay != nil {
|
if conn.wgProxyRelay != nil {
|
||||||
@ -533,11 +557,11 @@ func (conn *Conn) onRelayDisconnected() {
|
|||||||
conn.wgProxyRelay = nil
|
conn.wgProxyRelay = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
changed := conn.statusRelay.Get() != StatusDisconnected
|
changed := conn.statusRelay.Get() != worker.StatusDisconnected
|
||||||
if changed {
|
if changed {
|
||||||
conn.guard.SetRelayedConnDisconnected()
|
conn.guard.SetRelayedConnDisconnected()
|
||||||
}
|
}
|
||||||
conn.statusRelay.Set(StatusDisconnected)
|
conn.statusRelay.SetDisconnected()
|
||||||
|
|
||||||
peerState := State{
|
peerState := State{
|
||||||
PubKey: conn.config.Key,
|
PubKey: conn.config.Key,
|
||||||
@ -546,22 +570,15 @@ func (conn *Conn) onRelayDisconnected() {
|
|||||||
ConnStatusUpdate: time.Now(),
|
ConnStatusUpdate: time.Now(),
|
||||||
}
|
}
|
||||||
if err := conn.statusRecorder.UpdatePeerRelayedStateToDisconnected(peerState); err != nil {
|
if err := conn.statusRecorder.UpdatePeerRelayedStateToDisconnected(peerState); err != nil {
|
||||||
conn.log.Warnf("unable to save peer's state to Relay disconnected, got error: %v", err)
|
conn.Log.Warnf("unable to save peer's state to Relay disconnected, got error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn *Conn) listenGuardEvent(ctx context.Context) {
|
func (conn *Conn) onGuardEvent() {
|
||||||
for {
|
conn.Log.Debugf("send offer to peer")
|
||||||
select {
|
conn.dumpState.SendOffer()
|
||||||
case <-conn.guard.Reconnect:
|
if err := conn.handshaker.SendOffer(); err != nil {
|
||||||
conn.log.Infof("send offer to peer")
|
conn.Log.Errorf("failed to send offer: %v", err)
|
||||||
conn.dumpState.SendOffer()
|
|
||||||
if err := conn.handshaker.SendOffer(); err != nil {
|
|
||||||
conn.log.Errorf("failed to send offer: %v", err)
|
|
||||||
}
|
|
||||||
case <-ctx.Done():
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -588,7 +605,7 @@ func (conn *Conn) updateRelayStatus(relayServerAddr string, rosenpassPubKey []by
|
|||||||
|
|
||||||
err := conn.statusRecorder.UpdatePeerRelayedState(peerState)
|
err := conn.statusRecorder.UpdatePeerRelayedState(peerState)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
conn.log.Warnf("unable to save peer's Relay state, got error: %v", err)
|
conn.Log.Warnf("unable to save peer's Relay state, got error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -607,17 +624,18 @@ func (conn *Conn) updateIceState(iceConnInfo ICEConnInfo) {
|
|||||||
|
|
||||||
err := conn.statusRecorder.UpdatePeerICEState(peerState)
|
err := conn.statusRecorder.UpdatePeerICEState(peerState)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
conn.log.Warnf("unable to save peer's ICE state, got error: %v", err)
|
conn.Log.Warnf("unable to save peer's ICE state, got error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn *Conn) setStatusToDisconnected() {
|
func (conn *Conn) setStatusToDisconnected() {
|
||||||
conn.statusRelay.Set(StatusDisconnected)
|
conn.statusRelay.SetDisconnected()
|
||||||
conn.statusICE.Set(StatusDisconnected)
|
conn.statusICE.SetDisconnected()
|
||||||
|
conn.currentConnPriority = conntype.None
|
||||||
|
|
||||||
peerState := State{
|
peerState := State{
|
||||||
PubKey: conn.config.Key,
|
PubKey: conn.config.Key,
|
||||||
ConnStatus: StatusDisconnected,
|
ConnStatus: StatusIdle,
|
||||||
ConnStatusUpdate: time.Now(),
|
ConnStatusUpdate: time.Now(),
|
||||||
Mux: new(sync.RWMutex),
|
Mux: new(sync.RWMutex),
|
||||||
}
|
}
|
||||||
@ -625,10 +643,10 @@ func (conn *Conn) setStatusToDisconnected() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
// pretty common error because by that time Engine can already remove the peer and status won't be available.
|
// pretty common error because by that time Engine can already remove the peer and status won't be available.
|
||||||
// todo rethink status updates
|
// todo rethink status updates
|
||||||
conn.log.Debugf("error while updating peer's state, err: %v", err)
|
conn.Log.Debugf("error while updating peer's state, err: %v", err)
|
||||||
}
|
}
|
||||||
if err := conn.statusRecorder.UpdateWireGuardPeerState(conn.config.Key, configurer.WGStats{}); err != nil {
|
if err := conn.statusRecorder.UpdateWireGuardPeerState(conn.config.Key, configurer.WGStats{}); err != nil {
|
||||||
conn.log.Debugf("failed to reset wireguard stats for peer: %s", err)
|
conn.Log.Debugf("failed to reset wireguard stats for peer: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -656,27 +674,20 @@ func (conn *Conn) waitInitialRandomSleepTime(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (conn *Conn) isRelayed() bool {
|
func (conn *Conn) isRelayed() bool {
|
||||||
if conn.statusRelay.Get() == StatusDisconnected && (conn.statusICE.Get() == StatusDisconnected || conn.statusICE.Get() == StatusConnecting) {
|
switch conn.currentConnPriority {
|
||||||
|
case conntype.Relay, conntype.ICETurn:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if conn.currentConnPriority == connPriorityICEP2P {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn *Conn) evalStatus() ConnStatus {
|
func (conn *Conn) evalStatus() ConnStatus {
|
||||||
if conn.statusRelay.Get() == StatusConnected || conn.statusICE.Get() == StatusConnected {
|
if conn.statusRelay.Get() == worker.StatusConnected || conn.statusICE.Get() == worker.StatusConnected {
|
||||||
return StatusConnected
|
return StatusConnected
|
||||||
}
|
}
|
||||||
|
|
||||||
if conn.statusRelay.Get() == StatusConnecting || conn.statusICE.Get() == StatusConnecting {
|
return StatusConnecting
|
||||||
return StatusConnecting
|
|
||||||
}
|
|
||||||
|
|
||||||
return StatusDisconnected
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn *Conn) isConnectedOnAllWay() (connected bool) {
|
func (conn *Conn) isConnectedOnAllWay() (connected bool) {
|
||||||
@ -689,12 +700,12 @@ func (conn *Conn) isConnectedOnAllWay() (connected bool) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if conn.statusICE.Get() == StatusDisconnected {
|
if conn.statusICE.Get() == worker.StatusDisconnected {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if conn.workerRelay.IsRelayConnectionSupportedWithPeer() {
|
if conn.workerRelay.IsRelayConnectionSupportedWithPeer() {
|
||||||
if conn.statusRelay.Get() != StatusConnected {
|
if conn.statusRelay.Get() == worker.StatusDisconnected {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -716,7 +727,7 @@ func (conn *Conn) freeUpConnID() {
|
|||||||
if conn.connIDRelay != "" {
|
if conn.connIDRelay != "" {
|
||||||
for _, hook := range conn.afterRemovePeerHooks {
|
for _, hook := range conn.afterRemovePeerHooks {
|
||||||
if err := hook(conn.connIDRelay); err != nil {
|
if err := hook(conn.connIDRelay); err != nil {
|
||||||
conn.log.Errorf("After remove peer hook failed: %v", err)
|
conn.Log.Errorf("After remove peer hook failed: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
conn.connIDRelay = ""
|
conn.connIDRelay = ""
|
||||||
@ -725,7 +736,7 @@ func (conn *Conn) freeUpConnID() {
|
|||||||
if conn.connIDICE != "" {
|
if conn.connIDICE != "" {
|
||||||
for _, hook := range conn.afterRemovePeerHooks {
|
for _, hook := range conn.afterRemovePeerHooks {
|
||||||
if err := hook(conn.connIDICE); err != nil {
|
if err := hook(conn.connIDICE); err != nil {
|
||||||
conn.log.Errorf("After remove peer hook failed: %v", err)
|
conn.Log.Errorf("After remove peer hook failed: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
conn.connIDICE = ""
|
conn.connIDICE = ""
|
||||||
@ -733,7 +744,7 @@ func (conn *Conn) freeUpConnID() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (conn *Conn) newProxy(remoteConn net.Conn) (wgproxy.Proxy, error) {
|
func (conn *Conn) newProxy(remoteConn net.Conn) (wgproxy.Proxy, error) {
|
||||||
conn.log.Debugf("setup proxied WireGuard connection")
|
conn.Log.Debugf("setup proxied WireGuard connection")
|
||||||
udpAddr := &net.UDPAddr{
|
udpAddr := &net.UDPAddr{
|
||||||
IP: conn.config.WgConfig.AllowedIps[0].Addr().AsSlice(),
|
IP: conn.config.WgConfig.AllowedIps[0].Addr().AsSlice(),
|
||||||
Port: conn.config.WgConfig.WgListenPort,
|
Port: conn.config.WgConfig.WgListenPort,
|
||||||
@ -741,18 +752,18 @@ func (conn *Conn) newProxy(remoteConn net.Conn) (wgproxy.Proxy, error) {
|
|||||||
|
|
||||||
wgProxy := conn.config.WgConfig.WgInterface.GetProxy()
|
wgProxy := conn.config.WgConfig.WgInterface.GetProxy()
|
||||||
if err := wgProxy.AddTurnConn(conn.ctx, udpAddr, remoteConn); err != nil {
|
if err := wgProxy.AddTurnConn(conn.ctx, udpAddr, remoteConn); err != nil {
|
||||||
conn.log.Errorf("failed to add turn net.Conn to local proxy: %v", err)
|
conn.Log.Errorf("failed to add turn net.Conn to local proxy: %v", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return wgProxy, nil
|
return wgProxy, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn *Conn) isReadyToUpgrade() bool {
|
func (conn *Conn) isReadyToUpgrade() bool {
|
||||||
return conn.wgProxyRelay != nil && conn.currentConnPriority != connPriorityRelay
|
return conn.wgProxyRelay != nil && conn.currentConnPriority != conntype.Relay
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn *Conn) isICEActive() bool {
|
func (conn *Conn) isICEActive() bool {
|
||||||
return (conn.currentConnPriority == connPriorityICEP2P || conn.currentConnPriority == connPriorityICETurn) && conn.statusICE.Get() == StatusConnected
|
return (conn.currentConnPriority == conntype.ICEP2P || conn.currentConnPriority == conntype.ICETurn) && conn.statusICE.Get() == worker.StatusConnected
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn *Conn) removeWgPeer() error {
|
func (conn *Conn) removeWgPeer() error {
|
||||||
@ -760,10 +771,10 @@ func (conn *Conn) removeWgPeer() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (conn *Conn) handleConfigurationFailure(err error, wgProxy wgproxy.Proxy) {
|
func (conn *Conn) handleConfigurationFailure(err error, wgProxy wgproxy.Proxy) {
|
||||||
conn.log.Warnf("Failed to update wg peer configuration: %v", err)
|
conn.Log.Warnf("Failed to update wg peer configuration: %v", err)
|
||||||
if wgProxy != nil {
|
if wgProxy != nil {
|
||||||
if ierr := wgProxy.CloseConn(); ierr != nil {
|
if ierr := wgProxy.CloseConn(); ierr != nil {
|
||||||
conn.log.Warnf("Failed to close wg proxy: %v", ierr)
|
conn.Log.Warnf("Failed to close wg proxy: %v", ierr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if conn.wgProxyRelay != nil {
|
if conn.wgProxyRelay != nil {
|
||||||
@ -773,16 +784,16 @@ func (conn *Conn) handleConfigurationFailure(err error, wgProxy wgproxy.Proxy) {
|
|||||||
|
|
||||||
func (conn *Conn) logTraceConnState() {
|
func (conn *Conn) logTraceConnState() {
|
||||||
if conn.workerRelay.IsRelayConnectionSupportedWithPeer() {
|
if conn.workerRelay.IsRelayConnectionSupportedWithPeer() {
|
||||||
conn.log.Tracef("connectivity guard check, relay state: %s, ice state: %s", conn.statusRelay, conn.statusICE)
|
conn.Log.Tracef("connectivity guard check, relay state: %s, ice state: %s", conn.statusRelay, conn.statusICE)
|
||||||
} else {
|
} else {
|
||||||
conn.log.Tracef("connectivity guard check, ice state: %s", conn.statusICE)
|
conn.Log.Tracef("connectivity guard check, ice state: %s", conn.statusICE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn *Conn) setRelayedProxy(proxy wgproxy.Proxy) {
|
func (conn *Conn) setRelayedProxy(proxy wgproxy.Proxy) {
|
||||||
if conn.wgProxyRelay != nil {
|
if conn.wgProxyRelay != nil {
|
||||||
if err := conn.wgProxyRelay.CloseConn(); err != nil {
|
if err := conn.wgProxyRelay.CloseConn(); err != nil {
|
||||||
conn.log.Warnf("failed to close deprecated wg proxy conn: %v", err)
|
conn.Log.Warnf("failed to close deprecated wg proxy conn: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
conn.wgProxyRelay = proxy
|
conn.wgProxyRelay = proxy
|
||||||
@ -793,6 +804,10 @@ func (conn *Conn) AllowedIP() netip.Addr {
|
|||||||
return conn.config.WgConfig.AllowedIps[0].Addr()
|
return conn.config.WgConfig.AllowedIps[0].Addr()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (conn *Conn) AgentVersionString() string {
|
||||||
|
return conn.config.AgentVersion
|
||||||
|
}
|
||||||
|
|
||||||
func (conn *Conn) presharedKey(remoteRosenpassKey []byte) *wgtypes.Key {
|
func (conn *Conn) presharedKey(remoteRosenpassKey []byte) *wgtypes.Key {
|
||||||
if conn.config.RosenpassConfig.PubKey == nil {
|
if conn.config.RosenpassConfig.PubKey == nil {
|
||||||
return conn.config.WgConfig.PreSharedKey
|
return conn.config.WgConfig.PreSharedKey
|
||||||
@ -804,7 +819,7 @@ func (conn *Conn) presharedKey(remoteRosenpassKey []byte) *wgtypes.Key {
|
|||||||
|
|
||||||
determKey, err := conn.rosenpassDetermKey()
|
determKey, err := conn.rosenpassDetermKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
conn.log.Errorf("failed to generate Rosenpass initial key: %v", err)
|
conn.Log.Errorf("failed to generate Rosenpass initial key: %v", err)
|
||||||
return conn.config.WgConfig.PreSharedKey
|
return conn.config.WgConfig.PreSharedKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,58 +1,29 @@
|
|||||||
package peer
|
package peer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync/atomic"
|
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// StatusConnected indicate the peer is in connected state
|
// StatusIdle indicate the peer is in disconnected state
|
||||||
StatusConnected ConnStatus = iota
|
StatusIdle ConnStatus = iota
|
||||||
// StatusConnecting indicate the peer is in connecting state
|
// StatusConnecting indicate the peer is in connecting state
|
||||||
StatusConnecting
|
StatusConnecting
|
||||||
// StatusDisconnected indicate the peer is in disconnected state
|
// StatusConnected indicate the peer is in connected state
|
||||||
StatusDisconnected
|
StatusConnected
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConnStatus describe the status of a peer's connection
|
// ConnStatus describe the status of a peer's connection
|
||||||
type ConnStatus int32
|
type ConnStatus int32
|
||||||
|
|
||||||
// AtomicConnStatus is a thread-safe wrapper for ConnStatus
|
|
||||||
type AtomicConnStatus struct {
|
|
||||||
status atomic.Int32
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewAtomicConnStatus creates a new AtomicConnStatus with the given initial status
|
|
||||||
func NewAtomicConnStatus() *AtomicConnStatus {
|
|
||||||
acs := &AtomicConnStatus{}
|
|
||||||
acs.Set(StatusDisconnected)
|
|
||||||
return acs
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns the current connection status
|
|
||||||
func (acs *AtomicConnStatus) Get() ConnStatus {
|
|
||||||
return ConnStatus(acs.status.Load())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set updates the connection status
|
|
||||||
func (acs *AtomicConnStatus) Set(status ConnStatus) {
|
|
||||||
acs.status.Store(int32(status))
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the string representation of the current status
|
|
||||||
func (acs *AtomicConnStatus) String() string {
|
|
||||||
return acs.Get().String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s ConnStatus) String() string {
|
func (s ConnStatus) String() string {
|
||||||
switch s {
|
switch s {
|
||||||
case StatusConnecting:
|
case StatusConnecting:
|
||||||
return "Connecting"
|
return "Connecting"
|
||||||
case StatusConnected:
|
case StatusConnected:
|
||||||
return "Connected"
|
return "Connected"
|
||||||
case StatusDisconnected:
|
case StatusIdle:
|
||||||
return "Disconnected"
|
return "Idle"
|
||||||
default:
|
default:
|
||||||
log.Errorf("unknown status: %d", s)
|
log.Errorf("unknown status: %d", s)
|
||||||
return "INVALID_PEER_CONNECTION_STATUS"
|
return "INVALID_PEER_CONNECTION_STATUS"
|
||||||
|
@ -14,7 +14,7 @@ func TestConnStatus_String(t *testing.T) {
|
|||||||
want string
|
want string
|
||||||
}{
|
}{
|
||||||
{"StatusConnected", StatusConnected, "Connected"},
|
{"StatusConnected", StatusConnected, "Connected"},
|
||||||
{"StatusDisconnected", StatusDisconnected, "Disconnected"},
|
{"StatusIdle", StatusIdle, "Idle"},
|
||||||
{"StatusConnecting", StatusConnecting, "Connecting"},
|
{"StatusConnecting", StatusConnecting, "Connecting"},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,5 +24,4 @@ func TestConnStatus_String(t *testing.T) {
|
|||||||
assert.Equal(t, got, table.want, "they should be equal")
|
assert.Equal(t, got, table.want, "they should be equal")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package peer
|
package peer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
@ -11,6 +10,7 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/iface"
|
"github.com/netbirdio/netbird/client/iface"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/peer/dispatcher"
|
||||||
"github.com/netbirdio/netbird/client/internal/peer/guard"
|
"github.com/netbirdio/netbird/client/internal/peer/guard"
|
||||||
"github.com/netbirdio/netbird/client/internal/peer/ice"
|
"github.com/netbirdio/netbird/client/internal/peer/ice"
|
||||||
"github.com/netbirdio/netbird/client/internal/stdnet"
|
"github.com/netbirdio/netbird/client/internal/stdnet"
|
||||||
@ -18,6 +18,8 @@ import (
|
|||||||
semaphoregroup "github.com/netbirdio/netbird/util/semaphore-group"
|
semaphoregroup "github.com/netbirdio/netbird/util/semaphore-group"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var testDispatcher = dispatcher.NewConnectionDispatcher()
|
||||||
|
|
||||||
var connConf = ConnConfig{
|
var connConf = ConnConfig{
|
||||||
Key: "LLHf3Ma6z6mdLbriAJbqhX7+nM/B71lgw2+91q3LfhU=",
|
Key: "LLHf3Ma6z6mdLbriAJbqhX7+nM/B71lgw2+91q3LfhU=",
|
||||||
LocalKey: "RRHf3Ma6z6mdLbriAJbqhX7+nM/B71lgw2+91q3LfhU=",
|
LocalKey: "RRHf3Ma6z6mdLbriAJbqhX7+nM/B71lgw2+91q3LfhU=",
|
||||||
@ -48,7 +50,13 @@ func TestNewConn_interfaceFilter(t *testing.T) {
|
|||||||
|
|
||||||
func TestConn_GetKey(t *testing.T) {
|
func TestConn_GetKey(t *testing.T) {
|
||||||
swWatcher := guard.NewSRWatcher(nil, nil, nil, connConf.ICEConfig)
|
swWatcher := guard.NewSRWatcher(nil, nil, nil, connConf.ICEConfig)
|
||||||
conn, err := NewConn(context.Background(), connConf, nil, nil, nil, nil, swWatcher, semaphoregroup.NewSemaphoreGroup(1))
|
|
||||||
|
sd := ServiceDependencies{
|
||||||
|
SrWatcher: swWatcher,
|
||||||
|
Semaphore: semaphoregroup.NewSemaphoreGroup(1),
|
||||||
|
PeerConnDispatcher: testDispatcher,
|
||||||
|
}
|
||||||
|
conn, err := NewConn(connConf, sd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -60,7 +68,13 @@ func TestConn_GetKey(t *testing.T) {
|
|||||||
|
|
||||||
func TestConn_OnRemoteOffer(t *testing.T) {
|
func TestConn_OnRemoteOffer(t *testing.T) {
|
||||||
swWatcher := guard.NewSRWatcher(nil, nil, nil, connConf.ICEConfig)
|
swWatcher := guard.NewSRWatcher(nil, nil, nil, connConf.ICEConfig)
|
||||||
conn, err := NewConn(context.Background(), connConf, NewRecorder("https://mgm"), nil, nil, nil, swWatcher, semaphoregroup.NewSemaphoreGroup(1))
|
sd := ServiceDependencies{
|
||||||
|
StatusRecorder: NewRecorder("https://mgm"),
|
||||||
|
SrWatcher: swWatcher,
|
||||||
|
Semaphore: semaphoregroup.NewSemaphoreGroup(1),
|
||||||
|
PeerConnDispatcher: testDispatcher,
|
||||||
|
}
|
||||||
|
conn, err := NewConn(connConf, sd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -94,7 +108,13 @@ func TestConn_OnRemoteOffer(t *testing.T) {
|
|||||||
|
|
||||||
func TestConn_OnRemoteAnswer(t *testing.T) {
|
func TestConn_OnRemoteAnswer(t *testing.T) {
|
||||||
swWatcher := guard.NewSRWatcher(nil, nil, nil, connConf.ICEConfig)
|
swWatcher := guard.NewSRWatcher(nil, nil, nil, connConf.ICEConfig)
|
||||||
conn, err := NewConn(context.Background(), connConf, NewRecorder("https://mgm"), nil, nil, nil, swWatcher, semaphoregroup.NewSemaphoreGroup(1))
|
sd := ServiceDependencies{
|
||||||
|
StatusRecorder: NewRecorder("https://mgm"),
|
||||||
|
SrWatcher: swWatcher,
|
||||||
|
Semaphore: semaphoregroup.NewSemaphoreGroup(1),
|
||||||
|
PeerConnDispatcher: testDispatcher,
|
||||||
|
}
|
||||||
|
conn, err := NewConn(connConf, sd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -125,43 +145,6 @@ func TestConn_OnRemoteAnswer(t *testing.T) {
|
|||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
func TestConn_Status(t *testing.T) {
|
|
||||||
swWatcher := guard.NewSRWatcher(nil, nil, nil, connConf.ICEConfig)
|
|
||||||
conn, err := NewConn(context.Background(), connConf, NewRecorder("https://mgm"), nil, nil, nil, swWatcher, semaphoregroup.NewSemaphoreGroup(1))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
tables := []struct {
|
|
||||||
name string
|
|
||||||
statusIce ConnStatus
|
|
||||||
statusRelay ConnStatus
|
|
||||||
want ConnStatus
|
|
||||||
}{
|
|
||||||
{"StatusConnected", StatusConnected, StatusConnected, StatusConnected},
|
|
||||||
{"StatusDisconnected", StatusDisconnected, StatusDisconnected, StatusDisconnected},
|
|
||||||
{"StatusConnecting", StatusConnecting, StatusConnecting, StatusConnecting},
|
|
||||||
{"StatusConnectingIce", StatusConnecting, StatusDisconnected, StatusConnecting},
|
|
||||||
{"StatusConnectingIceAlternative", StatusConnecting, StatusConnected, StatusConnected},
|
|
||||||
{"StatusConnectingRelay", StatusDisconnected, StatusConnecting, StatusConnecting},
|
|
||||||
{"StatusConnectingRelayAlternative", StatusConnected, StatusConnecting, StatusConnected},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, table := range tables {
|
|
||||||
t.Run(table.name, func(t *testing.T) {
|
|
||||||
si := NewAtomicConnStatus()
|
|
||||||
si.Set(table.statusIce)
|
|
||||||
conn.statusICE = si
|
|
||||||
|
|
||||||
sr := NewAtomicConnStatus()
|
|
||||||
sr.Set(table.statusRelay)
|
|
||||||
conn.statusRelay = sr
|
|
||||||
|
|
||||||
got := conn.Status()
|
|
||||||
assert.Equal(t, got, table.want, "they should be equal")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConn_presharedKey(t *testing.T) {
|
func TestConn_presharedKey(t *testing.T) {
|
||||||
conn1 := Conn{
|
conn1 := Conn{
|
||||||
|
29
client/internal/peer/conntype/priority.go
Normal file
29
client/internal/peer/conntype/priority.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package conntype
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
None ConnPriority = 0
|
||||||
|
Relay ConnPriority = 1
|
||||||
|
ICETurn ConnPriority = 2
|
||||||
|
ICEP2P ConnPriority = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConnPriority int
|
||||||
|
|
||||||
|
func (cp ConnPriority) String() string {
|
||||||
|
switch cp {
|
||||||
|
case None:
|
||||||
|
return "None"
|
||||||
|
case Relay:
|
||||||
|
return "PriorityRelay"
|
||||||
|
case ICETurn:
|
||||||
|
return "PriorityICETurn"
|
||||||
|
case ICEP2P:
|
||||||
|
return "PriorityICEP2P"
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("ConnPriority(%d)", cp)
|
||||||
|
}
|
||||||
|
}
|
52
client/internal/peer/dispatcher/dispatcher.go
Normal file
52
client/internal/peer/dispatcher/dispatcher.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package dispatcher
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/internal/peer/id"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConnectionListener struct {
|
||||||
|
OnConnected func(peerID id.ConnID)
|
||||||
|
OnDisconnected func(peerID id.ConnID)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConnectionDispatcher struct {
|
||||||
|
listeners map[*ConnectionListener]struct{}
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnectionDispatcher() *ConnectionDispatcher {
|
||||||
|
return &ConnectionDispatcher{
|
||||||
|
listeners: make(map[*ConnectionListener]struct{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ConnectionDispatcher) AddListener(listener *ConnectionListener) {
|
||||||
|
e.mu.Lock()
|
||||||
|
defer e.mu.Unlock()
|
||||||
|
e.listeners[listener] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ConnectionDispatcher) RemoveListener(listener *ConnectionListener) {
|
||||||
|
e.mu.Lock()
|
||||||
|
defer e.mu.Unlock()
|
||||||
|
|
||||||
|
delete(e.listeners, listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ConnectionDispatcher) NotifyConnected(peerConnID id.ConnID) {
|
||||||
|
e.mu.Lock()
|
||||||
|
defer e.mu.Unlock()
|
||||||
|
for listener := range e.listeners {
|
||||||
|
listener.OnConnected(peerConnID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ConnectionDispatcher) NotifyDisconnected(peerConnID id.ConnID) {
|
||||||
|
e.mu.Lock()
|
||||||
|
defer e.mu.Unlock()
|
||||||
|
for listener := range e.listeners {
|
||||||
|
listener.OnDisconnected(peerConnID)
|
||||||
|
}
|
||||||
|
}
|
@ -8,10 +8,6 @@ import (
|
|||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
reconnectMaxElapsedTime = 30 * time.Minute
|
|
||||||
)
|
|
||||||
|
|
||||||
type isConnectedFunc func() bool
|
type isConnectedFunc func() bool
|
||||||
|
|
||||||
// Guard is responsible for the reconnection logic.
|
// Guard is responsible for the reconnection logic.
|
||||||
@ -25,7 +21,6 @@ type isConnectedFunc func() bool
|
|||||||
type Guard struct {
|
type Guard struct {
|
||||||
Reconnect chan struct{}
|
Reconnect chan struct{}
|
||||||
log *log.Entry
|
log *log.Entry
|
||||||
isController bool
|
|
||||||
isConnectedOnAllWay isConnectedFunc
|
isConnectedOnAllWay isConnectedFunc
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
srWatcher *SRWatcher
|
srWatcher *SRWatcher
|
||||||
@ -33,11 +28,10 @@ type Guard struct {
|
|||||||
iCEConnDisconnected chan struct{}
|
iCEConnDisconnected chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGuard(log *log.Entry, isController bool, isConnectedFn isConnectedFunc, timeout time.Duration, srWatcher *SRWatcher) *Guard {
|
func NewGuard(log *log.Entry, isConnectedFn isConnectedFunc, timeout time.Duration, srWatcher *SRWatcher) *Guard {
|
||||||
return &Guard{
|
return &Guard{
|
||||||
Reconnect: make(chan struct{}, 1),
|
Reconnect: make(chan struct{}, 1),
|
||||||
log: log,
|
log: log,
|
||||||
isController: isController,
|
|
||||||
isConnectedOnAllWay: isConnectedFn,
|
isConnectedOnAllWay: isConnectedFn,
|
||||||
timeout: timeout,
|
timeout: timeout,
|
||||||
srWatcher: srWatcher,
|
srWatcher: srWatcher,
|
||||||
@ -46,12 +40,8 @@ func NewGuard(log *log.Entry, isController bool, isConnectedFn isConnectedFunc,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Guard) Start(ctx context.Context) {
|
func (g *Guard) Start(ctx context.Context, eventCallback func()) {
|
||||||
if g.isController {
|
g.reconnectLoopWithRetry(ctx, eventCallback)
|
||||||
g.reconnectLoopWithRetry(ctx)
|
|
||||||
} else {
|
|
||||||
g.listenForDisconnectEvents(ctx)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Guard) SetRelayedConnDisconnected() {
|
func (g *Guard) SetRelayedConnDisconnected() {
|
||||||
@ -68,9 +58,9 @@ func (g *Guard) SetICEConnDisconnected() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// reconnectLoopWithRetry periodically check (max 30 min) the connection status.
|
// reconnectLoopWithRetry periodically check the connection status.
|
||||||
// Try to send offer while the P2P is not established or while the Relay is not connected if is it supported
|
// Try to send offer while the P2P is not established or while the Relay is not connected if is it supported
|
||||||
func (g *Guard) reconnectLoopWithRetry(ctx context.Context) {
|
func (g *Guard) reconnectLoopWithRetry(ctx context.Context, callback func()) {
|
||||||
waitForInitialConnectionTry(ctx)
|
waitForInitialConnectionTry(ctx)
|
||||||
|
|
||||||
srReconnectedChan := g.srWatcher.NewListener()
|
srReconnectedChan := g.srWatcher.NewListener()
|
||||||
@ -93,7 +83,7 @@ func (g *Guard) reconnectLoopWithRetry(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !g.isConnectedOnAllWay() {
|
if !g.isConnectedOnAllWay() {
|
||||||
g.triggerOfferSending()
|
callback()
|
||||||
}
|
}
|
||||||
|
|
||||||
case <-g.relayedConnDisconnected:
|
case <-g.relayedConnDisconnected:
|
||||||
@ -121,39 +111,12 @@ func (g *Guard) reconnectLoopWithRetry(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// listenForDisconnectEvents is used when the peer is not a controller and it should reconnect to the peer
|
|
||||||
// when the connection is lost. It will try to establish a connection only once time if before the connection was established
|
|
||||||
// It track separately the ice and relay connection status. Just because a lower priority connection reestablished it does not
|
|
||||||
// mean that to switch to it. We always force to use the higher priority connection.
|
|
||||||
func (g *Guard) listenForDisconnectEvents(ctx context.Context) {
|
|
||||||
srReconnectedChan := g.srWatcher.NewListener()
|
|
||||||
defer g.srWatcher.RemoveListener(srReconnectedChan)
|
|
||||||
|
|
||||||
g.log.Infof("start listen for reconnect events...")
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-g.relayedConnDisconnected:
|
|
||||||
g.log.Debugf("Relay connection changed, triggering reconnect")
|
|
||||||
g.triggerOfferSending()
|
|
||||||
case <-g.iCEConnDisconnected:
|
|
||||||
g.log.Debugf("ICE state changed, try to send new offer")
|
|
||||||
g.triggerOfferSending()
|
|
||||||
case <-srReconnectedChan:
|
|
||||||
g.triggerOfferSending()
|
|
||||||
case <-ctx.Done():
|
|
||||||
g.log.Debugf("context is done, stop reconnect loop")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *Guard) prepareExponentTicker(ctx context.Context) *backoff.Ticker {
|
func (g *Guard) prepareExponentTicker(ctx context.Context) *backoff.Ticker {
|
||||||
bo := backoff.WithContext(&backoff.ExponentialBackOff{
|
bo := backoff.WithContext(&backoff.ExponentialBackOff{
|
||||||
InitialInterval: 800 * time.Millisecond,
|
InitialInterval: 800 * time.Millisecond,
|
||||||
RandomizationFactor: 0.1,
|
RandomizationFactor: 0.1,
|
||||||
Multiplier: 2,
|
Multiplier: 2,
|
||||||
MaxInterval: g.timeout,
|
MaxInterval: g.timeout,
|
||||||
MaxElapsedTime: reconnectMaxElapsedTime,
|
|
||||||
Stop: backoff.Stop,
|
Stop: backoff.Stop,
|
||||||
Clock: backoff.SystemClock,
|
Clock: backoff.SystemClock,
|
||||||
}, ctx)
|
}, ctx)
|
||||||
@ -164,13 +127,6 @@ func (g *Guard) prepareExponentTicker(ctx context.Context) *backoff.Ticker {
|
|||||||
return ticker
|
return ticker
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Guard) triggerOfferSending() {
|
|
||||||
select {
|
|
||||||
case g.Reconnect <- struct{}{}:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Give chance to the peer to establish the initial connection.
|
// Give chance to the peer to establish the initial connection.
|
||||||
// With it, we can decrease to send necessary offer
|
// With it, we can decrease to send necessary offer
|
||||||
func waitForInitialConnectionTry(ctx context.Context) {
|
func waitForInitialConnectionTry(ctx context.Context) {
|
||||||
|
@ -43,7 +43,6 @@ type OfferAnswer struct {
|
|||||||
|
|
||||||
type Handshaker struct {
|
type Handshaker struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
ctx context.Context
|
|
||||||
log *log.Entry
|
log *log.Entry
|
||||||
config ConnConfig
|
config ConnConfig
|
||||||
signaler *Signaler
|
signaler *Signaler
|
||||||
@ -57,9 +56,8 @@ type Handshaker struct {
|
|||||||
remoteAnswerCh chan OfferAnswer
|
remoteAnswerCh chan OfferAnswer
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHandshaker(ctx context.Context, log *log.Entry, config ConnConfig, signaler *Signaler, ice *WorkerICE, relay *WorkerRelay) *Handshaker {
|
func NewHandshaker(log *log.Entry, config ConnConfig, signaler *Signaler, ice *WorkerICE, relay *WorkerRelay) *Handshaker {
|
||||||
return &Handshaker{
|
return &Handshaker{
|
||||||
ctx: ctx,
|
|
||||||
log: log,
|
log: log,
|
||||||
config: config,
|
config: config,
|
||||||
signaler: signaler,
|
signaler: signaler,
|
||||||
@ -74,10 +72,10 @@ func (h *Handshaker) AddOnNewOfferListener(offer func(remoteOfferAnswer *OfferAn
|
|||||||
h.onNewOfferListeners = append(h.onNewOfferListeners, offer)
|
h.onNewOfferListeners = append(h.onNewOfferListeners, offer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handshaker) Listen() {
|
func (h *Handshaker) Listen(ctx context.Context) {
|
||||||
for {
|
for {
|
||||||
h.log.Info("wait for remote offer confirmation")
|
h.log.Info("wait for remote offer confirmation")
|
||||||
remoteOfferAnswer, err := h.waitForRemoteOfferConfirmation()
|
remoteOfferAnswer, err := h.waitForRemoteOfferConfirmation(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var connectionClosedError *ConnectionClosedError
|
var connectionClosedError *ConnectionClosedError
|
||||||
if errors.As(err, &connectionClosedError) {
|
if errors.As(err, &connectionClosedError) {
|
||||||
@ -127,7 +125,7 @@ func (h *Handshaker) OnRemoteAnswer(answer OfferAnswer) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handshaker) waitForRemoteOfferConfirmation() (*OfferAnswer, error) {
|
func (h *Handshaker) waitForRemoteOfferConfirmation(ctx context.Context) (*OfferAnswer, error) {
|
||||||
select {
|
select {
|
||||||
case remoteOfferAnswer := <-h.remoteOffersCh:
|
case remoteOfferAnswer := <-h.remoteOffersCh:
|
||||||
// received confirmation from the remote peer -> ready to proceed
|
// received confirmation from the remote peer -> ready to proceed
|
||||||
@ -137,7 +135,7 @@ func (h *Handshaker) waitForRemoteOfferConfirmation() (*OfferAnswer, error) {
|
|||||||
return &remoteOfferAnswer, nil
|
return &remoteOfferAnswer, nil
|
||||||
case remoteOfferAnswer := <-h.remoteAnswerCh:
|
case remoteOfferAnswer := <-h.remoteAnswerCh:
|
||||||
return &remoteOfferAnswer, nil
|
return &remoteOfferAnswer, nil
|
||||||
case <-h.ctx.Done():
|
case <-ctx.Done():
|
||||||
// closed externally
|
// closed externally
|
||||||
return nil, NewConnectionClosedError(h.config.Key)
|
return nil, NewConnectionClosedError(h.config.Key)
|
||||||
}
|
}
|
||||||
|
5
client/internal/peer/id/connid.go
Normal file
5
client/internal/peer/id/connid.go
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package id
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
type ConnID unsafe.Pointer
|
@ -15,7 +15,7 @@ import (
|
|||||||
type WGIface interface {
|
type WGIface interface {
|
||||||
UpdatePeer(peerKey string, allowedIps []netip.Prefix, keepAlive time.Duration, endpoint *net.UDPAddr, preSharedKey *wgtypes.Key) error
|
UpdatePeer(peerKey string, allowedIps []netip.Prefix, keepAlive time.Duration, endpoint *net.UDPAddr, preSharedKey *wgtypes.Key) error
|
||||||
RemovePeer(peerKey string) error
|
RemovePeer(peerKey string) error
|
||||||
GetStats(peerKey string) (configurer.WGStats, error)
|
GetStats() (map[string]configurer.WGStats, error)
|
||||||
GetProxy() wgproxy.Proxy
|
GetProxy() wgproxy.Proxy
|
||||||
Address() wgaddr.Address
|
Address() wgaddr.Address
|
||||||
}
|
}
|
||||||
|
@ -135,14 +135,15 @@ type NSGroupState struct {
|
|||||||
|
|
||||||
// FullStatus contains the full state held by the Status instance
|
// FullStatus contains the full state held by the Status instance
|
||||||
type FullStatus struct {
|
type FullStatus struct {
|
||||||
Peers []State
|
Peers []State
|
||||||
ManagementState ManagementState
|
ManagementState ManagementState
|
||||||
SignalState SignalState
|
SignalState SignalState
|
||||||
LocalPeerState LocalPeerState
|
LocalPeerState LocalPeerState
|
||||||
RosenpassState RosenpassState
|
RosenpassState RosenpassState
|
||||||
Relays []relay.ProbeResult
|
Relays []relay.ProbeResult
|
||||||
NSGroupStates []NSGroupState
|
NSGroupStates []NSGroupState
|
||||||
NumOfForwardingRules int
|
NumOfForwardingRules int
|
||||||
|
LazyConnectionEnabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status holds a state of peers, signal, management connections and relays
|
// Status holds a state of peers, signal, management connections and relays
|
||||||
@ -164,6 +165,7 @@ type Status struct {
|
|||||||
rosenpassPermissive bool
|
rosenpassPermissive bool
|
||||||
nsGroupStates []NSGroupState
|
nsGroupStates []NSGroupState
|
||||||
resolvedDomainsStates map[domain.Domain]ResolvedDomainInfo
|
resolvedDomainsStates map[domain.Domain]ResolvedDomainInfo
|
||||||
|
lazyConnectionEnabled bool
|
||||||
|
|
||||||
// To reduce the number of notification invocation this bool will be true when need to call the notification
|
// To reduce the number of notification invocation this bool will be true when need to call the notification
|
||||||
// Some Peer actions mostly used by in a batch when the network map has been synchronized. In these type of events
|
// Some Peer actions mostly used by in a batch when the network map has been synchronized. In these type of events
|
||||||
@ -219,7 +221,7 @@ func (d *Status) ReplaceOfflinePeers(replacement []State) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AddPeer adds peer to Daemon status map
|
// AddPeer adds peer to Daemon status map
|
||||||
func (d *Status) AddPeer(peerPubKey string, fqdn string) error {
|
func (d *Status) AddPeer(peerPubKey string, fqdn string, ip string) error {
|
||||||
d.mux.Lock()
|
d.mux.Lock()
|
||||||
defer d.mux.Unlock()
|
defer d.mux.Unlock()
|
||||||
|
|
||||||
@ -229,7 +231,8 @@ func (d *Status) AddPeer(peerPubKey string, fqdn string) error {
|
|||||||
}
|
}
|
||||||
d.peers[peerPubKey] = State{
|
d.peers[peerPubKey] = State{
|
||||||
PubKey: peerPubKey,
|
PubKey: peerPubKey,
|
||||||
ConnStatus: StatusDisconnected,
|
IP: ip,
|
||||||
|
ConnStatus: StatusIdle,
|
||||||
FQDN: fqdn,
|
FQDN: fqdn,
|
||||||
Mux: new(sync.RWMutex),
|
Mux: new(sync.RWMutex),
|
||||||
}
|
}
|
||||||
@ -511,9 +514,9 @@ func shouldSkipNotify(receivedConnStatus ConnStatus, curr State) bool {
|
|||||||
switch {
|
switch {
|
||||||
case receivedConnStatus == StatusConnecting:
|
case receivedConnStatus == StatusConnecting:
|
||||||
return true
|
return true
|
||||||
case receivedConnStatus == StatusDisconnected && curr.ConnStatus == StatusConnecting:
|
case receivedConnStatus == StatusIdle && curr.ConnStatus == StatusConnecting:
|
||||||
return true
|
return true
|
||||||
case receivedConnStatus == StatusDisconnected && curr.ConnStatus == StatusDisconnected:
|
case receivedConnStatus == StatusIdle && curr.ConnStatus == StatusIdle:
|
||||||
return curr.IP != ""
|
return curr.IP != ""
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
@ -689,6 +692,12 @@ func (d *Status) UpdateRosenpass(rosenpassEnabled, rosenpassPermissive bool) {
|
|||||||
d.rosenpassEnabled = rosenpassEnabled
|
d.rosenpassEnabled = rosenpassEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Status) UpdateLazyConnection(enabled bool) {
|
||||||
|
d.mux.Lock()
|
||||||
|
defer d.mux.Unlock()
|
||||||
|
d.lazyConnectionEnabled = enabled
|
||||||
|
}
|
||||||
|
|
||||||
// MarkSignalDisconnected sets SignalState to disconnected
|
// MarkSignalDisconnected sets SignalState to disconnected
|
||||||
func (d *Status) MarkSignalDisconnected(err error) {
|
func (d *Status) MarkSignalDisconnected(err error) {
|
||||||
d.mux.Lock()
|
d.mux.Lock()
|
||||||
@ -761,6 +770,12 @@ func (d *Status) GetRosenpassState() RosenpassState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Status) GetLazyConnection() bool {
|
||||||
|
d.mux.Lock()
|
||||||
|
defer d.mux.Unlock()
|
||||||
|
return d.lazyConnectionEnabled
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Status) GetManagementState() ManagementState {
|
func (d *Status) GetManagementState() ManagementState {
|
||||||
d.mux.Lock()
|
d.mux.Lock()
|
||||||
defer d.mux.Unlock()
|
defer d.mux.Unlock()
|
||||||
@ -872,12 +887,13 @@ func (d *Status) GetResolvedDomainsStates() map[domain.Domain]ResolvedDomainInfo
|
|||||||
// GetFullStatus gets full status
|
// GetFullStatus gets full status
|
||||||
func (d *Status) GetFullStatus() FullStatus {
|
func (d *Status) GetFullStatus() FullStatus {
|
||||||
fullStatus := FullStatus{
|
fullStatus := FullStatus{
|
||||||
ManagementState: d.GetManagementState(),
|
ManagementState: d.GetManagementState(),
|
||||||
SignalState: d.GetSignalState(),
|
SignalState: d.GetSignalState(),
|
||||||
Relays: d.GetRelayStates(),
|
Relays: d.GetRelayStates(),
|
||||||
RosenpassState: d.GetRosenpassState(),
|
RosenpassState: d.GetRosenpassState(),
|
||||||
NSGroupStates: d.GetDNSStates(),
|
NSGroupStates: d.GetDNSStates(),
|
||||||
NumOfForwardingRules: len(d.ForwardingRules()),
|
NumOfForwardingRules: len(d.ForwardingRules()),
|
||||||
|
LazyConnectionEnabled: d.GetLazyConnection(),
|
||||||
}
|
}
|
||||||
|
|
||||||
d.mux.Lock()
|
d.mux.Lock()
|
||||||
|
@ -10,22 +10,24 @@ import (
|
|||||||
|
|
||||||
func TestAddPeer(t *testing.T) {
|
func TestAddPeer(t *testing.T) {
|
||||||
key := "abc"
|
key := "abc"
|
||||||
|
ip := "100.108.254.1"
|
||||||
status := NewRecorder("https://mgm")
|
status := NewRecorder("https://mgm")
|
||||||
err := status.AddPeer(key, "abc.netbird")
|
err := status.AddPeer(key, "abc.netbird", ip)
|
||||||
assert.NoError(t, err, "shouldn't return error")
|
assert.NoError(t, err, "shouldn't return error")
|
||||||
|
|
||||||
_, exists := status.peers[key]
|
_, exists := status.peers[key]
|
||||||
assert.True(t, exists, "value was found")
|
assert.True(t, exists, "value was found")
|
||||||
|
|
||||||
err = status.AddPeer(key, "abc.netbird")
|
err = status.AddPeer(key, "abc.netbird", ip)
|
||||||
|
|
||||||
assert.Error(t, err, "should return error on duplicate")
|
assert.Error(t, err, "should return error on duplicate")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetPeer(t *testing.T) {
|
func TestGetPeer(t *testing.T) {
|
||||||
key := "abc"
|
key := "abc"
|
||||||
|
ip := "100.108.254.1"
|
||||||
status := NewRecorder("https://mgm")
|
status := NewRecorder("https://mgm")
|
||||||
err := status.AddPeer(key, "abc.netbird")
|
err := status.AddPeer(key, "abc.netbird", ip)
|
||||||
assert.NoError(t, err, "shouldn't return error")
|
assert.NoError(t, err, "shouldn't return error")
|
||||||
|
|
||||||
peerStatus, err := status.GetPeer(key)
|
peerStatus, err := status.GetPeer(key)
|
||||||
|
@ -2,6 +2,7 @@ package peer
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -20,7 +21,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type WGInterfaceStater interface {
|
type WGInterfaceStater interface {
|
||||||
GetStats(key string) (configurer.WGStats, error)
|
GetStats() (map[string]configurer.WGStats, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type WGWatcher struct {
|
type WGWatcher struct {
|
||||||
@ -146,9 +147,13 @@ func (w *WGWatcher) handshakeCheck(lastHandshake time.Time) (*time.Time, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *WGWatcher) wgState() (time.Time, error) {
|
func (w *WGWatcher) wgState() (time.Time, error) {
|
||||||
wgState, err := w.wgIfaceStater.GetStats(w.peerKey)
|
wgStates, err := w.wgIfaceStater.GetStats()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return time.Time{}, err
|
return time.Time{}, err
|
||||||
}
|
}
|
||||||
|
wgState, ok := wgStates[w.peerKey]
|
||||||
|
if !ok {
|
||||||
|
return time.Time{}, fmt.Errorf("peer %s not found in WireGuard endpoints", w.peerKey)
|
||||||
|
}
|
||||||
return wgState.LastHandshake, nil
|
return wgState.LastHandshake, nil
|
||||||
}
|
}
|
||||||
|
@ -11,26 +11,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type MocWgIface struct {
|
type MocWgIface struct {
|
||||||
initial bool
|
stop bool
|
||||||
lastHandshake time.Time
|
|
||||||
stop bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MocWgIface) GetStats(key string) (configurer.WGStats, error) {
|
func (m *MocWgIface) GetStats() (map[string]configurer.WGStats, error) {
|
||||||
if !m.initial {
|
return map[string]configurer.WGStats{}, nil
|
||||||
m.initial = true
|
|
||||||
return configurer.WGStats{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if !m.stop {
|
|
||||||
m.lastHandshake = time.Now()
|
|
||||||
}
|
|
||||||
|
|
||||||
stats := configurer.WGStats{
|
|
||||||
LastHandshake: m.lastHandshake,
|
|
||||||
}
|
|
||||||
|
|
||||||
return stats, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MocWgIface) disconnect() {
|
func (m *MocWgIface) disconnect() {
|
||||||
|
55
client/internal/peer/worker/state.go
Normal file
55
client/internal/peer/worker/state.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package worker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
StatusDisconnected Status = iota
|
||||||
|
StatusConnected
|
||||||
|
)
|
||||||
|
|
||||||
|
type Status int32
|
||||||
|
|
||||||
|
func (s Status) String() string {
|
||||||
|
switch s {
|
||||||
|
case StatusDisconnected:
|
||||||
|
return "Disconnected"
|
||||||
|
case StatusConnected:
|
||||||
|
return "Connected"
|
||||||
|
default:
|
||||||
|
log.Errorf("unknown status: %d", s)
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AtomicWorkerStatus is a thread-safe wrapper for worker status
|
||||||
|
type AtomicWorkerStatus struct {
|
||||||
|
status atomic.Int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAtomicStatus() *AtomicWorkerStatus {
|
||||||
|
acs := &AtomicWorkerStatus{}
|
||||||
|
acs.SetDisconnected()
|
||||||
|
return acs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the current connection status
|
||||||
|
func (acs *AtomicWorkerStatus) Get() Status {
|
||||||
|
return Status(acs.status.Load())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (acs *AtomicWorkerStatus) SetConnected() {
|
||||||
|
acs.status.Store(int32(StatusConnected))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (acs *AtomicWorkerStatus) SetDisconnected() {
|
||||||
|
acs.status.Store(int32(StatusDisconnected))
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string representation of the current status
|
||||||
|
func (acs *AtomicWorkerStatus) String() string {
|
||||||
|
return acs.Get().String()
|
||||||
|
}
|
@ -14,6 +14,7 @@ import (
|
|||||||
|
|
||||||
"github.com/netbirdio/netbird/client/iface"
|
"github.com/netbirdio/netbird/client/iface"
|
||||||
"github.com/netbirdio/netbird/client/iface/bind"
|
"github.com/netbirdio/netbird/client/iface/bind"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/peer/conntype"
|
||||||
icemaker "github.com/netbirdio/netbird/client/internal/peer/ice"
|
icemaker "github.com/netbirdio/netbird/client/internal/peer/ice"
|
||||||
"github.com/netbirdio/netbird/client/internal/stdnet"
|
"github.com/netbirdio/netbird/client/internal/stdnet"
|
||||||
"github.com/netbirdio/netbird/route"
|
"github.com/netbirdio/netbird/route"
|
||||||
@ -397,10 +398,10 @@ func isRelayed(pair *ice.CandidatePair) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func selectedPriority(pair *ice.CandidatePair) ConnPriority {
|
func selectedPriority(pair *ice.CandidatePair) conntype.ConnPriority {
|
||||||
if isRelayed(pair) {
|
if isRelayed(pair) {
|
||||||
return connPriorityICETurn
|
return conntype.ICETurn
|
||||||
} else {
|
} else {
|
||||||
return connPriorityICEP2P
|
return conntype.ICEP2P
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package peerstore
|
package peerstore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@ -79,6 +80,32 @@ func (s *Store) PeerConn(pubKey string) (*peer.Conn, bool) {
|
|||||||
return p, true
|
return p, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Store) PeerConnOpen(ctx context.Context, pubKey string) {
|
||||||
|
s.peerConnsMu.RLock()
|
||||||
|
defer s.peerConnsMu.RUnlock()
|
||||||
|
|
||||||
|
p, ok := s.peerConns[pubKey]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// this can be blocked because of the connect open limiter semaphore
|
||||||
|
if err := p.Open(ctx); err != nil {
|
||||||
|
p.Log.Errorf("failed to open peer connection: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) PeerConnClose(pubKey string) {
|
||||||
|
s.peerConnsMu.RLock()
|
||||||
|
defer s.peerConnsMu.RUnlock()
|
||||||
|
|
||||||
|
p, ok := s.peerConns[pubKey]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.Close()
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Store) PeersPubKey() []string {
|
func (s *Store) PeersPubKey() []string {
|
||||||
s.peerConnsMu.RLock()
|
s.peerConnsMu.RLock()
|
||||||
defer s.peerConnsMu.RUnlock()
|
defer s.peerConnsMu.RUnlock()
|
||||||
|
@ -3,7 +3,6 @@ package iface
|
|||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/iface/configurer"
|
|
||||||
"github.com/netbirdio/netbird/client/iface/device"
|
"github.com/netbirdio/netbird/client/iface/device"
|
||||||
"github.com/netbirdio/netbird/client/iface/wgaddr"
|
"github.com/netbirdio/netbird/client/iface/wgaddr"
|
||||||
)
|
)
|
||||||
@ -18,5 +17,4 @@ type wgIfaceBase interface {
|
|||||||
IsUserspaceBind() bool
|
IsUserspaceBind() bool
|
||||||
GetFilter() device.PacketFilter
|
GetFilter() device.PacketFilter
|
||||||
GetDevice() *device.FilteredDevice
|
GetDevice() *device.FilteredDevice
|
||||||
GetStats(peerKey string) (configurer.WGStats, error)
|
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -134,6 +134,7 @@ message LoginRequest {
|
|||||||
// omits initialized empty slices due to omitempty tags
|
// omits initialized empty slices due to omitempty tags
|
||||||
bool cleanDNSLabels = 27;
|
bool cleanDNSLabels = 27;
|
||||||
|
|
||||||
|
optional bool lazyConnectionEnabled = 28;
|
||||||
}
|
}
|
||||||
|
|
||||||
message LoginResponse {
|
message LoginResponse {
|
||||||
@ -274,6 +275,8 @@ message FullStatus {
|
|||||||
int32 NumberOfForwardingRules = 8;
|
int32 NumberOfForwardingRules = 8;
|
||||||
|
|
||||||
repeated SystemEvent events = 7;
|
repeated SystemEvent events = 7;
|
||||||
|
|
||||||
|
bool lazyConnectionEnabled = 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Networks
|
// Networks
|
||||||
|
@ -139,6 +139,7 @@ func (s *Server) Start() error {
|
|||||||
|
|
||||||
s.statusRecorder.UpdateManagementAddress(config.ManagementURL.String())
|
s.statusRecorder.UpdateManagementAddress(config.ManagementURL.String())
|
||||||
s.statusRecorder.UpdateRosenpass(config.RosenpassEnabled, config.RosenpassPermissive)
|
s.statusRecorder.UpdateRosenpass(config.RosenpassEnabled, config.RosenpassPermissive)
|
||||||
|
s.statusRecorder.UpdateLazyConnection(config.LazyConnectionEnabled)
|
||||||
|
|
||||||
if s.sessionWatcher == nil {
|
if s.sessionWatcher == nil {
|
||||||
s.sessionWatcher = internal.NewSessionWatcher(s.rootCtx, s.statusRecorder)
|
s.sessionWatcher = internal.NewSessionWatcher(s.rootCtx, s.statusRecorder)
|
||||||
@ -417,6 +418,11 @@ func (s *Server) Login(callerCtx context.Context, msg *proto.LoginRequest) (*pro
|
|||||||
s.latestConfigInput.DisableNotifications = msg.DisableNotifications
|
s.latestConfigInput.DisableNotifications = msg.DisableNotifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if msg.LazyConnectionEnabled != nil {
|
||||||
|
inputConfig.LazyConnectionEnabled = msg.LazyConnectionEnabled
|
||||||
|
s.latestConfigInput.LazyConnectionEnabled = msg.LazyConnectionEnabled
|
||||||
|
}
|
||||||
|
|
||||||
s.mutex.Unlock()
|
s.mutex.Unlock()
|
||||||
|
|
||||||
if msg.OptionalPreSharedKey != nil {
|
if msg.OptionalPreSharedKey != nil {
|
||||||
@ -804,6 +810,7 @@ func toProtoFullStatus(fullStatus peer.FullStatus) *proto.FullStatus {
|
|||||||
pbFullStatus.LocalPeerState.RosenpassEnabled = fullStatus.RosenpassState.Enabled
|
pbFullStatus.LocalPeerState.RosenpassEnabled = fullStatus.RosenpassState.Enabled
|
||||||
pbFullStatus.LocalPeerState.Networks = maps.Keys(fullStatus.LocalPeerState.Routes)
|
pbFullStatus.LocalPeerState.Networks = maps.Keys(fullStatus.LocalPeerState.Routes)
|
||||||
pbFullStatus.NumberOfForwardingRules = int32(fullStatus.NumOfForwardingRules)
|
pbFullStatus.NumberOfForwardingRules = int32(fullStatus.NumOfForwardingRules)
|
||||||
|
pbFullStatus.LazyConnectionEnabled = fullStatus.LazyConnectionEnabled
|
||||||
|
|
||||||
for _, peerState := range fullStatus.Peers {
|
for _, peerState := range fullStatus.Peers {
|
||||||
pbPeerState := &proto.PeerState{
|
pbPeerState := &proto.PeerState{
|
||||||
|
@ -97,6 +97,7 @@ type OutputOverview struct {
|
|||||||
NumberOfForwardingRules int `json:"forwardingRules" yaml:"forwardingRules"`
|
NumberOfForwardingRules int `json:"forwardingRules" yaml:"forwardingRules"`
|
||||||
NSServerGroups []NsServerGroupStateOutput `json:"dnsServers" yaml:"dnsServers"`
|
NSServerGroups []NsServerGroupStateOutput `json:"dnsServers" yaml:"dnsServers"`
|
||||||
Events []SystemEventOutput `json:"events" yaml:"events"`
|
Events []SystemEventOutput `json:"events" yaml:"events"`
|
||||||
|
LazyConnectionEnabled bool `json:"lazyConnectionEnabled" yaml:"lazyConnectionEnabled"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConvertToStatusOutputOverview(resp *proto.StatusResponse, anon bool, statusFilter string, prefixNamesFilter []string, prefixNamesFilterMap map[string]struct{}, ipsFilter map[string]struct{}) OutputOverview {
|
func ConvertToStatusOutputOverview(resp *proto.StatusResponse, anon bool, statusFilter string, prefixNamesFilter []string, prefixNamesFilterMap map[string]struct{}, ipsFilter map[string]struct{}) OutputOverview {
|
||||||
@ -136,6 +137,7 @@ func ConvertToStatusOutputOverview(resp *proto.StatusResponse, anon bool, status
|
|||||||
NumberOfForwardingRules: int(pbFullStatus.GetNumberOfForwardingRules()),
|
NumberOfForwardingRules: int(pbFullStatus.GetNumberOfForwardingRules()),
|
||||||
NSServerGroups: mapNSGroups(pbFullStatus.GetDnsServers()),
|
NSServerGroups: mapNSGroups(pbFullStatus.GetDnsServers()),
|
||||||
Events: mapEvents(pbFullStatus.GetEvents()),
|
Events: mapEvents(pbFullStatus.GetEvents()),
|
||||||
|
LazyConnectionEnabled: pbFullStatus.GetLazyConnectionEnabled(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if anon {
|
if anon {
|
||||||
@ -206,7 +208,7 @@ func mapPeers(
|
|||||||
transferSent := int64(0)
|
transferSent := int64(0)
|
||||||
|
|
||||||
isPeerConnected := pbPeerState.ConnStatus == peer.StatusConnected.String()
|
isPeerConnected := pbPeerState.ConnStatus == peer.StatusConnected.String()
|
||||||
if skipDetailByFilters(pbPeerState, isPeerConnected, statusFilter, prefixNamesFilter, prefixNamesFilterMap, ipsFilter) {
|
if skipDetailByFilters(pbPeerState, pbPeerState.ConnStatus, statusFilter, prefixNamesFilter, prefixNamesFilterMap, ipsFilter) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if isPeerConnected {
|
if isPeerConnected {
|
||||||
@ -384,6 +386,11 @@ func ParseGeneralSummary(overview OutputOverview, showURL bool, showRelays bool,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lazyConnectionEnabledStatus := "false"
|
||||||
|
if overview.LazyConnectionEnabled {
|
||||||
|
lazyConnectionEnabledStatus = "true"
|
||||||
|
}
|
||||||
|
|
||||||
peersCountString := fmt.Sprintf("%d/%d Connected", overview.Peers.Connected, overview.Peers.Total)
|
peersCountString := fmt.Sprintf("%d/%d Connected", overview.Peers.Connected, overview.Peers.Total)
|
||||||
|
|
||||||
goos := runtime.GOOS
|
goos := runtime.GOOS
|
||||||
@ -405,6 +412,7 @@ func ParseGeneralSummary(overview OutputOverview, showURL bool, showRelays bool,
|
|||||||
"NetBird IP: %s\n"+
|
"NetBird IP: %s\n"+
|
||||||
"Interface type: %s\n"+
|
"Interface type: %s\n"+
|
||||||
"Quantum resistance: %s\n"+
|
"Quantum resistance: %s\n"+
|
||||||
|
"Lazy connection: %s\n"+
|
||||||
"Networks: %s\n"+
|
"Networks: %s\n"+
|
||||||
"Forwarding rules: %d\n"+
|
"Forwarding rules: %d\n"+
|
||||||
"Peers count: %s\n",
|
"Peers count: %s\n",
|
||||||
@ -419,6 +427,7 @@ func ParseGeneralSummary(overview OutputOverview, showURL bool, showRelays bool,
|
|||||||
interfaceIP,
|
interfaceIP,
|
||||||
interfaceTypeString,
|
interfaceTypeString,
|
||||||
rosenpassEnabledStatus,
|
rosenpassEnabledStatus,
|
||||||
|
lazyConnectionEnabledStatus,
|
||||||
networks,
|
networks,
|
||||||
overview.NumberOfForwardingRules,
|
overview.NumberOfForwardingRules,
|
||||||
peersCountString,
|
peersCountString,
|
||||||
@ -533,23 +542,13 @@ func parsePeers(peers PeersStateOutput, rosenpassEnabled, rosenpassPermissive bo
|
|||||||
return peersString
|
return peersString
|
||||||
}
|
}
|
||||||
|
|
||||||
func skipDetailByFilters(
|
func skipDetailByFilters(peerState *proto.PeerState, peerStatus string, statusFilter string, prefixNamesFilter []string, prefixNamesFilterMap map[string]struct{}, ipsFilter map[string]struct{}) bool {
|
||||||
peerState *proto.PeerState,
|
|
||||||
isConnected bool,
|
|
||||||
statusFilter string,
|
|
||||||
prefixNamesFilter []string,
|
|
||||||
prefixNamesFilterMap map[string]struct{},
|
|
||||||
ipsFilter map[string]struct{},
|
|
||||||
) bool {
|
|
||||||
statusEval := false
|
statusEval := false
|
||||||
ipEval := false
|
ipEval := false
|
||||||
nameEval := true
|
nameEval := true
|
||||||
|
|
||||||
if statusFilter != "" {
|
if statusFilter != "" {
|
||||||
lowerStatusFilter := strings.ToLower(statusFilter)
|
if !strings.EqualFold(peerStatus, statusFilter) {
|
||||||
if lowerStatusFilter == "disconnected" && isConnected {
|
|
||||||
statusEval = true
|
|
||||||
} else if lowerStatusFilter == "connected" && !isConnected {
|
|
||||||
statusEval = true
|
statusEval = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -383,7 +383,8 @@ func TestParsingToJSON(t *testing.T) {
|
|||||||
"error": "timeout"
|
"error": "timeout"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"events": []
|
"events": [],
|
||||||
|
"lazyConnectionEnabled": false
|
||||||
}`
|
}`
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
|
|
||||||
@ -484,6 +485,7 @@ dnsServers:
|
|||||||
enabled: false
|
enabled: false
|
||||||
error: timeout
|
error: timeout
|
||||||
events: []
|
events: []
|
||||||
|
lazyConnectionEnabled: false
|
||||||
`
|
`
|
||||||
|
|
||||||
assert.Equal(t, expectedYAML, yaml)
|
assert.Equal(t, expectedYAML, yaml)
|
||||||
@ -548,6 +550,7 @@ FQDN: some-localhost.awesome-domain.com
|
|||||||
NetBird IP: 192.168.178.100/16
|
NetBird IP: 192.168.178.100/16
|
||||||
Interface type: Kernel
|
Interface type: Kernel
|
||||||
Quantum resistance: false
|
Quantum resistance: false
|
||||||
|
Lazy connection: false
|
||||||
Networks: 10.10.0.0/24
|
Networks: 10.10.0.0/24
|
||||||
Forwarding rules: 0
|
Forwarding rules: 0
|
||||||
Peers count: 2/2 Connected
|
Peers count: 2/2 Connected
|
||||||
@ -570,6 +573,7 @@ FQDN: some-localhost.awesome-domain.com
|
|||||||
NetBird IP: 192.168.178.100/16
|
NetBird IP: 192.168.178.100/16
|
||||||
Interface type: Kernel
|
Interface type: Kernel
|
||||||
Quantum resistance: false
|
Quantum resistance: false
|
||||||
|
Lazy connection: false
|
||||||
Networks: 10.10.0.0/24
|
Networks: 10.10.0.0/24
|
||||||
Forwarding rules: 0
|
Forwarding rules: 0
|
||||||
Peers count: 2/2 Connected
|
Peers count: 2/2 Connected
|
||||||
|
@ -193,6 +193,7 @@ type serviceClient struct {
|
|||||||
mAllowSSH *systray.MenuItem
|
mAllowSSH *systray.MenuItem
|
||||||
mAutoConnect *systray.MenuItem
|
mAutoConnect *systray.MenuItem
|
||||||
mEnableRosenpass *systray.MenuItem
|
mEnableRosenpass *systray.MenuItem
|
||||||
|
mLazyConnEnabled *systray.MenuItem
|
||||||
mNotifications *systray.MenuItem
|
mNotifications *systray.MenuItem
|
||||||
mAdvancedSettings *systray.MenuItem
|
mAdvancedSettings *systray.MenuItem
|
||||||
mCreateDebugBundle *systray.MenuItem
|
mCreateDebugBundle *systray.MenuItem
|
||||||
@ -632,6 +633,7 @@ func (s *serviceClient) onTrayReady() {
|
|||||||
s.mAllowSSH = s.mSettings.AddSubMenuItemCheckbox("Allow SSH", allowSSHMenuDescr, false)
|
s.mAllowSSH = s.mSettings.AddSubMenuItemCheckbox("Allow SSH", allowSSHMenuDescr, false)
|
||||||
s.mAutoConnect = s.mSettings.AddSubMenuItemCheckbox("Connect on Startup", autoConnectMenuDescr, false)
|
s.mAutoConnect = s.mSettings.AddSubMenuItemCheckbox("Connect on Startup", autoConnectMenuDescr, false)
|
||||||
s.mEnableRosenpass = s.mSettings.AddSubMenuItemCheckbox("Enable Quantum-Resistance", quantumResistanceMenuDescr, false)
|
s.mEnableRosenpass = s.mSettings.AddSubMenuItemCheckbox("Enable Quantum-Resistance", quantumResistanceMenuDescr, false)
|
||||||
|
s.mLazyConnEnabled = s.mSettings.AddSubMenuItemCheckbox("Enable lazy connection", lazyConnMenuDescr, false)
|
||||||
s.mNotifications = s.mSettings.AddSubMenuItemCheckbox("Notifications", notificationsMenuDescr, false)
|
s.mNotifications = s.mSettings.AddSubMenuItemCheckbox("Notifications", notificationsMenuDescr, false)
|
||||||
s.mAdvancedSettings = s.mSettings.AddSubMenuItem("Advanced Settings", advancedSettingsMenuDescr)
|
s.mAdvancedSettings = s.mSettings.AddSubMenuItem("Advanced Settings", advancedSettingsMenuDescr)
|
||||||
s.mCreateDebugBundle = s.mSettings.AddSubMenuItem("Create Debug Bundle", debugBundleMenuDescr)
|
s.mCreateDebugBundle = s.mSettings.AddSubMenuItem("Create Debug Bundle", debugBundleMenuDescr)
|
||||||
@ -691,104 +693,114 @@ func (s *serviceClient) onTrayReady() {
|
|||||||
|
|
||||||
go s.eventManager.Start(s.ctx)
|
go s.eventManager.Start(s.ctx)
|
||||||
|
|
||||||
go func() {
|
go s.listenEvents()
|
||||||
for {
|
}
|
||||||
select {
|
|
||||||
case <-s.mUp.ClickedCh:
|
|
||||||
s.mUp.Disable()
|
|
||||||
go func() {
|
|
||||||
defer s.mUp.Enable()
|
|
||||||
err := s.menuUpClick()
|
|
||||||
if err != nil {
|
|
||||||
s.app.SendNotification(fyne.NewNotification("Error", "Failed to connect to NetBird service"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
case <-s.mDown.ClickedCh:
|
|
||||||
s.mDown.Disable()
|
|
||||||
go func() {
|
|
||||||
defer s.mDown.Enable()
|
|
||||||
err := s.menuDownClick()
|
|
||||||
if err != nil {
|
|
||||||
s.app.SendNotification(fyne.NewNotification("Error", "Failed to connect to NetBird service"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
case <-s.mAllowSSH.ClickedCh:
|
|
||||||
if s.mAllowSSH.Checked() {
|
|
||||||
s.mAllowSSH.Uncheck()
|
|
||||||
} else {
|
|
||||||
s.mAllowSSH.Check()
|
|
||||||
}
|
|
||||||
if err := s.updateConfig(); err != nil {
|
|
||||||
log.Errorf("failed to update config: %v", err)
|
|
||||||
}
|
|
||||||
case <-s.mAutoConnect.ClickedCh:
|
|
||||||
if s.mAutoConnect.Checked() {
|
|
||||||
s.mAutoConnect.Uncheck()
|
|
||||||
} else {
|
|
||||||
s.mAutoConnect.Check()
|
|
||||||
}
|
|
||||||
if err := s.updateConfig(); err != nil {
|
|
||||||
log.Errorf("failed to update config: %v", err)
|
|
||||||
}
|
|
||||||
case <-s.mEnableRosenpass.ClickedCh:
|
|
||||||
if s.mEnableRosenpass.Checked() {
|
|
||||||
s.mEnableRosenpass.Uncheck()
|
|
||||||
} else {
|
|
||||||
s.mEnableRosenpass.Check()
|
|
||||||
}
|
|
||||||
if err := s.updateConfig(); err != nil {
|
|
||||||
log.Errorf("failed to update config: %v", err)
|
|
||||||
}
|
|
||||||
case <-s.mAdvancedSettings.ClickedCh:
|
|
||||||
s.mAdvancedSettings.Disable()
|
|
||||||
go func() {
|
|
||||||
defer s.mAdvancedSettings.Enable()
|
|
||||||
defer s.getSrvConfig()
|
|
||||||
s.runSelfCommand("settings", "true")
|
|
||||||
}()
|
|
||||||
case <-s.mCreateDebugBundle.ClickedCh:
|
|
||||||
s.mCreateDebugBundle.Disable()
|
|
||||||
go func() {
|
|
||||||
defer s.mCreateDebugBundle.Enable()
|
|
||||||
s.runSelfCommand("debug", "true")
|
|
||||||
}()
|
|
||||||
case <-s.mQuit.ClickedCh:
|
|
||||||
systray.Quit()
|
|
||||||
return
|
|
||||||
case <-s.mGitHub.ClickedCh:
|
|
||||||
err := openURL("https://github.com/netbirdio/netbird")
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("%s", err)
|
|
||||||
}
|
|
||||||
case <-s.mUpdate.ClickedCh:
|
|
||||||
err := openURL(version.DownloadUrl())
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("%s", err)
|
|
||||||
}
|
|
||||||
case <-s.mNetworks.ClickedCh:
|
|
||||||
s.mNetworks.Disable()
|
|
||||||
go func() {
|
|
||||||
defer s.mNetworks.Enable()
|
|
||||||
s.runSelfCommand("networks", "true")
|
|
||||||
}()
|
|
||||||
case <-s.mNotifications.ClickedCh:
|
|
||||||
if s.mNotifications.Checked() {
|
|
||||||
s.mNotifications.Uncheck()
|
|
||||||
} else {
|
|
||||||
s.mNotifications.Check()
|
|
||||||
}
|
|
||||||
if s.eventManager != nil {
|
|
||||||
s.eventManager.SetNotificationsEnabled(s.mNotifications.Checked())
|
|
||||||
}
|
|
||||||
if err := s.updateConfig(); err != nil {
|
|
||||||
log.Errorf("failed to update config: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
func (s *serviceClient) listenEvents() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-s.mUp.ClickedCh:
|
||||||
|
s.mUp.Disable()
|
||||||
|
go func() {
|
||||||
|
defer s.mUp.Enable()
|
||||||
|
err := s.menuUpClick()
|
||||||
|
if err != nil {
|
||||||
|
s.app.SendNotification(fyne.NewNotification("Error", "Failed to connect to NetBird service"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
case <-s.mDown.ClickedCh:
|
||||||
|
s.mDown.Disable()
|
||||||
|
go func() {
|
||||||
|
defer s.mDown.Enable()
|
||||||
|
err := s.menuDownClick()
|
||||||
|
if err != nil {
|
||||||
|
s.app.SendNotification(fyne.NewNotification("Error", "Failed to connect to NetBird service"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
case <-s.mAllowSSH.ClickedCh:
|
||||||
|
if s.mAllowSSH.Checked() {
|
||||||
|
s.mAllowSSH.Uncheck()
|
||||||
|
} else {
|
||||||
|
s.mAllowSSH.Check()
|
||||||
|
}
|
||||||
|
if err := s.updateConfig(); err != nil {
|
||||||
|
log.Errorf("failed to update config: %v", err)
|
||||||
|
}
|
||||||
|
case <-s.mAutoConnect.ClickedCh:
|
||||||
|
if s.mAutoConnect.Checked() {
|
||||||
|
s.mAutoConnect.Uncheck()
|
||||||
|
} else {
|
||||||
|
s.mAutoConnect.Check()
|
||||||
|
}
|
||||||
|
if err := s.updateConfig(); err != nil {
|
||||||
|
log.Errorf("failed to update config: %v", err)
|
||||||
|
}
|
||||||
|
case <-s.mEnableRosenpass.ClickedCh:
|
||||||
|
if s.mEnableRosenpass.Checked() {
|
||||||
|
s.mEnableRosenpass.Uncheck()
|
||||||
|
} else {
|
||||||
|
s.mEnableRosenpass.Check()
|
||||||
|
}
|
||||||
|
if err := s.updateConfig(); err != nil {
|
||||||
|
log.Errorf("failed to update config: %v", err)
|
||||||
|
}
|
||||||
|
case <-s.mLazyConnEnabled.ClickedCh:
|
||||||
|
if s.mLazyConnEnabled.Checked() {
|
||||||
|
s.mLazyConnEnabled.Uncheck()
|
||||||
|
} else {
|
||||||
|
s.mLazyConnEnabled.Check()
|
||||||
|
}
|
||||||
|
if err := s.updateConfig(); err != nil {
|
||||||
|
log.Errorf("failed to update config: %v", err)
|
||||||
|
}
|
||||||
|
case <-s.mAdvancedSettings.ClickedCh:
|
||||||
|
s.mAdvancedSettings.Disable()
|
||||||
|
go func() {
|
||||||
|
defer s.mAdvancedSettings.Enable()
|
||||||
|
defer s.getSrvConfig()
|
||||||
|
s.runSelfCommand("settings", "true")
|
||||||
|
}()
|
||||||
|
case <-s.mCreateDebugBundle.ClickedCh:
|
||||||
|
s.mCreateDebugBundle.Disable()
|
||||||
|
go func() {
|
||||||
|
defer s.mCreateDebugBundle.Enable()
|
||||||
|
s.runSelfCommand("debug", "true")
|
||||||
|
}()
|
||||||
|
case <-s.mQuit.ClickedCh:
|
||||||
|
systray.Quit()
|
||||||
|
return
|
||||||
|
case <-s.mGitHub.ClickedCh:
|
||||||
|
err := openURL("https://github.com/netbirdio/netbird")
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("%s", err)
|
||||||
|
}
|
||||||
|
case <-s.mUpdate.ClickedCh:
|
||||||
|
err := openURL(version.DownloadUrl())
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("%s", err)
|
||||||
|
}
|
||||||
|
case <-s.mNetworks.ClickedCh:
|
||||||
|
s.mNetworks.Disable()
|
||||||
|
go func() {
|
||||||
|
defer s.mNetworks.Enable()
|
||||||
|
s.runSelfCommand("networks", "true")
|
||||||
|
}()
|
||||||
|
case <-s.mNotifications.ClickedCh:
|
||||||
|
if s.mNotifications.Checked() {
|
||||||
|
s.mNotifications.Uncheck()
|
||||||
|
} else {
|
||||||
|
s.mNotifications.Check()
|
||||||
|
}
|
||||||
|
if s.eventManager != nil {
|
||||||
|
s.eventManager.SetNotificationsEnabled(s.mNotifications.Checked())
|
||||||
|
}
|
||||||
|
if err := s.updateConfig(); err != nil {
|
||||||
|
log.Errorf("failed to update config: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *serviceClient) runSelfCommand(command, arg string) {
|
func (s *serviceClient) runSelfCommand(command, arg string) {
|
||||||
@ -1020,6 +1032,7 @@ func (s *serviceClient) updateConfig() error {
|
|||||||
sshAllowed := s.mAllowSSH.Checked()
|
sshAllowed := s.mAllowSSH.Checked()
|
||||||
rosenpassEnabled := s.mEnableRosenpass.Checked()
|
rosenpassEnabled := s.mEnableRosenpass.Checked()
|
||||||
notificationsDisabled := !s.mNotifications.Checked()
|
notificationsDisabled := !s.mNotifications.Checked()
|
||||||
|
lazyConnectionEnabled := s.mLazyConnEnabled.Checked()
|
||||||
|
|
||||||
loginRequest := proto.LoginRequest{
|
loginRequest := proto.LoginRequest{
|
||||||
IsUnixDesktopClient: runtime.GOOS == "linux" || runtime.GOOS == "freebsd",
|
IsUnixDesktopClient: runtime.GOOS == "linux" || runtime.GOOS == "freebsd",
|
||||||
@ -1027,6 +1040,7 @@ func (s *serviceClient) updateConfig() error {
|
|||||||
RosenpassEnabled: &rosenpassEnabled,
|
RosenpassEnabled: &rosenpassEnabled,
|
||||||
DisableAutoConnect: &disableAutoStart,
|
DisableAutoConnect: &disableAutoStart,
|
||||||
DisableNotifications: ¬ificationsDisabled,
|
DisableNotifications: ¬ificationsDisabled,
|
||||||
|
LazyConnectionEnabled: &lazyConnectionEnabled,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.restartClient(&loginRequest); err != nil {
|
if err := s.restartClient(&loginRequest); err != nil {
|
||||||
|
@ -5,6 +5,7 @@ const (
|
|||||||
allowSSHMenuDescr = "Allow SSH connections"
|
allowSSHMenuDescr = "Allow SSH connections"
|
||||||
autoConnectMenuDescr = "Connect automatically when the service starts"
|
autoConnectMenuDescr = "Connect automatically when the service starts"
|
||||||
quantumResistanceMenuDescr = "Enable post-quantum security via Rosenpass"
|
quantumResistanceMenuDescr = "Enable post-quantum security via Rosenpass"
|
||||||
|
lazyConnMenuDescr = "[Experimental] Enable lazy connect"
|
||||||
notificationsMenuDescr = "Enable notifications"
|
notificationsMenuDescr = "Enable notifications"
|
||||||
advancedSettingsMenuDescr = "Advanced settings of the application"
|
advancedSettingsMenuDescr = "Advanced settings of the application"
|
||||||
debugBundleMenuDescr = "Create and open debug information bundle"
|
debugBundleMenuDescr = "Create and open debug information bundle"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.26.0
|
// protoc-gen-go v1.26.0
|
||||||
// protoc v5.29.3
|
// protoc v3.21.12
|
||||||
// source: management.proto
|
// source: management.proto
|
||||||
|
|
||||||
package proto
|
package proto
|
||||||
@ -1624,6 +1624,7 @@ type PeerConfig struct {
|
|||||||
// Peer fully qualified domain name
|
// Peer fully qualified domain name
|
||||||
Fqdn string `protobuf:"bytes,4,opt,name=fqdn,proto3" json:"fqdn,omitempty"`
|
Fqdn string `protobuf:"bytes,4,opt,name=fqdn,proto3" json:"fqdn,omitempty"`
|
||||||
RoutingPeerDnsResolutionEnabled bool `protobuf:"varint,5,opt,name=RoutingPeerDnsResolutionEnabled,proto3" json:"RoutingPeerDnsResolutionEnabled,omitempty"`
|
RoutingPeerDnsResolutionEnabled bool `protobuf:"varint,5,opt,name=RoutingPeerDnsResolutionEnabled,proto3" json:"RoutingPeerDnsResolutionEnabled,omitempty"`
|
||||||
|
LazyConnectionEnabled bool `protobuf:"varint,6,opt,name=LazyConnectionEnabled,proto3" json:"LazyConnectionEnabled,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *PeerConfig) Reset() {
|
func (x *PeerConfig) Reset() {
|
||||||
@ -1693,6 +1694,13 @@ func (x *PeerConfig) GetRoutingPeerDnsResolutionEnabled() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *PeerConfig) GetLazyConnectionEnabled() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.LazyConnectionEnabled
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// NetworkMap represents a network state of the peer with the corresponding configuration parameters to establish peer-to-peer connections
|
// NetworkMap represents a network state of the peer with the corresponding configuration parameters to establish peer-to-peer connections
|
||||||
type NetworkMap struct {
|
type NetworkMap struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
@ -1856,7 +1864,8 @@ type RemotePeerConfig struct {
|
|||||||
// SSHConfig is a SSH config of the remote peer. SSHConfig.sshPubKey should be ignored because peer knows it's SSH key.
|
// SSHConfig is a SSH config of the remote peer. SSHConfig.sshPubKey should be ignored because peer knows it's SSH key.
|
||||||
SshConfig *SSHConfig `protobuf:"bytes,3,opt,name=sshConfig,proto3" json:"sshConfig,omitempty"`
|
SshConfig *SSHConfig `protobuf:"bytes,3,opt,name=sshConfig,proto3" json:"sshConfig,omitempty"`
|
||||||
// Peer fully qualified domain name
|
// Peer fully qualified domain name
|
||||||
Fqdn string `protobuf:"bytes,4,opt,name=fqdn,proto3" json:"fqdn,omitempty"`
|
Fqdn string `protobuf:"bytes,4,opt,name=fqdn,proto3" json:"fqdn,omitempty"`
|
||||||
|
AgentVersion string `protobuf:"bytes,5,opt,name=agentVersion,proto3" json:"agentVersion,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *RemotePeerConfig) Reset() {
|
func (x *RemotePeerConfig) Reset() {
|
||||||
@ -1919,6 +1928,13 @@ func (x *RemotePeerConfig) GetFqdn() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *RemotePeerConfig) GetAgentVersion() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.AgentVersion
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
// SSHConfig represents SSH configurations of a peer.
|
// SSHConfig represents SSH configurations of a peer.
|
||||||
type SSHConfig struct {
|
type SSHConfig struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
@ -3512,7 +3528,7 @@ var file_management_proto_rawDesc = []byte{
|
|||||||
0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72,
|
0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72,
|
||||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08,
|
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08,
|
||||||
0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
|
0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
|
||||||
0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0xcb, 0x01, 0x0a, 0x0a, 0x50, 0x65, 0x65,
|
0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x81, 0x02, 0x0a, 0x0a, 0x50, 0x65, 0x65,
|
||||||
0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65,
|
0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65,
|
||||||
0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73,
|
0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73,
|
||||||
0x73, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03,
|
0x73, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03,
|
||||||
@ -3525,287 +3541,293 @@ var file_management_proto_rawDesc = []byte{
|
|||||||
0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18,
|
0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18,
|
||||||
0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1f, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x65,
|
0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1f, 0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x65,
|
||||||
0x65, 0x72, 0x44, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x45,
|
0x65, 0x72, 0x44, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x45,
|
||||||
0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0xb9, 0x05, 0x0a, 0x0a, 0x4e, 0x65, 0x74, 0x77, 0x6f,
|
0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x34, 0x0a, 0x15, 0x4c, 0x61, 0x7a, 0x79, 0x43, 0x6f,
|
||||||
0x72, 0x6b, 0x4d, 0x61, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x18,
|
0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18,
|
||||||
0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x12, 0x36, 0x0a,
|
0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x4c, 0x61, 0x7a, 0x79, 0x43, 0x6f, 0x6e, 0x6e, 0x65,
|
||||||
0x0a, 0x70, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28,
|
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0xb9, 0x05, 0x0a,
|
||||||
0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50,
|
0x0a, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x53,
|
||||||
0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x43,
|
0x65, 0x72, 0x69, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x53, 0x65, 0x72,
|
||||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3e, 0x0a, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50,
|
0x69, 0x61, 0x6c, 0x12, 0x36, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
||||||
0x65, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x61, 0x6e,
|
0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
|
||||||
0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65,
|
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52,
|
||||||
0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65,
|
0x0a, 0x70, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3e, 0x0a, 0x0b, 0x72,
|
||||||
0x50, 0x65, 0x65, 0x72, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50,
|
0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b,
|
||||||
0x65, 0x65, 0x72, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28,
|
0x32, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65,
|
||||||
0x08, 0x52, 0x12, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x49, 0x73,
|
0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0b,
|
||||||
0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x29, 0x0a, 0x06, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18,
|
0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x72,
|
||||||
0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65,
|
0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74,
|
||||||
0x6e, 0x74, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x06, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73,
|
0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50,
|
||||||
0x12, 0x33, 0x0a, 0x09, 0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x06, 0x20,
|
0x65, 0x65, 0x72, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x29, 0x0a, 0x06, 0x52,
|
||||||
0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74,
|
0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6d, 0x61,
|
||||||
0x2e, 0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, 0x44, 0x4e, 0x53, 0x43,
|
0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x06,
|
||||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x40, 0x0a, 0x0c, 0x6f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65,
|
0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x33, 0x0a, 0x09, 0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e,
|
||||||
0x50, 0x65, 0x65, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x61,
|
0x66, 0x69, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e, 0x61,
|
||||||
0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50,
|
0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||||
0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, 0x6f, 0x66, 0x66, 0x6c, 0x69,
|
0x52, 0x09, 0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x40, 0x0a, 0x0c, 0x6f,
|
||||||
0x6e, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x12, 0x3e, 0x0a, 0x0d, 0x46, 0x69, 0x72, 0x65, 0x77,
|
0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28,
|
||||||
0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18,
|
0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52,
|
||||||
0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x46, 0x69, 0x72, 0x65,
|
0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52,
|
||||||
0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61,
|
0x0c, 0x6f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x12, 0x3e, 0x0a,
|
||||||
0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x14, 0x66, 0x69, 0x72, 0x65, 0x77,
|
0x0d, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x08,
|
||||||
0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x18,
|
0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e,
|
||||||
0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x66, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52,
|
0x74, 0x2e, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d,
|
||||||
0x75, 0x6c, 0x65, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x4f, 0x0a, 0x13, 0x72,
|
0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x32, 0x0a,
|
||||||
|
0x14, 0x66, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x49, 0x73,
|
||||||
|
0x45, 0x6d, 0x70, 0x74, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x66, 0x69, 0x72,
|
||||||
|
0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74,
|
||||||
|
0x79, 0x12, 0x4f, 0x0a, 0x13, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x46, 0x69, 0x72, 0x65, 0x77,
|
||||||
|
0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d,
|
||||||
|
0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x6f, 0x75, 0x74,
|
||||||
|
0x65, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x13, 0x72,
|
||||||
0x6f, 0x75, 0x74, 0x65, 0x73, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c,
|
0x6f, 0x75, 0x74, 0x65, 0x73, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c,
|
||||||
0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67,
|
0x65, 0x73, 0x12, 0x3e, 0x0a, 0x1a, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x46, 0x69, 0x72, 0x65,
|
||||||
0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x69, 0x72, 0x65, 0x77,
|
0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
||||||
0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x13, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x46,
|
0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x46, 0x69,
|
||||||
0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x3e, 0x0a, 0x1a,
|
0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70,
|
||||||
0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75,
|
0x74, 0x79, 0x12, 0x44, 0x0a, 0x0f, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67,
|
||||||
0x6c, 0x65, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08,
|
0x52, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61,
|
||||||
0x52, 0x1a, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c,
|
0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64,
|
||||||
0x52, 0x75, 0x6c, 0x65, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x44, 0x0a, 0x0f,
|
0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0f, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64,
|
||||||
0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x18,
|
0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x22, 0xbb, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x6d,
|
||||||
0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65,
|
0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a,
|
||||||
0x6e, 0x74, 0x2e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c,
|
0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||||
0x65, 0x52, 0x0f, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c,
|
0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x6c, 0x6c,
|
||||||
0x65, 0x73, 0x22, 0x97, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65,
|
0x6f, 0x77, 0x65, 0x64, 0x49, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x61,
|
||||||
0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62,
|
0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x49, 0x70, 0x73, 0x12, 0x33, 0x0a, 0x09, 0x73, 0x73, 0x68,
|
||||||
0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62,
|
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d,
|
||||||
0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x49, 0x70,
|
0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e,
|
||||||
0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64,
|
0x66, 0x69, 0x67, 0x52, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12,
|
||||||
0x49, 0x70, 0x73, 0x12, 0x33, 0x0a, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
0x0a, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71,
|
||||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d,
|
0x64, 0x6e, 0x12, 0x22, 0x0a, 0x0c, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69,
|
||||||
0x65, 0x6e, 0x74, 0x2e, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, 0x73,
|
0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x56,
|
||||||
0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x71, 0x64, 0x6e,
|
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x49, 0x0a, 0x09, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e,
|
||||||
0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x22, 0x49, 0x0a, 0x09,
|
0x66, 0x69, 0x67, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x73, 0x68, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65,
|
||||||
0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x73, 0x68,
|
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x73, 0x73, 0x68, 0x45, 0x6e, 0x61, 0x62,
|
||||||
0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x73,
|
0x6c, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79,
|
||||||
0x73, 0x68, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x73, 0x68,
|
0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65,
|
||||||
0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x73,
|
0x79, 0x22, 0x20, 0x0a, 0x1e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f,
|
||||||
0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x22, 0x20, 0x0a, 0x1e, 0x44, 0x65, 0x76, 0x69, 0x63,
|
0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75,
|
||||||
0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c,
|
0x65, 0x73, 0x74, 0x22, 0xbf, 0x01, 0x0a, 0x17, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75,
|
||||||
0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xbf, 0x01, 0x0a, 0x17, 0x44, 0x65,
|
0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12,
|
||||||
0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f,
|
0x48, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||||
0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x48, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65,
|
0x0e, 0x32, 0x2c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x44,
|
||||||
0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
|
0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69,
|
||||||
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f,
|
0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52,
|
||||||
0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x2e, 0x70, 0x72, 0x6f,
|
0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x42, 0x0a, 0x0e, 0x50, 0x72, 0x6f,
|
||||||
0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12,
|
0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||||
0x42, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50,
|
||||||
0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
|
0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x50,
|
||||||
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e,
|
0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x16, 0x0a,
|
||||||
0x66, 0x69, 0x67, 0x52, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e,
|
0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x0a, 0x0a, 0x06, 0x48, 0x4f, 0x53,
|
||||||
0x66, 0x69, 0x67, 0x22, 0x16, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12,
|
0x54, 0x45, 0x44, 0x10, 0x00, 0x22, 0x1e, 0x0a, 0x1c, 0x50, 0x4b, 0x43, 0x45, 0x41, 0x75, 0x74,
|
||||||
0x0a, 0x0a, 0x06, 0x48, 0x4f, 0x53, 0x54, 0x45, 0x44, 0x10, 0x00, 0x22, 0x1e, 0x0a, 0x1c, 0x50,
|
0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x52, 0x65,
|
||||||
0x4b, 0x43, 0x45, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x5b, 0x0a, 0x15, 0x50, 0x4b, 0x43, 0x45, 0x41, 0x75, 0x74,
|
||||||
0x46, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x5b, 0x0a, 0x15, 0x50,
|
0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x42,
|
||||||
0x4b, 0x43, 0x45, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||||
0x46, 0x6c, 0x6f, 0x77, 0x12, 0x42, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72,
|
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d,
|
||||||
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d,
|
0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66,
|
||||||
0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64,
|
0x69, 0x67, 0x52, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66,
|
||||||
0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64,
|
0x69, 0x67, 0x22, 0xb8, 0x03, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43,
|
||||||
0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xb8, 0x03, 0x0a, 0x0e, 0x50, 0x72, 0x6f,
|
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49,
|
||||||
0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x43,
|
0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49,
|
||||||
0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x43,
|
0x44, 0x12, 0x22, 0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65,
|
||||||
0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e,
|
0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53,
|
||||||
0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x43,
|
0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18,
|
||||||
0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x44,
|
0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x1a, 0x0a,
|
||||||
0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x44, 0x6f, 0x6d,
|
0x08, 0x41, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||||
0x61, 0x69, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x41, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x18,
|
0x08, 0x41, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x2e, 0x0a, 0x12, 0x44, 0x65, 0x76,
|
||||||
0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x41, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x12,
|
0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18,
|
||||||
0x2e, 0x0a, 0x12, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x45, 0x6e, 0x64,
|
0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74,
|
||||||
0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x44, 0x65, 0x76,
|
0x68, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x54, 0x6f, 0x6b,
|
||||||
0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12,
|
0x65, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09,
|
||||||
0x24, 0x0a, 0x0d, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74,
|
0x52, 0x0d, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12,
|
||||||
0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x45, 0x6e, 0x64,
|
0x14, 0x0a, 0x05, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
|
||||||
0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x07,
|
0x53, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x55, 0x73, 0x65, 0x49, 0x44, 0x54, 0x6f,
|
||||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x55,
|
0x6b, 0x65, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x55, 0x73, 0x65, 0x49, 0x44,
|
||||||
0x73, 0x65, 0x49, 0x44, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52,
|
0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x34, 0x0a, 0x15, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
|
||||||
0x0a, 0x55, 0x73, 0x65, 0x49, 0x44, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x34, 0x0a, 0x15, 0x41,
|
0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x09,
|
||||||
0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x64, 0x70,
|
0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74,
|
||||||
0x6f, 0x69, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x41, 0x75, 0x74, 0x68,
|
0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x52,
|
||||||
0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e,
|
0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x55, 0x52, 0x4c, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28,
|
||||||
0x74, 0x12, 0x22, 0x0a, 0x0c, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x55, 0x52, 0x4c,
|
0x09, 0x52, 0x0c, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x55, 0x52, 0x4c, 0x73, 0x12,
|
||||||
0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63,
|
0x2e, 0x0a, 0x12, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74,
|
||||||
0x74, 0x55, 0x52, 0x4c, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65,
|
0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x44, 0x69, 0x73,
|
||||||
0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28,
|
0x61, 0x62, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12,
|
||||||
0x08, 0x52, 0x12, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74,
|
0x1c, 0x0a, 0x09, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x46, 0x6c, 0x61, 0x67, 0x18, 0x0c, 0x20, 0x01,
|
||||||
0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x46, 0x6c,
|
0x28, 0x0d, 0x52, 0x09, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x46, 0x6c, 0x61, 0x67, 0x22, 0xed, 0x01,
|
||||||
0x61, 0x67, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x46,
|
0x0a, 0x05, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20,
|
||||||
0x6c, 0x61, 0x67, 0x22, 0xed, 0x01, 0x0a, 0x05, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x0e, 0x0a,
|
0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f,
|
||||||
0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x18, 0x0a,
|
0x72, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72,
|
||||||
0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
|
0x6b, 0x12, 0x20, 0x0a, 0x0b, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x79, 0x70, 0x65,
|
||||||
0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x20, 0x0a, 0x0b, 0x4e, 0x65, 0x74, 0x77, 0x6f,
|
0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54,
|
||||||
0x72, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x4e, 0x65,
|
0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x65, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28,
|
||||||
0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x65, 0x65,
|
0x09, 0x52, 0x04, 0x50, 0x65, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69,
|
||||||
0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x50, 0x65, 0x65, 0x72, 0x12, 0x16, 0x0a,
|
0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12,
|
||||||
0x06, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x4d,
|
0x1e, 0x0a, 0x0a, 0x4d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x18, 0x06, 0x20,
|
||||||
0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x1e, 0x0a, 0x0a, 0x4d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72,
|
0x01, 0x28, 0x08, 0x52, 0x0a, 0x4d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x12,
|
||||||
0x61, 0x64, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x4d, 0x61, 0x73, 0x71, 0x75,
|
0x14, 0x0a, 0x05, 0x4e, 0x65, 0x74, 0x49, 0x44, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
|
||||||
0x65, 0x72, 0x61, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x4e, 0x65, 0x74, 0x49, 0x44, 0x18, 0x07,
|
0x4e, 0x65, 0x74, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73,
|
||||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x4e, 0x65, 0x74, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x44,
|
0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12,
|
||||||
0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x44, 0x6f,
|
0x1c, 0x0a, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01,
|
||||||
0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x52, 0x6f, 0x75,
|
0x28, 0x08, 0x52, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x22, 0xb4, 0x01,
|
||||||
0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x52, 0x6f,
|
0x0a, 0x09, 0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x24, 0x0a, 0x0d, 0x53,
|
||||||
0x75, 0x74, 0x65, 0x22, 0xb4, 0x01, 0x0a, 0x09, 0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01,
|
||||||
0x67, 0x12, 0x24, 0x0a, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x45, 0x6e, 0x61, 0x62,
|
0x28, 0x08, 0x52, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c,
|
||||||
0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
|
0x65, 0x12, 0x47, 0x0a, 0x10, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47,
|
||||||
0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x47, 0x0a, 0x10, 0x4e, 0x61, 0x6d, 0x65, 0x53,
|
0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x61,
|
||||||
0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28,
|
0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72,
|
||||||
0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e,
|
0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x10, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65,
|
||||||
0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x10,
|
0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x38, 0x0a, 0x0b, 0x43, 0x75,
|
||||||
0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73,
|
0x73, 0x74, 0x6f, 0x6d, 0x5a, 0x6f, 0x6e, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||||
0x12, 0x38, 0x0a, 0x0b, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5a, 0x6f, 0x6e, 0x65, 0x73, 0x18,
|
0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x75, 0x73,
|
||||||
0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65,
|
0x74, 0x6f, 0x6d, 0x5a, 0x6f, 0x6e, 0x65, 0x52, 0x0b, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5a,
|
||||||
0x6e, 0x74, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5a, 0x6f, 0x6e, 0x65, 0x52, 0x0b, 0x43,
|
0x6f, 0x6e, 0x65, 0x73, 0x22, 0x58, 0x0a, 0x0a, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5a, 0x6f,
|
||||||
0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5a, 0x6f, 0x6e, 0x65, 0x73, 0x22, 0x58, 0x0a, 0x0a, 0x43, 0x75,
|
0x6e, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01,
|
||||||
0x73, 0x74, 0x6f, 0x6d, 0x5a, 0x6f, 0x6e, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x44, 0x6f, 0x6d, 0x61,
|
0x28, 0x09, 0x52, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x32, 0x0a, 0x07, 0x52, 0x65,
|
||||||
0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
|
0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61,
|
||||||
0x12, 0x32, 0x0a, 0x07, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28,
|
0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x52,
|
||||||
0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53,
|
0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x07, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x22, 0x74,
|
||||||
0x69, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x07, 0x52, 0x65, 0x63,
|
0x0a, 0x0c, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x12,
|
||||||
0x6f, 0x72, 0x64, 0x73, 0x22, 0x74, 0x0a, 0x0c, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x65,
|
0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61,
|
||||||
0x63, 0x6f, 0x72, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
|
0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03,
|
||||||
0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65,
|
0x52, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x18,
|
||||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05,
|
0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03,
|
||||||
0x43, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x43, 0x6c, 0x61,
|
0x54, 0x54, 0x4c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x54, 0x54, 0x4c, 0x12, 0x14,
|
||||||
0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x54, 0x54, 0x4c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52,
|
0x0a, 0x05, 0x52, 0x44, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x52,
|
||||||
0x03, 0x54, 0x54, 0x4c, 0x12, 0x14, 0x0a, 0x05, 0x52, 0x44, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20,
|
0x44, 0x61, 0x74, 0x61, 0x22, 0xb3, 0x01, 0x0a, 0x0f, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72,
|
||||||
0x01, 0x28, 0x09, 0x52, 0x05, 0x52, 0x44, 0x61, 0x74, 0x61, 0x22, 0xb3, 0x01, 0x0a, 0x0f, 0x4e,
|
0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x38, 0x0a, 0x0b, 0x4e, 0x61, 0x6d, 0x65,
|
||||||
0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x38,
|
0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e,
|
||||||
0x0a, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20,
|
0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53,
|
||||||
0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74,
|
0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65,
|
||||||
0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x0b, 0x4e, 0x61, 0x6d,
|
0x72, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x02, 0x20,
|
||||||
0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x50, 0x72, 0x69, 0x6d,
|
0x01, 0x28, 0x08, 0x52, 0x07, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07,
|
||||||
0x61, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x50, 0x72, 0x69, 0x6d, 0x61,
|
0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x44,
|
||||||
0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x03, 0x20,
|
0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x32, 0x0a, 0x14, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68,
|
||||||
0x03, 0x28, 0x09, 0x52, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x32, 0x0a, 0x14,
|
0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x04,
|
||||||
0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x45, 0x6e, 0x61,
|
0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x44, 0x6f, 0x6d, 0x61,
|
||||||
0x62, 0x6c, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x53, 0x65, 0x61, 0x72,
|
0x69, 0x6e, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x48, 0x0a, 0x0a, 0x4e, 0x61,
|
||||||
0x63, 0x68, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64,
|
0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x50, 0x18, 0x01,
|
||||||
0x22, 0x48, 0x0a, 0x0a, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x0e,
|
0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x50, 0x12, 0x16, 0x0a, 0x06, 0x4e, 0x53, 0x54, 0x79,
|
||||||
0x0a, 0x02, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x50, 0x12, 0x16,
|
0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x4e, 0x53, 0x54, 0x79, 0x70, 0x65,
|
||||||
0x0a, 0x06, 0x4e, 0x53, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06,
|
0x12, 0x12, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04,
|
||||||
0x4e, 0x53, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x03,
|
0x50, 0x6f, 0x72, 0x74, 0x22, 0xa7, 0x02, 0x0a, 0x0c, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c,
|
||||||
0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x22, 0xa7, 0x02, 0x0a, 0x0c, 0x46,
|
0x6c, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x50, 0x65, 0x65, 0x72, 0x49, 0x50, 0x18,
|
||||||
0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x50,
|
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x50, 0x65, 0x65, 0x72, 0x49, 0x50, 0x12, 0x37, 0x0a,
|
||||||
0x65, 0x65, 0x72, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x50, 0x65, 0x65,
|
0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e,
|
||||||
0x72, 0x49, 0x50, 0x12, 0x37, 0x0a, 0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
|
0x32, 0x19, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75,
|
||||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d,
|
0x6c, 0x65, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x44, 0x69, 0x72,
|
||||||
0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f,
|
0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e,
|
||||||
0x6e, 0x52, 0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x06,
|
0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d,
|
||||||
0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x6d,
|
0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06,
|
||||||
0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x41, 0x63,
|
0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63,
|
||||||
0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x08,
|
0x6f, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67,
|
||||||
0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18,
|
0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63,
|
||||||
0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65,
|
0x6f, 0x6c, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x12, 0x0a, 0x04,
|
||||||
0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63,
|
0x50, 0x6f, 0x72, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x50, 0x6f, 0x72, 0x74,
|
||||||
0x6f, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09,
|
0x12, 0x30, 0x0a, 0x08, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x06, 0x20, 0x01,
|
||||||
0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x30, 0x0a, 0x08, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e,
|
0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
|
||||||
0x66, 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67,
|
0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e,
|
||||||
0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08,
|
0x66, 0x6f, 0x12, 0x1a, 0x0a, 0x08, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x44, 0x18, 0x07,
|
||||||
0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1a, 0x0a, 0x08, 0x50, 0x6f, 0x6c, 0x69,
|
0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x44, 0x22, 0x38,
|
||||||
0x63, 0x79, 0x49, 0x44, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x50, 0x6f, 0x6c, 0x69,
|
0x0a, 0x0e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73,
|
||||||
0x63, 0x79, 0x49, 0x44, 0x22, 0x38, 0x0a, 0x0e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x41,
|
0x12, 0x14, 0x0a, 0x05, 0x6e, 0x65, 0x74, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||||
0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x65, 0x74, 0x49, 0x50, 0x18,
|
0x05, 0x6e, 0x65, 0x74, 0x49, 0x50, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x61, 0x63, 0x18, 0x02, 0x20,
|
||||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x65, 0x74, 0x49, 0x50, 0x12, 0x10, 0x0a, 0x03,
|
0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x61, 0x63, 0x22, 0x1e, 0x0a, 0x06, 0x43, 0x68, 0x65, 0x63,
|
||||||
0x6d, 0x61, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x61, 0x63, 0x22, 0x1e,
|
0x6b, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
|
||||||
0x0a, 0x06, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x46, 0x69, 0x6c, 0x65,
|
0x09, 0x52, 0x05, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x22, 0x96, 0x01, 0x0a, 0x08, 0x50, 0x6f, 0x72,
|
||||||
0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x22, 0x96,
|
0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x14, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20,
|
||||||
0x01, 0x0a, 0x08, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x14, 0x0a, 0x04, 0x70,
|
0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x32, 0x0a, 0x05, 0x72,
|
||||||
0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x04, 0x70, 0x6f, 0x72,
|
0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e,
|
||||||
0x74, 0x12, 0x32, 0x0a, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
|
0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f,
|
||||||
0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f,
|
0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x48, 0x00, 0x52, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x1a,
|
||||||
0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x48, 0x00, 0x52, 0x05,
|
0x2f, 0x0a, 0x05, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72,
|
||||||
0x72, 0x61, 0x6e, 0x67, 0x65, 0x1a, 0x2f, 0x0a, 0x05, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x14,
|
0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x12, 0x10,
|
||||||
0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x73,
|
0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x65, 0x6e, 0x64,
|
||||||
0x74, 0x61, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,
|
0x42, 0x0f, 0x0a, 0x0d, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f,
|
||||||
0x0d, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x42, 0x0f, 0x0a, 0x0d, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x65,
|
0x6e, 0x22, 0x87, 0x03, 0x0a, 0x11, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x46, 0x69, 0x72, 0x65, 0x77,
|
||||||
0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x87, 0x03, 0x0a, 0x11, 0x52, 0x6f, 0x75, 0x74,
|
0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63,
|
||||||
0x65, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x22, 0x0a,
|
0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x73,
|
||||||
0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20,
|
0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x2e, 0x0a, 0x06, 0x61,
|
||||||
0x03, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x61, 0x6e, 0x67, 0x65,
|
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x6d, 0x61,
|
||||||
0x73, 0x12, 0x2e, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28,
|
0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x41, 0x63, 0x74,
|
||||||
0x0e, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52,
|
0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x64,
|
||||||
0x75, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f,
|
0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
|
||||||
0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
0x52, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a,
|
||||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74,
|
0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32,
|
||||||
0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18,
|
0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c,
|
||||||
0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65,
|
0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||||
0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52,
|
0x63, 0x6f, 0x6c, 0x12, 0x30, 0x0a, 0x08, 0x70, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x18,
|
||||||
0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x30, 0x0a, 0x08, 0x70, 0x6f, 0x72,
|
0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65,
|
||||||
0x74, 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x61,
|
0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x70, 0x6f, 0x72,
|
||||||
0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66,
|
0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x73, 0x44, 0x79, 0x6e, 0x61, 0x6d,
|
||||||
0x6f, 0x52, 0x08, 0x70, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x69,
|
0x69, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x44, 0x79, 0x6e, 0x61,
|
||||||
0x73, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09,
|
0x6d, 0x69, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x07,
|
||||||
0x69, 0x73, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d,
|
0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x26, 0x0a,
|
||||||
0x61, 0x69, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61,
|
0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18,
|
||||||
0x69, 0x6e, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x50, 0x72, 0x6f,
|
0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x50, 0x72, 0x6f,
|
||||||
0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x63, 0x75, 0x73,
|
0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49,
|
||||||
0x74, 0x6f, 0x6d, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x50,
|
0x44, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49,
|
||||||
0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x44, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x50,
|
0x44, 0x12, 0x18, 0x0a, 0x07, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x49, 0x44, 0x18, 0x0a, 0x20, 0x01,
|
||||||
0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x52, 0x6f, 0x75, 0x74, 0x65,
|
0x28, 0x09, 0x52, 0x07, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x49, 0x44, 0x22, 0xf2, 0x01, 0x0a, 0x0e,
|
||||||
0x49, 0x44, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x49,
|
0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x34,
|
||||||
0x44, 0x22, 0xf2, 0x01, 0x0a, 0x0e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67,
|
0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e,
|
||||||
0x52, 0x75, 0x6c, 0x65, 0x12, 0x34, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
|
0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75,
|
||||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d,
|
0x6c, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74,
|
||||||
0x65, 0x6e, 0x74, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
|
0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x3e, 0x0a, 0x0f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74,
|
||||||
0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x3e, 0x0a, 0x0f, 0x64, 0x65,
|
0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e,
|
||||||
0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20,
|
0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x49,
|
||||||
0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74,
|
0x6e, 0x66, 0x6f, 0x52, 0x0f, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
||||||
0x2e, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0f, 0x64, 0x65, 0x73, 0x74, 0x69,
|
0x50, 0x6f, 0x72, 0x74, 0x12, 0x2c, 0x0a, 0x11, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74,
|
||||||
0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x2c, 0x0a, 0x11, 0x74, 0x72,
|
0x65, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52,
|
||||||
0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18,
|
0x11, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65,
|
||||||
0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x65,
|
0x73, 0x73, 0x12, 0x3c, 0x0a, 0x0e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x65, 0x64,
|
||||||
0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x3c, 0x0a, 0x0e, 0x74, 0x72, 0x61, 0x6e,
|
0x50, 0x6f, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6d, 0x61, 0x6e,
|
||||||
0x73, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b,
|
0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f,
|
||||||
0x32, 0x14, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x6f,
|
0x52, 0x0e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x50, 0x6f, 0x72, 0x74,
|
||||||
0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74,
|
0x2a, 0x4c, 0x0a, 0x0c, 0x52, 0x75, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
|
||||||
0x65, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x2a, 0x4c, 0x0a, 0x0c, 0x52, 0x75, 0x6c, 0x65, 0x50, 0x72,
|
0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a,
|
||||||
0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57,
|
0x03, 0x41, 0x4c, 0x4c, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x02, 0x12,
|
||||||
0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x4c, 0x4c, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03,
|
0x07, 0x0a, 0x03, 0x55, 0x44, 0x50, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x43, 0x4d, 0x50,
|
||||||
0x54, 0x43, 0x50, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x55, 0x44, 0x50, 0x10, 0x03, 0x12, 0x08,
|
0x10, 0x04, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x55, 0x53, 0x54, 0x4f, 0x4d, 0x10, 0x05, 0x2a, 0x20,
|
||||||
0x0a, 0x04, 0x49, 0x43, 0x4d, 0x50, 0x10, 0x04, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x55, 0x53, 0x54,
|
0x0a, 0x0d, 0x52, 0x75, 0x6c, 0x65, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12,
|
||||||
0x4f, 0x4d, 0x10, 0x05, 0x2a, 0x20, 0x0a, 0x0d, 0x52, 0x75, 0x6c, 0x65, 0x44, 0x69, 0x72, 0x65,
|
0x06, 0x0a, 0x02, 0x49, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4f, 0x55, 0x54, 0x10, 0x01,
|
||||||
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x06, 0x0a, 0x02, 0x49, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a,
|
0x2a, 0x22, 0x0a, 0x0a, 0x52, 0x75, 0x6c, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0a,
|
||||||
0x03, 0x4f, 0x55, 0x54, 0x10, 0x01, 0x2a, 0x22, 0x0a, 0x0a, 0x52, 0x75, 0x6c, 0x65, 0x41, 0x63,
|
0x0a, 0x06, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x52,
|
||||||
0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x10, 0x00,
|
0x4f, 0x50, 0x10, 0x01, 0x32, 0x90, 0x04, 0x0a, 0x11, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d,
|
||||||
0x12, 0x08, 0x0a, 0x04, 0x44, 0x52, 0x4f, 0x50, 0x10, 0x01, 0x32, 0x90, 0x04, 0x0a, 0x11, 0x4d,
|
0x65, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x45, 0x0a, 0x05, 0x4c, 0x6f,
|
||||||
0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
|
0x67, 0x69, 0x6e, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74,
|
||||||
0x12, 0x45, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61,
|
0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67,
|
||||||
|
0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45,
|
||||||
|
0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22,
|
||||||
|
0x00, 0x12, 0x46, 0x0a, 0x04, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61,
|
||||||
0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64,
|
0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64,
|
||||||
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
|
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
|
||||||
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65,
|
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65,
|
||||||
0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x46, 0x0a, 0x04, 0x53, 0x79, 0x6e, 0x63, 0x12,
|
0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x0c, 0x47, 0x65, 0x74,
|
||||||
0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63,
|
0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61,
|
||||||
0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e,
|
0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1d, 0x2e, 0x6d,
|
||||||
0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79,
|
0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
|
||||||
0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12,
|
0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a,
|
||||||
0x42, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x12,
|
0x09, 0x69, 0x73, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e,
|
||||||
0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70,
|
0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x11, 0x2e,
|
||||||
0x74, 0x79, 0x1a, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
|
0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
||||||
0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
0x22, 0x00, 0x12, 0x5a, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41,
|
||||||
0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x09, 0x69, 0x73, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79,
|
|
||||||
0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d,
|
|
||||||
0x70, 0x74, 0x79, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74,
|
|
||||||
0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x44,
|
|
||||||
0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69,
|
|
||||||
0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d,
|
|
||||||
0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73,
|
|
||||||
0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e,
|
|
||||||
0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61,
|
|
||||||
0x67, 0x65, 0x22, 0x00, 0x12, 0x58, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x50, 0x4b, 0x43, 0x45, 0x41,
|
|
||||||
0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77,
|
0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77,
|
||||||
0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e,
|
0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e,
|
||||||
0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c,
|
0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c,
|
||||||
0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72,
|
0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72,
|
||||||
0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x3d,
|
0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x58,
|
||||||
0x0a, 0x08, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e,
|
0x0a, 0x18, 0x47, 0x65, 0x74, 0x50, 0x4b, 0x43, 0x45, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
|
||||||
|
0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e,
|
||||||
0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65,
|
0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65,
|
||||||
0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67,
|
0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67,
|
||||||
0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x42, 0x08, 0x5a,
|
0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d,
|
||||||
0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x3d, 0x0a, 0x08, 0x53, 0x79, 0x6e, 0x63,
|
||||||
|
0x4d, 0x65, 0x74, 0x61, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e,
|
||||||
|
0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61,
|
||||||
|
0x67, 0x65, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
|
||||||
|
0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74,
|
||||||
|
0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -254,6 +254,8 @@ message PeerConfig {
|
|||||||
string fqdn = 4;
|
string fqdn = 4;
|
||||||
|
|
||||||
bool RoutingPeerDnsResolutionEnabled = 5;
|
bool RoutingPeerDnsResolutionEnabled = 5;
|
||||||
|
|
||||||
|
bool LazyConnectionEnabled = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NetworkMap represents a network state of the peer with the corresponding configuration parameters to establish peer-to-peer connections
|
// NetworkMap represents a network state of the peer with the corresponding configuration parameters to establish peer-to-peer connections
|
||||||
@ -312,6 +314,7 @@ message RemotePeerConfig {
|
|||||||
// Peer fully qualified domain name
|
// Peer fully qualified domain name
|
||||||
string fqdn = 4;
|
string fqdn = 4;
|
||||||
|
|
||||||
|
string agentVersion = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SSHConfig represents SSH configurations of a peer.
|
// SSHConfig represents SSH configurations of a peer.
|
||||||
|
@ -691,10 +691,11 @@ func toSyncResponse(ctx context.Context, config *types.Config, peer *nbpeer.Peer
|
|||||||
func appendRemotePeerConfig(dst []*proto.RemotePeerConfig, peers []*nbpeer.Peer, dnsName string) []*proto.RemotePeerConfig {
|
func appendRemotePeerConfig(dst []*proto.RemotePeerConfig, peers []*nbpeer.Peer, dnsName string) []*proto.RemotePeerConfig {
|
||||||
for _, rPeer := range peers {
|
for _, rPeer := range peers {
|
||||||
dst = append(dst, &proto.RemotePeerConfig{
|
dst = append(dst, &proto.RemotePeerConfig{
|
||||||
WgPubKey: rPeer.Key,
|
WgPubKey: rPeer.Key,
|
||||||
AllowedIps: []string{rPeer.IP.String() + "/32"},
|
AllowedIps: []string{rPeer.IP.String() + "/32"},
|
||||||
SshConfig: &proto.SSHConfig{SshPubKey: []byte(rPeer.SSHKey)},
|
SshConfig: &proto.SSHConfig{SshPubKey: []byte(rPeer.SSHKey)},
|
||||||
Fqdn: rPeer.FQDN(dnsName),
|
Fqdn: rPeer.FQDN(dnsName),
|
||||||
|
AgentVersion: rPeer.Meta.WtVersion,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return dst
|
return dst
|
||||||
|
Loading…
x
Reference in New Issue
Block a user