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