/*
 * Various comparison functions
 */
#include "config.h"

#include <errno.h>
#include <stdio.h>		/* For NULL */

#include "../include/cidr.h"

/* Is one block entirely contained in another? */
int cidr_contains(const CIDR * big, const CIDR * little)
{
	int i, oct, bit;
	int pflen;

	/* First off, they better be the same type */
	if (big->proto != little->proto) {
		errno = EPROTO;
		return (-1);
	}

	/* We better understand the protocol, too */
	if (big->proto != CIDR_IPV4 && big->proto != CIDR_IPV6) {
		errno = EINVAL;
		return (-1);
	}

	/*
	 * little better be SMALL enough to fit in big.  Note: The prefix
	 * lengths CAN be the same, and little could still 'fit' in big if
	 * the network bits are all the same.  No need to special-case it, as
	 * the normal tests below will DTRT.  Save big's pflen for the test
	 * loop.
	 */
	if (cidr_get_pflen(little) < (pflen = cidr_get_pflen(big))) {
		errno = 0;
		return (-1);
	}

	/*
	 * Now let's compare.  Note that for IPv4 addresses, the first 12
	 * octets are irrelevant.  We take care throughout to keep them
	 * zero'd out, so we don't _need_ to explicitly ignore them.  But,
	 * it's wasteful; it quadrules the amount of work needed to be done
	 * to compare to v4 blocks, and this function may be useful in fairly
	 * performance-sensitive parts of the application.  Sure, an extra 12
	 * uint8_t compares better not be the make-or-break perforamnce point
	 * for anything real, but why make it harder unnecessarily?
	 */
	if (big->proto == CIDR_IPV4) {
		i = 96;
		pflen += 96;
	} else if (big->proto == CIDR_IPV6)
		i = 0;
	else {
		/* Shouldn't happen */
		errno = ENOENT;	/* This is a really bad choice of errno */
		return (-1);
	}

	/* Start comparing */
	for ( /* i */ ; i < pflen; i++) {
		/* For convenience, set temp. vars to the octet/bit */
		oct = i / 8;
		bit = 7 - (i % 8);

		if ((big->addr[oct] & (1 << bit)) !=
		    (little->addr[oct] & (1 << bit))) {
			errno = 0;
			return (-1);
		}
	}

	/* If we get here, all their network bits are the same */
	return (0);
}

/* Are two CIDR's the same? */
int cidr_equals(const CIDR * one, const CIDR * two)
{
	int i;

	/* Check protocols */
	if (one->proto != two->proto)
		return (-1);

	/* Check addresses/masks */
	if (one->proto == CIDR_IPV4)
		i = 12;
	else
		i = 0;
	for ( /* i */ ; i <= 15; i++) {
		if (one->addr[i] != two->addr[i])
			return (-1);
		if (one->mask[i] != two->mask[i])
			return (-1);
	}

	/* If we make it here, they're the same */
	return (0);
}
