001: package org.apache.commons.net.ntp;
002:
003: /*
004: * Copyright 2001-2005 The Apache Software Foundation
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018:
019: import java.util.List;
020: import java.util.ArrayList;
021:
022: /**
023: * Wrapper class to network time packet messages (NTP, etc) that computes
024: * related timing info and stats.
025: *
026: * @author Jason Mathews, MITRE Corp
027: *
028: * @version $Revision: 165675 $ $Date: 2005-05-02 15:09:55 -0500 (Mon, 02 May 2005) $
029: */
030: public class TimeInfo {
031:
032: private NtpV3Packet _message;
033: private List _comments;
034: private Long _delay;
035: private Long _offset;
036:
037: /**
038: * time at which time message packet was received by local machine
039: */
040: private long _returnTime;
041:
042: /**
043: * flag indicating that the TimeInfo details was processed and delay/offset were computed
044: */
045: private boolean _detailsComputed;
046:
047: /**
048: * Create TimeInfo object with raw packet message and destination time received.
049: *
050: * @param message NTP message packet
051: * @param returnTime destination receive time
052: * @throws IllegalArgumentException if message is null
053: */
054: public TimeInfo(NtpV3Packet message, long returnTime) {
055: this (message, returnTime, null, true);
056: }
057:
058: /**
059: * Create TimeInfo object with raw packet message and destination time received.
060: *
061: * @param message NTP message packet
062: * @param returnTime destination receive time
063: * @param comments List of errors/warnings identified during processing
064: * @throws IllegalArgumentException if message is null
065: */
066: public TimeInfo(NtpV3Packet message, long returnTime, List comments) {
067: this (message, returnTime, comments, true);
068: }
069:
070: /**
071: * Create TimeInfo object with raw packet message and destination time received.
072: * Auto-computes details if computeDetails flag set otherwise this is delayed
073: * until computeDetails() is called. Delayed computation is for fast
074: * intialization when sub-millisecond timing is needed.
075: *
076: * @param msgPacket NTP message packet
077: * @param returnTime destination receive time
078: * @param doComputeDetails flag to pre-compute delay/offset values
079: * @throws IllegalArgumentException if message is null
080: */
081: public TimeInfo(NtpV3Packet msgPacket, long returnTime,
082: boolean doComputeDetails) {
083: this (msgPacket, returnTime, null, doComputeDetails);
084: }
085:
086: /**
087: * Create TimeInfo object with raw packet message and destination time received.
088: * Auto-computes details if computeDetails flag set otherwise this is delayed
089: * until computeDetails() is called. Delayed computation is for fast
090: * intialization when sub-millisecond timing is needed.
091: *
092: * @param message NTP message packet
093: * @param returnTime destination receive time
094: * @param comments list of comments used to store errors/warnings with message
095: * @param doComputeDetails flag to pre-compute delay/offset values
096: * @throws IllegalArgumentException if message is null
097: */
098: public TimeInfo(NtpV3Packet message, long returnTime,
099: List comments, boolean doComputeDetails) {
100: if (message == null)
101: throw new IllegalArgumentException("message cannot be null");
102: this ._returnTime = returnTime;
103: this ._message = message;
104: this ._comments = comments;
105: if (doComputeDetails)
106: computeDetails();
107: }
108:
109: /**
110: * Add comment (error/warning) to list of comments associated
111: * with processing of NTP parameters. If comment list not create
112: * then one will be created.
113: *
114: * @param comment
115: */
116: public void addComment(String comment) {
117: if (_comments == null) {
118: _comments = new ArrayList();
119: }
120: _comments.add(comment);
121: }
122:
123: /**
124: * Compute and validate details of the NTP message packet. Computed
125: * fields include the offset and delay.
126: */
127: public void computeDetails() {
128: if (_detailsComputed) {
129: return; // details already computed - do nothing
130: }
131: _detailsComputed = true;
132: if (_comments == null) {
133: _comments = new ArrayList();
134: }
135:
136: TimeStamp origNtpTime = _message.getOriginateTimeStamp();
137: long origTime = origNtpTime.getTime();
138:
139: // Receive Time is time request received by server (t2)
140: TimeStamp rcvNtpTime = _message.getReceiveTimeStamp();
141: long rcvTime = rcvNtpTime.getTime();
142:
143: // Transmit time is time reply sent by server (t3)
144: TimeStamp xmitNtpTime = _message.getTransmitTimeStamp();
145: long xmitTime = xmitNtpTime.getTime();
146:
147: /*
148: * Round-trip network delay and local clock offset (or time drift) is calculated
149: * according to this standard NTP equation:
150: *
151: * LocalClockOffset = ((ReceiveTimestamp - OriginateTimestamp) +
152: * (TransmitTimestamp - DestinationTimestamp)) / 2
153: *
154: * equations from RFC-1305 (NTPv3)
155: * roundtrip delay = (t4 - t1) - (t3 - t2)
156: * local clock offset = ((t2 - t1) + (t3 - t4)) / 2
157: *
158: * It takes into account network delays and assumes that they are symmetrical.
159: *
160: * Note the typo in SNTP RFCs 1769/2030 which state that the delay
161: * is (T4 - T1) - (T2 - T3) with the "T2" and "T3" switched.
162: */
163: if (origNtpTime.ntpValue() == 0) {
164: // without originate time cannot determine when packet went out
165: // might be via a broadcast NTP packet...
166: if (xmitNtpTime.ntpValue() != 0) {
167: _offset = new Long(xmitTime - _returnTime);
168: _comments
169: .add("Error: zero orig time -- cannot compute delay");
170: } else
171: _comments
172: .add("Error: zero orig time -- cannot compute delay/offset");
173: } else if (rcvNtpTime.ntpValue() == 0
174: || xmitNtpTime.ntpValue() == 0) {
175: _comments.add("Warning: zero rcvNtpTime or xmitNtpTime");
176: // assert destTime >= origTime since network delay cannot be negative
177: if (origTime > _returnTime)
178: _comments.add("Error: OrigTime > DestRcvTime");
179: else {
180: // without receive or xmit time cannot figure out processing time
181: // so delay is simply the network travel time
182: _delay = new Long(_returnTime - origTime);
183: }
184: // TODO: is offset still valid if rcvNtpTime=0 || xmitNtpTime=0 ???
185: // Could always hash origNtpTime (sendTime) but if host doesn't set it
186: // then it's an malformed ntp host anyway and we don't care?
187: // If server is in broadcast mode then we never send out a query in first place...
188: if (rcvNtpTime.ntpValue() != 0) {
189: // xmitTime is 0 just use rcv time
190: _offset = new Long(rcvTime - origTime);
191: } else if (xmitNtpTime.ntpValue() != 0) {
192: // rcvTime is 0 just use xmitTime time
193: _offset = new Long(xmitTime - _returnTime);
194: }
195: } else {
196: long delayValue = _returnTime - origTime;
197: // assert xmitTime >= rcvTime: difference typically < 1ms
198: if (xmitTime < rcvTime) {
199: // server cannot send out a packet before receiving it...
200: _comments.add("Error: xmitTime < rcvTime"); // time-travel not allowed
201: } else {
202: // subtract processing time from round-trip network delay
203: long delta = xmitTime - rcvTime;
204: // in normal cases the processing delta is less than
205: // the total roundtrip network travel time.
206: if (delta <= delayValue) {
207: delayValue -= delta; // delay = (t4 - t1) - (t3 - t2)
208: } else {
209: // if delta - delayValue == 1 ms then it's a round-off error
210: // e.g. delay=3ms, processing=4ms
211: if (delta - delayValue == 1) {
212: // delayValue == 0 -> local clock saw no tick change but destination clock did
213: if (delayValue != 0) {
214: _comments
215: .add("Info: processing time > total network time by 1 ms -> assume zero delay");
216: delayValue = 0;
217: }
218: } else
219: _comments
220: .add("Warning: processing time > total network time");
221: }
222: }
223: _delay = new Long(delayValue);
224: if (origTime > _returnTime) // assert destTime >= origTime
225: _comments.add("Error: OrigTime > DestRcvTime");
226:
227: _offset = new Long(
228: ((rcvTime - origTime) + (xmitTime - _returnTime)) / 2);
229: }
230: }
231:
232: /**
233: * Return list of comments (if any) during processing of NTP packet.
234: *
235: * @return List or null if not yet computed
236: */
237: public List getComments() {
238: return _comments;
239: }
240:
241: /**
242: * Get round-trip network delay. If null then could not compute the delay.
243: *
244: * @return Long or null if delay not available.
245: */
246: public Long getDelay() {
247: return _delay;
248: }
249:
250: /**
251: * Get clock offset needed to adjust local clock to match remote clock. If null then could not
252: * compute the offset.
253: *
254: * @return Long or null if offset not available.
255: */
256: public Long getOffset() {
257: return _offset;
258: }
259:
260: /**
261: * Returns NTP message packet.
262: *
263: * @return NTP message packet.
264: */
265: public NtpV3Packet getMessage() {
266: return _message;
267: }
268:
269: /**
270: * Returns time at which time message packet was received by local machine.
271: *
272: * @return packet return time.
273: */
274: public long getReturnTime() {
275: return _returnTime;
276: }
277:
278: }
|