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