Blame src/segtree.c

Packit c5a612
/*
Packit c5a612
 * Copyright (c) 2008-2012 Patrick McHardy <kaber@trash.net>
Packit c5a612
 *
Packit c5a612
 * This program is free software; you can redistribute it and/or modify
Packit c5a612
 * it under the terms of the GNU General Public License version 2 as
Packit c5a612
 * published by the Free Software Foundation.
Packit c5a612
 *
Packit c5a612
 * Development of this code funded by Astaro AG (http://www.astaro.com/)
Packit c5a612
 */
Packit c5a612
Packit c5a612
#include <stdlib.h>
Packit c5a612
#include <string.h>
Packit c5a612
#include <inttypes.h>
Packit c5a612
#include <arpa/inet.h>
Packit c5a612
Packit c5a612
#include <libnftnl/udata.h>
Packit c5a612
Packit c5a612
#include <rule.h>
Packit c5a612
#include <expression.h>
Packit c5a612
#include <gmputil.h>
Packit c5a612
#include <utils.h>
Packit c5a612
#include <rbtree.h>
Packit c5a612
Packit c5a612
/**
Packit c5a612
 * struct seg_tree - segment tree
Packit c5a612
 *
Packit c5a612
 * @root:	the rbtree's root
Packit c5a612
 * @type:	the datatype of the dimension
Packit c5a612
 * @dwidth:	width of the dimension
Packit c5a612
 * @byteorder:	byteorder of elements
Packit c5a612
 * @debug_mask:	display debugging information
Packit c5a612
 */
Packit c5a612
struct seg_tree {
Packit c5a612
	struct rb_root			root;
Packit c5a612
	const struct datatype		*keytype;
Packit c5a612
	unsigned int			keylen;
Packit c5a612
	const struct datatype		*datatype;
Packit c5a612
	unsigned int			datalen;
Packit c5a612
	enum byteorder			byteorder;
Packit c5a612
	unsigned int			debug_mask;
Packit c5a612
};
Packit c5a612
Packit c5a612
enum elementary_interval_flags {
Packit c5a612
	EI_F_INTERVAL_END	= 0x1,
Packit c5a612
	EI_F_INTERVAL_OPEN	= 0x2,
Packit c5a612
};
Packit c5a612
Packit c5a612
/**
Packit c5a612
 * struct elementary_interval - elementary interval [left, right]
Packit c5a612
 *
Packit c5a612
 * @rb_node:	seg_tree rb node
Packit c5a612
 * @list:	list node for linearized tree
Packit c5a612
 * @left:	left endpoint
Packit c5a612
 * @right:	right endpoint
Packit c5a612
 * @size:	interval size (right - left)
Packit c5a612
 * @flags:	flags
Packit c5a612
 * @expr:	associated expression
Packit c5a612
 */
Packit c5a612
struct elementary_interval {
Packit c5a612
	union {
Packit c5a612
		struct rb_node		rb_node;
Packit c5a612
		struct list_head	list;
Packit c5a612
	};
Packit c5a612
Packit c5a612
	mpz_t				left;
Packit c5a612
	mpz_t				right;
Packit c5a612
	mpz_t				size;
Packit c5a612
Packit c5a612
	enum elementary_interval_flags	flags;
Packit c5a612
	struct expr			*expr;
Packit c5a612
};
Packit c5a612
Packit c5a612
static void seg_tree_init(struct seg_tree *tree, const struct set *set,
Packit c5a612
			  struct expr *init, unsigned int debug_mask)
Packit c5a612
{
Packit c5a612
	struct expr *first;
Packit c5a612
Packit c5a612
	first = list_entry(init->expressions.next, struct expr, list);
Packit c5a612
	tree->root	= RB_ROOT;
Packit c5a612
	tree->keytype	= set->key->dtype;
Packit c5a612
	tree->keylen	= set->key->len;
Packit c5a612
	tree->datatype	= set->datatype;
Packit c5a612
	tree->datalen	= set->datalen;
Packit c5a612
	tree->byteorder	= first->byteorder;
Packit c5a612
	tree->debug_mask = debug_mask;
Packit c5a612
}
Packit c5a612
Packit c5a612
static struct elementary_interval *ei_alloc(const mpz_t left, const mpz_t right,
Packit c5a612
					    struct expr *expr,
Packit c5a612
					    enum elementary_interval_flags flags)
Packit c5a612
{
Packit c5a612
	struct elementary_interval *ei;
Packit c5a612
Packit c5a612
	ei = xzalloc(sizeof(*ei));
Packit c5a612
	mpz_init_set(ei->left, left);
Packit c5a612
	mpz_init_set(ei->right, right);
Packit c5a612
	mpz_init(ei->size);
Packit c5a612
	mpz_sub(ei->size, right, left);
Packit c5a612
	if (expr != NULL)
Packit c5a612
		ei->expr = expr_get(expr);
Packit c5a612
	ei->flags = flags;
Packit c5a612
	return ei;
Packit c5a612
}
Packit c5a612
Packit c5a612
static void ei_destroy(struct elementary_interval *ei)
Packit c5a612
{
Packit c5a612
	mpz_clear(ei->left);
Packit c5a612
	mpz_clear(ei->right);
Packit c5a612
	mpz_clear(ei->size);
Packit c5a612
	if (ei->expr != NULL)
Packit c5a612
		expr_free(ei->expr);
Packit c5a612
	xfree(ei);
Packit c5a612
}
Packit c5a612
Packit c5a612
/**
Packit c5a612
 * ei_lookup - find elementary interval containing point p
Packit c5a612
 *
Packit c5a612
 * @tree:	segment tree
Packit c5a612
 * @p:		the point
Packit c5a612
 */
Packit c5a612
static struct elementary_interval *ei_lookup(struct seg_tree *tree, const mpz_t p)
Packit c5a612
{
Packit c5a612
	struct rb_node *n = tree->root.rb_node;
Packit c5a612
	struct elementary_interval *ei;
Packit c5a612
Packit c5a612
	while (n != NULL) {
Packit c5a612
		ei = rb_entry(n, struct elementary_interval, rb_node);
Packit c5a612
Packit c5a612
		if (mpz_cmp(p, ei->left) >= 0 &&
Packit c5a612
		    mpz_cmp(p, ei->right) <= 0)
Packit c5a612
			return ei;
Packit c5a612
		else if (mpz_cmp(p, ei->left) <= 0)
Packit c5a612
			n = n->rb_left;
Packit c5a612
		else if (mpz_cmp(p, ei->right) > 0)
Packit c5a612
			n = n->rb_right;
Packit c5a612
	}
Packit c5a612
	return NULL;
Packit c5a612
}
Packit c5a612
Packit c5a612
static void ei_remove(struct seg_tree *tree, struct elementary_interval *ei)
Packit c5a612
{
Packit c5a612
	rb_erase(&ei->rb_node, &tree->root);
Packit c5a612
}
Packit c5a612
Packit c5a612
static void __ei_insert(struct seg_tree *tree, struct elementary_interval *new)
Packit c5a612
{
Packit c5a612
	struct rb_node **p = &tree->root.rb_node;
Packit c5a612
	struct rb_node *parent = NULL;
Packit c5a612
	struct elementary_interval *ei;
Packit c5a612
Packit c5a612
	while (*p != NULL) {
Packit c5a612
		parent = *p;
Packit c5a612
		ei = rb_entry(parent, struct elementary_interval, rb_node);
Packit c5a612
Packit c5a612
		if (mpz_cmp(new->left, ei->left) >= 0 &&
Packit c5a612
		    mpz_cmp(new->left, ei->right) <= 0)
Packit c5a612
			break;
Packit c5a612
		else if (mpz_cmp(new->left, ei->left) <= 0)
Packit c5a612
			p = &(*p)->rb_left;
Packit c5a612
		else if (mpz_cmp(new->left, ei->left) > 0)
Packit c5a612
			p = &(*p)->rb_right;
Packit c5a612
	}
Packit c5a612
Packit c5a612
	rb_link_node(&new->rb_node, parent, p);
Packit c5a612
	rb_insert_color(&new->rb_node, &tree->root);
Packit c5a612
}
Packit c5a612
Packit c5a612
static bool segtree_debug(unsigned int debug_mask)
Packit c5a612
{
Packit c5a612
	if (debug_mask & NFT_DEBUG_SEGTREE)
Packit c5a612
		return true;
Packit c5a612
Packit c5a612
	return false;
Packit c5a612
}
Packit c5a612
Packit c5a612
/**
Packit c5a612
 * ei_insert - insert an elementary interval into the tree
Packit c5a612
 *
Packit c5a612
 * @tree:	the seg_tree
Packit c5a612
 * @new:	the elementary interval
Packit c5a612
 *
Packit c5a612
 * New entries take precedence over existing ones. Insertions are assumed to
Packit c5a612
 * be ordered by descending interval size, meaning the new interval never
Packit c5a612
 * extends over more than two existing intervals.
Packit c5a612
 */
Packit c5a612
static int ei_insert(struct list_head *msgs, struct seg_tree *tree,
Packit c5a612
		     struct elementary_interval *new, bool merge)
Packit c5a612
{
Packit c5a612
	struct elementary_interval *lei, *rei;
Packit c5a612
	mpz_t p;
Packit c5a612
Packit c5a612
	mpz_init2(p, tree->keylen);
Packit c5a612
Packit c5a612
	/*
Packit c5a612
	 * Lookup the intervals containing the left and right endpoints.
Packit c5a612
	 */
Packit c5a612
	lei = ei_lookup(tree, new->left);
Packit c5a612
	rei = ei_lookup(tree, new->right);
Packit c5a612
Packit c5a612
	if (segtree_debug(tree->debug_mask))
Packit c5a612
		pr_gmp_debug("insert: [%Zx %Zx]\n", new->left, new->right);
Packit c5a612
Packit c5a612
	if (lei != NULL && rei != NULL && lei == rei) {
Packit c5a612
		if (!merge)
Packit c5a612
			goto err;
Packit c5a612
		/*
Packit c5a612
		 * The new interval is entirely contained in the same interval,
Packit c5a612
		 * split it into two parts:
Packit c5a612
		 *
Packit c5a612
		 * [lei_left, new_left) and (new_right, rei_right]
Packit c5a612
		 */
Packit c5a612
		if (segtree_debug(tree->debug_mask))
Packit c5a612
			pr_gmp_debug("split [%Zx %Zx]\n", lei->left, lei->right);
Packit c5a612
Packit c5a612
		ei_remove(tree, lei);
Packit c5a612
Packit c5a612
		mpz_sub_ui(p, new->left, 1);
Packit c5a612
		if (mpz_cmp(lei->left, p) <= 0)
Packit c5a612
			__ei_insert(tree, ei_alloc(lei->left, p, lei->expr, 0));
Packit c5a612
Packit c5a612
		mpz_add_ui(p, new->right, 1);
Packit c5a612
		if (mpz_cmp(p, rei->right) < 0)
Packit c5a612
			__ei_insert(tree, ei_alloc(p, rei->right, lei->expr, 0));
Packit c5a612
		ei_destroy(lei);
Packit c5a612
	} else {
Packit c5a612
		if (lei != NULL) {
Packit c5a612
			if (!merge)
Packit c5a612
				goto err;
Packit c5a612
			/*
Packit c5a612
			 * Left endpoint is within lei, adjust it so we have:
Packit c5a612
			 *
Packit c5a612
			 * [lei_left, new_left)[new_left, new_right]
Packit c5a612
			 */
Packit c5a612
			if (segtree_debug(tree->debug_mask)) {
Packit c5a612
				pr_gmp_debug("adjust left [%Zx %Zx]\n",
Packit c5a612
					     lei->left, lei->right);
Packit c5a612
			}
Packit c5a612
Packit c5a612
			mpz_sub_ui(lei->right, new->left, 1);
Packit c5a612
			mpz_sub(lei->size, lei->right, lei->left);
Packit c5a612
			if (mpz_sgn(lei->size) < 0) {
Packit c5a612
				ei_remove(tree, lei);
Packit c5a612
				ei_destroy(lei);
Packit c5a612
			}
Packit c5a612
		}
Packit c5a612
		if (rei != NULL) {
Packit c5a612
			if (!merge)
Packit c5a612
				goto err;
Packit c5a612
			/*
Packit c5a612
			 * Right endpoint is within rei, adjust it so we have:
Packit c5a612
			 *
Packit c5a612
			 * [new_left, new_right](new_right, rei_right]
Packit c5a612
			 */
Packit c5a612
			if (segtree_debug(tree->debug_mask)) {
Packit c5a612
				pr_gmp_debug("adjust right [%Zx %Zx]\n",
Packit c5a612
					     rei->left, rei->right);
Packit c5a612
			}
Packit c5a612
Packit c5a612
			mpz_add_ui(rei->left, new->right, 1);
Packit c5a612
			mpz_sub(rei->size, rei->right, rei->left);
Packit c5a612
			if (mpz_sgn(rei->size) < 0) {
Packit c5a612
				ei_remove(tree, rei);
Packit c5a612
				ei_destroy(rei);
Packit c5a612
			}
Packit c5a612
		}
Packit c5a612
	}
Packit c5a612
Packit c5a612
	__ei_insert(tree, new);
Packit c5a612
Packit c5a612
	mpz_clear(p);
Packit c5a612
Packit c5a612
	return 0;
Packit c5a612
err:
Packit c5a612
	errno = EEXIST;
Packit c5a612
	return expr_binary_error(msgs, lei->expr, new->expr,
Packit c5a612
				 "conflicting intervals specified");
Packit c5a612
}
Packit c5a612
Packit c5a612
/*
Packit c5a612
 * Sort intervals according to their priority, which is defined inversely to
Packit c5a612
 * their size.
Packit c5a612
 *
Packit c5a612
 * The beginning of the interval is used as secondary sorting criterion. This
Packit c5a612
 * makes sure that overlapping ranges with equal priority are next to each
Packit c5a612
 * other, allowing to easily detect unsolvable conflicts during insertion.
Packit c5a612
 *
Packit c5a612
 * Note: unsolvable conflicts can only occur when using ranges or two identical
Packit c5a612
 * prefix specifications.
Packit c5a612
 */
Packit c5a612
static int interval_cmp(const void *p1, const void *p2)
Packit c5a612
{
Packit c5a612
	const struct elementary_interval *e1 = *(void * const *)p1;
Packit c5a612
	const struct elementary_interval *e2 = *(void * const *)p2;
Packit c5a612
	mpz_t d;
Packit c5a612
	int ret;
Packit c5a612
Packit c5a612
	mpz_init(d);
Packit c5a612
Packit c5a612
	mpz_sub(d, e2->size, e1->size);
Packit c5a612
	if (mpz_cmp_ui(d, 0))
Packit c5a612
		ret = mpz_sgn(d);
Packit c5a612
	else
Packit c5a612
		ret = mpz_cmp(e1->left, e2->left);
Packit c5a612
Packit c5a612
	mpz_clear(d);
Packit c5a612
	return ret;
Packit c5a612
}
Packit c5a612
Packit c5a612
static unsigned int expr_to_intervals(const struct expr *set,
Packit c5a612
				      unsigned int keylen,
Packit c5a612
				      struct elementary_interval **intervals)
Packit c5a612
{
Packit c5a612
	struct elementary_interval *ei;
Packit c5a612
	struct expr *i, *next;
Packit c5a612
	unsigned int n;
Packit c5a612
	mpz_t low, high;
Packit c5a612
Packit c5a612
	mpz_init2(low, keylen);
Packit c5a612
	mpz_init2(high, keylen);
Packit c5a612
Packit c5a612
	/*
Packit c5a612
	 * Convert elements to intervals.
Packit c5a612
	 */
Packit c5a612
	n = 0;
Packit c5a612
	list_for_each_entry_safe(i, next, &set->expressions, list) {
Packit c5a612
		range_expr_value_low(low, i);
Packit c5a612
		range_expr_value_high(high, i);
Packit c5a612
		ei = ei_alloc(low, high, i, 0);
Packit c5a612
		intervals[n++] = ei;
Packit c5a612
	}
Packit c5a612
	mpz_clear(high);
Packit c5a612
	mpz_clear(low);
Packit c5a612
Packit c5a612
	return n;
Packit c5a612
}
Packit c5a612
Packit c5a612
static bool intervals_match(const struct elementary_interval *e1,
Packit c5a612
			    const struct elementary_interval *e2)
Packit c5a612
{
Packit c5a612
	return mpz_cmp(e1->left, e2->left) == 0 &&
Packit c5a612
	       mpz_cmp(e1->right, e2->right) == 0;
Packit c5a612
}
Packit c5a612
Packit c5a612
/* This function checks for overlaps in two ways:
Packit c5a612
 *
Packit c5a612
 * 1) A new interval end intersects an existing interval.
Packit c5a612
 * 2) New intervals that are larger than existing ones, that don't intersect
Packit c5a612
 *    at all, but that wrap the existing ones.
Packit c5a612
 */
Packit c5a612
static bool interval_overlap(const struct elementary_interval *e1,
Packit c5a612
			     const struct elementary_interval *e2)
Packit c5a612
{
Packit c5a612
	if (intervals_match(e1, e2))
Packit c5a612
		return false;
Packit c5a612
Packit c5a612
	return (mpz_cmp(e1->left, e2->left) >= 0 &&
Packit c5a612
	        mpz_cmp(e1->left, e2->right) <= 0) ||
Packit c5a612
	       (mpz_cmp(e1->right, e2->left) >= 0 &&
Packit c5a612
	        mpz_cmp(e1->right, e2->right) <= 0) ||
Packit c5a612
	       (mpz_cmp(e1->left, e2->left) <= 0 &&
Packit c5a612
		mpz_cmp(e1->right, e2->right) >= 0);
Packit c5a612
}
Packit c5a612
Packit c5a612
static int set_overlap(struct list_head *msgs, const struct set *set,
Packit c5a612
		       struct expr *init, unsigned int keylen, bool add)
Packit c5a612
{
Packit c5a612
	struct elementary_interval *new_intervals[init->size];
Packit c5a612
	struct elementary_interval *intervals[set->init->size];
Packit c5a612
	unsigned int n, m, i, j;
Packit c5a612
	int ret = 0;
Packit c5a612
Packit c5a612
	n = expr_to_intervals(init, keylen, new_intervals);
Packit c5a612
	m = expr_to_intervals(set->init, keylen, intervals);
Packit c5a612
Packit c5a612
	for (i = 0; i < n; i++) {
Packit c5a612
		bool found = false;
Packit c5a612
Packit c5a612
		for (j = 0; j < m; j++) {
Packit c5a612
			if (add && interval_overlap(new_intervals[i],
Packit c5a612
						    intervals[j])) {
Packit c5a612
				expr_error(msgs, new_intervals[i]->expr,
Packit c5a612
					   "interval overlaps with an existing one");
Packit c5a612
				errno = EEXIST;
Packit c5a612
				ret = -1;
Packit c5a612
				goto out;
Packit c5a612
			} else if (!add && intervals_match(new_intervals[i],
Packit c5a612
							   intervals[j])) {
Packit c5a612
				found = true;
Packit c5a612
				break;
Packit c5a612
			}
Packit c5a612
		}
Packit c5a612
		if (!add && !found) {
Packit c5a612
			expr_error(msgs, new_intervals[i]->expr,
Packit c5a612
				   "interval not found in set");
Packit c5a612
			errno = ENOENT;
Packit c5a612
			ret = -1;
Packit c5a612
			break;
Packit c5a612
		}
Packit c5a612
	}
Packit c5a612
out:
Packit c5a612
	for (i = 0; i < n; i++)
Packit c5a612
		ei_destroy(new_intervals[i]);
Packit c5a612
	for (i = 0; i < m; i++)
Packit c5a612
		ei_destroy(intervals[i]);
Packit c5a612
Packit c5a612
	return ret;
Packit c5a612
}
Packit c5a612
Packit c5a612
static int set_to_segtree(struct list_head *msgs, struct set *set,
Packit c5a612
			  struct expr *init, struct seg_tree *tree,
Packit c5a612
			  bool add, bool merge)
Packit c5a612
{
Packit c5a612
	struct elementary_interval *intervals[init->size];
Packit c5a612
	struct expr *i, *next;
Packit c5a612
	unsigned int n;
Packit c5a612
	int err;
Packit c5a612
Packit c5a612
	/* We are updating an existing set with new elements, check if the new
Packit c5a612
	 * interval overlaps with any of the existing ones.
Packit c5a612
	 */
Packit c5a612
	if (set->init && set->init != init) {
Packit c5a612
		err = set_overlap(msgs, set, init, tree->keylen, add);
Packit c5a612
		if (err < 0)
Packit c5a612
			return err;
Packit c5a612
	}
Packit c5a612
Packit c5a612
	n = expr_to_intervals(init, tree->keylen, intervals);
Packit c5a612
Packit c5a612
	list_for_each_entry_safe(i, next, &init->expressions, list) {
Packit c5a612
		list_del(&i->list);
Packit c5a612
		expr_free(i);
Packit c5a612
	}
Packit c5a612
Packit c5a612
	/*
Packit c5a612
	 * Sort intervals by priority.
Packit c5a612
	 */
Packit c5a612
	qsort(intervals, n, sizeof(intervals[0]), interval_cmp);
Packit c5a612
Packit c5a612
	/*
Packit c5a612
	 * Insert elements into tree
Packit c5a612
	 */
Packit c5a612
	for (n = 0; n < init->size; n++) {
Packit c5a612
		err = ei_insert(msgs, tree, intervals[n], merge);
Packit c5a612
		if (err < 0)
Packit c5a612
			return err;
Packit c5a612
	}
Packit c5a612
Packit c5a612
	return 0;
Packit c5a612
}
Packit c5a612
Packit c5a612
static bool segtree_needs_first_segment(const struct set *set,
Packit c5a612
					const struct expr *init, bool add)
Packit c5a612
{
Packit c5a612
	if (add) {
Packit c5a612
		/* Add the first segment in four situations:
Packit c5a612
		 *
Packit c5a612
		 * 1) This is an anonymous set.
Packit c5a612
		 * 2) This set exists and it is empty.
Packit c5a612
		 * 3) New empty set and, separately, new elements are added.
Packit c5a612
		 * 4) This set is created with a number of initial elements.
Packit c5a612
		 */
Packit c5a612
		if ((set_is_anonymous(set->flags)) ||
Packit c5a612
		    (set->init && set->init->size == 0) ||
Packit c5a612
		    (set->init == NULL && init) ||
Packit c5a612
		    (set->init == init)) {
Packit c5a612
			return true;
Packit c5a612
		}
Packit c5a612
	} else {
Packit c5a612
		/* If the set is empty after the removal, we have to
Packit c5a612
		 * remove the first non-matching segment too.
Packit c5a612
		 */
Packit c5a612
		if (set->init && set->init->size - init->size == 0)
Packit c5a612
			return true;
Packit c5a612
	}
Packit c5a612
	/* This is an update for a set that already contains elements, so don't
Packit c5a612
	 * add the first non-matching elements otherwise we hit EEXIST.
Packit c5a612
	 */
Packit c5a612
	return false;
Packit c5a612
}
Packit c5a612
Packit c5a612
static void segtree_linearize(struct list_head *list, const struct set *set,
Packit c5a612
			      const struct expr *init, struct seg_tree *tree,
Packit c5a612
			      bool add, bool merge)
Packit c5a612
{
Packit c5a612
	bool needs_first_segment = segtree_needs_first_segment(set, init, add);
Packit c5a612
	struct elementary_interval *ei, *nei, *prev = NULL;
Packit c5a612
	struct rb_node *node, *next;
Packit c5a612
	mpz_t p, q;
Packit c5a612
Packit c5a612
	mpz_init2(p, tree->keylen);
Packit c5a612
	mpz_init2(q, tree->keylen);
Packit c5a612
Packit c5a612
	/*
Packit c5a612
	 * Convert the tree of open intervals to half-closed map expressions.
Packit c5a612
	 */
Packit c5a612
	rb_for_each_entry_safe(ei, node, next, &tree->root, rb_node) {
Packit c5a612
		if (segtree_debug(tree->debug_mask))
Packit c5a612
			pr_gmp_debug("iter: [%Zx %Zx]\n", ei->left, ei->right);
Packit c5a612
Packit c5a612
		if (prev == NULL) {
Packit c5a612
			/*
Packit c5a612
			 * If the first segment doesn't begin at zero, insert a
Packit c5a612
			 * non-matching segment to cover [0, first_left).
Packit c5a612
			 */
Packit c5a612
			if (needs_first_segment && mpz_cmp_ui(ei->left, 0)) {
Packit c5a612
				mpz_set_ui(p, 0);
Packit c5a612
				mpz_sub_ui(q, ei->left, 1);
Packit c5a612
				nei = ei_alloc(p, q, NULL, EI_F_INTERVAL_END);
Packit c5a612
				list_add_tail(&nei->list, list);
Packit c5a612
			}
Packit c5a612
		} else {
Packit c5a612
			/*
Packit c5a612
			 * If the previous segment doesn't end directly left to
Packit c5a612
			 * this one, insert a non-matching segment to cover
Packit c5a612
			 * (prev_right, ei_left).
Packit c5a612
			 */
Packit c5a612
			mpz_add_ui(p, prev->right, 1);
Packit c5a612
			if (mpz_cmp(p, ei->left) < 0 ||
Packit c5a612
			    (!(set->flags & NFT_SET_ANONYMOUS) && !merge)) {
Packit c5a612
				mpz_sub_ui(q, ei->left, 1);
Packit c5a612
				nei = ei_alloc(p, q, NULL, EI_F_INTERVAL_END);
Packit c5a612
				list_add_tail(&nei->list, list);
Packit c5a612
			} else if (add && merge &&
Packit c5a612
			           ei->expr->etype != EXPR_MAPPING) {
Packit c5a612
				/* Merge contiguous segments only in case of
Packit c5a612
				 * new additions.
Packit c5a612
				 */
Packit c5a612
				mpz_set(prev->right, ei->right);
Packit c5a612
				ei_remove(tree, ei);
Packit c5a612
				ei_destroy(ei);
Packit c5a612
				continue;
Packit c5a612
			}
Packit c5a612
		}
Packit c5a612
Packit c5a612
		ei_remove(tree, ei);
Packit c5a612
		list_add_tail(&ei->list, list);
Packit c5a612
Packit c5a612
		prev = ei;
Packit c5a612
	}
Packit c5a612
Packit c5a612
	/*
Packit c5a612
	 * If the last segment doesn't end at the right side of the dimension,
Packit c5a612
	 * insert a non-matching segment to cover (last_right, end].
Packit c5a612
	 */
Packit c5a612
	if (mpz_scan0(prev->right, 0) != tree->keylen) {
Packit c5a612
		mpz_add_ui(p, prev->right, 1);
Packit c5a612
		mpz_bitmask(q, tree->keylen);
Packit c5a612
		nei = ei_alloc(p, q, NULL, EI_F_INTERVAL_END);
Packit c5a612
		list_add_tail(&nei->list, list);
Packit c5a612
	} else {
Packit c5a612
		prev->flags |= EI_F_INTERVAL_OPEN;
Packit c5a612
	}
Packit c5a612
Packit c5a612
	mpz_clear(p);
Packit c5a612
	mpz_clear(q);
Packit c5a612
}
Packit c5a612
Packit c5a612
static void set_insert_interval(struct expr *set, struct seg_tree *tree,
Packit c5a612
				const struct elementary_interval *ei)
Packit c5a612
{
Packit c5a612
	struct expr *expr;
Packit c5a612
Packit c5a612
	expr = constant_expr_alloc(&internal_location, tree->keytype,
Packit c5a612
				   tree->byteorder, tree->keylen, NULL);
Packit c5a612
	mpz_set(expr->value, ei->left);
Packit c5a612
	expr = set_elem_expr_alloc(&internal_location, expr);
Packit c5a612
Packit c5a612
	if (ei->expr != NULL) {
Packit c5a612
		if (ei->expr->etype == EXPR_MAPPING) {
Packit c5a612
			if (ei->expr->left->comment)
Packit c5a612
				expr->comment = xstrdup(ei->expr->left->comment);
Packit c5a612
			if (ei->expr->left->timeout)
Packit c5a612
				expr->timeout = ei->expr->left->timeout;
Packit c5a612
			expr = mapping_expr_alloc(&ei->expr->location, expr,
Packit c5a612
						  expr_get(ei->expr->right));
Packit c5a612
		} else {
Packit c5a612
			if (ei->expr->comment)
Packit c5a612
				expr->comment = xstrdup(ei->expr->comment);
Packit c5a612
			if (ei->expr->timeout)
Packit c5a612
				expr->timeout = ei->expr->timeout;
Packit c5a612
		}
Packit c5a612
	}
Packit c5a612
Packit c5a612
	if (ei->flags & EI_F_INTERVAL_END)
Packit c5a612
		expr->flags |= EXPR_F_INTERVAL_END;
Packit c5a612
	if (ei->flags & EI_F_INTERVAL_OPEN)
Packit c5a612
		expr->elem_flags |= NFTNL_SET_ELEM_F_INTERVAL_OPEN;
Packit c5a612
Packit c5a612
	compound_expr_add(set, expr);
Packit c5a612
}
Packit c5a612
Packit c5a612
int set_to_intervals(struct list_head *errs, struct set *set,
Packit c5a612
		     struct expr *init, bool add, unsigned int debug_mask,
Packit c5a612
		     bool merge, struct output_ctx *octx)
Packit c5a612
{
Packit c5a612
	struct elementary_interval *ei, *next;
Packit c5a612
	struct seg_tree tree;
Packit c5a612
	LIST_HEAD(list);
Packit c5a612
Packit c5a612
	seg_tree_init(&tree, set, init, debug_mask);
Packit c5a612
	if (set_to_segtree(errs, set, init, &tree, add, merge) < 0)
Packit c5a612
		return -1;
Packit c5a612
	segtree_linearize(&list, set, init, &tree, add, merge);
Packit c5a612
Packit c5a612
	init->size = 0;
Packit c5a612
	list_for_each_entry_safe(ei, next, &list, list) {
Packit c5a612
		if (segtree_debug(tree.debug_mask)) {
Packit c5a612
			pr_gmp_debug("list: [%.*Zx %.*Zx]\n",
Packit c5a612
				     2 * tree.keylen / BITS_PER_BYTE, ei->left,
Packit c5a612
				     2 * tree.keylen / BITS_PER_BYTE, ei->right);
Packit c5a612
		}
Packit c5a612
		set_insert_interval(init, &tree, ei);
Packit c5a612
		ei_destroy(ei);
Packit c5a612
	}
Packit c5a612
Packit c5a612
	if (segtree_debug(tree.debug_mask)) {
Packit c5a612
		expr_print(init, octx);
Packit c5a612
		pr_gmp_debug("\n");
Packit c5a612
	}
Packit c5a612
Packit c5a612
	return 0;
Packit c5a612
}
Packit c5a612
Packit c5a612
static void set_elem_add(const struct set *set, struct expr *init, mpz_t value,
Packit c5a612
			 uint32_t flags, enum byteorder byteorder)
Packit c5a612
{
Packit c5a612
	struct expr *expr;
Packit c5a612
Packit c5a612
	expr = constant_expr_alloc(&internal_location, set->key->dtype,
Packit c5a612
				   byteorder, set->key->len, NULL);
Packit c5a612
	mpz_set(expr->value, value);
Packit c5a612
	expr = set_elem_expr_alloc(&internal_location, expr);
Packit c5a612
	expr->flags = flags;
Packit c5a612
Packit c5a612
	compound_expr_add(init, expr);
Packit c5a612
}
Packit c5a612
Packit c5a612
struct expr *get_set_intervals(const struct set *set, const struct expr *init)
Packit c5a612
{
Packit c5a612
	struct expr *new_init;
Packit c5a612
	mpz_t low, high;
Packit c5a612
	struct expr *i;
Packit c5a612
Packit c5a612
	mpz_init2(low, set->key->len);
Packit c5a612
	mpz_init2(high, set->key->len);
Packit c5a612
Packit c5a612
	new_init = list_expr_alloc(&internal_location);
Packit c5a612
Packit c5a612
	list_for_each_entry(i, &init->expressions, list) {
Packit c5a612
		switch (i->key->etype) {
Packit c5a612
		case EXPR_VALUE:
Packit c5a612
			set_elem_add(set, new_init, i->key->value,
Packit c5a612
				     i->flags, i->byteorder);
Packit c5a612
			break;
Packit c5a612
		default:
Packit c5a612
			range_expr_value_low(low, i);
Packit c5a612
			set_elem_add(set, new_init, low, 0, i->byteorder);
Packit c5a612
			range_expr_value_high(high, i);
Packit c5a612
			mpz_add_ui(high, high, 1);
Packit c5a612
			set_elem_add(set, new_init, high,
Packit c5a612
				     EXPR_F_INTERVAL_END, i->byteorder);
Packit c5a612
			break;
Packit c5a612
		}
Packit c5a612
	}
Packit c5a612
Packit c5a612
	mpz_clear(low);
Packit c5a612
	mpz_clear(high);
Packit c5a612
Packit c5a612
	return new_init;
Packit c5a612
}
Packit c5a612
Packit c5a612
static struct expr *get_set_interval_find(const struct table *table,
Packit c5a612
					  const char *set_name,
Packit c5a612
					  struct expr *left,
Packit c5a612
					  struct expr *right)
Packit c5a612
{
Packit c5a612
	struct expr *range = NULL;
Packit c5a612
	struct set *set;
Packit Service 6f0138
	mpz_t low, high;
Packit c5a612
	struct expr *i;
Packit c5a612
Packit c5a612
	set = set_lookup(table, set_name);
Packit Service 6f0138
	mpz_init2(low, set->key->len);
Packit Service 6f0138
	mpz_init2(high, set->key->len);
Packit c5a612
Packit c5a612
	list_for_each_entry(i, &set->init->expressions, list) {
Packit c5a612
		switch (i->key->etype) {
Packit c5a612
		case EXPR_RANGE:
Packit Service 6f0138
			range_expr_value_low(low, i);
Packit Service 6f0138
			range_expr_value_high(high, i);
Packit Service 6f0138
			if (mpz_cmp(left->key->value, low) >= 0 &&
Packit Service 6f0138
			    mpz_cmp(right->key->value, high) <= 0) {
Packit Service 6f0138
				range = range_expr_alloc(&internal_location,
Packit Service 6f0138
							 expr_clone(left->key),
Packit Service 6f0138
							 expr_clone(right->key));
Packit Service 6f0138
				goto out;
Packit Service 6f0138
			}
Packit Service 6f0138
			break;
Packit Service 6f0138
		default:
Packit Service 6f0138
			break;
Packit Service 6f0138
		}
Packit Service 6f0138
	}
Packit Service 6f0138
out:
Packit Service 6f0138
	mpz_clear(low);
Packit Service 6f0138
	mpz_clear(high);
Packit c5a612
Packit Service 6f0138
	return range;
Packit Service 6f0138
}
Packit Service 6f0138
Packit Service 6f0138
static struct expr *get_set_interval_end(const struct table *table,
Packit Service 6f0138
					 const char *set_name,
Packit Service 6f0138
					 struct expr *left)
Packit Service 6f0138
{
Packit Service 6f0138
	struct expr *i, *range = NULL;
Packit Service 6f0138
	struct set *set;
Packit Service 6f0138
	mpz_t low, high;
Packit c5a612
Packit Service 6f0138
	set = set_lookup(table, set_name);
Packit Service 6f0138
	mpz_init2(low, set->key->len);
Packit Service 6f0138
	mpz_init2(high, set->key->len);
Packit Service 6f0138
Packit Service 6f0138
	list_for_each_entry(i, &set->init->expressions, list) {
Packit Service 6f0138
		switch (i->key->etype) {
Packit Service 6f0138
		case EXPR_RANGE:
Packit Service 6f0138
			range_expr_value_low(low, i);
Packit Service 6f0138
			if (mpz_cmp(low, left->key->value) == 0) {
Packit Service 6f0138
				range = range_expr_alloc(&internal_location,
Packit Service 6f0138
							 expr_clone(left->key),
Packit Service 6f0138
							 expr_clone(i->key->right));
Packit Service 6f0138
				goto out;
Packit Service 6f0138
			}
Packit Service 6f0138
			break;
Packit c5a612
		default:
Packit c5a612
			break;
Packit c5a612
		}
Packit c5a612
	}
Packit c5a612
out:
Packit Service 6f0138
	mpz_clear(low);
Packit Service 6f0138
	mpz_clear(high);
Packit c5a612
Packit c5a612
	return range;
Packit c5a612
}
Packit c5a612
Packit c5a612
int get_set_decompose(struct table *table, struct set *set)
Packit c5a612
{
Packit c5a612
	struct expr *i, *next, *range;
Packit c5a612
	struct expr *left = NULL;
Packit c5a612
	struct expr *new_init;
Packit c5a612
Packit c5a612
	new_init = set_expr_alloc(&internal_location, set);
Packit c5a612
Packit c5a612
	list_for_each_entry_safe(i, next, &set->init->expressions, list) {
Packit c5a612
		if (i->flags & EXPR_F_INTERVAL_END && left) {
Packit c5a612
			list_del(&left->list);
Packit c5a612
			list_del(&i->list);
Packit c5a612
			mpz_sub_ui(i->key->value, i->key->value, 1);
Packit c5a612
			range = get_set_interval_find(table, set->handle.set.name,
Packit c5a612
						    left, i);
Packit c5a612
			if (!range) {
Packit c5a612
				expr_free(new_init);
Packit c5a612
				errno = ENOENT;
Packit c5a612
				return -1;
Packit c5a612
			}
Packit c5a612
Packit c5a612
			compound_expr_add(new_init, range);
Packit c5a612
			left = NULL;
Packit c5a612
		} else {
Packit c5a612
			if (left) {
Packit Service 6f0138
				range = get_set_interval_end(table,
Packit Service 6f0138
							     set->handle.set.name,
Packit Service 6f0138
							     left);
Packit c5a612
				if (range)
Packit c5a612
					compound_expr_add(new_init, range);
Packit c5a612
				else
Packit c5a612
					compound_expr_add(new_init,
Packit c5a612
							  expr_clone(left));
Packit c5a612
			}
Packit c5a612
			left = i;
Packit c5a612
		}
Packit c5a612
	}
Packit c5a612
	if (left) {
Packit Service 6f0138
		range = get_set_interval_end(table, set->handle.set.name, left);
Packit c5a612
		if (range)
Packit c5a612
			compound_expr_add(new_init, range);
Packit c5a612
		else
Packit c5a612
			compound_expr_add(new_init, expr_clone(left));
Packit c5a612
	}
Packit c5a612
Packit c5a612
	expr_free(set->init);
Packit c5a612
	set->init = new_init;
Packit c5a612
Packit c5a612
	return 0;
Packit c5a612
}
Packit c5a612
Packit c5a612
static bool range_is_prefix(const mpz_t range)
Packit c5a612
{
Packit c5a612
	mpz_t tmp;
Packit c5a612
	bool ret;
Packit c5a612
Packit c5a612
	mpz_init_set(tmp, range);
Packit c5a612
	mpz_add_ui(tmp, tmp, 1);
Packit c5a612
	mpz_and(tmp, range, tmp);
Packit c5a612
	ret = !mpz_cmp_ui(tmp, 0);
Packit c5a612
	mpz_clear(tmp);
Packit c5a612
	return ret;
Packit c5a612
}
Packit c5a612
Packit c5a612
static struct expr *expr_value(struct expr *expr)
Packit c5a612
{
Packit c5a612
	switch (expr->etype) {
Packit c5a612
	case EXPR_MAPPING:
Packit c5a612
		return expr->left->key;
Packit c5a612
	case EXPR_SET_ELEM:
Packit c5a612
		return expr->key;
Packit c5a612
	default:
Packit c5a612
		BUG("invalid expression type %s\n", expr_name(expr));
Packit c5a612
	}
Packit c5a612
}
Packit c5a612
Packit c5a612
static int expr_value_cmp(const void *p1, const void *p2)
Packit c5a612
{
Packit c5a612
	struct expr *e1 = *(void * const *)p1;
Packit c5a612
	struct expr *e2 = *(void * const *)p2;
Packit c5a612
	int ret;
Packit c5a612
Packit c5a612
	ret = mpz_cmp(expr_value(e1)->value, expr_value(e2)->value);
Packit c5a612
	if (ret == 0) {
Packit c5a612
		if (e1->flags & EXPR_F_INTERVAL_END)
Packit c5a612
			return -1;
Packit c5a612
		else if (e2->flags & EXPR_F_INTERVAL_END)
Packit c5a612
			return 1;
Packit c5a612
	}
Packit c5a612
Packit c5a612
	return ret;
Packit c5a612
}
Packit c5a612
Packit c5a612
void interval_map_decompose(struct expr *set)
Packit c5a612
{
Packit c5a612
	struct expr **elements, **ranges;
Packit c5a612
	struct expr *i, *next, *low = NULL, *end;
Packit c5a612
	unsigned int n, m, size;
Packit c5a612
	mpz_t range, p;
Packit c5a612
	bool interval;
Packit c5a612
Packit c5a612
	if (set->size == 0)
Packit c5a612
		return;
Packit c5a612
Packit c5a612
	elements = xmalloc_array(set->size, sizeof(struct expr *));
Packit c5a612
	ranges = xmalloc_array(set->size * 2, sizeof(struct expr *));
Packit c5a612
Packit c5a612
	mpz_init(range);
Packit c5a612
	mpz_init(p);
Packit c5a612
Packit c5a612
	/* Sort elements */
Packit c5a612
	n = 0;
Packit c5a612
	list_for_each_entry_safe(i, next, &set->expressions, list) {
Packit c5a612
		compound_expr_remove(set, i);
Packit c5a612
		elements[n++] = i;
Packit c5a612
	}
Packit c5a612
	qsort(elements, n, sizeof(elements[0]), expr_value_cmp);
Packit c5a612
	size = n;
Packit c5a612
Packit c5a612
	/* Transform points (single values) into half-closed intervals */
Packit c5a612
	n = 0;
Packit c5a612
	interval = false;
Packit c5a612
	for (m = 0; m < size; m++) {
Packit c5a612
		i = elements[m];
Packit c5a612
Packit c5a612
		if (i->flags & EXPR_F_INTERVAL_END)
Packit c5a612
			interval = false;
Packit c5a612
		else if (interval) {
Packit c5a612
			end = expr_clone(i);
Packit c5a612
			end->flags |= EXPR_F_INTERVAL_END;
Packit c5a612
			ranges[n++] = end;
Packit c5a612
		} else
Packit c5a612
			interval = true;
Packit c5a612
Packit c5a612
		ranges[n++] = i;
Packit c5a612
	}
Packit c5a612
	size = n;
Packit c5a612
Packit c5a612
	for (n = 0; n < size; n++) {
Packit c5a612
		i = ranges[n];
Packit c5a612
Packit c5a612
		if (low == NULL) {
Packit c5a612
			if (i->flags & EXPR_F_INTERVAL_END) {
Packit c5a612
				/*
Packit c5a612
				 * End of interval mark
Packit c5a612
				 */
Packit c5a612
				expr_free(i);
Packit c5a612
				continue;
Packit c5a612
			} else {
Packit c5a612
				/*
Packit c5a612
				 * Start a new interval
Packit c5a612
				 */
Packit c5a612
				low = i;
Packit c5a612
				continue;
Packit c5a612
			}
Packit c5a612
		}
Packit c5a612
Packit c5a612
		mpz_sub(range, expr_value(i)->value, expr_value(low)->value);
Packit c5a612
		mpz_sub_ui(range, range, 1);
Packit c5a612
Packit c5a612
		mpz_and(p, expr_value(low)->value, range);
Packit c5a612
Packit c5a612
		if (!mpz_cmp_ui(range, 0))
Packit c5a612
			compound_expr_add(set, expr_get(low));
Packit c5a612
		else if ((!range_is_prefix(range) ||
Packit c5a612
			  !(i->dtype->flags & DTYPE_F_PREFIX)) ||
Packit c5a612
			 mpz_cmp_ui(p, 0)) {
Packit c5a612
			struct expr *tmp;
Packit c5a612
Packit c5a612
			tmp = constant_expr_alloc(&low->location, low->dtype,
Packit c5a612
						  low->byteorder, expr_value(low)->len,
Packit c5a612
						  NULL);
Packit c5a612
Packit c5a612
			mpz_add(range, range, expr_value(low)->value);
Packit c5a612
			mpz_set(tmp->value, range);
Packit c5a612
Packit c5a612
			tmp = range_expr_alloc(&low->location,
Packit c5a612
					       expr_clone(expr_value(low)),
Packit c5a612
					       tmp);
Packit c5a612
			tmp = set_elem_expr_alloc(&low->location, tmp);
Packit c5a612
Packit c5a612
			if (low->etype == EXPR_MAPPING) {
Packit c5a612
				if (low->left->comment)
Packit c5a612
					tmp->comment = xstrdup(low->left->comment);
Packit c5a612
				if (low->left->timeout)
Packit c5a612
					tmp->timeout = low->left->timeout;
Packit c5a612
				if (low->left->expiration)
Packit c5a612
					tmp->expiration = low->left->expiration;
Packit c5a612
Packit c5a612
				tmp = mapping_expr_alloc(&tmp->location, tmp,
Packit c5a612
							 expr_clone(low->right));
Packit c5a612
			} else {
Packit c5a612
				if (low->comment)
Packit c5a612
					tmp->comment = xstrdup(low->comment);
Packit c5a612
				if (low->timeout)
Packit c5a612
					tmp->timeout = low->timeout;
Packit c5a612
				if (low->expiration)
Packit c5a612
					tmp->expiration = low->expiration;
Packit c5a612
			}
Packit c5a612
Packit c5a612
			compound_expr_add(set, tmp);
Packit c5a612
		} else {
Packit c5a612
			struct expr *prefix;
Packit c5a612
			unsigned int prefix_len;
Packit c5a612
Packit c5a612
			prefix_len = expr_value(i)->len - mpz_scan0(range, 0);
Packit c5a612
			prefix = prefix_expr_alloc(&low->location,
Packit c5a612
						   expr_clone(expr_value(low)),
Packit c5a612
						   prefix_len);
Packit c5a612
			prefix->len = expr_value(i)->len;
Packit c5a612
Packit c5a612
			prefix = set_elem_expr_alloc(&low->location, prefix);
Packit c5a612
Packit c5a612
			if (low->etype == EXPR_MAPPING) {
Packit c5a612
				if (low->left->comment)
Packit c5a612
					prefix->comment = xstrdup(low->left->comment);
Packit c5a612
				if (low->left->timeout)
Packit c5a612
					prefix->timeout = low->left->timeout;
Packit c5a612
				if (low->left->expiration)
Packit c5a612
					prefix->expiration = low->left->expiration;
Packit c5a612
Packit c5a612
				prefix = mapping_expr_alloc(&low->location, prefix,
Packit c5a612
							    expr_clone(low->right));
Packit c5a612
			} else {
Packit c5a612
				if (low->comment)
Packit c5a612
					prefix->comment = xstrdup(low->comment);
Packit c5a612
				if (low->timeout)
Packit c5a612
					prefix->timeout = low->timeout;
Packit Service 6f0138
				if (low->left->expiration)
Packit c5a612
					prefix->expiration = low->expiration;
Packit c5a612
			}
Packit c5a612
Packit c5a612
			compound_expr_add(set, prefix);
Packit c5a612
		}
Packit c5a612
Packit c5a612
		if (i->flags & EXPR_F_INTERVAL_END) {
Packit c5a612
			expr_free(low);
Packit c5a612
			low = NULL;
Packit c5a612
		}
Packit c5a612
		expr_free(i);
Packit c5a612
	}
Packit c5a612
Packit c5a612
	if (!low) /* no unclosed interval at end */
Packit c5a612
		goto out;
Packit c5a612
Packit c5a612
	i = constant_expr_alloc(&low->location, low->dtype,
Packit c5a612
				low->byteorder, expr_value(low)->len, NULL);
Packit c5a612
	mpz_init_bitmask(i->value, i->len);
Packit c5a612
Packit c5a612
	if (!mpz_cmp(i->value, expr_value(low)->value)) {
Packit c5a612
		expr_free(i);
Packit c5a612
		i = low;
Packit c5a612
	} else {
Packit c5a612
		i = range_expr_alloc(&low->location, expr_value(low), i);
Packit c5a612
		i = set_elem_expr_alloc(&low->location, i);
Packit c5a612
		if (low->etype == EXPR_MAPPING)
Packit c5a612
			i = mapping_expr_alloc(&i->location, i, low->right);
Packit c5a612
	}
Packit c5a612
Packit c5a612
	compound_expr_add(set, i);
Packit c5a612
out:
Packit c5a612
	mpz_clear(range);
Packit c5a612
	mpz_clear(p);
Packit c5a612
Packit c5a612
	xfree(ranges);
Packit c5a612
	xfree(elements);
Packit c5a612
}