1 /* 2 * Copyright 2006-2008, 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 #define _KERNEL_DEBUG_H 11 // avoid including the private kernel debug.h header 12 13 #include "argv.h" 14 #include "tcp.h" 15 #include "utility.h" 16 17 #include <NetBufferUtilities.h> 18 #include <net_buffer.h> 19 #include <net_datalink.h> 20 #include <net_protocol.h> 21 #include <net_socket.h> 22 #include <net_stack.h> 23 #include <slab/Slab.h> 24 #include <util/AutoLock.h> 25 26 #include <KernelExport.h> 27 #include <Select.h> 28 #include <module.h> 29 #include <Locker.h> 30 31 #include <ctype.h> 32 #include <netinet/in.h> 33 #include <new> 34 #include <set> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 39 40 struct context { 41 BLocker lock; 42 sem_id wait_sem; 43 struct list list; 44 net_route route; 45 bool server; 46 thread_id thread; 47 }; 48 49 struct cmd_entry { 50 char* name; 51 void (*func)(int argc, char **argv); 52 char* help; 53 }; 54 55 struct net_socket_private : net_socket { 56 struct list_link link; 57 team_id owner; 58 uint32 max_backlog; 59 uint32 child_count; 60 struct list pending_children; 61 struct list connected_children; 62 63 struct select_sync_pool *select_pool; 64 mutex lock; 65 }; 66 67 68 extern "C" status_t _add_builtin_module(module_info *info); 69 extern "C" status_t _get_builtin_dependencies(void); 70 extern bool gDebugOutputEnabled; 71 // from libkernelland_emu.so 72 73 extern struct net_buffer_module_info gNetBufferModule; 74 // from net_buffer.cpp 75 extern net_address_module_info gIPv4AddressModule; 76 // from ipv4_address.cpp 77 extern module_info *modules[]; 78 // from tcp.cpp 79 80 81 extern struct net_protocol_module_info gDomainModule; 82 struct net_interface gInterface; 83 extern struct net_socket_module_info gNetSocketModule; 84 struct net_protocol_module_info *gTCPModule; 85 struct net_socket *gServerSocket, *gClientSocket; 86 static struct context sClientContext, sServerContext; 87 88 static vint32 sPacketNumber = 1; 89 static double sRandomDrop = 0.0; 90 static set<uint32> sDropList; 91 static bigtime_t sRoundTripTime = 0; 92 static bool sIncreasingRoundTrip = false; 93 static bool sRandomRoundTrip = false; 94 static bool sTCPDump = true; 95 static bigtime_t sStartTime; 96 static double sRandomReorder = 0.0; 97 static set<uint32> sReorderList; 98 static bool sSimultaneousConnect = false; 99 static bool sSimultaneousClose = false; 100 static bool sServerActiveClose = false; 101 102 static struct net_domain sDomain = { 103 "ipv4", 104 AF_INET, 105 {}, 106 &gDomainModule, 107 &gIPv4AddressModule 108 }; 109 110 111 static bool 112 is_server(const sockaddr* addr) 113 { 114 return ((sockaddr_in*)addr)->sin_port == htons(1024); 115 } 116 117 118 static uint8 119 tcp_segment_flags(net_buffer* buffer) 120 { 121 NetBufferHeaderReader<tcp_header> bufferHeader(buffer); 122 if (bufferHeader.Status() < B_OK) 123 return bufferHeader.Status(); 124 125 tcp_header &header = bufferHeader.Data(); 126 return header.flags; 127 } 128 129 130 static bool 131 is_syn(net_buffer* buffer) 132 { 133 return (tcp_segment_flags(buffer) & TCP_FLAG_SYNCHRONIZE) != 0; 134 } 135 136 137 static bool 138 is_fin(net_buffer* buffer) 139 { 140 return (tcp_segment_flags(buffer) & TCP_FLAG_FINISH) != 0; 141 } 142 143 144 // #pragma mark - stack 145 146 147 status_t 148 std_ops(int32, ...) 149 { 150 return B_OK; 151 } 152 153 154 net_domain * 155 get_domain(int family) 156 { 157 return &sDomain; 158 } 159 160 161 status_t 162 register_domain_protocols(int family, int type, int protocol, ...) 163 { 164 return B_OK; 165 } 166 167 168 status_t 169 register_domain_datalink_protocols(int family, int type, ...) 170 { 171 return B_OK; 172 } 173 174 175 static status_t 176 register_domain_receiving_protocol(int family, int type, const char *moduleName) 177 { 178 return B_OK; 179 } 180 181 182 static bool 183 dummy_is_syscall(void) 184 { 185 return false; 186 } 187 188 189 static bool 190 dummy_is_restarted_syscall(void) 191 { 192 return false; 193 } 194 195 196 static void 197 dummy_store_syscall_restart_timeout(bigtime_t timeout) 198 { 199 } 200 201 202 static net_stack_module_info gNetStackModule = { 203 { 204 NET_STACK_MODULE_NAME, 205 0, 206 std_ops 207 }, 208 NULL, // register_domain, 209 NULL, // unregister_domain, 210 get_domain, 211 212 register_domain_protocols, 213 register_domain_datalink_protocols, 214 register_domain_receiving_protocol, 215 216 NULL, // get_domain_receiving_protocol, 217 NULL, // put_domain_receiving_protocol, 218 219 NULL, // register_device_deframer, 220 NULL, // unregister_device_deframer, 221 NULL, // register_domain_device_handler, 222 NULL, // register_device_handler, 223 NULL, // unregister_device_handler, 224 NULL, // register_device_monitor, 225 NULL, // unregister_device_monitor, 226 NULL, // device_link_changed, 227 NULL, // device_removed, 228 NULL, // device_enqueue_buffer, 229 230 notify_socket, 231 232 checksum, 233 234 init_fifo, 235 uninit_fifo, 236 fifo_enqueue_buffer, 237 fifo_dequeue_buffer, 238 clear_fifo, 239 fifo_socket_enqueue_buffer, 240 241 init_timer, 242 set_timer, 243 cancel_timer, 244 wait_for_timer, 245 is_timer_active, 246 is_timer_running, 247 248 dummy_is_syscall, 249 dummy_is_restarted_syscall, 250 dummy_store_syscall_restart_timeout, 251 NULL, // restore_syscall_restart_timeout 252 253 // ancillary data is not used by TCP 254 }; 255 256 257 // #pragma mark - socket 258 259 260 status_t 261 socket_create(int family, int type, int protocol, net_socket **_socket) 262 { 263 net_protocol* domainProtocol; 264 265 struct net_socket_private *socket = new (std::nothrow) net_socket_private; 266 if (socket == NULL) 267 return B_NO_MEMORY; 268 269 memset(socket, 0, sizeof(net_socket)); 270 socket->family = family; 271 socket->type = type; 272 socket->protocol = protocol; 273 274 mutex_init(&socket->lock, "socket"); 275 276 // set defaults (may be overridden by the protocols) 277 socket->send.buffer_size = 65535; 278 socket->send.low_water_mark = 1; 279 socket->send.timeout = B_INFINITE_TIMEOUT; 280 socket->receive.buffer_size = 65535; 281 socket->receive.low_water_mark = 1; 282 socket->receive.timeout = B_INFINITE_TIMEOUT; 283 284 list_init_etc(&socket->pending_children, offsetof(net_socket_private, link)); 285 list_init_etc(&socket->connected_children, offsetof(net_socket_private, link)); 286 287 socket->first_protocol = gTCPModule->init_protocol(socket); 288 if (socket->first_protocol == NULL) { 289 fprintf(stderr, "tcp_tester: cannot create protocol\n"); 290 mutex_destroy(&socket->lock); 291 delete socket; 292 return B_ERROR; 293 } 294 295 socket->first_info = gTCPModule; 296 297 domainProtocol = new net_protocol; 298 domainProtocol->module = &gDomainModule; 299 domainProtocol->socket = socket; 300 301 socket->first_protocol->next = domainProtocol; 302 socket->first_protocol->module = gTCPModule; 303 socket->first_protocol->socket = socket; 304 305 *_socket = socket; 306 return B_OK; 307 } 308 309 310 void 311 socket_delete(net_socket *_socket) 312 { 313 net_socket_private *socket = (net_socket_private *)_socket; 314 315 if (socket->parent != NULL) 316 panic("socket still has a parent!"); 317 318 socket->first_info->uninit_protocol(socket->first_protocol); 319 mutex_destroy(&socket->lock); 320 delete socket; 321 } 322 323 324 int 325 socket_accept(net_socket *socket, struct sockaddr *address, 326 socklen_t *_addressLength, net_socket **_acceptedSocket) 327 { 328 net_socket *accepted; 329 status_t status = socket->first_info->accept(socket->first_protocol, 330 &accepted); 331 if (status < B_OK) 332 return status; 333 334 if (address && *_addressLength > 0) { 335 memcpy(address, &accepted->peer, min_c(*_addressLength, 336 accepted->peer.ss_len)); 337 *_addressLength = accepted->peer.ss_len; 338 } 339 340 *_acceptedSocket = accepted; 341 return B_OK; 342 } 343 344 345 int 346 socket_bind(net_socket *socket, const struct sockaddr *address, socklen_t addressLength) 347 { 348 sockaddr empty; 349 if (address == NULL) { 350 // special - try to bind to an empty address, like INADDR_ANY 351 memset(&empty, 0, sizeof(sockaddr)); 352 empty.sa_len = sizeof(sockaddr); 353 empty.sa_family = socket->family; 354 355 address = ∅ 356 addressLength = sizeof(sockaddr); 357 } 358 359 if (socket->address.ss_len != 0) { 360 status_t status = socket->first_info->unbind(socket->first_protocol, 361 (sockaddr *)&socket->address); 362 if (status < B_OK) 363 return status; 364 } 365 366 memcpy(&socket->address, address, sizeof(sockaddr)); 367 368 status_t status = socket->first_info->bind(socket->first_protocol, 369 (sockaddr *)address); 370 if (status < B_OK) { 371 // clear address again, as binding failed 372 socket->address.ss_len = 0; 373 } 374 375 return status; 376 } 377 378 379 int 380 socket_connect(net_socket *socket, const struct sockaddr *address, socklen_t addressLength) 381 { 382 if (address == NULL || addressLength == 0) 383 return ENETUNREACH; 384 385 if (socket->address.ss_len == 0) { 386 // try to bind first 387 status_t status = socket_bind(socket, NULL, 0); 388 if (status < B_OK) 389 return status; 390 } 391 392 return socket->first_info->connect(socket->first_protocol, address); 393 } 394 395 396 int 397 socket_listen(net_socket *socket, int backlog) 398 { 399 status_t status = socket->first_info->listen(socket->first_protocol, backlog); 400 if (status == B_OK) 401 socket->options |= SO_ACCEPTCONN; 402 403 return status; 404 } 405 406 407 ssize_t 408 socket_send(net_socket *socket, const void *data, size_t length, int flags) 409 { 410 if (socket->peer.ss_len == 0) 411 return EDESTADDRREQ; 412 413 if (socket->address.ss_len == 0) { 414 // try to bind first 415 status_t status = socket_bind(socket, NULL, 0); 416 if (status < B_OK) 417 return status; 418 } 419 420 // TODO: useful, maybe even computed header space! 421 net_buffer *buffer = gNetBufferModule.create(256); 422 if (buffer == NULL) 423 return ENOBUFS; 424 425 // copy data into buffer 426 if (gNetBufferModule.append(buffer, data, length) < B_OK) { 427 gNetBufferModule.free(buffer); 428 return ENOBUFS; 429 } 430 431 buffer->flags = flags; 432 memcpy(buffer->source, &socket->address, socket->address.ss_len); 433 memcpy(buffer->destination, &socket->peer, socket->peer.ss_len); 434 435 status_t status = socket->first_info->send_data(socket->first_protocol, buffer); 436 if (status < B_OK) { 437 gNetBufferModule.free(buffer); 438 return status; 439 } 440 441 return length; 442 } 443 444 445 ssize_t 446 socket_recv(net_socket *socket, void *data, size_t length, int flags) 447 { 448 net_buffer *buffer; 449 ssize_t status = socket->first_info->read_data( 450 socket->first_protocol, length, flags, &buffer); 451 if (status < B_OK) 452 return status; 453 454 // if 0 bytes we're received, no buffer will be created 455 if (buffer == NULL) 456 return 0; 457 458 ssize_t bytesReceived = buffer->size; 459 gNetBufferModule.read(buffer, 0, data, bytesReceived); 460 gNetBufferModule.free(buffer); 461 462 return bytesReceived; 463 } 464 465 466 status_t 467 socket_spawn_pending(net_socket *_parent, net_socket **_socket) 468 { 469 net_socket_private *parent = (net_socket_private *)_parent; 470 471 MutexLocker locker(parent->lock); 472 473 // We actually accept more pending connections to compensate for those 474 // that never complete, and also make sure at least a single connection 475 // can always be accepted 476 if (parent->child_count > 3 * parent->max_backlog / 2) 477 return ENOBUFS; 478 479 net_socket_private *socket; 480 status_t status = socket_create(parent->family, parent->type, parent->protocol, 481 (net_socket **)&socket); 482 if (status < B_OK) 483 return status; 484 485 // inherit parent's properties 486 socket->send = parent->send; 487 socket->receive = parent->receive; 488 socket->options = parent->options & ~SO_ACCEPTCONN; 489 socket->linger = parent->linger; 490 memcpy(&socket->address, &parent->address, parent->address.ss_len); 491 memcpy(&socket->peer, &parent->peer, parent->peer.ss_len); 492 493 // add to the parent's list of pending connections 494 list_add_item(&parent->pending_children, socket); 495 socket->parent = parent; 496 parent->child_count++; 497 498 *_socket = socket; 499 return B_OK; 500 } 501 502 503 status_t 504 socket_dequeue_connected(net_socket *_parent, net_socket **_socket) 505 { 506 net_socket_private *parent = (net_socket_private *)_parent; 507 508 mutex_lock(&parent->lock); 509 510 net_socket *socket = (net_socket *)list_remove_head_item(&parent->connected_children); 511 if (socket != NULL) { 512 socket->parent = NULL; 513 parent->child_count--; 514 *_socket = socket; 515 } 516 517 mutex_unlock(&parent->lock); 518 return socket != NULL ? B_OK : B_ENTRY_NOT_FOUND; 519 } 520 521 522 ssize_t 523 socket_count_connected(net_socket *_parent) 524 { 525 net_socket_private *parent = (net_socket_private *)_parent; 526 527 MutexLocker _(parent->lock); 528 529 ssize_t count = 0; 530 void *item = NULL; 531 while ((item = list_get_next_item(&parent->connected_children, 532 item)) != NULL) { 533 count++; 534 } 535 536 return count; 537 } 538 539 540 status_t 541 socket_set_max_backlog(net_socket *_socket, uint32 backlog) 542 { 543 net_socket_private *socket = (net_socket_private *)_socket; 544 545 // we enforce an upper limit of connections waiting to be accepted 546 if (backlog > 256) 547 backlog = 256; 548 549 mutex_lock(&socket->lock); 550 551 // first remove the pending connections, then the already connected ones as needed 552 net_socket *child; 553 while (socket->child_count > backlog 554 && (child = (net_socket *)list_remove_tail_item(&socket->pending_children)) != NULL) { 555 child->parent = NULL; 556 socket->child_count--; 557 } 558 while (socket->child_count > backlog 559 && (child = (net_socket *)list_remove_tail_item(&socket->connected_children)) != NULL) { 560 child->parent = NULL; 561 socket_delete(child); 562 socket->child_count--; 563 } 564 565 socket->max_backlog = backlog; 566 mutex_unlock(&socket->lock); 567 return B_OK; 568 } 569 570 571 /*! 572 The socket has been connected. It will be moved to the connected queue 573 of its parent socket. 574 */ 575 status_t 576 socket_connected(net_socket *socket) 577 { 578 net_socket_private *parent = (net_socket_private *)socket->parent; 579 if (parent == NULL) 580 return B_BAD_VALUE; 581 582 mutex_lock(&parent->lock); 583 584 list_remove_item(&parent->pending_children, socket); 585 list_add_item(&parent->connected_children, socket); 586 587 mutex_unlock(&parent->lock); 588 return B_OK; 589 } 590 591 592 status_t 593 socket_notify(net_socket *_socket, uint8 event, int32 value) 594 { 595 net_socket_private *socket = (net_socket_private *)_socket; 596 597 mutex_lock(&socket->lock); 598 599 bool notify = true; 600 601 switch (event) { 602 case B_SELECT_READ: 603 { 604 if ((ssize_t)socket->receive.low_water_mark > value && value >= B_OK) 605 notify = false; 606 break; 607 } 608 case B_SELECT_WRITE: 609 { 610 if ((ssize_t)socket->send.low_water_mark > value && value >= B_OK) 611 notify = false; 612 break; 613 } 614 case B_SELECT_ERROR: 615 socket->error = value; 616 break; 617 } 618 619 if (notify) 620 ; 621 622 mutex_unlock(&socket->lock); 623 return B_OK; 624 } 625 626 627 net_socket_module_info gNetSocketModule = { 628 { 629 NET_SOCKET_MODULE_NAME, 630 0, 631 std_ops 632 }, 633 NULL, // open, 634 NULL, // close, 635 NULL, // free, 636 637 NULL, // readv, 638 NULL, // writev, 639 NULL, // control, 640 641 NULL, // read_avail, 642 NULL, // send_avail, 643 644 NULL, // send_data, 645 NULL, // receive_data, 646 647 NULL, // get_option, 648 NULL, // set_option, 649 NULL, // get_next_stat, 650 651 // connections 652 socket_spawn_pending, 653 socket_delete, 654 socket_dequeue_connected, 655 socket_count_connected, 656 socket_set_max_backlog, 657 socket_connected, 658 659 // notifications 660 NULL, // request_notification, 661 NULL, // cancel_notification, 662 socket_notify, 663 664 // standard socket API 665 NULL, // accept, 666 NULL, // bind, 667 NULL, // connect, 668 NULL, // getpeername, 669 NULL, // getsockname, 670 NULL, // getsockopt, 671 NULL, // listen, 672 NULL, // receive, 673 NULL, // send, 674 NULL, // setsockopt, 675 NULL, // shutdown, 676 NULL, // socketpair 677 }; 678 679 680 // #pragma mark - protocol 681 682 683 net_protocol* 684 init_protocol(net_socket** _socket) 685 { 686 net_socket *socket; 687 status_t status = socket_create(AF_INET, SOCK_STREAM, IPPROTO_TCP, &socket); 688 if (status < B_OK) 689 return NULL; 690 691 status = socket->first_info->open(socket->first_protocol); 692 if (status < B_OK) { 693 fprintf(stderr, "tcp_tester: cannot open client: %s\n", strerror(status)); 694 socket_delete(socket); 695 return NULL; 696 } 697 698 *_socket = socket; 699 return socket->first_protocol; 700 } 701 702 703 void 704 close_protocol(net_protocol* protocol) 705 { 706 gTCPModule->close(protocol); 707 if (gTCPModule->free(protocol) == B_OK) 708 gTCPModule->uninit_protocol(protocol); 709 //socket_delete(protocol->socket); 710 } 711 712 713 // #pragma mark - datalink 714 715 716 status_t 717 datalink_send_data(struct net_route *route, net_buffer *buffer) 718 { 719 struct context* context = (struct context*)route->gateway; 720 721 buffer->interface = &gInterface; 722 723 context->lock.Lock(); 724 list_add_item(&context->list, buffer); 725 context->lock.Unlock(); 726 727 release_sem(context->wait_sem); 728 return B_OK; 729 } 730 731 732 status_t 733 datalink_send_datagram(net_protocol *protocol, net_domain *domain, 734 net_buffer *buffer) 735 { 736 panic("called"); 737 return B_ERROR; 738 } 739 740 741 struct net_route * 742 get_route(struct net_domain *_domain, const struct sockaddr *address) 743 { 744 if (is_server(address)) { 745 // to the server 746 return &sServerContext.route; 747 } 748 749 return &sClientContext.route; 750 } 751 752 753 net_datalink_module_info gNetDatalinkModule = { 754 { 755 NET_DATALINK_MODULE_NAME, 756 0, 757 std_ops 758 }, 759 760 NULL, //datalink_control, 761 datalink_send_data, 762 datalink_send_datagram, 763 764 NULL, //is_local_address, 765 NULL, //datalink_get_interface, 766 NULL, //datalink_get_interface_with_address, 767 768 NULL, //add_route, 769 NULL, //remove_route, 770 get_route, 771 NULL, //put_route, 772 NULL, //register_route_info, 773 NULL, //unregister_route_info, 774 NULL, //update_route_info 775 }; 776 777 778 // #pragma mark - domain 779 780 781 status_t 782 domain_open(net_protocol *protocol) 783 { 784 return B_OK; 785 } 786 787 788 status_t 789 domain_close(net_protocol *protocol) 790 { 791 return B_OK; 792 } 793 794 795 status_t 796 domain_free(net_protocol *protocol) 797 { 798 return B_OK; 799 } 800 801 802 status_t 803 domain_connect(net_protocol *protocol, const struct sockaddr *address) 804 { 805 return B_ERROR; 806 } 807 808 809 status_t 810 domain_accept(net_protocol *protocol, struct net_socket **_acceptedSocket) 811 { 812 return EOPNOTSUPP; 813 } 814 815 816 status_t 817 domain_control(net_protocol *protocol, int level, int option, void *value, 818 size_t *_length) 819 { 820 return B_ERROR; 821 } 822 823 824 status_t 825 domain_bind(net_protocol *protocol, const struct sockaddr *address) 826 { 827 memcpy(&protocol->socket->address, address, sizeof(struct sockaddr_in)); 828 protocol->socket->address.ss_len = sizeof(struct sockaddr_in); 829 // explicitly set length, as our callers can't be trusted to 830 // always provide the correct length! 831 return B_OK; 832 } 833 834 835 status_t 836 domain_unbind(net_protocol *protocol, struct sockaddr *address) 837 { 838 return B_OK; 839 } 840 841 842 status_t 843 domain_listen(net_protocol *protocol, int count) 844 { 845 return EOPNOTSUPP; 846 } 847 848 849 status_t 850 domain_shutdown(net_protocol *protocol, int direction) 851 { 852 return EOPNOTSUPP; 853 } 854 855 856 status_t 857 domain_send_data(net_protocol *protocol, net_buffer *buffer) 858 { 859 // find route 860 struct net_route *route = get_route(&sDomain, (sockaddr *)&buffer->destination); 861 if (route == NULL) 862 return ENETUNREACH; 863 864 return datalink_send_data(route, buffer); 865 } 866 867 868 status_t 869 domain_send_routed_data(net_protocol *protocol, struct net_route *route, 870 net_buffer *buffer) 871 { 872 return datalink_send_data(route, buffer); 873 } 874 875 876 ssize_t 877 domain_send_avail(net_protocol *protocol) 878 { 879 return B_ERROR; 880 } 881 882 883 status_t 884 domain_read_data(net_protocol *protocol, size_t numBytes, uint32 flags, 885 net_buffer **_buffer) 886 { 887 return B_ERROR; 888 } 889 890 891 ssize_t 892 domain_read_avail(net_protocol *protocol) 893 { 894 return B_ERROR; 895 } 896 897 898 struct net_domain * 899 domain_get_domain(net_protocol *protocol) 900 { 901 return &sDomain; 902 } 903 904 905 size_t 906 domain_get_mtu(net_protocol *protocol, const struct sockaddr *address) 907 { 908 return 1480; 909 // 1500 ethernet - IPv4 header 910 } 911 912 913 status_t 914 domain_receive_data(net_buffer *buffer) 915 { 916 static bigtime_t lastTime = 0; 917 918 uint32 packetNumber = atomic_add(&sPacketNumber, 1); 919 920 bool drop = false; 921 if (sDropList.find(packetNumber) != sDropList.end() 922 || (sRandomDrop > 0.0 && (1.0 * rand() / RAND_MAX) > sRandomDrop)) 923 drop = true; 924 925 if (!drop && (sRoundTripTime > 0 || sRandomRoundTrip || sIncreasingRoundTrip)) { 926 bigtime_t add = 0; 927 if (sRandomRoundTrip) 928 add = (bigtime_t)(1.0 * rand() / RAND_MAX * 500000) - 250000; 929 if (sIncreasingRoundTrip) 930 sRoundTripTime += (bigtime_t)(1.0 * rand() / RAND_MAX * 150000); 931 932 snooze(sRoundTripTime / 2 + add); 933 } 934 935 if (sTCPDump) { 936 NetBufferHeaderReader<tcp_header> bufferHeader(buffer); 937 if (bufferHeader.Status() < B_OK) 938 return bufferHeader.Status(); 939 940 tcp_header &header = bufferHeader.Data(); 941 942 bigtime_t now = system_time(); 943 if (lastTime == 0) 944 lastTime = now; 945 946 printf("\33[0m% 3ld %8.6f (%8.6f) ", packetNumber, (now - sStartTime) / 1000000.0, 947 (now - lastTime) / 1000000.0); 948 lastTime = now; 949 950 if (is_server((sockaddr *)buffer->source)) 951 printf("\33[31mserver > client: "); 952 else 953 printf("client > server: "); 954 955 int32 length = buffer->size - header.HeaderLength(); 956 957 if ((header.flags & TCP_FLAG_PUSH) != 0) 958 putchar('P'); 959 if ((header.flags & TCP_FLAG_SYNCHRONIZE) != 0) 960 putchar('S'); 961 if ((header.flags & TCP_FLAG_FINISH) != 0) 962 putchar('F'); 963 if ((header.flags & TCP_FLAG_RESET) != 0) 964 putchar('R'); 965 if ((header.flags 966 & (TCP_FLAG_SYNCHRONIZE | TCP_FLAG_FINISH | TCP_FLAG_PUSH | TCP_FLAG_RESET)) == 0) 967 putchar('.'); 968 969 printf(" %lu:%lu (%lu)", header.Sequence(), header.Sequence() + length, length); 970 if ((header.flags & TCP_FLAG_ACKNOWLEDGE) != 0) 971 printf(" ack %lu", header.Acknowledge()); 972 973 printf(" win %u", header.AdvertisedWindow()); 974 975 if (header.HeaderLength() > sizeof(tcp_header)) { 976 int32 size = header.HeaderLength() - sizeof(tcp_header); 977 978 tcp_option *option; 979 uint8 optionsBuffer[1024]; 980 if (gBufferModule->direct_access(buffer, sizeof(tcp_header), 981 size, (void **)&option) != B_OK) { 982 if (size > 1024) { 983 printf("options too large to take into account (%ld bytes)\n", size); 984 size = 1024; 985 } 986 987 gBufferModule->read(buffer, sizeof(tcp_header), optionsBuffer, size); 988 option = (tcp_option *)optionsBuffer; 989 } 990 991 while (size > 0) { 992 uint32 length = 1; 993 switch (option->kind) { 994 case TCP_OPTION_END: 995 case TCP_OPTION_NOP: 996 break; 997 case TCP_OPTION_MAX_SEGMENT_SIZE: 998 printf(" <mss %u>", ntohs(option->max_segment_size)); 999 length = 4; 1000 break; 1001 case TCP_OPTION_WINDOW_SHIFT: 1002 printf(" <ws %u>", option->window_shift); 1003 length = 3; 1004 break; 1005 case TCP_OPTION_TIMESTAMP: 1006 printf(" <ts %lu:%lu>", option->timestamp.value, option->timestamp.reply); 1007 length = 10; 1008 break; 1009 1010 default: 1011 length = option->length; 1012 // make sure we don't end up in an endless loop 1013 if (length == 0) 1014 size = 0; 1015 break; 1016 } 1017 1018 size -= length; 1019 option = (tcp_option *)((uint8 *)option + length); 1020 } 1021 } 1022 1023 if (drop) 1024 printf(" <DROPPED>"); 1025 printf("\33[0m\n"); 1026 } else if (drop) 1027 printf("<**** DROPPED %ld ****>\n", packetNumber); 1028 1029 if (drop) { 1030 gNetBufferModule.free(buffer); 1031 return B_OK; 1032 } 1033 1034 return gTCPModule->receive_data(buffer); 1035 } 1036 1037 1038 status_t 1039 domain_error(uint32 code, net_buffer *data) 1040 { 1041 return B_ERROR; 1042 } 1043 1044 1045 status_t 1046 domain_error_reply(net_protocol *protocol, net_buffer *causedError, uint32 code, 1047 void *errorData) 1048 { 1049 return B_ERROR; 1050 } 1051 1052 1053 net_protocol_module_info gDomainModule = { 1054 { 1055 NULL, 1056 0, 1057 std_ops 1058 }, 1059 NET_PROTOCOL_ATOMIC_MESSAGES, 1060 1061 NULL, // init 1062 NULL, // uninit 1063 domain_open, 1064 domain_close, 1065 domain_free, 1066 domain_connect, 1067 domain_accept, 1068 domain_control, 1069 NULL, // getsockopt 1070 NULL, // setsockopt 1071 domain_bind, 1072 domain_unbind, 1073 domain_listen, 1074 domain_shutdown, 1075 domain_send_data, 1076 domain_send_routed_data, 1077 domain_send_avail, 1078 domain_read_data, 1079 domain_read_avail, 1080 domain_get_domain, 1081 domain_get_mtu, 1082 domain_receive_data, 1083 NULL, // deliver_data 1084 domain_error, 1085 domain_error_reply, 1086 NULL, // attach_ancillary_data 1087 NULL, // process_ancillary_data 1088 }; 1089 1090 1091 // #pragma mark - test 1092 1093 1094 int32 1095 receiving_thread(void* _data) 1096 { 1097 struct context* context = (struct context*)_data; 1098 struct net_buffer* reorderBuffer = NULL; 1099 1100 while (true) { 1101 status_t status; 1102 do { 1103 status = acquire_sem(context->wait_sem); 1104 } while (status == B_INTERRUPTED); 1105 1106 if (status < B_OK) 1107 break; 1108 1109 while (true) { 1110 context->lock.Lock(); 1111 net_buffer* buffer = (net_buffer*)list_remove_head_item( 1112 &context->list); 1113 context->lock.Unlock(); 1114 1115 if (buffer == NULL) 1116 break; 1117 1118 if (sSimultaneousConnect && context->server && is_syn(buffer)) { 1119 // delay getting the SYN request, and connect as well 1120 sockaddr_in address; 1121 memset(&address, 0, sizeof(address)); 1122 address.sin_family = AF_INET; 1123 address.sin_port = htons(1023); 1124 address.sin_addr.s_addr = htonl(0xc0a80001); 1125 1126 status_t status = socket_connect(gServerSocket, 1127 (struct sockaddr *)&address, sizeof(struct sockaddr)); 1128 if (status < B_OK) 1129 fprintf(stderr, "tcp_tester: simultaneous connect failed: %s\n", strerror(status)); 1130 1131 sSimultaneousConnect = false; 1132 } 1133 if (sSimultaneousClose && !context->server && is_fin(buffer)) { 1134 close_protocol(gClientSocket->first_protocol); 1135 sSimultaneousClose = false; 1136 } 1137 if ((sRandomReorder > 0.0 1138 || sReorderList.find(sPacketNumber) != sReorderList.end()) 1139 && reorderBuffer == NULL 1140 && (1.0 * rand() / RAND_MAX) > sRandomReorder) { 1141 reorderBuffer = buffer; 1142 } else { 1143 if (sDomain.module->receive_data(buffer) < B_OK) 1144 gNetBufferModule.free(buffer); 1145 1146 if (reorderBuffer != NULL) { 1147 if (sDomain.module->receive_data(reorderBuffer) < B_OK) 1148 gNetBufferModule.free(reorderBuffer); 1149 reorderBuffer = NULL; 1150 } 1151 } 1152 } 1153 } 1154 1155 return 0; 1156 } 1157 1158 1159 int32 1160 server_thread(void*) 1161 { 1162 while (true) { 1163 // main accept() loop 1164 net_socket* connectionSocket; 1165 sockaddr_in address; 1166 socklen_t size = sizeof(struct sockaddr_in); 1167 status_t status = socket_accept(gServerSocket, 1168 (struct sockaddr *)&address, &size, &connectionSocket); 1169 if (status < B_OK) { 1170 fprintf(stderr, "SERVER: accepting failed: %s\n", strerror(status)); 1171 break; 1172 } 1173 1174 printf("server: got connection from %08x\n", address.sin_addr.s_addr); 1175 1176 char buffer[1024]; 1177 ssize_t bytesRead; 1178 while ((bytesRead = socket_recv(connectionSocket, buffer, 1179 sizeof(buffer), 0)) > 0) { 1180 printf("server: received %ld bytes\n", bytesRead); 1181 1182 if (sServerActiveClose) { 1183 printf("server: active close\n"); 1184 close_protocol(connectionSocket->first_protocol); 1185 return 0; 1186 } 1187 } 1188 if (bytesRead < 0) 1189 printf("server: receiving failed: %s\n", strerror(bytesRead)); 1190 else 1191 printf("server: peer closed connection.\n"); 1192 1193 snooze(1000000); 1194 close_protocol(connectionSocket->first_protocol); 1195 } 1196 1197 return 0; 1198 } 1199 1200 1201 void 1202 setup_server() 1203 { 1204 sockaddr_in address; 1205 memset(&address, 0, sizeof(address)); 1206 address.sin_len = sizeof(sockaddr_in); 1207 address.sin_family = AF_INET; 1208 address.sin_port = htons(1024); 1209 address.sin_addr.s_addr = INADDR_ANY; 1210 1211 status_t status = socket_bind(gServerSocket, (struct sockaddr*)&address, 1212 sizeof(struct sockaddr)); 1213 if (status < B_OK) { 1214 fprintf(stderr, "tcp_tester: cannot bind server: %s\n", strerror(status)); 1215 exit(1); 1216 } 1217 status = socket_listen(gServerSocket, 40); 1218 if (status < B_OK) { 1219 fprintf(stderr, "tcp_tester: server cannot listen: %s\n", 1220 strerror(status)); 1221 exit(1); 1222 } 1223 1224 thread_id serverThread = spawn_thread(server_thread, "server", 1225 B_NORMAL_PRIORITY, NULL); 1226 if (serverThread < B_OK) { 1227 fprintf(stderr, "tcp_tester: cannot start server: %s\n", 1228 strerror(serverThread)); 1229 exit(1); 1230 } 1231 1232 resume_thread(serverThread); 1233 } 1234 1235 1236 void 1237 setup_context(struct context& context, bool server) 1238 { 1239 list_init(&context.list); 1240 context.route.interface = &gInterface; 1241 context.route.gateway = (sockaddr *)&context; 1242 // backpointer to the context 1243 context.route.mtu = 1500; 1244 context.server = server; 1245 context.wait_sem = create_sem(0, "receive wait"); 1246 1247 context.thread = spawn_thread(receiving_thread, 1248 server ? "server receiver" : "client receiver", B_NORMAL_PRIORITY, 1249 &context); 1250 resume_thread(context.thread); 1251 } 1252 1253 1254 void 1255 cleanup_context(struct context& context) 1256 { 1257 delete_sem(context.wait_sem); 1258 1259 status_t status; 1260 wait_for_thread(context.thread, &status); 1261 } 1262 1263 1264 // #pragma mark - 1265 1266 1267 static void do_help(int argc, char** argv); 1268 1269 1270 static void 1271 do_connect(int argc, char** argv) 1272 { 1273 sSimultaneousConnect = false; 1274 1275 int port = 1024; 1276 if (argc > 1) { 1277 if (!strcmp(argv[1], "-s")) 1278 sSimultaneousConnect = true; 1279 else if (isdigit(argv[1][0])) 1280 port = atoi(argv[1]); 1281 else { 1282 fprintf(stderr, "usage: connect [-s|<port-number>]\n"); 1283 return; 1284 } 1285 } 1286 1287 if (sSimultaneousConnect) { 1288 // bind to a port first, so the other end can find us 1289 sockaddr_in address; 1290 memset(&address, 0, sizeof(address)); 1291 address.sin_family = AF_INET; 1292 address.sin_port = htons(1023); 1293 address.sin_addr.s_addr = htonl(0xc0a80001); 1294 1295 status_t status = socket_bind(gClientSocket, (struct sockaddr *)&address, 1296 sizeof(struct sockaddr)); 1297 if (status < B_OK) { 1298 fprintf(stderr, "Could not bind to port 1023: %s\n", strerror(status)); 1299 sSimultaneousConnect = false; 1300 return; 1301 } 1302 } 1303 1304 sStartTime = system_time(); 1305 1306 sockaddr_in address; 1307 memset(&address, 0, sizeof(address)); 1308 address.sin_family = AF_INET; 1309 address.sin_port = htons(port); 1310 address.sin_addr.s_addr = htonl(0xc0a80001); 1311 1312 status_t status = socket_connect(gClientSocket, (struct sockaddr *)&address, 1313 sizeof(struct sockaddr)); 1314 if (status < B_OK) 1315 fprintf(stderr, "tcp_tester: could not connect: %s\n", strerror(status)); 1316 } 1317 1318 1319 static void 1320 do_send(int argc, char** argv) 1321 { 1322 size_t size = 1024; 1323 if (argc > 1 && isdigit(argv[1][0])) { 1324 char *unit; 1325 size = strtoul(argv[1], &unit, 0); 1326 if (unit != NULL && unit[0]) { 1327 if (unit[0] == 'k' || unit[0] == 'K') 1328 size *= 1024; 1329 else if (unit[0] == 'm' || unit[0] == 'M') 1330 size *= 1024 * 1024; 1331 else { 1332 fprintf(stderr, "unknown unit specified!\n"); 1333 return; 1334 } 1335 } 1336 } else if (argc > 1) { 1337 fprintf(stderr, "invalid args!\n"); 1338 return; 1339 } 1340 1341 if (size > 4 * 1024 * 1024) { 1342 printf("amount to send will be limited to 4 MB\n"); 1343 size = 4 * 1024 * 1024; 1344 } 1345 1346 char *buffer = (char *)malloc(size); 1347 if (buffer == NULL) { 1348 fprintf(stderr, "not enough memory!\n"); 1349 return; 1350 } 1351 1352 // initialize buffer with some not so random data 1353 for (uint32 i = 0; i < size; i++) { 1354 buffer[i] = (char)(i & 0xff); 1355 } 1356 1357 ssize_t bytesWritten = socket_send(gClientSocket, buffer, size, 0); 1358 if (bytesWritten < B_OK) { 1359 fprintf(stderr, "failed sending buffer: %s\n", strerror(bytesWritten)); 1360 return; 1361 } 1362 } 1363 1364 1365 static void 1366 do_close(int argc, char** argv) 1367 { 1368 sSimultaneousClose = false; 1369 sServerActiveClose = true; 1370 1371 if (argc > 1) { 1372 if (!strcmp(argv[1], "-s")) 1373 sSimultaneousClose = true; 1374 else { 1375 fprintf(stderr, "usage: close [-s]\n"); 1376 return; 1377 } 1378 } 1379 1380 gClientSocket->send.timeout = 0; 1381 1382 char buffer[32767] = {'q'}; 1383 ssize_t bytesWritten = socket_send(gClientSocket, buffer, sizeof(buffer), 0); 1384 if (bytesWritten < B_OK) { 1385 fprintf(stderr, "failed sending buffer: %s\n", strerror(bytesWritten)); 1386 return; 1387 } 1388 } 1389 1390 1391 static void 1392 do_drop(int argc, char** argv) 1393 { 1394 if (argc == 1) { 1395 // show list of dropped packets 1396 if (sRandomDrop > 0.0) 1397 printf("Drop probability is %f\n", sRandomDrop); 1398 1399 printf("Drop pakets:\n"); 1400 1401 set<uint32>::iterator iterator = sDropList.begin(); 1402 uint32 count = 0; 1403 for (; iterator != sDropList.end(); iterator++) { 1404 printf("%4lu\n", *iterator); 1405 count++; 1406 } 1407 1408 if (count == 0) 1409 printf("<empty>\n"); 1410 } else if (!strcmp(argv[1], "-f")) { 1411 // flush drop list 1412 sDropList.clear(); 1413 puts("drop list cleared."); 1414 } else if (!strcmp(argv[1], "-r")) { 1415 if (argc < 3) { 1416 fprintf(stderr, "No drop probability specified.\n"); 1417 return; 1418 } 1419 1420 sRandomDrop = atof(argv[2]); 1421 if (sRandomDrop < 0.0) 1422 sRandomDrop = 0; 1423 else if (sRandomDrop > 1.0) 1424 sRandomDrop = 1.0; 1425 } else if (isdigit(argv[1][0])) { 1426 // add to drop list 1427 for (int i = 1; i < argc; i++) { 1428 uint32 packet = strtoul(argv[i], NULL, 0); 1429 if (packet == 0) { 1430 fprintf(stderr, "invalid packet number: %s\n", argv[i]); 1431 break; 1432 } 1433 1434 sDropList.insert(packet); 1435 } 1436 } else { 1437 // print usage 1438 puts("usage: drop <packet-number> [...]\n" 1439 " or: drop -r <probability>\n\n" 1440 " or: drop [-f]\n\n" 1441 "Specifiying -f flushes the drop list, -r sets the probability a packet\n" 1442 "is dropped; if you called drop without any arguments, the current\n" 1443 "drop list is dumped."); 1444 } 1445 } 1446 1447 1448 static void 1449 do_reorder(int argc, char** argv) 1450 { 1451 if (argc == 1) { 1452 // show list of dropped packets 1453 if (sRandomReorder > 0.0) 1454 printf("Reorder probability is %f\n", sRandomReorder); 1455 1456 printf("Reorder packets:\n"); 1457 1458 set<uint32>::iterator iterator = sReorderList.begin(); 1459 uint32 count = 0; 1460 for (; iterator != sReorderList.end(); iterator++) { 1461 printf("%4lu\n", *iterator); 1462 count++; 1463 } 1464 1465 if (count == 0) 1466 printf("<empty>\n"); 1467 } else if (!strcmp(argv[1], "-f")) { 1468 // flush reorder list 1469 sReorderList.clear(); 1470 puts("reorder list cleared."); 1471 } else if (!strcmp(argv[1], "-r")) { 1472 if (argc < 3) { 1473 fprintf(stderr, "No reorder probability specified.\n"); 1474 return; 1475 } 1476 1477 sRandomReorder = atof(argv[2]); 1478 if (sRandomReorder < 0.0) 1479 sRandomReorder = 0; 1480 else if (sRandomReorder > 1.0) 1481 sRandomReorder = 1.0; 1482 } else if (isdigit(argv[1][0])) { 1483 // add to reorder list 1484 for (int i = 1; i < argc; i++) { 1485 uint32 packet = strtoul(argv[i], NULL, 0); 1486 if (packet == 0) { 1487 fprintf(stderr, "invalid packet number: %s\n", argv[i]); 1488 break; 1489 } 1490 1491 sReorderList.insert(packet); 1492 } 1493 } else { 1494 // print usage 1495 puts("usage: reorder <packet-number> [...]\n" 1496 " or: reorder -r <probability>\n\n" 1497 " or: reorder [-f]\n\n" 1498 "Specifiying -f flushes the reorder list, -r sets the probability a packet\n" 1499 "is reordered; if you called reorder without any arguments, the current\n" 1500 "reorder list is dumped."); 1501 } 1502 } 1503 1504 1505 static void 1506 do_round_trip_time(int argc, char** argv) 1507 { 1508 if (argc == 1) { 1509 // show current time 1510 printf("Current round trip time: %g ms\n", sRoundTripTime / 1000.0); 1511 } else if (!strcmp(argv[1], "-r")) { 1512 // toggle random time 1513 sRandomRoundTrip = !sRandomRoundTrip; 1514 printf("Round trip time is now %s.\n", sRandomRoundTrip ? "random" : "fixed"); 1515 } else if (!strcmp(argv[1], "-i")) { 1516 // toggle increasing time 1517 sIncreasingRoundTrip = !sIncreasingRoundTrip; 1518 printf("Round trip time is now %s.\n", sIncreasingRoundTrip ? "increasing" : "fixed"); 1519 } else if (isdigit(argv[1][0])) { 1520 // set time 1521 sRoundTripTime = 1000LL * strtoul(argv[1], NULL, 0); 1522 } else { 1523 // print usage 1524 puts("usage: rtt <time in ms>\n" 1525 " or: rtt [-r|-i]\n\n" 1526 "Specifiying -r sets random time, -i causes the times to increase over time;\n" 1527 "witout any arguments, the current time is printed."); 1528 } 1529 } 1530 1531 1532 static void 1533 do_dprintf(int argc, char** argv) 1534 { 1535 if (argc > 1) 1536 gDebugOutputEnabled = !strcmp(argv[1], "on"); 1537 else 1538 gDebugOutputEnabled = !gDebugOutputEnabled; 1539 1540 printf("debug output turned %s.\n", gDebugOutputEnabled ? "on" : "off"); 1541 } 1542 1543 1544 static cmd_entry sBuiltinCommands[] = { 1545 {"connect", do_connect, "Connects the client"}, 1546 {"send", do_send, "Sends data from the client to the server"}, 1547 {"close", do_close, "Performs an active or simultaneous close"}, 1548 {"dprintf", do_dprintf, "Toggles debug output"}, 1549 {"drop", do_drop, "Lets you drop packets during transfer"}, 1550 {"reorder", do_reorder, "Lets you reorder packets during transfer"}, 1551 {"help", do_help, "prints this help text"}, 1552 {"rtt", do_round_trip_time, "Specifies the round trip time"}, 1553 {"quit", NULL, "exits the application"}, 1554 {NULL, NULL, NULL}, 1555 }; 1556 1557 1558 static void 1559 do_help(int argc, char** argv) 1560 { 1561 printf("Available commands:\n"); 1562 1563 for (cmd_entry* command = sBuiltinCommands; command->name != NULL; command++) { 1564 printf("%8s - %s\n", command->name, command->help); 1565 } 1566 } 1567 1568 1569 // #pragma mark - 1570 1571 1572 int 1573 main(int argc, char** argv) 1574 { 1575 status_t status = init_timers(); 1576 if (status < B_OK) { 1577 fprintf(stderr, "tcp_tester: Could not initialize timers: %s\n", 1578 strerror(status)); 1579 return 1; 1580 } 1581 1582 _add_builtin_module((module_info*)&gNetStackModule); 1583 _add_builtin_module((module_info*)&gNetBufferModule); 1584 _add_builtin_module((module_info*)&gNetSocketModule); 1585 _add_builtin_module((module_info*)&gNetDatalinkModule); 1586 _add_builtin_module(modules[0]); 1587 if (_get_builtin_dependencies() < B_OK) { 1588 fprintf(stderr, "tcp_tester: Could not initialize modules: %s\n", 1589 strerror(status)); 1590 return 1; 1591 } 1592 1593 sockaddr_in interfaceAddress; 1594 interfaceAddress.sin_len = sizeof(sockaddr_in); 1595 interfaceAddress.sin_family = AF_INET; 1596 interfaceAddress.sin_addr.s_addr = htonl(0xc0a80001); 1597 gInterface.address = (sockaddr*)&interfaceAddress; 1598 gInterface.domain = &sDomain; 1599 1600 status = get_module("network/protocols/tcp/v1", (module_info **)&gTCPModule); 1601 if (status < B_OK) { 1602 fprintf(stderr, "tcp_tester: Could not open TCP module: %s\n", 1603 strerror(status)); 1604 return 1; 1605 } 1606 1607 net_protocol* client = init_protocol(&gClientSocket); 1608 if (client == NULL) 1609 return 1; 1610 net_protocol* server = init_protocol(&gServerSocket); 1611 if (server == NULL) 1612 return 1; 1613 1614 setup_context(sClientContext, false); 1615 setup_context(sServerContext, true); 1616 1617 printf("*** Server: %p (%ld), Client: %p (%ld)\n", server, 1618 sServerContext.thread, client, sClientContext.thread); 1619 1620 setup_server(); 1621 1622 while (true) { 1623 printf("> "); 1624 fflush(stdout); 1625 1626 char line[1024]; 1627 if (fgets(line, sizeof(line), stdin) == NULL) 1628 break; 1629 1630 argc = 0; 1631 argv = build_argv(line, &argc); 1632 if (argv == NULL || argc == 0) 1633 continue; 1634 1635 int length = strlen(argv[0]); 1636 1637 #if 0 1638 char *newLine = strchr(line, '\n'); 1639 if (newLine != NULL) 1640 newLine[0] = '\0'; 1641 #endif 1642 1643 if (!strcmp(argv[0], "quit") 1644 || !strcmp(argv[0], "exit") 1645 || !strcmp(argv[0], "q")) 1646 break; 1647 1648 bool found = false; 1649 1650 for (cmd_entry* command = sBuiltinCommands; command->name != NULL; command++) { 1651 if (!strncmp(command->name, argv[0], length)) { 1652 command->func(argc, argv); 1653 found = true; 1654 break; 1655 } 1656 } 1657 1658 if (!found) 1659 fprintf(stderr, "Unknown command \"%s\". Type \"help\" for a list of commands.\n", argv[0]); 1660 1661 free(argv); 1662 } 1663 1664 close_protocol(client); 1665 close_protocol(server); 1666 1667 snooze(2000000); 1668 1669 cleanup_context(sClientContext); 1670 cleanup_context(sServerContext); 1671 1672 put_module("network/protocols/tcp/v1"); 1673 uninit_timers(); 1674 return 0; 1675 } 1676