/*
* Copyright (c) 2006-2007 Graz University of Technology. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. The names "Graz University of Technology" and "IAIK of Graz University of
* Technology" must not be used to endorse or promote products derived from
* this software without prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package ejip.jtcpip;
import ejip.jtcpip.util.Bitmap;
import ejip.jtcpip.util.Debug;
import ejip.jtcpip.util.NumFunctions;
/**
* Provides a buffer for a certain number of Packets. The number of packets and
* the size of the individual buffers can be set through constants.
*
* @author Tobias Kellner
* @author Ulrich Feichter
* @author Christof Rath
* @version $Rev: 950 $ $Date: 2007/01/24 19:37:07 $
*/
public class Payload {
/** Maximum Data length in words */
// TODO:
// private final static int MAXW =
// NumFunctions.divRoundUp(StackParameters.PAYLOAD_MAX_DATA_SIZE, 4);
private final static int MAXW = StackParameters.PAYLOAD_MAX_DATA_SIZE;
/** Maximum IP Header length */
private final static int MAXIPH = 60;
/** Maximum IP Header length in words */
private final static int MAXIPHW = MAXIPH / 4;
/** Free <code>Payload</code> */
protected final static byte PAYLOAD_FREE = 0x01;
/** Used <code>Payload</code> */
public final static byte PAYLOAD_USED = 0x02;
/** <code>Payload</code> is a future part of a TCP connections */
public final static byte PAYLOAD_WND_RX = 0x03;
/** <code>Payload</code> marked able to send */
protected final static byte PAYLOAD_SND_RD = 0x04;
/** <code>Payload</code> is waiting for an ARP response */
protected final static byte PAYLOAD_ARP_WT = 0x05;
/**
* <code>Payload</code> is split into fragments and not all fragments have
* been sent
*/
protected final static byte PAYLOAD_FRAGMT = 0x06;
/**
* <code>Payload</code> is split into fragments and not all fragments have
* been received
*/
protected final static byte PAYLOAD_RESMBL = 0x07;
/** Exception that gets thrown when passes data is too large */
private static JtcpipException payloadException;
/** The <code>Payload</code> pool */
public static Payload[] pool;
/**
* Holds the number of <code>Payload</code>s that are currently in a
* waiting state
*/
private static byte numWaitingPayloads = 0;
/**
* Stores the current status of one <code>Payload</code> <b>Note:</b> As
* the number of waiting payloads must be correct the setter methode has to
* be used!
*/
public byte status;
/**
* The <code>Payload</code> data (excluding the first 20 bytes of the IP
* Header)
*/
public int[] payload;
/** The IP Header (only the first 20 bytes) */
public int[] ipHeader;
/**
* Length of <code>Payload</code> data. Length (connection layer, without
* IP header) stored in {@link Payload#payload} in bytes.
*/
public int length;
/**
* Containing TCP Connection (only needed if the payload is in WND_RX state)
*/
protected TCPConnection conn = null;
/**
* If the payload is waiting (for e.g. ARP response, acknowledge) the
* timeout is used to free the payload if the response is not coming.
*/
private long timeout = 0;
/**
* Holds the offset of the next fragment to be sent if Payload is in the
* PAYLOAD_FRAGMT state. This field is also used by the IP.rsmblStore()
* function to mark the smallest offset yet received (used if a timeout
* occures to send an ICMP message if the first fragment was stored)
*/
private int nextFragOffset = 0;
/** Used to mark all received fragments of an incoming payload */
protected Bitmap reassembledBitMap;
public static void init() {
payloadException = new JtcpipException(
"The buffer exceeds the payload size");
pool = new Payload[StackParameters.PAYLOAD_POOL_SIZE];
}
/**
* Get a new <code>Payload</code>. Tries to find an unused
* <code>Payload</code>, allocates and returns it. If none is found, null
* is returned.
*
* @return The <code>Payload</code> (or null)
*/
public static synchronized Payload newPayload() {
/**
* If we can't find a free payload we look for a payload that holds a
* future part of a TCP connection (Payload marked with the
* PAYLOAD_WND_RX status).
*/
int futureIndex = -1;
int futureSeqNr = 0;
for (int i = 0; i < StackParameters.PAYLOAD_POOL_SIZE; i++) {
if (pool[i] == null)
pool[i] = new Payload();
if (pool[i].status != PAYLOAD_FREE) {
if (pool[i].status == PAYLOAD_WND_RX
&& NumFunctions.unsignedGt(TCPPacket.getSeqNr(pool[i]),
futureSeqNr) > 0) {
futureIndex = i;
futureSeqNr = TCPPacket.getSeqNr(pool[i]);
}
continue;
}
pool[i].setStatus(PAYLOAD_USED, 0);
clearPayload(pool[i]);
return pool[i];
}
if (futureIndex > -1) {
freePayload(pool[futureIndex]);
pool[futureIndex].setStatus(PAYLOAD_USED, 0);
clearPayload(pool[futureIndex]);
return pool[futureIndex];
}
if (Debug.enabled)
Debug.println("Payload: No more Payload", Debug.DBG_OTHER);
return null;
}
/**
* Clear all data from a Payload.
*
* @param pay
* the Payload
*/
private static void clearPayload(Payload pay) {
if (pay == null)
return;
pay.reassembledBitMap.clearBitmap();
pay.conn = null;
pay.length = 0;
}
/**
* Free a <code>Payload</code>. The <code>Payload</code> is marked as
* unused, so it can be allocated again.
*
* @param pay
* The <code>Payload</code> to be freed
*/
public static void freePayload(Payload pay) {
if (pay == null)
return;
pay.setStatus(PAYLOAD_FREE, 0);
pay.length = 0; // redundant
}
/**
* Create a <code>Payload</code>. This constructor is private, because
* <code>Payload</code>s can only be got through
* {@link Payload#newPayload}. Initializes both (payload and ip header)
* arrays.
*/
private Payload() {
payload = new int[MAXW];
ipHeader = new int[MAXIPHW];
status = PAYLOAD_FREE;
reassembledBitMap = new Bitmap(NumFunctions.divRoundUp(
StackParameters.PAYLOAD_MAX_DATA_SIZE, 8));
}
/**
* Fills the payload buffer starting at offset with a given area of a byte
* array
*
* @param offset
* within the payload as multiple of four octets
* @param buffer
* @param firstByte
* to copy within the buffer
* @param count
*
* @throws JtcpipException
* Data too large
*/
public void setData(int offset, byte[] buffer, int firstByte, int count)
throws JtcpipException {
length = offset * 4 + count;
if (length > StackParameters.PAYLOAD_MAX_DATA_SIZE)
throw payloadException;
for (int i = 0; i < count; i++) {
switch (i % 4) {
case 0:
payload[offset] = buffer[firstByte + i] << 24;
break;
case 1:
payload[offset] |= (buffer[firstByte + i] & 0xFF) << 16;
break;
case 2:
payload[offset] |= (buffer[firstByte + i] & 0xFF) << 8;
break;
case 3:
payload[offset] |= buffer[firstByte + i] & 0xFF;
offset++;
}
}
}
/**
* Sets the status field.
*
* @param newStatus
* @param msTimeout
* timeout in milli seconds
*/
protected synchronized void setStatus(byte newStatus, int msTimeout) {
if (Debug.enabled) {
Debug.println("Status change", Debug.DBG_OTHER);
// Byte byt = new Byte(status);
// Debug.print(byt.toString(),Debug.DBG_OTHER);
// Debug.print(" -> ",Debug.DBG_OTHER);
// Byte byt1 = new Byte(newStatus);
// Debug.print(byt1.toString(), Debug.DBG_OTHER);
// Debug.println(" T: ", Debug.DBG_OTHER);
//
}
if (status <= PAYLOAD_WND_RX && newStatus > PAYLOAD_WND_RX)
numWaitingPayloads++;
if (status > PAYLOAD_WND_RX && newStatus <= PAYLOAD_WND_RX)
numWaitingPayloads--;
status = newStatus;
if (status == PAYLOAD_FRAGMT) {
timeout = 0;
nextFragOffset = msTimeout;
} else {
if (msTimeout > 0) {
timeout = System.currentTimeMillis() + msTimeout;
} else {
timeout = 0;
}
}
}
/**
* Returns the current status
*
* @return byte
*/
protected synchronized byte getStatus() {
return status;
}
/**
* Returns true if a timeout occured for this <code>Payload</code>
*
* @return boolean
*/
protected boolean isTimeout() {
return timeout > 0 ? System.currentTimeMillis() > timeout : false;
}
/**
* Returns the offset for the next fragment. Required for fragmented
* payloads as the offset is stored
*
* @return The offset for the next fragment
*/
protected int getOffset() {
return (status == PAYLOAD_FRAGMT || status == PAYLOAD_RESMBL) ? nextFragOffset
: 0;
}
/**
* Sets the offset to the minimum of all received fragments of a Payload. If
* the first fragment (Offset == 0) has arrived and the reassambling has
* timed out we send an ICMP message
*
* @param curOffset
* Offset of the current fragment
*/
protected void setOffset(int curOffset) {
if (curOffset < nextFragOffset)
nextFragOffset = curOffset;
}
/**
* Returns the number of <code>Payload</code>s with a status >
* PAYLOAD_USED
*
* @return the number of payloads that are to be processed
*/
public synchronized static short waitingPayloadCount() {
return numWaitingPayloads;
}
/**
* Checks if a payload contains a certain seqNr.
*
* @param seqNr
* the sequence number to look for
* @param pay
* the Payload to check
* @return whether the Payload contains the seqNr
*/
public static boolean isSeqNrInPayload(int seqNr, Payload pay) {
if (Debug.enabled)
Debug.println("seqNextNrinPayload ", Debug.DBG_TCP);// + seqNr + "
// seq payload"
// +
// TCPPacket.getSeqNr(pay),
// Debug.DBG_TCP);
return NumFunctions.isBetweenOrEqualSmaller(TCPPacket.getSeqNr(pay),
TCPPacket.getSeqNr(pay) + TCP.calculateSegmentLength(pay),
seqNr);
}
/**
* Look for a payload which contains a certain seqNr.
*
* @param conn
* The connection containing the payload
* @param seqNr
* The sequence number we look for
* @return the <code>Payload</code> or <code>null</code> if none was
* found
*/
public synchronized static Payload findPayload(TCPConnection conn, int seqNr) {
Payload pay;
for (int i = 0; i < StackParameters.PACKET_POOL_SIZE; i++) {
pay = pool[i];
if (pay == null)
continue;
if (pay.status != PAYLOAD_WND_RX)
continue;
if (pay.conn != conn)
continue;
// inline----------------------(isSeqNrInPayload(seqNr, pay)
// if (isSeqNrInPayload(seqNr, pay))
boolean isSeqNumberInPl = false;
int seqLength = TCPPacket.getDataLength(pay);
if (TCPPacket.isFINFlagSet(pay))
seqLength++;
if (TCPPacket.isSYNFlagSet(pay))
seqLength++;
int smallerVal = TCPPacket.getSeqNr(pay);
int testVal = seqNr;
int biggerVal = TCPPacket.getSeqNr(pay) + seqLength;
if (smallerVal == testVal)
isSeqNumberInPl = true;
if (biggerVal < smallerVal)
isSeqNumberInPl = ((testVal >= smallerVal) && (testVal <= Integer.MAX_VALUE))
|| ((testVal >= Integer.MIN_VALUE) && (testVal < biggerVal));
else
isSeqNumberInPl = (testVal >= smallerVal)
&& (testVal < biggerVal);
// inline----------------------(isSeqNrInPayload(seqNr, pay)---end
if (isSeqNumberInPl) {
// So they won't get freed
pay.status = PAYLOAD_USED;
return pay;
}
}
return null;
}
}