xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/qbone/qbone_packet_processor.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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