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}