io.pkts.framer.IPv6Framer Maven / Gradle / Ivy
/**
*
*/
package io.pkts.framer;
import io.pkts.buffer.Buffer;
import io.pkts.buffer.Buffers;
import io.pkts.packet.IPv6Packet;
import io.pkts.packet.MACPacket;
import io.pkts.packet.impl.IPv6PacketImpl;
import io.pkts.protocol.Protocol;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author [email protected]
*
*/
public class IPv6Framer implements Framer {
public IPv6Framer() {
}
/**
* {@inheritDoc}
*/
@Override
public Protocol getProtocol() {
return Protocol.IPv6;
}
/**
* {@inheritDoc}
*/
@Override
public IPv6Packet frame(final MACPacket parent, final Buffer payload) throws IOException {
if (parent == null) {
throw new IllegalArgumentException("The parent frame cannot be null");
}
final Buffer fixedHeader = payload.readBytes(IPv6PacketImpl.FIXED_HEADER_LENGTH);
// byte 1, contains the version and the length
final int version = (fixedHeader.getByte(0) & 0xF0) >> 4;
if (version != IPv6Packet.VERSION_IDENTIFIER) {
throw new FramingException(String.format("Invalid IPv6 version: %d", version), Protocol.IPv6);
}
final int payloadLength = fixedHeader.getUnsignedShort(4);
int nextHeader = fixedHeader.getByte(6);
final Buffer extensionHeadersBuffer = Buffers.createBuffer(400);
while (nextHeader == IPv6Packet.EXTENSION_HOP_BY_HOP ||
nextHeader == IPv6Packet.EXTENSION_DESTINATION_OPTIONS ||
nextHeader == IPv6Packet.EXTENSION_ROUTING ||
nextHeader == IPv6Packet.EXTENSION_FRAGMENT ||
nextHeader == IPv6Packet.EXTENSION_AH ||
nextHeader == IPv6Packet.EXTENSION_ESP ||
nextHeader == IPv6Packet.EXTENSION_MOBILITY) {
nextHeader = accumulateNextHeader(nextHeader, payload, extensionHeadersBuffer);
}
// TODO: extract actual PayloadLength from Hop-by-Hop extension header, if present
// Trim off any padding from the upper layer, e.g. Ethernet padding for small packets.
// If the captured frame was truncated, then use the truncated size for the data buffer, instead of what the
// IPv6 header says its length should be.
final int totalLength = IPv6PacketImpl.FIXED_HEADER_LENGTH + extensionHeadersBuffer.getWriterIndex() + payloadLength;
final Buffer data = payload.slice(Math.min(totalLength, payload.capacity()));
return new IPv6PacketImpl(parent, Buffers.wrap(fixedHeader, extensionHeadersBuffer), nextHeader, data);
}
private int accumulateNextHeader(final int protocolNumber, final Buffer payload, final Buffer extensionHeadersBuffer) throws IOException, FramingException {
final int headerExtensionLen;
final AtomicInteger nextHeaderProtocol = new AtomicInteger();
switch (protocolNumber) {
case IPv6Packet.EXTENSION_HOP_BY_HOP:
case IPv6Packet.EXTENSION_ROUTING:
case IPv6Packet.EXTENSION_DESTINATION_OPTIONS:
nextHeaderProtocol.set(payload.getByte(IPv6PacketImpl.FIXED_HEADER_LENGTH));
headerExtensionLen = 8 + payload.getByte(IPv6PacketImpl.FIXED_HEADER_LENGTH + 1) * 8;
break;
case IPv6Packet.EXTENSION_FRAGMENT:
nextHeaderProtocol.set(payload.getByte(IPv6PacketImpl.FIXED_HEADER_LENGTH));
headerExtensionLen = 8;
break;
case IPv6Packet.EXTENSION_AH:
nextHeaderProtocol.set(payload.getByte(IPv6PacketImpl.FIXED_HEADER_LENGTH));
headerExtensionLen = 4 * (payload.getByte(IPv6PacketImpl.FIXED_HEADER_LENGTH + 1) + 2);
break;
case IPv6Packet.EXTENSION_ESP:
// TODO figure out how length is even parsed...
// If we can't parse the header, we have to throw, because we need to parse it to find the next header.
default:
throw new FramingException(String.format("Unsupported IPv6 extension header: %d", protocolNumber), Protocol.IPv6);
}
final Buffer tmp = payload.readBytes(headerExtensionLen);
extensionHeadersBuffer.write(tmp.getArray());
return nextHeaderProtocol.get();
}
@Override
public boolean accept(final Buffer data) {
// TODO Auto-generated method stub
return false;
}
}