All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opendaylight.vtn.manager.packet.IPv4 Maven / Gradle / Ivy

There is a newer version: 0.8.0
Show newest version
/*
 * Copyright (c) 2013, 2015 Cisco Systems, Inc. and others. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */

package org.opendaylight.vtn.manager.packet;

import static org.opendaylight.vtn.manager.util.NumberUtils.MASK_BYTE;
import static org.opendaylight.vtn.manager.util.NumberUtils.MASK_SHORT;
import static org.opendaylight.vtn.manager.util.NumberUtils.NUM_OCTETS_SHORT;
import static org.opendaylight.vtn.manager.util.NumberUtils.toBytes;

import java.util.Arrays;
import java.util.EnumMap;
import java.util.Map;

import com.google.common.collect.ImmutableMap;

import org.opendaylight.vtn.manager.util.ByteUtils;
import org.opendaylight.vtn.manager.util.InetProtocols;
import org.opendaylight.vtn.manager.util.Ip4Network;

/**
 * {@code IPv4} describes an IP version 4 packet.
 *
 * 

* This class is provided only for VTN internal use. * This class may be changed without any notice. *

* * @since Beryllium */ public final class IPv4 extends Packet { /** * The IPv4 version number. */ public static final byte IPV4_VERSION = 4; /** * The number of octets in IP checksum. */ public static final int CKSUM_BYTES = NUM_OCTETS_SHORT; /** * The field name that indicates the Inetnet Protocol version. */ private static final String VERSION = "Version"; /** * The field name that indicates the IP header length. */ private static final String HEADERLENGTH = "HeaderLength"; /** * The field name that indicates the IP differential service. */ private static final String DIFFSERV = "DiffServ"; /** * The field name that indicates the ECN bits. */ private static final String ECN = "ECN"; /** * The field name that indicates the total length. */ private static final String TOTLENGTH = "TotalLength"; /** * The field name that indicates the IPv4 identification. */ private static final String IDENTIFICATION = "Identification"; /** * The field name that indicates the IPv4 flags. */ private static final String FLAGS = "Flags"; /** * The field name that indicates the IPv4 fragment offset. */ private static final String FRAGOFFSET = "FragmentOffset"; /** * The field name that indicates the IPv4 Time-To-Live. */ private static final String TTL = "TTL"; /** * The field name that indicates the IP protocol number. */ private static final String PROTOCOL = "Protocol"; /** * The field name that indicates the IP checksum. */ private static final String CHECKSUM = "Checksum"; /** * The field name that indicates the source IP address. */ private static final String SIP = "SourceIPAddress"; /** * The field name that indicates the destination IP address. */ private static final String DIP = "DestinationIPAddress"; /** * The field name that indicates the IPv4 options. */ private static final String OPTIONS = "Options"; /** * The number of bits in the IP protocol version field. */ private static final int BITS_VERSION = 4; /** * The number of bits in the IP header length field. */ private static final int BITS_HEADERLENGTH = 4; /** * The number of bits in the IP differential service field. */ private static final int BITS_DIFFSERV = 6; /** * The number of bits in the ECN field. */ private static final int BITS_ECN = 2; /** * The number of bits in the IPv4 flags field. */ private static final int BITS_FLAGS = 3; /** * The number of bits in the IPv4 fragment offset field. */ private static final int BITS_FRAGOFFSET = 13; /** * The number of bits to calculate IPv4 unit size. */ private static final int UNIT_SIZE_SHIFT = 2; /** * The number of bytes in an IPv4 unit value. */ private static final int UNIT_SIZE = (1 << UNIT_SIZE_SHIFT); /** * The minimum size of the IPv4 header in bytes. */ private static final int MIN_HEADER_SIZE = 20; /** * The default value of the header length field. */ private static final byte DEFAULT_HEADER_LENGTH = (byte)(MIN_HEADER_SIZE >>> UNIT_SIZE_SHIFT); /** * The default value of the IPv4 flags. */ private static final byte DEFAULT_FLAGS = 2; /** * A map that determines the IPv4 packet header format. */ private static final Map HEADER_FORMAT; /** * A set of supported payload types. */ private static final Map> PAYLOAD_TYPES; /** * Initialize static fields. */ static { // Initialize the IPv4 header format. int addrSize = Ip4Network.SIZE * Byte.SIZE; HEADER_FORMAT = new HeaderMapBuilder(). addNumber(VERSION, BITS_VERSION). addNumber(HEADERLENGTH, BITS_HEADERLENGTH). addNumber(DIFFSERV, BITS_DIFFSERV). addNumber(ECN, BITS_ECN). addNumber(TOTLENGTH, Short.SIZE). addNumber(IDENTIFICATION, Short.SIZE). addNumber(FLAGS, BITS_FLAGS). addNumber(FRAGOFFSET, BITS_FRAGOFFSET). addNumber(TTL, Byte.SIZE). addNumber(PROTOCOL, Byte.SIZE). addNumber(CHECKSUM, Short.SIZE). addByte(SIP, addrSize). addByte(DIP, addrSize). addByte(OPTIONS, 0). build(); // Initialize the payload types. Map> typeMap = new EnumMap<>(InetProtocols.class); typeMap.put(InetProtocols.ICMP, ICMP.class); typeMap.put(InetProtocols.TCP, TCP.class); typeMap.put(InetProtocols.UDP, UDP.class); PAYLOAD_TYPES = ImmutableMap.copyOf(typeMap); } /** * Determine the payload type for the given IP protocol number. * * @param proto The IP protocol number. * @return A class for the payload type. * {@code null} if no payload type is defined for the given * IP protocol number. */ static Class getPayloadClass(byte proto) { InetProtocols ipproto = InetProtocols.forValue(proto); return (ipproto == null) ? null : PAYLOAD_TYPES.get(ipproto); } /** * Construct a new instance. * *

* This constructor sets the version to 4, headerLength to 5, * and flags to 2. *

*/ public IPv4() { setVersion(IPV4_VERSION); setHeaderLength(DEFAULT_HEADER_LENGTH); setFlags(DEFAULT_FLAGS); } /** * Gets the IP version stored. * * @return The IP version. */ public byte getVersion() { return getByte(VERSION); } /** * Gets the IP header length stored. * * @return The IP header length in bytes. */ public int getHeaderLen() { return (getByte(HEADERLENGTH) << UNIT_SIZE_SHIFT); } /** * Gets the differential services value stored. * * @return The differential services value. */ public byte getDiffServ() { return getByte(DIFFSERV); } /** * Gets the ECN bits stored. * * @return The ECN bits. */ public byte getECN() { return getByte(ECN); } /** * Gets the total length of the IPv4 packe in bytes. * * @return The total length of the IPv4 packet. */ public short getTotalLength() { return getShort(TOTLENGTH); } /** * Gets the identification value stored. * * @return The IP identification value. */ public short getIdentification() { return getShort(IDENTIFICATION); } /** * Gets the flag values stored. * * @return The IPv4 flags. */ public byte getFlags() { return getByte(FLAGS); } /** * Gets the TTL value stored. * * @return The TTL value. */ public byte getTtl() { return getByte(TTL); } /** * Gets the protocol value stored. * * @return The IP protocol number. */ public byte getProtocol() { return getByte(PROTOCOL); } /** * Gets the checksum value stored. * * @return The IP header checksum. */ public short getChecksum() { return getShort(CHECKSUM); } /** * Gets the fragment offset stored. * * @return The IP fragment offset. */ public short getFragmentOffset() { return getShort(FRAGOFFSET); } /** * Gets the source IP address stored. * * @return An {@link Ip4Network} instance that represents the source * IP address. */ public Ip4Network getSourceAddress() { return getIp4Network(SIP); } /** * Gets the destination IP address stored. * * @return An {@link Ip4Network} instance that represents the destination * IP address. */ public Ip4Network getDestinationAddress() { return getIp4Network(DIP); } /** * Gets the Options stored. * * @return The IPv4 options. {@code null} if no option is present. */ public byte[] getOptions() { byte[] opts = getHeaderFieldMap().get(OPTIONS); return (opts == null || opts.length == 0) ? null : opts.clone(); } /** * Stores the IP version from the header. * * @param version The version to set * @return This instance. */ public IPv4 setVersion(byte version) { getHeaderFieldMap().put(VERSION, new byte[]{version}); return this; } /** * Stores the length of IP header in words (4 bytes). * * @param len The headerLength to set. * @return This instance. */ public IPv4 setHeaderLength(byte len) { getHeaderFieldMap().put(HEADERLENGTH, new byte[]{len}); return this; } /** * Stores the differential services value from the IP header. * * @param dserv The differential services value. * @return This instance. */ public IPv4 setDiffServ(byte dserv) { getHeaderFieldMap().put(DIFFSERV, new byte[]{dserv}); return this; } /** * Stores the ECN bits from the header. * * @param ecn The ECN bits to set. * @return This instance. */ public IPv4 setECN(byte ecn) { getHeaderFieldMap().put(ECN, new byte[]{ecn}); return this; } /** * Stores the total length of IPv4 packet in bytes. * * @param len The total length of the IPv4 packet. * @return This instance. */ public IPv4 setTotalLength(short len) { getHeaderFieldMap().put(TOTLENGTH, toBytes(len)); return this; } /** * Stores the identification number from the header. * * @param id The identification to set. * @return This instance. */ public IPv4 setIdentification(short id) { getHeaderFieldMap().put(IDENTIFICATION, toBytes(id)); return this; } /** * Stores the IP flags value. * * @param flags The flags to set. * @return This instance. */ public IPv4 setFlags(byte flags) { getHeaderFieldMap().put(FLAGS, new byte[]{flags}); return this; } /** * Stores the IP fragmentation offset value. * * @param off The fragmentation offset. * @return This instance. */ public IPv4 setFragmentOffset(short off) { getHeaderFieldMap().put(FRAGOFFSET, toBytes(off)); return this; } /** * Stores the TTL value. * * @param ttl The ttl to set. * @return This instance. */ public IPv4 setTtl(byte ttl) { getHeaderFieldMap().put(TTL, new byte[]{ttl}); return this; } /** * Stores the protocol value of the IP payload. * * @param proto The protocol to set. * @return This instance. */ public IPv4 setProtocol(byte proto) { getHeaderFieldMap().put(PROTOCOL, new byte[]{proto}); return this; } /** * Set the IP checksum value. * *

* This method is only for testing. * IP checksum will be updated on serialization. *

* * @param cksum The IP checksum. * @return This instance. */ public IPv4 setChecksum(short cksum) { getHeaderFieldMap().put(CHECKSUM, toBytes(cksum)); return this; } /** * Stores the IP source address from the header. * *

* The network prefix in the given paramter is always ignored. *

* * @param addr The source IP address. * @return This instance. */ public IPv4 setSourceAddress(Ip4Network addr) { getHeaderFieldMap().put(SIP, addr.getBytes()); return this; } /** * Stores the IP destination address from the header. * *

* The network prefix in the given paramter is always ignored. *

* * @param addr The destination IP address. * @return This instance. */ public IPv4 setDestinationAddress(Ip4Network addr) { getHeaderFieldMap().put(DIP, addr.getBytes()); return this; } /** * Store the options from IP header. * * @param options The IP options. * @return This instance. */ public IPv4 setOptions(byte[] options) { Map map = getHeaderFieldMap(); byte newIHL = DEFAULT_HEADER_LENGTH; if (options == null || options.length == 0) { map.remove(OPTIONS); } else { int len = options.length; int mask = UNIT_SIZE - 1; int rlen = (len + mask) & ~mask; byte[] newopt; if (rlen > len) { // Padding is required. newopt = new byte[rlen]; System.arraycopy(options, 0, newopt, 0, len); len = rlen; } else { newopt = options.clone(); } map.put(OPTIONS, newopt); newIHL += ((len & MASK_BYTE) >>> UNIT_SIZE_SHIFT); } map.put(HEADERLENGTH, new byte[]{newIHL}); return this; } /** * Computes the IPv4 header checksum on the passed stream of bytes * representing the packet. * * @param data The byte stream. * @param start The byte offset from where the IPv4 packet starts. * @return The computed checksum */ short computeChecksum(byte[] data, int start) { int end = start + getHeaderLen() - 1; int sum = 0; int wordData; int checksumStart = start + (getFieldOffset(CHECKSUM) / Byte.SIZE); for (int i = start; i <= end; i += CKSUM_BYTES) { // Skip, if the current bytes are checkSum bytes if (i != checksumStart) { wordData = ((data[i] & MASK_BYTE) << Byte.SIZE) | (data[i + 1] & MASK_BYTE); sum = sum + wordData; } } int carry = sum >>> Short.SIZE; int finalSum = (sum & MASK_SHORT) + carry; return (short)~((short)finalSum & MASK_SHORT); } // Packet /** * Gets the header size in bits. * * @return The number of bits constituting the header. */ @Override public int getHeaderSize() { int len = getHeaderLen(); if (len == 0) { len = MIN_HEADER_SIZE; } return len * Byte.SIZE; } /** * Gets the number of bits for the specified field. * *

* If the fieldname has variable length like "Options", then this value * is computed using the header length. *

* * @param name The name of the header field. * @param entry The header field entry associated with {@code name}. * @return The number of bits of the requested field. */ @Override protected int getFieldNumBits(String name, HeaderField entry) { return (name.equals(OPTIONS)) ? (getHeaderLen() - MIN_HEADER_SIZE) * Byte.SIZE : entry.getNumBits(); } /** * {@inheritDoc} */ @Override protected Map getHeaderFormat() { return HEADER_FORMAT; } /** * Stores the value of fields read from data stream. * *

* Variable header value like payload protocol, is stored here. *

* * @param name The name of the header field. * @param value The value to be associated with the specified header * field. {@code null} cannot be specified. */ @Override protected void setHeaderField(String name, byte[] value) { Map map = getHeaderFieldMap(); if (name.equals(PROTOCOL)) { // Don't set payloadClass if framgment offset is not zero. byte[] fragoff = map.get(FRAGOFFSET); if (isZeroShort(fragoff)) { setPayloadClass(getPayloadClass(value[0])); } } else if (name.equals(FRAGOFFSET)) { if (!isZeroShort(value)) { // Clear payloadClass because protocol header is not present // in this packet. setPayloadClass(null); } } else if (name.equals(OPTIONS) && (value == null || value.length == 0)) { map.remove(name); return; } map.put(name, value); } /** * {@inheritDoc} */ @Override protected boolean equalsField(String name, byte[] value1, byte[] value2) { return (name.equals(OPTIONS)) ? Arrays.equals(value1, value2) : super.equalsField(name, value1, value2); } /** * Set the packet encapsulated by this IPv4 packet. * *

* This method updates the total length of the IPv4 packet. *

* * @param p The packet encapsulated by this IPv4 packet. */ @Override public void setPayload(Packet p) { super.setPayload(p); if (p != null) { // Determine the total length. int plen; try { plen = p.serialize().length; } catch (PacketException e) { throw new IllegalStateException( "Failed to serialize the specified payload.", e); } setTotalLength((short)(getHeaderLen() + plen)); } } /** * This method gets called at the end of the serialization process. * *

* This method computes the IP checksum and stores it into the IP header. *

* * @param myBytes Serialized bytes. * @throws PacketException An error occurred. */ @Override protected void postSerializeCustomOperation(byte[] myBytes) throws PacketException { try { Map map = getHeaderFieldMap(); String field = TOTLENGTH; HeaderField entry = HEADER_FORMAT.get(field); int off = getFieldOffset(field, entry); int nbits = getFieldNumBits(field, entry); // Recompute the total length field here. byte[] total = toBytes((short)myBytes.length); ByteUtils.setBits(myBytes, total, off, nbits); map.put(TOTLENGTH, total); // Compute the header checksum. field = CHECKSUM; entry = HEADER_FORMAT.get(field); off = getFieldOffset(field, entry); nbits = getFieldNumBits(field, entry); byte[] cksum = toBytes(computeChecksum(myBytes, 0)); ByteUtils.setBits(myBytes, cksum, off, nbits); map.put(CHECKSUM, cksum); } catch (RuntimeException e) { throw new PacketException("Failed to update checksum.", e); } } /** * This method re-computes the checksum of the bits received on the wire * and validates it with the checksum in the bits received. * * @param data The byte stream representing the Ethernet frame. * @param offset The bit offset from where the byte array corresponding to * this Packet starts in the frame */ @Override protected void postDeserializeCustomOperation(byte[] data, int offset) { short computed = computeChecksum(data, offset / Byte.SIZE); short actual = getChecksum(); if (computed != actual) { setCorrupted(true); } } // Object /** * {@inheritDoc} */ @Override public IPv4 clone() { return (IPv4)super.clone(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy