Skip to content

Commit

Permalink
Add support for Linux "cooked" capture encapsulation v2 (SLL2) layer …
Browse files Browse the repository at this point in the history
…type (#208)

* Add support for Linux "cooked" capture encapsulation v2 (SLL2) layer type

* Update LinuxSll2Packet.cs

---------

Co-authored-by: PhyxionNL <[email protected]>
  • Loading branch information
engycz and PhyxionNL authored Sep 27, 2024
1 parent 7ca0301 commit 89caf70
Show file tree
Hide file tree
Showing 7 changed files with 495 additions and 2 deletions.
7 changes: 5 additions & 2 deletions PacketDotNet/LinkLayers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -162,5 +162,8 @@ public enum LinkLayers : ushort
IPv6 = 229,

/// <summary>Protocol for communication between host and guest machines in VMware and KVM hypervisors.</summary>
VSock = 271
}
VSock = 271,

/// <summary>Linux "cooked" capture encapsulation v2.</summary>
LinuxSll2 = 276,
}
102 changes: 102 additions & 0 deletions PacketDotNet/LinuxSll2Fields.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
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/.
*/

namespace PacketDotNet;

/// <summary>
/// Lengths and offsets to the fields in the LinuxSll2 packet
/// See http://github.com/mcr/libpcap/blob/master/pcap/sll.h
/// </summary>
public struct LinuxSll2Fields
{
/// <summary>
/// Length of the ethernet protocol field
/// </summary>
public static readonly int EthernetProtocolTypeLength = 2;

/// <summary>
/// Position of the ethernet protocol type field
/// </summary>
public static readonly int EthernetProtocolTypePosition = 0;

/// <summary>
/// Link layer address length
/// </summary>
public static readonly int LinkLayerAddressLengthLength = 1;

/// <summary>
/// Positino of the link layer address length field
/// </summary>
public static readonly int LinkLayerAddressLengthPosition;

/// <summary>
/// The link layer address field length
/// NOTE: the actual link layer address MAY be shorter than this
/// </summary>
public static readonly int LinkLayerAddressMaximumLength = 8;

/// <summary>
/// Position of the link layer address field
/// </summary>
public static readonly int LinkLayerAddressPosition;

/// <summary>
/// Link layer address type
/// </summary>
public static readonly int LinkLayerAddressTypeLength = 2;

/// <summary>
/// Position of the link layer address type field
/// </summary>
public static readonly int LinkLayerAddressTypePosition;

/// <summary>
/// Length of the packet type field
/// </summary>
public static readonly int PacketTypeLength = 1;

/// <summary>
/// Position of the packet type field
/// </summary>
public static readonly int PacketTypePosition;

/// <summary>
/// Reserved (MBZ)
/// </summary>
public static readonly int ReservedMBZLength = 2;

/// <summary>
/// Position of the Reserved (MBZ) field
/// </summary>
public static readonly int ReservedMBZPosition = 0;

/// <summary>
/// Length of the interface index field
/// </summary>
public static readonly int InterfaceIndexLength = 4;

/// <summary>
/// Position of the interface index field
/// </summary>
public static readonly int InterfaceIndexPosition = 0;

/// <summary>
/// Number of bytes in a SLL2 header
/// </summary>
public static readonly int SLL2HeaderLength = 20;

static LinuxSll2Fields()
{
ReservedMBZPosition = EthernetProtocolTypePosition + EthernetProtocolTypeLength;
InterfaceIndexPosition = ReservedMBZPosition + ReservedMBZLength;
LinkLayerAddressTypePosition = InterfaceIndexPosition + InterfaceIndexLength;
PacketTypePosition = LinkLayerAddressTypePosition + LinkLayerAddressTypeLength;
LinkLayerAddressLengthPosition = PacketTypePosition + PacketTypeLength;
LinkLayerAddressPosition = LinkLayerAddressLengthPosition + LinkLayerAddressLengthLength;
}
}
207 changes: 207 additions & 0 deletions PacketDotNet/LinuxSll2Packet.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
/*
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 System.Collections.Generic;
using System.Text;
using PacketDotNet.Utils;
using PacketDotNet.Utils.Converters;

namespace PacketDotNet;

/// <summary>
/// Represents a Linux cooked capture packet, the kinds of packets
/// received when capturing on an 'any' device
/// See http://github.com/mcr/libpcap/blob/master/pcap/sll.h
/// </summary>
public class LinuxSll2Packet : InternetLinkLayerPacket
{
/// <summary>
/// Constructor
/// </summary>
/// <param name="byteArraySegment">
/// A <see cref="ByteArraySegment" />
/// </param>
public LinuxSll2Packet(ByteArraySegment byteArraySegment)
{
Header = new ByteArraySegment(byteArraySegment) { Length = LinuxSll2Fields.SLL2HeaderLength };

// parse the payload via an EthernetPacket method
PayloadPacketOrData = new LazySlim<PacketOrByteArraySegment>(() => EthernetPacket.ParseNextSegment(Header, EthernetProtocolType));
}

/// <value>
/// The encapsulated protocol type
/// </value>
public EthernetType EthernetProtocolType
{
get => (EthernetType) EndianBitConverter.Big.ToInt16(Header.Bytes,
Header.Offset + LinuxSll2Fields.EthernetProtocolTypePosition);
set
{
var v = (short) value;
EndianBitConverter.Big.CopyBytes(v,
Header.Bytes,
Header.Offset + LinuxSll2Fields.EthernetProtocolTypePosition);
}
}

/// <value>
/// Link layer header bytes, maximum of 8 bytes
/// </value>
public byte[] LinkLayerAddress
{
get
{
var headerLength = LinkLayerAddressLength;
var theHeader = new byte[headerLength];

Array.Copy(Header.Bytes,
Header.Offset + LinuxSll2Fields.LinkLayerAddressPosition,
theHeader,
0,
headerLength);

return theHeader;
}
set
{
// update the link layer length
LinkLayerAddressLength = value.Length;

// copy in the new link layer header bytes
Array.Copy(value,
0,
Header.Bytes,
Header.Offset + LinuxSll2Fields.LinkLayerAddressPosition,
value.Length);
}
}

/// <value>
/// Number of bytes in the link layer address of the sender of the packet
/// </value>
public int LinkLayerAddressLength
{
get => Header.Bytes[Header.Offset + LinuxSll2Fields.LinkLayerAddressLengthPosition];
set
{
// range check
if (value is < 0 or > 8)
{
throw new InvalidOperationException("value of " + value + " out of range of 0 to 8");
}

Header.Bytes[Header.Offset + LinuxSll2Fields.LinkLayerAddressLengthPosition] = (byte) value;
}
}

/// <value>
/// The
/// </value>
public int LinkLayerAddressType
{
get => EndianBitConverter.Big.ToInt16(Header.Bytes,
Header.Offset + LinuxSll2Fields.LinkLayerAddressTypePosition);
set
{
var v = (short) value;
EndianBitConverter.Big.CopyBytes(v,
Header.Bytes,
Header.Offset + LinuxSll2Fields.LinkLayerAddressTypePosition);
}
}

/// <value>
/// Information about the packet direction
/// </value>
public LinuxSll2Type Type
{
get => (LinuxSll2Type)Header.Bytes[Header.Offset + LinuxSll2Fields.PacketTypePosition];
set
{
Header.Bytes[Header.Offset + LinuxSll2Fields.PacketTypePosition] = (byte) value;
}
}

/// <value>
/// Information about the interface index
/// </value>
public int InterfaceIndex
{
get => EndianBitConverter.Big.ToInt32(Header.Bytes,
Header.Offset + LinuxSll2Fields.InterfaceIndexPosition);
set
{
var v = (int)value;
EndianBitConverter.Big.CopyBytes(v,
Header.Bytes,
Header.Offset + LinuxSll2Fields.InterfaceIndexPosition);
}
}

/// <inheritdoc cref="Packet.ToString(StringOutputType)" />
public override string ToString(StringOutputType outputFormat)
{
var buffer = new StringBuilder();
var color = "";
var colorEscape = "";

if (outputFormat is StringOutputType.Colored or StringOutputType.VerboseColored)
{
color = Color;
colorEscape = AnsiEscapeSequences.Reset;
}

if (outputFormat is StringOutputType.Normal or StringOutputType.Colored)
{
// build the output string
buffer.AppendFormat("[{0}LinuxSll2Packet{1}: ProtocolType={2}, InterfaceIndex={3}, LinkLayerAddressType={4}, Type={5}, LinkLayerAddressLength={6}, Source={7}]",
color,
colorEscape,
EthernetProtocolType,
InterfaceIndex,
LinkLayerAddressType,
Type,
LinkLayerAddressLength,
BitConverter.ToString(LinkLayerAddress, 0));
}

if (outputFormat is StringOutputType.Verbose or StringOutputType.VerboseColored)
{
// collect the properties and their value
var properties = new Dictionary<string, string>
{
{ "protocol", EthernetProtocolType + " (0x" + EthernetProtocolType.ToString("x") + ")" },
{ "interface index", InterfaceIndex.ToString() },
{ "link layer address type", LinkLayerAddressType.ToString() },
{ "type", Type + " (" + (int) Type + ")" },
{ "link layer address length", LinkLayerAddressLength.ToString() },
{ "source", BitConverter.ToString(LinkLayerAddress) },
};

// calculate the padding needed to right-justify the property names
var padLength = RandomUtils.LongestStringLength(new List<string>(properties.Keys));

// build the output string
buffer.AppendLine("LCC: ******* LinuxSll2 - \"Linux Cooked Capture v2\" - offset=? length=" + TotalPacketLength);
buffer.AppendLine("LCC:");
foreach (var property in properties)
{
buffer.AppendLine("LCC: " + property.Key.PadLeft(padLength) + " = " + property.Value);
}

buffer.AppendLine("LCC:");
}

// append the base output
buffer.Append(base.ToString(outputFormat));

return buffer.ToString();
}
}
41 changes: 41 additions & 0 deletions PacketDotNet/LinuxSll2Type.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
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/.
*/

namespace PacketDotNet;

/// <summary>
/// The types of cooked packets v2
/// See http://github.com/mcr/libpcap/blob/master/pcap/sll.h
/// </summary>
public enum LinuxSll2Type
{
/// <summary>
/// Packet was sent to us by somebody else
/// </summary>
PacketSentToUs = 0x0,

/// <summary>
/// Packet was broadcast by somebody else
/// </summary>
PacketBroadCast = 0x1,

/// <summary>
/// Packet was multicast, but not broadcast
/// </summary>
PacketMulticast = 0x2,

/// <summary>
/// Packet was sent by somebody else to somebody else
/// </summary>
PacketSentToSomeoneElse = 0x3,

/// <summary>
/// Packet was sent by us
/// </summary>
PacketSentByUs = 0x4
}
5 changes: 5 additions & 0 deletions PacketDotNet/Packet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,11 @@ public static Packet ParsePacket(LinkLayers linkLayers, byte[] packetData)
p = new LinuxSllPacket(byteArraySegment);
break;
}
case LinkLayers.LinuxSll2:
{
p = new LinuxSll2Packet(byteArraySegment);
break;
}
case LinkLayers.Null:
{
p = new NullPacket(byteArraySegment);
Expand Down
Binary file added Test/CaptureFiles/LinuxCookedCapturev2.pcap
Binary file not shown.
Loading

0 comments on commit 89caf70

Please sign in to comment.