mirror of
https://github.com/netbirdio/netbird.git
synced 2024-11-21 15:43:12 +01:00
Add route selection functionality for CLI and GUI (#1865)
This commit is contained in:
parent
3477108ce7
commit
f51dc13f8c
@ -119,6 +119,7 @@ func init() {
|
||||
rootCmd.PersistentFlags().StringVarP(&setupKey, "setup-key", "k", "", "Setup key obtained from the Management Service Dashboard (used to register peer)")
|
||||
rootCmd.PersistentFlags().StringVar(&preSharedKey, preSharedKeyFlag, "", "Sets Wireguard PreSharedKey property. If set, then only peers that have the same key can communicate.")
|
||||
rootCmd.PersistentFlags().StringVarP(&hostName, "hostname", "n", "", "Sets a custom hostname for the device")
|
||||
|
||||
rootCmd.AddCommand(serviceCmd)
|
||||
rootCmd.AddCommand(upCmd)
|
||||
rootCmd.AddCommand(downCmd)
|
||||
@ -126,8 +127,14 @@ func init() {
|
||||
rootCmd.AddCommand(loginCmd)
|
||||
rootCmd.AddCommand(versionCmd)
|
||||
rootCmd.AddCommand(sshCmd)
|
||||
rootCmd.AddCommand(routesCmd)
|
||||
|
||||
serviceCmd.AddCommand(runCmd, startCmd, stopCmd, restartCmd) // service control commands are subcommands of service
|
||||
serviceCmd.AddCommand(installCmd, uninstallCmd) // service installer commands are subcommands of service
|
||||
|
||||
routesCmd.AddCommand(routesListCmd)
|
||||
routesCmd.AddCommand(routesSelectCmd, routesDeselectCmd)
|
||||
|
||||
upCmd.PersistentFlags().StringSliceVar(&natExternalIPs, externalIPMapFlag, nil,
|
||||
`Sets external IPs maps between local addresses and interfaces.`+
|
||||
`You can specify a comma-separated list with a single IP and IP/IP or IP/Interface Name. `+
|
||||
|
144
client/cmd/route.go
Normal file
144
client/cmd/route.go
Normal file
@ -0,0 +1,144 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/netbirdio/netbird/client/proto"
|
||||
)
|
||||
|
||||
var appendFlag bool
|
||||
|
||||
var routesCmd = &cobra.Command{
|
||||
Use: "routes",
|
||||
Short: "Manage network routes",
|
||||
Long: `Commands to list, select, or deselect network routes.`,
|
||||
}
|
||||
|
||||
var routesListCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Aliases: []string{"ls"},
|
||||
Short: "List routes",
|
||||
Example: " netbird routes list",
|
||||
Long: "List all available network routes.",
|
||||
RunE: routesList,
|
||||
}
|
||||
|
||||
var routesSelectCmd = &cobra.Command{
|
||||
Use: "select route...|all",
|
||||
Short: "Select routes",
|
||||
Long: "Select a list of routes by identifiers or 'all' to clear all selections and to accept all (including new) routes.\nDefault mode is replace, use -a to append to already selected routes.",
|
||||
Example: " netbird routes select all\n netbird routes select route1 route2\n netbird routes select -a route3",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: routesSelect,
|
||||
}
|
||||
|
||||
var routesDeselectCmd = &cobra.Command{
|
||||
Use: "deselect route...|all",
|
||||
Short: "Deselect routes",
|
||||
Long: "Deselect previously selected routes by identifiers or 'all' to disable accepting any routes.",
|
||||
Example: " netbird routes deselect all\n netbird routes deselect route1 route2",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: routesDeselect,
|
||||
}
|
||||
|
||||
func init() {
|
||||
routesSelectCmd.PersistentFlags().BoolVarP(&appendFlag, "append", "a", false, "Append to current route selection instead of replacing")
|
||||
}
|
||||
|
||||
func routesList(cmd *cobra.Command, _ []string) error {
|
||||
conn, err := getClient(cmd.Context())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
client := proto.NewDaemonServiceClient(conn)
|
||||
resp, err := client.ListRoutes(cmd.Context(), &proto.ListRoutesRequest{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to list routes: %v", status.Convert(err).Message())
|
||||
}
|
||||
|
||||
if len(resp.Routes) == 0 {
|
||||
cmd.Println("No routes available.")
|
||||
return nil
|
||||
}
|
||||
|
||||
cmd.Println("Available Routes:")
|
||||
for _, route := range resp.Routes {
|
||||
selectedStatus := "Not Selected"
|
||||
if route.GetSelected() {
|
||||
selectedStatus = "Selected"
|
||||
}
|
||||
cmd.Printf("\n - ID: %s\n Network: %s\n Status: %s\n", route.GetID(), route.GetNetwork(), selectedStatus)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func routesSelect(cmd *cobra.Command, args []string) error {
|
||||
conn, err := getClient(cmd.Context())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
client := proto.NewDaemonServiceClient(conn)
|
||||
req := &proto.SelectRoutesRequest{
|
||||
RouteIDs: args,
|
||||
}
|
||||
|
||||
if len(args) == 1 && args[0] == "all" {
|
||||
req.All = true
|
||||
} else if appendFlag {
|
||||
req.Append = true
|
||||
}
|
||||
|
||||
if _, err := client.SelectRoutes(cmd.Context(), req); err != nil {
|
||||
return fmt.Errorf("failed to select routes: %v", status.Convert(err).Message())
|
||||
}
|
||||
|
||||
cmd.Println("Routes selected successfully.")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func routesDeselect(cmd *cobra.Command, args []string) error {
|
||||
conn, err := getClient(cmd.Context())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
client := proto.NewDaemonServiceClient(conn)
|
||||
req := &proto.SelectRoutesRequest{
|
||||
RouteIDs: args,
|
||||
}
|
||||
|
||||
if len(args) == 1 && args[0] == "all" {
|
||||
req.All = true
|
||||
}
|
||||
|
||||
if _, err := client.DeselectRoutes(cmd.Context(), req); err != nil {
|
||||
return fmt.Errorf("failed to deselect routes: %v", status.Convert(err).Message())
|
||||
}
|
||||
|
||||
cmd.Println("Routes deselected successfully.")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getClient(ctx context.Context) (*grpc.ClientConn, error) {
|
||||
conn, err := DialClientGRPCServer(ctx, daemonAddr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to connect to daemon error: %v\n"+
|
||||
"If the daemon is not running please run: "+
|
||||
"\nnetbird service install \nnetbird service start\n", err)
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
@ -24,7 +24,7 @@ var (
|
||||
)
|
||||
|
||||
var sshCmd = &cobra.Command{
|
||||
Use: "ssh",
|
||||
Use: "ssh [user@]host",
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return errors.New("requires a host argument")
|
||||
@ -94,7 +94,7 @@ func runSSH(ctx context.Context, addr string, pemKey []byte, cmd *cobra.Command)
|
||||
if err != nil {
|
||||
cmd.Printf("Error: %v\n", err)
|
||||
cmd.Printf("Couldn't connect. Please check the connection status or if the ssh server is enabled on the other peer" +
|
||||
"You can verify the connection by running:\n\n" +
|
||||
"\nYou can verify the connection by running:\n\n" +
|
||||
" netbird status\n\n")
|
||||
return err
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ import (
|
||||
|
||||
// RunClient with main logic.
|
||||
func RunClient(ctx context.Context, config *Config, statusRecorder *peer.Status) error {
|
||||
return runClient(ctx, config, statusRecorder, MobileDependency{}, nil, nil, nil, nil)
|
||||
return runClient(ctx, config, statusRecorder, MobileDependency{}, nil, nil, nil, nil, nil)
|
||||
}
|
||||
|
||||
// RunClientWithProbes runs the client's main logic with probes attached
|
||||
@ -43,8 +43,9 @@ func RunClientWithProbes(
|
||||
signalProbe *Probe,
|
||||
relayProbe *Probe,
|
||||
wgProbe *Probe,
|
||||
engineChan chan<- *Engine,
|
||||
) error {
|
||||
return runClient(ctx, config, statusRecorder, MobileDependency{}, mgmProbe, signalProbe, relayProbe, wgProbe)
|
||||
return runClient(ctx, config, statusRecorder, MobileDependency{}, mgmProbe, signalProbe, relayProbe, wgProbe, engineChan)
|
||||
}
|
||||
|
||||
// RunClientMobile with main logic on mobile system
|
||||
@ -66,7 +67,7 @@ func RunClientMobile(
|
||||
HostDNSAddresses: dnsAddresses,
|
||||
DnsReadyListener: dnsReadyListener,
|
||||
}
|
||||
return runClient(ctx, config, statusRecorder, mobileDependency, nil, nil, nil, nil)
|
||||
return runClient(ctx, config, statusRecorder, mobileDependency, nil, nil, nil, nil, nil)
|
||||
}
|
||||
|
||||
func RunClientiOS(
|
||||
@ -82,7 +83,7 @@ func RunClientiOS(
|
||||
NetworkChangeListener: networkChangeListener,
|
||||
DnsManager: dnsManager,
|
||||
}
|
||||
return runClient(ctx, config, statusRecorder, mobileDependency, nil, nil, nil, nil)
|
||||
return runClient(ctx, config, statusRecorder, mobileDependency, nil, nil, nil, nil, nil)
|
||||
}
|
||||
|
||||
func runClient(
|
||||
@ -94,6 +95,7 @@ func runClient(
|
||||
signalProbe *Probe,
|
||||
relayProbe *Probe,
|
||||
wgProbe *Probe,
|
||||
engineChan chan<- *Engine,
|
||||
) error {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
@ -243,6 +245,9 @@ func runClient(
|
||||
log.Errorf("error while starting Netbird Connection Engine: %s", err)
|
||||
return wrapErr(err)
|
||||
}
|
||||
if engineChan != nil {
|
||||
engineChan <- engine
|
||||
}
|
||||
|
||||
log.Print("Netbird engine started, my IP is: ", peerConfig.Address)
|
||||
state.Set(StatusConnected)
|
||||
@ -252,6 +257,10 @@ func runClient(
|
||||
|
||||
backOff.Reset()
|
||||
|
||||
if engineChan != nil {
|
||||
engineChan <- nil
|
||||
}
|
||||
|
||||
err = engine.Stop()
|
||||
if err != nil {
|
||||
log.Errorf("failed stopping engine %v", err)
|
||||
|
@ -111,6 +111,9 @@ type Engine struct {
|
||||
// TURNs is a list of STUN servers used by ICE
|
||||
TURNs []*stun.URI
|
||||
|
||||
// clientRoutes is the most recent list of clientRoutes received from the Management Service
|
||||
clientRoutes map[string][]*route.Route
|
||||
|
||||
cancel context.CancelFunc
|
||||
|
||||
ctx context.Context
|
||||
@ -216,6 +219,8 @@ func (e *Engine) Stop() error {
|
||||
return err
|
||||
}
|
||||
|
||||
e.clientRoutes = nil
|
||||
|
||||
// very ugly but we want to remove peers from the WireGuard interface first before removing interface.
|
||||
// Removing peers happens in the conn.CLose() asynchronously
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
@ -695,11 +700,14 @@ func (e *Engine) updateNetworkMap(networkMap *mgmProto.NetworkMap) error {
|
||||
if protoRoutes == nil {
|
||||
protoRoutes = []*mgmProto.Route{}
|
||||
}
|
||||
err := e.routeManager.UpdateRoutes(serial, toRoutes(protoRoutes))
|
||||
|
||||
_, clientRoutes, err := e.routeManager.UpdateRoutes(serial, toRoutes(protoRoutes))
|
||||
if err != nil {
|
||||
log.Errorf("failed to update routes, err: %v", err)
|
||||
log.Errorf("failed to update clientRoutes, err: %v", err)
|
||||
}
|
||||
|
||||
e.clientRoutes = clientRoutes
|
||||
|
||||
protoDNSConfig := networkMap.GetDNSConfig()
|
||||
if protoDNSConfig == nil {
|
||||
protoDNSConfig = &mgmProto.DNSConfig{}
|
||||
@ -1229,6 +1237,28 @@ func (e *Engine) newDnsServer() ([]*route.Route, dns.Server, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// GetClientRoutes returns the current routes from the route map
|
||||
func (e *Engine) GetClientRoutes() map[string][]*route.Route {
|
||||
return e.clientRoutes
|
||||
}
|
||||
|
||||
// GetClientRoutesWithNetID returns the current routes from the route map, but the keys consist of the network ID only
|
||||
func (e *Engine) GetClientRoutesWithNetID() map[string][]*route.Route {
|
||||
routes := make(map[string][]*route.Route, len(e.clientRoutes))
|
||||
for id, v := range e.clientRoutes {
|
||||
if i := strings.LastIndex(id, "-"); i != -1 {
|
||||
id = id[:i]
|
||||
}
|
||||
routes[id] = v
|
||||
}
|
||||
return routes
|
||||
}
|
||||
|
||||
// GetRouteManager returns the route manager
|
||||
func (e *Engine) GetRouteManager() routemanager.Manager {
|
||||
return e.routeManager
|
||||
}
|
||||
|
||||
func findIPFromInterfaceName(ifaceName string) (net.IP, error) {
|
||||
iface, err := net.InterfaceByName(ifaceName)
|
||||
if err != nil {
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"google.golang.org/grpc/keepalive"
|
||||
|
||||
"github.com/netbirdio/management-integrations/integrations"
|
||||
|
||||
"github.com/netbirdio/netbird/client/internal/dns"
|
||||
"github.com/netbirdio/netbird/client/internal/peer"
|
||||
"github.com/netbirdio/netbird/client/internal/routemanager"
|
||||
@ -577,10 +578,10 @@ func TestEngine_UpdateNetworkMapWithRoutes(t *testing.T) {
|
||||
}{}
|
||||
|
||||
mockRouteManager := &routemanager.MockManager{
|
||||
UpdateRoutesFunc: func(updateSerial uint64, newRoutes []*route.Route) error {
|
||||
UpdateRoutesFunc: func(updateSerial uint64, newRoutes []*route.Route) (map[string]*route.Route, map[string][]*route.Route, error) {
|
||||
input.inputSerial = updateSerial
|
||||
input.inputRoutes = newRoutes
|
||||
return testCase.inputErr
|
||||
return nil, nil, testCase.inputErr
|
||||
},
|
||||
}
|
||||
|
||||
@ -597,8 +598,8 @@ func TestEngine_UpdateNetworkMapWithRoutes(t *testing.T) {
|
||||
err = engine.updateNetworkMap(testCase.networkMap)
|
||||
assert.NoError(t, err, "shouldn't return error")
|
||||
assert.Equal(t, testCase.expectedSerial, input.inputSerial, "serial should match")
|
||||
assert.Len(t, input.inputRoutes, testCase.expectedLen, "routes len should match")
|
||||
assert.Equal(t, testCase.expectedRoutes, input.inputRoutes, "routes should match")
|
||||
assert.Len(t, input.inputRoutes, testCase.expectedLen, "clientRoutes len should match")
|
||||
assert.Equal(t, testCase.expectedRoutes, input.inputRoutes, "clientRoutes should match")
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -742,8 +743,8 @@ func TestEngine_UpdateNetworkMapWithDNSUpdate(t *testing.T) {
|
||||
assert.NoError(t, err, "shouldn't return error")
|
||||
|
||||
mockRouteManager := &routemanager.MockManager{
|
||||
UpdateRoutesFunc: func(updateSerial uint64, newRoutes []*route.Route) error {
|
||||
return nil
|
||||
UpdateRoutesFunc: func(updateSerial uint64, newRoutes []*route.Route) (map[string]*route.Route, map[string][]*route.Route, error) {
|
||||
return nil, nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
firewall "github.com/netbirdio/netbird/client/firewall/manager"
|
||||
"github.com/netbirdio/netbird/client/internal/listener"
|
||||
"github.com/netbirdio/netbird/client/internal/peer"
|
||||
"github.com/netbirdio/netbird/client/internal/routeselector"
|
||||
"github.com/netbirdio/netbird/iface"
|
||||
"github.com/netbirdio/netbird/route"
|
||||
nbnet "github.com/netbirdio/netbird/util/net"
|
||||
@ -28,7 +29,9 @@ var defaultv6 = netip.PrefixFrom(netip.IPv6Unspecified(), 0)
|
||||
// Manager is a route manager interface
|
||||
type Manager interface {
|
||||
Init() (peer.BeforeAddPeerHookFunc, peer.AfterRemovePeerHookFunc, error)
|
||||
UpdateRoutes(updateSerial uint64, newRoutes []*route.Route) error
|
||||
UpdateRoutes(updateSerial uint64, newRoutes []*route.Route) (map[string]*route.Route, map[string][]*route.Route, error)
|
||||
TriggerSelection(map[string][]*route.Route)
|
||||
GetRouteSelector() *routeselector.RouteSelector
|
||||
SetRouteChangeListener(listener listener.NetworkChangeListener)
|
||||
InitialRouteRange() []string
|
||||
EnableServerRouter(firewall firewall.Manager) error
|
||||
@ -41,6 +44,7 @@ type DefaultManager struct {
|
||||
stop context.CancelFunc
|
||||
mux sync.Mutex
|
||||
clientNetworks map[string]*clientNetwork
|
||||
routeSelector *routeselector.RouteSelector
|
||||
serverRouter serverRouter
|
||||
statusRecorder *peer.Status
|
||||
wgInterface *iface.WGIface
|
||||
@ -54,6 +58,7 @@ func NewManager(ctx context.Context, pubKey string, wgInterface *iface.WGIface,
|
||||
ctx: mCTX,
|
||||
stop: cancel,
|
||||
clientNetworks: make(map[string]*clientNetwork),
|
||||
routeSelector: routeselector.NewRouteSelector(),
|
||||
statusRecorder: statusRecorder,
|
||||
wgInterface: wgInterface,
|
||||
pubKey: pubKey,
|
||||
@ -117,28 +122,29 @@ func (m *DefaultManager) Stop() {
|
||||
}
|
||||
|
||||
// UpdateRoutes compares received routes with existing routes and removes, updates or adds them to the client and server maps
|
||||
func (m *DefaultManager) UpdateRoutes(updateSerial uint64, newRoutes []*route.Route) error {
|
||||
func (m *DefaultManager) UpdateRoutes(updateSerial uint64, newRoutes []*route.Route) (map[string]*route.Route, map[string][]*route.Route, error) {
|
||||
select {
|
||||
case <-m.ctx.Done():
|
||||
log.Infof("not updating routes as context is closed")
|
||||
return m.ctx.Err()
|
||||
return nil, nil, m.ctx.Err()
|
||||
default:
|
||||
m.mux.Lock()
|
||||
defer m.mux.Unlock()
|
||||
|
||||
newServerRoutesMap, newClientRoutesIDMap := m.classifiesRoutes(newRoutes)
|
||||
newServerRoutesMap, newClientRoutesIDMap := m.classifyRoutes(newRoutes)
|
||||
|
||||
m.updateClientNetworks(updateSerial, newClientRoutesIDMap)
|
||||
m.notifier.onNewRoutes(newClientRoutesIDMap)
|
||||
filteredClientRoutes := m.routeSelector.FilterSelected(newClientRoutesIDMap)
|
||||
m.updateClientNetworks(updateSerial, filteredClientRoutes)
|
||||
m.notifier.onNewRoutes(filteredClientRoutes)
|
||||
|
||||
if m.serverRouter != nil {
|
||||
err := m.serverRouter.updateRoutes(newServerRoutesMap)
|
||||
if err != nil {
|
||||
return fmt.Errorf("update routes: %w", err)
|
||||
return nil, nil, fmt.Errorf("update routes: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return newServerRoutesMap, newClientRoutesIDMap, nil
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,16 +158,51 @@ func (m *DefaultManager) InitialRouteRange() []string {
|
||||
return m.notifier.initialRouteRanges()
|
||||
}
|
||||
|
||||
func (m *DefaultManager) updateClientNetworks(updateSerial uint64, networks map[string][]*route.Route) {
|
||||
// removing routes that do not exist as per the update from the Management service.
|
||||
// GetRouteSelector returns the route selector
|
||||
func (m *DefaultManager) GetRouteSelector() *routeselector.RouteSelector {
|
||||
return m.routeSelector
|
||||
}
|
||||
|
||||
// GetClientRoutes returns the client routes
|
||||
func (m *DefaultManager) GetClientRoutes() map[string]*clientNetwork {
|
||||
return m.clientNetworks
|
||||
}
|
||||
|
||||
// TriggerSelection triggers the selection of routes, stopping deselected watchers and starting newly selected ones
|
||||
func (m *DefaultManager) TriggerSelection(networks map[string][]*route.Route) {
|
||||
m.mux.Lock()
|
||||
defer m.mux.Unlock()
|
||||
|
||||
networks = m.routeSelector.FilterSelected(networks)
|
||||
m.stopObsoleteClients(networks)
|
||||
|
||||
for id, routes := range networks {
|
||||
if _, found := m.clientNetworks[id]; found {
|
||||
// don't touch existing client network watchers
|
||||
continue
|
||||
}
|
||||
|
||||
clientNetworkWatcher := newClientNetworkWatcher(m.ctx, m.wgInterface, m.statusRecorder, routes[0].Network)
|
||||
m.clientNetworks[id] = clientNetworkWatcher
|
||||
go clientNetworkWatcher.peersStateAndUpdateWatcher()
|
||||
clientNetworkWatcher.sendUpdateToClientNetworkWatcher(routesUpdate{routes: routes})
|
||||
}
|
||||
}
|
||||
|
||||
// stopObsoleteClients stops the client network watcher for the networks that are not in the new list
|
||||
func (m *DefaultManager) stopObsoleteClients(networks map[string][]*route.Route) {
|
||||
for id, client := range m.clientNetworks {
|
||||
_, found := networks[id]
|
||||
if !found {
|
||||
log.Debugf("stopping client network watcher, %s", id)
|
||||
if _, ok := networks[id]; !ok {
|
||||
log.Debugf("Stopping client network watcher, %s", id)
|
||||
client.stop()
|
||||
delete(m.clientNetworks, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *DefaultManager) updateClientNetworks(updateSerial uint64, networks map[string][]*route.Route) {
|
||||
// removing routes that do not exist as per the update from the Management service.
|
||||
m.stopObsoleteClients(networks)
|
||||
|
||||
for id, routes := range networks {
|
||||
clientNetworkWatcher, found := m.clientNetworks[id]
|
||||
@ -178,7 +219,7 @@ func (m *DefaultManager) updateClientNetworks(updateSerial uint64, networks map[
|
||||
}
|
||||
}
|
||||
|
||||
func (m *DefaultManager) classifiesRoutes(newRoutes []*route.Route) (map[string]*route.Route, map[string][]*route.Route) {
|
||||
func (m *DefaultManager) classifyRoutes(newRoutes []*route.Route) (map[string]*route.Route, map[string][]*route.Route) {
|
||||
newClientRoutesIDMap := make(map[string][]*route.Route)
|
||||
newServerRoutesMap := make(map[string]*route.Route)
|
||||
ownNetworkIDs := make(map[string]bool)
|
||||
@ -210,7 +251,7 @@ func (m *DefaultManager) classifiesRoutes(newRoutes []*route.Route) (map[string]
|
||||
}
|
||||
|
||||
func (m *DefaultManager) clientRoutes(initialRoutes []*route.Route) []*route.Route {
|
||||
_, crMap := m.classifiesRoutes(initialRoutes)
|
||||
_, crMap := m.classifyRoutes(initialRoutes)
|
||||
rs := make([]*route.Route, 0)
|
||||
for _, routes := range crMap {
|
||||
rs = append(rs, routes...)
|
||||
|
@ -428,11 +428,11 @@ func TestManagerUpdateRoutes(t *testing.T) {
|
||||
}
|
||||
|
||||
if len(testCase.inputInitRoutes) > 0 {
|
||||
err = routeManager.UpdateRoutes(testCase.inputSerial, testCase.inputRoutes)
|
||||
_, _, err = routeManager.UpdateRoutes(testCase.inputSerial, testCase.inputRoutes)
|
||||
require.NoError(t, err, "should update routes with init routes")
|
||||
}
|
||||
|
||||
err = routeManager.UpdateRoutes(testCase.inputSerial+uint64(len(testCase.inputInitRoutes)), testCase.inputRoutes)
|
||||
_, _, err = routeManager.UpdateRoutes(testCase.inputSerial+uint64(len(testCase.inputInitRoutes)), testCase.inputRoutes)
|
||||
require.NoError(t, err, "should update routes")
|
||||
|
||||
expectedWatchers := testCase.clientNetworkWatchersExpected
|
||||
|
@ -7,14 +7,17 @@ import (
|
||||
firewall "github.com/netbirdio/netbird/client/firewall/manager"
|
||||
"github.com/netbirdio/netbird/client/internal/listener"
|
||||
"github.com/netbirdio/netbird/client/internal/peer"
|
||||
"github.com/netbirdio/netbird/client/internal/routeselector"
|
||||
"github.com/netbirdio/netbird/iface"
|
||||
"github.com/netbirdio/netbird/route"
|
||||
)
|
||||
|
||||
// MockManager is the mock instance of a route manager
|
||||
type MockManager struct {
|
||||
UpdateRoutesFunc func(updateSerial uint64, newRoutes []*route.Route) error
|
||||
StopFunc func()
|
||||
UpdateRoutesFunc func(updateSerial uint64, newRoutes []*route.Route) (map[string]*route.Route, map[string][]*route.Route, error)
|
||||
TriggerSelectionFunc func(map[string][]*route.Route)
|
||||
GetRouteSelectorFunc func() *routeselector.RouteSelector
|
||||
StopFunc func()
|
||||
}
|
||||
|
||||
func (m *MockManager) Init() (peer.BeforeAddPeerHookFunc, peer.AfterRemovePeerHookFunc, error) {
|
||||
@ -27,11 +30,25 @@ func (m *MockManager) InitialRouteRange() []string {
|
||||
}
|
||||
|
||||
// UpdateRoutes mock implementation of UpdateRoutes from Manager interface
|
||||
func (m *MockManager) UpdateRoutes(updateSerial uint64, newRoutes []*route.Route) error {
|
||||
func (m *MockManager) UpdateRoutes(updateSerial uint64, newRoutes []*route.Route) (map[string]*route.Route, map[string][]*route.Route, error) {
|
||||
if m.UpdateRoutesFunc != nil {
|
||||
return m.UpdateRoutesFunc(updateSerial, newRoutes)
|
||||
}
|
||||
return fmt.Errorf("method UpdateRoutes is not implemented")
|
||||
return nil, nil, fmt.Errorf("method UpdateRoutes is not implemented")
|
||||
}
|
||||
|
||||
func (m *MockManager) TriggerSelection(networks map[string][]*route.Route) {
|
||||
if m.TriggerSelectionFunc != nil {
|
||||
m.TriggerSelectionFunc(networks)
|
||||
}
|
||||
}
|
||||
|
||||
// GetRouteSelector mock implementation of GetRouteSelector from Manager interface
|
||||
func (m *MockManager) GetRouteSelector() *routeselector.RouteSelector {
|
||||
if m.GetRouteSelectorFunc != nil {
|
||||
return m.GetRouteSelectorFunc()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Start mock implementation of Start from Manager interface
|
||||
|
@ -304,7 +304,10 @@ func removeUnreachableRoute(prefix netip.Prefix, tableID int) error {
|
||||
Dst: ipNet,
|
||||
}
|
||||
|
||||
if err := netlink.RouteDel(route); err != nil && !errors.Is(err, syscall.ESRCH) && !errors.Is(err, syscall.EAFNOSUPPORT) {
|
||||
if err := netlink.RouteDel(route); err != nil &&
|
||||
!errors.Is(err, syscall.ESRCH) &&
|
||||
!errors.Is(err, syscall.ENOENT) &&
|
||||
!errors.Is(err, syscall.EAFNOSUPPORT) {
|
||||
return fmt.Errorf("netlink remove unreachable route: %w", err)
|
||||
}
|
||||
|
||||
|
132
client/internal/routeselector/routeselector.go
Normal file
132
client/internal/routeselector/routeselector.go
Normal file
@ -0,0 +1,132 @@
|
||||
package routeselector
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"golang.org/x/exp/maps"
|
||||
|
||||
route "github.com/netbirdio/netbird/route"
|
||||
)
|
||||
|
||||
type RouteSelector struct {
|
||||
selectedRoutes map[string]struct{}
|
||||
selectAll bool
|
||||
}
|
||||
|
||||
func NewRouteSelector() *RouteSelector {
|
||||
return &RouteSelector{
|
||||
selectedRoutes: map[string]struct{}{},
|
||||
// default selects all routes
|
||||
selectAll: true,
|
||||
}
|
||||
}
|
||||
|
||||
// SelectRoutes updates the selected routes based on the provided route IDs.
|
||||
func (rs *RouteSelector) SelectRoutes(routes []string, appendRoute bool, allRoutes []string) error {
|
||||
if !appendRoute {
|
||||
rs.selectedRoutes = map[string]struct{}{}
|
||||
}
|
||||
|
||||
var multiErr *multierror.Error
|
||||
for _, route := range routes {
|
||||
if !slices.Contains(allRoutes, route) {
|
||||
multiErr = multierror.Append(multiErr, fmt.Errorf("route '%s' is not available", route))
|
||||
continue
|
||||
}
|
||||
|
||||
rs.selectedRoutes[route] = struct{}{}
|
||||
}
|
||||
rs.selectAll = false
|
||||
|
||||
if multiErr != nil {
|
||||
multiErr.ErrorFormat = formatError
|
||||
}
|
||||
|
||||
return multiErr.ErrorOrNil()
|
||||
}
|
||||
|
||||
// SelectAllRoutes sets the selector to select all routes.
|
||||
func (rs *RouteSelector) SelectAllRoutes() {
|
||||
rs.selectAll = true
|
||||
rs.selectedRoutes = map[string]struct{}{}
|
||||
}
|
||||
|
||||
// DeselectRoutes removes specific routes from the selection.
|
||||
// If the selector is in "select all" mode, it will transition to "select specific" mode.
|
||||
func (rs *RouteSelector) DeselectRoutes(routes []string, allRoutes []string) error {
|
||||
if rs.selectAll {
|
||||
rs.selectAll = false
|
||||
rs.selectedRoutes = map[string]struct{}{}
|
||||
for _, route := range allRoutes {
|
||||
rs.selectedRoutes[route] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
var multiErr *multierror.Error
|
||||
|
||||
for _, route := range routes {
|
||||
if !slices.Contains(allRoutes, route) {
|
||||
multiErr = multierror.Append(multiErr, fmt.Errorf("route '%s' is not available", route))
|
||||
continue
|
||||
}
|
||||
delete(rs.selectedRoutes, route)
|
||||
}
|
||||
|
||||
if multiErr != nil {
|
||||
multiErr.ErrorFormat = formatError
|
||||
}
|
||||
|
||||
return multiErr.ErrorOrNil()
|
||||
}
|
||||
|
||||
// DeselectAllRoutes deselects all routes, effectively disabling route selection.
|
||||
func (rs *RouteSelector) DeselectAllRoutes() {
|
||||
rs.selectAll = false
|
||||
rs.selectedRoutes = map[string]struct{}{}
|
||||
}
|
||||
|
||||
// IsSelected checks if a specific route is selected.
|
||||
func (rs *RouteSelector) IsSelected(routeID string) bool {
|
||||
if rs.selectAll {
|
||||
return true
|
||||
}
|
||||
_, selected := rs.selectedRoutes[routeID]
|
||||
return selected
|
||||
}
|
||||
|
||||
// FilterSelected removes unselected routes from the provided map.
|
||||
func (rs *RouteSelector) FilterSelected(routes map[string][]*route.Route) map[string][]*route.Route {
|
||||
if rs.selectAll {
|
||||
return maps.Clone(routes)
|
||||
}
|
||||
|
||||
filtered := map[string][]*route.Route{}
|
||||
for id, rt := range routes {
|
||||
netID := id
|
||||
if i := strings.LastIndex(id, "-"); i != -1 {
|
||||
netID = id[:i]
|
||||
}
|
||||
if rs.IsSelected(netID) {
|
||||
filtered[id] = rt
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
func formatError(es []error) string {
|
||||
if len(es) == 1 {
|
||||
return fmt.Sprintf("1 error occurred:\n\t* %s", es[0])
|
||||
}
|
||||
|
||||
points := make([]string, len(es))
|
||||
for i, err := range es {
|
||||
points[i] = fmt.Sprintf("* %s", err)
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"%d errors occurred:\n\t%s",
|
||||
len(es), strings.Join(points, "\n\t"))
|
||||
}
|
275
client/internal/routeselector/routeselector_test.go
Normal file
275
client/internal/routeselector/routeselector_test.go
Normal file
@ -0,0 +1,275 @@
|
||||
package routeselector_test
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/netbirdio/netbird/client/internal/routeselector"
|
||||
"github.com/netbirdio/netbird/route"
|
||||
)
|
||||
|
||||
func TestRouteSelector_SelectRoutes(t *testing.T) {
|
||||
allRoutes := []string{"route1", "route2", "route3"}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
initialSelected []string
|
||||
|
||||
selectRoutes []string
|
||||
append bool
|
||||
|
||||
wantSelected []string
|
||||
wantError bool
|
||||
}{
|
||||
{
|
||||
name: "Select specific routes, initial all selected",
|
||||
selectRoutes: []string{"route1", "route2"},
|
||||
wantSelected: []string{"route1", "route2"},
|
||||
},
|
||||
{
|
||||
name: "Select specific routes, initial all deselected",
|
||||
initialSelected: []string{},
|
||||
selectRoutes: []string{"route1", "route2"},
|
||||
wantSelected: []string{"route1", "route2"},
|
||||
},
|
||||
{
|
||||
name: "Select specific routes with initial selection",
|
||||
initialSelected: []string{"route1"},
|
||||
selectRoutes: []string{"route2", "route3"},
|
||||
wantSelected: []string{"route2", "route3"},
|
||||
},
|
||||
{
|
||||
name: "Select non-existing route",
|
||||
selectRoutes: []string{"route1", "route4"},
|
||||
wantSelected: []string{"route1"},
|
||||
wantError: true,
|
||||
},
|
||||
{
|
||||
name: "Append route with initial selection",
|
||||
initialSelected: []string{"route1"},
|
||||
selectRoutes: []string{"route2"},
|
||||
append: true,
|
||||
wantSelected: []string{"route1", "route2"},
|
||||
},
|
||||
{
|
||||
name: "Append route without initial selection",
|
||||
selectRoutes: []string{"route2"},
|
||||
append: true,
|
||||
wantSelected: []string{"route2"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
rs := routeselector.NewRouteSelector()
|
||||
|
||||
if tt.initialSelected != nil {
|
||||
err := rs.SelectRoutes(tt.initialSelected, false, allRoutes)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
err := rs.SelectRoutes(tt.selectRoutes, tt.append, allRoutes)
|
||||
if tt.wantError {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
for _, id := range allRoutes {
|
||||
assert.Equal(t, rs.IsSelected(id), slices.Contains(tt.wantSelected, id))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouteSelector_SelectAllRoutes(t *testing.T) {
|
||||
allRoutes := []string{"route1", "route2", "route3"}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
initialSelected []string
|
||||
|
||||
wantSelected []string
|
||||
}{
|
||||
{
|
||||
name: "Initial all selected",
|
||||
wantSelected: []string{"route1", "route2", "route3"},
|
||||
},
|
||||
{
|
||||
name: "Initial all deselected",
|
||||
initialSelected: []string{},
|
||||
wantSelected: []string{"route1", "route2", "route3"},
|
||||
},
|
||||
{
|
||||
name: "Initial some selected",
|
||||
initialSelected: []string{"route1"},
|
||||
wantSelected: []string{"route1", "route2", "route3"},
|
||||
},
|
||||
{
|
||||
name: "Initial all selected",
|
||||
initialSelected: []string{"route1", "route2", "route3"},
|
||||
wantSelected: []string{"route1", "route2", "route3"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
rs := routeselector.NewRouteSelector()
|
||||
|
||||
if tt.initialSelected != nil {
|
||||
err := rs.SelectRoutes(tt.initialSelected, false, allRoutes)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
rs.SelectAllRoutes()
|
||||
|
||||
for _, id := range allRoutes {
|
||||
assert.Equal(t, rs.IsSelected(id), slices.Contains(tt.wantSelected, id))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouteSelector_DeselectRoutes(t *testing.T) {
|
||||
allRoutes := []string{"route1", "route2", "route3"}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
initialSelected []string
|
||||
|
||||
deselectRoutes []string
|
||||
|
||||
wantSelected []string
|
||||
wantError bool
|
||||
}{
|
||||
{
|
||||
name: "Deselect specific routes, initial all selected",
|
||||
deselectRoutes: []string{"route1", "route2"},
|
||||
wantSelected: []string{"route3"},
|
||||
},
|
||||
{
|
||||
name: "Deselect specific routes, initial all deselected",
|
||||
initialSelected: []string{},
|
||||
deselectRoutes: []string{"route1", "route2"},
|
||||
wantSelected: []string{},
|
||||
},
|
||||
{
|
||||
name: "Deselect specific routes with initial selection",
|
||||
initialSelected: []string{"route1", "route2"},
|
||||
deselectRoutes: []string{"route1", "route3"},
|
||||
wantSelected: []string{"route2"},
|
||||
},
|
||||
{
|
||||
name: "Deselect non-existing route",
|
||||
initialSelected: []string{"route1", "route2"},
|
||||
deselectRoutes: []string{"route1", "route4"},
|
||||
wantSelected: []string{"route2"},
|
||||
wantError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
rs := routeselector.NewRouteSelector()
|
||||
|
||||
if tt.initialSelected != nil {
|
||||
err := rs.SelectRoutes(tt.initialSelected, false, allRoutes)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
err := rs.DeselectRoutes(tt.deselectRoutes, allRoutes)
|
||||
if tt.wantError {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
for _, id := range allRoutes {
|
||||
assert.Equal(t, rs.IsSelected(id), slices.Contains(tt.wantSelected, id))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouteSelector_DeselectAll(t *testing.T) {
|
||||
allRoutes := []string{"route1", "route2", "route3"}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
initialSelected []string
|
||||
|
||||
wantSelected []string
|
||||
}{
|
||||
{
|
||||
name: "Initial all selected",
|
||||
wantSelected: []string{},
|
||||
},
|
||||
{
|
||||
name: "Initial all deselected",
|
||||
initialSelected: []string{},
|
||||
wantSelected: []string{},
|
||||
},
|
||||
{
|
||||
name: "Initial some selected",
|
||||
initialSelected: []string{"route1", "route2"},
|
||||
wantSelected: []string{},
|
||||
},
|
||||
{
|
||||
name: "Initial all selected",
|
||||
initialSelected: []string{"route1", "route2", "route3"},
|
||||
wantSelected: []string{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
rs := routeselector.NewRouteSelector()
|
||||
|
||||
if tt.initialSelected != nil {
|
||||
err := rs.SelectRoutes(tt.initialSelected, false, allRoutes)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
rs.DeselectAllRoutes()
|
||||
|
||||
for _, id := range allRoutes {
|
||||
assert.Equal(t, rs.IsSelected(id), slices.Contains(tt.wantSelected, id))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouteSelector_IsSelected(t *testing.T) {
|
||||
rs := routeselector.NewRouteSelector()
|
||||
|
||||
err := rs.SelectRoutes([]string{"route1", "route2"}, false, []string{"route1", "route2", "route3"})
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.True(t, rs.IsSelected("route1"))
|
||||
assert.True(t, rs.IsSelected("route2"))
|
||||
assert.False(t, rs.IsSelected("route3"))
|
||||
assert.False(t, rs.IsSelected("route4"))
|
||||
}
|
||||
|
||||
func TestRouteSelector_FilterSelected(t *testing.T) {
|
||||
rs := routeselector.NewRouteSelector()
|
||||
|
||||
err := rs.SelectRoutes([]string{"route1", "route2"}, false, []string{"route1", "route2", "route3"})
|
||||
require.NoError(t, err)
|
||||
|
||||
routes := map[string][]*route.Route{
|
||||
"route1-10.0.0.0/8": {},
|
||||
"route2-192.168.0.0/16": {},
|
||||
"route3-172.16.0.0/12": {},
|
||||
}
|
||||
|
||||
filtered := rs.FilterSelected(routes)
|
||||
|
||||
assert.Equal(t, map[string][]*route.Route{
|
||||
"route1-10.0.0.0/8": {},
|
||||
"route2-192.168.0.0/16": {},
|
||||
}, filtered)
|
||||
}
|
@ -1,17 +1,17 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.26.0
|
||||
// protoc v4.24.3
|
||||
// protoc v3.12.4
|
||||
// source: daemon.proto
|
||||
|
||||
package proto
|
||||
|
||||
import (
|
||||
_ "github.com/golang/protobuf/protoc-gen-go/descriptor"
|
||||
duration "github.com/golang/protobuf/ptypes/duration"
|
||||
timestamp "github.com/golang/protobuf/ptypes/timestamp"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
_ "google.golang.org/protobuf/types/descriptorpb"
|
||||
durationpb "google.golang.org/protobuf/types/known/durationpb"
|
||||
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
@ -766,23 +766,23 @@ type PeerState struct {
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
IP string `protobuf:"bytes,1,opt,name=IP,proto3" json:"IP,omitempty"`
|
||||
PubKey string `protobuf:"bytes,2,opt,name=pubKey,proto3" json:"pubKey,omitempty"`
|
||||
ConnStatus string `protobuf:"bytes,3,opt,name=connStatus,proto3" json:"connStatus,omitempty"`
|
||||
ConnStatusUpdate *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=connStatusUpdate,proto3" json:"connStatusUpdate,omitempty"`
|
||||
Relayed bool `protobuf:"varint,5,opt,name=relayed,proto3" json:"relayed,omitempty"`
|
||||
Direct bool `protobuf:"varint,6,opt,name=direct,proto3" json:"direct,omitempty"`
|
||||
LocalIceCandidateType string `protobuf:"bytes,7,opt,name=localIceCandidateType,proto3" json:"localIceCandidateType,omitempty"`
|
||||
RemoteIceCandidateType string `protobuf:"bytes,8,opt,name=remoteIceCandidateType,proto3" json:"remoteIceCandidateType,omitempty"`
|
||||
Fqdn string `protobuf:"bytes,9,opt,name=fqdn,proto3" json:"fqdn,omitempty"`
|
||||
LocalIceCandidateEndpoint string `protobuf:"bytes,10,opt,name=localIceCandidateEndpoint,proto3" json:"localIceCandidateEndpoint,omitempty"`
|
||||
RemoteIceCandidateEndpoint string `protobuf:"bytes,11,opt,name=remoteIceCandidateEndpoint,proto3" json:"remoteIceCandidateEndpoint,omitempty"`
|
||||
LastWireguardHandshake *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=lastWireguardHandshake,proto3" json:"lastWireguardHandshake,omitempty"`
|
||||
BytesRx int64 `protobuf:"varint,13,opt,name=bytesRx,proto3" json:"bytesRx,omitempty"`
|
||||
BytesTx int64 `protobuf:"varint,14,opt,name=bytesTx,proto3" json:"bytesTx,omitempty"`
|
||||
RosenpassEnabled bool `protobuf:"varint,15,opt,name=rosenpassEnabled,proto3" json:"rosenpassEnabled,omitempty"`
|
||||
Routes []string `protobuf:"bytes,16,rep,name=routes,proto3" json:"routes,omitempty"`
|
||||
Latency *durationpb.Duration `protobuf:"bytes,17,opt,name=latency,proto3" json:"latency,omitempty"`
|
||||
IP string `protobuf:"bytes,1,opt,name=IP,proto3" json:"IP,omitempty"`
|
||||
PubKey string `protobuf:"bytes,2,opt,name=pubKey,proto3" json:"pubKey,omitempty"`
|
||||
ConnStatus string `protobuf:"bytes,3,opt,name=connStatus,proto3" json:"connStatus,omitempty"`
|
||||
ConnStatusUpdate *timestamp.Timestamp `protobuf:"bytes,4,opt,name=connStatusUpdate,proto3" json:"connStatusUpdate,omitempty"`
|
||||
Relayed bool `protobuf:"varint,5,opt,name=relayed,proto3" json:"relayed,omitempty"`
|
||||
Direct bool `protobuf:"varint,6,opt,name=direct,proto3" json:"direct,omitempty"`
|
||||
LocalIceCandidateType string `protobuf:"bytes,7,opt,name=localIceCandidateType,proto3" json:"localIceCandidateType,omitempty"`
|
||||
RemoteIceCandidateType string `protobuf:"bytes,8,opt,name=remoteIceCandidateType,proto3" json:"remoteIceCandidateType,omitempty"`
|
||||
Fqdn string `protobuf:"bytes,9,opt,name=fqdn,proto3" json:"fqdn,omitempty"`
|
||||
LocalIceCandidateEndpoint string `protobuf:"bytes,10,opt,name=localIceCandidateEndpoint,proto3" json:"localIceCandidateEndpoint,omitempty"`
|
||||
RemoteIceCandidateEndpoint string `protobuf:"bytes,11,opt,name=remoteIceCandidateEndpoint,proto3" json:"remoteIceCandidateEndpoint,omitempty"`
|
||||
LastWireguardHandshake *timestamp.Timestamp `protobuf:"bytes,12,opt,name=lastWireguardHandshake,proto3" json:"lastWireguardHandshake,omitempty"`
|
||||
BytesRx int64 `protobuf:"varint,13,opt,name=bytesRx,proto3" json:"bytesRx,omitempty"`
|
||||
BytesTx int64 `protobuf:"varint,14,opt,name=bytesTx,proto3" json:"bytesTx,omitempty"`
|
||||
RosenpassEnabled bool `protobuf:"varint,15,opt,name=rosenpassEnabled,proto3" json:"rosenpassEnabled,omitempty"`
|
||||
Routes []string `protobuf:"bytes,16,rep,name=routes,proto3" json:"routes,omitempty"`
|
||||
Latency *duration.Duration `protobuf:"bytes,17,opt,name=latency,proto3" json:"latency,omitempty"`
|
||||
}
|
||||
|
||||
func (x *PeerState) Reset() {
|
||||
@ -838,7 +838,7 @@ func (x *PeerState) GetConnStatus() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *PeerState) GetConnStatusUpdate() *timestamppb.Timestamp {
|
||||
func (x *PeerState) GetConnStatusUpdate() *timestamp.Timestamp {
|
||||
if x != nil {
|
||||
return x.ConnStatusUpdate
|
||||
}
|
||||
@ -894,7 +894,7 @@ func (x *PeerState) GetRemoteIceCandidateEndpoint() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *PeerState) GetLastWireguardHandshake() *timestamppb.Timestamp {
|
||||
func (x *PeerState) GetLastWireguardHandshake() *timestamp.Timestamp {
|
||||
if x != nil {
|
||||
return x.LastWireguardHandshake
|
||||
}
|
||||
@ -929,7 +929,7 @@ func (x *PeerState) GetRoutes() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *PeerState) GetLatency() *durationpb.Duration {
|
||||
func (x *PeerState) GetLatency() *duration.Duration {
|
||||
if x != nil {
|
||||
return x.Latency
|
||||
}
|
||||
@ -1383,6 +1383,255 @@ func (x *FullStatus) GetDnsServers() []*NSGroupState {
|
||||
return nil
|
||||
}
|
||||
|
||||
type ListRoutesRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
}
|
||||
|
||||
func (x *ListRoutesRequest) Reset() {
|
||||
*x = ListRoutesRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_daemon_proto_msgTypes[19]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ListRoutesRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ListRoutesRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ListRoutesRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_daemon_proto_msgTypes[19]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ListRoutesRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ListRoutesRequest) Descriptor() ([]byte, []int) {
|
||||
return file_daemon_proto_rawDescGZIP(), []int{19}
|
||||
}
|
||||
|
||||
type ListRoutesResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Routes []*Route `protobuf:"bytes,1,rep,name=routes,proto3" json:"routes,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ListRoutesResponse) Reset() {
|
||||
*x = ListRoutesResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_daemon_proto_msgTypes[20]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ListRoutesResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ListRoutesResponse) ProtoMessage() {}
|
||||
|
||||
func (x *ListRoutesResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_daemon_proto_msgTypes[20]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ListRoutesResponse.ProtoReflect.Descriptor instead.
|
||||
func (*ListRoutesResponse) Descriptor() ([]byte, []int) {
|
||||
return file_daemon_proto_rawDescGZIP(), []int{20}
|
||||
}
|
||||
|
||||
func (x *ListRoutesResponse) GetRoutes() []*Route {
|
||||
if x != nil {
|
||||
return x.Routes
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type SelectRoutesRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
RouteIDs []string `protobuf:"bytes,1,rep,name=routeIDs,proto3" json:"routeIDs,omitempty"`
|
||||
Append bool `protobuf:"varint,2,opt,name=append,proto3" json:"append,omitempty"`
|
||||
All bool `protobuf:"varint,3,opt,name=all,proto3" json:"all,omitempty"`
|
||||
}
|
||||
|
||||
func (x *SelectRoutesRequest) Reset() {
|
||||
*x = SelectRoutesRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_daemon_proto_msgTypes[21]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *SelectRoutesRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SelectRoutesRequest) ProtoMessage() {}
|
||||
|
||||
func (x *SelectRoutesRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_daemon_proto_msgTypes[21]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SelectRoutesRequest.ProtoReflect.Descriptor instead.
|
||||
func (*SelectRoutesRequest) Descriptor() ([]byte, []int) {
|
||||
return file_daemon_proto_rawDescGZIP(), []int{21}
|
||||
}
|
||||
|
||||
func (x *SelectRoutesRequest) GetRouteIDs() []string {
|
||||
if x != nil {
|
||||
return x.RouteIDs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *SelectRoutesRequest) GetAppend() bool {
|
||||
if x != nil {
|
||||
return x.Append
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *SelectRoutesRequest) GetAll() bool {
|
||||
if x != nil {
|
||||
return x.All
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type SelectRoutesResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
}
|
||||
|
||||
func (x *SelectRoutesResponse) Reset() {
|
||||
*x = SelectRoutesResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_daemon_proto_msgTypes[22]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *SelectRoutesResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SelectRoutesResponse) ProtoMessage() {}
|
||||
|
||||
func (x *SelectRoutesResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_daemon_proto_msgTypes[22]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SelectRoutesResponse.ProtoReflect.Descriptor instead.
|
||||
func (*SelectRoutesResponse) Descriptor() ([]byte, []int) {
|
||||
return file_daemon_proto_rawDescGZIP(), []int{22}
|
||||
}
|
||||
|
||||
type Route struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"`
|
||||
Network string `protobuf:"bytes,2,opt,name=network,proto3" json:"network,omitempty"`
|
||||
Selected bool `protobuf:"varint,3,opt,name=selected,proto3" json:"selected,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Route) Reset() {
|
||||
*x = Route{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_daemon_proto_msgTypes[23]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Route) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Route) ProtoMessage() {}
|
||||
|
||||
func (x *Route) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_daemon_proto_msgTypes[23]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Route.ProtoReflect.Descriptor instead.
|
||||
func (*Route) Descriptor() ([]byte, []int) {
|
||||
return file_daemon_proto_rawDescGZIP(), []int{23}
|
||||
}
|
||||
|
||||
func (x *Route) GetID() string {
|
||||
if x != nil {
|
||||
return x.ID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Route) GetNetwork() string {
|
||||
if x != nil {
|
||||
return x.Network
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Route) GetSelected() bool {
|
||||
if x != nil {
|
||||
return x.Selected
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var File_daemon_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_daemon_proto_rawDesc = []byte{
|
||||
@ -1601,32 +1850,64 @@ var file_daemon_proto_rawDesc = []byte{
|
||||
0x72, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x12, 0x35, 0x0a, 0x0b, 0x64, 0x6e, 0x73, 0x5f, 0x73, 0x65,
|
||||
0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x64, 0x61,
|
||||
0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4e, 0x53, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x74, 0x61, 0x74,
|
||||
0x65, 0x52, 0x0a, 0x64, 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x32, 0xf7, 0x02,
|
||||
0x0a, 0x0d, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12,
|
||||
0x36, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x14, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f,
|
||||
0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15,
|
||||
0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0c, 0x57, 0x61, 0x69, 0x74, 0x53,
|
||||
0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e,
|
||||
0x2e, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x57, 0x61,
|
||||
0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x22, 0x00, 0x12, 0x2d, 0x0a, 0x02, 0x55, 0x70, 0x12, 0x11, 0x2e, 0x64, 0x61, 0x65,
|
||||
0x6d, 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e,
|
||||
0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x22, 0x00, 0x12, 0x39, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x15, 0x2e,
|
||||
0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74,
|
||||
0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33,
|
||||
0x0a, 0x04, 0x44, 0x6f, 0x77, 0x6e, 0x12, 0x13, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e,
|
||||
0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x64, 0x61,
|
||||
0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||
0x12, 0x18, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e,
|
||||
0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x64, 0x61, 0x65,
|
||||
0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x65, 0x52, 0x0a, 0x64, 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x22, 0x13, 0x0a,
|
||||
0x11, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||
0x73, 0x74, 0x22, 0x3b, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73,
|
||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x74,
|
||||
0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f,
|
||||
0x6e, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x22,
|
||||
0x5b, 0x0a, 0x13, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x49,
|
||||
0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x49,
|
||||
0x44, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01,
|
||||
0x28, 0x08, 0x52, 0x06, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6c,
|
||||
0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x22, 0x16, 0x0a, 0x14,
|
||||
0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70,
|
||||
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4d, 0x0a, 0x05, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x0e, 0x0a,
|
||||
0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x18, 0x0a,
|
||||
0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
|
||||
0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63,
|
||||
0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63,
|
||||
0x74, 0x65, 0x64, 0x32, 0xda, 0x04, 0x0a, 0x0d, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x53, 0x65,
|
||||
0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x36, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x14,
|
||||
0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f,
|
||||
0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a,
|
||||
0x0c, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1b, 0x2e,
|
||||
0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f,
|
||||
0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x64, 0x61, 0x65,
|
||||
0x6d, 0x6f, 0x6e, 0x2e, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e,
|
||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x2d, 0x0a, 0x02, 0x55, 0x70,
|
||||
0x12, 0x11, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x39, 0x0a, 0x06, 0x53, 0x74, 0x61,
|
||||
0x74, 0x75, 0x73, 0x12, 0x15, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61,
|
||||
0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x64, 0x61, 0x65,
|
||||
0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x04, 0x44, 0x6f, 0x77, 0x6e, 0x12, 0x13, 0x2e, 0x64,
|
||||
0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x1a, 0x14, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x09, 0x47, 0x65, 0x74,
|
||||
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e,
|
||||
0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x1a, 0x19, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e,
|
||||
0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a,
|
||||
0x0a, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x64, 0x61,
|
||||
0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e,
|
||||
0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f,
|
||||
0x75, 0x74, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65,
|
||||
0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x1a, 0x1c, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63,
|
||||
0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
|
||||
0x00, 0x12, 0x4d, 0x0a, 0x0e, 0x44, 0x65, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75,
|
||||
0x74, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6c,
|
||||
0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x1a, 0x1c, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74,
|
||||
0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00,
|
||||
0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@ -1641,58 +1922,70 @@ func file_daemon_proto_rawDescGZIP() []byte {
|
||||
return file_daemon_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_daemon_proto_msgTypes = make([]protoimpl.MessageInfo, 19)
|
||||
var file_daemon_proto_msgTypes = make([]protoimpl.MessageInfo, 24)
|
||||
var file_daemon_proto_goTypes = []interface{}{
|
||||
(*LoginRequest)(nil), // 0: daemon.LoginRequest
|
||||
(*LoginResponse)(nil), // 1: daemon.LoginResponse
|
||||
(*WaitSSOLoginRequest)(nil), // 2: daemon.WaitSSOLoginRequest
|
||||
(*WaitSSOLoginResponse)(nil), // 3: daemon.WaitSSOLoginResponse
|
||||
(*UpRequest)(nil), // 4: daemon.UpRequest
|
||||
(*UpResponse)(nil), // 5: daemon.UpResponse
|
||||
(*StatusRequest)(nil), // 6: daemon.StatusRequest
|
||||
(*StatusResponse)(nil), // 7: daemon.StatusResponse
|
||||
(*DownRequest)(nil), // 8: daemon.DownRequest
|
||||
(*DownResponse)(nil), // 9: daemon.DownResponse
|
||||
(*GetConfigRequest)(nil), // 10: daemon.GetConfigRequest
|
||||
(*GetConfigResponse)(nil), // 11: daemon.GetConfigResponse
|
||||
(*PeerState)(nil), // 12: daemon.PeerState
|
||||
(*LocalPeerState)(nil), // 13: daemon.LocalPeerState
|
||||
(*SignalState)(nil), // 14: daemon.SignalState
|
||||
(*ManagementState)(nil), // 15: daemon.ManagementState
|
||||
(*RelayState)(nil), // 16: daemon.RelayState
|
||||
(*NSGroupState)(nil), // 17: daemon.NSGroupState
|
||||
(*FullStatus)(nil), // 18: daemon.FullStatus
|
||||
(*timestamppb.Timestamp)(nil), // 19: google.protobuf.Timestamp
|
||||
(*durationpb.Duration)(nil), // 20: google.protobuf.Duration
|
||||
(*LoginRequest)(nil), // 0: daemon.LoginRequest
|
||||
(*LoginResponse)(nil), // 1: daemon.LoginResponse
|
||||
(*WaitSSOLoginRequest)(nil), // 2: daemon.WaitSSOLoginRequest
|
||||
(*WaitSSOLoginResponse)(nil), // 3: daemon.WaitSSOLoginResponse
|
||||
(*UpRequest)(nil), // 4: daemon.UpRequest
|
||||
(*UpResponse)(nil), // 5: daemon.UpResponse
|
||||
(*StatusRequest)(nil), // 6: daemon.StatusRequest
|
||||
(*StatusResponse)(nil), // 7: daemon.StatusResponse
|
||||
(*DownRequest)(nil), // 8: daemon.DownRequest
|
||||
(*DownResponse)(nil), // 9: daemon.DownResponse
|
||||
(*GetConfigRequest)(nil), // 10: daemon.GetConfigRequest
|
||||
(*GetConfigResponse)(nil), // 11: daemon.GetConfigResponse
|
||||
(*PeerState)(nil), // 12: daemon.PeerState
|
||||
(*LocalPeerState)(nil), // 13: daemon.LocalPeerState
|
||||
(*SignalState)(nil), // 14: daemon.SignalState
|
||||
(*ManagementState)(nil), // 15: daemon.ManagementState
|
||||
(*RelayState)(nil), // 16: daemon.RelayState
|
||||
(*NSGroupState)(nil), // 17: daemon.NSGroupState
|
||||
(*FullStatus)(nil), // 18: daemon.FullStatus
|
||||
(*ListRoutesRequest)(nil), // 19: daemon.ListRoutesRequest
|
||||
(*ListRoutesResponse)(nil), // 20: daemon.ListRoutesResponse
|
||||
(*SelectRoutesRequest)(nil), // 21: daemon.SelectRoutesRequest
|
||||
(*SelectRoutesResponse)(nil), // 22: daemon.SelectRoutesResponse
|
||||
(*Route)(nil), // 23: daemon.Route
|
||||
(*timestamp.Timestamp)(nil), // 24: google.protobuf.Timestamp
|
||||
(*duration.Duration)(nil), // 25: google.protobuf.Duration
|
||||
}
|
||||
var file_daemon_proto_depIdxs = []int32{
|
||||
18, // 0: daemon.StatusResponse.fullStatus:type_name -> daemon.FullStatus
|
||||
19, // 1: daemon.PeerState.connStatusUpdate:type_name -> google.protobuf.Timestamp
|
||||
19, // 2: daemon.PeerState.lastWireguardHandshake:type_name -> google.protobuf.Timestamp
|
||||
20, // 3: daemon.PeerState.latency:type_name -> google.protobuf.Duration
|
||||
24, // 1: daemon.PeerState.connStatusUpdate:type_name -> google.protobuf.Timestamp
|
||||
24, // 2: daemon.PeerState.lastWireguardHandshake:type_name -> google.protobuf.Timestamp
|
||||
25, // 3: daemon.PeerState.latency:type_name -> google.protobuf.Duration
|
||||
15, // 4: daemon.FullStatus.managementState:type_name -> daemon.ManagementState
|
||||
14, // 5: daemon.FullStatus.signalState:type_name -> daemon.SignalState
|
||||
13, // 6: daemon.FullStatus.localPeerState:type_name -> daemon.LocalPeerState
|
||||
12, // 7: daemon.FullStatus.peers:type_name -> daemon.PeerState
|
||||
16, // 8: daemon.FullStatus.relays:type_name -> daemon.RelayState
|
||||
17, // 9: daemon.FullStatus.dns_servers:type_name -> daemon.NSGroupState
|
||||
0, // 10: daemon.DaemonService.Login:input_type -> daemon.LoginRequest
|
||||
2, // 11: daemon.DaemonService.WaitSSOLogin:input_type -> daemon.WaitSSOLoginRequest
|
||||
4, // 12: daemon.DaemonService.Up:input_type -> daemon.UpRequest
|
||||
6, // 13: daemon.DaemonService.Status:input_type -> daemon.StatusRequest
|
||||
8, // 14: daemon.DaemonService.Down:input_type -> daemon.DownRequest
|
||||
10, // 15: daemon.DaemonService.GetConfig:input_type -> daemon.GetConfigRequest
|
||||
1, // 16: daemon.DaemonService.Login:output_type -> daemon.LoginResponse
|
||||
3, // 17: daemon.DaemonService.WaitSSOLogin:output_type -> daemon.WaitSSOLoginResponse
|
||||
5, // 18: daemon.DaemonService.Up:output_type -> daemon.UpResponse
|
||||
7, // 19: daemon.DaemonService.Status:output_type -> daemon.StatusResponse
|
||||
9, // 20: daemon.DaemonService.Down:output_type -> daemon.DownResponse
|
||||
11, // 21: daemon.DaemonService.GetConfig:output_type -> daemon.GetConfigResponse
|
||||
16, // [16:22] is the sub-list for method output_type
|
||||
10, // [10:16] is the sub-list for method input_type
|
||||
10, // [10:10] is the sub-list for extension type_name
|
||||
10, // [10:10] is the sub-list for extension extendee
|
||||
0, // [0:10] is the sub-list for field type_name
|
||||
23, // 10: daemon.ListRoutesResponse.routes:type_name -> daemon.Route
|
||||
0, // 11: daemon.DaemonService.Login:input_type -> daemon.LoginRequest
|
||||
2, // 12: daemon.DaemonService.WaitSSOLogin:input_type -> daemon.WaitSSOLoginRequest
|
||||
4, // 13: daemon.DaemonService.Up:input_type -> daemon.UpRequest
|
||||
6, // 14: daemon.DaemonService.Status:input_type -> daemon.StatusRequest
|
||||
8, // 15: daemon.DaemonService.Down:input_type -> daemon.DownRequest
|
||||
10, // 16: daemon.DaemonService.GetConfig:input_type -> daemon.GetConfigRequest
|
||||
19, // 17: daemon.DaemonService.ListRoutes:input_type -> daemon.ListRoutesRequest
|
||||
21, // 18: daemon.DaemonService.SelectRoutes:input_type -> daemon.SelectRoutesRequest
|
||||
21, // 19: daemon.DaemonService.DeselectRoutes:input_type -> daemon.SelectRoutesRequest
|
||||
1, // 20: daemon.DaemonService.Login:output_type -> daemon.LoginResponse
|
||||
3, // 21: daemon.DaemonService.WaitSSOLogin:output_type -> daemon.WaitSSOLoginResponse
|
||||
5, // 22: daemon.DaemonService.Up:output_type -> daemon.UpResponse
|
||||
7, // 23: daemon.DaemonService.Status:output_type -> daemon.StatusResponse
|
||||
9, // 24: daemon.DaemonService.Down:output_type -> daemon.DownResponse
|
||||
11, // 25: daemon.DaemonService.GetConfig:output_type -> daemon.GetConfigResponse
|
||||
20, // 26: daemon.DaemonService.ListRoutes:output_type -> daemon.ListRoutesResponse
|
||||
22, // 27: daemon.DaemonService.SelectRoutes:output_type -> daemon.SelectRoutesResponse
|
||||
22, // 28: daemon.DaemonService.DeselectRoutes:output_type -> daemon.SelectRoutesResponse
|
||||
20, // [20:29] is the sub-list for method output_type
|
||||
11, // [11:20] is the sub-list for method input_type
|
||||
11, // [11:11] is the sub-list for extension type_name
|
||||
11, // [11:11] is the sub-list for extension extendee
|
||||
0, // [0:11] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_daemon_proto_init() }
|
||||
@ -1929,6 +2222,66 @@ func file_daemon_proto_init() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_daemon_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ListRoutesRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_daemon_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ListRoutesResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_daemon_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*SelectRoutesRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_daemon_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*SelectRoutesResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_daemon_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Route); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
file_daemon_proto_msgTypes[0].OneofWrappers = []interface{}{}
|
||||
type x struct{}
|
||||
@ -1937,7 +2290,7 @@ func file_daemon_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_daemon_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 19,
|
||||
NumMessages: 24,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
@ -27,6 +27,15 @@ service DaemonService {
|
||||
|
||||
// GetConfig of the daemon.
|
||||
rpc GetConfig(GetConfigRequest) returns (GetConfigResponse) {}
|
||||
|
||||
// List available network routes
|
||||
rpc ListRoutes(ListRoutesRequest) returns (ListRoutesResponse) {}
|
||||
|
||||
// Select specific routes
|
||||
rpc SelectRoutes(SelectRoutesRequest) returns (SelectRoutesResponse) {}
|
||||
|
||||
// Deselect specific routes
|
||||
rpc DeselectRoutes(SelectRoutesRequest) returns (SelectRoutesResponse) {}
|
||||
};
|
||||
|
||||
message LoginRequest {
|
||||
@ -195,4 +204,26 @@ message FullStatus {
|
||||
repeated PeerState peers = 4;
|
||||
repeated RelayState relays = 5;
|
||||
repeated NSGroupState dns_servers = 6;
|
||||
}
|
||||
|
||||
message ListRoutesRequest {
|
||||
}
|
||||
|
||||
message ListRoutesResponse {
|
||||
repeated Route routes = 1;
|
||||
}
|
||||
|
||||
message SelectRoutesRequest {
|
||||
repeated string routeIDs = 1;
|
||||
bool append = 2;
|
||||
bool all = 3;
|
||||
}
|
||||
|
||||
message SelectRoutesResponse {
|
||||
}
|
||||
|
||||
message Route {
|
||||
string ID = 1;
|
||||
string network = 2;
|
||||
bool selected = 3;
|
||||
}
|
@ -31,6 +31,12 @@ type DaemonServiceClient interface {
|
||||
Down(ctx context.Context, in *DownRequest, opts ...grpc.CallOption) (*DownResponse, error)
|
||||
// GetConfig of the daemon.
|
||||
GetConfig(ctx context.Context, in *GetConfigRequest, opts ...grpc.CallOption) (*GetConfigResponse, error)
|
||||
// List available network routes
|
||||
ListRoutes(ctx context.Context, in *ListRoutesRequest, opts ...grpc.CallOption) (*ListRoutesResponse, error)
|
||||
// Select specific routes
|
||||
SelectRoutes(ctx context.Context, in *SelectRoutesRequest, opts ...grpc.CallOption) (*SelectRoutesResponse, error)
|
||||
// Deselect specific routes
|
||||
DeselectRoutes(ctx context.Context, in *SelectRoutesRequest, opts ...grpc.CallOption) (*SelectRoutesResponse, error)
|
||||
}
|
||||
|
||||
type daemonServiceClient struct {
|
||||
@ -95,6 +101,33 @@ func (c *daemonServiceClient) GetConfig(ctx context.Context, in *GetConfigReques
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *daemonServiceClient) ListRoutes(ctx context.Context, in *ListRoutesRequest, opts ...grpc.CallOption) (*ListRoutesResponse, error) {
|
||||
out := new(ListRoutesResponse)
|
||||
err := c.cc.Invoke(ctx, "/daemon.DaemonService/ListRoutes", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *daemonServiceClient) SelectRoutes(ctx context.Context, in *SelectRoutesRequest, opts ...grpc.CallOption) (*SelectRoutesResponse, error) {
|
||||
out := new(SelectRoutesResponse)
|
||||
err := c.cc.Invoke(ctx, "/daemon.DaemonService/SelectRoutes", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *daemonServiceClient) DeselectRoutes(ctx context.Context, in *SelectRoutesRequest, opts ...grpc.CallOption) (*SelectRoutesResponse, error) {
|
||||
out := new(SelectRoutesResponse)
|
||||
err := c.cc.Invoke(ctx, "/daemon.DaemonService/DeselectRoutes", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// DaemonServiceServer is the server API for DaemonService service.
|
||||
// All implementations must embed UnimplementedDaemonServiceServer
|
||||
// for forward compatibility
|
||||
@ -112,6 +145,12 @@ type DaemonServiceServer interface {
|
||||
Down(context.Context, *DownRequest) (*DownResponse, error)
|
||||
// GetConfig of the daemon.
|
||||
GetConfig(context.Context, *GetConfigRequest) (*GetConfigResponse, error)
|
||||
// List available network routes
|
||||
ListRoutes(context.Context, *ListRoutesRequest) (*ListRoutesResponse, error)
|
||||
// Select specific routes
|
||||
SelectRoutes(context.Context, *SelectRoutesRequest) (*SelectRoutesResponse, error)
|
||||
// Deselect specific routes
|
||||
DeselectRoutes(context.Context, *SelectRoutesRequest) (*SelectRoutesResponse, error)
|
||||
mustEmbedUnimplementedDaemonServiceServer()
|
||||
}
|
||||
|
||||
@ -137,6 +176,15 @@ func (UnimplementedDaemonServiceServer) Down(context.Context, *DownRequest) (*Do
|
||||
func (UnimplementedDaemonServiceServer) GetConfig(context.Context, *GetConfigRequest) (*GetConfigResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetConfig not implemented")
|
||||
}
|
||||
func (UnimplementedDaemonServiceServer) ListRoutes(context.Context, *ListRoutesRequest) (*ListRoutesResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListRoutes not implemented")
|
||||
}
|
||||
func (UnimplementedDaemonServiceServer) SelectRoutes(context.Context, *SelectRoutesRequest) (*SelectRoutesResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SelectRoutes not implemented")
|
||||
}
|
||||
func (UnimplementedDaemonServiceServer) DeselectRoutes(context.Context, *SelectRoutesRequest) (*SelectRoutesResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method DeselectRoutes not implemented")
|
||||
}
|
||||
func (UnimplementedDaemonServiceServer) mustEmbedUnimplementedDaemonServiceServer() {}
|
||||
|
||||
// UnsafeDaemonServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
@ -258,6 +306,60 @@ func _DaemonService_GetConfig_Handler(srv interface{}, ctx context.Context, dec
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _DaemonService_ListRoutes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListRoutesRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(DaemonServiceServer).ListRoutes(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/daemon.DaemonService/ListRoutes",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(DaemonServiceServer).ListRoutes(ctx, req.(*ListRoutesRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _DaemonService_SelectRoutes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SelectRoutesRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(DaemonServiceServer).SelectRoutes(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/daemon.DaemonService/SelectRoutes",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(DaemonServiceServer).SelectRoutes(ctx, req.(*SelectRoutesRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _DaemonService_DeselectRoutes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SelectRoutesRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(DaemonServiceServer).DeselectRoutes(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/daemon.DaemonService/DeselectRoutes",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(DaemonServiceServer).DeselectRoutes(ctx, req.(*SelectRoutesRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// DaemonService_ServiceDesc is the grpc.ServiceDesc for DaemonService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
@ -289,6 +391,18 @@ var DaemonService_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "GetConfig",
|
||||
Handler: _DaemonService_GetConfig_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListRoutes",
|
||||
Handler: _DaemonService_ListRoutes_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "SelectRoutes",
|
||||
Handler: _DaemonService_SelectRoutes_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "DeselectRoutes",
|
||||
Handler: _DaemonService_DeselectRoutes_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "daemon.proto",
|
||||
|
100
client/server/route.go
Normal file
100
client/server/route.go
Normal file
@ -0,0 +1,100 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"golang.org/x/exp/maps"
|
||||
|
||||
"github.com/netbirdio/netbird/client/proto"
|
||||
"github.com/netbirdio/netbird/route"
|
||||
)
|
||||
|
||||
// ListRoutes returns a list of all available routes.
|
||||
func (s *Server) ListRoutes(ctx context.Context, req *proto.ListRoutesRequest) (*proto.ListRoutesResponse, error) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
if s.engine == nil {
|
||||
return nil, fmt.Errorf("not connected")
|
||||
}
|
||||
|
||||
routesMap := s.engine.GetClientRoutesWithNetID()
|
||||
routeSelector := s.engine.GetRouteManager().GetRouteSelector()
|
||||
|
||||
var routes []*route.Route
|
||||
for id, rt := range routesMap {
|
||||
if len(rt) == 0 {
|
||||
continue
|
||||
}
|
||||
rt[0].ID = id
|
||||
routes = append(routes, rt[0])
|
||||
}
|
||||
|
||||
sort.Slice(routes, func(i, j int) bool {
|
||||
iPrefix := routes[i].Network.Bits()
|
||||
jPrefix := routes[j].Network.Bits()
|
||||
|
||||
if iPrefix == jPrefix {
|
||||
iAddr := routes[i].Network.Addr()
|
||||
jAddr := routes[j].Network.Addr()
|
||||
if iAddr == jAddr {
|
||||
return routes[i].ID < routes[j].ID
|
||||
}
|
||||
return iAddr.String() < jAddr.String()
|
||||
}
|
||||
return iPrefix < jPrefix
|
||||
})
|
||||
|
||||
var pbRoutes []*proto.Route
|
||||
for _, route := range routes {
|
||||
pbRoutes = append(pbRoutes, &proto.Route{
|
||||
ID: route.ID,
|
||||
Network: route.Network.String(),
|
||||
Selected: routeSelector.IsSelected(route.ID),
|
||||
})
|
||||
}
|
||||
|
||||
return &proto.ListRoutesResponse{
|
||||
Routes: pbRoutes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SelectRoutes selects specific routes based on the client request.
|
||||
func (s *Server) SelectRoutes(_ context.Context, req *proto.SelectRoutesRequest) (*proto.SelectRoutesResponse, error) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
routeManager := s.engine.GetRouteManager()
|
||||
routeSelector := routeManager.GetRouteSelector()
|
||||
if req.GetAll() {
|
||||
routeSelector.SelectAllRoutes()
|
||||
} else {
|
||||
if err := routeSelector.SelectRoutes(req.GetRouteIDs(), req.GetAppend(), maps.Keys(s.engine.GetClientRoutesWithNetID())); err != nil {
|
||||
return nil, fmt.Errorf("select routes: %w", err)
|
||||
}
|
||||
}
|
||||
routeManager.TriggerSelection(s.engine.GetClientRoutes())
|
||||
|
||||
return &proto.SelectRoutesResponse{}, nil
|
||||
}
|
||||
|
||||
// DeselectRoutes deselects specific routes based on the client request.
|
||||
func (s *Server) DeselectRoutes(_ context.Context, req *proto.SelectRoutesRequest) (*proto.SelectRoutesResponse, error) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
routeManager := s.engine.GetRouteManager()
|
||||
routeSelector := routeManager.GetRouteSelector()
|
||||
if req.GetAll() {
|
||||
routeSelector.DeselectAllRoutes()
|
||||
} else {
|
||||
if err := routeSelector.DeselectRoutes(req.GetRouteIDs(), maps.Keys(s.engine.GetClientRoutesWithNetID())); err != nil {
|
||||
return nil, fmt.Errorf("deselect routes: %w", err)
|
||||
}
|
||||
}
|
||||
routeManager.TriggerSelection(s.engine.GetClientRoutes())
|
||||
|
||||
return &proto.SelectRoutesResponse{}, nil
|
||||
}
|
@ -15,15 +15,15 @@ import (
|
||||
|
||||
"google.golang.org/protobuf/types/known/durationpb"
|
||||
|
||||
"github.com/netbirdio/netbird/client/internal/auth"
|
||||
"github.com/netbirdio/netbird/client/system"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
gstatus "google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
"github.com/netbirdio/netbird/client/internal/auth"
|
||||
"github.com/netbirdio/netbird/client/system"
|
||||
|
||||
"github.com/netbirdio/netbird/client/internal"
|
||||
"github.com/netbirdio/netbird/client/internal/peer"
|
||||
"github.com/netbirdio/netbird/client/proto"
|
||||
@ -57,6 +57,8 @@ type Server struct {
|
||||
config *internal.Config
|
||||
proto.UnimplementedDaemonServiceServer
|
||||
|
||||
engine *internal.Engine
|
||||
|
||||
statusRecorder *peer.Status
|
||||
sessionWatcher *internal.SessionWatcher
|
||||
|
||||
@ -141,8 +143,11 @@ func (s *Server) Start() error {
|
||||
s.sessionWatcher.SetOnExpireListener(s.onSessionExpire)
|
||||
}
|
||||
|
||||
engineChan := make(chan *internal.Engine, 1)
|
||||
go s.watchEngine(ctx, engineChan)
|
||||
|
||||
if !config.DisableAutoConnect {
|
||||
go s.connectWithRetryRuns(ctx, config, s.statusRecorder, s.mgmProbe, s.signalProbe, s.relayProbe, s.wgProbe)
|
||||
go s.connectWithRetryRuns(ctx, config, s.statusRecorder, s.mgmProbe, s.signalProbe, s.relayProbe, s.wgProbe, engineChan)
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -153,6 +158,7 @@ func (s *Server) Start() error {
|
||||
// we cancel retry if the client receive a stop or down command, or if disable auto connect is configured.
|
||||
func (s *Server) connectWithRetryRuns(ctx context.Context, config *internal.Config, statusRecorder *peer.Status,
|
||||
mgmProbe *internal.Probe, signalProbe *internal.Probe, relayProbe *internal.Probe, wgProbe *internal.Probe,
|
||||
engineChan chan<- *internal.Engine,
|
||||
) {
|
||||
backOff := getConnectWithBackoff(ctx)
|
||||
retryStarted := false
|
||||
@ -182,7 +188,7 @@ func (s *Server) connectWithRetryRuns(ctx context.Context, config *internal.Conf
|
||||
|
||||
runOperation := func() error {
|
||||
log.Tracef("running client connection")
|
||||
err := internal.RunClientWithProbes(ctx, config, statusRecorder, mgmProbe, signalProbe, relayProbe, wgProbe)
|
||||
err := internal.RunClientWithProbes(ctx, config, statusRecorder, mgmProbe, signalProbe, relayProbe, wgProbe, engineChan)
|
||||
if err != nil {
|
||||
log.Debugf("run client connection exited with error: %v. Will retry in the background", err)
|
||||
}
|
||||
@ -562,7 +568,10 @@ func (s *Server) Up(callerCtx context.Context, _ *proto.UpRequest) (*proto.UpRes
|
||||
s.statusRecorder.UpdateManagementAddress(s.config.ManagementURL.String())
|
||||
s.statusRecorder.UpdateRosenpass(s.config.RosenpassEnabled, s.config.RosenpassPermissive)
|
||||
|
||||
go s.connectWithRetryRuns(ctx, s.config, s.statusRecorder, s.mgmProbe, s.signalProbe, s.relayProbe, s.wgProbe)
|
||||
engineChan := make(chan *internal.Engine, 1)
|
||||
go s.watchEngine(ctx, engineChan)
|
||||
|
||||
go s.connectWithRetryRuns(ctx, s.config, s.statusRecorder, s.mgmProbe, s.signalProbe, s.relayProbe, s.wgProbe, engineChan)
|
||||
|
||||
return &proto.UpResponse{}, nil
|
||||
}
|
||||
@ -579,6 +588,8 @@ func (s *Server) Down(_ context.Context, _ *proto.DownRequest) (*proto.DownRespo
|
||||
state := internal.CtxGetState(s.rootCtx)
|
||||
state.Set(internal.StatusIdle)
|
||||
|
||||
s.engine = nil
|
||||
|
||||
return &proto.DownResponse{}, nil
|
||||
}
|
||||
|
||||
@ -661,7 +672,6 @@ func (s *Server) GetConfig(_ context.Context, _ *proto.GetConfigRequest) (*proto
|
||||
PreSharedKey: preSharedKey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) onSessionExpire() {
|
||||
if runtime.GOOS != "windows" {
|
||||
isUIActive := internal.CheckUIApp()
|
||||
@ -673,6 +683,22 @@ func (s *Server) onSessionExpire() {
|
||||
}
|
||||
}
|
||||
|
||||
// watchEngine watches the engine channel and updates the engine state
|
||||
func (s *Server) watchEngine(ctx context.Context, engineChan chan *internal.Engine) {
|
||||
log.Tracef("Started watching engine")
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
s.engine = nil
|
||||
log.Tracef("Stopped watching engine")
|
||||
return
|
||||
case engine := <-engineChan:
|
||||
log.Tracef("Received engine from watcher")
|
||||
s.engine = engine
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func toProtoFullStatus(fullStatus peer.FullStatus) *proto.FullStatus {
|
||||
pbFullStatus := proto.FullStatus{
|
||||
ManagementState: &proto.ManagementState{},
|
||||
|
@ -2,11 +2,12 @@ package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/netbirdio/management-integrations/integrations"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/netbirdio/management-integrations/integrations"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/keepalive"
|
||||
@ -69,7 +70,7 @@ func TestConnectWithRetryRuns(t *testing.T) {
|
||||
t.Setenv(maxRetryTimeVar, "5s")
|
||||
t.Setenv(retryMultiplierVar, "1")
|
||||
|
||||
s.connectWithRetryRuns(ctx, config, s.statusRecorder, s.mgmProbe, s.signalProbe, s.relayProbe, s.wgProbe)
|
||||
s.connectWithRetryRuns(ctx, config, s.statusRecorder, s.mgmProbe, s.signalProbe, s.relayProbe, s.wgProbe, nil)
|
||||
if counter < 3 {
|
||||
t.Fatalf("expected counter > 2, got %d", counter)
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
//go:build !(linux && 386)
|
||||
// +build !linux !386
|
||||
|
||||
// skipping linux 32 bits build and tests
|
||||
package main
|
||||
|
||||
import (
|
||||
@ -58,6 +56,8 @@ func main() {
|
||||
|
||||
var showSettings bool
|
||||
flag.BoolVar(&showSettings, "settings", false, "run settings windows")
|
||||
var showRoutes bool
|
||||
flag.BoolVar(&showRoutes, "routes", false, "run routes windows")
|
||||
var errorMSG string
|
||||
flag.StringVar(&errorMSG, "error-msg", "", "displays a error message window")
|
||||
|
||||
@ -71,8 +71,8 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
client := newServiceClient(daemonAddr, a, showSettings)
|
||||
if showSettings {
|
||||
client := newServiceClient(daemonAddr, a, showSettings, showRoutes)
|
||||
if showSettings || showRoutes {
|
||||
a.Run()
|
||||
} else {
|
||||
if err := checkPIDFile(); err != nil {
|
||||
@ -135,6 +135,7 @@ type serviceClient struct {
|
||||
mVersionDaemon *systray.MenuItem
|
||||
mUpdate *systray.MenuItem
|
||||
mQuit *systray.MenuItem
|
||||
mRoutes *systray.MenuItem
|
||||
|
||||
// application with main windows.
|
||||
app fyne.App
|
||||
@ -159,12 +160,15 @@ type serviceClient struct {
|
||||
daemonVersion string
|
||||
updateIndicationLock sync.Mutex
|
||||
isUpdateIconActive bool
|
||||
|
||||
showRoutes bool
|
||||
wRoutes fyne.Window
|
||||
}
|
||||
|
||||
// newServiceClient instance constructor
|
||||
//
|
||||
// This constructor also builds the UI elements for the settings window.
|
||||
func newServiceClient(addr string, a fyne.App, showSettings bool) *serviceClient {
|
||||
func newServiceClient(addr string, a fyne.App, showSettings bool, showRoutes bool) *serviceClient {
|
||||
s := &serviceClient{
|
||||
ctx: context.Background(),
|
||||
addr: addr,
|
||||
@ -172,6 +176,7 @@ func newServiceClient(addr string, a fyne.App, showSettings bool) *serviceClient
|
||||
sendNotification: false,
|
||||
|
||||
showSettings: showSettings,
|
||||
showRoutes: showRoutes,
|
||||
update: version.NewUpdate(),
|
||||
}
|
||||
|
||||
@ -191,14 +196,16 @@ func newServiceClient(addr string, a fyne.App, showSettings bool) *serviceClient
|
||||
}
|
||||
|
||||
if showSettings {
|
||||
s.showUIElements()
|
||||
s.showSettingsUI()
|
||||
return s
|
||||
} else if showRoutes {
|
||||
s.showRoutesUI()
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *serviceClient) showUIElements() {
|
||||
func (s *serviceClient) showSettingsUI() {
|
||||
// add settings window UI elements.
|
||||
s.wSettings = s.app.NewWindow("NetBird Settings")
|
||||
s.iMngURL = widget.NewEntry()
|
||||
@ -416,6 +423,7 @@ func (s *serviceClient) updateStatus() error {
|
||||
s.mStatus.SetTitle("Connected")
|
||||
s.mUp.Disable()
|
||||
s.mDown.Enable()
|
||||
s.mRoutes.Enable()
|
||||
systrayIconState = true
|
||||
} else if status.Status != string(internal.StatusConnected) && s.mUp.Disabled() {
|
||||
s.connected = false
|
||||
@ -428,6 +436,7 @@ func (s *serviceClient) updateStatus() error {
|
||||
s.mStatus.SetTitle("Disconnected")
|
||||
s.mDown.Disable()
|
||||
s.mUp.Enable()
|
||||
s.mRoutes.Disable()
|
||||
systrayIconState = false
|
||||
}
|
||||
|
||||
@ -483,9 +492,11 @@ func (s *serviceClient) onTrayReady() {
|
||||
s.mUp = systray.AddMenuItem("Connect", "Connect")
|
||||
s.mDown = systray.AddMenuItem("Disconnect", "Disconnect")
|
||||
s.mDown.Disable()
|
||||
s.mAdminPanel = systray.AddMenuItem("Admin Panel", "Wiretrustee Admin Panel")
|
||||
s.mAdminPanel = systray.AddMenuItem("Admin Panel", "Netbird Admin Panel")
|
||||
systray.AddSeparator()
|
||||
s.mSettings = systray.AddMenuItem("Settings", "Settings of the application")
|
||||
s.mRoutes = systray.AddMenuItem("Network Routes", "Open the routes management window")
|
||||
s.mRoutes.Disable()
|
||||
systray.AddSeparator()
|
||||
|
||||
s.mAbout = systray.AddMenuItem("About", "About")
|
||||
@ -557,6 +568,12 @@ func (s *serviceClient) onTrayReady() {
|
||||
if err != nil {
|
||||
log.Errorf("%s", err)
|
||||
}
|
||||
case <-s.mRoutes.ClickedCh:
|
||||
s.mRoutes.Disable()
|
||||
go func() {
|
||||
defer s.mRoutes.Enable()
|
||||
s.runSelfCommand("routes", "true")
|
||||
}()
|
||||
}
|
||||
if err != nil {
|
||||
log.Errorf("process connection: %v", err)
|
||||
|
203
client/ui/route.go
Normal file
203
client/ui/route.go
Normal file
@ -0,0 +1,203 @@
|
||||
//go:build !(linux && 386)
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/container"
|
||||
"fyne.io/fyne/v2/dialog"
|
||||
"fyne.io/fyne/v2/layout"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/netbirdio/netbird/client/proto"
|
||||
)
|
||||
|
||||
func (s *serviceClient) showRoutesUI() {
|
||||
s.wRoutes = s.app.NewWindow("NetBird Routes")
|
||||
|
||||
grid := container.New(layout.NewGridLayout(2))
|
||||
go s.updateRoutes(grid)
|
||||
routeCheckContainer := container.NewVBox()
|
||||
routeCheckContainer.Add(grid)
|
||||
scrollContainer := container.NewVScroll(routeCheckContainer)
|
||||
scrollContainer.SetMinSize(fyne.NewSize(200, 300))
|
||||
|
||||
buttonBox := container.NewHBox(
|
||||
layout.NewSpacer(),
|
||||
widget.NewButton("Refresh", func() {
|
||||
s.updateRoutes(grid)
|
||||
}),
|
||||
widget.NewButton("Select all", func() {
|
||||
s.selectAllRoutes()
|
||||
s.updateRoutes(grid)
|
||||
}),
|
||||
widget.NewButton("Deselect All", func() {
|
||||
s.deselectAllRoutes()
|
||||
s.updateRoutes(grid)
|
||||
}),
|
||||
layout.NewSpacer(),
|
||||
)
|
||||
|
||||
content := container.NewBorder(nil, buttonBox, nil, nil, scrollContainer)
|
||||
|
||||
s.wRoutes.SetContent(content)
|
||||
s.wRoutes.Show()
|
||||
|
||||
s.startAutoRefresh(5*time.Second, grid)
|
||||
}
|
||||
|
||||
func (s *serviceClient) updateRoutes(grid *fyne.Container) {
|
||||
routes, err := s.fetchRoutes()
|
||||
if err != nil {
|
||||
log.Errorf("get client: %v", err)
|
||||
s.showError(fmt.Errorf("get client: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
grid.Objects = nil
|
||||
idHeader := widget.NewLabelWithStyle(" ID", fyne.TextAlignLeading, fyne.TextStyle{Bold: true})
|
||||
networkHeader := widget.NewLabelWithStyle("Network", fyne.TextAlignLeading, fyne.TextStyle{Bold: true})
|
||||
|
||||
grid.Add(idHeader)
|
||||
grid.Add(networkHeader)
|
||||
for _, route := range routes {
|
||||
r := route
|
||||
|
||||
checkBox := widget.NewCheck(r.ID, func(checked bool) {
|
||||
s.selectRoute(r.ID, checked)
|
||||
})
|
||||
checkBox.Checked = route.Selected
|
||||
checkBox.Resize(fyne.NewSize(20, 20))
|
||||
checkBox.Refresh()
|
||||
|
||||
grid.Add(checkBox)
|
||||
grid.Add(widget.NewLabel(r.Network))
|
||||
}
|
||||
|
||||
s.wRoutes.Content().Refresh()
|
||||
}
|
||||
|
||||
func (s *serviceClient) fetchRoutes() ([]*proto.Route, error) {
|
||||
conn, err := s.getSrvClient(defaultFailTimeout)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get client: %v", err)
|
||||
}
|
||||
|
||||
resp, err := conn.ListRoutes(s.ctx, &proto.ListRoutesRequest{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list routes: %v", err)
|
||||
}
|
||||
|
||||
return resp.Routes, nil
|
||||
}
|
||||
|
||||
func (s *serviceClient) selectRoute(id string, checked bool) {
|
||||
conn, err := s.getSrvClient(defaultFailTimeout)
|
||||
if err != nil {
|
||||
log.Errorf("get client: %v", err)
|
||||
s.showError(fmt.Errorf("get client: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
req := &proto.SelectRoutesRequest{
|
||||
RouteIDs: []string{id},
|
||||
Append: checked,
|
||||
}
|
||||
|
||||
if checked {
|
||||
if _, err := conn.SelectRoutes(s.ctx, req); err != nil {
|
||||
log.Errorf("failed to select route: %v", err)
|
||||
s.showError(fmt.Errorf("failed to select route: %v", err))
|
||||
return
|
||||
}
|
||||
log.Infof("Route %s selected", id)
|
||||
} else {
|
||||
if _, err := conn.DeselectRoutes(s.ctx, req); err != nil {
|
||||
log.Errorf("failed to deselect route: %v", err)
|
||||
s.showError(fmt.Errorf("failed to deselect route: %v", err))
|
||||
return
|
||||
}
|
||||
log.Infof("Route %s deselected", id)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *serviceClient) selectAllRoutes() {
|
||||
conn, err := s.getSrvClient(defaultFailTimeout)
|
||||
if err != nil {
|
||||
log.Errorf("get client: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
req := &proto.SelectRoutesRequest{
|
||||
All: true,
|
||||
}
|
||||
if _, err := conn.SelectRoutes(s.ctx, req); err != nil {
|
||||
log.Errorf("failed to select all routes: %v", err)
|
||||
s.showError(fmt.Errorf("failed to select all routes: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
log.Debug("All routes selected")
|
||||
}
|
||||
|
||||
func (s *serviceClient) deselectAllRoutes() {
|
||||
conn, err := s.getSrvClient(defaultFailTimeout)
|
||||
if err != nil {
|
||||
log.Errorf("get client: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
req := &proto.SelectRoutesRequest{
|
||||
All: true,
|
||||
}
|
||||
if _, err := conn.DeselectRoutes(s.ctx, req); err != nil {
|
||||
log.Errorf("failed to deselect all routes: %v", err)
|
||||
s.showError(fmt.Errorf("failed to deselect all routes: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
log.Debug("All routes deselected")
|
||||
}
|
||||
|
||||
func (s *serviceClient) showError(err error) {
|
||||
wrappedMessage := wrapText(err.Error(), 50)
|
||||
|
||||
dialog.ShowError(fmt.Errorf("%s", wrappedMessage), s.wRoutes)
|
||||
}
|
||||
|
||||
func (s *serviceClient) startAutoRefresh(interval time.Duration, grid *fyne.Container) {
|
||||
ticker := time.NewTicker(interval)
|
||||
go func() {
|
||||
for range ticker.C {
|
||||
s.updateRoutes(grid)
|
||||
}
|
||||
}()
|
||||
|
||||
s.wRoutes.SetOnClosed(func() {
|
||||
ticker.Stop()
|
||||
})
|
||||
}
|
||||
|
||||
// wrapText inserts newlines into the text to ensure that each line is
|
||||
// no longer than 'lineLength' runes.
|
||||
func wrapText(text string, lineLength int) string {
|
||||
var sb strings.Builder
|
||||
var currentLineLength int
|
||||
|
||||
for _, runeValue := range text {
|
||||
sb.WriteRune(runeValue)
|
||||
currentLineLength++
|
||||
|
||||
if currentLineLength >= lineLength || runeValue == '\n' {
|
||||
sb.WriteRune('\n')
|
||||
currentLineLength = 0
|
||||
}
|
||||
}
|
||||
|
||||
return sb.String()
|
||||
}
|
Loading…
Reference in New Issue
Block a user