summaryrefslogtreecommitdiff
path: root/chromium/net/third_party/quiche/src/quic/qbone/qbone_packet_processor.cc
blob: db7a1382a8ff505a55f790d9cb2f2d110247dd30 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/third_party/quiche/src/quic/qbone/qbone_packet_processor.h"

#include <cstring>

#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
#include "net/third_party/quiche/src/quic/qbone/platform/icmp_packet.h"
#include "net/third_party/quiche/src/quic/qbone/platform/internet_checksum.h"
#include "net/third_party/quiche/src/quic/qbone/platform/tcp_packet.h"

namespace {

constexpr size_t kIPv6AddressSize = 16;
constexpr size_t kIPv6MinPacketSize = 1280;
constexpr size_t kIcmpTtl = 64;
constexpr size_t kICMPv6DestinationUnreachableDueToSourcePolicy = 5;

}  // namespace

namespace quic {

const QuicIpAddress QbonePacketProcessor::kInvalidIpAddress =
    QuicIpAddress::Any6();

QbonePacketProcessor::QbonePacketProcessor(QuicIpAddress self_ip,
                                           QuicIpAddress client_ip,
                                           size_t client_ip_subnet_length,
                                           OutputInterface* output,
                                           StatsInterface* stats)
    : client_ip_(client_ip),
      output_(output),
      stats_(stats),
      filter_(new Filter) {
  memcpy(self_ip_.s6_addr, self_ip.ToPackedString().data(), kIPv6AddressSize);
  DCHECK_LE(client_ip_subnet_length, kIPv6AddressSize * 8);
  client_ip_subnet_length_ = client_ip_subnet_length;

  DCHECK(IpAddressFamily::IP_V6 == self_ip.address_family());
  DCHECK(IpAddressFamily::IP_V6 == client_ip.address_family());
  DCHECK(self_ip != kInvalidIpAddress);
}

QbonePacketProcessor::OutputInterface::~OutputInterface() {}
QbonePacketProcessor::StatsInterface::~StatsInterface() {}
QbonePacketProcessor::Filter::~Filter() {}

QbonePacketProcessor::ProcessingResult
QbonePacketProcessor::Filter::FilterPacket(Direction direction,
                                           QuicStringPiece full_packet,
                                           QuicStringPiece payload,
                                           icmp6_hdr* icmp_header,
                                           OutputInterface* output) {
  return ProcessingResult::OK;
}

void QbonePacketProcessor::ProcessPacket(string* packet, Direction direction) {
  if (QUIC_PREDICT_FALSE(!IsValid())) {
    QUIC_BUG << "QuicPacketProcessor is invoked in an invalid state.";
    stats_->OnPacketDroppedSilently(direction);
    return;
  }

  uint8_t transport_protocol;
  char* transport_data;
  icmp6_hdr icmp_header;
  memset(&icmp_header, 0, sizeof(icmp_header));
  ProcessingResult result = ProcessIPv6HeaderAndFilter(
      packet, direction, &transport_protocol, &transport_data, &icmp_header);

  switch (result) {
    case ProcessingResult::OK:
      switch (direction) {
        case Direction::FROM_CLIENT:
          output_->SendPacketToNetwork(*packet);
          break;
        case Direction::FROM_NETWORK:
          output_->SendPacketToClient(*packet);
          break;
      }
      stats_->OnPacketForwarded(direction);
      break;
    case ProcessingResult::SILENT_DROP:
      stats_->OnPacketDroppedSilently(direction);
      break;
    case ProcessingResult::DEFER:
      stats_->OnPacketDeferred(direction);
      break;
    case ProcessingResult::ICMP:
      SendIcmpResponse(&icmp_header, *packet, direction);
      stats_->OnPacketDroppedWithIcmp(direction);
      break;
    case ProcessingResult::ICMP_AND_TCP_RESET:
      SendIcmpResponse(&icmp_header, *packet, direction);
      stats_->OnPacketDroppedWithIcmp(direction);
      SendTcpReset(*packet, direction);
      stats_->OnPacketDroppedWithTcpReset(direction);
      break;
  }
}

QbonePacketProcessor::ProcessingResult
QbonePacketProcessor::ProcessIPv6HeaderAndFilter(string* packet,
                                                 Direction direction,
                                                 uint8_t* transport_protocol,
                                                 char** transport_data,
                                                 icmp6_hdr* icmp_header) {
  ProcessingResult result = ProcessIPv6Header(
      packet, direction, transport_protocol, transport_data, icmp_header);

  if (result == ProcessingResult::OK) {
    char* packet_data = &*packet->begin();
    size_t header_size = *transport_data - packet_data;
    // Sanity-check the bounds.
    if (packet_data >= *transport_data || header_size > packet->size() ||
        header_size < kIPv6HeaderSize) {
      QUIC_BUG << "Invalid pointers encountered in "
                  "QbonePacketProcessor::ProcessPacket.  Dropping the packet";
      return ProcessingResult::SILENT_DROP;
    }

    result = filter_->FilterPacket(
        direction, *packet,
        QuicStringPiece(*transport_data, packet->size() - header_size),
        icmp_header, output_);
  }

  // Do not send ICMP error messages in response to ICMP errors.
  if (result == ProcessingResult::ICMP) {
    const uint8_t* header = reinterpret_cast<const uint8_t*>(packet->data());

    constexpr size_t kIPv6NextHeaderOffset = 6;
    constexpr size_t kIcmpMessageTypeOffset = kIPv6HeaderSize + 0;
    constexpr size_t kIcmpMessageTypeMaxError = 127;
    if (
        // Check size.
        packet->size() >= (kIPv6HeaderSize + kICMPv6HeaderSize) &&
        // Check that the packet is in fact ICMP.
        header[kIPv6NextHeaderOffset] == IPPROTO_ICMPV6 &&
        // Check that ICMP message type is an error.
        header[kIcmpMessageTypeOffset] < kIcmpMessageTypeMaxError) {
      result = ProcessingResult::SILENT_DROP;
    }
  }

  return result;
}

QbonePacketProcessor::ProcessingResult QbonePacketProcessor::ProcessIPv6Header(
    string* packet,
    Direction direction,
    uint8_t* transport_protocol,
    char** transport_data,
    icmp6_hdr* icmp_header) {
  // Check if the packet is big enough to have IPv6 header.
  if (packet->size() < kIPv6HeaderSize) {
    QUIC_DVLOG(1) << "Dropped malformed packet: IPv6 header too short";
    return ProcessingResult::SILENT_DROP;
  }

  // Check version field.
  ip6_hdr* header = reinterpret_cast<ip6_hdr*>(&*packet->begin());
  if (header->ip6_vfc >> 4 != 6) {
    QUIC_DVLOG(1) << "Dropped malformed packet: IP version is not IPv6";
    return ProcessingResult::SILENT_DROP;
  }

  // Check payload size.
  const size_t declared_payload_size =
      QuicEndian::NetToHost16(header->ip6_plen);
  const size_t actual_payload_size = packet->size() - kIPv6HeaderSize;
  if (declared_payload_size != actual_payload_size) {
    QUIC_DVLOG(1)
        << "Dropped malformed packet: incorrect packet length specified";
    return ProcessingResult::SILENT_DROP;
  }

  // Check that the address of the client is in the packet.
  QuicIpAddress address_to_check;
  uint8_t address_reject_code;
  bool ip_parse_result;
  switch (direction) {
    case Direction::FROM_CLIENT:
      // Expect the source IP to match the client.
      ip_parse_result = address_to_check.FromPackedString(
          reinterpret_cast<const char*>(&header->ip6_src),
          sizeof(header->ip6_src));
      address_reject_code = kICMPv6DestinationUnreachableDueToSourcePolicy;
      break;
    case Direction::FROM_NETWORK:
      // Expect the destination IP to match the client.
      ip_parse_result = address_to_check.FromPackedString(
          reinterpret_cast<const char*>(&header->ip6_dst),
          sizeof(header->ip6_src));
      address_reject_code = ICMP6_DST_UNREACH_NOROUTE;
      break;
  }
  DCHECK(ip_parse_result);
  if (!client_ip_.InSameSubnet(address_to_check, client_ip_subnet_length_)) {
    QUIC_DVLOG(1)
        << "Dropped packet: source/destination address is not client's";
    icmp_header->icmp6_type = ICMP6_DST_UNREACH;
    icmp_header->icmp6_code = address_reject_code;
    return ProcessingResult::ICMP;
  }

  // Check and decrement TTL.
  if (header->ip6_hops <= 1) {
    icmp_header->icmp6_type = ICMP6_TIME_EXCEEDED;
    icmp_header->icmp6_code = ICMP6_TIME_EXCEED_TRANSIT;
    return ProcessingResult::ICMP;
  }
  header->ip6_hops--;

  // Check and extract IP headers.
  switch (header->ip6_nxt) {
    case IPPROTO_TCP:
    case IPPROTO_UDP:
    case IPPROTO_ICMPV6:
      *transport_protocol = header->ip6_nxt;
      *transport_data = (&*packet->begin()) + kIPv6HeaderSize;
      break;
    default:
      icmp_header->icmp6_type = ICMP6_PARAM_PROB;
      icmp_header->icmp6_code = ICMP6_PARAMPROB_NEXTHEADER;
      return ProcessingResult::ICMP;
  }

  return ProcessingResult::OK;
}

void QbonePacketProcessor::SendIcmpResponse(icmp6_hdr* icmp_header,
                                            QuicStringPiece original_packet,
                                            Direction original_direction) {
  in6_addr dst;
  // TODO(b/70339814): ensure this is actually a unicast address.
  memcpy(dst.s6_addr, &original_packet[8], kIPv6AddressSize);

  CreateIcmpPacket(self_ip_, dst, *icmp_header, original_packet,
                   [this, original_direction](QuicStringPiece packet) {
                     SendResponse(original_direction, packet);
                   });
}

void QbonePacketProcessor::SendTcpReset(QuicStringPiece original_packet,
                                        Direction original_direction) {
  CreateTcpResetPacket(original_packet,
                       [this, original_direction](QuicStringPiece packet) {
                         SendResponse(original_direction, packet);
                       });
}

void QbonePacketProcessor::SendResponse(Direction original_direction,
                                        QuicStringPiece packet) {
  switch (original_direction) {
    case Direction::FROM_CLIENT:
      output_->SendPacketToClient(packet);
      break;
    case Direction::FROM_NETWORK:
      output_->SendPacketToNetwork(packet);
      break;
  }
}

}  // namespace quic