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