Custom Firmware for the Xiaomi AX3600 Wireless Router

As I have mentioned in the review, the stock firmware on the Xiaomi AX3600 wireless router is extremely limiting. On top of that, the firmware is also locked to install only authorized updates from the manufacturer. If you have been following the blog, you will know that I like the flexibility that ASUSWRT provides for customizing my router.

While there is currently an on-going effort to try and port vanilla OpenWRT for this router, I suspect that might take some time. In this post, I describe how to workaround the lousy firmware and configure the router with the advanced features I need.

Router Disassembly

It is recommended to have UART access handy, in case something bad happens and you need to recover your router, or if you want access to U-Boot, the bootloader. This would require you to crack open your router, so you might only want to do this if necessary. Feel free to skip this section if you are not interested in the hardware, or don’t need low-level access.

router top view, with cover opened

You need to unscrew 5 screws, 4 of which are hidden under the rubber feet, and one under the center sticker label. In the disassembled top view photo here, you can see the screw holes at the corners, as well as a missing chunk in the center of the heatsink for the mating screw post, directly aligned with the AIoT antenna and indicator LEDs.

The clips that hold the two halves in place are the trickiest. I tried my best, but still managed to destroy some clips and a few plastic nubs after excessive prying. The clips are located all along the front and back of the case, as well as at the sides (where the two antennas are). If I were to do this again, I’d advise to start from the side clips because they don’t have any other obstructions. The front & back rows have additional plastic nubs that prevent the top cover from being squeezed inwards easily to release the clips, whereas these aren’t present at the sides.

clip on top cover, broken after disassembly

You can check out more disassembly photos in my Flickr album, to hopefully help you avoid the mistakes I made.

Serial Port

The serial header is located at the left side of the board, with the unit facing towards you, antennas behind. I typically don’t like to solder down the header pins, so I taped the serial cable down to the metal plate to try and keep the header pins in place with friction.

serial connection to the AX3600

They have also helpfully marked the colour of the cables if you are using the typical Chinese UART-to-USB adapter: green, white and black. The serial settings are typical: 115,200 baud, 8N1.

Note that you can only interrupt the boot sequence if you have uart_en=1 and boot_wait=on set in the NVRAM.

Getting a Shell

You need to first get SSH access on the router, which is what some might call “jailbreaking”. This is done using an exploit found by others on the 1.0.17 version. I will not repeat the instructions here, but you essentially need to access a few URLs to

  • patch /etc/init.d/dropbear for unconditional SSH access
  • set a root password
  • start the dropbear service

Later versions do not seem to have any known exploits. But if we just stick to this old version, we won’t get the benefits of newer versions, like 160MHz bandwidth or improved wireless drivers.

Custom Firmware

While there are people working on porting vanilla OpenWRT to the AX3600, I suspect it would be quite some time before we see a usable port. The firmware is currently based on Qualcomm’s QSDK instead of the main OpenWRT tree, which means there might be proprietary changes and drivers that are not available in the main tree (yet). Unfortunately, this is also something I don’t have time to work on, or assist with. The stop-gap solution I have come up with is firmware modding.

Knowing how the update file is structured, we can extract the firmware, remove the protections, and flash the patched firmware directly.

update image format

The update image primarily contains a “root.ubi” portion that is a UBI volume, which has both the kernel and root filesystem in it. Fortunately, the RSA signature on the update image is only verified at the time a new firmware is uploaded and flashed, and not part of a secure boot chain like Android or iOS devices. The kernel portion uses a U-Boot FIT uImage format, which combines the kernel, device-tree blobs and some hashes and settings in a device-tree-like format. The root filesystem is just a squashfs, so it can be unpacked and repacked easily with existing tools.

I wrote a tool called xqrepack, which basically takes the official firmware, dissects it, performs some modifications and puts it back together. The important goal of this modding process is to preserve SSH and UART access, as the official firmware disables them after every upgrade. SSH is crucial to allow direct access to OpenWRT (or Linux) so that we can set up the wireless and Gigabit networking exactly how we want it.

The other task is to remove phone-home functionality from the firmware. In my review, I have mentioned the web UI tracking that is evident by looking at your browser network requests. In the firmware image, I have noticed more logging & collection of statistics on the router, which is routinely uploaded to Xiaomi. If there are any crashes or failed upgrades, these are also reported back to Xiaomi (along with your WiFi configuration settings), which I assume helps them be aware of and fix these problems. Last but not least, using the AIoT functionality of the router also requires interaction with the Xiaomi servers. This has been the case for all of their IoT products anyway. xqrepack will remove all the AIoT functionality and other phone-home mechanisms. Note that in doing so, you will lose AIoT functionality, and also the WAN LED indicator because it pings their servers as a connectivity test. If this is something that you cannot accept, then you should not use this tool, or you can modify the tool yourself.

There has been a few reports of the tool working successfully with the latest version, 1.0.67 at this time of writing, including myself. If you encounter any problems, please feel free to file issues (or pull requests) on the GitHub project.

Once you have a modified firmware image, you can flash this onto your router, using the SSH access obtained in the previous section.

Now, you have a wireless router with updated drivers and firmware, which you can customize beyond what Xiaomi has allowed.

Advanced Wireless Features

For my networking requirements, I need a VLAN segregated guest network, and I also use WPA Enterprise on my network.

Digging into the firmware, Xiaomi has relied on QSDK from Qualcomm to base their firmware on. Internally, it uses hostapd for creating the wireless APs, which means it is quite flexible, and is invokved in such a way that respects the OpenWRT UCI config. Because this firmware uses a dumbed down web UI, we will have to directly edit the OpenWRT config files to achieve what we need.

WPA Enterprise

The wireless AP settings are stored in /etc/config/wireless. An existing AP entry looks like this:

config wifi-iface
    option device 'wifi0'
    option ifname 'wl0'
    ...
    option encryption 'psk2+ccmp'
    option sae '1'
    option sae_password 'mywifipassword'
    option key 'mywifipassword'
    option ieee80211w '1'
    option ssid 'Xiaomi_1234`

The important parameter here is encryption. You can find the full list of “WPA Modes” in the OpenWRT guide here. Anything that is psk* or sae uses a pre-shared key.

In order to use WPA Enterprise, the “encryption” setting needs to be wpa*. If you have only modern devices and don’t have legacy devices trying to connect, you can go ahead and use wpa3 or wpa3-mixed for WPA2/3 Enterprise mode. Otherwise, a good option might be wpa2+ccmp.

You will also need to specify the RADIUS server settings with auth_server and auth_secret. You don’t need to specify the auth_port unless it’s not 1812. Verify that the hostapd file generated at /var/run/hostapd-wl*.conf is correct.

A modified section should look like this:

config wifi-iface
    ...
    option encryption 'wpa2+ccmp'
    option ssid 'Xiaomi_1234'
    option auth_server '192.168.0.1'
    option auth_secret 'testing123'

VLAN Segregated Guest Network

I typically have a guest SSID set up; this is easy to do on a more “advanced” router. In addition, I also want traffic to/from this guest AP to be separated out into its own VLAN.

You will need to create the guest APs (or wireless interfaces) by copying and pasting the current sections from /etc/config/wireless.

There are 3 wifi-devices: wifi0, wifi1 and wifi2. wifi0 and wifi1 correspond to the 5GHz and 2.4GHz radios for the router, and wifi2 (as I speculated in the review) is the 2.4GHz radio for AIoT.

config wifi-device 'wifi0'
    option type 'qcawificfg80211'
    option macaddr '88:c3:97:aa:bb:cc'
    option hwmode '11axa'
    ...

Depending on which radio you want your AP to be on, use the appropriate wifiX device.

After the initial router setup, there should have been 2 wifi-ifaces created by default, one for each band or wifi-device, called wl0 and wl1:

config wifi-iface
    option device 'wifi0'
    option ifname 'wl0'
    option network 'lan'
    option mode 'ap'
    option wpsdevicename 'XiaoMiRouter'
    option ssid 'Xiaomi_1234'
    ...

You will need to duplicate the wl0 and wl1 interfaces to create your own virtual APs. Note that the interface names cannot have dots, so I have named them wl01 and wl11 (a 1 suffix). You can set a different PSK for the virtual APs here or have them use different authentication mechanisms, like WPA Enterprise which I have mentioned.

To put them on a separate guest network, create a “guest” bridge by adding an entry in /etc/config/network. There should already be a “lan” interface section, which is where the LAN ports eth2, eth3 and eth4 are currently placed.

This section creates a bridge called br-guest and places VLAN 10 of all LAN ports into it:

config interface 'guest'
    option proto 'static'
    option ifname 'eth2.10 eth3.10 eth4.10'
    option type 'bridge'

The interfaces listed in option ifname will be created automatically. Make sure the wifi-iface section also specifies option network 'guest' in order for the guest APs to be placed in the same VLAN 10 network.

After you have updated the settings, you can use the wifi reload command to re-configure the network devices, or you can just reboot. To verify that the correct settings have been applied, use brctl show:

root@XiaoQiang:~# brctl show
bridge name   bridge id           STP enabled   interfaces
br-guest      7fff.88c300aabbcc   no            eth2.10
                                                eth3.10
                                                eth4.10
                                                wl01
                                                wl11

br-lan        7fff.88c300aabbcc   no            eth2
                                                eth3
                                                eth4
                                                wl0
                                                wl1

Now, wireless clients that join those APs will be placed on this “guest” network on a separate VLAN. Wireless clients that join the main wl0 and wl1 network will go out over the LAN ports as normal, untagged.

VLAN Caveat

One odd thing I noticed with this setup is that DHCP, DNS and ping requests can get through, but HTTP downloads do not work. Running tcpdump on both the server and the wireless router, I noticed that larger packets of the HTTP traffic did not make it through to the router. The main network did not exhibit this problem though.

I soon realized the problem was related to MTU size. While debugging the VLAN config, this swconfig output caught my eye:

root@XiaoQiang:~# swconfig dev switch0 show
Global attributes:
    enable_vlan: 1
    max_frame_size: 1518
    dump_arl: MAC: 00:0d:bb:aa:bb:cc PORTMAP: 0x08 VID: 0x9 STATUS: 0x0
    ...

The maximum frame size accepted by the switch was 1518, a typical 1500 MTU size plus Ethernet overhead. However, note that encapsulating traffic in a 802.1Q VLAN would add a 4-byte tag, causing the length to increase beyond the size of a typical Ethernet frame.

One easy way to fix this was to set the MTU on the server's interface lower. The default MTU is 1500, so we need to reduce this by 4 to accomodate the VLAN tag:

ip link set dev eth0 mtu 1496

After this, all traffic could pass through the guest network properly, thereby confirming this hypothesis. This would be a hassle if there were multiple devices plugged in to the router that needed to communicate with wireless guests.

I tried doing the reverse, to make the router accomodate larger MTUs:

root@XiaoQiang:~# swconfig dev switch0 get max_frame_size
1518

root@XiaoQiang:~# swconfig dev switch0 set max_frame_size 1528
failed

I noticed that the system scripts also used ssdk_sh to set parameters for the switch, so let’s try that:

dev0@qca>misc frameMaxSize get

This request is not support

dev0@qca>misc frameMaxSize set 1528

This request is not support

I am afraid the frame size is either hardcoded in the drivers somewhere, or worse, in the hardware. This also means that jumbo frames are not supported.

Indicator LEDs

Some routers allow you to turn off the LEDs to be more disceet, or to not be disturbed at night. The AX3600 indicator lights are quite tame, compared to other devices, but in a dark room it can still be distracting. You may also want to re-purpose the indicator lights for your own use.

The good news is that all the LEDs are available in sysfs:

  • led_blue / led_yellow for the SYSTEM indicator
  • net_blue / net_yellow for the INTERNET indicator
  • led_ant on the AIoT antenna indicator

I wrote a toy script to sequence the LEDs, just for fun.

Conclusion

As I mentioned in the review, this is really good router hardware for the price (US$100). You get a 802.11ax router with 4×4 MIMO configuration, 2×2 MIMO for the 2.4GHz band, and an extra 2.4GHz radio for AIoT which you can re-purpose, all powered by a 64-bit quad-core ARM CPU. And now that I realized that you can easily customize the network settings without jumping through too many hoops, it’s an even better value (for me, at least).

You can find the xqrepack tool to repack the firmware on GitHub here, together with some instructions on how to use it. You can also check out the OpenWRT documentation for more resources on how to configure the OS in general — it was a great help to me.

I will still need to assess the long-term stability of the router though, to see if the drivers lock up over time, or stop accepting clients, or stop routing traffic, etc.

9 comments on “Custom Firmware for the Xiaomi AX3600 Wireless Router

  1. Garbaty says:

    global version is available on aliexpress
    https://aliexpress.com/item/1005001346449211.html

  2. Garbaty says:

    is it possible to extract/get firmware url without ssh access ?

    • darell tan says:

      If you know how, you can probably extract the URL from the firmware. The official firmware can be downloaded from miwifi.com. You’ll probably need to translate the page if you don’t read Chinese. These steps can be performed without the hardware, and hence also without SSH access.

      • Garbaty says:

        i have firmware version 1.0.227 some other people have 1.0.67 and only that firmware is available on mifi site i tried to url sniffer but no firmware url

        • darell tan says:

          If you are looking for URLs for older versions of the firmware, you can try searching on forums. The old firmware URLs are still active.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.