/*
 * Copyright (c) 2019-2020 Job Snijders <job@sobornost.net>
 * Copyright (c) 2007-2019 Alexandre Snarskii <snar@snar.spb.ru>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>

#include "sx_prefix.h"
#include "sx_report.h"

int debug_aggregation = 0;
extern int debug_expander;

struct sx_prefix *
sx_prefix_alloc(struct sx_prefix *p)
{
	struct sx_prefix *sp;

	if ((sp = malloc(sizeof(struct sx_prefix))) == NULL)
		err(1, NULL);

	if (p)
		memcpy(sp, p, sizeof(struct sx_prefix));
	else
		memset(sp, 0, sizeof(struct sx_prefix));

	return sp;
}

void
sx_prefix_free(struct sx_prefix *p)
{
	if (p)
		free(p);
}

void
sx_radix_node_destroy(struct sx_radix_node *n)
{
	if (!n)
		return;

	if (n->payload)
		free(n->payload);

	if (n->prefix)
		free(n->prefix);

	free(n);
}

void
sx_prefix_adjust_masklen(struct sx_prefix *p)
{
	unsigned int	nbytes = (p->family == AF_INET ? 4 : 16);
	unsigned int	i;

	if (p->masklen == nbytes * 8)
		return; /* mask is all ones */

	for (i = nbytes -1; i > p->masklen / 8; i--) {
		p->addr.addrs[i] = 0;
	}

	for (i = 1; i <= 8 - p->masklen % 8; i++) {
		p->addr.addrs[p->masklen / 8] &= (0xff << i);
	}
}

static void
sx_prefix_mask(struct sx_prefix *p, struct sx_prefix *q)
{
	unsigned int	i;

	memset(q->addr.addrs, 0, sizeof(q->addr.addrs));

	q->family = p->family;
	q->masklen = p->masklen;

	for (i = 0; i < p->masklen / 8; i++)
		q->addr.addrs[i] = 0xff;

	for (i = 1; i <= p->masklen % 8; i++)
		q->addr.addrs[p->masklen / 8] |= (1 << (8 - i));
}

static void
sx_prefix_imask(struct sx_prefix *p, struct sx_prefix *q)
{
	unsigned int	i;

	memset(q->addr.addrs, 0xff, sizeof(q->addr.addrs));

	q->family = p->family;
	q->masklen = p->masklen;

	for (i = 0; i < p->masklen / 8; i++)
		q->addr.addrs[i] = 0;

	for (i = 1;i <= p->masklen % 8; i++)
		q->addr.addrs[p->masklen / 8] &= ~(1 <<(8 - i));
}


int
sx_prefix_parse(struct sx_prefix *p, int af, char *text)
{
	char	*c = NULL;
	int	 masklen, ret;
	char	 mtext[INET6_ADDRSTRLEN + 5];

	strlcpy(mtext, text, sizeof(mtext));

	c = strchr(mtext,'/');

	if (c) {
		char *eod;
		*c = 0;
		masklen = strtol(c + 1, &eod, 10);
		if (eod && eod[0] && !isspace(eod[0])) {
			*c = '/';
			sx_report(SX_ERROR, "Invalid masklen in prefix %s\n",
			    text);
			goto fixups;
		}
	} else {
		masklen = -1;
	}

	if (!af) {
		if (strchr(mtext, ':'))
			af = AF_INET6;
		else
			af = AF_INET;
	}

	ret = inet_pton(af, mtext, &p->addr);

	if (ret != 1) {
		int aparts[4];
		/*
		 * contrary to documentation (man inet_ntop on FreeBSD),
		 * addresses with leading zeros are not parsed correctly. Try
		 * to workaround this issue manually.
		 */
		if (af == AF_INET && sscanf(mtext, "%i.%i.%i.%i", aparts,
		    aparts + 1, aparts + 2, aparts + 3) == 4
		    && aparts[0] >= 0 && aparts[0] < 256
		    && aparts[1] >= 0 && aparts[1] < 256
		    && aparts[2] >= 0 && aparts[2] < 256
		    && aparts[3] >= 0 && aparts[3] < 256) {
			p->addr.addr.s_addr = htonl((aparts[0] << 24) +
			    (aparts[1] << 16) + (aparts[2] << 8) + aparts[3]);
		} else {
			if (c)
				*c = '/';

			sx_report(SX_ERROR,"Unable to parse prefix '%s', af=%i"
			    " (%s), ret=%i\n", mtext, af,
			    af == AF_INET ? "inet" : "inet6", ret);
			goto fixups;
		}
	}

	if (af == AF_INET) {
		if (masklen == -1)
			p->masklen = 32;
		else {
			if (masklen < 0 || masklen > 32) {
				p->masklen = 32;
			} else {
				p->masklen = masklen;
			}
		}
	} else if (af == AF_INET6) {
		if (masklen == -1)
			p->masklen = 128;
		else {
			if (masklen < 0 || masklen > 128) {
				p->masklen = 128;
			} else {
				p->masklen = masklen;
			}
		}
	} else {
		sx_report(SX_ERROR, "Invalid address family %i\n", af);
		goto fixups;
	}

	p->family = af;
	sx_prefix_adjust_masklen(p);

	if (c)
		*c = '/';

	return 1;

fixups:
	return 0;
}

static int
sx_prefix_isbitset(struct sx_prefix *p, int n)
{
	unsigned char	s;

	/* bits outside the prefix considered unset */
	if (p->family == AF_INET && (n < 0 || n > 32))
		return 0;
	else if (p->family == AF_INET6 && (n < 0 || n > 128))
		return 0;
	s = p->addr.addrs[(n - 1) / 8];

	return (s & (0x80 >> ((n - 1) % 8))) ? 1 : 0;
}

static void
sx_prefix_setbit(struct sx_prefix *p, int n)
{
	unsigned char	*s;

	if (p->family == AF_INET && (n < 0 || n > 32))
		return;
	else if (p->family == AF_INET6 && (n < 0 || n > 128))
		return;

	s = p->addr.addrs + (n - 1) / 8;

	(*s) |= 0x80 >> ((n - 1) % 8);
}


static int
sx_radix_tree_insert_specifics(struct sx_radix_tree *t, struct sx_prefix p,
    unsigned min, unsigned max)
{
	if (p.masklen >= min)
		sx_radix_tree_insert(t, &p);

	if (p.masklen + 1 > max)
		return 1;

	p.masklen += 1;
	sx_radix_tree_insert_specifics(t, p, min, max);
	sx_prefix_setbit(&p, p.masklen);
	sx_radix_tree_insert_specifics(t, p, min, max);

	return 1;
}

int
sx_prefix_range_parse(struct sx_radix_tree *tree, int af, unsigned int maxlen,
    char *text)
{
	struct sx_prefix	 p;
	unsigned long		 min, max = 0;
	char			*d = strchr(text, '^');

	if (!d || !d[1])
		return 0;

	*d = 0;

	if (!sx_prefix_parse(&p, 0, text)) {
		sx_report(SX_ERROR, "Unable to parse prefix %s^%s\n", text,
		    d + 1);
		return 0;
	}

	*d = '^';

	if (af && p.family != af) {
		sx_report(SX_ERROR, "Ignoring prefix %s, wrong af %i\n", text,
		    p.family);
		return 0;
	}

	if (maxlen && p.masklen > maxlen) {
		SX_DEBUG(debug_expander, "Ignoring prefix %s, masklen %i > max"
		    " masklen %u\n", text, p.masklen, maxlen);
		return 0;
	}

	if (d[1] == '-') {
		min = p.masklen + 1;
		max = maxlen;
	} else if (d[1] == '+') {
		min = p.masklen;
		max = maxlen;
	} else if (isdigit(d[1])) {
		char *dm = NULL;
		min = strtoul(d + 1, &dm, 10);
		if (dm && *dm == '-' && isdigit(dm[1])) {
			max = strtoul(dm + 1, NULL, 10);
		} else if (dm && *dm) {
			sx_report(SX_ERROR, "Unable to parse prefix-range "
			    "%s\n", text);
			return 0;
		}
	} else {
		sx_report(SX_ERROR, "Invalid prefix-range %s\n", text);
		return 0;
	}

	if (min < p.masklen) {
		sx_report(SX_ERROR, "Invalid prefix-range %s: min %lu < "
		    "masklen %u\n", text, min, p.masklen);
		return 0;
	}

	if (af == AF_INET && max > 32) {
		sx_report(SX_ERROR, "Invalid prefix-range %s: max %lu > "
		    "32\n", text, max);
		return 0;
	} else if (af == AF_INET6 && max > 128) {
		sx_report(SX_ERROR, "Invalid ipv6 prefix-range %s: max %lu > "
		    "128\n", text, max);
		return 0;
	}

	if (max > maxlen)
		max = maxlen;

	SX_DEBUG(debug_expander, "parsed prefix-range %s as %lu-%lu (maxlen: "
	    "%u)\n", text, min, max, maxlen);

	sx_radix_tree_insert_specifics(tree, p, min, max);

	return 1;
}

struct sx_prefix *
sx_prefix_new(int af, char *text)
{
	struct sx_prefix *p = NULL;

	if (!text)
		return NULL;

	p = sx_prefix_alloc(NULL);

	if (!p)
		return NULL;

	if (!sx_prefix_parse(p, af, text)) {
		sx_prefix_free(p);
		return NULL;
	}

	return p;
}

int
sx_prefix_fprint(FILE *f, struct sx_prefix *p)
{
	char buffer[128];

	if (!p) {
		fprintf(f?f:stdout,"(null)");
		return 0;
	}

	inet_ntop(p->family, &p->addr, buffer, sizeof(buffer));
	return fprintf( f ? f : stdout, "%s/%i", buffer, p->masklen);
}

int
sx_prefix_snprintf_sep(struct sx_prefix *p, char *rbuffer, int srb, char *sep)
{
	char buffer[128];

	if (!sep)
		sep="/";

	if (!p) {
		snprintf(rbuffer, srb, "(null)");
		return 0;
	}

	inet_ntop(p->family, &p->addr, buffer, sizeof(buffer));

	return snprintf(rbuffer, srb, "%s%s%i", buffer, sep, p->masklen);
}

int
sx_prefix_snprintf(struct sx_prefix *p, char *rbuffer, int srb)
{
	return sx_prefix_snprintf_sep(p, rbuffer, srb, "/");
}

void
sx_prefix_snprintf_fmt(struct sx_prefix *p, FILE *f,
    const char *name, const char *format,
    unsigned int aggregateLow, unsigned int aggregateHi)
{
	const char		*c = format;
	struct sx_prefix	*q = sx_prefix_alloc(NULL);
	char			 prefix[128];

	while (*c) {
		if (*c == '%') {
			switch (*(c + 1)) {
			case 'r':
			case 'n':
				if (NULL != inet_ntop(p->family, &p->addr, prefix, sizeof(prefix))) {
					fprintf(f, "%s", prefix);
				} else {
					sx_report(SX_ERROR, "inet_ntop failed\n");
					return;
				}
				break;
			case 'l':
				fprintf(f, "%i", p->masklen);
				break;
			case 'a':
				fprintf(f, "%u", aggregateLow);
				break;
			case 'A':
				fprintf(f, "%u", aggregateHi);
				break;
			case '%':
				fprintf(f, "%%");
				break;
			case 'N':
				fprintf(f, "%s", name);
				break;
			case 'm':
				sx_prefix_mask(p, q);
				if (NULL != inet_ntop(p->family, &q->addr, prefix, sizeof(prefix))) {
					fprintf(f, "%s", prefix);
				} else {
					sx_report(SX_ERROR, "inet_ntop failed\n");
					return;
				}
				break;
			case 'i':
				sx_prefix_imask(p, q);
				if (NULL != inet_ntop(p->family, &q->addr, prefix, sizeof(prefix))) {
					fprintf(f, "%s", prefix);
				} else {
					sx_report(SX_ERROR, "inet_ntop failed\n");
					return;
				}
				break;
			default :
				sx_report(SX_ERROR, "Unknown format char "
				    "'%c'\n", *(c + 1));
				return;
			}
			c += 2;
		} else if (*c == '\\') {
			switch(*(c + 1)) {
			case 'n':
				fprintf(f, "\n");
				break;
			case 't':
				fprintf(f, "\t");
				break;
			case '\\':
				fprintf(f, "\\");
				break;
			default:
				fprintf(f, "%c", *(c + 1));
				break;
			}
			c += 2;
		} else {
			fprintf(f, "%c", *c);
			c++;
		}
	}
}

int
sx_prefix_jsnprintf(struct sx_prefix *p, char *rbuffer, int srb)
{
	char buffer[128];

	if (!p) {
		snprintf(rbuffer, srb, "(null)");
		return 0;
	}
	inet_ntop(p->family, &p->addr, buffer, sizeof(buffer));

	return snprintf(rbuffer, srb, "%s\\/%i", buffer, p->masklen);
}

struct sx_radix_tree *
sx_radix_tree_new(int af)
{
	struct sx_radix_tree *rt;

	if ((rt = malloc(sizeof(struct sx_radix_tree))) == NULL)
		err(1, NULL);

	memset(rt, 0, sizeof(struct sx_radix_tree));
	rt->family = af;

	return rt;
}

int
sx_radix_tree_empty(struct sx_radix_tree *t)
{
	return t->head == NULL;
}

struct sx_radix_node *
sx_radix_node_new(struct sx_prefix *prefix)
{
	struct sx_radix_node *rn;

	if ((rn = malloc(sizeof(struct sx_radix_node))) == NULL)
		err(1, NULL);

	memset(rn, 0, sizeof(struct sx_radix_node));

	if (prefix)
		rn->prefix = sx_prefix_alloc(prefix);

	return rn;
}

static int
sx_prefix_eqbits(struct sx_prefix *a, struct sx_prefix *b)
{
	unsigned int	i;
	unsigned int	nbytes = (a->family == AF_INET ? 4 : 16);

	for (i = 0; i < nbytes; i++) {

		if (a->addr.addrs[i] == b->addr.addrs[i])
			continue;
		else {
			unsigned int j;
			for (j = 0; j < 8 && i * 8 + j <= a->masklen
			    && i * 8 + j <= b->masklen; j++) {
				if ((a->addr.addrs[i] & (0x80 >> j))
				    != (b->addr.addrs[i] & (0x80 >> j)))
					return i * 8 + j;
			}
		}
	}

	if (a->masklen < b->masklen)
		return a->masklen;

	return b->masklen;
}

struct sx_prefix *
sx_prefix_overlay(struct sx_prefix *p, int n)
{
	struct sx_prefix *sp = sx_prefix_alloc(p);
	sp->masklen = n;
	sx_prefix_adjust_masklen(sp);

	return sp;
}

void
sx_radix_tree_unlink(struct sx_radix_tree *tree, struct sx_radix_node *node)
{
next:
	if (node->r && node->l)
		node->isGlue = 1;
	else if (node->r) {
		if (node->parent) {
			if (node->parent->r == node) {
				node->parent->r = node->r;
				node->r->parent = node->parent;
			} else if (node->parent->l == node) {
				node->parent->l = node->r;
				node->r->parent = node->parent;
			} else {
				sx_report(SX_ERROR,"Unlinking node which is "
				    "not descendant of its parent\n");
			}
		} else if (tree->head == node) {
			/* only one case, really */
			tree->head = node->r;
			node->r->parent = NULL;
		} else {
			sx_report(SX_ERROR,"Unlinking node with no parent and"
			    " not root\n");
		}
		sx_radix_node_destroy(node);
		return;
	} else if (node->l) {
		if (node->parent) {
			if (node->parent->r == node) {
				node->parent->r = node->l;
				node->l->parent = node->parent;
			} else if (node->parent->l==node) {
				node->parent->l=node->l;
				node->l->parent=node->parent;
			} else {
				sx_report(SX_ERROR,"Unlinking node which is not descendant "
					"of its parent\n");
			}
		} else if (tree->head==node) {
			tree->head=node->l;
			node->l->parent=NULL;
		} else {
			sx_report(SX_ERROR,"Unlinking node with no parent and not root\n");
		}
		sx_radix_node_destroy(node);
		return;
	} else {
		/* the only case - node does not have descendants */
		if (node->parent) {
			if (node->parent->l == node)
				node->parent->l = NULL;
			else if (node->parent->r == node)
				node->parent->r=NULL;
			else {
				sx_report(SX_ERROR,"Unlinking node which is "
				    "not descendant of its parent\n");
			}

			if (node->parent->isGlue) {
				node = node->parent;
				goto next;
			}
		} else if (tree->head==node) {
			tree->head = NULL;
		} else {
			sx_report(SX_ERROR, "Unlinking node with no parent and"
			    " not root\n");
		}
		sx_radix_node_destroy(node);
		return;
	}
}
	
			
struct sx_radix_node *
sx_radix_tree_lookup(struct sx_radix_tree *tree, struct sx_prefix *prefix)
{
	unsigned int 		 eb;
	struct sx_radix_node	*candidate = NULL, *chead;

	if (!tree || !prefix)
		return NULL;

	if (tree->family!=prefix->family)
		return NULL;

	if (!tree->head)
		return NULL;

	chead = tree->head;

next:
	eb = sx_prefix_eqbits(chead->prefix, prefix);
	if (eb == chead->prefix->masklen && eb == prefix->masklen) {
		/* they are equal */
		if (chead->isGlue)
			return candidate;
		return chead;
	} else if (eb < chead->prefix->masklen) {
		return candidate;
	} else if (eb < prefix->masklen) {
		/* it equals chead->masklen */
		if (sx_prefix_isbitset(prefix, eb + 1)) {
			if (chead->r) {
				if (!chead->isGlue) {
					candidate = chead;
				}
				chead = chead->r;
				goto next;
			} else {
				if (chead->isGlue)
					return candidate;
				return chead;
			}
		} else {
			if (chead->l) {
				if (!chead->isGlue) {
					candidate = chead;
				}
				chead = chead->l;
				goto next;
			} else {
				if (chead->isGlue)
					return candidate;
				return chead;
			}
		}
	} else {
		char pbuffer[128], cbuffer[128];
		sx_prefix_snprintf(prefix, pbuffer, sizeof(pbuffer));
		sx_prefix_snprintf(chead->prefix, cbuffer, sizeof(cbuffer));
		printf("Unreachable point... eb=%i, prefix=%s, chead=%s\n",
		    eb, pbuffer, cbuffer);
		abort();
	}
}


struct sx_radix_node *
sx_radix_tree_insert(struct sx_radix_tree *tree, struct sx_prefix *prefix)
{
	unsigned int eb;
	struct sx_radix_node *chead, **candidate = NULL;

	if (!tree || !prefix)
		return NULL;

	if (tree->family != prefix->family)
		return NULL;

	if (!tree->head) {
		tree->head = sx_radix_node_new(prefix);
		return tree->head;
	}

	candidate = &tree->head;
	chead = tree->head;

 next:
	eb = sx_prefix_eqbits(prefix, chead->prefix);
	if (eb < prefix->masklen && eb < chead->prefix->masklen) {
		struct sx_prefix *neoRoot = sx_prefix_alloc(prefix);
		
		struct sx_radix_node *rn, *ret=sx_radix_node_new(prefix);
		neoRoot->masklen = eb;
		sx_prefix_adjust_masklen(neoRoot);
		rn=sx_radix_node_new(neoRoot);
                sx_prefix_free(neoRoot);
                neoRoot = rn->prefix;
		if (!rn) {
			sx_report(SX_ERROR,"Unable to create node: %s\n",
			    strerror(errno));
			return NULL;
		}

		if (sx_prefix_isbitset(prefix, eb + 1)) {
			rn->l = chead;
			rn->r = ret;
		} else {
			rn->l = ret;
			rn->r = chead;
		}

		rn->parent = chead->parent;
		chead->parent = rn;
		ret->parent = rn;
		rn->isGlue = 1;
		*candidate = rn;
		return ret;
	} else if (eb == prefix->masklen && eb < chead->prefix->masklen) {
		struct sx_radix_node *ret = sx_radix_node_new(prefix);
		if (sx_prefix_isbitset(chead->prefix, eb + 1))
			ret->r = chead;
		else
			ret->l = chead;
		ret->parent = chead->parent;
		chead->parent = ret;
		*candidate = ret;
		return ret;
	} else if (eb == chead->prefix->masklen && eb < prefix->masklen) {
		if (sx_prefix_isbitset(prefix, eb + 1)) {
			if (chead->r) {
				candidate = &chead->r;
				chead = chead->r;
				goto next;
			} else {
				chead->r = sx_radix_node_new(prefix);
				chead->r->parent = chead;
				return chead->r;
			}
		} else {
			if (chead->l) {
				candidate = &chead->l;
				chead = chead->l;
				goto next;
			} else {
				chead->l = sx_radix_node_new(prefix);
				chead->l->parent = chead;
				return chead->l;
			}
		}
	} else if (eb == chead->prefix->masklen && eb == prefix->masklen) {
		/* equal routes... */
		if (chead->isGlue) {
			chead->isGlue = 0;
		}
		return chead;
	} else {
		char pbuffer[128], cbuffer[128];
		sx_prefix_snprintf(prefix, pbuffer, sizeof(pbuffer));
		sx_prefix_snprintf(chead->prefix, cbuffer, sizeof(cbuffer));
		printf("Unreachable point... eb=%i, prefix=%s, chead=%s\n", eb,
		    pbuffer, cbuffer);
		abort();
	}
}

void
sx_radix_node_fprintf(struct sx_radix_node *node, void *udata)
{
	FILE	*out = (udata?udata:stdout);
	char	 buffer[128];

	if (!node) {
		fprintf(out, "(null)\n");
	} else {
		sx_prefix_snprintf(node->prefix, buffer, sizeof(buffer));
		fprintf(out, "%s %s\n", buffer, node->isGlue ? "(glue)" : "");
	}
}

int
sx_radix_node_foreach(struct sx_radix_node *node,
    void (*func)(struct sx_radix_node *, void *), void *udata)
{
	func(node, udata);

	if (node->l)
		sx_radix_node_foreach(node->l, func, udata);

	if (node->r)
		sx_radix_node_foreach(node->r, func, udata);

	return 0;
}

int
sx_radix_tree_foreach(struct sx_radix_tree *tree,
    void (*func)(struct sx_radix_node *, void *), void *udata)
{
	if (!func || !tree || !tree->head)
		return 0;

	sx_radix_node_foreach(tree->head, func, udata);
	return 0;
}

static int
sx_radix_node_aggregate(struct sx_radix_node *node)
{
	if (node->l)
		sx_radix_node_aggregate(node->l);
	if (node->r)
		sx_radix_node_aggregate(node->r);

	if (debug_aggregation) {
		printf("Aggregating on node: ");
		sx_prefix_fprint(stdout, node->prefix);
		printf(" %s%s%u,%u\n", node->isGlue?"Glue ":"",
			node->isAggregate?"Aggregate ":"",node->aggregateLow,
			node->aggregateHi);
		if (node->r) {
			printf("R-Tree: ");
			sx_prefix_fprint(stdout, node->r->prefix);
			printf(" %s%s%u,%u\n",
			    (node->r->isGlue) ? "Glue " : "",
			    (node->r->isAggregate) ? " Aggregate ": "",
			    node->r->aggregateLow, node->r->aggregateHi);
			if (node->r->son) {
				printf("R-Son: ");
			sx_prefix_fprint(stdout, node->r->son->prefix);
			printf(" %s%s%u,%u\n",
			    node->r->son->isGlue ? "Glue " : "",
			    node->r->son->isAggregate ? "Aggregate " : "",
			    node->r->son->aggregateLow,
			    node->r->son->aggregateHi);
			}
		}
		if (node->l) {
			printf("L-Tree: ");
			sx_prefix_fprint(stdout, node->l->prefix);
			printf(" %s%s%u,%u\n", node->l->isGlue ? "Glue ": "",
			    node->l->isAggregate ? "Aggregate ": "",
			    node->l->aggregateLow, node->l->aggregateHi);
			if (node->l->son) {
				printf("L-Son: ");
				sx_prefix_fprint(stdout, node->l->son->prefix);
				printf(" %s%s%u,%u\n",
				    node->l->son->isGlue ? "Glue " : "",
				    node->l->son->isAggregate ? "Aggregate " : "",
				    node->l->son->aggregateLow,
				    node->l->son->aggregateHi);
			}
		}
	}

	if (node->r && node->l) {
		if (!node->r->isAggregate && !node->l->isAggregate
		    && !node->r->isGlue && !node->l->isGlue
		    && node->r->prefix->masklen == node->l->prefix->masklen) {
			if (node->r->prefix->masklen == node->prefix->masklen + 1) {
				node->isAggregate = 1;
				node->r->isGlue = 1;
				node->l->isGlue = 1;
				node->aggregateHi = node->r->prefix->masklen;
				if (node->isGlue) {
					node->isGlue = 0;
					node->aggregateLow = node->r->prefix->masklen;
				} else {
					node->aggregateLow = node->prefix->masklen;
				}
			}
			if (node->r->son && node->l->son
			    && node->r->son->isAggregate
			    && node->l->son->isAggregate
			    && node->r->son->aggregateHi == node->l->son->aggregateHi
			    && node->r->son->aggregateLow == node->l->son->aggregateLow
			    && node->r->prefix->masklen == node->prefix->masklen + 1
			    && node->l->prefix->masklen == node->prefix->masklen + 1) {
				node->son = sx_radix_node_new(node->prefix);
				node->son->isGlue = 0;
				node->son->isAggregate = 1;
				node->son->aggregateHi = node->r->son->aggregateHi;
				node->son->aggregateLow = node->r->son->aggregateLow;
				node->r->son->isGlue = 1;
				node->l->son->isGlue = 1;
			}
		} else if (node->r->isAggregate && node->l->isAggregate
		    && node->r->aggregateHi == node->l->aggregateHi
		    && node->r->aggregateLow==node->l->aggregateLow) {
			if (node->r->prefix->masklen == node->prefix->masklen + 1
			    && node->l->prefix->masklen == node->prefix->masklen + 1) {
				if (node->isGlue) {
					node->r->isGlue = 1;
					node->l->isGlue = 1;
					node->isAggregate = 1;
					node->isGlue = 0;
					node->aggregateHi = node->r->aggregateHi;
					node->aggregateLow = node->r->aggregateLow;
				} else if (node->r->prefix->masklen == node->r->aggregateLow) {
					node->r->isGlue = 1;
					node->l->isGlue = 1;
					node->isAggregate = 1;
					node->aggregateHi = node->r->aggregateHi;
					node->aggregateLow = node->prefix->masklen;
				} else {
					node->son = sx_radix_node_new(node->prefix);
					node->son->isGlue = 0;
					node->son->isAggregate = 1;
					node->son->aggregateHi = node->r->aggregateHi;
					node->son->aggregateLow = node->r->aggregateLow;
					node->r->isGlue = 1;
					node->l->isGlue = 1;
					if (node->r->son && node->l->son
					    && node->r->son->aggregateHi == node->l->son->aggregateHi
					    && node->r->son->aggregateLow == node->l->son->aggregateLow) {
						node->son->son = sx_radix_node_new(node->prefix);
						node->son->son->isGlue = 0;
						node->son->son->isAggregate = 1;
						node->son->son->aggregateHi = node->r->son->aggregateHi;
						node->son->son->aggregateLow = node->r->son->aggregateLow;
						node->r->son->isGlue = 1;
						node->l->son->isGlue = 1;
					}
				}
			}
		} else if (node->l->son && node->r->isAggregate
		    && node->l->son->isAggregate
		    && node->r->aggregateHi == node->l->son->aggregateHi
		    && node->r->aggregateLow == node->l->son->aggregateLow) {
			if (node->r->prefix->masklen == node->prefix->masklen + 1
			    && node->l->prefix->masklen == node->prefix->masklen + 1) {
				if (node->isGlue) {
					node->r->isGlue = 1;
					node->l->son->isGlue = 1;
					node->isAggregate = 1;
					node->isGlue = 0;
					node->aggregateHi = node->r->aggregateHi;
					node->aggregateLow = node->r->aggregateLow;
				} else {
					node->son = sx_radix_node_new(node->prefix);
					node->son->isGlue = 0;
					node->son->isAggregate = 1;
					node->son->aggregateHi = node->r->aggregateHi;
					node->son->aggregateLow = node->r->aggregateLow;
					node->r->isGlue = 1;
					node->l->son->isGlue = 1;
				}
			}
		} else if (node->r->son && node->l->isAggregate
		    && node->r->son->isAggregate
		    && node->l->aggregateHi == node->r->son->aggregateHi
		    && node->l->aggregateLow == node->r->son->aggregateLow) {
			if (node->l->prefix->masklen == node->prefix->masklen + 1
			    && node->r->prefix->masklen == node->prefix->masklen + 1) {
				if (node->isGlue) {
					node->l->isGlue = 1;
					node->r->son->isGlue = 1;
					node->isAggregate = 1;
					node->isGlue = 0;
					node->aggregateHi = node->l->aggregateHi;
					node->aggregateLow = node->l->aggregateLow;
				} else {
					node->son = sx_radix_node_new(node->prefix);
					node->son->isGlue = 0;
					node->son->isAggregate = 1;
					node->son->aggregateHi = node->l->aggregateHi;
					node->son->aggregateLow = node->l->aggregateLow;
					node->l->isGlue = 1;
					node->r->son->isGlue = 1;
				}
			}
		}
	}

	return 0;
}

int
sx_radix_tree_aggregate(struct sx_radix_tree *tree)
{
	if (tree && tree->head)
		return sx_radix_node_aggregate(tree->head);

	return 0;
}

static void
setGlueUpTo(struct sx_radix_node *node, void *udata)
{
	unsigned refine = *(unsigned *)udata;

	if (node && node->prefix->masklen <= refine)
		node->isGlue = 1;
}

static int
sx_radix_node_refine(struct sx_radix_node *node, unsigned refine)
{
	if (!node->isGlue && node->prefix->masklen<refine) {
		node->isAggregate = 1;
		node->aggregateLow = node->prefix->masklen;
		node->aggregateHi = refine;
		if (node->l) {
			sx_radix_node_foreach(node->l, setGlueUpTo, &refine);
			sx_radix_node_refine(node->l, refine);
		}
		if (node->r) {
			sx_radix_node_foreach(node->r, setGlueUpTo, &refine);
			sx_radix_node_refine(node->r, refine);
		}
	} else if (!node->isGlue && node->prefix->masklen == refine) {
		/* not setting aggregate in this case */
		if (node->l)
			sx_radix_node_refine(node->l, refine);
		if (node->r)
			sx_radix_node_refine(node->r, refine);
	} else if (node->isGlue) {
		if (node->r)
			sx_radix_node_refine(node->r, refine);
		if (node->l)
			sx_radix_node_refine(node->l, refine);
	} else {
		/* node->prefix.masklen > refine */
		/*
		 * do nothing, should pass specifics 'as is'. Also, do not
		 * process any embedded routes, their masklen is bigger, too...
		node->isGlue = 1;
		if (node->l)
			sx_radix_node_foreach(node->l, setGlue, NULL);

		if (node->r)
			sx_radix_node_foreach(node->r, setGlue, NULL);
		*/
	}
	return 0;
}
	
int
sx_radix_tree_refine(struct sx_radix_tree *tree, unsigned refine)
{
	if (tree && tree->head)
		return sx_radix_node_refine(tree->head, refine);

	return 0;
}

static void
setGlueFrom(struct sx_radix_node *node, void *udata)
{
	unsigned refine = *(unsigned *)udata;

	if (node && node->prefix->masklen <= refine)
		node->isGlue = 1;
}

static int
sx_radix_node_refineLow(struct sx_radix_node *node, unsigned refineLow)
{
	if (!node->isGlue && node->prefix->masklen<=refineLow) {

		if (!node->isAggregate) {
			node->isAggregate = 1;
			node->aggregateLow = refineLow;
			if (node->prefix->family == AF_INET)
				node->aggregateHi = 32;
			else
				node->aggregateHi = 128;
		} else
			node->aggregateLow=refineLow;

		if (node->l) {
			sx_radix_node_foreach(node->l, setGlueFrom, &refineLow);
			sx_radix_node_refineLow(node->l, refineLow);
		}

		if (node->r) {
			sx_radix_node_foreach(node->r, setGlueFrom, &refineLow);
			sx_radix_node_refineLow(node->r, refineLow);
		}

	} else if (!node->isGlue && node->prefix->masklen == refineLow) {
		/* not setting aggregate in this case */
		if (node->l)
			sx_radix_node_refineLow(node->l, refineLow);
		if (node->r)
			sx_radix_node_refineLow(node->r, refineLow);
	} else if (node->isGlue) {
		if (node->r)
			sx_radix_node_refineLow(node->r, refineLow);
		if (node->l)
			sx_radix_node_refineLow(node->l, refineLow);
	} else {
		/* node->prefix.masklen > refine */
		/* do nothing, should pass specifics 'as is'. Also, do not
		process any embedded routes, their masklen is bigger, too...
		node->isGlue = 1;
		if (node->l)
			sx_radix_node_foreach(node->l, setGlue, NULL);
		if (node->r)
			sx_radix_node_foreach(node->r, setGlue, NULL);
		*/
	}

	return 0;
}

int
sx_radix_tree_refineLow(struct sx_radix_tree *tree, unsigned refineLow)
{
	if (tree && tree->head)
		return sx_radix_node_refineLow(tree->head, refineLow);

	return 0;
}

#if SX_PTREE_TEST
int
main() {
	struct sx_prefix	*p;
	struct sx_radix_tree	*tree;
	struct sx_radix_node	*node;
	int			 n;

	p = sx_prefix_new(0, "10.11.12.13/24");
	sx_prefix_fprint(stdout, p);
	printf("\n");
	p = sx_prefix_new(0, "10.11.12.13/33");
	sx_prefix_fprint(stdout, p);
	printf("\n");
	p = sx_prefix_new(0, "10.11.12.13/-133");
	sx_prefix_fprint(stdout, p);
	printf("\n");

	p = sx_prefix_new(AF_INET, "10.11.12.14/24");
	sx_prefix_fprint(stdout, p);
	printf("\n");
	p = sx_prefix_new(AF_INET, "10.11.12.14/33");
	sx_prefix_fprint(stdout, p);
	printf("\n");
	p = sx_prefix_new(AF_INET, "10.11.12.14/-133");
	sx_prefix_fprint(stdout, p);
	printf("\n");

	p = sx_prefix_new(AF_INET6, "10.11.12.15/24");
	sx_prefix_fprint(stdout, p);
	printf("\n");
	p = sx_prefix_new(AF_INET6, "10.11.12.15/33");
	sx_prefix_fprint(stdout, p);
	printf("\n");
	p = sx_prefix_new(AF_INET6, "10.11.12.15/-133");
	sx_prefix_fprint(stdout, p);
	printf("\n");

	p = sx_prefix_new(0, "2001:1b00::/24");
	sx_prefix_fprint(stdout, p);
	printf("\n");
	p = sx_prefix_new(0, "2001:1b00::/33");
	sx_prefix_fprint(stdout, p);
	printf("\n");
	p = sx_prefix_new(0, "2001:1b00::/-133");
	sx_prefix_fprint(stdout, p);
	printf("\n");

	p = sx_prefix_new(AF_INET6, "2001:1b01::/24");
	sx_prefix_fprint(stdout, p);
	printf("\n");
	p = sx_prefix_new(AF_INET6, "2001:1b01::/33");
	sx_prefix_fprint(stdout, p);
	printf("\n");
	p = sx_prefix_new(AF_INET6, "2001:1b01::/-133");
	sx_prefix_fprint(stdout, p);
	printf("\n");

#define SX_TEST_EBITS(a, b, susp) \
	n = sx_prefix_eqbits(sx_prefix_new(0, a), sx_prefix_new(0, b)); \
	if (n != susp) \
		printf("FAILED: %s eqbits %s=%i, not %i\n", a, b, n, susp); \
	else \
		printf("OK, %s eqbits %s = %i, as suspected\n", a, b, n);
	SX_TEST_EBITS("192.168.0.0/24", "192.168.1.0/24", 23);
	SX_TEST_EBITS("192.168.0.0/32", "192.168.0.1/32", 31);
#if SX_LIBPTREE_IPV6
	SX_TEST_EBITS("2001:1b00::/32", "2001:1b01::/32", 31);
#endif

	p = sx_prefix_new(0, "10.11.12.255/32");
	sx_prefix_fprint(stdout, p);
	printf("\n31'th bit is %i\n",sx_prefix_isbitset(p, 31));
	printf("32'th bit is %i\n",sx_prefix_isbitset(p, 32));
	printf("33'th bit is %i\n",sx_prefix_isbitset(p, 33));
	p = sx_prefix_new(0, "10.11.12.255/31");
	sx_prefix_fprint(stdout, p);
	printf("\n31'th bit is %i\n", sx_prefix_isbitset(p, 31));
	printf("32'th bit is %i\n", sx_prefix_isbitset(p, 32));
	printf("33'th bit is %i\n", sx_prefix_isbitset(p, 33));
	p = sx_prefix_new(0, "10.11.12.255/30");
	sx_prefix_fprint(stdout, p);
	printf("\n31'th bit is %i\n", sx_prefix_isbitset(p, 31));
	p = sx_prefix_new(0, "10.11.12.255/29");
	sx_prefix_fprint(stdout, p);
	printf("\n");
	p = sx_prefix_new(0, "10.11.12.255/28");
	sx_prefix_fprint(stdout, p);
	printf("\n");
	p = sx_prefix_new(0, "10.11.12.255/27");
	sx_prefix_fprint(stdout, p);
	printf("\n");
	p = sx_prefix_new(0, "10.11.12.255/26");
	sx_prefix_fprint(stdout, p);
	printf("\n");
	p = sx_prefix_new(0, "10.11.12.255/25");
	sx_prefix_fprint(stdout, p);
	printf("\n25'th bit is %i\n", sx_prefix_isbitset(p, 25));
	p = sx_prefix_new(0, "10.11.12.255/24");
	sx_prefix_fprint(stdout, p);
	printf("\n25'th bit is %i\n", sx_prefix_isbitset(p, 25));

	tree = sx_radix_tree_new(AF_INET);
	sx_radix_tree_insert(tree, sx_prefix_new(0, "81.9.100.10/32"));
	sx_radix_tree_insert(tree, sx_prefix_new(0, "217.170.80.83/32"));

	sx_radix_tree_foreach(tree, sx_radix_node_fprintf, NULL);

	tree = sx_radix_tree_new(AF_INET);
	sx_radix_tree_insert(tree, sx_prefix_new(0, "81.9.100.10/32"));
	sx_radix_tree_insert(tree, sx_prefix_new(0, "217.170.80.83/32"));
	sx_radix_tree_insert(tree, sx_prefix_new(0, "217.170.80.84/32"));
	sx_radix_tree_insert(tree, sx_prefix_new(0, "217.170.80.85/32"));
	sx_radix_tree_insert(tree, sx_prefix_new(0, "217.170.80.86/32"));
	sx_radix_tree_insert(tree, sx_prefix_new(0, "217.170.80.87/32"));
	sx_radix_tree_insert(tree, sx_prefix_new(0, "217.170.80.90/32"));
	sx_radix_tree_insert(tree, sx_prefix_new(0, "217.170.80.90/32"));
	sx_radix_tree_insert(tree, sx_prefix_new(0, "127.0.0.1/32"));
	sx_radix_tree_insert(tree, sx_prefix_new(0, "127.0.0.1/24"));
	sx_radix_tree_insert(tree, sx_prefix_new(0, "127.0.0.0/24"));
	sx_radix_tree_insert(tree, sx_prefix_new(0, "128.0.0.0/1"));

	sx_radix_tree_foreach(tree, sx_radix_node_fprintf, NULL);

	printf("lookup 1.1.1.1: ");
	node = sx_radix_tree_lookup(tree, sx_prefix_new(0, "1.1.1.1"));
	sx_radix_node_fprintf(node, NULL);

	printf("lookup 217.170.80.90: ");
	node = sx_radix_tree_lookup(tree, sx_prefix_new(0, "217.170.80.90"));
	sx_radix_node_fprintf(node, NULL);

	sx_radix_tree_unlink(tree, node);
	printf("lookup 217.170.80.90 after delete: ");
	node = sx_radix_tree_lookup(tree, sx_prefix_new(0, "217.170.80.90"));
	sx_radix_node_fprintf(node, NULL);

	sx_radix_tree_insert(tree, sx_prefix_new(0, "217.170.80.90/32"));
	printf("lookup 217.170.80.90 after reinsert: ");
	node = sx_radix_tree_lookup(tree, sx_prefix_new(0, "217.170.80.90"));
	sx_radix_node_fprintf(node, NULL);

	printf("lookup 217.170.80.81: ");
	node = sx_radix_tree_lookup(tree, sx_prefix_new(0, "217.170.80.81"));
	sx_radix_node_fprintf(node, NULL);

	printf("lookup 127.0.0.1/24: ");
	node = sx_radix_tree_lookup(tree, sx_prefix_new(0, "127.0.0.1/24"));
	sx_radix_node_fprintf(node, NULL);

	printf("lookup 127.0.0.1/26: ");
	node = sx_radix_tree_lookup(tree, sx_prefix_new(0, "127.0.0.1/26"));
	sx_radix_node_fprintf(node, NULL);

	printf("lookup 127.0.0.1/23: ");
	node = sx_radix_tree_lookup(tree, sx_prefix_new(0, "127.0.0.1/23"));
	sx_radix_node_fprintf(node, NULL);

	tree = sx_radix_tree_new(AF_INET6);
	sx_radix_tree_insert(tree, sx_prefix_new(0, "2100:1b00::/32"));
	sx_radix_tree_insert(tree, sx_prefix_new(0, "2100:1b01::/32"));
	sx_radix_tree_insert(tree, sx_prefix_new(0, "2100:1b00::/33"));
	sx_radix_tree_insert(tree, sx_prefix_new(0, "2100:1b00::1/128"));
	sx_radix_tree_foreach(tree, sx_radix_node_fprintf, NULL);

	return 0;
}

#endif
