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