xref: /haiku/src/kits/network/libnetapi/NetworkDevice.cpp (revision 308f0e195dc9a71620970f8ebfe6e02fe6e27ddc)
1 /*
2  * Copyright 2010, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <NetworkDevice.h>
8 
9 #include <errno.h>
10 #include <net/if.h>
11 #include <sys/sockio.h>
12 
13 #include <AutoDeleter.h>
14 
15 extern "C" {
16 #include <net80211/ieee80211_ioctl.h>
17 }
18 
19 
20 status_t
21 get_80211(const char* name, int32 type, void* data, int32& length)
22 {
23 	int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
24 	if (socket < 0)
25 		return errno;
26 
27 	FileDescriptorCloser closer(socket);
28 
29 	struct ieee80211req ireq;
30 	strlcpy(ireq.i_name, name, IF_NAMESIZE);
31 	ireq.i_type = type;
32 	ireq.i_val = 0;
33 	ireq.i_len = length;
34 	ireq.i_data = data;
35 
36 	if (ioctl(socket, SIOCG80211, &ireq, sizeof(struct ieee80211req)) < 0)
37 		return errno;
38 
39 	length = ireq.i_len;
40 	return B_OK;
41 }
42 
43 #if 0
44 static status_t
45 set_80211(const char* name, int32 type, void* data,
46 	int32 length = 0, int32 value = 0)
47 {
48 	int socket = ::socket(AF_INET, SOCK_DGRAM, 0);
49 	if (socket < 0)
50 		return errno;
51 
52 	FileDescriptorCloser closer(socket);
53 
54 	struct ieee80211req ireq;
55 	strlcpy(ireq.i_name, name, IF_NAMESIZE);
56 	ireq.i_type = type;
57 	ireq.i_val = value;
58 	ireq.i_len = length;
59 	ireq.i_data = data;
60 
61 	if (ioctl(socket, SIOCS80211, &ireq, sizeof(struct ieee80211req)) < 0)
62 		return errno;
63 
64 	return B_OK;
65 }
66 
67 
68 template<typename T> status_t
69 set_80211(const char* name, int32 type, T& data, int32 length = 0,
70 	int32 value = 0)
71 {
72 	if (length == 0)
73 		length = sizeof(T);
74 
75 	return set_80211(name, &data, length, value);
76 }
77 #endif
78 
79 template<typename T> status_t
80 do_request(T& request, const char* name, int option)
81 {
82 	int socket = ::socket(AF_LINK, SOCK_DGRAM, 0);
83 	if (socket < 0)
84 		return errno;
85 
86 	FileDescriptorCloser closer(socket);
87 
88 	strlcpy(((struct ifreq&)request).ifr_name, name, IF_NAMESIZE);
89 
90 	if (ioctl(socket, option, &request, sizeof(T)) < 0)
91 		return errno;
92 
93 	return B_OK;
94 }
95 
96 
97 // #pragma mark -
98 
99 
100 BNetworkDevice::BNetworkDevice()
101 {
102 	Unset();
103 }
104 
105 
106 BNetworkDevice::BNetworkDevice(const char* name)
107 {
108 	SetTo(name);
109 }
110 
111 
112 BNetworkDevice::~BNetworkDevice()
113 {
114 }
115 
116 
117 void
118 BNetworkDevice::Unset()
119 {
120 	fName[0] = '\0';
121 }
122 
123 
124 void
125 BNetworkDevice::SetTo(const char* name)
126 {
127 	strlcpy(fName, name, IF_NAMESIZE);
128 }
129 
130 
131 const char*
132 BNetworkDevice::Name() const
133 {
134 	return fName;
135 }
136 
137 
138 uint32
139 BNetworkDevice::Flags() const
140 {
141 	ifreq request;
142 	if (do_request(request, Name(), SIOCGIFFLAGS) != B_OK)
143 		return 0;
144 
145 	return request.ifr_flags;
146 }
147 
148 
149 bool
150 BNetworkDevice::HasLink() const
151 {
152 	return (Flags() & IFF_LINK) != 0;
153 }
154 
155 
156 int32
157 BNetworkDevice::CountMedia() const
158 {
159 	ifmediareq request;
160 	request.ifm_count = 0;
161 	request.ifm_ulist = NULL;
162 
163 	if (do_request(request, Name(), SIOCGIFMEDIA) != B_OK)
164 		return -1;
165 
166 	return request.ifm_count;
167 }
168 
169 
170 int32
171 BNetworkDevice::Media() const
172 {
173 	ifmediareq request;
174 	request.ifm_count = 0;
175 	request.ifm_ulist = NULL;
176 
177 	if (do_request(request, Name(), SIOCGIFMEDIA) != B_OK)
178 		return -1;
179 
180 	return request.ifm_current;
181 }
182 
183 
184 int32
185 BNetworkDevice::GetMediaAt(int32 index) const
186 {
187 	// TODO: this could do some caching
188 	return 0;
189 }
190 
191 
192 status_t
193 BNetworkDevice::SetMedia(int32 media)
194 {
195 	ifreq request;
196 	request.ifr_media = media;
197 	return do_request(request, Name(), SIOCSIFMEDIA);
198 }
199 
200 
201 status_t
202 BNetworkDevice::GetHardwareAddress(BNetworkAddress& address)
203 {
204 	ifreq request;
205 	status_t status = do_request(request, Name(), SIOCSIFMEDIA);
206 	if (status != B_OK)
207 		return status;
208 
209 	address.SetTo(request.ifr_addr);
210 	return B_OK;
211 }
212 
213 
214 ssize_t
215 BNetworkDevice::CountScanResults()
216 {
217 	// TODO: this needs some caching!
218 	const size_t kBufferSize = 32768;
219 	uint8* buffer = (uint8*)malloc(kBufferSize);
220 	if (buffer == NULL)
221 		return B_NO_MEMORY;
222 
223 	MemoryDeleter deleter(buffer);
224 
225 	int32 length = kBufferSize;
226 	status_t status = get_80211(Name(), IEEE80211_IOC_SCAN_RESULTS,
227 		buffer, length);
228 	if (status < B_OK)
229 		return status;
230 
231 	int32 bytesLeft = length;
232 	uint8* entry = buffer;
233 	int32 count = 0;
234 
235 	while (bytesLeft > (int32)sizeof(struct ieee80211req_scan_result)) {
236 		ieee80211req_scan_result* result
237 			= (ieee80211req_scan_result*)entry;
238 
239 		entry += result->isr_len;
240 		bytesLeft -= result->isr_len;
241 		count++;
242 	}
243 
244 	return count;
245 }
246 
247 
248 status_t
249 BNetworkDevice::GetScanResultAt(int32 index, wireless_network& network)
250 {
251 #if 0
252 	if (index == 0) {
253 		struct ieee80211_scan_req request;
254 		memset(&request, 0, sizeof(request));
255 		request.sr_flags = IEEE80211_IOC_SCAN_ACTIVE | IEEE80211_IOC_SCAN_ONCE
256 			| IEEE80211_IOC_SCAN_NOJOIN;
257 		request.sr_duration = IEEE80211_IOC_SCAN_FOREVER;
258 		set_80211(Name(), IEEE80211_IOC_SCAN_REQ, NULL);
259 	}
260 #endif
261 
262 	const size_t kBufferSize = 32768;
263 	uint8* buffer = (uint8*)malloc(kBufferSize);
264 	if (buffer == NULL)
265 		return B_NO_MEMORY;
266 
267 	MemoryDeleter deleter(buffer);
268 
269 	int32 length = kBufferSize;
270 	status_t status = get_80211(Name(), IEEE80211_IOC_SCAN_RESULTS,
271 		buffer, length);
272 	if (status != B_OK)
273 		return status;
274 
275 	int32 bytesLeft = length;
276 	uint8* entry = buffer;
277 	int32 count = 0;
278 
279 	while (bytesLeft > (int32)sizeof(struct ieee80211req_scan_result)) {
280 		ieee80211req_scan_result* result
281 			= (ieee80211req_scan_result*)entry;
282 
283 		if (count == index) {
284 			// Fill wireless_network with scan result data
285 			// TODO: add more fields
286 			strlcpy(network.name, (char*)(result + 1),
287 				min_c((int)sizeof(network.name), result->isr_ssid_len + 1));
288 			network.address.SetToLinkLevel(result->isr_bssid,
289 				IEEE80211_ADDR_LEN);
290 			network.signal_strength = result->isr_rssi;
291 			network.noise_level = result->isr_noise;
292 
293 			return B_OK;
294 		}
295 
296 		entry += result->isr_len;
297 		bytesLeft -= result->isr_len;
298 		count++;
299 	}
300 
301 	return B_BAD_INDEX;
302 }
303