// See the file "COPYING" in the main distribution directory for copyright.

#pragma once

#include "zeek/IPAddr.h"
#include "zeek/TunnelEncapsulation.h"
#include "zeek/packet_analysis/Analyzer.h"
#include "zeek/packet_analysis/Component.h"

namespace zeek::packet_analysis::IPTunnel {

namespace detail {
class IPTunnelTimer;
}

class IPTunnelAnalyzer : public Analyzer {
public:
    IPTunnelAnalyzer();
    ~IPTunnelAnalyzer() override = default;

    bool AnalyzePacket(size_t len, const uint8_t* data, Packet* packet) override;

    static zeek::packet_analysis::AnalyzerPtr Instantiate() { return std::make_shared<IPTunnelAnalyzer>(); }

    /**
     * Wrapper that handles encapsulated IP packets and passes them back into
     * packet analysis.
     *
     * @param t Network time.
     * @param pkt If the outer pcap header is available, this pointer can be set
     *        so the fake pcap header passed to the next analyzer will use the
     *        same timeval. The caplen and len fields of the fake pcap header
     *        are always set to the TotalLength() of \a inner.
     * @param inner Pointer to IP header wrapper of the inner packet, ownership
     *        of the pointer's memory is assumed by this function.
     * @param prev Any previous encapsulation stack of the caller, not including
     *        the most-recently found depth of encapsulation.
     * @param ec The most-recently found depth of encapsulation.
     */
    bool ProcessEncapsulatedPacket(double t, const Packet* pkt, const std::shared_ptr<IP_Hdr>& inner,
                                   std::shared_ptr<EncapsulationStack> prev, const EncapsulatingConn& ec);

    /**
     * Wrapper that handles encapsulated Ethernet/IP packets and passes them back into
     * packet analysis.
     *
     * @param t Network time.
     * @param pkt If the outer pcap header is available, this pointer can be set
     *        so the fake pcap header passed to the next analyzer will use the
     *        same timeval.
     * @param caplen Number of captured bytes remaining
     * @param len Number of bytes remaining as claimed by outer framing
     * @param data The remaining packet data
     * @param link_type Layer 2 link type used for initializing inner packet
     * @param prev Any previous encapsulation stack of the caller, not
     *        including the most-recently found depth of encapsulation.
     * @param ec The most-recently found depth of encapsulation.
     */
    bool ProcessEncapsulatedPacket(double t, const Packet* pkt, uint32_t caplen, uint32_t len, const u_char* data,
                                   int link_type, std::shared_ptr<EncapsulationStack> prev,
                                   const EncapsulatingConn& ec);

protected:
    friend class detail::IPTunnelTimer;

    using IPPair = std::pair<IPAddr, IPAddr>;
    using TunnelActivity = std::pair<EncapsulatingConn, double>;
    using IPTunnelMap = std::map<IPPair, TunnelActivity>;
    IPTunnelMap ip_tunnels;
};

/**
 * Utility function for packet analyzers for encapsulation/tunnel protocols. This
 * builds a new packet object containing the encapsulated/tunneled packet, as well
 * as adding to the associated encapsulation stack for the tunnel.
 *
 * The wire length (pkt->len) of the inner packet is computed based on the wire length
 * of the outer packet and the differences in capture lengths.
 *
 * The inner packet's timestamp is set to the same value as the outer packet's timestamp.
 *
 * @param outer_pkt The packet containing the encapsulation. This packet should contain
 * @param encap_index A return value for the current index into the encapsulation stack.
 * This is returned to allow analyzers to know what point in the stack they were operating
 * on as the packet analysis chain unwinds as it returns.
 * @param encap_stack Tracks the encapsulations as the new encapsulations are discovered
 * in the inner packets.
 * @param inner_cap_len The byte length of the packet data contained in the inner packet.
 * Also used as capture length for the inner packet.
 * @param data A pointer to the first byte of the inner packet.
 * @param link_type The link type (DLT_*) for the outer packet. If not known, DLT_RAW can
 * be passed for this value.
 * @param tunnel_type The type of tunnel the inner packet is stored in.
 * @param analyzer_tag The tag for the analyzer calling this method.
 * return A new packet object describing the encapsulated packet and data.
 */
extern std::unique_ptr<Packet> build_inner_packet(Packet* outer_pkt, int* encap_index,
                                                  std::shared_ptr<EncapsulationStack> encap_stack,
                                                  uint32_t inner_cap_len, const u_char* data, int link_type,
                                                  BifEnum::Tunnel::Type tunnel_type, const Tag& analyzer_tag);

namespace detail {

class IPTunnelTimer final : public zeek::detail::Timer {
public:
    IPTunnelTimer(double t, IPTunnelAnalyzer::IPPair p, IPTunnelAnalyzer* analyzer);
    ~IPTunnelTimer() override = default;

    void Dispatch(double t, bool is_expire) override;

protected:
    IPTunnelAnalyzer::IPPair tunnel_idx;
    IPTunnelAnalyzer* analyzer;
};

} // namespace detail

// This is temporary until the TCP and UDP analyzers are moved to be packet analyzers.
extern IPTunnelAnalyzer* ip_tunnel_analyzer;

} // namespace zeek::packet_analysis::IPTunnel
