├── LICENSE ├── README.md ├── airodump ├── airodumpscan ├── airodumpscan.py ├── b1300defconfig ├── collectd.conf ├── input.txt ├── lantiqdefconfig ├── presence.mustache ├── tcpdumpscan.py ├── utils └── find3-openwrt-scanner │ ├── Makefile │ └── files │ ├── tcpdumpscan.config │ ├── tcpdumpscan.init │ └── tcpdumpscan.sh ├── wr703n-8Mb.patch └── wr703ndefconfig /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 jekkos 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # find3-openwrt-scanner # 2 | This folder contains a couple of scripts that can be used with an OpenWrt router and turn it into as a [find3](https://www.internalpositioning.com) cli scanner. 3 | 4 | All of this runs on two [GLiInet b-1300 model](https://www.gl-inet.com/products/gl-b1300/) routers running a custom snapshot build. 5 | 6 | The setup consists of a monitor interface on which tcpdump will log probe requests, which will be submitted by a pythnon script to the 7 | find3 server backend. 8 | 9 | ## Requirements ## 10 | 11 | First make sure you find a usb dongle that has monitor mode enabled on OpenWrt. For this I used an rtl8192cu chipset which can be found 12 | in those rtl8818cus dongles, which became very popular for use with a raspberry pi. 13 | 14 | You can choose to run the scanner using `airodump-ng` or `tcpdump`. 15 | Initially I wanted to use airodump but for some reason it always stopped the packet capture (probably a driver issue). This issue was caused 16 | by the alternative ieee80211 rtl8xxx linux driver that did not seem to be very stable for my specific hardware. 17 | 18 | The scripts can now also be installed using a prepackaged ipk added here in the [release](https://github.com/jekkos/find3-openwrt-scanner/releases/tag/v1) section. 19 | 20 | ## Configuration ## 21 | 22 | ### wireless driver + monitor mode ### 23 | To enable the dongle in OpenWrt, first install the wifi driver 24 | 25 | * In my case I installed the driver using `opkg install kmod-rtl8192cu`. 26 | * Next you need to tell OpenWrt userspace that you want to use this dongle in monitor mode by typing `wifi config`. 27 | This will add the needed config section to the `/etc/config/wireless` configuration file. Now open this file with your editor of choice. 28 | 29 | * Enable the dongle by setting the `disabled` flag to 0 for the newly created interface 30 | * Change the `mode` line to `mode 'monitor'` to enable monitor mode. Remove all lines related to authentication (`key`, `ssid`, `encryption`) 31 | * Start the interface by `wifi up `. You should see the packet count increasing when typing `ifconfig` 32 | * Don't forget to replace the last value with the interface name that was added in the config file. 33 | 34 | ## Installation ## 35 | Over time I tried three different setups. The most stable (and recommended one) is the *tcpdumpscan + ash scanner* variant. This is also 36 | the version that is packaged and available for download under the release section. 37 | 38 | ### tcpdump + ash scanner ### 39 | If you are on a more constrained device, then using OpenWrt binaries + some shell scripts might be most appropriate. In my case WR703n router 40 | was not powerful enough to run the scripts in python3. Only dependencies here are libubox (preinstalled), tcpdump and curl 41 | 42 | * Install curl + tcpdump using `opkg update && opkg install tcpdump curl` on the device. 43 | * Copy the `find3_1_all.ipk` file in the (release)[https://github.com/jekkos/find3-openwrt-scanner/releases/tag/v1] section to the device using `scp find3_1_all.ipk root@:/tmp 44 | * Open an SSH session to the device and install using `opkg install /tmp/find3_1_all.ipk' 45 | * Adapt the `URL` and `FAMILY` variables in `/etc/config/tcpdumpscan`. 46 | * Hit `/etc/init.d/tcpdumpscan restart` to start scanning. 47 | * Finally type `/etc/init.d/tcpdumpscan enable` to enable scanning on startup. 48 | 49 | ### tcpdump + python scanner ### 50 | You need to setup tcpdump and the needed python packages so you can submit fingerprints to the find3 server. Currently I have not foreseen an .ipk for this version. 51 | 52 | * Enter following commnad `opkg install tcpdump python3-codecs python3-light python3-email python3-openssl` 53 | * Adapt the `URL` and `FAMILY` variables and Copy the `tcpdumpscan` procd init script to `/etc/init.d` using scp. 54 | * Copy the tcpdumpscan.py using scp to `/root` directory 55 | * type `/etc/init.d/tcpdumpscan start` to start scanning 56 | * Finally type `/etc/init.d/tcpdumpscan enable` to enable both on startup. 57 | 58 | Log output can be monitored using `logread` in the router's SSH session. 59 | 60 | ### airodump-ng + python scanner ### 61 | This setup might give you more control on how the wifi driver behaves in monitor mode by using specific airodump-ng parameters. I stepped away from it as changing the wifi 62 | channels didn't seem to play nicely with the rtl8192cu (it often freezed the whole device). 63 | 64 | * First install aircrack suite `opkg install aircrack-ng python3-codecs python3-light python3-email python3-openssl` 65 | * Adapt the `URL` and `FAMILY` variables and Copy the `airodump` and `airodumpscan` procd init scripts to `/etc/init.d` using scp. 66 | * Finally type `/etc/init.d/airodump enable` and `/etc/init.d/airodumpscan enable` to enable both on startup. 67 | 68 | ## OpenWrt build instructions ## 69 | 70 | If you want to include the tcpdumpscan scripts in your custom OpenWrt 18.06 build, you should do the following 71 | 72 | * Open `feed.conf.default` the Openwrt repository folder and add `src-git find3 https://github.com/jekkos/find3-openwrt-scanner.git` to the bottom of the file. 73 | * Then type `./scripts/feeds update find3 && ./scripts/feeds install` 74 | * When this command is completed, you should see a new entry under the `Utilities` section after starting `make menuconfig`. Then choose to include in the base image or to build as a package only. 75 | 76 | ### collectd ### 77 | Not needed for find3 but nice as an addon to use with grafana and influxdb 78 | 79 | ``` 80 | opkg install collectd-mod-network collectd-mod-uptime collectd-mod-interface collectd-mod-memory collectd-mod-cpu collectd-mod-load collectd-mod-iwinfo 81 | /etc/init.d/collectd enable 82 | ``` 83 | A cool dashboard can be found (here)[https://grafana.com/grafana/dashboards/3484] 84 | 85 | ## OpenWrt b-1300 & wr703n 8Mb build config ## 86 | Upgrading vanilla OpenWrt snapshots on a router is quite time intensive. After you install a new snapshot, you need to add the python and other packages again manually. Also this setup is using mesh functionality from `wpad-mesh` which is not in a default OpenWrt build. This meant that after every upgade the router lost internet connection. In this case it's easier to include all package upfront in the root filesystem. The config file in `diffconfig` version which can be easily used to build newer versions of OpenWrt with the same additional packages. The config contains all required python and collectd packages, as well as the rtl8192cu wifi kernel driver. 87 | 88 | Just copy the `b1300defconfig` or `wr703ndeconfig` file to your OpenWrt root folder. Mind that the latter requires a 8Mb flash. 89 | 90 | * In case you want to build for wr703n apply the patch first using `git apply wr703n-8Mb.patch` 91 | * Type `cat b1300defconfig >> .config` or `cat wr703ndefconfig >> .config` depending on your router model 92 | * Next hit `make defconfig` to create a full build config with this diff applied 93 | * Type `make V=j` to build the image 94 | * scp `openwrt-ipq40xx-glinet_gl-b1300-squashfs-sysupgrade.bin` to `/tmp` on the router 95 | * Open a shell to the router and hit `sysupgrade /tmp/openwrt-ipq40xx-glinet_gl-b1300-squashfs-sysupgrade.bin` 96 | 97 | -------------------------------------------------------------------------------- /airodump: -------------------------------------------------------------------------------- 1 | #!/bin/sh /etc/rc.common 2 | 3 | USE_PROCD=1 4 | START=90 5 | 6 | find_monitor() { 7 | local phys=$(ls /sys/class/ieee80211/) 8 | for phy in $phys; do 9 | local type=$(cat /sys/class/ieee80211/$phy/device/net/*/type) 10 | if [ $type -ne 1 -a $type -ne 0 ]; then 11 | IFACE=$(ls /sys/class/ieee80211/$phy/device/net) 12 | else 13 | sleep 3 14 | fi 15 | done 16 | } 17 | 18 | start_service() { 19 | find_monitor 20 | [ ! -z "$IFACE" ] || (echo "No interfaces found in monitor mode. Bailing out." && return 1) 21 | 22 | procd_open_instance tcpdumpscan 23 | procd_set_param command python3 /root/tcpdumpscan.py -i $IFACE -u $URL -f $FAMILY 24 | procd_set_param respawn ${respawn_threshold:-3600} 25 | procd_set_param stdout 1 26 | procd_set_param stderr 1 27 | procd_close_instance 28 | } 29 | -------------------------------------------------------------------------------- /airodumpscan: -------------------------------------------------------------------------------- 1 | #!/bin/sh /etc/rc.common 2 | 3 | USE_PROCD=1 4 | START=91 5 | 6 | URL=https://cloud.internalpositioning.com 7 | FAMILY= 8 | CSV=/tmp/airodumpscan.csv 9 | 10 | start_service() { 11 | [ -f $CSV ] || (echo "No airodump csv file found. Start airodump service first" && return 1) 12 | 13 | procd_open_instance airodumpscan 14 | procd_set_param command python3 /root/airodumpscan.py -i $CSV -u $URL 15 | procd_set_param respawn ${respawn_threshold:-3600} 16 | procd_set_param stdout 1 17 | procd_set_param stderr 1 18 | procd_close_instance 19 | } 20 | -------------------------------------------------------------------------------- /airodumpscan.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import json 4 | import argparse 5 | from datetime import datetime 6 | import os.path 7 | import csv 8 | import urllib.request 9 | import time 10 | import socket 11 | import sched 12 | import ssl 13 | 14 | def main(): 15 | parser = argparse.ArgumentParser(description="Submit find3 passive scan results") 16 | parser.add_argument("-p", dest="interval", type=int, help="The polling interval in seconds", default=5) 17 | parser.add_argument("-u", dest="url", required=True, help="The find3 REST endpoint url", default="https://cloud.internalpositioning.com") 18 | parser.add_argument("-f", dest="family", required=True, help="The find3 family identifier") 19 | parser.add_argument("-i", dest="inputfile", required=True, type=lambda file: is_valid_file(parser, file), help="The csv file in which airodump writes the scan results") 20 | args = parser.parse_args() 21 | ssl._create_default_https_context = ssl._create_unverified_context 22 | s = sched.scheduler(time.time, time.sleep) 23 | parser = SignalParser(args.inputfile, args.family, args.url, args.interval, s) 24 | parser.poll() 25 | 26 | def is_valid_file(parser, arg): 27 | if not os.path.exists(arg): 28 | parser.error("The file %s does not exist!" % arg) 29 | else: 30 | return arg 31 | 32 | class SignalParser(): 33 | 34 | def __init__(self, inputfile, family, url, interval, scheduler): 35 | self.family = family 36 | self.url = url 37 | self.inputfile = inputfile 38 | self.interval = interval 39 | self.scheduler = scheduler 40 | self.last_request = datetime.now() 41 | 42 | def poll(self): 43 | payload = self.build_payload() 44 | if payload is not None: 45 | clen = len(payload) 46 | headers = {'Content-Type': 'application/json', 'Content-Length': clen} 47 | req = urllib.request.Request(self.url + '/passive', payload, headers, method='POST') 48 | f = urllib.request.urlopen(req) 49 | response = f.read() 50 | print("respons: %s" % response) 51 | f.close() 52 | else: 53 | print("No probe requests found. Waiting for next interval") 54 | self.scheduler.enter(self.interval, 1, self.poll) 55 | self.scheduler.run() 56 | 57 | def build_payload(self): 58 | stations = self.read_scanfile() 59 | if len(stations) == 0: 60 | return None 61 | body = {'f': self.family} 62 | body['t'] = int(time.time()) 63 | body['d'] = socket.gethostname() 64 | body['s'] = {'wifi': stations} 65 | return str(json.dumps(body)).encode('utf8') 66 | 67 | def read_scanfile(self): 68 | stations = {} 69 | start_read = False 70 | try: 71 | with open(self.inputfile) as scan_file: 72 | csv_reader = csv.reader(scan_file, delimiter=',') 73 | for row in csv_reader: 74 | if len(row) == 0: 75 | continue 76 | if start_read: 77 | last_seen = datetime.strptime(row[2].strip(), "%Y-%m-%d %H:%M:%S") 78 | if last_seen > self.last_request: 79 | power = int(row[3].strip()) 80 | station = row[0].strip().lower() 81 | stations[station] = power 82 | print("Found %s with signal power of %d dBm at %s" % (station, int(power), last_seen)) 83 | self.last_request = datetime.now() 84 | if row[0] == "Station MAC": 85 | start_read = True 86 | except Exception as e : 87 | print("Failed to read %s: %s" % (self.inputfile, str(e))) 88 | 89 | return stations 90 | 91 | if __name__ == "__main__": 92 | main() 93 | -------------------------------------------------------------------------------- /b1300defconfig: -------------------------------------------------------------------------------- 1 | CONFIG_TARGET_ipq40xx=y 2 | CONFIG_TARGET_ipq40xx_DEVICE_glinet_gl-b1300=y 3 | CONFIG_TARGET_BOARD="ipq40xx" 4 | CONFIG_BUSYBOX_CUSTOM=y 5 | CONFIG_ATH10K_LEDS=y 6 | CONFIG_BINUTILS_VERSION="2.31.1" 7 | CONFIG_BINUTILS_VERSION_2_31_1=y 8 | CONFIG_BUSYBOX_CONFIG_FEATURE_VI_UNDO=y 9 | CONFIG_BUSYBOX_CONFIG_FEATURE_VI_UNDO_QUEUE=y 10 | CONFIG_BUSYBOX_CONFIG_FEATURE_VI_UNDO_QUEUE_MAX=32 11 | CONFIG_BUSYBOX_DEFAULT_BZIP2_SMALL=0 12 | CONFIG_COLLECT_KERNEL_DEBUG=y 13 | CONFIG_GCC_VERSION="7.4.0" 14 | CONFIG_KERNEL_CC_OPTIMIZE_FOR_PERFORMANCE=y 15 | CONFIG_KERNEL_SQUASHFS_FRAGMENT_CACHE_SIZE=3 16 | CONFIG_KERNEL_STACKPROTECTOR=y 17 | CONFIG_OPENSSL_WITH_DEPRECATED=y 18 | CONFIG_OPENSSL_WITH_EC=y 19 | CONFIG_OPENSSL_WITH_NPN=y 20 | CONFIG_OPENSSL_WITH_PSK=y 21 | CONFIG_OPENSSL_WITH_SRP=y 22 | CONFIG_PACKAGE_RTLWIFI_DEBUG=y 23 | CONFIG_PACKAGE_ath10k-firmware-qca4019=y 24 | # CONFIG_PACKAGE_ath10k-firmware-qca4019-ct is not set 25 | CONFIG_PACKAGE_collectd=y 26 | CONFIG_PACKAGE_collectd-mod-cpu=y 27 | CONFIG_PACKAGE_collectd-mod-df=y 28 | CONFIG_PACKAGE_collectd-mod-ethstat=y 29 | CONFIG_PACKAGE_collectd-mod-interface=y 30 | CONFIG_PACKAGE_collectd-mod-iwinfo=y 31 | CONFIG_PACKAGE_collectd-mod-load=y 32 | CONFIG_PACKAGE_collectd-mod-memory=y 33 | CONFIG_PACKAGE_collectd-mod-network=y 34 | CONFIG_PACKAGE_collectd-mod-uptime=y 35 | CONFIG_PACKAGE_collectd-mod-wireless=y 36 | CONFIG_PACKAGE_kmod-ath10k=y 37 | # CONFIG_PACKAGE_kmod-ath10k-ct is not set 38 | # CONFIG_PACKAGE_kmod-hwmon-core is not set 39 | CONFIG_PACKAGE_kmod-rtl8192c-common=y 40 | CONFIG_PACKAGE_kmod-rtl8192cu=y 41 | CONFIG_PACKAGE_kmod-rtl8xxxu=m 42 | CONFIG_PACKAGE_kmod-rtlwifi=y 43 | CONFIG_PACKAGE_kmod-rtlwifi-usb=y 44 | CONFIG_PACKAGE_libbz2=y 45 | CONFIG_PACKAGE_libffi=y 46 | CONFIG_PACKAGE_libltdl=y 47 | CONFIG_PACKAGE_libopenssl=y 48 | CONFIG_PACKAGE_libpcap=y 49 | CONFIG_PACKAGE_librt=y 50 | CONFIG_PACKAGE_libuuid=y 51 | CONFIG_PACKAGE_python3-base=y 52 | CONFIG_PACKAGE_python3-codecs=y 53 | CONFIG_PACKAGE_python3-email=y 54 | CONFIG_PACKAGE_python3-light=y 55 | CONFIG_PACKAGE_python3-openssl=y 56 | CONFIG_PACKAGE_rtl8192cu-firmware=y 57 | CONFIG_PACKAGE_tcpdump=y 58 | # CONFIG_PACKAGE_wpad-basic is not set 59 | CONFIG_PACKAGE_wpad-mesh-openssl=y 60 | CONFIG_PACKAGE_zlib=y 61 | CONFIG_WPA_MSG_MIN_PRIORITY=3 62 | CONFIG_BINUTILS_VERSION="2.31.1" 63 | CONFIG_BINUTILS_VERSION_2_31_1=y 64 | # CONFIG_BUSYBOX_CONFIG_STACK_OPTIMIZATION_386 is not set 65 | # CONFIG_BUSYBOX_DEFAULT_ASH_BASH_NOT_FOUND_HOOK is not set 66 | # CONFIG_BUSYBOX_DEFAULT_ASH_BASH_SOURCE_CURDIR is not set 67 | # CONFIG_BUSYBOX_DEFAULT_BC is not set 68 | CONFIG_BUSYBOX_DEFAULT_BZIP2_SMALL=0 69 | # CONFIG_BUSYBOX_DEFAULT_FEATURE_BC_INTERACTIVE is not set 70 | # CONFIG_BUSYBOX_DEFAULT_FEATURE_BC_LONG_OPTIONS is not set 71 | # CONFIG_BUSYBOX_DEFAULT_FEATURE_CP_REFLINK is not set 72 | # CONFIG_BUSYBOX_DEFAULT_FEATURE_DC_BIG is not set 73 | # CONFIG_BUSYBOX_DEFAULT_FEATURE_EDITING_WINCH is not set 74 | # CONFIG_BUSYBOX_DEFAULT_FEATURE_ETC_SERVICES is not set 75 | # CONFIG_BUSYBOX_DEFAULT_FEATURE_FIND_EXECUTABLE is not set 76 | # CONFIG_BUSYBOX_DEFAULT_FEATURE_FIND_QUIT is not set 77 | # CONFIG_BUSYBOX_DEFAULT_FEATURE_LESS_ENV is not set 78 | # CONFIG_BUSYBOX_DEFAULT_FEATURE_LESS_RAW is not set 79 | # CONFIG_BUSYBOX_DEFAULT_FEATURE_NSLOOKUP_BIG is not set 80 | # CONFIG_BUSYBOX_DEFAULT_FEATURE_NSLOOKUP_LONG_OPTIONS is not set 81 | # CONFIG_BUSYBOX_DEFAULT_FEATURE_NTP_AUTH is not set 82 | # CONFIG_BUSYBOX_DEFAULT_FEATURE_RTMINMAX_USE_LIBC_DEFINITIONS is not set 83 | # CONFIG_BUSYBOX_DEFAULT_FEATURE_SHOW_SCRIPT is not set 84 | # CONFIG_BUSYBOX_DEFAULT_FEATURE_SH_EMBEDDED_SCRIPTS is not set 85 | # CONFIG_BUSYBOX_DEFAULT_FEATURE_SORT_OPTIMIZE_MEMORY is not set 86 | # CONFIG_BUSYBOX_DEFAULT_FEATURE_TC_INGRESS is not set 87 | # CONFIG_BUSYBOX_DEFAULT_FEATURE_TLS_SHA1 is not set 88 | # CONFIG_BUSYBOX_DEFAULT_FEATURE_UDHCPC6_RFC5970 is not set 89 | # CONFIG_BUSYBOX_DEFAULT_FEATURE_VOLUMEID_LFS is not set 90 | # CONFIG_BUSYBOX_DEFAULT_FEATURE_WAIT_FOR_INIT is not set 91 | # CONFIG_BUSYBOX_DEFAULT_FLOAT_DURATION is not set 92 | # CONFIG_BUSYBOX_DEFAULT_HUSH_BASH_SOURCE_CURDIR is not set 93 | # CONFIG_BUSYBOX_DEFAULT_HUSH_COMMAND is not set 94 | # CONFIG_BUSYBOX_DEFAULT_HUSH_LINENO_VAR is not set 95 | # CONFIG_BUSYBOX_DEFAULT_NOLOGIN is not set 96 | # CONFIG_BUSYBOX_DEFAULT_NOLOGIN_DEPENDENCIES is not set 97 | # CONFIG_BUSYBOX_DEFAULT_STACK_OPTIMIZATION_386 is not set 98 | # CONFIG_BUSYBOX_DEFAULT_SVOK is not set 99 | # CONFIG_BUSYBOX_DEFAULT_TC is not set 100 | # CONFIG_GCC_USE_EMBEDDED_PATH_REMAP is not set 101 | CONFIG_GCC_VERSION="7.4.0" 102 | # CONFIG_IPK_FILES_CHECKSUMS is not set 103 | CONFIG_KERNEL_CC_OPTIMIZE_FOR_PERFORMANCE=y 104 | # CONFIG_KERNEL_CC_OPTIMIZE_FOR_SIZE is not set 105 | CONFIG_KERNEL_SQUASHFS_FRAGMENT_CACHE_SIZE=3 106 | CONFIG_KERNEL_STACKPROTECTOR=y 107 | # CONFIG_KERNEL_STACKPROTECTOR_STRONG is not set 108 | # CONFIG_KERNEL_TASKSTATS is not set 109 | # CONFIG_OPENSSL_NO_DEPRECATED is not set 110 | CONFIG_PACKAGE_collectd-mod-iwinfo=y 111 | -------------------------------------------------------------------------------- /collectd.conf: -------------------------------------------------------------------------------- 1 | # Config file for collectd. More info: https://collectd.org/ 2 | # Note: Luci statistics will generate a new config and overwrite this file. 3 | 4 | Hostname "b1300-2" 5 | #FQDNLookup true 6 | BaseDir "/var/run/collectd" 7 | Include "/etc/collectd/conf.d" 8 | PIDFile "/var/run/collectd.pid" 9 | PluginDir "/usr/lib/collectd" 10 | TypesDB "/usr/share/collectd/types.db" 11 | Interval 30 12 | ReadThreads 2 13 | 14 | LoadPlugin interface 15 | LoadPlugin load 16 | #LoadPlugin ping 17 | #LoadPlugin rrdtool 18 | LoadPlugin cpu 19 | LoadPlugin memory 20 | LoadPlugin uptime 21 | LoadPlugin network 22 | LoadPlugin iwinfo 23 | 24 | 25 | Interface "wlan0" 26 | Interface "wlan1" 27 | 28 | 29 | 30 | IgnoreSelected false 31 | Interface "br-wlan" 32 | Interface "wlan0" 33 | Interface "wlan1" 34 | Interface "wlan2" 35 | Interface "eth0" 36 | 37 | 38 | 39 | # Server "server" 40 | 41 | 42 | # 43 | # Host "host.foo.bar" 44 | # Interval 30 45 | # TTL 127 46 | # 47 | -------------------------------------------------------------------------------- /input.txt: -------------------------------------------------------------------------------- 1 | 00:10:31.581606 747052990us tsft 1.0 Mb/s 2412 MHz 11b -50dBm signal antenna 0 BSSID:Broadcast DA:Broadcast SA:ac:d1:b8:55:e5:69 (oui Unknown) Probe Request (meshap) [1.0 2.0 5.5 11.0 Mbit] 2 | 00:10:31.592599 747062607us tsft 1.0 Mb/s 2412 MHz 11b -50dBm signal antenna 0 BSSID:Broadcast DA:Broadcast SA:ac:d1:b8:55:e5:69 (oui Unknown) Probe Request (meshap) [1.0 2.0 5.5 11.0 Mbit] 3 | 00:10:31.606362 747077696us tsft 1.0 Mb/s 2412 MHz 11b -48dBm signal antenna 0 BSSID:Broadcast DA:Broadcast SA:ac:d1:b8:55:e5:69 (oui Unknown) Probe Request (meshap) [1.0 2.0 5.5 11.0 Mbit] 4 | 00:10:31.616532 747087914us tsft 1.0 Mb/s 2412 MHz 11b -48dBm signal antenna 0 BSSID:Broadcast DA:Broadcast SA:ac:d1:b8:55:e5:69 (oui Unknown) Probe Request (meshap) [1.0 2.0 5.5 11.0 Mbit] 5 | 00:10:31.682785 747153509us tsft 1.0 Mb/s 2412 MHz 11b -52dBm signal antenna 0 BSSID:Broadcast DA:Broadcast SA:ac:d1:b8:55:e5:69 (oui Unknown) Probe Request (meshap) [1.0 2.0 5.5 11.0 Mbit] 6 | 00:10:31.692507 747163806us tsft 1.0 Mb/s 2412 MHz 11b -52dBm signal antenna 0 BSSID:Broadcast DA:Broadcast SA:ac:d1:b8:55:e5:69 (oui Unknown) Probe Request (meshap) [1.0 2.0 5.5 11.0 Mbit] 7 | 8 | -------------------------------------------------------------------------------- /presence.mustache: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 13 | 16 | 17 | 18 | 46 |
47 | 48 | 49 | 50 |
51 |
52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /tcpdumpscan.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import argparse 4 | from datetime import datetime 5 | import json 6 | import os.path 7 | import urllib.request 8 | import time 9 | import socket 10 | import sys 11 | import re 12 | import ssl 13 | import subprocess 14 | 15 | def main(): 16 | parser = argparse.ArgumentParser(description="Submit find3 passive scan results") 17 | parser.add_argument("-u", dest="url", required=False, help="The find3 REST endpoint url", default="https://cloud.internalpositioning.com") 18 | parser.add_argument("-f", dest="family", required=True, help="The find3 family identifier") 19 | parser.add_argument("-i", dest="iface", required=True, help="The monitor mode interface") 20 | args = parser.parse_args() 21 | ssl._create_default_https_context = ssl._create_unverified_context 22 | parser = SignalParser(args.family, args.url, args.iface) 23 | parser.read() 24 | 25 | def is_valid_file(parser, arg): 26 | if not os.path.exists(arg): 27 | parser.error("The file %s does not exist!" % arg) 28 | else: 29 | return arg 30 | 31 | class SignalParser(): 32 | 33 | def __init__(self, family, url, iface): 34 | self.family = family 35 | self.url = url 36 | self.iface = iface 37 | 38 | def send_payload(self, stations, last_seen): 39 | payload = self.build_payload(stations, last_seen) 40 | clen = len(payload) 41 | headers = {'Content-Type': 'application/json', 'Content-Length': clen} 42 | req = urllib.request.Request(self.url + '/passive', payload, headers, method='POST') 43 | f = urllib.request.urlopen(req) 44 | response = f.read() 45 | print("response: %s" % response) 46 | f.close() 47 | 48 | def build_payload(self, stations, last_seen): 49 | body = {'f': self.family} 50 | body['t'] = int(last_seen.timestamp()) 51 | body['d'] = socket.gethostname() 52 | body['s'] = {'wifi': stations} 53 | return str(json.dumps(body)).encode('utf8') 54 | 55 | def read(self): 56 | proc = subprocess.Popen(['tcpdump', '-l', '-i', self.iface, '-e', '-s', str(256), 'type', 'mgt', 'subtype', 'probe-req'], stdout=subprocess.PIPE) 57 | stations = {} 58 | start = datetime.now() 59 | for bb in iter(proc.stdout.readline, ''): 60 | try: 61 | row = str(bb, 'utf8') 62 | if (row is None or "Probe" not in row): 63 | time.sleep(0.1) 64 | continue 65 | rows = row.split(' ') 66 | 67 | last_seen = datetime.strptime(rows[0].strip(), "%H:%M:%S.%f") 68 | last_seen = datetime.combine(datetime.today(), last_seen.time()) 69 | power = re.match(r".*(-\d+|0)dBm", row).group(1) 70 | station = re.match(r".*SA:(.*?)\s", row).group(1) 71 | stations[station] = int(power) 72 | elapsed = datetime.now() - start 73 | if (len(stations) > 0 and int(power) != 0): 74 | print("Found %s with signal power of %d dBm at %s" % (station, int(power), last_seen)) 75 | self.send_payload(stations, last_seen) 76 | stations = {} 77 | 78 | except Exception as e : 79 | time.sleep(0.02) 80 | sys.stdout.flush() 81 | print("Failed to read from tcpdump: %s" % (str(e))) 82 | 83 | if __name__ == "__main__": 84 | main() 85 | -------------------------------------------------------------------------------- /utils/find3-openwrt-scanner/Makefile: -------------------------------------------------------------------------------- 1 | include $(TOPDIR)/rules.mk 2 | 3 | PKG_NAME:=find3-openwrt-scanner 4 | PKG_RELEASE:=2 5 | 6 | PKG_MAINTAINER:=Jeroen Peelaerts 7 | 8 | include $(INCLUDE_DIR)/package.mk 9 | 10 | define Package/find3-openwrt-scanner 11 | SECTION:=utils 12 | CATEGORY:=Utilities 13 | TITLE:=Find3 CLI scanner 14 | URL:=http://www.internalpositioning.com/ 15 | PKGARCH:=all 16 | DEPENDS:=+tcpdump +curl 17 | endef 18 | 19 | define Package/find3-openwrt-scanner/description 20 | FIND attempts to simplify internal positioning. 21 | Internal positioning, simplified Using FIND, 22 | and only your smartphone or laptop, you will be able to pinpoint your position in your home or office. 23 | endef 24 | 25 | define Build/Prepare 26 | endef 27 | 28 | define Build/Compile 29 | endef 30 | 31 | define Package/find3-openwrt-scanner/install 32 | $(INSTALL_DIR) $(1)/usr/lib 33 | $(INSTALL_BIN) ./files/tcpdumpscan.sh $(1)/usr/lib 34 | $(INSTALL_DIR) $(1)/etc/init.d 35 | $(INSTALL_BIN) ./files/tcpdumpscan.init $(1)/etc/init.d/tcpdumpscan 36 | $(INSTALL_DIR) $(1)/etc/config 37 | $(INSTALL_CONF) ./files/tcpdumpscan.config $(1)/etc/config/tcpdumpscan 38 | endef 39 | 40 | define Package/find3-openwrt-scanner/conffiles 41 | /etc/config/tcpdumpscan 42 | endef 43 | 44 | $(eval $(call BuildPackage,find3-openwrt-scanner)) 45 | -------------------------------------------------------------------------------- /utils/find3-openwrt-scanner/files/tcpdumpscan.config: -------------------------------------------------------------------------------- 1 | config find3 2 | option 'family' 'family' 3 | option 'url' 'https://find3.internalpositioning.com' 4 | option 'iface' 'auto' 5 | -------------------------------------------------------------------------------- /utils/find3-openwrt-scanner/files/tcpdumpscan.init: -------------------------------------------------------------------------------- 1 | #!/bin/sh /etc/rc.common 2 | 3 | USE_PROCD=1 4 | START=99 5 | 6 | URL=$(uci get tcpdumpscan.@find3[-1].url) 7 | FAMILY=$(uci get tcpdumpscan.@find3[-1].family) 8 | IFACE=$(uci get tcpdumpscan.@find3[-1].iface) 9 | 10 | find_monitor() { 11 | if [ "$IFACE" == 'auto' ]; then 12 | local phys=$(ls /sys/class/ieee80211/) 13 | for phy in $phys; do 14 | local type=$(cat /sys/class/ieee80211/$phy/device/net/*/type) 15 | if [ $type -gt 1 ]; then 16 | IFACE=$(ls /sys/class/ieee80211/$phy/device/net) 17 | fi 18 | done 19 | fi 20 | } 21 | 22 | start_service() { 23 | find_monitor 24 | if [ -z "$IFACE" ] || [ "$IFACE" == 'auto' ]; then 25 | echo "No interfaces found in monitor mode. Bailing out." 26 | exit 1; 27 | fi 28 | 29 | procd_open_instance tcpdumpscan 30 | procd_set_param command sh /usr/lib/tcpdumpscan.sh -i $IFACE -u $URL -f $FAMILY 31 | procd_set_param respawn ${respawn_threshold:-3600} 32 | procd_set_param stdout 1 33 | procd_set_param stderr 1 34 | procd_close_instance 35 | } 36 | 37 | service_triggers() 38 | { 39 | procd_add_reload_trigger "network" "wireless" 40 | } 41 | -------------------------------------------------------------------------------- /utils/find3-openwrt-scanner/files/tcpdumpscan.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . /usr/share/libubox/jshn.sh 4 | 5 | usage() { echo "Usage: $0 [-i ] [-u ] [-f ]" 1>&2; exit 1; } 6 | 7 | while getopts "i:u:f:h" arg; do 8 | case $arg in 9 | i) 10 | iface=$OPTARG 11 | ;; 12 | u) 13 | url=$OPTARG 14 | ;; 15 | f) 16 | family=$OPTARG 17 | ;; 18 | h) 19 | echo "usage" 20 | ;; 21 | esac 22 | done 23 | 24 | if [ -z "${family}" ] || [ -z "${iface}" ] || [ -z "${url}" ]; then 25 | usage 26 | fi 27 | 28 | start=`date +%s` 29 | 30 | build_payload() { 31 | json_init 32 | json_add_string "f" $family 33 | json_add_string "d" `cat /proc/sys/kernel/hostname` 34 | json_add_int "t" "$3" 35 | json_add_object "s" 36 | json_add_object "wifi" 37 | 38 | json_add_int "$1" "$2" 39 | json_close_object 40 | json_close_object 41 | payload=$(json_dump) 42 | } 43 | 44 | send_payload() { 45 | build_payload "$@" 46 | local response=$(curl -s -H 'Content-Type: application/json' -d"${payload}" ${url}/passive) 47 | echo $response 48 | } 49 | 50 | read_output() { 51 | while IFS= read -r line 52 | do 53 | if [ -z "${line##*$Probe*}" ] && [ ! -z "$line" ]; then 54 | local power=$(echo $line | grep -o -E '\-[0-9]{2}dBm') 55 | power=${power%dBm} 56 | local station=$(echo $line | grep -o -E 'SA:([^[:space:]]{2}:?)*') 57 | station=${station:3:20} 58 | local now=`date +%s` 59 | local elapsed=$(expr $now - $start) 60 | send_payload "$station" "$power" "$now" 61 | echo "Power: ${power}dBm Station: $station Time: $now" 62 | fi 63 | done 64 | } 65 | 66 | tcpdump -i $iface -e -s 256 type mgt subtype probe-req | read_output 67 | -------------------------------------------------------------------------------- /wr703n-8Mb.patch: -------------------------------------------------------------------------------- 1 | diff --git a/target/linux/ar71xx/image/tiny-tp-link.mk b/target/linux/ar71xx/image/tiny-tp-link.mk 2 | index 5b0ab3e970..92cfca2d69 100644 3 | --- a/target/linux/ar71xx/image/tiny-tp-link.mk 4 | +++ b/target/linux/ar71xx/image/tiny-tp-link.mk 5 | @@ -340,7 +340,7 @@ endef 6 | TARGET_DEVICES += tl-wr1041n-v2 7 | 8 | define Device/tl-wr703n-v1 9 | - $(Device/tplink-4mlzma) 10 | + $(Device/tplink-8mlzma) 11 | DEVICE_TITLE := TP-LINK TL-WR703N v1 12 | DEVICE_PACKAGES := kmod-usb-core kmod-usb2 13 | BOARDNAME := TL-WR703N 14 | --------------------------------------------------------------------------------