mirror of
https://github.com/soypat/lneto.git
synced 2026-08-31 11:59:05 +00:00
NTP up and running
This commit is contained in:
+73
-12
@@ -11,7 +11,6 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -24,6 +23,7 @@ import (
|
|||||||
"github.com/soypat/lneto/internal/ltesto"
|
"github.com/soypat/lneto/internal/ltesto"
|
||||||
"github.com/soypat/lneto/internet"
|
"github.com/soypat/lneto/internet"
|
||||||
"github.com/soypat/lneto/internet/pcap"
|
"github.com/soypat/lneto/internet/pcap"
|
||||||
|
"github.com/soypat/lneto/ntp"
|
||||||
)
|
)
|
||||||
|
|
||||||
var softRand = time.Now().Unix()
|
var softRand = time.Now().Unix()
|
||||||
@@ -43,11 +43,13 @@ func run() (err error) {
|
|||||||
flagUseHTTP = false
|
flagUseHTTP = false
|
||||||
flagHostToResolve = ""
|
flagHostToResolve = ""
|
||||||
flagRequestedIP = ""
|
flagRequestedIP = ""
|
||||||
|
flagDoNTP = false
|
||||||
)
|
)
|
||||||
flag.StringVar(&flagInterface, "i", flagInterface, "Interface to use. Either tap* or the name of an existing interface to bridge to.")
|
flag.StringVar(&flagInterface, "i", flagInterface, "Interface to use. Either tap* or the name of an existing interface to bridge to.")
|
||||||
flag.BoolVar(&flagUseHTTP, "http", flagUseHTTP, "Use HTTP tap interface.")
|
flag.BoolVar(&flagUseHTTP, "http", flagUseHTTP, "Use HTTP tap interface.")
|
||||||
flag.StringVar(&flagHostToResolve, "host", flagHostToResolve, "Hostname to resolve via DNS.")
|
flag.StringVar(&flagHostToResolve, "host", flagHostToResolve, "Hostname to resolve via DNS.")
|
||||||
flag.StringVar(&flagRequestedIP, "addr", flagRequestedIP, "IP address to request via DHCP.")
|
flag.StringVar(&flagRequestedIP, "addr", flagRequestedIP, "IP address to request via DHCP.")
|
||||||
|
flag.BoolVar(&flagDoNTP, "ntp", flagDoNTP, "Do NTP round and print result time")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
fmt.Println("softrand", softRand)
|
fmt.Println("softrand", softRand)
|
||||||
_, err = dns.NewName(flagHostToResolve)
|
_, err = dns.NewName(flagHostToResolve)
|
||||||
@@ -80,34 +82,41 @@ func run() (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
brHW := nicHW
|
brHW := nicHW
|
||||||
brHW[5] += byte(softRand)%128 + 1 // We'll be using a similar HW address but with NIC specific identifier modified.
|
brHW[4]++
|
||||||
mtu, err := iface.MTU()
|
mtu, err := iface.MTU()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
nicAddr, err := iface.IPMask()
|
nicAddr, err := iface.IPMask()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Println("NIC hardware address:", net.HardwareAddr(nicHW[:]).String(), "bridgeHW:", net.HardwareAddr(brHW[:]).String(), "mtu:", mtu, "addr:", nicAddr.String())
|
fmt.Println("NIC hardware address:", net.HardwareAddr(nicHW[:]).String(), "bridgeHW:", net.HardwareAddr(brHW[:]).String(), "mtu:", mtu, "addr:", nicAddr.String())
|
||||||
var stack Stack
|
var stack Stack
|
||||||
|
|
||||||
err = stack.Reset(brHW, netip.AddrFrom4([4]byte{}), uint16(mtu))
|
err = stack.Reset(brHW, netip.AddrFrom4([4]byte{}), uint16(mtu))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = stack.BeginDHCPRequest([4]byte{192, 168, 1, 199})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
buf := make([]byte, mtu)
|
buf := make([]byte, mtu)
|
||||||
lastAction := time.Now()
|
lastAction := time.Now()
|
||||||
const (
|
const (
|
||||||
stateDHCP = iota
|
stateDHCP = iota
|
||||||
stateInitARP
|
stateInitARP
|
||||||
|
stateDNSNTP
|
||||||
|
stateNTP
|
||||||
stateDNS
|
stateDNS
|
||||||
stateDone
|
stateDone
|
||||||
)
|
)
|
||||||
|
|
||||||
|
err = stack.BeginDHCPRequest([4]byte{192, 168, 1, 96})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
state := stateDHCP
|
state := stateDHCP
|
||||||
|
prevState := state
|
||||||
for {
|
for {
|
||||||
switch state {
|
switch state {
|
||||||
case stateDHCP:
|
case stateDHCP:
|
||||||
@@ -128,14 +137,46 @@ func run() (err error) {
|
|||||||
router := stack.dhcp.RouterAddr()
|
router := stack.dhcp.RouterAddr()
|
||||||
hw, err := stack.ResultResolveHardwareAddress6(netip.AddrFrom4(router))
|
hw, err := stack.ResultResolveHardwareAddress6(netip.AddrFrom4(router))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
state = stateDNS
|
|
||||||
stack.link.SetGateway6(hw)
|
stack.link.SetGateway6(hw)
|
||||||
|
stack.link.SetHardwareAddr6([6]byte{0xd8, 0x5e, 0xd3, 0x43, 0x03, 0xeb})
|
||||||
|
stack.ip.SetAddr(netip.AddrFrom4([4]byte{192, 168, 1, 53}))
|
||||||
|
if flagDoNTP {
|
||||||
|
state = stateDNSNTP
|
||||||
|
err = stack.StartLookupIP("pool.ntp.org")
|
||||||
|
} else {
|
||||||
|
state = stateDNS
|
||||||
|
err = stack.StartLookupIP(flagHostToResolve)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case stateDNSNTP:
|
||||||
|
addrs, done, err := stack.ResultLookupIP()
|
||||||
|
if err == nil {
|
||||||
|
state = stateNTP
|
||||||
|
fmt.Println("START NTP")
|
||||||
|
err = stack.StartNTP(addrs[0])
|
||||||
|
} else if !done {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case stateNTP:
|
||||||
|
offset, done := stack.ResultNTP()
|
||||||
|
if done {
|
||||||
|
relative := "behind"
|
||||||
|
if offset < 0 {
|
||||||
|
relative = "ahead"
|
||||||
|
}
|
||||||
|
fmt.Println("NTP completed. You are", offset.Abs(), relative, "of the NTP server")
|
||||||
|
state = stateDNS
|
||||||
err = stack.StartLookupIP(flagHostToResolve)
|
err = stack.StartLookupIP(flagHostToResolve)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case stateDNS:
|
case stateDNS:
|
||||||
addrs, done, err := stack.ResultLookupIP()
|
addrs, done, err := stack.ResultLookupIP()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -145,6 +186,10 @@ func run() (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if prevState != state {
|
||||||
|
fmt.Println("STATE CHANGE", prevState, state)
|
||||||
|
}
|
||||||
|
prevState = state
|
||||||
|
|
||||||
clear(buf)
|
clear(buf)
|
||||||
nwrite, err := stack.Encapsulate(buf[:], 0)
|
nwrite, err := stack.Encapsulate(buf[:], 0)
|
||||||
@@ -189,6 +234,8 @@ type Stack struct {
|
|||||||
dns dns.Client
|
dns dns.Client
|
||||||
ednsopt dns.Resource
|
ednsopt dns.Resource
|
||||||
lookup dns.Message
|
lookup dns.Message
|
||||||
|
ntp ntp.Client
|
||||||
|
sysprec int8 // NTP system precision.
|
||||||
|
|
||||||
// Packet capture and top level filtering.
|
// Packet capture and top level filtering.
|
||||||
shark pcap.PacketBreakdown
|
shark pcap.PacketBreakdown
|
||||||
@@ -198,8 +245,9 @@ type Stack struct {
|
|||||||
func (s *Stack) Demux(b []byte, _ int) (err error) {
|
func (s *Stack) Demux(b []byte, _ int) (err error) {
|
||||||
s.aux, err = s.shark.CaptureEthernet(s.aux[:0], b, 0)
|
s.aux, err = s.shark.CaptureEthernet(s.aux[:0], b, 0)
|
||||||
topFrame := s.aux[len(s.aux)-1]
|
topFrame := s.aux[len(s.aux)-1]
|
||||||
isOK := topFrame.Protocol == "DHCPv4" || // Allow DHCP responses.
|
isOK := topFrame.Protocol == "DHCPv4" || // Allow DHCP, DNS and NTP responses.
|
||||||
topFrame.Protocol == "DNS" ||
|
topFrame.Protocol == "DNS" ||
|
||||||
|
topFrame.Protocol == "NTP" ||
|
||||||
topFrame.Protocol == ethernet.TypeARP // Allow ARP responses.
|
topFrame.Protocol == ethernet.TypeARP // Allow ARP responses.
|
||||||
if !isOK {
|
if !isOK {
|
||||||
return nil
|
return nil
|
||||||
@@ -269,6 +317,8 @@ func (s *Stack) Reset(mac [6]byte, addr netip.Addr, mtu uint16) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s.ip.SetLogger(slog.Default())
|
s.ip.SetLogger(slog.Default())
|
||||||
|
var timebuf [32]time.Time
|
||||||
|
s.sysprec = ntp.CalculateSystemPrecision(time.Now, timebuf[:])
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,8 +331,6 @@ func (s *Stack) StartLookupIP(host string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s.link.SetHardwareAddr6([6]byte{0xd8, 0x5e, 0xd3, 0x43, 0x03, 0xeb})
|
|
||||||
s.ip.SetAddr(netip.AddrFrom4([4]byte{192, 168, 1, 53}))
|
|
||||||
s.ednsopt.SetEDNS0(uint16(s.link.MTU())-100, 0, 0, nil)
|
s.ednsopt.SetEDNS0(uint16(s.link.MTU())-100, 0, 0, nil)
|
||||||
err = s.dns.StartResolve(uint16(softRand>>1)+1024, uint16(softRand), dns.ResolveConfig{
|
err = s.dns.StartResolve(uint16(softRand>>1)+1024, uint16(softRand), dns.ResolveConfig{
|
||||||
Questions: []dns.Question{
|
Questions: []dns.Question{
|
||||||
@@ -330,6 +378,10 @@ func (s *Stack) ResultLookupIP() ([]netip.Addr, bool, error) {
|
|||||||
return addrs, done, nil
|
return addrs, done, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Stack) ResultNTP() (time.Duration, bool) {
|
||||||
|
return s.ntp.Offset(), s.ntp.IsDone()
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Stack) BeginDHCPRequest(request [4]byte) error {
|
func (s *Stack) BeginDHCPRequest(request [4]byte) error {
|
||||||
var buf [4]byte
|
var buf [4]byte
|
||||||
rand.Read(buf[:])
|
rand.Read(buf[:])
|
||||||
@@ -337,7 +389,7 @@ func (s *Stack) BeginDHCPRequest(request [4]byte) error {
|
|||||||
err := s.dhcp.BeginRequest(xid, dhcpv4.RequestConfig{
|
err := s.dhcp.BeginRequest(xid, dhcpv4.RequestConfig{
|
||||||
RequestedAddr: request,
|
RequestedAddr: request,
|
||||||
ClientHardwareAddr: s.link.HardwareAddr6(),
|
ClientHardwareAddr: s.link.HardwareAddr6(),
|
||||||
Hostname: "lneto" + strconv.FormatInt(softRand%100, 16),
|
Hostname: "lneto",
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -351,6 +403,15 @@ func (s *Stack) BeginDHCPRequest(request [4]byte) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Stack) StartNTP(addr netip.Addr) error {
|
||||||
|
s.ntp.Reset(time.Now, s.sysprec)
|
||||||
|
var u internet.StackUDPPort
|
||||||
|
addr4 := addr.As4()
|
||||||
|
u.SetStackNode(&s.ntp, addr4[:], ntp.ServerPort)
|
||||||
|
err := s.udps.Register(&u)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Stack) StartResolveHardwareAddress6(ip netip.Addr) error {
|
func (s *Stack) StartResolveHardwareAddress6(ip netip.Addr) error {
|
||||||
if !ip.Is4() {
|
if !ip.Is4() {
|
||||||
return errors.New("unsupported or invalid IP address")
|
return errors.New("unsupported or invalid IP address")
|
||||||
|
|||||||
+104
-1
@@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/soypat/lneto/http/httpraw"
|
"github.com/soypat/lneto/http/httpraw"
|
||||||
"github.com/soypat/lneto/ipv4"
|
"github.com/soypat/lneto/ipv4"
|
||||||
"github.com/soypat/lneto/ipv6"
|
"github.com/soypat/lneto/ipv6"
|
||||||
|
"github.com/soypat/lneto/ntp"
|
||||||
"github.com/soypat/lneto/tcp"
|
"github.com/soypat/lneto/tcp"
|
||||||
"github.com/soypat/lneto/udp"
|
"github.com/soypat/lneto/udp"
|
||||||
)
|
)
|
||||||
@@ -324,10 +325,14 @@ func (pc *PacketBreakdown) CaptureUDP(dst []Frame, pkt []byte, bitOffset int) ([
|
|||||||
dst = append(dst, finfo)
|
dst = append(dst, finfo)
|
||||||
end := bitOffset + 8*octet
|
end := bitOffset + 8*octet
|
||||||
payload := ufrm.Payload()
|
payload := ufrm.Payload()
|
||||||
|
dstport := ufrm.DestinationPort()
|
||||||
|
srcport := ufrm.SourcePort()
|
||||||
if dhcpv4.PayloadIsDHCPv4(payload) {
|
if dhcpv4.PayloadIsDHCPv4(payload) {
|
||||||
dst, err = pc.CaptureDHCPv4(dst, pkt, end)
|
dst, err = pc.CaptureDHCPv4(dst, pkt, end)
|
||||||
} else if ufrm.DestinationPort() == 53 || ufrm.SourcePort() == 53 {
|
} else if dstport == dns.ServerPort || srcport == dns.ServerPort {
|
||||||
dst, err = pc.CaptureDNS(dst, pkt, end)
|
dst, err = pc.CaptureDNS(dst, pkt, end)
|
||||||
|
} else if dstport == ntp.ServerPort || srcport == ntp.ServerPort {
|
||||||
|
dst, err = pc.CaptureNTP(dst, pkt, end)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dst = append(dst, remainingFrameInfo(unknownPayloadProto, FieldClassPayload, end, octet*len(pkt)))
|
dst = append(dst, remainingFrameInfo(unknownPayloadProto, FieldClassPayload, end, octet*len(pkt)))
|
||||||
@@ -361,6 +366,24 @@ func (pc *PacketBreakdown) CaptureDNS(dst []Frame, pkt []byte, bitOffset int) ([
|
|||||||
return dst, nil
|
return dst, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pc *PacketBreakdown) CaptureNTP(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
|
||||||
|
if bitOffset%8 != 0 {
|
||||||
|
return nil, errors.New("NTP must be parsed at byte boundary")
|
||||||
|
}
|
||||||
|
ntpData := pkt[bitOffset/8:]
|
||||||
|
_, err := ntp.NewFrame(ntpData)
|
||||||
|
if err != nil {
|
||||||
|
return dst, err
|
||||||
|
}
|
||||||
|
finfo := Frame{
|
||||||
|
Protocol: "NTP",
|
||||||
|
PacketBitOffset: bitOffset,
|
||||||
|
}
|
||||||
|
finfo.Fields = append(finfo.Fields, baseNTPFields[:]...)
|
||||||
|
dst = append(dst, finfo)
|
||||||
|
return dst, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (pc *PacketBreakdown) CaptureDHCPv4(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
|
func (pc *PacketBreakdown) CaptureDHCPv4(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
|
||||||
if bitOffset%8 != 0 {
|
if bitOffset%8 != 0 {
|
||||||
return nil, errors.New("DHCP must be parsed at byte boundary")
|
return nil, errors.New("DHCP must be parsed at byte boundary")
|
||||||
@@ -954,6 +977,86 @@ var baseDHCPv4Fields = [...]FrameField{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var baseNTPFields = [...]FrameField{
|
||||||
|
{
|
||||||
|
Name: "Mode",
|
||||||
|
Class: FieldClassType,
|
||||||
|
FrameBitOffset: 0,
|
||||||
|
BitLength: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Class: FieldClassVersion,
|
||||||
|
FrameBitOffset: 3,
|
||||||
|
BitLength: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Leap Indicator",
|
||||||
|
Class: fieldClassUndefined,
|
||||||
|
FrameBitOffset: 5,
|
||||||
|
BitLength: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Stratum",
|
||||||
|
Class: fieldClassUndefined,
|
||||||
|
FrameBitOffset: 1 * octet,
|
||||||
|
BitLength: 1 * octet,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Poll",
|
||||||
|
Class: fieldClassUndefined,
|
||||||
|
FrameBitOffset: 2 * octet,
|
||||||
|
BitLength: 1 * octet,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "System Precision",
|
||||||
|
Class: fieldClassUndefined,
|
||||||
|
FrameBitOffset: 3 * octet,
|
||||||
|
BitLength: 1 * octet,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Root Delay",
|
||||||
|
Class: fieldClassUndefined,
|
||||||
|
FrameBitOffset: 4 * octet,
|
||||||
|
BitLength: 4 * octet,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Root Dispersion",
|
||||||
|
Class: fieldClassUndefined,
|
||||||
|
FrameBitOffset: 8 * octet,
|
||||||
|
BitLength: 4 * octet,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Reference ID",
|
||||||
|
Class: FieldClassText,
|
||||||
|
FrameBitOffset: 12 * octet,
|
||||||
|
BitLength: 4 * octet,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Reference Time",
|
||||||
|
Class: FieldClassText,
|
||||||
|
FrameBitOffset: 16 * octet,
|
||||||
|
BitLength: 8 * octet,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Origin Time",
|
||||||
|
Class: FieldClassText,
|
||||||
|
FrameBitOffset: 24 * octet,
|
||||||
|
BitLength: 8 * octet,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Receive Time",
|
||||||
|
Class: FieldClassText,
|
||||||
|
FrameBitOffset: 32 * octet,
|
||||||
|
BitLength: 8 * octet,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Transit Time",
|
||||||
|
Class: FieldClassText,
|
||||||
|
FrameBitOffset: 40 * octet,
|
||||||
|
BitLength: 8 * octet,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
func remainingFrameInfo(proto any, class FieldClass, pktBitOffset, pktBitLen int) Frame {
|
func remainingFrameInfo(proto any, class FieldClass, pktBitOffset, pktBitLen int) Frame {
|
||||||
return Frame{
|
return Frame{
|
||||||
Protocol: proto,
|
Protocol: proto,
|
||||||
|
|||||||
+70
-48
@@ -2,7 +2,6 @@ package ntp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -23,33 +22,37 @@ type Client struct {
|
|||||||
connID uint64
|
connID uint64
|
||||||
start time.Time
|
start time.Time
|
||||||
_now func() time.Time
|
_now func() time.Time
|
||||||
t [4]Timestamp
|
// t stores the time offsets needed to compute the time at client
|
||||||
|
// taking into consideration the round-trip delay.
|
||||||
|
// - t[0] (orig): Client timestamp of request packet transmission.
|
||||||
|
// - t[1] (rec): Server timestamp of request packet reception.
|
||||||
|
// - t[2] (xmt): Server timestamp of response packet transmission.
|
||||||
|
// - t[3]: Client timestamp of response packet reception.
|
||||||
|
t [4]Timestamp
|
||||||
// org Timestamp
|
// org Timestamp
|
||||||
// rec Timestamp
|
|
||||||
xmt Timestamp
|
|
||||||
state state
|
state state
|
||||||
serverStratum Stratum
|
serverStratum Stratum
|
||||||
_sysprec int8
|
sysprec int8
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) Reset(now func() time.Time) {
|
func (c *Client) Reset(now func() time.Time, sysprec int8) {
|
||||||
if c._sysprec == 0 {
|
|
||||||
c._sysprec = sysprecRecalcNeeded
|
|
||||||
}
|
|
||||||
*c = Client{
|
*c = Client{
|
||||||
connID: c.connID + 1,
|
connID: c.connID + 1,
|
||||||
_now: now,
|
_now: now,
|
||||||
_sysprec: c._sysprec,
|
sysprec: sysprec,
|
||||||
|
state: stateSend1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) Protocol() uint64 { return 0 }
|
||||||
|
func (c *Client) LocalPort() uint16 { return ClientPort }
|
||||||
func (c *Client) ConnectionID() *uint64 {
|
func (c *Client) ConnectionID() *uint64 {
|
||||||
return &c.connID
|
return &c.connID
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) Encapsulate(carrierData []byte, frameOffset int) (int, error) {
|
func (c *Client) Encapsulate(carrierData []byte, frameOffset int) (int, error) {
|
||||||
if c.isDone() {
|
if c.IsDone() {
|
||||||
return 0, io.EOF
|
return 0, nil
|
||||||
}
|
}
|
||||||
payload := carrierData[frameOffset:]
|
payload := carrierData[frameOffset:]
|
||||||
frm, err := NewFrame(payload)
|
frm, err := NewFrame(payload)
|
||||||
@@ -60,10 +63,10 @@ func (c *Client) Encapsulate(carrierData []byte, frameOffset int) (int, error) {
|
|||||||
switch c.state {
|
switch c.state {
|
||||||
case stateSend1:
|
case stateSend1:
|
||||||
c.start = c.now()
|
c.start = c.now()
|
||||||
c.xmt = TimestampFromUint64(0)
|
c.t[0] = TimestampFromUint64(0)
|
||||||
c.state = stateAwait1
|
c.state = stateAwait1
|
||||||
case stateSend2:
|
case stateSend2:
|
||||||
c.xmt = c.unsyncTimestamp(c.now())
|
// c.xmt = c.unsyncTimestamp(c.now())
|
||||||
c.state = stateDone
|
c.state = stateDone
|
||||||
default:
|
default:
|
||||||
return 0, nil // Nothing to handle.
|
return 0, nil // Nothing to handle.
|
||||||
@@ -72,46 +75,47 @@ func (c *Client) Encapsulate(carrierData []byte, frameOffset int) (int, error) {
|
|||||||
for i := range payload[:SizeHeader] {
|
for i := range payload[:SizeHeader] {
|
||||||
payload[i] = 0
|
payload[i] = 0
|
||||||
}
|
}
|
||||||
sysprec := c.sysprec()
|
|
||||||
frm.ClearHeader()
|
frm.ClearHeader()
|
||||||
frm.SetStratum(StratumUnsync)
|
frm.SetStratum(StratumUnsync)
|
||||||
frm.SetPoll(6)
|
frm.SetPoll(6)
|
||||||
frm.SetPrecision(sysprec)
|
frm.SetPrecision(c.sysprec)
|
||||||
frm.SetOriginTime(c.xmt)
|
frm.SetOriginTime(c.t[0])
|
||||||
frm.SetFlags(ModeClient, Version4, LeapNoWarning)
|
frm.SetFlags(ModeClient, Version4, LeapNoWarning)
|
||||||
return SizeHeader, nil
|
return SizeHeader, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) Demux(carrierData []byte, frameOffset int) error {
|
func (c *Client) Demux(carrierData []byte, frameOffset int) error {
|
||||||
if c.isDone() {
|
if c.IsDone() {
|
||||||
return io.EOF
|
return nil
|
||||||
}
|
}
|
||||||
payload := carrierData[frameOffset:]
|
payload := carrierData[frameOffset:]
|
||||||
frm, err := NewFrame(payload)
|
frm, err := NewFrame(payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
t := &c.t
|
|
||||||
switch c.state {
|
switch c.state {
|
||||||
case stateAwait1:
|
case stateAwait1:
|
||||||
tstx := frm.TransmitTime()
|
xmt := frm.TransmitTime()
|
||||||
tsorig := frm.OriginTime()
|
orig := frm.OriginTime()
|
||||||
if tstx == tsorig || tsorig == c.xmt {
|
if xmt == orig || orig != c.t[0] {
|
||||||
return errors.New("bogus NTP packet")
|
return errors.New("bogus NTP packet")
|
||||||
}
|
}
|
||||||
t[0] = tsorig
|
|
||||||
t[1] = frm.ReceiveTime()
|
txelapsed := c.now().Sub(c.start)
|
||||||
t[2] = tstx
|
c.t[1] = frm.ReceiveTime()
|
||||||
t[3] = c.unsyncTimestamp(c.now())
|
c.t[2] = xmt
|
||||||
|
c.t[3] = c.t[0].Add(txelapsed)
|
||||||
c.serverStratum = frm.Stratum()
|
c.serverStratum = frm.Stratum()
|
||||||
c.state = stateDone
|
c.state = stateDone // TODO: add second exchange part.
|
||||||
case stateAwait2:
|
case stateAwait2:
|
||||||
c.state = stateAwait2
|
c.state = stateDone
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) isDone() bool {
|
func (c *Client) IsDone() bool {
|
||||||
return c.state == stateDone
|
return c.state == stateDone
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,30 +126,48 @@ func (c *Client) now() time.Time {
|
|||||||
return c._now()
|
return c._now()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) unsyncTimestamp(now time.Time) Timestamp {
|
|
||||||
return TimestampFromUint64(0).Add(now.Sub(c.start))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) sysprec() int8 {
|
|
||||||
if c._sysprec == sysprecRecalcNeeded {
|
|
||||||
c._sysprec = CalculateSystemPrecision(c._now)
|
|
||||||
}
|
|
||||||
return c._sysprec
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now returns the current time as corrected by NTP protocol.
|
// Now returns the current time as corrected by NTP protocol.
|
||||||
func (c *Client) Now() time.Time {
|
func (c *Client) Now() time.Time {
|
||||||
return c.now().Add(c.Offset())
|
now, off := c.offsetAndNow()
|
||||||
|
return now.Add(off)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServerStratum returns the stratum of the server client synchronized with.
|
// ServerStratum returns the stratum of the server client synchronized with.
|
||||||
func (c *Client) ServerStratum() Stratum { return c.serverStratum }
|
func (c *Client) ServerStratum() Stratum { return c.serverStratum }
|
||||||
|
|
||||||
// Offset returns the
|
// Offset is a helper method to determine the difference between the Client's clock and the server's clock.
|
||||||
|
// Use [Client.Now] to calculate the server's time.
|
||||||
func (c *Client) Offset() time.Duration {
|
func (c *Client) Offset() time.Duration {
|
||||||
if c.isDone() {
|
if c.IsDone() {
|
||||||
t := &c.t
|
_, off := c.offsetAndNow()
|
||||||
return t[1].Sub(t[0])/2 + t[2].Sub(t[3])/2
|
return off
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) offsetAndNow() (clientNow time.Time, offset time.Duration) {
|
||||||
|
now := c.now()
|
||||||
|
serverToBase := c.OffsetUnsynced()
|
||||||
|
clientToBase := now.Sub(BaseTime())
|
||||||
|
serverToClient := serverToBase - clientToBase
|
||||||
|
return now, serverToClient
|
||||||
|
}
|
||||||
|
|
||||||
|
// OffsetUnsynced returns the absolute time offset difference between client and server clock
|
||||||
|
// as calculated by the clock synchonization algorithm. It is unsynchonized- the result of OffsetUnsynced will not change with time.
|
||||||
|
func (c *Client) OffsetUnsynced() time.Duration {
|
||||||
|
if c.IsDone() {
|
||||||
|
t := &c.t
|
||||||
|
return (t[1].Sub(t[0]) + t[2].Sub(t[3])) / 2
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) RoundTripDelay() time.Duration {
|
||||||
|
if c.IsDone() {
|
||||||
|
d0 := c.t[3].Sub(c.t[0])
|
||||||
|
d1 := c.t[2].Sub(c.t[1])
|
||||||
|
return d0 - d1
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|||||||
+8
-20
@@ -43,13 +43,13 @@ type Frame struct {
|
|||||||
func (frm Frame) Flags() (mode Mode, version uint8, lp LeapIndicator) {
|
func (frm Frame) Flags() (mode Mode, version uint8, lp LeapIndicator) {
|
||||||
b := frm.buf[0]
|
b := frm.buf[0]
|
||||||
mode = Mode(b & 0b111)
|
mode = Mode(b & 0b111)
|
||||||
version = (b << 3) & 0b11
|
version = (b >> 3) & 0b111
|
||||||
lp = LeapIndicator(b >> 5)
|
lp = LeapIndicator(b >> 6)
|
||||||
return mode, version, lp
|
return mode, version, lp
|
||||||
}
|
}
|
||||||
|
|
||||||
func (frm Frame) SetFlags(mode Mode, version uint8, lp LeapIndicator) {
|
func (frm Frame) SetFlags(mode Mode, version uint8, lp LeapIndicator) {
|
||||||
b := uint8(mode)&0b111 | (Version4&0b11)<<3 | uint8(lp&0b111)<<5
|
b := uint8(mode)&0b111 | (version&0b111)<<3 | uint8(lp&0b11)<<6
|
||||||
frm.buf[0] = b
|
frm.buf[0] = b
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,31 +269,19 @@ var (
|
|||||||
sysPrec int8
|
sysPrec int8
|
||||||
)
|
)
|
||||||
|
|
||||||
// SystemPrecision calculates the Precision field value for the NTP header once
|
|
||||||
// and reuses it for all future calls.
|
|
||||||
func SystemPrecision() int8 {
|
|
||||||
ntpOnceSystemClock.Do(recalculateSystemPrecision)
|
|
||||||
return sysPrec
|
|
||||||
}
|
|
||||||
|
|
||||||
func recalculateSystemPrecision() {
|
|
||||||
sysPrec = CalculateSystemPrecision(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CalculateSystemPrecision calculates the NTP system precision for a time source.
|
// CalculateSystemPrecision calculates the NTP system precision for a time source.
|
||||||
// If the time source is nil the default static call to [time.Now] is used.
|
// If the time source is nil the default static call to [time.Now] is used.
|
||||||
func CalculateSystemPrecision(now func() time.Time) int8 {
|
func CalculateSystemPrecision(now func() time.Time, iters []time.Time) int8 {
|
||||||
const maxIter = 16
|
maxIter := len(iters)
|
||||||
var times [maxIter]time.Time
|
|
||||||
if now == nil {
|
if now == nil {
|
||||||
for i := 0; i < maxIter; i++ {
|
for i := 0; i < maxIter; i++ {
|
||||||
times[i] = time.Now()
|
iters[i] = time.Now()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for i := 0; i < maxIter; i++ {
|
for i := 0; i < maxIter; i++ {
|
||||||
times[i] = now()
|
iters[i] = now()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
avg := times[maxIter-1].Sub(times[0]) / maxIter
|
avg := iters[maxIter-1].Sub(iters[0]) / time.Duration(maxIter)
|
||||||
return int8(math.Log2(avg.Seconds()))
|
return int8(math.Log2(avg.Seconds()))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user