diff --git a/README.md b/README.md index 43a868d..ce4b440 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ NTPal is an in-development, incomplete, and rough around the edges implementatio ## Code Credits -The code for NTPal is _very_ similar to pseudocode provided in the [NTPv4 RFC](https://datatracker.ietf.org/doc/html/rfc5905), which is nicely documented and made for a good rewriting experience. Certain changes were made based on more recent additions to projects such as [ntpd](https://github.com/ntp-project/ntp), [chrony](https://github.com/mlichvar/chrony), and others. Additionally, some care was taken to map the program's requirements into better-fit Golang structures, but there's still some room for improvement (especially regarding race conditions). +The code for NTPal is _very_ similar to pseudocode provided in the [NTPv4 RFC](https://datatracker.ietf.org/doc/html/rfc5905), which is nicely documented and made for a good rewriting experience. Certain changes were made based on more recent additions to projects such as [ntpd](https://github.com/ntp-project/ntp), [chrony](https://github.com/mlichvar/chrony), and others. Additionally, some care was taken to map the program's requirements into better-fit Golang structures, but there's room for improvement (especially regarding race conditions). ## Usage @@ -25,6 +25,12 @@ NTPal uses a configuration format similar to the [standard `ntpd` config](https: - `server
[key _key_] [burst] [iburst] [version _version_] [prefer] [minpoll _minpoll_] [maxpoll _maxpoll_]` - `driftfile ` +Some environment variables are also available to configure the application's runtime and logging: + +- `SYMMETRIC`: Set to "1" to allow symmetric active servers to connect. +- `INFO`: Set to "1" to print periodic system information logs. +- `DEBUG`: Set to "1" to print timeseries clock statistics meant for graphing. + ### NTPal — Query NTPal supports a simpler "query" flag to simply obtain your device's time offset from an NTP server. Accessible via `--query` or `-q`, 5 messages are sent in an attempt to obtain the best measurement possible. This command functions almost identically to the `sntp` command shipped with OSX, though NTPal has far less functionality. diff --git a/fly.toml b/fly.toml index f701718..96f046e 100644 --- a/fly.toml +++ b/fly.toml @@ -8,7 +8,6 @@ kill_timeout = 5 NTP_PORT = "123" NTP_HOST = "fly-global-services" REPORT_PORT = "8080" - ENABLED = "1" INFO = "1" [[mounts]] diff --git a/internal/templates/templates/index.tmpl.html b/internal/templates/templates/index.tmpl.html index 03b6e0d..1de905b 100644 --- a/internal/templates/templates/index.tmpl.html +++ b/internal/templates/templates/index.tmpl.html @@ -289,18 +289,16 @@

According to this server,

} const { offset } = minDelayAttempt; - let serverTime = new Date((Date.now() / 1e3 + offset) * 1e3); + let serverTime = new Date(Date.now() + offset / 1e3); // Wait for the second to hit await sleep(1e3 - (serverTime % 1e3)); setInterval(() => { - const serverTime = new Date( - (Date.now() / 1e3 + offset) * 1e3 - ); + const serverTime = new Date(Date.now() + offset / 1e3); displayTime(serverTime); }, 1000); - serverTime = new Date((Date.now() / 1e3 + offset) * 1e3); + serverTime = new Date(Date.now() + offset / 1e3); displayTime(serverTime); const offsetMessage = offset < 0 ? 'ahead' : 'behind'; @@ -308,7 +306,6 @@

According to this server,

offset ).toFixed(3)} seconds`; // We do have up to microsecond precision, but it's not great... elems.offset.classList.add('done'); - elems.time.classList.add('done'); } diff --git a/pkg/ntp/ntp.go b/pkg/ntp/ntp.go index 3eb5fa8..8adb95d 100644 --- a/pkg/ntp/ntp.go +++ b/pkg/ntp/ntp.go @@ -18,7 +18,7 @@ const PORT = 123 // NTP port number const VERSION byte = 4 // NTP version number const TOLERANCE = 15e-6 //frequency tolerance PHI (s/s) const MINPOLL int8 = 6 //minimum poll exponent (64 s) -const MAXPOLL int8 = 17 // maximum poll exponent (36 h) +const MAXPOLL int8 = 16 // maximum poll exponent (36 h) const MAXDISP float64 = 16 // maximum dispersion (16 s) const MINDISP = 0.005 // minimum dispersion increment (s) const NOSYNC byte = 0x3 // leap unsync @@ -696,8 +696,10 @@ func (system *NTPSystem) sendPoll(association *Association) { if err != nil { log.Fatal("Invalid address: ", association.hostname) } + if association.srcaddr == addr { + hpoll++ + } association.srcaddr = addr - hpoll++ association.unreach = -1 } association.unreach++ @@ -768,9 +770,20 @@ func (system *NTPSystem) receive(packet ReceivePacket) *TransmitPacket { // else if (auth == A_ERROR) // fast_xmit(r, M_SERV, A_CRYPTO); // return; /* M_SERV packet sent */ + case NEWPS: + if !isSymmetricEnabled() { + return nil + } + + association = &Association{ + ReceivePacket: packet, + hmode: SYMMETRIC_PASSIVE, + ephemeral: true, + } + system.clear(association, INIT) + system.associations = append(system.associations, association) case PROC: - break - case DSCRD: + default: return nil } @@ -1086,6 +1099,9 @@ func (system *NTPSystem) clockFilter(association *Association, offset float64, d for i := NSTAGE - 1; i > 0; i-- { association.f[i] = association.f[i-1] association.f[i].disp += PHI * (float64(system.clock.t) - association.t) + if association.f[i].disp > MAXDISP { + association.f[i].disp = MAXDISP + } f[i] = association.f[i] } association.f[0].t = system.clock.t @@ -1107,7 +1123,7 @@ func (system *NTPSystem) clockFilter(association *Association, offset float64, d association.disp = 0 association.jitter = 0 for i := NSTAGE - 1; i >= 0; i-- { - association.disp += f[i].disp / math.Pow(2, float64(i+1)) + association.disp = 0.5 * (association.disp + f[i].disp) if i < m { association.jitter += math.Pow((f[0].offset - f[i].offset), 2) } @@ -1443,8 +1459,7 @@ func (system *NTPSystem) clockUpdate(association *Association) { } system.reftime = association.Reftime system.rootdelay = association.Rootdelay + association.delay - dtemp := math.Sqrt(math.Pow(association.jitter, 2) + math.Pow(system.jitter, 2)) - dtemp += math.Max(association.disp+PHI*(float64(system.clock.t)-association.t)+ + dtemp := math.Max(association.disp+system.jitter+PHI*(float64(system.clock.t)-association.t)+ math.Abs(association.offset), MINDISP) system.rootdisp = association.Rootdisp + dtemp /* diff --git a/pkg/ntp/system.go b/pkg/ntp/system.go index 6f29305..113953e 100644 --- a/pkg/ntp/system.go +++ b/pkg/ntp/system.go @@ -24,11 +24,10 @@ func stepTime(offset float64) { Sec -= unixEraOffset info("CURRENT:", NTPTimestampToTime(systemTime), "STEPPING TO:", NTPTimestampToTime(ntpTime), "OFFSET WAS:", offset) - if shouldSetTime() { - err := settimeofday.Settimeofday(Sec, Usec) - if err != nil { - info("SETTIMEOFDAYERR:", err) - } + + err := settimeofday.Settimeofday(Sec, Usec) + if err != nil { + info("SETTIMEOFDAYERR:", err) } } @@ -50,10 +49,8 @@ func adjustTime(offset float64) { Usec += 1e6 } - if shouldSetTime() { - err := adjtime.Adjtime(Sec, Usec) - if err != nil { - info("ADJTIME ERROR:", err, "offset:", offset) - } + err := adjtime.Adjtime(Sec, Usec) + if err != nil { + info("ADJTIME ERROR:", err, "offset:", offset) } } diff --git a/pkg/ntp/utils.go b/pkg/ntp/utils.go index f662550..2ef5eef 100644 --- a/pkg/ntp/utils.go +++ b/pkg/ntp/utils.go @@ -5,10 +5,6 @@ import ( "os" ) -func shouldSetTime() bool { - return os.Getenv("ENABLED") == "1" -} - func info(args ...any) { if isInfo() { fmt.Println(args...) @@ -28,3 +24,7 @@ func isInfo() bool { func isDebug() bool { return os.Getenv("DEBUG") == "1" } + +func isSymmetricEnabled() bool { + return os.Getenv("SYMMETRIC") == "1" +}