1
#include "source/common/quic/udp_gso_batch_writer.h"
2

            
3
#include "source/common/network/io_socket_error_impl.h"
4
#include "source/common/quic/envoy_quic_utils.h"
5

            
6
namespace Envoy {
7
namespace Quic {
8
namespace {
9
13
Api::IoCallUint64Result convertQuicWriteResult(quic::WriteResult quic_result, size_t payload_len) {
10
13
  switch (quic_result.status) {
11
11
  case quic::WRITE_STATUS_OK:
12
11
    if (quic_result.bytes_written == 0) {
13
6
      ENVOY_LOG_MISC(trace, "sendmsg successful, message buffered to send");
14
6
    } else {
15
5
      ENVOY_LOG_MISC(trace, "sendmsg successful, flushed bytes {}", quic_result.bytes_written);
16
5
    }
17
    // Return payload_len as rc & nullptr as error on success
18
11
    return {/*rc=*/payload_len,
19
11
            /*err=*/Api::IoError::none()};
20
1
  case quic::WRITE_STATUS_BLOCKED_DATA_BUFFERED:
21
    // Data was buffered, Return payload_len as rc & nullptr as error
22
1
    ENVOY_LOG_MISC(trace, "sendmsg blocked, message buffered to send");
23
1
    return {/*rc=*/payload_len,
24
1
            /*err=*/Api::IoError::none()};
25
1
  case quic::WRITE_STATUS_BLOCKED:
26
    // Writer blocked, return error
27
1
    ENVOY_LOG_MISC(trace, "sendmsg blocked, message not buffered");
28
1
    return {/*rc=*/0,
29
1
            /*err=*/Network::IoSocketError::getIoSocketEagainError()};
30
  default:
31
    // Write Failed, return {0 and error_code}
32
    ENVOY_LOG_MISC(trace, "sendmsg failed with error code {}",
33
                   static_cast<int>(quic_result.error_code));
34
    return {/*rc=*/0,
35
            /*err=*/Network::IoSocketError::create(quic_result.error_code)};
36
13
  }
37
13
}
38

            
39
} // namespace
40

            
41
// Initialize QuicGsoBatchWriter, set io_handle_ and stats_
42
UdpGsoBatchWriter::UdpGsoBatchWriter(Network::IoHandle& io_handle, Stats::Scope& scope)
43
1148
    : quic::QuicGsoBatchWriter(io_handle.fdDoNotUse()), stats_(generateStats(scope)) {}
44

            
45
Api::IoCallUint64Result
46
UdpGsoBatchWriter::writePacket(const Buffer::Instance& buffer, const Network::Address::Ip* local_ip,
47
10
                               const Network::Address::Instance& peer_address) {
48
  // Convert received parameters to relevant forms
49
10
  quic::QuicSocketAddress peer_addr = envoyIpAddressToQuicSocketAddress(peer_address.ip());
50
10
  quic::QuicSocketAddress self_addr = envoyIpAddressToQuicSocketAddress(local_ip);
51
10
  ASSERT(buffer.getRawSlices().size() == 1);
52
10
  size_t payload_len = static_cast<size_t>(buffer.frontSlice().len_);
53

            
54
  // TODO(yugant): Currently we do not use PerPacketOptions with Quic, we may want to
55
  // specify this parameter here at a later stage.
56
10
  quic::QuicPacketWriterParams params;
57
10
  quic::WriteResult quic_result = WritePacket(static_cast<char*>(buffer.frontSlice().mem_),
58
10
                                              payload_len, self_addr.host(), peer_addr,
59
10
                                              /*quic::PerPacketOptions=*/nullptr, params);
60
10
  updateUdpGsoBatchWriterStats(quic_result);
61

            
62
10
  return convertQuicWriteResult(quic_result, payload_len);
63
10
}
64

            
65
uint64_t UdpGsoBatchWriter::getMaxPacketSize(const Network::Address::Instance& peer_address) const {
66
  quic::QuicSocketAddress peer_addr = envoyIpAddressToQuicSocketAddress(peer_address.ip());
67
  return static_cast<uint64_t>(GetMaxPacketSize(peer_addr));
68
}
69

            
70
Network::UdpPacketWriterBuffer
71
UdpGsoBatchWriter::getNextWriteLocation(const Network::Address::Ip* local_ip,
72
                                        const Network::Address::Instance& peer_address) {
73
  quic::QuicSocketAddress peer_addr = envoyIpAddressToQuicSocketAddress(peer_address.ip());
74
  quic::QuicSocketAddress self_addr = envoyIpAddressToQuicSocketAddress(local_ip);
75
  quic::QuicPacketBuffer quic_buf = GetNextWriteLocation(self_addr.host(), peer_addr);
76
  return {reinterpret_cast<uint8_t*>(quic_buf.buffer), Network::UdpMaxOutgoingPacketSize,
77
          quic_buf.release_buffer};
78
}
79

            
80
3
Api::IoCallUint64Result UdpGsoBatchWriter::flush() {
81
3
  quic::WriteResult quic_result = Flush();
82
3
  updateUdpGsoBatchWriterStats(quic_result);
83

            
84
3
  return convertQuicWriteResult(quic_result, /*payload_len=*/0);
85
3
}
86

            
87
13
void UdpGsoBatchWriter::updateUdpGsoBatchWriterStats(quic::WriteResult quic_result) {
88
13
  if (quic_result.status == quic::WRITE_STATUS_OK && quic_result.bytes_written > 0) {
89
5
    if (gso_size_ > 0u) {
90
5
      uint64_t num_pkts_in_batch =
91
5
          std::ceil(static_cast<float>(quic_result.bytes_written) / gso_size_);
92
5
      stats_.pkts_sent_per_batch_.recordValue(num_pkts_in_batch);
93
5
    }
94
5
    stats_.total_bytes_sent_.add(quic_result.bytes_written);
95
5
  }
96
13
  stats_.internal_buffer_size_.set(batch_buffer().SizeInUse());
97
13
  gso_size_ = buffered_writes().empty() ? 0u : buffered_writes().front().buf_len;
98
13
}
99

            
100
1148
UdpGsoBatchWriterStats UdpGsoBatchWriter::generateStats(Stats::Scope& scope) {
101
1148
  return {
102
1148
      UDP_GSO_BATCH_WRITER_STATS(POOL_COUNTER(scope), POOL_GAUGE(scope), POOL_HISTOGRAM(scope))};
103
1148
}
104

            
105
Network::UdpPacketWriterPtr
106
UdpGsoBatchWriterFactory::createUdpPacketWriter(Network::IoHandle& io_handle, Stats::Scope& scope,
107
                                                Envoy::Event::Dispatcher&,
108
1134
                                                absl::AnyInvocable<void() &&>) {
109
1134
  return std::make_unique<UdpGsoBatchWriter>(io_handle, scope);
110
1134
}
111

            
112
} // namespace Quic
113
} // namespace Envoy