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