using System; using SharpPcap.Packets.Util; namespace SharpPcap.Packets { public class IPv4Packet : EthernetPacket { /// Type of service code constants for IP. Type of service describes /// how a packet should be handled. ///

/// TOS is an 8-bit record in an IP header which contains a 3-bit /// precendence field, 4 TOS bit fields and a 0 bit. ///

/// The following constants are bit masks which can be logically and'ed /// with the 8-bit IP TOS field to determine what type of service is set. ///

/// Taken from TCP/IP Illustrated V1 by Richard Stevens, p34. /// ///

public struct TypesOfService_Fields{ public readonly static int MINIMIZE_DELAY = 0x10; public readonly static int MAXIMIZE_THROUGHPUT = 0x08; public readonly static int MAXIMIZE_RELIABILITY = 0x04; public readonly static int MINIMIZE_MONETARY_COST = 0x02; public readonly static int UNUSED = 0x01; } public static int ipVersion = 4; /// /// should be overriden by upper classes /// public override void OnOffsetChanged() { base.OnOffsetChanged(); _ipOffset = _ethOffset + IPHeaderLength; } /// Get the IP version code. public virtual int Version { get { return IPVersion; } set { IPVersion = value; } } /// Get the IP version code. virtual public int IPVersion { get { return (ArrayHelper.extractInteger(Bytes, _ethOffset + IPv4Fields_Fields.IP_VER_POS, IPv4Fields_Fields.IP_VER_LEN) >> 4) & 0xf; } set { Bytes[_ethOffset + IPv4Fields_Fields.IP_VER_POS] &= (byte)(0x0f); Bytes[_ethOffset + IPv4Fields_Fields.IP_VER_POS] |= (byte)(((value << 4) & 0xf0)); } } /// Fetch the IP header length in bytes. /// Sets the IP header length field. At most, this can be a /// four-bit value. The high order bits beyond the fourth bit /// will be ignored. /// /// /// The length of the IP header in 32-bit words. /// virtual public int IPHeaderLength { get { return (ArrayHelper.extractInteger(Bytes, _ethOffset + IPv4Fields_Fields.IP_VER_POS, IPv4Fields_Fields.IP_VER_LEN) & 0xf) * 4; } set { value /= 4; // Clear low order bits and then set Bytes[_ethOffset + IPv4Fields_Fields.IP_VER_POS] &= (byte)(0xf0); Bytes[_ethOffset + IPv4Fields_Fields.IP_VER_POS] |= (byte)(value & 0x0f); // set offset into _bytes of previous layers _ipOffset = _ethOffset + IPHeaderLength; } } /// Fetch the packet IP header length. override public int HeaderLength { get { return IPHeaderLength; } } /// Fetch the unique ID of this IP datagram. The ID normally /// increments by one each time a datagram is sent by a host. /// /// Sets the IP identification header value. /// /// /// A 16-bit unsigned integer. /// virtual public int Id { get { return ArrayHelper.extractInteger(Bytes, _ethOffset + IPv4Fields_Fields.IP_ID_POS, IPv4Fields_Fields.IP_ID_LEN); } set { ArrayHelper.insertLong(Bytes, value, _ethOffset + IPv4Fields_Fields.IP_ID_POS, IPv4Fields_Fields.IP_ID_LEN); } } /// Fetch fragmentation offset. /// Sets the fragment offset header value. The offset specifies a /// number of octets (i.e., bytes). /// /// /// A 13-bit unsigned integer. /// virtual public int FragmentOffset { get { return ArrayHelper.extractInteger(Bytes, _ethOffset + IPv4Fields_Fields.IP_FRAG_POS, IPv4Fields_Fields.IP_FRAG_LEN) & 0x1fff; } set { Bytes[_ethOffset + IPv4Fields_Fields.IP_FRAG_POS] &= (byte)(0xe0); Bytes[_ethOffset + IPv4Fields_Fields.IP_FRAG_POS] |= (byte)(((value >> 8) & 0x1f)); Bytes[_ethOffset + IPv4Fields_Fields.IP_FRAG_POS + 1] = (byte)(value & 0xff); } } /// Fetch the IP address of the host where the packet originated from. virtual public System.Net.IPAddress SourceAddress { get { return IPPacket.GetIPAddress(System.Net.Sockets.AddressFamily.InterNetwork, _ethOffset + IPv4Fields_Fields.IP_SRC_POS, Bytes); } set { byte[] address = value.GetAddressBytes(); System.Array.Copy(address, 0, Bytes, _ethOffset + IPv4Fields_Fields.IP_SRC_POS, address.Length); } } /// Fetch the IP address of the host where the packet is destined. virtual public System.Net.IPAddress DestinationAddress { get { return IPPacket.GetIPAddress(System.Net.Sockets.AddressFamily.InterNetwork, _ethOffset + IPv4Fields_Fields.IP_DST_POS, Bytes); } set { byte[] address = value.GetAddressBytes(); System.Array.Copy(address, 0, Bytes, _ethOffset + IPv4Fields_Fields.IP_DST_POS, address.Length); } } /// Fetch the IP header a byte array. virtual public byte[] IPHeader { get { return PacketEncoding.extractHeader(_ethOffset, IPHeaderLength, Bytes); } } /// Fetch the IP header as a byte array. override public byte[] Header { get { return IPHeader; } } /// Fetch the IP data as a byte array. virtual public byte[] IPData { get { // set data length based on info in headers (note: tcpdump // can return extra junk bytes which bubble up to here //tamir: changed getLength() to specific getIPTotalLength() to fix //confusion in subclasses overloading getLength() int payloadLen = IPTotalLength - IPHeaderLength; return PacketEncoding.extractData(_ethOffset, IPHeaderLength, Bytes, payloadLen); } } /// Fetch the header checksum. virtual public int IPChecksum { get { return ArrayHelper.extractInteger(Bytes, _ethOffset + IPv4Fields_Fields.IP_CSUM_POS, IPv4Fields_Fields.IP_CSUM_LEN); } set { SetChecksum(value, _ethOffset + IPv4Fields_Fields.IP_CSUM_POS); } } /// Check if the IP packet is valid, checksum-wise. virtual public bool ValidChecksum { get { return ValidIPChecksum; } } /// Check if the IP packet is valid, checksum-wise. virtual public bool ValidIPChecksum { get { // first validate other information about the packet. if this stuff // is not true, the packet (and therefore the checksum) is invalid // - ip_hl >= 5 (ip_hl is the length in 4-byte words) if (IPHeaderLength < IPv4Fields_Fields.IP_HEADER_LEN) { return false; } else { return (ChecksumUtils.OnesSum(Bytes, _ethOffset, IPHeaderLength) == 0xffff); } } } /// Fetch ascii escape sequence of the color associated with this packet type. override public System.String Color { get { return AnsiEscapeSequences_Fields.WHITE; } } // offset from beginning of byte array where IP header ends (i.e., // size of ethernet frame header and IP header protected internal int _ipOffset; /// Create a new IP packet. public IPv4Packet(int lLen, byte[] bytes) : base(lLen, bytes) { _ipOffset = _ethOffset + IPHeaderLength; } /// Create a new IP packet. public IPv4Packet(int lLen, byte[] bytes, Timeval tv) : this(lLen, bytes) { this._timeval = tv; } /// Fetch the type of service./// public virtual int TypeOfService { get { return ArrayHelper.extractInteger(Bytes, _ethOffset + IPv4Fields_Fields.IP_TOS_POS, IPv4Fields_Fields.IP_TOS_LEN); } set { Bytes[_ethOffset + IPv4Fields_Fields.IP_TOS_POS] = (byte)(value & 0xff); } } /// Fetch the IP length in bytes. public virtual int Length { get { return IPTotalLength; } set { IPTotalLength = value; } } /// Fetch the IP length in bytes. public virtual int IPTotalLength { get { return ArrayHelper.extractInteger(Bytes, _ethOffset + IPv4Fields_Fields.IP_LEN_POS, IPv4Fields_Fields.IP_LEN_LEN); } set { ArrayHelper.insertLong(Bytes, value, _ethOffset + IPv4Fields_Fields.IP_LEN_POS, IPv4Fields_Fields.IP_LEN_LEN); } } public virtual int IPPayloadLength { get { return IPTotalLength - IPv4Fields_Fields.IP_HEADER_LEN; } } /// Fetch fragmentation flags. /// /// A 3-bit unsigned integer. /// public virtual int FragmentFlags { get { // fragment flags are the high 3 bits // int huh = ArrayHelper.extractInteger(_bytes, _ethOffset // + IP_FRAG_POS, IP_FRAG_LEN); return (ArrayHelper.extractInteger(Bytes, _ethOffset + IPv4Fields_Fields.IP_FRAG_POS, IPv4Fields_Fields.IP_FRAG_LEN) >> 13) & 0x7; } set { Bytes[_ethOffset + IPv4Fields_Fields.IP_FRAG_POS] &= (byte)(0x1f); Bytes[_ethOffset + IPv4Fields_Fields.IP_FRAG_POS] |= (byte)(((value << 5) & 0xe0)); } } /// Fetch the time to live. TTL sets the upper limit on the number of /// routers through which this IP datagram is allowed to pass. /// Originally intended to be the number of seconds the packet lives it is now decremented /// by one each time a router passes the packet on /// /// 8-bit value /// public virtual int TimeToLive { get { return ArrayHelper.extractInteger(Bytes, _ethOffset + IPv4Fields_Fields.IP_TTL_POS, IPv4Fields_Fields.IP_TTL_LEN); } set { Bytes[_ethOffset + IPv4Fields_Fields.IP_TTL_POS] = (byte)value; } } /// Fetch the code indicating the type of protocol embedded in the IP /// /// public virtual IPProtocol.IPProtocolType IPProtocol { get { return (IPProtocol.IPProtocolType)ArrayHelper.extractInteger(Bytes, _ethOffset + IPv4Fields_Fields.IP_CODE_POS, IPv4Fields_Fields.IP_CODE_LEN); } set { Bytes[_ethOffset + IPv4Fields_Fields.IP_CODE_POS] = (byte)value; } } /// Fetch the IP data as a byte array. public override byte[] Data { get { return IPData; } } /// Fetch the IP header checksum. public virtual int Checksum { get { return IPChecksum; } set { IPChecksum=value; } } /// Sets the IP header checksum. protected internal virtual void SetChecksum(int cs, int checkSumOffset) { ArrayHelper.insertLong(Bytes, cs, checkSumOffset, 2); } protected internal virtual void SetTransportLayerChecksum(int cs, int csPos) { SetChecksum(cs, _ipOffset + csPos); } /// Computes the IP checksum, optionally updating the IP checksum header. /// /// /// Specifies whether or not to update the IP checksum /// header after computing the checksum. A value of true indicates /// the header should be updated, a value of false indicates it /// should not be updated. /// /// The computed IP checksum. /// public int ComputeIPChecksum(bool update) { //copy the ip header byte[] ip = ArrayHelper.copy(Bytes, _ethOffset, IPHeaderLength); //reset the checksum field (checksum is calculated when this field is zeroed) ArrayHelper.insertLong(ip, 0, IPv4Fields_Fields.IP_CSUM_POS, 2); //compute the one's complement sum of the ip header int cs = ChecksumUtils.OnesComplementSum(ip, 0, ip.Length); if (update) { IPChecksum = cs; } return cs; } /// Same as computeIPChecksum(true); /// /// /// The computed IP checksum value. /// public int ComputeIPChecksum() { return ComputeIPChecksum(true); } // Prepend to the given byte[] origHeader the portion of the IPv6 header used for // generating an tcp checksum // // http://en.wikipedia.org/wiki/Transmission_Control_Protocol#TCP_checksum_using_IPv4 // http://tools.ietf.org/html/rfc793 protected internal virtual byte[] AttachPseudoIPHeader(byte[] origHeader) { bool odd = origHeader.Length % 2 != 0; int numberOfBytesFromIPHeaderUsedToGenerateChecksum = 12; int headerSize = numberOfBytesFromIPHeaderUsedToGenerateChecksum + origHeader.Length; if (odd) headerSize++; byte[] headerForChecksum = new byte[headerSize]; // 0-7: ip src+dest addr Array.Copy(Bytes, _ethOffset + IPv4Fields_Fields.IP_SRC_POS, headerForChecksum, 0, 8); // 8: always zero headerForChecksum[8] = 0; // 9: ip protocol headerForChecksum[9] = (byte)IPProtocol; // 10-11: header+data length ArrayHelper.insertLong(headerForChecksum, origHeader.Length, 10, 2); // prefix the pseudoHeader to the header+data Array.Copy(origHeader, 0, headerForChecksum, numberOfBytesFromIPHeaderUsedToGenerateChecksum, origHeader.Length); //if not even length, pad with a zero if (odd) headerForChecksum[headerForChecksum.Length - 1] = 0; return headerForChecksum; } public virtual bool IsValidTransportLayerChecksum(bool pseudoIPHeader) { byte[] upperLayer = IPData; if (pseudoIPHeader) upperLayer = AttachPseudoIPHeader(upperLayer); int onesSum = ChecksumUtils.OnesSum(upperLayer); return (onesSum == 0xffff); } /// Convert this IP packet to a readable string. public override System.String ToString() { return ToColoredString(false); } /// Generate string with contents describing this IP packet. /// whether or not the string should contain ansi /// color escape sequences. /// public override System.String ToColoredString(bool colored) { System.Text.StringBuilder buffer = new System.Text.StringBuilder(); buffer.Append('['); if (colored) buffer.Append(Color); buffer.Append("IPv4Packet"); if (colored) buffer.Append(AnsiEscapeSequences_Fields.RESET); buffer.Append(": "); buffer.Append(SourceAddress + " -> " + DestinationAddress); buffer.Append(" proto=" + IPProtocol); buffer.Append(" l=" + IPHeaderLength + "," + Length); buffer.Append(']'); // append the base class output buffer.Append(base.ToColoredString(colored)); return buffer.ToString(); } /// Convert this IP packet to a more verbose string. public override System.String ToColoredVerboseString(bool colored) { System.Text.StringBuilder buffer = new System.Text.StringBuilder(); buffer.Append('['); if (colored) buffer.Append(Color); buffer.Append("IPv4Packet"); if (colored) buffer.Append(AnsiEscapeSequences_Fields.RESET); buffer.Append(": "); buffer.Append("version=" + Version + ", "); buffer.Append("hlen=" + HeaderLength + ", "); buffer.Append("tos=" + TypeOfService + ", "); buffer.Append("length=" + Length + ", "); buffer.Append("id=" + Id + ", "); buffer.Append("flags=0x" + System.Convert.ToString(FragmentFlags, 16) + ", "); buffer.Append("offset=" + FragmentOffset + ", "); buffer.Append("ttl=" + TimeToLive + ", "); buffer.Append("proto=" + IPProtocol + ", "); buffer.Append("sum=0x" + System.Convert.ToString(Checksum, 16)); if (this.ValidChecksum) buffer.Append(" (correct), "); else buffer.Append(" (incorrect, should be " + ComputeIPChecksum(false) + "), "); buffer.Append("src=" + SourceAddress + ", "); buffer.Append("dest=" + DestinationAddress); buffer.Append(']'); // append the base class output buffer.Append(base.ToColoredVerboseString(colored)); return buffer.ToString(); } /// This inner class provides access to private methods for unit testing. public class TestProbe { public TestProbe(IPv4Packet enclosingInstance) { InitBlock(enclosingInstance); } private void InitBlock(IPv4Packet enclosingInstance) { this.enclosingInstance = enclosingInstance; } private IPv4Packet enclosingInstance; virtual public int ComputedReceiverIPChecksum { get { return ChecksumUtils.OnesSum(Enclosing_Instance.Bytes, Enclosing_Instance._ethOffset, Enclosing_Instance.IPHeaderLength); } } virtual public int ComputedSenderIPChecksum() { return Enclosing_Instance.ComputeIPChecksum(false); } public IPv4Packet Enclosing_Instance { get { return enclosingInstance; } } } } }