package com.netifera.platform.net.packets.tcpip; import java.nio.ByteBuffer; import com.netifera.platform.net.packets.AbstractPacket; import com.netifera.platform.net.packets.PacketException; import com.netifera.platform.net.packets.PacketPayload; import com.netifera.platform.net.packets.link.EthernetEncapsulable; import com.netifera.platform.util.NetworkConstants; import com.netifera.platform.util.addresses.inet.IPv4Address; public class IPv4 extends AbstractPacket implements IP, IPFragment, EthernetEncapsulable, IPv4Encapsulable, IPv6Encapsulable { private static final int IPv4_VERSION = 4; private int version = IPv4_VERSION; private int headerLength32; private int tos; private int totalLength; private int identification; private boolean reserved; private boolean CE; /** * The Don't Fragment bit carried in the header flags field. * * false: May Fragment, * true: Don't Fragment. */ private boolean DF; /** * The More-Fragments Flag carried in the header flags field. * * false = Last Fragment, * true = More Fragments. */ private boolean MF = false; private int fragmentOffset = 0; private int timeToLive; private int protocol; private int headerChecksum; private IPv4Address sourceAddress; private IPv4Address destinationAddress; /** 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |Version| IHL |Type of Service| Total Length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Identification |Flags| Fragment Offset | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Time to Live | Protocol | Header Checksum | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Source Address | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Destination Address | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Options | Padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ @Override protected void packHeader() { pack8((version << 4) | headerLength32); pack8(tos); pack16(totalLength); pack16(identification); pack16(packFlagsFragment()); pack8(timeToLive); pack8(protocol); pack16(headerChecksum); pack32(sourceAddress.toInteger()); pack32(destinationAddress.toInteger()); } @Override protected void unpackHeader() { int vhl = unpack8(); version = (vhl >> 4) & 0x0F; headerLength32 = vhl & 0x0F; tos = unpack8(); totalLength = unpack16(); totalLengthSet = true; identification = unpack16(); unpackFlagsFragment(unpack16()); timeToLive = unpack8(); protocol = unpack8(); headerChecksum = unpack16(); sourceAddress = new IPv4Address(unpack32()); destinationAddress = new IPv4Address(unpack32()); } @Override protected boolean isValidHeader() { return (version == IPv4_VERSION) && (headerLength32 >= 5) && (totalLength >= (headerLength32 * 4)); } /** user override flags */ private boolean identificationSet; private boolean checksumSet; private boolean totalLengthSet; public static final int FL_RESERVED = (1 << 15); public static final int FL_CE = FL_RESERVED; public static final int FL_DF = (1 << 14); public IPv4() {}; public IPv4(IPv4Encapsulable payload) { super(payload); headerLength32 = 5; timeToLive = 255; DF = true; protocol = payload.protocolOverIPv4(); } public IPv4 createPacket() { return new IPv4(); } public int protocolOverEthernet() { return NetworkConstants.ETHERTYPE_IPv4; } public int protocolOverIPv4() { return NetworkConstants.IPPROTO_IPIP; } public int protocolOverIPv6() { return NetworkConstants.IPPROTO_IPV6; // FIXME verify } public int protocolOverPPP() { return NetworkConstants.PPP_IP; } @Override protected int nextProtocol() { /* value from header, but is it set yet? */ return protocol; } public void setVersion(int value) { verifyMaximum(value, 0xF); version = value; } public int getVersion() { return version; } public void setHeaderLength32(int value) { verifyMaximum(value, 0xF); headerLength32 = value; } public int getHeaderLength32() { return headerLength32; } @Override public int headerLength() { return headerLength32 * 4; } @Override public int getLength() { if (totalLengthSet) { return getTotalLength(); } else { return super.getLength(); } } public void setTypeOfService(int value) { verifyMaximum(value, 0xFF); tos = value; } public int getTypeOfService() { return tos; } public void setTotalLength(int value) { verifyMaximum(value, 0xFFFF); totalLength = value; totalLengthSet = true; } public int getTotalLength() { return totalLength; } public void setIdentification(int value) { verifyMaximum(value, 0xFFFF); identification = value; identificationSet = true; } public int getIdentification() { return identification; } public void setReserved(boolean value) { reserved = value; } public boolean getReserved() { return reserved; } public void setCongestion(boolean value) { CE = value; } public boolean getCongestion() { return CE; } public void setDF(boolean value) { DF = value; } public boolean getDF() { return DF; } public void setMF(boolean value) { MF = value; } public boolean getMF() { return MF; } public boolean hasMoreFragments() { return getMF(); } public void setMoreFragments(boolean value) { setMF(value); } public void setFragmentOffset(int value) { verifyMaximum(value, FRAGMENT_MASK); fragmentOffset = value; } public int getFragmentOffset() { return fragmentOffset; } public boolean isFragment() { return getMF() || getFragmentOffset() > 0; } public IPFragment fragment() { return this; } public void setTimeToLive(int value) { verifyMaximum(value, 0xFF); timeToLive = value; } public int getTimeToLive() { return timeToLive; } public void setProtocol(int value) { verifyMaximum(value, 0xFF); protocol = value; } public int getProtocol() { return protocol; } public void setHeaderChecksum(int value) { verifyMaximum(value, 0xFFFF); headerChecksum = value; checksumSet = true; } public int getHeaderChecksum() { return headerChecksum; } /** * Set the source address. * * @param address The source address */ public void setSourceAddress(IPv4Address address) { sourceAddress = address; } public IPv4Address getSourceAddress() { return sourceAddress; } /** * Set the destination address. * * @param address The destination address */ public void setDestinationAddress(IPv4Address address) { destinationAddress = address; } public IPv4Address getDestinationAddress() { return destinationAddress; } @Override protected void populateGeneratedFields() { if(getLength() > 0xFFFF) { throw new PacketException("Packet is too big to fit in IPv4 header"); } if(!totalLengthSet) { totalLength = getLength(); } if(!identificationSet) { generateIdentification(); } if(!checksumSet) { headerChecksum = 0; } if(getNextHeader() instanceof IPseudoHeaderClient) { sendPseudoheaderInfo((IPseudoHeaderClient)getNextHeader()); } } @Override protected void calculateChecksum() { if(!checksumSet) { headerChecksum = generateChecksum(headerLength32 * 4); pack16(headerChecksum, 10); } } private void sendPseudoheaderInfo(IPseudoHeaderClient target) { ByteBuffer pseudo = ByteBuffer.allocate(12); pseudo.putInt(sourceAddress.toInteger()); pseudo.putInt(destinationAddress.toInteger()); pseudo.put((byte) 0); pseudo.put((byte) protocol); pseudo.putShort((short) getNextHeader().getLength()); target.setPseudoHeader(pseudo); } private int packFlagsFragment() { int n = fragmentOffset / 8; if(reserved) { n |= FL_RESERVED; } if(CE) { n |= FL_CE; } if(DF) { n |= FL_DF; } if(MF) { n |= FL_MF; } return n; } private static int nextId = 1; private void generateIdentification() { synchronized(IPv4.class) { identification = nextId++; } } @Override protected int minimumHeaderLength() { return 20; } private void unpackFlagsFragment(int value) { fragmentOffset = (value & FRAGMENT_MASK) * 8; reserved = ((value & FL_RESERVED) != 0); CE = ((value & FL_CE) != 0); DF = ((value & FL_DF) != 0); MF = ((value & FL_MF) != 0); } @Override public String toString() { StringBuffer out = new StringBuffer(); out.append("IPv4 "); if (isFragment()) out.append("Fragment "); out.append(sourceAddress); out.append(" -> "); out.append(destinationAddress); if (getDF()) out.append(" (DF)"); if (getMF()) out.append(" (MF)"); if (isFragment()) out.append(" offset="+getFragmentOffset()+" length="+getLength()); return out.toString(); } @Override public PacketPayload payload() { return new PacketPayload(headerBufferSlice(getHeaderLength())); } public void copyHeader(ByteBuffer writeBuffer) { ByteBuffer b = toByteBuffer(); for(int i = 0; i < getHeaderLength(); i++) { writeBuffer.put(b.get(i)); } } }