/* * Copyright 2006-2010, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: * Axel Dörfler, axeld@pinc-software.de * Andrew Galante, haiku.galante@gmail.com * Hugo Santos, hugosantos@gmail.com */ #ifndef TCP_H #define TCP_H #include #include #include #include #include #include class EndpointManager; enum tcp_state { // establishing a connection CLOSED, LISTEN, SYNCHRONIZE_SENT, SYNCHRONIZE_RECEIVED, ESTABLISHED, // peer closes the connection FINISH_RECEIVED, // close-wait WAIT_FOR_FINISH_ACKNOWLEDGE, // last-ack // we close the connection FINISH_SENT, // fin-wait-1 FINISH_ACKNOWLEDGED, // fin-wait-2 CLOSING, TIME_WAIT }; struct tcp_header { uint16 source_port; uint16 destination_port; uint32 sequence; uint32 acknowledge; struct { #if B_HOST_IS_LENDIAN == 1 uint8 reserved : 4; uint8 header_length : 4; #else uint8 header_length : 4; uint8 reserved : 4; #endif }; uint8 flags; uint16 advertised_window; uint16 checksum; uint16 urgent_offset; uint32 HeaderLength() const { return (uint32)header_length << 2; } uint32 Sequence() const { return B_BENDIAN_TO_HOST_INT32(sequence); } uint32 Acknowledge() const { return B_BENDIAN_TO_HOST_INT32(acknowledge); } uint16 AdvertisedWindow() const { return B_BENDIAN_TO_HOST_INT16(advertised_window); } uint16 Checksum() const { return B_BENDIAN_TO_HOST_INT16(checksum); } uint16 UrgentOffset() const { return B_BENDIAN_TO_HOST_INT16(urgent_offset); } } _PACKED; class tcp_sequence { public: inline tcp_sequence() {} inline tcp_sequence(uint32 sequence) : fNumber(sequence) { } inline uint32 Number() const { return fNumber; } inline tcp_sequence& operator=(tcp_sequence sequence) { fNumber = sequence.fNumber; return *this; } inline tcp_sequence& operator+=(tcp_sequence sequence) { fNumber += sequence.fNumber; return *this; } inline tcp_sequence& operator++() { fNumber++; return *this; } inline tcp_sequence operator++(int _) { fNumber++; return fNumber - 1; } private: uint32 fNumber; }; // Global tcp_sequence Operators inline bool operator>(tcp_sequence a, tcp_sequence b) { return (int32)(a.Number() - b.Number()) > 0; } inline bool operator>=(tcp_sequence a, tcp_sequence b) { return (int32)(a.Number() - b.Number()) >= 0; } inline bool operator<(tcp_sequence a, tcp_sequence b) { return (int32)(a.Number() - b.Number()) < 0; } inline bool operator<=(tcp_sequence a, tcp_sequence b) { return (int32)(a.Number() - b.Number()) <= 0; } inline tcp_sequence operator+(tcp_sequence a, tcp_sequence b) { return a.Number() + b.Number(); } inline tcp_sequence operator-(tcp_sequence a, tcp_sequence b) { return a.Number() - b.Number(); } inline bool operator!=(tcp_sequence a, tcp_sequence b) { return a.Number() != b.Number(); } inline bool operator==(tcp_sequence a, tcp_sequence b) { return a.Number() == b.Number(); } // TCP flag constants #define TCP_FLAG_FINISH 0x01 #define TCP_FLAG_SYNCHRONIZE 0x02 #define TCP_FLAG_RESET 0x04 #define TCP_FLAG_PUSH 0x08 #define TCP_FLAG_ACKNOWLEDGE 0x10 #define TCP_FLAG_URGENT 0x20 #define TCP_FLAG_CONGESTION_NOTIFICATION_ECHO 0x40 #define TCP_FLAG_CONGESTION_WINDOW_REDUCED 0x80 #define TCP_CONNECTION_TIMEOUT 75000000 // 75 secs #define TCP_DELAYED_ACKNOWLEDGE_TIMEOUT 100000 // 100 msecs #define TCP_DEFAULT_MAX_SEGMENT_SIZE 536 #define TCP_MAX_WINDOW 65535 #define TCP_MAX_SEGMENT_LIFETIME 60000000 // 60 secs #define TCP_PERSIST_TIMEOUT 1000000 // 1 sec // Initial estimate for packet round trip time (RTT) #define TCP_INITIAL_RTT 2000000 // 2 secs // Minimum retransmit timeout (consider delayed ack) #define TCP_MIN_RETRANSMIT_TIMEOUT 200000 // 200 msecs // Maximum retransmit timeout (per RFC6298) #define TCP_MAX_RETRANSMIT_TIMEOUT 60000000 // 60 secs // New value for timeout in case of lost SYN (RFC 6298) #define TCP_SYN_RETRANSMIT_TIMEOUT 3000000 // 3 secs struct tcp_sack { uint32 left_edge; uint32 right_edge; } _PACKED; struct tcp_option { uint8 kind; uint8 length; union { uint8 window_shift; uint16 max_segment_size; struct { uint32 value; uint32 reply; } timestamp; tcp_sack sack[0]; }; } _PACKED; enum tcp_option_kind { TCP_OPTION_END = 0, TCP_OPTION_NOP = 1, TCP_OPTION_MAX_SEGMENT_SIZE = 2, TCP_OPTION_WINDOW_SHIFT = 3, TCP_OPTION_SACK_PERMITTED = 4, TCP_OPTION_SACK = 5, TCP_OPTION_TIMESTAMP = 8, }; #define TCP_MAX_WINDOW_SHIFT 14 enum { TCP_HAS_WINDOW_SCALE = 1 << 0, TCP_HAS_TIMESTAMPS = 1 << 1, TCP_SACK_PERMITTED = 1 << 2, TCP_HAS_SACK = 1 << 3, }; struct tcp_segment_header { tcp_segment_header(uint8 _flags) : flags(_flags), window_shift(0), max_segment_size(0), sackCount(0), options(0) {} uint32 sequence; uint32 acknowledge; uint16 advertised_window; uint16 urgent_offset; uint8 flags; uint8 window_shift; uint16 max_segment_size; uint32 timestamp_value; uint32 timestamp_reply; #define MAX_SACK_BLKS 4 tcp_sack sacks[MAX_SACK_BLKS]; int sackCount; uint32 options; bool AcknowledgeOnly() const { return (flags & (TCP_FLAG_SYNCHRONIZE | TCP_FLAG_FINISH | TCP_FLAG_RESET | TCP_FLAG_URGENT | TCP_FLAG_ACKNOWLEDGE)) == TCP_FLAG_ACKNOWLEDGE; } uint32 AdvertisedWindow(uint8 windowShift) const { return (uint32)advertised_window << windowShift; } void SetAdvertisedWindow(size_t availableBytes, uint8 windowShift) { availableBytes >>= windowShift; advertised_window = min_c(TCP_MAX_WINDOW, availableBytes); } }; enum tcp_segment_action { KEEP = 0, DROP = (1 << 0), RESET = (1 << 1), ACKNOWLEDGE = (1 << 2), IMMEDIATE_ACKNOWLEDGE = (1 << 3), SEND_QUEUED = (1 << 4), DELETED_ENDPOINT = (1 << 5), }; extern net_buffer_module_info* gBufferModule; extern net_datalink_module_info* gDatalinkModule; extern net_socket_module_info* gSocketModule; extern net_stack_module_info* gStackModule; EndpointManager* get_endpoint_manager(net_domain* domain); void put_endpoint_manager(EndpointManager* manager); status_t add_tcp_header(net_address_module_info* addressModule, tcp_segment_header& segment, net_buffer* buffer); size_t tcp_options_length(tcp_segment_header& segment); const char* name_for_state(tcp_state state); #endif // TCP_H