/// <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; } }
public MultipathTracerouteCaptureHandler(PacketCapture parent, MultipathTracerouteResult result, PacketCaptureCallback success, PacketErrorCallback error) : base(parent, result.PacketFilters, success, error) { this.Result = result; }
/// <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> /// 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 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. ///// </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> /// Runs a multipath traceroute to the specified destination. /// </summary> /// <param name="localAddress">The local IP address.</param> /// <param name="remoteAddress">The remote IP address.</param> /// <param name="cancel">The cancellation token.</param> /// <param name="callback">The callback method.</param> /// <returns>The result of the traceroute operation.</returns> public MultipathTracerouteResult RunIpv4(IPAddress localAddress, IPAddress remoteAddress, CancellationToken cancel, MultipathTracerouteCallback callback) { // Validate the arguments. if (null == localAddress) throw new ArgumentNullException("localAddress"); if (null == remoteAddress) throw new ArgumentNullException("remoteAddress"); if (localAddress.AddressFamily != remoteAddress.AddressFamily) throw new ArgumentException("The local and remote addresses have a different address family."); if (localAddress.AddressFamily != AddressFamily.InterNetwork) throw new ArgumentException("Unsupported address family."); // Create the traceroute result. using (MultipathTracerouteResult result = new MultipathTracerouteResult(localAddress, remoteAddress, this.settings, callback)) { // Add the result to the list of result. lock (this.syncResults) { this.results.Add(result); } // Create the local end-point. IPEndPoint localEndPoint = new IPEndPoint(localAddress, 0); IPEndPoint remoteEndPoint = new IPEndPoint(remoteAddress, 0); // Register the packet capture handler. using (MultipathTracerouteCaptureHandler handler = new MultipathTracerouteCaptureHandler(this.capture, result, this.PacketReceiveSuccess, this.PacketReceiveError)) { // Create a receiving socket. //using (Socket socketRecv = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP)) //{ // Bind the socket to the local address. //socketRecv.Bind(localEndPoint); // Indicate the IP header included by the application. //socketRecv.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.HeaderIncluded, true); // Set the control code for receiving all packets. //socketRecv.IOControl(IOControlCode.ReceiveAll, new byte[4] { 1, 0, 0, 0 }, new byte[4] { 1, 0, 0, 0 }); // Wait for packets. //this.ReceivePacket(socketRecv, cancel, result); // Create a sending socket. using (Socket socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IPv4)) { // Bind the socket to the local address. socketSend.Bind(localEndPoint); // Indicate the IP header included by the application. socketSend.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.HeaderIncluded, true); if ((this.settings.Algorithm & MultipathAlgorithm.Icmp) != 0) { // Run the traceroute using ICMP. this.RunIcmpv4(localEndPoint, remoteEndPoint, socketSend, cancel, result); } if ((this.settings.Algorithm & MultipathAlgorithm.Udp) != 0) { // Run the traceroute using UDP. this.RunUdpv4(localEndPoint, remoteEndPoint, socketSend, cancel, result, MultipathAlgorithm.Udp); } if ((this.settings.Algorithm & MultipathAlgorithm.UdpTest) != 0) { // Run the traceroute for the UDP test. this.RunUdpv4(localEndPoint, remoteEndPoint, socketSend, cancel, result, MultipathAlgorithm.UdpTest); } } } // Remove the result from the results list. lock (this.syncResults) { this.results.Remove(result); } return result; } }