You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by hu...@apache.org on 2022/10/06 15:23:54 UTC

[plc4x] 09/13: feat(plc4j/): split out dcp and lldp tasks, so they can be processed separately.

This is an automated email from the ASF dual-hosted git repository.

hutcheb pushed a commit to branch plc4j/profinet
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit d1c18db45f5334e1ddc6d529008fb531fc217722
Author: Ben Hutcheson <be...@gmail.com>
AuthorDate: Fri Sep 16 15:53:03 2022 -0600

    feat(plc4j/): split out dcp and lldp tasks, so they can be processed separately.
---
 .../profinet/discovery/ProfinetPlcDiscoverer.java  | 755 +++++++++++----------
 1 file changed, 387 insertions(+), 368 deletions(-)

diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
index 4486e1abc..bc7ef865d 100644
--- a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
@@ -20,7 +20,6 @@ package org.apache.plc4x.java.profinet.discovery;
 
 import org.apache.commons.codec.DecoderException;
 import org.apache.commons.codec.binary.Hex;
-import org.apache.plc4x.java.api.exceptions.PlcException;
 import org.apache.plc4x.java.api.messages.PlcDiscoveryItem;
 import org.apache.plc4x.java.api.messages.PlcDiscoveryItemHandler;
 import org.apache.plc4x.java.api.messages.PlcDiscoveryRequest;
@@ -50,7 +49,6 @@ import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.function.Function;
-import java.util.function.IntBinaryOperator;
 
 public class ProfinetPlcDiscoverer implements PlcDiscoverer {
 
@@ -68,6 +66,12 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
     private static final String DEVICE_INSTANCE = "DEVICE_PROPERTIES_OPTION-7";
     private static final String IP_OPTION_IP = "IP_OPTION-2";
 
+    ExecutorService pool = Executors.newSingleThreadExecutor();
+    Map<MacAddress, PcapHandle> openHandles = new HashMap<>();
+    List<PlcDiscoveryItem> values = new ArrayList<>();
+
+    Set<Timer> periodicTimers = new HashSet<>();
+
     private final Logger logger = LoggerFactory.getLogger(ProfinetPlcDiscoverer.class);
 
     @Override
@@ -75,200 +79,49 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
         return discoverWithHandler(discoveryRequest, null);
     }
 
-    public CompletableFuture<PlcDiscoveryResponse> discoverWithHandler(PlcDiscoveryRequest discoveryRequest, PlcDiscoveryItemHandler handler) {
-        CompletableFuture<PlcDiscoveryResponse> future = new CompletableFuture<>();
-        Set<PcapHandle> openHandles = new HashSet<>();
-        List<PlcDiscoveryItem> values = new ArrayList<>();
+    public void openDiscoverHandles() {
         try {
             for (PcapNetworkInterface dev : Pcaps.findAllDevs()) {
                 // It turned out on some MAC network devices without any ip addresses
                 // the compiling of the filter expression was causing errors. As
                 // currently there was no other way to detect this, this check seems
                 // to be sufficient.
-                if(dev.getAddresses().size() == 0) {
+                if (dev.getAddresses().size() == 0) {
                     continue;
                 }
                 if (!dev.isLoopBack()) {
                     for (LinkLayerAddress linkLayerAddress : dev.getLinkLayerAddresses()) {
                         org.pcap4j.util.MacAddress macAddress = (org.pcap4j.util.MacAddress) linkLayerAddress;
                         PcapHandle handle = dev.openLive(65536, PcapNetworkInterface.PromiscuousMode.PROMISCUOUS, 10);
-                        openHandles.add(handle);
+                        openHandles.put(toPlc4xMacAddress(macAddress), handle);
 
-                        ExecutorService pool = Executors.newSingleThreadExecutor();
-
-                        // Only react on PROFINET DCP packets targeted at our current MAC address.
+                        // Only react on PROFINET DCP or LLDP packets targeted at our current MAC address.
                         handle.setFilter(
-                            "((ether proto 0x8100) or (ether proto 0x8892)) and (ether dst " + Pcaps.toBpfString(macAddress) + ")",
+                            "(((ether proto 0x8100) or (ether proto 0x8892)) and (ether dst " + Pcaps.toBpfString(macAddress) + ")) or (ether proto 0x88cc)",
                             BpfProgram.BpfCompileMode.OPTIMIZE);
-
-                        PacketListener listener =
-                            packet -> {
-                                // EthernetPacket is the highest level of abstraction we can be expecting.
-                                // Everything inside this we will have to decode ourselves.
-                                if (packet instanceof EthernetPacket) {
-                                    EthernetPacket ethernetPacket = (EthernetPacket) packet;
-                                    boolean isPnPacket = false;
-                                    // I have observed some times the ethernet packets being wrapped inside a VLAN
-                                    // Packet, in this case we simply unpack the content.
-                                    if (ethernetPacket.getPayload() instanceof Dot1qVlanTagPacket) {
-                                        Dot1qVlanTagPacket vlanPacket = (Dot1qVlanTagPacket) ethernetPacket.getPayload();
-                                        if (PN_EtherType.equals(vlanPacket.getHeader().getType())) {
-                                            isPnPacket = true;
-                                        }
-                                    } else if (PN_EtherType.equals(ethernetPacket.getHeader().getType())) {
-                                        isPnPacket = true;
-                                    }
-
-                                    // It's a PROFINET packet.
-                                    if (isPnPacket) {
-                                        ReadBuffer reader = new ReadBufferByteBased(ethernetPacket.getRawData());
-                                        try {
-                                            Ethernet_Frame ethernetFrame = Ethernet_Frame.staticParse(reader);
-                                            PnDcp_Pdu pdu;
-                                            // Access the pdu data (either directly or by
-                                            // unpacking the content of the VLAN packet.
-                                            if (ethernetFrame.getPayload() instanceof Ethernet_FramePayload_VirtualLan) {
-                                                Ethernet_FramePayload_VirtualLan vlefpl = (Ethernet_FramePayload_VirtualLan) ethernetFrame.getPayload();
-                                                pdu = ((Ethernet_FramePayload_PnDcp) vlefpl.getPayload()).getPdu();
-                                            } else {
-                                                pdu = ((Ethernet_FramePayload_PnDcp) ethernetFrame.getPayload()).getPdu();
-                                            }
-                                            // Inspect the PDU itself
-                                            // (in this case we only process identify response packets)
-                                            if (pdu instanceof PnDcp_Pdu_IdentifyRes) {
-                                                PnDcp_Pdu_IdentifyRes identifyResPDU = (PnDcp_Pdu_IdentifyRes) pdu;
-
-                                                Map<String, PnDcp_Block> blocks = new HashMap<>();
-                                                for (PnDcp_Block block : identifyResPDU.getBlocks()) {
-                                                    String blockName = block.getOption().name() + "-" + block.getSuboption().toString();
-                                                    blocks.put(blockName, block);
-                                                }
-
-                                                // The mac address of the device we found
-                                                org.pcap4j.util.MacAddress srcAddr = ethernetPacket.getHeader().getSrcAddr();
-                                                // The mac address of the local network device
-                                                org.pcap4j.util.MacAddress dstAddr = ethernetPacket.getHeader().getDstAddr();
-
-                                                String deviceTypeName = "unknown";
-                                                if (blocks.containsKey(DEVICE_TYPE_NAME)) {
-                                                    PnDcp_Block_DevicePropertiesDeviceVendor block = (PnDcp_Block_DevicePropertiesDeviceVendor) blocks.get(DEVICE_TYPE_NAME);
-                                                    deviceTypeName = new String(block.getDeviceVendorValue()).replace(" ", "%20");
-                                                }
-
-                                                String deviceName = "unknown";
-                                                if (blocks.containsKey(DEVICE_NAME_OF_STATION)) {
-                                                    PnDcp_Block_DevicePropertiesNameOfStation block = (PnDcp_Block_DevicePropertiesNameOfStation) blocks.get(DEVICE_NAME_OF_STATION);
-                                                    deviceName = new String(block.getNameOfStation()).replace(" ", "%20");
-                                                }
-
-                                                String role = "unknown";
-                                                if (blocks.containsKey(DEVICE_ROLE)) {
-                                                    role = "";
-                                                    PnDcp_Block_DevicePropertiesDeviceRole block = (PnDcp_Block_DevicePropertiesDeviceRole) blocks.get(DEVICE_ROLE);
-                                                    if (block.getPnioSupervisor()) {
-                                                        role += ",SUPERVISOR";
-                                                    }
-                                                    if (block.getPnioMultidevive()) {
-                                                        role += ",MULTIDEVICE";
-                                                    }
-                                                    if (block.getPnioController()) {
-                                                        role += ",CONTROLLER";
-                                                    }
-                                                    if (block.getPnioDevice()) {
-                                                        role += ",DEVICE";
-                                                    }
-                                                    // Cut off the first comma
-                                                    if (role.length() > 0) {
-                                                        role = role.substring(1);
-                                                    } else {
-                                                        role = "unknown";
-                                                    }
-                                                }
-
-                                                String remoteIpAddress = "unknown";
-                                                String remoteSubnetMask = "unknown";
-                                                if (blocks.containsKey(IP_OPTION_IP)) {
-                                                    PnDcp_Block_IpParameter block = (PnDcp_Block_IpParameter) blocks.get(IP_OPTION_IP);
-                                                    try {
-                                                        InetAddress addr = InetAddress.getByAddress(block.getIpAddress());
-                                                        remoteIpAddress = addr.getHostAddress();
-                                                        InetAddress netMask = InetAddress.getByAddress(block.getSubnetMask());
-                                                        remoteSubnetMask = netMask.getHostAddress();
-                                                    } catch (UnknownHostException e) {
-                                                        remoteIpAddress = "invalid";
-                                                    }
-                                                }
-
-                                                // Get the Vendor Id and the Device Id
-                                                String vendorId = "unknown";
-                                                String deviceId = "unknown";
-                                                if (blocks.containsKey(DEVICE_ID)) {
-                                                    PnDcp_Block_DevicePropertiesDeviceId block = (PnDcp_Block_DevicePropertiesDeviceId) blocks.get(DEVICE_ID);
-                                                    vendorId = String.format("%04X", block.getVendorId());
-                                                    deviceId = String.format("%04X", block.getDeviceId());
-                                                }
-
-                                                Map<String, String> options = new HashMap<>();
-                                                options.put("remoteIpAddress", remoteIpAddress);
-                                                options.put("remoteSubnetMask", remoteSubnetMask);
-                                                options.put("remoteMacAddress", srcAddr.toString());
-                                                options.put("localMacAddress", dstAddr.toString());
-                                                options.put("deviceTypeName", deviceTypeName);
-                                                options.put("deviceName", deviceName);
-                                                options.put("vendorId", vendorId);
-                                                options.put("deviceId", deviceId);
-                                                options.put("role", role);
-                                                String name = deviceTypeName + " - " + deviceName;
-                                                PlcDiscoveryItem value = new DefaultPlcDiscoveryItem(
-                                                    ProfinetDriver.DRIVER_CODE, RawSocketTransport.TRANSPORT_CODE,
-                                                    remoteIpAddress, options, name, Collections.emptyMap());
-                                                values.add(value);
-
-                                                // If we have a discovery handler, pass it to the handler callback
-                                                if (handler != null) {
-                                                    handler.handle(value);
-                                                }
-
-                                                logger.debug("Found new device: '{}' with connection-url '{}'",
-                                                    value.getName(), value.getConnectionUrl());
-                                            }
-                                        } catch (ParseException e) {
-                                            logger.error("Got error decoding packet", e);
-                                        }
-                                    }
-                                }
-                            };
-                        Task t = new Task(handle, listener);
-                        pool.execute(t);
-
-                        // Construct and send the search request.
-                        Ethernet_Frame identificationRequest = new Ethernet_Frame(
-                            // Pre-Defined PROFINET discovery MAC address
-                            new MacAddress(new byte[]{0x01, 0x0E, (byte) 0xCF, 0x00, 0x00, 0x00}),
-                            toPlc4xMacAddress(macAddress),
-                            new Ethernet_FramePayload_VirtualLan(VirtualLanPriority.BEST_EFFORT, false, 0,
-                                new Ethernet_FramePayload_PnDcp(
-                                    new PnDcp_Pdu_IdentifyReq(PnDcp_FrameId.DCP_Identify_ReqPDU.getValue(),
-                                        1,
-                                        256,
-                                        Collections.singletonList(
-                                            new PnDcp_Block_ALLSelector()
-                                        )))));
-                        WriteBufferByteBased buffer = new WriteBufferByteBased(34);
-                        identificationRequest.serialize(buffer);
-                        Packet packet = EthernetPacket.newPacket(buffer.getData(), 0, 34);
-                        handle.sendPacket(packet);
                     }
                 }
             }
-        } catch (IllegalRawDataException | NotOpenException | PcapNativeException | SerializationException e) {
+        } catch (NotOpenException | PcapNativeException e) {
             logger.error("Got an exception while processing raw socket data", e);
-            future.completeExceptionally(new PlcException("Got an internal error while performing discovery"));
-            for (PcapHandle openHandle : openHandles) {
-                openHandle.close();
+            for (Map.Entry<MacAddress, PcapHandle> entry : openHandles.entrySet()) {
+                PcapHandle openHandle = entry.getValue();
+                try {
+                    openHandle.breakLoop();
+                    openHandle.close();
+                } catch (NotOpenException error) {
+                    logger.info("Handle already closed.");
+                }
+            }
+            for (Timer timer : periodicTimers) {
+                timer.cancel();
+                timer.purge();
             }
-            return future;
         }
+    }
+
+    public CompletableFuture<PlcDiscoveryResponse> setDiscoveryEndTimer(PlcDiscoveryRequest discoveryRequest, long delay) {
+        CompletableFuture<PlcDiscoveryResponse> future = new CompletableFuture<>();
 
         // Create a timer that completes the future after a given time with all the responses it found till then.
         Timer timer = new Timer("Discovery Timeout");
@@ -276,229 +129,395 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
             public void run() {
                 PlcDiscoveryResponse response =
                     new DefaultPlcDiscoveryResponse(discoveryRequest, PlcResponseCode.OK, values);
-                future.complete(response);
-                for (PcapHandle openHandle : openHandles) {
-                    openHandle.close();
+                for (Map.Entry<MacAddress, PcapHandle> entry : openHandles.entrySet()) {
+                    PcapHandle openHandle = entry.getValue();
+                    try {
+                        openHandle.breakLoop();
+                        openHandle.close();
+                    } catch (Exception e) {
+                        logger.error("Error occurred while closing handle");
+                    }
+
                 }
+                for (Timer timer : periodicTimers) {
+                    timer.cancel();
+                    timer.purge();
+                }
+                future.complete(response);
             }
-        }, 5000L);
+        }, delay);
 
         return future;
     }
 
-    public void lldpProbe() {
-        Set<PcapHandle> openHandles = new HashSet<>();
-        try {
-            for (PcapNetworkInterface dev : Pcaps.findAllDevs()) {
-                // It turned out on some MAC network devices without any ip addresses
-                // the compiling of the filter expression was causing errors. As
-                // currently there was no other way to detect this, this check seems
-                // to be sufficient.
-                if(dev.getAddresses().size() == 0) {
-                    continue;
+    public PacketListener createListener(PcapHandle handle, PlcDiscoveryItemHandler handler) {
+        PacketListener listener =
+            packet -> {
+                // EthernetPacket is the highest level of abstraction we can be expecting.
+                // Everything inside this we will have to decode ourselves.
+                if (packet instanceof EthernetPacket) {
+                    EthernetPacket ethernetPacket = (EthernetPacket) packet;
+                    boolean isPnPacket = false;
+                    // I have observed sometimes the ethernet packets being wrapped inside a VLAN
+                    // Packet, in this case we simply unpack the content.
+                    if (ethernetPacket.getPayload() instanceof Dot1qVlanTagPacket) {
+                        Dot1qVlanTagPacket vlanPacket = (Dot1qVlanTagPacket) ethernetPacket.getPayload();
+                        if (PN_EtherType.equals(vlanPacket.getHeader().getType()) || LLDP_EtherType.equals(vlanPacket.getHeader().getType())) {
+                            isPnPacket = true;
+                        }
+                    } else if (PN_EtherType.equals(ethernetPacket.getHeader().getType()) || LLDP_EtherType.equals(ethernetPacket.getHeader().getType())) {
+                        isPnPacket = true;
+                    }
+
+                    // It's a PROFINET or LLDP packet.
+                    if (isPnPacket) {
+                        ReadBuffer reader = new ReadBufferByteBased(ethernetPacket.getRawData());
+                        try {
+                            Ethernet_Frame ethernetFrame = Ethernet_Frame.staticParse(reader);
+
+                            // Access the pdu data (either directly or by
+                            // unpacking the content of the VLAN packet.
+                            if (ethernetFrame.getPayload() instanceof Ethernet_FramePayload_VirtualLan) {
+                                Ethernet_FramePayload_VirtualLan vlefpl = (Ethernet_FramePayload_VirtualLan) ethernetFrame.getPayload();
+                                if (vlefpl.getPayload() instanceof Ethernet_FramePayload_PnDcp) {
+                                    PnDcp_Pdu pdu = ((Ethernet_FramePayload_PnDcp) vlefpl.getPayload()).getPdu();
+                                    processPnDcp(pdu, ethernetPacket, handler);
+                                } else if (vlefpl.getPayload() instanceof Ethernet_FramePayload_LLDP) {
+                                    Lldp_Pdu pdu = ((Ethernet_FramePayload_LLDP) vlefpl.getPayload()).getPdu();
+                                    processLldp(pdu, ethernetPacket, handler);
+                                }
+                            } else if (ethernetFrame.getPayload() instanceof Ethernet_FramePayload_PnDcp) {
+                                PnDcp_Pdu pdu = ((Ethernet_FramePayload_PnDcp) ethernetFrame.getPayload()).getPdu();
+                                processPnDcp(pdu, ethernetPacket, handler);
+                            } else if (ethernetFrame.getPayload() instanceof Ethernet_FramePayload_LLDP) {
+                                Lldp_Pdu pdu = ((Ethernet_FramePayload_LLDP) ethernetFrame.getPayload()).getPdu();
+                                processLldp(pdu, ethernetPacket, handler);
+                            }
+
+                        } catch (ParseException e) {
+                            logger.error("Got error decoding packet", e);
+                        }
+                    }
                 }
-                if (!dev.isLoopBack()) {
-                    for (LinkLayerAddress linkLayerAddress : dev.getLinkLayerAddresses()) {
-                        org.pcap4j.util.MacAddress macAddress = (org.pcap4j.util.MacAddress) linkLayerAddress;
-                        PcapHandle handle = dev.openLive(65536, PcapNetworkInterface.PromiscuousMode.PROMISCUOUS, 10);
-                        openHandles.add(handle);
+            };
+        return listener;
+    }
 
-                        ExecutorService pool = Executors.newSingleThreadExecutor();
+    public CompletableFuture<PlcDiscoveryResponse> discoverWithHandler(PlcDiscoveryRequest discoveryRequest, PlcDiscoveryItemHandler handler) {
+        openDiscoverHandles();
+        startListener(handler);
+        startLldpPoll();
+        startPnDcpPoll();
+        CompletableFuture<PlcDiscoveryResponse> future = setDiscoveryEndTimer(discoveryRequest, 10000L);
+        return future;
+    }
 
-                        // Only react on PROFINET DCP packets targeted at our current MAC address.
-                        handle.setFilter(
-                            "(ether proto 0x88cc)",
-                            BpfProgram.BpfCompileMode.OPTIMIZE);
+    private void processPnDcp(PnDcp_Pdu pdu, EthernetPacket ethernetPacket, PlcDiscoveryItemHandler handler) {
+        // Inspect the PDU itself
+        // (in this case we only process identify response packets)
+        if (pdu instanceof PnDcp_Pdu_IdentifyRes) {
+            PnDcp_Pdu_IdentifyRes identifyResPDU = (PnDcp_Pdu_IdentifyRes) pdu;
 
-                        PacketListener listener =
-                            packet -> {
-                                // EthernetPacket is the highest level of abstraction we can be expecting.
-                                // Everything inside this we will have to decode ourselves.
-                                if (packet instanceof EthernetPacket) {
-                                    EthernetPacket ethernetPacket = (EthernetPacket) packet;
-                                    boolean isLldpPacket = false;
-                                    // I have observed sometimes the ethernet packets being wrapped inside a VLAN
-                                    // Packet, in this case we simply unpack the content.
-                                    if (ethernetPacket.getPayload() instanceof Dot1qVlanTagPacket) {
-                                        Dot1qVlanTagPacket vlanPacket = (Dot1qVlanTagPacket) ethernetPacket.getPayload();
-                                        if (LLDP_EtherType.equals(vlanPacket.getHeader().getType())) {
-                                            isLldpPacket = true;
-                                        }
-                                    } else if (LLDP_EtherType.equals(ethernetPacket.getHeader().getType())) {
-                                        isLldpPacket = true;
-                                    }
-
-                                    // It's a LLDP packet.
-                                    if (isLldpPacket) {
-                                        ReadBuffer reader = new ReadBufferByteBased(ethernetPacket.getRawData());
-                                        try {
-                                            Ethernet_Frame ethernetFrame = Ethernet_Frame.staticParse(reader);
-                                            Lldp_Pdu pdu;
-                                            // Access the pdu data (either directly or by
-                                            // unpacking the content of the VLAN packet.
-                                            if (ethernetFrame.getPayload() instanceof Ethernet_FramePayload_VirtualLan) {
-                                                Ethernet_FramePayload_VirtualLan vlefpl = (Ethernet_FramePayload_VirtualLan) ethernetFrame.getPayload();
-                                                pdu = ((Ethernet_FramePayload_LLDP) vlefpl.getPayload()).getPdu();
-                                            } else {
-                                                pdu = ((Ethernet_FramePayload_LLDP) ethernetFrame.getPayload()).getPdu();
-                                            }
-                                            // Inspect the PDU itself
-                                            // (in this case we only process identify response packets)
-
-
-                                            logger.debug("Found new device: '{}' using LLDP '{}'",
-                                                "Not Sure", "Not Sure");
-                                        } catch (ParseException ex) {
-                                            throw new RuntimeException(ex);
-                                        }
-                                    }
-                                }
-                            };
-                        Task t = new Task(handle, listener);
-                        pool.execute(t);
-
-                        Function<Object, Boolean> lldpTimer =
-                            message -> {
-                                // Construct and send the LLDP Probe
-                                TlvOrgSpecificProfibus portStatus = new TlvOrgSpecificProfibus(
-                                    new TlvProfibusSubTypePortStatus(0x00)
-                                );
-
-                                TlvOrgSpecificProfibus chassisMac = new TlvOrgSpecificProfibus(
-                                    new TlvProfibusSubTypeChassisMac(new MacAddress(linkLayerAddress.getAddress()))
-                                );
-
-                                TlvOrgSpecificIeee8023 ieee = new TlvOrgSpecificIeee8023(
-                                    (short) 0x01,
-                                    (short) 0x03,
-                                    0x0020,
-                                    0x0010
-                                );
-
-                                Ethernet_Frame identificationRequest = null;
-                                try {
-                                    identificationRequest = new Ethernet_Frame(
-                                        // Pre-Defined LLDP discovery MAC address
-                                        new MacAddress(new byte[]{0x01, (byte) 0x80, (byte) 0xc2, 0x00, 0x00, 0x0e}),
-                                        toPlc4xMacAddress(macAddress),
-                                        new Ethernet_FramePayload_LLDP(
-                                            new Lldp_Pdu(
-                                                Arrays.asList(
-                                                    new TlvChassisId(
-                                                        PLC4X_LLDP_IDENTIFIER.length() + 1,
-                                                        (short) 7,
-                                                        PLC4X_LLDP_IDENTIFIER
-                                                    ),
-                                                    new TlvPortId(
-                                                        PLC4X_LLDP_PORT.length() + 1,
-                                                        (short) 7,
-                                                        PLC4X_LLDP_PORT
-                                                    ),
-                                                    new TlvTimeToLive(2, 20),
-                                                    new TlvOrganizationSpecific(
-                                                        portStatus.getLengthInBytes(),
-                                                        portStatus
-                                                    ),
-                                                    new TlvOrganizationSpecific(
-                                                        chassisMac.getLengthInBytes(),
-                                                        chassisMac
-                                                    ),
-                                                    new TlvOrganizationSpecific(
-                                                        ieee.getLengthInBytes(),
-                                                        ieee
-                                                    ),
-                                                    new TlvManagementAddress(
-                                                        12,
-                                                        ManagementAddressSubType.IPV4,
-                                                        new IpAddress(Hex.decodeHex("c0a85a6e")),
-                                                        (short) 0x03,
-                                                        0x01L,
-                                                        (short) 0x00
-                                                    ),
-                                                    new EndOfLldp(0)
-                                                )
-                                            )));
-                                } catch (DecoderException e) {
-                                    throw new RuntimeException(e);
-                                }
-                                WriteBufferByteBased buffer = new WriteBufferByteBased(identificationRequest.getLengthInBytes());
-                                try {
-                                    identificationRequest.serialize(buffer);
-                                } catch (SerializationException e) {
-                                    throw new RuntimeException(e);
-                                }
-                                Packet packet = null;
-                                try {
-                                    packet = EthernetPacket.newPacket(buffer.getData(), 0, identificationRequest.getLengthInBytes());
-                                } catch (IllegalRawDataException e) {
-                                    throw new RuntimeException(e);
-                                }
-                                try {
-                                    handle.sendPacket(packet);
-                                } catch (PcapNativeException e) {
-                                    throw new RuntimeException(e);
-                                } catch (NotOpenException e) {
-                                    throw new RuntimeException(e);
-                                }
-                                return null;
-                            };
-                        Timer timer = new Timer();
-
-                        // Schedule to run after every 3 second(3000 millisecond)
-                        timer.scheduleAtFixedRate(
-                            new LLDPTask(handle, lldpTimer),
-                            3000,
-                            3000);
-                    }
+            Map<String, PnDcp_Block> blocks = new HashMap<>();
+            for (PnDcp_Block block : identifyResPDU.getBlocks()) {
+                String blockName = block.getOption().name() + "-" + block.getSuboption().toString();
+                blocks.put(blockName, block);
+            }
+
+            // The mac address of the device we found
+            org.pcap4j.util.MacAddress srcAddr = ethernetPacket.getHeader().getSrcAddr();
+            // The mac address of the local network device
+            org.pcap4j.util.MacAddress dstAddr = ethernetPacket.getHeader().getDstAddr();
+
+            String deviceTypeName = "unknown";
+            if (blocks.containsKey(DEVICE_TYPE_NAME)) {
+                PnDcp_Block_DevicePropertiesDeviceVendor block = (PnDcp_Block_DevicePropertiesDeviceVendor) blocks.get(DEVICE_TYPE_NAME);
+                deviceTypeName = new String(block.getDeviceVendorValue()).replace(" ", "%20");
+            }
+
+            String deviceName = "unknown";
+            if (blocks.containsKey(DEVICE_NAME_OF_STATION)) {
+                PnDcp_Block_DevicePropertiesNameOfStation block = (PnDcp_Block_DevicePropertiesNameOfStation) blocks.get(DEVICE_NAME_OF_STATION);
+                deviceName = new String(block.getNameOfStation()).replace(" ", "%20");
+            }
+
+            String role = "unknown";
+            if (blocks.containsKey(DEVICE_ROLE)) {
+                role = "";
+                PnDcp_Block_DevicePropertiesDeviceRole block = (PnDcp_Block_DevicePropertiesDeviceRole) blocks.get(DEVICE_ROLE);
+                if (block.getPnioSupervisor()) {
+                    role += ",SUPERVISOR";
+                }
+                if (block.getPnioMultidevive()) {
+                    role += ",MULTIDEVICE";
+                }
+                if (block.getPnioController()) {
+                    role += ",CONTROLLER";
+                }
+                if (block.getPnioDevice()) {
+                    role += ",DEVICE";
+                }
+                // Cut off the first comma
+                if (role.length() > 0) {
+                    role = role.substring(1);
+                } else {
+                    role = "unknown";
                 }
             }
-        } catch (NotOpenException | PcapNativeException e) {
-            logger.error("Got an exception while processing raw socket data", e);
 
-            for (PcapHandle openHandle : openHandles) {
-                openHandle.close();
+            String remoteIpAddress = "unknown";
+            String remoteSubnetMask = "unknown";
+            if (blocks.containsKey(IP_OPTION_IP)) {
+                PnDcp_Block_IpParameter block = (PnDcp_Block_IpParameter) blocks.get(IP_OPTION_IP);
+                try {
+                    InetAddress addr = InetAddress.getByAddress(block.getIpAddress());
+                    remoteIpAddress = addr.getHostAddress();
+                    InetAddress netMask = InetAddress.getByAddress(block.getSubnetMask());
+                    remoteSubnetMask = netMask.getHostAddress();
+                } catch (UnknownHostException e) {
+                    remoteIpAddress = "invalid";
+                }
             }
+
+            // Get the Vendor Id and the Device Id
+            String vendorId = "unknown";
+            String deviceId = "unknown";
+            if (blocks.containsKey(DEVICE_ID)) {
+                PnDcp_Block_DevicePropertiesDeviceId block = (PnDcp_Block_DevicePropertiesDeviceId) blocks.get(DEVICE_ID);
+                vendorId = String.format("%04X", block.getVendorId());
+                deviceId = String.format("%04X", block.getDeviceId());
+            }
+
+            Map<String, String> options = new HashMap<>();
+            options.put("remoteIpAddress", remoteIpAddress);
+            options.put("remoteSubnetMask", remoteSubnetMask);
+            options.put("remoteMacAddress", srcAddr.toString());
+            options.put("localMacAddress", dstAddr.toString());
+            options.put("deviceTypeName", deviceTypeName);
+            options.put("deviceName", deviceName);
+            options.put("vendorId", vendorId);
+            options.put("deviceId", deviceId);
+            options.put("role", role);
+            String name = deviceTypeName + " - " + deviceName;
+            PlcDiscoveryItem value = new DefaultPlcDiscoveryItem(
+                ProfinetDriver.DRIVER_CODE, RawSocketTransport.TRANSPORT_CODE,
+                remoteIpAddress, options, name, Collections.emptyMap());
+            values.add(value);
+
+            // If we have a discovery handler, pass it to the handler callback
+            if (handler != null) {
+                handler.handle(value);
+            }
+
+            logger.debug("Found new device: '{}' with connection-url '{}'",
+                value.getName(), value.getConnectionUrl());
         }
     }
 
-    private static MacAddress toPlc4xMacAddress(org.pcap4j.util.MacAddress pcap4jMacAddress) {
-        byte[] address = pcap4jMacAddress.getAddress();
-        return new MacAddress(new byte[]{address[0], address[1], address[2], address[3], address[4], address[5]});
+    private void processLldp(Lldp_Pdu pdu, EthernetPacket ethernetPacket, PlcDiscoveryItemHandler handler) {
+        logger.debug("Found new lldp device: '' with connection-url ''");
     }
 
-    private static class Task implements Runnable {
+    public void startPnDcpPoll() {
+        for (Map.Entry<MacAddress, PcapHandle> entry : openHandles.entrySet()) {
+            PcapHandle handle = entry.getValue();
+            MacAddress macAddress = entry.getKey();
+            // Construct and send the search request.
+
+            Function<Object, Boolean> pnDcpTimer =
+                message -> {
+                    Ethernet_Frame identificationRequest = new Ethernet_Frame(
+                        // Pre-Defined PROFINET discovery MAC address
+                        new MacAddress(new byte[]{0x01, 0x0E, (byte) 0xCF, 0x00, 0x00, 0x00}),
+                        macAddress,
+                        new Ethernet_FramePayload_VirtualLan(VirtualLanPriority.BEST_EFFORT, false, 0,
+                            new Ethernet_FramePayload_PnDcp(
+                                new PnDcp_Pdu_IdentifyReq(PnDcp_FrameId.DCP_Identify_ReqPDU.getValue(),
+                                    1,
+                                    256,
+                                    Collections.singletonList(
+                                        new PnDcp_Block_ALLSelector()
+                                    )))));
+                    WriteBufferByteBased buffer = new WriteBufferByteBased(34);
+                    try {
+                        identificationRequest.serialize(buffer);
+                    } catch (SerializationException e) {
+                        throw new RuntimeException(e);
+                    }
+                    Packet packet = null;
+                    try {
+                        packet = EthernetPacket.newPacket(buffer.getData(), 0, 34);
+                    } catch (IllegalRawDataException e) {
+                        throw new RuntimeException(e);
+                    }
+                    try {
+                        handle.sendPacket(packet);
+                    } catch (PcapNativeException e) {
+                        throw new RuntimeException(e);
+                    } catch (NotOpenException e) {
+                        throw new RuntimeException(e);
+                    }
+                    return null;
+            };
+
+            Timer timer = new Timer();
+            periodicTimers.add(timer);
 
-        private final Logger logger = LoggerFactory.getLogger(Task.class);
+            // Schedule to run after every 3 second(3000 millisecond)
+            timer.scheduleAtFixedRate(
+                new PeriodicTask(handle, pnDcpTimer),
+                5000,
+                5000);
+        }
+    }
 
-        private final PcapHandle handle;
-        private final PacketListener listener;
+    public void startListener(PlcDiscoveryItemHandler handler) {
+        for (Map.Entry<MacAddress, PcapHandle> entry : openHandles.entrySet()) {
+            PcapHandle handle = entry.getValue();
+            MacAddress macAddress = entry.getKey();
+            // Construct and send the search request.
+
+            Function<Object, Boolean> pnDcpTimer =
+                message -> {
+                    PacketListener listener = createListener(handle, handler);
+                    try {
+                        handle.loop(-1, listener);
+                    } catch (InterruptedException e) {
+                        logger.error("Got error handling raw socket", e);
+                        Thread.currentThread().interrupt();
+                    } catch (PcapNativeException | NotOpenException e) {
+                        logger.error("Got error handling raw socket", e);
+                    }
+                    return null;
+                };
 
-        public Task(PcapHandle handle, PacketListener listener) {
-            this.handle = handle;
-            this.listener = listener;
+            Timer timer = new Timer();
+            periodicTimers.add(timer);
+
+            // Schedule to run after every 3 second(3000 millisecond)
+            timer.schedule(
+                new PeriodicTask(handle, pnDcpTimer),
+                5000,
+                15000);
         }
+    }
 
-        @Override
-        public void run() {
-            try {
-                handle.loop(10, listener);
-            } catch (InterruptedException e) {
-                logger.error("Got error handling raw socket", e);
-                Thread.currentThread().interrupt();
-            } catch (PcapNativeException | NotOpenException e) {
-                logger.error("Got error handling raw socket", e);
-            }
+
+
+    public void startLldpPoll() {
+        for (Map.Entry<MacAddress, PcapHandle> entry : openHandles.entrySet()) {
+            PcapHandle handle = entry.getValue();
+            MacAddress macAddress = entry.getKey();
+
+            Function<Object, Boolean> lldpTimer =
+                message -> {
+                    // Construct and send the LLDP Probe
+                    TlvOrgSpecificProfibus portStatus = new TlvOrgSpecificProfibus(
+                        new TlvProfibusSubTypePortStatus(0x00)
+                    );
+
+                    TlvOrgSpecificProfibus chassisMac = new TlvOrgSpecificProfibus(
+                        new TlvProfibusSubTypeChassisMac(macAddress)
+                    );
+
+                    TlvOrgSpecificIeee8023 ieee = new TlvOrgSpecificIeee8023(
+                        (short) 0x01,
+                        (short) 0x03,
+                        0x0020,
+                        0x0010
+                    );
+
+                    Ethernet_Frame identificationRequest = null;
+                    try {
+                        identificationRequest = new Ethernet_Frame(
+                            // Pre-Defined LLDP discovery MAC address
+                            new MacAddress(new byte[]{0x01, (byte) 0x80, (byte) 0xc2, 0x00, 0x00, 0x0e}),
+                            macAddress,
+                            new Ethernet_FramePayload_LLDP(
+                                new Lldp_Pdu(
+                                    Arrays.asList(
+                                        new TlvChassisId(
+                                            PLC4X_LLDP_IDENTIFIER.length() + 1,
+                                            (short) 7,
+                                            PLC4X_LLDP_IDENTIFIER
+                                        ),
+                                        new TlvPortId(
+                                            PLC4X_LLDP_PORT.length() + 1,
+                                            (short) 7,
+                                            PLC4X_LLDP_PORT
+                                        ),
+                                        new TlvTimeToLive(2, 20),
+                                        new TlvOrganizationSpecific(
+                                            portStatus.getLengthInBytes(),
+                                            portStatus
+                                        ),
+                                        new TlvOrganizationSpecific(
+                                            chassisMac.getLengthInBytes(),
+                                            chassisMac
+                                        ),
+                                        new TlvOrganizationSpecific(
+                                            ieee.getLengthInBytes(),
+                                            ieee
+                                        ),
+                                        new TlvManagementAddress(
+                                            12,
+                                            ManagementAddressSubType.IPV4,
+                                            new IpAddress(Hex.decodeHex("c0a85a6e")),
+                                            (short) 0x03,
+                                            0x01L,
+                                            (short) 0x00
+                                        ),
+                                        new EndOfLldp(0)
+                                    )
+                                )));
+                    } catch (DecoderException e) {
+                        throw new RuntimeException(e);
+                    }
+                    WriteBufferByteBased buffer = new WriteBufferByteBased(identificationRequest.getLengthInBytes());
+                    try {
+                        identificationRequest.serialize(buffer);
+                    } catch (SerializationException e) {
+                        throw new RuntimeException(e);
+                    }
+                    Packet packet = null;
+                    try {
+                        packet = EthernetPacket.newPacket(buffer.getData(), 0, identificationRequest.getLengthInBytes());
+                    } catch (IllegalRawDataException e) {
+                        throw new RuntimeException(e);
+                    }
+                    try {
+                        handle.sendPacket(packet);
+                    } catch (PcapNativeException e) {
+                        throw new RuntimeException(e);
+                    } catch (NotOpenException e) {
+                        throw new RuntimeException(e);
+                    }
+                    return null;
+                };
+            Timer timer = new Timer();
+            periodicTimers.add(timer);
+
+            // Schedule to run after every 3 second(3000 millisecond)
+            timer.scheduleAtFixedRate(
+                new PeriodicTask(handle, lldpTimer),
+                5000,
+                5000);
         }
     }
 
-    private static class LLDPTask extends TimerTask {
+    private static MacAddress toPlc4xMacAddress(org.pcap4j.util.MacAddress pcap4jMacAddress) {
+        byte[] address = pcap4jMacAddress.getAddress();
+        return new MacAddress(new byte[]{address[0], address[1], address[2], address[3], address[4], address[5]});
+    }
+
+    private static class PeriodicTask extends TimerTask {
 
-        private final Logger logger = LoggerFactory.getLogger(Task.class);
+        private final Logger logger = LoggerFactory.getLogger(PeriodicTask.class);
 
         private final PcapHandle handle;
         private final Function<Object, Boolean> operator;
 
-        public LLDPTask(PcapHandle handle, Function<Object, Boolean> operator) {
+        public PeriodicTask(PcapHandle handle, Function<Object, Boolean> operator) {
             this.handle = handle;
             this.operator = operator;
         }
@@ -507,12 +526,12 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
         public void run() {
             operator.apply(null);
         }
+
     }
 
     public static void main(String[] args) throws Exception {
         ProfinetPlcDiscoverer discoverer = new ProfinetPlcDiscoverer();
-        //discoverer.discover(null);
-        discoverer.lldpProbe();
+        discoverer.discover(null);
         Thread.sleep(10000);
     }