public async Task Run_Log(bool modify, bool drop)
        {
            var outbound = (bool?)null;

            if (modify)
            {
                outbound = true;
            }
            var logger = new StringWriter();

            using var udp1 = new UdpClient(new IPEndPoint(IPAddress.Loopback, port1));
            udp1.Connect(IPAddress.Loopback, port2);

            using var udp2 = new UdpClient(new IPEndPoint(IPAddress.Loopback, port2));
            udp2.Connect(IPAddress.Loopback, port1);

            var nat = new WinDivertNAT.WinDivertNAT($"udp.SrcPort == {port1} and udp.DstPort == {port2} and loopback")
            {
                Outbound = outbound,
                Drop     = drop,
                Logger   = logger,
            };

            using var cancel = new CancellationTokenSource();
            using var task   = Task.Run(() => nat.Run(cancel.Token));
            await Task.Delay(250);

            _ = udp1.Send(new byte[1], 1);
            await Task.Delay(250);

            cancel.Cancel();
            _ = await Assert.ThrowsExceptionAsync <OperationCanceledException>(() => task);

            Assert.IsTrue(logger.GetStringBuilder().Length > 0);
        }
        public async Task Run_WaitForCancellation(bool modify, bool drop, bool log)
        {
            var outbound = (bool?)null;

            if (modify)
            {
                outbound = true;
            }
            var logger = (TextWriter?)null;

            if (log)
            {
                logger = new StringWriter();
            }

            var nat = new WinDivertNAT.WinDivertNAT("false")
            {
                Outbound = outbound,
                Drop     = drop,
                Logger   = logger,
            };

            using var cancel = new CancellationTokenSource();
            using var task   = Task.Run(() => nat.Run(cancel.Token));
            Assert.IsTrue(await Task.WhenAny(task, Task.Delay(250)) != task);
            cancel.Cancel();
            _ = await Assert.ThrowsExceptionAsync <OperationCanceledException>(() => task);
        }
        public async Task Run_Drop(bool modify, bool log)
        {
            var outbound = (bool?)null;

            if (modify)
            {
                outbound = true;
            }
            var logger = (TextWriter?)null;

            if (log)
            {
                logger = new StringWriter();
            }

            using var udp1 = new UdpClient(new IPEndPoint(IPAddress.Loopback, port1));
            udp1.Connect(IPAddress.Loopback, port2);

            using var udp2 = new UdpClient(new IPEndPoint(IPAddress.Loopback, port2));
            udp2.Connect(IPAddress.Loopback, port1);
            udp2.Client.ReceiveTimeout = 250;

            var nat = new WinDivertNAT.WinDivertNAT($"udp.SrcPort == {port1} and udp.DstPort == {port2} and loopback")
            {
                Outbound = outbound,
                Drop     = true,
                Logger   = logger,
            };

            using var cancel = new CancellationTokenSource();
            using var task   = Task.Run(() => nat.Run(cancel.Token));
            await Task.Delay(250);

            var remoteEP = new IPEndPoint(IPAddress.Any, 0);

            _ = udp1.Send(new byte[1], 1);
            var e = Assert.ThrowsException <SocketException>(() => udp2.Receive(ref remoteEP));

            Assert.AreEqual(SocketError.TimedOut, e.SocketErrorCode);

            cancel.Cancel();
            _ = await Assert.ThrowsExceptionAsync <OperationCanceledException>(() => task);
        }