/// <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>
        /// 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;
            }
        }