context: /*
  This file is part of PacketDotNet
  
  PacketDotNet is free software: you can redistribute it and/or modify
  it under the terms of the GNU Lesser General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.
  
  PacketDotNet is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU Lesser General Public License for more details.
  
  You should have received a copy of the GNU Lesser General Public License
  along with PacketDotNet.  If not, see <http://www.gnu.org/licenses/>.
  */
  /*
   * Copyright 2009 Chris Morgan <chmorgan@gmail.com>
   */
  
  using System;
  using System.Collections.Generic;
  using System.Text;
  using MiscUtil.Conversion;
  using PacketDotNet.Utils;
  
  namespace PacketDotNet
  {
      /// <summary>
      /// IPv4 packet
      /// See http://en.wikipedia.org/wiki/IPv4 for into
      /// </summary>
      public class IPv4Packet : IpPacket
      {
          // NOTE: No need to warn about lack of use, the compiler won't
          //       put any calls to 'log' here but we need 'log' to exist to compile
  #pragma warning disable 0169, 0649
          private static readonly ILogInactive log;
  #pragma warning restore 0169, 0649
  
          /// <value>
          /// Number of bytes in the smallest valid ipv4 packet
          /// </value>
          public const int HeaderMinimumLength = 20;
  
          /// <summary> Type of service code constants for IP. Type of service describes
          /// how a packet should be handled.
          /// <p>
          /// 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.
          /// </p>
          /// <p>
          /// 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.
          /// </p>
          /// <p>
          /// Taken from TCP/IP Illustrated V1 by Richard Stevens, p34.
          /// </p>
          /// </summary>
          public struct TypesOfService_Fields
          {
  #pragma warning disable 1591
              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;
  #pragma warning restore 1591
          }
  
          /// <value>
          /// Version number of the IP protocol being used
          /// </value>
          public static IpVersion ipVersion = IpVersion.IPv4;
  
          /// <summary> Get the IP version code.</summary>
          public override IpVersion Version
          {
              get
              {
                  return (IpVersion)((header.Bytes[header.Offset + IPv4Fields.VersionAndHeaderLengthPosition] >> 4) & 0x0F);
              }
  
              set
              {
                  // read the original value
                  var theByte = header.Bytes[header.Offset + IPv4Fields.VersionAndHeaderLengthPosition];
  
                  // mask in the version bits
                  theByte = (byte)((theByte & 0x0F) | (((byte)value << 4) & 0xF0));
  
                  // write back the modified value
                  header.Bytes[header.Offset + IPv4Fields.VersionAndHeaderLengthPosition] = theByte;
              }
          }
  
          /// <value>
          /// Forwards compatibility IPv6.PayloadLength property
          /// </value>
          public override ushort PayloadLength
          {
              get
              {
                  return (ushort)(TotalLength - (HeaderLength * 4));
              }
  
              set
              {
                  TotalLength = value + (HeaderLength * 4);
              }
          }
  
          /// <summary>
          /// 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.
          /// </summary>
          /// <param name="length">The length of the IP header in 32-bit words.
          /// </param>
          public override int HeaderLength
          {
              get
              {
                  return (header.Bytes[header.Offset + IPv4Fields.VersionAndHeaderLengthPosition]) & 0x0F;
              }
  
              set
              {
                  // read the original value
                  var theByte = header.Bytes[header.Offset + IPv4Fields.VersionAndHeaderLengthPosition];
  
                  // mask in the header length bits
                  theByte = (byte)((theByte & 0xF0) | (((byte)value) & 0x0F));
  
                  // write back the modified value
                  header.Bytes[header.Offset + IPv4Fields.VersionAndHeaderLengthPosition] = theByte;
              }
          }
  
          /// <summary>
          /// The unique ID of this IP datagram. The ID normally
          /// increments by one each time a datagram is sent by a host.
          /// A 16-bit unsigned integer.
          /// </summary>
          virtual public ushort Id
          {
              get
              {
                  return EndianBitConverter.Big.ToUInt16(header.Bytes,
                                                         header.Offset + IPv4Fields.IdPosition);
              }
  
              set
              {
                  EndianBitConverter.Big.CopyBytes(value,
                                                   header.Bytes,
                                                   header.Offset + IPv4Fields.IdPosition);
              }
          }
  
          /// <summary>
          /// Fragmentation offset
          /// The offset specifies a number of octets (i.e., bytes).
          /// A 13-bit unsigned integer.
          /// </summary>
          virtual public int FragmentOffset
          {
              get
              {
                  var fragmentOffsetAndFlags = EndianBitConverter.Big.ToInt16(header.Bytes,
                                                                             header.Offset + IPv4Fields.FragmentOffsetAndFlagsPosition);
  
                  // mask off the high flag bits
                  return (fragmentOffsetAndFlags & 0x1FFF);
              }
  
              set
              {
                  // retrieve the value
                  var fragmentOffsetAndFlags = EndianBitConverter.Big.ToInt16(header.Bytes,
                                                                             header.Offset + IPv4Fields.FragmentOffsetAndFlagsPosition);
  
                  // mask the fragementation offset in
                  fragmentOffsetAndFlags = (short)((fragmentOffsetAndFlags & 0xE000) | (value & 0x1FFF));
  
                  EndianBitConverter.Big.CopyBytes(fragmentOffsetAndFlags,
                                                   header.Bytes,
                                                   header.Offset + IPv4Fields.FragmentOffsetAndFlagsPosition);
              }
          }
  
          /// <summary> Fetch the IP address of the host where the packet originated from.</summary>
          public override System.Net.IPAddress SourceAddress
          {
              get
              {
                  return IpPacket.GetIPAddress(System.Net.Sockets.AddressFamily.InterNetwork,
                                               header.Offset + IPv4Fields.SourcePosition, header.Bytes);
              }
  
              set
              {
                  byte[] address = value.GetAddressBytes();
                  Array.Copy(address, 0,
                             header.Bytes, header.Offset + IPv4Fields.SourcePosition,
                             address.Length);
              }
          }
  
          /// <summary> Fetch the IP address of the host where the packet is destined.</summary>
          public override System.Net.IPAddress DestinationAddress
          {
              get
              {
                  return IpPacket.GetIPAddress(System.Net.Sockets.AddressFamily.InterNetwork,
                                               header.Offset + IPv4Fields.DestinationPosition, header.Bytes);
              }
  
              set
              {
                  byte[] address = value.GetAddressBytes();
                  Array.Copy(address, 0,
                             header.Bytes, header.Offset + IPv4Fields.DestinationPosition,
                             address.Length);
              }
          }
  
          /// <summary> Fetch the header checksum.</summary>
          virtual public ushort Checksum
          {
              get
              {
                  return EndianBitConverter.Big.ToUInt16(header.Bytes,
                                                        header.Offset + IPv4Fields.ChecksumPosition);
              }
  
              set
              {
                  var val = (UInt16)value;
                  EndianBitConverter.Big.CopyBytes(val,
                                                   header.Bytes,
                                                   header.Offset + IPv4Fields.ChecksumPosition);
              }
          }
  
          /// <summary> Check if the IP packet is valid, checksum-wise.</summary>
          virtual public bool ValidChecksum
          {
              get
              {
                  return ValidIPChecksum;
              }
  
          }
  
          /// <summary>
          /// Check if the IP packet header is valid, checksum-wise.
          /// </summary>
          public bool ValidIPChecksum
          {
              get
              {
                  log.Debug("");
  
                  // 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 (Header.Length < IPv4Fields.HeaderLength)
                  {
                      log.DebugFormat("invalid length, returning false");
                      return false;
                  }
                  else
                  {
                      var headerOnesSum = ChecksumUtils.OnesSum(Header);
                      log.DebugFormat(HexPrinter.GetString(Header, 0, Header.Length));
                      const int expectedHeaderOnesSum = 0xffff;
                      var retval = (headerOnesSum == expectedHeaderOnesSum);
                      log.DebugFormat("headerOnesSum: {0}, expectedHeaderOnesSum {1}, returning {2}",
                                      headerOnesSum,
                                      expectedHeaderOnesSum,
                                      retval);
                      log.DebugFormat("Header.Length {0}", Header.Length);
                      return retval;
                  }
              }
          }
  
          /// <summary> Fetch ascii escape sequence of the color associated with this packet type.</summary>
          override public System.String Color
          {
              get
              {
                  return AnsiEscapeSequences.White;
              }
          }
  
          /// <summary> Fetch the type of service. </summary>
          public int DifferentiatedServices
          {
              get
              {
                  return header.Bytes[header.Offset + IPv4Fields.DifferentiatedServicesPosition];
              }
  
              set
              {
                  header.Bytes[header.Offset + IPv4Fields.DifferentiatedServicesPosition] = (byte)value;
              }
          }
  
          /// <value>
          /// Renamed to DifferentiatedServices in IPv6 but present here
          /// for backwards compatibility
          /// </value>
          public int TypeOfService
          {
              get { return DifferentiatedServices; }
              set { DifferentiatedServices = value; }
          }
  
          /// <value>
          /// The entire datagram size including header and data
          /// </value>
          public override int TotalLength
          {
              get
              {
                  return EndianBitConverter.Big.ToUInt16(header.Bytes,
                                                         header.Offset + IPv4Fields.TotalLengthPosition);
              }
  
              set
              {
                  var theValue = (UInt16)value;
                  EndianBitConverter.Big.CopyBytes(theValue,
                                                   header.Bytes,
                                                   header.Offset + IPv4Fields.TotalLengthPosition);
              }
          }
  
          /// <summary> Fetch fragment flags.</summary>
          /// <param name="flags">A 3-bit unsigned integer.</param>
          public virtual int FragmentFlags
          {
              get
              {
                  var fragmentOffsetAndFlags = EndianBitConverter.Big.ToInt16(header.Bytes,
                                                                             header.Offset + IPv4Fields.FragmentOffsetAndFlagsPosition);
  
                  // shift off the fragment offset bits
                  return fragmentOffsetAndFlags >> (16 - 3);
              }
  
              set
              {
                  // retrieve the value
                  var fragmentOffsetAndFlags = EndianBitConverter.Big.ToInt16(header.Bytes,
                                                                             header.Offset + IPv4Fields.FragmentOffsetAndFlagsPosition);
  
                  // mask the flags in
                  fragmentOffsetAndFlags = (short)((fragmentOffsetAndFlags & 0x1FFF) | ((value & 0x07) << (16 - 3)));
  
                  EndianBitConverter.Big.CopyBytes(fragmentOffsetAndFlags,
                                                   header.Bytes,
  
gt:                                                  header.Offset + IPv4Fields.FragmentOffsetAndFlagsPosition);