xref: /haiku/src/libs/compat/openbsd_wlan/net80211/ieee80211_haiku.cpp (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
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_RESULTS: {
181 			if (op != SIOCG80211)
182 				return B_BAD_VALUE;
183 
184 			struct ieee80211_nodereq nodereq;
185 			struct ieee80211_nodereq_all nodereq_all = {};
186 
187 			// We need a scan_result of maximum possible size to work with.
188 			struct ieee80211req_scan_result* sr = (struct ieee80211req_scan_result*)
189 				alloca(sizeof(struct ieee80211req_scan_result) + IEEE80211_NWID_LEN + 257);
190 
191 			uint16 remaining = ireq.i_len, offset = 0;
192 			for (int i = 0; remaining > 0; i++) {
193 				nodereq_all.na_node = &nodereq;
194 				nodereq_all.na_size = sizeof(struct ieee80211_nodereq);
195 				nodereq_all.na_startnode = i;
196 
197 				IFF_LOCKGIANT(ifp);
198 				status_t status = ifp->if_ioctl(ifp, SIOCG80211ALLNODES, (caddr_t)&nodereq_all);
199 				IFF_UNLOCKGIANT(ifp);
200 				if (status != B_OK)
201 					return status;
202 				if (nodereq_all.na_nodes == 0)
203 					break;
204 
205 				int32 size = sizeof(struct ieee80211req_scan_result) + nodereq.nr_nwid_len;
206 				uint16_t ieLen = 0;
207 				if (nodereq.nr_rsnie[1] != 0) {
208 					ieLen = 2 + nodereq.nr_rsnie[1];
209 					size += ieLen;
210 				}
211 				const int32 roundedSize = roundup(size, 4);
212 				if (remaining < roundedSize)
213 					break;
214 
215 				sr->isr_ie_off = sizeof(struct ieee80211req_scan_result);
216 				sr->isr_ie_len = ieLen;
217 				sr->isr_freq = ieee80211_ieee2mhz(nodereq.nr_channel, nodereq.nr_chan_flags);
218 				sr->isr_flags = 0; /* not actually used by userland */
219 				sr->isr_noise = 0; /* unknown */
220 				sr->isr_rssi = nodereq.nr_rssi;
221 				sr->isr_intval = nodereq.nr_intval;
222 				sr->isr_capinfo = fbsd_capinfo_from_obsd(nodereq.nr_capinfo);
223 				sr->isr_erp = nodereq.nr_erp;
224 				memcpy(sr->isr_bssid, nodereq.nr_bssid, IEEE80211_ADDR_LEN);
225 				sr->isr_nrates = nodereq.nr_nrates;
226 				memcpy(sr->isr_rates, nodereq.nr_rates, IEEE80211_RATE_MAXSIZE);
227 				sr->isr_ssid_len = nodereq.nr_nwid_len;
228 				sr->isr_meshid_len = 0;
229 				memcpy((uint8*)sr + sr->isr_ie_off, nodereq.nr_nwid, sr->isr_ssid_len);
230 				memcpy((uint8*)sr + sr->isr_ie_off + sr->isr_ssid_len, nodereq.nr_rsnie, ieLen);
231 
232 				sr->isr_len = roundedSize;
233 				if (user_memcpy((uint8*)ireq.i_data + offset, sr, size) != B_OK)
234 					return B_BAD_ADDRESS;
235 
236 				offset += sr->isr_len;
237 				remaining -= sr->isr_len;
238 			}
239 			ireq.i_len = offset;
240 
241 			IFF_LOCKGIANT(ifp);
242 			const bigtime_t RAISE_INACT_INTERVAL = 5 * 1000 * 1000 /* 5s */;
243 			if (ic->ic_opmode == IEEE80211_M_STA
244 					&& system_time() >= (ic->ic_last_raise_inact + RAISE_INACT_INTERVAL)) {
245 				// net80211 only raises and checks inactivity during active scans, or background
246 				// scans performed in S_RUN, so we need to do it here so that stale nodes are
247 				// evicted for S_SCAN, too. (This means "inact" will be raised a bit more often
248 				// and nodes evicted faster during other modes, but that's acceptable.)
249 				ieee80211_iterate_nodes(ic, ieee80211_node_raise_inact, NULL);
250 				ieee80211_clean_inactive_nodes(ic, IEEE80211_INACT_SCAN);
251 				ic->ic_last_raise_inact = system_time();
252 			}
253 			IFF_UNLOCKGIANT(ifp);
254 
255 			break;
256 		}
257 
258 		case IEEE80211_IOC_BSSID: {
259 			if (ireq.i_len != IEEE80211_ADDR_LEN)
260 				return EINVAL;
261 
262 			struct ieee80211_bssid bssid;
263 			if (op == SIOCS80211) {
264 				if (user_memcpy(bssid.i_bssid, ireq.i_data, ireq.i_len) != B_OK)
265 					return B_BAD_ADDRESS;
266 			}
267 
268 			IFF_LOCKGIANT(ifp);
269 			status_t status = ifp->if_ioctl(ifp, op == SIOCG80211 ?
270 				SIOCG80211BSSID : SIOCS80211BSSID, (caddr_t)&bssid);
271 			IFF_UNLOCKGIANT(ifp);
272 			if (status != B_OK)
273 				return status;
274 
275 			if (op == SIOCG80211) {
276 				if (user_memcpy(ireq.i_data, bssid.i_bssid, ireq.i_len) != B_OK)
277 					return B_BAD_ADDRESS;
278 			}
279 
280 			break;
281 		}
282 
283 		case IEEE80211_IOC_SSID: {
284 			struct ifreq ifr;
285 			struct ieee80211_nwid nwid;
286 			ifr.ifr_data = (uint8_t*)&nwid;
287 
288 			if (op == SIOCS80211) {
289 				nwid.i_len = ireq.i_len;
290 				if (user_memcpy(nwid.i_nwid, ireq.i_data, ireq.i_len) != B_OK)
291 					return B_BAD_ADDRESS;
292 			}
293 
294 			IFF_LOCKGIANT(ifp);
295 			status_t status = ifp->if_ioctl(ifp, op == SIOCG80211 ?
296 				SIOCG80211NWID : SIOCS80211NWID, (caddr_t)&ifr);
297 			ieee80211_state state = ic->ic_state;
298 			IFF_UNLOCKGIANT(ifp);
299 			if (status != B_OK)
300 				return status;
301 
302 			if (op == SIOCG80211) {
303 				if (state == IEEE80211_S_INIT || state == IEEE80211_S_SCAN) {
304 					// The returned NWID will be the one we want to join, not connected to.
305 					ireq.i_len = 0;
306 				} else {
307 					if (ireq.i_len < nwid.i_len)
308 						return E2BIG;
309 					ireq.i_len = nwid.i_len;
310 					if (user_memcpy(ireq.i_data, nwid.i_nwid, ireq.i_len) != B_OK)
311 						return B_BAD_ADDRESS;
312 				}
313 			}
314 
315 			break;
316 		}
317 
318 		case IEEE80211_IOC_MLME: {
319 			if (op != SIOCS80211)
320 				return B_BAD_VALUE;
321 
322 			struct ieee80211req_mlme mlme;
323 			if (user_memcpy(&mlme, ireq.i_data, min_c(sizeof(mlme), ireq.i_len)) != B_OK)
324 				return B_BAD_ADDRESS;
325 
326 			switch (mlme.im_op) {
327 				case IEEE80211_MLME_DISASSOC:
328 				case IEEE80211_MLME_DEAUTH: {
329 					struct ifreq ifr;
330 					struct ieee80211_nwid nwid;
331 					ifr.ifr_data = (uint8_t*)&nwid;
332 					nwid.i_len = 0;
333 
334 					IFF_LOCKGIANT(ifp);
335 					status_t status = ifp->if_ioctl(ifp, SIOCS80211NWID, (caddr_t)&ifr);
336 					IFF_UNLOCKGIANT(ifp);
337 					if (status != 0)
338 						return status;
339 					break;
340 				}
341 
342 				default:
343 					TRACE("openbsd wlan_control: unsupported MLME operation %" B_PRIu8 "\n",
344 						mlme.im_op);
345 					return EOPNOTSUPP;
346 			}
347 
348 			break;
349 		}
350 
351 		case IEEE80211_IOC_HAIKU_JOIN: {
352 			if (op != SIOCS80211)
353 				return B_BAD_VALUE;
354 
355 			struct ieee80211_haiku_join_req* haiku_join =
356 				(struct ieee80211_haiku_join_req*)calloc(1, ireq.i_len);
357 			MemoryDeleter haikuJoinDeleter(haiku_join);
358 			if (!haikuJoinDeleter.IsSet())
359 				return B_NO_MEMORY;
360 
361 			if (user_memcpy(haiku_join, ireq.i_data, ireq.i_len) != B_OK)
362 				return B_BAD_ADDRESS;
363 
364 			struct ifreq ifr;
365 			struct ieee80211_nwid nwid;
366 			struct ieee80211_wpaparams wpaparams;
367 			struct ieee80211_wpapsk wpapsk;
368 			memset(&wpaparams, 0, sizeof(wpaparams));
369 			memset(&wpapsk, 0, sizeof(wpapsk));
370 
371 			// Convert join information.
372 			ifr.ifr_data = (uint8_t*)&nwid;
373 			nwid.i_len = haiku_join->i_nwid_len;
374 			memcpy(nwid.i_nwid, haiku_join->i_nwid, nwid.i_len);
375 
376 			switch (haiku_join->i_authentication_mode) {
377 				case B_NETWORK_AUTHENTICATION_NONE:
378 					break;
379 
380 				case B_NETWORK_AUTHENTICATION_WPA:
381 				case B_NETWORK_AUTHENTICATION_WPA2:
382 					wpaparams.i_enabled = 1;
383 					wpaparams.i_protos |=
384 						(haiku_join->i_authentication_mode == B_NETWORK_AUTHENTICATION_WPA2) ?
385 							IEEE80211_WPA_PROTO_WPA2 : IEEE80211_WPA_PROTO_WPA1;
386 
387 					// NOTE: i_wpaparams.i_groupcipher is not a flags field!
388 					wpaparams.i_ciphers = obsd_ciphers_from_haiku(haiku_join->i_ciphers);
389 					wpaparams.i_groupcipher =
390 						fls(obsd_ciphers_from_haiku(haiku_join->i_group_ciphers));
391 
392 					if ((haiku_join->i_key_mode & B_KEY_MODE_IEEE802_1X) != 0)
393 						wpaparams.i_akms |= IEEE80211_WPA_AKM_8021X;
394 					if ((haiku_join->i_key_mode & B_KEY_MODE_PSK) != 0)
395 						wpaparams.i_akms |= IEEE80211_WPA_AKM_PSK;
396 					if ((haiku_join->i_key_mode & B_KEY_MODE_IEEE802_1X_SHA256) != 0)
397 						wpaparams.i_akms |= IEEE80211_WPA_AKM_SHA256_8021X;
398 					if ((haiku_join->i_key_mode & B_KEY_MODE_PSK_SHA256) != 0)
399 						wpaparams.i_akms |= IEEE80211_WPA_AKM_SHA256_PSK;
400 
401 					if (haiku_join->i_key_len != 0) {
402 						wpapsk.i_enabled = 1;
403 						memcpy(wpapsk.i_psk, haiku_join->i_key, haiku_join->i_key_len);
404 						memset(&wpapsk.i_psk[haiku_join->i_key_len], 0,
405 							sizeof(wpapsk.i_psk) - haiku_join->i_key_len);
406 					}
407 					break;
408 
409 				default:
410 					TRACE("openbsd wlan_control: unsupported authentication mode %" B_PRIu32 "\n",
411 						haiku_join->i_authentication_mode);
412 					return EOPNOTSUPP;
413 			}
414 
415 			IFF_LOCKGIANT(ifp);
416 			status_t status = ifp->if_ioctl(ifp, SIOCS80211NWID, (caddr_t)&ifr);
417 			if (status != B_OK) {
418 				IFF_UNLOCKGIANT(ifp);
419 				return status;
420 			}
421 			if (wpapsk.i_enabled) {
422 				status = ifp->if_ioctl(ifp, SIOCS80211WPAPSK, (caddr_t)&wpapsk);
423 				if (status != B_OK) {
424 					IFF_UNLOCKGIANT(ifp);
425 					return status;
426 				}
427 			}
428 			if (wpaparams.i_enabled) {
429 				status = ifp->if_ioctl(ifp, SIOCS80211WPAPARMS, (caddr_t)&wpaparams);
430 				if (status != B_OK) {
431 					IFF_UNLOCKGIANT(ifp);
432 					return status;
433 				}
434 			}
435 			IFF_UNLOCKGIANT(ifp);
436 			if (status != B_OK)
437 				return status;
438 
439 			break;
440 		}
441 
442 		default:
443 			TRACE("openbsd wlan_control: %" B_PRIu32 ", %d (not supported)\n", op, ireq.i_type);
444 			return EOPNOTSUPP;
445 	}
446 
447 	if (op == SIOCG80211 && user_memcpy(arg, &ireq,
448 			sizeof(struct ieee80211req)) != B_OK)
449 		return B_BAD_ADDRESS;
450 	return B_OK;
451 }
452 
453 
454 void
455 ieee80211_rtm_80211info_task(void* arg)
456 {
457 	struct ieee80211com *ic = arg;
458 	struct ifnet *ifp = &ic->ic_if;
459 
460 	if (sNotificationModule != NULL) {
461 		char messageBuffer[512];
462 		KMessage message;
463 		message.SetTo(messageBuffer, sizeof(messageBuffer), B_NETWORK_MONITOR);
464 		if (ifp->if_link_state == LINK_STATE_UP)
465 			message.AddInt32("opcode", B_NETWORK_WLAN_JOINED);
466 		else
467 			message.AddInt32("opcode", B_NETWORK_WLAN_LEFT);
468 		message.AddString("interface", ifp->device_name);
469 		// TODO: add data about the node
470 
471 		sNotificationModule->send_notification(&message);
472 	}
473 }
474 
475 
476 #if 0
477 void
478 ieee80211_notify_scan_done(struct ieee80211vap* vap)
479 {
480 	release_sem_etc(vap->iv_ifp->scan_done_sem, 1,
481 		B_DO_NOT_RESCHEDULE | B_RELEASE_ALL);
482 
483 	TRACE("%s\n", __FUNCTION__);
484 
485 	if (sNotificationModule != NULL) {
486 		char messageBuffer[512];
487 		KMessage message;
488 		message.SetTo(messageBuffer, sizeof(messageBuffer), B_NETWORK_MONITOR);
489 		message.AddInt32("opcode", B_NETWORK_WLAN_SCANNED);
490 		message.AddString("interface", vap->iv_ifp->device_name);
491 
492 		sNotificationModule->send_notification(&message);
493 	}
494 }
495 #endif
496