/*
* 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.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import org.krakenapps.pcap.Injectable;
import org.krakenapps.pcap.PacketBuilder;
import org.krakenapps.pcap.decoder.ethernet.EthernetType;
import org.krakenapps.pcap.live.PcapDeviceManager;
import org.krakenapps.pcap.live.PcapDeviceMetadata;
import org.krakenapps.pcap.util.Buffer;
import org.krakenapps.pcap.util.ChainBuffer;
import org.krakenapps.pcap.util.IpConverter;
/**
* IP Packet
*
* @author mindori
*/
public class Ipv4Packet implements Injectable, IpPacket {
private Object l2Frame;
private int version;
private int ihl;
private int tos;
private int totalLength;
private int id;
private int flags;
private int fragmentOffset;
private int ttl;
private int protocol;
private int headerChecksum;
private int source;
private int destination;
private InetAddress sourceAddress;
private InetAddress destinationAddress;
private byte[] options; // This column need implement.
private byte[] padding; // This column need implement.
private Buffer data;
private Ipv4Packet() {
}
@Override
public Object getL2Frame() {
return l2Frame;
}
public void setL2Frame(Object l2Frame) {
this.l2Frame = l2Frame;
}
public static class Builder implements PacketBuilder {
private Integer ihl = 20;
private Integer tos = 0;
private Integer totalLength;
private Integer id = 1;
private Integer fragmentOffset = 0;
private Integer ttl = 128;
private Integer protocol;
private InetAddress srcIp;
private InetAddress dstIp;
private Buffer data;
private PacketBuilder nextBuilder;
public Builder id(int id) {
this.id = id;
return this;
}
public Builder src(String ip) {
try {
return src(InetAddress.getByName(ip));
} catch (UnknownHostException e) {
throw new IllegalArgumentException("invalid source ip format");
}
}
public Builder src(InetAddress ip) {
this.srcIp = ip;
return this;
}
public Builder dst(String ip) {
try {
return dst(InetAddress.getByName(ip));
} catch (UnknownHostException e) {
throw new IllegalArgumentException("invalid destination ip format");
}
}
public Builder dst(InetAddress ip) {
this.dstIp = ip;
return this;
}
public Builder proto(int protocol) {
this.protocol = protocol;
return this;
}
public Builder data(Buffer data) {
this.data = data;
return this;
}
public Builder data(PacketBuilder builder) {
this.nextBuilder = builder;
return this;
}
@Override
public Object getDefault(String name) {
if (name.equals("src_ip")) {
if (srcIp == null)
return nextBuilder.getDefault(name);
return srcIp;
}
if (name.equals("dst_ip")) {
if (dstIp == null)
return nextBuilder.getDefault(name);
return dstIp;
}
if (name.equals("eth_type")) {
return EthernetType.IPV4;
}
if (nextBuilder != null)
return nextBuilder.getDefault(name);
return null;
}
@Override
public Ipv4Packet build() {
// resolve
if (dstIp == null) {
dstIp = (InetAddress) getDefault("dst_ip");
if (dstIp == null)
throw new IllegalStateException("destination ip not found");
}
if (srcIp == null) {
srcIp = (InetAddress) getDefault("src_ip");
if (srcIp == null) {
PcapDeviceMetadata metadata = PcapDeviceManager.getDeviceMetadata(dstIp);
if (metadata == null)
throw new IllegalStateException("interface not found");
srcIp = metadata.getInet4Address();
}
}
if (protocol == null) {
protocol = (Integer) getDefault("ip_proto");
if (protocol == null)
throw new IllegalStateException("ip protocol is not specified");
}
// set
Ipv4Packet p = new Ipv4Packet();
p.version = 4;
p.ihl = ihl;
p.tos = tos;
p.id = id;
p.flags = 0;
p.fragmentOffset = fragmentOffset;
p.ttl = ttl;
p.protocol = protocol;
p.source = IpConverter.toInt((Inet4Address) srcIp);
p.sourceAddress = srcIp;
p.destination = IpConverter.toInt((Inet4Address) dstIp);
p.destinationAddress = dstIp;
p.data = data;
if (data == null && nextBuilder != null)
p.data = nextBuilder.build().getBuffer();
int dlen = 0;
if (p.data != null)
dlen = p.data.readableBytes();
if (totalLength != null)
p.totalLength = totalLength;
else
p.totalLength = ihl + dlen;
p.headerChecksum = IpChecksum.sum(p);
return p;
}
}
public static Ipv4Packet create(InetAddress source, InetAddress destination, Buffer buffer) {
Ipv4Packet p = new Ipv4Packet();
return p;
}
public static Ipv4Packet parse(Buffer buffer) {
Ipv4Packet p = new Ipv4Packet();
p.parseVersionIhl(buffer);
p.tos = (buffer.get() & 0xff);
p.totalLength = (buffer.getUnsignedShort());
p.id = (buffer.getUnsignedShort());
p.parseFlagsFragOffset(buffer);
p.ttl = (buffer.get() & 0xff);
p.protocol = (buffer.get() & 0xff);
p.headerChecksum = (buffer.getUnsignedShort());
p.source = buffer.getInt();
p.sourceAddress = IpConverter.toInetAddress(p.source);
p.destination = buffer.getInt();
p.destinationAddress = IpConverter.toInetAddress(p.destination);
p.parseOptions(buffer);
/* need discard */
p.data = buffer;
p.data.discardReadBytes();
return p;
}
private void parseVersionIhl(Buffer data) {
byte b = data.get();
short s = (short) (b & 0x00ff);
version = (int) ((s >> 4) & 0xf);
ihl = (int) ((s & 0x000f) * 4);
}
private void parseFlagsFragOffset(Buffer data) {
short flagsAndFragmentOffset = data.getShort();
flags = (int) (((short) ((flagsAndFragmentOffset >> 13) & 0x0007)) & 0xffff);
fragmentOffset = (int) ((short) (flagsAndFragmentOffset & 0x1fff) & 0xffff);
}
private void parseOptions(Buffer data) {
if (ihl <= 20)
return;
int optionLength = ihl - 20;
options = new byte[optionLength];
for (int i = 0; i < optionLength; i++)
options[i] = data.get();
if ((optionLength % 4) == 0)
return;
for (int i = 0; i < optionLength % 4; i++)
padding[i] = data.get();
}
public static Ipv4Packet makeReassembled(Ipv4Packet other, Buffer data, int totalLength) {
Ipv4Packet p = new Ipv4Packet();
p.version = other.version;
p.ihl = other.ihl;
p.tos = other.tos;
p.totalLength = totalLength;
p.id = other.id;
p.flags = 0;
p.fragmentOffset = 0;
p.ttl = other.ttl;
p.protocol = other.protocol;
p.headerChecksum = other.headerChecksum;
p.source = other.source;
p.destination = other.destination;
p.sourceAddress = other.sourceAddress;
p.destinationAddress = other.destinationAddress;
p.data = data;
if (other.options != null && other.padding != null) {
p.options = Arrays.copyOf(other.options, other.options.length);
p.padding = Arrays.copyOf(other.padding, other.padding.length);
}
return p;
}
@Override
public int getVersion() {
return version;
}
public int getIhl() {
return ihl;
}
public int getTos() {
return tos;
}
public int getTotalLength() {
return totalLength;
}
public int getId() {
return id;
}
public int getFlags() {
return flags;
}
public int getFragmentOffset() {
return fragmentOffset;
}
public int getTtl() {
return ttl;
}
public int getProtocol() {
return protocol;
}
public int getHeaderChecksum() {
return headerChecksum;
}
public int getSource() {
return source;
}
@Override
public InetAddress getSourceAddress() {
return sourceAddress;
}
public int getDestination() {
return destination;
}
@Override
public InetAddress getDestinationAddress() {
return destinationAddress;
}
public byte[] getOptions() {
return options;
}
public byte[] getPadding() {
return padding;
}
@Override
public Buffer getData() {
return data;
}
@Override
public Buffer getBuffer() {
ByteBuffer hbuf = ByteBuffer.allocate(20);
hbuf.put((byte) 0x45); // version and ihl hardcoded
hbuf.put((byte) 0);
hbuf.putShort((short) totalLength);
hbuf.putShort((short) id);
hbuf.putShort((short) 0); // frag offset not supported yet
hbuf.put((byte) ttl);
hbuf.put((byte) protocol);
hbuf.putShort((short) headerChecksum);
hbuf.putInt(source);
hbuf.putInt(destination);
Buffer buf = new ChainBuffer();
buf.addLast(hbuf.array());
buf.addLast(data);
return buf;
}
@Override
public String toString() {
return String
.format(
"ip {%s > %s - version: %s, header_length: %d, total_length: %d, id: %d, fragment_offset: %d, ttl: %d, header_checksum: 0x%02X}",
sourceAddress.toString().substring(1), destinationAddress.toString().substring(1),
getVersion(), ihl,
totalLength, id, fragmentOffset, ttl, headerChecksum);
}
}