1 // 2 // This file is part of the OpenNMS(R) Application. 3 // 4 // OpenNMS(R) is Copyright (C) 2002-2003 The OpenNMS Group, Inc. All rights reserved. 5 // OpenNMS(R) is a derivative work, containing both original code, included code and modified 6 // code that was published under the GNU General Public License. Copyrights for modified 7 // and included code are below. 8 // 9 // OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc. 10 // 11 // Modifications: 12 // 13 // 2003 Mar 05: Cleaned up some ICMP related code. 14 // 2003 Jan 31: Cleaned up some unused imports. 15 // 2002 Nov 13: Added response time stats for ICMP requests. 16 // 17 // Original code base Copyright (C) 1999-2001 Oculan Corp. All rights reserved. 18 // 19 // This program is free software; you can redistribute it and/or modify 20 // it under the terms of the GNU General Public License as published by 21 // the Free Software Foundation; either version 2 of the License, or 22 // (at your option) any later version. 23 // 24 // This program is distributed in the hope that it will be useful, 25 // but WITHOUT ANY WARRANTY; without even the implied warranty of 26 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 // GNU General Public License for more details. 28 // 29 // You should have received a copy of the GNU General Public License 30 // along with this program; if not, write to the Free Software 31 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 32 // 33 // For more information contact: 34 // OpenNMS Licensing <license@opennms.org> 35 // http://www.opennms.org/ 36 // http://www.opennms.com/ 37 // 38 // Tab Size = 8 39 // 40 41 package org.opennms.protocols.icmp; 42 43 import org.opennms.protocols.ip.OC16ChecksumProducer; 44 45 /** 46 * The ping packet for discovery 47 * 48 * @author <A HREF="mailto:weave@oculan.com">Brian Weaver </A> 49 * @author <A HREF="mailto:sowmya@opennms.org">Sowmya </A> 50 * @author <A HREF="http://www.opennms.org/">OpenNMS </A> 51 * 52 * @version 1.1.1.1 53 * 54 */ 55 public final class ICMPEchoPacket extends ICMPHeader { 56 /** 57 * Unique named padding that is placed in front of the incremental padding. 58 */ 59 private static final byte NAMED_PAD[] = { (byte) 'O', (byte) 'p', (byte) 'e', (byte) 'n', (byte) 'N', (byte) 'M', (byte) 'S', (byte) '!' }; 60 61 /** 62 * Timestamp when packet was sent 63 */ 64 private long m_sent; 65 66 /** 67 * Timestamp of when packet was received. 68 */ 69 private long m_recv; 70 71 /** 72 * The thread id of the sender. Effective key for the packet. 73 */ 74 private long m_tid; // thread id 75 76 /** 77 * Padding used to make the packet conform to the defacto unix ping program 78 * (56 bytes) or whatever packetsize is sent in 79 */ 80 private byte[] m_pad; 81 82 /** 83 * The ping rtt (microseconds) 84 */ 85 private long m_rtt; 86 87 /** 88 * Converts a byte to a long and wraps the value to avoid sign extension. 89 * The method essentially treats the value of 'b' as an 8-bit unsigned value 90 * for conversion purposes. 91 * 92 * @param b 93 * The byte to convert. 94 * 95 * @return The converted long value. 96 */ byteToLong(byte b)97 static private long byteToLong(byte b) { 98 long r = (long) b; 99 if (r < 0) 100 r += 256; 101 return r; 102 } 103 104 /** 105 * Private constructor to disallow default construction of an object. 106 * 107 * @exception java.lang.UnsupportedOperationException 108 * Always thrown. 109 */ 110 @SuppressWarnings("unused") ICMPEchoPacket()111 private ICMPEchoPacket() { 112 throw new java.lang.UnsupportedOperationException("illegal constructor call"); 113 } 114 115 /** 116 * Creates a new discovery ping packet that can be sent to a remote protocol 117 * stack. The ICMP type is set to an Echo Request. The next sequence in the 118 * ICMPHeader base class is set and the sent time is set to the current 119 * time. 120 * 121 * @param tid 122 * The thread id for the packet. 123 * 124 * @see java.lang.System#currentTimeMillis 125 */ ICMPEchoPacket(long tid)126 public ICMPEchoPacket(long tid) { 127 this(tid, 64); 128 } 129 130 /** 131 * Creates a new discovery ping packet that can be sent to a remote protocol 132 * stack. The ICMP type is set to an Echo Request. The next sequence in the 133 * ICMPHeader base class is set and the sent time is set to the current 134 * time. 135 * 136 * @param tid 137 * The thread id for the packet. 138 * @param packetsize 139 * The pad size in bytes 140 * @see java.lang.System#currentTimeMillis 141 */ ICMPEchoPacket(long tid, int packetsize)142 public ICMPEchoPacket(long tid, int packetsize) { 143 super(ICMPHeader.TYPE_ECHO_REQUEST, (byte) 0); 144 setNextSequenceId(); 145 146 m_rtt = 0; 147 m_sent = 0; 148 m_recv = 0; 149 m_tid = tid; 150 151 if (packetsize < getMinimumNetworkSize()) { 152 throw new IllegalArgumentException("Minimum size for a ICMPEchoPacket is " + getMinimumNetworkSize() + " bytes."); 153 } 154 155 m_pad = new byte[packetsize - getMinimumNetworkSize()]; 156 for (int x = 0; x < NAMED_PAD.length && x < m_pad.length; x++) 157 m_pad[x] = NAMED_PAD[x]; 158 for (int x = NAMED_PAD.length; x < m_pad.length; x++) 159 m_pad[x] = (byte) 0; 160 161 } 162 163 164 /** 165 * Creates a new discovery ping packet from the passed buffer. 166 * 167 * @param buf 168 * The buffer containing a refected ping packet. 169 */ ICMPEchoPacket(byte[] buf)170 public ICMPEchoPacket(byte[] buf) { 171 loadFromBuffer(buf, 0); 172 } 173 174 /** 175 * Returns the time the packet was sent. 176 */ getSentTime()177 public final long getSentTime() { 178 return m_sent; 179 } 180 181 /** 182 * Sets the sent time to the current time. 183 * 184 * @see java.lang.System#currentTimeMillis 185 */ setSentTime()186 public final long setSentTime() { 187 m_sent = System.currentTimeMillis(); 188 return m_sent; 189 } 190 191 /** 192 * Sets the sent time to the passed value. 193 * 194 * @param time 195 * The new sent time. 196 */ setSentTime(long time)197 public final void setSentTime(long time) { 198 m_sent = time; 199 } 200 201 /** 202 * Gets the currently set received time. 203 */ getReceivedTime()204 public final long getReceivedTime() { 205 return m_recv; 206 } 207 208 /** 209 * Sets the recieved time for the packet. 210 * 211 * @see java.lang.System#currentTimeMillis 212 */ setReceivedTime()213 public final long setReceivedTime() { 214 m_recv = System.currentTimeMillis(); 215 return m_recv; 216 } 217 218 /** 219 * Sets the received time to the passed value. 220 * 221 * @param time 222 * The new received time. 223 * 224 */ setReceivedTime(long time)225 public final void setReceivedTime(long time) { 226 m_recv = time; 227 } 228 229 /** 230 * Sets the ping Round Trip Time 231 */ setPingRTT(long time)232 public final void setPingRTT(long time) { 233 m_rtt = time; 234 } 235 236 /** 237 * Gets the ping Round Trip Time 238 */ getPingRTT()239 public final long getPingRTT() { 240 return m_rtt; 241 } 242 243 /** 244 * Returns the size of the integer headers in packet 245 */ getDataSize()246 public int getDataSize() { 247 return (getHeaderSize() + 32); 248 } 249 250 /** 251 * Returns the size of the integer headers in the packet plus the required 'OpenNMS!' string. 252 */ getMinimumNetworkSize()253 public int getMinimumNetworkSize() { 254 return (getDataSize() + NAMED_PAD.length); 255 } 256 257 /** 258 * Useless function with variable packet sizes but preserved for backwards compatability 259 * @return 260 */ 261 @Deprecated getNetworkSize()262 public static final int getNetworkSize() { 263 return ICMPHeader.getNetworkSize() + 32 + 16; 264 } 265 266 getPacketSize()267 public int getPacketSize() { 268 return getDataSize() + m_pad.length; 269 } 270 271 /** 272 * Computes and stores the current checksum based upon the data currently 273 * contained in the object. 274 */ computeChecksum()275 public final void computeChecksum() { 276 OC16ChecksumProducer summer = new OC16ChecksumProducer(); 277 278 super.computeChecksum(summer); 279 summer.add(m_rtt); 280 summer.add(m_sent); 281 summer.add(m_recv); 282 summer.add(m_tid); 283 284 // 285 // do all the elements combining two bytes 286 // into a single short. 287 // 288 int stop = m_pad.length - (m_pad.length % 2); 289 for (int i = 0; i < stop; i += 2) 290 summer.add(m_pad[i], m_pad[i + 1]); 291 292 // 293 // take care of any stray bytes 294 // 295 if ((m_pad.length % 2) == 1) 296 summer.add(m_pad[m_pad.length - 1]); 297 298 // 299 // set the checksum in the header 300 // 301 super.setChecksum(summer.getChecksum()); 302 } 303 304 /** 305 * Returns the currently set Thread ID 306 */ getTID()307 public final long getTID() { 308 return m_tid; 309 } 310 311 /** 312 * Sets the current Thread Id 313 */ setTID(long tid)314 public final void setTID(long tid) { 315 m_tid = tid; 316 } 317 318 /** 319 * Loads the data from the passed buffer into the current object. Once 320 * loaded the object's values should reflect the contents of the buffer. 321 * 322 * @param buf 323 * The buffer to load from 324 * @param offset 325 * The offset to begin loading from 326 * 327 * @return The offset of the next byte of data that was not used to 328 * initialize this object. 329 * 330 * @exception java.lang.IndexOutOfBoundsException 331 * Thrown if there is not enough data contained in the buffer 332 * to sufficent set the state of the object 333 * 334 */ loadFromBuffer(byte[] buf, int offset)335 public final int loadFromBuffer(byte[] buf, int offset) { 336 if ((buf.length - offset) < getMinimumNetworkSize()) 337 throw new IndexOutOfBoundsException("Insufficient Data: packet must be at least " + getMinimumNetworkSize() + " bytes long."); 338 339 offset = super.loadFromBuffer(buf, offset); 340 if (!isEchoReply() && !isEchoRequest()) 341 throw new IllegalArgumentException("Invalid type, must be echo request/reply packet"); 342 343 m_sent = 0; 344 for (int x = 0; x < 8; x++) { 345 m_sent <<= 8; 346 m_sent |= byteToLong(buf[offset++]); 347 } 348 349 m_recv = 0; 350 for (int x = 0; x < 8; x++) { 351 m_recv <<= 8; 352 m_recv |= byteToLong(buf[offset++]); 353 } 354 355 m_tid = 0; 356 for (int x = 0; x < 8; x++) { 357 m_tid <<= 8; 358 m_tid |= byteToLong(buf[offset++]); 359 } 360 361 m_rtt = 0; 362 for (int x = 0; x < 8; x++) { 363 m_rtt <<= 8; 364 m_rtt |= byteToLong(buf[offset++]); 365 } 366 367 // skip over the header and timestamp data 368 int remainingBytes = buf.length - getDataSize(); 369 if (m_pad == null || m_pad.length != remainingBytes) { 370 m_pad = new byte[remainingBytes]; 371 } 372 373 for (int x = 0; x < m_pad.length; x++) { 374 m_pad[x] = buf[offset++]; 375 } 376 377 return offset; 378 } 379 380 /** 381 * Writes the objects data out to the specified buffer at the starting 382 * offset. If the buffer does not have sufficent data to store the 383 * information then an IndexOutOfBoundsException is thrown. 384 * 385 * @param buf 386 * The storage buffer. 387 * @param offset 388 * The location to start in buf. 389 * 390 * @return The new offset after storing to the buffer. 391 * 392 * @exception IndexOutOfBoundsException 393 * Thrown if the buffer does not have enough storage space. 394 * 395 */ storeToBuffer(byte[] buf, int offset)396 public final int storeToBuffer(byte[] buf, int offset) { 397 if ((buf.length - offset) < getPacketSize()) { 398 throw new IndexOutOfBoundsException("Insufficient Buffer Size. Need at least " + getPacketSize() + " bytes to store packet."); 399 } 400 401 offset = super.storeToBuffer(buf, offset); 402 403 long t = m_sent; 404 for (int x = 0; x < 8; x++) { 405 buf[offset++] = (byte) (t >>> 56); 406 t <<= 8; 407 } 408 409 t = m_recv; 410 for (int x = 0; x < 8; x++) { 411 buf[offset++] = (byte) (t >>> 56); 412 t <<= 8; 413 } 414 415 t = m_tid; 416 for (int x = 0; x < 8; x++) { 417 buf[offset++] = (byte) (t >>> 56); 418 t <<= 8; 419 } 420 421 t = m_rtt; 422 for (int x = 0; x < 8; x++) { 423 buf[offset++] = (byte) (t >>> 56); 424 t <<= 8; 425 } 426 427 for (int x = 0; x < m_pad.length; x++) { 428 buf[offset++] = m_pad[x]; 429 } 430 431 return offset; 432 } 433 434 /** 435 * Converts the object into an array of bytes which is suitable for 436 * transmission to remote hosts. 437 * 438 * @return The object as an array of bytes. 439 */ toBytes()440 public final byte[] toBytes() { 441 byte[] buf = new byte[getPacketSize()]; 442 storeToBuffer(buf, 0); 443 return buf; 444 } 445 446 } // end Packet. 447