The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#define BUILD_MATCH
#define MODULE_DATATYPE struct ipt_esp
#define MODULE_NAME "esp"

#define __USE_GNU
#include "../module_iface.h"
#include <string.h>
#include <stdlib.h>
#include <netdb.h>
#include <stdio.h>
#include <netinet/in.h>
#include <limits.h>
#include <linux/netfilter_ipv4/ipt_esp.h>
#include <errno.h>

static void setup(void *myinfo, unsigned int *nfcache) {
	MODULE_DATATYPE *info = (void *)((MODULE_ENTRYTYPE *)myinfo)->data;

	info->spis[1] = 0xFFFFFFFF;
}

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;
	unsigned int val;

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

	if (SvIOK(value)) {
		val = SvIV(value);
		if (val > 0xFFFFFFFF) {
			SET_ERRSTR("%s: Value out of range", field);
			return(FALSE);
		}
		info->spis[0] = info->spis[1] = val;
	}
	else if (SvPOK(value)) {
		STRLEN len;
		char *temp = SvPV(value, len);
		char *text = NULL, *sep = NULL, *base = NULL;

		base = text = malloc(len + 1);
		strncpy(text, temp, len);
		text[len] = '\0';

		if(*text == INVCHAR) {
			info->invflags |= IPT_ESP_INV_SPI;
			text++;
		}
		sep = strchr(text, ':');
		if(sep == text)
			info->spis[0] = 0;
		else {
			val = strtoul(text, &temp, 10);
			if(sep > temp || (val == 0xFFFFFFFF && errno == ERANGE)) {
				SET_ERRSTR("%s: Unable to parse '%s'", field, text);
				free(base);
				return(FALSE);
			}
			info->spis[0] = val;
		}

		if(sep) {
			text = ++sep;
			if(*text == '\0')
				info->spis[1] = 0xFFFFFFFF;
			else {
				val = strtoul(text, &temp, 10);
				if((text + strlen(text)) > temp || (val == 0xFFFFFFFF &&
										errno == ERANGE)) {
					SET_ERRSTR("%s: Unable to parse '%s'", field, text);
					free(base);
					return(FALSE);
				}
				info->spis[1] = val;
			}
		}
		else
			info->spis[1] = info->spis[0];

		free(base);
	
	}
	else {
		SET_ERRSTR("%s: Must have integer or string arg", field);
		return(FALSE);
	}

	return(TRUE);
}

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

	if (info->spis[0] != 0 || info->spis[1] != 0xFFFFFFFF) {
		if (info->spis[0] == info->spis[1] &&
						!(info->invflags & IPT_ESP_INV_SPI)) {
			sv = newSViv(info->spis[0]);
		}
		else {
			asprintf(&str, "%u", info->spis[0]);
			if (info->spis[0] != info->spis[1]) {
				asprintf(&temp, "%s:%u", str, info->spis[1]);
				free(str);
				str = temp;
			}
			if (info->invflags & IPT_ESP_INV_SPI) {
				asprintf(&temp, "%c%s", INVCHAR, str);
				free(str);
				str = temp;
			}
			sv = newSVpv(str, 0);
			free(str);
		}
		hv_store(ent_hash, "espspi", 6, sv, 0);
	}
}

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,
};

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