Skip to content

Commit 3c90a0d

Browse files
authored
[network] Make icmp ping and arp ping optional by presence thing (openhab#18083)
Signed-off-by: Mark Herwege <[email protected]>
1 parent 9f9bb7b commit 3c90a0d

File tree

8 files changed

+136
-19
lines changed

8 files changed

+136
-19
lines changed

bundles/org.openhab.binding.network/README.md

+13
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ Use the following options for a **network:pingdevice**:
5454
- **timeout:** How long the ping will wait for an answer, in milliseconds. Default: `5000` (5 seconds).
5555
- **refreshInterval:** How often the device will be checked, in milliseconds. Default: `60000` (one minute).
5656
- **useIOSWakeUp:** When set to true, an additional port knock is performed before a ping. Default: `true`.
57+
- **useArpPing:** When set to true if the presence detection is allowed to use arp ping.
58+
This can speed up presence detection, but may lead to inaccurate ping latency measurements.
59+
Switch off if you want to use this for ping latency monitoring. Default: `true`.
60+
- **useIcmpPing:** When set to true if the presence detection is allowed to use icmp ping.
61+
When also using arp ping, the latency measurements will not be comparable.
62+
Switch off if you rather want to use arp ping latency monitoring. Default: `true`.
5763
- **networkInterfaceNames:** The network interface names used for communicating with the device.
5864
Limiting the network interfaces reduces the load when arping and Wake-on-LAN are used.
5965
Use comma separated values when using textual config. Default: empty (all network interfaces).
@@ -190,6 +196,7 @@ demo.things:
190196

191197
```java
192198
Thing network:pingdevice:devicename [ hostname="192.168.0.42", macAddress="6f:70:65:6e:48:41", useIOSWakeUp="false" ]
199+
Thing network:pingdevice:router [ hostname="192.168.0.1", useArpPing="false" ]
193200
Thing network:speedtest:local "SpeedTest 50Mo" @ "Internet" [url="https://bouygues.testdebit.info/", fileName="50M.iso"]
194201
```
195202

@@ -199,6 +206,8 @@ demo.items:
199206
Switch MyDevice { channel="network:pingdevice:devicename:online" }
200207
Number:Time MyDeviceResponseTime { channel="network:pingdevice:devicename:latency" }
201208

209+
Number:Time MyRouterResponseTime { channel="network:pingdevice:router:latency" }
210+
202211
String Speedtest_Running "Test running ... [%s]" {channel="network:speedtest:local:isRunning"}
203212
Number:Dimensionless Speedtest_Progress "Test progress [%d %unit%]" {channel="network:speedtest:local:progress"}
204213
Number:DataTransferRate Speedtest_ResultDown "Downlink [%.2f %unit%]" {channel="network:speedtest:local:rateDown"}
@@ -218,6 +227,10 @@ sitemap demo label="Main Menu"
218227
Text item=MyDeviceResponseTime label="Device Response Time [%s]"
219228
}
220229

230+
Frame {
231+
Text item=MyRouterResponseTime label="Router Response Time [%s]"
232+
}
233+
221234
Frame label="SpeedTest" {
222235
Text item=Speedtest_Start
223236
Switch item=Speedtest_Running

bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/NetworkHandlerConfiguration.java

+2
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,7 @@ public class NetworkHandlerConfiguration {
3232
public Integer refreshInterval = 60000;
3333
public Integer timeout = 5000;
3434
public boolean useIOSWakeUp = true;
35+
public boolean useArpPing = true;
36+
public boolean useIcmpPing = true;
3537
public Set<String> networkInterfaceNames = Set.of();
3638
}

bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/PresenceDetection.java

+45-8
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,10 @@ public class PresenceDetection implements IPRequestReceivedCallback {
7171
private String ipPingState = "Disabled";
7272
protected String arpPingUtilPath = "";
7373
private ArpPingUtilEnum arpPingMethod = ArpPingUtilEnum.DISABLED;
74-
protected @Nullable IpPingMethodEnum pingMethod = null;
74+
protected @Nullable IpPingMethodEnum pingMethod = IpPingMethodEnum.DISABLED;
7575
private boolean iosDevice;
76+
private boolean useArpPing;
77+
private boolean useIcmpPing;
7678
private Set<Integer> tcpPorts = new HashSet<>();
7779

7880
private Duration refreshInterval = Duration.ofMinutes(1);
@@ -188,7 +190,7 @@ public void setPreferResponseTimeAsLatency(boolean preferResponseTimeAsLatency)
188190
public void setUseIcmpPing(@Nullable Boolean useSystemPing) {
189191
if (useSystemPing == null) {
190192
ipPingState = "Disabled";
191-
pingMethod = null;
193+
pingMethod = IpPingMethodEnum.DISABLED;
192194
} else if (useSystemPing) {
193195
final IpPingMethodEnum pingMethod = networkUtils.determinePingMethod();
194196
this.pingMethod = pingMethod;
@@ -220,12 +222,17 @@ private void setUseArpPing(boolean enable, @Nullable InetAddress destinationAddr
220222
* Sets the path to ARP ping.
221223
*
222224
* @param enable enable or disable ARP ping
223-
* @param arpPingUtilPath enableDHCPListen(useDHCPsniffing);
225+
* @param arpPingUtilPath path to Arping tool
226+
* @param arpPingUtilMethod Arping tool method
224227
*/
225228
public void setUseArpPing(boolean enable, String arpPingUtilPath, ArpPingUtilEnum arpPingUtilMethod) {
226-
setUseArpPing(enable, destination.getValue());
227-
this.arpPingUtilPath = arpPingUtilPath;
228-
this.arpPingMethod = arpPingUtilMethod;
229+
if (!enable) {
230+
arpPingMethod = ArpPingUtilEnum.DISABLED;
231+
} else {
232+
setUseArpPing(enable, destination.getValue());
233+
this.arpPingUtilPath = arpPingUtilPath;
234+
this.arpPingMethod = arpPingUtilMethod;
235+
}
229236
}
230237

231238
public String getArpPingState() {
@@ -256,6 +263,36 @@ public void setIOSDevice(boolean value) {
256263
iosDevice = value;
257264
}
258265

266+
/**
267+
* Return <code>true</code> if the device presence detection is also performed using arp ping. This gives
268+
* less accurate ping latency results when used for an IPv4 destination host.
269+
*/
270+
public boolean isUseArpPing() {
271+
return useArpPing;
272+
}
273+
274+
/**
275+
* Set to <code>true</code> if the device presence detection should also be performed using arp ping. This gives
276+
* less accurate ping latency results when used for an IPv4 destination host.
277+
*/
278+
public void setUseArpPing(boolean useArpPing) {
279+
this.useArpPing = useArpPing;
280+
}
281+
282+
/**
283+
* Return <code>true</code> if the device presence detection is also performed using icmp ping.
284+
*/
285+
public boolean isUseIcmpPing() {
286+
return useIcmpPing;
287+
}
288+
289+
/**
290+
* Set to <code>true</code> if the device presence detection should also be performed using icmp ping.
291+
*/
292+
public void setUseIcmPing(boolean useIcmpPing) {
293+
this.useIcmpPing = useIcmpPing;
294+
}
295+
259296
/**
260297
* Return the last seen value as an {@link Instant} or <code>null</code> if not yet seen.
261298
*/
@@ -329,7 +366,7 @@ public CompletableFuture<PresenceDetectionValue> performPresenceDetection() {
329366
Set<String> interfaceNames = null;
330367

331368
detectionChecks = tcpPorts.size();
332-
if (pingMethod != null) {
369+
if (pingMethod != IpPingMethodEnum.DISABLED) {
333370
detectionChecks += 1;
334371
}
335372
if (arpPingMethod.canProceed) {
@@ -385,7 +422,7 @@ public CompletableFuture<PresenceDetectionValue> performPresenceDetection() {
385422
}
386423

387424
// ICMP ping
388-
if (pingMethod != null) {
425+
if (pingMethod != IpPingMethodEnum.DISABLED) {
389426
addAsyncDetection(completableFutures, () -> {
390427
Thread.currentThread().setName("presenceDetectionICMP_" + hostname);
391428
if (pingMethod == IpPingMethodEnum.JAVA_PING) {

bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/handler/NetworkHandler.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,9 @@ void initialize(PresenceDetection presenceDetection) {
183183
presenceDetection.setIOSDevice(handlerConfiguration.useIOSWakeUp);
184184
// Hand over binding configurations to the network service
185185
presenceDetection.setUseDhcpSniffing(configuration.allowDHCPlisten);
186-
presenceDetection.setUseIcmpPing(configuration.allowSystemPings);
187-
presenceDetection.setUseArpPing(true, configuration.arpPingToolPath, configuration.arpPingUtilMethod);
186+
presenceDetection.setUseIcmpPing(handlerConfiguration.useIcmpPing ? configuration.allowSystemPings : null);
187+
presenceDetection.setUseArpPing(handlerConfiguration.useArpPing, configuration.arpPingToolPath,
188+
configuration.arpPingUtilMethod);
188189
}
189190

190191
this.retries = handlerConfiguration.retry.intValue();

bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/utils/LatencyParser.java

+19-7
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
*/
1313
package org.openhab.binding.network.internal.utils;
1414

15-
import static org.openhab.binding.network.internal.utils.NetworkUtils.millisToDuration;
15+
import static org.openhab.binding.network.internal.utils.NetworkUtils.*;
1616

1717
import java.time.Duration;
1818
import java.util.regex.Matcher;
@@ -31,7 +31,7 @@
3131
@NonNullByDefault
3232
public class LatencyParser {
3333

34-
private static final Pattern LATENCY_PATTERN = Pattern.compile(".*time=(.*) ?ms");
34+
private static final Pattern LATENCY_PATTERN = Pattern.compile(".*time=(.*) ?(u|m)s.*");
3535
private final Logger logger = LoggerFactory.getLogger(LatencyParser.class);
3636

3737
// This is how the input looks like on Mac and Linux:
@@ -43,18 +43,30 @@ public class LatencyParser {
4343
// 1 packets transmitted, 1 packets received, 0.0% packet loss
4444
// round-trip min/avg/max/stddev = 1.225/1.225/1.225/0.000 ms
4545

46+
// This is an example of an arping response on Linux:
47+
// arping -c 1 -i eth0 192.168.0.1
48+
// ARPING 192.168.0.1
49+
// 60 bytes from xx:xx:xx:xx:xx:xx (192.168.0.1): index=0 time=792.847 usec
50+
//
51+
// --- 192.168.0.1 statistics ---
52+
// 1 packets transmitted, 1 packets received, 0% unanswered (0 extra)
53+
// rtt min/avg/max/std-dev = 0.793/0.793/0.793/0.000 ms
54+
4655
/**
47-
* Examine a single ping command output line and try to extract the latency value if it is contained.
56+
* Examine a single ping or arping command output line and try to extract the latency value if it is contained.
4857
*
49-
* @param inputLine Single output line of the ping command.
50-
* @return Latency value provided by the ping command. <code>null</code> if the provided line did not contain a
51-
* latency value which matches the known patterns.
58+
* @param inputLine Single output line of the ping or arping command.
59+
* @return Latency value provided by the ping or arping command. <code>null</code> if the provided line did not
60+
* contain a latency value which matches the known patterns.
5261
*/
5362
public @Nullable Duration parseLatency(String inputLine) {
5463
logger.debug("Parsing latency from input {}", inputLine);
5564

5665
Matcher m = LATENCY_PATTERN.matcher(inputLine);
57-
if (m.find() && m.groupCount() == 1) {
66+
if (m.find() && m.groupCount() >= 2) {
67+
if ("u".equals(m.group(2))) {
68+
return microsToDuration(Double.parseDouble(m.group(1).replace(",", ".")));
69+
}
5870
return millisToDuration(Double.parseDouble(m.group(1).replace(",", ".")));
5971
}
6072

bundles/org.openhab.binding.network/src/main/java/org/openhab/binding/network/internal/utils/NetworkUtils.java

+33-2
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,10 @@
5555
public class NetworkUtils {
5656

5757
/**
58-
* Nanos per millisecond.
58+
* Nanos per millisecond and microsecond.
5959
*/
6060
private static final long NANOS_PER_MILLI = 1000_000L;
61+
private static final long NANOS_PER_MICRO = 1000L;
6162

6263
/**
6364
* Converts a {@link Duration} to milliseconds.
@@ -84,6 +85,17 @@ public static Duration millisToDuration(double millis) {
8485
return Duration.ofNanos((long) (millis * NANOS_PER_MILLI));
8586
}
8687

88+
/**
89+
* Converts a double representing microseconds to a {@link Duration} instance.
90+
* <p>
91+
*
92+
* @param micros the microseconds to be converted
93+
* @return a {@link Duration} instance representing the given microseconds
94+
*/
95+
public static Duration microsToDuration(double micros) {
96+
return Duration.ofNanos((long) (micros * NANOS_PER_MICRO));
97+
}
98+
8799
private final Logger logger = LoggerFactory.getLogger(NetworkUtils.class);
88100

89101
private LatencyParser latencyParser = new LatencyParser();
@@ -286,6 +298,7 @@ public ArpPingUtilEnum determineNativeArpPingMethod(String arpToolPath) {
286298
}
287299

288300
public enum IpPingMethodEnum {
301+
DISABLED,
289302
JAVA_PING,
290303
WINDOWS_PING,
291304
IPUTILS_LINUX_PING,
@@ -414,7 +427,25 @@ public enum ArpPingUtilEnum {
414427

415428
// The return code is 0 for a successful ping. 1 if device didn't respond and 2 if there is another error like
416429
// network interface not ready.
417-
return new PingResult(proc.waitFor() == 0, Duration.between(execStartTime, Instant.now()));
430+
int result = proc.waitFor();
431+
if (result != 0) {
432+
return new PingResult(false, Duration.between(execStartTime, Instant.now()));
433+
}
434+
435+
PingResult pingResult = new PingResult(true, Duration.between(execStartTime, Instant.now()));
436+
try (BufferedReader r = new BufferedReader(new InputStreamReader(proc.getInputStream()))) {
437+
String line = r.readLine();
438+
while (line != null) {
439+
Duration responseTime = latencyParser.parseLatency(line);
440+
if (responseTime != null) {
441+
pingResult.setResponseTime(responseTime);
442+
return pingResult;
443+
}
444+
line = r.readLine();
445+
}
446+
}
447+
448+
return pingResult;
418449
}
419450

420451
/**

bundles/org.openhab.binding.network/src/main/resources/OH-INF/i18n/network.properties

+4
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,12 @@ thing-type.config.network.pingdevice.retry.label = Retry
4141
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
4242
thing-type.config.network.pingdevice.timeout.label = Timeout
4343
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
44+
thing-type.config.network.pingdevice.useArpPing.label = Use ARP Ping
45+
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.
4446
thing-type.config.network.pingdevice.useIOSWakeUp.label = Use iOS Wake Up
4547
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.
48+
thing-type.config.network.pingdevice.useIcmpPing.label = Use ICMP Ping
49+
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.
4650
thing-type.config.network.servicedevice.hostname.label = Hostname or IP
4751
thing-type.config.network.servicedevice.hostname.description = Hostname or IP of the device
4852
thing-type.config.network.servicedevice.macAddress.label = MAC Address

bundles/org.openhab.binding.network/src/main/resources/OH-INF/thing/thing-types.xml

+17
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,23 @@
7070
<advanced>true</advanced>
7171
</parameter>
7272

73+
<parameter name="useArpPing" type="boolean" required="true">
74+
<label>Use ARP Ping</label>
75+
<default>true</default>
76+
<description>Set to true if the presence detection is allowed to use arp ping. This can speed up presence detection,
77+
but may lead to inaccurate ping latency measurements. Switch off if you want to use this for ping latency
78+
monitoring.</description>
79+
<advanced>true</advanced>
80+
</parameter>
81+
82+
<parameter name="useIcmpPing" type="boolean" required="true">
83+
<label>Use ICMP Ping</label>
84+
<default>true</default>
85+
<description>Set to true if the presence detection is allowed to use icmp ping. If you are monitoring network
86+
latency using arping, you should switch this off to prevent mixing results with arp ping results.</description>
87+
<advanced>true</advanced>
88+
</parameter>
89+
7390
</config-description>
7491
</thing-type>
7592

0 commit comments

Comments
 (0)