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