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 <errno.h>
7#include <string.h>
8
9#define COMPAT_FREEBSD_NET80211 1
10#include <sys/ioctl.h>
11#include <sys/socket.h>
12#include <net/if.h>
13#include <net/if_media.h>
14#include <net80211/ieee80211.h>
15#include <net80211/ieee80211_ioctl.h>
16
17// ieee80211 header of NetBSD is full of mess. Add compatibility macros from FreeBSD
18#undef IEEE80211_IS_CHAN_ANYG
19#define IEEE80211_IS_CHAN_ANYG(x) (IEEE80211_IS_CHAN_PUREG(x) || IEEE80211_IS_CHAN_G(x))
20#undef IEEE80211_IS_CHAN_HT
21#define IEEE80211_IS_CHAN_HT(x) (((x)->ic_flags & IEEE80211_CHAN_HT) != 0)
22#undef IEEE80211_IS_CHAN_VHT
23#define IEEE80211_IS_CHAN_VHT(x) (((x)->ic_flags & IEEE80211_CHAN_VHT) != 0)
24
25const char* ffDetectWifi(FFlist* result) {
26 struct if_nameindex* infs = if_nameindex();
27 if (!infs) {
28 FF_DEBUG("if_nameindex() failed: %s", strerror(errno));
29 return "if_nameindex() failed";
30 }
31
32 FF_AUTO_CLOSE_FD int sock = socket(AF_INET, SOCK_DGRAM, 0);
33 if (sock < 0) {
34 FF_DEBUG("socket(AF_INET, SOCK_DGRAM) failed: %s", strerror(errno));
35 if_freenameindex(infs);
36 return "socket() failed";
37 }
38
39 for (struct if_nameindex* i = infs; !(i->if_index == 0 && i->if_name == nullptr); ++i) {
40 if (!ffStrStartsWith(i->if_name, "iwm")) {
41 continue;
42 }
43
44 FFWifiResult* item = FF_LIST_ADD(FFWifiResult, *result);
45 ffStrbufInitS(&item->inf.description, i->if_name);
46 ffStrbufInit(&item->inf.status);
47 ffStrbufInit(&item->conn.status);
48 ffStrbufInit(&item->conn.ssid);
49 ffStrbufInit(&item->conn.bssid);
50 ffStrbufInit(&item->conn.protocol);
51 ffStrbufInit(&item->conn.security);
52 item->conn.signalQuality = -DBL_MAX;
53 item->conn.rxRate = -DBL_MAX;
54 item->conn.txRate = -DBL_MAX;
55 item->conn.channel = 0;
56 item->conn.channelWidth = 0;
57 item->conn.frequency = 0;
58
59 char ssid[IEEE80211_NWID_LEN + 1] = {};
60 struct ieee80211req ireq = {};
61 strlcpy(ireq.i_name, i->if_name, sizeof(ireq.i_name));
62 ireq.i_type = IEEE80211_IOC_SSID;
63 ireq.i_data = ssid;
64 ireq.i_len = sizeof(ssid) - 1;
65
66 if (ioctl(sock, SIOCG80211, &ireq) < 0 || ireq.i_len == 0) {
67 struct ifreq ifr;
68 strlcpy(ifr.ifr_name, i->if_name, sizeof(ifr.ifr_name));
69 if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) {
70 ffStrbufSetStatic(&item->inf.status, "Unknown");
71 } else {
72 ffStrbufSetStatic(&item->inf.status, ifr.ifr_flags & IFF_UP ? "Up" : "Down");
73 }
74 ffStrbufAppendS(&item->conn.status, "Not associated");
75 continue;
76 }
77
78 ffStrbufSetStatic(&item->inf.status, "Up");
79 ffStrbufSetStatic(&item->conn.status, "Associated");
80 ffStrbufAppendNS(&item->conn.ssid, ireq.i_len, ssid);
81
82 uint8_t bssid[IEEE80211_ADDR_LEN] = {};
83 ireq.i_type = IEEE80211_IOC_BSSID;
84 ireq.i_data = bssid;
85 ireq.i_len = sizeof(bssid);
86
87 if (ioctl(sock, SIOCG80211, &ireq) >= 0) {
88 ffStrbufSetF(&item->conn.bssid, "%02X:%02X:%02X:%02X:%02X:%02X", bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
89 }
90
91 struct ieee80211_channel curchan = {};
92 ireq.i_type = IEEE80211_IOC_CHANNEL;
93 ireq.i_data = &curchan;
94 ireq.i_len = sizeof(curchan);
95
96 if (ioctl(sock, SIOCG80211, &ireq) >= 0) {
97 // item->conn.channel = curchan.ic_ieee; // No ic_ieee in NetBSD
98 item->conn.channel = ffWifiFreqToChannel(curchan.ic_freq);
99 item->conn.frequency = curchan.ic_freq;
100
101 // Determine channel width from channel flags
102 if (curchan.ic_flags & IEEE80211_CHAN_VHT160)
103 item->conn.channelWidth = 160;
104 else if (curchan.ic_flags & IEEE80211_CHAN_VHT80)
105 item->conn.channelWidth = 80;
106 else if (curchan.ic_flags & IEEE80211_CHAN_HT40)
107 item->conn.channelWidth = 40;
108 else
109 item->conn.channelWidth = 20;
110
111#ifdef IEEE80211_IS_CHAN_HE // for future use
112 if (IEEE80211_IS_CHAN_HE(&curchan)) {
113 ffStrbufSetStatic(&item->conn.protocol, "802.11ax (Wi-Fi 6)");
114 } else
115#endif
116 if (IEEE80211_IS_CHAN_VHT(&curchan))
117 ffStrbufSetStatic(&item->conn.protocol, "802.11ac (Wi-Fi 5)");
118 else if (IEEE80211_IS_CHAN_HT(&curchan)) {
119 ffStrbufSetStatic(&item->conn.protocol, "802.11n (Wi-Fi 4)");
120 } else if (IEEE80211_IS_CHAN_ANYG(&curchan)) {
121 ffStrbufSetStatic(&item->conn.protocol, "802.11g");
122 } else if (IEEE80211_IS_CHAN_B(&curchan)) {
123 ffStrbufSetStatic(&item->conn.protocol, "802.11b");
124 } else if (IEEE80211_IS_CHAN_A(&curchan)) {
125 ffStrbufSetStatic(&item->conn.protocol, "802.11a");
126 } else if (IEEE80211_IS_CHAN_FHSS(&curchan)) {
127 ffStrbufSetStatic(&item->conn.protocol, "802.11 (FHSS)");
128 }
129 }
130
131 union {
132 struct ieee80211req_sta_req req;
133 uint8_t buf[1024];
134 } stareq = {};
135 memcpy(stareq.req.is_u.macaddr, bssid, sizeof(bssid));
136 ireq.i_type = IEEE80211_IOC_STA_INFO;
137 ireq.i_data = &stareq;
138 ireq.i_len = sizeof(stareq);
139
140 if (ioctl(sock, SIOCG80211, &ireq) >= 0) {
141 struct ieee80211req_sta_info* sta = stareq.req.info;
142 if (sta->isi_len != 0) {
143 int8_t rssi = (int8_t) sta->isi_rssi; // Strangely, `sta->isi_rssi` is unsigned
144 item->conn.signalQuality = (rssi >= -50 ? 100 : rssi <= -100 ? 0
145 : (rssi + 100) * 2);
146
147 if (sta->isi_txrate) {
148 item->conn.txRate = (double) sta->isi_txrate / 2.0;
149 }
150 }
151 }
152
153 ireq.i_type = IEEE80211_IOC_AUTHMODE;
154 ireq.i_data = nullptr;
155 ireq.i_len = 0;
156 if (ioctl(sock, SIOCG80211, &ireq) >= 0) {
157 switch (ireq.i_val) {
158 case IEEE80211_AUTH_NONE:
159 ffStrbufSetStatic(&item->conn.security, "Insecure");
160 break;
161 case IEEE80211_AUTH_OPEN:
162 ffStrbufSetStatic(&item->conn.security, "Open");
163 break;
164 case IEEE80211_AUTH_SHARED:
165 ffStrbufSetStatic(&item->conn.security, "Shared");
166 break;
167 case IEEE80211_AUTH_8021X:
168 ffStrbufSetStatic(&item->conn.security, "8021X");
169 break;
170 case IEEE80211_AUTH_AUTO:
171 ffStrbufSetStatic(&item->conn.security, "Auto");
172 break;
173 case IEEE80211_AUTH_WPA:
174 ffStrbufSetStatic(&item->conn.security, "WPA");
175 break;
176 default:
177 ffStrbufSetF(&item->conn.security, "Unknown (%d)", ireq.i_val);
178 break;
179 }
180 }
181 }
182
183 if_freenameindex(infs);
184 return nullptr;
185}