private static unsafe byte[] CreateSendMessageBuffer(IpHeader ipHeader, IcmpHeader icmpHeader, byte[] payload) { int icmpHeaderSize = sizeof(IcmpHeader); int offset = 0; int packetSize = ipHeader.TotalLength != 0 ? ipHeader.TotalLength : checked (icmpHeaderSize + payload.Length); byte[] result = new byte[packetSize]; if (ipHeader.TotalLength != 0) { int ipHeaderSize = sizeof(IpHeader); new Span <byte>(&ipHeader, sizeof(IpHeader)).CopyTo(result); offset = ipHeaderSize; } //byte[] result = new byte[headerSize + payload.Length]; Marshal.Copy(new IntPtr(&icmpHeader), result, offset, icmpHeaderSize); payload.CopyTo(result, offset + icmpHeaderSize); // offset now still points to beginning of ICMP header. ushort checksum = ComputeBufferChecksum(result.AsSpan(offset)); // Jam the checksum into the buffer. result[offset + 2] = (byte)(checksum >> 8); result[offset + 3] = (byte)(checksum & (0xFF)); return(result); }
private SocketConfig GetSocketConfig(IPAddress address, byte[] buffer, int timeout, PingOptions options) { SocketConfig config = new SocketConfig(); config.EndPoint = new IPEndPoint(address, 0); config.Timeout = timeout; config.Options = options; config.IsIpv4 = address.AddressFamily == AddressFamily.InterNetwork; config.ProtocolType = config.IsIpv4 ? ProtocolType.Icmp : ProtocolType.IcmpV6; // Use a random value as the identifier. This doesn't need to be perfectly random // or very unpredictable, rather just good enough to avoid unexpected conflicts. Random rand = t_idGenerator ?? (t_idGenerator = new Random()); config.Identifier = (ushort)rand.Next((int)ushort.MaxValue + 1); IcmpHeader header = new IcmpHeader() { Type = config.IsIpv4 ? (byte)IcmpV4MessageType.EchoRequest : (byte)IcmpV6MessageType.EchoRequest, Code = 0, HeaderChecksum = 0, Identifier = config.Identifier, SequenceNumber = 0, }; config.SendBuffer = CreateSendMessageBuffer(header, buffer); return(config); }
// ctor public IcmpEchoRequest(ref IcmpHeader hdr, byte[] !msgData) { header = hdr; // set the data data = new byte[msgData.Length]; Array.Copy(msgData, 0, data, 0, msgData.Length); }
private DateTime pingSentTime; // Timestamp of when ping request was sent /// <summary> /// Base constructor that initializes the member variables to default values. It also /// creates the events used and initializes the async callback function. /// </summary> public RawSocketPing() { pingSocket = null; pingTtl = 8; pingPayloadLength = 8; pingSequence = 0; pingReceiveTimeout = 2000; destEndPoint = new IPEndPoint(IPAddress.Loopback, 0); icmpHeader = null; }
// construct an ICMP header from a packet public static bool ReadIcmpHeader(IBuffer !buf, out IcmpHeader hdr) { bool b; b = buf.Read8(out hdr.type); b &= buf.Read8(out hdr.code); b &= buf.ReadNet16(out hdr.chksum); b &= buf.ReadNet16(out hdr.id); b &= buf.ReadNet16(out hdr.seq); return(b); }
private bool TryGetPingReply( SocketConfig socketConfig, byte[] receiveBuffer, int bytesReceived, Stopwatch sw, ref int ipHeaderLength, [NotNullWhen(true)] out PingReply?reply) { byte type, code; reply = null; if (socketConfig.IsIpv4) { // Determine actual size of IP header byte ihl = (byte)(receiveBuffer[0] & 0x0f); // Internet Header Length ipHeaderLength = 4 * ihl; if (bytesReceived - ipHeaderLength < IcmpHeaderLengthInBytes) { return(false); // Not enough bytes to reconstruct actual IP header + ICMP header. } } int icmpHeaderOffset = ipHeaderLength; // Skip IP header. IcmpHeader receivedHeader = MemoryMarshal.Read <IcmpHeader>(receiveBuffer.AsSpan(icmpHeaderOffset)); type = receivedHeader.Type; code = receivedHeader.Code; if (socketConfig.Identifier != receivedHeader.Identifier || type == (byte)IcmpV4MessageType.EchoRequest || type == (byte)IcmpV6MessageType.EchoRequest) // Echo Request, ignore { return(false); } sw.Stop(); long roundTripTime = sw.ElapsedMilliseconds; int dataOffset = ipHeaderLength + IcmpHeaderLengthInBytes; // We want to return a buffer with the actual data we sent out, not including the header data. byte[] dataBuffer = new byte[bytesReceived - dataOffset]; Buffer.BlockCopy(receiveBuffer, dataOffset, dataBuffer, 0, dataBuffer.Length); IPStatus status = socketConfig.IsIpv4 ? IcmpV4MessageConstants.MapV4TypeToIPStatus(type, code) : IcmpV6MessageConstants.MapV6TypeToIPStatus(type, code); IPAddress address = ((IPEndPoint)socketConfig.EndPoint).Address; reply = new PingReply(address, socketConfig.Options, status, roundTripTime, dataBuffer); return(true); }
public IcmpDisplayPacket(IpV4Header ipHeader, IcmpHeader icmpHeader) { Destination = ipHeader.DestinationAddress; //DestinationPort = udpHeader.DestinationPort; Source = ipHeader.SourceAddress; //SourcePort = udpHeader.SourcePort; Type = ipHeader.ProtocolType; Flags = icmpHeader.Flags; StringBuilder retVal = new StringBuilder(); AppendIcmp(icmpHeader, retVal); AppendIPInfo(ipHeader, retVal); Data = retVal.ToString(); }
// write common ICMP header // (ignores the checksum field, notice that the checksum is // calculated for the whole packet!) public static int WriteIcmpHeader(byte[] !pkt, int offset, ref IcmpHeader hdr) { int o = offset; pkt[o++] = hdr.type; pkt[o++] = hdr.code; pkt[o++] = 0; pkt[o++] = 0; pkt[o++] = (byte)(hdr.id >> 8); pkt[o++] = (byte)hdr.id; pkt[o++] = (byte)(hdr.seq >> 8); pkt[o++] = (byte)hdr.seq; return(o); }
private static unsafe byte[] CreateSendMessageBuffer(IcmpHeader header, byte[] payload) { int headerSize = sizeof(IcmpHeader); byte[] result = new byte[headerSize + payload.Length]; Marshal.Copy(new IntPtr(&header), result, 0, headerSize); payload.CopyTo(result, headerSize); ushort checksum = ComputeBufferChecksum(result); // Jam the checksum into the buffer. result[2] = (byte)(checksum >> 8); result[3] = (byte)(checksum & (0xFF)); return(result); }
public ManualResetEvent pingDoneEvent; // Event to indicate all outstanding receives are done // this ping class can be disposed /// <summary> /// Base constructor that initializes the member variables to default values. It also /// creates the events used and initializes the async callback function. /// </summary> public RawSocketPing() { pingSocket = null; pingFamily = AddressFamily.InterNetwork; pingTtl = 8; pingPayloadLength = 8; pingSequence = 0; pingReceiveTimeout = 4000; pingOutstandingReceives = 0; destEndPoint = new IPEndPoint(IPAddress.Loopback, 0); protocolHeaderList = new ArrayList(); pingReceiveEvent = new ManualResetEvent(false); pingDoneEvent = new ManualResetEvent(false); receiveCallback = new AsyncCallback(PingReceiveCallback); icmpHeader = null; icmpv6Header = null; icmpv6EchoRequestHeader = null; }
/// <summary> /// This routine builds the appropriate ICMP echo packet depending on the /// protocol family requested. /// </summary> public void BuildPingPacket() { // Initialize the socket if it hasn't already been done if (pingSocket == null) { InitializeSocket(); } // Create the ICMP header and initialize the members icmpHeader = new IcmpHeader() { Id = pingId, Sequence = pingSequence, Type = IcmpHeader.EchoRequestType, Code = IcmpHeader.EchoRequestCode }; // Build the data payload of the ICMP echo request pingPayload = new byte[pingPayloadLength]; for (int i = 0; i < pingPayload.Length; i++) { pingPayload[i] = (byte)'e'; } }
/// <summary> /// This routine creates an instance of the IcmpHeader class from a byte /// array that is a received IGMP packet. This is useful when a packet /// is received from the network and the header object needs to be /// constructed from those values. /// </summary> /// <param name="icmpPacket">Byte array containing the binary ICMP header</param> /// <param name="bytesCopied">Number of bytes used in header</param> /// <returns>Returns the IcmpHeader object created from the byte array</returns> static public IcmpHeader Create(byte[] icmpPacket, ref int bytesCopied) { IcmpHeader icmpHeader = new IcmpHeader(); int offset = 0; // Make sure byte array is large enough to contain an ICMP header if (icmpPacket.Length < IcmpHeader.IcmpHeaderLength) { return(null); } icmpHeader.icmpType = icmpPacket[offset++]; icmpHeader.icmpCode = icmpPacket[offset++]; icmpHeader.icmpChecksum = BitConverter.ToUInt16(icmpPacket, offset); offset += 2; icmpHeader.icmpId = BitConverter.ToUInt16(icmpPacket, offset); offset += 2; icmpHeader.icmpSequence = BitConverter.ToUInt16(icmpPacket, offset); bytesCopied = IcmpHeader.IcmpHeaderLength; return(icmpHeader); }
/// <summary> /// This routine builds the appropriate ICMP echo packet depending on the /// protocol family requested. /// </summary> public void BuildPingPacket() { // Initialize the socket if it hasn't already been done Console.WriteLine("Building the ping packet..."); Console.WriteLine("Initializing the socket if not done yet..."); if (pingSocket == null) { InitializeSocket(); } // Clear any existing headers in the list Console.WriteLine("Clearing any existing headers in the list using Clear()..."); protocolHeaderList.Clear(); if (destEndPoint.AddressFamily == AddressFamily.InterNetwork) { // Create the ICMP header and initialize the members Console.WriteLine("Creating the ICMP header and initialize the members..."); icmpHeader = new IcmpHeader(); icmpHeader.Id = pingId; icmpHeader.Sequence = pingSequence; icmpHeader.Type = IcmpHeader.EchoRequestType; icmpHeader.Code = IcmpHeader.EchoRequestCode; // Build the data payload of the ICMP echo request Console.WriteLine("Building the data payload of the ICMP echo request..."); pingPayload = new byte[pingPayloadLength]; for (int i = 0; i < pingPayload.Length; i++) { pingPayload[i] = (byte)'e'; } // Add ICMP header to the list of headers Console.WriteLine("Adding ICMP header to the list of headers using Add()..."); protocolHeaderList.Add(icmpHeader); } else if (destEndPoint.AddressFamily == AddressFamily.InterNetworkV6) { Ipv6Header ipv6Header; // Required for pseudo header checksum IPEndPoint localInterface; byte[] localAddressBytes = new byte[28]; Console.WriteLine("This part is for IPv6..."); // An IPv6 header is required since the IPv6 protocol specifies that the // pseudo header checksum needs to be calculated on ICMPv6 packets which // requires the source and destination address that will appear in the // IPv6 packet. ipv6Header = new Ipv6Header(); // We definitely know the destination IPv6 address but the stack will // choose the "appropriate" local v6 interface depending on the // routing table which may be different than the address we bound // the socket to. Because of this we will call the Winsock ioctl // SIO_ROUTING_INTERFACE_QUERY which will return the local interface // for a given destination address by querying the routing table. Console.WriteLine("Implementing the IOControl()..."); pingSocket.IOControl( WinsockIoctl.SIO_ROUTING_INTERFACE_QUERY, SockaddrConvert.GetSockaddrBytes(destEndPoint), localAddressBytes ); localInterface = SockaddrConvert.GetEndPoint(localAddressBytes); // Fill out the fields of the IPv6 header used in the pseudo-header checksum calculation Console.WriteLine("Filling out the IPv6 header fields..."); ipv6Header.SourceAddress = localInterface.Address; ipv6Header.DestinationAddress = destEndPoint.Address; ipv6Header.NextHeader = 58; // IPPROTO_ICMP6 // Initialize the ICMPv6 header Console.WriteLine("Initializing the ICMPv6 header..."); icmpv6Header = new Icmpv6Header(ipv6Header); icmpv6Header.Type = Icmpv6Header.Icmpv6EchoRequestType; icmpv6Header.Code = Icmpv6Header.Icmpv6EchoRequestCode; // Initialize the payload Console.WriteLine("Initializing the payload..."); pingPayload = new byte[pingPayloadLength]; for (int i = 0; i < pingPayload.Length; i++) { pingPayload[i] = (byte)'e'; } // Create the ICMPv6 echo request header Console.WriteLine("Creating the ICMPv6 echo request header..."); icmpv6EchoRequestHeader = new Icmpv6EchoRequest(); icmpv6EchoRequestHeader.Id = pingId; // Add the headers to the protocol header list Console.WriteLine("Adding the headers to the protocol header list..."); protocolHeaderList.Add(icmpv6Header); protocolHeaderList.Add(icmpv6EchoRequestHeader); } }
private void AppendIcmp(IcmpHeader icmpHeader, StringBuilder retVal) { retVal.AppendFormat("[*ICMP* Header:{0}, ", icmpHeader.Header); retVal.AppendFormat("Code:{0},", icmpHeader.Code); retVal.AppendFormat("Type:{0}] ", icmpHeader.Type); }
private async Task <PingReply> SendIcmpEchoRequestOverRawSocket(IPAddress address, byte[] buffer, int timeout, PingOptions options) { EndPoint endPoint = new IPEndPoint(address, 0); bool isIpv4 = address.AddressFamily == AddressFamily.InterNetwork; ProtocolType protocolType = isIpv4 ? ProtocolType.Icmp : ProtocolType.IcmpV6; // Use the current thread's ID as the identifier. ushort identifier = (ushort)Environment.CurrentManagedThreadId; IcmpHeader header = new IcmpHeader() { Type = isIpv4 ? (byte)IcmpV4MessageType.EchoRequest : (byte)IcmpV6MessageType.EchoRequest, Code = 0, HeaderChecksum = 0, Identifier = identifier, SequenceNumber = 0, }; byte[] sendBuffer = CreateSendMessageBuffer(header, buffer); using (Socket socket = new Socket(address.AddressFamily, SocketType.Raw, protocolType)) { socket.ReceiveTimeout = timeout; socket.SendTimeout = timeout; // Setting Socket.DontFragment and .Ttl is not supported on Unix, so ignore the PingOptions parameter. int ipHeaderLength = isIpv4 ? IpHeaderLengthInBytes : 0; await socket.SendToAsync(new ArraySegment <byte>(sendBuffer), SocketFlags.None, endPoint).ConfigureAwait(false); byte[] receiveBuffer = new byte[ipHeaderLength + IcmpHeaderLengthInBytes + buffer.Length]; long elapsed; Stopwatch sw = Stopwatch.StartNew(); // Read from the socket in a loop. We may receive messages that are not echo replies, or that are not in response // to the echo request we just sent. We need to filter such messages out, and continue reading until our timeout. // For example, when pinging the local host, we need to filter out our own echo requests that the socket reads. while ((elapsed = sw.ElapsedMilliseconds) < timeout) { Task <SocketReceiveFromResult> receiveTask = socket.ReceiveFromAsync( new ArraySegment <byte>(receiveBuffer), SocketFlags.None, endPoint); var cts = new CancellationTokenSource(); Task finished = await Task.WhenAny(receiveTask, Task.Delay(timeout - (int)elapsed, cts.Token)).ConfigureAwait(false); cts.Cancel(); if (finished != receiveTask) { sw.Stop(); return(CreateTimedOutPingReply()); } SocketReceiveFromResult receiveResult = receiveTask.GetAwaiter().GetResult(); int bytesReceived = receiveResult.ReceivedBytes; if (bytesReceived - ipHeaderLength < IcmpHeaderLengthInBytes) { continue; // Not enough bytes to reconstruct IP header + ICMP header. } byte type, code; unsafe { fixed(byte *bytesPtr = receiveBuffer) { int icmpHeaderOffset = ipHeaderLength; IcmpHeader receivedHeader = *((IcmpHeader *)(bytesPtr + icmpHeaderOffset)); // Skip IP header. type = receivedHeader.Type; code = receivedHeader.Code; if (identifier != receivedHeader.Identifier || type == (byte)IcmpV4MessageType.EchoRequest || type == (byte)IcmpV6MessageType.EchoRequest) // Echo Request, ignore { continue; } } } sw.Stop(); long roundTripTime = sw.ElapsedMilliseconds; int dataOffset = ipHeaderLength + IcmpHeaderLengthInBytes; // We want to return a buffer with the actual data we sent out, not including the header data. byte[] dataBuffer = new byte[bytesReceived - dataOffset]; Buffer.BlockCopy(receiveBuffer, dataOffset, dataBuffer, 0, dataBuffer.Length); IPStatus status = isIpv4 ? IcmpV4MessageConstants.MapV4TypeToIPStatus(type, code) : IcmpV6MessageConstants.MapV6TypeToIPStatus(type, code); return(new PingReply(address, options, status, roundTripTime, dataBuffer)); } // We have exceeded our timeout duration, and no reply has been received. sw.Stop(); return(CreateTimedOutPingReply()); } }
private async Task<PingReply> SendIcmpEchoRequestOverRawSocket(IPAddress address, byte[] buffer, int timeout, PingOptions options) { EndPoint endPoint = new IPEndPoint(address, 0); bool isIpv4 = address.AddressFamily == AddressFamily.InterNetwork; ProtocolType protocolType = isIpv4 ? ProtocolType.Icmp : ProtocolType.IcmpV6; // Use the current thread's ID as the identifier. ushort identifier = (ushort)Environment.CurrentManagedThreadId; IcmpHeader header = new IcmpHeader() { Type = isIpv4 ? (byte)IcmpV4MessageType.EchoRequest : (byte)IcmpV6MessageType.EchoRequest, Code = 0, HeaderChecksum = 0, Identifier = identifier, SequenceNumber = 0, }; byte[] sendBuffer = CreateSendMessageBuffer(header, buffer); using (Socket socket = new Socket(address.AddressFamily, SocketType.Raw, protocolType)) { socket.ReceiveTimeout = timeout; socket.SendTimeout = timeout; // Setting Socket.DontFragment and .Ttl is not supported on Unix, so ignore the PingOptions parameter. int ipHeaderLength = isIpv4 ? IpHeaderLengthInBytes : 0; await socket.SendToAsync(new ArraySegment<byte>(sendBuffer), SocketFlags.None, endPoint).ConfigureAwait(false); byte[] receiveBuffer = new byte[ipHeaderLength + IcmpHeaderLengthInBytes + buffer.Length]; long elapsed; Stopwatch sw = Stopwatch.StartNew(); // Read from the socket in a loop. We may receive messages that are not echo replies, or that are not in response // to the echo request we just sent. We need to filter such messages out, and continue reading until our timeout. // For example, when pinging the local host, we need to filter out our own echo requests that the socket reads. while ((elapsed = sw.ElapsedMilliseconds) < timeout) { Task<SocketReceiveFromResult> receiveTask = socket.ReceiveFromAsync( new ArraySegment<byte>(receiveBuffer), SocketFlags.None, endPoint); var cts = new CancellationTokenSource(); Task finished = await Task.WhenAny(receiveTask, Task.Delay(timeout - (int)elapsed, cts.Token)).ConfigureAwait(false); cts.Cancel(); if (finished != receiveTask) { sw.Stop(); return CreateTimedOutPingReply(); } SocketReceiveFromResult receiveResult = receiveTask.GetAwaiter().GetResult(); int bytesReceived = receiveResult.ReceivedBytes; if (bytesReceived - ipHeaderLength < IcmpHeaderLengthInBytes) { continue; // Not enough bytes to reconstruct IP header + ICMP header. } byte type, code; unsafe { fixed (byte* bytesPtr = receiveBuffer) { int icmpHeaderOffset = ipHeaderLength; IcmpHeader receivedHeader = *((IcmpHeader*)(bytesPtr + icmpHeaderOffset)); // Skip IP header. type = receivedHeader.Type; code = receivedHeader.Code; if (identifier != receivedHeader.Identifier || type == (byte)IcmpV4MessageType.EchoRequest || type == (byte)IcmpV6MessageType.EchoRequest) // Echo Request, ignore { continue; } } } sw.Stop(); long roundTripTime = sw.ElapsedMilliseconds; int dataOffset = ipHeaderLength + IcmpHeaderLengthInBytes; // We want to return a buffer with the actual data we sent out, not including the header data. byte[] dataBuffer = new byte[bytesReceived - dataOffset]; Array.Copy(receiveBuffer, dataOffset, dataBuffer, 0, dataBuffer.Length); IPStatus status = isIpv4 ? IcmpV4MessageConstants.MapV4TypeToIPStatus(type, code) : IcmpV6MessageConstants.MapV6TypeToIPStatus(type, code); return new PingReply(address, options, status, roundTripTime, dataBuffer); } // We have exceeded our timeout duration, and no reply has been received. sw.Stop(); return CreateTimedOutPingReply(); } }
private static unsafe byte[] CreateSendMessageBuffer(IcmpHeader header, byte[] payload) { int headerSize = sizeof(IcmpHeader); byte[] result = new byte[headerSize + payload.Length]; Marshal.Copy(new IntPtr(&header), result, 0, headerSize); payload.CopyTo(result, headerSize); ushort checksum = ComputeBufferChecksum(result); // Jam the checksum into the buffer. result[2] = (byte)(checksum >> 8); result[3] = (byte)(checksum & (0xFF)); return result; }
private bool TryGetPingReply( SocketConfig socketConfig, byte[] receiveBuffer, int bytesReceived, Stopwatch sw, ref int ipHeaderLength, [NotNullWhen(true)] out PingReply?reply) { byte type, code; reply = null; if (socketConfig.IsIpv4) { // Determine actual size of IP header byte ihl = (byte)(receiveBuffer[0] & 0x0f); // Internet Header Length ipHeaderLength = 4 * ihl; if (bytesReceived - ipHeaderLength < IcmpHeaderLengthInBytes) { return(false); // Not enough bytes to reconstruct actual IP header + ICMP header. } } int icmpHeaderOffset = ipHeaderLength; int dataOffset = ipHeaderLength + IcmpHeaderLengthInBytes; // Skip IP header. IcmpHeader receivedHeader = MemoryMarshal.Read <IcmpHeader>(receiveBuffer.AsSpan(icmpHeaderOffset)); ushort identifier = 0; type = receivedHeader.Type; code = receivedHeader.Code; // Validate the ICMP header and get the identifier if (socketConfig.IsIpv4) { if (type == (byte)IcmpV4MessageType.EchoReply) { // Reply packet has the identifier in the ICMP header. identifier = receivedHeader.Identifier; } else if (type == (byte)IcmpV4MessageType.DestinationUnreachable || type == (byte)IcmpV4MessageType.TimeExceeded || type == (byte)IcmpV4MessageType.ParameterProblemBadIPHeader || type == (byte)IcmpV4MessageType.SourceQuench || type == (byte)IcmpV4MessageType.RedirectMessage) { // Original IP+ICMP request is in the payload. Read the ICMP header from // the payload to get identifier. if (dataOffset + MinIpHeaderLengthInBytes + IcmpHeaderLengthInBytes > bytesReceived) { return(false); } byte ihl = (byte)(receiveBuffer[dataOffset] & 0x0f); // Internet Header Length int payloadIpHeaderLength = 4 * ihl; if (bytesReceived - dataOffset - payloadIpHeaderLength < IcmpHeaderLengthInBytes) { return(false); // Not enough bytes to reconstruct actual IP header + ICMP header. } IcmpHeader originalRequestHeader = MemoryMarshal.Read <IcmpHeader>(receiveBuffer.AsSpan(dataOffset + payloadIpHeaderLength)); identifier = originalRequestHeader.Identifier; // Update the date offset to point past the payload IP+ICMP headers. While the specification // doesn't indicate there should be any additional data the reality is that we often get the // original packet data back. dataOffset += payloadIpHeaderLength + IcmpHeaderLengthInBytes; } else { return(false); } } else { if (type == (byte)IcmpV6MessageType.EchoReply) { // Reply packet has the identifier in the ICMP header. identifier = receivedHeader.Identifier; } else if (type == (byte)IcmpV6MessageType.DestinationUnreachable || type == (byte)IcmpV6MessageType.TimeExceeded || type == (byte)IcmpV6MessageType.ParameterProblem || type == (byte)IcmpV6MessageType.PacketTooBig) { // Original IP+ICMP request is in the payload. Read the ICMP header from // the payload to get identifier. if (bytesReceived - dataOffset < IpV6HeaderLengthInBytes + IcmpHeaderLengthInBytes) { return(false); // Not enough bytes to reconstruct actual IP header + ICMP header. } IcmpHeader originalRequestHeader = MemoryMarshal.Read <IcmpHeader>(receiveBuffer.AsSpan(dataOffset + IpV6HeaderLengthInBytes)); identifier = originalRequestHeader.Identifier; // Update the date offset to point past the payload IP+ICMP headers. While the specification // doesn't indicate there should be any additional data the reality is that we often get the // original packet data back. dataOffset += IpV6HeaderLengthInBytes + IcmpHeaderLengthInBytes; } else { return(false); } } if (socketConfig.Identifier != identifier) { return(false); } sw.Stop(); long roundTripTime = sw.ElapsedMilliseconds; // We want to return a buffer with the actual data we sent out, not including the header data. byte[] dataBuffer = new byte[bytesReceived - dataOffset]; Buffer.BlockCopy(receiveBuffer, dataOffset, dataBuffer, 0, dataBuffer.Length); IPStatus status = socketConfig.IsIpv4 ? IcmpV4MessageConstants.MapV4TypeToIPStatus(type, code) : IcmpV6MessageConstants.MapV6TypeToIPStatus(type, code); IPAddress address = ((IPEndPoint)socketConfig.EndPoint).Address; reply = new PingReply(address, socketConfig.Options, status, roundTripTime, dataBuffer); return(true); }
/// <summary> /// This routine creates an instance of the IcmpHeader class from a byte /// array that is a received IGMP packet. This is useful when a packet /// is received from the network and the header object needs to be /// constructed from those values. /// </summary> /// <param name="icmpPacket">Byte array containing the binary ICMP header</param> /// <param name="bytesCopied">Number of bytes used in header</param> /// <returns>Returns the IcmpHeader object created from the byte array</returns> public static IcmpHeader Create(byte[] icmpPacket, ref int bytesCopied) { IcmpHeader icmpHeader = new IcmpHeader(); int offset = 0; // Make sure byte array is large enough to contain an ICMP header if (icmpPacket.Length < IcmpHeader.IcmpHeaderLength) return null; icmpHeader.icmpType = icmpPacket[offset++]; icmpHeader.icmpCode = icmpPacket[offset++]; icmpHeader.icmpChecksum = BitConverter.ToUInt16(icmpPacket, offset); offset += 2; icmpHeader.icmpId = BitConverter.ToUInt16(icmpPacket, offset); offset += 2; icmpHeader.icmpSequence = BitConverter.ToUInt16(icmpPacket, offset); bytesCopied = IcmpHeader.IcmpHeaderLength; return icmpHeader; }
public static void WireguardTest() { var protocol = new Protocol( HandshakePattern.IK, CipherFunction.ChaChaPoly, HashFunction.Blake2s, PatternModifiers.Psk2 ); var buffer = new byte[Protocol.MaxMessageLength]; var buffer2 = new byte[Protocol.MaxMessageLength]; int bufferRead = 0; using (var hs = protocol.Create(true, new ReadOnlySpan <byte>(Encoding.UTF8.GetBytes("WireGuard v1 zx2c4 [email protected]")), OurPrivate, TheirPublic, new byte[][] { Preshared })) { var now = DateTimeOffset.UtcNow; //replace with Noda.Time? var tai64n = new byte[12]; (4611686018427387914ul + (ulong)now.ToUnixTimeSeconds()).ToBigEndian(tai64n); ((uint)(now.Millisecond * 1e6)).ToBigEndian(tai64n, 8); var initiationPacket = new List <byte> { 1, 0, 0, 0 }; //type initiation initiationPacket.AddRange(((uint)28).ToLittleEndian()); //sender, random 4byte var(bytesWritten, _, _) = hs.WriteMessage(tai64n, buffer); initiationPacket.AddRange(buffer.Take(bytesWritten)); // should be 24byte, ephemeral, static, timestamp var hasher = Blake2s.CreateIncrementalHasher(32); var hashThis = Encoding.UTF8.GetBytes("mac1----").Concat(TheirPublic).ToArray(); hasher.Update(hashThis); var finishedHash = hasher.Finish(); hasher = Blake2s.CreateIncrementalHasher(16, finishedHash); hashThis = initiationPacket.ToArray(); hasher.Update(hashThis); finishedHash = hasher.Finish(); initiationPacket.AddRange(finishedHash); //mac1 initiationPacket.AddRange(Enumerable.Repeat((byte)0, 16)); //mac2 = zeros if no cookie last received var socket = new DatagramSocket(); var responsePacket = new TaskCompletionSource <int>(); var autoResetEvent = new AutoResetEvent(false); socket.MessageReceived += (sender, args) => { bufferRead = args.GetDataStream().AsStreamForRead().Read(buffer); autoResetEvent.Set(); }; socket.ConnectAsync(new HostName("demo.wireguard.com"), "12913").AsTask().Wait(); var streamWriter = new BinaryWriter(socket.OutputStream.AsStreamForWrite()); streamWriter.Write(initiationPacket.ToArray()); streamWriter.Flush(); var successful = autoResetEvent.WaitOne(5000); if (!successful) { return; } if (buffer[0] != 2) //type init response { return; //"response packet type wrong: want %d, got %d" } if (bufferRead != 92) //always this length! for type=2 { return; //"response packet too short: want %d, got %d" } if (buffer[1] != 0 || buffer[2] != 0 || buffer[3] != 0) { return; //"response packet has non-zero reserved fields" } var theirIndex = buffer.LittleEndianToUInt32(4); var ourIndex = buffer.LittleEndianToUInt32(8); if (ourIndex != 28) { return; //log.Fatalf("response packet index wrong: want %d, got %d", 28, ourIndex) } var span = new Span <byte>(buffer); var(bytesRead, handshakeHash, transport) = hs.ReadMessage(span.Slice(12, 48), span.Slice(100)); //write on same buffer behind the received package (which if (bytesRead != 0) { return; //"unexpected payload: %x" } var icmpHeader = new IcmpHeader() { Type = 8, Id = 921, Sequence = 438 }; var pingMessage = icmpHeader.GetProtocolPacketBytes(Encoding.UTF8.GetBytes("WireGuard")); var pingHeader = new Ipv4Header() { Version = 4, Length = 20, TotalLength = (ushort)(20 + pingMessage.Length), Protocol = 1, Ttl = 20, SourceAddress = new IPAddress(new byte[] { 10, 189, 129, 2 }), DestinationAddress = new IPAddress(new byte[] { 10, 189, 129, 1 }) }.GetProtocolPacketBytes(new byte[0]); span[0] = 4; span.Slice(1, 3).Assign((byte)0); theirIndex.ToLittleEndian(buffer, 4); 0L.ToLittleEndian(buffer, 8); //this is the counter, little endian u64 bytesWritten = transport.WriteMessage( pingHeader.Concat(pingMessage).Concat(Enumerable.Repeat((byte)0, 11)).ToArray(), //pad message with 0 to make mod 16=0 span.Slice(16)); //using (var streamWriter = new BinaryWriter(socket.OutputStream.AsStreamForWrite())) streamWriter.Write(span.Slice(0, 16 + bytesWritten).ToArray()); streamWriter.Flush(); successful = autoResetEvent.WaitOne(5000); if (!successful) { return; } if (buffer[0] != 4) { return;//"response packet type wrong: want %d, got %d" } if (buffer[1] != 0 || buffer[2] != 0 || buffer[3] != 0) { return; //"response packet has non-zero reserved fields" } var replyPacket = buffer2.AsSpan(0, transport.ReadMessage(span.Slice(16, bufferRead - 16), buffer2)); if (replyPacket.Length != 48) { return; } var replyHeaderLen = ((int)(replyPacket[0] & 0x0f)) << 2; var replyLen = buffer2.BigEndianToUInt16(2); var our_index_received = buffer.LittleEndianToUInt32(4); if (our_index_received != 28) { return; } var nonce = buffer2.LittleEndianToUInt64(8); //if (nonce != 0)//not parsed correctly? // return; var replyMessage = IcmpHeader.Create(buffer2.AsSpan(replyHeaderLen, replyLen - replyHeaderLen).ToArray(), ref bytesRead); if (replyMessage.Type != 0 || replyMessage.Code != 0) { return; } if (replyMessage.Id != 921 || replyMessage.Sequence != 438) { return; } var replyPayload = Encoding.UTF8.GetString(buffer2.AsSpan(replyLen - replyHeaderLen + bytesRead, replyHeaderLen - bytesRead)); if (replyPayload != "WireGuard") //trim necessary? { return; } } }
/// <summary> /// This is the asynchronous callback that is fired when an async ReceiveFrom. /// An asynchronous ReceiveFrom is posted by calling BeginReceiveFrom. When this /// function is invoked, it calculates the elapsed time between when the ping /// packet was sent and when it was completed. /// </summary> /// <param name="ar">Asynchronous context for operation that completed</param> static void PingReceiveCallback(IAsyncResult ar) { RawSocketPing rawSock = (RawSocketPing)ar.AsyncState; TimeSpan elapsedTime; int bytesReceived = 0; ushort receivedId = 0; try { // Keep a count of how many async operations are outstanding -- one just completed // so decrement the count. Interlocked.Decrement(ref rawSock.pingOutstandingReceives); // If we're done because ping is exiting and the socket has been closed, // set the done event if (rawSock.pingSocket == null) { if (rawSock.pingOutstandingReceives == 0) { rawSock.pingDoneEvent.Set(); } return; } // Complete the receive op by calling EndReceiveFrom. This will return the number // of bytes received as well as the source address of who sent this packet. bytesReceived = rawSock.pingSocket.EndReceiveFrom(ar, ref rawSock.castResponseEndPoint); // Calculate the elapsed time from when the ping request was sent and a response was // received. elapsedTime = DateTime.Now - rawSock.pingSentTime; rawSock.responseEndPoint = (IPEndPoint)rawSock.castResponseEndPoint; // Here we unwrap the data received back into the respective protocol headers such // that we can find the ICMP ID in the ICMP or ICMPv6 packet to verify that // the echo response we received was really a response to our request. if (rawSock.pingSocket.AddressFamily == AddressFamily.InterNetwork) { Ipv4Header v4Header; IcmpHeader icmpv4Header; byte[] pktIcmp; int offset = 0; // Remember, raw IPv4 sockets will return the IPv4 header along with all // subsequent protocol headers v4Header = Ipv4Header.Create(rawSock.receiveBuffer, ref offset); pktIcmp = new byte[bytesReceived - offset]; Array.Copy(rawSock.receiveBuffer, offset, pktIcmp, 0, pktIcmp.Length); icmpv4Header = IcmpHeader.Create(pktIcmp, ref offset); /*Console.WriteLine("Icmp.Id = {0}; Icmp.Sequence = {1}", * icmpv4Header.Id, * icmpv4Header.Sequence * );*/ receivedId = icmpv4Header.Id; } else if (rawSock.pingSocket.AddressFamily == AddressFamily.InterNetworkV6) { Icmpv6Header icmp6Header; Icmpv6EchoRequest echoHeader; byte[] pktEchoRequest; int offset = 0; // For IPv6 raw sockets, the IPv6 header is never returned along with the // data received -- the received data always starts with the header // following the IPv6 header. icmp6Header = Icmpv6Header.Create(rawSock.receiveBuffer, ref offset); pktEchoRequest = new byte[bytesReceived - offset]; Array.Copy(rawSock.receiveBuffer, offset, pktEchoRequest, 0, pktEchoRequest.Length); echoHeader = Icmpv6EchoRequest.Create(pktEchoRequest, ref offset); /*Console.WriteLine("Icmpv6.Id = {0}; Icmp.Sequence = {1}", * echoHeader.Id, * echoHeader.Sequence * );*/ receivedId = echoHeader.Id; } if (receivedId == rawSock.pingId) { string elapsedString; // Print out the usual statistics for ping if (elapsedTime.Milliseconds < 1) { elapsedString = "<1"; } else { elapsedString = "=" + elapsedTime.Milliseconds.ToString(); } Console.WriteLine("Reply from {0}: byte={1} time{2}ms TTL={3} ", rawSock.responseEndPoint.Address.ToString(), bytesReceived, elapsedString, rawSock.pingTtl ); } // Post another async receive if the count indicates for us to do so. if (rawSock.pingCount > 0) { rawSock.pingSocket.BeginReceiveFrom( rawSock.receiveBuffer, 0, rawSock.receiveBuffer.Length, SocketFlags.None, ref rawSock.castResponseEndPoint, rawSock.receiveCallback, rawSock ); // Keep track of outstanding async operations Interlocked.Increment(ref rawSock.pingOutstandingReceives); } else { // If we're done then set the done event if (rawSock.pingOutstandingReceives == 0) { rawSock.pingDoneEvent.Set(); } } // If this is indeed the response to our echo request then signal the main thread // that we received the response so it can send additional echo requests if // necessary. This is done after another async ReceiveFrom is already posted. if (receivedId == rawSock.pingId) { rawSock.pingReceiveEvent.Set(); } } catch (SocketException err) { Console.WriteLine("Socket error occurred in async callback: {0}", err.Message); } }
/// <summary> /// This function parses the incoming packets and extracts the data based upon /// the protocol being carried by the IP datagram. /// </summary> /// <param name="byteData">Incoming bytes</param> /// <param name="nReceived">The number of bytes received</param> private IPDisplayPacket GetDisplayPacket(IpV4Header ipHeader) { IPDisplayPacket retVal; // Since all protocol packets are encapsulated in the IP datagram // so we start by parsing the IP header and see what protocol data // is being carried by it. // Now according to the protocol being carried by the IP datagram we parse // the data field of the datagram. switch (ipHeader.ProtocolType) { case Protocol.TCP: { TcpHeader tcpHeader = new TcpHeader(ipHeader.Data, ipHeader.MessageLength); retVal = new TCPDisplayPacket(ipHeader, tcpHeader); } break; case Protocol.UDP: { UdpHeader udpHeader = new UdpHeader(ipHeader.Data, (int)ipHeader.MessageLength); retVal = new UDPDisplayPacket(ipHeader, udpHeader); } break; case Protocol.ICMP: { IcmpHeader icmpHeader = new IcmpHeader(ipHeader.Data, (int)ipHeader.MessageLength); retVal = new IcmpDisplayPacket(ipHeader, icmpHeader); } break; case Protocol.IGMP: { IgmpHeader igmpHeader = new IgmpHeader(ipHeader.Data, (int)ipHeader.MessageLength); retVal = new IgmpDisplayPacket(ipHeader, igmpHeader); } break; case Protocol.DCCP: { DCCPHeader icmpHeader = new DCCPHeader(ipHeader.Data, (int)ipHeader.MessageLength); retVal = new DCCPDisplayPacket(ipHeader, icmpHeader); } break; case Protocol.EIGRP: { EIGRPHeader icmpHeader = new EIGRPHeader(ipHeader.Data, (int)ipHeader.MessageLength); retVal = new EIGRPDisplayPacket(ipHeader, icmpHeader); } break; case Protocol.GREs: { GREHeader icmpHeader = new GREHeader(ipHeader.Data, (int)ipHeader.MessageLength); retVal = new GREDisplayPacket(ipHeader, icmpHeader); } break; case Protocol.OSPF: { OSPFHeader icmpHeader = new OSPFHeader(ipHeader.Data, (int)ipHeader.MessageLength); retVal = new OSPFDisplayPacket(ipHeader, icmpHeader); } break; default: case Protocol.Unknown: retVal = new IPDisplayPacket(ipHeader); break; } return(retVal); }