aboutsummaryrefslogtreecommitdiff
path: root/inform_protocol.md
blob: 4338784637be530724dc0b88de3b044512a71b8e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
# Ubiquiti Inform Protocol

The mFi uses the Ubiquiti inform protocol to handle all communications to and
from the controller. This is the way that it transmits the current state of the
system to the controller (looks like it just sends mca-dump output) as well as
how it receives the instructions from the controller as to what to enable or
disable.

Everything appears to be pull-based, even provisioning. This makes sense with
the cloud controller where the controller has no access to your network. I
have not documented yet how anything works within the protocol, that is next.
This documents the overall protocol itself.

The device will inform by executing an HTTP POST with an encrypted payload to
http://controller:6080/inform at a regular interval (default is 10 seconds) and
will expect an encrypted payload to be returned. If the device gets a command
response instead of a noop response it will immediately do another inform; this
continues until the controller sends the next noop response. Responses never
appear to contain multiple commands.

## Raw Packet Structure
| Size     | Purpose                   | Data Type |
| -------- | ------------------------- | --------- |
| 4 bytes  | magic number              | integer   |
| 4 bytes  | version                   | integer   |
| 6 bytes  | mac address               | string    |
| 2 bytes  | flags                     | short     |
| 16 bytes | AES initialization vector | string    |
| 4 bytes  | data version              | integer   |
| 4 bytes  | data length               | integer   |
| n bytes  | AES encrypted payload     | string    |

## Raw Packet Constraints
* magic must == `1414414933` (TNBU)
* data version must < `1`
* `flags & 0x1 != 0` means encrypted
* `flags & 0x2 != 0` means compressed

## Payload Types
The payload is AES encrypted in CBC mode using PKCS5 padding. They key is the
device auth key from the database or a master key that is hard coded if the
device has not been provisioned yet. The master key is hard coded in the
controller code in the DeviceManager class and pretty easy to find.

    MASTER_KEY = "ba86f2bbe107c7c57eb5f2690775c712"

On devices running protocol version 1 the encrypted payload is just JSON data.
In version 0 of the protocol the data was key=value pairs separated by
newlines. All of the mFi hardware I have access to uses protocol version 1.

The payloads break down into two categories; those coming into the controller
and those going out of the controller.


## Output Payloads
Output payloads are those that originate from the controller and are bound for
the device. These always appear to contain a \_type field. I have observed the
following output payloads.

### Firmware Upgrade
    _type: upgrade
        url: full url to firmware.bin
        datetime: rfc3339 formatted date, server time
        server_time_in_utc: server time in UTC as a unix timestamp (string)
        version: firmware version (string)
        time: server time as unix timestamp (int)
        _id: unknown id string (5232701de4b0457a2f2f031f)
        device_id: device ID from mongodb

### Config Update
    _type: setparam
        port_cfg: configuration for ports as string
        analog_cfg: analog port config (empty for mPower)
        authorized_guests: authorized guests file (empty)
        blocked_sta: blocked stations (empty)
        cfgversion: management config version
        mgmt_cfg: management config file
        port_cfg: output port config (set for mPower)
        system_cfg: system config file
        server_time_in_utc: server time in UTC as a unix timestamp (string)

### Reboot
    _type: reboot
        datetime: rfc3339 formatted date, server time
        device_id: device ID from mongodb
        server_time_in_utc: server time in UTC as a unix timestamp (string)
        time: server time as unix timestamp (int)
        _id: unknown id string (5232701de4b0457a2f2f031f)

### Heartbeat / No-Op
    _type: noop
        interval: next checkin time in seconds (integer)
        server_time_in_utc: server time in UTC as a unix timestamp (string)

### Locate Mode
This mode didn't appear to change anything. My guess is that this should
trigger the LED to blink as it does on the Unifi devices but appears to have no
effect, at least on the mPower devices.

    _type: cmd
        cmd: locate
        datetime: rfc3339 formatted date, server time
        device_id: device ID from mongodb
        server_time_in_utc: server time in UTC as a unix timestamp (string)
        time: server time as unix timestamp (int)
        _id: unknown id string (5232701de4b0457a2f2f031f)

### Command
    _type: cmd
        _admin: admin data object
            _id: mongodb id of admin
            lang: admin language (en_US)
            name: admin username
            x_password: admin password
        _id: unknown id string (5232701de4b0457a2f2f031f)
        datetime: rfc3339 formatted date, server time
        server_time_in_utc: server time in UTC as a unix timestamp (string)
        time: server time as unix timestamp (int)
        device_id: device ID from mongodb
        cmd: command to use (mfi-output to change outputs)
        mac: device mac address
        model: device model (Outlet for mPower)
        off_volt: always 0?? (int)
        port: device port to update (int)
        sId: sensor ID
        timer: always 0?? (int)
        val: output value (int)
        volt: val and volt set to 1 to turn on, 0 to turn off (int)
        dimmer_ramp: always 1?? (int) (only for switch and outlet)


## Input Payloads
Incoming packets appear to be a JSON version of the out put of the `mca-dump`
command on the device. There is definitely some Unifi legacy in here. It
appears that mFi is just using the Unfi firmware and has hacked it a bit for
their use-case so most of the fields outside of alarm are not relevant to the
mFi use-case.

    alarm: list of sensors
        index: port name
        sId: sensor ID hash
        time: device time

        // For mPort Only
        tag: kind of reading presented (magnetic, temperature, humidity)
        type: kind of device (input, analog, output)
        val: value (float)

        // For mPower Only
        entries: list of entry objects
            tag: kind of reading (output, pf, energy_sum, v_rms, i_rms, active_pwr)
            type: sensor type (output, analog, rmsSum, rms)
            val: value (float)

    if_table: list of interfaces and stats
        ip: interface ip
        mac: interface mac address
        name: interface device name (dev handle)
        rx_bytes: bytes received on the interface
        rx_dropped: packets dropped by the interface
        rx_errors: receive errors on the interface
        rx_packets: packets received on the interface
        tx_bytes: bytes transmitted by the interface
        tx_dropped: trasmit drops on the interface
        tx_errors: transmit errors on the interface
        tx_packets: number of packets transmitted by the interface
        type: appears to be the same as name

    radio_table: list of radios in the device
        builtin_ant_gain: gain of builtin antenna
        builtin_antenna: boolean, does device have antenna
        max_txpower: maximum transmit power
        name: name of radio
        radio: radio type (ex: ng)
        scan_table: list, unknown
        
    vap_table: table of joined wireless networks
        bssid: network SSID
        ccq: client connection qality
        channel: channel number
        essid: network friendly name
        id: mode? (ex: user)
        name: uplink device name
        num_sta: number of connected stations (always 0)
        radio: radio type (ex: ng)
        rx_bytes: bytes received on the interface
        rx_dropped: packets dropped by the interface
        rx_errors: receive errors on the interface
        rx_packets: packets received on the interface
        tx_bytes: bytes transmitted by the interface
        tx_dropped: trasmit drops on the interface
        tx_errors: transmit errors on the interface
        tx_packets: number of packets transmitted by the interface
        rx_crypts: unknown
        rx_frags: received fragmented packets
        rx_nwids: received network beacons
        tx_power: transmitting power of the radio (assumed in dBm)
        tx_retries: number of transmit retries on interface
        usage: same as id

    hostname: hostname of device ("ubnt" unless changed)
    ip: IP of device
    mac: mac address of primary interface
    mfi: boolean, indicates if an mfi device
    model: device model name
    model_display: display name for device
    serial: device serial number
    uptime: uptime in seconds since last reboot
    version: firmware version
    default: boolean, device is unconfigured
    cfgversion: string, unknown (ex: c3846443e1b4860b)
    guest_token: string, unknown (ex: 364E8B215D16AB963A53232E3873000C)
    inform_url: string, url to which the device is reporting
    isolated: boolean, can the device reach the rest of the network
    localversion: string, unknown (ex: ?)
    locating: boolean, is the device in locating mode (blinking LED)
    portversion: string, unknown (ex: 443eb55240f26367)
    time: integer, device time as unix timestamp
    trackable: boolean as string, unknown
    uplink: string, unix device name (dev handle) of the primary uplink device


## Config Samples
These are some observed configuration payloads for the configuration packets.
In their json form it is a single line string with newlines encoded as `\n`.

### mgmt cfg
    mgmt.is_default=false
    mgmt.authkey=41d6529fd555fbb1bdeeafeb995510fa
    mgmt.cfgversion=f1bb359840b519a4
    mgmt.servers.1.url=http://172.16.0.38:6080/inform
    mgmt.selfrun_guest=pass
    selfrun_guest=pass
    cfgversion=f1bb359840b519a4


### port cfg
    port.0.sensorId=52210822e4b0959e7fe94009
    vpower.1.rep_output=1
    vpower.1.rep_pf=1
    vpower.1.rep_energy_sum=1
    vpower.1.rep_v_rms=1
    vpower.1.rep_i_rms=1
    vpower.1.rep_active_pwr=1
    vpower.1.relay=1
    vpower.1.output_tag=output
    vpower.1.pf_tag=pf
    vpower.1.energy_sum_tag=energy_sum
    vpower.1.v_rms_tag=v_rms
    vpower.1.i_rms_tag=i_rms
    vpower.1.active_pwr_tag=active_pwr
    port.1.sensorId=5221082be4b0959e7fe9400a
    vpower.2.rep_output=1
    vpower.2.rep_pf=1
    vpower.2.rep_energy_sum=1
    vpower.2.rep_v_rms=1
    vpower.2.rep_i_rms=1
    vpower.2.rep_active_pwr=1
    vpower.2.relay=1
    vpower.2.output_tag=output
    vpower.2.pf_tag=pf
    vpower.2.energy_sum_tag=energy_sum
    vpower.2.v_rms_tag=v_rms
    vpower.2.i_rms_tag=i_rms
    vpower.2.active_pwr_tag=active_pwr
    port.2.sensorId=5221083be4b0959e7fe9400b
    vpower.3.rep_output=1
    vpower.3.rep_pf=1
    vpower.3.rep_energy_sum=1
    vpower.3.rep_v_rms=1
    vpower.3.rep_i_rms=1
    vpower.3.rep_active_pwr=1
    vpower.3.relay=0
    vpower.3.output_tag=output
    vpower.3.pf_tag=pf
    vpower.3.energy_sum_tag=energy_sum
    vpower.3.v_rms_tag=v_rms
    vpower.3.i_rms_tag=i_rms
    vpower.3.active_pwr_tag=active_pwr


### system cfg
    # users
    users.status=enabled
    users.1.name=admin
    users.1.password=Mq9xt5C8DjcLA
    users.1.status=enabled
    # bridge
    bridge.status=disabled
    bridge.1.devname=br0
    bridge.1.fd=1
    bridge.1.stp.status=disabled
    bridge.1.port.1.devname=eth1
    snmp.status=disabled
    ppp.status=disabled
    pwdog.status=disabled
    dnsmasq.status=disabled
    dhcpd.status=disabled
    httpd.status=disabled
    httpd.port.http=80
    httpd.port=80
    igmpproxy.status=disabled
    telnetd.status=disabled
    tshaper.status=disabled
    netmode=bridge
    ntpclient.status=disabled
    ntpclient.1.server=pool.ntp.org
    ntpclient.1.status=disabled
    syslog.status=enabled
    resolv.status=enabled
    resolv.host.1.name=OfficePowerStrip
    resolv.nameserver.1.status=disabled
    resolv.nameserver.2.status=disabled
    dhcpc.status=enabled
    dhcpc.1.status=enabled
    dhcpc.1.devname=eth1
    route.status=enabled
    vlan.status=disabled
    radio.1.ack.auto=disabled
    radio.1.ackdistance=300
    radio.1.acktimeout=30
    radio.1.ampdu.status=enabled
    radio.1.clksel=1
    radio.1.countrycode=840
    radio.1.cwm.enable=0
    radio.1.cwm.mode=1
    radio.1.forbiasauto=0
    radio.1.channel=0
    radio.1.ieee_mode=11nght40
    radio.1.mcastrate=auto
    radio.1.mode=managed
    radio.1.puren=0
    radio.1.rate.auto=enabled
    radio.1.rate.mcs=auto
    radio.1.txpower=auto
    # wlans (radio)
    radio.status=enabled
    radio.countrycode=840
    aaa.status=disabled
    wireless.status=enabled
    dhcpc.2.status=enabled
    dhcpc.2.devname=ath0
    bridge.1.port.2.devname=ath0
    radio.1.devname=ath0
    radio.1.status=enabled
    aaa.1.br.devname=br0
    aaa.1.devname=ath0
    aaa.1.driver=madwifi
    aaa.1.ssid=
    aaa.1.status=disabled
    wireless.1.mode=managed
    wireless.1.devname=ath0
    wireless.1.status=enabled
    wireless.1.authmode=1
    wireless.1.l2_isolation=disabled
    wireless.1.is_guest=false
    wireless.1.security=none
    wireless.1.addmtikie=disabled
    wireless.1.ssid=
    wireless.1.hide_ssid=enabled
    wireless.1.mac_acl.status=disabled
    wireless.1.mac_acl.policy=deny
    wireless.1.wmm=enabled
    # netconf
    netconf.status=enabled
    netconf.1.devname=eth1
    netconf.1.autoip.status=disabled
    netconf.1.ip=0.0.0.0
    netconf.1.promisc=enabled
    netconf.1.status=enabled
    netconf.1.up=enabled
    netconf.2.devname=br0
    netconf.2.autoip.status=disabled
    netconf.2.ip=0.0.0.0
    netconf.2.status=enabled
    netconf.2.up=enabled
    netconf.3.devname=ath0
    netconf.3.autoip.status=disabled
    netconf.3.ip=0.0.0.0
    netconf.3.promisc=enabled
    netconf.3.status=enabled
    netconf.3.up=enabled
    qos.status=enabled
    qos.group.1.rate=100
    qos.group.2.rate=100
    qos.group.6.rate=100
    qos.if.1.devname=eth1
    qos.if.1.devspeed=100
    qos.if.1.group=1
    qos.if.2.devname=ath0
    qos.if.2.devspeed=150
    qos.if.2.group=20