Ubiquiti Bullet M2HP XW

By: Owen T. Heisler
Updated: 2024-02-15
Published: 2021-01-18

About

Ubiquiti’s Bullet M line includes the Bullet M2HP along with three other devices. The bullet radios (and other Ubiquiti devices) have two different hardware revisions: the older XM and the newer XW (see Choosing the Correct Device Firmware on Ubiquiti Support). This page applies to the Bullet M2HP XW specifically.

Installing OpenWrt 19.07.5

The OpenWrt operating system provides several advantages over the stock Ubiquiti firmware, including more features in the standard distribution, a large set of additional packages that can be installed, and the possibility of a common operating system across different types and models of embedded hardware.

Unfortunately Ubiquiti’s devices use signed firmware which prevents a straightforward installation of alternative firmware such as OpenWrt. However, True Systems has shared two workarounds for this in a GitHub repository. The following steps worked for me with OpenWrt 19.07.5 and a Bullet M2HP XW.

  1. Download the official AirOS XW.v6.1.7.32555.180523.1754.bin firmware from Ubiquiti. Install it using the normal procedure via the web interface.

    $ wget 'https://dl.ui.com/firmwares/XW-fw/v6.1.7/XW.v6.1.7.32555.180523.1754.bin'
    $ sha256sum XW.v6.1.7.32555.180523.1754.bin
    c6e36003ab82b588936b558dfaf06fd2e54e3bd63b84f17687e023de55f05b23  XW.v6.1.7.32555.180523.1754.bin
  2. Reset the router to factory defaults using the web interface.

  3. Make sure you can ping the router and log in via SSH. The default password is ubnt. If you get no matching host key type found then add the following to your ~/.ssh/config:

    Host 192.168.1.20
        HostKeyAlgorithms ssh-dss
    $ ping -c2 192.168.1.20
    PING 192.168.1.20 (192.168.1.20) 56(84) bytes of data.
    64 bytes from 192.168.1.20: icmp_seq=1 ttl=64 time=0.718 ms
    64 bytes from 192.168.1.20: icmp_seq=2 ttl=64 time=0.714 ms
    
    --- 192.168.1.20 ping statistics ---
    2 packets transmitted, 2 received, 0% packet loss, time 14ms
    rtt min/avg/max/mdev = 0.714/0.716/0.718/0.002 ms
    $ ssh ubnt@192.168.1.20
    ubnt@192.168.1.20's password:
    XW.v6.1.7# exit
  4. Clone the true-systems/ubnt-openwrt-flashing repository and make it your current working directory. Update the username and/or IP address in the Makefile if necessary (it should match the SSH user@hostname destination used in the previous step).

    $ git clone https://github.com/true-systems/ubnt-openwrt-flashing
    $ cd ubnt-openwrt-flashing/
    $ git rev-parse HEAD
    9a8fb9f0bb131aae6449771ead59190528956c73
    $ grep ^REMOTE_UBNT Makefile
    REMOTE_UBNT ?= ubnt@192.168.1.20
  5. Download the OpenWrt 19.07.5 factory image for the device.

    $ wget 'https://downloads.openwrt.org/releases/19.07.5/targets/ath79/generic/openwrt-19.07.5-ath79-generic-ubnt_bullet-m-xw-squashfs-factory.bin'
    $ sha256sum openwrt-19.07.5-ath79-generic-ubnt_bullet-m-xw-squashfs-factory.bin
    e2784584dc8a0dc8fca775d98ebac4796f94e85bde8f422b944511bb834be3b5  openwrt-19.07.5-ath79-generic-ubnt_bullet-m-xw-squashfs-factory.bin
  6. Run the automated flashing process. A backup of the stock firmware will be saved to firmware-backup.bin—you may want to keep this file in case it is necessary to revert to the stock firmware in the future.

      1 $ make flash-factory FW_OWRT=openwrt-19.07.5-ath79-generic-ubnt_bullet-m-xw-squashfs-factory.bin
      2 scp ubnt@192.168.1.20:/sbin/ubntbox ubntbox
      3 ubnt@192.168.1.20's password:
      4 ubntbox                                        100%  698KB   1.5MB/s   00:00
      5 ssh-copy-id ubnt@192.168.1.20
      6 /usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/user/.ssh/id_rsa.pub"
      7 /usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
      8 /usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
      9 ubnt@192.168.1.20's password:
     10 
     11 Number of key(s) added: 1
     12 
     13 Now try logging into the machine, with:   "ssh 'ubnt@192.168.1.20'"
     14 and check to make sure that only the key(s) you wanted were added.
     15 
     16 Creating factory firmware backup
     17 ssh ubnt@192.168.1.20 "cat /dev/mtd2 /dev/mtd3" > firmware-backup.bin
     18 ssh ubnt@192.168.1.20 "umount /tmp; mount -t tmpfs tmpfs /tmp"
     19 umount: can't unmount /tmp: Invalid argument
     20 scp ubntbox.patched ubnt@192.168.1.20:/tmp/fwupdate.real
     21 ubntbox.patched                                100%  698KB   1.3MB/s   00:00
     22 scp openwrt-19.07.5-ath79-generic-ubnt_bullet-m-xw-squashfs-factory.bin ubnt@192.168.1.20:/tmp
     23 openwrt-19.07.5-ath79-generic-ubnt_bullet-m-xw 100% 4096KB   1.3MB/s   00:03
     24 ssh ubnt@192.168.1.20 "/tmp/fwupdate.real -m /tmp/openwrt-19.07.5-ath79-generic-ubnt_bullet-m-xw-squashfs-factory.bin -d 2>&1" | tee flash-factory.log
     25 Found mtd block: /dev/mtd0(u-boot)
     26 Found mtd block: /dev/mtd1(u-boot-env)
     27 Found mtd block: /dev/mtd2(kernel)
     28 Found mtd block: /dev/mtd3(rootfs)
     29 Found mtd block: /dev/mtd4(cfg)
     30 Found mtd block: /dev/mtd5(EEPROM)
     31 Got U-Boot variable: mtdparts = mtdparts=ath-nor0:256k(u-boot),64k(u-boot-env),1024k(kernel),6528k(rootfs),256k(cfg),64k(EEPROM)
     32 Adding U-Boot partition: u-boot 9F000000 00040000
     33 Adding U-Boot partition: u-boot-env 9F040000 00010000
     34 Adding U-Boot partition: kernel 9F050000 00100000
     35 Adding U-Boot partition: rootfs 9F150000 00660000
     36 Adding U-Boot partition: cfg 9F7B0000 00040000
     37 Adding U-Boot partition: EEPROM 9F7F0000 00010000
     38 Calculating flash size:
     39 Adding block: /dev/mtd0("u-boot") - size: 00040000
     40 Adding block: /dev/mtd1("u-boot-env") - size: 00010000
     41 Adding block: /dev/mtd2("kernel") - size: 00100000
     42 Adding block: /dev/mtd3("rootfs") - size: 00660000
     43 Adding block: /dev/mtd4("cfg") - size: 00040000
     44 Adding block: /dev/mtd5("EEPROM") - size: 00010000
     45 Total flash size: 00800000
     46 Flash start: 9F000000
     47 Flash end: 9F800000
     48 Header MAGIC 'OPEN'
     49 Current: XW.ar934x.v6.1.7.32555.180523.1754
     50 
     51 New ver: XW.ar934x.v6.0.4-42.OpenWrt-r11257-5090152ae3
     52 Versions: New(393220) 6.0.4, Required(393220) 6.0.4
     53 FW Part: "kernel"(1), MAGIC: 'PART', Base: 0x9F050000, DLen: 0x00100000, PLen: 0x00100000
     54 FW Part: "rootfs"(2), MAGIC: 'PART', Base: 0x9F150000, DLen: 0x00300004, PLen: 0x00660000
     55 Signature verified
     56 Signature MAGIC 'END.'
     57 FW Part: "kernel"(1), MAGIC: 'PART', Base: 0x9F050000, DLen: 0x00100000, PLen: 0x00100000
     58 FW Part: "rootfs"(2), MAGIC: 'PART', Base: 0x9F150000, DLen: 0x00300004, PLen: 0x00660000
     59 Adding adjusted FW partition:
     60 name:'kernel'
     61 flash_base:0x9F050000
     62 mem_base:0x80002000
     63 size:0x00100000
     64 entry_point:0x80002000
     65 data_len:0x00100000
     66 desc_cksum:0x00000000
     67 file_cksum:0x00000000
     68 ===========================
     69 Adding adjusted FW partition:
     70 name:'rootfs'
     71 flash_base:0x9F150000
     72 mem_base:0xBDBDBDBD
     73 size:0x00660000
     74 entry_point:0xBDBDBDBD
     75 data_len:0x00300004
     76 desc_cksum:0x00000000
     77 file_cksum:0x00000000
     78 ===========================
     79 Signature MAGIC 'END.'
     80 Working(1) with block: /dev/mtd0
     81 Skipping: artificial: 0, unallocated: 0,writeable: 1024(WRITEABLE: 400, flags: C00), fw.flash_base: 9F050000 < blk->base: 9F000000 + blk->size: 40000
     82 Copying FIS partition: 0 =>
     83 name:'u-boot'
     84 flash_base:0x9F000000
     85 mem_base:0xBDBDBDBD
     86 size:0x00040000
     87 entry_point:0xBDBDBDBD
     88 data_len:0x00040000
     89 desc_cksum:0xBDBDBDBD
     90 file_cksum:0xBDBDBDBD
     91 ===========================
     92 Working(1) with block: /dev/mtd1
     93 Skipping: artificial: 0, unallocated: 0,writeable: 1024(WRITEABLE: 400, flags: C00), fw.flash_base: 9F050000 < blk->base: 9F040000 + blk->size: 10000
     94 Copying FIS partition: 1 =>
     95 name:'u-boot-env'
     96 flash_base:0x9F040000
     97 mem_base:0xBDBDBDBD
     98 size:0x00010000
     99 entry_point:0xBDBDBDBD
    100 data_len:0x00010000
    101 desc_cksum:0xBDBDBDBD
    102 file_cksum:0xBDBDBDBD
    103 ===========================
    104 Working(1) with block: /dev/mtd2
    105 Working(2) with block: /dev/mtd2
    106 End check: 9F050000 + 00100000 <= 9F150000
    107 Creating FIS partition: 2 =>
    108 name:'kernel'
    109 flash_base:0x9F050000
    110 mem_base:0x80002000
    111 size:0x00100000
    112 entry_point:0x80002000
    113 data_len:0x00100000
    114 desc_cksum:0x00000000
    115 file_cksum:0x00000000
    116 ===========================
    117 Working(1) with block: /dev/mtd2
    118 Skipping: artificial: 0, unallocated: 0,writeable: 1024(WRITEABLE: 400, flags: C00), fw.flash_base: 9F150000 < blk->base: 9F050000 + blk->size: 100000
    119 Working(1) with block: /dev/mtd3
    120 Working(2) with block: /dev/mtd3
    121 End check: 9F150000 + 00660000 <= 9F7B0000
    122 Creating FIS partition: 3 =>
    123 name:'rootfs'
    124 flash_base:0x9F150000
    125 mem_base:0xBDBDBDBD
    126 size:0x00660000
    127 entry_point:0xBDBDBDBD
    128 data_len:0x00300004
    129 desc_cksum:0x00000000
    130 file_cksum:0x00000000
    131 ===========================
    132 Working(3) with block: /dev/mtd4
    133 Copying FIS partition: 4 <=
    134 name:'cfg'
    135 flash_base:0x9F7B0000
    136 mem_base:0xBDBDBDBD
    137 size:0x00040000
    138 entry_point:0xBDBDBDBD
    139 data_len:0x00040000
    140 desc_cksum:0xBDBDBDBD
    141 file_cksum:0xBDBDBDBD
    142 ===========================
    143 Working(3) with block: /dev/mtd5
    144 Copying FIS partition: 5 <=
    145 name:'EEPROM'
    146 flash_base:0x9F7F0000
    147 mem_base:0xBDBDBDBD
    148 size:0x00010000
    149 entry_point:0xBDBDBDBD
    150 data_len:0x00010000
    151 desc_cksum:0xBDBDBDBD
    152 file_cksum:0xBDBDBDBD
    153 ===========================
    154 New FIS entries count 6
    155 Executing: '/bin/updatefix.sh 393479 393220'
    156 Current ver: 393479
    157 New version: 393220
    158 No need to fix.
    159 '/bin/updatefix.sh 393479 393220' result: 0
    160 Working(1) with block: /dev/mtd0
    161 Skipping: artificial: 0, unallocated: 0,writeable: 1024(WRITEABLE: 400, flags: C00), fw.flash_base: 9F050000 < blk->base: 9F000000 + blk->size: 40000
    162 Copying FIS partition: 0 =>
    163 name:'u-boot'
    164 flash_base:0x9F000000
    165 mem_base:0xBDBDBDBD
    166 size:0x00040000
    167 entry_point:0xBDBDBDBD
    168 data_len:0x00040000
    169 desc_cksum:0xBDBDBDBD
    170 file_cksum:0xBDBDBDBD
    171 ===========================
    172 Working(1) with block: /dev/mtd1
    173 Skipping: artificial: 0, unallocated: 0,writeable: 1024(WRITEABLE: 400, flags: C00), fw.flash_base: 9F050000 < blk->base: 9F040000 + blk->size: 10000
    174 Copying FIS partition: 1 =>
    175 name:'u-boot-env'
    176 flash_base:0x9F040000
    177 mem_base:0xBDBDBDBD
    178 size:0x00010000
    179 entry_point:0xBDBDBDBD
    180 data_len:0x00010000
    181 desc_cksum:0xBDBDBDBD
    182 file_cksum:0xBDBDBDBD
    183 ===========================
    184 Working(1) with block: /dev/mtd2
    185 Working(2) with block: /dev/mtd2
    186 Unlocking /dev/mtd2(kernel) ...
    187 Unlocking device failed: ioctl(MEMUNLOCK): Operation not supported
    188 Writing 'kernel         ' to /dev/mtd2(kernel         ) ...
    189 Writing: 0x00100000 bytes with offset 0x00000000
    190 [%0  ]
    191 Block on '/dev/mtd2' at 00000000(len: 00010000) has changes.               [%6  ]
    192 Block on '/dev/mtd2' at 00010000(len: 00010000) has changes.               [%12 ]
    193 Block on '/dev/mtd2' at 00020000(len: 00010000) has changes.               [%18 ]
    194 Block on '/dev/mtd2' at 00030000(len: 00010000) has changes.               [%25 ]
    195 Block on '/dev/mtd2' at 00040000(len: 00010000) has changes.               [%31 ]
    196 Block on '/dev/mtd2' at 00050000(len: 00010000) has changes.               [%37 ]
    197 Block on '/dev/mtd2' at 00060000(len: 00010000) has changes.               [%43 ]
    198 Block on '/dev/mtd2' at 00070000(len: 00010000) has changes.               [%50 ]
    199 Block on '/dev/mtd2' at 00080000(len: 00010000) has changes.               [%56 ]
    200 Block on '/dev/mtd2' at 00090000(len: 00010000) has changes.               [%62 ]
    201 Block on '/dev/mtd2' at 000A0000(len: 00010000) has changes.               [%68 ]
    202 Block on '/dev/mtd2' at 000B0000(len: 00010000) has changes.               [%75 ]
    203 Block on '/dev/mtd2' at 000C0000(len: 00010000) has changes.               [%81 ]
    204 Block on '/dev/mtd2' at 000D0000(len: 00010000) has changes.               [%87 ]
    205 Block on '/dev/mtd2' at 000E0000(len: 00010000) has changes.               [%93 ]
    206 Block on '/dev/mtd2' at 000F0000(len: 00010000) has changes.               [%100]
    207 
    208 End check: 9F050000 + 00100000 <= 9F150000
    209 Creating FIS partition: 2 =>
    210 name:'kernel'
    211 flash_base:0x9F050000
    212 mem_base:0x80002000
    213 size:0x00100000
    214 entry_point:0x80002000
    215 data_len:0x00100000
    216 desc_cksum:0x00000000
    217 file_cksum:0x00000000
    218 ===========================
    219 Working(1) with block: /dev/mtd2
    220 Skipping: artificial: 0, unallocated: 0,writeable: 1024(WRITEABLE: 400, flags: C00), fw.flash_base: 9F150000 < blk->base: 9F050000 + blk->size: 100000
    221 Working(1) with block: /dev/mtd3
    222 Working(2) with block: /dev/mtd3
    223 Unlocking /dev/mtd3(rootfs) ...
    224 Unlocking device failed: ioctl(MEMUNLOCK): Operation not supported
    225 Writing 'rootfs         ' to /dev/mtd3(rootfs         ) ...
    226 Writing: 0x00300004 bytes with offset 0x00000000
    227 [%0  ]
    228 Block on '/dev/mtd3' at 00000000(len: 00010000) has changes.               [%2  ]
    229 Block on '/dev/mtd3' at 00010000(len: 00010000) has changes.               [%4  ]
    230 Block on '/dev/mtd3' at 00020000(len: 00010000) has changes.               [%6  ]
    231 Block on '/dev/mtd3' at 00030000(len: 00010000) has changes.               [%8  ]
    232 Block on '/dev/mtd3' at 00040000(len: 00010000) has changes.               [%10 ]
    233 Block on '/dev/mtd3' at 00050000(len: 00010000) has changes.               [%12 ]
    234 Block on '/dev/mtd3' at 00060000(len: 00010000) has changes.               [%14 ]
    235 Block on '/dev/mtd3' at 00070000(len: 00010000) has changes.               [%16 ]
    236 Block on '/dev/mtd3' at 00080000(len: 00010000) has changes.               [%18 ]
    237 Block on '/dev/mtd3' at 00090000(len: 00010000) has changes.               [%20 ]
    238 Block on '/dev/mtd3' at 000A0000(len: 00010000) has changes.               [%22 ]
    239 Block on '/dev/mtd3' at 000B0000(len: 00010000) has changes.               [%24 ]
    240 Block on '/dev/mtd3' at 000C0000(len: 00010000) has changes.               [%27 ]
    241 Block on '/dev/mtd3' at 000D0000(len: 00010000) has changes.               [%29 ]
    242 Block on '/dev/mtd3' at 000E0000(len: 00010000) has changes.               [%31 ]
    243 Block on '/dev/mtd3' at 000F0000(len: 00010000) has changes.               [%33 ]
    244 Block on '/dev/mtd3' at 00100000(len: 00010000) has changes.               [%35 ]
    245 Block on '/dev/mtd3' at 00110000(len: 00010000) has changes.               [%37 ]
    246 Block on '/dev/mtd3' at 00120000(len: 00010000) has changes.               [%39 ]
    247 Block on '/dev/mtd3' at 00130000(len: 00010000) has changes.               [%41 ]
    248 Block on '/dev/mtd3' at 00140000(len: 00010000) has changes.               [%43 ]
    249 Block on '/dev/mtd3' at 00150000(len: 00010000) has changes.               [%45 ]
    250 Block on '/dev/mtd3' at 00160000(len: 00010000) has changes.               [%47 ]
    251 Block on '/dev/mtd3' at 00170000(len: 00010000) has changes.               [%49 ]
    252 Block on '/dev/mtd3' at 00180000(len: 00010000) has changes.               [%52 ]
    253 Block on '/dev/mtd3' at 00190000(len: 00010000) has changes.               [%54 ]
    254 Block on '/dev/mtd3' at 001A0000(len: 00010000) has changes.               [%56 ]
    255 Block on '/dev/mtd3' at 001B0000(len: 00010000) has changes.               [%58 ]
    256 Block on '/dev/mtd3' at 001C0000(len: 00010000) has changes.               [%60 ]
    257 Block on '/dev/mtd3' at 001D0000(len: 00010000) has changes.               [%62 ]
    258 Block on '/dev/mtd3' at 001E0000(len: 00010000) has changes.               [%64 ]
    259 Block on '/dev/mtd3' at 001F0000(len: 00010000) has changes.               [%66 ]
    260 Block on '/dev/mtd3' at 00200000(len: 00010000) has changes.               [%68 ]
    261 Block on '/dev/mtd3' at 00210000(len: 00010000) has changes.               [%70 ]
    262 Block on '/dev/mtd3' at 00220000(len: 00010000) has changes.               [%72 ]
    263 Block on '/dev/mtd3' at 00230000(len: 00010000) has changes.               [%74 ]
    264 Block on '/dev/mtd3' at 00240000(len: 00010000) has changes.               [%77 ]
    265 Block on '/dev/mtd3' at 00250000(len: 00010000) has changes.               [%79 ]
    266 Block on '/dev/mtd3' at 00260000(len: 00010000) has changes.               [%81 ]
    267 Block on '/dev/mtd3' at 00270000(len: 00010000) has changes.               [%83 ]
    268 Block on '/dev/mtd3' at 00280000(len: 00010000) has changes.               [%85 ]
    269 Block on '/dev/mtd3' at 00290000(len: 00010000) has changes.               [%87 ]
    270 Block on '/dev/mtd3' at 002A0000(len: 00010000) has changes.               [%89 ]
    271 Block on '/dev/mtd3' at 002B0000(len: 00010000) has changes.               [%91 ]
    272 Block on '/dev/mtd3' at 002C0000(len: 00010000) has changes.               [%93 ]
    273 Block on '/dev/mtd3' at 002D0000(len: 00010000) has changes.               [%95 ]
    274 Block on '/dev/mtd3' at 002E0000(len: 00010000) has changes.               [%97 ]
    275 Block on '/dev/mtd3' at 002F0000(len: 00010000) has changes.               [%99 ]
    276 Block on '/dev/mtd3' at 00300000(len: 00000004) has changes.               [%100]
    277 
    278 End check: 9F150000 + 00660000 <= 9F7B0000
    279 Creating FIS partition: 3 =>
    280 name:'rootfs'
    281 flash_base:0x9F150000
    282 mem_base:0xBDBDBDBD
    283 size:0x00660000
    284 entry_point:0xBDBDBDBD
    285 data_len:0x00300004
    286 desc_cksum:0x00000000
    287 file_cksum:0x00000000
    288 ===========================
    289 Working(3) with block: /dev/mtd4
    290 Copying FIS partition: 4 <=
    291 name:'cfg'
    292 flash_base:0x9F7B0000
    293 mem_base:0xBDBDBDBD
    294 size:0x00040000
    295 entry_point:0xBDBDBDBD
    296 data_len:0x00040000
    297 desc_cksum:0xBDBDBDBD
    298 file_cksum:0xBDBDBDBD
    299 ===========================
    300 Working(3) with block: /dev/mtd5
    301 Copying FIS partition: 5 <=
    302 name:'EEPROM'
    303 flash_base:0x9F7F0000
    304 mem_base:0xBDBDBDBD
    305 size:0x00010000
    306 entry_point:0xBDBDBDBD
    307 data_len:0x00010000
    308 desc_cksum:0xBDBDBDBD
    309 file_cksum:0xBDBDBDBD
    310 ===========================
    311 New FIS entries count 6
    312 FIS Change: change in partition entry 'kernel'.
    313 FIS Change: change in partition entry 'rootfs'.
    314 FIS Change: added partition terminator instead of 0xBD.
    315 New partition count: 6, changes: 3
    316 Done

    At this point, or soon after, OpenWrt should be running and accessible at 192.168.1.1. You may need to press Ctrl-C to exit, due to the SSH session not being terminating properly when the final firmware update completes, but make sure you don’t interrupt any part of the flashing process (if in doubt, wait).

Updating OpenWrt

Once OpenWrt has been installed, it can be updated with an OpenWrt sysupgrade image.

An OpenWrt sysupgrade has been tested with OpenWrt 19.07.10 and with r25153-869df9ecdf.

$ sha256sum openwrt-19.07.10-ath79-generic-ubnt_bullet-m-xw-squashfs-sysupgrade.bin
806ddc698b545b97ce3ed9d6cf4830de5c24da9ba31d1f1040be9f3d4e5a4965  openwrt-19.07.10-ath79-generic-ubnt_bullet-m-xw-squashfs-sysupgrade.bin

Using TFTP recovery

If something goes wrong at some point after a successful installation of OpenWrt (for example a failed upgrade or corrupted filesystem) and you need to flash OpenWrt again, you can use TFTP recovery as follows.

  1. Download the OpenWrt factory image to be flashed to the device. Save this file to /tmp/openwrt.bin or a similar location.

    $ sha256sum openwrt-19.07.8-ath79-generic-ubnt_bullet-m-xw-squashfs-factory.bin
    d0f3fbdba489026fab60872f0646f43eb691eb2b27af014a4656c2f2ac69ca24  openwrt-19.07.8-ath79-generic-ubnt_bullet-m-xw-squashfs-factory.bin
  2. Install the tftp tool. On Debian GNU/Linux for example, run apt install tftp.

  3. Disconnect power from the device. Use a paperclip to press and hold the reset button (either on the device or on the power-over-Ethernet injector, if it has one). While holding the reset button, connect power. Wait 25 seconds, or until the LED indicates upgrade mode is active; then release the button.

  4. Change to the OpenWrt image location, run tftp, and enter the commands shown here:

    $ cd /tmp
    $ tftp
    tftp> connect 192.168.1.20
    tftp> binary
    tftp> rexmt 1
    tftp> timeout 60
    tftp> put openwrt.bin
  5. The file should begin transferring. When it completes, you should see Sent 4194716 bytes in 2.5 seconds (for example). The device will flash the firmware and reboot when it is finished (do not reboot it yourself). Once it is finished, you should be able to connect to the router at 192.168.1.1.

Restoring the stock firmware

Before the OpenWrt image is flashed in the previous section, a backup of the AirOS v6.1.7 firmware is written to firmware-backup.bin. To restore this firmware, use the corresponding make target, providing the correct SSH user@hostname destination for the device. See Restore from OpenWrt back to factory image in the true-systems/ubnt-openwrt-flashing README.

This worked for me from OpenWrt 19.07.8.

$ cd ubnt-openwrt-flashing/
$ stat -c "%s bytes" firmware-backup.bin
7733248 bytes
$ sha256sum firmware-backup.bin
b0fb2d237cc689402b0a6d4bb8adf9f5f97748ee23f03c5bc7b4b0a69b30e357  firmware-backup.bin
$ make restore REMOTE_OWRT=root@192.168.1.1

(Your firmware backup may not be the same as the one I tested.)

Credits

  • OpenWrt is an excellent Linux distribution for embedded devices.

  • True Systems shared two ways to work around Ubiquiti’s firmware signing, one of which is shown here.

See also

Changes

  • 2024-02-15: Add details about the Ethernet fix

  • 2024-02-08: Update source repository commit and remove scp -O step that is now unnecessary

  • 2023-10-21: Use AirOS default settings, add temporary SSH host configuration, update source repository commit, add scp -O option, update tested OpenWrt version to 19.07.10

  • 2022-02-24: Updated the warning about OpenWrt 21.02.1

  • 2022-02-12: Updated Restoring the stock firmware section since testing the restore process

  • 2022-01-28: Added the Using TFTP recovery section

  • 2022-01-28: Added a warning about OpenWrt 21.02.1 in the Updating OpenWrt section

  • 2022-01-28: Updated the true-systems/ubnt-openwrt-flashing repository commit (no relevant changes upstream)

  • 2021-04-03: Added the Updating OpenWrt section