github.com/coreos/mantle@v0.13.0/network/ntp/protocol.go (about)

     1  // Copyright 2015 CoreOS, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // NTP v4 (RFC 5905)
    16  // http://tools.ietf.org/html/rfc5905
    17  package ntp
    18  
    19  //go:generate stringer -type=LeapIndicator,Mode,VersionNumber -output=protocol_string.go protocol.go
    20  
    21  import (
    22  	"encoding/binary"
    23  	"fmt"
    24  	"time"
    25  )
    26  
    27  // JAN_1970 is the Unix Epoch in NTP Seconds (1970 - 1900)
    28  const JAN_1970 = 2208988800
    29  
    30  // short-hand to make the marshal functions less tedious
    31  var be = binary.BigEndian
    32  
    33  // NTP Short Format
    34  //
    35  //   0                   1                   2                   3
    36  //   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    37  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    38  //  |          Seconds              |           Fraction            |
    39  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    40  //
    41  type Short struct {
    42  	Seconds  uint16
    43  	Fraction uint16
    44  }
    45  
    46  func (s *Short) marshalBinary(b []byte) {
    47  	be.PutUint16(b[0:], s.Seconds)
    48  	be.PutUint16(b[2:], s.Fraction)
    49  }
    50  
    51  func (s *Short) unmarshalBinary(b []byte) {
    52  	s.Seconds = be.Uint16(b[0:])
    53  	s.Fraction = be.Uint16(b[2:])
    54  }
    55  
    56  // NTP Timestamp Format
    57  //
    58  //   0                   1                   2                   3
    59  //   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    60  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    61  //  |                            Seconds                            |
    62  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    63  //  |                            Fraction                           |
    64  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    65  //
    66  type Timestamp struct {
    67  	Seconds  uint32
    68  	Fraction uint32
    69  }
    70  
    71  // Now gets the current NTP time in the 64-bit Timestamp format.
    72  func Now() Timestamp {
    73  	return NewTimestamp(time.Now())
    74  }
    75  
    76  // NewTimestamp converts from Go's Time to NTP's 64-bit Timestamp format.
    77  func NewTimestamp(t time.Time) Timestamp {
    78  	secs := t.Unix() + JAN_1970
    79  	// Convert from range [0,999999999] to [0,UINT32_MAX]
    80  	frac := (uint64(t.Nanosecond()) << 32) / 1000000000
    81  	return Timestamp{Seconds: uint32(secs), Fraction: uint32(frac)}
    82  }
    83  
    84  // Precision represents the accuracy of times reported by Now() for use
    85  // in Header.Precision. The return value is log2 seconds.
    86  func Precision() int8 {
    87  	// The RFC claims this should be determined by the clock resolution
    88  	// or the execution time of reading the clock, whichever is greater.
    89  	// This code is not a stickler for accuracy so just assume the worst
    90  	// and use ~1μs which huge compared to execution time.
    91  	return -20 // log2 1e-6 = -19.93
    92  }
    93  
    94  func (t *Timestamp) marshalBinary(b []byte) {
    95  	be.PutUint32(b[0:], t.Seconds)
    96  	be.PutUint32(b[4:], t.Fraction)
    97  }
    98  
    99  func (t *Timestamp) unmarshalBinary(b []byte) {
   100  	t.Seconds = be.Uint32(b[0:])
   101  	t.Fraction = be.Uint32(b[4:])
   102  }
   103  
   104  // LeapIndicator (LI): 2-bit integer warning of an impending leap second
   105  // to be inserted or deleted in the last minute of the current month.
   106  type LeapIndicator byte
   107  
   108  const (
   109  	LEAP_NONE   LeapIndicator = iota
   110  	LEAP_ADD                  // last minute of the day has 61 seconds
   111  	LEAP_SUB                  // last minute of the day has 59 seconds
   112  	LEAP_NOSYNC               // unknown (clock unsynchronized)
   113  )
   114  
   115  // VersionNumber (VN): 3-bit integer representing the NTP version
   116  // number, currently 4.
   117  type VersionNumber byte
   118  
   119  const NTPv4 VersionNumber = 4
   120  
   121  // Mode: 3-bit integer representing the association mode.
   122  type Mode byte
   123  
   124  const (
   125  	MODE_RESERVED Mode = iota
   126  	MODE_SYMMETRIC_ACTIVE
   127  	MODE_SYMMETRIC_PASSIVE
   128  	MODE_CLIENT
   129  	MODE_SERVER
   130  	MODE_BROADCAST
   131  	MODE_CONTROL // NTP control message
   132  	MODE_PRIVATE // reserved for private use
   133  )
   134  
   135  // NTP Packet Header Format
   136  //
   137  //   0                   1                   2                   3
   138  //   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   139  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   140  //  |LI | VN  |Mode |    Stratum    |     Poll      |   Precision   |
   141  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   142  //  |                         Root Delay                            |
   143  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   144  //  |                         Root Dispersion                       |
   145  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   146  //  |                          Reference ID                         |
   147  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   148  //  |                                                               |
   149  //  +                     Reference Timestamp (64)                  +
   150  //  |                                                               |
   151  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   152  //  |                                                               |
   153  //  +                      Origin Timestamp (64)                    +
   154  //  |                                                               |
   155  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   156  //  |                                                               |
   157  //  +                      Receive Timestamp (64)                   +
   158  //  |                                                               |
   159  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   160  //  |                                                               |
   161  //  +                      Transmit Timestamp (64)                  +
   162  //  |                                                               |
   163  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   164  //  |                                                               |
   165  //  .                                                               .
   166  //  .                    Extension Field 1 (variable)               .
   167  //  .                                                               .
   168  //  |                                                               |
   169  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   170  //  |                                                               |
   171  //  .                                                               .
   172  //  .                    Extension Field 2 (variable)               .
   173  //  .                                                               .
   174  //  |                                                               |
   175  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   176  //  |                          Key Identifier                       |
   177  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   178  //  |                                                               |
   179  //  |                            dgst (128)                         |
   180  //  |                                                               |
   181  //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   182  //
   183  type Header struct {
   184  	LeapIndicator      LeapIndicator // 2 bits
   185  	VersionNumber      VersionNumber // 3 bits
   186  	Mode               Mode          // 3 bits
   187  	Stratum            uint8
   188  	Poll               int8
   189  	Precision          int8
   190  	RootDelay          Short
   191  	RootDispersion     Short
   192  	ReferenceId        [4]byte
   193  	ReferenceTimestamp Timestamp
   194  	OriginTimestamp    Timestamp
   195  	ReceiveTimestamp   Timestamp
   196  	TransmitTimestamp  Timestamp
   197  	// Extension fields and digest not included
   198  }
   199  
   200  // magic values for packing a packet
   201  const (
   202  	liMax      = 3
   203  	liOffset   = 6
   204  	vnMax      = 7
   205  	vnOffset   = 3
   206  	modeMax    = 7
   207  	modeOffset = 0
   208  	headerSize = 48 // bytes
   209  )
   210  
   211  func (h *Header) MarshalBinary() ([]byte, error) {
   212  	if h.LeapIndicator > liMax ||
   213  		h.VersionNumber > vnMax ||
   214  		h.Mode > modeMax {
   215  		return nil, fmt.Errorf("Invalid NTP Header %v", h)
   216  	}
   217  
   218  	data := make([]byte, headerSize)
   219  	data[0] = ((byte(h.LeapIndicator) << liOffset) |
   220  		(byte(h.VersionNumber) << vnOffset) |
   221  		(byte(h.Mode) << modeOffset))
   222  	data[1] = byte(h.Stratum)
   223  	data[2] = byte(h.Poll)
   224  	data[3] = byte(h.Precision)
   225  	h.RootDelay.marshalBinary(data[4:])
   226  	h.RootDispersion.marshalBinary(data[8:])
   227  	copy(data[12:], h.ReferenceId[:])
   228  	h.ReferenceTimestamp.marshalBinary(data[16:])
   229  	h.OriginTimestamp.marshalBinary(data[24:])
   230  	h.ReceiveTimestamp.marshalBinary(data[32:])
   231  	h.TransmitTimestamp.marshalBinary(data[40:])
   232  
   233  	return data, nil
   234  }
   235  
   236  func (h *Header) UnmarshalBinary(data []byte) error {
   237  	if len(data) < headerSize {
   238  		return fmt.Errorf("NTP packet too small! %d < %d", len(data), headerSize)
   239  	}
   240  
   241  	h.LeapIndicator = LeapIndicator((data[0] >> liOffset) & liMax)
   242  	h.VersionNumber = VersionNumber((data[0] >> vnOffset) & vnMax)
   243  	h.Mode = Mode((data[0] >> modeOffset) & modeMax)
   244  	h.Stratum = uint8(data[1])
   245  	h.Poll = int8(data[2])
   246  	h.Precision = int8(data[3])
   247  	h.RootDelay.unmarshalBinary(data[4:])
   248  	h.RootDispersion.unmarshalBinary(data[8:])
   249  	copy(h.ReferenceId[:], data[12:])
   250  	h.ReferenceTimestamp.unmarshalBinary(data[16:])
   251  	h.OriginTimestamp.unmarshalBinary(data[24:])
   252  	h.ReceiveTimestamp.unmarshalBinary(data[32:])
   253  	h.TransmitTimestamp.unmarshalBinary(data[40:])
   254  
   255  	return nil
   256  }