xref: /haiku/src/add-ons/kernel/network/protocols/tcp/tcp.cpp (revision 4f2fd49bdc6078128b1391191e4edac647044c3d)
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