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