Edit wireless regulatory db a.k.a regulatory.bin

Category: Gentoo Tags: wireless regulatory db, crda

Howto adjust your wireless regdb a.k.a regulatory.bin used by crda which defines the radio spectrum your wlan hardware is allowed to use.

Why to change the wireless regulatory db anyway ?

The wireless regulatory db defines the radio spectrum which is allowed in certain countries/region. See http://git.kernel.org/cgit/linux/kernel/git/sforshee/wireless-regdb.git/tree/db.txt for a vanilla version. When you configure hostapd you set a country code. Based on this code the allowed radio spectrum (channels, power, etc.) is pulled of the regulatory db. Normally there is no need to change the db as it reflects the allowed settings in your country.

Recently I bought new wifi hardware (a 2.4 GHz card and a 5GHz card, both with Atheros chips). These cards have a country code set in their eeprom which is used as default. In my case it is "US". Well nothing to worry about I thought, as I did set "DE" in hostapd which is the country I live in.
But rather than getting the allowed "DE" radio spectrum, I get an intersection of "US" and "DE" spectrum. So I neither have the higher powers allowed in US nor channel 12 and 13 allowed in DE. Thats bad especially as I used channel 13 for 2.4 GHz and it gave the best wifi performance paired with HT40-.

I thought of 3 ways to change that. First off I could change the country code hard coded in the eeproms. This would be the best but I don't know how to do that :(.
Second, I can change to kernel configuration to use a compiled in wireless regulatory db and patch the db so US spectrum would be equal to DE spectrum. I think this would be the easiest solution.
Third, patch the regulary db used by crda. The db is compiled as binary and signed by its maintainers, so to patch it, one has to generate it's own key, patch the db, compile and sign the db and finally tell crda thats a valid signed db. Not that easy but I like it more then tinkering with the kernel, so I chose to give it it a try.


The patches

Extract the wireless-regdb sources. Adjust the db.txt to suit your needs and create a patch. I strongly advice you not to violate the rules of your country. You will act against the law if you do so!
Put the patch in /etc/portage/patches/net-wireless/wireless-regdb/.

To build the db binary from source we use a python script shipped with wireless-regdb. This script does not run with recent versions of python somehow, so we need to force to use python 2.7 which results in a second patch.

diff -ru wireless-regdb-2016.06.10.orig/db2bin.py wireless-regdb-2016.06.10/db2bin.py
--- wireless-regdb-2016.06.10.orig/db2bin.py    2016-06-10 15:02:34.000000000 +0200
+++ wireless-regdb-2016.06.10/db2bin.py 2016-10-14 20:55:49.015088473 +0200
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python2.7

 from cStringIO import StringIO
 import struct

Put it into /etc/portage/patches/net-wireless/wireless-regdb/ too.


The hooks

The wireless-regdb ebuild does not use the makefile to build and install regdb.
It simply installs the precompiled and signed regdb binary along with the public keys from its maintainers.
To change that one need to hook into the emerge process.
Simply create the file /etc/portage/env/net-wireless/wireless-regdb. It will be sourced in the emerge process and gives you that chance to define hooks.

#!/usr/bin/env bash
# /etc/portage/env/net-wireless/wireless-regdb

REGDB_PRIVKEY=".wireless-regdb-Gentoo.key.priv.pem"
REGDB_PUBKEY="Gentoo.key.pub.pem"

post_src_prepare() {
    if [[ ${EAPI:-0} == [012345] ]]; then
        if ! type epatch_user > /dev/null 2>&1; then
            local names="EPATCH_USER_SOURCE epatch_user epatch evar_push evar_push_set evar_pop estack_push estack_pop"
            source <(awk "/^# @(FUNCTION|VARIABLE): / { p = 0 } /^# @(FUNCTION|VARIABLE): (${names// /|})\$/ { p = 1 } p { print }" ${PORTDIR}/eclass/eutils.eclass)
        fi

        epatch_user

        for name in $names; do
            unset $name
        done
    fi

    if [ -f /etc/wireless-regdb/$REGDB_PRIVKEY ]; then
        cp /etc/wireless-regdb/$REGDB_PRIVKEY ~/$REGDB_PRIVKEY
    fi
}

post_src_compile() {
    sha1sum -c --status sha1sum.txt
    if [ $? -eq 1 ]; then
        if [ -f ~/$REGBDB_PRIVKEY ]; then
            einfo "Using private host key for signing"
        else
            einfo "Generate private host key for signing"
            make install-distro-key
        fi
        einfo "Recompile regulatory.bin to reflect changes in db.txt"
        make regulatory.bin
    fi
}

post_src_install() {
    if [ -f $REGDB_PUBKEY ]; then
        einfo "Install public key for signed regulatory.bin"
        insinto /etc/wireless-regdb/pubkeys
        doins $REGDB_PUBKEY
    fi

    if [ -f ~/$REGDB_PRIVKEY ]; then
        einfo "Install private host key"
        insinto /etc/wireless-regdb
        doins ~/$REGDB_PRIVKEY
    fi
}

First the ebuild does not incorporate the epatch_user function, so patches will not be applied. There is guide at https://wiki.gentoo.org/wiki//etc/portage/patches on howto inject the function and call it to apply user patches. I put this into the post_src_prepare hook.
Also the private signing key, if it exists, will be copied into location, where the makefile will look for it.

The ebuild doesn't acually compile anything, we change that with the post_src_compile hook.
First there is a check if db.txt actually has changed, otherwise there is no need to build the regdb.
Second there is a check for the private signing key. Should be copied in post_src_prepare if there is already one installed, otherwise the private signing key will be created.
Finally the regdb will be compiled and signed. This step will also create the public signing key.

So regdb has been build and keys has been created. The regdb binary and the maintainers public key will be installed by the ebuild. We need to install our own privat and public key.
In post_src_install are simple checks and calls of install routines to do so.

One could think we install the custom the private and public key anyway ? Well we need the public key for crda to accept our self signed regdb and the private key to sign the regdb always with the same key.
If we don't, we would have to emerge crda every time there is a change in regdb.

Ok, we got our self signed regdb ready and in place.
But crda will not accept this db as it is does not know about our public signing key.
We will need to emerge crda again and include our public signing key.
It is done the same way as above by hooking into the emerge process.

#!/usr/bin/env bash
# /etc/portage/env/net-wireless/crda

REGDB_PUBKEY="Gentoo.key.pub.pem"

post_src_prepare() {
    if [ -f "/etc/wireless-regdb/pubkeys/$REGDB_PUBKEY" ]; then
        einfo "Allow regulatory.bin to be signed by local private key"
        cp "/etc/wireless-regdb/pubkeys/$REGDB_PUBKEY" pubkeys
    fi
}

We simply put our public signing key into the folder used for public keys when building crda. All regdbs signed with private keys that correspond to public keys in the public keys folder will be accepted by crda. These public key will be statically intergrated in crda.

Thats it. If you didn't do earlier emerge wireless-regdb and crda afterwards.

Now crda will accept the custom regdb binary and any future regdbs as long as they are signed with our custom key.

I'll be glad for any feedback.


02/26/2018


Changes as of January 2018

There has been some changes to the wireless-regdb.ebuild.
First the kernel wants to have an own version of regulatory.db in /lib/firmware.
Also the ebuild uses eapi 6 now, so there is no need to manually incorporate the patches.

This results in the following files:
/etc/portage/patches/net-wireless/wireless-regdb/python.patch

diff -ru wireless-regdb-2017.12.23.orig/db2bin.py wireless-regdb-2017.12.23/db2bin.py
--- wireless-regdb-2017.12.23.orig/db2bin.py    2017-12-24 04:39:42.000000000 +0100
+++ wireless-regdb-2017.12.23/db2bin.py 2018-01-30 07:56:47.675054350 +0100
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python2.7

 from cStringIO import StringIO
 import struct
diff -ru wireless-regdb-2017.12.23.orig/db2fw.py wireless-regdb-2017.12.23/db2fw.py
--- wireless-regdb-2017.12.23.orig/db2fw.py     2017-12-24 04:39:42.000000000 +0100
+++ wireless-regdb-2017.12.23/db2fw.py  2018-01-30 07:57:03.685356967 +0100
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python2.7

 from cStringIO import StringIO
 import struct

/etc/portage/env/net-wireless/wireless-regdb

#!/usr/bin/env bash

export REGDB_PRIVKEY=".wireless-regdb-gentoo.key.priv.pem"
export REGDB_PUBKEY="gentoo.key.pub.pem"
export REGDB_PUBCERT="gentoo.x509.pem"
DOINSTALL_DISTROKEY="install.distro.key"

post_src_prepare() {
    if [ -f "/etc/wireless-regdb/$DISTRO_PRIVKEY" ]; then
        einfo "Found private distro key"
        cp "/etc/wireless-regdb/$DISTRO_PRIVKEY" $REGDB_PRIVKEY
    fi
}

post_src_compile() {
    if [ -f $REGDB_PRIVKEY ]; then
        einfo "Using private host key for signing"
    else
        einfo "Generate private host key for signing"
        make $REGDB_PRIVKEY
        touch $DOINSTALL_DISTROKEY
    fi

    make sha1sum.txt
    make regulatory.bin
    make regulatory.db
    make regulatory.db.p7s
}

post_src_install() {
    if [ -f $REGDB_PUBKEY ]; then
        einfo "Install public key for signed regulatory.bin"
        insinto /etc/wireless-regdb/pubkeys
        doins $REGDB_PUBKEY
    fi

    if [ -f $DOINSTALL_DISTROKEY ]; then
        einfo "Install private distro key"
        insinto "/etc/wireless-regdb"
        doins $REGDB_PRIVKEY
    fi
}

So what has changed.
Python patch needs applied to 2 files, one to build the regdb.bin other to build the regdb firmware.

Ebuild hooks have been adjusted to build the regdb firmware if necessary.
Also variables have been exported cause there were issues, with build scripts not recognizing them.

Functionality kept them same.