#define __USE_GNU
#include "../module_iface.h"
#include <string.h>
#include <stdio.h>
#include <linux/netfilter_ipv4/ipt_state.h>
#include <linux/netfilter_ipv4/ip_conntrack.h>

#define MODULE_TYPE MODULE_MATCH
#define MODULE_DATATYPE struct ipt_state_info
#define MODULE_NAME "state"

#if MODULE_TYPE == MODULE_TARGET
#  define MODULE_ENTRYTYPE struct ipt_entry_match
#else 
#  if MODULE_TYPE == MODULE_MATCH
#    define MODULE_ENTRYTYPE struct ipt_entry_target
#  else
#    error MODULE_TYPE is unknown!
#  endif
#endif

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

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;
	AV *av;
	SV **svp;
	int i;
	char *flagname, *temp;
	STRLEN len;

	if(strcmp(field, "state"))
		return(FALSE);

	*flags = 1;

	if(!SvROK(value) || !(av = (AV *)SvRV(value)) ||
			SvTYPE((SV *)av) != SVt_PVAV) {
		SET_ERRSTR("%s: Must have an array reference arg", field);
		return(FALSE);
	}
	
	for(i = 0; i <= av_len(av); i++) {
		svp = av_fetch(av, i, 0);
		if(!svp || !SvPOK(*svp)) {
			SET_ERRSTR("%s: Array element %i was not a string scalar", field,
							i);
			return(FALSE);
		}
		temp = SvPV(*svp, len);
		flagname = malloc(len + 1);
		strncpy(flagname, temp, len);
		flagname[len] = '\0';
		if(!strcmp(flagname, "INVALID"))
			info->statemask |= IPT_STATE_INVALID;
		else if(!strcmp(flagname, "NEW"))
			info->statemask |= IPT_STATE_BIT(IP_CT_NEW);
		else if(!strcmp(flagname, "ESTABLISHED"))
			info->statemask |= IPT_STATE_BIT(IP_CT_ESTABLISHED);
		else if(!strcmp(flagname, "RELATED"))
			info->statemask |= IPT_STATE_BIT(IP_CT_RELATED);
		else {
			SET_ERRSTR("%s: Unknown connection state '%s'", field, flagname);
			free(flagname);
			return(FALSE);
		}
		free(flagname);
	}
	
	return(TRUE);
}

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

	av = newAV();
	
	if(info->statemask & IPT_STATE_INVALID)
		av_push(av, newSVpv("INVALID", 0));
	if(info->statemask & IPT_STATE_BIT(IP_CT_NEW))
		av_push(av, newSVpv("NEW", 0));
	if(info->statemask & IPT_STATE_BIT(IP_CT_ESTABLISHED))
		av_push(av, newSVpv("ESTABLISHED", 0));
	if(info->statemask & IPT_STATE_BIT(IP_CT_RELATED))
		av_push(av, newSVpv("RELATED", 0));
	
	hv_store(ent_hash, "state", 5, newRV((SV *)av), 0);
}

int final_check(void *myinfo, int flags) {
	if(!flags) {
		SET_ERRSTR("state must have a parameter");
		return(FALSE);
	}

	return(TRUE);
}

static ModuleDef _module = {
	NULL, /* always NULL */
	MODULE_TYPE,
	MODULE_NAME,
	IPT_ALIGN(sizeof(MODULE_DATATYPE)),
	IPT_ALIGN(sizeof(MODULE_DATATYPE)),
	setup,
	parse_field,
	get_fields,
	final_check
};

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