    001package 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
    020import java.util.ArrayList;
    021import 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: 1299238 $
    030 */
    031public 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        }
    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}