wub | f975eac | 2019-08-19 19:41:01 -0700 | [diff] [blame] | 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 | #ifndef QUICHE_QUIC_QBONE_QBONE_PACKET_PROCESSOR_H_ |
| 6 | #define QUICHE_QUIC_QBONE_QBONE_PACKET_PROCESSOR_H_ |
| 7 | |
| 8 | #include <netinet/icmp6.h> |
| 9 | #include <netinet/ip6.h> |
| 10 | |
vasilvv | 1436e34 | 2020-10-09 12:31:16 -0700 | [diff] [blame] | 11 | #include "absl/strings/string_view.h" |
QUICHE team | 5be974e | 2020-12-29 18:35:24 -0500 | [diff] [blame] | 12 | #include "quic/core/quic_types.h" |
| 13 | #include "quic/platform/api/quic_ip_address.h" |
wub | f975eac | 2019-08-19 19:41:01 -0700 | [diff] [blame] | 14 | |
| 15 | namespace quic { |
| 16 | |
| 17 | enum : size_t { |
| 18 | kIPv6HeaderSize = 40, |
| 19 | kICMPv6HeaderSize = sizeof(icmp6_hdr), |
| 20 | kTotalICMPv6HeaderSize = kIPv6HeaderSize + kICMPv6HeaderSize, |
| 21 | }; |
| 22 | |
| 23 | // QBONE packet processor accepts packets destined in either direction |
| 24 | // (client-to-network or network-to-client). It inspects them and makes |
| 25 | // decisions on whether they should be forwarded or dropped, replying with ICMP |
| 26 | // messages as appropriate. |
| 27 | class QbonePacketProcessor { |
| 28 | public: |
| 29 | enum class Direction { |
| 30 | // Packet is going from the QBONE client into the network behind the QBONE. |
QUICHE team | 234c877 | 2019-12-06 14:14:12 -0800 | [diff] [blame] | 31 | FROM_OFF_NETWORK = 0, |
wub | f975eac | 2019-08-19 19:41:01 -0700 | [diff] [blame] | 32 | // Packet is going from the network begin QBONE to the client. |
| 33 | FROM_NETWORK = 1 |
| 34 | }; |
| 35 | |
| 36 | enum class ProcessingResult { |
| 37 | OK = 0, |
| 38 | SILENT_DROP = 1, |
| 39 | ICMP = 2, |
| 40 | // Equivalent to |SILENT_DROP| at the moment, but indicates that the |
| 41 | // downstream filter has buffered the packet and deferred its processing. |
| 42 | // The packet may be emitted at a later time. |
| 43 | DEFER = 3, |
| 44 | // In addition to sending an ICMP message, also send a TCP RST. This option |
| 45 | // requires the incoming packet to have been a valid TCP packet, as a TCP |
| 46 | // RST requires information from the current connection state to be |
| 47 | // well-formed. |
| 48 | ICMP_AND_TCP_RESET = 4, |
| 49 | }; |
| 50 | |
| 51 | class OutputInterface { |
| 52 | public: |
| 53 | virtual ~OutputInterface(); |
| 54 | |
vasilvv | 1436e34 | 2020-10-09 12:31:16 -0700 | [diff] [blame] | 55 | virtual void SendPacketToClient(absl::string_view packet) = 0; |
| 56 | virtual void SendPacketToNetwork(absl::string_view packet) = 0; |
wub | f975eac | 2019-08-19 19:41:01 -0700 | [diff] [blame] | 57 | }; |
| 58 | |
| 59 | class StatsInterface { |
| 60 | public: |
| 61 | virtual ~StatsInterface(); |
| 62 | |
| 63 | virtual void OnPacketForwarded(Direction direction) = 0; |
| 64 | virtual void OnPacketDroppedSilently(Direction direction) = 0; |
| 65 | virtual void OnPacketDroppedWithIcmp(Direction direction) = 0; |
| 66 | virtual void OnPacketDroppedWithTcpReset(Direction direction) = 0; |
| 67 | virtual void OnPacketDeferred(Direction direction) = 0; |
| 68 | }; |
| 69 | |
| 70 | // Allows to implement a custom packet filter on top of the filtering done by |
| 71 | // the packet processor itself. |
| 72 | class Filter { |
| 73 | public: |
| 74 | virtual ~Filter(); |
| 75 | // The main interface function. The following arguments are supplied: |
| 76 | // - |direction|, to indicate direction of the packet. |
| 77 | // - |full_packet|, which includes the IPv6 header and possibly the IPv6 |
| 78 | // options that were understood by the processor. |
| 79 | // - |payload|, the contents of the IPv6 packet, i.e. a TCP, a UDP or an |
| 80 | // ICMP packet. |
| 81 | // - |icmp_header|, an output argument which allows the filter to specify |
| 82 | // the ICMP message with which the packet is to be rejected. |
| 83 | // The method is called only on packets which were already verified as valid |
| 84 | // IPv6 packets. |
| 85 | // |
| 86 | // The implementer of this method has four options to return: |
| 87 | // - OK will cause the filter to pass the packet through |
| 88 | // - SILENT_DROP will cause the filter to drop the packet silently |
| 89 | // - ICMP will cause the filter to drop the packet and send an ICMP |
| 90 | // response. |
| 91 | // - DEFER will cause the packet to be not forwarded; the filter is |
| 92 | // responsible for sending (or not sending) it later using |output|. |
| 93 | // |
| 94 | // Note that |output| should not be used except in the DEFER case, as the |
| 95 | // processor will perform the necessary writes itself. |
| 96 | virtual ProcessingResult FilterPacket(Direction direction, |
vasilvv | 1436e34 | 2020-10-09 12:31:16 -0700 | [diff] [blame] | 97 | absl::string_view full_packet, |
| 98 | absl::string_view payload, |
wub | f975eac | 2019-08-19 19:41:01 -0700 | [diff] [blame] | 99 | icmp6_hdr* icmp_header, |
| 100 | OutputInterface* output); |
| 101 | |
| 102 | protected: |
| 103 | // Helper methods that allow to easily extract information that is required |
| 104 | // for filtering from the |ipv6_header| argument. All of those assume that |
| 105 | // the header is of valid size, which is true for everything passed into |
| 106 | // FilterPacket(). |
vasilvv | 1436e34 | 2020-10-09 12:31:16 -0700 | [diff] [blame] | 107 | inline uint8_t TransportProtocolFromHeader(absl::string_view ipv6_header) { |
wub | f975eac | 2019-08-19 19:41:01 -0700 | [diff] [blame] | 108 | return ipv6_header[6]; |
| 109 | } |
vasilvv | 1436e34 | 2020-10-09 12:31:16 -0700 | [diff] [blame] | 110 | inline QuicIpAddress SourceIpFromHeader(absl::string_view ipv6_header) { |
wub | f975eac | 2019-08-19 19:41:01 -0700 | [diff] [blame] | 111 | QuicIpAddress address; |
| 112 | address.FromPackedString(&ipv6_header[8], |
| 113 | QuicIpAddress::kIPv6AddressSize); |
| 114 | return address; |
| 115 | } |
dmcardle | d70b99e | 2019-12-12 09:52:39 -0800 | [diff] [blame] | 116 | inline QuicIpAddress DestinationIpFromHeader( |
vasilvv | 1436e34 | 2020-10-09 12:31:16 -0700 | [diff] [blame] | 117 | absl::string_view ipv6_header) { |
wub | f975eac | 2019-08-19 19:41:01 -0700 | [diff] [blame] | 118 | QuicIpAddress address; |
| 119 | address.FromPackedString(&ipv6_header[24], |
| 120 | QuicIpAddress::kIPv6AddressSize); |
| 121 | return address; |
| 122 | } |
| 123 | }; |
| 124 | |
| 125 | // |self_ip| is the IP address from which the processor will originate ICMP |
| 126 | // messages. |client_ip| is the expected IP address of the client, used for |
| 127 | // packet validation. |
| 128 | // |
| 129 | // |output| and |stats| are the visitor interfaces used by the processor. |
| 130 | // |output| gets notified whenever the processor decides to send a packet, and |
| 131 | // |stats| gets notified about any decisions that processor makes, without a |
| 132 | // reference to which packet that decision was made about. |
| 133 | QbonePacketProcessor(QuicIpAddress self_ip, |
| 134 | QuicIpAddress client_ip, |
| 135 | size_t client_ip_subnet_length, |
| 136 | OutputInterface* output, |
| 137 | StatsInterface* stats); |
| 138 | QbonePacketProcessor(const QbonePacketProcessor&) = delete; |
| 139 | QbonePacketProcessor& operator=(const QbonePacketProcessor&) = delete; |
| 140 | |
| 141 | // Accepts an IPv6 packet and handles it accordingly by either forwarding it, |
| 142 | // replying with an ICMP packet or silently dropping it. |packet| will be |
| 143 | // modified in the process, by having the TTL field decreased. |
QUICHE team | b80d7c3 | 2020-02-23 23:44:20 -0800 | [diff] [blame] | 144 | void ProcessPacket(std::string* packet, Direction direction); |
wub | f975eac | 2019-08-19 19:41:01 -0700 | [diff] [blame] | 145 | |
| 146 | void set_filter(std::unique_ptr<Filter> filter) { |
| 147 | filter_ = std::move(filter); |
| 148 | } |
| 149 | |
| 150 | void set_client_ip(QuicIpAddress client_ip) { client_ip_ = client_ip; } |
| 151 | void set_client_ip_subnet_length(size_t client_ip_subnet_length) { |
| 152 | client_ip_subnet_length_ = client_ip_subnet_length; |
| 153 | } |
| 154 | |
| 155 | static const QuicIpAddress kInvalidIpAddress; |
| 156 | |
| 157 | protected: |
| 158 | // Processes the header and returns what should be done with the packet. |
| 159 | // After that, calls an external packet filter if registered. TTL of the |
| 160 | // packet may be decreased in the process. |
QUICHE team | b80d7c3 | 2020-02-23 23:44:20 -0800 | [diff] [blame] | 161 | ProcessingResult ProcessIPv6HeaderAndFilter(std::string* packet, |
wub | f975eac | 2019-08-19 19:41:01 -0700 | [diff] [blame] | 162 | Direction direction, |
| 163 | uint8_t* transport_protocol, |
| 164 | char** transport_data, |
| 165 | icmp6_hdr* icmp_header); |
| 166 | |
| 167 | void SendIcmpResponse(icmp6_hdr* icmp_header, |
vasilvv | 1436e34 | 2020-10-09 12:31:16 -0700 | [diff] [blame] | 168 | absl::string_view original_packet, |
wub | f975eac | 2019-08-19 19:41:01 -0700 | [diff] [blame] | 169 | Direction original_direction); |
| 170 | |
vasilvv | 1436e34 | 2020-10-09 12:31:16 -0700 | [diff] [blame] | 171 | void SendTcpReset(absl::string_view original_packet, |
wub | f975eac | 2019-08-19 19:41:01 -0700 | [diff] [blame] | 172 | Direction original_direction); |
| 173 | |
| 174 | inline bool IsValid() const { return client_ip_ != kInvalidIpAddress; } |
| 175 | |
| 176 | // IP address of the server. Used to send ICMP messages. |
| 177 | in6_addr self_ip_; |
| 178 | // IP address range of the VPN client. |
| 179 | QuicIpAddress client_ip_; |
| 180 | size_t client_ip_subnet_length_; |
| 181 | |
| 182 | OutputInterface* output_; |
| 183 | StatsInterface* stats_; |
| 184 | std::unique_ptr<Filter> filter_; |
| 185 | |
| 186 | private: |
| 187 | // Performs basic sanity and permission checks on the packet, and decreases |
| 188 | // the TTL. |
QUICHE team | b80d7c3 | 2020-02-23 23:44:20 -0800 | [diff] [blame] | 189 | ProcessingResult ProcessIPv6Header(std::string* packet, |
wub | f975eac | 2019-08-19 19:41:01 -0700 | [diff] [blame] | 190 | Direction direction, |
| 191 | uint8_t* transport_protocol, |
| 192 | char** transport_data, |
| 193 | icmp6_hdr* icmp_header); |
| 194 | |
vasilvv | 1436e34 | 2020-10-09 12:31:16 -0700 | [diff] [blame] | 195 | void SendResponse(Direction original_direction, absl::string_view packet); |
wub | f975eac | 2019-08-19 19:41:01 -0700 | [diff] [blame] | 196 | }; |
| 197 | |
| 198 | } // namespace quic |
| 199 | #endif // QUICHE_QUIC_QBONE_QBONE_PACKET_PROCESSOR_H_ |