View Javadoc

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 }