/// <summary> /// Assign a filter to this device given a filterExpression /// </summary> /// <param name="filterExpression">The filter expression to compile</param> protected void SetFilter(string filterExpression) { // save the filter string _filterString = filterExpression; int res; IntPtr bpfProgram; string errorString; // pcap_setfilter() requires a valid pcap_t which isn't present if // the device hasn't been opened ThrowIfNotOpen("device is not open"); // attempt to compile the program if (!CompileFilter(PcapHandle, filterExpression, (uint)m_mask, out bpfProgram, out errorString)) { string err = string.Format("Can't compile filter ({0}) : {1} ", filterExpression, errorString); throw new PcapException(err); } //associate the filter with this device res = LibPcapSafeNativeMethods.pcap_setfilter(PcapHandle, bpfProgram); // Free the program whether or not we were successful in setting the filter // we don't want to leak unmanaged memory if we throw an exception. FreeBpfProgram(bpfProgram); //watch for errors if (res < 0) { errorString = string.Format("Can't set filter ({0}) : {1}", filterExpression, LastError); throw new PcapException(errorString); } }
/// <summary> /// Sends a raw packet throgh this device /// </summary> /// <param name="p">The packet bytes to send</param> /// <param name="size">The number of bytes to send</param> public override void SendPacket(byte[] p, int size) { ThrowIfNotOpen("Can't send packet, the device is closed"); if (size > p.Length) { throw new ArgumentException("Invalid packetSize value: " + size + "\nArgument size is larger than the total size of the packet."); } if (p.Length > Pcap.MAX_PACKET_SIZE) { throw new ArgumentException("Packet length can't be larger than " + Pcap.MAX_PACKET_SIZE); } IntPtr p_packet = IntPtr.Zero; p_packet = Marshal.AllocHGlobal(size); Marshal.Copy(p, 0, p_packet, size); int res = LibPcapSafeNativeMethods.pcap_sendpacket(PcapHandle, p_packet, size); Marshal.FreeHGlobal(p_packet); if (res < 0) { throw new PcapException("Can't send packet: " + LastError); } }
/// <summary> /// Open the device. To start capturing call the 'StartCapture' function /// </summary> /// <param name="mode"> /// A <see cref="DeviceMode"/> /// </param> /// <param name="read_timeout"> /// A <see cref="System.Int32"/> /// </param> public override void Open(DeviceMode mode, int read_timeout) { if (!Opened) { StringBuilder errbuf = new StringBuilder(Pcap.PCAP_ERRBUF_SIZE); //will hold errors // set the StopCaptureTimeout value to twice the read timeout to ensure that // we wait long enough before considering the capture thread to be stuck when stopping // a background capture via StopCapture() // // NOTE: Doesn't affect Mono if unix poll is available, doesn't affect Linux because // Linux devices have no timeout, they always block. Only affects Windows devices. StopCaptureTimeout = new TimeSpan(0, 0, 0, 0, read_timeout * 2); PcapHandle = LibPcapSafeNativeMethods.pcap_open_live (Name, // name of the device Pcap.MAX_PACKET_SIZE, // portion of the packet to capture. // MAX_PACKET_SIZE (65536) grants that the whole packet will be captured on all the MACs. (short)mode, // promiscuous mode (short)read_timeout, // read timeout errbuf); // error buffer if (PcapHandle == IntPtr.Zero) { string err = "Unable to open the adapter (" + Name + "). " + errbuf.ToString(); throw new PcapException(err); } } }
/// <summary> /// Closes the opened dump file /// </summary> public void DumpClose() { if (DumpOpened) { LibPcapSafeNativeMethods.pcap_dump_close(m_pcapDumpHandle); m_pcapDumpHandle = IntPtr.Zero; } }
/// <summary> /// Free memory allocated in CompileFilter() /// </summary> /// <param name="bpfProgram"> /// A <see cref="IntPtr"/> /// </param> private static void FreeBpfProgram(IntPtr bpfProgram) { // free any pcap internally allocated memory from pcap_compile() LibPcapSafeNativeMethods.pcap_freecode(bpfProgram); // free allocated buffers Marshal.FreeHGlobal(bpfProgram); }
/// <summary> /// Flushes all write buffers of the opened dump file /// </summary> public void DumpFlush() { if (DumpOpened) { int result = LibPcapSafeNativeMethods.pcap_dump_flush(m_pcapDumpHandle); if (result < 0) { throw new PcapException("Error writing buffer to dumpfile. " + LastError); } } }
/// <summary> /// Retrieve a list of the current PcapDevices /// </summary> /// <returns> /// A <see cref="List<LibPcapLiveDevice>"/> /// </returns> private static List <LibPcapLiveDevice> GetDevices() { var deviceList = new List <LibPcapLiveDevice>(); var devicePtr = IntPtr.Zero; var errorBuffer = new StringBuilder(Pcap.PCAP_ERRBUF_SIZE); int result = LibPcapSafeNativeMethods.pcap_findalldevs(ref devicePtr, errorBuffer); if (result < 0) { throw new PcapException(errorBuffer.ToString()); } IntPtr nextDevPtr = devicePtr; while (nextDevPtr != IntPtr.Zero) { // Marshal pointer into a struct PcapUnmanagedStructures.pcap_if pcap_if_unmanaged = (PcapUnmanagedStructures.pcap_if)Marshal.PtrToStructure(nextDevPtr, typeof(PcapUnmanagedStructures.pcap_if)); PcapInterface pcap_if = new PcapInterface(pcap_if_unmanaged); deviceList.Add(new LibPcapLiveDevice(pcap_if)); nextDevPtr = pcap_if_unmanaged.Next; } LibPcapSafeNativeMethods.pcap_freealldevs(devicePtr); // Free unmanaged memory allocation. // go through the network interfaces to populate the mac address // for each of the devices NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces(); foreach (LibPcapLiveDevice device in deviceList) { foreach (NetworkInterface adapter in nics) { // if the name and id match then we have found the NetworkInterface // that matches the PcapDevice if (device.Name.EndsWith(adapter.Id)) { var ipProperties = adapter.GetIPProperties(); if (ipProperties.GatewayAddresses.Count != 0) { device.Interface.GatewayAddress = ipProperties.GatewayAddresses[0].Address; } device.Interface.MacAddress = adapter.GetPhysicalAddress(); device.Interface.FriendlyName = adapter.Name; } } } return(deviceList); }
/// <summary> /// Opens a file for packet writings /// </summary> /// <param name="fileName"></param> public void DumpOpen(string fileName) { ThrowIfNotOpen("Dump requires an open device"); if (DumpOpened) { throw new PcapException("A dump file is already opened"); } m_pcapDumpHandle = LibPcapSafeNativeMethods.pcap_dump_open(PcapHandle, fileName); if (!DumpOpened) { throw new PcapException("Error openning dump file."); } }
/// <summary> /// Opens the device for capture /// </summary> public override void Open() { // holds errors StringBuilder errbuf = new StringBuilder(Pcap.PCAP_ERRBUF_SIZE); //will hold errors // opens offline pcap file IntPtr adapterHandle = LibPcapSafeNativeMethods.pcap_open_offline(this.Name, errbuf); // handle error if (adapterHandle == IntPtr.Zero) { string err = "Unable to open offline adapter: " + errbuf.ToString(); throw new PcapException(err); } // set the local handle this.PcapHandle = adapterHandle; }
/// <summary> /// Returns true if the filter expression was able to be compiled into a /// program without errors /// </summary> public static bool CheckFilter(string filterExpression, out string errorString) { IntPtr bpfProgram; IntPtr fakePcap = LibPcapSafeNativeMethods.pcap_open_dead((int)Kavprot.Packets.LinkLayers.Ethernet, Pcap.MAX_PACKET_SIZE); uint mask = 0; if (!CompileFilter(fakePcap, filterExpression, mask, out bpfProgram, out errorString)) { LibPcapSafeNativeMethods.pcap_close(fakePcap); return(false); } FreeBpfProgram(bpfProgram); LibPcapSafeNativeMethods.pcap_close(fakePcap); return(true); }
/// <summary> /// Writes a packet to the pcap dump file associated with this device. /// </summary> public void Dump(byte[] p, PcapHeader h) { ThrowIfNotOpen("Cannot dump packet, device is not opened"); if (!DumpOpened) { throw new DeviceNotReadyException("Cannot dump packet, dump file is not opened"); } //Marshal packet IntPtr pktPtr; pktPtr = Marshal.AllocHGlobal(p.Length); Marshal.Copy(p, 0, pktPtr, p.Length); //Marshal header IntPtr hdrPtr = h.MarshalToIntPtr(); LibPcapSafeNativeMethods.pcap_dump(m_pcapDumpHandle, hdrPtr, pktPtr); Marshal.FreeHGlobal(pktPtr); Marshal.FreeHGlobal(hdrPtr); }
// If CompileFilter() returns true bpfProgram must be freed by passing it to FreeBpfProgram() /// or unmanaged memory will be leaked private static bool CompileFilter(IntPtr pcapHandle, string filterExpression, uint mask, out IntPtr bpfProgram, out string errorString) { int result; string err = String.Empty; bpfProgram = IntPtr.Zero; errorString = null; //Alocate an unmanaged buffer bpfProgram = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(PcapUnmanagedStructures.bpf_program))); //compile the expressions result = LibPcapSafeNativeMethods.pcap_compile(pcapHandle, bpfProgram, filterExpression, 1, mask); if (result < 0) { err = GetLastError(pcapHandle); // free up the program memory Marshal.FreeHGlobal(bpfProgram); bpfProgram = IntPtr.Zero; // make sure not to pass out a valid pointer // set the error string errorString = err; return(false); } return(true); }
/// <summary> /// Gets the next packet captured on this device /// </summary> /// <param name="p"> /// A <see cref="Kavprot.Packets.RawPacket"/> /// </param> /// <returns> /// A <see cref="System.Int32"/> that contains the result code /// </returns> public virtual int GetNextPacket(out Kavprot.Packets.RawPacket p) { //Pointer to a packet info struct IntPtr header = IntPtr.Zero; //Pointer to a packet struct IntPtr data = IntPtr.Zero; int res = 0; // using an invalid PcapHandle can result in an unmanaged segfault // so check for that here ThrowIfNotOpen("Device must be opened via Open() prior to use"); // If a user is calling GetNextPacket() when the background capture loop // is also calling into libpcap then bad things can happen // // The bad behavior I (Chris M.) saw was that the background capture would keep running // but no more packets were captured. Took two days to debug and regular users // may hit the issue more often so check and report the issue here if (Started) { throw new InvalidOperationDuringBackgroundCaptureException("GetNextPacket() invalid during background capture"); } //Get a packet from winpcap res = LibPcapSafeNativeMethods.pcap_next_ex(PcapHandle, ref header, ref data); p = null; if (res > 0) { //Marshal the packet if ((header != IntPtr.Zero) && (data != IntPtr.Zero)) { p = MarshalRawPacket(header, data); } } return(res); }
/// <summary> /// Closes this adapter /// </summary> public virtual void Close() { if (PcapHandle == IntPtr.Zero) { return; } if (Started) { StopCapture(); } LibPcapSafeNativeMethods.pcap_close(PcapHandle); PcapHandle = IntPtr.Zero; //Remove event handlers if (OnPacketArrival != null) { foreach (PacketArrivalEventHandler pa in OnPacketArrival.GetInvocationList()) { OnPacketArrival -= pa; } } }
/// <summary> /// Retrieve pcap statistics from the adapter /// </summary> /// <param name="pcap_t"> /// pcap_t* for the adapter /// A <see cref="IntPtr"/> /// </param> internal PcapStatistics(IntPtr pcap_t) { IntPtr stat; if (Environment.OSVersion.Platform == PlatformID.Unix) { // allocate memory for the struct stat = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(PcapUnmanagedStructures.pcap_stat_unix))); } else { // allocate memory for the struct stat = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(PcapUnmanagedStructures.pcap_stat_windows))); } // retrieve the stats var result = (PcapUnmanagedStructures.PcapStatReturnValue)LibPcapSafeNativeMethods.pcap_stats(pcap_t, stat); // process the return value switch (result) { case PcapUnmanagedStructures.PcapStatReturnValue.Error: // retrieve the error information var error = LibPcapLiveDevice.GetLastError(pcap_t); // free the stats memory so we don't leak before we throw Marshal.FreeHGlobal(stat); throw new StatisticsException(error); case PcapUnmanagedStructures.PcapStatReturnValue.Success: // nothing to do upon success break; } // marshal the unmanaged memory into an object of the proper type if (Environment.OSVersion.Platform == PlatformID.Unix) { var managedStat = (PcapUnmanagedStructures.pcap_stat_unix)Marshal.PtrToStructure(stat, typeof(PcapUnmanagedStructures.pcap_stat_unix)); // copy the values this.ReceivedPackets = (uint)managedStat.ps_recv; this.DroppedPackets = (uint)managedStat.ps_drop; // this.InterfaceDroppedPackets = (uint)managedStat.ps_ifdrop; } else { var managedStat = (PcapUnmanagedStructures.pcap_stat_windows)Marshal.PtrToStructure(stat, typeof(PcapUnmanagedStructures.pcap_stat_windows)); // copy the values this.ReceivedPackets = (uint)managedStat.ps_recv; this.DroppedPackets = (uint)managedStat.ps_drop; // this.InterfaceDroppedPackets = (uint)managedStat.ps_ifdrop; } // NOTE: Not supported on unix or winpcap, no need to // put a bogus value in this field this.InterfaceDroppedPackets = 0; // free the stats Marshal.FreeHGlobal(stat); }
/// <summary> /// The capture thread /// </summary> protected virtual void CaptureThread() { if (!Opened) { throw new DeviceNotReadyException("Capture called before PcapDevice.Open()"); } var usePoll = (this is LibPcapLiveDevice) && isLibPcap && MonoUnixFound; // unix specific code int captureFileDescriptor = 0; if (usePoll) { // retrieve the file descriptor of the adapter for use with poll() captureFileDescriptor = LibPcapSafeNativeMethods.pcap_fileno(PcapHandle); if (captureFileDescriptor == -1) { SendCaptureStoppedEvent(CaptureStoppedEventStatus.ErrorWhileCapturing); return; } } LibPcapSafeNativeMethods.pcap_handler Callback = new LibPcapSafeNativeMethods.pcap_handler(PacketHandler); // unix specific code #if UseMonoUnixNativeDirectly Pollfd[] pollFds = new Pollfd[1]; #else System.Array pollFds = null; object[] PollParameters = null; #endif // Timeout chosen to allow the capture thread to loop frequently enough // to enable it to properly exit when the user requests it to but // infrequently enough to cause any noticable performance overhead int millisecondTimeout = 500; if (usePoll) { #if UseMonoUnixNativeDirectly pollFds[0].fd = captureFileDescriptor; pollFds[0].events = PollEvents.POLLPRI | Mono.Unix.Native.PollEvents.POLLIN; #else FieldInfo field; pollFds = Array.CreateInstance(PollfdType, 1); // create a PollFd struct instance var pollFd = Activator.CreateInstance(PollfdType); // set the descriptor field field = PollfdType.GetField("fd"); field.SetValue(pollFd, captureFileDescriptor); // set the events field short eventValue = (short)(POLLIN | POLLPRI); // mask the two together field = PollfdType.GetField("events"); field.SetValue(pollFd, eventValue); // set the Pollfd entry pollFds.SetValue(pollFd, 0); // setup the parameters we will pass to the poll() method PollParameters = new object[2]; PollParameters[0] = pollFds; PollParameters[1] = millisecondTimeout; #endif } while (!shouldCaptureThreadStop) { // unix specific code, we want to poll for packets // otherwise if we call pcap_dispatch() the read() will block // and won't resume until a packet arrives OR until a signal // occurs if (usePoll) { // block here #if UseMonoUnixNativeDirectly var result = Mono.Unix.Native.Syscall.poll(pollFds, millisecondTimeout); #else object o = SyscallType.InvokeMember("poll", BindingFlags.InvokeMethod, Type.DefaultBinder, null, PollParameters); int result = (int)o; #endif // if we have no poll results, just loop if (result <= 0) { continue; } // fall through here to the pcap_dispatch() call } int res = LibPcapSafeNativeMethods.pcap_dispatch(PcapHandle, m_pcapPacketCount, Callback, IntPtr.Zero); // pcap_dispatch() returns the number of packets read or, a status value if the value // is negative if (res <= 0) { switch (res) // Check pcap loop status results and notify upstream. { case Pcap.LOOP_USER_TERMINATED: // User requsted loop termination with StopCapture() SendCaptureStoppedEvent(CaptureStoppedEventStatus.CompletedWithoutError); return; case Pcap.LOOP_COUNT_EXHAUSTED: // m_pcapPacketCount exceeded (successful exit) { // NOTE: pcap_dispatch() returns 0 when a timeout occurrs so to prevent timeouts // from causing premature exiting from the capture loop we only consider // exhausted events to cause an escape from the loop when they are from // offline devices, ie. files read from disk if (this is OfflinePcapDevice) { SendCaptureStoppedEvent(CaptureStoppedEventStatus.CompletedWithoutError); return; } break; } case Pcap.LOOP_EXIT_WITH_ERROR: // An error occurred whilst capturing. SendCaptureStoppedEvent(CaptureStoppedEventStatus.CompletedWithoutError); return; default: // This can only be triggered by a bug in libpcap. throw new PcapException("Unknown pcap_loop exit status."); } } else // res > 0 { // if we aren't capturing infinitely we need to account for // the packets that we read if (m_pcapPacketCount != Pcap.InfinitePacketCount) { // take away for the packets read if (m_pcapPacketCount >= res) { m_pcapPacketCount -= res; } else { m_pcapPacketCount = 0; } // no more packets to capture, we are finished capturing if (m_pcapPacketCount == 0) { SendCaptureStoppedEvent(CaptureStoppedEventStatus.CompletedWithoutError); return; } } } } SendCaptureStoppedEvent(CaptureStoppedEventStatus.CompletedWithoutError); }
/// <summary> /// Retrieve the last error string for a given pcap_t* device /// </summary> /// <param name="deviceHandle"> /// A <see cref="IntPtr"/> /// </param> /// <returns> /// A <see cref="System.String"/> /// </returns> internal static string GetLastError(IntPtr deviceHandle) { IntPtr err_ptr = LibPcapSafeNativeMethods.pcap_geterr(deviceHandle); return(Marshal.PtrToStringAnsi(err_ptr)); }