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