/*
* Copyright 2010 NCHOVY
*
* 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.apache.org/licenses/LICENSE-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.krakenapps.pcap.decoder.ip;
import java.nio.ByteBuffer;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;
import org.krakenapps.pcap.decoder.ethernet.EthernetFrame;
import org.krakenapps.pcap.packet.PcapPacket;
import org.krakenapps.pcap.util.Buffer;
import org.krakenapps.pcap.util.ChainBuffer;
/**
* @author mindori
*/
public class IpReassembler {
private Map<Integer, HoleManagerWrapper> lmap;
private PriorityQueue<HoleManagerWrapper> wrapper;
private int dropTimeout;
public IpReassembler() {
this.lmap = new HashMap<Integer, HoleManagerWrapper>();
this.wrapper = new PriorityQueue<IpReassembler.HoleManagerWrapper>(11, new HoleManagerWrapperComparator());
this.dropTimeout = 30000;
}
public int getDropTimeout() {
return dropTimeout;
}
public void setDropTimeout(int dropTimeout) {
this.dropTimeout = dropTimeout;
}
public Ipv4Packet tryReassemble(Ipv4Packet fragment) {
/* check HoleDescriptor */
int id = fragment.getId();
if (!lmap.containsKey(id)) {
HoleManagerWrapper t = new HoleManagerWrapper(id, new HoleManager());
lmap.put(id, t);
wrapper.add(t);
}
HoleManagerWrapper t = lmap.get(id);
t.setTime(((EthernetFrame) fragment.getL2Frame()).getPcapPacket());
HoleManager h = t.getManager();
int offset = fragment.getFragmentOffset() * 8;
int length = fragment.getTotalLength() - fragment.getIhl();
Buffer data = fragment.getData();
/* check MF == 0 */
if (fragment.getFlags() == 0) {
int goal = offset + length;
h.setGoal(goal);
}
/* put data */
h.put(data, offset, length);
/* check flush */
if (h.isFlush(offset, length)) {
h.flush(data);
} else
return null;
if (h.isReassemble()) {
ByteBuffer b = h.getReassembled();
b.position(0);
return reassemble(fragment, b, h.getGoal());
} else
return null;
}
public void drop() {
long now = System.currentTimeMillis();
while (!wrapper.isEmpty() && now - wrapper.peek().getTime() > dropTimeout)
lmap.remove(wrapper.poll().getId());
}
private Ipv4Packet reassemble(Ipv4Packet fragment, ByteBuffer reassembled, int goal) {
byte[] b = new byte[goal];
reassembled.get(b, 0, goal);
Buffer data = new ChainBuffer();
data.addLast(b);
int tl = fragment.getIhl() + goal;
Ipv4Packet p = Ipv4Packet.makeReassembled(fragment, data, tl);
return p;
}
private class HoleManagerWrapper {
private int id;
private HoleManager manager;
private long time;
public HoleManagerWrapper(int id, HoleManager manager) {
this.id = id;
this.manager = manager;
}
public int getId() {
return id;
}
public long getTime() {
return time;
}
public void setTime(PcapPacket packet) {
this.time = ((long) (packet.getPacketHeader().getTsSec())) * 1000;
this.time += packet.getPacketHeader().getTsUsec() / 1000;
}
public HoleManager getManager() {
return manager;
}
}
private class HoleManagerWrapperComparator implements Comparator<HoleManagerWrapper> {
@Override
public int compare(HoleManagerWrapper o1, HoleManagerWrapper o2) {
return (int) (o1.getTime() - o2.getTime());
}
}
}