1 // Copyright (c) 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "quiche/quic/qbone/qbone_packet_processor.h"
6
7 #include <netinet/icmp6.h>
8 #include <netinet/in.h>
9 #include <netinet/ip6.h>
10
11 #include <cstdint>
12 #include <cstring>
13
14 #include "absl/base/optimization.h"
15 #include "absl/strings/string_view.h"
16 #include "quiche/quic/core/internet_checksum.h"
17 #include "quiche/quic/platform/api/quic_bug_tracker.h"
18 #include "quiche/quic/platform/api/quic_ip_address_family.h"
19 #include "quiche/quic/platform/api/quic_logging.h"
20 #include "quiche/quic/qbone/platform/icmp_packet.h"
21 #include "quiche/quic/qbone/platform/tcp_packet.h"
22 #include "quiche/common/quiche_endian.h"
23
24 namespace {
25
26 constexpr size_t kIPv6AddressSize = 16;
27 constexpr size_t kIPv6MinPacketSize = 1280;
28 constexpr size_t kIcmpTtl = 64;
29 constexpr size_t kICMPv6DestinationUnreachableDueToSourcePolicy = 5;
30 constexpr size_t kIPv6DestinationOffset = 8;
31
32 } // namespace
33
34 namespace quic {
35
36 const QuicIpAddress QbonePacketProcessor::kInvalidIpAddress =
37 QuicIpAddress::Any6();
38
QbonePacketProcessor(QuicIpAddress self_ip,QuicIpAddress client_ip,size_t client_ip_subnet_length,OutputInterface * output,StatsInterface * stats)39 QbonePacketProcessor::QbonePacketProcessor(QuicIpAddress self_ip,
40 QuicIpAddress client_ip,
41 size_t client_ip_subnet_length,
42 OutputInterface* output,
43 StatsInterface* stats)
44 : client_ip_(client_ip),
45 output_(output),
46 stats_(stats),
47 filter_(new Filter) {
48 memcpy(self_ip_.s6_addr, self_ip.ToPackedString().data(), kIPv6AddressSize);
49 QUICHE_DCHECK_LE(client_ip_subnet_length, kIPv6AddressSize * 8);
50 client_ip_subnet_length_ = client_ip_subnet_length;
51
52 QUICHE_DCHECK(IpAddressFamily::IP_V6 == self_ip.address_family());
53 QUICHE_DCHECK(IpAddressFamily::IP_V6 == client_ip.address_family());
54 QUICHE_DCHECK(self_ip != kInvalidIpAddress);
55 }
56
~OutputInterface()57 QbonePacketProcessor::OutputInterface::~OutputInterface() {}
~StatsInterface()58 QbonePacketProcessor::StatsInterface::~StatsInterface() {}
~Filter()59 QbonePacketProcessor::Filter::~Filter() {}
60
61 QbonePacketProcessor::ProcessingResult
FilterPacket(Direction direction,absl::string_view full_packet,absl::string_view payload,icmp6_hdr * icmp_header,OutputInterface * output)62 QbonePacketProcessor::Filter::FilterPacket(Direction direction,
63 absl::string_view full_packet,
64 absl::string_view payload,
65 icmp6_hdr* icmp_header,
66 OutputInterface* output) {
67 return ProcessingResult::OK;
68 }
69
ProcessPacket(std::string * packet,Direction direction)70 void QbonePacketProcessor::ProcessPacket(std::string* packet,
71 Direction direction) {
72 uint8_t traffic_class = TrafficClassFromHeader(*packet);
73 if (ABSL_PREDICT_FALSE(!IsValid())) {
74 QUIC_BUG(quic_bug_11024_1)
75 << "QuicPacketProcessor is invoked in an invalid state.";
76 stats_->OnPacketDroppedSilently(direction, traffic_class);
77 return;
78 }
79
80 stats_->RecordThroughput(packet->size(), direction, traffic_class);
81
82 uint8_t transport_protocol;
83 char* transport_data;
84 icmp6_hdr icmp_header;
85 memset(&icmp_header, 0, sizeof(icmp_header));
86 ProcessingResult result = ProcessIPv6HeaderAndFilter(
87 packet, direction, &transport_protocol, &transport_data, &icmp_header);
88
89 in6_addr dst;
90 // TODO(b/70339814): ensure this is actually a unicast address.
91 memcpy(&dst, &packet->data()[kIPv6DestinationOffset], kIPv6AddressSize);
92
93 switch (result) {
94 case ProcessingResult::OK:
95 switch (direction) {
96 case Direction::FROM_OFF_NETWORK:
97 output_->SendPacketToNetwork(*packet);
98 break;
99 case Direction::FROM_NETWORK:
100 output_->SendPacketToClient(*packet);
101 break;
102 }
103 stats_->OnPacketForwarded(direction, traffic_class);
104 break;
105 case ProcessingResult::SILENT_DROP:
106 stats_->OnPacketDroppedSilently(direction, traffic_class);
107 break;
108 case ProcessingResult::DEFER:
109 stats_->OnPacketDeferred(direction, traffic_class);
110 break;
111 case ProcessingResult::ICMP:
112 if (icmp_header.icmp6_type == ICMP6_ECHO_REPLY) {
113 // If this is an ICMP6 ECHO REPLY, the payload should be the same as the
114 // ICMP6 ECHO REQUEST that this came from, not the entire packet. So we
115 // need to take off both the IPv6 header and the ICMP6 header.
116 auto icmp_body = absl::string_view(*packet).substr(sizeof(ip6_hdr) +
117 sizeof(icmp6_hdr));
118 SendIcmpResponse(dst, &icmp_header, icmp_body, direction);
119 } else {
120 SendIcmpResponse(dst, &icmp_header, *packet, direction);
121 }
122 stats_->OnPacketDroppedWithIcmp(direction, traffic_class);
123 break;
124 case ProcessingResult::ICMP_AND_TCP_RESET:
125 SendIcmpResponse(dst, &icmp_header, *packet, direction);
126 stats_->OnPacketDroppedWithIcmp(direction, traffic_class);
127 SendTcpReset(*packet, direction);
128 stats_->OnPacketDroppedWithTcpReset(direction, traffic_class);
129 break;
130 case ProcessingResult::TCP_RESET:
131 SendTcpReset(*packet, direction);
132 stats_->OnPacketDroppedWithTcpReset(direction, traffic_class);
133 break;
134 }
135 }
136
137 QbonePacketProcessor::ProcessingResult
ProcessIPv6HeaderAndFilter(std::string * packet,Direction direction,uint8_t * transport_protocol,char ** transport_data,icmp6_hdr * icmp_header)138 QbonePacketProcessor::ProcessIPv6HeaderAndFilter(std::string* packet,
139 Direction direction,
140 uint8_t* transport_protocol,
141 char** transport_data,
142 icmp6_hdr* icmp_header) {
143 ProcessingResult result = ProcessIPv6Header(
144 packet, direction, transport_protocol, transport_data, icmp_header);
145
146 if (result == ProcessingResult::OK) {
147 char* packet_data = &*packet->begin();
148 size_t header_size = *transport_data - packet_data;
149 // Sanity-check the bounds.
150 if (packet_data >= *transport_data || header_size > packet->size() ||
151 header_size < kIPv6HeaderSize) {
152 QUIC_BUG(quic_bug_11024_2)
153 << "Invalid pointers encountered in "
154 "QbonePacketProcessor::ProcessPacket. Dropping the packet";
155 return ProcessingResult::SILENT_DROP;
156 }
157
158 result = filter_->FilterPacket(
159 direction, *packet,
160 absl::string_view(*transport_data, packet->size() - header_size),
161 icmp_header, output_);
162 }
163
164 // Do not send ICMP error messages in response to ICMP errors.
165 if (result == ProcessingResult::ICMP) {
166 const uint8_t* header = reinterpret_cast<const uint8_t*>(packet->data());
167
168 constexpr size_t kIPv6NextHeaderOffset = 6;
169 constexpr size_t kIcmpMessageTypeOffset = kIPv6HeaderSize + 0;
170 constexpr size_t kIcmpMessageTypeMaxError = 127;
171 if (
172 // Check size.
173 packet->size() >= (kIPv6HeaderSize + kICMPv6HeaderSize) &&
174 // Check that the packet is in fact ICMP.
175 header[kIPv6NextHeaderOffset] == IPPROTO_ICMPV6 &&
176 // Check that ICMP message type is an error.
177 header[kIcmpMessageTypeOffset] < kIcmpMessageTypeMaxError) {
178 result = ProcessingResult::SILENT_DROP;
179 }
180 }
181
182 return result;
183 }
184
ProcessIPv6Header(std::string * packet,Direction direction,uint8_t * transport_protocol,char ** transport_data,icmp6_hdr * icmp_header)185 QbonePacketProcessor::ProcessingResult QbonePacketProcessor::ProcessIPv6Header(
186 std::string* packet, Direction direction, uint8_t* transport_protocol,
187 char** transport_data, icmp6_hdr* icmp_header) {
188 // Check if the packet is big enough to have IPv6 header.
189 if (packet->size() < kIPv6HeaderSize) {
190 QUIC_DVLOG(1) << "Dropped malformed packet: IPv6 header too short";
191 return ProcessingResult::SILENT_DROP;
192 }
193
194 // Check version field.
195 ip6_hdr* header = reinterpret_cast<ip6_hdr*>(&*packet->begin());
196 if (header->ip6_vfc >> 4 != 6) {
197 QUIC_DVLOG(1) << "Dropped malformed packet: IP version is not IPv6";
198 return ProcessingResult::SILENT_DROP;
199 }
200
201 // Check payload size.
202 const size_t declared_payload_size =
203 quiche::QuicheEndian::NetToHost16(header->ip6_plen);
204 const size_t actual_payload_size = packet->size() - kIPv6HeaderSize;
205 if (declared_payload_size != actual_payload_size) {
206 QUIC_DVLOG(1)
207 << "Dropped malformed packet: incorrect packet length specified";
208 return ProcessingResult::SILENT_DROP;
209 }
210
211 // Check that the address of the client is in the packet.
212 QuicIpAddress address_to_check;
213 uint8_t address_reject_code;
214 bool ip_parse_result;
215 switch (direction) {
216 case Direction::FROM_OFF_NETWORK:
217 // Expect the source IP to match the client.
218 ip_parse_result = address_to_check.FromPackedString(
219 reinterpret_cast<const char*>(&header->ip6_src),
220 sizeof(header->ip6_src));
221 address_reject_code = kICMPv6DestinationUnreachableDueToSourcePolicy;
222 break;
223 case Direction::FROM_NETWORK:
224 // Expect the destination IP to match the client.
225 ip_parse_result = address_to_check.FromPackedString(
226 reinterpret_cast<const char*>(&header->ip6_dst),
227 sizeof(header->ip6_src));
228 address_reject_code = ICMP6_DST_UNREACH_NOROUTE;
229 break;
230 }
231 QUICHE_DCHECK(ip_parse_result);
232 if (!client_ip_.InSameSubnet(address_to_check, client_ip_subnet_length_)) {
233 QUIC_DVLOG(1)
234 << "Dropped packet: source/destination address is not client's";
235 icmp_header->icmp6_type = ICMP6_DST_UNREACH;
236 icmp_header->icmp6_code = address_reject_code;
237 return ProcessingResult::ICMP;
238 }
239
240 // Check and decrement TTL.
241 if (header->ip6_hops <= 1) {
242 icmp_header->icmp6_type = ICMP6_TIME_EXCEEDED;
243 icmp_header->icmp6_code = ICMP6_TIME_EXCEED_TRANSIT;
244 return ProcessingResult::ICMP;
245 }
246 header->ip6_hops--;
247
248 // Check and extract IP headers.
249 switch (header->ip6_nxt) {
250 case IPPROTO_TCP:
251 case IPPROTO_UDP:
252 case IPPROTO_ICMPV6:
253 *transport_protocol = header->ip6_nxt;
254 *transport_data = (&*packet->begin()) + kIPv6HeaderSize;
255 break;
256 default:
257 icmp_header->icmp6_type = ICMP6_PARAM_PROB;
258 icmp_header->icmp6_code = ICMP6_PARAMPROB_NEXTHEADER;
259 return ProcessingResult::ICMP;
260 }
261
262 return ProcessingResult::OK;
263 }
264
SendIcmpResponse(in6_addr dst,icmp6_hdr * icmp_header,absl::string_view payload,Direction original_direction)265 void QbonePacketProcessor::SendIcmpResponse(in6_addr dst,
266 icmp6_hdr* icmp_header,
267 absl::string_view payload,
268 Direction original_direction) {
269 CreateIcmpPacket(self_ip_, dst, *icmp_header, payload,
270 [this, original_direction](absl::string_view packet) {
271 SendResponse(original_direction, packet);
272 });
273 }
274
SendTcpReset(absl::string_view original_packet,Direction original_direction)275 void QbonePacketProcessor::SendTcpReset(absl::string_view original_packet,
276 Direction original_direction) {
277 CreateTcpResetPacket(original_packet,
278 [this, original_direction](absl::string_view packet) {
279 SendResponse(original_direction, packet);
280 });
281 }
282
SendResponse(Direction original_direction,absl::string_view packet)283 void QbonePacketProcessor::SendResponse(Direction original_direction,
284 absl::string_view packet) {
285 switch (original_direction) {
286 case Direction::FROM_OFF_NETWORK:
287 output_->SendPacketToClient(packet);
288 break;
289 case Direction::FROM_NETWORK:
290 output_->SendPacketToNetwork(packet);
291 break;
292 }
293 }
294
TrafficClassFromHeader(absl::string_view ipv6_header)295 uint8_t QbonePacketProcessor::TrafficClassFromHeader(
296 absl::string_view ipv6_header) {
297 // Packets that reach this function should have already been validated.
298 // However, there are tests that bypass that validation that fail because this
299 // would be out of bounds.
300 if (ipv6_header.length() < 2) {
301 return 0; // Default to BE1
302 }
303
304 return ipv6_header[0] << 4 | ipv6_header[1] >> 4;
305 }
306 } // namespace quic
307