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}