View Javadoc

1   package org.apache.commons.net.ntp;
2   /*
3    * Copyright 2001-2005 The Apache Software Foundation
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License");
6    * you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  import java.util.List;
19  import java.util.ArrayList;
20  
21  /**
22   * Wrapper class to network time packet messages (NTP, etc) that computes
23   * related timing info and stats.
24   *
25   * @author Jason Mathews, MITRE Corp
26   *
27   * @version $Revision: 165675 $ $Date: 2005-05-02 15:09:55 -0500 (Mon, 02 May 2005) $
28   */
29  public class TimeInfo {
30  
31      private NtpV3Packet _message;
32      private List _comments;
33      private Long _delay;
34      private Long _offset;
35  
36      /**
37       * time at which time message packet was received by local machine
38       */
39      private long _returnTime;
40  
41      /**
42       * flag indicating that the TimeInfo details was processed and delay/offset were computed
43       */
44      private boolean _detailsComputed;
45  
46      /**
47       * Create TimeInfo object with raw packet message and destination time received.
48       *
49       * @param message NTP message packet
50       * @param returnTime  destination receive time
51       * @throws IllegalArgumentException if message is null
52       */
53      public TimeInfo(NtpV3Packet message, long returnTime) {
54          this(message, returnTime, null, true);
55      }
56  
57      /**
58       * Create TimeInfo object with raw packet message and destination time received.
59       *
60       * @param message NTP message packet
61       * @param returnTime  destination receive time
62       * @param comments List of errors/warnings identified during processing
63       * @throws IllegalArgumentException if message is null
64       */
65      public TimeInfo(NtpV3Packet message, long returnTime, List comments)
66      {
67              this(message, returnTime, comments, true);
68      }
69  
70      /**
71       * Create TimeInfo object with raw packet message and destination time received.
72       * Auto-computes details if computeDetails flag set otherwise this is delayed
73       * until computeDetails() is called. Delayed computation is for fast
74       * intialization when sub-millisecond timing is needed.
75       *
76       * @param msgPacket NTP message packet
77       * @param returnTime  destination receive time
78       * @param doComputeDetails  flag to pre-compute delay/offset values
79       * @throws IllegalArgumentException if message is null
80       */
81      public TimeInfo(NtpV3Packet msgPacket, long returnTime, boolean doComputeDetails)
82      {
83              this(msgPacket, returnTime, null, doComputeDetails);
84      }
85  
86      /**
87       * Create TimeInfo object with raw packet message and destination time received.
88       * Auto-computes details if computeDetails flag set otherwise this is delayed
89       * until computeDetails() is called. Delayed computation is for fast
90       * intialization when sub-millisecond timing is needed.
91       *
92       * @param message NTP message packet
93       * @param returnTime  destination receive time
94       * @param comments  list of comments used to store errors/warnings with message
95       * @param doComputeDetails  flag to pre-compute delay/offset values
96       * @throws IllegalArgumentException if message is null
97       */
98      public TimeInfo(NtpV3Packet message, long returnTime, List comments,
99                     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 }