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