#define BUILD_MATCH
#define MODULE_DATATYPE struct ipt_addrtype_info
#define MODULE_NAME "addrtype"

#define __USE_GNU
#include "../module_iface.h"
#include <string.h>
#include <stdio.h>
#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter_ipv4/ipt_addrtype.h>

static char *rtn_names[] = {
	"UNSPEC",
	"UNICAST",
	"LOCAL",
	"BROADCAST",
	"ANYCAST",
	"MULTICAST",
	"BLACKHOLE",
	"UNREACHABLE",
	"PROHIBIT",
	"THROW",
	"NAT",
	"XRESOLVE",
	NULL
};

static void setup(void *myinfo, unsigned int *nfcache) {
	*nfcache |= NFC_UNKNOWN;
}

static int parse_value(SV *value, u_int16_t *mask, int *invert) {
	SV *sv = NULL;
	AV *av = NULL;
	SV **svp;
	int index = 0, i, len;
	char *str;

	if (SvROK(value) && (av = (AV *)SvRV(value)) &&
					(SvTYPE((SV *)av) == SVt_PVAV)) {
		if (index <= av_len(av)) {
			svp = av_fetch(av, index, 0);
			sv = *svp;
		}
	}
	else if (SvPOK(value))
		sv = value;
	else {
		SET_ERRSTR("Argument must be string or array");
		return(FALSE);
	}

	while (sv) {
		if (!SvPOK(sv)) {
			SET_ERRSTR("Array element must be string");
			return(FALSE);
		}

		str = SvPV(sv, len);
		if (av && !strncasecmp(str, "INVERT", len))
			*invert = 1;
		else {
			for (i = 0; i < sizeof(rtn_names) / sizeof(char *); i++) {
				printf("str is \"%s\", rtn_names[%d] is \"%s\"\n", str, i, rtn_names[i]);
				if (!strncasecmp(str, rtn_names[i], len)) {
					*mask |= (1 << i);
					break;
				}
			}
			if (i >= sizeof(rtn_names) / sizeof(char *)) {
				SET_ERRSTR("Couldn't parse type name");
				return(FALSE);
			}
		}

		if (av && index <= av_len(av)) {
			svp = av_fetch(av, index, 0);
			sv = *svp;
			index++;
		}
		else
			sv = NULL;
	}
	return(TRUE);
}

#define IPT_ADDRTYPE_OPT_SRCTYPE	0x1
#define IPT_ADDRTYPE_OPT_DSTTYPE	0x2

static int parse_field(char *field, SV *value, void *myinfo,
		unsigned int *nfcache, struct ipt_entry *entry, int *flags) {
	MODULE_DATATYPE *info = (void *)(*(MODULE_ENTRYTYPE **)myinfo)->data;
	char *temp;

	if (!strcmp(field, "src-type")) {
		if (!parse_value(value, &info->source, &info->invert_source)) {
			temp = strdup(SvPV_nolen(ERROR_SV));
			SET_ERRSTR("%s: %s", field, temp);
			free(temp);
			return(FALSE);
		}

		*flags |= IPT_ADDRTYPE_OPT_SRCTYPE;
		return(TRUE);
	} else if (!strcmp(field, "dst-type")) {
		if (!parse_value(value, &info->dest, &info->invert_dest)) {
			temp = strdup(SvPV_nolen(ERROR_SV));
			SET_ERRSTR("%s: %s", field, temp);
			free(temp);
			return(FALSE);
		}

		*flags |= IPT_ADDRTYPE_OPT_DSTTYPE;
		return(TRUE);
	}
	return(FALSE);
}

static SV *flag_list(u_int16_t mask, int invert) {
	AV *flags = newAV();
	int i;

	for (i = 0; i < sizeof(rtn_names) / sizeof(char *); i++) {
		if (mask & (1 << i))
			av_push((AV *)flags, newSVpv(rtn_names[i], strlen(rtn_names[i])));
	}

	if (invert)
		av_push((AV *)flags, newSVpv("INVERT", 6));

	return(newRV((SV *)flags));
}

static void get_fields(HV *ent_hash, void *myinfo, struct ipt_entry *entry) {
	MODULE_DATATYPE *info = (void *)((MODULE_ENTRYTYPE *)myinfo)->data;

	if (info->source)
		hv_store(ent_hash, "src-type", 8, flag_list(info->source,
								info->invert_source), 0);
	if (info->dest)
		hv_store(ent_hash, "dst-type", 8, flag_list(info->dest,
								info->invert_dest), 0);
}

int final_check(void *myinfo, int flags) {
	if(!flags) {
		SET_ERRSTR("addrtype requires at least one of 'src-type', 'dst-type'");
		return(FALSE);
	}
	
	return(TRUE);
}

static ModuleDef _module = {
	.type			= MODULE_TYPE,
	.name			= MODULE_NAME,
	.size			= IPT_ALIGN(sizeof(MODULE_DATATYPE)),
	.size_uspace	= IPT_ALIGN(sizeof(MODULE_DATATYPE)),
	.setup			= setup,
	.parse_field	= parse_field,
	.get_fields		= get_fields,
	.final_check	= final_check,
};

ModuleDef *init(void) {
	return(&_module);
}
/* vim: ts=4
 */