/* * $Id: IPPacket.java 8344 2009-01-14 19:14:20Z dfs $ * * Copyright 2004-2005 Daniel F. Savarese * Contact Information: http://www.savarese.org/contact.html * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.savarese.org/software/ApacheLicense-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.savarese.vserv.tcpip; import java.net.*; /** * IPPacket wraps the raw bytes comprising an IPv4 packet and exposes * its content via setter and getter methods. After you alter the * header of an IP packet you have to recompute the checksum with * {@link #computeIPChecksum computeIPChecksum()}. The structure of * IP packets is described in * <a href="http://www.ietf.org/rfc/rfc0760.txt?number=760">RFC 760</a>. * * @author <a href="http://www.savarese.org/">Daniel F. Savarese</a> */ public class IPPacket { /** Offset into byte array of the type of service header value. */ public static final int OFFSET_TYPE_OF_SERVICE = 1; /** Offset into byte array of total packet length header value. */ public static final int OFFSET_TOTAL_LENGTH = 2; /** Offset into byte array of the identification header value. */ public static final int OFFSET_IDENTIFICATION = 4; /** Offset into byte array of the flags header value. */ public static final int OFFSET_FLAGS = 6; /** Offset into byte array of source address header value. */ public static final int OFFSET_SOURCE_ADDRESS = 12; /** Number of bytes in source address. */ public static final int LENGTH_SOURCE_ADDRESS = 4; /** Offset into byte array of destination address header value. */ public static final int OFFSET_DESTINATION_ADDRESS = 16; /** Number of bytes in destination address. */ public static final int LENGTH_DESTINATION_ADDRESS = 4; /** Offset into byte array of time to live header value. */ public static final int OFFSET_TTL = 8; /** Offset into byte array of protocol number header value. */ public static final int OFFSET_PROTOCOL = 9; /** Offset into byte array of header checksum header value. */ public static final int OFFSET_IP_CHECKSUM = 10; /** Protocol constant for IPv4. */ public static final int PROTOCOL_IP = 0; /** Protocol constant for ICMP. */ public static final int PROTOCOL_ICMP = 1; /** Protocol constant for TCP. */ public static final int PROTOCOL_TCP = 6; /** Protocol constant for UDP. */ public static final int PROTOCOL_UDP = 17; /** Raw packet data. */ protected byte[] _data_; /** * Creates a new IPPacket of a given size. * * @param size The number of bytes in the packet. */ public IPPacket(int size) { setData(new byte[size]); } /** * @return The size of the packet. */ public int size() { return _data_.length; } /** * Sets the raw packet byte array. Although this method would * appear to violate object-oriented principles, it is necessary to * implement efficient packet processing. You don't necessarily * want to allocate a new IPPacket and data buffer every time a * packet arrives and you need to be able to wrap packets from * APIs that supply them as byte arrays. * * @param data The raw packet byte array to wrap. */ public void setData(byte[] data) { _data_ = data; } /** * Copies the raw packet data into a byte array. If the array * is too small to hold the data, the data is truncated. * * @param data The raw packet byte array to wrap. */ public void getData(byte[] data) { System.arraycopy(_data_, 0, data, 0, data.length); } /** * Copies the contents of an IPPacket to the calling instance. If * the two packets are of different lengths, a new byte array is * allocated equal to the length of the packet parameter. * * @param packet The packet to copy from. */ public final void copy(IPPacket packet) { if(_data_.length != packet.size()) setData(new byte[packet.size()]); System.arraycopy(packet._data_, 0, _data_, 0, _data_.length); } /** * Sets the IP version header value. * * @param version A 4-bit unsigned integer. */ public final void setIPVersion(int version) { _data_[0] &= 0x0f; _data_[0] |= ((version << 4) & 0xf0); } /** * Returns the IP version header value. * * @return The IP version header value. */ public final int getIPVersion() { return ((_data_[0] & 0xf0) >> 4); } /** * Sets the IP header length field. At most, this can be a * four-bit value. The high order bits beyond the fourth bit * will be ignored. * * @param length The length of the IP header in 32-bit words. */ public void setIPHeaderLength(int length) { // Clear low order bits and then set _data_[0] &= 0xf0; _data_[0] |= (length & 0x0f); } /** * @return The length of the IP header in 32-bit words. */ public final int getIPHeaderLength() { return (_data_[0] & 0x0f); } /** * @return The length of the IP header in bytes. */ public final int getIPHeaderByteLength() { return getIPHeaderLength() << 2; } /** * Sets the IP type of service header value. You have to set the individual * service bits yourself. Convenience methods for setting the service * bit fields directly may be added in a future version. * * @param service An 8-bit unsigned integer. */ public final void setTypeOfService(int service) { _data_[OFFSET_TYPE_OF_SERVICE] = (byte)(service & 0xff); } /** * Returns the IP type of service header value. * * @return The IP type of service header value. */ public final int getTypeOfService() { return (_data_[OFFSET_TYPE_OF_SERVICE] & 0xff); } /** * Sets the IP packet total length header value. * * @param length The total IP packet length in bytes. */ public final void setIPPacketLength(int length) { _data_[OFFSET_TOTAL_LENGTH] = (byte)((length >> 8) & 0xff); _data_[OFFSET_TOTAL_LENGTH + 1] = (byte)(length & 0xff); } /** * @return The IP packet total length header value. */ public final int getIPPacketLength() { return (((_data_[OFFSET_TOTAL_LENGTH] & 0xff) << 8) | (_data_[OFFSET_TOTAL_LENGTH + 1] & 0xff)); } /** * Sets the IP identification header value. * * @param id A 16-bit unsigned integer. */ public void setIdentification(int id) { _data_[OFFSET_IDENTIFICATION] = (byte)((id >> 8) & 0xff); _data_[OFFSET_IDENTIFICATION + 1] = (byte)(id & 0xff); } /** * Returns the IP identification header value. * * @return The IP identification header value. */ public final int getIdentification() { return (((_data_[OFFSET_IDENTIFICATION] & 0xff) << 8) | (_data_[OFFSET_IDENTIFICATION + 1] & 0xff)); } /** * Sets the IP flags header value. You have to set the individual * flag bits yourself. Convenience methods for setting the flag * bit fields directly may be added in a future version. * * @param flags A 3-bit unsigned integer. */ public final void setIPFlags(int flags) { _data_[OFFSET_FLAGS] &= 0x1f; _data_[OFFSET_FLAGS] |= ((flags << 5) & 0xe0); } /** * Returns the IP flags header value. * * @return The IP flags header value. */ public final int getIPFlags() { return ((_data_[OFFSET_FLAGS] & 0xe0) >> 5); } /** * Sets the fragment offset header value. The offset specifies a * number of octets (i.e., bytes). * * @param offset A 13-bit unsigned integer. */ public void setFragmentOffset(int offset) { _data_[OFFSET_FLAGS] &= 0xe0; _data_[OFFSET_FLAGS] |= ((offset >> 8) & 0x1f); _data_[OFFSET_FLAGS + 1] = (byte)(offset & 0xff); } /** * Returns the fragment offset header value. * * @return The fragment offset header value. */ public final int getFragmentOffset() { return (((_data_[OFFSET_FLAGS] & 0x1f) << 8) | (_data_[OFFSET_FLAGS + 1] & 0xff)); } /** * Sets the protocol number. * * @param protocol The protocol number. */ public final void setProtocol(int protocol) { _data_[OFFSET_PROTOCOL] = (byte)protocol; } /** * @return The protocol number. */ public final int getProtocol() { return _data_[OFFSET_PROTOCOL]; } /** * Sets the time to live value in seconds. * * @param ttl The time to live value in seconds. */ public final void setTTL(int ttl) { _data_[OFFSET_TTL] = (byte)(ttl & 0xff); } /** * @return The time to live value in seconds. */ public final int getTTL() { return (_data_[OFFSET_TTL] & 0xff); } /** * Calculates checksums assuming the checksum is a 16-bit header field. * This method is generalized to work for IP, ICMP, UDP, and TCP packets * given the proper parameters. */ protected int _computeChecksum_(int startOffset, int checksumOffset, int length, int virtualHeaderTotal, boolean update) { int total = 0; int i = startOffset; int imax = checksumOffset; while(i < imax) total+=(((_data_[i++] & 0xff) << 8) | (_data_[i++] & 0xff)); // Skip existing checksum. i = checksumOffset + 2; imax = length - (length % 2); while(i < imax) total+=(((_data_[i++] & 0xff) << 8) | (_data_[i++] & 0xff)); if(i < length) total+=((_data_[i] & 0xff) << 8); total+=virtualHeaderTotal; // Fold to 16 bits while((total & 0xffff0000) != 0) total = (total & 0xffff) + (total >>> 16); total = (~total & 0xffff); if(update) { _data_[checksumOffset] = (byte)(total >> 8); _data_[checksumOffset + 1] = (byte)(total & 0xff); } return total; } /** * Computes the IP checksum, optionally updating the IP checksum header. * * @param update Specifies whether or not to update the IP checksum * header after computing the checksum. A value of true indicates * the header should be updated, a value of false indicates it * should not be updated. * @return The computed IP checksum. */ public final int computeIPChecksum(boolean update) { return _computeChecksum_(0, OFFSET_IP_CHECKSUM, getIPHeaderByteLength(), 0, update); } /** * Same as <code>computeIPChecksum(true);</code> * * @return The computed IP checksum value. */ public final int computeIPChecksum() { return computeIPChecksum(true); } /** * @return The IP checksum header value. */ public final int getIPChecksum() { return (((_data_[OFFSET_IP_CHECKSUM] & 0xff) << 8) | (_data_[OFFSET_IP_CHECKSUM + 1] & 0xff)); } /** * Retrieves the source IP address into a byte array. The array * should be {@link #LENGTH_SOURCE_ADDRESS} bytes long. * * @param address The array in which to store the address. */ public final void getSource(byte[] address) { System.arraycopy(_data_, OFFSET_SOURCE_ADDRESS, address, 0, (address.length < LENGTH_SOURCE_ADDRESS ? address.length : LENGTH_SOURCE_ADDRESS)); } /** * Retrieves the destionation IP address into a byte array. The array * should be {@link #LENGTH_DESTINATION_ADDRESS} bytes long. * * @param address The array in which to store the address. */ public final void getDestination(byte[] address) { System.arraycopy(_data_, OFFSET_DESTINATION_ADDRESS, address, 0, (address.length < LENGTH_DESTINATION_ADDRESS ? address.length : LENGTH_DESTINATION_ADDRESS)); } /** * Retrieves the source IP address as a string into a StringBuffer. * * @param buffer The StringBuffer in which to store the address. */ public final void getSource(StringBuffer buffer) { OctetConverter.octetsToString(buffer, _data_, OFFSET_SOURCE_ADDRESS); } /** * Retrieves the destination IP address as a string into a StringBuffer. * * @param buffer The StringBuffer in which to store the address. */ public final void getDestination(StringBuffer buffer) { OctetConverter.octetsToString(buffer, _data_, OFFSET_DESTINATION_ADDRESS); } /** * Sets the source IP address using a word representation. * * @param src The source IP address as a 32-bit word. */ public final void setSourceAsWord(int src) { OctetConverter.intToOctets(src, _data_, OFFSET_SOURCE_ADDRESS); } /** * Sets the destination IP address using a word representation. * * @param dest The source IP address as a 32-bit word. */ public final void setDestinationAsWord(int dest) { OctetConverter.intToOctets(dest, _data_, OFFSET_DESTINATION_ADDRESS); } /** * @return The source IP address as a 32-bit word. */ public final int getSourceAsWord() { return OctetConverter.octetsToInt(_data_, OFFSET_SOURCE_ADDRESS); } /** * @return The destination IP address as a 32-bit word. */ public final int getDestinationAsWord() { return OctetConverter.octetsToInt(_data_, OFFSET_DESTINATION_ADDRESS); } /** * @return The source IP address as a java.net.InetAddress instance. */ public final InetAddress getSourceAsInetAddress() throws UnknownHostException { StringBuffer buf = new StringBuffer(); getSource(buf); return InetAddress.getByName(buf.toString()); // This works only with JDK 1.4 and up /* byte[] octets = new byte[4]; getSource(octets); return InetAddress.getByAddress(octets); */ } /** * @return The destination IP address as a java.net.InetAddress instance. */ public final InetAddress getDestinationAsInetAddress() throws UnknownHostException { StringBuffer buf = new StringBuffer(); getDestination(buf); return InetAddress.getByName(buf.toString()); // This works only with JDK 1.4 and up /* byte[] octets = new byte[4]; getDestination(octets); return InetAddress.getByAddress(octets); */ } }