// -*- 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


#ifndef __PIM_PIM_VIF_HH__
#define __PIM_PIM_VIF_HH__


//
// PIM virtual interface definition.
//


#include "libxorp/config_param.hh"
#include "libxorp/vif.hh"
#include "libxorp/timer.hh"
#include "libproto/proto_unit.hh"
#include "mrt/buffer.h"
#include "mrt/mifset.hh"
#include "mrt/multicast_defs.h"
#include "pim_nbr.hh"
#include "pim_proto_join_prune_message.hh"


class AssertMetric;
class BsrZone;
class PimJpGroup;
class PimJpHeader;
class PimNbr;
class PimNode;


struct PVifSet {
public:
    bool proto_version;
    bool hello_triggered_delay;
    bool hello_period;
    bool hello_holdtime;
    bool dr_priority;
    bool propagation_delay;
    bool override_interval;
    bool tracking_disabled;
    bool accept_nohello;
    bool join_prune_period;

    PVifSet() { init(); }

    void init() {
	proto_version = hello_triggered_delay = hello_period = hello_holdtime = dr_priority =
	    propagation_delay = override_interval = tracking_disabled = accept_nohello =
	    join_prune_period = false;
    }
};

struct PVifPermInfo : public VifPermInfo {
public:
    int proto_version;
    uint16_t hello_triggered_delay;
    uint16_t hello_period;
    uint16_t hello_holdtime;
    uint32_t dr_priority;
    uint16_t propagation_delay;
    uint16_t override_interval;
    bool tracking_disabled;
    bool accept_nohello;
    uint16_t join_prune_period;

    PVifSet vset;
    PVifSet vreset;

    PVifPermInfo() : VifPermInfo() {
	init();
    }

    PVifPermInfo(const string& n, bool start, bool enable) : VifPermInfo(n, start, enable) {
	init();
    }

    PVifPermInfo(const PVifPermInfo& p)
	    : VifPermInfo(p) {
	vset = p.vset;
	vreset = p.vreset;
	proto_version = p.proto_version;
	hello_triggered_delay = p.hello_triggered_delay;
	hello_period = p.hello_period;
	hello_holdtime = p.hello_holdtime;
	dr_priority = p.dr_priority;
	propagation_delay = p.propagation_delay;
	override_interval = p.override_interval;
	tracking_disabled = p.tracking_disabled;
	accept_nohello = p.accept_nohello;
	join_prune_period = p.join_prune_period;
    }

    virtual ~PVifPermInfo() { }

    void init() {
	vset.init();
	vreset.init();
	proto_version = 0;
	hello_triggered_delay = 0;
	hello_period = 0;
	hello_holdtime = 0;
	dr_priority = 0;
	propagation_delay = 0;
	override_interval = 0;
	tracking_disabled = false;
	accept_nohello = false;
	join_prune_period = 0;
    }

    PVifPermInfo& operator=(const PVifPermInfo& p) {
	if (this != &p) {
	    VifPermInfo::operator=(p);
	    vset = p.vset;
	    vreset = p.vreset;
	    proto_version = p.proto_version;
	    hello_triggered_delay = p.hello_triggered_delay;
	    hello_period = p.hello_period;
	    hello_holdtime = p.hello_holdtime;
	    dr_priority = p.dr_priority;
	    propagation_delay = p.propagation_delay;
	    override_interval = p.override_interval;
	    tracking_disabled = p.tracking_disabled;
	    accept_nohello = p.accept_nohello;
	    join_prune_period = p.join_prune_period;
	}
	return *this;
    }
};	

extern map<string, PVifPermInfo> perm_info;


/**
 * @short A class for PIM-specific virtual interface.
 */
class PimVif : public ProtoUnit, public Vif {
public:
    /**
     * Constructor for a given PIM node and a generic virtual interface.
     * 
     * @param pim_node the @ref PimNode this interface belongs to.
     * @param vif the generic Vif interface that contains various information.
     */
    PimVif(PimNode* pim_node, const Vif& vif);
    
    /**
     * Destructor
     */
    virtual ~PimVif();

    /**
     * Set configuration to default values.
     */
    void	set_default_config();
    
    /**
     * Set the current protocol version.
     * 
     * The protocol version must be in the interval
     * [PIM_VERSION_MIN, PIM_VERSION_MAX].
     * 
     * @param proto_version the protocol version to set.
     * @return XORP_OK on success, otherwise XORP_ERROR.
     */
    int set_proto_version(int proto_version, string& error_msg);
    
    /**
     *  Start PIM on a single virtual interface.
     * 
     * @param error_msg the error message (if error).
     * @return XORP_OK on success, otherwise XORP_ERROR.
     */
    int start(string& error_msg, const char* dbg);
    
    /** Try to join the PIM routers mcast group. */
    int try_join(string& error_msg);
    void setNeedsJoin(bool v) { needs_join = v; }

    /**  Attempt deferred start.
     */
    void notifyUpdated();

    /**
     * Gracefully stop PIM on a single virtual interface.
     * 
     *The graceful stop will attempt to send Join/Prune, Assert, etc.
     * messages for all multicast routing entries to gracefully clean-up
     * state with neighbors.
     * After the multicast routing entries cleanup is completed,
     * PimVif::final_stop() is called to complete the job.
     * 
     * @param error_msg the error message (if error).
     * @param stay_down  Should we stay stopped even if conditions improve later?
     * @param dbg  Debugging info about why this method was called.
     * @return XORP_OK on success, otherwise XORP_ERROR.
     */
    int stop(string& error_msg, bool stay_down, const char* dbg);
    
    /**
     * Completely stop PIM on a single virtual interface.
     * 
     * This method should be called after @ref PimVif::stop() to complete
     * the job.
     * 
     * @param error_msg the error message (if error).
     * @return XORP_OK on success, otherwise XORP_ERROR.
     */
    int		final_stop(string& error_msg);

    /**
     * Enable PIM on a single virtual interface.
     * 
     * If an unit is not enabled, it cannot be start, or pending-start.
     */
    void enable(const char* dbg);
    
    /**
     * Disable PIM on a single virtual interface.
     * 
     * If an unit is disabled, it cannot be start or pending-start.
     * If the unit was runnning, it will be stop first.
     */
    void disable(const char* dbg);
    
    /**
     * Receive a protocol message.
     * 
     * @param src the source address of the message.
     * @param dst the destination address of the message.
     * @param buffer the data buffer with the received message.
     * @return XORP_OK on success, otherwise XORP_ERROR.
     */
    int		pim_recv(const IPvX& src, const IPvX& dst, buffer_t *buffer);
    
    /**
     * Get the string with the flags about the vif status.
     * 
     * TODO: temporary here. Should go to the Vif class after the Vif
     * class starts using the Proto class.
     * 
     * @return the C++ style string with the flags about the vif status
     * (e.g., UP/DOWN/DISABLED, etc).
     */
    string	flags_string() const;
    
    /**
     * Get the PIM node (@ref PimNode).
     * 
     * @return a reference to the PIM node (@ref PimNode).
     */
    PimNode* pim_node() const { return _pim_node; }
    
    /**
     * Get the PIM Multicast Routing Table (@ref PimMrt).
     * 
     * @return a reference to the PIM Multicast Routing Table (@ref PimMrt).
     */
    PimMrt&	pim_mrt() const;
    
    /**
     * Get the PIM neighbor information (@ref PimNbr) about myself.
     * 
     * @return a reference to the PIM neighbor information (@ref PimNbr)
     * about myself.
     */
    PimNbr&	pim_nbr_me()		{ return (_pim_nbr_me);		}
    

    /** Restarts hello if we are not a pim-register vif */
    void check_restart_hello(string& error_msg);
    void check_restart_elect(string& error_msg);
    void check_hello_send(string& error_msg);


    /**
     * Start the PIM Hello operation.
     */
    void	pim_hello_start();
    
    /**
     * Stop the PIM Hello operation.
     */
    void	pim_hello_stop();
    
    /**
     * Elect a Designated Router on this interface.
     */
    void	pim_dr_elect();
    
    /**
     * Compute if I may become the Designated Router on this interface
     * if one of the PIM neighbor addresses is not considered.
     * 
     * Compute if I may become the DR on this interface if @ref exclude_addr
     * is excluded.
     * 
     * @param exclude_addr the address to exclude in the computation.
     * @return true if I may become the DR on this interface, otherwise
     * false.
     */
    bool	i_may_become_dr(const IPvX& exclude_addr);
    
    /**
     * Get my primary address on this interface.
     * 
     * @return my primary address on this interface.
     */
    const IPvX&	primary_addr() const	{ return (_pim_nbr_me.primary_addr()); }

    /**
     * Get my domain-wide reachable address on this interface.
     * 
     * @return my domain-wide reachable address on this interface.
     */
    const IPvX&	domain_wide_addr() const	{ return (_domain_wide_addr); }

    /**
     * Set my domain-wide reachable address on this interface.
     * 
     * @param v the value of the domain-wide reachable address.
     */
    void	set_domain_wide_addr(const IPvX& v) { _domain_wide_addr = v; }

    /**
     * Update the primary and the domain-wide reachable addresses.
     * 
     * The primary address should be a link-local unicast address, and
     * is used for transmitting the multicast control packets on the LAN.
     * The domain-wide reachable address is the address that should be
     * reachable by all PIM-SM routers in the domain
     * (e.g., the Cand-BSR, or the Cand-RP address).
     * 
     * @param error_msg the error message (if error).
     * @return XORP_OK on success, otherwise XORP_ERROR.
     */
    int		update_primary_and_domain_wide_address(string& error_msg);

    /**
     * Get the address of the Designated Router on this interface.
     * 
     * @return the address of the Designated Router on this interface.
     */
    const IPvX&	dr_addr() const		{ return (_dr_addr);		}

    //
    // Hello-related configuration parameters
    //
    ConfigParam<uint16_t>& hello_triggered_delay() { return (_hello_triggered_delay); }
    ConfigParam<uint16_t>& hello_period() { return (_hello_period); }
    ConfigParam<uint16_t>& hello_holdtime() { return (_hello_holdtime); }
    ConfigParam<uint32_t>& dr_priority() { return (_dr_priority); }
    ConfigParam<uint16_t>& propagation_delay() { return (_propagation_delay); }
    ConfigParam<uint16_t>& override_interval() { return (_override_interval); }
    ConfigParam<bool>& is_tracking_support_disabled() { return (_is_tracking_support_disabled); }
    ConfigParam<bool>& accept_nohello_neighbors() { return (_accept_nohello_neighbors); }
    
    //
    // Hello-related non-configurable parameters
    //
    ConfigParam<uint32_t>& genid() { return (_genid); }
    
    //
    // Join/Prune-related configuration parameters
    //
    ConfigParam<uint16_t>& join_prune_period() { return (_join_prune_period); }
    ConfigParam<uint16_t>& join_prune_holdtime() { return (_join_prune_holdtime); }
    
    //
    // Assert-related configuration parameters
    //
    ConfigParam<uint32_t>& assert_time() { return (_assert_time); }
    ConfigParam<uint32_t>& assert_override_interval() { return (_assert_override_interval); }
    
    //
    // Functions for sending protocol messages.
    //
    int		pim_send(const IPvX& src, const IPvX& dst,
			 uint8_t message_type, buffer_t *buffer,
			 string& error_msg);
    int		pim_hello_send(string& error_msg);
    int		pim_hello_first_send();
    int		pim_join_prune_send(PimNbr *pim_nbr, PimJpHeader *jp_header,
				    string& error_msg);
    int		pim_assert_mre_send(PimMre *pim_mre,
				    const IPvX& assert_source_addr,
				    string& error_msg);
    int		pim_assert_cancel_send(PimMre *pim_mre, string& error_msg);
    int		pim_assert_send(const IPvX& assert_source_addr,
				const IPvX& assert_group_addr,
				bool rpt_bit,
				uint32_t metric_preference,
				uint32_t metric,
				string& error_msg);
    int		pim_register_send(const IPvX& rp_addr,
				  const IPvX& source_addr,
				  const IPvX& group_addr,
				  const uint8_t *rcvbuf,
				  size_t rcvlen,
				  string& error_msg);
    int		pim_register_null_send(const IPvX& rp_addr,
				       const IPvX& source_addr,
				       const IPvX& group_addr,
				       string& error_msg);
    int		pim_register_stop_send(const IPvX& dr_addr,
				       const IPvX& source_addr,
				       const IPvX& group_addr,
				       string& error_msg);
    int		pim_bootstrap_send(const IPvX& dst_addr,
				   const BsrZone& bsr_zone,
				   string& error_msg);
    buffer_t	*pim_bootstrap_send_prepare(const IPvX& src_addr,
					    const IPvX& dst_addr,
					    const BsrZone& bsr_zone,
					    bool is_first_fragment);
    int		pim_cand_rp_adv_send(const IPvX& bsr_addr,
				     const BsrZone& bsr_zone);
    
    void	hello_timer_start(uint32_t sec, uint32_t usec);
    void	hello_timer_start_random(uint32_t sec, uint32_t usec);
    
    bool	is_lan_delay_enabled() const;
    // Link-related time intervals
    const TimeVal& effective_propagation_delay() const;
    const TimeVal& effective_override_interval() const;
    bool	is_lan_suppression_state_enabled() const;
    const TimeVal& upstream_join_timer_t_suppressed() const;
    const TimeVal& upstream_join_timer_t_override() const;
    
    // Misc. functions
    const TimeVal& jp_override_interval() const;
    list<PimNbr *>& pim_nbrs() { return (_pim_nbrs); }
    size_t	pim_nbrs_number() const { return (_pim_nbrs.size()); }
    bool	i_am_dr() const;
    void	set_i_am_dr(bool v);
    PimNbr	*pim_nbr_find(const IPvX& nbr_addr);
    void	add_pim_nbr(PimNbr *pim_nbr);
    int		delete_pim_nbr(PimNbr *pim_nbr);
    void	delete_pim_nbr_from_nbr_list(PimNbr *pim_nbr);
    const list<IPvXNet>& alternative_subnet_list() const { return _alternative_subnet_list; }
    void add_alternative_subnet(const IPvXNet& subnet);
    void delete_alternative_subnet(const IPvXNet& subnet);
    void remove_all_alternative_subnets();

    // Usage-related functions
    size_t	usage_by_pim_mre_task() const { return (_usage_by_pim_mre_task); }
    void	incr_usage_by_pim_mre_task();
    void	decr_usage_by_pim_mre_task();
    
    /**
     * Calculate the checksum of an IPv6 "pseudo-header" as described
     * in RFC 2460.
     * 
     * @param src the source address of the pseudo-header.
     * @param dst the destination address of the pseudo-header.
     * @param len the upper-layer packet length of the pseudo-header
     * (in host-order).
     * @param protocol the upper-layer protocol number.
     * @return the checksum of the IPv6 "pseudo-header".
     */
    uint16_t	calculate_ipv6_pseudo_header_checksum(const IPvX& src,
						      const IPvX& dst,
						      size_t len,
						      uint8_t protocol);
    buffer_t	*buffer_send_prepare();
    buffer_t	*buffer_send_prepare(buffer_t *buffer);

    //
    // Statistics-related counters and values
    //
    void	clear_pim_statistics();
    //
    uint32_t	pimstat_hello_messages_received() const { return _pimstat_hello_messages_received.get(); }
    uint32_t	pimstat_hello_messages_sent() const { return _pimstat_hello_messages_sent.get(); }
    uint32_t	pimstat_hello_messages_rx_errors() const { return _pimstat_hello_messages_rx_errors.get(); }
    uint32_t	pimstat_register_messages_received() const { return _pimstat_register_messages_received.get(); }
    uint32_t	pimstat_register_messages_sent() const { return _pimstat_register_messages_sent.get(); }
    uint32_t	pimstat_register_messages_rx_errors() const { return _pimstat_register_messages_rx_errors.get(); }
    uint32_t	pimstat_register_stop_messages_received() const { return _pimstat_register_stop_messages_received.get(); }
    uint32_t	pimstat_register_stop_messages_sent() const { return _pimstat_register_stop_messages_sent.get(); }
    uint32_t	pimstat_register_stop_messages_rx_errors() const { return _pimstat_register_stop_messages_rx_errors.get(); }
    uint32_t	pimstat_join_prune_messages_received() const { return _pimstat_join_prune_messages_received.get(); }
    uint32_t	pimstat_join_prune_messages_sent() const { return _pimstat_join_prune_messages_sent.get(); }
    uint32_t	pimstat_join_prune_messages_rx_errors() const { return _pimstat_join_prune_messages_rx_errors.get(); }
    uint32_t	pimstat_bootstrap_messages_received() const { return _pimstat_bootstrap_messages_received.get(); }
    uint32_t	pimstat_bootstrap_messages_sent() const { return _pimstat_bootstrap_messages_sent.get(); }
    uint32_t	pimstat_bootstrap_messages_rx_errors() const { return _pimstat_bootstrap_messages_rx_errors.get(); }
    uint32_t	pimstat_assert_messages_received() const { return _pimstat_assert_messages_received.get(); }
    uint32_t	pimstat_assert_messages_sent() const { return _pimstat_assert_messages_sent.get(); }
    uint32_t	pimstat_assert_messages_rx_errors() const { return _pimstat_assert_messages_rx_errors.get(); }
    uint32_t	pimstat_graft_messages_received() const { return _pimstat_graft_messages_received.get(); }
    uint32_t	pimstat_graft_messages_sent() const { return _pimstat_graft_messages_sent.get(); }
    uint32_t	pimstat_graft_messages_rx_errors() const { return _pimstat_graft_messages_rx_errors.get(); }
    uint32_t	pimstat_graft_ack_messages_received() const { return _pimstat_graft_ack_messages_received.get(); }
    uint32_t	pimstat_graft_ack_messages_sent() const { return _pimstat_graft_ack_messages_sent.get(); }
    uint32_t	pimstat_graft_ack_messages_rx_errors() const { return _pimstat_graft_ack_messages_rx_errors.get(); }
    uint32_t	pimstat_candidate_rp_messages_received() const { return _pimstat_candidate_rp_messages_received.get(); }
    uint32_t	pimstat_candidate_rp_messages_sent() const { return _pimstat_candidate_rp_messages_sent.get(); }
    uint32_t	pimstat_candidate_rp_messages_rx_errors() const { return _pimstat_candidate_rp_messages_rx_errors.get(); }
    //
    uint32_t	pimstat_unknown_type_messages() const { return _pimstat_unknown_type_messages.get(); }
    uint32_t	pimstat_unknown_version_messages() const { return _pimstat_unknown_version_messages.get(); }
    uint32_t	pimstat_neighbor_unknown_messages() const { return _pimstat_neighbor_unknown_messages.get(); }
    uint32_t	pimstat_bad_length_messages() const { return _pimstat_bad_length_messages.get(); }
    uint32_t	pimstat_bad_checksum_messages() const { return _pimstat_bad_checksum_messages.get(); }
    uint32_t	pimstat_bad_receive_interface_messages() const { return _pimstat_bad_receive_interface_messages.get(); }
    uint32_t	pimstat_rx_interface_disabled_messages() const { return _pimstat_rx_interface_disabled_messages.get(); }
    uint32_t	pimstat_rx_register_not_rp() const { return _pimstat_rx_register_not_rp.get(); }
    uint32_t	pimstat_rp_filtered_source() const { return _pimstat_rp_filtered_source.get(); }
    uint32_t	pimstat_unknown_register_stop() const { return _pimstat_unknown_register_stop.get(); }
    uint32_t	pimstat_rx_join_prune_no_state() const { return _pimstat_rx_join_prune_no_state.get(); }
    uint32_t	pimstat_rx_graft_graft_ack_no_state() const { return _pimstat_rx_graft_graft_ack_no_state.get(); }
    uint32_t	pimstat_rx_graft_on_upstream_interface() const { return _pimstat_rx_graft_on_upstream_interface.get(); }
    uint32_t	pimstat_rx_candidate_rp_not_bsr() const { return _pimstat_rx_candidate_rp_not_bsr.get(); }
    uint32_t	pimstat_rx_bsr_when_bsr() const { return _pimstat_rx_bsr_when_bsr.get(); }
    uint32_t	pimstat_rx_bsr_not_rpf_interface() const { return _pimstat_rx_bsr_not_rpf_interface.get(); }
    uint32_t	pimstat_rx_unknown_hello_option() const { return _pimstat_rx_unknown_hello_option.get(); }
    uint32_t	pimstat_rx_data_no_state() const { return _pimstat_rx_data_no_state.get(); }
    uint32_t	pimstat_rx_rp_no_state() const { return _pimstat_rx_rp_no_state.get(); }
    uint32_t	pimstat_rx_aggregate() const { return _pimstat_rx_aggregate.get(); }
    uint32_t	pimstat_rx_malformed_packet() const { return _pimstat_rx_malformed_packet.get(); }
    uint32_t	pimstat_no_rp() const { return _pimstat_no_rp.get(); }
    uint32_t	pimstat_no_route_upstream() const { return _pimstat_no_route_upstream.get(); }
    uint32_t	pimstat_rp_mismatch() const { return _pimstat_rp_mismatch.get(); }
    uint32_t	pimstat_rpf_neighbor_unknown() const { return _pimstat_rpf_neighbor_unknown.get(); }
    //
    uint32_t	pimstat_rx_join_rp() const { return _pimstat_rx_join_rp.get(); }
    uint32_t	pimstat_rx_prune_rp() const { return _pimstat_rx_prune_rp.get(); }
    uint32_t	pimstat_rx_join_wc() const { return _pimstat_rx_join_wc.get(); }
    uint32_t	pimstat_rx_prune_wc() const { return _pimstat_rx_prune_wc.get(); }
    uint32_t	pimstat_rx_join_sg() const { return _pimstat_rx_join_sg.get(); }
    uint32_t	pimstat_rx_prune_sg() const { return _pimstat_rx_prune_sg.get(); }
    uint32_t	pimstat_rx_join_sg_rpt() const { return _pimstat_rx_join_sg_rpt.get(); }
    uint32_t	pimstat_rx_prune_sg_rpt() const { return _pimstat_rx_prune_sg_rpt.get(); }
    
private:
    // Private functions
    void	hello_timer_timeout();
    void	hello_once_timer_timeout();
    
    //
    // Callbacks for configuration and non-configurable parameters
    //
    void	set_hello_period_callback(uint16_t v) {
	uint16_t old_hello_holdtime_divided
	    = (uint16_t) (_hello_holdtime.get() / PIM_HELLO_HELLO_HOLDTIME_PERIOD_RATIO);
	if (v != old_hello_holdtime_divided)
	    _hello_holdtime.set(
		(uint16_t)(v * PIM_HELLO_HELLO_HOLDTIME_PERIOD_RATIO));
	_pim_nbr_me.set_hello_holdtime(_hello_holdtime.get());
    }
    void	set_hello_holdtime_callback(uint16_t v) {
	uint16_t new_hello_period
	    = (uint16_t)(v / PIM_HELLO_HELLO_HOLDTIME_PERIOD_RATIO);
	if (_hello_period.get() != new_hello_period)
	    _hello_period.set(new_hello_period);
	_pim_nbr_me.set_hello_holdtime(_hello_holdtime.get());
    }
    void	set_dr_priority_callback(uint32_t v) {
	_pim_nbr_me.set_dr_priority(v);
	_pim_nbr_me.set_is_dr_priority_present(true);
    }
    void	set_propagation_delay_callback(uint16_t v) {
	_pim_nbr_me.set_propagation_delay(v);
	_pim_nbr_me.set_is_lan_prune_delay_present(true);
    }
    void	set_override_interval_callback(uint16_t v) {
	_pim_nbr_me.set_override_interval(v);
	_pim_nbr_me.set_is_lan_prune_delay_present(true);
    }
    void	set_is_tracking_support_disabled_callback(bool v) {
	_pim_nbr_me.set_is_tracking_support_disabled(v);
    }
    void	set_genid_callback(uint32_t v) {
	_pim_nbr_me.set_genid(v);
	_pim_nbr_me.set_is_genid_present(true);
    }
    void	set_join_prune_period_callback(uint16_t v) {
	_join_prune_holdtime.set(
	    (uint16_t)(v * PIM_JOIN_PRUNE_HOLDTIME_PERIOD_RATIO));
    }
    
    
    int jp_entry_add(const IPvX& source_addr, const IPvX& group_addr,
		     mrt_entry_type_t mrt_entry_type, action_jp_t action_jp,
		     uint16_t holdtime);
    int jp_entry_flush();
    
    bool is_send_unicast_bootstrap() const {
	return (! _send_unicast_bootstrap_nbr_list.empty());
    }
    void add_send_unicast_bootstrap_nbr(const IPvX& nbr_addr) {
	_send_unicast_bootstrap_nbr_list.push_back(nbr_addr);
    }
    const list<IPvX>& send_unicast_bootstrap_nbr_list() const {
	return (_send_unicast_bootstrap_nbr_list);
    }
    void delete_send_unicast_bootstrap_nbr_list() {
	_send_unicast_bootstrap_nbr_list.clear();
    }

    bool should_send_pim_hello() const { return (_should_send_pim_hello); }
    void set_should_send_pim_hello(bool v) { _should_send_pim_hello = v; }
    
    // Private state
    PimNode*	_pim_node;		// The PIM node I belong to
    buffer_t	*_buffer_send;		// Buffer for sending messages
    buffer_t	*_buffer_send_hello;	// Buffer for sending Hello messages
    buffer_t	*_buffer_send_bootstrap;// Buffer for sending Bootstrap msgs
    enum {
	PIM_VIF_DR	= 1 << 0	// I am the Designated Router
    };
    uint32_t	_proto_flags;		// Various flags (PIM_VIF_*)
    IPvX	_dr_addr;		// IP address of the current DR
    XorpTimer	_hello_timer;		// Timer to send a HELLO message
    XorpTimer	_hello_once_timer;	// Timer to send once a HELLO message
    list<PimNbr *> _pim_nbrs;		// List of all PIM neighbors
    PimNbr	_pim_nbr_me;		// Myself (for misc. purpose)
    IPvX	_domain_wide_addr;	// The domain-wide reachable address on
					// this vif
    list<IPvX>	_send_unicast_bootstrap_nbr_list; // List of new nbrs to
						  // unicast to them the
						  // Bootstrap message.

    // The alternative subnets on a vif. Used to make incoming traffic with a
    // non-local source address to appear as it is coming from a local subnet.
    list<IPvXNet> _alternative_subnet_list;

    //
    // Misc configuration parameters
    //

    //
    // Hello-related configuration parameters
    //
    ConfigParam<uint16_t> _hello_triggered_delay; // The Triggered_Hello_Delay
    ConfigParam<uint16_t> _hello_period;	// The Hello_Period
    ConfigParam<uint16_t> _hello_holdtime;	// The Hello_Holdtime
    ConfigParam<uint32_t> _dr_priority;		// The DR Priority
    ConfigParam<uint16_t> _propagation_delay;	// The Propagation_Delay
    ConfigParam<uint16_t> _override_interval;	// The Override_Interval
    ConfigParam<bool>	  _is_tracking_support_disabled; // The T-bit
    ConfigParam<bool>	  _accept_nohello_neighbors; // If true, accept
						// neighbors that didn't send
						// a Hello message first
    
    //
    // Hello-related non-configurable parameters
    //
    ConfigParam<uint32_t> _genid;		// The Generation ID
    
    //
    // Join/Prune-related configuration parameters
    //
    ConfigParam<uint16_t> _join_prune_period;	// The period between J/P msgs
    ConfigParam<uint16_t> _join_prune_holdtime;	// The holdtime in J/P msgs
    
    //
    // Assert-related configuration parameters
    //
    ConfigParam<uint32_t> _assert_time;		// The Assert_Time
    ConfigParam<uint32_t> _assert_override_interval; // The Assert_Override_Interval
    
    bool	_should_send_pim_hello;	// True if PIM_HELLO should be sent
					// before any other control messages
    
    //
    // Statistics-related counters and values
    //
    ConfigParam<uint32_t> _pimstat_hello_messages_received;
    ConfigParam<uint32_t> _pimstat_hello_messages_sent;
    ConfigParam<uint32_t> _pimstat_hello_messages_rx_errors;
    ConfigParam<uint32_t> _pimstat_register_messages_received;
    ConfigParam<uint32_t> _pimstat_register_messages_sent;
    ConfigParam<uint32_t> _pimstat_register_messages_rx_errors;
    ConfigParam<uint32_t> _pimstat_register_stop_messages_received;
    ConfigParam<uint32_t> _pimstat_register_stop_messages_sent;
    ConfigParam<uint32_t> _pimstat_register_stop_messages_rx_errors;
    ConfigParam<uint32_t> _pimstat_join_prune_messages_received;
    ConfigParam<uint32_t> _pimstat_join_prune_messages_sent;
    ConfigParam<uint32_t> _pimstat_join_prune_messages_rx_errors;
    ConfigParam<uint32_t> _pimstat_bootstrap_messages_received;
    ConfigParam<uint32_t> _pimstat_bootstrap_messages_sent;
    ConfigParam<uint32_t> _pimstat_bootstrap_messages_rx_errors;
    ConfigParam<uint32_t> _pimstat_assert_messages_received;
    ConfigParam<uint32_t> _pimstat_assert_messages_sent;
    ConfigParam<uint32_t> _pimstat_assert_messages_rx_errors;
    ConfigParam<uint32_t> _pimstat_graft_messages_received;
    ConfigParam<uint32_t> _pimstat_graft_messages_sent;
    ConfigParam<uint32_t> _pimstat_graft_messages_rx_errors;
    ConfigParam<uint32_t> _pimstat_graft_ack_messages_received;
    ConfigParam<uint32_t> _pimstat_graft_ack_messages_sent;
    ConfigParam<uint32_t> _pimstat_graft_ack_messages_rx_errors;
    ConfigParam<uint32_t> _pimstat_candidate_rp_messages_received;
    ConfigParam<uint32_t> _pimstat_candidate_rp_messages_sent;
    ConfigParam<uint32_t> _pimstat_candidate_rp_messages_rx_errors;
    //
    ConfigParam<uint32_t> _pimstat_unknown_type_messages;
    ConfigParam<uint32_t> _pimstat_unknown_version_messages;
    ConfigParam<uint32_t> _pimstat_neighbor_unknown_messages;
    ConfigParam<uint32_t> _pimstat_bad_length_messages;
    ConfigParam<uint32_t> _pimstat_bad_checksum_messages;
    ConfigParam<uint32_t> _pimstat_bad_receive_interface_messages; // XXX: unused
    ConfigParam<uint32_t> _pimstat_rx_interface_disabled_messages;
    ConfigParam<uint32_t> _pimstat_rx_register_not_rp;
    ConfigParam<uint32_t> _pimstat_rp_filtered_source;		// XXX: unused
    ConfigParam<uint32_t> _pimstat_unknown_register_stop;
    ConfigParam<uint32_t> _pimstat_rx_join_prune_no_state;	// XXX: unused
    ConfigParam<uint32_t> _pimstat_rx_graft_graft_ack_no_state;	// XXX: unused
    ConfigParam<uint32_t> _pimstat_rx_graft_on_upstream_interface; // XXX: unused
    ConfigParam<uint32_t> _pimstat_rx_candidate_rp_not_bsr;
    ConfigParam<uint32_t> _pimstat_rx_bsr_when_bsr;		// XXX: unused
    ConfigParam<uint32_t> _pimstat_rx_bsr_not_rpf_interface;
    ConfigParam<uint32_t> _pimstat_rx_unknown_hello_option;
    ConfigParam<uint32_t> _pimstat_rx_data_no_state;		// XXX: unused
    ConfigParam<uint32_t> _pimstat_rx_rp_no_state;		// XXX: unused
    ConfigParam<uint32_t> _pimstat_rx_aggregate;		// XXX: unused
    ConfigParam<uint32_t> _pimstat_rx_malformed_packet;
    ConfigParam<uint32_t> _pimstat_no_rp;			// XXX: unused
    ConfigParam<uint32_t> _pimstat_no_route_upstream;		// XXX: unused
    ConfigParam<uint32_t> _pimstat_rp_mismatch;			// XXX: unused
    ConfigParam<uint32_t> _pimstat_rpf_neighbor_unknown;	// XXX: unused
    //
    ConfigParam<uint32_t> _pimstat_rx_join_rp;
    ConfigParam<uint32_t> _pimstat_rx_prune_rp;
    ConfigParam<uint32_t> _pimstat_rx_join_wc;
    ConfigParam<uint32_t> _pimstat_rx_prune_wc;
    ConfigParam<uint32_t> _pimstat_rx_join_sg;
    ConfigParam<uint32_t> _pimstat_rx_prune_sg;
    ConfigParam<uint32_t> _pimstat_rx_join_sg_rpt;
    ConfigParam<uint32_t> _pimstat_rx_prune_sg_rpt;
    
    size_t	_usage_by_pim_mre_task;	// Counter for usage by PimMreTask
    
    // Not-so handy private functions that should go somewhere else
    // PIM control messages recv functions
    int		pim_hello_recv(PimNbr *pim_nbr, const IPvX& src,
			       const IPvX& dst, buffer_t *buffer,
			       int nbr_proto_version);
    int		pim_register_recv(PimNbr *pim_nbr, const IPvX& src,
				  const IPvX& dst, buffer_t *buffer);
    int		pim_register_stop_recv(PimNbr *pim_nbr, const IPvX& src,
				       const IPvX& dst, buffer_t *buffer);
    int		pim_join_prune_recv(PimNbr *pim_nbr, const IPvX& src,
				    const IPvX& dst, buffer_t *buffer,
				    uint8_t message_type);
    int		pim_bootstrap_recv(PimNbr *pim_nbr, const IPvX& src,
				   const IPvX& dst, buffer_t *buffer);
    int		pim_assert_recv(PimNbr *pim_nbr, const IPvX& src,
				const IPvX& dst, buffer_t *buffer);
    int		pim_graft_recv(PimNbr *pim_nbr, const IPvX& src,
			       const IPvX& dst, buffer_t *buffer);
    int		pim_graft_ack_recv(PimNbr *pim_nbr, const IPvX& src,
				   const IPvX& dst, buffer_t *buffer);
    int		pim_cand_rp_adv_recv(PimNbr *pim_nbr, const IPvX& src,
				     const IPvX& dst, buffer_t *buffer);
    
    // PIM control messages process functions
    int		pim_process(const IPvX& src, const IPvX& dst, buffer_t *buffer);
    int		pim_assert_process(PimNbr *pim_nbr,
				   const IPvX& src,
				   const IPvX& dst,
				   const IPvX& assert_source_addr,
				   const IPvX& assert_group_addr,
				   uint8_t assert_group_mask_len,
				   AssertMetric *assert_metric);
    int		pim_register_stop_process(const IPvX& rp_addr,
					  const IPvX& source_addr,
					  const IPvX& group_addr,
					  uint8_t group_mask_len);
    bool wants_to_be_started; // as soon as we can, ie if the interface appears.
    bool needs_join; // Need to (re)join the pim mcast group?
};

#endif // __PIM_PIM_VIF_HH__
