/* * Copyright 2004 The WebRTC Project Authors. All rights reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #ifndef P2P_BASE_PSEUDO_TCP_H_ #define P2P_BASE_PSEUDO_TCP_H_ #include #include #include #include #include "rtc_base/synchronization/mutex.h" #include "rtc_base/system/rtc_export.h" namespace cricket { ////////////////////////////////////////////////////////////////////// // IPseudoTcpNotify ////////////////////////////////////////////////////////////////////// class PseudoTcp; class IPseudoTcpNotify { public: // Notification of tcp events virtual void OnTcpOpen(PseudoTcp* tcp) = 0; virtual void OnTcpReadable(PseudoTcp* tcp) = 0; virtual void OnTcpWriteable(PseudoTcp* tcp) = 0; virtual void OnTcpClosed(PseudoTcp* tcp, uint32_t error) = 0; // Write the packet onto the network enum WriteResult { WR_SUCCESS, WR_TOO_LARGE, WR_FAIL }; virtual WriteResult TcpWritePacket(PseudoTcp* tcp, const char* buffer, size_t len) = 0; protected: virtual ~IPseudoTcpNotify() {} }; ////////////////////////////////////////////////////////////////////// // PseudoTcp ////////////////////////////////////////////////////////////////////// class RTC_EXPORT PseudoTcp { public: static uint32_t Now(); PseudoTcp(IPseudoTcpNotify* notify, uint32_t conv); virtual ~PseudoTcp(); int Connect(); int Recv(char* buffer, size_t len); int Send(const char* buffer, size_t len); void Close(bool force); int GetError(); enum TcpState { TCP_LISTEN, TCP_SYN_SENT, TCP_SYN_RECEIVED, TCP_ESTABLISHED, TCP_CLOSED }; TcpState State() const { return m_state; } // Call this when the PMTU changes. void NotifyMTU(uint16_t mtu); // Call this based on timeout value returned from GetNextClock. // It's ok to call this too frequently. void NotifyClock(uint32_t now); // Call this whenever a packet arrives. // Returns true if the packet was processed successfully. bool NotifyPacket(const char* buffer, size_t len); // Call this to determine the next time NotifyClock should be called. // Returns false if the socket is ready to be destroyed. bool GetNextClock(uint32_t now, long& timeout); // Call these to get/set option values to tailor this PseudoTcp // instance's behaviour for the kind of data it will carry. // If an unrecognized option is set or got, an assertion will fire. // // Setting options for OPT_RCVBUF or OPT_SNDBUF after Connect() is called // will result in an assertion. enum Option { OPT_NODELAY, // Whether to enable Nagle's algorithm (0 == off) OPT_ACKDELAY, // The Delayed ACK timeout (0 == off). OPT_RCVBUF, // Set the receive buffer size, in bytes. OPT_SNDBUF, // Set the send buffer size, in bytes. }; void GetOption(Option opt, int* value); void SetOption(Option opt, int value); // Returns current congestion window in bytes. uint32_t GetCongestionWindow() const; // Returns amount of data in bytes that has been sent, but haven't // been acknowledged. uint32_t GetBytesInFlight() const; // Returns number of bytes that were written in buffer and haven't // been sent. uint32_t GetBytesBufferedNotSent() const; // Returns current round-trip time estimate in milliseconds. uint32_t GetRoundTripTimeEstimateMs() const; protected: enum SendFlags { sfNone, sfDelayedAck, sfImmediateAck }; struct Segment { uint32_t conv, seq, ack; uint8_t flags; uint16_t wnd; const char* data; uint32_t len; uint32_t tsval, tsecr; }; struct SSegment { SSegment(uint32_t s, uint32_t l, bool c) : seq(s), len(l), /*tstamp(0),*/ xmit(0), bCtrl(c) {} uint32_t seq, len; // uint32_t tstamp; uint8_t xmit; bool bCtrl; }; typedef std::list SList; struct RSegment { uint32_t seq, len; }; uint32_t queue(const char* data, uint32_t len, bool bCtrl); // Creates a packet and submits it to the network. This method can either // send payload or just an ACK packet. // // `seq` is the sequence number of this packet. // `flags` is the flags for sending this packet. // `offset` is the offset to read from `m_sbuf`. // `len` is the number of bytes to read from `m_sbuf` as payload. If this // value is 0 then this is an ACK packet, otherwise this packet has payload. IPseudoTcpNotify::WriteResult packet(uint32_t seq, uint8_t flags, uint32_t offset, uint32_t len); bool parse(const uint8_t* buffer, uint32_t size); void attemptSend(SendFlags sflags = sfNone); void closedown(uint32_t err = 0); bool clock_check(uint32_t now, long& nTimeout); bool process(Segment& seg); bool transmit(const SList::iterator& seg, uint32_t now); void adjustMTU(); protected: // This method is used in test only to query receive buffer state. bool isReceiveBufferFull() const; // This method is only used in tests, to disable window scaling // support for testing backward compatibility. void disableWindowScale(); private: // Queue the connect message with TCP options. void queueConnectMessage(); // Parse TCP options in the header. void parseOptions(const char* data, uint32_t len); // Apply a TCP option that has been read from the header. void applyOption(char kind, const char* data, uint32_t len); // Apply window scale option. void applyWindowScaleOption(uint8_t scale_factor); // Resize the send buffer with `new_size` in bytes. void resizeSendBuffer(uint32_t new_size); // Resize the receive buffer with `new_size` in bytes. This call adjusts // window scale factor `m_swnd_scale` accordingly. void resizeReceiveBuffer(uint32_t new_size); class LockedFifoBuffer final { public: explicit LockedFifoBuffer(size_t size); ~LockedFifoBuffer(); size_t GetBuffered() const; bool SetCapacity(size_t size); bool ReadOffset(void* buffer, size_t bytes, size_t offset, size_t* bytes_read); bool WriteOffset(const void* buffer, size_t bytes, size_t offset, size_t* bytes_written); bool Read(void* buffer, size_t bytes, size_t* bytes_read); bool Write(const void* buffer, size_t bytes, size_t* bytes_written); void ConsumeReadData(size_t size); void ConsumeWriteBuffer(size_t size); bool GetWriteRemaining(size_t* size) const; private: bool ReadOffsetLocked(void* buffer, size_t bytes, size_t offset, size_t* bytes_read) RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); bool WriteOffsetLocked(const void* buffer, size_t bytes, size_t offset, size_t* bytes_written) RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); // the allocated buffer std::unique_ptr buffer_ RTC_GUARDED_BY(mutex_); // size of the allocated buffer size_t buffer_length_ RTC_GUARDED_BY(mutex_); // amount of readable data in the buffer size_t data_length_ RTC_GUARDED_BY(mutex_); // offset to the readable data size_t read_position_ RTC_GUARDED_BY(mutex_); mutable webrtc::Mutex mutex_; }; IPseudoTcpNotify* m_notify; enum Shutdown { SD_NONE, SD_GRACEFUL, SD_FORCEFUL } m_shutdown; int m_error; // TCB data TcpState m_state; uint32_t m_conv; bool m_bReadEnable, m_bWriteEnable, m_bOutgoing; uint32_t m_lasttraffic; // Incoming data typedef std::list RList; RList m_rlist; uint32_t m_rbuf_len, m_rcv_nxt, m_rcv_wnd, m_lastrecv; uint8_t m_rwnd_scale; // Window scale factor. LockedFifoBuffer m_rbuf; // Outgoing data SList m_slist; uint32_t m_sbuf_len, m_snd_nxt, m_snd_wnd, m_lastsend, m_snd_una; uint8_t m_swnd_scale; // Window scale factor. LockedFifoBuffer m_sbuf; // Maximum segment size, estimated protocol level, largest segment sent uint32_t m_mss, m_msslevel, m_largest, m_mtu_advise; // Retransmit timer uint32_t m_rto_base; // Timestamp tracking uint32_t m_ts_recent, m_ts_lastack; // Round-trip calculation uint32_t m_rx_rttvar, m_rx_srtt, m_rx_rto; // Congestion avoidance, Fast retransmit/recovery, Delayed ACKs uint32_t m_ssthresh, m_cwnd; uint8_t m_dup_acks; uint32_t m_recover; uint32_t m_t_ack; // Configuration options bool m_use_nagling; uint32_t m_ack_delay; // This is used by unit tests to test backward compatibility of // PseudoTcp implementations that don't support window scaling. bool m_support_wnd_scale; }; } // namespace cricket #endif // P2P_BASE_PSEUDO_TCP_H_