Commit 35a8a475 authored by Brickner_cp's avatar Brickner_cp

IGMP

parent c17d49be
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using PcapDotNet.Base;
using PcapDotNet.Packets.Ethernet;
......@@ -92,6 +93,7 @@ namespace PcapDotNet.Packets.Test
byte? igmpQueryRobustnessVariable = null;
TimeSpan? igmpQueryInterval = null;
IpV4Address[] igmpSourceAddresses = null;
IgmpGroupRecord[] igmpGroupRecords = null;
Packet packet;
switch (igmpMessageType)
......@@ -166,6 +168,23 @@ namespace PcapDotNet.Packets.Test
igmpMaxResponseTime, igmpGroupAddress);
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:
continue;
}
......@@ -178,10 +197,11 @@ namespace PcapDotNet.Packets.Test
Assert.AreEqual(igmpQueryVersion, packet.Ethernet.IpV4.Igmp.QueryVersion);
MoreAssert.IsInRange(igmpMaxResponseTime.Divide(2), igmpMaxResponseTime,
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
if (packet.Ethernet.IpV4.Igmp.QueryVersion == IgmpQueryVersion.Version3)
if (igmpQueryVersion == IgmpQueryVersion.Version3)
{
Assert.AreEqual(igmpIsSuppressRouterSideProcessing.Value, packet.Ethernet.IpV4.Igmp.IsSuppressRouterSideProcessing,
"IsSuppressRouterSideProcessing");
......@@ -190,6 +210,12 @@ namespace PcapDotNet.Packets.Test
Assert.AreEqual(igmpSourceAddresses.Length, packet.Ethernet.IpV4.Igmp.NumberOfSources);
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
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,
IgmpMessageType igmpMessageType, TimeSpan maxResponseTime, IpV4Address groupAddress)
{
......@@ -431,7 +437,7 @@ namespace PcapDotNet.Packets.Igmp
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,
......@@ -476,7 +482,28 @@ namespace PcapDotNet.Packets.Igmp
buffer.Write(offset + Offset.NumberOfSources, (ushort)numSourceAddresses, Endianity.Big);
// 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()
......@@ -529,6 +556,11 @@ namespace PcapDotNet.Packets.Igmp
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>
/// Calculates the value from the given code as follows:
/// <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
public const int SourceAddresses = 8;
}
public const int HeaderMinimumLength = 8;
public const int HeaderLength = 8;
/// <summary>
/// The type of group record included in the report message.
......@@ -88,11 +88,16 @@ namespace PcapDotNet.Packets.Igmp
public ReadOnlyCollection<IpV4Address> SourceAddresses
{
get
{
if (_sourceAddresses == null)
{
IpV4Address[] sourceAddresses = new IpV4Address[NumberOfSources];
for (int i = 0; i != sourceAddresses.Length; ++i)
sourceAddresses[i] = ReadIpV4Address(Offset.SourceAddresses + 4 * i, Endianity.Big);
return new ReadOnlyCollection<IpV4Address>(sourceAddresses);
_sourceAddresses = new ReadOnlyCollection<IpV4Address>(sourceAddresses);
}
return _sourceAddresses;
}
}
......@@ -105,26 +110,52 @@ namespace PcapDotNet.Packets.Igmp
/// </summary>
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)
: base(buffer, offset,
buffer.Length - offset < HeaderMinimumLength
buffer.Length - offset < HeaderLength
? 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.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>
/// The record is valid if the length is correct according to the header fields.
/// </summary>
protected override bool CalculateIsValid()
{
return Length >= HeaderMinimumLength &&
Length == HeaderMinimumLength + 4 * NumberOfSources + AuxiliaryDataLength;
return Length >= HeaderLength &&
Length == HeaderLength + IpV4Address.SizeOf * NumberOfSources + AuxiliaryDataLength;
}
private ReadOnlyCollection<IpV4Address> _sourceAddresses;
}
}
\ No newline at end of file
......@@ -451,6 +451,29 @@ namespace PcapDotNet.Packets
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>
/// Writes the given value to the buffer using the given endianity.
/// </summary>
......
......@@ -342,6 +342,48 @@ namespace PcapDotNet.Packets
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,
MacAddress ethernetSource, MacAddress ethernetDestination,
byte ipV4TypeOfService, ushort ipV4Identification, IpV4Fragmentation ipV4Fragmentation,
......
......@@ -72,6 +72,7 @@
<Compile Include="Ethernet\MacAddress.cs" />
<Compile Include="IDataLink.cs" />
<Compile Include="Igmp\IgmpDatagram.cs" />
<Compile Include="Igmp\IgmpGroupRecord.cs" />
<Compile Include="Igmp\IgmpGroupRecordDatagram.cs" />
<Compile Include="Igmp\IgmpQueryVersion.cs" />
<Compile Include="Igmp\IgmpRecordType.cs" />
......
......@@ -65,9 +65,19 @@ namespace PcapDotNet.TestUtils
public static void AreSequenceEqual<T>(IEnumerable<T> expectedSequence, IEnumerable<T> actualSequence, string message)
{
Assert.IsTrue(expectedSequence.SequenceEqual(actualSequence),
"Expected sequence: <" + expectedSequence.SequenceToString(",") + ">. Actual sequence: <" +
actualSequence.SequenceToString(",") + ">. " + message);
if (expectedSequence.SequenceEqual(actualSequence))
return;
// 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)
......
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