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