#include <stdio.h>
#include <stdlib.h>

/* fib entry stub */
struct fib_nh {
	struct fib_info *nh_parent;
	int	nh_weight;
	int nh_power;
	int nh_flags;
};

/* fib stub */
struct fib_info {
	int fib_power;
	struct fib_nh *fib_nh;
	int fib_nhs;
};

long jiffies;

/* pulled from net/ipv4/fib_semantics.c with a few modifications to remove dependencies on kernel code */
void fib_select_multipath(struct fib_info *fi)
{
	int w;

	if (fi->fib_power <= 0) {
		int power = 0;
		{ 
			int nhsel; 
			struct fib_nh *nexthop_nh; 
			for (nhsel = 0, nexthop_nh = (struct fib_nh *)((fi)->fib_nh); nhsel < (fi)->fib_nhs; nexthop_nh++, nhsel++) {
				if (!(nexthop_nh->nh_flags & 1)) {
					power += nexthop_nh->nh_weight;
					nexthop_nh->nh_power = nexthop_nh->nh_weight;
				}
			} 
		};

		fi->fib_power = power;

		if (power <= 0) {
			printf("1");
			return;
		}
	}

	w = jiffies % fi->fib_power;

	{
		int nhsel; 
		struct fib_nh *nexthop_nh; 
		for (nhsel = 0, nexthop_nh = (struct fib_nh *)((fi)->fib_nh); nhsel < (fi)->fib_nhs; nexthop_nh++, nhsel++) {
			if (!(nexthop_nh->nh_flags & 1) && nexthop_nh->nh_power) {
				w -= nexthop_nh->nh_power;
				if (w <= 0) {
					nexthop_nh->nh_power--;
					fi->fib_power--;
					printf("%d", nhsel+1);
					return;
				}
			}
		} 
	};

	printf("1");
}

/* would be nice if this was a bit smarter */
void update_jiffies()
{
	jiffies += 1;
}

int main(int argc, char **argv)
{
	char buf[1024];
	struct fib_info fi;
	struct fib_nh *nh = NULL;
	int count, itrs, i, weight, tmp;

	argc--; argv++;

	if (argc < 3 || argc > 4) {
		puts("usage: fib_select <jiffies> <fib-count> <iterations> [<weight>]");
		puts("if <weight> is not specified you will be asked to enter a weight for each entry");
		return 1;
	}

	jiffies = atoi(*argv++);
	count = atoi(*argv++);
	itrs = atoi(*argv++);
	nh = malloc(sizeof(struct fib_nh) * count);
	weight = (argc == 4) ? atoi(*argv) : -1;
	buf[1023] = '\0';

	/* setup dummy fib_info struct */
	fi.fib_nhs = count;
	fi.fib_power = 0;
	fi.fib_nh = nh;

	/* fill nexthop array */
	for(i = 0; i < count; ++i) {
		nh[i].nh_parent = &fi;
		if (weight == -1) {
			printf("enter weight for route %d:", i+1);
			fgets(buf, 1023, stdin);
			buf[1023] = '\0';
			tmp = atoi(buf);
			nh[i].nh_weight = (tmp) ? tmp : 1;
		} else {
			nh[i].nh_weight = weight;
		}
		nh[i].nh_power = 0;
		nh[i].nh_flags = 0;
	}

	/* call route selection function */
	for(i = 0; i < itrs; ++i)
		fib_select_multipath(&fi);
		update_jiffies();

	puts("");

	return 0;
}

