ln.types/net/IPv4Header.cs

124 lines
4.6 KiB
C#

using System;
namespace ln.types.net
{
public class IPv4Header
{
public byte Version { get; set; }
public byte IHL { get; set; }
public byte TypeOfService { get; set; }
public ushort TotalLength { get; set; }
public ushort Identification { get; set; }
public byte Flags { get; set; }
public ushort FragmentOffset { get; set; }
public byte TimeToLive { get; set; }
public byte Protocol { get; set; }
public ushort HeaderChecksum { get; set; }
public IPv4 SourceAddress { get; set; }
public IPv4 DestinationAddress { get; set; }
public byte[] Options { get; set; }
public IPv4Header()
{
}
public int HeaderLength => IHL << 2;
public void Write(byte[] buffer, int offset) => Write(buffer, offset, false);
public void Write(byte[] buffer,int offset,bool performChecksum)
{
if (buffer.Length < HeaderLength)
throw new ArgumentOutOfRangeException(nameof(buffer));
if (offset + HeaderLength < buffer.Length)
throw new ArgumentOutOfRangeException(nameof(offset));
if ((Options.Length % 4) != 0)
throw new InvalidOperationException("IPv4Header.Options must be length multiple of 4");
if ((Options.Length + 20) != HeaderLength)
throw new InvalidOperationException("IHL must match .Header and .Options length");
if (performChecksum)
HeaderChecksum = 0;
int i = (Version) | (IHL << 4) | (TypeOfService << 8) | (TotalLength << 16);
Array.Copy(BitConverter.GetBytes(i), 0, buffer, offset, 4);
i = (Identification) | (Flags << 16) | (FragmentOffset << 19);
Array.Copy(BitConverter.GetBytes(i), 0, buffer, offset + 4, 4);
i = (TimeToLive) | (Protocol << 8) | (HeaderChecksum << 16);
Array.Copy(BitConverter.GetBytes(i), 0, buffer, offset + 8, 4);
Array.Copy(SourceAddress.IPBytes, 0, buffer, offset + 12, 4);
Array.Copy(SourceAddress.IPBytes, 0, buffer, offset + 16, 4);
Array.Copy(Options, 0, buffer, offset + 20, Options.Length);
if (performChecksum)
{
HeaderChecksum = PerformChecksum(buffer,offset,HeaderLength);
Array.Copy(BitConverter.GetBytes(HeaderChecksum), 0, buffer, offset + 10, 4);
}
}
public byte[] ToBytes() => ToBytes(false);
public byte[] ToBytes(bool performChecksum)
{
byte[] packet = new byte[HeaderLength];
Write(packet, 0, performChecksum);
return packet;
}
public static ushort PerformChecksum(byte[] headerBytes) => PerformChecksum(headerBytes, 0, headerBytes.Length);
public static ushort PerformChecksum(byte[] headerBytes,int offset,int length)
{
uint chksum = 0;
for (int n = 0; n < headerBytes.Length; n += 4)
chksum += BitConverter.ToUInt16(headerBytes, n);
chksum += (chksum & 0xFFFF) + (chksum >> 16);
if ((chksum & 0xFFFF0000)!=0)
chksum += (chksum & 0xFFFF) + (chksum >> 16);
return (ushort)((~chksum) & 0xFFFF);
}
public static IPv4Header Parse(byte[] packet,int offset,int length)
{
if (offset >= packet.Length)
throw new ArgumentOutOfRangeException(nameof(offset));
if ((offset + length > packet.Length)|| (length < 20))
throw new ArgumentOutOfRangeException(nameof(length));
IPv4Header header = new IPv4Header();
uint ui = BitConverter.ToUInt32(packet, offset);
header.Version = (byte)(ui & 0x0F);
header.IHL = (byte)((ui & 0xF0) >> 4);
header.TypeOfService = (byte)(ui >> 8);
header.TotalLength = (ushort)(ui >> 16);
ui = BitConverter.ToUInt32(packet, offset + 4);
header.Identification = (ushort)(ui & 0x0000FFFF);
header.Flags = (byte)((ui & 0x00070000) >> 16);
header.FragmentOffset = (ushort)((ui & 0xFFF80000) >> 19);
ui = BitConverter.ToUInt32(packet, offset + 4);
header.TimeToLive = (byte)(ui & 0x000000FF);
header.Protocol = (byte)((ui & 0x0000FF00) >> 8);
header.HeaderChecksum = (ushort)((ui & 0xFFFF0000)>>16);
header.SourceAddress = new IPv4(packet, 12);
header.DestinationAddress= new IPv4(packet, 16);
header.Options = packet.Slice(20,header.HeaderLength - 20);
return header;
}
}
}