[network] Make icmp ping and arp ping optional by presence thing (#18083)
Signed-off-by: Mark Herwege <mark.herwege@telenet.be>pull/18090/head
parent
9f9bb7b5e2
commit
3c90a0dcf9
|
@ -54,6 +54,12 @@ Use the following options for a **network:pingdevice**:
|
|||
- **timeout:** How long the ping will wait for an answer, in milliseconds. Default: `5000` (5 seconds).
|
||||
- **refreshInterval:** How often the device will be checked, in milliseconds. Default: `60000` (one minute).
|
||||
- **useIOSWakeUp:** When set to true, an additional port knock is performed before a ping. Default: `true`.
|
||||
- **useArpPing:** When set to true if the presence detection is allowed to use arp ping.
|
||||
This can speed up presence detection, but may lead to inaccurate ping latency measurements.
|
||||
Switch off if you want to use this for ping latency monitoring. Default: `true`.
|
||||
- **useIcmpPing:** When set to true if the presence detection is allowed to use icmp ping.
|
||||
When also using arp ping, the latency measurements will not be comparable.
|
||||
Switch off if you rather want to use arp ping latency monitoring. Default: `true`.
|
||||
- **networkInterfaceNames:** The network interface names used for communicating with the device.
|
||||
Limiting the network interfaces reduces the load when arping and Wake-on-LAN are used.
|
||||
Use comma separated values when using textual config. Default: empty (all network interfaces).
|
||||
|
@ -190,6 +196,7 @@ demo.things:
|
|||
|
||||
```java
|
||||
Thing network:pingdevice:devicename [ hostname="192.168.0.42", macAddress="6f:70:65:6e:48:41", useIOSWakeUp="false" ]
|
||||
Thing network:pingdevice:router [ hostname="192.168.0.1", useArpPing="false" ]
|
||||
Thing network:speedtest:local "SpeedTest 50Mo" @ "Internet" [url="https://bouygues.testdebit.info/", fileName="50M.iso"]
|
||||
```
|
||||
|
||||
|
@ -199,6 +206,8 @@ demo.items:
|
|||
Switch MyDevice { channel="network:pingdevice:devicename:online" }
|
||||
Number:Time MyDeviceResponseTime { channel="network:pingdevice:devicename:latency" }
|
||||
|
||||
Number:Time MyRouterResponseTime { channel="network:pingdevice:router:latency" }
|
||||
|
||||
String Speedtest_Running "Test running ... [%s]" {channel="network:speedtest:local:isRunning"}
|
||||
Number:Dimensionless Speedtest_Progress "Test progress [%d %unit%]" {channel="network:speedtest:local:progress"}
|
||||
Number:DataTransferRate Speedtest_ResultDown "Downlink [%.2f %unit%]" {channel="network:speedtest:local:rateDown"}
|
||||
|
@ -218,6 +227,10 @@ sitemap demo label="Main Menu"
|
|||
Text item=MyDeviceResponseTime label="Device Response Time [%s]"
|
||||
}
|
||||
|
||||
Frame {
|
||||
Text item=MyRouterResponseTime label="Router Response Time [%s]"
|
||||
}
|
||||
|
||||
Frame label="SpeedTest" {
|
||||
Text item=Speedtest_Start
|
||||
Switch item=Speedtest_Running
|
||||
|
|
|
@ -32,5 +32,7 @@ public class NetworkHandlerConfiguration {
|
|||
public Integer refreshInterval = 60000;
|
||||
public Integer timeout = 5000;
|
||||
public boolean useIOSWakeUp = true;
|
||||
public boolean useArpPing = true;
|
||||
public boolean useIcmpPing = true;
|
||||
public Set<String> networkInterfaceNames = Set.of();
|
||||
}
|
||||
|
|
|
@ -71,8 +71,10 @@ public class PresenceDetection implements IPRequestReceivedCallback {
|
|||
private String ipPingState = "Disabled";
|
||||
protected String arpPingUtilPath = "";
|
||||
private ArpPingUtilEnum arpPingMethod = ArpPingUtilEnum.DISABLED;
|
||||
protected @Nullable IpPingMethodEnum pingMethod = null;
|
||||
protected @Nullable IpPingMethodEnum pingMethod = IpPingMethodEnum.DISABLED;
|
||||
private boolean iosDevice;
|
||||
private boolean useArpPing;
|
||||
private boolean useIcmpPing;
|
||||
private Set<Integer> tcpPorts = new HashSet<>();
|
||||
|
||||
private Duration refreshInterval = Duration.ofMinutes(1);
|
||||
|
@ -188,7 +190,7 @@ public class PresenceDetection implements IPRequestReceivedCallback {
|
|||
public void setUseIcmpPing(@Nullable Boolean useSystemPing) {
|
||||
if (useSystemPing == null) {
|
||||
ipPingState = "Disabled";
|
||||
pingMethod = null;
|
||||
pingMethod = IpPingMethodEnum.DISABLED;
|
||||
} else if (useSystemPing) {
|
||||
final IpPingMethodEnum pingMethod = networkUtils.determinePingMethod();
|
||||
this.pingMethod = pingMethod;
|
||||
|
@ -220,12 +222,17 @@ public class PresenceDetection implements IPRequestReceivedCallback {
|
|||
* Sets the path to ARP ping.
|
||||
*
|
||||
* @param enable enable or disable ARP ping
|
||||
* @param arpPingUtilPath enableDHCPListen(useDHCPsniffing);
|
||||
* @param arpPingUtilPath path to Arping tool
|
||||
* @param arpPingUtilMethod Arping tool method
|
||||
*/
|
||||
public void setUseArpPing(boolean enable, String arpPingUtilPath, ArpPingUtilEnum arpPingUtilMethod) {
|
||||
setUseArpPing(enable, destination.getValue());
|
||||
this.arpPingUtilPath = arpPingUtilPath;
|
||||
this.arpPingMethod = arpPingUtilMethod;
|
||||
if (!enable) {
|
||||
arpPingMethod = ArpPingUtilEnum.DISABLED;
|
||||
} else {
|
||||
setUseArpPing(enable, destination.getValue());
|
||||
this.arpPingUtilPath = arpPingUtilPath;
|
||||
this.arpPingMethod = arpPingUtilMethod;
|
||||
}
|
||||
}
|
||||
|
||||
public String getArpPingState() {
|
||||
|
@ -256,6 +263,36 @@ public class PresenceDetection implements IPRequestReceivedCallback {
|
|||
iosDevice = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return <code>true</code> if the device presence detection is also performed using arp ping. This gives
|
||||
* less accurate ping latency results when used for an IPv4 destination host.
|
||||
*/
|
||||
public boolean isUseArpPing() {
|
||||
return useArpPing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to <code>true</code> if the device presence detection should also be performed using arp ping. This gives
|
||||
* less accurate ping latency results when used for an IPv4 destination host.
|
||||
*/
|
||||
public void setUseArpPing(boolean useArpPing) {
|
||||
this.useArpPing = useArpPing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return <code>true</code> if the device presence detection is also performed using icmp ping.
|
||||
*/
|
||||
public boolean isUseIcmpPing() {
|
||||
return useIcmpPing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to <code>true</code> if the device presence detection should also be performed using icmp ping.
|
||||
*/
|
||||
public void setUseIcmPing(boolean useIcmpPing) {
|
||||
this.useIcmpPing = useIcmpPing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the last seen value as an {@link Instant} or <code>null</code> if not yet seen.
|
||||
*/
|
||||
|
@ -329,7 +366,7 @@ public class PresenceDetection implements IPRequestReceivedCallback {
|
|||
Set<String> interfaceNames = null;
|
||||
|
||||
detectionChecks = tcpPorts.size();
|
||||
if (pingMethod != null) {
|
||||
if (pingMethod != IpPingMethodEnum.DISABLED) {
|
||||
detectionChecks += 1;
|
||||
}
|
||||
if (arpPingMethod.canProceed) {
|
||||
|
@ -385,7 +422,7 @@ public class PresenceDetection implements IPRequestReceivedCallback {
|
|||
}
|
||||
|
||||
// ICMP ping
|
||||
if (pingMethod != null) {
|
||||
if (pingMethod != IpPingMethodEnum.DISABLED) {
|
||||
addAsyncDetection(completableFutures, () -> {
|
||||
Thread.currentThread().setName("presenceDetectionICMP_" + hostname);
|
||||
if (pingMethod == IpPingMethodEnum.JAVA_PING) {
|
||||
|
|
|
@ -183,8 +183,9 @@ public class NetworkHandler extends BaseThingHandler
|
|||
presenceDetection.setIOSDevice(handlerConfiguration.useIOSWakeUp);
|
||||
// Hand over binding configurations to the network service
|
||||
presenceDetection.setUseDhcpSniffing(configuration.allowDHCPlisten);
|
||||
presenceDetection.setUseIcmpPing(configuration.allowSystemPings);
|
||||
presenceDetection.setUseArpPing(true, configuration.arpPingToolPath, configuration.arpPingUtilMethod);
|
||||
presenceDetection.setUseIcmpPing(handlerConfiguration.useIcmpPing ? configuration.allowSystemPings : null);
|
||||
presenceDetection.setUseArpPing(handlerConfiguration.useArpPing, configuration.arpPingToolPath,
|
||||
configuration.arpPingUtilMethod);
|
||||
}
|
||||
|
||||
this.retries = handlerConfiguration.retry.intValue();
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
*/
|
||||
package org.openhab.binding.network.internal.utils;
|
||||
|
||||
import static org.openhab.binding.network.internal.utils.NetworkUtils.millisToDuration;
|
||||
import static org.openhab.binding.network.internal.utils.NetworkUtils.*;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.regex.Matcher;
|
||||
|
@ -31,7 +31,7 @@ import org.slf4j.LoggerFactory;
|
|||
@NonNullByDefault
|
||||
public class LatencyParser {
|
||||
|
||||
private static final Pattern LATENCY_PATTERN = Pattern.compile(".*time=(.*) ?ms");
|
||||
private static final Pattern LATENCY_PATTERN = Pattern.compile(".*time=(.*) ?(u|m)s.*");
|
||||
private final Logger logger = LoggerFactory.getLogger(LatencyParser.class);
|
||||
|
||||
// This is how the input looks like on Mac and Linux:
|
||||
|
@ -43,18 +43,30 @@ public class LatencyParser {
|
|||
// 1 packets transmitted, 1 packets received, 0.0% packet loss
|
||||
// round-trip min/avg/max/stddev = 1.225/1.225/1.225/0.000 ms
|
||||
|
||||
// This is an example of an arping response on Linux:
|
||||
// arping -c 1 -i eth0 192.168.0.1
|
||||
// ARPING 192.168.0.1
|
||||
// 60 bytes from xx:xx:xx:xx:xx:xx (192.168.0.1): index=0 time=792.847 usec
|
||||
//
|
||||
// --- 192.168.0.1 statistics ---
|
||||
// 1 packets transmitted, 1 packets received, 0% unanswered (0 extra)
|
||||
// rtt min/avg/max/std-dev = 0.793/0.793/0.793/0.000 ms
|
||||
|
||||
/**
|
||||
* Examine a single ping command output line and try to extract the latency value if it is contained.
|
||||
* Examine a single ping or arping command output line and try to extract the latency value if it is contained.
|
||||
*
|
||||
* @param inputLine Single output line of the ping command.
|
||||
* @return Latency value provided by the ping command. <code>null</code> if the provided line did not contain a
|
||||
* latency value which matches the known patterns.
|
||||
* @param inputLine Single output line of the ping or arping command.
|
||||
* @return Latency value provided by the ping or arping command. <code>null</code> if the provided line did not
|
||||
* contain a latency value which matches the known patterns.
|
||||
*/
|
||||
public @Nullable Duration parseLatency(String inputLine) {
|
||||
logger.debug("Parsing latency from input {}", inputLine);
|
||||
|
||||
Matcher m = LATENCY_PATTERN.matcher(inputLine);
|
||||
if (m.find() && m.groupCount() == 1) {
|
||||
if (m.find() && m.groupCount() >= 2) {
|
||||
if ("u".equals(m.group(2))) {
|
||||
return microsToDuration(Double.parseDouble(m.group(1).replace(",", ".")));
|
||||
}
|
||||
return millisToDuration(Double.parseDouble(m.group(1).replace(",", ".")));
|
||||
}
|
||||
|
||||
|
|
|
@ -55,9 +55,10 @@ import org.slf4j.LoggerFactory;
|
|||
public class NetworkUtils {
|
||||
|
||||
/**
|
||||
* Nanos per millisecond.
|
||||
* Nanos per millisecond and microsecond.
|
||||
*/
|
||||
private static final long NANOS_PER_MILLI = 1000_000L;
|
||||
private static final long NANOS_PER_MICRO = 1000L;
|
||||
|
||||
/**
|
||||
* Converts a {@link Duration} to milliseconds.
|
||||
|
@ -84,6 +85,17 @@ public class NetworkUtils {
|
|||
return Duration.ofNanos((long) (millis * NANOS_PER_MILLI));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a double representing microseconds to a {@link Duration} instance.
|
||||
* <p>
|
||||
*
|
||||
* @param micros the microseconds to be converted
|
||||
* @return a {@link Duration} instance representing the given microseconds
|
||||
*/
|
||||
public static Duration microsToDuration(double micros) {
|
||||
return Duration.ofNanos((long) (micros * NANOS_PER_MICRO));
|
||||
}
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(NetworkUtils.class);
|
||||
|
||||
private LatencyParser latencyParser = new LatencyParser();
|
||||
|
@ -286,6 +298,7 @@ public class NetworkUtils {
|
|||
}
|
||||
|
||||
public enum IpPingMethodEnum {
|
||||
DISABLED,
|
||||
JAVA_PING,
|
||||
WINDOWS_PING,
|
||||
IPUTILS_LINUX_PING,
|
||||
|
@ -414,7 +427,25 @@ public class NetworkUtils {
|
|||
|
||||
// The return code is 0 for a successful ping. 1 if device didn't respond and 2 if there is another error like
|
||||
// network interface not ready.
|
||||
return new PingResult(proc.waitFor() == 0, Duration.between(execStartTime, Instant.now()));
|
||||
int result = proc.waitFor();
|
||||
if (result != 0) {
|
||||
return new PingResult(false, Duration.between(execStartTime, Instant.now()));
|
||||
}
|
||||
|
||||
PingResult pingResult = new PingResult(true, Duration.between(execStartTime, Instant.now()));
|
||||
try (BufferedReader r = new BufferedReader(new InputStreamReader(proc.getInputStream()))) {
|
||||
String line = r.readLine();
|
||||
while (line != null) {
|
||||
Duration responseTime = latencyParser.parseLatency(line);
|
||||
if (responseTime != null) {
|
||||
pingResult.setResponseTime(responseTime);
|
||||
return pingResult;
|
||||
}
|
||||
line = r.readLine();
|
||||
}
|
||||
}
|
||||
|
||||
return pingResult;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -41,8 +41,12 @@ thing-type.config.network.pingdevice.retry.label = Retry
|
|||
thing-type.config.network.pingdevice.retry.description = How many refresh interval cycles should a presence detection should take place, before the device is stated as offline
|
||||
thing-type.config.network.pingdevice.timeout.label = Timeout
|
||||
thing-type.config.network.pingdevice.timeout.description = States how long to wait for a response (in ms), before if a device is stated as offline
|
||||
thing-type.config.network.pingdevice.useArpPing.label = Use ARP Ping
|
||||
thing-type.config.network.pingdevice.useArpPing.description = Set to true if the presence detection is allowed to use arp ping. This can speed up presence detection, but may lead to inaccurate ping latency measurements. Switch off if you want to use this for ping latency monitoring.
|
||||
thing-type.config.network.pingdevice.useIOSWakeUp.label = Use iOS Wake Up
|
||||
thing-type.config.network.pingdevice.useIOSWakeUp.description = Set to true if the device presence detection should be performed for an iOS device like iPhone or iPads. An additional port knock is performed before a ping.
|
||||
thing-type.config.network.pingdevice.useIcmpPing.label = Use ICMP Ping
|
||||
thing-type.config.network.pingdevice.useIcmpPing.description = Set to true if the presence detection is allowed to use icmp ping. If you are monitoring network latency using arping, you should switch this off to prevent mixing results with arp ping results.
|
||||
thing-type.config.network.servicedevice.hostname.label = Hostname or IP
|
||||
thing-type.config.network.servicedevice.hostname.description = Hostname or IP of the device
|
||||
thing-type.config.network.servicedevice.macAddress.label = MAC Address
|
||||
|
|
|
@ -70,6 +70,23 @@
|
|||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
|
||||
<parameter name="useArpPing" type="boolean" required="true">
|
||||
<label>Use ARP Ping</label>
|
||||
<default>true</default>
|
||||
<description>Set to true if the presence detection is allowed to use arp ping. This can speed up presence detection,
|
||||
but may lead to inaccurate ping latency measurements. Switch off if you want to use this for ping latency
|
||||
monitoring.</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
|
||||
<parameter name="useIcmpPing" type="boolean" required="true">
|
||||
<label>Use ICMP Ping</label>
|
||||
<default>true</default>
|
||||
<description>Set to true if the presence detection is allowed to use icmp ping. If you are monitoring network
|
||||
latency using arping, you should switch this off to prevent mixing results with arp ping results.</description>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
|
|
Loading…
Reference in New Issue