Add support for D-Link COVR-X1860

Hello everyone,

The COVR-X1860 are MT7621-based AX1800 devices (similar to DAP-X1860, but with two Ethernet ports and external power supply) that are sold in sets of two (COVR-X1862) and three (COVR-X1863).

Currently, these are available in Germany very cheaply at MediaMarkt / Saturn, the pack of two is € 39,99:
https://www.mediamarkt.de/de/product/_d-link-ax1800-whole-home-mesh-systems-2er-set-wlan-router-1800-mbits-2781527.html

I would like to start collecting information on the current state of supporting these devices here.
The devices can be flashed via the MediaTek bootloader recovery by pressing the reset button during power-on, although this causes issues with some browsers (works best with Chromium) and requires a wired Ethernet connection with manual IP address setting (the recovery is at 192.168.0.1).

I started working on support for this device some time ago, so here is the basic device support using UBI and a fixed kernel size of 8 MiB (outside of UBI), which works fine for flashing via the recovery:

A precompiled binary for those who want to test without building is available here: openwrt-ramips-mt7621-dlink_covr-x1860-a1-squashfs-recovery.bin

However, since the factory image encryption scheme is known as well as the key for signing, it is theoretically feasible to allow flashing via the regular D-Link web interface as well.

The firmware format and encryption are the same as with the older COVR-P2500, COVR-C1200 (both ath79) and several ramips-mt7621 devices from the DIR-8xx / DIR-12xx range. Support for these was attempted in an earlier PR a few years ago:

The only difference are the signing key and the AES key derivation scheme, although both of these are known.

Now the only problem remaining is how to feed a fixed-size-kernel + UBI image to the OEM web-based updater, which uses a former (probably heavily modified) version of sysupgrade from the Mediatek SDK, as well as the bootloader, which uses a dual image partition layout and would thoroughly check both images on every boot, restoring whatever does not look appealing using the image data from the other partition.

I will continue with the current state of efforts in the next post.

3 Likes

To have the factory image accepted by the regular D-Link Web-based updater, the supported_devices entry in the metadata needs to equal the string COVR-X1860.
Curiously, just adding the string via SUPPORTED_DEVICES += COVR-X1860 did not work, since that entry is expected to be a single string within the JSON, not an array as with current versions of OpenWrt. Also, an attribute firmware is required that contains the md5 of the image.

I have implemented both in append-dlink-covr-metadata (though I failed in calling $(MKHASH) from within the echo command and resorted to writing to a tempfile instead; please try to check if anyone can spot my mistake and optimize this :innocent: )

The encryption itself was a C program using libcrypto in the old PR, however in my experimental branch it is currently implemented in Python only, thus requiring PyCrypto (run pip install pycryptodome on the host before building).

This generates an image that is accepted and flashed by the OEM updater, however upon reboot, the Mediatek bootloader could not correctly identify the rootfs (since using UBI) and would proceed to overwrite the freshly-flashed OpenWrt image with the OEM image residing in the other partition :joy:

Dropping UBI in favor of a simple kernel + rootfs (without bad block management) will successfully boot the image after flashing. However, besides lacking UBI there is no persistence, also the tail part of the newly-generated jffs2 would be corrupted after another reboot, since the bootloader would recognize the backup image to be corrupted (by expanding jffs2) and overwrite this with the contents of the main image, i.e. there would be two copies of OpenWrt...

Any suggestions on how to proceed from here are welcome :slightly_smiling_face:

Probably we can append UBI to some fake-rootfs and use okli-loader etc. ?
For the second partiton, place a fake-rootfs there as well, and wrap around our actual image with mtd-split to make use all of the flash space?
I guess it also needs to be checked what happens when the image is written to the other partition by the web updater...

The bootloader recovery would always wipe both partitions and then flash into the first one. When recovering OEM firmware, after initial reboot it would recognize the second partition to be corrupted (i.e. blank), thus duplicate the first one and reboot once more.
When flashing OpenWrt using the recovery, it does not recognize the image (using UBI) as fully ok, thus does not try to duplicate it (especially important when rebooting with OpenWrt installed), but since there is nothing else to do and there was at least a valid-looking kernel, it will just boot the kernel from the broken partition anyways :joy:

Maybe we can somehow reproduce that behavior, even when using a (preferably UBI) image, that can be successfully sneaked through the OEM web updater :thinking:

I had a similar issue in the ZyXEL WSM20 that I solved the following way - I don't know if it can be applied to your device as I didn't check your header format.

The WSM20 has an image header that contains the size of the image and the checksum. For web-based flashing, an initramfs image is used where the checksum and size fields match the whole image. As it's initramfs, it is not going to change. After booting successfully into OpenWrt, sysupgrade needs to be run to get persistence. The header of the sysupgrade image contains the length and checksum of the kernel only and does not cover the UBI partition at all. The ZyXEL bootloader is happy with this approach and considers it a valid image, sysuprade doesn't complain either as it doesn't understand the header format anyway.

The structure of the flash is similar to yours, i.e. kernel in a regular MTD partition and rootfs + rootfs_data in UBI. There is a backup partition which is created equally.

Interesting, this reminds me of the DAP-2xxx devices, where the wrgg header is only covering the kernel as well, so it never changes. I think there was also a modification in mtd (fixwrgg) to automatically update checksums after flashing.
A dual flashing approach would make it much simpler of course (e.g. the DAP-X1860 only uses initramfs as OEM image, with proprietary ways of storing the config persistently), though I would prefer (as long as it is feasible) trying to aim for a single-step flashing procedure.

I think we also need to further analyze what exactly the bootloader verifies before it states RootFS not found, I will try to have a look at the GPL source and see if the bootloader sources are included (or maybe this is even part of the standard u-boot behavior, and not a modification by Mediatek?)

As long as you can flash from the web interface, the inconvenience is IMHO negligible. There are quite a lot of new devices that require a two-step flashing approach, for very similar reasons.

1 Like

Turns out at least the Mediatek bootloader source is available in the GPL tarball, the message RootFS not found arises from either find_rootfs or find_rootfs_ram in:
COVR-X1860_A1_V1.01B05_GPLCode_20211119/COVR-X1860_GPL_Release/MTK7621_AX1800_BASE/bootloader/Uboot-mips/board/ralink/common/dual_image.c

This is called by verify_image_fit and basically just looks for the squashfs magic, checks whether the size given in the header does not exceed the partition size, and (later) checks the last byte of rootfs is not 0xff to ensure it was written completely by uboot after erasing.

So I guess a fake squashfs with just the header and size 0 might even work :slightly_smiling_face:
We just need to make sure OpenWrt will somehow know which is the correct one :innocent:

Wow, this really seemed too simple :innocent:

Using | append-squashfs4-fakeroot at the end of the kernel will make Mediatek uboot stop complaining about the missing filesystem; since we have the kernel padded to 8MiB anyways, this does not make a difference or cause any trouble to OpenWrt. It will correctly attach UBI and configuration is persistent by now.

Only thing remaining now is the dual image...
We'd have to check just how much of the partition is actually moved when restoring the backup image from the main image, i.e. if this is triggered by some image header size fields, so we can just have a duplicate kernel and use the same UBI for both (with a gap in the middle, thus requiring mtd-concat), or if the full second partition is unusable, as to ensure it would never be overwritten during reboot.

1 Like

That sounds great! I am also constantly surprised by the vast amount of commands already available in the image generation system.

That definitely sounds familiar, I only wish there were some sort of documentation (i.e. an overview) of all the known formats, since there is no real chance of actively searching for the corresponding build recipe while looking at a binary factory image in the hex editor :innocent:

Regarding the duplicate image for the backup partition, did you use a full copy of the actual kernel image, or some sort of mock image similar to the fake squashfs?

The ZyXEL doesn't duplicate on its own. If flash from OEM ends up in image 1, you have a stock backup in image 2. If it ends up in image 2, the initramfs version remains in image 2. There is no known way to check from the stock firmware which image will be flashed next.

When adding support, I tried to come up with an automated backup system so that stock would be kept, but it had problems with bad block handling so I abandoned this.

Hi,
I tried openwrt on my devices. Flashing was possible with recovery and factory image.
I cannot see wifi settings in Luci. Is wifi not yet supported for this devices or did I miss something during building the images?
Thanks
Roland

Wifi is working for me on both radios, with flashing either via Recovery or Factory (we also have a couple of these devices running on gluon / Freifunk firmware here in Hannover without issues).

Maybe it's some issue with the state of the build tree, do images for other devices (e.g. DAP-X1860) work if you build them from your current tree?

Wirless settings are visiible on DAP-X1860 using the same branch (https://github.com/RolandoMagico/openwrt, master). One difference I saw: COVR-X1860 uses kmod-mt7915e, DAP-X1860 uses kmod-mt7915-firmware.
I built the firmware again for COVR-X1860 with enabled kmod-mt7915-firmware and the wireless settings are present now. Wireless scanning is working as well.

I think there was a refactoring in the mt7915 package dependencies some time ago; initially only the firmware package was included which would then include the driver as a dependency, now the driver is included which pulls the firmware as a dependency (or vice versa).

// edit: indeed, the new default is kmod-mt7915-firmware, must have forgotten that during rebase to main. So it was my dirty build tree that still had it working :innocent:

Just pushed another commit, now reducing the firmware partition size. This keeps the configuration safe from being overwritten by the bootloader, but wasting lots of precious flash space, only ~20MiB remaining for packages.
Sysupgrade also worked and persists configuration, but it probably needs to be tested with an actually different image (kernel and rootfs) to see what the bootloader will think about it, when main and backup images do not look identical.

Digging further through the code of the Mediatek-modified uboot from the GPL tarball, it is clear that there is no "active" boot partition here that could be toggled, instead uboot would only ever boot the kernel from the first partition (main). Before booting, the main and backup partitions are compared (hashes of kernel and rootfs), and, if both partitions look valid but have different hashes, the backup one will be overwritten with the data from the main partition (usually after an upgrade of OEM software to a newer version), then a reboot is performed, expecting both hashes to be identical upon re-checking, thus booting from the main partition. So the backup would only ever be used if the main partition fails the hash verification upon boot.

When it comes to using the flash space of the backup partition, we can split it into a kernel with fake-rootfs, all padded to 8 MiB, and the remaining space for fwconcat1, that would make a ubi partition with the ubi partition from the main image flash space, using mtd-concat .

Thus, when flashing OpenWrt via recovery, both partitions will be erased, OpenWrt flashed to the main flash area, upon reboot the image will be duplicated to the backup, making them identical after another reboot, so the image gets booted from the main partition without restoring anything.

When flashing OpenWrt via OEM web updater, only the main partition will be written, but (thanks to fakeroot) be considered valid, so it will be copied to overwrite backup as well.

Now the only thing to consider is, what happens during a sysupgrade?
When the kernel (and fakeroot, though the latter should be constant) differs, the new kernel would be duplicated to the backup partition, however we need to make sure it does not overwrite the UBI part of it.

Looking at the code in dual_image.c, there is function copy_image (line 651) called from within dual_image_check (line 820). It really seems like it would indeed evaluate the uimage and rootfs headers and the jffs2 end of filesystem marker to determine the partition size to be erased, rather than erasing the full destination partition:

	image_size = iif->kernel_size + iif->padding_size + iif->rootfs_size +
		iif->marker_size;
	ret = part_erase_range(&iit->part, 0, image_size);

This would be promising, as we could just have an 8 MiB backup kernel partition, with appended UBI data that would not be affected by a sysupgrade (i.e. the only possible worst case scenario that I could see now is that it still wipes the full partition size, and we'd somehow need to modify sysupgrade to perform the duplication explicitly after flashing, to ensure both partitions be identical when the bootloader checks them).

So this will require some further practical experimentation :slightly_smiling_face:

You can force sysupgrade to write to both partitions by adding support for it to the script in target/linux/ramips/mt7621/base-files/lib/upgrade/platform.sh.

1 Like

Thanks, so it would really be that simple if this should become required :blush:

Chances are good though this might not even be needed - pushed another commit, now splitting backup into an 8 MiB kernel2 and an fwconcat1 partition that will be merged with the first ubi partition (similar to DAP-X1860).
Configuration is persistent, flashing sysupgrade with a modified kernel will have the new kernel duplicated to kernel2, but no UBI warnings etc.; while it should see a little further testing (e.g. what happens when kernel size grows very close to 8 MiB, i.e. make sure erase size remains within the partition), after all this really seems to be quite robust :slightly_smiling_face:

Next step, implementing factory encryption in C, I might just dig out the code from my old PR then... :innocent:

1 Like

Just a quick update on the current state to prevent this thread from being closed after 10 days:

Forked the firmware-utils repo locally to cherry-pick the encryption tool from PR #4174.
Compilation works but with tons of deprecation warnings from OpenSSL 3.0, regarding the use of low-level APIs. So, before implementing the new key derivation functions for this device and eventually sending a patch, the parts using the crypto primitives should be rewritten to use the EVP functions.
I remember being aware of those during the initial implementation back then, but found it way too complex to get started and successfully use them, so I had sticked with the same APIs as the original SGE tool used.

Fortunately, there is a migration guide in the OpenSSL 3.0 docs that gives a basic overview of the changes and new concepts (i.e. fetching algorithms from providers), yet this will take some time to apply and test.

After all, it feels like the implementation would probably follow a rather ironic motivation here, somehow reminding me of highly sophisticated Java projects like FizzBuzz Enterprise Edition.

This thread won't auto-close after ten days, until one of the posts is marked as a solution or a moderator sets a timer/ closes it explicitly (which may happen once this device support gets merged into master) - at this point neither of which has happened.

2 Likes

Thanks for clarification!
I just noted recently that the GPL release containing the RSA private key for the DAP-X1860 repeater was published by D-Link, allowing to encrypt factory images for that device now as well.
I'm not sure whether opening another thread for this topic would be useful, as few people who were active on the previous DAP-X1860 thread might find it, however I have documented it in the wiki.

Regarding COVR-X1860, the current progress is: I spent most of yesterday calculating three SHA512 hashes with the new OpenSSL EVP API now, instead of the legacy one :joy:
After all it seems quite straightforward though, just takes a little bit of time for sifting through various revisions of what seems to be mostly automatically-generated documentation to get an estimate of which might happen to be the currently least deprecated way of doing things... :innocent:

SHA512 and AES (vendor key derivation and encryption/decryption) are done, RSA still to go.
Then it will be in the same state as two years ago just with the new API, so the code for new devices can finally be introduced (i.e. adding further keys and a parameter for selection of device model)

1 Like