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