Commit 71506133 authored by Brickner_cp's avatar Brickner_cp

Fixed IPv4 Header Checksum calculation.

UDP.
parent 8a4d4e61
......@@ -80,4 +80,68 @@ namespace PcapDotNet.Base.Test
}
}
}
/// <summary>
/// Summary description for DateTimeTests
/// </summary>
[TestClass]
public class DateTimeTests
{
public DateTimeTests()
{
//
// TODO: Add constructor logic here
//
}
private TestContext testContextInstance;
/// <summary>
///Gets or sets the test context which provides
///information about and functionality for the current test run.
///</summary>
public TestContext TestContext
{
get
{
return testContextInstance;
}
set
{
testContextInstance = value;
}
}
#region Additional test attributes
//
// You can use the following additional attributes as you write your tests:
//
// Use ClassInitialize to run code before running the first test in the class
// [ClassInitialize()]
// public static void MyClassInitialize(TestContext testContext) { }
//
// Use ClassCleanup to run code after all tests in a class have run
// [ClassCleanup()]
// public static void MyClassCleanup() { }
//
// Use TestInitialize to run code before running each test
// [TestInitialize()]
// public void MyTestInitialize() { }
//
// Use TestCleanup to run code after each test has run
// [TestCleanup()]
// public void MyTestCleanup() { }
//
#endregion
[TestMethod]
public void DateTimeToLocalTimeTest()
{
const long ticks = 633737178954260865;
Assert.AreEqual("Jerusalem Daylight Time", TimeZone.CurrentTimeZone.DaylightName);
DateTime dateTime = new DateTime(ticks, DateTimeKind.Local);
// Assert.AreEqual(dateTime.ToLocalTime(), dateTime.ToUniversalTime().ToLocalTime());
}
}
}
\ No newline at end of file
......@@ -129,7 +129,7 @@ namespace PcapDotNet.Packets.Test
ipV4Payload);
Assert.IsTrue(packet.IsValid);
Assert.IsTrue(ipV4Protocol == IpV4Protocol.Udp || packet.IsValid, "IsValid");
// Ethernet
Assert.AreEqual(packet.Length - EthernetDatagram.HeaderLength, packet.Ethernet.PayloadLength, "PayloadLength");
......@@ -324,21 +324,20 @@ namespace PcapDotNet.Packets.Test
Assert.IsFalse(badPacket.IsValid, "badPacket.IsValid");
}
private static Packet HexToPacket(string hexString, DataLinkKind dataLinkKind)
[TestMethod]
public void IpV4BadChecksumTest()
{
return HexToPacket(hexString, DateTime.MinValue, dataLinkKind);
Packet packet =
HexToPacket(
"14f50eac02b964f8ce8b889908004a800061088400007b11fffb9434d5228511ae3b88044b379404429319088f008b13b7f8000000002cea09e70039a38af80cf279bad8b22df584ea50177467c686df799957540db1ab0015140be458517507e456d123f14680b9f1f36b834d908c",
DataLinkKind.Ethernet);
Assert.IsTrue(packet.Ethernet.IpV4.IsHeaderChecksumCorrect);
}
private static Packet HexToPacket(string hexString, DateTime timestamp, DataLinkKind dataLinkKind)
private static Packet HexToPacket(string hexString, DataLinkKind dataLinkKind)
{
byte[] bytes = new byte[hexString.Length / 2];
for (int i = 0; i < hexString.Length; i += 2)
{
bytes[i / 2] = Convert.ToByte(hexString.Substring(i, 2), 16);
}
return new Packet(bytes, timestamp, dataLinkKind);
return Packet.FromHexadecimalString(hexString, DateTime.MinValue, dataLinkKind);
}
}
}
\ No newline at end of file
......@@ -51,6 +51,7 @@
<Compile Include="PacketTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="IpV4AddressTests.cs" />
<Compile Include="UdpTests.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\PcapDotNet.Base\PcapDotNet.Base.csproj">
......
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using PcapDotNet.Packets.Ethernet;
using PcapDotNet.Packets.IpV4;
using PcapDotNet.Packets.TestUtils;
using PcapDotNet.Packets.Udp;
using PcapDotNet.TestUtils;
namespace PcapDotNet.Packets.Test
{
/// <summary>
/// Summary description for UdpTests
/// </summary>
[TestClass]
public class UdpTests
{
public UdpTests()
{
//
// TODO: Add constructor logic here
//
}
private TestContext testContextInstance;
/// <summary>
///Gets or sets the test context which provides
///information about and functionality for the current test run.
///</summary>
public TestContext TestContext
{
get
{
return testContextInstance;
}
set
{
testContextInstance = value;
}
}
#region Additional test attributes
//
// You can use the following additional attributes as you write your tests:
//
// Use ClassInitialize to run code before running the first test in the class
// [ClassInitialize()]
// public static void MyClassInitialize(TestContext testContext) { }
//
// Use ClassCleanup to run code after all tests in a class have run
// [ClassCleanup()]
// public static void MyClassCleanup() { }
//
// Use TestInitialize to run code before running each test
// [TestInitialize()]
// public void MyTestInitialize() { }
//
// Use TestCleanup to run code after each test has run
// [TestCleanup()]
// public void MyTestCleanup() { }
//
#endregion
[TestMethod]
public void RandomUdpTest()
{
MacAddress ethernetSource = new MacAddress("00:01:02:03:04:05");
MacAddress ethernetDestination = new MacAddress("A0:A1:A2:A3:A4:A5");
const EthernetType ethernetType = EthernetType.IpV4;
Random random = new Random();
byte ipV4TypeOfService = random.NextByte();
ushort ipV4Identification = random.NextUShort();
byte ipV4Ttl = random.NextByte();
IpV4FragmentationOptions ipV4FragmentationOptions = random.NextEnum<IpV4FragmentationOptions>();
ushort ipV4FragmentationOffset = (ushort)(random.NextUShort(ushort.MaxValue / 8) * 8);
IpV4Fragmentation ipV4Fragmentation = new IpV4Fragmentation(ipV4FragmentationOptions, ipV4FragmentationOffset);
IpV4Protocol ipV4Protocol = random.NextEnum<IpV4Protocol>();
IpV4Address ipV4Source = new IpV4Address(random.NextUInt());
IpV4Address ipV4Destination = new IpV4Address(random.NextUInt());
IpV4Options ipV4Options = random.NextIpV4Options();
for (int i = 0; i != 1000; ++i)
{
ushort udpSourcePort = random.NextUShort();
ushort udpDestinationPort = random.NextUShort();
bool udpCalculateChecksum = random.NextBool();
Datagram udpPayload = random.NextDatagram(random.Next(60000));
Packet packet = PacketBuilder.EthernetIpV4Udp(DateTime.Now,
ethernetSource, ethernetDestination,
ipV4TypeOfService, ipV4Identification, ipV4Fragmentation, ipV4Ttl,
ipV4Source, ipV4Destination, ipV4Options,
udpSourcePort, udpDestinationPort, udpCalculateChecksum,
udpPayload);
Assert.IsTrue(packet.IsValid);
// UDP
Assert.AreEqual(udpSourcePort, packet.Ethernet.IpV4.Udp.SourcePort, "Source Port");
Assert.AreEqual(udpDestinationPort, packet.Ethernet.IpV4.Udp.DestinationPort, "Destination Port");
Assert.AreEqual(UdpDatagram.HeaderLength + udpPayload.Length, packet.Ethernet.IpV4.Udp.TotalLength, "Total Length");
Assert.IsTrue(!udpCalculateChecksum && packet.Ethernet.IpV4.Udp.Checksum == 0 ||
packet.Ethernet.IpV4.IsTransportChecksumCorrect, "IsTransportChecksumCorrect");
}
}
[TestMethod]
public void UdpChecksumTest()
{
Packet packet = Packet.FromHexadecimalString(
"3352c58e71ffc4f39ec3bae508004cfe0043361200008611eec22ea2c8d11e9eb7b9520c2a33f2bbbed998980bba4404f941019404eb51880496ce00000005a87a270013a683f572c10e1504a0df15448a",
DateTime.Now, DataLinkKind.Ethernet);
Assert.IsTrue(packet.Ethernet.IpV4.IsTransportChecksumCorrect);
}
}
}
\ No newline at end of file
......@@ -215,9 +215,6 @@ namespace PcapDotNet.Packets.TestUtils
{
IpV4Option option = random.NextIpV4Option(optionsLength);
if (option == null)
continue;
if (option.IsAppearsAtMostOnce &&
options.FindIndex(option.Equivalent) != -1)
{
......
......@@ -2,6 +2,7 @@
using System.Collections;
using System.Linq;
using System.Text;
using PcapDotNet.Packets.Udp;
namespace PcapDotNet.Packets.IpV4
{
......@@ -182,6 +183,19 @@ namespace PcapDotNet.Packets.IpV4
}
}
public bool IsTransportChecksumCorrect
{
get
{
if (_isTransportChecksumCorrect == null)
{
ushort transportChecksum = Udp.Checksum;
_isTransportChecksumCorrect = (transportChecksum != 0) && (CalculateTransportChecksum() == transportChecksum);
}
return _isTransportChecksumCorrect.Value;
}
}
/// <summary>
/// The payload of the datagram.
/// </summary>
......@@ -204,6 +218,20 @@ namespace PcapDotNet.Packets.IpV4
}
}
/// <summary>
/// The payload of the datagram as a UDP datagram.
/// </summary>
public UdpDatagram Udp
{
get
{
if (_udp == null && Length >= HeaderLength)
_udp = new UdpDatagram(Buffer, StartOffset + HeaderLength, Length - HeaderLength);
return _udp;
}
}
internal IpV4Datagram(byte[] buffer, int offset, int length)
: base(buffer, offset, length)
{
......@@ -232,6 +260,30 @@ namespace PcapDotNet.Packets.IpV4
buffer.Write(offset + Offset.HeaderChecksum, Sum16BitsToChecksum(Sum16Bits(buffer, offset, headerLength)), Endianity.Big);
}
internal static void WriteTransportChecksum(byte[] buffer, int offset, int headerLength, ushort transportLength, int transportChecksumOffset)
{
ushort checksum = CalculateTransportChecksum(buffer, offset, headerLength, transportLength, transportChecksumOffset);
buffer.Write(offset + headerLength + transportChecksumOffset, checksum, Endianity.Big);
}
private ushort CalculateTransportChecksum()
{
return CalculateTransportChecksum(Buffer, StartOffset, HeaderLength, (ushort)(TotalLength - HeaderLength), UdpDatagram.ChecksumOffset);
}
private static ushort CalculateTransportChecksum(byte[] buffer, int offset, int headerLength, ushort transportLength, int transportChecksumOffset)
{
uint sum = Sum16Bits(buffer, offset + Offset.Source, 2 * IpV4Address.SizeOf) +
buffer[offset + Offset.Protocol] + transportLength +
Sum16Bits(buffer, offset + headerLength, transportChecksumOffset) +
Sum16Bits(buffer, offset + headerLength + transportChecksumOffset + 2, transportLength - transportChecksumOffset - 2);
ushort checksumResult = Sum16BitsToChecksum(sum);
if (checksumResult == 0)
return 0xFFFF;
return checksumResult;
}
/// <summary>
/// An IPv4 datagram is valid if its length is big enough for the header, the header checksum is correct and the payload is valid.
/// </summary>
......@@ -246,10 +298,12 @@ namespace PcapDotNet.Packets.IpV4
switch (Protocol)
{
case IpV4Protocol.Tcp:
return Tcp.IsValid;
// case IpV4Protocol.Tcp:
// return Tcp.IsValid; //&& IsTransportChecksumCorrect;
case IpV4Protocol.Udp:
return Udp.IsValid && (Udp.Checksum == 0 || IsTransportChecksumCorrect);
default:
// Todo check the protocl further
// Todo check more protocols
return true;
}
}
......@@ -264,11 +318,14 @@ namespace PcapDotNet.Packets.IpV4
private static ushort Sum16BitsToChecksum(uint sum)
{
// take only 16 bits out of the 32 bit sum and add up the carrier
sum = (sum & 0x0000FFFF) + (sum >> 16);
// Take only 16 bits out of the 32 bit sum and add up the carrier.
// if the results overflows - do it again.
while (sum > 0xFFFF)
sum = (sum & 0xFFFF) + (sum >> 16);
// one's complement the result
sum = ~sum;
return (ushort)sum;
}
......@@ -279,12 +336,14 @@ namespace PcapDotNet.Packets.IpV4
while (offset < endOffset - 1)
sum += buffer.ReadUShort(ref offset, Endianity.Big);
if (offset < endOffset)
sum += buffer[offset];
sum += (ushort)(buffer[offset] << 8);
return sum;
}
private bool? _isHeaderChecksumCorrect;
private bool? _isTransportChecksumCorrect;
private IpV4Options _options;
private Datagram _tcp;
private UdpDatagram _udp;
}
}
\ No newline at end of file
......@@ -14,6 +14,18 @@ namespace PcapDotNet.Packets
/// </summary>
public class Packet : IList<byte>, IEquatable<Packet>
{
public static Packet FromHexadecimalString(string value, DateTime timestamp, DataLinkKind dataLink)
{
byte[] bytes = new byte[value.Length / 2];
for (int i = 0; i < value.Length; i += 2)
{
bytes[i / 2] = Convert.ToByte(value.Substring(i, 2), 16);
}
return new Packet(bytes, timestamp, dataLink);
}
/// <summary>
/// Create a packet from an array of bytes.
/// </summary>
......
using System;
using PcapDotNet.Packets.Ethernet;
using PcapDotNet.Packets.IpV4;
using PcapDotNet.Packets.Udp;
namespace PcapDotNet.Packets
{
......@@ -63,5 +64,55 @@ namespace PcapDotNet.Packets
ipV4Payload.Write(buffer, EthernetDatagram.HeaderLength + ipHeaderLength);
return new Packet(buffer, timestamp, new DataLink(DataLinkKind.Ethernet));
}
/// <summary>
/// Builds a UDP over IPv4 over Ethernet packet.
/// </summary>
/// <param name="timestamp">The packet timestamp.</param>
/// <param name="ethernetSource">The ethernet source mac address.</param>
/// <param name="ethernetDestination">The ethernet destination mac address.</param>
/// <param name="ipV4TypeOfService">The IPv4 Type of Service.</param>
/// <param name="ipV4Identification">The IPv4 Identification.</param>
/// <param name="ipV4Fragmentation">The IPv4 Fragmentation.</param>
/// <param name="ipV4Ttl">The IPv4 TTL.</param>
/// <param name="ipV4SourceAddress">The IPv4 source address.</param>
/// <param name="ipV4DestinationAddress">The IPv4 destination address.</param>
/// <param name="ipV4Options">The IPv4 options.</param>
/// <param name="udpSourcePort">The source udp port.</param>
/// <param name="udpDestinationPort">The destination udp port.</param>
/// <param name="udpCalculateChecksum">Whether to calculate udp checksum or leave it empty (UDP checksum is optional).</param>
/// <param name="udpPayload">The payload of UDP datagram.</param>
/// <returns>A packet with a UDP over IPv4 over Ethernet datagram.</returns>
public static Packet EthernetIpV4Udp(DateTime timestamp,
MacAddress ethernetSource, MacAddress ethernetDestination,
byte ipV4TypeOfService, ushort ipV4Identification, IpV4Fragmentation ipV4Fragmentation,
byte ipV4Ttl,
IpV4Address ipV4SourceAddress, IpV4Address ipV4DestinationAddress,
IpV4Options ipV4Options,
ushort udpSourcePort, ushort udpDestinationPort, bool udpCalculateChecksum,
Datagram udpPayload)
{
int ipV4HeaderLength = IpV4Datagram.HeaderMinimumLength + ipV4Options.BytesLength;
int transportLength = UdpDatagram.HeaderLength + udpPayload.Length;
int ethernetIpV4HeadersLength = EthernetDatagram.HeaderLength + ipV4HeaderLength;
byte[] buffer = new byte[ethernetIpV4HeadersLength + transportLength];
EthernetDatagram.WriteHeader(buffer, 0, ethernetSource, ethernetDestination, EthernetType.IpV4);
IpV4Datagram.WriteHeader(buffer, EthernetDatagram.HeaderLength,
ipV4TypeOfService, ipV4Identification, ipV4Fragmentation,
ipV4Ttl, IpV4Protocol.Udp,
ipV4SourceAddress, ipV4DestinationAddress,
ipV4Options, transportLength);
UdpDatagram.WriteHeader(buffer, ethernetIpV4HeadersLength, udpSourcePort, udpDestinationPort, udpPayload.Length);
udpPayload.Write(buffer, ethernetIpV4HeadersLength + UdpDatagram.HeaderLength);
if (udpCalculateChecksum)
IpV4Datagram.WriteTransportChecksum(buffer, EthernetDatagram.HeaderLength, ipV4HeaderLength, (ushort)transportLength, UdpDatagram.ChecksumOffset);
return new Packet(buffer, timestamp, new DataLink(DataLinkKind.Ethernet));
}
}
}
\ No newline at end of file
......@@ -42,7 +42,7 @@
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<RunCodeAnalysis>true</RunCodeAnalysis>
<CodeAnalysisRules>-Microsoft.Design#CA1021;-Microsoft.Design#CA1045;-Microsoft.Design#CA1028;-Microsoft.Naming#CA1710</CodeAnalysisRules>
<CodeAnalysisRules>-Microsoft.Design#CA1021;-Microsoft.Design#CA1045;-Microsoft.Design#CA1028;-Microsoft.Design#CA1027;-Microsoft.Naming#CA1710</CodeAnalysisRules>
<DocumentationFile>..\..\bin\Release\PcapDotNet.Packets.XML</DocumentationFile>
</PropertyGroup>
<ItemGroup>
......@@ -101,6 +101,7 @@
<Compile Include="Packet.cs" />
<Compile Include="PacketBuilder.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Udp\UdpDatagram.cs" />
</ItemGroup>
<ItemGroup>
<None Include="..\PcapDotNet.snk" />
......
using System;
namespace PcapDotNet.Packets.Udp
{
/// <summary>
/// This User Datagram Protocol (UDP) is defined to make available a datagram mode of packet-switched computer communication
/// in the environment of an interconnected set of computer networks.
/// This protocol assumes that the Internet Protocol (IP) is used as the underlying protocol.
///
/// This protocol provides a procedure for application programs to send messages to other programs
/// with a minimum of protocol mechanism.
/// The protocol is transaction oriented, and delivery and duplicate protection are not guaranteed.
/// Applications requiring ordered reliable delivery of streams of data should use the Transmission Control Protocol (TCP).
///
/// Format
/// 0 7 8 15 16 23 24 31
/// +--------+--------+--------+--------+
/// | Source | Destination |
/// | Port | Port |
/// +--------+--------+--------+--------+
/// | | |
/// | Length | Checksum |
/// +--------+--------+--------+--------+
/// |
/// | data octets ...
/// +---------------- ...
/// </summary>
public class UdpDatagram : Datagram
{
/// <summary>
/// The number of bytes the datagram header takes.
/// </summary>
public const int HeaderLength = 8;
private static class Offset
{
public const int SourcePort = 0;
public const int DestinationPort = 2;
public const int TotalLength = 4;
public const int Checksum = 6;
}
internal const int ChecksumOffset = Offset.Checksum;
/// <summary>
/// Source Port is an optional field, when meaningful, it indicates the port of the sending process,
/// and may be assumed to be the port to which a reply should be addressed in the absence of any other information.
/// If not used, a value of zero is inserted.
/// </summary>
public ushort SourcePort
{
get { return ReadUShort(Offset.SourcePort, Endianity.Big); }
}
/// <summary>
/// Destination Port has a meaning within the context of a particular internet destination address.
/// </summary>
public ushort DestinationPort
{
get { return ReadUShort(Offset.DestinationPort, Endianity.Big); }
}
/// <summary>
/// The length in octets of this user datagram including this header and the data.
/// (This means the minimum value of the length is eight.)
/// </summary>
public ushort TotalLength
{
get { return ReadUShort(Offset.TotalLength, Endianity.Big); }
}
/// <summary>
/// Checksum is the 16-bit one's complement of the one's complement sum of a pseudo header of information from the IP header,
/// the UDP header, and the data, padded with zero octets at the end (if necessary) to make a multiple of two octets.
/// </summary>
public ushort Checksum
{
get { return ReadUShort(Offset.Checksum, Endianity.Big); }
}
/// <summary>
/// The payload of the UDP datagram.
/// </summary>
public Datagram Payload
{
get { return new Datagram(Buffer, StartOffset + HeaderLength, Length - HeaderLength); }
}
internal UdpDatagram(byte[] buffer, int offset, int length)
: base(buffer, offset, length)
{
}
internal static void WriteHeader(byte[] buffer, int offset, ushort sourcePort, ushort destinationPort, int payloadLength)
{
buffer.Write(offset + Offset.SourcePort, sourcePort, Endianity.Big);
buffer.Write(offset + Offset.DestinationPort, destinationPort, Endianity.Big);
buffer.Write(offset + Offset.TotalLength, (ushort)(HeaderLength + payloadLength), Endianity.Big);
}
protected override bool CalculateIsValid()
{
return Length >= HeaderLength;
}
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment