Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

try to decode ESP frames #155

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions PacketDotNet/EspFields.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
This file is part of PacketDotNet.

This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
/*
* Copyright 2009 Chris Morgan <[email protected]>
*/

namespace PacketDotNet
{
public struct EspFields
{
/// <summary>Length of the Base Header in bytes.</summary>
public static readonly int HeaderLength = 8;

/// <summary>Length of the Security Parameters Index (SPI) in bytes.</summary>
public static readonly int SecurityParametersIndexLength = 4;

/// <summary>Length of the Sequence Number in bytes.</summary>
public static readonly int SequenceNumberLength = 4;

/// <summary>Length of the Pad Length in bytes.</summary>
public static readonly int PadLengthLength = 1;

/// <summary>Length of the Next Header in bytes.</summary>
public static readonly int NextHeaderLength = 1;

}
}
179 changes: 179 additions & 0 deletions PacketDotNet/EspPacket.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
/*
This file is part of PacketDotNet.

This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
/*
* Copyright 2010 Chris Morgan <[email protected]>
*/


using System;
using PacketDotNet.Utils;
using PacketDotNet.Utils.Converters;
#if DEBUG
using log4net;
using System.Reflection;
#endif

namespace PacketDotNet
{
/// <summary>
/// EspPacket
/// See: https://en.wikipedia.org/wiki/IPsec#Encapsulating_Security_Payload
/// </summary>
public sealed class EspPacket : Packet
{
#if DEBUG
private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
#else
// 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
#endif

/// <summary>
/// Create from values.
/// </summary>
public EspPacket(TransportPacket payloadPacket, byte[] authenticationData)
{
Log.Debug("");

// allocate memory for this packet
var length = EspFields.HeaderLength;
var headerBytes = new byte[length];
Header = new ByteArraySegment(headerBytes, 0, length);
NextHeader = payloadPacket is TcpPacket ? ProtocolType.Tcp : ProtocolType.Udp;
payloadPacket.ParentPacket = this;
this.AuthenticationData = authenticationData;
this.PadLength = (payloadPacket.TotalPacketLength + 2) % 4;
this.Pad = new byte[this.PadLength];
for (var i = 0; i < this.PadLength; i++)
{
this.Pad[i] = (byte) (i + 1);
}
PayloadPacketOrData = new LazySlim<PacketOrByteArraySegment>(() => new PacketOrByteArraySegment
{
Packet = payloadPacket
});
}

/// <summary>
/// Constructor.
/// </summary>
/// <param name="byteArraySegment">
/// A <see cref="ByteArraySegment" />
/// </param>
/// <param name="parentPacket">
/// A <see cref="Packet" />
/// </param>
public EspPacket(ByteArraySegment byteArraySegment, Packet parentPacket)
{
Log.Debug("");

// set the header field, header field values are retrieved from this byte array
Header = new ByteArraySegment(byteArraySegment) {Length = EspFields.HeaderLength};

ParentPacket = parentPacket;

var next = Header.NextSegment();

// try to decode, assuming a Null ciphering. Get first the last 96 bits (12 bytes) for the Authentication Data
// + 1 byte for the Next Header + 1 byte for the pad length
if (next.Length > 14)
{
AuthenticationData = new byte[12];

// copy the last 12 bytes
Array.Copy(next.Bytes, next.BytesLength - 12, AuthenticationData, 0, 12);
var nextHeader = next.Bytes[next.BytesLength - 13];
// Continue only if next header is Tcp or Udp
if ((ProtocolType)nextHeader == ProtocolType.Tcp || (ProtocolType)nextHeader == ProtocolType.Udp)
{
NextHeader = (ProtocolType) nextHeader;
PadLength = next.Bytes[next.BytesLength - 14];

if (next.Length > 14 + PadLength)
{
// so far ok, continue
Pad = new byte[PadLength];
Array.Copy(next.Bytes, next.BytesLength - 14 - PadLength, Pad, 0, PadLength);

var startingOffset = Header.Offset + Header.Length;
var segmentLength = Math.Max(0, next.Length - 14 - PadLength);
var payloadData = new byte[segmentLength];
Array.Copy(next.Bytes, startingOffset, payloadData, 0, segmentLength);
var payload = new ByteArraySegment(payloadData, 0, segmentLength, segmentLength);

if (NextHeader == ProtocolType.Tcp && segmentLength > 0)
{
PayloadPacketOrData = new LazySlim<PacketOrByteArraySegment>(() => new PacketOrByteArraySegment
{
Packet = new TcpPacket(payload, this)
});

return;
}

if (NextHeader == ProtocolType.Udp && segmentLength > 0)
{
PayloadPacketOrData = new LazySlim<PacketOrByteArraySegment>(() => new PacketOrByteArraySegment
{
Packet = new UdpPacket(payload, this)
});

return;
}
}
}
}

// store the payload bytes
PayloadPacketOrData = new LazySlim<PacketOrByteArraySegment>(() =>
{
var result = new PacketOrByteArraySegment {ByteArraySegment = Header.NextSegment()};
return result;
});
}

/// <summary>
/// Gets or sets the SecurityParametersIndex (SPI).
/// </summary>
public uint SecurityParametersIndex
{
get => EndianBitConverter.Big.ToUInt32(Header.Bytes, Header.Offset);
set => EndianBitConverter.Big.CopyBytes(value, Header.Bytes, Header.Offset);
}

/// <summary>
/// Gets or sets the SecurityParametersIndex (SPI).
/// </summary>
public uint SequenceNumber
{
get => EndianBitConverter.Big.ToUInt32(Header.Bytes, Header.Offset + EspFields.SecurityParametersIndexLength);
set => EndianBitConverter.Big.CopyBytes(value, Header.Bytes, Header.Offset + EspFields.SecurityParametersIndexLength);
}

/// <summary>
/// Identifies the next header field, which is the protocol of the encapsulated in packet unless there are extended headers.
/// </summary>
public ProtocolType NextHeader { get; set; }

/// <summary>Pad length.</summary>
public int PadLength { get; }

/// <summary>
/// Gets or sets the Authentication Data.
/// </summary>
public byte[] AuthenticationData { get; set; }

/// <summary>
/// Gets or sets the Pad.
/// </summary>
public byte[] Pad { get; }
}
}
5 changes: 5 additions & 0 deletions PacketDotNet/IPPacket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,11 @@ protected static PacketOrByteArraySegment ParseNextSegment
payloadPacketOrData.Packet = new GrePacket(payload, parentPacket);
break;
}
case ProtocolType.IPSecEncapsulatingSecurityPayload:
{
payloadPacketOrData.Packet = new EspPacket(payload, parentPacket);
break;
}
// NOTE: new payload parsing entries go here
default:
{
Expand Down
Binary file added Test/CaptureFiles/ipv6_esp.pcap
Binary file not shown.
57 changes: 57 additions & 0 deletions Test/PacketType/EspPacketTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
This file is part of PacketDotNet.

This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

using System;
using NUnit.Framework;
using PacketDotNet;
using SharpPcap;
using SharpPcap.LibPcap;

namespace Test.PacketType
{
[TestFixture]
public class EspPacketTest
{
[Test]
public void EspParsing()
{
var dev = new CaptureFileReaderDevice(NUnitSetupClass.CaptureDirectory + "ipv6_esp.pcap");
dev.Open();
PacketCapture c;
dev.GetNextPacket(out c);
var rawCapture = c.GetPacket();
dev.Close();

var p = Packet.ParsePacket(rawCapture.GetLinkLayers(), rawCapture.Data);

Assert.IsNotNull(p);
var esp = p.Extract<EspPacket>();
Assert.IsNotNull(esp);
Console.WriteLine(esp.GetType());
Assert.AreEqual(156633505, esp.SecurityParametersIndex);
Assert.AreEqual(1, esp.SequenceNumber);
Assert.AreEqual(ProtocolType.Tcp, esp.NextHeader);
Assert.AreEqual(2, esp.PadLength);
Assert.IsNotNull(esp.PayloadPacket);
var tcp = p.Extract<TcpPacket>();
Assert.IsNotNull(tcp);
Assert.AreEqual(0, tcp.UrgentPointer);
Assert.AreEqual(16000, tcp.WindowSize);
Assert.AreEqual(4, tcp.OptionsCollection.Count);

var espCreated = new EspPacket(tcp, esp.AuthenticationData)
{
SequenceNumber = 1,
SecurityParametersIndex = 156633505
};

Assert.NotNull(espCreated);
Assert.AreEqual(ProtocolType.Tcp, espCreated.NextHeader);
}
}
}