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