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