---
title: Remote Access Protocol for Reolink Battery Powered Cameras
date: 2021-07-24T07:17:33+00:00
modified: 2022-04-25T10:46:07+00:00
permalink: https://kaspars.net/blog/reolink-battery-camera-remote-protocol
post_type: post
author:
  name: Kaspars
  avatar: https://reverse.kaspars.net/gravatar/avatar/92bfcd3a8c3a21a033a6484d32c25a40b113ec6891f674336081513d5c98ef76?s=96&d=mm&r=g
category:
  - Electronics
  - Home Automation
---

# Remote Access Protocol for Reolink Battery Powered Cameras

**Update:** The protocol is now [well understood](https://github.com/thirtythreeforty/neolink/pull/199) and supported by the [Neolink RTSP bridge software](https://github.com/thirtythreeforty/neolink) and [this fork](https://github.com/vherrlein/camera_proxy/tree/develop) of the [Camera Proxy project](https://github.com/jdillenkofer/camera_proxy).

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](https://www.nozominetworks.com/blog/new-reolink-p2p-vulnerabilities-show-iot-security-camera-risks/) and the [camera\_proxy project](https://github.com/jdillenkofer/camera_proxy), 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](https://reolink.com/software-and-manual/) (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](https://github.com/jdillenkofer/camera_proxy/blob/af9a170d3a8db831318a1aafcb2bf097ab62fd40/wireshark-dissector.lua) 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](https://bford.info/pub/net/p2pnat/) 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](https://github.com/jdillenkofer/camera_proxy/blob/af9a170d3a8db831318a1aafcb2bf097ab62fd40/src/baichuan_udp_layer.py#L78-L106).

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 `<<meta charset="utf-8"></meta>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 `<meta charset="utf-8"></meta>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:<meta charset="utf-8"></meta>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><meta charset="utf-8"></meta>99933377</sid>
        <conn>map</conn>
        <cid><meta charset="utf-8"></meta>555777</cid>
        <mtu>1350</mtu>
    </C2D_T>
</P2P>
```

and the camera responds with:

```
<P2P>
    <D2C_T>
        <sid><meta charset="utf-8"></meta>99933377</sid>
        <conn>map</conn>
        <cid><meta charset="utf-8"></meta>555777</cid>
        <did>576</did>
    </D2C_T>
</P2P>
```

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

---

To be continued…