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

// Copyright (c) 2001-2009 XORP, Inc.
//
// 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/ifconfig_transaction.hh,v 1.22 2008/10/02 21:56:47 bms Exp $

#ifndef __FEA_IFCONFIG_TRANSACTION_HH__
#define __FEA_IFCONFIG_TRANSACTION_HH__

#include "libxorp/c_format.hh"
#include "libxorp/transaction.hh"

#include "ifconfig.hh"
#include "iftree.hh"


class IfConfigTransactionManager : public TransactionManager {
public:
    /**
     * Constructor.
     *
     * @param eventloop the event loop to use.
     */
    IfConfigTransactionManager(EventLoop& eventloop)
	: TransactionManager(eventloop, TIMEOUT_MS, MAX_PENDING)
    {}

    /**
     * Get the string with the first error during commit.
     *
     * @return the string with the first error during commit or an empty
     * string if no error.
     */
    const string& error() const 	{ return _first_error; }

protected:
    /**
     * Pre-commit method that is called before the first operation
     * in a commit.
     *
     * This is an overriding method.
     *
     * @param tid the transaction ID.
     */
    virtual void pre_commit(uint32_t tid);

    /**
     * Method that is called after each operation.
     *
     * This is an overriding method.
     *
     * @param success set to true if the operation succeeded, otherwise false.
     * @param op the operation that has been just called.
     */
    virtual void operation_result(bool success,
				  const TransactionOperation& op);

private:
    /**
     * Reset the string with the error.
     */
    void reset_error()			{ _first_error.erase(); }

    string	_first_error;		// The string with the first error
    uint32_t	_tid_exec;		// The transaction ID

    enum { TIMEOUT_MS = 5000, MAX_PENDING = 10 };
};

/**
 * Base class for Interface related operations acting on an
 * IfTree.
 */
class IfConfigTransactionOperation : public TransactionOperation {
public:
    IfConfigTransactionOperation(IfConfig& ifconfig, const string& ifname)
	: _ifconfig(ifconfig),
	  _iftree(ifconfig.user_config()),	// XXX: The iftree to modify
	  _ifname(ifname) {}

    /**
     * @return space separated path description.
     */
    virtual string path() const 		{ return _ifname; }

    /**
     * Get the interface name this operation applies to.
     *
     * @return the interface name this operation applies to.
     */
    const string& ifname() const		{ return _ifname; }

protected:
    /**
     * Get the corresponding interface manager.
     *
     * @return a reference to the corresponding interface manager.
     */
    IfConfig& ifconfig()			{ return _ifconfig; }

    /**
     * Get the interface tree to modify.
     *
     * @return a reference to the interface tree to modify.
     */
    IfTree& iftree()				{ return _iftree; }

private:
    IfConfig&		_ifconfig;
    IfTree&		_iftree;
    const string	_ifname;
};

/**
 * Class for adding an interface.
 */
class AddInterface : public IfConfigTransactionOperation {
public:
    AddInterface(IfConfig& ifconfig, const string& ifname)
	: IfConfigTransactionOperation(ifconfig, ifname) {}

    bool dispatch() {
	if (iftree().add_interface(ifname()) != XORP_OK)
	    return (false);
	return (true);
    }

    string str() const { return string("AddInterface: ") + ifname(); }
};

/**
 * Class for removing an interface.
 */
class RemoveInterface : public IfConfigTransactionOperation {
public:
    RemoveInterface(IfConfig& ifconfig, const string& ifname)
	: IfConfigTransactionOperation(ifconfig, ifname) {}

    bool dispatch() {
	if (iftree().remove_interface(ifname()) != XORP_OK)
	    return (false);
	return (true);
    }

    string str() const {
	return string("RemoveInterface: ") + ifname();
    }
};

/**
 * Class for configuring all interfaces within the FEA by using information
 * from the underlying system.
 */
class ConfigureAllInterfacesFromSystem : public IfConfigTransactionOperation {
public:
    ConfigureAllInterfacesFromSystem(IfConfig& ifconfig, bool enable)
	: IfConfigTransactionOperation(ifconfig, ""),
	  _enable(enable)
    {}

    bool dispatch() {
	if (_enable) {
	    //
	    // Configure all interfaces
	    //
	    const IfTree& dev_config = ifconfig().system_config();
	    IfTree::IfMap::const_iterator iter;
	    for (iter = dev_config.interfaces().begin();
		 iter != dev_config.interfaces().end();
		 ++iter) {
		const IfTreeInterface& iface = *(iter->second);
		if (iftree().update_interface(iface) != XORP_OK)
		    return (false);
	    }
	}

	//
	// Set the "default_system_config" flag for all interfaces
	//
	IfTree::IfMap::iterator iter;
	for (iter = iftree().interfaces().begin();
	     iter != iftree().interfaces().end();
	     ++iter) {
	    IfTreeInterface& iface = *(iter->second);
	    iface.set_default_system_config(_enable);
	}

	return (true);
    }

    string str() const {
	return c_format("ConfigureAllInterfacesFromSystem: %s",
			bool_c_str(_enable));
    }

private:
    bool	_enable;
};

/**
 * Class for configuring an interface within the FEA by using information
 * from the underlying system.
 */
class ConfigureInterfaceFromSystem : public IfConfigTransactionOperation {
public:
    ConfigureInterfaceFromSystem(IfConfig&	ifconfig,
				 const string&	ifname,
				 bool		enable)
	: IfConfigTransactionOperation(ifconfig, ifname),
	  _enable(enable)
    {}

    bool dispatch() {
	IfTreeInterface* ifp = iftree().find_interface(ifname());
	if (ifp == NULL)
	    return (false);
	ifp->set_default_system_config(_enable);
	return (true);
    }

    string str() const {
	return c_format("ConfigureInterfaceFromSystem: %s %s",
			ifname().c_str(), bool_c_str(_enable));
    }

private:
    bool	_enable;
};

/**
 * Base class for interface modifier operations.
 */
class InterfaceModifier : public IfConfigTransactionOperation {
public:
    InterfaceModifier(IfConfig&		ifconfig,
		      const string&	ifname)
	: IfConfigTransactionOperation(ifconfig, ifname) {}

protected:
    IfTreeInterface* interface() {
	IfTreeInterface* ifp = iftree().find_interface(ifname());
	return (ifp);
    }
};

/**
 * Class for setting the enabled state of an interface.
 */
class SetInterfaceEnabled : public InterfaceModifier {
public:
    SetInterfaceEnabled(IfConfig&	ifconfig,
			const string&	ifname,
			bool		enabled)
	: InterfaceModifier(ifconfig, ifname), _enabled(enabled) {}

    bool dispatch() {
	IfTreeInterface* fi = interface();
	if (fi == NULL)
	    return (false);
	fi->set_enabled(_enabled);
	return (true);
    }

    string str() const {
	return c_format("SetInterfaceEnabled: %s %s",
			ifname().c_str(), bool_c_str(_enabled));
    }

private:
    bool _enabled;
};

/**
 * Class for setting the discard state of an interface.
 */
class SetInterfaceDiscard : public InterfaceModifier {
public:
    SetInterfaceDiscard(IfConfig&	ifconfig,
			const string&	ifname,
			bool		discard)
	: InterfaceModifier(ifconfig, ifname), _discard(discard) {}

    bool dispatch() {
	IfTreeInterface* fi = interface();
	if (fi == NULL)
	    return (false);
	fi->set_discard(_discard);
	return (true);
    }

    string str() const {
	return c_format("SetInterfaceDiscard: %s %s",
			ifname().c_str(), bool_c_str(_discard));
    }

private:
    bool _discard;
};

/**
 * Class for setting the unreachable state of an interface.
 */
class SetInterfaceUnreachable : public InterfaceModifier {
public:
    SetInterfaceUnreachable(IfConfig&		ifconfig,
			    const string&	ifname,
			    bool		unreachable)
	: InterfaceModifier(ifconfig, ifname), _unreachable(unreachable) {}

    bool dispatch() {
	IfTreeInterface* fi = interface();
	if (fi == NULL)
	    return (false);
	fi->set_unreachable(_unreachable);
	return (true);
    }

    string str() const {
	return c_format("SetInterfaceUnreachable: %s %s",
			ifname().c_str(), bool_c_str(_unreachable));
    }

private:
    bool _unreachable;
};

/**
 * Class for setting the management state of an interface.
 */
class SetInterfaceManagement : public InterfaceModifier {
public:
    SetInterfaceManagement(IfConfig&		ifconfig,
			   const string&	ifname,
			   bool			management)
	: InterfaceModifier(ifconfig, ifname), _management(management) {}

    bool dispatch() {
	IfTreeInterface* fi = interface();
	if (fi == NULL)
	    return (false);
	fi->set_management(_management);
	return (true);
    }

    string str() const {
	return c_format("SetInterfaceManagement: %s %s",
			ifname().c_str(), bool_c_str(_management));
    }

private:
    bool _management;
};

/**
 * Class for setting an interface MTU.
 */
class SetInterfaceMtu : public InterfaceModifier {
public:
    SetInterfaceMtu(IfConfig&		ifconfig,
		    const string&	ifname,
		    uint32_t		mtu)
	: InterfaceModifier(ifconfig, ifname), _mtu(mtu) {}

    // Minimum and maximum MTU (as defined in RFC 791 and RFC 1191)
    static const uint32_t MIN_MTU = 68;
    static const uint32_t MAX_MTU = 65536;
    
    bool dispatch() {
	IfTreeInterface* fi = interface();
	if (fi == NULL)
	    return (false);

	if (_mtu < MIN_MTU || _mtu > MAX_MTU)
	    return (false);
	
	fi->set_mtu(_mtu);
	return (true);
    }

    string str() const {
	string s = c_format("SetInterfaceMtu: %s %u", ifname().c_str(),
			    XORP_UINT_CAST(_mtu));
	if (_mtu < MIN_MTU || _mtu > MAX_MTU) {
	    s += c_format(" (valid range %u--%u)",
			  XORP_UINT_CAST(MIN_MTU), XORP_UINT_CAST(MAX_MTU));
	}
	return s;
    }

private:
    uint32_t _mtu;
};

/**
 * Class for restoring an interface MTU.
 */
class RestoreInterfaceMtu : public InterfaceModifier {
public:
    RestoreInterfaceMtu(IfConfig&	ifconfig,
			const string&	ifname)
	: InterfaceModifier(ifconfig, ifname) {}

    bool dispatch() {
	// Get the original MTU
	const IfTree& orig_iftree = ifconfig().original_config();
	const IfTreeInterface* orig_fi = orig_iftree.find_interface(ifname());
	if (orig_fi == NULL)
	    return (false);
	uint32_t orig_mtu = orig_fi->mtu();

	IfTreeInterface* fi = interface();
	if (fi == NULL)
	    return (false);
	fi->set_mtu(orig_mtu);
	return (true);
    }

    string str() const {
	return c_format("RestoreInterfaceMtu: %s", ifname().c_str());
    }

private:
};

/**
 * Class for setting an interface MAC.
 */
class SetInterfaceMac : public InterfaceModifier {
public:
    SetInterfaceMac(IfConfig&		ifconfig,
		    const string&	ifname,
		    const Mac&		mac)
	: InterfaceModifier(ifconfig, ifname), _mac(mac) {}

    bool dispatch() {
	IfTreeInterface* fi = interface();
	if (fi == NULL)
	    return (false);
	fi->set_mac(_mac);
	return (true);
    }

    string str() const {
	return c_format("SetInterfaceMac: %s %s",
			ifname().c_str(), _mac.str().c_str());
    }
private:
    Mac _mac;
};

/**
 * Class for restoring an interface MAC.
 */
class RestoreInterfaceMac : public InterfaceModifier {
public:
    RestoreInterfaceMac(IfConfig&	ifconfig,
		    const string&	ifname)
	: InterfaceModifier(ifconfig, ifname) {}

    bool dispatch() {
	// Get the original MAC
	const IfTree& orig_iftree = ifconfig().original_config();
	const IfTreeInterface* orig_fi = orig_iftree.find_interface(ifname());
	if (orig_fi == NULL)
	    return (false);
	const Mac& orig_mac = orig_fi->mac();

	IfTreeInterface* fi = interface();
	if (fi == NULL)
	    return (false);
	fi->set_mac(orig_mac);
	return (true);
    }

    string str() const {
	return c_format("RestoreInterfaceMac: %s", ifname().c_str());
    }

private:
};

/**
 * Class for adding a VIF to an interface.
 */
class AddInterfaceVif : public InterfaceModifier {
public:
    AddInterfaceVif(IfConfig&		ifconfig,
		    const string&	ifname,
		    const string&	vifname)
	: InterfaceModifier(ifconfig, ifname), _vifname(vifname) {}

    bool dispatch() {
	IfTreeInterface* fi = interface();
	if (fi == NULL)
	    return (false);
	fi->add_vif(_vifname);
	return (true);
    }

    string str() const {
	return c_format("AddInterfaceVif: %s %s",
			ifname().c_str(), _vifname.c_str());
    }

private:
    const string _vifname;
};

/**
 * Class for removing a VIF from an interface.
 */
class RemoveInterfaceVif : public InterfaceModifier {
public:
    RemoveInterfaceVif(IfConfig&	ifconfig,
		    const string&	ifname,
		    const string&	vifname)
	: InterfaceModifier(ifconfig, ifname), _vifname(vifname) {}

    bool dispatch() {
	IfTreeInterface* fi = interface();
	if (fi == NULL)
	    return (false);
	if (fi->remove_vif(_vifname) != XORP_OK)
	    return (false);
	return (true);
    }

    string str() const {
	return c_format("RemoveInterfaceVif: %s %s",
			ifname().c_str(), _vifname.c_str());
    }

private:
    const string _vifname;
};

/**
 * Base class for vif modifier operations.
 */
class VifModifier : public InterfaceModifier {
public:
    VifModifier(IfConfig&	ifconfig,
		const string&	ifname,
		const string&	vifname)
	: InterfaceModifier(ifconfig, ifname), _vifname(vifname) {}

    string path() const {
	return InterfaceModifier::path() + string(" ") + vifname();
    }

    const string& vifname() const 		{ return _vifname; }

protected:
    IfTreeVif* vif() {
	IfTreeVif* vifp = iftree().find_vif(ifname(), _vifname);
	return (vifp);
    }

protected:
    const string _vifname;
};

/**
 * Class for setting the enabled state of a vif.
 */
class SetVifEnabled : public VifModifier {
public:
    SetVifEnabled(IfConfig& 	ifconfig,
		  const string&	ifname,
		  const string&	vifname,
		  bool		enabled)
	: VifModifier(ifconfig, ifname, vifname), _enabled(enabled) {}

    bool dispatch() {
	IfTreeVif* fv = vif();
	if (fv == NULL)
	    return (false);
	fv->set_enabled(_enabled);
	return (true);
    }

    string str() const {
	return c_format("SetVifEnabled: %s %s",
			path().c_str(), bool_c_str(_enabled));
    }

private:
    bool _enabled;
};

/**
 * Class for setting the VLAN state of a vif.
 */
class SetVifVlan : public VifModifier {
public:
    SetVifVlan(IfConfig& 	ifconfig,
	       const string&	ifname,
	       const string&	vifname,
	       uint32_t		vlan_id)
	: VifModifier(ifconfig, ifname, vifname), _vlan_id(vlan_id) {}

    // Maximum VLAN ID
    static const uint32_t MAX_VLAN_ID = 4095;

    bool dispatch() {
	IfTreeVif* fv = vif();
	if (fv == NULL)
	    return (false);
	if (_vlan_id > MAX_VLAN_ID)
	    return (false);
	fv->set_vlan(true);
	fv->set_vlan_id(_vlan_id);
	return (true);
    }

    string str() const {
	return c_format("SetVifVlan: %s %u",
			path().c_str(), _vlan_id);
    }

private:
    uint32_t _vlan_id;
};

/**
 * Class for adding an IPv4 address to a VIF.
 */
class AddAddr4 : public VifModifier {
public:
    AddAddr4(IfConfig&		ifconfig,
	     const string&	ifname,
	     const string&	vifname,
	     const IPv4&	addr)
	: VifModifier(ifconfig, ifname, vifname), _addr(addr) {}

    bool dispatch() {
	IfTreeVif* fv = vif();
	if (fv == NULL)
	    return (false);
	fv->add_addr(_addr);
	return (true);
    }

    string str() const {
	return c_format("AddAddr4: %s %s",
			path().c_str(), _addr.str().c_str());
    }

private:
    IPv4 _addr;
};

/**
 * Class for removing an IPv4 address to a VIF.
 */
class RemoveAddr4 : public VifModifier {
public:
    RemoveAddr4(IfConfig&	ifconfig,
		const string&	ifname,
		const string&	vifname,
		const IPv4&	addr)
	: VifModifier(ifconfig, ifname, vifname), _addr(addr) {}

    bool dispatch() {
	IfTreeVif* fv = vif();
	if (fv == NULL)
	    return (false);
	if (fv->remove_addr(_addr) != XORP_OK)
	    return (false);
	return (true);
    }

    string str() const {
	return c_format("RemoveAddr4: %s %s",
			path().c_str(), _addr.str().c_str());
    }

private:
    IPv4 _addr;
};

/**
 * Class for adding an IPv6 address to a VIF.
 */
class AddAddr6 : public VifModifier {
public:
    AddAddr6(IfConfig&		ifconfig,
	     const string&	ifname,
	     const string&	vifname,
	     const IPv6&	addr)
	: VifModifier(ifconfig, ifname, vifname), _addr(addr) {}

    bool dispatch() {
	IfTreeVif* fv = vif();
	if (fv == NULL)
	    return (false);
	if (fv->add_addr(_addr) != XORP_OK)
	    return (false);
	return (true);
    }

    string str() const {
	return c_format("AddAddr6: %s %s",
			path().c_str(), _addr.str().c_str());
    }

private:
    IPv6 _addr;
};

/**
 * Class for removing an IPv6 address to a VIF.
 */
class RemoveAddr6 : public VifModifier {
public:
    RemoveAddr6(IfConfig&	ifconfig,
		const string&	ifname,
		const string&	vifname,
		const IPv6& 	addr)
	: VifModifier(ifconfig, ifname, vifname), _addr(addr) {}

    bool dispatch() {
	IfTreeVif* fv = vif();
	if (fv == NULL)
	    return (false);
	if (fv->remove_addr(_addr) != XORP_OK)
	    return (false);
	return (true);
    }

    string str() const {
	return c_format("RemoveAddr6: %s %s",
			path().c_str(), _addr.str().c_str());
    }

private:
    IPv6 _addr;
};

/**
 * Base class for IPv4vif address modifier operations.
 */
class Addr4Modifier : public VifModifier {
public:
    Addr4Modifier(IfConfig& 	ifconfig,
		  const string&	ifname,
		  const string&	vifname,
		  const IPv4&	addr)
	: VifModifier(ifconfig, ifname, vifname), _addr(addr) {}

    string path() const {
	return VifModifier::path() + string(" ") + _addr.str();
    }

protected:
    IfTreeAddr4* addr() {
	IfTreeAddr4* ap = iftree().find_addr(ifname(), vifname(), _addr);
	return (ap);
    }

protected:
    const IPv4 _addr;
};

/**
 * Class to set enable state of an IPv4 address associated with a vif.
 */
class SetAddr4Enabled : public Addr4Modifier {
public:
    SetAddr4Enabled(IfConfig&		ifconfig,
		    const string&	ifname,
		    const string&	vifname,
		    const IPv4&		addr,
		    bool		enabled)
	: Addr4Modifier(ifconfig, ifname, vifname, addr), _enabled(enabled) {}

    bool dispatch() {
	IfTreeAddr4* fa = addr();
	if (fa == NULL)
	    return (false);
	fa->set_enabled(_enabled);
	return (true);
    }

    string str() const {
	return c_format("SetAddr4Enabled: %s %s",
			path().c_str(), bool_c_str(_enabled));
    }

protected:
    bool _enabled;
};

/**
 * Class to set the prefix of an IPv4 address associated with a vif.
 */
class SetAddr4Prefix : public Addr4Modifier {
public:
    SetAddr4Prefix(IfConfig&		ifconfig,
		   const string&	ifname,
		   const string&	vifname,
		   const IPv4&		addr,
		   uint32_t		prefix_len)
	: Addr4Modifier(ifconfig, ifname, vifname, addr),
	  _prefix_len(prefix_len) {}

    static const uint32_t MAX_PREFIX_LEN = 32;
    
    bool dispatch() {
	IfTreeAddr4* fa = addr();
	if (fa == NULL || _prefix_len > MAX_PREFIX_LEN)
	    return (false);
	if (fa->set_prefix_len(_prefix_len) != XORP_OK)
	    return (false);
	return (true);
    }

    string str() const {
	string s = c_format("SetAddr4Prefix: %s %u", path().c_str(),
			    XORP_UINT_CAST(_prefix_len));
	if (_prefix_len > MAX_PREFIX_LEN)
	    s += c_format(" (valid range 0--%u)",
			  XORP_UINT_CAST(MAX_PREFIX_LEN));
	return s;
    }

protected:
    uint32_t _prefix_len;
};

/**
 * Class to set the endpoint IPv4 address associated with a vif.
 */
class SetAddr4Endpoint : public Addr4Modifier {
public:
    SetAddr4Endpoint(IfConfig&	 	ifconfig,
		     const string&	ifname,
		     const string&	vifname,
		     const IPv4&	addr,
		     const IPv4&	endpoint)
	: Addr4Modifier(ifconfig, ifname, vifname, addr), _endpoint(endpoint) {}

    bool dispatch() {
	IfTreeAddr4* fa = addr();
	if (fa == NULL)
	    return (false);
	fa->set_endpoint(_endpoint);
	fa->set_point_to_point(true);
	return (true);
    }

    string str() const {
	return c_format("SetAddr4Endpoint: %s %s",
			path().c_str(), _endpoint.str().c_str());
    }

protected:
    IPv4 _endpoint;
};

/**
 * Class to set the broadcast address IPv4 address associated with a vif.
 */
class SetAddr4Broadcast : public Addr4Modifier {
public:
    SetAddr4Broadcast(IfConfig&		ifconfig,
		      const string&	ifname,
		      const string&	vifname,
		      const IPv4&	addr,
		      const IPv4&	bcast)
	: Addr4Modifier(ifconfig, ifname, vifname, addr), _bcast(bcast) {}

    bool dispatch() {
	IfTreeAddr4* fa = addr();
	if (fa == NULL)
	    return (false);
	fa->set_bcast(_bcast);
	fa->set_broadcast(true);
	return (true);
    }

    string str() const {
	return c_format("SetAddr4Broadcast: %s %s",
			path().c_str(), _bcast.str().c_str());
    }

protected:
    IPv4 _bcast;
};

/**
 * Base class for IPv6vif address modifier operations.
 */
class Addr6Modifier : public VifModifier {
public:
    Addr6Modifier(IfConfig&	ifconfig,
		  const string&	ifname,
		  const string&	vifname,
		  const IPv6&	addr)
	: VifModifier(ifconfig, ifname, vifname), _addr(addr) {}

    string path() const {
	return VifModifier::path() + string(" ") + _addr.str();
    }

protected:
    IfTreeAddr6* addr() {
	IfTreeAddr6* ap = iftree().find_addr(ifname(), vifname(), _addr);
	return (ap);
    }

protected:
    const IPv6 _addr;
};

/**
 * Class to set the enabled state of an IPv6 address associated with a vif.
 */
class SetAddr6Enabled : public Addr6Modifier {
public:
    SetAddr6Enabled(IfConfig& 		ifconfig,
		    const string&	ifname,
		    const string&	vifname,
		    const IPv6&		addr,
		    bool		enabled)
	: Addr6Modifier(ifconfig, ifname, vifname, addr), _enabled(enabled) {}

    bool dispatch() {
	IfTreeAddr6* fa = addr();
	if (fa == NULL)
	    return (false);
	fa->set_enabled(_enabled);
	return (true);
    }

    string str() const {
	return c_format("SetAddr6Enabled: %s %s",
			path().c_str(), bool_c_str(_enabled));
    }

protected:
    bool _enabled;
};

/**
 * Class to set the prefix of an IPv6 address associated with a vif.
 */
class SetAddr6Prefix : public Addr6Modifier {
public:
    SetAddr6Prefix(IfConfig&		ifconfig,
		   const string&	ifname,
		   const string&	vifname,
		   const IPv6&		addr,
		   uint32_t		prefix_len)
	: Addr6Modifier(ifconfig, ifname, vifname, addr),
	  _prefix_len(prefix_len) {}

    static const uint32_t MAX_PREFIX_LEN = 128;
    
    bool dispatch() {
	IfTreeAddr6* fa = addr();
	if (fa == NULL || _prefix_len > MAX_PREFIX_LEN)
	    return (false);
	if (fa->set_prefix_len(_prefix_len) != XORP_OK)
	    return (false);
	return (true);
    }

    string str() const {
	string s = c_format("SetAddr6Prefix: %s %u", path().c_str(),
			    XORP_UINT_CAST(_prefix_len));
	if (_prefix_len > MAX_PREFIX_LEN)
	    s += c_format(" (valid range 0--%u)",
			  XORP_UINT_CAST(MAX_PREFIX_LEN));
	return s;
    }

protected:
    uint32_t _prefix_len;
};

/**
 * Class to set the endpoint IPv6 address associated with a vif.
 */
class SetAddr6Endpoint : public Addr6Modifier {
public:
    SetAddr6Endpoint(IfConfig& 		ifconfig,
		     const string&	ifname,
		     const string&	vifname,
		     const IPv6&	addr,
		     const IPv6&	endpoint)
	: Addr6Modifier(ifconfig, ifname, vifname, addr), _endpoint(endpoint) {}

    bool dispatch() {
	IfTreeAddr6* fa = addr();
	if (fa == NULL)
	    return (false);
	fa->set_endpoint(_endpoint);
	fa->set_point_to_point(true);
	return (true);
    }

    string str() const {
	return c_format("SetAddr6Endpoint: %s %s",
			path().c_str(), _endpoint.str().c_str());
    }

protected:
    IPv6 _endpoint;
};

#endif // __FEA_IFCONFIG_TRANSACTION_HH__
