// -*- 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/libxorp/ipvx.cc,v 1.28 2007/02/16 22:46:20 pavlin Exp $"

#include "xorp.h"
#include "ipvx.hh"

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif

//
// Static class members
//

// Construct an IN_ADDR_ANY address of @family.
IPvX::IPvX(int family) throw (InvalidFamily)
{
    if ((family != AF_INET) && (family != AF_INET6))
	xorp_throw(InvalidFamily, family);

    _af = family;
    memset(_addr, 0, sizeof(_addr));
}

// Construct an IPvX address by copying the raw address from @from_uint8
// memory.
IPvX::IPvX(int family, const uint8_t *from_uint8) throw (InvalidFamily)
{
    assert(from_uint8 != NULL);

    _af = family;
    memset(_addr, 0, sizeof(_addr));
    memcpy(_addr, from_uint8, addr_bytelen());
}

IPvX::IPvX(const IPv4& ipv4)
{
    static_assert(sizeof(_addr) >= sizeof(IPv4));
    static_assert(sizeof(IPv4) == 4);

    _af = AF_INET;
    memset(_addr, 0, sizeof(_addr));
    memcpy(_addr, &ipv4, 4);
}

IPvX::IPvX(const IPv6& ipv6)
{
    static_assert(sizeof(_addr) >= sizeof(IPv6));
    static_assert(sizeof(IPv6) == 16);

    _af = AF_INET6;
    memcpy(_addr, &ipv6, 16);
}

IPvX::IPvX(const in_addr& from_in_addr)
{
    copy_in(AF_INET, reinterpret_cast<const uint8_t *>(&from_in_addr));
}

IPvX::IPvX(const in6_addr& from_in6_addr)
{
    copy_in(AF_INET6, reinterpret_cast<const uint8_t *>(&from_in6_addr));
}

IPvX::IPvX(const sockaddr& from_sockaddr) throw (InvalidFamily)
{
    copy_in(from_sockaddr);
}

IPvX::IPvX(const sockaddr_in& from_sockaddr_in) throw (InvalidFamily)
{
    copy_in(from_sockaddr_in);
}

IPvX::IPvX(const sockaddr_in6& from_sockaddr_in6) throw (InvalidFamily)
{
    copy_in(from_sockaddr_in6);
}

IPvX::IPvX(char const *from_cstring) throw (InvalidString)
{
    if (from_cstring == NULL) {
	xorp_throw(InvalidString, "Null value");
    } else if (inet_pton(AF_INET, from_cstring, _addr) > 0) {
	_af = AF_INET;
    } else if (inet_pton(AF_INET6, from_cstring, _addr) > 0) {
	_af = AF_INET6;
    } else {
	xorp_throw(InvalidString,
		   c_format("Bad IPvX \"%s\"", from_cstring));
    }
}


// TODO: if this method is used very often, then reimplement it
// by directly ~ of r._addr (see IPv6 implementation for details).
IPvX
IPvX::operator~() const
{
    if (is_ipv4()) {
	return (~get_ipv4());	// XXX: implicitly create IPvX return object
    } else {
	return (~get_ipv6());	// XXX: implicitly create IPvX return object
    }
}

IPvX
IPvX::operator|(const IPvX& other) const throw (InvalidCast)
{
    if (is_ipv4()) {
	return get_ipv4() | other.get_ipv4();
    } else {
	return get_ipv6() | other.get_ipv6();
    }
}

IPvX
IPvX::operator&(const IPvX& other) const throw (InvalidCast)
{
    if (is_ipv4()) {
	return get_ipv4() & other.get_ipv4();
    } else {
	return get_ipv6() & other.get_ipv6();
    }
}

IPvX
IPvX::operator^(const IPvX& other) const throw (InvalidCast)
{
    if (is_ipv4()) {
	return get_ipv4() ^ other.get_ipv4();
    } else {
	return get_ipv6() ^ other.get_ipv6();
    }
}

IPvX
IPvX::operator<<(uint32_t left_shift) const
{
    if (is_ipv4()) {
	return get_ipv4() << left_shift;
    } else {
	return get_ipv6() << left_shift;
    }
}

IPvX
IPvX::operator>>(uint32_t right_shift) const
{
    if (is_ipv4()) {
	return get_ipv4() >> right_shift;
    } else {
	return get_ipv6() >> right_shift;
    }
}

bool
IPvX::operator<(const IPvX& other) const
{
    static_assert(sizeof(_addr) == 16);
    int i;

    for (i = 0; i < 3; i++) {	// Loop ends intentionally at 3 not 4.
	if (_addr[i] != other._addr[i]) {
	    break;
	}
    }
    return ntohl(_addr[i]) < ntohl(other._addr[i]);
}

bool
IPvX::operator==(const IPvX& other) const
{
    if (is_ipv4() && other.is_ipv4())
	return (get_ipv4() == other.get_ipv4());
    else if (is_ipv6() && other.is_ipv6())
	return (get_ipv6() == other.get_ipv6());
    else
	return false;
}

bool
IPvX::operator!=(const IPvX& other) const
{
    if (is_ipv4() && other.is_ipv4())
	return (get_ipv4() != other.get_ipv4());
    else if (is_ipv6() && other.is_ipv6())
	return (get_ipv6() != other.get_ipv6());
    else
	return true;
}

IPvX&
IPvX::operator--()  {
    if (is_ipv4()) {
        *this = --get_ipv4();
    } else {
        *this = --get_ipv6();
    }
    return (*this);
}

IPvX&
IPvX::operator++() {
    if (is_ipv4()) {
        *this = ++get_ipv4();
    } else {
        *this = ++get_ipv6();
    }
    return (*this);
}

IPvX
IPvX::make_prefix(int family, uint32_t mask_len)
    throw (InvalidFamily, InvalidNetmaskLength)
{
    if (family == AF_INET)
	return IPv4::make_prefix(mask_len);
    else if (family == AF_INET6)
	return IPv6::make_prefix(mask_len);
    else
	xorp_throw(InvalidFamily, family);
    return IPvX(0);	/* Not Reached */
}

IPvX
IPvX::mask_by_prefix_len(uint32_t prefix_len) const throw (InvalidNetmaskLength)
{
    if (_af == AF_INET)
	return get_ipv4().mask_by_prefix_len(prefix_len);
    else
	return get_ipv6().mask_by_prefix_len(prefix_len);
}

string
IPvX::str() const
{
    if (_af == AF_INET)
	return get_ipv4().str();
    else
	return get_ipv6().str();
}

uint32_t
IPvX::mask_len() const
{
    if (is_ipv4())
	return get_ipv4().mask_len();
    else
	return get_ipv6().mask_len();
}

/**
 * Copy the raw address to memory pointed by @to.
 * @return the number of copied octets.
 */
size_t
IPvX::copy_out(uint8_t *to_uint8) const
{
    memcpy(to_uint8, _addr, addr_bytelen());
    return (addr_bytelen());
}

/**
 * Copy the raw address to @in_addr.
 * @return the number of copied octets.
 */
size_t
IPvX::copy_out(struct in_addr& to_in_addr) const throw (InvalidFamily)
{
    if (_af != AF_INET)
	xorp_throw(InvalidFamily, _af);
    return (copy_out((uint8_t *)&to_in_addr));
}

/**
 * Copy the raw address to @in6_addr.
 * @return the number of copied octets.
 */
size_t
IPvX::copy_out(struct in6_addr& to_in6_addr) const throw (InvalidFamily)
{
    if (_af != AF_INET6)
	xorp_throw(InvalidFamily, _af);
    return (copy_out((uint8_t *)&to_in6_addr));
}

/**
 * Copy the raw address to @to_sockaddr, and assign appropriately
 * the rest of the fields in @to_sockaddr.
 * @return the number of copied octets.
 */
size_t
IPvX::copy_out(struct sockaddr& to_sockaddr) const throw (InvalidFamily)
{
    return (copy_out(reinterpret_cast<sockaddr_in&>(to_sockaddr)));
}

/**
 * Copy the raw address to @to_sockaddr_in, and assign appropriately
 * the rest of the fields in @to_sockaddr_in.
 * @return the number of copied octets.
 */
size_t
IPvX::copy_out(struct sockaddr_in& to_sockaddr_in) const throw (InvalidFamily)
{
    switch (_af) {
    case AF_INET:
	memset(&to_sockaddr_in, 0, sizeof(to_sockaddr_in));
#ifdef HAVE_SIN_LEN
	to_sockaddr_in.sin_len = sizeof(to_sockaddr_in);
#endif
	to_sockaddr_in.sin_family = _af;
	to_sockaddr_in.sin_port = 0;			// XXX: not used
	return (copy_out(to_sockaddr_in.sin_addr));

    case AF_INET6:
	return (copy_out(reinterpret_cast<sockaddr_in6&>(to_sockaddr_in)));

    default:
	xorp_throw(InvalidFamily, _af);
    }

    return((size_t)-1);
}

/**
 * Copy the raw address to @to_sockaddr_in6, and assign appropriately
 * the rest of the fields in @to_sockaddr_in6.
 * @return the number of copied octets.
 */
size_t
IPvX::copy_out(struct sockaddr_in6& to_sockaddr_in6) const
    throw (InvalidFamily)
{
    switch (_af) {
    case AF_INET:
	return (copy_out(reinterpret_cast<sockaddr_in&>(to_sockaddr_in6)));

    case AF_INET6:
	memset(&to_sockaddr_in6, 0, sizeof(to_sockaddr_in6));
#ifdef HAVE_SIN6_LEN
	to_sockaddr_in6.sin6_len = sizeof(to_sockaddr_in6);
#endif
	to_sockaddr_in6.sin6_family = _af;

#ifdef HAVE_SIN6_SCOPE_ID
#ifdef IPV6_STACK_KAME
	//
	// XXX: In case of KAME the local interface index (also the link-local
	// scope_id) is encoded in the third and fourth octet of an IPv6
	// address (for link-local unicast/multicast addresses or
	// interface-local multicast addresses only).
	//
	if (is_linklocal_unicast()
	    || is_linklocal_multicast()
	    || is_interfacelocal_multicast()) {
	    uint32_t addr0 = ntohl(_addr[0]);
	    uint16_t zoneid = (addr0 & 0xffff);		// XXX: 16 bits only
	    to_sockaddr_in6.sin6_scope_id = zoneid;
	}
#endif // IPV6_STACK_KAME
#endif // HAVE_SIN6_SCOPE_ID

	return (copy_out(to_sockaddr_in6.sin6_addr));

    default:
	xorp_throw(InvalidFamily, _af);
    }

    return((size_t)-1);
}

/**
 * Copy a raw address of family @family from the memory pointed by @from_uint8.
 * @return the number of copied octets.
 */
size_t
IPvX::copy_in(int family, const uint8_t *from_uint8) throw (InvalidFamily)
{
    _af = family;

    switch (_af) {
    case AF_INET:
	// FALLTHROUGH
    case AF_INET6:
	memcpy(_addr, from_uint8, addr_bytelen());
	return (addr_bytelen());
    default:
	xorp_throw(InvalidFamily, _af);
    }
    return ((size_t)-1);
}

/**
 * Copy a raw address of family %AF_INET from @from_in_addr.
 * @return the number of copied octets.
 */
size_t
IPvX::copy_in(const in_addr& from_in_addr)
{
    return (copy_in(AF_INET, reinterpret_cast<const uint8_t *>(&from_in_addr)));
}

/**
 * Copy a raw address of family %AF_INET6 from @from_in6_addr.
 * @return the number of copied octets.
 */
size_t
IPvX::copy_in(const in6_addr& from_in6_addr)
{
    return (copy_in(AF_INET6, reinterpret_cast<const uint8_t *>(&from_in6_addr)));
}

/**
 * Copy a raw address from @from_sockaddr.
 * @return the number of copied octets.
 */
size_t
IPvX::copy_in(const sockaddr& from_sockaddr) throw (InvalidFamily)
{
    return (copy_in(reinterpret_cast<const sockaddr_in&>(from_sockaddr)));
}

/**
 * Copy a raw address from @from_sockaddr_in.
 * @return the number of copied octets.
 */
size_t
IPvX::copy_in(const sockaddr_in& from_sockaddr_in) throw (InvalidFamily)
{
    _af = from_sockaddr_in.sin_family;

    switch (_af) {
    case AF_INET:
	return (copy_in(from_sockaddr_in.sin_addr));
    case AF_INET6:
	return (copy_in(reinterpret_cast<const sockaddr_in6&>(from_sockaddr_in)));
    default:
	xorp_throw(InvalidFamily, _af);
    }
    return((size_t)-1);
}

/**
 * Copy a raw address from @from_sockaddr_in6.
 * @return the number of copied octets.
 */
size_t
IPvX::copy_in(const sockaddr_in6& from_sockaddr_in6) throw (InvalidFamily)
{
    _af = from_sockaddr_in6.sin6_family;

    switch (_af) {
    case AF_INET:
	return (copy_in(reinterpret_cast<const sockaddr_in&>(from_sockaddr_in6)));
    case AF_INET6:
	return (copy_in(from_sockaddr_in6.sin6_addr));
    default:
	xorp_throw(InvalidFamily, _af);
    }

    return((size_t)-1);
}

bool
IPvX::is_zero() const
{
    if (_af == AF_INET)
	return get_ipv4().is_zero();
    return get_ipv6().is_zero();
}

bool
IPvX::is_unicast() const
{
    if (_af == AF_INET)
	return get_ipv4().is_unicast();
    return get_ipv6().is_unicast();
}

bool
IPvX::is_multicast() const
{
    if (_af == AF_INET)
	return get_ipv4().is_multicast();
    return get_ipv6().is_multicast();
}

bool
IPvX::is_class_a() const
{
    if (_af == AF_INET)
	return get_ipv4().is_class_a();
    return (false);		// XXX: this method applies only for IPv4
}

bool
IPvX::is_class_b() const
{
    if (_af == AF_INET)
	return get_ipv4().is_class_b();
    return (false);		// XXX: this method applies only for IPv4
}

bool
IPvX::is_class_c() const
{
    if (_af == AF_INET)
	return get_ipv4().is_class_c();
    return (false);		// XXX: this method applies only for IPv4
}

bool
IPvX::is_experimental() const
{
    if (_af == AF_INET)
	return get_ipv4().is_experimental();
    return (false);		// XXX: this method applies only for IPv4
}

bool
IPvX::is_linklocal_unicast() const
{
    if (_af == AF_INET)
	return get_ipv4().is_linklocal_unicast();
    return get_ipv6().is_linklocal_unicast();
}

bool
IPvX::is_interfacelocal_multicast() const
{
    if (_af == AF_INET)
	return get_ipv4().is_interfacelocal_multicast();
    return get_ipv6().is_interfacelocal_multicast();
}

bool
IPvX::is_linklocal_multicast() const
{
    if (_af == AF_INET)
	return get_ipv4().is_linklocal_multicast();
    return get_ipv6().is_linklocal_multicast();
}

bool
IPvX::is_loopback() const
{
    if (_af == AF_INET)
	return get_ipv4().is_loopback();
    return get_ipv6().is_loopback();
}

/**
 * @return IP protocol version.
 */
uint32_t
IPvX::ip_version() const throw (InvalidFamily)
{
    if (_af == AF_INET)
	return (IPv4::ip_version());
    if (_af == AF_INET6)
	return (IPv6::ip_version());
    xorp_throw(InvalidFamily, _af);

    return ((uint32_t)-1);
}

/**
 * @return IP protocol version string.
 */
const string&
IPvX::ip_version_str() const throw (InvalidFamily)
{
    if (_af == AF_INET)
	return (IPv4::ip_version_str());
    if (_af != AF_INET6)
	xorp_throw(InvalidFamily, _af);
    return (IPv6::ip_version_str());
}

size_t
IPvX::addr_bytelen(int family) throw (InvalidFamily)
{
    if (family == AF_INET)
	return (IPv4::addr_bytelen());
    if (family == AF_INET6)
	return (IPv6::addr_bytelen());
    xorp_throw(InvalidFamily, family);

    return ((size_t)-1);
}

uint32_t
IPvX::ip_multicast_base_address_mask_len(int family) throw (InvalidFamily)
{
    if (family == AF_INET)
	return (IPv4::ip_multicast_base_address_mask_len());
    if (family == AF_INET6)
	return (IPv6::ip_multicast_base_address_mask_len());

    xorp_throw(InvalidFamily, family);
    return ((uint32_t)-1);
}

uint32_t
IPvX::ip_class_a_base_address_mask_len(int family) throw (InvalidFamily)
{
    if (family == AF_INET)
	return (IPv4::ip_class_a_base_address_mask_len());
    // XXX: this method applies only for IPv4

    xorp_throw(InvalidFamily, family);
    return ((uint32_t)-1);
}

uint32_t
IPvX::ip_class_b_base_address_mask_len(int family) throw (InvalidFamily)
{
    if (family == AF_INET)
	return (IPv4::ip_class_b_base_address_mask_len());
    // XXX: this method applies only for IPv4

    xorp_throw(InvalidFamily, family);
    return ((uint32_t)-1);
}

uint32_t
IPvX::ip_class_c_base_address_mask_len(int family) throw (InvalidFamily)
{
    if (family == AF_INET)
	return (IPv4::ip_class_c_base_address_mask_len());
    // XXX: this method applies only for IPv4

    xorp_throw(InvalidFamily, family);
    return ((uint32_t)-1);
}

uint32_t
IPvX::ip_experimental_base_address_mask_len(int family) throw (InvalidFamily)
{
    if (family == AF_INET)
	return (IPv4::ip_experimental_base_address_mask_len());
    // XXX: this method applies only for IPv4

    xorp_throw(InvalidFamily, family);
    return ((uint32_t)-1);
}

//
// IPvX "Constants"
//
#define IPVX_CONSTANT_ACCESSOR(name)					      \
const IPvX& IPvX::name(int family) throw (InvalidFamily)		      \
{									      \
    static const IPvX c4_##name (IPv4::name());	/* IPv4 constant */	      \
    static const IPvX c6_##name (IPv6::name());	/* IPv6 constant */	      \
    if (family == AF_INET)						      \
	return c4_##name;						      \
    if (family == AF_INET6)						      \
	return c6_##name;						      \
    xorp_throw(InvalidFamily, family);					      \
}

// IPvX "Constant" that applies only for IPv4
#define IPVX_CONSTANT_ACCESSOR_IPV4(name)				      \
const IPvX& IPvX::name(int family) throw (InvalidFamily)		      \
{									      \
    static const IPvX c4_##name (IPv4::name());	/* IPv4 constant */	      \
    if (family == AF_INET)						      \
	return c4_##name;						      \
    xorp_throw(InvalidFamily, family);					      \
}

// IPvX "Constant" that applies only for IPv6
#define IPVX_CONSTANT_ACCESSOR_IPV6(name)				      \
const IPvX& IPvX::name(int family) throw (InvalidFamily)		      \
{									      \
    static const IPvX c6_##name (IPv6::name());	/* IPv6 constant */	      \
    if (family == AF_INET6)						      \
	return c6_##name;						      \
    xorp_throw(InvalidFamily, family);					      \
}

IPVX_CONSTANT_ACCESSOR(ZERO);
IPVX_CONSTANT_ACCESSOR(ANY);
IPVX_CONSTANT_ACCESSOR(ALL_ONES);
IPVX_CONSTANT_ACCESSOR(LOOPBACK);
IPVX_CONSTANT_ACCESSOR(MULTICAST_BASE);
IPVX_CONSTANT_ACCESSOR(MULTICAST_ALL_SYSTEMS);
IPVX_CONSTANT_ACCESSOR(MULTICAST_ALL_ROUTERS);
IPVX_CONSTANT_ACCESSOR(DVMRP_ROUTERS);
IPVX_CONSTANT_ACCESSOR(OSPFIGP_ROUTERS);
IPVX_CONSTANT_ACCESSOR(OSPFIGP_DESIGNATED_ROUTERS);
IPVX_CONSTANT_ACCESSOR(RIP2_ROUTERS);
IPVX_CONSTANT_ACCESSOR(PIM_ROUTERS);
IPVX_CONSTANT_ACCESSOR(SSM_ROUTERS);
IPVX_CONSTANT_ACCESSOR_IPV4(CLASS_A_BASE);
IPVX_CONSTANT_ACCESSOR_IPV4(CLASS_B_BASE);
IPVX_CONSTANT_ACCESSOR_IPV4(CLASS_C_BASE);
IPVX_CONSTANT_ACCESSOR_IPV4(EXPERIMENTAL_BASE);


syntax highlighted by Code2HTML, v. 0.9.1