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
|