// -*- 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/libfeaclient/ifmgr_atoms.cc,v 1.16 2007/02/16 22:45:59 pavlin Exp $"

#include "ifmgr_atoms.hh"

// ----------------------------------------------------------------------------
// Find method helpers

static inline const IfMgrIfAtom*
find_interface(const IfMgrIfTree* tree, const string& ifname)
{
    const IfMgrIfTree::IfMap& ifs = tree->ifs();
    IfMgrIfTree::IfMap::const_iterator ii = ifs.find(ifname);
    if (ifs.end() == ii)
	return 0;
    return &ii->second;
}

static inline IfMgrIfAtom*
find_interface(IfMgrIfTree* tree, const string& ifname)
{
    IfMgrIfTree::IfMap& ifs = tree->ifs();
    IfMgrIfTree::IfMap::iterator ii = ifs.find(ifname);
    if (ifs.end() == ii)
	return 0;
    return &ii->second;
}


static inline const IfMgrVifAtom*
find_virtual_interface(const IfMgrIfAtom* ifa, const string& vifname)
{
    const IfMgrIfAtom::VifMap& vifs = ifa->vifs();
    IfMgrIfAtom::VifMap::const_iterator vi = vifs.find(vifname);
    if (vifs.end() == vi)
	return 0;
    return &vi->second;
}

static inline IfMgrVifAtom*
find_virtual_interface(IfMgrIfAtom* ifa, const string& vifname)
{
    IfMgrIfAtom::VifMap& vifs = ifa->vifs();
    IfMgrIfAtom::VifMap::iterator vi = vifs.find(vifname);
    if (vifs.end() == vi)
	return 0;
    return &vi->second;
}


static inline const IfMgrIPv4Atom*
find_virtual_interface_addr(const IfMgrVifAtom* vifa, const IPv4& addr)
{
    const IfMgrVifAtom::V4Map& addrs = vifa->ipv4addrs();
    IfMgrVifAtom::V4Map::const_iterator ai = addrs.find(addr);
    if (addrs.end() == ai)
	return 0;
    return &ai->second;
}

static inline IfMgrIPv4Atom*
find_virtual_interface_addr(IfMgrVifAtom* vifa, const IPv4& addr)
{
    IfMgrVifAtom::V4Map& addrs = vifa->ipv4addrs();
    IfMgrVifAtom::V4Map::iterator ai = addrs.find(addr);
    if (addrs.end() == ai)
	return 0;
    return &ai->second;
}

static inline const IfMgrIPv6Atom*
find_virtual_interface_addr(const IfMgrVifAtom* vifa, const IPv6& addr)
{
    const IfMgrVifAtom::V6Map& addrs = vifa->ipv6addrs();
    IfMgrVifAtom::V6Map::const_iterator ai = addrs.find(addr);
    if (addrs.end() == ai)
	return 0;
    return &ai->second;
}

static inline IfMgrIPv6Atom*
find_virtual_interface_addr(IfMgrVifAtom* vifa, const IPv6& addr)
{
    IfMgrVifAtom::V6Map& addrs = vifa->ipv6addrs();
    IfMgrVifAtom::V6Map::iterator ai = addrs.find(addr);
    if (addrs.end() == ai)
	return 0;
    return &ai->second;
}

// ----------------------------------------------------------------------------
// IfMgrIfTree Methods

const IfMgrIfAtom*
IfMgrIfTree::find_if(const string& ifname) const
{
    return find_interface(this, ifname);
}

IfMgrIfAtom*
IfMgrIfTree::find_if(const string& ifname)
{
    return find_interface(this, ifname);
}

const IfMgrVifAtom*
IfMgrIfTree::find_vif(const string& ifname, const string& vifname) const
{
    const IfMgrIfAtom* ifa = find_interface(this, ifname);
    if (ifa == 0)
	return 0;
    return find_virtual_interface(ifa, vifname);
}

IfMgrVifAtom*
IfMgrIfTree::find_vif(const string& ifname, const string& vifname)
{
    IfMgrIfAtom* ifa = find_interface(this, ifname);
    if (ifa == 0)
	return 0;
    return find_virtual_interface(ifa, vifname);
}

const IfMgrIPv4Atom*
IfMgrIfTree::find_addr(const string& ifname,
		       const string& vifname,
		       const IPv4&   addr) const
{
    const IfMgrIfAtom* ifa = find_interface(this, ifname);
    if (ifa == 0)
	return 0;
    const IfMgrVifAtom* vifa = find_virtual_interface(ifa, vifname);
    if (vifa == 0)
	return 0;
    return find_virtual_interface_addr(vifa, addr);
}

IfMgrIPv4Atom*
IfMgrIfTree::find_addr(const string& ifname,
		       const string& vifname,
		       const IPv4&   addr)
{
    IfMgrIfAtom* ifa = find_interface(this, ifname);
    if (ifa == 0)
	return 0;
    IfMgrVifAtom* vifa = find_virtual_interface(ifa, vifname);
    if (vifa == 0)
	return 0;
    return find_virtual_interface_addr(vifa, addr);
}

const IfMgrIPv6Atom*
IfMgrIfTree::find_addr(const string& ifname,
		       const string& vifname,
		       const IPv6&   addr) const
{
    const IfMgrIfAtom* ifa = find_interface(this, ifname);
    if (ifa == 0)
	return 0;
    const IfMgrVifAtom* vifa = find_virtual_interface(ifa, vifname);
    if (vifa == 0)
	return 0;
    return find_virtual_interface_addr(vifa, addr);
}

IfMgrIPv6Atom*
IfMgrIfTree::find_addr(const string& ifname,
		       const string& vifname,
		       const IPv6&   addr)
{
    IfMgrIfAtom* ifa = find_interface(this, ifname);
    if (ifa == 0)
	return 0;
    IfMgrVifAtom* vifa = find_virtual_interface(ifa, vifname);
    if (vifa == 0)
	return 0;
    return find_virtual_interface_addr(vifa, addr);
}

bool
IfMgrIfTree::operator==(const IfMgrIfTree& o) const
{
    return o.ifs() == ifs();
}

bool
IfMgrIfTree::is_my_addr(const IPv4& addr, string& ifname,
			string& vifname) const
{
    IfMgrIfTree::IfMap::const_iterator if_iter;

    for (if_iter = ifs().begin(); if_iter != ifs().end(); ++if_iter) {
	const IfMgrIfAtom& iface = if_iter->second;

	// Test if interface is enabled and the link state is up
	if ((! iface.enabled()) || iface.no_carrier())
	    continue;

	IfMgrIfAtom::VifMap::const_iterator vif_iter;
	for (vif_iter = iface.vifs().begin();
	     vif_iter != iface.vifs().end();
	     ++vif_iter) {
	    const IfMgrVifAtom& vif = vif_iter->second;

	    // Test if vif is enabled
	    if (! vif.enabled())
		continue;

	    // Test if there is matching IPv4 address
	    IfMgrVifAtom::V4Map::const_iterator a4_iter;

	    for (a4_iter = vif.ipv4addrs().begin();
		 a4_iter != vif.ipv4addrs().end();
		 ++a4_iter) {
		const IfMgrIPv4Atom& a4 = a4_iter->second;

		if (! a4.enabled())
		    continue;

		// Test if my own address
		if (a4.addr() == addr) {
		    ifname = iface.name();
		    vifname = vif.name();
		    return (true);
		}
	    }
	}
    }

    ifname = "";
    vifname = "";
    return (false);
}

bool
IfMgrIfTree::is_my_addr(const IPv6& addr, string& ifname,
			string& vifname) const
{
    IfMgrIfTree::IfMap::const_iterator if_iter;

    for (if_iter = ifs().begin(); if_iter != ifs().end(); ++if_iter) {
	const IfMgrIfAtom& iface = if_iter->second;

	// Test if interface is enabled and the link state is up
	if ((! iface.enabled()) || iface.no_carrier())
	    continue;

	IfMgrIfAtom::VifMap::const_iterator vif_iter;
	for (vif_iter = iface.vifs().begin();
	     vif_iter != iface.vifs().end();
	     ++vif_iter) {
	    const IfMgrVifAtom& vif = vif_iter->second;

	    // Test if vif is enabled
	    if (! vif.enabled())
		continue;

	    // Test if there is matching IPv6 address
	    IfMgrVifAtom::V6Map::const_iterator a6_iter;

	    for (a6_iter = vif.ipv6addrs().begin();
		 a6_iter != vif.ipv6addrs().end();
		 ++a6_iter) {
		const IfMgrIPv6Atom& a6 = a6_iter->second;

		if (! a6.enabled())
		    continue;

		// Test if my own address
		if (a6.addr() == addr) {
		    ifname = iface.name();
		    vifname = vif.name();
		    return (true);
		}
	    }
	}
    }

    ifname = "";
    vifname = "";
    return (false);
}

bool
IfMgrIfTree::is_my_addr(const IPvX& addr, string& ifname,
			string& vifname) const
{
    if (addr.is_ipv4()) {
	IPv4 addr4 = addr.get_ipv4();
	return (is_my_addr(addr4, ifname, vifname));
    }

    if (addr.is_ipv6()) {
	IPv6 addr6 = addr.get_ipv6();
	return (is_my_addr(addr6, ifname, vifname));
    }

    return (false);
}

bool
IfMgrIfTree::is_directly_connected(const IPv4& addr, string& ifname,
				   string& vifname) const
{
    IfMgrIfTree::IfMap::const_iterator if_iter;

    for (if_iter = ifs().begin(); if_iter != ifs().end(); ++if_iter) {
	const IfMgrIfAtom& iface = if_iter->second;

	// Test if interface is enabled and the link state is up
	if ((! iface.enabled()) || iface.no_carrier())
	    continue;

	IfMgrIfAtom::VifMap::const_iterator vif_iter;
	for (vif_iter = iface.vifs().begin();
	     vif_iter != iface.vifs().end();
	     ++vif_iter) {
	    const IfMgrVifAtom& vif = vif_iter->second;

	    // Test if vif is enabled
	    if (! vif.enabled())
		continue;

	    // Test if there is matching IPv4 address
	    IfMgrVifAtom::V4Map::const_iterator a4_iter;

	    for (a4_iter = vif.ipv4addrs().begin();
		 a4_iter != vif.ipv4addrs().end();
		 ++a4_iter) {
		const IfMgrIPv4Atom& a4 = a4_iter->second;

		if (! a4.enabled())
		    continue;

		// Test if my own address
		if (a4.addr() == addr) {
		    ifname = iface.name();
		    vifname = vif.name();
		    return (true);
		}

		// Test if p2p address
		if (a4.has_endpoint()) {
		    if (a4.endpoint_addr() == addr) {
			ifname = iface.name();
			vifname = vif.name();
			return (true);
		    }
		}

		// Test if same subnet
		if (IPv4Net(addr, a4.prefix_len())
		    == IPv4Net(a4.addr(), a4.prefix_len())) {
		    ifname = iface.name();
		    vifname = vif.name();
		    return (true);
		}
	    }
	}
    }

    ifname = "";
    vifname = "";
    return (false);
}

bool
IfMgrIfTree::is_directly_connected(const IPv6& addr, string& ifname,
				   string& vifname) const
{
    IfMgrIfTree::IfMap::const_iterator if_iter;

    for (if_iter = ifs().begin(); if_iter != ifs().end(); ++if_iter) {
	const IfMgrIfAtom& iface = if_iter->second;

	// Test if interface is enabled and the link state is up
	if ((! iface.enabled()) || iface.no_carrier())
	    continue;

	IfMgrIfAtom::VifMap::const_iterator vif_iter;
	for (vif_iter = iface.vifs().begin();
	     vif_iter != iface.vifs().end();
	     ++vif_iter) {
	    const IfMgrVifAtom& vif = vif_iter->second;

	    // Test if vif is enabled
	    if (! vif.enabled())
		continue;

	    // Test if there is matching IPv6 address
	    IfMgrVifAtom::V6Map::const_iterator a6_iter;

	    for (a6_iter = vif.ipv6addrs().begin();
		 a6_iter != vif.ipv6addrs().end();
		 ++a6_iter) {
		const IfMgrIPv6Atom& a6 = a6_iter->second;

		if (! a6.enabled())
		    continue;

		// Test if my own address
		if (a6.addr() == addr) {
		    ifname = iface.name();
		    vifname = vif.name();
		    return (true);
		}

		// Test if p2p address
		if (a6.has_endpoint()) {
		    if (a6.endpoint_addr() == addr) {
			ifname = iface.name();
			vifname = vif.name();
			return (true);
		    }
		}

		// Test if same subnet
		if (IPv6Net(addr, a6.prefix_len())
		    == IPv6Net(a6.addr(), a6.prefix_len())) {
		    ifname = iface.name();
		    vifname = vif.name();
		    return (true);
		}
	    }
	}
    }

    ifname = "";
    vifname = "";
    return (false);
}

bool
IfMgrIfTree::is_directly_connected(const IPvX& addr, string& ifname,
				   string& vifname) const
{
    if (addr.is_ipv4()) {
	IPv4 addr4 = addr.get_ipv4();
	return (is_directly_connected(addr4, ifname, vifname));
    }

    if (addr.is_ipv6()) {
	IPv6 addr6 = addr.get_ipv6();
	return (is_directly_connected(addr6, ifname, vifname));
    }

    return (false);
}


// ----------------------------------------------------------------------------
// IfMgrIfAtom methods

bool
IfMgrIfAtom::operator==(const IfMgrIfAtom& o) const
{
    return (
	    name()			== o.name()			&&
	    enabled()			== o.enabled()			&&
	    mtu_bytes()			== o.mtu_bytes()		&&
	    mac()			== o.mac()			&&
	    pif_index()			== o.pif_index()		&&
	    no_carrier()		== o.no_carrier()		&&
	    vifs()			== o.vifs()
	    );
}

const IfMgrVifAtom*
IfMgrIfAtom::find_vif(const string& vifname) const
{
    return find_virtual_interface(this, vifname);
}

IfMgrVifAtom*
IfMgrIfAtom::find_vif(const string& vifname)
{
    return find_virtual_interface(this, vifname);
}


// ----------------------------------------------------------------------------
// IfMgrVifAtom methods

bool
IfMgrVifAtom::operator==(const IfMgrVifAtom& o) const
{
    return (
	    name()			== o.name()			&&
	    enabled()			== o.enabled()			&&
	    multicast_capable()		== o.multicast_capable()	&&
	    broadcast_capable()	  	== o.broadcast_capable()	&&
	    p2p_capable()		== o.p2p_capable()		&&
	    loopback()			== o.loopback()			&&
	    pif_index()			== o.pif_index()		&&
	    ipv4addrs()			== o.ipv4addrs()		&&
	    ipv6addrs()			== o.ipv6addrs()
	    );
}

const IfMgrIPv4Atom*
IfMgrVifAtom::find_addr(const IPv4& addr) const
{
    return find_virtual_interface_addr(this, addr);
}

IfMgrIPv4Atom*
IfMgrVifAtom::find_addr(const IPv4& addr)
{
    return find_virtual_interface_addr(this, addr);
}

const IfMgrIPv6Atom*
IfMgrVifAtom::find_addr(const IPv6& addr) const
{
    return find_virtual_interface_addr(this, addr);
}

IfMgrIPv6Atom*
IfMgrVifAtom::find_addr(const IPv6& addr)
{
    return find_virtual_interface_addr(this, addr);
}


// ----------------------------------------------------------------------------
// IfMgrIPv4Atom methods

bool
IfMgrIPv4Atom::operator==(const IfMgrIPv4Atom& o) const
{
    return (
	    addr()			== o.addr()			&&
	    prefix_len()		== o.prefix_len()		&&
	    enabled()			== o.enabled()			&&
	    multicast_capable()		== o.multicast_capable()	&&
	    loopback()			== o.loopback()			&&
	    has_broadcast()		== o.has_broadcast()		&&
	    broadcast_addr()		== o.broadcast_addr()		&&
	    has_endpoint()		== o.has_endpoint()		&&
	    endpoint_addr()		== o.endpoint_addr()
	    );
}


// ----------------------------------------------------------------------------
// IfMgrIfIPv6Atom methods

bool
IfMgrIPv6Atom::operator==(const IfMgrIPv6Atom& o) const
{
    return (
	    addr()			== o.addr()			&&
	    prefix_len()		== o.prefix_len()		&&
	    enabled()			== o.enabled()			&&
	    multicast_capable()		== o.multicast_capable()	&&
	    loopback()			== o.loopback()			&&
	    has_endpoint()		== o.has_endpoint()		&&
	    endpoint_addr()		== o.endpoint_addr()
	    );
}


syntax highlighted by Code2HTML, v. 0.9.1