// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*-

// Copyright (c) 2001-2011 XORP, Inc and Others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License, Version 2, June
// 1991 as published by the Free Software Foundation. Redistribution
// and/or modification of this program under the terms of any other
// version of the GNU General Public License is not permitted.
// 
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details,
// see the GNU General Public License, Version 2, a copy of which can be
// found in the XORP LICENSE.gpl file.
// 
// XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA;
// http://xorp.net

// $XORP: xorp/fea/mfea_dataflow.hh,v 1.10 2008/10/02 21:56:49 bms Exp $


#ifndef __FEA_MFEA_DATAFLOW_HH__
#define __FEA_MFEA_DATAFLOW_HH__


//
// MFEA (Multicast Forwarding Engine Abstraction) dataflow definition.
//




#include "libxorp/timer.hh"
#include "libproto/proto_unit.hh"
#include "mrt/mrt.hh"
#include "mfea_mrouter.hh"


//
// Constants definitions
//


//
// Structures/classes, typedefs and macros
//

class MfeaDfe;
class MfeaDfeLookup;
class MfeaNode;

/**
 * @short The MFEA (S,G) dataflow table for monitoring forwarded bandwidth.
 */
class MfeaDft : public Mrt<MfeaDfeLookup> {
public:
    /**
     * Constructor for a given MFEA node.
     * 
     * @param mfea_node the @ref MfeaNode this table belongs to.
     */
    MfeaDft(MfeaNode& mfea_node);
    
    /**
     * Destructor
     */
    virtual ~MfeaDft();
    
    /**
     * Get a reference to to the MFEA node this table belongs to.
     * 
     * @return a reference to the MFEA node (@ref MfeaNode) this table
     * belongs to.
     */
    MfeaNode&	mfea_node() const { return (_mfea_node); }
    
    /**
     * Get the address family.
     * 
     * @return the address family (e.g., AF_INET or AF_INET6
     * for IPv4 and IPv6 respectively).
     */
    int		family() const;
    
    /**
     * Add a dataflow entry.
     * 
     * Note: either @ref is_threshold_in_packets or @ref is_threshold_in_bytes
     * (or both) must be true.
     * Note: either @ref is_geq_upcall or @ref is_leq_upcall
     * (but not both) must be true.
     * 
     * @param source the source address.
     * @param group the group address.
     * @param threshold_interval the dataflow threshold interval.
     * @param threshold_packets the threshold (in number of packets)
     * to compare against.
     * @param threshold_bytes the threshold (in number of bytes)
     * to compare against.
     * @param is_threshold_in_packets if true, @ref threshold_packets is valid.
     * @param is_threshold_in_bytes if true, @ref threshold_bytes is valid.
     * @param is_geq_upcall if true, the operation for comparison is ">=".
     * @param is_leq_upcall if true, the operation for comparison is "<=".
     * @param error_msg the error message (if error).
     * @return XORP_OK on success, otherwise XORP_ERROR.
     */
    int		add_entry(const IPvX& source, const IPvX& group,
			  const TimeVal& threshold_interval,
			  uint32_t threshold_packets,
			  uint32_t threshold_bytes,
			  bool is_threshold_in_packets,
			  bool is_threshold_in_bytes,
			  bool is_geq_upcall,
			  bool is_leq_upcall,
			  string& error_msg);
    
    /**
     * Delete a dataflow entry.
     * 
     * Note: either @ref is_threshold_in_packets or @ref is_threshold_in_bytes
     * (or both) must be true.
     * Note: either @ref is_geq_upcall or @ref is_leq_upcall
     * (but not both) must be true.
     * 
     * @param source the source address.
     * @param group the group address.
     * @param threshold_interval the dataflow threshold interval.
     * @param threshold_packets the threshold (in number of packets)
     * to compare against.
     * @param threshold_bytes the threshold (in number of bytes)
     * to compare against.
     * @param is_threshold_in_packets if true, @ref threshold_packets is valid.
     * @param is_threshold_in_bytes if true, @ref threshold_bytes is valid.
     * @param is_geq_upcall if true, the operation for comparison is ">=".
     * @param is_leq_upcall if true, the operation for comparison is "<=".
     * @param error_msg the error message (if error).
     * @return XORP_OK on success, otherwise XORP_ERROR.
     */
    int		delete_entry(const IPvX& source, const IPvX& group,
			     const TimeVal& threshold_interval,
			     uint32_t threshold_packets,
			     uint32_t threshold_bytes,
			     bool is_threshold_in_packets,
			     bool is_threshold_in_bytes,
			     bool is_geq_upcall,
			     bool is_leq_upcall,
			     string& error_msg);
    
    /**
     * Delete all dataflow entries for a given source and group address.
     * 
     * @param source the source address.
     * @param group the group address.
     * @return XORP_OK on success, otherwise XORP_ERROR.
     */
    int		delete_entry(const IPvX& source, const IPvX& group);
    
private:
    /**
     * Delete a given @ref MfeaDfe dataflow entry.
     * 
     * @param mfea_dfe the @ref MfeaDfe dataflow entry to delete.
     * @return XORP_OK on success, otherwise XORP_ERROR.
     */
    int		delete_entry(MfeaDfe *mfea_dfe);
    
    MfeaNode&	_mfea_node;	// The Mfea node
};

/**
 * @short A class for storing all dataflow entries per (S,G).
 */
class MfeaDfeLookup : public Mre<MfeaDfeLookup> {
public:
    /**
     * Constructor for a given dataflow table, source and group address.
     * 
     * @param mfea_dft the dataflow table (@ref MfeaDft) this entries
     * belongs to.
     * @param source the source address.
     * @param group the group address.
     */
    MfeaDfeLookup(MfeaDft& mfea_dft, const IPvX& source, const IPvX& group);
    
    /**
     * Destructor
     */
    ~MfeaDfeLookup();
    
    /**
     * Get a reference to the dataflow table this entry belongs to.
     * 
     * @return a reference to the dataflow table (@ref MfeaDataflow) this
     * entry belongs to.
     */
    MfeaDft& mfea_dft() const { return (_mfea_dft); }
    
    /**
     * Get the address family.
     * 
     * @return the address family (e.g., AF_INET or AF_INET6 for IPv4 and
     * IPv6 respectively).
     */
    int		family() const;
    
    /**
     * Find a @ref MfeaDfe dataflow entry.
     * 
     * Note: either @ref is_threshold_in_packets or @ref is_threshold_in_bytes
     * (or both) must be true.
     * Note: either @ref is_geq_upcall or @ref is_leq_upcall
     * (but not both) must be true.
     * 
     * @param threshold_interval the dataflow threshold interval.
     * @param threshold_packets the threshold (in number of packets)
     * to compare against.
     * @param threshold_bytes the threshold (in number of bytes)
     * to compare against.
     * @param is_threshold_in_packets if true, @ref threshold_packets is valid.
     * @param is_threshold_in_bytes if true, @ref threshold_bytes is valid.
     * @param is_geq_upcall if true, the operation for comparison is ">=".
     * @param is_leq_upcall if true, the operation for comparison is "<=".
     * @return the corresponding @ref MfeaDfe dataflow entry on success,
     * otherwise NULL.
     */
    MfeaDfe	*find(const TimeVal& threshold_interval,
		      uint32_t threshold_packets,
		      uint32_t threshold_bytes,
		      bool is_threshold_in_packets,
		      bool is_threshold_in_bytes,
		      bool is_geq_upcall,
		      bool is_leq_upcall);
    
    /**
     * Insert a @ref MfeaDfe dataflow entry.
     * 
     * @param mfea_dfe the @ref MfeaDfe dataflow entry to insert.
     */
    void	insert(MfeaDfe *mfea_dfe);

    /**
     * Remove a @ref MfeaDfe dataflow entry.
     * 
     * @param mfea_dfe the @ref MfeaDfe dataflow entry to remove.
     */
    void	remove(MfeaDfe *mfea_dfe);
    
    /**
     * Test if there are @ref MfeaDfe entries inserted within this entry.
     * 
     * @return true if there are @ref MfeaDfe entries inserted within this
     * entry, otherwise false.
     */
    bool	is_empty() const { return (_mfea_dfe_list.empty()); }
    
    /**
     * Get the list of @ref MfeaDfe dataflow entries for the same (S,G).
     * 
     * @return the list of @ref MfeaDfe dataflow entries for the same (S,G).
     */
    list<MfeaDfe *>&	mfea_dfe_list() { return (_mfea_dfe_list); }
    
private:
    MfeaDft&		_mfea_dft;	// The Mfea dataflow table (yuck!)
    list<MfeaDfe *>	_mfea_dfe_list;	// The list of dataflow monitor entries
};

/**
 * @short Multicast dataflow entry class.
 * 
 * This entry contains all the information about the condition a dataflow
 * must satisfy to deliver a signal. It is (S,G)-specific, but there could be
 * more than one @ref MfeaDfe entries per (S,G).
 */
class MfeaDfe {
public:
    /**
     * Constructor with information with the dataflow condition to satisfy.
     * 
     * Note: either @ref is_threshold_in_packets or @ref is_threshold_in_bytes
     * (or both) must be true.
     * Note: either @ref is_geq_upcall or @ref is_leq_upcall
     * (but not both) must be true.
     * 
     * @param mfea_dfe_lookup the @ref MfeaDfeLookup entry this entry
     * belongs to.
     * @param threshold_interval the dataflow threshold interval.
     * @param threshold_packets the threshold (in number of packets)
     * to compare against.
     * @param threshold_bytes the threshold (in number of bytes)
     * to compare against.
     * @param is_threshold_in_packets if true, @ref threshold_packets is valid.
     * @param is_threshold_in_bytes if true, @ref threshold_bytes is valid.
     * @param is_geq_upcall if true, the operation for comparison is ">=".
     * @param is_leq_upcall if true, the operation for comparison is "<=".
     */
    MfeaDfe(MfeaDfeLookup& mfea_dfe_lookup,
	    const TimeVal& threshold_interval,
	    uint32_t threshold_packets,
	    uint32_t threshold_bytes,
	    bool is_threshold_in_packets,
	    bool is_threshold_in_bytes,
	    bool is_geq_upcall,
	    bool is_leq_upcall);
    
    /**
     * Destructor
     */
    ~MfeaDfe();
    
    /**
     * Get a reference to the @ref MfeaDfeLookup entry this entry belongs to.
     * 
     * @return a reference to the @ref MfeaDfeLookup entry this entry
     * belongs to.
     */
    MfeaDfeLookup& mfea_dfe_lookup() const { return (_mfea_dfe_lookup); }
    
    /**
     * Get a reference to the @ref MfeaDft dataflow table this entry
     * belongs to.
     * 
     * @return a reference to the @ref MfeaDft dataflow table this entry
     * belongs to.
     */
    MfeaDft& mfea_dft() const;
    
    /**
     * Get a reference to the @ref EventLoop.
     * 
     * @return a reference to the @ref EventLoop.
     */
    EventLoop& eventloop() const;
    
    /**
     * Get the address family.
     * 
     * @return the address family (e.g., AF_INET or AF_INET6
     * for IPv4 and IPv6 respectively).
     */
    int		family() const;
    
    /**
     * Get the source address.
     * 
     * @return the source address.
     */
    const IPvX& source_addr() const { return (_mfea_dfe_lookup.source_addr());}
    
    /**
     * Get the group address.
     * 
     * @return the group address.
     */
    const IPvX& group_addr() const { return (_mfea_dfe_lookup.group_addr()); }
    
    /**
     * Test if this entry is valid.
     * 
     * An entry is valid if, for example:
     * (a) either @ref is_threshold_in_packets or @ref is_threshold_in_bytes
     * (or both) must be true.
     * (b) either @ref is_geq_upcall or @ref is_leq_upcall
     * (but not both) must be true.
     * (c) the threshold interval is not too small.
     * (d) the bandwidth-related statistics do not contain invalid values.
     * 
     * @return true if this entry is valid, otherwise false.
     */
    bool is_valid() const;
    
    /**
     * Compare whether the information contained within this @ref MfeaDfe
     * entry is same as the specified information.
     * 
     * @param threshold_interval_test the dataflow threshold interval.
     * @param threshold_packets_test the threshold (in number of packets)
     * to compare against.
     * @param threshold_bytes_test the threshold (in number of bytes)
     * to compare against.
     * @param is_threshold_in_packets_test if true, @ref threshold_packets is
     * valid.
     * @param is_threshold_in_bytes_test if true, @ref threshold_bytes is
     * valid.
     * @param is_geq_upcall_test if true, the operation for comparison is ">=".
     * @param is_leq_upcall_test if true, the operation for comparison is "<=".
     * @return true if the information contained within this @ref MfeaDfe
     * entry is same as the specified information, otherwise false.
     */
    bool is_same(const TimeVal& threshold_interval_test,
		 uint32_t threshold_packets_test,
		 uint32_t threshold_bytes_test,
		 bool is_threshold_in_packets_test,
		 bool is_threshold_in_bytes_test,
		 bool is_geq_upcall_test,
		 bool is_leq_upcall_test) const;
    
    /**
     * Initialize this entry with the current multicast forwarding information.
     * 
     * The current multicast forwarding bandwidth information is read from
     * the kernel.
     */
    void init_sg_count();
    
    /**
     * Test if the dataflow bandwidth satisfies the pre-defined condition.
     * 
     * The multicast forwarding bandwidth information is read from
     * the kernel, and then is tested whether is above/below the
     * pre-defined threshold.
     * 
     * @return true if the dataflow bandwidth satisifes the pre-defined
     * condition, otherwise false.
     * Note: if both "is_threshold_in_packets" and "is_threshold_in_bytes"
     * are true, then return true if the test is positive for either unit
     * (i.e., packets or bytes).
     */
    bool test_sg_count();
    
    /**
     * Start bandwidth measurement.
     */
    void start_measurement();
    
    /**
     * Send a dataflow signal that the pre-defined condition is true.
     */
    void dataflow_signal_send();
    
    /**
     * Get the threshold interval.
     * 
     * @return the threshold interval for this dataflow entry.
     */
    const TimeVal& threshold_interval() const { return (_threshold_interval); }
    
    /**
     * Get the threshold packets.
     * 
     * @return the threshold packets for this dataflow entry.
     */
    uint32_t threshold_packets() const { return (_threshold_packets); }
    
    /**
     * Get the threshold bytes.
     * 
     * @return the threshold bytes for this dataflow entry.
     */
    uint32_t threshold_bytes() const { return (_threshold_bytes); }
    
    /**
     * Test if the threshold is in number of packets.
     * 
     * @return true if the threshold is in number of packets.
     */
    bool is_threshold_in_packets() const { return (_is_threshold_in_packets); }
    
    /**
     * Test if the threshold is in number of bytes.
     * 
     * @return true if the threshold is in number of bytes.
     */
    bool is_threshold_in_bytes() const { return (_is_threshold_in_bytes); }
    
    /**
     * Test if the threshold type is "greater-or-equal" (i.e., ">=").
     * 
     * @return true if the threshold type is "greater-or-equal" (i.e., ">=").
     */
    bool is_geq_upcall() const { return (_is_geq_upcall); }
    
    /**
     * Test if the threshold type is "less-or-equal" (i.e., "<=").
     * 
     * @return true if the threshold type is "less-or-equal" (i.e., "<=").
     */
    bool is_leq_upcall() const { return (_is_leq_upcall); }

    /**
     * Get the start time for the most recent measurement interval window.
     * 
     * @return the start time for the most recent measurement interval window.
     */
    const TimeVal& start_time() const;
    
    /**
     * Get the number of packets measured in the most recent interval window.
     * 
     * @return the number of packets measured in the most recent interval
     * window.
     */
    uint32_t measured_packets() const;
    
    /**
     * Get the number of bytes measured in the most recent interval window.
     * 
     * @return the number of bytes measured in the most recent interval
     * window.
     */
    uint32_t measured_bytes() const;
    
    
private:
    // Private methods
    void measurement_timer_timeout();
    
    // Private state
    MfeaDfeLookup& _mfea_dfe_lookup;  // The Mfea dataflow lookup entry (yuck!)
    TimeVal	_threshold_interval;	// The threshold interval
    uint32_t	_threshold_packets;	// The threshold value (in packets)
    uint32_t	_threshold_bytes;	// The threshold value (in bytes)
    bool	_is_threshold_in_packets; // If true, _threshold_packets is
					  // valid
    bool	_is_threshold_in_bytes;	// If true, _threshold_bytes is valid
    bool	_is_geq_upcall;		// If true, the operation is ">=".
    bool	_is_leq_upcall;		// If true, the operation is "<=".
    
#define MFEA_DATAFLOW_TEST_FREQUENCY	4
    SgCount	_last_sg_count;		// Last measurement result
    SgCount	_measured_sg_count;	// Measured result
    SgCount	_delta_sg_count[MFEA_DATAFLOW_TEST_FREQUENCY]; // Delta measurement result
    size_t	_delta_sg_count_index; // Index into next '_delta_sg_count'
    bool	_is_bootstrap_completed;
    
    TimeVal	_measurement_interval;	// Interval between two measurements
    XorpTimer	_measurement_timer;	// Timer to perform measurements
    
    // Time when current measurement window has started
    // XXX: used for debug purpose only
    TimeVal	_start_time[MFEA_DATAFLOW_TEST_FREQUENCY];
};


//
// Global variables
//


//
// Global functions prototypes
//

#endif // __FEA_MFEA_DATAFLOW_HH__
