| #ifndef NET_THIRD_PARTY_QUIC_PLATFORM_IMPL_QUIC_LINUX_SOCKET_UTILS_H_ |
| #define NET_THIRD_PARTY_QUIC_PLATFORM_IMPL_QUIC_LINUX_SOCKET_UTILS_H_ |
| |
| #include <errno.h> |
| #include <string.h> |
| #include <sys/socket.h> |
| #include <sys/uio.h> |
| #include <deque> |
| #include <functional> |
| #include <iterator> |
| #include <memory> |
| #include <type_traits> |
| #include <utility> |
| |
| #include "net/third_party/quic/core/quic_packet_writer.h" |
| #include "net/third_party/quic/core/quic_types.h" |
| #include "net/third_party/quic/platform/api/quic_bug_tracker.h" |
| #include "net/third_party/quic/platform/api/quic_ip_address.h" |
| #include "net/third_party/quic/platform/api/quic_logging.h" |
| #include "net/third_party/quic/platform/api/quic_socket_address.h" |
| #include "net/third_party/quic/platform/impl/quic_socket_utils.h" |
| #include "starboard/memory.h" |
| #include "starboard/types.h" |
| |
| #ifndef SOL_UDP |
| #define SOL_UDP 17 |
| #endif |
| |
| #ifndef UDP_SEGMENT |
| #define UDP_SEGMENT 103 |
| #endif |
| |
| #ifndef UDP_MAX_SEGMENTS |
| #define UDP_MAX_SEGMENTS (1 << 6UL) |
| #endif |
| |
| namespace quic { |
| |
| // BufferedWrite holds all information needed to send a packet. |
| struct BufferedWrite { |
| BufferedWrite(const char* buffer, |
| size_t buf_len, |
| const QuicIpAddress& self_address, |
| const QuicSocketAddress& peer_address) |
| : BufferedWrite(buffer, |
| buf_len, |
| self_address, |
| peer_address, |
| std::unique_ptr<PerPacketOptions>()) {} |
| |
| BufferedWrite(const char* buffer, |
| size_t buf_len, |
| const QuicIpAddress& self_address, |
| const QuicSocketAddress& peer_address, |
| std::unique_ptr<PerPacketOptions> options) |
| : buffer(buffer), |
| buf_len(buf_len), |
| self_address(self_address), |
| peer_address(peer_address), |
| options(std::move(options)) {} |
| |
| const char* buffer; // Not owned. |
| size_t buf_len; |
| QuicIpAddress self_address; |
| QuicSocketAddress peer_address; |
| std::unique_ptr<PerPacketOptions> options; |
| }; |
| |
| // QuicMMsgHdr is used to build mmsghdr objects that can be used to send |
| // multiple packets at once via ::sendmmsg. |
| // |
| // Example: |
| // QuicDeque<BufferedWrite> buffered_writes; |
| // ... (Populate buffered_writes) ... |
| // |
| // QuicMMsgHdr mhdr( |
| // buffered_writes.begin(), buffered_writes.end(), kCmsgSpaceForIp, |
| // [](QuicMMsgHdr* mhdr, int i, const BufferedWrite& buffered_write) { |
| // mhdr->SetIpInNextCmsg(i, buffered_write.self_address); |
| // }); |
| // |
| // int num_packets_sent; |
| // QuicSocketUtils::WriteMultiplePackets(fd, &mhdr, &num_packets_sent); |
| class QuicMMsgHdr { |
| public: |
| typedef std::function< |
| void(QuicMMsgHdr* mhdr, int i, const BufferedWrite& buffered_write)> |
| ControlBufferInitializer; |
| template <typename IteratorT> |
| QuicMMsgHdr(const IteratorT& first, |
| const IteratorT& last, |
| size_t cbuf_size, |
| ControlBufferInitializer cbuf_initializer) |
| : num_msgs_(std::distance(first, last)), cbuf_size_(cbuf_size) { |
| static_assert( |
| std::is_same<typename std::iterator_traits<IteratorT>::value_type, |
| BufferedWrite>::value, |
| "Must iterate over a collection of BufferedWrite."); |
| |
| DCHECK_LE(0, num_msgs_); |
| if (num_msgs_ == 0) { |
| return; |
| } |
| |
| storage_.reset(new char[StorageSize()]); |
| memset(&storage_[0], 0, StorageSize()); |
| |
| int i = -1; |
| for (auto it = first; it != last; ++it) { |
| ++i; |
| |
| InitOneHeader(i, *it); |
| if (cbuf_initializer) { |
| cbuf_initializer(this, i, *it); |
| } |
| } |
| } |
| |
| void SetIpInNextCmsg(int i, const QuicIpAddress& self_address); |
| |
| template <typename DataType> |
| DataType* GetNextCmsgData(int i, int cmsg_level, int cmsg_type) { |
| return reinterpret_cast<DataType*>( |
| GetNextCmsgDataInternal(i, cmsg_level, cmsg_type, sizeof(DataType))); |
| } |
| |
| mmsghdr* mhdr() { return GetMMsgHdr(0); } |
| |
| int num_msgs() const { return num_msgs_; } |
| |
| // Get the total number of bytes in the first |num_packets_sent| packets. |
| int num_bytes_sent(int num_packets_sent); |
| |
| protected: |
| void InitOneHeader(int i, const BufferedWrite& buffered_write); |
| |
| void* GetNextCmsgDataInternal(int i, |
| int cmsg_level, |
| int cmsg_type, |
| size_t data_size); |
| |
| size_t StorageSize() const { |
| return num_msgs_ * |
| (sizeof(mmsghdr) + sizeof(IOVEC) + sizeof(sockaddr_storage) + |
| sizeof(cmsghdr*) + cbuf_size_); |
| } |
| |
| mmsghdr* GetMMsgHdr(int i) { |
| auto* first = reinterpret_cast<mmsghdr*>(&storage_[0]); |
| return &first[i]; |
| } |
| |
| IOVEC* GetIov(int i) { |
| auto* first = reinterpret_cast<IOVEC*>(GetMMsgHdr(num_msgs_)); |
| return &first[i]; |
| } |
| |
| sockaddr_storage* GetPeerAddressStorage(int i) { |
| auto* first = reinterpret_cast<sockaddr_storage*>(GetIov(num_msgs_)); |
| return &first[i]; |
| } |
| |
| cmsghdr** GetCmsgHdr(int i) { |
| auto* first = reinterpret_cast<cmsghdr**>(GetPeerAddressStorage(num_msgs_)); |
| return &first[i]; |
| } |
| |
| char* GetCbuf(int i) { |
| auto* first = reinterpret_cast<char*>(GetCmsgHdr(num_msgs_)); |
| return &first[i * cbuf_size_]; |
| } |
| |
| const int num_msgs_; |
| // Size of cmsg buffer for each message. |
| const size_t cbuf_size_; |
| // storage_ holds the memory of |
| // |num_msgs_| mmsghdr |
| // |num_msgs_| IOVEC |
| // |num_msgs_| sockaddr_storage, for peer addresses |
| // |num_msgs_| cmsghdr* |
| // |num_msgs_| cbuf, each of size cbuf_size |
| std::unique_ptr<char[]> storage_; |
| }; |
| |
| // QuicSyscallWrapper wraps system calls for testing. |
| class QuicSyscallWrapper { |
| public: |
| int Sendmmsg(int sockfd, |
| mmsghdr* msgvec, |
| unsigned int vlen, |
| int flags) const { |
| return ::sendmmsg(sockfd, msgvec, vlen, flags); |
| } |
| }; |
| |
| class QuicLinuxSocketUtils : public QuicSocketUtils { |
| public: |
| // Return the UDP segment size of |fd|, 0 means segment size has not been set |
| // on this socket. If GSO is not supported, return -1. |
| static int GetUDPSegmentSize(int fd); |
| |
| // Writes the packets in |mhdr| to the socket, using ::sendmmsg. |
| static WriteResult WriteMultiplePackets(int fd, |
| QuicMMsgHdr* mhdr, |
| int* num_packets_sent) { |
| return WriteMultiplePackets(fd, mhdr, num_packets_sent, |
| QuicSyscallWrapper()); |
| } |
| |
| template <typename SyscallWrapper> |
| static WriteResult WriteMultiplePackets(int fd, |
| QuicMMsgHdr* mhdr, |
| int* num_packets_sent, |
| const SyscallWrapper& syscall) { |
| *num_packets_sent = 0; |
| |
| if (mhdr->num_msgs() <= 0) { |
| return WriteResult(WRITE_STATUS_ERROR, EINVAL); |
| } |
| |
| int rc; |
| do { |
| rc = syscall.Sendmmsg(fd, mhdr->mhdr(), mhdr->num_msgs(), 0); |
| } while (rc < 0 && errno == EINTR); |
| |
| if (rc > 0) { |
| *num_packets_sent = rc; |
| |
| return WriteResult(WRITE_STATUS_OK, mhdr->num_bytes_sent(rc)); |
| } else if (rc == 0) { |
| QUIC_BUG << "sendmmsg returned 0, returning WRITE_STATUS_ERROR. errno: " |
| << errno; |
| errno = EIO; |
| } |
| |
| return WriteResult((errno == EAGAIN || errno == EWOULDBLOCK) |
| ? WRITE_STATUS_BLOCKED |
| : WRITE_STATUS_ERROR, |
| errno); |
| } |
| }; |
| |
| } // namespace quic |
| |
| #endif // NET_THIRD_PARTY_QUIC_PLATFORM_IMPL_QUIC_LINUX_SOCKET_UTILS_H_ |