[NOTE] Reverse engineering UCI and wifi

The wifi tools on most nix platforms are deficient. Sadly, this is the canonical tune known as *standards by (industry) committee(SBC). They adds to this because quite often collapsing lists** are used with these types of configurations. I could rant for pages on this, but let's get to the issue.

What I am going to document should help remove and transform scripts that at this point seem to serve little or no purpose on the Linux side of the Yun.

There are two (2) utilities we need to deal with

  • /sbin/uci - the wrapper to the libuci.so to maintain UCI configuration files
  • /sbin/wifi - an Almquist shell script intend to help with wifi deployment

In short, uci is a great idea, but it was convoluted and corrupted when it was wrapped with a shell (/bin/ash) script. This was done with a series of utility library shell scripts intended to help, but in fact they created obfuscation.

The scripts are:

  • /lib/functions.sh
  • /lib/config/uci.sh
  • /lib/wifi/hostapd.sh
  • /lib/wifi/mac80211.sh
  • /lib/wifi/wpa_supplicant.sh

The first script functions.sh maintains some of the UCI system (including load parameters into memory), but also adds users/groups, and a few more system tasks. This is part of the mess. This script also loads uci.sh, which deal exclusively with UCI and is infact a wrapper for many UCI calls.

The remaining three libraries deal with their respective configuration files and programs.


As I was walking through /sbin/wifi I came upon wifi_fixup_hwmode(). Just then it dawn on me that all I needed to do was an environment dump; becuase all these scripts do is load the parameters (stored on the SD) into memory.

While this technique is not a complete fix, it gives enough information so that some of the code bloat can be removed.

So I wrote a script(See below this). Here are the results after a small bit of filtering.

root@Arduino:~/wifi_tests# ./test_wifi_fixup_hwmode | sort | grep CONFIG
CONFIG_LIST_STATE=radio0_ht_capab
CONFIG_NUM_SECTIONS=2
CONFIG_SECTION=cfg033579
CONFIG_SECTIONS=radio0 cfg033579
CONFIG_cfg033579_TYPE=wifi-iface
CONFIG_cfg033579_device=radio0
CONFIG_cfg033579_encryption=psk2
CONFIG_cfg033579_ifname=wlan0
CONFIG_cfg033579_key=abcdefABCDEF
CONFIG_cfg033579_mode=sta
CONFIG_cfg033579_network=lan
CONFIG_cfg033579_ssid=ATT4FWBkmS
CONFIG_cfg033579_up=1
CONFIG_radio0_TYPE=wifi-device
CONFIG_radio0_channel=auto
CONFIG_radio0_country=MX
CONFIG_radio0_disabled=0
CONFIG_radio0_ht_capab=SHORT-GI-20 SHORT-GI-40 RX-STBC1 DSSS_CCK-40
CONFIG_radio0_ht_capab_ITEM1=SHORT-GI-20
CONFIG_radio0_ht_capab_ITEM2=SHORT-GI-40
CONFIG_radio0_ht_capab_ITEM3=RX-STBC1
CONFIG_radio0_ht_capab_ITEM4=DSSS_CCK-40
CONFIG_radio0_ht_capab_LENGTH=4
CONFIG_radio0_htmode=HT20
CONFIG_radio0_hwmode=11ng
CONFIG_radio0_path=platform/ar933x_wmac
CONFIG_radio0_type=mac80211
CONFIG_radio0_vifs=cfg033579

On the code below, there are five (5) important parts:

  1. . /lib/functions.sh - this line loads a set of standard functions, including uci.sh
  2. include /lib/wifi - this line loads: hostapd.sh, mac80211.sh, & wpa_supplicant.sh
  3. the inners of scan_wifi() - This explained below the code
  4. scan_wifi - this line runs the function scan_wifi()
  5. env - this dumps the environment variables; it produces what is above. Note: fixed the order at 6:47pm, el paso texas time
#!/bin/sh
#

. /lib/functions.sh

include /lib/wifi

scan_wifi() {
 local cfgfile="$1"
 DEVICES=
 config_cb() {
 local type="$1"
 local section="$2"

 # section start
 case "$type" in
 wifi-device)
 append DEVICES "$section"
 config_set "$section" vifs ""
 config_set "$section" ht_capab ""
 ;;
 esac

 # section end
 config_get TYPE "$CONFIG_SECTION" TYPE
 case "$TYPE" in
 wifi-iface)
 config_get device "$CONFIG_SECTION" device
 config_get vifs "$device" vifs 
 append vifs "$CONFIG_SECTION"
 config_set "$device" vifs "$vifs"
 ;;
 esac
 }
 config_load "${cfgfile:-wireless}"
}

scan_wifi
env

# The items commented out test the internals of wifi_fixup_hwmode()
# in /sbin/wifi just before the *case* statement.
#echo device:$device
#config_get channel "$device" channel
#config_get hwmode "$device" hwmode
#echo $channel
#echo $hwmode

= scan_wifi() =

The reasons for scan_wifi() does not seem obvious, so this will take some explaining. But, again, all it does is load the UCI parameters into memory -- as shell script variables.

1) At the end of this function is config_load (which is loaded from /lib/functions.sh).

2) In turn, config_load calls uci_load() (which is loaded /lib/config/uci.sh, which loaded at the bottom of /lib/functions.sh)

3) uci_load(), near the end of it's function calls the config_cb() which is the config_cb() just above in scan_wifi().

This diagram delineates the flow

scan_wifi()->config_load()->uci_load()->config_cb()

While this may seem like something clever to a few, this is a point of obfuscation. This code construction also creates a code chain spread across three (3) files. This is known as Content coupling, becuase if you change the output of one it affects the entire chain. That may have been the intent, but it makes debugging the script extremely difficult.

Futher, the script not only handles data in an obsure manner, but it also handles the functions by overwriting the functions. This means, while you see the function above written as it is, you will see the same function (name) written entirely different in another script.

In short, not only is the function chain highly depended on the order and use, but the middle function config_cb() will change depending on where it used. This is a side effect of using this method when faced with configurations that use collapsing lists**.

Jesse

** Expert Systems User Interfaces, Oct 2001 (See: Mechanisms for User Interfaces).