1 package org.apache.commons.net.ntp;
2 /*
3 * Licensed to the Apache Software Foundation (ASF) under one or more
4 * contributor license agreements. See the NOTICE file distributed with
5 * this work for additional information regarding copyright ownership.
6 * The ASF licenses this file to You under the Apache License, Version 2.0
7 * (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19
20 import java.util.ArrayList;
21 import java.util.List;
22
23 /**
24 * Wrapper class to network time packet messages (NTP, etc) that computes
25 * related timing info and stats.
26 *
27 * @author Jason Mathews, MITRE Corp
28 *
29 * @version $Revision: 1299238 $
30 */
31 public class TimeInfo {
32
33 private final NtpV3Packet _message;
34 private List<String> _comments;
35 private Long _delay;
36 private Long _offset;
37
38 /**
39 * time at which time message packet was received by local machine
40 */
41 private final long _returnTime;
42
43 /**
44 * flag indicating that the TimeInfo details was processed and delay/offset were computed
45 */
46 private boolean _detailsComputed;
47
48 /**
49 * Create TimeInfo object with raw packet message and destination time received.
50 *
51 * @param message NTP message packet
52 * @param returnTime destination receive time
53 * @throws IllegalArgumentException if message is null
54 */
55 public TimeInfo(NtpV3Packet message, long returnTime) {
56 this(message, returnTime, null, true);
57 }
58
59 /**
60 * Create TimeInfo object with raw packet message and destination time received.
61 *
62 * @param message NTP message packet
63 * @param returnTime destination receive time
64 * @param comments List of errors/warnings identified during processing
65 * @throws IllegalArgumentException if message is null
66 */
67 public TimeInfo(NtpV3Packet message, long returnTime, List<String> comments)
68 {
69 this(message, returnTime, comments, true);
70 }
71
72 /**
73 * Create TimeInfo object with raw packet message and destination time received.
74 * Auto-computes details if computeDetails flag set otherwise this is delayed
75 * until computeDetails() is called. Delayed computation is for fast
76 * intialization when sub-millisecond timing is needed.
77 *
78 * @param msgPacket NTP message packet
79 * @param returnTime destination receive time
80 * @param doComputeDetails flag to pre-compute delay/offset values
81 * @throws IllegalArgumentException if message is null
82 */
83 public TimeInfo(NtpV3Packet msgPacket, long returnTime, boolean doComputeDetails)
84 {
85 this(msgPacket, returnTime, null, doComputeDetails);
86 }
87
88 /**
89 * Create TimeInfo object with raw packet message and destination time received.
90 * Auto-computes details if computeDetails flag set otherwise this is delayed
91 * until computeDetails() is called. Delayed computation is for fast
92 * intialization when sub-millisecond timing is needed.
93 *
94 * @param message NTP message packet
95 * @param returnTime destination receive time
96 * @param comments list of comments used to store errors/warnings with message
97 * @param doComputeDetails flag to pre-compute delay/offset values
98 * @throws IllegalArgumentException if message is null
99 */
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 }
106 this._returnTime = returnTime;
107 this._message = message;
108 this._comments = comments;
109 if (doComputeDetails) {
110 computeDetails();
111 }
112 }
113
114 /**
115 * Add comment (error/warning) to list of comments associated
116 * with processing of NTP parameters. If comment list not create
117 * then one will be created.
118 *
119 * @param comment
120 */
121 public void addComment(String comment)
122 {
123 if (_comments == null) {
124 _comments = new ArrayList<String>();
125 }
126 _comments.add(comment);
127 }
128
129 /**
130 * Compute and validate details of the NTP message packet. Computed
131 * fields include the offset and delay.
132 */
133 public void computeDetails()
134 {
135 if (_detailsComputed) {
136 return; // details already computed - do nothing
137 }
138 _detailsComputed = true;
139 if (_comments == null) {
140 _comments = new ArrayList<String>();
141 }
142
143 TimeStamp origNtpTime = _message.getOriginateTimeStamp();
144 long origTime = origNtpTime.getTime();
145
146 // Receive Time is time request received by server (t2)
147 TimeStamp rcvNtpTime = _message.getReceiveTimeStamp();
148 long rcvTime = rcvNtpTime.getTime();
149
150 // Transmit time is time reply sent by server (t3)
151 TimeStamp xmitNtpTime = _message.getTransmitTimeStamp();
152 long xmitTime = xmitNtpTime.getTime();
153
154 /*
155 * Round-trip network delay and local clock offset (or time drift) is calculated
156 * according to this standard NTP equation:
157 *
158 * LocalClockOffset = ((ReceiveTimestamp - OriginateTimestamp) +
159 * (TransmitTimestamp - DestinationTimestamp)) / 2
160 *
161 * equations from RFC-1305 (NTPv3)
162 * roundtrip delay = (t4 - t1) - (t3 - t2)
163 * local clock offset = ((t2 - t1) + (t3 - t4)) / 2
164 *
165 * It takes into account network delays and assumes that they are symmetrical.
166 *
167 * Note the typo in SNTP RFCs 1769/2030 which state that the delay
168 * is (T4 - T1) - (T2 - T3) with the "T2" and "T3" switched.
169 */
170 if (origNtpTime.ntpValue() == 0)
171 {
172 // without originate time cannot determine when packet went out
173 // might be via a broadcast NTP packet...
174 if (xmitNtpTime.ntpValue() != 0)
175 {
176 _offset = Long.valueOf(xmitTime - _returnTime);
177 _comments.add("Error: zero orig time -- cannot compute delay");
178 } else {
179 _comments.add("Error: zero orig time -- cannot compute delay/offset");
180 }
181 } else if (rcvNtpTime.ntpValue() == 0 || xmitNtpTime.ntpValue() == 0) {
182 _comments.add("Warning: zero rcvNtpTime or xmitNtpTime");
183 // assert destTime >= origTime since network delay cannot be negative
184 if (origTime > _returnTime) {
185 _comments.add("Error: OrigTime > DestRcvTime");
186 } else {
187 // without receive or xmit time cannot figure out processing time
188 // so delay is simply the network travel time
189 _delay = Long.valueOf(_returnTime - origTime);
190 }
191 // TODO: is offset still valid if rcvNtpTime=0 || xmitNtpTime=0 ???
192 // Could always hash origNtpTime (sendTime) but if host doesn't set it
193 // then it's an malformed ntp host anyway and we don't care?
194 // If server is in broadcast mode then we never send out a query in first place...
195 if (rcvNtpTime.ntpValue() != 0)
196 {
197 // xmitTime is 0 just use rcv time
198 _offset = Long.valueOf(rcvTime - origTime);
199 } else if (xmitNtpTime.ntpValue() != 0)
200 {
201 // rcvTime is 0 just use xmitTime time
202 _offset = Long.valueOf(xmitTime - _returnTime);
203 }
204 } else
205 {
206 long delayValue = _returnTime - origTime;
207 // assert xmitTime >= rcvTime: difference typically < 1ms
208 if (xmitTime < rcvTime)
209 {
210 // server cannot send out a packet before receiving it...
211 _comments.add("Error: xmitTime < rcvTime"); // time-travel not allowed
212 } else
213 {
214 // subtract processing time from round-trip network delay
215 long delta = xmitTime - rcvTime;
216 // in normal cases the processing delta is less than
217 // the total roundtrip network travel time.
218 if (delta <= delayValue)
219 {
220 delayValue -= delta; // delay = (t4 - t1) - (t3 - t2)
221 } else
222 {
223 // if delta - delayValue == 1 ms then it's a round-off error
224 // e.g. delay=3ms, processing=4ms
225 if (delta - delayValue == 1)
226 {
227 // delayValue == 0 -> local clock saw no tick change but destination clock did
228 if (delayValue != 0)
229 {
230 _comments.add("Info: processing time > total network time by 1 ms -> assume zero delay");
231 delayValue = 0;
232 }
233 } else {
234 _comments.add("Warning: processing time > total network time");
235 }
236 }
237 }
238 _delay = Long.valueOf(delayValue);
239 if (origTime > _returnTime) {
240 _comments.add("Error: OrigTime > DestRcvTime");
241 }
242
243 _offset = Long.valueOf(((rcvTime - origTime) + (xmitTime - _returnTime)) / 2);
244 }
245 }
246
247 /**
248 * Return list of comments (if any) during processing of NTP packet.
249 *
250 * @return List or null if not yet computed
251 */
252 public List<String> getComments()
253 {
254 return _comments;
255 }
256
257 /**
258 * Get round-trip network delay. If null then could not compute the delay.
259 *
260 * @return Long or null if delay not available.
261 */
262 public Long getDelay()
263 {
264 return _delay;
265 }
266
267 /**
268 * Get clock offset needed to adjust local clock to match remote clock. If null then could not
269 * compute the offset.
270 *
271 * @return Long or null if offset not available.
272 */
273 public Long getOffset()
274 {
275 return _offset;
276 }
277
278 /**
279 * Returns NTP message packet.
280 *
281 * @return NTP message packet.
282 */
283 public NtpV3Packet getMessage()
284 {
285 return _message;
286 }
287
288 /**
289 * Returns time at which time message packet was received by local machine.
290 *
291 * @return packet return time.
292 */
293 public long getReturnTime()
294 {
295 return _returnTime;
296 }
297
298 }