ohmu
·
2026-10-15
1#include "wifi.h"
2#include "common/debug.h"
3#include "common/io.h"
4#include "common/mallocHelper.h"
5
6#include <dirent.h>
7#include <fcntl.h>
8#include <stropts.h>
9#include <sys/ioctl.h>
10#include <unistd.h>
11
12#include <inet/wifi_ioctl.h>
13
14static bool getWifiValue(int fd, uint32_t id, void* value, size_t valueSize, wldp_t* buffer) {
15 memset(buffer, 0, MAX_BUF_LEN);
16 buffer->wldp_type = NET_802_11;
17 buffer->wldp_id = id;
18
19 struct strioctl request = {
20 .ic_cmd = WLAN_GET_PARAM,
21 .ic_timout = 0,
22 .ic_len = MAX_BUF_LEN,
23 .ic_dp = (char*) buffer,
24 };
25
26 if (ioctl(fd, I_STR, &request) != 0 || buffer->wldp_result != WL_SUCCESS || buffer->wldp_length < WIFI_BUF_OFFSET + valueSize || buffer->wldp_length > MAX_BUF_LEN) {
27 return false;
28 }
29
30 memcpy(value, buffer->wldp_buf, valueSize);
31 return true;
32}
33
34static void setWifiProtocol(FFstrbuf* protocol, uint32_t subtype, bool htEnabled) {
35 switch (subtype) {
36 case WL_FHSS:
37 ffStrbufSetStatic(protocol, "802.11 (FHSS)");
38 break;
39 case WL_DSSS:
40 ffStrbufSetStatic(protocol, "802.11 (DSSS)");
41 break;
42 case WL_IRBASE:
43 ffStrbufSetStatic(protocol, "802.11 (IR)");
44 break;
45 case WL_OFDM:
46 ffStrbufSetStatic(protocol, htEnabled ? "802.11n (Wi-Fi 4)" : "802.11a");
47 break;
48 case WL_HRDS:
49 ffStrbufSetStatic(protocol, "802.11b");
50 break;
51 case WL_ERP:
52 ffStrbufSetStatic(protocol, htEnabled ? "802.11n (Wi-Fi 4)" : "802.11g");
53 break;
54 default:
55 break;
56 }
57}
58
59static void setWifiSecurity(FFstrbuf* security, uint32_t encryption, bool haveAuthMode, uint32_t authMode) {
60 switch (encryption) {
61 case WL_NOENCRYPTION:
62 ffStrbufSetStatic(security, "Insecure");
63 break;
64 case WL_ENC_WEP:
65 ffStrbufSetStatic(security, "WEP");
66 break;
67 case WL_ENC_WPA:
68 ffStrbufSetStatic(security, haveAuthMode && authMode == WL_SHAREDKEY ? "WPA Shared Key" : "WPA");
69 break;
70 default:
71 ffStrbufSetStatic(security, "Unknown");
72 break;
73 }
74}
75
76const char* ffDetectWifi(FFlist* result) {
77 // https://github.com/illumos/illumos-gate/blob/master/usr/src/cmd/cmd-inet/usr.sbin/wificonfig/wificonfig.c
78
79 FF_AUTO_CLOSE_DIR DIR* directory = opendir("/dev/wifi");
80 if (!directory) {
81 return nullptr;
82 }
83
84 FF_AUTO_FREE void* bufferMemory = malloc(MAX_BUF_LEN);
85 if (!bufferMemory) {
86 FF_DEBUG("malloc(MAX_BUF_LEN) failed");
87 return "malloc() failed";
88 }
89 wldp_t* buffer = (wldp_t*) bufferMemory;
90
91 struct dirent* entry;
92 while ((entry = readdir(directory)) != nullptr) {
93 size_t nameLength = strlen(entry->d_name);
94 if (nameLength == 0 || entry->d_name[nameLength - 1] < '0' || entry->d_name[nameLength - 1] > '9') {
95 continue;
96 }
97
98 char devicePath[PATH_MAX];
99 int pathLength = snprintf(devicePath, sizeof(devicePath), "/dev/wifi/%s", entry->d_name);
100 if (pathLength < 0 || (size_t) pathLength >= sizeof(devicePath)) {
101 continue;
102 }
103
104 FF_AUTO_CLOSE_FD int fd = open(devicePath, O_RDWR | O_CLOEXEC);
105 if (fd < 0) {
106 continue;
107 }
108 if (!isastream(fd)) {
109 continue;
110 }
111
112 FFWifiResult* item = FF_LIST_ADD(FFWifiResult, *result);
113 ffStrbufInitS(&item->inf.description, entry->d_name);
114 ffStrbufInit(&item->inf.status);
115 ffStrbufInit(&item->conn.status);
116 ffStrbufInit(&item->conn.ssid);
117 ffStrbufInit(&item->conn.bssid);
118 ffStrbufInit(&item->conn.protocol);
119 ffStrbufInit(&item->conn.security);
120 item->conn.signalQuality = -DBL_MAX;
121 item->conn.rxRate = -DBL_MAX;
122 item->conn.txRate = -DBL_MAX;
123 item->conn.channel = 0;
124 item->conn.channelWidth = 0;
125 item->conn.frequency = 0;
126
127 wl_radio_t radio;
128 if (getWifiValue(fd, WL_RADIO, &radio, sizeof(radio), buffer)) {
129 ffStrbufSetStatic(&item->inf.status, radio ? "Up" : "Down");
130 } else {
131 ffStrbufSetStatic(&item->inf.status, "Unknown");
132 }
133
134 wl_linkstatus_t linkStatus;
135 if (!getWifiValue(fd, WL_LINKSTATUS, &linkStatus, sizeof(linkStatus), buffer)) {
136 ffStrbufSetStatic(&item->conn.status, "Unknown");
137 continue;
138 }
139 if (linkStatus != WL_CONNECTED) {
140 ffStrbufSetStatic(&item->conn.status, "Not connected");
141 continue;
142 }
143 ffStrbufSetStatic(&item->conn.status, "Connected");
144
145 wl_essid_t essid;
146 if (getWifiValue(fd, WL_ESSID, &essid, sizeof(essid), buffer) && essid.wl_essid_length > 0 && essid.wl_essid_length < sizeof(essid.wl_essid_essid) && essid.wl_essid_length <= MAX_ESSID_LENGTH - 1) {
147 ffStrbufSetNS(&item->conn.ssid, essid.wl_essid_length, essid.wl_essid_essid);
148 }
149
150 wl_bssid_t bssid;
151 if (getWifiValue(fd, WL_BSSID, bssid, sizeof(bssid), buffer)) {
152 ffStrbufSetF(&item->conn.bssid, "%02X:%02X:%02X:%02X:%02X:%02X", (uint8_t) bssid[0], (uint8_t) bssid[1], (uint8_t) bssid[2], (uint8_t) bssid[3], (uint8_t) bssid[4], (uint8_t) bssid[5]);
153 }
154
155 wl_rssi_t rssi;
156 if (getWifiValue(fd, WL_RSSI, &rssi, sizeof(rssi), buffer)) {
157 item->conn.signalQuality = rssi >= MAX_RSSI ? 100.0 : ((double) rssi / MAX_RSSI) * 100.0;
158 }
159
160 wl_phy_conf_t phy;
161 if (getWifiValue(fd, WL_PHY_CONFIG, &phy, sizeof(phy), buffer)) {
162 uint32_t subtype = phy.wl_phy_dsss_conf.wl_dsss_subtype;
163 switch (subtype) {
164 case WL_FHSS:
165 item->conn.channel = (uint16_t) phy.wl_phy_fhss_conf.wl_fhss_channel;
166 setWifiProtocol(&item->conn.protocol, subtype, false);
167 break;
168 case WL_DSSS:
169 item->conn.channel = (uint16_t) phy.wl_phy_dsss_conf.wl_dsss_channel;
170 setWifiProtocol(&item->conn.protocol, subtype, false);
171 break;
172 case WL_OFDM:
173 item->conn.frequency = (uint16_t) phy.wl_phy_ofdm_conf.wl_ofdm_frequency;
174 item->conn.channel = ffWifiFreqToChannel(item->conn.frequency);
175 setWifiProtocol(&item->conn.protocol, subtype, phy.wl_phy_ofdm_conf.wl_ofdm_ht_enabled);
176 break;
177 case WL_ERP:
178 item->conn.channel = (uint16_t) phy.wl_phy_erp_conf.wl_erp_channel;
179 setWifiProtocol(&item->conn.protocol, subtype, phy.wl_phy_erp_conf.wl_erp_ht_enabled);
180 break;
181 default:
182 setWifiProtocol(&item->conn.protocol, subtype, false);
183 break;
184 }
185 }
186
187 wl_encryption_t encryption = 0;
188 wl_authmode_t authMode = 0;
189 bool haveEncryption = getWifiValue(fd, WL_ENCRYPTION, &encryption, sizeof(encryption), buffer);
190 bool haveAuthMode = getWifiValue(fd, WL_AUTH_MODE, &authMode, sizeof(authMode), buffer);
191 if (haveEncryption) {
192 setWifiSecurity(&item->conn.security, encryption, haveAuthMode, authMode);
193 }
194 }
195
196 return nullptr;
197}