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 }