Netgear Nighthawk R7800 : add USB camera support to create a security webcam
Posted on Wed 22 November 2017 in Article
This article explains how to customize Nighthawk X4S firmware to add a security camera feature to this always-online & almost-always-idle device. Alternative firmwares like OpenWRT or LEDE exist, but they don't fully support all stock features yet. So instead this approach is based on modified stock firmware.
Main steps are:
- Customize kernel to add USB video support (uvc, v4l2)
- Install additional software packages for motion detection
- Configure motion detection alerts
#YOLO
There's always a risk of bricking the device if something goes wrong. However, a recovery procedure via TFTP exists.
Software downloads are performed over HTTP, due to client limitation on target side.
Hardware
- Neatgear Nighthawk X4S router
- random cheap USB webcam (don't buy that one, quality is terrible)
Root that firmware
I first thought that this step would be a pain, but then reminded the device manufacturer name. By grepping 'telnet' in the firmware binary, we discover the existence of a debug page /debug.htm , with telnet option:
Telnet access is protected with the same password as WebUI, and gives a root shell.
Backup all the things
Thanks to telnet access, we backup the original kernel partition on an external USB drive :
$ telnet 192.168.1.1
Trying 192.168.1.1...
Connected to 192.168.1.1.
Escape character is '^]'.
=== LOGIN ===============================
Please enter your password,It's the same
with DUT login password
------------------------------------------
telnet password:JCVD4l1FE
=== IMPORTANT ============================
Use 'passwd' to set your login password
this will disable telnet and enable SSH
------------------------------------------
BusyBox v1.4.2 (2017-08-29 13:01:25 CST) Built-in shell (ash)
Enter 'help' for a list of built-in commands.
MM NM MMMMMMM M M
$MMMMM MMMMM MMMMMMMMMMM MMM MMM
MMMMMMMM MM MMMMM. MMMMM:MMMMMM: MMMM MMMMM
MMMM= MMMMMM MMM MMMM MMMMM MMMM MMMMMM MMMM MMMMM'
MMMM= MMMMM MMMM MM MMMMM MMMM MMMM MMMMNMMMMM
MMMM= MMMM MMMMM MMMMM MMMM MMMM MMMMMMMM
MMMM= MMMM MMMMMM MMMMM MMMM MMMM MMMMMMMMM
MMMM= MMMM MMMMM, NMMMMMMMM MMMM MMMM MMMMMMMMMMM
MMMM= MMMM MMMMMM MMMMMMMM MMMM MMMM MMMM MMMMMM
MMMM= MMMM MM MMMM MMMM MMMM MMMM MMMM MMMM
MMMM$ ,MMMMM MMMMM MMMM MMM MMMM MMMMM MMMM MMMM
MMMMMMM: MMMMMMM M MMMMMMMMMMMM MMMMMMM MMMMMMM
MMMMMM MMMMN M MMMMMMMMM MMMM MMMM
MMMM M MMMMMMM M M
M
---------------------------------------------------------------
For those about to rock... (%C, %R)
---------------------------------------------------------------
root@R7800:/# cat /proc/mtd | grep kernel
mtd5: 00220000 00020000 "kernel"
root@R7800:/# dd if=/dev/mtdblock5 of=/mnt/sda1/kernel.img
4352+0 records in
4352+0 records out
Kernel
Netgear has released GPL source code for the Linux kernel used in this device. This copy on GitHub integrates few fixes to compile with newer GCC and V4L2 headers, and also the original kernel configuration dumped from live device.
$ git clone https://github.com/frederic/netgear-R7800-GPL.git
$ cd netgear-R7800-GPL/
Build mkimage tool
The mkimage tool is used at the end of kernel building process to create the new kernel image partition for the device.
$ make tools/mkimage/install
make[1] tools/mkimage/install
make[2] -C tools/sed compile
make[2] -C tools/sed install
make[2] -C tools/mkimage compile
make[2] -C tools/mkimage install
Add freshly built mkimage binary to our PATH environment:
$ export PATH=$PWD/build_dir/host/u-boot-2012.04.01/tools:$PATH
Build Linux kernel
To build a kernel for this ARM-based device, we need to add a cross-compilation toolchain to our build environment :
$ git clone https://android.googlesource.com/platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8
$ export PATH=$PWD/arm-eabi-4.8/bin:$PATH
Then we create a new kernel configuration based on the original one :
$ cd git_home/linux.git/sourcecode/
$ make r7800_defconfig ARCH=arm CROSS_COMPILE=arm-eabi-
$ make menuconfig ARCH=arm CROSS_COMPILE=arm-eabi-
In the kernel configuration menu, we enable the following options to get V4L2 & USB Video support :
Device Drivers --->
-> <*> Multimedia support --->
--> <*> Video For Linux
--> <*> Video capture adapters --->
---> <*> V4L USB devices --->
----> <*> USB Video Class (UVC)
----> <*> GSPCA based webcams
For info, that should correspond to these options :
CONFIG_USB_VIDEO_CLASS=y
CONFIG_USB_GSPCA=y
CONFIG_VIDEO_V4L2_COMMON=y
CONFIG_VIDEO_V4L2=y
CONFIG_V4L_USB_DRIVERS=y
Finally, we build the kernel image :
$ make -j8 uImage ARCH=arm CROSS_COMPILE=arm-eabi-
[...]
Kernel: arch/arm/boot/Image is ready
Kernel: arch/arm/boot/zImage is ready
UIMAGE arch/arm/boot/uImage
Image Name: Linux-3.4.103
Created: Mon Jan 29 23:58:38 2018
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 2210952 Bytes = 2159.13 kB = 2.11 MB
Load Address: 41508000
Entry Point: 41508000
Image arch/arm/boot/uImage is ready
Output kernel image is arch/arm/boot/uImage
$ file arch/arm/boot/uImage
arch/arm/boot/uImage: u-boot legacy uImage, Linux-3.4.103, Linux/ARM, OS Kernel Image (Not compressed), 2210952 bytes, Tue Jan 30 07:58:38 2018, Load Address: 0x41508000, Entry Point: 0x41508000, Header CRC: 0x81013485, Data CRC: 0xDEA9B00E
In current firmware, kernel partition size is 2228224 bytes. So the new kernel image cannot be larger.
We copy that image to a USB drive and then, from the telnet shell, we overwrite original kernel with the new image :
root@R7800:/# dd if=/mnt/sda1/uImage of=/dev/mtdblock5
root@R7800:/# sync
root@R7800:/# reboot
Note: You have to enable telnet in WebUI after each reboot.
Now the router detects USB camera when plugged in :
usb 3-1: new high-speed USB device number 5 using xhci-hcd
usb 3-1: New USB device found, idVendor=1908, idProduct=2310
usb 3-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
usb 3-1: Product: USB2.0 PC CAMERA
usb 3-1: Manufacturer: Generic
usb 3-1: SerialNumber: 20100331010203
INFO008C: Add device intf d39cc400, dev d39a5000
INFO0C15: filter audio device
uvcvideo: Found UVC 1.00 device USB2.0 PC CAMERA (1908:2310)
input: USB2.0 PC CAMERA as /devices/platform/ipq-dwc3.1/dwc3.1/xhci-hcd.1/usb3/3-1/3-1:1.0/input/input3
Install software packages
"Motion is a program that monitors the video signal from cameras. It is able to detect if a significant part of the picture has changed; in other words, it can detect motion."
Fortunately, this package is available in OpenWrt repositories for our architecture. And stock firmware includes the OpenWrt package manager opkg.
However, opkg is outdated and needs to be patched first :
root@R7800:/# curl -k https://gist.githubusercontent.com/frederic/fcb7ddc14c46aa630143aaeafe2d706f/raw/8242c18a514f19b58c74387d4bfa0e5511bbb4e5/functions.sh -o '/lib/functions.sh'
Then, we can install motion & libjpeg packages :
root@R7800:~# curl -k https://raw.githubusercontent.com/frederic/netgear-R7800-GPL/master/ipk/libjpeg_9a-1_ipq806x.ipk -o /tmp/libjpeg_9a-1_ipq806x.ipk
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 92994 100 92994 0 0 259k 0 --:--:-- --:--:-- --:--:-- 430k
root@R7800:~# opkg install /tmp/libjpeg_9a-1_ipq806x.ipk
Installing libjpeg (9a-1) to root...
Configuring libjpeg.
root@R7800:~# curl -k https://raw.githubusercontent.com/frederic/netgear-R7800-GPL/master/ipk/motion_3.4.0-20141018-9479d910f2149b5558788bb86f97f26522794212-1_ipq806x.ipk -o /tmp/motion_3.4.0-20141018-9479d910f2149b5558788bb86f97f26522794212-1_ipq806x.ipk
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 94802 100 94802 0 0 297k 0 --:--:-- --:--:-- --:--:-- 387k
root@R7800:~# opkg install /tmp/motion_3.4.0-20141018-9479d910f2149b5558788bb86f97f26522794212-1_ipq806x.ipk
Installing motion (3.4.0-20141018-9479d910f2149b5558788bb86f97f26522794212-1) to root...
Configuring motion.
Note that motion software expects its default configuration file at different path than the one actually provided. So we move it back to default path :
root@R7800:/# mkdir /etc/motion
root@R7800:/# mv /overlay/etc/motion.conf /etc/motion/motion.conf
Configuration
To enable motion detection only when our smartphone (hence we) is not at home, we call our custom script in Hostapd script /lib/wifi/wps-hostapd-update-uci. It will only be triggered when a station connects or disconnects from the router.
diff --git a/lib/wifi/wps-hostapd-update-uci b/lib/wifi/wps-hostapd-update-uci
index f60abe3..dd6f3f2 100755
--- a/lib/wifi/wps-hostapd-update-uci
+++ b/lib/wifi/wps-hostapd-update-uci
@@ -157,6 +157,9 @@ check_ap_lock_down()
}
case "$CMD" in
+ AP-STA-CONNECTED|AP-STA-DISCONNECTED)
+ /etc/motion_cron.sh $CMD $3
+ ;;
WPS-NEW-AP-SETTINGS|WPS-NEW-AP-SETTINGS-AP-PIN)
local ssid=$(hostapd_cli -i$IFNAME -p/var/run/hostapd-$parent get_config | grep ^ssid= | cut -f2- -d =)
local wpa=$(hostapd_cli -i$IFNAME -p/var/run/hostapd-$parent get_config | grep ^wpa= | cut -f2- -d=)
We create the following script in /etc/motion_cron.sh :
#!/bin/sh
# Start or stop motion service depending on wifi client status
# Usage: motion_cron.sh [<event_type> <mac addr>]
HWADDR=00:11:22:33:44:55 # Update with your smartphone MAC address
CONNECTED=0
CMD=motion
CMD_PID=/var/run/$CMD.pid
CMD_BIN=/usr/bin/$CMD
PID=`pidof $CMD`
if [ $# -eq 2 ]; then
EVENT=$1
EVENT_MAC=$2
if [ "$EVENT_MAC" == "$HWADDR" ]; then
case "$EVENT" in
AP-STA-DISCONNECTED)
CONNECTED=0
;;
AP-STA-CONNECTED)
CONNECTED=1
;;
*)
echo "Non-interesting event, ignore..."
exit 0
esac
echo "$EVENT $EVENT_MAC"
else
echo "Not our MAC, ignore..."
exit 0
fi
else
if grep "0x2\W\+$HWADDR" /proc/net/arp ; then
echo "$HWADDR is connected in arp table"
CONNECTED=1
else
echo "$HWADDR is disconnected in arp table"
CONNECTED=0
fi
fi
if [ $CONNECTED -eq 1 ]; then
echo "$HWADDR is connected"
if [ -n "$PID" ]
then
echo "$CMD is running, stopping it..."
start-stop-daemon -K -x $CMD_BIN -p $CMD_PID
fi
else
echo "$HWADDR is disconnected"
if [ -z "$PID" ]
then
echo "$CMD is not running, starting it..."
start-stop-daemon -S -x $CMD_BIN -- -b -p $CMD_PID
fi
fi
$HWADDR has to be set to MAC address of our smartphone.
As failsafe mechanism, we can also add this script to cron :
echo " 0 * * * * /etc/motion_cron.sh" >> /etc/crontabs/root
Finally, we edit the file /etc/motion/motion.conf to configure motion. A guide is available on official motion website. In addition of your own settings, I recommend to set the output storage path to an external USB drive :
target_dir /mnt/sda1