github.com/coyove/sdss@v0.0.0-20231129015646-c2ec58cca6a2/future/chrony/packet.go (about)

     1  /*
     2  Copyright (c) Facebook, Inc. and its affiliates.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package chrony
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/binary"
    22  	"fmt"
    23  	"net"
    24  	"time"
    25  
    26  	log "github.com/sirupsen/logrus"
    27  )
    28  
    29  // original C++ versions of those consts/structs
    30  // are in https://github.com/mlichvar/chrony/blob/master/candm.h
    31  
    32  // ReplyType identifies reply packet type
    33  type ReplyType uint16
    34  
    35  // CommandType identifies command type in both request and repy
    36  type CommandType uint16
    37  
    38  // ModeType identifies source (peer) mode
    39  type ModeType uint16
    40  
    41  // SourceStateType identifies source (peer) state
    42  type SourceStateType uint16
    43  
    44  // ResponseStatusType identifies response status
    45  type ResponseStatusType uint16
    46  
    47  // PacketType - request or reply
    48  type PacketType uint8
    49  
    50  // we implement latest (at the moment) protocol version
    51  const protoVersionNumber uint8 = 6
    52  const maxDataLen = 396
    53  
    54  // packet types
    55  const (
    56  	pktTypeCmdRequest PacketType = 1
    57  	pktTypeCmdReply   PacketType = 2
    58  )
    59  
    60  func (t PacketType) String() string {
    61  	switch t {
    62  	case pktTypeCmdRequest:
    63  		return "request"
    64  	case pktTypeCmdReply:
    65  		return "reply"
    66  	default:
    67  		return fmt.Sprintf("unknown (%d)", t)
    68  	}
    69  }
    70  
    71  // request types. Only those we support, there are more
    72  const (
    73  	reqNSources    CommandType = 14
    74  	reqSourceData  CommandType = 15
    75  	reqTracking    CommandType = 33
    76  	reqSourceStats CommandType = 34
    77  	reqActivity    CommandType = 44
    78  	reqServerStats CommandType = 54
    79  	reqNTPData     CommandType = 57
    80  )
    81  
    82  // reply types
    83  const (
    84  	rpyNSources     ReplyType = 2
    85  	rpySourceData   ReplyType = 3
    86  	rpyTracking     ReplyType = 5
    87  	rpySourceStats  ReplyType = 6
    88  	rpyActivity     ReplyType = 12
    89  	rpyServerStats  ReplyType = 14
    90  	rpyNTPData      ReplyType = 16
    91  	rpyServerStats2 ReplyType = 22
    92  	rpyServerStats3 ReplyType = 24
    93  )
    94  
    95  // source modes
    96  const (
    97  	SourceModeClient ModeType = 0
    98  	SourceModePeer   ModeType = 1
    99  	SourceModeRef    ModeType = 2
   100  )
   101  
   102  // source state
   103  const (
   104  	SourceStateSync        SourceStateType = 0
   105  	SourceStateUnreach     SourceStateType = 1
   106  	SourceStateFalseTicker SourceStateType = 2
   107  	SourceStateJittery     SourceStateType = 3
   108  	SourceStateCandidate   SourceStateType = 4
   109  	SourceStateOutlier     SourceStateType = 5
   110  )
   111  
   112  // source data flags
   113  const (
   114  	FlagNoselect uint16 = 0x1
   115  	FlagPrefer   uint16 = 0x2
   116  	FlagTrust    uint16 = 0x4
   117  	FlagRequire  uint16 = 0x8
   118  )
   119  
   120  // ntpdata flags
   121  const (
   122  	NTPFlagsTests        uint16 = 0x3ff
   123  	NTPFlagInterleaved   uint16 = 0x4000
   124  	NTPFlagAuthenticated uint16 = 0x8000
   125  )
   126  
   127  // response status codes
   128  const (
   129  	sttSuccess            ResponseStatusType = 0
   130  	sttFailed             ResponseStatusType = 1
   131  	sttUnauth             ResponseStatusType = 2
   132  	sttInvalid            ResponseStatusType = 3
   133  	sttNoSuchSource       ResponseStatusType = 4
   134  	sttInvalidTS          ResponseStatusType = 5
   135  	sttNotEnabled         ResponseStatusType = 6
   136  	sttBadSubnet          ResponseStatusType = 7
   137  	sttAccessAllowed      ResponseStatusType = 8
   138  	sttAccessDenied       ResponseStatusType = 9
   139  	sttNoHostAccess       ResponseStatusType = 10
   140  	sttSourceAlreadyKnown ResponseStatusType = 11
   141  	sttTooManySources     ResponseStatusType = 12
   142  	sttNoRTC              ResponseStatusType = 13
   143  	sttBadRTCFile         ResponseStatusType = 14
   144  	sttInactive           ResponseStatusType = 15
   145  	sttBadSample          ResponseStatusType = 16
   146  	sttInvalidAF          ResponseStatusType = 17
   147  	sttBadPktVersion      ResponseStatusType = 18
   148  	sttBadPktLength       ResponseStatusType = 19
   149  )
   150  
   151  // StatusDesc provides mapping from ResponseStatusType to string
   152  var StatusDesc = [20]string{
   153  	"SUCCESS",
   154  	"FAILED",
   155  	"UNAUTH",
   156  	"INVALID",
   157  	"NOSUCHSOURCE",
   158  	"INVALIDTS",
   159  	"NOTENABLED",
   160  	"BADSUBNET",
   161  	"ACCESSALLOWED",
   162  	"ACCESSDENIED",
   163  	"NOHOSTACCESS",
   164  	"SOURCEALREADYKNOWN",
   165  	"TOOMANYSOURCES",
   166  	"NORTC",
   167  	"BADRTCFILE",
   168  	"INACTIVE",
   169  	"BADSAMPLE",
   170  	"INVALIDAF",
   171  	"BADPKTVERSION",
   172  	"BADPKTLENGTH",
   173  }
   174  
   175  func (r ResponseStatusType) String() string {
   176  	if int(r) >= len(StatusDesc) {
   177  		return fmt.Sprintf("UNKNOWN (%d)", r)
   178  	}
   179  	return StatusDesc[r]
   180  }
   181  
   182  // SourceStateDesc provides mapping from SourceStateType to string
   183  var SourceStateDesc = [6]string{
   184  	"sync",
   185  	"unreach",
   186  	"falseticker",
   187  	"jittery",
   188  	"candidate",
   189  	"outlier",
   190  }
   191  
   192  func (s SourceStateType) String() string {
   193  	if int(s) >= len(SourceStateDesc) {
   194  		return fmt.Sprintf("unknown (%d)", s)
   195  	}
   196  	return SourceStateDesc[s]
   197  }
   198  
   199  // ModeTypeDesc provides mapping from ModeType to string
   200  var ModeTypeDesc = [3]string{
   201  	"client",
   202  	"peer",
   203  	"reference clock",
   204  }
   205  
   206  func (m ModeType) String() string {
   207  	if int(m) >= len(ModeTypeDesc) {
   208  		return fmt.Sprintf("unknown (%d)", m)
   209  	}
   210  	return ModeTypeDesc[m]
   211  }
   212  
   213  // RequestHead is the first (common) part of the request,
   214  // in a format that can be directly passed to binary.Write
   215  type RequestHead struct {
   216  	Version  uint8
   217  	PKTType  PacketType
   218  	Res1     uint8
   219  	Res2     uint8
   220  	Command  CommandType
   221  	Attempt  uint16
   222  	Sequence uint32
   223  	Pad1     uint32
   224  	Pad2     uint32
   225  }
   226  
   227  // GetCommand returns request packet command
   228  func (r *RequestHead) GetCommand() CommandType {
   229  	return r.Command
   230  }
   231  
   232  // SetSequence sets request packet sequence number
   233  func (r *RequestHead) SetSequence(n uint32) {
   234  	r.Sequence = n
   235  }
   236  
   237  // RequestPacket is an interface to abstract all different outgoing packets
   238  type RequestPacket interface {
   239  	GetCommand() CommandType
   240  	SetSequence(n uint32)
   241  }
   242  
   243  // ResponsePacket is an interface to abstract all different incoming packets
   244  type ResponsePacket interface {
   245  	GetCommand() CommandType
   246  	GetType() PacketType
   247  	GetStatus() ResponseStatusType
   248  }
   249  
   250  // RequestSources - packet to request number of sources (peers)
   251  type RequestSources struct {
   252  	RequestHead
   253  	// we actually need this to send proper packet
   254  	data [maxDataLen]uint8
   255  }
   256  
   257  // RequestSourceData - packet to request source data for source id
   258  type RequestSourceData struct {
   259  	RequestHead
   260  	Index int32
   261  	EOR   int32
   262  	// we pass i32 - 4 bytes
   263  	data [maxDataLen - 4]uint8
   264  }
   265  
   266  // RequestNTPData - packet to request NTP data for peer IP.
   267  // As of now, it's only allowed by Chrony over unix socket connection.
   268  type RequestNTPData struct {
   269  	RequestHead
   270  	IPAddr ipAddr
   271  	EOR    int32
   272  	// we pass at max ipv6 addr - 16 bytes
   273  	data [maxDataLen - 16]uint8
   274  }
   275  
   276  // RequestServerStats - packet to request server stats
   277  type RequestServerStats struct {
   278  	RequestHead
   279  	// we actually need this to send proper packet
   280  	data [maxDataLen]uint8
   281  }
   282  
   283  // RequestTracking - packet to request 'tracking' data
   284  type RequestTracking struct {
   285  	RequestHead
   286  	// we actually need this to send proper packet
   287  	data [maxDataLen]uint8
   288  }
   289  
   290  // RequestSourceStats - packet to request 'sourcestats' data for source id
   291  type RequestSourceStats struct {
   292  	RequestHead
   293  	Index int32
   294  	EOR   int32
   295  	// we pass i32 - 4 bytes
   296  	data [maxDataLen - 4]uint8
   297  }
   298  
   299  // RequestActivity - packet to request 'activity' data
   300  type RequestActivity struct {
   301  	RequestHead
   302  	// we actually need this to send proper packet
   303  	data [maxDataLen]uint8
   304  }
   305  
   306  // ReplyHead is the first (common) part of the reply packet,
   307  // in a format that can be directly passed to binary.Read
   308  type ReplyHead struct {
   309  	Version  uint8
   310  	PKTType  PacketType
   311  	Res1     uint8
   312  	Res2     uint8
   313  	Command  CommandType
   314  	Reply    ReplyType
   315  	Status   ResponseStatusType
   316  	Pad1     uint16
   317  	Pad2     uint16
   318  	Pad3     uint16
   319  	Sequence uint32
   320  	Pad4     uint32
   321  	Pad5     uint32
   322  }
   323  
   324  // GetCommand returns reply packet command
   325  func (r *ReplyHead) GetCommand() CommandType {
   326  	return r.Command
   327  }
   328  
   329  // GetType returns reply packet type
   330  func (r *ReplyHead) GetType() PacketType {
   331  	return r.PKTType
   332  }
   333  
   334  // GetStatus returns reply packet status
   335  func (r *ReplyHead) GetStatus() ResponseStatusType {
   336  	return r.Status
   337  }
   338  
   339  type replySourcesContent struct {
   340  	NSources uint32
   341  }
   342  
   343  // ReplySources is a usable version of a reply to 'sources' command
   344  type ReplySources struct {
   345  	ReplyHead
   346  	NSources int
   347  }
   348  
   349  type replySourceDataContent struct {
   350  	IPAddr         ipAddr
   351  	Poll           int16
   352  	Stratum        uint16
   353  	State          SourceStateType
   354  	Mode           ModeType
   355  	Flags          uint16
   356  	Reachability   uint16
   357  	SinceSample    uint32
   358  	OrigLatestMeas chronyFloat
   359  	LatestMeas     chronyFloat
   360  	LatestMeasErr  chronyFloat
   361  }
   362  
   363  // SourceData contains parsed version of 'source data' reply
   364  type SourceData struct {
   365  	IPAddr         net.IP
   366  	Poll           int16
   367  	Stratum        uint16
   368  	State          SourceStateType
   369  	Mode           ModeType
   370  	Flags          uint16
   371  	Reachability   uint16
   372  	SinceSample    uint32
   373  	OrigLatestMeas float64
   374  	LatestMeas     float64
   375  	LatestMeasErr  float64
   376  }
   377  
   378  func newSourceData(r *replySourceDataContent) *SourceData {
   379  	return &SourceData{
   380  		IPAddr:         r.IPAddr.ToNetIP(),
   381  		Poll:           r.Poll,
   382  		Stratum:        r.Stratum,
   383  		State:          r.State,
   384  		Mode:           r.Mode,
   385  		Flags:          r.Flags,
   386  		Reachability:   r.Reachability,
   387  		SinceSample:    r.SinceSample,
   388  		OrigLatestMeas: r.OrigLatestMeas.ToFloat(),
   389  		LatestMeas:     r.LatestMeas.ToFloat(),
   390  		LatestMeasErr:  r.LatestMeasErr.ToFloat(),
   391  	}
   392  }
   393  
   394  // ReplySourceData is a usable version of 'source data' reply for given source id
   395  type ReplySourceData struct {
   396  	ReplyHead
   397  	SourceData
   398  }
   399  
   400  type replyTrackingContent struct {
   401  	RefID              uint32
   402  	IPAddr             ipAddr // our current sync source
   403  	Stratum            uint16
   404  	LeapStatus         uint16
   405  	RefTime            timeSpec
   406  	CurrentCorrection  chronyFloat
   407  	LastOffset         chronyFloat
   408  	RMSOffset          chronyFloat
   409  	FreqPPM            chronyFloat
   410  	ResidFreqPPM       chronyFloat
   411  	SkewPPM            chronyFloat
   412  	RootDelay          chronyFloat
   413  	RootDispersion     chronyFloat
   414  	LastUpdateInterval chronyFloat
   415  }
   416  
   417  // Tracking contains parsed version of 'tracking' reply
   418  type Tracking struct {
   419  	RefID              uint32
   420  	IPAddr             net.IP
   421  	Stratum            uint16
   422  	LeapStatus         uint16
   423  	RefTime            time.Time
   424  	CurrentCorrection  float64
   425  	LastOffset         float64
   426  	RMSOffset          float64
   427  	FreqPPM            float64
   428  	ResidFreqPPM       float64
   429  	SkewPPM            float64
   430  	RootDelay          float64
   431  	RootDispersion     float64
   432  	LastUpdateInterval float64
   433  }
   434  
   435  func newTracking(r *replyTrackingContent) *Tracking {
   436  	return &Tracking{
   437  		RefID:              r.RefID,
   438  		IPAddr:             r.IPAddr.ToNetIP(),
   439  		Stratum:            r.Stratum,
   440  		LeapStatus:         r.LeapStatus,
   441  		RefTime:            r.RefTime.ToTime(),
   442  		CurrentCorrection:  r.CurrentCorrection.ToFloat(),
   443  		LastOffset:         r.LastOffset.ToFloat(),
   444  		RMSOffset:          r.RMSOffset.ToFloat(),
   445  		FreqPPM:            r.FreqPPM.ToFloat(),
   446  		ResidFreqPPM:       r.ResidFreqPPM.ToFloat(),
   447  		SkewPPM:            r.SkewPPM.ToFloat(),
   448  		RootDelay:          r.RootDelay.ToFloat(),
   449  		RootDispersion:     r.RootDispersion.ToFloat(),
   450  		LastUpdateInterval: r.LastUpdateInterval.ToFloat(),
   451  	}
   452  }
   453  
   454  // ReplyTracking has usable 'tracking' response
   455  type ReplyTracking struct {
   456  	ReplyHead
   457  	Tracking
   458  }
   459  
   460  type replySourceStatsContent struct {
   461  	RefID              uint32
   462  	IPAddr             ipAddr
   463  	NSamples           uint32
   464  	NRuns              uint32
   465  	SpanSeconds        uint32
   466  	StandardDeviation  chronyFloat
   467  	ResidFreqPPM       chronyFloat
   468  	SkewPPM            chronyFloat
   469  	EstimatedOffset    chronyFloat
   470  	EstimatedOffsetErr chronyFloat
   471  }
   472  
   473  // SourceStats contains stats about the source
   474  type SourceStats struct {
   475  	RefID              uint32
   476  	IPAddr             net.IP
   477  	NSamples           uint32
   478  	NRuns              uint32
   479  	SpanSeconds        uint32
   480  	StandardDeviation  float64
   481  	ResidFreqPPM       float64
   482  	SkewPPM            float64
   483  	EstimatedOffset    float64
   484  	EstimatedOffsetErr float64
   485  }
   486  
   487  func newSourceStats(r *replySourceStatsContent) *SourceStats {
   488  	return &SourceStats{
   489  		RefID:              r.RefID,
   490  		IPAddr:             r.IPAddr.ToNetIP(),
   491  		NSamples:           r.NSamples,
   492  		NRuns:              r.NRuns,
   493  		SpanSeconds:        r.SpanSeconds,
   494  		StandardDeviation:  r.StandardDeviation.ToFloat(),
   495  		ResidFreqPPM:       r.ResidFreqPPM.ToFloat(),
   496  		SkewPPM:            r.SkewPPM.ToFloat(),
   497  		EstimatedOffset:    r.EstimatedOffset.ToFloat(),
   498  		EstimatedOffsetErr: r.EstimatedOffsetErr.ToFloat(),
   499  	}
   500  }
   501  
   502  // ReplySourceStats has usable 'sourcestats' response
   503  type ReplySourceStats struct {
   504  	ReplyHead
   505  	SourceStats
   506  }
   507  
   508  type replyNTPDataContent struct {
   509  	RemoteAddr      ipAddr
   510  	LocalAddr       ipAddr
   511  	RemotePort      uint16
   512  	Leap            uint8
   513  	Version         uint8
   514  	Mode            uint8
   515  	Stratum         uint8
   516  	Poll            int8
   517  	Precision       int8
   518  	RootDelay       chronyFloat
   519  	RootDispersion  chronyFloat
   520  	RefID           uint32
   521  	RefTime         timeSpec
   522  	Offset          chronyFloat
   523  	PeerDelay       chronyFloat
   524  	PeerDispersion  chronyFloat
   525  	ResponseTime    chronyFloat
   526  	JitterAsymmetry chronyFloat
   527  	Flags           uint16
   528  	TXTssChar       uint8
   529  	RXTssChar       uint8
   530  	TotalTXCount    uint32
   531  	TotalRXCount    uint32
   532  	TotalValidCount uint32
   533  	Reserved        [4]uint32
   534  }
   535  
   536  // NTPData contains parsed version of 'ntpdata' reply
   537  type NTPData struct {
   538  	RemoteAddr      net.IP
   539  	LocalAddr       net.IP
   540  	RemotePort      uint16
   541  	Leap            uint8
   542  	Version         uint8
   543  	Mode            uint8
   544  	Stratum         uint8
   545  	Poll            int8
   546  	Precision       int8
   547  	RootDelay       float64
   548  	RootDispersion  float64
   549  	RefID           uint32
   550  	RefTime         time.Time
   551  	Offset          float64
   552  	PeerDelay       float64
   553  	PeerDispersion  float64
   554  	ResponseTime    float64
   555  	JitterAsymmetry float64
   556  	Flags           uint16
   557  	TXTssChar       uint8
   558  	RXTssChar       uint8
   559  	TotalTXCount    uint32
   560  	TotalRXCount    uint32
   561  	TotalValidCount uint32
   562  }
   563  
   564  func newNTPData(r *replyNTPDataContent) *NTPData {
   565  	return &NTPData{
   566  		RemoteAddr:      r.RemoteAddr.ToNetIP(),
   567  		LocalAddr:       r.LocalAddr.ToNetIP(),
   568  		RemotePort:      r.RemotePort,
   569  		Leap:            r.Leap,
   570  		Version:         r.Version,
   571  		Mode:            r.Mode,
   572  		Stratum:         r.Stratum,
   573  		Poll:            r.Poll,
   574  		Precision:       r.Precision,
   575  		RootDelay:       r.RootDelay.ToFloat(),
   576  		RootDispersion:  r.RootDispersion.ToFloat(),
   577  		RefID:           r.RefID,
   578  		RefTime:         r.RefTime.ToTime(),
   579  		Offset:          r.Offset.ToFloat(),
   580  		PeerDelay:       r.PeerDelay.ToFloat(),
   581  		PeerDispersion:  r.PeerDispersion.ToFloat(),
   582  		ResponseTime:    r.ResponseTime.ToFloat(),
   583  		JitterAsymmetry: r.JitterAsymmetry.ToFloat(),
   584  		Flags:           r.Flags,
   585  		TXTssChar:       r.TXTssChar,
   586  		RXTssChar:       r.RXTssChar,
   587  		TotalTXCount:    r.TotalTXCount,
   588  		TotalRXCount:    r.TotalRXCount,
   589  		TotalValidCount: r.TotalValidCount,
   590  	}
   591  }
   592  
   593  // ReplyNTPData is a what end user will get for of 'ntp data' response
   594  type ReplyNTPData struct {
   595  	ReplyHead
   596  	NTPData
   597  }
   598  
   599  // Activity contains parsed version of 'activity' reply
   600  type Activity struct {
   601  	Online       int32
   602  	Offline      int32
   603  	BurstOnline  int32
   604  	BurstOffline int32
   605  	Unresolved   int32
   606  }
   607  
   608  // ReplyActivity is a usable version of 'activity' response
   609  type ReplyActivity struct {
   610  	ReplyHead
   611  	Activity
   612  }
   613  
   614  // ServerStats contains parsed version of 'serverstats' reply
   615  type ServerStats struct {
   616  	NTPHits  uint32
   617  	CMDHits  uint32
   618  	NTPDrops uint32
   619  	CMDDrops uint32
   620  	LogDrops uint32
   621  }
   622  
   623  // ReplyServerStats is a usable version of 'serverstats' response
   624  type ReplyServerStats struct {
   625  	ReplyHead
   626  	ServerStats
   627  }
   628  
   629  // ServerStats2 contains parsed version of 'serverstats2' reply
   630  type ServerStats2 struct {
   631  	NTPHits     uint32
   632  	NKEHits     uint32
   633  	CMDHits     uint32
   634  	NTPDrops    uint32
   635  	NKEDrops    uint32
   636  	CMDDrops    uint32
   637  	LogDrops    uint32
   638  	NTPAuthHits uint32
   639  }
   640  
   641  // ReplyServerStats2 is a usable version of 'serverstats2' response
   642  type ReplyServerStats2 struct {
   643  	ReplyHead
   644  	ServerStats2
   645  }
   646  
   647  // ServerStats3 contains parsed version of 'serverstats3' reply
   648  type ServerStats3 struct {
   649  	NTPHits            uint32
   650  	NKEHits            uint32
   651  	CMDHits            uint32
   652  	NTPDrops           uint32
   653  	NKEDrops           uint32
   654  	CMDDrops           uint32
   655  	LogDrops           uint32
   656  	NTPAuthHits        uint32
   657  	NTPInterleavedHits uint32
   658  	NTPTimestamps      uint32
   659  	NTPSpanSeconds     uint32
   660  }
   661  
   662  // ReplyServerStats3 is a usable version of 'serverstats3' response
   663  type ReplyServerStats3 struct {
   664  	ReplyHead
   665  	ServerStats3
   666  }
   667  
   668  // here go request constructors
   669  
   670  // NewSourcesPacket creates new packet to request number of sources (peers)
   671  func NewSourcesPacket() *RequestSources {
   672  	return &RequestSources{
   673  		RequestHead: RequestHead{
   674  			Version: protoVersionNumber,
   675  			PKTType: pktTypeCmdRequest,
   676  			Command: reqNSources,
   677  		},
   678  		data: [maxDataLen]uint8{},
   679  	}
   680  }
   681  
   682  // NewTrackingPacket creates new packet to request 'tracking' information
   683  func NewTrackingPacket() *RequestTracking {
   684  	return &RequestTracking{
   685  		RequestHead: RequestHead{
   686  			Version: protoVersionNumber,
   687  			PKTType: pktTypeCmdRequest,
   688  			Command: reqTracking,
   689  		},
   690  		data: [maxDataLen]uint8{},
   691  	}
   692  }
   693  
   694  // NewSourceStatsPacket creates a new packet to request 'sourcestats' information
   695  func NewSourceStatsPacket(sourceID int32) *RequestSourceStats {
   696  	return &RequestSourceStats{
   697  		RequestHead: RequestHead{
   698  			Version: protoVersionNumber,
   699  			PKTType: pktTypeCmdRequest,
   700  			Command: reqSourceStats,
   701  		},
   702  		Index: sourceID,
   703  		data:  [maxDataLen - 4]uint8{},
   704  	}
   705  }
   706  
   707  // NewSourceDataPacket creates new packet to request 'source data' information about source with given ID
   708  func NewSourceDataPacket(sourceID int32) *RequestSourceData {
   709  	return &RequestSourceData{
   710  		RequestHead: RequestHead{
   711  			Version: protoVersionNumber,
   712  			PKTType: pktTypeCmdRequest,
   713  			Command: reqSourceData,
   714  		},
   715  		Index: sourceID,
   716  		data:  [maxDataLen - 4]uint8{},
   717  	}
   718  }
   719  
   720  // NewNTPDataPacket creates new packet to request 'ntp data' information for given peer IP
   721  func NewNTPDataPacket(ip net.IP) *RequestNTPData {
   722  	return &RequestNTPData{
   723  		RequestHead: RequestHead{
   724  			Version: protoVersionNumber,
   725  			PKTType: pktTypeCmdRequest,
   726  			Command: reqNTPData,
   727  		},
   728  		IPAddr: *newIPAddr(ip),
   729  		data:   [maxDataLen - 16]uint8{},
   730  	}
   731  }
   732  
   733  // NewServerStatsPacket creates new packet to request 'serverstats' information
   734  func NewServerStatsPacket() *RequestServerStats {
   735  	return &RequestServerStats{
   736  		RequestHead: RequestHead{
   737  			Version: protoVersionNumber,
   738  			PKTType: pktTypeCmdRequest,
   739  			Command: reqServerStats,
   740  		},
   741  		data: [maxDataLen]uint8{},
   742  	}
   743  }
   744  
   745  // NewActivityPacket creates new packet to request 'activity' information
   746  func NewActivityPacket() *RequestActivity {
   747  	return &RequestActivity{
   748  		RequestHead: RequestHead{
   749  			Version: protoVersionNumber,
   750  			PKTType: pktTypeCmdRequest,
   751  			Command: reqActivity,
   752  		},
   753  		data: [maxDataLen]uint8{},
   754  	}
   755  }
   756  
   757  // decodePacket decodes bytes to valid response packet
   758  func decodePacket(response []byte) (ResponsePacket, error) {
   759  	var err error
   760  	r := bytes.NewReader(response)
   761  	head := new(ReplyHead)
   762  	if err = binary.Read(r, binary.BigEndian, head); err != nil {
   763  		return nil, err
   764  	}
   765  	log.Debugf("response head: %+v", head)
   766  	if head.Status != sttSuccess {
   767  		return nil, fmt.Errorf("got status %s (%d)", head.Status, head.Status)
   768  	}
   769  	switch head.Reply {
   770  	case rpyNSources:
   771  		data := new(replySourcesContent)
   772  		if err = binary.Read(r, binary.BigEndian, data); err != nil {
   773  			return nil, err
   774  		}
   775  		log.Debugf("response data: %+v", data)
   776  		return &ReplySources{
   777  			ReplyHead: *head,
   778  			NSources:  int(data.NSources),
   779  		}, nil
   780  	case rpySourceData:
   781  		data := new(replySourceDataContent)
   782  		if err = binary.Read(r, binary.BigEndian, data); err != nil {
   783  			return nil, err
   784  		}
   785  		log.Debugf("response data: %+v", data)
   786  		return &ReplySourceData{
   787  			ReplyHead:  *head,
   788  			SourceData: *newSourceData(data),
   789  		}, nil
   790  	case rpyTracking:
   791  		data := new(replyTrackingContent)
   792  		if err = binary.Read(r, binary.BigEndian, data); err != nil {
   793  			return nil, err
   794  		}
   795  		log.Debugf("response data: %+v", data)
   796  		return &ReplyTracking{
   797  			ReplyHead: *head,
   798  			Tracking:  *newTracking(data),
   799  		}, nil
   800  	case rpySourceStats:
   801  		data := new(replySourceStatsContent)
   802  		if err = binary.Read(r, binary.BigEndian, data); err != nil {
   803  			return nil, err
   804  		}
   805  		log.Debugf("response data: %+v", data)
   806  		return &ReplySourceStats{
   807  			ReplyHead:   *head,
   808  			SourceStats: *newSourceStats(data),
   809  		}, nil
   810  	case rpyActivity:
   811  		data := new(Activity)
   812  		if err = binary.Read(r, binary.BigEndian, data); err != nil {
   813  			return nil, err
   814  		}
   815  		log.Debugf("response data: %+v", data)
   816  		return &ReplyActivity{
   817  			ReplyHead: *head,
   818  			Activity:  *data,
   819  		}, nil
   820  	case rpyServerStats:
   821  		data := new(ServerStats)
   822  		if err = binary.Read(r, binary.BigEndian, data); err != nil {
   823  			return nil, err
   824  		}
   825  		log.Debugf("response data: %+v", data)
   826  		return &ReplyServerStats{
   827  			ReplyHead:   *head,
   828  			ServerStats: *data,
   829  		}, nil
   830  	case rpyNTPData:
   831  		data := new(replyNTPDataContent)
   832  		if err = binary.Read(r, binary.BigEndian, data); err != nil {
   833  			return nil, err
   834  		}
   835  		log.Debugf("response data: %+v", data)
   836  		return &ReplyNTPData{
   837  			ReplyHead: *head,
   838  			NTPData:   *newNTPData(data),
   839  		}, nil
   840  	case rpyServerStats2:
   841  		data := new(ServerStats2)
   842  		if err = binary.Read(r, binary.BigEndian, data); err != nil {
   843  			return nil, err
   844  		}
   845  		log.Debugf("response data: %+v", data)
   846  		return &ReplyServerStats2{
   847  			ReplyHead:    *head,
   848  			ServerStats2: *data,
   849  		}, nil
   850  	case rpyServerStats3:
   851  		data := new(ServerStats3)
   852  		if err = binary.Read(r, binary.BigEndian, data); err != nil {
   853  			return nil, err
   854  		}
   855  		log.Debugf("response data: %+v", data)
   856  		return &ReplyServerStats3{
   857  			ReplyHead:    *head,
   858  			ServerStats3: *data,
   859  		}, nil
   860  	default:
   861  		return nil, fmt.Errorf("not implemented reply type %d from %+v", head.Reply, head)
   862  	}
   863  }