/* Shared library add-on to iptables to add MAC address support. */
#include <stdio.h>
#include <netdb.h>
#include <string.h>
#include <stdlib.h>
#include <getopt.h>
#include <ctype.h>
#include <iptables.h>
#if defined(__GLIBC__) && __GLIBC__ == 2
#include <net/ethernet.h>
#else
#include <linux/if_ether.h>
#endif
#include <linux/netfilter/xt_phyport.h>

static void phyport_help(void)
{
	printf(
"phyport match options:\n"
"[!] --port-source input number(1-4)\n"
"				Match source port number\n"
" --port-destination output number(1-4)\n"
"				Match destination port number\n");
}

static const struct option phyport_opts[] = {
	{ "port-source", 1, NULL, '1' },
	{ "port-destination", 1, NULL, '2' },
	{ .name = NULL }
};

static void
parse_phyportnum(const char *phyportnum, struct xt_phyport_info *info)
{
	long number;
	char *end;

	if(phyportnum==NULL)
		exit_error(PARAMETER_PROBLEM, "phy port number pointer is null");
	
	number=strtol(phyportnum, &end, 10);

	if((number<0)||(number>255))
		exit_error(PARAMETER_PROBLEM, "Bad phy port number `%s'", phyportnum);

	if(info->flags & PORT_SRC){
		info->srcport=(unsigned char)number;
	}
	else if (info->flags & PORT_DST){
		info->dstport=(unsigned char)number;
	}
	else{
		exit_error(PARAMETER_PROBLEM, "wrong info->flags: 0x%x", info->flags);
	}	
}
static void
phyport_init(struct ipt_entry_match *m, unsigned int *nfcache)
{
}
static int
phyport_parse(int c, char **argv, int invert, unsigned int *flags,
      const struct ipt_entry *entry,
      unsigned int *nfcache,
      struct ipt_entry_match **match)
{
	struct xt_phyport_info *phyportinfo = (struct xt_phyport_info *)((*match)->data);
        switch (c) {
        case '1':
                if (*flags & PORT_SRC)
                        exit_error(PARAMETER_PROBLEM,
                                   "phyport match: Only use --port-source ONCE!");
                *flags |= PORT_SRC;

                phyportinfo->flags |= PORT_SRC;
                check_inverse(optarg, &invert, &optind, 0);
                if (invert) {
                        phyportinfo->flags |= PORT_SRC_INV;
                }
                parse_phyportnum(optarg, phyportinfo);
                break;

        case '2':
                if (*flags & PORT_DST)
                        exit_error(PARAMETER_PROBLEM,
                                   "phyport match: Only use --port-destination ONCE!");
                *flags |= PORT_DST;

                phyportinfo->flags |= PORT_DST;
                check_inverse(optarg, &invert, &optind, 0);
                if (invert){
                        phyportinfo->flags |= PORT_DST_INV;
                }
                parse_phyportnum(optarg, phyportinfo);
                break;

        default:
                return 0;
        }

        return 1;
}

static void print_phyportnum(struct xt_phyport_info *info)
{
	if(info->flags & PORT_SRC){
		printf("%d", info->srcport);
	}
	else if (info->flags & PORT_DST){
		printf("%d", info->dstport);
	}
	else{
		exit_error(PARAMETER_PROBLEM, "wrong info->flags: 0x%x", info->flags);
	}
		
	printf(" ");
}

static void phyport_check(unsigned int flags)
{
	if (!flags)
		exit_error(PARAMETER_PROBLEM,
			   "You must specify `--port-source' or `--port-destination'");
}

static void
phyport_print(const struct ipt_ip *ip,const struct ipt_entry_match *match, int numeric)
{
	struct xt_phyport_info *phyportinfo = (struct xt_phyport_info *)(match->data);

        if (phyportinfo->flags & PORT_SRC) {
                printf("source port  ");
                if (phyportinfo->flags & PORT_SRC_INV)
                        printf("! ");
                print_phyportnum(phyportinfo);
        }
        if (phyportinfo->flags & PORT_DST) {
                printf("destination port ");
                if (phyportinfo->flags & PORT_DST_INV)
                        printf("! ");
                print_phyportnum(phyportinfo);
        }
}

static void phyport_save(const struct ipt_ip *ip,const struct ipt_entry_match *match)
{
	struct xt_phyport_info *phyportinfo = (struct xt_phyport_info *)(match->data);

        if (phyportinfo->flags & PORT_SRC) {
                if (phyportinfo->flags & PORT_SRC_INV)
                        printf("! ");
                printf("--port-source ");
                print_phyportnum(phyportinfo);
                if (phyportinfo->flags & PORT_DST)
                        fputc(' ', stdout);
        }
        if (phyportinfo->flags & PORT_DST) {
                if (phyportinfo->flags & PORT_DST_INV)
                        printf("! ");
                printf("--port-destination ");
                print_phyportnum(phyportinfo);
        }
}

static struct iptables_match phyport_match = {

 	.name		= "phyport",
	.version	= IPTABLES_VERSION,
	.size		= IP6T_ALIGN(sizeof(struct xt_phyport_info)),
	.userspacesize	= IP6T_ALIGN(sizeof(struct xt_phyport_info)),
	.help		= &phyport_help,
	.init		= &phyport_init,
	.parse		= &phyport_parse,
	.final_check	= &phyport_check,
	.print		= &phyport_print,
	.save		= &phyport_save,
	.extra_opts	= phyport_opts,
};
void _init(void)
{
	register_match6(&phyport_match);
}
