xref: /haiku/src/add-ons/kernel/network/protocols/tcp/tcp.cpp (revision b2c7de82305294ddf7dd438eecf63f281ef33eba)
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)
290 		sEndpointManagers.Add(endpointManager);
291 
292 	return endpointManager;
293 }
294 
295 
296 void
297 put_endpoint_manager(EndpointManager* endpointManager)
298 {
299 	// TODO: when the connection and endpoint count reach zero
300 	// we should remove the endpoint manager from the endpoints
301 	// list and delete it.
302 }
303 
304 
305 const char*
306 name_for_state(tcp_state state)
307 {
308 	switch (state) {
309 		case CLOSED:
310 			return "closed";
311 		case LISTEN:
312 			return "listen";
313 		case SYNCHRONIZE_SENT:
314 			return "syn-sent";
315 		case SYNCHRONIZE_RECEIVED:
316 			return "syn-received";
317 		case ESTABLISHED:
318 			return "established";
319 
320 		// peer closes the connection
321 		case FINISH_RECEIVED:
322 			return "close-wait";
323 		case WAIT_FOR_FINISH_ACKNOWLEDGE:
324 			return "last-ack";
325 
326 		// we close the connection
327 		case FINISH_SENT:
328 			return "fin-wait1";
329 		case FINISH_ACKNOWLEDGED:
330 			return "fin-wait2";
331 		case CLOSING:
332 			return "closing";
333 
334 		case TIME_WAIT:
335 			return "time-wait";
336 	}
337 
338 	return "-";
339 }
340 
341 
342 /*!	Constructs a TCP header on \a buffer with the specified values
343 	for \a flags, \a seq \a ack and \a advertisedWindow.
344 */
345 status_t
346 add_tcp_header(net_address_module_info* addressModule,
347 	tcp_segment_header& segment, net_buffer* buffer)
348 {
349 	buffer->protocol = IPPROTO_TCP;
350 
351 	uint8 optionsBuffer[kMaxOptionSize];
352 	uint32 optionsLength = add_options(segment, optionsBuffer,
353 		sizeof(optionsBuffer));
354 
355 	NetBufferPrepend<tcp_header> bufferHeader(buffer,
356 		sizeof(tcp_header) + optionsLength);
357 	if (bufferHeader.Status() != B_OK)
358 		return bufferHeader.Status();
359 
360 	tcp_header& header = bufferHeader.Data();
361 
362 	header.source_port = addressModule->get_port(buffer->source);
363 	header.destination_port = addressModule->get_port(buffer->destination);
364 	header.sequence = htonl(segment.sequence);
365 	header.acknowledge = (segment.flags & TCP_FLAG_ACKNOWLEDGE)
366 		? htonl(segment.acknowledge) : 0;
367 	header.reserved = 0;
368 	header.header_length = (sizeof(tcp_header) + optionsLength) >> 2;
369 	header.flags = segment.flags;
370 	header.advertised_window = htons(segment.advertised_window);
371 	header.checksum = 0;
372 	header.urgent_offset = htons(segment.urgent_offset);
373 
374 	// we must detach before calculating the checksum as we may
375 	// not have a contiguous buffer.
376 	bufferHeader.Sync();
377 
378 	if (optionsLength > 0) {
379 		gBufferModule->write(buffer, sizeof(tcp_header), optionsBuffer,
380 			optionsLength);
381 	}
382 
383 	TRACE(("add_tcp_header(): buffer %p, flags 0x%x, seq %lu, ack %lu, up %lu, "
384 		"win %u\n", buffer, segment.flags, segment.sequence,
385 		segment.acknowledge, segment.urgent_offset, segment.advertised_window));
386 
387 	*TCPChecksumField(buffer) = Checksum::PseudoHeader(addressModule,
388 		gBufferModule, buffer, IPPROTO_TCP);
389 
390 	return B_OK;
391 }
392 
393 
394 size_t
395 tcp_options_length(tcp_segment_header& segment)
396 {
397 	size_t length = 0;
398 
399 	if (segment.max_segment_size > 0)
400 		length += 4;
401 
402 	if (segment.options & TCP_HAS_TIMESTAMPS)
403 		length += 12;
404 
405 	if (segment.options & TCP_HAS_WINDOW_SCALE)
406 		length += 4;
407 
408 	if (segment.options & TCP_SACK_PERMITTED)
409 		length += 2;
410 
411 	if (segment.sack_count > 0) {
412 		int sackCount = min_c((int)((kMaxOptionSize - length - 4)
413 			/ sizeof(tcp_sack)), segment.sack_count);
414 		if (sackCount > 0)
415 			length += 4 + sackCount * sizeof(tcp_sack);
416 	}
417 
418 	if ((length & 3) == 0)
419 		return length;
420 
421 	return (length + 3) & ~3;
422 }
423 
424 
425 //	#pragma mark - protocol API
426 
427 
428 net_protocol*
429 tcp_init_protocol(net_socket* socket)
430 {
431 	socket->send.buffer_size = 32768;
432 		// override net_socket default
433 
434 	TCPEndpoint* protocol = new (std::nothrow) TCPEndpoint(socket);
435 	if (protocol == NULL)
436 		return NULL;
437 
438 	if (protocol->InitCheck() != B_OK) {
439 		delete protocol;
440 		return NULL;
441 	}
442 
443 	TRACE(("Creating new TCPEndpoint: %p\n", protocol));
444 	socket->protocol = IPPROTO_TCP;
445 	return protocol;
446 }
447 
448 
449 status_t
450 tcp_uninit_protocol(net_protocol* protocol)
451 {
452 	TRACE(("Deleting TCPEndpoint: %p\n", protocol));
453 	delete (TCPEndpoint*)protocol;
454 	return B_OK;
455 }
456 
457 
458 status_t
459 tcp_open(net_protocol* protocol)
460 {
461 	return ((TCPEndpoint*)protocol)->Open();
462 }
463 
464 
465 status_t
466 tcp_close(net_protocol* protocol)
467 {
468 	return ((TCPEndpoint*)protocol)->Close();
469 }
470 
471 
472 status_t
473 tcp_free(net_protocol* protocol)
474 {
475 	return ((TCPEndpoint*)protocol)->Free();
476 }
477 
478 
479 status_t
480 tcp_connect(net_protocol* protocol, const struct sockaddr* address)
481 {
482 	return ((TCPEndpoint*)protocol)->Connect(address);
483 }
484 
485 
486 status_t
487 tcp_accept(net_protocol* protocol, struct net_socket** _acceptedSocket)
488 {
489 	return ((TCPEndpoint*)protocol)->Accept(_acceptedSocket);
490 }
491 
492 
493 status_t
494 tcp_control(net_protocol* _protocol, int level, int option, void* value,
495 	size_t* _length)
496 {
497 	TCPEndpoint* protocol = (TCPEndpoint*)_protocol;
498 
499 	if ((level & LEVEL_MASK) == IPPROTO_TCP) {
500 		if (option == NET_STAT_SOCKET)
501 			return protocol->FillStat((net_stat*)value);
502 	}
503 
504 	return protocol->next->module->control(protocol->next, level, option,
505 		value, _length);
506 }
507 
508 
509 status_t
510 tcp_getsockopt(net_protocol* _protocol, int level, int option, void* value,
511 	int* _length)
512 {
513 	TCPEndpoint* protocol = (TCPEndpoint*)_protocol;
514 
515 	if (level == IPPROTO_TCP)
516 		return protocol->GetOption(option, value, _length);
517 
518 	return protocol->next->module->getsockopt(protocol->next, level, option,
519 		value, _length);
520 }
521 
522 
523 status_t
524 tcp_setsockopt(net_protocol* _protocol, int level, int option,
525 	const void* _value, int length)
526 {
527 	TCPEndpoint* protocol = (TCPEndpoint*)_protocol;
528 
529 	if (level == SOL_SOCKET) {
530 		if (option == SO_SNDBUF || option == SO_RCVBUF) {
531 			if (length != sizeof(int))
532 				return B_BAD_VALUE;
533 
534 			status_t status;
535 			const int* value = (const int*)_value;
536 
537 			if (option == SO_SNDBUF)
538 				status = protocol->SetSendBufferSize(*value);
539 			else
540 				status = protocol->SetReceiveBufferSize(*value);
541 
542 			if (status < B_OK)
543 				return status;
544 		}
545 	} else if (level == IPPROTO_TCP)
546 		return protocol->SetOption(option, _value, length);
547 
548 	return protocol->next->module->setsockopt(protocol->next, level, option,
549 		_value, length);
550 }
551 
552 
553 status_t
554 tcp_bind(net_protocol* protocol, const struct sockaddr* address)
555 {
556 	return ((TCPEndpoint*)protocol)->Bind(address);
557 }
558 
559 
560 status_t
561 tcp_unbind(net_protocol* protocol, struct sockaddr* address)
562 {
563 	return ((TCPEndpoint*)protocol)->Unbind(address);
564 }
565 
566 
567 status_t
568 tcp_listen(net_protocol* protocol, int count)
569 {
570 	return ((TCPEndpoint*)protocol)->Listen(count);
571 }
572 
573 
574 status_t
575 tcp_shutdown(net_protocol* protocol, int direction)
576 {
577 	return ((TCPEndpoint*)protocol)->Shutdown(direction);
578 }
579 
580 
581 status_t
582 tcp_send_data(net_protocol* protocol, net_buffer* buffer)
583 {
584 	return ((TCPEndpoint*)protocol)->SendData(buffer);
585 }
586 
587 
588 status_t
589 tcp_send_routed_data(net_protocol* protocol, struct net_route* route,
590 	net_buffer* buffer)
591 {
592 	// TCP never sends routed data
593 	return B_ERROR;
594 }
595 
596 
597 ssize_t
598 tcp_send_avail(net_protocol* protocol)
599 {
600 	return ((TCPEndpoint*)protocol)->SendAvailable();
601 }
602 
603 
604 status_t
605 tcp_read_data(net_protocol* protocol, size_t numBytes, uint32 flags,
606 	net_buffer** _buffer)
607 {
608 	return ((TCPEndpoint*)protocol)->ReadData(numBytes, flags, _buffer);
609 }
610 
611 
612 ssize_t
613 tcp_read_avail(net_protocol* protocol)
614 {
615 	return ((TCPEndpoint*)protocol)->ReadAvailable();
616 }
617 
618 
619 struct net_domain*
620 tcp_get_domain(net_protocol* protocol)
621 {
622 	return protocol->next->module->get_domain(protocol->next);
623 }
624 
625 
626 size_t
627 tcp_get_mtu(net_protocol* protocol, const struct sockaddr* address)
628 {
629 	return protocol->next->module->get_mtu(protocol->next, address);
630 }
631 
632 
633 status_t
634 tcp_receive_data(net_buffer* buffer)
635 {
636 	TRACE(("TCP: Received buffer %p\n", buffer));
637 
638 	if (buffer->interface == NULL || buffer->interface->domain == NULL)
639 		return B_ERROR;
640 
641 	net_domain* domain = buffer->interface->domain;
642 	net_address_module_info* addressModule = domain->address_module;
643 
644 	NetBufferHeaderReader<tcp_header> bufferHeader(buffer);
645 	if (bufferHeader.Status() < B_OK)
646 		return bufferHeader.Status();
647 
648 	tcp_header& header = bufferHeader.Data();
649 
650 	uint16 headerLength = header.HeaderLength();
651 	if (headerLength < sizeof(tcp_header))
652 		return B_BAD_DATA;
653 
654 	if (Checksum::PseudoHeader(addressModule, gBufferModule, buffer,
655 			IPPROTO_TCP) != 0)
656 		return B_BAD_DATA;
657 
658 	addressModule->set_port(buffer->source, header.source_port);
659 	addressModule->set_port(buffer->destination, header.destination_port);
660 
661 	TRACE(("  Looking for: peer %s, local %s\n",
662 		AddressString(domain, buffer->source, true).Data(),
663 		AddressString(domain, buffer->destination, true).Data()));
664 	//dump_tcp_header(header);
665 	//gBufferModule->dump(buffer);
666 
667 	tcp_segment_header segment(header.flags);
668 	segment.sequence = header.Sequence();
669 	segment.acknowledge = header.Acknowledge();
670 	segment.advertised_window = header.AdvertisedWindow();
671 	segment.urgent_offset = header.UrgentOffset();
672 	process_options(segment, buffer, headerLength - sizeof(tcp_header));
673 
674 	bufferHeader.Remove(headerLength);
675 		// we no longer need to keep the header around
676 
677 	MutexLocker _(sEndpointManagersLock);
678 
679 	EndpointManager* endpointManager = endpoint_manager_for(domain);
680 	if (endpointManager == NULL)
681 		return B_ERROR;
682 
683 	int32 segmentAction = DROP;
684 
685 	TCPEndpoint* endpoint = endpointManager->FindConnection(
686 		buffer->destination, buffer->source);
687 	if (endpoint != NULL)
688 		segmentAction = endpoint->SegmentReceived(segment, buffer);
689 	else if ((segment.flags & TCP_FLAG_RESET) == 0)
690 		segmentAction = DROP | RESET;
691 
692 	if (segmentAction & RESET) {
693 		// send reset
694 		endpointManager->ReplyWithReset(segment, buffer);
695 	}
696 	if (segmentAction & DROP)
697 		gBufferModule->free(buffer);
698 
699 	return B_OK;
700 }
701 
702 
703 status_t
704 tcp_error(uint32 code, net_buffer* data)
705 {
706 	return B_ERROR;
707 }
708 
709 
710 status_t
711 tcp_error_reply(net_protocol* protocol, net_buffer* causedError, uint32 code,
712 	void* errorData)
713 {
714 	return B_ERROR;
715 }
716 
717 
718 //	#pragma mark -
719 
720 
721 static status_t
722 tcp_init()
723 {
724 	mutex_init(&sEndpointManagersLock, "endpoint managers lock");
725 
726 	status_t status = gStackModule->register_domain_protocols(AF_INET,
727 		SOCK_STREAM, 0,
728 		"network/protocols/tcp/v1",
729 		"network/protocols/ipv4/v1",
730 		NULL);
731 	if (status < B_OK)
732 		return status;
733 
734 	status = gStackModule->register_domain_protocols(AF_INET, SOCK_STREAM,
735 		IPPROTO_TCP,
736 		"network/protocols/tcp/v1",
737 		"network/protocols/ipv4/v1",
738 		NULL);
739 	if (status < B_OK)
740 		return status;
741 
742 	status = gStackModule->register_domain_receiving_protocol(AF_INET,
743 		IPPROTO_TCP, "network/protocols/tcp/v1");
744 	if (status < B_OK)
745 		return status;
746 
747 	add_debugger_command("tcp_endpoints", dump_endpoints,
748 		"lists all open TCP endpoints");
749 	add_debugger_command("tcp_endpoint", dump_endpoint,
750 		"dumps a TCP endpoint internal state");
751 
752 	return B_OK;
753 }
754 
755 
756 static status_t
757 tcp_uninit()
758 {
759 	remove_debugger_command("tcp_endpoint", dump_endpoint);
760 	remove_debugger_command("tcp_endpoints", dump_endpoints);
761 	mutex_destroy(&sEndpointManagersLock);
762 	return B_OK;
763 }
764 
765 
766 static status_t
767 tcp_std_ops(int32 op, ...)
768 {
769 	switch (op) {
770 		case B_MODULE_INIT:
771 			return tcp_init();
772 
773 		case B_MODULE_UNINIT:
774 			return tcp_uninit();
775 
776 		default:
777 			return B_ERROR;
778 	}
779 }
780 
781 
782 net_protocol_module_info sTCPModule = {
783 	{
784 		"network/protocols/tcp/v1",
785 		0,
786 		tcp_std_ops
787 	},
788 	0,
789 
790 	tcp_init_protocol,
791 	tcp_uninit_protocol,
792 	tcp_open,
793 	tcp_close,
794 	tcp_free,
795 	tcp_connect,
796 	tcp_accept,
797 	tcp_control,
798 	tcp_getsockopt,
799 	tcp_setsockopt,
800 	tcp_bind,
801 	tcp_unbind,
802 	tcp_listen,
803 	tcp_shutdown,
804 	tcp_send_data,
805 	tcp_send_routed_data,
806 	tcp_send_avail,
807 	tcp_read_data,
808 	tcp_read_avail,
809 	tcp_get_domain,
810 	tcp_get_mtu,
811 	tcp_receive_data,
812 	NULL,
813 	tcp_error,
814 	tcp_error_reply,
815 };
816 
817 module_dependency module_dependencies[] = {
818 	{NET_STACK_MODULE_NAME, (module_info **)&gStackModule},
819 	{NET_BUFFER_MODULE_NAME, (module_info **)&gBufferModule},
820 	{NET_DATALINK_MODULE_NAME, (module_info **)&gDatalinkModule},
821 	{NET_SOCKET_MODULE_NAME, (module_info **)&gSocketModule},
822 	{}
823 };
824 
825 module_info *modules[] = {
826 	(module_info *)&sTCPModule,
827 	NULL
828 };
829