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 }