/*
* $Id$
*
* Copyright (C) 2003-2015 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.net.ipv4;
import org.jnode.net.NetworkLayerHeader;
import org.jnode.net.ProtocolAddress;
import org.jnode.net.SocketBuffer;
/**
* @author epr
*/
public class IPv4Header implements NetworkLayerHeader, IPv4Constants {
/** IP version */
private int version;
/** Length of header in bytes */
private int hdrlength;
/** Type of service */
private int tos;
/** Length of message data in bytes (without this IP header) */
private int dataLength;
/** Identification */
private int identification;
/** Fragment offset */
private int fragmentOffset;
/** Time to live */
private int ttl;
/** Protocol ID */
private int protocol;
/** Source address */
private IPv4Address srcAddress;
/** Destination address */
private IPv4Address dstAddress;
/** Checksum is ok */
private final boolean checksumOk;
/**
* Create a new instance
*
* @param tos
* @param ttl
* @param protocol
* @param dstAddress
*/
public IPv4Header(int tos, int ttl, int protocol, IPv4Address dstAddress, int dataLength) {
if (dstAddress == null) {
throw new IllegalArgumentException("dstAddress cannot be null");
}
this.version = 4;
this.tos = tos;
this.ttl = ttl;
this.protocol = protocol;
this.dstAddress = dstAddress;
this.hdrlength = 20;
this.dataLength = dataLength;
this.checksumOk = true; // For this constructor not relevant
}
/**
* Create a new instance, read the header from the given buffer
*
* @param skbuf
*/
public IPv4Header(SocketBuffer skbuf) {
final int b0 = skbuf.get(0);
this.version = (b0 >> 4) & 0x0f;
this.hdrlength = (b0 & 0x0f) * 4;
this.tos = skbuf.get(1);
this.dataLength = skbuf.get16(2) - hdrlength;
this.identification = skbuf.get16(4);
this.fragmentOffset = skbuf.get16(6);
this.ttl = skbuf.get(8);
this.protocol = skbuf.get(9);
this.srcAddress = new IPv4Address(skbuf, 12);
this.dstAddress = new IPv4Address(skbuf, 16);
final int ccs = IPv4Utils.calcChecksum(skbuf, 0, hdrlength);
checksumOk = (ccs == 0);
}
/**
* Create a clone of the given header
*
* @param src
*/
public IPv4Header(IPv4Header src) {
this.version = src.version;
this.hdrlength = src.hdrlength;
this.tos = src.tos;
this.dataLength = src.dataLength;
this.identification = src.identification;
this.fragmentOffset = src.fragmentOffset;
this.ttl = src.ttl;
this.protocol = src.protocol;
this.srcAddress = src.srcAddress;
this.dstAddress = src.dstAddress;
this.checksumOk = src.checksumOk;
}
/**
* Prefix this header to the front of the given buffer
*
* @param skbuf
*/
public void prefixTo(SocketBuffer skbuf) {
skbuf.insert(hdrlength);
skbuf.set(0, ((version << 4) & 0xf0) | ((hdrlength / 4) & 0xf));
skbuf.set(1, tos);
skbuf.set16(2, dataLength + hdrlength);
skbuf.set16(4, identification);
skbuf.set16(6, fragmentOffset);
skbuf.set(8, ttl);
skbuf.set(9, protocol);
skbuf.set(10, 0); // checksum, calculate and set later
srcAddress.writeTo(skbuf, 12);
dstAddress.writeTo(skbuf, 16);
// calculate and set checksum
final int ccs = IPv4Utils.calcChecksum(skbuf, 0, hdrlength);
if (ccs == 0) {
skbuf.set(10, 0xffff);
} else {
skbuf.set16(10, ccs);
}
}
/**
* Finalize the header in the given buffer. This method is called when all
* layers have set their header data and can be used e.g. to update checksum
* values.
*
* @param skbuf The buffer
* @param offset The offset to the first byte (in the buffer) of this header
* (since low layer headers are already prefixed)
*/
public void finalizeHeader(SocketBuffer skbuf, int offset) {
// Nothing to do
}
/**
* Gets the source address of the packet described in this header
*/
public ProtocolAddress getSourceAddress() {
return srcAddress;
}
/**
* Gets the source address of the packet described in this header
*/
public ProtocolAddress getDestinationAddress() {
return dstAddress;
}
/**
* Gets the destination
*/
public IPv4Address getDestination() {
return dstAddress;
}
/**
* Is the don't fragment flag set?
*/
public boolean isDontFragment() {
return ((fragmentOffset & IP_DF) != 0);
}
/**
* Is the more fragments flag set?
*/
public boolean hasMoreFragments() {
return ((fragmentOffset & IP_MF) != 0);
}
/**
* Is this a fragment. That is, the MF flag is set, or fragment offset is
* greater then 0.
*/
public boolean isFragment() {
return (((fragmentOffset & IP_MF) != 0) || ((fragmentOffset & IP_FRAGOFS_MASK) != 0));
}
/**
* Gets the offset of this fragment in the IP packet
*/
public int getFragmentOffset() {
return (fragmentOffset & 0x1FFF) << 3;
}
/**
* Gets the key used to find the correct fragment list. This key contains:
* identification, protocol, srcAddress, dstAddress
*/
public Object getFragmentListKey() {
final StringBuilder b = new StringBuilder();
b.append(identification);
b.append('-');
b.append(protocol);
b.append('-');
b.append(srcAddress);
b.append('-');
b.append(dstAddress);
return b.toString();
}
/**
* Set the fragment offset (not the DF & MF bits)
*
* @param i
*/
public void setFragmentOffset(int i) {
fragmentOffset &= ~IP_FRAGOFS_MASK;
fragmentOffset |= ((i >> 3) & IP_FRAGOFS_MASK);
}
/**
* Sets the don't fragment flag
*/
public void setDontFragment(boolean on) {
if (on) {
fragmentOffset |= IP_DF;
} else {
fragmentOffset &= ~IP_DF;
}
}
/**
* Sets the more fragments flag
*/
public void setMoreFragments(boolean on) {
if (on) {
fragmentOffset |= IP_MF;
} else {
fragmentOffset &= ~IP_MF;
}
}
/**
* Get the header length
*/
public int getLength() {
return hdrlength;
}
/**
* Gets the identification number
*/
public int getIdentification() {
return identification;
}
/**
* Gets the length of the header and data
*/
public int getTotalLength() {
return dataLength + hdrlength;
}
/**
* Gets the length of the data (without the IP header)
*/
public int getDataLength() {
return dataLength;
}
/**
* Gets the protocol
*/
public int getProtocol() {
return protocol;
}
/**
* Gets the source address
*/
public IPv4Address getSource() {
return srcAddress;
}
/**
* Gets the type of service attribute
*/
public int getTos() {
return tos;
}
/**
* Gets the time to live attribute
*/
public int getTtl() {
return ttl;
}
/**
* Gets the IP version
*/
public int getVersion() {
return version;
}
/**
* Sets the destination address
*
* @param address
*/
public void setDestination(IPv4Address address) {
dstAddress = address;
}
/**
* Sets the identification number
* @param i
*/
public void setIdentification(int i) {
identification = i;
}
/**
* Sets the length of the data in bytes
* @param i
*/
public void setDataLength(int i) {
dataLength = i;
}
/**
* Sets the protocol
* @param i
*/
public void setProtocol(int i) {
protocol = i;
}
/**
* Sets the source address
* @param address
*/
public void setSource(IPv4Address address) {
srcAddress = address;
}
/**
* Sets the type of service
* @param i
*/
public void setTos(int i) {
tos = i;
}
/**
* Sets the time to live
* @param i
*/
public void setTtl(int i) {
ttl = i;
}
/**
* Is the checksum valid
*/
public boolean isChecksumOk() {
return checksumOk;
}
/**
* Swap the source and destination address
*/
public void swapAddresses() {
final IPv4Address tmp = dstAddress;
this.dstAddress = this.srcAddress;
this.srcAddress = tmp;
}
}