private async Task DnsReply(ushort transactionId, List <string> labels, ushort id, ushort sourcePort)
        {
            var answerDnsPacket = new DnsPacket(transactionId, 0, 0, new List <DnsPacket.ResourceRecord>
            {
                new DnsPacket.ResourceRecord
                {
                    Class  = 0x0001,
                    Type   = 0x0001,
                    Labels = labels
                }
            }, new List <DnsPacket.ResourceRecord>
            {
                new DnsPacket.ResourceRecord
                {
                    Class  = 0x0001,
                    Type   = 0x0001,
                    TTL    = 30 * 60,
                    Labels = labels,
                    Data   = GetIpFromId(id).GetAddressBytes()
                }
            },
                                                new List <DnsPacket.ResourceRecord>(),
                                                new List <DnsPacket.ResourceRecord>(), DnsPacket.DnsFlags.IsAutorative | DnsPacket.DnsFlags.IsResponse);

            var answerIpV4Packet = new IPv4Packet(GetIpFromId(-1), GetIpFromId(selfId));

            answerIpV4Packet.SetPayloadPacket(new UdpPacket(53, sourcePort,
                                                            answerDnsPacket, answerIpV4Packet));
            var answerEthernetPacket = new EthernetPacket(GetPhysicalAddressFromId(-1),
                                                          GetPhysicalAddressFromId(selfId), answerIpV4Packet, EthernetPacket.PacketType.IpV4);
            var answerData = answerEthernetPacket.ToBytes();
            await tapStream.WriteAsync(answerData, 0, answerData.Length);
        }
        public async Task StartAsyncReadData(CancellationToken cancellationToken)
        {
            logger.Info("TAP started");
            while (!cancellationToken.IsCancellationRequested)
            {
                try
                {
                    var buffer = new byte[4096];

                    var readBytes = await tapStream.ReadAsync(buffer, 0, 4096, cancellationToken);

                    if (readBytes <= 0)
                    {
                        continue;
                    }
                    var p = EthernetPacket.Parse(buffer.Take(readBytes).ToArray());
                    if (p.DestinationAddress.GetAddressBytes().Take(3).SequenceEqual(new byte[] { 0x01, 0x00, 0x5E }))
                    {
                        continue;
                    }
                    // ReSharper disable once SwitchStatementMissingSomeCases
                    switch (p.Type)
                    {
                    case EthernetPacket.PacketType.IpV4:
                    {
                        var intId = GetIdFromPhysicalAddress(p.DestinationAddress);
                        if (intId >= 0)
                        {
                            if (HostExists((ushort)intId) || intId == selfId)
                            {
                                await Send(p.Payload, (ushort)intId);
                            }
                            continue;
                        }

                        var ipPacket = (IPv4Packet)p.PayloadPacket;

                        switch (intId)
                        {
                        case -ipMacPoolShift:
                        {
                            if (ipPacket.PayloadPacket is UdpPacket udpPacket &&
                                udpPacket.PayloadPacket is DhcpPacket dhcpPacket)
                            {
                                if (dhcpPacket.Op != 1)
                                {
                                    continue;
                                }

                                var dhcpMessageType =
                                    dhcpPacket.Options.ContainsKey(53) &&
                                    dhcpPacket.Options[53].Length > 0
                                                        ? dhcpPacket.Options[53][0]
                                                        : -1;
                                DhcpPacket answerDhcpPacket;

                                switch (dhcpMessageType)
                                {
                                case 1:                     // DHCPDISCOVER
                                    answerDhcpPacket = new DhcpPacket
                                    {
                                        Xid                   = dhcpPacket.Xid,
                                        YourIpAddress         = GetIpFromId(selfId),
                                        ServerIpAddress       = GetIpFromId(-1),
                                        ClientHardwareAddress = dhcpPacket.ClientHardwareAddress,
                                        Options               =
                                            new Dictionary <byte, byte[]>
                                        {
                                            { 53, new byte[] { 2 } },
                                            { 1, new byte[] { 255, 255, 0, 0 } },
                                            {
                                                51,
                                                BitConverter.GetBytes(30 * 60).Reverse().ToArray()
                                            },
                                            { 54, GetIpFromId(-1).GetAddressBytes() },
                                            { 6, GetIpFromId(-1).GetAddressBytes() }
                                        }
                                    };
                                    break;

                                case 3:                     // DHCPREQUEST
                                    answerDhcpPacket = new DhcpPacket
                                    {
                                        Xid                   = dhcpPacket.Xid,
                                        YourIpAddress         = GetIpFromId(selfId),
                                        ServerIpAddress       = GetIpFromId(-1),
                                        ClientHardwareAddress = dhcpPacket.ClientHardwareAddress,
                                        Options               =
                                            new Dictionary <byte, byte[]>
                                        {
                                            { 53, new byte[] { 5 } },
                                            { 1, new byte[] { 255, 255, 0, 0 } },
                                            {
                                                51,
                                                BitConverter.GetBytes(30 * 60).Reverse().ToArray()
                                            },
                                            { 54, GetIpFromId(-1).GetAddressBytes() },
                                            { 6, GetIpFromId(-1).GetAddressBytes() }
                                        }
                                    };
                                    break;

                                default:
                                    continue;
                                }

                                var answerIpV4Packet = new IPv4Packet(GetIpFromId(-1),
                                                                      IPAddress.Broadcast);
                                answerIpV4Packet.SetPayloadPacket(new UdpPacket(67, 68,
                                                                                answerDhcpPacket, answerIpV4Packet));
                                var answerEthernetPacket = new EthernetPacket(
                                    GetPhysicalAddressFromId(-1),
                                    p.SourceAddress, answerIpV4Packet, EthernetPacket.PacketType.IpV4);
                                var answerData = answerEthernetPacket.ToBytes();
                                await tapStream.WriteAsync(answerData, 0, answerData.Length,
                                                           cancellationToken);

                                continue;
                            }

                            await Broadcast(p.Payload);
                        }
                            continue;

                        case -1:
                        {
                            if (ipPacket.PayloadPacket is UdpPacket udpPacket &&
                                udpPacket.PayloadPacket is DnsPacket dnsPacket)
                            {
                                if (dnsPacket.Queries.Count == 1 && dnsPacket.Queries[0].Type == 1 &&
                                    dnsPacket.Queries[0].Class == 1 && DomainNameUtil.GetName(string.Join(".", dnsPacket.Queries[0].Labels), dnsFormat) != null)
                                {
                                    var name = DomainNameUtil.GetName(string.Join(".", dnsPacket.Queries[0].Labels), dnsFormat);
                                    if (string.IsNullOrEmpty(name))
                                    {
                                        continue;
                                    }
                                    if (name == selfName)
                                    {
                                        await DnsReply(dnsPacket.TransactionId, dnsPacket.Queries[0].Labels, selfId, udpPacket.SourcePort);
                                    }
                                    else
                                    {
                                        var clientId = GetNodes().FirstOrDefault(x =>
                                                                                 x.GetDnmpNodeData().DomainName == name)?.Id;
                                        if (clientId != null)
                                        {
                                            await DnsReply(dnsPacket.TransactionId, dnsPacket.Queries[0].Labels, clientId.Value, udpPacket.SourcePort);
                                        }
                                    }
                                }
                            }
                        }
                            continue;
                        }
                    }
                    break;

                    case EthernetPacket.PacketType.Arp:
                    {
                        var arpPacket = (ArpPacket)p.PayloadPacket;
                        var targetIp  = new IPAddress(arpPacket.TargetProtocolAddress);
                        if (!targetIp.GetAddressBytes().Take(2).SequenceEqual(tapIpPrefix))
                        {
                            continue;
                        }
                        var targetId = GetIdFromPhysicalAddress(GetPhysicalAddressFromIp(targetIp));
                        if (targetId == -ipMacPoolShift)
                        {
                            continue;
                        }
                        if (!HostExists((ushort)targetId) && targetId != -1)
                        {
                            break;
                        }
                        var answerArpPacket = new ArpPacket
                        {
                            TargetHardwareAddress = arpPacket.SenderHardwareAddress,
                            TargetProtocolAddress = arpPacket.SenderProtocolAddress,
                            SenderHardwareAddress = GetPhysicalAddressFromIp(targetIp).GetAddressBytes(),
                            SenderProtocolAddress = arpPacket.TargetProtocolAddress,
                            Operation             = ArpPacket.OperationType.Response,
                            HardwareType          = 0x0001,
                            ProtocolType          = 0x0800
                        };
                        var answerEthernetPacket = new EthernetPacket(GetPhysicalAddressFromIp(targetIp),
                                                                      new PhysicalAddress(arpPacket.SenderHardwareAddress), answerArpPacket,
                                                                      EthernetPacket.PacketType.Arp);
                        var answerData = answerEthernetPacket.ToBytes();
                        await tapStream.WriteAsync(answerData, 0, answerData.Length, cancellationToken);
                    }
                    break;

                    default:
                        continue;
                    }
                }
                catch (TaskCanceledException)
                {
                    return;
                }
                catch (Exception e)
                {
                    logger.Error(e, "Exception in processing packet from TAP-Windows");
                }
            }
        }