Source/Tx.Network/Pcap/PcapNg.cs (250 lines of code) (raw):

// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.IO; using System.Text; // The Pcap Next Generation format is: https://www.winpcap.org/ntar/draft/PCAP-DumpFileFormat.html // The C# implementation below reads files in .pcapng format namespace Tx.Network { public class PcapNg { /// <summary> /// Reads network capture file and returns the raw blocks in the order they were written /// </summary> /// <param name="filename">Path to the file in pcap-next-generation (.pcapng) format</param> /// <returns></returns> public static IEnumerable<Block> ReadForward(string filename) { var stream = File.OpenRead(filename); return ReadForward(stream); } /// <summary> /// Reads a stream and returns the raw blocks in the order they were written /// </summary> /// <param name="stream">Stream in pcap-next-generation format</param> /// <returns></returns> public static IEnumerable<Block> ReadForward(Stream stream) { var interfaces = new List<InterfaceDescriptionBlock>(); using (var reader = new BinaryReader(stream)) { while (true) { if (stream.Position == stream.Length) yield break; BlockType type = (BlockType)reader.ReadUInt32(); UInt32 length = reader.ReadUInt32(); switch (type) { case BlockType.SectionHeaderBlock: yield return new SectionHeaderBlock(type, length, reader); break; case BlockType.InterfaceDescriptionBlock: var interfacceDesc = new InterfaceDescriptionBlock(type, length, reader); interfaces.Add(interfacceDesc); yield return interfacceDesc; break; case BlockType.EnhancedPacketBlock: yield return new EnhancedPacketBlock(type, length, reader, interfaces); break; default: yield return new GenericBlock(type, length, reader); break; } } } } } public enum BlockType { Reserved = 0, InterfaceDescriptionBlock = 0x00000001, PacketBlock = 0x00000002, SimplePacketBlock = 0x00000003, NameResolutionBlock = 0x00000004, InterfaceStatisticsBlock = 0x00000005, EnhancedPacketBlock = 0x00000006, IRIGTimestampBlock = 0x00000007, SectionHeaderBlock = 0x0A0D0D0A } public abstract class Block { public BlockType Type { get; private set; } public UInt32 Length { get; private set;} protected Block(BlockType type, UInt32 length) // called by the derived classes { Type = type; Length = length; } /// <summary> /// The inheriting classes must call this as last line in their constructor /// </summary> protected void ReadEndOfPacket(BinaryReader reader) { // Each packet ends with the same value of Block Total Length, // which is intended to support backward reading. // Reading this length is also useful to make sure the logic in the inheriting class read the block correctly UInt32 length2 = reader.ReadUInt32(); if (length2 != Length) throw new Exception("The Toatal Block Length at the end of bock " + Enum.GetName(typeof(BlockType), this.Type) +" does not match the length"); } protected string ReadAsciiOption(BinaryReader reader, int len) { int readLen = len + (4 - len % 4) % 4; byte[] bytes = reader.ReadBytes(readLen); string s = Encoding.ASCII.GetString(bytes, 0, len); return s; } protected byte[] ReadBytesOption(BinaryReader reader, int len) { int readLen = len + (4 - len % 4) % 4; byte[] bytes = reader.ReadBytes(readLen); return bytes; } } /// <summary> /// Represents block that does not have specific parsing implemented /// </summary> public class GenericBlock : Block { public byte[] Body; internal GenericBlock(BlockType type, UInt32 length, BinaryReader reader) : base(type, length) { Body = reader.ReadBytes((int)Length - 12); ReadEndOfPacket(reader); } } public class EnhancedPacketBlock : Block { public InterfaceDescriptionBlock InterfaceDescription { get; private set; } public UInt32 CapturedLen { get; private set; } public UInt32 PacketLen { get; private set; } public byte[] PacketData { get; private set; } public DateTime TimestampUtc { get; private set; } internal EnhancedPacketBlock(BlockType type, UInt32 length, BinaryReader reader, List<InterfaceDescriptionBlock> interfaces) : base(type, length) { int interfaceID = reader.ReadInt32(); InterfaceDescription = interfaces[interfaceID]; long timestampHigh = reader.ReadUInt32(); long timestampLow = reader.ReadUInt32(); long offset = (new DateTime(1970, 1, 1) - new DateTime(1601, 1, 1)).Ticks; long ticks = offset + ((timestampHigh << 32) | timestampLow) * InterfaceDescription.TimeMultiplier; TimestampUtc = DateTime.FromFileTime(ticks); CapturedLen = reader.ReadUInt32(); PacketLen = reader.ReadUInt32(); PacketData = reader.ReadBytes((int)CapturedLen); uint optionsLen = Length - 8*4 - CapturedLen; byte[] options = reader.ReadBytes((int)optionsLen); ReadEndOfPacket(reader); } } public class SectionHeaderBlock : Block { public UInt16 MajorVersion { get; private set; } public UInt16 MinorVersion { get; private set; } public UInt64 SectionLength { get; private set; } internal SectionHeaderBlock(BlockType type, UInt32 length, BinaryReader reader) : base(type, length) { UInt32 byteOrderMagic = reader.ReadUInt32(); MajorVersion = reader.ReadUInt16(); MinorVersion = reader.ReadUInt16(); SectionLength = reader.ReadUInt64(); uint optionsLen = Length - 7 * 4; byte[] options = reader.ReadBytes((int)optionsLen); ReadEndOfPacket(reader); } } public enum LinkType { /// <summary> /// // No link layer information. A packet saved with this link layer contains a raw L3 packet preceded by a 32-bit host-byte-order AF_ value indicating the specific L3 type. /// </summary> NULL = 0, /// <summary> /// D/I/X and 802.3 Ethernet /// </summary> ETHERNET = 1, /// <summary> /// Experimental Ethernet (3Mb) /// </summary> EXP_ETHERNET = 2, /// <summary> /// Amateur Radio AX.25 /// </summary> AX25 = 3, /// <summary> /// Proteon ProNET Token Ring /// </summary> PRONET = 4, /// <summary> /// Chaos /// </summary> CHAOS = 5, /// <summary> /// IEEE 802 Networks /// </summary> TOKEN_RING =6, /// <summary> /// ARCNET, with BSD-style header /// </summary> ARCNET =7, /// <summary> /// Serial Line IP /// </summary> SLIP =8, /// <summary> /// Point-to-point Protocol /// </summary> PPP =9, /// <summary> /// FDDI /// </summary> FDDI =10, /// <summary> /// PPP in HDLC-like framing /// </summary> PPP_HDLC =50, /// <summary> /// NetBSD PPP-over-Ethernet /// </summary> PPP_ETHER =51, /// <summary> /// Symantec Enterprise Firewall /// </summary> SYMANTEC_FIREWALL =99, /// <summary> /// LLC/SNAP-encapsulated ATM /// </summary> ATM_RFC1483 =100, /// <summary> /// Raw IP /// </summary> RAW = 101, /// <summary> /// BSD/OS SLIP BPF header /// </summary> SLIP_BSDOS =102, /// <summary> /// BSD/OS PPP BPF header /// </summary> PPP_BSDOS =103, /// <summary> /// Cisco HDLC /// </summary> C_HDLC =104, /// <summary> /// IEEE 802.11 (wireless) /// </summary> IEEE802_11 =105, /// <summary> /// Linux Classical IP over ATM /// </summary> ATM_CLIP =106, /// <summary> /// Frame Relay /// </summary> FRELAY =107, /// <summary> /// OpenBSD loopback /// </summary> LOOP =108, /// <summary> /// OpenBSD IPSEC enc /// </summary> ENC =109, /// <summary> /// ATM LANE + 802.3 (Reserved for future use) /// </summary> LANE8023 =110, /// <summary> /// NetBSD HIPPI (Reserved for future use) /// </summary> HIPPI =111, /// <summary> /// NetBSD HDLC framing (Reserved for future use) /// </summary> HDLC =112, /// <summary> /// Linux cooked socket capture /// </summary> LINUX_SLL =113, /// <summary> /// Apple LocalTalk hardware /// </summary> LTALK =114, /// <summary> /// Acorn Econet /// </summary> ECONET =115, /// <summary> /// Reserved for use with OpenBSD ipfilter /// </summary> IPFILTER =116, /// <summary> /// OpenBSD DLT_PFLOG /// </summary> PFLOG =117, /// <summary> /// For Cisco-internal use /// </summary> CISCO_IOS =118, /// <summary> /// 802.11+Prism II monitor mode /// </summary> PRISM_HEADER =119, /// <summary> /// FreeBSD Aironet driver stuff /// </summary> AIRONET_HEADER =120, /// <summary> /// Reserved for Siemens HiPath HDLC /// </summary> HHDLC =121, /// <summary> /// RFC 2625 IP-over-Fibre Channel /// </summary> IP_OVER_FC =122, /// <summary> /// Solaris+SunATM /// </summary> SUNATM =123, /// <summary> /// RapidIO /// </summary> RIO =124, /// <summary> /// PCI Express /// </summary> PCI_EXP =125, /// <summary> /// Xilinx Aurora link layer /// </summary> AURORA =126, /// <summary> /// 802.11 plus BSD radio header /// </summary> IEEE802_11_RADIO =127, /// <summary> /// Tazmen Sniffer Protocol - Reserved for the TZSP encapsulation, as per request from Chris Waters &lt;chris.waters@networkchemistry.com&gt; TZSP is a generic encapsulation for any other link type, which includes a means to include meta-information with the packet, e.g. signal strength and channel for 802.11 packets. /// </summary> TZSP =128, /// <summary> /// Linux-style headers /// </summary> ARCNET_LINUX =129, JUNIPER_MLPPP =130, JUNIPER_MLFR =131, JUNIPER_ES =132, JUNIPER_GGSN =133, JUNIPER_MFR =134, JUNIPER_ATM2 =135, JUNIPER_SERVICES =136, JUNIPER_ATM1 =137, /// <summary> /// Apple IP-over-IEEE 1394 cooked header /// </summary> APPLE_IP_OVER_IEEE1394 =138, MTP2_WITH_PHDR =139, MTP2 =140, MTP3 =141, SCCP =142, /// <summary> /// DOCSIS MAC frames /// </summary> DOCSIS =143, /// <summary> /// Linux-IrDA /// </summary> LINUX_IRDA =144, /// <summary> /// Reserved for IBM SP switch and IBM Next Federation switch. /// </summary> IBM_SP =145, /// <summary> /// Reserved for IBM SP switch and IBM Next Federation switch. /// </summary> IBM_SN =146 } public class InterfaceDescriptionBlock : Block { public LinkType LinkType { get; private set; } public UInt32 SnapLen { get; private set; } /// <summary> /// String containing the name of the device used to capture data. /// </summary> public string Name { get; private set; } /// <summary> /// string containing the description of the device used to capture data. /// </summary> public string Description { get; private set; } /// <summary> /// Number to multiply 64 bit timestamps to obtain Ticks for DateTime /// </summary> public long TimeMultiplier { get; private set; } internal InterfaceDescriptionBlock(BlockType type, UInt32 length, BinaryReader reader) : base(type, length) { LinkType = (LinkType)reader.ReadUInt16(); var reserved = reader.ReadUInt16(); SnapLen = reader.ReadUInt32(); uint optionsLen = Length - 5 * 4; //byte[] options = reader.ReadBytes((int)optionsLen); TimeMultiplier = 10; // default value of 10^-6 if the option if_tsresol is not present int optionCode; while(true) // Options can occur in any order, so we have too loop { optionCode = reader.ReadInt16(); int optionLength = reader.ReadInt16(); if (optionCode == 0) break; switch (optionCode) { case 2: Name = ReadAsciiOption(reader, optionLength); continue; case 3: Description = ReadAsciiOption(reader, optionLength); continue; case 9: byte[] buffer = ReadBytesOption(reader, 1); byte b = buffer[0]; if ((b & 0x80) == 0) TimeMultiplier = (long)(10E6 / Math.Pow(10, b)); else TimeMultiplier = (long)(10E6 / Math.Pow(2, b)); continue; default: // This is some unsupported option, but we still havo to read to skip it ReadBytesOption(reader, optionLength); continue; } } ReadEndOfPacket(reader); } } }