001    package org.apache.commons.net.ntp;
002    /*
003     * Copyright 2001-2005 The Apache Software Foundation
004     *
005     * Licensed under the Apache License, Version 2.0 (the "License");
006     * you may not use this file except in compliance with the License.
007     * You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    import java.util.List;
019    import java.util.ArrayList;
020    
021    /**
022     * Wrapper class to network time packet messages (NTP, etc) that computes
023     * related timing info and stats.
024     *
025     * @author Jason Mathews, MITRE Corp
026     *
027     * @version $Revision: 165675 $ $Date: 2005-05-02 21:09:55 +0100 (Mon, 02 May 2005) $
028     */
029    public class TimeInfo {
030    
031        private NtpV3Packet _message;
032        private List _comments;
033        private Long _delay;
034        private Long _offset;
035    
036        /**
037         * time at which time message packet was received by local machine
038         */
039        private long _returnTime;
040    
041        /**
042         * flag indicating that the TimeInfo details was processed and delay/offset were computed
043         */
044        private boolean _detailsComputed;
045    
046        /**
047         * Create TimeInfo object with raw packet message and destination time received.
048         *
049         * @param message NTP message packet
050         * @param returnTime  destination receive time
051         * @throws IllegalArgumentException if message is null
052         */
053        public TimeInfo(NtpV3Packet message, long returnTime) {
054            this(message, returnTime, null, true);
055        }
056    
057        /**
058         * Create TimeInfo object with raw packet message and destination time received.
059         *
060         * @param message NTP message packet
061         * @param returnTime  destination receive time
062         * @param comments List of errors/warnings identified during processing
063         * @throws IllegalArgumentException if message is null
064         */
065        public TimeInfo(NtpV3Packet message, long returnTime, List comments)
066        {
067                this(message, returnTime, comments, true);
068        }
069    
070        /**
071         * Create TimeInfo object with raw packet message and destination time received.
072         * Auto-computes details if computeDetails flag set otherwise this is delayed
073         * until computeDetails() is called. Delayed computation is for fast
074         * intialization when sub-millisecond timing is needed.
075         *
076         * @param msgPacket NTP message packet
077         * @param returnTime  destination receive time
078         * @param doComputeDetails  flag to pre-compute delay/offset values
079         * @throws IllegalArgumentException if message is null
080         */
081        public TimeInfo(NtpV3Packet msgPacket, long returnTime, boolean doComputeDetails)
082        {
083                this(msgPacket, returnTime, null, doComputeDetails);
084        }
085    
086        /**
087         * Create TimeInfo object with raw packet message and destination time received.
088         * Auto-computes details if computeDetails flag set otherwise this is delayed
089         * until computeDetails() is called. Delayed computation is for fast
090         * intialization when sub-millisecond timing is needed.
091         *
092         * @param message NTP message packet
093         * @param returnTime  destination receive time
094         * @param comments  list of comments used to store errors/warnings with message
095         * @param doComputeDetails  flag to pre-compute delay/offset values
096         * @throws IllegalArgumentException if message is null
097         */
098        public TimeInfo(NtpV3Packet message, long returnTime, List comments,
099                       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    }