master ohmu/fastfetch-openbsd / src / detection / wifi / wifi_obsd.c
ohmu  ·  2026-10-15
  1#include "wifi.h"
  2#include "common/debug.h"
  3#include "common/io.h"
  4
  5#include <sys/ioctl.h>
  6#include <sys/socket.h>
  7#include <net/if.h>
  8#include <net80211/ieee80211.h>
  9#include <net80211/ieee80211_ioctl.h>
 10#include <unistd.h>
 11
 12const char* ffDetectWifi(FFlist* result) {
 13    struct if_nameindex* infs = if_nameindex();
 14    if (!infs) {
 15        FF_DEBUG("if_nameindex() failed: %s", strerror(errno));
 16        return "if_nameindex() failed";
 17    }
 18
 19    FF_AUTO_CLOSE_FD int sock = socket(AF_INET, SOCK_DGRAM, 0);
 20    if (sock < 0) {
 21        FF_DEBUG("socket() failed: %s", strerror(errno));
 22        if_freenameindex(infs);
 23        return "socket() failed";
 24    }
 25
 26    for (struct if_nameindex* i = infs; !(i->if_index == 0 && i->if_name == nullptr); ++i) {
 27        struct ieee80211_nodereq nr = {};
 28        strlcpy(nr.nr_ifname, i->if_name, sizeof(nr.nr_ifname));
 29
 30        int err = 0;
 31        if (ioctl(sock, SIOCG80211NODE, &nr) < 0) {
 32            err = errno;
 33            if (err == ENXIO || err == ENODEV || err == EINVAL || err == ENOTTY) {
 34                continue; // Not a Wi-Fi interface
 35            }
 36        }
 37
 38        FFWifiResult* item = FF_LIST_ADD(FFWifiResult, *result);
 39        ffStrbufInitS(&item->inf.description, i->if_name);
 40        ffStrbufInit(&item->inf.status);
 41        ffStrbufInit(&item->conn.status);
 42        ffStrbufInit(&item->conn.ssid);
 43        ffStrbufInit(&item->conn.bssid);
 44        ffStrbufInit(&item->conn.protocol);
 45        ffStrbufInit(&item->conn.security);
 46        item->conn.signalQuality = -DBL_MAX;
 47        item->conn.rxRate = -DBL_MAX;
 48        item->conn.txRate = -DBL_MAX;
 49        item->conn.channel = 0;
 50        item->conn.channelWidth = 0; // not exposed by ieee80211_nodereq
 51        item->conn.frequency = 0;
 52
 53        struct ifreq ifr = {};
 54        strlcpy(ifr.ifr_name, i->if_name, sizeof(ifr.ifr_name));
 55        if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) {
 56            ffStrbufSetStatic(&item->inf.status, "Unknown");
 57        } else {
 58            ffStrbufSetStatic(&item->inf.status, ifr.ifr_flags & IFF_UP ? "Up" : "Down");
 59        }
 60
 61        if (err != 0 || nr.nr_nwid_len == 0) {
 62            ffStrbufSetStatic(&item->conn.status, "Not associated");
 63            continue;
 64        }
 65
 66        ffStrbufSetStatic(&item->conn.status, "Associated");
 67        ffStrbufAppendNS(&item->conn.ssid, nr.nr_nwid_len, (char*) nr.nr_nwid);
 68        ffStrbufSetF(&item->conn.bssid, "%02X:%02X:%02X:%02X:%02X:%02X", nr.nr_bssid[0], nr.nr_bssid[1], nr.nr_bssid[2], nr.nr_bssid[3], nr.nr_bssid[4], nr.nr_bssid[5]);
 69
 70        item->conn.channel = nr.nr_channel;
 71        if (item->conn.channel == 0) {
 72            struct ieee80211chanreq chreq = {};
 73            strlcpy(chreq.i_name, i->if_name, sizeof(chreq.i_name));
 74            if (ioctl(sock, SIOCG80211CHANNEL, &chreq) >= 0) {
 75                item->conn.channel = chreq.i_channel;
 76            }
 77        }
 78        item->conn.frequency = ffWifiChannelToFreq(item->conn.channel);
 79
 80        if (nr.nr_max_rssi) {
 81            item->conn.signalQuality = ((double) nr.nr_rssi / (double) nr.nr_max_rssi) * 100.0;
 82        }
 83
 84        // VHT implies HT, so test VHT first
 85        if (nr.nr_flags & IEEE80211_NODEREQ_VHT) {
 86            ffStrbufSetStatic(&item->conn.protocol, "802.11ac (Wi-Fi 5)");
 87        } else if (nr.nr_flags & IEEE80211_NODEREQ_HT) {
 88            ffStrbufSetStatic(&item->conn.protocol, "802.11n (Wi-Fi 4)");
 89        } else if (nr.nr_chan_flags & IEEE80211_CHANINFO_5GHZ) {
 90            ffStrbufSetStatic(&item->conn.protocol, "802.11a");
 91        } else if (nr.nr_chan_flags & IEEE80211_CHANINFO_2GHZ) {
 92            ffStrbufSetStatic(&item->conn.protocol, "802.11g");
 93        }
 94
 95        struct ieee80211_wpaparams wpa = {};
 96        strlcpy(wpa.i_name, i->if_name, sizeof(wpa.i_name));
 97
 98        if (ioctl(sock, SIOCG80211WPAPARMS, &wpa) >= 0 && wpa.i_enabled) {
 99            if (wpa.i_akms & IEEE80211_WPA_AKM_SAE) {
100                if (wpa.i_protos & IEEE80211_WPA_PROTO_WPA2) {
101                    ffStrbufSetStatic(&item->conn.security, "WPA2/WPA3");
102                } else {
103                    ffStrbufSetStatic(&item->conn.security, "WPA3");
104                }
105            } else if (wpa.i_protos & IEEE80211_WPA_PROTO_WPA2) {
106                ffStrbufSetStatic(&item->conn.security, "WPA2");
107            } else if (wpa.i_protos & IEEE80211_WPA_PROTO_WPA1) {
108                ffStrbufSetStatic(&item->conn.security, "WPA");
109            } else {
110                ffStrbufSetStatic(&item->conn.security, "Unknown");
111            }
112        } else {
113            struct ieee80211_nwkey nwkey = {};
114            strlcpy(nwkey.i_name, i->if_name, sizeof(nwkey.i_name));
115            if (ioctl(sock, SIOCG80211NWKEY, &nwkey) >= 0) {
116                if (nwkey.i_wepon) {
117                    ffStrbufSetStatic(&item->conn.security, "WEP");
118                } else {
119                    ffStrbufSetStatic(&item->conn.security, "Open");
120                }
121            }
122        }
123    }
124
125    if_freenameindex(infs);
126    return nullptr;
127}