// -*- 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/xrl_rawsock6.cc,v 1.12 2007/02/16 22:45:53 pavlin Exp $"

#include "fea_module.h"

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

#include "xrl/interfaces/fea_rawpkt6_client_xif.hh"

#include "iftree.hh"
#include "rawsock6.hh"
#include "xrl_rawsock6.hh"

class XrlFilterRawSocket6 : public FilterRawSocket6::InputFilter {
public:
    XrlFilterRawSocket6(XrlRawSocket6Manager&	rsm,
			const string&		xrl_target_name,
			uint32_t		ip_protocol)
	: _rsm(rsm),
	  _xrl_target_name(xrl_target_name),
	  _ip_protocol(ip_protocol)
    {}

    XrlRawSocket6Manager& manager()		{ return _rsm; }
    const string&	xrl_target_name() const	{ return _xrl_target_name; }
    uint32_t		ip_protocol() const	{ return _ip_protocol; }

protected:
    XrlRawSocket6Manager&	_rsm;
    const string		_xrl_target_name;
    const uint32_t		_ip_protocol;
};

//
// Filter class for checking incoming raw packets and checking whether
// to forward them.
//
class XrlVifInputFilter6 : public XrlFilterRawSocket6 {
public:
    XrlVifInputFilter6(XrlRawSocket6Manager&	rsm,
		       FilterRawSocket6&	rs,
		       const string&		xrl_target_name,
		       const string&		if_name,
		       const string&		vif_name,
		       uint32_t			ip_protocol)
	: XrlFilterRawSocket6(rsm, xrl_target_name, ip_protocol),
	  _rs(rs),
	  _if_name(if_name),
	  _vif_name(vif_name),
	  _enable_multicast_loopback(false)
    {}

    virtual ~XrlVifInputFilter6() {
	leave_all_multicast_groups();
    }

    void set_enable_multicast_loopback(bool v) { _enable_multicast_loopback = v; }

    void recv(const struct IPv6HeaderInfo& header,
	      const vector<uint8_t>& payload)
    {
	// Check the protocol
	if ((ip_protocol() != 0) && (ip_protocol() != header.ip_protocol)) {
	    debug_msg("Ignore packet with protocol %u (watching for %u)\n",
		      XORP_UINT_CAST(header.ip_protocol),
		      XORP_UINT_CAST(ip_protocol()));
	    return;
	}

	// Check the interface name
	if ((! _if_name.empty()) && (_if_name != header.if_name)) {
	    debug_msg("Ignore packet with interface %s (watching for %s)\n",
		      header.if_name.c_str(),
		      _if_name.c_str());
	    return;
	}

	// Check the vif name
	if ((! _vif_name.empty()) && (_vif_name != header.vif_name)) {
	    debug_msg("Ignore packet with vif %s (watching for %s)\n",
		      header.vif_name.c_str(),
		      _vif_name.c_str());
	    return;
	}

	// Check if multicast loopback is enabled
	if (header.dst_address.is_multicast()
	    && is_my_address(header.src_address)
	    && (! _enable_multicast_loopback)) {
	    debug_msg("Ignore packet with src %s dst %s: "
		      "multicast loopback is disabled\n",
		      header.src_address.str().c_str(),
		      header.dst_address.str().c_str());
	    return;
	}

	//
	// Create the extention headers info
	//
	XLOG_ASSERT(header.ext_headers_type.size()
		    == header.ext_headers_payload.size());
	XrlAtomList ext_headers_type_list, ext_headers_payload_list;
	size_t i;
	for (i = 0; i < header.ext_headers_type.size(); i++) {
	    ext_headers_type_list.append(XrlAtom(static_cast<uint32_t>(header.ext_headers_type[i])));
	    ext_headers_payload_list.append(XrlAtom(header.ext_headers_payload[i]));
	}

	//
	// Instantiate client sending interface
	//
	XrlRawPacket6ClientV0p1Client cl(&_rsm.router());

	//
	// Send notification, note callback goes to owning
	// XrlRawSocket6Manager instance since send failure to xrl_target
	// is useful for reaping all filters to connected to target.
	//
	cl.send_recv(_xrl_target_name.c_str(),
		     header.if_name,
		     header.vif_name,
		     header.src_address,
		     header.dst_address,
		     header.ip_protocol,
		     header.ip_ttl,
		     header.ip_tos,
		     header.ip_router_alert,
		     ext_headers_type_list,
		     ext_headers_payload_list,
		     payload,
		     callback(&_rsm,
			      &XrlRawSocket6Manager::xrl_send_recv_cb,
			      _xrl_target_name));
    }

    void bye() {}
    const string& if_name() const { return _if_name; }
    const string& vif_name() const { return _vif_name; }

    int join_multicast_group(const IPv6& group_address, string& error_msg) {
	if (_rs.join_multicast_group(if_name(), vif_name(), group_address,
				     xrl_target_name(), error_msg)
	    != XORP_OK) {
	    return (XORP_ERROR);
	}
	_joined_multicast_groups.insert(group_address);
	return (XORP_OK);
    }

    int leave_multicast_group(const IPv6& group_address, string& error_msg) {
	_joined_multicast_groups.erase(group_address);
	if (_rs.leave_multicast_group(if_name(), vif_name(), group_address,
				      xrl_target_name(), error_msg)
	    != XORP_OK) {
	    return (XORP_ERROR);
	}
	return (XORP_OK);
    }

    void leave_all_multicast_groups() {
	string error_msg;
	while (! _joined_multicast_groups.empty()) {
	    const IPv6& group_address = *(_joined_multicast_groups.begin());
	    leave_multicast_group(group_address, error_msg);
	}
    }

protected:
    bool is_my_address(const IPv6& addr) const {
	const IfTreeInterface* iftree_if = NULL;
	const IfTreeVif* iftree_vif = NULL;

	if (_rs.find_interface_vif_by_addr(IPvX(addr), iftree_if, iftree_vif)
	    != true) {
	    return (false);
	}
	if (iftree_if->enabled() && iftree_vif->enabled()) {
	    IfTreeVif::V6Map::const_iterator iter = iftree_vif->get_addr(addr);
	    if (iter != iftree_vif->v6addrs().end()) {
		const IfTreeAddr6* iftree_a6 = &iter->second;
		if (iftree_a6->enabled())
		    return (true);
	    }
	}
	return (false);
    }

    FilterRawSocket6&	_rs;
    const string	_if_name;
    const string	_vif_name;
    set<IPv6>           _joined_multicast_groups;
    bool		_enable_multicast_loopback;
};

// ----------------------------------------------------------------------------
// XrlRawSocket6Manager code

XrlRawSocket6Manager::XrlRawSocket6Manager(EventLoop&		eventloop,
					   const IfTree&	iftree,
					   XrlRouter&		xr)
    : _eventloop(eventloop), _iftree(iftree), _xrlrouter(xr)
{
}

XrlRawSocket6Manager::~XrlRawSocket6Manager()
{
    erase_filters(_filters.begin(), _filters.end());
}

void
XrlRawSocket6Manager::erase_filters(const FilterBag6::iterator& begin,
				    const FilterBag6::iterator& end)
{
    FilterBag6::iterator fi(begin);
    while (fi != end) {
	XrlFilterRawSocket6* filter = fi->second;

	SocketTable6::iterator sti = _sockets.find(filter->ip_protocol());
	XLOG_ASSERT(sti != _sockets.end());
	FilterRawSocket6* rs = sti->second;
	XLOG_ASSERT(rs != NULL);

	rs->remove_filter(filter);
	delete filter;

	_filters.erase(fi++);
    }
}

XrlCmdError
XrlRawSocket6Manager::send(const string&	if_name,
			   const string&	vif_name,
			   const IPv6&		src_address,
			   const IPv6&		dst_address,
			   uint32_t		ip_protocol,
			   int32_t		ip_ttl,
			   int32_t		ip_tos,
			   bool			ip_router_alert,
			   const vector<uint8_t>& ext_headers_type,
			   const vector<vector<uint8_t> >& ext_headers_payload,
			   const vector<uint8_t>& payload)
{
    string error_msg;

    // Find the socket associated with this protocol
    SocketTable6::iterator sti = _sockets.find(ip_protocol);
    if (sti == _sockets.end()) {
	error_msg = c_format("Protocol %u is not registered",
			     XORP_UINT_CAST(ip_protocol));
	return XrlCmdError::COMMAND_FAILED(error_msg);
    }
    FilterRawSocket6* rs = sti->second;
    XLOG_ASSERT(rs != NULL);

    if (rs->proto_socket_write(if_name,
			       vif_name,
			       src_address,
			       dst_address,
			       ip_ttl,
			       ip_tos,
			       ip_router_alert,
			       ext_headers_type,
			       ext_headers_payload,
			       payload,
			       error_msg)
	!= XORP_OK) {
	return XrlCmdError::COMMAND_FAILED(error_msg);
    }

    return XrlCmdError::OKAY();
}

XrlCmdError
XrlRawSocket6Manager::register_receiver(const string&	xrl_target_name,
					const string&	if_name,
					const string&	vif_name,
					uint32_t	ip_protocol,
					bool		enable_multicast_loopback)
{
    XrlVifInputFilter6* filter;

    //
    // Look in the SocketTable for a socket matching this protocol.
    // If a socket does not yet exist, create one.
    //
    SocketTable6::iterator sti = _sockets.find(ip_protocol);
    FilterRawSocket6* rs = NULL;
    if (sti == _sockets.end()) {
	rs = new FilterRawSocket6(_eventloop, ip_protocol, iftree());
	_sockets[ip_protocol] = rs;
    } else {
	rs = sti->second;
    }
    XLOG_ASSERT(rs != NULL);

    FilterBag6::iterator fi;
    FilterBag6::iterator fi_end = _filters.upper_bound(xrl_target_name);
    for (fi = _filters.lower_bound(xrl_target_name); fi != fi_end; ++fi) {
	filter = dynamic_cast<XrlVifInputFilter6*>(fi->second);
	if (filter == NULL)
	    continue; // Not a vif filter

	//
	// Search if we have already the filter
	//
	if ((filter->ip_protocol() == ip_protocol) &&
	    (filter->if_name() == if_name) &&
	    (filter->vif_name() == vif_name)) {
	    // Already have this filter
	    filter->set_enable_multicast_loopback(enable_multicast_loopback);
	    return XrlCmdError::OKAY();
	}
    }

    //
    // Create the filter
    //
    filter = new XrlVifInputFilter6(*this, *rs, xrl_target_name, if_name,
				    vif_name, ip_protocol);
    filter->set_enable_multicast_loopback(enable_multicast_loopback);

    // Add the filter to the appropriate raw socket
    rs->add_filter(filter);

    // Add the filter to those associated with xrl_target_name
    _filters.insert(FilterBag6::value_type(xrl_target_name, filter));

    return XrlCmdError::OKAY();
}

XrlCmdError
XrlRawSocket6Manager::unregister_receiver(const string&	xrl_target_name,
					  const string&	if_name,
					  const string&	vif_name,
					  uint32_t	ip_protocol)
{
    string error_msg;

    //
    // Find the socket associated with this protocol
    //
    SocketTable6::iterator sti = _sockets.find(ip_protocol);
    if (sti == _sockets.end()) {
	error_msg = c_format("Protocol %u is not registered",
			     XORP_UINT_CAST(ip_protocol));
	return XrlCmdError::COMMAND_FAILED(error_msg);
    }
    FilterRawSocket6* rs = sti->second;
    XLOG_ASSERT(rs != NULL);

    //
    // Walk through list of filters looking for matching interface and vif
    //
    FilterBag6::iterator fi;
    FilterBag6::iterator fi_end = _filters.upper_bound(xrl_target_name);
    for (fi = _filters.lower_bound(xrl_target_name); fi != fi_end; ++fi) {
	XrlVifInputFilter6* filter;
	filter = dynamic_cast<XrlVifInputFilter6*>(fi->second);

	// If filter found, remove it and delete it
	if ((filter != NULL) &&
	    (filter->ip_protocol() == ip_protocol) &&
	    (filter->if_name() == if_name) &&
	    (filter->vif_name() == vif_name)) {

	    // Remove the filter
	    rs->remove_filter(filter);

	    // Remove the filter from the group associated with this target
	    _filters.erase(fi);

	    // Destruct the filter
	    delete filter;

	    //
	    // Reference counting: if there are now no listeners on
	    // this protocol socket (and hence no filters), remove it
	    // from the table and delete it.
	    //
	    if (rs->empty()) {
		_sockets.erase(ip_protocol);
		delete rs;
	    }

	    return XrlCmdError::OKAY();
	}
    }

    error_msg = c_format("Cannot find registration for target %s interface %s "
			 "and vif %s",
			 xrl_target_name.c_str(),
			 if_name.c_str(),
			 vif_name.c_str());
    return XrlCmdError::COMMAND_FAILED(error_msg);
}

XrlCmdError
XrlRawSocket6Manager::join_multicast_group(const string& xrl_target_name,
					   const string& if_name,
					   const string& vif_name,
					   uint32_t	 ip_protocol,
					   const IPv6&	 group_address)
{
    string error_msg;

    //
    // Search if we have already the filter
    //
    FilterBag6::iterator fi;
    FilterBag6::iterator fi_end = _filters.upper_bound(xrl_target_name);
    for (fi = _filters.lower_bound(xrl_target_name); fi != fi_end; ++fi) {
	XrlVifInputFilter6* filter;
	filter = dynamic_cast<XrlVifInputFilter6*>(fi->second);
	if (filter == NULL)
	    continue; // Not a vif filter

	if ((filter->ip_protocol() == ip_protocol) &&
	    (filter->if_name() == if_name) &&
	    (filter->vif_name() == vif_name)) {
	    // Filter found
	    if (filter->join_multicast_group(group_address, error_msg)
		!= XORP_OK) {
		return XrlCmdError::COMMAND_FAILED(error_msg);
	    }
	    return XrlCmdError::OKAY();
	}
    }

    error_msg = c_format("Cannot join group %s on interface %s vif %s "
			 "protocol %u target %s: the target has not "
			 "registered as a receiver",
			 group_address.str().c_str(),
			 if_name.c_str(),
			 vif_name.c_str(),
			 XORP_UINT_CAST(ip_protocol),
			 xrl_target_name.c_str());

    return XrlCmdError::COMMAND_FAILED(error_msg);
}

XrlCmdError
XrlRawSocket6Manager::leave_multicast_group(const string& xrl_target_name,
					    const string& if_name,
					    const string& vif_name,
					    uint32_t	  ip_protocol,
					    const IPv6&	  group_address)
{
    string error_msg;

    //
    // Search if we have already the filter
    //
    FilterBag6::iterator fi;
    FilterBag6::iterator fi_end = _filters.upper_bound(xrl_target_name);
    for (fi = _filters.lower_bound(xrl_target_name); fi != fi_end; ++fi) {
	XrlVifInputFilter6* filter;
	filter = dynamic_cast<XrlVifInputFilter6*>(fi->second);
	if (filter == NULL)
	    continue; // Not a vif filter

	if ((filter->ip_protocol() == ip_protocol) &&
	    (filter->if_name() == if_name) &&
	    (filter->vif_name() == vif_name)) {
	    // Filter found
	    if (filter->leave_multicast_group(group_address, error_msg)
		!= XORP_OK) {
		return XrlCmdError::COMMAND_FAILED(error_msg);
	    }
	    return XrlCmdError::OKAY();
	}
    }

    error_msg = c_format("Cannot leave group %s on interface %s vif %s "
			 "protocol %u target %s: the target has not "
			 "registered as a receiver",
			 group_address.str().c_str(),
			 if_name.c_str(),
			 vif_name.c_str(),
			 XORP_UINT_CAST(ip_protocol),
			 xrl_target_name.c_str());

    return XrlCmdError::COMMAND_FAILED(error_msg);
}

void
XrlRawSocket6Manager::xrl_send_recv_cb(const XrlError& e,
				       string xrl_target_name)
{
    if (e == XrlError::OKAY())
	return;

    debug_msg("xrl_send_recv_cb: error %s\n", e.str().c_str());

    //
    // Sending Xrl generated an error.
    //
    // Remove all filters associated with Xrl Target that are tied to a raw
    // socket and then erase filters.
    //
    erase_filters(_filters.lower_bound(xrl_target_name),
		  _filters.upper_bound(xrl_target_name));
}


syntax highlighted by Code2HTML, v. 0.9.1