master ohmu/fastfetch-openbsd / src / detection / wifi / wifi_bsd.c
ohmu  ·  2026-10-15
  1#include "wifi.h"
  2#include "common/debug.h"
  3#include "common/io.h"
  4#include "common/strutil.h"
  5
  6#include <sys/ioctl.h>
  7#include <sys/socket.h>
  8#include <net/if.h>
  9#include <net/if_media.h>
 10#include <net80211/ieee80211_ioctl.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        if (!ffStrStartsWith(i->if_name, "wlan")) {
 28            continue;
 29        }
 30
 31        FFWifiResult* item = FF_LIST_ADD(FFWifiResult, *result);
 32        ffStrbufInitS(&item->inf.description, i->if_name);
 33        ffStrbufInit(&item->inf.status);
 34        ffStrbufInit(&item->conn.status);
 35        ffStrbufInit(&item->conn.ssid);
 36        ffStrbufInit(&item->conn.bssid);
 37        ffStrbufInit(&item->conn.protocol);
 38        ffStrbufInit(&item->conn.security);
 39        item->conn.signalQuality = -DBL_MAX;
 40        item->conn.rxRate = -DBL_MAX;
 41        item->conn.txRate = -DBL_MAX;
 42        item->conn.channel = 0;
 43        item->conn.channelWidth = 0;
 44        item->conn.frequency = 0;
 45
 46        char ssid[IEEE80211_NWID_LEN + 1] = {};
 47        struct ieee80211req ireq = {};
 48        strlcpy(ireq.i_name, i->if_name, sizeof(ireq.i_name));
 49        ireq.i_type = IEEE80211_IOC_SSID;
 50        ireq.i_data = ssid;
 51        ireq.i_len = sizeof(ssid) - 1;
 52
 53        if (ioctl(sock, SIOCG80211, &ireq) < 0 || ireq.i_len == 0) {
 54            struct ifreq ifr;
 55            strlcpy(ifr.ifr_name, i->if_name, sizeof(ifr.ifr_name));
 56            if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) {
 57                ffStrbufSetStatic(&item->inf.status, "Unknown");
 58            } else {
 59                ffStrbufSetStatic(&item->inf.status, ifr.ifr_flags & IFF_UP ? "Up" : "Down");
 60            }
 61            ffStrbufAppendS(&item->conn.status, "Not associated");
 62            continue;
 63        }
 64
 65        ffStrbufSetStatic(&item->inf.status, "Up");
 66        ffStrbufSetStatic(&item->conn.status, "Associated");
 67        ffStrbufAppendNS(&item->conn.ssid, ireq.i_len, ssid);
 68
 69        uint8_t bssid[IEEE80211_ADDR_LEN] = {};
 70        ireq.i_type = IEEE80211_IOC_BSSID;
 71        ireq.i_data = bssid;
 72        ireq.i_len = sizeof(bssid);
 73
 74        if (ioctl(sock, SIOCG80211, &ireq) >= 0) {
 75            ffStrbufSetF(&item->conn.bssid, "%02X:%02X:%02X:%02X:%02X:%02X", bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
 76        }
 77
 78        struct ieee80211_channel curchan = {};
 79        ireq.i_type = IEEE80211_IOC_CURCHAN;
 80        ireq.i_data = &curchan;
 81        ireq.i_len = sizeof(curchan);
 82
 83        if (ioctl(sock, SIOCG80211, &ireq) >= 0) {
 84            item->conn.channel = curchan.ic_ieee;
 85            item->conn.frequency = curchan.ic_freq;
 86
 87            // Determine channel width from channel flags
 88            if (curchan.ic_flags & IEEE80211_CHAN_VHT160)
 89                item->conn.channelWidth = 160;
 90            else if (curchan.ic_flags & IEEE80211_CHAN_VHT80)
 91                item->conn.channelWidth = 80;
 92            else if (curchan.ic_flags & IEEE80211_CHAN_HT40)
 93                item->conn.channelWidth = 40;
 94            else
 95                item->conn.channelWidth = 20;
 96
 97#ifdef IEEE80211_IS_CHAN_HE // for future use
 98            if (IEEE80211_IS_CHAN_HE(&curchan)) {
 99                ffStrbufSetStatic(&item->conn.protocol, "802.11ax (Wi-Fi 6)");
100            } else
101#endif
102                if (IEEE80211_IS_CHAN_VHT(&curchan))
103                ffStrbufSetStatic(&item->conn.protocol, "802.11ac (Wi-Fi 5)");
104            else if (IEEE80211_IS_CHAN_HT(&curchan)) {
105                ffStrbufSetStatic(&item->conn.protocol, "802.11n (Wi-Fi 4)");
106            } else if (IEEE80211_IS_CHAN_ANYG(&curchan)) {
107                ffStrbufSetStatic(&item->conn.protocol, "802.11g");
108            } else if (IEEE80211_IS_CHAN_B(&curchan)) {
109                ffStrbufSetStatic(&item->conn.protocol, "802.11b");
110            } else if (IEEE80211_IS_CHAN_A(&curchan)) {
111                ffStrbufSetStatic(&item->conn.protocol, "802.11a");
112            } else if (IEEE80211_IS_CHAN_FHSS(&curchan)) {
113                ffStrbufSetStatic(&item->conn.protocol, "802.11 (FHSS)");
114            }
115        }
116
117        union {
118            struct ieee80211req_sta_req req;
119            uint8_t buf[1024];
120        } stareq = {};
121        memcpy(stareq.req.is_u.macaddr, bssid, sizeof(bssid));
122        ireq.i_type = IEEE80211_IOC_STA_INFO;
123        ireq.i_data = &stareq;
124        ireq.i_len = sizeof(stareq);
125
126        if (ioctl(sock, SIOCG80211, &ireq) >= 0) {
127            struct ieee80211req_sta_info* sta = stareq.req.info;
128            if (sta->isi_len != 0) {
129                item->conn.signalQuality = (sta->isi_rssi >= -50 ? 100 : sta->isi_rssi <= -100 ? 0
130                                                                                               : (sta->isi_rssi + 100) * 2);
131                item->conn.rxRate = sta->isi_txmbps * 0.5;
132            }
133        }
134
135        ireq.i_type = IEEE80211_IOC_AUTHMODE;
136        ireq.i_data = nullptr;
137        ireq.i_len = 0;
138        if (ioctl(sock, SIOCG80211, &ireq) >= 0) {
139            switch (ireq.i_val) {
140                case IEEE80211_AUTH_NONE:
141                    ffStrbufSetStatic(&item->conn.security, "Insecure");
142                    break;
143                case IEEE80211_AUTH_OPEN:
144                    ffStrbufSetStatic(&item->conn.security, "Open");
145                    break;
146                case IEEE80211_AUTH_SHARED:
147                    ffStrbufSetStatic(&item->conn.security, "Shared");
148                    break;
149                case IEEE80211_AUTH_8021X:
150                    ffStrbufSetStatic(&item->conn.security, "802.1x");
151                    break;
152                case IEEE80211_AUTH_AUTO:
153                    ffStrbufSetStatic(&item->conn.security, "Auto");
154                    break;
155                case IEEE80211_AUTH_WPA: {
156                    ireq.i_type = IEEE80211_IOC_WPA;
157                    if (ioctl(sock, SIOCG80211, &ireq) >= 0) {
158                        switch (ireq.i_val) {
159                            case 2: ffStrbufSetStatic(&item->conn.security, "WPA2"); break;
160                            case 3: ffStrbufSetStatic(&item->conn.security, "WPA1+2"); break;
161                            default: ffStrbufSetStatic(&item->conn.security, "WPA"); break;
162                        }
163                    } else {
164                        ffStrbufSetStatic(&item->conn.security, "WPA");
165                    }
166                    break;
167                }
168                default:
169                    ffStrbufSetF(&item->conn.security, "Unknown (%d)", ireq.i_val);
170                    break;
171            }
172        }
173    }
174
175    if_freenameindex(infs);
176    return nullptr;
177}