class IPv4 extends BasePacket {

    @Override
    public IPacket deserialize(byte[] data, int offset, int length)
            throws PacketParsingException {
        ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
        short sscratch;

        this.version = bb.get();
        this.headerLength = (byte) (this.version & 0xf);
        this.version = (byte) ((this.version >> 4) & 0xf);
        if (this.version != 4) {
            throw new PacketParsingException(
                    "Invalid version for IPv4 packet: " +
                    this.version);
        }
        this.diffServ = bb.get();
        this.totalLength = bb.getShort();
        this.identification = bb.getShort();
        sscratch = bb.getShort();
        this.flags = (byte) ((sscratch >> IPV4_FLAGS_SHIFT) & IPV4_FLAGS_MASK);
        this.fragmentOffset = (short) (sscratch & IPV4_OFFSET_MASK);
        this.ttl = bb.get();
        this.protocol = IpProtocol.of(U8.f(bb.get()));
        this.checksum = bb.getShort();
        this.sourceAddress = IPv4Address.of(bb.getInt());
        this.destinationAddress = IPv4Address.of(bb.getInt());

        if (this.headerLength > 5) {
            int optionsLength = (this.headerLength - 5) * 4;
            this.options = new byte[optionsLength];
            bb.get(this.options);
        }

        IPacket payload;
        isFragment = ((this.flags & IPV4_FLAGS_DONTFRAG) == 0) &&
                ((this.flags & IPV4_FLAGS_MOREFRAG) != 0 ||
                this.fragmentOffset != 0);
        if (!isFragment && IPv4.protocolClassMap.containsKey(this.protocol)) {
            Class<? extends IPacket> clazz = IPv4.protocolClassMap.get(this.protocol);
            try {
                payload = clazz.newInstance();
            } catch (Exception e) {
                throw new RuntimeException("Error parsing payload for IPv4 packet", e);
            }
        } else {
            if (log.isTraceEnabled() && isFragment) {
                log.trace("IPv4 fragment detected {}->{}, forward using IP header only",
                        this.sourceAddress.toString(),
                        this.destinationAddress.toString());
            }
            payload = new Data();
        }
        int payloadLength = this.totalLength - this.headerLength * 4;
        int remLength = bb.limit()-bb.position();
        if (remLength < payloadLength)
            payloadLength = bb.limit()-bb.position();
        this.payload = payload.deserialize(data, bb.position(), payloadLength);
        this.payload.setParent(this);

        if (this.totalLength > length)
            this.isTruncated = true;
        else
            this.isTruncated = false;

        return this;
    }

    public  IPv4();

}