xref: /haiku/src/add-ons/kernel/network/devices/ethernet/ethernet.cpp (revision 5ac9b506412b11afb993bb52d161efe7666958a5)
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 	if (ioctl(device->fd, op, argument, length) < 0)
245 		return errno;
246 	return B_OK;
247 }
248 
249 
250 status_t
251 ethernet_send_data(net_device *_device, net_buffer *buffer)
252 {
253 	ethernet_device *device = (ethernet_device *)_device;
254 
255 //dprintf("try to send ethernet packet of %lu bytes (flags %ld):\n", buffer->size, buffer->flags);
256 	if (buffer->size > device->frame_size || buffer->size < ETHER_HEADER_LENGTH)
257 		return B_BAD_VALUE;
258 
259 	net_buffer *allocated = NULL;
260 	net_buffer *original = buffer;
261 
262 	if (gBufferModule->count_iovecs(buffer) > 1) {
263 		// TODO: for now, create a new buffer containing the data
264 		buffer = gBufferModule->duplicate(original);
265 		if (buffer == NULL)
266 			return ENOBUFS;
267 
268 		allocated = buffer;
269 
270 		if (gBufferModule->count_iovecs(buffer) > 1) {
271 			dprintf("scattered I/O is not yet supported by ethernet device.\n");
272 			gBufferModule->free(buffer);
273 			device->stats.send.errors++;
274 			return B_NOT_SUPPORTED;
275 		}
276 	}
277 
278 	struct iovec iovec;
279 	gBufferModule->get_iovecs(buffer, &iovec, 1);
280 
281 //dump_block((const char *)iovec.iov_base, buffer->size, "  ");
282 	ssize_t bytesWritten = write(device->fd, iovec.iov_base, iovec.iov_len);
283 //dprintf("sent: %ld\n", bytesWritten);
284 
285 	if (bytesWritten < 0) {
286 		device->stats.send.errors++;
287 		if (allocated)
288 			gBufferModule->free(allocated);
289 		return errno;
290 	}
291 
292 	device->stats.send.packets++;
293 	device->stats.send.bytes += bytesWritten;
294 
295 	gBufferModule->free(original);
296 	if (allocated)
297 		gBufferModule->free(allocated);
298 	return B_OK;
299 }
300 
301 
302 status_t
303 ethernet_receive_data(net_device *_device, net_buffer **_buffer)
304 {
305 	ethernet_device *device = (ethernet_device *)_device;
306 
307 	if (device->fd == -1)
308 		return B_FILE_ERROR;
309 
310 	// TODO: better header space
311 	net_buffer *buffer = gBufferModule->create(256);
312 	if (buffer == NULL)
313 		return ENOBUFS;
314 
315 	// TODO: this only works for standard ethernet frames - we need iovecs
316 	//	for jumbo frame support (or a separate read buffer)!
317 	//	It would be even nicer to get net_buffers from the ethernet driver
318 	//	directly.
319 
320 	ssize_t bytesRead;
321 	void *data;
322 
323 	status_t status = gBufferModule->append_size(buffer, device->frame_size, &data);
324 	if (status == B_OK && data == NULL) {
325 		dprintf("scattered I/O is not yet supported by ethernet device.\n");
326 		status = B_NOT_SUPPORTED;
327 	}
328 	if (status < B_OK)
329 		goto err;
330 
331 	bytesRead = read(device->fd, data, device->frame_size);
332 	if (bytesRead < 0) {
333 		device->stats.receive.errors++;
334 		status = errno;
335 		goto err;
336 	}
337 //dump_block((const char *)data, bytesRead, "rcv: ");
338 
339 	status = gBufferModule->trim(buffer, bytesRead);
340 	if (status < B_OK) {
341 		device->stats.receive.dropped++;
342 		goto err;
343 	}
344 
345 	device->stats.receive.bytes += bytesRead;
346 	device->stats.receive.packets++;
347 
348 	*_buffer = buffer;
349 	return B_OK;
350 
351 err:
352 	gBufferModule->free(buffer);
353 	return status;
354 }
355 
356 
357 status_t
358 ethernet_set_mtu(net_device *_device, size_t mtu)
359 {
360 	ethernet_device *device = (ethernet_device *)_device;
361 
362 	if (mtu > device->frame_size - ETHER_HEADER_LENGTH
363 		|| mtu <= ETHER_HEADER_LENGTH + 10)
364 		return B_BAD_VALUE;
365 
366 	device->mtu = mtu;
367 	return B_OK;
368 }
369 
370 
371 status_t
372 ethernet_set_promiscuous(net_device *_device, bool promiscuous)
373 {
374 	ethernet_device *device = (ethernet_device *)_device;
375 
376 	int32 value = (int32)promiscuous;
377 	if (ioctl(device->fd, ETHER_SETPROMISC, &value, sizeof(value)) < 0)
378 		return B_NOT_SUPPORTED;
379 
380 	return B_OK;
381 }
382 
383 
384 status_t
385 ethernet_set_media(net_device *device, uint32 media)
386 {
387 	return B_NOT_SUPPORTED;
388 }
389 
390 
391 status_t
392 ethernet_add_multicast(struct net_device *_device, const sockaddr *_address)
393 {
394 	ethernet_device *device = (ethernet_device *)_device;
395 
396 	if (_address->sa_family != AF_LINK)
397 		return B_BAD_VALUE;
398 
399 	const sockaddr_dl *address = (const sockaddr_dl *)_address;
400 	if (address->sdl_type != IFT_ETHER)
401 		return B_BAD_VALUE;
402 
403 	if (ioctl(device->fd, ETHER_ADDMULTI, LLADDR(address), 6) < 0)
404 		return errno;
405 	return B_OK;
406 }
407 
408 
409 status_t
410 ethernet_remove_multicast(struct net_device *_device, const sockaddr *_address)
411 {
412 	ethernet_device *device = (ethernet_device *)_device;
413 
414 	if (_address->sa_family != AF_LINK)
415 		return B_BAD_VALUE;
416 
417 	const sockaddr_dl *address = (const sockaddr_dl *)_address;
418 	if (address->sdl_type != IFT_ETHER)
419 		return B_BAD_VALUE;
420 
421 	if (ioctl(device->fd, ETHER_REMMULTI, LLADDR(address), 6) < 0)
422 		return errno;
423 	return B_OK;
424 }
425 
426 
427 static status_t
428 ethernet_std_ops(int32 op, ...)
429 {
430 	switch (op) {
431 		case B_MODULE_INIT:
432 		{
433 			status_t status = get_module(NET_STACK_MODULE_NAME,
434 				(module_info **)&sStackModule);
435 			if (status < B_OK)
436 				return status;
437 
438 			new (&sCheckList) DoublyLinkedList<ethernet_device>;
439 				// static C++ objects are not initialized in the module startup
440 
441 			sLinkCheckerThread = -1;
442 
443 			sLinkChangeSemaphore = create_sem(0, "ethernet link change");
444 			if (sLinkChangeSemaphore < B_OK) {
445 				put_module(NET_STACK_MODULE_NAME);
446 				return sLinkChangeSemaphore;
447 			}
448 
449 			mutex_init(&sListLock, "ethernet devices");
450 
451 			return B_OK;
452 		}
453 
454 		case B_MODULE_UNINIT:
455 		{
456 			delete_sem(sLinkChangeSemaphore);
457 
458 			status_t status;
459 			wait_for_thread(sLinkCheckerThread, &status);
460 
461 			mutex_destroy(&sListLock);
462 			put_module(NET_STACK_MODULE_NAME);
463 			return B_OK;
464 		}
465 
466 		default:
467 			return B_ERROR;
468 	}
469 }
470 
471 
472 net_device_module_info sEthernetModule = {
473 	{
474 		"network/devices/ethernet/v1",
475 		0,
476 		ethernet_std_ops
477 	},
478 	ethernet_init,
479 	ethernet_uninit,
480 	ethernet_up,
481 	ethernet_down,
482 	ethernet_control,
483 	ethernet_send_data,
484 	ethernet_receive_data,
485 	ethernet_set_mtu,
486 	ethernet_set_promiscuous,
487 	ethernet_set_media,
488 	ethernet_add_multicast,
489 	ethernet_remove_multicast,
490 };
491 
492 module_info *modules[] = {
493 	(module_info *)&sEthernetModule,
494 	NULL
495 };
496