feat(ntp): use ntp server from dhcp info

This commit is contained in:
Siyuan Miao 2025-02-15 19:48:12 +01:00
parent 951173ba19
commit ec92506452
4 changed files with 82 additions and 12 deletions

1
go.mod
View File

@ -14,6 +14,7 @@ require (
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/gwatts/rootcerts v0.0.0-20240401182218-3ab9db955caf github.com/gwatts/rootcerts v0.0.0-20240401182218-3ab9db955caf
github.com/hanwen/go-fuse/v2 v2.5.1 github.com/hanwen/go-fuse/v2 v2.5.1
github.com/hashicorp/go-envparse v0.1.0
github.com/openstadia/go-usb-gadget v0.0.0-20231115171102-aebd56bbb965 github.com/openstadia/go-usb-gadget v0.0.0-20231115171102-aebd56bbb965
github.com/pion/logging v0.2.2 github.com/pion/logging v0.2.2
github.com/pion/mdns/v2 v2.0.7 github.com/pion/mdns/v2 v2.0.7

2
go.sum
View File

@ -49,6 +49,8 @@ github.com/gwatts/rootcerts v0.0.0-20240401182218-3ab9db955caf h1:JO6ISZIvEUitto
github.com/gwatts/rootcerts v0.0.0-20240401182218-3ab9db955caf/go.mod h1:5Kt9XkWvkGi2OHOq0QsGxebHmhCcqJ8KCbNg/a6+n+g= github.com/gwatts/rootcerts v0.0.0-20240401182218-3ab9db955caf/go.mod h1:5Kt9XkWvkGi2OHOq0QsGxebHmhCcqJ8KCbNg/a6+n+g=
github.com/hanwen/go-fuse/v2 v2.5.1 h1:OQBE8zVemSocRxA4OaFJbjJ5hlpCmIWbGr7r0M4uoQQ= github.com/hanwen/go-fuse/v2 v2.5.1 h1:OQBE8zVemSocRxA4OaFJbjJ5hlpCmIWbGr7r0M4uoQQ=
github.com/hanwen/go-fuse/v2 v2.5.1/go.mod h1:xKwi1cF7nXAOBCXujD5ie0ZKsxc8GGSA1rlMJc+8IJs= github.com/hanwen/go-fuse/v2 v2.5.1/go.mod h1:xKwi1cF7nXAOBCXujD5ie0ZKsxc8GGSA1rlMJc+8IJs=
github.com/hashicorp/go-envparse v0.1.0 h1:bE++6bhIsNCPLvgDZkYqo3nA+/PFI51pkrHdmPSDFPY=
github.com/hashicorp/go-envparse v0.1.0/go.mod h1:OHheN1GoygLlAkTlXLXvAdnXdZxy8JUweQ1rAXx1xnc=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=

View File

@ -1,14 +1,22 @@
package kvm package kvm
import ( import (
"bytes"
"fmt" "fmt"
"github.com/pion/mdns/v2" "net"
"golang.org/x/net/ipv4" "os"
"golang.org/x/net/ipv6" "strings"
"time"
"net" "net"
"os/exec" "os/exec"
"time" "time"
"github.com/hashicorp/go-envparse"
"github.com/pion/mdns/v2"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
"github.com/vishvananda/netlink" "github.com/vishvananda/netlink"
"github.com/vishvananda/netlink/nl" "github.com/vishvananda/netlink/nl"
) )
@ -28,27 +36,32 @@ type LocalIpInfo struct {
MAC string MAC string
} }
const (
NetIfName = "eth0"
DHCPLeaseFile = "/run/udhcpc.%s.info"
)
// setDhcpClientState sends signals to udhcpc to change it's current mode // setDhcpClientState sends signals to udhcpc to change it's current mode
// of operation. Setting active to true will force udhcpc to renew the DHCP lease. // of operation. Setting active to true will force udhcpc to renew the DHCP lease.
// Setting active to false will put udhcpc into idle mode. // Setting active to false will put udhcpc into idle mode.
func setDhcpClientState(active bool) { func setDhcpClientState(active bool) {
var signal string; var signal string
if active { if active {
signal = "-SIGUSR1" signal = "-SIGUSR1"
} else { } else {
signal = "-SIGUSR2" signal = "-SIGUSR2"
} }
cmd := exec.Command("/usr/bin/killall", signal, "udhcpc"); cmd := exec.Command("/usr/bin/killall", signal, "udhcpc")
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
fmt.Printf("network: setDhcpClientState: failed to change udhcpc state: %s\n", err) fmt.Printf("network: setDhcpClientState: failed to change udhcpc state: %s\n", err)
} }
} }
func checkNetworkState() { func checkNetworkState() {
iface, err := netlink.LinkByName("eth0") iface, err := netlink.LinkByName(NetIfName)
if err != nil { if err != nil {
fmt.Printf("failed to get eth0 interface: %v\n", err) fmt.Printf("failed to get [%s] interface: %v\n", NetIfName, err)
return return
} }
@ -64,7 +77,13 @@ func checkNetworkState() {
addrs, err := netlink.AddrList(iface, nl.FAMILY_ALL) addrs, err := netlink.AddrList(iface, nl.FAMILY_ALL)
if err != nil { if err != nil {
fmt.Printf("failed to get addresses for eth0: %v\n", err) fmt.Printf("failed to get addresses for [%s]: %v\n", NetIfName, err)
}
// If the link is going down, put udhcpc into idle mode.
// If the link is coming back up, activate udhcpc and force it to renew the lease.
if newState.Up != networkState.Up {
setDhcpClientState(newState.Up)
} }
// If the link is going down, put udhcpc into idle mode. // If the link is going down, put udhcpc into idle mode.
@ -144,6 +163,39 @@ func startMDNS() error {
return nil return nil
} }
func getNTPServersFromDHCPInfo() ([]string, error) {
buf, err := os.ReadFile(fmt.Sprintf(DHCPLeaseFile, NetIfName))
if err != nil {
// do not return error if file does not exist
if os.IsNotExist(err) {
return nil, nil
}
return nil, fmt.Errorf("failed to load udhcpc info: %w", err)
}
// parse udhcpc info
env, err := envparse.Parse(bytes.NewReader(buf))
if err != nil {
return nil, fmt.Errorf("failed to parse udhcpc info: %w", err)
}
val, ok := env["ntpsrv"]
if !ok {
return nil, nil
}
var servers []string
for _, server := range strings.Fields(val) {
if net.ParseIP(server) == nil {
fmt.Printf("invalid NTP server IP: %s, ignoring ... \n", server)
}
servers = append(servers, server)
}
return servers, nil
}
func init() { func init() {
updates := make(chan netlink.LinkUpdate) updates := make(chan netlink.LinkUpdate)
done := make(chan struct{}) done := make(chan struct{})
@ -162,7 +214,7 @@ func init() {
for { for {
select { select {
case update := <-updates: case update := <-updates:
if update.Link.Attrs().Name == "eth0" { if update.Link.Attrs().Name == NetIfName {
fmt.Printf("link update: %+v\n", update) fmt.Printf("link update: %+v\n", update)
checkNetworkState() checkNetworkState()
} }

21
ntp.go
View File

@ -12,9 +12,19 @@ import (
) )
var timeSynced = false var timeSynced = false
var defaultNTPServers = []string{
"time.cloudflare.com",
"time.apple.com",
}
func TimeSyncLoop() { func TimeSyncLoop() {
for { for {
if !networkState.Up {
fmt.Printf("Waiting for network to come up\n")
time.Sleep(3 * time.Second)
continue
}
fmt.Println("Syncing system time") fmt.Println("Syncing system time")
start := time.Now() start := time.Now()
err := SyncSystemTime() err := SyncSystemTime()
@ -41,10 +51,15 @@ func SyncSystemTime() (err error) {
} }
func queryNetworkTime() (*time.Time, error) { func queryNetworkTime() (*time.Time, error) {
ntpServers := []string{ ntpServers, err := getNTPServersFromDHCPInfo()
"time.cloudflare.com", if err != nil {
"time.apple.com", fmt.Printf("failed to get NTP servers from DHCP info: %v\n", err)
} }
if ntpServers == nil {
ntpServers = defaultNTPServers
}
for _, server := range ntpServers { for _, server := range ntpServers {
now, err := queryNtpServer(server, 2*time.Second) now, err := queryNtpServer(server, 2*time.Second)
if err == nil { if err == nil {