/*
 * (C) 2005-2007 by Pablo Neira Ayuso <pablo@netfilter.org>
 *     2005 by Harald Welte <laforge@netfilter.org>
 *
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation; either version 2 of the License, or
 *      (at your option) any later version.
 *
 */

#include "conntrack.h"

#include <stdio.h>
#include <getopt.h>
#include <stdlib.h>
#include <netinet/in.h> /* For htons */
#include <netinet/ip_icmp.h>
#include <libnetfilter_conntrack/libnetfilter_conntrack.h>

enum {
	CT_ICMP_TYPE	= (1 << 0),
	CT_ICMP_CODE	= (1 << 1),
	CT_ICMP_ID	= (1 << 2),
};

static struct option opts[] = {
	{"icmp-type", 1, 0, '1'},
	{"icmp-code", 1, 0, '2'},
	{"icmp-id", 1, 0, '3'},
	{0, 0, 0, 0}
};

#define ICMP_NUMBER_OF_OPT 4

static const char *icmp_optflags[ICMP_NUMBER_OF_OPT] = {
"icmp-type", "icmp-code", "icmp-id"
};

static char icmp_commands_v_options[NUMBER_OF_CMD][ICMP_NUMBER_OF_OPT] =
/* Well, it's better than "Re: Maradona vs Pele" */
{
		/* 1 2 3 */
/*CT_LIST*/	  {2,2,2},
/*CT_CREATE*/	  {1,1,2},
/*CT_UPDATE*/	  {2,2,2},
/*CT_DELETE*/	  {2,2,2},
/*CT_GET*/	  {1,1,2},
/*CT_FLUSH*/	  {0,0,0},
/*CT_EVENT*/	  {2,2,2},
/*CT_VERSION*/	  {0,0,0},
/*CT_HELP*/	  {0,0,0},
/*EXP_LIST*/	  {0,0,0},
/*EXP_CREATE*/	  {0,0,0},
/*EXP_DELETE*/	  {0,0,0},
/*EXP_GET*/	  {0,0,0},
/*EXP_FLUSH*/	  {0,0,0},
/*EXP_EVENT*/	  {0,0,0},
};

static void help(void)
{
	fprintf(stdout, "  --icmp-type\t\t\ticmp type\n");
	fprintf(stdout, "  --icmp-code\t\t\ticmp code\n");
	fprintf(stdout, "  --icmp-id\t\t\ticmp id\n");
}

static int parse(char c,
		 struct nf_conntrack *ct,
		 struct nf_conntrack *exptuple,
		 struct nf_conntrack *mask,
		 unsigned int *flags)
{
	switch(c) {
		u_int8_t tmp;
		u_int16_t id;
		case '1':
			tmp = atoi(optarg);
			nfct_set_attr_u8(ct, ATTR_ICMP_TYPE, tmp);
			nfct_set_attr_u8(ct, ATTR_L4PROTO, IPPROTO_ICMP);
			*flags |= CT_ICMP_TYPE;
			break;
		case '2':
			tmp = atoi(optarg);
			nfct_set_attr_u8(ct, ATTR_ICMP_CODE, tmp);
			nfct_set_attr_u8(ct, ATTR_L4PROTO, IPPROTO_ICMP);
			*flags |= CT_ICMP_CODE;
			break;
		case '3':
			id = htons(atoi(optarg));
			nfct_set_attr_u16(ct, ATTR_ICMP_ID, id);
			nfct_set_attr_u8(ct, ATTR_L4PROTO, IPPROTO_ICMP);
			*flags |= CT_ICMP_ID;
			break;
	}
	return 1;
}

static void final_check(unsigned int flags,
		        unsigned int cmd,
		        struct nf_conntrack *ct)
{
	generic_opt_check(flags,
			  ICMP_NUMBER_OF_OPT,
			  icmp_commands_v_options[cmd],
			  icmp_optflags, NULL, 0, NULL);
}

static struct ctproto_handler icmp = {
	.name 		= "icmp",
	.protonum	= IPPROTO_ICMP,
	.parse_opts	= parse,
	.final_check	= final_check,
	.help		= help,
	.opts		= opts,
	.version	= VERSION,
};

void register_icmp(void)
{
	register_proto(&icmp);
}
