417 lines
18 KiB
Go
417 lines
18 KiB
Go
// Copyright 2016 Google, Inc. All rights reserved.
|
|
//
|
|
// Use of this source code is governed by a BSD-style license
|
|
// that can be found in the LICENSE file in the root of the source
|
|
// tree.
|
|
//
|
|
//******************************************************************************
|
|
|
|
package layers
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"errors"
|
|
|
|
"github.com/google/gopacket"
|
|
)
|
|
|
|
//******************************************************************************
|
|
//
|
|
// Network Time Protocol (NTP) Decoding Layer
|
|
// ------------------------------------------
|
|
// This file provides a GoPacket decoding layer for NTP.
|
|
//
|
|
//******************************************************************************
|
|
//
|
|
// About The Network Time Protocol (NTP)
|
|
// -------------------------------------
|
|
// NTP is a protocol that enables computers on the internet to set their
|
|
// clocks to the correct time (or to a time that is acceptably close to the
|
|
// correct time). NTP runs on top of UDP.
|
|
//
|
|
// There have been a series of versions of the NTP protocol. The latest
|
|
// version is V4 and is specified in RFC 5905:
|
|
// http://www.ietf.org/rfc/rfc5905.txt
|
|
//
|
|
//******************************************************************************
|
|
//
|
|
// References
|
|
// ----------
|
|
//
|
|
// Wikipedia's NTP entry:
|
|
// https://en.wikipedia.org/wiki/Network_Time_Protocol
|
|
// This is the best place to get an overview of NTP.
|
|
//
|
|
// Network Time Protocol Home Website:
|
|
// http://www.ntp.org/
|
|
// This appears to be the official website of NTP.
|
|
//
|
|
// List of current NTP Protocol RFCs:
|
|
// http://www.ntp.org/rfc.html
|
|
//
|
|
// RFC 958: "Network Time Protocol (NTP)" (1985)
|
|
// https://tools.ietf.org/html/rfc958
|
|
// This is the original NTP specification.
|
|
//
|
|
// RFC 1305: "Network Time Protocol (Version 3) Specification, Implementation and Analysis" (1992)
|
|
// https://tools.ietf.org/html/rfc1305
|
|
// The protocol was updated in 1992 yielding NTP V3.
|
|
//
|
|
// RFC 5905: "Network Time Protocol Version 4: Protocol and Algorithms Specification" (2010)
|
|
// https://www.ietf.org/rfc/rfc5905.txt
|
|
// The protocol was updated in 2010 yielding NTP V4.
|
|
// V4 is backwards compatible with all previous versions of NTP.
|
|
//
|
|
// RFC 5906: "Network Time Protocol Version 4: Autokey Specification"
|
|
// https://tools.ietf.org/html/rfc5906
|
|
// This document addresses the security of the NTP protocol
|
|
// and is probably not relevant to this package.
|
|
//
|
|
// RFC 5907: "Definitions of Managed Objects for Network Time Protocol Version 4 (NTPv4)"
|
|
// https://tools.ietf.org/html/rfc5907
|
|
// This document addresses the management of NTP servers and
|
|
// is probably not relevant to this package.
|
|
//
|
|
// RFC 5908: "Network Time Protocol (NTP) Server Option for DHCPv6"
|
|
// https://tools.ietf.org/html/rfc5908
|
|
// This document addresses the use of NTP in DHCPv6 and is
|
|
// probably not relevant to this package.
|
|
//
|
|
// "Let's make a NTP Client in C"
|
|
// https://lettier.github.io/posts/2016-04-26-lets-make-a-ntp-client-in-c.html
|
|
// This web page contains useful information about the details of NTP,
|
|
// including an NTP record struture in C, and C code.
|
|
//
|
|
// "NTP Packet Header (NTP Reference Implementation) (Computer Network Time Synchronization)"
|
|
// http://what-when-how.com/computer-network-time-synchronization/
|
|
// ntp-packet-header-ntp-reference-implementation-computer-network-time-synchronization/
|
|
// This web page contains useful information on the details of NTP.
|
|
//
|
|
// "Technical information - NTP Data Packet"
|
|
// https://www.meinbergglobal.com/english/info/ntp-packet.htm
|
|
// This page has a helpful diagram of an NTP V4 packet.
|
|
//
|
|
//******************************************************************************
|
|
//
|
|
// Obsolete References
|
|
// -------------------
|
|
//
|
|
// RFC 1119: "RFC-1119 "Network Time Protocol (Version 2) Specification and Implementation" (1989)
|
|
// https://tools.ietf.org/html/rfc1119
|
|
// Version 2 was drafted in 1989.
|
|
// It is unclear whether V2 was ever implememented or whether the
|
|
// ideas ended up in V3 (which was implemented in 1992).
|
|
//
|
|
// RFC 1361: "Simple Network Time Protocol (SNTP)"
|
|
// https://tools.ietf.org/html/rfc1361
|
|
// This document is obsoleted by RFC 1769 and is included only for completeness.
|
|
//
|
|
// RFC 1769: "Simple Network Time Protocol (SNTP)"
|
|
// https://tools.ietf.org/html/rfc1769
|
|
// This document is obsoleted by RFC 2030 and RFC 4330 and is included only for completeness.
|
|
//
|
|
// RFC 2030: "Simple Network Time Protocol (SNTP) Version 4 for IPv4, IPv6 and OSI"
|
|
// https://tools.ietf.org/html/rfc2030
|
|
// This document is obsoleted by RFC 4330 and is included only for completeness.
|
|
//
|
|
// RFC 4330: "Simple Network Time Protocol (SNTP) Version 4 for IPv4, IPv6 and OSI"
|
|
// https://tools.ietf.org/html/rfc4330
|
|
// This document is obsoleted by RFC 5905 and is included only for completeness.
|
|
//
|
|
//******************************************************************************
|
|
//
|
|
// Endian And Bit Numbering Issues
|
|
// -------------------------------
|
|
//
|
|
// Endian and bit numbering issues can be confusing. Here is some
|
|
// clarification:
|
|
//
|
|
// ENDIAN: Values are sent big endian.
|
|
// https://en.wikipedia.org/wiki/Endianness
|
|
//
|
|
// BIT NUMBERING: Bits are numbered 0 upwards from the most significant
|
|
// bit to the least significant bit. This means that if there is a 32-bit
|
|
// value, the most significant bit is called bit 0 and the least
|
|
// significant bit is called bit 31.
|
|
//
|
|
// See RFC 791 Appendix B for more discussion.
|
|
//
|
|
//******************************************************************************
|
|
//
|
|
// NTP V3 and V4 Packet Format
|
|
// ---------------------------
|
|
// NTP packets are UDP packets whose payload contains an NTP record.
|
|
//
|
|
// The NTP RFC defines the format of the NTP record.
|
|
//
|
|
// There have been four versions of the protocol:
|
|
//
|
|
// V1 in 1985
|
|
// V2 in 1989
|
|
// V3 in 1992
|
|
// V4 in 2010
|
|
//
|
|
// It is clear that V1 and V2 are obsolete, and there is no need to
|
|
// cater for these formats.
|
|
//
|
|
// V3 and V4 essentially use the same format, with V4 adding some optional
|
|
// fields on the end. So this package supports the V3 and V4 formats.
|
|
//
|
|
// The current version of NTP (NTP V4)'s RFC (V4 - RFC 5905) contains
|
|
// the following diagram for the NTP record format:
|
|
|
|
// 0 1 2 3
|
|
// 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
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
// |LI | VN |Mode | Stratum | Poll | Precision |
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
// | Root Delay |
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
// | Root Dispersion |
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
// | Reference ID |
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
// | |
|
|
// + Reference Timestamp (64) +
|
|
// | |
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
// | |
|
|
// + Origin Timestamp (64) +
|
|
// | |
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
// | |
|
|
// + Receive Timestamp (64) +
|
|
// | |
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
// | |
|
|
// + Transmit Timestamp (64) +
|
|
// | |
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
// | |
|
|
// . .
|
|
// . Extension Field 1 (variable) .
|
|
// . .
|
|
// | |
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
// | |
|
|
// . .
|
|
// . Extension Field 2 (variable) .
|
|
// . .
|
|
// | |
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
// | Key Identifier |
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
// | |
|
|
// | dgst (128) |
|
|
// | |
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
// From http://www.ietf.org/rfc/rfc5905.txt
|
|
//
|
|
// The fields "Extension Field 1 (variable)" and later are optional fields,
|
|
// and so we can set a minimum NTP record size of 48 bytes.
|
|
//
|
|
const ntpMinimumRecordSizeInBytes int = 48
|
|
|
|
//******************************************************************************
|
|
|
|
// NTP Type
|
|
// --------
|
|
// Type NTP implements the DecodingLayer interface. Each NTP object
|
|
// represents in a structured form the NTP record present as the UDP
|
|
// payload in an NTP UDP packet.
|
|
//
|
|
|
|
type NTPLeapIndicator uint8
|
|
type NTPVersion uint8
|
|
type NTPMode uint8
|
|
type NTPStratum uint8
|
|
type NTPLog2Seconds int8
|
|
type NTPFixed16Seconds uint32
|
|
type NTPReferenceID uint32
|
|
type NTPTimestamp uint64
|
|
|
|
type NTP struct {
|
|
BaseLayer // Stores the packet bytes and payload bytes.
|
|
|
|
LeapIndicator NTPLeapIndicator // [0,3]. Indicates whether leap second(s) is to be added.
|
|
Version NTPVersion // [0,7]. Version of the NTP protocol.
|
|
Mode NTPMode // [0,7]. Mode.
|
|
Stratum NTPStratum // [0,255]. Stratum of time server in the server tree.
|
|
Poll NTPLog2Seconds // [-128,127]. The maximum interval between successive messages, in log2 seconds.
|
|
Precision NTPLog2Seconds // [-128,127]. The precision of the system clock, in log2 seconds.
|
|
RootDelay NTPFixed16Seconds // [0,2^32-1]. Total round trip delay to the reference clock in seconds times 2^16.
|
|
RootDispersion NTPFixed16Seconds // [0,2^32-1]. Total dispersion to the reference clock, in seconds times 2^16.
|
|
ReferenceID NTPReferenceID // ID code of reference clock [0,2^32-1].
|
|
ReferenceTimestamp NTPTimestamp // Most recent timestamp from the reference clock.
|
|
OriginTimestamp NTPTimestamp // Local time when request was sent from local host.
|
|
ReceiveTimestamp NTPTimestamp // Local time (on server) that request arrived at server host.
|
|
TransmitTimestamp NTPTimestamp // Local time (on server) that request departed server host.
|
|
|
|
// FIX: This package should analyse the extension fields and represent the extension fields too.
|
|
ExtensionBytes []byte // Just put extensions in a byte slice.
|
|
}
|
|
|
|
//******************************************************************************
|
|
|
|
// LayerType returns the layer type of the NTP object, which is LayerTypeNTP.
|
|
func (d *NTP) LayerType() gopacket.LayerType {
|
|
return LayerTypeNTP
|
|
}
|
|
|
|
//******************************************************************************
|
|
|
|
// decodeNTP analyses a byte slice and attempts to decode it as an NTP
|
|
// record of a UDP packet.
|
|
//
|
|
// If it succeeds, it loads p with information about the packet and returns nil.
|
|
// If it fails, it returns an error (non nil).
|
|
//
|
|
// This function is employed in layertypes.go to register the NTP layer.
|
|
func decodeNTP(data []byte, p gopacket.PacketBuilder) error {
|
|
|
|
// Attempt to decode the byte slice.
|
|
d := &NTP{}
|
|
err := d.DecodeFromBytes(data, p)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// If the decoding worked, add the layer to the packet and set it
|
|
// as the application layer too, if there isn't already one.
|
|
p.AddLayer(d)
|
|
p.SetApplicationLayer(d)
|
|
|
|
return nil
|
|
}
|
|
|
|
//******************************************************************************
|
|
|
|
// DecodeFromBytes analyses a byte slice and attempts to decode it as an NTP
|
|
// record of a UDP packet.
|
|
//
|
|
// Upon succeeds, it loads the NTP object with information about the packet
|
|
// and returns nil.
|
|
// Upon failure, it returns an error (non nil).
|
|
func (d *NTP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
|
|
|
// If the data block is too short to be a NTP record, then return an error.
|
|
if len(data) < ntpMinimumRecordSizeInBytes {
|
|
df.SetTruncated()
|
|
return errors.New("NTP packet too short")
|
|
}
|
|
|
|
// RFC 5905 does not appear to define a maximum NTP record length.
|
|
// The protocol allows "extension fields" to be included in the record,
|
|
// and states about these fields:"
|
|
//
|
|
// "While the minimum field length containing required fields is
|
|
// four words (16 octets), a maximum field length remains to be
|
|
// established."
|
|
//
|
|
// For this reason, the packet length is not checked here for being too long.
|
|
|
|
// NTP type embeds type BaseLayer which contains two fields:
|
|
// Contents is supposed to contain the bytes of the data at this level.
|
|
// Payload is supposed to contain the payload of this level.
|
|
// Here we set the baselayer to be the bytes of the NTP record.
|
|
d.BaseLayer = BaseLayer{Contents: data[:len(data)]}
|
|
|
|
// Extract the fields from the block of bytes.
|
|
// To make sense of this, refer to the packet diagram
|
|
// above and the section on endian conventions.
|
|
|
|
// The first few fields are all packed into the first 32 bits. Unpack them.
|
|
f := data[0]
|
|
d.LeapIndicator = NTPLeapIndicator((f & 0xC0) >> 6)
|
|
d.Version = NTPVersion((f & 0x38) >> 3)
|
|
d.Mode = NTPMode(f & 0x07)
|
|
d.Stratum = NTPStratum(data[1])
|
|
d.Poll = NTPLog2Seconds(data[2])
|
|
d.Precision = NTPLog2Seconds(data[3])
|
|
|
|
// The remaining fields can just be copied in big endian order.
|
|
d.RootDelay = NTPFixed16Seconds(binary.BigEndian.Uint32(data[4:8]))
|
|
d.RootDispersion = NTPFixed16Seconds(binary.BigEndian.Uint32(data[8:12]))
|
|
d.ReferenceID = NTPReferenceID(binary.BigEndian.Uint32(data[12:16]))
|
|
d.ReferenceTimestamp = NTPTimestamp(binary.BigEndian.Uint64(data[16:24]))
|
|
d.OriginTimestamp = NTPTimestamp(binary.BigEndian.Uint64(data[24:32]))
|
|
d.ReceiveTimestamp = NTPTimestamp(binary.BigEndian.Uint64(data[32:40]))
|
|
d.TransmitTimestamp = NTPTimestamp(binary.BigEndian.Uint64(data[40:48]))
|
|
|
|
// This layer does not attempt to analyse the extension bytes.
|
|
// But if there are any, we'd like the user to know. So we just
|
|
// place them all in an ExtensionBytes field.
|
|
d.ExtensionBytes = data[48:]
|
|
|
|
// Return no error.
|
|
return nil
|
|
}
|
|
|
|
// SerializeTo writes the serialized form of this layer into the
|
|
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
|
// See the docs for gopacket.SerializableLayer for more info.
|
|
func (d *NTP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
|
data, err := b.PrependBytes(ntpMinimumRecordSizeInBytes)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Pack the first few fields into the first 32 bits.
|
|
h := uint8(0)
|
|
h |= (uint8(d.LeapIndicator) << 6) & 0xC0
|
|
h |= (uint8(d.Version) << 3) & 0x38
|
|
h |= (uint8(d.Mode)) & 0x07
|
|
data[0] = byte(h)
|
|
data[1] = byte(d.Stratum)
|
|
data[2] = byte(d.Poll)
|
|
data[3] = byte(d.Precision)
|
|
|
|
// The remaining fields can just be copied in big endian order.
|
|
binary.BigEndian.PutUint32(data[4:8], uint32(d.RootDelay))
|
|
binary.BigEndian.PutUint32(data[8:12], uint32(d.RootDispersion))
|
|
binary.BigEndian.PutUint32(data[12:16], uint32(d.ReferenceID))
|
|
binary.BigEndian.PutUint64(data[16:24], uint64(d.ReferenceTimestamp))
|
|
binary.BigEndian.PutUint64(data[24:32], uint64(d.OriginTimestamp))
|
|
binary.BigEndian.PutUint64(data[32:40], uint64(d.ReceiveTimestamp))
|
|
binary.BigEndian.PutUint64(data[40:48], uint64(d.TransmitTimestamp))
|
|
|
|
ex, err := b.AppendBytes(len(d.ExtensionBytes))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
copy(ex, d.ExtensionBytes)
|
|
|
|
return nil
|
|
}
|
|
|
|
//******************************************************************************
|
|
|
|
// CanDecode returns a set of layers that NTP objects can decode.
|
|
// As NTP objects can only decide the NTP layer, we can return just that layer.
|
|
// Apparently a single layer type implements LayerClass.
|
|
func (d *NTP) CanDecode() gopacket.LayerClass {
|
|
return LayerTypeNTP
|
|
}
|
|
|
|
//******************************************************************************
|
|
|
|
// NextLayerType specifies the next layer that GoPacket should attempt to
|
|
// analyse after this (NTP) layer. As NTP packets do not contain any payload
|
|
// bytes, there are no further layers to analyse.
|
|
func (d *NTP) NextLayerType() gopacket.LayerType {
|
|
return gopacket.LayerTypeZero
|
|
}
|
|
|
|
//******************************************************************************
|
|
|
|
// NTP packets do not carry any data payload, so the empty byte slice is retured.
|
|
// In Go, a nil slice is functionally identical to an empty slice, so we
|
|
// return nil to avoid a heap allocation.
|
|
func (d *NTP) Payload() []byte {
|
|
return nil
|
|
}
|
|
|
|
//******************************************************************************
|
|
//* End Of NTP File *
|
|
//******************************************************************************
|