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