merge main

This commit is contained in:
Pascal Fischer
2023-11-07 15:18:37 +01:00
150 changed files with 3375 additions and 1065 deletions

View File

@@ -53,7 +53,7 @@ func newDefaultManager(fm firewall.Manager) *DefaultManager {
// ApplyFiltering firewall rules to the local firewall manager processed by ACL policy.
//
// If allowByDefault is ture it appends allow ALL traffic rules to input and output chains.
// If allowByDefault is true it appends allow ALL traffic rules to input and output chains.
func (d *DefaultManager) ApplyFiltering(networkMap *mgmProto.NetworkMap) {
d.mutex.Lock()
defer d.mutex.Unlock()
@@ -366,7 +366,7 @@ func (d *DefaultManager) squashAcceptRules(
protocols[r.Protocol] = map[string]int{}
}
// special case, when we recieve this all network IP address
// special case, when we receive this all network IP address
// it means that rules for that protocol was already optimized on the
// management side
if r.PeerIP == "0.0.0.0" {
@@ -393,7 +393,7 @@ func (d *DefaultManager) squashAcceptRules(
}
// order of squashing by protocol is important
// only for ther first element ALL, it must be done first
// only for their first element ALL, it must be done first
protocolOrders := []mgmProto.FirewallRuleProtocol{
mgmProto.FirewallRule_ALL,
mgmProto.FirewallRule_ICMP,

View File

@@ -20,7 +20,7 @@ func Create(iface IFaceMapper) (manager *DefaultManager, err error) {
return nil, err
}
if err := fm.AllowNetbird(); err != nil {
log.Errorf("failed to allow netbird interface traffic: %v", err)
log.Warnf("failed to allow netbird interface traffic: %v", err)
}
return newDefaultManager(fm), nil
}

View File

@@ -13,8 +13,8 @@ import (
gstatus "google.golang.org/grpc/status"
"github.com/netbirdio/netbird/client/internal/dns"
"github.com/netbirdio/netbird/client/internal/listener"
"github.com/netbirdio/netbird/client/internal/peer"
"github.com/netbirdio/netbird/client/internal/routemanager"
"github.com/netbirdio/netbird/client/internal/stdnet"
"github.com/netbirdio/netbird/client/ssh"
"github.com/netbirdio/netbird/client/system"
@@ -22,6 +22,7 @@ import (
mgm "github.com/netbirdio/netbird/management/client"
mgmProto "github.com/netbirdio/netbird/management/proto"
signal "github.com/netbirdio/netbird/signal/client"
"github.com/netbirdio/netbird/version"
)
// RunClient with main logic.
@@ -30,14 +31,14 @@ func RunClient(ctx context.Context, config *Config, statusRecorder *peer.Status)
}
// RunClientMobile with main logic on mobile system
func RunClientMobile(ctx context.Context, config *Config, statusRecorder *peer.Status, tunAdapter iface.TunAdapter, iFaceDiscover stdnet.ExternalIFaceDiscover, routeListener routemanager.RouteListener, dnsAddresses []string, dnsReadyListener dns.ReadyListener) error {
func RunClientMobile(ctx context.Context, config *Config, statusRecorder *peer.Status, tunAdapter iface.TunAdapter, iFaceDiscover stdnet.ExternalIFaceDiscover, networkChangeListener listener.NetworkChangeListener, dnsAddresses []string, dnsReadyListener dns.ReadyListener) error {
// in case of non Android os these variables will be nil
mobileDependency := MobileDependency{
TunAdapter: tunAdapter,
IFaceDiscover: iFaceDiscover,
RouteListener: routeListener,
HostDNSAddresses: dnsAddresses,
DnsReadyListener: dnsReadyListener,
TunAdapter: tunAdapter,
IFaceDiscover: iFaceDiscover,
NetworkChangeListener: networkChangeListener,
HostDNSAddresses: dnsAddresses,
DnsReadyListener: dnsReadyListener,
}
return runClient(ctx, config, statusRecorder, mobileDependency)
}
@@ -53,6 +54,8 @@ func RunClientiOS(ctx context.Context, config *Config, statusRecorder *peer.Stat
}
func runClient(ctx context.Context, config *Config, statusRecorder *peer.Status, mobileDependency MobileDependency) error {
log.Infof("starting NetBird client version %s", version.NetbirdVersion())
backOff := &backoff.ExponentialBackOff{
InitialInterval: time.Second,
RandomizationFactor: 1,
@@ -106,7 +109,7 @@ func runClient(ctx context.Context, config *Config, statusRecorder *peer.Status,
cancel()
}()
log.Debugf("conecting to the Management service %s", config.ManagementURL.Host)
log.Debugf("connecting to the Management service %s", config.ManagementURL.Host)
mgmClient, err := mgm.NewClient(engineCtx, config.ManagementURL.Host, myPrivateKey, mgmTlsEnabled)
if err != nil {
return wrapErr(gstatus.Errorf(codes.FailedPrecondition, "failed connecting to Management Service : %s", err))

View File

@@ -3,29 +3,25 @@
package dns
import (
"bufio"
"bytes"
"fmt"
"os"
"strings"
log "github.com/sirupsen/logrus"
)
const (
fileGeneratedResolvConfContentHeader = "# Generated by NetBird"
fileGeneratedResolvConfSearchBeginContent = "search "
fileGeneratedResolvConfContentFormat = fileGeneratedResolvConfContentHeader +
"\n# If needed you can restore the original file by copying back %s\n\nnameserver %s\n" +
fileGeneratedResolvConfSearchBeginContent + "%s\n\n" +
"%s\n"
)
fileGeneratedResolvConfContentHeader = "# Generated by NetBird"
fileGeneratedResolvConfContentHeaderNextLine = fileGeneratedResolvConfContentHeader + `
# If needed you can restore the original file by copying back ` + fileDefaultResolvConfBackupLocation + "\n\n"
const (
fileDefaultResolvConfBackupLocation = defaultResolvConfPath + ".original.netbird"
fileMaxLineCharsLimit = 256
fileMaxNumberOfSearchDomains = 6
)
var fileSearchLineBeginCharCount = len(fileGeneratedResolvConfSearchBeginContent)
fileMaxLineCharsLimit = 256
fileMaxNumberOfSearchDomains = 6
)
type fileConfigurator struct {
originalPerms os.FileMode
@@ -55,58 +51,39 @@ func (f *fileConfigurator) applyDNSConfig(config HostDNSConfig) error {
}
return fmt.Errorf("unable to configure DNS for this peer using file manager without a nameserver group with all domains configured")
}
managerType, err := getOSDNSManagerType()
if err != nil {
return err
}
switch managerType {
case fileManager, netbirdManager:
if !backupFileExist {
err = f.backup()
if err != nil {
return fmt.Errorf("unable to backup the resolv.conf file")
}
}
default:
// todo improve this and maybe restart DNS manager from scratch
return fmt.Errorf("something happened and file manager is not your prefered host dns configurator, restart the agent")
}
var searchDomains string
appendedDomains := 0
for _, dConf := range config.domains {
if dConf.matchOnly || dConf.disabled {
continue
}
if appendedDomains >= fileMaxNumberOfSearchDomains {
// lets log all skipped domains
log.Infof("already appended %d domains to search list. Skipping append of %s domain", fileMaxNumberOfSearchDomains, dConf.domain)
continue
}
if fileSearchLineBeginCharCount+len(searchDomains) > fileMaxLineCharsLimit {
// lets log all skipped domains
log.Infof("search list line is larger than %d characters. Skipping append of %s domain", fileMaxLineCharsLimit, dConf.domain)
continue
}
searchDomains += " " + dConf.domain
appendedDomains++
}
originalContent, err := os.ReadFile(fileDefaultResolvConfBackupLocation)
if err != nil {
log.Errorf("Could not read existing resolv.conf")
}
content := fmt.Sprintf(fileGeneratedResolvConfContentFormat, fileDefaultResolvConfBackupLocation, config.serverIP, searchDomains, string(originalContent))
err = writeDNSConfig(content, defaultResolvConfPath, f.originalPerms)
if err != nil {
err = f.restore()
if !backupFileExist {
err = f.backup()
if err != nil {
return fmt.Errorf("unable to backup the resolv.conf file")
}
}
searchDomainList := searchDomains(config)
originalSearchDomains, nameServers, others, err := originalDNSConfigs(fileDefaultResolvConfBackupLocation)
if err != nil {
log.Error(err)
}
searchDomainList = mergeSearchDomains(searchDomainList, originalSearchDomains)
buf := prepareResolvConfContent(
searchDomainList,
append([]string{config.serverIP}, nameServers...),
others)
log.Debugf("creating managed file %s", defaultResolvConfPath)
err = os.WriteFile(defaultResolvConfPath, buf.Bytes(), f.originalPerms)
if err != nil {
restoreErr := f.restore()
if restoreErr != nil {
log.Errorf("attempt to restore default file failed with error: %s", err)
}
return err
return fmt.Errorf("got an creating resolver file %s. Error: %s", defaultResolvConfPath, err)
}
log.Infof("created a NetBird managed %s file with your DNS settings. Added %d search domains. Search list: %s", defaultResolvConfPath, appendedDomains, searchDomains)
log.Infof("created a NetBird managed %s file with your DNS settings. Added %d search domains. Search list: %s", defaultResolvConfPath, len(searchDomainList), searchDomainList)
return nil
}
@@ -138,15 +115,138 @@ func (f *fileConfigurator) restore() error {
return os.RemoveAll(fileDefaultResolvConfBackupLocation)
}
func writeDNSConfig(content, fileName string, permissions os.FileMode) error {
log.Debugf("creating managed file %s", fileName)
func prepareResolvConfContent(searchDomains, nameServers, others []string) bytes.Buffer {
var buf bytes.Buffer
buf.WriteString(content)
err := os.WriteFile(fileName, buf.Bytes(), permissions)
if err != nil {
return fmt.Errorf("got an creating resolver file %s. Error: %s", fileName, err)
buf.WriteString(fileGeneratedResolvConfContentHeaderNextLine)
for _, cfgLine := range others {
buf.WriteString(cfgLine)
buf.WriteString("\n")
}
return nil
if len(searchDomains) > 0 {
buf.WriteString("search ")
buf.WriteString(strings.Join(searchDomains, " "))
buf.WriteString("\n")
}
for _, ns := range nameServers {
buf.WriteString("nameserver ")
buf.WriteString(ns)
buf.WriteString("\n")
}
return buf
}
func searchDomains(config hostDNSConfig) []string {
listOfDomains := make([]string, 0)
for _, dConf := range config.domains {
if dConf.matchOnly || dConf.disabled {
continue
}
listOfDomains = append(listOfDomains, dConf.domain)
}
return listOfDomains
}
func originalDNSConfigs(resolvconfFile string) (searchDomains, nameServers, others []string, err error) {
file, err := os.Open(resolvconfFile)
if err != nil {
err = fmt.Errorf(`could not read existing resolv.conf`)
return
}
defer file.Close()
reader := bufio.NewReader(file)
for {
lineBytes, isPrefix, readErr := reader.ReadLine()
if readErr != nil {
break
}
if isPrefix {
err = fmt.Errorf(`resolv.conf line too long`)
return
}
line := strings.TrimSpace(string(lineBytes))
if strings.HasPrefix(line, "#") {
continue
}
if strings.HasPrefix(line, "domain") {
continue
}
if strings.HasPrefix(line, "options") && strings.Contains(line, "rotate") {
line = strings.ReplaceAll(line, "rotate", "")
splitLines := strings.Fields(line)
if len(splitLines) == 1 {
continue
}
line = strings.Join(splitLines, " ")
}
if strings.HasPrefix(line, "search") {
splitLines := strings.Fields(line)
if len(splitLines) < 2 {
continue
}
searchDomains = splitLines[1:]
continue
}
if strings.HasPrefix(line, "nameserver") {
splitLines := strings.Fields(line)
if len(splitLines) != 2 {
continue
}
nameServers = append(nameServers, splitLines[1])
continue
}
others = append(others, line)
}
return
}
// merge search domains lists and cut off the list if it is too long
func mergeSearchDomains(searchDomains []string, originalSearchDomains []string) []string {
lineSize := len("search")
searchDomainsList := make([]string, 0, len(searchDomains)+len(originalSearchDomains))
lineSize = validateAndFillSearchDomains(lineSize, &searchDomainsList, searchDomains)
_ = validateAndFillSearchDomains(lineSize, &searchDomainsList, originalSearchDomains)
return searchDomainsList
}
// validateAndFillSearchDomains checks if the search domains list is not too long and if the line is not too long
// extend s slice with vs elements
// return with the number of characters in the searchDomains line
func validateAndFillSearchDomains(initialLineChars int, s *[]string, vs []string) int {
for _, sd := range vs {
tmpCharsNumber := initialLineChars + 1 + len(sd)
if tmpCharsNumber > fileMaxLineCharsLimit {
// lets log all skipped domains
log.Infof("search list line is larger than %d characters. Skipping append of %s domain", fileMaxLineCharsLimit, sd)
continue
}
initialLineChars = tmpCharsNumber
if len(*s) >= fileMaxNumberOfSearchDomains {
// lets log all skipped domains
log.Infof("already appended %d domains to search list. Skipping append of %s domain", fileMaxNumberOfSearchDomains, sd)
continue
}
*s = append(*s, sd)
}
return initialLineChars
}
func copyFile(src, dest string) error {

View File

@@ -0,0 +1,62 @@
package dns
import (
"fmt"
"testing"
)
func Test_mergeSearchDomains(t *testing.T) {
searchDomains := []string{"a", "b"}
originDomains := []string{"a", "b"}
mergedDomains := mergeSearchDomains(searchDomains, originDomains)
if len(mergedDomains) != 4 {
t.Errorf("invalid len of result domains: %d, want: %d", len(mergedDomains), 4)
}
}
func Test_mergeSearchTooMuchDomains(t *testing.T) {
searchDomains := []string{"a", "b", "c", "d", "e", "f", "g"}
originDomains := []string{"h", "i"}
mergedDomains := mergeSearchDomains(searchDomains, originDomains)
if len(mergedDomains) != 6 {
t.Errorf("invalid len of result domains: %d, want: %d", len(mergedDomains), 6)
}
}
func Test_mergeSearchTooMuchDomainsInOrigin(t *testing.T) {
searchDomains := []string{"a", "b"}
originDomains := []string{"c", "d", "e", "f", "g"}
mergedDomains := mergeSearchDomains(searchDomains, originDomains)
if len(mergedDomains) != 6 {
t.Errorf("invalid len of result domains: %d, want: %d", len(mergedDomains), 6)
}
}
func Test_mergeSearchTooLongDomain(t *testing.T) {
searchDomains := []string{getLongLine()}
originDomains := []string{"b"}
mergedDomains := mergeSearchDomains(searchDomains, originDomains)
if len(mergedDomains) != 1 {
t.Errorf("invalid len of result domains: %d, want: %d", len(mergedDomains), 1)
}
searchDomains = []string{"b"}
originDomains = []string{getLongLine()}
mergedDomains = mergeSearchDomains(searchDomains, originDomains)
if len(mergedDomains) != 1 {
t.Errorf("invalid len of result domains: %d, want: %d", len(mergedDomains), 1)
}
}
func getLongLine() string {
x := "search "
for {
for i := 0; i <= 9; i++ {
if len(x) > fileMaxLineCharsLimit {
return x
}
x = fmt.Sprintf("%s%d", x, i)
}
}
}

View File

@@ -78,7 +78,7 @@ func dnsConfigToHostDNSConfig(dnsConfig nbdns.Config, ip string, port int) HostD
for _, domain := range nsConfig.Domains {
config.domains = append(config.domains, domainConfig{
domain: strings.TrimSuffix(domain, "."),
matchOnly: true,
matchOnly: !nsConfig.SearchDomainsEnabled,
})
}
}

View File

@@ -22,13 +22,11 @@ const (
interfaceConfigPath = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces"
interfaceConfigNameServerKey = "NameServer"
interfaceConfigSearchListKey = "SearchList"
tcpipParametersPath = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"
)
type registryConfigurator struct {
guid string
routingAll bool
existingSearchDomains []string
guid string
routingAll bool
}
func newHostManager(wgInterface WGIface) (hostManager, error) {
@@ -148,30 +146,11 @@ func (r *registryConfigurator) restoreHostDNS() error {
log.Error(err)
}
return r.updateSearchDomains([]string{})
return r.deleteInterfaceRegistryKeyProperty(interfaceConfigSearchListKey)
}
func (r *registryConfigurator) updateSearchDomains(domains []string) error {
value, err := getLocalMachineRegistryKeyStringValue(tcpipParametersPath, interfaceConfigSearchListKey)
if err != nil {
return fmt.Errorf("unable to get current search domains failed with error: %s", err)
}
valueList := strings.Split(value, ",")
setExisting := false
if len(r.existingSearchDomains) == 0 {
r.existingSearchDomains = valueList
setExisting = true
}
if len(domains) == 0 && setExisting {
log.Infof("added %d search domains to the registry. Domain list: %s", len(domains), domains)
return nil
}
newList := append(r.existingSearchDomains, domains...)
err = setLocalMachineRegistryKeyStringValue(tcpipParametersPath, interfaceConfigSearchListKey, strings.Join(newList, ","))
err := r.setInterfaceRegistryKeyStringValue(interfaceConfigSearchListKey, strings.Join(domains, ","))
if err != nil {
return fmt.Errorf("adding search domain failed with error: %s", err)
}
@@ -235,33 +214,3 @@ func removeRegistryKeyFromDNSPolicyConfig(regKeyPath string) error {
}
return nil
}
func getLocalMachineRegistryKeyStringValue(keyPath, key string) (string, error) {
regKey, err := registry.OpenKey(registry.LOCAL_MACHINE, keyPath, registry.QUERY_VALUE)
if err != nil {
return "", fmt.Errorf("unable to open existing key from registry, key path: HKEY_LOCAL_MACHINE\\%s, error: %s", keyPath, err)
}
defer regKey.Close()
val, _, err := regKey.GetStringValue(key)
if err != nil {
return "", fmt.Errorf("getting %s value for key path HKEY_LOCAL_MACHINE\\%s failed with error: %s", key, keyPath, err)
}
return val, nil
}
func setLocalMachineRegistryKeyStringValue(keyPath, key, value string) error {
regKey, err := registry.OpenKey(registry.LOCAL_MACHINE, keyPath, registry.SET_VALUE)
if err != nil {
return fmt.Errorf("unable to open existing key from registry, key path: HKEY_LOCAL_MACHINE\\%s, error: %s", keyPath, err)
}
defer regKey.Close()
err = regKey.SetStringValue(key, value)
if err != nil {
return fmt.Errorf("setting %s value %s for key path HKEY_LOCAL_MACHINE\\%s failed with error: %s", key, value, keyPath, err)
}
return nil
}

View File

@@ -2,6 +2,7 @@ package dns
import (
"fmt"
nbdns "github.com/netbirdio/netbird/dns"
)
@@ -43,3 +44,7 @@ func (m *MockServer) UpdateDNSServer(serial uint64, update nbdns.Config) error {
}
return fmt.Errorf("method UpdateDNSServer is not implemented")
}
func (m *MockServer) SearchDomains() []string {
return make([]string, 0)
}

View File

@@ -0,0 +1,57 @@
package dns
import (
"reflect"
"sort"
"sync"
"github.com/netbirdio/netbird/client/internal/listener"
)
type notifier struct {
listener listener.NetworkChangeListener
listenerMux sync.Mutex
searchDomains []string
}
func newNotifier(initialSearchDomains []string) *notifier {
sort.Strings(initialSearchDomains)
return &notifier{
searchDomains: initialSearchDomains,
}
}
func (n *notifier) setListener(listener listener.NetworkChangeListener) {
n.listenerMux.Lock()
defer n.listenerMux.Unlock()
n.listener = listener
}
func (n *notifier) onNewSearchDomains(searchDomains []string) {
sort.Strings(searchDomains)
if len(n.searchDomains) != len(searchDomains) {
n.searchDomains = searchDomains
n.notify()
return
}
if reflect.DeepEqual(n.searchDomains, searchDomains) {
return
}
n.searchDomains = searchDomains
n.notify()
}
func (n *notifier) notify() {
n.listenerMux.Lock()
defer n.listenerMux.Unlock()
if n.listener == nil {
return
}
go func(l listener.NetworkChangeListener) {
l.OnNetworkChanged()
}(n.listener)
}

View File

@@ -3,10 +3,9 @@
package dns
import (
"bytes"
"fmt"
"os"
"os/exec"
"strings"
log "github.com/sirupsen/logrus"
)
@@ -15,11 +14,24 @@ const resolvconfCommand = "resolvconf"
type resolvconf struct {
ifaceName string
originalSearchDomains []string
originalNameServers []string
othersConfigs []string
}
// supported "openresolv" only
func newResolvConfConfigurator(wgInterface WGIface) (hostManager, error) {
originalSearchDomains, nameServers, others, err := originalDNSConfigs("/etc/resolv.conf")
if err != nil {
log.Error(err)
}
return &resolvconf{
ifaceName: wgInterface.Name(),
ifaceName: wgInterface.Name(),
originalSearchDomains: originalSearchDomains,
originalNameServers: nameServers,
othersConfigs: others,
}, nil
}
@@ -37,41 +49,20 @@ func (r *resolvconf) applyDNSConfig(config HostDNSConfig) error {
return fmt.Errorf("unable to configure DNS for this peer using resolvconf manager without a nameserver group with all domains configured")
}
var searchDomains string
appendedDomains := 0
for _, dConf := range config.domains {
if dConf.matchOnly || dConf.disabled {
continue
}
searchDomainList := searchDomains(config)
searchDomainList = mergeSearchDomains(searchDomainList, r.originalSearchDomains)
if appendedDomains >= fileMaxNumberOfSearchDomains {
// lets log all skipped domains
log.Infof("already appended %d domains to search list. Skipping append of %s domain", fileMaxNumberOfSearchDomains, dConf.domain)
continue
}
buf := prepareResolvConfContent(
searchDomainList,
append([]string{config.serverIP}, r.originalNameServers...),
r.othersConfigs)
if fileSearchLineBeginCharCount+len(searchDomains) > fileMaxLineCharsLimit {
// lets log all skipped domains
log.Infof("search list line is larger than %d characters. Skipping append of %s domain", fileMaxLineCharsLimit, dConf.domain)
continue
}
searchDomains += " " + dConf.domain
appendedDomains++
}
originalContent, err := os.ReadFile(fileDefaultResolvConfBackupLocation)
if err != nil {
log.Errorf("Could not read existing resolv.conf")
}
content := fmt.Sprintf(fileGeneratedResolvConfContentFormat, fileDefaultResolvConfBackupLocation, config.serverIP, searchDomains, string(originalContent))
err = r.applyConfig(content)
err = r.applyConfig(buf)
if err != nil {
return err
}
log.Infof("added %d search domains. Search list: %s", appendedDomains, searchDomains)
log.Infof("added %d search domains. Search list: %s", len(searchDomainList), searchDomainList)
return nil
}
@@ -84,12 +75,12 @@ func (r *resolvconf) restoreHostDNS() error {
return nil
}
func (r *resolvconf) applyConfig(content string) error {
func (r *resolvconf) applyConfig(content bytes.Buffer) error {
cmd := exec.Command(resolvconfCommand, "-x", "-a", r.ifaceName)
cmd.Stdin = strings.NewReader(content)
cmd.Stdin = &content
_, err := cmd.Output()
if err != nil {
return fmt.Errorf("got an error while appying resolvconf configuration for %s interface, error: %s", r.ifaceName, err)
return fmt.Errorf("got an error while applying resolvconf configuration for %s interface, error: %s", r.ifaceName, err)
}
return nil
}

View File

@@ -4,13 +4,13 @@ import (
"context"
"fmt"
"net/netip"
"runtime"
"sync"
"github.com/miekg/dns"
"github.com/mitchellh/hashstructure/v2"
log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/client/internal/listener"
nbdns "github.com/netbirdio/netbird/dns"
)
@@ -31,6 +31,7 @@ type Server interface {
DnsIP() string
UpdateDNSServer(serial uint64, update nbdns.Config) error
OnUpdatedHostDNSServer(strings []string)
SearchDomains() []string
}
type registeredHandlerMap map[string]handlerWithStop
@@ -56,6 +57,9 @@ type DefaultServer struct {
interfaceName string
wgAddr string
// make sense on mobile only
searchDomainNotifier *notifier
}
type handlerWithStop interface {
@@ -90,12 +94,15 @@ func NewDefaultServer(ctx context.Context, wgInterface WGIface, customAddress st
}
// NewDefaultServerPermanentUpstream returns a new dns server. It optimized for mobile systems
func NewDefaultServerPermanentUpstream(ctx context.Context, wgInterface WGIface, hostsDnsList []string) *DefaultServer {
func NewDefaultServerPermanentUpstream(ctx context.Context, wgInterface WGIface, hostsDnsList []string, config nbdns.Config, listener listener.NetworkChangeListener) *DefaultServer {
log.Debugf("host dns address list is: %v", hostsDnsList)
ds := newDefaultServer(ctx, wgInterface, newServiceViaMemory(wgInterface), "", "")
ds.permanent = true
ds.hostsDnsList = hostsDnsList
ds.addHostRootZone()
ds.currentConfig = dnsConfigToHostDNSConfig(config, ds.service.RuntimeIP(), ds.service.RuntimePort())
ds.searchDomainNotifier = newNotifier(ds.SearchDomains())
ds.searchDomainNotifier.setListener(listener)
setServerDns(ds)
return ds
}
@@ -227,6 +234,21 @@ func (s *DefaultServer) UpdateDNSServer(serial uint64, update nbdns.Config) erro
}
}
func (s *DefaultServer) SearchDomains() []string {
var searchDomains []string
for _, dConf := range s.currentConfig.domains {
if dConf.disabled {
continue
}
if dConf.matchOnly {
continue
}
searchDomains = append(searchDomains, dConf.domain)
}
return searchDomains
}
func (s *DefaultServer) applyConfiguration(update nbdns.Config) error {
// is the service should be disabled, we stop the listener or fake resolver
// and proceed with a regular update to clean up the handlers and records
@@ -261,6 +283,10 @@ func (s *DefaultServer) applyConfiguration(update nbdns.Config) error {
log.Error(err)
}
if s.searchDomainNotifier != nil {
s.searchDomainNotifier.onNewSearchDomains(s.SearchDomains())
}
return nil
}
@@ -303,7 +329,7 @@ func (s *DefaultServer) buildUpstreamHandlerUpdate(nameServerGroups []*nbdns.Nam
handler := newUpstreamResolver(s.ctx, s.interfaceName, s.wgAddr)
for _, ns := range nsGroup.NameServers {
if ns.NSType != nbdns.UDPNameServerType {
log.Warnf("skiping nameserver %s with type %s, this peer supports only %s",
log.Warnf("skipping nameserver %s with type %s, this peer supports only %s",
ns.IP.String(), ns.NSType.String(), nbdns.UDPNameServerType.String())
continue
}
@@ -321,7 +347,7 @@ func (s *DefaultServer) buildUpstreamHandlerUpdate(nameServerGroups []*nbdns.Nam
// reapply DNS settings, but it not touch the original configuration and serial number
// because it is temporal deactivation until next try
//
// after some period defined by upstream it trys to reactivate self by calling this hook
// after some period defined by upstream it tries to reactivate self by calling this hook
// everything we need here is just to re-apply current configuration because it already
// contains this upstream settings (temporal deactivation not removed it)
handler.deactivate, handler.reactivate = s.upstreamCallbacks(nsGroup, handler)

View File

@@ -19,6 +19,6 @@ func TestGetServerDns(t *testing.T) {
}
if srvB != srv {
t.Errorf("missmatch dns instances")
t.Errorf("mismatch dns instances")
}
}

View File

@@ -593,7 +593,8 @@ func TestDNSPermanent_updateHostDNS_emptyUpstream(t *testing.T) {
defer wgIFace.Close()
var dnsList []string
dnsServer := NewDefaultServerPermanentUpstream(context.Background(), wgIFace, dnsList)
dnsConfig := nbdns.Config{}
dnsServer := NewDefaultServerPermanentUpstream(context.Background(), wgIFace, dnsList, dnsConfig, nil)
err = dnsServer.Initialize()
if err != nil {
t.Errorf("failed to initialize DNS server: %v", err)
@@ -616,8 +617,8 @@ func TestDNSPermanent_updateUpstream(t *testing.T) {
t.Fatal("failed to initialize wg interface")
}
defer wgIFace.Close()
dnsServer := NewDefaultServerPermanentUpstream(context.Background(), wgIFace, []string{"8.8.8.8"})
dnsConfig := nbdns.Config{}
dnsServer := NewDefaultServerPermanentUpstream(context.Background(), wgIFace, []string{"8.8.8.8"}, dnsConfig, nil)
err = dnsServer.Initialize()
if err != nil {
t.Errorf("failed to initialize DNS server: %v", err)
@@ -708,8 +709,8 @@ func TestDNSPermanent_matchOnly(t *testing.T) {
t.Fatal("failed to initialize wg interface")
}
defer wgIFace.Close()
dnsServer := NewDefaultServerPermanentUpstream(context.Background(), wgIFace, []string{"8.8.8.8"})
dnsConfig := nbdns.Config{}
dnsServer := NewDefaultServerPermanentUpstream(context.Background(), wgIFace, []string{"8.8.8.8"}, dnsConfig, nil)
err = dnsServer.Initialize()
if err != nil {
t.Errorf("failed to initialize DNS server: %v", err)

View File

@@ -118,7 +118,7 @@ func (u *upstreamResolver) getClientPrivate() *dns.Client {
}
func (u *upstreamResolver) stop() {
log.Debugf("stoping serving DNS for upstreams %s", u.upstreamServers)
log.Debugf("stopping serving DNS for upstreams %s", u.upstreamServers)
u.cancel()
}

View File

@@ -195,12 +195,13 @@ func (e *Engine) Start() error {
var routes []*route.Route
if runtime.GOOS == "android" {
routes, err = e.readInitialSettings()
var dnsConfig *nbdns.Config
routes, dnsConfig, err = e.readInitialSettings()
if err != nil {
return err
}
if e.dnsServer == nil {
e.dnsServer = dns.NewDefaultServerPermanentUpstream(e.ctx, e.wgInterface, e.mobileDep.HostDNSAddresses)
e.dnsServer = dns.NewDefaultServerPermanentUpstream(e.ctx, e.wgInterface, e.mobileDep.HostDNSAddresses, *dnsConfig, e.mobileDep.NetworkChangeListener)
go e.mobileDep.DnsReadyListener.OnReady()
}
} else {
@@ -214,17 +215,16 @@ func (e *Engine) Start() error {
}
}
log.Debugf("Initial routes contain %d routes", len(routes))
e.routeManager = routemanager.NewManager(e.ctx, e.config.WgPrivateKey.PublicKey().String(), e.wgInterface, e.statusRecorder, routes)
e.mobileDep.RouteListener.SetInterfaceIP(wgAddr)
e.routeManager.SetRouteChangeListener(e.mobileDep.RouteListener)
e.mobileDep.NetworkChangeListener.SetInterfaceIP(wgAddr)
e.routeManager.SetRouteChangeListener(e.mobileDep.NetworkChangeListener)
switch runtime.GOOS {
case "android":
err = e.wgInterface.CreateOnAndroid(iface.MobileIFaceArguments{
Routes: e.routeManager.InitialRouteRange(),
Dns: e.dnsServer.DnsIP(),
Routes: e.routeManager.InitialRouteRange(),
Dns: e.dnsServer.DnsIP(),
SearchDomains: e.dnsServer.SearchDomains(),
})
case "ios":
err = e.wgInterface.CreateOniOS(e.mobileDep.FileDescriptor)
@@ -724,8 +724,9 @@ func toDNSConfig(protoDNSConfig *mgmProto.DNSConfig) nbdns.Config {
for _, nsGroup := range protoDNSConfig.GetNameServerGroups() {
dnsNSGroup := &nbdns.NameServerGroup{
Primary: nsGroup.GetPrimary(),
Domains: nsGroup.GetDomains(),
Primary: nsGroup.GetPrimary(),
Domains: nsGroup.GetDomains(),
SearchDomainsEnabled: nsGroup.GetSearchDomainsEnabled(),
}
for _, ns := range nsGroup.GetNameServers() {
dnsNS := nbdns.NameServer{
@@ -1060,13 +1061,14 @@ func (e *Engine) close() {
}
}
func (e *Engine) readInitialSettings() ([]*route.Route, error) {
func (e *Engine) readInitialSettings() ([]*route.Route, *nbdns.Config, error) {
netMap, err := e.mgmClient.GetNetworkMap()
if err != nil {
return nil, err
return nil, nil, err
}
routes := toRoutes(netMap.GetRoutes())
return routes, nil
dnsCfg := toDNSConfig(netMap.GetDNSConfig())
return routes, &dnsCfg, nil
}
func findIPFromInterfaceName(ifaceName string) (net.IP, error) {

View File

@@ -1039,10 +1039,11 @@ func startManagement(dataDir string) (*grpc.Server, string, error) {
return nil, "", err
}
s := grpc.NewServer(grpc.KeepaliveEnforcementPolicy(kaep), grpc.KeepaliveParams(kasp))
store, err := server.NewFileStore(config.Datadir, nil)
store, err := server.NewStoreFromJson(config.Datadir, nil)
if err != nil {
log.Fatalf("failed creating a store: %s: %v", config.Datadir, err)
return nil, "", err
}
peersUpdateManager := server.NewPeersUpdateManager()
eventStore := &activity.InMemoryEventStore{}
if err != nil {

View File

@@ -0,0 +1,7 @@
package listener
// NetworkChangeListener is a callback interface for mobile system
type NetworkChangeListener interface {
// OnNetworkChanged invoke when network settings has been changed
OnNetworkChanged()
}

View File

@@ -2,19 +2,19 @@ package internal
import (
"github.com/netbirdio/netbird/client/internal/dns"
"github.com/netbirdio/netbird/client/internal/routemanager"
"github.com/netbirdio/netbird/client/internal/listener"
"github.com/netbirdio/netbird/client/internal/stdnet"
"github.com/netbirdio/netbird/iface"
)
// MobileDependency collect all dependencies for mobile platform
type MobileDependency struct {
TunAdapter iface.TunAdapter
IFaceDiscover stdnet.ExternalIFaceDiscover
RouteListener routemanager.RouteListener
HostDNSAddresses []string
DnsReadyListener dns.ReadyListener
DnsManager dns.IosDnsManager
FileDescriptor int32
InterfaceName string
TunAdapter iface.TunAdapter
IFaceDiscover stdnet.ExternalIFaceDiscover
NetworkChangeListener listener.NetworkChangeListener
HostDNSAddresses []string
DnsReadyListener dns.ReadyListener
DnsManager dns.IosDnsManager
FileDescriptor int32
InterfaceName string
}

View File

@@ -119,7 +119,7 @@ func (c *clientNetwork) getBestRouteFromStatuses(routePeerStatuses map[string]ro
log.Warnf("the network %s has not been assigned a routing peer as no peers from the list %s are currently connected", c.network, peers)
} else if chosen != currID {
log.Infof("new chosen route is %s with peer %s with score %d", chosen, c.routes[chosen].Peer, chosenScore)
log.Infof("new chosen route is %s with peer %s with score %d for network %s", chosen, c.routes[chosen].Peer, chosenScore, c.network)
}
return chosen

View File

@@ -7,6 +7,7 @@ import (
log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/client/internal/listener"
"github.com/netbirdio/netbird/client/internal/peer"
"github.com/netbirdio/netbird/iface"
"github.com/netbirdio/netbird/route"
@@ -16,7 +17,7 @@ import (
// Manager is a route manager interface
type Manager interface {
UpdateRoutes(updateSerial uint64, newRoutes []*route.Route) error
SetRouteChangeListener(listener RouteListener)
SetRouteChangeListener(listener listener.NetworkChangeListener)
InitialRouteRange() []string
Stop()
}
@@ -96,7 +97,7 @@ func (m *DefaultManager) UpdateRoutes(updateSerial uint64, newRoutes []*route.Ro
}
// SetRouteChangeListener set RouteListener for route change notifier
func (m *DefaultManager) SetRouteChangeListener(listener RouteListener) {
func (m *DefaultManager) SetRouteChangeListener(listener listener.NetworkChangeListener) {
m.notifier.setListener(listener)
}
@@ -155,7 +156,7 @@ func (m *DefaultManager) classifiesRoutes(newRoutes []*route.Route) (map[string]
// if prefix is too small, lets assume is a possible default route which is not yet supported
// we skip this route management
if newRoute.Network.Bits() < 7 {
log.Errorf("this agent version: %s, doesn't support default routes, received %s, skiping this route",
log.Errorf("this agent version: %s, doesn't support default routes, received %s, skipping this route",
version.NetbirdVersion(), newRoute.Network)
continue
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"github.com/netbirdio/netbird/client/internal/listener"
"github.com/netbirdio/netbird/iface"
"github.com/netbirdio/netbird/route"
)
@@ -32,7 +33,7 @@ func (m *MockManager) Start(ctx context.Context, iface *iface.WGIface) {
}
// SetRouteChangeListener mock implementation of SetRouteChangeListener from Manager interface
func (m *MockManager) SetRouteChangeListener(listener RouteListener) {
func (m *MockManager) SetRouteChangeListener(listener listener.NetworkChangeListener) {
}

View File

@@ -135,7 +135,8 @@ func (n *nftablesManager) RestoreOrCreateContainers() error {
}
for _, table := range tables {
if table.Name == "filter" {
if table.Name == "filter" && table.Family == nftables.TableFamilyIPv4 {
log.Debugf("nftables: found filter table for ipv4")
n.filterTable = table
continue
}
@@ -486,7 +487,7 @@ func (n *nftablesManager) RemoveRoutingRules(pair routerPair) error {
if len(n.rules) == 2 && n.defaultForwardRules[0] != nil {
err := n.eraseDefaultForwardRule()
if err != nil {
log.Errorf("failed to delte default fwd rule: %s", err)
log.Errorf("failed to delete default fwd rule: %s", err)
}
}

View File

@@ -2,37 +2,34 @@ package routemanager
import (
"sort"
"strings"
"sync"
log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/route"
)
// RouteListener is a callback interface for mobile system
type RouteListener interface {
// OnNewRouteSetting invoke when new route setting has been arrived
OnNewRouteSetting(string)
SetInterfaceIP(string)
}
// type RouteListener interface {
// // OnNewRouteSetting invoke when new route setting has been arrived
// OnNewRouteSetting(string)
// SetInterfaceIP(string)
// }
type notifier struct {
initialRouteRangers []string
routeRangers []string
routeListener RouteListener
routeListenerMux sync.Mutex
listener listener.NetworkChangeListener
listenerMux sync.Mutex
}
func newNotifier() *notifier {
return &notifier{}
}
func (n *notifier) setListener(listener RouteListener) {
n.routeListenerMux.Lock()
defer n.routeListenerMux.Unlock()
n.routeListener = listener
func (n *notifier) setListener(listener listener.NetworkChangeListener) {
n.listenerMux.Lock()
defer n.listenerMux.Unlock()
n.listener = listener
}
func (n *notifier) setInitialClientRoutes(clientRoutes []*route.Route) {
@@ -63,16 +60,16 @@ func (n *notifier) onNewRoutes(idMap map[string][]*route.Route) {
}
func (n *notifier) notify() {
n.routeListenerMux.Lock()
defer n.routeListenerMux.Unlock()
if n.routeListener == nil {
n.listenerMux.Lock()
defer n.listenerMux.Unlock()
if n.listener == nil {
return
}
go func(l RouteListener) {
go func(l listener.NetworkChangeListener) {
log.Debugf("notifying route listener with route ranges: %s", strings.Join(n.routeRangers, ","))
l.OnNewRouteSetting(strings.Join(n.routeRangers, ","))
}(n.routeListener)
l.OnNetworkChanged(strings.Join(n.routeRangers, ","))
}(n.listener)
}
func (n *notifier) hasDiff(a []string, b []string) bool {