iPhone Presence Detection with hping3 and ARP

tutorial
exec
iphone
Tags: #<Tag:0x00007fadfc530118> #<Tag:0x00007fae02a4fc88> #<Tag:0x00007fae02a4fa80>

(Peter Ha) #21

Hi,

I am really thankful for this thread (surprisingly it seems that there is not much discussion going on in the web at all about this topic) as it seems to be the only way that works in a stable manner.
I chose perl to implement the above concept on a Raspi2. I am sure the script can be improved (I am not exactly a perl expert), but it works fine for me.

Input: IP Address
Output: 1=IP detected, 0=IP not deteced

I use 4 cron scripts in parallel (every 5 minutes) to detect presence of the 4 phones in my household, each cron-script invoking the below’s perl script and depending on the result triggers an openhab-rest-api to set a switch…

Script to detect iphone “detect_ip.pl”:

#!/usr/bin/perl -w
#
# script will try to reach ip address 30 times (hping3 not always "wakes up" the iphone) 
# wait time between the pings: a random number of 1 - 10 secs (would probably work if you fix the sleep to 10 secs as well)
#
#
# Input-Parameter: Ip-Address
# Output-Parameter: 1 = ip-device deteced, 0 = not detected
#


use strict;

my $ip = $ARGV[0];
if (not defined $ip) { die "no IP address!\n"; }

exit (&checkIPpresence);




sub checkIPpresence {

  # first flush arp entry for ip
  system "sudo ip neigh flush dev eth0 $ip";

  #### hping3 not always wakes up iphone - trying 30 times should be enough to be stable...

  my $counter = 30;

  while ($counter > 0) {

     $counter--;

     # hping3 to ip
     system "sudo hping3 -2 -c 10 -p 5353 -i u1 $ip -q >/dev/null 2>/dev/null";

     # wait 0.5 sec then check if mac address shows up in arp
     select(undef, undef, undef, 0.5);

     if (&checkARP) {
        return 1;  # 1 = true, 0 = false;
     }

     #wait between 1 and 10 secs then arp-check if mac is found
     sleep (1 + int rand(10));

  }

  return 0;  # 1 = true, 0 = false;

}



sub checkARP {

  my $arp = "sudo arp -an $ip";

  open (ARP, "$arp |") or die "cannot run arp '$arp'\n$!\n";

  my $i = 0;

  if ( defined (my $line = <ARP>) ) {
     chomp($line);
     close ARP;

     if ($line =~ /(.+:.+:.+:.+:.+:.+)/) {
          return 1;  # 1 = true, 0 = false;
     }

   }

   close ARP;
   return 0;  # 1 = true, 0 = false;

}

The cron-scripts executed every 5:

#!/usr/bin/perl -w

use strict;
use warnings;
use Data::Dumper;

# define openhab server IP, openhab switch-item name, and ip of iphone to be checked
my $openhabServerIP = "<your openhab server ip address>";
my $openhab_item = "<your openhab switch>";    
my $ip = "<ip address of iphone to be tested>";

# define system commands to turn switch on/off and IP of openhab server
my $openhabON  = "curl --header \"Content-Type: text/plain\" --request POST --data 'ON' http://$openhabServerIP:8080/rest/items/$openhab_item";
my $openhabOFF = "curl --header \"Content-Type: text/plain\" --request POST --data 'OFF' http://$openhabServerIP:8080/rest/items/$openhab_item";

# run the detection script and pass along the ip address of the phone,
# depending on the return code, set openhab item to on / off
system ("/home/pi/perl.sources/find_iphone/detect_ip.pl $ip");
my $returnCode = $? >> 8;
if ($returnCode) {      system ("$openhabON"); }
else {                  system ("$openhabOFF"); }


(Vasily) #22

Dear peter_h!
Can you provide your cron script too. I am not good in Linux, so I need to use this script to turn Switch using rest api on windows Openhab server.
Also, what i need to add to the clean Raspberian to use that script? About the same way I worked with 1-wire, so I have a additional raspberry server with only owfs.


(Peter Ha) #23

Hi Vasily,

I added the cron-script above. You can customize with your IP addresses and openhab switch and add it to your cron jobs to be executed every x minutes or so.
When starting with a plain Raspberian installation, you need to install hping3 (sudo apt-get install hping3), see also at the top of this thread

br Peter


(Matha Goram) #24

Sorry for the belated reply. Once I marshal sufficient experience, I will give your suggestion a try-out. I am a very elementary learner right now. Regards.


(Dominic Bonneau) #25

Hi, I was wondering if instead of using a hping3 script to wake the iPhone if we could use the tcp/udp binding ? I’m not on linux and hping3 is hard to install on Mac… also I prefer to have all my thing running in openhab with no external script


(Robert) #26

I am planning to create a solution that

  • uses the Exec 2 binding
  • calls a bash shell script for the arp command
  • doesn’t require sudo
  • allows for 2 command line parameters IP and MAC to be able to call it for 3 different phones (3 items bound to 3 exec things)
  • get the least number of required hpings to conserve battery
  • get the smallest interval between calls (19 calls at 10 second interval?). Target is < 5 minutes.

(Dominic Bonneau) #27

Sound great ! keep me posted !


(Robert) #28

I have improved my iPhone detection, based on the above information. The solution allows detection of multiple phones. You can integrate this in your current presence solution.

Required steps:

  1. Install hping3
  2. Allow use of sudo commands
  3. Create script
  4. Create items and rules

Install hping3

sudo apt-get install hping3

Allow use of sudo commands

Instead of adding directly to the sudoers file, I decided to add an inlude file /etc/sudoers.d/openhab, that is automatically picked up by the system.

sudo visudo -f /etc/sudoers.d/openhab

Paste this text in the file:

/etc/sudoers.d/openhab

openhab ALL=(ALL) NOPASSWD: /sbin/ip
openhab ALL=(ALL) NOPASSWD: /usr/sbin/hping3

If you use openHABian or a regular pi, and like to use the script from the commandline, you can duplicate these lines and replace openhab with openhabian or pi respectively.

Create script

The script iphone.sh will hping3 the iphone to wake it from deep sleep. It is required to be pinged more than once. Instead of retrying through a rule as posted above, I decided to loop in the script itself. The script is called by a “Time cron” rule in openHAB at an interval depending on your needs. Mine is 5 minutes. I have tried 1 minute. This works, but will have more impact on your phone’s battery life. The script will try 20 times (this is configurable in the script) to ping the device. If it still fails, the device is considered ‘away’.

The script can best be located in the OH scripts folder. For an apt-get based installation, including openHABian, this is /etc/openhab2/scripts.

cd /etc/openhab2/scripts
touch iphone.sh
chmod +x iphone.sh
nano iphone.sh

/etc/openhab2/scripts/iphone.sh

#!/bin/bash

# detect iphone by IP and MAC address.
# use MAC address too, to prevent false positives if IP might change
# return ON or OFF so output can be directly bound to a switch item

# number of retries, less is faster, but less accurate
MAXRETRIES=20

# exit immediately if no parameters supplied
if [ $# -lt 2 ]
  then
    echo "UNDEF"
    exit 1
fi

# Set variables
IP=$1
MAC=$2

COUNT=0
while [ ${COUNT} -lt ${MAXRETRIES} ];
do

  # Change dev and eth0 if needed
  sudo ip neigh flush dev eth0 ${IP}

  sudo hping3 -q -2 -c 10 -p 5353 -i u1 ${IP} >/dev/null 2>&1
  sleep .1

  # Only arp specific device, grep for a mac-address
  STATUS=`arp -an ${IP} | awk '{print $4}' | grep "${MAC}"`

  if [ ${#STATUS} -eq 17 ]; then
      # exit when phone is detected
      echo "ON"
      exit 0
  fi
  let COUNT=COUNT+1
  sleep .1
done

# consider away if reached max retries 
echo "OFF"

Create items and rules

iphone.items

Switch Phone1 "iPhone 1 [%s]" <present>
Switch Phone2 "iPhone 2 [%s]" <present>
Switch Phone3 "iPhone 3 [%s]" <present>

iphone.rules

rule "Find My iPhone"
when
    Time cron "0 */5 * * * ? *" // every 5 minutes
then
    val Number timeout = 10*1000 // give the script some time to finish, to capture the result

    // replace 0.0.0.0 with your ip address
    // replace 00:00:00:00:00:00 with your mac address
    var String result1 = executeCommandLine('/etc/openhab2/scripts/iphone.sh@@0.0.0.0@@00:00:00:00:00:00', timeout)
    var String result2 = executeCommandLine('/etc/openhab2/scripts/iphone.sh@@0.0.0.0@@00:00:00:00:00:00', timeout)
    var String result3 = executeCommandLine('/etc/openhab2/scripts/iphone.sh@@0.0.0.0@@00:00:00:00:00:00', timeout)

    Phone1.postUpdate(result1)
    Phone2.postUpdate(result2)
    Phone3.postUpdate(result3)

end

TODO:

  • DRY: use lambdas for reusing code per phone.
  • get Exec2 binding working to replace current cron based rule
  • optimize detection timing
  • (perhaps) use REST api to sets items directly (with $3 for item)

Dockerized OH iPhone Detection with HPING3 and sensorReporter
(Dominic Bonneau) #29

I’m on Mac son I can’t install hping3… I mean there is some way but I don’t think it’s gonna work or I will need to remake all the script to make it compatible so …I need another way


(Robert) #30

Have you tried brew?


http://brewformulas.org/Hping

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
brew install hping

(Dominic Bonneau) #31

No I tought it was diferent so the script will not work … I’m gonna try it


(Dominic Bonneau) #32

it’s not working

I got this :

line 8: ip: command not found
usage: arp [-n] [-i interface] hostname
arp [-n] [-i interface] [-l] -a
arp -d hostname [pub] [ifscope interface]
arp -d [-i interface] -a
arp -s hostname ether_addr [temp] [reject] [blackhole] [pub [only]] [ifscope interface]
arp -S hostname ether_addr [temp] [reject] [blackhole] [pub [only]] [ifscope interface]
arp -f filename
Phone 192.168.111.15 is not present


( ) #33

Great one Robert!

Maybe @Maurits28 could reference your solution in the first posting so others can realistically find it… @Maurits28 you could also transform your first posting into a wiki posting so Robert can edit it himself. But that’s of course totally up to you!


(Robert) #34

If you are really persistent, Google should be your friend, or someone with Mac Terminal skills.

I googled for ‘mac ip command’ and found this:

You might give it a try. It suggests to ‘brew’ iproute2mac or use the ifconfig command.

And you can flush the arp table, too:

https://scottlinux.com/2011/08/03/clear-or-flush-arp-cache-in-os-x/#comment-892


(Halis) #35

@rtvb It it strange that I am able to detect my phone if I run the script from my console but if the script is executed by the rule, my phone is not found in my network.
Is there a difference if openhab is running ths script_


(Robert) #36

Have you tried a variation of the script, that logs its output to a file and see what happens? See my modified example script down here. Make sure user openhab has write access to the log file.

#!/bin/bash

# v1.1 2017-04-11: VARIATION WITH LOGGING INCLUDED

# detect iphone by IP and MAC address.
# use MAC address too, to prevent false positives if IP might change
# return ON or OFF so output can be directly bound to a switch item

# number of retries, less is faster, but less accurate
MAXRETRIES=20

# path to log file - to use logging, uncomment all lines containing 'echo >> logfile'
LOGFILE=/etc/openhab2/scripts/iphone.log

# exit immediately if no parameters supplied
if [ $# -lt 2 ]
  then
    # echo `date '+%F %T'` - Required parameters not specified >> ${LOGFILE}
    echo "UNDEF"
    exit 1
fi

# Set variables
IP=$1
MAC=$2

# echo `date '+%F %T'` - Script started for ${IP} and ${MAC} >> ${LOGFILE}

COUNT=0
while [ ${COUNT} -lt ${MAXRETRIES} ];
do

  # echo `date '+%F %T'` - Try ${COUNT} >> ${LOGFILE}

  # Change dev and eth0 if needed
  sudo ip neigh flush dev eth0 ${IP}

  sudo hping3 -q -2 -c 10 -p 5353 -i u1 ${IP} >/dev/null 2>&1
  sleep .1

  # Only arp specific device, grep for a mac-address
  STATUS=`arp -an ${IP} | awk '{print $4}' | grep "${MAC}"`

  if [ ${#STATUS} -eq 17 ]; then
      # exit when phone is detected

      # echo `date '+%F %T'` - Phone detected >> ${LOGFILE}

      echo "ON"
      exit 0
  fi
  let COUNT=COUNT+1
  sleep .1
done

# consider away if reached max retries 
# echo `date '+%F %T'` - Phone not detected >> ${LOGFILE}
echo "OFF"

(Halis) #37

Thank you for help. I am not sure but I assume that the script is not running up to the last line of code if it is not finding a phone. The log contains just the last ‘Try’ entry but it is never achieving the command ‘echo off’.
Also the script is not running correct. It results in ‘Phone detected’ although I am not connected to the network. Otherwise If I run the script from the command line the detection is correct.

2017-04-12 22:10:00 - Try 0
2017-04-12 22:10:00 - Try 1
2017-04-12 22:10:01 - Try 2
2017-04-12 22:10:01 - Try 3
2017-04-12 22:10:02 - Try 4
2017-04-12 22:10:02 - Try 5
2017-04-12 22:10:03 - Try 6
2017-04-12 22:10:03 - Phone detected
2017-04-12 22:10:03 - Try 0
2017-04-12 22:10:04 - Try 1
2017-04-12 22:10:04 - Try 2
2017-04-12 22:10:05 - Try 3
2017-04-12 22:10:05 - Try 4
2017-04-12 22:10:06 - Try 5
2017-04-12 22:10:06 - Try 6
2017-04-12 22:10:07 - Try 7
2017-04-12 22:10:07 - Try 8
2017-04-12 22:10:08 - Try 9
2017-04-12 22:10:08 - Try 10
2017-04-12 22:10:09 - Try 11
2017-04-12 22:10:09 - Try 12
2017-04-12 22:10:10 - Try 13
2017-04-12 22:10:10 - Try 14
2017-04-12 22:10:11 - Try 15
2017-04-12 22:10:12 - Try 16
2017-04-12 22:10:12 - Try 17
2017-04-12 22:10:13 - Try 18
2017-04-12 22:10:13 - Try 19
2017-04-12 22:11:00 - Try 0
2017-04-12 22:11:00 - Try 1
2017-04-12 22:11:00 - Phone detected
2017-04-12 22:11:01 - Try 0
2017-04-12 22:11:01 - Try 1
2017-04-12 22:11:02 - Try 2
2017-04-12 22:11:02 - Try 3
2017-04-12 22:11:03 - Try 4
2017-04-12 22:11:03 - Try 5
2017-04-12 22:11:04 - Try 6
2017-04-12 22:11:04 - Try 7
2017-04-12 22:11:05 - Try 8
2017-04-12 22:11:05 - Try 9
2017-04-12 22:11:06 - Try 10
2017-04-12 22:11:06 - Try 11
2017-04-12 22:11:07 - Try 12
2017-04-12 22:11:07 - Try 13
2017-04-12 22:11:08 - Try 14
2017-04-12 22:11:08 - Try 15
2017-04-12 22:11:09 - Try 16
2017-04-12 22:11:09 - Try 17
2017-04-12 22:11:10 - Try 18
2017-04-12 22:11:10 - Try 19
2017-04-12 22:12:00 - Try 0
2017-04-12 22:12:00 - Try 1
2017-04-12 22:12:01 - Try 2
2017-04-12 22:12:01 - Phone detected
2017-04-12 22:12:01 - Try 0
2017-04-12 22:12:02 - Try 1
2017-04-12 22:12:02 - Try 2
2017-04-12 22:12:03 - Try 3
2017-04-12 22:12:03 - Try 4
2017-04-12 22:12:04 - Try 5
2017-04-12 22:12:04 - Try 6
2017-04-12 22:12:05 - Try 7
2017-04-12 22:12:05 - Try 8
2017-04-12 22:12:06 - Try 9
2017-04-12 22:12:06 - Try 10
2017-04-12 22:12:07 - Try 11
2017-04-12 22:12:07 - Try 12
2017-04-12 22:12:08 - Try 13
2017-04-12 22:12:08 - Try 14
2017-04-12 22:12:09 - Try 15
2017-04-12 22:12:09 - Try 16
2017-04-12 22:12:10 - Try 17
2017-04-12 22:12:10 - Try 18
2017-04-12 22:12:11 - Try 19


(Robert) #38

I have noticed some glitches in my own use of the modified script, too. I will have a look at it the coming days.


(Maurits) #39

What I experienced during tests with the original script is that if you do the pings too close after each other, the results are often not reliable. Therefore I use an interval of one minute between the tests. It might help to test with one minute or 30 secs to get a reliable reading.


(Dominic Bonneau) #40

Little question :

It work really well with my girlfriend iPhone I have 0 issue ! But with mine … My iPhone goes offline really often … I try to set up a rules that said wait 20 min before declare my iPhone as gone but it does not fix it …

Could it be something like my iPhone 7 have more protection from ping when he sleep ? Or a physical issue with my iPhone ?

Does anyone have an iPhone 7 and make this work well ?