xref: /haiku/src/add-ons/kernel/network/devices/ethernet/ethernet.cpp (revision da4dbfa47a47beb355289f3dd685797cee69ab77)
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 				return EMSGSIZE;
317 			}
318 
319 			gBufferModule->get_iovecs(buffer, &iovec, 1);
320 		}
321 	} else {
322 		gBufferModule->get_iovecs(buffer, &iovec, 1);
323 	}
324 
325 //dump_block((const char *)iovec.iov_base, buffer->size, "  ");
326 	ssize_t bytesWritten = write(device->fd, iovec.iov_base, iovec.iov_len);
327 //dprintf("sent: %ld\n", bytesWritten);
328 
329 	if (bytesWritten < 0) {
330 		if (allocated)
331 			gBufferModule->free(allocated);
332 		return errno;
333 	}
334 
335 	gBufferModule->free(original);
336 	if (allocated)
337 		gBufferModule->free(allocated);
338 	return B_OK;
339 }
340 
341 
342 status_t
343 ethernet_receive_data(net_device *_device, net_buffer **_buffer)
344 {
345 	ethernet_device *device = (ethernet_device *)_device;
346 
347 	if (device->fd == -1)
348 		return B_FILE_ERROR;
349 
350 	// TODO: better header space
351 	net_buffer *buffer = gBufferModule->create(256);
352 	if (buffer == NULL)
353 		return ENOBUFS;
354 
355 	MutexLocker bufferLocker;
356 	struct iovec iovec;
357 	ssize_t bytesRead;
358 	status_t status;
359 	if (device->read_buffer != NULL) {
360 		bufferLocker.SetTo(device->read_buffer_lock, false);
361 
362 		iovec.iov_base = device->read_buffer;
363 		iovec.iov_len = device->frame_size;
364 	} else {
365 		void *data;
366 		status = gBufferModule->append_size(buffer, device->frame_size, &data);
367 		if (status == B_OK && data == NULL) {
368 			dprintf("ethernet_receive_data: no read buffer, cannot perform scattered I/O!\n");
369 			status = B_NOT_SUPPORTED;
370 		}
371 		if (status < B_OK)
372 			goto err;
373 
374 		iovec.iov_base = data;
375 		iovec.iov_len = device->frame_size;
376 	}
377 
378 	bytesRead = read(device->fd, iovec.iov_base, iovec.iov_len);
379 	if (bytesRead < 0) {
380 		status = errno;
381 		goto err;
382 	}
383 //dump_block((const char *)data, bytesRead, "rcv: ");
384 
385 	if (iovec.iov_base == device->read_buffer)
386 		status = gBufferModule->append(buffer, iovec.iov_base, bytesRead);
387 	else
388 		status = gBufferModule->trim(buffer, bytesRead);
389 	if (status < B_OK) {
390 		atomic_add((int32*)&device->stats.receive.dropped, 1);
391 		goto err;
392 	}
393 
394 	*_buffer = buffer;
395 	return B_OK;
396 
397 err:
398 	gBufferModule->free(buffer);
399 	return status;
400 }
401 
402 
403 status_t
404 ethernet_set_mtu(net_device *_device, size_t mtu)
405 {
406 	ethernet_device *device = (ethernet_device *)_device;
407 
408 	if (mtu > device->frame_size - ETHER_HEADER_LENGTH
409 		|| mtu <= ETHER_HEADER_LENGTH + 10)
410 		return B_BAD_VALUE;
411 
412 	device->mtu = mtu;
413 	return B_OK;
414 }
415 
416 
417 status_t
418 ethernet_set_promiscuous(net_device *_device, bool promiscuous)
419 {
420 	ethernet_device *device = (ethernet_device *)_device;
421 
422 	int32 value = (int32)promiscuous;
423 	if (ioctl(device->fd, ETHER_SETPROMISC, &value, sizeof(value)) < 0)
424 		return B_NOT_SUPPORTED;
425 
426 	return B_OK;
427 }
428 
429 
430 status_t
431 ethernet_set_media(net_device *device, uint32 media)
432 {
433 	return B_NOT_SUPPORTED;
434 }
435 
436 
437 status_t
438 ethernet_add_multicast(struct net_device *_device, const sockaddr *_address)
439 {
440 	ethernet_device *device = (ethernet_device *)_device;
441 
442 	if (_address->sa_family != AF_LINK)
443 		return B_BAD_VALUE;
444 
445 	const sockaddr_dl *address = (const sockaddr_dl *)_address;
446 	if (address->sdl_type != IFT_ETHER)
447 		return B_BAD_VALUE;
448 
449 	if (ioctl(device->fd, ETHER_ADDMULTI, LLADDR(address), 6) < 0)
450 		return errno;
451 	return B_OK;
452 }
453 
454 
455 status_t
456 ethernet_remove_multicast(struct net_device *_device, const sockaddr *_address)
457 {
458 	ethernet_device *device = (ethernet_device *)_device;
459 
460 	if (_address->sa_family != AF_LINK)
461 		return B_BAD_VALUE;
462 
463 	const sockaddr_dl *address = (const sockaddr_dl *)_address;
464 	if (address->sdl_type != IFT_ETHER)
465 		return B_BAD_VALUE;
466 
467 	if (ioctl(device->fd, ETHER_REMMULTI, LLADDR(address), 6) < 0)
468 		return errno;
469 	return B_OK;
470 }
471 
472 
473 static status_t
474 ethernet_std_ops(int32 op, ...)
475 {
476 	switch (op) {
477 		case B_MODULE_INIT:
478 		{
479 			status_t status = get_module(NET_STACK_MODULE_NAME,
480 				(module_info **)&sStackModule);
481 			if (status < B_OK)
482 				return status;
483 
484 			new (&sCheckList) DoublyLinkedList<ethernet_device>;
485 				// static C++ objects are not initialized in the module startup
486 
487 			sLinkCheckerThread = -1;
488 
489 			sLinkChangeSemaphore = create_sem(0, "ethernet link change");
490 			if (sLinkChangeSemaphore < B_OK) {
491 				put_module(NET_STACK_MODULE_NAME);
492 				return sLinkChangeSemaphore;
493 			}
494 
495 			mutex_init(&sListLock, "ethernet devices");
496 
497 			return B_OK;
498 		}
499 
500 		case B_MODULE_UNINIT:
501 		{
502 			delete_sem(sLinkChangeSemaphore);
503 
504 			status_t status;
505 			wait_for_thread(sLinkCheckerThread, &status);
506 
507 			mutex_destroy(&sListLock);
508 			put_module(NET_STACK_MODULE_NAME);
509 			return B_OK;
510 		}
511 
512 		default:
513 			return B_ERROR;
514 	}
515 }
516 
517 
518 net_device_module_info sEthernetModule = {
519 	{
520 		"network/devices/ethernet/v1",
521 		0,
522 		ethernet_std_ops
523 	},
524 	ethernet_init,
525 	ethernet_uninit,
526 	ethernet_up,
527 	ethernet_down,
528 	ethernet_control,
529 	ethernet_send_data,
530 	ethernet_receive_data,
531 	ethernet_set_mtu,
532 	ethernet_set_promiscuous,
533 	ethernet_set_media,
534 	ethernet_add_multicast,
535 	ethernet_remove_multicast,
536 };
537 
538 module_info *modules[] = {
539 	(module_info *)&sEthernetModule,
540 	NULL
541 };
542