Commit 35a8a475 authored by Brickner_cp's avatar Brickner_cp

IGMP

parent c17d49be
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using PcapDotNet.Base; using PcapDotNet.Base;
using PcapDotNet.Packets.Ethernet; using PcapDotNet.Packets.Ethernet;
...@@ -92,6 +93,7 @@ namespace PcapDotNet.Packets.Test ...@@ -92,6 +93,7 @@ namespace PcapDotNet.Packets.Test
byte? igmpQueryRobustnessVariable = null; byte? igmpQueryRobustnessVariable = null;
TimeSpan? igmpQueryInterval = null; TimeSpan? igmpQueryInterval = null;
IpV4Address[] igmpSourceAddresses = null; IpV4Address[] igmpSourceAddresses = null;
IgmpGroupRecord[] igmpGroupRecords = null;
Packet packet; Packet packet;
switch (igmpMessageType) switch (igmpMessageType)
...@@ -166,6 +168,23 @@ namespace PcapDotNet.Packets.Test ...@@ -166,6 +168,23 @@ namespace PcapDotNet.Packets.Test
igmpMaxResponseTime, igmpGroupAddress); igmpMaxResponseTime, igmpGroupAddress);
break; break;
case IgmpMessageType.MembershipReportVersion3:
igmpMaxResponseTime = TimeSpan.Zero;
igmpGroupRecords = new IgmpGroupRecord[random.Next(100)];
for (int groupRecordIndex = 0; groupRecordIndex != igmpGroupRecords.Length; ++groupRecordIndex)
{
IpV4Address[] sourceAddresses = new IpV4Address[random.Next(100)];
for (int sourceAddressIndex = 0; sourceAddressIndex != sourceAddresses.Length; ++sourceAddressIndex)
sourceAddresses[sourceAddressIndex] = random.NextIpV4Address();
igmpGroupRecords[groupRecordIndex] = new IgmpGroupRecord(random.NextEnum<IgmpRecordType>(), random.NextIpV4Address(), sourceAddresses, random.NextDatagram(100));
}
packet = PacketBuilder.EthernetIpV4IgmpReportVersion3(DateTime.Now,
ethernetSource, ethernetDestination,
ipV4TypeOfService, ipV4Identification, ipV4Fragmentation, ipV4Ttl,
ipV4Source, ipV4Destination, ipV4Options,
igmpGroupRecords);
break;
default: default:
continue; continue;
} }
...@@ -178,10 +197,11 @@ namespace PcapDotNet.Packets.Test ...@@ -178,10 +197,11 @@ namespace PcapDotNet.Packets.Test
Assert.AreEqual(igmpQueryVersion, packet.Ethernet.IpV4.Igmp.QueryVersion); Assert.AreEqual(igmpQueryVersion, packet.Ethernet.IpV4.Igmp.QueryVersion);
MoreAssert.IsInRange(igmpMaxResponseTime.Divide(2), igmpMaxResponseTime, MoreAssert.IsInRange(igmpMaxResponseTime.Divide(2), igmpMaxResponseTime,
packet.Ethernet.IpV4.Igmp.MaxResponseTime, "MaxResponseTime"); packet.Ethernet.IpV4.Igmp.MaxResponseTime, "MaxResponseTime");
Assert.AreEqual(igmpGroupAddress, packet.Ethernet.IpV4.Igmp.GroupAddress); if (igmpMessageType != IgmpMessageType.MembershipReportVersion3)
Assert.AreEqual(igmpGroupAddress, packet.Ethernet.IpV4.Igmp.GroupAddress, "GroupAddress");
// Query Version 3 // Query Version 3
if (packet.Ethernet.IpV4.Igmp.QueryVersion == IgmpQueryVersion.Version3) if (igmpQueryVersion == IgmpQueryVersion.Version3)
{ {
Assert.AreEqual(igmpIsSuppressRouterSideProcessing.Value, packet.Ethernet.IpV4.Igmp.IsSuppressRouterSideProcessing, Assert.AreEqual(igmpIsSuppressRouterSideProcessing.Value, packet.Ethernet.IpV4.Igmp.IsSuppressRouterSideProcessing,
"IsSuppressRouterSideProcessing"); "IsSuppressRouterSideProcessing");
...@@ -190,6 +210,12 @@ namespace PcapDotNet.Packets.Test ...@@ -190,6 +210,12 @@ namespace PcapDotNet.Packets.Test
Assert.AreEqual(igmpSourceAddresses.Length, packet.Ethernet.IpV4.Igmp.NumberOfSources); Assert.AreEqual(igmpSourceAddresses.Length, packet.Ethernet.IpV4.Igmp.NumberOfSources);
MoreAssert.AreSequenceEqual(igmpSourceAddresses, packet.Ethernet.IpV4.Igmp.SourceAddresses); MoreAssert.AreSequenceEqual(igmpSourceAddresses, packet.Ethernet.IpV4.Igmp.SourceAddresses);
} }
if (igmpMessageType == IgmpMessageType.MembershipReportVersion3)
{
Assert.AreEqual(igmpGroupRecords.Length, packet.Ethernet.IpV4.Igmp.NumberOfGroupRecords);
MoreAssert.AreSequenceEqual(igmpGroupRecords, packet.Ethernet.IpV4.Igmp.GroupRecords.Select(record => record.ToGroupRecord()));
}
} }
} }
} }
......
...@@ -419,6 +419,12 @@ namespace PcapDotNet.Packets.Igmp ...@@ -419,6 +419,12 @@ namespace PcapDotNet.Packets.Igmp
return QueryVersion3HeaderLength + IpV4Address.SizeOf * numSourceAddresses; return QueryVersion3HeaderLength + IpV4Address.SizeOf * numSourceAddresses;
} }
internal static int GetReportVersion3Length(IEnumerable<IgmpGroupRecord> igmpGroupRecords)
{
return HeaderLength +
igmpGroupRecords.Sum(record => IgmpGroupRecordDatagram.GetLength(record.SourceAddresses.Count, record.AuxiliaryData.Length));
}
internal static void WriteHeader(byte[] buffer, int offset, internal static void WriteHeader(byte[] buffer, int offset,
IgmpMessageType igmpMessageType, TimeSpan maxResponseTime, IpV4Address groupAddress) IgmpMessageType igmpMessageType, TimeSpan maxResponseTime, IpV4Address groupAddress)
{ {
...@@ -431,7 +437,7 @@ namespace PcapDotNet.Packets.Igmp ...@@ -431,7 +437,7 @@ namespace PcapDotNet.Packets.Igmp
buffer.Write(offset + Offset.GroupAddress, groupAddress, Endianity.Big); buffer.Write(offset + Offset.GroupAddress, groupAddress, Endianity.Big);
buffer.Write(offset + Offset.Checksum, Sum16BitsToChecksum(Sum16Bits(buffer, offset, HeaderLength)), Endianity.Big); WriteChecksum(buffer, offset, HeaderLength);
} }
internal static void WriteQueryVersion3(byte[] buffer, int offset, internal static void WriteQueryVersion3(byte[] buffer, int offset,
...@@ -476,9 +482,30 @@ namespace PcapDotNet.Packets.Igmp ...@@ -476,9 +482,30 @@ namespace PcapDotNet.Packets.Igmp
buffer.Write(offset + Offset.NumberOfSources, (ushort)numSourceAddresses, Endianity.Big); buffer.Write(offset + Offset.NumberOfSources, (ushort)numSourceAddresses, Endianity.Big);
// Checksum // Checksum
buffer.Write(offset + Offset.Checksum, Sum16BitsToChecksum(Sum16Bits(buffer, offset, QueryVersion3HeaderLength + IpV4Address.SizeOf * numSourceAddresses)), Endianity.Big); WriteChecksum(buffer, offset, QueryVersion3HeaderLength + IpV4Address.SizeOf * numSourceAddresses);
} }
internal static void WriteReportVersion3(byte[] buffer, int offset,
IEnumerable<IgmpGroupRecord> groupRecords)
{
// MessageType
buffer.Write(offset + Offset.MessageType, (byte)IgmpMessageType.MembershipReportVersion3);
ushort numGroupRecords = 0;
int recordOffset = offset + Offset.GroupRecords;
foreach (IgmpGroupRecord record in groupRecords)
{
IgmpGroupRecordDatagram.Write(buffer, ref recordOffset, record.RecordType, record.AuxiliaryData, record.MulticastAddress, record.SourceAddresses);
++numGroupRecords;
}
// NumberOfGroupRecords
buffer.Write(offset + Offset.NumberOfGroupRecords, numGroupRecords, Endianity.Big);
// Checksum
WriteChecksum(buffer, offset, recordOffset - offset);
}
protected override bool CalculateIsValid() protected override bool CalculateIsValid()
{ {
if (Length < HeaderLength || !IsChecksumCorrect) if (Length < HeaderLength || !IsChecksumCorrect)
...@@ -529,6 +556,11 @@ namespace PcapDotNet.Packets.Igmp ...@@ -529,6 +556,11 @@ namespace PcapDotNet.Packets.Igmp
return Sum16BitsToChecksum(sum); return Sum16BitsToChecksum(sum);
} }
private static void WriteChecksum(byte[] buffer, int offset, int length)
{
buffer.Write(offset + Offset.Checksum, Sum16BitsToChecksum(Sum16Bits(buffer, offset, length)), Endianity.Big);
}
/// <summary> /// <summary>
/// Calculates the value from the given code as follows: /// Calculates the value from the given code as follows:
/// <pre> /// <pre>
......
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using PcapDotNet.Base;
using PcapDotNet.Packets.IpV4;
namespace PcapDotNet.Packets.Igmp
{
public class IgmpGroupRecord : IEquatable<IgmpGroupRecord>
{
public IgmpGroupRecord(IgmpRecordType recordType, IpV4Address multicastAddress, ReadOnlyCollection<IpV4Address> sourceAddresses, Datagram auxiliaryData)
{
if (auxiliaryData.Length % 4 != 0)
throw new ArgumentException("Auxiliary data length must divide by 4 and can't be " + auxiliaryData.Length, "auxiliaryData");
RecordType = recordType;
MulticastAddress = multicastAddress;
SourceAddresses = new ReadOnlyCollection<IpV4Address>(sourceAddresses);
AuxiliaryData = auxiliaryData;
}
public IgmpGroupRecord(IgmpRecordType recordType, IpV4Address multicastAddress, IList<IpV4Address> sourceAddresses, Datagram auxiliaryData)
: this(recordType, multicastAddress, new ReadOnlyCollection<IpV4Address>(sourceAddresses), auxiliaryData)
{
}
/// <summary>
/// The type of group record included in the report message.
/// </summary>
public IgmpRecordType RecordType
{
get; private set;
}
/// <summary>
/// The Multicast Address field contains the IP multicast address to which this Group Record pertains.
/// </summary>
public IpV4Address MulticastAddress
{
get; private set;
}
/// <summary>
/// The Source Address [i] fields are a vector of n IP unicast addresses,
/// where n is the value in this record's Number of Sources (N) field.
/// </summary>
public ReadOnlyCollection<IpV4Address> SourceAddresses
{
get; private set;
}
/// <summary>
/// The Auxiliary Data field, if present, contains additional information pertaining to this Group Record.
/// The protocol specified in this document, IGMPv3, does not define any auxiliary data.
/// Therefore, implementations of IGMPv3 MUST NOT include any auxiliary data (i.e., MUST set the Aux Data Len field to zero) in any transmitted Group Record,
/// and MUST ignore any auxiliary data present in any received Group Record.
/// The semantics and internal encoding of the Auxiliary Data field are to be defined by any future version or extension of IGMP that uses this field.
/// </summary>
public Datagram AuxiliaryData
{
get; private set;
}
public override string ToString()
{
return RecordType + " " + MulticastAddress + " " + SourceAddresses.SequenceToString(", ", "[", "]") + " Aux=" + AuxiliaryData.Length;
}
public bool Equals(IgmpGroupRecord other)
{
if (other == null)
return false;
return RecordType == other.RecordType &&
MulticastAddress == other.MulticastAddress &&
SourceAddresses.SequenceEqual(other.SourceAddresses) &&
AuxiliaryData.Equals(other.AuxiliaryData);
}
public override bool Equals(object obj)
{
return Equals(obj as IgmpGroupRecord);
}
}
}
\ No newline at end of file
...@@ -46,7 +46,7 @@ namespace PcapDotNet.Packets.Igmp ...@@ -46,7 +46,7 @@ namespace PcapDotNet.Packets.Igmp
public const int SourceAddresses = 8; public const int SourceAddresses = 8;
} }
public const int HeaderMinimumLength = 8; public const int HeaderLength = 8;
/// <summary> /// <summary>
/// The type of group record included in the report message. /// The type of group record included in the report message.
...@@ -89,10 +89,15 @@ namespace PcapDotNet.Packets.Igmp ...@@ -89,10 +89,15 @@ namespace PcapDotNet.Packets.Igmp
{ {
get get
{ {
IpV4Address[] sourceAddresses = new IpV4Address[NumberOfSources]; if (_sourceAddresses == null)
for (int i = 0; i != sourceAddresses.Length; ++i) {
sourceAddresses[i] = ReadIpV4Address(Offset.SourceAddresses + 4 * i, Endianity.Big); IpV4Address[] sourceAddresses = new IpV4Address[NumberOfSources];
return new ReadOnlyCollection<IpV4Address>(sourceAddresses); for (int i = 0; i != sourceAddresses.Length; ++i)
sourceAddresses[i] = ReadIpV4Address(Offset.SourceAddresses + 4 * i, Endianity.Big);
_sourceAddresses = new ReadOnlyCollection<IpV4Address>(sourceAddresses);
}
return _sourceAddresses;
} }
} }
...@@ -105,26 +110,52 @@ namespace PcapDotNet.Packets.Igmp ...@@ -105,26 +110,52 @@ namespace PcapDotNet.Packets.Igmp
/// </summary> /// </summary>
public Datagram AuxiliaryData public Datagram AuxiliaryData
{ {
get { return new Datagram(Buffer, StartOffset + Offset.AuxiliaryDataLength, AuxiliaryDataLength); } get { return new Datagram(Buffer, StartOffset + Length - AuxiliaryDataLength, AuxiliaryDataLength); }
}
public IgmpGroupRecord ToGroupRecord()
{
return new IgmpGroupRecord(RecordType, MulticastAddress, SourceAddresses, AuxiliaryData);
} }
internal IgmpGroupRecordDatagram(byte[] buffer, int offset) internal IgmpGroupRecordDatagram(byte[] buffer, int offset)
: base(buffer, offset, : base(buffer, offset,
buffer.Length - offset < HeaderMinimumLength buffer.Length - offset < HeaderLength
? buffer.Length - offset ? buffer.Length - offset
: Math.Min(buffer.Length - offset, HeaderMinimumLength + : Math.Min(buffer.Length - offset, HeaderLength +
4 * buffer.ReadUShort(offset + Offset.NumberOfSources, Endianity.Big) + 4 * buffer.ReadUShort(offset + Offset.NumberOfSources, Endianity.Big) +
4 * buffer.ReadByte(offset + Offset.AuxiliaryDataLength))) 4 * buffer.ReadByte(offset + Offset.AuxiliaryDataLength)))
{ {
} }
internal static int GetLength(int numSourceAddresses, int auxiliaryDataLength)
{
return HeaderLength + IpV4Address.SizeOf * numSourceAddresses + auxiliaryDataLength;
}
internal static void Write(byte[] buffer, ref int offset, IgmpRecordType recordType, Datagram auxiliaryData, IpV4Address multicastAddress, ReadOnlyCollection<IpV4Address> sourceAddresses)
{
buffer.Write(offset + Offset.RecordType, (byte)recordType);
buffer.Write(offset + Offset.AuxiliaryDataLength, (byte)(auxiliaryData.Length / 4));
int numSourceAddresses = sourceAddresses.Count;
buffer.Write(offset + Offset.NumberOfSources, (ushort)numSourceAddresses, Endianity.Big);
buffer.Write(offset + Offset.MulticastAddress, multicastAddress, Endianity.Big);
for (int i = 0; i != numSourceAddresses; ++i)
buffer.Write(offset + Offset.SourceAddresses + IpV4Address.SizeOf * i, sourceAddresses[i], Endianity.Big);
offset += HeaderLength + numSourceAddresses * IpV4Address.SizeOf;
buffer.Write(ref offset, auxiliaryData);
}
/// <summary> /// <summary>
/// The record is valid if the length is correct according to the header fields. /// The record is valid if the length is correct according to the header fields.
/// </summary> /// </summary>
protected override bool CalculateIsValid() protected override bool CalculateIsValid()
{ {
return Length >= HeaderMinimumLength && return Length >= HeaderLength &&
Length == HeaderMinimumLength + 4 * NumberOfSources + AuxiliaryDataLength; Length == HeaderLength + IpV4Address.SizeOf * NumberOfSources + AuxiliaryDataLength;
} }
private ReadOnlyCollection<IpV4Address> _sourceAddresses;
} }
} }
\ No newline at end of file
...@@ -451,6 +451,29 @@ namespace PcapDotNet.Packets ...@@ -451,6 +451,29 @@ namespace PcapDotNet.Packets
offset += UInt48.SizeOf; offset += UInt48.SizeOf;
} }
/// <summary>
/// Writes the given value to the buffer.
/// </summary>
/// <param name="buffer">The buffer to write the value to.</param>
/// <param name="offset">The offset in the buffer to start writing.</param>
/// <param name="value">The value to write.</param>
public static void Write(this byte[] buffer, int offset, Datagram value)
{
value.Write(buffer, offset);
}
/// <summary>
/// Writes the given value to the buffer and increments the offset by the number of bytes written.
/// </summary>
/// <param name="buffer">The buffer to write the value to.</param>
/// <param name="offset">The offset in the buffer to start writing.</param>
/// <param name="value">The value to write.</param>
public static void Write(this byte[] buffer, ref int offset, Datagram value)
{
value.Write(buffer, offset);
offset += value.Length;
}
/// <summary> /// <summary>
/// Writes the given value to the buffer using the given endianity. /// Writes the given value to the buffer using the given endianity.
/// </summary> /// </summary>
......
...@@ -342,6 +342,48 @@ namespace PcapDotNet.Packets ...@@ -342,6 +342,48 @@ namespace PcapDotNet.Packets
return new Packet(buffer, timestamp, DataLinkKind.Ethernet); return new Packet(buffer, timestamp, DataLinkKind.Ethernet);
} }
/// <summary>
/// Builds an IGMP report version 3 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="igmpMaxResponseTime">The actual time allowed.</param>
/// <param name="igmpGroupAddress">
/// The Group Address field is set to zero when sending a General Query,
/// and set to the IP multicast address being queried when sending a Group-Specific Query or Group-and-Source-Specific Query.
/// In a Membership Report of version 1 or 2 or Leave Group message, the group address field holds the IP multicast group address of the group being reported or left.
/// In a Membership Report of version 3 this field is meaningless.
/// </param>
/// <returns>A packet with an IGMP report version 3 over IPv4 over Ethernet datagram.</returns>
public static Packet EthernetIpV4IgmpReportVersion3(DateTime timestamp,
MacAddress ethernetSource, MacAddress ethernetDestination,
byte ipV4TypeOfService, ushort ipV4Identification, IpV4Fragmentation ipV4Fragmentation,
byte ipV4Ttl,
IpV4Address ipV4SourceAddress, IpV4Address ipV4DestinationAddress,
IpV4Options ipV4Options,
IEnumerable<IgmpGroupRecord> igmpGroupRecords)
{
int ipHeaderLength = IpV4Datagram.HeaderMinimumLength + ipV4Options.BytesLength;
byte[] buffer = new byte[EthernetDatagram.HeaderLength + ipHeaderLength + IgmpDatagram.GetReportVersion3Length(igmpGroupRecords)];
EthernetDatagram.WriteHeader(buffer, 0, ethernetSource, ethernetDestination, EthernetType.IpV4);
IpV4Datagram.WriteHeader(buffer, EthernetDatagram.HeaderLength,
ipV4TypeOfService, ipV4Identification, ipV4Fragmentation,
ipV4Ttl, IpV4Protocol.InternetGroupManagementProtocol,
ipV4SourceAddress, ipV4DestinationAddress,
ipV4Options, IgmpDatagram.HeaderLength);
IgmpDatagram.WriteReportVersion3(buffer, EthernetDatagram.HeaderLength + ipHeaderLength,
igmpGroupRecords);
return new Packet(buffer, timestamp, DataLinkKind.Ethernet);
}
private static Packet EthernetIpV4Igmp(DateTime timestamp, private static Packet EthernetIpV4Igmp(DateTime timestamp,
MacAddress ethernetSource, MacAddress ethernetDestination, MacAddress ethernetSource, MacAddress ethernetDestination,
byte ipV4TypeOfService, ushort ipV4Identification, IpV4Fragmentation ipV4Fragmentation, byte ipV4TypeOfService, ushort ipV4Identification, IpV4Fragmentation ipV4Fragmentation,
......
...@@ -72,6 +72,7 @@ ...@@ -72,6 +72,7 @@
<Compile Include="Ethernet\MacAddress.cs" /> <Compile Include="Ethernet\MacAddress.cs" />
<Compile Include="IDataLink.cs" /> <Compile Include="IDataLink.cs" />
<Compile Include="Igmp\IgmpDatagram.cs" /> <Compile Include="Igmp\IgmpDatagram.cs" />
<Compile Include="Igmp\IgmpGroupRecord.cs" />
<Compile Include="Igmp\IgmpGroupRecordDatagram.cs" /> <Compile Include="Igmp\IgmpGroupRecordDatagram.cs" />
<Compile Include="Igmp\IgmpQueryVersion.cs" /> <Compile Include="Igmp\IgmpQueryVersion.cs" />
<Compile Include="Igmp\IgmpRecordType.cs" /> <Compile Include="Igmp\IgmpRecordType.cs" />
......
...@@ -65,9 +65,19 @@ namespace PcapDotNet.TestUtils ...@@ -65,9 +65,19 @@ namespace PcapDotNet.TestUtils
public static void AreSequenceEqual<T>(IEnumerable<T> expectedSequence, IEnumerable<T> actualSequence, string message) public static void AreSequenceEqual<T>(IEnumerable<T> expectedSequence, IEnumerable<T> actualSequence, string message)
{ {
Assert.IsTrue(expectedSequence.SequenceEqual(actualSequence), if (expectedSequence.SequenceEqual(actualSequence))
"Expected sequence: <" + expectedSequence.SequenceToString(",") + ">. Actual sequence: <" + return;
actualSequence.SequenceToString(",") + ">. " + message);
// message = "Expected sequence: <" + expectedSequence.SequenceToString(",") + ">. Actual sequence: <" +
// actualSequence.SequenceToString(",") + ">. " + message;
Assert.AreEqual(expectedSequence.Count(), actualSequence.Count(), "Different Count. " + message);
List<T> expectedList = expectedSequence.ToList();
List<T> actualList = actualSequence.ToList();
for (int i = 0; i != expectedList.Count; ++i)
Assert.AreEqual(expectedList[i], actualList[i], "Element " + (i + 1) + " is different in the sequence. " + message);
Assert.Fail(message);
} }
public static void AreSequenceEqual<T>(IEnumerable<T> expectedSequence, IEnumerable<T> actualSequence) public static void AreSequenceEqual<T>(IEnumerable<T> expectedSequence, IEnumerable<T> actualSequence)
......
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