From d1b97c8d9c52a19997bb637c1a9456588d4c396d Mon Sep 17 00:00:00 2001 From: xljiulang <366193849@qq.com> Date: Fri, 28 Oct 2022 21:02:54 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=A0=B7=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App/FastPinger.cs | 247 ------------------------- App/Icmp/FastPinger.cs | 175 ++++++++++++++++++ App/Icmp/PingPacket.cs | 80 ++++++++ App/Icmp/RouteTracer.cs | 149 +++++++++++++++ WindivertDotnet/WindivertDotnet.csproj | 2 +- 5 files changed, 405 insertions(+), 248 deletions(-) delete mode 100644 App/FastPinger.cs create mode 100644 App/Icmp/FastPinger.cs create mode 100644 App/Icmp/PingPacket.cs create mode 100644 App/Icmp/RouteTracer.cs diff --git a/App/FastPinger.cs b/App/FastPinger.cs deleted file mode 100644 index af772e1..0000000 --- a/App/FastPinger.cs +++ /dev/null @@ -1,247 +0,0 @@ -using System; -using System.Buffers.Binary; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Net; -using System.Net.Sockets; -using System.Threading; -using System.Threading.Tasks; -using WindivertDotnet; - -namespace App -{ - class FastPinger : IDisposable - { - private readonly WinDivert divert; - private readonly IdSeqNum idSeqNum = new(); - - public FastPinger() - { - // 只接受进入系统的icmp - var filter = Filter.True.And(f => (f.IsIcmp || f.IsIcmpV6) && f.Network.Inbound); - this.divert = new WinDivert(filter, WinDivertLayer.Network); - } - - /// - /// Ping所有地址 - /// 占用两个线程 - /// - /// 开始地址 - /// IP数量 - /// 最后一个IP发出ping之后的等待回复时长 - /// - public Task PingAllAsync(IPAddress startAddr, int count, TimeSpan delay) - { - var dstAddrs = CreateAddrs(startAddr, count); - return this.PingAllAsync(dstAddrs, delay); - } - - /// - /// 创建IP列表 - /// - /// - /// - /// - private static IEnumerable CreateAddrs(IPAddress startAddr, int count) - { - var bytes = startAddr.GetAddressBytes(); - var start = BinaryPrimitives.ReadUInt32BigEndian(bytes.AsSpan(bytes.Length - 4)); - - for (var i = 0; i < count; i++) - { - var value = (uint)(start + i); - BinaryPrimitives.WriteUInt32BigEndian(bytes.AsSpan(bytes.Length - 4), value); - yield return new IPAddress(bytes); - } - } - - /// - /// Ping所有地址 - /// 占用两个线程 - /// - /// 目标地址 - /// 最后一个IP发出ping之后的等待回复时长 - /// - public async Task PingAllAsync(IEnumerable dstAddrs, TimeSpan delay) - { - // 开始监听ping的回复 - using var cts = new CancellationTokenSource(); - var recvTask = this.RecvEchoReplyAsync(cts.Token); - - // 对所有ip发ping - await this.SendEchoRequestAsync(dstAddrs); - - // 延时取消监听 - cts.CancelAfter(delay); - var results = await recvTask; - - // 清洗数据 - return results.Intersect(dstAddrs).ToArray(); - } - - - /// - /// 监听ping的回复 - /// - /// 取消令牌 - /// - private async Task> RecvEchoReplyAsync(CancellationToken cancellationToken) - { - var results = new HashSet(); - using var packet = new WinDivertPacket(); - using var addr = new WinDivertAddress(); - - while (cancellationToken.IsCancellationRequested == false) - { - try - { - await this.divert.RecvAsync(packet, addr, cancellationToken); - if (TryGetEchoReplyAddr(packet, out var value)) - { - results.Add(value); - } - // 把packet发出,避免系统其它软件此刻也有ping而收不到回复 - await this.divert.SendAsync(packet, addr, cancellationToken); - } - catch (OperationCanceledException) - { - break; - } - } - return results; - } - - - /// - /// 解析出icmp回复信息 - /// - /// 数据包 - /// 回复的IP - /// - private unsafe static bool TryGetEchoReplyAddr(WinDivertPacket packet, [MaybeNullWhen(false)] out IPAddress value) - { - var result = packet.GetParseResult(); - if (result.IcmpV4Header != null && - result.IcmpV4Header->Type == IcmpV4MessageType.EchoReply) - { - value = result.IPV4Header->SrcAddr; - return true; - } - else if (result.IcmpV6Header != null && - result.IcmpV6Header->Type == IcmpV6MessageType.EchoReply) - { - value = result.IPV6Header->SrcAddr; - return true; - } - - value = null; - return false; - } - - /// - /// 发送icmp的echo请求包 - /// - /// - /// - private async Task SendEchoRequestAsync(IEnumerable dstAddrs) - { - foreach (var address in dstAddrs) - { - // 使用router计算将进行通讯的本机地址 - var router = new WinDivertRouter(address); - using var addr = router.CreateAddress(); - using var packet = address.AddressFamily == AddressFamily.InterNetwork - ? CreateIPV4EchoPacket(router.SrcAddress, router.DstAddress) - : CreateIPV6EchoPacket(router.SrcAddress, router.DstAddress); - - packet.CalcChecksums(addr); // 计算checksums,因为创建包时没有计算 - - await this.divert.SendAsync(packet, addr); - } - } - - /// - /// 创建icmp的echo包 - /// - /// - /// - /// - private unsafe WinDivertPacket CreateIPV4EchoPacket(IPAddress srcAddr, IPAddress dstAddr) - { - // ipv4头 - var ipHeader = new IPV4Header - { - TTL = 128, - Version = IPVersion.V4, - DstAddr = dstAddr, - SrcAddr = srcAddr, - Protocol = ProtocolType.Icmp, - HdrLength = (byte)(sizeof(IPV4Header) / 4), - Id = IdSeqNum.Shared.NextUInt16(), - Length = (ushort)(sizeof(IPV4Header) + sizeof(IcmpV4Header)) - }; - - // icmp头 - var icmpHeader = new IcmpV4Header - { - Type = IcmpV4MessageType.EchoRequest, - Code = default, - Identifier = this.idSeqNum.NextUInt16(), - SequenceNumber = this.idSeqNum.NextUInt16(), - }; - - // 将数据写到packet缓冲区 - var packet = new WinDivertPacket(ipHeader.Length); - - var writer = packet.GetWriter(); - writer.Write(ipHeader); - writer.Write(icmpHeader); - - return packet; - } - - /// - /// 创建icmpv6的echo包 - /// - /// - /// - /// - private unsafe WinDivertPacket CreateIPV6EchoPacket(IPAddress srcAddr, IPAddress dstAddr) - { - // ipv6头 - var ipHeader = new IPV6Header - { - DstAddr = dstAddr, - SrcAddr = srcAddr, - Length = (ushort)(sizeof(IcmpV6Header)), - Version = IPVersion.V6, - NextHdr = ProtocolType.IcmpV6, - HopLimit = 128 - }; - - // icmpv6头 - var icmpHeader = new IcmpV6Header - { - Type = IcmpV6MessageType.EchoRequest, - Code = default, - Identifier = this.idSeqNum.NextUInt16(), - SequenceNumber = this.idSeqNum.NextUInt16(), - }; - - // 将数据写到packet缓冲区 - var packet = new WinDivertPacket(sizeof(IPV6Header) + ipHeader.Length); - - var writer = packet.GetWriter(); - writer.Write(ipHeader); - writer.Write(icmpHeader); - - return packet; - } - - public void Dispose() - { - this.divert.Dispose(); - } - } -} diff --git a/App/Icmp/FastPinger.cs b/App/Icmp/FastPinger.cs new file mode 100644 index 0000000..07eeedf --- /dev/null +++ b/App/Icmp/FastPinger.cs @@ -0,0 +1,175 @@ +using System; +using System.Buffers.Binary; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using WindivertDotnet; + +namespace App.Icmp +{ + + /// + /// 快速ping工具 + /// + static class FastPinger + { + private record SrcAddrSeqNum(IPAddress SrcAddress, ushort SeqNum); + + /// + /// Ping所有地址 + /// + /// 开始地址 + /// IP数量 + /// 最后一个IP发出ping之后的等待回复时长 + /// + /// + public static Task PingAllAsync(IPAddress startAddr, int count, TimeSpan waitTime, CancellationToken cancellationToken = default) + { + var dstAddrs = CreateAddrs(startAddr, count); + return PingAllAsync(dstAddrs, waitTime, cancellationToken); + } + + /// + /// 创建IP列表 + /// + /// + /// + /// + private static IEnumerable CreateAddrs(IPAddress startAddr, int count) + { + var bytes = startAddr.GetAddressBytes(); + var start = BinaryPrimitives.ReadUInt32BigEndian(bytes.AsSpan(bytes.Length - 4)); + + for (var i = 0; i < count; i++) + { + var value = (uint)(start + i); + BinaryPrimitives.WriteUInt32BigEndian(bytes.AsSpan(bytes.Length - 4), value); + yield return new IPAddress(bytes); + } + } + + /// + /// Ping所有地址 + /// + /// 目标地址 + /// 最后一个IP发出ping之后的等待回复时长 + /// + /// + public static async Task PingAllAsync(IEnumerable dstAddrs, TimeSpan waitTime, CancellationToken cancellationToken = default) + { + var filter = Filter.True + .And(f => f.Network.Inbound) + .And(f => f.ICmp.Type == IcmpV4MessageType.EchoReply || f.IcmpV6.Type == IcmpV6MessageType.EchoReply); + + using var divert = new WinDivert(filter, WinDivertLayer.Network); + + // 开始监听ping的回复 + using var waitTokenSource = new CancellationTokenSource(); + using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(waitTokenSource.Token, cancellationToken); + var recvTask = RecvRepliesAsync(divert, linkedTokenSource.Token); + var seqNums = await SendPingsAsync(divert, dstAddrs, cancellationToken); + + // 延时取消监听 + waitTokenSource.CancelAfter(waitTime); + var results = await recvTask; + + return results + .Where(item => seqNums.Contains(item.SeqNum)) + .Select(item => item.SrcAddress) + .Distinct() + .ToArray(); + } + + + /// + /// 接收回复信息 + /// + /// + /// + /// + private async static Task> RecvRepliesAsync(WinDivert divert, CancellationToken cancellationToken) + { + var results = new List(); + using var packet = new WinDivertPacket(); + using var addr = new WinDivertAddress(); + + while (cancellationToken.IsCancellationRequested == false) + { + try + { + await divert.RecvAsync(packet, addr, cancellationToken); + + if (TryParseReply(packet, out var srcAddrSeqNum)) + { + results.Add(srcAddrSeqNum); + } + + await divert.SendAsync(packet, addr, cancellationToken); + } + catch (OperationCanceledException) + { + break; + } + } + + return results; + } + + + /// + /// 解析出icmp回复信息 + /// + /// + /// + /// + private unsafe static bool TryParseReply(WinDivertPacket packet, [MaybeNullWhen(false)] out SrcAddrSeqNum srcAddrSeqNum) + { + var result = packet.GetParseResult(); + var srcAddr = result.IPV4Header != null + ? result.IPV4Header->SrcAddr + : result.IPV6Header->SrcAddr; + + if (result.IcmpV4Header != null) + { + srcAddrSeqNum = new SrcAddrSeqNum(srcAddr, result.IcmpV4Header->SequenceNumber); + return true; + } + + if (result.IcmpV6Header != null) + { + srcAddrSeqNum = new SrcAddrSeqNum(srcAddr, result.IcmpV6Header->SequenceNumber); + return true; + } + + srcAddrSeqNum = null; + return false; + } + + /// + /// 发送ping请求 + /// + /// + /// + /// + /// + private static async Task> SendPingsAsync(WinDivert divert, IEnumerable dstAddrs, CancellationToken cancellationToken) + { + var seqNums = new HashSet(); + foreach (var address in dstAddrs) + { + // 使用router计算将进行通讯的本机地址 + var router = new WinDivertRouter(address); + using var addr = router.CreateAddress(); + + using var packet = new PingPacket(router); + packet.CalcChecksums(addr); + await divert.SendAsync(packet, addr, cancellationToken); + seqNums.Add(packet.SeqNum); + } + return seqNums; + } + } +} diff --git a/App/Icmp/PingPacket.cs b/App/Icmp/PingPacket.cs new file mode 100644 index 0000000..8a812db --- /dev/null +++ b/App/Icmp/PingPacket.cs @@ -0,0 +1,80 @@ +using System.Net.Sockets; +using WindivertDotnet; + +namespace App.Icmp +{ + /// + /// ping包 + /// + unsafe class PingPacket : WinDivertPacket + { + private static readonly IdSeqNum id = new(); + private static readonly IdSeqNum seqNum = new(); + private static readonly ushort v4PacketLength = (ushort)(sizeof(IPV4Header) + sizeof(IcmpV4Header) + 32); + private static readonly ushort v6PacketLength = (ushort)(sizeof(IPV6Header) + sizeof(IcmpV6Header) + 32); + + public ushort Id { get; } = id.NextUInt16(); + + public ushort SeqNum { get; } = seqNum.NextUInt16(); + + /// + /// ping包 + /// + /// + /// + public PingPacket(WinDivertRouter router, byte ttl = 128) + : base(v6PacketLength) + { + if (router.DstAddress.AddressFamily == AddressFamily.InterNetwork) + { + var ipHeader = new IPV4Header + { + TTL = ttl, + Version = IPVersion.V4, + DstAddr = router.DstAddress, + SrcAddr = router.SrcAddress, + Protocol = ProtocolType.Icmp, + HdrLength = 5, + Id = IdSeqNum.Shared.NextUInt16(), + Length = v4PacketLength + }; + + var icmpHeader = new IcmpV4Header + { + Type = IcmpV4MessageType.EchoRequest, + Identifier = Id, + SequenceNumber = SeqNum, + }; + + var writer = GetWriter(); + writer.Write(ipHeader); + writer.Write(icmpHeader); + writer.Advance(32); + } + else + { + var ipHeader = new IPV6Header + { + SrcAddr = router.SrcAddress, + DstAddr = router.DstAddress, + Length = v6PacketLength, + HopLimit = ttl, + NextHdr = ProtocolType.IcmpV6, + Version = IPVersion.V6 + }; + var icmpHeader = new IcmpV6Header + { + Type = IcmpV6MessageType.EchoRequest, + Identifier = Id, + SequenceNumber = SeqNum + }; + + var writer = GetWriter(); + writer.Write(ipHeader); + writer.Write(icmpHeader); + writer.Advance(32); + } + } + + } +} diff --git a/App/Icmp/RouteTracer.cs b/App/Icmp/RouteTracer.cs new file mode 100644 index 0000000..439b304 --- /dev/null +++ b/App/Icmp/RouteTracer.cs @@ -0,0 +1,149 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Net; +using System.Net.NetworkInformation; +using System.Threading; +using System.Threading.Tasks; +using WindivertDotnet; + +namespace App.Icmp +{ + /// + /// 路由信息 + /// + static class RouteTracer + { + private record SrcAddrSeqNum(IPAddress SrcAddress, ushort SeqNum); + + /// + /// 测试路由 + /// + /// + /// + /// + /// + /// + /// + public async static Task TraceAsync(IPAddress dstAddr, byte ttlCount, TimeSpan waitTime, CancellationToken cancellationToken = default) + { + var filter = Filter.True + .And(f => f.Network.Inbound) + .And(f => f.ICmp.Type == IcmpV4MessageType.TimeExceeded || f.IcmpV6.Type == IcmpV6MessageType.TimeExceeded); + + using var divert = new WinDivert(filter, WinDivertLayer.Network); + + // 开始监听ping的回复 + using var waitTokenSource = new CancellationTokenSource(); + using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(waitTokenSource.Token, cancellationToken); + var recvTask = RecvRepliesAsync(divert, linkedTokenSource.Token); + + // 开始ttl发ping + var ttls = Enumerable.Range(1, ttlCount).Select(i => (byte)i); + var seqNums = await SendPingsAsync(divert, dstAddr, ttls, cancellationToken); + + // 延时取消监听 + waitTokenSource.CancelAfter(waitTime); + var results = await recvTask; + + return results + .Where(item => seqNums.Contains(item.SeqNum)) + .Select(item => item.SrcAddress) + .Distinct() + .ToArray(); + } + + /// + /// 接收回复信息 + /// + /// + /// + /// + private async static Task> RecvRepliesAsync(WinDivert divert, CancellationToken cancellationToken) + { + var results = new List(); + using var packet = new WinDivertPacket(); + using var addr = new WinDivertAddress(); + + while (cancellationToken.IsCancellationRequested == false) + { + try + { + await divert.RecvAsync(packet, addr, cancellationToken); + + if (TryParseReply(packet, out var srcAddrSeqNum)) + { + results.Add(srcAddrSeqNum); + } + + await divert.SendAsync(packet, addr, cancellationToken); + } + catch (OperationCanceledException) + { + break; + } + } + + return results; + } + + /// + /// 解析出icmp回复信息 + /// + /// + /// + /// + private unsafe static bool TryParseReply(WinDivertPacket packet, [MaybeNullWhen(false)] out SrcAddrSeqNum srcAddrSeqNum) + { + var result = packet.GetParseResult(); + if (result.DataLength > 0) + { + var srcAddr = result.IPV4Header != null + ? result.IPV4Header->SrcAddr + : result.IPV6Header->SrcAddr; + + var dataPacket = packet.Slice(packet.Length - result.DataLength, result.DataLength); + dataPacket.ApplyLengthToHeaders(); + + var dataResult = dataPacket.GetParseResult(); + if (dataResult.IcmpV4Header != null) + { + srcAddrSeqNum = new SrcAddrSeqNum(srcAddr, dataResult.IcmpV4Header->SequenceNumber); + return true; + } + if (dataResult.IcmpV6Header != null) + { + srcAddrSeqNum = new SrcAddrSeqNum(srcAddr, dataResult.IcmpV6Header->SequenceNumber); + return true; + } + } + + srcAddrSeqNum = null; + return false; + } + + /// + /// 发送ping请求包 + /// + /// + /// + /// + /// + /// + private async static Task> SendPingsAsync(WinDivert divert, IPAddress dstAddr, IEnumerable ttls, CancellationToken cancellationToken) + { + var router = new WinDivertRouter(dstAddr); + using var addr = router.CreateAddress(); + var result = new HashSet(); + foreach (var ttl in ttls) + { + using var packet = new PingPacket(router, ttl); + packet.CalcChecksums(addr); + await divert.SendAsync(packet, addr, cancellationToken); + result.Add(packet.SeqNum); + } + return result; + } + } +} diff --git a/WindivertDotnet/WindivertDotnet.csproj b/WindivertDotnet/WindivertDotnet.csproj index d3c2621..06cedc3 100644 --- a/WindivertDotnet/WindivertDotnet.csproj +++ b/WindivertDotnet/WindivertDotnet.csproj @@ -1,7 +1,7 @@  - 1.0.9 + 1.1.0 netcoreapp3.1;net6.0 enable CA2012;IDE0079