// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*-
// vim:set sts=4 ts=8:

// 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/fticonfig.cc,v 1.54 2007/02/16 22:45:37 pavlin Exp $"

#include "fea_module.h"

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

#include "libcomm/comm_api.h"

#ifdef HAVE_STROPTS_H
#include <stropts.h>
#endif

#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif

#ifdef HAVE_IPHLPAPI_H
#include <iphlpapi.h>
#endif

#ifdef HAVE_INET_ND_H
#include <inet/nd.h>
#endif

#ifdef HAVE_SYS_SYSCTL_H
#include <sys/sysctl.h>
#endif

#include "profile_vars.hh"
#include "fticonfig.hh"

#ifdef HOST_OS_WINDOWS
#include "libxorp/win_io.h"
#include "win_rtsock.h"
#endif

#define PROC_LINUX_FILE_FORWARDING_V4 "/proc/sys/net/ipv4/ip_forward"
#define PROC_LINUX_FILE_FORWARDING_V6 "/proc/sys/net/ipv6/conf/all/forwarding"
#define DEV_SOLARIS_DRIVER_FORWARDING_V4 "/dev/ip"
#define DEV_SOLARIS_DRIVER_FORWARDING_V6 "/dev/ip6"
#define DEV_SOLARIS_DRIVER_PARAMETER_FORWARDING_V4 "ip_forwarding"
#define DEV_SOLARIS_DRIVER_PARAMETER_FORWARDING_V6 "ip6_forwarding"
#define DEV_SOLARIS_DRIVER_PARAMETER_IGNORE_REDIRECT_V6 "ip6_ignore_redirect"

#ifdef __MINGW32__
#define MIB_IP_FORWARDING	1
#define MIB_IP_NOT_FORWARDING	2
#endif

//
// Unicast forwarding table related configuration.
//


FtiConfig::FtiConfig(EventLoop& eventloop, Profile& profile,
		     const IfTree& iftree,
		     NexthopPortMapper& nexthop_port_mapper)
    : _eventloop(eventloop),
      _profile(profile),
      _nexthop_port_mapper(nexthop_port_mapper),
      _iftree(iftree),
      _ftic_entry_get_primary(NULL),
      _ftic_entry_set_primary(NULL),
      _ftic_entry_observer_primary(NULL),
      _ftic_table_get_primary(NULL),
      _ftic_table_set_primary(NULL),
      _ftic_table_observer_primary(NULL),
      _ftic_entry_get_dummy(*this),
      _ftic_entry_get_rtsock(*this),
      _ftic_entry_get_netlink(*this),
      _ftic_entry_get_iphelper(*this),
      //_ftic_entry_get_rtmv2(*this),
      _ftic_entry_get_click(*this),
      _ftic_entry_set_dummy(*this),
      _ftic_entry_set_rtsock(*this),
      _ftic_entry_set_netlink(*this),
      _ftic_entry_set_iphelper(*this),
      _ftic_entry_set_rtmv2(*this),
      _ftic_entry_set_click(*this),
      _ftic_entry_observer_dummy(*this),
      _ftic_entry_observer_rtsock(*this),
      _ftic_entry_observer_netlink(*this),
      _ftic_entry_observer_iphelper(*this),
      //_ftic_entry_observer_rtmv2(*this),
      _ftic_table_get_dummy(*this),
      _ftic_table_get_sysctl(*this),
      _ftic_table_get_netlink(*this),
      _ftic_table_get_iphelper(*this),
      _ftic_table_get_click(*this),
      _ftic_table_set_dummy(*this),
      _ftic_table_set_rtsock(*this),
      _ftic_table_set_netlink(*this),
      _ftic_table_set_iphelper(*this),
      //_ftic_table_set_rtmv2(*this),
      _ftic_table_set_click(*this),
      _ftic_table_observer_dummy(*this),
      _ftic_table_observer_rtsock(*this),
      _ftic_table_observer_netlink(*this),
      _ftic_table_observer_iphelper(*this),
      _ftic_table_observer_rtmv2(*this),
      _unicast_forwarding_enabled4(false),
      _unicast_forwarding_enabled6(false),
      _accept_rtadv_enabled6(false),
      _unicast_forwarding_entries_retain_on_startup4(false),
      _unicast_forwarding_entries_retain_on_shutdown4(false),
      _unicast_forwarding_entries_retain_on_startup6(false),
      _unicast_forwarding_entries_retain_on_shutdown6(false),
      _have_ipv4(false),
      _have_ipv6(false),
      _is_dummy(false),
      _is_running(false)
{
    string error_msg;

    //
    // Check that all necessary mechanisms to interact with the
    // underlying system are in place.
    //
    if (_ftic_entry_get_primary == NULL) {
	XLOG_FATAL("No primary mechanism to get forwarding table entries "
		   "from the underlying system");
    }
    if (_ftic_entry_set_primary == NULL) {
	XLOG_FATAL("No primary mechanism to set forwarding table entries "
		   "into the underlying system");
    }
    if (_ftic_entry_observer_primary == NULL) {
	XLOG_FATAL("No primary mechanism to observe forwarding table entries "
		   "from the underlying system");
    }
    if (_ftic_table_get_primary == NULL) {
	XLOG_FATAL("No primary mechanism to get the forwarding table "
		   "information from the underlying system");
    }
    if (_ftic_table_set_primary == NULL) {
	XLOG_FATAL("No primary mechanism to set the forwarding table "
		   "information into the underlying system");
    }
    if (_ftic_table_observer_primary == NULL) {
	XLOG_FATAL("No primary mechanism to observe the forwarding table "
		   "information from the underlying system");
    }

    //
    // Test if the system supports IPv4 and IPv6 respectively
    //
    _have_ipv4 = test_have_ipv4();
    _have_ipv6 = test_have_ipv6();

    //
    // Get the old state from the underlying system
    //
    if (_have_ipv4) {
	if (unicast_forwarding_enabled4(_unicast_forwarding_enabled4,
					error_msg) < 0) {
	    XLOG_FATAL("%s", error_msg.c_str());
	}
    }
#ifdef HAVE_IPV6
    if (_have_ipv6) {
	if (unicast_forwarding_enabled6(_unicast_forwarding_enabled6,
					error_msg) < 0) {
	    XLOG_FATAL("%s", error_msg.c_str());
	}
	if (accept_rtadv_enabled6(_accept_rtadv_enabled6, error_msg) < 0) {
	    XLOG_FATAL("%s", error_msg.c_str());
	}
    }
#endif // HAVE_IPV6

#ifdef HOST_OS_WINDOWS
    _event = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (_event == NULL)
	XLOG_FATAL("Could not create Win32 event object.");
    memset(&_overlapped, 0, sizeof(_overlapped));
    _overlapped.hEvent = _event;
    _enablecnt = 0;
#endif
}

FtiConfig::~FtiConfig()
{
    string error_msg;

    if (stop(error_msg) != XORP_OK) {
	XLOG_ERROR("Cannot stop the mechanism for manipulating "
		   "the forwarding table information: %s",
		   error_msg.c_str());
    }

#ifdef HOST_OS_WINDOWS
    if (_enablecnt > 0) {
	XLOG_WARNING("EnableRouter() without %d matching "
		     "UnenableRouter() calls.", _enablecnt);
    }
    CloseHandle(_event);
#endif
}

int
FtiConfig::register_ftic_entry_get_primary(FtiConfigEntryGet *ftic_entry_get)
{
    _ftic_entry_get_primary = ftic_entry_get;
    if (ftic_entry_get != NULL)
	ftic_entry_get->set_primary();
    
    return (XORP_OK);
}

int
FtiConfig::register_ftic_entry_set_primary(FtiConfigEntrySet *ftic_entry_set)
{
    _ftic_entry_set_primary = ftic_entry_set;
    if (ftic_entry_set != NULL)
	ftic_entry_set->set_primary();
    
    return (XORP_OK);
}

int
FtiConfig::register_ftic_entry_observer_primary(FtiConfigEntryObserver *ftic_entry_observer)
{
    _ftic_entry_observer_primary = ftic_entry_observer;
    if (ftic_entry_observer != NULL)
	ftic_entry_observer->set_primary();
    
    return (XORP_OK);
}

int
FtiConfig::register_ftic_table_get_primary(FtiConfigTableGet *ftic_table_get)
{
    _ftic_table_get_primary = ftic_table_get;
    if (ftic_table_get != NULL)
	ftic_table_get->set_primary();
    
    return (XORP_OK);
}

int
FtiConfig::register_ftic_table_set_primary(FtiConfigTableSet *ftic_table_set)
{
    _ftic_table_set_primary = ftic_table_set;
    if (ftic_table_set != NULL)
	ftic_table_set->set_primary();
    
    return (XORP_OK);
}

int
FtiConfig::register_ftic_table_observer_primary(FtiConfigTableObserver *ftic_table_observer)
{
    _ftic_table_observer_primary = ftic_table_observer;
    if (ftic_table_observer != NULL)
	ftic_table_observer->set_primary();
    
    return (XORP_OK);
}

int
FtiConfig::register_ftic_entry_get_secondary(FtiConfigEntryGet *ftic_entry_get)
{
    if (ftic_entry_get != NULL) {
	_ftic_entry_gets_secondary.push_back(ftic_entry_get);
	ftic_entry_get->set_secondary();
    }
    
    return (XORP_OK);
}

int
FtiConfig::register_ftic_entry_set_secondary(FtiConfigEntrySet *ftic_entry_set)
{
    if (ftic_entry_set != NULL) {
	_ftic_entry_sets_secondary.push_back(ftic_entry_set);
	ftic_entry_set->set_secondary();
    }
    
    return (XORP_OK);
}

int
FtiConfig::register_ftic_entry_observer_secondary(FtiConfigEntryObserver *ftic_entry_observer)
{
    if (ftic_entry_observer != NULL) {
	_ftic_entry_observers_secondary.push_back(ftic_entry_observer);
	ftic_entry_observer->set_secondary();
    }
    
    return (XORP_OK);
}

int
FtiConfig::register_ftic_table_get_secondary(FtiConfigTableGet *ftic_table_get)
{
    if (ftic_table_get != NULL) {
	_ftic_table_gets_secondary.push_back(ftic_table_get);
	ftic_table_get->set_secondary();
    }
    
    return (XORP_OK);
}

int
FtiConfig::register_ftic_table_set_secondary(FtiConfigTableSet *ftic_table_set)
{
    if (ftic_table_set != NULL) {
	_ftic_table_sets_secondary.push_back(ftic_table_set);
	ftic_table_set->set_secondary();
    }
    
    return (XORP_OK);
}

int
FtiConfig::register_ftic_table_observer_secondary(FtiConfigTableObserver *ftic_table_observer)
{
    if (ftic_table_observer != NULL) {
	_ftic_table_observers_secondary.push_back(ftic_table_observer);
	ftic_table_observer->set_secondary();
    }
    
    return (XORP_OK);
}

int
FtiConfig::set_dummy()
{
    register_ftic_entry_get_primary(&_ftic_entry_get_dummy);
    register_ftic_entry_set_primary(&_ftic_entry_set_dummy);
    register_ftic_entry_observer_primary(&_ftic_entry_observer_dummy);
    register_ftic_table_get_primary(&_ftic_table_get_dummy);
    register_ftic_table_set_primary(&_ftic_table_set_dummy);
    register_ftic_table_observer_primary(&_ftic_table_observer_dummy);

    //
    // XXX: if we are dummy FEA, then we always have IPv4 and IPv6
    //
    _have_ipv4 = true;
    _have_ipv6 = true;

    _is_dummy = true;

    return (XORP_OK);
}

int
FtiConfig::start(string& error_msg)
{
    list<FtiConfigEntryGet*>::iterator ftic_entry_get_iter;
    list<FtiConfigEntrySet*>::iterator ftic_entry_set_iter;
    list<FtiConfigEntryObserver*>::iterator ftic_entry_observer_iter;
    list<FtiConfigTableGet*>::iterator ftic_table_get_iter;
    list<FtiConfigTableSet*>::iterator ftic_table_set_iter;
    list<FtiConfigTableObserver*>::iterator ftic_table_observer_iter;

    if (_is_running)
	return (XORP_OK);

    //
    // Start the FtiConfigEntryGet methods
    //
    if (_ftic_entry_get_primary != NULL) {
	if (_ftic_entry_get_primary->start(error_msg) < 0)
	    return (XORP_ERROR);
    }
    for (ftic_entry_get_iter = _ftic_entry_gets_secondary.begin();
	 ftic_entry_get_iter != _ftic_entry_gets_secondary.end();
	 ++ftic_entry_get_iter) {
	FtiConfigEntryGet* ftic_entry_get = *ftic_entry_get_iter;
	if (ftic_entry_get->start(error_msg) < 0)
	    return (XORP_ERROR);
    }

    //
    // Start the FtiConfigEntrySet methods
    //
    if (_ftic_entry_set_primary != NULL) {
	if (_ftic_entry_set_primary->start(error_msg) < 0)
	    return (XORP_ERROR);
    }
    for (ftic_entry_set_iter = _ftic_entry_sets_secondary.begin();
	 ftic_entry_set_iter != _ftic_entry_sets_secondary.end();
	 ++ftic_entry_set_iter) {
	FtiConfigEntrySet* ftic_entry_set = *ftic_entry_set_iter;
	if (ftic_entry_set->start(error_msg) < 0)
	    return (XORP_ERROR);
    }

    //
    // Start the FtiConfigEntryObserver methods
    //
    if (_ftic_entry_observer_primary != NULL) {
	if (_ftic_entry_observer_primary->start(error_msg) < 0)
	    return (XORP_ERROR);
    }
    for (ftic_entry_observer_iter = _ftic_entry_observers_secondary.begin();
	 ftic_entry_observer_iter != _ftic_entry_observers_secondary.end();
	 ++ftic_entry_observer_iter) {
	FtiConfigEntryObserver* ftic_entry_observer = *ftic_entry_observer_iter;
	if (ftic_entry_observer->start(error_msg) < 0)
	    return (XORP_ERROR);
    }

    //
    // Start the FtiConfigTableGet methods
    //
    if (_ftic_table_get_primary != NULL) {
	if (_ftic_table_get_primary->start(error_msg) < 0)
	    return (XORP_ERROR);
    }
    for (ftic_table_get_iter = _ftic_table_gets_secondary.begin();
	 ftic_table_get_iter != _ftic_table_gets_secondary.end();
	 ++ftic_table_get_iter) {
	FtiConfigTableGet* ftic_table_get = *ftic_table_get_iter;
	if (ftic_table_get->start(error_msg) < 0)
	    return (XORP_ERROR);
    }

    //
    // Start the FtiConfigTableSet methods
    //
    if (_ftic_table_set_primary != NULL) {
	if (_ftic_table_set_primary->start(error_msg) < 0)
	    return (XORP_ERROR);
    }
    for (ftic_table_set_iter = _ftic_table_sets_secondary.begin();
	 ftic_table_set_iter != _ftic_table_sets_secondary.end();
	 ++ftic_table_set_iter) {
	FtiConfigTableSet* ftic_table_set = *ftic_table_set_iter;
	if (ftic_table_set->start(error_msg) < 0)
	    return (XORP_ERROR);
    }

    //
    // Start the FtiConfigTableObserver methods
    //
    if (_ftic_table_observer_primary != NULL) {
	if (_ftic_table_observer_primary->start(error_msg) < 0)
	    return (XORP_ERROR);
    }
    for (ftic_table_observer_iter = _ftic_table_observers_secondary.begin();
	 ftic_table_observer_iter != _ftic_table_observers_secondary.end();
	 ++ftic_table_observer_iter) {
	FtiConfigTableObserver* ftic_table_observer = *ftic_table_observer_iter;
	if (ftic_table_observer->start(error_msg) < 0)
	    return (XORP_ERROR);
    }

    _is_running = true;

    return (XORP_OK);
}

int
FtiConfig::stop(string& error_msg)
{
    list<FtiConfigEntryGet*>::iterator ftic_entry_get_iter;
    list<FtiConfigEntrySet*>::iterator ftic_entry_set_iter;
    list<FtiConfigEntryObserver*>::iterator ftic_entry_observer_iter;
    list<FtiConfigTableGet*>::iterator ftic_table_get_iter;
    list<FtiConfigTableSet*>::iterator ftic_table_set_iter;
    list<FtiConfigTableObserver*>::iterator ftic_table_observer_iter;
    int ret_value = XORP_OK;
    string error_msg2;

    if (! _is_running)
	return (XORP_OK);

    error_msg.erase();

    //
    // Stop the FtiConfigTableObserver methods
    //
    for (ftic_table_observer_iter = _ftic_table_observers_secondary.begin();
	 ftic_table_observer_iter != _ftic_table_observers_secondary.end();
	 ++ftic_table_observer_iter) {
	FtiConfigTableObserver* ftic_table_observer = *ftic_table_observer_iter;
	if (ftic_table_observer->stop(error_msg2) < 0) {
	    ret_value = XORP_ERROR;
	    if (error_msg.empty())
		error_msg = error_msg2;
	}
    }
    if (_ftic_table_observer_primary != NULL) {
	if (_ftic_table_observer_primary->stop(error_msg2) < 0) {
	    ret_value = XORP_ERROR;
	    if (error_msg.empty())
		error_msg = error_msg2;
	}
    }

    //
    // Stop the FtiConfigTableSet methods
    //
    for (ftic_table_set_iter = _ftic_table_sets_secondary.begin();
	 ftic_table_set_iter != _ftic_table_sets_secondary.end();
	 ++ftic_table_set_iter) {
	FtiConfigTableSet* ftic_table_set = *ftic_table_set_iter;
	if (ftic_table_set->stop(error_msg2) < 0) {
	    ret_value = XORP_ERROR;
	    if (error_msg.empty())
		error_msg = error_msg2;
	}
    }
    if (_ftic_table_set_primary != NULL) {
	if (_ftic_table_set_primary->stop(error_msg2) < 0) {
	    ret_value = XORP_ERROR;
	    if (error_msg.empty())
		error_msg = error_msg2;
	}
    }

    //
    // Stop the FtiConfigTableGet methods
    //
    for (ftic_table_get_iter = _ftic_table_gets_secondary.begin();
	 ftic_table_get_iter != _ftic_table_gets_secondary.end();
	 ++ftic_table_get_iter) {
	FtiConfigTableGet* ftic_table_get = *ftic_table_get_iter;
	if (ftic_table_get->stop(error_msg2) < 0) {
	    ret_value = XORP_ERROR;
	    if (error_msg.empty())
		error_msg = error_msg2;
	}
    }
    if (_ftic_table_get_primary != NULL) {
	if (_ftic_table_get_primary->stop(error_msg2) < 0) {
	    ret_value = XORP_ERROR;
	    if (error_msg.empty())
		error_msg = error_msg2;
	}
    }

    //
    // Stop the FtiConfigEntryObserver methods
    //
    for (ftic_entry_observer_iter = _ftic_entry_observers_secondary.begin();
	 ftic_entry_observer_iter != _ftic_entry_observers_secondary.end();
	 ++ftic_entry_observer_iter) {
	FtiConfigEntryObserver* ftic_entry_observer = *ftic_entry_observer_iter;
	if (ftic_entry_observer->stop(error_msg2) < 0) {
	    ret_value = XORP_ERROR;
	    if (error_msg.empty())
		error_msg = error_msg2;
	}
    }
    if (_ftic_entry_observer_primary != NULL) {
	if (_ftic_entry_observer_primary->stop(error_msg2) < 0) {
	    ret_value = XORP_ERROR;
	    if (error_msg.empty())
		error_msg = error_msg2;
	}
    }

    //
    // Stop the FtiConfigEntrySet methods
    //
    for (ftic_entry_set_iter = _ftic_entry_sets_secondary.begin();
	 ftic_entry_set_iter != _ftic_entry_sets_secondary.end();
	 ++ftic_entry_set_iter) {
	FtiConfigEntrySet* ftic_entry_set = *ftic_entry_set_iter;
	if (ftic_entry_set->stop(error_msg2) < 0) {
	    ret_value = XORP_ERROR;
	    if (error_msg.empty())
		error_msg = error_msg2;
	}
    }
    if (_ftic_entry_set_primary != NULL) {
	if (_ftic_entry_set_primary->stop(error_msg2) < 0) {
	    ret_value = XORP_ERROR;
	    if (error_msg.empty())
		error_msg = error_msg2;
	}
    }

    //
    // Stop the FtiConfigEntryGet methods
    //
    for (ftic_entry_get_iter = _ftic_entry_gets_secondary.begin();
	 ftic_entry_get_iter != _ftic_entry_gets_secondary.end();
	 ++ftic_entry_get_iter) {
	FtiConfigEntryGet* ftic_entry_get = *ftic_entry_get_iter;
	if (ftic_entry_get->stop(error_msg2) < 0) {
	    ret_value = XORP_ERROR;
	    if (error_msg.empty())
		error_msg = error_msg2;
	}
    }
    if (_ftic_entry_get_primary != NULL) {
	if (_ftic_entry_get_primary->stop(error_msg2) < 0) {
	    ret_value = XORP_ERROR;
	    if (error_msg.empty())
		error_msg = error_msg2;
	}
    }

    //
    // Restore the old forwarding state in the underlying system.
    //
    // XXX: Note that if the XORP forwarding entries are retained on shutdown,
    // then we don't restore the state.
    //
    if (_have_ipv4) {
	if (! unicast_forwarding_entries_retain_on_shutdown4()) {
	    if (set_unicast_forwarding_enabled4(_unicast_forwarding_enabled4,
						error_msg) < 0) {
		ret_value = XORP_ERROR;
	    }
	}
    }
#ifdef HAVE_IPV6
    if (_have_ipv6) {
	if (! unicast_forwarding_entries_retain_on_shutdown6()) {
	    if (set_unicast_forwarding_enabled6(_unicast_forwarding_enabled6,
						error_msg) < 0) {
		ret_value = XORP_ERROR;
	    }
	    if (set_accept_rtadv_enabled6(_accept_rtadv_enabled6, error_msg)
		< 0) {
		ret_value = XORP_ERROR;
	    }
	}
    }
#endif // HAVE_IPV6

    _is_running = false;

    return (ret_value);
}

/**
 * Enable/disable Click support.
 *
 * @param enable if true, then enable Click support, otherwise disable it.
 */
void
FtiConfig::enable_click(bool enable)
{
    _ftic_entry_get_click.enable_click(enable);
    _ftic_entry_set_click.enable_click(enable);
    _ftic_table_get_click.enable_click(enable);
    _ftic_table_set_click.enable_click(enable);
}

/**
 * Start Click support.
 *
 * @param error_msg the error message (if error).
 * @return XORP_OK on success, otherwise XORP_ERROR.
 */
int
FtiConfig::start_click(string& error_msg)
{
    if (_ftic_entry_get_click.start(error_msg) < 0) {
	return (XORP_ERROR);
    }
    if (_ftic_entry_set_click.start(error_msg) < 0) {
	string error_msg2;
	_ftic_entry_get_click.stop(error_msg2);
	return (XORP_ERROR);
    }
    if (_ftic_table_get_click.start(error_msg) < 0) {
	string error_msg2;
	_ftic_entry_get_click.stop(error_msg2);
	_ftic_entry_set_click.stop(error_msg2);
	return (XORP_ERROR);
    }
    if (_ftic_table_set_click.start(error_msg) < 0) {
	string error_msg2;
	_ftic_entry_get_click.stop(error_msg2);
	_ftic_entry_set_click.stop(error_msg2);
	_ftic_table_get_click.stop(error_msg2);
	return (XORP_ERROR);
    }

    return (XORP_OK);
}

/**
 * Stop Click support.
 *
 * @param error_msg the error message (if error).
 * @return XORP_OK on success, otherwise XORP_ERROR.
 */
int
FtiConfig::stop_click(string& error_msg)
{
    if (_ftic_entry_get_click.stop(error_msg) < 0) {
	string error_msg2;
	_ftic_entry_set_click.stop(error_msg2);
	_ftic_table_get_click.stop(error_msg2);
	_ftic_table_set_click.stop(error_msg2);
	return (XORP_ERROR);
    }
    if (_ftic_entry_set_click.stop(error_msg) < 0) {
	string error_msg2;
	_ftic_table_get_click.stop(error_msg2);
	_ftic_table_set_click.stop(error_msg2);
	return (XORP_ERROR);
    }
    if (_ftic_table_get_click.stop(error_msg) < 0) {
	string error_msg2;
	_ftic_table_set_click.stop(error_msg2);
	return (XORP_ERROR);
    }
    if (_ftic_table_set_click.stop(error_msg) < 0) {
	return (XORP_ERROR);
    }

    return (XORP_OK);
}

/**
 * Enable/disable duplicating the Click routes to the system kernel.
 *
 * @param enable if true, then enable duplicating the Click routes to the
 * system kernel, otherwise disable it.
 */
void
FtiConfig::enable_duplicate_routes_to_kernel(bool enable)
{
    _ftic_entry_get_click.enable_duplicate_routes_to_kernel(enable);
    _ftic_entry_set_click.enable_duplicate_routes_to_kernel(enable);
    _ftic_table_get_click.enable_duplicate_routes_to_kernel(enable);
    _ftic_table_set_click.enable_duplicate_routes_to_kernel(enable);
}

/**
 * Enable/disable kernel-level Click support.
 *
 * @param enable if true, then enable the kernel-level Click support,
 * otherwise disable it.
 */
void
FtiConfig::enable_kernel_click(bool enable)
{
    _ftic_entry_get_click.enable_kernel_click(enable);
    _ftic_entry_set_click.enable_kernel_click(enable);
    _ftic_table_get_click.enable_kernel_click(enable);
    _ftic_table_set_click.enable_kernel_click(enable);
}

/**
 * Enable/disable installing kernel-level Click on startup.
 *
 * @param enable if true, then install kernel-level Click on startup.
 */
void
FtiConfig::enable_kernel_click_install_on_startup(bool enable)
{
    // XXX: only IfConfigGet should install the kernel-level Click
    _ftic_entry_get_click.enable_kernel_click_install_on_startup(enable);
    _ftic_entry_set_click.enable_kernel_click_install_on_startup(false);
    _ftic_table_get_click.enable_kernel_click_install_on_startup(false);
    _ftic_table_set_click.enable_kernel_click_install_on_startup(false);
}

/**
 * Specify the list of kernel Click modules to load on startup if
 * installing kernel-level Click on startup is enabled.
 *
 * @param modules the list of kernel Click modules to load.
 */
void
FtiConfig::set_kernel_click_modules(const list<string>& modules)
{
    _ftic_entry_get_click.set_kernel_click_modules(modules);
    _ftic_entry_set_click.set_kernel_click_modules(modules);
    _ftic_table_get_click.set_kernel_click_modules(modules);
    _ftic_table_set_click.set_kernel_click_modules(modules);
}

/**
 * Specify the kernel-level Click mount directory.
 *
 * @param directory the kernel-level Click mount directory.
 */
void
FtiConfig::set_kernel_click_mount_directory(const string& directory)
{
    _ftic_entry_get_click.set_kernel_click_mount_directory(directory);
    _ftic_entry_set_click.set_kernel_click_mount_directory(directory);
    _ftic_table_get_click.set_kernel_click_mount_directory(directory);
    _ftic_table_set_click.set_kernel_click_mount_directory(directory);
}

/**
 * Specify the external program to generate the kernel-level Click
 * configuration.
 *
 * @param v the name of the external program to generate the kernel-level
 * Click configuration.
 */
void
FtiConfig::set_kernel_click_config_generator_file(const string& v)
{
    _ftic_entry_get_click.set_kernel_click_config_generator_file(v);
    _ftic_entry_set_click.set_kernel_click_config_generator_file(v);
    _ftic_table_get_click.set_kernel_click_config_generator_file(v);
    _ftic_table_set_click.set_kernel_click_config_generator_file(v);
}

/**
 * Enable/disable user-level Click support.
 *
 * @param enable if true, then enable the user-level Click support,
 * otherwise disable it.
 */
void
FtiConfig::enable_user_click(bool enable)
{
    _ftic_entry_get_click.enable_user_click(enable);
    _ftic_entry_set_click.enable_user_click(enable);
    _ftic_table_get_click.enable_user_click(enable);
    _ftic_table_set_click.enable_user_click(enable);
}

/**
 * Specify the user-level Click command file.
 *
 * @param v the name of the user-level Click command file.
 */
void
FtiConfig::set_user_click_command_file(const string& v)
{
    _ftic_entry_get_click.set_user_click_command_file(v);
    _ftic_entry_set_click.set_user_click_command_file(v);
    _ftic_table_get_click.set_user_click_command_file(v);
    _ftic_table_set_click.set_user_click_command_file(v);
}

/**
 * Specify the extra arguments to the user-level Click command.
 *
 * @param v the extra arguments to the user-level Click command.
 */
void
FtiConfig::set_user_click_command_extra_arguments(const string& v)
{
    _ftic_entry_get_click.set_user_click_command_extra_arguments(v);
    _ftic_entry_set_click.set_user_click_command_extra_arguments(v);
    _ftic_table_get_click.set_user_click_command_extra_arguments(v);
    _ftic_table_set_click.set_user_click_command_extra_arguments(v);
}

/**
 * Specify whether to execute on startup the user-level Click command.
 *
 * @param v if true, then execute the user-level Click command on startup.
 */
void
FtiConfig::set_user_click_command_execute_on_startup(bool v)
{
    UNUSED(v);

    // XXX: only IfConfigGet should execute the user-level Click command
    _ftic_entry_get_click.set_user_click_command_execute_on_startup(false);
    _ftic_entry_set_click.set_user_click_command_execute_on_startup(false);
    _ftic_table_get_click.set_user_click_command_execute_on_startup(false);
    _ftic_table_set_click.set_user_click_command_execute_on_startup(false);
}

/**
 * Specify the address to use for control access to the user-level
 * Click.
 *
 * @param v the address to use for control access to the user-level Click.
 */
void
FtiConfig::set_user_click_control_address(const IPv4& v)
{
    _ftic_entry_get_click.set_user_click_control_address(v);
    _ftic_entry_set_click.set_user_click_control_address(v);
    _ftic_table_get_click.set_user_click_control_address(v);
    _ftic_table_set_click.set_user_click_control_address(v);
}

/**
 * Specify the socket port to use for control access to the user-level
 * Click.
 *
 * @param v the socket port to use for control access to the user-level
 * Click.
 */
void
FtiConfig::set_user_click_control_socket_port(uint32_t v)
{
    _ftic_entry_get_click.set_user_click_control_socket_port(v);
    _ftic_entry_set_click.set_user_click_control_socket_port(v);
    _ftic_table_get_click.set_user_click_control_socket_port(v);
    _ftic_table_set_click.set_user_click_control_socket_port(v);
}

/**
 * Specify the configuration file to be used by user-level Click on
 * startup.
 *
 * @param v the name of the configuration file to be used by user-level
 * Click on startup.
 */
void
FtiConfig::set_user_click_startup_config_file(const string& v)
{
    _ftic_entry_get_click.set_user_click_startup_config_file(v);
    _ftic_entry_set_click.set_user_click_startup_config_file(v);
    _ftic_table_get_click.set_user_click_startup_config_file(v);
    _ftic_table_set_click.set_user_click_startup_config_file(v);
}

/**
 * Specify the external program to generate the user-level Click
 * configuration.
 *
 * @param v the name of the external program to generate the user-level
 * Click configuration.
 */
void
FtiConfig::set_user_click_config_generator_file(const string& v)
{
    _ftic_entry_get_click.set_user_click_config_generator_file(v);
    _ftic_entry_set_click.set_user_click_config_generator_file(v);
    _ftic_table_get_click.set_user_click_config_generator_file(v);
    _ftic_table_set_click.set_user_click_config_generator_file(v);
}

bool
FtiConfig::start_configuration(string& error_msg)
{
    list<FtiConfigEntrySet*>::iterator ftic_entry_set_iter;
    list<FtiConfigTableSet*>::iterator ftic_table_set_iter;
    bool ret_value = true;
    string error_msg2;

    error_msg.erase();

    //
    // XXX: we need to call start_configuration() for "entry" and "table",
    // because the top-level start/end configuration interface
    // does not distinguish between "entry" and "table" modification.
    //
    if (_ftic_entry_set_primary != NULL) {
	if (_ftic_entry_set_primary->start_configuration(error_msg2) != true) {
	    ret_value = false;
	    if (error_msg.empty())
		error_msg = error_msg2;
	}
    }
    for (ftic_entry_set_iter = _ftic_entry_sets_secondary.begin();
	 ftic_entry_set_iter != _ftic_entry_sets_secondary.end();
	 ++ftic_entry_set_iter) {
	FtiConfigEntrySet* ftic_entry_set = *ftic_entry_set_iter;
	if (ftic_entry_set->start_configuration(error_msg2) != true) {
	    ret_value = false;
	    if (error_msg.empty())
		error_msg = error_msg2;
	}
    }

    if (_ftic_table_set_primary != NULL) {
	if (_ftic_table_set_primary->start_configuration(error_msg2) != true) {
	    ret_value = false;
	    if (error_msg.empty())
		error_msg = error_msg2;
	}
    }
    for (ftic_table_set_iter = _ftic_table_sets_secondary.begin();
	 ftic_table_set_iter != _ftic_table_sets_secondary.end();
	 ++ftic_table_set_iter) {
	FtiConfigTableSet* ftic_table_set = *ftic_table_set_iter;
	if (ftic_table_set->start_configuration(error_msg2) != true) {
	    ret_value = false;
	    if (error_msg.empty())
		error_msg = error_msg2;
	}
    }
    
    return (ret_value);
}

bool
FtiConfig::end_configuration(string& error_msg)
{
    list<FtiConfigEntrySet*>::iterator ftic_entry_set_iter;
    list<FtiConfigTableSet*>::iterator ftic_table_set_iter;
    bool ret_value = true;
    string error_msg2;

    error_msg.erase();

    //
    // XXX: we need to call end_configuration() for "entry" and "table",
    // because the top-level start/end configuration interface
    // does not distinguish between "entry" and "table" modification.
    //
    if (_ftic_entry_set_primary != NULL) {
	if (_ftic_entry_set_primary->end_configuration(error_msg) != true) {
	    ret_value = false;
	    if (error_msg.empty())
		error_msg = error_msg2;
	}
    }
    for (ftic_entry_set_iter = _ftic_entry_sets_secondary.begin();
	 ftic_entry_set_iter != _ftic_entry_sets_secondary.end();
	 ++ftic_entry_set_iter) {
	FtiConfigEntrySet* ftic_entry_set = *ftic_entry_set_iter;
	if (ftic_entry_set->end_configuration(error_msg) != true) {
	    ret_value = false;
	    if (error_msg.empty())
		error_msg = error_msg2;
	}
    }

    if (_ftic_table_set_primary != NULL) {
	if (_ftic_table_set_primary->end_configuration(error_msg) != true) {
	    ret_value = false;
	    if (error_msg.empty())
		error_msg = error_msg2;
	}
    }
    for (ftic_table_set_iter = _ftic_table_sets_secondary.begin();
	 ftic_table_set_iter != _ftic_table_sets_secondary.end();
	 ++ftic_table_set_iter) {
	FtiConfigTableSet* ftic_table_set = *ftic_table_set_iter;
	if (ftic_table_set->end_configuration(error_msg) != true) {
	    ret_value = false;
	    if (error_msg.empty())
		error_msg = error_msg2;
	}
    }
    
    return (ret_value);
}

bool
FtiConfig::add_entry4(const Fte4& fte)
{
    list<FtiConfigEntrySet*>::iterator ftic_entry_set_iter;

    if ((_ftic_entry_set_primary == NULL) && _ftic_entry_sets_secondary.empty())
	return (false);

    if (_profile.enabled(profile_route_out))
	_profile.log(profile_route_out,
		     c_format("add %s", fte.net().str().c_str()));

    if (_ftic_entry_set_primary != NULL) {
	if (_ftic_entry_set_primary->add_entry4(fte) != true)
	    return (false);
    }
    for (ftic_entry_set_iter = _ftic_entry_sets_secondary.begin();
	 ftic_entry_set_iter != _ftic_entry_sets_secondary.end();
	 ++ftic_entry_set_iter) {
	FtiConfigEntrySet* ftic_entry_set = *ftic_entry_set_iter;
	if (ftic_entry_set->add_entry4(fte) != true)
	    return (false);
    }

    return (true);
}

bool
FtiConfig::delete_entry4(const Fte4& fte)
{
    list<FtiConfigEntrySet*>::iterator ftic_entry_set_iter;

    if ((_ftic_entry_set_primary == NULL) && _ftic_entry_sets_secondary.empty())
	return (false);

    if (_profile.enabled(profile_route_out))
	_profile.log(profile_route_out,
		     c_format("delete %s", fte.net().str().c_str()));

    if (_ftic_entry_set_primary != NULL) {
	if (_ftic_entry_set_primary->delete_entry4(fte) != true)
	    return (false);
    }
    for (ftic_entry_set_iter = _ftic_entry_sets_secondary.begin();
	 ftic_entry_set_iter != _ftic_entry_sets_secondary.end();
	 ++ftic_entry_set_iter) {
	FtiConfigEntrySet* ftic_entry_set = *ftic_entry_set_iter;
	if (ftic_entry_set->delete_entry4(fte) != true)
	    return (false);
    }

    return (true);
}

bool
FtiConfig::set_table4(const list<Fte4>& fte_list)
{
    list<FtiConfigTableSet*>::iterator ftic_table_set_iter;

    if ((_ftic_table_set_primary == NULL) && _ftic_table_sets_secondary.empty())
	return (false);

    if (_ftic_table_set_primary != NULL) {
	if (_ftic_table_set_primary->set_table4(fte_list) != true)
	    return (false);
    }
    for (ftic_table_set_iter = _ftic_table_sets_secondary.begin();
	 ftic_table_set_iter != _ftic_table_sets_secondary.end();
	 ++ftic_table_set_iter) {
	FtiConfigTableSet* ftic_table_set = *ftic_table_set_iter;
	if (ftic_table_set->set_table4(fte_list) != true)
	    return (false);
    }

    return (true);
}

bool
FtiConfig::delete_all_entries4()
{
    list<FtiConfigTableSet*>::iterator ftic_table_set_iter;

    if ((_ftic_table_set_primary == NULL) && _ftic_table_sets_secondary.empty())
	return (false);

    if (_ftic_table_set_primary != NULL) {
	if (_ftic_table_set_primary->delete_all_entries4() != true)
	    return (false);
    }
    for (ftic_table_set_iter = _ftic_table_sets_secondary.begin();
	 ftic_table_set_iter != _ftic_table_sets_secondary.end();
	 ++ftic_table_set_iter) {
	FtiConfigTableSet* ftic_table_set = *ftic_table_set_iter;
	if (ftic_table_set->delete_all_entries4() != true)
	    return (false);
    }

    return (true);
}

bool
FtiConfig::lookup_route_by_dest4(const IPv4& dst, Fte4& fte)
{
    if (_ftic_entry_get_primary == NULL)
	return (false);

    if (_ftic_entry_get_primary->lookup_route_by_dest4(dst, fte) != true)
	return (false);

    return (true);
}

bool
FtiConfig::lookup_route_by_network4(const IPv4Net& dst, Fte4& fte)
{
    if (_ftic_entry_get_primary == NULL)
	return (false);

    if (_ftic_entry_get_primary->lookup_route_by_network4(dst, fte) != true)
	return (false);

    return (true);
}

bool
FtiConfig::get_table4(list<Fte4>& fte_list)
{
    if (_ftic_table_get_primary == NULL)
	return (false);

    if (_ftic_table_get_primary->get_table4(fte_list) != true)
	return (false);

    return (true);
}

bool
FtiConfig::add_entry6(const Fte6& fte)
{
    list<FtiConfigEntrySet*>::iterator ftic_entry_set_iter;

    if ((_ftic_entry_set_primary == NULL) && _ftic_entry_sets_secondary.empty())
	return (false);

    if (_profile.enabled(profile_route_out))
	_profile.log(profile_route_out,
		     c_format("add %s", fte.net().str().c_str()));

    if (_ftic_entry_set_primary != NULL) {
	if (_ftic_entry_set_primary->add_entry6(fte) != true)
	    return (false);
    }
    for (ftic_entry_set_iter = _ftic_entry_sets_secondary.begin();
	 ftic_entry_set_iter != _ftic_entry_sets_secondary.end();
	 ++ftic_entry_set_iter) {
	FtiConfigEntrySet* ftic_entry_set = *ftic_entry_set_iter;
	if (ftic_entry_set->add_entry6(fte) != true)
	    return (false);
    }

    return (true);
}

bool
FtiConfig::delete_entry6(const Fte6& fte)
{
    list<FtiConfigEntrySet*>::iterator ftic_entry_set_iter;

    if ((_ftic_entry_set_primary == NULL) && _ftic_entry_sets_secondary.empty())
	return (false);

    if (_profile.enabled(profile_route_out))
	_profile.log(profile_route_out,
		     c_format("delete %s", fte.net().str().c_str()));

    if (_ftic_entry_set_primary != NULL) {
	if (_ftic_entry_set_primary->delete_entry6(fte) != true)
	    return (false);
    }
    for (ftic_entry_set_iter = _ftic_entry_sets_secondary.begin();
	 ftic_entry_set_iter != _ftic_entry_sets_secondary.end();
	 ++ftic_entry_set_iter) {
	FtiConfigEntrySet* ftic_entry_set = *ftic_entry_set_iter;
	if (ftic_entry_set->delete_entry6(fte) != true)
	    return (false);
    }

    return (true);
}

bool
FtiConfig::set_table6(const list<Fte6>& fte_list)
{
    list<FtiConfigTableSet*>::iterator ftic_table_set_iter;

    if ((_ftic_table_set_primary == NULL) && _ftic_table_sets_secondary.empty())
	return (false);

    if (_ftic_table_set_primary != NULL) {
	if (_ftic_table_set_primary->set_table6(fte_list) != true)
	    return (false);
    }
    for (ftic_table_set_iter = _ftic_table_sets_secondary.begin();
	 ftic_table_set_iter != _ftic_table_sets_secondary.end();
	 ++ftic_table_set_iter) {
	FtiConfigTableSet* ftic_table_set = *ftic_table_set_iter;
	if (ftic_table_set->set_table6(fte_list) != true)
	    return (false);
    }

    return (true);
}

bool
FtiConfig::delete_all_entries6()
{
    list<FtiConfigTableSet*>::iterator ftic_table_set_iter;

    if ((_ftic_table_set_primary == NULL) && _ftic_table_sets_secondary.empty())
	return (false);

    if (_ftic_table_set_primary != NULL) {
	if (_ftic_table_set_primary->delete_all_entries6() != true)
	    return (false);
    }
    for (ftic_table_set_iter = _ftic_table_sets_secondary.begin();
	 ftic_table_set_iter != _ftic_table_sets_secondary.end();
	 ++ftic_table_set_iter) {
	FtiConfigTableSet* ftic_table_set = *ftic_table_set_iter;
	if (ftic_table_set->delete_all_entries6() != true)
	    return (false);
    }

    return (true);
}

bool
FtiConfig::lookup_route_by_dest6(const IPv6& dst, Fte6& fte)
{
    if (_ftic_entry_get_primary == NULL)
	return (false);

    if (_ftic_entry_get_primary->lookup_route_by_dest6(dst, fte) != true)
	return (false);

    return (true);
}

bool
FtiConfig::lookup_route_by_network6(const IPv6Net& dst, Fte6& fte)
{
    if (_ftic_entry_get_primary == NULL)
	return (false);

    if (_ftic_entry_get_primary->lookup_route_by_network6(dst, fte) != true)
	return (false);

    return (true);
}

bool
FtiConfig::get_table6(list<Fte6>& fte_list)
{
    if (_ftic_table_get_primary == NULL)
	return (false);

    if (_ftic_table_get_primary->get_table6(fte_list) != true)
	return (false);

    return (true);
}

bool
FtiConfig::add_fib_table_observer(FibTableObserverBase* fib_table_observer)
{
    if (_ftic_table_observer_primary == NULL)
	return (false);

    _ftic_table_observer_primary->add_fib_table_observer(fib_table_observer);

    return (true);
}

bool
FtiConfig::delete_fib_table_observer(FibTableObserverBase* fib_table_observer)
{
    if (_ftic_table_observer_primary == NULL)
	return (false);

    _ftic_table_observer_primary->delete_fib_table_observer(fib_table_observer);

    return (true);
}

/**
 * Test if the underlying system supports IPv4.
 * 
 * @return true if the underlying system supports IPv4, otherwise false.
 */
bool
FtiConfig::test_have_ipv4() const
{
    // XXX: always return true if running in dummy mode
    if (is_dummy())
	return (true);

    XorpFd s = comm_sock_open(AF_INET, SOCK_DGRAM, 0, 0);
    if (!s.is_valid())
	return (false);
   
    comm_close(s);

    return (true);
}

/**
 * Test if the underlying system supports IPv6.
 * 
 * @return true if the underlying system supports IPv6, otherwise false.
 */
bool
FtiConfig::test_have_ipv6() const
{
    // XXX: always return true if running in dummy mode
    if (is_dummy())
	return (true);

#ifndef HAVE_IPV6
    return (false);
#else
    XorpFd s = comm_sock_open(AF_INET6, SOCK_DGRAM, 0, 0);
    if (!s.is_valid())
	return (false);
    
    comm_close(s);
    return (true);
#endif // HAVE_IPV6
}

/**
 * Test whether the IPv4 unicast forwarding engine is enabled or disabled
 * to forward packets.
 * 
 * @param ret_value if true on return, then the IPv4 unicast forwarding
 * is enabled, otherwise is disabled.
 * @param error_msg the error message (if error).
 * @return XORP_OK on success, otherwise XORP_ERROR.
 */
int
FtiConfig::unicast_forwarding_enabled4(bool& ret_value, string& error_msg) const
{
    // XXX: always return true if running in dummy mode
    if (is_dummy()) {
	ret_value = true;
	return (XORP_OK);
    }

    if (! have_ipv4()) {
	ret_value = false;
	error_msg = c_format("Cannot test whether IPv4 unicast forwarding "
			     "is enabled: IPv4 is not supported");
	XLOG_ERROR("%s", error_msg.c_str());
	return (XORP_ERROR);
    }

    int enabled = 0;
    
#if defined(CTL_NET) && defined(IPPROTO_IP) && defined(IPCTL_FORWARDING)
    {
	size_t sz = sizeof(enabled);
	int mib[4];
	
	mib[0] = CTL_NET;
	mib[1] = AF_INET;
	mib[2] = IPPROTO_IP;
	mib[3] = IPCTL_FORWARDING;
	
	if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), &enabled, &sz, NULL, 0)
	    != 0) {
	    error_msg = c_format("Get sysctl(IPCTL_FORWARDING) failed: %s",
				 strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    return (XORP_ERROR);
	}
    }
#elif defined(HOST_OS_LINUX)
    {
	FILE *fh = fopen(PROC_LINUX_FILE_FORWARDING_V4, "r");
	
	if (fh == NULL) {
	    error_msg = c_format("Cannot open file %s for reading: %s",
				 PROC_LINUX_FILE_FORWARDING_V4,
				 strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    return (XORP_ERROR);
	}
	if (fscanf(fh, "%d", &enabled) != 1) {
	    error_msg = c_format("Error reading file %s: %s",
				 PROC_LINUX_FILE_FORWARDING_V4,
				 strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    fclose(fh);
	    return (XORP_ERROR);
	}
	fclose(fh);
    }
#elif defined(HOST_OS_SOLARIS)
    {
	struct strioctl strioctl;
	char buf[256];
	int fd;

	fd = open(DEV_SOLARIS_DRIVER_FORWARDING_V4, O_RDONLY);
	if (fd < 0) {
	    error_msg = c_format("Cannot open file %s for reading: %s",
				 DEV_SOLARIS_DRIVER_FORWARDING_V4,
				 strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    return (XORP_ERROR);
	}
	int r = isastream(fd);
	if (r < 0) {
	    error_msg = c_format("Error testing whether file %s is a stream: %s",
				 DEV_SOLARIS_DRIVER_FORWARDING_V4,
				 strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    close(fd);
	    return (XORP_ERROR);
	}
	if (r == 0) {
	    error_msg = c_format("File %s is not a stream",
				 DEV_SOLARIS_DRIVER_FORWARDING_V4);
	    XLOG_ERROR("%s", error_msg.c_str());
	    close(fd);
	    return (XORP_ERROR);
	}

	memset(&strioctl, 0, sizeof(strioctl));
	memset(buf, 0, sizeof(buf));
	strncpy(buf, DEV_SOLARIS_DRIVER_PARAMETER_FORWARDING_V4,
		sizeof(buf) - 1);
	strioctl.ic_cmd = ND_GET;
	strioctl.ic_timout = 0;
	strioctl.ic_len = sizeof(buf);
	strioctl.ic_dp = buf;
	if (ioctl(fd, I_STR, &strioctl) < 0) {
	    error_msg = c_format("Error testing whether IPv4 unicast "
				 "forwarding is enabled: %s",
				 strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    close(fd);
	    return (XORP_ERROR);
	}
	if (sscanf(buf, "%d", &enabled) != 1) {
	    error_msg = c_format("Error reading result %s: %s",
				 buf, strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    close(fd);
	    return (XORP_ERROR);
	}
	close(fd);
    }
#elif defined(HOST_OS_WINDOWS)
    {
	MIB_IPSTATS ipstats;
	DWORD error = GetIpStatistics(&ipstats);
	if (error != NO_ERROR) {
	    XLOG_ERROR("GetIpStatistics() failed: %s",
		       win_strerror(GetLastError()));
	    return (XORP_ERROR);
	}
	enabled = (int)(ipstats.dwForwarding == MIB_IP_FORWARDING);
    }
#else
#error "OS not supported: don't know how to test whether"
#error "IPv4 unicast forwarding is enabled/disabled"
#endif
    
    if (enabled > 0)
	ret_value = true;
    else
	ret_value = false;
    
    return (XORP_OK);
}

/**
 * Test whether the IPv6 unicast forwarding engine is enabled or disabled
 * to forward packets.
 * 
 * @param ret_value if true on return, then the IPv6 unicast forwarding
 * is enabled, otherwise is disabled.
 * @param error_msg the error message (if error).
 * @return XORP_OK on success, otherwise XORP_ERROR.
 */
int
FtiConfig::unicast_forwarding_enabled6(bool& ret_value, string& error_msg) const
{
    // XXX: always return true if running in dummy mode
    if (is_dummy()) {
	ret_value = true;
	return (XORP_OK);
    }

#ifndef HAVE_IPV6
    ret_value = false;
    error_msg = c_format("Cannot test whether IPv6 unicast forwarding "
			 "is enabled: IPv6 is not supported");
    XLOG_ERROR("%s", error_msg.c_str());
    return (XORP_ERROR);
    
#else // HAVE_IPV6

    if (! have_ipv6()) {
	ret_value = false;
	error_msg = c_format("Cannot test whether IPv6 unicast forwarding "
			     "is enabled: IPv6 is not supported");
	XLOG_ERROR("%s", error_msg.c_str());
	return (XORP_ERROR);
    }
    
    int enabled = 0;
    
#if defined(CTL_NET) && defined(IPPROTO_IPV6) && defined(IPV6CTL_FORWARDING)
    {
	size_t sz = sizeof(enabled);
	int mib[4];
	
	mib[0] = CTL_NET;
	mib[1] = AF_INET6;
	mib[2] = IPPROTO_IPV6;
	mib[3] = IPV6CTL_FORWARDING;
	
	if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), &enabled, &sz, NULL, 0)
	    != 0) {
	    error_msg = c_format("Get sysctl(IPV6CTL_FORWARDING) failed: %s",
				 strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    return (XORP_ERROR);
	}
    }
#elif defined(HOST_OS_LINUX)
    {
	FILE *fh = fopen(PROC_LINUX_FILE_FORWARDING_V6, "r");
	
	if (fh == NULL) {
	    error_msg = c_format("Cannot open file %s for reading: %s",
				 PROC_LINUX_FILE_FORWARDING_V6,
				 strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    return (XORP_ERROR);
	}
	if (fscanf(fh, "%d", &enabled) != 1) {
	    error_msg = c_format("Error reading file %s: %s",
				 PROC_LINUX_FILE_FORWARDING_V6,
				 strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    fclose(fh);
	    return (XORP_ERROR);
	}
	fclose(fh);
    }
#elif defined(HOST_OS_SOLARIS)
    {
	struct strioctl strioctl;
	char buf[256];
	int fd;

	fd = open(DEV_SOLARIS_DRIVER_FORWARDING_V6, O_RDONLY);
	if (fd < 0) {
	    error_msg = c_format("Cannot open file %s for reading: %s",
				 DEV_SOLARIS_DRIVER_FORWARDING_V6,
				 strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    return (XORP_ERROR);
	}
	int r = isastream(fd);
	if (r < 0) {
	    error_msg = c_format("Error testing whether file %s is a stream: %s",
				 DEV_SOLARIS_DRIVER_FORWARDING_V6,
				 strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    close(fd);
	    return (XORP_ERROR);
	}
	if (r == 0) {
	    error_msg = c_format("File %s is not a stream",
				 DEV_SOLARIS_DRIVER_FORWARDING_V6);
	    XLOG_ERROR("%s", error_msg.c_str());
	    close(fd);
	    return (XORP_ERROR);
	}

	memset(&strioctl, 0, sizeof(strioctl));
	memset(buf, 0, sizeof(buf));
	strncpy(buf, DEV_SOLARIS_DRIVER_PARAMETER_FORWARDING_V6,
		sizeof(buf) - 1);
	strioctl.ic_cmd = ND_GET;
	strioctl.ic_timout = 0;
	strioctl.ic_len = sizeof(buf);
	strioctl.ic_dp = buf;
	if (ioctl(fd, I_STR, &strioctl) < 0) {
	    error_msg = c_format("Error testing whether IPv6 unicast "
				 "forwarding is enabled: %s",
				 strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    close(fd);
	    return (XORP_ERROR);
	}
	if (sscanf(buf, "%d", &enabled) != 1) {
	    error_msg = c_format("Error reading result %s: %s",
				 buf, strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    close(fd);
	    return (XORP_ERROR);
	}
	close(fd);
    }
#elif defined(HOST_OS_WINDOWS) && 0
    // XXX: Not in MinGW w32api yet.
    {
	MIB_IPSTATS ipstats;
	DWORD error = GetIpStatisticsEx(&ipstats, AF_INET6);
	if (error != NO_ERROR) {
	    XLOG_ERROR("GetIpStatisticsEx() failed: %s",
		       win_strerror(GetLastError()));
	    return (XORP_ERROR);
	}
	enabled = (int)(ipstats.dwForwarding == MIB_IP_FORWARDING);
    }
#else
#error "OS not supported: don't know how to test whether"
#error "IPv6 unicast forwarding is enabled/disabled"
#endif
    
    if (enabled > 0)
	ret_value = true;
    else
	ret_value = false;
    
    return (XORP_OK);
#endif // HAVE_IPV6
}

/**
 * Test whether the acceptance of IPv6 Router Advertisement messages is
 * enabled or disabled.
 * 
 * @param ret_value if true on return, then the acceptance of IPv6 Router
 * Advertisement messages is enabled, otherwise is disabled.
 * @param error_msg the error message (if error).
 * @return XORP_OK on success, otherwise XORP_ERROR.
 */
int
FtiConfig::accept_rtadv_enabled6(bool& ret_value, string& error_msg) const
{
    // XXX: always return true if running in dummy mode
    if (is_dummy()) {
	ret_value = true;
	return (XORP_OK);
    }

#ifndef HAVE_IPV6
    ret_value = false;
    error_msg = c_format("Cannot test whether the acceptance of IPv6 "
			 "Router Advertisement messages is enabled: "
			 "IPv6 is not supported");
    XLOG_ERROR("%s", error_msg.c_str());
    return (XORP_ERROR);
    
#else // HAVE_IPV6

    if (! have_ipv6()) {
	ret_value = false;
	error_msg = c_format("Cannot test whether the acceptance of IPv6 "
			     "Router Advertisement messages is enabled: "
			     "IPv6 is not supported");
	XLOG_ERROR("%s", error_msg.c_str());
	return (XORP_ERROR);
    }
    
    int enabled = 0;
    
#if defined(CTL_NET) && defined(IPPROTO_IPV6) && defined(IPV6CTL_ACCEPT_RTADV)
    {
	size_t sz = sizeof(enabled);
	int mib[4];
	
	mib[0] = CTL_NET;
	mib[1] = AF_INET6;
	mib[2] = IPPROTO_IPV6;
	mib[3] = IPV6CTL_ACCEPT_RTADV;
	
	if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), &enabled, &sz, NULL, 0)
	    != 0) {
	    error_msg = c_format("Get sysctl(IPV6CTL_ACCEPT_RTADV) failed: %s",
				 strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    return (XORP_ERROR);
	}
    }
#elif defined(HOST_OS_LINUX)
    // XXX: nothing to do in case of Linux
    error_msg = "";
#elif defined(HOST_OS_SOLARIS)
    {
	struct strioctl strioctl;
	char buf[256];
	int fd;
	int ignore_redirect = 0;

	fd = open(DEV_SOLARIS_DRIVER_FORWARDING_V6, O_RDONLY);
	if (fd < 0) {
	    error_msg = c_format("Cannot open file %s for reading: %s",
				 DEV_SOLARIS_DRIVER_FORWARDING_V6,
				 strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    return (XORP_ERROR);
	}
	int r = isastream(fd);
	if (r < 0) {
	    error_msg = c_format("Error testing whether file %s is a stream: %s",
				 DEV_SOLARIS_DRIVER_FORWARDING_V6,
				 strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    close(fd);
	    return (XORP_ERROR);
	}
	if (r == 0) {
	    error_msg = c_format("File %s is not a stream",
				 DEV_SOLARIS_DRIVER_FORWARDING_V6);
	    XLOG_ERROR("%s", error_msg.c_str());
	    close(fd);
	    return (XORP_ERROR);
	}

	memset(&strioctl, 0, sizeof(strioctl));
	memset(buf, 0, sizeof(buf));
	strncpy(buf, DEV_SOLARIS_DRIVER_PARAMETER_IGNORE_REDIRECT_V6,
		sizeof(buf) - 1);
	strioctl.ic_cmd = ND_GET;
	strioctl.ic_timout = 0;
	strioctl.ic_len = sizeof(buf);
	strioctl.ic_dp = buf;
	if (ioctl(fd, I_STR, &strioctl) < 0) {
	    error_msg = c_format("Error testing whether the acceptance of "
				 "IPv6 Router Advertisement messages is "
				 "enabled: %s",
				 strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    close(fd);
	    return (XORP_ERROR);
	}
	if (sscanf(buf, "%d", &ignore_redirect) != 1) {
	    error_msg = c_format("Error reading result %s: %s",
				 buf, strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    close(fd);
	    return (XORP_ERROR);
	}
	close(fd);

	//
	// XXX: The logic of "Accept IPv6 Router Advertisement" is just the
	// opposite of "Ignore Redirect".
	//
	if (ignore_redirect == 0)
	    enabled = 1;
	else
	    enabled = 0;
    }
#else
#error "OS not supported: don't know how to test whether"
#error "the acceptance of IPv6 Router Advertisement messages"
#error "is enabled/disabled"
#endif
    
    if (enabled > 0)
	ret_value = true;
    else
	ret_value = false;
    
    return (XORP_OK);
#endif // HAVE_IPV6
}

/**
 * Set the IPv4 unicast forwarding engine to enable or disable forwarding
 * of packets.
 * 
 * @param v if true, then enable IPv4 unicast forwarding, otherwise
 * disable it.
 * @param error_msg the error message (if error).
 * @return XORP_OK on success, otherwise XORP_ERROR.
 */
int
FtiConfig::set_unicast_forwarding_enabled4(bool v, string& error_msg)
{
    // XXX: don't do anything if running in dummy mode
    if (is_dummy())
	return (XORP_OK);

    if (! have_ipv4()) {
	if (! v) {
	    //
	    // XXX: we assume that "not supported" == "disable", hence
	    // return OK.
	    //
	    return (XORP_OK);
	}
	error_msg = c_format("Cannot set IPv4 unicast forwarding to %s: "
			     "IPv4 is not supported", (v) ? "true": "false");
	XLOG_ERROR("%s", error_msg.c_str());
	return (XORP_ERROR);
    }

    int enable = (v) ? 1 : 0;
    bool old_value;
    
    if (unicast_forwarding_enabled4(old_value, error_msg) < 0)
	return (XORP_ERROR);
    
    if (old_value == v)
	return (XORP_OK);	// Nothing changed
    
#if defined(CTL_NET) && defined(IPPROTO_IP) && defined(IPCTL_FORWARDING)
    {
	size_t sz = sizeof(enable);
	int mib[4];
	
	mib[0] = CTL_NET;
	mib[1] = AF_INET;
	mib[2] = IPPROTO_IP;
	mib[3] = IPCTL_FORWARDING;
	
	if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, NULL, &enable, sz)
	    != 0) {
	    error_msg = c_format("Set sysctl(IPCTL_FORWARDING) to %s failed: %s",
				 (v) ? "true" : "false", strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    return (XORP_ERROR);
	}
    }
#elif defined(HOST_OS_LINUX)
    {
	FILE *fh = fopen(PROC_LINUX_FILE_FORWARDING_V4, "w");
	
	if (fh == NULL) {
	    error_msg = c_format("Cannot open file %s for writing: %s",
				 PROC_LINUX_FILE_FORWARDING_V4,
				 strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    return (XORP_ERROR);
	}
	if (fprintf(fh, "%d", enable) != 1) {
	    error_msg = c_format("Error writing %d to file %s: %s",
				 enable,
				 PROC_LINUX_FILE_FORWARDING_V4,
				 strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    fclose(fh);
	    return (XORP_ERROR);
	}
	fclose(fh);
    }
#elif defined(HOST_OS_SOLARIS)
    {
	struct strioctl strioctl;
	char buf[256];
	int fd;

	fd = open(DEV_SOLARIS_DRIVER_FORWARDING_V4, O_WRONLY);
	if (fd < 0) {
	    error_msg = c_format("Cannot open file %s for writing: %s",
				 DEV_SOLARIS_DRIVER_FORWARDING_V4,
				 strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    return (XORP_ERROR);
	}
	int r = isastream(fd);
	if (r < 0) {
	    error_msg = c_format("Error testing whether file %s is a stream: %s",
				 DEV_SOLARIS_DRIVER_FORWARDING_V4,
				 strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    close(fd);
	    return (XORP_ERROR);
	}
	if (r == 0) {
	    error_msg = c_format("File %s is not a stream",
				 DEV_SOLARIS_DRIVER_FORWARDING_V4);
	    XLOG_ERROR("%s", error_msg.c_str());
	    close(fd);
	    return (XORP_ERROR);
	}

	memset(&strioctl, 0, sizeof(strioctl));
	memset(buf, 0, sizeof(buf));
	snprintf(buf, sizeof(buf) - 1, "%s %d",
		 DEV_SOLARIS_DRIVER_PARAMETER_FORWARDING_V4, enable);
	strioctl.ic_cmd = ND_SET;
	strioctl.ic_timout = 0;
	strioctl.ic_len = sizeof(buf);
	strioctl.ic_dp = buf;
	if (ioctl(fd, I_STR, &strioctl) < 0) {
	    error_msg = c_format("Cannot set IPv4 unicast forwarding to %s: %s",
				 (v) ? "true": "false", strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    close(fd);
	    return (XORP_ERROR);
	}
	close(fd);
    }
#elif defined(HOST_OS_WINDOWS)
    if (enable) {
	if (WinSupport::is_rras_running()) {
	    XLOG_WARNING("RRAS is running; ignoring request to enable "
			 "IPv4 forwarding.");
	    return (XORP_OK);
	}
	HANDLE hFwd;
	DWORD result = EnableRouter(&hFwd, &_overlapped);
	if (result != ERROR_IO_PENDING) {
	    error_msg = c_format("Error '%s' from EnableRouter",
				 win_strerror(GetLastError()));
	    XLOG_ERROR("%s", error_msg.c_str());
	    return (XORP_OK);	// XXX: This error is non-fatal.
	}
	++_enablecnt;
    } else {
	if (WinSupport::is_rras_running()) {
	    XLOG_WARNING("RRAS is running; ignoring request to disable "
			 "IPv4 forwarding.");
	    return (XORP_OK);
	}
	if (_enablecnt == 0) {
	    error_msg = c_format("UnenableRouter() called without any previous "
				 "call to EnableRouter()");
	    XLOG_ERROR("%s", error_msg.c_str());
	    return (XORP_OK);	// XXX: This error is non-fatal.
	}

	DWORD result = UnenableRouter(&_overlapped, NULL);
	if (result != NO_ERROR) {
	    error_msg = c_format("Error '%s' from UnenableRouter",
				 win_strerror(GetLastError()));
	    XLOG_ERROR("%s", error_msg.c_str());
	    return (XORP_OK);	// XXX: This error is non-fatal.
	}
	--_enablecnt;
    }
#else
#error "OS not supported: don't know how to enable/disable"
#error "IPv4 unicast forwarding"
#endif
    
    return (XORP_OK);
}

/**
 * Set the IPv6 unicast forwarding engine to enable or disable forwarding
 * of packets.
 * 
 * @param v if true, then enable IPv6 unicast forwarding, otherwise
 * disable it.
 * @param error_msg the error message (if error).
 * @return XORP_OK on success, otherwise XORP_ERROR.
 */
int
FtiConfig::set_unicast_forwarding_enabled6(bool v, string& error_msg)
{
    // XXX: don't do anything if running in dummy mode
    if (is_dummy())
	return (XORP_OK);

#ifndef HAVE_IPV6
    if (! v) {
	//
	// XXX: we assume that "not supported" == "disable", hence
	// return OK.
	//
	return (XORP_OK);
    }

    error_msg = c_format("Cannot set IPv6 unicast forwarding to %s: "
			 "IPv6 is not supported", (v) ? "true": "false");
    XLOG_ERROR("%s", error_msg.c_str());
    return (XORP_ERROR);
    
#else // HAVE_IPV6

    if (! have_ipv6()) {
	if (! v) {
	    //
	    // XXX: we assume that "not supported" == "disable", hence
	    // return OK.
	    //
	    return (XORP_OK);
	}

	error_msg = c_format("Cannot set IPv6 unicast forwarding to %s: "
			     "IPv6 is not supported", (v) ? "true": "false");
	XLOG_ERROR("%s", error_msg.c_str());
	return (XORP_ERROR);
    }

    int enable = (v) ? 1 : 0;
    bool old_value, old_value_accept_rtadv;
    
    if (unicast_forwarding_enabled6(old_value, error_msg) < 0)
	return (XORP_ERROR);
    if (accept_rtadv_enabled6(old_value_accept_rtadv, error_msg) < 0)
	return (XORP_ERROR);
    
    if ((old_value == v) && (old_value_accept_rtadv == !v))
	return (XORP_OK);	// Nothing changed
    
    if (set_accept_rtadv_enabled6(!v, error_msg) < 0)
	return (XORP_ERROR);
    
#if defined(CTL_NET) && defined(IPPROTO_IPV6) && defined(IPV6CTL_FORWARDING)
    {
	size_t sz = sizeof(enable);
	int mib[4];
	
	mib[0] = CTL_NET;
	mib[1] = AF_INET6;
	mib[2] = IPPROTO_IPV6;
	mib[3] = IPV6CTL_FORWARDING;
	
	if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, NULL, &enable, sz)
	    != 0) {
	    error_msg = c_format("Set sysctl(IPV6CTL_FORWARDING) to %s failed: %s",
				 (v) ? "true" : "false", strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    // Restore the old accept_rtadv value
	    if (old_value_accept_rtadv != !v) {
		string dummy_error_msg;
		set_accept_rtadv_enabled6(old_value_accept_rtadv,
					  dummy_error_msg);
	    }
	    return (XORP_ERROR);
	}
    }
#elif defined(HOST_OS_LINUX)
    {
	FILE *fh = fopen(PROC_LINUX_FILE_FORWARDING_V6, "w");
	
	if (fh == NULL) {
	    error_msg = c_format("Cannot open file %s for writing: %s",
				 PROC_LINUX_FILE_FORWARDING_V6,
				 strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    return (XORP_ERROR);
	}
	if (fprintf(fh, "%d", enable) != 1) {
	    error_msg = c_format("Error writing %d to file %s: %s",
				 enable,
				 PROC_LINUX_FILE_FORWARDING_V6,
				 strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    // Restore the old accept_rtadv value
	    if (old_value_accept_rtadv != !v) {
		string dummy_error_msg;
		set_accept_rtadv_enabled6(old_value_accept_rtadv,
					  dummy_error_msg);
	    }
	    fclose(fh);
	    return (XORP_ERROR);
	}
	fclose(fh);
    }
#elif defined(HOST_OS_SOLARIS)
    {
	struct strioctl strioctl;
	char buf[256];
	int fd;

	fd = open(DEV_SOLARIS_DRIVER_FORWARDING_V6, O_WRONLY);
	if (fd < 0) {
	    error_msg = c_format("Cannot open file %s for writing: %s",
				 DEV_SOLARIS_DRIVER_FORWARDING_V6,
				 strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    return (XORP_ERROR);
	}
	int r = isastream(fd);
	if (r < 0) {
	    error_msg = c_format("Error testing whether file %s is a stream: %s",
				 DEV_SOLARIS_DRIVER_FORWARDING_V6,
				 strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    close(fd);
	    return (XORP_ERROR);
	}
	if (r == 0) {
	    error_msg = c_format("File %s is not a stream",
				 DEV_SOLARIS_DRIVER_FORWARDING_V6);
	    XLOG_ERROR("%s", error_msg.c_str());
	    close(fd);
	    return (XORP_ERROR);
	}

	memset(&strioctl, 0, sizeof(strioctl));
	memset(buf, 0, sizeof(buf));
	snprintf(buf, sizeof(buf) - 1, "%s %d",
		 DEV_SOLARIS_DRIVER_PARAMETER_FORWARDING_V6, enable);
	strioctl.ic_cmd = ND_SET;
	strioctl.ic_timout = 0;
	strioctl.ic_len = sizeof(buf);
	strioctl.ic_dp = buf;
	if (ioctl(fd, I_STR, &strioctl) < 0) {
	    error_msg = c_format("Cannot set IPv6 unicast forwarding to %s: %s",
				 (v) ? "true": "false", strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    close(fd);
	    return (XORP_ERROR);
	}
	close(fd);
    }
#elif defined(HOST_OS_WINDOWS) && 0
    // XXX: Not yet in MinGW w32api
    {
	MIB_IPSTATS ipstats;
	DWORD error = GetIpStatisticsEx(&ipstats, AF_INET6);
	if (error != NO_ERROR) {
	    XLOG_ERROR("GetIpStatisticsEx() failed: %s",
		       win_strerror(GetLastError()));
	    return (XORP_ERROR);
	}
	ipstats.dwForwarding = (enable != 0) ? 1 : 0;
	ipstats.dwDefaultTTL = MIB_USE_CURRENT_TTL;
	error = SetIpStatisticsEx(&ipstats, AF_INET6);
	if (error != NO_ERROR) {
	    XLOG_ERROR("SetIpStatisticsEx() failed: %s",
		       win_strerror(GetLastError()));
	    return (XORP_ERROR);
	}
    }
#else
#error "OS not supported: don't know how to enable/disable"
#error "IPv6 unicast forwarding"
#endif
    
    return (XORP_OK);
#endif // HAVE_IPV6
}

/**
 * Enable or disable the acceptance of IPv6 Router Advertisement messages
 * from other routers. It should be enabled for hosts, and disabled for
 * routers.
 * 
 * @param v if true, then enable the acceptance of IPv6 Router Advertisement
 * messages, otherwise disable it.
 * @param error_msg the error message (if error).
 * @return XORP_OK on success, otherwise XORP_ERROR.
 */
int
FtiConfig::set_accept_rtadv_enabled6(bool v, string& error_msg)
{
    // XXX: don't do anything if running in dummy mode
    if (is_dummy())
	return (XORP_OK);

#ifndef HAVE_IPV6
    if (! v) {
	//
	// XXX: we assume that "not supported" == "disable", hence
	// return OK.
	//
	return (XORP_OK);
    }

    error_msg = c_format("Cannot set the acceptance of IPv6 "
			 "Router Advertisement messages to %s: "
			 "IPv6 is not supported",
			 (v) ? "true": "false");
    XLOG_ERROR("%s", error_msg.c_str());
    return (XORP_ERROR);
    
#else // HAVE_IPV6

    if (! have_ipv6()) {
	if (! v) {
	    //
	    // XXX: we assume that "not supported" == "disable", hence
	    // return OK.
	    //
	    return (XORP_OK);
	}

	error_msg = c_format("Cannot set the acceptance of IPv6 "
			     "Router Advertisement messages to %s: "
			     "IPv6 is not supported",
			     (v) ? "true": "false");
	XLOG_ERROR("%s", error_msg.c_str());
	return (XORP_ERROR);
    }

    int enable = (v) ? 1 : 0;
    bool old_value;
    
    if (accept_rtadv_enabled6(old_value, error_msg) < 0)
	return (XORP_ERROR);
    
    if (old_value == v)
	return (XORP_OK);	// Nothing changed
    
#if defined(CTL_NET) && defined(IPPROTO_IPV6) && defined(IPV6CTL_ACCEPT_RTADV)
    {
	size_t sz = sizeof(enable);
	int mib[4];
	
	mib[0] = CTL_NET;
	mib[1] = AF_INET6;
	mib[2] = IPPROTO_IPV6;
	mib[3] = IPV6CTL_ACCEPT_RTADV;
	
	if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, NULL, &enable, sz)
	    != 0) {
	    error_msg = c_format("Set sysctl(IPV6CTL_ACCEPT_RTADV) to %s failed: %s",
				 (v) ? "true" : "false", strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    return (XORP_ERROR);
	}
    }
#elif defined(HOST_OS_LINUX)
    {
	// XXX: nothing to do in case of Linux
	error_msg = "";
	UNUSED(enable);
    }
#elif defined(HOST_OS_SOLARIS)
    {
	struct strioctl strioctl;
	char buf[256];
	int fd;
	int ignore_redirect = 0;

	//
	// XXX: The logic of "Accept IPv6 Router Advertisement" is just the
	// opposite of "Ignore Redirect".
	//
	if (enable == 0)
	    ignore_redirect = 1;
	else
	    ignore_redirect = 0;

	fd = open(DEV_SOLARIS_DRIVER_FORWARDING_V6, O_WRONLY);
	if (fd < 0) {
	    error_msg = c_format("Cannot open file %s for writing: %s",
				 DEV_SOLARIS_DRIVER_FORWARDING_V6,
				 strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    return (XORP_ERROR);
	}
	int r = isastream(fd);
	if (r < 0) {
	    error_msg = c_format("Error testing whether file %s is a stream: %s",
				 DEV_SOLARIS_DRIVER_FORWARDING_V6,
				 strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    close(fd);
	    return (XORP_ERROR);
	}
	if (r == 0) {
	    error_msg = c_format("File %s is not a stream",
				 DEV_SOLARIS_DRIVER_FORWARDING_V6);
	    XLOG_ERROR("%s", error_msg.c_str());
	    close(fd);
	    return (XORP_ERROR);
	}

	memset(&strioctl, 0, sizeof(strioctl));
	memset(buf, 0, sizeof(buf));
	snprintf(buf, sizeof(buf) - 1, "%s %d",
		 DEV_SOLARIS_DRIVER_PARAMETER_IGNORE_REDIRECT_V6,
		 ignore_redirect);
	strioctl.ic_cmd = ND_SET;
	strioctl.ic_timout = 0;
	strioctl.ic_len = sizeof(buf);
	strioctl.ic_dp = buf;
	if (ioctl(fd, I_STR, &strioctl) < 0) {
	    error_msg = c_format("Cannot set IPv6 unicast forwarding to %s: %s",
				 (v) ? "true": "false", strerror(errno));
	    XLOG_ERROR("%s", error_msg.c_str());
	    close(fd);
	    return (XORP_ERROR);
	}
	close(fd);
    }
#elif defined(HOST_OS_WINDOWS)
    {
	// XXX: nothing to do in case of Windows
	error_msg = "";
	UNUSED(enable);
    }

#else
#error "OS not supported: don't know how to enable/disable"
#error "the acceptance of IPv6 Router Advertisement messages"
#endif
    
    return (XORP_OK);
#endif // HAVE_IPV6
}

/**
 * Set the IPv4 unicast forwarding engine whether to retain existing
 * XORP forwarding entries on startup.
 *
 * @param retain if true, then retain the XORP forwarding entries,
 * otherwise delete them.
 * @param error_msg the error message (if error).
 * @return XORP_OK on success, otherwise XORP_ERROR.
 */
int
FtiConfig::set_unicast_forwarding_entries_retain_on_startup4(bool retain,
							     string& error_msg)
{
    _unicast_forwarding_entries_retain_on_startup4 = retain;

    error_msg = "";		// XXX: reset
    return (XORP_OK);
}

/**
 * Set the IPv4 unicast forwarding engine whether to retain existing
 * XORP forwarding entries on shutdown.
 *
 * @param retain if true, then retain the XORP forwarding entries,
 * otherwise delete them.
 * @param error_msg the error message (if error).
 * @return XORP_OK on success, otherwise XORP_ERROR.
 */
int
FtiConfig::set_unicast_forwarding_entries_retain_on_shutdown4(bool retain,
							      string& error_msg)
{
    _unicast_forwarding_entries_retain_on_shutdown4 = retain;

    error_msg = "";		// XXX: reset
    return (XORP_OK);
}

/**
 * Set the IPv6 unicast forwarding engine whether to retain existing
 * XORP forwarding entries on startup.
 *
 * @param retain if true, then retain the XORP forwarding entries,
 * otherwise delete them.
 * @param error_msg the error message (if error).
 * @return XORP_OK on success, otherwise XORP_ERROR.
 */
int
FtiConfig::set_unicast_forwarding_entries_retain_on_startup6(bool retain,
							     string& error_msg)
{
    _unicast_forwarding_entries_retain_on_startup6 = retain;

    error_msg = "";		// XXX: reset
    return (XORP_OK);
}

/**
 * Set the IPv6 unicast forwarding engine whether to retain existing
 * XORP forwarding entries on shutdown.
 *
 * @param retain if true, then retain the XORP forwarding entries,
 * otherwise delete them.
 * @param error_msg the error message (if error).
 * @return XORP_OK on success, otherwise XORP_ERROR.
 */
int
FtiConfig::set_unicast_forwarding_entries_retain_on_shutdown6(bool retain,
							      string& error_msg)
{
    _unicast_forwarding_entries_retain_on_shutdown6 = retain;

    error_msg = "";		// XXX: reset
    return (XORP_OK);
}


syntax highlighted by Code2HTML, v. 0.9.1