wutil with libifconfig and libwpa_client

In the beginning

During the GSoC proposal period, I created wutil, a WiFi and network management utility, along the lines of GhostBSD’s NetworkMgr. NetworkMgr is a Python GTK3 network manager for FreeBSD and derived OSs, very similar in functionality to NetworkManager applet on linux. wutil was very much like a CLI form of NetworkMgr. You could list/enable/disable interfaces, scan and connect/disconnect from WiFi networks, and also configure IP/netmask, gateway, dns servers, and search domain very much like NetworkMgr.

Though wutil is in C, I initially followed NetworkMgr‘s way of directly calling ifconfig(8), wpa_supplicant(8), service(8) (to start/stop/restart services like dhclient(8), netif, routing), etc with system(3) (system(3) is not secure and is bad practice (╥﹏╥)), and also manually editing wpa_supplicant.conf(5) for network configuration, rc.conf(5) with sysrc(8) calls for IP/netmask/gateway, and resolv.conf for DNS nameservers. This version of wutil is available in v0.0.1 tag.

So what’s different now?

I went on a spree to get rid of all the system(3) calls to external programs and implement all the beforementioned functionality in just C with libraries like libifconfig, lib80211 and libwpa_client ( ◡̀_◡́)ᕤ.

net/libifconfig port

I made a port for the internal library libifconfig, a FreeBSD internal library that provides programmatic access to the functionality offered by ifconfig(8). I used net/libpfctl port as a reference, which is also an internal library. Currently, libifconfig can’t be made public, as its interface isn’t stable and making it public would require guaranteeing a stable ABI. I plan to link libifconfig from this port, when creating a port for wutil.

Retrieving Network Interface Details

I moved to using libifconfig‘s helpers ifconfig_foreach_iface(...), ifconfig_foreach_ifaddr(...), ifconfig_media_get_mediareq(...), ifconfig_media_get_status(...) to retrieve interface details. Before, I used to popen(3) on ifconfig(8) to get these information (╥﹏╥). ifconfig_foreach_ifaddr(...) and ifconfig_foreach_ifaddr(...) are actually just a wrapper around getifaddrs(3). Not really a fan of ifconfig_foreach_* callback functions though (ᵕ—ᴗ—).

Enabling/Disabling Network Interfaces.

NetworkMgr and even wutil used to directly call ifconfig IFACE [up|down] to toggle interface state. I tried to use rtnetlink(4) with RTM_GETLINK and RTM_NEWLINK to get and flip the IFF_UP flag of the interface, but the netlink message would succeed and nothing would change in the interface (it even froze my PC when I tried to send a message with ifi_change set (╥﹏╥)). So, for now I went with SIOCGIFFLAGS and SIOCSIFFLAGS ioctls to get and set the IFF_UP flag. This is the only network related part that uses ioctl currently. I gotta figure out how to properly do it with rtnetlink(4) and extract this as a helper in libifconfig.

Scanning for WiFi networks

I used to directly scan and parse results from ifconfig IFACE scan. I briefly switched to lib80211, a helper library for IEEE 802.11 related ioctls (net80211(4)), to perform scan with IEEE80211_IOC_SCAN_REQ and retrieve the results with IEEE80211_IOC_SCAN_RESULTS. Parsing struct ieee80211req_scan_result and other scan result fields was very fun but parsing IEs (Information Elements) to get the security related information was very painful (ᵕ—ᴗ—). So, I dropped lib80211 for libwpa_client‘s SCAN and SCAN_RESULTS commands, which also give the security and network capabilities. I manually wait for the WPA_EVENT_SCAN_RESULTS event on the wpa_supplicant(8)s ctrl_interface socket with a timeout for the scan to complete with kqueue(2).

Connecting/Disconnecting from WiFi networks

I fully switched to libwpa_client to connect/disconnect and configure key_mgmt on WiFi networks. Before I used to manually edit the wpa_supplicant.conf but now I use SAVE_CONFIG command (with update_config=1) on wpa_supplicant‘s ctrl_interface, which makes wpa_supplicant update the config nicely on its own. The user needs to be in the wheel group by default to use ctrl_interface commands.

Configuring Network Interfaces

I switched to using rtnetlink(4)‘s RTM_NEWADDR to configure IP/netmask and RTM_NEWROUTE for changing the default gateway. It’s very easy to create and send netlink messages with snl(3). Though sys/netlink/route/common.h says “not supported” for RTM_NEWADDR, it works fine (˶˃ ᵕ ˂˶). I gotta move these too as helper into libifconfig. I’m considering removing network configurations altogether from wutil as it’s already done by ifconfig(8) (configuring IP/netmask, MAC etc), route(8) (setting gateway), and resolv.conf (configuring DNS servers) in order for wutil to only handle WiFi related functions similar to iwctl (´-﹏-`;).

Switching to bsdmake

I was also using xmake-io before, my favorite C/C++ build tool, for wutil. Cuz it’s very nice to generate compile_commands.json for LSPs (with all cool warning flags -Wall -Wextra -Wpedantic -Wshadow -Wunused -g and sanitizers (˵ ¬ᴗ¬˵)), managing libraries, and compiling multiple binaries, and can also generate GNUMakefile etc with xmake. But after digging through freebsd-src/, I found bsd.prog.mk, which base system binaries use and there’s also bsd.progs.mk when generating multiple binaries. I completely switched to bsd.progs.mk and used bear(1) to generate compile_commands.json with WARNS=4 and CFLAGS/LDFLAGS to set include paths and sanitizers.

I also got rid of all the string utility functions I had for parsing outputs of shell commands. Even removed lots of code that became redundant with switch from shell calling other programs.

What’s next?

wutil currently has build dependency on libwpa_client library which has to be made available in security/wpa_supplicant port. I have a modified port of security/wpa_supplicant that builds libwpa_client, but it doesn’t have a SONAME which causes linking issues when porting wutil. The wutil CLI is kinda cluttery and could be made much cleaner. I plan to add commands to list and forget known WiFi networks too. Also, gotta extract the network interface related helpers into libifconfig. I’ll start working on the uncooked TUI in the upcoming weeks (˶ᵔ ᵕ ᵔ˶).