/// <summary> /// Parses an IP packet from the specified buffer at the given index, filtering according to the specified filters. /// </summary> /// <param name="buffer">The buffer.</param> /// <param name="index">The index.</param> /// <param name="length">The length.</param> /// <param name="filters">The list of filters.</param> /// <param name="packet">The IP packet or <b>null</b>.</param> /// <returns><b>True</b> if th packet was parsed, <b>false</b> otherwise.</returns> public static bool ParseFilter(byte[] buffer, ref int index, int length, FilterIp[] filters, out ProtoPacketIp packet) { // Set the packet to null. packet = null; // Validate the packet. if (buffer[index] >> 4 != ProtoPacketIp.version) return false; // Validate the length. if ((ushort)((buffer[index + 2] << 8) | buffer[index + 3]) != length - index) return false; // Set the protocol. byte protocol = buffer[index + 9]; // Parse the source address. IPAddress sourceAddress = new IPAddress(new byte[] { buffer[index + 12], buffer[index + 13], buffer[index + 14], buffer[index + 15] }); // Parse the destination address. IPAddress destinationAddress = new IPAddress(new byte[] { buffer[index + 16], buffer[index + 17], buffer[index + 18], buffer[index + 19] }); // Try and match the filters. bool match = false; for (int idx = 0; (idx < filters.Length) && (!match); idx++) { match = match || filters[idx].Matches(sourceAddress, destinationAddress, protocol); } if (!match) return false; // Parse the packet. packet = ProtoPacketIp.Parse(buffer, ref index, length); // Return true. return true; }
/// <summary> /// Creates a new payload packet instance. /// </summary> /// <param name="protocol">The protocol.</param> public ProtoPacketIpPayload(ProtoPacketIp.Protocols protocol) { this.Protocol = protocol; }
/// <summary> /// Runs the traceroute using ICMP version 4. /// </summary> /// <param name="localEndPoint">The local end point.</param> /// <param name="remoteEndPoint">The remote end point.</param> /// <param name="socket">The sending socket.</param> /// <param name="cancel">The cancellation token.</param> /// <param name="result">The result.</param> private void RunIcmpv4(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, Socket socket, CancellationToken cancel, MultipathTracerouteResult result) { // Set the packet processing delegate. lock (this.syncProcess) { this.processPacket = this.ProcessPacketIcmp; } // The data payload. byte[] data = new byte[this.settings.DataLength]; for (int index = 2; index < data.Length; index++) { data[index] = (byte)((index - 2) & 0xFF); } // Create an ICMP echo request packet. ProtoPacketIcmpEchoRequest packetIcmpEchoRequest = new ProtoPacketIcmpEchoRequest(0, 0, data); // Create an IP traceroute option. //ProtoPacketIpOptionTraceroute packetIpOptionTraceroute = new ProtoPacketIpOptionTraceroute(0, 0, 0, localEndPoint.Address); // Create an IP record route option. //ProtoPacketIpOptionRecordRoute packetIpOptionRecordRoute = new ProtoPacketIpOptionRecordRoute(ProtoPacketIpOptionRecordRoute.maxSize); // Create an IP version 4 packet. ProtoPacketIp packetIp = new ProtoPacketIp(localEndPoint.Address, remoteEndPoint.Address, packetIcmpEchoRequest); // Begin the ICMP measurements. result.Callback(MultipathTracerouteState.StateType.BeginAlgorithm, MultipathAlgorithm.Icmp); // For each attempt. for (byte attempt = 0; attempt < settings.AttemptsPerFlow; attempt++) { // For each flow. for (byte flow = 0; flow < result.Flows.Length; flow++) { // Call the start flow handler. result.Callback(MultipathTracerouteState.StateType.BeginFlow, flow); // Set the ICMP packet identifier. packetIcmpEchoRequest.Identifier = result.Flows[flow].IcmpId; for (byte retry = 0; (retry < this.settings.MaximumRetries) && (!result.IsIcmpDataComplete(flow, attempt)); retry++) { // If the retry is greater than zero, wait a random time. if (retry > 0) { // Wait before beginning the next attempt. Thread.Sleep(this.settings.RetryDelay); } // For each time-to-live. for (byte ttl = this.settings.MinimumHops; ttl <= this.settings.MaximumHops; ttl++) { // If the response was received for this flow, TTL and attempt, skip. if (result.IsIcmpDataResponseReceived(flow, ttl, attempt)) continue; // Call the begin time-to-live. result.Callback(MultipathTracerouteState.StateType.BeginTtl, ttl); // Set the packet TTL. packetIp.TimeToLive = ttl; // Set the ICMP packet sequence number. packetIcmpEchoRequest.Sequence = (ushort)((ttl << 8) | attempt); // Compute the ICMP data to set the checksum. int checksumDiff = (ushort)(~result.Flows[flow].IcmpChecksum & 0xFFFF) + ProtoPacket.ChecksumOneComplement16Bit(data, 2, data.Length - 2, (ushort)((packetIcmpEchoRequest.Type << 8) | packetIcmpEchoRequest.Code), packetIcmpEchoRequest.Identifier, packetIcmpEchoRequest.Sequence); checksumDiff = ((checksumDiff >> 16) + (checksumDiff & 0xFFFF)) & 0xFFFF; // Set the data checksum difference. data[0] = (byte)(checksumDiff >> 8); data[1] = (byte)(checksumDiff & 0xFF); // Write the packet to the buffer. packetIp.Write(bufferSend, 0); try { // Send a packet. socket.SendTo(bufferSend, (int)packetIp.Length, SocketFlags.None, remoteEndPoint); // Add the request. MultipathTracerouteResult.RequestState state = result.AddRequest(MultipathTracerouteResult.RequestType.Icmp, flow, ttl, attempt, TimeSpan.FromMilliseconds(this.settings.HopTimeout)); // Set the data. result.IcmpDataRequestSent(flow, ttl, attempt, state.Timestamp); } catch { } // Call the end time-to-live. result.Callback(MultipathTracerouteState.StateType.EndTtl, ttl); } } // Call the end flow handler. result.Callback(MultipathTracerouteState.StateType.EndFlow, flow); // Wait before beginning the next attempt. Thread.Sleep(this.settings.AttemptDelay); } } // Wait for the result to complete. result.Wait.WaitOne(); // Process the result statistics. result.ProcessStatistics(MultipathTracerouteResult.ResultAlgorithm.Icmp); // End the ICMP measurements. result.Callback(MultipathTracerouteState.StateType.EndAlgorithm, MultipathAlgorithm.Icmp); // Clear the packet processing delegate. lock (this.syncProcess) { this.processPacket = null; } }
/// <summary> /// Runs the traceroute using UDP over IP version 4. /// </summary> /// <param name="localEndPoint">The local end point.</param> /// <param name="remoteEndPoint">The remote end point.</param> /// <param name="socket">The sending socket.</param> /// <param name="cancel">The cancellation token.</param> /// <param name="result">The result.</param> /// <param name="algorithm">The multipath algorithm.</param> private void RunUdpv4(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, Socket socket, CancellationToken cancel, MultipathTracerouteResult result, MultipathAlgorithm algorithm) { // The request type. MultipathTracerouteResult.RequestType requestType = MultipathTracerouteResult.RequestType.None; if ((this.settings.Algorithm & MultipathAlgorithm.UdpType & MultipathAlgorithm.UdpIdentification) != 0) { // Set the request type. requestType = requestType | MultipathTracerouteResult.RequestType.UdpIdentification; } if ((this.settings.Algorithm & MultipathAlgorithm.UdpType & MultipathAlgorithm.UdpChecksum) != 0) { // Set the request type. requestType = requestType | MultipathTracerouteResult.RequestType.UdpChecksum; } if ((this.settings.Algorithm & MultipathAlgorithm.UdpTest) != 0) { // Set the request type. requestType = MultipathTracerouteResult.RequestType.UdpBoth; } // Set the packet processing delegate lock (this.syncProcess) { if ((requestType & MultipathTracerouteResult.RequestType.UdpIdentification) != 0) this.processPacket = this.ProcessPacketUdpIdentification; else this.processPacket = this.ProcessPacketUdpChecksum; } // The data payload. byte[] data = new byte[this.settings.DataLength]; for (int index = 2; index < data.Length; index++) { data[index] = (byte)((index - 2) & 0xFF); } // Create an UDP packet. ProtoPacketUdp packetUdp = new ProtoPacketUdp(0, 0, data); // Create an IP version 4 packet. ProtoPacketIp packetIp = new ProtoPacketIp(localEndPoint.Address, remoteEndPoint.Address, packetUdp); packetIp.DifferentiatedServices = 0x80; // Begin the UDP measurements. result.Callback(MultipathTracerouteState.StateType.BeginAlgorithm, algorithm); // For each attempt. for (byte attempt = 0; attempt < settings.AttemptsPerFlow; attempt++) { // For each flow. for (byte flow = 0; flow < result.Flows.Length; flow++) { // Call the start flow handler. result.Callback(MultipathTracerouteState.StateType.BeginFlow, flow); // Set the UDP packet source port. packetUdp.SourcePort = result.Flows[flow].UdpSourcePort; // Set the UDP packet destination port. packetUdp.DestinationPort = result.Flows[flow].UdpDestinationPort; for (byte retry = 0; (retry < this.settings.MaximumRetries) && (!result.IsUdpDataComplete(flow, attempt)); retry++) { // If the retry is greater than zero, wait a random time. if (retry > 0) { // Wait before beginning the next attempt. Thread.Sleep(this.settings.RetryDelay); } // For each time-to-live. for (byte ttl = this.settings.MinimumHops; ttl <= this.settings.MaximumHops; ttl++) { // If the response was received for this flow, TTL and attempt, skip. if (result.IsUdpDataResponseReceived(flow, ttl, attempt)) continue; // Call the begin time-to-live. result.Callback(MultipathTracerouteState.StateType.BeginTtl, ttl); // Set the packet TTL. packetIp.TimeToLive = ttl; if ((requestType & MultipathTracerouteResult.RequestType.UdpIdentification) != 0) { // Set the packet identification. packetIp.Identification = (ushort)((ttl << 8) | attempt); } if ((requestType & MultipathTracerouteResult.RequestType.UdpChecksum) != 0) { // Compute the UDP data to set the checksum. ushort checksum = (ushort)((ttl << 8) | attempt); int checksumDiff = (ushort)(~checksum & 0xFFFF) + ProtoPacket.ChecksumOneComplement16Bit(data, 2, data.Length - 2, packetUdp.SourcePort, packetUdp.DestinationPort, packetUdp.Length, (ushort)((packetIp.SourceAddressBytes[0] << 8) | packetIp.SourceAddressBytes[1]), (ushort)((packetIp.SourceAddressBytes[2] << 8) | packetIp.SourceAddressBytes[3]), (ushort)((packetIp.DestinationAddressBytes[0] << 8) | packetIp.DestinationAddressBytes[1]), (ushort)((packetIp.DestinationAddressBytes[2] << 8) | packetIp.DestinationAddressBytes[3]), packetIp.Protocol, packetUdp.Length); checksumDiff = ((checksumDiff >> 16) + (checksumDiff & 0xFFFF)) & 0xFFFF; // Set the data checksum difference. data[0] = (byte)(checksumDiff >> 8); data[1] = (byte)(checksumDiff & 0xFF); } // Write the packet to the buffer. packetIp.Write(bufferSend, 0); try { // Send a packet. socket.SendTo(bufferSend, (int)packetIp.Length, SocketFlags.None, remoteEndPoint); // Add the request. MultipathTracerouteResult.RequestState state = result.AddRequest(MultipathTracerouteResult.RequestType.Udp, flow, ttl, attempt, TimeSpan.FromMilliseconds(this.settings.HopTimeout)); // Set the data. result.UdpDataRequestSent(flow, ttl, attempt, state.Timestamp); } catch { } // Call the end time-to-live. result.Callback(MultipathTracerouteState.StateType.EndTtl, ttl); } } // Call the end flow handler. result.Callback(MultipathTracerouteState.StateType.EndFlow, flow); // Wait before beginning the next attempt. Thread.Sleep(this.settings.AttemptDelay); } } // Wait for the result to complete. result.Wait.WaitOne(); // Process the result statistics. result.ProcessStatistics(MultipathTracerouteResult.ResultAlgorithm.Udp); // End the UDP measurements. result.Callback(MultipathTracerouteState.StateType.EndAlgorithm, algorithm); // Clear the packet processing delegate. lock (this.syncProcess) { this.processPacket = null; } }
/// <summary> /// Processes a received packet for a UDP request using the checksum field. /// </summary> /// <param name="ip">The IP packet.</param> /// <param name="length">The data length.</param> /// <param name="result">The result.</param> private void ProcessPacketUdpChecksum(ProtoPacketIp ip, int length, MultipathTracerouteResult result) { }
/// <summary> /// Processes a received packet for a UDP request using the identification field. /// </summary> /// <param name="ip">The IP packet.</param> /// <param name="length">The data length.</param> /// <param name="result">The result.</param> private void ProcessPacketUdpIdentification(ProtoPacketIp ip, int length, MultipathTracerouteResult result) { ProtoPacketIcmp icmp; // If the packet payload is ICMP. if ((icmp = ip.Payload as ProtoPacketIcmp) != null) { if (icmp.Type == (byte)ProtoPacketIcmp.IcmpType.TimeExceeded) { // If the packet type is ICMP time exceeded. ProtoPacketIcmpTimeExceeded icmpTimeExceeded = icmp as ProtoPacketIcmpTimeExceeded; // Use the last bytes of the UDP ports to find the flow. ushort flowId = (ushort)((icmpTimeExceeded.IpPayload[1] << 8) | icmpTimeExceeded.IpPayload[3]); byte flow; if (result.TryGetUdpFlow(flowId, out flow)) { // Get the time-to-live. byte ttl = (byte)(icmpTimeExceeded.IpHeader.Identification >> 8); // Get the attempt. byte attempt = (byte)(icmpTimeExceeded.IpHeader.Identification & 0xF); // Add the result. result.UdpDataResponseReceived(flow, ttl, attempt, MultipathTracerouteData.ResponseType.TimeExceeded, DateTime.Now, ip); // Remove the corresponding request. result.RemoveRequest(MultipathTracerouteResult.RequestType.Udp, flow, ttl, attempt); } } else if (icmp.Type == (byte)ProtoPacketIcmp.IcmpType.DestinationUnreachable) { // If the packet type is ICMP destination unreachable. ProtoPacketIcmpDestinationUnreachable icmpDestinationUnreachable = icmp as ProtoPacketIcmpDestinationUnreachable; // Check the message is a port unreachable. if (icmpDestinationUnreachable.Code == (byte)ProtoPacketIcmpDestinationUnreachable.DestinationUnreachableCode.PortUnreachable) { // Use the last bytes of the UDP ports to find the flow. ushort flowId = (ushort)((icmpDestinationUnreachable.IpPayload[1] << 8) | icmpDestinationUnreachable.IpPayload[3]); byte flow; if (result.TryGetUdpFlow(flowId, out flow)) { // Get the time-to-live. byte ttl = (byte)(icmpDestinationUnreachable.IpHeader.Identification >> 8); // Get the attempt. byte attempt = (byte)(icmpDestinationUnreachable.IpHeader.Identification & 0xF); // Add the result. result.UdpDataResponseReceived(flow, ttl, attempt, MultipathTracerouteData.ResponseType.DestinationUnreachable, DateTime.Now, ip); // Remove the corresponding request. result.RemoveRequest(MultipathTracerouteResult.RequestType.Udp, flow, ttl, attempt); } } } } }
///// <summary> ///// Processes a received packet. ///// </summary> ///// <param name="buffer">The data buffer.</param> ///// <param name="length">The data length.</param> ///// <param name="result">The result.</param> //private void ProcessPacket(byte[] buffer, int length, MultipathTracerouteResult result) //{ // // Set the buffer index. // int index = 0; // // The packets. // ProtoPacketIp ip; // // Try and parse the packet using the specified filter. // if (ProtoPacketIp.ParseFilter(buffer, ref index, length, result.PacketFilters, out ip)) // { // // Call the callback methods. // result.Callback(MultipathTracerouteState.StateType.PacketCapture, ip); // // Process the packet for the current protocol. // lock (this.syncProcess) // { // if (null != this.processPacket) this.processPacket(ip, length, result); // } // } //} /// <summary> /// Processes a received packet for an ICMP request. /// </summary> /// <param name="ip">The IP packet.</param> /// <param name="length">The data length.</param> /// <param name="result">The result.</param> private void ProcessPacketIcmp(ProtoPacketIp ip, int length, MultipathTracerouteResult result) { ProtoPacketIcmp icmp; // If the packet payload is ICMP. if ((icmp = ip.Payload as ProtoPacketIcmp) != null) { if (icmp.Type == (byte)ProtoPacketIcmp.IcmpType.EchoReply) { // If the packet type is ICMP echo reply. ProtoPacketIcmpEchoReply icmpEchoReply = icmp as ProtoPacketIcmpEchoReply; // Use the reply identifier to find the flow. byte flow; if (result.TryGetIcmpFlow(icmpEchoReply.Identifier, out flow)) { // Get the time-to-live. byte ttl = (byte)(icmpEchoReply.Sequence >> 8); // Get the attempt. byte attempt = (byte)(icmpEchoReply.Sequence & 0xFF); // Add the result. result.IcmpDataResponseReceived(flow, ttl, attempt, MultipathTracerouteData.ResponseType.EchoReply, DateTime.Now, ip); // Remove the corresponding request. result.RemoveRequest(MultipathTracerouteResult.RequestType.Icmp, flow, ttl, attempt); } } else if (icmp.Type == (byte)ProtoPacketIcmp.IcmpType.TimeExceeded) { // If the packet type is ICMP time exceeded. ProtoPacketIcmpTimeExceeded icmpTimeExceeded = icmp as ProtoPacketIcmpTimeExceeded; // Use the ICMP request identifier to find the flow. ushort flowId = (ushort)((icmpTimeExceeded.IpPayload[4] << 8) | icmpTimeExceeded.IpPayload[5]); byte flow; if (result.TryGetIcmpFlow(flowId, out flow)) { // Get the time-to-live. byte ttl = icmpTimeExceeded.IpPayload[6]; // Get the attempt. byte attempt = icmpTimeExceeded.IpPayload[7]; // Add the result. result.IcmpDataResponseReceived(flow, ttl, attempt, MultipathTracerouteData.ResponseType.TimeExceeded, DateTime.Now, ip); // Remove the corresponding request. result.RemoveRequest(MultipathTracerouteResult.RequestType.Icmp, flow, ttl, attempt); } } } }
///// <summary> ///// Requests a receiving buffer. The method blocks until a buffer is available or until the cancellation is requested. ///// </summary> ///// <param name="cancel">The cancellation token.</param> ///// <returns>The buffer index.</returns> //private int RequestBuffer(CancellationToken cancel) //{ // // The buffer index. // int bufferIndex = -1; // do // { // // Wait for a buffer to become available. // this.bufferWait.WaitOne(); // lock (this.syncBuffer) // { // // If the buffer queue is not empty. // if (this.bufferQueue.Count > 0) // { // // Get the first buffer index. // bufferIndex = this.bufferQueue.Dequeue(); // // If the queue is empty, reset the buffer wait handle. // if (this.bufferQueue.Count == 0) this.bufferWait.Reset(); // } // } // } // while ((bufferIndex == -1) && (!cancel.IsCancellationRequested)); // // Return the buffer index. // return bufferIndex; //} ///// <summary> ///// Releases the specified buffer. ///// </summary> ///// <param name="bufferIndex">The buffer index.</param> //private void ReleaseBuffer(int bufferIndex) //{ // // Release the buffer. // lock (this.syncBuffer) // { // // If the buffer queue is empty, set the buffer wait handle. // if (this.bufferQueue.Count == 0) this.bufferWait.Set(); // // Add the buffer index to the queue. // this.bufferQueue.Enqueue(bufferIndex); // } //} ///// <summary> ///// Receives a packet from the specified socket. ///// </summary> ///// <param name="socket">The socket.</param> ///// <param name="cancel">The cancellation token.</param> ///// <param name="result">The result.</param> //private void ReceivePacket(Socket socket, CancellationToken cancel, MultipathTracerouteResult result) //{ // // The remote end-point. // EndPoint endPoint = new IPEndPoint(IPAddress.Any, 0); // // Request a buffer. // int bufferIndex = this.RequestBuffer(cancel); // // If the operation was canceled, return. // if (cancel.IsCancellationRequested) return; // // Synchronization object. // object localSync = new object(); // // Buffer flag. // bool bufferFlag = true; // try // { // // Begin receiving a packet. // socket.BeginReceiveFrom(this.bufferRecv[bufferIndex], 0, this.bufferRecv[bufferIndex].Length, SocketFlags.None, ref endPoint, (IAsyncResult asyncResult) => // { // lock (localSync) // { // try // { // // End receiving a packet. // int length = socket.EndReceiveFrom(asyncResult, ref endPoint); // // Process the packet. // this.ProcessPacket(this.bufferRecv[bufferIndex], length, result); // } // catch (ObjectDisposedException) { } // catch (Exception exception) // { // // Ignore all errors for received packets. // result.Callback(MultipathTracerouteState.StateType.PacketError, exception); // } // finally // { // // If the buffer flag is set. // if (bufferFlag) // { // // Release the buffer. // this.ReleaseBuffer(bufferIndex); // // Begin receiving the next packet. // this.ReceivePacket(socket, cancel, result); // // Set the flag to false. // bufferFlag = false; // } // } // } // }, null); // } // catch (ObjectDisposedException) { } // catch (Exception) // { // lock (localSync) // { // // If the buffer flag is set. // if (bufferFlag) // { // // Release the buffer. // this.ReleaseBuffer(bufferIndex); // // Begin receiving the next packet. // this.ReceivePacket(socket, cancel, result); // // Set the flag to false. // bufferFlag = false; // } // } // } //} public void PacketReceiveSuccess(PacketCaptureHandler handler, byte[] buffer, int length, ProtoPacketIp ip) { MultipathTracerouteCaptureHandler tracerouteHandler = handler as MultipathTracerouteCaptureHandler; // Call the callback methods. tracerouteHandler.Result.Callback(MultipathTracerouteState.StateType.PacketCapture, ip); // Process the packet for the current protocol. lock (this.syncProcess) { if (null != this.processPacket) this.processPacket(ip, length, tracerouteHandler.Result); } }
public bool Matches(ProtoPacketIp ip) { // Try and match the filters. bool match = false; for (int idx = 0; (idx < filters.Length) && (!match); idx++) { match = match || filters[idx].Matches(ip); } return match; }
public void Success(byte[] buffer, int length, ProtoPacketIp ip) { this.success(this, buffer, length, ip); }
public bool Parse(byte[] buffer, ref int index, int length, out ProtoPacketIp ip) { return ProtoPacketIp.ParseFilter(buffer, ref index, length, this.filters, out ip); }