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

// Copyright (c) 2001-2007 International Computer Science Institute
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software")
// to deal in the Software without restriction, subject to the conditions
// listed in the XORP LICENSE file. These conditions include: you must
// preserve this copyright notice, and you cannot mention the copyright
// holders in advertising related to the Software without their permission.
// The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
// notice is a summary of the XORP LICENSE file; the license in that file is
// legally binding.

#ident "$XORP: xorp/fea/mfea_config.cc,v 1.18 2007/02/16 22:45:45 pavlin Exp $"

//
// TODO: a temporary solution for various MFEA configuration
//

#include "mfea_module.h"

#include "libxorp/xorp.h"
#include "libxorp/xlog.h"
#include "libxorp/debug.h"
#include "libxorp/ipvx.hh"

#include "mfea_node.hh"
#include "mfea_vif.hh"

/**
 * Add a configured vif.
 * 
 * @param vif the vif with the information to add.
 * @param error_msg the error message (if error).
 * @return XORP_OK on success, otherwise XORP_ERROR.
 */
int
MfeaNode::add_config_vif(const Vif& vif, string& error_msg)
{
    //
    // Perform all the micro-operations that are required to add a vif.
    //
    if (add_config_vif(vif.name(), vif.vif_index(), error_msg) < 0)
	return (XORP_ERROR);
    
    list<VifAddr>::const_iterator vif_addr_iter;
    for (vif_addr_iter = vif.addr_list().begin();
	 vif_addr_iter != vif.addr_list().end();
	 ++vif_addr_iter) {
	const VifAddr& vif_addr = *vif_addr_iter;
	if (add_config_vif_addr(vif.name(),
				vif_addr.addr(),
				vif_addr.subnet_addr(),
				vif_addr.broadcast_addr(),
				vif_addr.peer_addr(),
				error_msg) < 0) {
	    return (XORP_ERROR);
	}
    }
    if (set_config_pif_index(vif.name(),
			     vif.pif_index(),
			     error_msg) < 0) {
	return (XORP_ERROR);
    }
    if (set_config_vif_flags(vif.name(),
			     vif.is_pim_register(),
			     vif.is_p2p(),
			     vif.is_loopback(),
			     vif.is_multicast_capable(),
			     vif.is_broadcast_capable(),
			     vif.is_underlying_vif_up(),
			     vif.mtu(),
			     error_msg) < 0) {
	return (XORP_ERROR);
    }
    
    return (XORP_OK);
}

/**
 * Add a configured vif.
 * 
 * @param vif_name the name of the vif to add.
 * @param vif_index the vif index of the vif to add.
 * @param error_msg the error message (if error).
 * @return XORP_OK on success, otherwise XORP_ERROR.
 */
int
MfeaNode::add_config_vif(const string& vif_name,
			 uint32_t vif_index,
			 string& error_msg)
{
    if (ProtoNode<MfeaVif>::add_config_vif(vif_name, vif_index, error_msg) < 0)
	return (XORP_ERROR);
    
    //
    // Send the message to all upper-layer protocols that expect it.
    //
    ProtoRegister& pr = _proto_register;
    const list<pair<string, xorp_module_id> >& module_list = pr.all_module_instance_name_list();
    list<pair<string, xorp_module_id> >::const_iterator iter;
    for (iter = module_list.begin(); iter != module_list.end(); ++iter) {
        const string& dst_module_instance_name = (*iter).first;
        xorp_module_id dst_module_id = (*iter).second;
        send_add_config_vif(dst_module_instance_name, dst_module_id,
			    vif_name, vif_index);
    }
    
    return (XORP_OK);
}

/**
 * Delete a configured vif.
 * 
 * @param vif_name the name of the vif to delete.
 * @param error_msg the error message (if error).
 * @return XORP_OK on success, otherwise XORP_ERROR.
 */
int
MfeaNode::delete_config_vif(const string& vif_name,
			    string& error_msg)
{
    if (ProtoNode<MfeaVif>::delete_config_vif(vif_name, error_msg) < 0)
	return (XORP_ERROR);
    
    //
    // Send the message to all upper-layer protocols that expect it.
    //
    ProtoRegister& pr = _proto_register;
    const list<pair<string, xorp_module_id> >& module_list = pr.all_module_instance_name_list();
    list<pair<string, xorp_module_id> >::const_iterator iter;
    for (iter = module_list.begin(); iter != module_list.end(); ++iter) {
        const string& dst_module_instance_name = (*iter).first;
        xorp_module_id dst_module_id = (*iter).second;
        send_delete_config_vif(dst_module_instance_name, dst_module_id,
			       vif_name);
    }
    
    return (XORP_OK);
}

/**
 * Add an address to a configured vif.
 * 
 * @param vif_name the name of the vif.
 * @param addr the address to add.
 * @param subnet the subnet address to add.
 * @param broadcast the broadcast address to add.
 * @param peer the peer address to add.
 * @param error_msg the error message (if error).
 * @return XORP_OK on success, otherwise XORP_ERROR.
 */
int
MfeaNode::add_config_vif_addr(const string& vif_name,
			      const IPvX& addr,
			      const IPvXNet& subnet,
			      const IPvX& broadcast,
			      const IPvX& peer,
			      string& error_msg)
{
    if (ProtoNode<MfeaVif>::add_config_vif_addr(vif_name, addr, subnet,
						broadcast, peer, error_msg)
	< 0)
	return (XORP_ERROR);
    
    //
    // Send the message to all upper-layer protocols that expect it.
    //
    ProtoRegister& pr = _proto_register;
    const list<pair<string, xorp_module_id> >& module_list = pr.all_module_instance_name_list();
    list<pair<string, xorp_module_id> >::const_iterator iter;
    for (iter = module_list.begin(); iter != module_list.end(); ++iter) {
        const string& dst_module_instance_name = (*iter).first;
        xorp_module_id dst_module_id = (*iter).second;
        send_add_config_vif_addr(dst_module_instance_name, dst_module_id,
				 vif_name, addr, subnet, broadcast, peer);
    }
    
    return (XORP_OK);
}
    
/**
 * Delete an address from a configured vif.
 * 
 * @param vif_name the name of the vif.
 * @param addr the address to delete.
 * @param error_msg the error message (if error).
 * @return XORP_OK on success, otherwise XORP_ERROR.
 */
int
MfeaNode::delete_config_vif_addr(const string& vif_name,
				 const IPvX& addr,
				 string& error_msg)
{
    if (ProtoNode<MfeaVif>::delete_config_vif_addr(vif_name, addr,
						   error_msg) < 0)
	return (XORP_ERROR);
    
    //
    // Send the message to all upper-layer protocols that expect it.
    //
    ProtoRegister& pr = _proto_register;
    const list<pair<string, xorp_module_id> >& module_list = pr.all_module_instance_name_list();
    list<pair<string, xorp_module_id> >::const_iterator iter;
    for (iter = module_list.begin(); iter != module_list.end(); ++iter) {
        const string& dst_module_instance_name = (*iter).first;
        xorp_module_id dst_module_id = (*iter).second;
        send_delete_config_vif_addr(dst_module_instance_name, dst_module_id,
				    vif_name, addr);
    }
    
    return (XORP_OK);
}

/**
 * Set the pif_index of a configured vif.
 * 
 * @param vif_name the name of the vif.
 * @param pif_index the physical interface index.
 * @param error_msg the error message (if error).
 * @return XORP_OK on success, otherwise XORP_ERROR.
 */
int
MfeaNode::set_config_pif_index(const string& vif_name,
			       uint32_t pif_index,
			       string& error_msg)
{
    if (ProtoNode<MfeaVif>::set_config_pif_index(vif_name, pif_index,
						 error_msg) < 0)
	return (XORP_ERROR);
    
    //
    // XXX: no need to propagate the message to upper-layer protocols.
    //
    
    return (XORP_OK);
}

/**
 * Set the vif flags of a configured vif.
 * 
 * @param vif_name the name of the vif.
 * @param is_pim_register true if the vif is a PIM Register interface.
 * @param is_p2p true if the vif is point-to-point interface.
 * @param is_loopback true if the vif is a loopback interface.
 * @param is_multicast true if the vif is multicast capable.
 * @param is_broadcast true if the vif is broadcast capable.
 * @param is_up true if the underlying vif is UP.
 * @param error_msg the error message (if error).
 * @param mtu the MTU of the vif.
 * @return XORP_OK on success, otherwise XORP_ERROR.
 */
int
MfeaNode::set_config_vif_flags(const string& vif_name,
			       bool is_pim_register,
			       bool is_p2p,
			       bool is_loopback,
			       bool is_multicast,
			       bool is_broadcast,
			       bool is_up,
			       uint32_t mtu,
			       string& error_msg)
{
    if (ProtoNode<MfeaVif>::set_config_vif_flags(vif_name, is_pim_register,
						 is_p2p, is_loopback,
						 is_multicast, is_broadcast,
						 is_up, mtu, error_msg) < 0) {
	return (XORP_ERROR);
    }
    
    //
    // Send the message to all upper-layer protocols that expect it.
    //
    ProtoRegister& pr = _proto_register;
    const list<pair<string, xorp_module_id> >& module_list = pr.all_module_instance_name_list();
    list<pair<string, xorp_module_id> >::const_iterator iter;
    for (iter = module_list.begin(); iter != module_list.end(); ++iter) {
        const string& dst_module_instance_name = (*iter).first;
        xorp_module_id dst_module_id = (*iter).second;
        send_set_config_vif_flags(dst_module_instance_name, dst_module_id,
				  vif_name, is_pim_register, is_p2p,
				  is_loopback, is_multicast, is_broadcast,
				  is_up, mtu);
    }
    
    return (XORP_OK);
}

/**
 * Complete the set of vif configuration changes.
 * 
 * @param error_msg the error message (if error).
 * @return XORP_OK on success, otherwise XORP_ERROR.
 */
int
MfeaNode::set_config_all_vifs_done(string& error_msg)
{
    map<string, Vif>::iterator vif_iter;
    map<string, Vif>& configured_vifs = ProtoNode<MfeaVif>::configured_vifs();
    string dummy_error_msg;
    
    //
    // Add new vifs, and update existing ones
    //
    for (vif_iter = configured_vifs.begin();
	 vif_iter != configured_vifs.end();
	 ++vif_iter) {
	Vif* vif = &vif_iter->second;
	Vif* node_vif = vif_find_by_name(vif->name());
	
	//
	// Add a new vif
	//
	if (node_vif == NULL) {
	    add_vif(*vif, dummy_error_msg);
	    continue;
	}
	
	//
	// Update the vif flags
	//
	node_vif->set_p2p(vif->is_p2p());
	node_vif->set_loopback(vif->is_loopback());
	node_vif->set_multicast_capable(vif->is_multicast_capable());
	node_vif->set_broadcast_capable(vif->is_broadcast_capable());
	node_vif->set_underlying_vif_up(vif->is_underlying_vif_up());
	node_vif->set_mtu(vif->mtu());

	//
	// Add new vif addresses, and update existing ones
	//
	{
	    list<VifAddr>::const_iterator vif_addr_iter;
	    for (vif_addr_iter = vif->addr_list().begin();
		 vif_addr_iter != vif->addr_list().end();
		 ++vif_addr_iter) {
		const VifAddr& vif_addr = *vif_addr_iter;
		VifAddr* node_vif_addr = node_vif->find_address(vif_addr.addr());
		if (node_vif_addr == NULL) {
		    node_vif->add_address(vif_addr);
		    continue;
		}
		// Update the address
		if (*node_vif_addr != vif_addr) {
		    *node_vif_addr = vif_addr;
		}
	    }
	}

	//
	// Delete vif addresses that don't exist anymore
	//
	{
	    list<IPvX> delete_addresses_list;
	    list<VifAddr>::const_iterator vif_addr_iter;
	    for (vif_addr_iter = node_vif->addr_list().begin();
		 vif_addr_iter != node_vif->addr_list().end();
		 ++vif_addr_iter) {
		const VifAddr& vif_addr = *vif_addr_iter;
		if (vif->find_address(vif_addr.addr()) == NULL)
		    delete_addresses_list.push_back(vif_addr.addr());
	    }
	    // Delete the addresses
	    list<IPvX>::iterator ipvx_iter;
	    for (ipvx_iter = delete_addresses_list.begin();
		 ipvx_iter != delete_addresses_list.end();
		 ++ipvx_iter) {
		const IPvX& ipvx = *ipvx_iter;
		node_vif->delete_address(ipvx);
	    }
	}
    }

    //
    // Remove vifs that don't exist anymore
    //
    for (uint32_t i = 0; i < maxvifs(); i++) {
	Vif* node_vif = vif_find_by_vif_index(i);
	if (node_vif == NULL)
	    continue;
	if (node_vif->is_pim_register())
	    continue;		// XXX: don't delete the PIM Register vif
	if (configured_vifs.find(node_vif->name()) == configured_vifs.end()) {
	    // Delete the interface
	    string vif_name = node_vif->name();
	    delete_vif(vif_name, dummy_error_msg);
	    continue;
	}
    }
    
    //
    // Send the message to all upper-layer protocols that expect it.
    //
    ProtoRegister& pr = _proto_register;
    const list<pair<string, xorp_module_id> >& module_list = pr.all_module_instance_name_list();
    list<pair<string, xorp_module_id> >::const_iterator iter;
    for (iter = module_list.begin(); iter != module_list.end(); ++iter) {
        const string& dst_module_instance_name = (*iter).first;
        xorp_module_id dst_module_id = (*iter).second;
        send_set_config_all_vifs_done(dst_module_instance_name, dst_module_id);
    }
    
    if (end_config(error_msg) != XORP_OK)
	return (XORP_ERROR);
    
    return (XORP_OK);
}


syntax highlighted by Code2HTML, v. 0.9.1