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