Remote Access Protocol for Reolink Battery Powered Cameras

Update: The protocol is now well understood and supported by the Neolink RTSP bridge software and this fork of the Camera Proxy project.

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.1 is the local IP of the app connecting to the camera remotely.
  • 7.0.0.1 is the public IP of the app on the internet.
  • 196.0.0.1 is the local IP of the camera.
  • 6.0.0.1 is the public IP of the camera on the internet.
  • p2p.reolink.com is the hostname of the Reolink P2P server.
  • 15.237.112.14 is the IP address of the Reolink P2P server.
  • 123456789 is 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 9999 of 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>

where 123456789 is the camera UID <uid> and 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 <C2M_Q>.

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>15.237.112.14</ip>
            <port>58200</port>
        </reg>
        <relay>
            <ip>15.237.112.14</ip>
            <port>58100</port>
        </relay>
        <log>
            <ip>15.237.112.14</ip>
            <port>57850</port>
        </log>
        <t>
            <ip>15.237.112.14</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 <C2M_Q> plus _R which I imagine stands for response or reply.

The response contains the IP addresses and port numbers of the following services:

  • <reg> for registration
  • <relay> for relay
  • <log> for logging
  • <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 15.237.112.14 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 <reg> endpoint 15.237.112.14:58200 with the following payload:

<P2P>
    <C2R_C>
        <uid>12345678</uid>
        <cli>
            <ip>10.0.0.1</ip>
            <port>10693</port>
        </cli>
        <relay>
            <ip>15.237.112.14</ip>
            <port>58100</port>
        </relay>
        <cid>693000</cid>
        <debug>251658240</debug>
        <family>4</family>
        <p>MAC</p>
        <r>3</r>
    </C2R_C>
</P2P>

where 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 15.237.112.14:58200 responds with the following UDP payload:

<P2P>
    <R2C_T>
        <dev>
            <ip>196.0.0.1</ip>
            <port>18371</port>
        </dev>
        <dmap>
            <ip>6.0.0.1</ip>
            <port>18371</port>
        </dmap>
        <sid>99933377</sid>
        <cid>555777</cid>
        <rsp>0</rsp>
    </R2C_T>
</P2P>

where 196.0.0.1:18371 is the IP and port number of the UDP keep-alive connection of the camera on its local network and 6.0.0.1:18371 is the public IP and port, while <sid> and <cid> are the two keys used to establish the connection.

App Connects to the Camera

Now the app sends a UDP packet directly to 6.0.0.1:18371 which is the the public IP and port number of the camera from the <dmap> element:

<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>

where <did> is the only unique value received from the camera.


To be continued…

3 Comments

  1. Seko says:

    Very interesting analysis. Did you ever continue? I’m looking for a way to integrate this camera into my smart home and happy about every single bit of info.

  2. Jg says:

    In cases where the hole punching doesn’t work and the connections go through Reo’s P2P server between the endpoints, do you think the stream stays encrypted all the way through or does it get decrypted at the P2P server and then re-encapsulated before being forwarded to the requesting client?

Leave a Reply to Kaspars Cancel reply