1/*
2 * Copyright 2022, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include <AutoDeleter.h>
7#include <util/KMessage.h>
8
9extern "C" {
10#include <sys/param.h>
11#include <sys/systm.h>
12#include <sys/mbuf.h>
13#include <sys/kernel.h>
14#include <sys/bus.h>
15#include <sys/socket.h>
16#include <sys/sockio.h>
17#include <sys/endian.h>
18#include <sys/errno.h>
19#include <sys/task.h>
20
21#include <net/if.h>
22#include <net/if_dl.h>
23#include <net/if_media.h>
24
25#include <netinet/in.h>
26#include <netinet/if_ether.h>
27
28#include <net80211/ieee80211_var.h>
29#include <net80211/ieee80211_node.h>
30#include <net80211/ieee80211_ioctl.h>
31
32#undef _NET80211_IEEE80211_IOCTL_H_
33#define IEEE80211_IOCTLS_ABBREVIATED
34#include "../../freebsd_wlan/net80211/ieee80211_ioctl.h"
35}
36
37#include <shared.h>
38
39#include <ether_driver.h>
40#include <NetworkDevice.h>
41#include <net_notifications.h>
42
43
44#define TRACE_WLAN
45#ifdef TRACE_WLAN
46#	define TRACE(x...) dprintf(x);
47#else
48#	define TRACE(x...) ;
49#endif
50
51
52static net_notifications_module_info* sNotificationModule;
53
54
55static struct ifnet*
56get_ifnet(device_t device, int& i)
57{
58	void* softc = device_get_softc(device);
59
60	for (i = 0; i < MAX_DEVICES; i++) {
61		if (gDevices[i] != NULL && gDevices[i]->if_softc == softc)
62			return gDevices[i];
63	}
64
65	return NULL;
66}
67
68
69status_t
70init_wlan_stack(void)
71{
72	get_module(NET_NOTIFICATIONS_MODULE_NAME,
73		(module_info**)&sNotificationModule);
74
75	return B_OK;
76}
77
78
79void
80uninit_wlan_stack(void)
81{
82	if (sNotificationModule != NULL)
83		put_module(NET_NOTIFICATIONS_MODULE_NAME);
84}
85
86
87static void
88ieee80211_init()
89{
90}
91
92
93status_t
94start_wlan(device_t device)
95{
96	int i;
97	struct ifnet* ifp = get_ifnet(device, i);
98	if (ifp == NULL)
99		return B_BAD_VALUE;
100
101	struct ieee80211com* ic = (ieee80211com*)ifp;
102
103	if (ifp->if_init == NULL)
104		ifp->if_init = ieee80211_init;
105	ifp->if_flags |= IFF_NEEDSGIANT;
106
107	if_initname(ifp, device_get_name(device), i);
108
109	TRACE("%s: wlan started.\n", __func__);
110	return B_OK;
111}
112
113
114status_t
115stop_wlan(device_t device)
116{
117	int i;
118	struct ifnet* ifp = get_ifnet(device, i);
119	if (ifp == NULL)
120		return B_BAD_VALUE;
121
122	struct ieee80211com* ic = (ieee80211com*)ifp;
123
124	return B_OK;
125}
126
127
128status_t
129wlan_close(void* cookie)
130{
131	TRACE("wlan_close(%p)\n", cookie);
132	struct ifnet* ifp = (struct ifnet*)cookie;
133
134	ifp->if_flags &= ~IFF_UP;
135	ifp->if_ioctl(ifp, SIOCSIFFLAGS, NULL);
136
137	return B_OK;
138}
139
140
141static uint8_t
142fbsd_capinfo_from_obsd(uint16_t obsd_capinfo)
143{
144	// FreeBSD only exposes the first 8 bits of the capinfo,
145	// and these are identical in OpenBSD. Makes things easy.
146	return uint8_t(obsd_capinfo);
147}
148
149
150static uint32
151obsd_ciphers_from_haiku(uint32 ciphers)
152{
153	uint32 obsd_ciphers = 0;
154	if ((ciphers & B_NETWORK_CIPHER_WEP_40) != 0)
155		obsd_ciphers |= IEEE80211_WPA_CIPHER_WEP40;
156	if ((ciphers & B_NETWORK_CIPHER_WEP_104) != 0)
157		obsd_ciphers |= IEEE80211_WPA_CIPHER_WEP104;
158	if ((ciphers & B_NETWORK_CIPHER_TKIP) != 0)
159		obsd_ciphers |= IEEE80211_WPA_CIPHER_TKIP;
160	if ((ciphers & B_NETWORK_CIPHER_CCMP) != 0)
161		obsd_ciphers |= IEEE80211_WPA_CIPHER_CCMP;
162	return obsd_ciphers;
163}
164
165
166status_t
167wlan_control(void* cookie, uint32 op, void* arg, size_t length)
168{
169	if (op != SIOCG80211 && op != SIOCS80211)
170		return B_BAD_VALUE;
171
172	struct ifnet* ifp = (struct ifnet*)cookie;
173	struct ieee80211com* ic = (ieee80211com*)ifp;
174
175	struct ieee80211req ireq;
176	if (user_memcpy(&ireq, arg, sizeof(struct ieee80211req)) != B_OK)
177		return B_BAD_ADDRESS;
178
179	switch (ireq.i_type) {
180		case IEEE80211_IOC_SCAN_REQ: {
181			if (op != SIOCS80211)
182				return B_BAD_VALUE;
183
184			// SIOCS80211SCAN is a no-op, scans cannot actually be initiated.
185			// But we can at least check if one is already in progress.
186			status_t status = EBUSY;
187
188			IFF_LOCKGIANT(ifp);
189			if (ic->ic_state == IEEE80211_S_SCAN || (ic->ic_flags & IEEE80211_F_BGSCAN) != 0)
190				status = EINPROGRESS;
191			IFF_UNLOCKGIANT(ifp);
192
193			return status;
194		}
195
196		case IEEE80211_IOC_SCAN_RESULTS: {
197			if (op != SIOCG80211)
198				return B_BAD_VALUE;
199
200			struct ieee80211_nodereq nodereq;
201			struct ieee80211_nodereq_all nodereq_all = {};
202
203			// We need a scan_result of maximum possible size to work with.
204			struct ieee80211req_scan_result* sr = (struct ieee80211req_scan_result*)
205				alloca(sizeof(struct ieee80211req_scan_result) + IEEE80211_NWID_LEN + 257);
206
207			uint16 remaining = ireq.i_len, offset = 0;
208			for (int i = 0; remaining > 0; i++) {
209				nodereq_all.na_node = &nodereq;
210				nodereq_all.na_size = sizeof(struct ieee80211_nodereq);
211				nodereq_all.na_startnode = i;
212
213				IFF_LOCKGIANT(ifp);
214				status_t status = ifp->if_ioctl(ifp, SIOCG80211ALLNODES, (caddr_t)&nodereq_all);
215				IFF_UNLOCKGIANT(ifp);
216				if (status != B_OK)
217					return status;
218				if (nodereq_all.na_nodes == 0)
219					break;
220
221				int32 size = sizeof(struct ieee80211req_scan_result) + nodereq.nr_nwid_len;
222				uint16_t ieLen = 0;
223				if (nodereq.nr_rsnie[1] != 0) {
224					ieLen = 2 + nodereq.nr_rsnie[1];
225					size += ieLen;
226				}
227				const int32 roundedSize = roundup(size, 4);
228				if (remaining < roundedSize)
229					break;
230
231				sr->isr_ie_off = sizeof(struct ieee80211req_scan_result);
232				sr->isr_ie_len = ieLen;
233				sr->isr_freq = ieee80211_ieee2mhz(nodereq.nr_channel, nodereq.nr_chan_flags);
234				sr->isr_flags = 0; /* not actually used by userland */
235				sr->isr_noise = 0; /* unknown */
236				sr->isr_rssi = nodereq.nr_rssi;
237				sr->isr_intval = nodereq.nr_intval;
238				sr->isr_capinfo = fbsd_capinfo_from_obsd(nodereq.nr_capinfo);
239				sr->isr_erp = nodereq.nr_erp;
240				memcpy(sr->isr_bssid, nodereq.nr_bssid, IEEE80211_ADDR_LEN);
241				sr->isr_nrates = nodereq.nr_nrates;
242				memcpy(sr->isr_rates, nodereq.nr_rates, IEEE80211_RATE_MAXSIZE);
243				sr->isr_ssid_len = nodereq.nr_nwid_len;
244				sr->isr_meshid_len = 0;
245				memcpy((uint8*)sr + sr->isr_ie_off, nodereq.nr_nwid, sr->isr_ssid_len);
246				memcpy((uint8*)sr + sr->isr_ie_off + sr->isr_ssid_len, nodereq.nr_rsnie, ieLen);
247
248				sr->isr_len = roundedSize;
249				if (user_memcpy((uint8*)ireq.i_data + offset, sr, size) != B_OK)
250					return B_BAD_ADDRESS;
251
252				offset += sr->isr_len;
253				remaining -= sr->isr_len;
254			}
255			ireq.i_len = offset;
256
257			IFF_LOCKGIANT(ifp);
258			const bigtime_t RAISE_INACT_INTERVAL = 5 * 1000 * 1000 /* 5s */;
259			if (ic->ic_opmode == IEEE80211_M_STA
260					&& system_time() >= (ic->ic_last_raise_inact + RAISE_INACT_INTERVAL)) {
261				// net80211 only raises and checks inactivity during active scans, or background
262				// scans performed in S_RUN, so we need to do it here so that stale nodes are
263				// evicted for S_SCAN, too. (This means "inact" will be raised a bit more often
264				// and nodes evicted faster during other modes, but that's acceptable.)
265				ieee80211_iterate_nodes(ic, ieee80211_node_raise_inact, NULL);
266				ieee80211_clean_inactive_nodes(ic, IEEE80211_INACT_SCAN);
267				ic->ic_last_raise_inact = system_time();
268			}
269			IFF_UNLOCKGIANT(ifp);
270
271			break;
272		}
273
274		case IEEE80211_IOC_BSSID: {
275			if (ireq.i_len != IEEE80211_ADDR_LEN)
276				return EINVAL;
277
278			struct ieee80211_bssid bssid;
279			if (op == SIOCS80211) {
280				if (user_memcpy(bssid.i_bssid, ireq.i_data, ireq.i_len) != B_OK)
281					return B_BAD_ADDRESS;
282			}
283
284			IFF_LOCKGIANT(ifp);
285			status_t status = ifp->if_ioctl(ifp, op == SIOCG80211 ?
286				SIOCG80211BSSID : SIOCS80211BSSID, (caddr_t)&bssid);
287			IFF_UNLOCKGIANT(ifp);
288			if (status != B_OK)
289				return status;
290
291			if (op == SIOCG80211) {
292				if (user_memcpy(ireq.i_data, bssid.i_bssid, ireq.i_len) != B_OK)
293					return B_BAD_ADDRESS;
294			}
295
296			break;
297		}
298
299		case IEEE80211_IOC_SSID: {
300			struct ifreq ifr;
301			struct ieee80211_nwid nwid;
302			ifr.ifr_data = (uint8_t*)&nwid;
303
304			if (op == SIOCS80211) {
305				nwid.i_len = ireq.i_len;
306				if (user_memcpy(nwid.i_nwid, ireq.i_data, ireq.i_len) != B_OK)
307					return B_BAD_ADDRESS;
308			}
309
310			IFF_LOCKGIANT(ifp);
311			status_t status = ifp->if_ioctl(ifp, op == SIOCG80211 ?
312				SIOCG80211NWID : SIOCS80211NWID, (caddr_t)&ifr);
313			ieee80211_state state = ic->ic_state;
314			IFF_UNLOCKGIANT(ifp);
315			if (status != B_OK)
316				return status;
317
318			if (op == SIOCG80211) {
319				if (state == IEEE80211_S_INIT || state == IEEE80211_S_SCAN) {
320					// The returned NWID will be the one we want to join, not connected to.
321					ireq.i_len = 0;
322				} else {
323					if (ireq.i_len < nwid.i_len)
324						return E2BIG;
325					ireq.i_len = nwid.i_len;
326					if (user_memcpy(ireq.i_data, nwid.i_nwid, ireq.i_len) != B_OK)
327						return B_BAD_ADDRESS;
328				}
329			}
330
331			break;
332		}
333
334		case IEEE80211_IOC_MLME: {
335			if (op != SIOCS80211)
336				return B_BAD_VALUE;
337
338			struct ieee80211req_mlme mlme;
339			if (user_memcpy(&mlme, ireq.i_data, min_c(sizeof(mlme), ireq.i_len)) != B_OK)
340				return B_BAD_ADDRESS;
341
342			switch (mlme.im_op) {
343				case IEEE80211_MLME_DISASSOC:
344				case IEEE80211_MLME_DEAUTH: {
345					struct ifreq ifr;
346					struct ieee80211_nwid nwid;
347					ifr.ifr_data = (uint8_t*)&nwid;
348					nwid.i_len = 0;
349
350					IFF_LOCKGIANT(ifp);
351					status_t status = ifp->if_ioctl(ifp, SIOCS80211NWID, (caddr_t)&ifr);
352					IFF_UNLOCKGIANT(ifp);
353					if (status != 0)
354						return status;
355					break;
356				}
357
358				default:
359					TRACE("openbsd wlan_control: unsupported MLME operation %" B_PRIu8 "\n",
360						mlme.im_op);
361					return EOPNOTSUPP;
362			}
363
364			break;
365		}
366
367		case IEEE80211_IOC_HAIKU_JOIN: {
368			if (op != SIOCS80211)
369				return B_BAD_VALUE;
370
371			struct ieee80211_haiku_join_req* haiku_join =
372				(struct ieee80211_haiku_join_req*)calloc(1, ireq.i_len);
373			MemoryDeleter haikuJoinDeleter(haiku_join);
374			if (!haikuJoinDeleter.IsSet())
375				return B_NO_MEMORY;
376
377			if (user_memcpy(haiku_join, ireq.i_data, ireq.i_len) != B_OK)
378				return B_BAD_ADDRESS;
379
380			struct ifreq ifr;
381			struct ieee80211_nwid nwid;
382			struct ieee80211_wpaparams wpaparams;
383			struct ieee80211_wpapsk wpapsk;
384			memset(&wpaparams, 0, sizeof(wpaparams));
385			memset(&wpapsk, 0, sizeof(wpapsk));
386
387			// Convert join information.
388			ifr.ifr_data = (uint8_t*)&nwid;
389			nwid.i_len = haiku_join->i_nwid_len;
390			memcpy(nwid.i_nwid, haiku_join->i_nwid, nwid.i_len);
391
392			switch (haiku_join->i_authentication_mode) {
393				case B_NETWORK_AUTHENTICATION_NONE:
394					break;
395
396				case B_NETWORK_AUTHENTICATION_WPA:
397				case B_NETWORK_AUTHENTICATION_WPA2:
398					wpaparams.i_enabled = 1;
399					wpaparams.i_protos |=
400						(haiku_join->i_authentication_mode == B_NETWORK_AUTHENTICATION_WPA2) ?
401							IEEE80211_WPA_PROTO_WPA2 : IEEE80211_WPA_PROTO_WPA1;
402
403					// NOTE: i_wpaparams.i_groupcipher is not a flags field!
404					wpaparams.i_ciphers = obsd_ciphers_from_haiku(haiku_join->i_ciphers);
405					wpaparams.i_groupcipher =
406						fls(obsd_ciphers_from_haiku(haiku_join->i_group_ciphers));
407
408					if ((haiku_join->i_key_mode & B_KEY_MODE_IEEE802_1X) != 0)
409						wpaparams.i_akms |= IEEE80211_WPA_AKM_8021X;
410					if ((haiku_join->i_key_mode & B_KEY_MODE_PSK) != 0)
411						wpaparams.i_akms |= IEEE80211_WPA_AKM_PSK;
412					if ((haiku_join->i_key_mode & B_KEY_MODE_IEEE802_1X_SHA256) != 0)
413						wpaparams.i_akms |= IEEE80211_WPA_AKM_SHA256_8021X;
414					if ((haiku_join->i_key_mode & B_KEY_MODE_PSK_SHA256) != 0)
415						wpaparams.i_akms |= IEEE80211_WPA_AKM_SHA256_PSK;
416
417					if (haiku_join->i_key_len != 0) {
418						wpapsk.i_enabled = 1;
419						memcpy(wpapsk.i_psk, haiku_join->i_key, haiku_join->i_key_len);
420						memset(&wpapsk.i_psk[haiku_join->i_key_len], 0,
421							sizeof(wpapsk.i_psk) - haiku_join->i_key_len);
422					}
423					break;
424
425				default:
426					TRACE("openbsd wlan_control: unsupported authentication mode %" B_PRIu32 "\n",
427						haiku_join->i_authentication_mode);
428					return EOPNOTSUPP;
429			}
430
431			IFF_LOCKGIANT(ifp);
432			status_t status = ifp->if_ioctl(ifp, SIOCS80211NWID, (caddr_t)&ifr);
433			if (status != B_OK) {
434				IFF_UNLOCKGIANT(ifp);
435				return status;
436			}
437			if (wpapsk.i_enabled) {
438				status = ifp->if_ioctl(ifp, SIOCS80211WPAPSK, (caddr_t)&wpapsk);
439				if (status != B_OK) {
440					IFF_UNLOCKGIANT(ifp);
441					return status;
442				}
443			}
444			if (wpaparams.i_enabled) {
445				status = ifp->if_ioctl(ifp, SIOCS80211WPAPARMS, (caddr_t)&wpaparams);
446				if (status != B_OK) {
447					IFF_UNLOCKGIANT(ifp);
448					return status;
449				}
450			}
451			IFF_UNLOCKGIANT(ifp);
452			if (status != B_OK)
453				return status;
454
455			break;
456		}
457
458		default:
459			TRACE("openbsd wlan_control: %" B_PRIu32 ", %d (not supported)\n", op, ireq.i_type);
460			return EOPNOTSUPP;
461	}
462
463	if (op == SIOCG80211 && user_memcpy(arg, &ireq,
464			sizeof(struct ieee80211req)) != B_OK)
465		return B_BAD_ADDRESS;
466	return B_OK;
467}
468
469
470void
471ieee80211_rtm_80211info_task(void* arg)
472{
473	struct ieee80211com *ic = arg;
474	struct ifnet *ifp = &ic->ic_if;
475
476	if (sNotificationModule != NULL) {
477		char messageBuffer[512];
478		KMessage message;
479		message.SetTo(messageBuffer, sizeof(messageBuffer), B_NETWORK_MONITOR);
480		if (ifp->if_link_state == LINK_STATE_UP)
481			message.AddInt32("opcode", B_NETWORK_WLAN_JOINED);
482		else
483			message.AddInt32("opcode", B_NETWORK_WLAN_LEFT);
484		message.AddString("interface", ifp->device_name);
485		// TODO: add data about the node
486
487		sNotificationModule->send_notification(&message);
488	}
489}
490
491
492#if 0
493void
494ieee80211_notify_scan_done(struct ieee80211vap* vap)
495{
496	release_sem_etc(vap->iv_ifp->scan_done_sem, 1,
497		B_DO_NOT_RESCHEDULE | B_RELEASE_ALL);
498
499	TRACE("%s\n", __FUNCTION__);
500
501	if (sNotificationModule != NULL) {
502		char messageBuffer[512];
503		KMessage message;
504		message.SetTo(messageBuffer, sizeof(messageBuffer), B_NETWORK_MONITOR);
505		message.AddInt32("opcode", B_NETWORK_WLAN_SCANNED);
506		message.AddString("interface", vap->iv_ifp->device_name);
507
508		sNotificationModule->send_notification(&message);
509	}
510}
511#endif
512