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