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);
}