Skip to content

Commit

Permalink
fix(inputs.chrony): Support local sources
Browse files Browse the repository at this point in the history
Local sources are directly connected to the server (PPS inputs from
oscillators, GPS, etc..). They can be seen as stratum-0 sources.

The standard way of getting their name doesn't work as it is stored into
their RefID instead.

See the following links for more details:
- https://gitlab.com/chrony/chrony/-/blob/05bd4898a9a64fe1e5432a78939c4b4b82619c2f/client.c?page=3#L2046
- https://gitlab.com/chrony/chrony/-/blob/05bd4898a9a64fe1e5432a78939c4b4b82619c2f/client.c?page=3#L2152

This fixes that

Signed-off-by: Frank Villaro-Dixon <frank@villaro-dixon.eu>
  • Loading branch information
Frankkkkk committed Jun 24, 2024
1 parent 78cbf53 commit cc9e21c
Showing 1 changed file with 42 additions and 28 deletions.
70 changes: 42 additions & 28 deletions plugins/inputs/chrony/chrony.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package chrony
import (
"bytes"
_ "embed"
"encoding/binary"
"errors"
"fmt"
"net"
Expand Down Expand Up @@ -291,6 +292,24 @@ func (c *Chrony) gatherServerStats(acc telegraf.Accumulator) error {
return nil
}

func (c *Chrony) getSourceName(ip net.IP) (string, error) {
sourceNameReq := fbchrony.NewNTPSourceNamePacket(ip)
sourceNameRaw, err := c.client.Communicate(sourceNameReq)
if err != nil {
return "", fmt.Errorf("querying name of source %q failed: %w", ip, err)
}
sourceName, ok := sourceNameRaw.(*fbchrony.ReplyNTPSourceName)
if !ok {
return "", fmt.Errorf("got unexpected response type %T while waiting for source name", sourceNameRaw)
}

// Cut the string at null termination
if termidx := bytes.Index(sourceName.Name[:], []byte{0}); termidx >= 0 {
return string(sourceName.Name[:termidx]), nil
}
return string(sourceName.Name[:]), nil
}

func (c *Chrony) gatherSources(acc telegraf.Accumulator) error {
sourcesReq := fbchrony.NewSourcesPacket()
sourcesRaw, err := c.client.Communicate(sourcesReq)
Expand All @@ -316,22 +335,20 @@ func (c *Chrony) gatherSources(acc telegraf.Accumulator) error {
}

// Trying to resolve the source name
sourceNameReq := fbchrony.NewNTPSourceNamePacket(sourceData.IPAddr)
sourceNameRaw, err := c.client.Communicate(sourceNameReq)
if err != nil {
return fmt.Errorf("querying name of source %d failed: %w", idx, err)
}
sourceName, ok := sourceNameRaw.(*fbchrony.ReplyNTPSourceName)
if !ok {
return fmt.Errorf("got unexpected response type %T while waiting for source name", sourceNameRaw)
}

// Cut the string at null termination
var peer string
if termidx := bytes.Index(sourceName.Name[:], []byte{0}); termidx >= 0 {
peer = string(sourceName.Name[:termidx])

if sourceData.Mode == fbchrony.SourceModeRef && sourceData.IPAddr.To4() != nil {
//https://gitlab.com/chrony/chrony/-/blob/05bd4898a9a64fe1e5432a78939c4b4b82619c2f/client.c?page=3#L2046
// References of local sources (PPS, etc..) are encoded in the bits of the IPv4 address,
// instead of the RefId. Extract the correct name for those sources.
ipU32 := binary.BigEndian.Uint32(sourceData.IPAddr.To4())

peer = fbchrony.RefidToString(ipU32)
} else {
peer = string(sourceName.Name[:])
peer, err = c.getSourceName(sourceData.IPAddr)
if err != nil {
return err
}
}

if peer == "" {
Expand Down Expand Up @@ -388,22 +405,19 @@ func (c *Chrony) gatherSourceStats(acc telegraf.Accumulator) error {
}

// Trying to resolve the source name
sourceNameReq := fbchrony.NewNTPSourceNamePacket(sourceStats.IPAddr)
sourceNameRaw, err := c.client.Communicate(sourceNameReq)
if err != nil {
return fmt.Errorf("querying name of source %d failed: %w", idx, err)
}
sourceName, ok := sourceNameRaw.(*fbchrony.ReplyNTPSourceName)
if !ok {
return fmt.Errorf("got unexpected response type %T while waiting for source name", sourceNameRaw)
}

// Cut the string at null termination
var peer string
if termidx := bytes.Index(sourceName.Name[:], []byte{0}); termidx >= 0 {
peer = string(sourceName.Name[:termidx])

if sourceStats.IPAddr.String() == "::" {
// `::` IPs mean a local source directly connected to the server.
// For example a PPS source or a local GPS receiver.
// Then the name of the source is encoded in its RefID
// https://gitlab.com/chrony/chrony/-/blob/05bd4898a9a64fe1e5432a78939c4b4b82619c2f/client.c?page=3#L2152
peer = fbchrony.RefidToString(sourceStats.RefID)
} else {
peer = string(sourceName.Name[:])
peer, err = c.getSourceName(sourceStats.IPAddr)
if err != nil {
return err
}
}

if peer == "" {
Expand Down

0 comments on commit cc9e21c

Please sign in to comment.