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: 658518 $ $Date: 2008-05-21 02:04:30 +0100 (Wed, 21 May 2008) $
30   */
31  public class TimeInfo {
32  
33      private 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 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         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 }