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 * Andrew Galante, haiku.galante@gmail.com 8 * Hugo Santos, hugosantos@gmail.com 9 */ 10 11 12 #include "EndpointManager.h" 13 #include "TCPEndpoint.h" 14 15 #include <net_protocol.h> 16 #include <net_stat.h> 17 18 #include <KernelExport.h> 19 #include <util/list.h> 20 21 #include <netinet/in.h> 22 #include <netinet/ip.h> 23 #include <new> 24 #include <stdlib.h> 25 #include <string.h> 26 27 #include <lock.h> 28 #include <util/AutoLock.h> 29 30 #include <NetBufferUtilities.h> 31 #include <NetUtilities.h> 32 33 34 //#define TRACE_TCP 35 #ifdef TRACE_TCP 36 # define TRACE(x) dprintf x 37 # define TRACE_BLOCK(x) dump_block x 38 #else 39 # define TRACE(x) 40 # define TRACE_BLOCK(x) 41 #endif 42 43 44 typedef NetBufferField<uint16, offsetof(tcp_header, checksum)> TCPChecksumField; 45 46 47 net_buffer_module_info *gBufferModule; 48 net_datalink_module_info *gDatalinkModule; 49 net_socket_module_info *gSocketModule; 50 net_stack_module_info *gStackModule; 51 52 53 // TODO we need to think of a better way to do this. It would be 54 // nice if we registered a per EndpointManager receiving 55 // protocol cookie, so we don't have to go through the list 56 // for each segment. 57 typedef DoublyLinkedList<EndpointManager> EndpointManagerList; 58 static mutex sEndpointManagersLock; 59 static EndpointManagerList sEndpointManagers; 60 61 62 // The TCP header length is at most 64 bytes. 63 static const int kMaxOptionSize = 64 - sizeof(tcp_header); 64 65 66 static EndpointManager * 67 endpoint_manager_for(net_domain *domain) 68 { 69 EndpointManagerList::Iterator iterator = sEndpointManagers.GetIterator(); 70 while (iterator.HasNext()) { 71 EndpointManager *endpointManager = iterator.Next(); 72 if (endpointManager->Domain() == domain) 73 return endpointManager; 74 } 75 76 return NULL; 77 } 78 79 80 static inline void 81 bump_option(tcp_option *&option, size_t &length) 82 { 83 if (option->kind <= TCP_OPTION_NOP) { 84 length++; 85 option = (tcp_option *)((uint8 *)option + 1); 86 } else { 87 length += option->length; 88 option = (tcp_option *)((uint8 *)option + option->length); 89 } 90 } 91 92 93 static inline size_t 94 add_options(tcp_segment_header &segment, uint8 *buffer, size_t bufferSize) 95 { 96 tcp_option *option = (tcp_option *)buffer; 97 size_t length = 0; 98 99 if (segment.max_segment_size > 0 && length + 8 <= bufferSize) { 100 option->kind = TCP_OPTION_MAX_SEGMENT_SIZE; 101 option->length = 4; 102 option->max_segment_size = htons(segment.max_segment_size); 103 bump_option(option, length); 104 } 105 106 if ((segment.options & TCP_HAS_TIMESTAMPS) 107 && length + 12 <= bufferSize) { 108 // two NOPs so the timestamps get aligned to a 4 byte boundary 109 option->kind = TCP_OPTION_NOP; 110 bump_option(option, length); 111 option->kind = TCP_OPTION_NOP; 112 bump_option(option, length); 113 option->kind = TCP_OPTION_TIMESTAMP; 114 option->length = 10; 115 option->timestamp.value = htonl(segment.timestamp_value); 116 // TSecr is opaque to us, we send it as we received it. 117 option->timestamp.reply = segment.timestamp_reply; 118 bump_option(option, length); 119 } 120 121 if ((segment.options & TCP_HAS_WINDOW_SCALE) 122 && length + 4 <= bufferSize) { 123 // insert one NOP so that the subsequent data is aligned on a 4 byte boundary 124 option->kind = TCP_OPTION_NOP; 125 bump_option(option, length); 126 127 option->kind = TCP_OPTION_WINDOW_SHIFT; 128 option->length = 3; 129 option->window_shift = segment.window_shift; 130 bump_option(option, length); 131 } 132 133 if ((segment.options & TCP_SACK_PERMITTED) 134 && length + 2 <= bufferSize) { 135 option->kind = TCP_OPTION_SACK_PERMITTED; 136 option->length = 2; 137 bump_option(option, length); 138 } 139 140 if (segment.sack_count > 0) { 141 int sackCount = ((int)(bufferSize - length) - 4) / sizeof(tcp_sack); 142 if (sackCount > segment.sack_count) 143 sackCount = segment.sack_count; 144 145 if (sackCount > 0) { 146 option->kind = TCP_OPTION_NOP; 147 bump_option(option, length); 148 option->kind = TCP_OPTION_NOP; 149 bump_option(option, length); 150 option->kind = TCP_OPTION_SACK; 151 option->length = 2 + sackCount * sizeof(tcp_sack); 152 memcpy(option->sack, segment.sacks, sackCount * sizeof(tcp_sack)); 153 bump_option(option, length); 154 } 155 } 156 157 if ((length & 3) == 0) { 158 // options completely fill out the option space 159 return length; 160 } 161 162 option->kind = TCP_OPTION_END; 163 return (length + 3) & ~3; 164 // bump to a multiple of 4 length 165 } 166 167 168 static void 169 process_options(tcp_segment_header &segment, net_buffer *buffer, size_t size) 170 { 171 if (size == 0) 172 return; 173 174 tcp_option *option; 175 176 uint8 optionsBuffer[kMaxOptionSize]; 177 if (gBufferModule->direct_access(buffer, sizeof(tcp_header), size, 178 (void **)&option) != B_OK) { 179 if (size > sizeof(optionsBuffer)) { 180 dprintf("Ignoring TCP options larger than expected.\n"); 181 return; 182 } 183 184 gBufferModule->read(buffer, sizeof(tcp_header), optionsBuffer, size); 185 option = (tcp_option *)optionsBuffer; 186 } 187 188 while (size > 0) { 189 int32 length = -1; 190 191 switch (option->kind) { 192 case TCP_OPTION_END: 193 case TCP_OPTION_NOP: 194 length = 1; 195 break; 196 case TCP_OPTION_MAX_SEGMENT_SIZE: 197 if (option->length == 4 && (size - 4) >= 0) 198 segment.max_segment_size = ntohs(option->max_segment_size); 199 break; 200 case TCP_OPTION_WINDOW_SHIFT: 201 if (option->length == 3 && (size - 3) >= 0) { 202 segment.options |= TCP_HAS_WINDOW_SCALE; 203 segment.window_shift = option->window_shift; 204 } 205 break; 206 case TCP_OPTION_TIMESTAMP: 207 if (option->length == 10 && (size - 10) >= 0) { 208 segment.options |= TCP_HAS_TIMESTAMPS; 209 segment.timestamp_value = option->timestamp.value; 210 segment.timestamp_reply = 211 ntohl(option->timestamp.reply); 212 } 213 break; 214 case TCP_OPTION_SACK_PERMITTED: 215 if (option->length == 2 && (size - 2) >= 0) 216 segment.options |= TCP_SACK_PERMITTED; 217 } 218 219 if (length < 0) { 220 length = option->length; 221 if (length == 0) 222 break; 223 } 224 225 size -= length; 226 option = (tcp_option *)((uint8 *)option + length); 227 } 228 } 229 230 231 #if 0 232 static void 233 dump_tcp_header(tcp_header &header) 234 { 235 dprintf(" source port: %u\n", ntohs(header.source_port)); 236 dprintf(" dest port: %u\n", ntohs(header.destination_port)); 237 dprintf(" sequence: %lu\n", header.Sequence()); 238 dprintf(" ack: %lu\n", header.Acknowledge()); 239 dprintf(" flags: %s%s%s%s%s%s\n", (header.flags & TCP_FLAG_FINISH) ? "FIN " : "", 240 (header.flags & TCP_FLAG_SYNCHRONIZE) ? "SYN " : "", 241 (header.flags & TCP_FLAG_RESET) ? "RST " : "", 242 (header.flags & TCP_FLAG_PUSH) ? "PUSH " : "", 243 (header.flags & TCP_FLAG_ACKNOWLEDGE) ? "ACK " : "", 244 (header.flags & TCP_FLAG_URGENT) ? "URG " : ""); 245 dprintf(" window: %u\n", header.AdvertisedWindow()); 246 dprintf(" urgent offset: %u\n", header.UrgentOffset()); 247 } 248 #endif 249 250 251 static int 252 dump_endpoints(int argc, char** argv) 253 { 254 EndpointManagerList::Iterator iterator = sEndpointManagers.GetIterator(); 255 256 while (iterator.HasNext()) 257 iterator.Next()->Dump(); 258 259 return 0; 260 } 261 262 263 static int 264 dump_endpoint(int argc, char** argv) 265 { 266 if (argc < 2) { 267 kprintf("usage: tcp_endpoint [address]\n"); 268 return 0; 269 } 270 271 TCPEndpoint* endpoint = (TCPEndpoint*)parse_expression(argv[1]); 272 endpoint->Dump(); 273 274 return 0; 275 } 276 277 278 // #pragma mark - internal API 279 280 281 EndpointManager* 282 get_endpoint_manager(net_domain* domain) 283 { 284 EndpointManager *endpointManager = endpoint_manager_for(domain); 285 if (endpointManager) 286 return endpointManager; 287 288 endpointManager = new (std::nothrow) EndpointManager(domain); 289 if (endpointManager == NULL) 290 return NULL; 291 292 if (endpointManager->Init() != B_OK) { 293 delete endpointManager; 294 return NULL; 295 } 296 297 sEndpointManagers.Add(endpointManager); 298 return endpointManager; 299 } 300 301 302 void 303 put_endpoint_manager(EndpointManager* endpointManager) 304 { 305 // TODO: when the connection and endpoint count reach zero 306 // we should remove the endpoint manager from the endpoints 307 // list and delete it. 308 } 309 310 311 const char* 312 name_for_state(tcp_state state) 313 { 314 switch (state) { 315 case CLOSED: 316 return "closed"; 317 case LISTEN: 318 return "listen"; 319 case SYNCHRONIZE_SENT: 320 return "syn-sent"; 321 case SYNCHRONIZE_RECEIVED: 322 return "syn-received"; 323 case ESTABLISHED: 324 return "established"; 325 326 // peer closes the connection 327 case FINISH_RECEIVED: 328 return "close-wait"; 329 case WAIT_FOR_FINISH_ACKNOWLEDGE: 330 return "last-ack"; 331 332 // we close the connection 333 case FINISH_SENT: 334 return "fin-wait1"; 335 case FINISH_ACKNOWLEDGED: 336 return "fin-wait2"; 337 case CLOSING: 338 return "closing"; 339 340 case TIME_WAIT: 341 return "time-wait"; 342 } 343 344 return "-"; 345 } 346 347 348 /*! Constructs a TCP header on \a buffer with the specified values 349 for \a flags, \a seq \a ack and \a advertisedWindow. 350 */ 351 status_t 352 add_tcp_header(net_address_module_info* addressModule, 353 tcp_segment_header& segment, net_buffer* buffer) 354 { 355 buffer->protocol = IPPROTO_TCP; 356 357 uint8 optionsBuffer[kMaxOptionSize]; 358 uint32 optionsLength = add_options(segment, optionsBuffer, 359 sizeof(optionsBuffer)); 360 361 NetBufferPrepend<tcp_header> bufferHeader(buffer, 362 sizeof(tcp_header) + optionsLength); 363 if (bufferHeader.Status() != B_OK) 364 return bufferHeader.Status(); 365 366 tcp_header& header = bufferHeader.Data(); 367 368 header.source_port = addressModule->get_port(buffer->source); 369 header.destination_port = addressModule->get_port(buffer->destination); 370 header.sequence = htonl(segment.sequence); 371 header.acknowledge = (segment.flags & TCP_FLAG_ACKNOWLEDGE) 372 ? htonl(segment.acknowledge) : 0; 373 header.reserved = 0; 374 header.header_length = (sizeof(tcp_header) + optionsLength) >> 2; 375 header.flags = segment.flags; 376 header.advertised_window = htons(segment.advertised_window); 377 header.checksum = 0; 378 header.urgent_offset = htons(segment.urgent_offset); 379 380 // we must detach before calculating the checksum as we may 381 // not have a contiguous buffer. 382 bufferHeader.Sync(); 383 384 if (optionsLength > 0) { 385 gBufferModule->write(buffer, sizeof(tcp_header), optionsBuffer, 386 optionsLength); 387 } 388 389 TRACE(("add_tcp_header(): buffer %p, flags 0x%x, seq %lu, ack %lu, up %lu, " 390 "win %u\n", buffer, segment.flags, segment.sequence, 391 segment.acknowledge, segment.urgent_offset, segment.advertised_window)); 392 393 *TCPChecksumField(buffer) = Checksum::PseudoHeader(addressModule, 394 gBufferModule, buffer, IPPROTO_TCP); 395 396 return B_OK; 397 } 398 399 400 size_t 401 tcp_options_length(tcp_segment_header& segment) 402 { 403 size_t length = 0; 404 405 if (segment.max_segment_size > 0) 406 length += 4; 407 408 if (segment.options & TCP_HAS_TIMESTAMPS) 409 length += 12; 410 411 if (segment.options & TCP_HAS_WINDOW_SCALE) 412 length += 4; 413 414 if (segment.options & TCP_SACK_PERMITTED) 415 length += 2; 416 417 if (segment.sack_count > 0) { 418 int sackCount = min_c((int)((kMaxOptionSize - length - 4) 419 / sizeof(tcp_sack)), segment.sack_count); 420 if (sackCount > 0) 421 length += 4 + sackCount * sizeof(tcp_sack); 422 } 423 424 if ((length & 3) == 0) 425 return length; 426 427 return (length + 3) & ~3; 428 } 429 430 431 // #pragma mark - protocol API 432 433 434 net_protocol* 435 tcp_init_protocol(net_socket* socket) 436 { 437 socket->send.buffer_size = 32768; 438 // override net_socket default 439 440 TCPEndpoint* protocol = new (std::nothrow) TCPEndpoint(socket); 441 if (protocol == NULL) 442 return NULL; 443 444 if (protocol->InitCheck() != B_OK) { 445 delete protocol; 446 return NULL; 447 } 448 449 TRACE(("Creating new TCPEndpoint: %p\n", protocol)); 450 socket->protocol = IPPROTO_TCP; 451 return protocol; 452 } 453 454 455 status_t 456 tcp_uninit_protocol(net_protocol* protocol) 457 { 458 TRACE(("Deleting TCPEndpoint: %p\n", protocol)); 459 delete (TCPEndpoint*)protocol; 460 return B_OK; 461 } 462 463 464 status_t 465 tcp_open(net_protocol* protocol) 466 { 467 return ((TCPEndpoint*)protocol)->Open(); 468 } 469 470 471 status_t 472 tcp_close(net_protocol* protocol) 473 { 474 return ((TCPEndpoint*)protocol)->Close(); 475 } 476 477 478 status_t 479 tcp_free(net_protocol* protocol) 480 { 481 return ((TCPEndpoint*)protocol)->Free(); 482 } 483 484 485 status_t 486 tcp_connect(net_protocol* protocol, const struct sockaddr* address) 487 { 488 return ((TCPEndpoint*)protocol)->Connect(address); 489 } 490 491 492 status_t 493 tcp_accept(net_protocol* protocol, struct net_socket** _acceptedSocket) 494 { 495 return ((TCPEndpoint*)protocol)->Accept(_acceptedSocket); 496 } 497 498 499 status_t 500 tcp_control(net_protocol* _protocol, int level, int option, void* value, 501 size_t* _length) 502 { 503 TCPEndpoint* protocol = (TCPEndpoint*)_protocol; 504 505 if ((level & LEVEL_MASK) == IPPROTO_TCP) { 506 if (option == NET_STAT_SOCKET) 507 return protocol->FillStat((net_stat*)value); 508 } 509 510 return protocol->next->module->control(protocol->next, level, option, 511 value, _length); 512 } 513 514 515 status_t 516 tcp_getsockopt(net_protocol* _protocol, int level, int option, void* value, 517 int* _length) 518 { 519 TCPEndpoint* protocol = (TCPEndpoint*)_protocol; 520 521 if (level == IPPROTO_TCP) 522 return protocol->GetOption(option, value, _length); 523 524 return protocol->next->module->getsockopt(protocol->next, level, option, 525 value, _length); 526 } 527 528 529 status_t 530 tcp_setsockopt(net_protocol* _protocol, int level, int option, 531 const void* _value, int length) 532 { 533 TCPEndpoint* protocol = (TCPEndpoint*)_protocol; 534 535 if (level == SOL_SOCKET) { 536 if (option == SO_SNDBUF || option == SO_RCVBUF) { 537 if (length != sizeof(int)) 538 return B_BAD_VALUE; 539 540 status_t status; 541 const int* value = (const int*)_value; 542 543 if (option == SO_SNDBUF) 544 status = protocol->SetSendBufferSize(*value); 545 else 546 status = protocol->SetReceiveBufferSize(*value); 547 548 if (status < B_OK) 549 return status; 550 } 551 } else if (level == IPPROTO_TCP) 552 return protocol->SetOption(option, _value, length); 553 554 return protocol->next->module->setsockopt(protocol->next, level, option, 555 _value, length); 556 } 557 558 559 status_t 560 tcp_bind(net_protocol* protocol, const struct sockaddr* address) 561 { 562 return ((TCPEndpoint*)protocol)->Bind(address); 563 } 564 565 566 status_t 567 tcp_unbind(net_protocol* protocol, struct sockaddr* address) 568 { 569 return ((TCPEndpoint*)protocol)->Unbind(address); 570 } 571 572 573 status_t 574 tcp_listen(net_protocol* protocol, int count) 575 { 576 return ((TCPEndpoint*)protocol)->Listen(count); 577 } 578 579 580 status_t 581 tcp_shutdown(net_protocol* protocol, int direction) 582 { 583 return ((TCPEndpoint*)protocol)->Shutdown(direction); 584 } 585 586 587 status_t 588 tcp_send_data(net_protocol* protocol, net_buffer* buffer) 589 { 590 return ((TCPEndpoint*)protocol)->SendData(buffer); 591 } 592 593 594 status_t 595 tcp_send_routed_data(net_protocol* protocol, struct net_route* route, 596 net_buffer* buffer) 597 { 598 // TCP never sends routed data 599 return B_ERROR; 600 } 601 602 603 ssize_t 604 tcp_send_avail(net_protocol* protocol) 605 { 606 return ((TCPEndpoint*)protocol)->SendAvailable(); 607 } 608 609 610 status_t 611 tcp_read_data(net_protocol* protocol, size_t numBytes, uint32 flags, 612 net_buffer** _buffer) 613 { 614 return ((TCPEndpoint*)protocol)->ReadData(numBytes, flags, _buffer); 615 } 616 617 618 ssize_t 619 tcp_read_avail(net_protocol* protocol) 620 { 621 return ((TCPEndpoint*)protocol)->ReadAvailable(); 622 } 623 624 625 struct net_domain* 626 tcp_get_domain(net_protocol* protocol) 627 { 628 return protocol->next->module->get_domain(protocol->next); 629 } 630 631 632 size_t 633 tcp_get_mtu(net_protocol* protocol, const struct sockaddr* address) 634 { 635 return protocol->next->module->get_mtu(protocol->next, address); 636 } 637 638 639 status_t 640 tcp_receive_data(net_buffer* buffer) 641 { 642 TRACE(("TCP: Received buffer %p\n", buffer)); 643 644 if (buffer->interface == NULL || buffer->interface->domain == NULL) 645 return B_ERROR; 646 647 net_domain* domain = buffer->interface->domain; 648 net_address_module_info* addressModule = domain->address_module; 649 650 NetBufferHeaderReader<tcp_header> bufferHeader(buffer); 651 if (bufferHeader.Status() < B_OK) 652 return bufferHeader.Status(); 653 654 tcp_header& header = bufferHeader.Data(); 655 656 uint16 headerLength = header.HeaderLength(); 657 if (headerLength < sizeof(tcp_header)) 658 return B_BAD_DATA; 659 660 if (Checksum::PseudoHeader(addressModule, gBufferModule, buffer, 661 IPPROTO_TCP) != 0) 662 return B_BAD_DATA; 663 664 addressModule->set_port(buffer->source, header.source_port); 665 addressModule->set_port(buffer->destination, header.destination_port); 666 667 TRACE((" Looking for: peer %s, local %s\n", 668 AddressString(domain, buffer->source, true).Data(), 669 AddressString(domain, buffer->destination, true).Data())); 670 //dump_tcp_header(header); 671 //gBufferModule->dump(buffer); 672 673 tcp_segment_header segment(header.flags); 674 segment.sequence = header.Sequence(); 675 segment.acknowledge = header.Acknowledge(); 676 segment.advertised_window = header.AdvertisedWindow(); 677 segment.urgent_offset = header.UrgentOffset(); 678 process_options(segment, buffer, headerLength - sizeof(tcp_header)); 679 680 bufferHeader.Remove(headerLength); 681 // we no longer need to keep the header around 682 683 MutexLocker _(sEndpointManagersLock); 684 685 EndpointManager* endpointManager = endpoint_manager_for(domain); 686 if (endpointManager == NULL) 687 return B_ERROR; 688 689 int32 segmentAction = DROP; 690 691 TCPEndpoint* endpoint = endpointManager->FindConnection( 692 buffer->destination, buffer->source); 693 if (endpoint != NULL) 694 segmentAction = endpoint->SegmentReceived(segment, buffer); 695 else if ((segment.flags & TCP_FLAG_RESET) == 0) 696 segmentAction = DROP | RESET; 697 698 if (segmentAction & RESET) { 699 // send reset 700 endpointManager->ReplyWithReset(segment, buffer); 701 } 702 if (segmentAction & DROP) 703 gBufferModule->free(buffer); 704 705 return B_OK; 706 } 707 708 709 status_t 710 tcp_error(uint32 code, net_buffer* data) 711 { 712 return B_ERROR; 713 } 714 715 716 status_t 717 tcp_error_reply(net_protocol* protocol, net_buffer* causedError, uint32 code, 718 void* errorData) 719 { 720 return B_ERROR; 721 } 722 723 724 // #pragma mark - 725 726 727 static status_t 728 tcp_init() 729 { 730 mutex_init(&sEndpointManagersLock, "endpoint managers lock"); 731 732 status_t status = gStackModule->register_domain_protocols(AF_INET, 733 SOCK_STREAM, 0, 734 "network/protocols/tcp/v1", 735 "network/protocols/ipv4/v1", 736 NULL); 737 if (status < B_OK) 738 return status; 739 740 status = gStackModule->register_domain_protocols(AF_INET, SOCK_STREAM, 741 IPPROTO_TCP, 742 "network/protocols/tcp/v1", 743 "network/protocols/ipv4/v1", 744 NULL); 745 if (status < B_OK) 746 return status; 747 748 status = gStackModule->register_domain_receiving_protocol(AF_INET, 749 IPPROTO_TCP, "network/protocols/tcp/v1"); 750 if (status < B_OK) 751 return status; 752 753 add_debugger_command("tcp_endpoints", dump_endpoints, 754 "lists all open TCP endpoints"); 755 add_debugger_command("tcp_endpoint", dump_endpoint, 756 "dumps a TCP endpoint internal state"); 757 758 return B_OK; 759 } 760 761 762 static status_t 763 tcp_uninit() 764 { 765 remove_debugger_command("tcp_endpoint", dump_endpoint); 766 remove_debugger_command("tcp_endpoints", dump_endpoints); 767 mutex_destroy(&sEndpointManagersLock); 768 return B_OK; 769 } 770 771 772 static status_t 773 tcp_std_ops(int32 op, ...) 774 { 775 switch (op) { 776 case B_MODULE_INIT: 777 return tcp_init(); 778 779 case B_MODULE_UNINIT: 780 return tcp_uninit(); 781 782 default: 783 return B_ERROR; 784 } 785 } 786 787 788 net_protocol_module_info sTCPModule = { 789 { 790 "network/protocols/tcp/v1", 791 0, 792 tcp_std_ops 793 }, 794 0, 795 796 tcp_init_protocol, 797 tcp_uninit_protocol, 798 tcp_open, 799 tcp_close, 800 tcp_free, 801 tcp_connect, 802 tcp_accept, 803 tcp_control, 804 tcp_getsockopt, 805 tcp_setsockopt, 806 tcp_bind, 807 tcp_unbind, 808 tcp_listen, 809 tcp_shutdown, 810 tcp_send_data, 811 tcp_send_routed_data, 812 tcp_send_avail, 813 tcp_read_data, 814 tcp_read_avail, 815 tcp_get_domain, 816 tcp_get_mtu, 817 tcp_receive_data, 818 NULL, 819 tcp_error, 820 tcp_error_reply, 821 }; 822 823 module_dependency module_dependencies[] = { 824 {NET_STACK_MODULE_NAME, (module_info **)&gStackModule}, 825 {NET_BUFFER_MODULE_NAME, (module_info **)&gBufferModule}, 826 {NET_DATALINK_MODULE_NAME, (module_info **)&gDatalinkModule}, 827 {NET_SOCKET_MODULE_NAME, (module_info **)&gSocketModule}, 828 {} 829 }; 830 831 module_info *modules[] = { 832 (module_info *)&sTCPModule, 833 NULL 834 }; 835