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