| package org.apache.commons.net.ntp; |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** |
| * Wrapper class to network time packet messages (NTP, etc) that computes |
| * related timing info and stats. |
| * |
| * @author Jason Mathews, MITRE Corp |
| * |
| * @version $Revision$ $Date$ |
| */ |
| public class TimeInfo { |
| |
| private NtpV3Packet _message; |
| private List<String> _comments; |
| private Long _delay; |
| private Long _offset; |
| |
| /** |
| * time at which time message packet was received by local machine |
| */ |
| private long _returnTime; |
| |
| /** |
| * flag indicating that the TimeInfo details was processed and delay/offset were computed |
| */ |
| private boolean _detailsComputed; |
| |
| /** |
| * Create TimeInfo object with raw packet message and destination time received. |
| * |
| * @param message NTP message packet |
| * @param returnTime destination receive time |
| * @throws IllegalArgumentException if message is null |
| */ |
| public TimeInfo(NtpV3Packet message, long returnTime) { |
| this(message, returnTime, null, true); |
| } |
| |
| /** |
| * Create TimeInfo object with raw packet message and destination time received. |
| * |
| * @param message NTP message packet |
| * @param returnTime destination receive time |
| * @param comments List of errors/warnings identified during processing |
| * @throws IllegalArgumentException if message is null |
| */ |
| public TimeInfo(NtpV3Packet message, long returnTime, List<String> comments) |
| { |
| this(message, returnTime, comments, true); |
| } |
| |
| /** |
| * Create TimeInfo object with raw packet message and destination time received. |
| * Auto-computes details if computeDetails flag set otherwise this is delayed |
| * until computeDetails() is called. Delayed computation is for fast |
| * intialization when sub-millisecond timing is needed. |
| * |
| * @param msgPacket NTP message packet |
| * @param returnTime destination receive time |
| * @param doComputeDetails flag to pre-compute delay/offset values |
| * @throws IllegalArgumentException if message is null |
| */ |
| public TimeInfo(NtpV3Packet msgPacket, long returnTime, boolean doComputeDetails) |
| { |
| this(msgPacket, returnTime, null, doComputeDetails); |
| } |
| |
| /** |
| * Create TimeInfo object with raw packet message and destination time received. |
| * Auto-computes details if computeDetails flag set otherwise this is delayed |
| * until computeDetails() is called. Delayed computation is for fast |
| * intialization when sub-millisecond timing is needed. |
| * |
| * @param message NTP message packet |
| * @param returnTime destination receive time |
| * @param comments list of comments used to store errors/warnings with message |
| * @param doComputeDetails flag to pre-compute delay/offset values |
| * @throws IllegalArgumentException if message is null |
| */ |
| public TimeInfo(NtpV3Packet message, long returnTime, List<String> comments, |
| boolean doComputeDetails) |
| { |
| if (message == null) |
| throw new IllegalArgumentException("message cannot be null"); |
| this._returnTime = returnTime; |
| this._message = message; |
| this._comments = comments; |
| if (doComputeDetails) |
| computeDetails(); |
| } |
| |
| /** |
| * Add comment (error/warning) to list of comments associated |
| * with processing of NTP parameters. If comment list not create |
| * then one will be created. |
| * |
| * @param comment |
| */ |
| public void addComment(String comment) |
| { |
| if (_comments == null) { |
| _comments = new ArrayList<String>(); |
| } |
| _comments.add(comment); |
| } |
| |
| /** |
| * Compute and validate details of the NTP message packet. Computed |
| * fields include the offset and delay. |
| */ |
| public void computeDetails() |
| { |
| if (_detailsComputed) { |
| return; // details already computed - do nothing |
| } |
| _detailsComputed = true; |
| if (_comments == null) { |
| _comments = new ArrayList<String>(); |
| } |
| |
| TimeStamp origNtpTime = _message.getOriginateTimeStamp(); |
| long origTime = origNtpTime.getTime(); |
| |
| // Receive Time is time request received by server (t2) |
| TimeStamp rcvNtpTime = _message.getReceiveTimeStamp(); |
| long rcvTime = rcvNtpTime.getTime(); |
| |
| // Transmit time is time reply sent by server (t3) |
| TimeStamp xmitNtpTime = _message.getTransmitTimeStamp(); |
| long xmitTime = xmitNtpTime.getTime(); |
| |
| /* |
| * Round-trip network delay and local clock offset (or time drift) is calculated |
| * according to this standard NTP equation: |
| * |
| * LocalClockOffset = ((ReceiveTimestamp - OriginateTimestamp) + |
| * (TransmitTimestamp - DestinationTimestamp)) / 2 |
| * |
| * equations from RFC-1305 (NTPv3) |
| * roundtrip delay = (t4 - t1) - (t3 - t2) |
| * local clock offset = ((t2 - t1) + (t3 - t4)) / 2 |
| * |
| * It takes into account network delays and assumes that they are symmetrical. |
| * |
| * Note the typo in SNTP RFCs 1769/2030 which state that the delay |
| * is (T4 - T1) - (T2 - T3) with the "T2" and "T3" switched. |
| */ |
| if (origNtpTime.ntpValue() == 0) |
| { |
| // without originate time cannot determine when packet went out |
| // might be via a broadcast NTP packet... |
| if (xmitNtpTime.ntpValue() != 0) |
| { |
| _offset = Long.valueOf(xmitTime - _returnTime); |
| _comments.add("Error: zero orig time -- cannot compute delay"); |
| } else |
| _comments.add("Error: zero orig time -- cannot compute delay/offset"); |
| } else if (rcvNtpTime.ntpValue() == 0 || xmitNtpTime.ntpValue() == 0) |
| { |
| _comments.add("Warning: zero rcvNtpTime or xmitNtpTime"); |
| // assert destTime >= origTime since network delay cannot be negative |
| if (origTime > _returnTime) |
| _comments.add("Error: OrigTime > DestRcvTime"); |
| else |
| { |
| // without receive or xmit time cannot figure out processing time |
| // so delay is simply the network travel time |
| _delay = Long.valueOf(_returnTime - origTime); |
| } |
| // TODO: is offset still valid if rcvNtpTime=0 || xmitNtpTime=0 ??? |
| // Could always hash origNtpTime (sendTime) but if host doesn't set it |
| // then it's an malformed ntp host anyway and we don't care? |
| // If server is in broadcast mode then we never send out a query in first place... |
| if (rcvNtpTime.ntpValue() != 0) |
| { |
| // xmitTime is 0 just use rcv time |
| _offset = Long.valueOf(rcvTime - origTime); |
| } else if (xmitNtpTime.ntpValue() != 0) |
| { |
| // rcvTime is 0 just use xmitTime time |
| _offset = Long.valueOf(xmitTime - _returnTime); |
| } |
| } else |
| { |
| long delayValue = _returnTime - origTime; |
| // assert xmitTime >= rcvTime: difference typically < 1ms |
| if (xmitTime < rcvTime) |
| { |
| // server cannot send out a packet before receiving it... |
| _comments.add("Error: xmitTime < rcvTime"); // time-travel not allowed |
| } else |
| { |
| // subtract processing time from round-trip network delay |
| long delta = xmitTime - rcvTime; |
| // in normal cases the processing delta is less than |
| // the total roundtrip network travel time. |
| if (delta <= delayValue) |
| { |
| delayValue -= delta; // delay = (t4 - t1) - (t3 - t2) |
| } else |
| { |
| // if delta - delayValue == 1 ms then it's a round-off error |
| // e.g. delay=3ms, processing=4ms |
| if (delta - delayValue == 1) |
| { |
| // delayValue == 0 -> local clock saw no tick change but destination clock did |
| if (delayValue != 0) |
| { |
| _comments.add("Info: processing time > total network time by 1 ms -> assume zero delay"); |
| delayValue = 0; |
| } |
| } else |
| _comments.add("Warning: processing time > total network time"); |
| } |
| } |
| _delay = Long.valueOf(delayValue); |
| if (origTime > _returnTime) // assert destTime >= origTime |
| _comments.add("Error: OrigTime > DestRcvTime"); |
| |
| _offset = Long.valueOf(((rcvTime - origTime) + (xmitTime - _returnTime)) / 2); |
| } |
| } |
| |
| /** |
| * Return list of comments (if any) during processing of NTP packet. |
| * |
| * @return List or null if not yet computed |
| */ |
| public List<String> getComments() |
| { |
| return _comments; |
| } |
| |
| /** |
| * Get round-trip network delay. If null then could not compute the delay. |
| * |
| * @return Long or null if delay not available. |
| */ |
| public Long getDelay() |
| { |
| return _delay; |
| } |
| |
| /** |
| * Get clock offset needed to adjust local clock to match remote clock. If null then could not |
| * compute the offset. |
| * |
| * @return Long or null if offset not available. |
| */ |
| public Long getOffset() |
| { |
| return _offset; |
| } |
| |
| /** |
| * Returns NTP message packet. |
| * |
| * @return NTP message packet. |
| */ |
| public NtpV3Packet getMessage() |
| { |
| return _message; |
| } |
| |
| /** |
| * Returns time at which time message packet was received by local machine. |
| * |
| * @return packet return time. |
| */ |
| public long getReturnTime() |
| { |
| return _returnTime; |
| } |
| |
| } |