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 }