| // 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. | |
| #ifndef QUICHE_QUIC_QBONE_QBONE_PACKET_PROCESSOR_H_ | |
| #define QUICHE_QUIC_QBONE_QBONE_PACKET_PROCESSOR_H_ | |
| #include <netinet/icmp6.h> | |
| #include <netinet/ip6.h> | |
| #include "absl/strings/string_view.h" | |
| #include "quic/core/quic_types.h" | |
| #include "quic/platform/api/quic_ip_address.h" | |
| namespace quic { | |
| enum : size_t { | |
| kIPv6HeaderSize = 40, | |
| kICMPv6HeaderSize = sizeof(icmp6_hdr), | |
| kTotalICMPv6HeaderSize = kIPv6HeaderSize + kICMPv6HeaderSize, | |
| }; | |
| // QBONE packet processor accepts packets destined in either direction | |
| // (client-to-network or network-to-client). It inspects them and makes | |
| // decisions on whether they should be forwarded or dropped, replying with ICMP | |
| // messages as appropriate. | |
| class QbonePacketProcessor { | |
| public: | |
| enum class Direction { | |
| // Packet is going from the QBONE client into the network behind the QBONE. | |
| FROM_OFF_NETWORK = 0, | |
| // Packet is going from the network begin QBONE to the client. | |
| FROM_NETWORK = 1 | |
| }; | |
| enum class ProcessingResult { | |
| OK = 0, | |
| SILENT_DROP = 1, | |
| ICMP = 2, | |
| // Equivalent to |SILENT_DROP| at the moment, but indicates that the | |
| // downstream filter has buffered the packet and deferred its processing. | |
| // The packet may be emitted at a later time. | |
| DEFER = 3, | |
| // In addition to sending an ICMP message, also send a TCP RST. This option | |
| // requires the incoming packet to have been a valid TCP packet, as a TCP | |
| // RST requires information from the current connection state to be | |
| // well-formed. | |
| ICMP_AND_TCP_RESET = 4, | |
| }; | |
| class OutputInterface { | |
| public: | |
| virtual ~OutputInterface(); | |
| virtual void SendPacketToClient(absl::string_view packet) = 0; | |
| virtual void SendPacketToNetwork(absl::string_view packet) = 0; | |
| }; | |
| class StatsInterface { | |
| public: | |
| virtual ~StatsInterface(); | |
| virtual void OnPacketForwarded(Direction direction) = 0; | |
| virtual void OnPacketDroppedSilently(Direction direction) = 0; | |
| virtual void OnPacketDroppedWithIcmp(Direction direction) = 0; | |
| virtual void OnPacketDroppedWithTcpReset(Direction direction) = 0; | |
| virtual void OnPacketDeferred(Direction direction) = 0; | |
| }; | |
| // Allows to implement a custom packet filter on top of the filtering done by | |
| // the packet processor itself. | |
| class Filter { | |
| public: | |
| virtual ~Filter(); | |
| // The main interface function. The following arguments are supplied: | |
| // - |direction|, to indicate direction of the packet. | |
| // - |full_packet|, which includes the IPv6 header and possibly the IPv6 | |
| // options that were understood by the processor. | |
| // - |payload|, the contents of the IPv6 packet, i.e. a TCP, a UDP or an | |
| // ICMP packet. | |
| // - |icmp_header|, an output argument which allows the filter to specify | |
| // the ICMP message with which the packet is to be rejected. | |
| // The method is called only on packets which were already verified as valid | |
| // IPv6 packets. | |
| // | |
| // The implementer of this method has four options to return: | |
| // - OK will cause the filter to pass the packet through | |
| // - SILENT_DROP will cause the filter to drop the packet silently | |
| // - ICMP will cause the filter to drop the packet and send an ICMP | |
| // response. | |
| // - DEFER will cause the packet to be not forwarded; the filter is | |
| // responsible for sending (or not sending) it later using |output|. | |
| // | |
| // Note that |output| should not be used except in the DEFER case, as the | |
| // processor will perform the necessary writes itself. | |
| virtual ProcessingResult FilterPacket(Direction direction, | |
| absl::string_view full_packet, | |
| absl::string_view payload, | |
| icmp6_hdr* icmp_header, | |
| OutputInterface* output); | |
| protected: | |
| // Helper methods that allow to easily extract information that is required | |
| // for filtering from the |ipv6_header| argument. All of those assume that | |
| // the header is of valid size, which is true for everything passed into | |
| // FilterPacket(). | |
| inline uint8_t TransportProtocolFromHeader(absl::string_view ipv6_header) { | |
| return ipv6_header[6]; | |
| } | |
| inline QuicIpAddress SourceIpFromHeader(absl::string_view ipv6_header) { | |
| QuicIpAddress address; | |
| address.FromPackedString(&ipv6_header[8], | |
| QuicIpAddress::kIPv6AddressSize); | |
| return address; | |
| } | |
| inline QuicIpAddress DestinationIpFromHeader( | |
| absl::string_view ipv6_header) { | |
| QuicIpAddress address; | |
| address.FromPackedString(&ipv6_header[24], | |
| QuicIpAddress::kIPv6AddressSize); | |
| return address; | |
| } | |
| }; | |
| // |self_ip| is the IP address from which the processor will originate ICMP | |
| // messages. |client_ip| is the expected IP address of the client, used for | |
| // packet validation. | |
| // | |
| // |output| and |stats| are the visitor interfaces used by the processor. | |
| // |output| gets notified whenever the processor decides to send a packet, and | |
| // |stats| gets notified about any decisions that processor makes, without a | |
| // reference to which packet that decision was made about. | |
| QbonePacketProcessor(QuicIpAddress self_ip, | |
| QuicIpAddress client_ip, | |
| size_t client_ip_subnet_length, | |
| OutputInterface* output, | |
| StatsInterface* stats); | |
| QbonePacketProcessor(const QbonePacketProcessor&) = delete; | |
| QbonePacketProcessor& operator=(const QbonePacketProcessor&) = delete; | |
| // Accepts an IPv6 packet and handles it accordingly by either forwarding it, | |
| // replying with an ICMP packet or silently dropping it. |packet| will be | |
| // modified in the process, by having the TTL field decreased. | |
| void ProcessPacket(std::string* packet, Direction direction); | |
| void set_filter(std::unique_ptr<Filter> filter) { | |
| filter_ = std::move(filter); | |
| } | |
| void set_client_ip(QuicIpAddress client_ip) { client_ip_ = client_ip; } | |
| void set_client_ip_subnet_length(size_t client_ip_subnet_length) { | |
| client_ip_subnet_length_ = client_ip_subnet_length; | |
| } | |
| static const QuicIpAddress kInvalidIpAddress; | |
| protected: | |
| // Processes the header and returns what should be done with the packet. | |
| // After that, calls an external packet filter if registered. TTL of the | |
| // packet may be decreased in the process. | |
| ProcessingResult ProcessIPv6HeaderAndFilter(std::string* packet, | |
| Direction direction, | |
| uint8_t* transport_protocol, | |
| char** transport_data, | |
| icmp6_hdr* icmp_header); | |
| void SendIcmpResponse(icmp6_hdr* icmp_header, | |
| absl::string_view original_packet, | |
| Direction original_direction); | |
| void SendTcpReset(absl::string_view original_packet, | |
| Direction original_direction); | |
| inline bool IsValid() const { return client_ip_ != kInvalidIpAddress; } | |
| // IP address of the server. Used to send ICMP messages. | |
| in6_addr self_ip_; | |
| // IP address range of the VPN client. | |
| QuicIpAddress client_ip_; | |
| size_t client_ip_subnet_length_; | |
| OutputInterface* output_; | |
| StatsInterface* stats_; | |
| std::unique_ptr<Filter> filter_; | |
| private: | |
| // Performs basic sanity and permission checks on the packet, and decreases | |
| // the TTL. | |
| ProcessingResult ProcessIPv6Header(std::string* packet, | |
| Direction direction, | |
| uint8_t* transport_protocol, | |
| char** transport_data, | |
| icmp6_hdr* icmp_header); | |
| void SendResponse(Direction original_direction, absl::string_view packet); | |
| }; | |
| } // namespace quic | |
| #endif // QUICHE_QUIC_QBONE_QBONE_PACKET_PROCESSOR_H_ |