xref: /haiku/src/add-ons/kernel/network/devices/ethernet/ethernet.cpp (revision e1caa56b1e49e83f71ba7998969efbf3bcdbf5dc)
1 /*
2  * Copyright 2006-2009, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Axel Dörfler, axeld@pinc-software.de
7  */
8 
9 
10 #include <ethernet.h>
11 
12 #include <ether_driver.h>
13 #include <net_buffer.h>
14 #include <net_device.h>
15 #include <net_stack.h>
16 
17 #include <lock.h>
18 #include <util/AutoLock.h>
19 #include <util/DoublyLinkedList.h>
20 
21 #include <KernelExport.h>
22 
23 #include <errno.h>
24 #include <net/if.h>
25 #include <net/if_dl.h>
26 #include <net/if_media.h>
27 #include <net/if_types.h>
28 #include <new>
29 #include <stdlib.h>
30 #include <string.h>
31 
32 
33 struct ethernet_device : net_device, DoublyLinkedListLinkImpl<ethernet_device> {
34 	int		fd;
35 	uint32	frame_size;
36 };
37 
38 static const bigtime_t kLinkCheckInterval = 1000000;
39 	// 1 second
40 
41 net_buffer_module_info *gBufferModule;
42 static net_stack_module_info *sStackModule;
43 
44 static mutex sListLock;
45 static DoublyLinkedList<ethernet_device> sCheckList;
46 static sem_id sLinkChangeSemaphore;
47 static thread_id sLinkCheckerThread;
48 
49 
50 static status_t
51 update_link_state(ethernet_device *device, bool notify = true)
52 {
53 	ether_link_state state;
54 	if (ioctl(device->fd, ETHER_GET_LINK_STATE, &state,
55 			sizeof(ether_link_state)) < 0) {
56 		// This device does not support retrieving the link
57 		return B_NOT_SUPPORTED;
58 	}
59 
60 	state.media |= IFM_ETHER;
61 		// make sure the media type is returned correctly
62 
63 	if (device->media != state.media
64 		|| device->link_quality != state.quality
65 		|| device->link_speed != state.speed) {
66 		device->media = state.media;
67 		device->link_quality = state.quality;
68 		device->link_speed = state.speed;
69 
70 		if (device->media & IFM_ACTIVE)
71 			device->flags |= IFF_LINK;
72 		else
73 			device->flags &= ~IFF_LINK;
74 
75 		dprintf("%s: media change, media 0x%0x quality %u speed %u\n",
76 				device->name, (unsigned int)device->media,
77 				(unsigned int)device->link_quality,
78 				(unsigned int)device->link_speed);
79 
80 		if (notify)
81 			sStackModule->device_link_changed(device);
82 	}
83 
84 	return B_OK;
85 }
86 
87 
88 static status_t
89 ethernet_link_checker(void *)
90 {
91 	while (true) {
92 		status_t status = acquire_sem_etc(sLinkChangeSemaphore, 1,
93 			B_RELATIVE_TIMEOUT, kLinkCheckInterval);
94 		if (status == B_BAD_SEM_ID)
95 			break;
96 
97 		MutexLocker _(sListLock);
98 
99 		if (sCheckList.IsEmpty())
100 			break;
101 
102 		// check link state of all existing devices
103 
104 		DoublyLinkedList<ethernet_device>::Iterator iterator
105 			= sCheckList.GetIterator();
106 		while (iterator.HasNext()) {
107 			update_link_state(iterator.Next());
108 		}
109 	}
110 
111 	return B_OK;
112 }
113 
114 
115 //	#pragma mark -
116 
117 
118 status_t
119 ethernet_init(const char *name, net_device **_device)
120 {
121 	// Make sure this is a device in /dev/net, but not the
122 	// networking (userland) stack driver.
123 	// Also make sure the user didn't pass a path like
124 	// /dev/net/../etc.
125 	if (strncmp(name, "/dev/net/", 9)
126 		|| !strcmp(name, "/dev/net/userland_server")
127 		|| strstr(name, "..") != NULL)
128 		return B_BAD_VALUE;
129 
130 	if (access(name, F_OK) != 0)
131 		return errno;
132 
133 	status_t status = get_module(NET_BUFFER_MODULE_NAME, (module_info **)&gBufferModule);
134 	if (status < B_OK)
135 		return status;
136 
137 	ethernet_device *device = new (std::nothrow) ethernet_device;
138 	if (device == NULL) {
139 		put_module(NET_BUFFER_MODULE_NAME);
140 		return B_NO_MEMORY;
141 	}
142 
143 	memset(device, 0, sizeof(ethernet_device));
144 
145 	strcpy(device->name, name);
146 	device->flags = IFF_BROADCAST | IFF_LINK;
147 	device->type = IFT_ETHER;
148 	device->mtu = 1500;
149 	device->media = IFM_ACTIVE | IFM_ETHER;
150 	device->header_length = ETHER_HEADER_LENGTH;
151 	device->fd = -1;
152 
153 	*_device = device;
154 	return B_OK;
155 }
156 
157 
158 status_t
159 ethernet_uninit(net_device *device)
160 {
161 	put_module(NET_BUFFER_MODULE_NAME);
162 	delete device;
163 
164 	return B_OK;
165 }
166 
167 
168 status_t
169 ethernet_up(net_device *_device)
170 {
171 	ethernet_device *device = (ethernet_device *)_device;
172 
173 	device->fd = open(device->name, O_RDWR);
174 	if (device->fd < 0)
175 		return errno;
176 
177 	uint64 dummy;
178 	if (ioctl(device->fd, ETHER_INIT, &dummy, sizeof(dummy)) < 0)
179 		goto err;
180 
181 	if (ioctl(device->fd, ETHER_GETADDR, device->address.data, ETHER_ADDRESS_LENGTH) < 0)
182 		goto err;
183 
184 	if (ioctl(device->fd, ETHER_GETFRAMESIZE, &device->frame_size, sizeof(uint32)) < 0) {
185 		// this call is obviously optional
186 		device->frame_size = ETHER_MAX_FRAME_SIZE;
187 	}
188 
189 	if (update_link_state(device, false) == B_OK) {
190 		// device supports retrieval of the link state
191 
192 		// Set the change notification semaphore; doesn't matter
193 		// if this is supported by the device or not
194 		ioctl(device->fd, ETHER_SET_LINK_STATE_SEM, &sLinkChangeSemaphore,
195 			sizeof(sem_id));
196 
197 		MutexLocker _(&sListLock);
198 
199 		if (sCheckList.IsEmpty()) {
200 			// start thread
201 			sLinkCheckerThread = spawn_kernel_thread(ethernet_link_checker,
202 				"ethernet link state checker", B_LOW_PRIORITY, NULL);
203 			if (sLinkCheckerThread >= B_OK)
204 				resume_thread(sLinkCheckerThread);
205 		}
206 
207 		sCheckList.Add(device);
208 	}
209 
210 	device->address.length = ETHER_ADDRESS_LENGTH;
211 	device->mtu = device->frame_size - device->header_length;
212 	return B_OK;
213 
214 err:
215 	close(device->fd);
216 	device->fd = -1;
217 	return errno;
218 }
219 
220 
221 void
222 ethernet_down(net_device *_device)
223 {
224 	ethernet_device *device = (ethernet_device *)_device;
225 
226 	MutexLocker _(sListLock);
227 
228 	// if the device is still part of the list, remove it
229 	if (device->GetDoublyLinkedListLink()->next != NULL
230 		|| device->GetDoublyLinkedListLink()->previous != NULL
231 		|| device == sCheckList.Head())
232 		sCheckList.Remove(device);
233 
234 	close(device->fd);
235 	device->fd = -1;
236 }
237 
238 
239 status_t
240 ethernet_control(net_device *_device, int32 op, void *argument,
241 	size_t length)
242 {
243 	ethernet_device *device = (ethernet_device *)_device;
244 	return ioctl(device->fd, op, argument, length);
245 }
246 
247 
248 status_t
249 ethernet_send_data(net_device *_device, net_buffer *buffer)
250 {
251 	ethernet_device *device = (ethernet_device *)_device;
252 
253 //dprintf("try to send ethernet packet of %lu bytes (flags %ld):\n", buffer->size, buffer->flags);
254 	if (buffer->size > device->frame_size || buffer->size < ETHER_HEADER_LENGTH)
255 		return B_BAD_VALUE;
256 
257 	net_buffer *allocated = NULL;
258 	net_buffer *original = buffer;
259 
260 	if (gBufferModule->count_iovecs(buffer) > 1) {
261 		// TODO: for now, create a new buffer containing the data
262 		buffer = gBufferModule->duplicate(original);
263 		if (buffer == NULL)
264 			return ENOBUFS;
265 
266 		allocated = buffer;
267 
268 		if (gBufferModule->count_iovecs(buffer) > 1) {
269 			dprintf("scattered I/O is not yet supported by ethernet device.\n");
270 			gBufferModule->free(buffer);
271 			device->stats.send.errors++;
272 			return B_NOT_SUPPORTED;
273 		}
274 	}
275 
276 	struct iovec iovec;
277 	gBufferModule->get_iovecs(buffer, &iovec, 1);
278 
279 //dump_block((const char *)iovec.iov_base, buffer->size, "  ");
280 	ssize_t bytesWritten = write(device->fd, iovec.iov_base, iovec.iov_len);
281 //dprintf("sent: %ld\n", bytesWritten);
282 
283 	if (bytesWritten < 0) {
284 		device->stats.send.errors++;
285 		if (allocated)
286 			gBufferModule->free(allocated);
287 		return errno;
288 	}
289 
290 	device->stats.send.packets++;
291 	device->stats.send.bytes += bytesWritten;
292 
293 	gBufferModule->free(original);
294 	if (allocated)
295 		gBufferModule->free(allocated);
296 	return B_OK;
297 }
298 
299 
300 status_t
301 ethernet_receive_data(net_device *_device, net_buffer **_buffer)
302 {
303 	ethernet_device *device = (ethernet_device *)_device;
304 
305 	if (device->fd == -1)
306 		return B_FILE_ERROR;
307 
308 	// TODO: better header space
309 	net_buffer *buffer = gBufferModule->create(256);
310 	if (buffer == NULL)
311 		return ENOBUFS;
312 
313 	// TODO: this only works for standard ethernet frames - we need iovecs
314 	//	for jumbo frame support (or a separate read buffer)!
315 	//	It would be even nicer to get net_buffers from the ethernet driver
316 	//	directly.
317 
318 	ssize_t bytesRead;
319 	void *data;
320 
321 	status_t status = gBufferModule->append_size(buffer, device->frame_size, &data);
322 	if (status == B_OK && data == NULL) {
323 		dprintf("scattered I/O is not yet supported by ethernet device.\n");
324 		status = B_NOT_SUPPORTED;
325 	}
326 	if (status < B_OK)
327 		goto err;
328 
329 	bytesRead = read(device->fd, data, device->frame_size);
330 	if (bytesRead < 0) {
331 		device->stats.receive.errors++;
332 		status = errno;
333 		goto err;
334 	}
335 //dump_block((const char *)data, bytesRead, "rcv: ");
336 
337 	status = gBufferModule->trim(buffer, bytesRead);
338 	if (status < B_OK) {
339 		device->stats.receive.dropped++;
340 		goto err;
341 	}
342 
343 	device->stats.receive.bytes += bytesRead;
344 	device->stats.receive.packets++;
345 
346 	*_buffer = buffer;
347 	return B_OK;
348 
349 err:
350 	gBufferModule->free(buffer);
351 	return status;
352 }
353 
354 
355 status_t
356 ethernet_set_mtu(net_device *_device, size_t mtu)
357 {
358 	ethernet_device *device = (ethernet_device *)_device;
359 
360 	if (mtu > device->frame_size - ETHER_HEADER_LENGTH
361 		|| mtu <= ETHER_HEADER_LENGTH + 10)
362 		return B_BAD_VALUE;
363 
364 	device->mtu = mtu;
365 	return B_OK;
366 }
367 
368 
369 status_t
370 ethernet_set_promiscuous(net_device *_device, bool promiscuous)
371 {
372 	ethernet_device *device = (ethernet_device *)_device;
373 
374 	int32 value = (int32)promiscuous;
375 	if (ioctl(device->fd, ETHER_SETPROMISC, &value, sizeof(value)) < 0)
376 		return B_NOT_SUPPORTED;
377 
378 	return B_OK;
379 }
380 
381 
382 status_t
383 ethernet_set_media(net_device *device, uint32 media)
384 {
385 	return B_NOT_SUPPORTED;
386 }
387 
388 
389 status_t
390 ethernet_add_multicast(struct net_device *_device, const sockaddr *_address)
391 {
392 	ethernet_device *device = (ethernet_device *)_device;
393 
394 	if (_address->sa_family != AF_LINK)
395 		return B_BAD_VALUE;
396 
397 	const sockaddr_dl *address = (const sockaddr_dl *)_address;
398 	if (address->sdl_type != IFT_ETHER)
399 		return B_BAD_VALUE;
400 
401 	return ioctl(device->fd, ETHER_ADDMULTI, LLADDR(address), 6);
402 }
403 
404 
405 status_t
406 ethernet_remove_multicast(struct net_device *_device, const sockaddr *_address)
407 {
408 	ethernet_device *device = (ethernet_device *)_device;
409 
410 	if (_address->sa_family != AF_LINK)
411 		return B_BAD_VALUE;
412 
413 	const sockaddr_dl *address = (const sockaddr_dl *)_address;
414 	if (address->sdl_type != IFT_ETHER)
415 		return B_BAD_VALUE;
416 
417 	return ioctl(device->fd, ETHER_REMMULTI, LLADDR(address), 6);
418 }
419 
420 
421 static status_t
422 ethernet_std_ops(int32 op, ...)
423 {
424 	switch (op) {
425 		case B_MODULE_INIT:
426 		{
427 			status_t status = get_module(NET_STACK_MODULE_NAME,
428 				(module_info **)&sStackModule);
429 			if (status < B_OK)
430 				return status;
431 
432 			new (&sCheckList) DoublyLinkedList<ethernet_device>;
433 				// static C++ objects are not initialized in the module startup
434 
435 			sLinkCheckerThread = -1;
436 
437 			sLinkChangeSemaphore = create_sem(0, "ethernet link change");
438 			if (sLinkChangeSemaphore < B_OK) {
439 				put_module(NET_STACK_MODULE_NAME);
440 				return sLinkChangeSemaphore;
441 			}
442 
443 			mutex_init(&sListLock, "ethernet devices");
444 
445 			return B_OK;
446 		}
447 
448 		case B_MODULE_UNINIT:
449 		{
450 			delete_sem(sLinkChangeSemaphore);
451 
452 			status_t status;
453 			wait_for_thread(sLinkCheckerThread, &status);
454 
455 			mutex_destroy(&sListLock);
456 			put_module(NET_STACK_MODULE_NAME);
457 			return B_OK;
458 		}
459 
460 		default:
461 			return B_ERROR;
462 	}
463 }
464 
465 
466 net_device_module_info sEthernetModule = {
467 	{
468 		"network/devices/ethernet/v1",
469 		0,
470 		ethernet_std_ops
471 	},
472 	ethernet_init,
473 	ethernet_uninit,
474 	ethernet_up,
475 	ethernet_down,
476 	ethernet_control,
477 	ethernet_send_data,
478 	ethernet_receive_data,
479 	ethernet_set_mtu,
480 	ethernet_set_promiscuous,
481 	ethernet_set_media,
482 	ethernet_add_multicast,
483 	ethernet_remove_multicast,
484 };
485 
486 module_info *modules[] = {
487 	(module_info *)&sEthernetModule,
488 	NULL
489 };
490