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