xref: /haiku/src/add-ons/kernel/network/protocols/tcp/tcp.cpp (revision 6e927a5fc080cb934e7584454f472cacf4c3e361)
1 /*
2  * Copyright 2006-2007, 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 	TCPEndpoint *protocol = new (std::nothrow) TCPEndpoint(socket);
425 	if (protocol == NULL)
426 		return NULL;
427 
428 	if (protocol->InitCheck() != B_OK) {
429 		delete protocol;
430 		return NULL;
431 	}
432 
433 	TRACE(("Creating new TCPEndpoint: %p\n", protocol));
434 	socket->protocol = IPPROTO_TCP;
435 	return protocol;
436 }
437 
438 
439 status_t
440 tcp_uninit_protocol(net_protocol *protocol)
441 {
442 	TRACE(("Deleting TCPEndpoint: %p\n", protocol));
443 	delete (TCPEndpoint *)protocol;
444 	return B_OK;
445 }
446 
447 
448 status_t
449 tcp_open(net_protocol *protocol)
450 {
451 	return ((TCPEndpoint *)protocol)->Open();
452 }
453 
454 
455 status_t
456 tcp_close(net_protocol *protocol)
457 {
458 	return ((TCPEndpoint *)protocol)->Close();
459 }
460 
461 
462 status_t
463 tcp_free(net_protocol *protocol)
464 {
465 	return ((TCPEndpoint *)protocol)->Free();
466 }
467 
468 
469 status_t
470 tcp_connect(net_protocol *protocol, const struct sockaddr *address)
471 {
472 	return ((TCPEndpoint *)protocol)->Connect(address);
473 }
474 
475 
476 status_t
477 tcp_accept(net_protocol *protocol, struct net_socket **_acceptedSocket)
478 {
479 	return ((TCPEndpoint *)protocol)->Accept(_acceptedSocket);
480 }
481 
482 
483 status_t
484 tcp_control(net_protocol *_protocol, int level, int option, void *value,
485 	size_t *_length)
486 {
487 	TCPEndpoint *protocol = (TCPEndpoint *)_protocol;
488 
489 	if ((level & LEVEL_MASK) == IPPROTO_TCP) {
490 		if (option == NET_STAT_SOCKET)
491 			return protocol->FillStat((net_stat *)value);
492 	}
493 
494 	return protocol->next->module->control(protocol->next, level, option,
495 		value, _length);
496 }
497 
498 
499 status_t
500 tcp_getsockopt(net_protocol *_protocol, int level, int option, void *value,
501 	int *_length)
502 {
503 	TCPEndpoint *protocol = (TCPEndpoint *)_protocol;
504 
505 	/* TODO getting IPPROTO_TCP options is missing */
506 	return protocol->next->module->getsockopt(protocol->next, level, option,
507 		value, _length);
508 }
509 
510 
511 status_t
512 tcp_setsockopt(net_protocol *_protocol, int level, int option,
513 	const void *_value, int length)
514 {
515 	TCPEndpoint *protocol = (TCPEndpoint *)_protocol;
516 
517 	if (level == SOL_SOCKET) {
518 		if (option == SO_SNDBUF || option == SO_RCVBUF) {
519 			if (length != sizeof(int))
520 				return B_BAD_VALUE;
521 
522 			status_t status;
523 			const int *value = (const int *)_value;
524 
525 			if (option == SO_SNDBUF)
526 				status = protocol->SetSendBufferSize(*value);
527 			else
528 				status = protocol->SetReceiveBufferSize(*value);
529 
530 			if (status < B_OK)
531 				return status;
532 		}
533 	} else if (level == IPPROTO_TCP)
534 		return protocol->SetOption(option, _value, length);
535 
536 	return protocol->next->module->setsockopt(protocol->next, level, option,
537 		_value, length);
538 }
539 
540 
541 status_t
542 tcp_bind(net_protocol *protocol, const struct sockaddr *address)
543 {
544 	return ((TCPEndpoint *)protocol)->Bind(address);
545 }
546 
547 
548 status_t
549 tcp_unbind(net_protocol *protocol, struct sockaddr *address)
550 {
551 	return ((TCPEndpoint *)protocol)->Unbind(address);
552 }
553 
554 
555 status_t
556 tcp_listen(net_protocol *protocol, int count)
557 {
558 	return ((TCPEndpoint *)protocol)->Listen(count);
559 }
560 
561 
562 status_t
563 tcp_shutdown(net_protocol *protocol, int direction)
564 {
565 	return ((TCPEndpoint *)protocol)->Shutdown(direction);
566 }
567 
568 
569 status_t
570 tcp_send_data(net_protocol *protocol, net_buffer *buffer)
571 {
572 	return ((TCPEndpoint *)protocol)->SendData(buffer);
573 }
574 
575 
576 status_t
577 tcp_send_routed_data(net_protocol *protocol, struct net_route *route,
578 	net_buffer *buffer)
579 {
580 	// TCP never sends routed data
581 	return B_ERROR;
582 }
583 
584 
585 ssize_t
586 tcp_send_avail(net_protocol *protocol)
587 {
588 	return ((TCPEndpoint *)protocol)->SendAvailable();
589 }
590 
591 
592 status_t
593 tcp_read_data(net_protocol *protocol, size_t numBytes, uint32 flags,
594 	net_buffer **_buffer)
595 {
596 	return ((TCPEndpoint *)protocol)->ReadData(numBytes, flags, _buffer);
597 }
598 
599 
600 ssize_t
601 tcp_read_avail(net_protocol *protocol)
602 {
603 	return ((TCPEndpoint *)protocol)->ReadAvailable();
604 }
605 
606 
607 struct net_domain *
608 tcp_get_domain(net_protocol *protocol)
609 {
610 	return protocol->next->module->get_domain(protocol->next);
611 }
612 
613 
614 size_t
615 tcp_get_mtu(net_protocol *protocol, const struct sockaddr *address)
616 {
617 	return protocol->next->module->get_mtu(protocol->next, address);
618 }
619 
620 
621 status_t
622 tcp_receive_data(net_buffer *buffer)
623 {
624 	TRACE(("TCP: Received buffer %p\n", buffer));
625 
626 	if (buffer->interface == NULL || buffer->interface->domain == NULL)
627 		return B_ERROR;
628 
629 	net_domain *domain = buffer->interface->domain;
630 	net_address_module_info *addressModule = domain->address_module;
631 
632 	NetBufferHeaderReader<tcp_header> bufferHeader(buffer);
633 	if (bufferHeader.Status() < B_OK)
634 		return bufferHeader.Status();
635 
636 	tcp_header &header = bufferHeader.Data();
637 
638 	uint16 headerLength = header.HeaderLength();
639 	if (headerLength < sizeof(tcp_header))
640 		return B_BAD_DATA;
641 
642 	if (Checksum::PseudoHeader(addressModule, gBufferModule, buffer,
643 			IPPROTO_TCP) != 0)
644 		return B_BAD_DATA;
645 
646 	addressModule->set_port(buffer->source, header.source_port);
647 	addressModule->set_port(buffer->destination, header.destination_port);
648 
649 	TRACE(("  Looking for: peer %s, local %s\n",
650 		AddressString(domain, buffer->source, true).Data(),
651 		AddressString(domain, buffer->destination, true).Data()));
652 	//dump_tcp_header(header);
653 	//gBufferModule->dump(buffer);
654 
655 	tcp_segment_header segment(header.flags);
656 	segment.sequence = header.Sequence();
657 	segment.acknowledge = header.Acknowledge();
658 	segment.advertised_window = header.AdvertisedWindow();
659 	segment.urgent_offset = header.UrgentOffset();
660 	process_options(segment, buffer, headerLength - sizeof(tcp_header));
661 
662 	bufferHeader.Remove(headerLength);
663 		// we no longer need to keep the header around
664 
665 	MutexLocker _(sEndpointManagersLock);
666 
667 	EndpointManager *endpointManager = endpoint_manager_for(domain);
668 	if (endpointManager == NULL)
669 		return B_ERROR;
670 
671 	int32 segmentAction = DROP;
672 
673 	TCPEndpoint *endpoint = endpointManager->FindConnection(
674 		buffer->destination, buffer->source);
675 	if (endpoint != NULL)
676 		segmentAction = endpoint->SegmentReceived(segment, buffer);
677 	else if ((segment.flags & TCP_FLAG_RESET) == 0)
678 		segmentAction = DROP | RESET;
679 
680 	if (segmentAction & RESET) {
681 		// send reset
682 		endpointManager->ReplyWithReset(segment, buffer);
683 	}
684 	if (segmentAction & DROP)
685 		gBufferModule->free(buffer);
686 
687 	return B_OK;
688 }
689 
690 
691 status_t
692 tcp_error(uint32 code, net_buffer *data)
693 {
694 	return B_ERROR;
695 }
696 
697 
698 status_t
699 tcp_error_reply(net_protocol *protocol, net_buffer *causedError, uint32 code,
700 	void *errorData)
701 {
702 	return B_ERROR;
703 }
704 
705 
706 //	#pragma mark -
707 
708 
709 static status_t
710 tcp_init()
711 {
712 	status_t status = mutex_init(&sEndpointManagersLock,
713 		"endpoint managers lock");
714 
715 	if (status < B_OK)
716 		return status;
717 
718 	status = gStackModule->register_domain_protocols(AF_INET, SOCK_STREAM, 0,
719 		"network/protocols/tcp/v1",
720 		"network/protocols/ipv4/v1",
721 		NULL);
722 	if (status < B_OK)
723 		return status;
724 
725 	status = gStackModule->register_domain_protocols(AF_INET, SOCK_STREAM, IPPROTO_TCP,
726 		"network/protocols/tcp/v1",
727 		"network/protocols/ipv4/v1",
728 		NULL);
729 	if (status < B_OK)
730 		return status;
731 
732 	status = gStackModule->register_domain_receiving_protocol(AF_INET, IPPROTO_TCP,
733 		"network/protocols/tcp/v1");
734 	if (status < B_OK)
735 		return status;
736 
737 	add_debugger_command("tcp_endpoints", dump_endpoints,
738 		"lists all open TCP endpoints");
739 	add_debugger_command("tcp_endpoint", dump_endpoint,
740 		"dumps a TCP endpoint internal state");
741 
742 	return B_OK;
743 }
744 
745 
746 static status_t
747 tcp_uninit()
748 {
749 	remove_debugger_command("tcp_endpoint", dump_endpoint);
750 	remove_debugger_command("tcp_endpoints", dump_endpoints);
751 	mutex_destroy(&sEndpointManagersLock);
752 	return B_OK;
753 }
754 
755 
756 static status_t
757 tcp_std_ops(int32 op, ...)
758 {
759 	switch (op) {
760 		case B_MODULE_INIT:
761 			return tcp_init();
762 
763 		case B_MODULE_UNINIT:
764 			return tcp_uninit();
765 
766 		default:
767 			return B_ERROR;
768 	}
769 }
770 
771 
772 net_protocol_module_info sTCPModule = {
773 	{
774 		"network/protocols/tcp/v1",
775 		0,
776 		tcp_std_ops
777 	},
778 	tcp_init_protocol,
779 	tcp_uninit_protocol,
780 	tcp_open,
781 	tcp_close,
782 	tcp_free,
783 	tcp_connect,
784 	tcp_accept,
785 	tcp_control,
786 	tcp_getsockopt,
787 	tcp_setsockopt,
788 	tcp_bind,
789 	tcp_unbind,
790 	tcp_listen,
791 	tcp_shutdown,
792 	tcp_send_data,
793 	tcp_send_routed_data,
794 	tcp_send_avail,
795 	tcp_read_data,
796 	tcp_read_avail,
797 	tcp_get_domain,
798 	tcp_get_mtu,
799 	tcp_receive_data,
800 	NULL,
801 	tcp_error,
802 	tcp_error_reply,
803 };
804 
805 module_dependency module_dependencies[] = {
806 	{NET_STACK_MODULE_NAME, (module_info **)&gStackModule},
807 	{NET_BUFFER_MODULE_NAME, (module_info **)&gBufferModule},
808 	{NET_DATALINK_MODULE_NAME, (module_info **)&gDatalinkModule},
809 	{NET_SOCKET_MODULE_NAME, (module_info **)&gSocketModule},
810 	{}
811 };
812 
813 module_info *modules[] = {
814 	(module_info *)&sTCPModule,
815 	NULL
816 };
817