diff --git a/PacketDotNet/LinkLayers.cs b/PacketDotNet/LinkLayers.cs index 54cb8d80..9524c933 100644 --- a/PacketDotNet/LinkLayers.cs +++ b/PacketDotNet/LinkLayers.cs @@ -162,5 +162,8 @@ public enum LinkLayers : ushort IPv6 = 229, /// Protocol for communication between host and guest machines in VMware and KVM hypervisors. - VSock = 271 - } + VSock = 271, + + /// Linux "cooked" capture encapsulation v2. + LinuxSll2 = 276, +} diff --git a/PacketDotNet/LinuxSll2Fields.cs b/PacketDotNet/LinuxSll2Fields.cs new file mode 100644 index 00000000..6c77c087 --- /dev/null +++ b/PacketDotNet/LinuxSll2Fields.cs @@ -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; + + /// + /// Lengths and offsets to the fields in the LinuxSll2 packet + /// See http://github.com/mcr/libpcap/blob/master/pcap/sll.h + /// + public struct LinuxSll2Fields + { + /// + /// Length of the ethernet protocol field + /// + public static readonly int EthernetProtocolTypeLength = 2; + + /// + /// Position of the ethernet protocol type field + /// + public static readonly int EthernetProtocolTypePosition = 0; + + /// + /// Link layer address length + /// + public static readonly int LinkLayerAddressLengthLength = 1; + + /// + /// Positino of the link layer address length field + /// + public static readonly int LinkLayerAddressLengthPosition; + + /// + /// The link layer address field length + /// NOTE: the actual link layer address MAY be shorter than this + /// + public static readonly int LinkLayerAddressMaximumLength = 8; + + /// + /// Position of the link layer address field + /// + public static readonly int LinkLayerAddressPosition; + + /// + /// Link layer address type + /// + public static readonly int LinkLayerAddressTypeLength = 2; + + /// + /// Position of the link layer address type field + /// + public static readonly int LinkLayerAddressTypePosition; + + /// + /// Length of the packet type field + /// + public static readonly int PacketTypeLength = 1; + + /// + /// Position of the packet type field + /// + public static readonly int PacketTypePosition; + + /// + /// Reserved (MBZ) + /// + public static readonly int ReservedMBZLength = 2; + + /// + /// Position of the Reserved (MBZ) field + /// + public static readonly int ReservedMBZPosition = 0; + + /// + /// Length of the interface index field + /// + public static readonly int InterfaceIndexLength = 4; + + /// + /// Position of the interface index field + /// + public static readonly int InterfaceIndexPosition = 0; + + /// + /// Number of bytes in a SLL2 header + /// + 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; + } + } \ No newline at end of file diff --git a/PacketDotNet/LinuxSll2Packet.cs b/PacketDotNet/LinuxSll2Packet.cs new file mode 100644 index 00000000..c6145623 --- /dev/null +++ b/PacketDotNet/LinuxSll2Packet.cs @@ -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; + + /// + /// 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 + /// + public class LinuxSll2Packet : InternetLinkLayerPacket + { + /// + /// Constructor + /// + /// + /// A + /// + public LinuxSll2Packet(ByteArraySegment byteArraySegment) + { + Header = new ByteArraySegment(byteArraySegment) { Length = LinuxSll2Fields.SLL2HeaderLength }; + + // parse the payload via an EthernetPacket method + PayloadPacketOrData = new LazySlim(() => EthernetPacket.ParseNextSegment(Header, + EthernetProtocolType)); + } + + /// + /// The encapsulated protocol type + /// + 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); + } + } + + /// + /// Link layer header bytes, maximum of 8 bytes + /// + 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); + } + } + + /// + /// Number of bytes in the link layer address of the sender of the packet + /// + 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; + } + } + + /// + /// The + /// + 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); + } + } + + /// + /// Information about the packet direction + /// + public LinuxSll2Type Type + { + get => (LinuxSll2Type)Header.Bytes[Header.Offset + LinuxSll2Fields.PacketTypePosition]; + set + { + Header.Bytes[Header.Offset + LinuxSll2Fields.PacketTypePosition] = (byte) value; + } + } + + /// + /// Information about the interface index + /// + 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); + } + } + + /// + 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 + { + { "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(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(); + } + } \ No newline at end of file diff --git a/PacketDotNet/LinuxSll2Type.cs b/PacketDotNet/LinuxSll2Type.cs new file mode 100644 index 00000000..9d22d908 --- /dev/null +++ b/PacketDotNet/LinuxSll2Type.cs @@ -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; + + /// + /// The types of cooked packets v2 + /// See http://github.com/mcr/libpcap/blob/master/pcap/sll.h + /// + public enum LinuxSll2Type + { + /// + /// Packet was sent to us by somebody else + /// + PacketSentToUs = 0x0, + + /// + /// Packet was broadcast by somebody else + /// + PacketBroadCast = 0x1, + + /// + /// Packet was multicast, but not broadcast + /// + PacketMulticast = 0x2, + + /// + /// Packet was sent by somebody else to somebody else + /// + PacketSentToSomeoneElse = 0x3, + + /// + /// Packet was sent by us + /// + PacketSentByUs = 0x4 + } \ No newline at end of file diff --git a/PacketDotNet/Packet.cs b/PacketDotNet/Packet.cs index 07434638..63f885d9 100644 --- a/PacketDotNet/Packet.cs +++ b/PacketDotNet/Packet.cs @@ -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);