xref: /haiku/src/libs/compat/openbsd_wlan/net80211/ieee80211_haiku.cpp (revision 445d4fd926c569e7b9ae28017da86280aaecbae2)
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 
9 extern "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 
52 static net_notifications_module_info* sNotificationModule;
53 
54 
55 static struct ifnet*
56 get_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 
69 status_t
70 init_wlan_stack(void)
71 {
72 	get_module(NET_NOTIFICATIONS_MODULE_NAME,
73 		(module_info**)&sNotificationModule);
74 
75 	return B_OK;
76 }
77 
78 
79 void
80 uninit_wlan_stack(void)
81 {
82 	if (sNotificationModule != NULL)
83 		put_module(NET_NOTIFICATIONS_MODULE_NAME);
84 }
85 
86 
87 static void
88 ieee80211_init()
89 {
90 }
91 
92 
93 status_t
94 start_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 
114 status_t
115 stop_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 
128 status_t
129 wlan_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 
141 static uint8_t
142 fbsd_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 
150 static uint32
151 obsd_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 
166 status_t
167 wlan_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 
470 void
471 ieee80211_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
493 void
494 ieee80211_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