/*
* $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 java.util.ArrayList;
import java.util.Iterator;
import org.apache.log4j.Logger;
import org.jnode.net.SocketBuffer;
/**
* Class used to reconstruct fragmented IP packets
*
* @author epr
*/
public class IPv4FragmentList implements IPv4Constants {
/** My logger */
private static final Logger log = Logger.getLogger(IPv4FragmentList.class);
/** When was this object created */
private final long creationTime;
/** List of fragments */
private final ArrayList<SocketBuffer> fragments;
/** The key of this fragment list */
private final Object key;
/** Is the first fragment in the list? */
private boolean haveFirstFragment;
/** Is the last fragment in the list? */
private boolean haveLastFragment;
/**
* Create a new instance
*
* @param firstFragment
*/
public IPv4FragmentList(SocketBuffer firstFragment) {
this.creationTime = System.currentTimeMillis();
this.fragments = new ArrayList<SocketBuffer>();
this.haveFirstFragment = false;
this.haveLastFragment = false;
final IPv4Header hdr = (IPv4Header) firstFragment.getNetworkLayerHeader();
this.key = hdr.getFragmentListKey();
add(firstFragment);
}
/**
* Add a packet to this object.
*
* @param skbuf
*/
public void add(SocketBuffer skbuf) {
final IPv4Header hdr = (IPv4Header) skbuf.getNetworkLayerHeader();
if (!hdr.isFragment()) {
throw new IllegalArgumentException("Buffer does not contain a fragment");
}
final int myFrOfs = hdr.getFragmentOffset();
final int mySize = hdr.getDataLength();
// Fixup some member variables
this.haveFirstFragment |= (myFrOfs == 0);
this.haveLastFragment |= !hdr.hasMoreFragments();
// Insert the fragment at the correct index in the list
for (Iterator<SocketBuffer> i = fragments.iterator(); i.hasNext();) {
final SocketBuffer f = (SocketBuffer) i.next();
final IPv4Header fhdr = (IPv4Header) f.getNetworkLayerHeader();
final int fOfs = fhdr.getFragmentOffset();
final int fSize = f.getSize();
if (myFrOfs == (fOfs + fSize)) {
// skbuf directly follows f, attach it.
f.append(skbuf);
// See if we can attach the following fragment directly to me
if (i.hasNext()) {
final SocketBuffer f2 = (SocketBuffer) i.next();
final IPv4Header f2hdr = (IPv4Header) f2.getNetworkLayerHeader();
final int f2Ofs = f2hdr.getFragmentOffset();
if (f2Ofs == (myFrOfs + skbuf.getSize())) {
// Yes we can attach it
skbuf.append(f2);
fragments.remove(f2);
}
}
return;
} else if (myFrOfs < fOfs) {
// skbuf is before f, insert it here
fragments.add(fragments.indexOf(f), skbuf);
return;
} else if (myFrOfs < (fOfs + fSize)) {
// Fragment offset in the middle of an existing fragment: this is an error!
log.debug("Fragment offset(" + myFrOfs + mySize + ',' +
") falls in another fragment (" + fOfs + ',' + fSize + ").");
return;
}
}
// The fragment has not been added, so it must be the last on the list
// with a gap before it.
fragments.add(skbuf);
}
/**
* Is this fragmentlist still alive. A fragmentlist is alive when is was
* created no more then IP_FRAGTIMEOUT milliseconds ago.
*/
public boolean isAlive() {
final long now = System.currentTimeMillis();
return ((now - creationTime) <= IP_FRAGTIMEOUT);
}
/**
* Do we have all fragments?
*/
public boolean isComplete() {
if (haveFirstFragment && haveLastFragment) {
if (fragments.size() == 1) {
return true;
}
}
return false;
}
/**
* Gets the complete packet
* This method can only be called when <code>isComplete</code> returns true.
*/
public SocketBuffer getPacket() {
return (SocketBuffer) fragments.get(0);
}
/**
* Gets the key of this fragmentlist.
* @see IPv4Header#getFragmentListKey()
*/
public Object getKey() {
return key;
}
}