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: 1299238 $ 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 } 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 }