Skip to content

Commit

Permalink
pillar/nireconciler: resolve ntp servers
Browse files Browse the repository at this point in the history
continuously in the background so that updated
IP addresses can be used if the edge application is
restarted (from controller)

Signed-off-by: Christoph Ostarek <christoph@zededa.com>
  • Loading branch information
christoph-zededa committed Dec 2, 2024
1 parent ee1fe07 commit 94174f9
Show file tree
Hide file tree
Showing 7 changed files with 250 additions and 173 deletions.
31 changes: 30 additions & 1 deletion pkg/pillar/cmd/zedrouter/networkinstance.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ import (
"net"
"strings"

"github.com/lf-edge/eve/pkg/pillar/devicenetwork"
"github.com/lf-edge/eve/pkg/pillar/nireconciler"
"github.com/lf-edge/eve/pkg/pillar/nistate"
"github.com/lf-edge/eve/pkg/pillar/types"
"github.com/lf-edge/eve/pkg/pillar/utils/generics"
"github.com/lf-edge/eve/pkg/pillar/utils/netutils"
uuid "github.com/satori/go.uuid"
"github.com/sirupsen/logrus"
)

// Return arguments describing network instance config as required by NIStateCollector
Expand Down Expand Up @@ -103,6 +105,33 @@ func (z *zedrouter) getNIPortConfig(
if port == nil {
continue
}
ntpServerIPs, ntpServerDomainsOrIPs := types.GetNTPServers(*z.deviceNetworkStatus, port.IfName)
ntpServers := make([]net.IP, 0, len(ntpServerDomainsOrIPs))
for _, ntpServer := range ntpServerDomainsOrIPs {
ip := net.ParseIP(ntpServer)
if ip != nil {
ntpServers = append(ntpServers, ip)
continue
}
dnsResponses, err := devicenetwork.ResolveWithPortsLambda(
ntpServer,
*z.deviceNetworkStatus,
devicenetwork.ResolveCacheWrap(func(domain string, dnsServerIP, srcIP net.IP) ([]devicenetwork.DNSResponse, error) {
return devicenetwork.ResolveWithSrcIP(domain, dnsServerIP, srcIP)
}),
)
if err != nil {
logrus.Warnf("could not resolve '%s': %v", ntpServer, err)
}

for _, dnsResponse := range dnsResponses {
ntpServers = append(ntpServers, dnsResponse.IP)
}
}

ntpServers = append(ntpServers, ntpServerIPs...)
generics.FilterDuplicatesFn(ntpServers, netutils.EqualIPs)

portConfigs = append(portConfigs, nireconciler.Port{
LogicalLabel: port.Logicallabel,
SharedLabels: port.SharedLabels,
Expand All @@ -111,7 +140,7 @@ func (z *zedrouter) getNIPortConfig(
MTU: port.MTU,
DhcpType: port.Dhcp,
DNSServers: types.GetDNSServers(*z.deviceNetworkStatus, port.IfName),
NTPServers: types.GetNTPServers(*z.deviceNetworkStatus, port.IfName),
NTPServers: ntpServers,
})
}
return portConfigs
Expand Down
36 changes: 36 additions & 0 deletions pkg/pillar/devicenetwork/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package devicenetwork

import (
"fmt"
"math"
"net"
"os"
"path/filepath"
Expand Down Expand Up @@ -96,6 +97,41 @@ func ResolveWithSrcIP(domain string, dnsServerIP net.IP, srcIP net.IP) ([]DNSRes
return response, nil
}

type cachedDNSResponses struct {
dnsResponses []DNSResponse
validUntil time.Time
}

var resolveCache = map[string]cachedDNSResponses{}

// ResolveCacheWrap wraps around a resolve func (e.g. ResolveWithSrcIP) and caches DNS entries
func ResolveCacheWrap(resolve func(string, net.IP, net.IP) ([]DNSResponse, error)) func(domain string, dnsServerIP net.IP, srcIP net.IP) ([]DNSResponse, error) {
return func(domain string, dnsServerIP net.IP, srcIP net.IP) ([]DNSResponse, error) {

dnsResponses, found := resolveCache[domain]
if !found || !dnsResponses.validUntil.After(time.Now()) {
dnsResponses, err := resolve(domain, dnsServerIP, srcIP)
if err == nil {
minValidUntil := uint32(math.MaxUint32)
for _, dnsResponse := range dnsResponses {
if dnsResponse.TTL < uint32(minValidUntil) {
minValidUntil = dnsResponse.TTL
}
}
validUntil := time.Now().Add(time.Duration(minValidUntil * uint32(time.Second)))
resolveCache[domain] = cachedDNSResponses{
dnsResponses: dnsResponses,
validUntil: validUntil,
}
}

return dnsResponses, err
}

return dnsResponses.dnsResponses, nil
}
}

// ResolveWithPortsLambda resolves a domain by using source IPs and dns servers from DeviceNetworkStatus
// As a resolver func ResolveWithSrcIP can be used
func ResolveWithPortsLambda(domain string,
Expand Down
34 changes: 34 additions & 0 deletions pkg/pillar/devicenetwork/dns_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,3 +244,37 @@ func TestResolveWithPortsLambdaWithErrors(t *testing.T) {
t.Errorf("expected empty response, but got: %+v", res)
}
}

func TestResolveCacheWrap(t *testing.T) {
t.Parallel()
called := 0

cw := devicenetwork.ResolveCacheWrap(func(domain string, dnsServerIP, srcIP net.IP) ([]devicenetwork.DNSResponse, error) {
called++
return []devicenetwork.DNSResponse{
{
IP: []byte{127, 0, 0, 1},
TTL: 2,
},
}, nil
})

cw("localhost", net.IP{8, 8, 8, 8}, net.IP{0, 0, 0, 0})

if called != 1 {
t.Fatalf("resolver func should have been called once, but called=%d", called)
}

cw("localhost", net.IP{8, 8, 8, 8}, net.IP{0, 0, 0, 0})

if called != 1 {
t.Fatalf("resolver func should have been called once, but called=%d", called)
}

time.Sleep(5 * time.Second)
cw("localhost", net.IP{8, 8, 8, 8}, net.IP{0, 0, 0, 0})
if called != 2 {
t.Fatalf("resolver func should have been called twice, but called=%d", called)
}

}
36 changes: 6 additions & 30 deletions pkg/pillar/nireconciler/linux_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"encoding/base64"
"fmt"
"net"
"os"
"strings"
"syscall"

Expand Down Expand Up @@ -1095,30 +1096,6 @@ func (r *LinuxNIReconciler) getIntendedMetadataSrvCfg(niID uuid.UUID) (items []d
return items
}

func (r *LinuxNIReconciler) resolveNTPServers(ntpServers []string) []net.IP {
ret := make([]net.IP, 0)
for _, ntpServer := range ntpServers {
ip := net.ParseIP(ntpServer)
if ip != nil {
ret = append(ret, ip)
continue
}

// TODO: discuss with Milan how to use:
// devicenetwork.ResolveWithPortsLambda(ntpServer, XXX, devicenetwork.ResolveWithSrcIP)
// TODO: add timeout or resolve in background
ips, err := net.LookupIP(ntpServer)
if err != nil {
r.log.Warnf("could not lookup '%s': %v, skipping", ntpServer, err)
continue
}
ret = append(ret, ips...)
}

ret = generics.FilterDuplicatesFn(ret, netutils.EqualIPs)
return ret
}

func (r *LinuxNIReconciler) getIntendedDnsmasqCfg(niID uuid.UUID) (items []dg.Item) {
ni := r.nis[niID]
if ni.config.Type == types.NetworkInstanceTypeSwitch {
Expand Down Expand Up @@ -1154,15 +1131,14 @@ func (r *LinuxNIReconciler) getIntendedDnsmasqCfg(niID uuid.UUID) (items []dg.It
}
// Combine NTP servers assigned to the port(s) together with those statically
// configured for the network instance.
var ntpServers []string
ntpServerIPs := make([]net.IP, 0)
for _, port := range ni.bridge.Ports {
ntpServers = append(ntpServers, port.NTPServers...)
ntpServerIPs = append(ntpServerIPs, port.NTPServers...)
}
if ni.config.NtpServers != nil {
ntpServers = append(ntpServers, ni.config.NtpServers...)
generics.FilterDuplicatesFn(ntpServerIPs, netutils.EqualIPs)
for _, ntpServer := range ntpServerIPs {
fmt.Fprintf(os.Stderr, "AAAAA dnsmasq ntpServer: %+v\n", ntpServer.String())
}
ntpServers = generics.FilterDuplicates(ntpServers)
ntpServerIPs := r.resolveNTPServers(ntpServers)
var propagateRoutes []generic.IPRoute
// Use DHCP to propagate host routes towards user-configured NTP and DNS servers.
if bridgeIP != nil {
Expand Down
Loading

0 comments on commit 94174f9

Please sign in to comment.