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