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 <[email protected]> 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); }
