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