Battery powered cameras from Reolink offer remote viewing and management even when cameras are behind mobile network data connections. Based on this research by Nozomi Networks Labs and the camera_proxy project, I’ve been able to capture and inspect the communication protocol used specifically by the Argus 3 camera.
I used the version 8.2.6 of the Reolink client for MacOS (which is an Electron app) and Wireshark to capture and decode the traffic between the software and the internet. The decoding of their proprietary packets was possible thanks to the Baichuan/Reolink proprietary IP camera protocol dissector for Wireshark bundled with the camera_proxy project.
In the description bellow we’ll use the following addresses and names to describe the network participants:
10.0.0.1is the local IP of the app connecting to the camera remotely.
220.127.116.11is the public IP of the app on the internet.
18.104.22.168is the local IP of the camera.
22.214.171.124is the public IP of the camera on the internet.
p2p.reolink.comis the hostname of the Reolink P2P server.
126.96.36.199is the IP address of the Reolink P2P server.
123456789is the Reolink UID (unique identifier) of the camera.
The Reolink client app uses the publicly accesible Reolink relay to establish a direct connection to the camera for remote management and video/audio communication. This article by Bryan Ford is the best explanation of network hole punching for peer-to-peer (P2P) access.
The communication uses UDP packets with special payload that contain the information to establish direct connection between the client and the camera. The payload is encoded to avoid network address translation (NAT) layers changing the IP addresses included in the payload. The encoding uses a shared secret key and some additional transformations.
The actual magic happens through the dynamic port numbers allocated by the routers between both communicating parties and the ability to route packets back to the originating device by re-using those port numbers. It is likely that the camera sends regular UDP pings to the public P2P server to keep an active connection with the server to enable incoming connections.
1. App Connects to the P2P Server
The client app sends a UDP packet from local port
16577 to port
p2p.reolink.com with the following XML body (encoded using the algorithm mentioned above). It also sends the same packet to the local broadcast address
255.255.255.255 of the local network which allows local cameras to respond, too:
<P2P> <C2M_Q> <uid>123456789</uid> <p>MAC</p> </C2M_Q> </P2P>
123456789 is the camera UID
MAC is the platform
<p> identifier of the connecting client. In cases where the client is connecting to a Reolink DVR (digital video recorder) or NVR (network video recorder) instead of a camera, the wrapping XML element is
<D2M_Q> instead of
It appears to send these packet to all IP addresses of the
p2p.reolink.com name server A records.
One of the P2P servers responds with the following payload:
<P2P> <M2C_Q_R> <reg> <ip>188.8.131.52</ip> <port>58200</port> </reg> <relay> <ip>184.108.40.206</ip> <port>58100</port> </relay> <log> <ip>220.127.116.11</ip> <port>57850</port> </log> <t> <ip>18.104.22.168</ip> <port>9996</port> </t> <timer/> <retry/> <mtu>1350</mtu> <debug>251658240</debug> <ac>-1700607721</ac> <rsp>0</rsp> </M2C_Q_R> </P2P>
while other IP addresses of the P2P server respond with:
<P2P> <M2C_Q_R> <devinfo> <type/> <mac/> <bat>0</bat> <qr>0</qr> </devinfo> <rsp>-3</rsp> </M2C_Q_R> </P2P>
Notice how the XML element name in the response
<M2C_Q_R> is the inverse of the request element name
_R which I imagine stands for response or reply.
The response contains the IP addresses and port numbers of the following services:
<t>for telemetry (?)
and some other information which I’m not sure how is used.
2. Client App Registers with the P2P Server
The client app appears to first make a TCP connection to IP
22.214.171.124 and port
9996 of the
<t> element in the XML response to possibly obtain a shared secret key to use for further connection. I haven’t been able to decode this TLS communication because the client appears to ignore system proxy setting to allow capturing and intercepting the TLS traffic.
Then it sends an encoded UDP packet the
126.96.36.199:58200 with the following payload:
<P2P> <C2R_C> <uid>12345678</uid> <cli> <ip>10.0.0.1</ip> <port>10693</port> </cli> <relay> <ip>188.8.131.52</ip> <port>58100</port> </relay> <cid>693000</cid> <debug>251658240</debug> <family>4</family> <p>MAC</p> <r>3</r> </C2R_C> </P2P>
12345678 is the UID of the camera we’re connecting to with its local IP
10.0.0.1 and local port number
10693. TODO Confirm if the value of
<CID> was retrieved during the TCP exchnage or generated randomly by the client.
The P2P registration endpoint
184.108.40.206:58200 responds with the following UDP payload:
<P2P> <R2C_T> <dev> <ip>220.127.116.11</ip> <port>18371</port> </dev> <dmap> <ip>18.104.22.168</ip> <port>18371</port> </dmap> <sid>99933377</sid> <cid>555777</cid> <rsp>0</rsp> </R2C_T> </P2P>
22.214.171.124:18371 is the IP and port number of the UDP keep-alive connection of the camera on its local network and
126.96.36.199:18371 is the public IP and port, while
<cid> are the two keys used to establish the connection.
App Connects to the Camera
Now the app sends a UDP packet directly to
188.8.131.52:18371 which is the the public IP and port number of the camera from the
<P2P> <C2D_T> <sid>99933377</sid> <conn>map</conn> <cid>555777</cid> <mtu>1350</mtu> </C2D_T> </P2P>
and the camera responds with:
<P2P> <D2C_T> <sid>99933377</sid> <conn>map</conn> <cid>555777</cid> <did>576</did> </D2C_T> </P2P>
<did> is the only unique value received from the camera.
To be continued…